diff --git a/drivers/staging/qcacld-3.0/Android.mk b/drivers/staging/qcacld-3.0/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..5dfb078e816bcfb0a2f48b0b2b17ef0462ba0104 --- /dev/null +++ b/drivers/staging/qcacld-3.0/Android.mk @@ -0,0 +1,180 @@ +ENABLE_QCACLD := true +ifeq ($(TARGET_USES_QMAA), true) +ifneq ($(TARGET_USES_QMAA_OVERRIDE_WLAN), true) +ENABLE_QCACLD := false +else +ENABLE_QCACLD := true +endif +endif + +ifeq ($(ENABLE_QCACLD), true) +# Android makefile for the WLAN Module +LOCAL_PATH := $(call my-dir) + +# Assume no targets will be supported +WLAN_CHIPSET := + +ifeq ($(BOARD_HAS_QCOM_WLAN), true) + +# Check if this driver needs be built for current target +ifneq ($(findstring qca_cld3,$(WIFI_DRIVER_BUILT)),) + WLAN_CHIPSET := qca_cld3 + WLAN_SELECT := CONFIG_QCA_CLD_WLAN=m +endif + +# Build/Package only in case of supported target +ifneq ($(WLAN_CHIPSET),) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + WLAN_BLD_DIR := vendor/qcom/opensource/wlan +endif # opensource + +# Multi-ko check +LOCAL_DEV_NAME := $(patsubst .%,%,\ + $(lastword $(strip $(subst /, ,$(LOCAL_PATH))))) + +ifeq (1, $(strip $(shell expr $(words $(strip $(TARGET_WLAN_CHIP))) \>= 2))) + +ifeq ($(LOCAL_DEV_NAME), qcacld-3.0) +LOCAL_MULTI_KO := true +else +LOCAL_MULTI_KO := false +endif + +endif + +ifeq ($(LOCAL_MULTI_KO), true) +LOCAL_ANDROID_ROOT := $(shell pwd) +LOCAL_WLAN_BLD_DIR := $(LOCAL_ANDROID_ROOT)/$(WLAN_BLD_DIR) +$(shell find $(LOCAL_WLAN_BLD_DIR)/qcacld-3.0/ -maxdepth 1 \ + -name '.*' ! -name '.git' -exec rm -rf {} +) + +$(foreach chip, $(TARGET_WLAN_CHIP), \ + $($(shell mkdir -p $(LOCAL_WLAN_BLD_DIR)/qcacld-3.0/.$(chip); \ + ln -sf $(LOCAL_WLAN_BLD_DIR)/qca-wifi-host-cmn \ + $(LOCAL_WLAN_BLD_DIR)/qcacld-3.0/.$(chip)/qca-wifi-host-cmn); \ + $(foreach node, \ + $(shell find $(LOCAL_WLAN_BLD_DIR)/qcacld-3.0/ -maxdepth 1 \ + ! -name '.*' ! -name '*~' \ + ! -name '.' ! -name 'qcacld-3.0'), \ + $(shell ln -sf $(node) \ + $(LOCAL_WLAN_BLD_DIR)/qcacld-3.0/.$(chip)/$(lastword $(strip $(subst /, ,$(node)))) \ + )))) + +include $(foreach chip, $(TARGET_WLAN_CHIP), $(LOCAL_PATH)/.$(chip)/Android.mk) + +else # Multi-ok check + +ifeq ($(WLAN_PROFILE),) +WLAN_PROFILE := default +endif + +ifeq ($(LOCAL_DEV_NAME), qcacld-3.0) + +LOCAL_DEV_NAME := wlan +LOCAL_MOD_NAME := wlan +CMN_OFFSET := .. +LOCAL_SRC_DIR := +TARGET_FW_DIR := firmware/wlan/qca_cld +TARGET_CFG_PATH := /vendor/etc/wifi +TARGET_MAC_BIN_PATH := /mnt/vendor/persist + +else + +LOCAL_SRC_DIR := .$(LOCAL_DEV_NAME) +CMN_OFFSET := . +# Use default profile if WLAN_CFG_USE_DEFAULT defined. +ifeq ($(WLAN_CFG_USE_DEFAULT),) +WLAN_PROFILE := $(LOCAL_DEV_NAME) +endif +TARGET_FW_DIR := firmware/wlan/qca_cld/$(LOCAL_DEV_NAME) +TARGET_CFG_PATH := /vendor/etc/wifi/$(LOCAL_DEV_NAME) +TARGET_MAC_BIN_PATH := /mnt/vendor/persist/$(LOCAL_DEV_NAME) + +ifneq ($(TARGET_MULTI_WLAN), true) +LOCAL_MOD_NAME := wlan +DYNAMIC_SINGLE_CHIP := $(LOCAL_DEV_NAME) +else +LOCAL_MOD_NAME := $(LOCAL_DEV_NAME) +endif + +endif + +# DLKM_DIR was moved for JELLY_BEAN (PLATFORM_SDK 16) +ifeq ($(call is-platform-sdk-version-at-least,16),true) + DLKM_DIR := $(TOP)/device/qcom/common/dlkm +else + DLKM_DIR := build/dlkm +endif # platform-sdk-version + +# Build wlan.ko as $(WLAN_CHIPSET)_wlan.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := WLAN_ROOT=$(WLAN_BLD_DIR)/qcacld-3.0/$(LOCAL_SRC_DIR) +KBUILD_OPTIONS += WLAN_COMMON_ROOT=$(CMN_OFFSET)/qca-wifi-host-cmn +KBUILD_OPTIONS += WLAN_COMMON_INC=$(WLAN_BLD_DIR)/qca-wifi-host-cmn +KBUILD_OPTIONS += WLAN_FW_API=$(WLAN_BLD_DIR)/fw-api +KBUILD_OPTIONS += WLAN_PROFILE=$(WLAN_PROFILE) +KBUILD_OPTIONS += DYNAMIC_SINGLE_CHIP=$(DYNAMIC_SINGLE_CHIP) + +# We are actually building wlan.ko here, as per the +# requirement we are specifying _wlan.ko as LOCAL_MODULE. +# This means we need to rename the module to _wlan.ko +# after wlan.ko is built. +KBUILD_OPTIONS += MODNAME=$(LOCAL_MOD_NAME) +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(WLAN_SELECT) + +ifneq ($(WLAN_CFG_OVERRIDE_$(LOCAL_DEV_NAME)),) +KBUILD_OPTIONS += WLAN_CFG_OVERRIDE="$(WLAN_CFG_OVERRIDE_$(LOCAL_DEV_NAME))" +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := $(WLAN_CHIPSET)_$(LOCAL_DEV_NAME).ko +LOCAL_MODULE_KBUILD_NAME := $(LOCAL_MOD_NAME).ko +LOCAL_MODULE_DEBUG_ENABLE := true +ifeq ($(PRODUCT_VENDOR_MOVE_ENABLED),true) + ifeq ($(WIFI_DRIVER_INSTALL_TO_KERNEL_OUT),true) + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + else + LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/$(WLAN_CHIPSET) + endif +else + LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/modules/$(WLAN_CHIPSET) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### + +# Create Symbolic link +ifneq ($(findstring $(WLAN_CHIPSET),$(WIFI_DRIVER_DEFAULT)),) +ifeq ($(PRODUCT_VENDOR_MOVE_ENABLED),true) +ifneq ($(WIFI_DRIVER_INSTALL_TO_KERNEL_OUT),) +$(shell mkdir -p $(TARGET_OUT_VENDOR)/lib/modules; \ + ln -sf /$(TARGET_COPY_OUT_VENDOR)/lib/modules/$(WLAN_CHIPSET)/$(LOCAL_MODULE) $(TARGET_OUT_VENDOR)/lib/modules/$(LOCAL_MODULE)) +endif +else +$(shell mkdir -p $(TARGET_OUT)/lib/modules; \ + ln -sf /system/lib/modules/$(WLAN_CHIPSET)/$(LOCAL_MODULE) $(TARGET_OUT)/lib/modules/$(LOCAL_MODULE)) +endif +endif + +ifeq ($(PRODUCT_VENDOR_MOVE_ENABLED),true) +TARGET_FW_PATH := $(TARGET_OUT_VENDOR)/$(TARGET_FW_DIR) +else +TARGET_FW_PATH := $(TARGET_OUT_ETC)/$(TARGET_FW_DIR) +endif + +$(shell mkdir -p $(TARGET_FW_PATH); \ + ln -sf $(TARGET_MAC_BIN_PATH)/wlan_mac.bin $(TARGET_FW_PATH)/wlan_mac.bin) +ifneq ($(GENERIC_ODM_IMAGE),true) +$(shell ln -sf $(TARGET_CFG_PATH)/WCNSS_qcom_cfg.ini $(TARGET_FW_PATH)/WCNSS_qcom_cfg.ini) +endif +endif # Multi-ko check +endif # DLKM check +endif # supported target check +endif # WLAN enabled check +endif # ENABLE_QCACLD diff --git a/drivers/staging/qcacld-3.0/Kbuild b/drivers/staging/qcacld-3.0/Kbuild new file mode 100644 index 0000000000000000000000000000000000000000..5884f9999d5f945e40779bd3cece717036b7d7fc --- /dev/null +++ b/drivers/staging/qcacld-3.0/Kbuild @@ -0,0 +1,3423 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := y +else + KERNEL_BUILD := n +endif +ifeq ($(KERNEL_BUILD), y) + # These are provided in external module based builds + # Need to explicitly define for Kernel-based builds + MODNAME := wlan + WLAN_ROOT := drivers/staging/wlan-qc/qcacld-3.0 + WLAN_COMMON_ROOT := ../qca-wifi-host-cmn + WLAN_COMMON_INC := $(WLAN_ROOT)/$(WLAN_COMMON_ROOT) + WLAN_FW_API := $(WLAN_ROOT)/../fw-api/ + WLAN_PROFILE := default + + # Disable debugging by forcing BUILD_VARIANT user if + # we are building this outside of Android + TARGET_BUILD_VARIANT := user +endif + +WLAN_COMMON_ROOT ?= ../qca-wifi-host-cmn +WLAN_COMMON_INC ?= $(WLAN_ROOT)/$(WLAN_COMMON_ROOT) +WLAN_FW_API ?= $(WLAN_ROOT)/../fw-api/ +WLAN_PROFILE ?= default +CONFIG_QCA_CLD_WLAN_PROFILE ?= $(WLAN_PROFILE) + +ifeq ($(KERNEL_BUILD), n) +ifneq ($(ANDROID_BUILD_TOP),) + KERNEL_PATH_4.4 := $(shell python -c "import os.path; print(os.path.exists('$(ANDROID_BUILD_TOP)/kernel/msm-4.4'))") +ifeq ($(KERNEL_PATH_4.4), True) + override WLAN_ROOT := $(ANDROID_BUILD_TOP)/$(WLAN_ROOT) + override WLAN_COMMON_INC := $(ANDROID_BUILD_TOP)/$(WLAN_COMMON_INC) + override WLAN_FW_API := $(ANDROID_BUILD_TOP)/$(WLAN_FW_API) +else + ANDROID_BUILD_TOP_REL := $(shell python -c "import os.path; print(os.path.relpath('$(ANDROID_BUILD_TOP)'))") + override WLAN_ROOT := $(ANDROID_BUILD_TOP_REL)/$(WLAN_ROOT) + override WLAN_COMMON_INC := $(ANDROID_BUILD_TOP_REL)/$(WLAN_COMMON_INC) + override WLAN_FW_API := $(ANDROID_BUILD_TOP_REL)/$(WLAN_FW_API) +endif +endif +endif + + +# add configurations in WLAN_CFG_OVERRIDE +ifneq ($(WLAN_CFG_OVERRIDE),) +WLAN_CFG_OVERRIDE_FILE := $(WLAN_ROOT)/.wlan_cfg_override +$(shell echo > $(WLAN_CFG_OVERRIDE_FILE)) + +$(foreach cfg, $(WLAN_CFG_OVERRIDE), \ + $(shell echo $(cfg) >> $(WLAN_CFG_OVERRIDE_FILE))) + +include $(WLAN_CFG_OVERRIDE_FILE) +$(warning "Overriding WLAN config with: $(shell cat $(WLAN_CFG_OVERRIDE_FILE))") +endif + +include $(WLAN_ROOT)/configs/$(CONFIG_QCA_CLD_WLAN_PROFILE)_defconfig + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(WLAN_ROOT)/$(UAPI_DIR)/linux + +############ COMMON ############ +COMMON_DIR := core/common +COMMON_INC := -I$(WLAN_ROOT)/$(COMMON_DIR) + +############ HDD ############ +HDD_DIR := core/hdd +HDD_INC_DIR := $(HDD_DIR)/inc +HDD_SRC_DIR := $(HDD_DIR)/src + +HDD_INC := -I$(WLAN_ROOT)/$(HDD_INC_DIR) \ + -I$(WLAN_ROOT)/$(HDD_SRC_DIR) + +HDD_OBJS := $(HDD_SRC_DIR)/wlan_hdd_assoc.o \ + $(HDD_SRC_DIR)/wlan_hdd_cfg.o \ + $(HDD_SRC_DIR)/wlan_hdd_cfg80211.o \ + $(HDD_SRC_DIR)/wlan_hdd_data_stall_detection.o \ + $(HDD_SRC_DIR)/wlan_hdd_driver_ops.o \ + $(HDD_SRC_DIR)/wlan_hdd_ftm.o \ + $(HDD_SRC_DIR)/wlan_hdd_hostapd.o \ + $(HDD_SRC_DIR)/wlan_hdd_ioctl.o \ + $(HDD_SRC_DIR)/wlan_hdd_main.o \ + $(HDD_SRC_DIR)/wlan_hdd_object_manager.o \ + $(HDD_SRC_DIR)/wlan_hdd_oemdata.o \ + $(HDD_SRC_DIR)/wlan_hdd_p2p.o \ + $(HDD_SRC_DIR)/wlan_hdd_power.o \ + $(HDD_SRC_DIR)/wlan_hdd_regulatory.o \ + $(HDD_SRC_DIR)/wlan_hdd_scan.o \ + $(HDD_SRC_DIR)/wlan_hdd_softap_tx_rx.o \ + $(HDD_SRC_DIR)/wlan_hdd_sta_info.o \ + $(HDD_SRC_DIR)/wlan_hdd_stats.o \ + $(HDD_SRC_DIR)/wlan_hdd_trace.o \ + $(HDD_SRC_DIR)/wlan_hdd_tx_rx.o \ + $(HDD_SRC_DIR)/wlan_hdd_wmm.o \ + $(HDD_SRC_DIR)/wlan_hdd_wowl.o + +ifeq ($(CONFIG_WLAN_FEATURE_PERIODIC_STA_STATS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_periodic_sta_stats.o +endif + +ifeq ($(CONFIG_WLAN_WEXT_SUPPORT_ENABLE), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_wext.o \ + $(HDD_SRC_DIR)/wlan_hdd_hostapd_wext.o +endif + +ifeq ($(CONFIG_FEATURE_WLAN_EXTSCAN), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_ext_scan.o +endif + +ifeq ($(CONFIG_WLAN_DEBUGFS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs.o +ifeq ($(CONFIG_WLAN_FEATURE_LINK_LAYER_STATS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_llstat.o +endif +ifeq ($(CONFIG_WLAN_FEATURE_MIB_STATS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_mibstat.o +endif +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_csr.o +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_connect.o +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_offload.o +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_roam.o +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_config.o +ifeq ($(CONFIG_WLAN_MWS_INFO_DEBUGFS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_coex.o +endif +endif + +ifeq ($(CONFIG_WLAN_CONV_SPECTRAL_ENABLE),y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_spectralscan.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) +HDD_OBJS+= $(HDD_SRC_DIR)/wlan_hdd_ocb.o +endif + +ifeq ($(CONFIG_FEATURE_MEMDUMP_ENABLE), y) +HDD_OBJS+= $(HDD_SRC_DIR)/wlan_hdd_memdump.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_FIPS), y) +HDD_OBJS+= $(HDD_SRC_DIR)/wlan_hdd_fips.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_GREEN_AP), y) +HDD_OBJS+= $(HDD_SRC_DIR)/wlan_hdd_green_ap.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_APF), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_apf.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_LPSS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_lpass.o +endif + +ifeq ($(CONFIG_WLAN_LRO), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_lro.o +endif + +ifeq ($(CONFIG_WLAN_NAPI), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_napi.o +endif + +ifeq ($(CONFIG_IPA_OFFLOAD), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_ipa.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_NAN), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_nan.o +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_nan_datapath.o +endif + +ifeq ($(CONFIG_QCOM_TDLS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_tdls.o +endif + +ifeq ($(CONFIG_WLAN_SYNC_TSF_PLUS), y) +CONFIG_WLAN_SYNC_TSF := y +endif + +ifeq ($(CONFIG_WLAN_SYNC_TSF), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_tsf.o +endif + +ifeq ($(CONFIG_MPC_UT_FRAMEWORK), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_conc_ut.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DISA), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_disa.o +endif + +ifeq ($(CONFIG_LFR_SUBNET_DETECTION), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_subnet_detect.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_11AX), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_he.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_TWT), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_twt.o +endif + +ifeq ($(CONFIG_LITHIUM), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_rx_monitor.o +endif + +ifeq ($(CONFIG_LITHIUM), y) +CONFIG_WLAN_FEATURE_DP_RX_THREADS := y +CONFIG_WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT := y +endif + +ifeq ($(CONFIG_WLAN_NUD_TRACKING), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_nud_tracking.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_PACKET_FILTERING), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_packet_filter.o +endif + +ifeq ($(CONFIG_FEATURE_RSSI_MONITOR), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_rssi_monitor.o +endif + +ifeq ($(CONFIG_FEATURE_BSS_TRANSITION), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_bss_transition.o +endif + +ifeq ($(CONFIG_FEATURE_STATION_INFO), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_station_info.o +endif + +ifeq ($(CONFIG_FEATURE_TX_POWER), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_tx_power.o +endif + +ifeq ($(CONFIG_FEATURE_OTA_TEST), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_ota_test.o +endif + +ifeq ($(CONFIG_FEATURE_ACTIVE_TOS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_active_tos.o +endif + +ifeq ($(CONFIG_FEATURE_SAR_LIMITS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_sar_limits.o +endif + +ifeq ($(CONFIG_FEATURE_CONCURRENCY_MATRIX), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_concurrency_matrix.o +endif + +ifeq ($(CONFIG_FEATURE_SAP_COND_CHAN_SWITCH), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_sap_cond_chan_switch.o +endif + +ifeq ($(CONFIG_FEATURE_P2P_LISTEN_OFFLOAD), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_p2p_listen_offload.o +endif + +ifeq ($(CONFIG_WLAN_SYSFS), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_sysfs.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_FW_STATE), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_fw_state.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_COEX_CONFIG), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_coex_config.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_MPTA_HELPER), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_mpta_helper.o +endif + +ifeq ($(CONFIG_WLAN_BCN_RECV_FEATURE), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_bcn_recv.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_HW_CAPABILITY), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_hw_capability.o +endif + +ifeq ($(CONFIG_FW_THERMAL_THROTTLE), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_thermal.o +endif + +ifeq ($(CONFIG_WLAN_CFR_ENABLE), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_cfr.o +endif + +ifeq ($(CONFIG_WLAN_HANG_EVENT), y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_hang_event.o +endif + +ifeq ($(CONFIG_FEATURE_GPIO_CFG),y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_gpio.o +endif + +###### OSIF_SYNC ######## +SYNC_DIR := os_if/sync +SYNC_INC_DIR := $(SYNC_DIR)/inc +SYNC_SRC_DIR := $(SYNC_DIR)/src + +SYNC_INC := \ + -I$(WLAN_ROOT)/$(SYNC_INC_DIR) \ + -I$(WLAN_ROOT)/$(SYNC_SRC_DIR) \ + +SYNC_OBJS := \ + $(SYNC_SRC_DIR)/osif_sync.o \ + $(SYNC_SRC_DIR)/osif_driver_sync.o \ + $(SYNC_SRC_DIR)/osif_psoc_sync.o \ + $(SYNC_SRC_DIR)/osif_vdev_sync.o \ + +########### Driver Synchronization Core (DSC) ########### +DSC_DIR := components/dsc +DSC_INC_DIR := $(DSC_DIR)/inc +DSC_SRC_DIR := $(DSC_DIR)/src +DSC_TEST_DIR := $(DSC_DIR)/test + +DSC_INC := \ + -I$(WLAN_ROOT)/$(DSC_INC_DIR) \ + -I$(WLAN_ROOT)/$(DSC_SRC_DIR) \ + -I$(WLAN_ROOT)/$(DSC_TEST_DIR) + +DSC_OBJS := \ + $(DSC_SRC_DIR)/__wlan_dsc.o \ + $(DSC_SRC_DIR)/wlan_dsc_driver.o \ + $(DSC_SRC_DIR)/wlan_dsc_psoc.o \ + $(DSC_SRC_DIR)/wlan_dsc_vdev.o + +ifeq ($(CONFIG_DSC_TEST), y) + DSC_OBJS += $(DSC_TEST_DIR)/wlan_dsc_test.o +endif + +cppflags-$(CONFIG_DSC_DEBUG) += -DWLAN_DSC_DEBUG +cppflags-$(CONFIG_DSC_TEST) += -DWLAN_DSC_TEST + +########### HOST DIAG LOG ########### +HOST_DIAG_LOG_DIR := $(WLAN_COMMON_ROOT)/utils/host_diag_log + +HOST_DIAG_LOG_INC_DIR := $(HOST_DIAG_LOG_DIR)/inc +HOST_DIAG_LOG_SRC_DIR := $(HOST_DIAG_LOG_DIR)/src + +HOST_DIAG_LOG_INC := -I$(WLAN_ROOT)/$(HOST_DIAG_LOG_INC_DIR) \ + -I$(WLAN_ROOT)/$(HOST_DIAG_LOG_SRC_DIR) + +HOST_DIAG_LOG_OBJS += $(HOST_DIAG_LOG_SRC_DIR)/host_diag_log.o + +############ EPPING ############ +EPPING_DIR := $(WLAN_COMMON_ROOT)/utils/epping +EPPING_INC_DIR := $(EPPING_DIR)/inc +EPPING_SRC_DIR := $(EPPING_DIR)/src + +EPPING_INC := -I$(WLAN_ROOT)/$(EPPING_INC_DIR) + +EPPING_OBJS := $(EPPING_SRC_DIR)/epping_main.o \ + $(EPPING_SRC_DIR)/epping_txrx.o \ + $(EPPING_SRC_DIR)/epping_tx.o \ + $(EPPING_SRC_DIR)/epping_rx.o \ + $(EPPING_SRC_DIR)/epping_helper.o + +############ SYS ############ +CMN_SYS_DIR := $(WLAN_COMMON_ROOT)/utils/sys +CMN_SYS_INC_DIR := $(CMN_SYS_DIR) +CMN_SYS_INC := -I$(WLAN_ROOT)/$(CMN_SYS_INC_DIR) + +############ MAC ############ +MAC_DIR := core/mac +MAC_INC_DIR := $(MAC_DIR)/inc +MAC_SRC_DIR := $(MAC_DIR)/src + +MAC_INC := -I$(WLAN_ROOT)/$(MAC_INC_DIR) \ + -I$(WLAN_ROOT)/$(MAC_SRC_DIR)/dph \ + -I$(WLAN_ROOT)/$(MAC_SRC_DIR)/include \ + -I$(WLAN_ROOT)/$(MAC_SRC_DIR)/pe/include \ + -I$(WLAN_ROOT)/$(MAC_SRC_DIR)/pe/lim \ + -I$(WLAN_ROOT)/$(MAC_SRC_DIR)/pe/nan + +MAC_DPH_OBJS := $(MAC_SRC_DIR)/dph/dph_hash_table.o + +MAC_LIM_OBJS := $(MAC_SRC_DIR)/pe/lim/lim_aid_mgmt.o \ + $(MAC_SRC_DIR)/pe/lim/lim_admit_control.o \ + $(MAC_SRC_DIR)/pe/lim/lim_api.o \ + $(MAC_SRC_DIR)/pe/lim/lim_assoc_utils.o \ + $(MAC_SRC_DIR)/pe/lim/lim_ft.o \ + $(MAC_SRC_DIR)/pe/lim/lim_link_monitoring_algo.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_action_frame.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_assoc_req_frame.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_assoc_rsp_frame.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_auth_frame.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_beacon_frame.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_cfg_updates.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_deauth_frame.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_disassoc_frame.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_message_queue.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_mlm_req_messages.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_mlm_rsp_messages.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_probe_req_frame.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_probe_rsp_frame.o \ + $(MAC_SRC_DIR)/pe/lim/lim_process_sme_req_messages.o \ + $(MAC_SRC_DIR)/pe/lim/lim_prop_exts_utils.o \ + $(MAC_SRC_DIR)/pe/lim/lim_scan_result_utils.o \ + $(MAC_SRC_DIR)/pe/lim/lim_security_utils.o \ + $(MAC_SRC_DIR)/pe/lim/lim_send_management_frames.o \ + $(MAC_SRC_DIR)/pe/lim/lim_send_messages.o \ + $(MAC_SRC_DIR)/pe/lim/lim_send_sme_rsp_messages.o \ + $(MAC_SRC_DIR)/pe/lim/lim_session.o \ + $(MAC_SRC_DIR)/pe/lim/lim_session_utils.o \ + $(MAC_SRC_DIR)/pe/lim/lim_sme_req_utils.o \ + $(MAC_SRC_DIR)/pe/lim/lim_timer_utils.o \ + $(MAC_SRC_DIR)/pe/lim/lim_trace.o \ + $(MAC_SRC_DIR)/pe/lim/lim_utils.o + +ifeq ($(CONFIG_QCA_IBSS_SUPPORT), y) +MAC_LIM_OBJS += $(MAC_SRC_DIR)/pe/lim/lim_ibss_peer_mgmt.o +endif + +ifeq ($(CONFIG_QCOM_TDLS), y) +MAC_LIM_OBJS += $(MAC_SRC_DIR)/pe/lim/lim_process_tdls.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_FILS), y) +MAC_LIM_OBJS += $(MAC_SRC_DIR)/pe/lim/lim_process_fils.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_NAN), y) +MAC_NDP_OBJS += $(MAC_SRC_DIR)/pe/nan/nan_datapath.o +endif + +ifeq ($(CONFIG_QCACLD_WLAN_LFR2), y) + MAC_LIM_OBJS += $(MAC_SRC_DIR)/pe/lim/lim_process_mlm_host_roam.o \ + $(MAC_SRC_DIR)/pe/lim/lim_send_frames_host_roam.o \ + $(MAC_SRC_DIR)/pe/lim/lim_roam_timer_utils.o \ + $(MAC_SRC_DIR)/pe/lim/lim_ft_preauth.o \ + $(MAC_SRC_DIR)/pe/lim/lim_reassoc_utils.o +endif + +MAC_SCH_OBJS := $(MAC_SRC_DIR)/pe/sch/sch_api.o \ + $(MAC_SRC_DIR)/pe/sch/sch_beacon_gen.o \ + $(MAC_SRC_DIR)/pe/sch/sch_beacon_process.o \ + $(MAC_SRC_DIR)/pe/sch/sch_message.o + +MAC_RRM_OBJS := $(MAC_SRC_DIR)/pe/rrm/rrm_api.o + +MAC_OBJS := $(MAC_CFG_OBJS) \ + $(MAC_DPH_OBJS) \ + $(MAC_LIM_OBJS) \ + $(MAC_SCH_OBJS) \ + $(MAC_RRM_OBJS) \ + $(MAC_NDP_OBJS) + +############ SAP ############ +SAP_DIR := core/sap +SAP_INC_DIR := $(SAP_DIR)/inc +SAP_SRC_DIR := $(SAP_DIR)/src + +SAP_INC := -I$(WLAN_ROOT)/$(SAP_INC_DIR) \ + -I$(WLAN_ROOT)/$(SAP_SRC_DIR) + +SAP_OBJS := $(SAP_SRC_DIR)/sap_api_link_cntl.o \ + $(SAP_SRC_DIR)/sap_ch_select.o \ + $(SAP_SRC_DIR)/sap_fsm.o \ + $(SAP_SRC_DIR)/sap_module.o + +############ CFG ############ +CFG_REL_DIR := $(WLAN_COMMON_ROOT)/cfg +CFG_DIR := $(WLAN_ROOT)/$(CFG_REL_DIR) +CFG_INC := \ + -I$(CFG_DIR)/inc \ + -I$(CFG_DIR)/dispatcher/inc \ + -I$(WLAN_ROOT)/components/cfg +CFG_OBJS := \ + $(CFG_REL_DIR)/src/cfg.o + +############ DFS ############ +DFS_DIR := $(WLAN_COMMON_ROOT)/umac/dfs +DFS_CORE_INC_DIR := $(DFS_DIR)/core/inc +DFS_CORE_SRC_DIR := $(DFS_DIR)/core/src + +DFS_DISP_INC_DIR := $(DFS_DIR)/dispatcher/inc +DFS_DISP_SRC_DIR := $(DFS_DIR)/dispatcher/src +DFS_TARGET_INC_DIR := $(WLAN_COMMON_ROOT)/target_if/dfs/inc +DFS_CMN_SERVICES_INC_DIR := $(WLAN_COMMON_ROOT)/umac/cmn_services/dfs/inc + +DFS_INC := -I$(WLAN_ROOT)/$(DFS_DISP_INC_DIR) \ + -I$(WLAN_ROOT)/$(DFS_TARGET_INC_DIR) \ + -I$(WLAN_ROOT)/$(DFS_CMN_SERVICES_INC_DIR) + +DFS_OBJS := $(DFS_CORE_SRC_DIR)/misc/dfs.o \ + $(DFS_CORE_SRC_DIR)/misc/dfs_cac.o \ + $(DFS_CORE_SRC_DIR)/misc/dfs_nol.o \ + $(DFS_CORE_SRC_DIR)/misc/dfs_random_chan_sel.o \ + $(DFS_CORE_SRC_DIR)/misc/dfs_process_radar_found_ind.o \ + $(DFS_DISP_SRC_DIR)/wlan_dfs_init_deinit_api.o \ + $(DFS_DISP_SRC_DIR)/wlan_dfs_lmac_api.o \ + $(DFS_DISP_SRC_DIR)/wlan_dfs_mlme_api.o \ + $(DFS_DISP_SRC_DIR)/wlan_dfs_tgt_api.o \ + $(DFS_DISP_SRC_DIR)/wlan_dfs_ucfg_api.o \ + $(DFS_DISP_SRC_DIR)/wlan_dfs_tgt_api.o \ + $(DFS_DISP_SRC_DIR)/wlan_dfs_utils_api.o \ + $(WLAN_COMMON_ROOT)/target_if/dfs/src/target_if_dfs.o + +ifeq ($(CONFIG_WLAN_FEATURE_DFS_OFFLOAD), y) +DFS_OBJS += $(WLAN_COMMON_ROOT)/target_if/dfs/src/target_if_dfs_full_offload.o \ + $(DFS_CORE_SRC_DIR)/misc/dfs_full_offload.o +else +DFS_OBJS += $(WLAN_COMMON_ROOT)/target_if/dfs/src/target_if_dfs_partial_offload.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_fcc_bin5.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_bindetects.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_debug.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_init.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_misc.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_phyerr_tlv.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_process_phyerr.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_process_radarevent.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_staggered.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_radar.o \ + $(DFS_CORE_SRC_DIR)/filtering/dfs_partial_offload_radar.o \ + $(DFS_CORE_SRC_DIR)/misc/dfs_filter_init.o +endif + +############ SME ############ +SME_DIR := core/sme +SME_INC_DIR := $(SME_DIR)/inc +SME_SRC_DIR := $(SME_DIR)/src + +SME_INC := -I$(WLAN_ROOT)/$(SME_INC_DIR) \ + -I$(WLAN_ROOT)/$(SME_SRC_DIR)/csr + +SME_CSR_OBJS := $(SME_SRC_DIR)/csr/csr_api_roam.o \ + $(SME_SRC_DIR)/csr/csr_api_scan.o \ + $(SME_SRC_DIR)/csr/csr_cmd_process.o \ + $(SME_SRC_DIR)/csr/csr_link_list.o \ + $(SME_SRC_DIR)/csr/csr_neighbor_roam.o \ + $(SME_SRC_DIR)/csr/csr_util.o \ + + +ifeq ($(CONFIG_QCACLD_WLAN_LFR2), y) +SME_CSR_OBJS += $(SME_SRC_DIR)/csr/csr_roam_preauth.o \ + $(SME_SRC_DIR)/csr/csr_host_scan_roam.o +endif + +SME_QOS_OBJS := $(SME_SRC_DIR)/qos/sme_qos.o + +SME_CMN_OBJS := $(SME_SRC_DIR)/common/sme_api.o \ + $(SME_SRC_DIR)/common/sme_ft_api.o \ + $(SME_SRC_DIR)/common/sme_power_save.o \ + $(SME_SRC_DIR)/common/sme_trace.o + +SME_RRM_OBJS := $(SME_SRC_DIR)/rrm/sme_rrm.o + +ifeq ($(CONFIG_QCACLD_FEATURE_NAN), y) +SME_NDP_OBJS += $(SME_SRC_DIR)/nan/nan_datapath_api.o +endif + +SME_OBJS := $(SME_CMN_OBJS) \ + $(SME_CSR_OBJS) \ + $(SME_QOS_OBJS) \ + $(SME_RRM_OBJS) \ + $(SME_NAN_OBJS) \ + $(SME_NDP_OBJS) + +############ NLINK ############ +NLINK_DIR := $(WLAN_COMMON_ROOT)/utils/nlink +NLINK_INC_DIR := $(NLINK_DIR)/inc +NLINK_SRC_DIR := $(NLINK_DIR)/src + +NLINK_INC := -I$(WLAN_ROOT)/$(NLINK_INC_DIR) +NLINK_OBJS := $(NLINK_SRC_DIR)/wlan_nlink_srv.o + +############ PTT ############ +PTT_DIR := $(WLAN_COMMON_ROOT)/utils/ptt +PTT_INC_DIR := $(PTT_DIR)/inc +PTT_SRC_DIR := $(PTT_DIR)/src + +PTT_INC := -I$(WLAN_ROOT)/$(PTT_INC_DIR) +PTT_OBJS := $(PTT_SRC_DIR)/wlan_ptt_sock_svc.o + +############ WLAN_LOGGING ############ +WLAN_LOGGING_DIR := $(WLAN_COMMON_ROOT)/utils/logging +WLAN_LOGGING_INC_DIR := $(WLAN_LOGGING_DIR)/inc +WLAN_LOGGING_SRC_DIR := $(WLAN_LOGGING_DIR)/src + +WLAN_LOGGING_INC := -I$(WLAN_ROOT)/$(WLAN_LOGGING_INC_DIR) +WLAN_LOGGING_OBJS := $(WLAN_LOGGING_SRC_DIR)/wlan_logging_sock_svc.o \ + $(WLAN_LOGGING_SRC_DIR)/wlan_roam_debug.o + +############ SYS ############ +SYS_DIR := core/mac/src/sys + +SYS_INC := -I$(WLAN_ROOT)/$(SYS_DIR)/common/inc \ + -I$(WLAN_ROOT)/$(SYS_DIR)/legacy/src/platform/inc \ + -I$(WLAN_ROOT)/$(SYS_DIR)/legacy/src/system/inc \ + -I$(WLAN_ROOT)/$(SYS_DIR)/legacy/src/utils/inc + +SYS_COMMON_SRC_DIR := $(SYS_DIR)/common/src +SYS_LEGACY_SRC_DIR := $(SYS_DIR)/legacy/src +SYS_OBJS := $(SYS_COMMON_SRC_DIR)/wlan_qct_sys.o \ + $(SYS_LEGACY_SRC_DIR)/platform/src/sys_wrapper.o \ + $(SYS_LEGACY_SRC_DIR)/system/src/mac_init_api.o \ + $(SYS_LEGACY_SRC_DIR)/system/src/sys_entry_func.o \ + $(SYS_LEGACY_SRC_DIR)/utils/src/dot11f.o \ + $(SYS_LEGACY_SRC_DIR)/utils/src/mac_trace.o \ + $(SYS_LEGACY_SRC_DIR)/utils/src/parser_api.o \ + $(SYS_LEGACY_SRC_DIR)/utils/src/utils_parser.o + +############ Qca-wifi-host-cmn ############ +QDF_OS_DIR := qdf +QDF_OS_INC_DIR := $(QDF_OS_DIR)/inc +QDF_OS_SRC_DIR := $(QDF_OS_DIR)/src +QDF_OS_LINUX_SRC_DIR := $(QDF_OS_DIR)/linux/src +QDF_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(QDF_OS_SRC_DIR) +QDF_LINUX_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(QDF_OS_LINUX_SRC_DIR) +QDF_TEST_DIR := $(QDF_OS_DIR)/test +QDF_TEST_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(QDF_TEST_DIR) + +QDF_INC := \ + -I$(WLAN_COMMON_INC)/$(QDF_OS_INC_DIR) \ + -I$(WLAN_COMMON_INC)/$(QDF_OS_LINUX_SRC_DIR) \ + -I$(WLAN_COMMON_INC)/$(QDF_TEST_DIR) + +QDF_OBJS := \ + $(QDF_LINUX_OBJ_DIR)/qdf_crypto.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_defer.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_delayed_work.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_event.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_file.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_func_tracker.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_idr.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_list.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_lock.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_mc_timer.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_mem.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_nbuf.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_periodic_work.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_status.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_threads.o \ + $(QDF_LINUX_OBJ_DIR)/qdf_trace.o \ + $(QDF_OBJ_DIR)/qdf_flex_mem.o \ + $(QDF_OBJ_DIR)/qdf_parse.o \ + $(QDF_OBJ_DIR)/qdf_platform.o \ + $(QDF_OBJ_DIR)/qdf_str.o \ + $(QDF_OBJ_DIR)/qdf_talloc.o \ + $(QDF_OBJ_DIR)/qdf_types.o \ + +ifeq ($(CONFIG_WLAN_DEBUGFS), y) +QDF_OBJS += $(QDF_LINUX_OBJ_DIR)/qdf_debugfs.o +endif + +ifeq ($(CONFIG_WLAN_STREAMFS), y) +QDF_OBJS += $(QDF_LINUX_OBJ_DIR)/qdf_streamfs.o +endif + +ifeq ($(CONFIG_IPA_OFFLOAD), y) +QDF_OBJS += $(QDF_LINUX_OBJ_DIR)/qdf_ipa.o +endif + +# enable CPU hotplug support if SMP is enabled +ifeq ($(CONFIG_SMP), y) + QDF_OBJS += $(QDF_OBJ_DIR)/qdf_cpuhp.o + QDF_OBJS += $(QDF_LINUX_OBJ_DIR)/qdf_cpuhp.o +endif + +ifeq ($(CONFIG_LEAK_DETECTION), y) + QDF_OBJS += $(QDF_OBJ_DIR)/qdf_debug_domain.o + QDF_OBJS += $(QDF_OBJ_DIR)/qdf_tracker.o +endif + +ifeq ($(CONFIG_WLAN_HANG_EVENT), y) + QDF_OBJS += $(QDF_OBJ_DIR)/qdf_notifier.o +endif + +ifeq ($(CONFIG_QDF_TEST), y) + QDF_OBJS += $(QDF_TEST_OBJ_DIR)/qdf_delayed_work_test.o + QDF_OBJS += $(QDF_TEST_OBJ_DIR)/qdf_hashtable_test.o + QDF_OBJS += $(QDF_TEST_OBJ_DIR)/qdf_periodic_work_test.o + QDF_OBJS += $(QDF_TEST_OBJ_DIR)/qdf_ptr_hash_test.o + QDF_OBJS += $(QDF_TEST_OBJ_DIR)/qdf_slist_test.o + QDF_OBJS += $(QDF_TEST_OBJ_DIR)/qdf_talloc_test.o + QDF_OBJS += $(QDF_TEST_OBJ_DIR)/qdf_tracker_test.o + QDF_OBJS += $(QDF_TEST_OBJ_DIR)/qdf_types_test.o +endif + +ifeq ($(CONFIG_WLAN_HANG_EVENT), y) + QDF_OBJS += $(QDF_OBJ_DIR)/qdf_hang_event_notifier.o +endif + +cppflags-$(CONFIG_TALLOC_DEBUG) += -DWLAN_TALLOC_DEBUG +cppflags-$(CONFIG_QDF_TEST) += -DWLAN_DELAYED_WORK_TEST +cppflags-$(CONFIG_QDF_TEST) += -DWLAN_HASHTABLE_TEST +cppflags-$(CONFIG_QDF_TEST) += -DWLAN_PERIODIC_WORK_TEST +cppflags-$(CONFIG_QDF_TEST) += -DWLAN_PTR_HASH_TEST +cppflags-$(CONFIG_QDF_TEST) += -DWLAN_SLIST_TEST +cppflags-$(CONFIG_QDF_TEST) += -DWLAN_TALLOC_TEST +cppflags-$(CONFIG_QDF_TEST) += -DWLAN_TRACKER_TEST +cppflags-$(CONFIG_QDF_TEST) += -DWLAN_TYPES_TEST +cppflags-$(CONFIG_WLAN_HANG_EVENT) += -DWLAN_HANG_EVENT + +############ WBUFF ############ +WBUFF_OS_DIR := wbuff +WBUFF_OS_INC_DIR := $(WBUFF_OS_DIR)/inc +WBUFF_OS_SRC_DIR := $(WBUFF_OS_DIR)/src +WBUFF_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(WBUFF_OS_SRC_DIR) + +WBUFF_INC := -I$(WLAN_COMMON_INC)/$(WBUFF_OS_INC_DIR) \ + +ifeq ($(CONFIG_WLAN_WBUFF), y) +WBUFF_OBJS += $(WBUFF_OBJ_DIR)/wbuff.o +endif + +##########QAL ####### +QAL_OS_DIR := qal +QAL_OS_INC_DIR := $(QAL_OS_DIR)/inc +QAL_OS_LINUX_SRC_DIR := $(QAL_OS_DIR)/linux/src + +QAL_INC := -I$(WLAN_COMMON_INC)/$(QAL_OS_INC_DIR) \ + -I$(WLAN_COMMON_INC)/$(QAL_OS_LINUX_SRC_DIR) + + +##########OS_IF ####### +OS_IF_DIR := $(WLAN_COMMON_ROOT)/os_if + +OS_IF_INC += -I$(WLAN_COMMON_INC)/os_if/linux \ + -I$(WLAN_COMMON_INC)/os_if/linux/scan/inc \ + -I$(WLAN_COMMON_INC)/os_if/linux/spectral/inc \ + -I$(WLAN_COMMON_INC)/os_if/linux/crypto/inc \ + -I$(WLAN_COMMON_INC)/os_if/linux/gpio/inc + +OS_IF_OBJ += $(OS_IF_DIR)/linux/wlan_osif_request_manager.o \ + $(OS_IF_DIR)/linux/crypto/src/wlan_nl_to_crypto_params.o + +CONFIG_CRYPTO_COMPONENT := y + +ifeq ($(CONFIG_CRYPTO_COMPONENT), y) +OS_IF_OBJ += $(OS_IF_DIR)/linux/crypto/src/wlan_cfg80211_crypto.o +endif + +############ UMAC_DISP ############ +UMAC_DISP_DIR := umac/global_umac_dispatcher/lmac_if +UMAC_DISP_INC_DIR := $(UMAC_DISP_DIR)/inc +UMAC_DISP_SRC_DIR := $(UMAC_DISP_DIR)/src +UMAC_DISP_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_DISP_SRC_DIR) + +UMAC_DISP_INC := -I$(WLAN_COMMON_INC)/$(UMAC_DISP_INC_DIR) + +UMAC_DISP_OBJS := $(UMAC_DISP_OBJ_DIR)/wlan_lmac_if.o + +############# UMAC_SCAN ############ +UMAC_SCAN_DIR := umac/scan +UMAC_SCAN_DISP_INC_DIR := $(UMAC_SCAN_DIR)/dispatcher/inc +UMAC_SCAN_CORE_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_SCAN_DIR)/core/src +UMAC_SCAN_DISP_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_SCAN_DIR)/dispatcher/src +UMAC_TARGET_SCAN_INC := -I$(WLAN_COMMON_INC)/target_if/scan/inc + +UMAC_SCAN_INC := -I$(WLAN_COMMON_INC)/$(UMAC_SCAN_DISP_INC_DIR) +UMAC_SCAN_OBJS := $(UMAC_SCAN_CORE_DIR)/wlan_scan_cache_db.o \ + $(UMAC_SCAN_CORE_DIR)/wlan_scan_11d.o \ + $(UMAC_SCAN_CORE_DIR)/wlan_scan_bss_score.o \ + $(UMAC_SCAN_CORE_DIR)/wlan_scan_filter.o \ + $(UMAC_SCAN_CORE_DIR)/wlan_scan_main.o \ + $(UMAC_SCAN_CORE_DIR)/wlan_scan_manager.o \ + $(UMAC_SCAN_DISP_DIR)/wlan_scan_tgt_api.o \ + $(UMAC_SCAN_DISP_DIR)/wlan_scan_ucfg_api.o \ + $(UMAC_SCAN_DISP_DIR)/wlan_scan_api.o \ + $(UMAC_SCAN_DISP_DIR)/wlan_scan_utils_api.o \ + $(WLAN_COMMON_ROOT)/os_if/linux/scan/src/wlan_cfg80211_scan.o \ + $(WLAN_COMMON_ROOT)/os_if/linux/wlan_cfg80211.o \ + $(WLAN_COMMON_ROOT)/target_if/scan/src/target_if_scan.o + +ifeq ($(CONFIG_FEATURE_WLAN_EXTSCAN), y) +UMAC_SCAN_OBJS += $(UMAC_SCAN_DISP_DIR)/wlan_extscan_api.o +endif + +############# UMAC_SPECTRAL_SCAN ############ +UMAC_SPECTRAL_DIR := spectral +UMAC_SPECTRAL_DISP_INC_DIR := $(UMAC_SPECTRAL_DIR)/dispatcher/inc +UMAC_SPECTRAL_CORE_INC_DIR := $(UMAC_SPECTRAL_DIR)/core +UMAC_SPECTRAL_CORE_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_SPECTRAL_DIR)/core +UMAC_SPECTRAL_DISP_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_SPECTRAL_DIR)/dispatcher/src +UMAC_TARGET_SPECTRAL_INC := -I$(WLAN_COMMON_INC)/target_if/spectral + +UMAC_SPECTRAL_INC := -I$(WLAN_COMMON_INC)/$(UMAC_SPECTRAL_DISP_INC_DIR) \ + -I$(WLAN_COMMON_INC)/$(UMAC_SPECTRAL_CORE_INC_DIR) \ + -I$(WLAN_COMMON_INC)/target_if/direct_buf_rx/inc +ifeq ($(CONFIG_WLAN_CONV_SPECTRAL_ENABLE),y) +UMAC_SPECTRAL_OBJS := $(UMAC_SPECTRAL_CORE_DIR)/spectral_offload.o \ + $(UMAC_SPECTRAL_CORE_DIR)/spectral_common.o \ + $(UMAC_SPECTRAL_DISP_DIR)/wlan_spectral_ucfg_api.o \ + $(UMAC_SPECTRAL_DISP_DIR)/wlan_spectral_utils_api.o \ + $(UMAC_SPECTRAL_DISP_DIR)/wlan_spectral_tgt_api.o \ + $(WLAN_COMMON_ROOT)/os_if/linux/spectral/src/wlan_cfg80211_spectral.o \ + $(WLAN_COMMON_ROOT)/os_if/linux/spectral/src/os_if_spectral_netlink.o \ + $(WLAN_COMMON_ROOT)/target_if/spectral/target_if_spectral_netlink.o \ + $(WLAN_COMMON_ROOT)/target_if/spectral/target_if_spectral_phyerr.o \ + $(WLAN_COMMON_ROOT)/target_if/spectral/target_if_spectral.o \ + $(WLAN_COMMON_ROOT)/target_if/spectral/target_if_spectral_sim.o +endif +############# WLAN_CFR ############ +WLAN_CFR_DIR := umac/cfr +WLAN_CFR_DISP_INC_DIR := $(WLAN_CFR_DIR)/dispatcher/inc +WLAN_CFR_CORE_INC_DIR := $(WLAN_CFR_DIR)/core/inc +WLAN_CFR_CORE_DIR := $(WLAN_COMMON_ROOT)/$(WLAN_CFR_DIR)/core/src +WLAN_CFR_DISP_DIR := $(WLAN_COMMON_ROOT)/$(WLAN_CFR_DIR)/dispatcher/src +WLAN_CFR_TARGET_INC_DIR := target_if/cfr/inc + +WLAN_CFR_INC := -I$(WLAN_COMMON_INC)/$(WLAN_CFR_DISP_INC_DIR) \ + -I$(WLAN_COMMON_INC)/$(WLAN_CFR_CORE_INC_DIR) \ + -I$(WLAN_COMMON_INC)/$(WLAN_CFR_TARGET_INC_DIR) +ifeq ($(CONFIG_WLAN_CFR_ENABLE),y) +WLAN_CFR_OBJS := $(WLAN_CFR_CORE_DIR)/cfr_common.o \ + $(WLAN_CFR_DISP_DIR)/wlan_cfr_tgt_api.o \ + $(WLAN_CFR_DISP_DIR)/wlan_cfr_ucfg_api.o \ + $(WLAN_CFR_DISP_DIR)/wlan_cfr_utils_api.o \ + $(WLAN_COMMON_ROOT)/target_if/cfr/src/target_if_cfr.o \ + $(WLAN_COMMON_ROOT)/target_if/cfr/src/target_if_cfr_6018.o \ + $(WLAN_COMMON_ROOT)/target_if/cfr/src/target_if_cfr_6490.o +endif +############# GPIO_CFG ############ +UMAC_GPIO_DIR := gpio +UMAC_GPIO_DISP_INC_DIR := $(UMAC_GPIO_DIR)/dispatcher/inc +UMAC_GPIO_CORE_INC_DIR := $(UMAC_GPIO_DIR)/core/inc +UMAC_GPIO_CORE_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_GPIO_DIR)/core/src +UMAC_GPIO_DISP_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_GPIO_DIR)/dispatcher/src +UMAC_TARGET_GPIO_INC := -I$(WLAN_COMMON_INC)/target_if/gpio + +UMAC_GPIO_INC := -I$(WLAN_COMMON_INC)/$(UMAC_GPIO_DISP_INC_DIR) \ + -I$(WLAN_COMMON_INC)/$(UMAC_GPIO_CORE_INC_DIR) \ + -I$(WLAN_COMMON_INC)/$(UMAC_TARGET_GPIO_INC) +ifeq ($(CONFIG_FEATURE_GPIO_CFG),y) +UMAC_GPIO_OBJS := $(UMAC_GPIO_DISP_DIR)/wlan_gpio_tgt_api.o \ + $(UMAC_GPIO_DISP_DIR)/wlan_gpio_ucfg_api.o \ + $(UMAC_GPIO_CORE_DIR)/wlan_gpio_api.o \ + $(WLAN_COMMON_ROOT)/os_if/linux/gpio/src/wlan_cfg80211_gpio.o \ + $(WLAN_COMMON_ROOT)/target_if/gpio/target_if_gpio.o +endif +############# UMAC_GREEN_AP ############ +UMAC_GREEN_AP_DIR := umac/green_ap +UMAC_GREEN_AP_DISP_INC_DIR := $(UMAC_GREEN_AP_DIR)/dispatcher/inc +UMAC_GREEN_AP_CORE_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_GREEN_AP_DIR)/core/src +UMAC_GREEN_AP_DISP_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_GREEN_AP_DIR)/dispatcher/src +UMAC_TARGET_GREEN_AP_INC := -I$(WLAN_COMMON_INC)/target_if/green_ap/inc + +UMAC_GREEN_AP_INC := -I$(WLAN_COMMON_INC)/$(UMAC_GREEN_AP_DISP_INC_DIR) +UMAC_GREEN_AP_OBJS := $(UMAC_GREEN_AP_CORE_DIR)/wlan_green_ap_main.o \ + $(UMAC_GREEN_AP_DISP_DIR)/wlan_green_ap_api.o \ + $(UMAC_GREEN_AP_DISP_DIR)/wlan_green_ap_ucfg_api.o \ + $(WLAN_COMMON_ROOT)/target_if/green_ap/src/target_if_green_ap.o + +############# WLAN_CONV_CRYPTO_SUPPORTED ############ +UMAC_CRYPTO_DIR := umac/cmn_services/crypto +UMAC_CRYPTO_CORE_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_CRYPTO_DIR)/src +UMAC_CRYPTO_INC := -I$(WLAN_COMMON_INC)/$(UMAC_CRYPTO_DIR)/inc \ + -I$(WLAN_COMMON_INC)/$(UMAC_CRYPTO_DIR)/src +UMAC_CRYPTO_OBJS := $(UMAC_CRYPTO_CORE_DIR)/wlan_crypto_global_api.o \ + $(UMAC_CRYPTO_CORE_DIR)/wlan_crypto_ucfg_api.o \ + $(UMAC_CRYPTO_CORE_DIR)/wlan_crypto_main.o \ + $(UMAC_CRYPTO_CORE_DIR)/wlan_crypto_obj_mgr.o \ + $(UMAC_CRYPTO_CORE_DIR)/wlan_crypto_param_handling.o + +############# FTM CORE ############ +FTM_CORE_DIR := ftm +TARGET_IF_FTM_DIR := target_if/ftm +OS_IF_LINUX_FTM_DIR := os_if/linux/ftm + +FTM_CORE_SRC := $(WLAN_COMMON_ROOT)/$(FTM_CORE_DIR)/core/src +FTM_DISP_SRC := $(WLAN_COMMON_ROOT)/$(FTM_CORE_DIR)/dispatcher/src +TARGET_IF_FTM_SRC := $(WLAN_COMMON_ROOT)/$(TARGET_IF_FTM_DIR)/src +OS_IF_FTM_SRC := $(WLAN_COMMON_ROOT)/$(OS_IF_LINUX_FTM_DIR)/src + +FTM_CORE_INC := $(WLAN_COMMON_INC)/$(FTM_CORE_DIR)/core/src +FTM_DISP_INC := $(WLAN_COMMON_INC)/$(FTM_CORE_DIR)/dispatcher/inc +TARGET_IF_FTM_INC := $(WLAN_COMMON_INC)/$(TARGET_IF_FTM_DIR)/inc +OS_IF_FTM_INC := $(WLAN_COMMON_INC)/$(OS_IF_LINUX_FTM_DIR)/inc + +FTM_INC := -I$(FTM_DISP_INC) \ + -I$(FTM_CORE_INC) \ + -I$(OS_IF_FTM_INC) \ + -I$(TARGET_IF_FTM_INC) + +ifeq ($(CONFIG_QCA_WIFI_FTM), y) +FTM_OBJS := $(FTM_DISP_SRC)/wlan_ftm_init_deinit.o \ + $(FTM_DISP_SRC)/wlan_ftm_ucfg_api.o \ + $(FTM_CORE_SRC)/wlan_ftm_svc.o \ + $(TARGET_IF_FTM_SRC)/target_if_ftm.o + +ifeq ($(QCA_WIFI_FTM_NL80211), y) +FTM_OBJS += $(OS_IF_FTM_SRC)/wlan_cfg80211_ftm.o +endif + +ifeq ($(CONFIG_LINUX_QCMBR), y) +FTM_OBJS += $(OS_IF_FTM_SRC)/wlan_ioctl_ftm.o +endif + +endif + +############# UMAC_CMN_SERVICES ############ +UMAC_COMMON_INC := -I$(WLAN_COMMON_INC)/umac/cmn_services/cmn_defs/inc \ + -I$(WLAN_COMMON_INC)/umac/cmn_services/utils/inc +UMAC_COMMON_OBJS := $(WLAN_COMMON_ROOT)/umac/cmn_services/utils/src/wlan_utility.o + +ifeq ($(CONFIG_WLAN_LRO), y) +QDF_OBJS += $(QDF_LINUX_OBJ_DIR)/qdf_lro.o +endif + +############ CDS (Connectivity driver services) ############ +CDS_DIR := core/cds +CDS_INC_DIR := $(CDS_DIR)/inc +CDS_SRC_DIR := $(CDS_DIR)/src + +CDS_INC := -I$(WLAN_ROOT)/$(CDS_INC_DIR) \ + -I$(WLAN_ROOT)/$(CDS_SRC_DIR) + +CDS_OBJS := $(CDS_SRC_DIR)/cds_api.o \ + $(CDS_SRC_DIR)/cds_reg_service.o \ + $(CDS_SRC_DIR)/cds_packet.o \ + $(CDS_SRC_DIR)/cds_regdomain.o \ + $(CDS_SRC_DIR)/cds_sched.o \ + $(CDS_SRC_DIR)/cds_utils.o + + +###### UMAC OBJMGR ######## +UMAC_OBJMGR_DIR := $(WLAN_COMMON_ROOT)/umac/cmn_services/obj_mgr + +UMAC_OBJMGR_INC := -I$(WLAN_COMMON_INC)/umac/cmn_services/obj_mgr/inc \ + -I$(WLAN_COMMON_INC)/umac/cmn_services/obj_mgr/src \ + -I$(WLAN_COMMON_INC)/umac/cmn_services/inc \ + -I$(WLAN_COMMON_INC)/umac/global_umac_dispatcher/lmac_if/inc + +UMAC_OBJMGR_OBJS := $(UMAC_OBJMGR_DIR)/src/wlan_objmgr_global_obj.o \ + $(UMAC_OBJMGR_DIR)/src/wlan_objmgr_pdev_obj.o \ + $(UMAC_OBJMGR_DIR)/src/wlan_objmgr_peer_obj.o \ + $(UMAC_OBJMGR_DIR)/src/wlan_objmgr_psoc_obj.o \ + $(UMAC_OBJMGR_DIR)/src/wlan_objmgr_vdev_obj.o + +ifeq ($(CONFIG_WLAN_OBJMGR_DEBUG), y) +UMAC_OBJMGR_OBJS += $(UMAC_OBJMGR_DIR)/src/wlan_objmgr_debug.o +endif + +########### UMAC MGMT TXRX ########## +UMAC_MGMT_TXRX_DIR := $(WLAN_COMMON_ROOT)/umac/cmn_services/mgmt_txrx + +UMAC_MGMT_TXRX_INC := -I$(WLAN_COMMON_INC)/umac/cmn_services/mgmt_txrx/dispatcher/inc \ + +UMAC_MGMT_TXRX_OBJS := $(UMAC_MGMT_TXRX_DIR)/core/src/wlan_mgmt_txrx_main.o \ + $(UMAC_MGMT_TXRX_DIR)/dispatcher/src/wlan_mgmt_txrx_utils_api.o \ + $(UMAC_MGMT_TXRX_DIR)/dispatcher/src/wlan_mgmt_txrx_tgt_api.o + +########## POWER MANAGEMENT OFFLOADS (PMO) ########## +PMO_DIR := components/pmo +PMO_INC := -I$(WLAN_ROOT)/$(PMO_DIR)/core/inc \ + -I$(WLAN_ROOT)/$(PMO_DIR)/core/src \ + -I$(WLAN_ROOT)/$(PMO_DIR)/dispatcher/inc \ + -I$(WLAN_ROOT)/$(PMO_DIR)/dispatcher/src \ + +ifeq ($(CONFIG_POWER_MANAGEMENT_OFFLOAD), y) +PMO_OBJS := $(PMO_DIR)/core/src/wlan_pmo_main.o \ + $(PMO_DIR)/core/src/wlan_pmo_apf.o \ + $(PMO_DIR)/core/src/wlan_pmo_arp.o \ + $(PMO_DIR)/core/src/wlan_pmo_gtk.o \ + $(PMO_DIR)/core/src/wlan_pmo_mc_addr_filtering.o \ + $(PMO_DIR)/core/src/wlan_pmo_static_config.o \ + $(PMO_DIR)/core/src/wlan_pmo_wow.o \ + $(PMO_DIR)/core/src/wlan_pmo_lphb.o \ + $(PMO_DIR)/core/src/wlan_pmo_suspend_resume.o \ + $(PMO_DIR)/core/src/wlan_pmo_hw_filter.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_obj_mgmt_api.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_ucfg_api.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_arp.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_gtk.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_wow.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_static_config.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_mc_addr_filtering.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_lphb.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_suspend_resume.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_hw_filter.o \ + +ifeq ($(CONFIG_WLAN_FEATURE_PACKET_FILTERING), y) +PMO_OBJS += $(PMO_DIR)/core/src/wlan_pmo_pkt_filter.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_pkt_filter.o +endif +endif + +ifeq ($(CONFIG_WLAN_NS_OFFLOAD), y) +PMO_OBJS += $(PMO_DIR)/core/src/wlan_pmo_ns.o \ + $(PMO_DIR)/dispatcher/src/wlan_pmo_tgt_ns.o +endif + +########## DISA (ENCRYPTION TEST) ########## + +DISA_DIR := components/disa +DISA_INC := -I$(WLAN_ROOT)/$(DISA_DIR)/core/inc \ + -I$(WLAN_ROOT)/$(DISA_DIR)/dispatcher/inc + +ifeq ($(CONFIG_WLAN_FEATURE_DISA), y) +DISA_OBJS := $(DISA_DIR)/core/src/wlan_disa_main.o \ + $(DISA_DIR)/dispatcher/src/wlan_disa_obj_mgmt_api.o \ + $(DISA_DIR)/dispatcher/src/wlan_disa_tgt_api.o \ + $(DISA_DIR)/dispatcher/src/wlan_disa_ucfg_api.o +endif + +######## OCB ############## +OCB_DIR := components/ocb +OCB_INC := -I$(WLAN_ROOT)/$(OCB_DIR)/core/inc \ + -I$(WLAN_ROOT)/$(OCB_DIR)/dispatcher/inc + +ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) +OCB_OBJS := $(OCB_DIR)/dispatcher/src/wlan_ocb_ucfg_api.o \ + $(OCB_DIR)/dispatcher/src/wlan_ocb_tgt_api.o \ + $(OCB_DIR)/core/src/wlan_ocb_main.o +endif + +######## IPA ############## +IPA_DIR := components/ipa +IPA_INC := -I$(WLAN_ROOT)/$(IPA_DIR)/core/inc \ + -I$(WLAN_ROOT)/$(IPA_DIR)/dispatcher/inc + +ifeq ($(CONFIG_IPA_OFFLOAD), y) +IPA_OBJS := $(IPA_DIR)/dispatcher/src/wlan_ipa_ucfg_api.o \ + $(IPA_DIR)/dispatcher/src/wlan_ipa_obj_mgmt_api.o \ + $(IPA_DIR)/dispatcher/src/wlan_ipa_tgt_api.o \ + $(IPA_DIR)/core/src/wlan_ipa_main.o \ + $(IPA_DIR)/core/src/wlan_ipa_core.o \ + $(IPA_DIR)/core/src/wlan_ipa_stats.o \ + $(IPA_DIR)/core/src/wlan_ipa_rm.o +endif + +######## FWOL ########## +FWOL_CORE_INC := components/fw_offload/core/inc +FWOL_CORE_SRC := components/fw_offload/core/src +FWOL_DISPATCHER_INC := components/fw_offload/dispatcher/inc +FWOL_DISPATCHER_SRC := components/fw_offload/dispatcher/src +FWOL_TARGET_IF_INC := components/target_if/fw_offload/inc +FWOL_TARGET_IF_SRC := components/target_if/fw_offload/src +FWOL_OS_IF_INC := os_if/fw_offload/inc +FWOL_OS_IF_SRC := os_if/fw_offload/src + +FWOL_INC := -I$(WLAN_ROOT)/$(FWOL_CORE_INC) \ + -I$(WLAN_ROOT)/$(FWOL_DISPATCHER_INC) \ + -I$(WLAN_ROOT)/$(FWOL_TARGET_IF_INC) \ + -I$(WLAN_ROOT)/$(FWOL_OS_IF_INC) + +FWOL_OBJS := $(FWOL_CORE_SRC)/wlan_fw_offload_main.o \ + $(FWOL_DISPATCHER_SRC)/wlan_fwol_ucfg_api.o \ + $(FWOL_DISPATCHER_SRC)/wlan_fwol_tgt_api.o \ + $(FWOL_TARGET_IF_SRC)/target_if_fwol.o \ + $(FWOL_OS_IF_SRC)/os_if_fwol.o + +######## SM FRAMEWORK ############## +UMAC_SM_DIR := umac/cmn_services/sm_engine +UMAC_SM_INC := -I$(WLAN_COMMON_INC)/$(UMAC_SM_DIR)/inc + +UMAC_SM_OBJS := $(WLAN_COMMON_ROOT)/$(UMAC_SM_DIR)/src/wlan_sm_engine.o + +ifeq ($(CONFIG_SM_ENG_HIST), y) +UMAC_SM_OBJS += $(WLAN_COMMON_ROOT)/$(UMAC_SM_DIR)/src/wlan_sm_engine_dbg.o +endif + +######## COMMON MLME ############## +UMAC_MLME_INC := -I$(WLAN_COMMON_INC)/umac/mlme \ + -I$(WLAN_COMMON_INC)/umac/mlme/mlme_objmgr/dispatcher/inc \ + -I$(WLAN_COMMON_INC)/umac/mlme/vdev_mgr/dispatcher/inc \ + -I$(WLAN_COMMON_INC)/umac/mlme/pdev_mgr/dispatcher/inc \ + -I$(WLAN_COMMON_INC)/umac/mlme/psoc_mgr/dispatcher/inc + +UMAC_MLME_OBJS := $(WLAN_COMMON_ROOT)/umac/mlme/mlme_objmgr/dispatcher/src/wlan_vdev_mlme_main.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/vdev_mgr/core/src/vdev_mlme_sm.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mlme_api.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/vdev_mgr/core/src/vdev_mgr_ops.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_tgt_if_rx_api.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_tgt_if_tx_api.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_ucfg_api.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_utils_api.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/mlme_objmgr/dispatcher/src/wlan_cmn_mlme_main.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/mlme_objmgr/dispatcher/src/wlan_pdev_mlme_main.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/pdev_mgr/dispatcher/src/wlan_pdev_mlme_api.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/mlme_objmgr/dispatcher/src/wlan_psoc_mlme_main.o \ + $(WLAN_COMMON_ROOT)/umac/mlme/psoc_mgr/dispatcher/src/wlan_psoc_mlme_api.o + +######## MLME ############## +MLME_DIR := components/mlme +MLME_INC := -I$(WLAN_ROOT)/$(MLME_DIR)/core/inc \ + -I$(WLAN_ROOT)/$(MLME_DIR)/dispatcher/inc + +MLME_OBJS := $(MLME_DIR)/core/src/wlan_mlme_main.o \ + $(MLME_DIR)/dispatcher/src/wlan_mlme_api.o \ + $(MLME_DIR)/dispatcher/src/wlan_mlme_ucfg_api.o + +MLME_OBJS += $(MLME_DIR)/core/src/wlan_mlme_vdev_mgr_interface.o + + +####### BLACKLIST_MGR ######## + +BLM_DIR := components/blacklist_mgr +BLM_INC := -I$(WLAN_ROOT)/$(BLM_DIR)/core/inc \ + -I$(WLAN_ROOT)/$(BLM_DIR)/dispatcher/inc +ifeq ($(CONFIG_FEATURE_BLACKLIST_MGR), y) +BLM_OBJS := $(BLM_DIR)/core/src/wlan_blm_main.o \ + $(BLM_DIR)/core/src/wlan_blm_core.o \ + $(BLM_DIR)/dispatcher/src/wlan_blm_ucfg_api.o \ + $(BLM_DIR)/dispatcher/src/wlan_blm_tgt_api.o +endif +########## ACTION OUI ########## + +ACTION_OUI_DIR := components/action_oui +ACTION_OUI_INC := -I$(WLAN_ROOT)/$(ACTION_OUI_DIR)/core/inc \ + -I$(WLAN_ROOT)/$(ACTION_OUI_DIR)/dispatcher/inc + +ifeq ($(CONFIG_WLAN_FEATURE_ACTION_OUI), y) +ACTION_OUI_OBJS := $(ACTION_OUI_DIR)/core/src/wlan_action_oui_main.o \ + $(ACTION_OUI_DIR)/core/src/wlan_action_oui_parse.o \ + $(ACTION_OUI_DIR)/dispatcher/src/wlan_action_oui_tgt_api.o \ + $(ACTION_OUI_DIR)/dispatcher/src/wlan_action_oui_ucfg_api.o +endif + +######## PACKET CAPTURE ######## + +PKT_CAPTURE_DIR := components/pkt_capture +PKT_CAPTURE_INC := -I$(WLAN_ROOT)/$(PKT_CAPTURE_DIR)/core/inc \ + -I$(WLAN_ROOT)/$(PKT_CAPTURE_DIR)/dispatcher/inc + +ifeq ($(CONFIG_WLAN_FEATURE_PKT_CAPTURE), y) +PKT_CAPTURE_OBJS := $(PKT_CAPTURE_DIR)/core/src/wlan_pkt_capture_main.o \ + $(PKT_CAPTURE_DIR)/dispatcher/src/wlan_pkt_capture_ucfg_api.o +endif + +########## FTM TIME SYNC ########## + +FTM_TIME_SYNC_DIR := components/ftm_time_sync +FTM_TIME_SYNC_INC := -I$(WLAN_ROOT)/$(FTM_TIME_SYNC_DIR)/core/inc \ + -I$(WLAN_ROOT)/$(FTM_TIME_SYNC_DIR)/dispatcher/inc + +ifeq ($(CONFIG_FEATURE_WLAN_TIME_SYNC_FTM), y) +FTM_TIME_SYNC_OBJS := $(FTM_TIME_SYNC_DIR)/core/src/ftm_time_sync_main.o \ + $(FTM_TIME_SYNC_DIR)/dispatcher/src/ftm_time_sync_ucfg_api.o +endif + +########## CLD TARGET_IF ####### +CLD_TARGET_IF_DIR := components/target_if + +CLD_TARGET_IF_INC := -I$(WLAN_ROOT)/$(CLD_TARGET_IF_DIR)/pmo/inc \ + -I$(WLAN_ROOT)/$(CLD_TARGET_IF_DIR)/pmo/src + +ifeq ($(CONFIG_POWER_MANAGEMENT_OFFLOAD), y) +CLD_TARGET_IF_OBJ := $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_arp.o \ + $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_gtk.o \ + $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_hw_filter.o \ + $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_lphb.o \ + $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_main.o \ + $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_mc_addr_filtering.o \ + $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_static_config.o \ + $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_suspend_resume.o \ + $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_wow.o +ifeq ($(CONFIG_WLAN_NS_OFFLOAD), y) +CLD_TARGET_IF_OBJ += $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_ns.o +endif +ifeq ($(CONFIG_WLAN_FEATURE_PACKET_FILTERING), y) +CLD_TARGET_IF_OBJ += $(CLD_TARGET_IF_DIR)/pmo/src/target_if_pmo_pkt_filter.o +endif +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) +CLD_TARGET_IF_INC += -I$(WLAN_ROOT)/$(CLD_TARGET_IF_DIR)/ocb/inc +CLD_TARGET_IF_OBJ += $(CLD_TARGET_IF_DIR)/ocb/src/target_if_ocb.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DISA), y) +CLD_TARGET_IF_INC += -I$(WLAN_ROOT)/$(CLD_TARGET_IF_DIR)/disa/inc +CLD_TARGET_IF_OBJ += $(CLD_TARGET_IF_DIR)/disa/src/target_if_disa.o +endif + +ifeq ($(CONFIG_FEATURE_BLACKLIST_MGR), y) +CLD_TARGET_IF_INC += -I$(WLAN_ROOT)/$(CLD_TARGET_IF_DIR)/blacklist_mgr/inc +CLD_TARGET_IF_OBJ += $(CLD_TARGET_IF_DIR)/blacklist_mgr/src/target_if_blm.o +endif + +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CLD_TARGET_IF_INC += -I$(WLAN_ROOT)/$(CLD_TARGET_IF_DIR)/ipa/inc +CLD_TARGET_IF_OBJ += $(CLD_TARGET_IF_DIR)/ipa/src/target_if_ipa.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_ACTION_OUI), y) +CLD_TARGET_IF_INC += -I$(WLAN_ROOT)/$(CLD_TARGET_IF_DIR)/action_oui/inc +CLD_TARGET_IF_OBJ += $(CLD_TARGET_IF_DIR)/action_oui/src/target_if_action_oui.o +endif + +ifeq ($(CONFIG_FEATURE_WLAN_TIME_SYNC_FTM), y) +CLD_TARGET_IF_INC += -I$(WLAN_ROOT)/$(CLD_TARGET_IF_DIR)/ftm_time_sync/inc +CLD_TARGET_IF_OBJ += $(CLD_TARGET_IF_DIR)/ftm_time_sync/src/target_if_ftm_time_sync.o +endif + +############## UMAC P2P ########### +P2P_DIR := components/p2p +P2P_CORE_OBJ_DIR := $(P2P_DIR)/core/src +P2P_DISPATCHER_DIR := $(P2P_DIR)/dispatcher +P2P_DISPATCHER_INC_DIR := $(P2P_DISPATCHER_DIR)/inc +P2P_DISPATCHER_OBJ_DIR := $(P2P_DISPATCHER_DIR)/src +P2P_OS_IF_INC := os_if/p2p/inc +P2P_OS_IF_SRC := os_if/p2p/src +P2P_TARGET_IF_INC := components/target_if/p2p/inc +P2P_TARGET_IF_SRC := components/target_if/p2p/src +P2P_INC := -I$(WLAN_ROOT)/$(P2P_DISPATCHER_INC_DIR) \ + -I$(WLAN_ROOT)/$(P2P_OS_IF_INC) \ + -I$(WLAN_ROOT)/$(P2P_TARGET_IF_INC) +P2P_OBJS := $(P2P_DISPATCHER_OBJ_DIR)/wlan_p2p_ucfg_api.o \ + $(P2P_DISPATCHER_OBJ_DIR)/wlan_p2p_tgt_api.o \ + $(P2P_DISPATCHER_OBJ_DIR)/wlan_p2p_cfg.o \ + $(P2P_DISPATCHER_OBJ_DIR)/wlan_p2p_api.o \ + $(P2P_CORE_OBJ_DIR)/wlan_p2p_main.o \ + $(P2P_CORE_OBJ_DIR)/wlan_p2p_roc.o \ + $(P2P_CORE_OBJ_DIR)/wlan_p2p_off_chan_tx.o \ + $(P2P_OS_IF_SRC)/wlan_cfg80211_p2p.o \ + $(P2P_TARGET_IF_SRC)/target_if_p2p.o + +###### UMAC POLICY MGR ######## +POLICY_MGR_DIR := components/cmn_services/policy_mgr + +POLICY_MGR_INC := -I$(WLAN_ROOT)/$(POLICY_MGR_DIR)/inc \ + -I$(WLAN_ROOT)/$(POLICY_MGR_DIR)/src + +POLICY_MGR_OBJS := $(POLICY_MGR_DIR)/src/wlan_policy_mgr_action.o \ + $(POLICY_MGR_DIR)/src/wlan_policy_mgr_core.o \ + $(POLICY_MGR_DIR)/src/wlan_policy_mgr_get_set_utils.o \ + $(POLICY_MGR_DIR)/src/wlan_policy_mgr_init_deinit.o \ + $(POLICY_MGR_DIR)/src/wlan_policy_mgr_ucfg.o \ + $(POLICY_MGR_DIR)/src/wlan_policy_mgr_pcl.o \ + +###### UMAC TDLS ######## +TDLS_DIR := components/tdls + +TDLS_OS_IF_INC := os_if/tdls/inc +TDLS_OS_IF_SRC := os_if/tdls/src +TDLS_TARGET_IF_INC := components/target_if/tdls/inc +TDLS_TARGET_IF_SRC := components/target_if/tdls/src +TDLS_INC := -I$(WLAN_ROOT)/$(TDLS_DIR)/dispatcher/inc \ + -I$(WLAN_ROOT)/$(TDLS_OS_IF_INC) \ + -I$(WLAN_ROOT)/$(TDLS_TARGET_IF_INC) + +ifeq ($(CONFIG_QCOM_TDLS), y) +TDLS_OBJS := $(TDLS_DIR)/core/src/wlan_tdls_main.o \ + $(TDLS_DIR)/core/src/wlan_tdls_cmds_process.o \ + $(TDLS_DIR)/core/src/wlan_tdls_peer.o \ + $(TDLS_DIR)/core/src/wlan_tdls_mgmt.o \ + $(TDLS_DIR)/core/src/wlan_tdls_ct.o \ + $(TDLS_DIR)/dispatcher/src/wlan_tdls_tgt_api.o \ + $(TDLS_DIR)/dispatcher/src/wlan_tdls_ucfg_api.o \ + $(TDLS_DIR)/dispatcher/src/wlan_tdls_utils_api.o \ + $(TDLS_DIR)/dispatcher/src/wlan_tdls_cfg.o \ + $(TDLS_OS_IF_SRC)/wlan_cfg80211_tdls.o \ + $(TDLS_TARGET_IF_SRC)/target_if_tdls.o +endif + +########### BMI ########### +BMI_DIR := core/bmi + +BMI_INC := -I$(WLAN_ROOT)/$(BMI_DIR)/inc + +ifeq ($(CONFIG_WLAN_FEATURE_BMI), y) +BMI_OBJS := $(BMI_DIR)/src/bmi.o \ + $(BMI_DIR)/src/bmi_1.o \ + $(BMI_DIR)/src/ol_fw.o \ + $(BMI_DIR)/src/ol_fw_common.o +endif + +########## TARGET_IF ####### +TARGET_IF_DIR := $(WLAN_COMMON_ROOT)/target_if + +TARGET_IF_INC := -I$(WLAN_COMMON_INC)/target_if/core/inc \ + -I$(WLAN_COMMON_INC)/target_if/core/src \ + -I$(WLAN_COMMON_INC)/target_if/init_deinit/inc \ + -I$(WLAN_COMMON_INC)/target_if/crypto/inc \ + -I$(WLAN_COMMON_INC)/target_if/regulatory/inc \ + -I$(WLAN_COMMON_INC)/target_if/mlme/vdev_mgr/inc \ + -I$(WLAN_COMMON_INC)/target_if/dispatcher/inc \ + -I$(WLAN_COMMON_INC)/target_if/mlme/psoc/inc + +TARGET_IF_OBJ := $(TARGET_IF_DIR)/core/src/target_if_main.o \ + $(TARGET_IF_DIR)/regulatory/src/target_if_reg.o \ + $(TARGET_IF_DIR)/regulatory/src/target_if_reg_lte.o \ + $(TARGET_IF_DIR)/regulatory/src/target_if_reg_11d.o \ + $(TARGET_IF_DIR)/init_deinit/src/init_cmd_api.o \ + $(TARGET_IF_DIR)/init_deinit/src/init_deinit_lmac.o \ + $(TARGET_IF_DIR)/init_deinit/src/init_event_handler.o \ + $(TARGET_IF_DIR)/init_deinit/src/service_ready_util.o \ + $(TARGET_IF_DIR)/mlme/vdev_mgr/src/target_if_vdev_mgr_tx_ops.o \ + $(TARGET_IF_DIR)/mlme/vdev_mgr/src/target_if_vdev_mgr_rx_ops.o \ + $(TARGET_IF_DIR)/mlme/psoc/src/target_if_psoc_timer_tx_ops.o + +ifeq ($(CONFIG_FEATURE_VDEV_RSP_WAKELOCK), y) +TARGET_IF_OBJ += $(TARGET_IF_DIR)/mlme/psoc/src/target_if_psoc_wake_lock.o +endif + +ifeq ($(CONFIG_CRYPTO_COMPONENT), y) +TARGET_IF_OBJ += $(TARGET_IF_DIR)/crypto/src/target_if_crypto.o +endif + +########### GLOBAL_LMAC_IF ########## +GLOBAL_LMAC_IF_DIR := $(WLAN_COMMON_ROOT)/global_lmac_if + +GLOBAL_LMAC_IF_INC := -I$(WLAN_COMMON_INC)/global_lmac_if/inc \ + -I$(WLAN_COMMON_INC)/global_lmac_if/src + +GLOBAL_LMAC_IF_OBJ := $(GLOBAL_LMAC_IF_DIR)/src/wlan_global_lmac_if.o + +########### WMI ########### +WMI_ROOT_DIR := wmi + +WMI_SRC_DIR := $(WMI_ROOT_DIR)/src +WMI_INC_DIR := $(WMI_ROOT_DIR)/inc +WMI_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(WMI_SRC_DIR) + +WMI_INC := -I$(WLAN_COMMON_INC)/$(WMI_INC_DIR) + +WMI_OBJS := $(WMI_OBJ_DIR)/wmi_unified.o \ + $(WMI_OBJ_DIR)/wmi_tlv_helper.o \ + $(WMI_OBJ_DIR)/wmi_unified_tlv.o \ + $(WMI_OBJ_DIR)/wmi_unified_api.o \ + $(WMI_OBJ_DIR)/wmi_unified_reg_api.o \ + $(WMI_OBJ_DIR)/wmi_unified_vdev_api.o \ + $(WMI_OBJ_DIR)/wmi_unified_vdev_tlv.o \ + $(WMI_OBJ_DIR)/wmi_unified_crypto_api.o + +ifeq ($(CONFIG_POWER_MANAGEMENT_OFFLOAD), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_pmo_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_pmo_tlv.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_APF), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_apf_tlv.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_ACTION_OUI), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_action_oui_tlv.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) +ifeq ($(CONFIG_OCB_UT_FRAMEWORK), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_ocb_ut.o +endif +endif + +ifeq ($(CONFIG_WLAN_DFS_MASTER_ENABLE), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_dfs_api.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_TWT), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_twt_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_twt_tlv.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_ocb_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_ocb_tlv.o +endif + +ifeq ($(CONFIG_FEATURE_WLAN_EXTSCAN), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_extscan_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_extscan_tlv.o +endif + +ifeq ($(CONFIG_FEATURE_INTEROP_ISSUES_AP), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_interop_issues_ap_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_interop_issues_ap_tlv.o +endif + +ifeq ($(CONFIG_QCACLD_FEATURE_NAN), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_nan_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_nan_tlv.o +endif + +ifeq ($(CONFIG_CONVERGED_P2P_ENABLE), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_p2p_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_p2p_tlv.o +endif + +ifeq ($(CONFIG_WMI_ROAM_SUPPORT), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_roam_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_roam_tlv.o +endif + +ifeq ($(CONFIG_WMI_CONCURRENCY_SUPPORT), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_concurrency_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_concurrency_tlv.o +endif + +ifeq ($(CONFIG_WMI_STA_SUPPORT), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_sta_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_sta_tlv.o +endif + +ifeq ($(CONFIG_WMI_BCN_OFFLOAD), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_bcn_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_bcn_tlv.o +endif + +ifeq ($(CONFIG_WLAN_FW_OFFLOAD), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_fwol_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_fwol_tlv.o +endif + +ifeq ($(CONFIG_WLAN_CFR_ENABLE), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_cfr_tlv.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_cfr_api.o +endif + +ifeq ($(CONFIG_WLAN_HANG_EVENT), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_hang_event.o +endif + +ifeq ($(CONFIG_FEATURE_GPIO_CFG), y) +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_gpio_api.o +WMI_OBJS += $(WMI_OBJ_DIR)/wmi_unified_gpio_tlv.o +endif + +########### FWLOG ########### +FWLOG_DIR := $(WLAN_COMMON_ROOT)/utils/fwlog + +FWLOG_INC := -I$(WLAN_ROOT)/$(FWLOG_DIR) + +FWLOG_OBJS := $(FWLOG_DIR)/dbglog_host.o + +############ TXRX ############ +TXRX_DIR := core/dp/txrx +TXRX_INC := -I$(WLAN_ROOT)/$(TXRX_DIR) + +ifneq ($(CONFIG_LITHIUM), y) +TXRX_OBJS := $(TXRX_DIR)/ol_txrx.o \ + $(TXRX_DIR)/ol_cfg.o \ + $(TXRX_DIR)/ol_rx.o \ + $(TXRX_DIR)/ol_rx_fwd.o \ + $(TXRX_DIR)/ol_txrx.o \ + $(TXRX_DIR)/ol_rx_defrag.o \ + $(TXRX_DIR)/ol_tx_desc.o \ + $(TXRX_DIR)/ol_tx.o \ + $(TXRX_DIR)/ol_rx_reorder_timeout.o \ + $(TXRX_DIR)/ol_rx_reorder.o \ + $(TXRX_DIR)/ol_rx_pn.o \ + $(TXRX_DIR)/ol_txrx_peer_find.o \ + $(TXRX_DIR)/ol_txrx_encap.o \ + $(TXRX_DIR)/ol_tx_send.o + +ifeq ($(CONFIG_WDI_EVENT_ENABLE), y) +TXRX_OBJS += $(TXRX_DIR)/ol_txrx_event.o +endif + +ifeq ($(CONFIG_LL_DP_SUPPORT), y) + +TXRX_OBJS += $(TXRX_DIR)/ol_tx_ll.o + +ifeq ($(CONFIG_WLAN_FASTPATH), y) +TXRX_OBJS += $(TXRX_DIR)/ol_tx_ll_fastpath.o +else +TXRX_OBJS += $(TXRX_DIR)/ol_tx_ll_legacy.o +endif + +ifeq ($(CONFIG_WLAN_TX_FLOW_CONTROL_V2), y) +TXRX_OBJS += $(TXRX_DIR)/ol_txrx_flow_control.o +endif + +endif #CONFIG_LL_DP_SUPPORT + +ifeq ($(CONFIG_HL_DP_SUPPORT), y) +TXRX_OBJS += $(TXRX_DIR)/ol_tx_hl.o +TXRX_OBJS += $(TXRX_DIR)/ol_tx_classify.o +TXRX_OBJS += $(TXRX_DIR)/ol_tx_sched.o +TXRX_OBJS += $(TXRX_DIR)/ol_tx_queue.o +endif #CONFIG_HL_DP_SUPPORT + +ifeq ($(CONFIG_WLAN_TX_FLOW_CONTROL_LEGACY), y) +TXRX_OBJS += $(TXRX_DIR)/ol_txrx_legacy_flow_control.o +endif + +ifeq ($(CONFIG_IPA_OFFLOAD), y) +TXRX_OBJS += $(TXRX_DIR)/ol_txrx_ipa.o +endif + +ifeq ($(CONFIG_QCA_SUPPORT_TX_THROTTLE), y) +TXRX_OBJS += $(TXRX_DIR)/ol_tx_throttle.o +endif +endif #LITHIUM + +############ TXRX 3.0 ############ +TXRX3.0_DIR := core/dp/txrx3.0 +TXRX3.0_INC := -I$(WLAN_ROOT)/$(TXRX3.0_DIR) +TXRX3.0_OBJS := $(TXRX3.0_DIR)/dp_txrx.o \ + $(TXRX3.0_DIR)/dp_rx_thread.o + +ifeq ($(CONFIG_RX_FISA), y) +TXRX3.0_OBJS += $(TXRX3.0_DIR)/dp_fisa_rx.o +TXRX3.0_OBJS += $(TXRX3.0_DIR)/dp_rx_fst.o +endif + +ifeq ($(CONFIG_LITHIUM), y) +############ DP 3.0 ############ +DP_INC := -I$(WLAN_COMMON_INC)/dp/inc \ + -I$(WLAN_COMMON_INC)/dp/wifi3.0 \ + -I$(WLAN_COMMON_INC)/target_if/dp/inc + +DP_SRC := $(WLAN_COMMON_ROOT)/dp/wifi3.0 +DP_OBJS := $(DP_SRC)/dp_main.o \ + $(DP_SRC)/dp_tx.o \ + $(DP_SRC)/dp_tx_desc.o \ + $(DP_SRC)/dp_rx.o \ + $(DP_SRC)/dp_rx_err.o \ + $(DP_SRC)/dp_htt.o \ + $(DP_SRC)/dp_peer.o \ + $(DP_SRC)/dp_rx_desc.o \ + $(DP_SRC)/dp_reo.o \ + $(DP_SRC)/dp_rx_mon_dest.o \ + $(DP_SRC)/dp_rx_mon_status.o \ + $(DP_SRC)/dp_rx_defrag.o \ + $(DP_SRC)/dp_mon_filter.o \ + $(DP_SRC)/dp_stats.o \ + $(WLAN_COMMON_ROOT)/target_if/dp/src/target_if_dp.o +ifeq ($(CONFIG_WLAN_TX_FLOW_CONTROL_V2), y) +DP_OBJS += $(DP_SRC)/dp_tx_flow_control.o +endif +endif + +ifeq ($(CONFIG_IPA_OFFLOAD), y) +DP_OBJS += $(DP_SRC)/dp_ipa.o +endif + +ifeq ($(CONFIG_WDI_EVENT_ENABLE), y) +DP_OBJS += $(DP_SRC)/dp_wdi_event.o +endif + +############ CFG ############ +WCFG_DIR := wlan_cfg +WCFG_INC := -I$(WLAN_COMMON_INC)/$(WCFG_DIR) +WCFG_SRC := $(WLAN_COMMON_ROOT)/$(WCFG_DIR) + +ifeq ($(CONFIG_LITHIUM), y) +WCFG_OBJS := $(WCFG_SRC)/wlan_cfg.o +endif + +############ OL ############ +OL_DIR := core/dp/ol +OL_INC := -I$(WLAN_ROOT)/$(OL_DIR)/inc + +############ CDP ############ +CDP_ROOT_DIR := dp +CDP_INC_DIR := $(CDP_ROOT_DIR)/inc +CDP_INC := -I$(WLAN_COMMON_INC)/$(CDP_INC_DIR) + +############ PKTLOG ############ +PKTLOG_DIR := $(WLAN_COMMON_ROOT)/utils/pktlog +PKTLOG_INC := -I$(WLAN_ROOT)/$(PKTLOG_DIR)/include + +PKTLOG_OBJS := $(PKTLOG_DIR)/pktlog_ac.o \ + $(PKTLOG_DIR)/pktlog_internal.o \ + $(PKTLOG_DIR)/linux_ac.o + +ifeq ($(CONFIG_PKTLOG_LEGACY), y) + PKTLOG_OBJS += $(PKTLOG_DIR)/pktlog_wifi2.o +else + PKTLOG_OBJS += $(PKTLOG_DIR)/pktlog_wifi3.o +endif + +############ HTT ############ +HTT_DIR := core/dp/htt +HTT_INC := -I$(WLAN_ROOT)/$(HTT_DIR) + +HTT_OBJS := $(HTT_DIR)/htt_tx.o \ + $(HTT_DIR)/htt.o \ + $(HTT_DIR)/htt_t2h.o \ + $(HTT_DIR)/htt_h2t.o \ + $(HTT_DIR)/htt_fw_stats.o \ + $(HTT_DIR)/htt_rx.o + +ifeq ($(CONFIG_FEATURE_MONITOR_MODE_SUPPORT), y) +HTT_OBJS += $(HTT_DIR)/htt_monitor_rx.o +endif + +ifeq ($(CONFIG_LL_DP_SUPPORT), y) +HTT_OBJS += $(HTT_DIR)/htt_rx_ll.o +endif + +ifeq ($(CONFIG_HL_DP_SUPPORT), y) +HTT_OBJS += $(HTT_DIR)/htt_rx_hl.o +endif + +############## INIT-DEINIT ########### +INIT_DEINIT_DIR := init_deinit/dispatcher +INIT_DEINIT_INC_DIR := $(INIT_DEINIT_DIR)/inc +INIT_DEINIT_SRC_DIR := $(INIT_DEINIT_DIR)/src +INIT_DEINIT_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(INIT_DEINIT_SRC_DIR) +INIT_DEINIT_INC := -I$(WLAN_COMMON_INC)/$(INIT_DEINIT_INC_DIR) +INIT_DEINIT_OBJS := $(INIT_DEINIT_OBJ_DIR)/dispatcher_init_deinit.o + +############## REGULATORY ########### +REGULATORY_DIR := umac/regulatory +REGULATORY_CORE_INC_DIR := $(REGULATORY_DIR)/core/inc +REGULATORY_CORE_SRC_DIR := $(REGULATORY_DIR)/core/src +REG_DISPATCHER_INC_DIR := $(REGULATORY_DIR)/dispatcher/inc +REG_DISPATCHER_SRC_DIR := $(REGULATORY_DIR)/dispatcher/src +REG_CORE_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(REGULATORY_CORE_SRC_DIR) +REG_DISPATCHER_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(REG_DISPATCHER_SRC_DIR) +REGULATORY_INC := -I$(WLAN_COMMON_INC)/$(REGULATORY_CORE_INC_DIR) +REGULATORY_INC += -I$(WLAN_COMMON_INC)/$(REG_DISPATCHER_INC_DIR) +REGULATORY_OBJS := $(REG_CORE_OBJ_DIR)/reg_build_chan_list.o \ + $(REG_CORE_OBJ_DIR)/reg_callbacks.o \ + $(REG_CORE_OBJ_DIR)/reg_db.o \ + $(REG_CORE_OBJ_DIR)/reg_db_parser.o \ + $(REG_CORE_OBJ_DIR)/reg_utils.o \ + $(REG_CORE_OBJ_DIR)/reg_lte.o \ + $(REG_CORE_OBJ_DIR)/reg_offload_11d_scan.o \ + $(REG_CORE_OBJ_DIR)/reg_opclass.o \ + $(REG_CORE_OBJ_DIR)/reg_priv_objs.o \ + $(REG_DISPATCHER_OBJ_DIR)/wlan_reg_services_api.o \ + $(REG_CORE_OBJ_DIR)/reg_services_common.o \ + $(REG_DISPATCHER_OBJ_DIR)/wlan_reg_tgt_api.o \ + $(REG_DISPATCHER_OBJ_DIR)/wlan_reg_ucfg_api.o +ifeq ($(CONFIG_HOST_11D_SCAN), y) +REGULATORY_OBJS += $(REG_CORE_OBJ_DIR)/reg_host_11d.o +endif + +############## Control path common scheduler ########## +SCHEDULER_DIR := scheduler +SCHEDULER_INC_DIR := $(SCHEDULER_DIR)/inc +SCHEDULER_SRC_DIR := $(SCHEDULER_DIR)/src +SCHEDULER_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(SCHEDULER_SRC_DIR) +SCHEDULER_INC := -I$(WLAN_COMMON_INC)/$(SCHEDULER_INC_DIR) +SCHEDULER_OBJS := $(SCHEDULER_OBJ_DIR)/scheduler_api.o \ + $(SCHEDULER_OBJ_DIR)/scheduler_core.o + +###### UMAC SERIALIZATION ######## +UMAC_SER_DIR := umac/cmn_services/serialization +UMAC_SER_INC_DIR := $(UMAC_SER_DIR)/inc +UMAC_SER_SRC_DIR := $(UMAC_SER_DIR)/src +UMAC_SER_OBJ_DIR := $(WLAN_COMMON_ROOT)/$(UMAC_SER_SRC_DIR) + +UMAC_SER_INC := -I$(WLAN_COMMON_INC)/$(UMAC_SER_INC_DIR) +UMAC_SER_OBJS := $(UMAC_SER_OBJ_DIR)/wlan_serialization_main.o \ + $(UMAC_SER_OBJ_DIR)/wlan_serialization_api.o \ + $(UMAC_SER_OBJ_DIR)/wlan_serialization_utils.o \ + $(UMAC_SER_OBJ_DIR)/wlan_serialization_legacy_api.o \ + $(UMAC_SER_OBJ_DIR)/wlan_serialization_rules.o \ + $(UMAC_SER_OBJ_DIR)/wlan_serialization_internal.o \ + $(UMAC_SER_OBJ_DIR)/wlan_serialization_non_scan.o \ + $(UMAC_SER_OBJ_DIR)/wlan_serialization_queue.o \ + $(UMAC_SER_OBJ_DIR)/wlan_serialization_scan.o + +###### WIFI POS ######## +WIFI_POS_OS_IF_DIR := $(WLAN_COMMON_ROOT)/os_if/linux/wifi_pos/src +WIFI_POS_OS_IF_INC := -I$(WLAN_COMMON_INC)/os_if/linux/wifi_pos/inc +WIFI_POS_TGT_DIR := $(WLAN_COMMON_ROOT)/target_if/wifi_pos/src +WIFI_POS_TGT_INC := -I$(WLAN_COMMON_INC)/target_if/wifi_pos/inc +WIFI_POS_CORE_DIR := $(WLAN_COMMON_ROOT)/umac/wifi_pos/src +WIFI_POS_API_INC := -I$(WLAN_COMMON_INC)/umac/wifi_pos/inc + + +ifeq ($(CONFIG_WIFI_POS_CONVERGED), y) +WIFI_POS_OBJS := $(WIFI_POS_CORE_DIR)/wifi_pos_api.o \ + $(WIFI_POS_CORE_DIR)/wifi_pos_main.o \ + $(WIFI_POS_CORE_DIR)/wifi_pos_ucfg.o \ + $(WIFI_POS_CORE_DIR)/wifi_pos_utils.o \ + $(WIFI_POS_OS_IF_DIR)/os_if_wifi_pos.o \ + $(WIFI_POS_TGT_DIR)/target_if_wifi_pos.o +endif + +###### CP STATS ######## +CP_STATS_OS_IF_SRC := $(WLAN_COMMON_ROOT)/os_if/linux/cp_stats/src +CP_STATS_TGT_SRC := $(WLAN_COMMON_ROOT)/target_if/cp_stats/src +CP_STATS_CORE_SRC := $(WLAN_COMMON_ROOT)/umac/cp_stats/core/src +CP_STATS_DISPATCHER_SRC := $(WLAN_COMMON_ROOT)/umac/cp_stats/dispatcher/src + +CP_STATS_OS_IF_INC := -I$(WLAN_COMMON_INC)/os_if/linux/cp_stats/inc +CP_STATS_TGT_INC := -I$(WLAN_COMMON_INC)/target_if/cp_stats/inc +CP_STATS_DISPATCHER_INC := -I$(WLAN_COMMON_INC)/umac/cp_stats/dispatcher/inc + +ifeq ($(CONFIG_CP_STATS), y) +CP_STATS_OBJS := $(CP_STATS_TGT_SRC)/target_if_mc_cp_stats.o \ + $(CP_STATS_CORE_SRC)/wlan_cp_stats_comp_handler.o \ + $(CP_STATS_CORE_SRC)/wlan_cp_stats_obj_mgr_handler.o \ + $(CP_STATS_CORE_SRC)/wlan_cp_stats_ol_api.o \ + $(CP_STATS_OS_IF_SRC)/wlan_cfg80211_mc_cp_stats.o \ + $(CP_STATS_DISPATCHER_SRC)/wlan_cp_stats_utils_api.o \ + $(CP_STATS_DISPATCHER_SRC)/wlan_cp_stats_mc_tgt_api.o \ + $(CP_STATS_DISPATCHER_SRC)/wlan_cp_stats_mc_ucfg_api.o +endif + +###### COMPONENT CP STATS ######## +COMP_CP_STATS_DISPATCHER_INC := -I$(WLAN_ROOT)/components/cp_stats/dispatcher/inc + +###### INTEROP ISSUES AP ######## +INTEROP_ISSUES_AP_OS_IF_SRC := os_if/interop_issues_ap/src +INTEROP_ISSUES_AP_TGT_SRC := components/target_if/interop_issues_ap/src +INTEROP_ISSUES_AP_CORE_SRC := components/interop_issues_ap/core/src +INTEROP_ISSUES_AP_DISPATCHER_SRC := components/interop_issues_ap/dispatcher/src + +INTEROP_ISSUES_AP_OS_IF_INC := -I$(WLAN_ROOT)/os_if/interop_issues_ap/inc +INTEROP_ISSUES_AP_TGT_INC := -I$(WLAN_ROOT)/components/target_if/interop_issues_ap/inc +INTEROP_ISSUES_AP_DISPATCHER_INC := -I$(WLAN_ROOT)/components/interop_issues_ap/dispatcher/inc +INTEROP_ISSUES_AP_CORE_INC := -I$(WLAN_ROOT)/components/interop_issues_ap/core/inc + +ifeq ($(CONFIG_FEATURE_INTEROP_ISSUES_AP), y) +INTEROP_ISSUES_AP_OBJS := $(INTEROP_ISSUES_AP_TGT_SRC)/target_if_interop_issues_ap.o \ + $(INTEROP_ISSUES_AP_CORE_SRC)/wlan_interop_issues_ap_api.o \ + $(INTEROP_ISSUES_AP_OS_IF_SRC)/wlan_cfg80211_interop_issues_ap.o \ + $(INTEROP_ISSUES_AP_DISPATCHER_SRC)/wlan_interop_issues_ap_tgt_api.o \ + $(INTEROP_ISSUES_AP_DISPATCHER_SRC)/wlan_interop_issues_ap_ucfg_api.o +endif + +######################### NAN ######################### +NAN_CORE_DIR := components/nan/core/src +NAN_CORE_INC := -I$(WLAN_ROOT)/components/nan/core/inc +NAN_UCFG_DIR := components/nan/dispatcher/src +NAN_UCFG_INC := -I$(WLAN_ROOT)/components/nan/dispatcher/inc +NAN_TGT_DIR := components/target_if/nan/src +NAN_TGT_INC := -I$(WLAN_ROOT)/components/target_if/nan/inc + +NAN_OS_IF_DIR := os_if/nan/src +NAN_OS_IF_INC := -I$(WLAN_ROOT)/os_if/nan/inc + +ifeq ($(CONFIG_QCACLD_FEATURE_NAN), y) +WLAN_NAN_OBJS := $(NAN_CORE_DIR)/nan_main.o \ + $(NAN_CORE_DIR)/nan_api.o \ + $(NAN_UCFG_DIR)/nan_ucfg_api.o \ + $(NAN_UCFG_DIR)/cfg_nan.o \ + $(NAN_TGT_DIR)/target_if_nan.o \ + $(NAN_OS_IF_DIR)/os_if_nan.o +endif +####################################################### + +############## HTC ########## +HTC_DIR := htc +HTC_INC := -I$(WLAN_COMMON_INC)/$(HTC_DIR) + +HTC_OBJS := $(WLAN_COMMON_ROOT)/$(HTC_DIR)/htc.o \ + $(WLAN_COMMON_ROOT)/$(HTC_DIR)/htc_send.o \ + $(WLAN_COMMON_ROOT)/$(HTC_DIR)/htc_recv.o \ + $(WLAN_COMMON_ROOT)/$(HTC_DIR)/htc_services.o + +ifeq ($(CONFIG_FEATURE_HTC_CREDIT_HISTORY), y) +HTC_OBJS += $(WLAN_COMMON_ROOT)/$(HTC_DIR)/htc_credit_history.o +endif + +ifeq ($(CONFIG_WLAN_HANG_EVENT), y) +HTC_OBJS += $(WLAN_COMMON_ROOT)/$(HTC_DIR)/htc_hang_event.o +endif + +########### HIF ########### +HIF_DIR := hif +HIF_CE_DIR := $(HIF_DIR)/src/ce + +HIF_DISPATCHER_DIR := $(HIF_DIR)/src/dispatcher + +HIF_PCIE_DIR := $(HIF_DIR)/src/pcie +HIF_IPCIE_DIR := $(HIF_DIR)/src/ipcie +HIF_SNOC_DIR := $(HIF_DIR)/src/snoc +HIF_USB_DIR := $(HIF_DIR)/src/usb +HIF_SDIO_DIR := $(HIF_DIR)/src/sdio + +HIF_SDIO_NATIVE_DIR := $(HIF_SDIO_DIR)/native_sdio +HIF_SDIO_NATIVE_INC_DIR := $(HIF_SDIO_NATIVE_DIR)/include +HIF_SDIO_NATIVE_SRC_DIR := $(HIF_SDIO_NATIVE_DIR)/src + +HIF_INC := -I$(WLAN_COMMON_INC)/$(HIF_DIR)/inc \ + -I$(WLAN_COMMON_INC)/$(HIF_DIR)/src + +ifeq ($(CONFIG_HIF_PCI), y) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_DISPATCHER_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_PCIE_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_CE_DIR) +endif + +ifeq ($(CONFIG_HIF_IPCI), y) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_DISPATCHER_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_IPCIE_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_CE_DIR) +endif + +ifeq ($(CONFIG_HIF_SNOC), y) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_DISPATCHER_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_SNOC_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_CE_DIR) +endif + +ifeq ($(CONFIG_HIF_USB), y) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_DISPATCHER_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_USB_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_CE_DIR) +endif + +ifeq ($(CONFIG_HIF_SDIO), y) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_DISPATCHER_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_SDIO_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_SDIO_NATIVE_INC_DIR) +HIF_INC += -I$(WLAN_COMMON_INC)/$(HIF_CE_DIR) +endif + +HIF_COMMON_OBJS := $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/ath_procfs.o \ + $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/hif_main.o \ + $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/hif_exec.o + +ifneq ($(CONFIG_LITHIUM), y) +HIF_COMMON_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/hif_main_legacy.o +endif + +ifeq ($(CONFIG_WLAN_NAPI), y) +HIF_COMMON_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/hif_irq_affinity.o +endif + +HIF_CE_OBJS := $(WLAN_COMMON_ROOT)/$(HIF_CE_DIR)/ce_diag.o \ + $(WLAN_COMMON_ROOT)/$(HIF_CE_DIR)/ce_main.o \ + $(WLAN_COMMON_ROOT)/$(HIF_CE_DIR)/ce_service.o \ + $(WLAN_COMMON_ROOT)/$(HIF_CE_DIR)/ce_tasklet.o \ + $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/mp_dev.o \ + $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/regtable.o + +ifeq ($(CONFIG_WLAN_FEATURE_BMI), y) +HIF_CE_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_CE_DIR)/ce_bmi.o +endif + +ifeq ($(CONFIG_LITHIUM), y) +ifeq ($(CONFIG_CNSS_QCA6290), y) +HIF_CE_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/qca6290def.o +endif + +ifeq ($(CONFIG_CNSS_QCA6390), y) +HIF_CE_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/qca6390def.o +endif + +ifeq ($(CONFIG_CNSS_QCA6490), y) +HIF_CE_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/qca6490def.o +endif + +ifeq ($(CONFIG_CNSS_QCA6750), y) +HIF_CE_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/qca6750def.o +endif + +HIF_CE_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_CE_DIR)/ce_service_srng.o +else +HIF_CE_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_CE_DIR)/ce_service_legacy.o +endif + +HIF_USB_OBJS := $(WLAN_COMMON_ROOT)/$(HIF_USB_DIR)/usbdrv.o \ + $(WLAN_COMMON_ROOT)/$(HIF_USB_DIR)/hif_usb.o \ + $(WLAN_COMMON_ROOT)/$(HIF_USB_DIR)/if_usb.o \ + $(WLAN_COMMON_ROOT)/$(HIF_USB_DIR)/regtable_usb.o + +HIF_SDIO_OBJS := $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/hif_diag_reg_access.o \ + $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/hif_sdio_dev.o \ + $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/hif_sdio.o \ + $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/regtable_sdio.o \ + $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/transfer/transfer.o + +ifeq ($(CONFIG_WLAN_FEATURE_BMI), y) +HIF_SDIO_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/hif_bmi_reg_access.o +endif + +ifeq ($(CONFIG_SDIO_TRANSFER), adma) +HIF_SDIO_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/transfer/adma.o +else +HIF_SDIO_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/transfer/mailbox.o +endif + +HIF_SDIO_NATIVE_OBJS := $(WLAN_COMMON_ROOT)/$(HIF_SDIO_NATIVE_SRC_DIR)/hif.o \ + $(WLAN_COMMON_ROOT)/$(HIF_SDIO_NATIVE_SRC_DIR)/hif_scatter.o \ + $(WLAN_COMMON_ROOT)/$(HIF_SDIO_NATIVE_SRC_DIR)/dev_quirks.o + +ifeq ($(CONFIG_WLAN_NAPI), y) +HIF_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/hif_napi.o +endif + +ifeq ($(CONFIG_FEATURE_UNIT_TEST_SUSPEND), y) + HIF_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DIR)/src/hif_unit_test_suspend.o +endif + +HIF_PCIE_OBJS := $(WLAN_COMMON_ROOT)/$(HIF_PCIE_DIR)/if_pci.o +HIF_IPCIE_OBJS := $(WLAN_COMMON_ROOT)/$(HIF_IPCIE_DIR)/if_ipci.o +HIF_SNOC_OBJS := $(WLAN_COMMON_ROOT)/$(HIF_SNOC_DIR)/if_snoc.o +HIF_SDIO_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/if_sdio.o + +HIF_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DISPATCHER_DIR)/multibus.o +HIF_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DISPATCHER_DIR)/dummy.o +HIF_OBJS += $(HIF_COMMON_OBJS) + +ifeq ($(CONFIG_HIF_PCI), y) +HIF_OBJS += $(HIF_PCIE_OBJS) +HIF_OBJS += $(HIF_CE_OBJS) +HIF_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DISPATCHER_DIR)/multibus_pci.o +endif + +ifeq ($(CONFIG_HIF_IPCI), y) +HIF_OBJS += $(HIF_IPCIE_OBJS) +HIF_OBJS += $(HIF_CE_OBJS) +HIF_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DISPATCHER_DIR)/multibus_ipci.o +endif + +ifeq ($(CONFIG_HIF_SNOC), y) +HIF_OBJS += $(HIF_SNOC_OBJS) +HIF_OBJS += $(HIF_CE_OBJS) +HIF_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DISPATCHER_DIR)/multibus_snoc.o +endif + +ifeq ($(CONFIG_HIF_SDIO), y) +HIF_OBJS += $(HIF_SDIO_OBJS) +HIF_OBJS += $(HIF_SDIO_NATIVE_OBJS) +HIF_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DISPATCHER_DIR)/multibus_sdio.o +endif + +ifeq ($(CONFIG_HIF_USB), y) +HIF_OBJS += $(HIF_USB_OBJS) +HIF_OBJS += $(WLAN_COMMON_ROOT)/$(HIF_DISPATCHER_DIR)/multibus_usb.o +endif + +ifeq ($(CONFIG_LITHIUM), y) +############ HAL ############ +HAL_DIR := hal +HAL_INC := -I$(WLAN_COMMON_INC)/$(HAL_DIR)/inc \ + -I$(WLAN_COMMON_INC)/$(HAL_DIR)/wifi3.0 + +HAL_OBJS := $(WLAN_COMMON_ROOT)/$(HAL_DIR)/wifi3.0/hal_srng.o \ + $(WLAN_COMMON_ROOT)/$(HAL_DIR)/wifi3.0/hal_reo.o + +ifeq ($(CONFIG_RX_FISA), y) +DP_OBJS += $(WLAN_COMMON_ROOT)/$(HAL_DIR)/wifi3.0/hal_rx_flow.o +endif + +ifeq ($(CONFIG_CNSS_QCA6290), y) +HAL_INC += -I$(WLAN_COMMON_INC)/$(HAL_DIR)/wifi3.0/qca6290 +HAL_OBJS += $(WLAN_COMMON_ROOT)/$(HAL_DIR)/wifi3.0/qca6290/hal_6290.o +else ifeq ($(CONFIG_CNSS_QCA6390), y) +HAL_INC += -I$(WLAN_COMMON_INC)/$(HAL_DIR)/wifi3.0/qca6390 +HAL_OBJS += $(WLAN_COMMON_ROOT)/$(HAL_DIR)/wifi3.0/qca6390/hal_6390.o +else ifeq ($(CONFIG_CNSS_QCA6490), y) +HAL_INC += -I$(WLAN_COMMON_INC)/$(HAL_DIR)/wifi3.0/qca6490 +HAL_OBJS += $(WLAN_COMMON_ROOT)/$(HAL_DIR)/wifi3.0/qca6490/hal_6490.o +else ifeq ($(CONFIG_CNSS_QCA6750), y) +HAL_INC += -I$(WLAN_COMMON_INC)/$(HAL_DIR)/wifi3.0/qca6750 +HAL_OBJS += $(WLAN_COMMON_ROOT)/$(HAL_DIR)/wifi3.0/qca6750/hal_6750.o +else +#error "Not 11ax" +endif + +endif #####CONFIG_LITHIUM#### + +############ WMA ############ +WMA_DIR := core/wma + +WMA_INC_DIR := $(WMA_DIR)/inc +WMA_SRC_DIR := $(WMA_DIR)/src + +WMA_INC := -I$(WLAN_ROOT)/$(WMA_INC_DIR) \ + -I$(WLAN_ROOT)/$(WMA_SRC_DIR) + +ifeq ($(CONFIG_QCACLD_FEATURE_NAN), y) +WMA_NDP_OBJS += $(WMA_SRC_DIR)/wma_nan_datapath.o +endif + +WMA_OBJS := $(WMA_SRC_DIR)/wma_main.o \ + $(WMA_SRC_DIR)/wma_scan_roam.o \ + $(WMA_SRC_DIR)/wma_dev_if.o \ + $(WMA_SRC_DIR)/wma_mgmt.o \ + $(WMA_SRC_DIR)/wma_power.o \ + $(WMA_SRC_DIR)/wma_data.o \ + $(WMA_SRC_DIR)/wma_utils.o \ + $(WMA_SRC_DIR)/wma_features.o \ + $(WMA_SRC_DIR)/wlan_qct_wma_legacy.o\ + $(WMA_NDP_OBJS) + +#######DIRECT_BUFFER_RX######### +ifeq ($(CONFIG_DIRECT_BUF_RX_ENABLE), y) +DBR_DIR = $(WLAN_COMMON_ROOT)/target_if/direct_buf_rx +UMAC_DBR_INC := -I$(WLAN_COMMON_INC)/target_if/direct_buf_tx/inc +UMAC_DBR_OBJS := $(DBR_DIR)/src/target_if_direct_buf_rx_api.o \ + $(DBR_DIR)/src/target_if_direct_buf_rx_main.o \ + $(WLAN_COMMON_ROOT)/wmi/src/wmi_unified_dbr_api.o \ + $(WLAN_COMMON_ROOT)/wmi/src/wmi_unified_dbr_tlv.o +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) +WMA_OBJS+= $(WMA_SRC_DIR)/wma_ocb.o +endif +ifeq ($(CONFIG_WLAN_FEATURE_FIPS), y) +WMA_OBJS+= $(WMA_SRC_DIR)/wma_fips_api.o +endif +ifeq ($(CONFIG_MPC_UT_FRAMEWORK), y) +WMA_OBJS += $(WMA_SRC_DIR)/wma_utils_ut.o +endif +ifeq ($(CONFIG_WLAN_FEATURE_11AX), y) +WMA_OBJS+= $(WMA_SRC_DIR)/wma_he.o +endif +ifeq ($(CONFIG_WLAN_FEATURE_TWT), y) +WMA_OBJS += $(WMA_SRC_DIR)/wma_twt.o +endif +ifeq ($(CONFIG_QCACLD_FEATURE_FW_STATE), y) +WMA_OBJS += $(WMA_SRC_DIR)/wma_fw_state.o +endif +ifeq ($(CONFIG_WLAN_MWS_INFO_DEBUGFS), y) +WMA_OBJS += $(WMA_SRC_DIR)/wma_coex.o +endif +############## PLD ########## +PLD_DIR := core/pld +PLD_INC_DIR := $(PLD_DIR)/inc +PLD_SRC_DIR := $(PLD_DIR)/src + +PLD_INC := -I$(WLAN_ROOT)/$(PLD_INC_DIR) \ + -I$(WLAN_ROOT)/$(PLD_SRC_DIR) + +PLD_OBJS := $(PLD_SRC_DIR)/pld_common.o + +ifeq ($(CONFIG_PCIE_FW_SIM), y) +PLD_OBJS += $(PLD_SRC_DIR)/pld_pcie_fw_sim.o +else ifeq ($(CONFIG_HIF_PCI), y) +PLD_OBJS += $(PLD_SRC_DIR)/pld_pcie.o +endif +ifeq ($(CONFIG_SNOC_FW_SIM),y) +PLD_OBJS += $(PLD_SRC_DIR)/pld_snoc_fw_sim.o +else ifeq ($(CONFIG_ICNSS),y) +PLD_OBJS += $(PLD_SRC_DIR)/pld_snoc.o +else ifeq ($(CONFIG_ICNSS2),y) +PLD_OBJS += $(PLD_SRC_DIR)/pld_ipci.o +endif + +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) +PLD_OBJS += $(PLD_SRC_DIR)/pld_sdio.o +endif +ifeq ($(CONFIG_HIF_USB), y) +PLD_OBJS += $(PLD_SRC_DIR)/pld_usb.o +endif + +TARGET_INC := -I$(WLAN_FW_API)/fw + +ifeq ($(CONFIG_CNSS_QCA6290), y) +ifeq ($(CONFIG_QCA6290_11AX), y) +TARGET_INC += -I$(WLAN_FW_API)/hw/qca6290/11ax/v2 +else +TARGET_INC += -I$(WLAN_FW_API)/hw/qca6290/v2 +endif +endif + +ifeq ($(CONFIG_CNSS_QCA6390), y) +TARGET_INC += -I$(WLAN_FW_API)/hw/qca6390/v1 +endif + +ifeq ($(CONFIG_CNSS_QCA6490), y) +TARGET_INC += -I$(WLAN_FW_API)/hw/qca6490/v1 +endif + +ifeq ($(CONFIG_CNSS_QCA6750), y) +TARGET_INC += -I$(WLAN_FW_API)/hw/qca6750/v1 +endif + +LINUX_INC := -Iinclude + +INCS := $(HDD_INC) \ + $(SYNC_INC) \ + $(DSC_INC) \ + $(EPPING_INC) \ + $(LINUX_INC) \ + $(MAC_INC) \ + $(SAP_INC) \ + $(SME_INC) \ + $(SYS_INC) \ + $(QAL_INC) \ + $(QDF_INC) \ + $(WBUFF_INC) \ + $(CDS_INC) \ + $(CFG_INC) \ + $(DFS_INC) \ + $(TARGET_IF_INC) \ + $(CLD_TARGET_IF_INC) \ + $(OS_IF_INC) \ + $(GLOBAL_LMAC_IF_INC) \ + $(FTM_INC) + +INCS += $(WMA_INC) \ + $(UAPI_INC) \ + $(COMMON_INC) \ + $(WMI_INC) \ + $(FWLOG_INC) \ + $(TXRX_INC) \ + $(OL_INC) \ + $(CDP_INC) \ + $(PKTLOG_INC) \ + $(HTT_INC) \ + $(INIT_DEINIT_INC) \ + $(SCHEDULER_INC) \ + $(REGULATORY_INC) \ + $(HTC_INC) \ + $(DFS_INC) \ + $(WCFG_INC) \ + $(TXRX3.0_INC) + +INCS += $(HIF_INC) \ + $(BMI_INC) \ + $(CMN_SYS_INC) + +ifeq ($(CONFIG_LITHIUM), y) +INCS += $(HAL_INC) \ + $(DP_INC) +endif + +################ WIFI POS ################ +INCS += $(WIFI_POS_API_INC) +INCS += $(WIFI_POS_TGT_INC) +INCS += $(WIFI_POS_OS_IF_INC) +################ CP STATS ################ +INCS += $(CP_STATS_OS_IF_INC) +INCS += $(CP_STATS_TGT_INC) +INCS += $(CP_STATS_DISPATCHER_INC) +INCS += $(COMP_CP_STATS_DISPATCHER_INC) +################ INTEROP ISSUES AP ################ +INCS += $(INTEROP_ISSUES_AP_OS_IF_INC) +INCS += $(INTEROP_ISSUES_AP_TGT_INC) +INCS += $(INTEROP_ISSUES_AP_DISPATCHER_INC) +INCS += $(INTEROP_ISSUES_AP_CORE_INC) +################ NAN POS ################ +INCS += $(NAN_CORE_INC) +INCS += $(NAN_UCFG_INC) +INCS += $(NAN_TGT_INC) +INCS += $(NAN_OS_IF_INC) +########################################## +INCS += $(UMAC_OBJMGR_INC) +INCS += $(UMAC_MGMT_TXRX_INC) +INCS += $(PMO_INC) +INCS += $(P2P_INC) +INCS += $(POLICY_MGR_INC) +INCS += $(TARGET_INC) +INCS += $(TDLS_INC) +INCS += $(UMAC_SER_INC) +INCS += $(NLINK_INC) \ + $(PTT_INC) \ + $(WLAN_LOGGING_INC) + +INCS += $(PLD_INC) +INCS += $(OCB_INC) + +INCS += $(IPA_INC) +INCS += $(UMAC_SM_INC) +INCS += $(UMAC_MLME_INC) +INCS += $(MLME_INC) +INCS += $(FWOL_INC) +INCS += $(BLM_INC) + +ifeq ($(CONFIG_REMOVE_PKT_LOG), n) +INCS += $(PKTLOG_INC) +endif + +INCS += $(HOST_DIAG_LOG_INC) + +INCS += $(DISA_INC) +INCS += $(ACTION_OUI_INC) +INCS += $(PKT_CAPTURE_INC) +INCS += $(FTM_TIME_SYNC_INC) + +INCS += $(UMAC_DISP_INC) +INCS += $(UMAC_SCAN_INC) +INCS += $(UMAC_TARGET_SCAN_INC) +INCS += $(UMAC_GREEN_AP_INC) +INCS += $(UMAC_TARGET_GREEN_AP_INC) +INCS += $(UMAC_COMMON_INC) +INCS += $(UMAC_SPECTRAL_INC) +INCS += $(WLAN_CFR_INC) +INCS += $(UMAC_TARGET_SPECTRAL_INC) +INCS += $(UMAC_GPIO_INC) +INCS += $(UMAC_TARGET_GPIO_INC) +INCS += $(UMAC_DBR_INC) +INCS += $(UMAC_CRYPTO_INC) + +OBJS := $(HDD_OBJS) \ + $(SYNC_OBJS) \ + $(DSC_OBJS) \ + $(MAC_OBJS) \ + $(SAP_OBJS) \ + $(SME_OBJS) \ + $(SYS_OBJS) \ + $(QDF_OBJS) \ + $(WBUFF_OBJS) \ + $(CDS_OBJS) \ + $(CFG_OBJS) \ + $(FTM_OBJS) + +OBJS += $(WMA_OBJS) \ + $(TXRX_OBJS) \ + $(WMI_OBJS) \ + $(HTC_OBJS) \ + $(INIT_DEINIT_OBJS) \ + $(SCHEDULER_OBJS) \ + $(REGULATORY_OBJS) + +OBJS += $(HIF_OBJS) \ + $(BMI_OBJS) \ + $(OS_IF_OBJ) \ + $(TARGET_IF_OBJ) \ + $(CLD_TARGET_IF_OBJ) \ + $(GLOBAL_LMAC_IF_OBJ) + +ifneq ($(CONFIG_LITHIUM), y) +OBJS += $(HTT_OBJS) +endif + +ifeq ($(CONFIG_LITHIUM), y) +OBJS += $(HAL_OBJS) +ifeq ($(CONFIG_WDI_EVENT_ENABLE), y) +OBJS += $(TXRX_DIR)/ol_txrx_event.o +endif +endif + +ifeq ($(CONFIG_FEATURE_FW_LOG_PARSING), y) +OBJS += $(FWLOG_OBJS) +endif + +ifeq ($(CONFIG_FEATURE_EPPING), y) +OBJS += $(EPPING_OBJS) +endif + +ifeq ($(CONFIG_WLAN_DFS_MASTER_ENABLE), y) +OBJS += $(DFS_OBJS) +endif + +OBJS += $(UMAC_OBJMGR_OBJS) +OBJS += $(WIFI_POS_OBJS) +OBJS += $(CP_STATS_OBJS) +OBJS += $(INTEROP_ISSUES_AP_OBJS) +OBJS += $(WLAN_NAN_OBJS) +OBJS += $(UMAC_MGMT_TXRX_OBJS) +OBJS += $(TDLS_OBJS) +OBJS += $(PMO_OBJS) +OBJS += $(P2P_OBJS) +OBJS += $(POLICY_MGR_OBJS) +OBJS += $(WLAN_LOGGING_OBJS) +OBJS += $(NLINK_OBJS) +OBJS += $(PTT_OBJS) +OBJS += $(UMAC_SER_OBJS) +OBJS += $(PLD_OBJS) +OBJS += $(UMAC_SM_OBJS) +OBJS += $(UMAC_MLME_OBJS) +OBJS += $(MLME_OBJS) +ifeq ($(CONFIG_WLAN_FW_OFFLOAD), y) +OBJS += $(FWOL_OBJS) +endif +OBJS += $(BLM_OBJS) + +ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) +OBJS += $(OCB_OBJS) +endif + +ifeq ($(CONFIG_IPA_OFFLOAD), y) +OBJS += $(IPA_OBJS) +endif + +ifeq ($(CONFIG_REMOVE_PKT_LOG), n) +OBJS += $(PKTLOG_OBJS) +endif + +ifeq ($(BUILD_DIAG_VERSION), y) +OBJS += $(HOST_DIAG_LOG_OBJS) +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DISA), y) +OBJS += $(DISA_OBJS) +endif + +ifeq ($(CONFIG_WLAN_FEATURE_ACTION_OUI), y) +OBJS += $(ACTION_OUI_OBJS) +endif + +ifeq ($(CONFIG_WLAN_FEATURE_PKT_CAPTURE), y) +OBJS += $(PKT_CAPTURE_OBJS) +endif + +ifeq ($(CONFIG_FEATURE_WLAN_TIME_SYNC_FTM), y) +OBJS += $(FTM_TIME_SYNC_OBJS) +endif + +OBJS += $(UMAC_DISP_OBJS) +OBJS += $(UMAC_SCAN_OBJS) +OBJS += $(UMAC_COMMON_OBJS) +OBJS += $(WCFG_OBJS) + +OBJS += $(UMAC_SPECTRAL_OBJS) +OBJS += $(UMAC_DBR_OBJS) +OBJS += $(WLAN_CFR_OBJS) +OBJS += $(UMAC_GPIO_OBJS) +ifeq ($(CONFIG_QCACLD_FEATURE_GREEN_AP), y) +OBJS += $(UMAC_GREEN_AP_OBJS) +endif + +ifeq ($(CONFIG_CRYPTO_COMPONENT), y) +OBJS += $(UMAC_CRYPTO_OBJS) +endif + +ifeq ($(CONFIG_LITHIUM), y) +OBJS += $(DP_OBJS) +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DP_RX_THREADS), y) +OBJS += $(TXRX3.0_OBJS) +endif + +ccflags-y += $(INCS) + +cppflags-y += -DANI_OS_TYPE_ANDROID=6 \ + -Wall\ + -D__linux__ + +cppflags-$(CONFIG_PTT_SOCK_SVC_ENABLE) += -DPTT_SOCK_SVC_ENABLE +cppflags-$(CONFIG_FEATURE_WLAN_WAPI) += -DFEATURE_WLAN_WAPI +cppflags-$(CONFIG_FEATURE_WLAN_WAPI) += -DATH_SUPPORT_WAPI +cppflags-$(CONFIG_AGEIE_ON_SCAN_RESULTS) += -DWLAN_ENABLE_AGEIE_ON_SCAN_RESULTS +cppflags-$(CONFIG_SOFTAP_CHANNEL_RANGE) += -DSOFTAP_CHANNEL_RANGE +cppflags-$(CONFIG_FEATURE_WLAN_SCAN_PNO) += -DFEATURE_WLAN_SCAN_PNO +cppflags-$(CONFIG_WLAN_FEATURE_PACKET_FILTERING) += -DWLAN_FEATURE_PACKET_FILTERING +cppflags-$(CONFIG_DHCP_SERVER_OFFLOAD) += -DDHCP_SERVER_OFFLOAD +cppflags-$(CONFIG_WLAN_NS_OFFLOAD) += -DWLAN_NS_OFFLOAD +cppflags-$(CONFIG_FEATURE_WLAN_RA_FILTERING) += -DFEATURE_WLAN_RA_FILTERING +cppflags-$(CONFIG_FEATURE_WLAN_LPHB) += -DFEATURE_WLAN_LPHB +cppflags-$(CONFIG_QCA_SUPPORT_TX_THROTTLE) += -DQCA_SUPPORT_TX_THROTTLE +cppflags-$(CONFIG_WMI_INTERFACE_EVENT_LOGGING) += -DWMI_INTERFACE_EVENT_LOGGING +cppflags-$(CONFIG_WLAN_FEATURE_LINK_LAYER_STATS) += -DWLAN_FEATURE_LINK_LAYER_STATS +cppflags-$(CONFIG_WLAN_FEATURE_MIB_STATS) += -DWLAN_FEATURE_MIB_STATS +cppflags-$(CONFIG_FEATURE_WLAN_EXTSCAN) += -DFEATURE_WLAN_EXTSCAN +cppflags-$(CONFIG_160MHZ_SUPPORT) += -DCONFIG_160MHZ_SUPPORT +cppflags-$(CONFIG_MCL) += -DCONFIG_MCL +cppflags-$(CONFIG_REG_CLIENT) += -DCONFIG_REG_CLIENT +cppflags-$(CONFIG_WLAN_PMO_ENABLE) += -DWLAN_PMO_ENABLE +cppflags-$(CONFIG_CONVERGED_P2P_ENABLE) += -DCONVERGED_P2P_ENABLE +cppflags-$(CONFIG_WLAN_POLICY_MGR_ENABLE) += -DWLAN_POLICY_MGR_ENABLE +cppflags-$(CONFIG_FEATURE_BLACKLIST_MGR) += -DFEATURE_BLACKLIST_MGR +cppflags-$(CONFIG_WAPI_BIG_ENDIAN) += -DFEATURE_WAPI_BIG_ENDIAN +cppflags-$(CONFIG_SUPPORT_11AX) += -DSUPPORT_11AX +cppflags-$(CONFIG_HDD_INIT_WITH_RTNL_LOCK) += -DCONFIG_HDD_INIT_WITH_RTNL_LOCK +cppflags-$(CONFIG_WLAN_CONV_SPECTRAL_ENABLE) += -DWLAN_CONV_SPECTRAL_ENABLE +cppflags-$(CONFIG_WLAN_CFR_ENABLE) += -DWLAN_CFR_ENABLE +cppflags-$(CONFIG_WLAN_ENH_CFR_ENABLE) += -DWLAN_ENH_CFR_ENABLE +cppflags-$(CONFIG_WLAN_CFR_ENABLE) += -DCFR_USE_FIXED_FOLDER +cppflags-$(CONFIG_DIRECT_BUF_RX_ENABLE) += -DDIRECT_BUF_RX_ENABLE +cppflags-$(CONFIG_WMI_DBR_SUPPORT) += -DWMI_DBR_SUPPORT +cppflags-$(CONFIG_DIRECT_BUF_RX_ENABLE) += -DDBR_MULTI_SRNG_ENABLE +cppflags-$(CONFIG_WMI_CMD_STRINGS) += -DWMI_CMD_STRINGS +cppflags-$(CONFIG_WLAN_FEATURE_TWT) += -DWLAN_SUPPORT_TWT + +cppflags-$(CONFIG_WLAN_DISABLE_EXPORT_SYMBOL) += -DWLAN_DISABLE_EXPORT_SYMBOL +cppflags-$(CONFIG_WIFI_POS_CONVERGED) += -DWIFI_POS_CONVERGED +cppflags-$(CONFIG_WIFI_POS_LEGACY) += -DFEATURE_OEM_DATA_SUPPORT +cppflags-$(CONFIG_FEATURE_HTC_CREDIT_HISTORY) += -DFEATURE_HTC_CREDIT_HISTORY +cppflags-$(CONFIG_WLAN_FEATURE_P2P_DEBUG) += -DWLAN_FEATURE_P2P_DEBUG +cppflags-$(CONFIG_WLAN_WEXT_SUPPORT_ENABLE) += -DWLAN_WEXT_SUPPORT_ENABLE +cppflags-$(CONFIG_WLAN_LOGGING_SOCK_SVC) += -DWLAN_LOGGING_SOCK_SVC_ENABLE +cppflags-$(CONFIG_WLAN_LOGGING_BUFFERS_DYNAMICALLY) += -DWLAN_LOGGING_BUFFERS_DYNAMICALLY +cppflags-$(CONFIG_WLAN_FEATURE_FILS) += -DWLAN_FEATURE_FILS_SK +cppflags-$(CONFIG_CP_STATS) += -DQCA_SUPPORT_CP_STATS +cppflags-$(CONFIG_FEATURE_INTEROP_ISSUES_AP) += -DWLAN_FEATURE_INTEROP_ISSUES_AP +cppflags-$(CONFIG_FEATURE_MEMDUMP_ENABLE) += -DWLAN_FEATURE_MEMDUMP_ENABLE +cppflags-$(CONFIG_FEATURE_FW_LOG_PARSING) += -DFEATURE_FW_LOG_PARSING +cppflags-$(CONFIG_FEATURE_OEM_DATA) += -DFEATURE_OEM_DATA +cppflags-$(CONFIG_FEATURE_MOTION_DETECTION) += -DWLAN_FEATURE_MOTION_DETECTION +cppflags-$(CONFIG_WLAN_FW_OFFLOAD) += -DWLAN_FW_OFFLOAD +cppflags-$(CONFIG_WLAN_FEATURE_ELNA) += -DWLAN_FEATURE_ELNA + +cppflags-$(CONFIG_PLD_IPCI_ICNSS_FLAG) += -DCONFIG_PLD_IPCI_ICNSS +cppflags-$(CONFIG_PLD_SDIO_CNSS_FLAG) += -DCONFIG_PLD_SDIO_CNSS +cppflags-$(CONFIG_FEATURE_GPIO_CFG) += -DWLAN_FEATURE_GPIO_CFG + +ifeq ($(CONFIG_PLD_PCIE_CNSS_FLAG), y) +ifeq ($(CONFIG_PCIE_FW_SIM), y) +cppflags-y += -DCONFIG_PLD_PCIE_FW_SIM +else +cppflags-y += -DCONFIG_PLD_PCIE_CNSS +endif +endif + +cppflags-$(CONFIG_PLD_PCIE_INIT_FLAG) += -DCONFIG_PLD_PCIE_INIT +cppflags-$(CONFIG_WLAN_FEATURE_DP_RX_THREADS) += -DFEATURE_WLAN_DP_RX_THREADS +cppflags-$(CONFIG_WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT) += -DWLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT +cppflags-$(CONFIG_FEATURE_HAL_DELAYED_REG_WRITE) += -DFEATURE_HAL_DELAYED_REG_WRITE +cppflags-$(CONFIG_SHADOW_WRITE_DELAY) += -DSHADOW_WRITE_DELAY + +cppflags-$(CONFIG_PLD_USB_CNSS) += -DCONFIG_PLD_USB_CNSS +cppflags-$(CONFIG_PLD_SDIO_CNSS2) += -DCONFIG_PLD_SDIO_CNSS2 +cppflags-$(CONFIG_WLAN_RECORD_RX_PADDR) += -DHIF_RECORD_RX_PADDR +cppflags-$(CONFIG_FEATURE_WLAN_TIME_SYNC_FTM) += -DFEATURE_WLAN_TIME_SYNC_FTM + +#For both legacy and lithium chip's monitor mode config +ifeq ($(CONFIG_FEATURE_MONITOR_MODE_SUPPORT), y) +cppflags-y += -DFEATURE_MONITOR_MODE_SUPPORT +else +cppflags-y += -DDISABLE_MON_CONFIG +endif + +#Enable NL80211 test mode +cppflags-$(CONFIG_NL80211_TESTMODE) += -DWLAN_NL80211_TESTMODE + +# Flag to enable bus auto suspend +ifeq ($(CONFIG_HIF_PCI), y) +ifeq ($(CONFIG_BUS_AUTO_SUSPEND), y) +cppflags-y += -DFEATURE_RUNTIME_PM +endif +endif + +ifeq ($(CONFIG_ICNSS), y) +ifeq ($(CONFIG_SNOC_FW_SIM), y) +cppflags-y += -DCONFIG_PLD_SNOC_FW_SIM +else +cppflags-y += -DCONFIG_PLD_SNOC_ICNSS +endif +endif + +cppflags-$(CONFIG_WIFI_3_0_ADRASTEA) += -DQCA_WIFI_3_0_ADRASTEA +cppflags-$(CONFIG_WIFI_3_0_ADRASTEA) += -DQCA_WIFI_3_0 +cppflags-$(CONFIG_ADRASTEA_SHADOW_REGISTERS) += -DADRASTEA_SHADOW_REGISTERS +cppflags-$(CONFIG_ADRASTEA_RRI_ON_DDR) += -DADRASTEA_RRI_ON_DDR + +ifeq ($(CONFIG_QMI_SUPPORT), n) +cppflags-y += -DCONFIG_BYPASS_QMI +endif + +cppflags-$(CONFIG_WLAN_FASTPATH) += -DWLAN_FEATURE_FASTPATH + +cppflags-$(CONFIG_FEATURE_PKTLOG) += -DFEATURE_PKTLOG + +ifeq ($(CONFIG_WLAN_NAPI), y) +cppflags-y += -DFEATURE_NAPI +cppflags-y += -DHIF_IRQ_AFFINITY +ifeq ($(CONFIG_WLAN_NAPI_DEBUG), y) +cppflags-y += -DFEATURE_NAPI_DEBUG +endif +endif + +ifeq (y,$(findstring y,$(CONFIG_ARCH_MSM) $(CONFIG_ARCH_QCOM))) +cppflags-y += -DMSM_PLATFORM +endif + +cppflags-$(CONFIG_WLAN_FEATURE_DP_BUS_BANDWIDTH) += -DWLAN_FEATURE_DP_BUS_BANDWIDTH +cppflags-$(CONFIG_WLAN_FEATURE_PERIODIC_STA_STATS) += -DWLAN_FEATURE_PERIODIC_STA_STATS + +cppflags-y += -DQCA_SUPPORT_TXRX_LOCAL_PEER_ID + +cppflags-$(CONFIG_WLAN_TX_FLOW_CONTROL_V2) += -DQCA_LL_TX_FLOW_CONTROL_V2 +cppflags-$(CONFIG_WLAN_TX_FLOW_CONTROL_V2) += -DQCA_LL_TX_FLOW_GLOBAL_MGMT_POOL +cppflags-$(CONFIG_WLAN_TX_FLOW_CONTROL_LEGACY) += -DQCA_LL_LEGACY_TX_FLOW_CONTROL +cppflags-$(CONFIG_WLAN_PDEV_TX_FLOW_CONTROL) += -DQCA_LL_PDEV_TX_FLOW_CONTROL + +ifeq ($(BUILD_DEBUG_VERSION), y) +cppflags-y += -DWLAN_DEBUG +ifeq ($(CONFIG_TRACE_RECORD_FEATURE), y) +cppflags-y += -DTRACE_RECORD \ + -DLIM_TRACE_RECORD \ + -DSME_TRACE_RECORD \ + -DHDD_TRACE_RECORD +endif +endif +cppflags-$(CONFIG_UNIT_TEST) += -DWLAN_UNIT_TEST +cppflags-$(CONFIG_WLAN_DEBUG_CRASH_INJECT) += -DCONFIG_WLAN_DEBUG_CRASH_INJECT +cppflags-$(CONFIG_FEATURE_UNIT_TEST_SUSPEND) += -DWLAN_SUSPEND_RESUME_TEST +cppflags-$(CONFIG_FEATURE_WLM_STATS) += -DFEATURE_WLM_STATS + +ifeq ($(CONFIG_LEAK_DETECTION), y) +cppflags-y += \ + -DCONFIG_HALT_KMEMLEAK \ + -DCONFIG_LEAK_DETECTION \ + -DMEMORY_DEBUG \ + -DNBUF_MEMORY_DEBUG \ + -DNBUF_MAP_UNMAP_DEBUG \ + -DTIMER_MANAGER \ + -DWLAN_DELAYED_WORK_DEBUG \ + -DWLAN_PERIODIC_WORK_DEBUG +endif + +cppflags-y += -DWLAN_FEATURE_P2P +cppflags-y += -DWLAN_FEATURE_WFD + +ifeq ($(CONFIG_QCOM_VOWIFI_11R), y) +cppflags-y += -DKERNEL_SUPPORT_11R_CFG80211 +cppflags-y += -DUSE_80211_WMMTSPEC_FOR_RIC +endif + +ifeq ($(CONFIG_QCOM_ESE), y) +cppflags-y += -DFEATURE_WLAN_ESE +endif + +#normally, TDLS negative behavior is not needed +cppflags-$(CONFIG_QCOM_TDLS) += -DFEATURE_WLAN_TDLS +ifeq ($(CONFIG_LITHIUM), y) +cppflags-$(CONFIG_QCOM_TDLS) += -DTDLS_WOW_ENABLED +endif + + +cppflags-$(CONFIG_QCACLD_WLAN_LFR3) += -DWLAN_FEATURE_ROAM_OFFLOAD + +cppflags-$(CONFIG_WLAN_FEATURE_MBSSID) += -DWLAN_FEATURE_MBSSID + +cppflags-$(CONFIG_CNSS_GENL) += -DCNSS_GENL + +cppflags-$(CONFIG_QCACLD_WLAN_LFR2) += -DWLAN_FEATURE_HOST_ROAM + +cppflags-$(CONFIG_FEATURE_ROAM_DEBUG) += -DFEATURE_ROAM_DEBUG + +cppflags-$(CONFIG_WLAN_POWER_DEBUG) += -DWLAN_POWER_DEBUG + +cppflags-$(CONFIG_WLAN_MWS_INFO_DEBUGFS) += -DWLAN_MWS_INFO_DEBUGFS + +# Enable object manager reference count debug infrastructure +cppflags-$(CONFIG_WLAN_OBJMGR_DEBUG) += -DWLAN_OBJMGR_DEBUG +cppflags-$(CONFIG_WLAN_OBJMGR_DEBUG) += -DWLAN_OBJMGR_REF_ID_DEBUG + +cppflags-$(CONFIG_WLAN_FEATURE_SAE) += -DWLAN_FEATURE_SAE + +ifeq ($(BUILD_DIAG_VERSION), y) +cppflags-y += -DFEATURE_WLAN_DIAG_SUPPORT +cppflags-y += -DFEATURE_WLAN_DIAG_SUPPORT_CSR +cppflags-y += -DFEATURE_WLAN_DIAG_SUPPORT_LIM +ifeq ($(CONFIG_HIF_PCI), y) +cppflags-y += -DCONFIG_ATH_PROCFS_DIAG_SUPPORT +endif +ifeq ($(CONFIG_HIF_IPCI), y) +cppflags-y += -DCONFIG_ATH_PROCFS_DIAG_SUPPORT +endif +endif + +ifeq ($(CONFIG_HIF_USB), y) +cppflags-y += -DCONFIG_ATH_PROCFS_DIAG_SUPPORT +cppflags-y += -DQCA_SUPPORT_OL_RX_REORDER_TIMEOUT +cppflags-y += -DCONFIG_ATH_PCIE_MAX_PERF=0 -DCONFIG_ATH_PCIE_AWAKE_WHILE_DRIVER_LOAD=0 -DCONFIG_DISABLE_CDC_MAX_PERF_WAR=0 +endif + +cppflags-$(CONFIG_QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK) += -DQCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK + +cppflags-$(CONFIG_WLAN_FEATURE_11W) += -DWLAN_FEATURE_11W + +cppflags-$(CONFIG_QCA_TXDESC_SANITY_CHECKS) += -DQCA_SUPPORT_TXDESC_SANITY_CHECKS + +cppflags-$(CONFIG_QCOM_LTE_COEX) += -DFEATURE_WLAN_CH_AVOID + +cppflags-$(CONFIG_WLAN_FEATURE_LPSS) += -DWLAN_FEATURE_LPSS + +cppflags-$(CONFIG_DESC_DUP_DETECT_DEBUG) += -DDESC_DUP_DETECT_DEBUG +cppflags-$(CONFIG_DEBUG_RX_RING_BUFFER) += -DDEBUG_RX_RING_BUFFER + +cppflags-$(CONFIG_DESC_TIMESTAMP_DEBUG_INFO) += -DDESC_TIMESTAMP_DEBUG_INFO + +cppflags-$(PANIC_ON_BUG) += -DPANIC_ON_BUG + +cppflags-$(WLAN_WARN_ON_ASSERT) += -DWLAN_WARN_ON_ASSERT + +ccflags-$(CONFIG_POWER_MANAGEMENT_OFFLOAD) += -DWLAN_POWER_MANAGEMENT_OFFLOAD + +cppflags-$(CONFIG_WLAN_LOG_FATAL) += -DWLAN_LOG_FATAL +cppflags-$(CONFIG_WLAN_LOG_ERROR) += -DWLAN_LOG_ERROR +cppflags-$(CONFIG_WLAN_LOG_WARN) += -DWLAN_LOG_WARN +cppflags-$(CONFIG_WLAN_LOG_INFO) += -DWLAN_LOG_INFO +cppflags-$(CONFIG_WLAN_LOG_DEBUG) += -DWLAN_LOG_DEBUG +cppflags-$(CONFIG_WLAN_LOG_ENTER) += -DWLAN_LOG_ENTER +cppflags-$(CONFIG_WLAN_LOG_EXIT) += -DWLAN_LOG_EXIT +cppflags-$(WLAN_OPEN_SOURCE) += -DWLAN_OPEN_SOURCE +cppflags-$(CONFIG_FEATURE_STATS_EXT) += -DWLAN_FEATURE_STATS_EXT +cppflags-$(CONFIG_QCACLD_FEATURE_NAN) += -DWLAN_FEATURE_NAN +cppflags-$(CONFIG_NDP_SAP_CONCURRENCY_ENABLE) += -DNDP_SAP_CONCURRENCY_ENABLE + +ifeq ($(CONFIG_QCA_IBSS_SUPPORT), y) +cppflags-$(CONFIG_QCA_IBSS_SUPPORT) += -DQCA_IBSS_SUPPORT +endif + +ifeq ($(CONFIG_DFS_FCC_TYPE4_DURATION_CHECK), y) +cppflags-$(CONFIG_DFS_FCC_TYPE4_DURATION_CHECK) += -DDFS_FCC_TYPE4_DURATION_CHECK +endif + +cppflags-$(CONFIG_WLAN_SYSFS) += -DWLAN_SYSFS +cppflags-$(CONFIG_FEATURE_WLAN_RMC) += -DFEATURE_WLAN_RMC +cppflags-$(CONFIG_FEATURE_BECN_STATS) += -DWLAN_FEATURE_BEACON_RECEPTION_STATS + +#Set RX_PERFORMANCE +cppflags-$(CONFIG_RX_PERFORMANCE) += -DRX_PERFORMANCE + +#Set MULTI_IF_LOG +cppflags-$(CONFIG_MULTI_IF_LOG) += -DMULTI_IF_LOG + +#Set SLUB_MEM_OPTIMIZE +cppflags-$(CONFIG_SLUB_MEM_OPTIMIZE) += -DSLUB_MEM_OPTIMIZE + +#Set DFS_PRI_MULTIPLIER +cppflags-$(CONFIG_DFS_PRI_MULTIPLIER) += -DDFS_PRI_MULTIPLIER + +#Set DFS_OVERRIDE_RF_THRESHOLD +cppflags-$(CONFIG_DFS_OVERRIDE_RF_THRESHOLD) += -DDFS_OVERRIDE_RF_THRESHOLD + +#Enable OL debug and wmi unified functions +cppflags-$(CONFIG_ATH_PERF_PWR_OFFLOAD) += -DATH_PERF_PWR_OFFLOAD + +#Disable packet log +cppflags-$(CONFIG_REMOVE_PKT_LOG) += -DREMOVE_PKT_LOG + +#Enable 11AC TX +cppflags-$(CONFIG_ATH_11AC_TXCOMPACT) += -DATH_11AC_TXCOMPACT + +#Enable PCI specific APIS (dma, etc) +cppflags-$(CONFIG_HIF_PCI) += -DHIF_PCI + +cppflags-$(CONFIG_HIF_IPCI) += -DHIF_IPCI + +cppflags-$(CONFIG_HIF_SNOC) += -DHIF_SNOC + +cppflags-$(CONFIG_HL_DP_SUPPORT) += -DCONFIG_HL_SUPPORT +cppflags-$(CONFIG_HL_DP_SUPPORT) += -DWLAN_PARTIAL_REORDER_OFFLOAD +cppflags-$(CONFIG_HL_DP_SUPPORT) += -DQCA_COMPUTE_TX_DELAY +cppflags-$(CONFIG_HL_DP_SUPPORT) += -DQCA_COMPUTE_TX_DELAY_PER_TID +cppflags-$(CONFIG_LL_DP_SUPPORT) += -DCONFIG_LL_DP_SUPPORT +cppflags-$(CONFIG_LL_DP_SUPPORT) += -DWLAN_FULL_REORDER_OFFLOAD + +# For OOB testing +cppflags-$(CONFIG_WLAN_FEATURE_WOW_PULSE) += -DWLAN_FEATURE_WOW_PULSE + +cppflags-$(CONFIG_AR6320_SUPPORT) += -DCONFIG_AR6320_SUPPORT +#Enable High Latency related Flags +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) +cppflags-y += -DSDIO_3_0 \ + -DHIF_SDIO \ + -DCONFIG_DISABLE_CDC_MAX_PERF_WAR=0 \ + -DCONFIG_ATH_PROCFS_DIAG_SUPPORT \ + -DHIF_MBOX_SLEEP_WAR \ + -DDEBUG_HL_LOGGING \ + -DQCA_BAD_PEER_TX_FLOW_CL \ + -DCONFIG_SDIO \ + -DFEATURE_WLAN_FORCE_SAP_SCC + +ifeq ($(CONFIG_SDIO_TRANSFER), adma) +cppflags-y += -DCONFIG_SDIO_TRANSFER_ADMA +else +cppflags-y += -DCONFIG_SDIO_TRANSFER_MAILBOX +endif +endif + +ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) +cppflags-y += -DWLAN_FEATURE_DSRC + +ifeq ($(CONFIG_OCB_UT_FRAMEWORK), y) +cppflags-y += -DWLAN_OCB_UT +endif + +endif + +cppflags-$(CONFIG_FEATURE_SKB_PRE_ALLOC) += -DFEATURE_SKB_PRE_ALLOC + +#Enable USB specific APIS +ifeq ($(CONFIG_HIF_USB), y) +cppflags-y += -DHIF_USB \ + -DDEBUG_HL_LOGGING +endif + +#Enable Genoa specific features. +cppflags-$(CONFIG_QCA_HL_NETDEV_FLOW_CONTROL) += -DQCA_HL_NETDEV_FLOW_CONTROL +cppflags-$(CONFIG_FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL) += -DFEATURE_HL_GROUP_CREDIT_FLOW_CONTROL +cppflags-$(CONFIG_FEATURE_HL_DBS_GROUP_CREDIT_SHARING) += -DFEATURE_HL_DBS_GROUP_CREDIT_SHARING +cppflags-$(CONFIG_CREDIT_REP_THROUGH_CREDIT_UPDATE) += -DCONFIG_CREDIT_REP_THROUGH_CREDIT_UPDATE +cppflags-$(CONFIG_RX_PN_CHECK_OFFLOAD) += -DCONFIG_RX_PN_CHECK_OFFLOAD + +cppflags-$(CONFIG_WLAN_SYNC_TSF_TIMER) += -DWLAN_FEATURE_TSF_TIMER_SYNC +cppflags-$(CONFIG_WLAN_SYNC_TSF_PTP) += -DWLAN_FEATURE_TSF_PTP +cppflags-$(CONFIG_WLAN_SYNC_TSF_PLUS_EXT_GPIO_IRQ) += -DWLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ +cppflags-$(CONFIG_WLAN_SYNC_TSF_PLUS_EXT_GPIO_SYNC) += -DWLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC +cppflags-$(CONFIG_TX_DESC_HI_PRIO_RESERVE) += -DCONFIG_TX_DESC_HI_PRIO_RESERVE + +#Enable FW logs through ini +cppflags-y += -DCONFIG_FW_LOGS_BASED_ON_INI + +#Enable power management suspend/resume functionality +cppflags-$(CONFIG_ATH_BUS_PM) += -DATH_BUS_PM + +#Enable FLOWMAC module support +cppflags-$(CONFIG_ATH_SUPPORT_FLOWMAC_MODULE) += -DATH_SUPPORT_FLOWMAC_MODULE + +#Enable spectral support +cppflags-$(CONFIG_ATH_SUPPORT_SPECTRAL) += -DATH_SUPPORT_SPECTRAL + +#Enable legacy pktlog +cppflags-$(CONFIG_PKTLOG_LEGACY) += -DPKTLOG_LEGACY + +#Enable WDI Event support +cppflags-$(CONFIG_WDI_EVENT_ENABLE) += -DWDI_EVENT_ENABLE + +#Enable the type_specific_data in the struct ath_pktlog_arg +cppflags-$(CONFIG_PKTLOG_HAS_SPECIFIC_DATA) += -DPKTLOG_HAS_SPECIFIC_DATA + +#Endianness selection +ifeq ($(CONFIG_LITTLE_ENDIAN), y) +cppflags-y += -DANI_LITTLE_BYTE_ENDIAN +cppflags-y += -DANI_LITTLE_BIT_ENDIAN +cppflags-y += -DDOT11F_LITTLE_ENDIAN_HOST +else +cppflags-y += -DANI_BIG_BYTE_ENDIAN +cppflags-y += -DBIG_ENDIAN_HOST +endif + +#Enable MWS COEX support for 4G quick TDM and 5G NR pwr limit +cppflags-y += -DMWS_COEX + +#Enable TX reclaim support +cppflags-$(CONFIG_TX_CREDIT_RECLAIM_SUPPORT) += -DTX_CREDIT_RECLAIM_SUPPORT + +#Enable FTM support +cppflags-$(CONFIG_QCA_WIFI_FTM) += -DQCA_WIFI_FTM +cppflags-$(CONFIG_NL80211_TESTMODE) += -DQCA_WIFI_FTM_NL80211 +cppflags-$(CONFIG_LINUX_QCMBR) += -DLINUX_QCMBR -DQCA_WIFI_FTM_IOCTL + +#Enable Checksum Offload support +cppflags-$(CONFIG_CHECKSUM_OFFLOAD) += -DCHECKSUM_OFFLOAD + +#Enable IPA Offload support +cppflags-$(CONFIG_IPA_OFFLOAD) += -DIPA_OFFLOAD + +cppflags-$(CONFIG_WDI3_IPA_OVER_GSI) += -DIPA_WDI3_GSI + +ifeq ($(CONFIG_ARCH_SDX20), y) +cppflags-y += -DSYNC_IPA_READY +endif + +ifeq ($(CONFIG_ARCH_SDXPOORWILLS), y) +CONFIG_FEATURE_SG := y +endif + +ifeq ($(CONFIG_ARCH_MSM8996), y) +ifneq ($(CONFIG_QCN7605_SUPPORT), y) +CONFIG_FEATURE_SG := y +CONFIG_RX_THREAD_PRIORITY := y +endif +endif + +ifeq ($(CONFIG_FEATURE_SG), y) +cppflags-y += -DFEATURE_SG +endif + +ifeq ($(CONFIG_RX_THREAD_PRIORITY), y) +cppflags-y += -DRX_THREAD_PRIORITY +endif + +ifeq ($(CONFIG_SUPPORT_P2P_BY_ONE_INTF_WLAN), y) +#sta support to tx P2P action frames +cppflags-y += -DSUPPORT_P2P_BY_ONE_INTF_WLAN +else +#Open P2P device interface only for non-Mobile router use cases +cppflags-$(CONFIG_WLAN_OPEN_P2P_INTERFACE) += -DWLAN_OPEN_P2P_INTERFACE +endif + +cppflags-$(CONFIG_WMI_BCN_OFFLOAD) += -DWLAN_WMI_BCN + +#Enable wbuff +cppflags-$(CONFIG_WLAN_WBUFF) += -DWLAN_FEATURE_WBUFF + +#Enable GTK Offload +cppflags-$(CONFIG_GTK_OFFLOAD) += -DWLAN_FEATURE_GTK_OFFLOAD + +#Enable External WoW +cppflags-$(CONFIG_EXT_WOW) += -DWLAN_FEATURE_EXTWOW_SUPPORT + +#Mark it as SMP Kernel +cppflags-$(CONFIG_SMP) += -DQCA_CONFIG_SMP + +cppflags-$(CONFIG_CHNL_MATRIX_RESTRICTION) += -DWLAN_ENABLE_CHNL_MATRIX_RESTRICTION + +#Enable ICMP packet disable powersave feature +cppflags-$(CONFIG_ICMP_DISABLE_PS) += -DWLAN_ICMP_DISABLE_PS + +#Enable OBSS feature +cppflags-y += -DQCA_HT_2040_COEX + +#enable MCC TO SCC switch +cppflags-$(CONFIG_FEATURE_WLAN_MCC_TO_SCC_SWITCH) += -DFEATURE_WLAN_MCC_TO_SCC_SWITCH + +#enable wlan auto shutdown feature +cppflags-$(CONFIG_FEATURE_WLAN_AUTO_SHUTDOWN) += -DFEATURE_WLAN_AUTO_SHUTDOWN + +#enable AP-AP ACS Optimization +cppflags-$(CONFIG_FEATURE_WLAN_AP_AP_ACS_OPTIMIZE) += -DFEATURE_WLAN_AP_AP_ACS_OPTIMIZE + +#Enable 4address scheme +cppflags-$(CONFIG_FEATURE_WLAN_STA_4ADDR_SCHEME) += -DFEATURE_WLAN_STA_4ADDR_SCHEME + +#enable MDM/SDX special config +cppflags-$(CONFIG_MDM_PLATFORM) += -DMDM_PLATFORM + +#Disable STA-AP Mode DFS support +cppflags-$(CONFIG_FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE) += -DFEATURE_WLAN_STA_AP_MODE_DFS_DISABLE + +#Enable 2.4 GHz social channels in 5 GHz only mode for p2p usage +cppflags-$(CONFIG_WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY) += -DWLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY + +#Green AP feature +cppflags-$(CONFIG_QCACLD_FEATURE_GREEN_AP) += -DWLAN_SUPPORT_GREEN_AP + +cppflags-$(CONFIG_QCACLD_FEATURE_APF) += -DFEATURE_WLAN_APF + +cppflags-$(CONFIG_WLAN_FEATURE_SARV1_TO_SARV2) += -DWLAN_FEATURE_SARV1_TO_SARV2 +#CRYPTO Coverged Component +cppflags-$(CONFIG_CRYPTO_COMPONENT) += -DWLAN_CONV_CRYPTO_SUPPORTED \ + -DCRYPTO_SET_KEY_CONVERGED \ + -DWLAN_CRYPTO_WEP_OS_DERIVATIVE \ + -DWLAN_CRYPTO_TKIP_OS_DERIVATIVE \ + -DWLAN_CRYPTO_CCMP_OS_DERIVATIVE \ + -DWLAN_CRYPTO_GCMP_OS_DERIVATIVE \ + -DWLAN_CRYPTO_WAPI_OS_DERIVATIVE \ + -DWLAN_CRYPTO_GCM_OS_DERIVATIVE \ + -DWLAN_CRYPTO_FILS_OS_DERIVATIVE \ + -DWLAN_CRYPTO_OMAC1_OS_DERIVATIVE + +cppflags-$(CONFIG_FEATURE_WLAN_FT_IEEE8021X) += -DFEATURE_WLAN_FT_IEEE8021X +cppflags-$(CONFIG_FEATURE_WLAN_FT_PSK) += -DFEATURE_WLAN_FT_PSK + +#Enable host 11d scan +cppflags-$(CONFIG_HOST_11D_SCAN) += -DHOST_11D_SCAN + +#Stats & Quota Metering feature +ifeq ($(CONFIG_IPA_OFFLOAD), y) +ifeq ($(CONFIG_QCACLD_FEATURE_METERING), y) +cppflags-y += -DFEATURE_METERING +endif +endif + +#Define Max IPA interface +ifeq ($(CONFIG_IPA_OFFLOAD), y) +ifdef CONFIG_NUM_IPA_IFACE +cppflags-y += -DMAX_IPA_IFACE=$(CONFIG_NUM_IPA_IFACE) +else +NUM_IPA_IFACE ?= 3 +cppflags-y += -DMAX_IPA_IFACE=$(NUM_IPA_IFACE) +endif +endif + +cppflags-$(CONFIG_TUFELLO_DUAL_FW_SUPPORT) += -DCONFIG_TUFELLO_DUAL_FW_SUPPORT +cppflags-$(CONFIG_CHANNEL_HOPPING_ALL_BANDS) += -DCHANNEL_HOPPING_ALL_BANDS + +#Enable Signed firmware support for split binary format +cppflags-$(CONFIG_QCA_SIGNED_SPLIT_BINARY_SUPPORT) += -DQCA_SIGNED_SPLIT_BINARY_SUPPORT + +#Enable single firmware binary format +cppflags-$(CONFIG_QCA_SINGLE_BINARY_SUPPORT) += -DQCA_SINGLE_BINARY_SUPPORT + +#Enable collecting target RAM dump after kernel panic +cppflags-$(CONFIG_TARGET_RAMDUMP_AFTER_KERNEL_PANIC) += -DTARGET_RAMDUMP_AFTER_KERNEL_PANIC + +#Enable/disable secure firmware feature +cppflags-$(CONFIG_FEATURE_SECURE_FIRMWARE) += -DFEATURE_SECURE_FIRMWARE + +cppflags-$(CONFIG_ATH_PCIE_ACCESS_DEBUG) += -DCONFIG_ATH_PCIE_ACCESS_DEBUG + +# Enable feature support for Linux version QCMBR +cppflags-$(CONFIG_LINUX_QCMBR) += -DLINUX_QCMBR + +# Enable feature sync tsf between multi devices +cppflags-$(CONFIG_WLAN_SYNC_TSF) += -DWLAN_FEATURE_TSF +cppflags-$(CONFIG_WLAN_SYNC_TSF_PLUS) += -DWLAN_FEATURE_TSF_PLUS +# Enable feature sync tsf for chips based on Adrastea arch +cppflags-$(CONFIG_WLAN_SYNC_TSF_PLUS_NOIRQ) += -DWLAN_FEATURE_TSF_PLUS_NOIRQ + +cppflags-$(CONFIG_ATH_PROCFS_DIAG_SUPPORT) += -DCONFIG_ATH_PROCFS_DIAG_SUPPORT + +cppflags-$(CONFIG_HELIUMPLUS) += -DHELIUMPLUS +cppflags-$(CONFIG_RX_OL) += -DRECEIVE_OFFLOAD +cppflags-$(CONFIG_TX_TID_OVERRIDE) += -DATH_TX_PRI_OVERRIDE +cppflags-$(CONFIG_AR900B) += -DAR900B +cppflags-$(CONFIG_HTT_PADDR64) += -DHTT_PADDR64 +cppflags-$(CONFIG_OL_RX_INDICATION_RECORD) += -DOL_RX_INDICATION_RECORD +cppflags-$(CONFIG_TSOSEG_DEBUG) += -DTSOSEG_DEBUG +cppflags-$(CONFIG_ALLOW_PKT_DROPPING) += -DFEATURE_ALLOW_PKT_DROPPING + +cppflags-$(CONFIG_ENABLE_DEBUG_ADDRESS_MARKING) += -DENABLE_DEBUG_ADDRESS_MARKING +cppflags-$(CONFIG_FEATURE_TSO) += -DFEATURE_TSO +cppflags-$(CONFIG_FEATURE_TSO_DEBUG) += -DFEATURE_TSO_DEBUG +cppflags-$(CONFIG_FEATURE_TSO_STATS) += -DFEATURE_TSO_STATS +cppflags-$(CONFIG_FEATURE_FORCE_WAKE) += -DFORCE_WAKE +cppflags-$(CONFIG_WLAN_LRO) += -DFEATURE_LRO + +cppflags-$(CONFIG_FEATURE_AP_MCC_CH_AVOIDANCE) += -DFEATURE_AP_MCC_CH_AVOIDANCE + +cppflags-$(CONFIG_MPC_UT_FRAMEWORK) += -DMPC_UT_FRAMEWORK + +cppflags-$(CONFIG_FEATURE_EPPING) += -DWLAN_FEATURE_EPPING + +cppflags-$(CONFIG_WLAN_OFFLOAD_PACKETS) += -DWLAN_FEATURE_OFFLOAD_PACKETS + +cppflags-$(CONFIG_WLAN_FEATURE_DISA) += -DWLAN_FEATURE_DISA + +cppflags-$(CONFIG_WLAN_FEATURE_ACTION_OUI) += -DWLAN_FEATURE_ACTION_OUI + +cppflags-$(CONFIG_WLAN_FEATURE_FIPS) += -DWLAN_FEATURE_FIPS + +cppflags-$(CONFIG_LFR_SUBNET_DETECTION) += -DFEATURE_LFR_SUBNET_DETECTION + +cppflags-$(CONFIG_MCC_TO_SCC_SWITCH) += -DFEATURE_WLAN_MCC_TO_SCC_SWITCH + +cppflags-$(CONFIG_FEATURE_WLAN_D0WOW) += -DFEATURE_WLAN_D0WOW + +cppflags-$(CONFIG_WLAN_FEATURE_PKT_CAPTURE) += -DWLAN_FEATURE_PKT_CAPTURE + +cppflags-$(CONFIG_QCA_WIFI_NAPIER_EMULATION) += -DQCA_WIFI_NAPIER_EMULATION +cppflags-$(CONFIG_SHADOW_V2) += -DCONFIG_SHADOW_V2 +cppflags-$(CONFIG_QCA6290_HEADERS_DEF) += -DQCA6290_HEADERS_DEF +cppflags-$(CONFIG_QCA_WIFI_QCA6290) += -DQCA_WIFI_QCA6290 +cppflags-$(CONFIG_QCA6390_HEADERS_DEF) += -DQCA6390_HEADERS_DEF +cppflags-$(CONFIG_QCA6750_HEADERS_DEF) += -DQCA6750_HEADERS_DEF +cppflags-$(CONFIG_QCA_WIFI_QCA6390) += -DQCA_WIFI_QCA6390 +cppflags-$(CONFIG_QCA6490_HEADERS_DEF) += -DQCA6490_HEADERS_DEF +cppflags-$(CONFIG_QCA_WIFI_QCA6490) += -DQCA_WIFI_QCA6490 +cppflags-$(CONFIG_QCA_WIFI_QCA6750) += -DQCA_WIFI_QCA6750 +cppflags-$(CONFIG_QCA_WIFI_QCA8074) += -DQCA_WIFI_QCA8074 +cppflags-$(CONFIG_SCALE_INCLUDES) += -DSCALE_INCLUDES +cppflags-$(CONFIG_QCA_WIFI_QCA8074_VP) += -DQCA_WIFI_QCA8074_VP +cppflags-$(CONFIG_DP_INTR_POLL_BASED) += -DDP_INTR_POLL_BASED +cppflags-$(CONFIG_TX_PER_PDEV_DESC_POOL) += -DTX_PER_PDEV_DESC_POOL +cppflags-$(CONFIG_DP_TRACE) += -DCONFIG_DP_TRACE +cppflags-$(CONFIG_FEATURE_TSO) += -DFEATURE_TSO +cppflags-$(CONFIG_TSO_DEBUG_LOG_ENABLE) += -DTSO_DEBUG_LOG_ENABLE +cppflags-$(CONFIG_DP_LFR) += -DDP_LFR +cppflags-$(CONFIG_DUP_RX_DESC_WAR) += -DDUP_RX_DESC_WAR +cppflags-$(CONFIG_DP_MEM_PRE_ALLOC) += -DDP_MEM_PRE_ALLOC +cppflags-$(CONFIG_DP_TXRX_SOC_ATTACH) += -DDP_TXRX_SOC_ATTACH +cppflags-$(CONFIG_HTT_PADDR64) += -DHTT_PADDR64 +cppflags-$(CONFIG_WLAN_FEATURE_BMI) += -DWLAN_FEATURE_BMI +cppflags-$(CONFIG_QCA_TX_PADDING_CREDIT_SUPPORT) += -DQCA_TX_PADDING_CREDIT_SUPPORT +cppflags-$(CONFIG_QCN7605_SUPPORT) += -DQCN7605_SUPPORT -DPLATFORM_GENOA +cppflags-$(CONFIG_HIF_REG_WINDOW_SUPPORT) += -DHIF_REG_WINDOW_SUPPORT +cppflags-$(CONFIG_WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY) += -DWLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY +cppflags-$(CONFIG_HIF_CE_DEBUG_DATA_BUF) += -DHIF_CE_DEBUG_DATA_BUF +cppflags-$(CONFIG_IPA_DISABLE_OVERRIDE) += -DIPA_DISABLE_OVERRIDE +ccflags-$(CONFIG_QCA_LL_TX_FLOW_CONTROL_RESIZE) += -DQCA_LL_TX_FLOW_CONTROL_RESIZE +ccflags-$(CONFIG_HIF_PCI) += -DCE_SVC_CMN_INIT +ccflags-$(CONFIG_HIF_IPCI) += -DCE_SVC_CMN_INIT +ccflags-$(CONFIG_HIF_SNOC) += -DCE_SVC_CMN_INIT +cppflags-$(CONFIG_RX_DESC_SANITY_WAR) += -DRX_DESC_SANITY_WAR +cppflags-$(CONFIG_DYNAMIC_RX_AGGREGATION) += -DWLAN_FEATURE_DYNAMIC_RX_AGGREGATION + +ifeq ($(CONFIG_QCA6290_11AX), y) +cppflags-y += -DQCA_WIFI_QCA6290_11AX -DQCA_WIFI_QCA6290_11AX_MU_UL +endif + +ifeq ($(CONFIG_LITHIUM), y) +cppflags-$(CONFIG_WLAN_TX_FLOW_CONTROL_V2) += -DQCA_AC_BASED_FLOW_CONTROL +cppflags-y += -DHAL_DISABLE_NON_BA_2K_JUMP_ERROR +cppflags-y += -DENABLE_HAL_SOC_STATS +cppflags-y += -DENABLE_HAL_REG_WR_HISTORY +cppflags-y += -DDP_RX_DESC_COOKIE_INVALIDATE +cppflags-y += -DPCI_LINK_STATUS_SANITY +cppflags-y += -DDISABLE_EAPOL_INTRABSS_FWD +cppflags-y += -DSYSTEM_PM_CHECK +endif + +cppflags-$(CONFIG_WLAN_CLD_PM_QOS) += -DCLD_PM_QOS +cppflags-$(CONFIG_REO_DESC_DEFER_FREE) += -DREO_DESC_DEFER_FREE +cppflags-$(CONFIG_WLAN_FEATURE_11AX) += -DWLAN_FEATURE_11AX +cppflags-$(CONFIG_WLAN_FEATURE_11AX) += -DWLAN_FEATURE_11AX_BSS_COLOR +cppflags-$(CONFIG_WLAN_FEATURE_11AX) += -DSUPPORT_11AX_D3 +cppflags-$(CONFIG_RXDMA_ERR_PKT_DROP) += -DRXDMA_ERR_PKT_DROP +cppflags-$(CONFIG_MAX_ALLOC_PAGE_SIZE) += -DMAX_ALLOC_PAGE_SIZE +cppflags-$(CONFIG_DELIVERY_TO_STACK_STATUS_CHECK) += -DDELIVERY_TO_STACK_STATUS_CHECK +cppflags-$(CONFIG_WLAN_TRACE_HIDE_MAC_ADDRESS) += -DWLAN_TRACE_HIDE_MAC_ADDRESS + +cppflags-$(CONFIG_LITHIUM) += -DFEATURE_AST +cppflags-$(CONFIG_LITHIUM) += -DPEER_PROTECTED_ACCESS +cppflags-$(CONFIG_LITHIUM) += -DSERIALIZE_QUEUE_SETUP +cppflags-$(CONFIG_LITHIUM) += -DDP_RX_PKT_NO_PEER_DELIVER +cppflags-$(CONFIG_LITHIUM) += -DDP_RX_DROP_RAW_FRM +cppflags-$(CONFIG_LITHIUM) += -DFEATURE_ALIGN_STATS_FROM_DP +cppflags-$(CONFIG_LITHIUM) += -DDP_RX_SPECIAL_FRAME_NEED +cppflags-$(CONFIG_LITHIUM) += -DFEATURE_STATS_EXT_V2 +cppflags-$(CONFIG_VERBOSE_DEBUG) += -DENABLE_VERBOSE_DEBUG +cppflags-$(CONFIG_RX_DESC_DEBUG_CHECK) += -DRX_DESC_DEBUG_CHECK +cppflags-$(CONFIG_REGISTER_OP_DEBUG) += -DHAL_REGISTER_WRITE_DEBUG +cppflags-$(CONFIG_ENABLE_QDF_PTR_HASH_DEBUG) += -DENABLE_QDF_PTR_HASH_DEBUG +#Enable STATE MACHINE HISTORY +cppflags-$(CONFIG_SM_ENG_HIST) += -DSM_ENG_HIST_ENABLE +cppflags-$(CONFIG_FEATURE_VDEV_RSP_WAKELOCK) += -DFEATURE_VDEV_RSP_WAKELOCK + +# Vendor Commands +cppflags-$(CONFIG_FEATURE_RSSI_MONITOR) += -DFEATURE_RSSI_MONITOR +cppflags-$(CONFIG_FEATURE_BSS_TRANSITION) += -DFEATURE_BSS_TRANSITION +cppflags-$(CONFIG_FEATURE_STATION_INFO) += -DFEATURE_STATION_INFO +cppflags-$(CONFIG_FEATURE_TX_POWER) += -DFEATURE_TX_POWER +cppflags-$(CONFIG_FEATURE_OTA_TEST) += -DFEATURE_OTA_TEST +cppflags-$(CONFIG_FEATURE_ACTIVE_TOS) += -DFEATURE_ACTIVE_TOS +cppflags-$(CONFIG_FEATURE_SAR_LIMITS) += -DFEATURE_SAR_LIMITS +cppflags-$(CONFIG_FEATURE_CONCURRENCY_MATRIX) += -DFEATURE_CONCURRENCY_MATRIX +cppflags-$(CONFIG_FEATURE_SAP_COND_CHAN_SWITCH) += -DFEATURE_SAP_COND_CHAN_SWITCH + +#if converged p2p is enabled +ifeq ($(CONFIG_CONVERGED_P2P_ENABLE), y) +cppflags-$(CONFIG_FEATURE_P2P_LISTEN_OFFLOAD) += -DFEATURE_P2P_LISTEN_OFFLOAD +endif + +#Enable support to get ANI value +ifeq ($(CONFIG_ANI_LEVEL_REQUEST), y) +cppflags-y += -DFEATURE_ANI_LEVEL_REQUEST +endif + +#Flags to enable/disable WMI APIs +cppflags-$(CONFIG_WMI_ROAM_SUPPORT) += -DWMI_ROAM_SUPPORT +cppflags-$(CONFIG_WMI_CONCURRENCY_SUPPORT) += -DWMI_CONCURRENCY_SUPPORT +cppflags-$(CONFIG_WMI_STA_SUPPORT) += -DWMI_STA_SUPPORT + +cppflags-y += -DWMI_MULTI_MAC_SVC + +# Dummy flag for WIN/MCL converged data path compilation +cppflags-y += -DDP_PRINT_ENABLE=0 +cppflags-y += -DATH_SUPPORT_WRAP=0 +cppflags-y += -DQCA_HOST2FW_RXBUF_RING +cppflags-y += -DDP_FLOW_CTL +cppflags-y += -DDP_PEER_EXTENDED_API +cppflags-y += -DDP_POWER_SAVE +cppflags-y += -DDP_CON_MON +cppflags-y += -DDP_MOB_DEFS +cppflags-y += -DDP_PRINT_NO_CONSOLE +cppflags-y += -DDP_INTR_POLL_BOTH +cppflags-y += -DDP_INVALID_PEER_ASSERT + +ifdef CONFIG_HIF_LARGE_CE_RING_HISTORY +ccflags-y += -DHIF_CE_HISTORY_MAX=$(CONFIG_HIF_LARGE_CE_RING_HISTORY) +endif + +cppflags-$(CONFIG_WLAN_HANG_EVENT) += -DHIF_CE_LOG_INFO +cppflags-$(CONFIG_WLAN_HANG_EVENT) += -DHIF_BUS_LOG_INFO +cppflags-$(CONFIG_WLAN_HANG_EVENT) += -DDP_SUPPORT_RECOVERY_NOTIFY +#endof dummy flags + +ccflags-$(CONFIG_ENABLE_SIZE_OPTIMIZE) += -Os + +# DFS component +cppflags-$(CONFIG_WLAN_DFS_STATIC_MEM_ALLOC) += -DWLAN_DFS_STATIC_MEM_ALLOC +cppflags-$(CONFIG_WLAN_DFS_MASTER_ENABLE) += -DQCA_MCL_DFS_SUPPORT +ifeq ($(CONFIG_WLAN_FEATURE_DFS_OFFLOAD), y) +cppflags-$(CONFIG_WLAN_DFS_MASTER_ENABLE) += -DWLAN_DFS_FULL_OFFLOAD +else +cppflags-$(CONFIG_WLAN_DFS_MASTER_ENABLE) += -DWLAN_DFS_PARTIAL_OFFLOAD +endif +cppflags-$(CONFIG_WLAN_DFS_MASTER_ENABLE) += -DDFS_COMPONENT_ENABLE +cppflags-$(CONFIG_WLAN_DFS_MASTER_ENABLE) += -DQCA_DFS_USE_POLICY_MANAGER +cppflags-$(CONFIG_WLAN_DFS_MASTER_ENABLE) += -DQCA_DFS_NOL_PLATFORM_DRV_SUPPORT + +cppflags-$(CONFIG_WLAN_DEBUGFS) += -DWLAN_DEBUGFS +cppflags-$(CONFIG_WLAN_STREAMFS) += -DWLAN_STREAMFS + +cppflags-$(CONFIG_DYNAMIC_DEBUG) += -DFEATURE_MULTICAST_HOST_FW_MSGS + +cppflags-$(CONFIG_ENABLE_SMMU_S1_TRANSLATION) += -DENABLE_SMMU_S1_TRANSLATION + +#Flag to enable/disable MTRACE feature +cppflags-$(CONFIG_ENABLE_MTRACE_LOG) += -DENABLE_MTRACE_LOG + +cppflags-$(CONFIG_FUNC_CALL_MAP) += -DFUNC_CALL_MAP + +#Flag to enable/disable Adaptive 11r feature +cppflags-$(CONFIG_ADAPTIVE_11R) += -DWLAN_ADAPTIVE_11R + +#Flag to enable/disable sae single pmk feature feature +cppflags-$(CONFIG_SAE_SINGLE_PMK) += -DWLAN_SAE_SINGLE_PMK + +#Flag to enable NUD tracking +cppflags-$(CONFIG_WLAN_NUD_TRACKING) += -DWLAN_NUD_TRACKING + +#Flag to enable set and get disable channel list feature +cppflags-$(CONFIG_DISABLE_CHANNEL_LIST) += -DDISABLE_CHANNEL_LIST + +#Flag to enable/disable WIPS feature +cppflags-$(CONFIG_WLAN_BCN_RECV_FEATURE) += -DWLAN_BCN_RECV_FEATURE + +#Flag to enable/disable thermal mitigation +cppflags-$(CONFIG_FW_THERMAL_THROTTLE) += -DFW_THERMAL_THROTTLE + +#Flag to enable/disable LTE COEX support +cppflags-$(CONFIG_LTE_COEX) += -DLTE_COEX + +#Flag to enable/disable HOST_OPCLASS +cppflags-$(CONFIG_HOST_OPCLASS) += -DHOST_OPCLASS +cppflags-$(CONFIG_HOST_OPCLASS) += -DHOST_OPCLASS_EXT + +#Flag to enable/disable TARGET_11D_SCAN +cppflags-$(CONFIG_TARGET_11D_SCAN) += -DTARGET_11D_SCAN + +#Flag to enable/disable avoid acs frequency list feature +cppflags-$(CONFIG_SAP_AVOID_ACS_FREQ_LIST) += -DSAP_AVOID_ACS_FREQ_LIST + +#Flag to enable Dynamic Voltage WDCVS (Config Voltage Mode) +cppflags-$(CONFIG_WLAN_DYNAMIC_CVM) += -DFEATURE_WLAN_DYNAMIC_CVM + +#Flag to enable get firmware state feature +cppflags-$(CONFIG_QCACLD_FEATURE_FW_STATE) += -DFEATURE_FW_STATE + +#Flag to enable set coex configuration feature +cppflags-$(CONFIG_QCACLD_FEATURE_COEX_CONFIG) += -DFEATURE_COEX_CONFIG + +#Flag to enable MPTA helper feature +cppflags-$(CONFIG_QCACLD_FEATURE_MPTA_HELPER) += -DFEATURE_MPTA_HELPER + +#Flag to enable get hw capability +cppflags-$(CONFIG_QCACLD_FEATURE_HW_CAPABILITY) += -DFEATURE_HW_CAPABILITY + +cppflags-$(CONFIG_DATA_CE_SW_INDEX_NO_INLINE_UPDATE) += -DDATA_CE_SW_INDEX_NO_INLINE_UPDATE + +#Flag to enable Multi page memory allocation for RX descriptor pool +cppflags-$(CONFIG_QCACLD_RX_DESC_MULTI_PAGE_ALLOC) += -DRX_DESC_MULTI_PAGE_ALLOC + +#Flag to enable SAR Safety Feature +cppflags-$(CONFIG_SAR_SAFETY_FEATURE) += -DSAR_SAFETY_FEATURE + +cppflags-$(CONFIG_WLAN_FEATURE_DP_EVENT_HISTORY) += -DWLAN_FEATURE_DP_EVENT_HISTORY +cppflags-$(CONFIG_WLAN_FEATURE_DP_RX_RING_HISTORY) += -DWLAN_FEATURE_DP_RX_RING_HISTORY +cppflags-$(CONFIG_WLAN_DP_PER_RING_TYPE_CONFIG) += -DWLAN_DP_PER_RING_TYPE_CONFIG +cppflags-$(CONFIG_WLAN_CE_INTERRUPT_THRESHOLD_CONFIG) += -DWLAN_CE_INTERRUPT_THRESHOLD_CONFIG +cppflags-$(CONFIG_SAP_DHCP_FW_IND) += -DSAP_DHCP_FW_IND +cppflags-$(CONFIG_WLAN_DP_PENDING_MEM_FLUSH) += -DWLAN_DP_PENDING_MEM_FLUSH +cppflags-$(CONFIG_WLAN_SUPPORT_DATA_STALL) += -DWLAN_SUPPORT_DATA_STALL +cppflags-$(CONFIG_WLAN_SUPPORT_TXRX_HL_BUNDLE) += -DWLAN_SUPPORT_TXRX_HL_BUNDLE + +ifdef CONFIG_MAX_LOGS_PER_SEC +ccflags-y += -DWLAN_MAX_LOGS_PER_SEC=$(CONFIG_MAX_LOGS_PER_SEC) +endif + +ifdef CONFIG_SCHED_HISTORY_SIZE +ccflags-y += -DWLAN_SCHED_HISTORY_SIZE=$(CONFIG_SCHED_HISTORY_SIZE) +endif + +# configure log buffer size +ifdef CONFIG_CFG_NUM_DP_TRACE_RECORD +ccflags-y += -DMAX_QDF_DP_TRACE_RECORDS=$(CONFIG_CFG_NUM_DP_TRACE_RECORD) +endif + +ifdef CONFIG_CFG_NUM_HTC_CREDIT_HISTORY +ccflags-y += -DHTC_CREDIT_HISTORY_MAX=$(CONFIG_CFG_NUM_HTC_CREDIT_HISTORY) +endif + +ifdef CONFIG_CFG_NUM_WMI_EVENT_HISTORY +ccflags-y += -DWMI_EVENT_DEBUG_MAX_ENTRY=$(CONFIG_CFG_NUM_WMI_EVENT_HISTORY) +endif + +ifdef CONFIG_CFG_NUM_WMI_MGMT_EVENT_HISTORY +ccflags-y += -DWMI_MGMT_EVENT_DEBUG_MAX_ENTRY=$(CONFIG_CFG_NUM_WMI_MGMT_EVENT_HISTORY) +endif + +ifdef CONFIG_CFG_NUM_TX_RX_HISTOGRAM +ccflags-y += -DNUM_TX_RX_HISTOGRAM=$(CONFIG_CFG_NUM_TX_RX_HISTOGRAM) +endif + +ifdef CONFIG_CFG_NUM_RX_IND_RECORD +ccflags-y += -DOL_RX_INDICATION_MAX_RECORDS=$(CONFIG_CFG_NUM_RX_IND_RECORD) +endif + +ifdef CONFIG_CFG_NUM_ROAM_DEBUG_RECORD +ccflags-y += -DWLAN_ROAM_DEBUG_MAX_REC=$(CONFIG_CFG_NUM_ROAM_DEBUG_RECORD) +endif + +ifdef CONFIG_CFG_PMO_WOW_FILTERS_MAX +ccflags-y += -DPMO_WOW_FILTERS_MAX=$(CONFIG_CFG_PMO_WOW_FILTERS_MAX) +endif + +ifdef CONFIG_CFG_GTK_OFFLOAD_MAX_VDEV +ccflags-y += -DCFG_TGT_DEFAULT_GTK_OFFLOAD_MAX_VDEV=$(CONFIG_CFG_GTK_OFFLOAD_MAX_VDEV) +endif + +ifdef CONFIG_TGT_NUM_MSDU_DESC +ccflags-y += -DCFG_TGT_NUM_MSDU_DESC=$(CONFIG_TGT_NUM_MSDU_DESC) +endif + +ifdef CONFIG_HTC_MAX_MSG_PER_BUNDLE_TX +ccflags-y += -DCFG_HTC_MAX_MSG_PER_BUNDLE_TX=$(CONFIG_HTC_MAX_MSG_PER_BUNDLE_TX) +endif + +ifdef CONFIG_CFG_ROAM_OFFLOAD_MAX_VDEV +ccflags-y += -DCFG_TGT_DEFAULT_ROAM_OFFLOAD_MAX_VDEV=$(CONFIG_CFG_ROAM_OFFLOAD_MAX_VDEV) +endif + +ifdef CONFIG_CFG_MAX_PERIODIC_TX_PTRNS +ccflags-y += -DWMA_MAXNUM_PERIODIC_TX_PTRNS=$(CONFIG_CFG_MAX_PERIODIC_TX_PTRNS) +endif + +ifdef CONFIG_CFG_MAX_STA_VDEVS +ccflags-y += -DCFG_TGT_DEFAULT_MAX_STA_VDEVS=$(CONFIG_CFG_MAX_STA_VDEVS) +endif + +ifdef CONFIG_CFG_NUM_OF_ADDITIONAL_FW_PEERS +ccflags-y += -DNUM_OF_ADDITIONAL_FW_PEERS=$(CONFIG_CFG_NUM_OF_ADDITIONAL_FW_PEERS) +endif + +ifdef CONFIG_CFG_NUM_OF_TDLS_CONN_TABLE_ENTRIES +ccflags-y += -DCFG_TGT_NUM_TDLS_CONN_TABLE_ENTRIES=$(CONFIG_CFG_NUM_OF_TDLS_CONN_TABLE_ENTRIES) +endif + +ifdef CONFIG_CFG_TGT_AST_SKID_LIMIT +ccflags-y += -DCFG_TGT_AST_SKID_LIMIT=$(CONFIG_CFG_TGT_AST_SKID_LIMIT) +endif + +ifdef CONFIG_TX_RESOURCE_HIGH_TH_IN_PER +ccflags-y += -DTX_RESOURCE_HIGH_TH_IN_PER=$(CONFIG_TX_RESOURCE_HIGH_TH_IN_PER) +endif + +ifdef CONFIG_TX_RESOURCE_LOW_TH_IN_PER +ccflags-y += -DTX_RESOURCE_LOW_TH_IN_PER=$(CONFIG_TX_RESOURCE_LOW_TH_IN_PER) +endif + +CONFIG_WLAN_MAX_PSOCS ?= 1 +ccflags-y += -DWLAN_MAX_PSOCS=$(CONFIG_WLAN_MAX_PSOCS) + +CONFIG_WLAN_MAX_PDEVS ?= 1 +ccflags-y += -DWLAN_MAX_PDEVS=$(CONFIG_WLAN_MAX_PDEVS) + +CONFIG_WLAN_MAX_VDEVS ?= 6 +ccflags-y += -DWLAN_MAX_VDEVS=$(CONFIG_WLAN_MAX_VDEVS) + +#Maximum pending commands for a vdev is calculated in vdev create handler +#by WLAN_SER_MAX_PENDING_CMDS/WLAN_SER_MAX_VDEVS. For SAP case, we will need +#to accommodate 32 Pending commands to handle multiple STA sending +#deauth/disassoc at the same time and for STA vdev,4 non scan pending commands +#are supported. So calculate WLAN_SER_MAX_PENDING_COMMANDS based on the SAP +#modes supported and no of STA vdev total non scan pending queue. Reserve +#additional 3 pending commands for WLAN_SER_MAX_PENDING_CMDS_AP to account for +#other commands like hardware mode change. + +ifdef CONFIG_SIR_SAP_MAX_NUM_PEERS +CONFIG_WLAN_SER_MAX_PENDING_CMDS_AP ?=$(CONFIG_SIR_SAP_MAX_NUM_PEERS) +else +CONFIG_WLAN_SER_MAX_PENDING_CMDS_AP ?=32 +endif +ccflags-y += -DWLAN_SER_MAX_PENDING_CMDS_AP=$(CONFIG_WLAN_SER_MAX_PENDING_CMDS_AP)+3 + +CONFIG_WLAN_SER_MAX_PENDING_CMDS_STA ?= 4 +ccflags-y += -DWLAN_SER_MAX_PENDING_CMDS_STA=$(CONFIG_WLAN_SER_MAX_PENDING_CMDS_STA) + +CONFIG_WLAN_MAX_PENDING_CMDS ?= $(CONFIG_WLAN_SER_MAX_PENDING_CMDS_AP)*3+$(CONFIG_WLAN_SER_MAX_PENDING_CMDS_STA)*2 + +ccflags-y += -DWLAN_SER_MAX_PENDING_CMDS=$(CONFIG_WLAN_MAX_PENDING_CMDS) + +CONFIG_WLAN_PDEV_MAX_VDEVS ?= $(CONFIG_WLAN_MAX_VDEVS) +ccflags-y += -DWLAN_PDEV_MAX_VDEVS=$(CONFIG_WLAN_PDEV_MAX_VDEVS) + +CONFIG_WLAN_PSOC_MAX_VDEVS ?= $(CONFIG_WLAN_MAX_VDEVS) +ccflags-y += -DWLAN_PSOC_MAX_VDEVS=$(CONFIG_WLAN_PSOC_MAX_VDEVS) + +CONFIG_MAX_SCAN_CACHE_SIZE ?= 300 +ccflags-y += -DMAX_SCAN_CACHE_SIZE=$(CONFIG_MAX_SCAN_CACHE_SIZE) +CONFIG_SCAN_MAX_REST_TIME ?= 0 +ccflags-y += -DSCAN_MAX_REST_TIME=$(CONFIG_SCAN_MAX_REST_TIME) +CONFIG_SCAN_MIN_REST_TIME ?= 0 +ccflags-y += -DSCAN_MIN_REST_TIME=$(CONFIG_SCAN_MIN_REST_TIME) +CONFIG_SCAN_BURST_DURATION ?= 0 +ccflags-y += -DSCAN_BURST_DURATION=$(CONFIG_SCAN_BURST_DURATION) +CONFIG_SCAN_PROBE_SPACING_TIME ?= 0 +ccflags-y += -DSCAN_PROBE_SPACING_TIME=$(CONFIG_SCAN_PROBE_SPACING_TIME) +CONFIG_SCAN_PROBE_DELAY ?= 0 +ccflags-y += -DSCAN_PROBE_DELAY=$(CONFIG_SCAN_PROBE_DELAY) +CONFIG_SCAN_MAX_SCAN_TIME ?= 30000 +ccflags-y += -DSCAN_MAX_SCAN_TIME=$(CONFIG_SCAN_MAX_SCAN_TIME) +CONFIG_SCAN_NETWORK_IDLE_TIMEOUT ?= 0 +ccflags-y += -DSCAN_NETWORK_IDLE_TIMEOUT=$(CONFIG_SCAN_NETWORK_IDLE_TIMEOUT) +CONFIG_HIDDEN_SSID_TIME ?= 0xFFFFFFFF +ccflags-y += -DHIDDEN_SSID_TIME=$(CONFIG_HIDDEN_SSID_TIME) +CONFIG_SCAN_CHAN_STATS_EVENT_ENAB ?= false +ccflags-y += -DSCAN_CHAN_STATS_EVENT_ENAB=$(CONFIG_SCAN_CHAN_STATS_EVENT_ENAB) +CONFIG_MAX_BCN_PROBE_IN_SCAN_QUEUE ?= 150 +ccflags-y += -DMAX_BCN_PROBE_IN_SCAN_QUEUE=$(CONFIG_MAX_BCN_PROBE_IN_SCAN_QUEUE) + + +CONFIG_MGMT_DESC_POOL_MAX ?= 64 +ccflags-y += -DMGMT_DESC_POOL_MAX=$(CONFIG_MGMT_DESC_POOL_MAX) + +ifdef CONFIG_SIR_SAP_MAX_NUM_PEERS +ccflags-y += -DSIR_SAP_MAX_NUM_PEERS=$(CONFIG_SIR_SAP_MAX_NUM_PEERS) +endif + +ifdef CONFIG_BEACON_TX_OFFLOAD_MAX_VDEV +ccflags-y += -DCFG_TGT_DEFAULT_BEACON_TX_OFFLOAD_MAX_VDEV=$(CONFIG_BEACON_TX_OFFLOAD_MAX_VDEV) +endif + +ifdef CONFIG_LOCK_STATS_ON +ccflags-y += -DQDF_LOCK_STATS=1 +ccflags-y += -DQDF_LOCK_STATS_DESTROY_PRINT=0 +ifneq ($(CONFIG_ARCH_SDXPRAIRIE), y) +ccflags-y += -DQDF_LOCK_STATS_BUG_ON=1 +endif +ccflags-y += -DQDF_LOCK_STATS_LIST=1 +ccflags-y += -DQDF_LOCK_STATS_LIST_SIZE=256 +endif + +ifdef CONFIG_FW_THERMAL_THROTTLE +ccflags-y += -DFW_THERMAL_THROTTLE_SUPPORT +endif + +cppflags-y += -DFEATURE_NBUFF_REPLENISH_TIMER +cppflags-y += -DPEER_CACHE_RX_PKTS +cppflags-y += -DPCIE_REG_WINDOW_LOCAL_NO_CACHE + +cppflags-y += -DSERIALIZE_VDEV_RESP +cppflags-y += -DTGT_IF_VDEV_MGR_CONV + +cppflags-y += -DCONFIG_CHAN_NUM_API +cppflags-y += -DCONFIG_CHAN_FREQ_API + +cppflags-$(CONFIG_BAND_6GHZ) += -DCONFIG_BAND_6GHZ +cppflags-$(CONFIG_6G_SCAN_CHAN_SORT_ALGO) += -DFEATURE_6G_SCAN_CHAN_SORT_ALGO + +cppflags-$(CONFIG_RX_FISA) += -DWLAN_SUPPORT_RX_FISA + +cppflags-$(CONFIG_RX_DEFRAG_DO_NOT_REINJECT) += -DRX_DEFRAG_DO_NOT_REINJECT + +cppflags-$(CONFIG_HANDLE_BC_EAP_TX_FRM) += -DHANDLE_BROADCAST_EAPOL_TX_FRAME + +cppflags-$(CONFIG_MORE_TX_DESC) += -DTX_TO_NPEERS_INC_TX_DESCS + +cppflags-$(CONFIG_FIX_TXDMA_ALIGNMENT) += -DFIX_TXDMA_LIMITATION + +ccflags-$(CONFIG_HASTINGS_BT_WAR) += -DHASTINGS_BT_WAR + +cppflags-$(CONFIG_SLUB_DEBUG_ON) += -DHIF_CONFIG_SLUB_DEBUG_ON +cppflags-$(CONFIG_SLUB_DEBUG_ON) += -DHAL_CONFIG_SLUB_DEBUG_ON + +ccflags-$(CONFIG_FOURTH_CONNECTION) += -DFEATURE_FOURTH_CONNECTION +ccflags-$(CONFIG_FOURTH_CONNECTION_AUTO) += -DFOURTH_CONNECTION_AUTO +ccflags-$(CONFIG_WMI_SEND_RECV_QMI) += -DWLAN_FEATURE_WMI_SEND_RECV_QMI + +cppflags-$(CONFIG_WDI3_STATS_UPDATE) += -DWDI3_STATS_UPDATE + +cppflags-$(CONFIG_WLAN_CUSTOM_DSCP_UP_MAP) += -DWLAN_CUSTOM_DSCP_UP_MAP +cppflags-$(CONFIG_WLAN_SEND_DSCP_UP_MAP_TO_FW) += -DWLAN_SEND_DSCP_UP_MAP_TO_FW + +cppflags-$(CONFIG_HIF_CPU_PERF_AFFINE_MASK) += -DHIF_CPU_PERF_AFFINE_MASK +cppflags-$(CONFIG_SMMU_S1_UNMAP) += -DCONFIG_SMMU_S1_UNMAP +cppflags-$(CONFIG_GENERIC_SHADOW_REGISTER_ACCESS_ENABLE) += -DGENERIC_SHADOW_REGISTER_ACCESS_ENABLE +cppflags-$(CONFIG_IPA_SET_RESET_TX_DB_PA) += -DIPA_SET_RESET_TX_DB_PA +cppflags-$(CONFIG_DUMP_REO_QUEUE_INFO_IN_DDR) += -DDUMP_REO_QUEUE_INFO_IN_DDR + +KBUILD_CPPFLAGS += $(cppflags-y) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# WLAN driver. Note that we must use ccflags-y here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized), y) +ccflags-y += -Wmaybe-uninitialized +ifneq (y,$(CONFIG_ARCH_MSM)) +ccflags-y += -Wframe-larger-than=4096 +endif +endif +ccflags-y += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard), y) +ccflags-y += -Wheader-guard +endif +# If the module name is not "wlan", then the define MULTI_IF_NAME to be the +# same a the QCA CHIP name. The host driver will then append MULTI_IF_NAME to +# any string that must be unique for all instances of the driver on the system. +# This allows multiple instances of the driver with different module names. +# If the module name is wlan, leave MULTI_IF_NAME undefined and the code will +# treat the driver as the primary driver. +# +# If DYNAMIC_SINGLE_CHIP is defined, which means there are multiple possible +# drivers, but only 1 driver will be loaded at a time(WLAN dynamic detect), +# leave MULTI_IF_NAME undefined, no matter what the module name is, then +# host driver will only append DYNAMIC_SINGLE_CHIP to the path of +# firmware/mac/ini file. +ifneq ($(DYNAMIC_SINGLE_CHIP),) +ccflags-y += -DDYNAMIC_SINGLE_CHIP=\"$(DYNAMIC_SINGLE_CHIP)\" +else + +ifneq ($(MODNAME), wlan) +CHIP_NAME ?= $(MODNAME) +ccflags-y += -DMULTI_IF_NAME=\"$(CHIP_NAME)\" +endif + +endif #DYNAMIC_SINGLE_CHIP + +# WLAN_HDD_ADAPTER_MAGIC must be unique for all instances of the driver on the +# system. If it is not defined, then the host driver will use the first 4 +# characters (including NULL) of MULTI_IF_NAME to construct +# WLAN_HDD_ADAPTER_MAGIC. +ifdef WLAN_HDD_ADAPTER_MAGIC +ccflags-y += -DWLAN_HDD_ADAPTER_MAGIC=$(WLAN_HDD_ADAPTER_MAGIC) +endif + +# Max no of Serialization msg queue depth for MCL. If it is not +# defined, then SCHEDULER_CORE_MAX_MESSAGES will be 4000 for +# WIN. + +ccflags-y += -DSCHEDULER_CORE_MAX_MESSAGES=1000 + +# Defining Reduction Limit 0 for MCL. If it is not defined, +#then WLAN_SCHED_REDUCTION_LIMIT will be 32 for +# WIN. +ccflags-y += -DWLAN_SCHED_REDUCTION_LIMIT=0 + +# Determine if we are building against an arm architecture host +ifeq ($(findstring arm, $(ARCH)),) + ccflags-y += -DWLAN_HOST_ARCH_ARM=0 +else + ccflags-y += -DWLAN_HOST_ARCH_ARM=1 +endif + +# inject some build related information +ifeq ($(CONFIG_BUILD_TAG), y) +CLD_CHECKOUT = $(shell cd "$(PWD)/$(WLAN_ROOT)" && \ + git reflog | grep -vm1 "}: cherry-pick: " | grep -oE ^[0-f]+) +CLD_IDS = $(shell cd "$(PWD)/$(WLAN_ROOT)" && \ + git log -50 $(CLD_CHECKOUT)~..HEAD | \ + sed -nE 's/^\s*Change-Id: (I[0-f]{10})[0-f]{30}\s*$$/\1/p' | \ + paste -sd "," -) + +CMN_CHECKOUT = $(shell cd "$(PWD)/$(WLAN_COMMON_INC)" && \ + git reflog | grep -vm1 "}: cherry-pick: " | grep -oE ^[0-f]+) +CMN_IDS = $(shell cd "$(PWD)/$(WLAN_COMMON_INC)" && \ + git log -50 $(CMN_CHECKOUT)~..HEAD | \ + sed -nE 's/^\s*Change-Id: (I[0-f]{10})[0-f]{30}\s*$$/\1/p' | \ + paste -sd "," -) + +TIMESTAMP = $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') +BUILD_TAG = "$(TIMESTAMP); cld:$(CLD_IDS); cmn:$(CMN_IDS);" +# It's assumed that BUILD_TAG is used only in wlan_hdd_main.c +CFLAGS_wlan_hdd_main.o += -DBUILD_TAG=\"$(BUILD_TAG)\" +endif + +# Module information used by KBuild framework +obj-$(CONFIG_QCA_CLD_WLAN) += $(MODNAME).o +$(MODNAME)-y := $(OBJS) +OBJS_DIRS := $(dir $(OBJS)) \ + $(WLAN_COMMON_ROOT)/$(HIF_CE_DIR)/ \ + $(QDF_OBJ_DIR)/ \ + $(WLAN_COMMON_ROOT)/$(HIF_PCIE_DIR)/ \ + $(WLAN_COMMON_ROOT)/$(HIF_SNOC_DIR)/ \ + $(WLAN_COMMON_ROOT)/$(HIF_SDIO_DIR)/ +CLEAN_DIRS := $(addsuffix *.o,$(sort $(OBJS_DIRS))) \ + $(addsuffix .*.o.cmd,$(sort $(OBJS_DIRS))) +clean-files := $(CLEAN_DIRS) diff --git a/drivers/staging/qcacld-3.0/Kconfig b/drivers/staging/qcacld-3.0/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..02ad2388209e742df5a3800b15a3b2ecfd016894 --- /dev/null +++ b/drivers/staging/qcacld-3.0/Kconfig @@ -0,0 +1,154 @@ +comment "Qualcomm Atheros CLD WLAN module" + +config QCA_CLD_WLAN + + tristate "Qualcomm Atheros CLD WLAN module" + default n + help + Add support for the Qualcomm Atheros CLD WLAN module + +if QCA_CLD_WLAN != n + +config QCACLD_WLAN_LFR3 + bool "Enable the WLAN Legacy Fast Roaming feature Version 3" + default n + +config PRIMA_WLAN_OKC + bool "Enable the Prima WLAN Opportunistic Key Caching feature" + default n + +config WLAN_FEATURE_11W + bool "Enable the WLAN 802.11w Protected Management Frames feature" + default n + +config WLAN_FEATURE_LPSS + bool "Enable the WLAN LPSS feature" + default n + +config QCOM_VOWIFI_11R + bool "Enable Fast Transition (11r) feature" + default n + +config QCACLD_FEATURE_NAN + bool "Enable NAN feature" + default n + +config QCACLD_FEATURE_GREEN_AP + bool "Enable Green AP feature" + default n + +config HELIUMPLUS + bool "Enable Beeliner based descriptor structures for Helium" + default n + +config 64BIT_PADDR + bool "Enable 37-bit physical/bus addresses" + depends on HELIUMPLUS + default n + +config QCOM_TDLS + bool "Enable TDLS feature" + default n + +config QCOM_LTE_COEX + bool "Enable QCOM LTE Coex feature" + default n + +config MPC_UT_FRAMEWORK + bool "Enable Unit test framework for multiport concurrency" + default n + +config WLAN_OFFLOAD_PACKETS + bool "Enable offload packets feature" + default n + +config FEATURE_TSO + bool "Enable TCP Segmentation Offload" + depends on HELIUMPLUS + default n + +config FEATURE_TSO_DEBUG + bool "Enable TCP Segmentation Offload with debug" + depends on FEATURE_TSO + default n + +config WLAN_FASTPATH + bool "Enable fastpath for datapackets" + default n + +config WLAN_NAPI + bool "Enable NAPI - datapath rx" + default n + +config WLAN_NAPI_DEBUG + bool "Enable debug logging on NAPI" + depends on WLAN_NAPI + default n + +config WLAN_TX_FLOW_CONTROL_V2 + bool "Enable tx flow control version:2" + default n + +config WLAN_LRO + bool "Enable Large Receive Offload" + depends on HELIUMPLUS + depends on INET_LRO + default n + +config WLAN_SYNC_TSF + bool "Enable QCOM sync multi devices tsf feature" + default n + +config LFR_SUBNET_DETECTION + bool "Enable LFR Subnet Change Detection" + default n + +config MCC_TO_SCC_SWITCH + bool "Enable MCC to SCC Switch Logic" + default n + +config QCACLD_WLAN_LFR2 + bool "Enable the WLAN Legacy Fast Roaming feature Version 2" + default n + +config WLAN_FEATURE_DISA + bool "Enable DISA certification feature" + default n + +config WLAN_FEATURE_FIPS + bool "Enable FIPS certification feature" + default n + +config WLAN_FEATURE_11AX + bool "Enable 11AX(High Efficiency) feature" + default n + +config ICMP_DISABLE_PS + bool "Enable ICMP packet disable powersave feature" + default n + +config BUILD_TIMESTAMP + bool "Embed timestamp in wlan version" + default n + +config WLAN_FEATURE_FILS + bool "Enable FILS feature" + default n + +config NAN_CONVERGENCE + bool "Enable NAN_CONVERGENCE feature" + default n + +config WLAN_OBJMGR_DEBUG + bool "Enable WLAN Obj Mgr Debug services" + default n + +config WLAN_FEATURE_DFS_OFFLOAD + bool "Enable dfs offload feature" + default n + +config WLAN_FEATURE_SARV1_TO_SARV2 + bool "Enable conversion of SAR v1 to v2 feature" + default n + +endif # QCA_CLD_WLAN diff --git a/drivers/staging/qcacld-3.0/Makefile b/drivers/staging/qcacld-3.0/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..fb78b03460b63dda2857e34c76e0ae2470f7f797 --- /dev/null +++ b/drivers/staging/qcacld-3.0/Makefile @@ -0,0 +1,32 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +# The Make variable $(M) must point to the directory that contains the module +# source code (which includes this Makefile). It can either be an absolute or a +# relative path. If it is a relative path, then it must be relative to the +# kernel source directory (KERNEL_SRC). An absolute path can be obtained very +# easily through $(shell pwd). Generating a path relative to KERNEL_SRC is +# difficult and we accept some outside help by letting the caller override the +# variable $(M). Allowing a relative path for $(M) enables us to have the build +# system put output/object files (.o, .ko.) into a directory different from the +# module source directory. +M ?= $(shell pwd) + +# WLAN_ROOT must contain an absolute path (i.e. not a relative path) +KBUILD_OPTIONS := WLAN_ROOT=$(shell cd $(KERNEL_SRC); readlink -e $(M)) +KBUILD_OPTIONS += MODNAME?=wlan + +#By default build for CLD +WLAN_SELECT := CONFIG_QCA_CLD_WLAN=m +KBUILD_OPTIONS += CONFIG_QCA_WIFI_ISOC=0 +KBUILD_OPTIONS += CONFIG_QCA_WIFI_2_0=1 +KBUILD_OPTIONS += $(WLAN_SELECT) +KBUILD_OPTIONS += $(KBUILD_EXTRA) # Extra config if any + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 M=$(M) -C $(KERNEL_SRC) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(M) clean $(KBUILD_OPTIONS) diff --git a/drivers/staging/qcacld-3.0/README.txt b/drivers/staging/qcacld-3.0/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..bdf3e7a7adc4eaf2883ac235c8ad755f3552a88f --- /dev/null +++ b/drivers/staging/qcacld-3.0/README.txt @@ -0,0 +1 @@ +This is CNSS WLAN Host Driver for products starting from iHelium diff --git a/drivers/staging/qcacld-3.0/components/action_oui/core/inc/wlan_action_oui_main.h b/drivers/staging/qcacld-3.0/components/action_oui/core/inc/wlan_action_oui_main.h new file mode 100644 index 0000000000000000000000000000000000000000..7777476a62b5b493d7c040e23177adb4fb93f222 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/core/inc/wlan_action_oui_main.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare private API which shall be used internally only + * in action_oui component. This file shall include prototypes of + * various notification handlers and logging functions. + * + * Note: This API should be never accessed out of action_oui component. + */ + +#ifndef _WLAN_ACTION_OUI_MAIN_H_ +#define _WLAN_ACTION_OUI_MAIN_H_ + +#include +#include "wlan_action_oui_public_struct.h" +#include "wlan_action_oui_priv.h" +#include "wlan_action_oui_objmgr.h" + +#define action_oui_log(level, args...) \ + QDF_TRACE(QDF_MODULE_ID_ACTION_OUI, level, ## args) + +#define action_oui_logfl(level, format, args...) \ + action_oui_log(level, FL(format), ## args) + +#define action_oui_fatal(format, args...) \ + action_oui_logfl(QDF_TRACE_LEVEL_FATAL, format, ## args) +#define action_oui_err(format, args...) \ + action_oui_logfl(QDF_TRACE_LEVEL_ERROR, format, ## args) +#define action_oui_warn(format, args...) \ + action_oui_logfl(QDF_TRACE_LEVEL_WARN, format, ## args) +#define action_oui_info(format, args...) \ + action_oui_logfl(QDF_TRACE_LEVEL_INFO, format, ## args) +#define action_oui_debug(format, args...) \ + action_oui_logfl(QDF_TRACE_LEVEL_DEBUG, format, ## args) + +#define ACTION_OUI_ENTER() action_oui_debug("enter") +#define ACTION_OUI_EXIT() action_oui_debug("exit") + +/** + * action_oui_psoc_create_notification(): Handler for psoc create notify. + * @psoc: psoc which is going to be created by objmgr + * @arg: argument for notification handler. + * + * Allocate and attach psoc private object. + * + * Return: QDF_STATUS status in case of success else return error. + */ +QDF_STATUS +action_oui_psoc_create_notification(struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * action_oui_psoc_destroy_notification(): Handler for psoc destroy notify. + * @psoc: psoc which is going to be destroyed by objmgr + * @arg: argument for notification handler. + * + * Deallocate and detach psoc private object. + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS +action_oui_psoc_destroy_notification(struct wlan_objmgr_psoc *psoc, void *arg); + +#endif /* end of _WLAN_ACTION_OUI_MAIN_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/action_oui/core/inc/wlan_action_oui_objmgr.h b/drivers/staging/qcacld-3.0/components/action_oui/core/inc/wlan_action_oui_objmgr.h new file mode 100644 index 0000000000000000000000000000000000000000..17d0b09c8979c25e6465a115f116cfedd10330a6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/core/inc/wlan_action_oui_objmgr.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains various object manager related wrappers and helpers + */ + +#ifndef _WLAN_ACTION_OUI_OBJMGR_H +#define _WLAN_ACTION_OUI_OBJMGR_H + +#include "wlan_cmn.h" +#include "wlan_objmgr_cmn.h" +#include "wlan_objmgr_psoc_obj.h" + +/** + * action_oui_psoc_get_ref() - Wrapper to increment action_oui ref count + * @psoc: psoc object + * + * Wrapper for action_oui to increment ref count after checking valid + * object state. + * + * Return: SUCCESS/FAILURE + */ +static inline +QDF_STATUS action_oui_psoc_get_ref(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_try_get_ref(psoc, WLAN_ACTION_OUI_ID); +} + +/** + * action_oui_psoc_put_ref() - Wrapper to decrement action_oui ref count + * @psoc: psoc object + * + * Wrapper for action_oui to decrement ref count of psoc. + * + * Return: SUCCESS/FAILURE + */ +static inline +void action_oui_psoc_put_ref(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_release_ref(psoc, WLAN_ACTION_OUI_ID); +} + +/** + * action_oui_psoc_get_priv(): Wrapper to retrieve psoc priv obj + * @psoc: psoc pointer + * + * Wrapper for action_oui to get psoc private object pointer. + * + * Return: Private object of psoc + */ +static inline struct action_oui_psoc_priv * +action_oui_psoc_get_priv(struct wlan_objmgr_psoc *psoc) +{ + struct action_oui_psoc_priv *psoc_priv; + + psoc_priv = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_ACTION_OUI); + QDF_BUG(psoc_priv); + + return psoc_priv; +} + +#endif /* _WLAN_ACTION_OUI_OBJMGR_H */ diff --git a/drivers/staging/qcacld-3.0/components/action_oui/core/inc/wlan_action_oui_priv.h b/drivers/staging/qcacld-3.0/components/action_oui/core/inc/wlan_action_oui_priv.h new file mode 100644 index 0000000000000000000000000000000000000000..2b325b4ae865ff22a8ef0810b2787c6ea51912fe --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/core/inc/wlan_action_oui_priv.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare private API which shall be used internally only + * in action_oui component. This file shall include prototypes of + * action_oui parsing and send logic. + * + * Note: This API should be never accessed out of action_oui component. + */ + +#ifndef _WLAN_ACTION_OUI_PRIV_STRUCT_H_ +#define _WLAN_ACTION_OUI_PRIV_STRUCT_H_ + +#include +#include +#include "wlan_action_oui_public_struct.h" +#include "wlan_action_oui_tgt_api.h" +#include "wlan_action_oui_objmgr.h" + +/** + * enum action_oui_token_type - String token types expected. + * @ACTION_OUI_TOKEN: oui string + * @ACTION_OUI_DATA_LENGTH_TOKEN: data length string + * @ACTION_OUI_DATA_TOKEN: OUI data string + * @ACTION_OUI_DATA_MASK_TOKEN: data mask string + * @ACTION_OUI_INFO_MASK_TOKEN: info mask string + * @ACTION_OUI_MAC_ADDR_TOKEN: mac addr string + * @ACTION_OUI_MAC_MASK_TOKEN: mac mask string + * @ACTION_OUI_CAPABILITY_TOKEN: capability string + * @ACTION_OUI_END_TOKEN: end of one oui extension + */ +enum action_oui_token_type { + ACTION_OUI_TOKEN = 1 << 0, + ACTION_OUI_DATA_LENGTH_TOKEN = 1 << 1, + ACTION_OUI_DATA_TOKEN = 1 << 2, + ACTION_OUI_DATA_MASK_TOKEN = 1 << 3, + ACTION_OUI_INFO_MASK_TOKEN = 1 << 4, + ACTION_OUI_MAC_ADDR_TOKEN = 1 << 5, + ACTION_OUI_MAC_MASK_TOKEN = 1 << 6, + ACTION_OUI_CAPABILITY_TOKEN = 1 << 7, + ACTION_OUI_END_TOKEN = 1 << 8, +}; + +/** + * struct action_oui_extension_priv - Private contents of extension. + * @item: list element + * @extension: Extension contents + * + * This structure encapsulates action_oui_extension and list item. + */ +struct action_oui_extension_priv { + qdf_list_node_t item; + struct action_oui_extension extension; +}; + +/** + * struct action_oui_priv - Each action info. + * @id: type of action + * @extension_list: list of extensions + * @extension_lock: lock to control access to @extension_list + * + * All extensions of action specified by action_id are stored + * at @extension_list as linked list. + */ +struct action_oui_priv { + enum action_oui_id id; + qdf_list_t extension_list; + qdf_mutex_t extension_lock; +}; + +/** + * struct action_oui_psoc_priv - Private object to be stored in psoc + * @psoc: pointer to psoc object + * @total_extensions: total count of extensions from all actions + * @oui_priv: array of pointers used to refer each action info + * @tx_ops: call-back functions to send OUIs to firmware + */ +struct action_oui_psoc_priv { + struct wlan_objmgr_psoc *psoc; + uint32_t total_extensions; + struct action_oui_priv *oui_priv[ACTION_OUI_MAXIMUM_ID]; + struct action_oui_tx_ops tx_ops; +}; + +/** + * action_oui_parse() - Parse action oui string + * @psoc_priv: pointer to action_oui psoc priv obj + * @oui_string: string to be parsed + * @ation_id: type of the action to be parsed + * + * This function parses the action oui string, extracts extensions and + * stores them @action_oui_priv using list data structure. + * + * Return: QDF_STATUS + * + */ +QDF_STATUS +action_oui_parse(struct action_oui_psoc_priv *psoc_priv, + uint8_t *oui_string, enum action_oui_id action_id); + +/** + * action_oui_send() - Send action oui extensions to target_if. + * @psoc_priv: pointer to action_oui psoc priv obj + * @ation_id: type of the action to be send + * + * This function sends action oui extensions to target_if. + * + * Return: QDF_STATUS + * + */ +QDF_STATUS +action_oui_send(struct action_oui_psoc_priv *psoc_priv, + enum action_oui_id action_id); + +/** + * action_oui_search() - Check if Vendor OUIs are present in IE buffer + * @psoc_priv: pointer to action_oui psoc priv obj + * @attr: pointer to structure containing type of action, beacon IE data etc., + * @action_id: type of action to be checked + * + * This function parses the IE buffer and finds if any of the vendor OUI + * and related attributes are present in it. + * + * Return: If vendor OUI is present return true else false + */ +bool +action_oui_search(struct action_oui_psoc_priv *psoc_priv, + struct action_oui_search_attr *attr, + enum action_oui_id action_id); + +#endif /* End of _WLAN_ACTION_OUI_PRIV_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/action_oui/core/src/wlan_action_oui_main.c b/drivers/staging/qcacld-3.0/components/action_oui/core/src/wlan_action_oui_main.c new file mode 100644 index 0000000000000000000000000000000000000000..825b989a16282e3668c505704a90abfd52666819 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/core/src/wlan_action_oui_main.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implement various notification handlers which are accessed + * internally in action_oui component only. + */ + +#include "wlan_action_oui_main.h" +#include "wlan_action_oui_public_struct.h" +#include "wlan_action_oui_tgt_api.h" +#include "target_if_action_oui.h" + +/** + * action_oui_allocate() - Allocates memory for various actions. + * @psoc_priv: pointer to action_oui psoc priv obj + * + * This function allocates memory for all the action_oui types + * and initializes the respective lists to store extensions + * extracted from action_oui_extract(). + * + * Return: QDF_STATUS + */ +static QDF_STATUS +action_oui_allocate(struct action_oui_psoc_priv *psoc_priv) +{ + struct action_oui_priv *oui_priv; + uint32_t i; + uint32_t j; + + for (i = 0; i < ACTION_OUI_MAXIMUM_ID; i++) { + oui_priv = qdf_mem_malloc(sizeof(*oui_priv)); + if (!oui_priv) { + action_oui_err("Mem alloc failed for oui_priv id: %u", + i); + goto free_mem; + } + oui_priv->id = i; + qdf_list_create(&oui_priv->extension_list, + ACTION_OUI_MAX_EXTENSIONS); + qdf_mutex_create(&oui_priv->extension_lock); + psoc_priv->oui_priv[i] = oui_priv; + } + + return QDF_STATUS_SUCCESS; + +free_mem: + for (j = 0; j < i; j++) { + oui_priv = psoc_priv->oui_priv[j]; + if (!oui_priv) + continue; + + qdf_list_destroy(&oui_priv->extension_list); + qdf_mutex_destroy(&oui_priv->extension_lock); + psoc_priv->oui_priv[j] = NULL; + } + + return QDF_STATUS_E_NOMEM; +} + +/** + * action_oui_destroy() - Deallocates memory for various actions. + * @psoc_priv: pointer to action_oui psoc priv obj + * + * This function Deallocates memory for all the action_oui types. + * As a part of deallocate, all extensions are destroyed. + * + * Return: None + */ +static void +action_oui_destroy(struct action_oui_psoc_priv *psoc_priv) +{ + struct action_oui_priv *oui_priv; + struct action_oui_extension_priv *ext_priv; + qdf_list_t *ext_list; + QDF_STATUS status; + qdf_list_node_t *node = NULL; + uint32_t i; + + psoc_priv->total_extensions = 0; + for (i = 0; i < ACTION_OUI_MAXIMUM_ID; i++) { + oui_priv = psoc_priv->oui_priv[i]; + psoc_priv->oui_priv[i] = NULL; + if (!oui_priv) + continue; + + ext_list = &oui_priv->extension_list; + qdf_mutex_acquire(&oui_priv->extension_lock); + while (!qdf_list_empty(ext_list)) { + status = qdf_list_remove_front(ext_list, &node); + if (!QDF_IS_STATUS_SUCCESS(status)) { + action_oui_err("Invalid delete in action: %u", + oui_priv->id); + break; + } + ext_priv = qdf_container_of(node, + struct action_oui_extension_priv, + item); + qdf_mem_free(ext_priv); + ext_priv = NULL; + } + + qdf_list_destroy(ext_list); + qdf_mutex_release(&oui_priv->extension_lock); + qdf_mutex_destroy(&oui_priv->extension_lock); + qdf_mem_free(oui_priv); + oui_priv = NULL; + } +} + +QDF_STATUS +action_oui_psoc_create_notification(struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct action_oui_psoc_priv *psoc_priv; + QDF_STATUS status; + + ACTION_OUI_ENTER(); + + psoc_priv = qdf_mem_malloc(sizeof(*psoc_priv)); + if (!psoc_priv) { + action_oui_err("Failed to allocate psoc_priv"); + status = QDF_STATUS_E_NOMEM; + goto exit; + } + + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_ACTION_OUI, + (void *)psoc_priv, QDF_STATUS_SUCCESS); + if (!QDF_IS_STATUS_SUCCESS(status)) { + action_oui_err("Failed to attach priv with psoc"); + goto free_psoc_priv; + } + + target_if_action_oui_register_tx_ops(&psoc_priv->tx_ops); + psoc_priv->psoc = psoc; + + status = action_oui_allocate(psoc_priv); + if (!QDF_IS_STATUS_SUCCESS(status)) { + action_oui_err("Failed to alloc action_oui"); + goto detach_psoc_priv; + } + + action_oui_debug("psoc priv attached"); + goto exit; + +detach_psoc_priv: + wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_ACTION_OUI, + (void *)psoc_priv); +free_psoc_priv: + qdf_mem_free(psoc_priv); + status = QDF_STATUS_E_INVAL; +exit: + ACTION_OUI_EXIT(); + return status; +} + +QDF_STATUS +action_oui_psoc_destroy_notification(struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct action_oui_psoc_priv *psoc_priv = NULL; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + ACTION_OUI_ENTER(); + + psoc_priv = action_oui_psoc_get_priv(psoc); + if (!psoc_priv) { + action_oui_err("psoc priv is NULL"); + goto exit; + } + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_ACTION_OUI, + (void *)psoc_priv); + if (!QDF_IS_STATUS_SUCCESS(status)) + action_oui_err("Failed to detach priv with psoc"); + + action_oui_destroy(psoc_priv); + qdf_mem_free(psoc_priv); + +exit: + ACTION_OUI_EXIT(); + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/action_oui/core/src/wlan_action_oui_parse.c b/drivers/staging/qcacld-3.0/components/action_oui/core/src/wlan_action_oui_parse.c new file mode 100644 index 0000000000000000000000000000000000000000..e59cac907e0d9a815815afc8fda1ad74b6500db8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/core/src/wlan_action_oui_parse.c @@ -0,0 +1,951 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implement parsing logic for action_oui strings, extract + * extensions and store them using linked list. Functions defined in + * this file can be accessed internally in action_oui component only. + */ + +#include "wlan_action_oui_main.h" +#include "wlan_action_oui_public_struct.h" +#include "wlan_action_oui_tgt_api.h" +#include "target_if_action_oui.h" +#include +#include + +/** + * action_oui_string_to_hex() - convert string to uint8_t hex array + * @token - string to be converted + * @hex - output string to hold converted string + * @no_of_lengths - count of possible lengths for input string + * @possible_lengths - array holding possible lengths + * + * This function converts the continuous input string of even length and + * containing hexa decimal characters into hexa decimal array of uint8_t type. + * Input string needs to be NULL terminated and the length should match with + * one of entries in @possible_lengths + * + * Return: If conversion is successful return true else false + */ +static bool action_oui_string_to_hex(uint8_t *token, uint8_t *hex, + uint32_t no_of_lengths, + uint32_t *possible_lengths) +{ + uint32_t token_len = qdf_str_len(token); + uint32_t hex_str_len; + uint32_t i; + int ret; + + if (!token_len || (token_len & 0x01)) { + action_oui_err("Token len is not multiple of 2"); + return false; + } + + for (i = 0; i < no_of_lengths; i++) + if (token_len == possible_lengths[i]) + break; + + if (i == no_of_lengths) { + action_oui_err("Token len doesn't match with expected len"); + return false; + } + + hex_str_len = token_len / 2; + + ret = qdf_hex_str_to_binary(hex, token, hex_str_len); + if (ret) { + action_oui_err("Token doesn't contain hex digits"); + return false; + } + + return true; +} + +/** + * action_oui_token_string() - converts enum value to string + * token_id: enum value to be converted to string + * + * This function converts the enum value of type action_oui_token_type + * to string + * + * Return: converted string + */ +static +uint8_t *action_oui_token_string(enum action_oui_token_type token_id) +{ + switch (token_id) { + CASE_RETURN_STRING(ACTION_OUI_TOKEN); + CASE_RETURN_STRING(ACTION_OUI_DATA_LENGTH_TOKEN); + CASE_RETURN_STRING(ACTION_OUI_DATA_TOKEN); + CASE_RETURN_STRING(ACTION_OUI_DATA_MASK_TOKEN); + CASE_RETURN_STRING(ACTION_OUI_INFO_MASK_TOKEN); + CASE_RETURN_STRING(ACTION_OUI_MAC_ADDR_TOKEN); + CASE_RETURN_STRING(ACTION_OUI_MAC_MASK_TOKEN); + CASE_RETURN_STRING(ACTION_OUI_CAPABILITY_TOKEN); + CASE_RETURN_STRING(ACTION_OUI_END_TOKEN); + } + + return (uint8_t *) "UNKNOWN"; +} + +/** + * validate_and_convert_oui() - validate and convert OUI str to hex array + * @token: OUI string + * @ext: pointer to container which holds converted hex array + * @action_token: next action to be parsed + * + * This is an internal function invoked from action_oui_parse to validate + * the OUI string for action OUI inis, convert them to hex array and store it + * in action_oui extension. After successful parsing update the + * @action_token to hold the next expected string. + * + * Return: If conversion is successful return true else false + */ +static +bool validate_and_convert_oui(uint8_t *token, + struct action_oui_extension *ext, + enum action_oui_token_type *action_token) +{ + bool valid; + uint32_t expected_token_len[2] = {6, 10}; + + valid = action_oui_string_to_hex(token, ext->oui, 2, + expected_token_len); + if (!valid) + return false; + + ext->oui_length = qdf_str_len(token) / 2; + + *action_token = ACTION_OUI_DATA_LENGTH_TOKEN; + + return valid; +} + +/** + * validate_and_convert_data_length() - validate data len str + * @token: data length string + * @ext: pointer to container which holds hex value formed from input str + * @action_token: next action to be parsed + * + * This is an internal function invoked from action_oui_parse to validate + * the data length string for action OUI inis, convert it to hex value and + * store it in action_oui extension. After successful parsing update the + * @action_token to hold the next expected string. + * + * Return: If conversion is successful return true else false + */ +static bool +validate_and_convert_data_length(uint8_t *token, + struct action_oui_extension *ext, + enum action_oui_token_type *action_token) +{ + uint32_t token_len = qdf_str_len(token); + int ret; + uint8_t len = 0; + + if (token_len != 1 && token_len != 2) { + action_oui_err("Invalid str token len for action OUI data len"); + return false; + } + + ret = kstrtou8(token, 16, &len); + if (ret) { + action_oui_err("Invalid char in action OUI data len str token"); + return false; + } + + if ((uint32_t)len > ACTION_OUI_MAX_DATA_LENGTH) { + action_oui_err("action OUI data len is more than %u", + ACTION_OUI_MAX_DATA_LENGTH); + return false; + } + + ext->data_length = len; + + if (!ext->data_length) + *action_token = ACTION_OUI_INFO_MASK_TOKEN; + else + *action_token = ACTION_OUI_DATA_TOKEN; + + return true; +} + +/** + * validate_and_convert_data() - validate and convert data str to hex array + * @token: data string + * @ext: pointer to container which holds converted hex array + * @action_token: next action to be parsed + * + * This is an internal function invoked from action_oui_parse to validate + * the data string for action OUI inis, convert it to hex array and store in + * action_oui extension. After successful parsing update the @action_token + * to hold the next expected string. + * + * Return: If conversion is successful return true else false + */ +static bool +validate_and_convert_data(uint8_t *token, + struct action_oui_extension *ext, + enum action_oui_token_type *action_token) +{ + bool valid; + uint32_t expected_token_len[1] = {2 * ext->data_length}; + + valid = action_oui_string_to_hex(token, ext->data, 1, + expected_token_len); + if (!valid) + return false; + + *action_token = ACTION_OUI_DATA_MASK_TOKEN; + + return true; +} + +/** + * validate_and_convert_data_mask() - validate and convert data mask str + * @token: data mask string + * @ext: pointer to container which holds converted hex array + * @action_token: next action to be parsed + * + * This is an internal function invoked from action_oui_parse to validate + * the data mask string for action OUI inis, convert it to hex array and store + * in action_oui extension. After successful parsing update the + * @action_token to hold the next expected string. + * + * Return: If conversion is successful return true else false + */ +static bool +validate_and_convert_data_mask(uint8_t *token, + struct action_oui_extension *ext, + enum action_oui_token_type *action_token) +{ + bool valid; + uint32_t expected_token_len[1]; + uint32_t data_mask_length; + uint32_t data_length = ext->data_length; + + if (data_length % 8 == 0) + data_mask_length = data_length / 8; + else + data_mask_length = ((data_length / 8) + 1); + + if (data_mask_length > ACTION_OUI_MAX_DATA_MASK_LENGTH) + return false; + + expected_token_len[0] = 2 * data_mask_length; + + valid = action_oui_string_to_hex(token, ext->data_mask, 1, + expected_token_len); + if (!valid) + return false; + + ext->data_mask_length = data_mask_length; + + *action_token = ACTION_OUI_INFO_MASK_TOKEN; + + return valid; +} + +/** + * validate_and_convert_info_mask() - validate and convert info mask str + * @token: info mask string + * @ext: pointer to container which holds converted hex array + * @action_token: next action to be parsed + * + * This is an internal function invoked from action_oui_parse to validate + * the info mask string for action OUI inis, convert it to hex array and store + * in action_oui extension. After successful parsing update the + * @action_token to hold the next expected string. + * + * Return: If conversion is successful return true else false + */ +static bool +validate_and_convert_info_mask(uint8_t *token, + struct action_oui_extension *ext, + enum action_oui_token_type *action_token) +{ + uint32_t token_len = qdf_str_len(token); + uint8_t hex_value = 0; + uint32_t info_mask; + int ret; + + if (token_len != 2) { + action_oui_err("action OUI info mask str token len is not of 2 chars"); + return false; + } + + ret = kstrtou8(token, 16, &hex_value); + if (ret) { + action_oui_err("Invalid char in action OUI info mask str token"); + return false; + } + + info_mask = hex_value; + + ext->info_mask = info_mask; + + if (!info_mask || !(info_mask & ~ACTION_OUI_INFO_OUI)) { + *action_token = ACTION_OUI_END_TOKEN; + return true; + } + + if (info_mask & ~ACTION_OUI_INFO_MASK) { + action_oui_err("Invalid bits are set in action OUI info mask"); + return false; + } + + /* + * If OUI bit is not set in the info presence, we need to ignore the + * OUI and OUI Data. Set OUI and OUI data length to 0 here. + */ + if (!(info_mask & ACTION_OUI_INFO_OUI)) { + ext->oui_length = 0; + ext->data_length = 0; + ext->data_mask_length = 0; + } + + if (info_mask & ACTION_OUI_INFO_MAC_ADDRESS) { + *action_token = ACTION_OUI_MAC_ADDR_TOKEN; + return true; + } + + *action_token = ACTION_OUI_CAPABILITY_TOKEN; + return true; +} + +/** + * validate_and_convert_mac_addr() - validate and convert mac addr str + * @token: mac address string + * @ext: pointer to container which holds converted hex array + * @action_token: next action to be parsed + * + * This is an internal function invoked from action_oui_parse to validate + * the mac address string for action OUI inis, convert it to hex array and store + * in action_oui extension. After successful parsing update the + * @action_token to hold the next expected string. + * + * Return: If conversion is successful return true else false + */ +static bool +validate_and_convert_mac_addr(uint8_t *token, + struct action_oui_extension *ext, + enum action_oui_token_type *action_token) +{ + uint32_t expected_token_len[1] = {2 * QDF_MAC_ADDR_SIZE}; + bool valid; + + valid = action_oui_string_to_hex(token, ext->mac_addr, 1, + expected_token_len); + if (!valid) + return false; + + ext->mac_addr_length = QDF_MAC_ADDR_SIZE; + + *action_token = ACTION_OUI_MAC_MASK_TOKEN; + + return true; +} + +/** + * validate_and_convert_mac_mask() - validate and convert mac mask + * @token: mac mask string + * @ext: pointer to container which holds converted hex value + * @action_token: next action to be parsed + * + * This is an internal function invoked from action_oui_parse to validate + * the mac mask string for action OUI inis, convert it to hex value and store + * in action_oui extension. After successful parsing update the + * @action_token to hold the next expected string. + * + * Return: If conversion is successful return true else false + */ +static bool +validate_and_convert_mac_mask(uint8_t *token, + struct action_oui_extension *ext, + enum action_oui_token_type *action_token) +{ + uint32_t expected_token_len[1] = {2}; + uint32_t info_mask = ext->info_mask; + bool valid; + uint32_t mac_mask_length; + + valid = action_oui_string_to_hex(token, ext->mac_mask, 1, + expected_token_len); + if (!valid) + return false; + + mac_mask_length = qdf_str_len(token) / 2; + if (mac_mask_length > ACTION_OUI_MAC_MASK_LENGTH) { + action_oui_err("action OUI mac mask str token len is more than %u chars", + expected_token_len[0]); + return false; + } + + ext->mac_mask_length = mac_mask_length; + + if ((info_mask & ACTION_OUI_INFO_AP_CAPABILITY_NSS) || + (info_mask & ACTION_OUI_INFO_AP_CAPABILITY_HT) || + (info_mask & ACTION_OUI_INFO_AP_CAPABILITY_VHT) || + (info_mask & ACTION_OUI_INFO_AP_CAPABILITY_BAND)) { + *action_token = ACTION_OUI_CAPABILITY_TOKEN; + return true; + } + + *action_token = ACTION_OUI_END_TOKEN; + return true; +} + +/** + * validate_and_convert_capability() - validate and convert capability str + * @token: capability string + * @ext: pointer to container which holds converted hex value + * @action_token: next action to be parsed + * + * This is an internal function invoked from action_oui_parse to validate + * the capability string for action OUI inis, convert it to hex value and store + * in action_oui extension. After successful parsing update the + * @action_token to hold the next expected string. + * + * Return: If conversion is successful return true else false + */ +static bool +validate_and_convert_capability(uint8_t *token, + struct action_oui_extension *ext, + enum action_oui_token_type *action_token) +{ + uint32_t expected_token_len[1] = {2}; + uint32_t info_mask = ext->info_mask; + uint32_t capability_length; + uint8_t caps_0; + bool valid; + + valid = action_oui_string_to_hex(token, ext->capability, 1, + expected_token_len); + if (!valid) + return false; + + capability_length = qdf_str_len(token) / 2; + if (capability_length > ACTION_OUI_MAX_CAPABILITY_LENGTH) { + action_oui_err("action OUI capability str token len is more than %u chars", + expected_token_len[0]); + return false; + } + + caps_0 = ext->capability[0]; + + if ((info_mask & ACTION_OUI_INFO_AP_CAPABILITY_NSS) && + (!(caps_0 & ACTION_OUI_CAPABILITY_NSS_MASK))) { + action_oui_err("Info presence for NSS is set but respective bits in capability are not set"); + return false; + } + + if ((info_mask & ACTION_OUI_INFO_AP_CAPABILITY_BAND) && + (!(caps_0 & ACTION_OUI_CAPABILITY_BAND_MASK))) { + action_oui_err("Info presence for BAND is set but respective bits in capability are not set"); + return false; + } + + ext->capability_length = capability_length; + + *action_token = ACTION_OUI_END_TOKEN; + + return true; +} + +/** + * action_oui_extension_store() - store action oui extension + * @priv_obj: pointer to action_oui priv obj + * @oui_priv: type of the action + * @ext: oui extension to store in sme + * + * This function stores the parsed oui extension + * + * Return: QDF_STATUS + * + */ +static QDF_STATUS +action_oui_extension_store(struct action_oui_psoc_priv *psoc_priv, + struct action_oui_priv *oui_priv, + struct action_oui_extension ext) +{ + struct action_oui_extension_priv *ext_priv; + + qdf_mutex_acquire(&oui_priv->extension_lock); + if (qdf_list_size(&oui_priv->extension_list) == + ACTION_OUI_MAX_EXTENSIONS) { + qdf_mutex_release(&oui_priv->extension_lock); + action_oui_err("Reached maximum OUI extensions"); + return QDF_STATUS_E_FAILURE; + } + + ext_priv = qdf_mem_malloc(sizeof(*ext_priv)); + if (!ext_priv) { + qdf_mutex_release(&oui_priv->extension_lock); + action_oui_err("Failed to allocate memory for action oui extension priv"); + return QDF_STATUS_E_NOMEM; + } + + ext_priv->extension = ext; + qdf_list_insert_back(&oui_priv->extension_list, &ext_priv->item); + psoc_priv->total_extensions++; + qdf_mutex_release(&oui_priv->extension_lock); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +action_oui_parse(struct action_oui_psoc_priv *psoc_priv, + uint8_t *oui_string, enum action_oui_id action_id) +{ + struct action_oui_extension ext = {0}; + enum action_oui_token_type action_token = ACTION_OUI_TOKEN; + char *str1; + char *str2; + char *token; + bool valid = true; + bool oui_count_exceed = false; + uint32_t oui_index = 0; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct action_oui_priv *oui_priv; + + if (!oui_string) { + action_oui_err("Invalid string for action oui: %u", action_id); + return QDF_STATUS_E_INVAL; + } + + oui_priv = psoc_priv->oui_priv[action_id]; + if (!oui_priv) { + action_oui_err("action oui priv not allocated"); + return QDF_STATUS_E_INVAL; + } + + str1 = qdf_str_trim((char *)oui_string); + + while (str1) { + str2 = skip_spaces(str1); + if (str2[0] == '\0') { + action_oui_err("Invalid spaces in action oui: %u at extension: %u for token: %s", + action_id, + oui_index + 1, + action_oui_token_string(action_token)); + valid = false; + break; + } + + token = strsep(&str2, " "); + if (!token) { + action_oui_err("Invalid string for token: %s at extension: %u in action oui: %u", + action_oui_token_string(action_token), + oui_index + 1, action_id); + valid = false; + break; + } + + str1 = str2; + + switch (action_token) { + + case ACTION_OUI_TOKEN: + valid = validate_and_convert_oui(token, &ext, + &action_token); + break; + + case ACTION_OUI_DATA_LENGTH_TOKEN: + valid = validate_and_convert_data_length(token, &ext, + &action_token); + break; + + case ACTION_OUI_DATA_TOKEN: + valid = validate_and_convert_data(token, &ext, + &action_token); + break; + + case ACTION_OUI_DATA_MASK_TOKEN: + valid = validate_and_convert_data_mask(token, &ext, + &action_token); + break; + + case ACTION_OUI_INFO_MASK_TOKEN: + valid = validate_and_convert_info_mask(token, &ext, + &action_token); + break; + + case ACTION_OUI_MAC_ADDR_TOKEN: + valid = validate_and_convert_mac_addr(token, &ext, + &action_token); + break; + + case ACTION_OUI_MAC_MASK_TOKEN: + valid = validate_and_convert_mac_mask(token, &ext, + &action_token); + break; + + case ACTION_OUI_CAPABILITY_TOKEN: + valid = validate_and_convert_capability(token, &ext, + &action_token); + break; + + default: + valid = false; + break; + } + + if (!valid) { + action_oui_err("Invalid string for token: %s at extension: %u in action oui: %u", + action_oui_token_string(action_token), + oui_index + 1, + action_id); + break; + } + + if (action_token != ACTION_OUI_END_TOKEN) + continue; + + status = action_oui_extension_store(psoc_priv, oui_priv, ext); + if (!QDF_IS_STATUS_SUCCESS(status)) { + valid = false; + action_oui_err("sme set of extension: %u for action oui: %u failed", + oui_index + 1, action_id); + break; + } + + oui_index++; + if (oui_index == ACTION_OUI_MAX_EXTENSIONS) { + if (str1) + oui_count_exceed = true; + break; + } + + /* reset the params for next action OUI parse */ + action_token = ACTION_OUI_TOKEN; + qdf_mem_zero(&ext, sizeof(ext)); + } + + if (oui_count_exceed) { + action_oui_err("Reached Maximum extensions: %u in action_oui: %u, ignoring the rest", + ACTION_OUI_MAX_EXTENSIONS, action_id); + return QDF_STATUS_SUCCESS; + } + + if (action_token != ACTION_OUI_TOKEN && + action_token != ACTION_OUI_END_TOKEN && + valid && !str1) { + action_oui_err("No string for token: %s at extension: %u in action oui: %u", + action_oui_token_string(action_token), + oui_index + 1, + action_id); + valid = false; + } + + if (!oui_index) { + action_oui_err("Not able to parse any extension in action oui: %u", + action_id); + return QDF_STATUS_E_INVAL; + } + + if (valid) + action_oui_debug("All extensions: %u parsed successfully in action oui: %u", + oui_index, action_id); + else + action_oui_err("First %u extensions parsed successfully in action oui: %u", + oui_index, action_id); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS action_oui_send(struct action_oui_psoc_priv *psoc_priv, + enum action_oui_id action_id) +{ + QDF_STATUS status; + struct action_oui_request *req; + struct action_oui_priv *oui_priv; + struct action_oui_extension *extension; + struct action_oui_extension_priv *ext_priv; + qdf_list_node_t *node = NULL; + qdf_list_node_t *next_node = NULL; + qdf_list_t *extension_list; + uint32_t len; + uint32_t no_oui_extensions; + + oui_priv = psoc_priv->oui_priv[action_id]; + if (!oui_priv) + return QDF_STATUS_SUCCESS; + + extension_list = &oui_priv->extension_list; + qdf_mutex_acquire(&oui_priv->extension_lock); + if (qdf_list_empty(extension_list)) { + qdf_mutex_release(&oui_priv->extension_lock); + return QDF_STATUS_SUCCESS; + } + + no_oui_extensions = qdf_list_size(extension_list); + len = sizeof(*req) + no_oui_extensions * sizeof(*extension); + req = qdf_mem_malloc(len); + if (!req) { + action_oui_err("Failed to allocate memory for action_oui"); + qdf_mutex_release(&oui_priv->extension_lock); + return QDF_STATUS_E_NOMEM; + } + + req->action_id = oui_priv->id; + req->no_oui_extensions = no_oui_extensions; + req->total_no_oui_extensions = psoc_priv->total_extensions; + + extension = req->extension; + qdf_list_peek_front(extension_list, &node); + while (node) { + ext_priv = qdf_container_of(node, + struct action_oui_extension_priv, + item); + *extension = ext_priv->extension; + status = qdf_list_peek_next(extension_list, node, + &next_node); + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + node = next_node; + next_node = NULL; + extension++; + } + + qdf_mutex_release(&oui_priv->extension_lock); + + status = tgt_action_oui_send(psoc_priv->psoc, req); + qdf_mem_free(req); + + return status; +} + +/** + * check_for_vendor_oui_data() - compares for vendor OUI data from IE + * and returns true if OUI data matches with the ini + * @extension: pointer to action oui extension data + * @oui_ptr: pointer to Vendor IE in the beacon + * + * Return: true or false + */ +static bool +check_for_vendor_oui_data(struct action_oui_extension *extension, + const uint8_t *oui_ptr) +{ + const uint8_t *data; + uint8_t i, j, elem_len, data_len; + uint8_t data_mask = 0x80; + + if (!oui_ptr) + return false; + + elem_len = oui_ptr[1]; + if (elem_len < extension->oui_length) + return false; + + data_len = elem_len - extension->oui_length; + if (data_len < extension->data_length) + return false; + + data = &oui_ptr[2 + extension->oui_length]; + for (i = 0, j = 0; + (i < data_len && j < extension->data_mask_length); + i++) { + if ((extension->data_mask[j] & data_mask) && + !(extension->data[i] == data[i])) + return false; + data_mask = data_mask >> 1; + if (!data_mask) { + data_mask = 0x80; + j++; + } + } + + return true; +} + +/** + * check_for_vendor_ap_mac() - compares for vendor AP MAC in the ini with + * bssid from the session and returns true if matches + * @extension: pointer to action oui extension data + * @attr: pointer to structure containing mac_addr (bssid) of AP + * + * Return: true or false + */ +static bool +check_for_vendor_ap_mac(struct action_oui_extension *extension, + struct action_oui_search_attr *attr) +{ + uint8_t i; + uint8_t mac_mask = 0x80; + uint8_t *mac_addr = attr->mac_addr; + + for (i = 0; i < QDF_MAC_ADDR_SIZE; i++) { + if ((*extension->mac_mask & mac_mask) && + !(extension->mac_addr[i] == mac_addr[i])) + return false; + mac_mask = mac_mask >> 1; + } + + return true; +} + +/** + * check_for_vendor_ap_capabilities() - Compares various Vendor AP + * capabilities like NSS, HT, VHT, Band from the ini with the AP's capability + * from the beacon and returns true if all the capability matches + * @extension: pointer to oui extension data + * @attr: pointer to structure containing type of action, ap capabilities + * + * Return: true or false + */ +static bool +check_for_vendor_ap_capabilities(struct action_oui_extension *extension, + struct action_oui_search_attr *attr) +{ + uint8_t nss_mask; + + if (extension->info_mask & ACTION_OUI_INFO_AP_CAPABILITY_NSS) { + nss_mask = 1 << (attr->nss - 1); + if (!((*extension->capability & + ACTION_OUI_CAPABILITY_NSS_MASK) & + nss_mask)) + return false; + } + + if (extension->info_mask & ACTION_OUI_INFO_AP_CAPABILITY_HT) { + if (*extension->capability & + ACTION_OUI_CAPABILITY_HT_ENABLE_MASK) { + if (!attr->ht_cap) + return false; + } else { + if (attr->ht_cap) + return false; + } + } + + if (extension->info_mask & ACTION_OUI_INFO_AP_CAPABILITY_VHT) { + if (*extension->capability & + ACTION_OUI_CAPABILITY_VHT_ENABLE_MASK) { + if (!attr->vht_cap) + return false; + } else { + if (attr->vht_cap) + return false; + } + } + + if (extension->info_mask & ACTION_OUI_INFO_AP_CAPABILITY_BAND && + ((attr->enable_5g && + !(*extension->capability & ACTION_CAPABILITY_5G_BAND_MASK)) || + (attr->enable_2g && + !(*extension->capability & ACTION_OUI_CAPABILITY_2G_BAND_MASK)))) + return false; + + return true; +} + +static const uint8_t * +action_oui_get_oui_ptr(struct action_oui_extension *extension, + struct action_oui_search_attr *attr) +{ + if (!attr->ie_data || !attr->ie_length) + return NULL; + + return wlan_get_vendor_ie_ptr_from_oui(extension->oui, + extension->oui_length, + attr->ie_data, + attr->ie_length); +} + +bool +action_oui_search(struct action_oui_psoc_priv *psoc_priv, + struct action_oui_search_attr *attr, + enum action_oui_id action_id) +{ + struct action_oui_priv *oui_priv; + struct action_oui_extension_priv *priv_ext; + struct action_oui_extension *extension; + qdf_list_node_t *node = NULL; + qdf_list_node_t *next_node = NULL; + qdf_list_t *extension_list; + QDF_STATUS qdf_status; + const uint8_t *oui_ptr; + bool wildcard_oui = false; + + oui_priv = psoc_priv->oui_priv[action_id]; + if (!oui_priv) { + action_oui_debug("action oui for id %d is empty", + action_id); + return false; + } + + extension_list = &oui_priv->extension_list; + qdf_mutex_acquire(&oui_priv->extension_lock); + if (qdf_list_empty(extension_list)) { + qdf_mutex_release(&oui_priv->extension_lock); + return false; + } + + qdf_list_peek_front(extension_list, &node); + while (node) { + priv_ext = qdf_container_of(node, + struct action_oui_extension_priv, + item); + extension = &priv_ext->extension; + + /* + * If a wildcard OUI bit is not set in the info_mask, proceed + * to other checks skipping the OUI and vendor data checks + */ + + if (!(extension->info_mask & ACTION_OUI_INFO_OUI)) + wildcard_oui = true; + + oui_ptr = action_oui_get_oui_ptr(extension, attr); + + if (!oui_ptr && !wildcard_oui) + goto next; + + if (extension->data_length && !wildcard_oui && + !check_for_vendor_oui_data(extension, oui_ptr)) + goto next; + + + if ((extension->info_mask & ACTION_OUI_INFO_MAC_ADDRESS) && + !check_for_vendor_ap_mac(extension, attr)) + goto next; + + if (!check_for_vendor_ap_capabilities(extension, attr)) + goto next; + + action_oui_debug("Vendor AP/STA found for OUI"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + extension->oui, extension->oui_length); + qdf_mutex_release(&oui_priv->extension_lock); + return true; +next: + qdf_status = qdf_list_peek_next(extension_list, + node, &next_node); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + break; + + node = next_node; + next_node = NULL; + wildcard_oui = false; + } + + qdf_mutex_release(&oui_priv->extension_lock); + return false; +} diff --git a/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/inc/wlan_action_oui_public_struct.h b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/inc/wlan_action_oui_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..b73ddc90d30138e39350b5d9eabb32cfc789f8ec --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/inc/wlan_action_oui_public_struct.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare structs and macros which can be accessed by various + * components and modules. + */ + +#ifndef _WLAN_ACTION_OUI_PUBLIC_STRUCT_H_ +#define _WLAN_ACTION_OUI_PUBLIC_STRUCT_H_ + +#include +#include +#include + +/* + * Maximum ini string length of actions oui extensions, + * (n * 83) + (n - 1) spaces + 1 (terminating character), + * where n is the no of oui extensions + * currently, max no of oui extensions is 10 + */ +#define ACTION_OUI_MAX_STR_LEN 840 + +/* + * Maximum number of action oui extensions supported in + * each action oui category + */ +#define ACTION_OUI_MAX_EXTENSIONS 10 + +#define ACTION_OUI_MAX_OUI_LENGTH 5 +#define ACTION_OUI_MAX_DATA_LENGTH 20 +#define ACTION_OUI_MAX_DATA_MASK_LENGTH 3 +#define ACTION_OUI_MAC_MASK_LENGTH 1 +#define ACTION_OUI_MAX_CAPABILITY_LENGTH 1 + +/* + * NSS Mask and NSS Offset to extract NSS info from + * capability field of action oui extension + */ +#define ACTION_OUI_CAPABILITY_NSS_MASK 0x0f +#define ACTION_OUI_CAPABILITY_NSS_OFFSET 0 +#define ACTION_OUI_CAPABILITY_NSS_MASK_1X1 1 +#define ACTION_OUI_CAPABILITY_NSS_MASK_2X2 2 +#define ACTION_OUI_CAPABILITY_NSS_MASK_3X3 4 +#define ACTION_OUI_CAPABILITY_NSS_MASK_4X4 8 + +/* + * Mask and offset to extract HT and VHT info from + * capability field of action oui extension + */ +#define ACTION_OUI_CAPABILITY_HT_ENABLE_MASK 0x10 +#define ACTION_OUI_CAPABILITY_HT_ENABLE_OFFSET 4 +#define ACTION_OUI_CAPABILITY_VHT_ENABLE_MASK 0x20 +#define ACTION_OUI_CAPABILITY_VHT_ENABLE_OFFSET 5 + +/* + * Mask and offset to extract Band (2G and 5G) info from + * capability field of action oui extension + */ +#define ACTION_OUI_CAPABILITY_BAND_MASK 0xC0 +#define ACTION_OUI_CAPABILITY_BAND_OFFSET 6 +#define ACTION_OUI_CAPABILITY_2G_BAND_MASK 0x40 +#define ACTION_OUI_CAPABILITY_2G_BAND_OFFSET 6 +#define ACTION_CAPABILITY_5G_BAND_MASK 0x80 +#define ACTION_CAPABILITY_5G_BAND_OFFSET 7 + +/** + * enum action_oui_id - to identify type of action oui + * @ACTION_OUI_CONNECT_1X1: for 1x1 connection only + * @ACTION_OUI_ITO_EXTENSION: for extending inactivity time of station + * @ACTION_OUI_CCKM_1X1: for TX with CCKM 1x1 only + * @ACTION_OUI_ITO_ALTERNATE: alternate ITO extensions used by firmware + * @ACTION_OUI_SWITCH_TO_11N_MODE: connect in 11n + * @ACTION_OUI_CONNECT_1X1_WITH_1_CHAIN: connect in 1x1 & disable diversity gain + * @ACTION_OUI_DISABLE_AGGRESSIVE_TX: disable aggressive TX in firmware + * @ACTION_OUI_FORCE_MAX_NSS: Force Max NSS connection with few IOT APs + * @ACTION_OUI_DISABLE_AGGRESSIVE_EDCA: disable aggressive EDCA with the ap + * @ACTION_OUI_DISABLE_TWT: disable TWT with the ap + * @ACTION_OUI_HOST_ONLY: host only action id start - placeholder. + * New Firmware related "ACTION" needs to be added before this placeholder. + * @ACTION_OUI_HOST_RECONN: reconnect to the same BSSID when wait for + * association response timeout from AP + * @ACTION_OUI_MAXIMUM_ID: maximum number of action oui types + */ +enum action_oui_id { + ACTION_OUI_CONNECT_1X1 = 0, + ACTION_OUI_ITO_EXTENSION = 1, + ACTION_OUI_CCKM_1X1 = 2, + ACTION_OUI_ITO_ALTERNATE = 3, + ACTION_OUI_SWITCH_TO_11N_MODE = 4, + ACTION_OUI_CONNECT_1X1_WITH_1_CHAIN = 5, + ACTION_OUI_DISABLE_AGGRESSIVE_TX = 6, + ACTION_OUI_FORCE_MAX_NSS = 7, + ACTION_OUI_DISABLE_AGGRESSIVE_EDCA = 8, + ACTION_OUI_DISABLE_TWT = 9, + ACTION_OUI_HOST_ONLY, + ACTION_OUI_HOST_RECONN = ACTION_OUI_HOST_ONLY, + ACTION_OUI_MAXIMUM_ID +}; + +/** + * enum action_oui_info - to indicate presence of various action OUI + * fields in action oui extension, following identifiers are to be set in + * the info mask field of action oui extension + * @ACTION_OUI_INFO_OUI: to indicate presence of OUI string + * @ACTION_OUI_INFO_MAC_ADDRESS: to indicate presence of mac address + * @ACTION_OUI_INFO_AP_CAPABILITY_NSS: to indicate presence of nss info + * @ACTION_OUI_INFO_AP_CAPABILITY_HT: to indicate presence of HT cap + * @ACTION_OUI_INFO_AP_CAPABILITY_VHT: to indicate presence of VHT cap + * @ACTION_OUI_INFO_AP_CAPABILITY_BAND: to indicate presence of band info + */ +enum action_oui_info { + /* + * OUI centric parsing, expect OUI in each action OUI extension, + * hence, ACTION_OUI_INFO_OUI is dummy + */ + ACTION_OUI_INFO_OUI = 1 << 0, + ACTION_OUI_INFO_MAC_ADDRESS = 1 << 1, + ACTION_OUI_INFO_AP_CAPABILITY_NSS = 1 << 2, + ACTION_OUI_INFO_AP_CAPABILITY_HT = 1 << 3, + ACTION_OUI_INFO_AP_CAPABILITY_VHT = 1 << 4, + ACTION_OUI_INFO_AP_CAPABILITY_BAND = 1 << 5, +}; + +/* Total mask of all enum action_oui_info IDs */ +#define ACTION_OUI_INFO_MASK 0x3F + +/** + * struct action_oui_extension - action oui extension contents + * @info_mask: info mask + * @oui_length: length of the oui, either 3 or 5 bytes + * @data_length: length of the oui data + * @data_mask_length: length of the data mask + * @mac_addr_length: length of the mac addr + * @mac_mask_length: length of the mac mask + * @capability_length: length of the capability + * @oui: oui value + * @data: data buffer + * @data_mask: data mask buffer + * @mac_addr: mac addr + * @mac_mask: mac mask + * @capability: capability buffer + */ +struct action_oui_extension { + uint32_t info_mask; + uint32_t oui_length; + uint32_t data_length; + uint32_t data_mask_length; + uint32_t mac_addr_length; + uint32_t mac_mask_length; + uint32_t capability_length; + uint8_t oui[ACTION_OUI_MAX_OUI_LENGTH]; + uint8_t data[ACTION_OUI_MAX_DATA_LENGTH]; + uint8_t data_mask[ACTION_OUI_MAX_DATA_MASK_LENGTH]; + uint8_t mac_addr[QDF_MAC_ADDR_SIZE]; + uint8_t mac_mask[ACTION_OUI_MAC_MASK_LENGTH]; + uint8_t capability[ACTION_OUI_MAX_CAPABILITY_LENGTH]; +}; + +/** + * struct action_oui_request - Contains specific action oui information + * @action_id: type of action from enum action_oui_info + * @no_oui_extensions: number of action oui extensions of type @action_id + * @total_no_oui_extensions: total no of oui extensions from all + * action oui types, this is just a total count needed by firmware + * @extension: pointer to zero length array, to indicate this structure is + * followed by a array of @no_oui_extensions structures of + * type struct action_oui_extension + */ +struct action_oui_request { + enum action_oui_id action_id; + uint32_t no_oui_extensions; + uint32_t total_no_oui_extensions; + struct action_oui_extension extension[0]; +}; + +/** + * struct action_oui_search_attr - Used to check against action_oui ini input + * + * @ie_data: beacon ie data + * @ie_length: length of ie data + * @mac_addr: bssid of access point + * @nss: AP spatial stream info + * @ht_cap: Whether AP is HT capable + * @vht_cap: Whether AP is VHT capable + * @enable_2g: Whether 2.4GHz band is enabled in AP + * @enable_5g: Whether 5GHz band is enabled in AP + */ +struct action_oui_search_attr { + uint8_t *ie_data; + uint32_t ie_length; + uint8_t *mac_addr; + uint32_t nss; + bool ht_cap; + bool vht_cap; + bool enable_2g; + bool enable_5g; +}; + +#endif /* _WLAN_ACTION_OUI_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/inc/wlan_action_oui_tgt_api.h b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/inc/wlan_action_oui_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..7a1d4f30802ffb0079cf9bc79fd16d06ee2c9176 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/inc/wlan_action_oui_tgt_api.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare public API for action_oui to interact with target/WMI + */ + +#ifndef _WLAN_ACTION_OUI_TGT_API_H_ +#define _WLAN_ACTION_OUI_TGT_API_H_ + +#include "wlan_action_oui_public_struct.h" +#include "wlan_action_oui_objmgr.h" + +#define GET_ACTION_OUI_TX_OPS_FROM_PSOC(psoc) \ + (&action_oui_psoc_get_priv(psoc)->tx_ops) + +/** + * tgt_action_oui_send() - Send request to target if + * @psoc: objmgr psoc object + * @req: action_oui request to be send + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_action_oui_send(struct wlan_objmgr_psoc *psoc, + struct action_oui_request *req); + +/** + * struct action_oui_tx_ops - structure of tx operations + * @send_req: Pointer to hold target_if send function + */ +struct action_oui_tx_ops { + QDF_STATUS (*send_req)(struct wlan_objmgr_psoc *psoc, + struct action_oui_request *req); +}; + +#endif /* _WLAN_ACTION_OUI_TGT_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/inc/wlan_action_oui_ucfg_api.h b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/inc/wlan_action_oui_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..00daeb9b66d65a732c3486e017709483c56ae514 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/inc/wlan_action_oui_ucfg_api.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare public API related to the action_oui called by north bound + * HDD/OSIF/LIM + */ + +#ifndef _WLAN_ACTION_OUI_UCFG_API_H_ +#define _WLAN_ACTION_OUI_UCFG_API_H_ + +#include +#include +#include "wlan_action_oui_public_struct.h" +#include "wlan_action_oui_objmgr.h" + +#ifdef WLAN_FEATURE_ACTION_OUI + +/** + * ucfg_action_oui_init() - Register notification handlers. + * + * This function registers action_oui notification handlers which are + * invoked from psoc create/destroy handlers. + * + * Return: For successful registration - QDF_STATUS_SUCCESS, + * else QDF_STATUS error codes. + */ +QDF_STATUS ucfg_action_oui_init(void); + +/** + * ucfg_action_oui_deinit() - Unregister notification handlers. + * + * This function Unregisters action_oui notification handlers which are + * invoked from psoc create/destroy handlers. + * + * Return: None + */ +void ucfg_action_oui_deinit(void); + +/** + * ucfg_action_oui_parse() - Parse input string and extract extensions. + * @psoc: objmgr psoc object + * @in_str: input string to be parsed + * @action_id: action to which given string corresponds + * + * This is a wrapper function which invokes internal function + * action_oui_extract() to extract OUIs and related attributes. + * + * Return: For successful parse - QDF_STATUS_SUCCESS, + * else QDF_STATUS error codes. + */ +QDF_STATUS +ucfg_action_oui_parse(struct wlan_objmgr_psoc *psoc, + const uint8_t *in_str, + enum action_oui_id action_id); + +/** + * ucfg_action_oui_send() - Send action_oui and related attributes to Fw. + * @psoc: objmgr psoc object + * + * This is a wrapper function which invokes internal function + * action_oui_send() to send OUIs and related attributes to firmware. + * + * Return: For successful send - QDF_STATUS_SUCCESS, + * else QDF_STATUS error codes. + */ +QDF_STATUS ucfg_action_oui_send(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_action_oui_enabled() - State of action_oui component + * + * Return: When action_oui component is present return true + * else return false. + */ +static inline bool ucfg_action_oui_enabled(void) +{ + return true; +} + +/** + * ucfg_action_oui_search() - Check for OUIs and related info in IE data. + * @psoc: objmgr psoc object + * @attr: pointer to structure containing type of action, beacon IE data etc., + * @action_id: type of action to be checked + * + * This is a wrapper function which invokes internal function to search + * for OUIs and related info (specified from ini file) in vendor specific + * data of beacon IE for given action. + * + * Return: If search is successful return true else false. + */ +bool ucfg_action_oui_search(struct wlan_objmgr_psoc *psoc, + struct action_oui_search_attr *attr, + enum action_oui_id action_id); + +#else + +/** + * ucfg_action_oui_init() - Register notification handlers. + * + * This function registers action_oui notification handlers which are + * invoked from psoc create/destroy handlers. + * + * Return: For successful registration - QDF_STATUS_SUCCESS, + * else QDF_STATUS error codes. + */ +static inline +QDF_STATUS ucfg_action_oui_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ucfg_action_oui_deinit() - Unregister notification handlers. + * + * This function Unregisters action_oui notification handlers which are + * invoked from psoc create/destroy handlers. + * + * Return: None + */ +static inline +void ucfg_action_oui_deinit(void) +{ +} + +/** + * ucfg_action_oui_parse() - Parse input string of action_id specified. + * @psoc: objmgr psoc object + * @in_str: input string to be parsed + * @action_id: action to which given string corresponds + * + * This is a wrapper function which invokes internal function + * action_oui_extract() to extract OUIs and related attributes. + * + * Return: For successful parse - QDF_STATUS_SUCCESS, + * else QDF_STATUS error codes. + */ +static inline QDF_STATUS +ucfg_action_oui_parse(struct wlan_objmgr_psoc *psoc, + const uint8_t *in_str, + enum action_oui_id action_id) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ucfg_action_oui_send() - Send action_oui and related attributes to Fw. + * @psoc: objmgr psoc object + * + * This is a wrapper function which invokes internal function + * action_oui_send() to send OUIs and related attributes to firmware. + * + * Return: For successful send - QDF_STATUS_SUCCESS, + * else QDF_STATUS error codes. + */ +static inline +QDF_STATUS ucfg_action_oui_send(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ucfg_action_oui_enabled() - State of action_oui component + * + * Return: When action_oui component is present return true + * else return false. + */ +static inline bool ucfg_action_oui_enabled(void) +{ + return false; +} + +/** + * ucfg_action_oui_search() - Check for OUIs and related info in IE data. + * @psoc: objmgr psoc object + * @attr: pointer to structure containing type of action, beacon IE data etc., + * @action_id: type of action to be checked + * + * This is a wrapper function which invokes internal function to search + * for OUIs and related info (specified from ini file) in vendor specific + * data of beacon IE for given action. + * + * Return: If search is successful return true else false. + */ +static inline +bool ucfg_action_oui_search(struct wlan_objmgr_psoc *psoc, + struct action_oui_search_attr *attr, + enum action_oui_id action_id) +{ + return false; +} + +#endif /* WLAN_FEATURE_ACTION_OUI */ + +#endif /* _WLAN_ACTION_OUI_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/src/wlan_action_oui_tgt_api.c b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/src/wlan_action_oui_tgt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..e3c2177d852819eab99194c5fcd4fa57ca4f73d0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/src/wlan_action_oui_tgt_api.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for action_oui to interact with target/WMI + */ + +#include "wlan_action_oui_tgt_api.h" +#include "wlan_action_oui_main.h" +#include "wlan_action_oui_public_struct.h" + +QDF_STATUS tgt_action_oui_send(struct wlan_objmgr_psoc *psoc, + struct action_oui_request *req) +{ + struct action_oui_tx_ops *tx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + ACTION_OUI_ENTER(); + + tx_ops = GET_ACTION_OUI_TX_OPS_FROM_PSOC(psoc); + QDF_ASSERT(tx_ops->send_req); + if (tx_ops->send_req) + status = tx_ops->send_req(psoc, req); + + ACTION_OUI_EXIT(); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/src/wlan_action_oui_ucfg_api.c b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/src/wlan_action_oui_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..76782add6fc33be55898eaeb53f010b34e6a2307 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/action_oui/dispatcher/src/wlan_action_oui_ucfg_api.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2012-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Public API implementation of action_oui called by north bound HDD/OSIF. + */ + +#include "wlan_action_oui_ucfg_api.h" +#include "wlan_action_oui_main.h" +#include "wlan_action_oui_main.h" +#include "target_if_action_oui.h" +#include "wlan_action_oui_tgt_api.h" +#include + +QDF_STATUS ucfg_action_oui_init(void) +{ + QDF_STATUS status; + + ACTION_OUI_ENTER(); + + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_ACTION_OUI, + action_oui_psoc_create_notification, NULL); + if (!QDF_IS_STATUS_SUCCESS(status)) { + action_oui_err("Failed to register psoc create handler"); + goto exit; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_ACTION_OUI, + action_oui_psoc_destroy_notification, NULL); + if (QDF_IS_STATUS_SUCCESS(status)) { + action_oui_debug("psoc create/delete notifications registered"); + goto exit; + } + + action_oui_err("Failed to register psoc delete handler"); + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_ACTION_OUI, + action_oui_psoc_create_notification, NULL); + +exit: + ACTION_OUI_EXIT(); + return status; +} + +void ucfg_action_oui_deinit(void) +{ + QDF_STATUS status; + + ACTION_OUI_ENTER(); + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_ACTION_OUI, + action_oui_psoc_create_notification, NULL); + if (!QDF_IS_STATUS_SUCCESS(status)) + action_oui_err("Failed to unregister psoc create handler"); + + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_ACTION_OUI, + action_oui_psoc_destroy_notification, + NULL); + if (!QDF_IS_STATUS_SUCCESS(status)) + action_oui_err("Failed to unregister psoc delete handler"); + + ACTION_OUI_EXIT(); +} + +QDF_STATUS +ucfg_action_oui_parse(struct wlan_objmgr_psoc *psoc, + const uint8_t *in_str, + enum action_oui_id action_id) +{ + struct action_oui_psoc_priv *psoc_priv; + QDF_STATUS status = QDF_STATUS_E_INVAL; + uint8_t *oui_str; + int len; + + ACTION_OUI_ENTER(); + + if (!psoc) { + action_oui_err("psoc is NULL"); + goto exit; + } + + if (action_id >= ACTION_OUI_MAXIMUM_ID) { + action_oui_err("Invalid action_oui id: %u", action_id); + goto exit; + } + + psoc_priv = action_oui_psoc_get_priv(psoc); + if (!psoc_priv) { + action_oui_err("psoc priv is NULL"); + goto exit; + } + + len = qdf_str_len(in_str); + if (len <= 0 || len > ACTION_OUI_MAX_STR_LEN - 1) { + action_oui_err("Invalid string length: %u", action_id); + goto exit; + } + + oui_str = qdf_mem_malloc(len + 1); + if (!oui_str) { + action_oui_err("Mem alloc failed for string: %u", action_id); + status = QDF_STATUS_E_NOMEM; + goto exit; + } + + qdf_mem_copy(oui_str, in_str, len); + oui_str[len] = '\0'; + + status = action_oui_parse(psoc_priv, oui_str, action_id); + if (!QDF_IS_STATUS_SUCCESS(status)) + action_oui_err("Failed to parse: %u", action_id); + + qdf_mem_free(oui_str); + +exit: + ACTION_OUI_EXIT(); + return status; +} + +QDF_STATUS ucfg_action_oui_send(struct wlan_objmgr_psoc *psoc) +{ + struct action_oui_psoc_priv *psoc_priv; + QDF_STATUS status = QDF_STATUS_E_INVAL; + uint32_t id; + + if (!psoc) { + action_oui_err("psoc is NULL"); + goto exit; + } + + psoc_priv = action_oui_psoc_get_priv(psoc); + if (!psoc_priv) { + action_oui_err("psoc priv is NULL"); + goto exit; + } + + for (id = 0; id < ACTION_OUI_MAXIMUM_ID; id++) { + if (id >= ACTION_OUI_HOST_ONLY) + continue; + status = action_oui_send(psoc_priv, id); + if (!QDF_IS_STATUS_SUCCESS(status)) + action_oui_err("Failed to send: %u", id); + } + +exit: + + return status; +} + +bool ucfg_action_oui_search(struct wlan_objmgr_psoc *psoc, + struct action_oui_search_attr *attr, + enum action_oui_id action_id) +{ + struct action_oui_psoc_priv *psoc_priv; + bool found = false; + + if (!psoc || !attr) { + action_oui_err("Invalid psoc or search attrs"); + goto exit; + } + + if (action_id >= ACTION_OUI_MAXIMUM_ID) { + action_oui_err("Invalid action_oui id: %u", action_id); + goto exit; + } + + psoc_priv = action_oui_psoc_get_priv(psoc); + if (!psoc_priv) { + action_oui_err("psoc priv is NULL"); + goto exit; + } + + found = action_oui_search(psoc_priv, attr, action_id); + +exit: + + return found; +} diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/inc/wlan_blm_core.h b/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/inc/wlan_blm_core.h new file mode 100644 index 0000000000000000000000000000000000000000..c5c2de8b99240b179d6162558f2b5c0cdd28d30a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/inc/wlan_blm_core.h @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare internal APIs related to the blacklist component + */ + +#ifndef _WLAN_BLM_CORE_H_ +#define _WLAN_BLM_CORE_H_ + +#include + +#define BLM_IS_AP_AVOIDED_BY_USERSPACE(cur_node) \ + cur_node->userspace_avoidlist + +#define BLM_IS_AP_AVOIDED_BY_DRIVER(cur_node) \ + cur_node->driver_avoidlist + +#define BLM_IS_AP_BLACKLISTED_BY_USERSPACE(cur_node) \ + cur_node->userspace_blacklist + +#define BLM_IS_AP_BLACKLISTED_BY_DRIVER(cur_node) \ + cur_node->driver_blacklist + +#define BLM_IS_AP_IN_MONITOR_LIST(cur_node) \ + cur_node->driver_monitorlist + +#define BLM_IS_AP_IN_RSSI_REJECT_LIST(cur_node) \ + cur_node->rssi_reject_list + +#define BLM_IS_AP_IN_BLACKLIST(cur_node) \ + (BLM_IS_AP_BLACKLISTED_BY_USERSPACE(cur_node) | \ + BLM_IS_AP_BLACKLISTED_BY_DRIVER(cur_node) | \ + BLM_IS_AP_IN_RSSI_REJECT_LIST(cur_node)) + +#define BLM_IS_AP_IN_AVOIDLIST(cur_node) \ + (BLM_IS_AP_AVOIDED_BY_USERSPACE(cur_node) | \ + BLM_IS_AP_AVOIDED_BY_DRIVER(cur_node)) + +#define IS_AP_IN_USERSPACE_BLACKLIST_ONLY(cur_node) \ + (BLM_IS_AP_BLACKLISTED_BY_USERSPACE(cur_node) & \ + !(BLM_IS_AP_IN_AVOIDLIST(cur_node) | \ + BLM_IS_AP_IN_MONITOR_LIST(cur_node) | \ + BLM_IS_AP_IN_RSSI_REJECT_LIST(cur_node) | \ + BLM_IS_AP_BLACKLISTED_BY_DRIVER(cur_node))) + +#define IS_AP_IN_MONITOR_LIST_ONLY(cur_node) \ + (BLM_IS_AP_IN_MONITOR_LIST(cur_node) & \ + !(BLM_IS_AP_IN_AVOIDLIST(cur_node) | \ + BLM_IS_AP_IN_BLACKLIST(cur_node))) + +#define IS_AP_IN_AVOID_LIST_ONLY(cur_node) \ + (BLM_IS_AP_IN_AVOIDLIST(cur_node) & \ + !(BLM_IS_AP_IN_MONITOR_LIST(cur_node) | \ + BLM_IS_AP_IN_BLACKLIST(cur_node))) + +#define IS_AP_IN_DRIVER_BLACKLIST_ONLY(cur_node) \ + (BLM_IS_AP_BLACKLISTED_BY_DRIVER(cur_node) & \ + !(BLM_IS_AP_IN_AVOIDLIST(cur_node) | \ + BLM_IS_AP_IN_MONITOR_LIST(cur_node) | \ + BLM_IS_AP_IN_RSSI_REJECT_LIST(cur_node) | \ + BLM_IS_AP_BLACKLISTED_BY_USERSPACE(cur_node))) + +#define IS_AP_IN_RSSI_REJECT_LIST_ONLY(cur_node) \ + (BLM_IS_AP_IN_RSSI_REJECT_LIST(cur_node) & \ + !(BLM_IS_AP_IN_AVOIDLIST(cur_node) | \ + BLM_IS_AP_IN_MONITOR_LIST(cur_node) | \ + BLM_IS_AP_BLACKLISTED_BY_DRIVER(cur_node) | \ + BLM_IS_AP_BLACKLISTED_BY_USERSPACE(cur_node))) + +#define IS_AP_IN_USERSPACE_AVOID_LIST_ONLY(cur_node) \ + (BLM_IS_AP_AVOIDED_BY_USERSPACE(cur_node) & \ + !(BLM_IS_AP_AVOIDED_BY_DRIVER(cur_node) | \ + BLM_IS_AP_IN_MONITOR_LIST(cur_node) | \ + BLM_IS_AP_IN_BLACKLIST(cur_node))) + +#define IS_AP_IN_DRIVER_AVOID_LIST_ONLY(cur_node) \ + (BLM_IS_AP_AVOIDED_BY_DRIVER(cur_node) & \ + !(BLM_IS_AP_AVOIDED_BY_USERSPACE(cur_node) | \ + BLM_IS_AP_IN_MONITOR_LIST(cur_node) | \ + BLM_IS_AP_IN_BLACKLIST(cur_node))) + +/** + * struct blm_reject_ap_timestamp - Structure to store the reject list BSSIDs + * entry time stamp. + * @userspace_avoid_timestamp: Time when userspace adds BSSID to avoid list. + * @driver_avoid_timestamp: Time when driver adds BSSID to avoid list. + * @userspace_blacklist_timestamp: Time when userspace adds BSSID to black list. + * @driver_blacklist_timestamp: Time when driver adds BSSID to black list. + * @rssi_reject_timestamp: Time when driver adds BSSID to rssi reject list. + * @driver_monitor_timestamp: Time when driver adds BSSID to monitor list. + */ +struct blm_reject_ap_timestamp { + qdf_time_t userspace_avoid_timestamp; + qdf_time_t driver_avoid_timestamp; + qdf_time_t userspace_blacklist_timestamp; + qdf_time_t driver_blacklist_timestamp; + qdf_time_t rssi_reject_timestamp; + qdf_time_t driver_monitor_timestamp; +}; + +/** + * struct blm_reject_ap - Structure of a node added to blacklist manager + * @node: Node of the entry + * @bssid: Bssid of the AP entry. + * @rssi_reject_params: Rssi reject params of the AP entry. + * @bad_bssid_counter: It represent how many times data stall happened. + * @ap_timestamp: Ap timestamp. + * @reject_ap_type: what is the type of rejection for the AP (avoid, black etc.) + * @reject_ap_reason: reason for adding the BSSID to BLM + * @connect_timestamp: Timestamp when the STA got connected with this BSSID + */ +struct blm_reject_ap { + qdf_list_node_t node; + struct qdf_mac_addr bssid; + struct blm_rssi_disallow_params rssi_reject_params; + uint8_t bad_bssid_counter; + struct blm_reject_ap_timestamp ap_timestamp; + union { + struct { + uint8_t userspace_blacklist:1, + driver_blacklist:1, + userspace_avoidlist:1, + driver_avoidlist:1, + rssi_reject_list:1, + driver_monitorlist:1; + }; + uint8_t reject_ap_type; + }; + union { + struct { + uint32_t nud_fail:1, + sta_kickout:1, + ho_fail:1, + poor_rssi:1, + oce_assoc_reject:1, + blacklist_userspace:1, + avoid_userspace:1, + btm_disassoc_imminent:1, + btm_bss_termination:1, + btm_mbo_retry:1, + reassoc_rssi_reject:1, + no_more_stas:1; + }; + uint32_t reject_ap_reason; + }; + enum blm_reject_ap_source source; + qdf_time_t connect_timestamp; +}; + +/** + * enum blm_bssid_action - action taken by driver for the scan results + * @BLM_ACTION_NOP: No operation to be taken for the BSSID in the scan list. + * @BLM_REMOVE_FROM_LIST: Remove the BSSID from the scan list ( Blacklisted APs) + * @BLM_MOVE_AT_LAST: Attach the Ap at last of the scan list (Avoided Aps) + */ +enum blm_bssid_action { + BLM_ACTION_NOP, + BLM_REMOVE_FROM_LIST, + BLM_MOVE_AT_LAST, +}; + +/** + * blm_filter_bssid() - Filter out the bad Aps from the scan list. + * @pdev: Pdev object + * @scan_list: Scan list from the caller + * + * This API will filter out the bad Aps, or add the bad APs at the last + * of the linked list if the APs are to be avoided. + * + * Return: QDF status + */ +QDF_STATUS +blm_filter_bssid(struct wlan_objmgr_pdev *pdev, qdf_list_t *scan_list); + +/** + * blm_add_bssid_to_reject_list() - Add BSSID to the specific reject list. + * @pdev: Pdev object + * @ap_info: Ap info params such as BSSID, and the type of rejection to be done + * + * This API will add the BSSID to the reject AP list maintained by the blacklist + * manager. + * + * Return: QDF status + */ +QDF_STATUS +blm_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_info *ap_info); + +/** + * blm_send_reject_ap_list_to_fw() - Send the blacklist BSSIDs to FW + * @pdev: Pdev object + * @reject_db_list: List of blacklist BSSIDs + * @cfg: Blacklist manager cfg + * + * This API will send the blacklist BSSIDs to FW for avoiding or blacklisting + * in roaming scenarios. + * + * Return: None + */ +void +blm_send_reject_ap_list_to_fw(struct wlan_objmgr_pdev *pdev, + qdf_list_t *reject_db_list, + struct blm_config *cfg); + +/** + * blm_update_reject_ap_list_to_fw() - Send the blacklist BSSIDs to FW + * @psoc: psoc object + * + * This API will send the blacklist BSSIDs to FW. + * + * Return: None + */ +void blm_update_reject_ap_list_to_fw(struct wlan_objmgr_psoc *psoc); + +/** + * blm_add_userspace_black_list() - Clear already existing userspace BSSID, and + * add the new ones to blacklist manager. + * @pdev: pdev object + * @bssid_black_list: BSSIDs to be blacklisted by userspace. + * @num_of_bssid: num of bssids to be blacklisted. + * + * This API will Clear already existing userspace BSSID, and add the new ones + * to blacklist manager's reject list. + * + * Return: QDF status + */ +QDF_STATUS +blm_add_userspace_black_list(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr *bssid_black_list, + uint8_t num_of_bssid); + +/** + * blm_update_bssid_connect_params() - Inform the BLM about connect/disconnect + * with the current AP. + * @pdev: pdev object + * @bssid: BSSID of the AP + * @con_state: Connection stae (connected/disconnected) + * + * This API will inform the BLM about the state with the AP so that if the AP + * is selected, and the connection went through, and the connection did not + * face any data stall till the bad bssid reset timer, BLM can remove the + * AP from the reject ap list maintained by it. + * + * Return: None + */ +void +blm_update_bssid_connect_params(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr bssid, + enum blm_connection_state con_state); + +/** + * blm_flush_reject_ap_list() - Clear away BSSID and destroy the reject ap list + * @blm_ctx: blacklist manager pdev priv object + * + * This API will clear the BSSID info in the reject AP list maintained by the + * blacklist manager, and will destroy the list as well. + * + * Return: None + */ +void +blm_flush_reject_ap_list(struct blm_pdev_priv_obj *blm_ctx); + +/** + * blm_get_bssid_reject_list() - Get the BSSIDs in reject list from BLM + * @pdev: pdev object + * @reject_list: reject list to be filled (passed by caller) + * @max_bssid_to_be_filled: num of bssids filled in reject list by BLM + * @reject_ap_type: reject ap type of the BSSIDs to be filled. + * + * This API will fill the reject ap list requested by caller of type given as + * argument reject_ap_type, and will return the number of BSSIDs filled. + * + * Return: Unsigned integer (number of BSSIDs filled by the blacklist manager) + */ +uint8_t +blm_get_bssid_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_config_params *reject_list, + uint8_t max_bssid_to_be_filled, + enum blm_reject_ap_type reject_ap_type); + +/** + * blm_get_rssi_blacklist_threshold() - Get rssi blacklist threshold value + * @pdev: pdev object + * + * This API will get the RSSI blacklist threshold info. + * + * Return: rssi theshold value + */ +int32_t +blm_get_rssi_blacklist_threshold(struct wlan_objmgr_pdev *pdev); +#endif diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/inc/wlan_blm_main.h b/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/inc/wlan_blm_main.h new file mode 100644 index 0000000000000000000000000000000000000000..fdcda80b87dcd5c9ce5d4969cf24a556b1bcd732 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/inc/wlan_blm_main.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare internal APIs related to the blacklist manager component + */ + +#ifndef _WLAN_BLM_MAIN_H_ +#define _WLAN_BLM_MAIN_H_ + +#include +#include +#include +#include + +#define blm_fatal(params...)\ + QDF_TRACE_FATAL(QDF_MODULE_ID_BLACKLIST_MGR, params) +#define blm_err(params...)\ + QDF_TRACE_ERROR(QDF_MODULE_ID_BLACKLIST_MGR, params) +#define blm_warn(params...)\ + QDF_TRACE_WARN(QDF_MODULE_ID_BLACKLIST_MGR, params) +#define blm_info(params...)\ + QDF_TRACE_INFO(QDF_MODULE_ID_BLACKLIST_MGR, params) +#define blm_debug(params...)\ + QDF_TRACE_DEBUG(QDF_MODULE_ID_BLACKLIST_MGR, params) + +/** + * struct blm_pdev_priv_obj - Pdev priv struct to store list of blacklist mgr. + * @reject_ap_list_lock: Mutex needed to restrict two threads updating the list. + * @reject_ap_list: The reject Ap list which would contain the list of bad APs. + * @blm_tx_ops: tx ops to send reject ap list to FW + */ +struct blm_pdev_priv_obj { + qdf_mutex_t reject_ap_list_lock; + qdf_list_t reject_ap_list; + struct wlan_blm_tx_ops blm_tx_ops; +}; + +/** + * struct blm_config - Structure to define the config params for blacklist mgr. + * @avoid_list_exipry_time: Timer after which transition from avoid->monitor + * would happen for the BSSID which is in avoid list. + * @black_list_exipry_time: Timer after which transition from black->monitor + * would happen for the BSSID which is in black list. + * @bad_bssid_counter_reset_time: Timer after which the bssid would be removed + * from the reject list when connected, and data stall is not seen with the AP. + * @bad_bssid_counter_thresh: This is the threshold count which is incremented + * after every NUD fail, and after this much count, the BSSID would be moved to + * blacklist. + * @delta_rssi: This is the rssi threshold, only when rssi + * improves by this value the entry for BSSID should be removed from black + * list manager list. + */ +struct blm_config { + qdf_time_t avoid_list_exipry_time; + qdf_time_t black_list_exipry_time; + qdf_time_t bad_bssid_counter_reset_time; + uint8_t bad_bssid_counter_thresh; + uint32_t delta_rssi; +}; + +/** + * struct blm_psoc_priv_obj - Psoc priv structure of the blacklist manager. + * @pdev_id: pdev id + * @is_suspended: is black list manager state suspended + * @blm_cfg: These are the config ini params that the user can configure. + */ +struct blm_psoc_priv_obj { + uint8_t pdev_id; + bool is_suspended; + struct blm_config blm_cfg; +}; + +/** + * blm_pdev_object_created_notification() - blacklist mgr pdev create + * handler + * @pdev: pdev which is going to be created by objmgr + * @arg: argument for pdev create handler + * + * Register this api with objmgr to detect if pdev is created. + * + * Return: QDF_STATUS status in case of success else return error + */ +QDF_STATUS +blm_pdev_object_created_notification(struct wlan_objmgr_pdev *pdev, + void *arg); + +/** + * blm_pdev_object_destroyed_notification() - blacklist mgr pdev delete handler + * @pdev: pdev which is going to be deleted by objmgr + * @arg: argument for pdev delete handler + * + * Register this api with objmgr to detect if pdev is deleted. + * + * Return: QDF_STATUS status in case of success else return error + */ +QDF_STATUS +blm_pdev_object_destroyed_notification(struct wlan_objmgr_pdev *pdev, + void *arg); + +/** + * blm_psoc_object_created_notification() - blacklist mgr psoc create handler + * @psoc: psoc which is going to be created by objmgr + * @arg: argument for psoc create handler + * + * Register this api with objmgr to detect if psoc is created. + * + * Return: QDF_STATUS status in case of success else return error + */ +QDF_STATUS +blm_psoc_object_created_notification(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * blm_psoc_object_destroyed_notification() - blacklist mgr psoc delete handler + * @psoc: psoc which is going to be deleted by objmgr + * @arg: argument for psoc delete handler. + * + * Register this api with objmgr to detect if psoc is deleted. + * + * Return: QDF_STATUS status in case of success else return error + */ +QDF_STATUS +blm_psoc_object_destroyed_notification(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * blm_cfg_psoc_open() - blacklist mgr psoc open handler + * @psoc: psoc which is initialized by objmgr + * + * This API will initialize the config file, and store the config while in the + * psoc priv object of the blacklist manager. + * + * Return: QDF_STATUS status in case of success else return error + */ +QDF_STATUS +blm_cfg_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * blm_get_pdev_obj() - Get the pdev priv object of the blacklist manager + * @pdev: pdev object + * + * Get the pdev priv object of the blacklist manager + * + * Return: Pdev priv object if present, else NULL. + */ +struct blm_pdev_priv_obj * +blm_get_pdev_obj(struct wlan_objmgr_pdev *pdev); + +/** + * blm_get_psoc_obj() - Get the psoc priv object of the blacklist manager + * @psoc: psoc object + * + * Get the psoc priv object of the blacklist manager + * + * Return: Psoc priv object if present, else NULL. + */ +struct blm_psoc_priv_obj * +blm_get_psoc_obj(struct wlan_objmgr_psoc *psoc); + +#endif diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/src/wlan_blm_core.c b/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/src/wlan_blm_core.c new file mode 100644 index 0000000000000000000000000000000000000000..d228984dd611e8460d4be220726156fedaa80af7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/src/wlan_blm_core.c @@ -0,0 +1,1374 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare internal APIs related to the blacklist component + */ + +#include +#include +#include +#include +#include +#include "wlan_blm_tgt_api.h" + +#define SECONDS_TO_MS(params) (params * 1000) +#define MINUTES_TO_MS(params) (SECONDS_TO_MS(params) * 60) +#define RSSI_TIMEOUT_VALUE 60 + +static void +blm_update_ap_info(struct blm_reject_ap *blm_entry, struct blm_config *cfg, + struct scan_cache_entry *scan_entry) +{ + qdf_time_t cur_timestamp = qdf_mc_timer_get_system_time(); + qdf_time_t entry_add_time = 0; + bool update_done = false; + uint8_t old_reject_ap_type; + + old_reject_ap_type = blm_entry->reject_ap_type; + + if (BLM_IS_AP_AVOIDED_BY_USERSPACE(blm_entry)) { + entry_add_time = + blm_entry->ap_timestamp.userspace_avoid_timestamp; + + if ((cur_timestamp - entry_add_time) >= + MINUTES_TO_MS(cfg->avoid_list_exipry_time)) { + /* Move AP to monitor list as avoid list time is over */ + blm_entry->userspace_avoidlist = false; + blm_entry->avoid_userspace = false; + blm_entry->driver_monitorlist = true; + + blm_entry->ap_timestamp.driver_monitor_timestamp = + cur_timestamp; + blm_debug("Userspace avoid list timer expired, moved to monitor list"); + update_done = true; + } + } + + if (BLM_IS_AP_AVOIDED_BY_DRIVER(blm_entry)) { + entry_add_time = blm_entry->ap_timestamp.driver_avoid_timestamp; + + if ((cur_timestamp - entry_add_time) >= + MINUTES_TO_MS(cfg->avoid_list_exipry_time)) { + /* Move AP to monitor list as avoid list time is over */ + blm_entry->driver_avoidlist = false; + blm_entry->nud_fail = false; + blm_entry->sta_kickout = false; + blm_entry->ho_fail = false; + blm_entry->driver_monitorlist = true; + + blm_entry->ap_timestamp.driver_monitor_timestamp = + cur_timestamp; + blm_debug("Driver avoid list timer expired, moved to monitor list"); + update_done = true; + } + } + + if (BLM_IS_AP_BLACKLISTED_BY_DRIVER(blm_entry)) { + entry_add_time = + blm_entry->ap_timestamp.driver_blacklist_timestamp; + + if ((cur_timestamp - entry_add_time) >= + MINUTES_TO_MS(cfg->black_list_exipry_time)) { + /* Move AP to monitor list as black list time is over */ + blm_entry->driver_blacklist = false; + blm_entry->driver_monitorlist = true; + blm_entry->nud_fail = false; + blm_entry->sta_kickout = false; + blm_entry->ho_fail = false; + blm_entry->ap_timestamp.driver_monitor_timestamp = + cur_timestamp; + blm_debug("Driver blacklist timer expired, moved to monitor list"); + update_done = true; + } + } + + if (BLM_IS_AP_IN_RSSI_REJECT_LIST(blm_entry)) { + qdf_time_t entry_age = cur_timestamp - + blm_entry->ap_timestamp.rssi_reject_timestamp; + + if ((blm_entry->rssi_reject_params.retry_delay && + entry_age >= blm_entry->rssi_reject_params.retry_delay) || + (scan_entry && (scan_entry->rssi_raw >= blm_entry-> + rssi_reject_params.expected_rssi))) { + /* + * Remove from the rssi reject list as:- + * 1. In case of OCE reject, both the time, and RSSI + * param are present, and one of them have improved + * now, so the STA can now connect to the AP. + * + * 2. In case of BTM message received from the FW, + * the STA just needs to wait for a certain time, + * hence RSSI is not a restriction (MIN RSSI needed + * in that case is filled as 0). + * Hence the above check will still pass, if BTM + * delay is over, and will fail is not. RSSI check + * for BTM message will fail (expected), as BTM does + * not care about the same. + */ + blm_entry->poor_rssi = false; + blm_entry->oce_assoc_reject = false; + blm_entry->btm_bss_termination = false; + blm_entry->btm_disassoc_imminent = false; + blm_entry->btm_mbo_retry = false; + blm_entry->no_more_stas = false; + blm_entry->reassoc_rssi_reject = false; + blm_entry->rssi_reject_list = false; + blm_debug("Remove BSSID from rssi reject expected RSSI = %d, current RSSI = %d, retry delay required = %d ms, delay = %lu ms", + blm_entry->rssi_reject_params.expected_rssi, + scan_entry ? scan_entry->rssi_raw : 0, + blm_entry->rssi_reject_params.retry_delay, + entry_age); + update_done = true; + } + } + + if (!update_done) + return; + + blm_debug(QDF_MAC_ADDR_FMT" Old %d Updated reject ap type = %x", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes), + old_reject_ap_type, + blm_entry->reject_ap_type); +} + +#define MAX_BL_TIME 255000 + +static enum blm_bssid_action +blm_prune_old_entries_and_get_action(struct blm_reject_ap *blm_entry, + struct blm_config *cfg, + struct scan_cache_entry *entry, + qdf_list_t *reject_ap_list) +{ + blm_update_ap_info(blm_entry, cfg, entry); + + /* + * If all entities have cleared the bits of reject ap type, then + * the AP is not needed in the database,(reject_ap_type should be 0), + * then remove the entry from the reject ap list. + */ + if (!blm_entry->reject_ap_type) { + blm_debug(QDF_MAC_ADDR_FMT" cleared from list", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes)); + qdf_list_remove_node(reject_ap_list, &blm_entry->node); + qdf_mem_free(blm_entry); + return BLM_ACTION_NOP; + } + + if (BLM_IS_AP_IN_RSSI_REJECT_LIST(blm_entry) && + !blm_entry->userspace_blacklist && !blm_entry->driver_blacklist && + blm_entry->rssi_reject_params.original_timeout > MAX_BL_TIME) { + blm_info("Allow BSSID "QDF_MAC_ADDR_FMT" as the retry delay is greater than %u ms, expected RSSI = %d, current RSSI = %d, retry delay = %u ms original timeout %u time added %lu source %d reason %d", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes), MAX_BL_TIME, + blm_entry->rssi_reject_params.expected_rssi, + entry ? entry->rssi_raw : 0, + blm_entry->rssi_reject_params.retry_delay, + blm_entry->rssi_reject_params.original_timeout, + blm_entry->rssi_reject_params.received_time, + blm_entry->source, blm_entry->reject_ap_reason); + + if (BLM_IS_AP_IN_AVOIDLIST(blm_entry)) { + blm_debug(QDF_MAC_ADDR_FMT" in avoid list, deprioritize it", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes)); + return BLM_MOVE_AT_LAST; + } + + return BLM_ACTION_NOP; + } + if (BLM_IS_AP_IN_BLACKLIST(blm_entry)) { + blm_debug(QDF_MAC_ADDR_FMT" in blacklist list, reject ap type %d removing from candidate list", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes), + blm_entry->reject_ap_type); + return BLM_REMOVE_FROM_LIST; + } + + if (BLM_IS_AP_IN_AVOIDLIST(blm_entry)) { + blm_debug(QDF_MAC_ADDR_FMT" in avoid list, deprioritize it", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes)); + return BLM_MOVE_AT_LAST; + } + + return BLM_ACTION_NOP; +} + +static enum blm_bssid_action +blm_action_on_bssid(struct wlan_objmgr_pdev *pdev, + struct scan_cache_entry *entry) +{ + struct blm_pdev_priv_obj *blm_ctx; + struct blm_psoc_priv_obj *blm_psoc_obj; + struct blm_config *cfg; + struct blm_reject_ap *blm_entry = NULL; + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + QDF_STATUS status; + enum blm_bssid_action action = BLM_ACTION_NOP; + + blm_ctx = blm_get_pdev_obj(pdev); + blm_psoc_obj = blm_get_psoc_obj(wlan_pdev_get_psoc(pdev)); + if (!blm_ctx || !blm_psoc_obj) { + blm_err("blm_ctx or blm_psoc_obj is NULL"); + return BLM_ACTION_NOP; + } + + status = qdf_mutex_acquire(&blm_ctx->reject_ap_list_lock); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("failed to acquire reject_ap_list_lock"); + return BLM_ACTION_NOP; + } + + cfg = &blm_psoc_obj->blm_cfg; + + qdf_list_peek_front(&blm_ctx->reject_ap_list, &cur_node); + + while (cur_node) { + qdf_list_peek_next(&blm_ctx->reject_ap_list, cur_node, + &next_node); + + blm_entry = qdf_container_of(cur_node, struct blm_reject_ap, + node); + + if (qdf_is_macaddr_equal(&blm_entry->bssid, &entry->bssid)) { + action = blm_prune_old_entries_and_get_action(blm_entry, + cfg, entry, &blm_ctx->reject_ap_list); + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + return action; + } + cur_node = next_node; + next_node = NULL; + } + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + + return BLM_ACTION_NOP; +} + +static void +blm_modify_scan_list(qdf_list_t *scan_list, + struct scan_cache_node *scan_node, + enum blm_bssid_action action) +{ + blm_debug(QDF_MAC_ADDR_FMT" Action %d", + QDF_MAC_ADDR_REF(scan_node->entry->bssid.bytes), action); + + switch (action) { + case BLM_REMOVE_FROM_LIST: + qdf_list_remove_node(scan_list, &scan_node->node); + util_scan_free_cache_entry(scan_node->entry); + qdf_mem_free(scan_node); + break; + + case BLM_MOVE_AT_LAST: + qdf_list_remove_node(scan_list, &scan_node->node); + qdf_list_insert_back(scan_list, &scan_node->node); + scan_node->entry->bss_score = 0; + break; + + default: + break; + } +} + +QDF_STATUS +blm_filter_bssid(struct wlan_objmgr_pdev *pdev, qdf_list_t *scan_list) +{ + struct scan_cache_node *scan_node = NULL; + uint32_t scan_list_size; + enum blm_bssid_action action; + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + struct scan_cache_node *force_connect_candidate = NULL; + bool are_all_candidate_blacklisted = true; + + if (!scan_list || !qdf_list_size(scan_list)) { + blm_debug("Scan list is NULL or No BSSIDs present"); + return QDF_STATUS_E_EMPTY; + } + + scan_list_size = qdf_list_size(scan_list); + qdf_list_peek_front(scan_list, &cur_node); + + while (cur_node && scan_list_size) { + qdf_list_peek_next(scan_list, cur_node, &next_node); + + scan_node = qdf_container_of(cur_node, struct scan_cache_node, + node); + action = blm_action_on_bssid(pdev, scan_node->entry); + + if (are_all_candidate_blacklisted && + (action == BLM_ACTION_NOP || + action == BLM_MOVE_AT_LAST)) + are_all_candidate_blacklisted = false; + + /* + * The below logic is added to select the best candidate + * amongst the blacklisted candidates. This is done to + * handle a case where all the BSSIDs become blacklisted + * and hence there are continuous connection failures. + * With the below logic if the action on BSSID is to remove + * then we keep a backup node and restore the candidate + * list. + */ + if (action == BLM_REMOVE_FROM_LIST && + are_all_candidate_blacklisted) { + if (!force_connect_candidate) { + force_connect_candidate = + qdf_mem_malloc( + sizeof(*force_connect_candidate)); + if (!force_connect_candidate) + return QDF_STATUS_E_NOMEM; + force_connect_candidate->entry = + util_scan_copy_cache_entry(scan_node->entry); + if (!force_connect_candidate->entry) + return QDF_STATUS_E_NOMEM; + } else if (scan_node->entry->bss_score > + force_connect_candidate->entry->bss_score) { + util_scan_free_cache_entry( + force_connect_candidate->entry); + force_connect_candidate->entry = + util_scan_copy_cache_entry(scan_node->entry); + if (!force_connect_candidate->entry) + return QDF_STATUS_E_NOMEM; + } + } + if (action != BLM_ACTION_NOP) + blm_modify_scan_list(scan_list, scan_node, action); + cur_node = next_node; + next_node = NULL; + scan_list_size--; + } + + if (are_all_candidate_blacklisted && force_connect_candidate) { + qdf_list_insert_front(scan_list, + &force_connect_candidate->node); + } else if (force_connect_candidate) { + util_scan_free_cache_entry(force_connect_candidate->entry); + qdf_mem_free(force_connect_candidate); + } + + return QDF_STATUS_SUCCESS; +} + +static void +blm_update_avoidlist_reject_reason(struct blm_reject_ap *entry, + enum blm_reject_ap_reason reject_reason) +{ + entry->nud_fail = false; + entry->sta_kickout = false; + entry->ho_fail = false; + + switch(reject_reason) { + case REASON_NUD_FAILURE: + entry->nud_fail = true; + break; + case REASON_STA_KICKOUT: + entry->sta_kickout = true; + break; + case REASON_ROAM_HO_FAILURE: + entry->ho_fail = true; + break; + default: + blm_err("Invalid reason passed %d", reject_reason); + } +} + +static void +blm_handle_avoid_list(struct blm_reject_ap *entry, + struct blm_config *cfg, + struct reject_ap_info *ap_info) +{ + qdf_time_t cur_timestamp = qdf_mc_timer_get_system_time(); + + if (ap_info->reject_ap_type == USERSPACE_AVOID_TYPE) { + entry->userspace_avoidlist = true; + entry->avoid_userspace = true; + entry->ap_timestamp.userspace_avoid_timestamp = cur_timestamp; + } else if (ap_info->reject_ap_type == DRIVER_AVOID_TYPE) { + entry->driver_avoidlist = true; + blm_update_avoidlist_reject_reason(entry, + ap_info->reject_reason); + entry->ap_timestamp.driver_avoid_timestamp = cur_timestamp; + } else { + return; + } + entry->source = ap_info->source; + /* Update bssid info for new entry */ + entry->bssid = ap_info->bssid; + + /* Clear the monitor list bit if the AP was present in monitor list */ + entry->driver_monitorlist = false; + + /* Increment bad bssid counter as NUD failure happenend with this ap */ + entry->bad_bssid_counter++; + + /* If bad bssid counter has reached threshold, move it to blacklist */ + if (entry->bad_bssid_counter >= cfg->bad_bssid_counter_thresh) { + if (ap_info->reject_ap_type == USERSPACE_AVOID_TYPE) + entry->userspace_avoidlist = false; + else if (ap_info->reject_ap_type == DRIVER_AVOID_TYPE) + entry->driver_avoidlist = false; + + /* Move AP to blacklist list */ + entry->driver_blacklist = true; + entry->ap_timestamp.driver_blacklist_timestamp = cur_timestamp; + + blm_debug(QDF_MAC_ADDR_FMT" moved to black list with counter %d", + QDF_MAC_ADDR_REF(entry->bssid.bytes), entry->bad_bssid_counter); + return; + } + blm_debug("Added "QDF_MAC_ADDR_FMT" to avoid list type %d, counter %d reason %d updated reject reason %d source %d", + QDF_MAC_ADDR_REF(entry->bssid.bytes), ap_info->reject_ap_type, + entry->bad_bssid_counter, ap_info->reject_reason, + entry->reject_ap_reason, entry->source); + + entry->connect_timestamp = qdf_mc_timer_get_system_time(); +} + +static void +blm_handle_blacklist(struct blm_reject_ap *entry, + struct reject_ap_info *ap_info) +{ + /* + * No entity will blacklist an AP internal to driver, so only + * userspace blacklist is the case to be taken care. Driver blacklist + * will only happen when the bad bssid counter has reached the max + * threshold. + */ + entry->bssid = ap_info->bssid; + entry->userspace_blacklist = true; + entry->ap_timestamp.userspace_blacklist_timestamp = + qdf_mc_timer_get_system_time(); + + entry->source = ADDED_BY_DRIVER; + entry->blacklist_userspace = true; + blm_debug(QDF_MAC_ADDR_FMT" added to userspace blacklist", + QDF_MAC_ADDR_REF(entry->bssid.bytes)); +} + +static void +blm_update_rssi_reject_reason(struct blm_reject_ap *entry, + enum blm_reject_ap_reason reject_reason) +{ + entry->poor_rssi = false; + entry->oce_assoc_reject = false; + entry->btm_bss_termination = false; + entry->btm_disassoc_imminent = false; + entry->btm_mbo_retry = false; + entry->no_more_stas = false; + entry->reassoc_rssi_reject = false; + + switch(reject_reason) { + case REASON_ASSOC_REJECT_POOR_RSSI: + entry->poor_rssi = true; + break; + case REASON_ASSOC_REJECT_OCE: + entry->oce_assoc_reject = true; + break; + case REASON_BTM_DISASSOC_IMMINENT: + entry->btm_disassoc_imminent = true; + break; + case REASON_BTM_BSS_TERMINATION: + entry->btm_bss_termination = true; + break; + case REASON_BTM_MBO_RETRY: + entry->btm_mbo_retry = true; + break; + case REASON_REASSOC_RSSI_REJECT: + entry->reassoc_rssi_reject = true; + break; + case REASON_REASSOC_NO_MORE_STAS: + entry->no_more_stas = true; + break; + default: + blm_err("Invalid reason passed %d", reject_reason); + } +} + +static void +blm_handle_rssi_reject_list(struct blm_reject_ap *entry, + struct reject_ap_info *ap_info) +{ + bool bssid_newly_added; + + if (entry->rssi_reject_list) { + bssid_newly_added = false; + } else { + entry->rssi_reject_params.source = ap_info->source; + entry->bssid = ap_info->bssid; + entry->rssi_reject_list = true; + bssid_newly_added = true; + } + + entry->ap_timestamp.rssi_reject_timestamp = + qdf_mc_timer_get_system_time(); + entry->rssi_reject_params = ap_info->rssi_reject_params; + blm_update_rssi_reject_reason(entry, ap_info->reject_reason); + blm_info(QDF_MAC_ADDR_FMT" %s to rssi reject list, expected RSSI %d retry delay %u source %d original timeout %u received time %lu reject reason %d updated reason %d", + QDF_MAC_ADDR_REF(entry->bssid.bytes), + bssid_newly_added ? "ADDED" : "UPDATED", + entry->rssi_reject_params.expected_rssi, + entry->rssi_reject_params.retry_delay, + entry->rssi_reject_params.source, + entry->rssi_reject_params.original_timeout, + entry->rssi_reject_params.received_time, + ap_info->reject_reason, entry->reject_ap_reason); +} + +static void +blm_modify_entry(struct blm_reject_ap *entry, struct blm_config *cfg, + struct reject_ap_info *ap_info) +{ + /* Modify the entry according to the ap_info */ + switch (ap_info->reject_ap_type) { + case USERSPACE_AVOID_TYPE: + case DRIVER_AVOID_TYPE: + blm_handle_avoid_list(entry, cfg, ap_info); + break; + case USERSPACE_BLACKLIST_TYPE: + blm_handle_blacklist(entry, ap_info); + break; + case DRIVER_RSSI_REJECT_TYPE: + blm_handle_rssi_reject_list(entry, ap_info); + break; + default: + blm_debug("Invalid input of ap type %d", + ap_info->reject_ap_type); + } +} + +static bool +blm_is_bssid_present_only_in_list_type(enum blm_reject_ap_type list_type, + struct blm_reject_ap *blm_entry) +{ + switch (list_type) { + case USERSPACE_AVOID_TYPE: + return IS_AP_IN_USERSPACE_AVOID_LIST_ONLY(blm_entry); + case USERSPACE_BLACKLIST_TYPE: + return IS_AP_IN_USERSPACE_BLACKLIST_ONLY(blm_entry); + case DRIVER_AVOID_TYPE: + return IS_AP_IN_DRIVER_AVOID_LIST_ONLY(blm_entry); + case DRIVER_BLACKLIST_TYPE: + return IS_AP_IN_DRIVER_BLACKLIST_ONLY(blm_entry); + case DRIVER_RSSI_REJECT_TYPE: + return IS_AP_IN_RSSI_REJECT_LIST_ONLY(blm_entry); + case DRIVER_MONITOR_TYPE: + return IS_AP_IN_MONITOR_LIST_ONLY(blm_entry); + default: + blm_debug("Wrong list type %d passed", list_type); + return false; + } +} + +static bool +blm_is_bssid_of_type(enum blm_reject_ap_type reject_ap_type, + struct blm_reject_ap *blm_entry) +{ + switch (reject_ap_type) { + case USERSPACE_AVOID_TYPE: + return BLM_IS_AP_AVOIDED_BY_USERSPACE(blm_entry); + case USERSPACE_BLACKLIST_TYPE: + return BLM_IS_AP_BLACKLISTED_BY_USERSPACE(blm_entry); + case DRIVER_AVOID_TYPE: + return BLM_IS_AP_AVOIDED_BY_DRIVER(blm_entry); + case DRIVER_BLACKLIST_TYPE: + return BLM_IS_AP_BLACKLISTED_BY_DRIVER(blm_entry); + case DRIVER_RSSI_REJECT_TYPE: + return BLM_IS_AP_IN_RSSI_REJECT_LIST(blm_entry); + case DRIVER_MONITOR_TYPE: + return BLM_IS_AP_IN_MONITOR_LIST(blm_entry); + default: + blm_err("Wrong list type %d passed", reject_ap_type); + return false; + } +} + +static qdf_time_t +blm_get_delta_of_bssid(enum blm_reject_ap_type list_type, + struct blm_reject_ap *blm_entry, + struct blm_config *cfg) +{ + qdf_time_t cur_timestamp = qdf_mc_timer_get_system_time(); + int32_t disallowed_time; + /* + * For all the list types, delta would be the entry age only. Hence the + * oldest entry would be removed first in case of list is full, and the + * driver needs to make space for newer entries. + */ + + switch (list_type) { + case USERSPACE_AVOID_TYPE: + return MINUTES_TO_MS(cfg->avoid_list_exipry_time) - + (cur_timestamp - + blm_entry->ap_timestamp.userspace_avoid_timestamp); + case USERSPACE_BLACKLIST_TYPE: + return cur_timestamp - + blm_entry->ap_timestamp.userspace_blacklist_timestamp; + case DRIVER_AVOID_TYPE: + return MINUTES_TO_MS(cfg->avoid_list_exipry_time) - + (cur_timestamp - + blm_entry->ap_timestamp.driver_avoid_timestamp); + case DRIVER_BLACKLIST_TYPE: + return MINUTES_TO_MS(cfg->black_list_exipry_time) - + (cur_timestamp - + blm_entry->ap_timestamp.driver_blacklist_timestamp); + + /* + * For RSSI reject lowest delta would be the BSSID whose retry delay + * is about to expire, hence the delta would be remaining duration for + * de-blacklisting the AP from rssi reject list. + */ + case DRIVER_RSSI_REJECT_TYPE: + if (blm_entry->rssi_reject_params.retry_delay) + disallowed_time = + blm_entry->rssi_reject_params.retry_delay - + (cur_timestamp - + blm_entry->ap_timestamp.rssi_reject_timestamp); + else + disallowed_time = (int32_t)(MINUTES_TO_MS(RSSI_TIMEOUT_VALUE) - + (cur_timestamp - + blm_entry->ap_timestamp.rssi_reject_timestamp)); + return ((disallowed_time < 0) ? 0 : disallowed_time); + case DRIVER_MONITOR_TYPE: + return cur_timestamp - + blm_entry->ap_timestamp.driver_monitor_timestamp; + default: + blm_debug("Wrong list type %d passed", list_type); + return 0; + } +} + +static bool +blm_is_oldest_entry(enum blm_reject_ap_type list_type, + qdf_time_t cur_node_delta, + qdf_time_t oldest_node_delta) +{ + switch (list_type) { + /* + * For RSSI reject, userspace avoid, driver avoid/blacklist type the + * lowest retry delay has to be found out hence if oldest_node_delta is + * 0, mean this is the first entry and thus return true, If + * oldest_node_delta is non zero, compare the delta and return true if + * the cur entry has lower retry delta. + */ + case DRIVER_RSSI_REJECT_TYPE: + case USERSPACE_AVOID_TYPE: + case DRIVER_AVOID_TYPE: + case DRIVER_BLACKLIST_TYPE: + if (!oldest_node_delta || (cur_node_delta < oldest_node_delta)) + return true; + break; + case USERSPACE_BLACKLIST_TYPE: + case DRIVER_MONITOR_TYPE: + if (cur_node_delta > oldest_node_delta) + return true; + break; + default: + blm_debug("Wrong list type passed %d", list_type); + return false; + } + + return false; +} + +static QDF_STATUS +blm_try_delete_bssid_in_list(qdf_list_t *reject_ap_list, + enum blm_reject_ap_type list_type, + struct blm_config *cfg) +{ + struct blm_reject_ap *blm_entry = NULL; + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + struct blm_reject_ap *oldest_blm_entry = NULL; + qdf_time_t oldest_node_delta = 0; + qdf_time_t cur_node_delta = 0; + + qdf_list_peek_front(reject_ap_list, &cur_node); + + while (cur_node) { + qdf_list_peek_next(reject_ap_list, cur_node, &next_node); + + blm_entry = qdf_container_of(cur_node, struct blm_reject_ap, + node); + + if (blm_is_bssid_present_only_in_list_type(list_type, + blm_entry)) { + cur_node_delta = blm_get_delta_of_bssid(list_type, + blm_entry, cfg); + + if (blm_is_oldest_entry(list_type, cur_node_delta, + oldest_node_delta)) { + /* now this is the oldest entry*/ + oldest_blm_entry = blm_entry; + oldest_node_delta = cur_node_delta; + } + } + cur_node = next_node; + next_node = NULL; + } + + if (oldest_blm_entry) { + /* Remove this entry to make space for the next entry */ + blm_debug("Removed "QDF_MAC_ADDR_FMT", type = %d", + QDF_MAC_ADDR_REF(oldest_blm_entry->bssid.bytes), + list_type); + qdf_list_remove_node(reject_ap_list, &oldest_blm_entry->node); + qdf_mem_free(oldest_blm_entry); + return QDF_STATUS_SUCCESS; + } + /* If the flow has reached here, that means no entry could be removed */ + + return QDF_STATUS_E_FAILURE; +} + +static QDF_STATUS +blm_remove_lowest_delta_entry(qdf_list_t *reject_ap_list, + struct blm_config *cfg) +{ + QDF_STATUS status; + + /* + * According to the Priority, the driver will try to remove the entries, + * as the least priority list, that is monitor list would not penalize + * the BSSIDs for connection. The priority order for the removal is:- + * 1. Monitor list + * 2. Driver avoid list + * 3. Userspace avoid list. + * 4. RSSI reject list. + * 5. Driver Blacklist. + * 6. Userspace Blacklist. + */ + + status = blm_try_delete_bssid_in_list(reject_ap_list, + DRIVER_MONITOR_TYPE, cfg); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + status = blm_try_delete_bssid_in_list(reject_ap_list, + DRIVER_AVOID_TYPE, cfg); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + status = blm_try_delete_bssid_in_list(reject_ap_list, + USERSPACE_AVOID_TYPE, cfg); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + status = blm_try_delete_bssid_in_list(reject_ap_list, + DRIVER_RSSI_REJECT_TYPE, cfg); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + status = blm_try_delete_bssid_in_list(reject_ap_list, + DRIVER_BLACKLIST_TYPE, cfg); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + status = blm_try_delete_bssid_in_list(reject_ap_list, + USERSPACE_BLACKLIST_TYPE, cfg); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + blm_debug("Failed to remove AP from blacklist manager"); + + return QDF_STATUS_E_FAILURE; +} + +static enum blm_reject_ap_reason +blm_get_rssi_reject_reason(struct blm_reject_ap *blm_entry) +{ + if (blm_entry->poor_rssi) + return REASON_ASSOC_REJECT_POOR_RSSI; + else if (blm_entry->oce_assoc_reject) + return REASON_ASSOC_REJECT_OCE; + else if(blm_entry->btm_bss_termination) + return REASON_BTM_BSS_TERMINATION; + else if (blm_entry->btm_disassoc_imminent) + return REASON_BTM_DISASSOC_IMMINENT; + else if (blm_entry->btm_mbo_retry) + return REASON_BTM_MBO_RETRY; + else if (blm_entry->no_more_stas) + return REASON_REASSOC_NO_MORE_STAS; + else if (blm_entry->reassoc_rssi_reject) + return REASON_REASSOC_RSSI_REJECT; + + return REASON_UNKNOWN; +} + +static void +blm_fill_rssi_reject_params(struct blm_reject_ap *blm_entry, + enum blm_reject_ap_type reject_ap_type, + struct reject_ap_config_params *blm_reject_list) +{ + if (reject_ap_type != DRIVER_RSSI_REJECT_TYPE) + return; + + blm_reject_list->source = blm_entry->rssi_reject_params.source; + blm_reject_list->original_timeout = + blm_entry->rssi_reject_params.original_timeout; + blm_reject_list->received_time = + blm_entry->rssi_reject_params.received_time; + blm_reject_list->reject_reason = blm_get_rssi_reject_reason(blm_entry); + blm_debug(QDF_MAC_ADDR_FMT" source %d original timeout %u received time %lu reject reason %d", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes), blm_reject_list->source, + blm_reject_list->original_timeout, + blm_reject_list->received_time, + blm_reject_list->reject_reason); +} + +static void blm_fill_reject_list(qdf_list_t *reject_db_list, + struct reject_ap_config_params *reject_list, + uint8_t *num_of_reject_bssid, + enum blm_reject_ap_type reject_ap_type, + uint8_t max_bssid_to_be_filled, + struct blm_config *cfg) +{ + struct blm_reject_ap *blm_entry = NULL; + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + + qdf_list_peek_front(reject_db_list, &cur_node); + while (cur_node) { + if (*num_of_reject_bssid == max_bssid_to_be_filled) { + blm_debug("Max size reached in list, reject_ap_type %d", + reject_ap_type); + return; + } + qdf_list_peek_next(reject_db_list, cur_node, &next_node); + + blm_entry = qdf_container_of(cur_node, struct blm_reject_ap, + node); + + blm_update_ap_info(blm_entry, cfg, NULL); + if (!blm_entry->reject_ap_type) { + blm_debug(QDF_MAC_ADDR_FMT" cleared from list", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes)); + qdf_list_remove_node(reject_db_list, &blm_entry->node); + qdf_mem_free(blm_entry); + cur_node = next_node; + next_node = NULL; + continue; + } + + if (blm_is_bssid_of_type(reject_ap_type, blm_entry)) { + struct reject_ap_config_params *blm_reject_list; + + blm_reject_list = &reject_list[*num_of_reject_bssid]; + blm_reject_list->expected_rssi = + blm_entry->rssi_reject_params.expected_rssi; + blm_reject_list->reject_duration = + blm_get_delta_of_bssid(reject_ap_type, blm_entry, + cfg); + + blm_fill_rssi_reject_params(blm_entry, reject_ap_type, + blm_reject_list); + blm_reject_list->reject_ap_type = reject_ap_type; + blm_reject_list->bssid = blm_entry->bssid; + (*num_of_reject_bssid)++; + blm_debug("Adding BSSID "QDF_MAC_ADDR_FMT" of type %d retry delay %d expected RSSI %d, entries added = %d reject reason %d", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes), + reject_ap_type, + reject_list[*num_of_reject_bssid -1].reject_duration, + blm_entry->rssi_reject_params.expected_rssi, + *num_of_reject_bssid, + blm_entry->reject_ap_reason); + } + cur_node = next_node; + next_node = NULL; + } +} + +void blm_update_reject_ap_list_to_fw(struct wlan_objmgr_psoc *psoc) +{ + struct blm_config *cfg; + struct wlan_objmgr_pdev *pdev; + struct blm_pdev_priv_obj *blm_ctx; + struct blm_psoc_priv_obj *blm_psoc_obj; + QDF_STATUS status; + + blm_psoc_obj = blm_get_psoc_obj(psoc); + if (!blm_psoc_obj) { + blm_err("BLM psoc obj NULL"); + return; + } + + pdev = wlan_objmgr_get_pdev_by_id(psoc, blm_psoc_obj->pdev_id, + WLAN_MLME_NB_ID); + if (!pdev) { + blm_err("pdev obj NULL"); + return; + } + + blm_ctx = blm_get_pdev_obj(pdev); + if (!blm_ctx) { + blm_err("BLM pdev obj NULL"); + goto end; + } + + status = qdf_mutex_acquire(&blm_ctx->reject_ap_list_lock); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("failed to acquire reject_ap_list_lock"); + goto end; + } + + cfg = &blm_psoc_obj->blm_cfg; + blm_send_reject_ap_list_to_fw(pdev, &blm_ctx->reject_ap_list, cfg); + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + +end: + wlan_objmgr_pdev_release_ref(pdev, WLAN_MLME_NB_ID); +} + +static void blm_store_pdevid_in_blm_psocpriv(struct wlan_objmgr_pdev *pdev) +{ + struct blm_psoc_priv_obj *blm_psoc_obj; + + blm_psoc_obj = blm_get_psoc_obj(wlan_pdev_get_psoc(pdev)); + + if (!blm_psoc_obj) { + blm_err("BLM psoc obj NULL"); + return; + } + + blm_psoc_obj->pdev_id = pdev->pdev_objmgr.wlan_pdev_id; +} + +void +blm_send_reject_ap_list_to_fw(struct wlan_objmgr_pdev *pdev, + qdf_list_t *reject_db_list, + struct blm_config *cfg) +{ + QDF_STATUS status; + bool is_blm_suspended; + struct reject_ap_params reject_params = {0}; + + ucfg_blm_psoc_get_suspended(wlan_pdev_get_psoc(pdev), + &is_blm_suspended); + if (is_blm_suspended) { + blm_store_pdevid_in_blm_psocpriv(pdev); + blm_debug("Failed to send reject AP list to FW as BLM is suspended"); + return; + } + + reject_params.bssid_list = + qdf_mem_malloc(sizeof(*reject_params.bssid_list) * + PDEV_MAX_NUM_BSSID_DISALLOW_LIST); + if (!reject_params.bssid_list) + return; + + /* The priority for filling is as below */ + blm_fill_reject_list(reject_db_list, reject_params.bssid_list, + &reject_params.num_of_reject_bssid, + USERSPACE_BLACKLIST_TYPE, + PDEV_MAX_NUM_BSSID_DISALLOW_LIST, cfg); + blm_fill_reject_list(reject_db_list, reject_params.bssid_list, + &reject_params.num_of_reject_bssid, + DRIVER_BLACKLIST_TYPE, + PDEV_MAX_NUM_BSSID_DISALLOW_LIST, cfg); + blm_fill_reject_list(reject_db_list, reject_params.bssid_list, + &reject_params.num_of_reject_bssid, + DRIVER_RSSI_REJECT_TYPE, + PDEV_MAX_NUM_BSSID_DISALLOW_LIST, cfg); + blm_fill_reject_list(reject_db_list, reject_params.bssid_list, + &reject_params.num_of_reject_bssid, + USERSPACE_AVOID_TYPE, + PDEV_MAX_NUM_BSSID_DISALLOW_LIST, cfg); + blm_fill_reject_list(reject_db_list, reject_params.bssid_list, + &reject_params.num_of_reject_bssid, + DRIVER_AVOID_TYPE, + PDEV_MAX_NUM_BSSID_DISALLOW_LIST, cfg); + + status = tgt_blm_send_reject_list_to_fw(pdev, &reject_params); + + if (QDF_IS_STATUS_ERROR(status)) + blm_err("failed to send the reject Ap list to FW"); + + qdf_mem_free(reject_params.bssid_list); +} + +QDF_STATUS +blm_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_info *ap_info) +{ + struct blm_pdev_priv_obj *blm_ctx; + struct blm_psoc_priv_obj *blm_psoc_obj; + struct blm_config *cfg; + struct blm_reject_ap *blm_entry; + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + QDF_STATUS status; + + blm_ctx = blm_get_pdev_obj(pdev); + blm_psoc_obj = blm_get_psoc_obj(wlan_pdev_get_psoc(pdev)); + + if (!blm_ctx || !blm_psoc_obj) { + blm_err("blm_ctx or blm_psoc_obj is NULL"); + return QDF_STATUS_E_INVAL; + } + + if (qdf_is_macaddr_zero(&ap_info->bssid) || + qdf_is_macaddr_group(&ap_info->bssid)) { + blm_err("Zero/Broadcast BSSID received, entry not added"); + return QDF_STATUS_E_INVAL; + } + + status = qdf_mutex_acquire(&blm_ctx->reject_ap_list_lock); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("failed to acquire reject_ap_list_lock"); + return status; + } + + cfg = &blm_psoc_obj->blm_cfg; + + qdf_list_peek_front(&blm_ctx->reject_ap_list, &cur_node); + + while (cur_node) { + qdf_list_peek_next(&blm_ctx->reject_ap_list, + cur_node, &next_node); + + blm_entry = qdf_container_of(cur_node, struct blm_reject_ap, + node); + + /* Update the AP info to the latest list first */ + blm_update_ap_info(blm_entry, cfg, NULL); + if (!blm_entry->reject_ap_type) { + blm_debug(QDF_MAC_ADDR_FMT" cleared from list", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes)); + qdf_list_remove_node(&blm_ctx->reject_ap_list, + &blm_entry->node); + qdf_mem_free(blm_entry); + cur_node = next_node; + next_node = NULL; + continue; + } + + if (qdf_is_macaddr_equal(&blm_entry->bssid, &ap_info->bssid)) { + blm_modify_entry(blm_entry, cfg, ap_info); + goto end; + } + + cur_node = next_node; + next_node = NULL; + } + + if (qdf_list_size(&blm_ctx->reject_ap_list) == MAX_BAD_AP_LIST_SIZE) { + /* List is FULL, need to delete entries */ + status = + blm_remove_lowest_delta_entry(&blm_ctx->reject_ap_list, + cfg); + + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + return status; + } + } + + blm_entry = qdf_mem_malloc(sizeof(*blm_entry)); + if (!blm_entry) { + blm_err("Memory allocation of node failed"); + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + return QDF_STATUS_E_FAILURE; + } + + qdf_list_insert_back(&blm_ctx->reject_ap_list, &blm_entry->node); + blm_modify_entry(blm_entry, cfg, ap_info); + +end: + blm_send_reject_ap_list_to_fw(pdev, &blm_ctx->reject_ap_list, cfg); + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +blm_clear_userspace_blacklist_info(struct wlan_objmgr_pdev *pdev) +{ + struct blm_pdev_priv_obj *blm_ctx; + struct blm_reject_ap *blm_entry; + QDF_STATUS status; + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + + blm_ctx = blm_get_pdev_obj(pdev); + if (!blm_ctx) { + blm_err("blm_ctx is NULL"); + return QDF_STATUS_E_INVAL; + } + + status = qdf_mutex_acquire(&blm_ctx->reject_ap_list_lock); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("failed to acquire reject_ap_list_lock"); + return QDF_STATUS_E_RESOURCES; + } + + qdf_list_peek_front(&blm_ctx->reject_ap_list, &cur_node); + + while (cur_node) { + qdf_list_peek_next(&blm_ctx->reject_ap_list, cur_node, + &next_node); + blm_entry = qdf_container_of(cur_node, struct blm_reject_ap, + node); + + if (IS_AP_IN_USERSPACE_BLACKLIST_ONLY(blm_entry)) { + blm_debug("removing bssid: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes)); + qdf_list_remove_node(&blm_ctx->reject_ap_list, + &blm_entry->node); + qdf_mem_free(blm_entry); + } else if (BLM_IS_AP_BLACKLISTED_BY_USERSPACE(blm_entry)) { + blm_debug("Clearing userspace blacklist bit for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes)); + blm_entry->userspace_blacklist = false; + blm_entry->blacklist_userspace = false; + } + cur_node = next_node; + next_node = NULL; + } + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +blm_add_userspace_black_list(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr *bssid_black_list, + uint8_t num_of_bssid) +{ + uint8_t i = 0; + struct reject_ap_info ap_info; + QDF_STATUS status; + struct blm_pdev_priv_obj *blm_ctx; + struct blm_psoc_priv_obj *blm_psoc_obj; + struct blm_config *cfg; + + blm_ctx = blm_get_pdev_obj(pdev); + blm_psoc_obj = blm_get_psoc_obj(wlan_pdev_get_psoc(pdev)); + + if (!blm_ctx || !blm_psoc_obj) { + blm_err("blm_ctx or blm_psoc_obj is NULL"); + return QDF_STATUS_E_INVAL; + } + + /* Clear all the info of APs already existing in BLM first */ + blm_clear_userspace_blacklist_info(pdev); + cfg = &blm_psoc_obj->blm_cfg; + + status = qdf_mutex_acquire(&blm_ctx->reject_ap_list_lock); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("failed to acquire reject_ap_list_lock"); + return status; + } + + blm_send_reject_ap_list_to_fw(pdev, &blm_ctx->reject_ap_list, cfg); + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + + if (!bssid_black_list || !num_of_bssid) { + blm_debug("Userspace blacklist/num of blacklist NULL"); + return QDF_STATUS_SUCCESS; + } + + for (i = 0; i < num_of_bssid; i++) { + ap_info.bssid = bssid_black_list[i]; + ap_info.reject_ap_type = USERSPACE_BLACKLIST_TYPE; + ap_info.source = ADDED_BY_DRIVER; + ap_info.reject_reason = REASON_USERSPACE_BL; + status = blm_add_bssid_to_reject_list(pdev, &ap_info); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("Failed to add bssid to userspace blacklist"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +void +blm_flush_reject_ap_list(struct blm_pdev_priv_obj *blm_ctx) +{ + struct blm_reject_ap *blm_entry = NULL; + QDF_STATUS status; + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + + status = qdf_mutex_acquire(&blm_ctx->reject_ap_list_lock); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("failed to acquire reject_ap_list_lock"); + return; + } + + qdf_list_peek_front(&blm_ctx->reject_ap_list, &cur_node); + + while (cur_node) { + qdf_list_peek_next(&blm_ctx->reject_ap_list, cur_node, + &next_node); + blm_entry = qdf_container_of(cur_node, struct blm_reject_ap, + node); + qdf_list_remove_node(&blm_ctx->reject_ap_list, + &blm_entry->node); + qdf_mem_free(blm_entry); + cur_node = next_node; + next_node = NULL; + } + + blm_debug("BLM reject ap list flushed"); + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); +} + +uint8_t +blm_get_bssid_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_config_params *reject_list, + uint8_t max_bssid_to_be_filled, + enum blm_reject_ap_type reject_ap_type) +{ + struct blm_pdev_priv_obj *blm_ctx; + struct blm_psoc_priv_obj *blm_psoc_obj; + uint8_t num_of_reject_bssid = 0; + QDF_STATUS status; + + blm_ctx = blm_get_pdev_obj(pdev); + blm_psoc_obj = blm_get_psoc_obj(wlan_pdev_get_psoc(pdev)); + + if (!blm_ctx || !blm_psoc_obj) { + blm_err("blm_ctx or blm_psoc_obj is NULL"); + return 0; + } + + status = qdf_mutex_acquire(&blm_ctx->reject_ap_list_lock); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("failed to acquire reject_ap_list_lock"); + return 0; + } + + blm_fill_reject_list(&blm_ctx->reject_ap_list, reject_list, + &num_of_reject_bssid, reject_ap_type, + max_bssid_to_be_filled, &blm_psoc_obj->blm_cfg); + + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + + return num_of_reject_bssid; +} + +void +blm_update_bssid_connect_params(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr bssid, + enum blm_connection_state con_state) +{ + struct blm_pdev_priv_obj *blm_ctx; + struct blm_psoc_priv_obj *blm_psoc_obj; + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + QDF_STATUS status; + struct blm_reject_ap *blm_entry = NULL; + qdf_time_t connection_age = 0; + bool entry_found = false; + qdf_time_t max_entry_time; + + blm_ctx = blm_get_pdev_obj(pdev); + blm_psoc_obj = blm_get_psoc_obj(wlan_pdev_get_psoc(pdev)); + + if (!blm_ctx || !blm_psoc_obj) { + blm_err("blm_ctx or blm_psoc_obj is NULL"); + return; + } + + status = qdf_mutex_acquire(&blm_ctx->reject_ap_list_lock); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("failed to acquire reject_ap_list_lock"); + return; + } + + qdf_list_peek_front(&blm_ctx->reject_ap_list, &cur_node); + + while (cur_node) { + qdf_list_peek_next(&blm_ctx->reject_ap_list, cur_node, + &next_node); + blm_entry = qdf_container_of(cur_node, struct blm_reject_ap, + node); + + if (!qdf_mem_cmp(blm_entry->bssid.bytes, bssid.bytes, + QDF_MAC_ADDR_SIZE)) { + blm_debug(QDF_MAC_ADDR_FMT" present in BLM reject list, updating connect info con_state = %d", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes), + con_state); + entry_found = true; + break; + } + cur_node = next_node; + next_node = NULL; + } + + /* This means that the BSSID was not added in the reject list of BLM */ + if (!entry_found) { + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); + return; + } + switch (con_state) { + case BLM_AP_CONNECTED: + blm_entry->connect_timestamp = qdf_mc_timer_get_system_time(); + break; + case BLM_AP_DISCONNECTED: + /* Update the blm info first */ + blm_update_ap_info(blm_entry, &blm_psoc_obj->blm_cfg, NULL); + + max_entry_time = blm_entry->connect_timestamp; + if (blm_entry->driver_blacklist) { + max_entry_time = + blm_entry->ap_timestamp.driver_blacklist_timestamp; + } else if (blm_entry->driver_avoidlist) { + max_entry_time = + QDF_MAX(blm_entry->ap_timestamp.driver_avoid_timestamp, + blm_entry->connect_timestamp); + } + connection_age = qdf_mc_timer_get_system_time() - + max_entry_time; + if ((connection_age > + SECONDS_TO_MS(blm_psoc_obj->blm_cfg. + bad_bssid_counter_reset_time))) { + blm_entry->driver_avoidlist = false; + blm_entry->driver_blacklist = false; + blm_entry->driver_monitorlist = false; + blm_entry->userspace_avoidlist = false; + blm_debug("updated reject ap type %d ", + blm_entry->reject_ap_type); + if (!blm_entry->reject_ap_type) { + blm_debug("Bad Bssid timer expired/AP cleared from all blacklisting, removed "QDF_MAC_ADDR_FMT" from list", + QDF_MAC_ADDR_REF(blm_entry->bssid.bytes)); + qdf_list_remove_node(&blm_ctx->reject_ap_list, + &blm_entry->node); + qdf_mem_free(blm_entry); + blm_send_reject_ap_list_to_fw(pdev, + &blm_ctx->reject_ap_list, + &blm_psoc_obj->blm_cfg); + } + } + break; + default: + blm_debug("Invalid AP connection state recevied %d", con_state); + }; + + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); +} + +int32_t blm_get_rssi_blacklist_threshold(struct wlan_objmgr_pdev *pdev) +{ + struct blm_pdev_priv_obj *blm_ctx; + struct blm_psoc_priv_obj *blm_psoc_obj; + struct blm_config *cfg; + + blm_ctx = blm_get_pdev_obj(pdev); + blm_psoc_obj = blm_get_psoc_obj(wlan_pdev_get_psoc(pdev)); + + if (!blm_ctx || !blm_psoc_obj) { + blm_err("blm_ctx or blm_psoc_obj is NULL"); + return 0; + } + + cfg = &blm_psoc_obj->blm_cfg; + return cfg->delta_rssi; +} + diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/src/wlan_blm_main.c b/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/src/wlan_blm_main.c new file mode 100644 index 0000000000000000000000000000000000000000..f965683c29d4e730ea1ce6edafaa476e4f799695 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/core/src/wlan_blm_main.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_blm_main.c + * + * WLAN Blacklist Mgr related APIs + * + */ + +/* Include files */ + +#include "target_if_blm.h" +#include +#include "cfg_ucfg_api.h" +#include + +struct blm_pdev_priv_obj * +blm_get_pdev_obj(struct wlan_objmgr_pdev *pdev) +{ + struct blm_pdev_priv_obj *blm_pdev_obj; + + blm_pdev_obj = wlan_objmgr_pdev_get_comp_private_obj(pdev, + WLAN_UMAC_COMP_BLACKLIST_MGR); + + return blm_pdev_obj; +} + +struct blm_psoc_priv_obj * +blm_get_psoc_obj(struct wlan_objmgr_psoc *psoc) +{ + struct blm_psoc_priv_obj *blm_psoc_obj; + + blm_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_BLACKLIST_MGR); + + return blm_psoc_obj; +} + +QDF_STATUS +blm_pdev_object_created_notification(struct wlan_objmgr_pdev *pdev, + void *arg) +{ + struct blm_pdev_priv_obj *blm_ctx; + QDF_STATUS status; + + blm_ctx = qdf_mem_malloc(sizeof(*blm_ctx)); + + if (!blm_ctx) + return QDF_STATUS_E_FAILURE; + + status = qdf_mutex_create(&blm_ctx->reject_ap_list_lock); + + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("Failed to create mutex"); + qdf_mem_free(blm_ctx); + return status; + } + qdf_list_create(&blm_ctx->reject_ap_list, MAX_BAD_AP_LIST_SIZE); + + target_if_blm_register_tx_ops(&blm_ctx->blm_tx_ops); + status = wlan_objmgr_pdev_component_obj_attach(pdev, + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_ctx, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("Failed to attach pdev_ctx with pdev"); + qdf_list_destroy(&blm_ctx->reject_ap_list); + qdf_mutex_destroy(&blm_ctx->reject_ap_list_lock); + qdf_mem_free(blm_ctx); + } + + return status; +} + +QDF_STATUS +blm_pdev_object_destroyed_notification(struct wlan_objmgr_pdev *pdev, + void *arg) +{ + struct blm_pdev_priv_obj *blm_ctx; + + blm_ctx = blm_get_pdev_obj(pdev); + + if (!blm_ctx) { + blm_err("BLM Pdev obj is NULL"); + return QDF_STATUS_E_FAILURE; + } + /* Clear away the memory allocated for the bad BSSIDs */ + blm_flush_reject_ap_list(blm_ctx); + qdf_list_destroy(&blm_ctx->reject_ap_list); + qdf_mutex_destroy(&blm_ctx->reject_ap_list_lock); + + wlan_objmgr_pdev_component_obj_detach(pdev, + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_ctx); + qdf_mem_free(blm_ctx); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +blm_psoc_object_created_notification(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + struct blm_psoc_priv_obj *blm_psoc_obj; + QDF_STATUS status; + + blm_psoc_obj = qdf_mem_malloc(sizeof(*blm_psoc_obj)); + + if (!blm_psoc_obj) + return QDF_STATUS_E_FAILURE; + + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_psoc_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("Failed to attach psoc_ctx with psoc"); + qdf_mem_free(blm_psoc_obj); + } + + return status; +} + +QDF_STATUS +blm_psoc_object_destroyed_notification(struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct blm_psoc_priv_obj *blm_psoc_obj; + + blm_psoc_obj = blm_get_psoc_obj(psoc); + + if (!blm_psoc_obj) { + blm_err("BLM psoc obj NULL"); + return QDF_STATUS_E_FAILURE; + } + wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_psoc_obj); + qdf_mem_free(blm_psoc_obj); + + return QDF_STATUS_SUCCESS; +} + +static void +blm_init_cfg(struct wlan_objmgr_psoc *psoc, struct blm_config *blm_cfg) +{ + blm_cfg->avoid_list_exipry_time = + cfg_get(psoc, CFG_AVOID_LIST_EXPIRY_TIME); + blm_cfg->black_list_exipry_time = + cfg_get(psoc, CFG_BLACK_LIST_EXPIRY_TIME); + blm_cfg->bad_bssid_counter_reset_time = + cfg_get(psoc, CFG_BAD_BSSID_RESET_TIME); + blm_cfg->bad_bssid_counter_thresh = + cfg_get(psoc, CFG_BAD_BSSID_COUNTER_THRESHOLD); + blm_cfg->delta_rssi = + cfg_get(psoc, CFG_BLACKLIST_RSSI_THRESHOLD); +} + +QDF_STATUS +blm_cfg_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + struct blm_psoc_priv_obj *blm_psoc_obj; + + blm_psoc_obj = blm_get_psoc_obj(psoc); + + if (!blm_psoc_obj) { + blm_err("BLM psoc obj NULL"); + return QDF_STATUS_E_FAILURE; + } + + blm_init_cfg(psoc, &blm_psoc_obj->blm_cfg); + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_api.h b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_api.h new file mode 100644 index 0000000000000000000000000000000000000000..75ba78d04ede206d8d96f9545364ddd487801029 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_api.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare public APIs exposed by the blacklist manager component + */ + +#ifndef _WLAN_BLM_API_H_ +#define _WLAN_BLM_API_H_ + +#include "qdf_types.h" +#include "wlan_objmgr_pdev_obj.h" +#include + +#ifdef FEATURE_BLACKLIST_MGR +#include "wlan_blm_core.h" + +/** + * wlan_blm_filter_bssid() - Wrapper API to blm_filter_bssid + * @pdev: pdev object + * @scan_list: scan list whose results are to filtered out. + * + * This API is a wrapper to blm_filter_bssid. + */ +static inline QDF_STATUS +wlan_blm_filter_bssid(struct wlan_objmgr_pdev *pdev, qdf_list_t *scan_list) +{ + return blm_filter_bssid(pdev, scan_list); +} + +/** + * wlan_blm_add_bssid_to_reject_list() - Add BSSID to the specific reject list. + * @pdev: Pdev object + * @ap_info: Ap info params such as BSSID, and the type of rejection to be done + * + * This API will add the BSSID to the reject AP list maintained by the blacklist + * manager. + */ +static inline QDF_STATUS +wlan_blm_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_info *ap_info) +{ + return blm_add_bssid_to_reject_list(pdev, ap_info); +} + +/** + * wlan_blm_update_bssid_connect_params() - Inform the BLM about connect or + * disconnect with the current AP. + * @pdev: pdev object + * @bssid: BSSID of the AP + * @con_state: Connection stae (connected/disconnected) + * + * This API will inform the BLM about the state with the AP so that if the AP + * is selected, and the connection went through, and the connection did not + * face any data stall till the bad bssid reset timer, BLM can remove the + * AP from the reject ap list maintained by it. + * + * Return: None + */ +static inline void +wlan_blm_update_bssid_connect_params(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr bssid, + enum blm_connection_state con_state) +{ + return blm_update_bssid_connect_params(pdev, bssid, con_state); +} + +/** + * wlan_blm_get_bssid_reject_list() - Get the BSSIDs in reject list from BLM + * @pdev: pdev object + * @reject_list: reject list to be filled (passed by caller) + * @max_bssid_to_be_filled: num of bssids filled in reject list by BLM + * @reject_ap_type: reject ap type of the BSSIDs to be filled. + * + * This API is a wrapper to an API of blacklist manager which will fill the + * reject ap list requested by caller of type given as argument reject_ap_type, + * and will return the number of BSSIDs filled. + * + * Return: Unsigned integer (number of BSSIDs filled by the blacklist manager) + */ +static inline uint8_t +wlan_blm_get_bssid_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_config_params *reject_list, + uint8_t max_bssid_to_be_filled, + enum blm_reject_ap_type reject_ap_type) +{ + return blm_get_bssid_reject_list(pdev, reject_list, + max_bssid_to_be_filled, + reject_ap_type); +} + +/** + * wlan_blm_get_rssi_blacklist_threshold() - Get the RSSI blacklist threshold + * @pdev: pdev object + * + * This API will get the rssi blacklist threshold value configured via + * CFG_BLACKLIST_RSSI_THRESHOLD. + * + * Return: int32_t (threshold value) + */ +static inline int32_t +wlan_blm_get_rssi_blacklist_threshold(struct wlan_objmgr_pdev *pdev) +{ + return blm_get_rssi_blacklist_threshold(pdev); +} + +#else +static inline QDF_STATUS +wlan_blm_filter_bssid(struct wlan_objmgr_pdev *pdev, qdf_list_t *scan_list) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +wlan_blm_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_info *ap_info) +{ + return QDF_STATUS_SUCCESS; +} + +static inline uint8_t +wlan_blm_get_bssid_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_config_params *reject_list, + uint8_t max_bssid_to_be_filled, + enum blm_reject_ap_type reject_ap_type) +{ + return 0; +} + +static inline void +wlan_blm_update_bssid_connect_params(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr bssid, + enum blm_connection_state con_state) +{ +} + +static inline int32_t +wlan_blm_get_rssi_blacklist_threshold(struct wlan_objmgr_pdev *pdev) +{ + return 0; +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_public_struct.h b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..4eb8e34c4e6f0c349ed78f4fef50495147655bf6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_public_struct.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define public structures of blacklist mgr. + */ + +#ifndef _WLAN_BLM_PUBLIC_STRUCT_H +#define _WLAN_BLM_PUBLIC_STRUCT_H + +#include +#include "wlan_objmgr_pdev_obj.h" + +#define MAX_BAD_AP_LIST_SIZE 28 +#define MAX_RSSI_AVOID_BSSID_LIST 10 +#define PDEV_MAX_NUM_BSSID_DISALLOW_LIST 28 + +/** + * enum blm_reject_ap_reason - Rejection reason for adding BSSID to BLM + * @ADDED_BY_DRIVER: Source adding this BSSID is driver + * @ADDED_BY_TARGET: Source adding this BSSID is target + */ +enum blm_reject_ap_source { + ADDED_BY_DRIVER = 1, + ADDED_BY_TARGET, +}; + +/** + * struct blm_rssi_disallow_params - structure to specify params for RSSI reject + * @retry_delay: Time before which the AP doesn't expect a connection. + * @expected_rssi: RSSI less than which only the STA should try association. + * @received_time: Time at which the AP was added to blacklist. + * @original_timeout: Original timeout which the AP sent while blacklisting. + * @source: Source of adding this BSSID to RSSI reject list + */ +struct blm_rssi_disallow_params { + uint32_t retry_delay; + int8_t expected_rssi; + qdf_time_t received_time; + uint32_t original_timeout; + enum blm_reject_ap_source source; +}; + +/** + * enum blm_reject_ap_type - Rejection type of the AP + * @USERSPACE_AVOID_TYPE: userspace wants the AP to be avoided. + * @USERSPACE_BLACKLIST_TYPE: userspace wants the AP to be blacklisted. + * @DRIVER_AVOID_TYPE: driver wants the AP to be avoided. + * @DRIVER_BLACKLIST_TYPE: driver wants the AP to be blacklisted. + * @DRIVER_RSSI_REJECT_TYPE: driver wants the AP to be in driver rssi reject. + * @DRIVER_MONITOR_TYPE: driver wants the AP to be in monitor list. + */ +enum blm_reject_ap_type { + USERSPACE_AVOID_TYPE = 0, + USERSPACE_BLACKLIST_TYPE = 1, + DRIVER_AVOID_TYPE = 2, + DRIVER_BLACKLIST_TYPE = 3, + DRIVER_RSSI_REJECT_TYPE = 4, + DRIVER_MONITOR_TYPE = 5 +}; + +/** + * enum blm_reject_ap_reason - Rejection reason for adding BSSID to BLM + * @REASON_UNKNOWN: Unknown reason + * @REASON_NUD_FAILURE: NUD failure happened with this BSSID + * @REASON_STA_KICKOUT: STA kickout happened with this BSSID + * @REASON_ROAM_HO_FAILURE: HO failure happenend with this BSSID + * @REASON_ASSOC_REJECT_POOR_RSSI: assoc rsp with reason 71 received from AP. + * @REASON_ASSOC_REJECT_OCE: OCE assoc reject received from the AP. + * @REASON_USERSPACE_BL: Userspace wants to blacklist this AP. + * @REASON_USERSPACE_AVOID_LIST: Userspace wants to avoid this AP. + * @REASON_BTM_DISASSOC_IMMINENT: BTM IE received with disassoc imminent set. + * @REASON_BTM_BSS_TERMINATION: BTM IE received with BSS termination set. + * @REASON_BTM_MBO_RETRY: BTM IE received from AP with MBO retry set. + * @REASON_REASSOC_RSSI_REJECT: Re-Assoc resp received with reason code 34 + * @REASON_REASSOC_NO_MORE_STAS: Re-assoc reject received with reason code 17 + */ +enum blm_reject_ap_reason { + REASON_UNKNOWN = 0, + REASON_NUD_FAILURE, + REASON_STA_KICKOUT, + REASON_ROAM_HO_FAILURE, + REASON_ASSOC_REJECT_POOR_RSSI, + REASON_ASSOC_REJECT_OCE, + REASON_USERSPACE_BL, + REASON_USERSPACE_AVOID_LIST, + REASON_BTM_DISASSOC_IMMINENT, + REASON_BTM_BSS_TERMINATION, + REASON_BTM_MBO_RETRY, + REASON_REASSOC_RSSI_REJECT, + REASON_REASSOC_NO_MORE_STAS, +}; + +/** + * enum blm_connection_state - State with AP (Connected, Disconnected) + * @BLM_AP_CONNECTED: Connected with the AP + * @BLM_AP_DISCONNECTED: Disconnected with the AP + */ +enum blm_connection_state { + BLM_AP_CONNECTED, + BLM_AP_DISCONNECTED, +}; + +/** + * struct reject_ap_config_params - Structure to send reject ap list to FW + * @bssid: BSSID of the AP + * @reject_ap_type: Type of the rejection done with the BSSID + * @reject_duration: time left till the AP is in the reject list. + * @expected_rssi: expected RSSI when the AP expects the connection to be made. + * @reject_reason: reason to add the BSSID to BLM + * @source: Source of adding the BSSID to BLM + * @received_time: Time at which the AP was added to blacklist. + * @original_timeout: Original timeout which the AP sent while blacklisting. + */ +struct reject_ap_config_params { + struct qdf_mac_addr bssid; + enum blm_reject_ap_type reject_ap_type; + uint32_t reject_duration; + int32_t expected_rssi; + enum blm_reject_ap_reason reject_reason; + enum blm_reject_ap_source source; + qdf_time_t received_time; + uint32_t original_timeout; +}; + +/** + * struct reject_ap_params - Struct to send bssid list and there num to FW + * @num_of_reject_bssid: num of bssid params there in bssid config. + * @bssid_list: Pointer to the bad bssid list + */ +struct reject_ap_params { + uint8_t num_of_reject_bssid; + struct reject_ap_config_params *bssid_list; +}; + +/** + * struct wlan_blm_tx_ops - structure of tx operation function + * pointers for blacklist manager component + * @blm_send_reject_ap_list: send reject ap list to fw + */ +struct wlan_blm_tx_ops { + QDF_STATUS (*blm_send_reject_ap_list)(struct wlan_objmgr_pdev *pdev, + struct reject_ap_params *reject_params); +}; + +/** + * struct reject_ap_info - structure to specify the reject ap info. + * @bssid: BSSID of the AP. + * @rssi_reject_params: RSSI reject params of the AP is of type RSSI reject + * @reject_ap_type: Reject type of AP (eg. avoid, blacklist, rssi reject etc.) + * @reject_reason: reason to add the BSSID to BLM + * @source: Source of adding the BSSID to BLM + */ +struct reject_ap_info { + struct qdf_mac_addr bssid; + struct blm_rssi_disallow_params rssi_reject_params; + enum blm_reject_ap_type reject_ap_type; + enum blm_reject_ap_reason reject_reason; + enum blm_reject_ap_source source; +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_tgt_api.h b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..6ca9e311b2de66bd9b7f364aa6cdf8474618e647 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_tgt_api.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare public API for blacklist manager to interact with target/WMI + */ + +#ifndef _WLAN_BLM_TGT_API_H +#define _WLAN_BLM_TGT_API_H + +#include "wlan_blm_main.h" + +/** + * tgt_blm_send_reject_list_to_fw() - API to send the reject ap list to FW. + * @pdev: pdev object + * @reject_params: Reject params contains the bssid list, and num of bssids + * + * This API will send the reject AP list maintained by the blacklist manager + * to the target. + * + * Return: QDF status + */ +QDF_STATUS +tgt_blm_send_reject_list_to_fw(struct wlan_objmgr_pdev *pdev, + struct reject_ap_params *reject_params); + +#endif diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_ucfg_api.h b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..e4844bf212d1c2a2e06f9865778ed4975259b167 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_blm_ucfg_api.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare UCFG APIs exposed by the blacklist manager component + */ + +#ifndef _WLAN_BLM_UCFG_H_ +#define _WLAN_BLM_UCFG_H_ + +#include "qdf_types.h" +#include "wlan_objmgr_psoc_obj.h" +#include + +#ifdef FEATURE_BLACKLIST_MGR + +/** + * ucfg_blm_init() - initialize blacklist mgr context + * + * This function initializes the blacklist mgr context + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS ucfg_blm_init(void); + +/** + * ucfg_blm_deinit() - De initialize blacklist mgr context + * + * This function De initializes blacklist mgr context + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS ucfg_blm_deinit(void); + +/** + * ucfg_blm_psoc_set_suspended() - API to set blacklist mgr state suspended + * @psoc: pointer to psoc object + * @state: state to be set + * + * This function sets blacklist mgr state to suspended + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS ucfg_blm_psoc_set_suspended(struct wlan_objmgr_psoc *psoc, + bool state); + +/** + * ucfg_blm_psoc_get_suspended() - API to get blacklist mgr state suspended + * @psoc: pointer to psoc object + * @state: pointer to get suspend state of blacklist manager + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS ucfg_blm_psoc_get_suspended(struct wlan_objmgr_psoc *psoc, + bool *state); + +/** + * ucfg_blm_psoc_open() - API to initialize the cfg when psoc is initialized. + * @psoc: psoc object + * + * This function initializes the config of blacklist mgr. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS ucfg_blm_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_blm_psoc_close() - API to deinit the blm when psoc is deinitialized. + * @psoc: psoc object + * + * This function deinits the blm psoc object. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS ucfg_blm_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_blm_add_userspace_black_list() - Clear already existing userspace BSSID, + * and add the new ones to blacklist manager. + * @pdev: pdev object + * @bssid_black_list: BSSIDs to be blacklisted by userspace. + * @num_of_bssid: num of bssids to be blacklisted. + * + * This API clear already existing userspace BSSID, and add the new ones to + * blacklist manager + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error. + */ +QDF_STATUS +ucfg_blm_add_userspace_black_list(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr *bssid_black_list, + uint8_t num_of_bssid); + +/** + * ucfg_blm_update_bssid_connect_params() - Inform the BLM about connect or + * disconnect with the current AP. + * @pdev: pdev object + * @bssid: BSSID of the AP + * @con_state: Connection stae (connected/disconnected) + * + * This API will inform the BLM about the state with the AP so that if the AP + * is selected, and the connection went through, and the connection did not + * face any data stall till the bad bssid reset timer, BLM can remove the + * AP from the reject ap list maintained by it. + * + * Return: None + */ +void +ucfg_blm_update_bssid_connect_params(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr bssid, + enum blm_connection_state con_state); + +/** + * ucfg_blm_add_bssid_to_reject_list() - Add BSSID to the specific reject list. + * @pdev: Pdev object + * @ap_info: Ap info params such as BSSID, and the type of rejection to be done + * + * This API will add the BSSID to the reject AP list maintained by the blacklist + * manager. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error. + */ +QDF_STATUS +ucfg_blm_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_info *ap_info); + +/** + * ucfg_blm_wifi_off() - Inform the blacklist manager about wifi off + * @blm_ctx: blacklist manager pdev priv object + * + * This API will inform the blacklist manager that the user has turned wifi off + * from the UI, and the blacklist manager can take action based upon this. + * + * Return: None + */ +void +ucfg_blm_wifi_off(struct wlan_objmgr_pdev *pdev); + +#else +static inline +QDF_STATUS ucfg_blm_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_blm_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_blm_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_blm_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS +ucfg_blm_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_info *ap_info) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_blm_add_userspace_black_list(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr *bssid_black_list, + uint8_t num_of_bssid) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void +ucfg_blm_update_bssid_connect_params(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr bssid, + enum blm_connection_state con_state) +{ +} + +static inline +void ucfg_blm_wifi_off(struct wlan_objmgr_pdev *pdev) +{ +} + +#endif +#endif /* _WLAN_BLM_UCFG_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_cfg_blm.h b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_cfg_blm.h new file mode 100644 index 0000000000000000000000000000000000000000..47de605e12a9db156a65f7efb3de1c69fbe0f870 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/inc/wlan_cfg_blm.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains ini params for blacklist mgr component + */ + +#ifndef __CFG_BLM_H_ +#define __CFG_BLM_H_ + +#ifdef FEATURE_BLACKLIST_MGR + +/* + * + * avoid_list_expiry_time - Config Param to move AP from avoid to monitor list. + * @Min: 1 minutes + * @Max: 300 minutes + * @Default: 5 minutes + * + * This ini is used to specify the time after which the BSSID which is in the + * avoid list should be moved to monitor list, assuming that the AP or the + * gateway with which the data stall happenend might have recovered, and now + * the STA can give another chance to connect to the AP. + * + * Supported Feature: Data Stall Recovery + * + * Usage: External + * + * + */ +#define CFG_AVOID_LIST_EXPIRY_TIME CFG_INI_UINT( \ + "avoid_list_expiry_time", \ + 1, \ + 300, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "avoid list expiry") + +/* + * + * bad_bssid_counter_thresh - Threshold to move the Ap from avoid to blacklist. + * @Min: 2 + * @Max: 10 + * @Default: 3 + * + * This ini is used to specify the threshld after which the BSSID which is in + * the avoid list should be moved to black list, assuming that the AP or the + * gateway with which the data stall happenend has no recovered, and now + * the STA got the NUD failure again with the BSSID + * + * Supported Feature: Data Stall Recovery + * + * Usage: External + * + * + */ +#define CFG_BAD_BSSID_COUNTER_THRESHOLD CFG_INI_UINT( \ + "bad_bssid_counter_thresh", \ + 2, \ + 10, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "bad bssid counter thresh") + +/* + * + * black_list_expiry_time - Config Param to move AP from blacklist to monitor + * list. + * @Min: 1 minutes + * @Max: 600 minutes + * @Default: 10 minutes + * + * This ini is used to specify the time after which the BSSID which is in the + * black list should be moved to monitor list, assuming that the AP or the + * gateway with which the data stall happenend might have recovered, and now + * the STA can give another chance to connect to the AP. + * + * Supported Feature: Data Stall Recovery + * + * Usage: External + * + * + */ +#define CFG_BLACK_LIST_EXPIRY_TIME CFG_INI_UINT( \ + "black_list_expiry_time", \ + 1, \ + 600, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "black list expiry") + +/* + * + * bad_bssid_reset_time - Config Param to specify time after which AP would be + * removed from monitor/avoid when connected. + * @Min: 30 seconds + * @Max: 1 minute + * @Default: 30 seconds + * + * This ini is used to specify the time after which the BSSID which is in the + * avoid or monitor list should be removed from the respective list, if the + * data stall has not happened till the mentioned time after connection to the + * AP. That means that the AP has recovered from the previous state where + * data stall was observed with it, and was moved to avoid list. + * + * Supported Feature: Data Stall Recovery + * + * Usage: External + * + * + */ +#define CFG_BAD_BSSID_RESET_TIME CFG_INI_UINT( \ + "bad_bssid_reset_time", \ + 30, \ + 60, \ + 30, \ + CFG_VALUE_OR_DEFAULT, \ + "bad bssid reset time") + +/* + * + * delta_rssi - RSSI threshold value, only when AP rssi improves + * by threshold value entry would be removed from blacklist manager and assoc + * req would be sent by FW. + * @Min: 0 + * @Max: 10 + * @Default: 5 + * + * This ini is used to specify the rssi threshold value, after rssi improves + * by threshold the BSSID which is in the blacklist manager list should be + * removed from the respective list. + * + * Supported Feature: Customer requirement + * + * Usage: Internal/External + * + * + */ +#define CFG_BLACKLIST_RSSI_THRESHOLD CFG_INI_INT( \ + "delta_rssi", \ + 0, \ + 10, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure delta RSSI") + +#define CFG_BLACKLIST_MGR_ALL \ + CFG(CFG_AVOID_LIST_EXPIRY_TIME) \ + CFG(CFG_BAD_BSSID_COUNTER_THRESHOLD) \ + CFG(CFG_BLACK_LIST_EXPIRY_TIME) \ + CFG(CFG_BAD_BSSID_RESET_TIME) \ + CFG(CFG_BLACKLIST_RSSI_THRESHOLD) + +#else + +#define CFG_BLACKLIST_MGR_ALL + +#endif /* FEATURE_BLACKLIST_MGR */ + +#endif /* __CFG_BLACKLIST_MGR */ diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/src/wlan_blm_tgt_api.c b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/src/wlan_blm_tgt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..03c8c8c31053bffa526cf8c13d2ba4957432ead9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/src/wlan_blm_tgt_api.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Implements public API for blacklist manager to interact with target/WMI + */ + +#include "wlan_blm_tgt_api.h" + +QDF_STATUS +tgt_blm_send_reject_list_to_fw(struct wlan_objmgr_pdev *pdev, + struct reject_ap_params *reject_params) +{ + struct wlan_blm_tx_ops *blm_tx_ops; + struct blm_pdev_priv_obj *blm_priv; + + blm_priv = blm_get_pdev_obj(pdev); + + if (!blm_priv) { + blm_err("blm_priv is NULL"); + return QDF_STATUS_E_FAILURE; + } + blm_tx_ops = &blm_priv->blm_tx_ops; + if (!blm_tx_ops) { + blm_err("blm_tx_ops is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (blm_tx_ops->blm_send_reject_ap_list) + return blm_tx_ops->blm_send_reject_ap_list(pdev, reject_params); + blm_err("Tx ops not registered, failed to send reject list to FW"); + + return QDF_STATUS_E_FAILURE; +} diff --git a/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/src/wlan_blm_ucfg_api.c b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/src/wlan_blm_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..5ce6da9b0e89b779dd019a9d8566c88e1148e8fe --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/blacklist_mgr/dispatcher/src/wlan_blm_ucfg_api.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define UCFG APIs exposed by the blacklist mgr component + */ + +#include +#include +#include +#include "wlan_pmo_obj_mgmt_api.h" + +QDF_STATUS ucfg_blm_init(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_register_pdev_create_handler( + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_pdev_object_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("pdev create register notification failed"); + goto fail_create_pdev; + } + + status = wlan_objmgr_register_pdev_destroy_handler( + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_pdev_object_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("pdev destroy register notification failed"); + goto fail_destroy_pdev; + } + + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_psoc_object_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("psoc create register notification failed"); + goto fail_create_psoc; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_psoc_object_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("psoc destroy register notification failed"); + goto fail_destroy_psoc; + } + + return QDF_STATUS_SUCCESS; + +fail_destroy_psoc: + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_psoc_object_created_notification, NULL); +fail_create_psoc: + wlan_objmgr_unregister_pdev_destroy_handler( + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_pdev_object_destroyed_notification, NULL); +fail_destroy_pdev: + wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_pdev_object_created_notification, NULL); +fail_create_pdev: + return status; +} + +QDF_STATUS ucfg_blm_deinit(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_psoc_object_destroyed_notification, + NULL); + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_psoc_object_created_notification, + NULL); + + status = wlan_objmgr_unregister_pdev_destroy_handler( + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_pdev_object_destroyed_notification, + NULL); + + status = wlan_objmgr_unregister_pdev_create_handler( + WLAN_UMAC_COMP_BLACKLIST_MGR, + blm_pdev_object_created_notification, + NULL); + + return status; +} + +QDF_STATUS ucfg_blm_psoc_set_suspended(struct wlan_objmgr_psoc *psoc, + bool state) +{ + struct blm_psoc_priv_obj *blm_psoc_obj; + + blm_psoc_obj = blm_get_psoc_obj(psoc); + + if (!blm_psoc_obj) { + blm_err("BLM psoc obj NULL"); + return QDF_STATUS_E_FAILURE; + } + + blm_psoc_obj->is_suspended = state; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_blm_psoc_get_suspended(struct wlan_objmgr_psoc *psoc, + bool *state) +{ + struct blm_psoc_priv_obj *blm_psoc_obj; + + blm_psoc_obj = blm_get_psoc_obj(psoc); + + if (!blm_psoc_obj) { + blm_err("BLM psoc obj NULL"); + *state = true; + return QDF_STATUS_E_FAILURE; + } + + *state = blm_psoc_obj->is_suspended; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +ucfg_blm_suspend_handler(struct wlan_objmgr_psoc *psoc, void *arg) +{ + ucfg_blm_psoc_set_suspended(psoc, true); + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +ucfg_blm_resume_handler(struct wlan_objmgr_psoc *psoc, void *arg) +{ + ucfg_blm_psoc_set_suspended(psoc, false); + blm_update_reject_ap_list_to_fw(psoc); + return QDF_STATUS_SUCCESS; +} + +static inline void +ucfg_blm_register_pmo_handler(void) +{ + pmo_register_suspend_handler(WLAN_UMAC_COMP_BLACKLIST_MGR, + ucfg_blm_suspend_handler, NULL); + pmo_register_resume_handler(WLAN_UMAC_COMP_BLACKLIST_MGR, + ucfg_blm_resume_handler, NULL); +} + +static inline void +ucfg_blm_unregister_pmo_handler(void) +{ + pmo_unregister_suspend_handler(WLAN_UMAC_COMP_BLACKLIST_MGR, + ucfg_blm_suspend_handler); + pmo_unregister_resume_handler(WLAN_UMAC_COMP_BLACKLIST_MGR, + ucfg_blm_resume_handler); +} + +QDF_STATUS ucfg_blm_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + ucfg_blm_register_pmo_handler(); + return blm_cfg_psoc_open(psoc); +} + +QDF_STATUS ucfg_blm_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + ucfg_blm_unregister_pmo_handler(); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_blm_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_info *ap_info) +{ + return blm_add_bssid_to_reject_list(pdev, ap_info); +} + +QDF_STATUS +ucfg_blm_add_userspace_black_list(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr *bssid_black_list, + uint8_t num_of_bssid) +{ + return blm_add_userspace_black_list(pdev, bssid_black_list, + num_of_bssid); +} + +void +ucfg_blm_update_bssid_connect_params(struct wlan_objmgr_pdev *pdev, + struct qdf_mac_addr bssid, + enum blm_connection_state con_state) +{ + wlan_blm_update_bssid_connect_params(pdev, bssid, con_state); +} + +void +ucfg_blm_wifi_off(struct wlan_objmgr_pdev *pdev) +{ + struct blm_pdev_priv_obj *blm_ctx; + struct blm_psoc_priv_obj *blm_psoc_obj; + struct blm_config *cfg; + QDF_STATUS status; + + if (!pdev) { + blm_err("pdev is NULL"); + return; + } + + blm_ctx = blm_get_pdev_obj(pdev); + blm_psoc_obj = blm_get_psoc_obj(wlan_pdev_get_psoc(pdev)); + + if (!blm_ctx || !blm_psoc_obj) { + blm_err("blm_ctx or blm_psoc_obj is NULL"); + return ; + } + + status = qdf_mutex_acquire(&blm_ctx->reject_ap_list_lock); + if (QDF_IS_STATUS_ERROR(status)) { + blm_err("failed to acquire reject_ap_list_lock"); + return; + } + + cfg = &blm_psoc_obj->blm_cfg; + + blm_flush_reject_ap_list(blm_ctx); + blm_send_reject_ap_list_to_fw(pdev, &blm_ctx->reject_ap_list, cfg); + qdf_mutex_release(&blm_ctx->reject_ap_list_lock); +} diff --git a/drivers/staging/qcacld-3.0/components/cfg/cfg_all.h b/drivers/staging/qcacld-3.0/components/cfg/cfg_all.h new file mode 100644 index 0000000000000000000000000000000000000000..cdc133b1a08db8a2a6f6491b453c340ae87d6114 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cfg/cfg_all.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "wlan_policy_mgr_cfg.h" +#include "cfg_define.h" +#include "cfg_converged.h" +#include "cfg_mlme.h" +#include "cfg_fwol.h" +#include "cfg_ipa.h" + +#ifdef CONVERGED_P2P_ENABLE +#include "wlan_p2p_cfg.h" +#else +#define CFG_P2P_ALL +#endif + +#ifdef FEATURE_WLAN_TDLS +#include "wlan_tdls_cfg.h" +#else +#define CFG_TDLS_ALL +#endif + +#ifdef WLAN_FEATURE_NAN +#include "cfg_nan.h" +#else +#define CFG_NAN_ALL +#endif + +#ifdef WLAN_CFR_ENABLE +#include "cfr_cfg.h" +#else +#define CFG_CFR_ALL +#endif + +#include "wlan_pmo_cfg.h" +#include "hdd_config.h" +#include "hdd_dp_cfg.h" +#include "cfg_legacy_dp.h" +#include "wlan_cfg_blm.h" +#include "cfg_pkt_capture.h" + +/* Maintain Alphabetic order here while adding components */ +#define CFG_ALL \ + CFG_BLACKLIST_MGR_ALL \ + CFG_CONVERGED_ALL \ + CFG_FWOL_ALL \ + CFG_POLICY_MGR_ALL \ + CFG_HDD_ALL \ + CFG_HDD_DP_ALL \ + CFG_IPA \ + CFG_LEGACY_DP_ALL \ + CFG_MLME_ALL \ + CFG_NAN_ALL \ + CFG_P2P_ALL \ + CFG_PMO_ALL \ + CFG_TDLS_ALL \ + CFG_PKT_CAPTURE_MODE_ALL \ + CFG_CFR_ALL diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h new file mode 100644 index 0000000000000000000000000000000000000000..849658669e620a94d5ccb20e2fe2c2d27db84bc9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h @@ -0,0 +1,3333 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_API_H +#define __WLAN_POLICY_MGR_API_H + +/** + * DOC: wlan_policy_mgr_api.h + * + * Concurrenct Connection Management entity + */ + +/* Include files */ +#include "qdf_types.h" +#include "qdf_status.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_policy_mgr_public_struct.h" +#include "wlan_utility.h" + +struct target_psoc_info; + +typedef const enum policy_mgr_pcl_type + pm_dbs_pcl_third_connection_table_type + [PM_MAX_TWO_CONNECTION_MODE][PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE]; + +typedef const enum policy_mgr_conc_next_action + policy_mgr_next_action_two_connection_table_type + [PM_MAX_ONE_CONNECTION_MODE][POLICY_MGR_MAX_BAND]; + +typedef const enum policy_mgr_conc_next_action + policy_mgr_next_action_three_connection_table_type + [PM_MAX_TWO_CONNECTION_MODE][POLICY_MGR_MAX_BAND]; + +#define PM_FW_MODE_STA_STA_BIT_POS 0 +#define PM_FW_MODE_STA_P2P_BIT_POS 1 + +#define PM_FW_MODE_STA_STA_BIT_MASK (0x1 << PM_FW_MODE_STA_STA_BIT_POS) +#define PM_FW_MODE_STA_P2P_BIT_MASK (0x1 << PM_FW_MODE_STA_P2P_BIT_POS) + +#define PM_CHANNEL_SELECT_LOGIC_STA_STA_GET(channel_select_logic_conc) \ + ((channel_select_logic_conc & PM_FW_MODE_STA_STA_BIT_MASK) >> \ + PM_FW_MODE_STA_STA_BIT_POS) +#define PM_CHANNEL_SELECT_LOGIC_STA_P2P_GET(channel_select_logic_conc) \ + ((channel_select_logic_conc & PM_FW_MODE_STA_P2P_BIT_MASK) >> \ + PM_FW_MODE_STA_P2P_BIT_POS) + +/** + * enum sap_csa_reason_code - SAP channel switch reason code + * @CSA_REASON_UNKNOWN: Unknown reason + * @CSA_REASON_STA_CONNECT_DFS_TO_NON_DFS: STA connection from DFS to NON DFS. + * @CSA_REASON_USER_INITIATED: User initiated form north bound. + * @CSA_REASON_PEER_ACTION_FRAME: Action frame received on sta iface. + * @CSA_REASON_PRE_CAC_SUCCESS: Pre CAC success. + * @CSA_REASON_CONCURRENT_STA_CHANGED_CHANNEL: concurrent sta changed channel. + * @CSA_REASON_UNSAFE_CHANNEL: Unsafe channel. + * @CSA_REASON_LTE_COEX: LTE coex. + * @CSA_REASON_CONCURRENT_NAN_EVENT: NAN concurrency. + * @CSA_REASON_BAND_RESTRICTED: band disabled or re-enabled + * + */ +enum sap_csa_reason_code { + CSA_REASON_UNKNOWN, + CSA_REASON_STA_CONNECT_DFS_TO_NON_DFS, + CSA_REASON_USER_INITIATED, + CSA_REASON_PEER_ACTION_FRAME, + CSA_REASON_PRE_CAC_SUCCESS, + CSA_REASON_CONCURRENT_STA_CHANGED_CHANNEL, + CSA_REASON_UNSAFE_CHANNEL, + CSA_REASON_LTE_COEX, + CSA_REASON_CONCURRENT_NAN_EVENT, + CSA_REASON_BAND_RESTRICTED +}; + +/** + * enum PM_AP_DFS_MASTER_MODE - AP dfs master mode + * @PM_STA_SAP_ON_DFS_DEFAULT - Disallow STA+SAP SCC on DFS channel + * @PM_STA_SAP_ON_DFS_MASTER_MODE_DISABLED - Allow STA+SAP SCC + * on DFS channel with master mode disabled + * @PM_STA_SAP_ON_DFS_MASTER_MODE_FLEX - enhance + * "PM_STA_SAP_ON_DFS_MASTER_MODE_DISABLED" with below requirement: + * a. Allow single SAP (GO) start on DFS channel. + * b. Allow CAC process on DFS channel in single SAP (GO) mode + * c. Allow DFS radar event process in single SAP (GO) mode + * d. Disallow CAC and radar event process in SAP (GO) + STA mode. + * + * This enum value will be used to set to INI g_sta_sap_scc_on_dfs_chan to + * config the sta+sap on dfs channel behaviour expected by user. + */ +enum PM_AP_DFS_MASTER_MODE { + PM_STA_SAP_ON_DFS_DEFAULT, + PM_STA_SAP_ON_DFS_MASTER_MODE_DISABLED, + PM_STA_SAP_ON_DFS_MASTER_MODE_FLEX, +}; + +/** + * policy_mgr_get_allow_mcc_go_diff_bi() - to get information on whether GO + * can have diff BI than STA in MCC + * @psoc: pointer to psoc + * @allow_mcc_go_diff_bi: value to be filled + * + * This API is used to find out whether GO's BI can different than STA in MCC + * scenario + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +policy_mgr_get_allow_mcc_go_diff_bi(struct wlan_objmgr_psoc *psoc, + uint8_t *allow_mcc_go_diff_bi); +/** + * policy_mgr_get_enable_overlap_chnl() - to find out if overlap channels + * are enabled for SAP + * @psoc: pointer to psoc + * @enable_overlap_chnl: value to be filled + * + * This API is used to find out whether overlap channels are enabled for SAP + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +policy_mgr_get_enable_overlap_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_overlap_chnl); +/** + * policy_mgr_get_dual_mac_feature() - to find out if DUAL MAC feature is + * enabled + * @psoc: pointer to psoc + * @dual_mac_feature: value to be filled + * + * This API is used to find out whether dual mac (dual radio) specific feature + * is enabled or not + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_dual_mac_feature(struct wlan_objmgr_psoc *psoc, + uint8_t *dual_mac_feature); + +/** + * policy_mgr_set_dual_mac_feature() - to set the dual mac feature value + * @psoc: pointer to psoc + * @dual_mac_feature: value to be updated + * + * This API is used to update the dual mac (dual radio) specific feature value + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_set_dual_mac_feature(struct wlan_objmgr_psoc *psoc, + uint8_t dual_mac_feature); + +/** + * policy_mgr_get_force_1x1() - to find out if 1x1 connection is enforced + * + * @psoc: pointer to psoc + * @force_1x1: value to be filled + * + * This API is used to find out if 1x1 connection is enforced. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_force_1x1(struct wlan_objmgr_psoc *psoc, + uint8_t *force_1x1); +/** + * policy_mgr_get_sta_sap_scc_on_dfs_chnl() - to find out if STA and SAP + * SCC is allowed on DFS channel + * @psoc: pointer to psoc + * @sta_sap_scc_on_dfs_chnl: value to be filled + * + * This API is used to find out whether STA and SAP SCC is allowed on + * DFS channels + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +policy_mgr_get_sta_sap_scc_on_dfs_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sta_sap_scc_on_dfs_chnl); + +/** + * policy_mgr_get_dfs_master_dynamic_enabled() - support dfs master or not + * on AP interafce when STA+SAP(GO) concurrency + * @psoc: pointer to psoc + * @vdev_id: sap vdev id + * + * This API is used to check AP dfs master functionality enabled or not when + * STA+SAP(GO) concurrency. + * If g_sta_sap_scc_on_dfs_chan is non-zero, the STA+SAP(GO) concurrency + * is allowed on DFS channel SCC and the SAP's DFS master functionality + * should be enable/disable according to: + * 1. g_sta_sap_scc_on_dfs_chan is 0: function return true - dfs master + * capability enabled. + * 2. g_sta_sap_scc_on_dfs_chan is 1: function return false - dfs master + * capability disabled. + * 3. g_sta_sap_scc_on_dfs_chan is 2: dfs master capability based on STA on + * 5G or not: + * a. 5G STA active - return false + * b. no 5G STA active -return true + * + * Return: true if dfs master functionality should be enabled. + */ +bool +policy_mgr_get_dfs_master_dynamic_enabled(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * policy_mgr_get_sta_sap_scc_lte_coex_chnl() - to find out if STA & SAP + * SCC is allowed on LTE COEX + * @psoc: pointer to psoc + * @sta_sap_scc_lte_coex: value to be filled + * + * This API is used to find out whether STA and SAP scc is allowed on LTE COEX + * channel + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +policy_mgr_get_sta_sap_scc_lte_coex_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sta_sap_scc_lte_coex); +/** + * policy_mgr_get_sap_mandt_chnl() - to find out if SAP mandatory channel + * support is enabled + * @psoc: pointer to psoc + * @sap_mandt_chnl: value to be filled + * + * This API is used to find out whether SAP's mandatory channel support + * is enabled + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_sap_mandt_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sap_mandt_chnl); +/** + * policy_mgr_get_indoor_chnl_marking() - to get if indoor channel can be + * marked as disabled + * @psoc: pointer to psoc + * @indoor_chnl_marking: value to be filled + * + * This API is used to find out whether indoor channel can be marked as disabled + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +policy_mgr_get_indoor_chnl_marking(struct wlan_objmgr_psoc *psoc, + uint8_t *indoor_chnl_marking); +/** + * policy_mgr_get_mcc_scc_switch() - To mcc to scc switch setting from INI + * @psoc: pointer to psoc + * @mcc_scc_switch: value to be filled + * + * This API pulls mcc to scc switch setting which is given as part of INI and + * stored in policy manager's CFGs. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch); +/** + * policy_mgr_get_sys_pref() - to get system preference + * @psoc: pointer to psoc + * @sys_pref: value to be filled + * + * This API pulls the system preference for policy manager to provide + * PCL + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t *sys_pref); +/** + * policy_mgr_set_sys_pref() - to set system preference + * @psoc: pointer to psoc + * @sys_pref: value to be applied as new INI setting + * + * This API is meant to override original INI setting for system pref + * with new value which is used by policy manager to provide PCL + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_set_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t sys_pref); + +/** + * policy_mgr_get_conc_rule1() - to find out if conc rule1 is enabled + * @psoc: pointer to psoc + * @conc_rule1: value to be filled + * + * This API is used to find out if conc rule-1 is enabled by user + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_conc_rule1(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule1); +/** + * policy_mgr_get_conc_rule2() - to find out if conc rule2 is enabled + * @psoc: pointer to psoc + * @conc_rule2: value to be filled + * + * This API is used to find out if conc rule-2 is enabled by user + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_conc_rule2(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule2); + +/** + * policy_mgr_get_chnl_select_plcy() - to get channel selection policy + * @psoc: pointer to psoc + * @chnl_select_plcy: value to be filled + * + * This API is used to find out which channel selection policy has been + * configured + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_chnl_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *chnl_select_plcy); + +/** + * policy_mgr_set_ch_select_plcy() - to set channel selection policy + * @psoc: pointer to psoc + * @ch_select_policy: value to be set + * + * This API is used to set the ch selection policy. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_set_ch_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t ch_select_policy); + +/** + * policy_mgr_get_dynamic_mcc_adaptive_sch() - to get dynamic mcc adaptive + * scheduler + * @psoc: pointer to psoc + * @dynamic_mcc_adaptive_sched: value to be filled + * + * This API is used to get dynamic mcc adaptive scheduler + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_dynamic_mcc_adaptive_sch( + struct wlan_objmgr_psoc *psoc, + bool *dynamic_mcc_adaptive_sched); + +/** + * policy_mgr_set_dynamic_mcc_adaptive_sch() - to set dynamic mcc adaptive + * scheduler + * @psoc: pointer to psoc + * @dynamic_mcc_adaptive_sched: value to be set + * + * This API is used to set dynamic mcc adaptive scheduler + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_set_dynamic_mcc_adaptive_sch( + struct wlan_objmgr_psoc *psoc, + bool dynamic_mcc_adaptive_sched); + +/** + * policy_mgr_get_mcc_adaptive_sch() - to get mcc adaptive scheduler + * @psoc: pointer to psoc + * @enable_mcc_adaptive_sch: value to be filled + * + * This API is used to find out if mcc adaptive scheduler enabled or disabled + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_mcc_adaptive_sch); + +/** + * policy_mgr_get_sta_cxn_5g_band() - to get STA's connection in 5G config + * + * @psoc: pointer to psoc + * @enable_sta_cxn_5g_band: value to be filled + * + * This API is used to find out if STA connection in 5G band is allowed or + * disallowed. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_sta_cxn_5g_band(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sta_cxn_5g_band); +/** + * policy_mgr_set_concurrency_mode() - To set concurrency mode + * @psoc: PSOC object data + * @mode: device mode + * + * This routine is called to set the concurrency mode + * + * Return: NONE + */ +void policy_mgr_set_concurrency_mode(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode); + +/** + * policy_mgr_clear_concurrency_mode() - To clear concurrency mode + * @psoc: PSOC object data + * @mode: device mode + * + * This routine is called to clear the concurrency mode + * + * Return: NONE + */ +void policy_mgr_clear_concurrency_mode(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode); + +/** + * policy_mgr_get_connection_count() - provides the count of + * current connections + * @psoc: PSOC object information + * + * This function provides the count of current connections + * + * Return: connection count + */ +uint32_t policy_mgr_get_connection_count(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_concurrency_mode() - return concurrency mode + * @psoc: PSOC object information + * + * This routine is used to retrieve concurrency mode + * + * Return: uint32_t value of concurrency mask + */ +uint32_t policy_mgr_get_concurrency_mode(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_search_and_check_for_session_conc() - Checks if + * concurrecy is allowed + * @psoc: PSOC object information + * @session_id: Session id + * @roam_profile: Pointer to the roam profile + * + * Searches and gets the channel number from the scan results and checks if + * concurrency is allowed for the given session ID + * + * Return: Non zero channel frequency value if concurrency is allowed else 0 + */ +uint32_t policy_mgr_search_and_check_for_session_conc( + struct wlan_objmgr_psoc *psoc, + uint8_t session_id, void *roam_profile); + +/** + * policy_mgr_is_chnl_in_diff_band() - to check that given channel + * is in diff band from existing channel or not + * @psoc: pointer to psoc + * @ch_freq: given channel frequency + * + * This API will check that if the passed channel is in diff band than the + * already existing connections or not. + * + * Return: true if channel is in diff band + */ +bool policy_mgr_is_chnl_in_diff_band(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq); + +/** + * policy_mgr_check_for_session_conc() - Check if concurrency is + * allowed for a session + * @psoc: PSOC object information + * @session_id: Session ID + * @ch_freq: Channel frequency + * + * Checks if connection is allowed for a given session_id + * + * True if the concurrency is allowed, false otherwise + */ +bool policy_mgr_check_for_session_conc(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint32_t ch_freq); + +/** + * policy_mgr_handle_conc_multiport() - to handle multiport concurrency + * @session_id: Session ID + * @ch_freq: Channel frequency + * @reason: reason for connection update + * + * This routine will handle STA side concurrency when policy manager + * is enabled. + * + * Return: QDF_STATUS + */ +QDF_STATUS +policy_mgr_handle_conc_multiport(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint32_t ch_freq, + enum policy_mgr_conn_update_reason reason); + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * policy_mgr_check_concurrent_intf_and_restart_sap() - Check + * concurrent change intf + * @psoc: PSOC object information + * @operation_channel: operation channel + * @vdev_id: vdev id of SAP + * + * Checks the concurrent change interface and restarts SAP + * + * Return: None + */ +void policy_mgr_check_concurrent_intf_and_restart_sap( + struct wlan_objmgr_psoc *psoc); +#else +static inline void policy_mgr_check_concurrent_intf_and_restart_sap( + struct wlan_objmgr_psoc *psoc) +{ + +} +#endif /* FEATURE_WLAN_MCC_TO_SCC_SWITCH */ + +/** + * policy_mgr_is_mcc_in_24G() - Function to check for MCC in 2.4GHz + * @psoc: PSOC object information + * + * This function is used to check for MCC operation in 2.4GHz band. + * STA, P2P and SAP adapters are only considered. + * + * Return: True if mcc is detected in 2.4 Ghz, false otherwise + * + */ +bool policy_mgr_is_mcc_in_24G(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_change_mcc_go_beacon_interval() - Change MCC beacon interval + * @psoc: PSOC object information + * @vdev_id: vdev id + * @dev_mode: device mode + * + * Updates the beacon parameters of the GO in MCC scenario + * + * Return: Success or Failure depending on the overall function behavior + */ +QDF_STATUS policy_mgr_change_mcc_go_beacon_interval( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, enum QDF_OPMODE dev_mode); + +#if defined(FEATURE_WLAN_MCC_TO_SCC_SWITCH) +/** + * policy_mgr_change_sap_channel_with_csa() - Move SAP channel using (E)CSA + * @psoc: PSOC object information + * @vdev_id: Vdev id + * @ch_freq: Channel frequency to change + * @ch_width: channel width to change + * @forced: Force to switch channel, ignore SCC/MCC check + * + * Invoke the callback function to change SAP channel using (E)CSA + * + * Return: None + */ +void policy_mgr_change_sap_channel_with_csa(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t ch_freq, + uint32_t ch_width, bool forced); + +#else +static inline void policy_mgr_change_sap_channel_with_csa( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t ch_freq, + uint32_t ch_width, bool forced) +{ + +} +#endif + +/** + * policy_mgr_set_pcl_for_existing_combo() - SET PCL for existing combo + * @psoc: PSOC object information + * @mode: Adapter mode + * + * Return: None + */ +void policy_mgr_set_pcl_for_existing_combo(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode); +/** + * policy_mgr_incr_active_session() - increments the number of active sessions + * @psoc: PSOC object information + * @mode: Adapter mode + * @session_id: session ID for the connection session + * + * This function increments the number of active sessions maintained per device + * mode. In the case of STA/P2P CLI/IBSS upon connection indication it is + * incremented; In the case of SAP/P2P GO upon bss start it is incremented + * + * Return: None + */ +void policy_mgr_incr_active_session(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, uint8_t session_id); + +/** + * policy_mgr_decr_active_session() - decrements the number of active sessions + * @psoc: PSOC object information + * @mode: Adapter mode + * @session_id: session ID for the connection session + * + * This function decrements the number of active sessions maintained per device + * mode. In the case of STA/P2P CLI/IBSS upon disconnection it is decremented + * In the case of SAP/P2P GO upon bss stop it is decremented + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_decr_active_session(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, uint8_t session_id); + +/** + * policy_mgr_decr_session_set_pcl() - Decrement session count and set PCL + * @psoc: PSOC object information + * @mode: Adapter mode + * @session_id: Session id + * + * Decrements the active session count and sets the PCL if a STA connection + * exists + * + * Return: None + */ +void policy_mgr_decr_session_set_pcl(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, uint8_t session_id); + +/** + * policy_mgr_get_channel() - provide channel number of given mode and vdevid + * @psoc: PSOC object information + * @mode: given mode + * @vdev_id: pointer to vdev_id + * + * This API will provide channel frequency value of matching mode and vdevid. + * If vdev_id is NULL then it will match only mode + * If vdev_id is not NULL the it will match both mode and vdev_id + * + * Return: channel frequency value + */ +uint32_t policy_mgr_get_channel(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t *vdev_id); + +/** + * policy_mgr_get_pcl() - provides the preferred channel list for + * new connection + * @psoc: PSOC object information + * @mode: Device mode + * @pcl_channels: Preferred channel freq list + * @len: length of the PCL + * @pcl_weight: Weights of the PCL + * @weight_len: Max length of the weights list + * + * This function provides the preferred channel list on which + * policy manager wants the new connection to come up. Various + * connection decision making entities will using this function + * to query the PCL info + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_pcl(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t *pcl_channels, uint32_t *len, + uint8_t *pcl_weight, uint32_t weight_len); + +/** + * policy_mgr_init_chan_avoidance() - init channel avoidance in policy manager. + * @psoc: PSOC object information + * @chan_freq_list: channel frequency list + * @chan_cnt: channel count + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_init_chan_avoidance(struct wlan_objmgr_psoc *psoc, + qdf_freq_t *chan_freq_list, + uint16_t chan_cnt); + +/** + * policy_mgr_update_with_safe_channel_list() - provides the safe + * channel list + * @psoc: PSOC object information + * @pcl_channels: channel freq list + * @len: length of the list + * @weight_list: Weights of the PCL + * @weight_len: Max length of the weights list + * + * This function provides the safe channel list from the list + * provided after consulting the channel avoidance list + * + * Return: None + */ +void policy_mgr_update_with_safe_channel_list(struct wlan_objmgr_psoc *psoc, + uint32_t *pcl_channels, + uint32_t *len, + uint8_t *weight_list, + uint32_t weight_len); + +/** + * policy_mgr_get_nondfs_preferred_channel() - to get non-dfs preferred channel + * for given mode + * @psoc: PSOC object information + * @mode: mode for which preferred non-dfs channel is requested + * @for_existing_conn: flag to indicate if preferred channel is requested + * for existing connection + * + * this routine will return non-dfs channel + * 1) for getting non-dfs preferred channel, first we check if there are any + * other connection exist whose channel is non-dfs. if yes then return that + * channel so that we can accommodate upto 3 mode concurrency. + * 2) if there no any other connection present then query concurrency module + * to give preferred channel list. once we get preferred channel list, loop + * through list to find first non-dfs channel from ascending order. + * + * Return: uint32_t non-dfs channel frequency + */ +uint32_t +policy_mgr_get_nondfs_preferred_channel(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + bool for_existing_conn); + +/** + * policy_mgr_is_any_nondfs_chnl_present() - Find any non-dfs + * channel from conc table + * @psoc: PSOC object information + * @ch_freq: pointer to channel frequency which needs to be filled + * + * In-case if any connection is already present whose channel is none dfs then + * return that channel + * + * Return: true up-on finding non-dfs channel else false + */ +bool policy_mgr_is_any_nondfs_chnl_present(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq); + +/** + * policy_mgr_get_dfs_beaconing_session_id() - to find the + * first DFS session id + * @psoc: PSOC object information + * + * Return: If any beaconing session such as SAP or GO present and it is on + * DFS channel then this function will return its session id + * + */ +uint32_t policy_mgr_get_dfs_beaconing_session_id( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_any_dfs_beaconing_session_present() - to find + * if any DFS session + * @psoc: PSOC object information + * @ch_freq: pointer to channel frequency that needs to filled + * + * If any beaconing session such as SAP or GO present and it is on DFS channel + * then this function will return true + * + * Return: true if session is on DFS or false if session is on non-dfs channel + */ +bool policy_mgr_is_any_dfs_beaconing_session_present( + struct wlan_objmgr_psoc *psoc, uint32_t *ch_freq); +/** + * policy_mgr_allow_concurrency() - Check for allowed concurrency + * combination consulting the PCL + * @psoc: PSOC object information + * @mode: new connection mode + * @ch_freq: channel frequency on which new connection is coming up + * @bw: Bandwidth requested by the connection (optional) + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * allowed or not based on the HW capability + * + * Return: True/False based on concurrency support + */ +bool policy_mgr_allow_concurrency(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, + enum hw_mode_bandwidth bw); + +/** + * policy_mgr_nan_sap_pre_enable_conc_check() - Check if NAN+SAP SCC is + * allowed in given ch + * @psoc: PSOC object information + * @mode: Connection mode + * @ch_freq: channel frequency to check + * + * Return: True if allowed else false + */ +bool +policy_mgr_nan_sap_pre_enable_conc_check(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq); + +/** + * policy_mgr_allow_concurrency_csa() - Check for allowed concurrency + * combination when channel switch + * @psoc: PSOC object information + * @mode: connection mode + * @ch_freq: target channel frequency to switch + * @vdev_id: vdev id of channel switch interface + * @forced: forced to chan switch. + * @reason: request reason of CSA + * + * There is already existing SAP+GO combination but due to upper layer + * notifying LTE-COEX event or sending command to move one of the connections + * to different channel. In such cases before moving existing connection to new + * channel, check if new channel can co-exist with the other existing + * connection. For example, one SAP1 is on channel-6 and second SAP2 is on + * channel-36 and lets say they are doing DBS, and lets say upper layer sends + * LTE-COEX to move SAP1 from channel-6 to channel-149. In this case, SAP1 and + * SAP2 will end up doing MCC which may not be desirable result. such cases + * will be prevented with this API. + * + * Return: True/False + */ +bool +policy_mgr_allow_concurrency_csa(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, uint32_t vdev_id, + bool forced, enum sap_csa_reason_code reason); + +/** + * policy_mgr_get_first_connection_pcl_table_index() - provides the + * row index to firstConnectionPclTable to get to the correct + * pcl + * @psoc: PSOC object information + * + * This function provides the row index to + * firstConnectionPclTable. The index is the preference config. + * + * Return: table index + */ +enum policy_mgr_conc_priority_mode + policy_mgr_get_first_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_second_connection_pcl_table_index() - provides the + * row index to secondConnectionPclTable to get to the correct + * pcl + * @psoc: PSOC object information + * + * This function provides the row index to + * secondConnectionPclTable. The index is derived based on + * current connection, band on which it is on & chain mask it is + * using, as obtained from pm_conc_connection_list. + * + * Return: table index + */ +enum policy_mgr_one_connection_mode + policy_mgr_get_second_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_third_connection_pcl_table_index() - provides the + * row index to thirdConnectionPclTable to get to the correct + * pcl + * @psoc: PSOC object information + * + * This function provides the row index to + * thirdConnectionPclTable. The index is derived based on + * current connection, band on which it is on & chain mask it is + * using, as obtained from pm_conc_connection_list. + * + * Return: table index + */ +enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc); + +#ifdef FEATURE_FOURTH_CONNECTION +/** + * policy_mgr_get_fourth_connection_pcl_table_index() - provides the + * row index to fourthConnectionPclTable to get to the correct + * pcl + * @psoc: PSOC object information + * + * This function provides the row index to + * fourthConnectionPclTable. The index is derived based on + * current connection, band on which it is on & chain mask it is + * using, as obtained from pm_conc_connection_list. + * + * Return: table index + */ +enum policy_mgr_three_connection_mode + policy_mgr_get_fourth_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc); +#endif + +/** + * policy_mgr_incr_connection_count() - adds the new connection to + * the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id + * @mode: Operating mode + * + * This function adds the new connection to the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_incr_connection_count(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + enum QDF_OPMODE mode); + +/** + * policy_mgr_update_connection_info() - updates the existing + * connection in the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id + * + * + * This function adds the new connection to the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_update_connection_info(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id); + +/** + * policy_mgr_decr_connection_count() - remove the old connection + * from the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id of the old connection + * + * + * This function removes the old connection from the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_decr_connection_count(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id); + +/** + * policy_mgr_current_connections_update() - initiates actions + * needed on current connections once channel has been decided + * for the new connection + * @psoc: PSOC object information + * @session_id: Session id + * @ch_freq: Channel frequency on which new connection will be + * @reason: Reason for which connection update is required + * + * This function initiates initiates actions + * needed on current connections once channel has been decided + * for the new connection. Notifies UMAC & FW as well + * + * Return: QDF_STATUS enum + */ +QDF_STATUS +policy_mgr_current_connections_update(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, uint32_t ch_freq, + enum policy_mgr_conn_update_reason); + +/** + * policy_mgr_is_dbs_allowed_for_concurrency() - If dbs is allowed for current + * concurreny + * @new_conn_mode: new connection mode + * + * When a new connection is about to come up, check if dbs is allowed for + * STA+STA or STA+P2P + * + * Return: true if dbs is allowed for STA+STA or STA+P2P else false + */ +bool policy_mgr_is_dbs_allowed_for_concurrency( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE new_conn_mode); + +/** + * policy_mgr_get_preferred_dbs_action_table() - get dbs action table type + * @psoc: Pointer to psoc + * @vdev_id: vdev Id + * @ch_freq: channel frequency of vdev. + * @reason: reason of request + * + * 1. Based on band preferred and vdev priority setting to choose the preferred + * dbs action. + * 2. This routine will be used to get DBS switching action tables. + * In Genoa, two action tables for DBS1 (2x2 5G + 1x1 2G), DBS2 + * (2x2 2G + 1x1 5G). + * 3. It can be used in mode change case in CSA channel switching or Roaming, + * opportunistic upgrade. If needs switch to DBS, we needs to query this + * function to get preferred DBS mode. + * 4. This is mainly used for dual dbs mode HW. For Legacy HW, there is + * only single DBS mode. This function will return PM_NOP. + * + * return : PM_NOP, PM_DBS1, PM_DBS2 + */ +enum policy_mgr_conc_next_action +policy_mgr_get_preferred_dbs_action_table( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint32_t ch_freq, + enum policy_mgr_conn_update_reason reason); + +/** + * policy_mgr_is_ibss_conn_exist() - to check if IBSS connection already present + * @psoc: PSOC object information + * @ibss_ch_freq: pointer to ibss channel which needs to be filled + * + * this routine will check if IBSS connection already exist or no. If it + * exist then this routine will return true and fill the ibss_channel value. + * + * Return: true if ibss connection exist else false + */ +bool policy_mgr_is_ibss_conn_exist(struct wlan_objmgr_psoc *psoc, + uint32_t *ibss_ch_freq); + +/** + * policy_mgr_get_conn_info() - get the current connections list + * @len: length of the list + * + * This function returns a pointer to the current connections + * list + * + * Return: pointer to connection list + */ +struct policy_mgr_conc_connection_info *policy_mgr_get_conn_info( + uint32_t *len); +#ifdef MPC_UT_FRAMEWORK +/** + * policy_mgr_incr_connection_count_utfw() - adds the new + * connection to the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id + * @tx_streams: number of transmit spatial streams + * @rx_streams: number of receive spatial streams + * @chain_mask: chain mask + * @type: connection type + * @sub_type: connection subtype + * @ch_freq: channel frequency value + * @mac_id: mac id + * + * This function adds the new connection to the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS +policy_mgr_incr_connection_count_utfw(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint32_t tx_streams, + uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, + uint32_t sub_type, + uint32_t ch_freq, uint32_t mac_id); + +/** + * policy_mgr_update_connection_info_utfw() - updates the + * existing connection in the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id + * @tx_streams: number of transmit spatial streams + * @rx_streams: number of receive spatial streams + * @chain_mask: chain mask + * @type: connection type + * @sub_type: connection subtype + * @ch_freq: channel frequency value + * @mac_id: mac id + * + * This function updates the connection to the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS +policy_mgr_update_connection_info_utfw(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint32_t tx_streams, + uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, + uint32_t sub_type, + uint32_t ch_freq, uint32_t mac_id); + +/** + * policy_mgr_decr_connection_count_utfw() - remove the old + * connection from the current connections list + * @psoc: PSOC object information + * @del_all: delete all entries + * @vdev_id: vdev id + * + * This function removes the old connection from the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_decr_connection_count_utfw(struct wlan_objmgr_psoc *psoc, + uint32_t del_all, uint32_t vdev_id); + +/** + * policy_mgr_get_pcl_from_first_conn_table() - Get PCL for new + * connection from first connection table + * @type: Connection mode of type 'policy_mgr_con_mode' + * @sys_pref: System preference + * + * Get the PCL for a new connection + * + * Return: PCL channels enum + */ +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_first_conn_table( + enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref); + +/** + * policy_mgr_get_pcl_from_second_conn_table() - Get PCL for new + * connection from second connection table + * @idx: index into first connection table + * @type: Connection mode of type 'policy_mgr_con_mode' + * @sys_pref: System preference + * @dbs_capable: if HW DBS capable + * + * Get the PCL for a new connection + * + * Return: PCL channels enum + */ +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_second_conn_table( + enum policy_mgr_one_connection_mode idx, enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable); + +/** + * policy_mgr_get_pcl_from_third_conn_table() - Get PCL for new + * connection from third connection table + * @idx: index into second connection table + * @type: Connection mode of type 'policy_mgr_con_mode' + * @sys_pref: System preference + * @dbs_capable: if HW DBS capable + * + * Get the PCL for a new connection + * + * Return: PCL channels enum + */ +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_third_conn_table( + enum policy_mgr_two_connection_mode idx, enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable); +#else +static inline QDF_STATUS policy_mgr_incr_connection_count_utfw( + struct wlan_objmgr_psoc *psoc, uint32_t vdev_id, + uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t ch_freq, uint32_t mac_id) +{ + return QDF_STATUS_SUCCESS; +} +static inline QDF_STATUS policy_mgr_update_connection_info_utfw( + struct wlan_objmgr_psoc *psoc, uint32_t vdev_id, + uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t ch_freq, uint32_t mac_id) +{ + return QDF_STATUS_SUCCESS; +} +static inline QDF_STATUS policy_mgr_decr_connection_count_utfw( + struct wlan_objmgr_psoc *psoc, uint32_t del_all, + uint32_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * policy_mgr_convert_device_mode_to_qdf_type() - provides the + * type translation from HDD to policy manager type + * @device_mode: Generic connection mode type + * + * + * This function provides the type translation + * + * Return: policy_mgr_con_mode enum + */ +enum policy_mgr_con_mode policy_mgr_convert_device_mode_to_qdf_type( + enum QDF_OPMODE device_mode); + +/** + * policy_mgr_get_qdf_mode_from_pm - provides the + * type translation from policy manager type + * to generic connection mode type + * @device_mode: policy manager mode type + * + * + * This function provides the type translation + * + * Return: QDF_OPMODE enum + */ +enum QDF_OPMODE policy_mgr_get_qdf_mode_from_pm( + enum policy_mgr_con_mode device_mode); + +/** + * policy_mgr_check_n_start_opportunistic_timer - check single mac upgrade + * needed or not, if needed start the oppurtunistic timer. + * @psoc: pointer to SOC + * + * This function starts the oppurtunistic timer if hw_mode change is needed + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_check_n_start_opportunistic_timer( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_pdev_set_hw_mode() - Set HW mode command to FW + * @psoc: PSOC object information + * @session_id: Session ID + * @mac0_ss: MAC0 spatial stream configuration + * @mac0_bw: MAC0 bandwidth configuration + * @mac1_ss: MAC1 spatial stream configuration + * @mac1_bw: MAC1 bandwidth configuration + * @mac0_band_cap: mac0 band capability requirement + * (0: Don't care, 1: 2.4G, 2: 5G) + * @dbs: HW DBS capability + * @dfs: HW Agile DFS capability + * @sbs: HW SBS capability + * @reason: Reason for connection update + * @next_action: next action to happen at policy mgr after + * HW mode change + * @action: action to be applied before hw mode change + * + * Sends the set hw mode request to FW + * + * e.g.: To configure 2x2_80 + * mac0_ss = HW_MODE_SS_2x2, mac0_bw = HW_MODE_80_MHZ + * mac1_ss = HW_MODE_SS_0x0, mac1_bw = HW_MODE_BW_NONE + * mac0_band_cap = HW_MODE_MAC_BAND_NONE, + * dbs = HW_MODE_DBS_NONE, dfs = HW_MODE_AGILE_DFS_NONE, + * sbs = HW_MODE_SBS_NONE + * e.g.: To configure 1x1_80_1x1_40 (DBS) + * mac0_ss = HW_MODE_SS_1x1, mac0_bw = HW_MODE_80_MHZ + * mac1_ss = HW_MODE_SS_1x1, mac1_bw = HW_MODE_40_MHZ + * mac0_band_cap = HW_MODE_MAC_BAND_NONE, + * dbs = HW_MODE_DBS, dfs = HW_MODE_AGILE_DFS_NONE, + * sbs = HW_MODE_SBS_NONE + * e.g.: To configure 1x1_80_1x1_40 (Agile DFS) + * mac0_ss = HW_MODE_SS_1x1, mac0_bw = HW_MODE_80_MHZ + * mac1_ss = HW_MODE_SS_1x1, mac1_bw = HW_MODE_40_MHZ + * mac0_band_cap = HW_MODE_MAC_BAND_NONE, + * dbs = HW_MODE_DBS, dfs = HW_MODE_AGILE_DFS, + * sbs = HW_MODE_SBS_NONE + * e.g.: To configure 2x2_5g_80+1x1_2g_40 + * mac0_ss = HW_MODE_SS_2x2, mac0_bw = HW_MODE_80_MHZ + * mac1_ss = HW_MODE_SS_1x1, mac1_bw = HW_MODE_40_MHZ + * mac0_band_cap = HW_MODE_MAC_BAND_5G + * dbs = HW_MODE_DBS, dfs = HW_MODE_AGILE_DFS_NONE, + * sbs = HW_MODE_SBS_NONE + * e.g.: To configure 2x2_2g_40+1x1_5g_40 + * mac0_ss = HW_MODE_SS_2x2, mac0_bw = HW_MODE_40_MHZ + * mac1_ss = HW_MODE_SS_1x1, mac1_bw = HW_MODE_40_MHZ + * mac0_band_cap = HW_MODE_MAC_BAND_2G + * dbs = HW_MODE_DBS, dfs = HW_MODE_AGILE_DFS_NONE, + * sbs = HW_MODE_SBS_NONE + * + * Return: Success if the message made it down to the next layer + */ +QDF_STATUS policy_mgr_pdev_set_hw_mode(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + enum hw_mode_ss_config mac0_ss, + enum hw_mode_bandwidth mac0_bw, + enum hw_mode_ss_config mac1_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs, + enum policy_mgr_conn_update_reason reason, + uint8_t next_action, enum policy_mgr_conc_next_action action); + +/** + * policy_mgr_pdev_set_hw_mode_cback() - callback invoked by + * other component to provide set HW mode request status + * @status: status of the request + * @cfgd_hw_mode_index: new HW mode index + * @num_vdev_mac_entries: Number of mac entries + * @vdev_mac_map: The table of vdev to mac mapping + * @next_action: next action to happen at policy mgr after + * beacon update + * @reason: Reason for set HW mode + * @session_id: vdev id on which the request was made + * @context: PSOC object information + * + * This function is the callback registered with SME at set HW + * mode request time + * + * Return: None + */ +typedef void (*policy_mgr_pdev_set_hw_mode_cback)(uint32_t status, + uint32_t cfgd_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id, void *context); + +/** + * policy_mgr_nss_update_cback() - callback invoked by other + * component to provide nss update request status + * @psoc: PSOC object information + * @tx_status: tx completion status for updated beacon with new + * nss value + * @vdev_id: vdev id for the specific connection + * @next_action: next action to happen at policy mgr after + * beacon update + * @reason: Reason for nss update + * @original_vdev_id: original request hwmode change vdev id + * + * This function is the callback registered with SME at nss + * update request time + * + * Return: None + */ +typedef void (*policy_mgr_nss_update_cback)(struct wlan_objmgr_psoc *psoc, + uint8_t tx_status, + uint8_t vdev_id, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id); + +/** + * struct policy_mgr_sme_cbacks - SME Callbacks to be invoked + * from policy manager + * @sme_get_valid_channels: Get valid channel list + * @sme_get_nss_for_vdev: Get the allowed nss value for the vdev + * @sme_soc_set_dual_mac_config: Set the dual MAC scan & FW + * config + * @sme_pdev_set_hw_mode: Set the new HW mode to FW + * @sme_pdev_set_pcl: Set new PCL to FW + * @sme_nss_update_request: Update NSS value to FW + * @sme_change_mcc_beacon_interval: Set MCC beacon interval to FW + */ +struct policy_mgr_sme_cbacks { + void (*sme_get_nss_for_vdev)(enum QDF_OPMODE, + uint8_t *nss_2g, uint8_t *nss_5g); + QDF_STATUS (*sme_soc_set_dual_mac_config)( + struct policy_mgr_dual_mac_config msg); + QDF_STATUS (*sme_pdev_set_hw_mode)(struct policy_mgr_hw_mode msg); + QDF_STATUS (*sme_pdev_set_pcl)(struct policy_mgr_pcl_list *msg); + QDF_STATUS (*sme_nss_update_request)(uint32_t vdev_id, + uint8_t new_nss, uint8_t ch_width, + policy_mgr_nss_update_cback cback, + uint8_t next_action, struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id); + QDF_STATUS (*sme_change_mcc_beacon_interval)(uint8_t session_id); + QDF_STATUS (*sme_get_ap_channel_from_scan)( + void *roam_profile, + void **scan_cache, + uint32_t *ch_freq); + QDF_STATUS (*sme_scan_result_purge)( + void *scan_result); +}; + +/** + * struct policy_mgr_hdd_cbacks - HDD Callbacks to be invoked + * from policy manager + * @sap_restart_chan_switch_cb: Restart SAP + * @wlan_hdd_get_channel_for_sap_restart: Get channel to restart + * SAP + * @get_mode_for_non_connected_vdev: Get the mode for a non + * connected vdev + * @hdd_get_device_mode: Get QDF_OPMODE type for session id (vdev id) + * @hdd_is_chan_switch_in_progress: Check if in any adater channel switch is in + * progress + * @wlan_hdd_set_sap_csa_reason: Set the sap csa reason in cases like NAN. + * @hdd_get_ap_6ghz_capable: get ap vdev 6ghz capable info from hdd ap adapter. + * @wlan_hdd_indicate_active_ndp_cnt: indicate active ndp cnt to hdd + */ +struct policy_mgr_hdd_cbacks { + void (*sap_restart_chan_switch_cb)(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t ch_freq, + uint32_t channel_bw, + bool forced); + QDF_STATUS (*wlan_hdd_get_channel_for_sap_restart)( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t *ch_freq); + enum policy_mgr_con_mode (*get_mode_for_non_connected_vdev)( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + enum QDF_OPMODE (*hdd_get_device_mode)(uint32_t session_id); + bool (*hdd_is_chan_switch_in_progress)(void); + bool (*hdd_is_cac_in_progress)(void); + void (*wlan_hdd_set_sap_csa_reason)(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t reason); + uint32_t (*hdd_get_ap_6ghz_capable)(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + void (*wlan_hdd_indicate_active_ndp_cnt)(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t cnt); +}; + + +/** + * struct policy_mgr_tdls_cbacks - TDLS Callbacks to be invoked + * from policy manager + * @set_tdls_ct_mode: Set the tdls connection tracker mode + * @check_is_tdls_allowed: check if tdls allowed or not + */ +struct policy_mgr_tdls_cbacks { + void (*tdls_notify_increment_session)(struct wlan_objmgr_psoc *psoc); + void (*tdls_notify_decrement_session)(struct wlan_objmgr_psoc *psoc); +}; + +/** + * struct policy_mgr_cdp_cbacks - CDP Callbacks to be invoked + * from policy manager + * @cdp_update_mac_id: update mac_id for vdev + */ +struct policy_mgr_cdp_cbacks { + void (*cdp_update_mac_id)(struct wlan_objmgr_psoc *soc, + uint8_t vdev_id, uint8_t mac_id); +}; + +/** + * struct policy_mgr_dp_cbacks - CDP Callbacks to be invoked + * from policy manager + * @hdd_disable_rx_ol_in_concurrency: Callback to disable LRO/GRO offloads + * @hdd_set_rx_mode_rps_cb: Callback to set RPS + * @hdd_ipa_set_mcc_mode_cb: Callback to set mcc mode for ipa module + * @hdd_v2_flow_pool_map: Callback to create vdev flow pool + * @hdd_v2_flow_pool_unmap: Callback to delete vdev flow pool + */ +struct policy_mgr_dp_cbacks { + void (*hdd_disable_rx_ol_in_concurrency)(bool); + void (*hdd_set_rx_mode_rps_cb)(bool); + void (*hdd_ipa_set_mcc_mode_cb)(bool); + void (*hdd_v2_flow_pool_map)(int); + void (*hdd_v2_flow_pool_unmap)(int); +}; + +/** + * struct policy_mgr_wma_cbacks - WMA Callbacks to be invoked + * from policy manager + * @wma_get_connection_info: Get the connection related info + * from wma table + */ +struct policy_mgr_wma_cbacks { + QDF_STATUS (*wma_get_connection_info)(uint8_t vdev_id, + struct policy_mgr_vdev_entry_info *conn_table_entry); +}; + +/** +* policy_mgr_need_opportunistic_upgrade - check whether needs to change current +* HW mode to single mac 2x2 or the other DBS mode(for Dual DBS HW only). +* @psoc: PSOC object information +* @reason: enum policy_mgr_conn_update_reason +* +* This function is to check whether needs to change to single Mac mode. +* when opportunistic timer fired. But a special case for Dual DBS HW, this +* function will check DBS to DBS change is required or not: +* 1. For Dual DBS HW, if user set vdev priority list, we may need to do +* DBS to DBS switching. +* eg. P2P GO (2g) < SAP (5G) < STA (2g) in DBS2. +* If STA down, we need to switch to DBS1: P2P GO (2g) < SAP (5g). +* So, for opportunistic checking, we need to add DBS ->DBS checking +* as well. +* 2. Reason code : +* DBS -> Single MAC : POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC +* DBS -> DBS : POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE +* +* return: PM_NOP, upgrade is not needed, otherwise new action type +* and reason code be returned. +*/ +enum policy_mgr_conc_next_action policy_mgr_need_opportunistic_upgrade( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conn_update_reason *reason); + +/** + * policy_mgr_next_actions() - initiates actions needed on current + * connections once channel has been decided for the new + * connection + * @psoc: PSOC object information + * @session_id: Session id + * @action: action to be executed + * @reason: Reason for connection update + * + * This function initiates initiates actions + * needed on current connections once channel has been decided + * for the new connection. Notifies UMAC & FW as well + * + * Return: QDF_STATUS enum + */ +QDF_STATUS policy_mgr_next_actions(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + enum policy_mgr_conc_next_action action, + enum policy_mgr_conn_update_reason reason); + +/** + * policy_mgr_validate_dbs_switch() - Check DBS action valid or not + * @psoc: Pointer to psoc + * @action: action requested + * + * This routine will check the current hw mode with requested action. + * If we are already in the mode, the caller will do nothing. + * This will be called by policy_mgr_next_actions to check the action needed + * or not. + * + * return : QDF_STATUS_SUCCESS, action is allowed. + * QDF_STATUS_E_ALREADY, action is not needed. + * QDF_STATUS_E_FAILURE, error happens. + * QDF_STATUS_E_NOSUPPORT, the requested mode not supported. + */ +QDF_STATUS +policy_mgr_validate_dbs_switch(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conc_next_action action); + +/** + * policy_mgr_set_dual_mac_scan_config() - Set the dual MAC scan config + * @psoc: PSOC object information + * @dbs_val: Value of DBS bit + * @dbs_plus_agile_scan_val: Value of DBS plus agile scan bit + * @single_mac_scan_with_dbs_val: Value of Single MAC scan with DBS + * + * Set the values of scan config. For FW mode config, the existing values + * will be retained + * + * Return: None + */ +void policy_mgr_set_dual_mac_scan_config(struct wlan_objmgr_psoc *psoc, + uint8_t dbs_val, + uint8_t dbs_plus_agile_scan_val, + uint8_t single_mac_scan_with_dbs_val); + +/** + * policy_mgr_set_dual_mac_fw_mode_config() - Set the dual mac FW mode config + * @psoc: PSOC object information + * @dbs: DBS bit + * @dfs: Agile DFS bit + * + * Set the values of fw mode config. For scan config, the existing values + * will be retain. + * + * Return: None + */ +void policy_mgr_set_dual_mac_fw_mode_config(struct wlan_objmgr_psoc *psoc, + uint8_t dbs, uint8_t dfs); + +/** + * policy_mgr_soc_set_dual_mac_cfg_cb() - Callback for set dual mac config + * @status: Status of set dual mac config + * @scan_config: Current scan config whose status is the first param + * @fw_mode_config: Current FW mode config whose status is the first param + * + * Callback on setting the dual mac configuration + * + * Return: None + */ +void policy_mgr_soc_set_dual_mac_cfg_cb(enum set_hw_mode_status status, + uint32_t scan_config, uint32_t fw_mode_config); + +/** + * policy_mgr_map_concurrency_mode() - to map concurrency mode + * between sme and hdd + * @old_mode: sme provided adapter mode + * @new_mode: hdd provided concurrency mode + * + * This routine will map concurrency mode between sme and hdd + * + * Return: true or false + */ +bool policy_mgr_map_concurrency_mode(enum QDF_OPMODE *old_mode, + enum policy_mgr_con_mode *new_mode); + +/** + * policy_mgr_get_channel_from_scan_result() - to get channel from scan result + * @psoc: PSOC object information + * @roam_profile: pointer to roam profile + * @ch_freq: channel frequency to be filled + * + * This routine gets channel which most likely a candidate to which STA + * will make connection. + * + * Return: QDF_STATUS + */ +QDF_STATUS +policy_mgr_get_channel_from_scan_result(struct wlan_objmgr_psoc *psoc, + void *roam_profile, + uint32_t *ch_freq); + +/** + * policy_mgr_mode_specific_num_open_sessions() - to get number of open sessions + * for a specific mode + * @psoc: PSOC object information + * @mode: device mode + * @num_sessions: to store num open sessions + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_mode_specific_num_open_sessions( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE mode, + uint8_t *num_sessions); + +/** + * policy_mgr_mode_specific_num_active_sessions() - to get number of active + * sessions for a specific mode + * @psoc: PSOC object information + * @mode: device mode + * @num_sessions: to store num active sessions + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_mode_specific_num_active_sessions( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE mode, + uint8_t *num_sessions); + +/** + * policy_mgr_concurrent_open_sessions_running() - Checks for + * concurrent open session + * @psoc: PSOC object information + * + * Checks if more than one open session is running for all the allowed modes + * in the driver + * + * Return: True if more than one open session exists, False otherwise + */ +bool policy_mgr_concurrent_open_sessions_running( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_max_concurrent_connections_reached() - Check if + * max conccurrency is reached + * @psoc: PSOC object information + * Checks for presence of concurrency where more than one connection exists + * + * Return: True if the max concurrency is reached, False otherwise + * + * Example: + * STA + STA (wlan0 and wlan1 are connected) - returns true + * STA + STA (wlan0 connected and wlan1 disconnected) - returns false + * DUT with P2P-GO + P2P-CLIENT connection) - returns true + * + */ +bool policy_mgr_max_concurrent_connections_reached( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_clear_concurrent_session_count() - Clear active session count + * @psoc: PSOC object information + * Clears the active session count for all modes + * + * Return: None + */ +void policy_mgr_clear_concurrent_session_count(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_multiple_active_sta_sessions() - Check for + * multiple STA connections + * @psoc: PSOC object information + * + * Checks if multiple active STA connection are in the driver + * + * Return: True if multiple STA sessions are present, False otherwise + * + */ +bool policy_mgr_is_multiple_active_sta_sessions( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_sta_active_connection_exists() - Check if a STA + * connection is active + * @psoc: PSOC object information + * + * Checks if there is atleast one active STA connection in the driver + * + * Return: True if an active STA session is present, False otherwise + */ +bool policy_mgr_is_sta_active_connection_exists( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_concurrent_beaconing_sessions_running() - Checks + * for concurrent beaconing entities + * @psoc: PSOC object information + * + * Checks if multiple beaconing sessions are running i.e., if SAP or GO or IBSS + * are beaconing together + * + * Return: True if multiple entities are beaconing together, False otherwise + */ +bool policy_mgr_concurrent_beaconing_sessions_running( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_wait_for_connection_update() - Wait for hw mode + * command to get processed + * @psoc: PSOC object information + * Waits for CONNECTION_UPDATE_TIMEOUT duration until the set hw mode + * response sets the event connection_update_done_evt + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_wait_for_connection_update( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_reset_connection_update() - Reset connection + * update event + * @psoc: PSOC object information + * Resets the concurrent connection update event + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_reset_connection_update(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_connection_update() - Set connection update + * event + * @psoc: PSOC object information + * Sets the concurrent connection update event + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_connection_update(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_chan_switch_complete_evt() - set channel + * switch completion event + * @psoc: PSOC object information + * Sets the channel switch completion event. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_chan_switch_complete_evt( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_reset_chan_switch_complete_evt() - reset channel + * switch completion event + * @psoc: PSOC object information + * Resets the channel switch completion event. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_reset_chan_switch_complete_evt( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_opportunistic_update() - Set opportunistic + * update event + * @psoc: PSOC object information + * Sets the opportunistic update event + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_opportunistic_update(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_stop_opportunistic_timer() - Stops opportunistic timer + * @psoc: PSOC object information + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_stop_opportunistic_timer(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_restart_opportunistic_timer() - Restarts opportunistic timer + * @psoc: PSOC object information + * @check_state: check timer state if this flag is set, else restart + * irrespective of state + * + * Restarts opportunistic timer for DBS_OPPORTUNISTIC_TIME seconds. + * Check if current state is RUNNING if check_state is set, else + * restart the timer irrespective of state. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_restart_opportunistic_timer( + struct wlan_objmgr_psoc *psoc, bool check_state); + +/** + * policy_mgr_modify_sap_pcl_based_on_mandatory_channel() - + * Modify SAPs PCL based on mandatory channel list + * @psoc: PSOC object information + * @pcl_list_org: Pointer to the preferred channel freq list to be trimmed + * @weight_list_org: Pointer to the weights of the preferred channel list + * @pcl_len_org: Pointer to the length of the preferred chanel list + * + * Modifies the preferred channel list of SAP based on the mandatory channel + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_modify_sap_pcl_based_on_mandatory_channel( + struct wlan_objmgr_psoc *psoc, uint32_t *pcl_list_org, + uint8_t *weight_list_org, uint32_t *pcl_len_org); + +/** + * policy_mgr_update_and_wait_for_connection_update() - Update and wait for + * connection update + * @psoc: PSOC object information + * @session_id: Session id + * @ch_freq: Channel frequency + * @reason: Reason for connection update + * + * Update the connection to either single MAC or dual MAC and wait for the + * update to complete + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_update_and_wait_for_connection_update( + struct wlan_objmgr_psoc *psoc, uint8_t session_id, + uint32_t ch_freq, enum policy_mgr_conn_update_reason reason); + +/** + * policy_mgr_is_sap_mandatory_channel_set() - Checks if SAP + * mandatory channel is set + * @psoc: PSOC object information + * Checks if any mandatory channel is set for SAP operation + * + * Return: True if mandatory channel is set, false otherwise + */ +bool policy_mgr_is_sap_mandatory_channel_set(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_list_has_24GHz_channel() - Check if list contains 2.4GHz channels + * @channel_list: Channel frequency list + * @list_len: Length of the channel list + * + * Checks if the channel list contains atleast one 2.4GHz channel + * + * Return: True if 2.4GHz channel is present, false otherwise + */ +bool policy_mgr_list_has_24GHz_channel(uint32_t *ch_freq_list, + uint32_t list_len); + +/** + * policy_mgr_get_valid_chans_from_range() - get valid channel from given range + * @psoc: PSOC object information + * @ch_freq_list: Pointer to the channel frequency list + * @ch_cnt: Pointer to the length of the channel list + * @mode: Device mode + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_valid_chans_from_range(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_list, + uint32_t *ch_cnt, + enum policy_mgr_con_mode mode); +/** + * policy_mgr_get_valid_chans() - Get the valid channel list + * @psoc: PSOC object information + * @ch_freq_list: Pointer to the valid channel frequency list + * @list_len: Pointer to the length of the valid channel list + * + * Gets the valid channel list filtered by band + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_valid_chans(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq_list, + uint32_t *list_len); + +/** + * policy_mgr_get_nss_for_vdev() - Get the allowed nss value for the + * vdev + * @psoc: PSOC object information + * @dev_mode: connection type. + * @nss2g: Pointer to the 2G Nss parameter. + * @nss5g: Pointer to the 5G Nss parameter. + * + * Fills the 2G and 5G Nss values based on connection type. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_nss_for_vdev(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t *nss_2g, uint8_t *nss_5g); + +/** + * policy_mgr_get_sap_mandatory_channel() - Get the mandatory channel for SAP + * @psoc: PSOC object information + * @ch_freq: Pointer to the SAP mandatory channel frequency + * + * Gets the mandatory channel for SAP operation + * + * Return: QDF_STATUS + */ +QDF_STATUS +policy_mgr_get_sap_mandatory_channel(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq); + +/** + * policy_mgr_set_sap_mandatory_channels() - Set the mandatory channel for SAP + * @psoc: PSOC object information + * @ch_freq_list: Channel frequency list to be set + * @len: Length of the channel list + * + * Sets the channels for the mandatory channel list along with the length of + * of the channel list. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_sap_mandatory_channels(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq_list, + uint32_t len); + +/** + * policy_mgr_is_any_mode_active_on_band_along_with_session() - + * Check if any connection mode is active on a band along with + * the given session + * @psoc: PSOC object information + * @session_id: Session along which active sessions are looked for + * @band: Operating frequency band of the connection + * POLICY_MGR_BAND_24: Looks for active connection on 2.4 GHz only + * POLICY_MGR_BAND_5: Looks for active connection on 5 GHz only + * + * Checks if any of the connection mode is active on a given frequency band + * + * Return: True if any connection is active on a given band, false otherwise + */ +bool policy_mgr_is_any_mode_active_on_band_along_with_session( + struct wlan_objmgr_psoc *psoc, uint8_t session_id, + enum policy_mgr_band band); + +/** + * policy_mgr_get_chan_by_session_id() - Get channel for a given session ID + * @psoc: PSOC object information + * @session_id: Session ID + * @ch_freq: Pointer to the channel frequency + * + * Gets the channel for a given session ID + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_chan_by_session_id(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + uint32_t *ch_freq); + +/** + * policy_mgr_get_mac_id_by_session_id() - Get MAC ID for a given session ID + * @psoc: PSOC object information + * @session_id: Session ID + * @mac_id: Pointer to the MAC ID + * + * Gets the MAC ID for a given session ID + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_mac_id_by_session_id(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint8_t *mac_id); + +/** + * policy_mgr_get_mcc_session_id_on_mac() - Get MCC session's ID + * @psoc: PSOC object information + * @mac_id: MAC ID on which MCC session needs to be found + * @session_id: Session with which MCC combination needs to be found + * @mcc_session_id: Pointer to the MCC session ID + * + * Get the session ID of the MCC interface + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_mcc_session_id_on_mac(struct wlan_objmgr_psoc *psoc, + uint8_t mac_id, uint8_t session_id, + uint8_t *mcc_session_id); + +/** + * policy_mgr_get_mcc_operating_channel() - Get the MCC channel + * @psoc: PSOC object information + * @session_id: Session ID with which MCC is being done + * + * Gets the MCC channel for a given session ID. + * + * Return: '0' (INVALID_CHANNEL_ID) or valid channel frequency + */ +uint32_t policy_mgr_get_mcc_operating_channel(struct wlan_objmgr_psoc *psoc, + uint8_t session_id); + +/** + * policy_mgr_get_pcl_for_existing_conn() - Get PCL for existing connection + * @psoc: PSOC object information + * @mode: Connection mode of type 'policy_mgr_con_mode' + * @pcl_ch: Pointer to the PCL + * @len: Pointer to the length of the PCL + * @pcl_weight: Pointer to the weights of the PCL + * @weight_len: Max length of the weights list + * @all_matching_cxn_to_del: Need remove all entries before getting pcl + * + * Get the PCL for an existing connection + * + * Return: None + */ +QDF_STATUS policy_mgr_get_pcl_for_existing_conn( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t *pcl_ch, uint32_t *len, + uint8_t *pcl_weight, uint32_t weight_len, + bool all_matching_cxn_to_del); + +/** + * policy_mgr_get_valid_chan_weights() - Get the weightage for + * all valid channels + * @psoc: PSOC object information + * @weight: Pointer to the structure containing pcl, saved channel list and + * weighed channel list + * + * Provides the weightage for all valid channels. This compares the PCL list + * with the valid channel list. The channels present in the PCL get their + * corresponding weightage and the non-PCL channels get the default weightage + * of WEIGHT_OF_NON_PCL_CHANNELS. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_valid_chan_weights(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_pcl_chan_weights *weight); + +/** + * policy_mgr_set_hw_mode_on_channel_switch() - Set hw mode + * after channel switch + * @session_id: Session ID + * + * Sets hw mode after doing a channel switch + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_hw_mode_on_channel_switch( + struct wlan_objmgr_psoc *psoc, uint8_t session_id); + +/** + * policy_mgr_check_and_set_hw_mode_for_channel_switch() - check if hw mode + * change is required before channel switch for STA/SAP, + * this is required if DBS mode is 2x2 + * @psoc: PSOC object information + * @vdev_id: vdev id on which channel switch is required + * @ch_freq: New channel frequency to which channel switch is requested + * @reason: reason for hw mode change + * + * Return: QDF_STATUS, success if HW mode change is required else Failure + */ +QDF_STATUS policy_mgr_check_and_set_hw_mode_for_channel_switch( + struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + uint32_t ch_freq, enum policy_mgr_conn_update_reason reason); + +/** + * policy_mgr_checkn_update_hw_mode_single_mac_mode() - Set hw_mode to SMM + * if required + * @psoc: PSOC object information + * @ch_freq: channel frequency for the new STA connection + * + * After the STA disconnection, if the hw_mode is in DBS and the new STA + * connection is coming in the band in which existing connections are + * present, then this function stops the dbs opportunistic timer and sets + * the hw_mode to Single MAC mode (SMM). + * + * Return: None + */ +void policy_mgr_checkn_update_hw_mode_single_mac_mode( + struct wlan_objmgr_psoc *psoc, uint32_t ch_freq); + +/** + * policy_mgr_dump_connection_status_info() - Dump the concurrency information + * @psoc: PSOC object information + * Prints the concurrency information such as tx/rx spatial stream, chainmask, + * etc. + * + * Return: None + */ +void policy_mgr_dump_connection_status_info(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_mode_specific_vdev_id() - provides the + * vdev id of the pecific mode + * @psoc: PSOC object information + * @mode: type of connection + * + * This function provides vdev id for the given mode + * + * Return: vdev id + */ +uint32_t policy_mgr_mode_specific_vdev_id(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode); + +/** + * policy_mgr_mode_specific_connection_count() - provides the + * count of connections of specific mode + * @psoc: PSOC object information + * @mode: type of connection + * @list: To provide the indices on pm_conc_connection_list + * (optional) + * + * This function provides the count of current connections + * + * Return: connection count of specific type + */ +uint32_t policy_mgr_mode_specific_connection_count( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode, + uint32_t *list); + +/** + * policy_mgr_check_conn_with_mode_and_vdev_id() - checks if any active + * session with specific mode and vdev_id + * @psoc: PSOC object information + * @mode: type of connection + * @vdev_id: vdev_id of the connection + * + * This function checks if any active session with specific mode and vdev_id + * is present + * + * Return: QDF STATUS with success if active session is found, else failure + */ +QDF_STATUS policy_mgr_check_conn_with_mode_and_vdev_id( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode, + uint32_t vdev_id); + +/** + * policy_mgr_hw_mode_transition_cb() - Callback for HW mode + * transition from FW + * @old_hw_mode_index: Old HW mode index + * @new_hw_mode_index: New HW mode index + * @num_vdev_mac_entries: Number of vdev-mac id mapping that follows + * @vdev_mac_map: vdev-mac id map. This memory will be freed by the caller. + * So, make local copy if needed. + * + * Provides the old and new HW mode index set by the FW + * + * Return: None + */ +void policy_mgr_hw_mode_transition_cb(uint32_t old_hw_mode_index, + uint32_t new_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + struct wlan_objmgr_psoc *context); + +/** + * policy_mgr_current_concurrency_is_mcc() - To check the current + * concurrency combination if it is doing MCC + * @psoc: PSOC object information + * This routine is called to check if it is doing MCC + * + * Return: True - MCC, False - Otherwise + */ +bool policy_mgr_current_concurrency_is_mcc(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_sap_p2pgo_on_dfs() - check if there is a P2PGO or SAP + * operating in a DFS channel + * @psoc: PSOC object information + * This routine is called to check if there is a P2PGO/SAP on DFS channel + * + * Return: True - P2PGO/SAP present on DFS Channel + * False - Otherwise + */ + +bool policy_mgr_is_sap_p2pgo_on_dfs(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_register_sme_cb() - register SME callbacks + * @psoc: PSOC object information + * @sme_cbacks: function pointers from SME + * + * API, allows SME to register callbacks to be invoked by policy + * mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_sme_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_sme_cbacks *sme_cbacks); + +/** + * policy_mgr_register_hdd_cb() - register HDD callbacks + * @psoc: PSOC object information + * @hdd_cbacks: function pointers from HDD + * + * API, allows HDD to register callbacks to be invoked by policy + * mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_hdd_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_hdd_cbacks *hdd_cbacks); + +/** + * policy_mgr_deregister_hdd_cb() - Deregister HDD callbacks + * @psoc: PSOC object information + * + * API, allows HDD to deregister callbacks + * + * Return: SUCCESS, + * Failure (if de-registration fails) + */ +QDF_STATUS policy_mgr_deregister_hdd_cb(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_register_tdls_cb() - register TDLS callbacks + * @psoc: PSOC object information + * @tdls_cbacks: function pointers from TDLS + * + * API, allows TDLS to register callbacks to be invoked by + * policy mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_tdls_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_tdls_cbacks *tdls_cbacks); + +/** + * policy_mgr_register_cdp_cb() - register CDP callbacks + * @psoc: PSOC object information + * @cdp_cbacks: function pointers from CDP + * + * API, allows CDP to register callbacks to be invoked by + * policy mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_cdp_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_cdp_cbacks *cdp_cbacks); + +/** + * policy_mgr_register_dp_cb() - register CDP callbacks + * @psoc: PSOC object information + * @cdp_cbacks: function pointers from CDP + * + * API, allows CDP to register callbacks to be invoked by + * policy mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_dp_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_dp_cbacks *dp_cbacks); + +/** + * policy_mgr_register_wma_cb() - register WMA callbacks + * @psoc: PSOC object information + * @wma_cbacks: function pointers from WMA + * + * API, allows WMA to register callbacks to be invoked by policy + * mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_wma_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_wma_cbacks *wma_cbacks); + +/** + * policy_mgr_find_if_fw_supports_dbs() - to find if FW/HW supports DBS + * @psoc: PSOC object information + * + * This API checks if legacy service ready event contains DBS or no. + * This API doesn't check service ready extension which contains actual + * hw mode list that tells if all supported HW modes' caps. + * + * Return: true (if service ready indication supports DBS or no) else false + * + */ +bool policy_mgr_find_if_fw_supports_dbs(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_dbs_enable() - Check if master DBS control is enabled + * @psoc: PSOC object information + * Checks if the master DBS control is enabled. This will be used + * to override any other DBS capability + * + * Return: True if master DBS control is enabled + */ +bool policy_mgr_is_dbs_enable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_hw_dbs_capable() - Check if HW is DBS capable + * @psoc: PSOC object information + * Checks if the HW is DBS capable + * + * Return: true if the HW is DBS capable + */ +bool policy_mgr_is_hw_dbs_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_dbs_scan_allowed() - Check if DBS scan is allowed or not + * @psoc: PSOC object information + * Checks if the DBS scan can be performed or not + * + * Return: true if DBS scan is allowed. + */ +bool policy_mgr_is_dbs_scan_allowed(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_hw_sbs_capable() - Check if HW is SBS capable + * @psoc: PSOC object information + * Checks if the HW is SBS capable + * + * Return: true if the HW is SBS capable + */ +bool policy_mgr_is_hw_sbs_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_current_hwmode_dbs() - Check if current hw mode is DBS + * @psoc: PSOC object information + * Checks if current hardware mode of the system is DBS or no + * + * Return: true or false + */ +bool policy_mgr_is_current_hwmode_dbs(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_dp_hw_dbs_2x2_capable() - if hardware is capable of dbs 2x2 + * for Data Path. + * @psoc: PSOC object information + * This API is for Data Path to get HW dbs 2x2 capable. + * + * Return: true - DBS2x2, false - DBS1x1 + */ +bool policy_mgr_is_dp_hw_dbs_2x2_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_hw_dbs_2x2_capable() - if hardware is capable of dbs 2x2 + * @psoc: PSOC object information + * This function checks if hw_modes supported are always capable of + * DBS and there is no need for downgrading while entering DBS. + * true: DBS 2x2 can always be supported + * false: hw_modes support DBS 1x1 as well + * Genoa DBS 2x2 + 1x1 will not be included. + * + * Return: true - DBS2x2, false - DBS1x1 + */ +bool policy_mgr_is_hw_dbs_2x2_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_hw_dbs_required_for_band() - Check whether hardware needs DBS + * mode to support the given band + * @psoc: PSOC object information + * @band: band + * + * The function checks whether DBS mode switching required or not to support + * given band based on target capability. + * Any HW which doesn't support given band on PHY A will need DBS HW mode when a + * connection is coming up on that band. + * + * Return: true - DBS mode required for requested band + */ +bool policy_mgr_is_hw_dbs_required_for_band(struct wlan_objmgr_psoc *psoc, + enum hw_mode_mac_band_cap band); + +/* + * policy_mgr_is_2x2_1x1_dbs_capable() - check 2x2+1x1 DBS supported or not + * @psoc: PSOC object data + * + * This routine is called to check 2x2 5G + 1x1 2G (DBS1) or + * 2x2 2G + 1x1 5G (DBS2) support or not. + * Either DBS1 or DBS2 supported + * + * Return: true/false + */ +bool policy_mgr_is_2x2_1x1_dbs_capable(struct wlan_objmgr_psoc *psoc); + +/* + * policy_mgr_is_2x2_5G_1x1_2G_dbs_capable() - check Genoa DBS1 enabled or not + * @psoc: PSOC object data + * + * This routine is called to check support DBS1 or not. + * Notes: DBS1: 2x2 5G + 1x1 2G. + * This function will call policy_mgr_get_hw_mode_idx_from_dbs_hw_list to match + * the HW mode from hw mode list. The parameters will also be matched to + * 2x2 5G +2x2 2G HW mode. But firmware will not report 2x2 5G + 2x2 2G alone + * with 2x2 5G + 1x1 2G at same time. So, it is safe to find DBS1 with + * policy_mgr_get_hw_mode_idx_from_dbs_hw_list. + * + * Return: true/false + */ +bool policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(struct wlan_objmgr_psoc *psoc); + +/* + * policy_mgr_is_2x2_2G_1x1_5G_dbs_capable() - check Genoa DBS2 enabled or not + * @psoc: PSOC object data + * + * This routine is called to check support DBS2 or not. + * Notes: DBS2: 2x2 2G + 1x1 5G + * + * Return: true/false + */ +bool policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_init() - Policy Manager component initialization + * routine + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_init(void); + +/** + * policy_mgr_deinit() - Policy Manager component + * de-initialization routine + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_deinit(void); + +/** + * policy_mgr_psoc_enable() - Policy Manager component + * enable routine + * @psoc: PSOC object information + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_psoc_disable() - Policy Manager component + * disable routine + * @psoc: PSOC object information + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_psoc_disable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_psoc_open() - Policy Manager component + * open routine + * @psoc: PSOC object information + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_psoc_close() - Policy Manager component + * close routine + * @psoc: PSOC object information + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_num_dbs_hw_modes() - Get number of HW mode + * @psoc: PSOC object information + * Fetches the number of DBS HW modes returned by the FW + * + * Return: Negative value on error or returns the number of DBS HW modes + */ +int8_t policy_mgr_get_num_dbs_hw_modes(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_dbs_hw_modes() - Get the DBS HW modes for userspace + * @psoc: PSOC object information + * @one_by_one_dbs: 1x1 DBS capability of HW + * @two_by_two_dbs: 2x2 DBS capability of HW + * + * Provides the DBS HW mode capability such as whether + * 1x1 DBS, 2x2 DBS is supported by the HW or not. + * + * Return: Failure in case of error and 0 on success + * one_by_one_dbs/two_by_two_dbs will be false, + * if they are not supported. + * one_by_one_dbs/two_by_two_dbs will be true, + * if they are supported. + * false values of one_by_one_dbs/two_by_two_dbs, + * indicate DBS is disabled + */ +QDF_STATUS policy_mgr_get_dbs_hw_modes(struct wlan_objmgr_psoc *psoc, + bool *one_by_one_dbs, bool *two_by_two_dbs); + +/** + * policy_mgr_check_sta_ap_concurrent_ch_intf() - Restart SAP in STA-AP case + * @data: Pointer to STA adapter + * + * Restarts the SAP interface in STA-AP concurrency scenario + * + * Restart: None + */ +void policy_mgr_check_sta_ap_concurrent_ch_intf(void *data); + +/** + * policy_mgr_get_current_hw_mode() - Get current HW mode params + * @psoc: PSOC object information + * @hw_mode: HW mode parameters + * + * Provides the current HW mode parameters if the HW mode is initialized + * in the driver + * + * Return: Success if the current HW mode params are successfully populated + */ +QDF_STATUS policy_mgr_get_current_hw_mode(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_hw_mode_params *hw_mode); + +/** + * policy_mgr_get_dbs_plus_agile_scan_config() - Get DBS plus agile scan bit + * @psoc: PSOC object information + * Gets the DBS plus agile scan bit of concurrent_scan_config_bits + * + * Return: 0 or 1 to indicate the DBS plus agile scan bit + */ +bool policy_mgr_get_dbs_plus_agile_scan_config(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_single_mac_scan_with_dfs_config() - Get Single + * MAC scan with DFS bit + * @psoc: PSOC object information + * Gets the Single MAC scan with DFS bit of concurrent_scan_config_bits + * + * Return: 0 or 1 to indicate the Single MAC scan with DFS bit + */ +bool policy_mgr_get_single_mac_scan_with_dfs_config( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_hw_mode_change_in_progress() - Set value + * corresponding to policy_mgr_hw_mode_change that indicate if + * HW mode change is in progress + * @psoc: PSOC object information + * @value: Indicate if hw mode change is in progress + * + * Set the value corresponding to policy_mgr_hw_mode_change that + * indicated if hw mode change is in progress. + * + * Return: None + */ +void policy_mgr_set_hw_mode_change_in_progress( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_hw_mode_change value); + +/** + * policy_mgr_is_hw_mode_change_in_progress() - Check if HW mode + * change is in progress. + * @psoc: PSOC object information + * + * Returns the corresponding policy_mgr_hw_mode_change value. + * + * Return: policy_mgr_hw_mode_change value. + */ +enum policy_mgr_hw_mode_change policy_mgr_is_hw_mode_change_in_progress( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_hw_mode_change_from_hw_mode_index() - Get + * matching HW mode from index + * @psoc: PSOC object information + * @hw_mode_index: HW mode index + * Returns the corresponding policy_mgr_hw_mode_change HW mode. + * + * Return: policy_mgr_hw_mode_change value. + */ +enum policy_mgr_hw_mode_change policy_mgr_get_hw_mode_change_from_hw_mode_index( + struct wlan_objmgr_psoc *psoc, uint32_t hw_mode_index); + +/** + * policy_mgr_is_scan_simultaneous_capable() - Check if scan + * parallelization is supported or not + * @psoc: PSOC object information + * currently scan parallelization feature support is dependent on DBS but + * it can be independent in future. + * + * Return: True if master DBS control is enabled + */ +bool policy_mgr_is_scan_simultaneous_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_user_cfg() - Function to set user cfg variables + * required by policy manager component + * @psoc: PSOC object information + * @user_cfg: User config valiables structure pointer + * + * This function sets the user cfg variables required by policy + * manager + * + * Return: SUCCESS or FAILURE + * + */ +QDF_STATUS policy_mgr_set_user_cfg(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_user_cfg *user_cfg); + +/** + * policy_mgr_init_dbs_config() - Function to initialize DBS + * config in policy manager component + * @psoc: PSOC object information + * @scan_config: DBS scan config + * @fw_config: DBS FW config + * + * This function sets the DBS configurations required by policy + * manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_init_dbs_config(struct wlan_objmgr_psoc *psoc, + uint32_t scan_config, uint32_t fw_config); + +/** + * policy_mgr_update_dbs_scan_config() - Function to update + * DBS scan config in policy manager component + * @psoc: PSOC object information + * + * This function updates the DBS scan configurations required by + * policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_dbs_scan_config(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_update_dbs_fw_config() - Function to update DBS FW + * config in policy manager component + * @psoc: PSOC object information + * + * This function updates the DBS FW configurations required by + * policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_dbs_fw_config(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_update_dbs_req_config() - Function to update DBS + * request config in policy manager component + * @psoc: PSOC object information + * @scan_config: DBS scan config + * @fw_config: DBS FW config + * + * This function updates DBS request configurations required by + * policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_dbs_req_config(struct wlan_objmgr_psoc *psoc, + uint32_t scan_config, uint32_t fw_mode_config); + +/** + * policy_mgr_dump_dbs_hw_mode() - Function to dump DBS config + * @psoc: PSOC object information + * + * This function dumps the DBS configurations + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_dump_dbs_hw_mode(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_init_dbs_hw_mode() - Function to initialize DBS HW + * modes in policy manager component + * @psoc: PSOC object information + * @num_dbs_hw_modes: Number of HW modes + * @ev_wlan_dbs_hw_mode_list: HW list + * + * This function to initialize the DBS HW modes in policy + * manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_init_dbs_hw_mode(struct wlan_objmgr_psoc *psoc, + uint32_t num_dbs_hw_modes, + uint32_t *ev_wlan_dbs_hw_mode_list); + +/** + * policy_mgr_update_hw_mode_list() - Function to initialize DBS + * HW modes in policy manager component + * @psoc: PSOC object information + * @tgt_hdl: Target psoc information + * + * This function to initialize the DBS HW modes in policy + * manager + * + * Return: SUCCESS or FAILURE + * + */ +QDF_STATUS policy_mgr_update_hw_mode_list(struct wlan_objmgr_psoc *psoc, + struct target_psoc_info *tgt_hdl); + +/** + * policy_mgr_update_hw_mode_index() - Function to update + * current HW mode in policy manager component + * @psoc: PSOC object information + * @new_hw_mode_index: index to new HW mode + * + * This function to update the current HW mode in policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t new_hw_mode_index); + +/** + * policy_mgr_update_old_hw_mode_index() - Function to update + * old HW mode in policy manager component + * @psoc: PSOC object information + * @new_hw_mode_index: index to old HW mode + * + * This function to update the old HW mode in policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_old_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t old_hw_mode_index); + +/** + * policy_mgr_update_new_hw_mode_index() - Function to update + * new HW mode in policy manager component + * @psoc: PSOC object information + * @new_hw_mode_index: index to new HW mode + * + * This function to update the new HW mode in policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_new_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t new_hw_mode_index); + +/** + * policy_mgr_is_chan_ok_for_dnbs() - Function to check if a channel + * is OK for "Do Not Break Stream" + * @psoc: PSOC object information + * @ch_freq: Channel frequency to check. + * @ok: Pointer to flag in which status will be stored + * This function checks if a channel is OK for + * "Do Not Break Stream" + * Return: SUCCESS or FAILURE + */ +QDF_STATUS policy_mgr_is_chan_ok_for_dnbs(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq, bool *ok); + +/** + * policy_mgr_get_hw_dbs_nss() - Computes DBS NSS + * @psoc: PSOC object information + * @nss_dbs: NSS info of both MAC0 and MAC1 + * This function computes NSS info of both MAC0 and MAC1 + * + * Return: uint32_t value signifies supported RF chains + */ +uint32_t policy_mgr_get_hw_dbs_nss(struct wlan_objmgr_psoc *psoc, + struct dbs_nss *nss_dbs); + +/** + * policy_mgr_is_dnsc_set - Check if user has set + * "Do_Not_Switch_Channel" for the vdev passed + * @vdev: vdev pointer + * + * Get "Do_Not_Switch_Channel" setting for the vdev passed. + * + * Return: true for success, else false + */ +bool policy_mgr_is_dnsc_set(struct wlan_objmgr_vdev *vdev); + +/** + * policy_mgr_get_updated_scan_and_fw_mode_config() - Function + * to get latest scan & fw config for DBS + * @psoc: PSOC object information + * @scan_config: DBS related scan config + * @fw_mode_config: DBS related FW config + * @dual_mac_disable_ini: DBS related ini config + * This function returns the latest DBS configuration for + * connection & scan, sent to FW + * Return: SUCCESS or FAILURE + */ +QDF_STATUS policy_mgr_get_updated_scan_and_fw_mode_config( + struct wlan_objmgr_psoc *psoc, uint32_t *scan_config, + uint32_t *fw_mode_config, uint32_t dual_mac_disable_ini, + uint32_t channel_select_logic_conc); + +/** + * policy_mgr_is_safe_channel - Check if the channel is in LTE + * coex channel avoidance list + * @psoc: PSOC object information + * @ch_freq: channel frequency to be checked + * + * Check if the channel is in LTE coex channel avoidance list. + * + * Return: true for success, else false + */ +bool policy_mgr_is_safe_channel(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq); + +/** + * policy_mgr_is_force_scc() - checks if SCC needs to be + * mandated + * @psoc: PSOC object information + * + * This function checks if SCC needs to be mandated or not + * + * Return: True if SCC to be mandated, false otherwise + */ +bool policy_mgr_is_force_scc(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_go_scc_enforced() - Get GO force SCC enabled or not + * @psoc: psoc object + * + * This function checks if force SCC logic should be used on GO interface. + * + * Return: True if allow GO force SCC + */ +bool policy_mgr_go_scc_enforced(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_valid_sap_conc_channel_check() - Check and update + * the SAP channel in case of STA+SAP concurrency + * @psoc: PSOC object information + * @con_ch_freq: pointer to the chan freq on which sap will come up + * @sap_ch_freq: initial channel frequency for SAP + * @sap_vdev_id: sap vdev id. + * + * This function checks & updates the channel SAP to come up on in + * case of STA+SAP concurrency + * Return: Success if SAP can come up on a channel + */ +QDF_STATUS policy_mgr_valid_sap_conc_channel_check( + struct wlan_objmgr_psoc *psoc, uint32_t *con_ch_freq, + uint32_t sap_ch_freq, uint8_t sap_vdev_id); + +/** + * policy_mgr_get_alternate_channel_for_sap() - Get an alternate + * channel to move the SAP to + * @psoc: PSOC object information + * @sap_vdev_id: sap vdev id. + * @sap_ch_freq: sap channel frequency. + * + * This function returns an alternate channel for SAP to move to + * Return: The new channel for SAP + */ +uint32_t policy_mgr_get_alternate_channel_for_sap( + struct wlan_objmgr_psoc *psoc, uint8_t sap_vdev_id, + uint32_t sap_ch_freq); + +/** + * policy_mgr_disallow_mcc() - Check for mcc + * + * @psoc: PSOC object information + * @ch_freq: channel frequency on which new connection is coming up + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * causing MCC + * + * Return: True if it is causing MCC + */ +bool policy_mgr_disallow_mcc(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq); + +/** + * policy_mgr_mode_specific_get_channel() - Get channel for a + * connection type + * @psoc: PSOC object information + * @chan_list: Connection type + * + * Get channel frequency for a connection type + * + * Return: channel frequency + */ +uint32_t policy_mgr_mode_specific_get_channel(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode); + +/** + * policy_mgr_add_sap_mandatory_chan() - Add chan to SAP mandatory channel + * list + * @psoc: Pointer to soc + * @ch_freq: Channel frequency to be added + * + * Add chan to SAP mandatory channel list + * + * Return: None + */ +void policy_mgr_add_sap_mandatory_chan(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq); + +/** + * policy_mgr_get_sap_mandatory_chan_list_len() - Return the SAP mandatory + * channel list len + * @psoc: Pointer to soc + * + * Get the SAP mandatory channel list len + * + * Return: Channel list length + */ +uint32_t policy_mgr_get_sap_mandatory_chan_list_len( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_init_sap_mandatory_2g_chan() - Init 2.4G SAP mandatory channel + * list + * @psoc: Pointer to soc + * + * Initialize the 2.4G SAP mandatory channels + * + * Return: None + */ +void policy_mgr_init_sap_mandatory_2g_chan(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_remove_sap_mandatory_chan() - Remove channel from SAP mandatory + * channel list + * @psoc: Pointer to soc + * @ch_freq: channel frequency to be removed from mandatory list + * + * Remove channel from SAP mandatory channel list + * + * Return: None + */ +void policy_mgr_remove_sap_mandatory_chan(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq); + +/* + * policy_set_cur_conc_system_pref - set current conc_system_pref + * @psoc: soc pointer + * + * Set the current concurrency system preference. + * + * Return: None + */ +void policy_mgr_set_cur_conc_system_pref(struct wlan_objmgr_psoc *psoc, + uint8_t conc_system_pref); +/** + * policy_mgr_get_cur_conc_system_pref - Get current conc_system_pref + * @psoc: soc pointer + * + * Get the current concurrent system preference. + * + * Return: conc_system_pref + */ +uint8_t policy_mgr_get_cur_conc_system_pref(struct wlan_objmgr_psoc *psoc); +/** + * policy_mgr_check_and_stop_opportunistic_timer - Get current + * state of opportunistic timer, if running, stop it and take + * action + * @psoc: soc pointer + * @id: Session/vdev id + * + * Get the current state of opportunistic timer, if it is + * running, stop it and take action. + * + * Return: None + */ +void policy_mgr_check_and_stop_opportunistic_timer( + struct wlan_objmgr_psoc *psoc, uint8_t id); + +/** + * policy_mgr_set_weight_of_dfs_passive_channels_to_zero() - set weight of dfs + * and passive channels to 0 + * @psoc: pointer to soc + * @pcl: preferred channel freq list + * @len: length of preferred channel list + * @weight_list: preferred channel weight list + * @weight_len: length of weight list + * This function set the weight of dfs and passive channels to 0 + * + * Return: None + */ +void policy_mgr_set_weight_of_dfs_passive_channels_to_zero( + struct wlan_objmgr_psoc *psoc, uint32_t *pcl, + uint32_t *len, uint8_t *weight_list, uint32_t weight_len); + +/** + * policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan() - check if sta+sap scc + * allowed on dfs chan + * @psoc: pointer to soc + * This function is used to check if sta+sap scc allowed on dfs channel + * + * Return: true if sta+sap scc is allowed on dfs channel, otherwise false + */ +bool policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan( + struct wlan_objmgr_psoc *psoc); +/** + * policy_mgr_is_sta_connected_2g() - check if sta connected in 2g + * @psoc: pointer to soc + * + * Return: true if sta is connected in 2g else false + */ +bool policy_mgr_is_sta_connected_2g(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_scan_trim_5g_chnls_for_dfs_ap() - check if sta scan should skip + * 5g channel when dfs ap is present. + * + * @psoc: pointer to soc + * + * Return: true if sta scan 5g chan should be skipped + */ +bool policy_mgr_scan_trim_5g_chnls_for_dfs_ap(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_hwmode_set_for_given_chnl() - to check for given channel + * if the hw mode is properly set. + * @psoc: pointer to psoc + * @ch_freq: given channel frequency + * + * If HW mode is properly set for given channel then it returns true else + * it returns false. + * For example, when 2x2 DBS is supported and if the first connection is + * coming up on 2G band then driver expects DBS HW mode to be set first + * before the connection can be established. Driver can call this API to + * find-out if HW mode is set properly. + * + * Return: true if HW mode is set properly else false + */ +bool policy_mgr_is_hwmode_set_for_given_chnl(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq); + +/** + * policy_mgr_get_connection_info() - Get info of all active connections + * @info: Pointer to connection info + * + * Return: Connection count + */ +uint32_t policy_mgr_get_connection_info(struct wlan_objmgr_psoc *psoc, + struct connection_info *info); +/** + * policy_mgr_register_mode_change_cb() - Register mode change callback with + * policy manager + * @callback: HDD callback to be registered + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_register_mode_change_cb(struct wlan_objmgr_psoc *psoc, + send_mode_change_event_cb mode_change_cb); +/** + * policy_mgr_deregister_mode_change_cb() - Deregister mode change callback with + * policy manager + * @callback: HDD callback to be registered + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_deregister_mode_change_cb(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_allow_sap_go_concurrency() - check whether SAP/GO concurrency is + * allowed. + * @psoc: pointer to soc + * @policy_mgr_con_mode: operating mode of interface to be checked + * @ch_freq: new operating channel of the interface to be checked + * @vdev_id: vdev id of the connection to be checked, 0xff for new connection + * + * Checks whether new channel SAP/GO can co-exist with the channel of existing + * SAP/GO connection. This API mainly used for two purposes: + * + * 1) When new GO/SAP session is coming up and needs to check if this session's + * channel can co-exist with existing existing GO/SAP sessions. For example, + * when single radio platform comes, MCC for SAP/GO+SAP/GO is not supported, in + * such case this API should prevent bringing the second connection. + * + * 2) There is already existing SAP+GO combination but due to upper layer + * notifying LTE-COEX event or sending command to move one of the connections + * to different channel. In such cases before moving existing connection to new + * channel, check if new channel can co-exist with the other existing + * connection. For example, one SAP1 is on channel-6 and second SAP2 is on + * channel-36 and lets say they are doing DBS, and lets say upper layer sends + * LTE-COEX to move SAP1 from channel-6 to channel-149. In this case, SAP1 and + * SAP2 will end up doing MCC which may not be desirable result. such cases + * will be prevented with this API. + * + * Return: true or false + */ +bool policy_mgr_allow_sap_go_concurrency(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, + uint32_t vdev_id); + +/** + * policy_mgr_dual_beacon_on_single_mac_scc_capable() - get capability that + * whether support dual beacon on same channel on single MAC + * @psoc: pointer to soc + * + * Return: bool: capable + */ +bool policy_mgr_dual_beacon_on_single_mac_scc_capable( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_dual_beacon_on_single_mac_mcc_capable() - get capability that + * whether support dual beacon on different channel on single MAC + * @psoc: pointer to soc + * + * Return: bool: capable + */ +bool policy_mgr_dual_beacon_on_single_mac_mcc_capable( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_sta_sap_scc_on_lte_coex_chan() - get capability that + * whether support sta sap scc on lte coex chan + * @psoc: pointer to soc + * + * Return: bool: capable + */ +bool policy_mgr_sta_sap_scc_on_lte_coex_chan( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_valid_channel_for_channel_switch() - check for valid channel for + * channel switch. + * @psoc: poniter to psoc + * @ch_freq: channel frequency to be validated. + * This function validates whether the given channel is valid for channel + * switch. + * + * Return: true or false + */ +bool policy_mgr_is_valid_for_channel_switch(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq); + +/** + * policy_mgr_update_user_config_sap_chan() - Update user configured channel + * @psoc: poniter to psoc + * @ch_freq: channel frequency to be upated + * + * Return: void + **/ +void policy_mgr_update_user_config_sap_chan(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq); + +/** + * policy_mgr_nan_sap_post_enable_conc_check() - Do concurrency operations + * post nan/sap enable + * @psoc: poniter to psoc + * + * Return: void + **/ +void policy_mgr_nan_sap_post_enable_conc_check(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_nan_sap_post_disable_conc_check() - Do concurrency related + * operation post nan/sap disable + * @psoc: poniter to psoc + * + * Return: void + **/ +void policy_mgr_nan_sap_post_disable_conc_check(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_sap_restart_required_after_sta_disconnect() - is sap restart + * required + * after sta disconnection + * @psoc: psoc object data + * @sap_vdev_id: sap vdev id + * @intf_ch_freq: sap channel frequency + * + * Check if SAP should be moved to a non dfs channel after STA disconnection. + * This API applicable only for STA+SAP SCC and ini 'sta_sap_scc_on_dfs_chan' + * or 'sta_sap_scc_on_lte_coex_chan' is enabled. + * + * Return: true if sap restart is required, otherwise false + */ +bool policy_mgr_is_sap_restart_required_after_sta_disconnect( + struct wlan_objmgr_psoc *psoc, uint32_t sap_vdev_id, + uint32_t *intf_ch_freq); + +/** + * policy_mgr_is_sta_sap_scc() - check whether SAP is doing SCC with + * STA + * @psoc: poniter to psoc + * @sap_ch_freq: operating channel frequency of SAP interface + * This function checks whether SAP is doing SCC with STA + * + * Return: true or false + */ +bool policy_mgr_is_sta_sap_scc(struct wlan_objmgr_psoc *psoc, + uint32_t sap_ch_freq); + +/** + * policy_mgr_nan_sap_scc_on_unsafe_ch_chk() - check whether SAP is doing SCC + * with NAN + * @psoc: poniter to psoc + * @sap_freq: operating channel frequency of SAP interface + * + * Return: true or false + */ +bool policy_mgr_nan_sap_scc_on_unsafe_ch_chk(struct wlan_objmgr_psoc *psoc, + uint32_t sap_freq); + +/** + * policy_mgr_get_hw_mode_from_idx() - Get HW mode based on index + * @psoc: psoc object + * @idx: HW mode id + * @hw_mode: HW mode params + * + * Fetches the HW mode parameters + * + * Return: Success if hw mode is obtained and the hw mode params + */ +QDF_STATUS policy_mgr_get_hw_mode_from_idx( + struct wlan_objmgr_psoc *psoc, + uint32_t idx, + struct policy_mgr_hw_mode_params *hw_mode); + +#if defined(CONFIG_BAND_6GHZ) && defined(WLAN_FEATURE_11AX) +/** + * policy_mgr_is_6ghz_conc_mode_supported() - Check connection mode supported + * on 6ghz or not + * @psoc: Pointer to soc + * @mode: new connection mode + * + * Current PORed 6ghz connection modes are STA, SAP. + * + * Return: true if supports else false. + */ +bool policy_mgr_is_6ghz_conc_mode_supported( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode); + +/** + * policy_mgr_init_ap_6ghz_capable - Init 6Ghz capable flags + * @psoc: PSOC object information + * @vdev_id: vdev id + * @ap_6ghz_capable: vdev 6ghz capable flag + * + * Init 6Ghz capable flags for active connection in policy mgr conn table + * + * Return: void + */ +void policy_mgr_init_ap_6ghz_capable(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum conn_6ghz_flag ap_6ghz_capable); + +/** + * policy_mgr_set_ap_6ghz_capable - Set 6Ghz capable flags to connection list + * @psoc: PSOC object information + * @vdev_id: vdev id + * @set: set or clear + * @ap_6ghz_capable: vdev 6ghz capable flag + * + * Set/Clear 6Ghz capable flags for active connection in policy mgr conn table + * + * Return: void + */ +void policy_mgr_set_ap_6ghz_capable(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + bool set, + enum conn_6ghz_flag ap_6ghz_capable); + +/** + * policy_mgr_get_ap_6ghz_capable - Get 6Ghz capable info for a vdev + * @psoc: PSOC object information + * @vdev_id: vdev id + * @conn_flag: output conntion flags + * + * Get 6Ghz capable flag for ap vdev (SAP). When SAP on 5G, for same reason + * the AP needs to be moved to 6G and this API will be called to check whether + * AP is 6Ghz capable or not. + * AP is allowed on 6G band only when all of below statements are true: + * a. SAP config includes WPA3 security - SAE,OWE,SuiteB. + * b. SAP is configured by ACS range which includes any 6G channel or + configured by 6G Fixed channel. + * c. SAP has no legacy clients (client doesn't support 6G band). + * legacy client (non 6ghz capable): association request frame has no + * 6G band global operating Class. + * + * Return: true if AP is 6ghz capable + */ +bool policy_mgr_get_ap_6ghz_capable( + struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, uint32_t *conn_flag); +#else +static inline bool policy_mgr_is_6ghz_conc_mode_supported( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode) +{ + return false; +} + +static inline void policy_mgr_init_ap_6ghz_capable( + struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + enum conn_6ghz_flag ap_6ghz_capable) +{} + +static inline +void policy_mgr_set_ap_6ghz_capable(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + bool set, + enum conn_6ghz_flag ap_6ghz_capable) +{} + +static inline bool policy_mgr_get_ap_6ghz_capable( + struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, uint32_t *conn_flag) +{ + return false; +} + +#endif + +/** + * policy_mgr_update_nan_vdev_mac_info() - Update the NAN vdev id and MAC id in + * policy manager + * @psoc: psoc object + * @nan_vdev_id: NAN Discovery pseudo vdev id + * @mac_id: NAN Discovery MAC ID + * + * Stores NAN Discovery related vdev and MAC id in policy manager + * + * Return: QDF Success + */ +QDF_STATUS policy_mgr_update_nan_vdev_mac_info(struct wlan_objmgr_psoc *psoc, + uint8_t nan_vdev_id, + uint8_t mac_id); + +/** + * policy_mgr_get_mode_specific_conn_info() - Get active mode specific + * channel and vdev id + * @psoc: PSOC object information + * @ch_freq_list: Mode specific channel freq list + * @vdev_id: Mode specific vdev id (list) + * @mode: Connection Mode + * + * Get active mode specific channel and vdev id + * + * Return: number of connection found as per given mode + */ +uint32_t policy_mgr_get_mode_specific_conn_info(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq_list, + uint8_t *vdev_id, + enum policy_mgr_con_mode mode); + +/** + * policy_mgr_is_sap_go_on_2g() - check if sap/go is on 2g + * @psoc: PSOC object information + * + * Return: true or false + */ +bool policy_mgr_is_sap_go_on_2g(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_5g_scc_prefer() - Prefer 5G SCC + * @psoc: psoc object + * @mode: Connection Mode + * + * This function checks if 5G SCC is preferred. + * + * Return: True if 5G SCC is preferred + */ +bool policy_mgr_get_5g_scc_prefer( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode); + +/** + * policy_mgr_dump_channel_list() - Print channel list + * @len: Length of pcl list + * @pcl_channels: pcl channels list + * @pcl_weight: pcl weight list + * + * + * Return: True or false + */ +bool policy_mgr_dump_channel_list(uint32_t len, + uint32_t *pcl_channels, + uint8_t *pcl_weight); + +#endif /* __WLAN_POLICY_MGR_API_H */ diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_cfg.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..833a18b5ae0cf201591bc67835763938f81889ba --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_cfg.h @@ -0,0 +1,638 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CFG_POLICY_MGR +#define __CFG_POLICY_MGR +#include "qdf_types.h" + +/* + * + * gWlanMccToSccSwitchMode - Control SAP channel. + * @Min: 0 + * @Max: 5 + * @Default: 0 + * + * This ini is used to override SAP channel. + * If gWlanMccToSccSwitchMode = 0: disabled. + * If gWlanMccToSccSwitchMode = 1: Enable switch. + * If gWlainMccToSccSwitchMode = 2: Force switch with SAP restart. + * If gWlainMccToSccSwitchMode = 3: Force switch without SAP restart. + * If gWlainMccToSccSwitchMode = 4: Switch using + * fav channel(s)without SAP restart. + * If gWlainMccToSccSwitchMode = 5: Force switch without SAP restart.MCC allowed + * in exceptional cases. + * If gWlainMccToSccSwitchMode = 6: Force Switch without SAP restart only in + user preffered band. + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_MCC_TO_SCC_SWITCH CFG_INI_UINT(\ + "gWlanMccToSccSwitchMode", \ + QDF_MCC_TO_SCC_SWITCH_DISABLE, \ + QDF_MCC_TO_SCC_SWITCH_MAX - 1, \ + QDF_MCC_TO_SCC_SWITCH_DISABLE, \ + CFG_VALUE_OR_DEFAULT, \ + "Provides MCC to SCC switch mode") +/* + * + * gSystemPref - Configure wlan system preference for PCL. + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to configure wlan system preference option to help + * policy manager decide on Preferred Channel List for a new connection. + * For possible values refer to enum hdd_conc_priority_mode + * + * Related: None. + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define CFG_CONC_SYS_PREF CFG_INI_UINT(\ + "gSystemPref", 0, 2, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "System preference to predict PCL") +/* + * + * gMaxConcurrentActiveSessions - Maximum number of concurrent connections. + * @Min: 1 + * @Max: 4 + * @Default: 3 + * + * This ini is used to configure the maximum number of concurrent connections. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_MAX_CONC_CXNS CFG_INI_UINT(\ + "gMaxConcurrentActiveSessions", \ + 1, 4, 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Config max num allowed connections") + +#define POLICY_MGR_CH_SELECT_POLICY_DEF 0x00000003 + +/* + * + * channel_select_logic_conc - Set channel selection logic + * for different concurrency combinations to DBS or inter band + * MCC. Default is DBS for STA+STA and STA+P2P. + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * 0 - inter-band MCC + * 1 - DBS + * + * BIT 0: STA+STA + * BIT 1: STA+P2P + * BIT 2-31: Reserved + * + * Supported Feature: STA+STA, STA+P2P + * + * Usage: External + * + * + */ +#define CFG_CHNL_SELECT_LOGIC_CONC CFG_INI_UINT(\ + "channel_select_logic_conc",\ + 0x00000000, \ + 0xFFFFFFFF, \ + POLICY_MGR_CH_SELECT_POLICY_DEF, \ + CFG_VALUE_OR_DEFAULT, \ + "Set channel selection policy for various concurrency") +/* + * + * dbs_selection_policy - Configure dbs selection policy. + * @Min: 0 + * @Max: 3 + * @Default: 0 + * + * set band preference or Vdev preference. + * bit[0] = 0: 5G 2x2 preferred to select 2x2 5G + 1x1 2G DBS mode. + * bit[0] = 1: 2G 2x2 preferred to select 2x2 2G + 1x1 5G DBS mode. + * bit[1] = 1: vdev priority enabled. The INI "vdev_priority_list" will + * specify the vdev priority. + * bit[1] = 0: vdev priority disabled. + * This INI only take effect for Genoa dual DBS hw. + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define CFG_DBS_SELECTION_PLCY CFG_INI_UINT(\ + "dbs_selection_policy", \ + 0, 3, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure dbs selection policy") +/* + * + * vdev_priority_list - Configure vdev priority list. + * @Min: 0 + * @Max: 0x4444 + * @Default: 0x4321 + * + * @vdev_priority_list: vdev priority list + * bit[0-3]: pri_id (policy_mgr_pri_id) of highest priority + * bit[4-7]: pri_id (policy_mgr_pri_id) of second priority + * bit[8-11]: pri_id (policy_mgr_pri_id) of third priority + * bit[12-15]: pri_id (policy_mgr_pri_id) of fourth priority + * example: 0x4321 - CLI < GO < SAP < STA + * vdev priority id mapping: + * PM_STA_PRI_ID = 1, + * PM_SAP_PRI_ID = 2, + * PM_P2P_GO_PRI_ID = 3, + * PM_P2P_CLI_PRI_ID = 4, + * When the previous INI "dbs_selection_policy" bit[1]=1, which means + * the vdev 2x2 prioritization enabled. Then this INI will be used to + * specify the vdev type priority list. For example : + * dbs_selection_policy=0x2 + * vdev_priority_list=0x4312 + * means: default preference 2x2 band is 5G, vdev 2x2 prioritization enabled. + * And the priority list is CLI < GO < STA < SAP + * + * This INI only take effect for Genoa dual DBS hw. + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define CFG_VDEV_CUSTOM_PRIORITY_LIST CFG_INI_UINT(\ + "vdev_priority_list", \ + 0, 0x4444, 0x4321, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure vdev priority list") +/* + * + * gEnableCustomConcRule1 - Enable custom concurrency rule1. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable custom concurrency rule1. + * If SAP comes up first and STA comes up later then SAP needs to follow STA's + * channel. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_CONC_RULE1 CFG_INI_UINT(\ + "gEnableCustomConcRule1", \ + 0, 1, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable custom concurrency rule 1") +/* + * + * gEnableCustomConcRule2 - Enable custom concurrency rule2. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable custom concurrency rule2. + * If P2PGO comes up first and STA comes up later then P2PGO need to follow + * STA's channel in 5Ghz. In following if condition we are just adding sanity + * check to make sure that by this time P2PGO's channel is same as STA's + * channel. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_CONC_RULE2 CFG_INI_UINT(\ + "gEnableCustomConcRule2", \ + 0, 1, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable custom concurrency rule 2") +/* + * + * gEnableMCCAdaptiveScheduler - MCC Adaptive Scheduler feature. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable MCC Adaptive Scheduler feature. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_MCC_ADAPTIVE_SCH_ENABLED_NAME CFG_INI_UINT(\ + "gEnableMCCAdaptiveScheduler", \ + 0, 1, 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable/Disable MCC Adaptive Scheduler") + +/* + * + * gEnableStaConnectionIn5Ghz - To enable/disable STA connection in 5G + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable STA connection in 5G band + * + * Related: STA + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_STA_CONNECTION_IN_5GHZ CFG_INI_UINT(\ + "gEnableStaConnectionIn5Ghz", \ + 0, 1, 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable/Disable STA connection in 5G") + +/* + * + * gAllowMCCGODiffBI - Allow GO in MCC mode to accept different beacon interval + * than STA's. + * @Min: 0 + * @Max: 4 + * @Default: 4 + * + * This ini is used to allow GO in MCC mode to accept different beacon interval + * than STA's. + * Added for Wi-Fi Cert. 5.1.12 + * If gAllowMCCGODiffBI = 1 + * Set to 1 for WFA certification. GO Beacon interval is not changed. + * MCC GO doesn't work well in optimized way. In worst scenario, it may + * invite STA disconnection. + * If gAllowMCCGODiffBI = 2 + * If set to 2 workaround 1 disassoc all the clients and update beacon + * Interval. + * If gAllowMCCGODiffBI = 3 + * If set to 3 tear down the P2P link in auto/Non-autonomous -GO case. + * If gAllowMCCGODiffBI = 4 + * If set to 4 don't disconnect the P2P client in autonomous/Non-auto- + * nomous -GO case update the BI dynamically + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_ALLOW_MCC_GO_DIFF_BI \ +CFG_INI_UINT("gAllowMCCGODiffBI", 0, 4, 4, CFG_VALUE_OR_DEFAULT, \ + "Allow GO in MCC mode to accept different BI than STA's") + +/* + * + * gEnableOverLapCh - Enables Overlap Channel. If set, allow overlapping + * channels to be selected for the SoftAP + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set Overlap Channel + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + +#define CFG_ENABLE_OVERLAP_CH \ +CFG_INI_UINT("gEnableOverLapCh", 0, 1, 0, CFG_VALUE_OR_DEFAULT, \ + "Overlap channels are allowed for SAP when flag is set") + +/* + * + * + * gDualMacFeatureDisable - Disable Dual MAC feature. + * @Min: 0 + * @Max: 6 + * @Default: 6 + * + * This ini is used to enable/disable dual MAC feature. + * 0 - enable DBS + * 1 - disable DBS + * 2 - disable DBS for connection but keep DBS for scan + * 3 - disable DBS for connection but keep DBS scan with async + * scan policy disabled + * 4 - enable DBS for connection as well as for scan with async + * scan policy disabled + * 5 - enable DBS for connection but disable DBS for scan. + * 6 - enable DBS for connection but disable simultaneous scan + * from upper layer (DBS scan remains enabled in FW). + * + * Note: INI item value should match 'enum dbs_support' + * + * Related: None. + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define CFG_DUAL_MAC_FEATURE_DISABLE \ +CFG_INI_UINT("gDualMacFeatureDisable", 0, 6, 6, CFG_VALUE_OR_DEFAULT, \ + "This INI is used to enable/disable Dual MAC feature") + +/* + * + * g_sta_sap_scc_on_dfs_chan - Allow STA+SAP SCC on DFS channel with master + * mode support disabled. + * @Min: 0 + * @Max: 2 + * @Default: 2 + * + * This ini is used to allow STA+SAP SCC on DFS channel with master mode + * support disabled, the value is defined by enum PM_AP_DFS_MASTER_MODE. + * 0 - Disallow STA+SAP SCC on DFS channel + * 1 - Allow STA+SAP SCC on DFS channel with master mode disabled + * 2 - enhance "1" with below requirement + * a. Allow single SAP (GO) start on DFS channel. + * b. Allow CAC process on DFS channel in single SAP (GO) mode + * c. Allow DFS radar event process in single SAP (GO) mode + * d. Disallow CAC and radar event process in SAP (GO) + STA mode. + * The value 2 of this ini requires master mode to be enabled so it is + * mandatory to enable the dfs master mode ini gEnableDFSMasterCap + * along with it. + * + * Related: None. + * + * Supported Feature: Non-DBS, DBS + * + * Usage: Internal/External + * + * + */ + +#define CFG_STA_SAP_SCC_ON_DFS_CHAN \ +CFG_INI_UINT("g_sta_sap_scc_on_dfs_chan", 0, 2, 2, CFG_VALUE_OR_DEFAULT, \ + "Allow STA+SAP SCC on DFS channel with master mode disable") + +/* + * + * gForce1x1Exception - force 1x1 when connecting to certain peer + * @Min: 0 + * @Max: 2 + * @Default: 2 + * + * This INI when enabled will force 1x1 connection with certain peer. + * The implementation for this ini would be as follows:- + * Value 0: Even if the AP is present in OUI, 1x1 will not be forced + * Value 1: If antenna sharing supported, then only do 1x1. + * Value 2: If AP present in OUI, force 1x1 connection. + + * + * Related: None + * + * Supported Feature: connection + * + * Usage: External + * + * + */ + +#define CFG_FORCE_1X1_FEATURE \ +CFG_INI_UINT("gForce1x1Exception", 0, 2, 1, CFG_VALUE_OR_DEFAULT, \ + "force 1x1 when connecting to certain peer") + +/* + * + * gEnableSAPManadatoryChanList - Enable SAP Mandatory channel list + * Options. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable the SAP manadatory chan list + * 0 - Disable SAP mandatory chan list + * 1 - Enable SAP mandatory chan list + * + * Supported Feature: SAP + * + * + * Usage: Internal/External + * + * + */ + +#define CFG_ENABLE_SAP_MANDATORY_CHAN_LIST \ +CFG_INI_UINT("gEnableSAPManadatoryChanList", 0, 1, 0, CFG_VALUE_OR_DEFAULT, \ + "Enable SAP Mandatory channel list") + +/* + * + * g_nan_sap_scc_on_lte_coex_chan - Allow NAN+SAP SCC on LTE coex channel + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to allow NAN+SAP SCC on LTE coex channel + * 0 - Disallow NAN+SAP SCC on LTE coex channel + * 1 - Allow NAN+SAP SCC on LTE coex channel + * + * Related: Depends on gWlanMccToSccSwitchMode config. + * + * Supported Feature: Non-DBS, DBS + * + * Usage: Internal/External + * + * + */ +#define CFG_NAN_SAP_SCC_ON_LTE_COEX_CHAN \ +CFG_INI_BOOL("g_nan_sap_scc_on_lte_coex_chan", 1, \ + "Allow NAN+SAP SCC on LTE coex channel") + +/* + * + * g_sta_sap_scc_on_lte_coex_chan - Allow STA+SAP SCC on LTE coex channel + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to allow STA+SAP SCC on LTE coex channel + * 0 - Disallow STA+SAP SCC on LTE coex channel + * 1 - Allow STA+SAP SCC on LTE coex channel + * + * Related: None. + * + * Supported Feature: Non-DBS, DBS + * + * Usage: Internal/External + * + * + */ +#define CFG_STA_SAP_SCC_ON_LTE_COEX_CHAN \ +CFG_INI_UINT("g_sta_sap_scc_on_lte_coex_chan", 0, 1, 1, CFG_VALUE_OR_DEFAULT, \ + "Allow STA+SAP SCC on LTE coex channel") + +/* + * + * g_mark_sap_indoor_as_disable - Enable/Disable Indoor channel + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to mark the Indoor channel as + * disable when SAP start and revert it on SAP stop, + * so SAP will not turn on indoor channel and + * sta will not scan/associate and roam on indoor + * channels. + * + * Related: If g_mark_sap_indoor_as_disable set, turn the + * indoor channels to disable and update Wiphy & fw. + * + * Supported Feature: SAP/STA + * + * Usage: External + * + * + */ + +#define CFG_MARK_INDOOR_AS_DISABLE_FEATURE \ +CFG_INI_UINT("g_mark_sap_indoor_as_disable", 0, 1, 0, CFG_VALUE_OR_DEFAULT, \ + "Enable/Disable Indoor channel") + +/* + * + * g_enable_go_force_scc - Enable/Disable force SCC on P2P GO + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini and along with "gWlanMccToSccSwitchMode" is used to enable + * force SCC on P2P GO interface. + * + * Supported Feature: P2P GO + * + * Usage: External + * + * + */ + +#define CFG_P2P_GO_ENABLE_FORCE_SCC \ +CFG_INI_UINT("g_enable_go_force_scc", 0, 1, 0, CFG_VALUE_OR_DEFAULT, \ + "Enable/Disable P2P GO force SCC") + +/** + * + * g_pcl_band_priority - Set 5G/6G Channel order + * Options. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set preference between 5G and 6G channels during + * PCL population. + * 0 - Prefer 5G channels, 5G channels will be placed before the 6G channels + * in PCL. + * 1 - Prefer 6G channels, 6G channels will be placed before the 5G channels + * in PCL. + * + * Supported Feature: STA, SAP + * + * + * Usage: Internal/External + * + * + */ + +#define CFG_PCL_BAND_PRIORITY \ +CFG_INI_UINT("g_pcl_band_priority", 0, 1, 0, CFG_VALUE_OR_DEFAULT, \ + "Set 5G and 6G Channel order") + +/* + * + * g_prefer_5g_scc_to_dbs - prefer 5g scc to dbs + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 0 + * + * This ini is used to give higher priority for 5g scc than dbs. + * It is bitmap per enum policy_mgr_con_mode. + * For example in GO+STA(5G) mode, when TPUT is onfigured as wlan system + * preference option, If 5G SCC needs higher priority than dbs, set it as 0x8. + * + * Supported Feature: P2P GO + * + * Usage: External + * + * + */ +#define CFG_PREFER_5G_SCC_TO_DBS \ +CFG_INI_UINT("g_prefer_5g_scc_to_dbs", 0, 0xFFFFFFFF, 0, CFG_VALUE_OR_DEFAULT, \ + "5G SCC has higher priority than DBS") + +#define CFG_POLICY_MGR_ALL \ + CFG(CFG_MCC_TO_SCC_SWITCH) \ + CFG(CFG_CONC_SYS_PREF) \ + CFG(CFG_MAX_CONC_CXNS) \ + CFG(CFG_DBS_SELECTION_PLCY) \ + CFG(CFG_VDEV_CUSTOM_PRIORITY_LIST) \ + CFG(CFG_CHNL_SELECT_LOGIC_CONC) \ + CFG(CFG_ENABLE_CONC_RULE1) \ + CFG(CFG_ENABLE_CONC_RULE2) \ + CFG(CFG_ENABLE_MCC_ADAPTIVE_SCH_ENABLED_NAME)\ + CFG(CFG_ENABLE_STA_CONNECTION_IN_5GHZ)\ + CFG(CFG_ENABLE_OVERLAP_CH)\ + CFG(CFG_DUAL_MAC_FEATURE_DISABLE)\ + CFG(CFG_STA_SAP_SCC_ON_DFS_CHAN)\ + CFG(CFG_FORCE_1X1_FEATURE)\ + CFG(CFG_ENABLE_SAP_MANDATORY_CHAN_LIST)\ + CFG(CFG_STA_SAP_SCC_ON_LTE_COEX_CHAN)\ + CFG(CFG_NAN_SAP_SCC_ON_LTE_COEX_CHAN) \ + CFG(CFG_MARK_INDOOR_AS_DISABLE_FEATURE)\ + CFG(CFG_ALLOW_MCC_GO_DIFF_BI) \ + CFG(CFG_P2P_GO_ENABLE_FORCE_SCC) \ + CFG(CFG_PCL_BAND_PRIORITY) \ + CFG(CFG_PREFER_5G_SCC_TO_DBS) +#endif diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..4a44dca56e1fbb6eb9563f4df069c7f029ed4930 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h @@ -0,0 +1,1290 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_PUBLIC_STRUCT_H +#define __WLAN_POLICY_MGR_PUBLIC_STRUCT_H + +/** + * DOC: wlan_policy_mgr_public_struct.h + * + * Concurrenct Connection Management entity + */ + +/* Include files */ +#include + +/** + * Some max value greater than the max length of the channel list + */ +#define MAX_WEIGHT_OF_PCL_CHANNELS 255 +/** + * Some fixed weight difference between the groups + */ +#define PCL_GROUPS_WEIGHT_DIFFERENCE 20 + +/** + * Currently max, only 3 groups are possible as per 'enum policy_mgr_pcl_type'. + * i.e., in a PCL only 3 groups of channels can be present + * e.g., SCC channel on 2.4 Ghz, SCC channel on 5 Ghz & 5 Ghz channels. + * Group 1 has highest priority, group 2 has the next higher priority + * and so on. + */ +#define WEIGHT_OF_GROUP1_PCL_CHANNELS MAX_WEIGHT_OF_PCL_CHANNELS +#define WEIGHT_OF_GROUP2_PCL_CHANNELS \ + (WEIGHT_OF_GROUP1_PCL_CHANNELS - PCL_GROUPS_WEIGHT_DIFFERENCE) +#define WEIGHT_OF_GROUP3_PCL_CHANNELS \ + (WEIGHT_OF_GROUP2_PCL_CHANNELS - PCL_GROUPS_WEIGHT_DIFFERENCE) +#define WEIGHT_OF_GROUP4_PCL_CHANNELS \ + (WEIGHT_OF_GROUP3_PCL_CHANNELS - PCL_GROUPS_WEIGHT_DIFFERENCE) + +#define WEIGHT_OF_NON_PCL_CHANNELS 1 +#define WEIGHT_OF_DISALLOWED_CHANNELS 0 + +#define MAX_MAC 2 + +#ifdef FEATURE_FOURTH_CONNECTION +#define MAX_NUMBER_OF_CONC_CONNECTIONS 4 +#else +#define MAX_NUMBER_OF_CONC_CONNECTIONS 3 +#endif + +typedef int (*send_mode_change_event_cb)(void); + +/** + * enum hw_mode_ss_config - Possible spatial stream configuration + * @HW_MODE_SS_0x0: Unused Tx and Rx of MAC + * @HW_MODE_SS_1x1: 1 Tx SS and 1 Rx SS + * @HW_MODE_SS_2x2: 2 Tx SS and 2 Rx SS + * @HW_MODE_SS_3x3: 3 Tx SS and 3 Rx SS + * @HW_MODE_SS_4x4: 4 Tx SS and 4 Rx SS + * + * Note: Right now only 1x1 and 2x2 are being supported. Other modes should + * be added when supported. Asymmetric configuration like 1x2, 2x1 are also + * not supported now. But, they are still valid. Right now, Tx/Rx SS support is + * 4 bits long. So, we can go upto 15x15 + */ +enum hw_mode_ss_config { + HW_MODE_SS_0x0, + HW_MODE_SS_1x1, + HW_MODE_SS_2x2, + HW_MODE_SS_3x3, + HW_MODE_SS_4x4, +}; + +/** + * enum hw_mode_dbs_capab - DBS HW mode capability + * @HW_MODE_DBS_NONE: Non DBS capable + * @HW_MODE_DBS: DBS capable + */ +enum hw_mode_dbs_capab { + HW_MODE_DBS_NONE, + HW_MODE_DBS, +}; + +/** + * enum hw_mode_agile_dfs_capab - Agile DFS HW mode capability + * @HW_MODE_AGILE_DFS_NONE: Non Agile DFS capable + * @HW_MODE_AGILE_DFS: Agile DFS capable + */ +enum hw_mode_agile_dfs_capab { + HW_MODE_AGILE_DFS_NONE, + HW_MODE_AGILE_DFS, +}; + +/** + * enum hw_mode_sbs_capab - SBS HW mode capability + * @HW_MODE_SBS_NONE: Non SBS capable + * @HW_MODE_SBS: SBS capable + */ +enum hw_mode_sbs_capab { + HW_MODE_SBS_NONE, + HW_MODE_SBS, +}; + +/** + * enum hw_mode_mac_band_cap - mac band capability + * @HW_MODE_MAC_BAND_NONE: No band requirement. + * @HW_MODE_MAC_BAND_2G: 2G band supported. + * @HW_MODE_MAC_BAND_5G: 5G band supported. + * + * To add HW_MODE_MAC_BAND_NONE value to help to + * match the HW DBS mode in hw mode list. + * Other enum values should match with WMI header: + * typedef enum { + * WLAN_2G_CAPABILITY = 0x1, + * WLAN_5G_CAPABILITY = 0x2, + * } WLAN_BAND_CAPABILITY; + */ +enum hw_mode_mac_band_cap { + HW_MODE_MAC_BAND_NONE = 0, + HW_MODE_MAC_BAND_2G = WLAN_2G_CAPABILITY, + HW_MODE_MAC_BAND_5G = WLAN_5G_CAPABILITY, +}; + +/** + * enum force_1x1_type - enum to specify the type of forced 1x1 ini provided. + * @FORCE_1X1_DISABLED: even if the AP is present in OUI, 1x1 will not be forced + * @FORCE_1X1_ENABLED_FOR_AS: If antenna sharing supported, then only do 1x1. + * @FORCE_1X1_ENABLED_FORCED: If AP present in OUI, force 1x1 connection. + */ +enum force_1x1_type { + FORCE_1X1_DISABLED, + FORCE_1X1_ENABLED_FOR_AS, + FORCE_1X1_ENABLED_FORCED, +}; + +/** + * enum policy_mgr_pcl_group_id - Identifies the pcl groups to be used + * @POLICY_MGR_PCL_GROUP_ID1_ID2: Use weights of group1 and group2 + * @POLICY_MGR_PCL_GROUP_ID2_ID3: Use weights of group2 and group3 + * @POLICY_MGR_PCL_GROUP_ID3_ID4: Use weights of group3 and group4 + * + * Since maximum of three groups are possible, this will indicate which + * PCL group needs to be used. + */ +enum policy_mgr_pcl_group_id { + POLICY_MGR_PCL_GROUP_ID1_ID2, + POLICY_MGR_PCL_GROUP_ID2_ID3, + POLICY_MGR_PCL_GROUP_ID3_ID4, +}; + +/** + * policy_mgr_pcl_channel_order - Order in which the PCL is requested + * @POLICY_MGR_PCL_ORDER_NONE: no order + * @POLICY_MGR_PCL_ORDER_24G_THEN_5G: 2.4 Ghz channel followed by 5 Ghz channel + * @POLICY_MGR_PCL_ORDER_5G_THEN_2G: 5 Ghz channel followed by 2.4 Ghz channel + * + * Order in which the PCL is requested + */ +enum policy_mgr_pcl_channel_order { + POLICY_MGR_PCL_ORDER_NONE, + POLICY_MGR_PCL_ORDER_24G_THEN_5G, + POLICY_MGR_PCL_ORDER_5G_THEN_2G, +}; + +/** + * policy_mgr_pcl_band_priority - Band priority between 5G and 6G channel + * @POLICY_MGR_PCL_BAND_5G_THEN_6G: 5 Ghz channel followed by 6 Ghz channel + * @POLICY_MGR_PCL_BAND_6G_THEN_5G: 6 Ghz channel followed by 5 Ghz channel + * + * Band priority between 5G and 6G + */ +enum policy_mgr_pcl_band_priority { + POLICY_MGR_PCL_BAND_5G_THEN_6G = 0, + POLICY_MGR_PCL_BAND_6G_THEN_5G, +}; + +/** + * enum policy_mgr_max_rx_ss - Maximum number of receive spatial streams + * @POLICY_MGR_RX_NSS_1: Receive Nss = 1 + * @POLICY_MGR_RX_NSS_2: Receive Nss = 2 + * @POLICY_MGR_RX_NSS_3: Receive Nss = 3 + * @POLICY_MGR_RX_NSS_4: Receive Nss = 4 + * @POLICY_MGR_RX_NSS_5: Receive Nss = 5 + * @POLICY_MGR_RX_NSS_6: Receive Nss = 6 + * @POLICY_MGR_RX_NSS_7: Receive Nss = 7 + * @POLICY_MGR_RX_NSS_8: Receive Nss = 8 + * + * Indicates the maximum number of spatial streams that the STA can receive + */ +enum policy_mgr_max_rx_ss { + POLICY_MGR_RX_NSS_1 = 0, + POLICY_MGR_RX_NSS_2 = 1, + POLICY_MGR_RX_NSS_3 = 2, + POLICY_MGR_RX_NSS_4 = 3, + POLICY_MGR_RX_NSS_5 = 4, + POLICY_MGR_RX_NSS_6 = 5, + POLICY_MGR_RX_NSS_7 = 6, + POLICY_MGR_RX_NSS_8 = 7, + POLICY_MGR_RX_NSS_MAX, +}; + +/** + * enum policy_mgr_chain_mode - Chain Mask tx & rx combination. + * + * @POLICY_MGR_ONE_ONE: One for Tx, One for Rx + * @POLICY_MGR_TWO_TWO: Two for Tx, Two for Rx + * @POLICY_MGR_MAX_NO_OF_CHAIN_MODE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_chain_mode { + POLICY_MGR_ONE_ONE = 0, + POLICY_MGR_TWO_TWO, + POLICY_MGR_MAX_NO_OF_CHAIN_MODE +}; + +/** + * enum policy_mgr_conc_priority_mode - t/p, powersave, latency. + * + * @PM_THROUGHPUT: t/p is the priority + * @PM_POWERSAVE: powersave is the priority + * @PM_LATENCY: latency is the priority + * @PM_MAX_CONC_PRIORITY_MODE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_conc_priority_mode { + PM_THROUGHPUT = 0, + PM_POWERSAVE, + PM_LATENCY, + PM_MAX_CONC_PRIORITY_MODE +}; + +/** + * enum policy_mgr_con_mode - concurrency mode for PCL table + * + * @PM_STA_MODE: station mode + * @PM_SAP_MODE: SAP mode + * @PM_P2P_CLIENT_MODE: P2P client mode + * @PM_P2P_GO_MODE: P2P Go mode + * @PM_IBSS_MODE: IBSS mode + * @PM_NDI_MODE: NDI mode + * @PM_NAN_DISC_MODE: NAN Discovery mode + * @PM_MAX_NUM_OF_MODE: max value place holder + */ +enum policy_mgr_con_mode { + PM_STA_MODE = 0, + PM_SAP_MODE, + PM_P2P_CLIENT_MODE, + PM_P2P_GO_MODE, + PM_IBSS_MODE, + PM_NDI_MODE, + PM_NAN_DISC_MODE, + PM_MAX_NUM_OF_MODE +}; + +/** + * enum policy_mgr_mac_use - MACs that are used + * @POLICY_MGR_MAC0: Only MAC0 is used + * @POLICY_MGR_MAC1: Only MAC1 is used + * @POLICY_MGR_MAC0_AND_MAC1: Both MAC0 and MAC1 are used + */ +enum policy_mgr_mac_use { + POLICY_MGR_MAC0 = 1, + POLICY_MGR_MAC1 = 2, + POLICY_MGR_MAC0_AND_MAC1 = 3 +}; + +/** + * enum policy_mgr_pcl_type - Various types of Preferred channel list (PCL). + * + * @PM_NONE: No channel preference + * @PM_24G: 2.4 Ghz channels only + * @PM_5G: 5 Ghz channels only + * @PM_SCC_CH: SCC channel only + * @PM_MCC_CH: MCC channels only + * @PM_SBS_CH: SBS channels only + * @PM_SCC_CH_24G: SCC channel & 2.4 Ghz channels + * @PM_SCC_CH_5G: SCC channel & 5 Ghz channels + * @PM_24G_SCC_CH: 2.4 Ghz channels & SCC channel + * @PM_5G_SCC_CH: 5 Ghz channels & SCC channel + * @PM_SCC_ON_5_SCC_ON_24_24G: SCC channel on 5 Ghz, SCC + * channel on 2.4 Ghz & 2.4 Ghz channels + * @PM_SCC_ON_5_SCC_ON_24_5G: SCC channel on 5 Ghz, SCC channel + * on 2.4 Ghz & 5 Ghz channels + * @PM_SCC_ON_24_SCC_ON_5_24G: SCC channel on 2.4 Ghz, SCC + * channel on 5 Ghz & 2.4 Ghz channels + * @PM_SCC_ON_24_SCC_ON_5_5G: SCC channel on 2.4 Ghz, SCC + * channel on 5 Ghz & 5 Ghz channels + * @PM_SCC_ON_5_SCC_ON_24: SCC channel on 5 Ghz, SCC channel on + * 2.4 Ghz + * @PM_SCC_ON_24_SCC_ON_5: SCC channel on 2.4 Ghz, SCC channel + * on 5 Ghz + * @PM_MCC_CH_24G: MCC channels & 2.4 Ghz channels + * @PM_MCC_CH_5G: MCC channels & 5 Ghz channels + * @PM_24G_MCC_CH: 2.4 Ghz channels & MCC channels + * @PM_5G_MCC_CH: 5 Ghz channels & MCC channels + * @PM_SBS_CH_5G: SBS channels & rest of 5 Ghz channels + * @PM_24G_SCC_CH_SBS_CH: 2.4 Ghz channels, SCC channel & SBS channels + * @PM_24G_SCC_CH_SBS_CH_5G: 2.4 Ghz channels, SCC channel, + * SBS channels & rest of the 5G channels + * @PM_24G_SBS_CH_MCC_CH: 2.4 Ghz channels, SBS channels & MCC channels + * @PM_MAX_PCL_TYPE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_pcl_type { + PM_NONE = 0, + PM_24G, + PM_5G, + PM_SCC_CH, + PM_MCC_CH, + PM_SBS_CH, + PM_SCC_CH_24G, + PM_SCC_CH_5G, + PM_24G_SCC_CH, + PM_5G_SCC_CH, + PM_SCC_ON_5_SCC_ON_24_24G, + PM_SCC_ON_5_SCC_ON_24_5G, + PM_SCC_ON_24_SCC_ON_5_24G, + PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_24_SCC_ON_5, + PM_MCC_CH_24G, + PM_MCC_CH_5G, + PM_24G_MCC_CH, + PM_5G_MCC_CH, + PM_SBS_CH_5G, + PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH_5G, + PM_24G_SBS_CH_MCC_CH, + + PM_MAX_PCL_TYPE +}; + +/** + * enum policy_mgr_one_connection_mode - Combination of first connection + * type, band & spatial stream used. + * + * @PM_STA_24_1x1: STA connection using 1x1@2.4 Ghz + * @PM_STA_24_2x2: STA connection using 2x2@2.4 Ghz + * @PM_STA_5_1x1: STA connection using 1x1@5 Ghz + * @PM_STA_5_2x2: STA connection using 2x2@5 Ghz + * @PM_P2P_CLI_24_1x1: P2P Client connection using 1x1@2.4 Ghz + * @PM_P2P_CLI_24_2x2: P2P Client connection using 2x2@2.4 Ghz + * @PM_P2P_CLI_5_1x1: P2P Client connection using 1x1@5 Ghz + * @PM_P2P_CLI_5_2x2: P2P Client connection using 2x2@5 Ghz + * @PM_P2P_GO_24_1x1: P2P GO connection using 1x1@2.4 Ghz + * @PM_P2P_GO_24_2x2: P2P GO connection using 2x2@2.4 Ghz + * @PM_P2P_GO_5_1x1: P2P GO connection using 1x1@5 Ghz + * @PM_P2P_GO_5_2x2: P2P GO connection using 2x2@5 Ghz + * @PM_SAP_24_1x1: SAP connection using 1x1@2.4 Ghz + * @PM_SAP_24_2x2: SAP connection using 2x2@2.4 Ghz + * @PM_SAP_5_1x1: SAP connection using 1x1@5 Ghz + * @PM_SAP_5_1x1: SAP connection using 2x2@5 Ghz + * @PM_IBSS_24_1x1: IBSS connection using 1x1@2.4 Ghz + * @PM_IBSS_24_2x2: IBSS connection using 2x2@2.4 Ghz + * @PM_IBSS_5_1x1: IBSS connection using 1x1@5 Ghz + * @PM_IBSS_5_2x2: IBSS connection using 2x2@5 Ghz + * @PM_NAN_DISC_24_1x1: NAN Discovery using 1x1@2.4 Ghz + * @PM_NAN_DISC_24_2x2: NAN Discovery using 2x2@2.4 Ghz + * @PM_NDI_24_1x1: NAN Datapath using 1x1@2.4 Ghz + * @PM_NDI_24_2x2: NAN Datapath using 2x2@2.4 Ghz + * @PM_NDI_5_1x1: NAN Datapath using 1x1@5 Ghz + * @PM_NDI_5_2x2: NAN Datapath using 2x2@5 Ghz + * @PM_MAX_ONE_CONNECTION_MODE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_one_connection_mode { + PM_STA_24_1x1 = 0, + PM_STA_24_2x2, + PM_STA_5_1x1, + PM_STA_5_2x2, + PM_P2P_CLI_24_1x1, + PM_P2P_CLI_24_2x2, + PM_P2P_CLI_5_1x1, + PM_P2P_CLI_5_2x2, + PM_P2P_GO_24_1x1, + PM_P2P_GO_24_2x2, + PM_P2P_GO_5_1x1, + PM_P2P_GO_5_2x2, + PM_SAP_24_1x1, + PM_SAP_24_2x2, + PM_SAP_5_1x1, + PM_SAP_5_2x2, + PM_IBSS_24_1x1, + PM_IBSS_24_2x2, + PM_IBSS_5_1x1, + PM_IBSS_5_2x2, + PM_NAN_DISC_24_1x1, + PM_NAN_DISC_24_2x2, + PM_NDI_24_1x1, + PM_NDI_24_2x2, + PM_NDI_5_1x1, + PM_NDI_5_2x2, + + PM_MAX_ONE_CONNECTION_MODE +}; + +/** + * enum policy_mgr_two_connection_mode - Combination of first two + * connections type, concurrency state, band & spatial stream + * used. + * + * @PM_STA_SAP_SCC_24_1x1: STA & SAP connection on SCC using + * 1x1@2.4 Ghz + * @PM_STA_SAP_SCC_24_2x2: STA & SAP connection on SCC using + * 2x2@2.4 Ghz + * @PM_STA_SAP_MCC_24_1x1: STA & SAP connection on MCC using + * 1x1@2.4 Ghz + * @PM_STA_SAP_MCC_24_2x2: STA & SAP connection on MCC using + * 2x2@2.4 Ghz + * @PM_STA_SAP_SCC_5_1x1: STA & SAP connection on SCC using + * 1x1@5 Ghz + * @PM_STA_SAP_SCC_5_2x2: STA & SAP connection on SCC using + * 2x2@5 Ghz + * @PM_STA_SAP_MCC_5_1x1: STA & SAP connection on MCC using + * 1x1@5 Ghz + * @PM_STA_SAP_MCC_5_2x2: STA & SAP connection on MCC using + * 2x2@5 Ghz + * @PM_STA_SAP_DBS_1x1: STA & SAP connection on DBS using 1x1 + * @PM_STA_SAP_DBS_2x2: STA & SAP connection on DBS using 2x2 + * @PM_STA_SAP_SBS_5_1x1: STA & SAP connection on 5G SBS using 1x1 + * @PM_STA_P2P_GO_SCC_24_1x1: STA & P2P GO connection on SCC + * using 1x1@2.4 Ghz + * @PM_STA_P2P_GO_SCC_24_2x2: STA & P2P GO connection on SCC + * using 2x2@2.4 Ghz + * @PM_STA_P2P_GO_MCC_24_1x1: STA & P2P GO connection on MCC + * using 1x1@2.4 Ghz + * @PM_STA_P2P_GO_MCC_24_2x2: STA & P2P GO connection on MCC + * using 2x2@2.4 Ghz + * @PM_STA_P2P_GO_SCC_5_1x1: STA & P2P GO connection on SCC + * using 1x1@5 Ghz + * @PM_STA_P2P_GO_SCC_5_2x2: STA & P2P GO connection on SCC + * using 2x2@5 Ghz + * @PM_STA_P2P_GO_MCC_5_1x1: STA & P2P GO connection on MCC + * using 1x1@5 Ghz + * @PM_STA_P2P_GO_MCC_5_2x2: STA & P2P GO connection on MCC + * using 2x2@5 Ghz + * @PM_STA_P2P_GO_DBS_1x1: STA & P2P GO connection on DBS using + * 1x1 + * @PM_STA_P2P_GO_DBS_2x2: STA & P2P GO connection on DBS using + * 2x2 + * @PM_STA_P2P_GO_SBS_5_1x1: STA & P2P GO connection on 5G SBS + * using 1x1 + * @PM_STA_P2P_CLI_SCC_24_1x1: STA & P2P CLI connection on SCC + * using 1x1@2.4 Ghz + * @PM_STA_P2P_CLI_SCC_24_2x2: STA & P2P CLI connection on SCC + * using 2x2@2.4 Ghz + * @PM_STA_P2P_CLI_MCC_24_1x1: STA & P2P CLI connection on MCC + * using 1x1@2.4 Ghz + * @PM_STA_P2P_CLI_MCC_24_2x2: STA & P2P CLI connection on MCC + * using 2x2@2.4 Ghz + * @PM_STA_P2P_CLI_SCC_5_1x1: STA & P2P CLI connection on SCC + * using 1x1@5 Ghz + * @PM_STA_P2P_CLI_SCC_5_2x2: STA & P2P CLI connection on SCC + * using 2x2@5 Ghz + * @PM_STA_P2P_CLI_MCC_5_1x1: STA & P2P CLI connection on MCC + * using 1x1@5 Ghz + * @PM_STA_P2P_CLI_MCC_5_2x2: STA & P2P CLI connection on MCC + * using 2x2@5 Ghz + * @PM_STA_P2P_CLI_DBS_1x1: STA & P2P CLI connection on DBS + * using 1x1 + * @PM_STA_P2P_CLI_DBS_2x2: STA & P2P CLI connection on DBS + * using 2x2 + * @PM_STA_P2P_CLI_SBS_5_1x1: STA & P2P CLI connection on 5G + * SBS using 1x1 + * @PM_P2P_GO_P2P_CLI_SCC_24_1x1: P2P GO & CLI connection on + * SCC using 1x1@2.4 Ghz + * @PM_P2P_GO_P2P_CLI_SCC_24_2x2: P2P GO & CLI connection on + * SCC using 2x2@2.4 Ghz + * @PM_P2P_GO_P2P_CLI_MCC_24_1x1: P2P GO & CLI connection on + * MCC using 1x1@2.4 Ghz + * @PM_P2P_GO_P2P_CLI_MCC_24_2x2: P2P GO & CLI connection on + * MCC using 2x2@2.4 Ghz + * @PM_P2P_GO_P2P_CLI_SCC_5_1x1: P2P GO & CLI connection on + * SCC using 1x1@5 Ghz + * @PM_P2P_GO_P2P_CLI_SCC_5_2x2: P2P GO & CLI connection on + * SCC using 2x2@5 Ghz + * @PM_P2P_GO_P2P_CLI_MCC_5_1x1: P2P GO & CLI connection on + * MCC using 1x1@5 Ghz + * @PM_P2P_GO_P2P_CLI_MCC_5_2x2: P2P GO & CLI connection on + * MCC using 2x2@5 Ghz + * @PM_P2P_GO_P2P_CLI_DBS_1x1: P2P GO & CLI connection on DBS + * using 1x1 + * @PM_P2P_GO_P2P_CLI_DBS_2x2: P2P GO & P2P CLI connection + * on DBS using 2x2 + * @PM_P2P_GO_P2P_CLI_SBS_5_1x1: P2P GO & P2P CLI connection + * on 5G SBS using 1x1 + * @PM_P2P_GO_SAP_SCC_24_1x1: P2P GO & SAP connection on + * SCC using 1x1@2.4 Ghz + * @PM_P2P_GO_SAP_SCC_24_2x2: P2P GO & SAP connection on + * SCC using 2x2@2.4 Ghz + * @PM_P2P_GO_SAP_MCC_24_1x1: P2P GO & SAP connection on + * MCC using 1x1@2.4 Ghz + * @PM_P2P_GO_SAP_MCC_24_2x2: P2P GO & SAP connection on + * MCC using 2x2@2.4 Ghz + * @PM_P2P_GO_SAP_SCC_5_1x1: P2P GO & SAP connection on + * SCC using 1x1@5 Ghz + * @PM_P2P_GO_SAP_SCC_5_2x2: P2P GO & SAP connection on + * SCC using 2x2@5 Ghz + * @PM_P2P_GO_SAP_MCC_5_1x1: P2P GO & SAP connection on + * MCC using 1x1@5 Ghz + * @PM_P2P_GO_SAP_MCC_5_2x2: P2P GO & SAP connection on + * MCC using 2x2@5 Ghz + * @PM_P2P_GO_SAP_DBS_1x1: P2P GO & SAP connection on DBS using + * 1x1 + * @PM_P2P_GO_SAP_DBS_2x2: P2P GO & SAP connection on DBS using + * 2x2 + * @PM_P2P_GO_SAP_SBS_5_1x1: P2P GO & SAP connection on 5G SBS + * using 1x1 + * @PM_P2P_CLI_SAP_SCC_24_1x1: CLI & SAP connection on SCC using + * 1x1@2.4 Ghz + * @PM_P2P_CLI_SAP_SCC_24_2x2: CLI & SAP connection on SCC using + * 2x2@2.4 Ghz + * @PM_P2P_CLI_SAP_MCC_24_1x1: CLI & SAP connection on MCC using + * 1x1@2.4 Ghz + * @PM_P2P_CLI_SAP_MCC_24_2x2: CLI & SAP connection on MCC using + * 2x2@2.4 Ghz + * @PM_P2P_CLI_SAP_SCC_5_1x1: CLI & SAP connection on SCC using + * 1x1@5 Ghz + * @PM_P2P_CLI_SAP_SCC_5_2x2: CLI & SAP connection on SCC using + * 2x2@5 Ghz + * @PM_P2P_CLI_SAP_MCC_5_1x1: CLI & SAP connection on MCC using + * 1x1@5 Ghz + * @PM_P2P_CLI_SAP_MCC_5_2x2: CLI & SAP connection on MCC using + * 2x2@5 Ghz + * @POLICY_MGR_P2P_STA_SAP_MCC_24_5_1x1: CLI and SAP connecting on MCC + * in 2.4 and 5GHz 1x1 + * @POLICY_MGR_P2P_STA_SAP_MCC_24_5_2x2: CLI and SAP connecting on MCC + * in 2.4 and 5GHz 2x2 + * @PM_P2P_CLI_SAP_DBS_1x1,: CLI & SAP connection on DBS using 1x1 + * @PM_P2P_CLI_SAP_DBS_2x2: P2P CLI & SAP connection on DBS using + * 2x2 + * @PM_P2P_CLI_SAP_SBS_5_1x1: P2P CLI & SAP connection on 5G SBS + * using 1x1 + * @PM_SAP_SAP_SCC_24_1x1: SAP & SAP connection on + * SCC using 1x1@2.4 Ghz + * @PM_SAP_SAP_SCC_24_2x2: SAP & SAP connection on + * SCC using 2x2@2.4 Ghz + * @PM_SAP_SAP_MCC_24_1x1: SAP & SAP connection on + * MCC using 1x1@2.4 Ghz + * @PM_SAP_SAP_MCC_24_2x2: SAP & SAP connection on + * MCC using 2x2@2.4 Ghz + * @PM_SAP_SAP_SCC_5_1x1: SAP & SAP connection on + * SCC using 1x1@5 Ghz + * @PM_SAP_SAP_SCC_5_2x2: SAP & SAP connection on + * SCC using 2x2@5 Ghz + * @PM_SAP_SAP_MCC_5_1x1: SAP & SAP connection on + * MCC using 1x1@5 Ghz + * @PM_SAP_SAP_MCC_5_2x2: SAP & SAP connection on + * MCC using 2x2@5 Ghz + * @PM_SAP_SAP_MCC_24_5_1x1: SAP & SAP connection on + * MCC in 2.4 and 5GHz 1x1 + * @PM_SAP_SAP_MCC_24_5_2x2: SAP & SAP connection on + * MCC in 2.4 and 5GHz 2x2 + * @PM_SAP_SAP_DBS_1x1: SAP & SAP connection on DBS using + * 1x1 + * @PM_SAP_SAP_DBS_2x2: SAP & SAP connection on DBS using 2x2 + * @PM_SAP_SAP_SBS_5_1x1: SAP & SAP connection on 5G SBS using 1x1 + * @PM_SAP_NAN_DISC_SCC_24_1x1: SAP & NAN connection on + * SCC using 1x1@2.4 Ghz + * @PM_SAP_NAN_DISC_SCC_24_2x2: SAP & NAN connection on + * SCC using 2x2@2.4 Ghz + * @PM_SAP_NAN_DISC_MCC_24_1x1: SAP & NAN connection on + * MCC using 1x1@2.4 Ghz + * @PM_SAP_NAN_DISC_MCC_24_2x2: SAP & NAN connection on + * SCC using 2x2@2.4 Ghz + * @PM_SAP_NAN_DISC_DBS_1x1: SAP & NAN connection on DBS using 1x1 + * @PM_SAP_NAN_DISC_DBS_2x2: SAP & NAN connection on DBS using 2x2 + * @PM_STA_STA_SCC_24_1x1: STA & STA connection on + * SCC using 1x1@2.4 Ghz + * @PM_STA_STA_SCC_24_2x2: STA & STA connection on + * SCC using 2x2@2.4 Ghz + * @PM_STA_STA_MCC_24_1x1: STA & STA connection on + * MCC using 1x1@2.4 Ghz + * @PM_STA_STA_MCC_24_2x2: STA & STA connection on + * MCC using 2x2@2.4 Ghz + * @PM_STA_STA_SCC_5_1x1: STA & STA connection on + * SCC using 1x1@5 Ghz + * @PM_STA_STA_SCC_5_2x2: STA & STA connection on + * SCC using 2x2@5 Ghz + * @PM_STA_STA_MCC_5_1x1: STA & STA connection on + * MCC using 1x1@5 Ghz + * @PM_STA_STA_MCC_5_2x2: STA & STA connection on + * MCC using 2x2@5 Ghz + * @PM_STA_STA_MCC_24_5_1x1: STA & STA connection on + * MCC in 2.4 and 5GHz 1x1 + * @PM_STA_STA_MCC_24_5_2x2: STA & STA connection on + * MCC in 2.4 and 5GHz 2x2 + * @PM_STA_STA_DBS_1x1: STA & STA connection on DBS using + * 1x1 + * @PM_STA_STA_DBS_2x2: STA & STA connection on DBS using 2x2 + * @PM_STA_STA_SBS_5_1x1: STA & STA connection on 5G SBS using 1x1 + * @PM_STA_NAN_DISC_SCC_24_1x1: NAN & STA connection on SCC using 1x1 on 2.4 GHz + * @PM_STA_NAN_DISC_SCC_24_2x2: NAN & STA connection on SCC using 2x2 on 2.4 GHz + * @PM_STA_NAN_DISC_MCC_24_1x1: NAN & STA connection on MCC using 1x1 on 2.4 GHz + * @PM_STA_NAN_DISC_MCC_24_2x2: NAN & STA connection on MCC using 2x2 on 2.4 GHz + * @PM_STA_NAN_DISC_DBS_1x1: NAN & STA connection on DBS using 1x1 + * @PM_STA_NAN_DISC_DBS_2x2: NAN & STA connection on DBS using 2x2 + * @PM_NAN_DISC_NDI_SCC_24_1x1: NAN & NDI connection on SCC using 1x1 on 2.4 GHz + * @PM_NAN_DISC_NDI_SCC_24_2x2: NAN & NDI connection on SCC using 2x2 on 2.4 GHz + * @PM_NAN_DISC_NDI_MCC_24_1x1: NAN & NDI connection on MCC using 1x1 on 2.4 GHz + * @PM_NAN_DISC_NDI_MCC_24_2x2: NAN & NDI connection on MCC using 2x2 on 2.4 GHz + * @PM_NAN_DISC_NDI_DBS_1x1: NAN & NDI connection on DBS using 1x1 + * @PM_NAN_DISC_NDI_DBS_2x2: NAN & NDI connection on DBS using 2x2 + * @PM_P2P_GO_P2P_GO_SCC_24_1x1: P2P GO & P2P GO SCC on 2.4G using 1x1 + * @PM_P2P_GO_P2P_GO_SCC_24_2x2: P2P GO & P2P GO SCC on 2.4G using 2x2 + * @PM_P2P_GO_P2P_GO_MCC_24_1x1: P2P GO & P2P GO MCC on 2.4G using 1x1 + * @PM_P2P_GO_P2P_GO_MCC_24_2x2: P2P GO & P2P GO MCC on 2.4G using 2x2 + * @PM_P2P_GO_P2P_GO_SCC_5_1x1: P2P GO & P2P GO SCC on 5G using 1x1 + * @PM_P2P_GO_P2P_GO_SCC_5_2x2: P2P GO & P2P GO SCC on 5G using 2x2 + * @PM_P2P_GO_P2P_GO_MCC_5_1x1: P2P GO & P2P GO MCC on 5G using 1x1 + * @PM_P2P_GO_P2P_GO_MCC_5_2x2: P2P GO & P2P GO MCC on 5G using 2x2 + * @PM_P2P_GO_P2P_GO_MCC_24_5_1x1: P2P GO 2.4G & P2P GO 5G dual band MCC + * using 1x1 + * @PM_P2P_GO_P2P_GO_MCC_24_5_2x2: P2P GO 2.4G & P2P GO 5G dual band MCC + * using 2x2 + * @PM_P2P_GO_P2P_GO_DBS_1x1: P2P GO & P2P GO on DBS using 1x1 + * @PM_P2P_GO_P2P_GO_DBS_2x2: P2P GO & P2P GO on DBS using 2x2 + * @PM_P2P_GO_P2P_GO_SBS_5_1x1: P2P GO & P2P GO on SBS using 1x1 + * + * These are generic IDs that identify the various roles in the + * software system + */ +enum policy_mgr_two_connection_mode { + PM_STA_SAP_SCC_24_1x1 = 0, + PM_STA_SAP_SCC_24_2x2, + PM_STA_SAP_MCC_24_1x1, + PM_STA_SAP_MCC_24_2x2, + PM_STA_SAP_SCC_5_1x1, + PM_STA_SAP_SCC_5_2x2, + PM_STA_SAP_MCC_5_1x1, + PM_STA_SAP_MCC_5_2x2, + PM_STA_SAP_MCC_24_5_1x1, + PM_STA_SAP_MCC_24_5_2x2, + PM_STA_SAP_DBS_1x1, + PM_STA_SAP_DBS_2x2, + PM_STA_SAP_SBS_5_1x1, + PM_STA_P2P_GO_SCC_24_1x1, + PM_STA_P2P_GO_SCC_24_2x2, + PM_STA_P2P_GO_MCC_24_1x1, + PM_STA_P2P_GO_MCC_24_2x2, + PM_STA_P2P_GO_SCC_5_1x1, + PM_STA_P2P_GO_SCC_5_2x2, + PM_STA_P2P_GO_MCC_5_1x1, + PM_STA_P2P_GO_MCC_5_2x2, + PM_STA_P2P_GO_MCC_24_5_1x1, + PM_STA_P2P_GO_MCC_24_5_2x2, + PM_STA_P2P_GO_DBS_1x1, + PM_STA_P2P_GO_DBS_2x2, + PM_STA_P2P_GO_SBS_5_1x1, + PM_STA_P2P_CLI_SCC_24_1x1, + PM_STA_P2P_CLI_SCC_24_2x2, + PM_STA_P2P_CLI_MCC_24_1x1, + PM_STA_P2P_CLI_MCC_24_2x2, + PM_STA_P2P_CLI_SCC_5_1x1, + PM_STA_P2P_CLI_SCC_5_2x2, + PM_STA_P2P_CLI_MCC_5_1x1, + PM_STA_P2P_CLI_MCC_5_2x2, + PM_STA_P2P_CLI_MCC_24_5_1x1, + PM_STA_P2P_CLI_MCC_24_5_2x2, + PM_STA_P2P_CLI_DBS_1x1, + PM_STA_P2P_CLI_DBS_2x2, + PM_STA_P2P_CLI_SBS_5_1x1, + PM_P2P_GO_P2P_CLI_SCC_24_1x1, + PM_P2P_GO_P2P_CLI_SCC_24_2x2, + PM_P2P_GO_P2P_CLI_MCC_24_1x1, + PM_P2P_GO_P2P_CLI_MCC_24_2x2, + PM_P2P_GO_P2P_CLI_SCC_5_1x1, + PM_P2P_GO_P2P_CLI_SCC_5_2x2, + PM_P2P_GO_P2P_CLI_MCC_5_1x1, + PM_P2P_GO_P2P_CLI_MCC_5_2x2, + PM_P2P_GO_P2P_CLI_MCC_24_5_1x1, + PM_P2P_GO_P2P_CLI_MCC_24_5_2x2, + PM_P2P_GO_P2P_CLI_DBS_1x1, + PM_P2P_GO_P2P_CLI_DBS_2x2, + PM_P2P_GO_P2P_CLI_SBS_5_1x1, + PM_P2P_GO_SAP_SCC_24_1x1, + PM_P2P_GO_SAP_SCC_24_2x2, + PM_P2P_GO_SAP_MCC_24_1x1, + PM_P2P_GO_SAP_MCC_24_2x2, + PM_P2P_GO_SAP_SCC_5_1x1, + PM_P2P_GO_SAP_SCC_5_2x2, + PM_P2P_GO_SAP_MCC_5_1x1, + PM_P2P_GO_SAP_MCC_5_2x2, + PM_P2P_GO_SAP_MCC_24_5_1x1, + PM_P2P_GO_SAP_MCC_24_5_2x2, + PM_P2P_GO_SAP_DBS_1x1, + PM_P2P_GO_SAP_DBS_2x2, + PM_P2P_GO_SAP_SBS_5_1x1, + PM_P2P_CLI_SAP_SCC_24_1x1, + PM_P2P_CLI_SAP_SCC_24_2x2, + PM_P2P_CLI_SAP_MCC_24_1x1, + PM_P2P_CLI_SAP_MCC_24_2x2, + PM_P2P_CLI_SAP_SCC_5_1x1, + PM_P2P_CLI_SAP_SCC_5_2x2, + PM_P2P_CLI_SAP_MCC_5_1x1, + PM_P2P_CLI_SAP_MCC_5_2x2, + PM_P2P_CLI_SAP_MCC_24_5_1x1, + PM_P2P_CLI_SAP_MCC_24_5_2x2, + PM_P2P_CLI_SAP_DBS_1x1, + PM_P2P_CLI_SAP_DBS_2x2, + PM_P2P_CLI_SAP_SBS_5_1x1, + PM_SAP_SAP_SCC_24_1x1, + PM_SAP_SAP_SCC_24_2x2, + PM_SAP_SAP_MCC_24_1x1, + PM_SAP_SAP_MCC_24_2x2, + PM_SAP_SAP_SCC_5_1x1, + PM_SAP_SAP_SCC_5_2x2, + PM_SAP_SAP_MCC_5_1x1, + PM_SAP_SAP_MCC_5_2x2, + PM_SAP_SAP_MCC_24_5_1x1, + PM_SAP_SAP_MCC_24_5_2x2, + PM_SAP_SAP_DBS_1x1, + PM_SAP_SAP_DBS_2x2, + PM_SAP_SAP_SBS_5_1x1, + PM_SAP_NAN_DISC_SCC_24_1x1, + PM_SAP_NAN_DISC_SCC_24_2x2, + PM_SAP_NAN_DISC_MCC_24_1x1, + PM_SAP_NAN_DISC_MCC_24_2x2, + PM_SAP_NAN_DISC_DBS_1x1, + PM_SAP_NAN_DISC_DBS_2x2, + PM_STA_STA_SCC_24_1x1, + PM_STA_STA_SCC_24_2x2, + PM_STA_STA_MCC_24_1x1, + PM_STA_STA_MCC_24_2x2, + PM_STA_STA_SCC_5_1x1, + PM_STA_STA_SCC_5_2x2, + PM_STA_STA_MCC_5_1x1, + PM_STA_STA_MCC_5_2x2, + PM_STA_STA_MCC_24_5_1x1, + PM_STA_STA_MCC_24_5_2x2, + PM_STA_STA_DBS_1x1, + PM_STA_STA_DBS_2x2, + PM_STA_STA_SBS_5_1x1, + PM_STA_NAN_DISC_SCC_24_1x1, + PM_STA_NAN_DISC_SCC_24_2x2, + PM_STA_NAN_DISC_MCC_24_1x1, + PM_STA_NAN_DISC_MCC_24_2x2, + PM_STA_NAN_DISC_DBS_1x1, + PM_STA_NAN_DISC_DBS_2x2, + PM_NAN_DISC_NDI_SCC_24_1x1, + PM_NAN_DISC_NDI_SCC_24_2x2, + PM_NAN_DISC_NDI_MCC_24_1x1, + PM_NAN_DISC_NDI_MCC_24_2x2, + PM_NAN_DISC_NDI_DBS_1x1, + PM_NAN_DISC_NDI_DBS_2x2, + PM_P2P_GO_P2P_GO_SCC_24_1x1, + PM_P2P_GO_P2P_GO_SCC_24_2x2, + PM_P2P_GO_P2P_GO_MCC_24_1x1, + PM_P2P_GO_P2P_GO_MCC_24_2x2, + PM_P2P_GO_P2P_GO_SCC_5_1x1, + PM_P2P_GO_P2P_GO_SCC_5_2x2, + PM_P2P_GO_P2P_GO_MCC_5_1x1, + PM_P2P_GO_P2P_GO_MCC_5_2x2, + PM_P2P_GO_P2P_GO_MCC_24_5_1x1, + PM_P2P_GO_P2P_GO_MCC_24_5_2x2, + PM_P2P_GO_P2P_GO_DBS_1x1, + PM_P2P_GO_P2P_GO_DBS_2x2, + PM_P2P_GO_P2P_GO_SBS_5_1x1, + PM_MAX_TWO_CONNECTION_MODE +}; + +#ifdef FEATURE_FOURTH_CONNECTION +/** + * enum policy_mgr_three_connection_mode - Combination of first three + * connections type, concurrency state, band used. + * + * @PM_STA_SAP_SCC_24_SAP_5_DBS: STA & SAP connection on 2.4 Ghz SCC, another + * SAP on 5 G + * @PM_STA_SAP_SCC_5_SAP_24_DBS: STA & SAP connection on 5 Ghz SCC, another + * SAP on 2.4 G + * @PM_STA_SAP_SCC_24_STA_5_DBS: STA & SAP connection on 2.4 Ghz SCC, another + * STA on 5G + * @PM_STA_SAP_SCC_5_STA_24_DBS: STA & SAP connection on 5 Ghz SCC, another + * STA on 2.4 G + * @PM_NAN_DISC_SAP_SCC_24_NDI_5_DBS: NAN_DISC & SAP connection on 2.4 Ghz SCC, + * NDI/NDP on 5 G + * @PM_NAN_DISC_NDI_SCC_24_SAP_5_DBS: NAN_DISC & NDI/NDP connection on 2.4 Ghz + * SCC, SAP on 5 G + * @PM_SAP_NDI_SCC_5_NAN_DISC_24_DBS: SAP & NDI/NDP connection on 5 Ghz, + * NAN_DISC on 24 Ghz + * @PM_NAN_DISC_STA_24_NDI_5_DBS: STA and NAN Disc on 2.4Ghz and NDI on 5ghz DBS + * @PM_NAN_DISC_NDI_24_STA_5_DBS: NDI and NAN Disc on 2.4Ghz and STA on 5ghz DBS + * @PM_STA_NDI_5_NAN_DISC_24_DBS: STA, NDI on 5ghz and NAN Disc on 2.4Ghz DBS + * @PM_STA_NDI_NAN_DISC_24_SMM: STA, NDI, NAN Disc all on 2.4ghz SMM + * @PM_NAN_DISC_NDI_24_NDI_5_DBS: NDI and NAN Disc on 2.4Ghz and second NDI in + * 5ghz DBS + * @PM_NDI_NDI_5_NAN_DISC_24_DBS: Both NDI on 5ghz and NAN Disc on 2.4Ghz DBS + * @PM_NDI_NDI_NAN_DISC_24_SMM: Both NDI, NAN Disc on 2.4ghz SMM + */ +enum policy_mgr_three_connection_mode { + PM_STA_SAP_SCC_24_SAP_5_DBS, + PM_STA_SAP_SCC_5_SAP_24_DBS, + PM_STA_SAP_SCC_24_STA_5_DBS, + PM_STA_SAP_SCC_5_STA_24_DBS, + PM_NAN_DISC_SAP_SCC_24_NDI_5_DBS, + PM_NAN_DISC_NDI_SCC_24_SAP_5_DBS, + PM_SAP_NDI_SCC_5_NAN_DISC_24_DBS, + PM_NAN_DISC_STA_24_NDI_5_DBS, + PM_NAN_DISC_NDI_24_STA_5_DBS, + PM_STA_NDI_5_NAN_DISC_24_DBS, + PM_STA_NDI_NAN_DISC_24_SMM, + PM_NAN_DISC_NDI_24_NDI_5_DBS, + PM_NDI_NDI_5_NAN_DISC_24_DBS, + PM_NDI_NDI_NAN_DISC_24_SMM, + + PM_MAX_THREE_CONNECTION_MODE +}; +#endif + +/** + * enum policy_mgr_conc_next_action - actions to be taken on old + * connections. + * + * @PM_NOP: No action + * @PM_DBS: switch to DBS mode + * @PM_DBS_DOWNGRADE: switch to DBS mode & downgrade to 1x1 + * @PM_DBS_UPGRADE: switch to DBS mode & upgrade to 2x2 + * @PM_SINGLE_MAC: switch to MCC/SCC mode + * @PM_SINGLE_MAC_UPGRADE: switch to MCC/SCC mode & upgrade to 2x2 + * @PM_SBS: switch to SBS mode + * @PM_SBS_DOWNGRADE: switch to SBS mode & downgrade to 1x1 + * @PM_DOWNGRADE: downgrade to 1x1 + * @PM_UPGRADE: upgrade to 2x2 + * @PM_DBS1: switch to DBS 1 + * @PM_DBS1_DOWNGRADE: downgrade 2G beaconing entity to 1x1 and switch to DBS1. + * @PM_DBS2: switch to DBS 2 + * @PM_DBS2_DOWNGRADE: downgrade 5G beaconing entity to 1x1 and switch to DBS2. + * @PM_UPGRADE_5G: upgrade 5g beaconing entity to 2x2. + * @PM_UPGRADE_2G: upgrade 2g beaconing entity to 2x2. + * @PM_MAX_CONC_PRIORITY_MODE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_conc_next_action { + PM_NOP = 0, + PM_DBS, + PM_DBS_DOWNGRADE, + PM_DBS_UPGRADE, + PM_SINGLE_MAC, + PM_SINGLE_MAC_UPGRADE, + PM_SBS, + PM_SBS_DOWNGRADE, + PM_DOWNGRADE, + PM_UPGRADE, + PM_DBS1, + PM_DBS1_DOWNGRADE, + PM_DBS2, + PM_DBS2_DOWNGRADE, + PM_UPGRADE_5G, + PM_UPGRADE_2G, + + PM_MAX_CONC_NEXT_ACTION +}; + +/** + * enum policy_mgr_band - wifi band. + * + * @POLICY_MGR_BAND_24: 2.4 Ghz band + * @POLICY_MGR_BAND_5: 5 Ghz band + * @POLICY_MGR_ANY: to specify all band + * @POLICY_MGR_MAX_BAND: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_band { + POLICY_MGR_BAND_24 = 0, + POLICY_MGR_BAND_5, + POLICY_MGR_ANY, + POLICY_MGR_MAX_BAND = POLICY_MGR_ANY, +}; + +/** + * enum policy_mgr_conn_update_reason: Reason for conc connection update + * @POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN: Set probable operating channel + * @POLICY_MGR_UPDATE_REASON_JOIN_IBSS: Join IBSS + * @POLICY_MGR_UPDATE_REASON_UT: Unit test related + * @POLICY_MGR_UPDATE_REASON_START_AP: Start AP + * @POLICY_MGR_UPDATE_REASON_NORMAL_STA: Connection to Normal STA + * @POLICY_MGR_UPDATE_REASON_HIDDEN_STA: Connection to Hidden STA + * @POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC: Opportunistic HW mode update + * @POLICY_MGR_UPDATE_REASON_NSS_UPDATE: NSS update + * @POLICY_MGR_UPDATE_REASON_AFTER_CHANNEL_SWITCH: After Channel switch + * @POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_STA: Before Channel switch for STA + * @POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_SAP: Before Channel switch for SAP + * @POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE: In Dual DBS HW, if the vdev based + * 2x2 preference enabled, the vdev down may cause prioritized active + * vdev change, then DBS hw mode may needs to change from one DBS mode + * to the other DBS mode. This reason code indicates such condition. + * @POLICY_MGR_UPDATE_REASON_NAN_DISCOVERY: NAN Discovery related + * @POLICY_MGR_UPDATE_REASON_NDP_UPDATE: NAN Datapath related update + */ +enum policy_mgr_conn_update_reason { + POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN, + POLICY_MGR_UPDATE_REASON_JOIN_IBSS, + POLICY_MGR_UPDATE_REASON_UT, + POLICY_MGR_UPDATE_REASON_START_AP, + POLICY_MGR_UPDATE_REASON_NORMAL_STA, + POLICY_MGR_UPDATE_REASON_HIDDEN_STA, + POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC, + POLICY_MGR_UPDATE_REASON_NSS_UPDATE, + POLICY_MGR_UPDATE_REASON_AFTER_CHANNEL_SWITCH, + POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_STA, + POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_SAP, + POLICY_MGR_UPDATE_REASON_PRE_CAC, + POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE, + POLICY_MGR_UPDATE_REASON_NAN_DISCOVERY, + POLICY_MGR_UPDATE_REASON_NDP_UPDATE, + POLICY_MGR_UPDATE_REASON_LFR2_ROAM, +}; + +/** + * enum hw_mode_bandwidth - bandwidth of wifi channel. + * + * @HW_MODE_5_MHZ: 5 Mhz bandwidth + * @HW_MODE_10_MHZ: 10 Mhz bandwidth + * @HW_MODE_20_MHZ: 20 Mhz bandwidth + * @HW_MODE_40_MHZ: 40 Mhz bandwidth + * @HW_MODE_80_MHZ: 80 Mhz bandwidth + * @HW_MODE_80_PLUS_80_MHZ: 80 Mhz plus 80 Mhz bandwidth + * @HW_MODE_160_MHZ: 160 Mhz bandwidth + * @HW_MODE_MAX_BANDWIDTH: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum hw_mode_bandwidth { + HW_MODE_BW_NONE, + HW_MODE_5_MHZ, + HW_MODE_10_MHZ, + HW_MODE_20_MHZ, + HW_MODE_40_MHZ, + HW_MODE_80_MHZ, + HW_MODE_80_PLUS_80_MHZ, + HW_MODE_160_MHZ, + HW_MODE_MAX_BANDWIDTH +}; + +/** + * enum set_hw_mode_status - Status of set HW mode command + * @SET_HW_MODE_STATUS_OK: command successful + * @SET_HW_MODE_STATUS_EINVAL: Requested invalid hw_mode + * @SET_HW_MODE_STATUS_ECANCELED: HW mode change cancelled + * @SET_HW_MODE_STATUS_ENOTSUP: HW mode not supported + * @SET_HW_MODE_STATUS_EHARDWARE: HW mode change prevented by hardware + * @SET_HW_MODE_STATUS_EPENDING: HW mode change is pending + * @SET_HW_MODE_STATUS_ECOEX: HW mode change conflict with Coex + * @SET_HW_MODE_STATUS_ALREADY: Requested hw mode is already applied to FW. + */ +enum set_hw_mode_status { + SET_HW_MODE_STATUS_OK, + SET_HW_MODE_STATUS_EINVAL, + SET_HW_MODE_STATUS_ECANCELED, + SET_HW_MODE_STATUS_ENOTSUP, + SET_HW_MODE_STATUS_EHARDWARE, + SET_HW_MODE_STATUS_EPENDING, + SET_HW_MODE_STATUS_ECOEX, + SET_HW_MODE_STATUS_ALREADY, +}; + +typedef void (*dual_mac_cb)(enum set_hw_mode_status status, + uint32_t scan_config, + uint32_t fw_mode_config); +/** + * enum policy_mgr_hw_mode_change - identify the HW mode switching to. + * + * @POLICY_MGR_HW_MODE_NOT_IN_PROGRESS: HW mode change not in progress + * @POLICY_MGR_SMM_IN_PROGRESS: switching to SMM mode + * @POLICY_MGR_DBS_IN_PROGRESS: switching to DBS mode + * @POLICY_MGR_SBS_IN_PROGRESS: switching to SBS mode + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_hw_mode_change { + POLICY_MGR_HW_MODE_NOT_IN_PROGRESS = 0, + POLICY_MGR_SMM_IN_PROGRESS, + POLICY_MGR_DBS_IN_PROGRESS, + POLICY_MGR_SBS_IN_PROGRESS +}; + +/** + * enum dbs_support - structure to define INI values and their meaning + * + * @ENABLE_DBS_CXN_AND_SCAN: Enable DBS support for connection and scan + * @DISABLE_DBS_CXN_AND_SCAN: Disable DBS support for connection and scan + * @DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN: disable dbs support for + * connection but keep dbs support for scan + * @DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN_WITH_ASYNC_SCAN_OFF: disable dbs support + * for connection but keep dbs for scan but switch off the async scan + * @ENABLE_DBS_CXN_AND_ENABLE_SCAN_WITH_ASYNC_SCAN_OFF: enable dbs support for + * connection and scan but switch off the async scan + * @ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN: Enable DBS support for connection and + * disable DBS support for scan + * @ENABLE_DBS_CXN_AND_DISABLE_SIMULTANEOUS_SCAN: Enable DBS + * support for connection and disable simultaneous scan from + * upper layer (DBS scan remains enabled in FW) + */ +enum dbs_support { + ENABLE_DBS_CXN_AND_SCAN, + DISABLE_DBS_CXN_AND_SCAN, + DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN, + DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN_WITH_ASYNC_SCAN_OFF, + ENABLE_DBS_CXN_AND_ENABLE_SCAN_WITH_ASYNC_SCAN_OFF, + ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN, + ENABLE_DBS_CXN_AND_DISABLE_SIMULTANEOUS_SCAN, +}; + +/** + * enum conn_6ghz_flag - structure to define connection 6ghz capable info + * in policy mgr conn info struct + * + * @CONN_6GHZ_FLAG_VALID: The 6ghz flag is valid (has been initialized) + * @CONN_6GHZ_FLAG_ACS_OR_USR_ALLOWED: AP is configured in 6ghz band capable + * by user: + * a. ACS channel range includes 6ghz. + * b. SAP is started in 6ghz fix channel. + * @CONN_6GHZ_FLAG_SECURITY_ALLOWED: AP has security mode which is permitted in + * 6ghz band. + * @CONN_6GHZ_FLAG_NO_LEGACY_CLIENT: AP has no legacy client connected + */ +enum conn_6ghz_flag { + CONN_6GHZ_FLAG_VALID = 0x0001, + CONN_6GHZ_FLAG_ACS_OR_USR_ALLOWED = 0x0002, + CONN_6GHZ_FLAG_SECURITY_ALLOWED = 0x0004, + CONN_6GHZ_FLAG_NO_LEGACY_CLIENT = 0x0008, +}; + +#define CONN_6GHZ_CAPABLIE (CONN_6GHZ_FLAG_VALID | \ + CONN_6GHZ_FLAG_ACS_OR_USR_ALLOWED | \ + CONN_6GHZ_FLAG_SECURITY_ALLOWED | \ + CONN_6GHZ_FLAG_NO_LEGACY_CLIENT) + +/** + * struct policy_mgr_conc_connection_info - information of all existing + * connections in the wlan system + * + * @mode: connection type + * @freq: Channel frequency + * @bw: channel bandwidth used for the connection + * @mac: The HW mac it is running + * @chain_mask: The original capability advertised by HW + * @original_nss: nss negotiated at connection time + * @vdev_id: vdev id of the connection + * @in_use: if the table entry is active + * @ch_flagext: Channel extension flags. + * @conn_6ghz_flag: connection 6ghz capable flags + */ +struct policy_mgr_conc_connection_info { + enum policy_mgr_con_mode mode; + uint32_t freq; + enum hw_mode_bandwidth bw; + uint8_t mac; + enum policy_mgr_chain_mode chain_mask; + uint32_t original_nss; + uint32_t vdev_id; + bool in_use; + uint16_t ch_flagext; + enum conn_6ghz_flag conn_6ghz_flag; +}; + +/** + * struct policy_mgr_hw_mode_params - HW mode params + * @mac0_tx_ss: MAC0 Tx spatial stream + * @mac0_rx_ss: MAC0 Rx spatial stream + * @mac1_tx_ss: MAC1 Tx spatial stream + * @mac1_rx_ss: MAC1 Rx spatial stream + * @mac0_bw: MAC0 bandwidth + * @mac1_bw: MAC1 bandwidth + * @mac0_band_cap: mac0 band (5g/2g) capability + * @dbs_cap: DBS capabality + * @agile_dfs_cap: Agile DFS capabality + * @action_type: for dbs mode, the field indicates the "Action type" to be + * used to switch to the mode. To help the hw mode validation. + */ +struct policy_mgr_hw_mode_params { + uint8_t mac0_tx_ss; + uint8_t mac0_rx_ss; + uint8_t mac1_tx_ss; + uint8_t mac1_rx_ss; + uint8_t mac0_bw; + uint8_t mac1_bw; + uint8_t mac0_band_cap; + uint8_t dbs_cap; + uint8_t agile_dfs_cap; + uint8_t sbs_cap; + enum policy_mgr_conc_next_action action_type; +}; + +/** + * struct policy_mgr_vdev_mac_map - vdev id-mac id map + * @vdev_id: VDEV id + * @mac_id: MAC id + */ +struct policy_mgr_vdev_mac_map { + uint32_t vdev_id; + uint32_t mac_id; +}; + +/** + * struct policy_mgr_dual_mac_config - Dual MAC configuration + * @scan_config: Scan configuration + * @fw_mode_config: FW mode configuration + * @set_dual_mac_cb: Callback function to be executed on response to the command + */ +struct policy_mgr_dual_mac_config { + uint32_t scan_config; + uint32_t fw_mode_config; + dual_mac_cb set_dual_mac_cb; +}; + +/** + * struct policy_mgr_hw_mode - Format of set HW mode + * @hw_mode_index: Index of HW mode to be set + * @set_hw_mode_cb: HDD set HW mode callback + * @reason: Reason for HW mode change + * @session_id: Session id + * @next_action: next action to happen at policy mgr + * @action: current hw change action to be done + * @context: psoc context + */ +struct policy_mgr_hw_mode { + uint32_t hw_mode_index; + void *set_hw_mode_cb; + enum policy_mgr_conn_update_reason reason; + uint32_t session_id; + uint8_t next_action; + enum policy_mgr_conc_next_action action; + struct wlan_objmgr_psoc *context; +}; + +/** + * struct policy_mgr_pcl_list - Format of PCL + * @pcl_list: List of preferred channels + * @weight_list: Weights of the PCL + * @pcl_len: Number of channels in the PCL + */ +struct policy_mgr_pcl_list { + uint32_t pcl_list[NUM_CHANNELS]; + uint8_t weight_list[NUM_CHANNELS]; + uint32_t pcl_len; +}; + +/** + * struct policy_mgr_pcl_chan_weights - Params to get the valid weighed list + * @pcl_list: Preferred channel list already sorted in the order of preference + * @pcl_len: Length of the PCL + * @saved_chan_list: Valid channel list updated as part of + * WMA_UPDATE_CHAN_LIST_REQ + * @saved_num_chan: Length of the valid channel list + * @weighed_valid_list: Weights of the valid channel list. This will have one + * to one mapping with valid_chan_list. FW expects channel order and size to be + * as per the list provided in WMI_SCAN_CHAN_LIST_CMDID. + * @weight_list: Weights assigned by policy manager + */ +struct policy_mgr_pcl_chan_weights { + uint32_t pcl_list[NUM_CHANNELS]; + uint32_t pcl_len; + uint32_t saved_chan_list[NUM_CHANNELS]; + uint32_t saved_num_chan; + uint8_t weighed_valid_list[NUM_CHANNELS]; + uint8_t weight_list[NUM_CHANNELS]; +}; + +/** + * struct policy_mgr_vdev_entry_info - vdev related param to be + * used by policy manager + * @type: type + * @sub_type: sub type + * @mhz: channel frequency in MHz + * @chan_width: channel bandwidth + * @mac_id: the mac on which vdev is on + * @ch_flagext: Channel extension flags. + */ +struct policy_mgr_vdev_entry_info { + uint32_t type; + uint32_t sub_type; + uint32_t mhz; + uint32_t chan_width; + uint32_t mac_id; + uint16_t ch_flagext; +}; + +/** + * struct dbs_hw_mode_info - WLAN_DBS_HW_MODES_TLV Format + * @tlv_header: TLV header, TLV tag and len; tag equals WMITLV_TAG_ARRAY_UINT32 + * @hw_mode_list: WLAN_DBS_HW_MODE_LIST entries + */ +struct dbs_hw_mode_info { + uint32_t tlv_header; + uint32_t *hw_mode_list; +}; + +/** + * struct dual_mac_config - Dual MAC configurations + * @prev_scan_config: Previous scan configuration + * @prev_fw_mode_config: Previous FW mode configuration + * @cur_scan_config: Current scan configuration + * @cur_fw_mode_config: Current FW mode configuration + * @req_scan_config: Requested scan configuration + * @req_fw_mode_config: Requested FW mode configuration + */ +struct dual_mac_config { + uint32_t prev_scan_config; + uint32_t prev_fw_mode_config; + uint32_t cur_scan_config; + uint32_t cur_fw_mode_config; + uint32_t req_scan_config; + uint32_t req_fw_mode_config; +}; + +/** + * enum policy_mgr_pri_id - vdev type priority id + * @PM_STA_PRI_ID: station vdev type priority id + * @PM_SAP_PRI_ID: sap vdev type priority id + * @PM_P2P_GO_PRI_ID: p2p go vdev type priority id + * @PM_P2P_CLI_PRI_ID: p2p cli vdev type priority id + * @PM_MAX_PRI_ID: vdev type priority id max value + */ +enum policy_mgr_pri_id { + PM_STA_PRI_ID = 1, + PM_SAP_PRI_ID, + PM_P2P_GO_PRI_ID, + PM_P2P_CLI_PRI_ID, + PM_MAX_PRI_ID = 0xF, +}; + +#define PM_GET_BAND_PREFERRED(_policy_) ((_policy_) & 0x1) +#define PM_GET_VDEV_PRIORITY_ENABLED(_policy_) (((_policy_) & 0x2) >> 1) + +/** + * struct policy_mgr_user_cfg - Policy manager user config variables + * @enable2x2: 2x2 chain mask user config + * @sub_20_mhz_enabled: Is 5 or 10 Mhz enabled + */ +struct policy_mgr_user_cfg { + bool enable2x2; + bool sub_20_mhz_enabled; +}; + +/** + * struct dbs_nss - Number of spatial streams in DBS mode + * @mac0_ss: Number of spatial streams on MAC0 + * @mac1_ss: Number of spatial streams on MAC1 + * @single_mac0_band_cap: Mac0 band capability for single mac hw mode + */ +struct dbs_nss { + enum hw_mode_ss_config mac0_ss; + enum hw_mode_ss_config mac1_ss; + uint32_t single_mac0_band_cap; +}; + +/** + * struct connection_info - connection information + * @mac_id: The HW mac it is running + * @vdev_id: vdev id + * @channel: channel of the connection + * @ch_freq: channel freq in Mhz + */ +struct connection_info { + uint8_t mac_id; + uint8_t vdev_id; + uint8_t channel; + uint32_t ch_freq; +}; +#endif /* __WLAN_POLICY_MGR_PUBLIC_STRUCT_H */ diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_ucfg.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_ucfg.h new file mode 100644 index 0000000000000000000000000000000000000000..dd4682377539b28eef4a41e6e0a939f6d490e157 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_ucfg.h @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __WLAN_POLICY_MGR_UCFG +#define __WLAN_POLICY_MGR_UCFG +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_global_obj.h" +#include "qdf_status.h" + + +/** + * ucfg_policy_mgr_psoc_open() - This API sets CFGs to policy manager context + * + * This API pulls policy manager's context from PSOC and initialize the CFG + * structure of policy manager. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_psoc_open(struct wlan_objmgr_psoc *psoc); +/** + * ucfg_policy_mgr_psoc_close() - This API resets CFGs for policy manager ctx + * + * This API pulls policy manager's context from PSOC and resets the CFG + * structure of policy manager. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +void ucfg_policy_mgr_psoc_close(struct wlan_objmgr_psoc *psoc); +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * ucfg_policy_mgr_get_mcc_scc_switch() - To mcc to scc switch setting from INI + * @psoc: pointer to psoc + * @mcc_scc_switch: value to be filled + * + * This API pulls mcc to scc switch setting which is given as part of INI and + * stored in policy manager's CFGs. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch); +#else +static inline +QDF_STATUS ucfg_policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch) +{ + return QDF_STATUS_SUCCESS; +} +#endif //FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * ucfg_policy_mgr_get_sys_pref() - to get system preference + * @psoc: pointer to psoc + * @sys_pref: value to be filled + * + * This API pulls the system preference for policy manager to provide + * PCL + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t *sys_pref); +/** + * ucfg_policy_mgr_set_sys_pref() - to set system preference + * @psoc: pointer to psoc + * @sys_pref: value to be applied as new INI setting + * + * This API is meant to override original INI setting for system pref + * with new value which is used by policy manager to provide PCL + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_set_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t sys_pref); + +/** + * ucfg_policy_mgr_get_conc_rule1() - to find out if conc rule1 is enabled + * @psoc: pointer to psoc + * @conc_rule1: value to be filled + * + * This API is used to find out if conc rule-1 is enabled by user + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_conc_rule1(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule1); +/** + * ucfg_policy_mgr_get_conc_rule2() - to find out if conc rule2 is enabled + * @psoc: pointer to psoc + * @conc_rule2: value to be filled + * + * This API is used to find out if conc rule-2 is enabled by user + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_conc_rule2(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule2); + +/** + * policy_mgr_get_chnl_select_plcy() - to get channel selection policy + * @psoc: pointer to psoc + * @chnl_select_plcy: value to be filled + * + * This API is used to find out which channel selection policy has been + * configured + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_chnl_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *chnl_select_plcy); +/** + * policy_mgr_get_mcc_adaptive_sch() - to get mcc adaptive scheduler + * @psoc: pointer to psoc + * @enable_mcc_adaptive_sch: value to be filled + * + * This API is used to find out if mcc adaptive scheduler enabled or disabled + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_get_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_mcc_adaptive_sch); + +/** + * ucfg_policy_mgr_get_dynamic_mcc_adaptive_sch() - to get dynamic mcc adaptive + * scheduler + * @psoc: pointer to psoc + * @dynamic_mcc_adaptive_sch: value to be filled + * + * This API is used to get dynamic mcc adaptive scheduler + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_get_dynamic_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + bool *dynamic_mcc_adaptive_sch); + +/** + * ucfg_policy_mgr_set_dynamic_mcc_adaptive_sch() - to set dynamic mcc adaptive + * scheduler + * @psoc: pointer to psoc + * @dynamic_mcc_adaptive_sch: value to be set + * + * This API is used to set dynamic mcc adaptive scheduler + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_set_dynamic_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + bool dynamic_mcc_adaptive_sch); + +/** + * ucfg_policy_mgr_get_sta_cxn_5g_band() - to get STA's connection in 5G config + * + * @psoc: pointer to psoc + * @enable_sta_cxn_5g_band: value to be filled + * + * This API is used to find out if STA connection in 5G band is allowed or + * disallowed. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_sta_cxn_5g_band(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sta_cxn_5g_band); +/** + * ucfg_policy_mgr_get_allow_mcc_go_diff_bi() - to get information on whether GO + * can have diff BI than STA in MCC + * @psoc: pointer to psoc + * @allow_mcc_go_diff_bi: value to be filled + * + * This API is used to find out whether GO's BI can different than STA in MCC + * scenario + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_get_allow_mcc_go_diff_bi(struct wlan_objmgr_psoc *psoc, + uint8_t *allow_mcc_go_diff_bi); +/** + * ucfg_policy_mgr_get_enable_overlap_chnl() - to find out if overlap channels + * are enabled for SAP + * @psoc: pointer to psoc + * @enable_overlap_chnl: value to be filled + * + * This API is used to find out whether overlap channels are enabled for SAP + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_get_enable_overlap_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_overlap_chnl); +/** + * ucfg_policy_mgr_get_dual_mac_feature() - to find out if DUAL MAC feature is + * enabled + * @psoc: pointer to psoc + * @dual_mac_feature: value to be filled + * + * This API is used to find out whether dual mac (dual radio) specific feature + * is enabled or not + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_dual_mac_feature(struct wlan_objmgr_psoc *psoc, + uint8_t *dual_mac_feature); +/** + * ucfg_policy_mgr_get_force_1x1() - to find out if 1x1 connection is enforced + * + * @psoc: pointer to psoc + * @force_1x1: value to be filled + * + * This API is used to find out if 1x1 connection is enforced. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_force_1x1(struct wlan_objmgr_psoc *psoc, + uint8_t *force_1x1); +/** + * ucfg_policy_mgr_get_sta_sap_scc_on_dfs_chnl() - to find out if STA and SAP + * SCC is allowed on DFS channel + * @psoc: pointer to psoc + * @sta_sap_scc_on_dfs_chnl: value to be filled + * + * This API is used to find out whether STA and SAP SCC is allowed on + * DFS channels + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_get_sta_sap_scc_on_dfs_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sta_sap_scc_on_dfs_chnl); +/** + * ucfg_policy_mgr_get_sta_sap_scc_lte_coex_chnl() - to find out if STA & SAP + * SCC is allowed on LTE COEX + * @psoc: pointer to psoc + * @sta_sap_scc_lte_coex: value to be filled + * + * This API is used to find out whether STA and SAP scc is allowed on LTE COEX + * channel + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_get_sta_sap_scc_lte_coex_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sta_sap_scc_lte_coex); + +/** + * ucfg_policy_mgr_get_dfs_master_dynamic_enabled() - support dfs master or not + * AP interface when STA+SAP(GO) concurrency + * @psoc: pointer to psoc + * @vdev_id: sap vdev id + * + * This API is used to check SAP (GO) dfs master functionality enabled or not + * when STA+SAP(GO) concurrency. + * If g_sta_sap_scc_on_dfs_chan is non-zero, the STA+SAP(GO) is allowed on DFS + * channel SCC and the SAP's DFS master functionality should be enable/disable + * according to: + * 1. g_sta_sap_scc_on_dfs_chan is 0: function return true - dfs master + * capability enabled. + * 2. g_sta_sap_scc_on_dfs_chan is 1: function return false - dfs master + * capability disabled. + * 3. g_sta_sap_scc_on_dfs_chan is 2: dfs master capability based on STA on + * 5G or not: + * a. 5G STA active - return false + * b. no 5G STA active -return true + * + * Return: true if dfs master functionality should be enabled. + */ +bool +ucfg_policy_mgr_get_dfs_master_dynamic_enabled(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * ucfg_policy_mgr_init_chan_avoidance() - init channel avoidance in policy + * manager + * @psoc: pointer to psoc + * @chan_freq_list: channel frequency list + * @chan_cnt: channel count + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_init_chan_avoidance(struct wlan_objmgr_psoc *psoc, + qdf_freq_t *chan_freq_list, + uint16_t chan_cnt); + +/** + * ucfg_policy_mgr_get_sap_mandt_chnl() - to find out if SAP mandatory channel + * support is enabled + * @psoc: pointer to psoc + * @sap_mandt_chnl: value to be filled + * + * This API is used to find out whether SAP's mandatory channel support + * is enabled + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_sap_mandt_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sap_mandt_chnl); +/** + * ucfg_policy_mgr_get_indoor_chnl_marking() - to get if indoor channel can be + * marked as disabled + * @psoc: pointer to psoc + * @indoor_chnl_marking: value to be filled + * + * This API is used to find out whether indoor channel can be marked as disabled + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_get_indoor_chnl_marking(struct wlan_objmgr_psoc *psoc, + uint8_t *indoor_chnl_marking); +#endif //__WLAN_POLICY_MGR_UCFG diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c new file mode 100644 index 0000000000000000000000000000000000000000..e4030175a1ccf46387ebaecbae3eb815f4d8cdf0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c @@ -0,0 +1,2812 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_action.c + * + * WLAN Concurrenct Connection Management APIs + * + */ + +/* Include files */ + +#include "wlan_policy_mgr_api.h" +#include "wlan_policy_mgr_tables_no_dbs_i.h" +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_objmgr_global_obj.h" +#include "qdf_platform.h" +#include "wlan_nan_api.h" +#include "nan_ucfg_api.h" +#include "sap_api.h" +#include "wlan_mlme_api.h" + +enum policy_mgr_conc_next_action (*policy_mgr_get_current_pref_hw_mode_ptr) + (struct wlan_objmgr_psoc *psoc); + +void policy_mgr_hw_mode_transition_cb(uint32_t old_hw_mode_index, + uint32_t new_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + struct wlan_objmgr_psoc *context) +{ + QDF_STATUS status; + struct policy_mgr_hw_mode_params hw_mode; + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(context); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + + if (!vdev_mac_map) { + policy_mgr_err("vdev_mac_map is NULL"); + return; + } + + policy_mgr_debug("old_hw_mode_index=%d, new_hw_mode_index=%d", + old_hw_mode_index, new_hw_mode_index); + + for (i = 0; i < num_vdev_mac_entries; i++) + policy_mgr_debug("vdev_id:%d mac_id:%d", + vdev_mac_map[i].vdev_id, + vdev_mac_map[i].mac_id); + + status = policy_mgr_get_hw_mode_from_idx(context, + new_hw_mode_index, &hw_mode); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Get HW mode failed: %d", status); + return; + } + + policy_mgr_debug("MAC0: TxSS:%d, RxSS:%d, Bw:%d band_cap:%d", + hw_mode.mac0_tx_ss, hw_mode.mac0_rx_ss, + hw_mode.mac0_bw, hw_mode.mac0_band_cap); + policy_mgr_debug("MAC1: TxSS:%d, RxSS:%d, Bw:%d", + hw_mode.mac1_tx_ss, hw_mode.mac1_rx_ss, + hw_mode.mac1_bw); + policy_mgr_debug("DBS:%d, Agile DFS:%d, SBS:%d", + hw_mode.dbs_cap, hw_mode.agile_dfs_cap, + hw_mode.sbs_cap); + + /* update pm_conc_connection_list */ + policy_mgr_update_hw_mode_conn_info(context, num_vdev_mac_entries, + vdev_mac_map, + hw_mode); + + if (pm_ctx->mode_change_cb) + pm_ctx->mode_change_cb(); + + return; +} + +QDF_STATUS policy_mgr_check_n_start_opportunistic_timer( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("PM ctx not valid. Oppurtunistic timer cannot start"); + return QDF_STATUS_E_FAILURE; + } + if (policy_mgr_need_opportunistic_upgrade(psoc, NULL)) { + /* let's start the timer */ + qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); + status = qdf_mc_timer_start( + &pm_ctx->dbs_opportunistic_timer, + DBS_OPPORTUNISTIC_TIME * 1000); + if (!QDF_IS_STATUS_SUCCESS(status)) + policy_mgr_err("Failed to start dbs opportunistic timer"); + } + return status; +} + +QDF_STATUS policy_mgr_pdev_set_hw_mode(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + enum hw_mode_ss_config mac0_ss, + enum hw_mode_bandwidth mac0_bw, + enum hw_mode_ss_config mac1_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs, + enum policy_mgr_conn_update_reason reason, + uint8_t next_action, enum policy_mgr_conc_next_action action) +{ + int8_t hw_mode_index; + struct policy_mgr_hw_mode msg; + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + /* + * if HW is not capable of doing 2x2 or ini config disabled 2x2, don't + * allow to request FW for 2x2 + */ + if ((HW_MODE_SS_2x2 == mac0_ss) && (!pm_ctx->user_cfg.enable2x2)) { + policy_mgr_debug("2x2 is not allowed downgrading to 1x1 for mac0"); + mac0_ss = HW_MODE_SS_1x1; + } + if ((HW_MODE_SS_2x2 == mac1_ss) && (!pm_ctx->user_cfg.enable2x2)) { + policy_mgr_debug("2x2 is not allowed downgrading to 1x1 for mac1"); + mac1_ss = HW_MODE_SS_1x1; + } + + hw_mode_index = policy_mgr_get_hw_mode_idx_from_dbs_hw_list(psoc, + mac0_ss, mac0_bw, mac1_ss, mac1_bw, mac0_band_cap, + dbs, dfs, sbs); + if (hw_mode_index < 0) { + policy_mgr_err("Invalid HW mode index obtained"); + return QDF_STATUS_E_FAILURE; + } + + /* Don't send WMI_PDEV_SET_HW_MODE_CMDID to FW if existing SAP / GO is + * in CAC-in-progress state. Host is blocking this command as FW is + * having design limitation and FW don't expect this command when CAC + * is in progress state. + */ + if (pm_ctx->hdd_cbacks.hdd_is_cac_in_progress && + pm_ctx->hdd_cbacks.hdd_is_cac_in_progress()) { + policy_mgr_err("SAP CAC_IN_PROGRESS state, drop WMI_PDEV_SET_HW_MODE_CMDID"); + return QDF_STATUS_E_FAILURE; + } + + msg.hw_mode_index = hw_mode_index; + msg.set_hw_mode_cb = (void *)policy_mgr_pdev_set_hw_mode_cb; + msg.reason = reason; + msg.session_id = session_id; + msg.next_action = next_action; + msg.action = action; + msg.context = psoc; + + policy_mgr_debug("set hw mode to sme: hw_mode_index: %d session:%d reason:%d action %d", + msg.hw_mode_index, msg.session_id, msg.reason, action); + + status = pm_ctx->sme_cbacks.sme_pdev_set_hw_mode(msg); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to set hw mode to SME"); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +enum policy_mgr_conc_next_action policy_mgr_need_opportunistic_upgrade( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conn_update_reason *reason) +{ + uint32_t conn_index; + enum policy_mgr_conc_next_action upgrade = PM_NOP; + enum policy_mgr_conc_next_action preferred_dbs_action; + uint8_t mac = 0; + struct policy_mgr_hw_mode_params hw_mode; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + goto exit; + } + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + policy_mgr_rl_debug("driver isn't dbs capable, no further action needed"); + goto exit; + } + + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + goto exit; + } + if (!hw_mode.dbs_cap) { + policy_mgr_debug("current HW mode is non-DBS capable"); + goto exit; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + /* Are both mac's still in use */ + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + policy_mgr_debug("index:%d mac:%d in_use:%d chan:%d org_nss:%d", + conn_index, + pm_conc_connection_list[conn_index].mac, + pm_conc_connection_list[conn_index].in_use, + pm_conc_connection_list[conn_index].freq, + pm_conc_connection_list[conn_index].original_nss); + if ((pm_conc_connection_list[conn_index].mac == 0) && + pm_conc_connection_list[conn_index].in_use) { + mac |= POLICY_MGR_MAC0; + if (POLICY_MGR_MAC0_AND_MAC1 == mac) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + } else if ((pm_conc_connection_list[conn_index].mac == 1) && + pm_conc_connection_list[conn_index].in_use) { + mac |= POLICY_MGR_MAC1; + if (policy_mgr_is_hw_dbs_required_for_band( + psoc, HW_MODE_MAC_BAND_2G) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[conn_index].freq)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("2X2 DBS capable with 2.4 GHZ connection"); + goto done; + } + if (POLICY_MGR_MAC0_AND_MAC1 == mac) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + } + } + /* Let's request for single MAC mode */ + upgrade = PM_SINGLE_MAC; + if (reason) + *reason = POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC; + /* Is there any connection had an initial connection with 2x2 */ + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].original_nss == 2) && + pm_conc_connection_list[conn_index].in_use) { + upgrade = PM_SINGLE_MAC_UPGRADE; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + +done: + if (upgrade == PM_NOP && hw_mode.dbs_cap && + policy_mgr_is_2x2_1x1_dbs_capable(psoc)) { + preferred_dbs_action = + policy_mgr_get_preferred_dbs_action_table( + psoc, INVALID_VDEV_ID, 0, 0); + if (hw_mode.action_type == PM_DBS1 && + preferred_dbs_action == PM_DBS2) { + upgrade = PM_DBS2_DOWNGRADE; + if (reason) + *reason = + POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE; + } else if (hw_mode.action_type == PM_DBS2 && + preferred_dbs_action == PM_DBS1) { + upgrade = PM_DBS1_DOWNGRADE; + if (reason) + *reason = + POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE; + } + } +exit: + return upgrade; +} + +QDF_STATUS policy_mgr_update_connection_info(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0, ch_freq; + bool found = false; + struct policy_mgr_vdev_entry_info conn_table_entry; + enum policy_mgr_chain_mode chain_mask = POLICY_MGR_ONE_ONE; + uint8_t nss_2g, nss_5g; + enum policy_mgr_con_mode mode; + uint32_t nss = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (vdev_id == pm_conc_connection_list[conn_index].vdev_id) { + /* debug msg */ + found = true; + break; + } + conn_index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + if (!found) { + /* err msg */ + policy_mgr_err("can't find vdev_id %d in pm_conc_connection_list", + vdev_id); + return status; + } + if (pm_ctx->wma_cbacks.wma_get_connection_info) { + status = pm_ctx->wma_cbacks.wma_get_connection_info( + vdev_id, &conn_table_entry); + if (QDF_STATUS_SUCCESS != status) { + policy_mgr_err("can't find vdev_id %d in connection table", + vdev_id); + return status; + } + } else { + policy_mgr_err("wma_get_connection_info is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = policy_mgr_get_mode(conn_table_entry.type, + conn_table_entry.sub_type); + ch_freq = conn_table_entry.mhz; + status = policy_mgr_get_nss_for_vdev(psoc, mode, &nss_2g, &nss_5g); + if (QDF_IS_STATUS_SUCCESS(status)) { + if ((WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq) && nss_2g > 1) || + (WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq) && nss_5g > 1)) + chain_mask = POLICY_MGR_TWO_TWO; + else + chain_mask = POLICY_MGR_ONE_ONE; + nss = (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) ? nss_2g : nss_5g; + } else { + policy_mgr_err("Error in getting nss"); + } + + policy_mgr_debug("update PM connection table for vdev:%d", vdev_id); + + /* add the entry */ + policy_mgr_update_conc_list( + psoc, conn_index, mode, ch_freq, + policy_mgr_get_bw(conn_table_entry.chan_width), + conn_table_entry.mac_id, chain_mask, + nss, vdev_id, true, true, conn_table_entry.ch_flagext); + + /* do we need to change the HW mode */ + policy_mgr_check_n_start_opportunistic_timer(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_update_and_wait_for_connection_update( + struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + uint32_t ch_freq, + enum policy_mgr_conn_update_reason reason) +{ + QDF_STATUS status; + + policy_mgr_debug("session:%d ch_freq:%d reason:%d", + session_id, ch_freq, reason); + + status = policy_mgr_reset_connection_update(psoc); + if (QDF_IS_STATUS_ERROR(status)) + policy_mgr_err("clearing event failed"); + + status = policy_mgr_current_connections_update( + psoc, session_id, ch_freq, reason); + if (QDF_STATUS_E_FAILURE == status) { + policy_mgr_err("connections update failed"); + return QDF_STATUS_E_FAILURE; + } + + /* Wait only when status is success */ + if (QDF_IS_STATUS_SUCCESS(status)) { + status = policy_mgr_wait_for_connection_update(psoc); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("qdf wait for event failed"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_is_dbs_allowed_for_concurrency() - If dbs is allowed for current + * concurreny + * @new_conn_mode: new connection mode + * + * When a new connection is about to come up, check if dbs is allowed for + * STA+STA or STA+P2P + * + * Return: true if dbs is allowed for STA+STA or STA+P2P else false + */ +bool policy_mgr_is_dbs_allowed_for_concurrency( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE new_conn_mode) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t count, dbs_for_sta_sta, dbs_for_sta_p2p; + bool ret = true; + uint32_t ch_sel_plcy; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return ret; + } + + count = policy_mgr_get_connection_count(psoc); + + if (count != 1 || new_conn_mode == QDF_MAX_NO_OF_MODE) + return ret; + + ch_sel_plcy = pm_ctx->cfg.chnl_select_plcy; + dbs_for_sta_sta = PM_CHANNEL_SELECT_LOGIC_STA_STA_GET(ch_sel_plcy); + dbs_for_sta_p2p = PM_CHANNEL_SELECT_LOGIC_STA_P2P_GET(ch_sel_plcy); + + switch (pm_conc_connection_list[0].mode) { + case PM_STA_MODE: + switch (new_conn_mode) { + case QDF_STA_MODE: + if (!dbs_for_sta_sta) + return false; + break; + case QDF_P2P_DEVICE_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + if (!dbs_for_sta_p2p) + return false; + break; + default: + break; + } + break; + case PM_P2P_CLIENT_MODE: + case PM_P2P_GO_MODE: + switch (new_conn_mode) { + case QDF_STA_MODE: + if (!dbs_for_sta_p2p) + return false; + break; + default: + break; + } + break; + case PM_NAN_DISC_MODE: + switch (new_conn_mode) { + case QDF_STA_MODE: + case QDF_SAP_MODE: + case QDF_NDI_MODE: + return true; + default: + return false; + } + break; + default: + break; + } + + return ret; +} + +bool policy_mgr_is_chnl_in_diff_band(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq) +{ + uint8_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + /* + * check given channel freq against already existing connections' + * channel freqs. if they differ then channels are in different bands + */ + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if (pm_conc_connection_list[i].in_use) + if (!WLAN_REG_IS_SAME_BAND_FREQS( + ch_freq, pm_conc_connection_list[i].freq)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("channel is in diff band"); + return true; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return false; +} + +bool policy_mgr_is_hwmode_set_for_given_chnl(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq) +{ + enum policy_mgr_band band; + bool is_hwmode_dbs, dbs_required_for_2g; + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) + return true; + + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) + band = POLICY_MGR_BAND_24; + else + band = POLICY_MGR_BAND_5; + + is_hwmode_dbs = policy_mgr_is_current_hwmode_dbs(psoc); + dbs_required_for_2g = policy_mgr_is_hw_dbs_required_for_band( + psoc, HW_MODE_MAC_BAND_2G); + /* + * If HW supports 2x2 chains in DBS HW mode and if DBS HW mode is not + * yet set then this is the right time to block the connection. + */ + if (band == POLICY_MGR_BAND_24 && dbs_required_for_2g && + !is_hwmode_dbs) { + policy_mgr_err("HW mode is not yet in DBS!!!!!"); + return false; + } + + return true; +} + +/** + * policy_mgr_pri_id_to_con_mode() - convert policy_mgr_pri_id to + * policy_mgr_con_mode + * @pri_id: policy_mgr_pri_id + * + * The help function converts policy_mgr_pri_id type to policy_mgr_con_mode + * type. + * + * Return: policy_mgr_con_mode type. + */ +static +enum policy_mgr_con_mode policy_mgr_pri_id_to_con_mode( + enum policy_mgr_pri_id pri_id) +{ + switch (pri_id) { + case PM_STA_PRI_ID: + return PM_STA_MODE; + case PM_SAP_PRI_ID: + return PM_SAP_MODE; + case PM_P2P_GO_PRI_ID: + return PM_P2P_GO_MODE; + case PM_P2P_CLI_PRI_ID: + return PM_P2P_CLIENT_MODE; + default: + return PM_MAX_NUM_OF_MODE; + } +} + +enum policy_mgr_conc_next_action +policy_mgr_get_preferred_dbs_action_table( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint32_t ch_freq, + enum policy_mgr_conn_update_reason reason) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum policy_mgr_con_mode pri_conn_mode = PM_MAX_NUM_OF_MODE; + enum policy_mgr_con_mode new_conn_mode = PM_MAX_NUM_OF_MODE; + enum QDF_OPMODE new_conn_op_mode = QDF_MAX_NO_OF_MODE; + bool band_pref_5g = true; + bool vdev_priority_enabled = false; + bool dbs_2x2_5g_1x1_2g_supported; + bool dbs_2x2_2g_1x1_5g_supported; + uint32_t vdev_pri_list, vdev_pri_id; + uint32_t ch_freq_list[MAX_NUMBER_OF_CONC_CONNECTIONS + 1]; + uint8_t vdev_list[MAX_NUMBER_OF_CONC_CONNECTIONS + 1]; + uint32_t vdev_count = 0; + uint32_t i; + bool found; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return PM_NOP; + } + dbs_2x2_5g_1x1_2g_supported = + policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(psoc); + dbs_2x2_2g_1x1_5g_supported = + policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(psoc); + policy_mgr_debug("target support DBS1 %d DBS2 %d", + dbs_2x2_5g_1x1_2g_supported, + dbs_2x2_2g_1x1_5g_supported); + /* + * If both DBS1 and DBS2 not supported, this should be Legacy Single + * DBS mode HW. The policy_mgr_psoc_enable has setup the correct + * action tables. + */ + if (!dbs_2x2_5g_1x1_2g_supported && !dbs_2x2_2g_1x1_5g_supported) + return PM_NOP; + if (!dbs_2x2_5g_1x1_2g_supported) { + band_pref_5g = false; + policy_mgr_debug("target only supports DBS2!"); + goto DONE; + } + if (!dbs_2x2_2g_1x1_5g_supported) { + policy_mgr_debug("target only supports DBS1!"); + goto DONE; + } + if (PM_GET_BAND_PREFERRED(pm_ctx->cfg.dbs_selection_plcy) == 1) + band_pref_5g = false; + + if (PM_GET_VDEV_PRIORITY_ENABLED( + pm_ctx->cfg.dbs_selection_plcy) == 1 && + pm_ctx->cfg.vdev_priority_list) + vdev_priority_enabled = true; + + if (!vdev_priority_enabled) + goto DONE; + + if (vdev_id != INVALID_VDEV_ID && ch_freq) { + if (pm_ctx->hdd_cbacks.hdd_get_device_mode) + new_conn_op_mode = pm_ctx->hdd_cbacks. + hdd_get_device_mode(vdev_id); + + new_conn_mode = policy_mgr_convert_device_mode_to_qdf_type( + new_conn_op_mode); + if (new_conn_mode == PM_MAX_NUM_OF_MODE) + policy_mgr_debug("new vdev %d op_mode %d freq %d reason %d: not prioritized", + vdev_id, new_conn_op_mode, + ch_freq, reason); + else + policy_mgr_debug("new vdev %d op_mode %d freq %d : reason %d", + vdev_id, new_conn_op_mode, ch_freq, + reason); + } + vdev_pri_list = pm_ctx->cfg.vdev_priority_list; + while (vdev_pri_list) { + vdev_pri_id = vdev_pri_list & 0xF; + pri_conn_mode = policy_mgr_pri_id_to_con_mode(vdev_pri_id); + if (pri_conn_mode == PM_MAX_NUM_OF_MODE) { + policy_mgr_debug("vdev_pri_id %d prioritization not supported", + vdev_pri_id); + goto NEXT; + } + vdev_count = policy_mgr_get_mode_specific_conn_info( + psoc, ch_freq_list, vdev_list, pri_conn_mode); + /** + * Take care of duplication case, the vdev id may + * exist in the conn list already with old chan. + * Replace with new chan before make decision. + */ + found = false; + for (i = 0; i < vdev_count; i++) { + policy_mgr_debug("[%d] vdev %d chan %d conn_mode %d", + i, vdev_list[i], ch_freq_list[i], + pri_conn_mode); + + if (new_conn_mode == pri_conn_mode && + vdev_list[i] == vdev_id) { + ch_freq_list[i] = ch_freq; + found = true; + } + } + /** + * The new coming vdev should be added to the list to + * make decision if it is prioritized. + */ + if (!found && new_conn_mode == pri_conn_mode) { + ch_freq_list[vdev_count] = ch_freq; + vdev_list[vdev_count++] = vdev_id; + } + /** + * if more than one vdev has same priority, keep "band_pref_5g" + * value as default band preference setting. + */ + if (vdev_count > 1) + break; + /** + * select the only active vdev (or new coming vdev) chan as + * preferred band. + */ + if (vdev_count > 0) { + band_pref_5g = + WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq_list[0]); + break; + } +NEXT: + vdev_pri_list >>= 4; + } +DONE: + policy_mgr_debug("band_pref_5g %d", band_pref_5g); + if (band_pref_5g) + return PM_DBS1; + else + return PM_DBS2; +} + +/** + * policy_mgr_get_second_conn_action_table() - get second conn action table + * @psoc: Pointer to psoc + * @vdev_id: vdev Id + * @ch_freq: channel frequency of vdev. + * @reason: reason of request + * + * Get the action table based on current HW Caps and INI user preference. + * This function will be called by policy_mgr_current_connections_update during + * DBS action decision. + * + * return : action table address + */ +static policy_mgr_next_action_two_connection_table_type * +policy_mgr_get_second_conn_action_table( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint32_t ch_freq, + enum policy_mgr_conn_update_reason reason) +{ + enum policy_mgr_conc_next_action preferred_action; + + if (!policy_mgr_is_2x2_1x1_dbs_capable(psoc)) + return next_action_two_connection_table; + + preferred_action = policy_mgr_get_preferred_dbs_action_table( + psoc, vdev_id, ch_freq, reason); + switch (preferred_action) { + case PM_DBS2: + return next_action_two_connection_2x2_2g_1x1_5g_table; + default: + return next_action_two_connection_table; + } +} + +/** + * policy_mgr_get_third_conn_action_table() - get third connection action table + * @psoc: Pointer to psoc + * @vdev_id: vdev Id + * @ch_freq: channel frequency of vdev. + * @reason: reason of request + * + * Get the action table based on current HW Caps and INI user preference. + * This function will be called by policy_mgr_current_connections_update during + * DBS action decision. + * + * return : action table address + */ +static policy_mgr_next_action_three_connection_table_type * +policy_mgr_get_third_conn_action_table( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint32_t ch_freq, + enum policy_mgr_conn_update_reason reason) +{ + enum policy_mgr_conc_next_action preferred_action; + + if (!policy_mgr_is_2x2_1x1_dbs_capable(psoc)) + return next_action_three_connection_table; + + preferred_action = policy_mgr_get_preferred_dbs_action_table( + psoc, vdev_id, ch_freq, reason); + switch (preferred_action) { + case PM_DBS2: + return next_action_three_connection_2x2_2g_1x1_5g_table; + default: + return next_action_three_connection_table; + } +} + +static QDF_STATUS +policy_mgr_get_next_action(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + uint32_t ch_freq, + enum policy_mgr_conn_update_reason reason, + enum policy_mgr_conc_next_action *next_action) +{ + uint32_t num_connections = 0; + enum policy_mgr_one_connection_mode second_index = 0; + enum policy_mgr_two_connection_mode third_index = 0; + policy_mgr_next_action_two_connection_table_type *second_conn_table; + policy_mgr_next_action_three_connection_table_type *third_conn_table; + enum policy_mgr_band band; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum QDF_OPMODE new_conn_mode = QDF_MAX_NO_OF_MODE; + + if (!next_action) { + policy_mgr_err("next_action is NULL"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) + band = POLICY_MGR_BAND_24; + else + band = POLICY_MGR_BAND_5; + + num_connections = policy_mgr_get_connection_count(psoc); + + policy_mgr_debug("num_connections=%d freq=%d", + num_connections, ch_freq); + + switch (num_connections) { + case 0: + if (band == POLICY_MGR_BAND_24) + if (policy_mgr_is_hw_dbs_required_for_band( + psoc, HW_MODE_MAC_BAND_2G)) + *next_action = PM_DBS; + else + *next_action = PM_NOP; + else + *next_action = PM_NOP; + break; + case 1: + second_index = + policy_mgr_get_second_connection_pcl_table_index(psoc); + if (PM_MAX_ONE_CONNECTION_MODE == second_index) { + policy_mgr_err( + "couldn't find index for 2nd connection next action table"); + return QDF_STATUS_E_FAILURE; + } + second_conn_table = policy_mgr_get_second_conn_action_table( + psoc, session_id, ch_freq, reason); + *next_action = (*second_conn_table)[second_index][band]; + break; + case 2: + third_index = + policy_mgr_get_third_connection_pcl_table_index(psoc); + if (PM_MAX_TWO_CONNECTION_MODE == third_index) { + policy_mgr_err( + "couldn't find index for 3rd connection next action table"); + return QDF_STATUS_E_FAILURE; + } + third_conn_table = policy_mgr_get_third_conn_action_table( + psoc, session_id, ch_freq, reason); + *next_action = (*third_conn_table)[third_index][band]; + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + break; + } + + /* + * There is no adapter associated with NAN Discovery, hence skip the + * HDD callback and fill separately. + */ + if (reason == POLICY_MGR_UPDATE_REASON_NAN_DISCOVERY) + new_conn_mode = QDF_NAN_DISC_MODE; + else if (pm_ctx->hdd_cbacks.hdd_get_device_mode) + new_conn_mode = pm_ctx->hdd_cbacks. + hdd_get_device_mode(session_id); + + /* + * Based on channel_select_logic_conc ini, hw mode is set + * when second connection is about to come up that results + * in STA+STA and STA+P2P concurrency. + * 1) If MCC is set and if current hw mode is dbs, hw mode + * should be set to single mac for above concurrency. + * 2) If MCC is set and if current hw mode is not dbs, hw + * mode change is not required. + */ + if (policy_mgr_is_current_hwmode_dbs(psoc) && + !policy_mgr_is_dbs_allowed_for_concurrency(psoc, new_conn_mode)) + *next_action = PM_SINGLE_MAC; + else if (!policy_mgr_is_current_hwmode_dbs(psoc) && + !policy_mgr_is_dbs_allowed_for_concurrency(psoc, new_conn_mode)) + *next_action = PM_NOP; + + policy_mgr_debug("idx2=%d idx3=%d next_action=%d, band=%d reason=%d session_id=%d", + second_index, third_index, *next_action, band, + reason, session_id); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +policy_mgr_current_connections_update(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, uint32_t ch_freq, + enum policy_mgr_conn_update_reason + reason) +{ + enum policy_mgr_conc_next_action next_action = PM_NOP; + QDF_STATUS status; + + if (!policy_mgr_is_hw_dbs_capable(psoc)) { + policy_mgr_rl_debug("driver isn't dbs capable, no further action needed"); + return QDF_STATUS_E_NOSUPPORT; + } + + status = policy_mgr_get_next_action(psoc, session_id, ch_freq, reason, + &next_action); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + if (PM_NOP != next_action) + status = policy_mgr_next_actions(psoc, session_id, + next_action, reason); + else + status = QDF_STATUS_E_NOSUPPORT; + + policy_mgr_debug("next_action %d reason=%d session_id=%d", + next_action, reason, session_id); + + return status; +} + +QDF_STATUS +policy_mgr_validate_dbs_switch(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conc_next_action action) +{ + QDF_STATUS status; + struct policy_mgr_hw_mode_params hw_mode; + + /* check for the current HW index to see if really need any action */ + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + return status; + } + + if ((action == PM_SBS) || (action == PM_SBS_DOWNGRADE)) { + if (!policy_mgr_is_hw_sbs_capable(psoc)) { + /* No action */ + policy_mgr_notice("firmware is not sbs capable"); + return QDF_STATUS_E_NOSUPPORT; + } + /* current mode is already SBS nothing to be + * done + */ + if (hw_mode.sbs_cap) { + policy_mgr_notice("current mode is already SBS"); + return QDF_STATUS_E_ALREADY; + } + return QDF_STATUS_SUCCESS; + } + + if (!hw_mode.dbs_cap) { + if (action == PM_SINGLE_MAC || + action == PM_SINGLE_MAC_UPGRADE) { + policy_mgr_notice("current mode is already single MAC"); + return QDF_STATUS_E_ALREADY; + } else { + return QDF_STATUS_SUCCESS; + } + } + /** + * If already in DBS, no need to request DBS again (HL, Napier). + * For dual DBS HW, in case DBS1 -> DBS2 or DBS2 -> DBS1 + * switching, we need to check the current DBS mode is same as + * requested or not. + */ + if (policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(psoc) || + policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(psoc)) { + policy_mgr_debug("curr dbs action %d new action %d", + hw_mode.action_type, action); + if (hw_mode.action_type == PM_DBS1 && + ((action == PM_DBS1 || + action == PM_DBS1_DOWNGRADE))) { + policy_mgr_debug("driver is already in DBS_5G_2x2_24G_1x1 (%d), no further action %d needed", + hw_mode.action_type, action); + return QDF_STATUS_E_ALREADY; + } else if (hw_mode.action_type == PM_DBS2 && + ((action == PM_DBS2 || + action == PM_DBS2_DOWNGRADE))) { + policy_mgr_debug("driver is already in DBS_24G_2x2_5G_1x1 (%d), no further action %d needed", + hw_mode.action_type, action); + return QDF_STATUS_E_ALREADY; + } + } else if ((action == PM_DBS_DOWNGRADE) || (action == PM_DBS) || + (action == PM_DBS_UPGRADE)) { + policy_mgr_debug("driver is already in %s mode, no further action needed", + (hw_mode.dbs_cap) ? "dbs" : "non dbs"); + return QDF_STATUS_E_ALREADY; + } + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_validate_unsupported_action() - unsupported action validation + * @psoc: psoc object + * @action: action type + * + * The help function checks the Action supported by HW or not. + * + * Return: QDF_STATUS_SUCCESS if supported by HW, otherwise + * return QDF_STATUS_E_NOSUPPORT + */ +static QDF_STATUS policy_mgr_validate_unsupported_action + (struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conc_next_action action) +{ + if (action == PM_SBS || action == PM_SBS_DOWNGRADE) { + if (!policy_mgr_is_hw_sbs_capable(psoc)) { + /* No action */ + policy_mgr_notice("firmware is not sbs capable"); + return QDF_STATUS_E_NOSUPPORT; + } + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_next_actions( + struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + enum policy_mgr_conc_next_action action, + enum policy_mgr_conn_update_reason reason) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct dbs_nss nss_dbs = {0}; + struct policy_mgr_hw_mode_params hw_mode; + enum policy_mgr_conc_next_action next_action; + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + policy_mgr_rl_debug("driver isn't dbs capable, no further action needed"); + return QDF_STATUS_E_NOSUPPORT; + } + status = policy_mgr_validate_unsupported_action(psoc, action); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + /* check for the current HW index to see if really need any action */ + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + return status; + } + + switch (action) { + case PM_DBS_DOWNGRADE: + /* + * check if we have a beaconing entity that is using 2x2. If yes, + * update the beacon template & notify FW. Once FW confirms + * beacon updated, send down the HW mode change req + */ + status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, + PM_DBS, reason, session_id); + break; + case PM_DBS: + (void)policy_mgr_get_hw_dbs_nss(psoc, &nss_dbs); + + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + nss_dbs.mac0_ss, + HW_MODE_80_MHZ, + nss_dbs.mac1_ss, + HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, PM_NOP, PM_DBS); + break; + case PM_SINGLE_MAC_UPGRADE: + /* + * change the HW mode first before the NSS upgrade + */ + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_0x0, HW_MODE_BW_NONE, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS_NONE, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, PM_UPGRADE, + PM_SINGLE_MAC_UPGRADE); + break; + case PM_SINGLE_MAC: + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_0x0, HW_MODE_BW_NONE, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS_NONE, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, PM_NOP, PM_SINGLE_MAC); + break; + case PM_DBS_UPGRADE: + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_2x2, HW_MODE_80_MHZ, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, PM_UPGRADE, + PM_DBS_UPGRADE); + break; + case PM_SBS_DOWNGRADE: + status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, + PM_SBS, reason, session_id); + break; + case PM_SBS: + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + HW_MODE_SS_1x1, + HW_MODE_80_MHZ, + HW_MODE_SS_1x1, HW_MODE_80_MHZ, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS, + reason, PM_NOP, PM_SBS); + break; + case PM_DOWNGRADE: + /* + * check if we have a beaconing entity that advertised 2x2 + * intially. If yes, update the beacon template & notify FW. + */ + status = policy_mgr_nss_update(psoc, POLICY_MGR_RX_NSS_1, + PM_NOP, POLICY_MGR_ANY, reason, + session_id); + break; + case PM_UPGRADE: + /* + * check if we have a beaconing entity that advertised 2x2 + * intially. If yes, update the beacon template & notify FW. + */ + status = policy_mgr_nss_update(psoc, POLICY_MGR_RX_NSS_2, + PM_NOP, POLICY_MGR_ANY, reason, + session_id); + break; + case PM_DBS1_DOWNGRADE: + status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, + PM_DBS1, reason, + session_id); + break; + case PM_DBS2_DOWNGRADE: + status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, + PM_DBS2, reason, + session_id); + break; + case PM_DBS1: + /* + * PM_DBS1 (2x2 5G + 1x1 2G) will support 5G 2x2. If previous + * mode is DBS, that should be 2x2 2G + 1x1 5G mode and + * the 5G band was downgraded to 1x1. So, we need to + * upgrade 5G vdevs after hw mode change. + */ + if (hw_mode.dbs_cap) + next_action = PM_UPGRADE_5G; + else + next_action = PM_NOP; + status = policy_mgr_pdev_set_hw_mode( + psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_1x1, HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_5G, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, next_action, PM_DBS1); + break; + case PM_DBS2: + /* + * PM_DBS2 (2x2 2G + 1x1 5G) will support 2G 2x2. If previous + * mode is DBS, that should be 2x2 5G + 1x1 2G mode and + * the 2G band was downgraded to 1x1. So, we need to + * upgrade 5G vdevs after hw mode change. + */ + if (hw_mode.dbs_cap) + next_action = PM_UPGRADE_2G; + else + next_action = PM_NOP; + status = policy_mgr_pdev_set_hw_mode( + psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_40_MHZ, + HW_MODE_SS_1x1, HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_2G, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, next_action, PM_DBS2); + break; + case PM_UPGRADE_5G: + status = policy_mgr_nss_update( + psoc, POLICY_MGR_RX_NSS_2, + PM_NOP, POLICY_MGR_BAND_5, reason, + session_id); + break; + case PM_UPGRADE_2G: + status = policy_mgr_nss_update( + psoc, POLICY_MGR_RX_NSS_2, + PM_NOP, POLICY_MGR_BAND_24, reason, + session_id); + break; + default: + policy_mgr_err("unexpected action value %d", action); + status = QDF_STATUS_E_FAILURE; + break; + } + + return status; +} + +QDF_STATUS +policy_mgr_handle_conc_multiport(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint32_t ch_freq, + enum policy_mgr_conn_update_reason reason) +{ + QDF_STATUS status; + + if (!policy_mgr_check_for_session_conc(psoc, session_id, ch_freq)) { + policy_mgr_err("Conc not allowed for the session %d", + session_id); + return QDF_STATUS_E_FAILURE; + } + + status = policy_mgr_reset_connection_update(psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) + policy_mgr_err("clearing event failed"); + + status = policy_mgr_current_connections_update(psoc, session_id, + ch_freq, reason); + if (QDF_STATUS_E_FAILURE == status) { + policy_mgr_err("connections update failed"); + return status; + } + + return status; +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +void policy_mgr_update_user_config_sap_chan( + struct wlan_objmgr_psoc *psoc, uint32_t ch_freq) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid pm context and failed to update the user config sap channel"); + return; + } + pm_ctx->user_config_sap_ch_freq = ch_freq; +} + +/** + * policy_mgr_is_sap_go_existed() - Check if restart SAP/Go exist + * @psoc: PSOC object data + * + * To simplify, if SAP/P2P Go exist, they may need switch channel for + * forcing scc with sta or band capability change. + * Restart: true or false + */ +static bool policy_mgr_is_sap_go_existed(struct wlan_objmgr_psoc *psoc) +{ + uint32_t ap_present, go_present; + + ap_present = policy_mgr_mode_specific_connection_count( + psoc, PM_SAP_MODE, NULL); + if (ap_present) + return true; + + go_present = policy_mgr_mode_specific_connection_count( + psoc, PM_P2P_GO_MODE, NULL); + if (go_present) + return true; + + return false; +} + +bool policy_mgr_is_safe_channel(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool is_safe = true; + uint8_t j; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return is_safe; + } + + + if (pm_ctx->unsafe_channel_count == 0) + return is_safe; + + for (j = 0; j < pm_ctx->unsafe_channel_count; j++) { + if (ch_freq == pm_ctx->unsafe_channel_list[j]) { + is_safe = false; + policy_mgr_warn("Freq %d is not safe", ch_freq); + break; + } + } + + return is_safe; +} + +bool policy_mgr_is_sap_restart_required_after_sta_disconnect( + struct wlan_objmgr_psoc *psoc, + uint32_t sap_vdev_id, uint32_t *intf_ch_freq) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t curr_sap_freq = 0, new_sap_freq = 0; + bool sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + bool sta_sap_scc_on_lte_coex_chan = + policy_mgr_sta_sap_scc_on_lte_coex_chan(psoc); + uint8_t sta_sap_scc_on_dfs_chnl_config_value = 0; + uint32_t cc_count, i, go_index_start, pcl_len = 0; + uint32_t op_ch_freq_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS]; + enum policy_mgr_con_mode mode; + uint32_t pcl_channels[NUM_CHANNELS + 1]; + uint8_t pcl_weight[NUM_CHANNELS + 1]; + struct policy_mgr_conc_connection_info info = {0}; + uint8_t num_cxn_del = 0; + QDF_STATUS status; + + if (intf_ch_freq) + *intf_ch_freq = 0; + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid pm context"); + return false; + } + if (!pm_ctx->do_sap_unsafe_ch_check) + return false; + if (!sta_sap_scc_on_dfs_chan && !sta_sap_scc_on_lte_coex_chan) + return false; + + policy_mgr_get_sta_sap_scc_on_dfs_chnl(psoc, &sta_sap_scc_on_dfs_chnl_config_value); + + if (!policy_mgr_is_hw_dbs_capable(psoc)) + if (policy_mgr_get_connection_count(psoc) > 1) + return false; + + cc_count = policy_mgr_get_mode_specific_conn_info(psoc, + &op_ch_freq_list[0], + &vdev_id[0], + PM_SAP_MODE); + go_index_start = cc_count; + if (cc_count < MAX_NUMBER_OF_CONC_CONNECTIONS) + cc_count += policy_mgr_get_mode_specific_conn_info( + psoc, &op_ch_freq_list[cc_count], + &vdev_id[cc_count], PM_P2P_GO_MODE); + + for (i = 0 ; i < cc_count; i++) { + if (sap_vdev_id != INVALID_VDEV_ID && + sap_vdev_id != vdev_id[i]) + continue; + if (policy_mgr_is_any_mode_active_on_band_along_with_session( + psoc, vdev_id[i], + WLAN_REG_IS_24GHZ_CH_FREQ(op_ch_freq_list[i]) ? + POLICY_MGR_BAND_24 : POLICY_MGR_BAND_5)) + continue; + if (sta_sap_scc_on_dfs_chan && + (sta_sap_scc_on_dfs_chnl_config_value != 2) && + wlan_reg_is_dfs_for_freq(pm_ctx->pdev, + op_ch_freq_list[i])) { + sap_vdev_id = vdev_id[i]; + curr_sap_freq = op_ch_freq_list[i]; + policy_mgr_debug("sta_sap_scc_on_dfs_chan %u, sta_sap_scc_on_dfs_chnl_config_value %u, dfs sap_ch_freq %u", + sta_sap_scc_on_dfs_chan, + sta_sap_scc_on_dfs_chnl_config_value, + curr_sap_freq); + break; + } + if (sta_sap_scc_on_lte_coex_chan && + !policy_mgr_is_safe_channel(psoc, op_ch_freq_list[i])) { + sap_vdev_id = vdev_id[i]; + curr_sap_freq = op_ch_freq_list[i]; + policy_mgr_debug("sta_sap_scc_on_lte_coex_chan %u unsafe sap_ch_freq %u", + sta_sap_scc_on_lte_coex_chan, + curr_sap_freq); + break; + } + } + + if (!curr_sap_freq) + return false; + mode = i >= go_index_start ? PM_P2P_GO_MODE : PM_SAP_MODE; + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + policy_mgr_store_and_del_conn_info_by_vdev_id(psoc, sap_vdev_id, + &info, &num_cxn_del); + /* Add the user config ch as first condidate */ + pcl_channels[0] = pm_ctx->user_config_sap_ch_freq; + pcl_weight[0] = 0; + status = policy_mgr_get_pcl(psoc, mode, &pcl_channels[1], &pcl_len, + &pcl_weight[1], + QDF_ARRAY_SIZE(pcl_weight) - 1); + if (status == QDF_STATUS_SUCCESS) + pcl_len++; + else + pcl_len = 1; + for (i = 0; i < pcl_len; i++) { + if (pcl_channels[i] == curr_sap_freq) + continue; + if (!wlan_reg_is_same_band_freqs( + curr_sap_freq, pcl_channels[i])) + continue; + if (!policy_mgr_is_safe_channel(psoc, pcl_channels[i]) || + wlan_reg_is_dfs_for_freq(pm_ctx->pdev, pcl_channels[i])) + continue; + new_sap_freq = pcl_channels[i]; + break; + } + /* Restore the connection entry */ + if (num_cxn_del > 0) + policy_mgr_restore_deleted_conn_info(psoc, &info, num_cxn_del); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (new_sap_freq == 0 || curr_sap_freq == new_sap_freq) + return false; + if (!intf_ch_freq) + return true; + + *intf_ch_freq = new_sap_freq; + policy_mgr_debug("Standalone SAP(vdev_id %d) is not allowed on DFS/Unsafe channel, Move it to channel %u", + sap_vdev_id, *intf_ch_freq); + + return true; +} + +/** + * policy_mgr_is_nan_sap_unsafe_ch_scc_allowed() - Check if NAN+SAP SCC is + * allowed in LTE COEX unsafe ch + * @pm_ctx: policy_mgr_psoc_priv_obj policy mgr context + * @ch_freq: Channel frequency to check + * + * Return: True if allowed else false + */ +static bool +policy_mgr_is_nan_sap_unsafe_ch_scc_allowed(struct policy_mgr_psoc_priv_obj + *pm_ctx, uint32_t ch_freq) +{ + if (policy_mgr_is_safe_channel(pm_ctx->psoc, ch_freq) || + pm_ctx->cfg.nan_sap_scc_on_lte_coex_chnl) + return true; + + return false; +} + +/** + * policy_mgr_nan_disable_work() - qdf defer function wrapper for NAN disable + * @data: qdf_work data + * + * Return: None + */ +static void policy_mgr_nan_disable_work(void *data) +{ + struct wlan_objmgr_psoc *psoc = data; + + ucfg_nan_disable_concurrency(psoc); +} + +bool policy_mgr_nan_sap_scc_on_unsafe_ch_chk(struct wlan_objmgr_psoc *psoc, + uint32_t sap_freq) +{ + uint32_t nan_2g_freq, nan_5g_freq; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + nan_2g_freq = policy_mgr_mode_specific_get_channel(psoc, + PM_NAN_DISC_MODE); + if (nan_2g_freq == 0) { + policy_mgr_debug("No NAN+SAP SCC"); + return false; + } + nan_5g_freq = wlan_nan_get_disc_5g_ch_freq(psoc); + + policy_mgr_debug("Freq SAP: %d NAN: %d %d", sap_freq, + nan_2g_freq, nan_5g_freq); + if (WLAN_REG_IS_SAME_BAND_FREQS(nan_2g_freq, sap_freq)) { + if (policy_mgr_is_force_scc(pm_ctx->psoc) && + policy_mgr_is_nan_sap_unsafe_ch_scc_allowed(pm_ctx, + nan_2g_freq)) + return true; + } else if (WLAN_REG_IS_SAME_BAND_FREQS(nan_5g_freq, sap_freq)) { + if (policy_mgr_is_force_scc(pm_ctx->psoc) && + policy_mgr_is_nan_sap_unsafe_ch_scc_allowed(pm_ctx, + nan_5g_freq)) + return true; + } else { + /* + * NAN + SAP in different bands. Continue to check for + * SAP in unsafe channel + */ + return false; + } + policy_mgr_info("NAN+SAP unsafe ch SCC not allowed. Disabling NAN"); + /* change context to worker since this is executed in sched thread ctx*/ + qdf_create_work(0, &pm_ctx->nan_sap_conc_work, + policy_mgr_nan_disable_work, psoc); + qdf_sched_work(0, &pm_ctx->nan_sap_conc_work); + + return false; +} + +bool +policy_mgr_nan_sap_pre_enable_conc_check(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t sap_freq, nan_2g_freq, nan_5g_freq; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + if (!(mode == PM_SAP_MODE || mode == PM_NAN_DISC_MODE)) { + policy_mgr_debug("Not NAN or SAP mode"); + return true; + } + + if (!ch_freq) { + policy_mgr_err("Invalid channel"); + return false; + } + + if (!wlan_nan_get_sap_conc_support(pm_ctx->psoc)) { + policy_mgr_debug("NAN+SAP not supported in fw"); + if (mode == PM_NAN_DISC_MODE) + return false; + /* Before SAP start disable NAN */ + ucfg_nan_disable_concurrency(pm_ctx->psoc); + } + if (mode == PM_NAN_DISC_MODE) { + sap_freq = policy_mgr_mode_specific_get_channel(pm_ctx->psoc, + PM_SAP_MODE); + policy_mgr_debug("FREQ SAP: %d NAN: %d", sap_freq, ch_freq); + if (WLAN_REG_IS_SAME_BAND_FREQS(sap_freq, ch_freq)) { + if (sap_freq == ch_freq) { + policy_mgr_debug("NAN+SAP SCC"); + return true; + } + + if (!policy_mgr_is_force_scc(pm_ctx->psoc)) { + policy_mgr_debug("SAP force SCC disabled"); + return false; + } + if (!policy_mgr_is_nan_sap_unsafe_ch_scc_allowed( + pm_ctx, ch_freq)) { + policy_mgr_debug("NAN+SAP unsafe ch SCC disabled"); + return false; + } + } + } else if (mode == PM_SAP_MODE) { + nan_2g_freq = + policy_mgr_mode_specific_get_channel(pm_ctx->psoc, + PM_NAN_DISC_MODE); + nan_5g_freq = wlan_nan_get_disc_5g_ch_freq(pm_ctx->psoc); + policy_mgr_debug("SAP CH: %d NAN Ch: %d %d", ch_freq, + nan_2g_freq, nan_5g_freq); + if (WLAN_REG_IS_SAME_BAND_FREQS(nan_2g_freq, ch_freq) || + WLAN_REG_IS_SAME_BAND_FREQS(nan_5g_freq, ch_freq)) { + if (ch_freq == nan_2g_freq || ch_freq == nan_5g_freq) { + policy_mgr_debug("NAN+SAP SCC"); + return true; + } + if (!policy_mgr_is_force_scc(pm_ctx->psoc)) { + policy_mgr_debug("SAP force SCC disabled"); + ucfg_nan_disable_concurrency(pm_ctx->psoc); + return false; + } + if ((WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq) && + !policy_mgr_is_nan_sap_unsafe_ch_scc_allowed( + pm_ctx, nan_5g_freq)) || + (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq) && + !policy_mgr_is_nan_sap_unsafe_ch_scc_allowed( + pm_ctx, nan_2g_freq))) { + policy_mgr_debug("NAN+SAP unsafe ch SCC disabled"); + ucfg_nan_disable_concurrency(pm_ctx->psoc); + return false; + } + } + } + return true; +} + +void policy_mgr_nan_sap_post_enable_conc_check(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct policy_mgr_conc_connection_info *sap_info = NULL; + uint8_t nan_freq_2g, nan_freq_5g, i; + QDF_STATUS status; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid pm context"); + return; + } + + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if (pm_conc_connection_list[i].mode == PM_SAP_MODE && + pm_conc_connection_list[i].in_use) { + sap_info = &pm_conc_connection_list[i]; + break; + } + } + if (!sap_info) + return; + if (sap_info->freq == 0) + return; + nan_freq_2g = policy_mgr_mode_specific_get_channel(psoc, + PM_NAN_DISC_MODE); + nan_freq_5g = wlan_nan_get_disc_5g_ch_freq(psoc); + if (sap_info->freq == nan_freq_2g || sap_info->freq == nan_freq_5g) { + policy_mgr_debug("NAN and SAP already in SCC"); + return; + } + if (nan_freq_2g == 0) + return; + + if (pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress && + pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress()) { + policy_mgr_debug("wait as channel switch is already in progress"); + status = qdf_wait_single_event( + &pm_ctx->channel_switch_complete_evt, + CHANNEL_SWITCH_COMPLETE_TIMEOUT); + if (QDF_IS_STATUS_ERROR(status)) + policy_mgr_err("wait for event failed, still continue with channel switch"); + } + + policy_mgr_debug("Force SCC for NAN+SAP Ch freq: %d", + WLAN_REG_IS_5GHZ_CH_FREQ(sap_info->freq) ? + nan_freq_5g : nan_freq_2g); + if (pm_ctx->hdd_cbacks.wlan_hdd_set_sap_csa_reason) + pm_ctx->hdd_cbacks.wlan_hdd_set_sap_csa_reason(psoc, + sap_info->vdev_id, + CSA_REASON_CONCURRENT_NAN_EVENT); + + if (WLAN_REG_IS_5GHZ_CH_FREQ(sap_info->freq)) { + policy_mgr_change_sap_channel_with_csa(psoc, sap_info->vdev_id, + nan_freq_5g, + policy_mgr_get_ch_width( + sap_info->bw), + true); + } else { + policy_mgr_change_sap_channel_with_csa(psoc, sap_info->vdev_id, + nan_freq_2g, + policy_mgr_get_ch_width( + sap_info->bw), + true); + } +} + +void policy_mgr_nan_sap_post_disable_conc_check(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct policy_mgr_conc_connection_info *sap_info = NULL; + uint32_t sap_freq = 0, i; + QDF_STATUS status; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid pm context"); + return; + } + + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if (pm_conc_connection_list[i].mode == PM_SAP_MODE && + pm_conc_connection_list[i].in_use) { + sap_info = &pm_conc_connection_list[i]; + sap_freq = sap_info->freq; + break; + } + } + if (sap_freq == 0 || policy_mgr_is_safe_channel(psoc, sap_freq)) + return; + + sap_freq = policy_mgr_get_nondfs_preferred_channel(psoc, PM_SAP_MODE, + false); + policy_mgr_debug("User/ACS orig Freq: %d New SAP Freq: %d", + pm_ctx->user_config_sap_ch_freq, sap_freq); + if (pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress && + pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress()) { + policy_mgr_debug("wait as channel switch is already in progress"); + status = qdf_wait_single_event( + &pm_ctx->channel_switch_complete_evt, + CHANNEL_SWITCH_COMPLETE_TIMEOUT); + if (QDF_IS_STATUS_ERROR(status)) + policy_mgr_err("wait for event failed, still continue with channel switch"); + } + + if (pm_ctx->hdd_cbacks.wlan_hdd_set_sap_csa_reason) + pm_ctx->hdd_cbacks.wlan_hdd_set_sap_csa_reason( + psoc, sap_info->vdev_id, + CSA_REASON_CONCURRENT_NAN_EVENT); + + policy_mgr_change_sap_channel_with_csa(psoc, sap_info->vdev_id, + sap_freq, + policy_mgr_get_ch_width( + sap_info->bw), true); +} + +static void __policy_mgr_check_sta_ap_concurrent_ch_intf(void *data) +{ + struct wlan_objmgr_psoc *psoc; + struct policy_mgr_psoc_priv_obj *pm_ctx = NULL; + struct sta_ap_intf_check_work_ctx *work_info = NULL; + uint32_t mcc_to_scc_switch, cc_count = 0, i; + QDF_STATUS status; + uint32_t ch_freq; + uint32_t op_ch_freq_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS]; + + work_info = data; + if (!work_info) { + policy_mgr_err("Invalid work_info"); + return; + } + + psoc = work_info->psoc; + if (!psoc) { + policy_mgr_err("Invalid psoc"); + return; + } + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + + mcc_to_scc_switch = + policy_mgr_get_mcc_to_scc_switch_mode(psoc); + + policy_mgr_debug("Concurrent open sessions running: %d", + policy_mgr_concurrent_open_sessions_running(psoc)); + + if (!policy_mgr_is_sap_go_existed(psoc)) + goto end; + + cc_count = policy_mgr_get_mode_specific_conn_info( + psoc, &op_ch_freq_list[cc_count], + &vdev_id[cc_count], PM_SAP_MODE); + policy_mgr_debug("Number of concurrent SAP: %d", cc_count); + if (cc_count < MAX_NUMBER_OF_CONC_CONNECTIONS) + cc_count = cc_count + + policy_mgr_get_mode_specific_conn_info( + psoc, &op_ch_freq_list[cc_count], + &vdev_id[cc_count], PM_P2P_GO_MODE); + policy_mgr_debug("Number of beaconing entities (SAP + GO):%d", + cc_count); + if (!cc_count) { + policy_mgr_err("Could not retrieve SAP/GO operating channel&vdevid"); + goto end; + } + + if (pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress && + pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress()) { + policy_mgr_debug("wait as channel switch is already in progress"); + status = qdf_wait_single_event( + &pm_ctx->channel_switch_complete_evt, + CHANNEL_SWITCH_COMPLETE_TIMEOUT); + if (QDF_IS_STATUS_ERROR(status)) + policy_mgr_err("wait for event failed, still continue with channel switch"); + } + + if (!pm_ctx->hdd_cbacks.wlan_hdd_get_channel_for_sap_restart) { + policy_mgr_err("SAP restart get channel callback in NULL"); + goto end; + } + if (cc_count <= MAX_NUMBER_OF_CONC_CONNECTIONS) + for (i = 0; i < cc_count; i++) { + status = pm_ctx->hdd_cbacks. + wlan_hdd_get_channel_for_sap_restart + (psoc, vdev_id[i], &ch_freq); + if (status == QDF_STATUS_SUCCESS) { + policy_mgr_debug("SAP restarts due to MCC->SCC switch, old ch freq :%d new ch freq: %d", + op_ch_freq_list[i], ch_freq); + break; + } + } + +end: + pm_ctx->do_sap_unsafe_ch_check = false; +} + +void policy_mgr_check_sta_ap_concurrent_ch_intf(void *data) +{ + struct qdf_op_sync *op_sync; + + if (qdf_op_protect(&op_sync)) + return; + + __policy_mgr_check_sta_ap_concurrent_ch_intf(data); + + qdf_op_unprotect(op_sync); +} + +static bool policy_mgr_valid_sta_channel_check(struct wlan_objmgr_psoc *psoc, + uint32_t sta_ch_freq) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool sta_sap_scc_on_dfs_chan; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return false; + } + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + if (wlan_reg_is_dfs_for_freq(pm_ctx->pdev, sta_ch_freq) && + sta_sap_scc_on_dfs_chan) { + policy_mgr_debug("STA, SAP SCC is allowed on DFS chan %u", + sta_ch_freq); + return true; + } + if ((wlan_reg_is_dfs_for_freq(pm_ctx->pdev, sta_ch_freq) && + !sta_sap_scc_on_dfs_chan) || + wlan_reg_is_passive_or_disable_for_freq( + pm_ctx->pdev, sta_ch_freq) || + !policy_mgr_is_safe_channel(psoc, sta_ch_freq)) { + if (policy_mgr_is_hw_dbs_capable(psoc)) + return true; + else + return false; + } + else + return true; +} + +/** + * policy_mgr_select_2g_chan() - select 2G usable channel for SAP + * @psoc: psoc object + * + * Select active 2G home channel or select safe 2G channel. + * + * return: uint32 chan freq + */ +static uint32_t policy_mgr_select_2g_chan(struct wlan_objmgr_psoc *psoc) +{ + uint8_t count, i; + struct connection_info info[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct regulatory_channel band_2g_list[NUM_24GHZ_CHANNELS]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return 0; + } + + count = policy_mgr_get_connection_info(psoc, info); + for (i = 0; i < count; i++) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(info[i].ch_freq)) + return info[i].ch_freq; + } + + count = wlan_reg_get_band_channel_list(pm_ctx->pdev, BIT(REG_BAND_2G), + band_2g_list); + for (i = 0; i < count; i++) { + if (policy_mgr_is_safe_channel(psoc, + band_2g_list[i].center_freq)) + return band_2g_list[i].center_freq; + } + + return PM_24_GHZ_CH_FREQ_6; +} + +/** + * policy_mgr_check_6ghz_sap_conc() - check sap force scc to 6ghz + * @psoc: psoc object + * @con_ch_freq: concurrency channel + * @sap_ch_freq: SAP starting channel + * @sap_vdev_id: sap vdev id + * + * Validate whether SAP can be forced scc to 6ghz band or not. + * If not, select 2G band channel for DBS hw + * or keep SAP starting channel not changed for non-DBS hw. + * + * return: QDF_STATUS_SUCCESS + */ +static QDF_STATUS policy_mgr_check_6ghz_sap_conc( + struct wlan_objmgr_psoc *psoc, uint32_t *con_ch_freq, + uint32_t sap_ch_freq, uint8_t sap_vdev_id) +{ + uint32_t ch_freq = *con_ch_freq; + + if (ch_freq && WLAN_REG_IS_6GHZ_CHAN_FREQ(ch_freq) && + !WLAN_REG_IS_6GHZ_CHAN_FREQ(sap_ch_freq) && + !policy_mgr_get_ap_6ghz_capable(psoc, sap_vdev_id, NULL)) { + policy_mgr_debug("sap %d not support 6ghz freq %d", + sap_vdev_id, ch_freq); + if (policy_mgr_is_hw_dbs_capable(psoc)) { + ch_freq = policy_mgr_select_2g_chan(psoc); + policy_mgr_debug("select 2G ch %d to achieve DBS", + ch_freq); + } else { + /* Keep MCC 2G(or 5G) + 6G for non-DBS chip*/ + ch_freq = 0; + policy_mgr_debug("do not force SCC for non-dbs hw"); + } + } + if (ch_freq != sap_ch_freq) + *con_ch_freq = ch_freq; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_valid_sap_conc_channel_check( + struct wlan_objmgr_psoc *psoc, uint32_t *con_ch_freq, + uint32_t sap_ch_freq, uint8_t sap_vdev_id) +{ + uint32_t ch_freq = *con_ch_freq; + uint32_t temp_ch_freq = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool sta_sap_scc_on_dfs_chan; + struct wlan_objmgr_vdev *vdev; + enum QDF_OPMODE vdev_opmode; + bool enable_srd_channel; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, sap_vdev_id, + WLAN_POLICY_MGR_ID); + if (!vdev) { + policy_mgr_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + vdev_opmode = wlan_vdev_mlme_get_opmode(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_POLICY_MGR_ID); + + wlan_mlme_get_srd_master_mode_for_vdev(psoc, vdev_opmode, + &enable_srd_channel); + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + /* + * if force SCC is set, Check if conc channel is DFS + * or passive or part of LTE avoided channel list. + * In that case move SAP to other band if DBS is supported, + * return otherwise + */ + if (!policy_mgr_is_force_scc(psoc)) + return QDF_STATUS_SUCCESS; + + /* + * if interference is 0, check if it is DBS case. If DBS case + * return from here. If SCC, check further if SAP can move to + * STA's channel. + */ + if (!ch_freq && + sap_ch_freq != policy_mgr_mode_specific_get_channel(psoc, + PM_STA_MODE)) { + return QDF_STATUS_SUCCESS; + } else if (!ch_freq) { + ch_freq = sap_ch_freq; + } else if (ch_freq && WLAN_REG_IS_6GHZ_CHAN_FREQ(ch_freq)) { + return policy_mgr_check_6ghz_sap_conc( + psoc, con_ch_freq, sap_ch_freq, sap_vdev_id); + } + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + + if (policy_mgr_valid_sta_channel_check(psoc, ch_freq)) { + if (wlan_reg_is_dfs_for_freq(pm_ctx->pdev, ch_freq) || + wlan_reg_is_passive_or_disable_for_freq(pm_ctx->pdev, + ch_freq) || + !(policy_mgr_sta_sap_scc_on_lte_coex_chan(psoc) || + policy_mgr_is_safe_channel(psoc, ch_freq)) || + (!enable_srd_channel && + wlan_reg_is_etsi13_srd_chan_for_freq(pm_ctx->pdev, + ch_freq))) { + if (wlan_reg_is_dfs_for_freq(pm_ctx->pdev, ch_freq) && + sta_sap_scc_on_dfs_chan) { + policy_mgr_debug("STA SAP SCC is allowed on DFS channel"); + goto update_chan; + } + + if (policy_mgr_is_hw_dbs_capable(psoc)) { + temp_ch_freq = + policy_mgr_get_alternate_channel_for_sap( + psoc, sap_vdev_id, sap_ch_freq); + policy_mgr_debug("temp ch freq is %d", + temp_ch_freq); + if (temp_ch_freq) { + ch_freq = temp_ch_freq; + } else { + if (WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq)) + ch_freq = PM_24_GHZ_CH_FREQ_6; + else + ch_freq = PM_5_GHZ_CH_FREQ_36; + } + if (!policy_mgr_is_safe_channel( + psoc, ch_freq)) { + policy_mgr_warn("Can't have concurrency on %d as it is not safe", + *con_ch_freq); + return QDF_STATUS_E_FAILURE; + } + } else { + policy_mgr_warn("Can't have concurrency on %d", + ch_freq); + return QDF_STATUS_E_FAILURE; + } + } + } + +update_chan: + if (ch_freq != sap_ch_freq) + *con_ch_freq = ch_freq; + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_check_concurrent_intf_and_restart_sap() - Check + * concurrent change intf + * @psoc: PSOC object information + * @operation_channel: operation channel + * @vdev_id: vdev id of SAP + * + * Checks the concurrent change interface and restarts SAP + * + * Return: None + */ +void policy_mgr_check_concurrent_intf_and_restart_sap( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t mcc_to_scc_switch; + uint32_t op_ch_freq_list[MAX_NUMBER_OF_CONC_CONNECTIONS] = {0}; + uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS] = {0}; + uint32_t cc_count = 0; + bool restart_sap = false; + uint32_t sap_freq; + /* + * if no sta, sap/p2p go may need switch channel for band + * capability change. + * If sta exist, sap/p2p go may need switch channel to force scc + */ + bool sta_check = false, gc_check = false; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + if (!pm_ctx->sta_ap_intf_check_work_info) { + policy_mgr_err("Invalid sta_ap_intf_check_work_info"); + return; + } + if (!policy_mgr_is_sap_go_existed(psoc)) { + policy_mgr_debug( + "No action taken at check_concurrent_intf_and_restart_sap"); + return; + } + /* + * If STA+SAP sessions are on DFS channel and STA+SAP SCC is + * enabled on DFS channel then move the SAP out of DFS channel + * as soon as STA gets disconnect. + * If STA+SAP sessions are on unsafe channel and STA+SAP SCC is + * enabled on unsafe channel then move the SAP to safe channel + * as soon as STA disconnected. + */ + if (policy_mgr_is_sap_restart_required_after_sta_disconnect( + psoc, INVALID_VDEV_ID, &sap_freq)) { + policy_mgr_debug("move the SAP to configured channel %u", + sap_freq); + restart_sap = true; + goto sap_restart; + } + + /* + * force SCC with STA+STA+SAP will need some additional logic + */ + cc_count = policy_mgr_get_mode_specific_conn_info( + psoc, &op_ch_freq_list[cc_count], + &vdev_id[cc_count], PM_STA_MODE); + if (!cc_count) { + policy_mgr_debug("Could not get STA operating channel&vdevid"); + } + + sta_check = !cc_count || + policy_mgr_valid_sta_channel_check(psoc, op_ch_freq_list[0]); + + cc_count = 0; + cc_count = policy_mgr_get_mode_specific_conn_info( + psoc, &op_ch_freq_list[cc_count], + &vdev_id[cc_count], PM_P2P_CLIENT_MODE); + if (!cc_count) + policy_mgr_debug("Could not get GC operating channel&vdevid"); + + gc_check = !!cc_count; + policy_mgr_debug("gc_check: %d", gc_check); + + mcc_to_scc_switch = + policy_mgr_get_mcc_to_scc_switch_mode(psoc); + policy_mgr_debug("MCC to SCC switch: %d chan: %d", + mcc_to_scc_switch, op_ch_freq_list[0]); +sap_restart: + /* + * If sta_sap_scc_on_dfs_chan is true then standalone SAP is not + * allowed on DFS channel. SAP is allowed on DFS channel only when STA + * is already connected on that channel. + * In following condition restart_sap will be true if + * sta_sap_scc_on_dfs_chan is true and SAP is on DFS channel. + * This scenario can come if STA+SAP are operating on DFS channel and + * STA gets disconnected. + */ + if (restart_sap || + ((mcc_to_scc_switch != QDF_MCC_TO_SCC_SWITCH_DISABLE) && + (sta_check || gc_check))) { + if (pm_ctx->sta_ap_intf_check_work_info) { + qdf_sched_work(0, &pm_ctx->sta_ap_intf_check_work); + policy_mgr_debug( + "Checking for Concurrent Change interference"); + } + } +} +#endif /* FEATURE_WLAN_MCC_TO_SCC_SWITCH */ + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * policy_mgr_change_sap_channel_with_csa() - Move SAP channel using (E)CSA + * @psoc: PSOC object information + * @vdev_id: Vdev id + * @ch_freq: Channel to change + * @ch_width: channel width to change + * @forced: Force to switch channel, ignore SCC/MCC check + * + * Invoke the callback function to change SAP channel using (E)CSA + * + * Return: None + */ +void policy_mgr_change_sap_channel_with_csa(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t ch_freq, + uint32_t ch_width, bool forced) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + + if (pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb) { + policy_mgr_info("SAP change change without restart"); + pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb(psoc, + vdev_id, ch_freq, ch_width, forced); + } +} +#endif + +QDF_STATUS policy_mgr_wait_for_connection_update(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_wait_single_event( + &policy_mgr_context->connection_update_done_evt, + CONNECTION_UPDATE_TIMEOUT); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("wait for event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_reset_connection_update(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_event_reset( + &policy_mgr_context->connection_update_done_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("clear event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_connection_update(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_event_set(&policy_mgr_context->connection_update_done_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("set event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_chan_switch_complete_evt( + struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + /* + * Set channel_switch_complete_evt only if no vdev has channel switch + * in progress. + */ + if (pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress && + pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress()) { + policy_mgr_info("Not all channel switch completed"); + return QDF_STATUS_SUCCESS; + } + + status = qdf_event_set( + &pm_ctx->channel_switch_complete_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("set event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_reset_chan_switch_complete_evt( + struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + status = qdf_event_reset( + &policy_mgr_context->channel_switch_complete_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("reset event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_opportunistic_update(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_event_set( + &policy_mgr_context->opportunistic_update_done_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("set event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_stop_opportunistic_timer(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + + policy_mgr_ctx = policy_mgr_get_context(psoc); + if (!policy_mgr_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + if (policy_mgr_ctx->dbs_opportunistic_timer.state != + QDF_TIMER_STATE_RUNNING) + return QDF_STATUS_SUCCESS; + + qdf_mc_timer_stop(&policy_mgr_ctx->dbs_opportunistic_timer); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_restart_opportunistic_timer( + struct wlan_objmgr_psoc *psoc, bool check_state) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + + policy_mgr_ctx = policy_mgr_get_context(psoc); + if (!policy_mgr_ctx) { + policy_mgr_err("Invalid context"); + return status; + } + + if (check_state && + QDF_TIMER_STATE_RUNNING != + policy_mgr_ctx->dbs_opportunistic_timer.state) + return status; + + qdf_mc_timer_stop(&policy_mgr_ctx->dbs_opportunistic_timer); + + status = qdf_mc_timer_start( + &policy_mgr_ctx->dbs_opportunistic_timer, + DBS_OPPORTUNISTIC_TIME * 1000); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("failed to start opportunistic timer"); + return status; + } + + return status; +} + +QDF_STATUS policy_mgr_set_hw_mode_on_channel_switch( + struct wlan_objmgr_psoc *psoc, uint8_t session_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE, qdf_status; + enum policy_mgr_conc_next_action action; + + if (!policy_mgr_is_hw_dbs_capable(psoc)) { + policy_mgr_rl_debug("PM/DBS is disabled"); + return status; + } + + action = (*policy_mgr_get_current_pref_hw_mode_ptr)(psoc); + if ((action != PM_DBS_DOWNGRADE) && + (action != PM_SINGLE_MAC_UPGRADE) && + (action != PM_DBS1_DOWNGRADE) && + (action != PM_DBS2_DOWNGRADE)) { + policy_mgr_err("Invalid action: %d", action); + status = QDF_STATUS_SUCCESS; + goto done; + } + + policy_mgr_debug("action:%d session id:%d", action, session_id); + + /* Opportunistic timer is started, PM will check if MCC upgrade can be + * done on timer expiry. This avoids any possible ping pong effect + * as well. + */ + if (action == PM_SINGLE_MAC_UPGRADE) { + qdf_status = policy_mgr_restart_opportunistic_timer( + psoc, false); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + policy_mgr_debug("opportunistic timer for MCC upgrade"); + goto done; + } + + /* For DBS, we want to move right away to DBS mode */ + status = policy_mgr_next_actions(psoc, session_id, action, + POLICY_MGR_UPDATE_REASON_AFTER_CHANNEL_SWITCH); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("no set hw mode command was issued"); + goto done; + } +done: + /* success must be returned only when a set hw mode was done */ + return status; +} + +QDF_STATUS policy_mgr_check_and_set_hw_mode_for_channel_switch( + struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + uint32_t ch_freq, enum policy_mgr_conn_update_reason reason) +{ + QDF_STATUS status; + struct policy_mgr_conc_connection_info info; + uint8_t num_cxn_del = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum policy_mgr_conc_next_action next_action = PM_NOP; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + if (!policy_mgr_is_hw_dbs_capable(psoc) || + (!policy_mgr_is_hw_dbs_2x2_capable(psoc) && + !policy_mgr_is_hw_dbs_required_for_band( + psoc, HW_MODE_MAC_BAND_2G))) + return QDF_STATUS_E_NOSUPPORT; + + /* + * Stop opportunistic timer as current connection info will change once + * channel is switched and thus if required it will be started once + * channel switch is completed. With new connection info. + */ + policy_mgr_stop_opportunistic_timer(psoc); + + if (policy_mgr_is_current_hwmode_dbs(psoc)) { + policy_mgr_debug("Already in DBS mode"); + return QDF_STATUS_E_ALREADY; + } + + if (wlan_reg_freq_to_band(ch_freq) != REG_BAND_2G) + return QDF_STATUS_E_NOSUPPORT; + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + /* + * Store the connection's parameter and temporarily delete it + * from the concurrency table. This way the allow concurrency + * check can be used as though a new connection is coming up, + * after check, restore the connection to concurrency table. + */ + policy_mgr_store_and_del_conn_info_by_vdev_id(psoc, vdev_id, + &info, &num_cxn_del); + + status = policy_mgr_get_next_action(psoc, vdev_id, ch_freq, + reason, &next_action); + /* Restore the connection entry */ + if (num_cxn_del) + policy_mgr_restore_deleted_conn_info(psoc, &info, num_cxn_del); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (QDF_IS_STATUS_ERROR(status)) + goto chk_opportunistic_timer; + + if (PM_NOP != next_action) + status = policy_mgr_next_actions(psoc, vdev_id, + next_action, reason); + else + status = QDF_STATUS_E_NOSUPPORT; + +chk_opportunistic_timer: + /* + * If hw mode change failed restart the opportunistic timer to + * Switch to single mac if required. + */ + if (status == QDF_STATUS_E_FAILURE) { + policy_mgr_err("Failed to update HW modeStatus %d", status); + policy_mgr_check_n_start_opportunistic_timer(psoc); + } + + return status; +} + +void policy_mgr_checkn_update_hw_mode_single_mac_mode( + struct wlan_objmgr_psoc *psoc, uint32_t ch_freq) +{ + uint8_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (QDF_TIMER_STATE_RUNNING == pm_ctx->dbs_opportunistic_timer.state) + qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); + + if (policy_mgr_is_hw_dbs_required_for_band(psoc, HW_MODE_MAC_BAND_2G) && + (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq))) { + policy_mgr_debug("DBS required for new connection"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if (pm_conc_connection_list[i].in_use) { + if (!WLAN_REG_IS_SAME_BAND_FREQS( + ch_freq, pm_conc_connection_list[i].freq)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("DBS required"); + return; + } + if (policy_mgr_is_hw_dbs_required_for_band( + psoc, HW_MODE_MAC_BAND_2G) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[i].freq)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("DBS required"); + return; + } + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + pm_dbs_opportunistic_timer_handler((void *)psoc); +} + +void policy_mgr_check_and_stop_opportunistic_timer( + struct wlan_objmgr_psoc *psoc, uint8_t id) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum policy_mgr_conc_next_action action = PM_NOP; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + enum policy_mgr_conn_update_reason reason; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + if (QDF_TIMER_STATE_RUNNING == + pm_ctx->dbs_opportunistic_timer.state) { + qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); + action = policy_mgr_need_opportunistic_upgrade(psoc, &reason); + if (action) { + qdf_event_reset(&pm_ctx->opportunistic_update_done_evt); + status = policy_mgr_next_actions(psoc, id, action, + reason); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed in policy_mgr_next_actions"); + return; + } + status = qdf_wait_single_event( + &pm_ctx->opportunistic_update_done_evt, + CONNECTION_UPDATE_TIMEOUT); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("wait for event failed"); + return; + } + } + } +} + +void policy_mgr_set_hw_mode_change_in_progress( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_hw_mode_change value) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + pm_ctx->hw_mode_change_in_progress = value; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + policy_mgr_debug("hw_mode_change_in_progress:%d", value); +} + +enum policy_mgr_hw_mode_change policy_mgr_is_hw_mode_change_in_progress( + struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_hw_mode_change value; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + value = POLICY_MGR_HW_MODE_NOT_IN_PROGRESS; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return value; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + value = pm_ctx->hw_mode_change_in_progress; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return value; +} + +enum policy_mgr_hw_mode_change policy_mgr_get_hw_mode_change_from_hw_mode_index( + struct wlan_objmgr_psoc *psoc, uint32_t hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct policy_mgr_hw_mode_params hw_mode; + enum policy_mgr_hw_mode_change value + = POLICY_MGR_HW_MODE_NOT_IN_PROGRESS; + QDF_STATUS status; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return value; + } + + status = policy_mgr_get_hw_mode_from_idx(psoc, hw_mode_index, &hw_mode); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to get HW mode index"); + return value; + } + + if (hw_mode.dbs_cap) { + policy_mgr_debug("DBS is requested with HW (%d)", + hw_mode_index); + value = POLICY_MGR_DBS_IN_PROGRESS; + goto ret_value; + } + + if (hw_mode.sbs_cap) { + policy_mgr_debug("SBS is requested with HW (%d)", + hw_mode_index); + value = POLICY_MGR_SBS_IN_PROGRESS; + goto ret_value; + } + + value = POLICY_MGR_SMM_IN_PROGRESS; + policy_mgr_debug("SMM is requested with HW (%d)", hw_mode_index); + +ret_value: + return value; +} + +#ifdef MPC_UT_FRAMEWORK +QDF_STATUS policy_mgr_update_connection_info_utfw( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t ch_freq, uint32_t mac_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0, found = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint16_t ch_flagext = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (vdev_id == pm_conc_connection_list[conn_index].vdev_id) { + /* debug msg */ + found = 1; + break; + } + conn_index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + if (!found) { + /* err msg */ + policy_mgr_err("can't find vdev_id %d in pm_conc_connection_list", + vdev_id); + return status; + } + policy_mgr_debug("--> updating entry at index[%d]", conn_index); + + if (wlan_reg_is_dfs_for_freq(pm_ctx->pdev, ch_freq)) + ch_flagext |= IEEE80211_CHAN_DFS; + + policy_mgr_update_conc_list(psoc, conn_index, + policy_mgr_get_mode(type, sub_type), + ch_freq, HW_MODE_20_MHZ, + mac_id, chain_mask, 0, vdev_id, true, true, + ch_flagext); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_incr_connection_count_utfw( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t ch_freq, uint32_t mac_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0; + bool update_conn = true; + enum policy_mgr_con_mode mode; + uint16_t ch_flagext = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + conn_index = policy_mgr_get_connection_count(psoc); + if (MAX_NUMBER_OF_CONC_CONNECTIONS <= conn_index) { + /* err msg */ + policy_mgr_err("exceeded max connection limit %d", + MAX_NUMBER_OF_CONC_CONNECTIONS); + return status; + } + policy_mgr_debug("--> filling entry at index[%d]", conn_index); + + mode = policy_mgr_get_mode(type, sub_type); + if (mode == PM_STA_MODE || mode == PM_P2P_CLIENT_MODE) + update_conn = false; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid pm context"); + return false; + } + if (wlan_reg_is_dfs_for_freq(pm_ctx->pdev, ch_freq)) + ch_flagext |= IEEE80211_CHAN_DFS; + + policy_mgr_update_conc_list(psoc, conn_index, mode, ch_freq, + HW_MODE_20_MHZ, mac_id, chain_mask, + 0, vdev_id, true, update_conn, ch_flagext); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_decr_connection_count_utfw(struct wlan_objmgr_psoc *psoc, + uint32_t del_all, uint32_t vdev_id) +{ + QDF_STATUS status; + + if (del_all) { + status = policy_mgr_psoc_disable(psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("Policy manager initialization failed"); + return QDF_STATUS_E_FAILURE; + } + status = policy_mgr_psoc_enable(psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("Policy manager initialization failed"); + return QDF_STATUS_E_FAILURE; + } + } else { + policy_mgr_decr_connection_count(psoc, vdev_id); + } + + return QDF_STATUS_SUCCESS; +} + +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_first_conn_table( + enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref) +{ + if ((sys_pref >= PM_MAX_CONC_PRIORITY_MODE) || + (type >= PM_MAX_NUM_OF_MODE)) + return PM_MAX_PCL_TYPE; + return first_connection_pcl_table[type][sys_pref]; +} + +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_second_conn_table( + enum policy_mgr_one_connection_mode idx, enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable) +{ + if ((idx >= PM_MAX_ONE_CONNECTION_MODE) || + (sys_pref >= PM_MAX_CONC_PRIORITY_MODE) || + (type >= PM_MAX_NUM_OF_MODE)) + return PM_MAX_PCL_TYPE; + if (dbs_capable) + return (*second_connection_pcl_dbs_table)[idx][type][sys_pref]; + else + return second_connection_pcl_nodbs_table[idx][type][sys_pref]; +} + +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_third_conn_table( + enum policy_mgr_two_connection_mode idx, enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable) +{ + if ((idx >= PM_MAX_TWO_CONNECTION_MODE) || + (sys_pref >= PM_MAX_CONC_PRIORITY_MODE) || + (type >= PM_MAX_NUM_OF_MODE)) + return PM_MAX_PCL_TYPE; + if (dbs_capable) + return (*third_connection_pcl_dbs_table)[idx][type][sys_pref]; + else + return third_connection_pcl_nodbs_table[idx][type][sys_pref]; +} +#endif diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c new file mode 100644 index 0000000000000000000000000000000000000000..8b17d4c23069dd78b9fc8ea0630e2bf7aac36e13 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c @@ -0,0 +1,3416 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_core.c + * + * WLAN Concurrenct Connection Management functions + * + */ + +/* Include files */ + +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_objmgr_global_obj.h" + +#define POLICY_MGR_MAX_CON_STRING_LEN 100 + +struct policy_mgr_conc_connection_info + pm_conc_connection_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + +struct policy_mgr_psoc_priv_obj *policy_mgr_get_context( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + pm_ctx = (struct policy_mgr_psoc_priv_obj *) + wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_POLICY_MGR); + return pm_ctx; +} + +/** + * policy_mgr_get_updated_scan_config() - Get the updated scan configuration + * @scan_config: Pointer containing the updated scan config + * @dbs_scan: 0 or 1 indicating if DBS scan needs to be enabled/disabled + * @dbs_plus_agile_scan: 0 or 1 indicating if DBS plus agile scan needs to be + * enabled/disabled + * @single_mac_scan_with_dfs: 0 or 1 indicating if single MAC scan with DFS + * needs to be enabled/disabled + * + * Takes the current scan configuration and set the necessary scan config + * bits to either 0/1 and provides the updated value to the caller who + * can use this to pass it on to the FW + * + * Return: 0 on success + */ +QDF_STATUS policy_mgr_get_updated_scan_config( + struct wlan_objmgr_psoc *psoc, + uint32_t *scan_config, + bool dbs_scan, + bool dbs_plus_agile_scan, + bool single_mac_scan_with_dfs) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + *scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + + WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_SET(*scan_config, dbs_scan); + WMI_DBS_CONC_SCAN_CFG_AGILE_SCAN_SET(*scan_config, + dbs_plus_agile_scan); + WMI_DBS_CONC_SCAN_CFG_AGILE_DFS_SCAN_SET(*scan_config, + single_mac_scan_with_dfs); + + policy_mgr_debug("scan_config:%x ", *scan_config); + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_get_updated_fw_mode_config() - Get the updated fw + * mode configuration + * @fw_mode_config: Pointer containing the updated fw mode config + * @dbs: 0 or 1 indicating if DBS needs to be enabled/disabled + * @agile_dfs: 0 or 1 indicating if agile DFS needs to be enabled/disabled + * + * Takes the current fw mode configuration and set the necessary fw mode config + * bits to either 0/1 and provides the updated value to the caller who + * can use this to pass it on to the FW + * + * Return: 0 on success + */ +QDF_STATUS policy_mgr_get_updated_fw_mode_config( + struct wlan_objmgr_psoc *psoc, + uint32_t *fw_mode_config, + bool dbs, + bool agile_dfs) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + *fw_mode_config = pm_ctx->dual_mac_cfg.cur_fw_mode_config; + + WMI_DBS_FW_MODE_CFG_DBS_SET(*fw_mode_config, dbs); + WMI_DBS_FW_MODE_CFG_AGILE_DFS_SET(*fw_mode_config, agile_dfs); + + policy_mgr_debug("fw_mode_config:%x ", *fw_mode_config); + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_is_dual_mac_disabled_in_ini() - Check if dual mac + * is disabled in INI + * + * Checks if the dual mac feature is disabled in INI + * + * Return: true if the dual mac connection is disabled from INI + */ +bool policy_mgr_is_dual_mac_disabled_in_ini( + struct wlan_objmgr_psoc *psoc) +{ + bool is_disabled = false; + uint8_t dbs_type = DISABLE_DBS_CXN_AND_SCAN; + + policy_mgr_get_dual_mac_feature(psoc, &dbs_type); + /* + * If DBS support for connection is disabled through INI then assume + * that DBS is not supported, so that policy manager takes + * the decision considering non-dbs cases only. + * + * For DBS scan check the INI value explicitly + */ + switch (dbs_type) { + case DISABLE_DBS_CXN_AND_SCAN: + case DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN: + case DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN_WITH_ASYNC_SCAN_OFF: + is_disabled = true; + break; + default: + break; + } + + return is_disabled; +} + +uint32_t policy_mgr_get_mcc_to_scc_switch_mode( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + return pm_ctx->cfg.mcc_to_scc_switch; +} + +/** + * policy_mgr_get_dbs_config() - Get DBS bit + * + * Gets the DBS bit of fw_mode_config_bits + * + * Return: 0 or 1 to indicate the DBS bit + */ +bool policy_mgr_get_dbs_config(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t fw_mode_config; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + fw_mode_config = pm_ctx->dual_mac_cfg.cur_fw_mode_config; + + return WMI_DBS_FW_MODE_CFG_DBS_GET(fw_mode_config); +} + +/** + * policy_mgr_get_agile_dfs_config() - Get Agile DFS bit + * + * Gets the Agile DFS bit of fw_mode_config_bits + * + * Return: 0 or 1 to indicate the Agile DFS bit + */ +bool policy_mgr_get_agile_dfs_config(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t fw_mode_config; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + fw_mode_config = pm_ctx->dual_mac_cfg.cur_fw_mode_config; + + return WMI_DBS_FW_MODE_CFG_AGILE_DFS_GET(fw_mode_config); +} + +/** + * policy_mgr_get_dbs_scan_config() - Get DBS scan bit + * + * Gets the DBS scan bit of concurrent_scan_config_bits + * + * Return: 0 or 1 to indicate the DBS scan bit + */ +bool policy_mgr_get_dbs_scan_config(struct wlan_objmgr_psoc *psoc) +{ + uint32_t scan_config; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + + return WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_GET(scan_config); +} + +/** + * policy_mgr_get_tx_rx_ss_from_config() - Get Tx/Rx spatial + * stream from HW mode config + * @mac_ss: Config which indicates the HW mode as per 'hw_mode_ss_config' + * @tx_ss: Contains the Tx spatial stream + * @rx_ss: Contains the Rx spatial stream + * + * Returns the number of spatial streams of Tx and Rx + * + * Return: None + */ +void policy_mgr_get_tx_rx_ss_from_config(enum hw_mode_ss_config mac_ss, + uint32_t *tx_ss, uint32_t *rx_ss) +{ + switch (mac_ss) { + case HW_MODE_SS_0x0: + *tx_ss = 0; + *rx_ss = 0; + break; + case HW_MODE_SS_1x1: + *tx_ss = 1; + *rx_ss = 1; + break; + case HW_MODE_SS_2x2: + *tx_ss = 2; + *rx_ss = 2; + break; + case HW_MODE_SS_3x3: + *tx_ss = 3; + *rx_ss = 3; + break; + case HW_MODE_SS_4x4: + *tx_ss = 4; + *rx_ss = 4; + break; + default: + *tx_ss = 0; + *rx_ss = 0; + } +} + +/** + * policy_mgr_get_matching_hw_mode_index() - Get matching HW mode index + * @psoc: psoc handle + * @mac0_tx_ss: Number of tx spatial streams of MAC0 + * @mac0_rx_ss: Number of rx spatial streams of MAC0 + * @mac0_bw: Bandwidth of MAC0 of type 'hw_mode_bandwidth' + * @mac1_tx_ss: Number of tx spatial streams of MAC1 + * @mac1_rx_ss: Number of rx spatial streams of MAC1 + * @mac1_bw: Bandwidth of MAC1 of type 'hw_mode_bandwidth' + * @mac0_band_cap: mac0 band capability requirement + * (0: Don't care, 1: 2.4G, 2: 5G) + * @dbs: DBS capability of type 'hw_mode_dbs_capab' + * @dfs: Agile DFS capability of type 'hw_mode_agile_dfs_capab' + * @sbs: SBS capability of type 'hw_mode_sbs_capab' + * + * Fetches the HW mode index corresponding to the HW mode provided. + * In Genoa two DBS HW modes (2x2 5G + 1x1 2G, 2x2 2G + 1x1 5G), + * the "ss" number and "bw" value are not enough to specify the expected + * HW mode. But in both HW mode, the mac0 can support either 5G or 2G. + * So, the Parameter "mac0_band_cap" will specify the expected band support + * requirement on mac 0 to find the expected HW mode. + * + * Return: Positive hw mode index in case a match is found or a negative + * value, otherwise + */ +int8_t policy_mgr_get_matching_hw_mode_index( + struct wlan_objmgr_psoc *psoc, + uint32_t mac0_tx_ss, uint32_t mac0_rx_ss, + enum hw_mode_bandwidth mac0_bw, + uint32_t mac1_tx_ss, uint32_t mac1_rx_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs) +{ + uint32_t i; + uint32_t t_mac0_tx_ss, t_mac0_rx_ss, t_mac0_bw; + uint32_t t_mac1_tx_ss, t_mac1_rx_ss, t_mac1_bw; + uint32_t dbs_mode, agile_dfs_mode, sbs_mode; + uint32_t t_mac0_band_cap; + int8_t found = -EINVAL; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return found; + } + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + t_mac0_tx_ss = POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac0_tx_ss < mac0_tx_ss) + continue; + + t_mac0_rx_ss = POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac0_rx_ss < mac0_rx_ss) + continue; + + t_mac0_bw = POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + /* + * Firmware advertises max bw capability as CBW 80+80 + * for single MAC. Thus CBW 20/40/80 should also be + * supported, if CBW 80+80 is supported. + */ + if (t_mac0_bw < mac0_bw) + continue; + + t_mac1_tx_ss = POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac1_tx_ss < mac1_tx_ss) + continue; + + t_mac1_rx_ss = POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac1_rx_ss < mac1_rx_ss) + continue; + + t_mac1_bw = POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac1_bw < mac1_bw) + continue; + + dbs_mode = POLICY_MGR_HW_MODE_DBS_MODE_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (dbs_mode != dbs) + continue; + + agile_dfs_mode = POLICY_MGR_HW_MODE_AGILE_DFS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (agile_dfs_mode != dfs) + continue; + + sbs_mode = POLICY_MGR_HW_MODE_SBS_MODE_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (sbs_mode != sbs) + continue; + + t_mac0_band_cap = POLICY_MGR_HW_MODE_MAC0_BAND_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (mac0_band_cap && t_mac0_band_cap != mac0_band_cap) + continue; + + found = POLICY_MGR_HW_MODE_ID_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + + policy_mgr_debug("hw_mode id %d found at %d", found, i); + + break; + } + return found; +} + +/** + * policy_mgr_get_hw_mode_from_dbs_hw_list() - Get hw_mode index + * @mac0_ss: MAC0 spatial stream configuration + * @mac0_bw: MAC0 bandwidth configuration + * @mac1_ss: MAC1 spatial stream configuration + * @mac1_bw: MAC1 bandwidth configuration + * @mac0_band_cap: mac0 band capability requirement + * (0: Don't care, 1: 2.4G, 2: 5G) + * @dbs: HW DBS capability + * @dfs: HW Agile DFS capability + * @sbs: HW SBS capability + * + * Get the HW mode index corresponding to the HW modes spatial stream, + * bandwidth, DBS, Agile DFS and SBS capability + * + * In Genoa two DBS HW modes (2x2 5G + 1x1 2G, 2x2 2G + 1x1 5G), + * the "ss" number and "bw" value are not enough to specify the expected + * HW mode. But in both HW mode, the mac0 can support either 5G or 2G. + * So, the Parameter "mac0_band_cap" will specify the expected band support + * requirement on mac 0 to find the expected HW mode. + * + * Return: Index number if a match is found or -negative value if not found + */ +int8_t policy_mgr_get_hw_mode_idx_from_dbs_hw_list( + struct wlan_objmgr_psoc *psoc, + enum hw_mode_ss_config mac0_ss, + enum hw_mode_bandwidth mac0_bw, + enum hw_mode_ss_config mac1_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs) +{ + uint32_t mac0_tx_ss, mac0_rx_ss; + uint32_t mac1_tx_ss, mac1_rx_ss; + + policy_mgr_get_tx_rx_ss_from_config(mac0_ss, &mac0_tx_ss, &mac0_rx_ss); + policy_mgr_get_tx_rx_ss_from_config(mac1_ss, &mac1_tx_ss, &mac1_rx_ss); + + policy_mgr_debug("MAC0: TxSS=%d, RxSS=%d, BW=%d band=%d", + mac0_tx_ss, mac0_rx_ss, mac0_bw, mac0_band_cap); + policy_mgr_debug("MAC1: TxSS=%d, RxSS=%d, BW=%d", + mac1_tx_ss, mac1_rx_ss, mac1_bw); + policy_mgr_debug("DBS=%d, Agile DFS=%d, SBS=%d", + dbs, dfs, sbs); + + return policy_mgr_get_matching_hw_mode_index(psoc, mac0_tx_ss, + mac0_rx_ss, + mac0_bw, + mac1_tx_ss, mac1_rx_ss, + mac1_bw, + mac0_band_cap, + dbs, dfs, sbs); +} + +/** + * policy_mgr_get_hw_mode_from_idx() - Get HW mode based on index + * @psoc: psoc object + * @idx: HW mode id + * @hw_mode: HW mode params + * + * Fetches the HW mode parameters + * + * Return: Success if hw mode is obtained and the hw mode params + */ +QDF_STATUS policy_mgr_get_hw_mode_from_idx( + struct wlan_objmgr_psoc *psoc, + uint32_t idx, + struct policy_mgr_hw_mode_params *hw_mode) +{ + uint32_t param; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t mac0_min_ss; + uint8_t mac1_min_ss; + uint32_t i, hw_mode_id; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + if (!pm_ctx->num_dbs_hw_modes) { + policy_mgr_err("No dbs hw modes available"); + return QDF_STATUS_E_FAILURE; + } + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + hw_mode_id = POLICY_MGR_HW_MODE_ID_GET(param); + if (hw_mode_id == idx) + break; + } + if (i >= pm_ctx->num_dbs_hw_modes) { + policy_mgr_err("hw mode id %d not found", idx); + return QDF_STATUS_E_FAILURE; + } + + param = pm_ctx->hw_mode.hw_mode_list[i]; + + hw_mode->mac0_tx_ss = POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET(param); + hw_mode->mac0_rx_ss = POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET(param); + hw_mode->mac0_bw = POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_GET(param); + hw_mode->mac1_tx_ss = POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET(param); + hw_mode->mac1_rx_ss = POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET(param); + hw_mode->mac1_bw = POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_GET(param); + hw_mode->mac0_band_cap = POLICY_MGR_HW_MODE_MAC0_BAND_GET(param); + hw_mode->dbs_cap = POLICY_MGR_HW_MODE_DBS_MODE_GET(param); + hw_mode->agile_dfs_cap = POLICY_MGR_HW_MODE_AGILE_DFS_GET(param); + hw_mode->sbs_cap = POLICY_MGR_HW_MODE_SBS_MODE_GET(param); + if (hw_mode->dbs_cap) { + mac0_min_ss = QDF_MIN(hw_mode->mac0_tx_ss, hw_mode->mac0_rx_ss); + mac1_min_ss = QDF_MIN(hw_mode->mac1_tx_ss, hw_mode->mac1_rx_ss); + if (hw_mode->mac0_band_cap == WLAN_5G_CAPABILITY && + mac0_min_ss && mac1_min_ss && + mac0_min_ss > mac1_min_ss) + hw_mode->action_type = PM_DBS1; + else if (hw_mode->mac0_band_cap == WLAN_2G_CAPABILITY && + mac0_min_ss && mac1_min_ss && + mac0_min_ss > mac1_min_ss) + hw_mode->action_type = PM_DBS2; + else + hw_mode->action_type = PM_DBS; + } + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_get_old_and_new_hw_index() - Get the old and new HW index + * @old_hw_mode_index: Value at this pointer contains the old HW mode index + * Default value when not configured is POLICY_MGR_DEFAULT_HW_MODE_INDEX + * @new_hw_mode_index: Value at this pointer contains the new HW mode index + * Default value when not configured is POLICY_MGR_DEFAULT_HW_MODE_INDEX + * + * Get the old and new HW index configured in the driver + * + * Return: Failure in case the HW mode indices cannot be fetched and Success + * otherwise. When no HW mode transition has happened the values of + * old_hw_mode_index and new_hw_mode_index will be the same. + */ +QDF_STATUS policy_mgr_get_old_and_new_hw_index( + struct wlan_objmgr_psoc *psoc, + uint32_t *old_hw_mode_index, + uint32_t *new_hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_INVAL; + } + + *old_hw_mode_index = pm_ctx->old_hw_mode_index; + *new_hw_mode_index = pm_ctx->new_hw_mode_index; + + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_update_conc_list(struct wlan_objmgr_psoc *psoc, + uint32_t conn_index, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, + enum hw_mode_bandwidth bw, + uint8_t mac, + enum policy_mgr_chain_mode chain_mask, + uint32_t original_nss, + uint32_t vdev_id, + bool in_use, + bool update_conn, + uint16_t ch_flagext) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool mcc_mode; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (conn_index >= MAX_NUMBER_OF_CONC_CONNECTIONS) { + policy_mgr_err("Number of connections exceeded conn_index: %d", + conn_index); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + pm_conc_connection_list[conn_index].mode = mode; + pm_conc_connection_list[conn_index].freq = ch_freq; + pm_conc_connection_list[conn_index].bw = bw; + pm_conc_connection_list[conn_index].mac = mac; + pm_conc_connection_list[conn_index].chain_mask = chain_mask; + pm_conc_connection_list[conn_index].original_nss = original_nss; + pm_conc_connection_list[conn_index].vdev_id = vdev_id; + pm_conc_connection_list[conn_index].in_use = in_use; + pm_conc_connection_list[conn_index].ch_flagext = ch_flagext; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + /* + * For STA and P2P client mode, the mode change event sent as part + * of the callback causes delay in processing M1 frame at supplicant + * resulting in cert test case failure. The mode change event is sent + * as part of add key for STA and P2P client mode. + */ + if (pm_ctx->mode_change_cb && update_conn) + pm_ctx->mode_change_cb(); + + policy_mgr_dump_connection_status_info(psoc); + if (pm_ctx->cdp_cbacks.cdp_update_mac_id) + pm_ctx->cdp_cbacks.cdp_update_mac_id(psoc, vdev_id, mac); + + /* IPA only cares about STA or SAP mode */ + if (mode == PM_STA_MODE || mode == PM_SAP_MODE) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + mcc_mode = policy_mgr_current_concurrency_is_mcc(psoc); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb) + pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb(mcc_mode); + } +} + +/** + * policy_mgr_store_and_del_conn_info() - Store and del a connection info + * @mode: Mode whose entry has to be deleted + * @all_matching_cxn_to_del: All the specified mode entries should be deleted + * @info: Struture array pointer where the connection info will be saved + * @num_cxn_del: Number of connection which are going to be deleted + * + * Saves the connection info corresponding to the provided mode + * and deleted that corresponding entry based on vdev from the + * connection info structure + * + * Return: None + */ +void policy_mgr_store_and_del_conn_info(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, bool all_matching_cxn_to_del, + struct policy_mgr_conc_connection_info *info, uint8_t *num_cxn_del) +{ + int32_t conn_index = 0; + uint32_t found_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (!num_cxn_del) { + policy_mgr_err("num_cxn_del is NULL"); + return; + } + *num_cxn_del = 0; + if (!info) { + policy_mgr_err("Invalid connection info"); + return; + } + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (mode == pm_conc_connection_list[conn_index].mode) { + /* + * Storing the connection entry which will be + * temporarily deleted. + */ + info[found_index] = pm_conc_connection_list[conn_index]; + /* Deleting the connection entry */ + policy_mgr_decr_connection_count(psoc, + info[found_index].vdev_id); + policy_mgr_debug("Stored %d (%d), deleted STA entry with vdev id %d, index %d", + info[found_index].vdev_id, + info[found_index].mode, + info[found_index].vdev_id, conn_index); + pm_ctx->no_of_active_sessions[info->mode]--; + found_index++; + if (all_matching_cxn_to_del) + continue; + else + break; + } + conn_index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (!found_index) { + *num_cxn_del = 0; + policy_mgr_debug("Mode:%d not available in the conn info", + mode); + } else { + *num_cxn_del = found_index; + policy_mgr_debug("Mode:%d number of conn %d temp del", + mode, *num_cxn_del); + } + + /* + * Caller should set the PCL and restore the connection entry + * in conn info. + */ +} + +void policy_mgr_store_and_del_conn_info_by_vdev_id( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + struct policy_mgr_conc_connection_info *info, + uint8_t *num_cxn_del) +{ + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (!info || !num_cxn_del) { + policy_mgr_err("Invalid parameters"); + return; + } + *num_cxn_del = 0; + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].vdev_id == vdev_id) && + pm_conc_connection_list[conn_index].in_use) { + *num_cxn_del = 1; + break; + } + } + /* + * Storing the connection entry which will be + * temporarily deleted. + */ + if (*num_cxn_del == 1) { + *info = pm_conc_connection_list[conn_index]; + pm_ctx->no_of_active_sessions[info->mode]--; + /* Deleting the connection entry */ + policy_mgr_decr_connection_count( + psoc, + pm_conc_connection_list[conn_index].vdev_id); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); +} + +void policy_mgr_store_and_del_conn_info_by_chan_and_mode( + struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq, + enum policy_mgr_con_mode mode, + struct policy_mgr_conc_connection_info *info, + uint8_t *num_cxn_del) +{ + uint32_t conn_index = 0; + uint8_t found_index = 0; + + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (!info || !num_cxn_del) { + policy_mgr_err("Invalid parameters"); + return; + } + *num_cxn_del = 0; + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (ch_freq != pm_conc_connection_list[conn_index].freq || + mode != pm_conc_connection_list[conn_index].mode) { + conn_index++; + continue; + } + info[found_index] = pm_conc_connection_list[conn_index]; + policy_mgr_debug("Stored %d (%d), deleted STA entry with vdev id %d, index %d ch %d", + info[found_index].vdev_id, + info[found_index].mode, + info[found_index].vdev_id, conn_index, + ch_freq); + found_index++; + conn_index++; + } + conn_index = 0; + while (conn_index < found_index) { + policy_mgr_decr_connection_count( + psoc, info[conn_index].vdev_id); + + pm_ctx->no_of_active_sessions[info[conn_index].mode]--; + conn_index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + *num_cxn_del = found_index; +} + +/** + * policy_mgr_restore_deleted_conn_info() - Restore connection info + * @info: An array saving connection info that is to be restored + * @num_cxn_del: Number of connection temporary deleted + * + * Restores the connection info of STA that was saved before + * updating the PCL to the FW + * + * Return: None + */ +void policy_mgr_restore_deleted_conn_info(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_conc_connection_info *info, + uint8_t num_cxn_del) +{ + uint32_t conn_index; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (MAX_NUMBER_OF_CONC_CONNECTIONS <= num_cxn_del || 0 == num_cxn_del) { + policy_mgr_err("Failed to restore %d/%d deleted information", + num_cxn_del, MAX_NUMBER_OF_CONC_CONNECTIONS); + return; + } + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + conn_index = policy_mgr_get_connection_count(psoc); + if (MAX_NUMBER_OF_CONC_CONNECTIONS <= conn_index) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_err("Failed to restore the deleted information %d/%d", + conn_index, MAX_NUMBER_OF_CONC_CONNECTIONS); + return; + } + + qdf_mem_copy(&pm_conc_connection_list[conn_index], info, + num_cxn_del * sizeof(*info)); + pm_ctx->no_of_active_sessions[info->mode] += num_cxn_del; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + policy_mgr_debug("Restored the deleleted conn info, vdev:%d, index:%d", + info->vdev_id, conn_index); +} + +/** + * policy_mgr_update_hw_mode_conn_info() - Update connection + * info based on HW mode + * @num_vdev_mac_entries: Number of vdev-mac id entries that follow + * @vdev_mac_map: Mapping of vdev-mac id + * @hw_mode: HW mode + * + * Updates the connection info parameters based on the new HW mode + * + * Return: None + */ +void policy_mgr_update_hw_mode_conn_info(struct wlan_objmgr_psoc *psoc, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + struct policy_mgr_hw_mode_params hw_mode) +{ + uint32_t i, conn_index, found; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < num_vdev_mac_entries; i++) { + conn_index = 0; + found = 0; + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (vdev_mac_map[i].vdev_id == + pm_conc_connection_list[conn_index].vdev_id) { + found = 1; + break; + } + conn_index++; + } + if (found) { + pm_conc_connection_list[conn_index].mac = + vdev_mac_map[i].mac_id; + policy_mgr_debug("vdev:%d, mac:%d", + pm_conc_connection_list[conn_index].vdev_id, + pm_conc_connection_list[conn_index].mac); + if (pm_ctx->cdp_cbacks.cdp_update_mac_id) + pm_ctx->cdp_cbacks.cdp_update_mac_id( + psoc, + vdev_mac_map[i].vdev_id, + vdev_mac_map[i].mac_id); + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_dump_connection_status_info(psoc); +} + +void policy_mgr_pdev_set_hw_mode_cb(uint32_t status, + uint32_t cfgd_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id, void *context) +{ + QDF_STATUS ret; + struct policy_mgr_hw_mode_params hw_mode; + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(context); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + goto set_done_event; + } + + policy_mgr_set_hw_mode_change_in_progress(context, + POLICY_MGR_HW_MODE_NOT_IN_PROGRESS); + + if (status == SET_HW_MODE_STATUS_OK || + status == SET_HW_MODE_STATUS_ALREADY) { + policy_mgr_set_connection_update(context); + } + + if (status != SET_HW_MODE_STATUS_OK) { + policy_mgr_debug("Set HW mode failed with status %d", status); + goto next_action; + } + + /* vdev mac map for NAN Discovery is expected in NAN Enable resp */ + if (reason != POLICY_MGR_UPDATE_REASON_NAN_DISCOVERY && + !vdev_mac_map) { + policy_mgr_err("vdev_mac_map is NULL"); + goto set_done_event; + } + + policy_mgr_debug("cfgd_hw_mode_index=%d", cfgd_hw_mode_index); + + for (i = 0; i < num_vdev_mac_entries; i++) + policy_mgr_debug("vdev_id:%d mac_id:%d", + vdev_mac_map[i].vdev_id, + vdev_mac_map[i].mac_id); + + ret = policy_mgr_get_hw_mode_from_idx(context, cfgd_hw_mode_index, + &hw_mode); + if (ret != QDF_STATUS_SUCCESS) { + policy_mgr_err("Get HW mode failed: %d", ret); + goto set_done_event; + } + + policy_mgr_debug("MAC0: TxSS:%d, RxSS:%d, Bw:%d, band_cap %d", + hw_mode.mac0_tx_ss, hw_mode.mac0_rx_ss, + hw_mode.mac0_bw, hw_mode.mac0_band_cap); + policy_mgr_debug("MAC1: TxSS:%d, RxSS:%d, Bw:%d", + hw_mode.mac1_tx_ss, hw_mode.mac1_rx_ss, + hw_mode.mac1_bw); + policy_mgr_debug("DBS:%d, Agile DFS:%d, SBS:%d", + hw_mode.dbs_cap, hw_mode.agile_dfs_cap, + hw_mode.sbs_cap); + + /* update pm_conc_connection_list */ + policy_mgr_update_hw_mode_conn_info(context, + num_vdev_mac_entries, + vdev_mac_map, + hw_mode); + if (pm_ctx->mode_change_cb) + pm_ctx->mode_change_cb(); + + /* Notify tdls */ + if (pm_ctx->tdls_cbacks.tdls_notify_decrement_session) + pm_ctx->tdls_cbacks.tdls_notify_decrement_session(pm_ctx->psoc); + +next_action: + if (PM_NOP != next_action && (status == SET_HW_MODE_STATUS_ALREADY || + status == SET_HW_MODE_STATUS_OK)) + policy_mgr_next_actions(context, session_id, + next_action, reason); + else + policy_mgr_debug("No action needed right now"); + +set_done_event: + ret = policy_mgr_set_opportunistic_update(context); + if (!QDF_IS_STATUS_SUCCESS(ret)) + policy_mgr_err("ERROR: set opportunistic_update event failed"); +} + +/** + * policy_mgr_dump_current_concurrency_one_connection() - To dump the + * current concurrency info with one connection + * @cc_mode: connection string + * @length: Maximum size of the string + * + * This routine is called to dump the concurrency info + * + * Return: length of the string + */ +static uint32_t policy_mgr_dump_current_concurrency_one_connection( + char *cc_mode, uint32_t length) +{ + uint32_t count = 0; + enum policy_mgr_con_mode mode; + + mode = pm_conc_connection_list[0].mode; + + switch (mode) { + case PM_STA_MODE: + count = strlcat(cc_mode, "STA", + length); + break; + case PM_SAP_MODE: + count = strlcat(cc_mode, "SAP", + length); + break; + case PM_P2P_CLIENT_MODE: + count = strlcat(cc_mode, "P2P CLI", + length); + break; + case PM_P2P_GO_MODE: + count = strlcat(cc_mode, "P2P GO", + length); + break; + case PM_IBSS_MODE: + count = strlcat(cc_mode, "IBSS", + length); + break; + case PM_NAN_DISC_MODE: + count = strlcat(cc_mode, "NAN DISC", length); + break; + case PM_NDI_MODE: + count = strlcat(cc_mode, "NDI", length); + break; + default: + policy_mgr_err("unexpected mode %d", mode); + break; + } + + return count; +} + +/** + * policy_mgr_dump_current_concurrency_two_connection() - To dump the + * current concurrency info with two connections + * @cc_mode: connection string + * @length: Maximum size of the string + * + * This routine is called to dump the concurrency info + * + * Return: length of the string + */ +static uint32_t policy_mgr_dump_current_concurrency_two_connection( + char *cc_mode, uint32_t length) +{ + uint32_t count = 0; + enum policy_mgr_con_mode mode; + + mode = pm_conc_connection_list[1].mode; + + switch (mode) { + case PM_STA_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+STA", + length); + break; + case PM_SAP_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+SAP", + length); + break; + case PM_P2P_CLIENT_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+P2P CLI", + length); + break; + case PM_P2P_GO_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+P2P GO", + length); + break; + case PM_IBSS_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+IBSS", + length); + break; + case PM_NDI_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+NDI", + length); + break; + default: + policy_mgr_err("unexpected mode %d", mode); + break; + } + + return count; +} + +/** + * policy_mgr_dump_current_concurrency_three_connection() - To dump the + * current concurrency info with three connections + * @cc_mode: connection string + * @length: Maximum size of the string + * + * This routine is called to dump the concurrency info + * + * Return: length of the string + */ +static uint32_t policy_mgr_dump_current_concurrency_three_connection( + char *cc_mode, uint32_t length) +{ + uint32_t count = 0; + enum policy_mgr_con_mode mode; + + mode = pm_conc_connection_list[2].mode; + + switch (mode) { + case PM_STA_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+STA", + length); + break; + case PM_SAP_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+SAP", + length); + break; + case PM_P2P_CLIENT_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+P2P CLI", + length); + break; + case PM_P2P_GO_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+P2P GO", + length); + break; + case PM_IBSS_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+IBSS", + length); + break; + case PM_NAN_DISC_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+NAN Disc", + length); + break; + case PM_NDI_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+NDI", + length); + break; + default: + policy_mgr_err("unexpected mode %d", mode); + break; + } + + return count; +} + +/** + * policy_mgr_dump_dbs_concurrency() - To dump the dbs concurrency + * combination + * @cc_mode: connection string + * + * This routine is called to dump the concurrency info + * + * Return: None + */ +static void policy_mgr_dump_dbs_concurrency(struct wlan_objmgr_psoc *psoc, + char *cc_mode, uint32_t length) +{ + char buf[4] = {0}; + uint8_t mac = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + strlcat(cc_mode, " DBS", length); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) + strlcat(cc_mode, + " with SCC for 1st two connections on mac ", + length); + else + strlcat(cc_mode, + " with MCC for 1st two connections on mac ", + length); + mac = pm_conc_connection_list[0].mac; + } + if (pm_conc_connection_list[0].mac == pm_conc_connection_list[2].mac) { + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[2].freq) + strlcat(cc_mode, + " with SCC for 1st & 3rd connections on mac ", + length); + else + strlcat(cc_mode, + " with MCC for 1st & 3rd connections on mac ", + length); + mac = pm_conc_connection_list[0].mac; + } + if (pm_conc_connection_list[1].mac == pm_conc_connection_list[2].mac) { + if (pm_conc_connection_list[1].freq == + pm_conc_connection_list[2].freq) + strlcat(cc_mode, + " with SCC for 2nd & 3rd connections on mac ", + length); + else + strlcat(cc_mode, + " with MCC for 2nd & 3rd connections on mac ", + length); + mac = pm_conc_connection_list[1].mac; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + snprintf(buf, sizeof(buf), "%d ", mac); + strlcat(cc_mode, buf, length); +} + +/** + * policy_mgr_dump_current_concurrency() - To dump the current + * concurrency combination + * + * This routine is called to dump the concurrency info + * + * Return: None + */ +void policy_mgr_dump_current_concurrency(struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections = 0; + char cc_mode[POLICY_MGR_MAX_CON_STRING_LEN] = {0}; + uint32_t count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + num_connections = policy_mgr_get_connection_count(psoc); + + switch (num_connections) { + case 1: + policy_mgr_dump_current_concurrency_one_connection(cc_mode, + sizeof(cc_mode)); + policy_mgr_debug("%s Standalone", cc_mode); + break; + case 2: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, sizeof(cc_mode)); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + strlcat(cc_mode, " SCC", sizeof(cc_mode)); + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + strlcat(cc_mode, " MCC", sizeof(cc_mode)); + } else + strlcat(cc_mode, " DBS", sizeof(cc_mode)); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("%s", cc_mode); + break; + case 3: + count = policy_mgr_dump_current_concurrency_three_connection( + cc_mode, sizeof(cc_mode)); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq && + pm_conc_connection_list[0].freq == + pm_conc_connection_list[2].freq){ + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + strlcat(cc_mode, " SCC", + sizeof(cc_mode)); + } else if ((pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) + && (pm_conc_connection_list[0].mac == + pm_conc_connection_list[2].mac)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + strlcat(cc_mode, " MCC on single MAC", + sizeof(cc_mode)); + } else { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_dump_dbs_concurrency(psoc, cc_mode, + sizeof(cc_mode)); + } + policy_mgr_debug("%s", cc_mode); + break; + default: + policy_mgr_debug("unexpected num_connections value %d", + num_connections); + break; + } + + return; +} + +QDF_STATUS policy_mgr_pdev_get_pcl(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, + struct policy_mgr_pcl_list *pcl) +{ + QDF_STATUS status; + enum policy_mgr_con_mode con_mode; + + pcl->pcl_len = 0; + + switch (mode) { + case QDF_STA_MODE: + con_mode = PM_STA_MODE; + break; + case QDF_P2P_CLIENT_MODE: + con_mode = PM_P2P_CLIENT_MODE; + break; + case QDF_P2P_GO_MODE: + con_mode = PM_P2P_GO_MODE; + break; + case QDF_SAP_MODE: + con_mode = PM_SAP_MODE; + break; + case QDF_IBSS_MODE: + con_mode = PM_IBSS_MODE; + break; + default: + policy_mgr_err("Unable to set PCL to FW: %d", mode); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_debug("get pcl to set it to the FW"); + + status = policy_mgr_get_pcl(psoc, con_mode, + pcl->pcl_list, &pcl->pcl_len, + pcl->weight_list, + QDF_ARRAY_SIZE(pcl->weight_list)); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Unable to set PCL to FW, Get PCL failed"); + + return status; +} + +/** + * policy_mgr_set_pcl_for_existing_combo() - Set PCL for existing connection + * @mode: Connection mode of type 'policy_mgr_con_mode' + * + * Set the PCL for an existing connection + * + * Return: None + */ +void policy_mgr_set_pcl_for_existing_combo( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct policy_mgr_conc_connection_info + info[MAX_NUMBER_OF_CONC_CONNECTIONS] = { {0} }; + enum QDF_OPMODE pcl_mode; + uint8_t num_cxn_del = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct policy_mgr_pcl_list pcl; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pcl_mode = policy_mgr_get_qdf_mode_from_pm(mode); + if (pcl_mode == QDF_MAX_NO_OF_MODE) + return; + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (policy_mgr_mode_specific_connection_count(psoc, mode, NULL) > 0) { + /* Check, store and temp delete the mode's parameter */ + policy_mgr_store_and_del_conn_info(psoc, mode, false, + info, &num_cxn_del); + /* Set the PCL to the FW since connection got updated */ + status = policy_mgr_pdev_get_pcl(psoc, pcl_mode, &pcl); + policy_mgr_debug("Set PCL to FW for mode:%d", mode); + /* Restore the connection info */ + policy_mgr_restore_deleted_conn_info(psoc, info, num_cxn_del); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + /* Send PCL only if policy_mgr_pdev_get_pcl returned success */ + if (QDF_IS_STATUS_SUCCESS(status)) { + status = pm_ctx->sme_cbacks.sme_pdev_set_pcl(&pcl); + if (QDF_IS_STATUS_ERROR(status)) + policy_mgr_err("Send set PCL to SME failed"); + } +} + +static uint32_t pm_get_vdev_id_of_first_conn_idx(struct wlan_objmgr_psoc *psoc) +{ + uint32_t conn_index = 0, vdev_id = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return conn_index; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].in_use) { + vdev_id = pm_conc_connection_list[conn_index].vdev_id; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + if (conn_index == MAX_NUMBER_OF_CONC_CONNECTIONS) + policy_mgr_debug("Use default vdev_id:%d for opportunistic upgrade", + vdev_id); + else + policy_mgr_debug("Use vdev_id:%d for opportunistic upgrade", + vdev_id); + + return vdev_id; +} + +/** + * pm_dbs_opportunistic_timer_handler() - handler of + * dbs_opportunistic_timer + * @data: context + * + * handler for dbs_opportunistic_timer + * + * Return: None + */ +void pm_dbs_opportunistic_timer_handler(void *data) +{ + enum policy_mgr_conc_next_action action = PM_NOP; + uint32_t session_id; + struct wlan_objmgr_psoc *psoc = (struct wlan_objmgr_psoc *)data; + enum policy_mgr_conn_update_reason reason; + struct policy_mgr_psoc_priv_obj *pm_ctx = policy_mgr_get_context(psoc); + + if (!psoc) { + policy_mgr_err("Invalid Context"); + return; + } + + /* if we still need it */ + action = policy_mgr_need_opportunistic_upgrade(psoc, &reason); + policy_mgr_debug("action:%d", action); + if (!action) { + return; + } else if (pm_ctx->hdd_cbacks.hdd_is_cac_in_progress && + pm_ctx->hdd_cbacks.hdd_is_cac_in_progress()) { + policy_mgr_debug("SAP is in CAC_IN_PROGRESS state, restarting"); + policy_mgr_restart_opportunistic_timer(psoc, false); + return; + } + session_id = pm_get_vdev_id_of_first_conn_idx(psoc); + policy_mgr_next_actions(psoc, session_id, action, + reason); +} + +/** + * policy_mgr_get_connection_for_vdev_id() - provides the + * perticular connection with the requested vdev id + * @vdev_id: vdev id of the connection + * + * This function provides the specific connection with the + * requested vdev id + * + * Return: index in the connection table + */ +static uint32_t policy_mgr_get_connection_for_vdev_id( + struct wlan_objmgr_psoc *psoc, uint32_t vdev_id) +{ + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return conn_index; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].vdev_id == vdev_id) && + pm_conc_connection_list[conn_index].in_use) { + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return conn_index; +} + +/** + * policy_mgr_get_mode() - Get mode from type and subtype + * @type: type + * @subtype: subtype + * + * Get the concurrency mode from the type and subtype + * of the interface + * + * Return: policy_mgr_con_mode + */ +enum policy_mgr_con_mode policy_mgr_get_mode(uint8_t type, + uint8_t subtype) +{ + enum policy_mgr_con_mode mode = PM_MAX_NUM_OF_MODE; + + if (type == WMI_VDEV_TYPE_AP) { + switch (subtype) { + case 0: + mode = PM_SAP_MODE; + break; + case WMI_UNIFIED_VDEV_SUBTYPE_P2P_GO: + mode = PM_P2P_GO_MODE; + break; + default: + policy_mgr_err("Unknown subtype %d for type %d", + subtype, type); + break; + } + } else if (type == WMI_VDEV_TYPE_STA) { + switch (subtype) { + case 0: + mode = PM_STA_MODE; + break; + case WMI_UNIFIED_VDEV_SUBTYPE_P2P_CLIENT: + mode = PM_P2P_CLIENT_MODE; + break; + default: + policy_mgr_err("Unknown subtype %d for type %d", + subtype, type); + break; + } + } else if (type == WMI_VDEV_TYPE_IBSS) { + mode = PM_IBSS_MODE; + } else if (type == WMI_VDEV_TYPE_NAN) { + mode = PM_NAN_DISC_MODE; + } else if (type == WMI_VDEV_TYPE_NDI) { + mode = PM_NDI_MODE; + } else { + policy_mgr_err("Unknown type %d", type); + } + + return mode; +} + +/** + * policy_mgr_get_bw() - Get channel bandwidth type used by WMI + * @chan_width: channel bandwidth type defined by host + * + * Get the channel bandwidth type used by WMI + * + * Return: hw_mode_bandwidth + */ +enum hw_mode_bandwidth policy_mgr_get_bw(enum phy_ch_width chan_width) +{ + enum hw_mode_bandwidth bw = HW_MODE_BW_NONE; + + switch (chan_width) { + case CH_WIDTH_20MHZ: + bw = HW_MODE_20_MHZ; + break; + case CH_WIDTH_40MHZ: + bw = HW_MODE_40_MHZ; + break; + case CH_WIDTH_80MHZ: + bw = HW_MODE_80_MHZ; + break; + case CH_WIDTH_160MHZ: + bw = HW_MODE_160_MHZ; + break; + case CH_WIDTH_80P80MHZ: + bw = HW_MODE_80_PLUS_80_MHZ; + break; + case CH_WIDTH_5MHZ: + bw = HW_MODE_5_MHZ; + break; + case CH_WIDTH_10MHZ: + bw = HW_MODE_10_MHZ; + break; + default: + policy_mgr_err("Unknown channel BW type %d", chan_width); + break; + } + + return bw; +} + +enum phy_ch_width policy_mgr_get_ch_width(enum hw_mode_bandwidth bw) +{ + enum phy_ch_width ch_width = CH_WIDTH_INVALID; + + switch (bw) { + case HW_MODE_20_MHZ: + ch_width = CH_WIDTH_20MHZ; + break; + case HW_MODE_40_MHZ: + ch_width = CH_WIDTH_40MHZ; + break; + case HW_MODE_80_MHZ: + ch_width = CH_WIDTH_80MHZ; + break; + case HW_MODE_160_MHZ: + ch_width = CH_WIDTH_160MHZ; + break; + case HW_MODE_80_PLUS_80_MHZ: + ch_width = CH_WIDTH_80P80MHZ; + break; + case HW_MODE_5_MHZ: + ch_width = CH_WIDTH_5MHZ; + break; + case HW_MODE_10_MHZ: + ch_width = CH_WIDTH_10MHZ; + break; + default: + policy_mgr_err("Invalid phy_ch_width type %d", ch_width); + break; + } + + return ch_width; +} + +/** + * policy_mgr_get_sbs_channels() - provides the sbs channel(s) + * with respect to current connection(s) + * @channels: the channel(s) on which current connection(s) is + * @len: Number of channels + * @pcl_weight: Pointer to the weights of PCL + * @weight_len: Max length of the weight list + * @index: Index from which the weight list needs to be populated + * @group_id: Next available groups for weight assignment + * @available_5g_channels: List of available 5g channels + * @available_5g_channels_len: Length of the 5g channels list + * @add_5g_channels: If this flag is true append 5G channel list as well + * + * This function provides the channel(s) on which current + * connection(s) is/are + * + * Return: QDF_STATUS + */ + +static QDF_STATUS policy_mgr_get_sbs_channels( + uint32_t *ch_freq_list, + uint32_t *len, uint8_t *pcl_weight, uint32_t weight_len, + uint32_t *index, enum policy_mgr_pcl_group_id group_id, + uint32_t *available_5g_ch_freqs, + uint32_t available_5g_channels_len, + bool add_5g_channels) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t conn_index = 0, num_channels = 0; + uint32_t num_5g_channels = 0, cur_5g_ch_freq = 0; + uint32_t remaining_5g_ch_freqs[NUM_CHANNELS] = {}; + uint32_t remaining_channel_index = 0; + uint32_t j = 0, i = 0, weight1, weight2; + + if (!ch_freq_list || !len) { + policy_mgr_err("channels or len is NULL"); + status = QDF_STATUS_E_FAILURE; + return status; + } + + if (group_id == POLICY_MGR_PCL_GROUP_ID1_ID2) { + weight1 = WEIGHT_OF_GROUP1_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + } else if (group_id == POLICY_MGR_PCL_GROUP_ID2_ID3) { + weight1 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP3_PCL_CHANNELS; + } else { + weight1 = WEIGHT_OF_GROUP3_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP4_PCL_CHANNELS; + } + + policy_mgr_debug("weight1=%d weight2=%d index=%d ", + weight1, weight2, *index); + + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[conn_index].freq) && + pm_conc_connection_list[conn_index].in_use) { + num_5g_channels++; + cur_5g_ch_freq = + pm_conc_connection_list[conn_index].freq; + } + conn_index++; + } + + conn_index = 0; + if (num_5g_channels > 1) { + /* This case we are already in SBS so return the channels */ + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + ch_freq_list[num_channels++] = + pm_conc_connection_list[conn_index++].freq; + if (*index < weight_len) + pcl_weight[(*index)++] = weight1; + } + *len = num_channels; + /* fix duplicate issue later */ + if (add_5g_channels) + for (j = 0; j < available_5g_channels_len; j++) + remaining_5g_ch_freqs[ + remaining_channel_index++] = + available_5g_ch_freqs[j]; + } else { + /* Get list of valid sbs channels for the current + * connected channel + */ + for (j = 0; j < available_5g_channels_len; j++) { + if (WLAN_REG_IS_FREQUENCY_VALID_5G_SBS( + cur_5g_ch_freq, available_5g_ch_freqs[j])) { + ch_freq_list[num_channels++] = + available_5g_ch_freqs[j]; + } else { + remaining_5g_ch_freqs[ + remaining_channel_index++] = + available_5g_ch_freqs[j]; + continue; + } + if (*index < weight_len) + pcl_weight[(*index)++] = weight1; + } + *len = num_channels; + } + + if (add_5g_channels) { + qdf_mem_copy(ch_freq_list + num_channels, remaining_5g_ch_freqs, + remaining_channel_index * sizeof(*ch_freq_list)); + *len += remaining_channel_index; + for (i = 0; ((i < remaining_channel_index) + && (i < weight_len)); i++) + pcl_weight[i] = weight2; + } + + return status; +} + +/** + * policy_mgr_get_connection_channels() - provides the channel(s) + * on which current connection(s) is + * @psoc: psoc object + * @mode: conn mode + * @ch_freq_list: the channel(s) on which current connection(s) is + * @len: Number of channels + * @order: no order OR 2.4 Ghz channel followed by 5 Ghz + * channel OR 5 Ghz channel followed by 2.4 Ghz channel + * @skip_dfs_channel: if this flag is true then skip the dfs channel + * @pcl_weight: Pointer to the weights of PCL + * @weight_len: Max length of the weight list + * @index: Index from which the weight list needs to be populated + * @group_id: Next available groups for weight assignment + * + * + * This function provides the channel(s) on which current + * connection(s) is/are + * + * Return: QDF_STATUS + */ +static +QDF_STATUS policy_mgr_get_connection_channels(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t *ch_freq_list, + uint32_t *len, enum policy_mgr_pcl_channel_order order, + bool skip_dfs_channel, + uint8_t *pcl_weight, uint32_t weight_len, + uint32_t *index, enum policy_mgr_pcl_group_id group_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t conn_index = 0, num_channels = 0; + uint32_t weight1, weight2; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool add_6ghz = true; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + if (!ch_freq_list || !len) { + policy_mgr_err("channels or len is NULL"); + status = QDF_STATUS_E_FAILURE; + return status; + } + + /* POLICY_MGR_PCL_GROUP_ID1_ID2 indicates that all three weights are + * available for assignment. i.e., WEIGHT_OF_GROUP1_PCL_CHANNELS, + * WEIGHT_OF_GROUP2_PCL_CHANNELS and WEIGHT_OF_GROUP3_PCL_CHANNELS + * are all available. Since in this function only two weights are + * assigned at max, only group1 and group2 weights are considered. + * + * The other possible group id POLICY_MGR_PCL_GROUP_ID2_ID3 indicates + * group1 was assigned the weight WEIGHT_OF_GROUP1_PCL_CHANNELS and + * only weights WEIGHT_OF_GROUP2_PCL_CHANNELS and + * WEIGHT_OF_GROUP3_PCL_CHANNELS are available for further weight + * assignments. + * + * e.g., when order is POLICY_MGR_PCL_ORDER_24G_THEN_5G and group id is + * POLICY_MGR_PCL_GROUP_ID2_ID3, WEIGHT_OF_GROUP2_PCL_CHANNELS is + * assigned to 2.4GHz channels and the weight + * WEIGHT_OF_GROUP3_PCL_CHANNELS is assigned to the 5GHz channels. + */ + if (group_id == POLICY_MGR_PCL_GROUP_ID1_ID2) { + weight1 = WEIGHT_OF_GROUP1_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + } else if (group_id == POLICY_MGR_PCL_GROUP_ID2_ID3) { + weight1 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP3_PCL_CHANNELS; + } else { + weight1 = WEIGHT_OF_GROUP3_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP4_PCL_CHANNELS; + } + if (!policy_mgr_is_6ghz_conc_mode_supported(psoc, mode)) + add_6ghz = false; + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (POLICY_MGR_PCL_ORDER_NONE == order) { + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + bool is_6ghz_ch = WLAN_REG_IS_6GHZ_CHAN_FREQ( + pm_conc_connection_list[conn_index].freq); + if (skip_dfs_channel && wlan_reg_is_dfs_for_freq( + pm_ctx->pdev, + pm_conc_connection_list[conn_index].freq)) { + conn_index++; + } else if ((*index < weight_len) && + (!is_6ghz_ch || add_6ghz)) { + ch_freq_list[num_channels++] = + pm_conc_connection_list[ + conn_index++].freq; + pcl_weight[(*index)++] = weight1; + } else { + conn_index++; + } + } + *len = num_channels; + } else if (POLICY_MGR_PCL_ORDER_24G_THEN_5G == order) { + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[conn_index].freq) + && (*index < weight_len)) { + ch_freq_list[num_channels++] = + pm_conc_connection_list[ + conn_index++].freq; + pcl_weight[(*index)++] = weight1; + } else { + conn_index++; + } + } + conn_index = 0; + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (skip_dfs_channel && + wlan_reg_is_dfs_for_freq( + pm_ctx->pdev, + pm_conc_connection_list[conn_index].freq)) { + conn_index++; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[conn_index].freq) && + (*index < weight_len)) { + ch_freq_list[num_channels++] = + pm_conc_connection_list[ + conn_index++].freq; + pcl_weight[(*index)++] = weight2; + } else { + conn_index++; + } + } + conn_index = 0; + while (add_6ghz && + PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + bool is_6ghz_ch = WLAN_REG_IS_6GHZ_CHAN_FREQ( + pm_conc_connection_list[conn_index].freq); + if (is_6ghz_ch && (*index < weight_len)) { + ch_freq_list[num_channels++] = + pm_conc_connection_list[ + conn_index++].freq; + pcl_weight[(*index)++] = weight2; + } else { + conn_index++; + } + } + *len = num_channels; + } else if (POLICY_MGR_PCL_ORDER_5G_THEN_2G == order) { + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (skip_dfs_channel && + wlan_reg_is_dfs_for_freq( + pm_ctx->pdev, + pm_conc_connection_list[conn_index].freq)) { + conn_index++; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[conn_index].freq) && + (*index < weight_len)) { + ch_freq_list[num_channels++] = + pm_conc_connection_list[ + conn_index++].freq; + pcl_weight[(*index)++] = weight1; + } else { + conn_index++; + } + } + conn_index = 0; + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[conn_index].freq) && + (*index < weight_len)) { + ch_freq_list[num_channels++] = + pm_conc_connection_list[ + conn_index++].freq; + pcl_weight[(*index)++] = weight2; + + } else { + conn_index++; + } + } + conn_index = 0; + while (add_6ghz && + PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + bool is_6ghz_ch = WLAN_REG_IS_6GHZ_CHAN_FREQ( + pm_conc_connection_list[conn_index].freq); + if (is_6ghz_ch && (*index < weight_len)) { + ch_freq_list[num_channels++] = + pm_conc_connection_list[ + conn_index++].freq; + pcl_weight[(*index)++] = weight2; + } else { + conn_index++; + } + } + *len = num_channels; + } else { + policy_mgr_err("unknown order %d", order); + status = QDF_STATUS_E_FAILURE; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +/** + * policy_mgr_set_weight_of_dfs_passive_channels_to_zero() - set weight of dfs + * and passive channels to 0 + * @psoc: pointer to soc + * @pcl_channels: preferred channel list + * @len: length of preferred channel list + * @weight_list: preferred channel weight list + * @weight_len: length of weight list + * This function set the weight of dfs and passive channels to 0 + * + * Return: None + */ +void policy_mgr_set_weight_of_dfs_passive_channels_to_zero( + struct wlan_objmgr_psoc *psoc, uint32_t *pcl_channels, + uint32_t *len, uint8_t *weight_list, uint32_t weight_len) +{ + uint8_t i; + uint32_t orig_channel_count = 0; + bool sta_sap_scc_on_dfs_chan; + uint32_t sap_count; + enum channel_state channel_state; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + sap_count = policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, NULL); + policy_mgr_debug("sta_sap_scc_on_dfs_chan %u, sap_count %u", + sta_sap_scc_on_dfs_chan, sap_count); + + if (!sta_sap_scc_on_dfs_chan || !sap_count) + return; + + if (len) + orig_channel_count = QDF_MIN(*len, NUM_CHANNELS); + else { + policy_mgr_err("invalid number of channel length"); + return; + } + + policy_mgr_debug("Set weight of DFS/passive channels to 0"); + + for (i = 0; i < orig_channel_count; i++) { + channel_state = wlan_reg_get_channel_state_for_freq( + pm_ctx->pdev, pcl_channels[i]); + if ((channel_state == CHANNEL_STATE_DISABLE) || + (channel_state == CHANNEL_STATE_INVALID)) + /* Set weight of inactive channels to 0 */ + weight_list[i] = 0; + } + + policy_mgr_dump_channel_list(orig_channel_count, + pcl_channels, weight_list); + + return; +} + +static void policy_mgr_add_5g_to_pcl( + struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq_list, + uint32_t *len, + uint8_t *pcl_weight, uint32_t weight_len, + uint32_t *index, enum policy_mgr_pcl_group_id group_id, + const uint32_t *chlist_5g, uint8_t chlist_5g_len, + const uint32_t *chlist_6g, uint8_t chlist_6g_len) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t weight1, weight2; + const uint32_t *chlist1; + uint8_t chlist1_len; + const uint32_t *chlist2; + uint8_t chlist2_len; + uint32_t i; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (group_id == POLICY_MGR_PCL_GROUP_ID1_ID2) { + weight1 = WEIGHT_OF_GROUP1_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + } else if (group_id == POLICY_MGR_PCL_GROUP_ID2_ID3) { + weight1 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP3_PCL_CHANNELS; + } else { + weight1 = WEIGHT_OF_GROUP3_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP4_PCL_CHANNELS; + } + if (pm_ctx->cfg.pcl_band_priority == POLICY_MGR_PCL_BAND_6G_THEN_5G) { + chlist1 = chlist_6g; + chlist1_len = chlist_6g_len; + chlist2 = chlist_5g; + chlist2_len = chlist_5g_len; + } else { + chlist1 = chlist_5g; + chlist1_len = chlist_5g_len; + chlist2 = chlist_6g; + chlist2_len = chlist_6g_len; + } + if ((chlist1_len + *index) > weight_len) { + policy_mgr_err("no enough weight len %d chlist1_len %d %d", + weight_len, chlist1_len, *index); + return; + } + qdf_mem_copy(ch_freq_list, chlist1, chlist1_len * sizeof(*chlist1)); + for (i = 0; i < chlist1_len; i++) + pcl_weight[(*index)++] = weight1; + + *len += chlist1_len; + + if ((chlist2_len + *index) > weight_len) { + policy_mgr_err("no enough weight len chlist2_len %d %d %d", + weight_len, chlist2_len, *index); + return; + } + qdf_mem_copy(&ch_freq_list[chlist1_len], chlist2, + chlist2_len * sizeof(*chlist2)); + for (i = 0; i < chlist2_len; i++) + pcl_weight[(*index)++] = weight2; + *len += chlist2_len; + + policy_mgr_debug("Add 5g chlist len %d 6g chlist len %d len %d index %d order %d", + chlist_5g_len, chlist_6g_len, *len, *index, + pm_ctx->cfg.pcl_band_priority); +} + +/** + * policy_mgr_get_channel_list() - provides the channel list + * suggestion for new connection + * @pcl: The preferred channel list enum + * @pcl_channels: PCL channels + * @len: length of the PCL + * @mode: concurrency mode for which channel list is requested + * @pcl_weights: Weights of the PCL + * @weight_len: Max length of the weight list + * + * This function provides the actual channel list based on the + * current regulatory domain derived using preferred channel + * list enum obtained from one of the pcl_table + * + * Return: Channel List + */ +QDF_STATUS policy_mgr_get_channel_list(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_pcl_type pcl, + uint32_t *pcl_channels, uint32_t *len, + enum policy_mgr_con_mode mode, + uint8_t *pcl_weights, uint32_t weight_len) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t num_channels = 0; + uint32_t sbs_num_channels = 0; + uint32_t chan_index_24 = 0, chan_index_5 = 0, chan_index_6 = 0; + bool skip_dfs_channel = false; + uint32_t i = 0, j = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool sta_sap_scc_on_dfs_chan; + uint32_t *channel_list, *channel_list_24, *channel_list_5, + *sbs_channel_list, *channel_list_6; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + if ((!pcl_channels) || (!len)) { + policy_mgr_err("pcl_channels or len is NULL"); + return status; + } + + *len = 0; + if (PM_MAX_PCL_TYPE == pcl) { + /* msg */ + policy_mgr_err("pcl is invalid"); + return status; + } + + if (PM_NONE == pcl) { + /* msg */ + policy_mgr_debug("pcl is 0"); + return QDF_STATUS_SUCCESS; + } + + channel_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(uint32_t)); + channel_list_24 = qdf_mem_malloc(NUM_CHANNELS * sizeof(uint32_t)); + channel_list_5 = qdf_mem_malloc(NUM_CHANNELS * sizeof(uint32_t)); + sbs_channel_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(uint32_t)); + channel_list_6 = qdf_mem_malloc(NUM_CHANNELS * sizeof(uint32_t)); + if (!channel_list || !channel_list_24 || !channel_list_5 || + !sbs_channel_list || !channel_list_6) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + + /* get the channel list for current domain */ + status = policy_mgr_get_valid_chans(psoc, channel_list, + &num_channels); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Error in getting valid channels"); + goto end; + } + + /* + * if you have atleast one STA connection then don't fill DFS channels + * in the preferred channel list + */ + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + if ((mode == PM_SAP_MODE) || (mode == PM_P2P_GO_MODE)) { + if ((policy_mgr_mode_specific_connection_count(psoc, + PM_STA_MODE, + NULL) > 0) && + (!sta_sap_scc_on_dfs_chan)) { + policy_mgr_debug("skip DFS ch from pcl for SAP/Go"); + skip_dfs_channel = true; + } + } + + /* Let's divide the list in 2.4 & 5 Ghz lists */ + for (i = 0; i < num_channels; i++) { + if (wlan_reg_is_24ghz_ch_freq(channel_list[i])) { + channel_list_24[chan_index_24++] = channel_list[i]; + } else if (wlan_reg_is_5ghz_ch_freq(channel_list[i])) { + if ((true == skip_dfs_channel) && + wlan_reg_is_dfs_for_freq(pm_ctx->pdev, + channel_list[i])) + continue; + + channel_list_5[chan_index_5++] = channel_list[i]; + } else if (wlan_reg_is_6ghz_chan_freq(channel_list[i])) { + /* Add to 5G list untill 6G conc support is enabled */ + channel_list_6[chan_index_6++] = channel_list[i]; + } + } + if (!policy_mgr_is_6ghz_conc_mode_supported(psoc, mode)) + chan_index_6 = 0; + num_channels = 0; + sbs_num_channels = 0; + i = 0; + /* In the below switch case, the channel list is populated based on the + * pcl. e.g., if the pcl is PM_SCC_CH_24G, the SCC channel group is + * populated first followed by the 2.4GHz channel group. Along with + * this, the weights are also populated in the same order for each of + * these groups. There are three weight groups: + * WEIGHT_OF_GROUP1_PCL_CHANNELS, WEIGHT_OF_GROUP2_PCL_CHANNELS and + * WEIGHT_OF_GROUP3_PCL_CHANNELS. + * + * e.g., if pcl is PM_SCC_ON_5_SCC_ON_24_24G: scc on 5GHz (group1) + * channels take the weight WEIGHT_OF_GROUP1_PCL_CHANNELS, scc on 2.4GHz + * (group2) channels take the weight WEIGHT_OF_GROUP2_PCL_CHANNELS and + * 2.4GHz (group3) channels take the weight + * WEIGHT_OF_GROUP3_PCL_CHANNELS. + * + * When the weight to be assigned to the group is known along with the + * number of channels, the weights are directly assigned to the + * pcl_weights list. But, the channel list is populated using + * policy_mgr_get_connection_channels(), the order of weights to be used + * is passed as an argument to the function + * policy_mgr_get_connection_channels() using + * 'enum policy_mgr_pcl_group_id' which indicates the next available + * weights to be used and policy_mgr_get_connection_channels() will take + * care of the weight assignments. + * + * e.g., 'enum policy_mgr_pcl_group_id' value of + * POLICY_MGR_PCL_GROUP_ID2_ID3 indicates that the next available groups + * for weight assignment are WEIGHT_OF_GROUP2_PCL_CHANNELS and + * WEIGHT_OF_GROUP3_PCL_CHANNELS and that the + * weight WEIGHT_OF_GROUP1_PCL_CHANNELS was already allocated. + * So, in the same example, when order is + * POLICY_MGR_PCL_ORDER_24G_THEN_5G, + * policy_mgr_get_connection_channels() will assign the weight + * WEIGHT_OF_GROUP2_PCL_CHANNELS to 2.4GHz channels and assign the + * weight WEIGHT_OF_GROUP3_PCL_CHANNELS to 5GHz channels. + */ + switch (pcl) { + case PM_24G: + chan_index_24 = QDF_MIN(chan_index_24, weight_len); + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24 * sizeof(*pcl_channels)); + *len = chan_index_24; + for (i = 0; i < *len; i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_5G: + policy_mgr_add_5g_to_pcl( + psoc, pcl_channels, len, + pcl_weights, weight_len, + &i, + POLICY_MGR_PCL_GROUP_ID1_ID2, + channel_list_5, chan_index_5, + channel_list_6, chan_index_6); + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_CH: + case PM_MCC_CH: + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels * sizeof(*pcl_channels)); + *len = num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_CH_24G: + case PM_MCC_CH_24G: + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels * sizeof(*pcl_channels)); + *len = num_channels; + chan_index_24 = QDF_MIN((num_channels + chan_index_24), + weight_len) - num_channels; + qdf_mem_copy(&pcl_channels[num_channels], channel_list_24, + chan_index_24 * sizeof(*pcl_channels)); + *len += chan_index_24; + for (j = 0; j < chan_index_24; i++, j++) + pcl_weights[i] = WEIGHT_OF_GROUP2_PCL_CHANNELS; + + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_CH_5G: + case PM_MCC_CH_5G: + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels * sizeof(*pcl_channels)); + *len = num_channels; + policy_mgr_add_5g_to_pcl( + psoc, &pcl_channels[num_channels], len, + pcl_weights, weight_len, + &i, + POLICY_MGR_PCL_GROUP_ID2_ID3, + channel_list_5, chan_index_5, + channel_list_6, chan_index_6); + status = QDF_STATUS_SUCCESS; + break; + case PM_24G_SCC_CH: + case PM_24G_MCC_CH: + chan_index_24 = QDF_MIN(chan_index_24, weight_len); + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24 * sizeof(*pcl_channels)); + *len = chan_index_24; + for (i = 0; i < chan_index_24; i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID2_ID3); + qdf_mem_copy(&pcl_channels[chan_index_24], channel_list, + num_channels * sizeof(*pcl_channels)); + *len += num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_5G_SCC_CH: + case PM_5G_MCC_CH: + policy_mgr_add_5g_to_pcl( + psoc, pcl_channels, len, + pcl_weights, weight_len, + &i, + POLICY_MGR_PCL_GROUP_ID1_ID2, + channel_list_5, chan_index_5, + channel_list_6, chan_index_6); + + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID3_ID4); + qdf_mem_copy(&pcl_channels[*len], channel_list, + num_channels * sizeof(*pcl_channels)); + *len += num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_24_SCC_ON_5: + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_24G_THEN_5G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels * sizeof(*pcl_channels)); + *len = num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_5_SCC_ON_24: + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_5G_THEN_2G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels * sizeof(*pcl_channels)); + *len = num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_24_SCC_ON_5_24G: + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_24G_THEN_5G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels * sizeof(*pcl_channels)); + *len = num_channels; + chan_index_24 = QDF_MIN((num_channels + chan_index_24), + weight_len) - num_channels; + qdf_mem_copy(&pcl_channels[num_channels], channel_list_24, + chan_index_24 * sizeof(*pcl_channels)); + *len += chan_index_24; + for (j = 0; j < chan_index_24; i++, j++) + pcl_weights[i] = WEIGHT_OF_GROUP3_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_24_SCC_ON_5_5G: + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_24G_THEN_5G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels * sizeof(*pcl_channels)); + *len = num_channels; + policy_mgr_add_5g_to_pcl( + psoc, &pcl_channels[num_channels], len, + pcl_weights, weight_len, + &i, + POLICY_MGR_PCL_GROUP_ID3_ID4, + channel_list_5, chan_index_5, + channel_list_6, chan_index_6); + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_5_SCC_ON_24_24G: + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_5G_THEN_2G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels * sizeof(*pcl_channels)); + *len = num_channels; + chan_index_24 = QDF_MIN((num_channels + chan_index_24), + weight_len) - num_channels; + qdf_mem_copy(&pcl_channels[num_channels], channel_list_24, + chan_index_24 * sizeof(*pcl_channels)); + *len += chan_index_24; + for (j = 0; j < chan_index_24; i++, j++) + pcl_weights[i] = WEIGHT_OF_GROUP3_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_5_SCC_ON_24_5G: + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_5G_THEN_2G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels * sizeof(*pcl_channels)); + *len = num_channels; + policy_mgr_add_5g_to_pcl( + psoc, &pcl_channels[num_channels], len, + pcl_weights, weight_len, + &i, + POLICY_MGR_PCL_GROUP_ID3_ID4, + channel_list_5, chan_index_5, + channel_list_6, chan_index_6); + status = QDF_STATUS_SUCCESS; + break; + case PM_24G_SCC_CH_SBS_CH: + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24 * sizeof(*pcl_channels)); + *len = chan_index_24; + for (i = 0; ((i < chan_index_24) && (i < weight_len)); i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID2_ID3); + qdf_mem_copy(&pcl_channels[chan_index_24], channel_list, + num_channels * sizeof(*pcl_channels)); + *len += num_channels; + if (policy_mgr_is_hw_sbs_capable(psoc)) { + policy_mgr_get_sbs_channels( + sbs_channel_list, &sbs_num_channels, pcl_weights, + weight_len, &i, POLICY_MGR_PCL_GROUP_ID3_ID4, + channel_list_5, chan_index_5, false); + qdf_mem_copy( + &pcl_channels[chan_index_24 + num_channels], + sbs_channel_list, + sbs_num_channels * sizeof(*pcl_channels)); + *len += sbs_num_channels; + } + status = QDF_STATUS_SUCCESS; + break; + case PM_24G_SCC_CH_SBS_CH_5G: + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24 * sizeof(*pcl_channels)); + *len = chan_index_24; + for (i = 0; ((i < chan_index_24) && (i < weight_len)); i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID2_ID3); + qdf_mem_copy(&pcl_channels[chan_index_24], channel_list, + num_channels * sizeof(*pcl_channels)); + *len += num_channels; + if (policy_mgr_is_hw_sbs_capable(psoc)) { + policy_mgr_get_sbs_channels( + sbs_channel_list, &sbs_num_channels, pcl_weights, + weight_len, &i, POLICY_MGR_PCL_GROUP_ID3_ID4, + channel_list_5, chan_index_5, true); + qdf_mem_copy( + &pcl_channels[chan_index_24 + num_channels], + sbs_channel_list, + sbs_num_channels * sizeof(*pcl_channels)); + *len += sbs_num_channels; + } else { + policy_mgr_add_5g_to_pcl( + psoc, + &pcl_channels[chan_index_24 + num_channels], + len, + pcl_weights, weight_len, + &i, + POLICY_MGR_PCL_GROUP_ID3_ID4, + channel_list_5, chan_index_5, + channel_list_6, chan_index_6); + } + status = QDF_STATUS_SUCCESS; + break; + case PM_24G_SBS_CH_MCC_CH: + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24 * sizeof(*pcl_channels)); + *len = chan_index_24; + for (i = 0; ((i < chan_index_24) && (i < weight_len)); i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + if (policy_mgr_is_hw_sbs_capable(psoc)) { + policy_mgr_get_sbs_channels( + sbs_channel_list, &sbs_num_channels, pcl_weights, + weight_len, &i, POLICY_MGR_PCL_GROUP_ID2_ID3, + channel_list_5, chan_index_5, false); + qdf_mem_copy(&pcl_channels[num_channels], + sbs_channel_list, + sbs_num_channels * sizeof(*pcl_channels)); + *len += sbs_num_channels; + } + policy_mgr_get_connection_channels( + psoc, mode, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID2_ID3); + qdf_mem_copy(&pcl_channels[chan_index_24], channel_list, + num_channels * sizeof(*pcl_channels)); + *len += num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SBS_CH_5G: + if (policy_mgr_is_hw_sbs_capable(psoc)) { + policy_mgr_get_sbs_channels( + sbs_channel_list, &sbs_num_channels, pcl_weights, + weight_len, &i, POLICY_MGR_PCL_GROUP_ID1_ID2, + channel_list_5, chan_index_5, true); + qdf_mem_copy(&pcl_channels[num_channels], + sbs_channel_list, + sbs_num_channels * sizeof(*pcl_channels)); + *len += sbs_num_channels; + } else { + policy_mgr_add_5g_to_pcl( + psoc, + pcl_channels, + len, + pcl_weights, weight_len, + &i, + POLICY_MGR_PCL_GROUP_ID1_ID2, + channel_list_5, chan_index_5, + channel_list_6, chan_index_6); + } + status = QDF_STATUS_SUCCESS; + break; + default: + policy_mgr_err("unknown pcl value %d", pcl); + break; + } + + if ((*len != 0) && (*len != i)) + policy_mgr_debug("pcl len (%d) and weight list len mismatch (%d)", + *len, i); + + /* check the channel avoidance list for beaconing entities */ + if ((mode == PM_SAP_MODE) || (mode == PM_P2P_GO_MODE)) + policy_mgr_update_with_safe_channel_list(psoc, pcl_channels, + len, pcl_weights, + weight_len); + + policy_mgr_set_weight_of_dfs_passive_channels_to_zero(psoc, + pcl_channels, len, pcl_weights, weight_len); +end: + qdf_mem_free(channel_list); + qdf_mem_free(channel_list_24); + qdf_mem_free(channel_list_5); + qdf_mem_free(sbs_channel_list); + qdf_mem_free(channel_list_6); + + return status; +} + +bool policy_mgr_disallow_mcc(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq) +{ + uint32_t index = 0; + bool match = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return match; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(index)) { + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + if (pm_conc_connection_list[index].freq != ch_freq) { + match = true; + break; + } + } else if (WLAN_REG_IS_5GHZ_CH_FREQ + (pm_conc_connection_list[index].freq)) { + if (pm_conc_connection_list[index].freq != ch_freq) { + match = true; + break; + } + } + index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return match; +} + +/** + * policy_mgr_is_multi_ap_plus_sta_3vif_conc() - Check multiple AP plus STA + * concurrency + * @mode1: policy_mgr_con_mode of connection 1 + * @mode2: policy_mgr_con_mode of connection 2 + * @mode2: policy_mgr_con_mode of connection 3 + * + * Check the 3vif concurrency is SAP(GO)+SAP(GO)+STA or not based on + * connection mode. + * + * Return: True/False + */ +static bool policy_mgr_is_multi_ap_plus_sta_3vif_conc( + enum policy_mgr_con_mode mode1, enum policy_mgr_con_mode mode2, + enum policy_mgr_con_mode mode3) +{ + if (mode1 == PM_STA_MODE && + (mode2 == PM_SAP_MODE || mode2 == PM_P2P_GO_MODE) && + (mode3 == PM_SAP_MODE || mode3 == PM_P2P_GO_MODE)) + return true; + if (mode2 == PM_STA_MODE && + (mode1 == PM_SAP_MODE || mode1 == PM_P2P_GO_MODE) && + (mode3 == PM_SAP_MODE || mode3 == PM_P2P_GO_MODE)) + return true; + if (mode3 == PM_STA_MODE && + (mode1 == PM_SAP_MODE || mode1 == PM_P2P_GO_MODE) && + (mode2 == PM_SAP_MODE || mode2 == PM_P2P_GO_MODE)) + return true; + + return false; +} + +/** + * policy_mgr_allow_new_home_channel() - Check for allowed number of + * home channels + * @mode: policy_mgr_con_mode of new connection, + * @channel: channel on which new connection is coming up + * @num_connections: number of current connections + * @is_dfs_ch: DFS channel or not + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * allowed or not based on the HW capability + * + * Return: True/False + */ +bool policy_mgr_allow_new_home_channel( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode, + uint32_t ch_freq, uint32_t num_connections, bool is_dfs_ch) +{ + bool status = true; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t mcc_to_scc_switch; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + mcc_to_scc_switch = + policy_mgr_get_mcc_to_scc_switch_mode(psoc); + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (num_connections == 2) { + /* No SCC or MCC combination is allowed with / on DFS channel */ + if ((mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) && + is_dfs_ch && + ((pm_conc_connection_list[0].ch_flagext & + (IEEE80211_CHAN_DFS | IEEE80211_CHAN_DFS_CFREQ2)) || + (pm_conc_connection_list[1].ch_flagext & + (IEEE80211_CHAN_DFS | IEEE80211_CHAN_DFS_CFREQ2)))) { + policy_mgr_rl_debug("Existing DFS connection, new 3-port DFS connection is not allowed"); + status = false; + + } else if (((pm_conc_connection_list[0].freq != + pm_conc_connection_list[1].freq) + || (mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) + ) && (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac)) { + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + if (ch_freq != + pm_conc_connection_list[0].freq && + ch_freq != + pm_conc_connection_list[1].freq) { + policy_mgr_rl_debug("don't allow 3rd home channel on same MAC"); + status = false; + } + } else if ((pm_conc_connection_list[0].mode == + PM_NAN_DISC_MODE && + pm_conc_connection_list[1].mode == + PM_NDI_MODE) || + (pm_conc_connection_list[0].mode == + PM_NDI_MODE && + pm_conc_connection_list[1].mode == + PM_NAN_DISC_MODE)) { + /* + * NAN + NDI are managed in Firmware by dividing + * up slots. Connection on NDI is re-negotiable + * and therefore a 3rd connection with the + * same MAC is possible. + */ + status = true; + } else if (((WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) && + (WLAN_REG_IS_24GHZ_CH_FREQ + (pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_24GHZ_CH_FREQ + (pm_conc_connection_list[1].freq))) || + ((WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ + (pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ + (pm_conc_connection_list[1].freq)))) { + policy_mgr_rl_debug("don't allow 3rd home channel on same MAC"); + status = false; + } + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + /* Existing two connections are SCC */ + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + /* keep legacy chip "allow" as it is */ + policy_mgr_rl_debug("allow 2 intf SCC + new intf ch %d for legacy hw", + ch_freq); + } else if ((pm_conc_connection_list[0].mode == + PM_NAN_DISC_MODE && + pm_conc_connection_list[1].mode == + PM_NDI_MODE) || + (pm_conc_connection_list[0].mode == + PM_NDI_MODE && + pm_conc_connection_list[1].mode == + PM_NAN_DISC_MODE)) { + /* + * NAN + NDI are managed in Firmware by dividing + * up slots. Connection on NDI is re-negotiable + * and therefore a 3rd connection with the + * same MAC is possible. + */ + } else if (wlan_reg_is_same_band_channels( + ch_freq, pm_conc_connection_list[0].freq) && + policy_mgr_is_multi_ap_plus_sta_3vif_conc( + pm_conc_connection_list[0].mode, + pm_conc_connection_list[1].mode, + mode)) { + policy_mgr_rl_debug("don't allow 3rd home channel on same MAC - sta existing"); + status = false; + } + } + } else if ((num_connections == 1) && + (mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) && + is_dfs_ch && + (pm_conc_connection_list[0].ch_flagext & + (IEEE80211_CHAN_DFS | IEEE80211_CHAN_DFS_CFREQ2))) { + + policy_mgr_rl_debug("Existing DFS connection, new 2-port DFS connection is not allowed"); + status = false; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +bool policy_mgr_is_5g_channel_allowed(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq, uint32_t *list, + enum policy_mgr_con_mode mode) +{ + uint32_t index = 0, count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + count = policy_mgr_mode_specific_connection_count(psoc, mode, list); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (index < count) { + if ((pm_conc_connection_list[list[index]].ch_flagext & + (IEEE80211_CHAN_DFS | IEEE80211_CHAN_DFS_CFREQ2)) && + WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq) && + (ch_freq != pm_conc_connection_list[list[index]].freq)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_rl_debug("don't allow MCC if SAP/GO on DFS channel"); + return false; + } + index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return true; +} + +/** + * policy_mgr_nss_update_cb() - callback from SME confirming nss + * update + * @hdd_ctx: HDD Context + * @tx_status: tx completion status for updated beacon with new + * nss value + * @vdev_id: vdev id for the specific connection + * @next_action: next action to happen at policy mgr after + * beacon update + * @reason: Reason for nss update + * @original_vdev_id: original request hwmode change vdev id + * + * This function is the callback registered with SME at nss + * update request time + * + * Return: None + */ +static void policy_mgr_nss_update_cb(struct wlan_objmgr_psoc *psoc, + uint8_t tx_status, + uint8_t vdev_id, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id) +{ + uint32_t conn_index = 0; + QDF_STATUS ret; + + if (QDF_STATUS_SUCCESS != tx_status) + policy_mgr_err("nss update failed(%d) for vdev %d", + tx_status, vdev_id); + + /* + * Check if we are ok to request for HW mode change now + */ + conn_index = policy_mgr_get_connection_for_vdev_id(psoc, vdev_id); + if (MAX_NUMBER_OF_CONC_CONNECTIONS == conn_index) { + policy_mgr_err("connection not found for vdev %d", vdev_id); + return; + } + + policy_mgr_debug("nss update successful for vdev:%d ori %d reason %d", + vdev_id, original_vdev_id, reason); + if (PM_NOP != next_action) { + if (reason == POLICY_MGR_UPDATE_REASON_AFTER_CHANNEL_SWITCH) + policy_mgr_next_actions(psoc, vdev_id, next_action, + reason); + else + policy_mgr_next_actions(psoc, original_vdev_id, + next_action, reason); + } else { + policy_mgr_debug("No action needed right now"); + ret = policy_mgr_set_opportunistic_update(psoc); + if (!QDF_IS_STATUS_SUCCESS(ret)) + policy_mgr_err("ERROR: set opportunistic_update event failed"); + } + + return; +} + +QDF_STATUS policy_mgr_nss_update(struct wlan_objmgr_psoc *psoc, + uint8_t new_nss, uint8_t next_action, + enum policy_mgr_band band, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t index, count; + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t conn_index = 0; + uint32_t vdev_id; + uint32_t original_nss, ch_freq; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum phy_ch_width ch_width = CH_WIDTH_MAX; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + if (next_action == PM_DBS2 && band == POLICY_MGR_BAND_5) + ch_width = CH_WIDTH_40MHZ; + + count = policy_mgr_mode_specific_connection_count(psoc, + PM_P2P_GO_MODE, list); + for (index = 0; index < count; index++) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + vdev_id = pm_conc_connection_list[list[index]].vdev_id; + original_nss = + pm_conc_connection_list[list[index]].original_nss; + ch_freq = pm_conc_connection_list[list[index]].freq; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + conn_index = policy_mgr_get_connection_for_vdev_id( + psoc, vdev_id); + if (MAX_NUMBER_OF_CONC_CONNECTIONS == conn_index) { + policy_mgr_err("connection not found for vdev %d", + vdev_id); + continue; + } + + if (original_nss == 2 && + (band == POLICY_MGR_ANY || + (band == POLICY_MGR_BAND_24 && + WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) || + (band == POLICY_MGR_BAND_5 && + WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq)))) { + status = pm_ctx->sme_cbacks.sme_nss_update_request( + vdev_id, new_nss, ch_width, + policy_mgr_nss_update_cb, + next_action, psoc, reason, + original_vdev_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("sme_nss_update_request() failed for vdev %d", + vdev_id); + } + } + } + + count = policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, list); + for (index = 0; index < count; index++) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + vdev_id = pm_conc_connection_list[list[index]].vdev_id; + original_nss = + pm_conc_connection_list[list[index]].original_nss; + ch_freq = pm_conc_connection_list[list[index]].freq; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + conn_index = policy_mgr_get_connection_for_vdev_id( + psoc, vdev_id); + if (MAX_NUMBER_OF_CONC_CONNECTIONS == conn_index) { + policy_mgr_err("connection not found for vdev %d", + vdev_id); + continue; + } + if (original_nss == 2 && + (band == POLICY_MGR_ANY || + (band == POLICY_MGR_BAND_24 && + WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) || + (band == POLICY_MGR_BAND_5 && + WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq)))) { + status = pm_ctx->sme_cbacks.sme_nss_update_request( + vdev_id, new_nss, ch_width, + policy_mgr_nss_update_cb, + next_action, psoc, reason, + original_vdev_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("sme_nss_update_request() failed for vdev %d", + vdev_id); + } + } + } + + return status; +} + +/** + * policy_mgr_complete_action() - initiates actions needed on + * current connections once channel has been decided for the new + * connection + * @new_nss: the new nss value + * @next_action: next action to happen at policy mgr after + * beacon update + * @reason: Reason for connection update + * @session_id: Session id + * + * This function initiates initiates actions + * needed on current connections once channel has been decided + * for the new connection. Notifies UMAC & FW as well + * + * Return: QDF_STATUS enum + */ +QDF_STATUS policy_mgr_complete_action(struct wlan_objmgr_psoc *psoc, + uint8_t new_nss, uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + enum policy_mgr_band downgrade_band; + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + policy_mgr_rl_debug("driver isn't dbs capable, no further action needed"); + return QDF_STATUS_E_NOSUPPORT; + } + + /* policy_mgr_complete_action() is called by policy_mgr_next_actions(). + * All other callers of policy_mgr_next_actions() have taken mutex + * protection. So, not taking any lock inside + * policy_mgr_complete_action() during pm_conc_connection_list access. + */ + if (next_action == PM_DBS1) + downgrade_band = POLICY_MGR_BAND_24; + else if (next_action == PM_DBS2) + downgrade_band = POLICY_MGR_BAND_5; + else + downgrade_band = POLICY_MGR_ANY; + + status = policy_mgr_nss_update(psoc, new_nss, next_action, + downgrade_band, reason, + session_id); + if (!QDF_IS_STATUS_SUCCESS(status)) + status = policy_mgr_next_actions(psoc, session_id, + next_action, reason); + + return status; +} + +enum policy_mgr_con_mode policy_mgr_get_mode_by_vdev_id( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + enum policy_mgr_con_mode mode = PM_MAX_NUM_OF_MODE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t conn_index; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return mode; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) + if ((pm_conc_connection_list[conn_index].vdev_id == vdev_id) && + pm_conc_connection_list[conn_index].in_use){ + mode = pm_conc_connection_list[conn_index].mode; + break; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return mode; +} + +/** + * policy_mgr_init_connection_update() - Initialize connection + * update event + * @pm_ctx: policy mgr context + * + * Initializes the concurrent connection update event + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_init_connection_update( + struct policy_mgr_psoc_priv_obj *pm_ctx) +{ + QDF_STATUS qdf_status; + + qdf_status = qdf_event_create(&pm_ctx->connection_update_done_evt); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + policy_mgr_err("init event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_get_current_pref_hw_mode_dbs_2x2() - Get the + * current preferred hw mode + * + * Get the preferred hw mode based on the current connection combinations + * + * Return: No change (PM_NOP), MCC (PM_SINGLE_MAC), + * DBS (PM_DBS), SBS (PM_SBS) + */ +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dbs_2x2( + struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections; + uint8_t band1, band2, band3; + struct policy_mgr_hw_mode_params hw_mode; + QDF_STATUS status; + + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + return PM_NOP; + } + + num_connections = policy_mgr_get_connection_count(psoc); + + policy_mgr_debug("chan[0]:%d chan[1]:%d chan[2]:%d num_connections:%d dbs:%d", + pm_conc_connection_list[0].freq, + pm_conc_connection_list[1].freq, + pm_conc_connection_list[2].freq, num_connections, + hw_mode.dbs_cap); + + /* If the band of operation of both the MACs is the same, + * single MAC is preferred, otherwise DBS is preferred. + */ + switch (num_connections) { + case 1: + band1 = wlan_reg_freq_to_band(pm_conc_connection_list[0].freq); + if (band1 == REG_BAND_2G) + return PM_DBS; + else + return PM_NOP; + case 2: + band1 = wlan_reg_freq_to_band(pm_conc_connection_list[0].freq); + band2 = wlan_reg_freq_to_band(pm_conc_connection_list[1].freq); + if (band1 == REG_BAND_2G || band2 == REG_BAND_2G) { + if (!hw_mode.dbs_cap) + return PM_DBS; + else + return PM_NOP; + } else if (band1 == REG_BAND_5G && band2 == REG_BAND_5G) { + if (WLAN_REG_IS_FREQUENCY_VALID_5G_SBS( + pm_conc_connection_list[0].freq, + pm_conc_connection_list[1].freq)) { + if (!hw_mode.sbs_cap) + return PM_SBS; + else + return PM_NOP; + } else { + if (hw_mode.sbs_cap || hw_mode.dbs_cap) + return PM_SINGLE_MAC; + else + return PM_NOP; + } + } else + return PM_NOP; + case 3: + band1 = wlan_reg_freq_to_band(pm_conc_connection_list[0].freq); + band2 = wlan_reg_freq_to_band(pm_conc_connection_list[1].freq); + band3 = wlan_reg_freq_to_band(pm_conc_connection_list[2].freq); + if (band1 == REG_BAND_2G || band2 == REG_BAND_2G || + band3 == REG_BAND_2G) { + if (!hw_mode.dbs_cap) + return PM_DBS; + else + return PM_NOP; + } else if (band1 == REG_BAND_5G && band2 == REG_BAND_5G && + band3 == REG_BAND_5G) { + if (WLAN_REG_IS_FREQUENCY_VALID_5G_SBS( + pm_conc_connection_list[0].freq, + pm_conc_connection_list[2].freq) && + WLAN_REG_IS_FREQUENCY_VALID_5G_SBS( + pm_conc_connection_list[1].freq, + pm_conc_connection_list[2].freq) && + WLAN_REG_IS_FREQUENCY_VALID_5G_SBS( + pm_conc_connection_list[0].freq, + pm_conc_connection_list[1].freq)) { + if (!hw_mode.sbs_cap) + return PM_SBS; + else + return PM_NOP; + } else { + if (hw_mode.sbs_cap || hw_mode.dbs_cap) + return PM_SINGLE_MAC; + else + return PM_NOP; + } + } else + return PM_NOP; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + return PM_NOP; + } +} + +/** + * policy_mgr_get_current_pref_hw_mode_dbs_1x1() - Get the + * current preferred hw mode + * + * Get the preferred hw mode based on the current connection combinations + * + * Return: No change (PM_NOP), MCC (PM_SINGLE_MAC_UPGRADE), + * DBS (PM_DBS_DOWNGRADE) + */ +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dbs_1x1( + struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections; + uint8_t band1, band2, band3; + struct policy_mgr_hw_mode_params hw_mode; + QDF_STATUS status; + enum policy_mgr_conc_next_action next_action; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return PM_NOP; + } + + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + return PM_NOP; + } + + num_connections = policy_mgr_get_connection_count(psoc); + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("chan[0]:%d chan[1]:%d chan[2]:%d num_connections:%d dbs:%d", + pm_conc_connection_list[0].freq, + pm_conc_connection_list[1].freq, + pm_conc_connection_list[2].freq, num_connections, + hw_mode.dbs_cap); + + /* If the band of operation of both the MACs is the same, + * single MAC is preferred, otherwise DBS is preferred. + */ + switch (num_connections) { + case 1: + /* The driver would already be in the required hw mode */ + next_action = PM_NOP; + break; + case 2: + band1 = wlan_reg_freq_to_band(pm_conc_connection_list[0].freq); + band2 = wlan_reg_freq_to_band(pm_conc_connection_list[1].freq); + if ((band1 == band2) && (hw_mode.dbs_cap)) + next_action = PM_SINGLE_MAC_UPGRADE; + else if ((band1 != band2) && (!hw_mode.dbs_cap)) + next_action = PM_DBS_DOWNGRADE; + else + next_action = PM_NOP; + + break; + + case 3: + band1 = wlan_reg_freq_to_band(pm_conc_connection_list[0].freq); + band2 = wlan_reg_freq_to_band(pm_conc_connection_list[1].freq); + band3 = wlan_reg_freq_to_band(pm_conc_connection_list[2].freq); + if (((band1 == band2) && (band2 == band3)) && + (hw_mode.dbs_cap)) { + next_action = PM_SINGLE_MAC_UPGRADE; + } else if (((band1 != band2) || (band2 != band3) || + (band1 != band3)) && + (!hw_mode.dbs_cap)) { + next_action = PM_DBS_DOWNGRADE; + } else { + next_action = PM_NOP; + } + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + next_action = PM_NOP; + break; + } + + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return next_action; +} + +enum policy_mgr_conc_next_action +policy_mgr_get_current_pref_hw_mode_dual_dbs( + struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_conc_next_action next_action; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum policy_mgr_conc_next_action preferred_dbs; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return PM_NOP; + } + + next_action = policy_mgr_get_current_pref_hw_mode_dbs_1x1(psoc); + policy_mgr_info("next_action %d", next_action); + if (next_action != PM_DBS_DOWNGRADE) + return next_action; + + preferred_dbs = policy_mgr_get_preferred_dbs_action_table( + psoc, INVALID_VDEV_ID, 0, 0); + if (preferred_dbs == PM_DBS1) { + next_action = PM_DBS1_DOWNGRADE; + } else if (preferred_dbs == PM_DBS2) { + next_action = PM_DBS2_DOWNGRADE; + } else { + policy_mgr_err("DBS1 and DBS2 hw mode not supported"); + return PM_NOP; + } + policy_mgr_info("preferred_dbs %d", next_action); + return next_action; +} + +/** + * policy_mgr_reset_sap_mandatory_channels() - Reset the SAP mandatory channels + * + * Resets the SAP mandatory channel list and the length of the list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_reset_sap_mandatory_channels( + struct policy_mgr_psoc_priv_obj *pm_ctx) +{ + pm_ctx->sap_mandatory_channels_len = 0; + qdf_mem_zero(pm_ctx->sap_mandatory_channels, + QDF_ARRAY_SIZE(pm_ctx->sap_mandatory_channels) * + sizeof(*pm_ctx->sap_mandatory_channels)); + + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_add_sap_mandatory_chan(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq) +{ + int i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + for (i = 0; i < pm_ctx->sap_mandatory_channels_len; i++) { + if (ch_freq == pm_ctx->sap_mandatory_channels[i]) + return; + } + + policy_mgr_debug("Ch freq: %hu", ch_freq); + pm_ctx->sap_mandatory_channels[pm_ctx->sap_mandatory_channels_len++] + = ch_freq; +} + +uint32_t policy_mgr_get_sap_mandatory_chan_list_len( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + return pm_ctx->sap_mandatory_channels_len; +} + +void policy_mgr_init_sap_mandatory_2g_chan(struct wlan_objmgr_psoc *psoc) +{ + uint32_t ch_freq_list[NUM_CHANNELS] = {0}; + uint32_t len = 0; + int i; + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + status = policy_mgr_get_valid_chans(psoc, ch_freq_list, &len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Error in getting valid channels"); + return; + } + pm_ctx->sap_mandatory_channels_len = 0; + + for (i = 0; (i < len) && (i < NUM_CHANNELS); i++) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq_list[i])) { + policy_mgr_debug("Add chan %hu to mandatory list", + ch_freq_list[i]); + pm_ctx->sap_mandatory_channels[ + pm_ctx->sap_mandatory_channels_len++] = + ch_freq_list[i]; + } + } +} + +void policy_mgr_remove_sap_mandatory_chan(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq) +{ + uint32_t ch_freq_list[NUM_CHANNELS] = {0}; + uint32_t num_chan = 0; + int i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (pm_ctx->sap_mandatory_channels_len >= NUM_CHANNELS) { + policy_mgr_err("Invalid channel len %d ", + pm_ctx->sap_mandatory_channels_len); + return; + } + + for (i = 0; i < pm_ctx->sap_mandatory_channels_len; i++) { + if (ch_freq == pm_ctx->sap_mandatory_channels[i]) + continue; + ch_freq_list[num_chan++] = pm_ctx->sap_mandatory_channels[i]; + } + + qdf_mem_zero(pm_ctx->sap_mandatory_channels, + pm_ctx->sap_mandatory_channels_len * + sizeof(*pm_ctx->sap_mandatory_channels)); + qdf_mem_copy(pm_ctx->sap_mandatory_channels, ch_freq_list, + num_chan * sizeof(*pm_ctx->sap_mandatory_channels)); + pm_ctx->sap_mandatory_channels_len = num_chan; +} diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..a1ca9e79d6a92ff0a389961246ca92c1a425ae22 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c @@ -0,0 +1,4266 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_get_set_utils.c + * + * WLAN Concurrenct Connection Management APIs + * + */ + +/* Include files */ +#include "target_if.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_objmgr_global_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_nan_api.h" +#include "nan_public_structs.h" +#include "wlan_reg_services_api.h" +#include "wlan_mlme_vdev_mgr_interface.h" + +/* invalid channel id. */ +#define INVALID_CHANNEL_ID 0 + +QDF_STATUS +policy_mgr_get_allow_mcc_go_diff_bi(struct wlan_objmgr_psoc *psoc, + uint8_t *allow_mcc_go_diff_bi) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *allow_mcc_go_diff_bi = pm_ctx->cfg.allow_mcc_go_diff_bi; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +policy_mgr_get_enable_overlap_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_overlap_chnl) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *enable_overlap_chnl = pm_ctx->cfg.enable_overlap_chnl; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_dual_mac_feature(struct wlan_objmgr_psoc *psoc, + uint8_t dual_mac_feature) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->cfg.dual_mac_feature = dual_mac_feature; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_dual_mac_feature(struct wlan_objmgr_psoc *psoc, + uint8_t *dual_mac_feature) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *dual_mac_feature = pm_ctx->cfg.dual_mac_feature; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_force_1x1(struct wlan_objmgr_psoc *psoc, + uint8_t *force_1x1) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *force_1x1 = pm_ctx->cfg.is_force_1x1_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +policy_mgr_get_sta_sap_scc_on_dfs_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sta_sap_scc_on_dfs_chnl) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *sta_sap_scc_on_dfs_chnl = pm_ctx->cfg.sta_sap_scc_on_dfs_chnl; + + return QDF_STATUS_SUCCESS; +} + +bool +policy_mgr_get_dfs_master_dynamic_enabled( + struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool sta_on_5g = false; + bool sta_on_2g = false; + uint32_t i; + bool enable; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return true; + } + + if (!pm_ctx->cfg.sta_sap_scc_on_dfs_chnl) + return true; + if (pm_ctx->cfg.sta_sap_scc_on_dfs_chnl == + PM_STA_SAP_ON_DFS_MASTER_MODE_DISABLED) + return false; + if (pm_ctx->cfg.sta_sap_scc_on_dfs_chnl != + PM_STA_SAP_ON_DFS_MASTER_MODE_FLEX) { + policy_mgr_debug("sta_sap_scc_on_dfs_chnl %d unknown", + pm_ctx->cfg.sta_sap_scc_on_dfs_chnl); + return true; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if (!((pm_conc_connection_list[i].vdev_id != vdev_id) && + pm_conc_connection_list[i].in_use && + (pm_conc_connection_list[i].mode == PM_STA_MODE || + pm_conc_connection_list[i].mode == PM_P2P_CLIENT_MODE))) + continue; + if (WLAN_REG_IS_5GHZ_CH_FREQ(pm_conc_connection_list[i].freq)) + sta_on_5g = true; + else + sta_on_2g = true; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (policy_mgr_is_hw_dbs_capable(psoc) && !sta_on_5g) + enable = true; + else if (!sta_on_5g && !sta_on_2g) + enable = true; + else + enable = false; + policy_mgr_debug("sta_sap_scc_on_dfs_chnl %d sta_on_2g %d sta_on_5g %d enable %d", + pm_ctx->cfg.sta_sap_scc_on_dfs_chnl, sta_on_2g, + sta_on_5g, enable); + + return enable; +} + +QDF_STATUS +policy_mgr_get_sta_sap_scc_lte_coex_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sta_sap_scc_lte_coex) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *sta_sap_scc_lte_coex = pm_ctx->cfg.sta_sap_scc_on_lte_coex_chnl; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_sap_mandt_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sap_mandt_chnl) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *sap_mandt_chnl = pm_ctx->cfg.sap_mandatory_chnl_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +policy_mgr_get_indoor_chnl_marking(struct wlan_objmgr_psoc *psoc, + uint8_t *indoor_chnl_marking) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *indoor_chnl_marking = pm_ctx->cfg.mark_indoor_chnl_disable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *mcc_scc_switch = pm_ctx->cfg.mcc_to_scc_switch; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t *sys_pref) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *sys_pref = pm_ctx->cfg.sys_pref; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t sys_pref) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + pm_ctx->cfg.sys_pref = sys_pref; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_conc_rule1(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule1) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *conc_rule1 = pm_ctx->cfg.conc_rule1; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_conc_rule2(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule2) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *conc_rule2 = pm_ctx->cfg.conc_rule2; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_chnl_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *chnl_select_plcy) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *chnl_select_plcy = pm_ctx->cfg.chnl_select_plcy; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_ch_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t ch_select_policy) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + pm_ctx->cfg.chnl_select_plcy = ch_select_policy; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_dynamic_mcc_adaptive_sch( + struct wlan_objmgr_psoc *psoc, + bool dynamic_mcc_adaptive_sched) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + pm_ctx->dynamic_mcc_adaptive_sched = dynamic_mcc_adaptive_sched; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_dynamic_mcc_adaptive_sch( + struct wlan_objmgr_psoc *psoc, + bool *dynamic_mcc_adaptive_sched) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *dynamic_mcc_adaptive_sched = pm_ctx->dynamic_mcc_adaptive_sched; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_mcc_adaptive_sch) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *enable_mcc_adaptive_sch = pm_ctx->cfg.enable_mcc_adaptive_sch; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_sta_cxn_5g_band(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sta_cxn_5g_band) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *enable_sta_cxn_5g_band = pm_ctx->cfg.enable_sta_cxn_5g_band; + + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_update_new_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t new_hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + pm_ctx->new_hw_mode_index = new_hw_mode_index; +} + +void policy_mgr_update_old_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t old_hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + pm_ctx->old_hw_mode_index = old_hw_mode_index; +} + +void policy_mgr_update_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t new_hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + if (POLICY_MGR_DEFAULT_HW_MODE_INDEX == pm_ctx->new_hw_mode_index) { + pm_ctx->new_hw_mode_index = new_hw_mode_index; + } else { + pm_ctx->old_hw_mode_index = pm_ctx->new_hw_mode_index; + pm_ctx->new_hw_mode_index = new_hw_mode_index; + } + policy_mgr_debug("Updated: old_hw_mode_index:%d new_hw_mode_index:%d", + pm_ctx->old_hw_mode_index, pm_ctx->new_hw_mode_index); +} + +/** + * policy_mgr_get_num_of_setbits_from_bitmask() - to get num of + * setbits from bitmask + * @mask: given bitmask + * + * This helper function should return number of setbits from bitmask + * + * Return: number of setbits from bitmask + */ +static uint32_t policy_mgr_get_num_of_setbits_from_bitmask(uint32_t mask) +{ + uint32_t num_of_setbits = 0; + + while (mask) { + mask &= (mask - 1); + num_of_setbits++; + } + return num_of_setbits; +} + +/** + * policy_mgr_map_wmi_channel_width_to_hw_mode_bw() - returns + * bandwidth in terms of hw_mode_bandwidth + * @width: bandwidth in terms of wmi_channel_width + * + * This function returns the bandwidth in terms of hw_mode_bandwidth. + * + * Return: BW in terms of hw_mode_bandwidth. + */ +static enum hw_mode_bandwidth policy_mgr_map_wmi_channel_width_to_hw_mode_bw( + wmi_channel_width width) +{ + switch (width) { + case WMI_CHAN_WIDTH_20: + return HW_MODE_20_MHZ; + case WMI_CHAN_WIDTH_40: + return HW_MODE_40_MHZ; + case WMI_CHAN_WIDTH_80: + return HW_MODE_80_MHZ; + case WMI_CHAN_WIDTH_160: + return HW_MODE_160_MHZ; + case WMI_CHAN_WIDTH_80P80: + return HW_MODE_80_PLUS_80_MHZ; + case WMI_CHAN_WIDTH_5: + return HW_MODE_5_MHZ; + case WMI_CHAN_WIDTH_10: + return HW_MODE_10_MHZ; + default: + return HW_MODE_BW_NONE; + } + + return HW_MODE_BW_NONE; +} + +static void policy_mgr_get_hw_mode_params( + struct wlan_psoc_host_mac_phy_caps *caps, + struct policy_mgr_mac_ss_bw_info *info) +{ + if (!caps) { + policy_mgr_err("Invalid capabilities"); + return; + } + + info->mac_tx_stream = policy_mgr_get_num_of_setbits_from_bitmask( + QDF_MAX(caps->tx_chain_mask_2G, + caps->tx_chain_mask_5G)); + info->mac_rx_stream = policy_mgr_get_num_of_setbits_from_bitmask( + QDF_MAX(caps->rx_chain_mask_2G, + caps->rx_chain_mask_5G)); + info->mac_bw = policy_mgr_map_wmi_channel_width_to_hw_mode_bw( + QDF_MAX(caps->max_bw_supported_2G, + caps->max_bw_supported_5G)); + info->mac_band_cap = caps->supported_bands; +} + +/** + * policy_mgr_set_hw_mode_params() - sets TX-RX stream, + * bandwidth and DBS in hw_mode_list + * @wma_handle: pointer to wma global structure + * @mac0_ss_bw_info: TX-RX streams, BW for MAC0 + * @mac1_ss_bw_info: TX-RX streams, BW for MAC1 + * @pos: refers to hw_mode_list array index + * @hw_mode_id: hw mode id value used by firmware + * @dbs_mode: dbs_mode for the dbs_hw_mode + * @sbs_mode: sbs_mode for the sbs_hw_mode + * + * This function sets TX-RX stream, bandwidth and DBS mode in + * hw_mode_list. + * + * Return: none + */ +static void policy_mgr_set_hw_mode_params(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_mac_ss_bw_info mac0_ss_bw_info, + struct policy_mgr_mac_ss_bw_info mac1_ss_bw_info, + uint32_t pos, uint32_t hw_mode_id, uint32_t dbs_mode, + uint32_t sbs_mode) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_tx_stream); + POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_rx_stream); + POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_bw); + POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac1_ss_bw_info.mac_tx_stream); + POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac1_ss_bw_info.mac_rx_stream); + POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac1_ss_bw_info.mac_bw); + POLICY_MGR_HW_MODE_DBS_MODE_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + dbs_mode); + POLICY_MGR_HW_MODE_AGILE_DFS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + HW_MODE_AGILE_DFS_NONE); + POLICY_MGR_HW_MODE_SBS_MODE_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + sbs_mode); + POLICY_MGR_HW_MODE_MAC0_BAND_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_band_cap); + POLICY_MGR_HW_MODE_ID_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + hw_mode_id); +} + +QDF_STATUS policy_mgr_update_hw_mode_list(struct wlan_objmgr_psoc *psoc, + struct target_psoc_info *tgt_hdl) +{ + struct wlan_psoc_host_mac_phy_caps *tmp; + uint32_t i, hw_config_type, j = 0; + uint32_t dbs_mode, sbs_mode; + struct policy_mgr_mac_ss_bw_info mac0_ss_bw_info = {0}; + struct policy_mgr_mac_ss_bw_info mac1_ss_bw_info = {0}; + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct tgt_info *info; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + info = &tgt_hdl->info; + if (!info->service_ext_param.num_hw_modes) { + policy_mgr_err("Number of HW modes: %d", + info->service_ext_param.num_hw_modes); + return QDF_STATUS_E_FAILURE; + } + + /* + * This list was updated as part of service ready event. Re-populate + * HW mode list from the device capabilities. + */ + if (pm_ctx->hw_mode.hw_mode_list) { + qdf_mem_free(pm_ctx->hw_mode.hw_mode_list); + pm_ctx->hw_mode.hw_mode_list = NULL; + policy_mgr_debug("DBS list is freed"); + } + + pm_ctx->num_dbs_hw_modes = info->service_ext_param.num_hw_modes; + pm_ctx->hw_mode.hw_mode_list = + qdf_mem_malloc(sizeof(*pm_ctx->hw_mode.hw_mode_list) * + pm_ctx->num_dbs_hw_modes); + if (!pm_ctx->hw_mode.hw_mode_list) { + policy_mgr_err("Memory allocation failed for DBS"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_debug("Updated HW mode list: Num modes:%d", + pm_ctx->num_dbs_hw_modes); + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + /* Update for MAC0 */ + tmp = &info->mac_phy_cap[j++]; + policy_mgr_get_hw_mode_params(tmp, &mac0_ss_bw_info); + hw_config_type = tmp->hw_mode_config_type; + dbs_mode = HW_MODE_DBS_NONE; + sbs_mode = HW_MODE_SBS_NONE; + mac1_ss_bw_info.mac_tx_stream = 0; + mac1_ss_bw_info.mac_rx_stream = 0; + mac1_ss_bw_info.mac_bw = 0; + + /* SBS and DBS have dual MAC. Upto 2 MACs are considered. */ + if ((hw_config_type == WMI_HW_MODE_DBS) || + (hw_config_type == WMI_HW_MODE_SBS_PASSIVE) || + (hw_config_type == WMI_HW_MODE_SBS)) { + /* Update for MAC1 */ + tmp = &info->mac_phy_cap[j++]; + policy_mgr_get_hw_mode_params(tmp, &mac1_ss_bw_info); + if (hw_config_type == WMI_HW_MODE_DBS) + dbs_mode = HW_MODE_DBS; + if ((hw_config_type == WMI_HW_MODE_SBS_PASSIVE) || + (hw_config_type == WMI_HW_MODE_SBS)) + sbs_mode = HW_MODE_SBS; + } + + /* Updating HW mode list */ + policy_mgr_set_hw_mode_params(psoc, mac0_ss_bw_info, + mac1_ss_bw_info, i, tmp->hw_mode_id, dbs_mode, + sbs_mode); + } + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_init_dbs_hw_mode(struct wlan_objmgr_psoc *psoc, + uint32_t num_dbs_hw_modes, + uint32_t *ev_wlan_dbs_hw_mode_list) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pm_ctx->num_dbs_hw_modes = num_dbs_hw_modes; + pm_ctx->hw_mode.hw_mode_list = + qdf_mem_malloc(sizeof(*pm_ctx->hw_mode.hw_mode_list) * + pm_ctx->num_dbs_hw_modes); + if (!pm_ctx->hw_mode.hw_mode_list) { + policy_mgr_err("Memory allocation failed for DBS"); + return; + } + qdf_mem_copy(pm_ctx->hw_mode.hw_mode_list, + ev_wlan_dbs_hw_mode_list, + (sizeof(*pm_ctx->hw_mode.hw_mode_list) * + pm_ctx->num_dbs_hw_modes)); +} + +void policy_mgr_dump_dbs_hw_mode(struct wlan_objmgr_psoc *psoc) +{ + uint32_t i, param; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + policy_mgr_debug("[%d]-MAC0: tx_ss:%d rx_ss:%d bw_idx:%d band_cap:%d", + i, + POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET(param), + POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET(param), + POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_GET(param), + POLICY_MGR_HW_MODE_MAC0_BAND_GET(param)); + policy_mgr_debug("[%d]-MAC1: tx_ss:%d rx_ss:%d bw_idx:%d", + i, + POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET(param), + POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET(param), + POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_GET(param)); + policy_mgr_debug("[%d] DBS:%d SBS:%d hw_mode_id:%d", i, + POLICY_MGR_HW_MODE_DBS_MODE_GET(param), + POLICY_MGR_HW_MODE_SBS_MODE_GET(param), + POLICY_MGR_HW_MODE_ID_GET(param)); + } +} + +void policy_mgr_init_dbs_config(struct wlan_objmgr_psoc *psoc, + uint32_t scan_config, uint32_t fw_config) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t dual_mac_feature; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + pm_ctx->dual_mac_cfg.cur_scan_config = 0; + pm_ctx->dual_mac_cfg.cur_fw_mode_config = 0; + + dual_mac_feature = pm_ctx->cfg.dual_mac_feature; + /* If dual mac features are disabled in the INI, we + * need not proceed further + */ + if (dual_mac_feature == DISABLE_DBS_CXN_AND_SCAN) { + policy_mgr_err("Disabling dual mac capabilities"); + /* All capabilities are initialized to 0. We can return */ + goto done; + } + + /* Initialize concurrent_scan_config_bits with default FW value */ + WMI_DBS_CONC_SCAN_CFG_ASYNC_DBS_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_ASYNC_DBS_SCAN_GET(scan_config)); + WMI_DBS_CONC_SCAN_CFG_SYNC_DBS_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_SYNC_DBS_SCAN_GET(scan_config)); + WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_GET(scan_config)); + WMI_DBS_CONC_SCAN_CFG_AGILE_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_AGILE_SCAN_GET(scan_config)); + WMI_DBS_CONC_SCAN_CFG_AGILE_DFS_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_AGILE_DFS_SCAN_GET(scan_config)); + + /* Initialize fw_mode_config_bits with default FW value */ + WMI_DBS_FW_MODE_CFG_DBS_SET( + pm_ctx->dual_mac_cfg.cur_fw_mode_config, + WMI_DBS_FW_MODE_CFG_DBS_GET(fw_config)); + WMI_DBS_FW_MODE_CFG_AGILE_DFS_SET( + pm_ctx->dual_mac_cfg.cur_fw_mode_config, + WMI_DBS_FW_MODE_CFG_AGILE_DFS_GET(fw_config)); + WMI_DBS_FW_MODE_CFG_DBS_FOR_CXN_SET( + pm_ctx->dual_mac_cfg.cur_fw_mode_config, + WMI_DBS_FW_MODE_CFG_DBS_FOR_CXN_GET(fw_config)); +done: + /* Initialize the previous scan/fw mode config */ + pm_ctx->dual_mac_cfg.prev_scan_config = + pm_ctx->dual_mac_cfg.cur_scan_config; + pm_ctx->dual_mac_cfg.prev_fw_mode_config = + pm_ctx->dual_mac_cfg.cur_fw_mode_config; + + policy_mgr_debug("cur_scan_config:%x cur_fw_mode_config:%x", + pm_ctx->dual_mac_cfg.cur_scan_config, + pm_ctx->dual_mac_cfg.cur_fw_mode_config); +} + +void policy_mgr_update_dbs_scan_config(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pm_ctx->dual_mac_cfg.prev_scan_config = + pm_ctx->dual_mac_cfg.cur_scan_config; + pm_ctx->dual_mac_cfg.cur_scan_config = + pm_ctx->dual_mac_cfg.req_scan_config; +} + +void policy_mgr_update_dbs_fw_config(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pm_ctx->dual_mac_cfg.prev_fw_mode_config = + pm_ctx->dual_mac_cfg.cur_fw_mode_config; + pm_ctx->dual_mac_cfg.cur_fw_mode_config = + pm_ctx->dual_mac_cfg.req_fw_mode_config; +} + +void policy_mgr_update_dbs_req_config(struct wlan_objmgr_psoc *psoc, + uint32_t scan_config, uint32_t fw_mode_config) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + pm_ctx->dual_mac_cfg.req_scan_config = scan_config; + pm_ctx->dual_mac_cfg.req_fw_mode_config = fw_mode_config; +} + +bool policy_mgr_get_dbs_plus_agile_scan_config(struct wlan_objmgr_psoc *psoc) +{ + uint32_t scan_config; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + + return WMI_DBS_CONC_SCAN_CFG_AGILE_SCAN_GET(scan_config); +} + +bool policy_mgr_get_single_mac_scan_with_dfs_config( + struct wlan_objmgr_psoc *psoc) +{ + uint32_t scan_config; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + + return WMI_DBS_CONC_SCAN_CFG_AGILE_DFS_SCAN_GET(scan_config); +} + +int8_t policy_mgr_get_num_dbs_hw_modes(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return -EINVAL; + } + return pm_ctx->num_dbs_hw_modes; +} + +bool policy_mgr_find_if_fw_supports_dbs(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct wmi_unified *wmi_handle; + bool dbs_support; + + pm_ctx = policy_mgr_get_context(psoc); + + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + policy_mgr_debug("Invalid WMI handle"); + return false; + } + dbs_support = + wmi_service_enabled(wmi_handle, + wmi_service_dual_band_simultaneous_support); + + /* The agreement with FW is that: To know if the target is DBS + * capable, DBS needs to be supported both in the HW mode list + * and in the service ready event + */ + if (!dbs_support) + return false; + + return true; +} + +bool policy_mgr_find_if_hwlist_has_dbs(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t param, i, found = 0; + + pm_ctx = policy_mgr_get_context(psoc); + + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + if (POLICY_MGR_HW_MODE_DBS_MODE_GET(param)) { + found = 1; + break; + } + } + if (found) + return true; + + return false; +} + +static bool policy_mgr_find_if_hwlist_has_sbs(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t param, i, found = 0; + + pm_ctx = policy_mgr_get_context(psoc); + + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + if (POLICY_MGR_HW_MODE_SBS_MODE_GET(param)) { + found = 1; + break; + } + } + if (found) + return true; + + return false; +} + +bool policy_mgr_is_dbs_scan_allowed(struct wlan_objmgr_psoc *psoc) +{ + uint8_t dbs_type = DISABLE_DBS_CXN_AND_SCAN; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + if (!policy_mgr_find_if_fw_supports_dbs(psoc) || + !policy_mgr_find_if_hwlist_has_dbs(psoc)) + return false; + + policy_mgr_get_dual_mac_feature(psoc, &dbs_type); + /* + * If DBS support for scan is disabled through INI then DBS is not + * supported for scan. + * + * For DBS scan check the INI value explicitly + */ + switch (dbs_type) { + case DISABLE_DBS_CXN_AND_SCAN: + case ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN: + return false; + default: + return true; + } +} + +bool policy_mgr_is_hw_dbs_capable(struct wlan_objmgr_psoc *psoc) +{ + if (!policy_mgr_is_dbs_enable(psoc)) + return false; + + if (!policy_mgr_find_if_fw_supports_dbs(psoc)) + return false; + + if (!policy_mgr_find_if_hwlist_has_dbs(psoc)) { + policymgr_nofl_debug("HW mode list has no DBS"); + return false; + } + + return true; +} + +bool policy_mgr_is_hw_sbs_capable(struct wlan_objmgr_psoc *psoc) +{ + if (!policy_mgr_find_if_fw_supports_dbs(psoc)) { + return false; + } + + if (!policy_mgr_find_if_hwlist_has_sbs(psoc)) { + policymgr_nofl_debug("HW mode list has no SBS"); + return false; + } + + return true; +} + +QDF_STATUS policy_mgr_get_dbs_hw_modes(struct wlan_objmgr_psoc *psoc, + bool *one_by_one_dbs, bool *two_by_two_dbs) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t i; + int8_t found_one_by_one = -EINVAL, found_two_by_two = -EINVAL; + uint32_t conf1_tx_ss, conf1_rx_ss; + uint32_t conf2_tx_ss, conf2_rx_ss; + + *one_by_one_dbs = false; + *two_by_two_dbs = false; + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + policy_mgr_rl_debug("HW is not DBS capable"); + /* Caller will understand that DBS is disabled */ + return QDF_STATUS_SUCCESS; + + } + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + /* To check 1x1 capability */ + policy_mgr_get_tx_rx_ss_from_config(HW_MODE_SS_1x1, + &conf1_tx_ss, &conf1_rx_ss); + /* To check 2x2 capability */ + policy_mgr_get_tx_rx_ss_from_config(HW_MODE_SS_2x2, + &conf2_tx_ss, &conf2_rx_ss); + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + uint32_t t_conf0_tx_ss, t_conf0_rx_ss; + uint32_t t_conf1_tx_ss, t_conf1_rx_ss; + uint32_t dbs_mode; + + t_conf0_tx_ss = POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + t_conf0_rx_ss = POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + t_conf1_tx_ss = POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + t_conf1_rx_ss = POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + dbs_mode = POLICY_MGR_HW_MODE_DBS_MODE_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + + if (((((t_conf0_tx_ss == conf1_tx_ss) && + (t_conf0_rx_ss == conf1_rx_ss)) || + ((t_conf1_tx_ss == conf1_tx_ss) && + (t_conf1_rx_ss == conf1_rx_ss))) && + (dbs_mode == HW_MODE_DBS)) && + (found_one_by_one < 0)) { + found_one_by_one = i; + policy_mgr_debug("1x1 hw_mode index %d found", i); + /* Once an entry is found, need not check for 1x1 + * again + */ + continue; + } + + if (((((t_conf0_tx_ss == conf2_tx_ss) && + (t_conf0_rx_ss == conf2_rx_ss)) || + ((t_conf1_tx_ss == conf2_tx_ss) && + (t_conf1_rx_ss == conf2_rx_ss))) && + (dbs_mode == HW_MODE_DBS)) && + (found_two_by_two < 0)) { + found_two_by_two = i; + policy_mgr_debug("2x2 hw_mode index %d found", i); + /* Once an entry is found, need not check for 2x2 + * again + */ + continue; + } + } + + if (found_one_by_one >= 0) + *one_by_one_dbs = true; + if (found_two_by_two >= 0) + *two_by_two_dbs = true; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_current_hw_mode(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_hw_mode_params *hw_mode) +{ + QDF_STATUS status; + uint32_t old_hw_index = 0, new_hw_index = 0; + + status = policy_mgr_get_old_and_new_hw_index(psoc, &old_hw_index, + &new_hw_index); + if (QDF_STATUS_SUCCESS != status) { + policy_mgr_err("Failed to get HW mode index"); + return QDF_STATUS_E_FAILURE; + } + + if (new_hw_index == POLICY_MGR_DEFAULT_HW_MODE_INDEX) { + policy_mgr_err("HW mode is not yet initialized"); + return QDF_STATUS_E_FAILURE; + } + + status = policy_mgr_get_hw_mode_from_idx(psoc, new_hw_index, hw_mode); + if (QDF_STATUS_SUCCESS != status) { + policy_mgr_err("Failed to get HW mode index"); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_is_current_hwmode_dbs(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_hw_mode_params hw_mode; + + if (!policy_mgr_is_hw_dbs_capable(psoc)) + return false; + if (QDF_STATUS_SUCCESS != + policy_mgr_get_current_hw_mode(psoc, &hw_mode)) + return false; + if (hw_mode.dbs_cap) + return true; + return false; +} + +bool policy_mgr_is_dbs_enable(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) { + policy_mgr_rl_debug("DBS is disabled from ini"); + return false; + } + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + if (WMI_DBS_FW_MODE_CFG_DBS_GET( + pm_ctx->dual_mac_cfg.cur_fw_mode_config)) + return true; + + return false; +} + +bool policy_mgr_is_hw_dbs_2x2_capable(struct wlan_objmgr_psoc *psoc) +{ + struct dbs_nss nss_dbs = {0}; + uint32_t nss; + + nss = policy_mgr_get_hw_dbs_nss(psoc, &nss_dbs); + if (nss >= HW_MODE_SS_2x2 && (nss_dbs.mac0_ss == nss_dbs.mac1_ss)) + return true; + else + return false; +} + +bool policy_mgr_is_hw_dbs_required_for_band(struct wlan_objmgr_psoc *psoc, + enum hw_mode_mac_band_cap band) +{ + struct dbs_nss nss_dbs = {0}; + uint32_t nss; + + nss = policy_mgr_get_hw_dbs_nss(psoc, &nss_dbs); + if (nss >= HW_MODE_SS_1x1 && nss_dbs.mac0_ss == nss_dbs.mac1_ss && + !(nss_dbs.single_mac0_band_cap & band)) + return true; + else + return false; +} + +bool policy_mgr_is_dp_hw_dbs_2x2_capable(struct wlan_objmgr_psoc *psoc) +{ + return policy_mgr_is_hw_dbs_2x2_capable(psoc) || + policy_mgr_is_hw_dbs_required_for_band(psoc, + HW_MODE_MAC_BAND_2G); +} + +/* + * policy_mgr_is_2x2_1x1_dbs_capable() - check 2x2+1x1 DBS supported or not + * @psoc: PSOC object data + * + * This routine is called to check 2x2 5G + 1x1 2G (DBS1) or + * 2x2 2G + 1x1 5G (DBS2) support or not. + * Either DBS1 or DBS2 supported + * + * Return: true/false + */ +bool policy_mgr_is_2x2_1x1_dbs_capable(struct wlan_objmgr_psoc *psoc) +{ + struct dbs_nss nss_dbs; + uint32_t nss; + + nss = policy_mgr_get_hw_dbs_nss(psoc, &nss_dbs); + if (nss >= HW_MODE_SS_2x2 && (nss_dbs.mac0_ss > nss_dbs.mac1_ss)) + return true; + else + return false; +} + +/* + * policy_mgr_is_2x2_5G_1x1_2G_dbs_capable() - check Genoa DBS1 enabled or not + * @psoc: PSOC object data + * + * This routine is called to check support DBS1 or not. + * Notes: DBS1: 2x2 5G + 1x1 2G. + * This function will call policy_mgr_get_hw_mode_idx_from_dbs_hw_list to match + * the HW mode from hw mode list. The parameters will also be matched to + * 2x2 5G +2x2 2G HW mode. But firmware will not report 2x2 5G + 2x2 2G alone + * with 2x2 5G + 1x1 2G at same time. So, it is safe to find DBS1 with + * policy_mgr_get_hw_mode_idx_from_dbs_hw_list. + * + * Return: true/false + */ +bool policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(struct wlan_objmgr_psoc *psoc) +{ + return policy_mgr_is_2x2_1x1_dbs_capable(psoc) && + (policy_mgr_get_hw_mode_idx_from_dbs_hw_list( + psoc, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_1x1, HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_5G, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE) >= 0); +} + +/* + * policy_mgr_is_2x2_2G_1x1_5G_dbs_capable() - check Genoa DBS2 enabled or not + * @psoc: PSOC object data + * + * This routine is called to check support DBS2 or not. + * Notes: DBS2: 2x2 2G + 1x1 5G + * + * Return: true/false + */ +bool policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(struct wlan_objmgr_psoc *psoc) +{ + return policy_mgr_is_2x2_1x1_dbs_capable(psoc) && + (policy_mgr_get_hw_mode_idx_from_dbs_hw_list( + psoc, + HW_MODE_SS_2x2, + HW_MODE_40_MHZ, + HW_MODE_SS_1x1, HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_2G, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE) >= 0); +} + +uint32_t policy_mgr_get_connection_count(struct wlan_objmgr_psoc *psoc) +{ + uint32_t conn_index, count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return count; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].in_use) + count++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return count; +} + +uint32_t policy_mgr_mode_specific_vdev_id(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode) +{ + uint32_t conn_index = 0; + uint32_t vdev_id = WLAN_INVALID_VDEV_ID; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return vdev_id; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + /* + * Note: This gives you the first vdev id of the mode type in a + * sta+sta or sap+sap or p2p + p2p case + */ + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].mode == mode) && + pm_conc_connection_list[conn_index].in_use) { + vdev_id = pm_conc_connection_list[conn_index].vdev_id; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return vdev_id; +} + +uint32_t policy_mgr_mode_specific_connection_count( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t *list) +{ + uint32_t conn_index = 0, count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return count; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].mode == mode) && + pm_conc_connection_list[conn_index].in_use) { + if (list) + list[count] = conn_index; + count++; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return count; +} + +QDF_STATUS policy_mgr_check_conn_with_mode_and_vdev_id( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode, + uint32_t vdev_id) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return qdf_status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if ((pm_conc_connection_list[conn_index].mode == mode) && + (pm_conc_connection_list[conn_index].vdev_id == vdev_id)) { + qdf_status = QDF_STATUS_SUCCESS; + break; + } + conn_index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return qdf_status; +} + +void policy_mgr_soc_set_dual_mac_cfg_cb(enum set_hw_mode_status status, + uint32_t scan_config, + uint32_t fw_mode_config) +{ + policy_mgr_debug("Status:%d for scan_config:%x fw_mode_config:%x", + status, scan_config, fw_mode_config); +} + +void policy_mgr_set_dual_mac_scan_config(struct wlan_objmgr_psoc *psoc, + uint8_t dbs_val, + uint8_t dbs_plus_agile_scan_val, + uint8_t single_mac_scan_with_dbs_val) +{ + struct policy_mgr_dual_mac_config cfg; + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + /* Any non-zero positive value is treated as 1 */ + if (dbs_val != 0) + dbs_val = 1; + if (dbs_plus_agile_scan_val != 0) + dbs_plus_agile_scan_val = 1; + if (single_mac_scan_with_dbs_val != 0) + single_mac_scan_with_dbs_val = 1; + + status = policy_mgr_get_updated_scan_config(psoc, &cfg.scan_config, + dbs_val, + dbs_plus_agile_scan_val, + single_mac_scan_with_dbs_val); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("policy_mgr_get_updated_scan_config failed %d", + status); + return; + } + + status = policy_mgr_get_updated_fw_mode_config(psoc, + &cfg.fw_mode_config, + policy_mgr_get_dbs_config(psoc), + policy_mgr_get_agile_dfs_config(psoc)); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("policy_mgr_get_updated_fw_mode_config failed %d", + status); + return; + } + + cfg.set_dual_mac_cb = policy_mgr_soc_set_dual_mac_cfg_cb; + + policy_mgr_debug("scan_config:%x fw_mode_config:%x", + cfg.scan_config, cfg.fw_mode_config); + + status = pm_ctx->sme_cbacks.sme_soc_set_dual_mac_config(cfg); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("sme_soc_set_dual_mac_config failed %d", status); +} + +void policy_mgr_set_dual_mac_fw_mode_config(struct wlan_objmgr_psoc *psoc, + uint8_t dbs, uint8_t dfs) +{ + struct policy_mgr_dual_mac_config cfg; + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + /* Any non-zero positive value is treated as 1 */ + if (dbs != 0) + dbs = 1; + if (dfs != 0) + dfs = 1; + + status = policy_mgr_get_updated_scan_config(psoc, &cfg.scan_config, + policy_mgr_get_dbs_scan_config(psoc), + policy_mgr_get_dbs_plus_agile_scan_config(psoc), + policy_mgr_get_single_mac_scan_with_dfs_config(psoc)); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("policy_mgr_get_updated_scan_config failed %d", + status); + return; + } + + status = policy_mgr_get_updated_fw_mode_config(psoc, + &cfg.fw_mode_config, dbs, dfs); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("policy_mgr_get_updated_fw_mode_config failed %d", + status); + return; + } + + cfg.set_dual_mac_cb = policy_mgr_soc_set_dual_mac_cfg_cb; + + policy_mgr_debug("scan_config:%x fw_mode_config:%x", + cfg.scan_config, cfg.fw_mode_config); + + status = pm_ctx->sme_cbacks.sme_soc_set_dual_mac_config(cfg); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("sme_soc_set_dual_mac_config failed %d", status); +} + +bool policy_mgr_current_concurrency_is_mcc(struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections = 0; + bool is_mcc = false; + + num_connections = policy_mgr_get_connection_count(psoc); + + switch (num_connections) { + case 1: + break; + case 2: + if (pm_conc_connection_list[0].freq != + pm_conc_connection_list[1].freq && + pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + is_mcc = true; + } + break; + case 3: + if (pm_conc_connection_list[0].freq != + pm_conc_connection_list[1].freq || + pm_conc_connection_list[0].freq != + pm_conc_connection_list[2].freq || + pm_conc_connection_list[1].freq != + pm_conc_connection_list[2].freq){ + is_mcc = true; + } + break; + default: + policy_mgr_debug("unexpected num_connections value %d", + num_connections); + break; + } + + return is_mcc; +} + +bool policy_mgr_is_sap_p2pgo_on_dfs(struct wlan_objmgr_psoc *psoc) +{ + int index, count; + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct policy_mgr_psoc_priv_obj *pm_ctx = NULL; + + if (psoc) + pm_ctx = policy_mgr_get_context(psoc); + + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + index = 0; + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + count = policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, + list); + while (index < count) { + if (pm_conc_connection_list[list[index]].ch_flagext & + (IEEE80211_CHAN_DFS | IEEE80211_CHAN_DFS_CFREQ2)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return true; + } + index++; + } + count = policy_mgr_mode_specific_connection_count(psoc, + PM_P2P_GO_MODE, + list); + index = 0; + while (index < count) { + if (pm_conc_connection_list[list[index]].ch_flagext & + (IEEE80211_CHAN_DFS | IEEE80211_CHAN_DFS_CFREQ2)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return true; + } + index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return false; +} + +/** + * policy_mgr_set_concurrency_mode() - To set concurrency mode + * @psoc: PSOC object data + * @mode: device mode + * + * This routine is called to set the concurrency mode + * + * Return: NONE + */ +void policy_mgr_set_concurrency_mode(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + case QDF_IBSS_MODE: + case QDF_MONITOR_MODE: + pm_ctx->concurrency_mode |= (1 << mode); + pm_ctx->no_of_open_sessions[mode]++; + break; + default: + break; + } + + policy_mgr_debug("concurrency_mode = 0x%x Number of open sessions for mode %d = %d", + pm_ctx->concurrency_mode, mode, + pm_ctx->no_of_open_sessions[mode]); +} + +/** + * policy_mgr_clear_concurrency_mode() - To clear concurrency mode + * @psoc: PSOC object data + * @mode: device mode + * + * This routine is called to clear the concurrency mode + * + * Return: NONE + */ +void policy_mgr_clear_concurrency_mode(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + case QDF_MONITOR_MODE: + pm_ctx->no_of_open_sessions[mode]--; + if (!(pm_ctx->no_of_open_sessions[mode])) + pm_ctx->concurrency_mode &= (~(1 << mode)); + break; + default: + break; + } + + policy_mgr_debug("concurrency_mode = 0x%x Number of open sessions for mode %d = %d", + pm_ctx->concurrency_mode, mode, + pm_ctx->no_of_open_sessions[mode]); +} + +void policy_mgr_incr_active_session(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, + uint8_t session_id) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t conn_6ghz_flag = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + /* + * Need to aquire mutex as entire functionality in this function + * is in critical section + */ + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + case QDF_IBSS_MODE: + case QDF_NAN_DISC_MODE: + case QDF_NDI_MODE: + pm_ctx->no_of_active_sessions[mode]++; + break; + default: + break; + } + + if (mode == QDF_NDI_MODE && + pm_ctx->hdd_cbacks.wlan_hdd_indicate_active_ndp_cnt) + pm_ctx->hdd_cbacks.wlan_hdd_indicate_active_ndp_cnt( + psoc, session_id, + pm_ctx->no_of_active_sessions[mode]); + + if (mode != QDF_NAN_DISC_MODE && pm_ctx->dp_cbacks.hdd_v2_flow_pool_map) + pm_ctx->dp_cbacks.hdd_v2_flow_pool_map(session_id); + if (mode == QDF_SAP_MODE) + policy_mgr_get_ap_6ghz_capable(psoc, session_id, + &conn_6ghz_flag); + + policy_mgr_debug("No.# of active sessions for mode %d = %d", + mode, pm_ctx->no_of_active_sessions[mode]); + policy_mgr_incr_connection_count(psoc, session_id, mode); + if ((policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL) > 0) && (mode != QDF_STA_MODE)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_set_pcl_for_existing_combo(psoc, PM_STA_MODE); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + } + + /* Notify tdls */ + if (pm_ctx->tdls_cbacks.tdls_notify_increment_session) + pm_ctx->tdls_cbacks.tdls_notify_increment_session(psoc); + + /* + * Disable LRO/GRO if P2P or IBSS or SAP connection has come up or + * there are more than one STA connections + */ + if ((policy_mgr_mode_specific_connection_count(psoc, PM_STA_MODE, NULL) > 1) || + (policy_mgr_mode_specific_connection_count(psoc, PM_SAP_MODE, NULL) > 0) || + (policy_mgr_mode_specific_connection_count(psoc, PM_P2P_CLIENT_MODE, NULL) > + 0) || + (policy_mgr_mode_specific_connection_count(psoc, PM_P2P_GO_MODE, NULL) > 0) || + (policy_mgr_mode_specific_connection_count(psoc, + PM_IBSS_MODE, + NULL) > 0) || + (policy_mgr_mode_specific_connection_count(psoc, + PM_NDI_MODE, + NULL) > 0)) { + if (pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency) + pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency(true); + }; + + /* Enable RPS if SAP interface has come up */ + if (policy_mgr_mode_specific_connection_count(psoc, PM_SAP_MODE, NULL) + == 1) { + if (pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb) + pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb(true); + } + if (mode == QDF_SAP_MODE) + policy_mgr_init_ap_6ghz_capable(psoc, session_id, + conn_6ghz_flag); + + policy_mgr_dump_current_concurrency(psoc); + + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); +} + +QDF_STATUS policy_mgr_decr_active_session(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, + uint8_t session_id) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + QDF_STATUS qdf_status; + bool mcc_mode; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return QDF_STATUS_E_EMPTY; + } + + qdf_status = policy_mgr_check_conn_with_mode_and_vdev_id(psoc, + policy_mgr_convert_device_mode_to_qdf_type(mode), + session_id); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + policy_mgr_debug("No connection with mode:%d vdev_id:%d", + policy_mgr_convert_device_mode_to_qdf_type(mode), + session_id); + return qdf_status; + } + + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + case QDF_IBSS_MODE: + case QDF_NAN_DISC_MODE: + case QDF_NDI_MODE: + if (pm_ctx->no_of_active_sessions[mode]) + pm_ctx->no_of_active_sessions[mode]--; + break; + default: + break; + } + + if (mode != QDF_NAN_DISC_MODE && + pm_ctx->dp_cbacks.hdd_v2_flow_pool_unmap) + pm_ctx->dp_cbacks.hdd_v2_flow_pool_unmap(session_id); + + if (mode == QDF_NDI_MODE && + pm_ctx->hdd_cbacks.wlan_hdd_indicate_active_ndp_cnt) + pm_ctx->hdd_cbacks.wlan_hdd_indicate_active_ndp_cnt( + psoc, session_id, + pm_ctx->no_of_active_sessions[mode]); + + policy_mgr_debug("No.# of active sessions for mode %d = %d", + mode, pm_ctx->no_of_active_sessions[mode]); + + policy_mgr_decr_connection_count(psoc, session_id); + + /* Notify tdls */ + if (pm_ctx->tdls_cbacks.tdls_notify_decrement_session) + pm_ctx->tdls_cbacks.tdls_notify_decrement_session(psoc); + /* Enable LRO/GRO if there no concurrency */ + if ((policy_mgr_get_connection_count(psoc) == 0) || + ((policy_mgr_mode_specific_connection_count(psoc, + PM_STA_MODE, + NULL) == 1) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, + NULL) == 0) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_P2P_CLIENT_MODE, + NULL) == 0) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_P2P_GO_MODE, + NULL) == 0) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_IBSS_MODE, + NULL) == 0) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_NDI_MODE, + NULL) == 0))) { + if (pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency) + pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency(false); + }; + + /* Disable RPS if SAP interface has come up */ + if (policy_mgr_mode_specific_connection_count(psoc, PM_SAP_MODE, NULL) + == 0) { + if (pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb) + pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb(false); + } + + policy_mgr_dump_current_concurrency(psoc); + + /* + * Check mode of entry being removed. Update mcc_mode only when STA + * or SAP since IPA only cares about these two + */ + if (mode == QDF_STA_MODE || mode == QDF_SAP_MODE) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + mcc_mode = policy_mgr_current_concurrency_is_mcc(psoc); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb) + pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb(mcc_mode); + } + /* + * When STA disconnected, we need to move DFS SAP + * to Non-DFS if g_sta_sap_scc_on_dfs_chan enabled. + * The same if g_sta_sap_scc_on_lte_coex_chan enabled, + * need to move SAP on unsafe channel to safe channel. + * The flag will be checked by + * policy_mgr_is_sap_restart_required_after_sta_disconnect. + */ + if (mode == QDF_STA_MODE || mode == QDF_P2P_CLIENT_MODE) + pm_ctx->do_sap_unsafe_ch_check = true; + + return qdf_status; +} + +QDF_STATUS policy_mgr_incr_connection_count(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + enum QDF_OPMODE op_mode) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index; + struct policy_mgr_vdev_entry_info conn_table_entry = {0}; + enum policy_mgr_chain_mode chain_mask = POLICY_MGR_ONE_ONE; + uint8_t nss_2g = 0, nss_5g = 0; + enum policy_mgr_con_mode mode; + uint32_t ch_freq; + uint32_t nss = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool update_conn = true; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return status; + } + + conn_index = policy_mgr_get_connection_count(psoc); + if (pm_ctx->cfg.max_conc_cxns < conn_index) { + policy_mgr_err("exceeded max connection limit %d", + pm_ctx->cfg.max_conc_cxns); + return status; + } + + if (op_mode == QDF_NAN_DISC_MODE) { + status = wlan_nan_get_connection_info(psoc, &conn_table_entry); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Can't get NAN Connection info"); + return status; + } + } else if (pm_ctx->wma_cbacks.wma_get_connection_info) { + status = pm_ctx->wma_cbacks.wma_get_connection_info( + vdev_id, &conn_table_entry); + if (QDF_STATUS_SUCCESS != status) { + policy_mgr_err("can't find vdev_id %d in connection table", + vdev_id); + return status; + } + } else { + policy_mgr_err("wma_get_connection_info is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = policy_mgr_get_mode(conn_table_entry.type, + conn_table_entry.sub_type); + ch_freq = conn_table_entry.mhz; + status = policy_mgr_get_nss_for_vdev(psoc, mode, &nss_2g, &nss_5g); + if (QDF_IS_STATUS_SUCCESS(status)) { + if ((WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq) && nss_2g > 1) || + (WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq) && nss_5g > 1)) + chain_mask = POLICY_MGR_TWO_TWO; + else + chain_mask = POLICY_MGR_ONE_ONE; + nss = (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) ? nss_2g : nss_5g; + } else { + policy_mgr_err("Error in getting nss"); + } + + if (mode == PM_STA_MODE || mode == PM_P2P_CLIENT_MODE) + update_conn = false; + + /* add the entry */ + policy_mgr_update_conc_list(psoc, conn_index, + mode, + ch_freq, + policy_mgr_get_bw(conn_table_entry.chan_width), + conn_table_entry.mac_id, + chain_mask, + nss, vdev_id, true, update_conn, + conn_table_entry.ch_flagext); + policy_mgr_debug("Add at idx:%d vdev %d mac=%d", + conn_index, vdev_id, + conn_table_entry.mac_id); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_decr_connection_count(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0, next_conn_index = 0; + bool found = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (vdev_id == pm_conc_connection_list[conn_index].vdev_id) { + /* debug msg */ + found = true; + break; + } + conn_index++; + } + if (!found) { + policy_mgr_err("can't find vdev_id %d in pm_conc_connection_list", + vdev_id); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return status; + } + next_conn_index = conn_index + 1; + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(next_conn_index)) { + pm_conc_connection_list[conn_index].vdev_id = + pm_conc_connection_list[next_conn_index].vdev_id; + pm_conc_connection_list[conn_index].mode = + pm_conc_connection_list[next_conn_index].mode; + pm_conc_connection_list[conn_index].mac = + pm_conc_connection_list[next_conn_index].mac; + pm_conc_connection_list[conn_index].freq = + pm_conc_connection_list[next_conn_index].freq; + pm_conc_connection_list[conn_index].bw = + pm_conc_connection_list[next_conn_index].bw; + pm_conc_connection_list[conn_index].chain_mask = + pm_conc_connection_list[next_conn_index].chain_mask; + pm_conc_connection_list[conn_index].original_nss = + pm_conc_connection_list[next_conn_index].original_nss; + pm_conc_connection_list[conn_index].in_use = + pm_conc_connection_list[next_conn_index].in_use; + pm_conc_connection_list[conn_index].ch_flagext = + pm_conc_connection_list[next_conn_index].ch_flagext; + conn_index++; + next_conn_index++; + } + + /* clean up the entry */ + qdf_mem_zero(&pm_conc_connection_list[next_conn_index - 1], + sizeof(*pm_conc_connection_list)); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_map_concurrency_mode(enum QDF_OPMODE *old_mode, + enum policy_mgr_con_mode *new_mode) +{ + bool status = true; + + switch (*old_mode) { + + case QDF_STA_MODE: + *new_mode = PM_STA_MODE; + break; + case QDF_SAP_MODE: + *new_mode = PM_SAP_MODE; + break; + case QDF_P2P_CLIENT_MODE: + *new_mode = PM_P2P_CLIENT_MODE; + break; + case QDF_P2P_GO_MODE: + *new_mode = PM_P2P_GO_MODE; + break; + case QDF_IBSS_MODE: + *new_mode = PM_IBSS_MODE; + break; + case QDF_NAN_DISC_MODE: + *new_mode = PM_NAN_DISC_MODE; + break; + case QDF_NDI_MODE: + *new_mode = PM_NDI_MODE; + break; + default: + *new_mode = PM_MAX_NUM_OF_MODE; + status = false; + break; + } + + return status; +} + +bool policy_mgr_is_ibss_conn_exist(struct wlan_objmgr_psoc *psoc, + uint32_t *ibss_ch_freq) +{ + uint32_t count = 0, index = 0; + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + bool status = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + if (!ibss_ch_freq) { + policy_mgr_err("Null pointer error"); + return false; + } + count = policy_mgr_mode_specific_connection_count( + psoc, PM_IBSS_MODE, list); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (count == 0) { + /* No IBSS connection */ + status = false; + } else if (count == 1) { + *ibss_ch_freq = pm_conc_connection_list[list[index]].freq; + status = true; + } else { + *ibss_ch_freq = pm_conc_connection_list[list[index]].freq; + policy_mgr_debug("Multiple IBSS connections, picking first one"); + status = true; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +uint32_t policy_mgr_get_mode_specific_conn_info( + struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq_list, uint8_t *vdev_id, + enum policy_mgr_con_mode mode) +{ + + uint32_t count = 0, index = 0; + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return count; + } + if (!vdev_id) { + policy_mgr_err("Null pointer error"); + return count; + } + + count = policy_mgr_mode_specific_connection_count( + psoc, mode, list); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (count == 1) { + if (ch_freq_list) + *ch_freq_list = + pm_conc_connection_list[list[index]].freq; + *vdev_id = + pm_conc_connection_list[list[index]].vdev_id; + } else { + for (index = 0; index < count; index++) { + if (ch_freq_list) + ch_freq_list[index] = + pm_conc_connection_list[list[index]].freq; + + vdev_id[index] = + pm_conc_connection_list[list[index]].vdev_id; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return count; +} + +bool policy_mgr_max_concurrent_connections_reached( + struct wlan_objmgr_psoc *psoc) +{ + uint8_t i = 0, j = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (pm_ctx) { + for (i = 0; i < QDF_MAX_NO_OF_MODE; i++) + j += pm_ctx->no_of_active_sessions[i]; + return j > + (pm_ctx->cfg.max_conc_cxns - 1); + } + + return false; +} + +static bool policy_mgr_is_sub_20_mhz_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + return pm_ctx->user_cfg.sub_20_mhz_enabled; +} + +/** + * policy_mgr_check_privacy_for_new_conn() - Check privacy mode concurrency + * @pm_ctx: policy_mgr_psoc_priv_obj policy mgr context + * + * This routine is called to check vdev security mode allowed in concurrency. + * At present, WAPI security mode is not allowed to run concurrency with any + * other vdev. + * + * Return: true - allow + */ +static bool policy_mgr_check_privacy_for_new_conn( + struct policy_mgr_psoc_priv_obj *pm_ctx) +{ + struct wlan_objmgr_pdev *pdev = pm_ctx->pdev; + + if (!pdev) { + policy_mgr_debug("pdev is Null"); + return true; + } + + if (mlme_is_wapi_sta_active(pdev) && + policy_mgr_get_connection_count(pm_ctx->psoc) > 0) + return false; + + return true; + +} + +#ifdef FEATURE_FOURTH_CONNECTION +static bool policy_mgr_is_concurrency_allowed_4_port( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, + struct policy_mgr_pcl_list pcl) +{ + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t sap_cnt, go_cnt; + + /* new STA may just have ssid, no channel until bssid assigned */ + if (ch_freq == 0 && mode == PM_STA_MODE) + return true; + + sap_cnt = policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, NULL); + + go_cnt = policy_mgr_mode_specific_connection_count(psoc, + PM_P2P_GO_MODE, NULL); + if (sap_cnt || go_cnt) { + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return false; + } + + if (policy_mgr_get_mcc_to_scc_switch_mode(psoc) != + QDF_MCC_TO_SCC_SWITCH_FORCE_WITHOUT_DISCONNECTION) { + policy_mgr_err("couldn't start 4th port for bad force scc cfg"); + return false; + } + if (pm_ctx->cfg.dual_mac_feature || + !pm_ctx->cfg.sta_sap_scc_on_dfs_chnl || + !pm_ctx->cfg.sta_sap_scc_on_lte_coex_chnl) { + policy_mgr_err( + "Couldn't start 4th port for bad cfg of dual mac, dfs scc, lte coex scc"); + return false; + } + + for (i = 0; i < pcl.pcl_len; i++) + if (ch_freq == pcl.pcl_list[i]) + return true; + + policy_mgr_err("4th port failed on ch freq %d with mode %d", + ch_freq, mode); + + return false; + } + + return true; +} +#else +static inline bool policy_mgr_is_concurrency_allowed_4_port( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, + struct policy_mgr_pcl_list pcl) +{return false; } +#endif + +/** + * policy_mgr_allow_multiple_sta_connections() - check whether multiple STA + * concurrency is allowed and F/W supported + * @psoc: Pointer to soc + * @second_sta_freq: 2nd STA channel frequency + * @first_sta_freq: 1st STA channel frequency + * + * Return: true if supports else false. + */ +static bool policy_mgr_allow_multiple_sta_connections(struct wlan_objmgr_psoc *psoc, + uint32_t second_sta_freq, + uint32_t first_sta_freq) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + policy_mgr_debug("Invalid WMI handle"); + return false; + } + if (!wmi_service_enabled(wmi_handle, + wmi_service_sta_plus_sta_support)) + return false; + + return true; +} + +#if defined(CONFIG_BAND_6GHZ) && defined(WLAN_FEATURE_11AX) +bool policy_mgr_is_6ghz_conc_mode_supported( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode) +{ + if (mode == PM_STA_MODE || mode == PM_SAP_MODE) + return true; + else + return false; +} +#endif + +/** + * policy_mgr_is_6g_channel_allowed() - Check new 6Ghz connection + * allowed or not + * @psoc: Pointer to soc + * @mode: new connection mode + * @ch_freq: channel freq + * + * 1. Only STA/SAP are allowed on 6Ghz. + * 2. If there is DFS beacon entity existing on 5G band, 5G+6G MCC is not + * allowed. + * + * Return: true if supports else false. + */ +static bool policy_mgr_is_6g_channel_allowed( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode, + uint32_t ch_freq) +{ + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct policy_mgr_conc_connection_info *conn; + bool is_dfs; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + if (!WLAN_REG_IS_6GHZ_CHAN_FREQ(ch_freq)) + return true; + + /* Only STA/SAP is supported on 6Ghz currently */ + if (!policy_mgr_is_6ghz_conc_mode_supported(psoc, mode)) + return false; + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + conn = &pm_conc_connection_list[conn_index]; + if (!conn->in_use) + continue; + is_dfs = (conn->ch_flagext & + (IEEE80211_CHAN_DFS | IEEE80211_CHAN_DFS_CFREQ2)) && + WLAN_REG_IS_5GHZ_CH_FREQ(conn->freq); + if ((conn->mode == PM_SAP_MODE || + conn->mode == PM_P2P_GO_MODE) && + is_dfs && ch_freq != conn->freq) { + policy_mgr_rl_debug("don't allow MCC if SAP/GO on DFS channel"); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return false; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return true; +} + +bool policy_mgr_is_concurrency_allowed(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, + enum hw_mode_bandwidth bw) +{ + uint32_t num_connections = 0, count = 0, index = 0; + bool status = false, match = false; + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool sta_sap_scc_on_dfs_chan; + bool go_force_scc; + uint32_t sta_freq; + enum channel_state chan_state; + bool is_dfs_ch = false; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + /* find the current connection state from pm_conc_connection_list*/ + num_connections = policy_mgr_get_connection_count(psoc); + + if (num_connections && policy_mgr_is_sub_20_mhz_enabled(psoc)) { + policy_mgr_rl_debug("dont allow concurrency if Sub 20 MHz is enabled"); + status = false; + goto done; + } + + if (policy_mgr_max_concurrent_connections_reached(psoc)) { + policy_mgr_rl_debug("Reached max concurrent connections: %d", + pm_ctx->cfg.max_conc_cxns); + goto done; + } + + if (ch_freq) { + chan_state = wlan_reg_get_5g_bonded_channel_state_for_freq( + pm_ctx->pdev, ch_freq, + policy_mgr_get_ch_width(bw)); + if (chan_state == CHANNEL_STATE_DFS) + is_dfs_ch = true; + /* don't allow 3rd home channel on same MAC */ + if (!policy_mgr_allow_new_home_channel(psoc, mode, ch_freq, + num_connections, + is_dfs_ch)) + goto done; + + /* + * 1) DFS MCC is not yet supported + * 2) If you already have STA connection on 5G channel then + * don't allow any other persona to make connection on DFS + * channel because STA 5G + DFS MCC is not allowed. + * 3) If STA is on 2G channel and SAP is coming up on + * DFS channel then allow concurrency but make sure it is + * going to DBS and send PCL to firmware indicating that + * don't allow STA to roam to 5G channels. + * 4) On a single MAC device, if a SAP/P2PGO is already on a DFS + * channel, don't allow a 2 channel as it will result + * in MCC which is not allowed. + */ + if (!policy_mgr_is_5g_channel_allowed(psoc, + ch_freq, list, PM_P2P_GO_MODE)) + goto done; + if (!policy_mgr_is_5g_channel_allowed(psoc, + ch_freq, list, PM_SAP_MODE)) + goto done; + if (!policy_mgr_is_6g_channel_allowed(psoc, mode, + ch_freq)) + goto done; + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + go_force_scc = policy_mgr_go_scc_enforced(psoc); + if ((!sta_sap_scc_on_dfs_chan && mode == PM_SAP_MODE) || + (!go_force_scc && mode == PM_P2P_GO_MODE)) { + if (is_dfs_ch) + match = policy_mgr_disallow_mcc(psoc, + ch_freq); + } + if (true == match) { + policy_mgr_rl_debug("No MCC, SAP/GO about to come up on DFS channel"); + goto done; + } + if ((policy_mgr_is_hw_dbs_capable(psoc) != true) && + num_connections) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) { + if (policy_mgr_is_sap_p2pgo_on_dfs(psoc)) { + policy_mgr_rl_debug("MCC not allowed: SAP/P2PGO on DFS"); + goto done; + } + } + } + } + + /* Check for STA+STA concurrency */ + count = policy_mgr_mode_specific_connection_count(psoc, PM_STA_MODE, + list); + if (mode == PM_STA_MODE && count) { + if (count >= 2) { + policy_mgr_rl_debug("3rd STA isn't permitted"); + goto done; + } + sta_freq = pm_conc_connection_list[list[0]].freq; + if (!policy_mgr_allow_multiple_sta_connections(psoc, ch_freq, + sta_freq)) + goto done; + } + + /* + * Check all IBSS+STA concurrencies + * + * don't allow IBSS + STA MCC + * don't allow IBSS + STA SCC if IBSS is on DFS channel + */ + if ((PM_IBSS_MODE == mode) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_IBSS_MODE, list)) && count) { + policy_mgr_rl_debug("No 2nd IBSS, we already have STA + IBSS"); + goto done; + } + if ((PM_IBSS_MODE == mode) && + (wlan_reg_is_dfs_for_freq(pm_ctx->pdev, ch_freq)) && count) { + policy_mgr_rl_debug("No IBSS + STA SCC/MCC, IBSS is on DFS channel"); + goto done; + } + if (PM_IBSS_MODE == mode) { + if (policy_mgr_is_hw_dbs_capable(psoc) == true) { + if (num_connections > 1) { + policy_mgr_rl_debug("No IBSS, we have concurrent connections already"); + goto done; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (PM_STA_MODE != pm_conc_connection_list[0].mode) { + policy_mgr_rl_debug("No IBSS, we've a non-STA connection"); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + /* + * This logic protects STA and IBSS to come up on same + * band. If requirement changes then this condition + * needs to be removed + */ + if (ch_freq && + pm_conc_connection_list[0].freq != ch_freq && + WLAN_REG_IS_SAME_BAND_FREQS( + pm_conc_connection_list[0].freq, ch_freq)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_rl_debug("No IBSS + STA MCC"); + goto done; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + } else if (num_connections) { + policy_mgr_rl_debug("No IBSS, we have one connection already"); + goto done; + } + } + + if ((PM_STA_MODE == mode) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_IBSS_MODE, list)) && count) { + policy_mgr_rl_debug("No 2nd STA, we already have STA + IBSS"); + goto done; + } + + if ((PM_STA_MODE == mode) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_IBSS_MODE, list))) { + if (policy_mgr_is_hw_dbs_capable(psoc) == true) { + if (num_connections > 1) { + policy_mgr_rl_debug("No 2nd STA, we already have IBSS concurrency"); + goto done; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (ch_freq && + (wlan_reg_is_dfs_for_freq(pm_ctx->pdev, + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq))) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_rl_debug("No IBSS + STA SCC/MCC, IBSS is on DFS channel"); + goto done; + } + /* + * This logic protects STA and IBSS to come up on same + * band. If requirement changes then this condition + * needs to be removed + */ + if (pm_conc_connection_list[0].freq != ch_freq && + WLAN_REG_IS_SAME_BAND_FREQS( + pm_conc_connection_list[0].freq, ch_freq)) { + policy_mgr_rl_debug("No IBSS + STA MCC"); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + } else { + policy_mgr_rl_debug("No STA, we have IBSS connection already"); + goto done; + } + } + + if (!policy_mgr_allow_sap_go_concurrency(psoc, mode, ch_freq, + WLAN_INVALID_VDEV_ID)) { + policy_mgr_rl_debug("This concurrency combination is not allowed"); + goto done; + } + /* don't allow two P2P GO on same band */ + if (ch_freq && mode == PM_P2P_GO_MODE && num_connections) { + index = 0; + count = policy_mgr_mode_specific_connection_count( + psoc, PM_P2P_GO_MODE, list); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (index < count) { + if (WLAN_REG_IS_SAME_BAND_FREQS( + ch_freq, + pm_conc_connection_list[list[index]].freq)) { + policy_mgr_rl_debug("Don't allow P2P GO on same band"); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + } + + if (!policy_mgr_check_privacy_for_new_conn(pm_ctx)) { + policy_mgr_rl_debug("Don't allow new conn when wapi security conn existing"); + goto done; + } + + status = true; + +done: + return status; +} + +bool policy_mgr_allow_concurrency(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, + enum hw_mode_bandwidth bw) +{ + QDF_STATUS status; + struct policy_mgr_pcl_list pcl; + bool allowed; + + qdf_mem_zero(&pcl, sizeof(pcl)); + status = policy_mgr_get_pcl(psoc, mode, pcl.pcl_list, &pcl.pcl_len, + pcl.weight_list, + QDF_ARRAY_SIZE(pcl.weight_list)); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("disallow connection:%d", status); + return false; + } + + allowed = policy_mgr_is_concurrency_allowed(psoc, mode, ch_freq, bw); + + /* Fourth connection concurrency check */ + if (allowed && policy_mgr_get_connection_count(psoc) == 3) + allowed = policy_mgr_is_concurrency_allowed_4_port( + psoc, + mode, + ch_freq, + pcl); + return allowed; +} + +bool +policy_mgr_allow_concurrency_csa(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, uint32_t vdev_id, + bool forced, enum sap_csa_reason_code reason) +{ + bool allow = false; + struct policy_mgr_conc_connection_info + info[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t num_cxn_del = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t old_ch_freq; + QDF_STATUS status; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return allow; + } + policy_mgr_debug("check concurrency_csa vdev:%d ch %d, forced %d, reason %d", + vdev_id, ch_freq, forced, reason); + + status = policy_mgr_get_chan_by_session_id(psoc, vdev_id, + &old_ch_freq); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Failed to get channel for vdev:%d", + vdev_id); + return allow; + } + qdf_mem_zero(info, sizeof(info)); + + /* + * Store the connection's parameter and temporarily delete it + * from the concurrency table. This way the allow concurrency + * check can be used as though a new connection is coming up, + * after check, restore the connection to concurrency table. + * + * In SAP+SAP SCC case, when LTE unsafe event processing, + * we should remove the all SAP conn entry on the same ch before + * do the concurrency check. Otherwise the left SAP on old channel + * will cause the concurrency check failure because of dual beacon + * MCC not supported. for the CSA request reason code, + * PM_CSA_REASON_UNSAFE_CHANNEL, we remove all the SAP + * entry on old channel before do concurrency check. + * + * The assumption is both SAP should move to the new channel later for + * the reason code. + */ + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + + if (forced && reason == CSA_REASON_UNSAFE_CHANNEL) + policy_mgr_store_and_del_conn_info_by_chan_and_mode( + psoc, old_ch_freq, mode, info, &num_cxn_del); + else + policy_mgr_store_and_del_conn_info_by_vdev_id( + psoc, vdev_id, info, &num_cxn_del); + + allow = policy_mgr_allow_concurrency(psoc, mode, ch_freq, + HW_MODE_20_MHZ); + /* Restore the connection entry */ + if (num_cxn_del > 0) + policy_mgr_restore_deleted_conn_info(psoc, info, num_cxn_del); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (!allow) + policy_mgr_err("CSA concurrency check failed"); + + return allow; +} + +/** + * policy_mgr_get_concurrency_mode() - return concurrency mode + * @psoc: PSOC object information + * + * This routine is used to retrieve concurrency mode + * + * Return: uint32_t value of concurrency mask + */ +uint32_t policy_mgr_get_concurrency_mode(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STA_MASK; + } + + policy_mgr_debug("concurrency_mode: 0x%x", + pm_ctx->concurrency_mode); + + return pm_ctx->concurrency_mode; +} + +/** + * policy_mgr_get_channel_from_scan_result() - to get channel from scan result + * @psoc: PSOC object information + * @roam_profile: pointer to roam profile + * @channel: channel to be filled + * + * This routine gets channel which most likely a candidate to which STA + * will make connection. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_channel_from_scan_result( + struct wlan_objmgr_psoc *psoc, + void *roam_profile, uint32_t *ch_freq) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + void *scan_cache = NULL; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_INVAL; + } + + if (!roam_profile || !ch_freq) { + policy_mgr_err("Invalid input parameters"); + return QDF_STATUS_E_INVAL; + } + + if (pm_ctx->sme_cbacks.sme_get_ap_channel_from_scan) { + status = pm_ctx->sme_cbacks.sme_get_ap_channel_from_scan + (roam_profile, &scan_cache, ch_freq); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Get AP channel failed"); + return status; + } + } else { + policy_mgr_err("sme_get_ap_channel_from_scan_cache NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (pm_ctx->sme_cbacks.sme_scan_result_purge) + status = pm_ctx->sme_cbacks.sme_scan_result_purge(scan_cache); + else + policy_mgr_err("sme_scan_result_purge NULL"); + + return status; +} + +QDF_STATUS policy_mgr_set_user_cfg(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_user_cfg *user_cfg) +{ + + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + if (!user_cfg) { + policy_mgr_err("Invalid User Config"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->user_cfg = *user_cfg; + policy_mgr_debug("dbs_selection_plcy 0x%x", + pm_ctx->cfg.dbs_selection_plcy); + policy_mgr_debug("vdev_priority_list 0x%x", + pm_ctx->cfg.vdev_priority_list); + pm_ctx->cur_conc_system_pref = pm_ctx->cfg.sys_pref; + + return QDF_STATUS_SUCCESS; +} + +uint32_t policy_mgr_search_and_check_for_session_conc( + struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + void *roam_profile) +{ + uint32_t ch_freq = 0; + QDF_STATUS status; + enum policy_mgr_con_mode mode; + bool ret; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return ch_freq; + } + + if (pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev) { + mode = pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev( + psoc, session_id); + if (PM_MAX_NUM_OF_MODE == mode) { + policy_mgr_err("Invalid mode"); + return ch_freq; + } + } else + return ch_freq; + + status = policy_mgr_get_channel_from_scan_result( + psoc, roam_profile, &ch_freq); + if (QDF_STATUS_SUCCESS != status || ch_freq == 0) { + policy_mgr_err("%s error %d %d", + __func__, status, ch_freq); + return 0; + } + + /* Take care of 160MHz and 80+80Mhz later */ + ret = policy_mgr_allow_concurrency(psoc, mode, ch_freq, + HW_MODE_20_MHZ); + if (false == ret) { + policy_mgr_err("Connection failed due to conc check fail"); + return 0; + } + + return ch_freq; +} + +/** + * policy_mgr_is_two_connection_mcc() - Check if MCC scenario + * when there are two connections + * + * If if MCC scenario when there are two connections + * + * Return: true or false + */ +static bool policy_mgr_is_two_connection_mcc(void) +{ + return ((pm_conc_connection_list[0].freq != + pm_conc_connection_list[1].freq) && + (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) && + (pm_conc_connection_list[0].freq <= + WLAN_REG_MAX_24GHZ_CHAN_FREQ) && + (pm_conc_connection_list[1].freq <= + WLAN_REG_MAX_24GHZ_CHAN_FREQ)) ? true : false; +} + +/** + * policy_mgr_is_three_connection_mcc() - Check if MCC scenario + * when there are three connections + * + * If if MCC scenario when there are three connections + * + * Return: true or false + */ +static bool policy_mgr_is_three_connection_mcc(void) +{ + return (((pm_conc_connection_list[0].freq != + pm_conc_connection_list[1].freq) || + (pm_conc_connection_list[0].freq != + pm_conc_connection_list[2].freq) || + (pm_conc_connection_list[1].freq != + pm_conc_connection_list[2].freq)) && + (pm_conc_connection_list[0].freq <= + WLAN_REG_MAX_24GHZ_CHAN_FREQ) && + (pm_conc_connection_list[1].freq <= + WLAN_REG_MAX_24GHZ_CHAN_FREQ) && + (pm_conc_connection_list[2].freq <= + WLAN_REG_MAX_24GHZ_CHAN_FREQ)) ? true : false; +} + +bool policy_mgr_is_mcc_in_24G(struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections = 0; + bool is_24G_mcc = false; + + num_connections = policy_mgr_get_connection_count(psoc); + + switch (num_connections) { + case 1: + break; + case 2: + if (policy_mgr_is_two_connection_mcc()) + is_24G_mcc = true; + break; + case 3: + if (policy_mgr_is_three_connection_mcc()) + is_24G_mcc = true; + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + break; + } + + return is_24G_mcc; +} + +bool policy_mgr_check_for_session_conc(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint32_t ch_freq) +{ + enum policy_mgr_con_mode mode; + bool ret; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + if (pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev) { + mode = pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev( + psoc, session_id); + if (PM_MAX_NUM_OF_MODE == mode) { + policy_mgr_err("Invalid mode"); + return false; + } + } else + return false; + + if (ch_freq == 0) { + policy_mgr_err("Invalid channel number 0"); + return false; + } + + /* Take care of 160MHz and 80+80Mhz later */ + ret = policy_mgr_allow_concurrency(psoc, mode, ch_freq, HW_MODE_20_MHZ); + if (false == ret) { + policy_mgr_err("Connection failed due to conc check fail"); + return 0; + } + + return true; +} + +/** + * policy_mgr_change_mcc_go_beacon_interval() - Change MCC beacon interval + * @psoc: PSOC object information + * @vdev_id: vdev id + * @dev_mode: device mode + * + * Updates the beacon parameters of the GO in MCC scenario + * + * Return: Success or Failure depending on the overall function behavior + */ +QDF_STATUS policy_mgr_change_mcc_go_beacon_interval( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, enum QDF_OPMODE dev_mode) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_debug("UPDATE Beacon Params"); + + if (QDF_SAP_MODE == dev_mode) { + if (pm_ctx->sme_cbacks.sme_change_mcc_beacon_interval + ) { + status = pm_ctx->sme_cbacks. + sme_change_mcc_beacon_interval(vdev_id); + if (status == QDF_STATUS_E_FAILURE) { + policy_mgr_err("Failed to update Beacon Params"); + return QDF_STATUS_E_FAILURE; + } + } else { + policy_mgr_err("sme_change_mcc_beacon_interval callback is NULL"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +struct policy_mgr_conc_connection_info *policy_mgr_get_conn_info(uint32_t *len) +{ + struct policy_mgr_conc_connection_info *conn_ptr = + &pm_conc_connection_list[0]; + *len = MAX_NUMBER_OF_CONC_CONNECTIONS; + + return conn_ptr; +} + +enum policy_mgr_con_mode policy_mgr_convert_device_mode_to_qdf_type( + enum QDF_OPMODE device_mode) +{ + enum policy_mgr_con_mode mode = PM_MAX_NUM_OF_MODE; + switch (device_mode) { + case QDF_STA_MODE: + mode = PM_STA_MODE; + break; + case QDF_P2P_CLIENT_MODE: + mode = PM_P2P_CLIENT_MODE; + break; + case QDF_P2P_GO_MODE: + mode = PM_P2P_GO_MODE; + break; + case QDF_SAP_MODE: + mode = PM_SAP_MODE; + break; + case QDF_IBSS_MODE: + mode = PM_IBSS_MODE; + break; + case QDF_NAN_DISC_MODE: + mode = PM_NAN_DISC_MODE; + break; + case QDF_NDI_MODE: + mode = PM_NDI_MODE; + break; + default: + policy_mgr_debug("Unsupported mode (%d)", + device_mode); + } + + return mode; +} + +enum QDF_OPMODE policy_mgr_get_qdf_mode_from_pm( + enum policy_mgr_con_mode device_mode) +{ + enum QDF_OPMODE mode = QDF_MAX_NO_OF_MODE; + + switch (device_mode) { + case PM_STA_MODE: + mode = QDF_STA_MODE; + break; + case PM_SAP_MODE: + mode = QDF_SAP_MODE; + break; + case PM_P2P_CLIENT_MODE: + mode = QDF_P2P_CLIENT_MODE; + break; + case PM_P2P_GO_MODE: + mode = QDF_P2P_GO_MODE; + break; + case PM_IBSS_MODE: + mode = QDF_IBSS_MODE; + break; + case PM_NAN_DISC_MODE: + mode = QDF_NAN_DISC_MODE; + break; + case PM_NDI_MODE: + mode = QDF_NDI_MODE; + break; + default: + policy_mgr_debug("Unsupported policy mgr mode (%d)", + device_mode); + } + return mode; +} + +QDF_STATUS policy_mgr_mode_specific_num_open_sessions( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE mode, + uint8_t *num_sessions) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + *num_sessions = pm_ctx->no_of_open_sessions[mode]; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_mode_specific_num_active_sessions( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE mode, + uint8_t *num_sessions) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + *num_sessions = pm_ctx->no_of_active_sessions[mode]; + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_concurrent_open_sessions_running() - Checks for + * concurrent open session + * @psoc: PSOC object information + * + * Checks if more than one open session is running for all the allowed modes + * in the driver + * + * Return: True if more than one open session exists, False otherwise + */ +bool policy_mgr_concurrent_open_sessions_running( + struct wlan_objmgr_psoc *psoc) +{ + uint8_t i = 0; + uint8_t j = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return false; + } + + for (i = 0; i < QDF_MAX_NO_OF_MODE; i++) + j += pm_ctx->no_of_open_sessions[i]; + + return j > 1; +} + +/** + * policy_mgr_concurrent_beaconing_sessions_running() - Checks + * for concurrent beaconing entities + * @psoc: PSOC object information + * + * Checks if multiple beaconing sessions are running i.e., if SAP or GO or IBSS + * are beaconing together + * + * Return: True if multiple entities are beaconing together, False otherwise + */ +bool policy_mgr_concurrent_beaconing_sessions_running( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return false; + } + + return (pm_ctx->no_of_open_sessions[QDF_SAP_MODE] + + pm_ctx->no_of_open_sessions[QDF_P2P_GO_MODE] + + pm_ctx->no_of_open_sessions[QDF_IBSS_MODE] > 1) ? true : false; +} + + +void policy_mgr_clear_concurrent_session_count(struct wlan_objmgr_psoc *psoc) +{ + uint8_t i = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (pm_ctx) { + for (i = 0; i < QDF_MAX_NO_OF_MODE; i++) + pm_ctx->no_of_active_sessions[i] = 0; + } +} + +bool policy_mgr_is_multiple_active_sta_sessions(struct wlan_objmgr_psoc *psoc) +{ + return policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL) > 1; +} + +/** + * policy_mgr_is_sta_active_connection_exists() - Check if a STA + * connection is active + * @psoc: PSOC object information + * + * Checks if there is atleast one active STA connection in the driver + * + * Return: True if an active STA session is present, False otherwise + */ +bool policy_mgr_is_sta_active_connection_exists( + struct wlan_objmgr_psoc *psoc) +{ + return (!policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL)) ? false : true; +} + +bool policy_mgr_is_any_nondfs_chnl_present(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq) +{ + bool status = false; + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].in_use && + !wlan_reg_is_dfs_for_freq(pm_ctx->pdev, + pm_conc_connection_list[conn_index].freq)) { + *ch_freq = pm_conc_connection_list[conn_index].freq; + status = true; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +uint32_t policy_mgr_get_dfs_beaconing_session_id( + struct wlan_objmgr_psoc *psoc) +{ + uint32_t session_id = WLAN_UMAC_VDEV_ID_MAX; + struct policy_mgr_conc_connection_info *conn_info; + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return session_id; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + conn_info = &pm_conc_connection_list[conn_index]; + if (conn_info->in_use && + wlan_reg_chan_has_dfs_attribute_for_freq( + pm_ctx->pdev, conn_info->freq) && + (conn_info->mode == PM_SAP_MODE || + conn_info->mode == PM_P2P_GO_MODE)) { + session_id = + pm_conc_connection_list[conn_index].vdev_id; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return session_id; +} + +bool policy_mgr_is_any_dfs_beaconing_session_present( + struct wlan_objmgr_psoc *psoc, uint32_t *ch_freq) +{ + struct policy_mgr_conc_connection_info *conn_info; + bool status = false; + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + conn_info = &pm_conc_connection_list[conn_index]; + if (conn_info->in_use && + wlan_reg_is_dfs_for_freq(pm_ctx->pdev, conn_info->freq) && + (PM_SAP_MODE == conn_info->mode || + PM_P2P_GO_MODE == conn_info->mode)) { + *ch_freq = pm_conc_connection_list[conn_index].freq; + status = true; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +bool policy_mgr_scan_trim_5g_chnls_for_dfs_ap(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t ap_dfs_ch_freq = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + policy_mgr_is_any_dfs_beaconing_session_present(psoc, &ap_dfs_ch_freq); + if (!ap_dfs_ch_freq) + return false; + /* + * 1) if agile & DFS scans are supportet + * 2) if hardware is DBS capable + * 3) if current hw mode is non-dbs + * if all above 3 conditions are true then don't skip any + * channel from scan list + */ + if (policy_mgr_is_hw_dbs_capable(psoc) && + !policy_mgr_is_current_hwmode_dbs(psoc) && + policy_mgr_get_dbs_plus_agile_scan_config(psoc) && + policy_mgr_get_single_mac_scan_with_dfs_config(psoc)) + return false; + + policy_mgr_debug("scan skip 5g chan due to dfs ap(ch %d) present", + ap_dfs_ch_freq); + + return true; +} + +QDF_STATUS policy_mgr_get_nss_for_vdev(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t *nss_2g, uint8_t *nss_5g) +{ + enum QDF_OPMODE dev_mode; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + dev_mode = policy_mgr_get_qdf_mode_from_pm(mode); + if (dev_mode == QDF_MAX_NO_OF_MODE) + return QDF_STATUS_E_FAILURE; + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (pm_ctx->sme_cbacks.sme_get_nss_for_vdev) { + pm_ctx->sme_cbacks.sme_get_nss_for_vdev( + dev_mode, nss_2g, nss_5g); + + } else { + policy_mgr_err("sme_get_nss_for_vdev callback is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_dump_connection_status_info(struct wlan_objmgr_psoc *psoc) +{ + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + policy_mgr_debug("%d: use:%d vdev:%d mode:%d mac:%d freq:%d orig chainmask:%d orig nss:%d bw:%d, ch_flags %0X", + i, pm_conc_connection_list[i].in_use, + pm_conc_connection_list[i].vdev_id, + pm_conc_connection_list[i].mode, + pm_conc_connection_list[i].mac, + pm_conc_connection_list[i].freq, + pm_conc_connection_list[i].chain_mask, + pm_conc_connection_list[i].original_nss, + pm_conc_connection_list[i].bw, + pm_conc_connection_list[i].ch_flagext); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); +} + +bool policy_mgr_is_any_mode_active_on_band_along_with_session( + struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + enum policy_mgr_band band) +{ + uint32_t i; + bool status = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + status = false; + goto send_status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + switch (band) { + case POLICY_MGR_BAND_24: + if ((pm_conc_connection_list[i].vdev_id != session_id) + && (pm_conc_connection_list[i].in_use) && + (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[i].freq))) { + status = true; + goto release_mutex_and_send_status; + } + break; + case POLICY_MGR_BAND_5: + if ((pm_conc_connection_list[i].vdev_id != session_id) + && (pm_conc_connection_list[i].in_use) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[i].freq))) { + status = true; + goto release_mutex_and_send_status; + } + break; + default: + policy_mgr_err("Invalidband option:%d", band); + status = false; + goto release_mutex_and_send_status; + } + } +release_mutex_and_send_status: + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); +send_status: + return status; +} + +QDF_STATUS policy_mgr_get_chan_by_session_id(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + uint32_t *ch_freq) +{ + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if ((pm_conc_connection_list[i].vdev_id == session_id) && + (pm_conc_connection_list[i].in_use)) { + *ch_freq = pm_conc_connection_list[i].freq; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return QDF_STATUS_SUCCESS; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return QDF_STATUS_E_FAILURE; +} + +QDF_STATUS policy_mgr_get_mac_id_by_session_id(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + uint8_t *mac_id) +{ + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if ((pm_conc_connection_list[i].vdev_id == session_id) && + (pm_conc_connection_list[i].in_use)) { + *mac_id = pm_conc_connection_list[i].mac; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return QDF_STATUS_SUCCESS; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return QDF_STATUS_E_FAILURE; +} + +QDF_STATUS policy_mgr_get_mcc_session_id_on_mac(struct wlan_objmgr_psoc *psoc, + uint8_t mac_id, uint8_t session_id, + uint8_t *mcc_session_id) +{ + uint32_t i, ch_freq; + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + status = policy_mgr_get_chan_by_session_id(psoc, session_id, &ch_freq); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Failed to get channel for session id:%d", + session_id); + return QDF_STATUS_E_FAILURE; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if (pm_conc_connection_list[i].mac != mac_id) + continue; + if (pm_conc_connection_list[i].vdev_id == session_id) + continue; + /* Inter band or intra band MCC */ + if (pm_conc_connection_list[i].freq != ch_freq && + pm_conc_connection_list[i].in_use) { + *mcc_session_id = pm_conc_connection_list[i].vdev_id; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return QDF_STATUS_SUCCESS; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return QDF_STATUS_E_FAILURE; +} + +uint32_t policy_mgr_get_mcc_operating_channel(struct wlan_objmgr_psoc *psoc, + uint8_t session_id) +{ + uint8_t mac_id, mcc_session_id; + QDF_STATUS status; + uint32_t ch_freq; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return INVALID_CHANNEL_ID; + } + + status = policy_mgr_get_mac_id_by_session_id(psoc, session_id, &mac_id); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get MAC ID"); + return INVALID_CHANNEL_ID; + } + + status = policy_mgr_get_mcc_session_id_on_mac(psoc, mac_id, session_id, + &mcc_session_id); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get MCC session ID"); + return INVALID_CHANNEL_ID; + } + + status = policy_mgr_get_chan_by_session_id(psoc, mcc_session_id, + &ch_freq); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Failed to get channel for MCC session ID:%d", + mcc_session_id); + return INVALID_CHANNEL_ID; + } + + return ch_freq; +} + +bool policy_mgr_is_dnsc_set(struct wlan_objmgr_vdev *vdev) +{ + bool roffchan; + + if (!vdev) { + policy_mgr_err("Invalid parameter"); + return false; + } + + roffchan = wlan_vdev_mlme_cap_get(vdev, WLAN_VDEV_C_RESTRICT_OFFCHAN); + + if (roffchan) + policy_mgr_debug("Restrict offchannel is set"); + + return roffchan; +} + +QDF_STATUS policy_mgr_is_chan_ok_for_dnbs(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq, bool *ok) +{ + uint32_t cc_count = 0, i; + uint32_t op_ch_freq_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct wlan_objmgr_vdev *vdev; + + if (!ok) { + policy_mgr_err("Invalid parameter"); + return QDF_STATUS_E_INVAL; + } + + cc_count = policy_mgr_get_mode_specific_conn_info( + psoc, &op_ch_freq_list[cc_count], + &vdev_id[cc_count], PM_SAP_MODE); + + if (cc_count < MAX_NUMBER_OF_CONC_CONNECTIONS) + cc_count = cc_count + + policy_mgr_get_mode_specific_conn_info( + psoc, &op_ch_freq_list[cc_count], + &vdev_id[cc_count], PM_P2P_GO_MODE); + + if (!cc_count) { + *ok = true; + return QDF_STATUS_SUCCESS; + } + + if (!ch_freq) { + policy_mgr_err("channel is 0, cc count %d", cc_count); + return QDF_STATUS_E_INVAL; + } + + if (cc_count <= MAX_NUMBER_OF_CONC_CONNECTIONS) { + for (i = 0; i < cc_count; i++) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, vdev_id[i], WLAN_POLICY_MGR_ID); + if (!vdev) { + policy_mgr_err("vdev for vdev_id:%d is NULL", + vdev_id[i]); + return QDF_STATUS_E_INVAL; + } + + /** + * If channel passed is same as AP/GO operating + * channel, return true. + * + * If channel is different from operating channel but + * in same band, return false. + * + * If operating channel in different band + * (DBS capable), return true. + * + * If operating channel in different band + * (not DBS capable), return false. + */ + /* TODO: To be enhanced for SBS */ + if (policy_mgr_is_dnsc_set(vdev)) { + if (op_ch_freq_list[i] == ch_freq) { + *ok = true; + wlan_objmgr_vdev_release_ref( + vdev, + WLAN_POLICY_MGR_ID); + break; + } else if (WLAN_REG_IS_SAME_BAND_FREQS( + op_ch_freq_list[i], ch_freq)) { + *ok = false; + wlan_objmgr_vdev_release_ref( + vdev, + WLAN_POLICY_MGR_ID); + break; + } else if (policy_mgr_is_hw_dbs_capable(psoc)) { + *ok = true; + wlan_objmgr_vdev_release_ref( + vdev, + WLAN_POLICY_MGR_ID); + break; + } else { + *ok = false; + wlan_objmgr_vdev_release_ref( + vdev, + WLAN_POLICY_MGR_ID); + break; + } + } else { + *ok = true; + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_POLICY_MGR_ID); + } + } + + return QDF_STATUS_SUCCESS; +} + +uint32_t policy_mgr_get_hw_dbs_nss(struct wlan_objmgr_psoc *psoc, + struct dbs_nss *nss_dbs) +{ + int i, param; + uint32_t dbs, sbs, tx_chain0, rx_chain0, tx_chain1, rx_chain1; + uint32_t min_mac0_rf_chains, min_mac1_rf_chains; + uint32_t max_rf_chains, final_max_rf_chains = HW_MODE_SS_0x0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return final_max_rf_chains; + } + + nss_dbs->single_mac0_band_cap = 0; + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + dbs = POLICY_MGR_HW_MODE_DBS_MODE_GET(param); + sbs = POLICY_MGR_HW_MODE_SBS_MODE_GET(param); + + if (!dbs && !sbs && !nss_dbs->single_mac0_band_cap) + nss_dbs->single_mac0_band_cap = + POLICY_MGR_HW_MODE_MAC0_BAND_GET(param); + + if (dbs) { + tx_chain0 + = POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET(param); + rx_chain0 + = POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET(param); + + tx_chain1 + = POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET(param); + rx_chain1 + = POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET(param); + + min_mac0_rf_chains = QDF_MIN(tx_chain0, rx_chain0); + min_mac1_rf_chains = QDF_MIN(tx_chain1, rx_chain1); + + max_rf_chains + = QDF_MAX(min_mac0_rf_chains, min_mac1_rf_chains); + + if (final_max_rf_chains < max_rf_chains) { + final_max_rf_chains + = (max_rf_chains == 2) + ? HW_MODE_SS_2x2 : HW_MODE_SS_1x1; + + nss_dbs->mac0_ss + = (min_mac0_rf_chains == 2) + ? HW_MODE_SS_2x2 : HW_MODE_SS_1x1; + + nss_dbs->mac1_ss + = (min_mac1_rf_chains == 2) + ? HW_MODE_SS_2x2 : HW_MODE_SS_1x1; + } + } else { + continue; + } + } + + return final_max_rf_chains; +} + +bool policy_mgr_is_scan_simultaneous_capable(struct wlan_objmgr_psoc *psoc) +{ + uint8_t dual_mac_feature = DISABLE_DBS_CXN_AND_SCAN; + + policy_mgr_get_dual_mac_feature(psoc, &dual_mac_feature); + if ((dual_mac_feature == DISABLE_DBS_CXN_AND_SCAN) || + (dual_mac_feature == ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN) || + (dual_mac_feature == + ENABLE_DBS_CXN_AND_DISABLE_SIMULTANEOUS_SCAN) || + !policy_mgr_is_hw_dbs_capable(psoc)) + return false; + + return true; +} + +void policy_mgr_set_cur_conc_system_pref(struct wlan_objmgr_psoc *psoc, + uint8_t conc_system_pref) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + policy_mgr_debug("conc_system_pref %hu", conc_system_pref); + pm_ctx->cur_conc_system_pref = conc_system_pref; +} + +uint8_t policy_mgr_get_cur_conc_system_pref(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return PM_THROUGHPUT; + } + + policy_mgr_debug("conc_system_pref %hu", pm_ctx->cur_conc_system_pref); + return pm_ctx->cur_conc_system_pref; +} + +QDF_STATUS policy_mgr_get_updated_scan_and_fw_mode_config( + struct wlan_objmgr_psoc *psoc, uint32_t *scan_config, + uint32_t *fw_mode_config, uint32_t dual_mac_disable_ini, + uint32_t channel_select_logic_conc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + *scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + *fw_mode_config = pm_ctx->dual_mac_cfg.cur_fw_mode_config; + switch (dual_mac_disable_ini) { + case DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN_WITH_ASYNC_SCAN_OFF: + policy_mgr_debug("dual_mac_disable_ini:%d async/dbs off", + dual_mac_disable_ini); + WMI_DBS_CONC_SCAN_CFG_ASYNC_DBS_SCAN_SET(*scan_config, 0); + WMI_DBS_FW_MODE_CFG_DBS_FOR_CXN_SET(*fw_mode_config, 0); + break; + case DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN: + policy_mgr_debug("dual_mac_disable_ini:%d dbs_cxn off", + dual_mac_disable_ini); + WMI_DBS_FW_MODE_CFG_DBS_FOR_CXN_SET(*fw_mode_config, 0); + break; + case ENABLE_DBS_CXN_AND_ENABLE_SCAN_WITH_ASYNC_SCAN_OFF: + policy_mgr_debug("dual_mac_disable_ini:%d async off", + dual_mac_disable_ini); + WMI_DBS_CONC_SCAN_CFG_ASYNC_DBS_SCAN_SET(*scan_config, 0); + break; + case ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN: + policy_mgr_debug("%s: dual_mac_disable_ini:%d ", __func__, + dual_mac_disable_ini); + WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_SET(*scan_config, 0); + break; + default: + break; + } + + WMI_DBS_FW_MODE_CFG_DBS_FOR_STA_PLUS_STA_SET(*fw_mode_config, + PM_CHANNEL_SELECT_LOGIC_STA_STA_GET(channel_select_logic_conc)); + WMI_DBS_FW_MODE_CFG_DBS_FOR_STA_PLUS_P2P_SET(*fw_mode_config, + PM_CHANNEL_SELECT_LOGIC_STA_P2P_GET(channel_select_logic_conc)); + + policy_mgr_debug("*scan_config:%x ", *scan_config); + policy_mgr_debug("*fw_mode_config:%x ", *fw_mode_config); + + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_is_force_scc(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + return ((pm_ctx->cfg.mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_WITHOUT_DISCONNECTION) || + (pm_ctx->cfg.mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_WITH_FAVORITE_CHANNEL) || + (pm_ctx->cfg.mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) || + (pm_ctx->cfg.mcc_to_scc_switch == + QDF_MCC_TO_SCC_WITH_PREFERRED_BAND)); +} + +bool policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t sta_sap_scc_on_dfs_chnl = 0; + bool status = false; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + policy_mgr_get_sta_sap_scc_on_dfs_chnl(psoc, + &sta_sap_scc_on_dfs_chnl); + if (policy_mgr_is_force_scc(psoc) && sta_sap_scc_on_dfs_chnl) + status = true; + + return status; +} + +bool policy_mgr_is_sta_connected_2g(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t conn_index; + bool ret = false; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return ret; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].mode == PM_STA_MODE && + pm_conc_connection_list[conn_index].freq <= + WLAN_REG_MAX_24GHZ_CHAN_FREQ && + pm_conc_connection_list[conn_index].in_use) + ret = true; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return ret; +} + +uint32_t policy_mgr_get_connection_info(struct wlan_objmgr_psoc *psoc, + struct connection_info *info) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t conn_index, count = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return count; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + info[count].vdev_id = + pm_conc_connection_list[conn_index].vdev_id; + info[count].mac_id = + pm_conc_connection_list[conn_index].mac; + info[count].channel = wlan_reg_freq_to_chan( + pm_ctx->pdev, + pm_conc_connection_list[conn_index].freq); + info[count].ch_freq = + pm_conc_connection_list[conn_index].freq; + count++; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return count; +} + +bool policy_mgr_allow_sap_go_concurrency(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, + uint32_t vdev_id) +{ + enum policy_mgr_con_mode con_mode; + int id; + uint32_t vdev, con_freq; + bool dbs; + + if (mode != PM_SAP_MODE && mode != PM_P2P_GO_MODE) + return true; + if (policy_mgr_dual_beacon_on_single_mac_mcc_capable(psoc)) + return true; + dbs = policy_mgr_is_hw_dbs_capable(psoc); + for (id = 0; id < MAX_NUMBER_OF_CONC_CONNECTIONS; id++) { + if (!pm_conc_connection_list[id].in_use) + continue; + vdev = pm_conc_connection_list[id].vdev_id; + if (vdev_id == vdev) + continue; + con_mode = pm_conc_connection_list[id].mode; + if (con_mode != PM_SAP_MODE && con_mode != PM_P2P_GO_MODE) + continue; + con_freq = pm_conc_connection_list[id].freq; + if (policy_mgr_dual_beacon_on_single_mac_scc_capable(psoc) && + (ch_freq == con_freq)) { + policy_mgr_debug("SCC enabled, 2 AP on same channel, allow 2nd AP"); + return true; + } + if (!dbs) { + policy_mgr_debug("DBS unsupported, mcc and scc unsupported too, don't allow 2nd AP"); + return false; + } + if (WLAN_REG_IS_SAME_BAND_FREQS(ch_freq, con_freq)) { + policy_mgr_debug("DBS supported, 2 SAP on same band, reject 2nd AP"); + return false; + } + } + + /* Don't block the second interface */ + return true; +} + +bool policy_mgr_dual_beacon_on_single_mac_scc_capable( + struct wlan_objmgr_psoc *psoc) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + policy_mgr_debug("Invalid WMI handle"); + return false; + } + + if (wmi_service_enabled( + wmi_handle, + wmi_service_dual_beacon_on_single_mac_scc_support)) { + policy_mgr_debug("Dual beaconing on same channel on single MAC supported"); + return true; + } + policy_mgr_debug("Dual beaconing on same channel on single MAC is not supported"); + return false; +} + +bool policy_mgr_dual_beacon_on_single_mac_mcc_capable( + struct wlan_objmgr_psoc *psoc) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + policy_mgr_debug("Invalid WMI handle"); + return false; + } + + if (wmi_service_enabled( + wmi_handle, + wmi_service_dual_beacon_on_single_mac_mcc_support)) { + policy_mgr_debug("Dual beaconing on different channel on single MAC supported"); + return true; + } + policy_mgr_debug("Dual beaconing on different channel on single MAC is not supported"); + return false; +} + +bool policy_mgr_sta_sap_scc_on_lte_coex_chan( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t scc_lte_coex = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + policy_mgr_get_sta_sap_scc_lte_coex_chnl(psoc, &scc_lte_coex); + + return scc_lte_coex; +} + +#if defined(CONFIG_BAND_6GHZ) && defined(WLAN_FEATURE_11AX) +void policy_mgr_init_ap_6ghz_capable(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum conn_6ghz_flag ap_6ghz_capable) +{ + struct policy_mgr_conc_connection_info *conn_info; + uint32_t conn_index; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum conn_6ghz_flag conn_6ghz_flag = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + conn_info = &pm_conc_connection_list[conn_index]; + if (conn_info->in_use && PM_SAP_MODE == conn_info->mode && + vdev_id == conn_info->vdev_id) { + conn_info->conn_6ghz_flag = ap_6ghz_capable; + conn_info->conn_6ghz_flag |= CONN_6GHZ_FLAG_VALID; + conn_6ghz_flag = conn_info->conn_6ghz_flag; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("vdev %d init conn_6ghz_flag %x new %x", + vdev_id, ap_6ghz_capable, conn_6ghz_flag); +} + +void policy_mgr_set_ap_6ghz_capable(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + bool set, + enum conn_6ghz_flag ap_6ghz_capable) +{ + struct policy_mgr_conc_connection_info *conn_info; + uint32_t conn_index; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum conn_6ghz_flag conn_6ghz_flag = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + conn_info = &pm_conc_connection_list[conn_index]; + if (conn_info->in_use && PM_SAP_MODE == conn_info->mode && + vdev_id == conn_info->vdev_id) { + if (set) + conn_info->conn_6ghz_flag |= ap_6ghz_capable; + else + conn_info->conn_6ghz_flag &= ~ap_6ghz_capable; + conn_info->conn_6ghz_flag |= CONN_6GHZ_FLAG_VALID; + conn_6ghz_flag = conn_info->conn_6ghz_flag; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("vdev %d %s conn_6ghz_flag %x new %x", + vdev_id, set ? "set" : "clr", + ap_6ghz_capable, conn_6ghz_flag); +} + +bool policy_mgr_get_ap_6ghz_capable(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + uint32_t *conn_flag) +{ + struct policy_mgr_conc_connection_info *conn_info; + uint32_t conn_index; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum conn_6ghz_flag conn_6ghz_flag = 0; + bool is_6g_allowed = false; + + if (conn_flag) + *conn_flag = 0; + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + conn_info = &pm_conc_connection_list[conn_index]; + if (conn_info->in_use && PM_SAP_MODE == conn_info->mode && + vdev_id == conn_info->vdev_id) { + conn_6ghz_flag = conn_info->conn_6ghz_flag; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + /* If the vdev connection is not active, policy mgr will query legacy + * hdd to get sap acs and security information. + * The assumption is no legacy client connected for non active + * connection. + */ + if (!(conn_6ghz_flag & CONN_6GHZ_FLAG_VALID) && + pm_ctx->hdd_cbacks.hdd_get_ap_6ghz_capable) + conn_6ghz_flag = pm_ctx->hdd_cbacks.hdd_get_ap_6ghz_capable( + psoc, vdev_id) | + CONN_6GHZ_FLAG_NO_LEGACY_CLIENT; + + if ((conn_6ghz_flag & CONN_6GHZ_CAPABLIE) == CONN_6GHZ_CAPABLIE) + is_6g_allowed = true; + policy_mgr_debug("vdev %d conn_6ghz_flag %x 6ghz %s", vdev_id, + conn_6ghz_flag, is_6g_allowed ? "allowed" : "deny"); + if (conn_flag) + *conn_flag = conn_6ghz_flag; + + return is_6g_allowed; +} +#endif + +bool policy_mgr_is_valid_for_channel_switch(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq) +{ + uint32_t sta_sap_scc_on_dfs_chan; + uint32_t sap_count; + enum channel_state state; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + sap_count = policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, + NULL); + state = wlan_reg_get_channel_state_for_freq(pm_ctx->pdev, ch_freq); + + policy_mgr_debug("sta_sap_scc_on_dfs_chan %u, sap_count %u, ch freq %u, state %u", + sta_sap_scc_on_dfs_chan, sap_count, ch_freq, state); + + if ((state == CHANNEL_STATE_ENABLE) || (sap_count == 0) || + ((state == CHANNEL_STATE_DFS) && sta_sap_scc_on_dfs_chan)) { + policy_mgr_debug("Valid channel for channel switch"); + return true; + } + + policy_mgr_debug("Invalid channel for channel switch"); + return false; +} + +bool policy_mgr_is_sta_sap_scc(struct wlan_objmgr_psoc *psoc, + uint32_t sap_freq) +{ + uint32_t conn_index; + bool is_scc = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return is_scc; + } + + if (!policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL)) { + policy_mgr_debug("There is no STA+SAP conc"); + return is_scc; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].in_use && + (pm_conc_connection_list[conn_index].mode == + PM_STA_MODE) && (sap_freq == + pm_conc_connection_list[conn_index].freq)) { + is_scc = true; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return is_scc; +} + +bool policy_mgr_go_scc_enforced(struct wlan_objmgr_psoc *psoc) +{ + uint32_t mcc_to_scc_switch; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + mcc_to_scc_switch = policy_mgr_get_mcc_to_scc_switch_mode(psoc); + if (mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) + return true; + + if (pm_ctx->cfg.go_force_scc && policy_mgr_is_force_scc(psoc)) + return true; + + return false; +} + +QDF_STATUS policy_mgr_update_nan_vdev_mac_info(struct wlan_objmgr_psoc *psoc, + uint8_t nan_vdev_id, + uint8_t mac_id) +{ + struct policy_mgr_hw_mode_params hw_mode = {0}; + struct policy_mgr_vdev_mac_map vdev_mac_map = {0}; + QDF_STATUS status; + + vdev_mac_map.vdev_id = nan_vdev_id; + vdev_mac_map.mac_id = mac_id; + + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + + if (QDF_IS_STATUS_SUCCESS(status)) + policy_mgr_update_hw_mode_conn_info(psoc, 1, &vdev_mac_map, + hw_mode); + + return status; +} + +bool policy_mgr_is_sap_go_on_2g(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t conn_index; + bool ret = false; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return ret; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].mode == PM_SAP_MODE || + pm_conc_connection_list[conn_index].mode == PM_P2P_GO_MODE) && + pm_conc_connection_list[conn_index].freq <= + WLAN_REG_MAX_24GHZ_CHAN_FREQ && + pm_conc_connection_list[conn_index].in_use) + ret = true; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return ret; +} + +bool policy_mgr_get_5g_scc_prefer( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + return pm_ctx->cfg.prefer_5g_scc_to_dbs & (1 << mode); +} diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h new file mode 100644 index 0000000000000000000000000000000000000000..07f28f25cf00b750fe8ca3a7e6aec85d8d09ef89 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h @@ -0,0 +1,710 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_POLICY_MGR_I_H +#define WLAN_POLICY_MGR_I_H + +#include "wlan_policy_mgr_api.h" +#include "qdf_event.h" +#include "qdf_mc_timer.h" +#include "qdf_lock.h" +#include "qdf_defer.h" +#include "wlan_reg_services_api.h" +#include "cds_ieee80211_common_i.h" + +#define DBS_OPPORTUNISTIC_TIME 5 + +#define POLICY_MGR_SER_CMD_TIMEOUT 4000 + +#ifdef QCA_WIFI_3_0_EMU +#define CONNECTION_UPDATE_TIMEOUT (POLICY_MGR_SER_CMD_TIMEOUT + 3000) +#else +#define CONNECTION_UPDATE_TIMEOUT (POLICY_MGR_SER_CMD_TIMEOUT + 2000) +#endif + +#define PM_24_GHZ_CH_FREQ_6 (2437) +#define PM_5_GHZ_CH_FREQ_36 (5180) +#define CHANNEL_SWITCH_COMPLETE_TIMEOUT (2000) + +/** + * Policy Mgr hardware mode list bit-mask definitions. + * Bits 4:0, 31:29 are unused. + * + * The below definitions are added corresponding to WMI DBS HW mode + * list to make it independent of firmware changes for WMI definitions. + * Currently these definitions have dependency with BIT positions of + * the existing WMI macros. Thus, if the BIT positions are changed for + * WMI macros, then these macros' BIT definitions are also need to be + * changed. + */ +#define POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_BITPOS (28) +#define POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_BITPOS (24) +#define POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_BITPOS (20) +#define POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_BITPOS (16) +#define POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_BITPOS (12) +#define POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_BITPOS (8) +#define POLICY_MGR_HW_MODE_DBS_MODE_BITPOS (7) +#define POLICY_MGR_HW_MODE_AGILE_DFS_MODE_BITPOS (6) +#define POLICY_MGR_HW_MODE_SBS_MODE_BITPOS (5) +#define POLICY_MGR_HW_MODE_MAC0_BAND_BITPOS (3) +#define POLICY_MGR_HW_MODE_ID_BITPOS (0) + +#define POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_BITPOS) +#define POLICY_MGR_HW_MODE_DBS_MODE_MASK \ + (0x1 << POLICY_MGR_HW_MODE_DBS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_AGILE_DFS_MODE_MASK \ + (0x1 << POLICY_MGR_HW_MODE_AGILE_DFS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_SBS_MODE_MASK \ + (0x1 << POLICY_MGR_HW_MODE_SBS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_BAND_MASK \ + (0x3 << POLICY_MGR_HW_MODE_MAC0_BAND_BITPOS) +#define POLICY_MGR_HW_MODE_ID_MASK \ + (0x7 << POLICY_MGR_HW_MODE_ID_BITPOS) + +#define POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_DBS_MODE_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_DBS_MODE_BITPOS,\ + 1, value) +#define POLICY_MGR_HW_MODE_AGILE_DFS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_AGILE_DFS_MODE_BITPOS,\ + 1, value) +#define POLICY_MGR_HW_MODE_SBS_MODE_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_SBS_MODE_BITPOS,\ + 1, value) +#define POLICY_MGR_HW_MODE_MAC0_BAND_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC0_BAND_BITPOS,\ + 2, value) +#define POLICY_MGR_HW_MODE_ID_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_ID_BITPOS,\ + 3, value) + +#define POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_MASK) >> \ + POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_MASK) >> \ + POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_MASK) >> \ + POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_MASK) >> \ + POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_MASK) >> \ + POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_MASK) >> \ + POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_BITPOS) +#define POLICY_MGR_HW_MODE_DBS_MODE_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_DBS_MODE_MASK) >> \ + POLICY_MGR_HW_MODE_DBS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_AGILE_DFS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_AGILE_DFS_MODE_MASK) >> \ + POLICY_MGR_HW_MODE_AGILE_DFS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_SBS_MODE_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_SBS_MODE_MASK) >> \ + POLICY_MGR_HW_MODE_SBS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_BAND_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC0_BAND_MASK) >> \ + POLICY_MGR_HW_MODE_MAC0_BAND_BITPOS) +#define POLICY_MGR_HW_MODE_ID_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_ID_MASK) >> \ + POLICY_MGR_HW_MODE_ID_BITPOS) + +#define POLICY_MGR_DEFAULT_HW_MODE_INDEX 0xFFFF + +#define policy_mgr_alert(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_warn(params...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_notice(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_info(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_debug(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_POLICY_MGR, params) + +#define policymgr_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) +#define policymgr_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) +#define policymgr_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) +#define policymgr_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) +#define policymgr_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) + +#define policy_mgr_rl_debug(params...) \ + QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_POLICY_MGR, params) + +#define PM_CONC_CONNECTION_LIST_VALID_INDEX(index) \ + ((MAX_NUMBER_OF_CONC_CONNECTIONS > index) && \ + (pm_conc_connection_list[index].in_use)) + +extern struct policy_mgr_conc_connection_info + pm_conc_connection_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + +extern const enum policy_mgr_pcl_type + first_connection_pcl_table[PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE]; +extern enum policy_mgr_pcl_type + (*second_connection_pcl_dbs_table)[PM_MAX_ONE_CONNECTION_MODE] + [PM_MAX_NUM_OF_MODE][PM_MAX_CONC_PRIORITY_MODE]; +extern pm_dbs_pcl_third_connection_table_type + *third_connection_pcl_dbs_table; +extern policy_mgr_next_action_two_connection_table_type + *next_action_two_connection_table; +extern policy_mgr_next_action_three_connection_table_type + *next_action_three_connection_table; + +#ifdef FEATURE_FOURTH_CONNECTION +extern const enum policy_mgr_pcl_type + fourth_connection_pcl_dbs_table + [PM_MAX_THREE_CONNECTION_MODE][PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE]; +#endif + +extern policy_mgr_next_action_two_connection_table_type + *next_action_two_connection_2x2_2g_1x1_5g_table; +extern policy_mgr_next_action_three_connection_table_type + *next_action_three_connection_2x2_2g_1x1_5g_table; + +extern enum policy_mgr_conc_next_action + (*policy_mgr_get_current_pref_hw_mode_ptr) + (struct wlan_objmgr_psoc *psoc); + +/** + * struct sta_ap_intf_check_work_ctx - sta_ap_intf_check_work + * related info + * @psoc: pointer to PSOC object information + */ +struct sta_ap_intf_check_work_ctx { + struct wlan_objmgr_psoc *psoc; +}; + +/** + * struct policy_mgr_cfg - all the policy manager owned configs + * @mcc_to_scc_switch: switch to indicate MCC to SCC config + * @sys_pref: system's preference while selecting PCLs + * @max_conc_cxns: Max allowed concurrenct active connections + * @conc_rule1: concurrency rule1 + * @conc_rule2: concurrency rule2 + * @allow_mcc_go_diff_bi: Allow GO and STA diff beacon interval in MCC + * @enable_overlap_chnl: Enable overlap channels for SAP's channel selection + * @dual_mac_feature: To enable/disable dual mac features + * @is_force_1x1_enable: Is 1x1 forced for connection + * @sta_sap_scc_on_dfs_chnl: STA-SAP SCC on DFS channel + * @sta_sap_scc_on_lte_coex_chnl: STA-SAP SCC on LTE Co-ex channel + * @nan_sap_scc_on_lte_coex_chnl: NAN-SAP SCC on LTE Co-ex channel + * @sap_mandatory_chnl_enable: To enable/disable SAP mandatory channels + * @mark_indoor_chnl_disable: Mark indoor channel as disable or enable + * @dbs_selection_plcy: DBS selection policy for concurrency + * @vdev_priority_list: Priority list for various vdevs + * @chnl_select_plcy: Channel selection policy + * @enable_mcc_adaptive_sch: Enable/Disable MCC adaptive scheduler + * @enable_sta_cxn_5g_band: Enable/Disable STA connection in 5G band + * @go_force_scc: Enable/Disable P2P GO force SCC + * @pcl_band_priority: PCL channel order between 5G and 6G. + * @prefer_5g_scc_to_dbs: Prefer to work in 5G SCC mode. + */ +struct policy_mgr_cfg { + uint8_t mcc_to_scc_switch; + uint8_t sys_pref; + uint8_t max_conc_cxns; + uint8_t conc_rule1; + uint8_t conc_rule2; + uint8_t enable_mcc_adaptive_sch; + uint8_t allow_mcc_go_diff_bi; + uint8_t enable_overlap_chnl; + uint8_t dual_mac_feature; + enum force_1x1_type is_force_1x1_enable; + uint8_t sta_sap_scc_on_dfs_chnl; + uint8_t sta_sap_scc_on_lte_coex_chnl; + uint8_t nan_sap_scc_on_lte_coex_chnl; + uint8_t sap_mandatory_chnl_enable; + uint8_t mark_indoor_chnl_disable; + uint8_t enable_sta_cxn_5g_band; + uint32_t dbs_selection_plcy; + uint32_t vdev_priority_list; + uint32_t chnl_select_plcy; + uint8_t go_force_scc; + enum policy_mgr_pcl_band_priority pcl_band_priority; + uint32_t prefer_5g_scc_to_dbs; +}; + +/** + * struct policy_mgr_psoc_priv_obj - Policy manager private data + * @psoc: pointer to PSOC object information + * @pdev: pointer to PDEV object information + * @connection_update_done_evt: qdf event to synchronize + * connection activities + * @qdf_conc_list_lock: To protect connection table + * @dbs_opportunistic_timer: Timer to drop down to Single Mac + * Mode opportunistically + * @sap_restart_chan_switch_cb: Callback for channel switch + * notification for SAP + * @sme_cbacks: callbacks to be registered by SME for + * interaction with Policy Manager + * @wma_cbacks: callbacks to be registered by SME for + * interaction with Policy Manager + * @tdls_cbacks: callbacks to be registered by SME for + * interaction with Policy Manager + * @cdp_cbacks: callbacks to be registered by SME for + * interaction with Policy Manager + * @sap_mandatory_channels: The user preferred master list on + * which SAP can be brought up. This + * mandatory channel freq list would be as per + * OEMs preference & conforming to the + * regulatory/other considerations + * @sap_mandatory_channels_len: Length of the SAP mandatory + * channel list + * @concurrency_mode: active concurrency combination + * @no_of_open_sessions: Number of active vdevs + * @no_of_active_sessions: Number of active connections + * @sta_ap_intf_check_work: delayed sap restart work + * @num_dbs_hw_modes: Number of different HW modes supported + * @hw_mode: List of HW modes supported + * @old_hw_mode_index: Old HW mode from hw_mode table + * @new_hw_mode_index: New HW mode from hw_mode table + * @dual_mac_cfg: DBS configuration currenctly used by FW for + * scan & connections + * @hw_mode_change_in_progress: This is to track if HW mode + * change is in progress + * @enable_mcc_adaptive_scheduler: Enable MCC adaptive scheduler + * value from INI + * @unsafe_channel_list: LTE coex channel freq avoidance list + * @unsafe_channel_count: LTE coex channel avoidance list count + * @sta_ap_intf_check_work_info: Info related to sta_ap_intf_check_work + * @nan_sap_conc_work: Info related to nan sap conc work + * @opportunistic_update_done_evt: qdf event to synchronize host + * & FW HW mode + * @channel_switch_complete_evt: qdf event for channel switch completion check + * @mode_change_cb: Mode change callback + * @user_config_sap_ch_freq: SAP channel freq configured by user application + * @cfg: Policy manager config data + * @dynamic_mcc_adaptive_sched: disable/enable mcc adaptive scheduler feature + */ +struct policy_mgr_psoc_priv_obj { + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + qdf_event_t connection_update_done_evt; + qdf_mutex_t qdf_conc_list_lock; + qdf_mc_timer_t dbs_opportunistic_timer; + struct policy_mgr_hdd_cbacks hdd_cbacks; + struct policy_mgr_sme_cbacks sme_cbacks; + struct policy_mgr_wma_cbacks wma_cbacks; + struct policy_mgr_tdls_cbacks tdls_cbacks; + struct policy_mgr_cdp_cbacks cdp_cbacks; + struct policy_mgr_dp_cbacks dp_cbacks; + uint32_t sap_mandatory_channels[NUM_CHANNELS]; + uint32_t sap_mandatory_channels_len; + bool do_sap_unsafe_ch_check; + uint32_t concurrency_mode; + uint8_t no_of_open_sessions[QDF_MAX_NO_OF_MODE]; + uint8_t no_of_active_sessions[QDF_MAX_NO_OF_MODE]; + qdf_work_t sta_ap_intf_check_work; + qdf_work_t nan_sap_conc_work; + uint32_t num_dbs_hw_modes; + struct dbs_hw_mode_info hw_mode; + uint32_t old_hw_mode_index; + uint32_t new_hw_mode_index; + struct dual_mac_config dual_mac_cfg; + uint32_t hw_mode_change_in_progress; + struct policy_mgr_user_cfg user_cfg; + uint32_t unsafe_channel_list[NUM_CHANNELS]; + uint16_t unsafe_channel_count; + struct sta_ap_intf_check_work_ctx *sta_ap_intf_check_work_info; + uint8_t cur_conc_system_pref; + qdf_event_t opportunistic_update_done_evt; + qdf_event_t channel_switch_complete_evt; + send_mode_change_event_cb mode_change_cb; + uint32_t user_config_sap_ch_freq; + struct policy_mgr_cfg cfg; + uint32_t valid_ch_freq_list[NUM_CHANNELS]; + uint32_t valid_ch_freq_list_count; + bool dynamic_mcc_adaptive_sched; +}; + +/** + * struct policy_mgr_mac_ss_bw_info - hw_mode_list PHY/MAC params for each MAC + * @mac_tx_stream: Max TX stream number supported on MAC + * @mac_rx_stream: Max RX stream number supported on MAC + * @mac_bw: Max bandwidth(wmi_channel_width enum type) + * @mac_band_cap: supported Band bit map(WLAN_2G_CAPABILITY = 0x1, + * WLAN_5G_CAPABILITY = 0x2) + */ +struct policy_mgr_mac_ss_bw_info { + uint32_t mac_tx_stream; + uint32_t mac_rx_stream; + uint32_t mac_bw; + uint32_t mac_band_cap; +}; + +struct policy_mgr_psoc_priv_obj *policy_mgr_get_context( + struct wlan_objmgr_psoc *psoc); +QDF_STATUS policy_mgr_get_updated_scan_config( + struct wlan_objmgr_psoc *psoc, + uint32_t *scan_config, + bool dbs_scan, + bool dbs_plus_agile_scan, + bool single_mac_scan_with_dfs); +QDF_STATUS policy_mgr_get_updated_fw_mode_config( + struct wlan_objmgr_psoc *psoc, + uint32_t *fw_mode_config, + bool dbs, + bool agile_dfs); +bool policy_mgr_is_dual_mac_disabled_in_ini( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_find_if_hwlist_has_dbs() - Find if hw list has DBS modes or not + * @psoc: PSOC object information + * + * Find if hw list has DBS modes or not + * + * Return: true or false + */ +bool policy_mgr_find_if_hwlist_has_dbs(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_mcc_to_scc_switch_mode() - MCC to SCC + * switch mode value in the user config + * @psoc: PSOC object information + * + * MCC to SCC switch mode value in user config + * + * Return: MCC to SCC switch mode value + */ +uint32_t policy_mgr_get_mcc_to_scc_switch_mode( + struct wlan_objmgr_psoc *psoc); +bool policy_mgr_get_dbs_config(struct wlan_objmgr_psoc *psoc); +bool policy_mgr_get_agile_dfs_config(struct wlan_objmgr_psoc *psoc); +bool policy_mgr_get_dbs_scan_config(struct wlan_objmgr_psoc *psoc); +void policy_mgr_get_tx_rx_ss_from_config(enum hw_mode_ss_config mac_ss, + uint32_t *tx_ss, uint32_t *rx_ss); +int8_t policy_mgr_get_matching_hw_mode_index( + struct wlan_objmgr_psoc *psoc, + uint32_t mac0_tx_ss, uint32_t mac0_rx_ss, + enum hw_mode_bandwidth mac0_bw, + uint32_t mac1_tx_ss, uint32_t mac1_rx_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs); +int8_t policy_mgr_get_hw_mode_idx_from_dbs_hw_list( + struct wlan_objmgr_psoc *psoc, + enum hw_mode_ss_config mac0_ss, + enum hw_mode_bandwidth mac0_bw, + enum hw_mode_ss_config mac1_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs); +QDF_STATUS policy_mgr_get_old_and_new_hw_index( + struct wlan_objmgr_psoc *psoc, + uint32_t *old_hw_mode_index, + uint32_t *new_hw_mode_index); + +/** + * policy_mgr_update_conc_list() - Update the concurrent connection list + * @conn_index: Connection index + * @mode: Mode + * @ch_freq: channel frequency + * @bw: Bandwidth + * @mac: Mac id + * @chain_mask: Chain mask + * @vdev_id: vdev id + * @in_use: Flag to indicate if the index is in use or not + * @update_conn: Flag to indicate if mode change event should + * be sent or not + * @ch_flagext: channel state flags + * + * Updates the index value of the concurrent connection list + * + * Return: None + */ +void policy_mgr_update_conc_list(struct wlan_objmgr_psoc *psoc, + uint32_t conn_index, + enum policy_mgr_con_mode mode, + uint32_t freq, + enum hw_mode_bandwidth bw, + uint8_t mac, + enum policy_mgr_chain_mode chain_mask, + uint32_t original_nss, + uint32_t vdev_id, + bool in_use, + bool update_conn, + uint16_t ch_flagext); + +void policy_mgr_store_and_del_conn_info(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + bool all_matching_cxn_to_del, + struct policy_mgr_conc_connection_info *info, + uint8_t *num_cxn_del); + +/** + * policy_mgr_store_and_del_conn_info_by_vdev_id() - Store and del a + * connection info by vdev id + * @psoc: PSOC object information + * @vdev_id: vdev id whose entry has to be deleted + * @info: structure array pointer where the connection info will be saved + * @num_cxn_del: number of connection which are going to be deleted + * + * Saves the connection info corresponding to the provided mode + * and deleted that corresponding entry based on vdev from the + * connection info structure + * + * Return: None + */ +void policy_mgr_store_and_del_conn_info_by_vdev_id( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + struct policy_mgr_conc_connection_info *info, + uint8_t *num_cxn_del); + +/** + * policy_mgr_store_and_del_conn_info_by_chan_and_mode() - Store and del a + * connection info by chan number and conn mode + * @psoc: PSOC object information + * @ch_freq: channel frequency value + * @mode: conn mode + * @info: structure array pointer where the connection info will be saved + * @num_cxn_del: number of connection which are going to be deleted + * + * Saves and deletes the entries if the active connection entry chan and mode + * matches the provided chan & mode from the function parameters. + * + * Return: None + */ +void policy_mgr_store_and_del_conn_info_by_chan_and_mode( + struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq, + enum policy_mgr_con_mode mode, + struct policy_mgr_conc_connection_info *info, + uint8_t *num_cxn_del); + +void policy_mgr_restore_deleted_conn_info(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_conc_connection_info *info, + uint8_t num_cxn_del); +void policy_mgr_update_hw_mode_conn_info(struct wlan_objmgr_psoc *psoc, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + struct policy_mgr_hw_mode_params hw_mode); +void policy_mgr_pdev_set_hw_mode_cb(uint32_t status, + uint32_t cfgd_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id, void *context); +void policy_mgr_dump_current_concurrency(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_pdev_get_pcl() - GET PCL channel list + * @psoc: PSOC object information + * @mode: Adapter mode + * @pcl: the pointer of pcl list + * + * Fetches the PCL. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_pdev_get_pcl(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, + struct policy_mgr_pcl_list *pcl); +void pm_dbs_opportunistic_timer_handler(void *data); +enum policy_mgr_con_mode policy_mgr_get_mode(uint8_t type, + uint8_t subtype); +enum hw_mode_bandwidth policy_mgr_get_bw(enum phy_ch_width chan_width); + +/** + * policy_mgr_get_bw() - Convert hw_mode_bandwidth to phy_ch_width + * @bw: Hardware mode band width used by WMI + * + * Return: phy_ch_width + */ +enum phy_ch_width policy_mgr_get_ch_width(enum hw_mode_bandwidth bw); + +QDF_STATUS policy_mgr_get_channel_list(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_pcl_type pcl, + uint32_t *pcl_channels, uint32_t *len, + enum policy_mgr_con_mode mode, + uint8_t *pcl_weights, uint32_t weight_len); + +/** + * policy_mgr_allow_new_home_channel() - Check for allowed number of + * home channels + * @psoc: PSOC Pointer + * @mode: Connection mode + * @ch_freq: channel frequency on which new connection is coming up + * @num_connections: number of current connections + * @is_dfs_ch: DFS channel or not + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * allowed or not based on the HW capability + * + * Return: True/False + */ +bool policy_mgr_allow_new_home_channel( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode, + uint32_t ch_freq, uint32_t num_connections, bool is_dfs_ch); + +/** + * policy_mgr_is_5g_channel_allowed() - check if 5g channel is allowed + * @ch_freq: channel frequency which needs to be validated + * @list: list of existing connections. + * @mode: mode against which channel needs to be validated + * + * This API takes the channel frequency as input and compares with existing + * connection channels. If existing connection's channel is DFS channel + * and provided channel is 5G channel then don't allow concurrency to + * happen as MCC with DFS channel is not yet supported + * + * Return: true if 5G channel is allowed, false if not allowed + * + */ +bool policy_mgr_is_5g_channel_allowed(struct wlan_objmgr_psoc *psoc, + uint32_t ch_freq, uint32_t *list, + enum policy_mgr_con_mode mode); + +QDF_STATUS policy_mgr_complete_action(struct wlan_objmgr_psoc *psoc, + uint8_t new_nss, uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id); +enum policy_mgr_con_mode policy_mgr_get_mode_by_vdev_id( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); +QDF_STATUS policy_mgr_init_connection_update( + struct policy_mgr_psoc_priv_obj *pm_ctx); +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dbs_2x2( + struct wlan_objmgr_psoc *psoc); +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dbs_1x1( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_current_pref_hw_mode_dual_dbs() - Get the + * current preferred hw mode + * + * Get the preferred hw mode based on the current connection combinations + * + * Return: No change (PM_NOP), (PM_SINGLE_MAC_UPGRADE), + * DBS (PM_DBS1_DOWNGRADE or PM_DBS2_DOWNGRADE) + */ +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dual_dbs( + struct wlan_objmgr_psoc *psoc); + +QDF_STATUS policy_mgr_reset_sap_mandatory_channels( + struct policy_mgr_psoc_priv_obj *pm_ctx); + +/** + * policy_mgr_reg_chan_change_callback() - Callback to be + * invoked by regulatory module when valid channel list changes + * @psoc: PSOC object information + * @pdev: PDEV object information + * @chan_list: New channel list + * @avoid_freq_ind: LTE coex avoid channel list + * @arg: Information passed at registration + * + * Get updated channel list from regulatory module + * + * Return: None + */ +void policy_mgr_reg_chan_change_callback(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev, + struct regulatory_channel *chan_list, + struct avoid_freq_ind_data *avoid_freq_ind, + void *arg); + +/** + * policy_mgr_nss_update() - update nss for AP vdev + * @psoc: PSOC object information + * @new_nss: new NSS value + * @next_action: Next action after nss update + * @band: update AP vdev on the Band. + * @reason: action reason + * @original_vdev_id: original request hwmode change vdev id + * + * The function will update AP vdevs on specific band. + * eg. band = POLICY_MGR_ANY will request to update all band (2g and 5g) + * + * Return: QDF_STATUS_SUCCESS, update requested successfully. + */ +QDF_STATUS policy_mgr_nss_update(struct wlan_objmgr_psoc *psoc, + uint8_t new_nss, uint8_t next_action, + enum policy_mgr_band band, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id); + +/** + * policy_mgr_is_concurrency_allowed() - Check for allowed + * concurrency combination + * @psoc: PSOC object information + * @mode: new connection mode + * @ch_freq: channel frequency on which new connection is coming up + * @bw: Bandwidth requested by the connection (optional) + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * allowed or not based on the HW capability, but no need to + * invoke get_pcl + * + * Return: True/False + */ +bool policy_mgr_is_concurrency_allowed(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t ch_freq, + enum hw_mode_bandwidth bw); +#endif diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_init_deinit.c b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_init_deinit.c new file mode 100644 index 0000000000000000000000000000000000000000..585067da092cec1f5068e7d9f2707ebb9e2bd628 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_init_deinit.c @@ -0,0 +1,805 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_init_deinit.c + * + * WLAN Concurrenct Connection Management APIs + * + */ + +/* Include files */ + +#include "wlan_policy_mgr_api.h" +#include "wlan_policy_mgr_tables_1x1_dbs_i.h" +#include "wlan_policy_mgr_tables_2x2_dbs_i.h" +#include "wlan_policy_mgr_tables_2x2_5g_1x1_2g.h" +#include "wlan_policy_mgr_tables_2x2_2g_1x1_5g.h" +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_objmgr_global_obj.h" + +static QDF_STATUS policy_mgr_psoc_obj_create_cb(struct wlan_objmgr_psoc *psoc, + void *data) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + + policy_mgr_ctx = qdf_mem_malloc( + sizeof(struct policy_mgr_psoc_priv_obj)); + if (!policy_mgr_ctx) { + policy_mgr_err("memory allocation failed"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_ctx->psoc = psoc; + policy_mgr_ctx->old_hw_mode_index = POLICY_MGR_DEFAULT_HW_MODE_INDEX; + policy_mgr_ctx->new_hw_mode_index = POLICY_MGR_DEFAULT_HW_MODE_INDEX; + + wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_ctx, + QDF_STATUS_SUCCESS); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_psoc_obj_destroy_cb(struct wlan_objmgr_psoc *psoc, + void *data) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + + policy_mgr_ctx = policy_mgr_get_context(psoc); + wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_ctx); + qdf_mem_free(policy_mgr_ctx); + + return QDF_STATUS_SUCCESS; +} + +static void policy_mgr_psoc_obj_status_cb(struct wlan_objmgr_psoc *psoc, + void *data, QDF_STATUS status) +{ + return; +} + +static QDF_STATUS policy_mgr_pdev_obj_create_cb(struct wlan_objmgr_pdev *pdev, + void *data) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + struct wlan_objmgr_psoc *psoc; + + psoc = wlan_pdev_get_psoc(pdev); + policy_mgr_ctx = policy_mgr_get_context(psoc); + if (!policy_mgr_ctx) { + policy_mgr_err("invalid context"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_ctx->pdev = pdev; + + wlan_reg_register_chan_change_callback(psoc, + policy_mgr_reg_chan_change_callback, NULL); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_pdev_obj_destroy_cb(struct wlan_objmgr_pdev *pdev, + void *data) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + struct wlan_objmgr_psoc *psoc; + + psoc = wlan_pdev_get_psoc(pdev); + policy_mgr_ctx = policy_mgr_get_context(psoc); + if (!policy_mgr_ctx) { + policy_mgr_err("invalid context"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_ctx->pdev = NULL; + wlan_reg_unregister_chan_change_callback(psoc, + policy_mgr_reg_chan_change_callback); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_vdev_obj_create_cb(struct wlan_objmgr_vdev *vdev, + void *data) +{ + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_vdev_obj_destroy_cb(struct wlan_objmgr_vdev *vdev, + void *data) +{ + return QDF_STATUS_SUCCESS; +} + +static void policy_mgr_vdev_obj_status_cb(struct wlan_objmgr_vdev *vdev, + void *data, QDF_STATUS status) +{ + return; +} + +QDF_STATUS policy_mgr_init(void) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register psoc obj create cback"); + goto err_psoc_create; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register psoc obj delete cback"); + goto err_psoc_delete; + } + + status = wlan_objmgr_register_psoc_status_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_status_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register psoc obj status cback"); + goto err_psoc_status; + } + + status = wlan_objmgr_register_pdev_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register pdev obj create cback"); + goto err_pdev_create; + } + + status = wlan_objmgr_register_pdev_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register pdev obj delete cback"); + goto err_pdev_delete; + } + + status = wlan_objmgr_register_vdev_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register vdev obj create cback"); + goto err_vdev_create; + } + + status = wlan_objmgr_register_vdev_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register vdev obj delete cback"); + goto err_vdev_delete; + } + + status = wlan_objmgr_register_vdev_status_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_status_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register vdev obj status cback"); + goto err_vdev_status; + } + + policy_mgr_notice("Callbacks registered with obj mgr"); + + return QDF_STATUS_SUCCESS; + +err_vdev_status: + wlan_objmgr_unregister_vdev_destroy_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_destroy_cb, + NULL); +err_vdev_delete: + wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_create_cb, + NULL); +err_vdev_create: + wlan_objmgr_unregister_pdev_destroy_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_destroy_cb, + NULL); +err_pdev_delete: + wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_create_cb, + NULL); +err_pdev_create: + wlan_objmgr_unregister_psoc_status_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_status_cb, + NULL); +err_psoc_status: + wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_destroy_cb, + NULL); +err_psoc_delete: + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_create_cb, + NULL); +err_psoc_create: + return status; +} + +QDF_STATUS policy_mgr_deinit(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_unregister_psoc_status_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_status_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister psoc obj status cback"); + + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister psoc obj delete cback"); + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister psoc obj create cback"); + + status = wlan_objmgr_unregister_pdev_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister pdev obj delete cback"); + + status = wlan_objmgr_unregister_pdev_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister pdev obj create cback"); + + status = wlan_objmgr_unregister_vdev_status_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_status_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister vdev obj status cback"); + + status = wlan_objmgr_unregister_vdev_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister vdev obj delete cback"); + + status = wlan_objmgr_unregister_vdev_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister vdev obj create cback"); + + policy_mgr_info("deregistered callbacks with obj mgr successfully"); + + return status; +} + +QDF_STATUS policy_mgr_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (!QDF_IS_STATUS_SUCCESS(qdf_mutex_create( + &pm_ctx->qdf_conc_list_lock))) { + policy_mgr_err("Failed to init qdf_conc_list_lock"); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->sta_ap_intf_check_work_info = qdf_mem_malloc( + sizeof(struct sta_ap_intf_check_work_ctx)); + if (!pm_ctx->sta_ap_intf_check_work_info) { + qdf_mutex_destroy(&pm_ctx->qdf_conc_list_lock); + policy_mgr_err("Failed to alloc sta_ap_intf_check_work_info"); + return QDF_STATUS_E_FAILURE; + } + pm_ctx->sta_ap_intf_check_work_info->psoc = psoc; + qdf_create_work(0, &pm_ctx->sta_ap_intf_check_work, + policy_mgr_check_sta_ap_concurrent_ch_intf, + pm_ctx->sta_ap_intf_check_work_info); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (!QDF_IS_STATUS_SUCCESS(qdf_mutex_destroy( + &pm_ctx->qdf_conc_list_lock))) { + policy_mgr_err("Failed to destroy qdf_conc_list_lock"); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + if (pm_ctx->hw_mode.hw_mode_list) { + qdf_mem_free(pm_ctx->hw_mode.hw_mode_list); + pm_ctx->hw_mode.hw_mode_list = NULL; + policy_mgr_debug("HW list is freed"); + } + + if (pm_ctx->sta_ap_intf_check_work_info) { + qdf_cancel_work(&pm_ctx->sta_ap_intf_check_work); + qdf_mem_free(pm_ctx->sta_ap_intf_check_work_info); + pm_ctx->sta_ap_intf_check_work_info = NULL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_update_5g_scc_prefer() - Update pcl if 5g scc is preferred + * @psoc: psoc object + * + * Return: void + */ +static void policy_mgr_update_5g_scc_prefer(struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_con_mode mode; + + for (mode = PM_STA_MODE; mode < PM_MAX_NUM_OF_MODE; mode++) { + if (policy_mgr_get_5g_scc_prefer(psoc, mode)) { + (*second_connection_pcl_dbs_table) + [PM_STA_5_1x1][mode][PM_THROUGHPUT] = + PM_SCC_CH_24G; + policy_mgr_info("overwrite pm_second_connection_pcl_dbs_2x2_table, index %d mode %d system prefer %d new pcl %d", + PM_STA_5_1x1, mode, + PM_THROUGHPUT, PM_SCC_CH_24G); + (*second_connection_pcl_dbs_table) + [PM_STA_5_2x2][mode][PM_THROUGHPUT] = + PM_SCC_CH_24G; + policy_mgr_info("overwrite pm_second_connection_pcl_dbs_2x2_table, index %d mode %d system prefer %d new pcl %d", + PM_STA_5_2x2, mode, + PM_THROUGHPUT, PM_SCC_CH_24G); + } + } +} + +QDF_STATUS policy_mgr_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t enable_mcc_adaptive_sch = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_debug("Initializing the policy manager"); + + /* init pm_conc_connection_list */ + qdf_mem_zero(pm_conc_connection_list, sizeof(pm_conc_connection_list)); + + /* init dbs_opportunistic_timer */ + status = qdf_mc_timer_init(&pm_ctx->dbs_opportunistic_timer, + QDF_TIMER_TYPE_SW, + pm_dbs_opportunistic_timer_handler, + (void *)psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("Failed to init DBS opportunistic timer"); + return status; + } + + /* init connection_update_done_evt */ + status = policy_mgr_init_connection_update(pm_ctx); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("connection_update_done_evt init failed"); + return status; + } + + status = qdf_event_create(&pm_ctx->opportunistic_update_done_evt); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("opportunistic_update_done_evt init failed"); + return status; + } + + status = qdf_event_create(&pm_ctx->channel_switch_complete_evt); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("channel_switch_complete_evt init failed"); + return status; + } + policy_mgr_get_mcc_adaptive_sch(psoc, &enable_mcc_adaptive_sch); + policy_mgr_set_dynamic_mcc_adaptive_sch(psoc, enable_mcc_adaptive_sch); + pm_ctx->hw_mode_change_in_progress = POLICY_MGR_HW_MODE_NOT_IN_PROGRESS; + /* reset sap mandatory channels */ + status = policy_mgr_reset_sap_mandatory_channels(pm_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to reset mandatory channels"); + return status; + } + + /* init PCL table & function pointers based on HW capability */ + if (policy_mgr_is_hw_dbs_2x2_capable(psoc) || + policy_mgr_is_hw_dbs_required_for_band(psoc, HW_MODE_MAC_BAND_2G)) + policy_mgr_get_current_pref_hw_mode_ptr = + policy_mgr_get_current_pref_hw_mode_dbs_2x2; + else if (policy_mgr_is_2x2_1x1_dbs_capable(psoc)) + policy_mgr_get_current_pref_hw_mode_ptr = + policy_mgr_get_current_pref_hw_mode_dual_dbs; + else + policy_mgr_get_current_pref_hw_mode_ptr = + policy_mgr_get_current_pref_hw_mode_dbs_1x1; + + if (policy_mgr_is_hw_dbs_2x2_capable(psoc) || + policy_mgr_is_hw_dbs_required_for_band(psoc, + HW_MODE_MAC_BAND_2G) || + policy_mgr_is_2x2_1x1_dbs_capable(psoc)) { + second_connection_pcl_dbs_table = + &pm_second_connection_pcl_dbs_2x2_table; + policy_mgr_update_5g_scc_prefer(psoc); + } else { + second_connection_pcl_dbs_table = + &pm_second_connection_pcl_dbs_1x1_table; + } + + if (policy_mgr_is_hw_dbs_2x2_capable(psoc) || + policy_mgr_is_hw_dbs_required_for_band(psoc, + HW_MODE_MAC_BAND_2G) || + policy_mgr_is_2x2_1x1_dbs_capable(psoc)) + third_connection_pcl_dbs_table = + &pm_third_connection_pcl_dbs_2x2_table; + else + third_connection_pcl_dbs_table = + &pm_third_connection_pcl_dbs_1x1_table; + + if (policy_mgr_is_hw_dbs_2x2_capable(psoc) || + policy_mgr_is_hw_dbs_required_for_band(psoc, + HW_MODE_MAC_BAND_2G)) { + next_action_two_connection_table = + &pm_next_action_two_connection_dbs_2x2_table; + } else if (policy_mgr_is_2x2_1x1_dbs_capable(psoc)) { + next_action_two_connection_table = + &pm_next_action_two_connection_dbs_2x2_5g_1x1_2g_table; + next_action_two_connection_2x2_2g_1x1_5g_table = + &pm_next_action_two_connection_dbs_2x2_2g_1x1_5g_table; + } else { + next_action_two_connection_table = + &pm_next_action_two_connection_dbs_1x1_table; + } + + if (policy_mgr_is_hw_dbs_2x2_capable(psoc) || + policy_mgr_is_hw_dbs_required_for_band(psoc, + HW_MODE_MAC_BAND_2G)) { + next_action_three_connection_table = + &pm_next_action_three_connection_dbs_2x2_table; + } else if (policy_mgr_is_2x2_1x1_dbs_capable(psoc)) { + next_action_three_connection_table = + &pm_next_action_three_connection_dbs_2x2_5g_1x1_2g_table; + next_action_three_connection_2x2_2g_1x1_5g_table = + &pm_next_action_three_connection_dbs_2x2_2g_1x1_5g_table; + } else { + next_action_three_connection_table = + &pm_next_action_three_connection_dbs_1x1_table; + } + policy_mgr_debug("is DBS Capable %d, is SBS Capable %d", + policy_mgr_is_hw_dbs_capable(psoc), + policy_mgr_is_hw_sbs_capable(psoc)); + policy_mgr_debug("is2x2 %d, 2g-on-dbs %d is2x2+1x1 %d, is2x2_5g+1x1_2g %d, is2x2_2g+1x1_5g %d", + policy_mgr_is_hw_dbs_2x2_capable(psoc), + policy_mgr_is_hw_dbs_required_for_band( + psoc, HW_MODE_MAC_BAND_2G), + policy_mgr_is_2x2_1x1_dbs_capable(psoc), + policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(psoc), + policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(psoc)); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + /* destroy connection_update_done_evt */ + if (!QDF_IS_STATUS_SUCCESS(qdf_event_destroy + (&pm_ctx->connection_update_done_evt))) { + policy_mgr_err("Failed to destroy connection_update_done_evt"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + + /* destroy opportunistic_update_done_evt */ + if (!QDF_IS_STATUS_SUCCESS(qdf_event_destroy + (&pm_ctx->opportunistic_update_done_evt))) { + policy_mgr_err("Failed to destroy opportunistic_update_done_evt"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + /* destroy channel_switch_complete_evt */ + if (!QDF_IS_STATUS_SUCCESS(qdf_event_destroy + (&pm_ctx->channel_switch_complete_evt))) { + policy_mgr_err("Failed to destroy channel_switch_complete evt"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + + /* deallocate dbs_opportunistic_timer */ + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state( + &pm_ctx->dbs_opportunistic_timer)) { + qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); + } + + if (!QDF_IS_STATUS_SUCCESS(qdf_mc_timer_destroy( + &pm_ctx->dbs_opportunistic_timer))) { + policy_mgr_err("Cannot deallocate dbs opportunistic timer"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + + /* reset sap mandatory channels */ + if (QDF_IS_STATUS_ERROR( + policy_mgr_reset_sap_mandatory_channels(pm_ctx))) { + policy_mgr_err("failed to reset sap mandatory channels"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + + /* deinit pm_conc_connection_list */ + qdf_mem_zero(pm_conc_connection_list, sizeof(pm_conc_connection_list)); + + return status; +} + +QDF_STATUS policy_mgr_register_sme_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_sme_cbacks *sme_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->sme_cbacks.sme_get_nss_for_vdev = + sme_cbacks->sme_get_nss_for_vdev; + pm_ctx->sme_cbacks.sme_nss_update_request = + sme_cbacks->sme_nss_update_request; + pm_ctx->sme_cbacks.sme_pdev_set_hw_mode = + sme_cbacks->sme_pdev_set_hw_mode; + pm_ctx->sme_cbacks.sme_pdev_set_pcl = + sme_cbacks->sme_pdev_set_pcl; + pm_ctx->sme_cbacks.sme_soc_set_dual_mac_config = + sme_cbacks->sme_soc_set_dual_mac_config; + pm_ctx->sme_cbacks.sme_change_mcc_beacon_interval = + sme_cbacks->sme_change_mcc_beacon_interval; + pm_ctx->sme_cbacks.sme_get_ap_channel_from_scan = + sme_cbacks->sme_get_ap_channel_from_scan; + pm_ctx->sme_cbacks.sme_scan_result_purge = + sme_cbacks->sme_scan_result_purge; + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_register_hdd_cb() - register HDD callbacks + * @psoc: PSOC object information + * @hdd_cbacks: function pointers from HDD + * + * API, allows HDD to register callbacks to be invoked by policy + * mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_hdd_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_hdd_cbacks *hdd_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb = + hdd_cbacks->sap_restart_chan_switch_cb; + pm_ctx->hdd_cbacks.wlan_hdd_get_channel_for_sap_restart = + hdd_cbacks->wlan_hdd_get_channel_for_sap_restart; + pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev = + hdd_cbacks->get_mode_for_non_connected_vdev; + pm_ctx->hdd_cbacks.hdd_get_device_mode = + hdd_cbacks->hdd_get_device_mode; + pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress = + hdd_cbacks->hdd_is_chan_switch_in_progress; + pm_ctx->hdd_cbacks.hdd_is_cac_in_progress = + hdd_cbacks->hdd_is_cac_in_progress; + pm_ctx->hdd_cbacks.hdd_get_ap_6ghz_capable = + hdd_cbacks->hdd_get_ap_6ghz_capable; + pm_ctx->hdd_cbacks.wlan_hdd_indicate_active_ndp_cnt = + hdd_cbacks->wlan_hdd_indicate_active_ndp_cnt; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_deregister_hdd_cb(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb = NULL; + pm_ctx->hdd_cbacks.wlan_hdd_get_channel_for_sap_restart = NULL; + pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev = NULL; + pm_ctx->hdd_cbacks.hdd_get_device_mode = NULL; + pm_ctx->hdd_cbacks.hdd_is_chan_switch_in_progress = NULL; + pm_ctx->hdd_cbacks.hdd_is_cac_in_progress = NULL; + pm_ctx->hdd_cbacks.hdd_get_ap_6ghz_capable = NULL; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_wma_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_wma_cbacks *wma_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->wma_cbacks.wma_get_connection_info = + wma_cbacks->wma_get_connection_info; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_cdp_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_cdp_cbacks *cdp_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->cdp_cbacks.cdp_update_mac_id = + cdp_cbacks->cdp_update_mac_id; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_dp_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_dp_cbacks *dp_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency = + dp_cbacks->hdd_disable_rx_ol_in_concurrency; + pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb = + dp_cbacks->hdd_set_rx_mode_rps_cb; + pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb = + dp_cbacks->hdd_ipa_set_mcc_mode_cb; + pm_ctx->dp_cbacks.hdd_v2_flow_pool_map = + dp_cbacks->hdd_v2_flow_pool_map; + pm_ctx->dp_cbacks.hdd_v2_flow_pool_unmap = + dp_cbacks->hdd_v2_flow_pool_unmap; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_tdls_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_tdls_cbacks *tdls_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->tdls_cbacks.tdls_notify_increment_session = + tdls_cbacks->tdls_notify_increment_session; + pm_ctx->tdls_cbacks.tdls_notify_decrement_session = + tdls_cbacks->tdls_notify_decrement_session; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_mode_change_cb(struct wlan_objmgr_psoc *psoc, + send_mode_change_event_cb mode_change_cb) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->mode_change_cb = mode_change_cb; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_deregister_mode_change_cb(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->mode_change_cb = NULL; + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_pcl.c b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_pcl.c new file mode 100644 index 0000000000000000000000000000000000000000..707a3a84a6f56dd7cb3ddaf8af95030c98fbf33d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_pcl.c @@ -0,0 +1,2558 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_pcl.c + * + * WLAN Concurrenct Connection Management APIs + * + */ + +/* Include files */ + +#include "wlan_policy_mgr_api.h" +#include "wlan_policy_mgr_tables_no_dbs_i.h" +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "qdf_str.h" +#include "wlan_objmgr_global_obj.h" +#include "wlan_utility.h" +#include "wlan_mlme_ucfg_api.h" + +/** + * first_connection_pcl_table - table which provides PCL for the + * very first connection in the system + */ +const enum policy_mgr_pcl_type +first_connection_pcl_table[PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE] = { + [PM_STA_MODE] = {PM_NONE, PM_NONE, PM_NONE}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G }, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_5G, PM_5G }, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G }, + [PM_IBSS_MODE] = {PM_NONE, PM_NONE, PM_NONE}, + [PM_NAN_DISC_MODE] = {PM_5G, PM_5G, PM_5G}, +}; + +enum policy_mgr_pcl_type + (*second_connection_pcl_dbs_table)[PM_MAX_ONE_CONNECTION_MODE] + [PM_MAX_NUM_OF_MODE][PM_MAX_CONC_PRIORITY_MODE]; +pm_dbs_pcl_third_connection_table_type + *third_connection_pcl_dbs_table; +policy_mgr_next_action_two_connection_table_type + *next_action_two_connection_table; +policy_mgr_next_action_three_connection_table_type + *next_action_three_connection_table; +policy_mgr_next_action_two_connection_table_type + *next_action_two_connection_2x2_2g_1x1_5g_table; +policy_mgr_next_action_three_connection_table_type + *next_action_three_connection_2x2_2g_1x1_5g_table; + +QDF_STATUS policy_mgr_get_pcl_for_existing_conn( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t *pcl_ch, uint32_t *len, + uint8_t *pcl_weight, uint32_t weight_len, + bool all_matching_cxn_to_del) +{ + struct policy_mgr_conc_connection_info + info[MAX_NUMBER_OF_CONC_CONNECTIONS] = { {0} }; + uint8_t num_cxn_del = 0; + + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + policy_mgr_debug("get pcl for existing conn:%d", mode); + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + *len = 0; + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (policy_mgr_mode_specific_connection_count(psoc, mode, NULL) > 0) { + /* Check, store and temp delete the mode's parameter */ + policy_mgr_store_and_del_conn_info(psoc, mode, + all_matching_cxn_to_del, info, &num_cxn_del); + /* Get the PCL */ + status = policy_mgr_get_pcl(psoc, mode, pcl_ch, len, + pcl_weight, weight_len); + policy_mgr_debug("Get PCL to FW for mode:%d", mode); + /* Restore the connection info */ + policy_mgr_restore_deleted_conn_info(psoc, info, num_cxn_del); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +void policy_mgr_decr_session_set_pcl(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, + uint8_t session_id) +{ + QDF_STATUS qdf_status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_status = policy_mgr_decr_active_session(psoc, mode, session_id); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + policy_mgr_debug("Invalid active session"); + return; + } + + /* + * After the removal of this connection, we need to check if + * a STA connection still exists. The reason for this is that + * if one or more STA exists, we need to provide the updated + * PCL to the FW for cases like LFR. + * + * Since policy_mgr_get_pcl provides PCL list based on the new + * connection that is going to come up, we will find the + * existing STA entry, save it and delete it temporarily. + * After this we will get PCL as though as new STA connection + * is coming up. This will give the exact PCL that needs to be + * given to the FW. After setting the PCL, we need to restore + * the entry that we have saved before. + */ + policy_mgr_set_pcl_for_existing_combo(psoc, PM_STA_MODE); + /* do we need to change the HW mode */ + policy_mgr_check_n_start_opportunistic_timer(psoc); + return; +} + +/** + * policy_mgr_update_valid_ch_freq_list() - Update policy manager valid ch list + * @pm_ctx: policy manager context data + * @ch_list: Regulatory channel list + * + * When regulatory component channel list is updated this internal function is + * called to update policy manager copy of valid channel list. + * + * Return: QDF_STATUS_SUCCESS on success other qdf error status code + */ +static void +policy_mgr_update_valid_ch_freq_list(struct policy_mgr_psoc_priv_obj *pm_ctx, + struct regulatory_channel *reg_ch_list) +{ + uint32_t i, j = 0, ch; + enum channel_state state; + + for (i = 0; i < NUM_CHANNELS; i++) { + ch = reg_ch_list[i].chan_num; + state = wlan_reg_get_channel_state(pm_ctx->pdev, ch); + + if (state != CHANNEL_STATE_DISABLE && + state != CHANNEL_STATE_INVALID) { + pm_ctx->valid_ch_freq_list[j] = + reg_ch_list[i].center_freq; + j++; + } + } + pm_ctx->valid_ch_freq_list_count = j; +} + +void +policy_mgr_reg_chan_change_callback(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev, + struct regulatory_channel *chan_list, + struct avoid_freq_ind_data *avoid_freq_ind, + void *arg) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t i; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + policy_mgr_update_valid_ch_freq_list(pm_ctx, chan_list); + + if (!avoid_freq_ind) { + policy_mgr_debug("avoid_freq_ind NULL"); + return; + } + + /* + * The ch_list buffer can accomadate a maximum of + * NUM_CHANNELS and hence the ch_cnt should also not + * exceed NUM_CHANNELS. + */ + pm_ctx->unsafe_channel_count = avoid_freq_ind->chan_list.chan_cnt >= + NUM_CHANNELS ? + NUM_CHANNELS : avoid_freq_ind->chan_list.chan_cnt; + + for (i = 0; i < pm_ctx->unsafe_channel_count; i++) + pm_ctx->unsafe_channel_list[i] = + avoid_freq_ind->chan_list.chan_freq_list[i]; + + policy_mgr_debug("Channel list update, received %d avoided channels", + pm_ctx->unsafe_channel_count); +} + +QDF_STATUS policy_mgr_init_chan_avoidance(struct wlan_objmgr_psoc *psoc, + qdf_freq_t *chan_freq_list, + uint16_t chan_cnt) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t i; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->unsafe_channel_count = chan_cnt >= NUM_CHANNELS ? + NUM_CHANNELS : chan_cnt; + + for (i = 0; i < pm_ctx->unsafe_channel_count; i++) + pm_ctx->unsafe_channel_list[i] = chan_freq_list[i]; + + policy_mgr_debug("Channel list init, received %d avoided channels", + pm_ctx->unsafe_channel_count); + + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_update_with_safe_channel_list(struct wlan_objmgr_psoc *psoc, + uint32_t *pcl_channels, + uint32_t *len, + uint8_t *weight_list, + uint32_t weight_len) +{ + uint32_t current_channel_list[NUM_CHANNELS]; + uint8_t org_weight_list[NUM_CHANNELS]; + uint8_t is_unsafe = 1; + uint8_t i, j; + uint32_t safe_channel_count = 0, current_channel_count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t scc_on_lte_coex = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (len) { + current_channel_count = QDF_MIN(*len, NUM_CHANNELS); + } else { + policy_mgr_err("invalid number of channel length"); + return; + } + + if (pm_ctx->unsafe_channel_count == 0) { + policy_mgr_debug("There are no unsafe channels"); + return; + } + + qdf_mem_copy(current_channel_list, pcl_channels, + current_channel_count * sizeof(*current_channel_list)); + qdf_mem_zero(pcl_channels, + current_channel_count * sizeof(*pcl_channels)); + + qdf_mem_copy(org_weight_list, weight_list, NUM_CHANNELS); + qdf_mem_zero(weight_list, weight_len); + + policy_mgr_get_sta_sap_scc_lte_coex_chnl(psoc, &scc_on_lte_coex); + for (i = 0; i < current_channel_count; i++) { + is_unsafe = 0; + for (j = 0; j < pm_ctx->unsafe_channel_count; j++) { + if (current_channel_list[i] == + pm_ctx->unsafe_channel_list[j]) { + /* Found unsafe channel, update it */ + is_unsafe = 1; + policy_mgr_debug("CH %d is not safe", + current_channel_list[i]); + break; + } + } + if (is_unsafe && scc_on_lte_coex && + policy_mgr_is_sta_sap_scc(psoc, current_channel_list[i])) { + policy_mgr_debug("CH %d unsafe ingored when STA present on it", + current_channel_list[i]); + is_unsafe = 0; + } + + if (!is_unsafe) { + pcl_channels[safe_channel_count] = + current_channel_list[i]; + if (safe_channel_count < weight_len) + weight_list[safe_channel_count] = + org_weight_list[i]; + safe_channel_count++; + } + } + *len = safe_channel_count; + + return; +} + +static QDF_STATUS policy_mgr_modify_pcl_based_on_enabled_channels( + struct policy_mgr_psoc_priv_obj *pm_ctx, + uint32_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + uint32_t i, pcl_len = 0; + + for (i = 0; i < *pcl_len_org; i++) { + if (!wlan_reg_is_passive_or_disable_for_freq( + pm_ctx->pdev, pcl_list_org[i])) { + pcl_list_org[pcl_len] = pcl_list_org[i]; + weight_list_org[pcl_len++] = weight_list_org[i]; + } + } + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_modify_pcl_based_on_dnbs( + struct wlan_objmgr_psoc *psoc, + uint32_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + uint32_t i, pcl_len = 0; + uint32_t pcl_list[NUM_CHANNELS]; + uint8_t weight_list[NUM_CHANNELS]; + bool ok; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (*pcl_len_org > NUM_CHANNELS) { + policy_mgr_err("Invalid PCL List Length %d", *pcl_len_org); + return status; + } + for (i = 0; i < *pcl_len_org; i++) { + status = policy_mgr_is_chan_ok_for_dnbs(psoc, pcl_list_org[i], + &ok); + + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Not able to check DNBS eligibility"); + return status; + } + if (ok) { + pcl_list[pcl_len] = pcl_list_org[i]; + weight_list[pcl_len++] = weight_list_org[i]; + } + } + + qdf_mem_zero(pcl_list_org, *pcl_len_org * sizeof(*pcl_list_org)); + qdf_mem_zero(weight_list_org, *pcl_len_org); + qdf_mem_copy(pcl_list_org, pcl_list, pcl_len * sizeof(*pcl_list_org)); + qdf_mem_copy(weight_list_org, weight_list, pcl_len); + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +uint32_t policy_mgr_get_channel(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t *vdev_id) +{ + uint32_t idx = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + if (mode >= PM_MAX_NUM_OF_MODE) { + policy_mgr_err("incorrect mode"); + return 0; + } + + for (idx = 0; idx < MAX_NUMBER_OF_CONC_CONNECTIONS; idx++) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if ((pm_conc_connection_list[idx].mode == mode) && + (!vdev_id || (*vdev_id == + pm_conc_connection_list[idx].vdev_id)) + && pm_conc_connection_list[idx].in_use) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return pm_conc_connection_list[idx].freq; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + } + + return 0; +} + +/** + * policy_mgr_skip_dfs_ch() - skip dfs channel or not + * @psoc: pointer to soc + * @skip_dfs_channel: pointer to result + * + * Return: QDF_STATUS + */ +static QDF_STATUS policy_mgr_skip_dfs_ch(struct wlan_objmgr_psoc *psoc, + bool *skip_dfs_channel) +{ + bool sta_sap_scc_on_dfs_chan; + bool dfs_master_capable; + QDF_STATUS status; + + status = ucfg_mlme_get_dfs_master_capability(psoc, + &dfs_master_capable); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get dfs master capable"); + return status; + } + + *skip_dfs_channel = false; + if (!dfs_master_capable) { + policy_mgr_debug("skip DFS ch for SAP/Go dfs master cap %d", + dfs_master_capable); + *skip_dfs_channel = true; + return QDF_STATUS_SUCCESS; + } + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + if ((policy_mgr_mode_specific_connection_count(psoc, PM_STA_MODE, + NULL) > 0) && + !sta_sap_scc_on_dfs_chan) { + policy_mgr_debug("SAP/Go skips DFS ch if sta connects"); + *skip_dfs_channel = true; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_modify_sap_pcl_based_on_dfs() - filter out DFS channel if needed + * @psoc: pointer to soc + * @pcl_list_org: channel list to filter out + * @weight_list_org: weight of channel list + * @pcl_len_org: length of channel list + * + * Return: QDF_STATUS + */ +static QDF_STATUS policy_mgr_modify_sap_pcl_based_on_dfs( + struct wlan_objmgr_psoc *psoc, + uint32_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + size_t i, pcl_len = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool skip_dfs_channel = false; + QDF_STATUS status; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + if (*pcl_len_org > NUM_CHANNELS) { + policy_mgr_err("Invalid PCL List Length %d", *pcl_len_org); + return QDF_STATUS_E_FAILURE; + } + + status = policy_mgr_skip_dfs_ch(psoc, &skip_dfs_channel); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get dfs channel skip info"); + return status; + } + + if (!skip_dfs_channel) { + policy_mgr_debug("No more operation on DFS channel"); + return QDF_STATUS_SUCCESS; + } + + for (i = 0; i < *pcl_len_org; i++) { + if (!wlan_reg_is_dfs_for_freq(pm_ctx->pdev, pcl_list_org[i])) { + pcl_list_org[pcl_len] = pcl_list_org[i]; + weight_list_org[pcl_len++] = weight_list_org[i]; + } + } + + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_modify_sap_pcl_based_on_nol( + struct wlan_objmgr_psoc *psoc, + uint32_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + uint32_t i, pcl_len = 0; + uint32_t pcl_list[NUM_CHANNELS]; + uint8_t weight_list[NUM_CHANNELS]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + if (*pcl_len_org > NUM_CHANNELS) { + policy_mgr_err("Invalid PCL List Length %d", *pcl_len_org); + return QDF_STATUS_E_FAILURE; + } + + for (i = 0; i < *pcl_len_org; i++) { + if (!wlan_reg_is_disable_for_freq( + pm_ctx->pdev, pcl_list_org[i])) { + pcl_list[pcl_len] = pcl_list_org[i]; + weight_list[pcl_len++] = weight_list_org[i]; + } + } + + qdf_mem_zero(pcl_list_org, *pcl_len_org * sizeof(*pcl_list_org)); + qdf_mem_zero(weight_list_org, *pcl_len_org); + qdf_mem_copy(pcl_list_org, pcl_list, pcl_len * sizeof(*pcl_list_org)); + qdf_mem_copy(weight_list_org, weight_list, pcl_len); + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +policy_mgr_modify_pcl_based_on_srd(struct wlan_objmgr_psoc *psoc, + uint32_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + uint32_t i, pcl_len = 0; + uint32_t pcl_list[NUM_CHANNELS]; + uint8_t weight_list[NUM_CHANNELS]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (*pcl_len_org > NUM_CHANNELS) { + policy_mgr_err("Invalid PCL List Length %d", *pcl_len_org); + return QDF_STATUS_E_FAILURE; + } + for (i = 0; i < *pcl_len_org; i++) { + if (wlan_reg_is_etsi13_srd_chan_for_freq( + pm_ctx->pdev, pcl_list_org[i])) + continue; + pcl_list[pcl_len] = pcl_list_org[i]; + weight_list[pcl_len++] = weight_list_org[i]; + } + + qdf_mem_zero(pcl_list_org, *pcl_len_org * sizeof(*pcl_list_org)); + qdf_mem_zero(weight_list_org, *pcl_len_org); + qdf_mem_copy(pcl_list_org, pcl_list, pcl_len * sizeof(*pcl_list_org)); + qdf_mem_copy(weight_list_org, weight_list, pcl_len); + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_pcl_modification_for_sap( + struct wlan_objmgr_psoc *psoc, + uint32_t *pcl_channels, uint8_t *pcl_weight, + uint32_t *len) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + bool mandatory_modified_pcl = false; + bool nol_modified_pcl = false; + bool dfs_modified_pcl = false; + bool modified_final_pcl = false; + bool srd_chan_enabled; + + if (policy_mgr_is_sap_mandatory_channel_set(psoc)) { + status = policy_mgr_modify_sap_pcl_based_on_mandatory_channel( + psoc, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err( + "failed to get mandatory modified pcl for SAP"); + return status; + } + mandatory_modified_pcl = true; + } + + status = policy_mgr_modify_sap_pcl_based_on_nol( + psoc, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get nol modified pcl for SAP"); + return status; + } + nol_modified_pcl = true; + + status = policy_mgr_modify_sap_pcl_based_on_dfs( + psoc, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get dfs modified pcl for SAP"); + return status; + } + dfs_modified_pcl = true; + + wlan_mlme_get_srd_master_mode_for_vdev(psoc, QDF_SAP_MODE, + &srd_chan_enabled); + + if (!srd_chan_enabled) { + status = policy_mgr_modify_pcl_based_on_srd + (psoc, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Failed to modify SRD in pcl for SAP"); + return status; + } + } + + modified_final_pcl = true; + policy_mgr_debug(" %d %d %d %d", + mandatory_modified_pcl, + nol_modified_pcl, + dfs_modified_pcl, + modified_final_pcl); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_pcl_modification_for_p2p_go( + struct wlan_objmgr_psoc *psoc, + uint32_t *pcl_channels, uint8_t *pcl_weight, + uint32_t *len) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool srd_chan_enabled; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return status; + } + + status = policy_mgr_modify_pcl_based_on_enabled_channels( + pm_ctx, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get modified pcl for GO"); + return status; + } + + wlan_mlme_get_srd_master_mode_for_vdev(psoc, QDF_P2P_GO_MODE, + &srd_chan_enabled); + + if (!srd_chan_enabled) { + status = policy_mgr_modify_pcl_based_on_srd + (psoc, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Failed to modify SRD in pcl for GO"); + return status; + } + } + + policy_mgr_dump_channel_list(*len, pcl_channels, pcl_weight); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_mode_specific_modification_on_pcl( + struct wlan_objmgr_psoc *psoc, + uint32_t *pcl_channels, uint8_t *pcl_weight, + uint32_t *len, enum policy_mgr_con_mode mode) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + switch (mode) { + case PM_SAP_MODE: + status = policy_mgr_pcl_modification_for_sap( + psoc, pcl_channels, pcl_weight, len); + break; + case PM_P2P_GO_MODE: + status = policy_mgr_pcl_modification_for_p2p_go( + psoc, pcl_channels, pcl_weight, len); + break; + case PM_STA_MODE: + case PM_P2P_CLIENT_MODE: + case PM_IBSS_MODE: + case PM_NAN_DISC_MODE: + status = QDF_STATUS_SUCCESS; + break; + default: + policy_mgr_err("unexpected mode %d", mode); + break; + } + + return status; +} + +#ifdef FEATURE_FOURTH_CONNECTION +static enum policy_mgr_pcl_type policy_mgr_get_pcl_4_port( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + enum policy_mgr_conc_priority_mode pref) +{ + enum policy_mgr_three_connection_mode fourth_index = 0; + enum policy_mgr_pcl_type pcl; + + /* Will be enhanced for other types of 4 port conc (NaN etc.) + * in future. + */ + if (!policy_mgr_is_hw_dbs_capable(psoc)) { + policy_mgr_err("Can't find index for 4th port pcl table for non dbs capable"); + return PM_MAX_PCL_TYPE; + } + + /* SAP and P2P Go have same result in 4th port pcl table */ + if (mode == PM_SAP_MODE || mode == PM_P2P_GO_MODE) { + mode = PM_SAP_MODE; + } + + if (mode != PM_STA_MODE && mode != PM_SAP_MODE && + mode != PM_NDI_MODE) { + policy_mgr_err("Can't start 4th port if not STA, SAP, NDI"); + return PM_MAX_PCL_TYPE; + } + + fourth_index = + policy_mgr_get_fourth_connection_pcl_table_index(psoc); + if (PM_MAX_THREE_CONNECTION_MODE == fourth_index) { + policy_mgr_err("Can't find index for 4th port pcl table"); + return PM_MAX_PCL_TYPE; + } + policy_mgr_debug("Index for 4th port pcl table: %d", fourth_index); + + pcl = fourth_connection_pcl_dbs_table[fourth_index][mode][pref]; + + return pcl; +} +#else +static inline enum policy_mgr_pcl_type policy_mgr_get_pcl_4_port( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + enum policy_mgr_conc_priority_mode pref) +{return PM_MAX_PCL_TYPE; } +#endif + +QDF_STATUS policy_mgr_get_pcl(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t *pcl_channels, uint32_t *len, + uint8_t *pcl_weight, uint32_t weight_len) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t num_connections = 0; + enum policy_mgr_conc_priority_mode first_index = 0; + enum policy_mgr_one_connection_mode second_index = 0; + enum policy_mgr_two_connection_mode third_index = 0; + enum policy_mgr_pcl_type pcl = PM_NONE; + enum policy_mgr_conc_priority_mode conc_system_pref = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum QDF_OPMODE qdf_mode; + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return status; + } + + if ((mode < 0) || (mode >= PM_MAX_NUM_OF_MODE)) { + policy_mgr_err("Invalid connection mode %d received", mode); + return status; + } + + /* find the current connection state from pm_conc_connection_list*/ + num_connections = policy_mgr_get_connection_count(psoc); + policy_mgr_debug("connections:%d pref:%d requested mode:%d", + num_connections, pm_ctx->cur_conc_system_pref, mode); + + switch (pm_ctx->cur_conc_system_pref) { + case 0: + conc_system_pref = PM_THROUGHPUT; + break; + case 1: + conc_system_pref = PM_POWERSAVE; + break; + case 2: + conc_system_pref = PM_LATENCY; + break; + default: + policy_mgr_err("unknown cur_conc_system_pref value %d", + pm_ctx->cur_conc_system_pref); + break; + } + + switch (num_connections) { + case 0: + first_index = + policy_mgr_get_first_connection_pcl_table_index(psoc); + pcl = first_connection_pcl_table[mode][first_index]; + break; + case 1: + second_index = + policy_mgr_get_second_connection_pcl_table_index(psoc); + if (PM_MAX_ONE_CONNECTION_MODE == second_index) { + policy_mgr_err("couldn't find index for 2nd connection pcl table"); + return status; + } + qdf_mode = policy_mgr_get_qdf_mode_from_pm(mode); + if (qdf_mode == QDF_MAX_NO_OF_MODE) + return status; + + if (policy_mgr_is_hw_dbs_capable(psoc) == true && + policy_mgr_is_dbs_allowed_for_concurrency( + psoc, qdf_mode)) { + pcl = (*second_connection_pcl_dbs_table) + [second_index][mode][conc_system_pref]; + } else { + pcl = second_connection_pcl_nodbs_table + [second_index][mode][conc_system_pref]; + } + + break; + case 2: + third_index = + policy_mgr_get_third_connection_pcl_table_index(psoc); + if (PM_MAX_TWO_CONNECTION_MODE == third_index) { + policy_mgr_err( + "couldn't find index for 3rd connection pcl table"); + return status; + } + if (policy_mgr_is_hw_dbs_capable(psoc) == true) { + pcl = (*third_connection_pcl_dbs_table) + [third_index][mode][conc_system_pref]; + } else { + pcl = third_connection_pcl_nodbs_table + [third_index][mode][conc_system_pref]; + } + break; + case 3: + pcl = policy_mgr_get_pcl_4_port(psoc, mode, conc_system_pref); + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + break; + } + + /* once the PCL enum is obtained find out the exact channel list with + * help from sme_get_cfg_valid_channels + */ + status = policy_mgr_get_channel_list(psoc, pcl, pcl_channels, len, mode, + pcl_weight, weight_len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get channel list:%d", status); + return status; + } + + policy_mgr_dump_channel_list(*len, pcl_channels, pcl_weight); + + policy_mgr_mode_specific_modification_on_pcl( + psoc, pcl_channels, pcl_weight, len, mode); + + status = policy_mgr_modify_pcl_based_on_dnbs(psoc, pcl_channels, + pcl_weight, len); + + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get modified pcl based on DNBS"); + return status; + } + return QDF_STATUS_SUCCESS; +} + +enum policy_mgr_conc_priority_mode + policy_mgr_get_first_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return PM_THROUGHPUT; + } + + if (pm_ctx->cur_conc_system_pref >= PM_MAX_CONC_PRIORITY_MODE) + return PM_THROUGHPUT; + + return pm_ctx->cur_conc_system_pref; +} + +enum policy_mgr_one_connection_mode + policy_mgr_get_second_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_one_connection_mode index = PM_MAX_ONE_CONNECTION_MODE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return index; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (PM_STA_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_24_1x1; + else + index = PM_STA_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_5_1x1; + else + index = PM_STA_5_2x2; + } + } else if (PM_SAP_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_24_1x1; + else + index = PM_SAP_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_5_1x1; + else + index = PM_SAP_5_2x2; + } + } else if (PM_P2P_CLIENT_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_24_1x1; + else + index = PM_P2P_CLI_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_5_1x1; + else + index = PM_P2P_CLI_5_2x2; + } + } else if (PM_P2P_GO_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_24_1x1; + else + index = PM_P2P_GO_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_5_1x1; + else + index = PM_P2P_GO_5_2x2; + } + } else if (PM_IBSS_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_IBSS_24_1x1; + else + index = PM_IBSS_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_IBSS_5_1x1; + else + index = PM_IBSS_5_2x2; + } + } else if (PM_NAN_DISC_MODE == pm_conc_connection_list[0].mode) { + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_NAN_DISC_24_1x1; + else + index = PM_NAN_DISC_24_2x2; + } + + policy_mgr_debug("mode:%d freq:%d chain:%d index:%d", + pm_conc_connection_list[0].mode, + pm_conc_connection_list[0].freq, + pm_conc_connection_list[0].chain_mask, index); + + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_cli_sap(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_SCC_24_1x1; + else + index = PM_P2P_CLI_SAP_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_SCC_5_1x1; + else + index = PM_P2P_CLI_SAP_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_MCC_24_1x1; + else + index = PM_P2P_CLI_SAP_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_MCC_5_1x1; + else + index = PM_P2P_CLI_SAP_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_MCC_24_5_1x1; + else + index = PM_P2P_CLI_SAP_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_DBS_1x1; + else + index = PM_P2P_CLI_SAP_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sta_sap(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_SCC_24_1x1; + else + index = PM_STA_SAP_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_SCC_5_1x1; + else + index = PM_STA_SAP_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_MCC_24_1x1; + else + index = PM_STA_SAP_MCC_24_2x2; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_MCC_5_1x1; + else + index = PM_STA_SAP_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_MCC_24_5_1x1; + else + index = PM_STA_SAP_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_DBS_1x1; + else + index = PM_STA_SAP_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sap_sap(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_SCC_24_1x1; + else + index = PM_SAP_SAP_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_SCC_5_1x1; + else + index = PM_SAP_SAP_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_MCC_24_1x1; + else + index = PM_SAP_SAP_MCC_24_2x2; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_MCC_5_1x1; + else + index = PM_SAP_SAP_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_MCC_24_5_1x1; + else + index = PM_SAP_SAP_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_DBS_1x1; + else + index = PM_SAP_SAP_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sta_go(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_SCC_24_1x1; + else + index = PM_STA_P2P_GO_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_SCC_5_1x1; + else + index = PM_STA_P2P_GO_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_MCC_24_1x1; + else + index = PM_STA_P2P_GO_MCC_24_2x2; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_MCC_5_1x1; + else + index = PM_STA_P2P_GO_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_MCC_24_5_1x1; + else + index = PM_STA_P2P_GO_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_DBS_1x1; + else + index = PM_STA_P2P_GO_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sta_cli(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_SCC_24_1x1; + else + index = PM_STA_P2P_CLI_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_SCC_5_1x1; + else + index = PM_STA_P2P_CLI_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_MCC_24_1x1; + else + index = PM_STA_P2P_CLI_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_MCC_5_1x1; + else + index = PM_STA_P2P_CLI_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_MCC_24_5_1x1; + else + index = PM_STA_P2P_CLI_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_DBS_1x1; + else + index = PM_STA_P2P_CLI_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_go_cli(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_SCC_24_1x1; + else + index = PM_P2P_GO_P2P_CLI_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_SCC_5_1x1; + else + index = PM_P2P_GO_P2P_CLI_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_MCC_24_1x1; + else + index = PM_P2P_GO_P2P_CLI_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_MCC_5_1x1; + else + index = PM_P2P_GO_P2P_CLI_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_MCC_24_5_1x1; + else + index = PM_P2P_GO_P2P_CLI_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_DBS_1x1; + else + index = PM_P2P_GO_P2P_CLI_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_go_sap(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_SCC_24_1x1; + else + index = PM_P2P_GO_SAP_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_SCC_5_1x1; + else + index = PM_P2P_GO_SAP_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_MCC_24_1x1; + else + index = PM_P2P_GO_SAP_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_MCC_5_1x1; + else + index = PM_P2P_GO_SAP_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_MCC_24_5_1x1; + else + index = PM_P2P_GO_SAP_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_DBS_1x1; + else + index = PM_P2P_GO_SAP_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sta_sta(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_SCC_24_1x1; + else + index = PM_STA_STA_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_SCC_5_1x1; + else + index = PM_STA_STA_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_MCC_24_1x1; + else + index = PM_STA_STA_MCC_24_2x2; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_MCC_5_1x1; + else + index = PM_STA_STA_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_MCC_24_5_1x1; + else + index = PM_STA_STA_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_DBS_1x1; + else + index = PM_STA_STA_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_go_go(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_GO_SCC_24_1x1; + else + index = PM_P2P_GO_P2P_GO_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_GO_SCC_5_1x1; + else + index = PM_P2P_GO_P2P_GO_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_GO_MCC_24_1x1; + else + index = PM_P2P_GO_P2P_GO_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_GO_MCC_5_1x1; + else + index = PM_P2P_GO_P2P_GO_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_GO_MCC_24_5_1x1; + else + index = PM_P2P_GO_P2P_GO_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[0].freq)) && + (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[1].freq))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_GO_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_GO_DBS_1x1; + else + index = PM_P2P_GO_P2P_GO_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_nan_ndi(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + /* Policy mgr only considers NAN Disc ch in 2.4GHz */ + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_NAN_DISC_NDI_SCC_24_1x1; + else + index = PM_NAN_DISC_NDI_SCC_24_2x2; + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + /* Policy mgr only considers NAN Disc ch in 2.4GHz */ + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_NAN_DISC_NDI_MCC_24_1x1; + else + index = PM_NAN_DISC_NDI_MCC_24_2x2; + /* DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_NAN_DISC_NDI_DBS_1x1; + else + index = PM_NAN_DISC_NDI_DBS_2x2; + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sta_nan(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + /* Policy mgr only considers NAN Disc ch in 2.4GHz */ + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_STA_NAN_DISC_SCC_24_1x1; + else + index = PM_STA_NAN_DISC_SCC_24_2x2; + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + /* Policy mgr only considers NAN Disc ch in 2.4 GHz */ + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_STA_NAN_DISC_MCC_24_1x1; + else + index = PM_STA_NAN_DISC_MCC_24_2x2; + /* DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_STA_NAN_DISC_DBS_1x1; + else + index = PM_STA_NAN_DISC_DBS_2x2; + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sap_nan(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].freq == + pm_conc_connection_list[1].freq) { + /* Policy mgr only considers NAN Disc ch in 2.4 GHz */ + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_SAP_NAN_DISC_SCC_24_1x1; + else + index = PM_SAP_NAN_DISC_SCC_24_2x2; + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + /* Policy mgr only considers NAN Disc ch in 2.4GHz */ + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_SAP_NAN_DISC_MCC_24_1x1; + else + index = PM_SAP_NAN_DISC_MCC_24_2x2; + /* DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + if (POLICY_MGR_ONE_ONE == pm_conc_connection_list[0].chain_mask) + index = PM_SAP_NAN_DISC_DBS_1x1; + else + index = PM_SAP_NAN_DISC_DBS_2x2; + } + return index; +} + +enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return index; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (((PM_P2P_CLIENT_MODE == pm_conc_connection_list[0].mode) && + (PM_SAP_MODE == pm_conc_connection_list[1].mode)) || + ((PM_SAP_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_CLIENT_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_cli_sap(); + else if (((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_SAP_MODE == pm_conc_connection_list[1].mode)) || + ((PM_SAP_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sta_sap(); + else if ((PM_SAP_MODE == pm_conc_connection_list[0].mode) && + (PM_SAP_MODE == pm_conc_connection_list[1].mode)) + index = + policy_mgr_get_third_connection_pcl_table_index_sap_sap(); + else if (((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_GO_MODE == pm_conc_connection_list[1].mode)) || + ((PM_P2P_GO_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sta_go(); + else if (((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_CLIENT_MODE == pm_conc_connection_list[1].mode)) || + ((PM_P2P_CLIENT_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sta_cli(); + else if (((PM_P2P_GO_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_CLIENT_MODE == pm_conc_connection_list[1].mode)) || + ((PM_P2P_CLIENT_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_GO_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_go_cli(); + else if (((PM_SAP_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_GO_MODE == pm_conc_connection_list[1].mode)) || + ((PM_P2P_GO_MODE == pm_conc_connection_list[0].mode) && + (PM_SAP_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_go_sap(); + else if (((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode)) || + ((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sta_sta(); + else if (((PM_NAN_DISC_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode)) || + ((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_NAN_DISC_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sta_nan(); + else if (((PM_NAN_DISC_MODE == pm_conc_connection_list[0].mode) && + (PM_NDI_MODE == pm_conc_connection_list[1].mode)) || + ((PM_NDI_MODE == pm_conc_connection_list[0].mode) && + (PM_NAN_DISC_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_nan_ndi(); + else if (((PM_SAP_MODE == pm_conc_connection_list[0].mode) && + (PM_NAN_DISC_MODE == pm_conc_connection_list[1].mode)) || + ((PM_NAN_DISC_MODE == pm_conc_connection_list[0].mode) && + (PM_SAP_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sap_nan(); + else if ((pm_conc_connection_list[0].mode == PM_P2P_GO_MODE) && + (pm_conc_connection_list[1].mode == PM_P2P_GO_MODE)) + index = + policy_mgr_get_third_connection_pcl_table_index_go_go(); + + policy_mgr_debug("mode0:%d mode1:%d freq0:%d freq1:%d chain:%d index:%d", + pm_conc_connection_list[0].mode, + pm_conc_connection_list[1].mode, + pm_conc_connection_list[0].freq, + pm_conc_connection_list[1].freq, + pm_conc_connection_list[0].chain_mask, index); + + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return index; +} + +#ifdef FEATURE_FOURTH_CONNECTION +enum policy_mgr_three_connection_mode + policy_mgr_get_fourth_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_three_connection_mode index = + PM_MAX_THREE_CONNECTION_MODE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t count_sap = 0; + uint32_t count_sta = 0; + uint32_t count_ndi = 0; + uint32_t count_nan_disc = 0; + uint32_t list_sap[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t list_sta[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t list_ndi[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t list_nan_disc[MAX_NUMBER_OF_CONC_CONNECTIONS]; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return index; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + + /* For 4 port concurrency case, + * 1st step: (SAP+STA)(2.4G MAC SCC) + (SAP+STA)(5G MAC SCC) + * 2nd step: (AGO+STA)(2.4G MAC SCC) + (AGO+STA)(5G MAC SCC) + */ + count_sap += policy_mgr_mode_specific_connection_count( + psoc, PM_SAP_MODE, &list_sap[count_sap]); + count_sap += policy_mgr_mode_specific_connection_count( + psoc, PM_P2P_GO_MODE, &list_sap[count_sap]); + count_sta = policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, list_sta); + count_ndi = policy_mgr_mode_specific_connection_count( + psoc, PM_NDI_MODE, list_ndi); + count_nan_disc = policy_mgr_mode_specific_connection_count( + psoc, PM_NAN_DISC_MODE, list_nan_disc); + policy_mgr_debug("sap/ago %d, sta %d, ndi %d nan disc %d", + count_sap, count_sta, count_ndi, count_nan_disc); + if (count_sap == 2 && count_sta == 1) { + policy_mgr_debug( + "channel: sap0: %d, sap1: %d, sta0: %d", + pm_conc_connection_list[list_sap[0]].freq, + pm_conc_connection_list[list_sap[1]].freq, + pm_conc_connection_list[list_sta[0]].freq); + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[1]].freq)) { + index = PM_STA_SAP_SCC_24_SAP_5_DBS; + } else if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[1]].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq)) { + index = PM_STA_SAP_SCC_24_SAP_5_DBS; + } else if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[1]].freq)) { + index = PM_STA_SAP_SCC_5_SAP_24_DBS; + } else if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[1]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq)) { + index = PM_STA_SAP_SCC_5_SAP_24_DBS; + } else { + index = PM_MAX_THREE_CONNECTION_MODE; + } + } else if (count_sap == 1 && count_sta == 2) { + policy_mgr_debug( + "channel: sap0: %d, sta0: %d, sta1: %d", + pm_conc_connection_list[list_sap[0]].freq, + pm_conc_connection_list[list_sta[0]].freq, + pm_conc_connection_list[list_sta[1]].freq); + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[1]].freq)) { + index = PM_STA_SAP_SCC_24_STA_5_DBS; + } else if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[1]].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq)) { + index = PM_STA_SAP_SCC_24_STA_5_DBS; + } else if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[1]].freq)) { + index = PM_STA_SAP_SCC_5_STA_24_DBS; + } else if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[1]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq)) { + index = PM_STA_SAP_SCC_5_STA_24_DBS; + } else { + index = PM_MAX_THREE_CONNECTION_MODE; + } + } else if (count_nan_disc == 1 && count_ndi == 1 && count_sap == 1) { + /* Policy mgr only considers NAN Disc ch in 2.4GHz */ + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_NAN_DISC_SAP_SCC_24_NDI_5_DBS; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_NAN_DISC_NDI_SCC_24_SAP_5_DBS; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sap[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_SAP_NDI_SCC_5_NAN_DISC_24_DBS; + } else { + index = PM_MAX_THREE_CONNECTION_MODE; + } + } else if (count_nan_disc == 1 && count_ndi == 1 && count_sta == 1) { + /* Policy mgr only considers NAN Disc ch in 2.4GHz */ + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_NAN_DISC_STA_24_NDI_5_DBS; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_NAN_DISC_NDI_24_STA_5_DBS; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_STA_NDI_5_NAN_DISC_24_DBS; + } else if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_sta[0]].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_STA_NDI_NAN_DISC_24_SMM; + } + } else if (count_nan_disc == 1 && count_ndi == 2) { + /* Policy mgr only considers NAN Disc ch in 2.4GHz */ + if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[1]].freq)) { + index = PM_NAN_DISC_NDI_24_NDI_5_DBS; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_NAN_DISC_NDI_24_NDI_5_DBS; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq) && + WLAN_REG_IS_5GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_NDI_NDI_5_NAN_DISC_24_DBS; + } else if (WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq) && + WLAN_REG_IS_24GHZ_CH_FREQ( + pm_conc_connection_list[list_ndi[0]].freq)) { + index = PM_NDI_NDI_NAN_DISC_24_SMM; + } + } + + policy_mgr_debug( + "mode0:%d mode1:%d mode2:%d chan0:%d chan1:%d chan2:%d chain:%d index:%d", + pm_conc_connection_list[0].mode, + pm_conc_connection_list[1].mode, + pm_conc_connection_list[2].mode, + pm_conc_connection_list[0].freq, + pm_conc_connection_list[1].freq, + pm_conc_connection_list[2].freq, + pm_conc_connection_list[0].chain_mask, index); + + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return index; +} +#endif + +uint32_t +policy_mgr_get_nondfs_preferred_channel(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + bool for_existing_conn) +{ + uint32_t pcl_channels[NUM_CHANNELS]; + uint8_t pcl_weight[NUM_CHANNELS]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + /* + * in worst case if we can't find any channel at all + * then return 2.4G channel, so atleast we won't fall + * under 5G MCC scenario + */ + uint32_t i, pcl_len = 0, non_dfs_freq, freq; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return PM_24_GHZ_CH_FREQ_6; + } + + freq = PM_24_GHZ_CH_FREQ_6; + if (true == for_existing_conn) { + /* + * First try to see if there is any non-dfs channel already + * present in current connection table. If yes then return + * that channel + */ + if (true == policy_mgr_is_any_nondfs_chnl_present( + psoc, &non_dfs_freq)) + return non_dfs_freq; + + if (QDF_STATUS_SUCCESS != + policy_mgr_get_pcl_for_existing_conn( + psoc, mode, + pcl_channels, &pcl_len, + pcl_weight, QDF_ARRAY_SIZE(pcl_weight), + false)) + return freq; + } else { + if (QDF_STATUS_SUCCESS != policy_mgr_get_pcl( + psoc, mode, pcl_channels, &pcl_len, pcl_weight, + QDF_ARRAY_SIZE(pcl_weight))) + return freq; + } + + for (i = 0; i < pcl_len; i++) { + if (wlan_reg_is_dfs_for_freq(pm_ctx->pdev, pcl_channels[i]) || + !policy_mgr_is_safe_channel(psoc, pcl_channels[i])) { + continue; + } else { + freq = pcl_channels[i]; + break; + } + } + + return freq; +} + +static void policy_mgr_remove_dsrc_channels(uint32_t *ch_freq_list, + uint32_t *num_channels, + struct wlan_objmgr_pdev *pdev) +{ + uint32_t num_chan_temp = 0; + int i; + + for (i = 0; i < *num_channels; i++) { + if (!wlan_reg_is_dsrc_freq(ch_freq_list[i])) { + ch_freq_list[num_chan_temp] = ch_freq_list[i]; + num_chan_temp++; + } + } + + *num_channels = num_chan_temp; +} + +QDF_STATUS policy_mgr_get_valid_chans_from_range( + struct wlan_objmgr_psoc *psoc, uint32_t *ch_freq_list, + uint32_t *ch_cnt, enum policy_mgr_con_mode mode) +{ + uint8_t ch_weight_list[NUM_CHANNELS] = {0}; + uint32_t ch_weight_len; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + size_t chan_index = 0; + + if (!ch_freq_list || !ch_cnt) { + policy_mgr_err("NULL parameters"); + return QDF_STATUS_E_FAILURE; + } + + for (chan_index = 0; chan_index < *ch_cnt; chan_index++) + ch_weight_list[chan_index] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + + ch_weight_len = *ch_cnt; + + /* check the channel avoidance list for beaconing entities */ + if (mode == PM_SAP_MODE || mode == PM_P2P_GO_MODE) + policy_mgr_update_with_safe_channel_list( + psoc, ch_freq_list, ch_cnt, ch_weight_list, + ch_weight_len); + + status = policy_mgr_mode_specific_modification_on_pcl( + psoc, ch_freq_list, ch_weight_list, ch_cnt, mode); + + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get modified pcl for mode %d", mode); + return status; + } + + status = policy_mgr_modify_pcl_based_on_dnbs(psoc, ch_freq_list, + ch_weight_list, ch_cnt); + + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get modified pcl based on DNBS"); + return status; + } + + return status; +} + +QDF_STATUS policy_mgr_get_valid_chans(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq_list, + uint32_t *list_len) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + *list_len = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + if (!pm_ctx->valid_ch_freq_list_count) { + policy_mgr_err("Invalid PM valid channel list"); + return QDF_STATUS_E_INVAL; + } + + *list_len = pm_ctx->valid_ch_freq_list_count; + qdf_mem_copy(ch_freq_list, pm_ctx->valid_ch_freq_list, + pm_ctx->valid_ch_freq_list_count * + sizeof(pm_ctx->valid_ch_freq_list[0])); + + policy_mgr_remove_dsrc_channels(ch_freq_list, list_len, pm_ctx->pdev); + + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_list_has_24GHz_channel(uint32_t *ch_freq_list, + uint32_t list_len) +{ + uint32_t i; + + for (i = 0; i < list_len; i++) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq_list[i])) + return true; + } + + return false; +} + +QDF_STATUS +policy_mgr_set_sap_mandatory_channels(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq_list, uint32_t len) +{ + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (!len) { + policy_mgr_err("No mandatory freq/chan configured"); + return QDF_STATUS_E_FAILURE; + } + + if (!policy_mgr_list_has_24GHz_channel(ch_freq_list, len)) { + policy_mgr_err("2.4GHz channels missing, this is not expected"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_debug("mandatory chan length:%d", + pm_ctx->sap_mandatory_channels_len); + + for (i = 0; i < len; i++) { + pm_ctx->sap_mandatory_channels[i] = ch_freq_list[i]; + policy_mgr_debug("chan:%d", pm_ctx->sap_mandatory_channels[i]); + } + + pm_ctx->sap_mandatory_channels_len = len; + + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_is_sap_mandatory_channel_set(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + if (pm_ctx->sap_mandatory_channels_len) + return true; + else + return false; +} + +QDF_STATUS policy_mgr_modify_sap_pcl_based_on_mandatory_channel( + struct wlan_objmgr_psoc *psoc, uint32_t *pcl_list_org, + uint8_t *weight_list_org, uint32_t *pcl_len_org) +{ + uint32_t i, j, pcl_len = 0; + bool found; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (!pm_ctx->sap_mandatory_channels_len) + return QDF_STATUS_SUCCESS; + + if (!policy_mgr_list_has_24GHz_channel(pm_ctx->sap_mandatory_channels, + pm_ctx->sap_mandatory_channels_len)) { + policy_mgr_err("fav channel list is missing 2.4GHz channels"); + return QDF_STATUS_E_FAILURE; + } + + for (i = 0; i < pm_ctx->sap_mandatory_channels_len; i++) + policy_mgr_debug("fav chan:%d", + pm_ctx->sap_mandatory_channels[i]); + + for (i = 0; i < *pcl_len_org; i++) { + found = false; + if (i >= NUM_CHANNELS) { + policy_mgr_debug("index is exceeding NUM_CHANNELS"); + break; + } + for (j = 0; j < pm_ctx->sap_mandatory_channels_len; j++) { + if (pcl_list_org[i] == + pm_ctx->sap_mandatory_channels[j]) { + found = true; + break; + } + } + if (found && (pcl_len < NUM_CHANNELS)) { + pcl_list_org[pcl_len] = pcl_list_org[i]; + weight_list_org[pcl_len++] = weight_list_org[i]; + } + } + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_sap_mandatory_channel(struct wlan_objmgr_psoc *psoc, + uint32_t *ch_freq) +{ + QDF_STATUS status; + struct policy_mgr_pcl_list pcl; + + qdf_mem_zero(&pcl, sizeof(pcl)); + + status = policy_mgr_get_pcl_for_existing_conn( + psoc, PM_SAP_MODE, pcl.pcl_list, &pcl.pcl_len, + pcl.weight_list, QDF_ARRAY_SIZE(pcl.weight_list), + false); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Unable to get PCL for SAP"); + return status; + } + + /* + * Get inside below loop if no existing SAP connection and hence a new + * SAP connection might be coming up. pcl.pcl_len can be 0 if no common + * channel between PCL & mandatory channel list as well + */ + if (!pcl.pcl_len && !policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, NULL)) { + policy_mgr_debug("policy_mgr_get_pcl_for_existing_conn returned no pcl"); + status = policy_mgr_get_pcl( + psoc, PM_SAP_MODE, + pcl.pcl_list, &pcl.pcl_len, + pcl.weight_list, + QDF_ARRAY_SIZE(pcl.weight_list)); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Unable to get PCL for SAP: policy_mgr_get_pcl"); + return status; + } + } + + status = policy_mgr_modify_sap_pcl_based_on_mandatory_channel( + psoc, pcl.pcl_list, + pcl.weight_list, + &pcl.pcl_len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Unable to modify SAP PCL"); + return status; + } + + if (!pcl.pcl_len) { + policy_mgr_err("No common channel between mandatory list & PCL"); + return QDF_STATUS_E_FAILURE; + } + + *ch_freq = pcl.pcl_list[0]; + policy_mgr_debug("mandatory channel:%d", *ch_freq); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_valid_chan_weights(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_pcl_chan_weights *weight) +{ + uint32_t i, j; + struct policy_mgr_conc_connection_info + info[MAX_NUMBER_OF_CONC_CONNECTIONS] = { {0} }; + uint8_t num_cxn_del = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_set(weight->weighed_valid_list, NUM_CHANNELS, + WEIGHT_OF_DISALLOWED_CHANNELS); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL) > 0) { + /* + * Store the STA mode's parameter and temporarily delete it + * from the concurrency table. This way the allow concurrency + * check can be used as though a new connection is coming up, + * allowing to detect the disallowed channels. + */ + policy_mgr_store_and_del_conn_info(psoc, PM_STA_MODE, false, + info, &num_cxn_del); + /* + * There is a small window between releasing the above lock + * and acquiring the same in policy_mgr_allow_concurrency, + * below! + */ + for (i = 0; i < weight->saved_num_chan; i++) { + if (policy_mgr_is_concurrency_allowed + (psoc, PM_STA_MODE, weight->saved_chan_list[i], + HW_MODE_20_MHZ)) { + weight->weighed_valid_list[i] = + WEIGHT_OF_NON_PCL_CHANNELS; + } + } + /* Restore the connection info */ + policy_mgr_restore_deleted_conn_info(psoc, info, num_cxn_del); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + for (i = 0; i < weight->saved_num_chan; i++) { + for (j = 0; j < weight->pcl_len; j++) { + if (weight->saved_chan_list[i] == weight->pcl_list[j]) { + weight->weighed_valid_list[i] = + weight->weight_list[j]; + break; + } + } + } + + return QDF_STATUS_SUCCESS; +} + +uint32_t policy_mgr_mode_specific_get_channel( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode) +{ + uint32_t conn_index; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t freq = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + /* provides the channel for the first matching mode type */ + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].mode == mode) && + pm_conc_connection_list[conn_index].in_use) { + freq = pm_conc_connection_list[conn_index].freq; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return freq; +} + +uint32_t policy_mgr_get_alternate_channel_for_sap( + struct wlan_objmgr_psoc *psoc, uint8_t sap_vdev_id, + uint32_t sap_ch_freq) +{ + uint32_t pcl_channels[NUM_CHANNELS]; + uint8_t pcl_weight[NUM_CHANNELS]; + uint32_t ch_freq = 0; + uint32_t pcl_len = 0; + struct policy_mgr_conc_connection_info info; + uint8_t num_cxn_del = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t i; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + /* + * Store the connection's parameter and temporarily delete it + * from the concurrency table. This way the get pcl can be used as a + * new connection is coming up, after check, restore the connection to + * concurrency table. + */ + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + policy_mgr_store_and_del_conn_info_by_vdev_id(psoc, sap_vdev_id, + &info, &num_cxn_del); + + if (QDF_STATUS_SUCCESS == policy_mgr_get_pcl( + psoc, PM_SAP_MODE, pcl_channels, &pcl_len, + pcl_weight, QDF_ARRAY_SIZE(pcl_weight))) { + for (i = 0; i < pcl_len; i++) { + if (pcl_channels[i] == sap_ch_freq) + continue; + ch_freq = pcl_channels[i]; + break; + } + } + /* Restore the connection entry */ + if (num_cxn_del > 0) + policy_mgr_restore_deleted_conn_info(psoc, &info, num_cxn_del); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return ch_freq; +} + +/* + * Buffer len size to consider the 4 char freq, 3 char weight, 2 char + * for open close brackets and space and a space, Total 10 + */ +#define CHAN_WEIGHT_CHAR_LEN 10 +#define MAX_CHAN_TO_PRINT 39 + +bool policy_mgr_dump_channel_list(uint32_t len, uint32_t *pcl_channels, + uint8_t *pcl_weight) +{ + uint32_t idx, buff_len, num = 0, count = 0, count_6G = 0; + char *chan_buff = NULL; + + buff_len = (QDF_MIN(len, MAX_CHAN_TO_PRINT) * CHAN_WEIGHT_CHAR_LEN) + 1; + chan_buff = qdf_mem_malloc(buff_len); + if (!chan_buff) + return false; + + policymgr_nofl_debug("Total PCL Chan Freq %d", len); + for (idx = 0; (idx < len) && (idx < NUM_CHANNELS); idx++) { + if (!WLAN_REG_IS_6GHZ_CHAN_FREQ(pcl_channels[idx])) { + num += qdf_scnprintf(chan_buff + num, buff_len - num, + " %d[%d]", pcl_channels[idx], + pcl_weight[idx]); + count++; + if (count >= MAX_CHAN_TO_PRINT) { + /* Print the MAX_CHAN_TO_PRINT channels */ + policymgr_nofl_debug("2G+5G Freq[weight]:%s", + chan_buff); + count = 0; + num = 0; + } + } else { + count_6G++; + } + } + /* Print any pending channels */ + if (num) + policymgr_nofl_debug("2G+5G Freq[weight]:%s", chan_buff); + + if (!count_6G) + goto free; + + count = 0; + num = 0; + for (idx = 0; (idx < len) && (idx < NUM_CHANNELS); idx++) { + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(pcl_channels[idx])) { + num += qdf_scnprintf(chan_buff + num, buff_len - num, + " %d[%d]", pcl_channels[idx], + pcl_weight[idx]); + count++; + if (count >= MAX_CHAN_TO_PRINT) { + /* Print the MAX_CHAN_TO_PRINT channels */ + policymgr_nofl_debug("6G Freq[weight]:%s", + chan_buff); + count = 0; + num = 0; + } + } + } + /* Print any pending channels */ + if (num) + policymgr_nofl_debug("6G Freq[weight]:%s", chan_buff); + +free: + qdf_mem_free(chan_buff); + + return true; +} diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_1x1_dbs_i.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_1x1_dbs_i.h new file mode 100644 index 0000000000000000000000000000000000000000..7acc8292fa08faf3373d1c85889a9ab8cf40d0ab --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_1x1_dbs_i.h @@ -0,0 +1,2747 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_1X1_DBS_H +#define __WLAN_POLICY_MGR_TABLES_1X1_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * second_connection_pcl_dbs_1x1_table - table which provides PCL + * for the 2nd connection, when we have a connection already in + * the system (with DBS supported by HW) + */ +enum policy_mgr_pcl_type + pm_second_connection_pcl_dbs_1x1_table[PM_MAX_ONE_CONNECTION_MODE] + [PM_MAX_NUM_OF_MODE][PM_MAX_CONC_PRIORITY_MODE] = { + [PM_STA_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_5G, PM_5G, PM_5G } }, + + [PM_STA_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_5G, PM_5G, PM_5G } }, + + [PM_STA_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = {PM_24G, PM_24G, PM_24G } }, + + [PM_STA_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = {PM_24G, PM_24G, PM_24G } }, + + [PM_P2P_CLI_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH, PM_24G_SCC_CH, PM_24G_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH, PM_24G_SCC_CH, PM_24G_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, +}; + +/** + * third_connection_pcl_dbs_table - table which provides PCL for + * the 3rd connection, when we have two connections already in + * the system (with DBS supported by HW). For helium that is NON-DBS, + * DBS 1x1, three port concurrency would be disabled, so for every + * combination MAX_PCL_TYPE would be the return value, if in future + * the requirement for 3 port concurrency comes, refer to below table + * for details. + * static pm_dbs_pcl_third_connection_table_type + * pm_third_connection_pcl_dbs_1x1_table = { + * [PM_STA_SAP_SCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_SCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_SCC_5_1x1] = { + * [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_SCC_5_2x2] = { + * [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_24_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH_5G, PM_5G, PM_5G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_24_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH_5G, PM_5G, PM_5G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_SCC_ON_5_SCC_ON_24_5G, PM_NONE, PM_SCC_ON_5_SCC_ON_24}, + * [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + * PM_SCC_ON_5_SCC_ON_24}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + * PM_SCC_ON_5_SCC_ON_24}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_SCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_SCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_MCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_MCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_SCC_5_1x1] = { + * [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + * PM_24G_SCC_CH_SBS_CH}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_SCC_5_2x2] = { + * [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + * PM_24G_SCC_CH_SBS_CH}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_MCC_5_1x1] = { + * [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + * [PM_SAP_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_MCC_5_2x2] = { + * [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + * [PM_SAP_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_MCC_24_5_1x1] = { + * [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_MCC_24_5_2x2] = { + * [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_SAP_SAP_DBS_1x1] = { + * [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + * PM_SCC_ON_5_SCC_ON_24}, + * [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + * PM_SCC_ON_5_SCC_ON_24}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + * PM_SCC_ON_5_SCC_ON_24}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_SCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_SCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_SCC_5_1x1] = { + * [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_SCC_5_2x2] = { + * [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_24_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH_5G, PM_5G, PM_5G}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_24_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH_5G, PM_5G, PM_5G}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_SCC_ON_5_SCC_ON_24_5G, PM_NONE, PM_SCC_ON_5_SCC_ON_24}, + * [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + * PM_SCC_ON_5_SCC_ON_24}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_SCC_ON_5_SCC_ON_24_5G, PM_NONE, PM_SCC_ON_5_SCC_ON_24}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_SCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_SCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_SCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_SCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_24_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_SCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_SCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_SCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_SCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_24_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + * PM_SCC_ON_5_SCC_ON_24}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_SCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_SCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_SCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_SCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_24_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_24_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * }; + */ +static pm_dbs_pcl_third_connection_table_type +pm_third_connection_pcl_dbs_1x1_table = { + [PM_STA_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_DBS_1x1] = { + [PM_STA_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_DBS_2x2] = { + [PM_STA_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_DBS_1x1] = { + [PM_STA_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_DBS_2x2] = { + [PM_STA_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_DBS_2x2] = { + [PM_STA_MODE] = { PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_DBS_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_DBS_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_NAN_DISC_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_NAN_DISC_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_NAN_DISC_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_NAN_DISC_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_NAN_DISC_DBS_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_STA_NAN_DISC_DBS_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_DBS_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_DBS_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, +}; + +/** + * next_action_two_connection_table - table which provides next + * action while a new connection is coming up, with one + * connection already in the system + */ +static policy_mgr_next_action_two_connection_table_type + pm_next_action_two_connection_dbs_1x1_table = { + [PM_STA_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_CLI_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_CLI_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_CLI_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_SAP_24_1x1] = {PM_NOP, PM_DBS}, + [PM_SAP_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_SAP_5_1x1] = {PM_DBS, PM_NOP}, + [PM_SAP_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_IBSS_24_1x1] = {PM_NOP, PM_DBS}, + [PM_IBSS_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_IBSS_5_1x1] = {PM_DBS, PM_NOP}, + [PM_IBSS_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, +}; + +/** + * next_action_three_connection_table - table which provides next + * action while a new connection is coming up, with two + * connections already in the system + */ +static policy_mgr_next_action_three_connection_table_type + pm_next_action_three_connection_dbs_1x1_table = { + [PM_STA_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_SAP_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_SAP_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_SAP_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_SAP_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_STA_SAP_MCC_24_5_2x2] = {PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_STA_SAP_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_P2P_GO_SCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_P2P_GO_MCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_GO_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_P2P_GO_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_STA_P2P_GO_MCC_24_5_2x2] = { + PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_P2P_CLI_SCC_24_2x2] = { + PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_P2P_CLI_MCC_24_2x2] = { + PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_CLI_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_P2P_CLI_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_CLI_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_SAP_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_SAP_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_CLI_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_CLI_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_CLI_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_CLI_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_CLI_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_CLI_SAP_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_P2P_CLI_SAP_MCC_24_5_2x2] = {PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_1x1] = {PM_NOP, PM_NOP}, + + [PM_SAP_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_SAP_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_2g_1x1_5g.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_2g_1x1_5g.h new file mode 100644 index 0000000000000000000000000000000000000000..54d4c8ab80c645d18042597e1ebf6e1b880e8e93 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_2g_1x1_5g.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_2X2_2G_1X1_5G_DBS_H +#define __WLAN_POLICY_MGR_TABLES_2X2_2G_1X1_5G_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * next_action_two_connection_table - table which provides next + * action while a new connection is coming up, with one + * connection already in the system + */ +static policy_mgr_next_action_two_connection_table_type + pm_next_action_two_connection_dbs_2x2_2g_1x1_5g_table = { + [PM_STA_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_5_2x2] = {PM_DBS2, PM_NOP}, + [PM_P2P_CLI_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_CLI_5_2x2] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_SAP_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_SAP_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_SAP_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_SAP_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_IBSS_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_IBSS_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_IBSS_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_IBSS_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, +}; + +/** + * next_action_three_connection_table - table which provides next + * action while a new connection is coming up, with two + * connections already in the system + */ +static policy_mgr_next_action_three_connection_table_type + pm_next_action_three_connection_dbs_2x2_2g_1x1_5g_table = { + [PM_STA_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_SAP_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_SAP_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_STA_SAP_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_SAP_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_STA_SAP_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_SAP_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_SAP_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_SAP_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_STA_P2P_GO_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_GO_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_GO_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_GO_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_GO_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_GO_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_STA_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_CLI_SCC_5_2x2] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_2x2] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_5_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_MCC_24_5_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_DBS_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_DBS_2x2] = {PM_NOP, PM_DBS2}, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, + PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, + PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_P2P_GO_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_SAP_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_SAP_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_SAP_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_SAP_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_P2P_CLI_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_SAP_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_CLI_SAP_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_CLI_SAP_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_SAP_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_SAP_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_SAP_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_SAP_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_SAP_SAP_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_SAP_SAP_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_SAP_SAP_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_SAP_SAP_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_SAP_SAP_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_SAP_SAP_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_SAP_SAP_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_SAP_SAP_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_5g_1x1_2g.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_5g_1x1_2g.h new file mode 100644 index 0000000000000000000000000000000000000000..bdf8223d8fbbab5b175de84b5ce0c5ce5176a9da --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_5g_1x1_2g.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_2X2_5G_1X1_2G_DBS_H +#define __WLAN_POLICY_MGR_TABLES_2X2_5G_1X1_2G_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * next_action_two_connection_table - table which provides next + * action while a new connection is coming up, with one + * connection already in the system + */ +static policy_mgr_next_action_two_connection_table_type + pm_next_action_two_connection_dbs_2x2_5g_1x1_2g_table = { + [PM_STA_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_24_2x2] = {PM_NOP, PM_DBS1}, + [PM_STA_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_CLI_24_2x2] = {PM_NOP, PM_DBS1}, + [PM_P2P_CLI_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_SAP_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_SAP_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_SAP_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_SAP_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_IBSS_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_IBSS_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_IBSS_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_IBSS_5_2x2] = {PM_DBS1, PM_NOP}, +}; + +/** + * next_action_three_connection_table - table which provides next + * action while a new connection is coming up, with two + * connections already in the system + */ +static policy_mgr_next_action_three_connection_table_type + pm_next_action_three_connection_dbs_2x2_5g_1x1_2g_table = { + [PM_STA_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_SAP_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_SAP_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_SAP_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_SAP_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_STA_P2P_GO_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_GO_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_GO_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_GO_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_STA_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_CLI_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_CLI_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_5_1x1] = {PM_DBS1, PM_DBS1}, + [PM_STA_P2P_CLI_MCC_24_5_2x2] = {PM_DBS1, PM_DBS1}, + [PM_STA_P2P_CLI_DBS_1x1] = {PM_DBS1, PM_DBS1}, + [PM_STA_P2P_CLI_DBS_2x2] = {PM_DBS1, PM_DBS1}, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, + PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, + PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_P2P_GO_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_SAP_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_SAP_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_P2P_CLI_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_CLI_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_CLI_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_SAP_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_SAP_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_SAP_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_SAP_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_SAP_SAP_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_SAP_SAP_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_SAP_SAP_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_SAP_SAP_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_dbs_i.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_dbs_i.h new file mode 100644 index 0000000000000000000000000000000000000000..dc4da9276e26056486e66d3bc87820729e1038f1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_dbs_i.h @@ -0,0 +1,2117 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_2X2_DBS_H +#define __WLAN_POLICY_MGR_TABLES_2X2_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * second_connection_pcl_dbs_2x2_table - table which provides PCL + * for the 2nd connection, when we have a connection already in + * the system (with DBS supported by HW) + * This table consolidates selection for P2PCLI, P2PGO, STA, SAP + * into the single set of STA entries for 2.4G and 5G. + */ +static enum policy_mgr_pcl_type + pm_second_connection_pcl_dbs_2x2_table[PM_MAX_ONE_CONNECTION_MODE] + [PM_MAX_NUM_OF_MODE][PM_MAX_CONC_PRIORITY_MODE] = { + [PM_STA_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_NAN_DISC_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_NAN_DISC_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_NAN_DISC_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_NAN_DISC_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH} }, + + [PM_SAP_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH} }, + + [PM_SAP_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G} }, + + [PM_SAP_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G} }, + + + [PM_IBSS_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_1x1] = { + [PM_STA_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_2x2] = { + [PM_STA_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24 } }, + + [PM_NAN_DISC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, + PM_5G_SCC_CH } }, +}; + +/** + * third_connection_pcl_dbs_table - table which provides PCL for + * the 3rd connection, when we have two connections already in + * the system (with DBS supported by HW) + */ +static pm_dbs_pcl_third_connection_table_type +pm_third_connection_pcl_dbs_2x2_table = { + [PM_STA_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_DBS_1x1] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_DBS_2x2] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_SAP_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_DBS_1x1] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_GO_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_DBS_2x2] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_GO_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_SAP_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_NAN_DISC_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_NAN_DISC_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_NAN_DISC_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_NAN_DISC_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_NAN_DISC_DBS_1x1] = { + [PM_STA_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_SAP_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_NAN_DISC_DBS_2x2] = { + [PM_STA_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_SAP_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = { PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { + PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_DBS_2x2] = { + [PM_STA_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { + PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_DBS_1x1] = { + [PM_STA_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24_5G, + PM_SCC_ON_5_SCC_ON_24_5G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_DBS_2x2] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_GO_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_DBS_1x1] = { + [PM_STA_MODE] = {PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_DBS_2x2] = { + [PM_STA_MODE] = {PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_SAP_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NAN_DISC_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_NAN_DISC_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH} }, + + [PM_STA_NAN_DISC_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH} }, + + [PM_STA_NAN_DISC_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_5G, PM_5G, PM_5G} }, + + [PM_STA_NAN_DISC_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_5G, PM_5G, PM_5G} }, + + [PM_STA_NAN_DISC_DBS_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G} }, + + [PM_STA_NAN_DISC_DBS_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_NDI_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G} }, + + [PM_NAN_DISC_NDI_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_DBS_1x1] = { + [PM_STA_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_SAP_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_NAN_DISC_NDI_DBS_2x2] = { + [PM_STA_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_SAP_MODE] = {PM_SCC_ON_24_SCC_ON_5_5G, PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_24_SCC_ON_5_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, +}; + +#ifdef FEATURE_FOURTH_CONNECTION +/** + * fourth_connection_pcl_dbs_table - table which provides PCL for + * the 4th connection, when we have 3 connections already in + * the system (with DBS supported by HW), this table is for auto products. + */ +#ifdef FOURTH_CONNECTION_AUTO +const enum policy_mgr_pcl_type +fourth_connection_pcl_dbs_table + [PM_MAX_THREE_CONNECTION_MODE][PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE] = { + [PM_STA_SAP_SCC_24_SAP_5_DBS] = { + [PM_STA_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_STA_SAP_SCC_5_SAP_24_DBS] = { + [PM_STA_MODE] = { PM_24G, PM_24G, PM_24G } }, + [PM_STA_SAP_SCC_24_STA_5_DBS] = { + [PM_SAP_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_STA_SAP_SCC_5_STA_24_DBS] = { + [PM_SAP_MODE] = { PM_24G, PM_24G, PM_24G } }, + [PM_NAN_DISC_SAP_SCC_24_NDI_5_DBS] = { + [PM_SAP_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_NAN_DISC_NDI_SCC_24_SAP_5_DBS] = { + [PM_SAP_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_SAP_NDI_SCC_5_NAN_DISC_24_DBS] = { + [PM_SAP_MODE] = { PM_24G, PM_24G, PM_24G } } +}; +#else +/** + * fourth_connection_pcl_dbs_table - table which provides PCL for + * the 4th connection, when we have 3 connections already in + * the system (with DBS supported by HW), this table is for mobile products + * If you want to support any 4 port other than the below in MCL add below as + * other concurrencies supported by auto may not be PORed for mobile products + * and vice-versa. + */ +const enum policy_mgr_pcl_type +fourth_connection_pcl_dbs_table + [PM_MAX_THREE_CONNECTION_MODE][PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE] = { + [PM_NAN_DISC_STA_24_NDI_5_DBS] = { + [PM_NDI_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_NAN_DISC_NDI_24_STA_5_DBS] = { + [PM_NDI_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_STA_NDI_5_NAN_DISC_24_DBS] = { + [PM_NDI_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_STA_NDI_NAN_DISC_24_SMM] = { + [PM_NDI_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_NAN_DISC_NDI_24_NDI_5_DBS] = { + [PM_STA_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_NDI_NDI_5_NAN_DISC_24_DBS] = { + [PM_STA_MODE] = { PM_5G, PM_5G, PM_5G } }, + [PM_NDI_NDI_NAN_DISC_24_SMM] = { + [PM_STA_MODE] = { PM_5G, PM_5G, PM_5G } } +}; +#endif +#endif + +/** + * next_action_two_connection_table - table which provides next + * action while a new connection is coming up, with one + * connection already in the system + */ +static policy_mgr_next_action_two_connection_table_type + pm_next_action_two_connection_dbs_2x2_table = { + [PM_STA_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_CLI_24_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_CLI_24_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_CLI_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_CLI_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_GO_24_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_24_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_GO_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_SAP_24_1x1] = {PM_NOP, PM_NOP}, + [PM_SAP_24_2x2] = {PM_NOP, PM_NOP}, + [PM_SAP_5_1x1] = {PM_DBS, PM_SBS}, + [PM_SAP_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_IBSS_24_1x1] = {PM_NOP, PM_NOP}, + [PM_IBSS_24_2x2] = {PM_NOP, PM_NOP}, + [PM_IBSS_5_1x1] = {PM_DBS, PM_NOP}, + [PM_IBSS_5_2x2] = {PM_DBS, PM_NOP}, +}; + +/** + * next_action_three_connection_table - table which provides next + * action while a new connection is coming up, with two + * connections already in the system + */ +static policy_mgr_next_action_three_connection_table_type + pm_next_action_three_connection_dbs_2x2_table = { + [PM_STA_SAP_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_SAP_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_SAP_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_SAP_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_SAP_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_STA_P2P_GO_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_P2P_GO_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_P2P_GO_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_STA_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_P2P_CLI_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_P2P_CLI_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_P2P_CLI_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_P2P_CLI_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_STA_STA_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_STA_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_STA_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_STA_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_STA_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_STA_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_STA_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_STA_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_STA_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_STA_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_STA_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_STA_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_STA_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_SAP_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_SAP_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS}, + [PM_SAP_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_SAP_SAP_SCC_5_2x2] = {PM_DBS, PM_NOP}, + + [PM_P2P_GO_P2P_GO_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_GO_P2P_GO_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_GO_P2P_GO_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_GO_P2P_GO_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_GO_P2P_GO_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_no_dbs_i.h b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_no_dbs_i.h new file mode 100644 index 0000000000000000000000000000000000000000..87fdc5da90f69d5dfcb7b7705a71f4d386a6caaa --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_no_dbs_i.h @@ -0,0 +1,1614 @@ +/* + * Copyright (c) 2012-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_NO_DBS_H +#define __WLAN_POLICY_MGR_TABLES_NO_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * second_connection_pcl_nodbs_table - table which provides PCL + * for the 2nd connection, when we have a connection already in + * the system (with DBS not supported by HW) + */ +static const enum policy_mgr_pcl_type +second_connection_pcl_nodbs_table[PM_MAX_ONE_CONNECTION_MODE] + [PM_MAX_NUM_OF_MODE][PM_MAX_CONC_PRIORITY_MODE] = { + [PM_STA_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_GO_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_GO_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, +}; + +/** + * third_connection_pcl_nodbs_table - table which provides PCL + * for the 3rd connection, when we have two connections already + * in the system (with DBS not supported by HW). For helium that is NON-DBS, + * DBS 1x1, three port concurrency would be disabled, so for every + * combination MAX_PCL_TYPE would be the return value, if in future + * the requirement for 3 port concurrency comes, refer to below table + * for details. + * static const enum policy_mgr_pcl_type + * third_connection_pcl_nodbs_table[PM_MAX_TWO_CONNECTION_MODE] + * [PM_MAX_NUM_OF_MODE][PM_MAX_CONC_PRIORITY_MODE] = { + * [PM_STA_SAP_SCC_24_1x1] = { + * [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_SCC_24_2x2] = { + * [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_SCC_5_1x1] = { + * [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_SCC_5_2x2] = { + * [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_24_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_MCC_24_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_SAP_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_SCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_SCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = { + * PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = { + * PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_SCC_5_1x1] = { + * [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_SCC_5_2x2] = { + * [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_24_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_MCC_24_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_GO_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_SCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_SCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_SCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_SCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_24_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_STA_P2P_CLI_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = { + * PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + * [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = { + * PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = { + * PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = { + * [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = { + * [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + * [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_P2P_CLI_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_SCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_SCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_SCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_SCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_24_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_GO_SAP_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * + * [PM_P2P_CLI_SAP_SCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_SCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_24_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_24_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_SCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_SCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_24_5_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_MCC_24_5_2x2] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * + * [PM_P2P_CLI_SAP_DBS_1x1] = { + * [PM_STA_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_SAP_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_CLIENT_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_P2P_GO_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + * [PM_IBSS_MODE] = { + * PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + * }; + */ +static const enum policy_mgr_pcl_type +third_connection_pcl_nodbs_table[PM_MAX_TWO_CONNECTION_MODE] + [PM_MAX_NUM_OF_MODE][PM_MAX_CONC_PRIORITY_MODE] = { + [PM_STA_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = + {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + + [PM_P2P_CLI_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_ucfg.c b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_ucfg.c new file mode 100644 index 0000000000000000000000000000000000000000..44fcb2d964afd7faefd6a41a687ae53ea00126b1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cmn_services/policy_mgr/src/wlan_policy_mgr_ucfg.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include "wlan_policy_mgr_ucfg.h" +#include "wlan_policy_mgr_i.h" +#include "cfg_ucfg_api.h" +#include "wlan_policy_mgr_api.h" + +static QDF_STATUS policy_mgr_init_cfg(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct policy_mgr_cfg *cfg; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + cfg = &pm_ctx->cfg; + + cfg->mcc_to_scc_switch = cfg_get(psoc, CFG_MCC_TO_SCC_SWITCH); + cfg->sys_pref = cfg_get(psoc, CFG_CONC_SYS_PREF); + cfg->max_conc_cxns = cfg_get(psoc, CFG_MAX_CONC_CXNS); + cfg->conc_rule1 = cfg_get(psoc, CFG_ENABLE_CONC_RULE1); + cfg->conc_rule2 = cfg_get(psoc, CFG_ENABLE_CONC_RULE2); + cfg->pcl_band_priority = cfg_get(psoc, CFG_PCL_BAND_PRIORITY); + cfg->dbs_selection_plcy = cfg_get(psoc, CFG_DBS_SELECTION_PLCY); + cfg->vdev_priority_list = cfg_get(psoc, CFG_VDEV_CUSTOM_PRIORITY_LIST); + cfg->chnl_select_plcy = cfg_get(psoc, CFG_CHNL_SELECT_LOGIC_CONC); + cfg->enable_mcc_adaptive_sch = + cfg_get(psoc, CFG_ENABLE_MCC_ADAPTIVE_SCH_ENABLED_NAME); + cfg->enable_sta_cxn_5g_band = + cfg_get(psoc, CFG_ENABLE_STA_CONNECTION_IN_5GHZ); + cfg->allow_mcc_go_diff_bi = + cfg_get(psoc, CFG_ALLOW_MCC_GO_DIFF_BI); + cfg->enable_overlap_chnl = + cfg_get(psoc, CFG_ENABLE_OVERLAP_CH); + cfg->dual_mac_feature = + cfg_get(psoc, CFG_DUAL_MAC_FEATURE_DISABLE); + cfg->is_force_1x1_enable = + cfg_get(psoc, CFG_FORCE_1X1_FEATURE); + cfg->sta_sap_scc_on_dfs_chnl = + cfg_get(psoc, CFG_STA_SAP_SCC_ON_DFS_CHAN); + if (cfg->sta_sap_scc_on_dfs_chnl == 2 && + !cfg_get(psoc, CFG_ENABLE_DFS_MASTER_CAPABILITY)) + cfg->sta_sap_scc_on_dfs_chnl = 0; + cfg->nan_sap_scc_on_lte_coex_chnl = + cfg_get(psoc, CFG_NAN_SAP_SCC_ON_LTE_COEX_CHAN); + cfg->sta_sap_scc_on_lte_coex_chnl = + cfg_get(psoc, CFG_STA_SAP_SCC_ON_LTE_COEX_CHAN); + cfg->sap_mandatory_chnl_enable = + cfg_get(psoc, CFG_ENABLE_SAP_MANDATORY_CHAN_LIST); + cfg->mark_indoor_chnl_disable = + cfg_get(psoc, CFG_MARK_INDOOR_AS_DISABLE_FEATURE); + cfg->go_force_scc = cfg_get(psoc, CFG_P2P_GO_ENABLE_FORCE_SCC); + cfg->prefer_5g_scc_to_dbs = cfg_get(psoc, CFG_PREFER_5G_SCC_TO_DBS); + + return QDF_STATUS_SUCCESS; +} + +static void policy_mgr_deinit_cfg(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return; + } + + qdf_mem_zero(&pm_ctx->cfg, sizeof(pm_ctx->cfg)); +} + +QDF_STATUS ucfg_policy_mgr_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + + status = policy_mgr_init_cfg(psoc); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("pm_ctx is NULL"); + return status; + } + + status = policy_mgr_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("psoc open fail"); + policy_mgr_psoc_close(psoc); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +void ucfg_policy_mgr_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + policy_mgr_psoc_close(psoc); + policy_mgr_deinit_cfg(psoc); +} + +QDF_STATUS ucfg_policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch) +{ + return policy_mgr_get_mcc_scc_switch(psoc, mcc_scc_switch); +} + +QDF_STATUS ucfg_policy_mgr_get_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t *sys_pref) +{ + return policy_mgr_get_sys_pref(psoc, sys_pref); +} + +QDF_STATUS ucfg_policy_mgr_set_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t sys_pref) +{ + return policy_mgr_set_sys_pref(psoc, sys_pref); +} + +QDF_STATUS ucfg_policy_mgr_get_conc_rule1(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule1) +{ + return policy_mgr_get_conc_rule1(psoc, conc_rule1); +} + +QDF_STATUS ucfg_policy_mgr_get_conc_rule2(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule2) +{ + return policy_mgr_get_conc_rule2(psoc, conc_rule2); +} + +QDF_STATUS ucfg_policy_mgr_get_chnl_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *chnl_select_plcy) +{ + return policy_mgr_get_chnl_select_plcy(psoc, chnl_select_plcy); +} + + +QDF_STATUS ucfg_policy_mgr_set_dynamic_mcc_adaptive_sch( + struct wlan_objmgr_psoc *psoc, + bool dynamic_mcc_adaptive_sch) +{ + return policy_mgr_set_dynamic_mcc_adaptive_sch( + psoc, dynamic_mcc_adaptive_sch); +} + +QDF_STATUS ucfg_policy_mgr_get_dynamic_mcc_adaptive_sch( + struct wlan_objmgr_psoc *psoc, + bool *dynamic_mcc_adaptive_sch) +{ + return policy_mgr_get_dynamic_mcc_adaptive_sch( + psoc, dynamic_mcc_adaptive_sch); +} + +QDF_STATUS ucfg_policy_mgr_get_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_adaptive_sch) +{ + return policy_mgr_get_mcc_adaptive_sch(psoc, mcc_adaptive_sch); +} + +QDF_STATUS ucfg_policy_mgr_get_sta_cxn_5g_band(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sta_cxn_5g_band) +{ + return policy_mgr_get_sta_cxn_5g_band(psoc, enable_sta_cxn_5g_band); +} + +QDF_STATUS +ucfg_policy_mgr_get_allow_mcc_go_diff_bi(struct wlan_objmgr_psoc *psoc, + uint8_t *allow_mcc_go_diff_bi) +{ + return policy_mgr_get_allow_mcc_go_diff_bi(psoc, allow_mcc_go_diff_bi); +} + +QDF_STATUS +ucfg_policy_mgr_get_enable_overlap_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_overlap_chnl) +{ + return policy_mgr_get_enable_overlap_chnl(psoc, enable_overlap_chnl); +} + +QDF_STATUS ucfg_policy_mgr_get_dual_mac_feature(struct wlan_objmgr_psoc *psoc, + uint8_t *dual_mac_feature) +{ + return policy_mgr_get_dual_mac_feature(psoc, dual_mac_feature); +} + +QDF_STATUS ucfg_policy_mgr_get_force_1x1(struct wlan_objmgr_psoc *psoc, + uint8_t *force_1x1) +{ + return policy_mgr_get_force_1x1(psoc, force_1x1); +} + +QDF_STATUS +ucfg_policy_mgr_get_sta_sap_scc_on_dfs_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sta_sap_scc_on_dfs_chnl) +{ + return policy_mgr_get_sta_sap_scc_on_dfs_chnl(psoc, + sta_sap_scc_on_dfs_chnl); +} + +bool +ucfg_policy_mgr_get_dfs_master_dynamic_enabled(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + return policy_mgr_get_dfs_master_dynamic_enabled(psoc, vdev_id); +} + +QDF_STATUS +ucfg_policy_mgr_get_sta_sap_scc_lte_coex_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sta_sap_scc_lte_coex) +{ + return policy_mgr_get_sta_sap_scc_lte_coex_chnl(psoc, + sta_sap_scc_lte_coex); +} + +QDF_STATUS +ucfg_policy_mgr_init_chan_avoidance(struct wlan_objmgr_psoc *psoc, + qdf_freq_t *chan_freq_list, + uint16_t chan_cnt) +{ + return policy_mgr_init_chan_avoidance(psoc, chan_freq_list, chan_cnt); +} + +QDF_STATUS ucfg_policy_mgr_get_sap_mandt_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t *sap_mandt_chnl) +{ + return policy_mgr_get_sap_mandt_chnl(psoc, sap_mandt_chnl); +} + +QDF_STATUS +ucfg_policy_mgr_get_indoor_chnl_marking(struct wlan_objmgr_psoc *psoc, + uint8_t *indoor_chnl_marking) +{ + return policy_mgr_get_indoor_chnl_marking(psoc, indoor_chnl_marking); +} diff --git a/drivers/staging/qcacld-3.0/components/cp_stats/dispatcher/inc/wlan_cp_stats_ext_type.h b/drivers/staging/qcacld-3.0/components/cp_stats/dispatcher/inc/wlan_cp_stats_ext_type.h new file mode 100644 index 0000000000000000000000000000000000000000..a4f5ae1a19e02c52fff27a43107b63eda85bb87f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/cp_stats/dispatcher/inc/wlan_cp_stats_ext_type.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_ext_cp_stats_types.h + * + * This header file is included by the converged control path statistics + * component to map legacy statistic structures to the external + * (ext)typedefs used by the converged code. This mechanism allows the + * legacy structs to be included as part of the global objmgr objects. + */ + +#ifndef __WLAN_EXT_CP_STATS_TYPE_H__ +#define __WLAN_EXT_CP_STATS_TYPE_H__ + +/** + * typedef psoc_ext_cp_stats_t - Definition of psoc cp stats pointer + * Define obj_stats from external umac/cp_stats component point to this type + */ +typedef struct psoc_mc_cp_stats psoc_ext_cp_stats_t; + +/** + * typedef pdev_ext_cp_stats_t - Definition of pdev cp stats pointer + * Define pdev_stats from external umac/cp_stats component point to this type + */ +typedef struct pdev_mc_cp_stats pdev_ext_cp_stats_t; + +/** + * typedef vdev_ext_cp_stats_t - Definition of vdev cp stats pointer + * Define vdev_stats from external umac/cp_stats component point to this type + */ +typedef struct vdev_mc_cp_stats vdev_ext_cp_stats_t; + +/** + * typedef peer_ext_cp_stats_t - Definition of peer cp stats pointer + * Define peer_stats from external umac/cp_stats component point to this type + */ +typedef struct peer_mc_cp_stats peer_ext_cp_stats_t; + +/** + * typedef peer_ext_adv_cp_stats_t - Definition of peer adv cp stats pointer + * Define peer_adv_stats from external umac/cp_stats component point to this + * type + */ +typedef struct peer_adv_mc_cp_stats peer_ext_adv_cp_stats_t; + +#endif /* __WLAN_EXT_CP_STATS_TYPE_H__ */ + diff --git a/drivers/staging/qcacld-3.0/components/disa/core/inc/wlan_disa_main.h b/drivers/staging/qcacld-3.0/components/disa/core/inc/wlan_disa_main.h new file mode 100644 index 0000000000000000000000000000000000000000..cefc30007a126d805536e32c82d602cf9b646bb7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/core/inc/wlan_disa_main.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare various api which shall be used by + * DISA user configuration and target interface + */ + +#ifndef _WLAN_DISA_MAIN_H_ +#define _WLAN_DISA_MAIN_H_ + +#include "wlan_disa_public_struct.h" +#include "wlan_disa_obj_mgmt_public_struct.h" +#include "wlan_disa_priv.h" +#include "wlan_disa_objmgr.h" + +#define disa_fatal(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_DISA, params) +#define disa_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_DISA, params) +#define disa_warn(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_DISA, params) +#define disa_info(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_DISA, params) +#define disa_debug(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_DISA, params) + +#define disa_nofl_fatal(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_DISA, params) +#define disa_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_DISA, params) +#define disa_nofl_warn(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_DISA, params) +#define disa_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_DISA, params) +#define disa_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_DISA, params) + +#define DISA_ENTER() \ + QDF_TRACE_ENTER(QDF_MODULE_ID_DISA, "enter") +#define DISA_EXIT() \ + QDF_TRACE_EXIT(QDF_MODULE_ID_DISA, "exit") + +/** + * disa_allocate_ctx() - Api to allocate disa ctx + * + * Helper function to allocate disa ctx + * + * Return: Success or failure. + */ +QDF_STATUS disa_allocate_ctx(void); + +/** + * disa_free_ctx() - to free disa context + * + * Helper function to free disa context + * + * Return: None. + */ +void disa_free_ctx(void); + +/** + * disa_get_context() - to get disa context + * + * Helper function to get disa context + * + * Return: disa context. + */ +struct wlan_disa_ctx *disa_get_context(void); + +/** + * disa_core_encrypt_decrypt_req() - Form encrypt/decrypt request + * @psoc: objmgr psoc object + * @req: DISA encrypt/decrypt request parameters + * + * Return: QDF status success or failure + */ +QDF_STATUS disa_core_encrypt_decrypt_req(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_req_params *req, + encrypt_decrypt_resp_callback cb, + void *cookie); + +#endif /* end of _WLAN_DISA_MAIN_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/disa/core/inc/wlan_disa_objmgr.h b/drivers/staging/qcacld-3.0/components/disa/core/inc/wlan_disa_objmgr.h new file mode 100644 index 0000000000000000000000000000000000000000..6bb497741a6c5f175276ee4015f455300f8bcad6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/core/inc/wlan_disa_objmgr.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains various object manager related wrappers and helpers + */ + +#ifndef _WLAN_DISA_OBJMGR_H +#define _WLAN_DISA_OBJMGR_H + +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_disa_obj_mgmt_public_struct.h" + +/* Get/Put Ref */ + +/** + * disa_psoc_get_ref() - DISA wrapper to increment ref count, if allowed + * @psoc: PSOC object + * + * DISA wrapper to increment ref count after checking valid object state + * + * Return: SUCCESS/FAILURE + */ +static inline QDF_STATUS disa_psoc_get_ref(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_try_get_ref(psoc, WLAN_DISA_ID); +} + +/** + * disa_psoc_put_ref() - DISA wrapper to decrement ref count + * @psoc: PSOC object + * + * DISA wrapper to decrement ref count of psoc + * + * Return: SUCCESS/FAILURE + */ +static inline void disa_psoc_put_ref(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_release_ref(psoc, WLAN_DISA_ID); +} + +/* Private Data */ + +/** + * disa_psoc_get_priv_nolock(): DISA wrapper to retrieve component object + * @psoc: Psoc pointer + * + * DISA wrapper used to get the component private object pointer + * + * Return: Component private object + */ +static inline void *disa_psoc_get_priv_nolock(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_DISA); +} + +/* Ids */ +static inline uint8_t +disa_vdev_get_id(struct wlan_objmgr_vdev *vdev) +{ + uint8_t vdev_id; + + vdev_id = wlan_vdev_get_id(vdev); + QDF_BUG(vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS); + + return vdev_id; +} + +/* Tree Navigation */ + +/** + * !PLEASE READ! + * + * The following are objmgr navigation helpers for traversing objmgr object + * trees. + * + * Objmgr ensures parents of an objmgr object cannot be freed while a valid + * reference to one of its children is held. Based on this fact, all of these + * navigation helpers make the following assumptions to ensure safe usage: + * + * 1) The caller must hold a valid reference to the input objmgr object! + * E.g. Use disa_[peer|vdev|pdev|psoc]_get_ref() on the input objmgr + * object before using these APIs + * 2) Given assumption #1, the caller does not need to hold a reference to the + * parents of the input objmgr object + * 3) Given assumption #1, parents of the input objmgr object cannot be null + * 4) Given assumption #1, private contexts of any parent of the input objmgr + * object cannot be null + * + * These characteristics remove the need for most sanity checks when dealing + * with objmgr objects. However, please note that if you ever walk the tree + * from parent to child, references must be acquired all the way down! + * + * Example #1: + * + * psoc = disa_vdev_get_psoc(vdev); + * if (!psoc) + * // this is dead code + * + * Example #2: + * + * psoc_priv = disa_psoc_get_priv(psoc); + * if (!psoc_priv) + * // this is dead code + * + * Example #3: + * + * status = disa_psoc_get_ref(psoc); + * + * ... + * + * psoc = disa_vdev_get_psoc(vdev); + * + * // the next line is redundant, don't do it! + * status = disa_psoc_get_ref(psoc); + */ + +/* Tree Navigation: psoc */ +static inline struct disa_psoc_priv_obj * +disa_psoc_get_priv(struct wlan_objmgr_psoc *psoc) +{ + struct disa_psoc_priv_obj *psoc_priv; + + psoc_priv = disa_psoc_get_priv_nolock(psoc); + QDF_BUG(psoc_priv); + + return psoc_priv; +} + +static inline struct wlan_objmgr_vdev * +disa_psoc_get_vdev(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + + QDF_BUG(vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS); + if (vdev_id >= WLAN_UMAC_PSOC_MAX_VDEVS) + return NULL; + + wlan_psoc_obj_lock(psoc); + vdev = psoc->soc_objmgr.wlan_vdev_list[vdev_id]; + wlan_psoc_obj_unlock(psoc); + + return vdev; +} + +/* Tree Navigation: pdev */ +static inline struct wlan_objmgr_psoc * +disa_pdev_get_psoc(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_objmgr_psoc *psoc; + + psoc = wlan_pdev_get_psoc(pdev); + QDF_BUG(psoc); + + return psoc; +} + +/* Tree Navigation: vdev */ +static inline struct wlan_objmgr_pdev * +disa_vdev_get_pdev(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_pdev *pdev; + + pdev = wlan_vdev_get_pdev(vdev); + QDF_BUG(pdev); + + return pdev; +} + +static inline struct wlan_objmgr_psoc * +disa_vdev_get_psoc(struct wlan_objmgr_vdev *vdev) +{ + return disa_pdev_get_psoc(disa_vdev_get_pdev(vdev)); +} + +#endif /* _WLAN_DISA_OBJMGR_H */ diff --git a/drivers/staging/qcacld-3.0/components/disa/core/inc/wlan_disa_priv.h b/drivers/staging/qcacld-3.0/components/disa/core/inc/wlan_disa_priv.h new file mode 100644 index 0000000000000000000000000000000000000000..010d82f08d253f7d8b72517910134d7a584b0e15 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/core/inc/wlan_disa_priv.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + /** + * DOC: Declare various struct, macros which are used privately in DISA + * component. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_DISA_PRIV_STRUCT_H_ +#define _WLAN_DISA_PRIV_STRUCT_H_ + +#include +#include "wlan_disa_public_struct.h" + +/** + * struct disa_psoc_priv_obj -psoc specific user configuration required for disa + * + * @disa_rx_ops: rx operations for disa + * @disa_tx_ops: tx operations for disa + * @disa_psoc_lock: spin lock for disa psoc priv ctx + */ +struct disa_psoc_priv_obj { + struct wlan_disa_rx_ops disa_rx_ops; + struct wlan_disa_tx_ops disa_tx_ops; + qdf_spinlock_t lock; +}; + +/** + * struct wlan_disa_ctx - disa context for single command + * + * @callback: hdd callback for disa encrypt/decrypt resp + * @callback_context: context for the callback + * @lock: spin lock for disa context + */ +struct wlan_disa_ctx { + encrypt_decrypt_resp_callback callback; + void *callback_context; + bool request_active; + qdf_spinlock_t lock; +}; + +#endif /* end of _WLAN_DISA_PRIV_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/disa/core/src/wlan_disa_main.c b/drivers/staging/qcacld-3.0/components/disa/core/src/wlan_disa_main.c new file mode 100644 index 0000000000000000000000000000000000000000..86431bf3fb1061b66d74233aefbfb6bbb6582411 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/core/src/wlan_disa_main.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Implement various api / helper function which shall be used for + * DISA user and target interface. + */ + +#include "wlan_disa_main.h" +#include "wlan_disa_obj_mgmt_public_struct.h" +#include "wlan_disa_tgt_api.h" + +static struct wlan_disa_ctx *gp_disa_ctx; + +QDF_STATUS disa_allocate_ctx(void) +{ + /* If it is already created, ignore */ + if (gp_disa_ctx) { + disa_debug("already allocated disa_ctx"); + return QDF_STATUS_SUCCESS; + } + + /* allocate DISA ctx */ + gp_disa_ctx = qdf_mem_malloc(sizeof(*gp_disa_ctx)); + if (!gp_disa_ctx) { + disa_err("unable to allocate disa_ctx"); + QDF_ASSERT(0); + return QDF_STATUS_E_NOMEM; + } + qdf_spinlock_create(&gp_disa_ctx->lock); + + return QDF_STATUS_SUCCESS; +} + +void disa_free_ctx(void) +{ + if (!gp_disa_ctx) { + disa_err("disa ctx is already freed"); + QDF_ASSERT(0); + return; + } + qdf_spinlock_destroy(&gp_disa_ctx->lock); + qdf_mem_free(gp_disa_ctx); + gp_disa_ctx = NULL; +} + +struct wlan_disa_ctx *disa_get_context(void) +{ + return gp_disa_ctx; +} + +QDF_STATUS disa_core_encrypt_decrypt_req(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_req_params *req, + encrypt_decrypt_resp_callback cb, + void *cookie) +{ + struct wlan_disa_ctx *disa_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + DISA_ENTER(); + disa_ctx = disa_get_context(); + if (!disa_ctx) { + disa_err("DISA context is NULL!"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock_bh(&disa_ctx->lock); + if (!disa_ctx->request_active) { + disa_ctx->callback = cb; + disa_ctx->callback_context = cookie; + disa_ctx->request_active = true; + } else { + status = QDF_STATUS_E_INVAL; + } + qdf_spin_unlock_bh(&disa_ctx->lock); + + if (status != QDF_STATUS_SUCCESS) { + disa_err("A request is already active!"); + return status; + } + + status = disa_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + disa_err("DISA cannot get the reference out of psoc"); + return status; + } + + status = tgt_disa_encrypt_decrypt_req(psoc, req); + disa_psoc_put_ref(psoc); + + DISA_EXIT(); + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_obj_mgmt_api.h b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_obj_mgmt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..12a689a699119767f10246727b1a8f0e4a0a9034 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_obj_mgmt_api.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare utility API related to the disa component + * called by other components + */ + +#ifndef _WLAN_DISA_OBJ_MGMT_API_H_ +#define _WLAN_DISA_OBJ_MGMT_API_H_ + +#include + +struct wlan_objmgr_psoc; + +/** + * disa_init() - register disa notification handlers. + * + * This function registers disa related notification handlers. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +#ifdef WLAN_FEATURE_DISA +QDF_STATUS disa_init(void); +#else +static inline QDF_STATUS disa_init(void) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * disa_deinit() - unregister disa notification handlers. + * + * This function unregisters disa related notification handlers. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +#ifdef WLAN_FEATURE_DISA +QDF_STATUS disa_deinit(void); +#else +static inline QDF_STATUS disa_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * disa_psoc_enable() - Trigger psoc enable for DISA + * @psoc: objmgr psoc object + * + * Return: QDF status success or failure + */ +#ifdef WLAN_FEATURE_DISA +QDF_STATUS disa_psoc_enable(struct wlan_objmgr_psoc *psoc); +#else +static inline QDF_STATUS disa_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * disa_psoc_disable() - Trigger psoc disable for DISA + * @psoc: objmgr psoc object + * + * Return: QDF status success or failure + */ +#ifdef WLAN_FEATURE_DISA +QDF_STATUS disa_psoc_disable(struct wlan_objmgr_psoc *psoc); +#else +static inline QDF_STATUS disa_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * disa_psoc_object_created_notification(): disa psoc create handler + * @psoc: psoc which is going to created by objmgr + * @arg: argument for psoc create handler + * + * Attach psoc private object, register rx/tx ops and event handlers + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS disa_psoc_object_created_notification( + struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * disa_psoc_object_destroyed_notification(): disa psoc destroy handler + * @psoc: objmgr object corresponding to psoc which is going to be destroyed + * @arg: argument for psoc destroy handler + * + * Detach and free psoc private object, unregister event handlers + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS disa_psoc_object_destroyed_notification( + struct wlan_objmgr_psoc *psoc, void *arg); + +#endif /* end of _WLAN_DISA_OBJ_MGMT_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_obj_mgmt_public_struct.h b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_obj_mgmt_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..76aae8163d82d5aa721c3c423c53c4611bf733f4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_obj_mgmt_public_struct.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which are used for object mgmt in disa. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_DISA_OBJ_MGMT_PUBLIC_STRUCT_H_ +#define _WLAN_DISA_OBJ_MGMT_PUBLIC_STRUCT_H_ + +struct wlan_objmgr_psoc; +struct disa_encrypt_decrypt_req_params; +struct disa_encrypt_decrypt_resp_params; + +/** + * struct wlan_disa_tx_ops - structure of tx operation function + * pointers for disa component + * @disa_encrypt_decrypt_req: send encrypt/decrypt request + * @disa_register_ev_handlers: register disa event handlers + * @disa_unregister_ev_handlers: unregister disa event handlers + */ +struct wlan_disa_tx_ops { + QDF_STATUS (*disa_encrypt_decrypt_req)(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_req_params *req); + QDF_STATUS (*disa_register_ev_handlers)(struct wlan_objmgr_psoc *psoc); + QDF_STATUS (*disa_unregister_ev_handlers) + (struct wlan_objmgr_psoc *psoc); +}; + +/** + * struct wlan_disa_rx_ops - structure of rx operation function + * pointers for disa component + * @encrypt_decrypt_msg_resp: send response of encrypt/decrypt request + */ +struct wlan_disa_rx_ops { + QDF_STATUS (*encrypt_decrypt_msg_resp)(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_resp_params *resp); +}; +#endif /* end of _WLAN_DISA_OBJ_MGMT_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_public_struct.h b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..1c30f853166775b9227af5e7071aee5a34bc437a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_public_struct.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which shall be used in the DISA + * component. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_DISA_PUBLIC_STRUCT_H_ +#define _WLAN_DISA_PUBLIC_STRUCT_H_ + +#include + +#define MAC_MAX_KEY_LENGTH 32 +#define MAC_PN_LENGTH 8 +#define MAX_MAC_HEADER_LEN 32 +#define MIN_MAC_HEADER_LEN 24 + +/** + * struct disa_encrypt_decrypt_req_params - disa encrypt request + * @vdev_id: virtual device id + * @key_flag: This indicates firmware to encrypt/decrypt payload + * see ENCRYPT_DECRYPT_FLAG + * @key_idx: Index used in storing key + * @key_cipher: cipher used for encryption/decryption + * Eg: see WMI_CIPHER_AES_CCM for CCMP + * @key_len: length of key data + * @key_txmic_len: length of Tx MIC + * @key_rxmic_len: length of Rx MIC + * @key_data: Key + * @pn: packet number + * @mac_header: MAC header + * @data_len: length of data + * @data: pointer to payload + */ +struct disa_encrypt_decrypt_req_params { + uint32_t vdev_id; + uint8_t key_flag; + uint32_t key_idx; + uint32_t key_cipher; + uint32_t key_len; + uint32_t key_txmic_len; + uint32_t key_rxmic_len; + uint8_t key_data[MAC_MAX_KEY_LENGTH]; + uint8_t pn[MAC_PN_LENGTH]; + uint8_t mac_header[MAX_MAC_HEADER_LEN]; + uint32_t data_len; + uint8_t *data; +}; + +/** + * struct disa_encrypt_decrypt_resp_params - disa encrypt response + * @vdev_id: vdev id + * @status: status + * @data_length: data length + * @data: data pointer + */ +struct disa_encrypt_decrypt_resp_params { + uint32_t vdev_id; + int32_t status; + uint32_t data_len; + uint8_t *data; +}; + +typedef void (*encrypt_decrypt_resp_callback)(void *cookie, + struct disa_encrypt_decrypt_resp_params *resp) ; +#endif /* end of _WLAN_DISA_PUBLIC_STRUCT_H_ */ + diff --git a/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_tgt_api.h b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..ad900deb14cfa980772ff08a4acbb87d9b2eda89 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_tgt_api.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare public API for disa to interact with target/WMI + */ + +#ifndef _WLAN_DISA_TGT_API_H_ +#define _WLAN_DISA_TGT_API_H_ + +#include + +struct wlan_objmgr_psoc; +struct disa_encrypt_decrypt_req_params; +struct disa_encrypt_decrypt_resp_params; + +#define GET_DISA_TX_OPS_FROM_PSOC(psoc) \ + (&disa_psoc_get_priv(psoc)->disa_tx_ops) + +/** + * tgt_disa_encrypt_decrypt_req() - send encrypt/decrypt request to target if + * @psoc: objmgr psoc object + * @req: encrypt/decrypt parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_disa_encrypt_decrypt_req(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_req_params *req); + +/** + * tgt_disa_encrypt_decrypt_resp() - receive encrypt/decrypt response + * from target if + * @psoc: objmgr psoc object + * @resp: encrypt/decrypt response containing results + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_disa_encrypt_decrypt_resp(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_resp_params *resp); + +/** + * tgt_disa_register_ev_handlers() - API to register disa event handlers + * @psoc: objmgr psoc object + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS tgt_disa_register_ev_handlers(struct wlan_objmgr_psoc *psoc); + +/** + * tgt_disa_unregister_ev_handlers() - API to unregister disa event handlers + * @psoc: objmgr psoc object + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS tgt_disa_unregister_ev_handlers(struct wlan_objmgr_psoc *psoc); + +#endif /* end of _WLAN_DISA_TGT_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_ucfg_api.h b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..b9c8206d3582438b1a24dbfae14a744820dabee9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/dispatcher/inc/wlan_disa_ucfg_api.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare public API related to the disa called by north bound HDD/OSIF + */ + +#ifndef _WLAN_DISA_UCFG_API_H_ +#define _WLAN_DISA_UCFG_API_H_ + +#include "wlan_disa_public_struct.h" +struct wlan_objmgr_psoc; +struct disa_encrypt_decrypt_req_params; + + +/** + * ucfg_disa_encrypt_decrypt_req() - Send encrypt/decrypt request to the DISA + * core + * @psoc: objmgr psoc object + * @req: DISA encrypt/decrypt request parameters + * @cb: Response callback for the encrypt/decrypt request + * @cookie: Cookie to pass to the response callback + * + * Return: QDF status success or failure + */ +QDF_STATUS ucfg_disa_encrypt_decrypt_req(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_req_params *req, + encrypt_decrypt_resp_callback cb, + void *cookie); + + +#endif /* end of _WLAN_DISA_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/disa/dispatcher/src/wlan_disa_obj_mgmt_api.c b/drivers/staging/qcacld-3.0/components/disa/dispatcher/src/wlan_disa_obj_mgmt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..885ab982ef6c02ae0eca18db990bf5ab7d4ba786 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/dispatcher/src/wlan_disa_obj_mgmt_api.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define utility API related to the DISA component + * called by other components + */ + +#include "wlan_disa_obj_mgmt_api.h" +#include "wlan_disa_main.h" +#include "target_if_disa.h" +#include "wlan_disa_tgt_api.h" +#include "wlan_objmgr_global_obj.h" + +/** + * disa_init() - register disa notification handlers. + * + * This function registers disa related notification handlers and + * allocates disa context. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS disa_init(void) +{ + QDF_STATUS status; + + DISA_ENTER(); + + if (disa_allocate_ctx() != QDF_STATUS_SUCCESS) { + disa_err("unable to allocate disa ctx"); + status = QDF_STATUS_E_FAULT; + goto out; + } + + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_DISA, + disa_psoc_object_created_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + disa_err("unable to register psoc create handler"); + goto err_free_ctx; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_DISA, + disa_psoc_object_destroyed_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + disa_err("unable to register psoc destroy handler"); + wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_DISA, + disa_psoc_object_created_notification, + NULL); + } else { + goto out; + } + +err_free_ctx: + disa_free_ctx(); +out: + DISA_EXIT(); + + return status; +} + +/** + * disa_deinit() - unregister disa notification handlers. + * + * This function unregisters disa related notification handlers and + * frees disa context. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS disa_deinit(void) +{ + QDF_STATUS status; + + DISA_ENTER(); + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_DISA, + disa_psoc_object_destroyed_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) + disa_err("unable to unregister psoc create handle"); + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_DISA, + disa_psoc_object_created_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) + disa_err("unable to unregister psoc create handle"); + + disa_free_ctx(); + DISA_EXIT(); + + return status; +} + +/** + * disa_psoc_object_created_notification(): disa psoc create handler + * @psoc: psoc which is going to created by objmgr + * @arg: argument for psoc create handler + * + * Attach psoc private object, register rx/tx ops and event handlers + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS disa_psoc_object_created_notification( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct disa_psoc_priv_obj *disa_priv; + QDF_STATUS status; + + DISA_ENTER(); + + disa_priv = qdf_mem_malloc(sizeof(*disa_priv)); + if (!disa_priv) { + disa_err("Failed to allocate disa_priv"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_DISA, + (void *)disa_priv, QDF_STATUS_SUCCESS); + if (status != QDF_STATUS_SUCCESS) { + disa_err("Failed to attach disa_priv with psoc"); + qdf_mem_free(disa_priv); + goto out; + } + + qdf_spinlock_create(&disa_priv->lock); + target_if_disa_register_tx_ops(&disa_priv->disa_tx_ops); + +out: + DISA_EXIT(); + + return status; +} + +/** + * disa_psoc_object_destroyed_notification(): disa psoc destroy handler + * @psoc: objmgr object corresponding to psoc which is going to be destroyed + * @arg: argument for psoc destroy handler + * + * Detach and free psoc private object, unregister event handlers + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS disa_psoc_object_destroyed_notification( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct disa_psoc_priv_obj *disa_priv = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + DISA_ENTER(); + + disa_priv = disa_psoc_get_priv(psoc); + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_DISA, + (void *)disa_priv); + + if (status != QDF_STATUS_SUCCESS) + disa_err("Failed to detach disa_priv with psoc"); + + qdf_spinlock_destroy(&disa_priv->lock); + qdf_mem_free(disa_priv); + DISA_EXIT(); + + return status; +} + +/** + * disa_psoc_enable() - Trigger psoc enable for DISA + * @psoc: objmgr psoc object + * + * Return: QDF status success or failure + */ +QDF_STATUS disa_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + return tgt_disa_register_ev_handlers(psoc); +} + +/** + * disa_psoc_disable() - Trigger psoc disable for DISA + * @psoc: objmgr psoc object + * + * Return: QDF status success or failure + */ +QDF_STATUS disa_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + return tgt_disa_unregister_ev_handlers(psoc); + +} + diff --git a/drivers/staging/qcacld-3.0/components/disa/dispatcher/src/wlan_disa_tgt_api.c b/drivers/staging/qcacld-3.0/components/disa/dispatcher/src/wlan_disa_tgt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..384916c0c44b7da789cd756f1a22f80de006e448 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/dispatcher/src/wlan_disa_tgt_api.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Implements public API for disa to interact with target/WMI + */ + +#include "wlan_disa_tgt_api.h" +#include "wlan_disa_main.h" +#include "wlan_disa_public_struct.h" + +/** + * tgt_disa_encrypt_decrypt_req() - send encrypt/decrypt request to target if + * @psoc: objmgr psoc object + * @req: encrypt/decrypt parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_disa_encrypt_decrypt_req(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_req_params *req) +{ + struct wlan_disa_tx_ops *disa_tx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + DISA_ENTER(); + + disa_tx_ops = GET_DISA_TX_OPS_FROM_PSOC(psoc); + QDF_ASSERT(disa_tx_ops->disa_encrypt_decrypt_req); + + if (disa_tx_ops->disa_encrypt_decrypt_req) + status = disa_tx_ops->disa_encrypt_decrypt_req(psoc, req); + + DISA_EXIT(); + return status; +} + +/** + * tgt_disa_encrypt_decrypt_resp() - receive encrypt/decrypt response + * from target if + * @psoc: objmgr psoc object + * @resp: encrypt/decrypt response containing results + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_disa_encrypt_decrypt_resp(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_resp_params *resp) +{ + struct wlan_disa_ctx *disa_ctx; + encrypt_decrypt_resp_callback cb; + void *cookie; + + DISA_ENTER(); + + if (!resp) { + disa_err("encrypt/decrypt resp is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + disa_ctx = disa_get_context(); + if (!disa_ctx) { + disa_err("DISA context is NULL!"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock_bh(&disa_ctx->lock); + cb = disa_ctx->callback; + disa_ctx->callback = NULL; + cookie = disa_ctx->callback_context; + disa_ctx->callback_context = NULL; + disa_ctx->request_active = false; + qdf_spin_unlock_bh(&disa_ctx->lock); + + if (cb) + cb(cookie, resp); + + DISA_EXIT(); + return QDF_STATUS_SUCCESS; +} + +/** + * tgt_disa_register_ev_handlers() - API to register disa event handlers + * @psoc: objmgr psoc object + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS tgt_disa_register_ev_handlers(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_disa_tx_ops *disa_tx_ops; + + disa_tx_ops = GET_DISA_TX_OPS_FROM_PSOC(psoc); + + QDF_ASSERT(disa_tx_ops->disa_register_ev_handlers); + + if (disa_tx_ops->disa_register_ev_handlers) + return disa_tx_ops->disa_register_ev_handlers(psoc); + + return QDF_STATUS_SUCCESS; +} + +/** + * tgt_disa_unregister_ev_handlers() - API to unregister disa event handlers + * @psoc: objmgr psoc object + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS tgt_disa_unregister_ev_handlers(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_disa_tx_ops *disa_tx_ops; + + disa_tx_ops = GET_DISA_TX_OPS_FROM_PSOC(psoc); + + QDF_ASSERT(disa_tx_ops->disa_unregister_ev_handlers); + + if (disa_tx_ops->disa_unregister_ev_handlers) + return disa_tx_ops->disa_unregister_ev_handlers(psoc); + + return QDF_STATUS_SUCCESS; +} + diff --git a/drivers/staging/qcacld-3.0/components/disa/dispatcher/src/wlan_disa_ucfg_api.c b/drivers/staging/qcacld-3.0/components/disa/dispatcher/src/wlan_disa_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..e93c707ad8e303b6586dfd6a4fd5741740af7e31 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/disa/dispatcher/src/wlan_disa_ucfg_api.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: public API related to the disa called by north bound HDD/OSIF + */ + +#include "wlan_disa_ucfg_api.h" +#include "wlan_disa_main.h" + +/** + * ucfg_disa_encrypt_decrypt_req() - Send encrypt/decrypt request to the DISA + * core + * @psoc: objmgr psoc object + * @req: DISA encrypt/decrypt request parameters + * + * Return: QDF status success or failure + */ +QDF_STATUS ucfg_disa_encrypt_decrypt_req(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_req_params *req, + encrypt_decrypt_resp_callback cb, + void *cookie) +{ + return disa_core_encrypt_decrypt_req(psoc, req, cb, cookie); +} + + diff --git a/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc.h b/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc.h new file mode 100644 index 0000000000000000000000000000000000000000..6bacfc37a9294fe7a356480f684acfe51bdbd148 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Driver Synchronization Core (DSC) APIs for use by the driver + * orchestration layer. + * + * This infrastructure accomplishes two high level goals: + * 1) Replace ad-hoc locking/flags (hdd_init_deinit_lock, + * iface_change_lock, con_mode_flag, etc., etc., etc.) + * 2) Make cds_ssr_protect() and driver state checking atomic + * + * These two goals are commplished in DSC via two corollary concepts: + * 1) Transitions (as in driver state machine transitions) + * These are mutually exclusive, and replace ad-hoc locking + * 2) Operations (as in operations the driver is currently servicing) + * These execute concurrently with other operations, and replace + * cds_ssr_protect(). Any active transition causes new operations to be + * rejected, in the same way as cds_ssr_protect/hdd_validate_context would. + * + * Transitions and operations are split into 3 distinct levels: driver, psoc, + * and vdev. These levels are arranged into a tree, with a single driver at + * the root, zero or more psocs per driver, and zero or more vdevs per psoc. + * + * High level transitions block transitions and operations at the same level, + * down-tree, and up-tree. So a driver transition effectively prevents any new + * activity in the system, while a vdev transition prevents transtitions and + * operations on the same vdev, its parent psoc, and the driver. This also means + * that sibling nodes can transition at the same time, e.g. one vdev going up at + * the same time another is going down. + */ + +#ifndef __WLAN_DSC_H +#define __WLAN_DSC_H + +#include "wlan_dsc_driver.h" +#include "wlan_dsc_psoc.h" +#include "wlan_dsc_vdev.h" + +#endif /* __WLAN_DSC_H */ diff --git a/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc_driver.h b/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc_driver.h new file mode 100644 index 0000000000000000000000000000000000000000..29e16387fae512a83eaf5658ef0ed6e69be00d0a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc_driver.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Driver Synchronization Core (DSC) driver-level APIs + */ + +#ifndef __WLAN_DSC_DRIVER_H +#define __WLAN_DSC_DRIVER_H + +#include "qdf_status.h" + +/** + * struct dsc_driver - opaque dsc driver context + */ +struct dsc_driver; + +/** + * dsc_driver_create() - create a dsc driver context + * @out_driver: opaque double pointer to assign the new context to + * + * Return: QDF_STATUS + */ +QDF_STATUS dsc_driver_create(struct dsc_driver **out_driver); + +/** + * dsc_driver_destroy() - destroy a dsc driver context + * @out_driver: opaque double pointer to context to destroy and NULL + * + * Note, this: + * - aborts all queued transitions on @driver + * - asserts @driver has no attached psoc's + * - asserts @driver has no operations in flight + * + * Return: None + */ +void dsc_driver_destroy(struct dsc_driver **out_driver); + +/** + * dsc_driver_trans_start() - start a transition on @driver + * @driver: the driver to start a transition on + * @desc: a unique description of the transition to start + * + * This API immediately aborts if a transition on @driver is already in flight + * + * Call dsc_driver_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already in flight + */ +QDF_STATUS dsc_driver_trans_start(struct dsc_driver *driver, const char *desc); + +/** + * dsc_driver_trans_start_wait() - start a transition on @driver, blocking if a + * transition is already in flight + * @driver: the driver to start a transition on + * @desc: a unique description of the transition to start + * + * Call dsc_driver_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already queued or in flight + */ +QDF_STATUS +dsc_driver_trans_start_wait(struct dsc_driver *driver, const char *desc); + +/** + * dsc_driver_trans_stop() - complete current transition in flight on @driver + * @driver: the driver to complete the transition on + * + * Note: this asserts a transition is currently in flight on @driver + * + * Return: None + */ +void dsc_driver_trans_stop(struct dsc_driver *driver); + +/** + * dsc_driver_assert_trans_protected() - assert @driver is protected by a + * transition + * @driver: the driver to check + * + * Return: None + */ +void dsc_driver_assert_trans_protected(struct dsc_driver *driver); + +/** + * dsc_driver_op_start() - start an operation on @driver + * @driver: the driver to start an operation on + * + * Return: + * QDF_STATUS_SUCCESS - operation started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - operation cannot currently be started + * QDF_STATUS_E_NOMEM - out of memory + */ +#define dsc_driver_op_start(driver) _dsc_driver_op_start(driver, __func__) +QDF_STATUS _dsc_driver_op_start(struct dsc_driver *driver, const char *func); + +/** + * dsc_driver_op_stop() - complete operation with matching @func on @driver + * @driver: the driver to stop an operation on + * + * Note: this asserts @func was previously started + * + * Return: None + */ +#define dsc_driver_op_stop(driver) _dsc_driver_op_stop(driver, __func__) +void _dsc_driver_op_stop(struct dsc_driver *driver, const char *func); + +/** + * dsc_driver_wait_for_ops() - blocks until all operations on @driver have + * stopped + * @driver: the driver to wait for operations on + * + * Note: this asserts that @driver cannot currently transition + * + * Return: None + */ +void dsc_driver_wait_for_ops(struct dsc_driver *driver); + +#endif /* __WLAN_DSC_DRIVER_H */ diff --git a/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc_psoc.h b/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc_psoc.h new file mode 100644 index 0000000000000000000000000000000000000000..7354121d8942d47d2b37834ce236330634d617c4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc_psoc.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Driver Synchronization Core (DSC) psoc-level APIs + */ + +#ifndef __WLAN_DSC_PSOC_H +#define __WLAN_DSC_PSOC_H + +#include "qdf_status.h" +#include "wlan_dsc_driver.h" + +/** + * struct dsc_psoc - opaque dsc psoc context + */ +struct dsc_psoc; + +/** + * dsc_psoc_create() - create a dsc psoc context + * @driver: parent dsc driver context + * @out_psoc: opaque double pointer to assign the new context to + * + * Note: this attaches @out_psoc to @driver + * + * Return: QDF_STATUS + */ +QDF_STATUS +dsc_psoc_create(struct dsc_driver *driver, struct dsc_psoc **out_psoc); + +/** + * dsc_psoc_destroy() - destroy a dsc psoc context + * @out_psoc: opaque double pointer to context to destroy and NULL + * + * Note, this: + * - detaches @out_psoc from its parent driver context + * - aborts all queued transitions on @psoc + * - asserts @psoc has no attached vdev's + * - asserts @psoc has no operations in flight + * + * Return: None + */ +void dsc_psoc_destroy(struct dsc_psoc **out_psoc); + +/** + * dsc_psoc_trans_start() - start a transition on @psoc + * @psoc: the psoc to start a transition on + * @desc: a unique description of the transition to start + * + * This API immediately aborts if a transition on @psoc is already in flight + * + * Call dsc_psoc_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already in flight + */ +QDF_STATUS dsc_psoc_trans_start(struct dsc_psoc *psoc, const char *desc); + +/** + * dsc_psoc_trans_start_wait() - start a transition on @psoc, blocking if a + * transition is already in flight + * @psoc: the psoc to start a transition on + * @desc: a unique description of the transition to start + * + * Call dsc_psoc_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already queued or in flight + * QDF_STATUS_E_ABORTED - transition was aborted + */ +QDF_STATUS dsc_psoc_trans_start_wait(struct dsc_psoc *psoc, const char *desc); + +/** + * dsc_psoc_trans_stop() - complete current transition in flight on @psoc + * @psoc: the psoc to complete the transition on + * + * Note: this asserts a transition is currently in flight on @psoc + * + * Return: None + */ +void dsc_psoc_trans_stop(struct dsc_psoc *psoc); + +/** + * dsc_psoc_assert_trans_protected() - assert @psoc is protected by a transition + * @psoc: the psoc to check + * + * The protecting transition may be in flight on @psoc or its parent. + * + * Return: None + */ +void dsc_psoc_assert_trans_protected(struct dsc_psoc *psoc); + +/** + * dsc_psoc_op_start() - start an operation on @psoc + * @psoc: the psoc to start an operation on + * + * Return: + * QDF_STATUS_SUCCESS - operation started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - operation cannot currently be started + * QDF_STATUS_E_NOMEM - out of memory + */ +#define dsc_psoc_op_start(psoc) _dsc_psoc_op_start(psoc, __func__) +QDF_STATUS _dsc_psoc_op_start(struct dsc_psoc *psoc, const char *func); + +/** + * dsc_psoc_op_stop() - complete operation with matching @func on @psoc + * @psoc: the psoc to stop an operation on + * + * Note: this asserts @func was previously started + * + * Return: None + */ +#define dsc_psoc_op_stop(psoc) _dsc_psoc_op_stop(psoc, __func__) +void _dsc_psoc_op_stop(struct dsc_psoc *psoc, const char *func); + +/** + * dsc_psoc_wait_for_ops() - blocks until all operations on @psoc have stopped + * @psoc: the psoc to wait for operations on + * + * Note: this asserts that @psoc cannot currently transition + * + * Return: None + */ +void dsc_psoc_wait_for_ops(struct dsc_psoc *psoc); + +#endif /* __WLAN_DSC_PSOC_H */ diff --git a/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc_vdev.h b/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc_vdev.h new file mode 100644 index 0000000000000000000000000000000000000000..846c3c45ce5a2e7b9c1f0f754d9aa2547c238a84 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/inc/wlan_dsc_vdev.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Driver Synchronization Core (DSC) vdev-level APIs + */ + +#ifndef __WLAN_DSC_VDEV_H +#define __WLAN_DSC_VDEV_H + +#include "qdf_status.h" +#include "wlan_dsc_psoc.h" + +/** + * struct dsc_vdev - opaque dsc vdev context + */ +struct dsc_vdev; + +/** + * dsc_vdev_create() - create a dsc vdev context + * @psoc: parent dsc psoc context + * @out_vdev: opaque double pointer to assign the new context to + * + * Note: this attaches @out_vdev to @psoc + * + * Return: QDF_STATUS + */ +QDF_STATUS dsc_vdev_create(struct dsc_psoc *psoc, struct dsc_vdev **out_vdev); + +/** + * dsc_vdev_destroy() - destroy a dsc vdev context + * @out_vdev: opaque double pointer to context to destroy and NULL + * + * Note, this: + * - detaches @out_vdev from its parent psoc context + * - aborts all queued transitions on @vdev + * - asserts @vdev has no operations in flight + * + * Return: None + */ +void dsc_vdev_destroy(struct dsc_vdev **out_vdev); + +/** + * dsc_vdev_trans_start() - start a transition on @vdev + * @vdev: the vdev to start a transition on + * @desc: a unique description of the transition to start + * + * This API immediately aborts if a transition on @vdev is already in flight + * + * Call dsc_vdev_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already in flight + */ +QDF_STATUS dsc_vdev_trans_start(struct dsc_vdev *vdev, const char *desc); + +/** + * dsc_vdev_trans_start_wait() - start a transition on @vdev, blocking if a + * transition is already in flight + * @vdev: the vdev to start a transition on + * @desc: a unique description of the transition to start + * + * Call dsc_vdev_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already queued or in flight + * QDF_STATUS_E_ABORTED - transition was aborted + */ +QDF_STATUS dsc_vdev_trans_start_wait(struct dsc_vdev *vdev, const char *desc); + +/** + * dsc_vdev_trans_stop() - complete current transition in flight on @vdev + * @vdev: the vdev to complete the transition on + * + * Note: this asserts a transition is currently in flight on @vdev + * + * Return: None + */ +void dsc_vdev_trans_stop(struct dsc_vdev *vdev); + +/** + * dsc_vdev_assert_trans_protected() - assert @vdev is protected by a transition + * @vdev: the vdev to check + * + * The protecting transition may be in flight on @vdev or its ancestors. + * + * Return: None + */ +void dsc_vdev_assert_trans_protected(struct dsc_vdev *vdev); + +/** + * dsc_vdev_op_start() - start an operation on @vdev + * @vdev: the vdev to start an operation on + * + * Return: + * QDF_STATUS_SUCCESS - operation started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - operation cannot currently be started + * QDF_STATUS_E_NOMEM - out of memory + */ +#define dsc_vdev_op_start(vdev) _dsc_vdev_op_start(vdev, __func__) +QDF_STATUS _dsc_vdev_op_start(struct dsc_vdev *vdev, const char *func); + +/** + * dsc_vdev_op_stop() - complete operation with matching @func on @vdev + * @vdev: the vdev to stop an operation on + * + * Note: this asserts @func was previously started + * + * Return: None + */ +#define dsc_vdev_op_stop(vdev) _dsc_vdev_op_stop(vdev, __func__) +void _dsc_vdev_op_stop(struct dsc_vdev *vdev, const char *func); + +/** + * dsc_vdev_wait_for_ops() - blocks until all operations on @vdev have stopped + * @vdev: the vdev to wait for operations on + * + * Note: this asserts that @vdev cannot currently transition + * + * Return: None + */ +void dsc_vdev_wait_for_ops(struct dsc_vdev *vdev); + +#endif /* __WLAN_DSC_VDEV_H */ diff --git a/drivers/staging/qcacld-3.0/components/dsc/src/__wlan_dsc.c b/drivers/staging/qcacld-3.0/components/dsc/src/__wlan_dsc.c new file mode 100644 index 0000000000000000000000000000000000000000..0bb404ca934275de1dc159e8f67e35b1cf6fa365 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/src/__wlan_dsc.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "qdf_list.h" +#include "qdf_mem.h" +#include "qdf_status.h" +#include "qdf_str.h" +#include "qdf_threads.h" +#include "qdf_timer.h" +#include "__wlan_dsc.h" +#include "cds_api.h" + +#ifdef WLAN_DSC_DEBUG +static void __dsc_dbg_op_timeout(void *opaque_op) +{ + struct dsc_op *op = opaque_op; + + qdf_print_thread_trace(op->thread); + QDF_DEBUG_PANIC("Operation '%s' exceeded %ums", + op->func, DSC_OP_TIMEOUT_MS); +} + +/** + * __dsc_dbg_ops_init() - initialize debug ops data structures + * @ops: the ops container to initialize + * + * Return: None + */ +static inline void __dsc_dbg_ops_init(struct dsc_ops *ops) +{ + qdf_list_create(&ops->list, 0); +} + +/** + * __dsc_dbg_ops_deinit() - de-initialize debug ops data structures + * @ops: the ops container to de-initialize + * + * Return: None + */ +static inline void __dsc_dbg_ops_deinit(struct dsc_ops *ops) +{ + qdf_list_destroy(&ops->list); +} + +/** + * __dsc_dbg_ops_insert() - insert @func into the debug information in @ops + * @ops: the ops container to insert into + * @func: the debug information to insert + * + * Return: QDF_STATUS + */ +static QDF_STATUS __dsc_dbg_ops_insert(struct dsc_ops *ops, const char *func) +{ + QDF_STATUS status; + struct dsc_op *op; + + op = qdf_mem_malloc(sizeof(*op)); + if (!op) + return QDF_STATUS_E_NOMEM; + + op->thread = qdf_get_current_task(); + status = qdf_timer_init(NULL, &op->timeout_timer, __dsc_dbg_op_timeout, + op, QDF_TIMER_TYPE_SW); + if (QDF_IS_STATUS_ERROR(status)) + goto free_op; + + op->func = func; + + qdf_timer_start(&op->timeout_timer, DSC_OP_TIMEOUT_MS); + qdf_list_insert_back(&ops->list, &op->node); + + return QDF_STATUS_SUCCESS; + +free_op: + qdf_mem_free(op); + + return status; +} + +/** + * __dsc_dbg_ops_remove() - remove @func from the debug information in @ops + * @ops: the ops container to remove from + * @func: the debug information to remove + * + * Return: None + */ +static void __dsc_dbg_ops_remove(struct dsc_ops *ops, const char *func) +{ + struct dsc_op *op; + + /* Global pending op depth is usually <=3. Use linear search for now */ + qdf_list_for_each(&ops->list, op, node) { + if (!qdf_str_eq(op->func, func)) + continue; + + /* this is safe because we cease iteration */ + qdf_list_remove_node(&ops->list, &op->node); + + qdf_timer_stop(&op->timeout_timer); + qdf_timer_free(&op->timeout_timer); + qdf_mem_free(op); + + return; + } + + QDF_DEBUG_PANIC("Driver op '%s' is not pending", func); +} +#else +static inline void __dsc_dbg_ops_init(struct dsc_ops *ops) { } + +static inline void __dsc_dbg_ops_deinit(struct dsc_ops *ops) { } + +static inline QDF_STATUS +__dsc_dbg_ops_insert(struct dsc_ops *ops, const char *func) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void +__dsc_dbg_ops_remove(struct dsc_ops *ops, const char *func) { } +#endif /* WLAN_DSC_DEBUG */ + +void __dsc_ops_init(struct dsc_ops *ops) +{ + ops->count = 0; + qdf_event_create(&ops->event); + __dsc_dbg_ops_init(ops); +} + +void __dsc_ops_deinit(struct dsc_ops *ops) +{ + /* assert no ops in flight */ + dsc_assert(!ops->count); + + __dsc_dbg_ops_deinit(ops); + qdf_event_destroy(&ops->event); +} + +QDF_STATUS __dsc_ops_insert(struct dsc_ops *ops, const char *func) +{ + QDF_STATUS status; + + status = __dsc_dbg_ops_insert(ops, func); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + ops->count++; + + return QDF_STATUS_SUCCESS; +} + +bool __dsc_ops_remove(struct dsc_ops *ops, const char *func) +{ + dsc_assert(ops->count); + ops->count--; + + __dsc_dbg_ops_remove(ops, func); + + return ops->count == 0; +} + +#ifdef WLAN_DSC_DEBUG +static void __dsc_dbg_trans_timeout(void *opaque_trans) +{ + struct dsc_trans *trans = opaque_trans; + + qdf_print_thread_trace(trans->thread); + + if (cds_is_fw_down()) + dsc_err("fw is down avoid panic"); + else + QDF_DEBUG_PANIC("Transition '%s' exceeded %ums", + trans->active_desc, DSC_TRANS_TIMEOUT_MS); +} + +/** + * __dsc_dbg_trans_timeout_start() - start a timeout timer for @trans + * @trans: the active transition to start a timeout timer for + * + * Return: QDF_STATUS + */ +static QDF_STATUS __dsc_dbg_trans_timeout_start(struct dsc_trans *trans) +{ + QDF_STATUS status; + + trans->thread = qdf_get_current_task(); + status = qdf_timer_init(NULL, &trans->timeout_timer, + __dsc_dbg_trans_timeout, trans, + QDF_TIMER_TYPE_SW); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + qdf_timer_start(&trans->timeout_timer, DSC_TRANS_TIMEOUT_MS); + + return QDF_STATUS_SUCCESS; +} + +/** + * __dsc_dbg_trans_timeout_stop() - stop the timeout timer for @trans + * @trans: the active transition to stop the timeout timer for + * + * Return: None + */ +static void __dsc_dbg_trans_timeout_stop(struct dsc_trans *trans) +{ + qdf_timer_stop(&trans->timeout_timer); + qdf_timer_free(&trans->timeout_timer); +} + +static void __dsc_dbg_tran_wait_timeout(void *opaque_tran) +{ + struct dsc_tran *tran = opaque_tran; + + qdf_print_thread_trace(tran->thread); + QDF_DEBUG_PANIC("Transition '%s' waited more than %ums", + tran->desc, DSC_TRANS_WAIT_TIMEOUT_MS); +} + +/** + * __dsc_dbg_tran_wait_timeout_start() - start a timeout timer for @tran + * @tran: the pending transition to start a timeout timer for + * + * Return: QDF_STATUS + */ +static QDF_STATUS __dsc_dbg_tran_wait_timeout_start(struct dsc_tran *tran) +{ + QDF_STATUS status; + + tran->thread = qdf_get_current_task(); + status = qdf_timer_init(NULL, &tran->timeout_timer, + __dsc_dbg_tran_wait_timeout, tran, + QDF_TIMER_TYPE_SW); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + qdf_timer_start(&tran->timeout_timer, DSC_TRANS_WAIT_TIMEOUT_MS); + + return QDF_STATUS_SUCCESS; +} + +/** + * __dsc_dbg_tran_wait_timeout_stop() - stop the timeout timer for @tran + * @tran: the pending transition to stop the timeout timer for + * + * Return: None + */ +static void __dsc_dbg_tran_wait_timeout_stop(struct dsc_tran *tran) +{ + qdf_timer_stop(&tran->timeout_timer); + qdf_timer_free(&tran->timeout_timer); +} +#else +static inline QDF_STATUS __dsc_dbg_trans_timeout_start(struct dsc_trans *trans) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void __dsc_dbg_trans_timeout_stop(struct dsc_trans *trans) { } + +static inline QDF_STATUS +__dsc_dbg_tran_wait_timeout_start(struct dsc_tran *tran) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void __dsc_dbg_tran_wait_timeout_stop(struct dsc_tran *tran) { } +#endif /* WLAN_DSC_DEBUG */ + +void __dsc_trans_init(struct dsc_trans *trans) +{ + trans->active_desc = NULL; + qdf_list_create(&trans->queue, 0); +} + +void __dsc_trans_deinit(struct dsc_trans *trans) +{ + qdf_list_destroy(&trans->queue); + trans->active_desc = NULL; +} + +QDF_STATUS __dsc_trans_start(struct dsc_trans *trans, const char *desc) +{ + QDF_STATUS status; + + status = __dsc_dbg_trans_timeout_start(trans); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + dsc_assert(!trans->active_desc); + trans->active_desc = desc; + + return QDF_STATUS_SUCCESS; +} + +void __dsc_trans_stop(struct dsc_trans *trans) +{ + dsc_assert(trans->active_desc); + trans->active_desc = NULL; + __dsc_dbg_trans_timeout_stop(trans); +} + +QDF_STATUS __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran, + const char *desc) +{ + QDF_STATUS status; + + tran->abort = false; + tran->desc = desc; + qdf_event_create(&tran->event); + + status = __dsc_dbg_tran_wait_timeout_start(tran); + if (QDF_IS_STATUS_ERROR(status)) + goto event_destroy; + + qdf_list_insert_back(&trans->queue, &tran->node); + + return QDF_STATUS_SUCCESS; + +event_destroy: + qdf_event_destroy(&tran->event); + + return status; +} + +/** + * __dsc_trans_dequeue() - dequeue the next queued transition from @trans + * @trans: the transactions container to dequeue from + * + * Return: the dequeued transition, or NULL if @trans is empty + */ +static struct dsc_tran *__dsc_trans_dequeue(struct dsc_trans *trans) +{ + QDF_STATUS status; + qdf_list_node_t *node; + struct dsc_tran *tran; + + status = qdf_list_remove_front(&trans->queue, &node); + if (QDF_IS_STATUS_ERROR(status)) + return NULL; + + tran = qdf_container_of(node, struct dsc_tran, node); + __dsc_dbg_tran_wait_timeout_stop(tran); + + return tran; +} + +bool __dsc_trans_abort(struct dsc_trans *trans) +{ + struct dsc_tran *tran; + + tran = __dsc_trans_dequeue(trans); + if (!tran) + return false; + + tran->abort = true; + qdf_event_set(&tran->event); + + return true; +} + +bool __dsc_trans_trigger(struct dsc_trans *trans) +{ + struct dsc_tran *tran; + + tran = __dsc_trans_dequeue(trans); + if (!tran) + return false; + + __dsc_trans_start(trans, tran->desc); + qdf_event_set(&tran->event); + + return true; +} + +bool __dsc_trans_active(struct dsc_trans *trans) +{ + return !!trans->active_desc; +} + +bool __dsc_trans_queued(struct dsc_trans *trans) +{ + return !qdf_list_empty(&trans->queue); +} + +bool __dsc_trans_active_or_queued(struct dsc_trans *trans) +{ + return __dsc_trans_active(trans) || __dsc_trans_queued(trans); +} + +QDF_STATUS __dsc_tran_wait(struct dsc_tran *tran) +{ + QDF_STATUS status; + + status = qdf_wait_single_event(&tran->event, 0); + qdf_event_destroy(&tran->event); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + if (tran->abort) + return QDF_STATUS_E_ABORTED; + + return QDF_STATUS_SUCCESS; +} + diff --git a/drivers/staging/qcacld-3.0/components/dsc/src/__wlan_dsc.h b/drivers/staging/qcacld-3.0/components/dsc/src/__wlan_dsc.h new file mode 100644 index 0000000000000000000000000000000000000000..bd9de768547b7cf3a5a20da408e37e510d9660ad --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/src/__wlan_dsc.h @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Driver State Management (DSC) APIs for *internal* use + */ + +#ifndef ____WLAN_DSC_H +#define ____WLAN_DSC_H + +#include "qdf_event.h" +#include "qdf_list.h" +#include "qdf_threads.h" +#include "qdf_timer.h" +#include "qdf_trace.h" +#include "qdf_types.h" +#include "wlan_dsc.h" + +#define dsc_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_QDF, params) +#define dsc_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_QDF, params) +#ifdef WLAN_DSC_DEBUG +#define dsc_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_QDF, params) +#else +#define dsc_debug(params...) /* no-op */ +#endif + +#define dsc_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_QDF, params) +#define dsc_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_QDF, params) +#define dsc_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_QDF, params) + +#define dsc_enter_exit dsc_debug +#define dsc_enter() dsc_enter_exit("enter") +#define dsc_enter_str(str) dsc_enter_exit("enter(\"%s\")", str) +#define dsc_exit() dsc_enter_exit("exit") +#define dsc_exit_status(status) dsc_enter_exit("exit(status:%u)", status) + +static inline bool __dsc_assert(const bool cond, const char *cond_str, + const char *func, const uint32_t line) +{ + if (cond) + return true; + + QDF_DEBUG_PANIC_FL(func, line, "Failed assertion '%s'!", cond_str); + + return false; +} + +#define dsc_assert(cond) __dsc_assert(cond, #cond, __func__, __LINE__) +#define dsc_assert_success(status) dsc_assert(QDF_IS_STATUS_SUCCESS(status)) + +#ifdef WLAN_DSC_DEBUG +#define DSC_OP_TIMEOUT_MS (1 * 60 * 1000) /* 1 minute */ +#define DSC_TRANS_TIMEOUT_MS (1 * 60 * 1000) /* 1 minute */ +#define DSC_TRANS_WAIT_TIMEOUT_MS (2 * 60 * 1000) /* 2 minutes */ + +/** + * struct dsc_op - list node for operation tracking information + * @node: list node + * @timeout_timer: a timer used to detect operation timeouts + * @thread: the thread which started the operation + * @func: name of the function the operation was started from + */ +struct dsc_op { + qdf_list_node_t node; + qdf_timer_t timeout_timer; + qdf_thread_t *thread; + const char *func; +}; +#endif /* WLAN_DSC_DEBUG */ + +/** + * struct dsc_ops - operations in flight tracking container + * @list: list for tracking debug information + * @count: count of current operations in flight + * @event: event used to wait in *_wait_for_ops() APIs + */ +struct dsc_ops { +#ifdef WLAN_DSC_DEBUG + qdf_list_t list; +#endif + uint32_t count; + qdf_event_t event; +}; + +/** + * struct dsc_tran - representation of a pending transition + * @abort: used to indicate if the transition stopped waiting due to an abort + * @desc: unique description of the transition + * @node: list node + * @event: event used to wait in *_start_trans_wait() APIs + * @timeout_timer: a timer used to detect transition wait timeouts + * @thread: the thread which started the transition wait + */ +struct dsc_tran { + bool abort; + const char *desc; + qdf_list_node_t node; + qdf_event_t event; +#ifdef WLAN_DSC_DEBUG + qdf_timer_t timeout_timer; + qdf_thread_t *thread; +#endif +}; + +/** + * struct dsc_trans - transition information container + * @active_desc: unique description of the current transition in progress + * @queue: queue of pending transitions + * @timeout_timer: a timer used to detect transition timeouts + * @thread: the thread which started the transition + */ +struct dsc_trans { + const char *active_desc; + qdf_list_t queue; +#ifdef WLAN_DSC_DEBUG + qdf_timer_t timeout_timer; + qdf_thread_t *thread; +#endif +}; + +/** + * struct dsc_driver - concrete dsc driver context + * @lock: lock under which all dsc APIs execute + * @psocs: list of children psoc contexts + * @trans: transition tracking container for this node + * @ops: operations in flight tracking container for this node + */ +struct dsc_driver { + struct qdf_spinlock lock; + qdf_list_t psocs; + struct dsc_trans trans; + struct dsc_ops ops; +}; + +/** + * struct dsc_psoc - concrete dsc psoc context + * @node: list node for membership in @driver->psocs + * @driver: parent driver context + * @vdevs: list of children vdevs contexts + * @trans: transition tracking container for this node + * @ops: operations in flight tracking container for this node + */ +struct dsc_psoc { + qdf_list_node_t node; + struct dsc_driver *driver; + qdf_list_t vdevs; + struct dsc_trans trans; + struct dsc_ops ops; +}; + +/** + * struct dsc_vdev - concrete dsc vdev context + * @node: list node for membership in @psoc->vdevs + * @psoc: parent psoc context + * @trans: transition tracking container for this node + * @ops: operations in flight tracking container for this node + */ +struct dsc_vdev { + qdf_list_node_t node; + struct dsc_psoc *psoc; + struct dsc_trans trans; + struct dsc_ops ops; +}; + +#define dsc_for_each_driver_psoc(driver_ptr, psoc_cursor) \ + qdf_list_for_each(&(driver_ptr)->psocs, psoc_cursor, node) + +#define dsc_for_each_psoc_vdev(psoc_ptr, vdev_cursor) \ + qdf_list_for_each(&(psoc_ptr)->vdevs, vdev_cursor, node) + +/** + * __dsc_lock() - grab the dsc driver lock + * @driver: the driver to lock + * + * Return: None + */ +void __dsc_lock(struct dsc_driver *driver); + +/** + * __dsc_unlock() - release the dsc driver lock + * @driver: the driver to unlock + * + * Return: None + */ +void __dsc_unlock(struct dsc_driver *driver); + +/** + * __dsc_ops_init() - initialize @ops + * @ops: the ops container to initialize + * + * Return: None + */ +void __dsc_ops_init(struct dsc_ops *ops); + +/** + * __dsc_ops_deinit() - de-initialize @ops + * @ops: the ops container to de-initialize + * + * Return: None + */ +void __dsc_ops_deinit(struct dsc_ops *ops); + +/** + * __dsc_ops_insert() - insert @func into the trakcing information in @ops + * @ops: the ops container to insert into + * @func: the debug information to insert + * + * Return: QDF_STATUS + */ +QDF_STATUS __dsc_ops_insert(struct dsc_ops *ops, const char *func); + +/** + * __dsc_ops_remove() - remove @func from the tracking information in @ops + * @ops: the ops container to remove from + * @func: the debug information to remove + * + * Return: None + */ +bool __dsc_ops_remove(struct dsc_ops *ops, const char *func); + +/** + * __dsc_trans_init() - initialize @trans + * @trans: the trans container to initialize + * + * Return: None + */ +void __dsc_trans_init(struct dsc_trans *trans); + +/** + * __dsc_trans_deinit() - de-initialize @trans + * @trans: the trans container to de-initialize + * + * Return: None + */ +void __dsc_trans_deinit(struct dsc_trans *trans); + +/** + * __dsc_trans_start() - set the active transition on @trans + * @trans: the transition container used to track the new active transition + * @desc: unique description of the transition being started + * + * Return: QDF_STATUS + */ +QDF_STATUS __dsc_trans_start(struct dsc_trans *trans, const char *desc); + +/** + * __dsc_trans_stop() - unset the active transition on @trans + * @trans: the transition container currently tracking the active transition + * + * Return: None + */ +void __dsc_trans_stop(struct dsc_trans *trans); + +/** + * __dsc_trans_queue() - queue @tran at the back of @trans + * @trans: the transitions container to enqueue to + * @tran: the transition to enqueue + * @desc: unique description of the transition being queued + * + * Return: QDF_STATUS + */ +QDF_STATUS __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran, + const char *desc); + +/** + * __dsc_tran_wait() - block until @tran completes + * @tran: the transition to wait on + * + * Return: QDF_STATUS + */ +QDF_STATUS __dsc_tran_wait(struct dsc_tran *tran); + +/** + * __dsc_trans_abort() - abort the next queued transition from @trans + * @trans: the transitions container to abort from + * + * Return: true if a transition was aborted, false if @trans is empty + */ +bool __dsc_trans_abort(struct dsc_trans *trans); + +/** + * __dsc_trans_trigger() - trigger the next queued trans in @trans + * @trans: the transitions container to trigger from + * + * Return: true if a transition was triggered + */ +bool __dsc_trans_trigger(struct dsc_trans *trans); + +/** + * __dsc_trans_active() - check if a transition is active in @trans + * @trans: the transitions container to check + * + * Return: true if @trans has an active transition + */ +bool __dsc_trans_active(struct dsc_trans *trans); + +/** + * __dsc_trans_queued() - check if a transition is queued in @trans + * @trans: the transitions container to check + * + * Return: true if @trans has a queued transition + */ +bool __dsc_trans_queued(struct dsc_trans *trans); + +/** + * __dsc_trans_active_or_queued() - check if a transition is active or queued + * in @trans + * @trans: the transitions container to check + * + * Return: true if @trans has an active or queued transition + */ +bool __dsc_trans_active_or_queued(struct dsc_trans *trans); + +/** + * __dsc_driver_trans_trigger_checked() - trigger any next pending driver + * transition, only after passing the "can trans" check + * + * Return: true if the trigger was "handled." This indicates down-tree nodes + * should _not_ attempt to trigger a new transition. + */ +bool __dsc_driver_trans_trigger_checked(struct dsc_driver *driver); + +/** + * __dsc_psoc_trans_trigger_checked() - trigger any next pending psoc + * transition, only after passing the "can trans" check + * + * Return: true if the trigger was "handled." This indicates down-tree nodes + * should _not_ attempt to trigger a new transition. + */ +bool __dsc_psoc_trans_trigger_checked(struct dsc_psoc *psoc); + +#endif /* ____WLAN_DSC_H */ diff --git a/drivers/staging/qcacld-3.0/components/dsc/src/wlan_dsc_driver.c b/drivers/staging/qcacld-3.0/components/dsc/src/wlan_dsc_driver.c new file mode 100644 index 0000000000000000000000000000000000000000..fd17662c712264fdcdbff62c4f12b474406f9e6a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/src/wlan_dsc_driver.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "qdf_list.h" +#include "qdf_mem.h" +#include "qdf_status.h" +#include "qdf_types.h" +#include "__wlan_dsc.h" +#include "wlan_dsc.h" + +void __dsc_lock(struct dsc_driver *driver) +{ + dsc_assert(driver); + qdf_spin_lock_bh(&driver->lock); +} + +void __dsc_unlock(struct dsc_driver *driver) +{ + dsc_assert(driver); + qdf_spin_unlock_bh(&driver->lock); +} + +static QDF_STATUS __dsc_driver_create(struct dsc_driver **out_driver) +{ + struct dsc_driver *driver; + + if (!dsc_assert(out_driver)) + return QDF_STATUS_E_INVAL; + + *out_driver = NULL; + + driver = qdf_mem_malloc(sizeof(*driver)); + if (!driver) + return QDF_STATUS_E_NOMEM; + + qdf_spinlock_create(&driver->lock); + qdf_list_create(&driver->psocs, 0); + __dsc_trans_init(&driver->trans); + __dsc_ops_init(&driver->ops); + + *out_driver = driver; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dsc_driver_create(struct dsc_driver **out_driver) +{ + QDF_STATUS status; + + status = __dsc_driver_create(out_driver); + + return status; +} + +static void __dsc_driver_destroy(struct dsc_driver **out_driver) +{ + struct dsc_driver *driver; + + if (!dsc_assert(out_driver)) + return; + + driver = *out_driver; + if (!dsc_assert(driver)) + return; + + *out_driver = NULL; + + /* assert no children */ + dsc_assert(qdf_list_empty(&driver->psocs)); + + /* flush pending transitions */ + while (__dsc_trans_abort(&driver->trans)) + ; + + /* de-init */ + __dsc_ops_deinit(&driver->ops); + __dsc_trans_deinit(&driver->trans); + qdf_list_destroy(&driver->psocs); + qdf_spinlock_destroy(&driver->lock); + + qdf_mem_free(driver); +} + +void dsc_driver_destroy(struct dsc_driver **out_driver) +{ + __dsc_driver_destroy(out_driver); +} + +static bool __dsc_driver_trans_active_down_tree(struct dsc_driver *driver) +{ + struct dsc_psoc *psoc; + struct dsc_vdev *vdev; + + dsc_for_each_driver_psoc(driver, psoc) { + if (__dsc_trans_active(&psoc->trans)) + return true; + + dsc_for_each_psoc_vdev(psoc, vdev) { + if (__dsc_trans_active(&vdev->trans)) + return true; + } + } + + return false; +} + +#define __dsc_driver_can_op(driver) __dsc_driver_can_trans(driver) + +static bool __dsc_driver_can_trans(struct dsc_driver *driver) +{ + return !__dsc_trans_active_or_queued(&driver->trans) && + !__dsc_driver_trans_active_down_tree(driver); +} + +static bool __dsc_driver_can_trigger(struct dsc_driver *driver) +{ + return !__dsc_trans_active(&driver->trans) && + !__dsc_driver_trans_active_down_tree(driver); +} + +static QDF_STATUS +__dsc_driver_trans_start_nolock(struct dsc_driver *driver, const char *desc) +{ + if (!__dsc_driver_can_trans(driver)) + return QDF_STATUS_E_AGAIN; + + return __dsc_trans_start(&driver->trans, desc); +} + +static QDF_STATUS +__dsc_driver_trans_start(struct dsc_driver *driver, const char *desc) +{ + QDF_STATUS status; + + if (!dsc_assert(driver)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(desc)) + return QDF_STATUS_E_INVAL; + + __dsc_lock(driver); + status = __dsc_driver_trans_start_nolock(driver, desc); + __dsc_unlock(driver); + + return status; +} + +QDF_STATUS dsc_driver_trans_start(struct dsc_driver *driver, const char *desc) +{ + QDF_STATUS status; + + dsc_enter_str(desc); + status = __dsc_driver_trans_start(driver, desc); + if (QDF_IS_STATUS_ERROR(status)) + dsc_exit_status(status); + + return status; +} + +static QDF_STATUS +__dsc_driver_trans_start_wait(struct dsc_driver *driver, const char *desc) +{ + QDF_STATUS status; + struct dsc_tran tran = { 0 }; + + if (!dsc_assert(driver)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(desc)) + return QDF_STATUS_E_INVAL; + + __dsc_lock(driver); + + /* try to start without waiting */ + status = __dsc_driver_trans_start_nolock(driver, desc); + if (QDF_IS_STATUS_SUCCESS(status)) + goto unlock; + + status = __dsc_trans_queue(&driver->trans, &tran, desc); + if (QDF_IS_STATUS_ERROR(status)) + goto unlock; + + __dsc_unlock(driver); + + return __dsc_tran_wait(&tran); + +unlock: + __dsc_unlock(driver); + + return status; +} + +QDF_STATUS +dsc_driver_trans_start_wait(struct dsc_driver *driver, const char *desc) +{ + QDF_STATUS status; + + dsc_enter_str(desc); + status = __dsc_driver_trans_start_wait(driver, desc); + if (QDF_IS_STATUS_ERROR(status)) + dsc_exit_status(status); + + return status; +} + +bool __dsc_driver_trans_trigger_checked(struct dsc_driver *driver) +{ + if (!__dsc_trans_queued(&driver->trans)) + return false; + + /* handled, but don't trigger; we need to wait for more children */ + if (!__dsc_driver_can_trigger(driver)) + return true; + + return __dsc_trans_trigger(&driver->trans); +} + +static void __dsc_driver_trigger_trans(struct dsc_driver *driver) +{ + struct dsc_psoc *psoc; + struct dsc_vdev *vdev; + + if (__dsc_trans_trigger(&driver->trans)) + return; + + dsc_for_each_driver_psoc(driver, psoc) { + if (__dsc_trans_trigger(&psoc->trans)) + continue; + + dsc_for_each_psoc_vdev(psoc, vdev) + __dsc_trans_trigger(&vdev->trans); + } +} + +static void __dsc_driver_trans_stop(struct dsc_driver *driver) +{ + if (!dsc_assert(driver)) + return; + + __dsc_lock(driver); + + __dsc_trans_stop(&driver->trans); + __dsc_driver_trigger_trans(driver); + + __dsc_unlock(driver); +} + +void dsc_driver_trans_stop(struct dsc_driver *driver) +{ + __dsc_driver_trans_stop(driver); +} + +static void __dsc_driver_assert_trans_protected(struct dsc_driver *driver) +{ + if (!dsc_assert(driver)) + return; + + __dsc_lock(driver); + dsc_assert(__dsc_trans_active(&driver->trans)); + __dsc_unlock(driver); +} + +void dsc_driver_assert_trans_protected(struct dsc_driver *driver) +{ + __dsc_driver_assert_trans_protected(driver); +} + +static QDF_STATUS +__dsc_driver_op_start(struct dsc_driver *driver, const char *func) +{ + QDF_STATUS status; + + if (!dsc_assert(driver)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(func)) + return QDF_STATUS_E_INVAL; + + __dsc_lock(driver); + + if (!__dsc_driver_can_op(driver)) { + status = QDF_STATUS_E_AGAIN; + goto unlock; + } + + status = __dsc_ops_insert(&driver->ops, func); + +unlock: + __dsc_unlock(driver); + + return status; +} + +QDF_STATUS _dsc_driver_op_start(struct dsc_driver *driver, const char *func) +{ + QDF_STATUS status; + + dsc_enter_str(func); + status = __dsc_driver_op_start(driver, func); + if (QDF_IS_STATUS_ERROR(status)) + dsc_exit_status(status); + + return status; +} + +static void __dsc_driver_op_stop(struct dsc_driver *driver, const char *func) +{ + if (!dsc_assert(driver)) + return; + + if (!dsc_assert(func)) + return; + + __dsc_lock(driver); + if (__dsc_ops_remove(&driver->ops, func)) + qdf_event_set(&driver->ops.event); + __dsc_unlock(driver); +} + +void _dsc_driver_op_stop(struct dsc_driver *driver, const char *func) +{ + __dsc_driver_op_stop(driver, func); +} + +static void __dsc_driver_wait_for_ops(struct dsc_driver *driver) +{ + struct dsc_psoc *psoc; + bool wait; + + if (!dsc_assert(driver)) + return; + + __dsc_lock(driver); + + /* flushing without preventing new ops is almost certainly a bug */ + dsc_assert(!__dsc_driver_can_op(driver)); + + wait = driver->ops.count > 0; + if (wait) + qdf_event_reset(&driver->ops.event); + + __dsc_unlock(driver); + + if (wait) + qdf_wait_single_event(&driver->ops.event, 0); + + /* wait for down-tree ops to complete as well */ + dsc_for_each_driver_psoc(driver, psoc) + dsc_psoc_wait_for_ops(psoc); +} + +void dsc_driver_wait_for_ops(struct dsc_driver *driver) +{ + __dsc_driver_wait_for_ops(driver); +} + diff --git a/drivers/staging/qcacld-3.0/components/dsc/src/wlan_dsc_psoc.c b/drivers/staging/qcacld-3.0/components/dsc/src/wlan_dsc_psoc.c new file mode 100644 index 0000000000000000000000000000000000000000..c49af6c0ca0f12638e185acaf69c30493ea58d82 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/src/wlan_dsc_psoc.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "qdf_list.h" +#include "qdf_status.h" +#include "qdf_talloc.h" +#include "qdf_types.h" +#include "__wlan_dsc.h" +#include "wlan_dsc.h" + +#define __dsc_driver_lock(psoc) __dsc_lock((psoc)->driver) +#define __dsc_driver_unlock(psoc) __dsc_unlock((psoc)->driver) + +static QDF_STATUS +__dsc_psoc_create(struct dsc_driver *driver, struct dsc_psoc **out_psoc) +{ + struct dsc_psoc *psoc; + + if (!dsc_assert(driver)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(out_psoc)) + return QDF_STATUS_E_INVAL; + + *out_psoc = NULL; + + psoc = qdf_talloc_type(driver, psoc); + if (!psoc) + return QDF_STATUS_E_NOMEM; + + /* init */ + psoc->driver = driver; + qdf_list_create(&psoc->vdevs, 0); + __dsc_trans_init(&psoc->trans); + __dsc_ops_init(&psoc->ops); + + /* attach */ + __dsc_driver_lock(psoc); + qdf_list_insert_back(&driver->psocs, &psoc->node); + __dsc_driver_unlock(psoc); + + *out_psoc = psoc; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +dsc_psoc_create(struct dsc_driver *driver, struct dsc_psoc **out_psoc) +{ + QDF_STATUS status; + + status = __dsc_psoc_create(driver, out_psoc); + + return status; +} + +static void __dsc_psoc_destroy(struct dsc_psoc **out_psoc) +{ + struct dsc_psoc *psoc; + + if (!dsc_assert(out_psoc)) + return; + + psoc = *out_psoc; + if (!dsc_assert(psoc)) + return; + + *out_psoc = NULL; + + /* assert no children */ + dsc_assert(qdf_list_empty(&psoc->vdevs)); + + /* flush pending transitions */ + while (__dsc_trans_abort(&psoc->trans)) + ; + + /* detach */ + __dsc_driver_lock(psoc); + qdf_list_remove_node(&psoc->driver->psocs, &psoc->node); + __dsc_driver_unlock(psoc); + + /* de-init */ + __dsc_ops_deinit(&psoc->ops); + __dsc_trans_deinit(&psoc->trans); + qdf_list_destroy(&psoc->vdevs); + psoc->driver = NULL; + + qdf_tfree(psoc); +} + +void dsc_psoc_destroy(struct dsc_psoc **out_psoc) +{ + __dsc_psoc_destroy(out_psoc); +} + +static bool __dsc_psoc_trans_active_down_tree(struct dsc_psoc *psoc) +{ + struct dsc_vdev *vdev; + + dsc_for_each_psoc_vdev(psoc, vdev) { + if (__dsc_trans_active(&vdev->trans)) + return true; + } + + return false; +} + +#define __dsc_psoc_can_op(psoc) __dsc_psoc_can_trans(psoc) + +/* + * __dsc_psoc_can_trans() - Returns if the psoc transition can occur or not + * @psoc: The DSC psoc + * + * This function checks if the psoc transition can occur or not by checking if + * any other down the tree/up the tree transition/operation is taking place. + * + * If there are any driver transition taking place, then the psoc trans/ops + * should be rejected and not queued in the DSC queue. Return QDF_STATUS_E_INVAL + * in this case. + * + * If there any psoc or vdev trans/ops is taking place, then the psoc trans/ops + * should be rejected and queued in the DSC queue so that it may be resumed + * after the current trans/ops is completed. Return QDF_STATUS_E_AGAIN in this + * case. + * + * Return: QDF_STATUS_SUCCESS if transition is allowed, error code if not. + */ +static QDF_STATUS __dsc_psoc_can_trans(struct dsc_psoc *psoc) +{ + if (__dsc_trans_active_or_queued(&psoc->driver->trans)) + return QDF_STATUS_E_INVAL; + + if (__dsc_trans_active_or_queued(&psoc->trans) || + __dsc_psoc_trans_active_down_tree(psoc)) + return QDF_STATUS_E_AGAIN; + + return QDF_STATUS_SUCCESS; +} + +static bool __dsc_psoc_can_trigger(struct dsc_psoc *psoc) +{ + return !__dsc_trans_active_or_queued(&psoc->driver->trans) && + !__dsc_trans_active(&psoc->trans) && + !__dsc_psoc_trans_active_down_tree(psoc); +} + +static QDF_STATUS +__dsc_psoc_trans_start_nolock(struct dsc_psoc *psoc, const char *desc) +{ + QDF_STATUS status; + + status = __dsc_psoc_can_trans(psoc); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + return __dsc_trans_start(&psoc->trans, desc); +} + +static QDF_STATUS +__dsc_psoc_trans_start(struct dsc_psoc *psoc, const char *desc) +{ + QDF_STATUS status; + + if (!dsc_assert(psoc)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(desc)) + return QDF_STATUS_E_INVAL; + + __dsc_driver_lock(psoc); + status = __dsc_psoc_trans_start_nolock(psoc, desc); + __dsc_driver_unlock(psoc); + + return status; +} + +QDF_STATUS dsc_psoc_trans_start(struct dsc_psoc *psoc, const char *desc) +{ + QDF_STATUS status; + + dsc_enter_str(desc); + status = __dsc_psoc_trans_start(psoc, desc); + if (QDF_IS_STATUS_ERROR(status)) + dsc_exit_status(status); + + return status; +} + +static QDF_STATUS +__dsc_psoc_trans_start_wait(struct dsc_psoc *psoc, const char *desc) +{ + QDF_STATUS status; + struct dsc_tran tran = { 0 }; + + if (!dsc_assert(psoc)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(desc)) + return QDF_STATUS_E_INVAL; + + __dsc_driver_lock(psoc); + + /* try to start without waiting */ + status = __dsc_psoc_trans_start_nolock(psoc, desc); + if (QDF_IS_STATUS_SUCCESS(status) || status == QDF_STATUS_E_INVAL) + goto unlock; + + status = __dsc_trans_queue(&psoc->trans, &tran, desc); + if (QDF_IS_STATUS_ERROR(status)) + goto unlock; + + __dsc_driver_unlock(psoc); + + return __dsc_tran_wait(&tran); + +unlock: + __dsc_driver_unlock(psoc); + + return status; +} + +QDF_STATUS dsc_psoc_trans_start_wait(struct dsc_psoc *psoc, const char *desc) +{ + QDF_STATUS status; + + dsc_enter_str(desc); + status = __dsc_psoc_trans_start_wait(psoc, desc); + if (QDF_IS_STATUS_ERROR(status)) + dsc_exit_status(status); + + return status; +} + +static void __dsc_psoc_trigger_trans(struct dsc_psoc *psoc) +{ + struct dsc_vdev *vdev; + + if (__dsc_driver_trans_trigger_checked(psoc->driver)) + return; + + if (__dsc_trans_trigger(&psoc->trans)) + return; + + dsc_for_each_psoc_vdev(psoc, vdev) + __dsc_trans_trigger(&vdev->trans); +} + +static void __dsc_psoc_trans_stop(struct dsc_psoc *psoc) +{ + if (!dsc_assert(psoc)) + return; + + __dsc_driver_lock(psoc); + + __dsc_trans_stop(&psoc->trans); + __dsc_psoc_trigger_trans(psoc); + + __dsc_driver_unlock(psoc); +} + +void dsc_psoc_trans_stop(struct dsc_psoc *psoc) +{ + __dsc_psoc_trans_stop(psoc); +} + +static void __dsc_psoc_assert_trans_protected(struct dsc_psoc *psoc) +{ + if (!dsc_assert(psoc)) + return; + + __dsc_driver_lock(psoc); + dsc_assert(__dsc_trans_active(&psoc->trans) || + __dsc_trans_active(&psoc->driver->trans)); + __dsc_driver_unlock(psoc); +} + +void dsc_psoc_assert_trans_protected(struct dsc_psoc *psoc) +{ + __dsc_psoc_assert_trans_protected(psoc); +} + +bool __dsc_psoc_trans_trigger_checked(struct dsc_psoc *psoc) +{ + if (qdf_list_empty(&psoc->trans.queue)) + return false; + + /* handled, but don't trigger; we need to wait for more children */ + if (!__dsc_psoc_can_trigger(psoc)) + return true; + + return __dsc_trans_trigger(&psoc->trans); +} + +static QDF_STATUS __dsc_psoc_op_start(struct dsc_psoc *psoc, const char *func) +{ + QDF_STATUS status; + + if (!dsc_assert(psoc)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(func)) + return QDF_STATUS_E_INVAL; + + __dsc_driver_lock(psoc); + + status = __dsc_psoc_can_op(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto unlock; + + status = __dsc_ops_insert(&psoc->ops, func); + +unlock: + __dsc_driver_unlock(psoc); + + return status; +} + +QDF_STATUS _dsc_psoc_op_start(struct dsc_psoc *psoc, const char *func) +{ + QDF_STATUS status; + + status = __dsc_psoc_op_start(psoc, func); + + return status; +} + +static void __dsc_psoc_op_stop(struct dsc_psoc *psoc, const char *func) +{ + if (!dsc_assert(psoc)) + return; + + if (!dsc_assert(func)) + return; + + __dsc_driver_lock(psoc); + if (__dsc_ops_remove(&psoc->ops, func)) + qdf_event_set(&psoc->ops.event); + __dsc_driver_unlock(psoc); +} + +void _dsc_psoc_op_stop(struct dsc_psoc *psoc, const char *func) +{ + __dsc_psoc_op_stop(psoc, func); +} + +static void __dsc_psoc_wait_for_ops(struct dsc_psoc *psoc) +{ + struct dsc_vdev *vdev; + bool wait; + + if (!dsc_assert(psoc)) + return; + + __dsc_driver_lock(psoc); + + wait = psoc->ops.count > 0; + if (wait) + qdf_event_reset(&psoc->ops.event); + + __dsc_driver_unlock(psoc); + + if (wait) + qdf_wait_single_event(&psoc->ops.event, 0); + + /* wait for down-tree ops to complete as well */ + dsc_for_each_psoc_vdev(psoc, vdev) + dsc_vdev_wait_for_ops(vdev); +} + +void dsc_psoc_wait_for_ops(struct dsc_psoc *psoc) +{ + __dsc_psoc_wait_for_ops(psoc); +} + diff --git a/drivers/staging/qcacld-3.0/components/dsc/src/wlan_dsc_vdev.c b/drivers/staging/qcacld-3.0/components/dsc/src/wlan_dsc_vdev.c new file mode 100644 index 0000000000000000000000000000000000000000..b045ffd53334d22d24fd4e53dd3b7fcef23acd22 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/src/wlan_dsc_vdev.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "qdf_list.h" +#include "qdf_status.h" +#include "qdf_talloc.h" +#include "qdf_types.h" +#include "__wlan_dsc.h" +#include "wlan_dsc.h" +#include "qdf_platform.h" + +#define __dsc_driver_lock(vdev) __dsc_lock((vdev)->psoc->driver) +#define __dsc_driver_unlock(vdev) __dsc_unlock((vdev)->psoc->driver) + +static QDF_STATUS +__dsc_vdev_create(struct dsc_psoc *psoc, struct dsc_vdev **out_vdev) +{ + struct dsc_vdev *vdev; + + if (!dsc_assert(psoc)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(out_vdev)) + return QDF_STATUS_E_INVAL; + + *out_vdev = NULL; + + vdev = qdf_talloc_type(psoc, vdev); + if (!vdev) + return QDF_STATUS_E_NOMEM; + + /* init */ + vdev->psoc = psoc; + __dsc_trans_init(&vdev->trans); + __dsc_ops_init(&vdev->ops); + + /* attach */ + __dsc_driver_lock(vdev); + qdf_list_insert_back(&psoc->vdevs, &vdev->node); + __dsc_driver_unlock(vdev); + + *out_vdev = vdev; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dsc_vdev_create(struct dsc_psoc *psoc, struct dsc_vdev **out_vdev) +{ + QDF_STATUS status; + + status = __dsc_vdev_create(psoc, out_vdev); + + return status; +} + +static void __dsc_vdev_destroy(struct dsc_vdev **out_vdev) +{ + struct dsc_vdev *vdev; + + if (!dsc_assert(out_vdev)) + return; + + vdev = *out_vdev; + if (!dsc_assert(vdev)) + return; + + *out_vdev = NULL; + + /* flush pending transitions */ + while (__dsc_trans_abort(&vdev->trans)) + ; + + /* detach */ + __dsc_driver_lock(vdev); + qdf_list_remove_node(&vdev->psoc->vdevs, &vdev->node); + __dsc_driver_unlock(vdev); + + /* de-init */ + __dsc_ops_deinit(&vdev->ops); + __dsc_trans_deinit(&vdev->trans); + vdev->psoc = NULL; + + qdf_tfree(vdev); +} + +void dsc_vdev_destroy(struct dsc_vdev **out_vdev) +{ + __dsc_vdev_destroy(out_vdev); +} + +#define __dsc_vdev_can_op(vdev) __dsc_vdev_can_trans(vdev) + +/* + * __dsc_vdev_can_trans() - Returns if the vdev transition can occur or not + * @vdev: The DSC vdev + * + * This function checks if the vdev transition can occur or not by checking if + * any other down the tree/up the tree transition/operation is taking place. + * + * If there are any driver transition taking place, then the vdev trans/ops + * should be rejected and not queued in the DSC queue. Return QDF_STATUS_E_INVAL + * in this case. + * + * If there are any psoc transition taking place because of SSR, then vdev + * trans/op should be rejected and queued in the DSC queue so that it may be + * resumed after the current trans/op is completed. return QDF_STATUS_E_AGAIN + * in this case. + * + * If there is a psoc transition taking place becasue of psoc idle shutdown, + * then the vdev trans/ops should be rejected and queued in the DSC queue so + * that it may be resumed after the current trans/ops is completed. Return + * QDF_STATUS_E_AGAIN in this case. + * + * If there are any vdev trans/ops taking place, then the vdev trans/ops + * should be rejected and queued in the DSC queue so that it may be resumed + * after the current trans/ops is completed. Return QDF_STATUS_E_AGAIN in this + * case. + * + * Return: QDF_STATUS_SUCCESS if transition is allowed, error code if not. + */ +static QDF_STATUS __dsc_vdev_can_trans(struct dsc_vdev *vdev) +{ + if (__dsc_trans_active_or_queued(&vdev->psoc->driver->trans)) + return QDF_STATUS_E_INVAL; + + if (qdf_is_recovering()) + return QDF_STATUS_E_INVAL; + + if (__dsc_trans_active_or_queued(&vdev->psoc->trans)) { + /* psoc idle shutdown(wifi off) needs to be added in DSC queue + * to avoid wifi on failure while previous psoc idle shutdown + * is in progress and wifi is turned on. And Wifi On also needs + * to be added to the queue so that it waits for SSR to + * complete. + */ + if (qdf_is_driver_unloading()) + return QDF_STATUS_E_INVAL; + else + return QDF_STATUS_E_AGAIN; + } + + if (__dsc_trans_active_or_queued(&vdev->trans)) + return QDF_STATUS_E_AGAIN; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +__dsc_vdev_trans_start_nolock(struct dsc_vdev *vdev, const char *desc) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = __dsc_vdev_can_trans(vdev); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + return __dsc_trans_start(&vdev->trans, desc); +} + +static QDF_STATUS +__dsc_vdev_trans_start(struct dsc_vdev *vdev, const char *desc) +{ + QDF_STATUS status; + + if (!dsc_assert(vdev)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(desc)) + return QDF_STATUS_E_INVAL; + + __dsc_driver_lock(vdev); + status = __dsc_vdev_trans_start_nolock(vdev, desc); + __dsc_driver_unlock(vdev); + + return status; +} + +QDF_STATUS dsc_vdev_trans_start(struct dsc_vdev *vdev, const char *desc) +{ + QDF_STATUS status; + + dsc_enter_str(desc); + status = __dsc_vdev_trans_start(vdev, desc); + if (QDF_IS_STATUS_ERROR(status)) + dsc_exit_status(status); + + return status; +} + +static QDF_STATUS +__dsc_vdev_trans_start_wait(struct dsc_vdev *vdev, const char *desc) +{ + QDF_STATUS status; + struct dsc_tran tran = { 0 }; + + if (!dsc_assert(vdev)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(desc)) + return QDF_STATUS_E_INVAL; + + __dsc_driver_lock(vdev); + + /* try to start without waiting */ + status = __dsc_vdev_trans_start_nolock(vdev, desc); + if (QDF_IS_STATUS_SUCCESS(status) || status == QDF_STATUS_E_INVAL) + goto unlock; + + status = __dsc_trans_queue(&vdev->trans, &tran, desc); + if (QDF_IS_STATUS_ERROR(status)) + goto unlock; + + __dsc_driver_unlock(vdev); + + return __dsc_tran_wait(&tran); + +unlock: + __dsc_driver_unlock(vdev); + + return status; +} + +QDF_STATUS dsc_vdev_trans_start_wait(struct dsc_vdev *vdev, const char *desc) +{ + QDF_STATUS status; + + dsc_enter_str(desc); + status = __dsc_vdev_trans_start_wait(vdev, desc); + if (QDF_IS_STATUS_ERROR(status)) + dsc_exit_status(status); + + return status; +} + +static void __dsc_vdev_trigger_trans(struct dsc_vdev *vdev) +{ + if (__dsc_driver_trans_trigger_checked(vdev->psoc->driver)) + return; + + if (__dsc_psoc_trans_trigger_checked(vdev->psoc)) + return; + + __dsc_trans_trigger(&vdev->trans); +} + +static void __dsc_vdev_trans_stop(struct dsc_vdev *vdev) +{ + if (!dsc_assert(vdev)) + return; + + __dsc_driver_lock(vdev); + + __dsc_trans_stop(&vdev->trans); + __dsc_vdev_trigger_trans(vdev); + + __dsc_driver_unlock(vdev); +} + +void dsc_vdev_trans_stop(struct dsc_vdev *vdev) +{ + __dsc_vdev_trans_stop(vdev); +} + +static void __dsc_vdev_assert_trans_protected(struct dsc_vdev *vdev) +{ + if (!dsc_assert(vdev)) + return; + + __dsc_driver_lock(vdev); + dsc_assert(__dsc_trans_active(&vdev->trans) || + __dsc_trans_active(&vdev->psoc->trans) || + __dsc_trans_active(&vdev->psoc->driver->trans)); + __dsc_driver_unlock(vdev); +} + +void dsc_vdev_assert_trans_protected(struct dsc_vdev *vdev) +{ + __dsc_vdev_assert_trans_protected(vdev); +} + +static QDF_STATUS __dsc_vdev_op_start(struct dsc_vdev *vdev, const char *func) +{ + QDF_STATUS status; + + if (!dsc_assert(vdev)) + return QDF_STATUS_E_INVAL; + + if (!dsc_assert(func)) + return QDF_STATUS_E_INVAL; + + __dsc_driver_lock(vdev); + + status = __dsc_vdev_can_op(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto unlock; + + status = __dsc_ops_insert(&vdev->ops, func); + +unlock: + __dsc_driver_unlock(vdev); + + return status; +} + +QDF_STATUS _dsc_vdev_op_start(struct dsc_vdev *vdev, const char *func) +{ + QDF_STATUS status; + + /* do not log from here because it can flood log message because vdev + * op protect is per vdev operation + */ + + status = __dsc_vdev_op_start(vdev, func); + + return status; +} + +static void __dsc_vdev_op_stop(struct dsc_vdev *vdev, const char *func) +{ + if (!dsc_assert(vdev)) + return; + + if (!dsc_assert(func)) + return; + + __dsc_driver_lock(vdev); + if (__dsc_ops_remove(&vdev->ops, func)) + qdf_event_set(&vdev->ops.event); + __dsc_driver_unlock(vdev); +} + +void _dsc_vdev_op_stop(struct dsc_vdev *vdev, const char *func) +{ + /* do not log from here because it can flood log message because vdev + * op protect is per vdev operation + */ + __dsc_vdev_op_stop(vdev, func); +} + +static void __dsc_vdev_wait_for_ops(struct dsc_vdev *vdev) +{ + bool wait; + + if (!dsc_assert(vdev)) + return; + + __dsc_driver_lock(vdev); + + wait = vdev->ops.count > 0; + if (wait) + qdf_event_reset(&vdev->ops.event); + + __dsc_driver_unlock(vdev); + + if (wait) + qdf_wait_single_event(&vdev->ops.event, 0); +} + +void dsc_vdev_wait_for_ops(struct dsc_vdev *vdev) +{ + __dsc_vdev_wait_for_ops(vdev); +} + diff --git a/drivers/staging/qcacld-3.0/components/dsc/test/wlan_dsc_test.c b/drivers/staging/qcacld-3.0/components/dsc/test/wlan_dsc_test.c new file mode 100644 index 0000000000000000000000000000000000000000..3354e0d1c1ebdba12b2bec084a60cb3adb834363 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/test/wlan_dsc_test.c @@ -0,0 +1,628 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "__wlan_dsc.h" +#include "qdf_event.h" +#include "qdf_threads.h" +#include "qdf_trace.h" +#include "qdf_types.h" +#include "wlan_dsc.h" +#include "wlan_dsc_test.h" +#include "cds_api.h" + +#define dsc_driver_trans_start(driver) dsc_driver_trans_start(driver, __func__) +#define dsc_psoc_trans_start(psoc) dsc_psoc_trans_start(psoc, __func__) +#define dsc_vdev_trans_start(vdev) dsc_vdev_trans_start(vdev, __func__) + +#define dsc_driver_trans_start_wait(driver) \ + dsc_driver_trans_start_wait(driver, "") +#define dsc_psoc_trans_start_wait(psoc) \ + dsc_psoc_trans_start_wait(psoc, __func__) +#define dsc_vdev_trans_start_wait(vdev) \ + dsc_vdev_trans_start_wait(vdev, __func__) + +static struct dsc_psoc *nth_psoc(struct dsc_driver *driver, int n) +{ + struct dsc_psoc *psoc; + + QDF_BUG(n > 0); + if (n <= 0) + return NULL; + + dsc_for_each_driver_psoc(driver, psoc) { + n--; + if (n) + continue; + + return psoc; + } + + QDF_DEBUG_PANIC("Failed to find nth psoc: %d", n); + + return NULL; +} + +static struct dsc_vdev *nth_vdev(struct dsc_psoc *psoc, int n) +{ + struct dsc_vdev *vdev; + + QDF_BUG(n > 0); + if (n <= 0) + return NULL; + + dsc_for_each_psoc_vdev(psoc, vdev) { + n--; + if (n) + continue; + + return vdev; + } + + QDF_DEBUG_PANIC("Failed to find nth vdev: %d", n); + + return NULL; +} + +static void __dsc_tree_destroy(struct dsc_driver *driver) +{ + struct dsc_psoc *psoc; + struct dsc_psoc *next_psoc; + + QDF_BUG(driver); + + qdf_list_for_each_del(&driver->psocs, psoc, next_psoc, node) { + struct dsc_vdev *vdev; + struct dsc_vdev *next_vdev; + + qdf_list_for_each_del(&psoc->vdevs, vdev, next_vdev, node) + dsc_vdev_destroy(&vdev); + + dsc_psoc_destroy(&psoc); + } + + dsc_driver_destroy(&driver); +} + +static QDF_STATUS __dsc_tree_create(struct dsc_driver **out_driver, + uint8_t psocs_per_driver, + uint8_t vdevs_per_psoc) +{ + QDF_STATUS status; + struct dsc_driver *driver; + int i, j; + + status = dsc_driver_create(&driver); + if (QDF_IS_STATUS_ERROR(status)) { + dsc_err("Failed to create driver; status:%u", status); + return status; + } + + for (i = 0; i < psocs_per_driver; i++) { + struct dsc_psoc *psoc; + + status = dsc_psoc_create(driver, &psoc); + if (QDF_IS_STATUS_ERROR(status)) { + dsc_err("Failed to create psoc; status:%u", status); + goto free_tree; + } + + for (j = 0; j < vdevs_per_psoc; j++) { + struct dsc_vdev *vdev; + + status = dsc_vdev_create(psoc, &vdev); + if (QDF_IS_STATUS_ERROR(status)) { + dsc_err("Failed to create vdev; status:%u", + status); + goto free_tree; + } + } + } + + *out_driver = driver; + + return QDF_STATUS_SUCCESS; + +free_tree: + __dsc_tree_destroy(driver); + + return status; +} + +static uint32_t dsc_test_create_destroy(void) +{ + uint32_t errors = 0; + QDF_STATUS status; + struct dsc_driver *driver; + + dsc_enter(); + + status = __dsc_tree_create(&driver, 2, 2); + if (QDF_IS_STATUS_ERROR(status)) { + errors++; + goto exit; + } + + __dsc_tree_destroy(driver); + +exit: + dsc_exit(); + + return errors; +} + +#define action_expect(obj, action, status, errors) \ +do { \ + void *__obj = obj; \ + QDF_STATUS __expected = status; \ + QDF_STATUS __result; \ +\ + __result = dsc_##obj##_##action##_start(__obj); \ + if (__result != __expected) { \ + dsc_err("FAIL: " #obj " " #action \ + "; expected " #status " (%u), found %u", \ + __expected, __result); \ + (errors)++; \ + } \ + if (QDF_IS_STATUS_SUCCESS(__result) && QDF_IS_STATUS_ERROR(__expected))\ + dsc_##obj##_##action##_stop(__obj); \ +} while (false) + +static uint32_t dsc_test_driver_trans_blocks(void) +{ + uint32_t errors = 0; + QDF_STATUS status; + struct dsc_driver *driver; + struct dsc_psoc *psoc; + struct dsc_vdev *vdev; + + dsc_enter(); + + /* setup */ + + status = __dsc_tree_create(&driver, 2, 2); + if (QDF_IS_STATUS_ERROR(status)) { + errors++; + goto exit; + } + + /* test */ + /* a driver in transition should cause ... */ + action_expect(driver, trans, QDF_STATUS_SUCCESS, errors); + + /* ... the same driver trans/ops to fail */ + action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(driver, op, QDF_STATUS_E_AGAIN, errors); + + /* ... children psoc trans/ops to fail */ + dsc_for_each_driver_psoc(driver, psoc) { + action_expect(psoc, trans, QDF_STATUS_E_INVAL, errors); + action_expect(psoc, op, QDF_STATUS_E_INVAL, errors); + + /* ... grandchildren vdev trans/ops to fail */ + dsc_for_each_psoc_vdev(psoc, vdev) { + action_expect(vdev, trans, QDF_STATUS_E_INVAL, errors); + action_expect(vdev, op, QDF_STATUS_E_INVAL, errors); + } + } + + /* teardown */ + + dsc_driver_trans_stop(driver); + + __dsc_tree_destroy(driver); + +exit: + dsc_exit(); + + return errors; +} + +static uint32_t dsc_test_psoc_trans_blocks(void) +{ + uint32_t errors = 0; + QDF_STATUS status; + struct dsc_driver *driver; + struct dsc_psoc *psoc; + struct dsc_vdev *vdev; + + dsc_enter(); + + /* setup */ + + status = __dsc_tree_create(&driver, 2, 2); + if (QDF_IS_STATUS_ERROR(status)) { + errors++; + goto exit; + } + + /* test */ + /* a psoc in transition should cause ... */ + psoc = nth_psoc(driver, 1); + action_expect(psoc, trans, QDF_STATUS_SUCCESS, errors); + + /* ... driver trans/ops to fail */ + action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(driver, op, QDF_STATUS_E_AGAIN, errors); + + /* ... the same psoc trans/ops to fail */ + action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors); + + /* ... children vdev trans/ops to fail */ + dsc_for_each_psoc_vdev(psoc, vdev) { + action_expect(vdev, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(vdev, op, QDF_STATUS_E_AGAIN, errors); + } + + /* ... while driver unload in progress vdev op and trans should be + * rejected with EINVAL + */ + cds_set_unload_in_progress(true); + dsc_for_each_psoc_vdev(psoc, vdev) { + action_expect(vdev, trans, QDF_STATUS_E_INVAL, errors); + action_expect(vdev, op, QDF_STATUS_E_INVAL, errors); + } + cds_set_unload_in_progress(false); + + /* ... while SSR recovery in progress vdev op and trans should be + * rejected with EINVAL + */ + cds_set_recovery_in_progress(true); + dsc_for_each_psoc_vdev(psoc, vdev) { + action_expect(vdev, trans, QDF_STATUS_E_INVAL, errors); + action_expect(vdev, op, QDF_STATUS_E_INVAL, errors); + } + cds_set_recovery_in_progress(false); + + /* a sibling psoc in transition should succeed and cause ... */ + psoc = nth_psoc(driver, 2); + action_expect(psoc, trans, QDF_STATUS_SUCCESS, errors); + + /* ... driver trans/ops to fail */ + action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(driver, op, QDF_STATUS_E_AGAIN, errors); + + /* ... the same psoc trans/ops to fail */ + action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors); + + /* ... children vdev trans/ops to fail */ + dsc_for_each_psoc_vdev(psoc, vdev) { + action_expect(vdev, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(vdev, op, QDF_STATUS_E_AGAIN, errors); + } + + /* teardown */ + + dsc_for_each_driver_psoc(driver, psoc) + dsc_psoc_trans_stop(psoc); + + __dsc_tree_destroy(driver); + +exit: + dsc_exit(); + + return errors; +} + +static uint32_t dsc_test_vdev_trans_blocks(void) +{ + uint32_t errors = 0; + QDF_STATUS status; + struct dsc_driver *driver; + struct dsc_psoc *psoc; + struct dsc_vdev *vdev; + + dsc_enter(); + + /* setup */ + + status = __dsc_tree_create(&driver, 2, 2); + if (QDF_IS_STATUS_ERROR(status)) { + errors++; + goto exit; + } + + /* test */ + + /* a vdev in transition should cause ... */ + dsc_for_each_driver_psoc(driver, psoc) { + dsc_for_each_psoc_vdev(psoc, vdev) + action_expect(vdev, trans, QDF_STATUS_SUCCESS, errors); + } + + /* ... driver trans/ops to fail */ + action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(driver, op, QDF_STATUS_E_AGAIN, errors); + + /* ... psoc trans/ops to fail */ + dsc_for_each_driver_psoc(driver, psoc) { + action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors); + + /* ... the same vdev trans/ops to fail */ + dsc_for_each_psoc_vdev(psoc, vdev) { + action_expect(vdev, trans, QDF_STATUS_E_AGAIN, errors); + action_expect(vdev, op, QDF_STATUS_E_AGAIN, errors); + } + } + + /* teardown */ + + dsc_for_each_driver_psoc(driver, psoc) { + dsc_for_each_psoc_vdev(psoc, vdev) + dsc_vdev_trans_stop(vdev); + } + + __dsc_tree_destroy(driver); + +exit: + dsc_exit(); + + return errors; +} + +#define THREAD_TIMEOUT 1000 /* ms */ +#define dsc_event_wait(event) qdf_wait_single_event(event, THREAD_TIMEOUT) + +#define step_assert(field, expected) \ +do { \ + uint32_t _step = ++(field); \ + uint32_t _expected = (expected); \ +\ + if (_step != _expected) \ + QDF_DEBUG_PANIC("Step count is %u; Expected %u", \ + _step, _expected); \ +} while (false) + +#define trans_waiting(ctx) (!qdf_list_empty(&(ctx)->trans.queue)) + +struct thread_ctx { + struct dsc_driver *driver; + qdf_event_t start_vdev_trans; + qdf_event_t start_vdev_wait; + uint32_t step; +}; + +static QDF_STATUS dsc_thread_ops(void *context) +{ + struct thread_ctx *ctx = context; + struct dsc_driver *driver = ctx->driver; + struct dsc_psoc *psoc = nth_psoc(driver, 1); + struct dsc_vdev *vdev = nth_vdev(psoc, 1); + + dsc_enter(); + + /* thread 1 is doing some operations ... */ + step_assert(ctx->step, 1); + dsc_assert_success(dsc_driver_op_start(driver)); + dsc_assert_success(dsc_psoc_op_start(psoc)); + dsc_assert_success(dsc_vdev_op_start(vdev)); + step_assert(ctx->step, 2); + + /* ... at which point, thread 2 starts to transition the vdevs */ + qdf_event_set(&ctx->start_vdev_trans); + + /* meanwhile, thread 3,4,5 queue up to transition vdev/psoc/driver */ + while (!trans_waiting(driver)) + schedule(); + + /* at this point, each thread is: + * 1) doing operations + * 2) transitioning vdevs 1/2, waiting for ops to finish + * 3) waiting to transition vdev 1 + * 4) waiting to transition psoc + * 5) waitint to transition driver + */ + + step_assert(ctx->step, 8); + dsc_driver_op_stop(driver); + schedule(); + dsc_psoc_op_stop(psoc); + schedule(); + dsc_vdev_op_stop(vdev); + + /* all operations complete; thread2 is now unblocked */ + + dsc_exit(); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS dsc_thread_vdev_trans(void *context) +{ + struct thread_ctx *ctx = context; + struct dsc_driver *driver = ctx->driver; + struct dsc_psoc *psoc = nth_psoc(driver, 1); + struct dsc_vdev *vdev; + + dsc_enter(); + + /* wait for thread 1 to start operations */ + dsc_assert_success(dsc_event_wait(&ctx->start_vdev_trans)); + + /* start transitions on all vdevs */ + step_assert(ctx->step, 3); + dsc_for_each_psoc_vdev(psoc, vdev) + dsc_assert_success(dsc_vdev_trans_start(vdev)); + step_assert(ctx->step, 4); + + /* meanwhile, thread 3,4,5 queue up to transition vdev/psoc/driver */ + qdf_event_set(&ctx->start_vdev_wait); + + /* wait for thread 1 to complete pending vdev ops */ + dsc_for_each_psoc_vdev(psoc, vdev) + dsc_vdev_wait_for_ops(vdev); + + /* actual vdev transition work would happen here */ + + /* stop transition on vdev 1 */ + step_assert(ctx->step, 9); + dsc_vdev_trans_stop(nth_vdev(psoc, 1)); + + /* psoc trans should not start until both vdev trans are complete */ + schedule(); + step_assert(ctx->step, 10); + dsc_vdev_trans_stop(nth_vdev(psoc, 2)); + + dsc_exit(); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS dsc_thread_vdev_wait(void *context) +{ + struct thread_ctx *ctx = context; + struct dsc_vdev *vdev = nth_vdev(nth_psoc(ctx->driver, 1), 1); + + dsc_enter(); + + dsc_assert_success(dsc_event_wait(&ctx->start_vdev_wait)); + + step_assert(ctx->step, 5); + /* vdev trans queues first ... */ + dsc_assert_success(dsc_vdev_trans_start_wait(vdev)); + /* ... but de-queues third */ + step_assert(ctx->step, 15); + + dsc_vdev_wait_for_ops(vdev); + + step_assert(ctx->step, 16); + dsc_vdev_trans_stop(vdev); + + dsc_exit(); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS dsc_thread_psoc_wait(void *context) +{ + struct thread_ctx *ctx = context; + struct dsc_psoc *psoc = nth_psoc(ctx->driver, 1); + struct dsc_vdev *vdev = nth_vdev(psoc, 1); + + dsc_enter(); + + while (!trans_waiting(vdev)) + schedule(); + + step_assert(ctx->step, 6); + /* psoc trans queues second ... */ + dsc_assert_success(dsc_psoc_trans_start_wait(psoc)); + /* ... and de-queues second */ + step_assert(ctx->step, 13); + + dsc_psoc_wait_for_ops(psoc); + + step_assert(ctx->step, 14); + dsc_psoc_trans_stop(psoc); + + dsc_exit(); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS dsc_thread_driver_wait(void *context) +{ + struct thread_ctx *ctx = context; + struct dsc_driver *driver = ctx->driver; + struct dsc_psoc *psoc = nth_psoc(driver, 1); + + dsc_enter(); + + while (!trans_waiting(psoc)) + schedule(); + + step_assert(ctx->step, 7); + /* driver trans queues third ... */ + dsc_assert_success(dsc_driver_trans_start_wait(driver)); + /* ... but de-queues first */ + step_assert(ctx->step, 11); + + dsc_driver_wait_for_ops(driver); + + step_assert(ctx->step, 12); + dsc_driver_trans_stop(driver); + + dsc_exit(); + + return QDF_STATUS_SUCCESS; +} + +static uint32_t dsc_test_trans_wait(void) +{ + uint32_t errors = 0; + QDF_STATUS status; + qdf_thread_t *ops_thread; + qdf_thread_t *vdev_trans_thread; + qdf_thread_t *vdev_wait_thread; + qdf_thread_t *psoc_wait_thread; + qdf_thread_t *driver_wait_thread; + struct thread_ctx ctx = { 0 }; + + dsc_enter(); + + status = __dsc_tree_create(&ctx.driver, 1, 2); + if (QDF_IS_STATUS_ERROR(status)) { + errors++; + goto exit; + } + + dsc_assert_success(qdf_event_create(&ctx.start_vdev_trans)); + dsc_assert_success(qdf_event_create(&ctx.start_vdev_wait)); + + dsc_debug("starting threads"); + + ops_thread = qdf_thread_run(dsc_thread_ops, &ctx); + vdev_trans_thread = qdf_thread_run(dsc_thread_vdev_trans, &ctx); + vdev_wait_thread = qdf_thread_run(dsc_thread_vdev_wait, &ctx); + psoc_wait_thread = qdf_thread_run(dsc_thread_psoc_wait, &ctx); + driver_wait_thread = qdf_thread_run(dsc_thread_driver_wait, &ctx); + + qdf_thread_join(ops_thread); + qdf_thread_join(vdev_trans_thread); + qdf_thread_join(vdev_wait_thread); + qdf_thread_join(psoc_wait_thread); + qdf_thread_join(driver_wait_thread); + + dsc_debug("threads joined"); + + qdf_event_destroy(&ctx.start_vdev_wait); + qdf_event_destroy(&ctx.start_vdev_trans); + + __dsc_tree_destroy(ctx.driver); + +exit: + dsc_exit(); + + return errors; +} + +uint32_t dsc_unit_test(void) +{ + uint32_t errors = 0; + + errors += dsc_test_create_destroy(); + errors += dsc_test_driver_trans_blocks(); + errors += dsc_test_psoc_trans_blocks(); + errors += dsc_test_vdev_trans_blocks(); + errors += dsc_test_trans_wait(); + + return errors; +} + diff --git a/drivers/staging/qcacld-3.0/components/dsc/test/wlan_dsc_test.h b/drivers/staging/qcacld-3.0/components/dsc/test/wlan_dsc_test.h new file mode 100644 index 0000000000000000000000000000000000000000..b2c23d2acee935c6d5e149898f8a7254076cd2e8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/dsc/test/wlan_dsc_test.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_DSC_TEST +#define __WLAN_DSC_TEST + +#ifdef WLAN_DSC_TEST +/** + * dsc_unit_test() - run the dsc unit test suite + * + * Return: number of failed test cases + */ +uint32_t dsc_unit_test(void); +#else +static inline uint32_t dsc_unit_test(void) +{ + return 0; +} +#endif /* WLAN_DSC_TEST */ + +#endif /* __WLAN_DSC_TEST */ + diff --git a/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/inc/ftm_time_sync_main.h b/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/inc/ftm_time_sync_main.h new file mode 100644 index 0000000000000000000000000000000000000000..a7d5a8df29916e48ec40653be11910f34e1e6b3f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/inc/ftm_time_sync_main.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare private API which shall be used internally only + * in ftm_time_sync component. This file shall include prototypes of + * various notification handlers and logging functions. + * + * Note: This API should be never accessed out of ftm_time_sync component. + */ + +#ifndef _FTM_TIME_SYNC_MAIN_H_ +#define _FTM_TIME_SYNC_MAIN_H_ + +#include +#include "ftm_time_sync_priv.h" +#include "ftm_time_sync_objmgr.h" + +#define ftm_time_sync_log(level, args...) \ + QDF_TRACE(QDF_MODULE_ID_FTM_TIME_SYNC, level, ## args) + +#define ftm_time_sync_logfl(level, format, args...) \ + ftm_time_sync_log(level, FL(format), ## args) + +#define ftm_time_sync_fatal(format, args...) \ + ftm_time_sync_logfl(QDF_TRACE_LEVEL_FATAL, format, ## args) +#define ftm_time_sync_err(format, args...) \ + ftm_time_sync_logfl(QDF_TRACE_LEVEL_ERROR, format, ## args) +#define ftm_time_sync_warn(format, args...) \ + ftm_time_sync_logfl(QDF_TRACE_LEVEL_WARN, format, ## args) +#define ftm_time_sync_info(format, args...) \ + ftm_time_sync_logfl(QDF_TRACE_LEVEL_INFO, format, ## args) +#define ftm_time_sync_debug(format, args...) \ + ftm_time_sync_logfl(QDF_TRACE_LEVEL_DEBUG, format, ## args) + +#define FTM_TIME_SYNC_ENTER() ftm_time_sync_debug("enter") +#define FTM_TIME_SYNC_EXIT() ftm_time_sync_debug("exit") + +/** + * ftm_time_sync_vdev_create_notification(): Handler for vdev create notify. + * @vdev: vdev which is going to be created by objmgr + * @arg: argument for notification handler. + * + * Allocate and attach vdev private object. + * + * Return: QDF_STATUS status in case of success else return error. + */ +QDF_STATUS ftm_timesync_vdev_create_notification(struct wlan_objmgr_vdev *vdev, + void *arg); + +/** + * ftm_time_sync_vdev_destroy_notification(): Handler for vdev destroy notify. + * @vdev: vdev which is going to be destroyed by objmgr + * @arg: argument for notification handler. + * + * Deallocate and detach vdev private object. + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS +ftm_timesync_vdev_destroy_notification(struct wlan_objmgr_vdev *vdev, + void *arg); + +#endif /* end of _FTM_TIME_SYNC_MAIN_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/inc/ftm_time_sync_objmgr.h b/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/inc/ftm_time_sync_objmgr.h new file mode 100644 index 0000000000000000000000000000000000000000..59b6168e5d9bf72e56a5935c150bc7653c3ee917 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/inc/ftm_time_sync_objmgr.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains various object manager related wrappers and helpers + */ + +#ifndef _FTM_TIME_SYNC_OBJMGR_H +#define _FTM_TIME_SYNC_OBJMGR_H + +#include "wlan_cmn.h" +#include "wlan_objmgr_cmn.h" +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_objmgr_global_obj.h" + +/** + * ftm_timesync_vdev_get_ref() - Wrapper to increment ftm_timesync ref count + * @vdev: vdev object + * + * Wrapper for ftm_timesync to increment ref count after checking valid + * object state. + * + * Return: SUCCESS/FAILURE + */ +static inline +QDF_STATUS ftm_timesync_vdev_get_ref(struct wlan_objmgr_vdev *vdev) +{ + return wlan_objmgr_vdev_try_get_ref(vdev, FTM_TIME_SYNC_ID); +} + +/** + * ftm_timesync_vdev_put_ref() - Wrapper to decrement ftm_timesync ref count + * @vdev: vdev object + * + * Wrapper for ftm_timesync to decrement ref count of vdev. + * + * Return: SUCCESS/FAILURE + */ +static inline +void ftm_timesync_vdev_put_ref(struct wlan_objmgr_vdev *vdev) +{ + return wlan_objmgr_vdev_release_ref(vdev, FTM_TIME_SYNC_ID); +} + +/** + * ftm_timesync_vdev_get_priv(): Wrapper to retrieve vdev priv obj + * @vdev: vdev pointer + * + * Wrapper for ftm_timesync to get vdev private object pointer. + * + * Return: Private object of vdev + */ +static inline struct ftm_timesync_vdev_priv * +ftm_timesync_vdev_get_priv(struct wlan_objmgr_vdev *vdev) +{ + struct ftm_timesync_vdev_priv *vdev_priv; + + vdev_priv = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_FTM_TIME_SYNC); + QDF_BUG(vdev_priv); + + return vdev_priv; +} + +#endif /* _FTM_TIME_SYNC_OBJMGR_H */ diff --git a/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/inc/ftm_time_sync_priv.h b/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/inc/ftm_time_sync_priv.h new file mode 100644 index 0000000000000000000000000000000000000000..9619cd28fc1b65ea9166b9ca4a3d6df944081f30 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/inc/ftm_time_sync_priv.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare private API which shall be used internally only + * in ftm_timesync component. This file shall include prototypes of + * ftm_timesync parsing and send logic. + * + * Note: This API should be never accessed out of ftm_timesync component. + */ + +#ifndef _FTM_TIME_SYNC_PRIV_STRUCT_H_ +#define _FTM_TIME_SYNC_PRIV_STRUCT_H_ + +#include +#include +#include "ftm_time_sync_objmgr.h" +#include "wlan_ftm_time_sync_public_struct.h" + +#define WLAN_FTM_TIME_SYNC_PAIR_MAX 32 + +/** + * struct wlan_time_sync_pair - wlan time sync pair + * @qtime_master: master qtime + * @qtime_slave: slave qtime + */ +struct wlan_time_sync_pair { + uint64_t qtime_master; + uint64_t qtime_slave; +}; + +/** + * struct ftm_timesync_vdev_priv - Private object to be stored in vdev + * @qtime_ref: qtime ref + * @mac_ref: mac time ref + * @time_pair: array of master/slave qtime pair + */ + +struct ftm_timesync_priv { + uint64_t qtime_ref; + uint64_t mac_ref; + struct wlan_time_sync_pair time_pair[WLAN_FTM_TIME_SYNC_PAIR_MAX]; +}; + +/** + * struct ftm_timesync_vdev_priv - Private object to be stored in vdev + * @vdev: pointer to vdev object + * @ftm_ts_priv: time sync private struct + * @rx_ops: rx operations for ftm time sync + * @tx_ops: tx operations for ftm time sync + */ +struct ftm_timesync_vdev_priv { + struct wlan_objmgr_vdev *vdev; + struct ftm_timesync_priv ftm_ts_priv; + struct wlan_ftm_timesync_rx_ops rx_ops; + struct wlan_ftm_timesync_tx_ops tx_ops; +}; + +#endif /* End of _FTM_TIME_SYNC_PRIV_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/src/ftm_time_sync_main.c b/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/src/ftm_time_sync_main.c new file mode 100644 index 0000000000000000000000000000000000000000..e16139e655223134a53304783e756e6c238466da --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ftm_time_sync/core/src/ftm_time_sync_main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implement various notification handlers which are accessed + * internally in ftm_timesync component only. + */ + +#include "ftm_time_sync_main.h" + +QDF_STATUS +ftm_timesync_vdev_create_notification(struct wlan_objmgr_vdev *vdev, void *arg) +{ + struct ftm_timesync_vdev_priv *vdev_priv; + QDF_STATUS status; + + vdev_priv = qdf_mem_malloc(sizeof(*vdev_priv)); + if (!vdev_priv) { + status = QDF_STATUS_E_NOMEM; + goto exit; + } + + status = wlan_objmgr_vdev_component_obj_attach( + vdev, WLAN_UMAC_COMP_FTM_TIME_SYNC, + (void *)vdev_priv, QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + ftm_time_sync_err("Failed to attach priv with vdev"); + goto free_vdev_priv; + } + + vdev_priv->vdev = vdev; + goto exit; + +free_vdev_priv: + qdf_mem_free(vdev_priv); + status = QDF_STATUS_E_INVAL; +exit: + return status; +} + +QDF_STATUS +ftm_timesync_vdev_destroy_notification(struct wlan_objmgr_vdev *vdev, void *arg) +{ + struct ftm_timesync_vdev_priv *vdev_priv = NULL; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + vdev_priv = ftm_timesync_vdev_get_priv(vdev); + if (!vdev_priv) { + ftm_time_sync_err("vdev priv is NULL"); + goto exit; + } + + status = wlan_objmgr_vdev_component_obj_detach( + vdev, WLAN_UMAC_COMP_FTM_TIME_SYNC, + (void *)vdev_priv); + if (QDF_IS_STATUS_ERROR(status)) + ftm_time_sync_err("Failed to detach priv with vdev"); + + qdf_mem_free(vdev_priv); + vdev_priv = NULL; + +exit: + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/ftm_time_sync/dispatcher/inc/ftm_time_sync_ucfg_api.h b/drivers/staging/qcacld-3.0/components/ftm_time_sync/dispatcher/inc/ftm_time_sync_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..8f7169de34e5e473a0b25099c563e0f4b6e17270 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ftm_time_sync/dispatcher/inc/ftm_time_sync_ucfg_api.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare public API related to the ftm timesync called by north bound + * HDD/OSIF/LIM + */ + +#ifndef _FTM_TIME_SYNC_UCFG_API_H_ +#define _FTM_TIME_SYNC_UCFG_API_H_ + +#include +#include +//#include "ftm_time_sync_public_struct.h" +#include "ftm_time_sync_objmgr.h" + +#ifdef FEATURE_WLAN_TIME_SYNC_FTM + +/** + * ucfg_ftm_timesync_init() - FTM time sync component initialization. + * + * This function initializes the ftm time sync component and registers + * the handlers which are invoked on vdev creation. + * + * Return: For successful registration - QDF_STATUS_SUCCESS, + * else QDF_STATUS error codes. + */ +QDF_STATUS ucfg_ftm_timesync_init(void); + +/** + * ucfg_ftm_timesync_deinit() - FTM time sync component deinit. + * + * This function deinits ftm time sync component. + * + * Return: None + */ +void ucfg_ftm_timesync_deinit(void); + +#else + +static inline +QDF_STATUS ucfg_ftm_timesync_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void ucfg_ftm_timesync_deinit(void) +{ +} + +#endif /* FEATURE_WLAN_TIME_SYNC_FTM */ +#endif /* _FTM_TIME_SYNC_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ftm_time_sync/dispatcher/inc/wlan_ftm_time_sync_public_struct.h b/drivers/staging/qcacld-3.0/components/ftm_time_sync/dispatcher/inc/wlan_ftm_time_sync_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..6b1b5ba22f6cd6b4bcb70d3ebb166a7fd9257799 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ftm_time_sync/dispatcher/inc/wlan_ftm_time_sync_public_struct.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare various struct, macros which are used for obj mgmt in ftm time + * sync. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_TIME_SYNC_FTM_PUBLIC_STRUCT_H_ +#define _WLAN_TIME_SYNC_FTM_PUBLIC_STRUCT_H_ + +/** + * struct wlan_ftm_timesync_tx_ops - structure of tx operation function + * pointers for ftm timesync component + * @ftm_time_sync_send_qtime: send qtime wmi cmd to FW + * @ftm_time_sync_send_trigger: send ftm time sync trigger cmd + * + */ +struct wlan_ftm_timesync_tx_ops { + QDF_STATUS (*ftm_time_sync_send_qtime)(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint64_t lpass_ts); + QDF_STATUS (*ftm_time_sync_send_trigger)(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, bool mode); +}; + +/** + * struct wlan_ftm_timesync_rx_ops - structure of rx operation function + * pointers for ftm timesync component + * @ftm_timesync_register_start_stop: register ftm timesync start stop event + * @ftm_timesync_regiser_master_slave_offset: register master slave qtime + * offset event + */ +struct wlan_ftm_timesync_rx_ops { + QDF_STATUS (*ftm_timesync_register_start_stop) + (struct wlan_objmgr_psoc *psoc); + QDF_STATUS (*ftm_timesync_regiser_master_slave_offset) + (struct wlan_objmgr_psoc *psoc); +}; +#endif /*_WLAN_TIME_SYNC_FTM_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ftm_time_sync/dispatcher/src/ftm_time_sync_ucfg_api.c b/drivers/staging/qcacld-3.0/components/ftm_time_sync/dispatcher/src/ftm_time_sync_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..cd4412ca2ad586d68e0ebae74537f2c10be0dcb6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ftm_time_sync/dispatcher/src/ftm_time_sync_ucfg_api.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Public API implementation of ftm timesync called by north bound iface. + */ + +#include "ftm_time_sync_ucfg_api.h" +#include "ftm_time_sync_main.h" +#include + +QDF_STATUS ucfg_ftm_timesync_init(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_register_vdev_create_handler( + WLAN_UMAC_COMP_FTM_TIME_SYNC, + ftm_timesync_vdev_create_notification, NULL); + if (!QDF_IS_STATUS_SUCCESS(status)) { + ftm_time_sync_err("Failed to register vdev create handler"); + goto exit; + } + + status = wlan_objmgr_register_vdev_destroy_handler( + WLAN_UMAC_COMP_FTM_TIME_SYNC, + ftm_timesync_vdev_destroy_notification, NULL); + if (QDF_IS_STATUS_SUCCESS(status)) { + ftm_time_sync_debug("vdev create/delete notif registered"); + goto exit; + } + + ftm_time_sync_err("Failed to register vdev delete handler"); + wlan_objmgr_unregister_vdev_create_handler( + WLAN_UMAC_COMP_FTM_TIME_SYNC, + ftm_timesync_vdev_create_notification, NULL); + +exit: + return status; +} + +void ucfg_ftm_timesync_deinit(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_unregister_vdev_create_handler( + WLAN_UMAC_COMP_FTM_TIME_SYNC, + ftm_timesync_vdev_create_notification, NULL); + if (!QDF_IS_STATUS_SUCCESS(status)) + ftm_time_sync_err("Failed to unregister vdev create handler"); + + status = wlan_objmgr_unregister_vdev_destroy_handler( + WLAN_UMAC_COMP_FTM_TIME_SYNC, + ftm_timesync_vdev_destroy_notification, + NULL); + if (!QDF_IS_STATUS_SUCCESS(status)) + ftm_time_sync_err("Failed to unregister vdev delete handler"); +} diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/core/inc/wlan_fw_offload_main.h b/drivers/staging/qcacld-3.0/components/fw_offload/core/inc/wlan_fw_offload_main.h new file mode 100644 index 0000000000000000000000000000000000000000..6fb325ccfddf4667314b1d02c9b4a13551b7921b --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/core/inc/wlan_fw_offload_main.h @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2012 - 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare utility API related to the fw_offload component + * called by other components + */ + +#ifndef _WLAN_FW_OFFLOAD_MAIN_H_ +#define _WLAN_FW_OFFLOAD_MAIN_H_ + +#include +#include +#include +#include + +#include "cfg_ucfg_api.h" +#include "wlan_fwol_public_structs.h" + +#define fwol_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_FWOL, params) +#define fwol_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_FWOL, params) +#define fwol_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_FWOL, params) +#define fwol_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_FWOL, params) +#define fwol_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_FWOL, params) + +#define fwol_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_FWOL, params) +#define fwol_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_FWOL, params) +#define fwol_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_FWOL, params) +#define fwol_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_FWOL, params) +#define fwol_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_FWOL, params) + +/** + * enum wlan_fwol_southbound_event - fw offload south bound event type + * @WLAN_FWOL_EVT_GET_ELNA_BYPASS_RESPONSE: get eLNA bypass response + */ +enum wlan_fwol_southbound_event { + WLAN_FWOL_EVT_INVALID = 0, + WLAN_FWOL_EVT_GET_ELNA_BYPASS_RESPONSE, + WLAN_FWOL_EVT_LAST, + WLAN_FWOL_EVT_MAX = WLAN_FWOL_EVT_LAST - 1 +}; + +/** + * struct wlan_fwol_three_antenna_btc - Three antenna BTC config items + * @btc_mode: Config BTC mode + * @antenna_isolation: Antenna isolation + * @max_tx_power_for_btc: Max wlan tx power in co-ex scenario + * @wlan_low_rssi_threshold: Wlan low rssi threshold for BTC mode switching + * @bt_low_rssi_threshold: BT low rssi threshold for BTC mode switching + * @bt_interference_low_ll: Lower limit of low level BT interference + * @bt_interference_low_ul: Upper limit of low level BT interference + * @bt_interference_medium_ll: Lower limit of medium level BT interference + * @bt_interference_medium_ul: Upper limit of medium level BT interference + * @bt_interference_high_ll: Lower limit of high level BT interference + * @bt_interference_high_ul: Upper limit of high level BT interference + * @btc_mpta_helper_enable: Enable/Disable tri-radio MPTA helper + * @bt_sco_allow_wlan_2g_scan: Enable/Disble wlan 2g scan when + * BT SCO connection is on + * @btc_three_way_coex_config_legacy_enable: Enable/Disable tri-radio coex + * config legacy feature + */ +struct wlan_fwol_coex_config { + uint8_t btc_mode; + uint8_t antenna_isolation; + uint8_t max_tx_power_for_btc; + int16_t wlan_low_rssi_threshold; + int16_t bt_low_rssi_threshold; + int16_t bt_interference_low_ll; + int16_t bt_interference_low_ul; + int16_t bt_interference_medium_ll; + int16_t bt_interference_medium_ul; + int16_t bt_interference_high_ll; + int16_t bt_interference_high_ul; +#ifdef FEATURE_MPTA_HELPER + bool btc_mpta_helper_enable; +#endif + bool bt_sco_allow_wlan_2g_scan; +#ifdef FEATURE_COEX_CONFIG + bool btc_three_way_coex_config_legacy_enable; +#endif +}; + +#define FWOL_THERMAL_LEVEL_MAX 4 +#define FWOL_THERMAL_THROTTLE_LEVEL_MAX 6 +/* + * struct wlan_fwol_thermal_temp - Thermal temperature config items + * @thermal_temp_min_level: Array of temperature minimum levels + * @thermal_temp_max_level: Array of temperature maximum levels + * @thermal_mitigation_enable: Control for Thermal mitigation feature + * @throttle_period: Thermal throttle period value + * @throttle_dutycycle_level: Array of throttle duty cycle levels + * @thermal_sampling_time: sampling time for thermal mitigation in ms + */ +struct wlan_fwol_thermal_temp { + bool thermal_mitigation_enable; + uint32_t throttle_period; + uint16_t thermal_temp_min_level[FWOL_THERMAL_LEVEL_MAX]; + uint16_t thermal_temp_max_level[FWOL_THERMAL_LEVEL_MAX]; + uint32_t throttle_dutycycle_level[FWOL_THERMAL_THROTTLE_LEVEL_MAX]; + uint16_t thermal_sampling_time; +}; + +/** + * struct wlan_fwol_ie_whitelist - Probe request IE whitelist config items + * @ie_whitelist: IE whitelist flag + * @ie_bitmap_0: IE bitmap 0 + * @ie_bitmap_1: IE bitmap 1 + * @ie_bitmap_2: IE bitmap 2 + * @ie_bitmap_3: IE bitmap 3 + * @ie_bitmap_4: IE bitmap 4 + * @ie_bitmap_5: IE bitmap 5 + * @ie_bitmap_6: IE bitmap 6 + * @ie_bitmap_7: IE bitmap 7 + * @no_of_probe_req_ouis: Total number of ouis present in probe req + * @probe_req_voui: Stores oui values after parsing probe req ouis + */ +struct wlan_fwol_ie_whitelist { + bool ie_whitelist; + uint32_t ie_bitmap_0; + uint32_t ie_bitmap_1; + uint32_t ie_bitmap_2; + uint32_t ie_bitmap_3; + uint32_t ie_bitmap_4; + uint32_t ie_bitmap_5; + uint32_t ie_bitmap_6; + uint32_t ie_bitmap_7; + uint32_t no_of_probe_req_ouis; + uint32_t probe_req_voui[MAX_PROBE_REQ_OUIS]; +}; + +/** + * struct wlan_fwol_neighbor_report_cfg - Neighbor report config params + * @enable_bitmask: Neighbor report offload bitmask control + * @params_bitmask: Param validity bitmask + * @time_offset: Neighbor report frame time offset + * @low_rssi_offset: Low RSSI offset + * @bmiss_count_trigger: Beacon miss trigger count + * @per_threshold_offset: PER Threshold offset + * @cache_timeout: Cache timeout + * @max_req_cap: Max request per peer + */ +struct wlan_fwol_neighbor_report_cfg { + uint32_t enable_bitmask; + uint32_t params_bitmask; + uint32_t time_offset; + uint32_t low_rssi_offset; + uint32_t bmiss_count_trigger; + uint32_t per_threshold_offset; + uint32_t cache_timeout; + uint32_t max_req_cap; +}; + +/** + * struct wlan_fwol_cfg - fwol config items + * @coex_config: coex config items + * @thermal_temp_cfg: Thermal temperature related config items + * @ie_whitelist_cfg: IE Whitelist related config items + * @neighbor_report_cfg: 11K neighbor report config + * @ani_enabled: ANI enable/disable + * @enable_rts_sifsbursting: Enable RTS SIFS Bursting + * @enable_sifs_burst: Enable SIFS burst + * @max_mpdus_inampdu: Max number of MPDUS + * @enable_phy_reg_retention: Enable PHY reg retention + * @upper_brssi_thresh: Upper BRSSI threshold + * @lower_brssi_thresh: Lower BRSSI threshold + * @enable_dtim_1chrx: Enable/disable DTIM 1 CHRX + * @alternative_chainmask_enabled: Alternate chainmask + * @smart_chainmask_enabled: Enable/disable chainmask + * @get_rts_profile: Set the RTS profile + * @enable_fw_log_level: Set the FW log level + * @enable_fw_log_type: Set the FW log type + * @enable_fw_module_log_level: enable fw module log level + * @enable_fw_module_log_level_num: enable fw module log level num + * @enable_fw_mod_wow_log_level: enable fw wow module log level + * @enable_fw_mod_wow_log_level_num: enable fw wow module log level num + * @sap_xlna_bypass: bypass SAP xLNA + * @is_rate_limit_enabled: Enable/disable RA rate limited + * @tsf_gpio_pin: TSF GPIO Pin config + * @tsf_irq_host_gpio_pin: TSF GPIO Pin config + * @tsf_sync_host_gpio_pin: TSF Sync GPIO Pin config + * @tsf_ptp_options: TSF Plus feature options config + * @lprx_enable: LPRx feature enable config + * @sae_enable: SAE feature enable config + * @gcmp_enable: GCMP feature enable config + * @enable_tx_sch_delay: Enable TX SCH delay value config + * @enable_secondary_rate: Enable secondary retry rate config + * @enable_dhcp_server_offload: DHCP Offload is enabled or not + * @dhcp_max_num_clients: Max number of DHCP client supported + * @dwelltime_params: adaptive dwell time parameters + * @disable_hw_assist: Flag to configure HW assist feature in FW + */ +struct wlan_fwol_cfg { + /* Add CFG and INI items here */ + struct wlan_fwol_coex_config coex_config; + struct wlan_fwol_thermal_temp thermal_temp_cfg; + struct wlan_fwol_ie_whitelist ie_whitelist_cfg; + struct wlan_fwol_neighbor_report_cfg neighbor_report_cfg; + bool ani_enabled; + bool enable_rts_sifsbursting; + uint8_t enable_sifs_burst; + uint8_t max_mpdus_inampdu; + uint8_t enable_phy_reg_retention; + uint16_t upper_brssi_thresh; + uint16_t lower_brssi_thresh; + bool enable_dtim_1chrx; + bool alternative_chainmask_enabled; + bool smart_chainmask_enabled; + uint16_t get_rts_profile; + uint16_t enable_fw_log_level; + uint16_t enable_fw_log_type; + uint8_t enable_fw_module_log_level[FW_MODULE_LOG_LEVEL_STRING_LENGTH]; + uint8_t enable_fw_module_log_level_num; + uint8_t enable_fw_mod_wow_log_level[FW_MODULE_LOG_LEVEL_STRING_LENGTH]; + uint8_t enable_fw_mod_wow_log_level_num; + bool sap_xlna_bypass; +#ifdef FEATURE_WLAN_RA_FILTERING + bool is_rate_limit_enabled; +#endif +#ifdef WLAN_FEATURE_TSF + uint32_t tsf_gpio_pin; +#ifdef WLAN_FEATURE_TSF_PLUS + uint32_t tsf_ptp_options; +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ + uint32_t tsf_irq_host_gpio_pin; +#endif +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC + uint32_t tsf_sync_host_gpio_pin; +#endif +#endif +#endif + bool lprx_enable; +#ifdef WLAN_FEATURE_SAE + bool sae_enable; +#endif + bool gcmp_enable; + uint8_t enable_tx_sch_delay; + uint32_t enable_secondary_rate; +#ifdef DHCP_SERVER_OFFLOAD + bool enable_dhcp_server_offload; + uint32_t dhcp_max_num_clients; +#endif + struct adaptive_dwelltime_params dwelltime_params; + bool disable_hw_assist; +}; + +/** + * struct wlan_fwol_psoc_obj - FW offload psoc priv object + * @cfg: cfg items + * @cbs: callback functions + * @tx_ops: tx operations for target interface + * @rx_ops: rx operations for target interface + */ +struct wlan_fwol_psoc_obj { + struct wlan_fwol_cfg cfg; + struct wlan_fwol_callbacks cbs; + struct wlan_fwol_tx_ops tx_ops; + struct wlan_fwol_rx_ops rx_ops; +}; + +/** + * struct wlan_fwol_rx_event - event from south bound + * @psoc: psoc handle + * @event_id: event ID + * @get_elna_bypass_response: get eLNA bypass response + */ +struct wlan_fwol_rx_event { + struct wlan_objmgr_psoc *psoc; + enum wlan_fwol_southbound_event event_id; + union { +#ifdef WLAN_FEATURE_ELNA + struct get_elna_bypass_response get_elna_bypass_response; +#endif + }; +}; + +/** + * wlan_psoc_get_fwol_obj() - private API to get fwol object from psoc + * @psoc: psoc object + * + * Return: fwol object + */ +struct wlan_fwol_psoc_obj *fwol_get_psoc_obj(struct wlan_objmgr_psoc *psoc); + +/* + * fwol_cfg_on_psoc_enable() - Populate FWOL structure from CFG and INI + * @psoc: pointer to the psoc object + * + * Populate the FWOL CFG structure from CFG and INI values using CFG APIs + * + * Return: QDF_STATUS + */ +QDF_STATUS fwol_cfg_on_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/* + * fwol_cfg_on_psoc_disable() - Clear the CFG structure on psoc disable + * @psoc: pointer to the psoc object + * + * Clear the FWOL CFG structure on psoc disable + * + * Return: QDF_STATUS + */ +QDF_STATUS fwol_cfg_on_psoc_disable(struct wlan_objmgr_psoc *psoc); + +/** + * fwol_process_event() - API to process event from south bound + * @msg: south bound message + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS fwol_process_event(struct scheduler_msg *msg); + +/* + * fwol_release_rx_event() - Release fw offload RX event + * @event: fw offload RX event + * + * Return: none + */ +void fwol_release_rx_event(struct wlan_fwol_rx_event *event); + +/* + * fwol_init_neighbor_report_cfg() - Populate default neighbor report CFG values + * @psoc: pointer to the psoc object + * @fwol_neighbor_report_cfg: Pointer to Neighbor report config data structure + * + * Return: QDF_STATUS + */ +QDF_STATUS fwol_init_neighbor_report_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_neighbor_report_cfg + *fwol_neighbor_report_cfg); + +/** + * wlan_fwol_init_adapt_dwelltime_in_cfg - initialize adaptive dwell time params + * @psoc: Pointer to struct wlan_objmgr_psoc context + * @dwelltime_params: Pointer to dwell time params + * + * This function parses initialize the adaptive dwell params from ini. + * + * Return: QDF_STATUS + */ +QDF_STATUS +fwol_init_adapt_dwelltime_in_cfg( + struct wlan_objmgr_psoc *psoc, + struct adaptive_dwelltime_params *dwelltime_params); + +/** + * fwol_set_adaptive_dwelltime_config - API to set adaptive dwell params config + * + * @adaptive_dwelltime_params: adaptive_dwelltime_params structure + * + * Return: QDF Status + */ +QDF_STATUS +fwol_set_adaptive_dwelltime_config( + struct adaptive_dwelltime_params *dwelltime_params); + +/** + * fwol_configure_hw_assist() - API to configure HW assist feature in FW + * @pdev: pointer to the pdev object + * @disable_he_assist: Flag to enable/disable HW assist feature + * + * Return: QDF_STATUS + */ +QDF_STATUS fwol_configure_hw_assist(struct wlan_objmgr_pdev *pdev, + bool disable_hw_assist); + +#endif diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/core/src/wlan_fw_offload_main.c b/drivers/staging/qcacld-3.0/components/fw_offload/core/src/wlan_fw_offload_main.c new file mode 100644 index 0000000000000000000000000000000000000000..e114752ccb9376deed5f4bdc45bcbe604057a6bb --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/core/src/wlan_fw_offload_main.c @@ -0,0 +1,670 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define internal APIs related to the fwol component + */ + +#include "wlan_fw_offload_main.h" +#include "cds_api.h" +#include "wma.h" +#include "wlan_fwol_tgt_api.h" + +struct wlan_fwol_psoc_obj *fwol_get_psoc_obj(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_FWOL); +} + +/** + * fwol_mpta_helper_config_get: Populate btc_mpta_helper_enable from cfg + * @psoc: The global psoc handler + * @coex_config: The cfg structure + * + * Return: none + */ +#ifdef FEATURE_MPTA_HELPER +static void +fwol_mpta_helper_config_get(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_coex_config *coex_config) +{ + coex_config->btc_mpta_helper_enable = + cfg_get(psoc, CFG_COEX_MPTA_HELPER); +} +#else +static void +fwol_mpta_helper_config_get(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_coex_config *coex_config) +{ +} +#endif + +/** + * fwol_three_way_coex_config_legacy_config_get: Populate + * btc_three_way_coex_config_legacy_enable from cfg + * @psoc: The global psoc handler + * @coex_config: The cfg structure + * + * Return: none + */ +#ifdef FEATURE_COEX_CONFIG +static void +fwol_three_way_coex_config_legacy_config_get( + struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_coex_config *coex_config) +{ + coex_config->btc_three_way_coex_config_legacy_enable = + cfg_get(psoc, CFG_THREE_WAY_COEX_CONFIG_LEGACY); +} +#else +static void +fwol_three_way_coex_config_legacy_config_get( + struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_coex_config *coex_config) +{ +} +#endif + +static void +fwol_init_coex_config_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_coex_config *coex_config) +{ + coex_config->btc_mode = cfg_get(psoc, CFG_BTC_MODE); + coex_config->antenna_isolation = cfg_get(psoc, CFG_ANTENNA_ISOLATION); + coex_config->max_tx_power_for_btc = + cfg_get(psoc, CFG_MAX_TX_POWER_FOR_BTC); + coex_config->wlan_low_rssi_threshold = + cfg_get(psoc, CFG_WLAN_LOW_RSSI_THRESHOLD); + coex_config->bt_low_rssi_threshold = + cfg_get(psoc, CFG_BT_LOW_RSSI_THRESHOLD); + coex_config->bt_interference_low_ll = + cfg_get(psoc, CFG_BT_INTERFERENCE_LOW_LL); + coex_config->bt_interference_low_ul = + cfg_get(psoc, CFG_BT_INTERFERENCE_LOW_UL); + coex_config->bt_interference_medium_ll = + cfg_get(psoc, CFG_BT_INTERFERENCE_MEDIUM_LL); + coex_config->bt_interference_medium_ul = + cfg_get(psoc, CFG_BT_INTERFERENCE_MEDIUM_UL); + coex_config->bt_interference_high_ll = + cfg_get(psoc, CFG_BT_INTERFERENCE_HIGH_LL); + coex_config->bt_interference_high_ul = + cfg_get(psoc, CFG_BT_INTERFERENCE_HIGH_UL); + fwol_mpta_helper_config_get(psoc, coex_config); + coex_config->bt_sco_allow_wlan_2g_scan = + cfg_get(psoc, CFG_BT_SCO_ALLOW_WLAN_2G_SCAN); + fwol_three_way_coex_config_legacy_config_get(psoc, coex_config); +} + +static void +fwol_init_thermal_temp_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_thermal_temp *thermal_temp) +{ + thermal_temp->thermal_temp_min_level[0] = + cfg_get(psoc, CFG_THERMAL_TEMP_MIN_LEVEL0); + thermal_temp->thermal_temp_max_level[0] = + cfg_get(psoc, CFG_THERMAL_TEMP_MAX_LEVEL0); + thermal_temp->thermal_temp_min_level[1] = + cfg_get(psoc, CFG_THERMAL_TEMP_MIN_LEVEL1); + thermal_temp->thermal_temp_max_level[1] = + cfg_get(psoc, CFG_THERMAL_TEMP_MAX_LEVEL1); + thermal_temp->thermal_temp_min_level[2] = + cfg_get(psoc, CFG_THERMAL_TEMP_MIN_LEVEL2); + thermal_temp->thermal_temp_max_level[2] = + cfg_get(psoc, CFG_THERMAL_TEMP_MAX_LEVEL2); + thermal_temp->thermal_temp_min_level[3] = + cfg_get(psoc, CFG_THERMAL_TEMP_MIN_LEVEL3); + thermal_temp->thermal_temp_max_level[3] = + cfg_get(psoc, CFG_THERMAL_TEMP_MAX_LEVEL3); + + thermal_temp->thermal_mitigation_enable = + cfg_get(psoc, CFG_THERMAL_MITIGATION_ENABLE); + thermal_temp->throttle_period = cfg_get(psoc, CFG_THROTTLE_PERIOD); + thermal_temp->thermal_sampling_time = + cfg_get(psoc, CFG_THERMAL_SAMPLING_TIME); + thermal_temp->throttle_dutycycle_level[0] = + cfg_get(psoc, CFG_THROTTLE_DUTY_CYCLE_LEVEL0); + thermal_temp->throttle_dutycycle_level[1] = + cfg_get(psoc, CFG_THROTTLE_DUTY_CYCLE_LEVEL1); + thermal_temp->throttle_dutycycle_level[2] = + cfg_get(psoc, CFG_THROTTLE_DUTY_CYCLE_LEVEL2); + thermal_temp->throttle_dutycycle_level[3] = + cfg_get(psoc, CFG_THROTTLE_DUTY_CYCLE_LEVEL3); + thermal_temp->throttle_dutycycle_level[4] = + cfg_get(psoc, CFG_THROTTLE_DUTY_CYCLE_LEVEL4); + thermal_temp->throttle_dutycycle_level[5] = + cfg_get(psoc, CFG_THROTTLE_DUTY_CYCLE_LEVEL5); +} + +QDF_STATUS fwol_init_neighbor_report_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_neighbor_report_cfg + *fwol_neighbor_report_cfg) +{ + if (!fwol_neighbor_report_cfg) { + fwol_err("Neighbor report config pointer null"); + return QDF_STATUS_E_FAILURE; + } + + fwol_neighbor_report_cfg->enable_bitmask = + cfg_get(psoc, CFG_OFFLOAD_11K_ENABLE_BITMASK); + fwol_neighbor_report_cfg->params_bitmask = + cfg_get(psoc, CFG_OFFLOAD_NEIGHBOR_REPORT_PARAMS_BITMASK); + fwol_neighbor_report_cfg->time_offset = + cfg_get(psoc, CFG_OFFLOAD_NEIGHBOR_REPORT_TIME_OFFSET); + fwol_neighbor_report_cfg->low_rssi_offset = + cfg_get(psoc, CFG_OFFLOAD_NEIGHBOR_REPORT_LOW_RSSI_OFFSET); + fwol_neighbor_report_cfg->bmiss_count_trigger = + cfg_get(psoc, CFG_OFFLOAD_NEIGHBOR_REPORT_BMISS_COUNT_TRIGGER); + fwol_neighbor_report_cfg->per_threshold_offset = + cfg_get(psoc, CFG_OFFLOAD_NEIGHBOR_REPORT_PER_THRESHOLD_OFFSET); + fwol_neighbor_report_cfg->cache_timeout = + cfg_get(psoc, CFG_OFFLOAD_NEIGHBOR_REPORT_CACHE_TIMEOUT); + fwol_neighbor_report_cfg->max_req_cap = + cfg_get(psoc, CFG_OFFLOAD_NEIGHBOR_REPORT_MAX_REQ_CAP); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +fwol_init_adapt_dwelltime_in_cfg( + struct wlan_objmgr_psoc *psoc, + struct adaptive_dwelltime_params *dwelltime_params) +{ + if (!dwelltime_params) { + fwol_err("dwelltime params config pointer null"); + return QDF_STATUS_E_FAILURE; + } + dwelltime_params->is_enabled = + cfg_get(psoc, CFG_ADAPTIVE_DWELL_MODE_ENABLED); + dwelltime_params->dwelltime_mode = + cfg_get(psoc, CFG_GLOBAL_ADAPTIVE_DWELL_MODE); + dwelltime_params->lpf_weight = + cfg_get(psoc, CFG_ADAPT_DWELL_LPF_WEIGHT); + dwelltime_params->passive_mon_intval = + cfg_get(psoc, CFG_ADAPT_DWELL_PASMON_INTVAL); + dwelltime_params->wifi_act_threshold = + cfg_get(psoc, CFG_ADAPT_DWELL_WIFI_THRESH); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +fwol_set_adaptive_dwelltime_config( + struct adaptive_dwelltime_params *dwelltime_params) +{ + tp_wma_handle wma_handle; + QDF_STATUS status; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + fwol_err("wma handle is null"); + return QDF_STATUS_E_FAILURE; + } + status = wma_send_adapt_dwelltime_params(wma_handle, + dwelltime_params); + return status; +} +/** + * fwol_parse_probe_req_ouis - form ouis from ini gProbeReqOUIs + * @psoc: Pointer to struct wlan_objmgr_psoc context + * @whitelist: Pointer to struct wlan_fwol_ie_whitelist + * + * This function parses the ini string gProbeReqOUIs which needs be to in the + * following format: + * "<8 characters of [0-9] or [A-F]>space<8 characters from [0-9] etc.," + * example: "AABBCCDD 1122EEFF" + * and the logic counts the number of OUIS and allocates the memory + * for every valid OUI and is stored in struct hdd_context + * + * Return: None + */ +static void fwol_parse_probe_req_ouis(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_ie_whitelist *whitelist) +{ + uint8_t probe_req_ouis[MAX_PRB_REQ_VENDOR_OUI_INI_LEN] = {0}; + uint32_t *voui = whitelist->probe_req_voui; + char *str; + uint8_t *token; + uint32_t oui_indx = 0; + int ret; + uint32_t hex_value; + + qdf_str_lcopy(probe_req_ouis, cfg_get(psoc, CFG_PROBE_REQ_OUI), + MAX_PRB_REQ_VENDOR_OUI_INI_LEN); + str = probe_req_ouis; + whitelist->no_of_probe_req_ouis = 0; + + if (!qdf_str_len(str)) { + fwol_debug("NO OUIs to parse"); + return; + } + + token = strsep(&str, " "); + while (token) { + if (qdf_str_len(token) != 8) + goto next_token; + + ret = qdf_kstrtouint(token, 16, &hex_value); + if (ret) + goto next_token; + + voui[oui_indx++] = cpu_to_be32(hex_value); + if (oui_indx >= MAX_PROBE_REQ_OUIS) + break; +next_token: + token = strsep(&str, " "); + } + + if (!oui_indx) { + whitelist->ie_whitelist = false; + return; + } + + whitelist->no_of_probe_req_ouis = oui_indx; +} + +/** + * fwol_validate_ie_bitmaps() - Validate all IE whitelist bitmap param values + * @psoc: Pointer to struct wlan_objmgr_psoc + * @whitelist: Pointer to struct wlan_fwol_ie_whitelist + * + * Return: True if all bitmap values are valid, else false + */ +static bool fwol_validate_ie_bitmaps(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_ie_whitelist *whitelist) +{ + if (!(whitelist->ie_bitmap_0 || whitelist->ie_bitmap_1 || + whitelist->ie_bitmap_2 || whitelist->ie_bitmap_3 || + whitelist->ie_bitmap_4 || whitelist->ie_bitmap_5 || + whitelist->ie_bitmap_6 || whitelist->ie_bitmap_7)) + return false; + + /* + * check whether vendor oui IE is set and OUIs are present, each OUI + * is entered in the form of string of 8 characters from ini, therefore, + * for atleast one OUI, minimum length is 8 and hence this string length + * is checked for minimum of 8 + */ + if ((whitelist->ie_bitmap_6 & VENDOR_SPECIFIC_IE_BITMAP) && + (qdf_str_len(cfg_get(psoc, CFG_PROBE_REQ_OUI)) < 8)) + return false; + + /* check whether vendor oui IE is not set but OUIs are present */ + if (!(whitelist->ie_bitmap_6 & VENDOR_SPECIFIC_IE_BITMAP) && + (qdf_str_len(cfg_get(psoc, CFG_PROBE_REQ_OUI)) > 0)) + return false; + + return true; +} + +static void +fwol_init_ie_whiltelist_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_ie_whitelist *whitelist) +{ + whitelist->ie_whitelist = cfg_get(psoc, CFG_PROBE_REQ_IE_WHITELIST); + whitelist->ie_bitmap_0 = cfg_get(psoc, CFG_PROBE_REQ_IE_BIT_MAP0); + whitelist->ie_bitmap_1 = cfg_get(psoc, CFG_PROBE_REQ_IE_BIT_MAP1); + whitelist->ie_bitmap_2 = cfg_get(psoc, CFG_PROBE_REQ_IE_BIT_MAP2); + whitelist->ie_bitmap_3 = cfg_get(psoc, CFG_PROBE_REQ_IE_BIT_MAP3); + whitelist->ie_bitmap_4 = cfg_get(psoc, CFG_PROBE_REQ_IE_BIT_MAP4); + whitelist->ie_bitmap_5 = cfg_get(psoc, CFG_PROBE_REQ_IE_BIT_MAP5); + whitelist->ie_bitmap_6 = cfg_get(psoc, CFG_PROBE_REQ_IE_BIT_MAP6); + whitelist->ie_bitmap_7 = cfg_get(psoc, CFG_PROBE_REQ_IE_BIT_MAP7); + if (!fwol_validate_ie_bitmaps(psoc, whitelist)) + whitelist->ie_whitelist = false; + fwol_parse_probe_req_ouis(psoc, whitelist); +} + +/** + * ucfg_fwol_fetch_dhcp_server_settings: Populate the enable_dhcp_server_offload + * and dhcp_max_num_clients from cfg + * @psoc: The global psoc handler + * @fwol_cfg: The cfg structure + * + * Return: none + */ +#ifdef DHCP_SERVER_OFFLOAD +static void ucfg_fwol_fetch_dhcp_server_settings(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ + fwol_cfg->enable_dhcp_server_offload = + cfg_get(psoc, CFG_DHCP_SERVER_OFFLOAD_SUPPORT); + fwol_cfg->dhcp_max_num_clients = + cfg_get(psoc, CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT); +} +#else +static void ucfg_fwol_fetch_dhcp_server_settings(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ +} +#endif + +/** + * ucfg_fwol_fetch_tsf_gpio_pin: Populate the tsf_gpio_pin from cfg + * @psoc: The global psoc handler + * @fwol_cfg: The cfg structure + * + * Return: none + */ +#ifdef WLAN_FEATURE_TSF +static void ucfg_fwol_fetch_tsf_gpio_pin(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ + fwol_cfg->tsf_gpio_pin = cfg_get(psoc, CFG_SET_TSF_GPIO_PIN); +} +#else +static void ucfg_fwol_fetch_tsf_gpio_pin(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ +} +#endif + +/** + * ucfg_fwol_init_tsf_ptp_options: Populate the tsf_ptp_options from cfg + * @psoc: The global psoc handler + * @fwol_cfg: The cfg structure + * + * Return: none + */ +#if defined(WLAN_FEATURE_TSF) && defined(WLAN_FEATURE_TSF_PLUS) +static void ucfg_fwol_init_tsf_ptp_options(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ + fwol_cfg->tsf_ptp_options = cfg_get(psoc, CFG_SET_TSF_PTP_OPT); +} +#else +static void ucfg_fwol_init_tsf_ptp_options(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ +} +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ +/** + * ucfg_fwol_fetch_tsf_irq_host_gpio_pin: Populate the + * tsf_irq_host_gpio_pin from cfg + * @psoc: The global psoc handler + * @fwol_cfg: The cfg structure + * + * This function is used to populate the cfg value of host platform + * gpio pin configured to receive tsf interrupt from fw. + * + * Return: none + */ +static void +ucfg_fwol_fetch_tsf_irq_host_gpio_pin(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ + fwol_cfg->tsf_irq_host_gpio_pin = + cfg_get(psoc, CFG_SET_TSF_IRQ_HOST_GPIO_PIN); +} +#else +static void +ucfg_fwol_fetch_tsf_irq_host_gpio_pin(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ +} +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC +/** + * ucfg_fwol_fetch_tsf_sync_host_gpio_pin: Populate the + * tsf_sync_host_gpio_pin from cfg + * @psoc: The global psoc handler + * @fwol_cfg: The cfg structure + * + * This function is used to populate the cfg value of host platform + * gpio pin configured to drive tsf sync interrupt pin on wlan chip. + * + * Return: none + */ +static void +ucfg_fwol_fetch_tsf_sync_host_gpio_pin(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ + fwol_cfg->tsf_sync_host_gpio_pin = + cfg_get(psoc, CFG_SET_TSF_SYNC_HOST_GPIO_PIN); +} +#else +static void +ucfg_fwol_fetch_tsf_sync_host_gpio_pin(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ +} +#endif +/** + * ucfg_fwol_init_sae_cfg: Populate the sae control config from cfg + * @psoc: The global psoc handler + * @fwol_cfg: The cfg structure + * + * Return: none + */ +#ifdef WLAN_FEATURE_SAE +static void ucfg_fwol_init_sae_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ + fwol_cfg->sae_enable = cfg_get(psoc, CFG_IS_SAE_ENABLED); +} +#else +static void ucfg_fwol_init_sae_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ +} +#endif + +/** + * ucfg_fwol_fetch_ra_filter: Populate the RA filter enabled or not from cfg + * @psoc: The global psoc handler + * @fwol_cfg: The cfg structure + * + * Return: none + */ +#ifdef FEATURE_WLAN_RA_FILTERING +static void ucfg_fwol_fetch_ra_filter(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ + fwol_cfg->is_rate_limit_enabled = cfg_get(psoc, CFG_RA_FILTER_ENABLE); +} +#else +static void ucfg_fwol_fetch_ra_filter(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_cfg *fwol_cfg) +{ +} +#endif + +QDF_STATUS fwol_cfg_on_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_fwol_psoc_obj *fwol_obj; + struct wlan_fwol_cfg *fwol_cfg; + qdf_size_t enable_fw_module_log_level_num; + qdf_size_t enable_fw_wow_mod_log_level_num; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL Obj"); + return QDF_STATUS_E_FAILURE; + } + + fwol_cfg = &fwol_obj->cfg; + + fwol_init_coex_config_in_cfg(psoc, &fwol_cfg->coex_config); + fwol_init_thermal_temp_in_cfg(psoc, &fwol_cfg->thermal_temp_cfg); + fwol_init_ie_whiltelist_in_cfg(psoc, &fwol_cfg->ie_whitelist_cfg); + fwol_init_neighbor_report_cfg(psoc, &fwol_cfg->neighbor_report_cfg); + fwol_cfg->ani_enabled = cfg_get(psoc, CFG_ENABLE_ANI); + fwol_cfg->enable_rts_sifsbursting = + cfg_get(psoc, CFG_SET_RTS_FOR_SIFS_BURSTING); + fwol_cfg->enable_sifs_burst = cfg_get(psoc, CFG_SET_SIFS_BURST); + fwol_cfg->max_mpdus_inampdu = cfg_get(psoc, CFG_MAX_MPDUS_IN_AMPDU); + fwol_cfg->enable_phy_reg_retention = cfg_get(psoc, CFG_ENABLE_PHY_REG); + fwol_cfg->upper_brssi_thresh = cfg_get(psoc, CFG_UPPER_BRSSI_THRESH); + fwol_cfg->lower_brssi_thresh = cfg_get(psoc, CFG_LOWER_BRSSI_THRESH); + fwol_cfg->enable_dtim_1chrx = cfg_get(psoc, CFG_DTIM_1CHRX_ENABLE); + fwol_cfg->alternative_chainmask_enabled = + cfg_get(psoc, CFG_ENABLE_COEX_ALT_CHAINMASK); + fwol_cfg->smart_chainmask_enabled = + cfg_get(psoc, CFG_ENABLE_SMART_CHAINMASK); + fwol_cfg->get_rts_profile = cfg_get(psoc, CFG_ENABLE_FW_RTS_PROFILE); + fwol_cfg->enable_fw_log_level = + cfg_get(psoc, CFG_ENABLE_FW_DEBUG_LOG_LEVEL); + fwol_cfg->enable_fw_log_type = cfg_get(psoc, CFG_ENABLE_FW_LOG_TYPE); + qdf_uint8_array_parse(cfg_get(psoc, CFG_ENABLE_FW_MODULE_LOG_LEVEL), + fwol_cfg->enable_fw_module_log_level, + FW_MODULE_LOG_LEVEL_STRING_LENGTH, + &enable_fw_module_log_level_num); + fwol_cfg->enable_fw_module_log_level_num = + (uint8_t)enable_fw_module_log_level_num; + qdf_uint8_array_parse(cfg_get(psoc, CFG_ENABLE_FW_WOW_MODULE_LOG_LEVEL), + fwol_cfg->enable_fw_mod_wow_log_level, + FW_MODULE_LOG_LEVEL_STRING_LENGTH, + &enable_fw_wow_mod_log_level_num); + fwol_cfg->enable_fw_mod_wow_log_level_num = + (uint8_t)enable_fw_wow_mod_log_level_num; + ucfg_fwol_init_tsf_ptp_options(psoc, fwol_cfg); + ucfg_fwol_init_sae_cfg(psoc, fwol_cfg); + fwol_cfg->lprx_enable = cfg_get(psoc, CFG_LPRX); + fwol_cfg->gcmp_enable = cfg_get(psoc, CFG_ENABLE_GCMP); + fwol_cfg->enable_tx_sch_delay = cfg_get(psoc, CFG_TX_SCH_DELAY); + fwol_cfg->enable_secondary_rate = cfg_get(psoc, + CFG_ENABLE_SECONDARY_RATE); + fwol_init_adapt_dwelltime_in_cfg(psoc, &fwol_cfg->dwelltime_params); + ucfg_fwol_fetch_ra_filter(psoc, fwol_cfg); + ucfg_fwol_fetch_tsf_gpio_pin(psoc, fwol_cfg); + ucfg_fwol_fetch_tsf_irq_host_gpio_pin(psoc, fwol_cfg); + ucfg_fwol_fetch_tsf_sync_host_gpio_pin(psoc, fwol_cfg); + ucfg_fwol_fetch_dhcp_server_settings(psoc, fwol_cfg); + fwol_cfg->sap_xlna_bypass = cfg_get(psoc, CFG_SET_SAP_XLNA_BYPASS); + fwol_cfg->disable_hw_assist = cfg_get(psoc, CFG_DISABLE_HW_ASSIST); + + return status; +} + +QDF_STATUS fwol_cfg_on_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + /* Clear the CFG structure */ + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_ELNA +/** + * fwol_process_get_elna_bypass_resp() - Process get eLNA bypass response + * @event: response event + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +fwol_process_get_elna_bypass_resp(struct wlan_fwol_rx_event *event) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_objmgr_psoc *psoc; + struct wlan_fwol_psoc_obj *fwol_obj; + struct wlan_fwol_callbacks *cbs; + struct get_elna_bypass_response *resp; + + if (!event) { + fwol_err("Event buffer is NULL"); + return QDF_STATUS_E_FAILURE; + } + + psoc = event->psoc; + if (!psoc) { + fwol_err("psoc is NULL"); + return QDF_STATUS_E_INVAL; + } + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL Obj"); + return QDF_STATUS_E_INVAL; + } + + cbs = &fwol_obj->cbs; + if (cbs->get_elna_bypass_callback) { + resp = &event->get_elna_bypass_response; + cbs->get_elna_bypass_callback(cbs->get_elna_bypass_context, + resp); + } else { + fwol_err("NULL pointer for callback"); + status = QDF_STATUS_E_IO; + } + + return status; +} +#else +static QDF_STATUS +fwol_process_get_elna_bypass_resp(struct wlan_fwol_rx_event *event) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_ELNA */ + +QDF_STATUS fwol_process_event(struct scheduler_msg *msg) +{ + QDF_STATUS status; + struct wlan_fwol_rx_event *event; + + fwol_debug("msg type %d", msg->type); + + if (!(msg->bodyptr)) { + fwol_err("Invalid message body"); + return QDF_STATUS_E_INVAL; + } + + event = msg->bodyptr; + msg->bodyptr = NULL; + + switch (msg->type) { + case WLAN_FWOL_EVT_GET_ELNA_BYPASS_RESPONSE: + status = fwol_process_get_elna_bypass_resp(event); + break; + default: + status = QDF_STATUS_E_INVAL; + break; + } + + fwol_release_rx_event(event); + + return status; +} + +void fwol_release_rx_event(struct wlan_fwol_rx_event *event) +{ + if (!event) { + fwol_err("event is NULL"); + return; + } + + if (event->psoc) + wlan_objmgr_psoc_release_ref(event->psoc, WLAN_FWOL_SB_ID); + qdf_mem_free(event); +} + +QDF_STATUS fwol_configure_hw_assist(struct wlan_objmgr_pdev *pdev, + bool disable_hw_assist) +{ + QDF_STATUS status; + struct pdev_params pdev_param; + + pdev_param.param_id = WMI_PDEV_PARAM_DISABLE_HW_ASSIST; + pdev_param.param_value = disable_hw_assist; + + status = tgt_fwol_pdev_param_send(pdev, pdev_param); + if (QDF_IS_STATUS_ERROR(status)) + fwol_err("WMI_PDEV_PARAM_DISABLE_HW_ASSIST failed %d", status); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_adaptive_dwelltime.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_adaptive_dwelltime.h new file mode 100644 index 0000000000000000000000000000000000000000..9943b1fdb811daf04b4e33aabc2a44498a312e3e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_adaptive_dwelltime.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains adaptive dwell components. + */ + +#ifndef __CFG_ADAPTIVE_DWELLTIME_H +#define __CFG_ADAPTIVE_DWELLTIME_H + +/* + * + * adaptive_dwell_mode_enabled - enable/disable the adaptive dwell config. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * + * This ini will globally disable/enable the adaptive dwell config. + * Following parameters will set different values of attributes for dwell + * time optimization thus reducing total scan time. + * Acceptable values for this: + * 0: Config is disabled + * 1: Config is enabled + * + * Related: None. + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_ADAPTIVE_DWELL_MODE_ENABLED CFG_INI_BOOL(\ + "adaptive_dwell_mode_enabled",\ + 1, \ + "enable the adaptive dwell config") + +/* + * + * global_adapt_dwelltime_mode - set default adaptive mode. + * @Min: 0 + * @Max: 4 + * @Default: 0 + * + * This ini will set default adaptive mode, will be used if any of the + * scan dwell mode is set to default. + * For uses : see enum scan_dwelltime_adaptive_mode + * + * Related: None. + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_GLOBAL_ADAPTIVE_DWELL_MODE CFG_INI_UINT(\ + "global_adapt_dwelltime_mode",\ + 0, 4, 0,\ + CFG_VALUE_OR_DEFAULT, \ + "set default adaptive mode") + +/* + * + * adapt_dwell_lpf_weight - weight to caclulate avg low pass filter. + * @Min: 0 + * @Max: 100 + * @Default: 80 + * + * This ini is used to set the weight to calculate + * the average low pass filter for channel congestion. + * Acceptable values for this: 0-100 (In %) + * + * Related: None. + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_ADAPT_DWELL_LPF_WEIGHT CFG_INI_UINT(\ + "adapt_dwell_lpf_weight",\ + 0, 100, 80,\ + CFG_VALUE_OR_DEFAULT, \ + "weight to calc avg low pass filter") + +/* + * + * adapt_dwell_passive_mon_intval - Interval to monitor passive scan in msec. + * @Min: 0 + * @Max: 25 + * @Default: 10 + * + * This ini is used to set interval to monitor wifi + * activity in passive scan in milliseconds. + * + * Related: None. + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_ADAPT_DWELL_PASMON_INTVAL CFG_INI_UINT(\ + "adapt_dwell_passive_mon_intval",\ + 0, 25, 10,\ + CFG_VALUE_OR_DEFAULT, \ + "interval to monitor passive scan") + +/* + * + * adapt_dwell_wifi_act_threshold - % of wifi activity used in passive scan + * @Min: 0 + * @Max: 100 + * @Default: 10 + * + * This ini is used to set % of wifi activity used in passive scan + * Acceptable values for this: 0-100 (in %) + * + * Related: None. + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_ADAPT_DWELL_WIFI_THRESH CFG_INI_UINT(\ + "adapt_dwell_wifi_act_threshold",\ + 0, 100, 10,\ + CFG_VALUE_OR_DEFAULT, \ + "percent of wifi activity in pas scan") + +#define CFG_ADAPTIVE_DWELLTIME_ALL \ + CFG(CFG_ADAPTIVE_DWELL_MODE_ENABLED) \ + CFG(CFG_GLOBAL_ADAPTIVE_DWELL_MODE) \ + CFG(CFG_ADAPT_DWELL_LPF_WEIGHT) \ + CFG(CFG_ADAPT_DWELL_PASMON_INTVAL) \ + CFG(CFG_ADAPT_DWELL_WIFI_THRESH) + +#endif + diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_coex.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_coex.h new file mode 100644 index 0000000000000000000000000000000000000000..c3b665b3f06a0fbfacc9d3c4fbe7306d9f12c619 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_coex.h @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2012 - 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_COEX_H +#define __CFG_COEX_H + +/* + * + * gSetBTCMode - Config BTC mode + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * 0 - TDD + * 1 - FDD + * 2 - Hybrid + * + * Usage: External + * + * + */ +#define CFG_BTC_MODE CFG_INI_UINT( \ + "gSetBTCMode", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "BTC mode") + +/* + * + * gSetAntennaIsolation - Set Antenna Isolation + * @Min: 0 + * @Max: 255 + * @Default: 25 + * + * Usage: External + * + * + */ +#define CFG_ANTENNA_ISOLATION CFG_INI_UINT( \ + "gSetAntennaIsolation", \ + 0, \ + 255, \ + 25, \ + CFG_VALUE_OR_DEFAULT, \ + "Antenna Isolation") + +/* + * + * gSetMaxTxPowerForBTC - Set Max WLAN Tx power in COEX scenario + * @Min: 0 + * @Max: 100 + * @Default: 100 + * + * Usage: External + * + * + */ +#define CFG_MAX_TX_POWER_FOR_BTC CFG_INI_UINT( \ + "gSetMaxTxPowerForBTC", \ + 0, \ + 100, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "Max Tx Power for BTC") + +/* + * + * gSetWlanLowRssiThreshold - Set WLAN low RSSI threshold for BTC mode switching + * @Min: -100 + * @Max: 0 + * @Default: -80 + * + * Usage: External + * + * + */ +#define CFG_WLAN_LOW_RSSI_THRESHOLD CFG_INI_INT( \ + "gSetWlanLowRssiThreshold", \ + -100, \ + 0, \ + -80, \ + CFG_VALUE_OR_DEFAULT, \ + "WLAN Low RSSI Threshold") + +/* + * + * gSetBtLowRssiThreshold - Set BT low RSSI threshold for BTC mode switching + * @Min: -100 + * @Max: 0 + * @Default: -80 + * + * Usage: External + * + * + */ +#define CFG_BT_LOW_RSSI_THRESHOLD CFG_INI_INT( \ + "gSetBtLowRssiThreshold", \ + -100, \ + 0, \ + -80, \ + CFG_VALUE_OR_DEFAULT, \ + "BT Low RSSI Threshold") + +/* + * + * gSetBtInterferenceLowLL - Set lower limit of low level BT interference + * @Min: -100 + * @Max: 100 + * @Default: -25 + * + * Usage: External + * + * + */ +#define CFG_BT_INTERFERENCE_LOW_LL CFG_INI_INT( \ + "gSetBtInterferenceLowLL", \ + -100, \ + 100, \ + -25, \ + CFG_VALUE_OR_DEFAULT, \ + "BT Interference Low LL") + +/* + * + * gSetBtInterferenceLowUL - Set upper limit of low level BT interference + * @Min: -100 + * @Max: 100 + * @Default: -21 + * + * Usage: External + * + * + */ +#define CFG_BT_INTERFERENCE_LOW_UL CFG_INI_INT( \ + "gSetBtInterferenceLowUL", \ + -100, \ + 100, \ + -21, \ + CFG_VALUE_OR_DEFAULT, \ + "BT Interference Low UL") + +/* + * + * gSetBtInterferenceMediumLL - Set lower limit of medium level BT interference + * @Min: -100 + * @Max: 100 + * @Default: -20 + * + * Usage: External + * + * + */ +#define CFG_BT_INTERFERENCE_MEDIUM_LL CFG_INI_INT( \ + "gSetBtInterferenceMediumLL", \ + -100, \ + 100, \ + -20, \ + CFG_VALUE_OR_DEFAULT, \ + "BT Interference Medium LL") + +/* + * + * gSetBtInterferenceMediumUL - Set upper limit of medium level BT interference + * @Min: -100 + * @Max: 100 + * @Default: -16 + * + * Usage: External + * + * + */ +#define CFG_BT_INTERFERENCE_MEDIUM_UL CFG_INI_INT( \ + "gSetBtInterferenceMediumUL", \ + -100, \ + 100, \ + -16, \ + CFG_VALUE_OR_DEFAULT, \ + "BT Interference Medium UL") + +/* + * + * gSetBtInterferenceHighLL - Set lower limit of high level BT interference + * @Min: -100 + * @Max: 100 + * @Default: -15 + * + * Usage: External + * + * + */ +#define CFG_BT_INTERFERENCE_HIGH_LL CFG_INI_INT( \ + "gSetBtInterferenceHighLL", \ + -100, \ + 100, \ + -15, \ + CFG_VALUE_OR_DEFAULT, \ + "BT Interference High LL") + +/* + * + * gSetBtInterferenceHighUL - Set upper limit of high level BT interference + * @Min: -100 + * @Max: 100 + * @Default: -11 + * + * Usage: External + * + * + */ +#define CFG_BT_INTERFERENCE_HIGH_UL CFG_INI_INT( \ + "gSetBtInterferenceHighUL", \ + -100, \ + 100, \ + -11, \ + CFG_VALUE_OR_DEFAULT, \ + "BT Interference High UL") + +#ifdef FEATURE_MPTA_HELPER +/* + * + * gMPTAHelperEnable - Enable MPTA Helper + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable or disable coex MPTA Helper. + * + * Usage: External + * + * + */ +#define CFG_COEX_MPTA_HELPER CFG_INI_BOOL( \ + "gMPTAHelperEnable", \ + 0, \ + "Enable/Disable MPTA Helper") + +#define COEX_MPTA_HELPER_CFG CFG(CFG_COEX_MPTA_HELPER) +#else +#define COEX_MPTA_HELPER_CFG +#endif + +/* + * + * gBtScoAllowWlan2GScan - Allow wlan 2g scan when BT SCO connection is on + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * 0 - Disable + * 1 - Enable + * + * This ini is used to enable or disable wlan 2g scan + * when BT SCO connection is on. + * + * Usage: External + * + * + */ +#define CFG_BT_SCO_ALLOW_WLAN_2G_SCAN CFG_INI_BOOL( \ + "gBtScoAllowWlan2GScan", \ + 1, \ + "Bt Sco Allow Wlan 2G Scan") + +#ifdef FEATURE_COEX_CONFIG +/* + * + * gThreeWayCoexConfigLegacyEnable - Enable coex config legacy feature + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable or disable three way coex config legacy feature. + * This feature is designed only for non-mobile solution. + * When the feature is disabled, Firmware use the default configuration to + * set the coex priority of three antenna(WLAN, BT, ZIGBEE). + * when enable this feature, customer can use the vendor command to set antenna + * coex priority dynamically. + * + * Supported Feature: three way coex config + * + * Usage: External + * + * + */ +#define CFG_THREE_WAY_COEX_CONFIG_LEGACY CFG_INI_BOOL( \ + "gThreeWayCoexConfigLegacyEnable", \ + 0, \ + "Enable/Disable COEX Config Legacy") + +#define THREE_WAY_COEX_CONFIG_LEGACY_CFG CFG(CFG_THREE_WAY_COEX_CONFIG_LEGACY) +#else +#define THREE_WAY_COEX_CONFIG_LEGACY_CFG +#endif + +#define CFG_COEX_ALL \ + CFG(CFG_BTC_MODE) \ + CFG(CFG_ANTENNA_ISOLATION) \ + CFG(CFG_MAX_TX_POWER_FOR_BTC) \ + CFG(CFG_WLAN_LOW_RSSI_THRESHOLD) \ + CFG(CFG_BT_LOW_RSSI_THRESHOLD) \ + CFG(CFG_BT_INTERFERENCE_LOW_LL) \ + CFG(CFG_BT_INTERFERENCE_LOW_UL) \ + CFG(CFG_BT_INTERFERENCE_MEDIUM_LL) \ + CFG(CFG_BT_INTERFERENCE_MEDIUM_UL) \ + CFG(CFG_BT_INTERFERENCE_HIGH_LL) \ + CFG(CFG_BT_INTERFERENCE_HIGH_UL) \ + COEX_MPTA_HELPER_CFG \ + CFG(CFG_BT_SCO_ALLOW_WLAN_2G_SCAN) \ + THREE_WAY_COEX_CONFIG_LEGACY_CFG +#endif diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_fwol.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_fwol.h new file mode 100644 index 0000000000000000000000000000000000000000..245d3d75c9cf77d4984139f91556c98ab3d7096d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_fwol.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012 - 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CFG_FWOL_H +#define __CFG_FWOL_H + +#include "cfg_define.h" +#include "cfg_converged.h" +#include "qdf_types.h" +#include "cfg_coex.h" +#include "cfg_thermal_temp.h" +#include "cfg_ie_whitelist.h" +#include "cfg_fwol_generic.h" +#include "cfg_neighbor_roam.h" +#include "cfg_adaptive_dwelltime.h" + +#ifdef WLAN_FW_OFFLOAD +#define CFG_FWOL_ALL \ + CFG_ADAPTIVE_DWELLTIME_ALL \ + CFG_11K_ALL \ + CFG_COEX_ALL \ + CFG_FWOL_GENERIC_ALL \ + CFG_IE_WHITELIST \ + CFG_THERMAL_TEMP_ALL +#else +#define CFG_FWOL_ALL +#endif + +#endif /* __CFG_FWOL_H */ + diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_fwol_generic.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_fwol_generic.h new file mode 100644 index 0000000000000000000000000000000000000000..b4dc29f33b7eec9235a2c2727c9611aab4091406 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_fwol_generic.h @@ -0,0 +1,820 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_FWOL_GENERIC_H +#define __CFG_FWOL_GENERIC_H + + +/* + * + * + * gEnableANI - Enable Adaptive Noise Immunity + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable or disable Adaptive Noise Immunity. + * + * Related: None + * + * Supported Feature: ANI + * + * Usage: External + * + * + */ +#define CFG_ENABLE_ANI CFG_INI_BOOL( \ + "gEnableANI", \ + 1, \ + "Enable/Disable Adaptive Noise Immunity") + +/* + * + * gSetRTSForSIFSBursting - set rts for sifs bursting + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini set rts for sifs bursting + * + * Usage: External + * + * + */ +#define CFG_SET_RTS_FOR_SIFS_BURSTING CFG_INI_BOOL( \ + "gSetRTSForSIFSBursting", \ + 0, \ + "Set rts for sifs bursting") + +/* + * + * sifs_burst_mask - Set sifs burst mask + * @Min: 0 + * @Max: 3 + * @Default: 1 + * + * This ini is used to set 11n and legacy(non 11n/wmm) + * sifs burst. Especially under running multi stream + * traffic test case, it can be useful to let the low + * priority AC, or legacy mode device, or the specified + * AC to aggressively contend air medium, then have a + * obvious improvement of throughput. Bit0 is the switch + * of sifs burst, it must be set if want to enable sifs + * burst, Bit1 is for legacy mode. + * Supported configuration: + * 0: disabled + * 1: enabled, but disabled for legacy mode + * 3: all enabled + * + * Usage: External + * + * + */ +#define CFG_SET_SIFS_BURST CFG_INI_UINT( \ + "sifs_burst_mask", \ + 0, \ + 3, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Set SIFS burst mask") + +/* + * + * gMaxMPDUsInAMPDU - max mpdus in ampdu + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * This ini configure max mpdus in ampdu + * + * Usage: External + * + * + */ +#define CFG_MAX_MPDUS_IN_AMPDU CFG_INI_INT( \ + "gMaxMPDUsInAMPDU", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "This ini configure max mpdus in ampdu") + +/* + * + * gEnableFastPwrTransition - Configuration for fast power transition + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini supported values: + * 0x0: Phy register retention disabled (Higher timeline, Good for power) + * 0x1: Phy register retention statically enabled + * 0x2: Phy register retention enabled/disabled dynamically + * + * Usage: Internal + * + * + */ +#define CFG_ENABLE_PHY_REG CFG_INI_UINT( \ + "gEnableFastPwrTransition", \ + 0x0, \ + 0x2, \ + 0x0, \ + CFG_VALUE_OR_DEFAULT, \ + "Configuration for fast power transition") + +/* + * + * gUpperBrssiThresh - Sets Upper threshold for beacon RSSI + * @Min: 36 + * @Max: 66 + * @Default: 46 + * + * This ini sets Upper beacon threshold for beacon RSSI in FW + * Used to reduced RX chainmask in FW, once this threshold is + * reached FW will switch to 1X1 (Single chain). + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_UPPER_BRSSI_THRESH CFG_INI_UINT( \ + "gUpperBrssiThresh", \ + 36, \ + 66, \ + 46, \ + CFG_VALUE_OR_DEFAULT, \ + "Sets Upper threshold for beacon RSSI") + +/* + * + * gLowerBrssiThresh - Sets Lower threshold for beacon RSSI + * @Min: 6 + * @Max: 36 + * @Default: 26 + * + * This ini sets Lower beacon threshold for beacon RSSI in FW + * Used to increase RX chainmask in FW, once this threshold is + * reached FW will switch to 2X2 chain. + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_LOWER_BRSSI_THRESH CFG_INI_UINT( \ + "gLowerBrssiThresh", \ + 6, \ + 36, \ + 26, \ + CFG_VALUE_OR_DEFAULT, \ + "Sets Lower threshold for beacon RSSI") + +/* + * + * gDtim1ChRxEnable - Enable/Disable DTIM 1Chrx feature + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini Enables or Disables DTIM 1CHRX feature in FW + * If this flag is set FW enables shutting off one chain + * while going to power save. + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_DTIM_1CHRX_ENABLE CFG_INI_BOOL( \ + "gDtim1ChRxEnable", \ + 1, \ + "Enable/Disable DTIM 1Chrx feature") + +/* + * + * gEnableAlternativeChainmask - Enable Co-Ex Alternative Chainmask + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable the Co-ex Alternative Chainmask + * feature via the WMI_PDEV_PARAM_ALTERNATIVE_CHAINMASK_SCHEME + * firmware parameter. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_COEX_ALT_CHAINMASK CFG_INI_BOOL( \ + "gEnableAlternativeChainmask", \ + 0, \ + "Enable Co-Ex Alternative Chainmask") + +/* + * + * gEnableSmartChainmask - Enable Smart Chainmask + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable the Smart Chainmask feature via + * the WMI_PDEV_PARAM_SMART_CHAINMASK_SCHEME firmware parameter. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_SMART_CHAINMASK CFG_INI_BOOL( \ + "gEnableSmartChainmask", \ + 0, \ + "Enable/disable the Smart Chainmask feature") + +/* + * + * gEnableRTSProfiles - It will use configuring different RTS profiles + * @Min: 0 + * @Max: 66 + * @Default: 33 + * + * This ini used for configuring different RTS profiles + * to firmware. + * Following are the valid values for the rts profile: + * RTSCTS_DISABLED 0 + * NOT_ALLOWED 1 + * NOT_ALLOWED 2 + * RTSCTS_DISABLED 16 + * RTSCTS_ENABLED_4_SECOND_RATESERIES 17 + * CTS2SELF_ENABLED_4_SECOND_RATESERIES 18 + * RTSCTS_DISABLED 32 + * RTSCTS_ENABLED_4_SWRETRIES 33 + * CTS2SELF_ENABLED_4_SWRETRIES 34 + * NOT_ALLOWED 48 + * NOT_ALLOWED 49 + * NOT_ALLOWED 50 + * RTSCTS_DISABLED 64 + * RTSCTS_ENABLED_4_ALL_RATESERIES 65 + * CTS2SELF_ENABLED_4_ALL_RATESERIES 66 + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_FW_RTS_PROFILE CFG_INI_INT( \ + "gEnableRTSProfiles", \ + 0, \ + 66, \ + 33, \ + CFG_VALUE_OR_DEFAULT, \ + "It is used to configure different RTS profiles") + +/* + * gFwDebugLogLevel - Firmware debug log level + * @Min: 0 + * @Max: 255 + * @Default: 3 + * + * This option controls the level of firmware debug log. Default value is + * DBGLOG_WARN, which is to enable error and warning logs. + * + * Related: None + * + * Supported Features: Debugging + * + * Usage: Internal + * + * + */ +#define CFG_ENABLE_FW_DEBUG_LOG_LEVEL CFG_INI_INT( \ + "gFwDebugLogLevel", \ + 0, \ + 255, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "enable error and warning logs by default") + +/* + * gFwDebugLogType - Firmware debug log type + * @Min: 0 + * @Max: 255 + * @Default: 3 + * + * This option controls how driver is to give the firmware logs to net link + * when cnss_diag service is started. + * + * Related: None + * + * Supported Features: Debugging + * + * Usage: Internal + * + * + */ +#define CFG_ENABLE_FW_LOG_TYPE CFG_INI_INT( \ + "gFwDebugLogType", \ + 0, \ + 255, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Default value to be given to the net link cnss_diag service") + +/* + * + * gFwDebugModuleLoglevel - modulized firmware debug log level + * @Min: N/A + * @Max: N/A + * @Default: N/A + * + * This ini is used to set modulized firmware debug log level. + * FW module log level input string format looks like below: + * gFwDebugModuleLoglevel=",,..." + * For example: + * gFwDebugModuleLoglevel="1,0,2,1,3,2,4,3,5,4,6,5,7,6" + * The above input string means: + * For FW module ID 1 enable log level 0 + * For FW module ID 2 enable log level 1 + * For FW module ID 3 enable log level 2 + * For FW module ID 4 enable log level 3 + * For FW module ID 5 enable log level 4 + * For FW module ID 6 enable log level 5 + * For FW module ID 7 enable log level 6 + * For valid values of log levels check enum DBGLOG_LOG_LVL and + * for valid values of module ids check enum WLAN_MODULE_ID. + * + * Related: None + * + * Supported Feature: Debugging + * + * Usage: Internal/External + * + * + */ + +#define FW_MODULE_LOG_LEVEL_STRING_LENGTH (512) +#define CFG_ENABLE_FW_MODULE_LOG_LEVEL CFG_INI_STRING( \ + "gFwDebugModuleLoglevel", \ + 0, \ + FW_MODULE_LOG_LEVEL_STRING_LENGTH, \ + "1,1,2,1,3,1,4,1,5,1,8,1,9,1,13,1,14,1,17,1,18,1,19,1,22,1,26,1,28,1," \ + "29,1,31,1,36,1,38,1,46,1,47,1,50,1,52,1,53,1,56,1,60,1,61,1", \ + "Set modulized firmware debug log level") + +/* + * + * gFwDebugWowModuleLoglevel - modulized firmware wow debug log level + * @Min: N/A + * @Max: N/A + * @Default: N/A + * + * This ini is used to set modulized firmware wow debug log level. + * FW module log level input string format looks like below: + * gFwDebugWowModuleLoglevel=",,..." + * For example: + * gFwDebugWowModuleLoglevel="1,0,2,1,3,2,4,3,5,4,6,5,7,6" + * The above input string means: + * For FW module ID 1 enable log level 0 + * For FW module ID 2 enable log level 1 + * For FW module ID 3 enable log level 2 + * For FW module ID 4 enable log level 3 + * For FW module ID 5 enable log level 4 + * For FW module ID 6 enable log level 5 + * For FW module ID 7 enable log level 6 + * For valid values of log levels check enum DBGLOG_LOG_LVL and + * for valid values of module ids check enum WLAN_MODULE_ID. + * + * Related: None + * + * Supported Feature: Debugging + * + * Usage: External + * + * + */ +#define CFG_ENABLE_FW_WOW_MODULE_LOG_LEVEL CFG_INI_STRING( \ + "gFwDebugWowModuleLoglevel", \ + 0, \ + FW_MODULE_LOG_LEVEL_STRING_LENGTH, \ + "5,3,18,3,31,3,36,3", \ + "Set modulized firmware wow debug log level") + +#ifdef FEATURE_WLAN_RA_FILTERING +/* + * gRAFilterEnable + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Related: None + * + * Usage: Internal + * + * + */ +#define CFG_RA_FILTER_ENABLE CFG_INI_BOOL( \ + "gRAFilterEnable", \ + 1, \ + "Enable RA Filter") +#else +#define CFG_RA_FILTER_ENABLE +#endif + +/* + * gtsf_gpio_pin + * @Min: 0 + * @Max: 254 + * @Default: 255 + * + * GPIO pin to toggle when capture tsf + * + * Related: None + * + * Usage: Internal + * + * + */ +#define CFG_SET_TSF_GPIO_PIN CFG_INI_INT( \ + "gtsf_gpio_pin", \ + 0, \ + 255, \ + 255, \ + CFG_VALUE_OR_DEFAULT, \ + "GPIO pin to toggle when capture tsf") + +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ +/* + * gtsf_irq_host_gpio_pin + * @Min: 0 + * @Max: 254 + * @Default: 255 + * + * TSF irq GPIO pin of host platform + * + * Related: None + * + * Usage: Internal + * + * + */ +#define CFG_SET_TSF_IRQ_HOST_GPIO_PIN CFG_INI_INT( \ + "gtsf_irq_host_gpio_pin", \ + 0, \ + 255, \ + 255, \ + CFG_VALUE_OR_DEFAULT, \ + "TSF irq GPIO pin of host platform") + +#define __CFG_SET_TSF_IRQ_HOST_GPIO_PIN CFG(CFG_SET_TSF_IRQ_HOST_GPIO_PIN) +#else +#define __CFG_SET_TSF_IRQ_HOST_GPIO_PIN +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC +/* + * + * gtsf_sync_host_gpio_pin + * @Min: 0 + * @Max: 254 + * @Default: 255 + * + * TSF sync GPIO pin of host platform + * + * The driver will use this gpio on host platform + * to drive the TSF sync pin on wlan chip. + * Toggling this gpio will generate a strobe to fw + * for latching TSF. + * + * Related: None + * + * Usage: External + * + * + */ +#define CFG_SET_TSF_SYNC_HOST_GPIO_PIN CFG_INI_UINT( \ + "gtsf_sync_host_gpio_pin", \ + 0, \ + 254, \ + 255, \ + CFG_VALUE_OR_DEFAULT, \ + "TSF sync GPIO pin of host platform") + +#define __CFG_SET_TSF_SYNC_HOST_GPIO_PIN CFG(CFG_SET_TSF_SYNC_HOST_GPIO_PIN) +#else +#define __CFG_SET_TSF_SYNC_HOST_GPIO_PIN +#endif + +#if defined(WLAN_FEATURE_TSF) && defined(WLAN_FEATURE_TSF_PLUS) +/* + * gtsf_ptp_options: TSF Plus feature options + * @Min: 0 + * @Max: 0xff + * @Default: 0xf + * + * CFG_SET_TSF_PTP_OPT_RX (0x1) + * CFG_SET_TSF_PTP_OPT_TX (0x2) + * CFG_SET_TSF_PTP_OPT_RAW (0x4) + * CFG_SET_TSF_DBG_FS (0x8) + * CFG_SET_TSF_PTP_OPT_TSF64_TX (0x10) + * + * Related: None + * + * Usage: Internal + * + * + */ +#define CFG_SET_TSF_PTP_OPT_RX (0x1) +#define CFG_SET_TSF_PTP_OPT_TX (0x2) +#define CFG_SET_TSF_PTP_OPT_RAW (0x4) +#define CFG_SET_TSF_DBG_FS (0x8) +#define CFG_SET_TSF_PTP_OPT_TSF64_TX (0x10) + +#define CFG_SET_TSF_PTP_OPT CFG_INI_UINT( \ + "gtsf_ptp_options", \ + 0, \ + 0xff, \ + 0xf, \ + CFG_VALUE_OR_DEFAULT, \ + "TSF Plus feature options") + +#define __CFG_SET_TSF_PTP_OPT CFG(CFG_SET_TSF_PTP_OPT) +#else +#define __CFG_SET_TSF_PTP_OPT +#endif + +#ifdef DHCP_SERVER_OFFLOAD +/* + * gDHCPServerOffloadEnable + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * DHCP Server offload support + * + * Related: None + * + * Usage: Internal + * + * + */ +#define CFG_DHCP_SERVER_OFFLOAD_SUPPORT CFG_INI_BOOL( \ + "gDHCPServerOffloadEnable", \ + 0, \ + "DHCP Server offload support") + +/* + * gDHCPMaxNumClients + * @Min: 1 + * @Max: 8 + * @Default: 8 + * + * Number of DHCP server offload clients + * + * Related: None + * + * Usage: Internal + * + * + */ +#define CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT CFG_INI_INT( \ + "gDHCPMaxNumClients", \ + 1, \ + 8, \ + 8, \ + CFG_VALUE_OR_DEFAULT, \ + "Number of DHCP server offload clients") + +#define CFG_FWOL_DHCP \ + CFG(CFG_DHCP_SERVER_OFFLOAD_SUPPORT) \ + CFG(CFG_DHCP_SERVER_OFFLOAD_NUM_CLIENT) + +#else +#define CFG_FWOL_DHCP +#endif + +/* + * + * gEnableLPRx - Enable/Disable LPRx + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini Enables or disables the LPRx in FW + * + * Usage: External + * + * + */ + +#define CFG_LPRX CFG_INI_BOOL( \ + "gEnableLPRx", \ + 1, \ + "LPRx control") + +#ifdef WLAN_FEATURE_SAE +/* + * + * sae_enabled - Enable/Disable SAE support in driver + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable SAE support in driver + * Driver will update config to supplicant based on this config. + * + * Related: None + * + * Supported Feature: SAE + * Usage: External + * + * + */ + +#define CFG_IS_SAE_ENABLED CFG_INI_BOOL( \ + "sae_enabled", \ + 1, \ + "SAE feature control") +#define __CFG_IS_SAE_ENABLED CFG(CFG_IS_SAE_ENABLED) +#else +#define __CFG_IS_SAE_ENABLED +#endif + +/* + * + * gcmp_enabled - ini to enable/disable GCMP + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Currently Firmware update the sequence number for each TID with 2^3 + * because of security issues. But with this PN mechanism, throughput drop + * is observed. With this ini FW takes the decision to trade off between + * security and throughput + * + * Supported Feature: STA/SAP/P2P + * + * Usage: External + * + * + */ + +#define CFG_ENABLE_GCMP CFG_INI_BOOL( \ + "gcmp_enabled", \ + 1, \ + "GCMP Feature control param") + +/* + * + * gTxSchDelay - Enable/Disable Tx sch delay + * @Min: 0 + * @Max: 5 + * @Default: 0 + * + * Usage: Internal/External + * + * + */ +#define CFG_TX_SCH_DELAY CFG_INI_UINT( \ + "gTxSchDelay", \ + 0, \ + 5, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable/Disable Tx sch delay") + +/* + * + * gEnableSecondaryRate - Enable/Disable Secondary Retry Rate feature subset + * + * @Min: 0x0 + * @Max: 0x3F + * @Default: 0x17 + * + * It is a 32 bit value such that the various bits represent as below - + * Bit-0 : is Enable/Disable Control for "PPDU Secondary Retry Support" + * Bit-1 : is Enable/Disable Control for "RTS Black/White-listing Support" + * Bit-2 : is Enable/Disable Control for "Higher MCS retry restriction + * on XRETRY failures" + * Bit 3-5 : is "Xretry threshold" to use + * Bit 3~31 : reserved for future use. + * + * Usage: External + * + * + */ +#define CFG_ENABLE_SECONDARY_RATE CFG_INI_UINT( \ + "gEnableSecondaryRate", \ + 0, \ + 0x3f, \ + 0x17, \ + CFG_VALUE_OR_DEFAULT, \ + "Secondary Retry Rate feature subset control") + +/* + * + * sap_xlna_bypass - Enable/Disable xLNA bypass + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable SAP xLNA bypass in the FW + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: Internal + * + * + */ + +#define CFG_SET_SAP_XLNA_BYPASS CFG_INI_BOOL( \ + "xlna_bypass", \ + 0, \ + "SAP xLNA bypass control") + +/* + * + * g_disable_hw_assist - Flag to disable HW assist feature + * @Default: 0 + * + * This ini is used to enable/disable the HW assist feature in FW + * + * Related: none + * + * Supported Feature: STA/SAP + * + * Usage: External + * + * + */ + +#define CFG_DISABLE_HW_ASSIST CFG_INI_BOOL( \ + "g_disable_hw_assist", \ + 0, \ + "Disable HW assist feature in FW") + +#define CFG_FWOL_GENERIC_ALL \ + CFG_FWOL_DHCP \ + CFG(CFG_ENABLE_ANI) \ + CFG(CFG_SET_RTS_FOR_SIFS_BURSTING) \ + CFG(CFG_SET_SIFS_BURST) \ + CFG(CFG_MAX_MPDUS_IN_AMPDU) \ + CFG(CFG_ENABLE_PHY_REG) \ + CFG(CFG_UPPER_BRSSI_THRESH) \ + CFG(CFG_LOWER_BRSSI_THRESH) \ + CFG(CFG_DTIM_1CHRX_ENABLE) \ + CFG(CFG_ENABLE_COEX_ALT_CHAINMASK) \ + CFG(CFG_ENABLE_SMART_CHAINMASK) \ + CFG(CFG_ENABLE_FW_RTS_PROFILE) \ + CFG(CFG_ENABLE_FW_DEBUG_LOG_LEVEL) \ + CFG(CFG_ENABLE_FW_LOG_TYPE) \ + CFG(CFG_ENABLE_FW_MODULE_LOG_LEVEL) \ + CFG(CFG_RA_FILTER_ENABLE) \ + CFG(CFG_SET_TSF_GPIO_PIN) \ + __CFG_SET_TSF_IRQ_HOST_GPIO_PIN \ + __CFG_SET_TSF_SYNC_HOST_GPIO_PIN \ + __CFG_SET_TSF_PTP_OPT \ + CFG(CFG_LPRX) \ + __CFG_IS_SAE_ENABLED \ + CFG(CFG_ENABLE_GCMP) \ + CFG(CFG_TX_SCH_DELAY) \ + CFG(CFG_ENABLE_SECONDARY_RATE) \ + CFG(CFG_SET_SAP_XLNA_BYPASS) \ + CFG(CFG_ENABLE_FW_WOW_MODULE_LOG_LEVEL) \ + CFG(CFG_DISABLE_HW_ASSIST) + +#endif diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_ie_whitelist.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_ie_whitelist.h new file mode 100644 index 0000000000000000000000000000000000000000..a526d7b15e19f283949ab10aefe0a2db8068f8b1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_ie_whitelist.h @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_IE_WHITELIST_H +#define __CFG_IE_WHITELIST_H + +/* + * + * g_enable_probereq_whitelist_ies - Enable IE white listing + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable probe request IE white listing feature. + * Values 0 and 1 are used to disable and enable respectively, by default this + * feature is disabled. + * + * Related: None + * + * Supported Feature: Probe request IE whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_IE_WHITELIST CFG_INI_BOOL( \ + "g_enable_probereq_whitelist_ies", \ + 0, \ + "Enable IE whitelisting") + +/* + * For IE white listing in Probe Req, following ini parameters from + * g_probe_req_ie_bitmap_0 to g_probe_req_ie_bitmap_7 are used. User needs to + * input this values in hexa decimal format, when bit is set in bitmap, + * corresponding IE needs to be included in probe request. + * + * Example: + * ======== + * If IE 221 needs to be in the probe request, set the corresponding bit + * as follows: + * a= IE/32 = 221/32 = 6 = g_probe_req_ie_bitmap_6 + * b = IE modulo 32 = 29, + * means set the bth bit in g_probe_req_ie_bitmap_a, + * therefore set 29th bit in g_probe_req_ie_bitmap_6, + * as a result, g_probe_req_ie_bitmap_6=20000000 + * + * Note: For IE 221, its mandatory to set the gProbeReqOUIs. + */ + +/* + * + * g_probe_req_ie_bitmap_0 - Used to set the bitmap of IEs from 0 to 31 + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * This ini is used to include the IEs from 0 to 31 in probe request, + * when corresponding bit is set. + * + * Related: Need to enable g_enable_probereq_whitelist_ies. + * + * Supported Feature: Probe request ie whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_IE_BIT_MAP0 CFG_INI_UINT( \ + "g_probe_req_ie_bitmap_0", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "IE Bitmap 0") + +/* + * + * g_probe_req_ie_bitmap_1 - Used to set the bitmap of IEs from 32 to 63 + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * This ini is used to include the IEs from 32 to 63 in probe request, + * when corresponding bit is set. + * + * Related: Need to enable g_enable_probereq_whitelist_ies. + * + * Supported Feature: Probe request ie whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_IE_BIT_MAP1 CFG_INI_UINT( \ + "g_probe_req_ie_bitmap_1", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "IE Bitmap 1") + +/* + * + * g_probe_req_ie_bitmap_2 - Used to set the bitmap of IEs from 64 to 95 + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * This ini is used to include the IEs from 64 to 95 in probe request, + * when corresponding bit is set. + * + * Related: Need to enable g_enable_probereq_whitelist_ies. + * + * Supported Feature: Probe request ie whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_IE_BIT_MAP2 CFG_INI_UINT( \ + "g_probe_req_ie_bitmap_2", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "IE Bitmap 2") + +/* + * + * g_probe_req_ie_bitmap_3 - Used to set the bitmap of IEs from 96 to 127 + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * This ini is used to include the IEs from 96 to 127 in probe request, + * when corresponding bit is set. + * + * Related: Need to enable g_enable_probereq_whitelist_ies. + * + * Supported Feature: Probe request ie whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_IE_BIT_MAP3 CFG_INI_UINT( \ + "g_probe_req_ie_bitmap_3", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "IE Bitmap 3") + +/* + * + * g_probe_req_ie_bitmap_4 - Used to set the bitmap of IEs from 128 to 159 + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * This ini is used to include the IEs from 128 to 159 in probe request, + * when corresponding bit is set. + * + * Related: Need to enable g_enable_probereq_whitelist_ies. + * + * Supported Feature: Probe request ie whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_IE_BIT_MAP4 CFG_INI_UINT( \ + "g_probe_req_ie_bitmap_4", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "IE Bitmap 4") + +/* + * + * g_probe_req_ie_bitmap_5 - Used to set the bitmap of IEs from 160 to 191 + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * This ini is used to include the IEs from 160 to 191 in probe request, + * when corresponding bit is set. + * + * Related: Need to enable g_enable_probereq_whitelist_ies. + * + * Supported Feature: Probe request ie whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_IE_BIT_MAP5 CFG_INI_UINT( \ + "g_probe_req_ie_bitmap_5", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "IE Bitmap 5") + +/* + * + * g_probe_req_ie_bitmap_6 - Used to set the bitmap of IEs from 192 to 223 + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * This ini is used to include the IEs from 192 to 223 in probe request, + * when corresponding bit is set. + * + * Related: Need to enable g_enable_probereq_whitelist_ies. + * + * Supported Feature: Probe request ie whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_IE_BIT_MAP6 CFG_INI_UINT( \ + "g_probe_req_ie_bitmap_6", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "IE Bitmap 6") + +/* + * + * g_probe_req_ie_bitmap_7 - Used to set the bitmap of IEs from 224 to 255 + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * This ini is used to include the IEs from 224 to 255 in probe request, + * when corresponding bit is set. + * + * Related: Need to enable g_enable_probereq_whitelist_ies. + * + * Supported Feature: Probe request ie whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_IE_BIT_MAP7 CFG_INI_UINT( \ + "g_probe_req_ie_bitmap_7", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "IE Bitmap 7") + +#define MAX_PRB_REQ_VENDOR_OUI_INI_LEN 160 +#define VENDOR_SPECIFIC_IE_BITMAP 0x20000000 +/* + * For vendor specific IE, Probe Req OUI types and sub types which are + * to be white listed are specified in gProbeReqOUIs in the following + * example format - gProbeReqOUIs=AABBCCDD EEFF1122 + */ + +/* + * + * gProbeReqOUIs - Used to specify vendor specific OUIs + * @Default: Empty string + * + * This ini is used to include the specified OUIs in vendor specific IE + * of probe request. + * + * Related: Need to enable g_enable_probereq_whitelist_ies and + * vendor specific IE should be set in g_probe_req_ie_bitmap_6. + * + * Supported Feature: Probe request ie whitelisting + * + * Usage: Internal/External + * + * + */ +#define CFG_PROBE_REQ_OUI CFG_INI_STRING( \ + "gProbeReqOUIs", \ + 0, \ + MAX_PRB_REQ_VENDOR_OUI_INI_LEN, \ + "", \ + "Probe Req OUIs") + +#define CFG_IE_WHITELIST \ + CFG(CFG_PROBE_REQ_IE_WHITELIST) \ + CFG(CFG_PROBE_REQ_IE_BIT_MAP0) \ + CFG(CFG_PROBE_REQ_IE_BIT_MAP1) \ + CFG(CFG_PROBE_REQ_IE_BIT_MAP2) \ + CFG(CFG_PROBE_REQ_IE_BIT_MAP3) \ + CFG(CFG_PROBE_REQ_IE_BIT_MAP4) \ + CFG(CFG_PROBE_REQ_IE_BIT_MAP5) \ + CFG(CFG_PROBE_REQ_IE_BIT_MAP6) \ + CFG(CFG_PROBE_REQ_IE_BIT_MAP7) \ + CFG(CFG_PROBE_REQ_OUI) + +#endif diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_neighbor_roam.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_neighbor_roam.h new file mode 100644 index 0000000000000000000000000000000000000000..d2b1c69bb637600a6d76d0bcda1a1ed47155a3eb --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_neighbor_roam.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2012 - 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_NR_H +#define __CFG_NR_H + +/* + * + * 11k_offload_enable_bitmask - 11K offload bitmask feature control + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Disabled when 0 and enabled when 1 + * Usage: External + * + * + */ +#define CFG_OFFLOAD_11K_ENABLE_BITMASK CFG_INI_BOOL( \ + "11k_offload_enable_bitmask", \ + 1, \ + "11K offload bitmask feature control") + +/* + * + * nr_offload_params_bitmask - bitmask to specify which of the + * neighbor report offload params are valid in the ini + * frame + * @Min: 0 + * @Max: 63 + * @Default: 63 + * + * This ini specifies which of the neighbor report offload params are valid + * and should be considered by the FW. The bitmask is as follows + * B0: nr_offload_time_offset + * B1: nr_offload_low_rssi_offset + * B2: nr_offload_bmiss_count_trigger + * B3: nr_offload_per_threshold_offset + * B4: nr_offload_cache_timeout + * B5: nr_offload_max_req_cap + * B6-B7: Reserved + * + * Related : 11k_offload_enable_bitmask + * + * Usage: External + * + * + */ +#define CFG_OFFLOAD_NEIGHBOR_REPORT_PARAMS_BITMASK CFG_INI_UINT( \ + "nr_offload_params_bitmask", \ + 0, \ + 63, \ + 63, \ + CFG_VALUE_OR_DEFAULT, \ + "Neighbor report offload params validity bitmask") + +#define OFFLOAD_11K_BITMASK_NEIGHBOR_REPORT_REQUEST 0x1 + +/* + * + * nr_offload_time_offset - time interval in seconds after the + * neighbor report offload command to send the first neighbor report request + * frame + * @Min: 0 + * @Max: 3600 + * @Default: 30 + * + * Related : nr_offload_params_bitmask + * + * Usage: External + * + * + */ +#define CFG_OFFLOAD_NEIGHBOR_REPORT_TIME_OFFSET CFG_INI_UINT( \ + "nr_offload_time_offset", \ + 0, \ + 3600, \ + 30, \ + CFG_VALUE_OR_DEFAULT, \ + "Neighbor report time offset") + +/* + * + * nr_offload_low_rssi_offset - offset from the roam RSSI threshold + * to trigger the neighbor report request frame (in dBm) + * @Min: 4 + * @Max: 10 + * @Default: 4 + * + * Related : nr_offload_params_bitmask + * + * Usage: External + * + * + */ +#define CFG_OFFLOAD_NEIGHBOR_REPORT_LOW_RSSI_OFFSET CFG_INI_UINT( \ + "nr_offload_low_rssi_offset", \ + 4, \ + 10, \ + 4, \ + CFG_VALUE_OR_DEFAULT, \ + "Neighbor report low RSSI offset") + +/* + * + * nr_offload_bmiss_count_trigger - Number of beacon miss events to + * trigger a neighbor report request frame + * @Min: 1 + * @Max: 5 + * @Default: 1 + * + * Related : nr_offload_params_bitmask + * + * Usage: External + * + * + */ +#define CFG_OFFLOAD_NEIGHBOR_REPORT_BMISS_COUNT_TRIGGER CFG_INI_UINT( \ + "nr_offload_bmiss_count_trigger", \ + 1, \ + 5, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Beacon miss count trigger for neighbor report req frame") + +/* + * + * nr_offload_per_threshold_offset - offset from PER threshold to + * trigger a neighbor report request frame (in %) + * @Min: 5 + * @Max: 20 + * @Default: 5 + * + * This ini is used to set the neighbor report offload parameter: + * + * Related : nr_offload_params_bitmask + * + * Usage: External + * + * + */ +#define CFG_OFFLOAD_NEIGHBOR_REPORT_PER_THRESHOLD_OFFSET CFG_INI_UINT( \ + "nr_offload_per_threshold_offset", \ + 5, \ + 20, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "PER threshold offset to trigger neighbor report req frame") + +/* + * + * nr_offload_cache_timeout - time in seconds after which the + * neighbor report cache is marked as timed out and any of the triggers would + * cause a neighbor report request frame to be sent. + * @Min: 5 + * @Max: 86400 + * @Default: 1200 + * + * Related : nr_offload_params_bitmask + * + * Usage: External + * + * + */ +#define CFG_OFFLOAD_NEIGHBOR_REPORT_CACHE_TIMEOUT CFG_INI_UINT( \ + "nr_offload_cache_timeout", \ + 5, \ + 86400, \ + 1200, \ + CFG_VALUE_OR_DEFAULT, \ + "Neighbor report cache timeout to trigger report req frame") + +/* + * + * nr_offload_max_req_cap - Max number of neighbor + * report requests that can be sent to a connected peer in the current session. + * This counter is reset once a successful roam happens or at cache timeout + * @Min: 3 + * @Max: 300 + * @Default: 3 + * + * Related : nr_offload_params_bitmask + * + * Usage: External + * + * + */ +#define CFG_OFFLOAD_NEIGHBOR_REPORT_MAX_REQ_CAP CFG_INI_UINT( \ + "nr_offload_max_req_cap", \ + 3, \ + 300, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Max neighbor report request to be sent to connected peer") + +#define CFG_11K_ALL \ + CFG(CFG_OFFLOAD_11K_ENABLE_BITMASK) \ + CFG(CFG_OFFLOAD_NEIGHBOR_REPORT_PARAMS_BITMASK) \ + CFG(CFG_OFFLOAD_NEIGHBOR_REPORT_TIME_OFFSET) \ + CFG(CFG_OFFLOAD_NEIGHBOR_REPORT_LOW_RSSI_OFFSET) \ + CFG(CFG_OFFLOAD_NEIGHBOR_REPORT_BMISS_COUNT_TRIGGER) \ + CFG(CFG_OFFLOAD_NEIGHBOR_REPORT_PER_THRESHOLD_OFFSET) \ + CFG(CFG_OFFLOAD_NEIGHBOR_REPORT_CACHE_TIMEOUT) \ + CFG(CFG_OFFLOAD_NEIGHBOR_REPORT_MAX_REQ_CAP) + +#endif diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_thermal_temp.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_thermal_temp.h new file mode 100644 index 0000000000000000000000000000000000000000..e500a107cded05fa90d8e96905639ffb1a9b6107 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/cfg_thermal_temp.h @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2012-2018,2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_THERMAL_TEMP_H +#define __CFG_THERMAL_TEMP_H + +/* + * + * gThermalTempMinLevel0 - Set Thermal Temp Min Level0 + * @Min: 0 + * @Max: 1000 + * @Default: 0 + * + * Usage: External + * + * + */ +#define CFG_THERMAL_TEMP_MIN_LEVEL0 CFG_INI_UINT( \ + "gThermalTempMinLevel0", \ + 0, \ + 1000, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal Temp Min Level0") + +/* + * + * gThermalTempMaxLevel0 - Set Thermal Temp Max Level0 + * @Min: 0 + * @Max: 1000 + * @Default: 90 + * + * Usage: External + * + * + */ +#define CFG_THERMAL_TEMP_MAX_LEVEL0 CFG_INI_UINT( \ + "gThermalTempMaxLevel0", \ + 0, \ + 1000, \ + 90, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal Temp Max Level0") + +/* + * + * gThermalTempMinLevel1 - Set Thermal Temp Min Level1 + * @Min: 0 + * @Max: 1000 + * @Default: 70 + * + * Usage: External + * + * + */ +#define CFG_THERMAL_TEMP_MIN_LEVEL1 CFG_INI_UINT( \ + "gThermalTempMinLevel1", \ + 0, \ + 1000, \ + 70, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal Temp Min Level1") + +/* + * + * gThermalTempMaxLevel1 - Set Thermal Temp Max Level1 + * @Min: 0 + * @Max: 1000 + * @Default: 110 + * + * Usage: External + * + * + */ +#define CFG_THERMAL_TEMP_MAX_LEVEL1 CFG_INI_UINT( \ + "gThermalTempMaxLevel1", \ + 0, \ + 1000, \ + 110, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal Temp Max Level1") + +/* + * + * gThermalTempMinLevel2 - Set Thermal Temp Min Level2 + * @Min: 0 + * @Max: 1000 + * @Default: 90 + * + * Usage: External + * + * + */ +#define CFG_THERMAL_TEMP_MIN_LEVEL2 CFG_INI_UINT( \ + "gThermalTempMinLevel2", \ + 0, \ + 1000, \ + 90, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal Temp Min Level2") + +/* + * + * gThermalTempMaxLevel2 - Set Thermal Temp Max Level2 + * @Min: 0 + * @Max: 1000 + * @Default: 125 + * + * Usage: External + * + * + */ +#define CFG_THERMAL_TEMP_MAX_LEVEL2 CFG_INI_UINT( \ + "gThermalTempMaxLevel2", \ + 0, \ + 1000, \ + 125, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal Temp Max Level2") + +/* + * + * gThermalTempMinLevel3 - Set Thermal Temp Min Level3 + * @Min: 0 + * @Max: 1000 + * @Default: 110 + * + * Usage: External + * + * + */ +#define CFG_THERMAL_TEMP_MIN_LEVEL3 CFG_INI_UINT( \ + "gThermalTempMinLevel3", \ + 0, \ + 1000, \ + 110, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal Temp Min Level3") + +/* + * + * gThermalTempMaxLevel3 - Set Thermal Temp Max Level3 + * @Min: 0 + * @Max: 1000 + * @Default: 0 + * + * Usage: External + * + * + */ +#define CFG_THERMAL_TEMP_MAX_LEVEL3 CFG_INI_UINT( \ + "gThermalTempMaxLevel3", \ + 0, \ + 1000, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal Temp Max Level3") + +/* + * + * gThermalMitigationEnable - Set Thermal mitigation feature control + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Usage: External + * + * + */ +#define CFG_THERMAL_MITIGATION_ENABLE CFG_INI_BOOL( \ + "gThermalMitigationEnable", \ + 0, \ + "Thermal mitigation feature control") + +/* + * + * gThrottlePeriod - Set Thermal mitigation throttle period + * @Min: 10 + * @Max: 10000 + * @Default: 4000 + * + * Usage: External + * + * + */ +#define CFG_THROTTLE_PERIOD CFG_INI_UINT( \ + "gThrottlePeriod", \ + 10, \ + 10000, \ + 4000, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal mitigation throttle period") + +/* + * + * gThrottleDutyCycleLevel0 - Set Thermal mitigation throttle duty cycle level0 + * @Min: 0 + * @Max: 0 + * @Default: 0 + * + * Usage: External + * + * + */ +#define CFG_THROTTLE_DUTY_CYCLE_LEVEL0 CFG_INI_UINT( \ + "gThrottleDutyCycleLevel0", \ + 0, \ + 0, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal mitigation throttle duty cycle level0") + +/* + * + * gThrottleDutyCycleLevel1 - Set Thermal mitigation throttle duty cycle level1 + * @Min: 0 + * @Max: 100 + * @Default: 10 + * + * Usage: External + * + * + */ +#define CFG_THROTTLE_DUTY_CYCLE_LEVEL1 CFG_INI_UINT( \ + "gThrottleDutyCycleLevel1", \ + 0, \ + 100, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal mitigation throttle duty cycle level1") + +/* + * + * gThrottleDutyCycleLevel2 - Set Thermal mitigation throttle duty cycle level2 + * @Min: 0 + * @Max: 100 + * @Default: 30 + * + * Usage: External + * + * + */ +#define CFG_THROTTLE_DUTY_CYCLE_LEVEL2 CFG_INI_UINT( \ + "gThrottleDutyCycleLevel2", \ + 0, \ + 100, \ + 30, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal mitigation throttle duty cycle level2") + +/* + * + * gThrottleDutyCycleLevel3 - Set Thermal mitigation throttle duty cycle level3 + * @Min: 0 + * @Max: 100 + * @Default: 50 + * + * Usage: External + * + * + */ +#define CFG_THROTTLE_DUTY_CYCLE_LEVEL3 CFG_INI_UINT( \ + "gThrottleDutyCycleLevel3", \ + 0, \ + 100, \ + 50, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal mitigation throttle duty cycle level3") + +/* + * + * gThrottleDutyCycleLevel4 - Set Thermal mitigation throttle duty cycle level4 + * @Min: 0 + * @Max: 100 + * @Default: 70 + * + * This ini will apply the thermal throttle duty cycle value in FW + * Usage: External + * + * + */ +#define CFG_THROTTLE_DUTY_CYCLE_LEVEL4 CFG_INI_UINT( \ + "gThrottleDutyCycleLevel4", \ + 0, \ + 100, \ + 70, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal mitigation throttle duty cycle level4") + +/* + * + * gThrottleDutyCycleLevel5 - Set Thermal mitigation throttle duty cycle level5 + * @Min: 0 + * @Max: 100 + * @Default: 90 + * + * This ini will apply the thermal throttle duty cycle value in FW + * Usage: External + * + * + */ +#define CFG_THROTTLE_DUTY_CYCLE_LEVEL5 CFG_INI_UINT( \ + "gThrottleDutyCycleLevel5", \ + 0, \ + 100, \ + 90, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal mitigation throttle duty cycle level5") + +/* + *gThermalSamplingTime - Configure the thermal mitigation sampling time in ms. + * + * @Min: 10 + * @Max: 100 + * @Default: 100 + * + * This ini will control the sampling time that the thermal mitigation in FW + * will consider while applying the duty cycle. + * + * Usage: External + * + * Supported features: Thermal Mitigation + * + * + */ +#define CFG_THERMAL_SAMPLING_TIME CFG_INI_UINT( \ + "gThermalSamplingTime", \ + 10, \ + 100, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "Thermal mitigation sampling time") + +#define CFG_THERMAL_TEMP_ALL \ + CFG(CFG_THERMAL_TEMP_MIN_LEVEL0) \ + CFG(CFG_THERMAL_TEMP_MAX_LEVEL0) \ + CFG(CFG_THERMAL_TEMP_MIN_LEVEL1) \ + CFG(CFG_THERMAL_TEMP_MAX_LEVEL1) \ + CFG(CFG_THERMAL_TEMP_MIN_LEVEL2) \ + CFG(CFG_THERMAL_TEMP_MAX_LEVEL2) \ + CFG(CFG_THERMAL_TEMP_MIN_LEVEL3) \ + CFG(CFG_THERMAL_TEMP_MAX_LEVEL3) \ + CFG(CFG_THERMAL_MITIGATION_ENABLE) \ + CFG(CFG_THROTTLE_PERIOD) \ + CFG(CFG_THROTTLE_DUTY_CYCLE_LEVEL0) \ + CFG(CFG_THROTTLE_DUTY_CYCLE_LEVEL1) \ + CFG(CFG_THROTTLE_DUTY_CYCLE_LEVEL2) \ + CFG(CFG_THROTTLE_DUTY_CYCLE_LEVEL3) \ + CFG(CFG_THROTTLE_DUTY_CYCLE_LEVEL4) \ + CFG(CFG_THROTTLE_DUTY_CYCLE_LEVEL5) \ + CFG(CFG_THERMAL_SAMPLING_TIME) + +#endif + diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/wlan_fwol_public_structs.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/wlan_fwol_public_structs.h new file mode 100644 index 0000000000000000000000000000000000000000..10f253a8bb90e0ea15af6f42a6c4d9bfb46f2b43 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/wlan_fwol_public_structs.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains fw offload structure definations + */ + +#ifndef _WLAN_FWOL_PUBLIC_STRUCTS_H_ +#define _WLAN_FWOL_PUBLIC_STRUCTS_H_ + +#include "wlan_objmgr_psoc_obj.h" + +#ifdef WLAN_FEATURE_ELNA +/** + * struct set_elna_bypass_request - set eLNA bypass request + * @vdev_id: vdev id + * @en_dis: 0 - disable eLNA bypass + * 1 - enable eLNA bypass + */ +struct set_elna_bypass_request { + uint8_t vdev_id; + uint8_t en_dis; +}; + +/** + * struct get_elna_bypass_request - get eLNA bypass request + * @vdev_id: vdev id + */ +struct get_elna_bypass_request { + uint8_t vdev_id; +}; + +/** + * struct get_elna_bypass_response - get eLNA bypass response + * @vdev_id: vdev id + * @en_dis: 0 - disable eLNA bypass + * 1 - enable eLNA bypass + */ +struct get_elna_bypass_response { + uint8_t vdev_id; + uint8_t en_dis; +}; +#endif + +/** + * struct wlan_fwol_callbacks - fw offload callbacks + * @get_elna_bypass_callback: callback for get eLNA bypass + * @get_elna_bypass_context: context for get eLNA bypass + */ +struct wlan_fwol_callbacks { +#ifdef WLAN_FEATURE_ELNA + void (*get_elna_bypass_callback)(void *context, + struct get_elna_bypass_response *response); + void *get_elna_bypass_context; +#endif +}; + +/** + * struct wlan_fwol_tx_ops - structure of tx func pointers + * @set_elna_bypass: set eLNA bypass + * @get_elna_bypass: get eLNA bypass + * @reg_evt_handler: register event handler + * @unreg_evt_handler: unregister event handler + * @send_dscp_up_map_to_fw: send dscp-to-up map values to FW + */ +struct wlan_fwol_tx_ops { +#ifdef WLAN_FEATURE_ELNA + QDF_STATUS (*set_elna_bypass)(struct wlan_objmgr_psoc *psoc, + struct set_elna_bypass_request *req); + QDF_STATUS (*get_elna_bypass)(struct wlan_objmgr_psoc *psoc, + struct get_elna_bypass_request *req); +#endif + QDF_STATUS (*reg_evt_handler)(struct wlan_objmgr_psoc *psoc, + void *arg); + QDF_STATUS (*unreg_evt_handler)(struct wlan_objmgr_psoc *psoc, + void *arg); +#ifdef WLAN_SEND_DSCP_UP_MAP_TO_FW + QDF_STATUS (*send_dscp_up_map_to_fw)( + struct wlan_objmgr_psoc *psoc, + uint32_t *dscp_to_up_map); +#endif +}; + +/** + * struct wlan_fwol_rx_ops - structure of rx func pointers + * @get_elna_bypass_resp: get eLNA bypass response + */ +struct wlan_fwol_rx_ops { +#ifdef WLAN_FEATURE_ELNA + QDF_STATUS (*get_elna_bypass_resp)(struct wlan_objmgr_psoc *psoc, + struct get_elna_bypass_response *resp); +#endif +}; + +#endif /* _WLAN_FWOL_PUBLIC_STRUCTS_H_ */ + diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/wlan_fwol_tgt_api.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/wlan_fwol_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..bbbff5212c199b117d4bf00bdfa45a1749853f24 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/wlan_fwol_tgt_api.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: fw offload south bound interface declaration + */ +#ifndef _WLAN_FWOL_TGT_API_H +#define _WLAN_FWOL_TGT_API_H + +#include "wlan_fwol_public_structs.h" + +#define FWOL_WILDCARD_PDEV_ID 0 + +/** + * tgt_fwol_register_ev_handler() - register south bound event handler + * @psoc: psoc handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS tgt_fwol_register_ev_handler(struct wlan_objmgr_psoc *psoc); + +/** + * tgt_fwol_unregister_ev_handler() - unregister south bound event handler + * @psoc: psoc handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS tgt_fwol_unregister_ev_handler(struct wlan_objmgr_psoc *psoc); + +/** + * tgt_fwol_register_rx_ops() - register fw offload rx operations + * @rx_ops: fps to rx operations + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS tgt_fwol_register_rx_ops(struct wlan_fwol_rx_ops *rx_ops); + +/** + * tgt_fwol_pdev_param_send() - send pdev params to firmware + * @pdev: pdev handle + * @pdev_params: pdev params + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS tgt_fwol_pdev_param_send(struct wlan_objmgr_pdev *pdev, + struct pdev_params pdev_param); + +#endif /* _WLAN_FWOL_TGT_API_H */ diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/wlan_fwol_ucfg_api.h b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/wlan_fwol_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..21f62083721482eff8b0d231cf7abbe9c5096e69 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/inc/wlan_fwol_ucfg_api.h @@ -0,0 +1,929 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare internal API related to the fwol component + */ + +#ifndef _WLAN_FWOL_UCFG_API_H_ +#define _WLAN_FWOL_UCFG_API_H_ + +#include +#include +#include +#include "wlan_fw_offload_main.h" +#include "wlan_fwol_public_structs.h" + +#ifdef WLAN_FW_OFFLOAD +/** + * ucfg_fwol_psoc_open() - FWOL component Open + * @psoc: pointer to psoc object + * + * Open the FWOL component and initialize the FWOL structure + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_fwol_psoc_close() - FWOL component close + * @psoc: pointer to psoc object + * + * Close the FWOL component and clear the FWOL structures + * + * Return: None + */ +void ucfg_fwol_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_fwol_init() - initialize fwol_ctx context. + * + * This function initializes the fwol context. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS ucfg_fwol_init(void); + +/** + * ucfg_fwol_deinit() - De initialize fwol_ctx context. + * + * This function De initializes fwol contex. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +void ucfg_fwol_deinit(void); + +/** + * ucfg_fwol_get_coex_config_params() - Get coex config params + * @psoc: Pointer to psoc object + * @coex_config: Pointer to struct wlan_fwol_coex_config + * + * Return: QDF_STATUS + */ +QDF_STATUS +ucfg_fwol_get_coex_config_params(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_coex_config *coex_config); + +/** + * ucfg_fwol_get_thermal_temp() - Get thermal temperature config params + * @psoc: Pointer to psoc object + * @thermal_temp: Pointer to struct wlan_fwol_thermal_temp + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_thermal_temp(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_thermal_temp *thermal_temp); + +/** + * ucfg_fwol_get_neighbor_report_cfg() - Get neighbor report config params + * @psoc: Pointer to psoc object + * @fwol_neighbor_report_cfg: Pointer to return neighbor report config + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_neighbor_report_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_neighbor_report_cfg + *fwol_neighbor_report_cfg); + +/** + * ucfg_fwol_get_neighbor_report_req() - Get neighbor report request bit + * @psoc: Pointer to psoc object + * @neighbor_report_req: Pointer to return value + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_is_neighbor_report_req_supported(struct wlan_objmgr_psoc *psoc, + bool *neighbor_report_req); + +/** + * ucfg_fwol_get_ie_whitelist() - Get IE whitelist param value + * @psoc: Pointer to psoc object + * @ie_whitelist: Pointer to return the IE whitelist param value + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_ie_whitelist(struct wlan_objmgr_psoc *psoc, bool *ie_whitelist); + +/** + * ucfg_fwol_set_ie_whitelist() - Set IE whitelist param value + * @psoc: Pointer to psoc object + * @ie_whitelist: Value to set IE whitelist param + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_set_ie_whitelist(struct wlan_objmgr_psoc *psoc, bool ie_whitelist); + +/** + * ucfg_fwol_get_all_whitelist_params() - Get all IE whitelist param values + * @psoc: Pointer to psoc object + * @whitelist: Pointer to struct wlan_fwol_ie_whitelist + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_all_whitelist_params(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_ie_whitelist *whitelist); + +/** ucfg_fwol_get_ani_enabled() - Assigns the ani_enabled value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_ani_enabled(struct wlan_objmgr_psoc *psoc, + bool *ani_enabled); + +/** + * ucfg_fwol_get_ani_enabled() - Assigns the enable_rts_sifsbursting value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_get_enable_rts_sifsbursting(struct wlan_objmgr_psoc *psoc, + bool *enable_rts_sifsbursting); + +/** + * ucfg_get_enable_sifs_burst() - Get the enable_sifs_burst value + * @psoc: pointer to the psoc object + * @enable_sifs_burst: pointer to return enable_sifs_burst value + * + * Return: QDF Status + */ +QDF_STATUS ucfg_get_enable_sifs_burst(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sifs_burst); + +/** + * ucfg_get_max_mpdus_inampdu() - Assigns the max_mpdus_inampdu value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_get_max_mpdus_inampdu(struct wlan_objmgr_psoc *psoc, + uint8_t *max_mpdus_inampdu); + +/** + * ucfg_get_enable_phy_reg_retention() - Assigns enable_phy_reg_retention value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_get_enable_phy_reg_retention(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_phy_reg_retention); + +/** + * ucfg_get_upper_brssi_thresh() - Assigns upper_brssi_thresh value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_get_upper_brssi_thresh(struct wlan_objmgr_psoc *psoc, + uint16_t *upper_brssi_thresh); + +/** + * ucfg_get_lower_brssi_thresh() - Assigns lower_brssi_thresh value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_get_lower_brssi_thresh(struct wlan_objmgr_psoc *psoc, + uint16_t *lower_brssi_thresh); + +/** + * ucfg_get_enable_dtim_1chrx() - Assigns enable_dtim_1chrx value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_get_enable_dtim_1chrx(struct wlan_objmgr_psoc *psoc, + bool *enable_dtim_1chrx); + +/** + * ucfg_get_alternate_chainmask_enabled() - Assigns alt chainmask_enabled value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_get_alternative_chainmask_enabled(struct wlan_objmgr_psoc *psoc, + bool *alternative_chainmask_enabled); + +/** + * ucfg_get_smart_chainmask_enabled() - Assigns smart_chainmask_enabled value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_get_smart_chainmask_enabled(struct wlan_objmgr_psoc *psoc, + bool *smart_chainmask_enabled); + +/** + * ucfg_fwol_get_rts_profile() - Assigns get_rts_profile value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_rts_profile(struct wlan_objmgr_psoc *psoc, + uint16_t *get_rts_profile); + +/** + * ucfg_fwol_get_enable_fw_log_level() - Assigns enable_fw_log_level value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_enable_fw_log_level(struct wlan_objmgr_psoc *psoc, + uint16_t *enable_fw_log_level); + +/** + * ucfg_fwol_get_enable_fw_log_type() - Assigns enable_fw_log_type value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_enable_fw_log_type(struct wlan_objmgr_psoc *psoc, + uint16_t *enable_fw_log_type); +/** + * ucfg_fwol_get_enable_fw_module_log_level() - Assigns + * enable_fw_module_log_level string + * @psoc: pointer to the psoc object + * @enable_fw_module_log_level: + * pointer to enable_fw_module_log_level array + * @enable_fw_module_log_level_num: + * pointer to enable_fw_module_log_level array element num + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_enable_fw_module_log_level( + struct wlan_objmgr_psoc *psoc, + uint8_t **enable_fw_module_log_level, + uint8_t *enable_fw_module_log_level_num); + +/** + * ucfg_fwol_wow_get_enable_fw_module_log_level() - Assigns + * enable_fw_module_log_level string + * + * @psoc: pointer to the psoc object + * @enable_fw_wow_module_log_level: + * pointer to enable_fw_wow_module_log_level array + * @enable_fw_wow_module_log_level_num: + * pointer to enable_fw_wow_module_log_level array element num + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_wow_get_enable_fw_module_log_level( + struct wlan_objmgr_psoc *psoc, + uint8_t **enable_fw_wow_module_log_level, + uint8_t *enable_fw_wow_module_log_level_num); + +/** + * ucfg_fwol_get_sap_xlna_bypass() - Assigns sap_xlna_bypass value + * @psoc: pointer to the psoc object + * @sap_xlna_bypass: pointer to return sap_xlna_bypass bool + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_sap_xlna_bypass(struct wlan_objmgr_psoc *psoc, + bool *sap_xlna_bypass); + +#ifdef FEATURE_WLAN_RA_FILTERING +/** + * ucfg_fwol_set_is_rate_limit_enabled() - Sets the is_rate_limit_enabled value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_set_is_rate_limit_enabled(struct wlan_objmgr_psoc *psoc, + bool is_rate_limit_enabled); + +/** + * ucfg_fwol_get_is_rate_limit_enabled() - Assigns is_rate_limit_enabled value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_is_rate_limit_enabled(struct wlan_objmgr_psoc *psoc, + bool *is_rate_limit_enabled); + +#endif /* FEATURE_WLAN_RA_FILTERING */ + +/** + * ucfg_fwol_get_tsf_gpio_pin() - Assigns tsf_gpio_pin value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ + +QDF_STATUS ucfg_fwol_get_tsf_gpio_pin(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_gpio_pin); + +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ +/** + * ucfg_fwol_get_tsf_irq_host_gpio_pin() - Assigns tsf_irq_host_gpio_pin value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ + +QDF_STATUS +ucfg_fwol_get_tsf_irq_host_gpio_pin(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_irq_host_gpio_pin); +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC +/** + * ucfg_fwol_get_tsf_sync_host_gpio_pin() - Assigns tsf_sync_host_gpio_pin value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ + +QDF_STATUS +ucfg_fwol_get_tsf_sync_host_gpio_pin(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_irq_host_gpio_pin); +#endif + +#ifdef DHCP_SERVER_OFFLOAD +/** + * ucfg_fwol_get_enable_dhcp_server_offload()-Assign enable_dhcp_server_offload + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_enable_dhcp_server_offload(struct wlan_objmgr_psoc *psoc, + bool *enable_dhcp_server_offload); + +/** + * ucfg_fwol_get_dhcp_max_num_clients() - Assigns dhcp_max_num_clients value + * @psoc: pointer to the psoc object + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_dhcp_max_num_clients(struct wlan_objmgr_psoc *psoc, + uint32_t *dhcp_max_num_clients); +#endif + +/** + * ucfg_fwol_get_tsf_ptp_options() - Get TSF Plus feature options + * @psoc: pointer to the psoc object + * @tsf_ptp_options: Pointer to return tsf ptp options + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_tsf_ptp_options(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_ptp_options); +/** + * ucfg_fwol_get_lprx_enable() - Get LPRx feature enable status + * @psoc: pointer to the psoc object + * @lprx_enable: Pointer to return LPRX feature enable status + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_lprx_enable(struct wlan_objmgr_psoc *psoc, + bool *lprx_enable); + +/** + * ucfg_fwol_get_sae_enable() - Get SAE feature enable status + * @psoc: pointer to the psoc object + * + * Return: True if enabled else false + */ +bool ucfg_fwol_get_sae_enable(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_fwol_get_gcmp_enable() - Get GCMP feature enable status + * @psoc: pointer to the psoc object + * + * Return: True if enabled else false + */ +bool ucfg_fwol_get_gcmp_enable(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_fwol_get_enable_tx_sch_delay() - Get enable tx sch delay + * @psoc: pointer to the psoc object + * @enable_tx_sch_delay: Pointer to return enable_tx_sch_delay value + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_enable_tx_sch_delay(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_tx_sch_delay); + +/** + * ucfg_fwol_get_enable_secondary_rate() - Get enable secondary rate + * @psoc: pointer to the psoc object + * @enable_tx_sch_delay: Pointer to return enable secondary rate value + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_enable_secondary_rate(struct wlan_objmgr_psoc *psoc, + uint32_t *enable_secondary_rate); +/** + * ucfg_fwol_get_all_adaptive_dwelltime_params() - Get all adaptive + dwelltime_params + * @psoc: Pointer to psoc object + * @dwelltime_params: Pointer to struct adaptive_dwelltime_params + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_all_adaptive_dwelltime_params( + struct wlan_objmgr_psoc *psoc, + struct adaptive_dwelltime_params *dwelltime_params); +/** + * ucfg_fwol_get_adaptive_dwell_mode_enabled() - API to globally disable/enable + * the adaptive dwell config. + * Acceptable values for this: + * 0: Config is disabled + * 1: Config is enabled + * + * @psoc: pointer to psoc object + * @adaptive_dwell_mode_enabled: adaptive dwell mode enable/disable + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_adaptive_dwell_mode_enabled(struct wlan_objmgr_psoc *psoc, + bool *adaptive_dwell_mode_enabled); + +/** + * ucfg_fwol_get_global_adapt_dwelltime_mode() - API to set default + * adaptive mode. + * It will be used if any of the scan dwell mode is set to default. + * For uses : see enum scan_dwelltime_adaptive_mode + * + * @psoc: pointer to psoc object + * global_adapt_dwelltime_mode@: global adaptive dwell mode value + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_global_adapt_dwelltime_mode(struct wlan_objmgr_psoc *psoc, + uint8_t *global_adapt_dwelltime_mode); +/** + * ucfg_fwol_get_adapt_dwell_lpf_weight() - API to get weight to calculate + * the average low pass filter for channel congestion + * @psoc: pointer to psoc object + * @adapt_dwell_lpf_weight: adaptive low pass filter weight + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_adapt_dwell_lpf_weight(struct wlan_objmgr_psoc *psoc, + uint8_t *adapt_dwell_lpf_weight); + +/** + * ucfg_fwol_get_adapt_dwell_passive_mon_intval() - API to get interval value + * for montitoring wifi activity in passive scan in msec. + * @psoc: pointer to psoc object + * @adapt_dwell_passive_mon_intval: adaptive monitor interval in passive scan + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_fwol_get_adapt_dwell_passive_mon_intval( + struct wlan_objmgr_psoc *psoc, + uint8_t *adapt_dwell_passive_mon_intval); + +/** + * ucfg_fwol_get_adapt_dwell_wifi_act_threshold - API to get % of wifi activity + * used in passive scan + * @psoc: pointer to psoc object + * @adapt_dwell_wifi_act_threshold: percent of wifi activity in passive scan + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_get_adapt_dwell_wifi_act_threshold( + struct wlan_objmgr_psoc *psoc, + uint8_t *adapt_dwell_wifi_act_threshold); + +/** + * ucfg_fwol_init_adapt_dwelltime_in_cfg - API to initialize adaptive + * dwell params + * @psoc: pointer to psoc object + * @adaptive_dwelltime_params: pointer to adaptive_dwelltime_params structure + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_fwol_init_adapt_dwelltime_in_cfg( + struct wlan_objmgr_psoc *psoc, + struct adaptive_dwelltime_params *dwelltime_params) +{ + return fwol_init_adapt_dwelltime_in_cfg(psoc, dwelltime_params); +} + +/** + * ucfg_fwol_set_adaptive_dwelltime_config - API to set adaptive + * dwell params config + * @adaptive_dwelltime_params: adaptive_dwelltime_params structure + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_fwol_set_adaptive_dwelltime_config( + struct adaptive_dwelltime_params *dwelltime_params) +{ + return fwol_set_adaptive_dwelltime_config(dwelltime_params); +} + +#ifdef WLAN_FEATURE_ELNA +/** + * ucfg_fwol_set_elna_bypass() - send set eLNA bypass request + * @vdev: vdev handle + * @req: set eLNA bypass request + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_fwol_set_elna_bypass(struct wlan_objmgr_vdev *vdev, + struct set_elna_bypass_request *req); + +/** + * ucfg_fwol_get_elna_bypass() - send get eLNA bypass request + * @vdev: vdev handle + * @req: get eLNA bypass request + * @callback: get eLNA bypass response callback + * @context: request manager context + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_fwol_get_elna_bypass(struct wlan_objmgr_vdev *vdev, + struct get_elna_bypass_request *req, + void (*callback)(void *context, + struct get_elna_bypass_response *response), + void *context); +#endif /* WLAN_FEATURE_ELNA */ + +#ifdef WLAN_SEND_DSCP_UP_MAP_TO_FW +/** + * ucfg_fwol_send_dscp_up_map_to_fw() - send dscp_up map to FW + * @vdev: vdev handle + * @dscp_to_up_map: DSCP to UP map array + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_fwol_send_dscp_up_map_to_fw( + struct wlan_objmgr_vdev *vdev, + uint32_t *dscp_to_up_map); +#else +static inline +QDF_STATUS ucfg_fwol_send_dscp_up_map_to_fw( + struct wlan_objmgr_vdev *vdev, + uint32_t *dscp_to_up_map) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * ucfg_fwol_configure_global_params - API to configure global params + * @psoc: pointer to psoc object + * @pdev: pointer to pdev object + * + * Used to configure global firmware params. This is invoked from hdd during + * bootup. + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_configure_global_params(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_fwol_configure_vdev_params - API to configure vdev specific params + * @psoc: pointer to psoc object + * @pdev: pointer to pdev object + * @device_mode: device mode + * @vdev_id: vdev ID + * + * Used to configure per vdev firmware params based on device mode. This is + * invoked from hdd during vdev creation. + * + * Return: QDF Status + */ +QDF_STATUS ucfg_fwol_configure_vdev_params(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev, + enum QDF_OPMODE device_mode, + uint8_t vdev_id); +#else +static inline QDF_STATUS ucfg_fwol_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void ucfg_fwol_psoc_close(struct wlan_objmgr_psoc *psoc) +{ +} + +static inline QDF_STATUS ucfg_fwol_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void ucfg_fwol_deinit(void) +{ +} + +static inline QDF_STATUS +ucfg_fwol_get_coex_config_params(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_coex_config *coex_config) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_thermal_temp(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_thermal_temp *thermal_temp) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_neighbor_report_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_neighbor_report_cfg + *fwol_neighbor_report_cfg) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_is_neighbor_report_req_supported(struct wlan_objmgr_psoc *psoc, + bool *neighbor_report_req) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_ie_whitelist(struct wlan_objmgr_psoc *psoc, bool *ie_whitelist) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_set_ie_whitelist(struct wlan_objmgr_psoc *psoc, bool ie_whitelist) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_all_whitelist_params(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_ie_whitelist *whitelist) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_ani_enabled(struct wlan_objmgr_psoc *psoc, + bool *ani_enabled) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_get_enable_rts_sifsbursting(struct wlan_objmgr_psoc *psoc, + bool *enable_rts_sifsbursting) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_get_max_mpdus_inampdu(struct wlan_objmgr_psoc *psoc, + uint8_t *max_mpdus_inampdu) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_get_enable_phy_reg_retention(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_phy_reg_retention) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_get_upper_brssi_thresh(struct wlan_objmgr_psoc *psoc, + uint16_t *upper_brssi_thresh) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_get_lower_brssi_thresh(struct wlan_objmgr_psoc *psoc, + uint16_t *lower_brssi_thresh) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_get_enable_dtim_1chrx(struct wlan_objmgr_psoc *psoc, + bool *enable_dtim_1chrx) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_get_alternative_chainmask_enabled(struct wlan_objmgr_psoc *psoc, + bool *alternative_chainmask_enabled) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_get_smart_chainmask_enabled(struct wlan_objmgr_psoc *psoc, + bool *smart_chainmask_enabled) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_rts_profile(struct wlan_objmgr_psoc *psoc, + uint16_t *get_rts_profile) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_enable_fw_log_level(struct wlan_objmgr_psoc *psoc, + uint16_t *enable_fw_log_level) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_enable_fw_log_type(struct wlan_objmgr_psoc *psoc, + uint16_t *enable_fw_log_type) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_enable_fw_module_log_level( + struct wlan_objmgr_psoc *psoc, + uint8_t **enable_fw_module_log_level, + uint8_t *enable_fw_module_log_level_num) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_sap_xlna_bypass(struct wlan_objmgr_psoc *psoc, + uint8_t *sap_xlna_bypass) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_tsf_gpio_pin(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_gpio_pin) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_tsf_ptp_options(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_ptp_options) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_lprx_enable(struct wlan_objmgr_psoc *psoc, + bool *lprx_enable) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline bool ucfg_fwol_get_sae_enable(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline bool ucfg_fwol_get_gcmp_enable(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline QDF_STATUS +ucfg_fwol_get_enable_tx_sch_delay(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_tx_sch_delay) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_enable_secondary_rate(struct wlan_objmgr_psoc *psoc, + uint32_t *enable_secondary_rate) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_all_adaptive_dwelltime_params( + struct wlan_objmgr_psoc *psoc, + struct adaptive_dwelltime_params *dwelltime_params) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_adaptive_dwell_mode_enabled(struct wlan_objmgr_psoc *psoc, + bool *adaptive_dwell_mode_enabled) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_global_adapt_dwelltime_mode(struct wlan_objmgr_psoc *psoc, + uint8_t *global_adapt_dwelltime_mode) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_adapt_dwell_lpf_weight(struct wlan_objmgr_psoc *psoc, + uint8_t *adapt_dwell_lpf_weight) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_adapt_dwell_passive_mon_intval( + struct wlan_objmgr_psoc *psoc, + uint8_t *adapt_dwell_passive_mon_intval) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_adapt_dwell_wifi_act_threshold( + struct wlan_objmgr_psoc *psoc, + uint8_t *adapt_dwell_wifi_act_threshold) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_init_adapt_dwelltime_in_cfg( + struct wlan_objmgr_psoc *psoc, + struct adaptive_dwelltime_params *dwelltime_params) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_set_adaptive_dwelltime_config( + struct adaptive_dwelltime_params *dwelltime_params) +{ + return QDF_STATUS_E_FAILURE; +} + +#ifdef FEATURE_WLAN_RA_FILTERING +static inline QDF_STATUS +ucfg_fwol_set_is_rate_limit_enabled(struct wlan_objmgr_psoc *psoc, + bool is_rate_limit_enabled) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_get_is_rate_limit_enabled(struct wlan_objmgr_psoc *psoc, + bool *is_rate_limit_enabled) +{ + return QDF_STATUS_E_FAILURE; +} +#endif /* FEATURE_WLAN_RA_FILTERING */ + +static inline QDF_STATUS +ucfg_fwol_configure_global_params(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_fwol_configure_vdev_params(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev, + enum QDF_OPMODE device_mode, uint8_t vdev_id) +{ + return QDF_STATUS_E_FAILURE; +} + +#endif /* WLAN_FW_OFFLOAD */ + +#endif /* _WLAN_FWOL_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/src/wlan_fwol_tgt_api.c b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/src/wlan_fwol_tgt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..a8f88506b934321d8b2f4d215476c49c0d3c46c6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/src/wlan_fwol_tgt_api.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains fw offload south bound interface definitions + */ + +#include "scheduler_api.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_global_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_fwol_public_structs.h" +#include "wlan_fwol_ucfg_api.h" +#include "wlan_fwol_tgt_api.h" +#include "wlan_fw_offload_main.h" +#include "target_if.h" + +QDF_STATUS tgt_fwol_register_ev_handler(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + struct wlan_fwol_tx_ops *tx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!psoc) { + fwol_err("NULL psoc handle"); + return QDF_STATUS_E_INVAL; + } + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL Obj"); + return QDF_STATUS_E_INVAL; + } + + tx_ops = &fwol_obj->tx_ops; + if (tx_ops->reg_evt_handler) { + status = tx_ops->reg_evt_handler(psoc, NULL); + fwol_debug("reg_evt_handler, status:%d", status); + } else { + fwol_alert("No reg_evt_handler"); + } + + return status; +} + +QDF_STATUS tgt_fwol_unregister_ev_handler(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + struct wlan_fwol_tx_ops *tx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!psoc) { + fwol_err("NNULL psoc handle"); + return QDF_STATUS_E_INVAL; + } + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL Obj"); + return QDF_STATUS_E_INVAL; + } + + tx_ops = &fwol_obj->tx_ops; + if (tx_ops->unreg_evt_handler) { + status = tx_ops->unreg_evt_handler(psoc, NULL); + fwol_debug("unreg_evt_handler, status:%d", status); + } else { + fwol_alert("No unreg_evt_handler"); + } + + return status; +} + +/** + * fwol_flush_callback() - fw offload message flush callback + * @msg: fw offload message + * + * Return: QDF_STATUS_SUCCESS on success. + */ +__attribute__((unused)) +static QDF_STATUS fwol_flush_callback(struct scheduler_msg *msg) +{ + struct wlan_fwol_rx_event *event; + + if (!msg) { + fwol_err("NULL pointer for eLNA message"); + return QDF_STATUS_E_INVAL; + } + + event = msg->bodyptr; + msg->bodyptr = NULL; + fwol_release_rx_event(event); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_ELNA +/** + * tgt_fwol_get_elna_bypass_resp() - handler for get eLNA bypass response + * @psoc: psoc handle + * @resp: status for last channel config + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +tgt_fwol_get_elna_bypass_resp(struct wlan_objmgr_psoc *psoc, + struct get_elna_bypass_response *resp) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0}; + struct wlan_fwol_rx_event *event; + + event = qdf_mem_malloc(sizeof(*event)); + if (!event) + return QDF_STATUS_E_NOMEM; + + status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_FWOL_SB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + fwol_err("Failed to get psoc ref"); + fwol_release_rx_event(event); + return status; + } + + event->psoc = psoc; + event->event_id = WLAN_FWOL_EVT_GET_ELNA_BYPASS_RESPONSE; + event->get_elna_bypass_response = *resp; + msg.type = WLAN_FWOL_EVT_GET_ELNA_BYPASS_RESPONSE; + msg.bodyptr = event; + msg.callback = fwol_process_event; + msg.flush_callback = fwol_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_FWOL, + QDF_MODULE_ID_FWOL, + QDF_MODULE_ID_TARGET_IF, &msg); + + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + fwol_err("failed to send WLAN_FWOL_GET_ELNA_BYPASS_RESPONSE msg"); + fwol_flush_callback(&msg); + + return status; +} + +static void tgt_fwol_register_elna_rx_ops(struct wlan_fwol_rx_ops *rx_ops) +{ + rx_ops->get_elna_bypass_resp = tgt_fwol_get_elna_bypass_resp; +} +#else +static void tgt_fwol_register_elna_rx_ops(struct wlan_fwol_rx_ops *rx_ops) +{ +} +#endif /* WLAN_FEATURE_ELNA */ + +QDF_STATUS tgt_fwol_register_rx_ops(struct wlan_fwol_rx_ops *rx_ops) +{ + tgt_fwol_register_elna_rx_ops(rx_ops); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_fwol_pdev_param_send(struct wlan_objmgr_pdev *pdev, + struct pdev_params pdev_param) +{ + struct wmi_unified *wmi_handle = get_wmi_unified_hdl_from_pdev(pdev); + + if (!wmi_handle) + return QDF_STATUS_E_FAILURE; + + return wmi_unified_pdev_param_send(wmi_handle, &pdev_param, + FWOL_WILDCARD_PDEV_ID); +} diff --git a/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/src/wlan_fwol_ucfg_api.c b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/src/wlan_fwol_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..4c035117a08f7c0d76dfbb47a7230c0935df4024 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/fw_offload/dispatcher/src/wlan_fwol_ucfg_api.c @@ -0,0 +1,1005 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define internal APIs related to the fwol component + */ + +#include "wlan_fw_offload_main.h" +#include "wlan_fwol_public_structs.h" +#include "wlan_fwol_ucfg_api.h" +#include "wlan_fwol_tgt_api.h" +#include "target_if_fwol.h" +#include "wlan_objmgr_vdev_obj.h" + +QDF_STATUS ucfg_fwol_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + + status = fwol_cfg_on_psoc_enable(psoc); + if (QDF_IS_STATUS_ERROR(status)) + fwol_err("Failed to initialize FWOL CFG"); + + tgt_fwol_register_ev_handler(psoc); + + return status; +} + +void ucfg_fwol_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + /* Clear the FWOL CFG Structure */ + + tgt_fwol_unregister_ev_handler(psoc); +} + +/** + * fwol_psoc_object_created_notification(): fwol psoc create handler + * @psoc: psoc which is going to created by objmgr + * @arg: argument for vdev create handler + * + * Register this api with objmgr to detect psoc is created + * + * Return QDF_STATUS status in case of success else return error + */ +static QDF_STATUS +fwol_psoc_object_created_notification(struct wlan_objmgr_psoc *psoc, void *arg) +{ + QDF_STATUS status; + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = qdf_mem_malloc(sizeof(*fwol_obj)); + if (!fwol_obj) + return QDF_STATUS_E_NOMEM; + + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_FWOL, + fwol_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + fwol_err("Failed to attach psoc_ctx with psoc"); + qdf_mem_free(fwol_obj); + return status; + } + + tgt_fwol_register_rx_ops(&fwol_obj->rx_ops); + target_if_fwol_register_tx_ops(&fwol_obj->tx_ops); + + return status; +} + +/** + * fwol_psoc_object_destroyed_notification(): fwol psoc delete handler + * @psoc: psoc which is going to delete by objmgr + * @arg: argument for vdev delete handler + * + * Register this api with objmgr to detect psoc is deleted + * + * Return QDF_STATUS status in case of success else return error + */ +static QDF_STATUS fwol_psoc_object_destroyed_notification( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + QDF_STATUS status; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) + return QDF_STATUS_E_NOMEM; + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_FWOL, + fwol_obj); + if (QDF_IS_STATUS_ERROR(status)) { + fwol_err("Failed to detach psoc_ctx from psoc"); + return status; + } + + qdf_mem_free(fwol_obj); + + return status; +} + +QDF_STATUS ucfg_fwol_init(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_FWOL, + fwol_psoc_object_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + fwol_err("unable to register psoc create handle"); + return status; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_FWOL, + fwol_psoc_object_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + fwol_err("unable to register psoc create handle"); + wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_FWOL, + fwol_psoc_object_created_notification, + NULL); + } + + return status; +} + +void ucfg_fwol_deinit(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_FWOL, + fwol_psoc_object_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) + fwol_err("unable to unregister psoc destroy handle"); + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_FWOL, + fwol_psoc_object_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) + fwol_err("unable to unregister psoc create handle"); +} + +QDF_STATUS +ucfg_fwol_get_coex_config_params(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_coex_config *coex_config) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get fwol obj"); + return QDF_STATUS_E_FAILURE; + } + + *coex_config = fwol_obj->cfg.coex_config; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_fwol_get_thermal_temp(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_thermal_temp *thermal_info) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get fwol obj"); + return QDF_STATUS_E_FAILURE; + } + + *thermal_info = fwol_obj->cfg.thermal_temp_cfg; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_fwol_get_neighbor_report_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_neighbor_report_cfg + *fwol_neighbor_report_cfg) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!fwol_neighbor_report_cfg) + return QDF_STATUS_E_FAILURE; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get fwol obj"); + fwol_init_neighbor_report_cfg(psoc, fwol_neighbor_report_cfg); + status = QDF_STATUS_E_FAILURE; + } else { + *fwol_neighbor_report_cfg = fwol_obj->cfg.neighbor_report_cfg; + } + + return status; +} + +QDF_STATUS +ucfg_fwol_is_neighbor_report_req_supported(struct wlan_objmgr_psoc *psoc, + bool *neighbor_report_req) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get fwol obj"); + *neighbor_report_req = + !!(cfg_get(psoc, + CFG_OFFLOAD_11K_ENABLE_BITMASK) & + OFFLOAD_11K_BITMASK_NEIGHBOR_REPORT_REQUEST); + return QDF_STATUS_E_FAILURE; + } + + *neighbor_report_req = + !!(fwol_obj->cfg.neighbor_report_cfg.enable_bitmask & + OFFLOAD_11K_BITMASK_NEIGHBOR_REPORT_REQUEST); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_fwol_get_ie_whitelist(struct wlan_objmgr_psoc *psoc, bool *ie_whitelist) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get fwol obj"); + return QDF_STATUS_E_FAILURE; + } + + *ie_whitelist = fwol_obj->cfg.ie_whitelist_cfg.ie_whitelist; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_fwol_set_ie_whitelist(struct wlan_objmgr_psoc *psoc, bool ie_whitelist) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get fwol obj"); + return QDF_STATUS_E_FAILURE; + } + + fwol_obj->cfg.ie_whitelist_cfg.ie_whitelist = ie_whitelist; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_ani_enabled(struct wlan_objmgr_psoc *psoc, + bool *ani_enabled) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *ani_enabled = fwol_obj->cfg.ani_enabled; + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS ucfg_fwol_get_hw_assist_config(struct wlan_objmgr_psoc *psoc, + bool *disable_hw_assist) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *disable_hw_assist = fwol_obj->cfg.disable_hw_assist; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_get_enable_rts_sifsbursting(struct wlan_objmgr_psoc *psoc, + bool *enable_rts_sifsbursting) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *enable_rts_sifsbursting = fwol_obj->cfg.enable_rts_sifsbursting; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_get_enable_sifs_burst(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sifs_burst) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *enable_sifs_burst = fwol_obj->cfg.enable_sifs_burst; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_get_max_mpdus_inampdu(struct wlan_objmgr_psoc *psoc, + uint8_t *max_mpdus_inampdu) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *max_mpdus_inampdu = fwol_obj->cfg.max_mpdus_inampdu; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_get_enable_phy_reg_retention(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_phy_reg_retention) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *enable_phy_reg_retention = fwol_obj->cfg.enable_phy_reg_retention; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_fwol_get_all_whitelist_params(struct wlan_objmgr_psoc *psoc, + struct wlan_fwol_ie_whitelist *whitelist) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get fwol obj"); + return QDF_STATUS_E_FAILURE; + } + + *whitelist = fwol_obj->cfg.ie_whitelist_cfg; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_get_upper_brssi_thresh(struct wlan_objmgr_psoc *psoc, + uint16_t *upper_brssi_thresh) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *upper_brssi_thresh = fwol_obj->cfg.upper_brssi_thresh; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_get_lower_brssi_thresh(struct wlan_objmgr_psoc *psoc, + uint16_t *lower_brssi_thresh) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *lower_brssi_thresh = fwol_obj->cfg.lower_brssi_thresh; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_get_enable_dtim_1chrx(struct wlan_objmgr_psoc *psoc, + bool *enable_dtim_1chrx) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *enable_dtim_1chrx = fwol_obj->cfg.enable_dtim_1chrx; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_get_alternative_chainmask_enabled(struct wlan_objmgr_psoc *psoc, + bool *alternative_chainmask_enabled) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get fwol obj"); + return QDF_STATUS_E_FAILURE; + } + + *alternative_chainmask_enabled = + fwol_obj->cfg.alternative_chainmask_enabled; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_get_smart_chainmask_enabled(struct wlan_objmgr_psoc *psoc, + bool *smart_chainmask_enabled) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *smart_chainmask_enabled = + fwol_obj->cfg.smart_chainmask_enabled; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_rts_profile(struct wlan_objmgr_psoc *psoc, + uint16_t *get_rts_profile) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *get_rts_profile = fwol_obj->cfg.get_rts_profile; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_enable_fw_log_level(struct wlan_objmgr_psoc *psoc, + uint16_t *enable_fw_log_level) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *enable_fw_log_level = fwol_obj->cfg.enable_fw_log_level; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_enable_fw_log_type(struct wlan_objmgr_psoc *psoc, + uint16_t *enable_fw_log_type) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *enable_fw_log_type = fwol_obj->cfg.enable_fw_log_type; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_enable_fw_module_log_level( + struct wlan_objmgr_psoc *psoc, + uint8_t **enable_fw_module_log_level, + uint8_t *enable_fw_module_log_level_num) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *enable_fw_module_log_level = fwol_obj->cfg.enable_fw_module_log_level; + *enable_fw_module_log_level_num = + fwol_obj->cfg.enable_fw_module_log_level_num; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_wow_get_enable_fw_module_log_level( + struct wlan_objmgr_psoc *psoc, + uint8_t **enable_fw_wow_module_log_level, + uint8_t *enable_fw_wow_module_log_level_num) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *enable_fw_wow_module_log_level = + fwol_obj->cfg.enable_fw_mod_wow_log_level; + *enable_fw_wow_module_log_level_num = + fwol_obj->cfg.enable_fw_mod_wow_log_level_num; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_sap_xlna_bypass(struct wlan_objmgr_psoc *psoc, + bool *sap_xlna_bypass) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *sap_xlna_bypass = fwol_obj->cfg.sap_xlna_bypass; + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_RA_FILTERING +QDF_STATUS ucfg_fwol_set_is_rate_limit_enabled(struct wlan_objmgr_psoc *psoc, + bool is_rate_limit_enabled) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + fwol_obj->cfg.is_rate_limit_enabled = is_rate_limit_enabled; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_is_rate_limit_enabled(struct wlan_objmgr_psoc *psoc, + bool *is_rate_limit_enabled) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *is_rate_limit_enabled = fwol_obj->cfg.is_rate_limit_enabled; + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef WLAN_FEATURE_TSF +QDF_STATUS ucfg_fwol_get_tsf_gpio_pin(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_gpio_pin) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + *tsf_gpio_pin = cfg_default(CFG_SET_TSF_GPIO_PIN); + return QDF_STATUS_E_FAILURE; + } + + *tsf_gpio_pin = fwol_obj->cfg.tsf_gpio_pin; + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_TSF_PLUS +QDF_STATUS ucfg_fwol_get_tsf_ptp_options(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_ptp_options) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + *tsf_ptp_options = cfg_default(CFG_SET_TSF_PTP_OPT); + return QDF_STATUS_E_FAILURE; + } + + *tsf_ptp_options = fwol_obj->cfg.tsf_ptp_options; + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ +QDF_STATUS +ucfg_fwol_get_tsf_irq_host_gpio_pin(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_irq_host_gpio_pin) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + *tsf_irq_host_gpio_pin = + cfg_default(CFG_SET_TSF_IRQ_HOST_GPIO_PIN); + return QDF_STATUS_E_FAILURE; + } + + *tsf_irq_host_gpio_pin = fwol_obj->cfg.tsf_irq_host_gpio_pin; + return QDF_STATUS_SUCCESS; +} + +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC +QDF_STATUS +ucfg_fwol_get_tsf_sync_host_gpio_pin(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_sync_host_gpio_pin) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + *tsf_sync_host_gpio_pin = + cfg_default(CFG_SET_TSF_SYNC_HOST_GPIO_PIN); + return QDF_STATUS_E_FAILURE; + } + + *tsf_sync_host_gpio_pin = fwol_obj->cfg.tsf_sync_host_gpio_pin; + return QDF_STATUS_SUCCESS; +} + +#endif +#endif +#else +QDF_STATUS ucfg_fwol_get_tsf_gpio_pin(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_gpio_pin) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_tsf_ptp_options(struct wlan_objmgr_psoc *psoc, + uint32_t *tsf_ptp_options) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +#endif + +QDF_STATUS ucfg_fwol_get_lprx_enable(struct wlan_objmgr_psoc *psoc, + bool *lprx_enable) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + *lprx_enable = cfg_default(CFG_LPRX); + return QDF_STATUS_E_FAILURE; + } + + *lprx_enable = fwol_obj->cfg.lprx_enable; + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_SAE +bool ucfg_fwol_get_sae_enable(struct wlan_objmgr_psoc *psoc) +{ + return cfg_get(psoc, CFG_IS_SAE_ENABLED); +} + +#else +bool ucfg_fwol_get_sae_enable(struct wlan_objmgr_psoc *psoc) +{ + return false; +} +#endif + +bool ucfg_fwol_get_gcmp_enable(struct wlan_objmgr_psoc *psoc) +{ + return cfg_get(psoc, CFG_ENABLE_GCMP); +} + +QDF_STATUS ucfg_fwol_get_enable_tx_sch_delay(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_tx_sch_delay) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + *enable_tx_sch_delay = cfg_default(CFG_TX_SCH_DELAY); + return QDF_STATUS_E_FAILURE; + } + + *enable_tx_sch_delay = fwol_obj->cfg.enable_tx_sch_delay; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_enable_secondary_rate(struct wlan_objmgr_psoc *psoc, + uint32_t *enable_secondary_rate) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + *enable_secondary_rate = cfg_default(CFG_ENABLE_SECONDARY_RATE); + return QDF_STATUS_E_FAILURE; + } + + *enable_secondary_rate = fwol_obj->cfg.enable_secondary_rate; + return QDF_STATUS_SUCCESS; +} + +#ifdef DHCP_SERVER_OFFLOAD +QDF_STATUS +ucfg_fwol_get_enable_dhcp_server_offload(struct wlan_objmgr_psoc *psoc, + bool *enable_dhcp_server_offload) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *enable_dhcp_server_offload = fwol_obj->cfg.enable_dhcp_server_offload; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_dhcp_max_num_clients(struct wlan_objmgr_psoc *psoc, + uint32_t *dhcp_max_num_clients) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *dhcp_max_num_clients = fwol_obj->cfg.dhcp_max_num_clients; + return QDF_STATUS_SUCCESS; +} + +#endif + +QDF_STATUS +ucfg_fwol_get_all_adaptive_dwelltime_params( + struct wlan_objmgr_psoc *psoc, + struct adaptive_dwelltime_params *dwelltime_params) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get fwol obj"); + return QDF_STATUS_E_FAILURE; + } + + *dwelltime_params = fwol_obj->cfg.dwelltime_params; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_fwol_get_adaptive_dwell_mode_enabled( + struct wlan_objmgr_psoc *psoc, + bool *adaptive_dwell_mode_enabled) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *adaptive_dwell_mode_enabled = + fwol_obj->cfg.dwelltime_params.is_enabled; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_fwol_get_global_adapt_dwelltime_mode(struct wlan_objmgr_psoc *psoc, + uint8_t *global_adapt_dwelltime_mode) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *global_adapt_dwelltime_mode = + fwol_obj->cfg.dwelltime_params.dwelltime_mode; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_fwol_get_adapt_dwell_lpf_weight(struct wlan_objmgr_psoc *psoc, + uint8_t *adapt_dwell_lpf_weight) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *adapt_dwell_lpf_weight = fwol_obj->cfg.dwelltime_params.lpf_weight; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_adapt_dwell_passive_mon_intval( + struct wlan_objmgr_psoc *psoc, + uint8_t *adapt_dwell_passive_mon_intval) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *adapt_dwell_passive_mon_intval = + fwol_obj->cfg.dwelltime_params.passive_mon_intval; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_fwol_get_adapt_dwell_wifi_act_threshold( + struct wlan_objmgr_psoc *psoc, + uint8_t *adapt_dwell_wifi_act_threshold) +{ + struct wlan_fwol_psoc_obj *fwol_obj; + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL obj"); + return QDF_STATUS_E_FAILURE; + } + + *adapt_dwell_wifi_act_threshold = + fwol_obj->cfg.dwelltime_params.wifi_act_threshold; + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_ELNA +QDF_STATUS ucfg_fwol_set_elna_bypass(struct wlan_objmgr_vdev *vdev, + struct set_elna_bypass_request *req) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_fwol_psoc_obj *fwol_obj; + struct wlan_fwol_tx_ops *tx_ops; + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + fwol_err("NULL pointer for psoc"); + return QDF_STATUS_E_INVAL; + } + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL Obj"); + return QDF_STATUS_E_INVAL; + } + + tx_ops = &fwol_obj->tx_ops; + if (tx_ops->set_elna_bypass) + status = tx_ops->set_elna_bypass(psoc, req); + else + status = QDF_STATUS_E_IO; + + return status; +} + +QDF_STATUS ucfg_fwol_get_elna_bypass(struct wlan_objmgr_vdev *vdev, + struct get_elna_bypass_request *req, + void (*callback)(void *context, + struct get_elna_bypass_response *response), + void *context) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_fwol_psoc_obj *fwol_obj; + struct wlan_fwol_tx_ops *tx_ops; + struct wlan_fwol_callbacks *cbs; + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + fwol_err("NULL pointer for psoc"); + return QDF_STATUS_E_INVAL; + } + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL Obj"); + return QDF_STATUS_E_INVAL; + } + + cbs = &fwol_obj->cbs; + cbs->get_elna_bypass_callback = callback; + cbs->get_elna_bypass_context = context; + + tx_ops = &fwol_obj->tx_ops; + if (tx_ops->get_elna_bypass) + status = tx_ops->get_elna_bypass(psoc, req); + else + status = QDF_STATUS_E_IO; + + return status; +} +#endif /* WLAN_FEATURE_ELNA */ + +#ifdef WLAN_SEND_DSCP_UP_MAP_TO_FW +QDF_STATUS ucfg_fwol_send_dscp_up_map_to_fw(struct wlan_objmgr_vdev *vdev, + uint32_t *dscp_to_up_map) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_fwol_psoc_obj *fwol_obj; + struct wlan_fwol_tx_ops *tx_ops; + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + fwol_err("NULL pointer for psoc"); + return QDF_STATUS_E_INVAL; + } + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + fwol_err("Failed to get FWOL Obj"); + return QDF_STATUS_E_INVAL; + } + + tx_ops = &fwol_obj->tx_ops; + if (tx_ops && tx_ops->send_dscp_up_map_to_fw) + status = tx_ops->send_dscp_up_map_to_fw(psoc, dscp_to_up_map); + else + status = QDF_STATUS_E_IO; + + return status; +} +#endif /* WLAN_SEND_DSCP_UP_MAP_TO_FW */ + +QDF_STATUS ucfg_fwol_configure_global_params(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev) +{ + QDF_STATUS status; + bool value; + + /* Configure HW assist feature in FW */ + status = ucfg_fwol_get_hw_assist_config(psoc, &value); + if (QDF_IS_STATUS_ERROR(status)) + return status; + status = fwol_configure_hw_assist(pdev, value); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + return status; +} + +QDF_STATUS ucfg_fwol_configure_vdev_params(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev, + enum QDF_OPMODE device_mode, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/interop_issues_ap/core/inc/wlan_interop_issues_ap_api.h b/drivers/staging/qcacld-3.0/components/interop_issues_ap/core/inc/wlan_interop_issues_ap_api.h new file mode 100644 index 0000000000000000000000000000000000000000..b3de4ca59f325066808f7293c906e4841437b196 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/interop_issues_ap/core/inc/wlan_interop_issues_ap_api.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_interop_issues_ap_api.h + * + * This header file provide API declarations required for interop issues + * ap global context specific to offload + */ + +#ifndef __WLAN_INTEROP_ISSUES_AP_API_H__ +#define __WLAN_INTEROP_ISSUES_AP_API_H__ + +#ifdef WLAN_FEATURE_INTEROP_ISSUES_AP +#include +#include +#include +#include +#include + +#define interop_issues_ap_debug(args ...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_INTEROP_ISSUES_AP, ## args) +#define interop_issues_ap_err(args ...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_INTEROP_ISSUES_AP, ## args) + +/** + * struct interop_issues_ap_psoc_priv_obj - psoc private object + * @lock: qdf spin lock + * @soc: pointer to psoc object + * @cbs: interop issues ap ps event callbacks + * @tx_ops: interop issues ap ps tx ops + */ +struct interop_issues_ap_psoc_priv_obj { + qdf_spinlock_t lock; + struct wlan_objmgr_psoc *soc; + struct wlan_interop_issues_ap_callbacks cbs; + struct wlan_interop_issues_ap_tx_ops tx_ops; +}; + +/** + * wlan_interop_issues_ap_psoc_enable() - interop issues ap psoc enable + * @psoc: the pointer to psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_interop_issues_ap_psoc_enable(struct wlan_objmgr_psoc *soc); + +/** + * wlan_interop_issues_ap_psoc_disable() - interop issues ap psoc disable + * @psoc: the pointer to psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_interop_issues_ap_psoc_disable(struct wlan_objmgr_psoc *soc); + +/** + * wlan_interop_issues_ap_init() - API to init component + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS wlan_interop_issues_ap_init(void); + +/** + * wlan_interop_issues_ap_deinit() - API to deinit component + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS wlan_interop_issues_ap_deinit(void); + +/** + * interop_issues_ap_get_psoc_priv_obj() - get priv object from psoc object + * @psoc: pointer to psoc object + * + * Return: pointer to interop issues ap psoc private object + */ +static inline +struct interop_issues_ap_psoc_priv_obj *interop_issues_ap_get_psoc_priv_obj( + struct wlan_objmgr_psoc *psoc) +{ + struct interop_issues_ap_psoc_priv_obj *obj; + + if (!psoc) + return NULL; + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_INTEROP_ISSUES_AP); + + return obj; +} + +/** + * interop_issues_ap_psoc_get_tx_ops() - get TX ops from the private object + * @psoc: pointer to psoc object + * + * Return: pointer to TX op callback + */ +static inline +struct wlan_interop_issues_ap_tx_ops *interop_issues_ap_psoc_get_tx_ops( + struct wlan_objmgr_psoc *psoc) +{ + struct interop_issues_ap_psoc_priv_obj *interop_issues_ap_priv; + + if (!psoc) + return NULL; + + interop_issues_ap_priv = interop_issues_ap_get_psoc_priv_obj(psoc); + if (!interop_issues_ap_priv) { + interop_issues_ap_err("psoc private object is null"); + return NULL; + } + + return &interop_issues_ap_priv->tx_ops; +} + +/** + * interop_issues_ap_psoc_get_cbs() - get RX ops from private object + * @psoc: pointer to psoc object + * + * Return: pointer to RX op callback + */ +static inline +struct wlan_interop_issues_ap_callbacks *interop_issues_ap_psoc_get_cbs( + struct wlan_objmgr_psoc *psoc) +{ + struct interop_issues_ap_psoc_priv_obj *interop_issues_ap_priv; + + if (!psoc) + return NULL; + + interop_issues_ap_priv = interop_issues_ap_get_psoc_priv_obj(psoc); + if (!interop_issues_ap_priv) { + interop_issues_ap_err("psoc private object is null"); + return NULL; + } + + return &interop_issues_ap_priv->cbs; +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/components/interop_issues_ap/core/src/wlan_interop_issues_ap_api.c b/drivers/staging/qcacld-3.0/components/interop_issues_ap/core/src/wlan_interop_issues_ap_api.c new file mode 100644 index 0000000000000000000000000000000000000000..88d08fbeb19896b3191d5179ca80222c9c24d59f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/interop_issues_ap/core/src/wlan_interop_issues_ap_api.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_interop_issues_ap_api.c + */ +#include +#include +#include + +/** + * interop_issues_ap_psoc_obj_created_notification() - PSOC obj create callback + * @psoc: PSOC object + * @arg_list: Variable argument list + * + * This callback is registered with object manager during initialization to + * get notified when the object is created. + * + * Return: Success or Failure + */ +static QDF_STATUS +interop_issues_ap_psoc_obj_created_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct interop_issues_ap_psoc_priv_obj *interop_issues_ap_obj; + + interop_issues_ap_obj = qdf_mem_malloc(sizeof(*interop_issues_ap_obj)); + if (!interop_issues_ap_obj) + return QDF_STATUS_E_NOMEM; + + qdf_spinlock_create(&interop_issues_ap_obj->lock); + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_INTEROP_ISSUES_AP, + interop_issues_ap_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + interop_issues_ap_err("obj attach with psoc failed"); + goto interop_issues_ap_psoc_attach_failed; + } + + target_if_interop_issues_ap_register_tx_ops(psoc, + &interop_issues_ap_obj->tx_ops); + + return QDF_STATUS_SUCCESS; + +interop_issues_ap_psoc_attach_failed: + qdf_spinlock_destroy(&interop_issues_ap_obj->lock); + qdf_mem_free(interop_issues_ap_obj); + return status; +} + +/** + * interop_issues_ap_psoc_obj_destroyed_notification() - obj delete callback + * @psoc: PSOC object + * @arg_list: Variable argument list + * + * This callback is registered with object manager during initialization to + * get notified when the object is deleted. + * + * Return: Success or Failure + */ +static QDF_STATUS +interop_issues_ap_psoc_obj_destroyed_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct interop_issues_ap_psoc_priv_obj *interop_issues_ap_obj; + + interop_issues_ap_obj = interop_issues_ap_get_psoc_priv_obj(psoc); + + if (!interop_issues_ap_obj) { + interop_issues_ap_err("interop_issues_ap_obj is NULL"); + return QDF_STATUS_E_FAULT; + } + target_if_interop_issues_ap_unregister_tx_ops(psoc, + &interop_issues_ap_obj->tx_ops); + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_INTEROP_ISSUES_AP, + interop_issues_ap_obj); + if (QDF_IS_STATUS_ERROR(status)) + interop_issues_ap_err("interop_issues_ap_obj detach failed"); + + qdf_spinlock_destroy(&interop_issues_ap_obj->lock); + qdf_mem_free(interop_issues_ap_obj); + + return status; +} + +QDF_STATUS wlan_interop_issues_ap_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + return target_if_interop_issues_ap_register_event_handler(psoc); +} + +QDF_STATUS wlan_interop_issues_ap_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + return target_if_interop_issues_ap_unregister_event_handler(psoc); +} + +QDF_STATUS wlan_interop_issues_ap_init(void) +{ + QDF_STATUS status; + + /* register psoc create handler functions. */ + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_INTEROP_ISSUES_AP, + interop_issues_ap_psoc_obj_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + interop_issues_ap_err("register create handler failed"); + return status; + } + + /* register psoc delete handler functions. */ + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_INTEROP_ISSUES_AP, + interop_issues_ap_psoc_obj_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + interop_issues_ap_err("register destroy handler failed"); + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_INTEROP_ISSUES_AP, + interop_issues_ap_psoc_obj_created_notification, + NULL); + } + + return status; +} + +QDF_STATUS wlan_interop_issues_ap_deinit(void) +{ + QDF_STATUS ret = QDF_STATUS_SUCCESS, status; + + /* unregister psoc delete handler functions. */ + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_INTEROP_ISSUES_AP, + interop_issues_ap_psoc_obj_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + interop_issues_ap_err("unregister destroy handler failed"); + ret = status; + } + + /* unregister psoc create handler functions. */ + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_INTEROP_ISSUES_AP, + interop_issues_ap_psoc_obj_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + interop_issues_ap_err("unregister create handler failed"); + ret = status; + } + + return ret; +} diff --git a/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/inc/wlan_interop_issues_ap_public_structs.h b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/inc/wlan_interop_issues_ap_public_structs.h new file mode 100644 index 0000000000000000000000000000000000000000..b4332c8f0a4ca0d382e1a067d4c0da54034d2dfe --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/inc/wlan_interop_issues_ap_public_structs.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains interop issues ap structure definations + */ + +#ifndef _WLAN_INTEROP_ISSUES_AP_STRUCTS_H_ +#define _WLAN_INTEROP_ISSUES_AP_STRUCTS_H_ + +#ifdef WLAN_FEATURE_INTEROP_ISSUES_AP +#include +#include + +#define MAX_INTEROP_ISSUES_AP_NUM 20 + +/** + * struct wlan_interop_issues_ap_info - interop issues ap info + * @detect_enable: the flag to enable detect issue ap + * @count: the number of interop issues ap + * @rap_items: interop issues ap items + */ +struct wlan_interop_issues_ap_info { + bool detect_enable; + uint32_t count; + struct qdf_mac_addr rap_items[MAX_INTEROP_ISSUES_AP_NUM]; +}; + +/** + * struct wlan_interop_issues_ap_event - interop issues ap event + * @pdev: pdev object + * @psoc: psoc object + * @pdev_id: pdev id number + * @rap_addr: interop issues ap mac address + */ +struct wlan_interop_issues_ap_event { + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + uint32_t pdev_id; + struct qdf_mac_addr rap_addr; +}; + +/** + * struct wlan_interop_issues_ap_callbacks - interop issues ap callbacks + * @os_if_interop_issues_ap_event_handler: OS IF callback for handling events + */ +struct wlan_interop_issues_ap_callbacks { + void (*os_if_interop_issues_ap_event_handler) + (struct wlan_interop_issues_ap_event *event); +}; + +/** + * struct wlan_interop_issues_ap_tx_ops - structure of tx func pointers + * @set_rap_ps: handler for TX operations for the interop issues ap ps config + */ +struct wlan_interop_issues_ap_tx_ops { + QDF_STATUS (*set_rap_ps)(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_info *rap); +}; +#endif +#endif /* _WLAN_INTEROP_ISSUES_AP_STRUCTS_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/inc/wlan_interop_issues_ap_tgt_api.h b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/inc/wlan_interop_issues_ap_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..6f5b2c8d6354fbbced0e0fa4300fde1a5b5af67c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/inc/wlan_interop_issues_ap_tgt_api.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_interop_issues_ap_tgt_api.h + * + * This header file provide with API declarations to interface with Southbound + */ +#ifndef __WLAN_INTEROP_ISSUES_AP_TGT_API_H__ +#define __WLAN_INTEROP_ISSUES_AP_TGT_API_H__ + +#ifdef WLAN_FEATURE_INTEROP_ISSUES_AP +/** + * tgt_interop_issues_ap_info_callback() - interop issues ap info callback + * @psoc: the pointer to psoc object manager + * @rap: the interop issues ap mac address + * + * Return: QDF_STATUS + */ +QDF_STATUS +tgt_interop_issues_ap_info_callback(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_event *rap); + +/** + * tgt_set_interop_issues_ap_req(): API to set interop issues ap to lmac + * @rx_ops: rx ops struct + * @rap: the pointer to interop issues ap info + * + * Return: status of operation + */ +QDF_STATUS +tgt_set_interop_issues_ap_req(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_info *rap); +#endif +#endif /* __WLAN_INTEROP_ISSUES_AP_TGT_API_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/inc/wlan_interop_issues_ap_ucfg_api.h b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/inc/wlan_interop_issues_ap_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..ce90efdf56f36d69c7be2423b4ed22fc5a9aa477 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/inc/wlan_interop_issues_ap_ucfg_api.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_interop_issues_ap_ucfg_api.h + * + * This header file maintain API declaration required for northbound interaction + */ + +#ifndef __WLAN_INTEROP_ISSUES_AP_UCFG_API_H__ +#define __WLAN_INTEROP_ISSUES_AP_UCFG_API_H__ + +#ifdef WLAN_FEATURE_INTEROP_ISSUES_AP +#include +#include +#include + +/** + * ucfg_interop_issues_ap_psoc_enable() - interop issues ap component enable + * @psoc: the point to psoc object + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_interop_issues_ap_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_interop_issues_ap_psoc_disable() - interop issues ap component disable + * @psoc: the point to psoc object + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_interop_issues_ap_psoc_disable(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_interop_issues_ap_init() - interop issues ap component initialization + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_interop_issues_ap_init(void); + +/** + * ucfg_interop_issues_ap_deinit() - interop issues ap component de-init + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_interop_issues_ap_deinit(void); + +/** + * ucfg_register_interop_issues_ap_callback() - API to register callback + * @cbs: pointer to callback structure + * + * Return: none + */ +void ucfg_register_interop_issues_ap_callback(struct wlan_objmgr_pdev *pdev, + struct wlan_interop_issues_ap_callbacks *cbs); + +/** + * ucfg_set_interop_issues_ap_config() - API to set interop issues ap + * @psoc: the pointer of psoc object + * @rap: the pointer of interop issues ap info + * + * Return: none + */ +QDF_STATUS ucfg_set_interop_issues_ap_config(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_info *rap); +#else +static inline +QDF_STATUS ucfg_interop_issues_ap_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_interop_issues_ap_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_interop_issues_ap_init(void) { return QDF_STATUS_SUCCESS; } + +static inline +QDF_STATUS ucfg_interop_issues_ap_deinit(void) { return QDF_STATUS_SUCCESS; } +#endif /* WLAN_FEATURE_INTEROP_ISSUES_AP */ +#endif /* __WLAN_RAP_PS_UCFG_API_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/src/wlan_interop_issues_ap_tgt_api.c b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/src/wlan_interop_issues_ap_tgt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..e58bd4a7f7d080ae96401150a842ce203558229c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/src/wlan_interop_issues_ap_tgt_api.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC:wlan_interop_issues_ap_tgt_api.c + * + * This file provide API definitions to update interop issues ap from interface + */ +#include +#include +#include +#include +#include + +static QDF_STATUS wlan_interop_issues_ap_flush_cbk(struct scheduler_msg *msg) +{ + if (msg->bodyptr) { + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS wlan_interop_issues_ap_info_cbk(struct scheduler_msg *msg) +{ + struct wlan_interop_issues_ap_event *data; + struct wlan_interop_issues_ap_callbacks *cbs; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + data = msg->bodyptr; + data->pdev = wlan_objmgr_get_pdev_by_id(data->psoc, + data->pdev_id, + WLAN_INTEROP_ISSUES_AP_ID); + if (!data->pdev) { + interop_issues_ap_err("pdev is null."); + status = QDF_STATUS_E_FAILURE; + goto err; + } + + cbs = interop_issues_ap_psoc_get_cbs(data->psoc); + if (cbs && cbs->os_if_interop_issues_ap_event_handler) + cbs->os_if_interop_issues_ap_event_handler(msg->bodyptr); + + wlan_objmgr_pdev_release_ref(data->pdev, WLAN_INTEROP_ISSUES_AP_ID); +err: + qdf_mem_free(data); + msg->bodyptr = NULL; + return status; +} + +QDF_STATUS tgt_interop_issues_ap_info_callback(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_event *rap) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + struct wlan_interop_issues_ap_event *data; + + data = qdf_mem_malloc(sizeof(*data)); + if (!data) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(data, rap, sizeof(*data)); + + msg.bodyptr = data; + msg.callback = wlan_interop_issues_ap_info_cbk; + msg.flush_callback = wlan_interop_issues_ap_flush_cbk; + + status = scheduler_post_message(QDF_MODULE_ID_INTEROP_ISSUES_AP, + QDF_MODULE_ID_INTEROP_ISSUES_AP, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + interop_issues_ap_err("scheduler msg posting failed"); + qdf_mem_free(msg.bodyptr); + msg.bodyptr = NULL; + } + + return status; +} + +QDF_STATUS tgt_set_interop_issues_ap_req(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_info *rap) +{ + struct interop_issues_ap_psoc_priv_obj *obj; + + obj = interop_issues_ap_get_psoc_priv_obj(psoc); + if (!obj || !obj->tx_ops.set_rap_ps) + return QDF_STATUS_E_NULL_VALUE; + + return obj->tx_ops.set_rap_ps(psoc, rap); +} diff --git a/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/src/wlan_interop_issues_ap_ucfg_api.c b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/src/wlan_interop_issues_ap_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..d40611e49383bd2c92dca540e68c1c2f0b19568d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/interop_issues_ap/dispatcher/src/wlan_interop_issues_ap_ucfg_api.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains interop issues ap north bound interface definitions + */ +#include +#include +#include +#include +#include + +QDF_STATUS +ucfg_set_interop_issues_ap_config(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_info *rap) +{ + return tgt_set_interop_issues_ap_req(psoc, rap); +} + +void ucfg_register_interop_issues_ap_callback(struct wlan_objmgr_pdev *pdev, + struct wlan_interop_issues_ap_callbacks *cb) +{ + struct wlan_objmgr_psoc *psoc; + struct interop_issues_ap_psoc_priv_obj *obj; + + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) { + interop_issues_ap_err("psoc object is NULL"); + return; + } + + obj = interop_issues_ap_get_psoc_priv_obj(psoc); + if (!obj) { + interop_issues_ap_err("interop issues ap priv obj is NULL"); + return; + } + + obj->cbs.os_if_interop_issues_ap_event_handler = + cb->os_if_interop_issues_ap_event_handler; +} + +QDF_STATUS ucfg_interop_issues_ap_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + return wlan_interop_issues_ap_psoc_enable(psoc); +} + +QDF_STATUS ucfg_interop_issues_ap_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + return wlan_interop_issues_ap_psoc_disable(psoc); +} + +QDF_STATUS ucfg_interop_issues_ap_init(void) +{ + return wlan_interop_issues_ap_init(); +} + +QDF_STATUS ucfg_interop_issues_ap_deinit(void) +{ + return wlan_interop_issues_ap_deinit(); +} diff --git a/drivers/staging/qcacld-3.0/components/ipa/core/inc/wlan_ipa_core.h b/drivers/staging/qcacld-3.0/components/ipa/core/inc/wlan_ipa_core.h new file mode 100644 index 0000000000000000000000000000000000000000..8b88b6921d912d412a368d0810d742881f501020 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/core/inc/wlan_ipa_core.h @@ -0,0 +1,835 @@ +/* + * Copyright (c) 2013-2019, 2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_IPA_CORE_H_ +#define _WLAN_IPA_CORE_H_ + +#ifdef IPA_OFFLOAD + +#include "wlan_ipa_priv.h" +#include "wlan_ipa_public_struct.h" + +/** + * wlan_ipa_is_enabled() - Is IPA enabled? + * @ipa_cfg: IPA config + * + * Return: true if IPA is enabled, false otherwise + */ +static inline bool wlan_ipa_is_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, WLAN_IPA_ENABLE_MASK); +} + +/** + * wlan_ipa_uc_is_enabled() - Is IPA UC enabled? + * @ipa_cfg: IPA config + * + * Return: true if IPA UC is enabled, false otherwise + */ +static inline bool wlan_ipa_uc_is_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, WLAN_IPA_UC_ENABLE_MASK); +} + +/** + * wlan_ipa_is_rt_debugging_enabled() - Is IPA RT debugging enabled? + * @ipa_cfg: IPA config + * + * Return: true if IPA RT debugging is enabled, false otherwise + */ +static inline +bool wlan_ipa_is_rt_debugging_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, + WLAN_IPA_REAL_TIME_DEBUGGING); +} + +/** + * wlan_ipa_setup - IPA initialize and setup + * @ipa_ctx: IPA priv obj + * @ipa_cfg: IPA config + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_setup(struct wlan_ipa_priv *ipa_ctx, + struct wlan_ipa_config *ipa_cfg); + +/** + * wlan_ipa_get_obj_context - Get IPA OBJ context + * + * Return: IPA context + */ +struct wlan_ipa_priv *wlan_ipa_get_obj_context(void); + +/** + * wlan_ipa_cleanup - IPA cleanup + * @ipa_ctx: IPA priv obj + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_cleanup(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_uc_enable_pipes() - Enable IPA uC pipes + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_uc_enable_pipes(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_uc_disable_pipes() - Disable IPA uC pipes + * @ipa_ctx: IPA context + * @force_disable: If true, immediately disable IPA pipes. If false, wait for + * pending IPA WLAN TX completions + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_uc_disable_pipes(struct wlan_ipa_priv *ipa_ctx, + bool force_disable); + +/** + * wlan_ipa_is_tx_pending() - Check if IPA TX Completions are pending + * @ipa_ctx: IPA context + * + * Return: bool + */ +bool wlan_ipa_is_tx_pending(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_set_perf_level() - Set IPA performance level + * @ipa_ctx: IPA context + * @tx_packets: Number of packets transmitted in the last sample period + * @rx_packets: Number of packets received in the last sample period + * + * Return: QDF STATUS + */ +QDF_STATUS wlan_ipa_set_perf_level(struct wlan_ipa_priv *ipa_ctx, + uint64_t tx_packets, uint64_t rx_packets); + +/** + * wlan_ipa_init_perf_level() - Initialize IPA performance level + * @ipa_ctx: IPA context + * + * If IPA clock scaling is disabled, initialize perf level to maximum. + * Else set the lowest level to start with. + * + * Return: QDF STATUS + */ +QDF_STATUS wlan_ipa_init_perf_level(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_get_iface() - Get IPA interface + * @ipa_ctx: IPA context + * @mode: Interface device mode + * + * Return: IPA interface address + */ +struct wlan_ipa_iface_context +*wlan_ipa_get_iface(struct wlan_ipa_priv *ipa_ctx, uint8_t mode); + +/** + * wlan_ipa_get_iface_by_mode_netdev() - Get IPA interface + * @ipa_ctx: IPA context + * @ndev: Interface netdev pointer + * @mode: Interface device mode + * + * Return: IPA interface address + */ +struct wlan_ipa_iface_context * +wlan_ipa_get_iface_by_mode_netdev(struct wlan_ipa_priv *ipa_ctx, + qdf_netdev_t ndev, uint8_t mode); + +#ifndef CONFIG_IPA_WDI_UNIFIED_API + +/** + * wlan_ipa_is_rm_enabled() - Is IPA RM enabled? + * @ipa_cfg: IPA config + * + * Return: true if IPA RM is enabled, false otherwise + */ +static inline bool wlan_ipa_is_rm_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, WLAN_IPA_RM_ENABLE_MASK); +} + +/** + * wlan_ipa_is_clk_scaling_enabled() - Is IPA clock scaling enabled? + * @ipa_cfg: IPA config + * + * Return: true if IPA clock scaling is enabled, false otherwise + */ +static inline +bool wlan_ipa_is_clk_scaling_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, + WLAN_IPA_CLK_SCALING_ENABLE_MASK | + WLAN_IPA_RM_ENABLE_MASK); +} + +/** + * wlan_ipa_wdi_rm_request_resource() - IPA WDI request resource + * @ipa_ctx: IPA context + * @res_name: IPA RM resource name + * + * Return: 0 on success, negative errno on error + */ +static inline +int wlan_ipa_wdi_rm_request_resource(struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_resource_name_t res_name) +{ + return qdf_ipa_rm_request_resource(res_name); +} + +/** + * wlan_ipa_wdi_rm_release_resource() - IPA WDI release resource + * @ipa_ctx: IPA context + * @res_name: IPA RM resource name + * + * Return: 0 on success, negative errno on error + */ +static inline +int wlan_ipa_wdi_rm_release_resource(struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_resource_name_t res_name) +{ + return qdf_ipa_rm_release_resource(res_name); +} + +/** + * wlan_ipa_wdi_rm_request() - Request resource from IPA + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wdi_rm_request(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_wdi_rm_try_release() - Attempt to release IPA resource + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wdi_rm_try_release(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_wdi_setup_rm() - Setup IPA resource management + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wdi_setup_rm(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_wdi_destroy_rm() - Destroy IPA resources + * @ipa_ctx: IPA context + * + * Destroys all resources associated with the IPA resource manager + * + * Return: None + */ +void wlan_ipa_wdi_destroy_rm(struct wlan_ipa_priv *ipa_ctx); + +static inline +int wlan_ipa_wdi_rm_notify_completion(qdf_ipa_rm_event_t event, + qdf_ipa_rm_resource_name_t res_name) +{ + return qdf_ipa_rm_notify_completion(event, res_name); +} + +static inline +int wlan_ipa_wdi_rm_inactivity_timer_destroy( + qdf_ipa_rm_resource_name_t res_name) +{ + return qdf_ipa_rm_inactivity_timer_destroy(res_name); +} + +bool wlan_ipa_is_rm_released(struct wlan_ipa_priv *ipa_ctx); + +#else /* CONFIG_IPA_WDI_UNIFIED_API */ + +/** + * wlan_ipa_is_rm_enabled() - Is IPA RM enabled? + * @ipa_cfg: IPA config + * + * IPA RM is deprecated and IPA PM is involved. WLAN driver + * has no control over IPA PM and thus we could regard IPA + * RM as always enabled for power efficiency. + * + * Return: true + */ +static inline bool wlan_ipa_is_rm_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return true; +} + +/** + * wlan_ipa_is_clk_scaling_enabled() - Is IPA clock scaling enabled? + * @ipa_cfg: IPA config + * + * Return: true if IPA clock scaling is enabled, false otherwise + */ +static inline +bool wlan_ipa_is_clk_scaling_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, + WLAN_IPA_CLK_SCALING_ENABLE_MASK); +} + +static inline int wlan_ipa_wdi_rm_request_resource( + struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_resource_name_t res_name) +{ + return 0; +} + +static inline int wlan_ipa_wdi_rm_release_resource( + struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_resource_name_t res_name) +{ + return 0; +} + +static inline QDF_STATUS wlan_ipa_wdi_setup_rm(struct wlan_ipa_priv *ipa_ctx) +{ + return 0; +} + +static inline int wlan_ipa_wdi_destroy_rm(struct wlan_ipa_priv *ipa_ctx) +{ + return 0; +} + +static inline QDF_STATUS wlan_ipa_wdi_rm_request(struct wlan_ipa_priv *ipa_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS wlan_ipa_wdi_rm_try_release(struct wlan_ipa_priv + *ipa_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +int wlan_ipa_wdi_rm_notify_completion(qdf_ipa_rm_event_t event, + qdf_ipa_rm_resource_name_t res_name) +{ + return 0; +} + +static inline +int wlan_ipa_wdi_rm_inactivity_timer_destroy( + qdf_ipa_rm_resource_name_t res_name) +{ + return 0; +} + +static inline +bool wlan_ipa_is_rm_released(struct wlan_ipa_priv *ipa_ctx) +{ + return true; +} + +#endif /* CONFIG_IPA_WDI_UNIFIED_API */ + +#ifdef FEATURE_METERING + +#ifndef WDI3_STATS_UPDATE +/** + * wlan_ipa_uc_op_metering() - IPA uC operation for stats and quota limit + * @ipa_ctx: IPA context + * @op_msg: operation message received from firmware + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS wlan_ipa_uc_op_metering(struct wlan_ipa_priv *ipa_ctx, + struct op_msg_type *op_msg); +#else +static inline +QDF_STATUS wlan_ipa_uc_op_metering(struct wlan_ipa_priv *ipa_ctx, + struct op_msg_type *op_msg) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * wlan_ipa_wdi_meter_notifier_cb() - SSR wrapper for + * __wlan_ipa_wdi_meter_notifier_cb + * @priv: pointer to private data registered with IPA (we register a + * pointer to the global IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +void wlan_ipa_wdi_meter_notifier_cb(qdf_ipa_wdi_meter_evt_type_t evt, + void *data); + +/** + * wlan_ipa_init_metering() - IPA metering stats completion event reset + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS enumeration + */ +void wlan_ipa_init_metering(struct wlan_ipa_priv *ipa_ctx); + +#ifdef WDI3_STATS_UPDATE +/** + * wlan_ipa_update_tx_stats() - send embedded tx traffic in bytes to IPA + * @ipa_ctx: IPA context + * @sta_tx: tx in bytes on sta interface + * @sap_tx: tx in bytes on sap interface + * + * Return: void + */ +void wlan_ipa_update_tx_stats(struct wlan_ipa_priv *ipa_ctx, uint64_t sta_tx, + uint64_t sap_tx); +#else +static inline void wlan_ipa_update_tx_stats(struct wlan_ipa_priv *ipa_ctx, + uint64_t sta_tx, uint64_t sap_tx) +{ +} +#endif /* WDI3_STATS_UPDATE */ + +#else + +static inline +QDF_STATUS wlan_ipa_uc_op_metering(struct wlan_ipa_priv *ipa_ctx, + struct op_msg_type *op_msg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void wlan_ipa_wdi_meter_notifier_cb(void) +{ +} + +static inline void wlan_ipa_init_metering(struct wlan_ipa_priv *ipa_ctx) +{ +} + +static inline void wlan_ipa_update_tx_stats(struct wlan_ipa_priv *ipa_ctx, + uint64_t sta_tx, uint64_t sap_tx) +{ +} +#endif /* FEATURE_METERING */ + +/** + * wlan_ipa_uc_stat() - Print IPA uC stats + * @ipa_ctx: IPA context + * + * Return: None + */ +void wlan_ipa_uc_stat(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_uc_info() - Print IPA uC resource and session information + * @ipa_ctx: IPA context + * + * Return: None + */ +void wlan_ipa_uc_info(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_print_fw_wdi_stats() - Print FW IPA WDI stats + * @ipa_ctx: IPA context + * + * Return: None + */ +void wlan_ipa_print_fw_wdi_stats(struct wlan_ipa_priv *ipa_ctx, + struct ipa_uc_fw_stats *uc_fw_stat); + +/** + * wlan_ipa_uc_stat_request() - Get IPA stats from IPA + * @ipa_ctx: IPA context + * @reason: STAT REQ Reason + * + * Return: None + */ +void wlan_ipa_uc_stat_request(struct wlan_ipa_priv *ipa_ctx, uint8_t reason); + +/** + * wlan_ipa_uc_stat_query() - Query the IPA stats + * @ipa_ctx: IPA context + * @ipa_tx_diff: tx packet count diff from previous tx packet count + * @ipa_rx_diff: rx packet count diff from previous rx packet count + * + * Return: None + */ +void wlan_ipa_uc_stat_query(struct wlan_ipa_priv *ipa_ctx, + uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff); + +/** + * wlan_ipa_dump_info() - dump IPA IPA struct + * @ipa_ctx: IPA context + * + * Dump entire struct ipa_ctx + * + * Return: none + */ +void wlan_ipa_dump_info(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_uc_rt_debug_host_dump - dump rt debug buffer + * @ipa_ctx: IPA context + * + * If rt debug enabled, dump debug buffer contents based on requirement + * + * Return: none + */ +void wlan_ipa_uc_rt_debug_host_dump(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_uc_rt_debug_destructor() - called by data packet free + * @nbuff: packet pinter + * + * when free data packet, will be invoked by wlan client and will increase + * free counter + * + * Return: none + */ +void wlan_ipa_uc_rt_debug_destructor(qdf_nbuf_t nbuff); + +/** + * wlan_ipa_uc_rt_debug_deinit() - remove resources to handle rt debugging + * @ipa_ctx: IPA context + * + * free all rt debugging resources + * + * Return: none + */ +void wlan_ipa_uc_rt_debug_deinit(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_uc_rt_debug_init() - initialize resources to handle rt debugging + * @ipa_ctx: IPA context + * + * alloc and initialize all rt debugging resources + * + * Return: none + */ +void wlan_ipa_uc_rt_debug_init(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_reg_sap_xmit_cb() - Register upper layer SAP cb to transmit + * @ipa_ctx: IPA context + * @cb: callback + * + * Return: None + */ +static inline +void wlan_ipa_reg_sap_xmit_cb(struct wlan_ipa_priv *ipa_ctx, + wlan_ipa_softap_xmit cb) +{ + ipa_ctx->softap_xmit = cb; +} + +/** + * wlan_ipa_reg_send_to_nw_cb() - Register cb to send IPA Rx packet to network + * @ipa_ctx: IPA context + * @cb: callback + * + * Return: None + */ +static inline +void wlan_ipa_reg_send_to_nw_cb(struct wlan_ipa_priv *ipa_ctx, + wlan_ipa_send_to_nw cb) +{ + ipa_ctx->send_to_nw = cb; +} + +#ifdef IPA_LAN_RX_NAPI_SUPPORT +/** + * wlan_ipa_reg_rps_enable_cb() - Register callback to enable RPS + * @ipa_ctx: IPA context + * @cb: callback + * + * Return: None + */ +static inline +void wlan_ipa_reg_rps_enable_cb(struct wlan_ipa_priv *ipa_ctx, + wlan_ipa_rps_enable cb) +{ + ipa_ctx->rps_enable = cb; +} + +/** + * ipa_set_rps_enable(): Enable/disable RPS for all interfaces of specific mode + * @ipa_ctx: IPA context + * @mode: mode of interface for which RPS needs to be enabled + * @enable: Set true to enable RPS + * + * Return: None + */ +void ipa_set_rps(struct wlan_ipa_priv *ipa_ctx, enum QDF_OPMODE mode, + bool enable); + +/** + * ipa_set_rps_per_vdev(): Enable/disable RPS for a specific vdev + * @ipa_ctx: IPA context + * @vdev_id: vdev id for which RPS needs to be enabled + * @enable: Set true to enable RPS + * + * Return: None + */ +static inline +void ipa_set_rps_per_vdev(struct wlan_ipa_priv *ipa_ctx, uint8_t vdev_id, + bool enable) +{ + if (ipa_ctx->rps_enable) + ipa_ctx->rps_enable(vdev_id, enable); +} + +/** + * wlan_ipa_handle_multiple_sap_evt() - Handle multiple SAP connect/disconnect + * @ipa_ctx: IPA global context + * @type: IPA event type. + * + * This function is used to disable pipes when multiple SAP are connected and + * enable pipes back when only one SAP is connected. + * + * Return: None + */ +void wlan_ipa_handle_multiple_sap_evt(struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_wlan_event type); + +#else +static inline +void ipa_set_rps(struct wlan_ipa_priv *ipa_ctx, enum QDF_OPMODE mode, + bool enable) +{ +} + +static inline +void ipa_set_rps_per_vdev(struct wlan_ipa_priv *ipa_ctx, uint8_t vdev_id, + bool enable) +{ +} + +static inline +void wlan_ipa_handle_multiple_sap_evt(struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_wlan_event type) +{ +} + +#endif + +/** + * wlan_ipa_set_mcc_mode() - Set MCC mode + * @ipa_ctx: IPA context + * @mcc_mode: 1=MCC/0=SCC + * + * Return: void + */ +void wlan_ipa_set_mcc_mode(struct wlan_ipa_priv *ipa_ctx, bool mcc_mode); + +/** + * wlan_ipa_set_dfs_cac_tx() - Set DFS cac tx block + * @ipa_ctx: IPA context + * @tx_block: dfs cac tx block + * + * Return: void + */ +static inline +void wlan_ipa_set_dfs_cac_tx(struct wlan_ipa_priv *ipa_ctx, bool tx_block) +{ + ipa_ctx->dfs_cac_block_tx = tx_block; +} + +/** + * wlan_ipa_set_ap_ibss_fwd() - Set AP intra bss forward + * @ipa_ctx: IPA context + * @intra_bss: enable or disable ap intra bss forward + * + * Return: void + */ +static inline +void wlan_ipa_set_ap_ibss_fwd(struct wlan_ipa_priv *ipa_ctx, bool intra_bss) +{ + ipa_ctx->ap_intrabss_fwd = intra_bss; +} + +/** + * wlan_ipa_uc_ol_init() - Initialize IPA uC offload + * @ipa_ctx: IPA context + * @osdev: Parent device instance + * + * This function is called to update IPA pipe configuration with resources + * allocated by wlan driver (cds_pre_enable) before enabling it in FW + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_uc_ol_init(struct wlan_ipa_priv *ipa_ctx, + qdf_device_t osdev); + +/** + * wlan_ipa_uc_ol_deinit() - Disconnect IPA TX and RX pipes + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_uc_ol_deinit(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_flush() - flush IPA exception path SKB's + * @ipa_ctx: IPA context + * + * Return: None + */ +void wlan_ipa_flush(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_suspend() - Suspend IPA + * @ipa_ctx: IPA context + * + * Return: QDF STATUS + */ +QDF_STATUS wlan_ipa_suspend(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_resume() - Resume IPA + * @ipa_ctx: IPA context + * + * Return: QDF STATUS + */ +QDF_STATUS wlan_ipa_resume(struct wlan_ipa_priv *ipa_ctx); + +#ifndef QCA_LL_TX_FLOW_CONTROL_V2 +/** + * wlan_ipa_send_mcc_scc_msg() - Send IPA WLAN_SWITCH_TO_MCC/SCC message + * @ipa_ctx: IPA context + * @mcc_mode: 0=MCC/1=SCC + * + * Return: QDF STATUS + */ +QDF_STATUS wlan_ipa_send_mcc_scc_msg(struct wlan_ipa_priv *ipa_ctx, + bool mcc_mode); +#else +static inline +QDF_STATUS wlan_ipa_send_mcc_scc_msg(struct wlan_ipa_priv *ipa_ctx, + bool mcc_mode) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void wlan_ipa_mcc_work_handler(void *data) +{ +} +#endif + +/** + * wlan_ipa_wlan_evt() - IPA event handler + * @net_dev: Interface net device + * @device_mode: Net interface device mode + * @session_id: session id for the event + * @type: event enum of type ipa_wlan_event + * @mac_address: MAC address associated with the event + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wlan_evt(qdf_netdev_t net_dev, uint8_t device_mode, + uint8_t session_id, + enum wlan_ipa_wlan_event ipa_event_type, + uint8_t *mac_addr); + +/** + * wlan_ipa_uc_smmu_map() - Map / Unmap DMA buffer to IPA UC + * @map: Map / unmap operation + * @num_buf: Number of buffers in array + * @buf_arr: Buffer array of DMA mem mapping info + * + * This API maps/unmaps WLAN-IPA buffers if SMMU S1 translation + * is enabled. + * + * Return: Status of map operation + */ +int wlan_ipa_uc_smmu_map(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr); + +/** + * wlan_ipa_is_fw_wdi_activated() - Is FW WDI actived? + * @ipa_ctx: IPA contex + * + * Return: true if FW WDI actived, false otherwise + */ +bool wlan_ipa_is_fw_wdi_activated(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_uc_cleanup_sta - disconnect and cleanup sta iface + * @ipa_ctx: IPA context + * @net_dev: Interface net device + * + * Send disconnect sta event to IPA driver and cleanup IPA iface + * if not yet done + * + * Return: void + */ +void wlan_ipa_uc_cleanup_sta(struct wlan_ipa_priv *ipa_ctx, + qdf_netdev_t net_dev); + +/** + * wlan_ipa_uc_disconnect_ap() - send ap disconnect event + * @ipa_ctx: IPA context + * @net_dev: Interface net device + * + * Send disconnect ap event to IPA driver + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_uc_disconnect_ap(struct wlan_ipa_priv *ipa_ctx, + qdf_netdev_t net_dev); + +/** + * wlan_ipa_cleanup_dev_iface() - Clean up net dev IPA interface + * @ipa_ctx: IPA context + * @net_dev: Interface net device + * + * Return: None + */ +void wlan_ipa_cleanup_dev_iface(struct wlan_ipa_priv *ipa_ctx, + qdf_netdev_t net_dev); + +/** + * wlan_ipa_uc_ssr_cleanup() - handle IPA UC clean up during SSR + * @ipa_ctx: IPA context + * + * Return: None + */ +void wlan_ipa_uc_ssr_cleanup(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_fw_rejuvenate_send_msg() - send fw rejuvenate message to IPA driver + * @ipa_ctx: IPA context + * + * Return: void + */ +void wlan_ipa_fw_rejuvenate_send_msg(struct wlan_ipa_priv *ipa_ctx); + +/** + * wlan_ipa_flush_pending_vdev_events() - flush pending vdev ipa events + * @ipa_ctx: IPA context + * vdev_id: vdev id + * + * This function is to flush vdev wlan ipa pending events + * + * Return: None + */ +void wlan_ipa_flush_pending_vdev_events(struct wlan_ipa_priv *ipa_ctx, + uint8_t vdev_id); +#endif /* IPA_OFFLOAD */ +#endif /* _WLAN_IPA_CORE_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/core/inc/wlan_ipa_main.h b/drivers/staging/qcacld-3.0/components/ipa/core/inc/wlan_ipa_main.h new file mode 100644 index 0000000000000000000000000000000000000000..1e066f8f227e989666e985bc1bbd8fbf48d526b4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/core/inc/wlan_ipa_main.h @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare various api which shall be used by + * IPA user configuration and target interface + */ + +#ifndef _WLAN_IPA_MAIN_H_ +#define _WLAN_IPA_MAIN_H_ + +#ifdef IPA_OFFLOAD + +#include +#include +#include +#include + +#define ipa_fatal(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_IPA, params) +#define ipa_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_IPA, params) +#define ipa_warn(params...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_IPA, params) +#define ipa_info(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_IPA, params) +#define ipa_debug(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_IPA, params) + +#define ipa_nofl_fatal(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_IPA, params) +#define ipa_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_IPA, params) +#define ipa_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_IPA, params) +#define ipa_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_IPA, params) +#define ipa_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_IPA, params) + +#define ipa_fatal_rl(params...) QDF_TRACE_FATAL_RL(QDF_MODULE_ID_IPA, params) +#define ipa_err_rl(params...) QDF_TRACE_ERROR_RL(QDF_MODULE_ID_IPA, params) +#define ipa_warn_rl(params...) QDF_TRACE_WARN_RL(QDF_MODULE_ID_IPA, params) +#define ipa_info_rl(params...) QDF_TRACE_INFO_RL(QDF_MODULE_ID_IPA, params) +#define ipa_debug_rl(params...) QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_IPA, params) + +#define IPA_ENTER() \ + QDF_TRACE_ENTER(QDF_MODULE_ID_IPA, "enter") +#define IPA_EXIT() \ + QDF_TRACE_EXIT(QDF_MODULE_ID_IPA, "exit") + +/** + * ipa_check_hw_present() - get IPA hw status + * + * ipa_uc_reg_rdyCB is not directly designed to check + * ipa hw status. This is an undocumented function which + * has confirmed with IPA team. + * + * Return: true - ipa hw present + * false - ipa hw not present + */ +bool ipa_check_hw_present(void); + +/** + * wlan_get_pdev_ipa_obj() - private API to get ipa pdev object + * @pdev: pdev object + * + * Return: ipa object + */ +static inline struct wlan_ipa_priv * +ipa_pdev_get_priv_obj(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *pdev_obj; + + pdev_obj = (struct wlan_ipa_priv *) + wlan_objmgr_pdev_get_comp_private_obj(pdev, + WLAN_UMAC_COMP_IPA); + + return pdev_obj; +} + +/** + * ipa_is_hw_support() - Is IPA HW support? + * + * Return: true if IPA HW is present or false otherwise + */ +bool ipa_is_hw_support(void); + +/** + * ipa_config_mem_alloc() - IPA config allocation + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ipa_config_mem_alloc(void); + +/** + * ipa_config_mem_free() - IPA config mem free + * + * Return: None + */ +void ipa_config_mem_free(void); + +/** + * ipa_config_is_enabled() - Is IPA config enabled? + * + * Return: true if IPA is enabled in IPA config + */ +bool ipa_config_is_enabled(void); + +/** + * ipa_config_is_uc_enabled() - Is IPA uC config enabled? + * + * Return: true if IPA uC is enabled in IPA config + */ +bool ipa_config_is_uc_enabled(void); + +/** + * ipa_obj_setup() - IPA obj initialization and setup + * @ipa_ctx: IPA obj context + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ipa_obj_setup(struct wlan_ipa_priv *ipa_ctx); + +/** + * ipa_obj_cleanup() - IPA obj cleanup + * @ipa_ctx: IPA obj context + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ipa_obj_cleanup(struct wlan_ipa_priv *ipa_ctx); + +/** + * ipa_send_uc_offload_enable_disable() - wdi enable/disable notify to fw + * @pdev: objmgr pdev object + * @req: ipa offload control request + * + * Return: QDF status success or failure + */ +QDF_STATUS ipa_send_uc_offload_enable_disable(struct wlan_objmgr_pdev *pdev, + struct ipa_uc_offload_control_params *req); + +/** + * ipa_set_dp_handle() - set dp soc handle + * @psoc: psoc handle + * @dp_soc: dp soc handle + * + * Return: None + */ +void ipa_set_dp_handle(struct wlan_objmgr_psoc *psoc, void *dp_soc); + +/** + * ipa_set_pdev_id() - set dp pdev id + * @psoc: psoc handle + * @pdev_id: dp txrx physical device id + * + * Return: None + */ +void ipa_set_pdev_id(struct wlan_objmgr_psoc *psoc, uint8_t pdev_id); + +/** + * ipa_rm_set_perf_level() - set ipa rm perf level + * @pdev: pdev handle + * @tx_packets: packets transmitted in the last sample period + * @rx_packets: packets received in the last sample period + * + * Return: QDF_STATUS + */ +QDF_STATUS ipa_rm_set_perf_level(struct wlan_objmgr_pdev *pdev, + uint64_t tx_packets, uint64_t rx_packets); + +/** + * ipa_uc_info() - Print IPA uC resource and session information + * @pdev: pdev obj + * + * Return: None + */ +void ipa_uc_info(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_uc_stat() - Print IPA uC stats + * @pdev: pdev obj + * + * Return: None + */ +void ipa_uc_stat(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_uc_rt_debug_host_dump() - IPA rt debug host dump + * @pdev: pdev obj + * + * Return: None + */ +void ipa_uc_rt_debug_host_dump(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_dump_info() - Dump IPA context information + * @pdev: pdev obj + * + * Return: None + */ +void ipa_dump_info(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_uc_stat_request() - Get IPA stats from IPA. + * @pdev: pdev obj + * @reason: STAT REQ Reason + * + * Return: None + */ +void ipa_uc_stat_request(struct wlan_objmgr_pdev *pdev, + uint8_t reason); + +/** + * ipa_uc_stat_query() - Query the IPA stats + * @pdev: pdev obj + * @ipa_tx_diff: tx packet count diff from previous tx packet count + * @ipa_rx_diff: rx packet count diff from previous rx packet count + * + * Return: None + */ +void ipa_uc_stat_query(struct wlan_objmgr_pdev *pdev, + uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff); + +/** + * ipa_reg_sap_xmit_cb() - Register upper layer SAP cb to transmit + * @pdev: pdev obj + * @cb: callback + * + * Return: None + */ +void ipa_reg_sap_xmit_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_softap_xmit cb); + +/** + * ipa_reg_send_to_nw_cb() - Register cb to send IPA Rx packet to network + * @pdev: pdev obj + * @cb: callback + * + * Return: None + */ +void ipa_reg_send_to_nw_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_send_to_nw cb); + +#ifdef IPA_LAN_RX_NAPI_SUPPORT +/** + * ipa_reg_rps_enable_cb() - Register cb to enable RPS + * @pdev: pdev obj + * @cb: callback + * + * Return: None + */ +void ipa_reg_rps_enable_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_rps_enable cb); +#endif + +/** + * ipa_set_mcc_mode() - Set MCC mode + * @pdev: pdev obj + * @mcc_mode: 0=MCC/1=SCC + * + * Return: void + */ +void ipa_set_mcc_mode(struct wlan_objmgr_pdev *pdev, bool mcc_mode); + +/** + * ipa_set_dfs_cac_tx() - Set DFS cac tx block + * @pdev: pdev obj + * @tx_block: dfs cac tx block + * + * Return: void + */ +void ipa_set_dfs_cac_tx(struct wlan_objmgr_pdev *pdev, bool tx_block); + +/** + * ipa_set_ap_ibss_fwd() - Set AP intra bss forward + * @pdev: pdev obj + * @intra_bss: enable or disable ap intra bss forward + * + * Return: void + */ +void ipa_set_ap_ibss_fwd(struct wlan_objmgr_pdev *pdev, bool intra_bss); + +/** + * ipa_uc_force_pipe_shutdown() - Force IPA pipe shutdown + * @pdev: pdev obj + * + * Return: void + */ +void ipa_uc_force_pipe_shutdown(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_flush() - flush IPA exception path SKB's + * @pdev: pdev obj + * + * Return: None + */ +void ipa_flush(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_suspend() - Suspend IPA + * @pdev: pdev obj + * + * Return: QDF STATUS + */ +QDF_STATUS ipa_suspend(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_resume() - Resume IPA + * @pdev: pdev obj + * + * Return: None + */ +QDF_STATUS ipa_resume(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_uc_ol_init() - Initialize IPA uC offload + * @pdev: pdev obj + * @osdev: OS dev + * + * Return: QDF STATUS + */ +QDF_STATUS ipa_uc_ol_init(struct wlan_objmgr_pdev *pdev, + qdf_device_t osdev); + +/** + * ucfg_ipa_uc_ol_deinit() - Deinitialize IPA uC offload + * @pdev: pdev obj + * + * Return: QDF STATUS + */ +QDF_STATUS ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_is_tx_pending() - Check if IPA WLAN TX completions are pending + * @pdev: pdev obj + * + * Return: bool if pending TX for IPA. + */ +bool ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_send_mcc_scc_msg() - Send IPA WLAN_SWITCH_TO_MCC/SCC message + * @pdev: pdev obj + * @mcc_mode: 0=MCC/1=SCC + * + * Return: QDF STATUS + */ +QDF_STATUS ipa_send_mcc_scc_msg(struct wlan_objmgr_pdev *pdev, + bool mcc_mode); + +/** + * ipa_wlan_evt() - IPA event handler + * @pdev: pdev obj + * @net_dev: Interface net device + * @device_mode: Net interface device mode + * @session_id: session id for the event + * @type: event enum of type ipa_wlan_event + * @mac_address: MAC address associated with the event + * + * Return: QDF_STATUS + */ +QDF_STATUS ipa_wlan_evt(struct wlan_objmgr_pdev *pdev, qdf_netdev_t net_dev, + uint8_t device_mode, uint8_t session_id, + enum wlan_ipa_wlan_event ipa_event_type, + uint8_t *mac_addr); + +/** + * ipa_uc_smmu_map() - Map / Unmap DMA buffer to IPA UC + * @map: Map / unmap operation + * @num_buf: Number of buffers in array + * @buf_arr: Buffer array of DMA mem mapping info + * + * Return: Status of map operation + */ +int ipa_uc_smmu_map(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr); + +/** + * ipa_is_fw_wdi_activated - Is FW WDI activated? + * @pdev: pdev obj + * + * Return: true if FW WDI activated, false otherwise + */ +bool ipa_is_fw_wdi_activated(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_uc_cleanup_sta() - disconnect and cleanup sta iface + * @pdev: pdev obj + * @net_dev: Interface net device + * + * Send disconnect sta event to IPA driver and cleanup IPA iface, + * if not yet done + * + * Return: void + */ +void ipa_uc_cleanup_sta(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev); + +/** + * ipa_uc_disconnect_ap() - send ap disconnect event + * @pdev: pdev obj + * @net_dev: Interface net device + * + * Send disconnect ap event to IPA driver + * + * Return: QDF_STATUS + */ +QDF_STATUS ipa_uc_disconnect_ap(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev); + +/** + * ipa_cleanup_dev_iface() - Clean up net dev IPA interface + * @pdev: pdev obj + * @net_dev: Interface net device + * + * Return: None + */ +void ipa_cleanup_dev_iface(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev); + +/** + * ipa_uc_ssr_cleanup() - handle IPA UC cleanup during SSR + * @pdev: pdev obj + * + * Return: None + */ +void ipa_uc_ssr_cleanup(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_fw_rejuvenate_send_msg() - send fw rejuvenate message to IPA driver + * @pdev: pdev obj + * + * Return: None + */ +void ipa_fw_rejuvenate_send_msg(struct wlan_objmgr_pdev *pdev); + +/** + * ipa_component_config_update() - update ipa config from psoc + * @psoc: psoc obj + * + * Return: None + */ +void ipa_component_config_update(struct wlan_objmgr_psoc *psoc); + +/** + * ipa_get_tx_buf_count() - get IPA config tx buffer count + * + * Return: IPA config tx buffer count + */ +uint32_t ipa_get_tx_buf_count(void); + +/** + * ipa_update_tx_stats() - Update embedded tx traffic in bytes to IPA + * @pdev: pdev obj + * @sta_tx: tx in bytes on sta vdev + * @ap_tx: tx in bytes on sap vdev + * + * Return: None + */ +void ipa_update_tx_stats(struct wlan_objmgr_pdev *pdev, uint64_t sta_tx, + uint64_t ap_tx); + +/** + * ipa_flush_pending_vdev_events() - flush pending vdev wlan ipa events + * @pdev: pdev obj + * @vdev_id: vdev id + * + * Return: None + */ +void ipa_flush_pending_vdev_events(struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id); + +#else /* Not IPA_OFFLOAD */ +typedef QDF_STATUS (*wlan_ipa_softap_xmit)(qdf_nbuf_t nbuf, qdf_netdev_t dev); +typedef void (*wlan_ipa_send_to_nw)(qdf_nbuf_t nbuf, qdf_netdev_t dev); +typedef void (*wlan_ipa_rps_enable)(uint8_t vdev_id, bool enable); + +#endif /* IPA_OFFLOAD */ +#endif /* end of _WLAN_IPA_MAIN_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/core/inc/wlan_ipa_priv.h b/drivers/staging/qcacld-3.0/components/ipa/core/inc/wlan_ipa_priv.h new file mode 100644 index 0000000000000000000000000000000000000000..d3555b5e442bc4d51d6a29aa53a4000afe7d0aa6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/core/inc/wlan_ipa_priv.h @@ -0,0 +1,779 @@ +/* + * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare various struct, macros which are used privately in IPA + * component. + */ + +#ifndef _WLAN_IPA_PRIV_STRUCT_H_ +#define _WLAN_IPA_PRIV_STRUCT_H_ + +#ifdef IPA_OFFLOAD + +#ifdef CONFIG_IPA_WDI_UNIFIED_API +#include +#else +#include +#endif + +#include +#include +#include +#include +#include "qdf_delayed_work.h" +#include +#include "wlan_ipa_public_struct.h" + +#define WLAN_IPA_RX_INACTIVITY_MSEC_DELAY 1000 +#define WLAN_IPA_UC_WLAN_8023_HDR_SIZE 14 + +#define WLAN_IPA_UC_NUM_WDI_PIPE 2 +#define WLAN_IPA_UC_MAX_PENDING_EVENT 33 + +#define WLAN_IPA_UC_DEBUG_DUMMY_MEM_SIZE 32000 +#define WLAN_IPA_UC_RT_DEBUG_PERIOD 300 +#define WLAN_IPA_UC_RT_DEBUG_BUF_COUNT 30 +#define WLAN_IPA_UC_RT_DEBUG_FILL_INTERVAL 10000 + +#define WLAN_IPA_WLAN_HDR_DES_MAC_OFFSET 0 +#define WLAN_IPA_MAX_IFACE MAX_IPA_IFACE +#define WLAN_IPA_CLIENT_MAX_IFACE 3 +#define WLAN_IPA_MAX_SYSBAM_PIPE 4 +#define WLAN_IPA_MAX_SESSION 5 +#define WLAN_IPA_MAX_STA_COUNT 41 + +#define WLAN_IPA_RX_PIPE WLAN_IPA_MAX_IFACE +#define WLAN_IPA_ENABLE_MASK BIT(0) +#define WLAN_IPA_PRE_FILTER_ENABLE_MASK BIT(1) +#define WLAN_IPA_IPV6_ENABLE_MASK BIT(2) +#define WLAN_IPA_RM_ENABLE_MASK BIT(3) +#define WLAN_IPA_CLK_SCALING_ENABLE_MASK BIT(4) +#define WLAN_IPA_UC_ENABLE_MASK BIT(5) +#define WLAN_IPA_UC_STA_ENABLE_MASK BIT(6) +#define WLAN_IPA_REAL_TIME_DEBUGGING BIT(8) + +#define WLAN_IPA_MAX_BANDWIDTH 800 + +#define WLAN_IPA_MAX_PENDING_EVENT_COUNT 20 + +#define IPA_WLAN_RX_SOFTIRQ_THRESH 32 + +#define WLAN_IPA_UC_BW_MONITOR_LEVEL 3 + +/** + * enum - IPA UC operation message + * + * @WLAN_IPA_UC_OPCODE_TX_SUSPEND: IPA WDI TX pipe suspend + * @WLAN_IPA_UC_OPCODE_TX_RESUME: IPA WDI TX pipe resume + * @WLAN_IPA_UC_OPCODE_RX_SUSPEND: IPA WDI RX pipe suspend + * @WLAN_IPA_UC_OPCODE_RX_RESUME: IPA WDI RX pipe resume + * @WLAN_IPA_UC_OPCODE_STATS: IPA UC stats + * @WLAN_IPA_UC_OPCODE_SHARING_STATS: IPA UC sharing stats + * @WLAN_IPA_UC_OPCODE_QUOTA_RSP: IPA UC quota response + * @WLAN_IPA_UC_OPCODE_QUOTA_IND: IPA UC quota indication + * @WLAN_IPA_UC_OPCODE_UC_READY: IPA UC ready indication + * @WLAN_IPA_UC_OPCODE_MAX: IPA UC max operation code + */ +enum wlan_ipa_uc_op_code { + WLAN_IPA_UC_OPCODE_TX_SUSPEND = 0, + WLAN_IPA_UC_OPCODE_TX_RESUME = 1, + WLAN_IPA_UC_OPCODE_RX_SUSPEND = 2, + WLAN_IPA_UC_OPCODE_RX_RESUME = 3, + WLAN_IPA_UC_OPCODE_STATS = 4, +#ifdef FEATURE_METERING + WLAN_IPA_UC_OPCODE_SHARING_STATS = 5, + WLAN_IPA_UC_OPCODE_QUOTA_RSP = 6, + WLAN_IPA_UC_OPCODE_QUOTA_IND = 7, +#endif + WLAN_IPA_UC_OPCODE_UC_READY = 8, + /* keep this last */ + WLAN_IPA_UC_OPCODE_MAX +}; + +/** + * enum - Reason codes for stat query + * + * @WLAN_IPA_UC_STAT_REASON_NONE: Initial value + * @WLAN_IPA_UC_STAT_REASON_DEBUG: For debug/info + * @WLAN_IPA_UC_STAT_REASON_BW_CAL: For bandwidth calibration + * @WLAN_IPA_UC_STAT_REASON_DUMP_INFO: For debug info dump + */ +enum { + WLAN_IPA_UC_STAT_REASON_NONE, + WLAN_IPA_UC_STAT_REASON_DEBUG, + WLAN_IPA_UC_STAT_REASON_BW_CAL +}; + +/** + * enum wlan_ipa_rm_state - IPA resource manager state + * @WLAN_IPA_RM_RELEASED: PROD pipe resource released + * @WLAN_IPA_RM_GRANT_PENDING: PROD pipe resource requested but not granted yet + * @WLAN_IPA_RM_GRANTED: PROD pipe resource granted + */ +enum wlan_ipa_rm_state { + WLAN_IPA_RM_RELEASED, + WLAN_IPA_RM_GRANT_PENDING, + WLAN_IPA_RM_GRANTED, +}; + +/** + * enum wlan_ipa_forward_type: Type of forward packet received from IPA + * @WLAN_IPA_FORWARD_PKT_NONE: No forward packet + * @WLAN_IPA_FORWARD_PKT_LOCAL_STACK: Packet forwarded to kernel network stack + * @WLAN_IPA_FORWARD_PKT_DISCARD: Discarded packet before sending to kernel + */ +enum wlan_ipa_forward_type { + WLAN_IPA_FORWARD_PKT_NONE = 0, + WLAN_IPA_FORWARD_PKT_LOCAL_STACK = 1, + WLAN_IPA_FORWARD_PKT_DISCARD = 2 +}; + +/** + * enum wlan_ipa_bw_level -ipa bandwidth level + * @WLAN_IPA_BW_LEVEL_LOW: vote for low bandwidth + * @WLAN_IPA_BW_LEVEL_MEDIUM: vote for medium bandwidth + * @WLAN_IPA_BW_LEVEL_HIGH: vote for high bandwidth + */ +enum wlan_ipa_bw_level { + WLAN_IPA_BW_LEVEL_LOW, + WLAN_IPA_BW_LEVEL_MEDIUM, + WLAN_IPA_BW_LEVEL_HIGH, +}; + +/** + * struct llc_snap_hdr - LLC snap header + * @dsap: Destination service access point + * @ssap: Source service access point + * @resv: Reserved for future use + * @eth_type: Ether type + */ +struct llc_snap_hdr { + uint8_t dsap; + uint8_t ssap; + uint8_t resv[4]; + qdf_be16_t eth_type; +} qdf_packed; + +/** + * struct wlan_ipa_tx_hdr - header type which IPA should handle to TX packet + * @eth: ether II header + * @llc_snap: LLC snap header + */ +struct wlan_ipa_tx_hdr { + qdf_ether_header_t eth; + struct llc_snap_hdr llc_snap; +} qdf_packed; + +/** + * struct frag_header - fragment header type registered to IPA hardware + * @length: fragment length + * @reserved1: Reserved not used + * @reserved2: Reserved not used + */ +#if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ + defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) +struct frag_header { + uint8_t reserved[0]; +}; +#elif defined(QCA_WIFI_3_0) +struct frag_header { + uint16_t length; + uint32_t reserved1; + uint32_t reserved2; +} qdf_packed; +#else +struct frag_header { + uint32_t + length:16, + reserved16:16; + uint32_t reserved2; +} qdf_packed; +#endif + +/** + * struct ipa_header - ipa header type registered to IPA hardware + * @vdev_id: vdev id + * @reserved: Reserved not used + */ + +#if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ + defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) +struct ipa_header { + uint8_t reserved[0]; +}; +#else +struct ipa_header { + uint32_t + vdev_id:8, /* vdev_id field is LSB of IPA DESC */ + reserved:24; +} qdf_packed; +#endif + +/** + * struct wlan_ipa_uc_tx_hdr - full tx header registered to IPA hardware + * @frag_hd: fragment header + * @ipa_hd: ipa header + * @eth: ether II header + */ +struct wlan_ipa_uc_tx_hdr { + struct frag_header frag_hd; + struct ipa_header ipa_hd; + qdf_ether_header_t eth; +} qdf_packed; + +/** + * struct wlan_ipa_cld_hdr - IPA CLD Header + * @reserved: reserved fields + * @iface_id: interface ID + * @sta_id: Station ID + * + * Packed 32-bit structure + * +----------+----------+--------------+--------+ + * | Reserved | QCMAP ID | interface id | STA ID | + * +----------+----------+--------------+--------+ + */ +struct wlan_ipa_cld_hdr { + uint8_t reserved[2]; + uint8_t iface_id; + uint8_t sta_id; +} qdf_packed; + +/** + * struct wlan_ipa_rx_hdr - IPA RX header + * @cld_hdr: IPA CLD header + * @eth: ether II header + */ +struct wlan_ipa_rx_hdr { + struct wlan_ipa_cld_hdr cld_hdr; + qdf_ether_header_t eth; +} qdf_packed; + +/** + * struct wlan_ipa_pm_tx_cb - PM resume TX callback + * @exception: Exception packet + * @iface_context: Interface context + * @ipa_tx_desc: IPA TX descriptor + */ +struct wlan_ipa_pm_tx_cb { + bool exception; + struct wlan_ipa_iface_context *iface_context; + qdf_ipa_rx_data_t *ipa_tx_desc; +}; + +/** + * struct wlan_ipa_sys_pipe - IPA system pipe + * @conn_hdl: IPA system pipe connection handle + * @conn_hdl_valid: IPA system pipe valid flag + * @ipa_sys_params: IPA system pipe params + */ +struct wlan_ipa_sys_pipe { + uint32_t conn_hdl; + uint8_t conn_hdl_valid; + qdf_ipa_sys_connect_params_t ipa_sys_params; +}; + +/** + * struct wlan_ipa_iface_stats - IPA system pipe + * @num_tx: Number of TX packets + * @num_tx_drop: Number of TX packet drops + * @num_tx_err: Number of TX packet errors + * @num_tx_cac_drop: Number of TX packet drop due to CAC + * @num_rx_ipa_excep: Number of RX IPA exception packets + */ +struct wlan_ipa_iface_stats { + uint64_t num_tx; + uint64_t num_tx_drop; + uint64_t num_tx_err; + uint64_t num_tx_cac_drop; + uint64_t num_rx_ipa_excep; +}; + +/* IPA private context structure */ +struct wlan_ipa_priv; + +/** + * struct wlan_ipa_iface_context - IPA interface context + * @ipa_ctx: IPA private context + * @cons_client: IPA consumer pipe + * @prod_client: IPA producer pipe + * @prod_client: IPA producer pipe + * @iface_id: IPA interface ID + * @dev: Net device structure + * @device_mode: Interface device mode + * @session_id: Session ID + * @interface_lock: Interface lock + * @ifa_address: Interface address + * @stats: Interface stats + * @bssid: BSSID. valid only for sta iface ctx; + */ +struct wlan_ipa_iface_context { + struct wlan_ipa_priv *ipa_ctx; + + qdf_ipa_client_type_t cons_client; + qdf_ipa_client_type_t prod_client; + + uint8_t iface_id; /* This iface ID */ + qdf_netdev_t dev; + enum QDF_OPMODE device_mode; + uint8_t session_id; + qdf_spinlock_t interface_lock; + uint32_t ifa_address; + struct wlan_ipa_iface_stats stats; + struct qdf_mac_addr bssid; +}; + +/** + * struct wlan_ipa_stats - IPA system stats + * @event: WLAN IPA event record + * @num_send_msg: Number of sent IPA messages + * @num_rm_grant: Number of times IPA RM resource granted + * @num_rm_release: Number of times IPA RM resource released + * @num_rm_grant_imm: Number of immediate IPA RM granted + e @num_cons_perf_req: Number of CONS pipe perf request + * @num_prod_perf_req: Number of PROD pipe perf request + * @num_rx_drop: Number of RX packet drops + * @num_tx_desc_q_cnt: Number of TX descriptor queue count + * @num_tx_desc_err: Number of TX descriptor error + * @num_tx_comp_cnt: Number of TX qdf_event_t count + * @num_tx_queued: Number of TX queued + * @num_tx_dequeued: Number of TX dequeued + * @num_max_pm_queue: Number of packets in PM queue + * @num_rx_excep: Number of RX IPA exception packets + * @num_rx_no_iface_eapol: No of EAPOL pkts before iface setup + * @num_tx_fwd_ok: Number of TX forward packet success + * @num_tx_fwd_err: Number of TX forward packet failures + */ +struct wlan_ipa_stats { + uint32_t event[QDF_IPA_WLAN_EVENT_MAX]; + uint64_t num_send_msg; + uint64_t num_free_msg; + uint64_t num_rm_grant; + uint64_t num_rm_release; + uint64_t num_rm_grant_imm; + uint64_t num_cons_perf_req; + uint64_t num_prod_perf_req; + uint64_t num_rx_drop; + uint64_t num_tx_desc_q_cnt; + uint64_t num_tx_desc_error; + uint64_t num_tx_comp_cnt; + uint64_t num_tx_queued; + uint64_t num_tx_dequeued; + uint64_t num_max_pm_queue; + uint64_t num_rx_excep; + uint64_t num_rx_no_iface_eapol; + uint64_t num_tx_fwd_ok; + uint64_t num_tx_fwd_err; +}; + +/** + * struct ipa_uc_stas_map - IPA UC assoc station map + * @is_reserved: STA reserved flag + * @mac_addr: Station mac address + */ +struct ipa_uc_stas_map { + bool is_reserved; + struct qdf_mac_addr mac_addr; +}; + +/** + * struct op_msg_type - IPA operation message type + * @msg_t: Message type + * @rsvd: Reserved + * @op_code: IPA Operation type + * @len: IPA message length + * @rsvd_snd: Reserved + */ +struct op_msg_type { + uint8_t msg_t; + uint8_t rsvd; + uint16_t op_code; + uint16_t len; + uint16_t rsvd_snd; +}; + +/** + * struct ipa_uc_fw_stats - IPA FW stats + * @tx_comp_ring_size: TX completion ring size + * @txcomp_ring_dbell_addr: TX comp ring door bell address + * @txcomp_ring_dbell_ind_val: TX cop ring door bell indication + * @txcomp_ring_dbell_cached_val: TX cop ring cached value + * @txpkts_enqueued: TX packets enqueued + * @txpkts_completed: TX packets completed + * @tx_is_suspend: TX suspend flag + * @tx_reserved: Reserved for TX stat + * @rx_ind_ring_base: RX indication ring base addess + * @rx_ind_ring_size: RX indication ring size + * @rx_ind_ring_dbell_addr: RX indication ring doorbell address + * @rx_ind_ring_dbell_ind_val: RX indication ring doorbell indication + * @rx_ind_ring_dbell_ind_cached_val: RX indication ring doorbell cached value + * @rx_ind_ring_rdidx_addr: RX indication ring read index address + * @rx_ind_ring_rd_idx_cached_val: RX indication ring read index cached value + * @rx_refill_idx: RX ring refill index + * @rx_num_pkts_indicated: Number of RX packets indicated + * @rx_buf_refilled: Number of RX buffer refilled + * @rx_num_ind_drop_no_space: Number of RX indication drops due to no space + * @rx_num_ind_drop_no_buf: Number of RX indication drops due to no buffer + * @rx_is_suspend: RX suspend flag + * @rx_reserved: Reserved for RX stat + */ +struct ipa_uc_fw_stats { + uint32_t tx_comp_ring_base; + uint32_t tx_comp_ring_size; + uint32_t tx_comp_ring_dbell_addr; + uint32_t tx_comp_ring_dbell_ind_val; + uint32_t tx_comp_ring_dbell_cached_val; + uint32_t tx_pkts_enqueued; + uint32_t tx_pkts_completed; + uint32_t tx_is_suspend; + uint32_t tx_reserved; + uint32_t rx_ind_ring_base; + uint32_t rx_ind_ring_size; + uint32_t rx_ind_ring_dbell_addr; + uint32_t rx_ind_ring_dbell_ind_val; + uint32_t rx_ind_ring_dbell_ind_cached_val; + uint32_t rx_ind_ring_rdidx_addr; + uint32_t rx_ind_ring_rd_idx_cached_val; + uint32_t rx_refill_idx; + uint32_t rx_num_pkts_indicated; + uint32_t rx_buf_refilled; + uint32_t rx_num_ind_drop_no_space; + uint32_t rx_num_ind_drop_no_buf; + uint32_t rx_is_suspend; + uint32_t rx_reserved; +}; + +/** + * struct wlan_ipa_uc_pending_event - WLAN IPA UC pending event + * @node: Pending event list node + * @type: WLAN IPA event type + * @device_mode: Device mode + * @session_id: Session ID + * @mac_addr: Mac address + * @is_loading: Driver loading flag + */ +struct wlan_ipa_uc_pending_event { + qdf_list_node_t node; + qdf_ipa_wlan_event type; + qdf_netdev_t net_dev; + uint8_t device_mode; + uint8_t session_id; + uint8_t mac_addr[QDF_MAC_ADDR_SIZE]; + bool is_loading; +}; + +/** + * struct uc_rm_work_struct + * @work: uC RM work + * @event: IPA RM event + */ +struct uc_rm_work_struct { + qdf_work_t work; + qdf_ipa_rm_event_t event; +}; + +/** + * struct uc_op_work_struct + * @work: uC OP work + * @msg: OP message + * @osdev: poiner to qdf net device, used by osif_psoc_sync_trans_start_wait + */ +struct uc_op_work_struct { + qdf_work_t work; + struct op_msg_type *msg; + qdf_device_t osdev; +}; + +/** + * struct uc_rt_debug_info + * @time: system time + * @ipa_excep_count: IPA exception packet count + * @rx_drop_count: IPA Rx drop packet count + * @net_sent_count: IPA Rx packet sent to network stack count + * @rx_discard_count: IPA Rx discard packet count + * @tx_fwd_ok_count: IPA Tx forward success packet count + * @tx_fwd_count: IPA Tx forward packet count + * @rx_destructor_call: IPA Rx packet destructor count + */ +struct uc_rt_debug_info { + uint64_t time; + uint64_t ipa_excep_count; + uint64_t rx_drop_count; + uint64_t net_sent_count; + uint64_t rx_discard_count; + uint64_t tx_fwd_ok_count; + uint64_t tx_fwd_count; + uint64_t rx_destructor_call; +}; + +#ifdef FEATURE_METERING +/** + * struct ipa_uc_sharing_stats - IPA UC sharing stats + * @ipv4_rx_packets: IPv4 RX packets + * @ipv4_rx_bytes: IPv4 RX bytes + * @ipv6_rx_packets: IPv6 RX packets + * @ipv6_rx_bytes: IPv6 RX bytes + * @ipv4_tx_packets: IPv4 TX packets + * @ipv4_tx_bytes: IPv4 TX bytes + * @ipv6_tx_packets: IPv4 TX packets + * @ipv6_tx_bytes: IPv6 TX bytes + */ +struct ipa_uc_sharing_stats { + uint64_t ipv4_rx_packets; + uint64_t ipv4_rx_bytes; + uint64_t ipv6_rx_packets; + uint64_t ipv6_rx_bytes; + uint64_t ipv4_tx_packets; + uint64_t ipv4_tx_bytes; + uint64_t ipv6_tx_packets; + uint64_t ipv6_tx_bytes; +}; + +/** + * struct ipa_uc_quota_rsp - IPA UC quota response + * @success: Success or fail flag + * @reserved[3]: Reserved + * @quota_lo: Quota limit low bytes + * @quota_hi: Quota limit high bytes + */ +struct ipa_uc_quota_rsp { + uint8_t success; + uint8_t reserved[3]; + uint32_t quota_lo; + uint32_t quota_hi; +}; + +/** + * struct ipa_uc_quota_ind + * @quota_bytes: Quota bytes to set + */ +struct ipa_uc_quota_ind { + uint64_t quota_bytes; +}; +#endif + +/** + * struct wlan_ipa_tx_desc + * @node: TX descriptor node + * @priv: pointer to priv list entry + * @id: Tx desc idex + * @ipa_tx_desc_ptr: pointer to IPA Tx descriptor + */ +struct wlan_ipa_tx_desc { + qdf_list_node_t node; + void *priv; + uint32_t id; + qdf_ipa_rx_data_t *ipa_tx_desc_ptr; +}; + +typedef QDF_STATUS (*wlan_ipa_softap_xmit)(qdf_nbuf_t nbuf, qdf_netdev_t dev); +typedef void (*wlan_ipa_send_to_nw)(qdf_nbuf_t nbuf, qdf_netdev_t dev); + +/** + * typedef wlan_ipa_rps_enable - Enable/disable RPS for adapter using vdev id + * @vdev_id: vdev_id of adapter + * @enable: Set true to enable RPS + */ +typedef void (*wlan_ipa_rps_enable)(uint8_t vdev_id, bool enable); + +/* IPA private context structure definition */ +struct wlan_ipa_priv { + struct wlan_objmgr_pdev *pdev; + struct wlan_ipa_sys_pipe sys_pipe[WLAN_IPA_MAX_SYSBAM_PIPE]; + struct wlan_ipa_iface_context iface_context[WLAN_IPA_MAX_IFACE]; + uint8_t num_iface; + void *dp_soc; + uint8_t dp_pdev_id; + struct wlan_ipa_config *config; + enum wlan_ipa_rm_state rm_state; + /* + * IPA driver can send RM notifications with IRQ disabled so using qdf + * APIs as it is taken care gracefully. Without this, kernel would throw + * an warning if spin_lock_bh is used while IRQ is disabled + */ + qdf_spinlock_t rm_lock; + struct uc_rm_work_struct uc_rm_work; + struct uc_op_work_struct uc_op_work[WLAN_IPA_UC_OPCODE_MAX]; + qdf_wake_lock_t wake_lock; + struct qdf_delayed_work wake_lock_work; + bool wake_lock_released; + + qdf_atomic_t tx_ref_cnt; + qdf_nbuf_queue_t pm_queue_head; + qdf_work_t pm_work; + qdf_spinlock_t pm_lock; + bool suspended; + qdf_spinlock_t q_lock; + qdf_spinlock_t enable_disable_lock; + /* Flag to indicate wait on pending TX completions */ + qdf_atomic_t waiting_on_pending_tx; + /* Timer ticks to keep track of time after which pipes are disabled */ + uint64_t pending_tx_start_ticks; + /* Indicates if cdp_ipa_disable_autonomy is called for IPA pipes */ + qdf_atomic_t autonomy_disabled; + /* Indicates if cdp_disable_ipa_pipes has been called for IPA pipes */ + qdf_atomic_t pipes_disabled; + /* + * IPA pipes are considered "down" when both autonomy_disabled and + * ipa_pipes_disabled are set + */ + bool ipa_pipes_down; + /* Flag for mutual exclusion during IPA disable pipes */ + bool pipes_down_in_progress; + /* Flag for mutual exclusion during IPA enable pipes */ + bool pipes_enable_in_progress; + qdf_list_node_t pend_desc_head; + struct wlan_ipa_tx_desc *tx_desc_pool; + qdf_list_t tx_desc_free_list; + + struct wlan_ipa_stats stats; + + uint32_t curr_prod_bw; + uint32_t curr_cons_bw; + + uint8_t activated_fw_pipe; + uint8_t num_sap_connected; + uint8_t sap_num_connected_sta; + uint8_t sta_connected; + uint32_t tx_pipe_handle; + uint32_t rx_pipe_handle; + bool resource_loading; + bool resource_unloading; + bool pending_cons_req; + struct ipa_uc_stas_map assoc_stas_map[WLAN_IPA_MAX_STA_COUNT]; + qdf_list_t pending_event; + qdf_mutex_t event_lock; + uint32_t ipa_tx_packets_diff; + uint32_t ipa_rx_packets_diff; + uint32_t ipa_p_tx_packets; + uint32_t ipa_p_rx_packets; + uint32_t stat_req_reason; + uint64_t ipa_tx_forward; + uint64_t ipa_rx_discard; + uint64_t ipa_rx_net_send_count; + uint64_t ipa_rx_internal_drop_count; + uint64_t ipa_rx_destructor_count; + qdf_mc_timer_t rt_debug_timer; + struct uc_rt_debug_info rt_bug_buffer[WLAN_IPA_UC_RT_DEBUG_BUF_COUNT]; + unsigned int rt_buf_fill_index; + qdf_ipa_wdi_in_params_t cons_pipe_in; + qdf_ipa_wdi_in_params_t prod_pipe_in; + bool uc_loaded; + bool wdi_enabled; + bool over_gsi; + qdf_mc_timer_t rt_debug_fill_timer; + qdf_mutex_t rt_debug_lock; + qdf_mutex_t ipa_lock; + + uint8_t vdev_to_iface[WLAN_IPA_MAX_SESSION]; + bool vdev_offload_enabled[WLAN_IPA_MAX_SESSION]; + bool mcc_mode; + qdf_work_t mcc_work; + bool ap_intrabss_fwd; + bool dfs_cac_block_tx; +#ifdef FEATURE_METERING + struct ipa_uc_sharing_stats ipa_sharing_stats; + struct ipa_uc_quota_rsp ipa_quota_rsp; + struct ipa_uc_quota_ind ipa_quota_ind; + qdf_event_t ipa_uc_sharing_stats_comp; + qdf_event_t ipa_uc_set_quota_comp; +#endif + + wlan_ipa_softap_xmit softap_xmit; + wlan_ipa_send_to_nw send_to_nw; + ipa_uc_offload_control_req ipa_tx_op; + +#ifdef IPA_LAN_RX_NAPI_SUPPORT + /*Callback to enable RPS for STA in STA+SAP scenario*/ + wlan_ipa_rps_enable rps_enable; +#endif + + qdf_event_t ipa_resource_comp; + + uint32_t wdi_version; + bool is_smmu_enabled; /* IPA caps returned from ipa_wdi_init */ + qdf_atomic_t stats_quota; + uint8_t curr_bw_level; +}; + +#define WLAN_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header) +#define WLAN_IPA_WLAN_IPA_HEADER sizeof(struct ipa_header) +#define WLAN_IPA_WLAN_CLD_HDR_LEN sizeof(struct wlan_ipa_cld_hdr) +#define WLAN_IPA_UC_WLAN_CLD_HDR_LEN 0 +#define WLAN_IPA_WLAN_TX_HDR_LEN sizeof(struct wlan_ipa_tx_hdr) +#define WLAN_IPA_UC_WLAN_TX_HDR_LEN sizeof(struct wlan_ipa_uc_tx_hdr) +#define WLAN_IPA_WLAN_RX_HDR_LEN sizeof(struct wlan_ipa_rx_hdr) +#define WLAN_IPA_UC_WLAN_HDR_DES_MAC_OFFSET \ + (WLAN_IPA_WLAN_FRAG_HEADER + WLAN_IPA_WLAN_IPA_HEADER) + +#define WLAN_IPA_GET_IFACE_ID(_data) \ + (((struct wlan_ipa_cld_hdr *) (_data))->iface_id) + +#define WLAN_IPA_LOG(LVL, fmt, args ...) \ + QDF_TRACE(QDF_MODULE_ID_IPA, LVL, \ + "%s:%d: "fmt, __func__, __LINE__, ## args) + +#define WLAN_IPA_IS_CONFIG_ENABLED(_ipa_cfg, _mask) \ + (((_ipa_cfg)->ipa_config & (_mask)) == (_mask)) + +#define BW_GET_DIFF(_x, _y) (unsigned long)((ULONG_MAX - (_y)) + (_x) + 1) + +#define IPA_RESOURCE_COMP_WAIT_TIME 500 + +#ifdef FEATURE_METERING +#define IPA_UC_SHARING_STATES_WAIT_TIME 500 +#define IPA_UC_SET_QUOTA_WAIT_TIME 500 +#endif + +/** + * wlan_ipa_wlan_event_to_str() - convert IPA WLAN event to string + * @event: IPA WLAN event to be converted to a string + * + * Return: ASCII string representing the IPA WLAN event + */ +static inline char *wlan_ipa_wlan_event_to_str(qdf_ipa_wlan_event event) +{ + switch (event) { + CASE_RETURN_STRING(QDF_IPA_CLIENT_CONNECT); + CASE_RETURN_STRING(QDF_IPA_CLIENT_DISCONNECT); + CASE_RETURN_STRING(QDF_IPA_AP_CONNECT); + CASE_RETURN_STRING(QDF_IPA_AP_DISCONNECT); + CASE_RETURN_STRING(QDF_IPA_STA_CONNECT); + CASE_RETURN_STRING(QDF_IPA_STA_DISCONNECT); + CASE_RETURN_STRING(QDF_IPA_CLIENT_CONNECT_EX); + CASE_RETURN_STRING(QDF_SWITCH_TO_SCC); + CASE_RETURN_STRING(QDF_SWITCH_TO_MCC); + CASE_RETURN_STRING(QDF_WDI_ENABLE); + CASE_RETURN_STRING(QDF_WDI_DISABLE); + default: + return "UNKNOWN"; + } +} + +/** + * wlan_ipa_get_iface() - Get IPA interface + * @ipa_ctx: IPA context + * @mode: Interface device mode + * + * Return: IPA interface address + */ +struct wlan_ipa_iface_context +*wlan_ipa_get_iface(struct wlan_ipa_priv *ipa_ctx, uint8_t mode); + +#endif /* IPA_OFFLOAD */ +#endif /* _WLAN_IPA_PRIV_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_core.c b/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_core.c new file mode 100644 index 0000000000000000000000000000000000000000..971f66f33f79a87d2dbcd4a9c9f1f9a71ead7762 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_core.c @@ -0,0 +1,4040 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* Include Files */ +#include "wlan_ipa_core.h" +#include "wlan_ipa_main.h" +#include "wlan_hdd_main.h" +#include +#include "cdp_txrx_ipa.h" +#include "wal_rx_desc.h" +#include "qdf_str.h" +#include "sir_api.h" +#include "host_diag_core_event.h" +#include "wlan_objmgr_vdev_obj.h" +#include "qdf_platform.h" + +static struct wlan_ipa_priv *gp_ipa; +static void wlan_ipa_set_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx); +static void wlan_ipa_reset_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx); + + +static struct wlan_ipa_iface_2_client { + qdf_ipa_client_type_t cons_client; + qdf_ipa_client_type_t prod_client; +} wlan_ipa_iface_2_client[WLAN_IPA_CLIENT_MAX_IFACE] = { + { + QDF_IPA_CLIENT_WLAN2_CONS, QDF_IPA_CLIENT_WLAN1_PROD + }, { + QDF_IPA_CLIENT_WLAN3_CONS, QDF_IPA_CLIENT_WLAN1_PROD + }, { + QDF_IPA_CLIENT_WLAN4_CONS, QDF_IPA_CLIENT_WLAN1_PROD + } +}; + +/* Local Function Prototypes */ +static void wlan_ipa_i2w_cb(void *priv, qdf_ipa_dp_evt_type_t evt, + unsigned long data); +static void wlan_ipa_w2i_cb(void *priv, qdf_ipa_dp_evt_type_t evt, + unsigned long data); + +/** + * wlan_ipa_uc_sta_is_enabled() - Is STA mode IPA uC offload enabled? + * @ipa_cfg: IPA config + * + * Return: true if STA mode IPA uC offload is enabled, false otherwise + */ +static inline bool wlan_ipa_uc_sta_is_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, WLAN_IPA_UC_STA_ENABLE_MASK); +} + +/** + * wlan_ipa_is_pre_filter_enabled() - Is IPA pre-filter enabled? + * @ipa_cfg: IPA config + * + * Return: true if pre-filter is enabled, otherwise false + */ +static inline +bool wlan_ipa_is_pre_filter_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, + WLAN_IPA_PRE_FILTER_ENABLE_MASK); +} + +/** + * wlan_ipa_is_ipv6_enabled() - Is IPA IPv6 enabled? + * @ipa_cfg: IPA config + * + * Return: true if IPv6 is enabled, otherwise false + */ +static inline bool wlan_ipa_is_ipv6_enabled(struct wlan_ipa_config *ipa_cfg) +{ + return WLAN_IPA_IS_CONFIG_ENABLED(ipa_cfg, WLAN_IPA_IPV6_ENABLE_MASK); +} + +/** + * wlan_ipa_is_sta_only_offload_enabled() - Is IPA STA only offload enabled + * + * STA only IPA offload is needed on MDM platforms to support + * tethering scenarios in STA-SAP configurations when SAP is idle. + * + * Currently in STA-SAP configurations, IPA pipes are enabled only + * when a wifi client is connected to SAP. + * + * Impact of this API is only limited to when IPA pipes are enabled + * and disabled. To take effect, WLAN_IPA_UC_STA_ENABLE_MASK needs to + * set to 1. + * + * Return: true if MDM_PLATFORM is defined, false otherwise + */ +#ifdef MDM_PLATFORM +static inline bool wlan_ipa_is_sta_only_offload_enabled(void) +{ + return true; +} +#else +static inline bool wlan_ipa_is_sta_only_offload_enabled(void) +{ + return false; +} +#endif + +/** + * wlan_ipa_msg_free_fn() - Free an IPA message + * @buff: pointer to the IPA message + * @len: length of the IPA message + * @type: type of IPA message + * + * Return: None + */ +static void wlan_ipa_msg_free_fn(void *buff, uint32_t len, uint32_t type) +{ + ipa_debug("msg type:%d, len:%d", type, len); + qdf_mem_free(buff); +} + +/** + * wlan_ipa_uc_loaded_uc_cb() - IPA UC loaded event callback + * @priv_ctxt: IPA context + * + * Will be called by IPA context. + * It's atomic context, then should be scheduled to kworker thread + * + * Return: None + */ +static void wlan_ipa_uc_loaded_uc_cb(void *priv_ctxt) +{ + struct wlan_ipa_priv *ipa_ctx; + struct op_msg_type *msg; + struct uc_op_work_struct *uc_op_work; + + if (!priv_ctxt) { + ipa_err("Invalid IPA context"); + return; + } + + ipa_ctx = priv_ctxt; + + uc_op_work = &ipa_ctx->uc_op_work[WLAN_IPA_UC_OPCODE_UC_READY]; + if (!list_empty(&uc_op_work->work.work.entry)) { + /* uc_op_work is not initialized yet */ + ipa_ctx->uc_loaded = true; + return; + } + + msg = qdf_mem_malloc(sizeof(*msg)); + if (!msg) { + ipa_err("op_msg allocation fails"); + return; + } + + msg->op_code = WLAN_IPA_UC_OPCODE_UC_READY; + + /* When the same uC OPCODE is already pended, just return */ + if (uc_op_work->msg) + goto done; + + uc_op_work->msg = msg; + qdf_sched_work(0, &uc_op_work->work); + + /* work handler will free the msg buffer */ + return; + +done: + qdf_mem_free(msg); +} + +struct wlan_ipa_priv *wlan_ipa_get_obj_context(void) +{ + return gp_ipa; +} + +/** + * wlan_ipa_send_pkt_to_tl() - Send an IPA packet to TL + * @iface_context: interface-specific IPA context + * @ipa_tx_desc: packet data descriptor + * + * Return: None + */ +static void wlan_ipa_send_pkt_to_tl( + struct wlan_ipa_iface_context *iface_context, + qdf_ipa_rx_data_t *ipa_tx_desc) +{ + struct wlan_ipa_priv *ipa_ctx = iface_context->ipa_ctx; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + qdf_device_t osdev; + qdf_nbuf_t skb; + struct wlan_ipa_tx_desc *tx_desc; + qdf_dma_addr_t paddr; + QDF_STATUS status; + + if (!ipa_ctx) + return; + pdev = ipa_ctx->pdev; + psoc = wlan_pdev_get_psoc(pdev); + osdev = wlan_psoc_get_qdf_dev(psoc); + + qdf_spin_lock_bh(&iface_context->interface_lock); + /* + * During CAC period, data packets shouldn't be sent over the air so + * drop all the packets here + */ + if (iface_context->device_mode == QDF_SAP_MODE || + iface_context->device_mode == QDF_P2P_GO_MODE) { + if (ipa_ctx->dfs_cac_block_tx) { + ipa_free_skb(ipa_tx_desc); + qdf_spin_unlock_bh(&iface_context->interface_lock); + iface_context->stats.num_tx_cac_drop++; + wlan_ipa_wdi_rm_try_release(ipa_ctx); + return; + } + } + + if (!osdev) { + ipa_free_skb(ipa_tx_desc); + iface_context->stats.num_tx_drop++; + qdf_spin_unlock_bh(&iface_context->interface_lock); + wlan_ipa_wdi_rm_try_release(ipa_ctx); + return; + } + qdf_spin_unlock_bh(&iface_context->interface_lock); + + skb = QDF_IPA_RX_DATA_SKB(ipa_tx_desc); + + qdf_mem_zero(skb->cb, sizeof(skb->cb)); + + /* Store IPA Tx buffer ownership into SKB CB */ + qdf_nbuf_ipa_owned_set(skb); + + if (qdf_mem_smmu_s1_enabled(osdev)) { + status = qdf_nbuf_map(osdev, skb, QDF_DMA_TO_DEVICE); + if (QDF_IS_STATUS_SUCCESS(status)) { + paddr = qdf_nbuf_get_frag_paddr(skb, 0); + } else { + ipa_free_skb(ipa_tx_desc); + qdf_spin_lock_bh(&iface_context->interface_lock); + iface_context->stats.num_tx_drop++; + qdf_spin_unlock_bh(&iface_context->interface_lock); + wlan_ipa_wdi_rm_try_release(ipa_ctx); + return; + } + } else { + paddr = QDF_IPA_RX_DATA_DMA_ADDR(ipa_tx_desc); + } + + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) { + qdf_nbuf_mapped_paddr_set(skb, + paddr + + WLAN_IPA_WLAN_FRAG_HEADER + + WLAN_IPA_WLAN_IPA_HEADER); + QDF_IPA_RX_DATA_SKB_LEN(ipa_tx_desc) -= + WLAN_IPA_WLAN_FRAG_HEADER + WLAN_IPA_WLAN_IPA_HEADER; + } else { + qdf_nbuf_mapped_paddr_set(skb, paddr); + } + + qdf_spin_lock_bh(&ipa_ctx->q_lock); + /* get free Tx desc and assign ipa_tx_desc pointer */ + if (ipa_ctx->tx_desc_free_list.count && + qdf_list_remove_front(&ipa_ctx->tx_desc_free_list, + (qdf_list_node_t **)&tx_desc) == + QDF_STATUS_SUCCESS) { + tx_desc->ipa_tx_desc_ptr = ipa_tx_desc; + ipa_ctx->stats.num_tx_desc_q_cnt++; + qdf_spin_unlock_bh(&ipa_ctx->q_lock); + /* Store Tx Desc index into SKB CB */ + QDF_NBUF_CB_TX_IPA_PRIV(skb) = tx_desc->id; + } else { + ipa_ctx->stats.num_tx_desc_error++; + qdf_spin_unlock_bh(&ipa_ctx->q_lock); + + if (qdf_mem_smmu_s1_enabled(osdev)) { + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) + qdf_nbuf_mapped_paddr_set(skb, paddr); + + qdf_nbuf_unmap(osdev, skb, QDF_DMA_TO_DEVICE); + } + + qdf_ipa_free_skb(ipa_tx_desc); + wlan_ipa_wdi_rm_try_release(ipa_ctx); + return; + } + + skb = cdp_ipa_tx_send_data_frame(cds_get_context(QDF_MODULE_ID_SOC), + iface_context->session_id, + QDF_IPA_RX_DATA_SKB(ipa_tx_desc)); + if (skb) { + qdf_nbuf_free(skb); + iface_context->stats.num_tx_err++; + return; + } + + atomic_inc(&ipa_ctx->tx_ref_cnt); + + iface_context->stats.num_tx++; +} + +/** + * wlan_ipa_forward() - handle packet forwarding to wlan tx + * @ipa_ctx: pointer to ipa ipa context + * @iface_ctx: interface context + * @skb: data pointer + * + * if exception packet has set forward bit, copied new packet should be + * forwarded to wlan tx. if wlan subsystem is in suspend state, packet should + * put into pm queue and tx procedure will be differed + * + * Return: None + */ +static void wlan_ipa_forward(struct wlan_ipa_priv *ipa_ctx, + struct wlan_ipa_iface_context *iface_ctx, + qdf_nbuf_t skb) +{ + struct wlan_ipa_pm_tx_cb *pm_tx_cb; + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + + /* Set IPA ownership for intra-BSS Tx packets to avoid skb_orphan */ + qdf_nbuf_ipa_owned_set(skb); + + /* WLAN subsystem is in suspend, put in queue */ + if (ipa_ctx->suspended) { + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + ipa_info_rl("Tx in suspend, put in queue"); + qdf_mem_zero(skb->cb, sizeof(skb->cb)); + pm_tx_cb = (struct wlan_ipa_pm_tx_cb *)skb->cb; + pm_tx_cb->exception = true; + pm_tx_cb->iface_context = iface_ctx; + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + qdf_nbuf_queue_add(&ipa_ctx->pm_queue_head, skb); + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + ipa_ctx->stats.num_tx_queued++; + } else { + /* Resume, put packet into WLAN TX */ + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + if (ipa_ctx->softap_xmit) { + if (ipa_ctx->softap_xmit(skb, iface_ctx->dev)) { + ipa_err_rl("packet Tx fail"); + ipa_ctx->stats.num_tx_fwd_err++; + } else { + ipa_ctx->stats.num_tx_fwd_ok++; + } + } else { + dev_kfree_skb_any(skb); + } + } +} + +/** + * wlan_ipa_intrabss_forward() - Forward intra bss packets. + * @ipa_ctx: pointer to IPA IPA struct + * @iface_ctx: ipa interface context + * @desc: Firmware descriptor + * @skb: Data buffer + * + * Return: + * WLAN_IPA_FORWARD_PKT_NONE + * WLAN_IPA_FORWARD_PKT_DISCARD + * WLAN_IPA_FORWARD_PKT_LOCAL_STACK + * + */ + +static enum wlan_ipa_forward_type wlan_ipa_intrabss_forward( + struct wlan_ipa_priv *ipa_ctx, + struct wlan_ipa_iface_context *iface_ctx, + uint8_t desc, + qdf_nbuf_t skb) +{ + int ret = WLAN_IPA_FORWARD_PKT_NONE; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if ((desc & FW_RX_DESC_FORWARD_M)) { + if (cdp_tx_desc_thresh_reached(soc, iface_ctx->session_id)) { + /* Drop the packet*/ + ipa_ctx->stats.num_tx_fwd_err++; + goto drop_pkt; + } + + ipa_debug_rl("Forward packet to Tx (fw_desc=%d)", desc); + ipa_ctx->ipa_tx_forward++; + + if ((desc & FW_RX_DESC_DISCARD_M)) { + wlan_ipa_forward(ipa_ctx, iface_ctx, skb); + ipa_ctx->ipa_rx_internal_drop_count++; + ipa_ctx->ipa_rx_discard++; + ret = WLAN_IPA_FORWARD_PKT_DISCARD; + } else { + struct sk_buff *cloned_skb = skb_clone(skb, GFP_ATOMIC); + + if (cloned_skb) + wlan_ipa_forward(ipa_ctx, iface_ctx, + cloned_skb); + else + ipa_err_rl("tx skb alloc failed"); + ret = WLAN_IPA_FORWARD_PKT_LOCAL_STACK; + } + } + return ret; + +drop_pkt: + dev_kfree_skb_any(skb); + ret = WLAN_IPA_FORWARD_PKT_DISCARD; + return ret; +} + +#ifdef CONFIG_IPA_WDI_UNIFIED_API +/* + * TODO: Get WDI version through FW capabilities + */ +#if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ + defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) +static inline void wlan_ipa_wdi_get_wdi_version(struct wlan_ipa_priv *ipa_ctx) +{ + ipa_ctx->wdi_version = IPA_WDI_3; +} +#elif defined(QCA_WIFI_3_0) +static inline void wlan_ipa_wdi_get_wdi_version(struct wlan_ipa_priv *ipa_ctx) +{ + ipa_ctx->wdi_version = IPA_WDI_2; +} +#else +static inline void wlan_ipa_wdi_get_wdi_version(struct wlan_ipa_priv *ipa_ctx) +{ + ipa_ctx->wdi_version = IPA_WDI_1; +} +#endif + +static inline bool wlan_ipa_wdi_is_smmu_enabled(struct wlan_ipa_priv *ipa_ctx, + qdf_device_t osdev) +{ + return ipa_ctx->is_smmu_enabled && qdf_mem_smmu_s1_enabled(osdev); +} + +static inline QDF_STATUS +wlan_ipa_wdi_setup(struct wlan_ipa_priv *ipa_ctx, + qdf_device_t osdev) +{ + qdf_ipa_sys_connect_params_t *sys_in = NULL; + int i; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + sys_in = qdf_mem_malloc(sizeof(*sys_in) * WLAN_IPA_MAX_IFACE); + if (!sys_in) { + ipa_err("sys_in allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) + qdf_mem_copy(sys_in + i, + &ipa_ctx->sys_pipe[i].ipa_sys_params, + sizeof(qdf_ipa_sys_connect_params_t)); + + qdf_status = cdp_ipa_setup(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id, + wlan_ipa_i2w_cb, wlan_ipa_w2i_cb, + wlan_ipa_wdi_meter_notifier_cb, + ipa_ctx->config->desc_size, + ipa_ctx, + wlan_ipa_is_rm_enabled(ipa_ctx->config), + &ipa_ctx->tx_pipe_handle, + &ipa_ctx->rx_pipe_handle, + wlan_ipa_wdi_is_smmu_enabled(ipa_ctx, osdev), + sys_in, ipa_ctx->over_gsi); + + qdf_mem_free(sys_in); + + return qdf_status; +} + +#ifdef FEATURE_METERING +/** + * wlan_ipa_wdi_init_metering() - IPA WDI metering init + * @ipa_ctx: IPA context + * @in: IPA WDI in param + * + * Return: QDF_STATUS + */ +static inline void wlan_ipa_wdi_init_metering(struct wlan_ipa_priv *ipa_ctxt, + qdf_ipa_wdi_init_in_params_t *in) +{ + QDF_IPA_WDI_INIT_IN_PARAMS_WDI_NOTIFY(in) = + wlan_ipa_wdi_meter_notifier_cb; +} +#else +static inline void wlan_ipa_wdi_init_metering(struct wlan_ipa_priv *ipa_ctxt, + qdf_ipa_wdi_init_in_params_t *in) +{ +} +#endif + +/** + * wlan_ipa_wdi_init() - IPA WDI init + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS wlan_ipa_wdi_init(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_ipa_wdi_init_in_params_t in; + qdf_ipa_wdi_init_out_params_t out; + int ret; + + ipa_ctx->uc_loaded = false; + + qdf_mem_zero(&in, sizeof(in)); + qdf_mem_zero(&out, sizeof(out)); + + QDF_IPA_WDI_INIT_IN_PARAMS_WDI_VERSION(&in) = ipa_ctx->wdi_version; + QDF_IPA_WDI_INIT_IN_PARAMS_NOTIFY(&in) = wlan_ipa_uc_loaded_uc_cb; + QDF_IPA_WDI_INIT_IN_PARAMS_PRIV(&in) = ipa_ctx; + wlan_ipa_wdi_init_metering(ipa_ctx, &in); + + ret = qdf_ipa_wdi_init(&in, &out); + if (ret) { + ipa_err("ipa_wdi_init failed with ret=%d", ret); + return QDF_STATUS_E_FAILURE; + } + + ipa_ctx->over_gsi = + QDF_IPA_WDI_INIT_OUT_PARAMS_IS_OVER_GSI(&out); + ipa_ctx->is_smmu_enabled = + QDF_IPA_WDI_INIT_OUT_PARAMS_IS_SMMU_ENABLED(&out); + ipa_info("ipa_over_gsi: %d, is_smmu_enabled: %d", + ipa_ctx->over_gsi, ipa_ctx->is_smmu_enabled); + + if (QDF_IPA_WDI_INIT_OUT_PARAMS_IS_UC_READY(&out)) { + ipa_debug("IPA uC READY"); + ipa_ctx->uc_loaded = true; + } else { + ipa_info("IPA uc not ready"); + return QDF_STATUS_E_BUSY; + } + + return QDF_STATUS_SUCCESS; +} + +static inline int wlan_ipa_wdi_cleanup(void) +{ + int ret; + + ret = qdf_ipa_wdi_cleanup(); + if (ret) + ipa_info("ipa_wdi_cleanup failed ret=%d", ret); + return ret; +} + +static inline int wlan_ipa_wdi_setup_sys_pipe(struct wlan_ipa_priv *ipa_ctx, + struct ipa_sys_connect_params *sys, + uint32_t *handle) +{ + return 0; +} + +static inline int wlan_ipa_wdi_teardown_sys_pipe(struct wlan_ipa_priv *ipa_ctx, + uint32_t handle) +{ + return 0; +} + +/** + * wlan_ipa_pm_flush() - flush queued packets + * @work: pointer to the scheduled work + * + * Called during PM resume to send packets to TL which were queued + * while host was in the process of suspending. + * + * Return: None + */ +static void wlan_ipa_pm_flush(void *data) +{ + struct wlan_ipa_priv *ipa_ctx = (struct wlan_ipa_priv *)data; + struct wlan_ipa_pm_tx_cb *pm_tx_cb = NULL; + qdf_nbuf_t skb; + uint32_t dequeued = 0; + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + while (((skb = qdf_nbuf_queue_remove(&ipa_ctx->pm_queue_head)) != + NULL)) { + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + pm_tx_cb = (struct wlan_ipa_pm_tx_cb *)skb->cb; + dequeued++; + + if (pm_tx_cb->exception) { + if (ipa_ctx->softap_xmit && + pm_tx_cb->iface_context->dev) { + ipa_ctx->softap_xmit(skb, + pm_tx_cb->iface_context->dev); + ipa_ctx->stats.num_tx_fwd_ok++; + } else { + dev_kfree_skb_any(skb); + } + } else { + wlan_ipa_send_pkt_to_tl(pm_tx_cb->iface_context, + pm_tx_cb->ipa_tx_desc); + } + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + } + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + ipa_ctx->stats.num_tx_dequeued += dequeued; + if (dequeued > ipa_ctx->stats.num_max_pm_queue) + ipa_ctx->stats.num_max_pm_queue = dequeued; +} + +int wlan_ipa_uc_smmu_map(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr) +{ + if (!num_buf) { + ipa_info("No buffers to map/unmap"); + return 0; + } + + if (map) + return qdf_ipa_wdi_create_smmu_mapping(num_buf, buf_arr); + else + return qdf_ipa_wdi_release_smmu_mapping(num_buf, buf_arr); +} + +#ifdef MDM_PLATFORM +/** + * is_rx_dest_bridge_dev() - is RX skb bridge device terminated + * @iface_ctx: pointer to WLAN IPA interface context + * @nbuf: skb buffer + * + * Check if skb is destined for bridge device, where SAP is a bridge + * port of it. + * + * FIXME: If there's a BH lockless API to check if destination MAC + * address is a valid peer, this check can be deleted. Currently + * dp_find_peer_by_addr() is used to check if destination MAC + * is a valid peer. Since WLAN IPA RX is in process context, + * qdf_spin_lock_bh in dp_find_peer_by_addr() turns to spin_lock_bh + * and this BH lock hurts netif_rx. + * + * Return: true/false + */ +static bool is_rx_dest_bridge_dev(struct wlan_ipa_iface_context *iface_ctx, + qdf_nbuf_t nbuf) +{ + qdf_netdev_t master_ndev; + qdf_netdev_t ndev; + struct ethhdr *eh; + uint8_t da_is_bcmc; + bool ret; + + if (iface_ctx->device_mode != QDF_SAP_MODE) + return false; + + /* + * WDI 3.0 skb->cb[] info from IPA driver + * skb->cb[0] = vdev_id + * skb->cb[1].bit#1 = da_is_bcmc + */ + da_is_bcmc = ((uint8_t)nbuf->cb[1]) & 0x2; + if (da_is_bcmc) + return false; + + ndev = iface_ctx->dev; + if (!ndev) + return false; + + if (!netif_is_bridge_port(ndev)) + return false; + + rcu_read_lock(); + + master_ndev = netdev_master_upper_dev_get_rcu(ndev); + if (!master_ndev) { + ret = false; + goto out; + } + + eh = (struct ethhdr *)qdf_nbuf_data(nbuf); + if (qdf_mem_cmp(eh->h_dest, master_ndev->dev_addr, QDF_MAC_ADDR_SIZE)) { + ret = false; + goto out; + } + + ret = true; + +out: + rcu_read_unlock(); + return ret; +} +#else /* !MDM_PLATFORM */ +static bool is_rx_dest_bridge_dev(struct wlan_ipa_iface_context *iface_ctx, + qdf_nbuf_t nbuf) +{ + return false; +} +#endif /* MDM_PLATFORM */ + +static enum wlan_ipa_forward_type +wlan_ipa_rx_intrabss_fwd(struct wlan_ipa_priv *ipa_ctx, + struct wlan_ipa_iface_context *iface_ctx, + qdf_nbuf_t nbuf) +{ + uint8_t fw_desc = 0; + bool fwd_success; + int ret; + + /* legacy intra-bss fowarding for WDI 1.0 and 2.0 */ + if (ipa_ctx->wdi_version != IPA_WDI_3) { + fw_desc = (uint8_t)nbuf->cb[1]; + return wlan_ipa_intrabss_forward(ipa_ctx, iface_ctx, fw_desc, + nbuf); + } + + if (is_rx_dest_bridge_dev(iface_ctx, nbuf)) { + fwd_success = 0; + ret = WLAN_IPA_FORWARD_PKT_LOCAL_STACK; + goto exit; + } + + if (cdp_ipa_rx_intrabss_fwd(ipa_ctx->dp_soc, iface_ctx->session_id, + nbuf, &fwd_success)) { + ipa_ctx->ipa_rx_internal_drop_count++; + ipa_ctx->ipa_rx_discard++; + + ret = WLAN_IPA_FORWARD_PKT_DISCARD; + } else { + ret = WLAN_IPA_FORWARD_PKT_LOCAL_STACK; + } + +exit: + if (fwd_success) + ipa_ctx->stats.num_tx_fwd_ok++; + else + ipa_ctx->stats.num_tx_fwd_err++; + + return ret; +} + +#else /* CONFIG_IPA_WDI_UNIFIED_API */ + +static inline void wlan_ipa_wdi_get_wdi_version(struct wlan_ipa_priv *ipa_ctx) +{ +} + +static inline int wlan_ipa_wdi_is_smmu_enabled(struct wlan_ipa_priv *ipa_ctx, + qdf_device_t osdev) +{ + return qdf_mem_smmu_s1_enabled(osdev); +} + +static inline QDF_STATUS wlan_ipa_wdi_setup(struct wlan_ipa_priv *ipa_ctx, + qdf_device_t osdev) +{ + return cdp_ipa_setup(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id, + wlan_ipa_i2w_cb, wlan_ipa_w2i_cb, + wlan_ipa_wdi_meter_notifier_cb, + ipa_ctx->config->desc_size, + ipa_ctx, wlan_ipa_is_rm_enabled(ipa_ctx->config), + &ipa_ctx->tx_pipe_handle, + &ipa_ctx->rx_pipe_handle); +} + +static inline QDF_STATUS wlan_ipa_wdi_init(struct wlan_ipa_priv *ipa_ctx) +{ + struct ipa_wdi_uc_ready_params uc_ready_param; + + ipa_ctx->uc_loaded = false; + uc_ready_param.priv = (void *)ipa_ctx; + uc_ready_param.notify = wlan_ipa_uc_loaded_uc_cb; + if (qdf_ipa_uc_reg_rdyCB(&uc_ready_param)) { + ipa_info("UC Ready CB register fail"); + return QDF_STATUS_E_FAILURE; + } + + if (true == uc_ready_param.is_uC_ready) { + ipa_info("UC Ready"); + ipa_ctx->uc_loaded = true; + } else { + return QDF_STATUS_E_BUSY; + } + + return QDF_STATUS_SUCCESS; +} + +static inline int wlan_ipa_wdi_cleanup(void) +{ + int ret; + + ret = qdf_ipa_uc_dereg_rdyCB(); + if (ret) + ipa_info("UC Ready CB deregister fail"); + return ret; +} + +static inline int wlan_ipa_wdi_setup_sys_pipe( + struct wlan_ipa_priv *ipa_ctx, + struct ipa_sys_connect_params *sys, uint32_t *handle) +{ + return qdf_ipa_setup_sys_pipe(sys, handle); +} + +static inline int wlan_ipa_wdi_teardown_sys_pipe( + struct wlan_ipa_priv *ipa_ctx, + uint32_t handle) +{ + return qdf_ipa_teardown_sys_pipe(handle); +} + +/** + * wlan_ipa_pm_flush() - flush queued packets + * @work: pointer to the scheduled work + * + * Called during PM resume to send packets to TL which were queued + * while host was in the process of suspending. + * + * Return: None + */ +static void wlan_ipa_pm_flush(void *data) +{ + struct wlan_ipa_priv *ipa_ctx = (struct wlan_ipa_priv *)data; + struct wlan_ipa_pm_tx_cb *pm_tx_cb = NULL; + qdf_nbuf_t skb; + uint32_t dequeued = 0; + + qdf_wake_lock_acquire(&ipa_ctx->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_IPA); + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + while (((skb = qdf_nbuf_queue_remove(&ipa_ctx->pm_queue_head)) != + NULL)) { + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + pm_tx_cb = (struct wlan_ipa_pm_tx_cb *)skb->cb; + dequeued++; + + if (pm_tx_cb->exception) { + if (ipa_ctx->softap_xmit && + pm_tx_cb->iface_context->dev) { + ipa_ctx->softap_xmit(skb, + pm_tx_cb->iface_context->dev); + ipa_ctx->stats.num_tx_fwd_ok++; + } else { + dev_kfree_skb_any(skb); + } + } else { + wlan_ipa_send_pkt_to_tl(pm_tx_cb->iface_context, + pm_tx_cb->ipa_tx_desc); + } + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + } + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + qdf_wake_lock_release(&ipa_ctx->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_IPA); + + ipa_ctx->stats.num_tx_dequeued += dequeued; + if (dequeued > ipa_ctx->stats.num_max_pm_queue) + ipa_ctx->stats.num_max_pm_queue = dequeued; +} + +int wlan_ipa_uc_smmu_map(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr) +{ + if (!num_buf) { + ipa_info("No buffers to map/unmap"); + return 0; + } + + if (map) + return qdf_ipa_create_wdi_mapping(num_buf, buf_arr); + else + return qdf_ipa_release_wdi_mapping(num_buf, buf_arr); +} + +static enum wlan_ipa_forward_type +wlan_ipa_rx_intrabss_fwd(struct wlan_ipa_priv *ipa_ctx, + struct wlan_ipa_iface_context *iface_ctx, + qdf_nbuf_t nbuf) +{ + uint8_t fw_desc; + + fw_desc = (uint8_t)nbuf->cb[1]; + + return wlan_ipa_intrabss_forward(ipa_ctx, iface_ctx, fw_desc, nbuf); +} + +#endif /* CONFIG_IPA_WDI_UNIFIED_API */ + +/** + * wlan_ipa_send_sta_eapol_to_nw() - Send Rx EAPOL pkt for STA to Kernel + * @skb: network buffer + * + * Called when a EAPOL packet is received via IPA Exception path + * before wlan_ipa_setup_iface is done for STA. + * + * Return: 0 on success, err_code for failure. + */ +static int wlan_ipa_send_sta_eapol_to_nw(qdf_nbuf_t skb) +{ + struct wlan_ipa_priv *ipa_ctx = gp_ipa; + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + struct ethhdr *eh; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + ipa_err_rl("Invalid hdd_context"); + return -EINVAL; + } + + eh = (struct ethhdr *)qdf_nbuf_data(skb); + adapter = hdd_get_adapter_by_macaddr(hdd_ctx, eh->h_dest); + if (hdd_validate_adapter(adapter)) { + ipa_err_rl("Invalid adapter"); + return -EINVAL; + } + if (adapter->device_mode != QDF_STA_MODE) { + ipa_err_rl("device_mode is not STA"); + return -EINVAL; + } + + skb->destructor = wlan_ipa_uc_rt_debug_destructor; + + if (ipa_ctx->send_to_nw) + ipa_ctx->send_to_nw(skb, adapter->dev); + + ipa_ctx->ipa_rx_net_send_count++; + ipa_ctx->stats.num_rx_no_iface_eapol++; + + return 0; +} + +/** + * wlan_ipa_send_skb_to_network() - Send skb to kernel + * @skb: network buffer + * @iface_ctx: IPA interface context + * + * Called when a network buffer is received which should not be routed + * to the IPA module. + * + * Return: None + */ +static void +wlan_ipa_send_skb_to_network(qdf_nbuf_t skb, + struct wlan_ipa_iface_context *iface_ctx) +{ + struct wlan_ipa_priv *ipa_ctx = gp_ipa; + + if (!iface_ctx->dev) { + ipa_debug_rl("Invalid interface"); + ipa_ctx->ipa_rx_internal_drop_count++; + dev_kfree_skb_any(skb); + return; + } + + skb->destructor = wlan_ipa_uc_rt_debug_destructor; + + if (ipa_ctx->send_to_nw) + ipa_ctx->send_to_nw(skb, iface_ctx->dev); + + ipa_ctx->ipa_rx_net_send_count++; +} + +/** + * wlan_ipa_eapol_intrabss_fwd_check() - Check if eapol pkt intrabss fwd is + * allowed or not + * @ipa_ctx: IPA global context + * @vdev_id: vdev id + * @nbuf: network buffer + * + * Return: true if intrabss fwd is allowed for eapol else false + */ +static bool +wlan_ipa_eapol_intrabss_fwd_check(struct wlan_ipa_priv *ipa_ctx, + uint8_t vdev_id, qdf_nbuf_t nbuf) +{ + uint8_t *vdev_mac_addr; + + vdev_mac_addr = cdp_get_vdev_mac_addr(ipa_ctx->dp_soc, vdev_id); + + if (!vdev_mac_addr) + return false; + + if (qdf_mem_cmp(qdf_nbuf_data(nbuf) + QDF_NBUF_DEST_MAC_OFFSET, + vdev_mac_addr, QDF_MAC_ADDR_SIZE)) + return false; + + return true; +} + +/** + * __wlan_ipa_w2i_cb() - WLAN to IPA callback handler + * @priv: pointer to private data registered with IPA (we register a + * pointer to the global IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void __wlan_ipa_w2i_cb(void *priv, qdf_ipa_dp_evt_type_t evt, + unsigned long data) +{ + struct wlan_ipa_priv *ipa_ctx = NULL; + qdf_nbuf_t skb; + uint8_t iface_id; + uint8_t session_id = 0xff; + struct wlan_ipa_iface_context *iface_context; + bool is_eapol_wapi = false; + struct qdf_mac_addr peer_mac_addr = QDF_MAC_ADDR_ZERO_INIT; + + ipa_ctx = (struct wlan_ipa_priv *)priv; + if (!ipa_ctx) { + if (evt == IPA_RECEIVE) { + skb = (qdf_nbuf_t)data; + dev_kfree_skb_any(skb); + } + return; + } + + switch (evt) { + case IPA_RECEIVE: + skb = (qdf_nbuf_t) data; + if (wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + session_id = (uint8_t)skb->cb[0]; + iface_id = ipa_ctx->vdev_to_iface[session_id]; + ipa_ctx->stats.num_rx_excep++; + qdf_nbuf_pull_head(skb, WLAN_IPA_UC_WLAN_CLD_HDR_LEN); + } else { + iface_id = WLAN_IPA_GET_IFACE_ID(skb->data); + qdf_nbuf_pull_head(skb, WLAN_IPA_WLAN_CLD_HDR_LEN); + } + + if (iface_id >= WLAN_IPA_MAX_IFACE) { + ipa_err_rl("Invalid iface_id %u,session_id %x %x %x %x", + iface_id, session_id, (uint8_t)skb->cb[1], + (uint8_t)skb->cb[2], (uint8_t)skb->cb[3]); + + if (qdf_nbuf_is_ipv4_eapol_pkt(skb)) { + ipa_err_rl("EAPOL pkt. Sending to NW!"); + if (!wlan_ipa_send_sta_eapol_to_nw(skb)) + break; + } + ipa_err_rl("Pkt Dropped!"); + ipa_ctx->ipa_rx_internal_drop_count++; + dev_kfree_skb_any(skb); + return; + } + + iface_context = &ipa_ctx->iface_context[iface_id]; + if (iface_context->session_id == WLAN_IPA_MAX_SESSION) { + ipa_err_rl("session_id of iface_id %u is invalid:%d", + iface_id, iface_context->session_id); + ipa_ctx->ipa_rx_internal_drop_count++; + dev_kfree_skb_any(skb); + return; + } + iface_context->stats.num_rx_ipa_excep++; + + if (iface_context->device_mode == QDF_STA_MODE) + qdf_copy_macaddr(&peer_mac_addr, &iface_context->bssid); + else if (iface_context->device_mode == QDF_SAP_MODE) + qdf_mem_copy(&peer_mac_addr.bytes[0], + qdf_nbuf_data(skb) + + QDF_NBUF_SRC_MAC_OFFSET, + QDF_MAC_ADDR_SIZE); + + if (qdf_nbuf_is_ipv4_eapol_pkt(skb)) { + is_eapol_wapi = true; + if (iface_context->device_mode == QDF_SAP_MODE && + !wlan_ipa_eapol_intrabss_fwd_check(ipa_ctx, + iface_context->session_id, skb)) { + ipa_err_rl("EAPOL intrabss fwd drop DA:"QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(qdf_nbuf_data(skb) + + QDF_NBUF_DEST_MAC_OFFSET)); + ipa_ctx->ipa_rx_internal_drop_count++; + dev_kfree_skb_any(skb); + return; + } + } else if (qdf_nbuf_is_ipv4_wapi_pkt(skb)) { + is_eapol_wapi = true; + } + + /* + * Check for peer authorized state before allowing + * non-EAPOL/WAPI frames to be intrabss forwarded + * or submitted to stack. + */ + if (cdp_peer_state_get(ipa_ctx->dp_soc, + iface_context->session_id, + &peer_mac_addr.bytes[0]) != + OL_TXRX_PEER_STATE_AUTH && !is_eapol_wapi) { + ipa_err_rl("Non EAPOL/WAPI packet received when peer "QDF_MAC_ADDR_FMT" is unauthorized", + QDF_MAC_ADDR_REF(peer_mac_addr.bytes)); + ipa_ctx->ipa_rx_internal_drop_count++; + dev_kfree_skb_any(skb); + return; + } + + /* Disable to forward Intra-BSS Rx packets when + * ap_isolate=1 in hostapd.conf + */ + if (!ipa_ctx->ap_intrabss_fwd) { + /* + * When INTRA_BSS_FWD_OFFLOAD is enabled, FW will send + * all Rx packets to IPA uC, which need to be forwarded + * to other interface. + * And, IPA driver will send back to WLAN host driver + * through exception pipe with fw_desc field set by FW. + * Here we are checking fw_desc field for FORWARD bit + * set, and forward to Tx. Then copy to kernel stack + * only when DISCARD bit is not set. + */ + if (WLAN_IPA_FORWARD_PKT_DISCARD == + wlan_ipa_rx_intrabss_fwd(ipa_ctx, iface_context, + skb)) + break; + } else { + ipa_debug_rl("Intra-BSS forwarding is disabled"); + } + + wlan_ipa_send_skb_to_network(skb, iface_context); + break; + + default: + ipa_err_rl("w2i cb wrong event: 0x%x", evt); + return; + } +} + +#ifndef MDM_PLATFORM +/** + * wlan_ipa_w2i_cb() - SSR wrapper for __wlan_ipa_w2i_cb + * @priv: pointer to private data registered with IPA (we register a + * pointer to the global IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void wlan_ipa_w2i_cb(void *priv, qdf_ipa_dp_evt_type_t evt, + unsigned long data) +{ + struct qdf_op_sync *op_sync; + + if (qdf_op_protect(&op_sync)) { + if (evt == IPA_RECEIVE) { + struct wlan_ipa_priv *ipa_ctx = priv; + qdf_nbuf_t skb = (qdf_nbuf_t)data; + + ipa_ctx->ipa_rx_internal_drop_count++; + dev_kfree_skb_any(skb); + } + + return; + } + + __wlan_ipa_w2i_cb(priv, evt, data); + + qdf_op_unprotect(op_sync); +} +#else /* MDM_PLATFORM */ +static void wlan_ipa_w2i_cb(void *priv, qdf_ipa_dp_evt_type_t evt, + unsigned long data) +{ + __wlan_ipa_w2i_cb(priv, evt, data); +} +#endif /* MDM_PLATFORM */ + +/** + * __wlan_ipa_i2w_cb() - IPA to WLAN callback + * @priv: pointer to private data registered with IPA (we register a + * pointer to the interface-specific IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void __wlan_ipa_i2w_cb(void *priv, qdf_ipa_dp_evt_type_t evt, + unsigned long data) +{ + struct wlan_ipa_priv *ipa_ctx = NULL; + qdf_ipa_rx_data_t *ipa_tx_desc; + struct wlan_ipa_iface_context *iface_context; + qdf_nbuf_t skb; + struct wlan_ipa_pm_tx_cb *pm_tx_cb = NULL; + + iface_context = (struct wlan_ipa_iface_context *)priv; + ipa_tx_desc = (qdf_ipa_rx_data_t *)data; + ipa_ctx = iface_context->ipa_ctx; + + if (evt != IPA_RECEIVE) { + ipa_err_rl("Event is not IPA_RECEIVE"); + ipa_free_skb(ipa_tx_desc); + iface_context->stats.num_tx_drop++; + return; + } + + skb = QDF_IPA_RX_DATA_SKB(ipa_tx_desc); + + /* + * If PROD resource is not requested here then there may be cases where + * IPA hardware may be clocked down because of not having proper + * dependency graph between WLAN CONS and modem PROD pipes. Adding the + * workaround to request PROD resource while data is going over CONS + * pipe to prevent the IPA hardware clockdown. + */ + wlan_ipa_wdi_rm_request(ipa_ctx); + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + /* + * If host is still suspended then queue the packets and these will be + * drained later when resume completes. When packet is arrived here and + * host is suspended, this means that there is already resume is in + * progress. + */ + if (ipa_ctx->suspended) { + qdf_mem_zero(skb->cb, sizeof(skb->cb)); + pm_tx_cb = (struct wlan_ipa_pm_tx_cb *)skb->cb; + pm_tx_cb->iface_context = iface_context; + pm_tx_cb->ipa_tx_desc = ipa_tx_desc; + qdf_nbuf_queue_add(&ipa_ctx->pm_queue_head, skb); + ipa_ctx->stats.num_tx_queued++; + + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + return; + } + + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + /* + * If we are here means, host is not suspended, wait for the work queue + * to finish. + */ + qdf_flush_work(&ipa_ctx->pm_work); + + return wlan_ipa_send_pkt_to_tl(iface_context, ipa_tx_desc); +} + +/** + * wlan_ipa_i2w_cb() - IPA to WLAN callback + * @priv: pointer to private data registered with IPA (we register a + * pointer to the interface-specific IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void wlan_ipa_i2w_cb(void *priv, qdf_ipa_dp_evt_type_t evt, + unsigned long data) +{ + struct qdf_op_sync *op_sync; + + if (qdf_op_protect(&op_sync)) { + qdf_ipa_rx_data_t *ipa_tx_desc = (qdf_ipa_rx_data_t *)data; + struct wlan_ipa_iface_context *iface_context = priv; + + ipa_free_skb(ipa_tx_desc); + iface_context->stats.num_tx_drop++; + + return; + } + + __wlan_ipa_i2w_cb(priv, evt, data); + + qdf_op_unprotect(op_sync); +} + +QDF_STATUS wlan_ipa_suspend(struct wlan_ipa_priv *ipa_ctx) +{ + /* + * Check if IPA is ready for suspend, If we are here means, there is + * high chance that suspend would go through but just to avoid any race + * condition after suspend started, these checks are conducted before + * allowing to suspend. + */ + if (atomic_read(&ipa_ctx->tx_ref_cnt)) + return QDF_STATUS_E_AGAIN; + + if (!wlan_ipa_is_rm_released(ipa_ctx)) + return QDF_STATUS_E_AGAIN; + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + ipa_ctx->suspended = true; + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + if (ipa_ctx->config->ipa_force_voting && + !ipa_ctx->ipa_pipes_down) + wlan_ipa_set_perf_level(ipa_ctx, + ipa_ctx->config->bus_bw_high, + ipa_ctx->config->bus_bw_high); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_ipa_resume(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_sched_work(0, &ipa_ctx->pm_work); + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + ipa_ctx->suspended = false; + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_ipa_uc_enable_pipes(struct wlan_ipa_priv *ipa_ctx) +{ + int result; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + ipa_debug("enter"); + + qdf_spin_lock_bh(&ipa_ctx->enable_disable_lock); + if (ipa_ctx->pipes_enable_in_progress) { + ipa_warn("IPA Pipes Enable in progress"); + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); + return QDF_STATUS_E_ALREADY; + } + ipa_ctx->pipes_enable_in_progress = true; + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); + + if (qdf_atomic_read(&ipa_ctx->waiting_on_pending_tx)) + wlan_ipa_reset_pending_tx_timer(ipa_ctx); + + if (qdf_atomic_read(&ipa_ctx->pipes_disabled)) { + result = cdp_ipa_enable_pipes(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev_id); + if (result) { + ipa_err("Enable IPA WDI PIPE failed: ret=%d", result); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + qdf_atomic_set(&ipa_ctx->pipes_disabled, 0); + } + + qdf_event_reset(&ipa_ctx->ipa_resource_comp); + + if (qdf_atomic_read(&ipa_ctx->autonomy_disabled)) { + cdp_ipa_enable_autonomy(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev_id); + qdf_atomic_set(&ipa_ctx->autonomy_disabled, 0); + } +end: + qdf_spin_lock_bh(&ipa_ctx->enable_disable_lock); + if (!qdf_atomic_read(&ipa_ctx->autonomy_disabled) && + !qdf_atomic_read(&ipa_ctx->pipes_disabled)) + ipa_ctx->ipa_pipes_down = false; + + ipa_ctx->pipes_enable_in_progress = false; + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); + + ipa_debug("exit: ipa_pipes_down=%d", ipa_ctx->ipa_pipes_down); + return qdf_status; +} + +QDF_STATUS +wlan_ipa_uc_disable_pipes(struct wlan_ipa_priv *ipa_ctx, bool force_disable) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + ipa_debug("enter: force_disable %u autonomy_disabled %u pipes_disabled %u", + force_disable, + qdf_atomic_read(&ipa_ctx->autonomy_disabled), + qdf_atomic_read(&ipa_ctx->pipes_disabled)); + + qdf_spin_lock_bh(&ipa_ctx->enable_disable_lock); + if (ipa_ctx->ipa_pipes_down || ipa_ctx->pipes_down_in_progress) { + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); + ipa_info("IPA WDI Pipes are already deactivated"); + return QDF_STATUS_E_ALREADY; + } + ipa_ctx->pipes_down_in_progress = true; + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); + + + if (!qdf_atomic_read(&ipa_ctx->autonomy_disabled)) { + cdp_ipa_disable_autonomy(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev_id); + qdf_atomic_set(&ipa_ctx->autonomy_disabled, 1); + } + + if (!qdf_atomic_read(&ipa_ctx->pipes_disabled)) { + if (!force_disable) { + wlan_ipa_set_pending_tx_timer(ipa_ctx); + } else { + qdf_status = cdp_ipa_disable_pipes(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev_id); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + ipa_err("Disable IPA WDI PIPE failed: ret=%u", + qdf_status); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + qdf_atomic_set(&ipa_ctx->pipes_disabled, 1); + wlan_ipa_reset_pending_tx_timer(ipa_ctx); + } + } + +end: + qdf_spin_lock_bh(&ipa_ctx->enable_disable_lock); + if (qdf_atomic_read(&ipa_ctx->pipes_disabled) && + qdf_atomic_read(&ipa_ctx->autonomy_disabled)) { + ipa_ctx->ipa_pipes_down = true; + } + ipa_ctx->pipes_down_in_progress = false; + qdf_spin_unlock_bh(&ipa_ctx->enable_disable_lock); + + ipa_debug("exit: ipa_pipes_down %u autonomy_disabled %u pipes_disabled %u", + ipa_ctx->ipa_pipes_down, + qdf_atomic_read(&ipa_ctx->autonomy_disabled), + qdf_atomic_read(&ipa_ctx->pipes_disabled)); + return qdf_status; +} + +/** + * wlan_ipa_uc_find_add_assoc_sta() - Find associated station + * @ipa_ctx: Global IPA IPA context + * @sta_add: Should station be added + * @mac_addr: mac address of station being queried + * + * Return: true if the station was found + */ +static bool wlan_ipa_uc_find_add_assoc_sta(struct wlan_ipa_priv *ipa_ctx, + bool sta_add, + uint8_t *mac_addr) +{ + bool sta_found = false; + uint8_t idx; + + for (idx = 0; idx < WLAN_IPA_MAX_STA_COUNT; idx++) { + if ((ipa_ctx->assoc_stas_map[idx].is_reserved) && + (qdf_is_macaddr_equal( + &ipa_ctx->assoc_stas_map[idx].mac_addr, + (struct qdf_mac_addr *)mac_addr))) { + sta_found = true; + break; + } + } + if (sta_add && sta_found) { + ipa_err("STA already exist, cannot add: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_addr)); + return sta_found; + } + if (sta_add) { + for (idx = 0; idx < WLAN_IPA_MAX_STA_COUNT; idx++) { + if (!ipa_ctx->assoc_stas_map[idx].is_reserved) { + ipa_ctx->assoc_stas_map[idx].is_reserved = true; + qdf_mem_copy(&ipa_ctx->assoc_stas_map[idx]. + mac_addr, mac_addr, + QDF_NET_ETH_LEN); + return sta_found; + } + } + } + if (!sta_add && !sta_found) { + ipa_info("STA does not exist, cannot delete: " + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(mac_addr)); + return sta_found; + } + if (!sta_add) { + for (idx = 0; idx < WLAN_IPA_MAX_STA_COUNT; idx++) { + if ((ipa_ctx->assoc_stas_map[idx].is_reserved) && + (qdf_is_macaddr_equal( + &ipa_ctx->assoc_stas_map[idx].mac_addr, + (struct qdf_mac_addr *)mac_addr))) { + ipa_ctx->assoc_stas_map[idx].is_reserved = + false; + qdf_mem_zero( + &ipa_ctx->assoc_stas_map[idx].mac_addr, + QDF_NET_ETH_LEN); + return sta_found; + } + } + } + + return sta_found; +} + +/** + * wlan_ipa_get_ifaceid() - Get IPA context interface ID + * @ipa_ctx: IPA context + * @session_id: Session ID + * + * Return: None + */ +static int wlan_ipa_get_ifaceid(struct wlan_ipa_priv *ipa_ctx, + uint8_t session_id) +{ + struct wlan_ipa_iface_context *iface_ctx; + int i; + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + if (iface_ctx->session_id == session_id) + break; + } + + return i; +} + +/** + * wlan_ipa_cleanup_iface() - Cleanup IPA on a given interface + * @iface_context: interface-specific IPA context + * + * Return: None + */ +static void wlan_ipa_cleanup_iface(struct wlan_ipa_iface_context *iface_context) +{ + struct wlan_ipa_priv *ipa_ctx = iface_context->ipa_ctx; + + ipa_debug("enter"); + + if (iface_context->session_id == WLAN_IPA_MAX_SESSION) + return; + + cdp_ipa_cleanup_iface(ipa_ctx->dp_soc, + iface_context->dev->name, + wlan_ipa_is_ipv6_enabled(ipa_ctx->config)); + + if (iface_context->device_mode == QDF_SAP_MODE) + ipa_ctx->num_sap_connected--; + + qdf_spin_lock_bh(&iface_context->interface_lock); + iface_context->dev = NULL; + iface_context->device_mode = QDF_MAX_NO_OF_MODE; + iface_context->session_id = WLAN_IPA_MAX_SESSION; + qdf_spin_unlock_bh(&iface_context->interface_lock); + iface_context->ifa_address = 0; + qdf_zero_macaddr(&iface_context->bssid); + if (!iface_context->ipa_ctx->num_iface) { + ipa_err("NUM INTF 0, Invalid"); + QDF_ASSERT(0); + } + iface_context->ipa_ctx->num_iface--; + ipa_debug("exit: num_iface=%d", iface_context->ipa_ctx->num_iface); +} + +/** + * wlan_ipa_nbuf_cb() - IPA TX complete callback + * @skb: packet buffer which was transmitted + * + * Return: None + */ +static void wlan_ipa_nbuf_cb(qdf_nbuf_t skb) +{ + struct wlan_ipa_priv *ipa_ctx = gp_ipa; + qdf_ipa_rx_data_t *ipa_tx_desc; + struct wlan_ipa_tx_desc *tx_desc; + uint16_t id; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + qdf_device_t osdev; + + if (!qdf_nbuf_ipa_owned_get(skb)) { + dev_kfree_skb_any(skb); + return; + } + + if (!ipa_ctx) + return; + pdev = ipa_ctx->pdev; + psoc = wlan_pdev_get_psoc(pdev); + osdev = wlan_psoc_get_qdf_dev(psoc); + + if (osdev && qdf_mem_smmu_s1_enabled(osdev)) { + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) { + qdf_dma_addr_t paddr = QDF_NBUF_CB_PADDR(skb); + + qdf_nbuf_mapped_paddr_set(skb, + paddr - + WLAN_IPA_WLAN_FRAG_HEADER - + WLAN_IPA_WLAN_IPA_HEADER); + } + + qdf_nbuf_unmap(osdev, skb, QDF_DMA_TO_DEVICE); + } + + /* Get Tx desc pointer from SKB CB */ + id = QDF_NBUF_CB_TX_IPA_PRIV(skb); + tx_desc = &ipa_ctx->tx_desc_pool[id]; + ipa_tx_desc = tx_desc->ipa_tx_desc_ptr; + + /* Return Tx Desc to IPA */ + qdf_ipa_free_skb(ipa_tx_desc); + + /* Return to free tx desc list */ + qdf_spin_lock_bh(&ipa_ctx->q_lock); + tx_desc->ipa_tx_desc_ptr = NULL; + qdf_list_insert_back(&ipa_ctx->tx_desc_free_list, &tx_desc->node); + ipa_ctx->stats.num_tx_desc_q_cnt--; + qdf_spin_unlock_bh(&ipa_ctx->q_lock); + + ipa_ctx->stats.num_tx_comp_cnt++; + + qdf_atomic_dec(&ipa_ctx->tx_ref_cnt); + + wlan_ipa_wdi_rm_try_release(ipa_ctx); +} + +/** + * wlan_ipa_setup_iface() - Setup IPA on a given interface + * @ipa_ctx: IPA IPA global context + * @net_dev: Interface net device + * @device_mode: Net interface device mode + * @adapter: Interface upon which IPA is being setup + * @session_id: Station ID of the API instance + * + * Return: QDF STATUS + */ +static QDF_STATUS wlan_ipa_setup_iface(struct wlan_ipa_priv *ipa_ctx, + qdf_netdev_t net_dev, + uint8_t device_mode, + uint8_t session_id) +{ + struct wlan_ipa_iface_context *iface_context = NULL; + int i; + QDF_STATUS status; + + /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during + * channel change indication. Since these indications are sent by lower + * layer as SAP updates and IPA doesn't have to do anything for these + * updates so ignoring! + */ + if (device_mode == QDF_SAP_MODE) { + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_context = &(ipa_ctx->iface_context[i]); + if (iface_context->dev == net_dev) { + if (iface_context->device_mode == + device_mode) { + ipa_debug("found iface %u device_mode %u", + i, device_mode); + return QDF_STATUS_SUCCESS; + } + + ipa_err("Obsolete iface %u found, device_mode %u, will remove it.", + i, + iface_context->device_mode); + wlan_ipa_cleanup_iface(iface_context); + } + } + } + + if (WLAN_IPA_MAX_IFACE == ipa_ctx->num_iface) { + ipa_err("Max interface reached %d", WLAN_IPA_MAX_IFACE); + status = QDF_STATUS_E_NOMEM; + QDF_ASSERT(0); + goto end; + } + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + if (ipa_ctx->iface_context[i].session_id == + WLAN_IPA_MAX_SESSION) { + iface_context = &(ipa_ctx->iface_context[i]); + break; + } + } + + if (!iface_context) { + ipa_err("All the IPA interfaces are in use"); + status = QDF_STATUS_E_NOMEM; + QDF_ASSERT(0); + goto end; + } + + iface_context->dev = net_dev; + iface_context->device_mode = device_mode; + iface_context->session_id = session_id; + + status = cdp_ipa_setup_iface(ipa_ctx->dp_soc, net_dev->name, + net_dev->dev_addr, + iface_context->prod_client, + iface_context->cons_client, + session_id, + wlan_ipa_is_ipv6_enabled(ipa_ctx->config)); + if (status != QDF_STATUS_SUCCESS) + goto end; + + /* Register IPA Tx desc free callback */ + qdf_nbuf_reg_free_cb(wlan_ipa_nbuf_cb); + + ipa_ctx->num_iface++; + + if (device_mode == QDF_SAP_MODE) + ipa_ctx->num_sap_connected++; + + ipa_debug("exit: num_iface=%d", ipa_ctx->num_iface); + + return status; + +end: + if (iface_context) + wlan_ipa_cleanup_iface(iface_context); + + return status; +} + +#if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ + defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) + +#ifdef IPA_LAN_RX_NAPI_SUPPORT +void ipa_set_rps(struct wlan_ipa_priv *ipa_ctx, enum QDF_OPMODE mode, + bool enable) +{ + struct wlan_ipa_iface_context *iface_ctx; + wlan_ipa_rps_enable cb = ipa_ctx->rps_enable; + int i; + + if (!cb) + return; + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + if (iface_ctx->device_mode == mode) + cb(iface_ctx->session_id, enable); + } +} +#endif + +/** + * wlan_ipa_uc_handle_first_con() - Handle first uC IPA connection + * @ipa_ctx: IPA context + * + * Return: QDF STATUS + */ +static QDF_STATUS wlan_ipa_uc_handle_first_con(struct wlan_ipa_priv *ipa_ctx) +{ + ipa_debug("enter"); + + if (qdf_ipa_get_lan_rx_napi() && (ipa_ctx->num_sap_connected > 1)) { + ipa_debug("Multiple SAP connected. Not enabling pipes. Exit"); + return QDF_STATUS_E_PERM; + } + + if (qdf_ipa_get_lan_rx_napi() && ipa_ctx->sta_connected) + ipa_set_rps(ipa_ctx, QDF_STA_MODE, true); + + if (wlan_ipa_uc_enable_pipes(ipa_ctx) != QDF_STATUS_SUCCESS) { + ipa_err("IPA WDI Pipe activation failed"); + return QDF_STATUS_E_BUSY; + } + + ipa_debug("exit"); + + return QDF_STATUS_SUCCESS; +} + +static +void wlan_ipa_uc_handle_last_discon(struct wlan_ipa_priv *ipa_ctx, + bool force_disable) +{ + ipa_debug("enter"); + + wlan_ipa_uc_disable_pipes(ipa_ctx, force_disable); + + if (qdf_ipa_get_lan_rx_napi() && ipa_ctx->sta_connected) + ipa_set_rps(ipa_ctx, QDF_STA_MODE, false); + + ipa_debug("exit: IPA WDI Pipes deactivated"); +} + +bool wlan_ipa_is_fw_wdi_activated(struct wlan_ipa_priv *ipa_ctx) +{ + return !ipa_ctx->ipa_pipes_down; +} + +/* Time(ms) to wait for pending TX comps after last SAP client disconnects */ +#define WLAN_IPA_TX_PENDING_TIMEOUT_MS 15000 + +static void wlan_ipa_set_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx) +{ + ipa_ctx->pending_tx_start_ticks = qdf_system_ticks(); + qdf_atomic_set(&ipa_ctx->waiting_on_pending_tx, 1); + ipa_info("done. pending_tx_start_ticks %llu wait_on_pending %u", + ipa_ctx->pending_tx_start_ticks, + qdf_atomic_read(&ipa_ctx->waiting_on_pending_tx)); +} + +bool wlan_ipa_is_tx_pending(struct wlan_ipa_priv *ipa_ctx) +{ + bool ret = false; + uint64_t diff_ms = 0; + uint64_t current_ticks = 0; + + if (!qdf_atomic_read(&ipa_ctx->waiting_on_pending_tx)) { + ipa_debug("nothing pending"); + return false; + } + + current_ticks = qdf_system_ticks(); + + diff_ms = qdf_system_ticks_to_msecs(current_ticks - + ipa_ctx->pending_tx_start_ticks); + + if (diff_ms < WLAN_IPA_TX_PENDING_TIMEOUT_MS) { + ret = true; + } else { + ipa_debug("disabling pipes"); + wlan_ipa_uc_disable_pipes(ipa_ctx, true); + } + + ipa_debug("diff_ms %llu pending_tx_start_ticks %llu current_ticks %llu wait_on_pending %u", + diff_ms, ipa_ctx->pending_tx_start_ticks, current_ticks, + qdf_atomic_read(&ipa_ctx->waiting_on_pending_tx)); + + return ret; +} + +static void wlan_ipa_reset_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx) +{ + ipa_ctx->pending_tx_start_ticks = 0; + qdf_atomic_set(&ipa_ctx->waiting_on_pending_tx, 0); + ipa_info("done"); +} + +#else + +/** + * wlan_ipa_uc_handle_first_con() - Handle first uC IPA connection + * @ipa_ctx: IPA context + * + * Return: QDF STATUS + */ +static QDF_STATUS wlan_ipa_uc_handle_first_con(struct wlan_ipa_priv *ipa_ctx) +{ + ipa_debug("enter"); + + ipa_ctx->activated_fw_pipe = 0; + ipa_ctx->resource_loading = true; + + /* If RM feature enabled + * Request PROD Resource first + * PROD resource may return sync or async manners + */ + if (wlan_ipa_is_rm_enabled(ipa_ctx->config)) { + if (!wlan_ipa_wdi_rm_request_resource(ipa_ctx, + IPA_RM_RESOURCE_WLAN_PROD)) { + /* RM PROD request sync return + * enable pipe immediately + */ + if (wlan_ipa_uc_enable_pipes(ipa_ctx)) { + ipa_err("IPA WDI Pipe activation failed"); + ipa_ctx->resource_loading = false; + return QDF_STATUS_E_BUSY; + } + } else { + ipa_err("IPA WDI Pipe activation deferred"); + } + } else { + /* RM Disabled + * Just enabled all the PIPEs + */ + if (wlan_ipa_uc_enable_pipes(ipa_ctx)) { + ipa_err("IPA WDI Pipe activation failed"); + ipa_ctx->resource_loading = false; + return QDF_STATUS_E_BUSY; + } + ipa_ctx->resource_loading = false; + } + + ipa_debug("exit"); + + return QDF_STATUS_SUCCESS; +} + +/** + * wlan_ipa_uc_handle_last_discon() - Handle last uC IPA disconnection + * @ipa_ctx: IPA context + * @force_disable: force IPA pipes disablement + * + * Return: None + */ +static +void wlan_ipa_uc_handle_last_discon(struct wlan_ipa_priv *ipa_ctx, + bool force_disable) +{ + ipa_debug("enter"); + + ipa_ctx->resource_unloading = true; + qdf_event_reset(&ipa_ctx->ipa_resource_comp); + ipa_info("Disable FW RX PIPE"); + cdp_ipa_set_active(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id, false, false); + + ipa_debug("exit: IPA WDI Pipes deactivated"); +} + +bool wlan_ipa_is_fw_wdi_activated(struct wlan_ipa_priv *ipa_ctx) +{ + return (WLAN_IPA_UC_NUM_WDI_PIPE == ipa_ctx->activated_fw_pipe); +} + +static inline +void wlan_ipa_set_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx) +{ +} + +bool wlan_ipa_is_tx_pending(struct wlan_ipa_priv *ipa_ctx) +{ + return false; +} + +static inline +void wlan_ipa_reset_pending_tx_timer(struct wlan_ipa_priv *ipa_ctx) +{ +} + +#endif + +static inline +bool wlan_sap_no_client_connected(struct wlan_ipa_priv *ipa_ctx) +{ + return !(ipa_ctx->sap_num_connected_sta); +} + +static inline +bool wlan_sta_is_connected(struct wlan_ipa_priv *ipa_ctx) +{ + return ipa_ctx->sta_connected; +} + +static inline +bool wlan_ipa_uc_is_loaded(struct wlan_ipa_priv *ipa_ctx) +{ + return ipa_ctx->uc_loaded; +} + +/** + * wlan_ipa_uc_offload_enable_disable() - wdi enable/disable notify to fw + * @ipa_ctx: global IPA context + * @offload_type: MCC or SCC + * @session_id: Session Id + * @enable: TX offload enable or disable + * + * Return: none + */ +static void wlan_ipa_uc_offload_enable_disable(struct wlan_ipa_priv *ipa_ctx, + uint32_t offload_type, + uint8_t session_id, + bool enable) +{ + + struct ipa_uc_offload_control_params req = {0}; + + if (session_id >= WLAN_IPA_MAX_SESSION) { + ipa_err("invalid session id: %d", session_id); + return; + } + + if (enable == ipa_ctx->vdev_offload_enabled[session_id]) { + ipa_info("IPA offload status is already set"); + ipa_info("offload_type=%d, vdev_id=%d, enable=%d", + offload_type, session_id, enable); + return; + } + + ipa_info("offload_type=%d, session_id=%d, enable=%d", + offload_type, session_id, enable); + + req.offload_type = offload_type; + req.vdev_id = session_id; + req.enable = enable; + + if (QDF_STATUS_SUCCESS != + ipa_send_uc_offload_enable_disable(ipa_ctx->pdev, &req)) { + ipa_err("Fail to enable IPA offload"); + ipa_err("offload type=%d, vdev_id=%d, enable=%d", + offload_type, session_id, enable); + } else { + ipa_ctx->vdev_offload_enabled[session_id] = enable; + } +} + +#ifdef WDI3_STATS_UPDATE +static void wlan_ipa_uc_bw_monitor(struct wlan_ipa_priv *ipa_ctx, bool stop) +{ + qdf_ipa_wdi_bw_info_t bw_info; + uint32_t bw_low = ipa_ctx->config->ipa_bw_low; + uint32_t bw_medium = ipa_ctx->config->ipa_bw_medium; + uint32_t bw_high = ipa_ctx->config->ipa_bw_high; + int ret; + + bw_info.num = WLAN_IPA_UC_BW_MONITOR_LEVEL; + /* IPA uc will mobitor three bw levels for wlan client */ + QDF_IPA_WDI_BW_INFO_THRESHOLD_LEVEL_1(&bw_info) = bw_low; + QDF_IPA_WDI_BW_INFO_THRESHOLD_LEVEL_2(&bw_info) = bw_medium; + QDF_IPA_WDI_BW_INFO_THRESHOLD_LEVEL_3(&bw_info) = bw_high; + QDF_IPA_WDI_BW_INFO_START_STOP(&bw_info) = stop; + + ret = qdf_ipa_uc_bw_monitor(&bw_info); + if (ret) + ipa_err("ipa uc bw monitor fails"); + + if (!stop) { + cdp_ipa_set_perf_level(ipa_ctx->dp_soc, + QDF_IPA_CLIENT_WLAN2_CONS, + ipa_ctx->config->ipa_bw_low); + ipa_ctx->curr_bw_level = WLAN_IPA_BW_LEVEL_LOW; + } + + ipa_debug("ipa uc bw monitor %s", stop ? "stop" : "start"); +} +#else +static inline +void wlan_ipa_uc_bw_monitor(struct wlan_ipa_priv *ipa_ctx, bool stop) +{ +} +#endif + +/** + * wlan_ipa_send_msg() - Allocate and send message to IPA + * @net_dev: Interface net device + * @type: event enum of type ipa_wlan_event + * @mac_address: MAC address associated with the event + * + * Return: QDF STATUS + */ +static QDF_STATUS wlan_ipa_send_msg(qdf_netdev_t net_dev, + qdf_ipa_wlan_event type, + uint8_t *mac_addr) +{ + qdf_ipa_msg_meta_t meta; + qdf_ipa_wlan_msg_t *msg; + QDF_IPA_MSG_META_MSG_LEN(&meta) = sizeof(qdf_ipa_wlan_msg_t); + + msg = qdf_mem_malloc(QDF_IPA_MSG_META_MSG_LEN(&meta)); + + if (!msg) { + ipa_err("msg allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + QDF_IPA_SET_META_MSG_TYPE(&meta, type); + strlcpy(QDF_IPA_WLAN_MSG_NAME(msg), net_dev->name, IPA_RESOURCE_NAME_MAX); + qdf_mem_copy(QDF_IPA_WLAN_MSG_MAC_ADDR(msg), mac_addr, QDF_NET_ETH_LEN); + + ipa_debug("%s: Evt: %d", QDF_IPA_WLAN_MSG_NAME(msg), QDF_IPA_MSG_META_MSG_TYPE(&meta)); + + if (qdf_ipa_send_msg(&meta, msg, wlan_ipa_msg_free_fn)) { + ipa_err("%s: Evt: %d fail", + QDF_IPA_WLAN_MSG_NAME(msg), + QDF_IPA_MSG_META_MSG_TYPE(&meta)); + qdf_mem_free(msg); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef IPA_LAN_RX_NAPI_SUPPORT +void wlan_ipa_handle_multiple_sap_evt(struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_wlan_event type) +{ + struct wlan_ipa_iface_context *iface_ctx; + int i; + + if (type == QDF_IPA_AP_DISCONNECT) { + ipa_debug("Multiple SAP disconnecting. Enabling IPA"); + + if (ipa_ctx->sap_num_connected_sta > 0) + wlan_ipa_uc_handle_first_con(ipa_ctx); + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + + if (iface_ctx->device_mode == QDF_SAP_MODE) { + wlan_ipa_uc_offload_enable_disable(ipa_ctx, + SIR_AP_RX_DATA_OFFLOAD, + iface_ctx->session_id, + true); + break; + } + } + } else if (type == QDF_IPA_AP_CONNECT) { + ipa_debug("Multiple SAP connected. Disabling IPA"); + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + + if (iface_ctx->device_mode == QDF_SAP_MODE) { + wlan_ipa_uc_offload_enable_disable(ipa_ctx, + SIR_AP_RX_DATA_OFFLOAD, + iface_ctx->session_id, + false); + } + } + + if (!ipa_ctx->ipa_pipes_down) + wlan_ipa_uc_disable_pipes(ipa_ctx, true); + } +} +#endif + +static inline void +wlan_ipa_save_bssid_iface_ctx(struct wlan_ipa_priv *ipa_ctx, uint8_t iface_id, + uint8_t *mac_addr) +{ + qdf_mem_copy(ipa_ctx->iface_context[iface_id].bssid.bytes, + mac_addr, QDF_MAC_ADDR_SIZE); +} + +/** + * __wlan_ipa_wlan_evt() - IPA event handler + * @net_dev: Interface net device + * @device_mode: Net interface device mode + * @session_id: session id for the event + * @type: event enum of type ipa_wlan_event + * @mac_address: MAC address associated with the event + * + * This function is meant to be called from within wlan_ipa_ctx.c + * + * Return: QDF STATUS + */ +static QDF_STATUS __wlan_ipa_wlan_evt(qdf_netdev_t net_dev, uint8_t device_mode, + uint8_t session_id, + qdf_ipa_wlan_event type, + uint8_t *mac_addr) +{ + struct wlan_ipa_priv *ipa_ctx = gp_ipa; + struct wlan_ipa_iface_context *iface_ctx = NULL; + qdf_ipa_msg_meta_t meta; + qdf_ipa_wlan_msg_t *msg; + qdf_ipa_wlan_msg_ex_t *msg_ex = NULL; + int i; + QDF_STATUS status; + uint8_t sta_session_id = WLAN_IPA_MAX_SESSION; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + + ipa_debug("%s: EVT: %d, MAC: "QDF_MAC_ADDR_FMT", session_id: %u", + net_dev->name, type, QDF_MAC_ADDR_REF(mac_addr), session_id); + + if (type >= QDF_IPA_WLAN_EVENT_MAX) + return QDF_STATUS_E_INVAL; + + if (wlan_ipa_uc_is_enabled(ipa_ctx->config) && + !wlan_ipa_uc_sta_is_enabled(ipa_ctx->config) && + (device_mode != QDF_SAP_MODE)) { + return QDF_STATUS_SUCCESS; + } + + pdev = ipa_ctx->pdev; + psoc = wlan_pdev_get_psoc(pdev); + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, session_id, + WLAN_IPA_ID); + QDF_BUG(session_id < WLAN_IPA_MAX_SESSION); + + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_IPA_ID); + else + ipa_err("vdev is NULL, session_id: %u", session_id); + + if (ipa_ctx->sta_connected) { + iface_ctx = wlan_ipa_get_iface(ipa_ctx, QDF_STA_MODE); + if (iface_ctx) + sta_session_id = iface_ctx->session_id; + else + ipa_err("sta iface_ctx is NULL"); + } + + /* + * During IPA UC resource loading/unloading new events can be issued. + */ + if (wlan_ipa_uc_is_enabled(ipa_ctx->config) && + (ipa_ctx->resource_loading || ipa_ctx->resource_unloading)) { + unsigned int pending_event_count; + struct wlan_ipa_uc_pending_event *pending_event = NULL; + + ipa_info("Event:%d IPA resource %s inprogress", type, + ipa_ctx->resource_loading ? + "load" : "unload"); + + /* Wait until completion of the long/unloading */ + status = qdf_wait_for_event_completion( + &ipa_ctx->ipa_resource_comp, + IPA_RESOURCE_COMP_WAIT_TIME); + if (status != QDF_STATUS_SUCCESS) { + /* + * If timed out, store the events separately and + * handle them later. + */ + ipa_info("IPA resource %s timed out", + ipa_ctx->resource_loading ? + "load" : "unload"); + + if (type == QDF_IPA_AP_DISCONNECT) { + wlan_ipa_uc_offload_enable_disable(ipa_ctx, + SIR_AP_RX_DATA_OFFLOAD, + session_id, false); + } else if (type == QDF_IPA_CLIENT_CONNECT_EX && + wlan_sap_no_client_connected(ipa_ctx)) { + if (wlan_sta_is_connected(ipa_ctx) && + wlan_ipa_uc_is_loaded(ipa_ctx) && + wlan_ipa_uc_sta_is_enabled(ipa_ctx-> + config) && + !wlan_ipa_is_sta_only_offload_enabled()) { + wlan_ipa_uc_offload_enable_disable( + ipa_ctx, + SIR_STA_RX_DATA_OFFLOAD, + sta_session_id, true); + } + } + + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + + pending_event_count = + qdf_list_size(&ipa_ctx->pending_event); + if (pending_event_count >= + WLAN_IPA_MAX_PENDING_EVENT_COUNT) { + ipa_info("Reached max pending evt count"); + qdf_list_remove_front( + &ipa_ctx->pending_event, + (qdf_list_node_t **)&pending_event); + } else { + pending_event = + (struct wlan_ipa_uc_pending_event *) + qdf_mem_malloc(sizeof( + struct wlan_ipa_uc_pending_event)); + } + + if (!pending_event) { + ipa_err("Pending event memory alloc fail"); + qdf_mutex_release(&ipa_ctx->ipa_lock); + return QDF_STATUS_E_NOMEM; + } + + pending_event->net_dev = net_dev; + pending_event->device_mode = device_mode; + pending_event->session_id = session_id; + pending_event->type = type; + pending_event->is_loading = ipa_ctx->resource_loading; + qdf_mem_copy(pending_event->mac_addr, + mac_addr, QDF_MAC_ADDR_SIZE); + qdf_list_insert_back(&ipa_ctx->pending_event, + &pending_event->node); + + qdf_mutex_release(&ipa_ctx->ipa_lock); + + /* Cleanup interface */ + if (type == QDF_IPA_STA_DISCONNECT || + type == QDF_IPA_AP_DISCONNECT) { + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + if (iface_ctx->dev == net_dev) { + wlan_ipa_cleanup_iface( + iface_ctx); + break; + } + } + + if (qdf_ipa_get_lan_rx_napi() && + ipa_ctx->num_sap_connected == 1) { + wlan_ipa_handle_multiple_sap_evt(ipa_ctx, + type); + } + } + + return QDF_STATUS_SUCCESS; + } + ipa_info("IPA resource %s completed", + ipa_ctx->resource_loading ? + "load" : "unload"); + } + + ipa_ctx->stats.event[type]++; + + QDF_IPA_SET_META_MSG_TYPE(&meta, type); + switch (type) { + case QDF_IPA_STA_CONNECT: + qdf_mutex_acquire(&ipa_ctx->event_lock); + + /* STA already connected and without disconnect, connect again + * This is Roaming scenario + */ + if (ipa_ctx->sta_connected) { + iface_ctx = wlan_ipa_get_iface_by_mode_netdev( + ipa_ctx, net_dev, QDF_STA_MODE); + if (iface_ctx) + wlan_ipa_cleanup_iface(iface_ctx); + status = wlan_ipa_send_msg(net_dev, + QDF_IPA_STA_DISCONNECT, + mac_addr); + if (status != QDF_STATUS_SUCCESS) { + ipa_err("QDF_IPA_STA_DISCONNECT send failed %u", + status); + qdf_mutex_release(&ipa_ctx->event_lock); + goto end; + } + } + + status = wlan_ipa_setup_iface(ipa_ctx, net_dev, device_mode, + session_id); + if (status != QDF_STATUS_SUCCESS) { + ipa_err("wlan_ipa_setup_iface failed %u", status); + qdf_mutex_release(&ipa_ctx->event_lock); + goto end; + } + + ipa_ctx->vdev_to_iface[session_id] = + wlan_ipa_get_ifaceid(ipa_ctx, session_id); + + wlan_ipa_save_bssid_iface_ctx(ipa_ctx, + ipa_ctx->vdev_to_iface[session_id], + mac_addr); + + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config) && + (ipa_ctx->sap_num_connected_sta > 0 || + wlan_ipa_is_sta_only_offload_enabled()) && + !ipa_ctx->sta_connected) { + qdf_mutex_release(&ipa_ctx->event_lock); + wlan_ipa_uc_offload_enable_disable(ipa_ctx, + SIR_STA_RX_DATA_OFFLOAD, session_id, + true); + qdf_mutex_acquire(&ipa_ctx->event_lock); + qdf_atomic_set(&ipa_ctx->stats_quota, 1); + } + + if (!wlan_ipa_is_sta_only_offload_enabled()) { + ipa_debug("IPA STA only offload not enabled"); + } else if (ipa_ctx->uc_loaded && + !ipa_ctx->sap_num_connected_sta && + !ipa_ctx->sta_connected) { + status = wlan_ipa_uc_handle_first_con(ipa_ctx); + if (status) { + qdf_mutex_release(&ipa_ctx->event_lock); + ipa_info("handle 1st conn failed %d", status); + wlan_ipa_uc_offload_enable_disable( + ipa_ctx, + SIR_STA_RX_DATA_OFFLOAD, + session_id, + false); + ipa_ctx->vdev_to_iface[session_id] = + WLAN_IPA_MAX_SESSION; + goto end; + } + } + + ipa_ctx->sta_connected++; + + if (qdf_ipa_get_lan_rx_napi() && ipa_ctx->sap_num_connected_sta) + ipa_set_rps_per_vdev(ipa_ctx, session_id, true); + + qdf_mutex_release(&ipa_ctx->event_lock); + + ipa_debug("sta_connected=%d vdev_to_iface[%u] %u", + ipa_ctx->sta_connected, + session_id, + ipa_ctx->vdev_to_iface[session_id]); + break; + + case QDF_IPA_AP_CONNECT: + qdf_mutex_acquire(&ipa_ctx->event_lock); + + /* For DFS channel we get two start_bss event (before and after + * CAC). Also when ACS range includes both DFS and non DFS + * channels, we could possibly change channel many times due to + * RADAR detection and chosen channel may not be a DFS channels. + * So dont return error here. Just discard the event. + */ + if (ipa_ctx->vdev_to_iface[session_id] != + WLAN_IPA_MAX_SESSION) { + qdf_mutex_release(&ipa_ctx->event_lock); + return 0; + } + + status = wlan_ipa_setup_iface(ipa_ctx, net_dev, device_mode, + session_id); + if (status != QDF_STATUS_SUCCESS) { + qdf_mutex_release(&ipa_ctx->event_lock); + ipa_err("%s: Evt: %d, Interface setup failed", + msg_ex->name, QDF_IPA_MSG_META_MSG_TYPE(&meta)); + goto end; + } + + if (wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + qdf_mutex_release(&ipa_ctx->event_lock); + if (qdf_ipa_get_lan_rx_napi() && + (ipa_ctx->num_sap_connected > 1)) { + wlan_ipa_handle_multiple_sap_evt(ipa_ctx, type); + } else { + wlan_ipa_uc_offload_enable_disable(ipa_ctx, + SIR_AP_RX_DATA_OFFLOAD, + session_id, true); + } + qdf_mutex_acquire(&ipa_ctx->event_lock); + } + + ipa_ctx->vdev_to_iface[session_id] = + wlan_ipa_get_ifaceid(ipa_ctx, session_id); + ipa_debug("vdev_to_iface[%u]=%u", + session_id, + ipa_ctx->vdev_to_iface[session_id]); + qdf_mutex_release(&ipa_ctx->event_lock); + break; + + case QDF_IPA_STA_DISCONNECT: + qdf_mutex_acquire(&ipa_ctx->event_lock); + + if (!ipa_ctx->sta_connected) { + struct wlan_ipa_iface_context *iface; + + qdf_mutex_release(&ipa_ctx->event_lock); + ipa_info("%s: Evt: %d, STA already disconnected", + msg_ex->name, + QDF_IPA_MSG_META_MSG_TYPE(&meta)); + + iface = wlan_ipa_get_iface_by_mode_netdev(ipa_ctx, + net_dev, + QDF_STA_MODE); + if (iface) + wlan_ipa_cleanup_iface(iface); + + return QDF_STATUS_E_INVAL; + } + + ipa_ctx->sta_connected--; + + if (!wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + ipa_debug("%s: IPA UC OFFLOAD NOT ENABLED", + msg_ex->name); + } else { + /* + * Disable IPA pipes when + * 1. STA is the last interface or + * 2. STA only offload enabled and no clients connected + * to SAP + */ + if ((ipa_ctx->num_iface == 1 || + (wlan_ipa_is_sta_only_offload_enabled() && + !ipa_ctx->sap_num_connected_sta)) && + wlan_ipa_is_fw_wdi_activated(ipa_ctx) && + !ipa_ctx->ipa_pipes_down && + (ipa_ctx->resource_unloading == false)) { + if (cds_is_driver_unloading()) { + /* + * We disable WDI pipes directly here + * since IPA_OPCODE_TX/RX_SUSPEND + * message will not be processed when + * unloading WLAN driver is in progress + */ + wlan_ipa_uc_disable_pipes(ipa_ctx, + true); + } else { + wlan_ipa_uc_handle_last_discon(ipa_ctx, + true); + } + } + } + + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config) && + (ipa_ctx->sap_num_connected_sta > 0 || + wlan_ipa_is_sta_only_offload_enabled())) { + qdf_atomic_set(&ipa_ctx->stats_quota, 0); + qdf_mutex_release(&ipa_ctx->event_lock); + wlan_ipa_uc_offload_enable_disable(ipa_ctx, + SIR_STA_RX_DATA_OFFLOAD, session_id, false); + qdf_mutex_acquire(&ipa_ctx->event_lock); + } + + ipa_ctx->vdev_to_iface[session_id] = WLAN_IPA_MAX_SESSION; + ipa_debug("vdev_to_iface[%u]=%u", session_id, + ipa_ctx->vdev_to_iface[session_id]); + + iface_ctx = wlan_ipa_get_iface_by_mode_netdev(ipa_ctx, + net_dev, + QDF_STA_MODE); + if (iface_ctx) + wlan_ipa_cleanup_iface(iface_ctx); + + if (qdf_ipa_get_lan_rx_napi() && ipa_ctx->sap_num_connected_sta) + ipa_set_rps_per_vdev(ipa_ctx, session_id, false); + + qdf_mutex_release(&ipa_ctx->event_lock); + + ipa_debug("sta_connected=%d", ipa_ctx->sta_connected); + break; + + case QDF_IPA_AP_DISCONNECT: + qdf_mutex_acquire(&ipa_ctx->event_lock); + + if ((ipa_ctx->num_iface == 1) && + wlan_ipa_is_fw_wdi_activated(ipa_ctx) && + !ipa_ctx->ipa_pipes_down && + (ipa_ctx->resource_unloading == false)) { + if (cds_is_driver_unloading()) { + /* + * We disable WDI pipes directly here since + * IPA_OPCODE_TX/RX_SUSPEND message will not be + * processed when unloading WLAN driver is in + * progress + */ + wlan_ipa_uc_disable_pipes(ipa_ctx, true); + } else { + /* + * This shouldn't happen : + * No interface left but WDI pipes are still + * active - force close WDI pipes + */ + ipa_err("No interface left but WDI pipes are still active"); + wlan_ipa_uc_handle_last_discon(ipa_ctx, true); + } + } + + if (wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + qdf_mutex_release(&ipa_ctx->event_lock); + wlan_ipa_uc_offload_enable_disable(ipa_ctx, + SIR_AP_RX_DATA_OFFLOAD, session_id, false); + qdf_mutex_acquire(&ipa_ctx->event_lock); + ipa_ctx->vdev_to_iface[session_id] = + WLAN_IPA_MAX_SESSION; + ipa_debug("vdev_to_iface[%u]=%u", + session_id, + ipa_ctx->vdev_to_iface[session_id]); + } + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + if (iface_ctx->dev == net_dev) { + wlan_ipa_cleanup_iface(iface_ctx); + break; + } + } + + if (qdf_ipa_get_lan_rx_napi() && + (ipa_ctx->num_sap_connected == 1)) + wlan_ipa_handle_multiple_sap_evt(ipa_ctx, type); + + qdf_mutex_release(&ipa_ctx->event_lock); + break; + + case QDF_IPA_CLIENT_CONNECT_EX: + if (!wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + ipa_debug("%s: Evt: %d, IPA UC OFFLOAD NOT ENABLED", + net_dev->name, type); + return QDF_STATUS_SUCCESS; + } + + qdf_mutex_acquire(&ipa_ctx->event_lock); + if (wlan_ipa_uc_find_add_assoc_sta(ipa_ctx, true, + mac_addr)) { + qdf_mutex_release(&ipa_ctx->event_lock); + ipa_err("%s: STA found, addr: " QDF_MAC_ADDR_FMT, + net_dev->name, + QDF_MAC_ADDR_REF(mac_addr)); + return QDF_STATUS_SUCCESS; + } + + /* Enable IPA UC Data PIPEs when first STA connected */ + if (ipa_ctx->sap_num_connected_sta == 0 && + ipa_ctx->uc_loaded == true) { + + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config) && + ipa_ctx->sta_connected && + !wlan_ipa_is_sta_only_offload_enabled()) { + qdf_mutex_release(&ipa_ctx->event_lock); + wlan_ipa_uc_offload_enable_disable(ipa_ctx, + SIR_STA_RX_DATA_OFFLOAD, + sta_session_id, true); + qdf_mutex_acquire(&ipa_ctx->event_lock); + qdf_atomic_set(&ipa_ctx->stats_quota, 1); + } + + /* + * IPA pipes already enabled if STA only offload + * is enabled and STA is connected to remote AP. + */ + if (wlan_ipa_is_sta_only_offload_enabled() && + ipa_ctx->sta_connected) { + ipa_debug("IPA pipes already enabled"); + } else if (wlan_ipa_uc_handle_first_con(ipa_ctx)) { + ipa_info("%s: handle 1st con fail", + net_dev->name); + + if (wlan_ipa_uc_sta_is_enabled( + ipa_ctx->config) && + ipa_ctx->sta_connected && + !wlan_ipa_is_sta_only_offload_enabled()) { + qdf_atomic_set(&ipa_ctx->stats_quota, + 0); + qdf_mutex_release(&ipa_ctx->event_lock); + wlan_ipa_uc_offload_enable_disable( + ipa_ctx, + SIR_STA_RX_DATA_OFFLOAD, + sta_session_id, false); + } else { + qdf_mutex_release(&ipa_ctx->event_lock); + } + + return QDF_STATUS_E_BUSY; + } + wlan_ipa_uc_bw_monitor(ipa_ctx, false); + ipa_info("first sap client connected"); + } + + ipa_ctx->sap_num_connected_sta++; + + qdf_mutex_release(&ipa_ctx->event_lock); + + QDF_IPA_SET_META_MSG_TYPE(&meta, type); + QDF_IPA_MSG_META_MSG_LEN(&meta) = + (sizeof(qdf_ipa_wlan_msg_ex_t) + + sizeof(qdf_ipa_wlan_hdr_attrib_val_t)); + msg_ex = qdf_mem_malloc(QDF_IPA_MSG_META_MSG_LEN(&meta)); + + if (!msg_ex) { + ipa_err("msg_ex allocation failed"); + return QDF_STATUS_E_NOMEM; + } + strlcpy(msg_ex->name, net_dev->name, + IPA_RESOURCE_NAME_MAX); + msg_ex->num_of_attribs = 1; + msg_ex->attribs[0].attrib_type = WLAN_HDR_ATTRIB_MAC_ADDR; + if (wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + msg_ex->attribs[0].offset = + WLAN_IPA_UC_WLAN_HDR_DES_MAC_OFFSET; + } else { + msg_ex->attribs[0].offset = + WLAN_IPA_WLAN_HDR_DES_MAC_OFFSET; + } + memcpy(msg_ex->attribs[0].u.mac_addr, mac_addr, + IPA_MAC_ADDR_SIZE); + + if (qdf_ipa_send_msg(&meta, msg_ex, wlan_ipa_msg_free_fn)) { + ipa_info("%s: Evt: %d send ipa msg fail", + net_dev->name, type); + qdf_mem_free(msg_ex); + return QDF_STATUS_E_FAILURE; + } + ipa_ctx->stats.num_send_msg++; + + ipa_debug("sap_num_connected_sta=%d", + ipa_ctx->sap_num_connected_sta); + + return QDF_STATUS_SUCCESS; + + case WLAN_CLIENT_DISCONNECT: + if (!wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + ipa_debug("%s: IPA UC OFFLOAD NOT ENABLED", + msg_ex->name); + return QDF_STATUS_SUCCESS; + } + + qdf_mutex_acquire(&ipa_ctx->event_lock); + if (!ipa_ctx->sap_num_connected_sta) { + qdf_mutex_release(&ipa_ctx->event_lock); + ipa_debug("%s: Evt: %d, Client already disconnected", + msg_ex->name, + QDF_IPA_MSG_META_MSG_TYPE(&meta)); + + return QDF_STATUS_SUCCESS; + } + if (!wlan_ipa_uc_find_add_assoc_sta(ipa_ctx, false, + mac_addr)) { + qdf_mutex_release(&ipa_ctx->event_lock); + ipa_debug("%s: STA NOT found, not valid: " + QDF_MAC_ADDR_FMT, + msg_ex->name, QDF_MAC_ADDR_REF(mac_addr)); + + return QDF_STATUS_SUCCESS; + } + ipa_ctx->sap_num_connected_sta--; + + /* + * Disable IPA pipes when + * 1. last client disconnected and + * 2. STA is not connected if STA only offload is enabled + */ + if (!ipa_ctx->sap_num_connected_sta && + ipa_ctx->uc_loaded && + !(wlan_ipa_is_sta_only_offload_enabled() && + ipa_ctx->sta_connected)) { + if ((false == ipa_ctx->resource_unloading) && + wlan_ipa_is_fw_wdi_activated(ipa_ctx) && + !ipa_ctx->ipa_pipes_down) { + if (cds_is_driver_unloading()) { + /* + * We disable WDI pipes directly here + * since IPA_OPCODE_TX/RX_SUSPEND + * message will not be processed when + * unloading WLAN driver is in progress + */ + + wlan_ipa_uc_bw_monitor(ipa_ctx, true); + wlan_ipa_uc_disable_pipes(ipa_ctx, + true); + } else { + /* + * If STA is connected, wait for IPA TX + * completions before disabling + * IPA pipes + */ + wlan_ipa_uc_handle_last_discon(ipa_ctx, + !ipa_ctx->sta_connected); + wlan_ipa_uc_bw_monitor(ipa_ctx, true); + } + ipa_info("last sap client disconnected"); + } + + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config) && + ipa_ctx->sta_connected && + !wlan_ipa_is_sta_only_offload_enabled()) { + qdf_atomic_set(&ipa_ctx->stats_quota, 0); + qdf_mutex_release(&ipa_ctx->event_lock); + wlan_ipa_uc_offload_enable_disable(ipa_ctx, + SIR_STA_RX_DATA_OFFLOAD, + sta_session_id, false); + } else { + qdf_mutex_release(&ipa_ctx->event_lock); + } + } else { + qdf_mutex_release(&ipa_ctx->event_lock); + } + + ipa_debug("sap_num_connected_sta=%d", + ipa_ctx->sap_num_connected_sta); + break; + + default: + return QDF_STATUS_SUCCESS; + } + + QDF_IPA_MSG_META_MSG_LEN(&meta) = sizeof(qdf_ipa_wlan_msg_t); + msg = qdf_mem_malloc(QDF_IPA_MSG_META_MSG_LEN(&meta)); + if (!msg) { + ipa_err("msg allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + QDF_IPA_SET_META_MSG_TYPE(&meta, type); + strlcpy(QDF_IPA_WLAN_MSG_NAME(msg), net_dev->name, + IPA_RESOURCE_NAME_MAX); + qdf_mem_copy(QDF_IPA_WLAN_MSG_MAC_ADDR(msg), mac_addr, QDF_NET_ETH_LEN); + + ipa_debug("%s: Evt: %d", QDF_IPA_WLAN_MSG_NAME(msg), + QDF_IPA_MSG_META_MSG_TYPE(&meta)); + + if (qdf_ipa_send_msg(&meta, msg, wlan_ipa_msg_free_fn)) { + + ipa_err("%s: Evt: %d fail", + QDF_IPA_WLAN_MSG_NAME(msg), + QDF_IPA_MSG_META_MSG_TYPE(&meta)); + qdf_mem_free(msg); + + return QDF_STATUS_E_FAILURE; + } + + ipa_ctx->stats.num_send_msg++; + +end: + return QDF_STATUS_SUCCESS; +} + +/** + * wlan_host_to_ipa_wlan_event() - convert wlan_ipa_wlan_event to ipa_wlan_event + * @wlan_ipa_event_type: event to be converted to an ipa_wlan_event + * + * Return: qdf_ipa_wlan_event representing the wlan_ipa_wlan_event + */ +static qdf_ipa_wlan_event +wlan_host_to_ipa_wlan_event(enum wlan_ipa_wlan_event wlan_ipa_event_type) +{ + qdf_ipa_wlan_event ipa_event; + + switch (wlan_ipa_event_type) { + case WLAN_IPA_CLIENT_CONNECT: + ipa_event = QDF_IPA_CLIENT_CONNECT; + break; + case WLAN_IPA_CLIENT_DISCONNECT: + ipa_event = QDF_IPA_CLIENT_DISCONNECT; + break; + case WLAN_IPA_AP_CONNECT: + ipa_event = QDF_IPA_AP_CONNECT; + break; + case WLAN_IPA_AP_DISCONNECT: + ipa_event = QDF_IPA_AP_DISCONNECT; + break; + case WLAN_IPA_STA_CONNECT: + ipa_event = QDF_IPA_STA_CONNECT; + break; + case WLAN_IPA_STA_DISCONNECT: + ipa_event = QDF_IPA_STA_DISCONNECT; + break; + case WLAN_IPA_CLIENT_CONNECT_EX: + ipa_event = QDF_IPA_CLIENT_CONNECT_EX; + break; + case WLAN_IPA_WLAN_EVENT_MAX: + default: + ipa_event = QDF_IPA_WLAN_EVENT_MAX; + break; + } + + return ipa_event; +} + +/** + * wlan_ipa_wlan_evt() - SSR wrapper for __wlan_ipa_wlan_evt + * @net_dev: Interface net device + * @device_mode: Net interface device mode + * @session_id: session id for the event + * @ipa_event_type: event enum of type wlan_ipa_wlan_event + * @mac_address: MAC address associated with the event + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wlan_evt(qdf_netdev_t net_dev, uint8_t device_mode, + uint8_t session_id, + enum wlan_ipa_wlan_event ipa_event_type, + uint8_t *mac_addr) +{ + qdf_ipa_wlan_event type = wlan_host_to_ipa_wlan_event(ipa_event_type); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* Data path offload only support for STA and SAP mode */ + if ((device_mode == QDF_STA_MODE) || + (device_mode == QDF_SAP_MODE)) + status = __wlan_ipa_wlan_evt(net_dev, device_mode, + session_id, type, mac_addr); + + return status; +} + +/** + * wlan_ipa_uc_proc_pending_event() - Process IPA uC pending events + * @ipa_ctx: Global IPA IPA context + * @is_loading: Indicate if invoked during loading + * + * Return: None + */ +static void +wlan_ipa_uc_proc_pending_event(struct wlan_ipa_priv *ipa_ctx, bool is_loading) +{ + unsigned int pending_event_count; + struct wlan_ipa_uc_pending_event *pending_event = NULL; + + pending_event_count = qdf_list_size(&ipa_ctx->pending_event); + ipa_debug("Pending Event Count %d", pending_event_count); + if (!pending_event_count) { + ipa_debug("No Pending Event"); + return; + } + + qdf_list_remove_front(&ipa_ctx->pending_event, + (qdf_list_node_t **)&pending_event); + while (pending_event) { + struct wlan_objmgr_pdev *pdev = ipa_ctx->pdev; + struct wlan_objmgr_psoc *psoc = wlan_pdev_get_psoc(pdev); + struct wlan_objmgr_vdev *vdev = + wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + pending_event->session_id, + WLAN_IPA_ID); + if (pending_event->is_loading == is_loading && vdev) { + __wlan_ipa_wlan_evt(pending_event->net_dev, + pending_event->device_mode, + pending_event->session_id, + pending_event->type, + pending_event->mac_addr); + } + + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_IPA_ID); + qdf_mem_free(pending_event); + pending_event = NULL; + qdf_list_remove_front(&ipa_ctx->pending_event, + (qdf_list_node_t **)&pending_event); + } +} + +/** + * wlan_ipa_free_tx_desc_list() - Free IPA Tx desc list + * @ipa_ctx: IPA context + * + * Return: None + */ +static inline void wlan_ipa_free_tx_desc_list(struct wlan_ipa_priv *ipa_ctx) +{ + int i; + qdf_ipa_rx_data_t *ipa_tx_desc; + uint32_t pool_size; + + if (!ipa_ctx->tx_desc_pool) + return; + + qdf_spin_lock_bh(&ipa_ctx->q_lock); + pool_size = ipa_ctx->tx_desc_free_list.max_size; + for (i = 0; i < pool_size; i++) { + ipa_tx_desc = ipa_ctx->tx_desc_pool[i].ipa_tx_desc_ptr; + if (ipa_tx_desc) + qdf_ipa_free_skb(ipa_tx_desc); + + if (ipa_ctx->tx_desc_free_list.count && + qdf_list_remove_node(&ipa_ctx->tx_desc_free_list, + &ipa_ctx->tx_desc_pool[i].node) != + QDF_STATUS_SUCCESS) + ipa_err("Failed to remove node from tx desc freelist"); + } + qdf_spin_unlock_bh(&ipa_ctx->q_lock); + + qdf_list_destroy(&ipa_ctx->tx_desc_free_list); + qdf_mem_free(ipa_ctx->tx_desc_pool); + ipa_ctx->tx_desc_pool = NULL; + + ipa_ctx->stats.num_tx_desc_q_cnt = 0; + ipa_ctx->stats.num_tx_desc_error = 0; +} + +/** + * wlan_ipa_alloc_tx_desc_free_list() - Allocate IPA Tx desc list + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +static QDF_STATUS +wlan_ipa_alloc_tx_desc_free_list(struct wlan_ipa_priv *ipa_ctx) +{ + int i; + uint32_t max_desc_cnt; + + max_desc_cnt = ipa_ctx->config->txbuf_count; + + ipa_ctx->tx_desc_pool = qdf_mem_malloc(sizeof(struct wlan_ipa_tx_desc) * + max_desc_cnt); + + if (!ipa_ctx->tx_desc_pool) { + ipa_err("Free Tx descriptor allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + qdf_list_create(&ipa_ctx->tx_desc_free_list, max_desc_cnt); + + qdf_spin_lock_bh(&ipa_ctx->q_lock); + for (i = 0; i < max_desc_cnt; i++) { + ipa_ctx->tx_desc_pool[i].id = i; + ipa_ctx->tx_desc_pool[i].ipa_tx_desc_ptr = NULL; + qdf_list_insert_back(&ipa_ctx->tx_desc_free_list, + &ipa_ctx->tx_desc_pool[i].node); + } + + ipa_ctx->stats.num_tx_desc_q_cnt = 0; + ipa_ctx->stats.num_tx_desc_error = 0; + + qdf_spin_unlock_bh(&ipa_ctx->q_lock); + + return QDF_STATUS_SUCCESS; +} + +#ifndef QCA_LL_TX_FLOW_CONTROL_V2 +/** + * wlan_ipa_setup_tx_sys_pipe() - Setup IPA Tx system pipes + * @ipa_ctx: Global IPA IPA context + * @desc_fifo_sz: Number of descriptors + * + * Return: 0 on success, negative errno on error + */ +static int wlan_ipa_setup_tx_sys_pipe(struct wlan_ipa_priv *ipa_ctx, + int32_t desc_fifo_sz) +{ + int i, ret = 0; + qdf_ipa_sys_connect_params_t *ipa; + + /*setup TX pipes */ + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + ipa = &ipa_ctx->sys_pipe[i].ipa_sys_params; + + ipa->client = wlan_ipa_iface_2_client[i].cons_client; + ipa->desc_fifo_sz = desc_fifo_sz; + ipa->priv = &ipa_ctx->iface_context[i]; + ipa->notify = wlan_ipa_i2w_cb; + + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) { + ipa->ipa_ep_cfg.hdr.hdr_len = + WLAN_IPA_UC_WLAN_TX_HDR_LEN; + ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT; + ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1; + ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0; + ipa->ipa_ep_cfg.hdr.hdr_additional_const_len = + WLAN_IPA_UC_WLAN_8023_HDR_SIZE; + ipa->ipa_ep_cfg.hdr_ext.hdr_little_endian = true; + } else { + ipa->ipa_ep_cfg.hdr.hdr_len = WLAN_IPA_WLAN_TX_HDR_LEN; + } + ipa->ipa_ep_cfg.mode.mode = IPA_BASIC; + + ret = wlan_ipa_wdi_setup_sys_pipe(ipa_ctx, ipa, + &ipa_ctx->sys_pipe[i].conn_hdl); + if (ret) { + ipa_err("Failed for pipe %d ret: %d", i, ret); + return ret; + } + ipa_ctx->sys_pipe[i].conn_hdl_valid = 1; + } + + return ret; +} +#else +/** + * wlan_ipa_setup_tx_sys_pipe() - Setup IPA Tx system pipes + * @ipa_ctx: IPA context + * @desc_fifo_sz: Number of descriptors + * + * Return: 0 on success, negative errno on error + */ +static int wlan_ipa_setup_tx_sys_pipe(struct wlan_ipa_priv *ipa_ctx, + int32_t desc_fifo_sz) +{ + /* + * The Tx system pipes are not needed for MCC when TX_FLOW_CONTROL_V2 + * is enabled, where per vdev descriptors are supported in firmware. + */ + return 0; +} +#endif + +#if defined(CONFIG_IPA_WDI_UNIFIED_API) && defined(IPA_WDI3_GSI) +/** + * wlan_ipa_get_rx_ipa_client() - Get IPA RX ipa client + * @ipa_ctx: IPA context + * + * Return: rx ipa sys client + */ +static inline uint8_t wlan_ipa_get_rx_ipa_client(struct wlan_ipa_priv *ipa_ctx) +{ + if (ipa_ctx->over_gsi) + return IPA_CLIENT_WLAN2_PROD; + else + return IPA_CLIENT_WLAN1_PROD; +} + +/** + * wlan_ipa_uc_send_wdi_control_msg() - Set WDI control message + * @ctrl: WDI control value + * + * Send WLAN_WDI_ENABLE for ctrl = true and WLAN_WDI_DISABLE otherwise. + * + * Return: QDF_STATUS + */ +static QDF_STATUS wlan_ipa_uc_send_wdi_control_msg(bool ctrl) +{ + return QDF_STATUS_SUCCESS; +} + +#else +static inline uint8_t wlan_ipa_get_rx_ipa_client(struct wlan_ipa_priv *ipa_ctx) +{ + return IPA_CLIENT_WLAN1_PROD; +} + +static QDF_STATUS wlan_ipa_uc_send_wdi_control_msg(bool ctrl) +{ + struct wlan_ipa_priv *ipa_ctx = gp_ipa; + qdf_ipa_msg_meta_t meta; + qdf_ipa_wlan_msg_t *ipa_msg; + int ret = 0; + + /* WDI enable message to IPA */ + QDF_IPA_MSG_META_MSG_LEN(&meta) = sizeof(*ipa_msg); + ipa_msg = qdf_mem_malloc(QDF_IPA_MSG_META_MSG_LEN(&meta)); + if (!ipa_msg) { + ipa_err("msg allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + if (ctrl) { + QDF_IPA_SET_META_MSG_TYPE(&meta, QDF_WDI_ENABLE); + ipa_ctx->stats.event[QDF_WDI_ENABLE]++; + } else { + QDF_IPA_SET_META_MSG_TYPE(&meta, QDF_WDI_DISABLE); + ipa_ctx->stats.event[QDF_WDI_DISABLE]++; + } + + ipa_debug("ipa_send_msg(Evt:%d)", QDF_IPA_MSG_META_MSG_TYPE(&meta)); + ret = qdf_ipa_send_msg(&meta, ipa_msg, wlan_ipa_msg_free_fn); + if (ret) { + ipa_err("ipa_send_msg(Evt:%d)-fail=%d", + QDF_IPA_MSG_META_MSG_TYPE(&meta), ret); + qdf_mem_free(ipa_msg); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * wlan_ipa_setup_rx_sys_pipe() - Setup IPA Rx system pipes + * @ipa_ctx: Global IPA IPA context + * @desc_fifo_sz: Number of descriptors + * + * Return: 0 on success, negative errno on error + */ +static int wlan_ipa_setup_rx_sys_pipe(struct wlan_ipa_priv *ipa_ctx, + int32_t desc_fifo_sz) +{ + int ret = 0; + qdf_ipa_sys_connect_params_t *ipa; + + /* + * Hard code it here, this can be extended if in case + * PROD pipe is also per interface. + * Right now there is no advantage of doing this. + */ + ipa = &ipa_ctx->sys_pipe[WLAN_IPA_RX_PIPE].ipa_sys_params; + + ipa->client = wlan_ipa_get_rx_ipa_client(ipa_ctx); + ipa->desc_fifo_sz = desc_fifo_sz; + ipa->priv = ipa_ctx; + ipa->notify = wlan_ipa_w2i_cb; + + ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT; + ipa->ipa_ep_cfg.hdr.hdr_len = WLAN_IPA_WLAN_RX_HDR_LEN; + ipa->ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1; + ipa->ipa_ep_cfg.mode.mode = IPA_BASIC; + + ret = qdf_ipa_setup_sys_pipe(ipa, + &ipa_ctx->sys_pipe[WLAN_IPA_RX_PIPE].conn_hdl); + if (ret) { + ipa_err("Failed for RX pipe: %d", ret); + return ret; + } + ipa_ctx->sys_pipe[WLAN_IPA_RX_PIPE].conn_hdl_valid = 1; + + return ret; +} + +/** + * wlan_ipa_teardown_sys_pipe() - Tear down all IPA Sys pipes + * @ipa_ctx: Global IPA IPA context + * + * Return: None + */ +static void wlan_ipa_teardown_sys_pipe(struct wlan_ipa_priv *ipa_ctx) +{ + int ret, i; + + if (!ipa_ctx) + return; + + for (i = 0; i < WLAN_IPA_MAX_SYSBAM_PIPE; i++) { + if (ipa_ctx->sys_pipe[i].conn_hdl_valid) { + ret = wlan_ipa_wdi_teardown_sys_pipe(ipa_ctx, + ipa_ctx->sys_pipe[i].conn_hdl); + if (ret) + ipa_err("Failed:%d", ret); + + ipa_ctx->sys_pipe[i].conn_hdl_valid = 0; + } + } + + wlan_ipa_free_tx_desc_list(ipa_ctx); +} + +/** + * wlan_ipa_setup_sys_pipe() - Setup all IPA system pipes + * @ipa_ctx: Global IPA IPA context + * + * Return: 0 on success, negative errno on error + */ +static int wlan_ipa_setup_sys_pipe(struct wlan_ipa_priv *ipa_ctx) +{ + int ret = 0; + uint32_t desc_fifo_sz; + + /* The maximum number of descriptors that can be provided to a BAM at + * once is one less than the total number of descriptors that the buffer + * can contain. + * If max_num_of_descriptors = (BAM_PIPE_DESCRIPTOR_FIFO_SIZE / sizeof + * (SPS_DESCRIPTOR)), then (max_num_of_descriptors - 1) descriptors can + * be provided at once. + * Because of above requirement, one extra descriptor will be added to + * make sure hardware always has one descriptor. + */ + desc_fifo_sz = ipa_ctx->config->desc_size + + SPS_DESC_SIZE; + + ret = wlan_ipa_setup_tx_sys_pipe(ipa_ctx, desc_fifo_sz); + if (ret) { + ipa_err("Failed for TX pipe: %d", ret); + goto setup_sys_pipe_fail; + } + + if (!wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) { + ret = wlan_ipa_setup_rx_sys_pipe(ipa_ctx, desc_fifo_sz); + if (ret) { + ipa_err("Failed for RX pipe: %d", ret); + goto setup_sys_pipe_fail; + } + } + + /* Allocate free Tx desc list */ + ret = wlan_ipa_alloc_tx_desc_free_list(ipa_ctx); + if (ret) + goto setup_sys_pipe_fail; + + return ret; + +setup_sys_pipe_fail: + wlan_ipa_teardown_sys_pipe(ipa_ctx); + + return ret; +} + +#ifndef QCA_LL_TX_FLOW_CONTROL_V2 +QDF_STATUS wlan_ipa_send_mcc_scc_msg(struct wlan_ipa_priv *ipa_ctx, + bool mcc_mode) +{ + qdf_ipa_msg_meta_t meta; + qdf_ipa_wlan_msg_t *msg; + int ret; + + if (!wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) + return QDF_STATUS_SUCCESS; + + /* Send SCC/MCC Switching event to IPA */ + QDF_IPA_MSG_META_MSG_LEN(&meta) = sizeof(*msg); + msg = qdf_mem_malloc(QDF_IPA_MSG_META_MSG_LEN(&meta)); + if (!msg) { + ipa_err("msg allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + if (mcc_mode) { + QDF_IPA_SET_META_MSG_TYPE(&meta, QDF_SWITCH_TO_MCC); + ipa_ctx->stats.event[QDF_SWITCH_TO_MCC]++; + } else { + QDF_IPA_SET_META_MSG_TYPE(&meta, QDF_SWITCH_TO_SCC); + ipa_ctx->stats.event[QDF_SWITCH_TO_SCC]++; + } + + WLAN_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, + "ipa_send_msg(Evt:%d)", + QDF_IPA_MSG_META_MSG_TYPE(&meta)); + + ret = qdf_ipa_send_msg(&meta, msg, wlan_ipa_msg_free_fn); + + if (ret) { + ipa_err("ipa_send_msg(Evt:%d) - fail=%d", + QDF_IPA_MSG_META_MSG_TYPE(&meta), ret); + qdf_mem_free(msg); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +static void wlan_ipa_mcc_work_handler(void *data) +{ + struct wlan_ipa_priv *ipa_ctx = (struct wlan_ipa_priv *)data; + + wlan_ipa_send_mcc_scc_msg(ipa_ctx, ipa_ctx->mcc_mode); +} +#endif + +/** + * wlan_ipa_setup() - IPA initialization function + * @ipa_ctx: IPA context + * @ipa_cfg: IPA config + * + * Allocate ipa_ctx resources, ipa pipe resource and register + * wlan interface with IPA module. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS wlan_ipa_setup(struct wlan_ipa_priv *ipa_ctx, + struct wlan_ipa_config *ipa_cfg) +{ + int ret, i; + struct wlan_ipa_iface_context *iface_context = NULL; + QDF_STATUS status; + + ipa_debug("enter"); + + gp_ipa = ipa_ctx; + ipa_ctx->num_iface = 0; + ipa_ctx->config = ipa_cfg; + + wlan_ipa_wdi_get_wdi_version(ipa_ctx); + + /* Create the interface context */ + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_context = &ipa_ctx->iface_context[i]; + iface_context->ipa_ctx = ipa_ctx; + iface_context->cons_client = + wlan_ipa_iface_2_client[i].cons_client; + iface_context->prod_client = + wlan_ipa_iface_2_client[i].prod_client; + iface_context->iface_id = i; + iface_context->dev = NULL; + iface_context->device_mode = QDF_MAX_NO_OF_MODE; + iface_context->session_id = WLAN_IPA_MAX_SESSION; + qdf_spinlock_create(&iface_context->interface_lock); + } + + qdf_create_work(0, &ipa_ctx->pm_work, wlan_ipa_pm_flush, ipa_ctx); + qdf_spinlock_create(&ipa_ctx->pm_lock); + qdf_spinlock_create(&ipa_ctx->q_lock); + qdf_spinlock_create(&ipa_ctx->enable_disable_lock); + ipa_ctx->pipes_down_in_progress = false; + ipa_ctx->pipes_enable_in_progress = false; + qdf_nbuf_queue_init(&ipa_ctx->pm_queue_head); + qdf_list_create(&ipa_ctx->pending_event, 1000); + qdf_mutex_create(&ipa_ctx->event_lock); + qdf_mutex_create(&ipa_ctx->ipa_lock); + + status = wlan_ipa_wdi_setup_rm(ipa_ctx); + if (status != QDF_STATUS_SUCCESS) + goto fail_setup_rm; + + for (i = 0; i < WLAN_IPA_MAX_SYSBAM_PIPE; i++) + qdf_mem_zero(&ipa_ctx->sys_pipe[i], + sizeof(struct wlan_ipa_sys_pipe)); + + if (wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + qdf_mem_zero(&ipa_ctx->stats, sizeof(ipa_ctx->stats)); + ipa_ctx->sap_num_connected_sta = 0; + ipa_ctx->ipa_tx_packets_diff = 0; + ipa_ctx->ipa_rx_packets_diff = 0; + ipa_ctx->ipa_p_tx_packets = 0; + ipa_ctx->ipa_p_rx_packets = 0; + ipa_ctx->resource_loading = false; + ipa_ctx->resource_unloading = false; + ipa_ctx->num_sap_connected = 0; + ipa_ctx->sta_connected = 0; + ipa_ctx->ipa_pipes_down = true; + qdf_atomic_set(&ipa_ctx->pipes_disabled, 1); + qdf_atomic_set(&ipa_ctx->autonomy_disabled, 1); + ipa_ctx->wdi_enabled = false; + + status = wlan_ipa_wdi_init(ipa_ctx); + if (status == QDF_STATUS_SUCCESS) { + /* Setup IPA system pipes */ + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) { + ret = wlan_ipa_setup_sys_pipe(ipa_ctx); + if (ret) + goto ipa_wdi_destroy; + + qdf_create_work(0, &ipa_ctx->mcc_work, + wlan_ipa_mcc_work_handler, + ipa_ctx); + } + } else if (status == QDF_STATUS_E_BUSY) { + ret = wlan_ipa_uc_send_wdi_control_msg(false); + if (ret) { + ipa_err("IPA WDI msg send failed: ret=%d", ret); + goto ipa_wdi_destroy; + } + } else { + ipa_err("IPA WDI init failed: ret=%d", status); + goto ipa_wdi_destroy; + } + } else { + ret = wlan_ipa_setup_sys_pipe(ipa_ctx); + if (ret) + goto ipa_wdi_destroy; + } + + qdf_event_create(&ipa_ctx->ipa_resource_comp); + + ipa_debug("exit: success"); + + return QDF_STATUS_SUCCESS; + +ipa_wdi_destroy: + wlan_ipa_wdi_destroy_rm(ipa_ctx); + +fail_setup_rm: + qdf_spinlock_destroy(&ipa_ctx->pm_lock); + qdf_spinlock_destroy(&ipa_ctx->q_lock); + qdf_spinlock_destroy(&ipa_ctx->enable_disable_lock); + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_context = &ipa_ctx->iface_context[i]; + qdf_spinlock_destroy(&iface_context->interface_lock); + } + qdf_mutex_destroy(&ipa_ctx->event_lock); + qdf_mutex_destroy(&ipa_ctx->ipa_lock); + qdf_list_destroy(&ipa_ctx->pending_event); + gp_ipa = NULL; + ipa_debug("exit: fail"); + + return QDF_STATUS_E_FAILURE; +} + +void wlan_ipa_flush(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_nbuf_t skb; + struct wlan_ipa_pm_tx_cb *pm_tx_cb; + + if (!wlan_ipa_is_enabled(ipa_ctx->config)) + return; + + qdf_cancel_work(&ipa_ctx->pm_work); + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + + while (((skb = qdf_nbuf_queue_remove(&ipa_ctx->pm_queue_head)) + != NULL)) { + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + pm_tx_cb = (struct wlan_ipa_pm_tx_cb *)skb->cb; + + if (pm_tx_cb->exception) { + dev_kfree_skb_any(skb); + } else { + if (pm_tx_cb->ipa_tx_desc) + ipa_free_skb(pm_tx_cb->ipa_tx_desc); + } + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + } + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); +} + +QDF_STATUS wlan_ipa_cleanup(struct wlan_ipa_priv *ipa_ctx) +{ + struct wlan_ipa_iface_context *iface_context; + int i; + + if (!wlan_ipa_uc_is_enabled(ipa_ctx->config)) + wlan_ipa_teardown_sys_pipe(ipa_ctx); + + /* Teardown IPA sys_pipe for MCC */ + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) { + wlan_ipa_teardown_sys_pipe(ipa_ctx); + if (ipa_ctx->uc_loaded) + qdf_cancel_work(&ipa_ctx->mcc_work); + } + + wlan_ipa_wdi_destroy_rm(ipa_ctx); + + wlan_ipa_flush(ipa_ctx); + + qdf_spinlock_destroy(&ipa_ctx->pm_lock); + qdf_spinlock_destroy(&ipa_ctx->q_lock); + qdf_spinlock_destroy(&ipa_ctx->enable_disable_lock); + + /* destroy the interface lock */ + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_context = &ipa_ctx->iface_context[i]; + qdf_spinlock_destroy(&iface_context->interface_lock); + } + + if (wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + wlan_ipa_wdi_cleanup(); + qdf_mutex_destroy(&ipa_ctx->event_lock); + qdf_mutex_destroy(&ipa_ctx->ipa_lock); + qdf_list_destroy(&ipa_ctx->pending_event); + + } + + gp_ipa = NULL; + + return QDF_STATUS_SUCCESS; +} + +struct wlan_ipa_iface_context +*wlan_ipa_get_iface(struct wlan_ipa_priv *ipa_ctx, uint8_t mode) +{ + struct wlan_ipa_iface_context *iface_ctx = NULL; + int i; + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + + if (iface_ctx->device_mode == mode) + return iface_ctx; + } + + return NULL; +} + +struct wlan_ipa_iface_context * +wlan_ipa_get_iface_by_mode_netdev(struct wlan_ipa_priv *ipa_ctx, + qdf_netdev_t ndev, uint8_t mode) +{ + struct wlan_ipa_iface_context *iface_ctx = NULL; + int i; + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + + if (iface_ctx->device_mode == mode && iface_ctx->dev == ndev) + return iface_ctx; + } + + return NULL; +} + +void wlan_ipa_set_mcc_mode(struct wlan_ipa_priv *ipa_ctx, bool mcc_mode) +{ + if (!wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) + return; + + if (ipa_ctx->mcc_mode == mcc_mode) + return; + + ipa_ctx->mcc_mode = mcc_mode; + qdf_sched_work(0, &ipa_ctx->mcc_work); +} + +/** + * wlan_ipa_uc_loaded_handler() - Process IPA uC loaded indication + * @ipa_ctx: ipa ipa local context + * + * Will handle IPA UC image loaded indication comes from IPA kernel + * + * Return: None + */ +static void wlan_ipa_uc_loaded_handler(struct wlan_ipa_priv *ipa_ctx) +{ + struct wlan_objmgr_pdev *pdev = ipa_ctx->pdev; + struct wlan_objmgr_psoc *psoc = wlan_pdev_get_psoc(pdev); + qdf_device_t qdf_dev = wlan_psoc_get_qdf_dev(psoc); + QDF_STATUS status; + + ipa_info("UC READY"); + + if (true == ipa_ctx->uc_loaded) { + ipa_info("UC already loaded"); + return; + } + ipa_ctx->uc_loaded = true; + + if (!qdf_dev) { + ipa_err("qdf_dev is null"); + return; + } + + if (wlan_ipa_uc_sta_is_enabled(ipa_ctx->config)) { + /* Setup IPA system pipes */ + status = wlan_ipa_setup_sys_pipe(ipa_ctx); + if (status) { + ipa_err("Fail to setup sys pipes (status=%d)", status); + return; + } + qdf_create_work(0, &ipa_ctx->mcc_work, + wlan_ipa_mcc_work_handler, ipa_ctx); + } + + /* Connect pipe */ + status = wlan_ipa_wdi_setup(ipa_ctx, qdf_dev); + if (status) { + ipa_err("Failure to setup IPA pipes (status=%d)", + status); + return; + } + + cdp_ipa_set_doorbell_paddr(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id); + wlan_ipa_init_metering(ipa_ctx); + + if (QDF_IS_STATUS_ERROR(wlan_ipa_init_perf_level(ipa_ctx))) + ipa_err("Failed to init perf level"); + + /* + * Enable IPA/FW PIPEs if + * 1. any clients connected to SAP or + * 2. STA connected to remote AP if STA only offload is enabled + */ + if (ipa_ctx->sap_num_connected_sta || + (wlan_ipa_is_sta_only_offload_enabled() && + ipa_ctx->sta_connected)) { + ipa_debug("Client already connected, enable IPA/FW PIPEs"); + wlan_ipa_uc_handle_first_con(ipa_ctx); + } +} + +/** + * wlan_ipa_uc_op_cb() - IPA uC operation callback + * @op_msg: operation message received from firmware + * @usr_ctxt: user context registered with TL (we register the IPA Global + * context) + * + * Return: None + */ +static void wlan_ipa_uc_op_cb(struct op_msg_type *op_msg, + struct wlan_ipa_priv *ipa_ctx) +{ + struct op_msg_type *msg = op_msg; + struct ipa_uc_fw_stats *uc_fw_stat; + + if (!op_msg) { + ipa_err("INVALID ARG"); + return; + } + + if (msg->op_code >= WLAN_IPA_UC_OPCODE_MAX) { + ipa_err("INVALID OPCODE %d", msg->op_code); + qdf_mem_free(op_msg); + return; + } + + ipa_debug("OPCODE=%d", msg->op_code); + + if ((msg->op_code == WLAN_IPA_UC_OPCODE_TX_RESUME) || + (msg->op_code == WLAN_IPA_UC_OPCODE_RX_RESUME)) { + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + ipa_ctx->activated_fw_pipe++; + if (wlan_ipa_is_fw_wdi_activated(ipa_ctx)) { + ipa_ctx->resource_loading = false; + qdf_event_set(&ipa_ctx->ipa_resource_comp); + if (ipa_ctx->wdi_enabled == false) { + ipa_ctx->wdi_enabled = true; + if (wlan_ipa_uc_send_wdi_control_msg(true) == 0) + wlan_ipa_send_mcc_scc_msg(ipa_ctx, + ipa_ctx->mcc_mode); + } + wlan_ipa_uc_proc_pending_event(ipa_ctx, true); + if (ipa_ctx->pending_cons_req) + wlan_ipa_wdi_rm_notify_completion( + QDF_IPA_RM_RESOURCE_GRANTED, + QDF_IPA_RM_RESOURCE_WLAN_CONS); + ipa_ctx->pending_cons_req = false; + } + qdf_mutex_release(&ipa_ctx->ipa_lock); + } else if ((msg->op_code == WLAN_IPA_UC_OPCODE_TX_SUSPEND) || + (msg->op_code == WLAN_IPA_UC_OPCODE_RX_SUSPEND)) { + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + + if (msg->op_code == WLAN_IPA_UC_OPCODE_RX_SUSPEND) { + wlan_ipa_uc_disable_pipes(ipa_ctx, true); + ipa_info("Disable FW TX PIPE"); + cdp_ipa_set_active(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id, + false, true); + } + + ipa_ctx->activated_fw_pipe--; + if (!ipa_ctx->activated_fw_pipe) { + /* + * Async return success from FW + * Disable/suspend all the PIPEs + */ + ipa_ctx->resource_unloading = false; + qdf_event_set(&ipa_ctx->ipa_resource_comp); + if (wlan_ipa_is_rm_enabled(ipa_ctx->config)) + wlan_ipa_wdi_rm_release_resource(ipa_ctx, + QDF_IPA_RM_RESOURCE_WLAN_PROD); + wlan_ipa_uc_proc_pending_event(ipa_ctx, false); + ipa_ctx->pending_cons_req = false; + } + qdf_mutex_release(&ipa_ctx->ipa_lock); + } else if ((msg->op_code == WLAN_IPA_UC_OPCODE_STATS) && + (ipa_ctx->stat_req_reason == WLAN_IPA_UC_STAT_REASON_DEBUG)) { + uc_fw_stat = (struct ipa_uc_fw_stats *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + /* WLAN FW WDI stats */ + wlan_ipa_print_fw_wdi_stats(ipa_ctx, uc_fw_stat); + } else if ((msg->op_code == WLAN_IPA_UC_OPCODE_STATS) && + (ipa_ctx->stat_req_reason == WLAN_IPA_UC_STAT_REASON_BW_CAL)) { + /* STATs from FW */ + uc_fw_stat = (struct ipa_uc_fw_stats *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + ipa_ctx->ipa_tx_packets_diff = BW_GET_DIFF( + uc_fw_stat->tx_pkts_completed, + ipa_ctx->ipa_p_tx_packets); + ipa_ctx->ipa_rx_packets_diff = BW_GET_DIFF( + (uc_fw_stat->rx_num_ind_drop_no_space + + uc_fw_stat->rx_num_ind_drop_no_buf + + uc_fw_stat->rx_num_pkts_indicated), + ipa_ctx->ipa_p_rx_packets); + + ipa_ctx->ipa_p_tx_packets = uc_fw_stat->tx_pkts_completed; + ipa_ctx->ipa_p_rx_packets = + (uc_fw_stat->rx_num_ind_drop_no_space + + uc_fw_stat->rx_num_ind_drop_no_buf + + uc_fw_stat->rx_num_pkts_indicated); + qdf_mutex_release(&ipa_ctx->ipa_lock); + } else if (msg->op_code == WLAN_IPA_UC_OPCODE_UC_READY) { + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + wlan_ipa_uc_loaded_handler(ipa_ctx); + qdf_mutex_release(&ipa_ctx->ipa_lock); + } else if (wlan_ipa_uc_op_metering(ipa_ctx, op_msg)) { + ipa_err("Invalid message: op_code=%d, reason=%d", + msg->op_code, ipa_ctx->stat_req_reason); + } + + qdf_mem_free(op_msg); +} + +/** + * __wlan_ipa_uc_fw_op_event_handler - IPA uC FW OPvent handler + * @data: uC OP work + * + * Return: None + */ +static void __wlan_ipa_uc_fw_op_event_handler(void *data) +{ + struct op_msg_type *msg; + struct uc_op_work_struct *uc_op_work = + (struct uc_op_work_struct *)data; + struct wlan_ipa_priv *ipa_ctx = gp_ipa; + + msg = uc_op_work->msg; + uc_op_work->msg = NULL; + ipa_debug("posted msg %d", msg->op_code); + + wlan_ipa_uc_op_cb(msg, ipa_ctx); +} + +/** + * wlan_ipa_uc_fw_op_event_handler - SSR wrapper for + * __wlan_ipa_uc_fw_op_event_handler + * @data: uC OP work + * + * Return: None + */ +static void wlan_ipa_uc_fw_op_event_handler(void *data) +{ + if (qdf_is_recovering()) { + ipa_err("in recovering"); + return; + } + + __wlan_ipa_uc_fw_op_event_handler(data); +} + +/** + * wlan_ipa_uc_op_event_handler() - IPA UC OP event handler + * @op_msg: operation message received from firmware + * @ipa_ctx: Global IPA context + * + * Return: None + */ +static void wlan_ipa_uc_op_event_handler(uint8_t *op_msg, void *ctx) +{ + struct wlan_ipa_priv *ipa_ctx = (struct wlan_ipa_priv *)ctx; + struct op_msg_type *msg; + struct uc_op_work_struct *uc_op_work; + + if (!ipa_ctx) + goto end; + + msg = (struct op_msg_type *)op_msg; + + if (msg->op_code >= WLAN_IPA_UC_OPCODE_MAX) { + ipa_err("Invalid OP Code (%d)", msg->op_code); + goto end; + } + + uc_op_work = &ipa_ctx->uc_op_work[msg->op_code]; + if (uc_op_work->msg) { + /* When the same uC OPCODE is already pended, just return */ + goto end; + } + + uc_op_work->msg = msg; + qdf_sched_work(0, &uc_op_work->work); + return; + +end: + qdf_mem_free(op_msg); +} + +QDF_STATUS wlan_ipa_uc_ol_init(struct wlan_ipa_priv *ipa_ctx, + qdf_device_t osdev) +{ + uint8_t i; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!wlan_ipa_uc_is_enabled(ipa_ctx->config)) + return QDF_STATUS_SUCCESS; + + ipa_debug("enter"); + + if (!osdev) { + ipa_err("osdev null"); + status = QDF_STATUS_E_FAILURE; + goto fail_return; + } + + for (i = 0; i < WLAN_IPA_MAX_SESSION; i++) { + ipa_ctx->vdev_to_iface[i] = WLAN_IPA_MAX_SESSION; + ipa_ctx->vdev_offload_enabled[i] = false; + } + + if (cdp_ipa_get_resource(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id)) { + ipa_err("IPA UC resource alloc fail"); + status = QDF_STATUS_E_FAILURE; + goto fail_return; + } + + if (true == ipa_ctx->uc_loaded) { + status = wlan_ipa_wdi_setup(ipa_ctx, osdev); + if (status) { + ipa_err("Failure to setup IPA pipes (status=%d)", + status); + status = QDF_STATUS_E_FAILURE; + goto fail_return; + } + + cdp_ipa_set_doorbell_paddr(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev_id); + wlan_ipa_init_metering(ipa_ctx); + + if (wlan_ipa_init_perf_level(ipa_ctx) != QDF_STATUS_SUCCESS) + ipa_err("Failed to init perf level"); + } + + cdp_ipa_register_op_cb(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id, + wlan_ipa_uc_op_event_handler, (void *)ipa_ctx); + + for (i = 0; i < WLAN_IPA_UC_OPCODE_MAX; i++) { + ipa_ctx->uc_op_work[i].osdev = osdev; + qdf_create_work(0, &ipa_ctx->uc_op_work[i].work, + wlan_ipa_uc_fw_op_event_handler, + &ipa_ctx->uc_op_work[i]); + ipa_ctx->uc_op_work[i].msg = NULL; + } + +fail_return: + ipa_debug("exit: status=%d", status); + return status; +} + +/** + * wlan_ipa_cleanup_pending_event() - Cleanup IPA pending event list + * @ipa_ctx: pointer to IPA IPA struct + * + * Return: none + */ +static void wlan_ipa_cleanup_pending_event(struct wlan_ipa_priv *ipa_ctx) +{ + struct wlan_ipa_uc_pending_event *pending_event = NULL; + + while (qdf_list_remove_front(&ipa_ctx->pending_event, + (qdf_list_node_t **)&pending_event) == QDF_STATUS_SUCCESS) + qdf_mem_free(pending_event); +} + +QDF_STATUS wlan_ipa_uc_ol_deinit(struct wlan_ipa_priv *ipa_ctx) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + int i; + + ipa_debug("enter"); + + if (!wlan_ipa_uc_is_enabled(ipa_ctx->config)) + return status; + + wlan_ipa_uc_disable_pipes(ipa_ctx, true); + + if (true == ipa_ctx->uc_loaded) { + status = cdp_ipa_cleanup(ipa_ctx->dp_soc, + ipa_ctx->dp_pdev_id, + ipa_ctx->tx_pipe_handle, + ipa_ctx->rx_pipe_handle); + if (status) + ipa_err("Failure to cleanup IPA pipes (status=%d)", + status); + } + + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + wlan_ipa_cleanup_pending_event(ipa_ctx); + qdf_mutex_release(&ipa_ctx->ipa_lock); + + for (i = 0; i < WLAN_IPA_UC_OPCODE_MAX; i++) { + qdf_cancel_work(&ipa_ctx->uc_op_work[i].work); + qdf_mem_free(ipa_ctx->uc_op_work[i].msg); + ipa_ctx->uc_op_work[i].msg = NULL; + } + + ipa_debug("exit: ret=%d", status); + return status; +} + +/** + * wlan_ipa_uc_send_evt() - send event to ipa + * @net_dev: Interface net device + * @type: event type + * @mac_addr: pointer to mac address + * + * Send event to IPA driver + * + * Return: QDF_STATUS + */ +static QDF_STATUS wlan_ipa_uc_send_evt(qdf_netdev_t net_dev, + qdf_ipa_wlan_event type, + uint8_t *mac_addr) +{ + struct wlan_ipa_priv *ipa_ctx = gp_ipa; + qdf_ipa_msg_meta_t meta; + qdf_ipa_wlan_msg_t *msg; + + QDF_IPA_MSG_META_MSG_LEN(&meta) = sizeof(qdf_ipa_wlan_msg_t); + msg = qdf_mem_malloc(QDF_IPA_MSG_META_MSG_LEN(&meta)); + if (!msg) { + ipa_err("msg allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + QDF_IPA_SET_META_MSG_TYPE(&meta, type); + qdf_str_lcopy(QDF_IPA_WLAN_MSG_NAME(msg), net_dev->name, + IPA_RESOURCE_NAME_MAX); + qdf_mem_copy(QDF_IPA_WLAN_MSG_MAC_ADDR(msg), mac_addr, QDF_NET_ETH_LEN); + + if (qdf_ipa_send_msg(&meta, msg, wlan_ipa_msg_free_fn)) { + ipa_err("%s: Evt: %d fail", + QDF_IPA_WLAN_MSG_NAME(msg), + QDF_IPA_MSG_META_MSG_TYPE(&meta)); + qdf_mem_free(msg); + + return QDF_STATUS_E_FAILURE; + } + + ipa_ctx->stats.num_send_msg++; + + return QDF_STATUS_SUCCESS; +} + +void wlan_ipa_uc_cleanup_sta(struct wlan_ipa_priv *ipa_ctx, + qdf_netdev_t net_dev) +{ + struct wlan_ipa_iface_context *iface_ctx; + int i; + + ipa_debug("enter"); + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + if (iface_ctx && iface_ctx->device_mode == QDF_STA_MODE && + iface_ctx->dev && iface_ctx->dev == net_dev) { + wlan_ipa_uc_send_evt(net_dev, QDF_IPA_STA_DISCONNECT, + net_dev->dev_addr); + wlan_ipa_cleanup_iface(iface_ctx); + } + } + + ipa_debug("exit"); +} + +QDF_STATUS wlan_ipa_uc_disconnect_ap(struct wlan_ipa_priv *ipa_ctx, + qdf_netdev_t net_dev) +{ + struct wlan_ipa_iface_context *iface_ctx; + QDF_STATUS status; + + ipa_debug("enter"); + + iface_ctx = wlan_ipa_get_iface(ipa_ctx, QDF_SAP_MODE); + if (iface_ctx) + status = wlan_ipa_uc_send_evt(net_dev, QDF_IPA_AP_DISCONNECT, + net_dev->dev_addr); + else + return QDF_STATUS_E_INVAL; + + ipa_debug("exit :%d", status); + + return status; +} + +void wlan_ipa_cleanup_dev_iface(struct wlan_ipa_priv *ipa_ctx, + qdf_netdev_t net_dev) +{ + struct wlan_ipa_iface_context *iface_ctx; + int i; + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_ctx = &ipa_ctx->iface_context[i]; + if (iface_ctx->dev == net_dev) { + wlan_ipa_cleanup_iface(iface_ctx); + break; + } + } +} + +void wlan_ipa_uc_ssr_cleanup(struct wlan_ipa_priv *ipa_ctx) +{ + struct wlan_ipa_iface_context *iface; + int i; + + ipa_info("enter"); + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface = &ipa_ctx->iface_context[i]; + if (iface->dev) { + if (iface->device_mode == QDF_SAP_MODE) + wlan_ipa_uc_send_evt(iface->dev, + QDF_IPA_AP_DISCONNECT, + iface->dev->dev_addr); + else if (iface->device_mode == QDF_STA_MODE) + wlan_ipa_uc_send_evt(iface->dev, + QDF_IPA_STA_DISCONNECT, + iface->dev->dev_addr); + wlan_ipa_cleanup_iface(iface); + } + } +} + +void wlan_ipa_fw_rejuvenate_send_msg(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_ipa_msg_meta_t meta; + qdf_ipa_wlan_msg_t *msg; + int ret; + + meta.msg_len = sizeof(*msg); + msg = qdf_mem_malloc(meta.msg_len); + if (!msg) { + ipa_debug("msg allocation failed"); + return; + } + + QDF_IPA_SET_META_MSG_TYPE(&meta, QDF_FWR_SSR_BEFORE_SHUTDOWN); + ipa_debug("ipa_send_msg(Evt:%d)", + meta.msg_type); + ret = qdf_ipa_send_msg(&meta, msg, wlan_ipa_msg_free_fn); + + if (ret) { + ipa_err("ipa_send_msg(Evt:%d)-fail=%d", + meta.msg_type, ret); + qdf_mem_free(msg); + } + ipa_ctx->stats.num_send_msg++; +} + +void wlan_ipa_flush_pending_vdev_events(struct wlan_ipa_priv *ipa_ctx, + uint8_t vdev_id) +{ + struct wlan_ipa_uc_pending_event *event; + struct wlan_ipa_uc_pending_event *next_event; + + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + + qdf_list_for_each_del(&ipa_ctx->pending_event, event, next_event, + node) { + if (event->session_id == vdev_id) { + qdf_list_remove_node(&ipa_ctx->pending_event, + &event->node); + qdf_mem_free(event); + } + } + + qdf_mutex_release(&ipa_ctx->ipa_lock); +} diff --git a/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_main.c b/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_main.c new file mode 100644 index 0000000000000000000000000000000000000000..5d33d4ec96e1b72b34770dc5a57bbf950caf0fc6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_main.c @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains ipa component main function definitions + */ + +#include "wlan_ipa_main.h" +#include "wlan_ipa_core.h" +#include "wlan_ipa_tgt_api.h" +#include "cfg_ucfg_api.h" + +static struct wlan_ipa_config *g_ipa_config; +static bool g_ipa_hw_support; + +bool ipa_check_hw_present(void) +{ + /* Check if ipa hw is enabled */ + if (qdf_ipa_uc_reg_rdyCB(NULL) != -EPERM) { + g_ipa_hw_support = true; + return true; + } else { + return false; + } +} + +QDF_STATUS ipa_config_mem_alloc(void) +{ + struct wlan_ipa_config *ipa_cfg; + + ipa_cfg = qdf_mem_malloc(sizeof(*ipa_cfg)); + if (!ipa_cfg) { + ipa_err("Failed to allocate memory for ipa config"); + return QDF_STATUS_E_NOMEM; + } + + g_ipa_config = ipa_cfg; + + return QDF_STATUS_SUCCESS; +} + +void ipa_config_mem_free(void) +{ + if (!g_ipa_config) { + ipa_err("IPA config already freed"); + return; + } + + qdf_mem_free(g_ipa_config); + g_ipa_config = NULL; +} + +bool ipa_is_hw_support(void) +{ + return g_ipa_hw_support; +} + +bool ipa_config_is_enabled(void) +{ + return g_ipa_config ? wlan_ipa_is_enabled(g_ipa_config) : 0; +} + +bool ipa_config_is_uc_enabled(void) +{ + return g_ipa_config ? wlan_ipa_uc_is_enabled(g_ipa_config) : 0; +} + +QDF_STATUS ipa_obj_setup(struct wlan_ipa_priv *ipa_ctx) +{ + return wlan_ipa_setup(ipa_ctx, g_ipa_config); +} + +QDF_STATUS ipa_obj_cleanup(struct wlan_ipa_priv *ipa_ctx) +{ + return wlan_ipa_cleanup(ipa_ctx); +} + +QDF_STATUS ipa_send_uc_offload_enable_disable(struct wlan_objmgr_pdev *pdev, + struct ipa_uc_offload_control_params *req) +{ + return tgt_ipa_uc_offload_enable_disable(pdev, req); +} + +void ipa_set_dp_handle(struct wlan_objmgr_psoc *psoc, void *dp_soc) +{ + struct wlan_objmgr_pdev *pdev; + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_IPA_ID); + + if (!pdev) { + ipa_err("Failed to get pdev handle"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + wlan_objmgr_pdev_release_ref(pdev, WLAN_IPA_ID); + return; + } + + ipa_obj->dp_soc = dp_soc; + wlan_objmgr_pdev_release_ref(pdev, WLAN_IPA_ID); +} + +void ipa_set_pdev_id(struct wlan_objmgr_psoc *psoc, uint8_t pdev_id) +{ + struct wlan_objmgr_pdev *pdev; + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_IPA_ID); + + if (!pdev) { + ipa_err("Failed to get pdev handle"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + wlan_objmgr_pdev_release_ref(pdev, WLAN_IPA_ID); + return; + } + + ipa_obj->dp_pdev_id = pdev_id; + wlan_objmgr_pdev_release_ref(pdev, WLAN_IPA_ID); +} + +QDF_STATUS ipa_rm_set_perf_level(struct wlan_objmgr_pdev *pdev, + uint64_t tx_packets, uint64_t rx_packets) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wlan_ipa_set_perf_level(ipa_obj, tx_packets, rx_packets); +} + +void ipa_uc_info(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_uc_info(ipa_obj); +} + +void ipa_uc_stat(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_uc_stat(ipa_obj); +} + +void ipa_uc_rt_debug_host_dump(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_uc_rt_debug_host_dump(ipa_obj); +} + +void ipa_dump_info(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_dump_info(ipa_obj); +} + +void ipa_uc_stat_request(struct wlan_objmgr_pdev *pdev, uint8_t reason) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_uc_stat_request(ipa_obj, reason); +} + +void ipa_uc_stat_query(struct wlan_objmgr_pdev *pdev, + uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_uc_stat_query(ipa_obj, ipa_tx_diff, ipa_rx_diff); +} + +void ipa_reg_sap_xmit_cb(struct wlan_objmgr_pdev *pdev, wlan_ipa_softap_xmit cb) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_reg_sap_xmit_cb(ipa_obj, cb); +} + +void ipa_reg_send_to_nw_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_send_to_nw cb) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_reg_send_to_nw_cb(ipa_obj, cb); +} + +#ifdef IPA_LAN_RX_NAPI_SUPPORT +void ipa_reg_rps_enable_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_rps_enable cb) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + if (!ipa_is_ready()) + return; + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_reg_rps_enable_cb(ipa_obj, cb); +} +#endif + +void ipa_set_mcc_mode(struct wlan_objmgr_pdev *pdev, bool mcc_mode) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_set_mcc_mode(ipa_obj, mcc_mode); +} + +void ipa_set_dfs_cac_tx(struct wlan_objmgr_pdev *pdev, bool tx_block) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_set_dfs_cac_tx(ipa_obj, tx_block); +} + +void ipa_set_ap_ibss_fwd(struct wlan_objmgr_pdev *pdev, bool intra_bss) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_set_ap_ibss_fwd(ipa_obj, intra_bss); +} + +void ipa_uc_force_pipe_shutdown(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!pdev) { + ipa_debug("objmgr pdev is null!"); + return; + } + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + wlan_ipa_uc_disable_pipes(ipa_obj, true); +} + +void ipa_flush(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_flush(ipa_obj); +} + +QDF_STATUS ipa_suspend(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wlan_ipa_suspend(ipa_obj); +} + +QDF_STATUS ipa_resume(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wlan_ipa_resume(ipa_obj); +} + +QDF_STATUS ipa_uc_ol_init(struct wlan_objmgr_pdev *pdev, + qdf_device_t osdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wlan_ipa_uc_ol_init(ipa_obj, osdev); +} + +bool ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + + return wlan_ipa_is_tx_pending(ipa_obj); +} + +QDF_STATUS ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wlan_ipa_uc_ol_deinit(ipa_obj); +} + +QDF_STATUS ipa_send_mcc_scc_msg(struct wlan_objmgr_pdev *pdev, + bool mcc_mode) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("ipa is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wlan_ipa_send_mcc_scc_msg(ipa_obj, mcc_mode); +} + +QDF_STATUS ipa_wlan_evt(struct wlan_objmgr_pdev *pdev, qdf_netdev_t net_dev, + uint8_t device_mode, uint8_t session_id, + enum wlan_ipa_wlan_event ipa_event_type, + uint8_t *mac_addr) +{ + struct wlan_ipa_priv *ipa_obj; + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wlan_ipa_wlan_evt(net_dev, device_mode, session_id, + ipa_event_type, mac_addr); +} + +int ipa_uc_smmu_map(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr) +{ + return wlan_ipa_uc_smmu_map(map, num_buf, buf_arr); +} + +bool ipa_is_fw_wdi_activated(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug_rl("ipa is disabled"); + return false; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err_rl("IPA object is NULL"); + return false; + } + + return wlan_ipa_is_fw_wdi_activated(ipa_obj); +} + +void ipa_uc_cleanup_sta(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev) +{ + struct wlan_ipa_priv *ipa_obj; + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_uc_cleanup_sta(ipa_obj, net_dev); +} + +QDF_STATUS ipa_uc_disconnect_ap(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev) +{ + struct wlan_ipa_priv *ipa_obj; + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wlan_ipa_uc_disconnect_ap(ipa_obj, net_dev); +} + +void ipa_cleanup_dev_iface(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev) +{ + struct wlan_ipa_priv *ipa_obj; + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_cleanup_dev_iface(ipa_obj, net_dev); +} + +void ipa_uc_ssr_cleanup(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_uc_ssr_cleanup(ipa_obj); +} + +void ipa_fw_rejuvenate_send_msg(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!pdev) { + ipa_debug("objmgr pdev is null!"); + return; + } + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + return wlan_ipa_fw_rejuvenate_send_msg(ipa_obj); +} + +void ipa_component_config_update(struct wlan_objmgr_psoc *psoc) +{ + if (!g_ipa_config) { + ipa_err("g_ipa_config is NULL"); + return; + } + + g_ipa_config->ipa_config = + cfg_get(psoc, CFG_DP_IPA_OFFLOAD_CONFIG); + g_ipa_config->desc_size = + cfg_get(psoc, CFG_DP_IPA_DESC_SIZE); + g_ipa_config->txbuf_count = + qdf_rounddown_pow_of_two(cfg_get(psoc, + CFG_DP_IPA_UC_TX_BUF_COUNT)); + g_ipa_config->ipa_bw_high = + cfg_get(psoc, CFG_DP_IPA_HIGH_BANDWIDTH_MBPS); + g_ipa_config->ipa_bw_medium = + cfg_get(psoc, CFG_DP_IPA_MEDIUM_BANDWIDTH_MBPS); + g_ipa_config->ipa_bw_low = + cfg_get(psoc, CFG_DP_IPA_LOW_BANDWIDTH_MBPS); + g_ipa_config->bus_bw_high = + cfg_get(psoc, CFG_DP_BUS_BANDWIDTH_HIGH_THRESHOLD); + g_ipa_config->bus_bw_medium = + cfg_get(psoc, CFG_DP_BUS_BANDWIDTH_MEDIUM_THRESHOLD); + g_ipa_config->bus_bw_low = + cfg_get(psoc, CFG_DP_BUS_BANDWIDTH_LOW_THRESHOLD); + g_ipa_config->ipa_force_voting = + cfg_get(psoc, CFG_DP_IPA_ENABLE_FORCE_VOTING); +} + +uint32_t ipa_get_tx_buf_count(void) +{ + return g_ipa_config ? g_ipa_config->txbuf_count : 0; +} + +void ipa_update_tx_stats(struct wlan_objmgr_pdev *pdev, uint64_t sta_tx, + uint64_t ap_tx) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) + return; + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + wlan_ipa_update_tx_stats(ipa_obj, sta_tx, ap_tx); +} + +void ipa_flush_pending_vdev_events(struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id) +{ + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) + return; + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + if (!ipa_obj) { + ipa_err("IPA object is NULL"); + return; + } + + wlan_ipa_flush_pending_vdev_events(ipa_obj, vdev_id); +} diff --git a/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_rm.c b/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_rm.c new file mode 100644 index 0000000000000000000000000000000000000000..da47ecc5dfe09951fc219059a9733868ae77dfb6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_rm.c @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* Include Files */ +#include "qdf_delayed_work.h" +#include "wlan_ipa_core.h" +#include "wlan_ipa_main.h" +#include "cdp_txrx_ipa.h" +#include "host_diag_core_event.h" + +QDF_STATUS wlan_ipa_set_perf_level(struct wlan_ipa_priv *ipa_ctx, + uint64_t tx_packets, + uint64_t rx_packets) +{ + int ret; + uint32_t next_bw; + uint64_t total_packets = tx_packets + rx_packets; + + if ((!wlan_ipa_is_enabled(ipa_ctx->config)) || + (!wlan_ipa_is_clk_scaling_enabled(ipa_ctx->config))) + return 0; + + if (total_packets > (ipa_ctx->config->bus_bw_high / 2)) + next_bw = ipa_ctx->config->ipa_bw_high; + else if (total_packets > (ipa_ctx->config->bus_bw_medium / 2)) + next_bw = ipa_ctx->config->ipa_bw_medium; + else + next_bw = ipa_ctx->config->ipa_bw_low; + + if (ipa_ctx->curr_cons_bw != next_bw) { + ipa_debug("Requesting IPA perf curr: %d, next: %d", + ipa_ctx->curr_cons_bw, next_bw); + ret = cdp_ipa_set_perf_level(ipa_ctx->dp_soc, + QDF_IPA_CLIENT_WLAN1_CONS, + next_bw); + if (ret) { + ipa_err("RM CONS set perf profile failed: %d", ret); + + return QDF_STATUS_E_FAILURE; + } + ret = cdp_ipa_set_perf_level(ipa_ctx->dp_soc, + QDF_IPA_CLIENT_WLAN1_PROD, + next_bw); + if (ret) { + ipa_err("RM PROD set perf profile failed: %d", ret); + return QDF_STATUS_E_FAILURE; + } + ipa_ctx->curr_cons_bw = next_bw; + ipa_ctx->stats.num_cons_perf_req++; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_ipa_init_perf_level(struct wlan_ipa_priv *ipa_ctx) +{ + int ret; + + /* Set lowest bandwidth to start with */ + if (wlan_ipa_is_clk_scaling_enabled(ipa_ctx->config)) + return wlan_ipa_set_perf_level(ipa_ctx, 0, 0); + + ipa_debug("IPA clk scaling disabled. Set perf level to maximum %d", + WLAN_IPA_MAX_BANDWIDTH); + + ret = cdp_ipa_set_perf_level(ipa_ctx->dp_soc, + QDF_IPA_CLIENT_WLAN1_CONS, + WLAN_IPA_MAX_BANDWIDTH); + if (ret) { + ipa_err("CONS set perf profile failed: %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = cdp_ipa_set_perf_level(ipa_ctx->dp_soc, + QDF_IPA_CLIENT_WLAN1_PROD, + WLAN_IPA_MAX_BANDWIDTH); + if (ret) { + ipa_err("PROD set perf profile failed: %d", ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_METERING +void wlan_ipa_init_metering(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_event_create(&ipa_ctx->ipa_uc_sharing_stats_comp); + qdf_event_create(&ipa_ctx->ipa_uc_set_quota_comp); +} +#endif + +#ifndef CONFIG_IPA_WDI_UNIFIED_API +/** + * wlan_ipa_rm_cons_release() - WLAN consumer resource release handler + * + * Callback function registered with IPA that is called when IPA wants + * to release the WLAN consumer resource + * + * Return: 0 if the request is granted, negative errno otherwise + */ +static int wlan_ipa_rm_cons_release(void) +{ + return 0; +} + +/** + * wlan_ipa_wdi_rm_request() - Request resource from IPA + * @ipa_ctx: IPA context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_ipa_wdi_rm_request(struct wlan_ipa_priv *ipa_ctx) +{ + int ret; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return QDF_STATUS_SUCCESS; + + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + + switch (ipa_ctx->rm_state) { + case WLAN_IPA_RM_GRANTED: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + return QDF_STATUS_SUCCESS; + case WLAN_IPA_RM_GRANT_PENDING: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + return QDF_STATUS_E_PENDING; + case WLAN_IPA_RM_RELEASED: + ipa_ctx->rm_state = WLAN_IPA_RM_GRANT_PENDING; + break; + } + + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + + ret = qdf_ipa_rm_inactivity_timer_request_resource( + QDF_IPA_RM_RESOURCE_WLAN_PROD); + + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + if (ret == 0) { + ipa_ctx->rm_state = WLAN_IPA_RM_GRANTED; + ipa_ctx->stats.num_rm_grant_imm++; + } + + if (ipa_ctx->wake_lock_released) { + qdf_wake_lock_acquire(&ipa_ctx->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_IPA); + ipa_ctx->wake_lock_released = false; + } + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + + qdf_delayed_work_stop_sync(&ipa_ctx->wake_lock_work); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_ipa_wdi_rm_try_release(struct wlan_ipa_priv *ipa_ctx) +{ + int ret; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return QDF_STATUS_SUCCESS; + + if (qdf_atomic_read(&ipa_ctx->tx_ref_cnt)) + return QDF_STATUS_E_AGAIN; + + qdf_spin_lock_bh(&ipa_ctx->pm_lock); + + if (!qdf_nbuf_is_queue_empty(&ipa_ctx->pm_queue_head)) { + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + return QDF_STATUS_E_AGAIN; + } + qdf_spin_unlock_bh(&ipa_ctx->pm_lock); + + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + switch (ipa_ctx->rm_state) { + case WLAN_IPA_RM_GRANTED: + break; + case WLAN_IPA_RM_GRANT_PENDING: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + return QDF_STATUS_E_PENDING; + case WLAN_IPA_RM_RELEASED: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + return QDF_STATUS_SUCCESS; + } + + /* IPA driver returns immediately so set the state here to avoid any + * race condition. + */ + ipa_ctx->rm_state = WLAN_IPA_RM_RELEASED; + ipa_ctx->stats.num_rm_release++; + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + + ret = qdf_ipa_rm_inactivity_timer_release_resource( + QDF_IPA_RM_RESOURCE_WLAN_PROD); + + if (qdf_unlikely(ret != 0)) { + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + ipa_ctx->rm_state = WLAN_IPA_RM_GRANTED; + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + QDF_ASSERT(0); + ipa_warn("rm_inactivity_timer_release_resource ret fail"); + } + + /* + * If wake_lock is released immediately, kernel would try to suspend + * immediately as well, Just avoid ping-pong between suspend-resume + * while there is healthy amount of data transfer going on by + * releasing the wake_lock after some delay. + */ + qdf_delayed_work_start(&ipa_ctx->wake_lock_work, + WLAN_IPA_RX_INACTIVITY_MSEC_DELAY); + + return QDF_STATUS_SUCCESS; +} + +/** + * wlan_ipa_uc_rm_notify_handler() - IPA uC resource notification handler + * @ipa_ctx: IPA context + * @event: IPA RM event + * + * Return: None + */ +static void +wlan_ipa_uc_rm_notify_handler(struct wlan_ipa_priv *ipa_ctx, + qdf_ipa_rm_event_t event) +{ + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return; + + ipa_debug("event code %d", event); + + switch (event) { + case QDF_IPA_RM_RESOURCE_GRANTED: + /* Differed RM Granted */ + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + if ((!ipa_ctx->resource_unloading) && + (!ipa_ctx->activated_fw_pipe)) { + wlan_ipa_uc_enable_pipes(ipa_ctx); + ipa_ctx->resource_loading = false; + } + qdf_mutex_release(&ipa_ctx->ipa_lock); + break; + + case QDF_IPA_RM_RESOURCE_RELEASED: + /* Differed RM Released */ + ipa_ctx->resource_unloading = false; + break; + + default: + ipa_err("invalid event code %d", event); + break; + } +} + +/** + * wlan_ipa_uc_rm_notify_defer() - Defer IPA uC notification + * * @data: IPA context + * + * This function is called when a resource manager event is received + * from firmware in interrupt context. This function will defer the + * handling to the OL RX thread + * + * Return: None + */ +static void wlan_ipa_uc_rm_notify_defer(void *data) +{ + struct wlan_ipa_priv *ipa_ctx = data; + qdf_ipa_rm_event_t event; + struct uc_rm_work_struct *uc_rm_work = &ipa_ctx->uc_rm_work; + + event = uc_rm_work->event; + + wlan_ipa_uc_rm_notify_handler(ipa_ctx, event); +} + +/** + * wlan_ipa_wake_lock_timer_func() - Wake lock work handler + * @data: IPA context + * + * When IPA resources are released in wlan_ipa_wdi_rm_try_release() we do + * not want to immediately release the wake lock since the system + * would then potentially try to suspend when there is a healthy data + * rate. Deferred work is scheduled and this function handles the + * work. When this function is called, if the IPA resource is still + * released then we release the wake lock. + * + * Return: None + */ +static void wlan_ipa_wake_lock_timer_func(void *data) +{ + struct wlan_ipa_priv *ipa_ctx = data; + + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + + if (ipa_ctx->rm_state != WLAN_IPA_RM_RELEASED) + goto end; + + ipa_ctx->wake_lock_released = true; + qdf_wake_lock_release(&ipa_ctx->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_IPA); + +end: + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); +} + +/** + * wlan_ipa_rm_cons_request() - WLAN consumer resource request handler + * + * Callback function registered with IPA that is called when IPA wants + * to access the WLAN consumer resource + * + * Return: 0 if the request is granted, negative errno otherwise + */ +static int wlan_ipa_rm_cons_request(void) +{ + struct wlan_ipa_priv *ipa_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + ipa_ctx = wlan_ipa_get_obj_context(); + + if (ipa_ctx->resource_loading) { + ipa_err("IPA resource loading in progress"); + ipa_ctx->pending_cons_req = true; + status = QDF_STATUS_E_PENDING; + } else if (ipa_ctx->resource_unloading) { + ipa_err("IPA resource unloading in progress"); + ipa_ctx->pending_cons_req = true; + status = QDF_STATUS_E_PERM; + } + + return qdf_status_to_os_return(status); +} + +/** + * wlan_ipa_rm_notify() - IPA resource manager notifier callback + * @user_data: user data registered with IPA + * @event: the IPA resource manager event that occurred + * @data: the data associated with the event + * + * Return: None + */ +static void wlan_ipa_rm_notify(void *user_data, qdf_ipa_rm_event_t event, + unsigned long data) +{ + struct wlan_ipa_priv *ipa_ctx = user_data; + + if (qdf_unlikely(!ipa_ctx)) + return; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return; + + ipa_debug("Evt: %d", event); + + switch (event) { + case QDF_IPA_RM_RESOURCE_GRANTED: + if (wlan_ipa_uc_is_enabled(ipa_ctx->config)) { + /* RM Notification comes with ISR context + * it should be serialized into work queue to avoid + * ISR sleep problem + */ + ipa_ctx->uc_rm_work.event = event; + qdf_sched_work(0, &ipa_ctx->uc_rm_work.work); + break; + } + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + ipa_ctx->rm_state = WLAN_IPA_RM_GRANTED; + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + ipa_ctx->stats.num_rm_grant++; + break; + + case QDF_IPA_RM_RESOURCE_RELEASED: + ipa_debug("RM Release"); + ipa_ctx->resource_unloading = false; + break; + + default: + ipa_err("Unknown RM Evt: %d", event); + break; + } +} + +QDF_STATUS wlan_ipa_wdi_setup_rm(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_ipa_rm_create_params_t create_params; + QDF_STATUS status; + int ret; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return 0; + + qdf_create_work(0, &ipa_ctx->uc_rm_work.work, + wlan_ipa_uc_rm_notify_defer, ipa_ctx); + qdf_mem_zero(&create_params, sizeof(create_params)); + create_params.name = QDF_IPA_RM_RESOURCE_WLAN_PROD; + create_params.reg_params.user_data = ipa_ctx; + create_params.reg_params.notify_cb = wlan_ipa_rm_notify; + create_params.floor_voltage = QDF_IPA_VOLTAGE_LEVEL; + + ret = qdf_ipa_rm_create_resource(&create_params); + if (ret) { + ipa_err("Create RM resource failed: %d", ret); + goto setup_rm_fail; + } + + qdf_mem_zero(&create_params, sizeof(create_params)); + create_params.name = QDF_IPA_RM_RESOURCE_WLAN_CONS; + create_params.request_resource = wlan_ipa_rm_cons_request; + create_params.release_resource = wlan_ipa_rm_cons_release; + create_params.floor_voltage = QDF_IPA_VOLTAGE_LEVEL; + + ret = qdf_ipa_rm_create_resource(&create_params); + if (ret) { + ipa_err("Create RM CONS resource failed: %d", ret); + goto delete_prod; + } + + qdf_ipa_rm_add_dependency(QDF_IPA_RM_RESOURCE_WLAN_PROD, + QDF_IPA_RM_RESOURCE_APPS_CONS); + + ret = qdf_ipa_rm_inactivity_timer_init(QDF_IPA_RM_RESOURCE_WLAN_PROD, + WLAN_IPA_RX_INACTIVITY_MSEC_DELAY); + if (ret) { + ipa_err("Timer init failed: %d", ret); + goto timer_init_failed; + } + + status = qdf_delayed_work_create(&ipa_ctx->wake_lock_work, + wlan_ipa_wake_lock_timer_func, + ipa_ctx); + if (QDF_IS_STATUS_ERROR(status)) + goto timer_destroy; + + qdf_wake_lock_create(&ipa_ctx->wake_lock, "wlan_ipa"); + qdf_spinlock_create(&ipa_ctx->rm_lock); + ipa_ctx->rm_state = WLAN_IPA_RM_RELEASED; + ipa_ctx->wake_lock_released = true; + qdf_atomic_set(&ipa_ctx->tx_ref_cnt, 0); + + return QDF_STATUS_SUCCESS; + +timer_destroy: + qdf_ipa_rm_inactivity_timer_destroy(QDF_IPA_RM_RESOURCE_WLAN_PROD); + +timer_init_failed: + qdf_ipa_rm_delete_resource(QDF_IPA_RM_RESOURCE_APPS_CONS); + +delete_prod: + qdf_ipa_rm_delete_resource(QDF_IPA_RM_RESOURCE_WLAN_PROD); + +setup_rm_fail: + return QDF_STATUS_E_FAILURE; +} + +void wlan_ipa_wdi_destroy_rm(struct wlan_ipa_priv *ipa_ctx) +{ + int ret; + + if (!wlan_ipa_is_rm_enabled(ipa_ctx->config)) + return; + + qdf_wake_lock_destroy(&ipa_ctx->wake_lock); + qdf_delayed_work_destroy(&ipa_ctx->wake_lock_work); + qdf_cancel_work(&ipa_ctx->uc_rm_work.work); + qdf_spinlock_destroy(&ipa_ctx->rm_lock); + + qdf_ipa_rm_inactivity_timer_destroy(QDF_IPA_RM_RESOURCE_WLAN_PROD); + + ret = qdf_ipa_rm_delete_resource(QDF_IPA_RM_RESOURCE_WLAN_CONS); + if (ret) + ipa_err("RM CONS resource delete failed %d", ret); + + ret = qdf_ipa_rm_delete_resource(QDF_IPA_RM_RESOURCE_WLAN_PROD); + if (ret) + ipa_err("RM PROD resource delete failed %d", ret); +} + +bool wlan_ipa_is_rm_released(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_spin_lock_bh(&ipa_ctx->rm_lock); + + if (ipa_ctx->rm_state != WLAN_IPA_RM_RELEASED) { + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + return false; + } + + qdf_spin_unlock_bh(&ipa_ctx->rm_lock); + + return true; +} +#endif /* CONFIG_IPA_WDI_UNIFIED_API */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_stats.c b/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_stats.c new file mode 100644 index 0000000000000000000000000000000000000000..ad125fdac321dc06fdc8514b19414f0f421143c6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/core/src/wlan_ipa_stats.c @@ -0,0 +1,1035 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* Include Files */ +#include "wlan_ipa_main.h" +#include "wlan_ipa_core.h" +#include "cdp_txrx_ipa.h" +#include "qdf_platform.h" + +/** + * wlan_ipa_uc_rt_debug_host_fill - fill rt debug buffer + * @ctext: pointer to ipa context. + * + * If rt debug enabled, periodically called, and fill debug buffer + * + * Return: none + */ +static void wlan_ipa_uc_rt_debug_host_fill(void *ctext) +{ + struct wlan_ipa_priv *ipa_ctx = ctext; + struct uc_rt_debug_info *dump_info = NULL; + + if (!ipa_ctx) + return; + + qdf_mutex_acquire(&ipa_ctx->rt_debug_lock); + dump_info = &ipa_ctx->rt_bug_buffer[ + ipa_ctx->rt_buf_fill_index % WLAN_IPA_UC_RT_DEBUG_BUF_COUNT]; + + dump_info->time = (uint64_t)qdf_mc_timer_get_system_time(); + dump_info->ipa_excep_count = ipa_ctx->stats.num_rx_excep; + dump_info->rx_drop_count = ipa_ctx->ipa_rx_internal_drop_count; + dump_info->net_sent_count = ipa_ctx->ipa_rx_net_send_count; + dump_info->tx_fwd_count = ipa_ctx->ipa_tx_forward; + dump_info->tx_fwd_ok_count = ipa_ctx->stats.num_tx_fwd_ok; + dump_info->rx_discard_count = ipa_ctx->ipa_rx_discard; + dump_info->rx_destructor_call = ipa_ctx->ipa_rx_destructor_count; + ipa_ctx->rt_buf_fill_index++; + qdf_mutex_release(&ipa_ctx->rt_debug_lock); + + qdf_mc_timer_start(&ipa_ctx->rt_debug_fill_timer, + WLAN_IPA_UC_RT_DEBUG_FILL_INTERVAL); +} + +void wlan_ipa_uc_rt_debug_host_dump(struct wlan_ipa_priv *ipa_ctx) +{ + unsigned int dump_count; + unsigned int dump_index; + struct uc_rt_debug_info *dump_info = NULL; + + if (!wlan_ipa_is_rt_debugging_enabled(ipa_ctx->config)) { + ipa_debug("IPA RT debug is not enabled"); + return; + } + + ipa_info("========= WLAN-IPA DEBUG BUF DUMP ==========\n"); + ipa_info(" TM : EXEP : DROP : NETS : FWOK" + ": TXFD : DSTR : DSCD\n"); + + qdf_mutex_acquire(&ipa_ctx->rt_debug_lock); + for (dump_count = 0; + dump_count < WLAN_IPA_UC_RT_DEBUG_BUF_COUNT; + dump_count++) { + dump_index = (ipa_ctx->rt_buf_fill_index + dump_count) % + WLAN_IPA_UC_RT_DEBUG_BUF_COUNT; + dump_info = &ipa_ctx->rt_bug_buffer[dump_index]; + ipa_info("%12llu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu\n", + dump_info->time, dump_info->ipa_excep_count, + dump_info->rx_drop_count, dump_info->net_sent_count, + dump_info->tx_fwd_ok_count, dump_info->tx_fwd_count, + dump_info->rx_destructor_call, + dump_info->rx_discard_count); + } + qdf_mutex_release(&ipa_ctx->rt_debug_lock); +} + +/** + * wlan_ipa_uc_rt_debug_handler - periodic memory health monitor handler + * @ctext: pointer to ipa context. + * + * periodically called by timer expire + * will try to alloc dummy memory and detect out of memory condition + * if out of memory detected, dump wlan-ipa stats + * + * Return: none + */ +static void wlan_ipa_uc_rt_debug_handler(void *ctext) +{ + struct wlan_ipa_priv *ipa_ctx = ctext; + void *dummy_ptr = NULL; + + if (!wlan_ipa_is_rt_debugging_enabled(ipa_ctx->config)) { + ipa_debug("IPA RT debug is not enabled"); + return; + } + + /* Allocate dummy buffer periodically and free immediately. this will + * proactively detect OOM and if allocation fails dump ipa stats + */ + dummy_ptr = qdf_mem_malloc(WLAN_IPA_UC_DEBUG_DUMMY_MEM_SIZE); + if (!dummy_ptr) { + wlan_ipa_uc_rt_debug_host_dump(ipa_ctx); + wlan_ipa_uc_stat_request(ipa_ctx, + WLAN_IPA_UC_STAT_REASON_DEBUG); + } else { + qdf_mem_free(dummy_ptr); + } + + qdf_mc_timer_start(&ipa_ctx->rt_debug_timer, + WLAN_IPA_UC_RT_DEBUG_PERIOD); +} + +void wlan_ipa_uc_rt_debug_destructor(qdf_nbuf_t nbuff) +{ + struct wlan_ipa_priv *ipa_ctx = wlan_ipa_get_obj_context(); + + if (!ipa_ctx) { + ipa_err("invalid ipa context"); + return; + } + + ipa_ctx->ipa_rx_destructor_count++; +} + +void wlan_ipa_uc_rt_debug_deinit(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_mutex_destroy(&ipa_ctx->rt_debug_lock); + + if (!wlan_ipa_is_rt_debugging_enabled(ipa_ctx->config)) { + ipa_debug("IPA RT debug is not enabled"); + return; + } + + if (QDF_TIMER_STATE_STOPPED != + qdf_mc_timer_get_current_state(&ipa_ctx->rt_debug_fill_timer)) { + qdf_mc_timer_stop(&ipa_ctx->rt_debug_fill_timer); + } + qdf_mc_timer_destroy(&ipa_ctx->rt_debug_fill_timer); + + if (QDF_TIMER_STATE_STOPPED != + qdf_mc_timer_get_current_state(&ipa_ctx->rt_debug_timer)) { + qdf_mc_timer_stop(&ipa_ctx->rt_debug_timer); + } + qdf_mc_timer_destroy(&ipa_ctx->rt_debug_timer); +} + +void wlan_ipa_uc_rt_debug_init(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_mutex_create(&ipa_ctx->rt_debug_lock); + ipa_ctx->rt_buf_fill_index = 0; + qdf_mem_zero(ipa_ctx->rt_bug_buffer, + sizeof(struct uc_rt_debug_info) * + WLAN_IPA_UC_RT_DEBUG_BUF_COUNT); + ipa_ctx->ipa_tx_forward = 0; + ipa_ctx->ipa_rx_discard = 0; + ipa_ctx->ipa_rx_net_send_count = 0; + ipa_ctx->ipa_rx_internal_drop_count = 0; + ipa_ctx->ipa_rx_destructor_count = 0; + + /* Reatime debug enable on feature enable */ + if (!wlan_ipa_is_rt_debugging_enabled(ipa_ctx->config)) { + ipa_debug("IPA RT debug is not enabled"); + return; + } + + qdf_mc_timer_init(&ipa_ctx->rt_debug_fill_timer, QDF_TIMER_TYPE_SW, + wlan_ipa_uc_rt_debug_host_fill, (void *)ipa_ctx); + qdf_mc_timer_start(&ipa_ctx->rt_debug_fill_timer, + WLAN_IPA_UC_RT_DEBUG_FILL_INTERVAL); + + qdf_mc_timer_init(&ipa_ctx->rt_debug_timer, QDF_TIMER_TYPE_SW, + wlan_ipa_uc_rt_debug_handler, (void *)ipa_ctx); + qdf_mc_timer_start(&ipa_ctx->rt_debug_timer, + WLAN_IPA_UC_RT_DEBUG_PERIOD); + +} + +/** + * wlan_ipa_dump_ipa_ctx() - dump entries in IPA IPA struct + * @ipa_ctx: IPA context + * + * Dump entries in struct ipa_ctx + * + * Return: none + */ +static void wlan_ipa_dump_ipa_ctx(struct wlan_ipa_priv *ipa_ctx) +{ + int i; + + /* IPA IPA */ + ipa_info("\n==== IPA IPA ====\n" + "num_iface: %d\n" + "rm_state: %d\n" + "rm_lock: %pK\n" + "uc_rm_work: %pK\n" + "uc_op_work: %pK\n" + "wake_lock: %pK\n" + "wake_lock_work: %pK\n" + "wake_lock_released: %d\n" + "tx_ref_cnt: %d\n" + "pm_queue_head----\n" + "\thead: %pK\n" + "\ttail: %pK\n" + "\tqlen: %d\n" + "pm_work: %pK\n" + "pm_lock: %pK\n" + "suspended: %d\n", + ipa_ctx->num_iface, + ipa_ctx->rm_state, + &ipa_ctx->rm_lock, + &ipa_ctx->uc_rm_work, + &ipa_ctx->uc_op_work, + &ipa_ctx->wake_lock, + &ipa_ctx->wake_lock_work, + ipa_ctx->wake_lock_released, + ipa_ctx->tx_ref_cnt.counter, + ipa_ctx->pm_queue_head.head, + ipa_ctx->pm_queue_head.tail, + ipa_ctx->pm_queue_head.qlen, + &ipa_ctx->pm_work, + &ipa_ctx->pm_lock, + ipa_ctx->suspended); + + ipa_info("\nq_lock: %pK\n" + "pend_desc_head----\n" + "\tnext: %pK\n" + "\tprev: %pK\n" + "stats: %pK\n" + "curr_prod_bw: %d\n" + "curr_cons_bw: %d\n" + "activated_fw_pipe: %d\n" + "sap_num_connected_sta: %d\n" + "sta_connected: %d\n", + &ipa_ctx->q_lock, + ipa_ctx->pend_desc_head.next, + ipa_ctx->pend_desc_head.prev, + &ipa_ctx->stats, + ipa_ctx->curr_prod_bw, + ipa_ctx->curr_cons_bw, + ipa_ctx->activated_fw_pipe, + ipa_ctx->sap_num_connected_sta, + (unsigned int)ipa_ctx->sta_connected); + + ipa_info("\ntx_pipe_handle: 0x%x\n" + "rx_pipe_handle: 0x%x\n" + "resource_loading: %d\n" + "resource_unloading: %d\n" + "pending_cons_req: %d\n" + "pending_event----\n" + "\tanchor.next: %pK\n" + "\tanchor.prev: %pK\n" + "\tcount: %d\n" + "\tmax_size: %d\n" + "event_lock: %pK\n" + "ipa_tx_packets_diff: %d\n" + "ipa_rx_packets_diff: %d\n" + "ipa_p_tx_packets: %d\n" + "ipa_p_rx_packets: %d\n" + "stat_req_reason: %d\n", + ipa_ctx->tx_pipe_handle, + ipa_ctx->rx_pipe_handle, + ipa_ctx->resource_loading, + ipa_ctx->resource_unloading, + ipa_ctx->pending_cons_req, + ipa_ctx->pending_event.anchor.next, + ipa_ctx->pending_event.anchor.prev, + ipa_ctx->pending_event.count, + ipa_ctx->pending_event.max_size, + &ipa_ctx->event_lock, + ipa_ctx->ipa_tx_packets_diff, + ipa_ctx->ipa_rx_packets_diff, + ipa_ctx->ipa_p_tx_packets, + ipa_ctx->ipa_p_rx_packets, + ipa_ctx->stat_req_reason); + + ipa_info("\ncons_pipe_in----\n" + "\tsys: %pK\n" + "\tdl.comp_ring_base_pa: 0x%x\n" + "\tdl.comp_ring_size: %d\n" + "\tdl.ce_ring_base_pa: 0x%x\n" + "\tdl.ce_door_bell_pa: 0x%x\n" + "\tdl.ce_ring_size: %d\n" + "\tdl.num_tx_buffers: %d\n" + "prod_pipe_in----\n" + "\tsys: %pK\n" + "\tul.rdy_ring_base_pa: 0x%x\n" + "\tul.rdy_ring_size: %d\n" + "\tul.rdy_ring_rp_pa: 0x%x\n" + "uc_loaded: %d\n" + "wdi_enabled: %d\n" + "rt_debug_fill_timer: %pK\n" + "rt_debug_lock: %pK\n" + "ipa_lock: %pK\n", + &ipa_ctx->cons_pipe_in.sys, + (unsigned int)ipa_ctx->cons_pipe_in.u.dl.comp_ring_base_pa, + ipa_ctx->cons_pipe_in.u.dl.comp_ring_size, + (unsigned int)ipa_ctx->cons_pipe_in.u.dl.ce_ring_base_pa, + (unsigned int)ipa_ctx->cons_pipe_in.u.dl.ce_door_bell_pa, + ipa_ctx->cons_pipe_in.u.dl.ce_ring_size, + ipa_ctx->cons_pipe_in.u.dl.num_tx_buffers, + &ipa_ctx->prod_pipe_in.sys, + (unsigned int)ipa_ctx->prod_pipe_in.u.ul.rdy_ring_base_pa, + ipa_ctx->prod_pipe_in.u.ul.rdy_ring_size, + (unsigned int)ipa_ctx->prod_pipe_in.u.ul.rdy_ring_rp_pa, + ipa_ctx->uc_loaded, + ipa_ctx->wdi_enabled, + &ipa_ctx->rt_debug_fill_timer, + &ipa_ctx->rt_debug_lock, + &ipa_ctx->ipa_lock); + + ipa_info("\nvdev_to_iface----"); + for (i = 0; i < WLAN_IPA_MAX_SESSION; i++) + ipa_info("\n\t[%d]=%d", i, ipa_ctx->vdev_to_iface[i]); + + QDF_TRACE(QDF_MODULE_ID_IPA, QDF_TRACE_LEVEL_INFO, + "\nvdev_offload_enabled----"); + for (i = 0; i < WLAN_IPA_MAX_SESSION; i++) + ipa_info("\n\t[%d]=%d", i, ipa_ctx->vdev_offload_enabled[i]); + + QDF_TRACE(QDF_MODULE_ID_IPA, QDF_TRACE_LEVEL_INFO, + "\nassoc_stas_map ----"); + for (i = 0; i < WLAN_IPA_MAX_STA_COUNT; i++) { + ipa_info("\n\t[%d]: is_reserved=%d mac: " QDF_MAC_ADDR_FMT, i, + ipa_ctx->assoc_stas_map[i].is_reserved, + QDF_MAC_ADDR_REF( + ipa_ctx->assoc_stas_map[i].mac_addr.bytes)); + } +} + +/** + * wlan_ipa_dump_sys_pipe() - dump IPA IPA SYS Pipe struct + * @ipa_ctx: IPA IPA struct + * + * Dump entire struct wlan_ipa_sys_pipe + * + * Return: none + */ +static void wlan_ipa_dump_sys_pipe(struct wlan_ipa_priv *ipa_ctx) +{ + int i; + + /* IPA SYS Pipes */ + ipa_info("==== IPA SYS Pipes ===="); + + for (i = 0; i < WLAN_IPA_MAX_SYSBAM_PIPE; i++) { + struct wlan_ipa_sys_pipe *sys_pipe; + qdf_ipa_sys_connect_params_t *ipa_sys_params; + + sys_pipe = &ipa_ctx->sys_pipe[i]; + ipa_sys_params = &sys_pipe->ipa_sys_params; + + ipa_info("\nsys_pipe[%d]----\n" + "\tconn_hdl: 0x%x\n" + "\tconn_hdl_valid: %d\n" + "\tnat_en: %d\n" + "\thdr_len %d\n" + "\thdr_additional_const_len: %d\n" + "\thdr_ofst_pkt_size_valid: %d\n" + "\thdr_ofst_pkt_size: %d\n" + "\thdr_little_endian: %d\n" + "\tmode: %d\n" + "\tclient: %d\n" + "\tdesc_fifo_sz: %d\n" + "\tpriv: %pK\n" + "\tnotify: %pK\n" + "\tskip_ep_cfg: %d\n" + "\tkeep_ipa_awake: %d\n", + i, + sys_pipe->conn_hdl, + sys_pipe->conn_hdl_valid, + QDF_IPA_SYS_PARAMS_NAT_EN(ipa_sys_params), + QDF_IPA_SYS_PARAMS_HDR_LEN(ipa_sys_params), + QDF_IPA_SYS_PARAMS_HDR_ADDITIONAL_CONST_LEN( + ipa_sys_params), + QDF_IPA_SYS_PARAMS_HDR_OFST_PKT_SIZE_VALID( + ipa_sys_params), + QDF_IPA_SYS_PARAMS_HDR_OFST_PKT_SIZE(ipa_sys_params), + QDF_IPA_SYS_PARAMS_HDR_LITTLE_ENDIAN(ipa_sys_params), + QDF_IPA_SYS_PARAMS_MODE(ipa_sys_params), + QDF_IPA_SYS_PARAMS_CLIENT(ipa_sys_params), + QDF_IPA_SYS_PARAMS_DESC_FIFO_SZ(ipa_sys_params), + QDF_IPA_SYS_PARAMS_PRIV(ipa_sys_params), + QDF_IPA_SYS_PARAMS_NOTIFY(ipa_sys_params), + QDF_IPA_SYS_PARAMS_SKIP_EP_CFG(ipa_sys_params), + QDF_IPA_SYS_PARAMS_KEEP_IPA_AWAKE(ipa_sys_params)); + } +} + +/** + * wlan_ipa_dump_iface_context() - dump IPA IPA Interface Context struct + * @ipa_ctx: IPA IPA struct + * + * Dump entire struct wlan_ipa_iface_context + * + * Return: none + */ +static void wlan_ipa_dump_iface_context(struct wlan_ipa_priv *ipa_ctx) +{ + int i; + + /* IPA Interface Contexts */ + ipa_info("\n==== IPA Interface Contexts ====\n"); + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + struct wlan_ipa_iface_context *iface_context; + + iface_context = &ipa_ctx->iface_context[i]; + + ipa_info("\niface_context[%d]----\n" + "\tipa_ctx: %pK\n" + "\tsession_id: %d\n" + "\tcons_client: %d\n" + "\tprod_client: %d\n" + "\tiface_id: %d\n" + "\tinterface_lock: %pK\n" + "\tifa_address: 0x%x\n", + i, + iface_context->ipa_ctx, + iface_context->session_id, + iface_context->cons_client, + iface_context->prod_client, + iface_context->iface_id, + &iface_context->interface_lock, + iface_context->ifa_address); + } +} + +void wlan_ipa_dump_info(struct wlan_ipa_priv *ipa_ctx) +{ + wlan_ipa_dump_ipa_ctx(ipa_ctx); + wlan_ipa_dump_sys_pipe(ipa_ctx); + wlan_ipa_dump_iface_context(ipa_ctx); +} + +void wlan_ipa_uc_stat_query(struct wlan_ipa_priv *ipa_ctx, + uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff) +{ + *ipa_tx_diff = 0; + *ipa_rx_diff = 0; + + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + if (wlan_ipa_is_fw_wdi_activated(ipa_ctx) && + (false == ipa_ctx->resource_loading)) { + *ipa_tx_diff = ipa_ctx->ipa_tx_packets_diff; + *ipa_rx_diff = ipa_ctx->ipa_rx_packets_diff; + } + qdf_mutex_release(&ipa_ctx->ipa_lock); +} + +void wlan_ipa_uc_stat_request(struct wlan_ipa_priv *ipa_ctx, uint8_t reason) +{ + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + if (wlan_ipa_is_fw_wdi_activated(ipa_ctx) && + (false == ipa_ctx->resource_loading)) { + ipa_ctx->stat_req_reason = reason; + cdp_ipa_get_stat(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id); + qdf_mutex_release(&ipa_ctx->ipa_lock); + } else { + qdf_mutex_release(&ipa_ctx->ipa_lock); + } +} + +/** + * wlan_ipa_print_session_info - Print IPA session info + * @ipa_ctx: IPA context + * + * Return: None + */ +static void wlan_ipa_print_session_info(struct wlan_ipa_priv *ipa_ctx) +{ + struct wlan_ipa_uc_pending_event *event = NULL, *next = NULL; + struct wlan_ipa_iface_context *iface_context = NULL; + int i; + + ipa_info("\n==== IPA SESSION INFO ====\n" + "NUM IFACE: %d\n" + "RM STATE: %d\n" + "ACTIVATED FW PIPE: %d\n" + "SAP NUM STAs: %d\n" + "STA CONNECTED: %d\n" + "CONCURRENT MODE: %s\n" + "RSC LOADING: %d\n" + "RSC UNLOADING: %d\n" + "PENDING CONS REQ: %d\n" + "IPA PIPES DOWN: %d\n" + "IPA UC LOADED: %d\n" + "IPA WDI ENABLED: %d\n" + "NUM SEND MSG: %d\n" + "NUM FREE MSG: %d\n", + ipa_ctx->num_iface, + ipa_ctx->rm_state, + ipa_ctx->activated_fw_pipe, + ipa_ctx->sap_num_connected_sta, + ipa_ctx->sta_connected, + (ipa_ctx->mcc_mode ? "MCC" : "SCC"), + ipa_ctx->resource_loading, + ipa_ctx->resource_unloading, + ipa_ctx->pending_cons_req, + ipa_ctx->ipa_pipes_down, + ipa_ctx->uc_loaded, + ipa_ctx->wdi_enabled, + (unsigned int)ipa_ctx->stats.num_send_msg, + (unsigned int)ipa_ctx->stats.num_free_msg); + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + iface_context = &ipa_ctx->iface_context[i]; + + if (iface_context->session_id == WLAN_IPA_MAX_SESSION) + continue; + + ipa_info("\nIFACE[%d]: mode:%d, offload:%d", + i, iface_context->device_mode, + ipa_ctx->vdev_offload_enabled[iface_context-> + session_id]); + } + + for (i = 0; i < QDF_IPA_WLAN_EVENT_MAX; i++) + ipa_info("\nEVENT[%d]=%d", + i, ipa_ctx->stats.event[i]); + + i = 0; + qdf_list_peek_front(&ipa_ctx->pending_event, + (qdf_list_node_t **)&event); + while (event) { + ipa_info("PENDING EVENT[%d]: EVT:%s, MAC:"QDF_MAC_ADDR_FMT, + i, wlan_ipa_wlan_event_to_str(event->type), + QDF_MAC_ADDR_REF(event->mac_addr)); + + qdf_list_peek_next(&ipa_ctx->pending_event, + (qdf_list_node_t *)event, + (qdf_list_node_t **)&next); + event = next; + next = NULL; + i++; + } +} + +/** + * wlan_ipa_print_txrx_stats - Print IPA IPA TX/RX stats + * @ipa_ctx: IPA context + * + * Return: None + */ +static void wlan_ipa_print_txrx_stats(struct wlan_ipa_priv *ipa_ctx) +{ + int i; + struct wlan_ipa_iface_context *iface_context = NULL; + + ipa_info("\n==== IPA IPA TX/RX STATS ====\n" + "NUM RM GRANT: %llu\n" + "NUM RM RELEASE: %llu\n" + "NUM RM GRANT IMM: %llu\n" + "NUM CONS PERF REQ: %llu\n" + "NUM PROD PERF REQ: %llu\n" + "NUM RX DROP: %llu\n" + "NUM EXCP PKT: %llu\n" + "NUM TX FWD OK: %llu\n" + "NUM TX FWD ERR: %llu\n" + "NUM TX DESC Q CNT: %llu\n" + "NUM TX DESC ERROR: %llu\n" + "NUM TX COMP CNT: %llu\n" + "NUM TX QUEUED: %llu\n" + "NUM TX DEQUEUED: %llu\n" + "NUM MAX PM QUEUE: %llu\n" + "TX REF CNT: %d\n" + "SUSPENDED: %d\n" + "PEND DESC HEAD: %pK\n" + "TX DESC LIST: %pK\n", + ipa_ctx->stats.num_rm_grant, + ipa_ctx->stats.num_rm_release, + ipa_ctx->stats.num_rm_grant_imm, + ipa_ctx->stats.num_cons_perf_req, + ipa_ctx->stats.num_prod_perf_req, + ipa_ctx->stats.num_rx_drop, + ipa_ctx->stats.num_rx_excep, + ipa_ctx->stats.num_tx_fwd_ok, + ipa_ctx->stats.num_tx_fwd_err, + ipa_ctx->stats.num_tx_desc_q_cnt, + ipa_ctx->stats.num_tx_desc_error, + ipa_ctx->stats.num_tx_comp_cnt, + ipa_ctx->stats.num_tx_queued, + ipa_ctx->stats.num_tx_dequeued, + ipa_ctx->stats.num_max_pm_queue, + ipa_ctx->tx_ref_cnt.counter, + ipa_ctx->suspended, + &ipa_ctx->pend_desc_head, + &ipa_ctx->tx_desc_free_list); + + for (i = 0; i < WLAN_IPA_MAX_IFACE; i++) { + + iface_context = &ipa_ctx->iface_context[i]; + if (iface_context->session_id == WLAN_IPA_MAX_SESSION) + continue; + + ipa_info("IFACE[%d]: TX:%llu, TX DROP:%llu, TX ERR:%llu," + " TX CAC DROP:%llu, RX IPA EXCEP:%llu", + i, iface_context->stats.num_tx, + iface_context->stats.num_tx_drop, + iface_context->stats.num_tx_err, + iface_context->stats.num_tx_cac_drop, + iface_context->stats.num_rx_ipa_excep); + } +} + +void wlan_ipa_print_fw_wdi_stats(struct wlan_ipa_priv *ipa_ctx, + struct ipa_uc_fw_stats *uc_fw_stat) +{ + ipa_info("\n==== WLAN FW WDI TX STATS ====\n" + "COMP RING SIZE: %d\n" + "COMP RING DBELL IND VAL : %d\n" + "COMP RING DBELL CACHED VAL : %d\n" + "PKTS ENQ : %d\n" + "PKTS COMP : %d\n" + "IS SUSPEND : %d\n", + uc_fw_stat->tx_comp_ring_size, + uc_fw_stat->tx_comp_ring_dbell_ind_val, + uc_fw_stat->tx_comp_ring_dbell_cached_val, + uc_fw_stat->tx_pkts_enqueued, + uc_fw_stat->tx_pkts_completed, + uc_fw_stat->tx_is_suspend); + + ipa_info("\n==== WLAN FW WDI RX STATS ====\n" + "IND RING SIZE: %d\n" + "IND RING DBELL IND VAL : %d\n" + "IND RING DBELL CACHED VAL : %d\n" + "RDY IND CACHE VAL : %d\n" + "RFIL IND : %d\n" + "NUM PKT INDICAT : %d\n" + "BUF REFIL : %d\n" + "NUM DROP NO SPC : %d\n" + "NUM DROP NO BUF : %d\n" + "IS SUSPND : %d\n", + uc_fw_stat->rx_ind_ring_size, + uc_fw_stat->rx_ind_ring_dbell_ind_val, + uc_fw_stat->rx_ind_ring_dbell_ind_cached_val, + uc_fw_stat->rx_ind_ring_rd_idx_cached_val, + uc_fw_stat->rx_refill_idx, + uc_fw_stat->rx_num_pkts_indicated, + uc_fw_stat->rx_buf_refilled, + uc_fw_stat->rx_num_ind_drop_no_space, + uc_fw_stat->rx_num_ind_drop_no_buf, + uc_fw_stat->rx_is_suspend); +} + +/** + * wlan_ipa_print_ipa_wdi_stats - Print IPA WDI stats + * @ipa_ctx: IPA context + * + * Return: None + */ +static void wlan_ipa_print_ipa_wdi_stats(struct wlan_ipa_priv *ipa_ctx) +{ + qdf_ipa_hw_stats_wdi_info_data_t ipa_stat; + + qdf_ipa_get_wdi_stats(&ipa_stat); + + ipa_info("\n==== IPA WDI TX STATS ====\n" + "NUM PROCD : %d\n" + "CE DBELL : 0x%x\n" + "NUM DBELL FIRED : %d\n" + "COMP RNG FULL : %d\n" + "COMP RNG EMPT : %d\n" + "COMP RNG USE HGH : %d\n" + "COMP RNG USE LOW : %d\n" + "BAM FIFO FULL : %d\n" + "BAM FIFO EMPT : %d\n" + "BAM FIFO USE HGH : %d\n" + "BAM FIFO USE LOW : %d\n" + "NUM DBELL : %d\n" + "NUM UNEXP DBELL : %d\n" + "NUM BAM INT HDL : 0x%x\n" + "NUM QMB INT HDL : 0x%x\n", + ipa_stat.tx_ch_stats.num_pkts_processed, + ipa_stat.tx_ch_stats.copy_engine_doorbell_value, + ipa_stat.tx_ch_stats.num_db_fired, + ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringFull, + ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringEmpty, + ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageHigh, + ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageLow, + ipa_stat.tx_ch_stats.bam_stats.bamFifoFull, + ipa_stat.tx_ch_stats.bam_stats.bamFifoEmpty, + ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageHigh, + ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageLow, + ipa_stat.tx_ch_stats.num_db, + ipa_stat.tx_ch_stats.num_unexpected_db, + ipa_stat.tx_ch_stats.num_bam_int_handled, + ipa_stat.tx_ch_stats.num_qmb_int_handled); + + ipa_info("\n==== IPA WDI RX STATS ====\n" + "MAX OST PKT : %d\n" + "NUM PKT PRCSD : %d\n" + "RNG RP : 0x%x\n" + "IND RNG FULL : %d\n" + "IND RNG EMPT : %d\n" + "IND RNG USE HGH : %d\n" + "IND RNG USE LOW : %d\n" + "BAM FIFO FULL : %d\n" + "BAM FIFO EMPT : %d\n" + "BAM FIFO USE HGH : %d\n" + "BAM FIFO USE LOW : %d\n" + "NUM DB : %d\n" + "NUM UNEXP DB : %d\n" + "NUM BAM INT HNDL : 0x%x\n", + ipa_stat.rx_ch_stats.max_outstanding_pkts, + ipa_stat.rx_ch_stats.num_pkts_processed, + ipa_stat.rx_ch_stats.rx_ring_rp_value, + ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringFull, + ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringEmpty, + ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageHigh, + ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageLow, + ipa_stat.rx_ch_stats.bam_stats.bamFifoFull, + ipa_stat.rx_ch_stats.bam_stats.bamFifoEmpty, + ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageHigh, + ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageLow, + ipa_stat.rx_ch_stats.num_db, + ipa_stat.rx_ch_stats.num_unexpected_db, + ipa_stat.rx_ch_stats.num_bam_int_handled); +} + +void wlan_ipa_uc_info(struct wlan_ipa_priv *ipa_ctx) +{ + wlan_ipa_print_session_info(ipa_ctx); +} + +void wlan_ipa_uc_stat(struct wlan_ipa_priv *ipa_ctx) +{ + /* IPA IPA TX/RX stats */ + wlan_ipa_print_txrx_stats(ipa_ctx); + /* IPA WDI stats */ + wlan_ipa_print_ipa_wdi_stats(ipa_ctx); + /* WLAN FW WDI stats */ + wlan_ipa_uc_stat_request(ipa_ctx, WLAN_IPA_UC_STAT_REASON_DEBUG); +} + +#ifdef FEATURE_METERING + +#ifdef WDI3_STATS_UPDATE +/** + * wlan_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. + * IPA calls to get WLAN stats or set quota limit. + * @priv: pointer to private data registered with IPA (we register a + * pointer to the IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void __wlan_ipa_wdi_meter_notifier_cb(qdf_ipa_wdi_meter_evt_type_t evt, + void *data) +{ + struct wlan_ipa_priv *ipa_ctx = wlan_ipa_get_obj_context(); + struct qdf_ipa_inform_wlan_bw *bw_info; + uint8_t bw_level_index; + uint64_t throughput; + + if (evt != IPA_INFORM_WLAN_BW) + return; + + bw_info = data; + bw_level_index = QDF_IPA_INFORM_WLAN_BW_INDEX(bw_info); + throughput = QDF_IPA_INFORM_WLAN_BW_THROUGHPUT(bw_info); + ipa_debug("bw_info idx:%d tp:%llu", bw_level_index, throughput); + + if (bw_level_index == ipa_ctx->curr_bw_level) + return; + + if (bw_level_index == WLAN_IPA_BW_LEVEL_LOW) { + cdp_ipa_set_perf_level(ipa_ctx->dp_soc, + QDF_IPA_CLIENT_WLAN2_CONS, + ipa_ctx->config->ipa_bw_low); + ipa_ctx->curr_bw_level = WLAN_IPA_BW_LEVEL_LOW; + } else if (bw_level_index == WLAN_IPA_BW_LEVEL_MEDIUM) { + cdp_ipa_set_perf_level(ipa_ctx->dp_soc, + QDF_IPA_CLIENT_WLAN2_CONS, + ipa_ctx->config->ipa_bw_medium); + ipa_ctx->curr_bw_level = WLAN_IPA_BW_LEVEL_MEDIUM; + } else if (bw_level_index == WLAN_IPA_BW_LEVEL_HIGH) { + cdp_ipa_set_perf_level(ipa_ctx->dp_soc, + QDF_IPA_CLIENT_WLAN2_CONS, + ipa_ctx->config->ipa_bw_high); + ipa_ctx->curr_bw_level = WLAN_IPA_BW_LEVEL_HIGH; + } + + ipa_debug("Requested BW level: %d", ipa_ctx->curr_bw_level); +} + +void wlan_ipa_update_tx_stats(struct wlan_ipa_priv *ipa_ctx, uint64_t sta_tx, + uint64_t ap_tx) +{ + qdf_ipa_wdi_tx_info_t tx_stats; + + if (!qdf_atomic_read(&ipa_ctx->stats_quota)) + return; + + QDF_IPA_WDI_TX_INFO_STA_TX_BYTES(&tx_stats) = sta_tx; + QDF_IPA_WDI_TX_INFO_SAP_TX_BYTES(&tx_stats) = ap_tx; + + qdf_ipa_wdi_wlan_stats(&tx_stats); +} + +#else + +/** + * wlan_ipa_uc_sharing_stats_request() - Get IPA stats from IPA. + * @ipa_ctx: IPA context + * @reset_stats: reset stat countis after response + * + * Return: None + */ +static void wlan_ipa_uc_sharing_stats_request(struct wlan_ipa_priv *ipa_ctx, + uint8_t reset_stats) +{ + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + if (false == ipa_ctx->resource_loading) { + qdf_mutex_release(&ipa_ctx->ipa_lock); + cdp_ipa_uc_get_share_stats(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id, + reset_stats); + } else { + qdf_mutex_release(&ipa_ctx->ipa_lock); + } +} + +/** + * wlan_ipa_uc_set_quota() - Set quota limit bytes from IPA. + * @ipa_ctx: IPA context + * @set_quota: when 1, FW starts quota monitoring + * @quota_bytes: quota limit in bytes + * + * Return: None + */ +static void wlan_ipa_uc_set_quota(struct wlan_ipa_priv *ipa_ctx, + uint8_t set_quota, + uint64_t quota_bytes) +{ + ipa_info("SET_QUOTA: set_quota=%d, quota_bytes=%llu", + set_quota, quota_bytes); + + qdf_mutex_acquire(&ipa_ctx->ipa_lock); + if (false == ipa_ctx->resource_loading) { + qdf_mutex_release(&ipa_ctx->ipa_lock); + cdp_ipa_uc_set_quota(ipa_ctx->dp_soc, ipa_ctx->dp_pdev_id, + quota_bytes); + } else { + qdf_mutex_release(&ipa_ctx->ipa_lock); + } +} + +/** + * __wlan_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. + * IPA calls to get WLAN stats or set quota limit. + * @priv: pointer to private data registered with IPA (we register a + * pointer to the IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void __wlan_ipa_wdi_meter_notifier_cb(qdf_ipa_wdi_meter_evt_type_t evt, + void *data) +{ + struct wlan_ipa_priv *ipa_ctx = wlan_ipa_get_obj_context(); + struct wlan_ipa_iface_context *iface_ctx; + qdf_ipa_get_wdi_sap_stats_t *wdi_sap_stats; + qdf_ipa_set_wifi_quota_t *ipa_set_quota; + QDF_STATUS status; + + ipa_debug("event=%d", evt); + + iface_ctx = wlan_ipa_get_iface(ipa_ctx, QDF_STA_MODE); + if (!iface_ctx) { + ipa_err_rl("IPA uC share stats failed - no iface"); + return; + } + + switch (evt) { + case IPA_GET_WDI_SAP_STATS: + /* fill-up ipa_get_wdi_sap_stats structure after getting + * ipa_uc_fw_stats from FW + */ + wdi_sap_stats = data; + + qdf_event_reset(&ipa_ctx->ipa_uc_sharing_stats_comp); + wlan_ipa_uc_sharing_stats_request(ipa_ctx, + QDF_IPA_GET_WDI_SAP_STATS_RESET_STATS(wdi_sap_stats)); + status = qdf_wait_for_event_completion( + &ipa_ctx->ipa_uc_sharing_stats_comp, + IPA_UC_SHARING_STATES_WAIT_TIME); + if (!QDF_IS_STATUS_SUCCESS(status)) { + ipa_err("IPA uC share stats request timed out"); + QDF_IPA_GET_WDI_SAP_STATS_STATS_VALID(wdi_sap_stats) + = 0; + } else { + QDF_IPA_GET_WDI_SAP_STATS_STATS_VALID(wdi_sap_stats) + = 1; + + QDF_IPA_GET_WDI_SAP_STATS_IPV4_RX_PACKETS(wdi_sap_stats) + = ipa_ctx->ipa_sharing_stats.ipv4_rx_packets; + QDF_IPA_GET_WDI_SAP_STATS_IPV4_RX_BYTES(wdi_sap_stats) + = ipa_ctx->ipa_sharing_stats.ipv4_rx_bytes; + QDF_IPA_GET_WDI_SAP_STATS_IPV6_RX_PACKETS(wdi_sap_stats) + = ipa_ctx->ipa_sharing_stats.ipv6_rx_packets; + QDF_IPA_GET_WDI_SAP_STATS_IPV6_RX_BYTES(wdi_sap_stats) + = ipa_ctx->ipa_sharing_stats.ipv6_rx_bytes; + QDF_IPA_GET_WDI_SAP_STATS_IPV4_TX_PACKETS(wdi_sap_stats) + = ipa_ctx->ipa_sharing_stats.ipv4_tx_packets; + QDF_IPA_GET_WDI_SAP_STATS_IPV4_TX_BYTES(wdi_sap_stats) + = ipa_ctx->ipa_sharing_stats.ipv4_tx_bytes; + QDF_IPA_GET_WDI_SAP_STATS_IPV6_TX_PACKETS(wdi_sap_stats) + = ipa_ctx->ipa_sharing_stats.ipv6_tx_packets; + QDF_IPA_GET_WDI_SAP_STATS_IPV6_TX_BYTES(wdi_sap_stats) + = ipa_ctx->ipa_sharing_stats.ipv6_tx_bytes; + } + break; + + case IPA_SET_WIFI_QUOTA: + /* get ipa_set_wifi_quota structure from IPA and pass to FW + * through quota_exceeded field in ipa_uc_fw_stats + */ + ipa_set_quota = data; + + qdf_event_reset(&ipa_ctx->ipa_uc_set_quota_comp); + wlan_ipa_uc_set_quota(ipa_ctx, ipa_set_quota->set_quota, + ipa_set_quota->quota_bytes); + + status = qdf_wait_for_event_completion( + &ipa_ctx->ipa_uc_set_quota_comp, + IPA_UC_SET_QUOTA_WAIT_TIME); + if (!QDF_IS_STATUS_SUCCESS(status)) { + ipa_err("IPA uC set quota request timed out"); + QDF_IPA_SET_WIFI_QUOTA_SET_VALID(ipa_set_quota) = 0; + } else { + QDF_IPA_SET_WIFI_QUOTA_BYTES(ipa_set_quota) = + ((uint64_t)(ipa_ctx->ipa_quota_rsp.quota_hi) + <<32)|ipa_ctx->ipa_quota_rsp.quota_lo; + QDF_IPA_SET_WIFI_QUOTA_SET_VALID(ipa_set_quota) = + ipa_ctx->ipa_quota_rsp.success; + } + break; + + default: + break; + } +} + +QDF_STATUS wlan_ipa_uc_op_metering(struct wlan_ipa_priv *ipa_ctx, + struct op_msg_type *op_msg) +{ + struct op_msg_type *msg = op_msg; + struct ipa_uc_sharing_stats *uc_sharing_stats; + struct ipa_uc_quota_rsp *uc_quota_rsp; + struct ipa_uc_quota_ind *uc_quota_ind; + struct wlan_ipa_iface_context *iface_ctx; + uint64_t quota_bytes; + + if (msg->op_code == WLAN_IPA_UC_OPCODE_SHARING_STATS) { + /* fill-up ipa_uc_sharing_stats structure from FW */ + uc_sharing_stats = (struct ipa_uc_sharing_stats *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + memcpy(&ipa_ctx->ipa_sharing_stats, uc_sharing_stats, + sizeof(struct ipa_uc_sharing_stats)); + + qdf_event_set(&ipa_ctx->ipa_uc_sharing_stats_comp); + } else if (msg->op_code == WLAN_IPA_UC_OPCODE_QUOTA_RSP) { + /* received set quota response */ + uc_quota_rsp = (struct ipa_uc_quota_rsp *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + memcpy(&ipa_ctx->ipa_quota_rsp, uc_quota_rsp, + sizeof(struct ipa_uc_quota_rsp)); + + qdf_event_set(&ipa_ctx->ipa_uc_set_quota_comp); + } else if (msg->op_code == WLAN_IPA_UC_OPCODE_QUOTA_IND) { + /* hit quota limit */ + uc_quota_ind = (struct ipa_uc_quota_ind *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + ipa_ctx->ipa_quota_ind.quota_bytes = + uc_quota_ind->quota_bytes; + + /* send quota exceeded indication to IPA */ + iface_ctx = wlan_ipa_get_iface(ipa_ctx, QDF_STA_MODE); + quota_bytes = uc_quota_ind->quota_bytes; + if (iface_ctx) + qdf_ipa_broadcast_wdi_quota_reach_ind( + iface_ctx->dev->ifindex, + quota_bytes); + else + ipa_err("Failed quota_reach_ind: NULL interface"); + } else { + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} +#endif /* WDI3_STATS_UPDATE */ + +/** + * wlan_ipa_wdi_meter_notifier_cb() - SSR wrapper for + * __wlan_ipa_wdi_meter_notifier_cb + * @priv: pointer to private data registered with IPA (we register a + * pointer to the IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +void wlan_ipa_wdi_meter_notifier_cb(qdf_ipa_wdi_meter_evt_type_t evt, + void *data) +{ + struct qdf_op_sync *op_sync; + + if (qdf_op_protect(&op_sync)) + return; + + __wlan_ipa_wdi_meter_notifier_cb(evt, data); + + qdf_op_unprotect(op_sync); +} +#endif /* FEATURE_METERING */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/cfg_ipa.h b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/cfg_ipa.h new file mode 100644 index 0000000000000000000000000000000000000000..4fc90e0a3f8120ab5a664e722678c4eab448e327 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/cfg_ipa.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains definitions of Data Path configuration. + */ + +#ifndef _CFG_IPA_H_ +#define _CFG_IPA_H_ + +#include "cfg_define.h" + +/* DP INI Declerations */ +/* + * IPA Offload configuration - Each bit enables a feature + * bit0 - IPA Enable + * bit1 - IPA Pre filter enable + * bit2 - IPv6 enable + * bit3 - IPA Resource Manager (RM) enable + * bit4 - IPA Clock scaling enable + */ + +/* + * + * gIPAConfig - IPA configuration + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 0 + * + * This ini specifies the IPA configuration + * + * Related: N/A + * + * Supported Feature: IPA + * + * Usage: Internal + * + * + */ +#define CFG_DP_IPA_OFFLOAD_CONFIG \ + CFG_INI_UINT("gIPAConfig", \ + 0, \ + 0xFFFFFFFF, \ + 0, \ + CFG_VALUE_OR_DEFAULT, "IPA offload configuration") + +/* + * + * gIPADescSize - IPA descriptor size + * @Min: 800 + * @Max: 8000 + * @Default: 800 + * + * This ini specifies the IPA descriptor size + * + * Related: N/A + * + * Supported Feature: IPA + * + * Usage: Internal + * + * + */ + #define CFG_DP_IPA_DESC_SIZE \ + CFG_INI_UINT("gIPADescSize", \ + 800, \ + 8000, \ + 800, \ + CFG_VALUE_OR_DEFAULT, "IPA DESC SIZE") + +/* + * + * gIPAHighBandwidthMbps - IPA high bw threshold + * @Min: 200 + * @Max: 1000 + * @Default: 400 + * + * This ini specifies the IPA high bw threshold + * + * Related: N/A + * + * Supported Feature: IPA + * + * Usage: Internal + * + * + */ + #define CFG_DP_IPA_HIGH_BANDWIDTH_MBPS \ + CFG_INI_UINT("gIPAHighBandwidthMbps", \ + 200, \ + 1000, \ + 400, \ + CFG_VALUE_OR_DEFAULT, "IPA high bw threshold") + +/* + * + * gIPAMediumBandwidthMbps - IPA medium bw threshold + * @Min: 100 + * @Max: 400 + * @Default: 200 + * + * This ini specifies the IPA medium bw threshold + * + * Related: N/A + * + * Supported Feature: IPA + * + * Usage: Internal + * + * + */ + #define CFG_DP_IPA_MEDIUM_BANDWIDTH_MBPS \ + CFG_INI_UINT("gIPAMediumBandwidthMbps", \ + 100, \ + 400, \ + 200, \ + CFG_VALUE_OR_DEFAULT, "IPA medium bw threshold") + +/* + * + * gIPALowBandwidthMbps - IPA low bw threshold + * @Min: 0 + * @Max: 100 + * @Default: 100 + * + * This ini specifies the IPA low bw threshold + * + * Related: N/A + * + * Supported Feature: IPA + * + * Usage: Internal + * + * + */ + #define CFG_DP_IPA_LOW_BANDWIDTH_MBPS \ + CFG_INI_UINT("gIPALowBandwidthMbps", \ + 0, \ + 100, \ + 100, \ + CFG_VALUE_OR_DEFAULT, "IPA low bw threshold") + +/* + * + * gIPAForceVotingEnable - IPA force voting enable + * @Default: false + * + * This ini specifies to enable IPA force voting + * + * Related: N/A + * + * Supported Feature: IPA + * + * Usage: Internal + * + * + */ +#define CFG_DP_IPA_ENABLE_FORCE_VOTING \ + CFG_INI_BOOL("gIPAForceVotingEnable", \ + false, "Ctrl to enable force voting") + +/* + * + * IpaUcTxBufCount - IPA tx buffer count + * @Min: 0 + * @Max: 2048 + * @Default: 512 + * + * This ini specifies the IPA tx buffer count + * + * Related: N/A + * + * Supported Feature: IPA + * + * Usage: Internal + * + * + */ + #define CFG_DP_IPA_UC_TX_BUF_COUNT \ + CFG_INI_UINT("IpaUcTxBufCount", \ + 0, \ + 2048, \ + 512, \ + CFG_VALUE_OR_DEFAULT, "IPA tx buffer count") + +#define CFG_IPA \ + CFG(CFG_DP_IPA_OFFLOAD_CONFIG) \ + CFG(CFG_DP_IPA_DESC_SIZE) \ + CFG(CFG_DP_IPA_HIGH_BANDWIDTH_MBPS) \ + CFG(CFG_DP_IPA_MEDIUM_BANDWIDTH_MBPS) \ + CFG(CFG_DP_IPA_LOW_BANDWIDTH_MBPS) \ + CFG(CFG_DP_IPA_ENABLE_FORCE_VOTING) \ + CFG(CFG_DP_IPA_UC_TX_BUF_COUNT) + +#endif /* _CFG_IPA_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_obj_mgmt_api.h b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_obj_mgmt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..b4e7e81b12ae122809f9bd45d66ac7ee8c5e9741 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_obj_mgmt_api.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare public API related to the wlan ipa called by north bound + */ + +#ifndef _WLAN_IPA_OBJ_MGMT_H_ +#define _WLAN_IPA_OBJ_MGMT_H_ + +#include "wlan_ipa_public_struct.h" +#include "wlan_objmgr_pdev_obj.h" + +#ifdef IPA_OFFLOAD + +/** + * ipa_init() - IPA module initialization + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ipa_init(void); + +/** + * ipa_deinit() - IPA module deinitialization + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ipa_deinit(void); +#else + +static inline QDF_STATUS ipa_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS ipa_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* IPA_OFFLOAD */ + +#endif /* _WLAN_IPA_OBJ_MGMT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_public_struct.h b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..c86ee4b36352782f685c39b91b2d748a171b779c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_public_struct.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: contains ipa public structure definitions + */ + +#ifndef _WLAN_IPA_PUBLIC_STRUCT_H_ +#define _WLAN_IPA_PUBLIC_STRUCT_H_ + +#include +#include +#include + +/** + * struct wlan_ipa_config + * @ipa_config: IPA config + * @desc_size: IPA descriptor size + * @txbuf_count: TX buffer count + * @bus_bw_high: Bus bandwidth high threshold + * @bus_bw_medium: Bus bandwidth medium threshold + * @bus_bw_low: Bus bandwidth low threshold + * @ipa_bw_high: IPA bandwidth high threshold + * @ipa_bw_medium: IPA bandwidth medium threshold + * @ipa_bw_low: IPA bandwidth low threshold + * @ipa_force_voting: support force bw voting + */ +struct wlan_ipa_config { + uint32_t ipa_config; + uint32_t desc_size; + uint32_t txbuf_count; + uint32_t bus_bw_high; + uint32_t bus_bw_medium; + uint32_t bus_bw_low; + uint32_t ipa_bw_high; + uint32_t ipa_bw_medium; + uint32_t ipa_bw_low; + bool ipa_force_voting; +}; + +/** + * enum wlan_ipa_wlan_event - WLAN IPA events + * @WLAN_IPA_CLIENT_CONNECT: Client Connects + * @WLAN_IPA_CLIENT_DISCONNECT: Client Disconnects + * @WLAN_IPA_AP_CONNECT: SoftAP is started + * @WLAN_IPA_AP_DISCONNECT: SoftAP is stopped + * @WLAN_IPA_STA_CONNECT: STA associates to AP + * @WLAN_IPA_STA_DISCONNECT: STA dissociates from AP + * @WLAN_IPA_CLIENT_CONNECT_EX: Peer associates/re-associates to softap + * @WLAN_IPA_WLAN_EVENT_MAX: Max value for the enum + */ +enum wlan_ipa_wlan_event { + WLAN_IPA_CLIENT_CONNECT, + WLAN_IPA_CLIENT_DISCONNECT, + WLAN_IPA_AP_CONNECT, + WLAN_IPA_AP_DISCONNECT, + WLAN_IPA_STA_CONNECT, + WLAN_IPA_STA_DISCONNECT, + WLAN_IPA_CLIENT_CONNECT_EX, + WLAN_IPA_WLAN_EVENT_MAX +}; + +/** + * struct ipa_uc_offload_control_params - ipa offload control params + * @offload_type: ipa offload type + * @vdev_id: vdev id + * @enable: ipa offload enable/disable + */ +struct ipa_uc_offload_control_params { + uint32_t offload_type; + uint32_t vdev_id; + uint32_t enable; +}; + +/* fp to send IPA UC offload cmd */ +typedef QDF_STATUS (*ipa_uc_offload_control_req)(struct wlan_objmgr_psoc *psoc, + struct ipa_uc_offload_control_params *req); +#endif /* end of _WLAN_IPA_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_tgt_api.h b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..417b387cad9e98d3c5796149b3d59187feba8b9d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_tgt_api.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare public API for wlan ipa to interact with target/WMI + */ + +#ifndef _WLAN_IPA_TGT_API_H_ +#define _WLAN_IPA_TGT_API_H_ + +#include "wlan_ipa_public_struct.h" + +/** + * tgt_ipa_uc_offload_enable_disable() - send ipa offload control to target if + * @pdev: objmgr pdev object + * @req: ipa offload control request + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_ipa_uc_offload_enable_disable(struct wlan_objmgr_pdev *pdev, + struct ipa_uc_offload_control_params *req); +#endif /* _WLAN_IPA_TGT_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_ucfg_api.h b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..57d41f5a2e7aedd3bbd6fe914fb95086e970de90 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/inc/wlan_ipa_ucfg_api.h @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare public API related to the wlan ipa called by north bound + */ + +#ifndef _WLAN_IPA_UCFG_API_H_ +#define _WLAN_IPA_UCFG_API_H_ + +#include "wlan_ipa_public_struct.h" +#include "wlan_ipa_obj_mgmt_api.h" +#include "wlan_objmgr_pdev_obj.h" +#include "qdf_types.h" +#include "wlan_ipa_main.h" + +#ifdef IPA_OFFLOAD + +/** + * ucfg_ipa_is_present() - get IPA hw status + * + * ipa_uc_reg_rdyCB is not directly designed to check + * ipa hw status. This is an undocumented function which + * has confirmed with IPA team. + * + * Return: true - ipa hw present + * false - ipa hw not present + */ +bool ucfg_ipa_is_present(void); + +/** + * ucfg_ipa_is_enabled() - get IPA enable status + * + * Return: true - ipa is enabled + * false - ipa is not enabled + */ +bool ucfg_ipa_is_enabled(void); + +/** + * ucfg_ipa_uc_is_enabled() - get IPA uC enable status + * + * Return: true - ipa uC is enabled + * false - ipa uC is not enabled + */ +bool ucfg_ipa_uc_is_enabled(void); + +/** + * ucfg_ipa_set_dp_handle() - register DP handle + * @psoc: psoc handle + * @dp_soc: data path soc handle + * + * Return: None + */ +void ucfg_ipa_set_dp_handle(struct wlan_objmgr_psoc *psoc, + void *dp_soc); + +/** + * ucfg_ipa_set_pdev_id() - register pdev id + * @psoc: psoc handle + * @pdev_id: data path txrx pdev id + * + * Return: None + */ +void ucfg_ipa_set_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t pdev_id); + +/** + * ucfg_ipa_set_perf_level() - Set IPA perf level + * @pdev: pdev obj + * @tx_packets: Number of packets transmitted in the last sample period + * @rx_packets: Number of packets received in the last sample period + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ipa_set_perf_level(struct wlan_objmgr_pdev *pdev, + uint64_t tx_packets, uint64_t rx_packets); + +/** + * ucfg_ipa_uc_info() - Print IPA uC resource and session information + * @pdev: pdev obj + * + * Return: None + */ +void ucfg_ipa_uc_info(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_uc_stat() - Print IPA uC stats + * @pdev: pdev obj + * + * Return: None + */ +void ucfg_ipa_uc_stat(struct wlan_objmgr_pdev *pdev); + + +/** + * ucfg_ipa_uc_rt_debug_host_dump() - IPA rt debug host dump + * @pdev: pdev obj + * + * Return: None + */ +void ucfg_ipa_uc_rt_debug_host_dump(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_dump_info() - Dump IPA context information + * @pdev: pdev obj + * + * Return: None + */ +void ucfg_ipa_dump_info(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_uc_stat_request() - Get IPA stats from IPA. + * @pdev: pdev obj + * @reason: STAT REQ Reason + * + * Return: None + */ +void ucfg_ipa_uc_stat_request(struct wlan_objmgr_pdev *pdev, + uint8_t reason); + +/** + * ucfg_ipa_uc_stat_query() - Query the IPA stats + * @pdev: pdev obj + * @ipa_tx_diff: tx packet count diff from previous tx packet count + * @ipa_rx_diff: rx packet count diff from previous rx packet count + * + * Return: None + */ +void ucfg_ipa_uc_stat_query(struct wlan_objmgr_pdev *pdev, + uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff); + +/** + * ucfg_ipa_reg_sap_xmit_cb() - Register upper layer SAP cb to transmit + * @pdev: pdev obj + * @cb: callback + * + * Return: None + */ +void ucfg_ipa_reg_sap_xmit_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_softap_xmit cb); + +/** + * ucfg_ipa_reg_send_to_nw_cb() - Register cb to send IPA Rx packet to network + * @pdev: pdev obj + * @cb: callback + * + * Return: None + */ +void ucfg_ipa_reg_send_to_nw_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_send_to_nw cb); + +/** + * ucfg_ipa_reg_rps_enable_cb() - Register cb to enable RPS + * @pdev: pdev obj + * @cb: callback + * + * Return: None + */ +#ifdef IPA_LAN_RX_NAPI_SUPPORT +void ucfg_ipa_reg_rps_enable_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_rps_enable cb); +#else +static inline +void ucfg_ipa_reg_rps_enable_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_rps_enable cb) +{ +} +#endif + +/** + * ucfg_ipa_set_mcc_mode() - Set MCC mode + * @pdev: pdev obj + * @mcc_mode: 0=MCC/1=SCC + * + * Return: void + */ +void ucfg_ipa_set_mcc_mode(struct wlan_objmgr_pdev *pdev, bool mcc_mode); + +/** + * ucfg_ipa_set_dfs_cac_tx() - Set DFS cac tx block + * @pdev: pdev obj + * @tx_block: dfs cac tx block + * + * Return: void + */ +void ucfg_ipa_set_dfs_cac_tx(struct wlan_objmgr_pdev *pdev, bool tx_block); + +/** + * ucfg_ipa_set_ap_ibss_fwd() - Set AP intra bss forward + * @pdev: pdev obj + * @intra_bss: enable or disable ap intra bss forward + * + * Return: void + */ +void ucfg_ipa_set_ap_ibss_fwd(struct wlan_objmgr_pdev *pdev, bool intra_bss); + +/** + * ucfg_ipa_uc_force_pipe_shutdown() - Force shutdown IPA pipe + * @pdev: pdev obj + * + * Return: void + */ +void ucfg_ipa_uc_force_pipe_shutdown(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_flush() - flush IPA exception path SKB's + * @pdev: pdev obj + * + * Return: None + */ +void ucfg_ipa_flush(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_suspend() - Suspend IPA + * @pdev: pdev obj + * + * Return: QDF STATUS + */ +QDF_STATUS ucfg_ipa_suspend(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_resume() - Resume IPA + * @pdev: pdev obj + * + * Return: QDF STATUS + */ +QDF_STATUS ucfg_ipa_resume(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_uc_ol_init() - Initialize IPA uC offload + * @pdev: pdev obj + * @osdev: OS dev + * + * Return: QDF STATUS + */ +QDF_STATUS ucfg_ipa_uc_ol_init(struct wlan_objmgr_pdev *pdev, + qdf_device_t osdev); + +/** + * ucfg_ipa_uc_ol_deinit() - Deinitialize IPA uC offload + * @pdev: pdev obj + * + * Return: QDF STATUS + */ +QDF_STATUS ucfg_ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_is_tx_pending() - Check if IPA WLAN TX completions are pending + * @pdev: pdev obj + * + * Return: bool if pending TX for IPA. + */ +bool ucfg_ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_send_mcc_scc_msg() - Send IPA WLAN_SWITCH_TO_MCC/SCC message + * @mcc_mode: 0=MCC/1=SCC + * + * Return: QDF STATUS + */ +QDF_STATUS ucfg_ipa_send_mcc_scc_msg(struct wlan_objmgr_pdev *pdev, + bool mcc_mode); + +/** + * ucfg_ipa_wlan_evt() - IPA event handler + * @pdev: pdev obj + * @net_dev: Interface net device + * @device_mode: Net interface device mode + * @session_id: session id for the event + * @type: event enum of type ipa_wlan_event + * @mac_address: MAC address associated with the event + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_ipa_wlan_evt(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev, uint8_t device_mode, + uint8_t session_id, + enum wlan_ipa_wlan_event ipa_event_type, + uint8_t *mac_addr); + +/** + * ucfg_ipa_uc_smmu_map() - Map / Unmap DMA buffer to IPA UC + * @map: Map / unmap operation + * @num_buf: Number of buffers in array + * @buf_arr: Buffer array of DMA mem mapping info + * + * Return: Status of map operation + */ +int ucfg_ipa_uc_smmu_map(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr); + +/** + * ucfg_ipa_is_fw_wdi_activated - Is FW WDI activated? + * @pdev: pdev obj + * + * Return: true if FW WDI activated, false otherwise + */ +bool ucfg_ipa_is_fw_wdi_activated(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_uc_cleanup_sta() - disconnect and cleanup sta iface + * @pdev: pdev obj + * @net_dev: Interface net device + * + * Send disconnect sta event to IPA driver and cleanup IPA iface, + * if not yet done + * + * Return: void + */ +void ucfg_ipa_uc_cleanup_sta(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev); + +/** + * ucfg_ipa_uc_disconnect_ap() - send ap disconnect event + * @pdev: pdev obj + * @net_dev: Interface net device + * + * Send disconnect ap event to IPA driver during SSR + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_ipa_uc_disconnect_ap(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev); + +/** + * ucfg_ipa_cleanup_dev_iface() - Clean up net dev IPA interface + * @pdev: pdev obj + * @net_dev: Interface net device + * + * + * Return: None + */ +void ucfg_ipa_cleanup_dev_iface(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev); + +/** + * ucfg_ipa_uc_ssr_cleanup() - Handle IPA cleanup for SSR + * @pdev: pdev obj + * + * From hostside do cleanup such as deregister IPA interafces + * and send disconnect events so that it will be sync after SSR + * + * Return: None + */ +void ucfg_ipa_uc_ssr_cleanup(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_fw_rejuvenate_send_msg() - Send msg to IPA driver in FW rejuvenate + * @pdev: pdev obj + * + * Return: None + */ +void ucfg_ipa_fw_rejuvenate_send_msg(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ipa_component_config_update() - update IPA component config + * @psoc: pointer to psoc object + * + * Return: None + */ +void ucfg_ipa_component_config_update(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_get_ipa_tx_buf_count() - get IPA tx buffer count + * + * Return: IPA tx buffer count + */ +uint32_t ucfg_ipa_get_tx_buf_count(void); + +/** + * ucfg_ipa_update_tx_stats() - send embedded tx traffic in bytes to IPA + * @pdev: pdev obj + * @sta_tx: tx in bytes on sta vdev + * @ap_tx: tx in bytes on sap vdev + * + * Return: void + */ +void ucfg_ipa_update_tx_stats(struct wlan_objmgr_pdev *pdev, uint64_t sta_tx, + uint64_t ap_tx); +/** + * ucfg_ipa_flush_pending_vdev_events() - flush pending vdev wlan ipa events + * @pdev: pdev obj + * @vdev_id: vdev id + * + * Return: None + */ +void ucfg_ipa_flush_pending_vdev_events(struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id); +#else + +static inline bool ucfg_ipa_is_present(void) +{ + return false; +} + +static inline void ucfg_ipa_update_config(struct wlan_ipa_config *config) +{ +} + +static inline bool ucfg_ipa_is_enabled(void) +{ + return false; +} + +static inline bool ucfg_ipa_uc_is_enabled(void) +{ + return false; +} + +static inline +QDF_STATUS ucfg_ipa_set_dp_handle(struct wlan_objmgr_psoc *psoc, + void *dp_soc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_ipa_set_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t pdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_ipa_set_perf_level(struct wlan_objmgr_pdev *pdev, + uint64_t tx_packets, uint64_t rx_packets) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void ucfg_ipa_uc_info(struct wlan_objmgr_pdev *pdev) +{ +} + +static inline +void ucfg_ipa_uc_stat(struct wlan_objmgr_pdev *pdev) +{ +} + +static inline +void ucfg_ipa_uc_rt_debug_host_dump(struct wlan_objmgr_pdev *pdev) +{ +} + +static inline +void ucfg_ipa_dump_info(struct wlan_objmgr_pdev *pdev) +{ +} + +static inline +void ucfg_ipa_uc_stat_request(struct wlan_objmgr_pdev *pdev, + uint8_t reason) +{ +} + +static inline +void ucfg_ipa_uc_stat_query(struct wlan_objmgr_pdev *pdev, + uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff) +{ +} + +static inline +void ucfg_ipa_reg_sap_xmit_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_softap_xmit cb) +{ +} + +static inline +void ucfg_ipa_reg_send_to_nw_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_send_to_nw cb) +{ +} + +static inline +void ucfg_ipa_reg_rps_enable_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_rps_enable cb) +{ +} + +static inline +void ucfg_ipa_set_mcc_mode(struct wlan_objmgr_pdev *pdev, bool mcc_mode) +{ +} + +static inline +void ucfg_ipa_set_dfs_cac_tx(struct wlan_objmgr_pdev *pdev, bool tx_block) +{ +} + +static inline +void ucfg_ipa_set_ap_ibss_fwd(struct wlan_objmgr_pdev *pdev, bool intra_bss) +{ +} + +static inline +void ucfg_ipa_uc_force_pipe_shutdown(struct wlan_objmgr_pdev *pdev) +{ +} + +static inline +void ucfg_ipa_flush(struct wlan_objmgr_pdev *pdev) +{ +} + +static inline +QDF_STATUS ucfg_ipa_suspend(struct wlan_objmgr_pdev *pdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_ipa_resume(struct wlan_objmgr_pdev *pdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_ipa_uc_ol_init(struct wlan_objmgr_pdev *pdev, + qdf_device_t osdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline bool ucfg_ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev) +{ + return false; +} + +static inline +QDF_STATUS ucfg_ipa_send_mcc_scc_msg(struct wlan_objmgr_pdev *pdev, + bool mcc_mode) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_ipa_wlan_evt(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev, uint8_t device_mode, + uint8_t session_id, + enum wlan_ipa_wlan_event ipa_event_type, + uint8_t *mac_addr) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +int ucfg_ipa_uc_smmu_map(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr) +{ + return 0; +} + +static inline +bool ucfg_ipa_is_fw_wdi_activated(struct wlan_objmgr_pdev *pdev) +{ + return false; +} + +static inline +void ucfg_ipa_uc_cleanup_sta(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev) +{ +} + +static inline +QDF_STATUS ucfg_ipa_uc_disconnect_ap(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void ucfg_ipa_cleanup_dev_iface(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev) +{ +} + +static inline +void ucfg_ipa_uc_ssr_cleanup(struct wlan_objmgr_pdev *pdev) +{ +} + +static inline +void ucfg_ipa_fw_rejuvenate_send_msg(struct wlan_objmgr_pdev *pdev) +{ +} + +static inline +void ucfg_ipa_component_config_update(struct wlan_objmgr_psoc *psoc) +{ +} + +static inline +uint32_t ucfg_ipa_get_tx_buf_count(void) +{ + return 0; +} + +static inline +void ucfg_ipa_update_tx_stats(struct wlan_objmgr_pdev *pdev, uint64_t sta_tx, + uint64_t ap_tx) +{ +} + +static inline +void ucfg_ipa_flush_pending_vdev_events(struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id) +{ +} +#endif /* IPA_OFFLOAD */ +#endif /* _WLAN_IPA_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/ipa/dispatcher/src/wlan_ipa_obj_mgmt_api.c b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/src/wlan_ipa_obj_mgmt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..8ca5779d33ffae6416050b31688482cc5fccd5df --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/src/wlan_ipa_obj_mgmt_api.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: public API related to the wlan ipa called by north bound HDD/OSIF + */ + +#include "wlan_ipa_obj_mgmt_api.h" +#include "wlan_ipa_main.h" +#include "wlan_objmgr_global_obj.h" +#include "target_if_ipa.h" + +/** + * ipa_pdev_obj_destroy_notification() - IPA pdev object destroy notification + * @pdev: pdev handle + * @arg_list: arguments list + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +ipa_pdev_obj_destroy_notification(struct wlan_objmgr_pdev *pdev, + void *arg_list) +{ + QDF_STATUS status; + struct wlan_ipa_priv *ipa_obj; + + if (!ipa_config_is_enabled()) { + ipa_debug("IPA is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = wlan_objmgr_pdev_get_comp_private_obj(pdev, + WLAN_UMAC_COMP_IPA); + if (!ipa_obj) { + ipa_err("Failed to get ipa pdev object"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_pdev_component_obj_detach(pdev, + WLAN_UMAC_COMP_IPA, + ipa_obj); + if (QDF_IS_STATUS_ERROR(status)) + ipa_err("Failed to detatch ipa pdev object"); + + ipa_obj_cleanup(ipa_obj); + qdf_mem_free(ipa_obj); + + return status; +} + +/** + * ipa_pdev_obj_create_notification() - IPA pdev object creation notification + * @pdev: pdev handle + * @arg_list: arguments list + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +ipa_pdev_obj_create_notification(struct wlan_objmgr_pdev *pdev, + void *arg_list) +{ + QDF_STATUS status; + struct wlan_ipa_priv *ipa_obj; + + ipa_debug("ipa pdev created"); + + if (!ipa_config_is_enabled()) { + ipa_info("IPA is disabled"); + return QDF_STATUS_SUCCESS; + } + + ipa_obj = qdf_mem_malloc(sizeof(*ipa_obj)); + if (!ipa_obj) { + ipa_err("Failed to allocate memory for ipa pdev object"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_pdev_component_obj_attach(pdev, + WLAN_UMAC_COMP_IPA, + (void *)ipa_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + ipa_err("Failed to attach pdev ipa component"); + qdf_mem_free(ipa_obj); + return status; + } + + ipa_obj->pdev = pdev; + + status = ipa_obj_setup(ipa_obj); + if (QDF_IS_STATUS_ERROR(status)) { + ipa_err("Failed to setup ipa component"); + wlan_objmgr_pdev_component_obj_detach(pdev, + WLAN_UMAC_COMP_IPA, + ipa_obj); + qdf_mem_free(ipa_obj); + return status; + } + + target_if_ipa_register_tx_ops(&ipa_obj->ipa_tx_op); + + ipa_debug("ipa pdev attached"); + + return status; +} + +QDF_STATUS ipa_init(void) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + ipa_info("ipa module dispatcher init"); + + if (!ipa_check_hw_present()) { + ipa_info("ipa hw not present"); + return status; + } + + status = ipa_config_mem_alloc(); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = wlan_objmgr_register_pdev_create_handler(WLAN_UMAC_COMP_IPA, + ipa_pdev_obj_create_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + ipa_err("Failed to register pdev create handler for ipa"); + + return status; + } + + status = wlan_objmgr_register_pdev_destroy_handler(WLAN_UMAC_COMP_IPA, + ipa_pdev_obj_destroy_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + ipa_err("Failed to register pdev destroy handler for ipa"); + goto fail_delete_pdev; + } + + return status; + +fail_delete_pdev: + wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_IPA, + ipa_pdev_obj_create_notification, NULL); + + return status; +} + +QDF_STATUS ipa_deinit(void) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + ipa_info("ipa module dispatcher deinit"); + + if (!ipa_is_hw_support()) { + ipa_info("ipa hw is not present"); + return status; + } + + if (!ipa_config_is_enabled()) { + ipa_info("ipa is disabled"); + ipa_config_mem_free(); + return status; + } + + status = wlan_objmgr_unregister_pdev_destroy_handler(WLAN_UMAC_COMP_IPA, + ipa_pdev_obj_destroy_notification, NULL); + if (QDF_IS_STATUS_ERROR(status)) + ipa_err("Failed to unregister pdev destroy handler"); + + status = wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_IPA, + ipa_pdev_obj_create_notification, NULL); + if (QDF_IS_STATUS_ERROR(status)) + ipa_err("Failed to unregister pdev create handler"); + + ipa_config_mem_free(); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/ipa/dispatcher/src/wlan_ipa_tgt_api.c b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/src/wlan_ipa_tgt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..c9c1e97bef995f4c3a68e35643bca9ad11ba5bd7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/src/wlan_ipa_tgt_api.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Implements public API for ipa to interact with target/WMI + */ + +#include "wlan_ipa_tgt_api.h" +#include "wlan_ipa_main.h" +#include "wlan_ipa_public_struct.h" +#include +#include + +QDF_STATUS tgt_ipa_uc_offload_enable_disable(struct wlan_objmgr_pdev *pdev, + struct ipa_uc_offload_control_params *req) +{ + struct wlan_ipa_priv *ipa_obj; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + IPA_ENTER(); + + ipa_obj = ipa_pdev_get_priv_obj(pdev); + psoc = wlan_pdev_get_psoc(pdev); + + if (ipa_obj->ipa_tx_op) + status = ipa_obj->ipa_tx_op(psoc, req); + + IPA_EXIT(); + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/ipa/dispatcher/src/wlan_ipa_ucfg_api.c b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/src/wlan_ipa_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..53ad2635086bba6d82a9178c2043fa71cfec0fb2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ipa/dispatcher/src/wlan_ipa_ucfg_api.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: public API related to the wlan ipa called by north bound HDD/OSIF + */ + +#include "wlan_ipa_ucfg_api.h" +#include "wlan_ipa_main.h" +#include "cfg_ucfg_api.h" + + +bool ucfg_ipa_is_present(void) +{ + return ipa_is_hw_support(); +} + +bool ucfg_ipa_is_enabled(void) +{ + return ipa_config_is_enabled(); +} + +bool ucfg_ipa_uc_is_enabled(void) +{ + return ipa_config_is_uc_enabled(); +} + +void ucfg_ipa_set_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t pdev_id) +{ + return ipa_set_pdev_id(psoc, pdev_id); +} + +void ucfg_ipa_set_dp_handle(struct wlan_objmgr_psoc *psoc, + void *dp_soc) +{ + return ipa_set_dp_handle(psoc, dp_soc); +} + +QDF_STATUS ucfg_ipa_set_perf_level(struct wlan_objmgr_pdev *pdev, + uint64_t tx_packets, uint64_t rx_packets) +{ + return ipa_rm_set_perf_level(pdev, tx_packets, rx_packets); +} + +void ucfg_ipa_uc_info(struct wlan_objmgr_pdev *pdev) +{ + return ipa_uc_info(pdev); +} + +void ucfg_ipa_uc_stat(struct wlan_objmgr_pdev *pdev) +{ + return ipa_uc_stat(pdev); +} + +void ucfg_ipa_uc_rt_debug_host_dump(struct wlan_objmgr_pdev *pdev) +{ + return ipa_uc_rt_debug_host_dump(pdev); +} + +void ucfg_ipa_dump_info(struct wlan_objmgr_pdev *pdev) +{ + return ipa_dump_info(pdev); +} + +void ucfg_ipa_uc_stat_request(struct wlan_objmgr_pdev *pdev, + uint8_t reason) +{ + return ipa_uc_stat_request(pdev, reason); +} + +void ucfg_ipa_uc_stat_query(struct wlan_objmgr_pdev *pdev, + uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff) +{ + return ipa_uc_stat_query(pdev, ipa_tx_diff, ipa_rx_diff); +} + +void ucfg_ipa_reg_sap_xmit_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_softap_xmit cb) +{ + return ipa_reg_sap_xmit_cb(pdev, cb); +} + +void ucfg_ipa_reg_send_to_nw_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_send_to_nw cb) +{ + return ipa_reg_send_to_nw_cb(pdev, cb); +} + +#ifdef IPA_LAN_RX_NAPI_SUPPORT +void ucfg_ipa_reg_rps_enable_cb(struct wlan_objmgr_pdev *pdev, + wlan_ipa_rps_enable cb) +{ + return ipa_reg_rps_enable_cb(pdev, cb); +} +#endif + +void ucfg_ipa_set_mcc_mode(struct wlan_objmgr_pdev *pdev, bool mcc_mode) +{ + return ipa_set_mcc_mode(pdev, mcc_mode); +} + +void ucfg_ipa_set_dfs_cac_tx(struct wlan_objmgr_pdev *pdev, bool tx_block) +{ + return ipa_set_dfs_cac_tx(pdev, tx_block); +} + +void ucfg_ipa_set_ap_ibss_fwd(struct wlan_objmgr_pdev *pdev, bool intra_bss) +{ + return ipa_set_ap_ibss_fwd(pdev, intra_bss); +} + +void ucfg_ipa_uc_force_pipe_shutdown(struct wlan_objmgr_pdev *pdev) +{ + return ipa_uc_force_pipe_shutdown(pdev); +} + +void ucfg_ipa_flush(struct wlan_objmgr_pdev *pdev) +{ + return ipa_flush(pdev); +} + +QDF_STATUS ucfg_ipa_suspend(struct wlan_objmgr_pdev *pdev) +{ + return ipa_suspend(pdev); +} + +QDF_STATUS ucfg_ipa_resume(struct wlan_objmgr_pdev *pdev) +{ + return ipa_resume(pdev); +} + +QDF_STATUS ucfg_ipa_uc_ol_init(struct wlan_objmgr_pdev *pdev, + qdf_device_t osdev) +{ + return ipa_uc_ol_init(pdev, osdev); +} + +QDF_STATUS ucfg_ipa_uc_ol_deinit(struct wlan_objmgr_pdev *pdev) +{ + return ipa_uc_ol_deinit(pdev); +} + +bool ucfg_ipa_is_tx_pending(struct wlan_objmgr_pdev *pdev) +{ + return ipa_is_tx_pending(pdev); +} + +QDF_STATUS ucfg_ipa_send_mcc_scc_msg(struct wlan_objmgr_pdev *pdev, + bool mcc_mode) +{ + return ipa_send_mcc_scc_msg(pdev, mcc_mode); +} + +QDF_STATUS ucfg_ipa_wlan_evt(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev, uint8_t device_mode, + uint8_t session_id, + enum wlan_ipa_wlan_event ipa_event_type, + uint8_t *mac_addr) +{ + return ipa_wlan_evt(pdev, net_dev, device_mode, session_id, + ipa_event_type, mac_addr); +} + +int ucfg_ipa_uc_smmu_map(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr) +{ + return ipa_uc_smmu_map(map, num_buf, buf_arr); +} + +bool ucfg_ipa_is_fw_wdi_activated(struct wlan_objmgr_pdev *pdev) +{ + return ipa_is_fw_wdi_activated(pdev); +} + +void ucfg_ipa_uc_cleanup_sta(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev) +{ + return ipa_uc_cleanup_sta(pdev, net_dev); +} + +QDF_STATUS ucfg_ipa_uc_disconnect_ap(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev) +{ + return ipa_uc_disconnect_ap(pdev, net_dev); +} + +void ucfg_ipa_cleanup_dev_iface(struct wlan_objmgr_pdev *pdev, + qdf_netdev_t net_dev) +{ + return ipa_cleanup_dev_iface(pdev, net_dev); +} + +void ucfg_ipa_uc_ssr_cleanup(struct wlan_objmgr_pdev *pdev) +{ + return ipa_uc_ssr_cleanup(pdev); +} + +void ucfg_ipa_fw_rejuvenate_send_msg(struct wlan_objmgr_pdev *pdev) +{ + return ipa_fw_rejuvenate_send_msg(pdev); +} + +void ucfg_ipa_component_config_update(struct wlan_objmgr_psoc *psoc) +{ + ipa_component_config_update(psoc); +} + +uint32_t ucfg_ipa_get_tx_buf_count(void) +{ + return ipa_get_tx_buf_count(); +} + +void ucfg_ipa_update_tx_stats(struct wlan_objmgr_pdev *pdev, uint64_t sta_tx, + uint64_t ap_tx) +{ + ipa_update_tx_stats(pdev, sta_tx, ap_tx); +} + +void ucfg_ipa_flush_pending_vdev_events(struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id) +{ + ipa_flush_pending_vdev_events(pdev, vdev_id); +} diff --git a/drivers/staging/qcacld-3.0/components/mlme/core/inc/wlan_mlme_main.h b/drivers/staging/qcacld-3.0/components/mlme/core/inc/wlan_mlme_main.h new file mode 100644 index 0000000000000000000000000000000000000000..e98e7a9223f00cc9ec52cde02325f1151afa80dd --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/core/inc/wlan_mlme_main.h @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare internal API related to the mlme component + */ + +#ifndef _WLAN_MLME_MAIN_H_ +#define _WLAN_MLME_MAIN_H_ + +#include +#include +#include +#include +#include +#include + +#define mlme_legacy_fatal(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_MLME, params) +#define mlme_legacy_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_MLME, params) +#define mlme_legacy_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_MLME, params) +#define mlme_legacy_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_MLME, params) +#define mlme_legacy_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_MLME, params) + +/** + * struct wlan_mlme_psoc_ext_obj -MLME ext psoc priv object + * @cfg: cfg items + */ +struct wlan_mlme_psoc_ext_obj { + struct wlan_mlme_cfg cfg; +}; + +/** + * struct wlan_disconnect_info - WLAN Disconnection Information + * @self_discon_ies: Disconnect IEs to be sent in deauth/disassoc frames + * originated from driver + * @peer_discon_ies: Disconnect IEs received in deauth/disassoc frames + * from peer + * @discon_reason: Disconnect reason as per enum eSirMacReasonCodes + * @from_ap: True if the disconnection is initiated from AP + */ +struct wlan_disconnect_info { + struct wlan_ies self_discon_ies; + struct wlan_ies peer_discon_ies; + uint32_t discon_reason; + bool from_ap; +}; + +/** + * struct sae_auth_retry - SAE auth retry Information + * @sae_auth_max_retry: Max number of sae auth retries + * @sae_auth: SAE auth frame information + */ +struct sae_auth_retry { + uint8_t sae_auth_max_retry; + struct wlan_ies sae_auth; +}; + +/** + * struct peer_mlme_priv_obj - peer MLME component object + * @last_pn_valid if last PN is valid + * @last_pn: last pn received + * @rmf_pn_replays: rmf pn replay count + * @is_pmf_enabled: True if PMF is enabled + * @last_assoc_received_time: last assoc received time + * @last_disassoc_deauth_received_time: last disassoc/deauth received time + */ +struct peer_mlme_priv_obj { + uint8_t last_pn_valid; + uint64_t last_pn; + uint32_t rmf_pn_replays; + bool is_pmf_enabled; + qdf_time_t last_assoc_received_time; + qdf_time_t last_disassoc_deauth_received_time; +}; + +/** + * enum vdev_assoc_type - VDEV associate/reassociate type + * @VDEV_ASSOC: associate + * @VDEV_REASSOC: reassociate + * @VDEV_FT_REASSOC: fast reassociate + */ +enum vdev_assoc_type { + VDEV_ASSOC, + VDEV_REASSOC, + VDEV_FT_REASSOC +}; + +/** + * wlan_mlme_roam_state_info - Structure containing roaming + * state related details + * @state: Roaming module state. + * @mlme_operations_bitmap: Bitmap containing what mlme operations are in + * progress where roaming should not be allowed. + */ +struct wlan_mlme_roam_state_info { + enum roam_offload_state state; + uint8_t mlme_operations_bitmap; +}; + +/** + * struct wlan_mlme_roaming_config - Roaming configurations structure + * @roam_trigger_bitmap: Master bitmap of roaming triggers. If the bitmap is + * zero, roaming module will be deinitialized at firmware for this vdev. + * @supplicant_disabled_roaming: Enable/disable roam scan in firmware; will be + * used by supplicant to do roam invoke after disabling roam scan in firmware + */ +struct wlan_mlme_roaming_config { + uint32_t roam_trigger_bitmap; + bool supplicant_disabled_roaming; +}; + +/** + * struct wlan_mlme_roam - Roam structure containing roam state and + * roam config info + * @roam_sm: Structure containing roaming state related details + * @roam_config: Roaming configurations structure + * @sae_single_pmk: Details for sae roaming using single pmk + */ +struct wlan_mlme_roam { + struct wlan_mlme_roam_state_info roam_sm; + struct wlan_mlme_roaming_config roam_cfg; +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) + struct wlan_mlme_sae_single_pmk sae_single_pmk; +#endif +}; + +/** + * struct mlme_legacy_priv - VDEV MLME legacy priv object + * @chan_switch_in_progress: flag to indicate that channel switch is in progress + * @hidden_ssid_restart_in_progress: flag to indicate hidden ssid restart is + * in progress + * @vdev_start_failed: flag to indicate that vdev start failed. + * @connection_fail: flag to indicate connection failed + * @cac_required_for_new_channel: if CAC is required for new channel + * @follow_ap_edca: if true, it is forced to follow the AP's edca. + * @reconn_after_assoc_timeout: reconnect to the same AP if association timeout + * @assoc_type: vdev associate/reassociate type + * @dynamic_cfg: current configuration of nss, chains for vdev. + * @ini_cfg: Max configuration of nss, chains supported for vdev. + * @sta_dynamic_oce_value: Dyanmic oce flags value for sta + * @roam_invoke_params: Roam invoke params + * @disconnect_info: Disconnection information + * @vdev_stop_type: vdev stop type request + * @roam_off_state: Roam offload state + * @sae_auth_retry: SAE auth retry information + */ +struct mlme_legacy_priv { + bool chan_switch_in_progress; + bool hidden_ssid_restart_in_progress; + bool vdev_start_failed; + bool connection_fail; + bool cac_required_for_new_channel; + bool follow_ap_edca; + bool reconn_after_assoc_timeout; + enum vdev_assoc_type assoc_type; + struct wlan_mlme_nss_chains dynamic_cfg; + struct wlan_mlme_nss_chains ini_cfg; + uint8_t sta_dynamic_oce_value; + struct mlme_roam_after_data_stall roam_invoke_params; + struct wlan_disconnect_info disconnect_info; + uint32_t vdev_stop_type; + struct wlan_mlme_roam mlme_roam; + struct sae_auth_retry sae_retry; +}; + +/** + * wma_get_peer_mic_len() - get mic hdr len and mic length for peer + * @psoc: psoc + * @pdev_id: pdev id for the peer + * @peer_mac: peer mac + * @mic_len: mic length for peer + * @mic_hdr_len: mic header length for peer + * + * Return: Success or Failure status + */ +QDF_STATUS mlme_get_peer_mic_len(struct wlan_objmgr_psoc *psoc, uint8_t pdev_id, + uint8_t *peer_mac, uint8_t *mic_len, + uint8_t *mic_hdr_len); + +/** + * mlme_peer_object_created_notification(): mlme peer create handler + * @peer: peer which is going to created by objmgr + * @arg: argument for vdev create handler + * + * Register this api with objmgr to detect peer is created + * + * Return: QDF_STATUS status in case of success else return error + */ + +QDF_STATUS +mlme_peer_object_created_notification(struct wlan_objmgr_peer *peer, + void *arg); + +/** + * mlme_peer_object_destroyed_notification(): mlme peer delete handler + * @peer: peer which is going to delete by objmgr + * @arg: argument for vdev delete handler + * + * Register this api with objmgr to detect peer is deleted + * + * Return: QDF_STATUS status in case of success else return error + */ +QDF_STATUS +mlme_peer_object_destroyed_notification(struct wlan_objmgr_peer *peer, + void *arg); + +/** + * mlme_get_dynamic_oce_flags(): mlme get dynamic oce flags + * @vdev: pointer to vdev object + * + * This api is used to get the dynamic oce flags pointer + * + * Return: QDF_STATUS status in case of success else return error + */ +uint8_t *mlme_get_dynamic_oce_flags(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_get_dynamic_vdev_config() - get the vdev dynamic config params + * @vdev: vdev pointer + * + * Return: pointer to the dynamic vdev config structure + */ +struct wlan_mlme_nss_chains *mlme_get_dynamic_vdev_config( + struct wlan_objmgr_vdev *vdev); + +/** + * mlme_get_ini_vdev_config() - get the vdev ini config params + * @vdev: vdev pointer + * + * Return: pointer to the ini vdev config structure + */ +struct wlan_mlme_nss_chains *mlme_get_ini_vdev_config( + struct wlan_objmgr_vdev *vdev); + +/** + * mlme_get_roam_invoke_params() - get the roam invoke params + * @vdev: vdev pointer + * + * Return: pointer to the vdev roam invoke config structure + */ +struct mlme_roam_after_data_stall * +mlme_get_roam_invoke_params(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_cfg_on_psoc_enable() - Populate MLME structure from CFG and INI + * @psoc: pointer to the psoc object + * + * Populate the MLME CFG structure from CFG and INI values using CFG APIs + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_cfg_on_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * mlme_get_psoc_ext_obj() - Get MLME object from psoc + * @psoc: pointer to the psoc object + * + * Get the MLME object pointer from the psoc + * + * Return: pointer to MLME object + */ +#define mlme_get_psoc_ext_obj(psoc) \ + mlme_get_psoc_ext_obj_fl(psoc, __func__, __LINE__) +struct wlan_mlme_psoc_ext_obj *mlme_get_psoc_ext_obj_fl(struct wlan_objmgr_psoc + *psoc, + const char *func, + uint32_t line); + +/** + * mlme_init_ibss_cfg() - Init IBSS config data structure with default CFG value + * @psoc: pointer to the psoc object + * @ibss_cfg: Pointer to IBSS cfg data structure to return values + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_init_ibss_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_ibss_cfg *ibss_cfg); + +/** + * mlme_get_sae_auth_retry() - Get sae_auth_retry pointer + * @vdev: vdev pointer + * + * Return: Pointer to struct sae_auth_retry or NULL + */ +struct sae_auth_retry *mlme_get_sae_auth_retry(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_free_sae_auth_retry() - Free the SAE auth info + * @vdev: vdev pointer + * + * Return: None + */ +void mlme_free_sae_auth_retry(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_set_self_disconnect_ies() - Set diconnect IEs configured from userspace + * @vdev: vdev pointer + * @ie: pointer for disconnect IEs + * + * Return: None + */ +void mlme_set_self_disconnect_ies(struct wlan_objmgr_vdev *vdev, + struct wlan_ies *ie); + +/** + * mlme_free_self_disconnect_ies() - Free the self diconnect IEs + * @vdev: vdev pointer + * + * Return: None + */ +void mlme_free_self_disconnect_ies(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_get_self_disconnect_ies() - Get diconnect IEs from vdev object + * @vdev: vdev pointer + * + * Return: Returns a pointer to the self disconnect IEs present in vdev object + */ +struct wlan_ies *mlme_get_self_disconnect_ies(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_set_peer_disconnect_ies() - Cache disconnect IEs received from peer + * @vdev: vdev pointer + * @ie: pointer for disconnect IEs + * + * Return: None + */ +void mlme_set_peer_disconnect_ies(struct wlan_objmgr_vdev *vdev, + struct wlan_ies *ie); + +/** + * mlme_free_peer_disconnect_ies() - Free the peer diconnect IEs + * @vdev: vdev pointer + * + * Return: None + */ +void mlme_free_peer_disconnect_ies(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_set_follow_ap_edca_flag() - Set follow ap's edca flag + * @vdev: vdev pointer + * @flag: carries if following ap's edca is true or not. + * + * Return: None + */ +void mlme_set_follow_ap_edca_flag(struct wlan_objmgr_vdev *vdev, bool flag); + +/** + * mlme_get_follow_ap_edca_flag() - Get follow ap's edca flag + * @vdev: vdev pointer + * + * Return: value of follow_ap_edca + */ +bool mlme_get_follow_ap_edca_flag(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_set_reconn_after_assoc_timeout_flag() - Set reconn after assoc timeout + * flag + * @psoc: soc object + * @vdev_id: vdev id + * @flag: enable or disable reconnect + * + * Return: void + */ +void mlme_set_reconn_after_assoc_timeout_flag(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool flag); + +/** + * mlme_get_reconn_after_assoc_timeout_flag() - Get reconn after assoc timeout + * flag + * @psoc: soc object + * @vdev_id: vdev id + * + * Return: true for enabling reconnect, otherwise false + */ +bool mlme_get_reconn_after_assoc_timeout_flag(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * mlme_get_peer_disconnect_ies() - Get diconnect IEs from vdev object + * @vdev: vdev pointer + * + * Return: Returns a pointer to the peer disconnect IEs present in vdev object + */ +struct wlan_ies *mlme_get_peer_disconnect_ies(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_set_peer_pmf_status() - set pmf status of peer + * @peer: PEER object + * @is_pmf_enabled: Carries if PMF is enabled or not + * + * is_pmf_enabled will be set to true if PMF is enabled by peer + * + * Return: void + */ +void mlme_set_peer_pmf_status(struct wlan_objmgr_peer *peer, + bool is_pmf_enabled); +/** + * mlme_get_peer_pmf_status() - get if peer is of pmf capable + * @peer: PEER object + * + * Return: Value of is_pmf_enabled; True if PMF is enabled by peer + */ +bool mlme_get_peer_pmf_status(struct wlan_objmgr_peer *peer); + +/** + * mlme_set_discon_reason_n_from_ap() - set disconnect reason and from ap flag + * @psoc: PSOC pointer + * @vdev_id: vdev id + * @from_ap: True if the disconnect is initiated from peer. + * False otherwise. + * @reason_code: The disconnect code received from peer or internally generated. + * + * Set the reason code and from_ap. + * + * Return: void + */ +void mlme_set_discon_reason_n_from_ap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool from_ap, + uint32_t reason_code); + +/** + * mlme_get_discon_reason_n_from_ap() - Get disconnect reason and from ap flag + * @psoc: PSOC pointer + * @vdev_id: vdev id + * @from_ap: Get the from_ap cached through mlme_set_discon_reason_n_from_ap + * and copy to this buffer. + * @reason_code: Get the reason_code cached through + * mlme_set_discon_reason_n_from_ap and copy to this buffer. + * + * Copy the contents of from_ap and reason_code to given buffers. + * + * Return: void + */ +void mlme_get_discon_reason_n_from_ap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool *from_ap, + uint32_t *reason_code); + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * mlme_get_supplicant_disabled_roaming() - Get supplicant disabled roaming + * value for a given vdev. + * @psoc: PSOC pointer + * @vdev_id: Vdev for which the supplicant disabled roaming value is being + * requested + * + * Return: True if supplicant disabled roaming else false + */ +bool +mlme_get_supplicant_disabled_roaming(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * mlme_set_supplicant_disabled_roaming - Set the supplicant disabled + * roaming flag. + * @psoc: PSOC pointer + * @vdev_id: Vdev for which the supplicant disabled roaming needs to + * be set + * @val: value true is to disable RSO and false to enable RSO + * + * Return: None + */ +void mlme_set_supplicant_disabled_roaming(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool val); + +/** + * mlme_get_roam_trigger_bitmap() - Get roaming trigger bitmap value for a given + * vdev. + * @psoc: PSOC pointer + * @vdev_id: Vdev for which the roam trigger bitmap is being requested + * + * Return: roaming trigger bitmap + */ +uint32_t +mlme_get_roam_trigger_bitmap(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id); + +/** + * mlme_set_roam_trigger_bitmap() - Set the roaming trigger bitmap value for + * the given vdev. If the bitmap is zero then roaming is completely disabled + * on the vdev which means roam structure in firmware is not allocated and no + * RSO start/stop commands can be sent + * @psoc: PSOC pointer + * @vdev_id: Vdev for which the roam trigger bitmap is to be set + * @val: bitmap value to set + * + * Return: None + */ +void mlme_set_roam_trigger_bitmap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t val); + +/** + * mlme_get_roam_state() - Get roam state from vdev object + * @psoc: psoc pointer + * @vdev_id: vdev id + * + * Return: Returns roam offload state + */ +enum roam_offload_state +mlme_get_roam_state(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id); + +/** + * mlme_set_roam_state() - Set roam state in vdev object + * @psoc: psoc pointer + * @vdev_id: vdev id + * @val: roam offload state + * + * Return: None + */ +void mlme_set_roam_state(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + enum roam_offload_state val); + +/** + * mlme_get_operations_bitmap() - Get the mlme operations bitmap which + * contains the bitmap of mlme operations which have disabled roaming + * temporarily + * @psoc: PSOC pointer + * @vdev_id: vdev for which the mlme operation bitmap is requested + * + * Return: bitmap value + */ +uint8_t +mlme_get_operations_bitmap(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id); + +/** + * mlme_set_operations_bitmap() - Set the mlme operations bitmap which + * indicates what mlme operations are in progress + * @psoc: PSOC pointer + * @vdev_id: vdev for which the mlme operation bitmap is requested + * @reqs: RSO stop requestor + * @clear: clear bit if true else set bit + * + * Return: None + */ +void +mlme_set_operations_bitmap(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + enum roam_control_requestor reqs, bool clear); + +/** + * mlme_clear_operations_bitmap() - Clear mlme operations bitmap which + * indicates what mlme operations are in progress + * @psoc: PSOC pointer + * @vdev_id: vdev for which the mlme operation bitmap is requested + * + * Return: None + */ +void +mlme_clear_operations_bitmap(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id); + +/** + * mlme_get_cfg_wlm_level() - Get the WLM level value + * @psoc: pointer to psoc object + * @level: level that needs to be filled. + * + * Return: QDF Status + */ +QDF_STATUS mlme_get_cfg_wlm_level(struct wlan_objmgr_psoc *psoc, + uint8_t *level); + +/** + * mlme_get_cfg_wlm_reset() - Get the WLM reset flag + * @psoc: pointer to psoc object + * @reset: reset that needs to be filled. + * + * Return: QDF Status + */ +QDF_STATUS mlme_get_cfg_wlm_reset(struct wlan_objmgr_psoc *psoc, + bool *reset); + +#define MLME_IS_ROAM_STATE_RSO_STARTED(psoc, vdev_id) \ + (mlme_get_roam_state(psoc, vdev_id) == ROAM_RSO_STARTED) + +#define MLME_IS_ROAM_STATE_DEINIT(psoc, vdev_id) \ + (mlme_get_roam_state(psoc, vdev_id) == ROAM_DEINIT) + +#define MLME_IS_ROAM_STATE_INIT(psoc, vdev_id) \ + (mlme_get_roam_state(psoc, vdev_id) == ROAM_INIT) + +#define MLME_IS_ROAM_STATE_STOPPED(psoc, vdev_id) \ + (mlme_get_roam_state(psoc, vdev_id) == ROAM_RSO_STOPPED) + +#define MLME_IS_ROAM_INITIALIZED(psoc, vdev_id) \ + (mlme_get_roam_state(psoc, vdev_id) >= ROAM_INIT) +#endif + +/** + * mlme_reinit_control_config_lfr_params() - Reinitialize roam control config + * @psoc: PSOC pointer + * @lfr: Pointer of an lfr_cfg buffer to fill. + * + * Reinitialize/restore the param related control roam config lfr params with + * default values of corresponding ini params. + * + * Return: None + */ +void mlme_reinit_control_config_lfr_params(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr); + +/** + * wlan_is_vdev_id_up() - check if vdev id is in UP state + * @pdev: Pointer to pdev + * @vdev_id: vdev id + * + * Return: if vdev is up + */ +bool wlan_is_vdev_id_up(struct wlan_objmgr_pdev *pdev, uint8_t vdev_id); +#endif diff --git a/drivers/staging/qcacld-3.0/components/mlme/core/inc/wlan_mlme_vdev_mgr_interface.h b/drivers/staging/qcacld-3.0/components/mlme/core/inc/wlan_mlme_vdev_mgr_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..3bd0ec7a0d92d24061c6e213ada9ba492bd7f398 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/core/inc/wlan_mlme_vdev_mgr_interface.h @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare VDEV Manager interface APIs exposed by the mlme component + */ + +#ifndef _WLAN_MLME_VDEV_MGR_INT_API_H_ +#define _WLAN_MLME_VDEV_MGR_INT_API_H_ + +#include +#include "include/wlan_vdev_mlme.h" +#include "wlan_mlme_main.h" + +/** + * mlme_register_mlme_ext_ops() - Register mlme ext ops + * + * This function is called to register mlme ext operations + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_register_mlme_ext_ops(void); +/** + * mlme_register_vdev_mgr_ops() - Register vdev mgr ops + * @vdev_mlme: vdev mlme object + * + * This function is called to register vdev manager operations + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_register_vdev_mgr_ops(struct vdev_mlme_obj *vdev_mlme); +/** + * mlme_unregister_vdev_mgr_ops() - Unregister vdev mgr ops + * @vdev_mlme: vdev mlme object + * + * This function is called to unregister vdev manager operations + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_unregister_vdev_mgr_ops(struct vdev_mlme_obj *vdev_mlme); + +/** + * mlme_set_chan_switch_in_progress() - set mlme priv restart in progress + * @vdev: vdev pointer + * @val: value to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_set_chan_switch_in_progress(struct wlan_objmgr_vdev *vdev, + bool val); + +/** + * mlme_is_chan_switch_in_progress() - get mlme priv restart in progress + * @vdev: vdev pointer + * + * Return: value of mlme priv restart in progress + */ +bool mlme_is_chan_switch_in_progress(struct wlan_objmgr_vdev *vdev); + +/** + * ap_mlme_set_hidden_ssid_restart_in_progress() - set mlme priv hidden ssid + * restart in progress + * @vdev: vdev pointer + * @val: value to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +ap_mlme_set_hidden_ssid_restart_in_progress(struct wlan_objmgr_vdev *vdev, + bool val); + +/** + * ap_mlme_is_hidden_ssid_restart_in_progress() - get mlme priv hidden ssid + * restart in progress + * @vdev: vdev pointer + * + * Return: value of mlme priv hidden ssid restart in progress + */ +bool ap_mlme_is_hidden_ssid_restart_in_progress(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_set_vdev_start_failed() - set mlme priv vdev restart fail flag + * @vdev: vdev pointer + * @val: value to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +mlme_set_vdev_start_failed(struct wlan_objmgr_vdev *vdev, bool val); + +/** + * mlme_is_connection_fail() - get connection fail flag + * @vdev: vdev pointer + * + * Return: value of vdev connection failure flag + */ +bool mlme_is_connection_fail(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_is_wapi_sta_active() - check sta with wapi security exists and is +active + * @pdev: pdev pointer + * + * Return: true if sta with wapi security exists + */ +#ifdef FEATURE_WLAN_WAPI +bool mlme_is_wapi_sta_active(struct wlan_objmgr_pdev *pdev); +#else +static inline bool mlme_is_wapi_sta_active(struct wlan_objmgr_pdev *pdev) +{ + return false; +} +#endif + +/** + * mlme_set_connection_fail() - set connection failure flag + * @vdev: vdev pointer + * @val: value to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +mlme_set_connection_fail(struct wlan_objmgr_vdev *vdev, bool val); + +/** + * mlme_get_vdev_start_failed() - get mlme priv vdev restart fail flag + * @vdev: vdev pointer + * + * Return: value of mlme priv vdev restart fail flag + */ +bool mlme_get_vdev_start_failed(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_get_cac_required() - get if cac is required for new channel + * @vdev: vdev pointer + * + * Return: if cac is required + */ +bool mlme_get_cac_required(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_set_cac_required() - set if cac is required for new channel + * @vdev: vdev pointer + * @val: value to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +mlme_set_cac_required(struct wlan_objmgr_vdev *vdev, bool val); + +/** + * mlme_set_mbssid_info() - save mbssid info + * @vdev: vdev pointer + * @mbssid_info: mbssid info + * + * Return: QDF_STATUS + */ +QDF_STATUS +mlme_set_mbssid_info(struct wlan_objmgr_vdev *vdev, + struct scan_mbssid_info *mbssid_info); + +/** + * mlme_get_mbssid_info() - get mbssid info + * @vdev: vdev pointer + * @mbss_11ax: mbss 11ax info + * + * Return: None + */ +void mlme_get_mbssid_info(struct wlan_objmgr_vdev *vdev, + struct vdev_mlme_mbss_11ax *mbss_11ax); + +/** + * mlme_set_tx_power() - set tx power + * @vdev: vdev pointer + * @tx_power: tx power to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_set_tx_power(struct wlan_objmgr_vdev *vdev, + int8_t tx_power); + +/** + * mlme_get_tx_power() - get tx power + * @vdev: vdev pointer + * @tx_power: tx power info + * + * Return: None + */ +int8_t mlme_get_tx_power(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_get_max_reg_power() - get max reg power + * @vdev: vdev pointer + * + * Return: max reg power + */ +int8_t mlme_get_max_reg_power(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_set_max_reg_power() - set max reg power + * @vdev: vdev pointer + * @max_tx_power: max tx power to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_set_max_reg_power(struct wlan_objmgr_vdev *vdev, + int8_t max_reg_power); + +/** + * mlme_is_vdev_in_beaconning_mode() - check if vdev is beaconing mode + * @vdev_opmode: vdev opmode + * + * To check if vdev is operating in beaconing mode or not. + * + * Return: true or false + */ +bool mlme_is_vdev_in_beaconning_mode(enum QDF_OPMODE vdev_opmode); + +/** + * mlme_set_assoc_type() - set associate type + * @vdev: vdev pointer + * @assoc_type: type to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_set_assoc_type(struct wlan_objmgr_vdev *vdev, + enum vdev_assoc_type assoc_type); + +/** + * mlme_get_vdev_bss_peer_mac_addr() - to get peer mac address + * @vdev: pointer to vdev + * @bss_peer_mac_address: pointer to bss_peer_mac_address + * + * This API is used to get mac address of peer. + * + * Return: QDF_STATUS based on overall success + */ +QDF_STATUS mlme_get_vdev_bss_peer_mac_addr( + struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *bss_peer_mac_address); + +/** + * mlme_get_vdev_stop_type() - to get vdev stop type + * @vdev: vdev pointer + * @vdev_stop_type: vdev stop type + * + * This API will get vdev stop type from mlme legacy priv. + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_get_vdev_stop_type(struct wlan_objmgr_vdev *vdev, + uint32_t *vdev_stop_type); + +/** + * mlme_set_vdev_stop_type() - to set vdev stop type + * @vdev: vdev pointer + * @vdev_stop_type: vdev stop type + * + * This API will set vdev stop type from mlme legacy priv. + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_set_vdev_stop_type(struct wlan_objmgr_vdev *vdev, + uint32_t vdev_stop_type); + +/** + * mlme_get_assoc_type() - get associate type + * @vdev: vdev pointer + * + * Return: associate type + */ +enum vdev_assoc_type mlme_get_assoc_type(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_vdev_create_send() - function to send the vdev create to firmware + * @vdev: vdev pointer + * + * Return: QDF_STATUS_SUCCESS when the command has been successfully sent + * to firmware or QDF_STATUS_E_** when there is a failure in sending the command + * to firmware. + */ +QDF_STATUS mlme_vdev_create_send(struct wlan_objmgr_vdev *vdev); + +/** + * mlme_vdev_self_peer_create() - function to send the vdev create self peer + * @vdev: vdev pointer + * + * Return: QDF_STATUS_SUCCESS when the self peer is successfully created + * to firmware or QDF_STATUS_E_** when there is a failure. + */ +QDF_STATUS mlme_vdev_self_peer_create(struct wlan_objmgr_vdev *vdev); +#endif diff --git a/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_main.c b/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_main.c new file mode 100644 index 0000000000000000000000000000000000000000..9035571d1a0c3820f09475453b1f0165a11f70ca --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_main.c @@ -0,0 +1,3136 @@ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define internal APIs related to the mlme component + */ + +#include "wlan_mlme_main.h" +#include "include/wlan_vdev_mlme.h" +#include "cfg_ucfg_api.h" +#include "wmi_unified.h" +#include "wlan_scan_public_structs.h" +#include "wlan_psoc_mlme_api.h" +#include "wlan_vdev_mlme_api.h" +#include "wlan_mlme_api.h" +#include + +#define NUM_OF_SOUNDING_DIMENSIONS 1 /*Nss - 1, (Nss = 2 for 2x2)*/ + +struct wlan_mlme_psoc_ext_obj *mlme_get_psoc_ext_obj_fl( + struct wlan_objmgr_psoc *psoc, + const char *func, uint32_t line) +{ + + return wlan_psoc_mlme_get_ext_hdl(psoc); +} + +struct wlan_mlme_nss_chains *mlme_get_dynamic_vdev_config( + struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return NULL; + } + + return &mlme_priv->dynamic_cfg; +} + +struct wlan_mlme_nss_chains *mlme_get_ini_vdev_config( + struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return NULL; + } + + return &mlme_priv->ini_cfg; +} + +struct mlme_roam_after_data_stall * +mlme_get_roam_invoke_params(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return NULL; + } + + return &mlme_priv->roam_invoke_params; +} + +uint8_t *mlme_get_dynamic_oce_flags(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return NULL; + } + + return &mlme_priv->sta_dynamic_oce_value; +} + +QDF_STATUS mlme_get_peer_mic_len(struct wlan_objmgr_psoc *psoc, uint8_t pdev_id, + uint8_t *peer_mac, uint8_t *mic_len, + uint8_t *mic_hdr_len) +{ + struct wlan_objmgr_peer *peer; + uint32_t key_cipher; + + if (!psoc || !mic_len || !mic_hdr_len || !peer_mac) { + mlme_legacy_debug("psoc/mic_len/mic_hdr_len/peer_mac null"); + return QDF_STATUS_E_NULL_VALUE; + } + + peer = wlan_objmgr_get_peer(psoc, pdev_id, + peer_mac, WLAN_LEGACY_MAC_ID); + if (!peer) { + mlme_legacy_debug("Peer of peer_mac "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(peer_mac)); + return QDF_STATUS_E_INVAL; + } + + key_cipher = + wlan_crypto_get_peer_param(peer, + WLAN_CRYPTO_PARAM_UCAST_CIPHER); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID); + + if (key_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_GCM) || + key_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_GCM_256)) { + *mic_hdr_len = WLAN_IEEE80211_GCMP_HEADERLEN; + *mic_len = WLAN_IEEE80211_GCMP_MICLEN; + } else { + *mic_hdr_len = IEEE80211_CCMP_HEADERLEN; + *mic_len = IEEE80211_CCMP_MICLEN; + } + mlme_legacy_debug("peer "QDF_MAC_ADDR_FMT" hdr_len %d mic_len %d key_cipher 0x%x", + QDF_MAC_ADDR_REF(peer_mac), + *mic_hdr_len, *mic_len, key_cipher); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +mlme_peer_object_created_notification(struct wlan_objmgr_peer *peer, + void *arg) +{ + struct peer_mlme_priv_obj *peer_priv; + QDF_STATUS status; + + if (!peer) { + mlme_legacy_err(" peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + + peer_priv = qdf_mem_malloc(sizeof(*peer_priv)); + if (!peer_priv) { + mlme_legacy_err(" peer_priv component object alloc failed"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_peer_component_obj_attach(peer, + WLAN_UMAC_COMP_MLME, + (void *)peer_priv, + QDF_STATUS_SUCCESS); + + if (QDF_IS_STATUS_ERROR(status)) { + mlme_legacy_err("unable to attach peer_priv obj to peer obj"); + qdf_mem_free(peer_priv); + } + + return status; +} + +QDF_STATUS +mlme_peer_object_destroyed_notification(struct wlan_objmgr_peer *peer, + void *arg) +{ + struct peer_mlme_priv_obj *peer_priv; + QDF_STATUS status; + + if (!peer) { + mlme_legacy_err(" peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + + peer_priv = wlan_objmgr_peer_get_comp_private_obj(peer, + WLAN_UMAC_COMP_MLME); + if (!peer_priv) { + mlme_legacy_err(" peer MLME component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_peer_component_obj_detach(peer, + WLAN_UMAC_COMP_MLME, + peer_priv); + + if (QDF_IS_STATUS_ERROR(status)) + mlme_legacy_err("unable to detach peer_priv obj to peer obj"); + + qdf_mem_free(peer_priv); + + return status; +} + +static void mlme_init_chainmask_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_chainmask *chainmask_info) +{ + chainmask_info->txchainmask1x1 = + cfg_get(psoc, CFG_VHT_ENABLE_1x1_TX_CHAINMASK); + + chainmask_info->rxchainmask1x1 = + cfg_get(psoc, CFG_VHT_ENABLE_1x1_RX_CHAINMASK); + + chainmask_info->tx_chain_mask_cck = + cfg_get(psoc, CFG_TX_CHAIN_MASK_CCK); + + chainmask_info->tx_chain_mask_1ss = + cfg_get(psoc, CFG_TX_CHAIN_MASK_1SS); + + chainmask_info->num_11b_tx_chains = + cfg_get(psoc, CFG_11B_NUM_TX_CHAIN); + + chainmask_info->num_11ag_tx_chains = + cfg_get(psoc, CFG_11AG_NUM_TX_CHAIN); + + chainmask_info->tx_chain_mask_2g = + cfg_get(psoc, CFG_TX_CHAIN_MASK_2G); + + chainmask_info->rx_chain_mask_2g = + cfg_get(psoc, CFG_RX_CHAIN_MASK_2G); + + chainmask_info->tx_chain_mask_5g = + cfg_get(psoc, CFG_TX_CHAIN_MASK_5G); + + chainmask_info->rx_chain_mask_5g = + cfg_get(psoc, CFG_RX_CHAIN_MASK_5G); + + chainmask_info->enable_bt_chain_separation = + cfg_get(psoc, CFG_ENABLE_BT_CHAIN_SEPARATION); +} + +static void mlme_init_ratemask_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_ratemask *ratemask_cfg) +{ + uint32_t masks[CFG_MLME_RATE_MASK_LEN] = { 0 }; + qdf_size_t len = 0; + QDF_STATUS status; + + ratemask_cfg->type = cfg_get(psoc, CFG_RATEMASK_TYPE); + if ((ratemask_cfg->type <= WLAN_MLME_RATEMASK_TYPE_NO_MASK) || + (ratemask_cfg->type >= WLAN_MLME_RATEMASK_TYPE_MAX)) { + mlme_legacy_debug("Ratemask disabled"); + return; + } + + status = qdf_uint32_array_parse(cfg_get(psoc, CFG_RATEMASK_SET), + masks, + CFG_MLME_RATE_MASK_LEN, + &len); + + if (status != QDF_STATUS_SUCCESS || len != CFG_MLME_RATE_MASK_LEN) { + /* Do not enable ratemaks if config is invalid */ + ratemask_cfg->type = WLAN_MLME_RATEMASK_TYPE_NO_MASK; + mlme_legacy_err("Failed to parse ratemask"); + return; + } + + ratemask_cfg->lower32 = masks[0]; + ratemask_cfg->higher32 = masks[1]; + ratemask_cfg->lower32_2 = masks[2]; + ratemask_cfg->higher32_2 = masks[3]; + mlme_legacy_debug("Ratemask type: %d, masks:0x%x, 0x%x, 0x%x, 0x%x", + ratemask_cfg->type, ratemask_cfg->lower32, + ratemask_cfg->higher32, ratemask_cfg->lower32_2, + ratemask_cfg->higher32_2); +} + +#ifdef WLAN_FEATURE_11W +static void mlme_init_pmf_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_generic *gen) +{ + gen->pmf_sa_query_max_retries = + cfg_get(psoc, CFG_PMF_SA_QUERY_MAX_RETRIES); + gen->pmf_sa_query_retry_interval = + cfg_get(psoc, CFG_PMF_SA_QUERY_RETRY_INTERVAL); +} +#else +static void mlme_init_pmf_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_generic *gen) +{ + gen->pmf_sa_query_max_retries = + cfg_default(CFG_PMF_SA_QUERY_MAX_RETRIES); + gen->pmf_sa_query_retry_interval = + cfg_default(CFG_PMF_SA_QUERY_RETRY_INTERVAL); +} +#endif /*WLAN_FEATURE_11W*/ + +#ifdef WLAN_FEATURE_LPSS +static inline void +mlme_init_lpass_support_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_generic *gen) +{ + gen->lpass_support = cfg_get(psoc, CFG_ENABLE_LPASS_SUPPORT); +} +#else +static inline void +mlme_init_lpass_support_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_generic *gen) +{ + gen->lpass_support = cfg_default(CFG_ENABLE_LPASS_SUPPORT); +} +#endif + +static void mlme_init_generic_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_generic *gen) +{ + gen->rtt3_enabled = cfg_default(CFG_RTT3_ENABLE); + gen->rtt_mac_randomization = + cfg_get(psoc, CFG_ENABLE_RTT_MAC_RANDOMIZATION); + gen->band_capability = + cfg_get(psoc, CFG_BAND_CAPABILITY); + if (!gen->band_capability) + gen->band_capability = (BIT(REG_BAND_2G) | BIT(REG_BAND_5G)); + gen->band = gen->band_capability; + gen->select_5ghz_margin = + cfg_get(psoc, CFG_SELECT_5GHZ_MARGIN); + gen->sub_20_chan_width = + cfg_get(psoc, CFG_SUB_20_CHANNEL_WIDTH); + gen->ito_repeat_count = + cfg_get(psoc, CFG_ITO_REPEAT_COUNT); + gen->dropped_pkt_disconnect_thresh = + cfg_get(psoc, CFG_DROPPED_PKT_DISCONNECT_THRESHOLD); + gen->prevent_link_down = + cfg_get(psoc, CFG_PREVENT_LINK_DOWN); + gen->memory_deep_sleep = + cfg_get(psoc, CFG_ENABLE_MEM_DEEP_SLEEP); + gen->cck_tx_fir_override = + cfg_get(psoc, CFG_ENABLE_CCK_TX_FIR_OVERRIDE); + gen->crash_inject = + cfg_get(psoc, CFG_ENABLE_CRASH_INJECT); + gen->self_recovery = + cfg_get(psoc, CFG_ENABLE_SELF_RECOVERY); + gen->sap_dot11mc = + cfg_get(psoc, CFG_SAP_DOT11MC); + gen->fatal_event_trigger = + cfg_get(psoc, CFG_ENABLE_FATAL_EVENT_TRIGGER); + gen->optimize_ca_event = + cfg_get(psoc, CFG_OPTIMIZE_CA_EVENT); + gen->fw_timeout_crash = + cfg_get(psoc, CFG_CRASH_FW_TIMEOUT); + gen->debug_packet_log = cfg_get(psoc, CFG_ENABLE_DEBUG_PACKET_LOG); + gen->enable_deauth_to_disassoc_map = + cfg_get(psoc, CFG_ENABLE_DEAUTH_TO_DISASSOC_MAP); + mlme_init_pmf_cfg(psoc, gen); + mlme_init_lpass_support_cfg(psoc, gen); + + gen->enabled_11h = cfg_get(psoc, CFG_11H_SUPPORT_ENABLED); + gen->enabled_11d = cfg_get(psoc, CFG_11D_SUPPORT_ENABLED); + gen->enable_beacon_reception_stats = + cfg_get(psoc, CFG_ENABLE_BEACON_RECEPTION_STATS); + gen->enable_remove_time_stamp_sync_cmd = + cfg_get(psoc, CFG_REMOVE_TIME_STAMP_SYNC_CMD); + gen->disable_4way_hs_offload = + cfg_get(psoc, CFG_DISABLE_4WAY_HS_OFFLOAD); + gen->mgmt_retry_max = cfg_get(psoc, CFG_MGMT_RETRY_MAX); + gen->bmiss_skip_full_scan = cfg_get(psoc, CFG_BMISS_SKIP_FULL_SCAN); + gen->enable_ring_buffer = cfg_get(psoc, CFG_ENABLE_RING_BUFFER); + gen->enable_peer_unmap_conf_support = + cfg_get(psoc, CFG_DP_ENABLE_PEER_UMAP_CONF_SUPPORT); + gen->sae_connect_retries = + cfg_get(psoc, CFG_SAE_CONNECION_RETRIES); +} + +static void mlme_init_edca_ani_cfg(struct wlan_mlme_edca_params *edca_params) +{ + /* initialize the max allowed array length for read/write */ + edca_params->ani_acbe_l.max_len = CFG_EDCA_DATA_LEN; + edca_params->ani_acbk_l.max_len = CFG_EDCA_DATA_LEN; + edca_params->ani_acvi_l.max_len = CFG_EDCA_DATA_LEN; + edca_params->ani_acvo_l.max_len = CFG_EDCA_DATA_LEN; + + edca_params->ani_acbe_b.max_len = CFG_EDCA_DATA_LEN; + edca_params->ani_acbk_b.max_len = CFG_EDCA_DATA_LEN; + edca_params->ani_acvi_b.max_len = CFG_EDCA_DATA_LEN; + edca_params->ani_acvo_b.max_len = CFG_EDCA_DATA_LEN; + + /* parse the ETSI edca parameters from cfg string for BK,BE,VI,VO ac */ + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ANI_ACBK_LOCAL), + edca_params->ani_acbk_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->ani_acbk_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ANI_ACBE_LOCAL), + edca_params->ani_acbe_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->ani_acbe_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ANI_ACVI_LOCAL), + edca_params->ani_acvi_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->ani_acvi_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ANI_ACVO_LOCAL), + edca_params->ani_acvo_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->ani_acvo_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ANI_ACBK), + edca_params->ani_acbk_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->ani_acbk_b.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ANI_ACBE), + edca_params->ani_acbe_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->ani_acbe_b.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ANI_ACVI), + edca_params->ani_acvi_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->ani_acvi_b.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ANI_ACVO), + edca_params->ani_acvo_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->ani_acvo_b.len); +} + +static void mlme_init_edca_wme_cfg(struct wlan_mlme_edca_params *edca_params) +{ + /* initialize the max allowed array length for read/write */ + edca_params->wme_acbk_l.max_len = CFG_EDCA_DATA_LEN; + edca_params->wme_acbe_l.max_len = CFG_EDCA_DATA_LEN; + edca_params->wme_acvi_l.max_len = CFG_EDCA_DATA_LEN; + edca_params->wme_acvo_l.max_len = CFG_EDCA_DATA_LEN; + + edca_params->wme_acbk_b.max_len = CFG_EDCA_DATA_LEN; + edca_params->wme_acbe_b.max_len = CFG_EDCA_DATA_LEN; + edca_params->wme_acvi_b.max_len = CFG_EDCA_DATA_LEN; + edca_params->wme_acvo_b.max_len = CFG_EDCA_DATA_LEN; + + /* parse the WME edca parameters from cfg string for BK,BE,VI,VO ac */ + qdf_uint8_array_parse(cfg_default(CFG_EDCA_WME_ACBK_LOCAL), + edca_params->wme_acbk_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->wme_acbk_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_WME_ACBE_LOCAL), + edca_params->wme_acbe_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->wme_acbe_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_WME_ACVI_LOCAL), + edca_params->wme_acvi_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->wme_acvi_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_WME_ACVO_LOCAL), + edca_params->wme_acvo_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->wme_acvo_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_WME_ACBK), + edca_params->wme_acbk_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->wme_acbk_b.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_WME_ACBE), + edca_params->wme_acbe_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->wme_acbe_b.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_WME_ACVI), + edca_params->wme_acvi_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->wme_acvi_b.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_WME_ACVO), + edca_params->wme_acvo_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->wme_acvo_b.len); +} + +static void mlme_init_edca_etsi_cfg(struct wlan_mlme_edca_params *edca_params) +{ + /* initialize the max allowed array length for read/write */ + edca_params->etsi_acbe_l.max_len = CFG_EDCA_DATA_LEN; + edca_params->etsi_acbk_l.max_len = CFG_EDCA_DATA_LEN; + edca_params->etsi_acvi_l.max_len = CFG_EDCA_DATA_LEN; + edca_params->etsi_acvo_l.max_len = CFG_EDCA_DATA_LEN; + + edca_params->etsi_acbe_b.max_len = CFG_EDCA_DATA_LEN; + edca_params->etsi_acbk_b.max_len = CFG_EDCA_DATA_LEN; + edca_params->etsi_acvi_b.max_len = CFG_EDCA_DATA_LEN; + edca_params->etsi_acvo_b.max_len = CFG_EDCA_DATA_LEN; + + /* parse the ETSI edca parameters from cfg string for BK,BE,VI,VO ac */ + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ETSI_ACBK_LOCAL), + edca_params->etsi_acbk_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->etsi_acbk_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ETSI_ACBE_LOCAL), + edca_params->etsi_acbe_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->etsi_acbe_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ETSI_ACVI_LOCAL), + edca_params->etsi_acvi_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->etsi_acvi_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ETSI_ACVO_LOCAL), + edca_params->etsi_acvo_l.data, + CFG_EDCA_DATA_LEN, + &edca_params->etsi_acvo_l.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ETSI_ACBK), + edca_params->etsi_acbk_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->etsi_acbk_b.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ETSI_ACBE), + edca_params->etsi_acbe_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->etsi_acbe_b.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ETSI_ACVI), + edca_params->etsi_acvi_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->etsi_acvi_b.len); + + qdf_uint8_array_parse(cfg_default(CFG_EDCA_ETSI_ACVO), + edca_params->etsi_acvo_b.data, + CFG_EDCA_DATA_LEN, + &edca_params->etsi_acvo_b.len); +} + +static void +mlme_init_qos_edca_params(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_edca_params *edca_params) +{ + edca_params->enable_edca_params = + cfg_get(psoc, CFG_EDCA_ENABLE_PARAM); + + edca_params->edca_ac_vo.vo_cwmin = + cfg_get(psoc, CFG_EDCA_VO_CWMIN); + edca_params->edca_ac_vo.vo_cwmax = + cfg_get(psoc, CFG_EDCA_VO_CWMAX); + edca_params->edca_ac_vo.vo_aifs = + cfg_get(psoc, CFG_EDCA_VO_AIFS); + + edca_params->edca_ac_vi.vi_cwmin = + cfg_get(psoc, CFG_EDCA_VI_CWMIN); + edca_params->edca_ac_vi.vi_cwmax = + cfg_get(psoc, CFG_EDCA_VI_CWMAX); + edca_params->edca_ac_vi.vi_aifs = + cfg_get(psoc, CFG_EDCA_VI_AIFS); + + edca_params->edca_ac_bk.bk_cwmin = + cfg_get(psoc, CFG_EDCA_BK_CWMIN); + edca_params->edca_ac_bk.bk_cwmax = + cfg_get(psoc, CFG_EDCA_BK_CWMAX); + edca_params->edca_ac_bk.bk_aifs = + cfg_get(psoc, CFG_EDCA_BK_AIFS); + + edca_params->edca_ac_be.be_cwmin = + cfg_get(psoc, CFG_EDCA_BE_CWMIN); + edca_params->edca_ac_be.be_cwmax = + cfg_get(psoc, CFG_EDCA_BE_CWMAX); + edca_params->edca_ac_be.be_aifs = + cfg_get(psoc, CFG_EDCA_BE_AIFS); +} + +static void mlme_init_edca_params(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_edca_params *edca_params) +{ + mlme_init_edca_ani_cfg(edca_params); + mlme_init_edca_wme_cfg(edca_params); + mlme_init_edca_etsi_cfg(edca_params); + mlme_init_qos_edca_params(psoc, edca_params); +} + +static void mlme_init_timeout_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_timeout *timeouts) +{ + timeouts->join_failure_timeout = + cfg_get(psoc, CFG_JOIN_FAILURE_TIMEOUT); + timeouts->auth_failure_timeout = + cfg_get(psoc, CFG_AUTH_FAILURE_TIMEOUT); + timeouts->auth_rsp_timeout = + cfg_get(psoc, CFG_AUTH_RSP_TIMEOUT); + timeouts->assoc_failure_timeout = + cfg_get(psoc, CFG_ASSOC_FAILURE_TIMEOUT); + timeouts->reassoc_failure_timeout = + cfg_get(psoc, CFG_REASSOC_FAILURE_TIMEOUT); + timeouts->probe_after_hb_fail_timeout = + cfg_get(psoc, CFG_PROBE_AFTER_HB_FAIL_TIMEOUT); + timeouts->olbc_detect_timeout = + cfg_get(psoc, CFG_OLBC_DETECT_TIMEOUT); + timeouts->addts_rsp_timeout = + cfg_get(psoc, CFG_ADDTS_RSP_TIMEOUT); + timeouts->heart_beat_threshold = + cfg_get(psoc, CFG_HEART_BEAT_THRESHOLD); + timeouts->ap_keep_alive_timeout = + cfg_get(psoc, CFG_AP_KEEP_ALIVE_TIMEOUT); + timeouts->ap_link_monitor_timeout = + cfg_get(psoc, CFG_AP_LINK_MONITOR_TIMEOUT); + timeouts->ps_data_inactivity_timeout = + cfg_get(psoc, CFG_PS_DATA_INACTIVITY_TIMEOUT); + timeouts->wmi_wq_watchdog_timeout = + cfg_get(psoc, CFG_WMI_WQ_WATCHDOG); +} + +static void mlme_init_ht_cap_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_ht_caps *ht_caps) +{ + union { + uint16_t val_16; + struct mlme_ht_capabilities_info ht_cap_info; + } u1; + + union { + uint16_t val_16; + struct mlme_ht_ext_cap_info ext_cap_info; + } u2; + + union { + uint8_t val_8; + struct mlme_ht_info_field_1 info_field_1; + } u3; + + union { + uint16_t val_16; + struct mlme_ht_info_field_2 info_field_2; + } u4; + + union { + uint16_t val_16; + struct mlme_ht_info_field_3 info_field_3; + } u5; + + /* HT Capabilities - HT Caps Info Field */ + u1.val_16 = (uint16_t)cfg_default(CFG_HT_CAP_INFO); + u1.ht_cap_info.adv_coding_cap = + cfg_get(psoc, CFG_RX_LDPC_ENABLE); + u1.ht_cap_info.rx_stbc = cfg_get(psoc, CFG_RX_STBC_ENABLE); + u1.ht_cap_info.tx_stbc = cfg_get(psoc, CFG_TX_STBC_ENABLE); + u1.ht_cap_info.short_gi_20_mhz = + cfg_get(psoc, CFG_SHORT_GI_20MHZ); + u1.ht_cap_info.short_gi_40_mhz = + cfg_get(psoc, CFG_SHORT_GI_40MHZ); + ht_caps->ht_cap_info = u1.ht_cap_info; + + /* HT Capapabilties - AMPDU Params */ + ht_caps->ampdu_params.max_rx_ampdu_factor = + cfg_get(psoc, CFG_MAX_RX_AMPDU_FACTOR); + ht_caps->ampdu_params.mpdu_density = + cfg_get(psoc, CFG_MPDU_DENSITY); + ht_caps->ampdu_params.reserved = 0; + + /* HT Capabilities - Extended Capabilities field */ + u2.val_16 = (uint16_t)cfg_default(CFG_EXT_HT_CAP_INFO); + ht_caps->ext_cap_info = u2.ext_cap_info; + + /* HT Operation - Information subset 1 of 3 */ + u3.val_8 = (uint8_t)cfg_default(CFG_HT_INFO_FIELD_1); + ht_caps->info_field_1 = u3.info_field_1; + + /* HT Operation - Information subset 2 of 3 */ + u4.val_16 = (uint16_t)cfg_default(CFG_HT_INFO_FIELD_2); + ht_caps->info_field_2 = u4.info_field_2; + + /* HT Operation - Information subset 3 of 3 */ + u5.val_16 = (uint16_t)cfg_default(CFG_HT_INFO_FIELD_3); + ht_caps->info_field_3 = u5.info_field_3; + + ht_caps->short_preamble = cfg_get(psoc, CFG_SHORT_PREAMBLE); + ht_caps->enable_ampdu_ps = cfg_get(psoc, CFG_ENABLE_AMPDUPS); + ht_caps->enable_smps = cfg_get(psoc, CFG_ENABLE_HT_SMPS); + ht_caps->smps = cfg_get(psoc, CFG_HT_SMPS_MODE); + ht_caps->max_num_amsdu = cfg_get(psoc, CFG_MAX_AMSDU_NUM); + ht_caps->tx_ldpc_enable = cfg_get(psoc, CFG_TX_LDPC_ENABLE); + ht_caps->short_slot_time_enabled = + cfg_get(psoc, CFG_SHORT_SLOT_TIME_ENABLED); +} + +static void mlme_init_qos_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_qos *qos_aggr_params) +{ + qos_aggr_params->tx_aggregation_size = + cfg_get(psoc, CFG_TX_AGGREGATION_SIZE); + qos_aggr_params->tx_aggregation_size_be = + cfg_get(psoc, CFG_TX_AGGREGATION_SIZEBE); + qos_aggr_params->tx_aggregation_size_bk = + cfg_get(psoc, CFG_TX_AGGREGATION_SIZEBK); + qos_aggr_params->tx_aggregation_size_vi = + cfg_get(psoc, CFG_TX_AGGREGATION_SIZEVI); + qos_aggr_params->tx_aggregation_size_vo = + cfg_get(psoc, CFG_TX_AGGREGATION_SIZEVO); + qos_aggr_params->rx_aggregation_size = + cfg_get(psoc, CFG_RX_AGGREGATION_SIZE); + qos_aggr_params->tx_aggr_sw_retry_threshold_be = + cfg_get(psoc, CFG_TX_AGGR_SW_RETRY_BE); + qos_aggr_params->tx_aggr_sw_retry_threshold_bk = + cfg_get(psoc, CFG_TX_AGGR_SW_RETRY_BK); + qos_aggr_params->tx_aggr_sw_retry_threshold_vi = + cfg_get(psoc, CFG_TX_AGGR_SW_RETRY_VI); + qos_aggr_params->tx_aggr_sw_retry_threshold_vo = + cfg_get(psoc, CFG_TX_AGGR_SW_RETRY_VO); + qos_aggr_params->tx_aggr_sw_retry_threshold = + cfg_get(psoc, CFG_TX_AGGR_SW_RETRY); + qos_aggr_params->tx_non_aggr_sw_retry_threshold_be = + cfg_get(psoc, CFG_TX_NON_AGGR_SW_RETRY_BE); + qos_aggr_params->tx_non_aggr_sw_retry_threshold_bk = + cfg_get(psoc, CFG_TX_NON_AGGR_SW_RETRY_BK); + qos_aggr_params->tx_non_aggr_sw_retry_threshold_vi = + cfg_get(psoc, CFG_TX_NON_AGGR_SW_RETRY_VI); + qos_aggr_params->tx_non_aggr_sw_retry_threshold_vo = + cfg_get(psoc, CFG_TX_NON_AGGR_SW_RETRY_VO); + qos_aggr_params->tx_non_aggr_sw_retry_threshold = + cfg_get(psoc, CFG_TX_NON_AGGR_SW_RETRY); + qos_aggr_params->sap_max_inactivity_override = + cfg_get(psoc, CFG_SAP_MAX_INACTIVITY_OVERRIDE); + qos_aggr_params->sap_uapsd_enabled = + cfg_get(psoc, CFG_SAP_QOS_UAPSD); +} + +static void mlme_init_mbo_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_mbo *mbo_params) +{ + mbo_params->mbo_candidate_rssi_thres = + cfg_get(psoc, CFG_MBO_CANDIDATE_RSSI_THRESHOLD); + mbo_params->mbo_current_rssi_thres = + cfg_get(psoc, CFG_MBO_CURRENT_RSSI_THRESHOLD); + mbo_params->mbo_current_rssi_mcc_thres = + cfg_get(psoc, CFG_MBO_CUR_RSSI_MCC_THRESHOLD); + mbo_params->mbo_candidate_rssi_btc_thres = + cfg_get(psoc, CFG_MBO_CAND_RSSI_BTC_THRESHOLD); +} + +static void mlme_init_vht_cap_cfg(struct wlan_objmgr_psoc *psoc, + struct mlme_vht_capabilities_info + *vht_cap_info) +{ + vht_cap_info->supp_chan_width = + cfg_default(CFG_VHT_SUPP_CHAN_WIDTH); + vht_cap_info->num_soundingdim = + cfg_default(CFG_VHT_NUM_SOUNDING_DIMENSIONS); + vht_cap_info->htc_vhtc = + cfg_default(CFG_VHT_HTC_VHTC); + vht_cap_info->link_adap_cap = + cfg_default(CFG_VHT_LINK_ADAPTATION_CAP); + vht_cap_info->rx_antpattern = + cfg_default(CFG_VHT_RX_ANT_PATTERN); + vht_cap_info->tx_antpattern = + cfg_default(CFG_VHT_TX_ANT_PATTERN); + vht_cap_info->rx_supp_data_rate = + cfg_default(CFG_VHT_RX_SUPP_DATA_RATE); + vht_cap_info->tx_supp_data_rate = + cfg_default(CFG_VHT_TX_SUPP_DATA_RATE); + vht_cap_info->txop_ps = + cfg_default(CFG_VHT_TXOP_PS); + vht_cap_info->rx_mcs_map = + CFG_VHT_RX_MCS_MAP_STADEF; + vht_cap_info->tx_mcs_map = + CFG_VHT_TX_MCS_MAP_STADEF; + vht_cap_info->basic_mcs_set = + CFG_VHT_BASIC_MCS_SET_STADEF; + + vht_cap_info->tx_bfee_ant_supp = + cfg_get(psoc, CFG_VHT_BEAMFORMEE_ANT_SUPP); + + vht_cap_info->enable_txbf_20mhz = + cfg_get(psoc, CFG_VHT_ENABLE_TXBF_IN_20MHZ); + vht_cap_info->ampdu_len = + cfg_get(psoc, CFG_VHT_MPDU_LEN); + + vht_cap_info->ldpc_coding_cap = + cfg_get(psoc, CFG_RX_LDPC_ENABLE); + vht_cap_info->short_gi_80mhz = + cfg_get(psoc, CFG_SHORT_GI_40MHZ); + vht_cap_info->short_gi_160mhz = + cfg_get(psoc, CFG_SHORT_GI_40MHZ); + vht_cap_info->tx_stbc = + cfg_get(psoc, CFG_TX_STBC_ENABLE); + vht_cap_info->rx_stbc = + cfg_get(psoc, CFG_RX_STBC_ENABLE); + + vht_cap_info->su_bformee = + cfg_get(psoc, CFG_VHT_SU_BEAMFORMEE_CAP); + + vht_cap_info->mu_bformer = + cfg_default(CFG_VHT_MU_BEAMFORMER_CAP); + + vht_cap_info->enable_mu_bformee = + cfg_get(psoc, CFG_VHT_ENABLE_MU_BFORMEE_CAP_FEATURE); + vht_cap_info->ampdu_len_exponent = + cfg_get(psoc, CFG_VHT_AMPDU_LEN_EXPONENT); + vht_cap_info->channel_width = + cfg_get(psoc, CFG_VHT_CHANNEL_WIDTH); + vht_cap_info->rx_mcs = + cfg_get(psoc, CFG_VHT_ENABLE_RX_MCS_8_9); + vht_cap_info->tx_mcs = + cfg_get(psoc, CFG_VHT_ENABLE_TX_MCS_8_9); + vht_cap_info->rx_mcs2x2 = + cfg_get(psoc, CFG_VHT_ENABLE_RX_MCS2x2_8_9); + vht_cap_info->tx_mcs2x2 = + cfg_get(psoc, CFG_VHT_ENABLE_TX_MCS2x2_8_9); + vht_cap_info->enable_vht20_mcs9 = + cfg_get(psoc, CFG_ENABLE_VHT20_MCS9); + vht_cap_info->enable2x2 = + cfg_get(psoc, CFG_VHT_ENABLE_2x2_CAP_FEATURE); + vht_cap_info->enable_paid = + cfg_get(psoc, CFG_VHT_ENABLE_PAID_FEATURE); + vht_cap_info->enable_gid = + cfg_get(psoc, CFG_VHT_ENABLE_GID_FEATURE); + vht_cap_info->b24ghz_band = + cfg_get(psoc, CFG_ENABLE_VHT_FOR_24GHZ); + vht_cap_info->vendor_24ghz_band = + cfg_get(psoc, CFG_ENABLE_VENDOR_VHT_FOR_24GHZ); + vht_cap_info->tx_bfee_sap = + cfg_get(psoc, CFG_VHT_ENABLE_TXBF_SAP_MODE); + vht_cap_info->vendor_vhtie = + cfg_get(psoc, CFG_ENABLE_SUBFEE_IN_VENDOR_VHTIE); + + if (vht_cap_info->enable2x2) + vht_cap_info->su_bformer = + cfg_get(psoc, CFG_VHT_ENABLE_TX_SU_BEAM_FORMER); + + if (vht_cap_info->enable2x2 && vht_cap_info->su_bformer) + vht_cap_info->num_soundingdim = NUM_OF_SOUNDING_DIMENSIONS; + + vht_cap_info->tx_bf_cap = cfg_default(CFG_TX_BF_CAP); + vht_cap_info->as_cap = cfg_default(CFG_AS_CAP); + vht_cap_info->disable_ldpc_with_txbf_ap = + cfg_get(psoc, CFG_DISABLE_LDPC_WITH_TXBF_AP); +} + +static void mlme_init_rates_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_rates *rates) +{ + rates->cfp_period = cfg_default(CFG_CFP_PERIOD); + rates->cfp_max_duration = cfg_default(CFG_CFP_MAX_DURATION); + rates->max_htmcs_txdata = cfg_get(psoc, CFG_MAX_HT_MCS_FOR_TX_DATA); + rates->disable_abg_rate_txdata = cfg_get(psoc, + CFG_DISABLE_ABG_RATE_FOR_TX_DATA); + rates->sap_max_mcs_txdata = cfg_get(psoc, + CFG_SAP_MAX_MCS_FOR_TX_DATA); + rates->disable_high_ht_mcs_2x2 = cfg_get(psoc, + CFG_DISABLE_HIGH_HT_RX_MCS_2x2); + + rates->supported_11b.max_len = CFG_SUPPORTED_RATES_11B_LEN; + qdf_uint8_array_parse(cfg_default(CFG_SUPPORTED_RATES_11B), + rates->supported_11b.data, + sizeof(rates->supported_11b.data), + &rates->supported_11b.len); + rates->supported_11a.max_len = CFG_SUPPORTED_RATES_11A_LEN; + qdf_uint8_array_parse(cfg_default(CFG_SUPPORTED_RATES_11A), + rates->supported_11a.data, + sizeof(rates->supported_11a.data), + &rates->supported_11a.len); + rates->opr_rate_set.max_len = CFG_OPERATIONAL_RATE_SET_LEN; + rates->opr_rate_set.len = 0; + rates->ext_opr_rate_set.max_len = CFG_EXTENDED_OPERATIONAL_RATE_SET_LEN; + rates->ext_opr_rate_set.len = 0; + rates->supported_mcs_set.max_len = CFG_SUPPORTED_MCS_SET_LEN; + qdf_uint8_array_parse(cfg_default(CFG_SUPPORTED_MCS_SET), + rates->supported_mcs_set.data, + sizeof(rates->supported_mcs_set.data), + &rates->supported_mcs_set.len); + rates->basic_mcs_set.max_len = CFG_BASIC_MCS_SET_LEN; + qdf_uint8_array_parse(cfg_default(CFG_BASIC_MCS_SET), + rates->basic_mcs_set.data, + sizeof(rates->basic_mcs_set.data), + &rates->basic_mcs_set.len); + rates->current_mcs_set.max_len = CFG_CURRENT_MCS_SET_LEN; + qdf_uint8_array_parse(cfg_default(CFG_CURRENT_MCS_SET), + rates->current_mcs_set.data, + sizeof(rates->current_mcs_set.data), + &rates->current_mcs_set.len); +} + +static void mlme_init_dfs_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_dfs_cfg *dfs_cfg) +{ + dfs_cfg->dfs_ignore_cac = cfg_get(psoc, CFG_IGNORE_CAC); + dfs_cfg->dfs_master_capable = + cfg_get(psoc, CFG_ENABLE_DFS_MASTER_CAPABILITY); + dfs_cfg->dfs_disable_channel_switch = + cfg_get(psoc, CFG_DISABLE_DFS_CH_SWITCH); + dfs_cfg->dfs_filter_offload = + cfg_get(psoc, CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD); + dfs_cfg->dfs_prefer_non_dfs = + cfg_get(psoc, CFG_ENABLE_NON_DFS_CHAN_ON_RADAR); + dfs_cfg->dfs_beacon_tx_enhanced = + cfg_get(psoc, CFG_DFS_BEACON_TX_ENHANCED); + dfs_cfg->dfs_disable_japan_w53 = + cfg_get(psoc, CFG_DISABLE_DFS_JAPAN_W53); + dfs_cfg->sap_tx_leakage_threshold = + cfg_get(psoc, CFG_SAP_TX_LEAKAGE_THRESHOLD); + dfs_cfg->dfs_pri_multiplier = + cfg_get(psoc, CFG_DFS_RADAR_PRI_MULTIPLIER); +} + +static void mlme_init_feature_flag_in_cfg( + struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_feature_flag *feature_flags) +{ + feature_flags->accept_short_slot_assoc = + cfg_default(CFG_ACCEPT_SHORT_SLOT_ASSOC_ONLY); + feature_flags->enable_hcf = cfg_default(CFG_HCF_ENABLED); + feature_flags->enable_rsn = cfg_default(CFG_RSN_ENABLED); + feature_flags->enable_short_preamble_11g = + cfg_default(CFG_11G_SHORT_PREAMBLE_ENABLED); + feature_flags->enable_short_slot_time_11g = + cfg_default(CFG_11G_SHORT_SLOT_TIME_ENABLED); + feature_flags->channel_bonding_mode = + cfg_default(CFG_CHANNEL_BONDING_MODE); + feature_flags->enable_block_ack = cfg_default(CFG_BLOCK_ACK_ENABLED); + feature_flags->enable_ampdu = cfg_get(psoc, CFG_ENABLE_AMPDUPS); + feature_flags->mcc_rts_cts_prot = cfg_get(psoc, + CFG_FW_MCC_RTS_CTS_PROT); + feature_flags->mcc_bcast_prob_rsp = cfg_get(psoc, + CFG_FW_MCC_BCAST_PROB_RESP); + feature_flags->enable_mcc = cfg_get(psoc, CFG_MCC_FEATURE); + feature_flags->channel_bonding_mode_24ghz = + cfg_get(psoc, CFG_CHANNEL_BONDING_MODE_24GHZ); + feature_flags->channel_bonding_mode_5ghz = + cfg_get(psoc, CFG_CHANNEL_BONDING_MODE_5GHZ); +} + +static void mlme_init_sap_protection_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_sap_protection + *sap_protection_params) +{ + sap_protection_params->protection_enabled = + cfg_default(CFG_PROTECTION_ENABLED); + sap_protection_params->protection_force_policy = + cfg_default(CFG_FORCE_POLICY_PROTECTION); + sap_protection_params->ignore_peer_ht_opmode = + cfg_get(psoc, CFG_IGNORE_PEER_HT_MODE); + sap_protection_params->enable_ap_obss_protection = + cfg_get(psoc, CFG_AP_OBSS_PROTECTION_ENABLE); + sap_protection_params->is_ap_prot_enabled = + cfg_get(psoc, CFG_AP_ENABLE_PROTECTION_MODE); + sap_protection_params->ap_protection_mode = + cfg_get(psoc, CFG_AP_PROTECTION_MODE); +} + +#ifdef WLAN_FEATURE_11AX +static void mlme_init_he_cap_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_cfg *mlme_cfg) +{ + uint32_t chan_width; + uint16_t value = 0; + struct wlan_mlme_he_caps *he_caps = &mlme_cfg->he_caps; + + he_caps->dot11_he_cap.htc_he = cfg_default(CFG_HE_CONTROL); + he_caps->dot11_he_cap.twt_request = cfg_default(CFG_HE_TWT_REQUESTOR); + he_caps->dot11_he_cap.twt_responder = cfg_default(CFG_HE_TWT_RESPONDER); + he_caps->dot11_he_cap.fragmentation = + cfg_default(CFG_HE_FRAGMENTATION); + he_caps->dot11_he_cap.max_num_frag_msdu_amsdu_exp = + cfg_default(CFG_HE_MAX_FRAG_MSDU); + he_caps->dot11_he_cap.min_frag_size = cfg_default(CFG_HE_MIN_FRAG_SIZE); + he_caps->dot11_he_cap.trigger_frm_mac_pad = + cfg_default(CFG_HE_TRIG_PAD); + he_caps->dot11_he_cap.multi_tid_aggr_rx_supp = + cfg_default(CFG_HE_MTID_AGGR_RX); + he_caps->dot11_he_cap.he_link_adaptation = + cfg_default(CFG_HE_LINK_ADAPTATION); + he_caps->dot11_he_cap.all_ack = cfg_default(CFG_HE_ALL_ACK); + he_caps->dot11_he_cap.trigd_rsp_sched = + cfg_default(CFG_HE_TRIGD_RSP_SCHEDULING); + he_caps->dot11_he_cap.a_bsr = cfg_default(CFG_HE_BUFFER_STATUS_RPT); + he_caps->dot11_he_cap.broadcast_twt = cfg_default(CFG_HE_BCAST_TWT); + he_caps->dot11_he_cap.ba_32bit_bitmap = cfg_default(CFG_HE_BA_32BIT); + he_caps->dot11_he_cap.mu_cascade = cfg_default(CFG_HE_MU_CASCADING); + he_caps->dot11_he_cap.ack_enabled_multitid = + cfg_default(CFG_HE_MULTI_TID); + he_caps->dot11_he_cap.omi_a_ctrl = cfg_default(CFG_HE_OMI); + he_caps->dot11_he_cap.ofdma_ra = cfg_default(CFG_HE_OFDMA_RA); + he_caps->dot11_he_cap.max_ampdu_len_exp_ext = + cfg_default(CFG_HE_MAX_AMPDU_LEN); + he_caps->dot11_he_cap.amsdu_frag = cfg_default(CFG_HE_AMSDU_FRAG); + he_caps->dot11_he_cap.flex_twt_sched = + cfg_default(CFG_HE_FLEX_TWT_SCHED); + he_caps->dot11_he_cap.rx_ctrl_frame = cfg_default(CFG_HE_RX_CTRL); + he_caps->dot11_he_cap.bsrp_ampdu_aggr = + cfg_default(CFG_HE_BSRP_AMPDU_AGGR); + he_caps->dot11_he_cap.qtp = cfg_default(CFG_HE_QTP); + he_caps->dot11_he_cap.a_bqr = cfg_default(CFG_HE_A_BQR); + he_caps->dot11_he_cap.spatial_reuse_param_rspder = + cfg_default(CFG_HE_SR_RESPONDER); + he_caps->dot11_he_cap.ndp_feedback_supp = + cfg_default(CFG_HE_NDP_FEEDBACK_SUPP); + he_caps->dot11_he_cap.ops_supp = cfg_default(CFG_HE_OPS_SUPP); + he_caps->dot11_he_cap.amsdu_in_ampdu = + cfg_default(CFG_HE_AMSDU_IN_AMPDU); + + chan_width = cfg_default(CFG_HE_CHAN_WIDTH); + he_caps->dot11_he_cap.chan_width_0 = HE_CH_WIDTH_GET_BIT(chan_width, 0); + he_caps->dot11_he_cap.chan_width_1 = HE_CH_WIDTH_GET_BIT(chan_width, 1); + he_caps->dot11_he_cap.chan_width_2 = HE_CH_WIDTH_GET_BIT(chan_width, 2); + he_caps->dot11_he_cap.chan_width_3 = HE_CH_WIDTH_GET_BIT(chan_width, 3); + he_caps->dot11_he_cap.chan_width_4 = HE_CH_WIDTH_GET_BIT(chan_width, 4); + he_caps->dot11_he_cap.chan_width_5 = HE_CH_WIDTH_GET_BIT(chan_width, 5); + he_caps->dot11_he_cap.chan_width_6 = HE_CH_WIDTH_GET_BIT(chan_width, 6); + + he_caps->dot11_he_cap.multi_tid_aggr_tx_supp = + cfg_default(CFG_HE_MTID_AGGR_TX); + he_caps->dot11_he_cap.he_sub_ch_sel_tx_supp = + cfg_default(CFG_HE_SUB_CH_SEL_TX); + he_caps->dot11_he_cap.ul_2x996_tone_ru_supp = + cfg_default(CFG_HE_UL_2X996_RU); + he_caps->dot11_he_cap.om_ctrl_ul_mu_data_dis_rx = + cfg_default(CFG_HE_OM_CTRL_UL_MU_DIS_RX); + he_caps->dot11_he_cap.he_dynamic_smps = + cfg_default(CFG_HE_DYNAMIC_SMPS); + he_caps->dot11_he_cap.punctured_sounding_supp = + cfg_default(CFG_HE_PUNCTURED_SOUNDING); + he_caps->dot11_he_cap.ht_vht_trg_frm_rx_supp = + cfg_default(CFG_HE_HT_VHT_TRG_FRM_RX); + he_caps->dot11_he_cap.rx_pream_puncturing = + cfg_default(CFG_HE_RX_PREAM_PUNC); + he_caps->dot11_he_cap.device_class = + cfg_default(CFG_HE_CLASS_OF_DEVICE); + he_caps->dot11_he_cap.ldpc_coding = cfg_default(CFG_HE_LDPC); + he_caps->dot11_he_cap.he_1x_ltf_800_gi_ppdu = + cfg_default(CFG_HE_LTF_PPDU); + he_caps->dot11_he_cap.midamble_tx_rx_max_nsts = + cfg_default(CFG_HE_MIDAMBLE_RX_MAX_NSTS); + he_caps->dot11_he_cap.he_4x_ltf_3200_gi_ndp = + cfg_default(CFG_HE_LTF_NDP); + he_caps->dot11_he_cap.tb_ppdu_tx_stbc_lt_80mhz = + cfg_default(CFG_HE_TX_STBC_LT80); + he_caps->dot11_he_cap.rx_stbc_lt_80mhz = + cfg_default(CFG_HE_RX_STBC_LT80); + he_caps->dot11_he_cap.doppler = cfg_default(CFG_HE_DOPPLER); + he_caps->dot11_he_cap.ul_mu = + cfg_get(psoc, CFG_HE_UL_MUMIMO); + he_caps->dot11_he_cap.dcm_enc_tx = cfg_default(CFG_HE_DCM_TX); + he_caps->dot11_he_cap.dcm_enc_rx = cfg_default(CFG_HE_DCM_RX); + he_caps->dot11_he_cap.ul_he_mu = cfg_default(CFG_HE_MU_PPDU); + he_caps->dot11_he_cap.su_beamformer = cfg_default(CFG_HE_SU_BEAMFORMER); + he_caps->dot11_he_cap.su_beamformee = cfg_default(CFG_HE_SU_BEAMFORMEE); + he_caps->dot11_he_cap.mu_beamformer = cfg_default(CFG_HE_MU_BEAMFORMER); + he_caps->dot11_he_cap.bfee_sts_lt_80 = + cfg_default(CFG_HE_BFEE_STS_LT80); + he_caps->dot11_he_cap.bfee_sts_gt_80 = + cfg_default(CFG_HE_BFEE_STS_GT80); + he_caps->dot11_he_cap.num_sounding_lt_80 = + cfg_default(CFG_HE_NUM_SOUND_LT80); + he_caps->dot11_he_cap.num_sounding_gt_80 = + cfg_default(CFG_HE_NUM_SOUND_GT80); + he_caps->dot11_he_cap.su_feedback_tone16 = + cfg_default(CFG_HE_SU_FEED_TONE16); + he_caps->dot11_he_cap.mu_feedback_tone16 = + cfg_default(CFG_HE_MU_FEED_TONE16); + he_caps->dot11_he_cap.codebook_su = cfg_default(CFG_HE_CODEBOOK_SU); + he_caps->dot11_he_cap.codebook_mu = cfg_default(CFG_HE_CODEBOOK_MU); + he_caps->dot11_he_cap.beamforming_feedback = + cfg_default(CFG_HE_BFRM_FEED); + he_caps->dot11_he_cap.he_er_su_ppdu = cfg_default(CFG_HE_ER_SU_PPDU); + he_caps->dot11_he_cap.dl_mu_mimo_part_bw = + cfg_default(CFG_HE_DL_PART_BW); + he_caps->dot11_he_cap.ppet_present = cfg_default(CFG_HE_PPET_PRESENT); + he_caps->dot11_he_cap.srp = cfg_default(CFG_HE_SRP); + he_caps->dot11_he_cap.power_boost = cfg_default(CFG_HE_POWER_BOOST); + he_caps->dot11_he_cap.he_ltf_800_gi_4x = cfg_default(CFG_HE_4x_LTF_GI); + he_caps->dot11_he_cap.max_nc = cfg_default(CFG_HE_MAX_NC); + he_caps->dot11_he_cap.tb_ppdu_tx_stbc_gt_80mhz = + cfg_default(CFG_HE_TX_STBC_GT80); + he_caps->dot11_he_cap.rx_stbc_gt_80mhz = + cfg_default(CFG_HE_RX_STBC_GT80); + he_caps->dot11_he_cap.er_he_ltf_800_gi_4x = + cfg_default(CFG_HE_ER_4x_LTF_GI); + he_caps->dot11_he_cap.he_ppdu_20_in_40Mhz_2G = + cfg_default(CFG_HE_PPDU_20_IN_40MHZ_2G); + he_caps->dot11_he_cap.he_ppdu_20_in_160_80p80Mhz = + cfg_default(CFG_HE_PPDU_20_IN_160_80P80MHZ); + he_caps->dot11_he_cap.he_ppdu_80_in_160_80p80Mhz = + cfg_default(CFG_HE_PPDU_80_IN_160_80P80MHZ); + he_caps->dot11_he_cap.er_1x_he_ltf_gi = + cfg_default(CFG_HE_ER_1X_HE_LTF_GI); + he_caps->dot11_he_cap.midamble_tx_rx_1x_he_ltf = + cfg_default(CFG_HE_MIDAMBLE_TXRX_1X_HE_LTF); + he_caps->dot11_he_cap.dcm_max_bw = cfg_default(CFG_HE_DCM_MAX_BW); + he_caps->dot11_he_cap.longer_than_16_he_sigb_ofdm_sym = + cfg_default(CFG_HE_LONGER_16_SIGB_OFDM_SYM); + he_caps->dot11_he_cap.non_trig_cqi_feedback = + cfg_default(CFG_HE_NON_TRIG_CQI_FEEDBACK); + he_caps->dot11_he_cap.tx_1024_qam_lt_242_tone_ru = + cfg_default(CFG_HE_TX_1024_QAM_LT_242_RU); + he_caps->dot11_he_cap.rx_1024_qam_lt_242_tone_ru = + cfg_default(CFG_HE_RX_1024_QAM_LT_242_RU); + he_caps->dot11_he_cap.rx_full_bw_su_he_mu_compress_sigb = + cfg_default(CFG_HE_RX_FULL_BW_MU_CMPR_SIGB); + he_caps->dot11_he_cap.rx_full_bw_su_he_mu_non_cmpr_sigb = + cfg_default(CFG_HE_RX_FULL_BW_MU_NON_CMPR_SIGB); + he_caps->dot11_he_cap.rx_he_mcs_map_lt_80 = + cfg_get(psoc, CFG_HE_RX_MCS_MAP_LT_80); + he_caps->dot11_he_cap.tx_he_mcs_map_lt_80 = + cfg_get(psoc, CFG_HE_TX_MCS_MAP_LT_80); + value = cfg_get(psoc, CFG_HE_RX_MCS_MAP_160); + qdf_mem_copy(he_caps->dot11_he_cap.rx_he_mcs_map_160, &value, + sizeof(uint16_t)); + value = cfg_get(psoc, CFG_HE_TX_MCS_MAP_160); + qdf_mem_copy(he_caps->dot11_he_cap.tx_he_mcs_map_160, &value, + sizeof(uint16_t)); + value = cfg_default(CFG_HE_RX_MCS_MAP_80_80); + qdf_mem_copy(he_caps->dot11_he_cap.rx_he_mcs_map_80_80, &value, + sizeof(uint16_t)); + value = cfg_default(CFG_HE_TX_MCS_MAP_80_80); + qdf_mem_copy(he_caps->dot11_he_cap.tx_he_mcs_map_80_80, &value, + sizeof(uint16_t)); + he_caps->he_ops_basic_mcs_nss = cfg_default(CFG_HE_OPS_BASIC_MCS_NSS); + he_caps->he_dynamic_fragmentation = + cfg_get(psoc, CFG_HE_DYNAMIC_FRAGMENTATION); + he_caps->enable_ul_mimo = + cfg_get(psoc, CFG_ENABLE_UL_MIMO); + he_caps->enable_ul_ofdm = + cfg_get(psoc, CFG_ENABLE_UL_OFDMA); + he_caps->he_sta_obsspd = + cfg_get(psoc, CFG_HE_STA_OBSSPD); + qdf_mem_zero(he_caps->he_ppet_2g, MLME_HE_PPET_LEN); + qdf_mem_zero(he_caps->he_ppet_5g, MLME_HE_PPET_LEN); +} +#else +static void mlme_init_he_cap_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_cfg *mlme_cfg) +{ +} +#endif + +static void mlme_init_twt_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_cfg_twt *twt_cfg) +{ + twt_cfg->is_twt_bcast_enabled = cfg_get(psoc, CFG_BCAST_TWT); + twt_cfg->is_twt_enabled = cfg_get(psoc, CFG_ENABLE_TWT); + twt_cfg->is_twt_responder_enabled = cfg_get(psoc, CFG_TWT_RESPONDER); + twt_cfg->is_twt_requestor_enabled = cfg_get(psoc, CFG_TWT_REQUESTOR); + twt_cfg->twt_congestion_timeout = cfg_get(psoc, CFG_TWT_CONGESTION_TIMEOUT); +} + +#ifdef WLAN_FEATURE_SAE +static bool is_sae_sap_enabled(struct wlan_objmgr_psoc *psoc) +{ + return cfg_get(psoc, CFG_IS_SAP_SAE_ENABLED); +} +#else +static bool is_sae_sap_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} +#endif + +static void mlme_init_sap_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_cfg_sap *sap_cfg) +{ + uint8_t *ssid; + + ssid = cfg_default(CFG_SSID); + qdf_mem_zero(sap_cfg->cfg_ssid, WLAN_SSID_MAX_LEN); + sap_cfg->cfg_ssid_len = STR_SSID_DEFAULT_LEN; + qdf_mem_copy(sap_cfg->cfg_ssid, ssid, STR_SSID_DEFAULT_LEN); + sap_cfg->beacon_interval = cfg_get(psoc, CFG_BEACON_INTERVAL); + sap_cfg->dtim_interval = cfg_default(CFG_DTIM_PERIOD); + sap_cfg->listen_interval = cfg_default(CFG_LISTEN_INTERVAL); + sap_cfg->sap_11g_policy = cfg_default(CFG_11G_ONLY_POLICY); + sap_cfg->assoc_sta_limit = cfg_default(CFG_ASSOC_STA_LIMIT); + sap_cfg->enable_lte_coex = cfg_get(psoc, CFG_ENABLE_LTE_COEX); + sap_cfg->rmc_action_period_freq = + cfg_default(CFG_RMC_ACTION_PERIOD_FREQUENCY); + sap_cfg->rate_tx_mgmt = cfg_get(psoc, CFG_RATE_FOR_TX_MGMT); + sap_cfg->rate_tx_mgmt_2g = cfg_get(psoc, CFG_RATE_FOR_TX_MGMT_2G); + sap_cfg->rate_tx_mgmt_5g = cfg_get(psoc, CFG_RATE_FOR_TX_MGMT_5G); + sap_cfg->tele_bcn_wakeup_en = cfg_get(psoc, CFG_TELE_BCN_WAKEUP_EN); + sap_cfg->tele_bcn_max_li = cfg_get(psoc, CFG_TELE_BCN_MAX_LI); + sap_cfg->sap_get_peer_info = cfg_get(psoc, CFG_SAP_GET_PEER_INFO); + sap_cfg->sap_allow_all_chan_param_name = + cfg_get(psoc, CFG_SAP_ALLOW_ALL_CHANNEL_PARAM); + sap_cfg->sap_max_no_peers = cfg_get(psoc, CFG_SAP_MAX_NO_PEERS); + sap_cfg->sap_max_offload_peers = + cfg_get(psoc, CFG_SAP_MAX_OFFLOAD_PEERS); + sap_cfg->sap_max_offload_reorder_buffs = + cfg_get(psoc, CFG_SAP_MAX_OFFLOAD_REORDER_BUFFS); + sap_cfg->sap_ch_switch_beacon_cnt = + cfg_get(psoc, CFG_SAP_CH_SWITCH_BEACON_CNT); + sap_cfg->sap_ch_switch_mode = cfg_get(psoc, CFG_SAP_CH_SWITCH_MODE); + sap_cfg->sap_internal_restart = + cfg_get(psoc, CFG_SAP_INTERNAL_RESTART); + sap_cfg->chan_switch_hostapd_rate_enabled_name = + cfg_get(psoc, CFG_CHAN_SWITCH_HOSTAPD_RATE_ENABLED_NAME); + sap_cfg->reduced_beacon_interval = + cfg_get(psoc, CFG_REDUCED_BEACON_INTERVAL); + sap_cfg->max_li_modulated_dtim_time = + cfg_get(psoc, CFG_MAX_LI_MODULATED_DTIM); + sap_cfg->country_code_priority = + cfg_get(psoc, CFG_COUNTRY_CODE_PRIORITY); + sap_cfg->sap_pref_chan_location = + cfg_get(psoc, CFG_SAP_PREF_CHANNEL_LOCATION); + sap_cfg->sap_force_11n_for_11ac = + cfg_get(psoc, CFG_SAP_FORCE_11N_FOR_11AC); + sap_cfg->go_force_11n_for_11ac = + cfg_get(psoc, CFG_GO_FORCE_11N_FOR_11AC); + sap_cfg->ap_random_bssid_enable = + cfg_get(psoc, CFG_AP_ENABLE_RANDOM_BSSID); + sap_cfg->sap_mcc_chnl_avoid = + cfg_get(psoc, CFG_SAP_MCC_CHANNEL_AVOIDANCE); + sap_cfg->sap_11ac_override = + cfg_get(psoc, CFG_SAP_11AC_OVERRIDE); + sap_cfg->go_11ac_override = + cfg_get(psoc, CFG_GO_11AC_OVERRIDE); + sap_cfg->sap_sae_enabled = is_sae_sap_enabled(psoc); + sap_cfg->is_sap_bcast_deauth_enabled = + cfg_get(psoc, CFG_IS_SAP_BCAST_DEAUTH_ENABLED); + sap_cfg->is_6g_sap_fd_enabled = + cfg_get(psoc, CFG_6G_SAP_FILS_DISCOVERY_ENABLED); +} + +static void mlme_init_obss_ht40_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_obss_ht40 *obss_ht40) +{ + obss_ht40->active_dwelltime = + cfg_get(psoc, CFG_OBSS_HT40_SCAN_ACTIVE_DWELL_TIME); + obss_ht40->passive_dwelltime = + cfg_get(psoc, CFG_OBSS_HT40_SCAN_PASSIVE_DWELL_TIME); + obss_ht40->width_trigger_interval = + cfg_get(psoc, CFG_OBSS_HT40_SCAN_WIDTH_TRIGGER_INTERVAL); + obss_ht40->passive_per_channel = (uint32_t) + cfg_default(CFG_OBSS_HT40_SCAN_PASSIVE_TOTAL_PER_CHANNEL); + obss_ht40->active_per_channel = (uint32_t) + cfg_default(CFG_OBSS_HT40_SCAN_ACTIVE_TOTAL_PER_CHANNEL); + obss_ht40->width_trans_delay = (uint32_t) + cfg_default(CFG_OBSS_HT40_WIDTH_CH_TRANSITION_DELAY); + obss_ht40->scan_activity_threshold = (uint32_t) + cfg_default(CFG_OBSS_HT40_SCAN_ACTIVITY_THRESHOLD); + obss_ht40->is_override_ht20_40_24g = + cfg_get(psoc, CFG_OBSS_HT40_OVERRIDE_HT40_20_24GHZ); + obss_ht40->obss_detection_offload_enabled = + (bool)cfg_default(CFG_OBSS_DETECTION_OFFLOAD); + obss_ht40->obss_color_collision_offload_enabled = + (bool)cfg_default(CFG_OBSS_COLOR_COLLISION_OFFLOAD); + obss_ht40->bss_color_collision_det_sta = + cfg_get(psoc, CFG_BSS_CLR_COLLISION_DETCN_STA); +} + +static void mlme_init_threshold_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_threshold *threshold) +{ + threshold->rts_threshold = cfg_get(psoc, CFG_RTS_THRESHOLD); + threshold->frag_threshold = cfg_get(psoc, CFG_FRAG_THRESHOLD); +} + +static bool +mlme_is_freq_present_in_list(struct acs_weight *normalize_weight_chan_list, + uint8_t num_freq, uint32_t freq, uint8_t *index) +{ + uint8_t i; + + for (i = 0; i < num_freq && i < NUM_CHANNELS; i++) { + if (normalize_weight_chan_list[i].chan_freq == freq) { + *index = i; + return true; + } + } + + return false; +} + +static void +mlme_acs_parse_weight_list(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_acs *acs) +{ + char *acs_weight, *str1, *str2 = NULL, *acs_weight_temp, is_range = '-'; + int freq1, freq2, normalize_factor; + uint8_t num_acs_weight = 0, num_acs_weight_range = 0, index = 0; + struct acs_weight *weight_list = acs->normalize_weight_chan; + struct acs_weight_range *range_list = acs->normalize_weight_range; + + if (!qdf_str_len(cfg_get(psoc, CFG_NORMALIZE_ACS_WEIGHT))) + return; + + acs_weight = qdf_mem_malloc(ACS_WEIGHT_MAX_STR_LEN); + + if (!acs_weight) + return; + + qdf_mem_copy(acs_weight, cfg_get(psoc, CFG_NORMALIZE_ACS_WEIGHT), + ACS_WEIGHT_MAX_STR_LEN); + acs_weight_temp = acs_weight; + + while(acs_weight_temp) { + str1 = strsep(&acs_weight_temp, ","); + if (!str1) + goto end; + freq1 = 0; + freq2 = 0; + if (strchr(str1, is_range)) { + str2 = strsep(&str1, "-"); + sscanf(str2, "%d", &freq1); + sscanf(str1, "%d", &freq2); + strsep(&str1, "="); + if (!str1) + goto end; + sscanf(str1, "%d", &normalize_factor); + + if (num_acs_weight_range == MAX_ACS_WEIGHT_RANGE) + continue; + range_list[num_acs_weight_range].normalize_weight = + normalize_factor; + range_list[num_acs_weight_range].start_freq = freq1; + range_list[num_acs_weight_range++].end_freq = freq2; + } else { + sscanf(str1, "%d", &freq1); + strsep(&str1, "="); + if (!str1 || !weight_list) + goto end; + sscanf(str1, "%d", &normalize_factor); + if (mlme_is_freq_present_in_list(weight_list, + num_acs_weight, freq1, + &index)) { + weight_list[index].normalize_weight = + normalize_factor; + } else { + if (num_acs_weight == NUM_CHANNELS) + continue; + + weight_list[num_acs_weight].chan_freq = freq1; + weight_list[num_acs_weight++].normalize_weight = + normalize_factor; + } + } + } + + acs->normalize_weight_num_chan = num_acs_weight; + acs->num_weight_range = num_acs_weight_range; + +end: + qdf_mem_free(acs_weight); +} + +static void mlme_init_acs_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_acs *acs) +{ + acs->is_acs_with_more_param = + cfg_get(psoc, CFG_ACS_WITH_MORE_PARAM); + acs->auto_channel_select_weight = + cfg_get(psoc, CFG_AUTO_CHANNEL_SELECT_WEIGHT); + acs->is_vendor_acs_support = + cfg_get(psoc, CFG_USER_AUTO_CHANNEL_SELECTION); + acs->is_acs_support_for_dfs_ltecoex = + cfg_get(psoc, CFG_USER_ACS_DFS_LTE); + acs->is_external_acs_policy = + cfg_get(psoc, CFG_EXTERNAL_ACS_POLICY); + acs->force_sap_start = + cfg_get(psoc, CFG_ACS_FORCE_START_SAP); + acs->np_chan_weightage = cfg_get(psoc, CFG_ACS_NP_CHAN_WEIGHT); + mlme_acs_parse_weight_list(psoc, acs); +} + +QDF_STATUS mlme_init_ibss_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_ibss_cfg *ibss_cfg) +{ + if (!ibss_cfg) + return QDF_STATUS_E_FAILURE; + + ibss_cfg->auto_bssid = cfg_default(CFG_IBSS_AUTO_BSSID); + ibss_cfg->atim_win_size = cfg_get(psoc, CFG_IBSS_ATIM_WIN_SIZE); + ibss_cfg->adhoc_ch_5g = cfg_get(psoc, CFG_IBSS_ADHOC_CHANNEL_5GHZ); + ibss_cfg->adhoc_ch_2g = cfg_get(psoc, CFG_IBSS_ADHOC_CHANNEL_24GHZ); + ibss_cfg->coalesing_enable = cfg_get(psoc, CFG_IBSS_COALESING); + ibss_cfg->power_save_allow = cfg_get(psoc, + CFG_IBSS_IS_POWER_SAVE_ALLOWED); + ibss_cfg->power_collapse_allow = + cfg_get(psoc, CFG_IBSS_IS_POWER_COLLAPSE_ALLOWED); + ibss_cfg->awake_on_tx_rx = cfg_get(psoc, CFG_IBSS_AWAKE_ON_TX_RX); + ibss_cfg->inactivity_bcon_count = + cfg_get(psoc, CFG_IBSS_INACTIVITY_TIME); + ibss_cfg->txsp_end_timeout = + cfg_get(psoc, CFG_IBSS_TXSP_END_INACTIVITY); + ibss_cfg->ps_warm_up_time = cfg_get(psoc, CFG_IBSS_PS_WARMUP_TIME); + ibss_cfg->ps_1rx_chain_atim_win = + cfg_get(psoc, CFG_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW); + qdf_copy_macaddr(&ibss_cfg->bssid, (struct qdf_mac_addr *) + &cfg_get(psoc, CFG_IBSS_BSSID)); + + return QDF_STATUS_SUCCESS; +} + +static void +mlme_init_product_details_cfg(struct wlan_mlme_product_details_cfg + *product_details) +{ + qdf_str_lcopy(product_details->manufacturer_name, + cfg_default(CFG_MFR_NAME), + sizeof(product_details->manufacturer_name)); + qdf_str_lcopy(product_details->manufacture_product_name, + cfg_default(CFG_MFR_PRODUCT_NAME), + sizeof(product_details->manufacture_product_name)); + qdf_str_lcopy(product_details->manufacture_product_version, + cfg_default(CFG_MFR_PRODUCT_VERSION), + sizeof(product_details->manufacture_product_version)); + qdf_str_lcopy(product_details->model_name, + cfg_default(CFG_MODEL_NAME), + sizeof(product_details->model_name)); + qdf_str_lcopy(product_details->model_number, + cfg_default(CFG_MODEL_NUMBER), + sizeof(product_details->model_number)); +} + +static void mlme_init_sta_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_sta_cfg *sta) +{ + sta->sta_keep_alive_period = + cfg_get(psoc, CFG_INFRA_STA_KEEP_ALIVE_PERIOD); + sta->tgt_gtx_usr_cfg = + cfg_get(psoc, CFG_TGT_GTX_USR_CFG); + sta->pmkid_modes = + cfg_get(psoc, CFG_PMKID_MODES); + sta->ignore_peer_erp_info = + cfg_get(psoc, CFG_IGNORE_PEER_ERP_INFO); + sta->sta_prefer_80mhz_over_160mhz = + cfg_get(psoc, CFG_STA_PREFER_80MHZ_OVER_160MHZ); + sta->enable_5g_ebt = + cfg_get(psoc, CFG_PPS_ENABLE_5G_EBT); + sta->deauth_before_connection = + cfg_get(psoc, CFG_ENABLE_DEAUTH_BEFORE_CONNECTION); + sta->dot11p_mode = + cfg_get(psoc, CFG_DOT11P_MODE); + sta->enable_go_cts2self_for_sta = + cfg_get(psoc, CFG_ENABLE_GO_CTS2SELF_FOR_STA); + sta->qcn_ie_support = + cfg_get(psoc, CFG_QCN_IE_SUPPORT); + sta->fils_max_chan_guard_time = + cfg_get(psoc, CFG_FILS_MAX_CHAN_GUARD_TIME); + sta->deauth_retry_cnt = cfg_get(psoc, CFG_DEAUTH_RETRY_CNT); + sta->single_tid = + cfg_get(psoc, CFG_SINGLE_TID_RC); + sta->sta_miracast_mcc_rest_time = + cfg_get(psoc, CFG_STA_MCAST_MCC_REST_TIME); + sta->wait_cnf_timeout = + (uint32_t)cfg_default(CFG_WT_CNF_TIMEOUT); + sta->current_rssi = + (uint32_t)cfg_default(CFG_CURRENT_RSSI); + sta->allow_tpc_from_ap = cfg_get(psoc, CFG_TX_POWER_CTRL); + sta->sta_keepalive_method = + cfg_get(psoc, CFG_STA_KEEPALIVE_METHOD); +} + +static void mlme_init_stats_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_stats_cfg *stats) +{ + stats->stats_periodic_display_time = + cfg_get(psoc, CFG_PERIODIC_STATS_DISPLAY_TIME); + stats->stats_link_speed_rssi_high = + cfg_get(psoc, CFG_LINK_SPEED_RSSI_HIGH); + stats->stats_link_speed_rssi_med = + cfg_get(psoc, CFG_LINK_SPEED_RSSI_MID); + stats->stats_link_speed_rssi_low = + cfg_get(psoc, CFG_LINK_SPEED_RSSI_LOW); + stats->stats_report_max_link_speed_rssi = + cfg_get(psoc, CFG_REPORT_MAX_LINK_SPEED); +} + +#ifdef WLAN_ADAPTIVE_11R +/** + * mlme_init_adaptive_11r_cfg() - initialize enable_adaptive_11r + * flag + * @psoc: Pointer to PSOC + * @lfr: pointer to mlme lfr config + * + * Return: None + */ +static void +mlme_init_adaptive_11r_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ + lfr->enable_adaptive_11r = cfg_get(psoc, CFG_ADAPTIVE_11R); +} + +#else +static inline void +mlme_init_adaptive_11r_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ +} +#endif + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * mlme_init_sae_single_pmk_cfg() - initialize sae_same_pmk_config + * flag + * @psoc: Pointer to PSOC + * @lfr: pointer to mlme lfr config + * + * Return: None + */ +static void +mlme_init_sae_single_pmk_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ + lfr->sae_single_pmk_feature_enabled = cfg_get(psoc, CFG_SAE_SINGLE_PMK); +} + +#else +static inline void +mlme_init_sae_single_pmk_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static void mlme_init_roam_offload_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ + lfr->lfr3_roaming_offload = + cfg_get(psoc, CFG_LFR3_ROAMING_OFFLOAD); + lfr->enable_self_bss_roam = cfg_get(psoc, CFG_LFR3_ENABLE_SELF_BSS_ROAM); + lfr->enable_roam_reason_vsie = + cfg_get(psoc, CFG_ENABLE_ROAM_REASON_VSIE); + lfr->enable_disconnect_roam_offload = + cfg_get(psoc, CFG_LFR_ENABLE_DISCONNECT_ROAM); + lfr->enable_idle_roam = + cfg_get(psoc, CFG_LFR_ENABLE_IDLE_ROAM); + lfr->idle_roam_rssi_delta = + cfg_get(psoc, CFG_LFR_IDLE_ROAM_RSSI_DELTA); + lfr->idle_roam_inactive_time = + cfg_get(psoc, CFG_LFR_IDLE_ROAM_INACTIVE_TIME); + lfr->idle_data_packet_count = + cfg_get(psoc, CFG_LFR_IDLE_ROAM_PACKET_COUNT); + lfr->idle_roam_min_rssi = cfg_get(psoc, CFG_LFR_IDLE_ROAM_MIN_RSSI); + lfr->roam_trigger_bitmap = + cfg_get(psoc, CFG_ROAM_TRIGGER_BITMAP); + lfr->idle_roam_band = cfg_get(psoc, CFG_LFR_IDLE_ROAM_BAND); + lfr->sta_roam_disable = cfg_get(psoc, CFG_STA_DISABLE_ROAM); + mlme_init_sae_single_pmk_cfg(psoc, lfr); +} + +#else +static void mlme_init_roam_offload_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ +} + +#endif + +#ifdef FEATURE_WLAN_ESE +static void mlme_init_ese_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ + lfr->ese_enabled = cfg_get(psoc, CFG_LFR_ESE_FEATURE_ENABLED); +} +#else +static void mlme_init_ese_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ +} +#endif + +#ifdef FEATURE_LFR_SUBNET_DETECTION +static void mlme_init_subnet_detection(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ + lfr->enable_lfr_subnet_detection = + cfg_get(psoc, CFG_LFR3_ENABLE_SUBNET_DETECTION); +} +#else +static void mlme_init_subnet_detection(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ +} +#endif + +static void +mlme_init_bss_load_trigger_params(struct wlan_objmgr_psoc *psoc, + struct bss_load_trigger *bss_load_trig) +{ + bss_load_trig->enabled = + cfg_get(psoc, CFG_ENABLE_BSS_LOAD_TRIGGERED_ROAM); + bss_load_trig->threshold = cfg_get(psoc, CFG_BSS_LOAD_THRESHOLD); + bss_load_trig->sample_time = cfg_get(psoc, CFG_BSS_LOAD_SAMPLE_TIME); + bss_load_trig->rssi_threshold_5ghz = + cfg_get(psoc, CFG_BSS_LOAD_TRIG_5G_RSSI_THRES); + bss_load_trig->rssi_threshold_24ghz = + cfg_get(psoc, CFG_BSS_LOAD_TRIG_2G_RSSI_THRES); +} + +void mlme_reinit_control_config_lfr_params(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ + /* Restore the params set through SETDFSSCANMODE */ + lfr->roaming_dfs_channel = + cfg_get(psoc, CFG_LFR_ROAMING_DFS_CHANNEL); + + /* Restore the params set through SETWESMODE */ + lfr->wes_mode_enabled = cfg_get(psoc, CFG_LFR_ENABLE_WES_MODE); +} + +static void mlme_init_lfr_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_lfr_cfg *lfr) +{ + qdf_size_t neighbor_scan_chan_list_num = 0; + + lfr->mawc_roam_enabled = + cfg_get(psoc, CFG_LFR_MAWC_ROAM_ENABLED); + lfr->enable_fast_roam_in_concurrency = + cfg_get(psoc, CFG_LFR_ENABLE_FAST_ROAM_IN_CONCURRENCY); + lfr->early_stop_scan_enable = + cfg_get(psoc, CFG_LFR_EARLY_STOP_SCAN_ENABLE); + lfr->enable_5g_band_pref = + cfg_get(psoc, CFG_LFR_ENABLE_5G_BAND_PREF); + lfr->lfr_enabled = cfg_get(psoc, CFG_LFR_FEATURE_ENABLED); + lfr->mawc_enabled = cfg_get(psoc, CFG_LFR_MAWC_FEATURE_ENABLED); + lfr->fast_transition_enabled = + cfg_get(psoc, CFG_LFR_FAST_TRANSITION_ENABLED); + lfr->wes_mode_enabled = cfg_get(psoc, CFG_LFR_ENABLE_WES_MODE); + lfr->mawc_roam_traffic_threshold = + cfg_get(psoc, CFG_LFR_MAWC_ROAM_TRAFFIC_THRESHOLD); + lfr->mawc_roam_ap_rssi_threshold = + cfg_get(psoc, CFG_LFR_MAWC_ROAM_AP_RSSI_THRESHOLD); + lfr->mawc_roam_rssi_high_adjust = + cfg_get(psoc, CFG_LFR_MAWC_ROAM_RSSI_HIGH_ADJUST); + lfr->mawc_roam_rssi_low_adjust = + cfg_get(psoc, CFG_LFR_MAWC_ROAM_RSSI_LOW_ADJUST); + lfr->roam_rssi_abs_threshold = + cfg_get(psoc, CFG_LFR_ROAM_RSSI_ABS_THRESHOLD); + lfr->rssi_threshold_offset_5g = + cfg_get(psoc, CFG_LFR_5G_RSSI_THRESHOLD_OFFSET); + lfr->early_stop_scan_min_threshold = + cfg_get(psoc, CFG_LFR_EARLY_STOP_SCAN_MIN_THRESHOLD); + lfr->early_stop_scan_max_threshold = + cfg_get(psoc, CFG_LFR_EARLY_STOP_SCAN_MAX_THRESHOLD); + lfr->first_scan_bucket_threshold = + cfg_get(psoc, CFG_LFR_FIRST_SCAN_BUCKET_THRESHOLD); + lfr->roam_dense_traffic_threshold = + cfg_get(psoc, CFG_LFR_ROAM_DENSE_TRAFFIC_THRESHOLD); + lfr->roam_dense_rssi_thre_offset = + cfg_get(psoc, CFG_LFR_ROAM_DENSE_RSSI_THRE_OFFSET); + lfr->roam_dense_min_aps = + cfg_get(psoc, CFG_LFR_ROAM_DENSE_MIN_APS); + lfr->roam_bg_scan_bad_rssi_threshold = + cfg_get(psoc, CFG_LFR_ROAM_BG_SCAN_BAD_RSSI_THRESHOLD); + lfr->roam_bg_scan_client_bitmap = + cfg_get(psoc, CFG_LFR_ROAM_BG_SCAN_CLIENT_BITMAP); + lfr->roam_bg_scan_bad_rssi_offset_2g = + cfg_get(psoc, CFG_LFR_ROAM_BG_SCAN_BAD_RSSI_OFFSET_2G); + lfr->roam_data_rssi_threshold_triggers = + cfg_get(psoc, CFG_ROAM_DATA_RSSI_THRESHOLD_TRIGGERS); + lfr->roam_data_rssi_threshold = + cfg_get(psoc, CFG_ROAM_DATA_RSSI_THRESHOLD); + lfr->rx_data_inactivity_time = + cfg_get(psoc, CFG_RX_DATA_INACTIVITY_TIME); + lfr->adaptive_roamscan_dwell_mode = + cfg_get(psoc, CFG_LFR_ADAPTIVE_ROAMSCAN_DWELL_MODE); + lfr->per_roam_enable = + cfg_get(psoc, CFG_LFR_PER_ROAM_ENABLE); + lfr->per_roam_config_high_rate_th = + cfg_get(psoc, CFG_LFR_PER_ROAM_CONFIG_HIGH_RATE_TH); + lfr->per_roam_config_low_rate_th = + cfg_get(psoc, CFG_LFR_PER_ROAM_CONFIG_LOW_RATE_TH); + lfr->per_roam_config_rate_th_percent = + cfg_get(psoc, CFG_LFR_PER_ROAM_CONFIG_RATE_TH_PERCENT); + lfr->per_roam_rest_time = + cfg_get(psoc, CFG_LFR_PER_ROAM_REST_TIME); + lfr->per_roam_monitor_time = + cfg_get(psoc, CFG_LFR_PER_ROAM_MONITOR_TIME); + lfr->per_roam_min_candidate_rssi = + cfg_get(psoc, CFG_LFR_PER_ROAM_MIN_CANDIDATE_RSSI); + lfr->lfr3_disallow_duration = + cfg_get(psoc, CFG_LFR3_ROAM_DISALLOW_DURATION); + lfr->lfr3_rssi_channel_penalization = + cfg_get(psoc, CFG_LFR3_ROAM_RSSI_CHANNEL_PENALIZATION); + lfr->lfr3_num_disallowed_aps = + cfg_get(psoc, CFG_LFR3_ROAM_NUM_DISALLOWED_APS); + + if (lfr->enable_5g_band_pref) { + lfr->rssi_boost_threshold_5g = + cfg_get(psoc, CFG_LFR_5G_RSSI_BOOST_THRESHOLD); + lfr->rssi_boost_factor_5g = + cfg_get(psoc, CFG_LFR_5G_RSSI_BOOST_FACTOR); + lfr->max_rssi_boost_5g = + cfg_get(psoc, CFG_LFR_5G_MAX_RSSI_BOOST); + lfr->rssi_penalize_threshold_5g = + cfg_get(psoc, CFG_LFR_5G_RSSI_PENALIZE_THRESHOLD); + lfr->rssi_penalize_factor_5g = + cfg_get(psoc, CFG_LFR_5G_RSSI_PENALIZE_FACTOR); + lfr->max_rssi_penalize_5g = + cfg_get(psoc, CFG_LFR_5G_MAX_RSSI_PENALIZE); + } + + lfr->max_num_pre_auth = (uint32_t) + cfg_default(CFG_LFR_MAX_NUM_PRE_AUTH); + lfr->roam_preauth_no_ack_timeout = + cfg_get(psoc, CFG_LFR3_ROAM_PREAUTH_NO_ACK_TIMEOUT); + lfr->roam_preauth_retry_count = + cfg_get(psoc, CFG_LFR3_ROAM_PREAUTH_RETRY_COUNT); + lfr->roam_rssi_diff = cfg_get(psoc, CFG_LFR_ROAM_RSSI_DIFF); + lfr->roam_scan_offload_enabled = + cfg_get(psoc, CFG_LFR_ROAM_SCAN_OFFLOAD_ENABLED); + lfr->neighbor_scan_timer_period = + cfg_get(psoc, CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD); + lfr->neighbor_scan_min_timer_period = + cfg_get(psoc, CFG_LFR_NEIGHBOR_SCAN_MIN_TIMER_PERIOD); + lfr->neighbor_lookup_rssi_threshold = + cfg_get(psoc, CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD); + lfr->opportunistic_scan_threshold_diff = + cfg_get(psoc, CFG_LFR_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF); + lfr->roam_rescan_rssi_diff = + cfg_get(psoc, CFG_LFR_ROAM_RESCAN_RSSI_DIFF); + lfr->neighbor_scan_min_chan_time = + cfg_get(psoc, CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME); + lfr->neighbor_scan_max_chan_time = + cfg_get(psoc, CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME); + lfr->neighbor_scan_results_refresh_period = + cfg_get(psoc, CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD); + lfr->empty_scan_refresh_period = + cfg_get(psoc, CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD); + lfr->roam_bmiss_first_bcnt = + cfg_get(psoc, CFG_LFR_ROAM_BMISS_FIRST_BCNT); + lfr->roam_bmiss_final_bcnt = + cfg_get(psoc, CFG_LFR_ROAM_BMISS_FINAL_BCNT); + lfr->roam_beacon_rssi_weight = + cfg_get(psoc, CFG_LFR_ROAM_BEACON_RSSI_WEIGHT); + lfr->roaming_dfs_channel = + cfg_get(psoc, CFG_LFR_ROAMING_DFS_CHANNEL); + lfr->roam_scan_hi_rssi_maxcount = + cfg_get(psoc, CFG_LFR_ROAM_SCAN_HI_RSSI_MAXCOUNT); + lfr->roam_scan_hi_rssi_delta = + cfg_get(psoc, CFG_LFR_ROAM_SCAN_HI_RSSI_DELTA); + lfr->roam_scan_hi_rssi_delay = + cfg_get(psoc, CFG_LFR_ROAM_SCAN_HI_RSSI_DELAY); + lfr->roam_scan_hi_rssi_ub = + cfg_get(psoc, CFG_LFR_ROAM_SCAN_HI_RSSI_UB); + lfr->roam_prefer_5ghz = + cfg_get(psoc, CFG_LFR_ROAM_PREFER_5GHZ); + lfr->roam_intra_band = + cfg_get(psoc, CFG_LFR_ROAM_INTRA_BAND); + lfr->roam_scan_home_away_time = + cfg_get(psoc, CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME); + lfr->roam_scan_n_probes = + cfg_get(psoc, CFG_LFR_ROAM_SCAN_N_PROBES); + lfr->delay_before_vdev_stop = + cfg_get(psoc, CFG_LFR_DELAY_BEFORE_VDEV_STOP); + qdf_uint8_array_parse(cfg_get(psoc, CFG_LFR_NEIGHBOR_SCAN_CHANNEL_LIST), + lfr->neighbor_scan_channel_list, + CFG_VALID_CHANNEL_LIST_LEN, + &neighbor_scan_chan_list_num); + lfr->neighbor_scan_channel_list_num = + (uint8_t)neighbor_scan_chan_list_num; + lfr->ho_delay_for_rx = + cfg_get(psoc, CFG_LFR3_ROAM_HO_DELAY_FOR_RX); + lfr->min_delay_btw_roam_scans = + cfg_get(psoc, CFG_LFR_MIN_DELAY_BTW_ROAM_SCAN); + lfr->roam_trigger_reason_bitmask = + cfg_get(psoc, CFG_LFR_ROAM_SCAN_TRIGGER_REASON_BITMASK); + lfr->enable_ftopen = + cfg_get(psoc, CFG_LFR_ROAM_FT_OPEN_ENABLE); + lfr->roam_force_rssi_trigger = + cfg_get(psoc, CFG_LFR_ROAM_FORCE_RSSI_TRIGGER); + lfr->roaming_scan_policy = + cfg_get(psoc, CFG_ROAM_SCAN_SCAN_POLICY); + + lfr->roam_scan_inactivity_time = + cfg_get(psoc, CFG_ROAM_SCAN_INACTIVITY_TIME); + lfr->roam_inactive_data_packet_count = + cfg_get(psoc, CFG_ROAM_INACTIVE_COUNT); + lfr->roam_scan_period_after_inactivity = + cfg_get(psoc, CFG_POST_INACTIVITY_ROAM_SCAN_PERIOD); + lfr->fw_akm_bitmap = 0; + lfr->enable_ft_im_roaming = cfg_get(psoc, CFG_FT_IM_ROAMING); + + mlme_init_roam_offload_cfg(psoc, lfr); + mlme_init_ese_cfg(psoc, lfr); + mlme_init_bss_load_trigger_params(psoc, &lfr->bss_load_trig); + mlme_init_adaptive_11r_cfg(psoc, lfr); + mlme_init_subnet_detection(psoc, lfr); +} + +static uint32_t +mlme_limit_max_per_index_score(uint32_t per_index_score) +{ + uint8_t i, score; + + for (i = 0; i < MAX_INDEX_PER_INI; i++) { + score = WLAN_GET_SCORE_PERCENTAGE(per_index_score, i); + if (score > MAX_INDEX_SCORE) + WLAN_SET_SCORE_PERCENTAGE(per_index_score, + MAX_INDEX_SCORE, i); + } + + return per_index_score; +} + +static void mlme_init_power_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_power *power) +{ + power->tx_power_2g = cfg_get(psoc, CFG_SET_TXPOWER_LIMIT2G); + power->tx_power_5g = cfg_get(psoc, CFG_SET_TXPOWER_LIMIT5G); + + power->max_tx_power_24_chan.max_len = CFG_MAX_TX_POWER_2_4_LEN; + qdf_uint8_array_parse(cfg_default(CFG_MAX_TX_POWER_2_4), + power->max_tx_power_24_chan.data, + sizeof(power->max_tx_power_24_chan.data), + &power->max_tx_power_24_chan.len); + + power->max_tx_power_5_chan.max_len = CFG_MAX_TX_POWER_5_LEN; + qdf_uint8_array_parse(cfg_default(CFG_MAX_TX_POWER_5), + power->max_tx_power_5_chan.data, + sizeof(power->max_tx_power_5_chan.data), + &power->max_tx_power_5_chan.len); + + power->power_usage.max_len = CFG_POWER_USAGE_MAX_LEN; + power->power_usage.len = CFG_POWER_USAGE_MAX_LEN; + qdf_mem_copy(power->power_usage.data, cfg_get(psoc, CFG_POWER_USAGE), + power->power_usage.len); + power->max_tx_power = cfg_get(psoc, CFG_MAX_TX_POWER); + power->current_tx_power_level = + (uint8_t)cfg_default(CFG_CURRENT_TX_POWER_LEVEL); + power->local_power_constraint = + (uint8_t)cfg_default(CFG_LOCAL_POWER_CONSTRAINT); +} + +static void mlme_init_scoring_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_scoring_cfg *scoring_cfg) +{ + uint32_t total_weight; + + scoring_cfg->vendor_roam_score_algorithm = + cfg_get(psoc, CFG_VENDOR_ROAM_SCORE_ALGORITHM); + scoring_cfg->enable_scoring_for_roam = + cfg_get(psoc, CFG_ENABLE_SCORING_FOR_ROAM); + scoring_cfg->weight_cfg.rssi_weightage = + cfg_get(psoc, CFG_SCORING_RSSI_WEIGHTAGE); + scoring_cfg->weight_cfg.ht_caps_weightage = + cfg_get(psoc, CFG_SCORING_HT_CAPS_WEIGHTAGE); + scoring_cfg->weight_cfg.vht_caps_weightage = + cfg_get(psoc, CFG_SCORING_VHT_CAPS_WEIGHTAGE); + scoring_cfg->weight_cfg.he_caps_weightage = + cfg_get(psoc, CFG_SCORING_HE_CAPS_WEIGHTAGE); + scoring_cfg->weight_cfg.chan_width_weightage = + cfg_get(psoc, CFG_SCORING_CHAN_WIDTH_WEIGHTAGE); + scoring_cfg->weight_cfg.chan_band_weightage = + cfg_get(psoc, CFG_SCORING_CHAN_BAND_WEIGHTAGE); + scoring_cfg->weight_cfg.nss_weightage = + cfg_get(psoc, CFG_SCORING_NSS_WEIGHTAGE); + scoring_cfg->weight_cfg.beamforming_cap_weightage = + cfg_get(psoc, CFG_SCORING_BEAMFORM_CAP_WEIGHTAGE); + scoring_cfg->weight_cfg.pcl_weightage = + cfg_get(psoc, CFG_SCORING_PCL_WEIGHTAGE); + scoring_cfg->weight_cfg.channel_congestion_weightage = + cfg_get(psoc, CFG_SCORING_CHAN_CONGESTION_WEIGHTAGE); + scoring_cfg->weight_cfg.oce_wan_weightage = + cfg_get(psoc, CFG_SCORING_OCE_WAN_WEIGHTAGE); + scoring_cfg->weight_cfg.sae_pk_ap_weightage = + cfg_get(psoc, CFG_SAE_PK_AP_WEIGHTAGE); + + total_weight = scoring_cfg->weight_cfg.rssi_weightage + + scoring_cfg->weight_cfg.ht_caps_weightage + + scoring_cfg->weight_cfg.vht_caps_weightage + + scoring_cfg->weight_cfg.he_caps_weightage + + scoring_cfg->weight_cfg.chan_width_weightage + + scoring_cfg->weight_cfg.chan_band_weightage + + scoring_cfg->weight_cfg.nss_weightage + + scoring_cfg->weight_cfg.beamforming_cap_weightage + + scoring_cfg->weight_cfg.pcl_weightage + + scoring_cfg->weight_cfg.channel_congestion_weightage + + scoring_cfg->weight_cfg.oce_wan_weightage + + scoring_cfg->weight_cfg.sae_pk_ap_weightage; + + /* + * If configured weights are greater than max weight, + * fallback to default weights + */ + if (total_weight > MAX_BSS_SCORE) { + mlme_legacy_err("Total weight greater than %d, using default weights", + MAX_BSS_SCORE); + scoring_cfg->weight_cfg.rssi_weightage = RSSI_WEIGHTAGE; + scoring_cfg->weight_cfg.ht_caps_weightage = + HT_CAPABILITY_WEIGHTAGE; + scoring_cfg->weight_cfg.vht_caps_weightage = + VHT_CAP_WEIGHTAGE; + scoring_cfg->weight_cfg.he_caps_weightage = HE_CAP_WEIGHTAGE; + scoring_cfg->weight_cfg.chan_width_weightage = + CHAN_WIDTH_WEIGHTAGE; + scoring_cfg->weight_cfg.chan_band_weightage = + CHAN_BAND_WEIGHTAGE; + scoring_cfg->weight_cfg.nss_weightage = NSS_WEIGHTAGE; + scoring_cfg->weight_cfg.beamforming_cap_weightage = + BEAMFORMING_CAP_WEIGHTAGE; + scoring_cfg->weight_cfg.pcl_weightage = PCL_WEIGHT; + scoring_cfg->weight_cfg.channel_congestion_weightage = + CHANNEL_CONGESTION_WEIGHTAGE; + scoring_cfg->weight_cfg.oce_wan_weightage = OCE_WAN_WEIGHTAGE; + scoring_cfg->weight_cfg.sae_pk_ap_weightage = + SAE_PK_AP_WEIGHTAGE; + } + + scoring_cfg->rssi_score.best_rssi_threshold = + cfg_get(psoc, CFG_SCORING_BEST_RSSI_THRESHOLD); + scoring_cfg->rssi_score.good_rssi_threshold = + cfg_get(psoc, CFG_SCORING_GOOD_RSSI_THRESHOLD); + scoring_cfg->rssi_score.bad_rssi_threshold = + cfg_get(psoc, CFG_SCORING_BAD_RSSI_THRESHOLD); + + scoring_cfg->rssi_score.good_rssi_pcnt = + cfg_get(psoc, CFG_SCORING_GOOD_RSSI_PERCENT); + scoring_cfg->rssi_score.bad_rssi_pcnt = + cfg_get(psoc, CFG_SCORING_BAD_RSSI_PERCENT); + + scoring_cfg->rssi_score.good_rssi_bucket_size = + cfg_get(psoc, CFG_SCORING_GOOD_RSSI_BUCKET_SIZE); + scoring_cfg->rssi_score.bad_rssi_bucket_size = + cfg_get(psoc, CFG_SCORING_BAD_RSSI_BUCKET_SIZE); + + scoring_cfg->rssi_score.rssi_pref_5g_rssi_thresh = + cfg_get(psoc, CFG_SCORING_RSSI_PREF_5G_THRESHOLD); + + scoring_cfg->bandwidth_weight_per_index = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_BW_WEIGHT_PER_IDX)); + scoring_cfg->nss_weight_per_index = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_NSS_WEIGHT_PER_IDX)); + scoring_cfg->band_weight_per_index = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_BAND_WEIGHT_PER_IDX)); + + scoring_cfg->esp_qbss_scoring.num_slot = + cfg_get(psoc, CFG_SCORING_NUM_ESP_QBSS_SLOTS); + scoring_cfg->esp_qbss_scoring.score_pcnt3_to_0 = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_ESP_QBSS_SCORE_IDX_3_TO_0)); + scoring_cfg->esp_qbss_scoring.score_pcnt7_to_4 = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_ESP_QBSS_SCORE_IDX_7_TO_4)); + scoring_cfg->esp_qbss_scoring.score_pcnt11_to_8 = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_ESP_QBSS_SCORE_IDX_11_TO_8)); + scoring_cfg->esp_qbss_scoring.score_pcnt15_to_12 = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_ESP_QBSS_SCORE_IDX_15_TO_12)); + + scoring_cfg->oce_wan_scoring.num_slot = + cfg_get(psoc, CFG_SCORING_NUM_OCE_WAN_SLOTS); + scoring_cfg->oce_wan_scoring.score_pcnt3_to_0 = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_OCE_WAN_SCORE_IDX_3_TO_0)); + scoring_cfg->oce_wan_scoring.score_pcnt7_to_4 = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_OCE_WAN_SCORE_IDX_7_TO_4)); + scoring_cfg->oce_wan_scoring.score_pcnt11_to_8 = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_OCE_WAN_SCORE_IDX_11_TO_8)); + scoring_cfg->oce_wan_scoring.score_pcnt15_to_12 = + mlme_limit_max_per_index_score( + cfg_get(psoc, CFG_SCORING_OCE_WAN_SCORE_IDX_15_TO_12)); + scoring_cfg->roam_trigger_bitmap = + cfg_get(psoc, CFG_ROAM_SCORE_DELTA_TRIGGER_BITMAP); + scoring_cfg->roam_score_delta = cfg_get(psoc, CFG_ROAM_SCORE_DELTA); + scoring_cfg->apsd_enabled = (bool)cfg_default(CFG_APSD_ENABLED); + scoring_cfg->min_roam_score_delta = + cfg_get(psoc, CFG_CAND_MIN_ROAM_SCORE_DELTA); +} + +static void mlme_init_oce_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_oce *oce) +{ + uint8_t val; + bool rssi_assoc_reject_enabled; + bool probe_req_rate_enabled; + bool probe_resp_rate_enabled; + bool beacon_rate_enabled; + bool probe_req_deferral_enabled; + bool fils_discovery_sap_enabled; + bool esp_for_roam_enabled; + + oce->enable_bcast_probe_rsp = + cfg_get(psoc, CFG_ENABLE_BCAST_PROBE_RESP); + oce->oce_sta_enabled = cfg_get(psoc, CFG_OCE_ENABLE_STA); + oce->oce_sap_enabled = cfg_get(psoc, CFG_OCE_ENABLE_SAP); + oce->fils_enabled = cfg_get(psoc, CFG_IS_FILS_ENABLED); + + rssi_assoc_reject_enabled = + cfg_get(psoc, CFG_OCE_ENABLE_RSSI_BASED_ASSOC_REJECT); + probe_req_rate_enabled = cfg_get(psoc, CFG_OCE_PROBE_REQ_RATE); + probe_resp_rate_enabled = cfg_get(psoc, CFG_OCE_PROBE_RSP_RATE); + beacon_rate_enabled = cfg_get(psoc, CFG_OCE_BEACON_RATE); + probe_req_deferral_enabled = + cfg_get(psoc, CFG_ENABLE_PROBE_REQ_DEFERRAL); + fils_discovery_sap_enabled = + cfg_get(psoc, CFG_ENABLE_FILS_DISCOVERY_SAP); + esp_for_roam_enabled = cfg_get(psoc, CFG_ENABLE_ESP_FEATURE); + + if (!rssi_assoc_reject_enabled || + !oce->enable_bcast_probe_rsp) { + oce->oce_sta_enabled = 0; + } + + val = (probe_req_rate_enabled * + WMI_VDEV_OCE_PROBE_REQUEST_RATE_FEATURE_BITMAP) + + (probe_resp_rate_enabled * + WMI_VDEV_OCE_PROBE_RESPONSE_RATE_FEATURE_BITMAP) + + (beacon_rate_enabled * + WMI_VDEV_OCE_BEACON_RATE_FEATURE_BITMAP) + + (probe_req_deferral_enabled * + WMI_VDEV_OCE_PROBE_REQUEST_DEFERRAL_FEATURE_BITMAP) + + (fils_discovery_sap_enabled * + WMI_VDEV_OCE_FILS_DISCOVERY_FRAME_FEATURE_BITMAP) + + (esp_for_roam_enabled * + WMI_VDEV_OCE_ESP_FEATURE_BITMAP) + + (rssi_assoc_reject_enabled * + WMI_VDEV_OCE_REASSOC_REJECT_FEATURE_BITMAP); + oce->feature_bitmap = val; +} + +static void mlme_init_nss_chains(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_nss_chains *nss_chains) +{ + nss_chains->num_rx_chains[NSS_CHAINS_BAND_2GHZ] = + cfg_get(psoc, CFG_NUM_RX_CHAINS_2G); + nss_chains->num_rx_chains[NSS_CHAINS_BAND_5GHZ] = + cfg_get(psoc, CFG_NUM_RX_CHAINS_5G); + nss_chains->num_tx_chains[NSS_CHAINS_BAND_2GHZ] = + cfg_get(psoc, CFG_NUM_TX_CHAINS_2G); + nss_chains->num_tx_chains[NSS_CHAINS_BAND_5GHZ] = + cfg_get(psoc, CFG_NUM_TX_CHAINS_5G); + + nss_chains->tx_nss[NSS_CHAINS_BAND_2GHZ] = cfg_get(psoc, CFG_TX_NSS_2G); + nss_chains->tx_nss[NSS_CHAINS_BAND_5GHZ] = cfg_get(psoc, CFG_TX_NSS_5G); + nss_chains->rx_nss[NSS_CHAINS_BAND_2GHZ] = cfg_get(psoc, CFG_RX_NSS_2G); + nss_chains->rx_nss[NSS_CHAINS_BAND_5GHZ] = cfg_get(psoc, CFG_RX_NSS_5G); + + nss_chains->num_tx_chains_11b = cfg_get(psoc, CFG_NUM_TX_CHAINS_11b); + nss_chains->num_tx_chains_11g = cfg_get(psoc, CFG_NUM_TX_CHAINS_11g); + nss_chains->num_tx_chains_11a = cfg_get(psoc, CFG_NUM_TX_CHAINS_11a); + + nss_chains->disable_rx_mrc[NSS_CHAINS_BAND_2GHZ] = + cfg_get(psoc, CFG_DISABLE_RX_MRC_2G); + nss_chains->disable_rx_mrc[NSS_CHAINS_BAND_5GHZ] = + cfg_get(psoc, CFG_DISABLE_RX_MRC_5G); + nss_chains->disable_tx_mrc[NSS_CHAINS_BAND_2GHZ] = + cfg_get(psoc, CFG_DISABLE_TX_MRC_2G); + nss_chains->disable_tx_mrc[NSS_CHAINS_BAND_5GHZ] = + cfg_get(psoc, CFG_DISABLE_TX_MRC_5G); +} +static void mlme_init_wep_keys(struct wlan_mlme_wep_cfg *wep_params) +{ + /* initialize the default key values to zero */ + wep_params->wep_default_key_1.len = WLAN_CRYPTO_KEY_WEP104_LEN; + wep_params->wep_default_key_1.max_len = WLAN_CRYPTO_KEY_WEP104_LEN; + qdf_mem_zero(wep_params->wep_default_key_1.data, + WLAN_CRYPTO_KEY_WEP104_LEN); + + wep_params->wep_default_key_2.len = WLAN_CRYPTO_KEY_WEP104_LEN; + wep_params->wep_default_key_2.max_len = WLAN_CRYPTO_KEY_WEP104_LEN; + qdf_mem_zero(wep_params->wep_default_key_2.data, + WLAN_CRYPTO_KEY_WEP104_LEN); + + wep_params->wep_default_key_3.len = WLAN_CRYPTO_KEY_WEP104_LEN; + wep_params->wep_default_key_3.max_len = WLAN_CRYPTO_KEY_WEP104_LEN; + qdf_mem_zero(wep_params->wep_default_key_3.data, + WLAN_CRYPTO_KEY_WEP104_LEN); + + wep_params->wep_default_key_4.len = WLAN_CRYPTO_KEY_WEP104_LEN; + wep_params->wep_default_key_4.max_len = WLAN_CRYPTO_KEY_WEP104_LEN; + qdf_mem_zero(wep_params->wep_default_key_4.data, + WLAN_CRYPTO_KEY_WEP104_LEN); +} + +static void mlme_init_wep_cfg(struct wlan_mlme_wep_cfg *wep_params) +{ + wep_params->is_privacy_enabled = cfg_default(CFG_PRIVACY_ENABLED); + wep_params->auth_type = cfg_default(CFG_AUTHENTICATION_TYPE); + wep_params->is_shared_key_auth = + cfg_default(CFG_SHARED_KEY_AUTH_ENABLE); + wep_params->is_auth_open_system = + cfg_default(CFG_OPEN_SYSTEM_AUTH_ENABLE); + + wep_params->wep_default_key_id = cfg_default(CFG_WEP_DEFAULT_KEYID); + mlme_init_wep_keys(wep_params); +} + +static void mlme_init_wifi_pos_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_wifi_pos_cfg *wifi_pos_cfg) +{ + wifi_pos_cfg->fine_time_meas_cap = + cfg_get(psoc, CFG_FINE_TIME_MEAS_CAPABILITY); + wifi_pos_cfg->oem_6g_support_disable = + cfg_get(psoc, CFG_OEM_SIXG_SUPPORT_DISABLE); +} + +#ifdef FEATURE_WLAN_ESE +static void mlme_init_inactivity_intv(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_wmm_params *wmm_params) +{ + wmm_params->wmm_tspec_element.inactivity_intv = + cfg_get(psoc, CFG_QOS_WMM_INACTIVITY_INTERVAL); +} +#else +static inline void +mlme_init_inactivity_intv(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_wmm_params *wmm_params) +{ +} +#endif /* FEATURE_WLAN_ESE */ + +static void mlme_init_wmm_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_wmm_params *wmm_params) +{ + wmm_params->qos_enabled = cfg_default(CFG_QOS_ENABLED); + wmm_params->wme_enabled = cfg_default(CFG_WME_ENABLED); + wmm_params->max_sp_length = cfg_default(CFG_MAX_SP_LENGTH); + wmm_params->wsm_enabled = cfg_default(CFG_WSM_ENABLED); + wmm_params->edca_profile = cfg_default(CFG_EDCA_PROFILE); + + wmm_params->ac_vo.dir_ac_vo = cfg_get(psoc, CFG_QOS_WMM_DIR_AC_VO); + wmm_params->ac_vo.nom_msdu_size_ac_vo = + cfg_get(psoc, CFG_QOS_WMM_NOM_MSDU_SIZE_AC_VO); + wmm_params->ac_vo.mean_data_rate_ac_vo = + cfg_get(psoc, CFG_QOS_WMM_MEAN_DATA_RATE_AC_VO); + wmm_params->ac_vo.min_phy_rate_ac_vo = + cfg_get(psoc, CFG_QOS_WMM_MIN_PHY_RATE_AC_VO); + wmm_params->ac_vo.sba_ac_vo = cfg_get(psoc, CFG_QOS_WMM_SBA_AC_VO); + wmm_params->ac_vo.uapsd_vo_srv_intv = + cfg_get(psoc, CFG_QOS_WMM_UAPSD_VO_SRV_INTV); + wmm_params->ac_vo.uapsd_vo_sus_intv = + cfg_get(psoc, CFG_QOS_WMM_UAPSD_VO_SUS_INTV); + + wmm_params->ac_vi.dir_ac_vi = + cfg_get(psoc, CFG_QOS_WMM_DIR_AC_VI); + wmm_params->ac_vi.nom_msdu_size_ac_vi = + cfg_get(psoc, CFG_QOS_WMM_NOM_MSDU_SIZE_AC_VI); + wmm_params->ac_vi.mean_data_rate_ac_vi = + cfg_get(psoc, CFG_QOS_WMM_MEAN_DATA_RATE_AC_VI); + wmm_params->ac_vi.min_phy_rate_ac_vi = + cfg_get(psoc, CFG_QOS_WMM_MIN_PHY_RATE_AC_VI); + wmm_params->ac_vi.sba_ac_vi = + cfg_get(psoc, CFG_QOS_WMM_SBA_AC_VI); + wmm_params->ac_vi.uapsd_vi_srv_intv = + cfg_get(psoc, CFG_QOS_WMM_UAPSD_VI_SRV_INTV); + wmm_params->ac_vi.uapsd_vi_sus_intv = + cfg_get(psoc, CFG_QOS_WMM_UAPSD_VI_SUS_INTV); + + wmm_params->ac_be.dir_ac_be = + cfg_get(psoc, CFG_QOS_WMM_DIR_AC_BE); + wmm_params->ac_be.nom_msdu_size_ac_be = + cfg_get(psoc, CFG_QOS_WMM_NOM_MSDU_SIZE_AC_BE); + wmm_params->ac_be.mean_data_rate_ac_be = + cfg_get(psoc, CFG_QOS_WMM_MEAN_DATA_RATE_AC_BE); + wmm_params->ac_be.min_phy_rate_ac_be = + cfg_get(psoc, CFG_QOS_WMM_MIN_PHY_RATE_AC_BE); + wmm_params->ac_be.sba_ac_be = + cfg_get(psoc, CFG_QOS_WMM_SBA_AC_BE); + wmm_params->ac_be.uapsd_be_srv_intv = + cfg_get(psoc, CFG_QOS_WMM_UAPSD_BE_SRV_INTV); + wmm_params->ac_be.uapsd_be_sus_intv = + cfg_get(psoc, CFG_QOS_WMM_UAPSD_BE_SUS_INTV); + + wmm_params->ac_bk.dir_ac_bk = + cfg_get(psoc, CFG_QOS_WMM_DIR_AC_BK); + wmm_params->ac_bk.nom_msdu_size_ac_bk = + cfg_get(psoc, CFG_QOS_WMM_NOM_MSDU_SIZE_AC_BK); + wmm_params->ac_bk.mean_data_rate_ac_bk = + cfg_get(psoc, CFG_QOS_WMM_MEAN_DATA_RATE_AC_BK); + wmm_params->ac_bk.min_phy_rate_ac_bk = + cfg_get(psoc, CFG_QOS_WMM_MIN_PHY_RATE_AC_BK); + wmm_params->ac_bk.sba_ac_bk = + cfg_get(psoc, CFG_QOS_WMM_SBA_AC_BK); + wmm_params->ac_bk.uapsd_bk_srv_intv = + cfg_get(psoc, CFG_QOS_WMM_UAPSD_BK_SRV_INTV); + wmm_params->ac_bk.uapsd_bk_sus_intv = + cfg_get(psoc, CFG_QOS_WMM_UAPSD_BK_SUS_INTV); + + wmm_params->wmm_config.wmm_mode = + cfg_get(psoc, CFG_QOS_WMM_MODE); + wmm_params->wmm_config.b80211e_is_enabled = + cfg_get(psoc, CFG_QOS_WMM_80211E_ENABLED); + wmm_params->wmm_config.uapsd_mask = + cfg_get(psoc, CFG_QOS_WMM_UAPSD_MASK); + wmm_params->wmm_config.bimplicit_qos_enabled = + cfg_get(psoc, CFG_QOS_WMM_IMPLICIT_SETUP_ENABLED); + + mlme_init_inactivity_intv(psoc, wmm_params); + wmm_params->wmm_tspec_element.burst_size_def = + cfg_get(psoc, CFG_QOS_WMM_BURST_SIZE_DEFN); + wmm_params->wmm_tspec_element.ts_ack_policy = + cfg_get(psoc, CFG_QOS_WMM_TS_INFO_ACK_POLICY); + wmm_params->wmm_tspec_element.ts_acm_is_off = + cfg_get(psoc, CFG_QOS_ADDTS_WHEN_ACM_IS_OFF); + wmm_params->delayed_trigger_frm_int = + cfg_get(psoc, CFG_TL_DELAYED_TRGR_FRM_INTERVAL); + +} + +static void mlme_init_wps_params_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_wps_params *wps_params) +{ + wps_params->enable_wps = cfg_default(CFG_WPS_ENABLE); + wps_params->wps_cfg_method = cfg_default(CFG_WPS_CFG_METHOD); + wps_params->wps_device_password_id = + cfg_default(CFG_WPS_DEVICE_PASSWORD_ID); + wps_params->wps_device_sub_category = + cfg_default(CFG_WPS_DEVICE_SUB_CATEGORY); + wps_params->wps_primary_device_category = + cfg_default(CFG_WPS_PRIMARY_DEVICE_CATEGORY); + wps_params->wps_primary_device_oui = + cfg_default(CFG_WPS_PIMARY_DEVICE_OUI); + wps_params->wps_state = cfg_default(CFG_WPS_STATE); + wps_params->wps_version = cfg_default(CFG_WPS_VERSION); + wps_params->wps_uuid.max_len = MLME_CFG_WPS_UUID_MAX_LEN; + qdf_uint8_array_parse(cfg_default(CFG_WPS_UUID), + wps_params->wps_uuid.data, + MLME_CFG_WPS_UUID_MAX_LEN, + &wps_params->wps_uuid.len); +} + +static void mlme_init_btm_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_btm *btm) +{ + btm->btm_offload_config = cfg_get(psoc, CFG_BTM_ENABLE); + btm->prefer_btm_query = cfg_get(psoc, CFG_PREFER_BTM_QUERY); + if (btm->prefer_btm_query) + MLME_SET_BIT(btm->btm_offload_config, BTM_OFFLOAD_CONFIG_BIT_8); + + btm->abridge_flag = cfg_get(psoc, CFG_ENABLE_BTM_ABRIDGE); + if (btm->abridge_flag) + MLME_SET_BIT(btm->btm_offload_config, BTM_OFFLOAD_CONFIG_BIT_7); + + btm->btm_solicited_timeout = cfg_get(psoc, CFG_BTM_SOLICITED_TIMEOUT); + btm->btm_max_attempt_cnt = cfg_get(psoc, CFG_BTM_MAX_ATTEMPT_CNT); + btm->btm_sticky_time = cfg_get(psoc, CFG_BTM_STICKY_TIME); + btm->rct_validity_timer = cfg_get(psoc, CFG_BTM_VALIDITY_TIMER); + btm->disassoc_timer_threshold = + cfg_get(psoc, CFG_BTM_DISASSOC_TIMER_THRESHOLD); + btm->btm_query_bitmask = cfg_get(psoc, CFG_BTM_QUERY_BITMASK); + btm->btm_trig_min_candidate_score = + cfg_get(psoc, CFG_MIN_BTM_CANDIDATE_SCORE); +} + +static void +mlme_init_roam_score_config(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_cfg *mlme_cfg) +{ + struct roam_trigger_score_delta *score_delta_param; + struct roam_trigger_min_rssi *min_rssi_param; + + score_delta_param = &mlme_cfg->trig_score_delta[IDLE_ROAM_TRIGGER]; + score_delta_param->roam_score_delta = + cfg_get(psoc, CFG_IDLE_ROAM_SCORE_DELTA); + score_delta_param->trigger_reason = ROAM_TRIGGER_REASON_IDLE; + + score_delta_param = &mlme_cfg->trig_score_delta[BTM_ROAM_TRIGGER]; + score_delta_param->roam_score_delta = + cfg_get(psoc, CFG_BTM_ROAM_SCORE_DELTA); + score_delta_param->trigger_reason = ROAM_TRIGGER_REASON_BTM; + + min_rssi_param = &mlme_cfg->trig_min_rssi[DEAUTH_MIN_RSSI]; + min_rssi_param->min_rssi = + cfg_get(psoc, CFG_DISCONNECT_ROAM_TRIGGER_MIN_RSSI); + min_rssi_param->trigger_reason = ROAM_TRIGGER_REASON_DEAUTH; + + min_rssi_param = &mlme_cfg->trig_min_rssi[BMISS_MIN_RSSI]; + min_rssi_param->min_rssi = + cfg_get(psoc, CFG_BMISS_ROAM_MIN_RSSI); + min_rssi_param->trigger_reason = ROAM_TRIGGER_REASON_BMISS; + + min_rssi_param = &mlme_cfg->trig_min_rssi[MIN_RSSI_2G_TO_5G_ROAM]; + min_rssi_param->min_rssi = + cfg_get(psoc, CFG_2G_TO_5G_ROAM_MIN_RSSI); + min_rssi_param->trigger_reason = ROAM_TRIGGER_REASON_HIGH_RSSI; + +} + +/** + * mlme_init_fe_wlm_in_cfg() - Populate WLM INI in MLME cfg + * @psoc: pointer to the psoc object + * @wlm_config: pointer to the MLME WLM cfg + * + * Return: None + */ +static void mlme_init_fe_wlm_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_fe_wlm *wlm_config) +{ + wlm_config->latency_enable = cfg_get(psoc, CFG_LATENCY_ENABLE); + wlm_config->latency_reset = cfg_get(psoc, CFG_LATENCY_RESET); + wlm_config->latency_level = cfg_get(psoc, CFG_LATENCY_LEVEL); + wlm_config->latency_flags[0] = cfg_get(psoc, CFG_LATENCY_FLAGS_NORMAL); + wlm_config->latency_flags[1] = cfg_get(psoc, CFG_LATENCY_FLAGS_MOD); + wlm_config->latency_flags[2] = cfg_get(psoc, CFG_LATENCY_FLAGS_LOW); + wlm_config->latency_flags[3] = cfg_get(psoc, CFG_LATENCY_FLAGS_ULTLOW); +} + +/** + * mlme_init_fe_rrm_in_cfg() - Populate RRM INI in MLME cfg + * @psoc: pointer to the psoc object + * @rrm_config: pointer to the MLME RRM cfg + * + * Return: None + */ +static void mlme_init_fe_rrm_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_fe_rrm *rrm_config) +{ + qdf_size_t len; + + rrm_config->rrm_enabled = cfg_get(psoc, CFG_RRM_ENABLE); + rrm_config->sap_rrm_enabled = cfg_get(psoc, CFG_SAP_RRM_ENABLE); + rrm_config->rrm_rand_interval = cfg_get(psoc, CFG_RRM_MEAS_RAND_INTVL); + + qdf_uint8_array_parse(cfg_get(psoc, CFG_RM_CAPABILITY), + rrm_config->rm_capability, + sizeof(rrm_config->rm_capability), &len); + + if (len < MLME_RMENABLEDCAP_MAX_LEN) { + mlme_legacy_debug("Incorrect RM capability, using default"); + qdf_uint8_array_parse(cfg_default(CFG_RM_CAPABILITY), + rrm_config->rm_capability, + sizeof(rrm_config->rm_capability), &len); + } +} + +static void mlme_init_powersave_params(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_powersave *ps_cfg) +{ + ps_cfg->is_imps_enabled = cfg_get(psoc, CFG_ENABLE_IMPS); + ps_cfg->is_bmps_enabled = cfg_get(psoc, CFG_ENABLE_PS); + ps_cfg->auto_bmps_timer_val = cfg_get(psoc, CFG_AUTO_BMPS_ENABLE_TIMER); + ps_cfg->bmps_min_listen_interval = cfg_get(psoc, CFG_BMPS_MINIMUM_LI); + ps_cfg->bmps_max_listen_interval = cfg_get(psoc, CFG_BMPS_MAXIMUM_LI); + ps_cfg->dtim_selection_diversity = + cfg_get(psoc, CFG_DTIM_SELECTION_DIVERSITY); +} + +#ifdef MWS_COEX +static void mlme_init_mwc_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_mwc *mwc) +{ + mwc->mws_coex_4g_quick_tdm = + cfg_get(psoc, CFG_MWS_COEX_4G_QUICK_FTDM); + mwc->mws_coex_5g_nr_pwr_limit = + cfg_get(psoc, CFG_MWS_COEX_5G_NR_PWR_LIMIT); + mwc->mws_coex_pcc_channel_avoid_delay = + cfg_get(psoc, CFG_MWS_COEX_PCC_CHANNEL_AVOID_DELAY); + mwc->mws_coex_scc_channel_avoid_delay = + cfg_get(psoc, CFG_MWS_COEX_SCC_CHANNEL_AVOID_DELAY); +} +#else +static void mlme_init_mwc_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_mwc *mwc) +{ +} +#endif + +#ifdef SAP_AVOID_ACS_FREQ_LIST +static void mlme_init_acs_avoid_freq_list(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_reg *reg) +{ + qdf_size_t avoid_acs_freq_list_num = 0; + uint8_t i; + + qdf_uint16_array_parse(cfg_get(psoc, CFG_SAP_AVOID_ACS_FREQ_LIST), + reg->avoid_acs_freq_list, + CFG_VALID_CHANNEL_LIST_LEN, + &avoid_acs_freq_list_num); + reg->avoid_acs_freq_list_num = avoid_acs_freq_list_num; + + for (i = 0; i < avoid_acs_freq_list_num; i++) + mlme_legacy_debug("avoid_acs_freq %d", + reg->avoid_acs_freq_list[i]); +} +#else +static void mlme_init_acs_avoid_freq_list(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_reg *reg) +{ +} +#endif + +static void mlme_init_reg_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_reg *reg) +{ + qdf_size_t valid_channel_list_num = 0; + uint8_t channel_list[CFG_VALID_CHANNEL_LIST_LEN]; + uint8_t i; + struct wlan_objmgr_pdev *pdev = NULL; + + reg->self_gen_frm_pwr = cfg_get(psoc, CFG_SELF_GEN_FRM_PWR); + reg->etsi_srd_chan_in_master_mode = + cfg_get(psoc, CFG_ETSI_SRD_CHAN_IN_MASTER_MODE); + reg->restart_beaconing_on_ch_avoid = + cfg_get(psoc, CFG_RESTART_BEACONING_ON_CH_AVOID); + reg->indoor_channel_support = cfg_get(psoc, CFG_INDOOR_CHANNEL_SUPPORT); + reg->enable_11d_in_world_mode = cfg_get(psoc, + CFG_ENABLE_11D_IN_WORLD_MODE); + reg->scan_11d_interval = cfg_get(psoc, CFG_SCAN_11D_INTERVAL); + reg->enable_pending_chan_list_req = cfg_get(psoc, + CFG_ENABLE_PENDING_CHAN_LIST_REQ); + reg->ignore_fw_reg_offload_ind = cfg_get( + psoc, + CFG_IGNORE_FW_REG_OFFLOAD_IND); + reg->retain_nol_across_regdmn_update = + cfg_get(psoc, CFG_RETAIN_NOL_ACROSS_REG_DOMAIN); + + qdf_uint8_array_parse(cfg_default(CFG_VALID_CHANNEL_LIST), + channel_list, + CFG_VALID_CHANNEL_LIST_LEN, + &valid_channel_list_num); + reg->valid_channel_list_num = (uint8_t)valid_channel_list_num; + + reg->enable_nan_on_indoor_channels = + cfg_get(psoc, CFG_INDOOR_CHANNEL_SUPPORT_FOR_NAN); + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_MLME_NB_ID); + if (!pdev) { + mlme_legacy_err("null pdev"); + return; + } + + for (i = 0; i < valid_channel_list_num; i++) + reg->valid_channel_freq_list[i] = + wlan_reg_chan_to_freq(pdev, channel_list[i]); + + wlan_objmgr_pdev_release_ref(pdev, WLAN_MLME_NB_ID); + + qdf_str_lcopy(reg->country_code, cfg_default(CFG_COUNTRY_CODE), + sizeof(reg->country_code)); + reg->country_code_len = (uint8_t)sizeof(reg->country_code); + mlme_init_acs_avoid_freq_list(psoc, reg); +} + +static void +mlme_init_dot11_mode_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_dot11_mode *dot11_mode) +{ + dot11_mode->dot11_mode = cfg_default(CFG_DOT11_MODE); + dot11_mode->vdev_type_dot11_mode = cfg_get(psoc, CFG_VDEV_DOT11_MODE); +} + +QDF_STATUS mlme_cfg_on_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + struct wlan_mlme_cfg *mlme_cfg; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_FAILURE; + } + + mlme_cfg = &mlme_obj->cfg; + mlme_init_generic_cfg(psoc, &mlme_cfg->gen); + mlme_init_timeout_cfg(psoc, &mlme_cfg->timeouts); + mlme_init_edca_params(psoc, &mlme_cfg->edca_params); + mlme_init_ht_cap_in_cfg(psoc, &mlme_cfg->ht_caps); + mlme_init_wmm_in_cfg(psoc, &mlme_cfg->wmm_params); + mlme_init_mbo_cfg(psoc, &mlme_cfg->mbo_cfg); + mlme_init_qos_cfg(psoc, &mlme_cfg->qos_mlme_params); + mlme_init_rates_in_cfg(psoc, &mlme_cfg->rates); + mlme_init_dfs_cfg(psoc, &mlme_cfg->dfs_cfg); + mlme_init_sap_protection_cfg(psoc, &mlme_cfg->sap_protection_cfg); + mlme_init_vht_cap_cfg(psoc, &mlme_cfg->vht_caps.vht_cap_info); + mlme_init_chainmask_cfg(psoc, &mlme_cfg->chainmask_cfg); + mlme_init_sap_cfg(psoc, &mlme_cfg->sap_cfg); + mlme_init_nss_chains(psoc, &mlme_cfg->nss_chains_ini_cfg); + mlme_init_he_cap_in_cfg(psoc, mlme_cfg); + mlme_init_obss_ht40_cfg(psoc, &mlme_cfg->obss_ht40); + mlme_init_product_details_cfg(&mlme_cfg->product_details); + mlme_init_powersave_params(psoc, &mlme_cfg->ps_params); + mlme_init_sta_cfg(psoc, &mlme_cfg->sta); + mlme_init_stats_cfg(psoc, &mlme_cfg->stats); + mlme_init_twt_cfg(psoc, &mlme_cfg->twt_cfg); + mlme_init_lfr_cfg(psoc, &mlme_cfg->lfr); + mlme_init_ibss_cfg(psoc, &mlme_cfg->ibss); + mlme_init_feature_flag_in_cfg(psoc, &mlme_cfg->feature_flags); + mlme_init_scoring_cfg(psoc, &mlme_cfg->scoring); + mlme_init_dot11_mode_cfg(psoc, &mlme_cfg->dot11_mode); + mlme_init_threshold_cfg(psoc, &mlme_cfg->threshold); + mlme_init_acs_cfg(psoc, &mlme_cfg->acs); + mlme_init_power_cfg(psoc, &mlme_cfg->power); + mlme_init_oce_cfg(psoc, &mlme_cfg->oce); + mlme_init_wep_cfg(&mlme_cfg->wep_params); + mlme_init_wifi_pos_cfg(psoc, &mlme_cfg->wifi_pos_cfg); + mlme_init_wps_params_cfg(psoc, &mlme_cfg->wps_params); + mlme_init_fe_wlm_in_cfg(psoc, &mlme_cfg->wlm_config); + mlme_init_fe_rrm_in_cfg(psoc, &mlme_cfg->rrm_config); + mlme_init_mwc_cfg(psoc, &mlme_cfg->mwc); + mlme_init_reg_cfg(psoc, &mlme_cfg->reg); + mlme_init_btm_cfg(psoc, &mlme_cfg->btm); + mlme_init_roam_score_config(psoc, mlme_cfg); + mlme_init_ratemask_cfg(psoc, &mlme_cfg->ratemask_cfg); + + return status; +} + +struct sae_auth_retry *mlme_get_sae_auth_retry(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return NULL; + } + + return &mlme_priv->sae_retry; +} + +void mlme_free_sae_auth_retry(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + mlme_priv->sae_retry.sae_auth_max_retry = 0; + if (mlme_priv->sae_retry.sae_auth.data) + qdf_mem_free(mlme_priv->sae_retry.sae_auth.data); + mlme_priv->sae_retry.sae_auth.data = NULL; + mlme_priv->sae_retry.sae_auth.len = 0; +} + +void mlme_set_self_disconnect_ies(struct wlan_objmgr_vdev *vdev, + struct wlan_ies *ie) +{ + struct mlme_legacy_priv *mlme_priv; + + if (!ie || !ie->len || !ie->data) { + mlme_legacy_debug("disocnnect IEs are NULL"); + return; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + if (mlme_priv->disconnect_info.self_discon_ies.data) { + qdf_mem_free(mlme_priv->disconnect_info.self_discon_ies.data); + mlme_priv->disconnect_info.self_discon_ies.len = 0; + } + + mlme_priv->disconnect_info.self_discon_ies.data = + qdf_mem_malloc(ie->len); + if (!mlme_priv->disconnect_info.self_discon_ies.data) + return; + + qdf_mem_copy(mlme_priv->disconnect_info.self_discon_ies.data, + ie->data, ie->len); + mlme_priv->disconnect_info.self_discon_ies.len = ie->len; + + mlme_legacy_debug("Self disconnect IEs"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLME, QDF_TRACE_LEVEL_DEBUG, + mlme_priv->disconnect_info.self_discon_ies.data, + mlme_priv->disconnect_info.self_discon_ies.len); +} + +void mlme_free_self_disconnect_ies(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + if (mlme_priv->disconnect_info.self_discon_ies.data) { + qdf_mem_free(mlme_priv->disconnect_info.self_discon_ies.data); + mlme_priv->disconnect_info.self_discon_ies.data = NULL; + mlme_priv->disconnect_info.self_discon_ies.len = 0; + } +} + +struct wlan_ies *mlme_get_self_disconnect_ies(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return NULL; + } + + return &mlme_priv->disconnect_info.self_discon_ies; +} + +void mlme_set_peer_disconnect_ies(struct wlan_objmgr_vdev *vdev, + struct wlan_ies *ie) +{ + struct mlme_legacy_priv *mlme_priv; + + if (!ie || !ie->len || !ie->data) { + mlme_legacy_debug("disocnnect IEs are NULL"); + return; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + if (mlme_priv->disconnect_info.peer_discon_ies.data) { + qdf_mem_free(mlme_priv->disconnect_info.peer_discon_ies.data); + mlme_priv->disconnect_info.peer_discon_ies.len = 0; + } + + mlme_priv->disconnect_info.peer_discon_ies.data = + qdf_mem_malloc(ie->len); + if (!mlme_priv->disconnect_info.peer_discon_ies.data) + return; + + qdf_mem_copy(mlme_priv->disconnect_info.peer_discon_ies.data, + ie->data, ie->len); + mlme_priv->disconnect_info.peer_discon_ies.len = ie->len; + + mlme_legacy_debug("peer disconnect IEs"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLME, QDF_TRACE_LEVEL_DEBUG, + mlme_priv->disconnect_info.peer_discon_ies.data, + mlme_priv->disconnect_info.peer_discon_ies.len); +} + +void mlme_free_peer_disconnect_ies(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + if (mlme_priv->disconnect_info.peer_discon_ies.data) { + qdf_mem_free(mlme_priv->disconnect_info.peer_discon_ies.data); + mlme_priv->disconnect_info.peer_discon_ies.data = NULL; + mlme_priv->disconnect_info.peer_discon_ies.len = 0; + } +} + +struct wlan_ies *mlme_get_peer_disconnect_ies(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return NULL; + } + + return &mlme_priv->disconnect_info.peer_discon_ies; +} + +void mlme_set_follow_ap_edca_flag(struct wlan_objmgr_vdev *vdev, bool flag) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + mlme_priv->follow_ap_edca = flag; +} + +bool mlme_get_follow_ap_edca_flag(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return false; + } + + return mlme_priv->follow_ap_edca; +} + +void mlme_set_reconn_after_assoc_timeout_flag(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool flag) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + + if (!psoc) + return; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) + return; + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + return; + } + + mlme_priv->reconn_after_assoc_timeout = flag; + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); +} + +bool mlme_get_reconn_after_assoc_timeout_flag(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + bool reconn_after_assoc_timeout; + + if (!psoc) + return false; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) + return false; + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + return false; + } + + reconn_after_assoc_timeout = mlme_priv->reconn_after_assoc_timeout; + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + + return reconn_after_assoc_timeout; +} + +void mlme_set_peer_pmf_status(struct wlan_objmgr_peer *peer, + bool is_pmf_enabled) +{ + struct peer_mlme_priv_obj *peer_priv; + + peer_priv = wlan_objmgr_peer_get_comp_private_obj(peer, + WLAN_UMAC_COMP_MLME); + if (!peer_priv) { + mlme_legacy_err(" peer mlme component object is NULL"); + return; + } + peer_priv->is_pmf_enabled = is_pmf_enabled; +} + +bool mlme_get_peer_pmf_status(struct wlan_objmgr_peer *peer) +{ + struct peer_mlme_priv_obj *peer_priv; + + peer_priv = wlan_objmgr_peer_get_comp_private_obj(peer, + WLAN_UMAC_COMP_MLME); + if (!peer_priv) { + mlme_legacy_err("peer mlme component object is NULL"); + return false; + } + + return peer_priv->is_pmf_enabled; +} + +void mlme_set_discon_reason_n_from_ap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool from_ap, + uint32_t reason_code) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + + if (!psoc) + return; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) + return; + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + return; + } + + mlme_priv->disconnect_info.from_ap = from_ap; + mlme_priv->disconnect_info.discon_reason = reason_code; + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); +} + +void mlme_get_discon_reason_n_from_ap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool *from_ap, + uint32_t *reason_code) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + + if (!psoc) + return; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) + return; + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + return; + } + + *from_ap = mlme_priv->disconnect_info.from_ap; + *reason_code = mlme_priv->disconnect_info.discon_reason; + mlme_priv->disconnect_info.from_ap = false; + mlme_priv->disconnect_info.discon_reason = 0; + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); +} + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +static +const char *mlme_roam_state_to_string(enum roam_offload_state state) +{ + switch (state) { + case ROAM_INIT: + return "ROAM_INIT"; + case ROAM_DEINIT: + return "ROAM_DEINIT"; + case ROAM_RSO_STARTED: + return "ROAM_RSO_STARTED"; + case ROAM_RSO_STOPPED: + return "ROAM_RSO_STOPPED"; + default: + return ""; + } +} + +static void +mlme_print_roaming_state(uint8_t vdev_id, enum roam_offload_state cur_state, + enum roam_offload_state new_state) +{ + mlme_debug("ROAM: vdev %d: %s(%d) --> %s(%d)", + vdev_id, mlme_roam_state_to_string(cur_state), cur_state, + mlme_roam_state_to_string(new_state), new_state); + + /* TODO: Try to print the state change requestor also */ +} + +bool +mlme_get_supplicant_disabled_roaming(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + bool value; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + + if (!vdev) { + mlme_legacy_err("vdev object is NULL"); + return 0; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + return 0; + } + + value = mlme_priv->mlme_roam.roam_cfg.supplicant_disabled_roaming; + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + + return value; +} + +void mlme_set_supplicant_disabled_roaming(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool val) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + + if (!vdev) { + mlme_legacy_err("vdev object is NULL"); + return; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + return; + } + + mlme_priv->mlme_roam.roam_cfg.supplicant_disabled_roaming = val; + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); +} + +uint32_t +mlme_get_roam_trigger_bitmap(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + uint32_t roam_bitmap; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + + if (!vdev) { + mlme_legacy_err("vdev object is NULL"); + return 0; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + return 0; + } + + roam_bitmap = mlme_priv->mlme_roam.roam_cfg.roam_trigger_bitmap; + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + + return roam_bitmap; +} + +void mlme_set_roam_trigger_bitmap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t val) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + if (!vdev) { + mlme_legacy_err("vdev object is NULL"); + return; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + return; + } + + mlme_priv->mlme_roam.roam_cfg.roam_trigger_bitmap = val; + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); +} + +uint8_t +mlme_get_operations_bitmap(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + uint8_t bitmap; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + + if (!vdev) { + mlme_legacy_err("vdev object is NULL"); + return 0xFF; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + return 0xFF; + } + + bitmap = mlme_priv->mlme_roam.roam_sm.mlme_operations_bitmap; + mlme_legacy_debug("vdev[%d] bitmap[0x%x]", vdev_id, + mlme_priv->mlme_roam.roam_sm.mlme_operations_bitmap); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + + return bitmap; +} + +void +mlme_set_operations_bitmap(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + enum roam_control_requestor reqs, bool clear) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + if (!vdev) { + mlme_legacy_err("vdev object is NULL"); + return; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + return; + } + + if (clear) + mlme_priv->mlme_roam.roam_sm.mlme_operations_bitmap &= ~reqs; + else + mlme_priv->mlme_roam.roam_sm.mlme_operations_bitmap |= reqs; + + mlme_legacy_debug("vdev[%d] bitmap[0x%x], reqs: %d, clear: %d", vdev_id, + mlme_priv->mlme_roam.roam_sm.mlme_operations_bitmap, + reqs, clear); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); +} + +void +mlme_clear_operations_bitmap(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + + if (!vdev) { + mlme_legacy_err("vdev object is NULL"); + return; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + return; + } + + mlme_priv->mlme_roam.roam_sm.mlme_operations_bitmap = 0; + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); +} + +QDF_STATUS mlme_get_cfg_wlm_level(struct wlan_objmgr_psoc *psoc, + uint8_t *level) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *level = mlme_obj->cfg.wlm_config.latency_level; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_get_cfg_wlm_reset(struct wlan_objmgr_psoc *psoc, + bool *reset) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *reset = mlme_obj->cfg.wlm_config.latency_reset; + + return QDF_STATUS_SUCCESS; +} + +enum roam_offload_state +mlme_get_roam_state(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + enum roam_offload_state roam_state; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + + if (!vdev) { + mlme_legacy_err("vdev object is NULL"); + return ROAM_DEINIT; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + return ROAM_DEINIT; + } + + roam_state = mlme_priv->mlme_roam.roam_sm.state; + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + + return roam_state; +} + +void mlme_set_roam_state(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + enum roam_offload_state new_state) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_legacy_priv *mlme_priv; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + + if (!vdev) { + mlme_legacy_err("vdev object is NULL"); + return; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + return; + } + + mlme_print_roaming_state(vdev_id, mlme_priv->mlme_roam.roam_sm.state, + new_state); + mlme_priv->mlme_roam.roam_sm.state = new_state; + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); +} + +bool wlan_is_vdev_id_up(struct wlan_objmgr_pdev *pdev, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + bool is_up = false; + + if (!pdev) + return is_up; + + vdev = wlan_objmgr_get_vdev_by_id_from_pdev(pdev, vdev_id, + WLAN_LEGACY_MAC_ID); + if (vdev) { + is_up = QDF_IS_STATUS_SUCCESS(wlan_vdev_is_up(vdev)); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + } + + return is_up; +} +#endif diff --git a/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_vdev_mgr_interface.c b/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_vdev_mgr_interface.c new file mode 100644 index 0000000000000000000000000000000000000000..a40ca88ffcc91a6fac329e1ee978a399acabb3c5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_vdev_mgr_interface.c @@ -0,0 +1,1444 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define internal APIs related to the mlme component + */ +#include "wlan_mlme_main.h" +#include "wlan_mlme_vdev_mgr_interface.h" +#include "lim_utils.h" +#include "wma_api.h" +#include "lim_types.h" +#include +#include <../../core/src/vdev_mgr_ops.h> +#include "wlan_psoc_mlme_api.h" +#include "wlan_crypto_global_api.h" + +static struct vdev_mlme_ops sta_mlme_ops; +static struct vdev_mlme_ops ap_mlme_ops; +static struct vdev_mlme_ops mon_mlme_ops; +static struct mlme_ext_ops ext_ops; + +bool mlme_is_vdev_in_beaconning_mode(enum QDF_OPMODE vdev_opmode) +{ + switch (vdev_opmode) { + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + case QDF_IBSS_MODE: + case QDF_NDI_MODE: + return true; + default: + return false; + } +} + +/** + * mlme_get_global_ops() - Register ext global ops + * + * Return: ext_ops global ops + */ +static struct mlme_ext_ops *mlme_get_global_ops(void) +{ + return &ext_ops; +} + +QDF_STATUS mlme_register_mlme_ext_ops(void) +{ + mlme_set_ops_register_cb(mlme_get_global_ops); + return QDF_STATUS_SUCCESS; +} + +/** + * mlme_register_vdev_mgr_ops() - Register vdev mgr ops + * @vdev_mlme: vdev mlme object + * + * This function is called to register vdev manager operations + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_register_vdev_mgr_ops(struct vdev_mlme_obj *vdev_mlme) +{ + struct wlan_objmgr_vdev *vdev; + + vdev = vdev_mlme->vdev; + + if (mlme_is_vdev_in_beaconning_mode(vdev->vdev_mlme.vdev_opmode)) + vdev_mlme->ops = &ap_mlme_ops; + else if (vdev->vdev_mlme.vdev_opmode == QDF_MONITOR_MODE) + vdev_mlme->ops = &mon_mlme_ops; + else + vdev_mlme->ops = &sta_mlme_ops; + + return QDF_STATUS_SUCCESS; +} + +/** + * mlme_unregister_vdev_mgr_ops() - Unregister vdev mgr ops + * @vdev_mlme: vdev mlme object + * + * This function is called to unregister vdev manager operations + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_unregister_vdev_mgr_ops(struct vdev_mlme_obj *vdev_mlme) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * sta_mlme_vdev_start_send() - MLME vdev start callback + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to initiate actions of VDEV.start + * + * Return: QDF_STATUS + */ +static QDF_STATUS sta_mlme_vdev_start_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t event_data_len, + void *event_data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_sta_mlme_vdev_start_send(vdev_mlme, event_data_len, + event_data); +} + +/** + * sta_mlme_start_continue() - vdev start rsp calback + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to handle the VDEV START/RESTART calback + * + * Return: QDF_STATUS + */ +static QDF_STATUS sta_mlme_start_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_sta_mlme_vdev_start_continue(vdev_mlme, data_len, data); +} + +/** + * sta_mlme_vdev_restart_send() - MLME vdev restart send + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to initiate actions of VDEV.start + * + * Return: QDF_STATUS + */ +static QDF_STATUS sta_mlme_vdev_restart_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t event_data_len, + void *event_data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_sta_mlme_vdev_restart_send(vdev_mlme, event_data_len, + event_data); +} + +/** + * sta_mlme_vdev_start_req_failed() - MLME start fail callback + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to send the vdev stop to firmware + * + * Return: QDF_STATUS + */ +static QDF_STATUS sta_mlme_vdev_start_req_failed(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, + void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_sta_mlme_vdev_req_fail(vdev_mlme, data_len, data); +} + +/** + * sta_mlme_vdev_start_connection() - MLME vdev start callback + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to initiate actions of STA connection + * + * Return: QDF_STATUS + */ +static QDF_STATUS sta_mlme_vdev_start_connection(struct vdev_mlme_obj *vdev_mlme, + uint16_t event_data_len, + void *event_data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return QDF_STATUS_SUCCESS; +} + +/** + * sta_mlme_vdev_up_send() - MLME vdev UP callback + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to send the vdev up command + * + * Return: QDF_STATUS + */ +static QDF_STATUS sta_mlme_vdev_up_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t event_data_len, + void *event_data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_sta_vdev_up_send(vdev_mlme, event_data_len, event_data); +} + +/** + * sta_mlme_vdev_notify_up_complete() - MLME vdev UP complete callback + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to VDEV MLME on moving + * to UP state + * + * Return: QDF_STATUS + */ +static QDF_STATUS sta_mlme_vdev_notify_up_complete(struct vdev_mlme_obj *vdev_mlme, + uint16_t event_data_len, + void *event_data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return QDF_STATUS_SUCCESS; +} + +/** + * sta_mlme_vdev_notify_roam_start() - MLME vdev Roam start callback + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to VDEV MLME on roaming + * to UP state + * + * Return: QDF_STATUS + */ +static +QDF_STATUS sta_mlme_vdev_notify_roam_start(struct vdev_mlme_obj *vdev_mlme, + uint16_t event_data_len, + void *event_data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_sta_mlme_vdev_roam_notify(vdev_mlme, event_data_len, + event_data); +} + +/** + * sta_mlme_vdev_disconnect_bss() - MLME vdev disconnect bss callback + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to disconnect BSS/send deauth to AP + * + * Return: QDF_STATUS + */ +static QDF_STATUS sta_mlme_vdev_disconnect_bss(struct vdev_mlme_obj *vdev_mlme, + uint16_t event_data_len, + void *event_data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_sta_mlme_vdev_disconnect_bss(vdev_mlme, event_data_len, + event_data); +} + +/** + * sta_mlme_vdev_stop_send() - MLME vdev stop send callback + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to send the vdev stop to firmware + * + * Return: QDF_STATUS + */ +static QDF_STATUS sta_mlme_vdev_stop_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, + void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_sta_mlme_vdev_stop_send(vdev_mlme, data_len, data); +} + +/** + * vdevmgr_mlme_stop_continue() - MLME vdev stop send callback + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to initiate operations on + * LMAC/FW stop response such as remove peer. + * + * Return: QDF_STATUS + */ +static QDF_STATUS vdevmgr_mlme_stop_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, + void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_mlme_vdev_stop_continue(vdev_mlme, data_len, data); +} + +/** + * ap_mlme_vdev_start_send () - send vdev start req + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to initiate actions of VDEV start ie start bss + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_mlme_vdev_start_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_ap_mlme_vdev_start_send(vdev_mlme, data_len, data); +} + +/** + * ap_start_continue () - vdev start rsp calback + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to handle the VDEV START/RESTART calback + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_mlme_start_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_ap_mlme_vdev_start_continue(vdev_mlme, data_len, data); +} + +/** + * ap_mlme_vdev_update_beacon() - callback to initiate beacon update + * @vdev_mlme: vdev mlme object + * @op: beacon operation + * @data_len: event data length + * @data: event data + * + * This function is called to update beacon + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_mlme_vdev_update_beacon(struct vdev_mlme_obj *vdev_mlme, + enum beacon_update_op op, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_ap_mlme_vdev_update_beacon(vdev_mlme, op, data_len, data); +} + +/** + * ap_mlme_vdev_up_send() - callback to send vdev up + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to send vdev up req + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_mlme_vdev_up_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_ap_mlme_vdev_up_send(vdev_mlme, data_len, data); +} + +/** + * ap_mlme_vdev_notify_up_complete() - callback to notify up completion + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to indicate up is completed + * + * Return: QDF_STATUS + */ +static QDF_STATUS +ap_mlme_vdev_notify_up_complete(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + if (!vdev_mlme) { + mlme_legacy_err("data is NULL"); + return QDF_STATUS_E_INVAL; + } + + pe_debug("Vdev %d is up", wlan_vdev_get_id(vdev_mlme->vdev)); + + return QDF_STATUS_SUCCESS; +} + +/** + * ap_mlme_vdev_disconnect_peers() - callback to disconnect all connected peers + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to disconnect all connected peers + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_mlme_vdev_disconnect_peers(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_ap_mlme_vdev_disconnect_peers(vdev_mlme, data_len, data); +} + +/** + * ap_mlme_vdev_stop_send() - callback to send stop vdev request + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to send stop vdev request + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_mlme_vdev_stop_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_ap_mlme_vdev_stop_send(vdev_mlme, data_len, data); +} + +/** + * ap_mlme_vdev_is_newchan_no_cac - VDEV SM CSA complete notification + * @vdev_mlme: VDEV MLME comp object + * + * On CSA complete, checks whether Channel does not needs CAC period, if + * it doesn't need cac return SUCCESS else FAILURE + * + * Return: SUCCESS if new channel doesn't need cac + * else FAILURE + */ +static QDF_STATUS +ap_mlme_vdev_is_newchan_no_cac(struct vdev_mlme_obj *vdev_mlme) +{ + bool cac_required; + + cac_required = mlme_get_cac_required(vdev_mlme->vdev); + mlme_legacy_debug("vdev id = %d cac_required %d", + vdev_mlme->vdev->vdev_objmgr.vdev_id, cac_required); + + if (!cac_required) + return QDF_STATUS_SUCCESS; + + mlme_set_cac_required(vdev_mlme->vdev, false); + + return QDF_STATUS_E_FAILURE; +} + +/** + * ap_mlme_vdev_down_send() - callback to send vdev down req + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to send vdev down req + * + * Return: QDF_STATUS + */ +static QDF_STATUS vdevmgr_mlme_vdev_down_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_ap_mlme_vdev_down_send(vdev_mlme, data_len, data); +} + +/** + * vdevmgr_notify_down_complete() - callback to indicate vdev down is completed + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to indicate vdev down is completed + * + * Return: QDF_STATUS + */ +static QDF_STATUS vdevmgr_notify_down_complete(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_mlme_vdev_notify_down_complete(vdev_mlme, data_len, data); +} + +/** + * ap_mlme_vdev_start_req_failed () - vdev start req fail callback + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to handle vdev start req/rsp failure + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_mlme_vdev_start_req_failed(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_ap_mlme_vdev_start_req_failed(vdev_mlme, data_len, data); +} + +/** + * ap_mlme_vdev_restart_send() a callback to send vdev restart + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to initiate and send vdev restart req + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_mlme_vdev_restart_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_ap_mlme_vdev_restart_send(vdev_mlme, data_len, data); +} + +/** + * ap_mlme_vdev_stop_start_send() - handle vdev stop during start req + * @vdev_mlme: vdev mlme object + * @type: restart req or start req + * @data_len: event data length + * @data: event data + * + * This function is called to handle vdev stop during start req + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_mlme_vdev_stop_start_send(struct vdev_mlme_obj *vdev_mlme, + enum vdev_cmd_type type, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_ap_mlme_vdev_stop_start_send(vdev_mlme, type, + data_len, data); +} + +QDF_STATUS mlme_set_chan_switch_in_progress(struct wlan_objmgr_vdev *vdev, + bool val) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mlme_priv->chan_switch_in_progress = val; + + return QDF_STATUS_SUCCESS; +} + +bool mlme_is_chan_switch_in_progress(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return false; + } + + return mlme_priv->chan_switch_in_progress; +} + +QDF_STATUS +ap_mlme_set_hidden_ssid_restart_in_progress(struct wlan_objmgr_vdev *vdev, + bool val) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mlme_priv->hidden_ssid_restart_in_progress = val; + + return QDF_STATUS_SUCCESS; +} + +bool ap_mlme_is_hidden_ssid_restart_in_progress(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return false; + } + + return mlme_priv->hidden_ssid_restart_in_progress; +} + +QDF_STATUS mlme_set_connection_fail(struct wlan_objmgr_vdev *vdev, bool val) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mlme_priv->connection_fail = val; + + return QDF_STATUS_SUCCESS; +} + +bool mlme_is_connection_fail(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return false; + } + + return mlme_priv->connection_fail; +} + +#ifdef FEATURE_WLAN_WAPI +static void mlme_is_sta_vdev_wapi(struct wlan_objmgr_pdev *pdev, + void *object, void *arg) +{ + struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)object; + int32_t keymgmt; + bool *is_wapi_sta_exist = (bool *)arg; + QDF_STATUS status; + + if (*is_wapi_sta_exist) + return; + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE) + return; + + status = wlan_vdev_is_up(vdev); + if (QDF_IS_STATUS_ERROR(status)) + return; + + keymgmt = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT); + if (keymgmt < 0) + return; + + if (keymgmt & ((1 << WLAN_CRYPTO_KEY_MGMT_WAPI_PSK) | + (1 << WLAN_CRYPTO_KEY_MGMT_WAPI_CERT))) { + *is_wapi_sta_exist = true; + mlme_debug("wapi exist for Vdev: %d", + wlan_vdev_get_id(vdev)); + } +} + +bool mlme_is_wapi_sta_active(struct wlan_objmgr_pdev *pdev) +{ + bool is_wapi_sta_exist = false; + + wlan_objmgr_pdev_iterate_obj_list(pdev, + WLAN_VDEV_OP, + mlme_is_sta_vdev_wapi, + &is_wapi_sta_exist, 0, + WLAN_MLME_OBJMGR_ID); + + return is_wapi_sta_exist; +} +#endif + +QDF_STATUS mlme_set_assoc_type(struct wlan_objmgr_vdev *vdev, + enum vdev_assoc_type assoc_type) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mlme_priv->assoc_type = assoc_type; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_get_vdev_bss_peer_mac_addr( + struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *bss_peer_mac_address) +{ + struct wlan_objmgr_peer *peer; + + if (!vdev) { + mlme_legacy_err("vdev is null"); + return QDF_STATUS_E_INVAL; + } + + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_MLME_OBJMGR_ID); + if (!peer) { + mlme_legacy_err("peer is null"); + return QDF_STATUS_E_INVAL; + } + wlan_peer_obj_lock(peer); + qdf_mem_copy(bss_peer_mac_address->bytes, wlan_peer_get_macaddr(peer), + QDF_MAC_ADDR_SIZE); + wlan_peer_obj_unlock(peer); + + wlan_objmgr_peer_release_ref(peer, WLAN_MLME_OBJMGR_ID); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_get_vdev_stop_type(struct wlan_objmgr_vdev *vdev, + uint32_t *vdev_stop_type) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + *vdev_stop_type = mlme_priv->vdev_stop_type; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_set_vdev_stop_type(struct wlan_objmgr_vdev *vdev, + uint32_t vdev_stop_type) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mlme_priv->vdev_stop_type = vdev_stop_type; + + return QDF_STATUS_SUCCESS; +} + +enum vdev_assoc_type mlme_get_assoc_type(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return false; + } + + return mlme_priv->assoc_type; +} + +QDF_STATUS +mlme_set_vdev_start_failed(struct wlan_objmgr_vdev *vdev, bool val) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mlme_priv->vdev_start_failed = val; + + return QDF_STATUS_SUCCESS; +} + +bool mlme_get_vdev_start_failed(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return false; + } + + return mlme_priv->vdev_start_failed; +} + +QDF_STATUS mlme_set_cac_required(struct wlan_objmgr_vdev *vdev, bool val) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mlme_priv->cac_required_for_new_channel = val; + + return QDF_STATUS_SUCCESS; +} + +bool mlme_get_cac_required(struct wlan_objmgr_vdev *vdev) +{ + struct mlme_legacy_priv *mlme_priv; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return false; + } + + return mlme_priv->cac_required_for_new_channel; +} + +QDF_STATUS mlme_set_mbssid_info(struct wlan_objmgr_vdev *vdev, + struct scan_mbssid_info *mbssid_info) +{ + struct vdev_mlme_obj *vdev_mlme; + struct vdev_mlme_mbss_11ax *mbss_11ax; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + mlme_legacy_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mbss_11ax = &vdev_mlme->mgmt.mbss_11ax; + mbss_11ax->profile_idx = mbssid_info->profile_num; + mbss_11ax->profile_num = mbssid_info->profile_count; + qdf_mem_copy(mbss_11ax->trans_bssid, + mbssid_info->trans_bssid, QDF_MAC_ADDR_SIZE); + + return QDF_STATUS_SUCCESS; +} + +void mlme_get_mbssid_info(struct wlan_objmgr_vdev *vdev, + struct vdev_mlme_mbss_11ax *mbss_11ax) +{ + struct vdev_mlme_obj *vdev_mlme; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + mlme_legacy_err("vdev component object is NULL"); + return; + } + + mbss_11ax = &vdev_mlme->mgmt.mbss_11ax; +} + +QDF_STATUS mlme_set_tx_power(struct wlan_objmgr_vdev *vdev, + int8_t tx_power) +{ + struct vdev_mlme_obj *vdev_mlme; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + + if (!vdev_mlme) { + mlme_legacy_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + vdev_mlme->mgmt.generic.tx_power = tx_power; + + return QDF_STATUS_SUCCESS; +} + +int8_t mlme_get_tx_power(struct wlan_objmgr_vdev *vdev) +{ + struct vdev_mlme_obj *vdev_mlme; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + mlme_legacy_err("vdev component object is NULL"); + return QDF_STATUS_E_INVAL; + } + + return vdev_mlme->mgmt.generic.tx_power; +} + +QDF_STATUS mlme_set_max_reg_power(struct wlan_objmgr_vdev *vdev, + int8_t max_reg_power) +{ + struct vdev_mlme_obj *vdev_mlme; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + + if (!vdev_mlme) { + mlme_legacy_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + vdev_mlme->mgmt.generic.maxregpower = max_reg_power; + + return QDF_STATUS_SUCCESS; +} + +int8_t mlme_get_max_reg_power(struct wlan_objmgr_vdev *vdev) +{ + struct vdev_mlme_obj *vdev_mlme; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + mlme_legacy_err("vdev component object is NULL"); + return QDF_STATUS_E_INVAL; + } + + return vdev_mlme->mgmt.generic.maxregpower; +} + +/** + * mlme_get_vdev_types() - get vdev type and subtype from its operation mode + * @mode: operation mode of vdev + * @type: type of vdev + * @sub_type: sub_type of vdev + * + * This API is called to get vdev type and subtype from its operation mode. + * Vdev operation modes are defined in enum QDF_OPMODE. + * + * Type of vdev are WLAN_VDEV_MLME_TYPE_AP, WLAN_VDEV_MLME_TYPE_STA, + * WLAN_VDEV_MLME_TYPE_IBSS, ,WLAN_VDEV_MLME_TYPE_MONITOR, + * WLAN_VDEV_MLME_TYPE_NAN, WLAN_VDEV_MLME_TYPE_OCB, WLAN_VDEV_MLME_TYPE_NDI + * + * Sub_types of vdev are WLAN_VDEV_MLME_SUBTYPE_P2P_DEVICE, + * WLAN_VDEV_MLME_SUBTYPE_P2P_CLIENT, WLAN_VDEV_MLME_SUBTYPE_P2P_GO, + * WLAN_VDEV_MLME_SUBTYPE_PROXY_STA, WLAN_VDEV_MLME_SUBTYPE_MESH + * Return: QDF_STATUS + */ + +static QDF_STATUS mlme_get_vdev_types(enum QDF_OPMODE mode, uint8_t *type, + uint8_t *sub_type) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + *type = 0; + *sub_type = 0; + + switch (mode) { + case QDF_STA_MODE: + *type = WLAN_VDEV_MLME_TYPE_STA; + break; + case QDF_SAP_MODE: + *type = WLAN_VDEV_MLME_TYPE_AP; + break; + case QDF_P2P_DEVICE_MODE: + *type = WLAN_VDEV_MLME_TYPE_AP; + *sub_type = WLAN_VDEV_MLME_SUBTYPE_P2P_DEVICE; + break; + case QDF_P2P_CLIENT_MODE: + *type = WLAN_VDEV_MLME_TYPE_STA; + *sub_type = WLAN_VDEV_MLME_SUBTYPE_P2P_CLIENT; + break; + case QDF_P2P_GO_MODE: + *type = WLAN_VDEV_MLME_TYPE_AP; + *sub_type = WLAN_VDEV_MLME_SUBTYPE_P2P_GO; + break; + case QDF_OCB_MODE: + *type = WLAN_VDEV_MLME_TYPE_OCB; + break; + case QDF_IBSS_MODE: + *type = WLAN_VDEV_MLME_TYPE_IBSS; + break; + case QDF_MONITOR_MODE: + *type = WMI_HOST_VDEV_TYPE_MONITOR; + break; + case QDF_NDI_MODE: + *type = WLAN_VDEV_MLME_TYPE_NDI; + break; + case QDF_NAN_DISC_MODE: + *type = WLAN_VDEV_MLME_TYPE_NAN; + break; + default: + mlme_err("Invalid device mode %d", mode); + status = QDF_STATUS_E_INVAL; + break; + } + return status; +} + +/** + * vdevmgr_mlme_ext_hdl_create () - Create mlme legacy priv object + * @vdev_mlme: vdev mlme object + * + * Return: QDF_STATUS + */ +static +QDF_STATUS vdevmgr_mlme_ext_hdl_create(struct vdev_mlme_obj *vdev_mlme) +{ + QDF_STATUS status; + + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + vdev_mlme->ext_vdev_ptr = + qdf_mem_malloc(sizeof(struct mlme_legacy_priv)); + if (!vdev_mlme->ext_vdev_ptr) { + mlme_legacy_err("failed to allocate meory for ext_vdev_ptr"); + return QDF_STATUS_E_NOMEM; + } + + sme_get_vdev_type_nss(wlan_vdev_mlme_get_opmode(vdev_mlme->vdev), + &vdev_mlme->proto.generic.nss_2g, + &vdev_mlme->proto.generic.nss_5g); + + status = mlme_get_vdev_types(wlan_vdev_mlme_get_opmode(vdev_mlme->vdev), + &vdev_mlme->mgmt.generic.type, + &vdev_mlme->mgmt.generic.subtype); + if (QDF_IS_STATUS_ERROR(status)) { + mlme_err("Get vdev type failed; status:%d", status); + qdf_mem_free(vdev_mlme->ext_vdev_ptr); + return status; + } + + status = vdev_mgr_create_send(vdev_mlme); + if (QDF_IS_STATUS_ERROR(status)) { + mlme_err("Failed to create vdev for vdev id %d", + wlan_vdev_get_id(vdev_mlme->vdev)); + qdf_mem_free(vdev_mlme->ext_vdev_ptr); + return status; + } + + return status; +} + +/** + * vdevmgr_mlme_ext_hdl_destroy () - Destroy mlme legacy priv object + * @vdev_mlme: vdev mlme object + * + * Return: QDF_STATUS + */ +static +QDF_STATUS vdevmgr_mlme_ext_hdl_destroy(struct vdev_mlme_obj *vdev_mlme) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + + if (!vdev_mlme->ext_vdev_ptr) + return QDF_STATUS_E_FAILURE; + + mlme_free_self_disconnect_ies(vdev_mlme->vdev); + mlme_free_peer_disconnect_ies(vdev_mlme->vdev); + mlme_free_sae_auth_retry(vdev_mlme->vdev); + qdf_mem_free(vdev_mlme->ext_vdev_ptr); + vdev_mlme->ext_vdev_ptr = NULL; + + return QDF_STATUS_SUCCESS; +} + +/** + * ap_vdev_dfs_cac_timer_stop() – callback to stop cac timer + * @vdev_mlme: vdev mlme object + * @event_data_len: event data length + * @event_data: event data + * + * This function is called to stop cac timer + * + * Return: QDF_STATUS + */ +static QDF_STATUS ap_vdev_dfs_cac_timer_stop(struct vdev_mlme_obj *vdev_mlme, + uint16_t event_data_len, + void *event_data) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return QDF_STATUS_SUCCESS; +} + +/** + * mon_mlme_vdev_start_restart_send () - send vdev start/restart req + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to initiate actions of VDEV start/restart + * + * Return: QDF_STATUS + */ +static QDF_STATUS mon_mlme_vdev_start_restart_send( + struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return lim_mon_mlme_vdev_start_send(vdev_mlme, data_len, data); +} + +/** + * mon_start_continue () - vdev start rsp calback + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to handle the VDEV START/RESTART calback + * + * Return: QDF_STATUS + */ +static QDF_STATUS mon_mlme_start_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_mon_mlme_vdev_start_continue(vdev_mlme, data_len, data); +} + +/** + * mon_mlme_vdev_up_send() - callback to send vdev up + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to send vdev up req + * + * Return: QDF_STATUS + */ +static QDF_STATUS mon_mlme_vdev_up_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_mon_mlme_vdev_up_send(vdev_mlme, data_len, data); +} + +/** + * mon_mlme_vdev_disconnect_peers() - callback to disconnect all connected peers + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * montior mode no connected peers, only do VDEV state transition. + * + * Return: QDF_STATUS + */ +static QDF_STATUS mon_mlme_vdev_disconnect_peers( + struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wlan_vdev_mlme_sm_deliver_evt( + vdev_mlme->vdev, + WLAN_VDEV_SM_EV_DISCONNECT_COMPLETE, + 0, NULL); +} + +/** + * mon_mlme_vdev_stop_send() - callback to send stop vdev request + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to send stop vdev request + * + * Return: QDF_STATUS + */ +static QDF_STATUS mon_mlme_vdev_stop_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_mon_mlme_vdev_stop_send(vdev_mlme, data_len, data); +} + +/** + * mon_mlme_vdev_down_send() - callback to send vdev down req + * @vdev_mlme: vdev mlme object + * @data_len: event data length + * @data: event data + * + * This function is called to send vdev down req + * + * Return: QDF_STATUS + */ +static QDF_STATUS mon_mlme_vdev_down_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + mlme_legacy_debug("vdev id = %d", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_mon_mlme_vdev_down_send(vdev_mlme, data_len, data); +} + +/** + * vdevmgr_vdev_delete_rsp_handle() - callback to handle vdev delete response + * @vdev_mlme: vdev mlme object + * @rsp: pointer to vdev delete response + * + * This function is called to handle vdev delete response and send result to + * upper layer + * + * Return: QDF_STATUS + */ +static QDF_STATUS +vdevmgr_vdev_delete_rsp_handle(struct wlan_objmgr_psoc *psoc, + struct vdev_delete_response *rsp) +{ + mlme_legacy_debug("vdev id = %d ", rsp->vdev_id); + return wma_vdev_detach_callback(rsp); +} + +/** + * vdevmgr_vdev_stop_rsp_handle() - callback to handle vdev stop response + * @vdev_mlme: vdev mlme object + * @rsp: pointer to vdev stop response + * + * This function is called to handle vdev stop response and send result to + * upper layer + * + * Return: QDF_STATUS + */ +static QDF_STATUS +vdevmgr_vdev_stop_rsp_handle(struct vdev_mlme_obj *vdev_mlme, + struct vdev_stop_response *rsp) +{ + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + return wma_vdev_stop_resp_handler(vdev_mlme, rsp); +} + +/** + * psoc_mlme_ext_hdl_create() - Create mlme legacy priv object + * @psoc_mlme: psoc mlme object + * + * Return: QDF_STATUS + */ +static +QDF_STATUS psoc_mlme_ext_hdl_create(struct psoc_mlme_obj *psoc_mlme) +{ + psoc_mlme->ext_psoc_ptr = + qdf_mem_malloc(sizeof(struct wlan_mlme_psoc_ext_obj)); + if (!psoc_mlme->ext_psoc_ptr) { + mlme_legacy_err("Failed to allocate memory"); + return QDF_STATUS_E_NOMEM; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * psoc_mlme_ext_hdl_destroy() - Destroy mlme legacy priv object + * @psoc_mlme: psoc mlme object + * + * Return: QDF_STATUS + */ +static +QDF_STATUS psoc_mlme_ext_hdl_destroy(struct psoc_mlme_obj *psoc_mlme) +{ + if (!psoc_mlme) { + mlme_err("PSOC MLME is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (psoc_mlme->ext_psoc_ptr) { + qdf_mem_free(psoc_mlme->ext_psoc_ptr); + psoc_mlme->ext_psoc_ptr = NULL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * vdevmgr_vdev_delete_rsp_handle() - callback to handle vdev delete response + * @vdev_mlme: vdev mlme object + * @rsp: pointer to vdev delete response + * + * This function is called to handle vdev delete response and send result to + * upper layer + * + * Return: QDF_STATUS + */ +static QDF_STATUS +vdevmgr_vdev_start_rsp_handle(struct vdev_mlme_obj *vdev_mlme, + struct vdev_start_response *rsp) +{ + QDF_STATUS status; + + mlme_legacy_debug("vdev id = %d ", + vdev_mlme->vdev->vdev_objmgr.vdev_id); + status = wma_vdev_start_resp_handler(vdev_mlme, rsp); + + return status; +} + +QDF_STATUS mlme_vdev_self_peer_create(struct wlan_objmgr_vdev *vdev) +{ + struct vdev_mlme_obj *vdev_mlme; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + mlme_err("Failed to get vdev mlme obj for vdev id %d", + wlan_vdev_get_id(vdev)); + return QDF_STATUS_E_INVAL; + } + + return wma_vdev_self_peer_create(vdev_mlme); +} + +static +QDF_STATUS vdevmgr_mlme_ext_post_hdl_create(struct vdev_mlme_obj *vdev_mlme) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * struct sta_mlme_ops - VDEV MLME operation callbacks strucutre for sta + * @mlme_vdev_start_send: callback to initiate actions of VDEV + * MLME start operation + * @mlme_vdev_restart_send: callback to initiate actions of VDEV + * MLME restart operation + * @mlme_vdev_stop_start_send: callback to block start/restart VDEV + * request command + * @mlme_vdev_sta_conn_start: callback to initiate connection + * @mlme_vdev_start_continue: callback to initiate operations on + * LMAC/FW start response + * @mlme_vdev_up_send: callback to initiate actions of VDEV + * MLME up operation + * @mlme_vdev_notify_up_complete: callback to notify VDEV MLME on moving + * to UP state + * @mlme_vdev_update_beacon: callback to initiate beacon update + * @mlme_vdev_disconnect_peers: callback to initiate disconnection of + * peers + * @mlme_vdev_stop_send: callback to initiate actions of VDEV + * MLME stop operation + * @mlme_vdev_stop_continue: callback to initiate operations on + * LMAC/FW stop response + * @mlme_vdev_down_send: callback to initiate actions of VDEV + * MLME down operation + * @mlme_vdev_notify_down_complete: callback to notify VDEV MLME on moving + * to INIT state + */ +static struct vdev_mlme_ops sta_mlme_ops = { + .mlme_vdev_start_send = sta_mlme_vdev_start_send, + .mlme_vdev_restart_send = sta_mlme_vdev_restart_send, + .mlme_vdev_start_continue = sta_mlme_start_continue, + .mlme_vdev_start_req_failed = sta_mlme_vdev_start_req_failed, + .mlme_vdev_sta_conn_start = sta_mlme_vdev_start_connection, + .mlme_vdev_up_send = sta_mlme_vdev_up_send, + .mlme_vdev_notify_up_complete = sta_mlme_vdev_notify_up_complete, + .mlme_vdev_notify_roam_start = sta_mlme_vdev_notify_roam_start, + .mlme_vdev_disconnect_peers = sta_mlme_vdev_disconnect_bss, + .mlme_vdev_stop_send = sta_mlme_vdev_stop_send, + .mlme_vdev_stop_continue = vdevmgr_mlme_stop_continue, + .mlme_vdev_down_send = vdevmgr_mlme_vdev_down_send, + .mlme_vdev_notify_down_complete = vdevmgr_notify_down_complete, + .mlme_vdev_ext_stop_rsp = vdevmgr_vdev_stop_rsp_handle, + .mlme_vdev_ext_start_rsp = vdevmgr_vdev_start_rsp_handle, +}; + +/** + * struct ap_mlme_ops - VDEV MLME operation callbacks strucutre for beaconing + * interface + * @mlme_vdev_start_send: callback to initiate actions of VDEV + * MLME start operation + * @mlme_vdev_restart_send: callback to initiate actions of VDEV + * MLME restart operation + * @mlme_vdev_stop_start_send: callback to block start/restart VDEV + * request command + * @mlme_vdev_start_continue: callback to initiate operations on + * LMAC/FW start response + * @mlme_vdev_up_send: callback to initiate actions of VDEV + * MLME up operation + * @mlme_vdev_notify_up_complete: callback to notify VDEV MLME on moving + * to UP state + * @mlme_vdev_update_beacon: callback to initiate beacon update + * @mlme_vdev_disconnect_peers: callback to initiate disconnection of + * peers + * @mlme_vdev_dfs_cac_timer_stop: callback to stop the DFS CAC timer + * @mlme_vdev_stop_send: callback to initiate actions of VDEV + * MLME stop operation + * @mlme_vdev_stop_continue: callback to initiate operations on + * LMAC/FW stop response + * @mlme_vdev_down_send: callback to initiate actions of VDEV + * MLME down operation + * @mlme_vdev_notify_down_complete: callback to notify VDEV MLME on moving + * to INIT state + * @mlme_vdev_is_newchan_no_cac: callback to check if new channel is DFS + * and cac is not required + */ +static struct vdev_mlme_ops ap_mlme_ops = { + .mlme_vdev_start_send = ap_mlme_vdev_start_send, + .mlme_vdev_restart_send = ap_mlme_vdev_restart_send, + .mlme_vdev_stop_start_send = ap_mlme_vdev_stop_start_send, + .mlme_vdev_start_continue = ap_mlme_start_continue, + .mlme_vdev_start_req_failed = ap_mlme_vdev_start_req_failed, + .mlme_vdev_up_send = ap_mlme_vdev_up_send, + .mlme_vdev_notify_up_complete = ap_mlme_vdev_notify_up_complete, + .mlme_vdev_update_beacon = ap_mlme_vdev_update_beacon, + .mlme_vdev_disconnect_peers = ap_mlme_vdev_disconnect_peers, + .mlme_vdev_dfs_cac_timer_stop = ap_vdev_dfs_cac_timer_stop, + .mlme_vdev_stop_send = ap_mlme_vdev_stop_send, + .mlme_vdev_stop_continue = vdevmgr_mlme_stop_continue, + .mlme_vdev_down_send = vdevmgr_mlme_vdev_down_send, + .mlme_vdev_notify_down_complete = vdevmgr_notify_down_complete, + .mlme_vdev_is_newchan_no_cac = ap_mlme_vdev_is_newchan_no_cac, + .mlme_vdev_ext_stop_rsp = vdevmgr_vdev_stop_rsp_handle, + .mlme_vdev_ext_start_rsp = vdevmgr_vdev_start_rsp_handle, +}; + +static struct vdev_mlme_ops mon_mlme_ops = { + .mlme_vdev_start_send = mon_mlme_vdev_start_restart_send, + .mlme_vdev_restart_send = mon_mlme_vdev_start_restart_send, + .mlme_vdev_start_continue = mon_mlme_start_continue, + .mlme_vdev_up_send = mon_mlme_vdev_up_send, + .mlme_vdev_disconnect_peers = mon_mlme_vdev_disconnect_peers, + .mlme_vdev_stop_send = mon_mlme_vdev_stop_send, + .mlme_vdev_down_send = mon_mlme_vdev_down_send, + .mlme_vdev_ext_start_rsp = vdevmgr_vdev_start_rsp_handle, +}; + +static struct mlme_ext_ops ext_ops = { + .mlme_psoc_ext_hdl_create = psoc_mlme_ext_hdl_create, + .mlme_psoc_ext_hdl_destroy = psoc_mlme_ext_hdl_destroy, + .mlme_vdev_ext_hdl_create = vdevmgr_mlme_ext_hdl_create, + .mlme_vdev_ext_hdl_destroy = vdevmgr_mlme_ext_hdl_destroy, + .mlme_vdev_ext_hdl_post_create = vdevmgr_mlme_ext_post_hdl_create, + .mlme_vdev_ext_delete_rsp = vdevmgr_vdev_delete_rsp_handle, +}; diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme.h new file mode 100644 index 0000000000000000000000000000000000000000..94484241fc411b4fa4c1cdaeab5bf43bab427171 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CFG_MLME_H +#define __CFG_MLME_H + +#include "cfg_define.h" +#include "cfg_converged.h" +#include "qdf_types.h" +#include "cfg_mlme_wps_params.h" +#include "cfg_mlme_chainmask.h" +#include "cfg_mlme_edca_params.h" +#include "cfg_mlme_generic.h" +#include "cfg_mlme_ibss.h" +#include "cfg_mlme_acs.h" +#include "cfg_mlme_power.h" +#include "cfg_mlme_ht_caps.h" +#include "cfg_mlme_he_caps.h" +#include "cfg_mlme_lfr.h" +#include "cfg_mlme_obss_ht40.h" +#include "cfg_mlme_dfs.h" +#include "cfg_mlme_mbo.h" +#include "cfg_mlme_dot11mode.h" +#include "cfg_mlme_nss_chains.h" +#include "cfg_mlme_vht_caps.h" +#include "cfg_qos.h" +#include "cfg_mlme_timeout.h" +#include "cfg_mlme_rates.h" +#include "wlan_mlme_product_details_cfg.h" +#include "cfg_mlme_sta.h" +#include "cfg_sap_protection.h" +#include "cfg_mlme_fe_wmm.h" +#include "cfg_mlme_powersave.h" +#include "cfg_mlme_sap.h" +#include "cfg_mlme_stats.h" +#include "cfg_mlme_twt.h" +#include "cfg_mlme_scoring.h" +#include "cfg_mlme_oce.h" +#include "cfg_mlme_threshold.h" +#include "cfg_mlme_feature_flag.h" +#include "cfg_mlme_wep_params.h" +#include "cfg_mlme_wifi_pos.h" +#include "cfg_mlme_btm.h" +#include "cfg_mlme_fe_wlm.h" +#include "cfg_mlme_fe_rrm.h" +#include "cfg_mlme_mwc.h" +#include "cfg_mlme_reg.h" + +/* Please Maintain Alphabetic Order here */ +#define CFG_MLME_ALL \ + CFG_ACS_ALL \ + CFG_BTM_ALL \ + CFG_CHAINMASK_ALL \ + CFG_DFS_ALL \ + CFG_DOT11_MODE_ALL \ + CFG_EDCA_PARAMS_ALL \ + CFG_FE_RRM_ALL \ + CFG_FE_WLM_ALL \ + CFG_FEATURE_FLAG_ALL \ + CFG_GENERIC_ALL \ + CFG_HT_CAPS_ALL \ + CFG_HE_CAPS_ALL \ + CFG_IBSS_ALL \ + CFG_LFR_ALL \ + CFG_MBO_ALL \ + CFG_MLME_POWER_ALL \ + CFG_MLME_PRODUCT_DETAILS_ALL \ + CFG_MWC_ALL \ + CFG_NSS_CHAINS_ALL \ + CFG_OBSS_HT40_ALL \ + CFG_OCE_ALL \ + CFG_POWERSAVE_ALL \ + CFG_QOS_ALL \ + CFG_RATES_ALL \ + CFG_REG_ALL \ + CFG_SAP_ALL \ + CFG_SAP_PROTECTION_ALL \ + CFG_SCORING_ALL \ + CFG_STA_ALL \ + CFG_STATS_ALL \ + CFG_THRESHOLD_ALL \ + CFG_TIMEOUT_ALL \ + CFG_TWT_ALL \ + CFG_VHT_CAPS_ALL \ + CFG_WEP_PARAMS_ALL \ + CFG_WIFI_POS_ALL \ + CFG_WMM_PARAMS_ALL\ + CFG_WPS_ALL + +#endif /* __CFG_MLME_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_acs.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_acs.h new file mode 100644 index 0000000000000000000000000000000000000000..3cb820e6efd6d7bdd129dfda82e9ac0d2af0750a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_acs.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_ACS_H +#define __CFG_MLME_ACS_H + +/* + * + * acs_with_more_param- Enable acs calculation with more param. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable acs calculation with more param. + * + * Related: NA + * + * Supported Feature: ACS + * + * Usage: Internal/External + * + * + */ + +#define CFG_ACS_WITH_MORE_PARAM CFG_INI_BOOL( \ + "acs_with_more_param", \ + 0, \ + "Enable ACS with more param") + +/* + * + * AutoChannelSelectWeight - ACS channel weight + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 0x000000FF + * + * This ini is used to adjust weight of factors in + * acs algorithm. + * + * Supported Feature: ACS + * + * Usage: Internal/External + * + * bits 0-3: rssi weight + * bits 4-7: bss count weight + * bits 8-11: noise floor weight + * bits 12-15: channel free weight + * bits 16-19: tx power range weight + * bits 20-23: tx power throughput weight + * bits 24-31: reserved + * + * + */ + +#define CFG_AUTO_CHANNEL_SELECT_WEIGHT CFG_INI_UINT( \ + "AutoChannelSelectWeight", \ + 0, \ + 0xFFFFFFFF, \ + 0x000000FF, \ + CFG_VALUE_OR_DEFAULT, \ + "Adjust weight factor in ACS") + +/* + * + * gvendor_acs_support - vendor based channel selection manager + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Enabling this parameter will force driver to use user application based + * channel selection algo instead of driver based auto channel selection + * logic. + * + * Supported Feature: ACS + * + * Usage: External/Internal + * + * + */ + +#define CFG_USER_AUTO_CHANNEL_SELECTION CFG_INI_BOOL( \ + "gvendor_acs_support", \ + 0, \ + "Vendor channel selection manager") + +/* + * + * gacs_support_for_dfs_lte_coex - acs support for lte coex and dfs event + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Enabling this parameter will force driver to use user application based + * channel selection algo for channel selection in case of dfs and lte + * coex event. + * + * Supported Feature: ACS + * + * Usage: Internal + * + * + */ + +#define CFG_USER_ACS_DFS_LTE CFG_INI_BOOL( \ + "gacs_support_for_dfs_lte_coex", \ + 0, \ + "Acs support for lte coex and dfs") + +/* + * + * acs_policy - External ACS policy control + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Values are per enum hdd_external_acs_policy. + * + * This ini is used to control the external ACS policy. + * + * 0 -Preferable for ACS to select a + * channel with non-zero pcl weight. + * 1 -Mandatory for ACS to select a + * channel with non-zero pcl weight. + * + * Related: None + * + * Supported Feature: ACS + * + * Usage: Internal/External + * + * + */ + +#define CFG_EXTERNAL_ACS_POLICY CFG_INI_BOOL( \ + "acs_policy", \ + 1, \ + "External ACS Policy Control") + +#define ACS_WEIGHT_MAX_STR_LEN 500 + +/* + * + * normalize_acs_weight - Used to control the ACS channel weightage. + * + * This ini is used to specify the weight percentage of the channel. Channel + * weights can be controlled by user to prioritize or de-prioritize channels. + * + * Related: ACS + * + * Supported Feature: ACS + * + * Usage: External + * + * + */ +#define CFG_NORMALIZE_ACS_WEIGHT CFG_INI_STRING( \ + "normalize_acs_weight", \ + 0, \ + ACS_WEIGHT_MAX_STR_LEN, \ + "5940-7105=0, 5965=100, 6045=100, 6125=100, 6205=100, 6285=100, 6365=100, 6605=100, 6685=100, 6765=100, 6845=100", \ + "Used to specify the channel weights") + +/* + * + * force_start_sap- Enable the SAP even if no channel is suitable for SAP + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable the SAP even if no channel is found suitable + * for SAP by ACS. + * + * Related: NA + * + * Supported Feature: ACS + * + * Usage: Internal + * + * + */ +#define CFG_ACS_FORCE_START_SAP CFG_INI_BOOL( \ + "force_start_sap", \ + 0, \ + "Force start SAP") + +/* + * + * np_chan_weight - chan weightage for non preferred channels + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x00000000 + * + * This INI give percentage value of weights to be considered in the ACS algo + * for the non preferred channels. the distribution of the channel type is:- + * Example:- If the percentage of lets say DFS channels is set to 50%, and + * the weight comes out to be x, then we would increase the weight of DFS + * channels by 50% ( 100 - y% set in INI), so that it gets de-prioritized in + * the ACS sorted channel list, the lesser the weight, the better the channel. + * So the channel with more weight is less likely to be selected. So by default + * the np chan weightage for DFS is set to 0, that is it will be assigned max + * weightage, so no probality of getting selected, as for standlaone, DFS is not + * recommended (it takes 60 sec/10min to start depending upon channel type). + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): DFS - Def 0% + * 1 Index (BITS 8-15): Reserved + * 2 Index (BITS 16-23): Reserved + * 3 Index (BITS 24-31): Reserved + * These percentage values are stored in HEX. Max can be 0x64 + * Supported Feature: ACS + * + * Usage: External + * + * + */ +#define CFG_ACS_NP_CHAN_WEIGHT CFG_INI_UINT( \ + "np_chan_weight", \ + 0x00000000, \ + 0x64646464, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "np chan weight") + +#define CFG_ACS_ALL \ + CFG(CFG_ACS_WITH_MORE_PARAM) \ + CFG(CFG_AUTO_CHANNEL_SELECT_WEIGHT) \ + CFG(CFG_USER_AUTO_CHANNEL_SELECTION) \ + CFG(CFG_USER_ACS_DFS_LTE) \ + CFG(CFG_EXTERNAL_ACS_POLICY) \ + CFG(CFG_NORMALIZE_ACS_WEIGHT) \ + CFG(CFG_ACS_FORCE_START_SAP) \ + CFG(CFG_ACS_NP_CHAN_WEIGHT) + +#endif /* __CFG_MLME_ACS_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_btm.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_btm.h new file mode 100644 index 0000000000000000000000000000000000000000..56353fe1b82f0a81cad6008cc1b0f68e1c719726 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_btm.h @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains MLME BTM related CFG/INI Items. + */ + +#ifndef CFG_MLME_BTM_H_ +#define CFG_MLME_BTM_H_ + +/* + * + * prefer_btm_query - Prefer btm query over 11k neighbor report + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable the STA to send BTM query instead of + * 11k neighbor report. + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_PREFER_BTM_QUERY CFG_INI_BOOL( \ + "prefer_btm_query", \ + 1, \ + "prefer btm query over 11k neighbor report") + +/* + * + * prefer_roam_score_for_candidate_selection - choose to sort the candidates on + * roam score or preferred AP + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable the the firmware to sort the candidates + * based on the roam score rather than selecting APs as per the order + * of the APs sent by the connected AP. + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_ENABLE_BTM_ABRIDGE CFG_INI_BOOL( \ + "prefer_roam_score_for_candidate_selection", \ + 1, \ + "sort candidate based on roam score") + +/* + * + * btm_offload_config - Configure BTM + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x000001c1 + * + * This ini is used to configure BTM + * + * Bit 0: Enable/Disable the BTM offload. Set this to 1 will + * enable and 0 will disable BTM offload. + * + * BIT 2, 1: Action on non matching candidate with cache. If a BTM request + * is received from AP then the candidate AP's may/may-not be present in + * the firmware scan cache . Based on below config firmware will decide + * whether to forward BTM frame to host or consume with firmware and proceed + * with Roaming to candidate AP. + * 00 scan and consume + * 01 no scan and forward to host + * 10, 11 reserved + * + * BIT 5, 4, 3: Roaming handoff decisions on multiple candidates match + * 000 match if exact BSSIDs are found + * 001 match if at least one top priority BSSID only + * 010, 011, 100, 101, 110, 111 reserved + * + * BIT 6: Set this to 1 will send BTM query frame and 0 not sent. + * + * BIT 7: Roam BTM candidates based on the roam score instead of BTM preferred + * value + * + * BIT 8: BTM query preference over 11k neighbor report request + * + * BIT 9-31: Reserved + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_BTM_ENABLE CFG_INI_UINT( \ + "btm_offload_config", \ + 0x00000000, \ + 0xffffffff, \ + 0x000001c1, \ + CFG_VALUE_OR_DEFAULT, \ + "configure btm offload") + +/* + * + * btm_solicited_timeout - timeout value for waiting BTM request + * @Min: 1 + * @Max: 10000 + * @Default: 100 + * + * This ini is used to configure timeout value for waiting BTM request. + * Unit: millionsecond + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_BTM_SOLICITED_TIMEOUT CFG_INI_UINT( \ + "btm_solicited_timeout", \ + 1, \ + 10000, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "configure timeout value for waiting BTM request") + +/* + * + * btm_max_attempt_cnt - Maximum attempt for sending BTM query to ESS + * @Min: 1 + * @Max: 0xFFFFFFFF + * @Default: 3 + * + * This ini is used to configure maximum attempt for sending BTM query to ESS. + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_BTM_MAX_ATTEMPT_CNT CFG_INI_UINT( \ + "btm_max_attempt_cnt", \ + 1, \ + 0xFFFFFFFF, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "configure maximum attempt for sending BTM query to ESS") + +/* + * + * btm_sticky_time - Stick time after roaming to new AP by BTM + * @Min: 0 + * @Max: 0x0000FFFF + * @Default: 0 + * + * This ini is used to configure Stick time after roaming to new AP by BTM. + * Unit: seconds + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_BTM_STICKY_TIME CFG_INI_UINT( \ + "btm_sticky_time", \ + 0, \ + 0x0000FFFF, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "configure Stick time after roaming to new AP by BTM") + +/* + * + * roam_candidate_validity_timer - roam cache entries validity timer + * @Min: 0 + * @Max: 0xffffffff + * @Default: 0xffffffff + * + * This value is the timeout values for the cached roam candidate + * entries in firmware. If this value is 0, then that entry is not + * valid + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_BTM_VALIDITY_TIMER CFG_INI_UINT( \ + "roam_candidate_validity_timer", \ + 0, \ + 0xffffffff, \ + 0xffffffff, \ + CFG_VALUE_OR_DEFAULT, \ + "BTM validity timer") + +/* + * + * btm_disassoc_timer_threshold - Disassociation timer threshold to wait + * after which the full scan for roaming can be started after the AP has sent + * the disassoc imminent + * @Min: 0 + * @Max: 0xffffffff + * @Default: 10000 + * + * When AP sends, BTM request with disassoc imminent bit set, the STA should + * roam to a new AP within the disassc timeout provided by the ap. If the Roam + * scan period is less than the disassoc timeout value, then instead of + * triggering the roam scan immediately, STA can wait for this + * btm_disassoc_timer_threshold and then start roaming. + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_BTM_DISASSOC_TIMER_THRESHOLD CFG_INI_UINT( \ + "btm_disassoc_timer_threshold", \ + 0, \ + 0xffffffff, \ + 10000, \ + CFG_VALUE_OR_DEFAULT, \ + "BTM disassociation timer threshold") + +/* + * + * btm_query_bitmask - To send BTM query with candidate list on various roam + * scans reasons + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 0x8 + * + * This new ini is introduced to configure the bitmask for various roam scan + * reasons. Fw sends "BTM query with preferred candidate list" only for those + * roam scans which are enable through this bitmask. + + * For Example: + * Bitmask : 0x8 (LOW_RSSI) refer enum WMI_ROAM_TRIGGER_REASON_ID + * Bitmask : 0xDA (PER, LOW_RSSI, HIGH_RSSI, MAWC, DENSE) + * refer enum WMI_ROAM_TRIGGER_REASON_ID + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_BTM_QUERY_BITMASK CFG_INI_UINT( \ + "btm_query_bitmask", \ + 0, \ + 0xFFFFFFFF, \ + 0x8, \ + CFG_VALUE_OR_DEFAULT, \ + "btm query with candidate list bitmask") + +/* + * + * minimum_btm_candidate_score - Consider the AP as roam candidate only if + * its score is greater than minimum_btm_candidate_score. + * @Min: 0 + * @Max: 10000 + * @Default: 2600 + * + * This ini is applicable only for candidate selection during BTM roam trigger. + * For this roam_score_delta_bitmap bit 10 should be set to 1. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_MIN_BTM_CANDIDATE_SCORE CFG_INI_UINT( \ + "minimum_btm_candidate_score", \ + 0, \ + 10000, \ + 2600, \ + CFG_VALUE_OR_DEFAULT, \ + "Minimum BTM candidate score") + +#define CFG_BTM_ALL \ + CFG(CFG_PREFER_BTM_QUERY) \ + CFG(CFG_ENABLE_BTM_ABRIDGE) \ + CFG(CFG_BTM_ENABLE) \ + CFG(CFG_BTM_SOLICITED_TIMEOUT) \ + CFG(CFG_BTM_MAX_ATTEMPT_CNT) \ + CFG(CFG_BTM_STICKY_TIME) \ + CFG(CFG_BTM_VALIDITY_TIMER) \ + CFG(CFG_BTM_DISASSOC_TIMER_THRESHOLD) \ + CFG(CFG_BTM_QUERY_BITMASK) \ + CFG(CFG_MIN_BTM_CANDIDATE_SCORE) + +#endif /* CFG_MLME_BTM_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_chainmask.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_chainmask.h new file mode 100644 index 0000000000000000000000000000000000000000..0507dda8a33566f9f7a7ca4d1d114be16883cb02 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_chainmask.h @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_CHAINMASK_H +#define __CFG_CHAINMASK_H + +/* + * + * gSetTxChainmask1x1 - Sets Transmit chain mask. + * @Min: 1 + * @Max: 3 + * @Default: 0 + * + * This ini Sets Transmit chain mask. + * + * If gEnable2x2 is disabled, gSetTxChainmask1x1 and gSetRxChainmask1x1 values + * are taken into account. If chainmask value exceeds the maximum number of + * chains supported by target, the max number of chains is used. By default, + * chain0 is selected for both Tx and Rx. + * gSetTxChainmask1x1=1 or gSetRxChainmask1x1=1 to select chain0. + * gSetTxChainmask1x1=2 or gSetRxChainmask1x1=2 to select chain1. + * gSetTxChainmask1x1=3 or gSetRxChainmask1x1=3 to select both chains. + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_1x1_TX_CHAINMASK CFG_INI_UINT( \ + "gSetTxChainmask1x1", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "1x1 VHT Tx Chainmask") + +/* + * + * gSetRxChainmask1x1 - Sets Receive chain mask. + * @Min: 1 + * @Max: 3 + * @Default: 0 + * + * This ini is used to set Receive chain mask. + * + * If gEnable2x2 is disabled, gSetTxChainmask1x1 and gSetRxChainmask1x1 values + * are taken into account. If chainmask value exceeds the maximum number of + * chains supported by target, the max number of chains is used. By default, + * chain0 is selected for both Tx and Rx. + * gSetTxChainmask1x1=1 or gSetRxChainmask1x1=1 to select chain0. + * gSetTxChainmask1x1=2 or gSetRxChainmask1x1=2 to select chain1. + * gSetTxChainmask1x1=3 or gSetRxChainmask1x1=3 to select both chains. + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_1x1_RX_CHAINMASK CFG_INI_UINT( \ + "gSetRxChainmask1x1", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "1x1 VHT Rx Chainmask") + +/* + * + * gCckChainMaskEnable - Used to enable/disable Cck ChainMask + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set default Cck ChainMask + * 0: disable the cck tx chain mask (default) + * 1: enable the cck tx chain mask + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_TX_CHAIN_MASK_CCK CFG_INI_BOOL( \ + "gCckChainMaskEnable", \ + 0, \ + "Set default CCK Tx Chainmask") + +/* + * + * gTxChainMask1ss - Enables/disables tx chain mask1ss, used by Rome + * @Min: 0 + * @Max: 3 + * @Default: 0 + * + * This ini is used to set default tx chain mask for 1ss + * + * gTxChainMask1ss=0 : 1ss data tx chain mask set to 3 and self gen chain mask + * set to 3. This is default setting of fw side. For 1x1 case, WIFI will + * using chain0 to sent 1ss data and selfgen packets. 2x2 case, WIFI will + * using chain0 and chain1 to sent 1ss data and selfgen packets. + * + * gTxChainMask1ss=1 : 1ss data tx chain mask set to 2 and self gen chain mask + * set to 2. This setting can work only when 2x2 case, WIFI will use chain1 + * to sent 1ss data packets and selfgen packets, this can improve BTC + * performance a little, but have side affect when chain0 and chain1 RSSI + * is unbalance or green AP is enabled. So we recommend not using it. + * + * gTxChainMask1ss=2 : 1ss data tx chain mask set to 3 and self gen chain mask + * set to 2. This setting never used before. + * + * gTxChainMask1ss=3 : 1ss data tx chain mask set to 2 and self gen chain mask + * set to 3. This setting never used before. + * + * Related: None + * + * Supported Feature: STA/SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_TX_CHAIN_MASK_1SS CFG_INI_UINT( \ + "gTxChainMask1ss", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "1SS Tx Chainmask") + +/* + * + * g11bNumTxChains - Number of Tx Chanins in 11b mode + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * Number of Tx Chanins in 11b mode + * + * + * Related: None + * + * Supported Feature: connection + * + * Usage: External + * + * + */ +#define CFG_11B_NUM_TX_CHAIN CFG_INI_UINT( \ + "g11bNumTxChains", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "11b Num Tx chains") + +/* + * + * g11agNumTxChains - Number of Tx Chanins in 11ag mode + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * Number of Tx Chanins in 11ag mode + * + * + * Related: None + * + * Supported Feature: connection + * + * Usage: External + * + * + */ +#define CFG_11AG_NUM_TX_CHAIN CFG_INI_UINT( \ + "g11agNumTxChains", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "11ag Num Tx chains") + +/* + * + * tx_chain_mask_2g - tx chain mask for 2g + * @Min: 0 + * @Max: 3 + * @Default: 0 + * + * This ini will set tx chain mask for 2g. To use the ini, make sure: + * gSetTxChainmask1x1/gSetRxChainmask1x1 = 0, + * gDualMacFeatureDisable = 1 + * gEnable2x2 = 0 + * + * tx_chain_mask_2g=0 : don't care + * tx_chain_mask_2g=1 : for 2g tx use chain 0 + * tx_chain_mask_2g=2 : for 2g tx use chain 1 + * tx_chain_mask_2g=3 : for 2g tx can use either chain + * + * Related: None + * + * Supported Feature: All profiles + * + * Usage: External + * + * + */ +#define CFG_TX_CHAIN_MASK_2G CFG_INI_UINT( \ + "tx_chain_mask_2g", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "2.4G Tx Chainmask") + +/* + * + * rx_chain_mask_2g - rx chain mask for 2g + * @Min: 0 + * @Max: 3 + * @Default: 0 + * + * This ini will set rx chain mask for 2g. To use the ini, make sure: + * gSetTxChainmask1x1/gSetRxChainmask1x1 = 0, + * gDualMacFeatureDisable = 1 + * gEnable2x2 = 0 + * + * rx_chain_mask_2g=0 : don't care + * rx_chain_mask_2g=1 : for 2g rx use chain 0 + * rx_chain_mask_2g=2 : for 2g rx use chain 1 + * rx_chain_mask_2g=3 : for 2g rx can use either chain + * + * Related: None + * + * Supported Feature: All profiles + * + * Usage: External + * + * + */ +#define CFG_RX_CHAIN_MASK_2G CFG_INI_UINT( \ + "rx_chain_mask_2g", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "2.4G Rx Chainmask") + +/* + * + * tx_chain_mask_5g - tx chain mask for 5g + * @Min: 0 + * @Max: 3 + * @Default: 0 + * + * This ini will set tx chain mask for 5g. To use the ini, make sure: + * gSetTxChainmask1x1/gSetRxChainmask1x1 = 0, + * gDualMacFeatureDisable = 1 + * gEnable2x2 = 0 + * + * tx_chain_mask_5g=0 : don't care + * tx_chain_mask_5g=1 : for 5g tx use chain 0 + * tx_chain_mask_5g=2 : for 5g tx use chain 1 + * tx_chain_mask_5g=3 : for 5g tx can use either chain + * + * Related: None + * + * Supported Feature: All profiles + * + * Usage: External + * + * + */ +#define CFG_TX_CHAIN_MASK_5G CFG_INI_UINT( \ + "tx_chain_mask_5g", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "5Ghz Tx Chainmask") + +/* + * + * rx_chain_mask_5g - rx chain mask for 5g + * @Min: 0 + * @Max: 3 + * @Default: 0 + * + * This ini will set rx chain mask for 5g. To use the ini, make sure: + * gSetTxChainmask1x1/gSetRxChainmask1x1 = 0, + * gDualMacFeatureDisable = 1 + * gEnable2x2 = 0 + * + * rx_chain_mask_5g=0 : don't care + * rx_chain_mask_5g=1 : for 5g rx use chain 0 + * rx_chain_mask_5g=2 : for 5g rx use chain 1 + * rx_chain_mask_5g=3 : for 5g rx can use either chain + * + * Related: None + * + * Supported Feature: All profiles + * + * Usage: External + * + * + */ +#define CFG_RX_CHAIN_MASK_5G CFG_INI_UINT( \ + "rx_chain_mask_5g", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "5Ghz Tx Chainmask") + +/* + * + * enable_bt_chain_separation - Enables/disables bt /wlan chainmask assignment + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini disables/enables chainmask setting on 2x2, mainly used for ROME + * BT/WLAN chainmask assignment. + * + * 0, Disable + * 1, Enable + * + * Related: NA + * + * Supported Feature: 11n/11ac + * + * Usage: External + * + * + */ +#define CFG_ENABLE_BT_CHAIN_SEPARATION CFG_INI_BOOL( \ + "enableBTChainSeparation", \ + 0, \ + "Enable/disable BT chainmask assignment") + +#define CFG_CHAINMASK_ALL \ + CFG(CFG_VHT_ENABLE_1x1_TX_CHAINMASK) \ + CFG(CFG_VHT_ENABLE_1x1_RX_CHAINMASK) \ + CFG(CFG_TX_CHAIN_MASK_CCK) \ + CFG(CFG_TX_CHAIN_MASK_1SS) \ + CFG(CFG_11B_NUM_TX_CHAIN) \ + CFG(CFG_11AG_NUM_TX_CHAIN) \ + CFG(CFG_TX_CHAIN_MASK_2G) \ + CFG(CFG_RX_CHAIN_MASK_2G) \ + CFG(CFG_TX_CHAIN_MASK_5G) \ + CFG(CFG_RX_CHAIN_MASK_5G) \ + CFG(CFG_ENABLE_BT_CHAIN_SEPARATION) + +#endif /* __CFG_CHAINMASK_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_dfs.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_dfs.h new file mode 100644 index 0000000000000000000000000000000000000000..e1a1eea334f2f4fad395ec37ad3c519c2ae2b45e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_dfs.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_DFS_H +#define __CFG_MLME_DFS_H + +/* + * + * gsap_tx_leakage_threshold - sap tx leakage threshold + * @Min: 100 + * @Max: 1000 + * @Default: 310 + * + * customer can set this value from 100 to 1000 which means + * sap tx leakage threshold is -10db to -100db + * + * Related: none + * + * Usage: External + * + * + */ +#define CFG_SAP_TX_LEAKAGE_THRESHOLD CFG_INI_UINT( \ + "gsap_tx_leakage_threshold", \ + 100, \ + 1000, \ + 310, \ + CFG_VALUE_OR_DEFAULT, \ + "sap tx leakage threshold") + +/* + * + * gDFSradarMappingPriMultiplier - dfs pri multiplier + * @Min: 1 + * @Max: 10 + * @Default: 2 + * + * customer can set this value from 1 to 10 which means + * host could handle missing pulses while there is high + * channel loading, for example: 30% ETSI and 50% Japan W53 + * + * Related: none + * + * Usage: External + * + * + */ +#define CFG_DFS_RADAR_PRI_MULTIPLIER CFG_INI_UINT( \ + "gDFSradarMappingPriMultiplier", \ + 1, \ + 10, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "dfs pri multiplier") + +/* + * + * gDfsBeaconTxEnhanced - beacon tx enhanced + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enhance dfs beacon tx + * + * Related: none + * + * Usage: External + * + * + */ +#define CFG_DFS_BEACON_TX_ENHANCED CFG_INI_BOOL( \ + "gDfsBeaconTxEnhanced", \ + 0, \ + "beacon tx enhanced") + +/* + * + * gPreferNonDfsChanOnRadar - During random channel selection prefer non dfs + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * During random channel selection prefer non dfs. + * + * Related: none + * + * Usage: External + * + * + */ +#define CFG_ENABLE_NON_DFS_CHAN_ON_RADAR CFG_INI_BOOL( \ + "gPreferNonDfsChanOnRadar", \ + 0, \ + "channel selection prefer non dfs") + +/* + * + * dfsPhyerrFilterOffload - Enable dfs phyerror filtering offload in FW + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to to enable dfs phyerror filtering offload to firmware + * Enabling it will cause basic phy error to be discarding in firmware. + * Related: NA. + * + * Supported Feature: DFS + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD CFG_INI_BOOL( \ + "dfsPhyerrFilterOffload", \ + 0, \ + "dfs phyerror filtering offload") + +/* + * + * gIgnoreCAC - Used to ignore CAC + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set default CAC + * + * Related: None + * + * Supported Feature: DFS + * + * Usage: Internal/External + * + * + */ +#define CFG_IGNORE_CAC CFG_INI_BOOL( \ + "gIgnoreCAC", \ + 0, \ + "ignore CAC on DFS channel") + +/* + * + * gDisableDFSChSwitch - Disable channel switch if radar is found + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to disable channel switch if radar is found + * on that channel. + * Related: NA. + * + * Supported Feature: DFS + * + * Usage: Internal + * + * + */ +#define CFG_DISABLE_DFS_CH_SWITCH CFG_INI_BOOL( \ + "gDisableDFSChSwitch", \ + 0, \ + "Disable channel switch on radar") + +/* + * + * gEnableDFSMasterCap - Enable DFS master capability + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable the DFS master capability. + * Disabling it will cause driver to not advertise the spectrum + * management capability + * Related: NA. + * + * upported Feature: DFS + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_DFS_MASTER_CAPABILITY CFG_INI_BOOL( \ + "gEnableDFSMasterCap", \ + 0, \ + "DFS master mode capability") + +/* + * + * gDisableDfsJapanW53 - Block W53 channels in random channel selection + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to block W53 Japan channel in random channel selection + * + * Related: none + * + * Supported Feature: DFS + * + * Usage: External + * + * + */ +#define CFG_DISABLE_DFS_JAPAN_W53 CFG_INI_BOOL( \ + "gDisableDfsJapanW53", \ + 0, \ + "Block W53 channels in random selection") + +#define CFG_DFS_ALL \ + CFG(CFG_IGNORE_CAC) \ + CFG(CFG_DISABLE_DFS_CH_SWITCH) \ + CFG(CFG_DFS_BEACON_TX_ENHANCED) \ + CFG(CFG_SAP_TX_LEAKAGE_THRESHOLD) \ + CFG(CFG_DFS_RADAR_PRI_MULTIPLIER) \ + CFG(CFG_ENABLE_NON_DFS_CHAN_ON_RADAR) \ + CFG(CFG_ENABLE_DFS_MASTER_CAPABILITY) \ + CFG(CFG_DISABLE_DFS_JAPAN_W53) \ + CFG(CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD) + +#endif /* __CFG_MLME_DFS_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_dot11mode.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_dot11mode.h new file mode 100644 index 0000000000000000000000000000000000000000..fad8e6357a8cda446fc03256b44ba84d63c86088 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_dot11mode.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_DOT11MODE_H +#define __CFG_MLME_DOT11MODE_H + +#define CFG_DOT11_MODE CFG_UINT( \ + "dot11_mode", \ + 0, \ + 10, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "dot 11 mode") + +/* + * + * vdev_dot11_mode- Bit mask to set the dot11 mode for different vdev types + * @Min: 0x0 + * @Max: 0x333333 + * @Default: 0 + * + * This ini is used to set the dot11mode different vdev types. + * dot11_mode ini value (CFG_DOT11_MODE) is the master configuration + * Min configuration of INI dot11_mode and vdev_dot11_mode is used for that + * vdev type. + * dot11_mode vdev_dot11_mode dot11_mode_used + * 11AX 11AC 11AC + * 11AC 11AX 11AC + * + * Dot11 mode value is 4 bit length for each vdev. Below is the bit definition + * for different vdev types dot11 mode value bit index. + * + * Bits used for dot11mode Vdev Type + * BIT[3:0] STA mode + * BIT[7:4] P2P_CLI/P2P_DEVICE mode + * BIT[11:8] NAN DISCOVERY + * BIT[15:12] OCB + * BIT[19:16] TDLS + * BIT[23:20] NDI mode + * + * Dot11 mode value to be set in the above bit definition: + * 0 - Auto, Uses CFG_DOT11_MODE setting + * 1 - HT mode(11N) + * 2 - VHT mode(11AC) + * 3 - HE mode(11AX) + * + * E.g: vdev_dot11_mode=0x013220 + * + * 0 1 3 2 2 0 + * NDI(auto) TDLS HT OCB_HE VHT NAN_DISC VHT P2P STA_AUTO + * + * Usage: Internal/External + * + * + */ +#define CFG_VDEV_DOT11_MODE CFG_INI_UINT( \ + "vdev_dot11_mode", \ + 0, \ + 0x333333, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "vdev dot 11 mode") + +#define CFG_DOT11_MODE_ALL \ + CFG(CFG_DOT11_MODE) \ + CFG(CFG_VDEV_DOT11_MODE) \ + +#endif /* __CFG_MLME_DOT11MODE_H */ + diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_edca_params.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_edca_params.h new file mode 100644 index 0000000000000000000000000000000000000000..ece0a8de019ec4a3353b41bbce7dfab17aaedafb --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_edca_params.h @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_EDCA__PARAM_H +#define __CFG_MLME_EDCA__PARAM_H + +#define STR_EDCA_ANI_ACBK_LOCAL "0x0, 0x7, 0x0, 0xf, 0x3, 0xff, 0x0, 0x0, 0x1f, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0" +#define STR_EDCA_ANI_ACBK_LOCAL_LEN (sizeof(STR_EDCA_ANI_ACBK_LOCAL) - 1) + +#define CFG_EDCA_ANI_ACBK_LOCAL CFG_STRING( \ + "edca_ani_acbk_local", \ + 0, \ + STR_EDCA_ANI_ACBK_LOCAL_LEN, \ + STR_EDCA_ANI_ACBK_LOCAL, \ + "EDCA ANI ACBK LOCAL") + +#define STR_EDCA_ANI_ACBE_LOCAL "0x0, 0x2, 0x0, 0xf, 0x3, 0xff, 0x64, 0x0, 0x1f, 0x3, 0xff, 0x64, 0x0, 0xf, 0x3, 0xff, 0x64" +#define STR_EDCA_ANI_ACBE_LOCAL_LEN (sizeof(STR_EDCA_ANI_ACBE_LOCAL) - 1) + +#define CFG_EDCA_ANI_ACBE_LOCAL CFG_STRING( \ + "edca_ani_acbe_local", \ + 0, \ + STR_EDCA_ANI_ACBE_LOCAL_LEN, \ + STR_EDCA_ANI_ACBE_LOCAL, \ + "EDCA ANI ACBE LOCAL") + +#define STR_EDCA_ANI_ACVI_LOCAL "0x0, 0x2, 0x0, 0x7, 0x0, 0xf, 0xc8, 0x0, 0xf, 0x0, 0x1f, 0xbc, 0x0, 0x7, 0x0, 0xf, 0xc8" +#define STR_EDCA_ANI_ACVI_LOCAL_LEN (sizeof(STR_EDCA_ANI_ACVI_LOCAL) - 1) + +#define CFG_EDCA_ANI_ACVI_LOCAL CFG_STRING( \ + "edca_ani_acvi_local",\ + 0, \ + STR_EDCA_ANI_ACVI_LOCAL_LEN, \ + STR_EDCA_ANI_ACVI_LOCAL, \ + "EDCA ANI ACVI LOCAL") + +#define STR_EDCA_ANI_ACVO_LOCAL "0x0, 0x2, 0x0, 0x3, 0x0, 0x7, 0x64, 0x0, 0x7, 0x0, 0xf, 0x66, 0x0, 0x3, 0x0, 0x7, 0x64" +#define STR_EDCA_ANI_ACVO_LOCAL_LEN (sizeof(STR_EDCA_ANI_ACVO_LOCAL) - 1) + +#define CFG_EDCA_ANI_ACVO_LOCAL CFG_STRING( \ + "edca_ani_acvo_local", \ + 0, \ + STR_EDCA_ANI_ACVO_LOCAL_LEN, \ + STR_EDCA_ANI_ACVO_LOCAL, \ + "EDCA ANI ACVO LOCAL") + +#define STR_EDCA_ANI_ACBK "0x0, 0x7, 0x0, 0xf, 0x3, 0xff, 0x0, 0x0, 0x1f, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0" +#define STR_EDCA_ANI_ACBK_LEN (sizeof(STR_EDCA_ANI_ACBK) - 1) + +#define CFG_EDCA_ANI_ACBK CFG_STRING( \ + "edca_ani_acbk", \ + 0, \ + STR_EDCA_ANI_ACBK_LEN, \ + STR_EDCA_ANI_ACBK, \ + "EDCA ANI ACBK BROADCAST") + +#define STR_EDCA_ANI_ACBE "0x0, 0x2, 0x0, 0xf, 0x3, 0xff, 0x64, 0x0, 0x1f, 0x3, 0xff, 0x64, 0x0, 0xf, 0x3, 0xff, 0x64" +#define STR_EDCA_ANI_ACBE_LEN (sizeof(STR_EDCA_ANI_ACBE) - 1) + +#define CFG_EDCA_ANI_ACBE CFG_STRING( \ + "edca_ani_acbe", \ + 0, \ + STR_EDCA_ANI_ACBE_LEN, \ + STR_EDCA_ANI_ACBE, \ + "EDCA ANI ACBE BROADCAST") + +#define STR_EDCA_ANI_ACVI "0x0, 0x2, 0x0, 0x7, 0x0, 0xf, 0xc8, 0x0, 0xf, 0x0, 0x1f, 0xbc, 0x0, 0x7, 0x0, 0xf, 0xc8" +#define STR_EDCA_ANI_ACVI_LEN (sizeof(STR_EDCA_ANI_ACVI) - 1) + +#define CFG_EDCA_ANI_ACVI CFG_STRING( \ + "edca_ani_acvi", \ + 0, \ + STR_EDCA_ANI_ACVI_LEN, \ + STR_EDCA_ANI_ACVI, \ + "EDCA ANI ACVI BROADCAST") + +#define STR_EDCA_ANI_ACVO "0x0, 0x2, 0x0, 0x3, 0x0, 0x7, 0x64, 0x0, 0x7, 0x0, 0xf, 0x66, 0x0, 0x3, 0x0, 0x7, 0x64" +#define STR_EDCA_ANI_ACVO_LEN (sizeof(STR_EDCA_ANI_ACVO) - 1) + +#define CFG_EDCA_ANI_ACVO CFG_STRING( \ + "edca_ani_acvo", \ + 0, \ + STR_EDCA_ANI_ACVO_LEN, \ + STR_EDCA_ANI_ACVO, \ + "EDCA ANI ACVO BROADCAST") + +#define STR_EDCA_WME_ACBK_LOCAL "0x0, 0x7, 0x0, 0xf, 0x3, 0xff, 0x0, 0x0, 0x1f, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0" +#define STR_EDCA_WME_ACBK_LOCAL_LEN (sizeof(STR_EDCA_WME_ACBK_LOCAL) - 1) + +#define CFG_EDCA_WME_ACBK_LOCAL CFG_STRING( \ + "edca_wme_acbk_local", \ + 0, \ + STR_EDCA_WME_ACBK_LOCAL_LEN, \ + STR_EDCA_WME_ACBK_LOCAL, \ + "EDCA WME ACBK LOCAL") + +#define STR_EDCA_WME_ACBE_LOCAL "0x0, 0x3, 0x0, 0xf, 0x0, 0x3f, 0x0, 0x0, 0x1f, 0x3, 0xff, 0x0, 0x0, 0xf, 0x0, 0x3f, 0x0" +#define STR_EDCA_WME_ACBE_LOCAL_LEN (sizeof(STR_EDCA_WME_ACBE_LOCAL) - 1) + +#define CFG_EDCA_WME_ACBE_LOCAL CFG_STRING( \ + "edca_wme_acbe_local", \ + 0, \ + STR_EDCA_WME_ACBE_LOCAL_LEN, \ + STR_EDCA_WME_ACBE_LOCAL, \ + "EDCA WME ACBE LOCAL") + +#define STR_EDCA_WME_ACVI_LOCAL "0x0, 0x1, 0x0, 0x7, 0x0, 0xf, 0x5e, 0x0, 0x7, 0x0, 0xf, 0xbc, 0x0, 0x7, 0x0, 0xf, 0x5e" +#define STR_EDCA_WME_ACVI_LOCAL_LEN (sizeof(STR_EDCA_WME_ACVI_LOCAL) - 1) + +#define CFG_EDCA_WME_ACVI_LOCAL CFG_STRING( \ + "edca_wme_acvi_local", \ + 0, \ + STR_EDCA_WME_ACVI_LOCAL_LEN, \ + STR_EDCA_WME_ACVI_LOCAL, \ + "EDCA WME ACVI LOCAL") + +#define STR_EDCA_WME_ACVO_LOCAL "0x0, 0x1, 0x0, 0x3, 0x0, 0x7, 0x2f, 0x0, 0x3, 0x0, 0x7, 0x66, 0x0, 0x3, 0x0, 0x7, 0x2f" +#define STR_EDCA_WME_ACVO_LOCAL_LEN (sizeof(STR_EDCA_WME_ACVO_LOCAL) - 1) + +#define CFG_EDCA_WME_ACVO_LOCAL CFG_STRING( \ + "edca_wme_acvo_local", \ + 0, \ + STR_EDCA_WME_ACVO_LOCAL_LEN, \ + STR_EDCA_WME_ACVO_LOCAL, \ + "EDCA WME ACVO LOCAL") + +#define STR_EDCA_WME_ACBK "0x0, 0x7, 0x0, 0xf, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0" +#define STR_EDCA_WME_ACBK_LEN (sizeof(STR_EDCA_WME_ACBK) - 1) + +#define CFG_EDCA_WME_ACBK CFG_STRING( \ + "edca_wme_acbk", \ + 0, \ + STR_EDCA_WME_ACBK_LEN, \ + STR_EDCA_WME_ACBK, \ + "EDCA WME ACBK BROADCAST") + +#define STR_EDCA_WME_ACBE "0x0, 0x3, 0x0, 0xf, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0" +#define STR_EDCA_WME_ACBE_LEN (sizeof(STR_EDCA_WME_ACBE) - 1) + +#define CFG_EDCA_WME_ACBE CFG_STRING( \ + "edca_wme_acbe", \ + 0, \ + STR_EDCA_WME_ACBE_LEN, \ + STR_EDCA_WME_ACBE, \ + "EDCA WME ACBE BROADCAST") + +#define STR_EDCA_WME_ACVI "0x0, 0x2, 0x0, 0x7, 0x0, 0xf, 0x5e, 0x0, 0x7, 0x0, 0xf, 0xbc, 0x0, 0x7, 0x0, 0xf, 0x5e" +#define STR_EDCA_WME_ACVI_LEN (sizeof(STR_EDCA_WME_ACVI) - 1) + +#define CFG_EDCA_WME_ACVI CFG_STRING( \ + "edca_wme_acvi", \ + 0, \ + STR_EDCA_WME_ACVI_LEN, \ + STR_EDCA_WME_ACVI, \ + "EDCA WME ACVI BROADCAST") + +#define STR_EDCA_WME_ACVO "0x0, 0x2, 0x0, 0x3, 0x0, 0x7, 0x2f, 0x0, 0x3, 0x0, 0x7, 0x66, 0x0, 0x3, 0x0, 0x7, 0x2f" +#define STR_EDCA_WME_ACVO_LEN (sizeof(STR_EDCA_WME_ACVO) - 1) + +#define CFG_EDCA_WME_ACVO CFG_STRING( \ + "edca_wme_acvo", \ + 0, \ + STR_EDCA_WME_ACVO_LEN, \ + STR_EDCA_WME_ACVO, \ + "EDCA WME ACVO BROADCAST") + +#define STR_EDCA_ETSI_ACBK_LOCAL "0x0, 0x7, 0x0, 0xf, 0x3, 0xff, 0xbb, 0x0, 0x1f, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0" +#define STR_EDCA_ETSI_ACBK_LOCAL_LEN (sizeof(STR_EDCA_ETSI_ACBK_LOCAL) - 1) + +#define CFG_EDCA_ETSI_ACBK_LOCAL CFG_STRING( \ + "edca_etsi_acbk_local", \ + 0, \ + STR_EDCA_ETSI_ACBK_LOCAL_LEN, \ + STR_EDCA_ETSI_ACBK_LOCAL, \ + "EDCA ETSI ACBK LOCAL") + +#define STR_EDCA_ETSI_ACBE_LOCAL "0x0, 0x3, 0x0, 0xf, 0x0, 0x3f, 0xbb, 0x0, 0x1f, 0x3, 0xff, 0x0, 0x0, 0xf, 0x0, 0x3f, 0x0" +#define STR_EDCA_ETSI_ACBE_LOCAL_LEN (sizeof(STR_EDCA_ETSI_ACBE_LOCAL) - 1) + +#define CFG_EDCA_ETSI_ACBE_LOCAL CFG_STRING( \ + "edca_etsi_acbe_local", \ + 0, \ + STR_EDCA_ETSI_ACBE_LOCAL_LEN, \ + STR_EDCA_ETSI_ACBE_LOCAL, \ + "EDCA ETSI ACBE LOCAL") + +#define STR_EDCA_ETSI_ACVI_LOCAL "0x0, 0x1, 0x0, 0x7, 0x0, 0xf, 0x7d, 0x0, 0x7, 0x0, 0xf, 0xbc, 0x0, 0x7, 0x0, 0xf, 0x5e" +#define STR_EDCA_ETSI_ACVI_LOCAL_LEN (sizeof(STR_EDCA_ETSI_ACVI_LOCAL) - 1) + +#define CFG_EDCA_ETSI_ACVI_LOCAL CFG_STRING( \ + "edca_etsi_acvi_local", \ + 0, \ + STR_EDCA_ETSI_ACVI_LOCAL_LEN, \ + STR_EDCA_ETSI_ACVI_LOCAL, \ + "EDCA ETSI ACVI LOCAL") + +#define STR_EDCA_ETSI_ACVO_LOCAL "0x0, 0x1, 0x0, 0x3, 0x0, 0x7, 0x3e, 0x0, 0x3, 0x0, 0x7, 0x66, 0x0, 0x3, 0x0, 0x7, 0x2f" +#define STR_EDCA_ETSI_ACVO_LOCAL_LEN (sizeof(STR_EDCA_ETSI_ACVO_LOCAL) - 1) + +#define CFG_EDCA_ETSI_ACVO_LOCAL CFG_STRING( \ + "edca_etsi_acvo_local", \ + 0, \ + STR_EDCA_ETSI_ACVO_LOCAL_LEN, \ + STR_EDCA_ETSI_ACVO_LOCAL, \ + "EDCA ETSI ACVO LOCAL") + +#define STR_EDCA_ETSI_ACBK "0x0, 0x7, 0x0, 0xf, 0x3, 0xff, 0xbb, 0x0, 0xf, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0" +#define STR_EDCA_ETSI_ACBK_LEN (sizeof(STR_EDCA_ETSI_ACBK) - 1) + +#define CFG_EDCA_ETSI_ACBK CFG_STRING( \ + "edca_etsi_acbk", \ + 0, \ + STR_EDCA_ETSI_ACBK_LEN, \ + STR_EDCA_ETSI_ACBK, \ + "EDCA ETSI ACBK BROADCAST") + +#define STR_EDCA_ETSI_ACBE "0x0, 0x3, 0x0, 0xf, 0x3, 0xff, 0xbb, 0x0, 0xf, 0x3, 0xff, 0x0, 0x0, 0xf, 0x3, 0xff, 0x0" +#define STR_EDCA_ETSI_ACBE_LEN (sizeof(STR_EDCA_ETSI_ACBE) - 1) + +#define CFG_EDCA_ETSI_ACBE CFG_STRING( \ + "edca_etsi_acbe", \ + 0, \ + STR_EDCA_ETSI_ACBE_LEN, \ + STR_EDCA_ETSI_ACBE, \ + "EDCA ETSI ACBE BROADCAST") + +#define STR_EDCA_ETSI_ACVI "0x0, 0x2, 0x0, 0x7, 0x0, 0xf, 0x7d, 0x0, 0x7, 0x0, 0xf, 0xbc, 0x0, 0x7, 0x0, 0xf, 0x5e" +#define STR_EDCA_ETSI_ACVI_LEN (sizeof(STR_EDCA_ETSI_ACVI) - 1) + +#define CFG_EDCA_ETSI_ACVI CFG_STRING( \ + "edca_etsi_acvi", \ + 0, \ + STR_EDCA_ETSI_ACVI_LEN, \ + STR_EDCA_ETSI_ACVI, \ + "EDCA ETSI ACVI BROADCAST") + +#define STR_EDCA_ETSI_ACVO "0x0, 0x2, 0x0, 0x3, 0x0, 0x7, 0x3e, 0x0, 0x3, 0x0, 0x7, 0x66, 0x0, 0x3, 0x0, 0x7, 0x2f" +#define STR_EDCA_ETSI_ACVO_LEN (sizeof(STR_EDCA_ETSI_ACVO) - 1) + +#define CFG_EDCA_ETSI_ACVO CFG_STRING( \ + "edca_etsi_acvo", \ + 0, \ + STR_EDCA_ETSI_ACVO_LEN, \ + STR_EDCA_ETSI_ACVO, \ + "EDCA ETSI ACVO BROADCAST") + +/* + * + * gEnableEdcaParams - Enable edca parameter + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used if gEnableEdcaParams is set to 1, params gEdcaVoCwmin, + * gEdcaViCwmin, gEdcaBkCwmin, gEdcaBeCwmin, gEdcaVoCwmax, + * gEdcaViCwmax, gEdcaBkCwmax, gEdcaBeCwmax, gEdcaVoAifs, + * gEdcaViAifs, gEdcaBkAifs and gEdcaBeAifs values are used + * to overwrite the values received from AP + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_ENABLE_PARAM CFG_INI_BOOL( \ + "gEnableEdcaParams", \ + 0, \ + "Enable edca parameter") + +/* + * + * gEdcaVoCwmin - Set Cwmin value for QCA_WLAN_AC_VO + * @Min: 0 + * @Max: 0x15 + * @Default: 2 + * + * This ini is used to set default Cwmin value for QCA_WLAN_AC_VO + * Cwmin value for QCA_WLAN_AC_VO. CWVomin = 2^gEdcaVoCwmin -1 + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin etc + * are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_VO_CWMIN CFG_INI_UINT( \ + "gEdcaVoCwmin", \ + 0x0, \ + 15, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "Cwmin value for QCA_WLAN_AC_VO") + +/* + * + * gEdcaVoCwmax - Set Cwmax value for QCA_WLAN_AC_VO + * @Min: 0 + * @Max: 15 + * @Default: 3 + * + * This ini is used to set default Cwmax value for QCA_WLAN_AC_VO + * Cwmax value for QCA_WLAN_AC_VO. CWVomax = 2^gEdcaVoCwmax -1 + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_VO_CWMAX CFG_INI_UINT( \ + "gEdcaVoCwmax", \ + 0x0, \ + 15, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Cwmax value for QCA_WLAN_AC_VO") + +/* + * + * gEdcaVoAifs - Set Aifs value for QCA_WLAN_AC_VO + * @Min: 0 + * @Max: 15 + * @Default: 2 + * + * This ini is used to set default Aifs value for QCA_WLAN_AC_VO + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_VO_AIFS CFG_INI_UINT( \ + "gEdcaVoAifs", \ + 0x0, \ + 15, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "default Aifs value for QCA_WLAN_AC_VO") + +/* + * + * gEdcaViCwmin - Set Cwmin value for QCA_WLAN_AC_VI + * @Min: 0x0 + * @Max: 15 + * @Default: 3 + * + * This ini is used to set default value for QCA_WLAN_AC_VI + * Cwmin value for QCA_WLAN_AC_VI. CWVimin = 2^gEdcaViCwmin -1 + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_VI_CWMIN CFG_INI_UINT( \ + "gEdcaViCwmin", \ + 0x0, \ + 15, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Cwmin value for QCA_WLAN_AC_VI") + +/* + * + * gEdcaViCwmax - Set Cwmax value for QCA_WLAN_AC_VI + * @Min: 0 + * @Max: 15 + * @Default: 4 + * + * This ini is used to set default Cwmax value for QCA_WLAN_AC_VI + * Cwmax value for QCA_WLAN_AC_VI. CWVimax = 2^gEdcaViCwmax -1 + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_VI_CWMAX CFG_INI_UINT( \ + "gEdcaViCwmax", \ + 0x0, \ + 15, \ + 4, \ + CFG_VALUE_OR_DEFAULT, \ + "cwmax value for QCA_WLAN_AC_VI") + +/* + * + * gEdcaViAifs - Set Aifs value for QCA_WLAN_AC_VI + * @Min: 0 + * @Max: 15 + * @Default: 2 + * + * This ini is used to set default Aifs value for QCA_WLAN_AC_VI + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_VI_AIFS CFG_INI_UINT( \ + "gEdcaViAifs", \ + 0x0, \ + 15, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "default Aifs value for QCA_WLAN_AC_VI") + +/* + * + * gEdcaBkCwmin - Set Cwmin value for QCA_WLAN_AC_BK + * @Min: 0x0 + * @Max: 15 + * @Default: 4 + * + * This ini is used to set default Cwmin value for QCA_WLAN_AC_BK + * Cwmin value for QCA_WLAN_AC_BK. CWBkmin = 2^gEdcaBkCwmin -1 + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + */ +#define CFG_EDCA_BK_CWMIN CFG_INI_UINT( \ + "gEdcaBkCwmin", \ + 0x0, \ + 15, \ + 4, \ + CFG_VALUE_OR_DEFAULT, \ + "Cwmin value for QCA_WLAN_AC_BK") + +/* + * + * gEdcaBkCwmax - Set Cwmax value for QCA_WLAN_AC_BK + * @Min: 0 + * @Max: 15 + * @Default: 10 + * + * This ini is used to set default Cwmax value for QCA_WLAN_AC_BK + * Cwmax value for QCA_WLAN_AC_BK. CWBkmax = 2^gEdcaBkCwmax -1 + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_BK_CWMAX CFG_INI_UINT( \ + "gEdcaBkCwmax", \ + 0, \ + 15, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "cwmax value for QCA_WLAN_AC_BK") + +/* + * + * gEdcaBkAifs - Set Aifs value for QCA_WLAN_AC_BK + * @Min: 0 + * @Max: 15 + * @Default: 7 + * + * This ini is used to set default Aifs value for QCA_WLAN_AC_BK + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_BK_AIFS CFG_INI_UINT( \ + "gEdcaBkAifs", \ + 0, \ + 15, \ + 7, \ + CFG_VALUE_OR_DEFAULT, \ + "default Aifs value for QCA_WLAN_AC_BK") + +/* + * + * gEdcaBeCwmin - Set Cwmin value for QCA_WLAN_AC_BE + * @Min: 0x0 + * @Max: 15 + * @Default: 4 + * + * This ini is used to set default Cwmin value for QCA_WLAN_AC_BE + * Cwmin value for QCA_WLAN_AC_BE. CWBemin = 2^gEdcaBeCwmin + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_BE_CWMIN CFG_INI_UINT( \ + "gEdcaBeCwmin", \ + 0x0, \ + 15, \ + 4, \ + CFG_VALUE_OR_DEFAULT, \ + "Cwmin value for QCA_WLAN_AC_BE") + +/* + * + * gEdcaBeCwmax - Set Cwmax value for QCA_WLAN_AC_BE + * @Min: 0 + * @Max: 15 + * @Default: 10 + * + * This ini is used to set default Cwmax value for QCA_WLAN_AC_BE + * Cwmax value for QCA_WLAN_AC_BE. CWBemax = 2^gEdcaBeCwmax -1 + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + +#define CFG_EDCA_BE_CWMAX CFG_INI_UINT( \ + "gEdcaBeCwmax", \ + 0, \ + 15, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "cwmax value for QCA_WLAN_AC_BE") + +/* + * + * gEdcaBeAifs - Set Aifs value for QCA_WLAN_AC_BE + * @Min: 0 + * @Max: 15 + * @Default: 3 + * + * This ini is used to set default Aifs value for QCA_WLAN_AC_BE + * + * Related: If gEnableEdcaParams is set to 1, params gEdcaVoCwmin + * etc are aplicable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_EDCA_BE_AIFS CFG_INI_UINT( \ + "gEdcaBeAifs", \ + 0, \ + 15, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "default Aifs value for QCA_WLAN_AC_BE") + +#define CFG_EDCA_PARAMS_ALL \ + CFG(CFG_EDCA_ANI_ACBK_LOCAL) \ + CFG(CFG_EDCA_ANI_ACBE_LOCAL) \ + CFG(CFG_EDCA_ANI_ACVI_LOCAL) \ + CFG(CFG_EDCA_ANI_ACVO_LOCAL) \ + CFG(CFG_EDCA_ANI_ACBK) \ + CFG(CFG_EDCA_ANI_ACBE) \ + CFG(CFG_EDCA_ANI_ACVI) \ + CFG(CFG_EDCA_ANI_ACVO) \ + CFG(CFG_EDCA_WME_ACBK_LOCAL) \ + CFG(CFG_EDCA_WME_ACBE_LOCAL) \ + CFG(CFG_EDCA_WME_ACVI_LOCAL) \ + CFG(CFG_EDCA_WME_ACVO_LOCAL) \ + CFG(CFG_EDCA_WME_ACBK) \ + CFG(CFG_EDCA_WME_ACBE) \ + CFG(CFG_EDCA_WME_ACVI) \ + CFG(CFG_EDCA_WME_ACVO) \ + CFG(CFG_EDCA_ETSI_ACBK_LOCAL) \ + CFG(CFG_EDCA_ETSI_ACBE_LOCAL) \ + CFG(CFG_EDCA_ETSI_ACVI_LOCAL) \ + CFG(CFG_EDCA_ETSI_ACVO_LOCAL) \ + CFG(CFG_EDCA_ETSI_ACBK) \ + CFG(CFG_EDCA_ETSI_ACBE) \ + CFG(CFG_EDCA_ETSI_ACVI) \ + CFG(CFG_EDCA_ETSI_ACVO) \ + CFG(CFG_EDCA_ENABLE_PARAM) \ + CFG(CFG_EDCA_VO_CWMIN) \ + CFG(CFG_EDCA_VO_CWMAX) \ + CFG(CFG_EDCA_VO_AIFS) \ + CFG(CFG_EDCA_VI_CWMIN) \ + CFG(CFG_EDCA_VI_CWMAX) \ + CFG(CFG_EDCA_VI_AIFS) \ + CFG(CFG_EDCA_BK_CWMIN) \ + CFG(CFG_EDCA_BK_CWMAX) \ + CFG(CFG_EDCA_BK_AIFS) \ + CFG(CFG_EDCA_BE_CWMIN) \ + CFG(CFG_EDCA_BE_CWMAX) \ + CFG(CFG_EDCA_BE_AIFS) + +#endif /* __CFG_MLME_EDCA__PARAM_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_fe_rrm.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_fe_rrm.h new file mode 100644 index 0000000000000000000000000000000000000000..9ce59b4e9d9a31d858aa533ac80bdf59275a687d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_fe_rrm.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2012-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_FE_RRM_H +#define __CFG_MLME_FE_RRM_H + +/* + * + * gRrmEnable - Enable/Disable RRM on STA + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to controls the capabilities (11 k) included + * in the capabilities field for STA. + * + * Related: None. + * + * Supported Feature: 11k + * + * Usage: Internal + * + * + */ +#define CFG_RRM_ENABLE CFG_INI_BOOL("gRrmEnable", \ + 0, \ + "Enable/Disable RRM") + +/* + * + * sap_rrm_enable - Enable/Disable RRM on SAP + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to control the capabilities (11 k) included + * in the capabilities field for SAP. + * + * Related: None. + * + * Supported Feature: 11k + * + * Usage: Internal + * + * + */ +#define CFG_SAP_RRM_ENABLE CFG_INI_BOOL("sap_rrm_enable", \ + 0, \ + "Enable/Disable RRM on SAP") + +/* + * + * gRrmRandnIntvl - Randomization interval + * @Min: 10 + * @Max: 100 + * @Default: 100 + * + * This ini is used to set randomization interval which is used to start a timer + * of a random value within randomization interval. Next RRM Scan request + * will be issued after the expiry of this random interval. + * + * Related: None. + * + * Supported Feature: 11k + * + * Usage: Internal/External + * + * + */ +#define CFG_RRM_MEAS_RAND_INTVL CFG_INI_UINT("gRrmRandnIntvl", \ + 10, \ + 100, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "RRM Randomization interval") + +/* + * + * rm_capability - Configure RM enabled capabilities IE + * @Default: 0x73,0x10,0x91,0x00,0x04 + * + * This ini is used to configure RM enabled capabilities IE. + * Using this INI, we can set/unset any of the bits in 5 bytes + * (last 4bytes are reserved). Bit details are updated as per + * Draft version of 11mc spec. (Draft P802.11REVmc_D4.2) + * + * Bitwise details are defined as bit mask in rrm_global.h + * Comma is used as a separator for each byte. + * + * Related: None. + * + * Supported Feature: 11k + * + * Usage: Internal/External + * + * + */ +#define CFG_RM_CAPABILITY CFG_INI_STRING("rm_capability", \ + 24, \ + 40, \ + "0x73,0x10,0x91,0x00,0x04", \ + "RM enabled capabilities IE") + +#define CFG_FE_RRM_ALL \ + CFG(CFG_RRM_ENABLE) \ + CFG(CFG_SAP_RRM_ENABLE) \ + CFG(CFG_RRM_MEAS_RAND_INTVL) \ + CFG(CFG_RM_CAPABILITY) + +#endif /* __CFG_MLME_FE_RRM_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_fe_wlm.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_fe_wlm.h new file mode 100644 index 0000000000000000000000000000000000000000..c5dbb256da3652e3385eca89e3df6ce09644f4f4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_fe_wlm.h @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_FE_WLM_H +#define __CFG_MLME_FE_WLM_H + +/* + * + * wlm_latency_enable - WLM latency Enable + * + * @min: 0 + * @max: 1 + * @default: 1 + * + * 0 - disable + * 1 - enable + * + * + */ +#define CFG_LATENCY_ENABLE CFG_INI_BOOL("wlm_latency_enable", \ + 1, \ + "WLM latency Enable") + +/* + * + * wlm_latency_reset_on_disconnect - WLM latency level reset on disconnect + * + * @min: 0 + * @max: 1 + * @default: 0 + * + * 0 - disable + * 1 - enable + * + * + */ +#define CFG_LATENCY_RESET CFG_INI_BOOL("wlm_latency_reset_on_disconnect", \ + 0, \ + "WLM latency reset on disconnect") + +/* + * + * wlm_latency_level - WLM latency level + * Define 4 latency level to gain latency + * + * @min: 0 + * @max: 3 + * @defalut: 0 + * + * 0 - normal + * 1 - moderate + * 2 - low + * 3 - ultralow + * + * + */ +#define CFG_LATENCY_LEVEL CFG_INI_UINT("wlm_latency_level", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "WLM latency level") + +/* + * + * wlm_latency_flags_normal - WLM flags setting for normal level + * + * @min: 0x0 + * @max: 0xffffffff + * @defalut: 0x0 + * + * |31 12| 11 | 10 |9 8|7 6|5 4|3 2| 1 | 0 | + * +------+------+------+------+------+------+------+-----+-----+ + * | RSVD | SSLP | CSLP | RSVD | Roam | RSVD | DWLT | DFS | SUP | + * +------+-------------+-------------+-------------------------+ + * | WAL | PS | Roam | Scan | + * + * bit 0: Avoid scan request from HLOS if setting + * bit 1: Skip DFS channel SCAN if setting + * bit 2-3: Define policy of dwell time/duration for each foreign channel + * (b2 b3) + * (0 0 ): Default scan dwell time + * (0 1 ): Reserve + * (1 0 ): Shrink off channel dwell time + * (1 1 ): Reserve + * bit 4-5: Reserve for scan + * bit 6-7: Define roaming policy + * (b6 b7) + * (0 0 ): Default roaming behavior, allow roaming in all scenarios + * (0 1 ): Disallow all roaming + * (1 0 ): Allow roaming when final bmissed + * (1 1 ): Reserve + * bit 8-9: Reserve for roaming + * bit 10: Disable css power collapse if setting + * bit 11: Disable sys sleep if setting + * bit 12-31: Reserve for future useage + * + * + */ +#define CFG_LATENCY_FLAGS_NORMAL CFG_INI_UINT("wlm_latency_flags_normal", \ + 0, \ + 0xffffffff, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "WLM flag setting for normal lvl") + +/* + * + * wlm_latency_flags_moderate - WLM flags setting for moderate level + * + * @min: 0x0 + * @max: 0xffffffff + * @defalut: 0x8 + * + * |31 12| 11 | 10 |9 8|7 6|5 4|3 2| 1 | 0 | + * +------+------+------+------+------+------+------+-----+-----+ + * | RSVD | SSLP | CSLP | RSVD | Roam | RSVD | DWLT | DFS | SUP | + * +------+-------------+-------------+-------------------------+ + * | WAL | PS | Roam | Scan | + * + * bit 0: Avoid scan request from HLOS if setting + * bit 1: Skip DFS channel SCAN if setting + * bit 2-3: Define policy of dwell time/duration for each foreign channel + * (b2 b3) + * (0 0 ): Default scan dwell time + * (0 1 ): Reserve + * (1 0 ): Shrink off channel dwell time + * (1 1 ): Reserve + * bit 4-5: Reserve for scan + * bit 6-7: Define roaming policy + * (b6 b7) + * (0 0 ): Default roaming behavior, allow roaming in all scenarios + * (0 1 ): Disallow all roaming + * (1 0 ): Allow roaming when final bmissed + * (1 1 ): Reserve + * bit 8-9: Reserve for roaming + * bit 10: Disable css power collapse if setting + * bit 11: Disable sys sleep if setting + * bit 12-31: Reserve for future useage + * + * + */ +#define CFG_LATENCY_FLAGS_MOD CFG_INI_UINT("wlm_latency_flags_moderate", \ + 0, \ + 0xffffffff, \ + 0x8, \ + CFG_VALUE_OR_DEFAULT, \ + "WLM flag setting for moderate lvl") + +/* + * + * wlm_latency_flags_low - WLM flags setting for low level + * + * @min: 0x0 + * @max: 0xffffffff + * @defalut: 0xa + * + * |31 12| 11 | 10 |9 8|7 6|5 4|3 2| 1 | 0 | + * +------+------+------+------+------+------+------+-----+-----+ + * | RSVD | SSLP | CSLP | RSVD | Roam | RSVD | DWLT | DFS | SUP | + * +------+-------------+-------------+-------------------------+ + * | WAL | PS | Roam | Scan | + * + * bit 0: Avoid scan request from HLOS if setting + * bit 1: Skip DFS channel SCAN if setting + * bit 2-3: Define policy of dwell time/duration for each foreign channel + * (b2 b3) + * (0 0 ): Default scan dwell time + * (0 1 ): Reserve + * (1 0 ): Shrink off channel dwell time + * (1 1 ): Reserve + * bit 4-5: Reserve for scan + * bit 6-7: Define roaming policy + * (b6 b7) + * (0 0 ): Default roaming behavior, allow roaming in all scenarios + * (0 1 ): Disallow all roaming + * (1 0 ): Allow roaming when final bmissed + * (1 1 ): Reserve + * bit 8-9: Reserve for roaming + * bit 10: Disable css power collapse if setting + * bit 11: Disable sys sleep if setting + * bit 12-31: Reserve for future useage + * + * + */ +#define CFG_LATENCY_FLAGS_LOW CFG_INI_UINT("wlm_latency_flags_low", \ + 0, \ + 0xffffffff, \ + 0xa, \ + CFG_VALUE_OR_DEFAULT, \ + "WLM flags setting for low level") + +/* + * + * wlm_latency_flags_ultralow - WLM flags setting for ultralow level + * + * @min: 0x0 + * @max: 0xffffffff + * @defalut: 0xc83 + * + * |31 12| 11 | 10 |9 8|7 6|5 4|3 2| 1 | 0 | + * +------+------+------+------+------+------+------+-----+-----+ + * | RSVD | SSLP | CSLP | RSVD | Roam | RSVD | DWLT | DFS | SUP | + * +------+-------------+-------------+-------------------------+ + * | WAL | PS | Roam | Scan | + * + * bit 0: Avoid scan request from HLOS if setting + * bit 1: Skip DFS channel SCAN if setting + * bit 2-3: Define policy of dwell time/duration for each foreign channel + * (b2 b3) + * (0 0 ): Default scan dwell time + * (0 1 ): Reserve + * (1 0 ): Shrink off channel dwell time + * (1 1 ): Reserve + * bit 4-5: Reserve for scan + * bit 6-7: Define roaming policy + * (b6 b7) + * (0 0 ): Default roaming behavior, allow roaming in all scenarios + * (0 1 ): Disallow all roaming + * (1 0 ): Allow roaming when final bmissed + * (1 1 ): Reserve + * bit 8-9: Reserve for roaming + * bit 10: Disable css power collapse if setting + * bit 11: Disable sys sleep if setting + * bit 12-31: Reserve for future useage + * + * + */ +#define CFG_LATENCY_FLAGS_ULTLOW CFG_INI_UINT("wlm_latency_flags_ultralow",\ + 0, \ + 0xffffffff, \ + 0xc83, \ + CFG_VALUE_OR_DEFAULT, \ + "WLM flags for ultralow level") + +#define CFG_FE_WLM_ALL \ + CFG(CFG_LATENCY_ENABLE) \ + CFG(CFG_LATENCY_RESET) \ + CFG(CFG_LATENCY_LEVEL) \ + CFG(CFG_LATENCY_FLAGS_NORMAL) \ + CFG(CFG_LATENCY_FLAGS_MOD) \ + CFG(CFG_LATENCY_FLAGS_LOW) \ + CFG(CFG_LATENCY_FLAGS_ULTLOW) + +#endif /* __CFG_MLME_FE_WLM_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_fe_wmm.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_fe_wmm.h new file mode 100644 index 0000000000000000000000000000000000000000..cb2b04fbc839d7fe2b0bf1eb1d26145cc3656e97 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_fe_wmm.h @@ -0,0 +1,1058 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_FE_WMM_H +#define __CFG_MLME_FE_WMM_H + +#define CFG_QOS_ENABLED CFG_BOOL( \ + "qos_enabled", \ + 0, \ + "QOS Enabled") + +#define CFG_WME_ENABLED CFG_BOOL( \ + "wme_enabled", \ + 1, \ + "WME Enabled") + +#define CFG_MAX_SP_LENGTH CFG_UINT( \ + "max_sp_length", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "MAX sp length") + +#define CFG_WSM_ENABLED CFG_BOOL( \ + "wsm_enabled", \ + 0, \ + "WSM Enabled") + +#define CFG_EDCA_PROFILE CFG_UINT( \ + "edca_profile", \ + 0, \ + 4, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Edca Profile") + +/* default TSPEC parameters for AC_VO */ +/* + * + * InfraDirAcVo - Set TSPEC direction for VO + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * This ini is used to set TSPEC direction for VO + * + * 0 - uplink + * 1 - direct link + * 2 - down link + * 3 - bidirectional link + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_DIR_AC_VO CFG_INI_UINT( \ + "InfraDirAcVo", \ + 0, \ + 3, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "direction for vo") + +/* + * + * InfraNomMsduSizeAcVo - Set normal MSDU size for VO + * @Min: 0x0 + * @Max: 0xFFFF + * @Default: 0x80D0 + * + * This ini is used to set normal MSDU size for VO + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_NOM_MSDU_SIZE_AC_VO CFG_INI_UINT( \ + "InfraNomMsduSizeAcVo", \ + 0x0, \ + 0xFFFF, \ + 0x80D0, \ + CFG_VALUE_OR_DEFAULT, \ + "MSDU size for VO") + +/* + * + * InfraMeanDataRateAcVo - Set mean data rate for VO + * @Min: 0x0 + * @Max: 0xFFFFFFFF + * @Default: 0x14500 + * + * This ini is used to set mean data rate for VO + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_MEAN_DATA_RATE_AC_VO CFG_INI_UINT( \ + "InfraMeanDataRateAcVo", \ + 0x0, \ + 0xFFFFFFFF, \ + 0x14500, \ + CFG_VALUE_OR_DEFAULT, \ + "mean data rate for VO") + +/* + * + * InfraMinPhyRateAcVo - Set min PHY rate for VO + * @Min: 0x0 + * @Max: 0xFFFFFFFF + * @Default: 0x5B8D80 + * + * This ini is used to set min PHY rate for VO + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_MIN_PHY_RATE_AC_VO CFG_INI_UINT( \ + "InfraMinPhyRateAcVo", \ + 0x0, \ + 0xFFFFFFFF, \ + 0x5B8D80, \ + CFG_VALUE_OR_DEFAULT, \ + "min PHY rate for VO") + +/* + * + * InfraSbaAcVo - Set surplus bandwidth allowance for VO + * @Min: 0x2001 + * @Max: 0xFFFF + * @Default: 0x2001 + * + * This ini is used to set surplus bandwidth allowance for VO + * + * Related: None. + * + * Supported Feature: WMM +* + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_SBA_AC_VO CFG_INI_UINT( \ + "InfraSbaAcVo", \ + 0x2001, \ + 0xFFFF, \ + 0x2001, \ + CFG_VALUE_OR_DEFAULT, \ + "surplus bandwidth allowance for VO") +/* + * + * InfraDirAcVi - Set TSPEC direction for VI + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * This ini is used to set TSPEC direction for VI + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_DIR_AC_VI CFG_INI_UINT( \ + "InfraDirAcVi", \ + 0, \ + 3, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "TSPEC direction for VI") + +/* + * + * InfraNomMsduSizeAcVi - Set normal MSDU size for VI + * @Min: 0x0 + * @Max: 0xFFFF + * @Default: 0x85DC + * + * This ini is used to set normal MSDU size for VI + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_NOM_MSDU_SIZE_AC_VI CFG_INI_UINT( \ + "InfraNomMsduSizeAcVi", \ + 0x0, \ + 0xFFFF, \ + 0x85DC, \ + CFG_VALUE_OR_DEFAULT, \ + "MSDU size for VI") + +/* + * + * InfraMeanDataRateAcVi - Set mean data rate for VI + * @Min: 0x0 + * @Max: 0xFFFFFFFF + * @Default: 0x57E40 + * + * This ini is used to set mean data rate for VI + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_MEAN_DATA_RATE_AC_VI CFG_INI_UINT( \ + "InfraMeanDataRateAcVi", \ + 0x0, \ + 0xFFFFFFFF, \ + 0x57E40, \ + CFG_VALUE_OR_DEFAULT, \ + "data rate for VI") + +/* + * + * InfraMinPhyRateAcVi - Set min PHY rate for VI + * @Min: 0x0 + * @Max: 0xFFFFFFFF + * @Default: 0x5B8D80 + * + * This ini is used to set min PHY rate for VI + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_MIN_PHY_RATE_AC_VI CFG_INI_UINT( \ + "InfraMinPhyRateAcVi", \ + 0x0, \ + 0xFFFFFFFF, \ + 0x5B8D80, \ + CFG_VALUE_OR_DEFAULT, \ + "min PHY rate for VI") + +/* + * + * InfraSbaAcVi - Set surplus bandwidth allowance for VI + * @Min: 0x2001 + * @Max: 0xFFFF + * @Default: 0x2001 + * + * This ini is used to set surplus bandwidth allowance for VI + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ + +#define CFG_QOS_WMM_SBA_AC_VI CFG_INI_UINT( \ + "InfraSbaAcVi", \ + 0x2001, \ + 0xFFFF, \ + 0x2001, \ + CFG_VALUE_OR_DEFAULT, \ + "surplus bandwidth allowance for VI") + +/* + * + * InfraUapsdVoSrvIntv - Set Uapsd service interval for voice + * @Min: 0 + * @Max: 4294967295UL + * @Default: 20 + * + * This ini is used to set Uapsd service interval(in ms) for voice. + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_UAPSD_VO_SRV_INTV CFG_INI_UINT( \ + "InfraUapsdVoSrvIntv", \ + 0, \ + 4294967295UL, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "Infra uapsd vo srv intv") + +/* + * + * InfraUapsdVoSuspIntv - Set Uapsd suspension interval for voice + * @Min: 0 + * @Max: 4294967295UL + * @Default: 2000 + * + * This ini is used to set Uapsd suspension interval(in ms) for voice. + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_UAPSD_VO_SUS_INTV CFG_INI_UINT( \ + "InfraUapsdVoSuspIntv", \ + 0, \ + 4294967295UL, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "Infra uapsd vo sus intv") + +/* + * + * InfraUapsdViSrvIntv - Set Uapsd service interval for video + * @Min: 0 + * @Max: 4294967295UL + * @Default: 300 + * + * This ini is used to set Uapsd service interval(in ms) for video. + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ + +#define CFG_QOS_WMM_UAPSD_VI_SRV_INTV CFG_INI_UINT( \ + "InfraUapsdViSrvIntv", \ + 0, \ + 4294967295UL, \ + 300, \ + CFG_VALUE_OR_DEFAULT, \ + "Infra uapsd vi srv intv") + +/* + * + * InfraUapsdViSuspIntv - Set Uapsd suspension interval for video + * @Min: 0 + * @Max: 4294967295UL + * @Default: 2000 + * + * This ini is used to set Uapsd suspension interval(in ms) for video + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_UAPSD_VI_SUS_INTV CFG_INI_UINT( \ + "InfraUapsdViSuspIntv", \ + 0, \ + 4294967295UL, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "Infra uapsd vi sus intv") + +/* + * + * InfraDirAcBe - Set TSPEC direction for BE + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * This ini is used to set TSPEC direction for BE + * + * 0 - uplink + * 1 - direct link + * 2 - down link + * 3 - bidirectional link + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_DIR_AC_BE CFG_INI_UINT( \ + "InfraDirAcBe", \ + 0, \ + 3, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "TSPEC direction for BE") + +/* + * + * InfraNomMsduSizeAcBe - Set normal MSDU size for BE + * @Min: 0x0 + * @Max: 0xFFFF + * @Default: 0x85DC + * + * This ini is used to set normal MSDU size for BE + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_NOM_MSDU_SIZE_AC_BE CFG_INI_UINT( \ + "InfraNomMsduSizeAcBe", \ + 0x0, \ + 0xFFFF, \ + 0x85DC, \ + CFG_VALUE_OR_DEFAULT, \ + "MSDU size for BE") + +/* + * + * InfraMeanDataRateAcBe - Set mean data rate for BE + * @Min: 0x0 + * @Max: 0xFFFFFFFF + * @Default: 0x493E0 + * + * This ini is used to set mean data rate for BE + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_MEAN_DATA_RATE_AC_BE CFG_INI_UINT( \ + "InfraMeanDataRateAcBe", \ + 0x0, \ + 0xFFFFFFFF, \ + 0x493E0, \ + CFG_VALUE_OR_DEFAULT, \ + "data rate for BE") + +/* + * + * InfraMinPhyRateAcBe - Set min PHY rate for BE + * @Min: 0x0 + * @Max: 0xFFFFFFFF + * @Default: 0x5B8D80 + * + * This ini is used to set min PHY rate for BE + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_MIN_PHY_RATE_AC_BE CFG_INI_UINT( \ + "InfraMinPhyRateAcBe", \ + 0x0, \ + 0xFFFFFFFF, \ + 0x5B8D80, \ + CFG_VALUE_OR_DEFAULT, \ + "min PHY rate for BE") + +/* + * + * InfraSbaAcBe - Set surplus bandwidth allowance for BE + * @Min: 0x2001 + * @Max: 0xFFFF + * @Default: 0x2001 + * + * This ini is used to set surplus bandwidth allowance for BE + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_SBA_AC_BE CFG_INI_UINT( \ + "InfraSbaAcBe", \ + 0x2001, \ + 0xFFFF, \ + 0x2001, \ + CFG_VALUE_OR_DEFAULT, \ + "surplus bandwidth allowance for BE") + +/* + * + * InfraUapsdBeSrvIntv - Set Uapsd service interval for BE + * @Min: 0 + * @Max: 4294967295UL + * @Default: 300 + * + * This ini is used to set Uapsd service interval(in ms) for BE + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_UAPSD_BE_SRV_INTV CFG_INI_UINT( \ + "InfraUapsdBeSrvIntv", \ + 0, \ + 4294967295UL, \ + 300, \ + CFG_VALUE_OR_DEFAULT, \ + "Infra uapsd be srv intv") + +/* + * + * InfraUapsdBeSuspIntv - Set Uapsd suspension interval for BE + * @Min: 0 + * @Max: 4294967295UL + * @Default: 2000 + * + * This ini is used to set Uapsd suspension interval(in ms) for BE + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_UAPSD_BE_SUS_INTV CFG_INI_UINT( \ + "InfraUapsdBeSuspIntv", \ + 0, \ + 4294967295UL, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "Infra uapsd vi sus intv") + +/* + * + * InfraDirAcBk - Set TSPEC direction for BK + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * This ini is used to set TSPEC direction for BK + * + * 0 - uplink + * 1 - direct link + * 2 - down link + * 3 - bidirectional link + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_DIR_AC_BK CFG_INI_UINT( \ + "InfraDirAcBk", \ + 0, \ + 3, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "TSPEC direction for BK") + +/* + * + * InfraNomMsduSizeAcBk - Set normal MSDU size for BK + * @Min: 0x0 + * @Max: 0xFFFF + * @Default: 0x85DC + * + * This ini is used to set normal MSDU size for BK + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_NOM_MSDU_SIZE_AC_BK CFG_INI_UINT( \ + "InfraNomMsduSizeAcBk", \ + 0x0, \ + 0xFFFF, \ + 0x85DC, \ + CFG_VALUE_OR_DEFAULT, \ + "MSDU size for BK") + +/* + * + * InfraMeanDataRateAcBk - Set mean data rate for BK + * @Min: 0x0 + * @Max: 0xFFFFFFFF + * @Default: 0x493E0 + * + * This ini is used to set mean data rate for BK + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_MEAN_DATA_RATE_AC_BK CFG_INI_UINT( \ + "InfraMeanDataRateAcBk", \ + 0x0, \ + 0xFFFFFFFF, \ + 0x493E0, \ + CFG_VALUE_OR_DEFAULT, \ + "data rate for BK") + +/* + * + * InfraMinPhyRateAcBk - Set min PHY rate for BK + * @Min: 0x0 + * @Max: 0xFFFFFFFF + * @Default: 0x5B8D80 + * + * This ini is used to set min PHY rate for BK + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_MIN_PHY_RATE_AC_BK CFG_INI_UINT( \ + "InfraMinPhyRateAcBk", \ + 0x0, \ + 0xFFFFFFFF, \ + 0x5B8D80, \ + CFG_VALUE_OR_DEFAULT, \ + "min PHY rate for BK") + +/* + * + * InfraSbaAcBk - Set surplus bandwidth allowance for BK + * @Min: 0x2001 + * @Max: 0xFFFF + * @Default: 0x2001 + * + * This ini is used to set surplus bandwidth allowance for BK + * + * The 13 least significant bits (LSBs) indicate the decimal part while the + * three MSBs indicate the integer part of the number. + * + * A value of 1 indicates that no additional allocation of time is requested. + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_SBA_AC_BK CFG_INI_UINT( \ + "InfraSbaAcBk", \ + 0x2001, \ + 0xFFFF, \ + 0x2001, \ + CFG_VALUE_OR_DEFAULT, \ + "surplus bandwidth allowance for BK") + +/* + * + * InfraUapsdBkSrvIntv - Set Uapsd service interval for BK + * @Min: 0 + * @Max: 4294967295UL + * @Default: 300 + * + * This ini is used to set Uapsd service interval(in ms) for BK + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_UAPSD_BK_SRV_INTV CFG_INI_UINT( \ + "InfraUapsdBkSrvIntv", \ + 0, \ + 4294967295UL, \ + 300, \ + CFG_VALUE_OR_DEFAULT, \ + "Infra uapsd bk srv intv") + +/* + * + * InfraUapsdBkSuspIntv - Set Uapsd suspension interval for BK + * @Min: 0 + * @Max: 4294967295UL + * @Default: 2000 + * + * This ini is used to set Uapsd suspension interval(in ms) for BK + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_UAPSD_BK_SUS_INTV CFG_INI_UINT( \ + "InfraUapsdBkSuspIntv", \ + 0, \ + 4294967295UL, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "Infra uapsd bk sus intv") + +/* WMM configuration */ +/* + * + * WmmIsEnabled - Enable WMM feature + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to enable/disable WMM. + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_MODE CFG_INI_UINT( \ + "WmmIsEnabled", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable WMM feature") + +/* + * + * 80211eIsEnabled - Enable 802.11e feature + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable 802.11e. + * + * Related: None. + * + * Supported Feature: 802.11e + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_80211E_ENABLED CFG_INI_BOOL( \ + "80211eIsEnabled", \ + 0, \ + "Enable 802.11e feature") + +/* + * + * UapsdMask - To setup U-APSD mask for ACs + * @Min: 0x00 + * @Max: 0xFF + * @Default: 0x00 + * + * This ini is used to setup U-APSD mask for ACs. + * + * Bit 0 set, Voice both deliver/trigger enabled + * Bit 1 set, Video both deliver/trigger enabled + * Bit 2 set, Background both deliver/trigger enabled + * Bit 3 set, Best Effort both deliver/trigger enabled + * others, reserved + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_UAPSD_MASK CFG_INI_UINT( \ + "UapsdMask", \ + 0x00, \ + 0xFF, \ + 0x00, \ + CFG_VALUE_OR_DEFAULT, \ + "setup U-APSD mask for ACs") + +/* + * + * ImplicitQosIsEnabled - Enableimplicit QOS + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable implicit QOS. + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_IMPLICIT_SETUP_ENABLED CFG_INI_BOOL( \ + "ImplicitQosIsEnabled", \ + 0, \ + "Enable implicit QOS") + +#ifdef FEATURE_WLAN_ESE +/* + * + * InfraInactivityInterval - To setup Infra Inactivity Interval for ACs + * @Min: 0 + * @Max: 4294967295UL + * @Default: 0 + * + * This ini is used to setup Infra Inactivity Interval for + * ACs. + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_INACTIVITY_INTERVAL CFG_INI_UINT( \ + "InfraInactivityInterval", \ + 0, \ + 4294967295UL, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Infra Inactivity Interval") + +#define QOS_CFG CFG(CFG_QOS_WMM_INACTIVITY_INTERVAL) +#else + +#define QOS_CFG + +#endif /* FEATURE_WLAN_ESE */ + +/* + * + * burstSizeDefinition - Set TS burst size + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set TS burst size + * + * 0 - burst is disabled + * 1 - burst is enabled + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_BURST_SIZE_DEFN CFG_INI_BOOL( \ + "burstSizeDefinition", \ + 0, \ + "burst size definition") + +/* + * + * tsInfoAckPolicy - Set TS ack policy + * @Min: 0x00 + * @Max: 0x01 + * @Default: 0x00 + * + * This ini is used to set TS ack policy + * + * TS Info Ack Policy can be either of the following values: + * + * 0 - normal ack + * 1 - HT immediate block ack + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_WMM_TS_INFO_ACK_POLICY CFG_INI_UINT( \ + "tsInfoAckPolicy", \ + 0, \ + 1, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "ts info ack policy") + +/* + * + * gAddTSWhenACMIsOff - Set ACM value for AC + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set ACM value for AC + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_QOS_ADDTS_WHEN_ACM_IS_OFF CFG_INI_BOOL( \ + "gAddTSWhenACMIsOff", \ + 0, \ + "ACM value for AC") + +/* + * + * DelayedTriggerFrmInt - UAPSD delay interval + * @Min: 1 + * @Max: 4294967295 + * @Default: 3000 + * + * This parameter controls the delay interval(in ms) of UAPSD auto trigger. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_TL_DELAYED_TRGR_FRM_INTERVAL CFG_INI_UINT( \ + "DelayedTriggerFrmInt", \ + 1, \ + 4294967295UL, \ + 3000, \ + CFG_VALUE_OR_DEFAULT, \ + "UAPSD auto trigger Interval") + +#define CFG_WMM_PARAMS_ALL \ + CFG(CFG_QOS_ENABLED) \ + CFG(CFG_WME_ENABLED) \ + CFG(CFG_MAX_SP_LENGTH) \ + CFG(CFG_WSM_ENABLED) \ + CFG(CFG_EDCA_PROFILE) \ + CFG(CFG_QOS_WMM_DIR_AC_VO) \ + CFG(CFG_QOS_WMM_NOM_MSDU_SIZE_AC_VO) \ + CFG(CFG_QOS_WMM_MEAN_DATA_RATE_AC_VO) \ + CFG(CFG_QOS_WMM_MIN_PHY_RATE_AC_VO) \ + CFG(CFG_QOS_WMM_SBA_AC_VO) \ + CFG(CFG_QOS_WMM_UAPSD_VO_SRV_INTV) \ + CFG(CFG_QOS_WMM_UAPSD_VO_SUS_INTV) \ + CFG(CFG_QOS_WMM_DIR_AC_VI) \ + CFG(CFG_QOS_WMM_NOM_MSDU_SIZE_AC_VI) \ + CFG(CFG_QOS_WMM_MEAN_DATA_RATE_AC_VI) \ + CFG(CFG_QOS_WMM_MIN_PHY_RATE_AC_VI) \ + CFG(CFG_QOS_WMM_SBA_AC_VI) \ + CFG(CFG_QOS_WMM_UAPSD_VI_SRV_INTV) \ + CFG(CFG_QOS_WMM_UAPSD_VI_SUS_INTV) \ + CFG(CFG_QOS_WMM_DIR_AC_BE) \ + CFG(CFG_QOS_WMM_NOM_MSDU_SIZE_AC_BE) \ + CFG(CFG_QOS_WMM_MEAN_DATA_RATE_AC_BE) \ + CFG(CFG_QOS_WMM_MIN_PHY_RATE_AC_BE) \ + CFG(CFG_QOS_WMM_SBA_AC_BE) \ + CFG(CFG_QOS_WMM_UAPSD_BE_SRV_INTV) \ + CFG(CFG_QOS_WMM_UAPSD_BE_SUS_INTV) \ + CFG(CFG_QOS_WMM_DIR_AC_BK) \ + CFG(CFG_QOS_WMM_NOM_MSDU_SIZE_AC_BK) \ + CFG(CFG_QOS_WMM_MEAN_DATA_RATE_AC_BK) \ + CFG(CFG_QOS_WMM_MIN_PHY_RATE_AC_BK) \ + CFG(CFG_QOS_WMM_SBA_AC_BK) \ + CFG(CFG_QOS_WMM_UAPSD_BK_SRV_INTV) \ + CFG(CFG_QOS_WMM_UAPSD_BK_SUS_INTV) \ + CFG(CFG_QOS_WMM_MODE) \ + CFG(CFG_QOS_WMM_80211E_ENABLED) \ + CFG(CFG_QOS_WMM_UAPSD_MASK) \ + CFG(CFG_QOS_WMM_IMPLICIT_SETUP_ENABLED) \ + QOS_CFG \ + CFG(CFG_QOS_WMM_BURST_SIZE_DEFN) \ + CFG(CFG_QOS_WMM_TS_INFO_ACK_POLICY) \ + CFG(CFG_QOS_ADDTS_WHEN_ACM_IS_OFF) \ + CFG(CFG_TL_DELAYED_TRGR_FRM_INTERVAL) + +#endif /* __CFG_MLME_FE_WMM_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_feature_flag.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_feature_flag.h new file mode 100644 index 0000000000000000000000000000000000000000..2fc896b5457d3d29dca271b48306512bbd5aa739 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_feature_flag.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_FEATURE_FLAG_H +#define __CFG_MLME_FEATURE_FLAG_H + +#define CFG_ACCEPT_SHORT_SLOT_ASSOC_ONLY CFG_BOOL( \ + "accept_short_slot_assoc", \ + 0, \ + "Accept short slot assoc only") + +#define CFG_HCF_ENABLED CFG_BOOL( \ + "enable_hcf", \ + 0, \ + "HCF enabled") + +#define CFG_RSN_ENABLED CFG_BOOL( \ + "enable_rsn", \ + 0, \ + "RSN enabled") + +#define CFG_11G_SHORT_PREAMBLE_ENABLED CFG_BOOL( \ + "enable_short_preamble_11g", \ + 0, \ + "Short Preamble Enable") + +#define CFG_11G_SHORT_SLOT_TIME_ENABLED CFG_BOOL( \ + "enable_short_slot_time_11g", \ + 1, \ + "Short Slot time enable") + +#define CFG_CHANNEL_BONDING_MODE CFG_UINT( \ + "channel_bonding_mode", \ + 0, \ + 10, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "channel bonding mode") + +#define CFG_BLOCK_ACK_ENABLED CFG_UINT( \ + "enable_block_ack", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "enable block Ack") +/* + * + * gEnableAMPDUPS - Enable the AMPDUPS + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set default AMPDUPS + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + +#define CFG_ENABLE_AMPDUPS CFG_INI_BOOL( \ + "gEnableAMPDUPS", \ + 0, \ + "Enable AMPDU") + +/* + * + * gFWMccRtsCtsProtection - RTS-CTS protection in MCC. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable use of long duration RTS-CTS protection + * when SAP goes off channel in MCC mode. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_FW_MCC_RTS_CTS_PROT CFG_INI_UINT( \ + "gFWMccRtsCtsProtection", \ + 0, 1, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "RTS-CTS protection in MCC") + +/* + * + * gFWMccBCastProbeResponse - Broadcast Probe Response in MCC. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable use of broadcast probe response to + * increase the detectability of SAP in MCC mode. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ + +#define CFG_FW_MCC_BCAST_PROB_RESP CFG_INI_UINT( \ + "gFWMccBCastProbeResponse", \ + 0, 1, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Broadcast Probe Response in MCC") + +/* + * + * gEnableMCCMode - Enable/Disable MCC feature. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable MCC feature. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_MCC_FEATURE CFG_INI_UINT( \ + "gEnableMCCMode", \ + 0, 1, 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable/Disable MCC feature.") + +/* + * + * gChannelBondingMode24GHz - Configures Channel Bonding in 24 GHz + * @Min: 0 + * @Max: 10 + * @Default: 1 + * + * This ini is used to set default channel bonding mode 24GHZ + * + * 0 - 20MHz IF bandwidth centered on IF carrier + * 1 - 40MHz IF bandwidth with lower 20MHz supporting the primary channel + * 2 - reserved + * 3 - 40MHz IF bandwidth with higher 20MHz supporting the primary channel + * 4 - 20/40MHZ offset LOW 40/80MHZ offset CENTERED + * 5 - 20/40MHZ offset CENTERED 40/80MHZ offset CENTERED + * 6 - 20/40MHZ offset HIGH 40/80MHZ offset CENTERED + * 7 - 20/40MHZ offset LOW 40/80MHZ offset LOW + * 8 - 20/40MHZ offset HIGH 40/80MHZ offset LOW + * 9 - 20/40MHZ offset LOW 40/80MHZ offset HIGH + * 10 - 20/40MHZ offset-HIGH 40/80MHZ offset HIGH + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_CHANNEL_BONDING_MODE_24GHZ CFG_INI_UINT( \ + "gChannelBondingMode24GHz", \ + 0, \ + 10, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Configures Channel Bonding in 24 GHz") + +/* + * + * gChannelBondingMode5GHz - Configures Channel Bonding in 5 GHz + * @Min: 0 + * @Max: 10 + * @Default: 0 + * + * This ini is used to set default channel bonding mode 5GHZ + * + * Values of 0 - 10 have the same meanings as for gChannelBondingMode24GHz. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_CHANNEL_BONDING_MODE_5GHZ CFG_INI_UINT( \ + "gChannelBondingMode5GHz", \ + 0, \ + 10, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Configures Channel Bonding in 5 GHz") + +#define CFG_FEATURE_FLAG_ALL \ + CFG(CFG_ACCEPT_SHORT_SLOT_ASSOC_ONLY) \ + CFG(CFG_HCF_ENABLED) \ + CFG(CFG_RSN_ENABLED) \ + CFG(CFG_FW_MCC_RTS_CTS_PROT) \ + CFG(CFG_FW_MCC_BCAST_PROB_RESP) \ + CFG(CFG_MCC_FEATURE) \ + CFG(CFG_11G_SHORT_PREAMBLE_ENABLED) \ + CFG(CFG_11G_SHORT_SLOT_TIME_ENABLED) \ + CFG(CFG_CHANNEL_BONDING_MODE) \ + CFG(CFG_BLOCK_ACK_ENABLED) \ + CFG(CFG_ENABLE_AMPDUPS) \ + CFG(CFG_CHANNEL_BONDING_MODE_24GHZ) \ + CFG(CFG_CHANNEL_BONDING_MODE_5GHZ) + +#endif /* __CFG_MLME_FEATURE_FLAG_H */ + diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_generic.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_generic.h new file mode 100644 index 0000000000000000000000000000000000000000..567ddca9514e8a5bfc5c8f294751ef1b4512d88f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_generic.h @@ -0,0 +1,772 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_GENERIC_H +#define __CFG_MLME_GENERIC_H + +#ifdef WLAN_FEATURE_11W +#define CFG_PMF_SA_QUERY_MAX_RETRIES_TYPE CFG_INI_UINT +#define CFG_PMF_SA_QUERY_RETRY_INTERVAL_TYPE CFG_INI_UINT +#else +#define CFG_PMF_SA_QUERY_MAX_RETRIES_TYPE CFG_UINT +#define CFG_PMF_SA_QUERY_RETRY_INTERVAL_TYPE CFG_UINT +#endif /*WLAN_FEATURE_11W*/ + +/* + * pmfSaQueryMaxRetries - Control PMF SA query retries for SAP + * @Min: 0 + * @Max: 20 + * @Default: 5 + * + * This ini to set the number of PMF SA query retries for SAP + * + * Related: None. + * + * Supported Feature: PMF(11W) + * + */ +#define CFG_PMF_SA_QUERY_MAX_RETRIES CFG_PMF_SA_QUERY_MAX_RETRIES_TYPE( \ + "pmfSaQueryMaxRetries", \ + 0, \ + 20, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "PMF SA query retries for SAP") +/* + * pmfSaQueryRetryInterval - Control PMF SA query retry interval + * for SAP in ms + * @Min: 10 + * @Max: 2000 + * @Default: 200 + * + * This ini to set the PMF SA query retry interval for SAP in ms + * + * Related: None. + * + * Supported Feature: PMF(11W) + * + */ +#define CFG_PMF_SA_QUERY_RETRY_INTERVAL CFG_PMF_SA_QUERY_RETRY_INTERVAL_TYPE( \ + "pmfSaQueryRetryInterval", \ + 10, \ + 2000, \ + 200, \ + CFG_VALUE_OR_DEFAULT, \ + "PMF SA query retry interval for SAP") + +/* + * + * enable_rtt_mac_randomization - Enable/Disable rtt mac randomization + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Usage: External + * + * + */ +#define CFG_ENABLE_RTT_MAC_RANDOMIZATION CFG_INI_BOOL( \ + "enable_rtt_mac_randomization", \ + 0, \ + "Enable RTT MAC randomization") + +#define CFG_RTT3_ENABLE CFG_BOOL( \ + "rtt3_enabled", \ + 1, \ + "RTT3 enable/disable info") + +/* + * + * g11hSupportEnabled - Enable 11h support + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set 11h support flag + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_11H_SUPPORT_ENABLED CFG_INI_BOOL( \ + "g11hSupportEnabled", \ + 1, \ + "11h Enable Flag") + +/* + * + * g11dSupportEnabled - Enable 11d support + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set 11d support flag + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_11D_SUPPORT_ENABLED CFG_INI_BOOL( \ + "g11dSupportEnabled", \ + 1, \ + "11d Enable Flag") + +/* + * + * BandCapability - Preferred band (0: Both 2.4G and 5G, + * 1: 2.4G only, + * 2: 5G only, + * 3: Both 2.4G and 5G, + * 4: 6G only, + * 5: Both 2.4G and 6G, + * 6: Both 5G and 6G, + * 7: 2.4G, 5G, and 6G) + * @Min: 0 + * @Max: 7 + * @Default: 7 + * + * This ini is used to set default band capability + * (0: Both 2.4G and 5G, 1: 2.4G only, 2: 5G only, 3: Both 2.4G and 5G, + * 4: 6G only, 5: Both 2.4G and 6G, 6: Both 5G and 6G, 7: 2.4G, 5G, and 6G) + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_BAND_CAPABILITY CFG_INI_UINT( \ + "BandCapability", \ + 0, \ + 7, \ + 7, \ + CFG_VALUE_OR_DEFAULT, \ + "Band Capability") + +/* + * + * gPreventLinkDown - Enable to prevent bus link from going down + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Enable to prevent bus link from going down. Useful for platforms that do not + * (yet) support link down suspend cases. + * + * Related: N/A + * + * Supported Feature: Suspend/Resume + * + * Usage: Internal + * + * + */ +#if defined(QCA_WIFI_NAPIER_EMULATION) || defined(QCA_WIFI_QCA6290) +#define CFG_PREVENT_LINK_DOWN CFG_INI_BOOL( \ + "gPreventLinkDown", \ + 1, \ + "Prevent Bus Link Down") +#else +#define CFG_PREVENT_LINK_DOWN CFG_INI_BOOL( \ + "gPreventLinkDown", \ + 0, \ + "Prevent Bus Link Down") +#endif /* QCA_WIFI_NAPIER_EMULATION */ + +/* + * + * gSelect5GHzMargin - Sets RSSI preference for 5GHz over 2.4GHz AP. + * @Min: 0 + * @Max: 60 + * @Default: 0 + * + * Prefer connecting to 5G AP even if its RSSI is lower by gSelect5GHzMargin + * dBm than 2.4G AP. This feature requires the dependent cfg.ini + * "gRoamPrefer5GHz" set to 1 + * + * Related: gRoamPrefer5GHz + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_SELECT_5GHZ_MARGIN CFG_INI_UINT( \ + "gSelect5GHzMargin", \ + 0, \ + 60, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Select 5Ghz Margin") + +/* + * + * gEnableMemDeepSleep - Sets Memory Deep Sleep on/off. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This option enables/disables memory deep sleep. + * Related: None + * + * Supported Feature: General + * + * Usage: External + * + * + */ +#define CFG_ENABLE_MEM_DEEP_SLEEP CFG_INI_BOOL( \ + "gEnableMemDeepSleep", \ + 1, \ + "Enable Memory Deep Sleep") + +/* + * + * + * gEnableCckTxFirOverride - Enable/disable CCK TxFIR Override + * @Min: 0 (disabled) + * @Max: 1 (enabled) + * @Default: 0 (disabled) + * + * When operating in an 802.11b mode, this configuration item forces a 2x2 radio + * configuration into 1x for Tx and 2x for Rx (ie 1x2) for regulatory compliance + * reasons. + * + * Related: enable2x2 + * + * Supported Feature: 802.11b, 2x2 + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_CCK_TX_FIR_OVERRIDE CFG_INI_BOOL( \ + "gEnableCckTxFirOverride", \ + 0, \ + "Enable CCK TX FIR Override") + +/* + * + * + * gEnableForceTargetAssert - Enable/disable SSR + * @Min: 0 (disabled) + * @Max: 1 (enabled) + * @Default: 0 (disabled) + * + * This INI item is used to control subsystem restart(SSR) test framework + * Set it's value to 1 to enable APPS trigerred SSR testing + * + * Related: None + * + * Supported Feature: General + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_CRASH_INJECT CFG_INI_BOOL( \ + "gEnableForceTargetAssert", \ + 0, \ + "Enable Crash Inject") + +/* + * + * + * gEnableLpassSupport - Enable/disable LPASS Support + * @Min: 0 (disabled) + * @Max: 1 (enabled) + * @Default: 0 (disabled) + * + * Related: None + * + * Supported Feature: General + * + * Usage: Internal/External + * + * + */ +#ifdef WLAN_FEATURE_LPSS +#define CFG_ENABLE_LPASS_SUPPORT CFG_INI_BOOL( \ + "gEnableLpassSupport", \ + 0, \ + "Enable LPASS Support") +#else +#define CFG_ENABLE_LPASS_SUPPORT CFG_BOOL( \ + "gEnableLpassSupport", \ + 0, \ + "Enable LPASS Support") +#endif + +/* + * + * + * gEnableSelfRecovery - Enable/disable Self Recovery + * @Min: 0 (disabled) + * @Max: 1 (enabled) + * @Default: 0 (disabled) + * + * Related: None + * + * Supported Feature: General + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_SELF_RECOVERY CFG_INI_BOOL( \ + "gEnableSelfRecovery", \ + 0, \ + "Enable Self Recovery") + +/* + * + * + * gSapDot11mc - Enable/disable SAP 802.11mc support + * @Min: 0 (disabled) + * @Max: 1 (enabled) + * @Default: 0 (disabled) + * + * Related: None + * + * Supported Feature: General + * + * Usage: Internal/External + * + * + */ +#define CFG_SAP_DOT11MC CFG_INI_BOOL( \ + "gSapDot11mc", \ + 0, \ + "SAP 802.11mc support") + +/* + * + * + * gEnableFatalEvent - Enable/Disable BUG report in case of fatal event + * @Min: 0 (disabled) + * @Max: 1 (enabled) + * @Default: 1 (enabled) + * + * Related: None + * + * Supported Feature: General + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_FATAL_EVENT_TRIGGER CFG_INI_BOOL( \ + "gEnableFatalEvent", \ + 1, \ + "Enable Fatal Event Trigger") + +/* + * + * gSub20ChannelWidth - Control sub 20 channel width (5/10 Mhz) + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to set the sub 20 channel width. + * gSub20ChannelWidth=0: indicates do not use Sub 20 MHz bandwidth + * gSub20ChannelWidth=1: Bring up SAP/STA in 5 MHz bandwidth + * gSub20ChannelWidth=2: Bring up SAP/STA in 10 MHz bandwidth + * + * Related: None + * + * Supported Feature: 5/10 Mhz channel width support + * + * Usage: Internal/External + * + * + */ +#define CFG_SUB_20_CHANNEL_WIDTH CFG_INI_UINT( \ + "gSub20ChannelWidth", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Sub 20 Channel Width") + +/* + * + * goptimize_chan_avoid_event - Optimize channel avoidance indication + * coming from firmware + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: None + * + * Supported Feature: General + * + * Usage: Internal/External + * + * + */ +#define CFG_OPTIMIZE_CA_EVENT CFG_INI_BOOL( \ + "goptimize_chan_avoid_event", \ + 0, \ + "Optimize FW CA Event") + +/* + * + * fw_timeout_crash - Enable/Disable BUG ON + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to Trigger host crash when firmware fails to send the + * response to host + * fw_timeout_crash = 0 Disabled + * fw_timeout_crash = 1 Trigger host crash + * + * Related: None + * + * Supported Feature: SSR + * + * Usage: Internal/External + * + * + */ +#define CFG_CRASH_FW_TIMEOUT CFG_INI_BOOL( \ + "fw_timeout_crash", \ + 1, \ + "Enable FW Timeout Crash") + +/* + * + * gDroppedPktDisconnectTh - Sets dropped packet threshold in firmware + * @Min: 0 + * @Max: 65535 + * @Default: 512 + * + * This INI is the packet drop threshold will trigger disconnect from remote + * peer. + * + * Related: None + * + * Supported Feature: connection + * + * Usage: External + * + * + */ +#define CFG_DROPPED_PKT_DISCONNECT_THRESHOLD CFG_INI_UINT( \ + "gDroppedPktDisconnectTh", \ + 0, \ + 65535, \ + 512, \ + CFG_VALUE_OR_DEFAULT, \ + "Dropped Pkt Disconnect threshold") + +/* + * + * gItoRepeatCount - sets ito repeated count + * @Min: 0 + * @Max: 5 + * @Default: 0 + * + * This ini sets the ito count in FW + * + * Usage: External + * + * + */ +#define CFG_ITO_REPEAT_COUNT CFG_INI_UINT( \ + "gItoRepeatCount", \ + 0, \ + 5, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "ITO Repeat Count") + +/* + * + * gEnableDeauthToDisassocMap - Enables deauth to disassoc map + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set default disassoc map + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_ENABLE_DEAUTH_TO_DISASSOC_MAP CFG_INI_BOOL( \ + "gEnableDeauthToDisassocMap", \ + 0, \ + "Enables deauth to disassoc map") + +/* + * + * gEnableDebugLog - Enable/Disable the Connection related logs + * @Min: 0 + * @Max: 0xFF + * @Default: 0x0F + * + * This ini is used to enable/disable the connection related logs + * 0x1 - Enable mgmt pkt logs (excpet probe req/rsp, beacons). + * 0x2 - Enable EAPOL pkt logs. + * 0x4 - Enable DHCP pkt logs. + * 0x8 - Enable mgmt action frames logs. + * 0x0 - Disable all the above connection related logs. + * The default value of 0x0F will enable all the above logs + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_DEBUG_PACKET_LOG CFG_INI_UINT( \ + "gEnableDebugLog", \ + 0, 0xFF, 0x0F, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable debug log") + +/* + * + * enable_beacon_reception_stats - Enable disable beacon reception stats + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable the beacon reception stats collected per + * vdev and then sent to the driver to be displayed in sysfs + * + * Related: None + * + * Supported Feature: Stats + * + * Usage: External + * + * + */ + #define CFG_ENABLE_BEACON_RECEPTION_STATS CFG_INI_BOOL( \ + "enable_beacon_reception_stats", \ + 0, \ + "Enable disable beacon reception stats") + +/* + * + * gRemoveTimeStampSyncCmd - Enable/Disable to remove time stamp sync cmd + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable the removal of time stamp sync cmd + * + * Usage: External + * + * + */ +#define CFG_REMOVE_TIME_STAMP_SYNC_CMD CFG_INI_BOOL( \ + "gRemoveTimeStampSyncCmd", \ + 0, \ + "Enable to remove time stamp sync cmd") + +/* + * + * disable_4way_hs_offload - Enable/Disable 4 way handshake offload to firmware + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * 0 4-way HS to be handled in firmware + * 1 4-way HS to be handled in supplicant + * + * Related: None + * + * Supported Feature: STA Roaming + * + * Usage: External + * + * + */ +#define CFG_DISABLE_4WAY_HS_OFFLOAD CFG_INI_BOOL("disable_4way_hs_offload", \ + 0, \ + "Enable/disable 4 way handshake offload to firmware") + +/* + * + * mgmt_retry_max - Maximum Retries for mgmt frames + * @Min: 0 + * @Max: 31 + * @Default: 15 + * + * This ini is used to set maximum retries for mgmt frames + * + * Supported Feature: STA/SAP + * + * Usage: External + * + * + */ +#define CFG_MGMT_RETRY_MAX CFG_INI_UINT( \ + "mgmt_retry_max", \ + 0, \ + 31, \ + 15, \ + CFG_VALUE_OR_DEFAULT, \ + "Max retries for mgmt frames") + +/* + * + * bmiss_skip_full_scan - To decide whether firmware does channel map based + * partial scan or partial scan followed by full scan in case no candidate is + * found in partial scan. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * 0 : Based on the channel map , firmware does scan to find new AP. if AP is + * not found then it does a full scan on all valid channels. + * 1 : Firmware does channel map based partial scan only. + * + * Related: None + * + * Supported Feature: STA Roaming + * + * Usage: External + * + * + */ +#define CFG_BMISS_SKIP_FULL_SCAN CFG_INI_BOOL("bmiss_skip_full_scan", \ + 0, \ + "To decide partial/partial scan followed by full scan") + +/* + * + * gEnableRingBuffer - Enable Ring Buffer for Bug Report + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable Ring Buffer + * + * Related: None + * + * Supported Feature: STA/SAP + * + * Usage: External + * + * + */ +#define CFG_ENABLE_RING_BUFFER CFG_INI_BOOL( \ + "gEnableRingBuffer", \ + 1, \ + "To Enable Ring Buffer") + +/* + * + * sae_connect_retries - Bit mask to retry Auth and full connection on assoc + * timeout to same AP and auth retries during roaming + * @Min: 0x0 + * @Max: 0x53 + * @Default: 0x49 + * + * This ini is used to set max auth retry in auth phase of roaming and initial + * connection and max connection retry in case of assoc timeout. MAX Auth + * retries are capped to 3, connection retries are capped to 2 and roam Auth + * retry is capped to 1. + * Default is 0x49 i.e. 1 retry each. + * + * Bits Retry Type + * BIT[0:2] AUTH retries + * BIT[3:5] Connection reties + * BIT[6:8] ROAM AUTH retries + * + * Some Possible values are as below + * 0 - NO auth/roam Auth retry and NO full connection retry after + * assoc timeout + * 0x49 - 1 auth/roam auth retry and 1 full connection retry + * 0x52 - 1 roam auth retry, 2 auth retry and 2 full connection retry + * 0x1 /0x2 - 0 roam auth retry, 1 or 2 auth retry respectively and NO full + * connection retry + * 0x8 /0x10 - 0 roam auth retry,NO auth retry and 1 or 2 full connection retry + * respectively. + * 0x4A - 1 roam auth retry,2 auth retry and 1 full connection retry + * 0x51 - 1 auth/roam auth retry and 2 full connection retry + * + * Related: None + * + * Supported Feature: STA SAE + * + * Usage: External + * + * + */ +#define CFG_SAE_CONNECION_RETRIES CFG_INI_UINT("sae_connect_retries", \ + 0, 0x53, 0x49, CFG_VALUE_OR_DEFAULT, \ + "Bit mask to retry Auth and full connection on assoc timeout to same AP for SAE connection") + +#define CFG_GENERIC_ALL \ + CFG(CFG_ENABLE_DEBUG_PACKET_LOG) \ + CFG(CFG_PMF_SA_QUERY_MAX_RETRIES) \ + CFG(CFG_PMF_SA_QUERY_RETRY_INTERVAL) \ + CFG(CFG_ENABLE_RTT_MAC_RANDOMIZATION) \ + CFG(CFG_RTT3_ENABLE) \ + CFG(CFG_11H_SUPPORT_ENABLED) \ + CFG(CFG_11D_SUPPORT_ENABLED) \ + CFG(CFG_BAND_CAPABILITY) \ + CFG(CFG_PREVENT_LINK_DOWN) \ + CFG(CFG_SELECT_5GHZ_MARGIN) \ + CFG(CFG_ENABLE_MEM_DEEP_SLEEP) \ + CFG(CFG_ENABLE_CCK_TX_FIR_OVERRIDE) \ + CFG(CFG_ENABLE_CRASH_INJECT) \ + CFG(CFG_ENABLE_LPASS_SUPPORT) \ + CFG(CFG_ENABLE_SELF_RECOVERY) \ + CFG(CFG_ENABLE_DEAUTH_TO_DISASSOC_MAP) \ + CFG(CFG_DISABLE_4WAY_HS_OFFLOAD) \ + CFG(CFG_SAP_DOT11MC) \ + CFG(CFG_ENABLE_FATAL_EVENT_TRIGGER) \ + CFG(CFG_SUB_20_CHANNEL_WIDTH) \ + CFG(CFG_OPTIMIZE_CA_EVENT) \ + CFG(CFG_CRASH_FW_TIMEOUT) \ + CFG(CFG_DROPPED_PKT_DISCONNECT_THRESHOLD) \ + CFG(CFG_ITO_REPEAT_COUNT) \ + CFG(CFG_ENABLE_BEACON_RECEPTION_STATS) \ + CFG(CFG_REMOVE_TIME_STAMP_SYNC_CMD) \ + CFG(CFG_MGMT_RETRY_MAX) \ + CFG(CFG_BMISS_SKIP_FULL_SCAN) \ + CFG(CFG_ENABLE_RING_BUFFER) \ + CFG(CFG_SAE_CONNECION_RETRIES) +#endif /* __CFG_MLME_GENERIC_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_he_caps.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_he_caps.h new file mode 100644 index 0000000000000000000000000000000000000000..ecba16b1e8a4f9f350ad267969ad6ce463d459b3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_he_caps.h @@ -0,0 +1,917 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_HE_CAPS_H +#define __CFG_MLME_HE_CAPS_H + +#define CFG_HE_CONTROL CFG_BOOL( \ + "he_control", \ + 0, \ + "HE Control") + +#define CFG_HE_TWT_REQUESTOR CFG_BOOL( \ + "he_twt_requestor", \ + 0, \ + "HE Twt Requestor") + +#define CFG_HE_TWT_RESPONDER CFG_BOOL( \ + "he_twt_responder", \ + 0, \ + "HE Twt Responder") + +#define CFG_HE_FRAGMENTATION CFG_UINT( \ + "he_fragmentation", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HE Fragmentation") + +#define CFG_HE_MAX_FRAG_MSDU CFG_UINT( \ + "he_max_frag_msdu", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HE Max Frag Msdu") + +#define CFG_HE_MIN_FRAG_SIZE CFG_UINT( \ + "he_min_frag_size", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HE Min Frag Size") + +#define CFG_HE_TRIG_PAD CFG_UINT( \ + "he_trig_pad", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HE Trig Pad") + +#define CFG_HE_MTID_AGGR_RX CFG_UINT( \ + "he_mtid_aggr_rx", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HE Mtid Aggr") + +#define CFG_HE_LINK_ADAPTATION CFG_UINT( \ + "he_link_adaptation", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HE Link Adaptation") + +#define CFG_HE_ALL_ACK CFG_BOOL( \ + "he_all_ack", \ + 0, \ + "HE All Ack") + +#define CFG_HE_TRIGD_RSP_SCHEDULING CFG_BOOL( \ + "he_trigd_rsp_scheduling", \ + 0, \ + "HE Trigd Rsp Scheduling") + +#define CFG_HE_BUFFER_STATUS_RPT CFG_BOOL( \ + "he_buffer_status_rpt", \ + 0, \ + "HE Buffer Status Rpt") + +#define CFG_HE_BCAST_TWT CFG_BOOL( \ + "he_bcast_twt", \ + 0, \ + "HE Bcast twt") + +#define CFG_HE_BA_32BIT CFG_BOOL( \ + "he_ba_32bit", \ + 0, \ + "HE BA 32Bit") + +#define CFG_HE_MU_CASCADING CFG_BOOL( \ + "he_mu_cascading", \ + 0, \ + "HE Mu Cascading") + +#define CFG_HE_MULTI_TID CFG_BOOL( \ + "he_multi_tid", \ + 0, \ + "HE Multi Tid") + +#define CFG_HE_OMI CFG_BOOL( \ + "he_omi", \ + 0, \ + "HE Omi") + +#define CFG_HE_OFDMA_RA CFG_BOOL( \ + "he_ofdma_ra", \ + 0, \ + "HE Ofdma Ra") + +#define CFG_HE_MAX_AMPDU_LEN CFG_UINT( \ + "he_max_ampdu_len", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HE Max Ampdu Len") + +#define CFG_HE_AMSDU_FRAG CFG_BOOL( \ + "he_amspdu_frag", \ + 0, \ + "HE Amsdu Frag") + +#define CFG_HE_FLEX_TWT_SCHED CFG_BOOL( \ + "he_flex_twt_sched", \ + 0, \ + "HE Flex Twt Sched") + +#define CFG_HE_RX_CTRL CFG_BOOL( \ + "he_rx_ctrl", \ + 0, \ + "HE Rx Ctrl") + +#define CFG_HE_BSRP_AMPDU_AGGR CFG_BOOL( \ + "he_bsrp_ampdu_aggr", \ + 0, \ + "He Bspr Ampdu Aggr") + +#define CFG_HE_QTP CFG_BOOL( \ + "he_qtp", \ + 0, \ + "He Qtp") + +#define CFG_HE_A_BQR CFG_BOOL( \ + "he_a_bqr", \ + 0, \ + "He A Bqr") + +#define CFG_HE_SR_RESPONDER CFG_BOOL( \ + "he_sr_responder", \ + 0, \ + "He Sr Responder") + +#define CFG_HE_NDP_FEEDBACK_SUPP CFG_BOOL( \ + "he_ndp_feedback_supp", \ + 0, \ + "He Ndp Feedback Supp") + +#define CFG_HE_OPS_SUPP CFG_BOOL( \ + "he_ops_supp", \ + 0, \ + "He Ops Supp") + +#define CFG_HE_AMSDU_IN_AMPDU CFG_BOOL( \ + "he_amsdu_in_ampdu", \ + 0, \ + "He Amsdu In Ampdu") + +#define CFG_HE_MTID_AGGR_TX CFG_UINT( \ + "he_mtid_aggr_tx", \ + 0, \ + 0x7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He MTid Aggr Tx") + +#define CFG_HE_SUB_CH_SEL_TX CFG_BOOL( \ + "he_sub_ch_sel_tx", \ + 0, \ + "He Sub cg sel tx") + +#define CFG_HE_UL_2X996_RU CFG_BOOL( \ + "he_ul_2x996_ru", \ + 0, \ + "He Ul 2x996 Ru") + +#define CFG_HE_OM_CTRL_UL_MU_DIS_RX CFG_BOOL( \ + "he_om_ctrl_ul_mu_dis_rx", \ + 0, \ + "He Om Ctrl Ul My Dis Rx") + +#define CFG_HE_DYNAMIC_SMPS CFG_BOOL( \ + "he_dynamic_smps", \ + 0, \ + "He Dyanmic SMPS") + +#define CFG_HE_PUNCTURED_SOUNDING CFG_BOOL( \ + "he_punctured_sounding", \ + 0, \ + "He Punctured Sounding") + +#define CFG_HE_HT_VHT_TRG_FRM_RX CFG_BOOL( \ + "ht_vht_trg_frm_rx", \ + 0, \ + "HT VHT Trigger frame Rx") + +#define CFG_HE_CHAN_WIDTH CFG_UINT( \ + "he_chan_width", \ + 0, \ + 0x3F, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Chan Width") + +#define CFG_HE_RX_PREAM_PUNC CFG_UINT( \ + "he_rx_pream_punc", \ + 0, \ + 0xF, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Rx Pream Punc") + +#define CFG_HE_CLASS_OF_DEVICE CFG_BOOL( \ + "he_class_of_device", \ + 0, \ + "He Class Of Device") + +#define CFG_HE_LDPC CFG_BOOL( \ + "he_ldpc", \ + 0, \ + "He Ldpc") + +#define CFG_HE_LTF_PPDU CFG_UINT( \ + "he_ltf_ppdu", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Ltf Ppdu") + +#define CFG_HE_MIDAMBLE_RX_MAX_NSTS CFG_UINT( \ + "he_midamble_rx_max_nsts", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Midamble Rx Max Nsts") + +#define CFG_HE_LTF_NDP CFG_UINT( \ + "he_ltf_ndp", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Ltf Ndp") + +#define CFG_HE_TX_STBC_LT80 CFG_BOOL( \ + "he_tx_stbc_lt80_sta", \ + 0, \ + "He Tx Stbc Lt80") + +#define CFG_HE_RX_STBC_LT80 CFG_BOOL( \ + "he_rx_stbc_lt80", \ + 0, \ + "He Rx Stbc Lt80") + +#define CFG_HE_DOPPLER CFG_UINT( \ + "he_doppler", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Doppler") + +#define CFG_HE_DCM_TX CFG_UINT( \ + "he_dcm_tx", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Dcm Tx") + +#define CFG_HE_DCM_RX CFG_UINT( \ + "he_dcm_rx", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Dcm Rx") + +#define CFG_HE_MU_PPDU CFG_BOOL( \ + "he_mu_ppdu", \ + 0, \ + "He Mu Ppdu") + +#define CFG_HE_SU_BEAMFORMER CFG_BOOL( \ + "he_su_beamformer", \ + 0, \ + "He Su Beamformer") + +#define CFG_HE_SU_BEAMFORMEE CFG_BOOL( \ + "he_su_beamformee", \ + 0, \ + "He Su Beamformee") + +#define CFG_HE_MU_BEAMFORMER CFG_BOOL( \ + "he_mu_beamformer", \ + 0, \ + "He Mu Beamformer") + +#define CFG_HE_BFEE_STS_LT80 CFG_UINT( \ + "he_bfee_sts_lt80", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Mu Bfee Sts Lt80") + +#define CFG_HE_BFEE_STS_GT80 CFG_UINT( \ + "he_bfee_sts_lt80", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Mu Bfee Sts Gt80") + +#define CFG_HE_NUM_SOUND_LT80 CFG_UINT( \ + "he_num_sound_lt80", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Num Sound Lt80") + +#define CFG_HE_NUM_SOUND_GT80 CFG_UINT( \ + "he_num_sound_gt80", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Num Sound Gt80") + +#define CFG_HE_SU_FEED_TONE16 CFG_BOOL( \ + "he_su_feed_tone16", \ + 0, \ + "He Su Feed Tone16") + +#define CFG_HE_MU_FEED_TONE16 CFG_BOOL( \ + "he_mu_feed_tone16", \ + 0, \ + "He Mu Feed Tone16") + +#define CFG_HE_CODEBOOK_SU CFG_BOOL( \ + "he_codebook_su", \ + 0, \ + "He Codebook Su") + +#define CFG_HE_CODEBOOK_MU CFG_BOOL( \ + "he_codebook_mu", \ + 0, \ + "He Codebook Mu") + +#define CFG_HE_BFRM_FEED CFG_UINT( \ + "he_bfrm_feed", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Bfrm Feed") + +#define CFG_HE_ER_SU_PPDU CFG_BOOL( \ + "he_bfrm_feed", \ + 0, \ + "He Er Su Ppdu") + +#define CFG_HE_DL_PART_BW CFG_BOOL( \ + "he_dl_part_bw", \ + 0, \ + "He Dl Part Bw") + +#define CFG_HE_PPET_PRESENT CFG_BOOL( \ + "he_ppet_present", \ + 0, \ + "He Pper Present") + +#define CFG_HE_SRP CFG_BOOL( \ + "he_srp", \ + 0, \ + "He Srp") + +#define CFG_HE_POWER_BOOST CFG_BOOL( \ + "he_power_boost", \ + 0, \ + "He Power Boost") + +#define CFG_HE_4x_LTF_GI CFG_BOOL( \ + "he_4x_ltf_gi", \ + 0, \ + "He 4x Ltf Gi") + +#define CFG_HE_MAX_NC CFG_UINT( \ + "he_max_nc", \ + 0, \ + 7, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Max Nc") + +#define CFG_HE_RX_STBC_GT80 CFG_BOOL( \ + "he_rx_stbc_gt80", \ + 0, \ + "He Rx Stbc Gt80") + +#define CFG_HE_TX_STBC_GT80 CFG_BOOL( \ + "he_Tx_stbc_gt80", \ + 0, \ + "He Tx Stbc Gt80") + +#define CFG_HE_ER_4x_LTF_GI CFG_BOOL( \ + "he_er_4x_ltf_gi", \ + 0, \ + "He Er 4x Ltf Gi") + +#define CFG_HE_PPDU_20_IN_40MHZ_2G CFG_BOOL( \ + "he_ppdu_20_in_40mhz_2g", \ + 0, \ + "He Ppdu 20 In 40Mhz 2g") + +#define CFG_HE_PPDU_20_IN_160_80P80MHZ CFG_BOOL( \ + "he_ppdu_20_in_160_80p80mhz", \ + 0, \ + "He Ppdu 20 In 160 80p80mhz") + +#define CFG_HE_PPDU_80_IN_160_80P80MHZ CFG_BOOL( \ + "he_ppdu_80_in_160_80p80mhz", \ + 0, \ + "He Ppdu 80 In 160 80p80mhz") + +#define CFG_HE_ER_1X_HE_LTF_GI CFG_BOOL( \ + "he_er_1x_he_ltf_gi", \ + 0, \ + "He Er 1x He Ltf Gi") + +#define CFG_HE_MIDAMBLE_TXRX_1X_HE_LTF CFG_BOOL( \ + "he_midamble_txrx_1x_he_ltf", \ + 0, \ + "He Midamble Tx Rx 1x He Ltf") + +#define CFG_HE_DCM_MAX_BW CFG_UINT( \ + "he_dcm_max_bw", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Dcm Max Bw") + +#define CFG_HE_LONGER_16_SIGB_OFDM_SYM CFG_BOOL( \ + "he_longer_16_sigb_ofdm_sys", \ + 0, \ + "He Longer 16 Sigb Ofdm Sys") + +#define CFG_HE_NON_TRIG_CQI_FEEDBACK CFG_BOOL( \ + "he_rx_mcs_map_lt_80", \ + 0, \ + "He Non Trig Cqi Feedback") + +#define CFG_HE_TX_1024_QAM_LT_242_RU CFG_BOOL( \ + "he_tx_1024_qam_lt_242_ru", \ + 0, \ + "He Tx 1024 Qam Lt 242 Ru") + +#define CFG_HE_RX_1024_QAM_LT_242_RU CFG_BOOL( \ + "he_rx_1024_qam_lt_242_ru", \ + 0, \ + "He Rx 1024 Qam Lt 242 Ru") + +#define CFG_HE_RX_FULL_BW_MU_CMPR_SIGB CFG_BOOL( \ + "he_rx_full_bw_cmpr_sigb", \ + 0, \ + "He Rx Full Bw Mu Cmpr Sigb") + +#define CFG_HE_RX_FULL_BW_MU_NON_CMPR_SIGB CFG_BOOL( \ + "he_rx_full_bw_mu_non_cmpr_sigb", \ + 0, \ + "He Rx Full Bw Mu Non Cmpr Sigb") + +/* 11AX related INI configuration */ +/* + * + * he_rx_mcs_map_lt_80 - configure Rx HE-MCS Map for ≤ 80 MHz + * @Min: 0 + * @Max: 0xFFFF + * @Default: 0xFFFA + * + * This ini is used to configure Rx HE-MCS Map for ≤ 80 MHz + * 0:1 Max HE-MCS For 1 SS + * 2:3 Max HE-MCS For 2 SS + * 4:5 Max HE-MCS For 3 SS + * 6:7 Max HE-MCS For 4 SS + * 8:9 Max HE-MCS For 5 SS + * 10:11 Max HE-MCS For 6 SS + * 12:13 Max HE-MCS For 7 SS + * 14:15 Max HE-MCS For 8 SS + * + * 0 indicates support for HE-MCS 0-7 for n spatial streams + * 1 indicates support for HE-MCS 0-9 for n spatial streams + * 2 indicates support for HE-MCS 0-11 for n spatial streams + * 3 indicates that n spatial streams is not supported for HE PPDUs + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: External + * + * + */ +#define CFG_HE_RX_MCS_MAP_LT_80 CFG_INI_UINT( \ + "he_rx_mcs_map_lt_80", \ + 0, \ + 0xFFFF, \ + 0xFFFA, \ + CFG_VALUE_OR_DEFAULT, \ + "He Rx Mcs Map Lt 80") + +/* 11AX related INI configuration */ +/* + * + * he_tx_mcs_map_lt_80 - configure Tx HE-MCS Map for ≤ 80 MHz + * @Min: 0 + * @Max: 0xFFFF + * @Default: 0xFFFA + * + * This ini is used to configure Tx HE-MCS Map for ≤ 80 MHz + * 0:1 Max HE-MCS For 1 SS + * 2:3 Max HE-MCS For 2 SS + * 4:5 Max HE-MCS For 3 SS + * 6:7 Max HE-MCS For 4 SS + * 8:9 Max HE-MCS For 5 SS + * 10:11 Max HE-MCS For 6 SS + * 12:13 Max HE-MCS For 7 SS + * 14:15 Max HE-MCS For 8 SS + * + * 0 indicates support for HE-MCS 0-7 for n spatial streams + * 1 indicates support for HE-MCS 0-9 for n spatial streams + * 2 indicates support for HE-MCS 0-11 for n spatial streams + * 3 indicates that n spatial streams is not supported for HE PPDUs + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: External + * + * + */ +#define CFG_HE_TX_MCS_MAP_LT_80 CFG_INI_UINT( \ + "he_tx_mcs_map_lt_80", \ + 0, \ + 0xFFFF, \ + 0xFFFA, \ + CFG_VALUE_OR_DEFAULT, \ + "He Tx Mcs Map Lt 80") +/* 11AX related INI configuration */ +/* + * + * he_rx_mcs_map_160 - configure Rx HE-MCS Map for 160 MHz + * @Min: 0 + * @Max: 0xFFFF + * @Default: 0xFFFA + * + * This ini is used to configure Rx HE-MCS Map for 160 MHz + * 0:1 Max HE-MCS For 1 SS + * 2:3 Max HE-MCS For 2 SS + * 4:5 Max HE-MCS For 3 SS + * 6:7 Max HE-MCS For 4 SS + * 8:9 Max HE-MCS For 5 SS + * 10:11 Max HE-MCS For 6 SS + * 12:13 Max HE-MCS For 7 SS + * 14:15 Max HE-MCS For 8 SS + * + * 0 indicates support for HE-MCS 0-7 for n spatial streams + * 1 indicates support for HE-MCS 0-9 for n spatial streams + * 2 indicates support for HE-MCS 0-11 for n spatial streams + * 3 indicates that n spatial streams is not supported for HE PPDUs + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: External + * + * + */ +#define CFG_HE_RX_MCS_MAP_160 CFG_INI_UINT( \ + "he_rx_mcs_map_160", \ + 0, \ + 0xFFFF, \ + 0xFFFA, \ + CFG_VALUE_OR_DEFAULT, \ + "He Rx Mcs Map 160") + +/* 11AX related INI configuration */ +/* + * + * he_tx_mcs_map_160 - configure Tx HE-MCS Map for 160 MHz + * @Min: 0 + * @Max: 0xFFFF + * @Default: 0xFFFA + * + * This ini is used to configure Tx HE-MCS Map for 160 MHz + * 0:1 Max HE-MCS For 1 SS + * 2:3 Max HE-MCS For 2 SS + * 4:5 Max HE-MCS For 3 SS + * 6:7 Max HE-MCS For 4 SS + * 8:9 Max HE-MCS For 5 SS + * 10:11 Max HE-MCS For 6 SS + * 12:13 Max HE-MCS For 7 SS + * 14:15 Max HE-MCS For 8 SS + * + * 0 indicates support for HE-MCS 0-7 for n spatial streams + * 1 indicates support for HE-MCS 0-9 for n spatial streams + * 2 indicates support for HE-MCS 0-11 for n spatial streams + * 3 indicates that n spatial streams is not supported for HE PPDUs + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: External + * + * + */ +#define CFG_HE_TX_MCS_MAP_160 CFG_INI_UINT( \ + "he_tx_mcs_map_160", \ + 0, \ + 0xFFFF, \ + 0xFFFA, \ + CFG_VALUE_OR_DEFAULT, \ + "He Tx Mcs Map 160") + +#define CFG_HE_RX_MCS_MAP_80_80 CFG_UINT( \ + "he_rx_mcs_map_80_80", \ + 0, \ + 0xFFFF, \ + 0xFFF0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Rx Mcs Map 80 80") + +#define CFG_HE_TX_MCS_MAP_80_80 CFG_UINT( \ + "he_tx_mcs_map_80_80", \ + 0, \ + 0xFFFF, \ + 0xFFF0, \ + CFG_VALUE_OR_DEFAULT, \ + "He tx Mcs Map 80 80") + +#define CFG_HE_OPS_BASIC_MCS_NSS CFG_UINT( \ + "cfg_he_ops_basic_mcs_nss", \ + 0x0000, \ + 0xFFFF, \ + 0xFFFC, \ + CFG_VALUE_OR_DEFAULT, \ + "He Ops Basic Mcs NSS") + +/* 11AX related INI configuration */ +/* + * + * he_ul_mumimo - configure ul mu capabilities + * @Min: 0 + * @Max: 3 + * @Default: 0 + * + * This ini is used to configure capabilities of ul mu-mimo + * 0-> no support + * 1-> full bandwidth support + * 2-> partial bandwidth support + * 3-> full and partial bandwidth support + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: Internal/External + * + * + */ +#define CFG_HE_UL_MUMIMO CFG_INI_UINT( \ + "he_ul_mumimo", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "He Ul Mumimo") + +/* 11AX related INI configuration */ +/* + * + * he_dynamic_frag_support - configure dynamic fragmentation + * @Min: 0 + * @Max: 3 + * @Default: 0 + * + * This ini is used to configure dynamic fragmentation. + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: Internal/External + * + * + */ +#define CFG_HE_DYNAMIC_FRAGMENTATION CFG_INI_UINT( \ + "he_dynamic_frag_support", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HE Dynamic Fragmentation") + + +/* + * + * enable_ul_mimo- Enable UL MIMO. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable or disable UL MIMO. + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: External + * + * + */ +#define CFG_ENABLE_UL_MIMO CFG_INI_BOOL( \ + "enable_ul_mimo", \ + 1, \ + "He Enble Ul Mimo Name") + +/* + * + * enable_ul_ofdma- Enable UL OFDMA. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable or disable UL OFDMA. + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: External + * + * + */ + +#define CFG_ENABLE_UL_OFDMA CFG_INI_BOOL( \ + "enable_ul_ofdma", \ + 1, \ + "He Enable Ul Ofdma Name") + +/* + * + * he_sta_obsspd- 11AX HE OBSS PD bit field + * @Min: 0 + * @Max: uin32_t max + * @Default: 0x15b8c2ae + * + * 4 Byte value with each byte representing a signed value for following params: + * Param Bit position Default + * OBSS_PD min (primary) 7:0 -82 (0xae) + * OBSS_PD max (primary) 15:8 -62 (0xc2) + * Secondary channel Ed 23:16 -72 (0xb8) + * TX_PWR(ref) 31:24 21 (0x15) + * This bit field value is directly applied to FW + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: External + * + * + */ +#define CFG_HE_STA_OBSSPD CFG_INI_UINT( \ + "he_sta_obsspd", \ + 0, \ + 0xFFFFFFFF, \ + 0x15b8c2ae, \ + CFG_VALUE_OR_DEFAULT, \ + "He Mu Bfee Sts Gt80") + + #define CFG_HE_CAPS_ALL \ + CFG(CFG_HE_CONTROL) \ + CFG(CFG_HE_TWT_REQUESTOR) \ + CFG(CFG_HE_TWT_RESPONDER) \ + CFG(CFG_HE_FRAGMENTATION) \ + CFG(CFG_HE_MAX_FRAG_MSDU) \ + CFG(CFG_HE_MIN_FRAG_SIZE) \ + CFG(CFG_HE_TRIG_PAD) \ + CFG(CFG_HE_MTID_AGGR_RX) \ + CFG(CFG_HE_LINK_ADAPTATION) \ + CFG(CFG_HE_ALL_ACK) \ + CFG(CFG_HE_TRIGD_RSP_SCHEDULING) \ + CFG(CFG_HE_BUFFER_STATUS_RPT) \ + CFG(CFG_HE_BCAST_TWT) \ + CFG(CFG_HE_BA_32BIT) \ + CFG(CFG_HE_MU_CASCADING) \ + CFG(CFG_HE_MULTI_TID) \ + CFG(CFG_HE_OMI) \ + CFG(CFG_HE_OFDMA_RA) \ + CFG(CFG_HE_MAX_AMPDU_LEN) \ + CFG(CFG_HE_AMSDU_FRAG) \ + CFG(CFG_HE_FLEX_TWT_SCHED) \ + CFG(CFG_HE_RX_CTRL) \ + CFG(CFG_HE_BSRP_AMPDU_AGGR) \ + CFG(CFG_HE_QTP) \ + CFG(CFG_HE_A_BQR) \ + CFG(CFG_HE_SR_RESPONDER) \ + CFG(CFG_HE_NDP_FEEDBACK_SUPP) \ + CFG(CFG_HE_OPS_SUPP) \ + CFG(CFG_HE_AMSDU_IN_AMPDU) \ + CFG(CFG_HE_CHAN_WIDTH) \ + CFG(CFG_HE_MTID_AGGR_TX) \ + CFG(CFG_HE_SUB_CH_SEL_TX) \ + CFG(CFG_HE_UL_2X996_RU) \ + CFG(CFG_HE_OM_CTRL_UL_MU_DIS_RX) \ + CFG(CFG_HE_RX_PREAM_PUNC) \ + CFG(CFG_HE_CLASS_OF_DEVICE) \ + CFG(CFG_HE_LDPC) \ + CFG(CFG_HE_LTF_PPDU) \ + CFG(CFG_HE_MIDAMBLE_RX_MAX_NSTS) \ + CFG(CFG_HE_LTF_NDP) \ + CFG(CFG_HE_TX_STBC_LT80) \ + CFG(CFG_HE_RX_STBC_LT80) \ + CFG(CFG_HE_DOPPLER) \ + CFG(CFG_HE_UL_MUMIMO) \ + CFG(CFG_HE_DCM_TX) \ + CFG(CFG_HE_DCM_RX) \ + CFG(CFG_HE_MU_PPDU) \ + CFG(CFG_HE_SU_BEAMFORMER) \ + CFG(CFG_HE_SU_BEAMFORMEE) \ + CFG(CFG_HE_MU_BEAMFORMER) \ + CFG(CFG_HE_BFEE_STS_LT80) \ + CFG(CFG_HE_BFEE_STS_GT80) \ + CFG(CFG_HE_NUM_SOUND_LT80) \ + CFG(CFG_HE_NUM_SOUND_GT80) \ + CFG(CFG_HE_SU_FEED_TONE16) \ + CFG(CFG_HE_MU_FEED_TONE16) \ + CFG(CFG_HE_CODEBOOK_SU) \ + CFG(CFG_HE_CODEBOOK_MU) \ + CFG(CFG_HE_BFRM_FEED) \ + CFG(CFG_HE_ER_SU_PPDU) \ + CFG(CFG_HE_DL_PART_BW) \ + CFG(CFG_HE_PPET_PRESENT) \ + CFG(CFG_HE_SRP) \ + CFG(CFG_HE_POWER_BOOST) \ + CFG(CFG_HE_4x_LTF_GI) \ + CFG(CFG_HE_MAX_NC) \ + CFG(CFG_HE_RX_STBC_GT80) \ + CFG(CFG_HE_TX_STBC_GT80) \ + CFG(CFG_HE_ER_4x_LTF_GI) \ + CFG(CFG_HE_PPDU_20_IN_40MHZ_2G) \ + CFG(CFG_HE_PPDU_20_IN_160_80P80MHZ) \ + CFG(CFG_HE_PPDU_80_IN_160_80P80MHZ) \ + CFG(CFG_HE_ER_1X_HE_LTF_GI) \ + CFG(CFG_HE_MIDAMBLE_TXRX_1X_HE_LTF) \ + CFG(CFG_HE_DCM_MAX_BW) \ + CFG(CFG_HE_LONGER_16_SIGB_OFDM_SYM) \ + CFG(CFG_HE_NON_TRIG_CQI_FEEDBACK) \ + CFG(CFG_HE_TX_1024_QAM_LT_242_RU) \ + CFG(CFG_HE_RX_1024_QAM_LT_242_RU) \ + CFG(CFG_HE_RX_FULL_BW_MU_CMPR_SIGB) \ + CFG(CFG_HE_RX_FULL_BW_MU_NON_CMPR_SIGB) \ + CFG(CFG_HE_RX_MCS_MAP_LT_80) \ + CFG(CFG_HE_TX_MCS_MAP_LT_80) \ + CFG(CFG_HE_RX_MCS_MAP_160) \ + CFG(CFG_HE_TX_MCS_MAP_160) \ + CFG(CFG_HE_RX_MCS_MAP_80_80) \ + CFG(CFG_HE_TX_MCS_MAP_80_80) \ + CFG(CFG_HE_OPS_BASIC_MCS_NSS) \ + CFG(CFG_HE_DYNAMIC_FRAGMENTATION) \ + CFG(CFG_ENABLE_UL_MIMO) \ + CFG(CFG_ENABLE_UL_OFDMA) \ + CFG(CFG_HE_STA_OBSSPD) + +#endif /* __CFG_MLME_HE_CAPS_H */ + diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_ht_caps.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_ht_caps.h new file mode 100644 index 0000000000000000000000000000000000000000..71eb4111519709b303e3c3994a9fbc751c14acf2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_ht_caps.h @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_HT_CAPS_H +#define __CFG_MLME_HT_CAPS_H + +/* + * + * gTxLdpcEnable - Config Param to enable Tx LDPC capability + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * This ini is used to enable/disable Tx LDPC capability + * 0 - disable + * 1 - HT LDPC enable + * 2 - VHT LDPC enable + * 3 - HT & VHT LDPC enable + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Concurrency/Standalone + * + * Usage: Internal/External + * + * + */ +#define CFG_TX_LDPC_ENABLE CFG_INI_UINT( \ + "gTxLdpcEnable", \ + 0, \ + 3, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx LDPC capability") + +/* + * + * gEnableRXLDPC - Config Param to enable Rx LDPC capability + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable Rx LDPC capability + * 0 - disable Rx LDPC + * 1 - enable Rx LDPC + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Concurrency/Standalone + * + * Usage: Internal/External + * + * + */ +#define CFG_RX_LDPC_ENABLE CFG_INI_BOOL( \ + "gEnableRXLDPC", \ + 0, \ + "Rx LDPC capability") + +/* + * + * gEnableTXSTBC - Enables/disables Tx STBC capability in STA mode + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set default Tx STBC capability + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_TX_STBC_ENABLE CFG_INI_BOOL( \ + "gEnableTXSTBC", \ + 0, \ + "Tx STBC capability") + +/* + * + * gEnableRXSTBC - Enables/disables Rx STBC capability in STA mode + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set default Rx STBC capability + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_RX_STBC_ENABLE CFG_INI_BOOL( \ + "gEnableRXSTBC", \ + 1, \ + "Rx STBC capability") + +/* + * + * gShortGI20Mhz - Short Guard Interval for HT20 + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set default short interval for HT20 + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_SHORT_GI_20MHZ CFG_INI_BOOL( \ + "gShortGI20Mhz", \ + 1, \ + "Short Guard Interval for HT20") + +/* + * + * gShortGI40Mhz - It will check gShortGI20Mhz and + * gShortGI40Mhz from session entry + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set default gShortGI40Mhz + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_SHORT_GI_40MHZ CFG_INI_BOOL( \ + "gShortGI40Mhz", \ + 1, \ + "Short Guard Interval for HT40") + +#define CFG_HT_CAP_INFO CFG_UINT( \ + "ht_cap_info", \ + 0, \ + 65535, \ + 364, \ + CFG_VALUE_OR_DEFAULT, \ + "HT cap info") + +/* + * + * gShortPreamble - Set Short Preamble + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set default short Preamble + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_SHORT_PREAMBLE CFG_INI_BOOL( \ + "gShortPreamble", \ + 1, \ + "Short Preamble") + +#define CFG_HT_AMPDU_PARAMS CFG_UINT( \ + "ht_ampdu_params", \ + 0, \ + 255, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HT AMPDU Params") + +#define CFG_EXT_HT_CAP_INFO CFG_UINT( \ + "ext_ht_cap_info", \ + 0, \ + 65535, \ + 1024, \ + CFG_VALUE_OR_DEFAULT, \ + "HT Ext Cap Info") + +#define CFG_HT_INFO_FIELD_1 CFG_UINT( \ + "ht_info_field_1", \ + 0, \ + 255, \ + 15, \ + CFG_VALUE_OR_DEFAULT, \ + "HT Info Field 1") + +#define CFG_HT_INFO_FIELD_2 CFG_UINT( \ + "ht_info_field_2", \ + 0, \ + 65535, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HT Info Field 2") + +#define CFG_HT_INFO_FIELD_3 CFG_UINT( \ + "ht_info_field_3", \ + 0, \ + 65535, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "HT Info Field 3") + +/* + * + * gEnableHtSMPS - Enable the SM Power Save + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable SM Power Save + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_HT_SMPS CFG_INI_BOOL( \ + "gEnableHtSMPS", \ + 0, \ + "Enable HT SM PowerSave") + +/* + * + * gHtSMPS - SMPS Mode + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * This ini is used to set default SM Power Save Antenna mode + * 0 - Static + * 1 - Dynamic + * 2 - Reserved/Invalid + * 3 - Disabled + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_HT_SMPS_MODE CFG_INI_UINT( \ + "gHtSMPS", \ + 0, \ + 3, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "HT SM Power Save Config") + +/* + * + * gMaxAmsduNum - Max number of MSDU's in aggregate + * @Min: 0 + * @Max: 15 + * @Default: 0 + * + * gMaxAmsduNum is the number of MSDU's transmitted in the aggregated + * frame. Setting it to a value larger than 1 enables transmit aggregation. + * Set the value to 0 to enable FW automode selection where it decides + * the maximum number of MSDUs in AMSDU based on connection mode. + * + * It is a PHY parameter that applies to all vdev's in firmware. + * + * Supported Feature: 11n aggregation + * + * Usage: Internal + * + * + */ +#define CFG_MAX_AMSDU_NUM CFG_INI_UINT( \ + "gMaxAmsduNum", \ + 0, \ + 15, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Max AMSDU Number") + +/* + * + * gMaxRxAmpduFactor - Provide the maximum ampdu factor. + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * This ini is used to set default maxampdu factor + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_MAX_RX_AMPDU_FACTOR CFG_INI_UINT( \ + "gMaxRxAmpduFactor", \ + 0, \ + 3, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Max Rx AMPDU Factor") + +/* + * + * ght_mpdu_density - Configuration option for HT MPDU density + * @Min: 0 + * @Max: 7 + * @Default: 7 + * + * This ini is used to set default MPDU Density + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * As per (Table 8-125 802.11-2012) + * 0 for no restriction + * 1 for 1/4 micro sec + * 2 for 1/2 micro sec + * 3 for 1 micro sec + * 4 for 2 micro sec + * 5 for 4 micro sec + * 6 for 8 micro sec + * 7 for 16 micro sec + * + * + */ +#define CFG_MPDU_DENSITY CFG_INI_UINT( \ + "ght_mpdu_density", \ + 0, \ + 7, \ + 7, \ + CFG_VALUE_OR_DEFAULT, \ + "MPDU Density") + +/* + * + * gShortSlotTimeEnabled - It will set slot timing slot. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set default timing slot. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_SHORT_SLOT_TIME_ENABLED CFG_INI_BOOL( \ + "gShortSlotTimeEnabled", \ + 1, \ + "Short Slot Time Enabled") + +#define CFG_HT_CAPS_ALL \ + CFG(CFG_HT_CAP_INFO) \ + CFG(CFG_TX_LDPC_ENABLE) \ + CFG(CFG_RX_LDPC_ENABLE) \ + CFG(CFG_TX_STBC_ENABLE) \ + CFG(CFG_RX_STBC_ENABLE) \ + CFG(CFG_SHORT_GI_20MHZ) \ + CFG(CFG_SHORT_GI_40MHZ) \ + CFG(CFG_SHORT_PREAMBLE) \ + CFG(CFG_HT_AMPDU_PARAMS) \ + CFG(CFG_EXT_HT_CAP_INFO) \ + CFG(CFG_HT_INFO_FIELD_1) \ + CFG(CFG_HT_INFO_FIELD_2) \ + CFG(CFG_HT_INFO_FIELD_3) \ + CFG(CFG_ENABLE_HT_SMPS) \ + CFG(CFG_HT_SMPS_MODE) \ + CFG(CFG_MAX_AMSDU_NUM) \ + CFG(CFG_MAX_RX_AMPDU_FACTOR) \ + CFG(CFG_MPDU_DENSITY) \ + CFG(CFG_SHORT_SLOT_TIME_ENABLED) + +#endif /* __CFG_MLME_HT_CAPS_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_ibss.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_ibss.h new file mode 100644 index 0000000000000000000000000000000000000000..dd15791db384d72c936cbf2562fdfa5d1b8b0224 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_ibss.h @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_IBSS_H +#define __CFG_MLME_IBSS_H + +/* + * + * g_IBSS_AUTO_BSSID - Control IBSS Auto BSSID setup + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Control IBSS Auto BSSID enable / disable + * Usage: External + * + * + */ +#define CFG_IBSS_AUTO_BSSID CFG_BOOL( \ + "gIbssAutoBssid", \ + 1, \ + "Enable Auto BSSID for IBSS") + +/* + * + * gAdHocChannel5G - Default 5Ghz IBSS channel if channel freq is not + * provided by supplicant. + * @Min: 5180 + * @Max: 5825 + * @Default: 5220 + * + * This ini is used to set default 5Ghz IBSS channel frequency + * if channel is not provided by supplicant and band is 5Ghz + * + * Related: None + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_ADHOC_CHANNEL_5GHZ CFG_INI_UINT( \ + "ad_hoc_ch_freq_5g", \ + 5180, \ + 5825, \ + 5220, \ + CFG_VALUE_OR_DEFAULT, \ + "Default 5Ghz IBSS ch freq if not provided by supplicant") + +/* + * + * gAdHocChannel24G - Default 2.4Ghz IBSS channel if channel freq is not + * provided by supplicant. + * @Min: 2412 + * @Max: 2484 + * @Default: 2437 + * + * This ini is used to set default 2.4Ghz IBSS channel frequency + * if channel is not provided by supplicant and band is 2.4Ghz + * + * Related: None + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_ADHOC_CHANNEL_24GHZ CFG_INI_UINT( \ + "ad_hoc_ch_freq_2g", \ + 2412, \ + 2484, \ + 2437, \ + CFG_VALUE_OR_DEFAULT, \ + "Default 2.4Ghz IBSS ch freq if not provided by supplicant") + +/* + * + * gCoalesingInIBSS - If IBSS coalesing is enabled. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set IBSS coalesing + * + * Related: None + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_COALESING CFG_INI_BOOL( \ + "gCoalesingInIBSS", \ + 0, \ + "IBSS coalesing control param") + +/* + * + * gIbssATIMWinSize - Set IBSS ATIM window size + * @Min: 0 + * @Max: 50 + * @Default: 0 + * + * This ini is used to set IBSS ATIM window size + * + * Related: None + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_ATIM_WIN_SIZE CFG_INI_UINT( \ + "gIbssATIMWinSize", \ + 0, \ + 50, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Set IBSS ATIM window size") + +/* + * + * gIbssIsPowerSaveAllowed - Indicates if IBSS Power Save is + * supported or not + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to Indicates if IBSS Power Save is + * supported or not. When not allowed,IBSS station has + * to stay awake all the time and should never set PM=1 + * in its transmitted frames. + * + * Related: valid only when gIbssATIMWinSize is non-zero + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_IS_POWER_SAVE_ALLOWED CFG_INI_BOOL( \ + "gIbssIsPowerSaveAllowed", \ + 1, \ + "IBSS Power Save control") + +/* + * + * gIbssIsPowerCollapseAllowed - Indicates if IBSS Power Collapse + * is allowed + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to indicates if IBSS Power Collapse + * is allowed + * + * Related: None + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_IS_POWER_COLLAPSE_ALLOWED CFG_INI_BOOL( \ + "gIbssIsPowerCollapseAllowed", \ + 1, \ + "Indicates if IBSS Power Collapse is allowed") + +/* + * + * gIbssAwakeOnTxRx - Indicates whether IBSS station + * can exit power save mode and enter power active + * state whenever there is a TX/RX activity. + * + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to ndicates whether IBSS station + * can exit power save mode and enter power active + * state whenever there is a TX/RX activity. + * + * Related: None + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_AWAKE_ON_TX_RX CFG_INI_BOOL( \ + "gIbssAwakeOnTxRx", \ + 0, \ + "IBSS sta power save mode on TX/RX activity") + +/* + * + * gIbssInactivityTime - Indicates the data + * inactivity time in number of beacon intervals + * after which IBSS station re-inters power save + * + * @Min: 1 + * @Max: 10 + * @Default: 1 + * + * In IBSS mode if Awake on TX/RX activity is enabled + * Ibss Inactivity parameter indicates the data + * inactivity time in number of beacon intervals + * after which IBSS station re-inters power save + * by sending Null frame with PM=1 + * + * Related: Aplicable if gIbssAwakeOnTxRx is enabled + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_INACTIVITY_TIME CFG_INI_UINT( \ + "gIbssInactivityTime", \ + 1, \ + 10, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "No of Beacons intervals of data inactivity for power save") + +/* + * + * gIbssTxSpEndInactivityTime - Indicates the time after + * which TX Service Period is terminated by + * sending a Qos Null frame with EOSP. + * + * @Min: 0 + * @Max: 100 + * @Default: 0 + * + * In IBSS mode Tx Service Period Inactivity + * time in msecs indicates the time after + * which TX Service Period is terminated by + * sending a Qos Null frame with EOSP. + * If value is 0, TX SP is terminated with the + * last buffered packet itself instead of waiting + * for the inactivity. + * + * Related: None + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_TXSP_END_INACTIVITY CFG_INI_UINT( \ + "gIbssTxSpEndInactivityTime", \ + 0, \ + 100, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "TX service period inactivity timeout") + +/* + * + * gIbssPsWarmupTime - PS-supporting device + * does not enter protocol sleep state during first + * gIbssPsWarmupTime seconds. + * + * @Min: 0 + * @Max: 65535 + * @Default: 0 + * + * When IBSS network is initialized, PS-supporting device + * does not enter protocol sleep state during first + * gIbssPsWarmupTime seconds. + * + * Related: valid if gIbssIsPowerSaveAllowed is set + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_PS_WARMUP_TIME CFG_INI_UINT( \ + "gIbssPsWarmupTime", \ + 0, \ + 65535, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "IBSS Power save skip time") + +/* + * + * gIbssPs1RxChainInAtim - IBSS Power Save Enable/Disable 1 RX + * chain usage during the ATIM window + * + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * IBSS Power Save Enable/Disable 1 RX + * chain usage during the ATIM window + * + * Related: Depend on gIbssIsPowerSaveAllowed + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define CFG_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW CFG_INI_BOOL( \ + "gIbssPs1RxChainInAtim", \ + 0, \ + "Control IBSS Power save in 1RX chain during ATIM") + +/* + * + * gIbssBssid - Default IBSS BSSID if BSSID is not provided by supplicant + * @Min: "000000000000" + * @Max: "ffffffffffff" + * @Default: "000AF5040506" + * + * This ini is used to set Default IBSS BSSID if BSSID + * is not provided by supplicant and Coalesing is disabled + * + * Related: Only applicable if gCoalesingInIBSS is 0 + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define IBSS_BSSID_DEFAULT { .bytes = { 0x00, 0x0a, 0xf5, 0x04, 0x05, 0x06 } } +#define CFG_IBSS_BSSID CFG_INI_MAC("gIbssBssid", \ + IBSS_BSSID_DEFAULT, \ + "IBSS BSSID if not provided by supplicant") + +#define CFG_IBSS_ALL \ + CFG(CFG_IBSS_ADHOC_CHANNEL_5GHZ) \ + CFG(CFG_IBSS_ADHOC_CHANNEL_24GHZ) \ + CFG(CFG_IBSS_ATIM_WIN_SIZE) \ + CFG(CFG_IBSS_AUTO_BSSID) \ + CFG(CFG_IBSS_AWAKE_ON_TX_RX) \ + CFG(CFG_IBSS_BSSID) \ + CFG(CFG_IBSS_COALESING) \ + CFG(CFG_IBSS_INACTIVITY_TIME) \ + CFG(CFG_IBSS_IS_POWER_COLLAPSE_ALLOWED) \ + CFG(CFG_IBSS_IS_POWER_SAVE_ALLOWED) \ + CFG(CFG_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW) \ + CFG(CFG_IBSS_PS_WARMUP_TIME) \ + CFG(CFG_IBSS_TXSP_END_INACTIVITY) +#endif diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_lfr.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_lfr.h new file mode 100644 index 0000000000000000000000000000000000000000..123aaa80ada4945dcb8d378956b0febb8e38ba89 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_lfr.h @@ -0,0 +1,2939 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains configuration definitions for MLME LFR. + */ + +#ifndef CFG_MLME_LFR_H__ +#define CFG_MLME_LFR_H__ + +/* + * + * mawc_roam_enabled - Enable/Disable MAWC during roaming + * @Min: 0 - Disabled + * @Max: 1 - Enabled + * @Default: 0 + * + * This ini is used to control MAWC during roaming. + * + * Related: MAWCEnabled. + * + * Supported Feature: MAWC Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_MAWC_ROAM_ENABLED CFG_INI_BOOL( \ + "mawc_roam_enabled", \ + 0, \ + "Enable/Disable MAWC during roaming") + +/* + * + * mawc_roam_traffic_threshold - Configure traffic threshold + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 300 + * + * This ini is used to configure the data traffic load in kbps to + * register CMC. + * + * Related: mawc_roam_enabled. + * + * Supported Feature: MAWC Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_MAWC_ROAM_TRAFFIC_THRESHOLD CFG_INI_UINT( \ + "mawc_roam_traffic_threshold", \ + 0, \ + 0xFFFFFFFF, \ + 300, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure traffic threshold") + +/* + * + * mawc_roam_ap_rssi_threshold - Best AP RSSI threshold + * @Min: -120 + * @Max: 0 + * @Default: -66 + * + * This ini is used to specify the RSSI threshold to scan for the AP. + * + * Related: mawc_roam_enabled. + * + * Supported Feature: MAWC Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_MAWC_ROAM_AP_RSSI_THRESHOLD CFG_INI_INT( \ + "mawc_roam_ap_rssi_threshold", \ + -120, \ + 0, \ + -66, \ + CFG_VALUE_OR_DEFAULT, \ + "Best AP RSSI threshold") + +/* + * + * mawc_roam_rssi_high_adjust - Adjust MAWC roam high RSSI + * @Min: 3 + * @Max: 5 + * @Default: 5 + * + * This ini is used for high RSSI threshold adjustment in stationary state + * to suppress the scan. + * + * Related: mawc_roam_enabled. + * + * Supported Feature: MAWC Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_MAWC_ROAM_RSSI_HIGH_ADJUST CFG_INI_UINT( \ + "mawc_roam_rssi_high_adjust", \ + 3, \ + 5, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Adjust MAWC roam high RSSI") + +/* + * + * mawc_roam_rssi_low_adjust - Adjust MAWC roam low RSSI + * @Min: 3 + * @Max: 5 + * @Default: 5 + * + * This ini is used for low RSSI threshold adjustment in stationary state + * to suppress the scan. + * + * Related: mawc_roam_enabled. + * + * Supported Feature: MAWC Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_MAWC_ROAM_RSSI_LOW_ADJUST CFG_INI_UINT( \ + "mawc_roam_rssi_low_adjust", \ + 3, \ + 5, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Adjust MAWC roam low RSSI") + +/* + * + * rssi_abs_thresh - The min RSSI of the candidate AP to consider roam + * @Min: -96 + * @Max: 0 + * @Default: 0 + * + * The RSSI value of the candidate AP should be higher than rssi_abs_thresh + * to roam to the AP. 0 means no absolute minimum RSSI is required. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_RSSI_ABS_THRESHOLD CFG_INI_INT( \ + "rssi_abs_thresh", \ + -96, \ + 0, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "The min RSSI of the candidate AP to consider roam") + +/* + * + * lookup_threshold_5g_offset - Lookup threshold offset for 5G band + * @Min: -120 + * @Max: 120 + * @Default: 0 + * + * This ini is used to set the 5G band lookup threshold for roaming. + * It depends on another INI which is gNeighborLookupThreshold. + * gNeighborLookupThreshold is a legacy INI item which will be used to + * set the RSSI lookup threshold for both 2G and 5G bands. If the + * user wants to setup a different threshold for a 5G band, then user + * can use this offset value which will be summed up to the value of + * gNeighborLookupThreshold and used for 5G + * e.g: gNeighborLookupThreshold = -76dBm + * lookup_threshold_5g_offset = 6dBm + * Then the 5G band will be configured to -76+6 = -70dBm + * A default value of Zero to lookup_threshold_5g_offset will keep the + * thresholds same for both 2G and 5G bands + * + * Related: gNeighborLookupThreshold + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_5G_RSSI_THRESHOLD_OFFSET CFG_INI_INT( \ + "lookup_threshold_5g_offset", \ + -120, \ + 120, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Lookup threshold offset for 5G band") + +/* + * + * gEnableFastRoamInConcurrency - Enable LFR roaming on STA during concurrency + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This INI is used to enable Legacy fast roaming(LFR) on STA link during + * concurrent sessions. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ENABLE_FAST_ROAM_IN_CONCURRENCY CFG_INI_BOOL( \ + "gEnableFastRoamInConcurrency", \ + 1, \ + "Enable LFR roaming on STA during concurrency") + +/* + * + * gEnableEarlyStopScan - Set early stop scan + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set early stop scan. Early stop + * scan is a feature for roaming to stop the scans at + * an early stage as soon as we find a better AP to roam. + * This would make the roaming happen quickly. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_EARLY_STOP_SCAN_ENABLE CFG_INI_BOOL( \ + "gEnableEarlyStopScan", \ + 0, \ + "Set early stop scan") + +/* + * + * gEarlyStopScanMinThreshold - Set early stop scan min + * threshold + * @Min: -80 + * @Max: -70 + * @Default: -73 + * + * This ini is used to set the early stop scan minimum + * threshold. Early stop scan minimum threshold is the + * minimum threshold to be considered for stopping the + * scan. The algorithm starts with a scan on the greedy + * channel list with the maximum threshold and steps down + * the threshold by 20% for each further channel. It can + * step down on each channel but cannot go lower than the + * minimum threshold. + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_EARLY_STOP_SCAN_MIN_THRESHOLD CFG_INI_INT( \ + "gEarlyStopScanMinThreshold", \ + -80, \ + -70, \ + -73, \ + CFG_VALUE_OR_DEFAULT, \ + "Set early stop scan min") + +/* + * + * gEarlyStopScanMaxThreshold - Set early stop scan max + * threshold + * @Min: -60 + * @Max: -40 + * @Default: -43 + * + * This ini is used to set the the early stop scan maximum + * threshold at which the candidate AP should be to be + * qualified as a potential roam candidate and good enough + * to stop the roaming scan. + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_EARLY_STOP_SCAN_MAX_THRESHOLD CFG_INI_INT( \ + "gEarlyStopScanMaxThreshold", \ + -60, \ + -40, \ + -43, \ + CFG_VALUE_OR_DEFAULT, \ + "Set early stop scan max") + +/* + * + * gFirstScanBucketThreshold - Set first scan bucket + * threshold + * @Min: -50 + * @Max: -30 + * @Default: -30 + * + * This ini will configure the first scan bucket + * threshold to the mentioned value and all the AP's which + * have RSSI under this threshold will fall under this + * bucket. This configuration item used to tweak and + * test the input for internal algorithm. + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: Internal + * + * + */ +#define CFG_LFR_FIRST_SCAN_BUCKET_THRESHOLD CFG_INI_INT( \ + "gFirstScanBucketThreshold", \ + -50, \ + -30, \ + -30, \ + CFG_VALUE_OR_DEFAULT, \ + "Set first scan bucket") + +/* + * + * gtraffic_threshold - Dense traffic threshold + * @Min: 0 + * @Max: 0xffffffff + * @Default: 400 + * + * Dense traffic threshold + * traffic threshold required for dense roam scan + * Measured in kbps + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_DENSE_TRAFFIC_THRESHOLD CFG_INI_UINT( \ + "gtraffic_threshold", \ + 0, \ + 0xffffffff, \ + 400, \ + CFG_VALUE_OR_DEFAULT, \ + "Dense traffic threshold") + +/* + * + * groam_dense_rssi_thresh_offset - Sets dense roam RSSI threshold diff + * @Min: 0 + * @Max: 20 + * @Default: 10 + * + * This INI is used to set offset value from normal RSSI threshold to dense + * RSSI threshold FW will optimize roaming based on new RSSI threshold once + * it detects dense environment. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_DENSE_RSSI_THRE_OFFSET CFG_INI_UINT( \ + "groam_dense_rssi_thresh_offset", \ + 0, \ + 20, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Dense traffic threshold") + +/* + * + * groam_dense_min_aps - Sets minimum number of AP for dense roam + * @Min: 1 + * @Max: 5 + * @Default: 3 + * + * Minimum number of APs required for dense roam. FW will consider + * environment as dense once it detects #APs operating is more than + * groam_dense_min_aps. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_DENSE_MIN_APS CFG_INI_UINT( \ + "groam_dense_min_aps", \ + 1, \ + 5, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Sets minimum number of AP for dense roam") + +/* + * + * roam_bg_scan_bad_rssi_thresh - RSSI threshold for background roam + * @Min: -96 + * @Max: 0 + * @Default: -76 + * + * If the DUT is connected to an AP with weak signal, then the bad RSSI + * threshold will be used as an opportunity to use the scan results + * from other scan clients and try to roam if there is a better AP + * available in the environment. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_BG_SCAN_BAD_RSSI_THRESHOLD CFG_INI_INT( \ + "roam_bg_scan_bad_rssi_thresh", \ + -96, \ + 0, \ + -76, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI threshold for background roam") + +/* + * + * roam_bg_scan_client_bitmap - Bitmap used to identify the scan clients + * @Min: 0 + * @Max: 0x7FF + * @Default: 0x424 + * + * This bitmap is used to define the client scans that need to be used + * by the roaming module to perform a background roaming. + * Currently supported bit positions are as follows: + * Bit 0 is reserved in the firmware. + * WMI_SCAN_CLIENT_NLO - 1 + * WMI_SCAN_CLIENT_EXTSCAN - 2 + * WMI_SCAN_CLIENT_ROAM - 3 + * WMI_SCAN_CLIENT_P2P - 4 + * WMI_SCAN_CLIENT_LPI - 5 + * WMI_SCAN_CLIENT_NAN - 6 + * WMI_SCAN_CLIENT_ANQP - 7 + * WMI_SCAN_CLIENT_OBSS - 8 + * WMI_SCAN_CLIENT_PLM - 9 + * WMI_SCAN_CLIENT_HOST - 10 + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_BG_SCAN_CLIENT_BITMAP CFG_INI_UINT( \ + "roam_bg_scan_client_bitmap", \ + 0, \ + 0x7FF, \ + 0x424, \ + CFG_VALUE_OR_DEFAULT, \ + "Bitmap used to identify the scan clients") + +/* + * + * roam_bad_rssi_thresh_offset_2g - RSSI threshold offset for 2G to 5G roam + * @Min: 0 + * @Max: 86 + * @Default: 40 + * + * If the DUT is connected to an AP with weak signal in 2G band, then the + * bad RSSI offset for 2g would be used as offset from the bad RSSI + * threshold configured and then use the resulting rssi for an opportunity + * to use the scan results from other scan clients and try to roam to + * 5G Band ONLY if there is a better AP available in the environment. + * + * For example if the roam_bg_scan_bad_rssi_thresh is -76 and + * roam_bad_rssi_thresh_offset_2g is 40 then the difference of -36 would be + * used as a trigger to roam to a 5G AP if DUT initially connected to a 2G AP + * + * Related: roam_bg_scan_bad_rssi_thresh + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_BG_SCAN_BAD_RSSI_OFFSET_2G CFG_INI_UINT( \ + "roam_bad_rssi_thresh_offset_2g", \ + 0, \ + 86, \ + 40, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI threshold offset for 2G to 5G roam") + +/* + * + * roam_data_rssi_threshold_triggers - triggers of data rssi threshold for roam + * @Min: 0 + * @Max: 0xffff + * @Default: 0x3 + * + * If the DUT is connected to an AP with weak signal, during latest + * rx_data_inactivity_time, if there is no activity or avg of data_rssi is + * better than roam_data_rssi_threshold(-70dbM), then suppress roaming + * triggered by roam_data_rssi_threshold_triggers: low RSSI or bg scan. + * Triggers bitmap definition: + * ROAM_DATA_RSSI_FLAG_LOW_RSSI 1<<0 + * ROAM_DATA_RSSI_FLAG_BACKGROUND 1<<1 + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_ROAM_DATA_RSSI_THRESHOLD_TRIGGERS CFG_INI_UINT( \ + "roam_data_rssi_threshold_triggers", \ + 0, \ + 0xffff, \ + 0x3, \ + CFG_VALUE_OR_DEFAULT, \ + "Triggers of DATA RSSI threshold for roam") + +/* + * + * roam_data_rssi_threshold - Data RSSI threshold for background roam + * @Min: -96 + * @Max: 0 + * @Default: -70 + * + * If the DUT is connected to an AP with weak signal, during latest + * rx_data_inactivity_time, if there is no activity or avg of data_rssi is + * better than roam_data_rssi_threshold(-70dbM), then suppress roaming + * triggered by roam_data_rssi_threshold_triggers: low RSSI or bg scan. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_ROAM_DATA_RSSI_THRESHOLD CFG_INI_INT( \ + "roam_data_rssi_threshold", \ + -96, \ + 0, \ + -70, \ + CFG_VALUE_OR_DEFAULT, \ + "DATA RSSI threshold for roam") + +/* + * + * rx_data_inactivity_time - Duration to check data rssi + * @Min: 0 + * @Max: 100000 ms + * @Default: 2000 + * + * If the DUT is connected to an AP with weak signal, during latest + * rx_data_inactivity_time, if there is no activity or avg of data_rssi is + * better than roam_data_rssi_threshold(-70dbM), then suppress roaming + * triggered by roam_data_rssi_threshold_triggers: low RSSI or bg scan. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_RX_DATA_INACTIVITY_TIME CFG_INI_UINT( \ + "rx_data_inactivity_time", \ + 0, \ + 100000, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "Rx inactivity time to check data rssi") + +/* + * + * roamscan_adaptive_dwell_mode - Sets dwell time adaptive mode + * @Min: 0 + * @Max: 4 + * @Default: 4 + * + * This parameter will set the algo used in dwell time optimization during + * roam scan. see enum scan_dwelltime_adaptive_mode. + * Acceptable values for this: + * 0: Default (Use firmware default mode) + * 1: Conservative optimization + * 2: Moderate optimization + * 3: Aggressive optimization + * 4: Static + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ADAPTIVE_ROAMSCAN_DWELL_MODE CFG_INI_UINT( \ + "roamscan_adaptive_dwell_mode", \ + 0, \ + 4, \ + 4, \ + CFG_VALUE_OR_DEFAULT, \ + "Sets dwell time adaptive mode") + +/* + * + * gper_roam_enabled - To enabled/disable PER based roaming in FW + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * This ini is used to enable/disable Packet error based roaming, enabling this + * will cause DUT to monitor Tx and Rx traffic and roam to a better candidate + * if current is not good enough. + * + * Values supported: + * 0: disabled + * 1: enabled for Rx traffic + * 2: enabled for Tx traffic + * 3: enabled for Tx and Rx traffic + * + * Related: gper_roam_high_rate_th, gper_roam_low_rate_th, + * gper_roam_th_percent, gper_roam_rest_time + * + * Supported Feature: LFR-3.0 + * + * Usage: Internal + * + * + */ +#define CFG_LFR_PER_ROAM_ENABLE CFG_INI_UINT( \ + "gper_roam_enabled", \ + 0, \ + 3, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "To enabled/disable PER based roaming in FW") + +/* + * + * gper_roam_high_rate_th - Rate at which PER based roam will stop + * @Min: 1 Mbps + * @Max: 0xffffffff + * @Default: 40 Mbps + * + * This ini is used to define the data rate in mbps*10 at which FW will stop + * monitoring the traffic for PER based roam. + * + * Related: gper_roam_enabled, gper_roam_low_rate_th, + * gper_roam_th_percent, gper_roam_rest_time + * + * Supported Feature: LFR-3.0 + * + * Usage: Internal + * + * + */ +#define CFG_LFR_PER_ROAM_CONFIG_HIGH_RATE_TH CFG_INI_UINT( \ + "gper_roam_high_rate_th", \ + 10, \ + 0xffffffff, \ + 400, \ + CFG_VALUE_OR_DEFAULT, \ + "Rate at which PER based roam will stop") + +/* + * + * gper_roam_low_rate_th - Rate at which FW starts considering traffic for PER + * based roam. + * + * @Min: 1 Mbps + * @Max: 0xffffffff + * @Default: 20 Mbps + * + * This ini is used to define the rate in mbps*10 at which FW starts considering + * traffic for PER based roam, if gper_roam_th_percent of data is below this + * rate, FW will issue a roam scan. + * + * Related: gper_roam_enabled, gper_roam_high_rate_th, + * gper_roam_th_percent, gper_roam_rest_time + * + * Supported Feature: LFR-3.0 + * + * Usage: Internal + * + * + */ +#define CFG_LFR_PER_ROAM_CONFIG_LOW_RATE_TH CFG_INI_UINT( \ + "gper_roam_low_rate_th", \ + 10, \ + 0xffffffff, \ + 200, \ + CFG_VALUE_OR_DEFAULT, \ + "Rate at which FW starts considering traffic for PER") + +/* + * + * gper_roam_th_percent - Percentage at which FW will issue a roam scan if + * traffic is below gper_roam_low_rate_th rate. + * + * @Min: 10% + * @Max: 100% + * @Default: 60% + * + * This ini is used to define the percentage at which FW will issue a roam scan + * if traffic is below gper_roam_low_rate_th rate. + * + * Related: gper_roam_enabled, gper_roam_high_rate_th, + * gper_roam_high_rate_th, gper_roam_rest_time + * + * Supported Feature: LFR-3.0 + * + * Usage: Internal + * + * + */ +#define CFG_LFR_PER_ROAM_CONFIG_RATE_TH_PERCENT CFG_INI_UINT( \ + "gper_roam_th_percent", \ + 10, \ + 100, \ + 60, \ + CFG_VALUE_OR_DEFAULT, \ + "Percentage at which FW will issue a roam scan") + +/* + * + * gper_roam_rest_time - Time for which FW will wait once it issues a + * roam scan. + * + * @Min: 10 seconds + * @Max: 3600 seconds + * @Default: 300 seconds + * + * This ini is used to define the time for which FW will wait once it issues a + * PER based roam scan. + * + * Related: gper_roam_enabled, gper_roam_high_rate_th, + * gper_roam_high_rate_th, gper_roam_th_percent + * + * Supported Feature: LFR-3.0 + * + * Usage: Internal + * + * + */ +#define CFG_LFR_PER_ROAM_REST_TIME CFG_INI_UINT( \ + "gper_roam_rest_time", \ + 10, \ + 3600, \ + 300, \ + CFG_VALUE_OR_DEFAULT, \ + "Time for which FW will wait once it issues a roam scan") + +/* + * + * gper_roam_mon_time - Minimum time required in seconds to + * be considered as valid scenario for PER based roam + * @Min: 5 + * @Max: 25 + * @Default: 25 + * + * This ini is used to define minimum time in seconds for which DUT has + * collected the PER stats before it can consider the stats hysteresis to be + * valid for PER based scan. + * DUT collects following information during this period: + * 1. % of packets below gper_roam_low_rate_th + * 2. # packets above gper_roam_high_rate_th + * if DUT gets (1) greater than gper_roam_th_percent and (2) is zero during + * this period, it triggers PER based roam scan. + * + * Related: gper_roam_enabled, gper_roam_high_rate_th, gper_roam_low_rate_th, + * gper_roam_th_percent, gper_roam_rest_time + * + * Supported Feature: LFR-3.0 + * + * Usage: Internal + * + * + */ +#define CFG_LFR_PER_ROAM_MONITOR_TIME CFG_INI_UINT( \ + "gper_roam_mon_time", \ + 5, \ + 25, \ + 25, \ + CFG_VALUE_OR_DEFAULT, \ + "Minimum time to be considered as valid scenario for PER based roam") + +/* + * + * gper_min_rssi_threshold_for_roam - Minimum roamable AP RSSI for + * candidate selection for PER based roam + * @Min: 0 + * @Max: 96 + * @Default: 83 + * + * Minimum roamable AP RSSI for candidate selection for PER based roam + * + * Related: gper_roam_enabled, gper_roam_high_rate_th, gper_roam_low_rate_th, + * gper_roam_th_percent, gper_roam_rest_time + * + * Supported Feature: LFR-3.0 + * + * Usage: Internal + * + * + */ +#define CFG_LFR_PER_ROAM_MIN_CANDIDATE_RSSI CFG_INI_UINT( \ + "gper_min_rssi_threshold_for_roam", \ + 10, \ + 96, \ + 83, \ + CFG_VALUE_OR_DEFAULT, \ + "Minimum roamable AP RSSI for candidate selection for PER based roam") + +/* + * + * groam_disallow_duration - disallow duration before roaming + * @Min: 0 + * @Max: 3600 + * @Default: 30 + * + * This ini is used to configure how long LCA[Last Connected AP] AP will + * be disallowed before it can be a roaming candidate again, in units of + * seconds. + * + * Related: LFR + * + * Usage: Internal + * + * + */ +#define CFG_LFR3_ROAM_DISALLOW_DURATION CFG_INI_UINT( \ + "groam_disallow_duration", \ + 0, \ + 3600, \ + 30, \ + CFG_VALUE_OR_DEFAULT, \ + "disallow duration before roaming") + +/* + * + * grssi_channel_penalization - RSSI penalization + * @Min: 0 + * @Max: 15 + * @Default: 5 + * + * This ini is used to configure RSSI that will be penalized if candidate(s) + * are found to be in the same channel as disallowed AP's, in units of db. + * + * Related: LFR + * + * Usage: Internal + * + * + */ +#define CFG_LFR3_ROAM_RSSI_CHANNEL_PENALIZATION CFG_INI_UINT( \ + "grssi_channel_penalization", \ + 0, \ + 15, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI penalization") + +/* + * + * groam_num_disallowed_aps - Max number of AP's to maintain in LCA list + * @Min: 0 + * @Max: 8 + * @Default: 3 + * + * This ini is used to set the maximum number of AP's to be maintained + * in LCA [Last Connected AP] list. + * + * Related: LFR + * + * Usage: Internal + * + * + */ +#define CFG_LFR3_ROAM_NUM_DISALLOWED_APS CFG_INI_UINT( \ + "groam_num_disallowed_aps", \ + 0, \ + 8, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Max number of AP's to maintain in LCA list") + +/* + * + * enable_5g_band_pref - Enable preference for 5G from INI. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * This ini is used to enable 5G preference parameters. + * + * Related: 5g_rssi_boost_threshold, 5g_rssi_boost_factor, 5g_max_rssi_boost + * 5g_rssi_penalize_threshold, 5g_rssi_penalize_factor, 5g_max_rssi_penalize + * + * Supported Feature: 5G band preference + * + * Usage: External + * + * + */ +#define CFG_LFR_ENABLE_5G_BAND_PREF CFG_INI_BOOL( \ + "enable_5g_band_pref", \ + 0, \ + "Enable preference for 5G from INI") + +/* + * + * 5g_rssi_boost_threshold - A_band_boost_threshold above which 5G is favored. + * @Min: -70 + * @Max: -55 + * @Default: -60 + * This ini is used to set threshold for 5GHz band preference. + * + * Related: 5g_rssi_boost_factor, 5g_max_rssi_boost + * 5g_rssi_penalize_threshold, 5g_rssi_penalize_factor, 5g_max_rssi_penalize + * + * Supported Feature: 5G band preference + * + * Usage: External + * + * + */ +#define CFG_LFR_5G_RSSI_BOOST_THRESHOLD CFG_INI_INT( \ + "5g_rssi_boost_threshold", \ + -70, \ + -55, \ + -60, \ + CFG_VALUE_OR_DEFAULT, \ + "A_band_boost_threshold above which 5 GHz is favored") + +/* + * + * 5g_rssi_boost_factor - Factor by which 5GHz RSSI is boosted. + * @Min: 0 + * @Max: 2 + * @Default: 1 + * This ini is used to set the 5Ghz boost factor. + * + * Related: 5g_rssi_boost_threshold, 5g_max_rssi_boost + * 5g_rssi_penalize_threshold, 5g_rssi_penalize_factor, 5g_max_rssi_penalize + * + * Supported Feature: 5G band preference + * + * Usage: External + * + * + */ +#define CFG_LFR_5G_RSSI_BOOST_FACTOR CFG_INI_UINT( \ + "5g_rssi_boost_factor", \ + 0, \ + 2, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Factor by which 5GHz RSSI is boosted") + +/* + * + * 5g_max_rssi_boost - Maximum boost that can be applied to 5GHz RSSI. + * @Min: 0 + * @Max: 20 + * @Default: 10 + * This ini is used to set maximum boost which can be given to a 5Ghz network. + * + * Related: 5g_rssi_boost_threshold, 5g_rssi_boost_factor + * 5g_rssi_penalize_threshold, 5g_rssi_penalize_factor, 5g_max_rssi_penalize + * + * Supported Feature: 5G band preference + * + * Usage: External + * + * + */ +#define CFG_LFR_5G_MAX_RSSI_BOOST CFG_INI_UINT( \ + "5g_max_rssi_boost", \ + 0, \ + 20, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Maximum boost that can be applied to 5GHz RSSI") + +/* + * + * 5g_rssi_penalize_threshold - A_band_penalize_threshold above which + * 5 GHz is not favored. + * @Min: -80 + * @Max: -65 + * @Default: -70 + * This ini is used to set threshold for 5GHz band preference. + * + * Related: 5g_rssi_penalize_factor, 5g_max_rssi_penalize + * 5g_rssi_boost_threshold, 5g_rssi_boost_factor, 5g_max_rssi_boost + * + * Supported Feature: 5G band preference + * + * Usage: External + * + * + */ +#define CFG_LFR_5G_RSSI_PENALIZE_THRESHOLD CFG_INI_INT( \ + "5g_rssi_penalize_threshold", \ + -80, \ + -65, \ + -70, \ + CFG_VALUE_OR_DEFAULT, \ + "A_band_penalize_threshold above which 5 GHz is not favored") + +/* + * + * 5g_rssi_penalize_factor - Factor by which 5GHz RSSI is penalizeed. + * @Min: 0 + * @Max: 2 + * @Default: 1 + * This ini is used to set the 5Ghz penalize factor. + * + * Related: 5g_rssi_penalize_threshold, 5g_max_rssi_penalize + * 5g_rssi_boost_threshold, 5g_rssi_boost_factor, 5g_max_rssi_boost + * + * Supported Feature: 5G band preference + * + * Usage: External + * + * + */ +#define CFG_LFR_5G_RSSI_PENALIZE_FACTOR CFG_INI_UINT( \ + "5g_rssi_penalize_factor", \ + 0, \ + 2, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Factor by which 5GHz RSSI is penalizeed") + +/* + * + * 5g_max_rssi_penalize - Maximum penalty that can be applied to 5GHz RSSI. + * @Min: 0 + * @Max: 20 + * @Default: 10 + * This ini is used to set maximum penalty which can be given to a 5Ghz network. + * + * Related: 5g_rssi_penalize_threshold, 5g_rssi_penalize_factor + * 5g_rssi_boost_threshold, 5g_rssi_boost_factor, 5g_max_rssi_boost + * + * Supported Feature: 5G band preference + * + * Usage: External + * + * + */ +#define CFG_LFR_5G_MAX_RSSI_PENALIZE CFG_INI_UINT( \ + "5g_max_rssi_penalize", \ + 0, \ + 20, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Maximum penalty that can be applied to 5GHz RSSI") + +/* + * + * max_num_pre_auth - Configure max number of pre-auth + * @Min: 0 + * @Max: 256 + * @Default: 64 + * + * This ini is used to configure the data max number of pre-auth + * + * Usage: Internal + * + * + */ +#define CFG_LFR_MAX_NUM_PRE_AUTH CFG_UINT( \ + "max_num_pre_auth", \ + 0, \ + 256, \ + 64, \ + CFG_VALUE_OR_DEFAULT, \ + "") + +/* + * + * roam_preauth_retry_count + * + * @Min: 1 + * @Max: 10 + * @Default: 5 + * + * The maximum number of software retries for preauth or + * reassoc made before picking up the next candidate for + * connection during roaming. + * + * Related: N/A + * + * Supported Features: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR3_ROAM_PREAUTH_RETRY_COUNT CFG_INI_INT( \ + "roam_preauth_retry_count", \ + 1, \ + 10, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "The maximum number of software retries for preauth") + +/* + * + * roam_preauth_no_ack_timeout + * + * @Min: 5 + * @Max: 50 + * @Default: 5 + * + * Time to wait (in ms) after sending an preauth or reassoc + * request which didn't have an ack, before considering + * it as a failure and making another software retry. + * + * Related: N/A + * + * Supported Features: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR3_ROAM_PREAUTH_NO_ACK_TIMEOUT CFG_INI_INT( \ + "roam_preauth_no_ack_timeout", \ + 5, \ + 50, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Time to wait after sending an preauth or reassoc") + +/* + * + * FastRoamEnabled - Enable fast roaming + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to inform FW to enable fast roaming + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_FEATURE_ENABLED CFG_INI_BOOL( \ + "FastRoamEnabled", \ + 0, \ + "Enable fast roaming") + +/* + * + * MAWCEnabled - Enable/Disable Motion Aided Wireless Connectivity Global + * @Min: 0 - Disabled + * @Max: 1 - Enabled + * @Default: 0 + * + * This ini is used to controls the MAWC feature globally. + * MAWC is Motion Aided Wireless Connectivity. + * + * Related: mawc_roam_enabled. + * + * Supported Feature: Roaming and PNO/NLO + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_MAWC_FEATURE_ENABLED CFG_INI_BOOL( \ + "MAWCEnabled", \ + 0, \ + "Enable MAWC") + +/* + * + * FastTransitionEnabled - Enable fast transition in case of 11r and ese. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to turn ON/OFF the whole neighbor roam, pre-auth, reassoc. + * With this turned OFF 11r will completely not work. For 11r this flag has to + * be ON. For ESE fastroam will not work. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_FAST_TRANSITION_ENABLED CFG_INI_BOOL( \ + "FastTransitionEnabled", \ + 1, \ + "Enable fast transition") + +/* + * + * RoamRssiDiff - Enable roam based on rssi + * @Min: 0 + * @Max: 100 + * @Default: 5 + * + * This INI is used to decide whether to Roam or not based on RSSI. AP1 is the + * currently associated AP and AP2 is chosen for roaming. The Roaming will + * happen only if AP2 has better Signal Quality and it has a RSSI better than + * AP2. RoamRssiDiff is the number of units (typically measured in dB) AP2 + * is better than AP1. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_RSSI_DIFF CFG_INI_UINT( \ + "RoamRssiDiff", \ + 0, \ + 100, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable roam based on rssi") + +/* + * + * gWESModeEnabled - Enable WES mode + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable Wireless Extended Security mode. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ENABLE_WES_MODE CFG_INI_BOOL( \ + "gWESModeEnabled", \ + 0, \ + "Enable WES mode") + +/* + * + * gRoamScanOffloadEnabled - Enable Roam Scan Offload + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This INI is used to enable Roam Scan Offload in firmware + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_SCAN_OFFLOAD_ENABLED CFG_INI_BOOL( \ + "gRoamScanOffloadEnabled", \ + 1, \ + "Enable Roam Scan Offload") + +/* + * + * gNeighborScanChannelList - Set channels to be scanned + * by firmware for LFR scan + * @Default: "" + * + * This ini is used to set the channels to be scanned + * by firmware for LFR scan. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ + +#define CFG_LFR_NEIGHBOR_SCAN_CHANNEL_LIST CFG_INI_STRING( \ + "gNeighborScanChanList", \ + 0, \ + CFG_VALID_CHANNEL_LIST_STRING_LEN, \ + "", \ + "Set channels to be scanned") + +/* + * + * gNeighborScanTimerPeriod - Set neighbor scan timer period + * @Min: 3 + * @Max: 300 + * @Default: 100 + * + * This ini is used to set the timer period in secs after + * which neighbor scan is trigerred. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD CFG_INI_UINT( \ + "gNeighborScanTimerPeriod", \ + 3, \ + 300, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "Neighbor scan timer period") + +/* + * + * gRoamRestTimeMin - Set min neighbor scan timer period + * @Min: 3 + * @Max: 300 + * @Default: 50 + * + * This is the min rest time after which firmware will check for traffic + * and if there no traffic it will move to a new channel to scan + * else it will stay on the home channel till gNeighborScanTimerPeriod time + * and then will move to a new channel to scan. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_NEIGHBOR_SCAN_MIN_TIMER_PERIOD CFG_INI_UINT( \ + "gRoamRestTimeMin", \ + 3, \ + 300, \ + 50, \ + CFG_VALUE_OR_DEFAULT, \ + "Min neighbor scan timer period") + +/* + * + * gNeighborLookupThreshold - Set neighbor lookup rssi threshold + * @Min: 10 + * @Max: 120 + * @Default: 78 + * + * This is used to control the RSSI threshold for neighbor lookup. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD CFG_INI_UINT( \ + "gNeighborLookupThreshold", \ + 10, \ + 120, \ + 78, \ + CFG_VALUE_OR_DEFAULT, \ + "Neighbor lookup rssi threshold") + +/* + * + * gOpportunisticThresholdDiff - Set oppurtunistic threshold diff + * @Min: 0 + * @Max: 127 + * @Default: 0 + * + * This ini is used to set opportunistic threshold diff. + * This parameter is the RSSI diff above neighbor lookup + * threshold, when opportunistic scan should be triggered. + * MAX value is chosen so that this type of scan can be + * always enabled by user. + * MIN value will cause opportunistic scan to be triggered + * in neighbor lookup RSSI range. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF CFG_INI_UINT( \ + "gOpportunisticThresholdDiff", \ + 0, \ + 127, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Set oppurtunistic threshold diff") + +/* + * + * gRoamRescanRssiDiff - Sets RSSI for Scan trigger in firmware + * @Min: 0 + * @Max: 100 + * @Default: 5 + * + * This INI is the drop in RSSI value that will trigger a precautionary + * scan by firmware. Max value is chosen in such a way that this type + * of scan can be disabled by user. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_RESCAN_RSSI_DIFF CFG_INI_UINT( \ + "gRoamRescanRssiDiff", \ + 0, \ + 100, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Sets RSSI for Scan trigger in firmware") + +/* + * + * gNeighborScanChannelMinTime - Set neighbor scan channel min time + * @Min: 10 + * @Max: 40 + * @Default: 20 + * + * This ini is used to set the minimum time in secs spent on each + * channel in LFR scan inside firmware. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME CFG_INI_UINT( \ + "gNeighborScanChannelMinTime", \ + 10, \ + 40, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "Neighbor scan channel min time") + +/* + * + * gNeighborScanChannelMaxTime - Set neighbor scan channel max time + * @Min: 3 + * @Max: 300 + * @Default: 40 + * + * This ini is used to set the maximum time in secs spent on each + * channel in LFR scan inside firmware. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME CFG_INI_UINT( \ + "gNeighborScanChannelMaxTime", \ + 3, \ + 300, \ + 40, \ + CFG_VALUE_OR_DEFAULT, \ + "Neighbor scan channel max time") + +/* + * + * gNeighborScanRefreshPeriod - Set neighbor scan refresh period + * @Min: 1000 + * @Max: 60000 + * @Default: 20000 + * + * This ini is used by firmware to set scan refresh period + * in msecs for lfr scan. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD CFG_INI_UINT( \ + "gNeighborScanRefreshPeriod", \ + 1000, \ + 60000, \ + 20000, \ + CFG_VALUE_OR_DEFAULT, \ + "Neighbor scan refresh period") + +/* + * + * gFullRoamScanPeriod - Set full roam scan refresh period + * @Min: 0 + * @Max: 600 + * @Default: 0 + * + * This ini is used by firmware to set full roam scan period in secs. + * Full roam scan period is the minimum idle period in seconds between two + * successive full channel roam scans. If this is configured as a non-zero, + * full roam scan will be triggered for every configured interval. + * If this configured as 0, full roam scan will not be triggered at all. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_FULL_ROAM_SCAN_REFRESH_PERIOD CFG_INI_UINT( \ + "gFullRoamScanPeriod", \ + 0, \ + 600, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Full roam scan refresh period") + +/* + * + * gEmptyScanRefreshPeriod - Set empty scan refresh period + * @Min: 0 + * @Max: 60000 + * @Default: 0 + * + * This ini is used by firmware to set scan period in msecs + * following empty scan results. + * + * Related: None + * + * Supported Feature: LFR Scan + * + * Usage: External + * + * + */ +#define CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD CFG_INI_UINT( \ + "gEmptyScanRefreshPeriod", \ + 0, \ + 60000, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Empty scan refresh period") + +/* + * + * gRoamBmissFirstBcnt - Beacon miss count to trigger 1st bmiss event + * @Min: 5 + * @Max: 100 + * @Default: 10 + * + * This ini used to control how many beacon miss will trigger first bmiss + * event. First bmiss event will result in roaming scan. + * + * Related: None + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_BMISS_FIRST_BCNT CFG_INI_UINT( \ + "gRoamBmissFirstBcnt", \ + 5, \ + 100, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "First beacon miss count") + +/* + * + * gRoamBmissFinalBcnt - Beacon miss count to trigger final bmiss event + * @Min: 5 + * @Max: 100 + * @Default: 20 + * + * This ini used to control how many beacon miss will trigger final bmiss + * event. Final bmiss event will make roaming take place or cause the + * indication of final bmiss event. + * + * Related: None + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_BMISS_FINAL_BCNT CFG_INI_UINT( \ + "gRoamBmissFinalBcnt", \ + 5, \ + 100, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "Final beacon miss count") + +/* + * + * gRoamBeaconRssiWeight - Set beacon miss weight + * @Min: 5 + * @Max: 16 + * @Default: 14 + * + * This ini controls how many beacons' RSSI values will be used to calculate + * the average value of RSSI. + * + * Related: None + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_BEACON_RSSI_WEIGHT CFG_INI_UINT( \ + "gRoamBeaconRssiWeight", \ + 0, \ + 16, \ + 14, \ + CFG_VALUE_OR_DEFAULT, \ + "Beacon miss weight") + +/* + * + * gAllowDFSChannelRoam - Allow dfs channel in roam + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to set default dfs channel + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_ROAMING_DFS_CHANNEL CFG_INI_UINT( \ + "gAllowDFSChannelRoam", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Allow dfs channel in roam") + +/* + * + * gRoamScanHiRssiMaxCount - Sets 5GHz maximum scan count + * @Min: 0 + * @Max: 10 + * @Default: 3 + * + * This INI is used to set maximum scan count in 5GHz + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_SCAN_HI_RSSI_MAXCOUNT CFG_INI_UINT( \ + "gRoamScanHiRssiMaxCount", \ + 0, \ + 10, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "5GHz maximum scan count") + +/* + * + * gRoamScanHiRssiDelta - Sets RSSI Delta for scan trigger + * @Min: 0 + * @Max: 16 + * @Default: 10 + * + * This INI is used to set change in RSSI at which scan is triggered + * in 5GHz. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_SCAN_HI_RSSI_DELTA CFG_INI_UINT( \ + "gRoamScanHiRssiDelta", \ + 0, \ + 16, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI Delta for scan trigger") + +/* + * + * gRoamScanHiRssiDelay - Sets minimum delay between 5GHz scans + * @Min: 5000 + * @Max: 0x7fffffff + * @Default: 15000 + * + * This INI is used to set the minimum delay between 5GHz scans. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_SCAN_HI_RSSI_DELAY CFG_INI_UINT( \ + "gRoamScanHiRssiDelay", \ + 5000, \ + 0x7fffffff, \ + 15000, \ + CFG_VALUE_OR_DEFAULT, \ + "Minimum delay between 5GHz scans") + +/* + * + * gRoamScanHiRssiUpperBound - Sets upper bound after which 5GHz scan + * @Min: -66 + * @Max: 0 + * @Default: -30 + * + * This INI is used to set the RSSI upper bound above which the 5GHz scan + * will not be performed. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_SCAN_HI_RSSI_UB CFG_INI_INT( \ + "gRoamScanHiRssiUpperBound", \ + -66, \ + 0, \ + -30, \ + CFG_VALUE_OR_DEFAULT, \ + "Upper bound after which 5GHz scan") + +/* + * + * gRoamPrefer5GHz - Prefer roaming to 5GHz Bss + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to inform FW to prefer roaming to 5GHz BSS + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_PREFER_5GHZ CFG_INI_BOOL( \ + "gRoamPrefer5GHz", \ + 1, \ + "Prefer roaming to 5GHz Bss") + +/* + * + * gRoamIntraBand - Prefer roaming within Band + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to inform FW to prefer roaming within band + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_INTRA_BAND CFG_INI_BOOL( \ + "gRoamIntraBand", \ + 0, \ + "Prefer roaming within Band") + +/* + * + * gRoamScanNProbes - Sets the number of probes to be sent for firmware roaming + * @Min: 1 + * @Max: 10 + * @Default: 2 + * + * This INI is used to set the maximum number of probes the firmware can send + * for firmware internal roaming cases. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_SCAN_N_PROBES CFG_INI_UINT( \ + "gRoamScanNProbes", \ + 1, \ + 10, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "The number of probes to be sent for firmware roaming") + +/* + * + * gRoamScanHomeAwayTime - Sets the Home Away Time to firmware + * @Min: 0 + * @Max: 300 + * @Default: 0 + * + * Home Away Time should be at least equal to (gNeighborScanChannelMaxTime + * + (2*RFS)), where RFS is the RF Switching time(3). It is twice RFS + * to consider the time to go off channel and return to the home channel. + * + * Related: gNeighborScanChannelMaxTime + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME CFG_INI_UINT( \ + "gRoamScanHomeAwayTime", \ + 0, \ + 300, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "the home away time to firmware") + +/* + * + * gDelayBeforeVdevStop - wait time for tx complete before vdev stop + * @Min: 2 + * @Max: 200 + * @Default: 20 + * + * This INI is used to set wait time for tx complete before vdev stop. + * + * Related: None + * + * Usage: External + * + * + */ +#define CFG_LFR_DELAY_BEFORE_VDEV_STOP CFG_INI_UINT( \ + "gDelayBeforeVdevStop", \ + 2, \ + 200, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "wait time for tx complete before vdev stop") +/* + * + * enable_bss_load_roam_trigger - enable/disable bss load based roam trigger + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini when enabled, allows the firmware to roam when bss load outpaces + * the configured bss load threshold. When this ini is disabled, firmware + * doesn't consider bss load values to trigger roam. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_ENABLE_BSS_LOAD_TRIGGERED_ROAM CFG_INI_BOOL( \ + "enable_bss_load_roam_trigger", \ + 0, \ + "enable bss load triggered roaming") + +/* + * + * bss_load_threshold - bss load above which the STA should trigger roaming + * @Min: 0 + * @Max: 100 + * @Default: 70 + * + * When the bss laod value that is sampled exceeds this threshold, firmware + * will trigger roaming if bss load trigger is enabled. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_BSS_LOAD_THRESHOLD CFG_INI_UINT( \ + "bss_load_threshold", \ + 0, \ + 100, \ + 70, \ + CFG_VALUE_OR_DEFAULT, \ + "bss load threshold") + +/* + * + * bss_load_sample_time - Time in milliseconds for which the bss load values + * obtained from the beacons is sampled. + * @Min: 0 + * @Max: 0xffffffff + * @Default: 10000 + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_BSS_LOAD_SAMPLE_TIME CFG_INI_UINT( \ + "bss_load_sample_time", \ + 0, \ + 0xffffffff, \ + 10000, \ + CFG_VALUE_OR_DEFAULT, \ + "bss load sampling time") + +/* + * + * bss_load_trigger_5g_rssi_threshold - Current AP minimum RSSI in dBm below + * which roaming can be triggered if BSS load exceeds bss_load_threshold. + * @Min: -120 + * @Max: 0 + * @Default: -70 + * + * If connected AP is in 5Ghz, then consider bss load roam triggered only if + * load % > bss_load_threshold && connected AP rssi is worse than + * bss_load_trigger_5g_rssi_threshold + * + * Related: "bss_load_threshold" + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_BSS_LOAD_TRIG_5G_RSSI_THRES CFG_INI_INT( \ + "bss_load_trigger_5g_rssi_threshold", \ + -120, \ + 0, \ + -70, \ + CFG_VALUE_OR_DEFAULT, \ + "Minimum RSSI of current AP in 5GHz band for BSS load roam trigger") + +/* + * + * bss_load_trigger_2g_rssi_threshold - Current AP minimum RSSI in dBm below + * which roaming can be triggered if BSS load exceeds bss_load_threshold. + * @Min: -120 + * @Max: 0 + * @Default: -60 + * + * If connected AP is in 2Ghz, then consider bss load roam triggered only if + * load % > bss_load_threshold && connected AP rssi is worse than + * bss_load_trigger_2g_rssi_threshold. + * + * Related: "bss_load_threshold" + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_BSS_LOAD_TRIG_2G_RSSI_THRES CFG_INI_INT( \ + "bss_load_trigger_2g_rssi_threshold", \ + -120, \ + 0, \ + -60, \ + CFG_VALUE_OR_DEFAULT, \ + "Minimum RSSI of current AP in 2.4GHz band for BSS load roam trigger") + +/* + * + * ho_delay_for_rx - Delay hand-off (in msec) by this duration to receive + * pending rx frames from current BSS + * @Min: 0 + * @Max: 200 + * @Default: 0 + * + * For LFR 3.0 roaming scenario, once roam candidate is found, firmware + * waits for minimum this much duration to receive pending rx frames from + * current BSS before switching to new channel for handoff to new AP. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR3_ROAM_HO_DELAY_FOR_RX CFG_INI_UINT( \ + "ho_delay_for_rx", \ + 0, \ + 200, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Delay Hand-off by this duration to receive") + +/* + * + * min_delay_btw_roam_scans - Min duration (in sec) allowed btw two + * consecutive roam scans + * @Min: 0 + * @Max: 60 + * @Default: 10 + * + * Roam scan is not allowed if duration between two consecutive + * roam scans is less than this time. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_MIN_DELAY_BTW_ROAM_SCAN CFG_INI_UINT( \ + "min_delay_btw_roam_scans", \ + 0, \ + 60, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Min duration") + +/* + * + * roam_trigger_reason_bitmask - Contains roam_trigger_reasons + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 0x10DA + * + * Bitmask containing roam_trigger_reasons for which + * min_delay_btw_roam_scans constraint should be applied. + * Currently supported bit positions are as follows: + * Bit 0 is reserved in the firmware. + * WMI_ROAM_TRIGGER_REASON_PER - 1 + * WMI_ROAM_TRIGGER_REASON_BMISS - 2 + * WMI_ROAM_TRIGGER_REASON_LOW_RSSI - 3 + * WMI_ROAM_TRIGGER_REASON_HIGH_RSSI - 4 + * WMI_ROAM_TRIGGER_REASON_PERIODIC - 5 + * WMI_ROAM_TRIGGER_REASON_MAWC - 6 + * WMI_ROAM_TRIGGER_REASON_DENSE - 7 + * WMI_ROAM_TRIGGER_REASON_BACKGROUND - 8 + * WMI_ROAM_TRIGGER_REASON_FORCED - 9 + * WMI_ROAM_TRIGGER_REASON_BTM - 10 + * WMI_ROAM_TRIGGER_REASON_UNIT_TEST - 11 + * WMI_ROAM_TRIGGER_REASON_BSS_LOAD - 12 + * WMI_ROAM_TRIGGER_REASON_DEAUTH - 13 + * WMI_ROAM_TRIGGER_REASON_IDLE - 14 + * WMI_ROAM_TRIGGER_REASON_MAX - 15 + * + * For Ex: 0xDA (PER, LOW_RSSI, HIGH_RSSI, MAWC, DENSE) + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_SCAN_TRIGGER_REASON_BITMASK CFG_INI_UINT( \ + "roam_trigger_reason_bitmask", \ + 0, \ + 0xFFFFFFFF, \ + 0x10DA, \ + CFG_VALUE_OR_DEFAULT, \ + "Contains roam_trigger_reasons") + +/* + * + * enable_ftopen - enable/disable FT open feature + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This INI is used to enable/disable FT open feature + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_FT_OPEN_ENABLE CFG_INI_BOOL( \ + "enable_ftopen", \ + 1, \ + "enable/disable FT open feature") + +/* + * + * roam_force_rssi_trigger - To force RSSI trigger + * irrespective of channel list type + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set roam scan mode + * WMI_ROAM_SCAN_MODE_RSSI_CHANGE, irrespective of whether + * channel list type is CHANNEL_LIST_STATIC or not + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_FORCE_RSSI_TRIGGER CFG_INI_BOOL( \ + "roam_force_rssi_trigger", \ + 1, \ + "To force RSSI trigger") + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/* + * + * gRoamOffloadEnabled - enable/disable roam offload feature + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This INI is used to enable/disable roam offload feature + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR3_ROAMING_OFFLOAD CFG_INI_BOOL( \ + "gRoamOffloadEnabled", \ + 1, \ + "enable roam offload") + +/* + * + * enable_self_bss_roam - enable/disable roaming to self bss + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This INI is used to enable/disable roaming to already connected BSSID + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: Internal + * + * + */ +#define CFG_LFR3_ENABLE_SELF_BSS_ROAM CFG_INI_BOOL( \ + "enable_self_bss_roam", \ + 0, \ + "enable self bss roam") + +/* + * + * enable_disconnect_roam_offload - Enable/Disable emergency roaming during + * deauth/disassoc + * @Min: 0 - Disabled + * @Max: 1 - Enabled + * @Default: 1 + * + * When this ini is enabled firmware will trigger roam scan and roam to a new ap + * if candidate is found and it will not send the deauth/disassoc frame to + * the host driver. + * If roaming fails after this deauth, then firmware will send + * WMI_ROAM_REASON_DEAUTH event to the host. If roaming is successful, driver + * follows the normal roam synch event path. + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_ENABLE_DISCONNECT_ROAM CFG_INI_BOOL( \ + "enable_disconnect_roam_offload", \ + true, \ + "Enable/Disable roaming on deauth/disassoc from AP") + +/* + * + * enable_idle_roam - Enable/Disable idle roaming + * @Min: 0 - Disabled + * @Max: 1 - Enabled + * @Default: 0 + * + * When this ini is enabled firmware will trigger roam scan and roam to a new + * ap if current connected AP rssi falls below the threshold. To consider the + * connection as idle, the following conditions should be met if this ini + * "enable_idle_roam" is enabled: + * 1. User space sends "SET SUSPENDMODE" command with value 0. + * 2. No TX/RX data for idle time configured via ini "idle_roam_inactive_time". + * 3. Connected AP rssi change doesn't exceed a specific delta value. + * (configured via ini idle_roam_rssi_delta) + * 4. Connected AP rssi falls below minimum rssi (configured via ini + * "idle_roam_min_rssi"). + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_ENABLE_IDLE_ROAM CFG_INI_BOOL( \ + "enable_idle_roam", \ + false, \ + "Enable/Disable idle roam") + +/* + * + * idle_roam_rssi_delta - This threshold is the criteria to decide whether DUT + * is idle or moving. If rssi delta is more than configured thresold then its + * considered as not idle. RSSI delta is entered in dBm. Idle roaming can be + * triggered if the connected AP rssi change exceeds or falls below the + * rssi delta and if other criteria of ini "enable_idle_roam" is met + * @Min: 0 + * @Max: 50 + * @Default: 3 + * + * Related: enable_idle_roam + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_IDLE_ROAM_RSSI_DELTA CFG_INI_UINT( \ + "idle_roam_rssi_delta", \ + 0, \ + 50, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure RSSI delta to start idle roam") + +/* + * + * idle_roam_inactive_time - Time duration in millseconds for which the + * connection is idle. + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 10000 + * + * This ini is used to configure the time in seconds for which the connection + * candidate is idle and after which idle roam scan can be triggered if + * other criteria of ini "enable_idle_roam" is met. + * + * Related: enable_idle_roam + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_IDLE_ROAM_INACTIVE_TIME CFG_INI_UINT( \ + "idle_roam_inactive_time", \ + 0, \ + 0xFFFFFFFF, \ + 10000, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure RSSI delta to start idle roam") + +/* + * + * idle_data_packet_count - No of tx/rx packets above which the connection is + * not idle. + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 10 + * + * This ini is used to configure the acceptable number of tx/rx packets below + * which the connection is idle. Ex: If idle_data_packet_count is 10 + * and if the tx/rx packet count is less than 10, the connection is + * idle. If there are more than 10 packets, the connection is active one. + * + * Related: enable_idle_roam + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_IDLE_ROAM_PACKET_COUNT CFG_INI_UINT( \ + "idle_data_packet_count", \ + 0, \ + 0xFFFFFFFF, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure idle packet count") + +/* + * + * idle_roam_min_rssi - Minimum RSSI of connected AP, below which + * idle roam scan can be triggered if other criteria of ini "enable_idle_roam" + * is met. + * @Min: -96 + * @Max: 0 + * @Default: -65 + * + * Related: enable_idle_roam + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_IDLE_ROAM_MIN_RSSI CFG_INI_INT( \ + "idle_roam_min_rssi", \ + -96, \ + 0, \ + -65, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure idle roam minimum RSSI") + +/* + * + * idle_roam_band - Band on which idle roam scan will be + * enabled + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * Value 0 - Allow idle roam on both bands + * Value 1 - Allow idle roam only on 2G band + * Value 2 - Allow idle roam only on 5G band + * + * Related: enable_idle_roam + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_LFR_IDLE_ROAM_BAND CFG_INI_UINT( \ + "idle_roam_band", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Band on which idle roam needs to be enabled") + +/* + * + * roam_triggers - Bitmap of roaming triggers. Setting this to + * zero will disable roaming altogether for the STA interface. + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 0xFFFF + * + * ROAM_TRIGGER_REASON_PER BIT 1 + * ROAM_TRIGGER_REASON_BMISS BIT 2 + * ROAM_TRIGGER_REASON_LOW_RSSI BIT 3 + * ROAM_TRIGGER_REASON_HIGH_RSSI BIT 4 + * ROAM_TRIGGER_REASON_PERIODIC BIT 5 + * ROAM_TRIGGER_REASON_MAWC BIT 6 + * ROAM_TRIGGER_REASON_DENSE BIT 7 + * ROAM_TRIGGER_REASON_BACKGROUND BIT 8 + * ROAM_TRIGGER_REASON_FORCED BIT 9 + * ROAM_TRIGGER_REASON_BTM BIT 10 + * ROAM_TRIGGER_REASON_UNIT_TEST BIT 11 + * ROAM_TRIGGER_REASON_BSS_LOAD BIT 12 + * ROAM_TRIGGER_REASON_DEAUTH BIT 13 + * ROAM_TRIGGER_REASON_IDLE BIT 14 + * ROAM_TRIGGER_REASON_STA_KICKOUT BIT 15 + * ROAM_TRIGGER_REASON_MAX BIT 16 + * + * Related: none + * + * Supported Feature: Roaming + * + * Usage: Internal + * + * + */ +#define CFG_ROAM_TRIGGER_BITMAP CFG_INI_UINT( \ + "roam_triggers", \ + 0, \ + 0xFFFFFFFF, \ + 0xFFFF, \ + CFG_VALUE_OR_DEFAULT, \ + "Bitmap of roaming triggers") + +/* + * + * sta_disable_roam - Disable Roam on sta interface + * @Min: 0 - Roam Enabled on sta interface + * @Max: 0xffffffff - Roam Disabled on sta interface irrespective + * of other interface connections + * @Default: 0x00 + * + * Disable roaming on STA iface to avoid audio glitches on p2p and ndp if + * those are in connected state. Each bit for "sta_disable_roam" INI represents + * an interface for which sta roaming can be disabled. + * + * LFR3_STA_ROAM_DISABLE_BY_P2P BIT(0) + * LFR3_STA_ROAM_DISABLE_BY_NAN BIT(1) + * + * Related: None. + * + * Supported Feature: ROAM + * + * Usage: Internal + * + * + */ +#define CFG_STA_DISABLE_ROAM CFG_INI_UINT( \ + "sta_disable_roam", \ + 0, \ + 0xffffffff, \ + 0x00, \ + CFG_VALUE_OR_DEFAULT, \ + "disable roam on STA iface if one of the iface mentioned in default is in connected state") + +#define ROAM_OFFLOAD_ALL \ + CFG(CFG_LFR3_ROAMING_OFFLOAD) \ + CFG(CFG_LFR3_ENABLE_SELF_BSS_ROAM) \ + CFG(CFG_LFR_ENABLE_DISCONNECT_ROAM) \ + CFG(CFG_LFR_ENABLE_IDLE_ROAM) \ + CFG(CFG_LFR_IDLE_ROAM_RSSI_DELTA) \ + CFG(CFG_LFR_IDLE_ROAM_INACTIVE_TIME) \ + CFG(CFG_LFR_IDLE_ROAM_PACKET_COUNT) \ + CFG(CFG_LFR_IDLE_ROAM_MIN_RSSI) \ + CFG(CFG_LFR_IDLE_ROAM_BAND) \ + CFG(CFG_ROAM_TRIGGER_BITMAP) \ + CFG(CFG_STA_DISABLE_ROAM) \ + +#else +#define ROAM_OFFLOAD_ALL +#endif + +#ifdef FEATURE_WLAN_ESE +/* + * + * EseEnabled - Enable ESE feature + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable ESE feature + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ESE_FEATURE_ENABLED CFG_INI_BOOL( \ + "EseEnabled", \ + 0, \ + "Enable ESE") +#define LFR_ESE_ALL CFG(CFG_LFR_ESE_FEATURE_ENABLED) +#else +#define LFR_ESE_ALL +#endif + +#ifdef FEATURE_LFR_SUBNET_DETECTION +/* + * + * gLFRSubnetDetectionEnable - Enable LFR3 subnet detection + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Enable IP subnet detection during legacy fast roming version 3. Legacy fast + * roaming could roam across IP subnets without host processors' knowledge. + * This feature enables firmware to wake up the host processor if it + * successfully determines change in the IP subnet. Change in IP subnet could + * potentially cause disruption in IP connnectivity if IP address is not + * refreshed. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR3_ENABLE_SUBNET_DETECTION CFG_INI_BOOL( \ + "gLFRSubnetDetectionEnable", \ + 1, \ + "Enable LFR3 subnet detection") + +#define LFR_SUBNET_DETECTION_ALL CFG(CFG_LFR3_ENABLE_SUBNET_DETECTION) +#else +#define LFR_SUBNET_DETECTION_ALL +#endif + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/* + * + * sae_single_pmk_feature_enabled - Enable/disable sae single pmk feature. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This INI is to enable/disable SAE Roaming with same PMK/PMKID feature support + * + * Related: None. + * + * Supported Feature: Roaming + * + * Usage: Internal + * + * + */ +#define CFG_SAE_SINGLE_PMK CFG_INI_BOOL( \ + "sae_single_pmk_feature_enabled", \ + false, \ + "Enable/disable SAE Roaming with single PMK/PMKID") + +#define SAE_SINGLE_PMK_ALL CFG(CFG_SAE_SINGLE_PMK) +#else +#define SAE_SINGLE_PMK_ALL +#endif + +#ifdef WLAN_ADAPTIVE_11R +/* + * + * adaptive_11r - Enable/disable adaptive 11r feature. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Adaptive 11r feature enables the AP to support FT-AKM without + * configuring the FT-AKM in the network. The AP will advertise non-FT akm + * with a vendor specific IE having Adaptive 11r bit set to 1 in the IE data. + * The AP also advertises the MDE in beacon/probe response. + * + * STA should check the adaptive 11r capability if the AP advertises MDE in + * beacon/probe and adaptive 11r capability in vendor specific IE. If adaptive + * 11r capability is found, STA can advertise the FT equivalent of the non-FT + * AKM and connect with 11r protocol. + * + * Related: None. + * + * Supported Feature: Fast BSS Transition + * + * Usage: External + * + * + */ +#define CFG_ADAPTIVE_11R CFG_INI_BOOL( \ + "enable_adaptive_11r", \ + false, \ + "Enable/disable adaptive 11r support") + +#define ADAPTIVE_11R_ALL CFG(CFG_ADAPTIVE_11R) +#else +#define ADAPTIVE_11R_ALL +#endif + +/* + * + * roaming_scan_policy - To config roaming scan policy + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to configure roaming scan behavior from HOST + * 0 : DBS scan + * 1 : Non-DBS scan + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_ROAM_SCAN_SCAN_POLICY CFG_INI_BOOL( \ + "roaming_scan_policy", \ + 0, \ + "Config roam scan policy") + +/* + * + * enable_ft_im_roaming - FW needs to perform FT initial moiblity association + * instead of FT roaming for deauth roam trigger + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to FT roaming for deauth roam trigger behavior from HOST + * 0 - To disable FT-IM + * 1 - To enable FT-IM + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: Internal + * + * + */ +#define CFG_FT_IM_ROAMING CFG_INI_BOOL( \ + "enable_ft_im_roaming", \ + 1, \ + "FT roaming for deauth roam trigger") + +/* + * + * roam_scan_inactivity_time - Device inactivity monitoring time in + * milliseconds for which the device is considered to be inactive with data + * packets count is less than configured roam_inactive_data_count. + * + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 0 + * + * The below three ini values are used to control the roam scan after the + * firmware gets empty roam scan results during periodic roam scans. + * 1. roam_scan_inactivity_time + * 2. roam_inactive_data_count + * 3. roam_scan_period_after_inactivity + * The first two ini "roam_scan_inactivity_time" and "roam_inactive_data_count" + * is frames the criteria to detect if the DUT is inactive. If the device is + * identified to be inactive based on the above two ini, then the value, + * "roam_scan_period_after_inactivity" will be used as periodic roam scan + * duration. + * + * Related: roam_inactive_data_count + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_ROAM_SCAN_INACTIVITY_TIME CFG_INI_UINT( \ + "roam_scan_inactivity_time", \ + 0, \ + 0xFFFFFFFF, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Device inactivity monitoring time") + +/* + * + * roam_inactive_data_count - Maximum allowed data packets count during + * roam_scan_inactivity_time. + * + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 10 + * + * The DUT is said to be inactive only if the data packets count + * during this roam_scan_inactivity_time is less than the configured + * roam_inactive_data_count. + * + * Related: roam_scan_inactivity_time + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_ROAM_INACTIVE_COUNT CFG_INI_UINT( \ + "roam_inactive_data_count", \ + 0, \ + 0xFFFFFFFF, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Roam scan inactivity period data pkt count") + +/* + * + * roam_scan_period_after_inactivity - Roam scan duration in ms after device is + * out of inactivity state. + * + * @Min: 0 + * @Max: 0xFFFFFFFF + * @Default: 120000 + * + * If there is empty scan results during roam scan, firmware will move to + * roam scan inactive state if roam_scan_inactivity and + * roam_inactive_data_count criteria are met. + * This ini is used to configure the roam scan duration in ms once the + * inactivity is finished and roam scan can be started. + * + * Related: roam_scan_inactivity_time, roam_inactive_data_count + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_POST_INACTIVITY_ROAM_SCAN_PERIOD CFG_INI_UINT( \ + "roam_scan_period_after_inactivity", \ + 0, \ + 0xFFFFFFFF, \ + 120000, \ + CFG_VALUE_OR_DEFAULT, \ + "Roam scan period post inactivity") + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/* + * + * enable_roam_reason_vsie - Enable/Disable inclusion of Roam Reason + * in Re(association) frame + * + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable fw to include/exclude roam reason vsie in + * Re(association) + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: internal + * + * + */ +#define CFG_ENABLE_ROAM_REASON_VSIE CFG_INI_BOOL( \ + "enable_roam_reason_vsie", \ + 0, \ + "To Enable enable_roam_reason_vsie") +#define ROAM_REASON_VSIE_ALL CFG(CFG_ENABLE_ROAM_REASON_VSIE) +#else +#define ROAM_REASON_VSIE_ALL +#endif + +#define CFG_LFR_ALL \ + CFG(CFG_LFR_MAWC_ROAM_ENABLED) \ + CFG(CFG_LFR_MAWC_ROAM_TRAFFIC_THRESHOLD) \ + CFG(CFG_LFR_MAWC_ROAM_AP_RSSI_THRESHOLD) \ + CFG(CFG_LFR_MAWC_ROAM_RSSI_HIGH_ADJUST) \ + CFG(CFG_LFR_MAWC_ROAM_RSSI_LOW_ADJUST) \ + CFG(CFG_LFR_ROAM_RSSI_ABS_THRESHOLD) \ + CFG(CFG_LFR_5G_RSSI_THRESHOLD_OFFSET) \ + CFG(CFG_LFR_ENABLE_FAST_ROAM_IN_CONCURRENCY) \ + CFG(CFG_LFR_EARLY_STOP_SCAN_ENABLE) \ + CFG(CFG_LFR_EARLY_STOP_SCAN_MIN_THRESHOLD) \ + CFG(CFG_LFR_EARLY_STOP_SCAN_MAX_THRESHOLD) \ + CFG(CFG_LFR_FIRST_SCAN_BUCKET_THRESHOLD) \ + CFG(CFG_LFR_ROAM_DENSE_TRAFFIC_THRESHOLD) \ + CFG(CFG_LFR_ROAM_DENSE_RSSI_THRE_OFFSET) \ + CFG(CFG_LFR_ROAM_DENSE_MIN_APS) \ + CFG(CFG_LFR_ROAM_BG_SCAN_BAD_RSSI_THRESHOLD) \ + CFG(CFG_LFR_ROAM_BG_SCAN_CLIENT_BITMAP) \ + CFG(CFG_LFR_ROAM_BG_SCAN_BAD_RSSI_OFFSET_2G) \ + CFG(CFG_ROAM_DATA_RSSI_THRESHOLD_TRIGGERS) \ + CFG(CFG_ROAM_DATA_RSSI_THRESHOLD) \ + CFG(CFG_RX_DATA_INACTIVITY_TIME) \ + CFG(CFG_LFR_ADAPTIVE_ROAMSCAN_DWELL_MODE) \ + CFG(CFG_LFR_PER_ROAM_ENABLE) \ + CFG(CFG_LFR_PER_ROAM_CONFIG_HIGH_RATE_TH) \ + CFG(CFG_LFR_PER_ROAM_CONFIG_LOW_RATE_TH) \ + CFG(CFG_LFR_PER_ROAM_CONFIG_RATE_TH_PERCENT) \ + CFG(CFG_LFR_PER_ROAM_REST_TIME) \ + CFG(CFG_LFR_PER_ROAM_MONITOR_TIME) \ + CFG(CFG_LFR_PER_ROAM_MIN_CANDIDATE_RSSI) \ + CFG(CFG_LFR3_ROAM_DISALLOW_DURATION) \ + CFG(CFG_LFR3_ROAM_RSSI_CHANNEL_PENALIZATION) \ + CFG(CFG_LFR3_ROAM_NUM_DISALLOWED_APS) \ + CFG(CFG_LFR_ENABLE_5G_BAND_PREF) \ + CFG(CFG_LFR_5G_RSSI_BOOST_THRESHOLD) \ + CFG(CFG_LFR_5G_RSSI_BOOST_FACTOR) \ + CFG(CFG_LFR_5G_MAX_RSSI_BOOST) \ + CFG(CFG_LFR_5G_RSSI_PENALIZE_THRESHOLD) \ + CFG(CFG_LFR_5G_RSSI_PENALIZE_FACTOR) \ + CFG(CFG_LFR_5G_MAX_RSSI_PENALIZE) \ + CFG(CFG_LFR_MAX_NUM_PRE_AUTH) \ + CFG(CFG_LFR3_ROAM_PREAUTH_RETRY_COUNT) \ + CFG(CFG_LFR3_ROAM_PREAUTH_NO_ACK_TIMEOUT) \ + CFG(CFG_LFR_FEATURE_ENABLED) \ + CFG(CFG_LFR_MAWC_FEATURE_ENABLED) \ + CFG(CFG_LFR_FAST_TRANSITION_ENABLED) \ + CFG(CFG_LFR_ROAM_RSSI_DIFF) \ + CFG(CFG_LFR_ENABLE_WES_MODE) \ + CFG(CFG_LFR_ROAM_SCAN_OFFLOAD_ENABLED) \ + CFG(CFG_LFR_NEIGHBOR_SCAN_CHANNEL_LIST) \ + CFG(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD) \ + CFG(CFG_LFR_NEIGHBOR_SCAN_MIN_TIMER_PERIOD) \ + CFG(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD) \ + CFG(CFG_LFR_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF) \ + CFG(CFG_LFR_ROAM_RESCAN_RSSI_DIFF) \ + CFG(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME) \ + CFG(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME) \ + CFG(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD) \ + CFG(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD) \ + CFG(CFG_LFR_ROAM_BMISS_FIRST_BCNT) \ + CFG(CFG_LFR_ROAM_BMISS_FINAL_BCNT) \ + CFG(CFG_LFR_ROAM_BEACON_RSSI_WEIGHT) \ + CFG(CFG_LFR_ROAMING_DFS_CHANNEL) \ + CFG(CFG_LFR_ROAM_SCAN_HI_RSSI_MAXCOUNT) \ + CFG(CFG_LFR_ROAM_SCAN_HI_RSSI_DELTA) \ + CFG(CFG_LFR_ROAM_SCAN_HI_RSSI_DELAY) \ + CFG(CFG_LFR_ROAM_SCAN_HI_RSSI_UB) \ + CFG(CFG_LFR_ROAM_PREFER_5GHZ) \ + CFG(CFG_LFR_ROAM_INTRA_BAND) \ + CFG(CFG_LFR_ROAM_SCAN_N_PROBES) \ + CFG(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME) \ + CFG(CFG_LFR_DELAY_BEFORE_VDEV_STOP) \ + CFG(CFG_ENABLE_BSS_LOAD_TRIGGERED_ROAM) \ + CFG(CFG_BSS_LOAD_THRESHOLD) \ + CFG(CFG_BSS_LOAD_SAMPLE_TIME) \ + CFG(CFG_LFR3_ROAM_HO_DELAY_FOR_RX) \ + CFG(CFG_LFR_MIN_DELAY_BTW_ROAM_SCAN) \ + CFG(CFG_LFR_ROAM_SCAN_TRIGGER_REASON_BITMASK) \ + CFG(CFG_LFR_ROAM_FT_OPEN_ENABLE) \ + CFG(CFG_LFR_ROAM_FORCE_RSSI_TRIGGER) \ + CFG(CFG_ROAM_SCAN_SCAN_POLICY) \ + CFG(CFG_ROAM_SCAN_INACTIVITY_TIME) \ + CFG(CFG_FT_IM_ROAMING) \ + CFG(CFG_ROAM_INACTIVE_COUNT) \ + CFG(CFG_POST_INACTIVITY_ROAM_SCAN_PERIOD) \ + CFG(CFG_BSS_LOAD_TRIG_5G_RSSI_THRES) \ + CFG(CFG_BSS_LOAD_TRIG_2G_RSSI_THRES) \ + CFG(CFG_LFR_FULL_ROAM_SCAN_REFRESH_PERIOD) \ + ADAPTIVE_11R_ALL \ + ROAM_OFFLOAD_ALL \ + LFR_ESE_ALL \ + LFR_SUBNET_DETECTION_ALL \ + SAE_SINGLE_PMK_ALL \ + ROAM_REASON_VSIE_ALL + +#endif /* CFG_MLME_LFR_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_mbo.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_mbo.h new file mode 100644 index 0000000000000000000000000000000000000000..02dce2278e59d6aa896b1fd15eae4cdb444e8f2e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_mbo.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of QOS related + * converged configurations. + */ + +#ifndef __CFG_MLME_MBO_H +#define __CFG_MLME_MBO_H + +/* + * + * g_mbo_candidate_rssi_thres - Candidate AP's minimum RSSI to accept + * @Min: -120 + * @Max: 0 + * @Default: -72 + * + * This ini specifies the minimum RSSI value a candidate should have to accept + * it as a target for transition. + * + * Related: N/A + * + * Supported Feature: MBO + * + * Usage: Internal + * + * + */ +#define CFG_MBO_CANDIDATE_RSSI_THRESHOLD CFG_INI_INT( \ + "g_mbo_candidate_rssi_thres", \ + -120, \ + 0, \ + -72, \ + CFG_VALUE_OR_DEFAULT, \ + "candidate AP rssi threshold") +/* + * + * g_mbo_current_rssi_thres - Connected AP's RSSI threshold to consider a + * transition + * @Min: -120 + * @Max: 0 + * @Default: -65 + * + * This ini is used to configure connected AP's RSSI threshold value to consider + * a transition. + * + * Related: N/A + * + * Supported Feature: MBO + * + * Usage: Internal + * + * + */ +#define CFG_MBO_CURRENT_RSSI_THRESHOLD CFG_INI_INT( \ + "g_mbo_current_rssi_thres", \ + -120, \ + 0, \ + -65, \ + CFG_VALUE_OR_DEFAULT, \ + "current AP rssi threshold") + +/* + * + * g_mbo_current_rssi_mcc_thres - connected AP's RSSI threshold value to prefer + * against a MCC + * @Min: -120 + * @Max: 0 + * @Default: -75 + * + * This ini is used to configure connected AP's minimum RSSI threshold that is + * preferred against a MCC case, if the candidate can cause MCC. + * + * Related: N/A + * + * Supported Feature: MBO + * + * Usage: Internal + * + * + */ +#define CFG_MBO_CUR_RSSI_MCC_THRESHOLD CFG_INI_INT( \ + "g_mbo_current_rssi_mcc_thres", \ + -120, \ + 0, \ + -75, \ + CFG_VALUE_OR_DEFAULT, \ + "current AP mcc rssi threshold") + +/* + * + * g_mbo_candidate_rssi_btc_thres - Candidate AP's minimum RSSI threshold to + * prefer it even in case of BT coex + * @Min: -120 + * @Max: 0 + * @Default: -70 + * + * This ini is used to configure candidate AP's minimum RSSI threshold to prefer + * it for transition even in case of BT coex. + * + * Related: N/A + * + * Supported Feature: MBO + * + * Usage: Internal + * + * + */ +#define CFG_MBO_CAND_RSSI_BTC_THRESHOLD CFG_INI_INT( \ + "g_mbo_candidate_rssi_btc_thres", \ + -120, \ + 0, \ + -70, \ + CFG_VALUE_OR_DEFAULT, \ + "candidate AP rssi threshold") + +#define CFG_MBO_ALL \ + CFG(CFG_MBO_CANDIDATE_RSSI_THRESHOLD) \ + CFG(CFG_MBO_CURRENT_RSSI_THRESHOLD) \ + CFG(CFG_MBO_CUR_RSSI_MCC_THRESHOLD) \ + CFG(CFG_MBO_CAND_RSSI_BTC_THRESHOLD) + +#endif /* __CFG_MLME_MBO_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_mwc.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_mwc.h new file mode 100644 index 0000000000000000000000000000000000000000..677cafe44bd8387fb3aaf10f4365be36eb3fb493 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_mwc.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2012-2018,2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains configuration definitions for MLME WMC. + */ +#ifndef CFG_MLME_MWC_H_ +#define CFG_MLME_MWC_H_ + +#ifdef MWS_COEX +/* + * + * gMwsCoex4gQuickTdm - Bitmap to control MWS-COEX 4G quick FTDM policy + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * It is a 32 bit value such that the various bits represent as below: + * Bit-0 : 0 - Don't allow quick FTDM policy (Default) + * 1 - Allow quick FTDM policy + * Bit 1-31 : reserved for future use + * + * It is used to enable or disable MWS-COEX 4G (LTE) Quick FTDM + * + * Usage: Internal + * + * + */ + +#define CFG_MWS_COEX_4G_QUICK_FTDM CFG_INI_UINT( \ + "gMwsCoex4gQuickTdm", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "set mws-coex 4g quick ftdm policy") + +/* + * + * gMwsCoex5gnrPwrLimit - Bitmap to set MWS-COEX 5G-NR power limit + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * It is a 32 bit value such that the various bits represent as below: + * Bit-0 : Don't apply user specific power limit, + * use internal power limit (Default) + * Bit 1-2 : Invalid value (Ignored) + * Bit 3-21 : Apply the specified value as the external power limit, in dBm + * Bit 22-31 : Invalid value (Ignored) + * + * It is used to set MWS-COEX 5G-NR power limit + * + * Usage: Internal + * + * + */ + +#define CFG_MWS_COEX_5G_NR_PWR_LIMIT CFG_INI_UINT( \ + "gMwsCoex5gnrPwrLimit", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "set mws-coex 5g-nr power limit") + +/* + * + * mws_coex_pcc_channel_avoid_delay - configures the duration, when WWAN PCC + * (Primary Component Carrier) conflicts with WLAN channel. + * @Min: 0x00 + * @Max: 0xFF + * @Default: 0x3C + * + * It is used to set MWS-COEX WWAN PCC channel avoidance delay + * + * Usage: External + * + * + */ +#define CFG_MWS_COEX_PCC_CHANNEL_AVOID_DELAY CFG_INI_UINT(\ + "mws_coex_pcc_channel_avoid_delay", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x3C, \ + CFG_VALUE_OR_DEFAULT, \ + "set mws-coex PCC channel avoidance delay") + +/* + * + * mws_coex_scc_channel_avoid_delay - configures the duration, when WWAN SCC + * (Secondary Component Carrier) conflicts with WLAN channel. + * @Min: 0x00 + * @Max: 0xFF + * @Default: 0x78 + * + * It is used to set MWS-COEX WWAN SCC channel avoidance delay + * + * Usage: External + * + * + */ +#define CFG_MWS_COEX_SCC_CHANNEL_AVOID_DELAY CFG_INI_UINT(\ + "mws_coex_scc_channel_avoid_delay", \ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x78, \ + CFG_VALUE_OR_DEFAULT, \ + "set mws-coex SCC channel avoidance delay") + +#define CFG_MWC_ALL \ + CFG(CFG_MWS_COEX_4G_QUICK_FTDM) \ + CFG(CFG_MWS_COEX_5G_NR_PWR_LIMIT) \ + CFG(CFG_MWS_COEX_PCC_CHANNEL_AVOID_DELAY) \ + CFG(CFG_MWS_COEX_SCC_CHANNEL_AVOID_DELAY) + +#else +#define CFG_MWC_ALL +#endif /* MWS_COEX */ + +#endif /* CFG_MLME_MWC_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_nss_chains.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_nss_chains.h new file mode 100644 index 0000000000000000000000000000000000000000..a4d47e71df0e662abd079e1ef7cd6c7644a8ba18 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_nss_chains.h @@ -0,0 +1,554 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_NSS_CHAINS +#define __CFG_MLME_NSS_CHAINS + +/* + * + * num_tx_chains_2g - Config Param to change number of tx + * chains per vdev for 2.4ghz frequency connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of chains for eg:- + * 0x01249249 - change all vdev's num tx chains for 2.4ghz connection to 1 each + * 0x02492492 - change all vdev's num tx chains for 2.4ghz connection to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_NUM_TX_CHAINS_2G CFG_INI_UINT( \ + "num_tx_chains_2g", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "num tx chains 2g") + +/* + * + * num_tx_chains_5g - Config Param to change number of tx + * chains per vdev for 5 ghz frequency connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of chains for eg:- + * 0x01249249- change all vdev's tx num chains for 5ghz connection to 1 each + * 0x02492492 - change all vdev's tx num chains for 5ghz connection to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_NUM_TX_CHAINS_5G CFG_INI_UINT( \ + "num_tx_chains_5g", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "num tx chains 5g") + +/* + * + * num_rx_chains_2g - Config Param to change number of rx + * chains per vdev for 2.4 ghz frequency connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of chains for eg:- + * 0x01249249 - change all vdev's rx num chains for 2.4ghz connections to 1 each + * 0x02492492 - change all vdev's rx num chains for 2.4ghz connections to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_NUM_RX_CHAINS_2G CFG_INI_UINT( \ + "num_rx_chains_2g", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "num rx chains 2g") + +/* + * + * num_rx_chains_5g - Config Param to change number of rx + * chains per vdev for 5 ghz frequency connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of chains for eg:- + * 0x01249249 - change all vdev's rx num chains for 5ghz connections to 1 each + * 0x02492492 - change all vdev's rx num chains for 5ghz connections to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_NUM_RX_CHAINS_5G CFG_INI_UINT( \ + "num_rx_chains_5g", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "num rx chains 5g") + +/* + * + * tx_nss_2g - Config Param to change tx nss + * per vdev for 2.4ghz frequency connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of tx spatial streams for eg:- + * 0x01249249 - change all vdev's tx nss for 2.4ghz connections to 1 each + * 0x02492492 - change all vdev's tx nss for 2.4ghz connections to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_TX_NSS_2G CFG_INI_UINT( \ + "tx_nss_2g", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "tx nss 2.4ghz") + +/* + * + * tx_nss_5g - Config Param to change tx nss + * per vdev for 5ghz frequency connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of tx spatial streams for eg:- + * 0x01249249 - change all vdev's tx nss for 5ghz connections to 1 each + * 0x02492492 - change all vdev's tx nss for 5ghz connections to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_TX_NSS_5G CFG_INI_UINT( \ + "tx_nss_5g", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "tx nss 5ghz") + +/* + * + * rx_nss_2g - Config Param to change rx nss + * per vdev for 2.4ghz frequency connections + * + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of rx spatial streams for eg:- + * 0x01249249 - change all vdev's rx nss for 2.4ghz connections to 1 each + * 0x02492492 - change all vdev's rx nss for 2.4ghz connections to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_RX_NSS_2G CFG_INI_UINT( \ + "rx_nss_2g", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "rx nss 2.4ghz") + +/* + * + * rx_nss_5g - Config Param to change rx nss + * per vdev for 5ghz frequency connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of rx spatial streams for eg:- + * 0x01249249 - change all vdev's rx nss for 5ghz connections to 1 each + * 0x02492492 - change all vdev's rx nss for 5ghz connections to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_RX_NSS_5G CFG_INI_UINT( \ + "rx_nss_5g", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "rx nss 5ghz") + +/* + * + * num_tx_chains_11b - Config Param to change number of tx + * chains per vdev for 2.4ghz 11b mode connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of chains for eg:- + * 0x01249249 - change all vdev's num chains for 11b connections to 1 each + * 0x02492492 - change all vdev's num chains for 11b connections to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_NUM_TX_CHAINS_11b CFG_INI_UINT( \ + "num_tx_chains_11b", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "num tx chains 11b") + +/* + * + * num_tx_chains_11g - Config Param to change number of tx + * chains per vdev for 2.4ghz 11g mode connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of chains for eg:- + * 0x01249249 - change all vdev's num chains for 11g connections to 1 each + * 0x02492492 - change all vdev's num chains for 11g connections to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_NUM_TX_CHAINS_11g CFG_INI_UINT( \ + "num_tx_chains_11g", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "num tx chains 11g") + +/* + * + * num_tx_chains_11a - Config Param to change number of tx + * chains per vdev for 5ghz 11a mode connections + * @Min: 0x01249249 + * @Max: 0x02492492 + * @Default: 0x02492492 + * + * This ini is used to change the num of chains for eg:- + * 0x01249249 - change all vdev's num chains for 11a connections to 1 each + * 0x02492492 - change all vdev's num chains for 11a connections to 2 each + * Bits VDEV Type + * BIT[0:2] STA + * BIT[3:5] SAP + * BIT[6:8] P2P GO + * BIT[9:11] P2P Client + * BIT[12:14] TDLS + * BIT[15:17] IBSS + * BIT[18:20] P2P device + * BIT[21:23] OCB + * BIT[24:26] NAN + * BIT[27:31] Reserved + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_NUM_TX_CHAINS_11a CFG_INI_UINT( \ + "num_tx_chains_11a", \ + 0x01249249, \ + 0x02492492, \ + 0x02492492, \ + CFG_VALUE_OR_DEFAULT, \ + "num tx chains 11a") + +/* + * + * disable_tx_mrc_2g - Config Param to disable 2 chains in 1x1 nss mode + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_DISABLE_TX_MRC_2G CFG_INI_BOOL( \ + "disable_tx_mrc_2g", \ + 0, \ + "disable diversity gain tx 2g") + +/* + * + * disable_rx_mrc_2g - Config Param to disable 2 chains in 1x1 nss mode + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_DISABLE_RX_MRC_2G CFG_INI_BOOL( \ + "disable_rx_mrc_2g", \ + 0, \ + "disable diversity gain rx 2g") + +/* + * + * disable_tx_mrc_5g - Config Param to disable 2 chains in 1x1 nss mode + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_DISABLE_TX_MRC_5G CFG_INI_BOOL( \ + "disable_tx_mrc_5g", \ + 0, \ + "disable diversity gain tx 5g") + +/* + * + * disable_rx_mrc_5g - Config Param to disable 2 chains in 1x1 nss mode + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: STA/SAP/P2P/IBSS/NAN. + * + * Supported Feature: Dynamic chainmask + * + * Usage: External + * + * + */ +#define CFG_DISABLE_RX_MRC_5G CFG_INI_BOOL( \ + "disable_rx_mrc_5g", \ + 0, \ + "disable diversity gain rx 5g") + +#define CFG_NSS_CHAINS_ALL \ + CFG(CFG_NUM_TX_CHAINS_2G) \ + CFG(CFG_NUM_TX_CHAINS_5G) \ + CFG(CFG_NUM_RX_CHAINS_2G) \ + CFG(CFG_NUM_RX_CHAINS_5G) \ + CFG(CFG_TX_NSS_5G) \ + CFG(CFG_TX_NSS_2G) \ + CFG(CFG_RX_NSS_5G) \ + CFG(CFG_RX_NSS_2G) \ + CFG(CFG_NUM_TX_CHAINS_11b) \ + CFG(CFG_NUM_TX_CHAINS_11g) \ + CFG(CFG_NUM_TX_CHAINS_11a) \ + CFG(CFG_DISABLE_TX_MRC_2G) \ + CFG(CFG_DISABLE_RX_MRC_2G) \ + CFG(CFG_DISABLE_TX_MRC_5G) \ + CFG(CFG_DISABLE_RX_MRC_5G) + +#endif /* __CFG_MLME_NSS_CHAINS */ + diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_obss_ht40.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_obss_ht40.h new file mode 100644 index 0000000000000000000000000000000000000000..cb7a5873ff6722bb6b57c8e98fd4074a9583441e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_obss_ht40.h @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains configuration definitions for MLME OBSS HT40. + */ +#ifndef CFG_MLME_OBSS_HT40_H__ +#define CFG_MLME_OBSS_HT40_H__ + +/* + * + * obss_active_dwelltime - Set obss active dwelltime + * @Min: 10 + * @Max: 1000 + * @Default: 10 + * + * This ini is used to set dwell time in secs for active + * obss scan + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_OBSS_HT40_SCAN_ACTIVE_DWELL_TIME CFG_INI_UINT( \ + "obss_active_dwelltime", \ + 10, \ + 1000, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Set obss active dwelltime") + +/* + * + * obss_passive_dwelltime - Set obss passive dwelltime + * @Min: 5 + * @Max: 1000 + * @Default: 20 + * + * This ini is used to set dwell time in secs for passive + * obss scan + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_OBSS_HT40_SCAN_PASSIVE_DWELL_TIME CFG_INI_UINT( \ + "obss_passive_dwelltime", \ + 5, \ + 1000, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "Set obss passive dwelltime") + +/* + * + * obss_width_trigger_interval - Set obss trigger interval + * @Min: 10 + * @Max: 900 + * @Default: 200 + * + * This ini is used during an OBSS scan operation, + * where each channel in the set is scanned at least + * once per configured trigger interval time. Unit is TUs. + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_OBSS_HT40_SCAN_WIDTH_TRIGGER_INTERVAL CFG_INI_UINT( \ + "obss_width_trigger_interval", \ + 10, \ + 900, \ + 200, \ + CFG_VALUE_OR_DEFAULT, \ + "Set obss trigger interval") + +/* + * obss_passive_total_per_channel - Set obss scan passive total per channel + * @Min: 200 + * @Max: 10000 + * @Default: 200 + * + * FW can perform multiple scans with in a OBSS scan interval. This cfg is for + * the total per channel dwell time of passive scans. Unit is TUs. + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: Internal + * + */ +#define CFG_OBSS_HT40_SCAN_PASSIVE_TOTAL_PER_CHANNEL CFG_UINT( \ + "obss_passive_total_per_channel", \ + 200, \ + 10000, \ + 200, \ + CFG_VALUE_OR_DEFAULT, \ + "obss passive total per channel") + +/* + * obss_active_total_per_channel - Set obss scan active total per channel + * @Min: 20 + * @Max: 10000 + * @Default: 20 + * + * FW can perform multiple scans with in a OBSS scan interval. This cfg is for + * the total per channel dwell time of active scans. Unit is TUs. + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: Internal + * + */ +#define CFG_OBSS_HT40_SCAN_ACTIVE_TOTAL_PER_CHANNEL CFG_UINT( \ + "obss_active_total_per_channel", \ + 20, \ + 10000, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "obss active total per channel") + +/* + * obss_scan_activity_thre - Set obss scan activity threshold + * @Min: 0 + * @Max: 100 + * @Default: 25 + * + * This cfg sets obss scan activity threshold. + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: Internal + * + */ +#define CFG_OBSS_HT40_SCAN_ACTIVITY_THRESHOLD CFG_UINT( \ + "obss_scan_activity_thre", \ + 0, \ + 100, \ + 25, \ + CFG_VALUE_OR_DEFAULT, \ + "obss ht40 scan activity threshold") + +/* + * obss_width_transition_delay - Set obss width transition delay + * @Min: 5 + * @Max: 100 + * @Default: 5 + * + * This cfg sets obss width transition delay, it is used to check exemption + * from scan. The unit is TUs. + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: Internal + * + */ +#define CFG_OBSS_HT40_WIDTH_CH_TRANSITION_DELAY CFG_UINT( \ + "obss_width_transition_delay", \ + 5, \ + 100, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "obss ht40 width transition delay") + +/* + * + * override_ht20_40_24g - Use channel bonding in 2.4GHz from supplicant + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini used to set whether use channel Bonding in 2.4GHz from supplicant + * if gChannelBondingMode24GHz is set + * + * Related: gChannelBondingMode24GHz + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_OBSS_HT40_OVERRIDE_HT40_20_24GHZ CFG_INI_BOOL( \ + "override_ht20_40_24g", \ + 0, \ + "Use channel bonding in 24 GHz") + +/* + * + * obss_detection_offload - Enable OBSS detection offload + * @Min: 0 + * @Max: 1 + * @Default: 0 + */ +#define CFG_OBSS_DETECTION_OFFLOAD CFG_BOOL( \ + "obss_detection_offload", \ + 0, \ + "Enable OBSS detection offload") + +/* + * + * obss_color_collision_offload - Enable obss color collision offload + * @Min: 0 + * @Max: 1 + * @Default: 0 + */ +#define CFG_OBSS_COLOR_COLLISION_OFFLOAD CFG_BOOL( \ + "obss_color_collision_offload", \ + 0, \ + "Enable obss color collision offload") + +/* + * + * bss_color_collision_det_sta - Enables BSS color collision detection in STA + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini used to enable or disable the BSS color collision detection in + * STA mode if obss_color_collision_offload is enabled. + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_BSS_CLR_COLLISION_DETCN_STA CFG_INI_BOOL( \ + "bss_color_collision_det_sta", \ + 1, \ + "BSS color collision detection in STA") + +#define CFG_OBSS_HT40_ALL \ + CFG(CFG_OBSS_HT40_SCAN_ACTIVE_DWELL_TIME) \ + CFG(CFG_OBSS_HT40_SCAN_PASSIVE_DWELL_TIME) \ + CFG(CFG_OBSS_HT40_SCAN_WIDTH_TRIGGER_INTERVAL) \ + CFG(CFG_OBSS_HT40_SCAN_PASSIVE_TOTAL_PER_CHANNEL) \ + CFG(CFG_OBSS_HT40_SCAN_ACTIVE_TOTAL_PER_CHANNEL) \ + CFG(CFG_OBSS_HT40_SCAN_ACTIVITY_THRESHOLD) \ + CFG(CFG_OBSS_HT40_WIDTH_CH_TRANSITION_DELAY) \ + CFG(CFG_OBSS_HT40_OVERRIDE_HT40_20_24GHZ) \ + CFG(CFG_OBSS_DETECTION_OFFLOAD) \ + CFG(CFG_BSS_CLR_COLLISION_DETCN_STA) \ + CFG(CFG_OBSS_COLOR_COLLISION_OFFLOAD) + +#endif /* CFG_MLME_OBSS_HT40_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_oce.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_oce.h new file mode 100644 index 0000000000000000000000000000000000000000..90142a21f11c1122e1eec734a01684350b54acd9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_oce.h @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_OCE_H +#define __CFG_MLME_OCE_H + +/* + * + * g_enable_bcast_probe_rsp - Enable Broadcast probe response. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable broadcast probe response. + * If this is disabled then OCE ini oce_sta_enable will also be + * disabled and OCE IE will not be sent in frames. + * + * Related: None + * + * Supported Feature: FILS + * + * Usage: External + * + * + */ +#define CFG_ENABLE_BCAST_PROBE_RESP CFG_INI_BOOL( \ + "g_enable_bcast_probe_rsp", \ + 1, \ + "Enable Broadcast probe response") + +/* + * + * oce_sta_enable - Enable/disable oce feature for STA + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable oce feature for STA + * + * Related: None + * + * Supported Feature: OCE + * + * Usage: External + * + * + */ +#define CFG_OCE_ENABLE_STA CFG_INI_BOOL( \ + "oce_sta_enable", \ + 1, \ + "Enable/disable oce feature for STA") + +/* + * + * oce_sap_enable - Enable/disable oce feature for SAP + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable oce feature for SAP + * + * Related: None + * + * Supported Feature: OCE + * + * Usage: External + * + * + */ +#define CFG_OCE_ENABLE_SAP CFG_INI_BOOL( \ + "oce_sap_enable", \ + 1, \ + "Enable/disable oce feature for SAP") + +/* + * + * oce_enable_rssi_assoc_reject - Enable/disable rssi based assoc rejection + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable rssi based assoc rejection. If this is + * disabled then OCE ini oce_sta_enable will also be disabled and OCE IE will + * not be sent in frames. + * + * Related: None + * + * Supported Feature: OCE + * + * Usage: External + * + * + */ +#define CFG_OCE_ENABLE_RSSI_BASED_ASSOC_REJECT CFG_INI_BOOL( \ + "oce_enable_rssi_assoc_reject", \ + 1, \ + "Enable/disable rssi based assoc rejection") + +/* + * + * oce_enable_probe_req_rate - Set probe request rate + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set probe request rate to 5.5Mbps as per OCE requirement + * in 2.4G band + * + * Related: None + * + * Supported Feature: OCE + * + * Usage: External + * + * + */ +#define CFG_OCE_PROBE_REQ_RATE CFG_INI_BOOL( \ + "oce_enable_probe_req_rate", \ + 1, \ + "Set probe request rate for OCE") + +/* + * + * oce_enable_probe_resp_rate - Set probe response rate + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set probe response rate to 5.5Mbps as per OCE requirement + * in 2.4G band + * + * Related: None + * + * Supported Feature: OCE + * + * Usage: External + * + * + */ +#define CFG_OCE_PROBE_RSP_RATE CFG_INI_BOOL( \ + "oce_enable_probe_resp_rate", \ + 0, \ + "Set probe response rate for OCE") + +/* + * + * oce_enable_beacon_rate - Set beacon rate + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set beacon rate to 5.5Mbps as per OCE requirement in + * 2.4G band + * + * Related: None + * + * Supported Feature: OCE + * + * Usage: External + * + * + */ +#define CFG_OCE_BEACON_RATE CFG_INI_BOOL( \ + "oce_enable_beacon_rate", \ + 0, \ + "Set Beacon rate for OCE") +/* + * + * oce_enable_probe_req_deferral - Enable/disable probe request deferral + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable probe request deferral as per OCE spec + * + * Related: None + * + * Supported Feature: OCE + * + * Usage: External + * + * + */ +#define CFG_ENABLE_PROBE_REQ_DEFERRAL CFG_INI_BOOL( \ + "oce_enable_probe_req_deferral", \ + 1, \ + "Enable/disable probe request deferral for OCE") + +/* + * + * oce_enable_fils_discovery_sap - Enable/disable fils discovery in sap mode + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable fils discovery in sap mode + * + * Related: None + * + * Supported Feature: FILS + * + * Usage: External + * + * + */ +#define CFG_ENABLE_FILS_DISCOVERY_SAP CFG_INI_BOOL( \ + "oce_enable_fils_discovery_sap", \ + 1, \ + "Enable/disable fils discovery in sap mode") + +/* + * + * enable_esp_for_roam - Enable/disable esp feature + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable ESP(Estimated service parameters) IE + * parsing and decides whether firmware will include this in its scoring algo. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_ENABLE_ESP_FEATURE CFG_INI_BOOL( \ + "enable_esp_for_roam", \ + 1, \ + "Enable/disable esp feature") + +/* + * + * g_is_fils_enabled - Enable/Disable FILS support in driver + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable FILS support in driver + * Driver will update config to supplicant based on this config. + * + * Related: None + * + * Supported Feature: FILS + * + * Usage: External + * + * + */ +#define CFG_IS_FILS_ENABLED CFG_INI_BOOL( \ + "g_is_fils_enabled", \ + 1, \ + "Enable/disable support") + +#define CFG_OCE_ALL \ + CFG(CFG_ENABLE_BCAST_PROBE_RESP) \ + CFG(CFG_OCE_ENABLE_STA) \ + CFG(CFG_OCE_ENABLE_SAP) \ + CFG(CFG_OCE_ENABLE_RSSI_BASED_ASSOC_REJECT) \ + CFG(CFG_OCE_PROBE_REQ_RATE) \ + CFG(CFG_OCE_PROBE_RSP_RATE) \ + CFG(CFG_OCE_BEACON_RATE) \ + CFG(CFG_ENABLE_PROBE_REQ_DEFERRAL) \ + CFG(CFG_ENABLE_FILS_DISCOVERY_SAP) \ + CFG(CFG_ENABLE_ESP_FEATURE) \ + CFG(CFG_IS_FILS_ENABLED) +#endif /* __CFG_MLME_OCE_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_power.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_power.h new file mode 100644 index 0000000000000000000000000000000000000000..81763ef8bc41d6ca8d4ea86559d6796fc2ec3a6c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_power.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_POWER_H +#define __CFG_MLME_POWER_H + +/* + * + * max_tx_power_24 - max tx power allowed for 2.4 ghz + * @Min: 0 minimum length of tx power + * @Max: default data length of tx power in string format + * @Default: 1, 14, 20 + * + * This ini contains the string in the form of first_channel number, + * number of channels and max tx power triplets + */ +#define CFG_MAX_TX_POWER_2_4_DATA "1, 14, 20" +#define CFG_MAX_TX_POWER_2_4 CFG_STRING( \ + "max_tx_power_24", \ + 0, \ + sizeof(CFG_MAX_TX_POWER_2_4_DATA) - 1, \ + CFG_MAX_TX_POWER_2_4_DATA, \ + "max tx power 24") + +/* + * + * max_tx_power_5 - max tx power allowed for 5 ghz + * @Min: 0 minimum length of tx power + * @Max: default data length of tx power in string format + * @Default: 36, 126, 20 + * + * This ini contains the string in the form of first_channel number, + * number of channels and max tx power triplets + */ +#define CFG_MAX_TX_POWER_5_DATA "36, 126, 20" +#define CFG_MAX_TX_POWER_5 CFG_STRING( \ + "max_tx_power_5", \ + 0, \ + sizeof(CFG_MAX_TX_POWER_5_DATA) - 1, \ + CFG_MAX_TX_POWER_5_DATA, \ + "max tx power 5") + +/* + * + * gPowerUsage - power usage name + * @Min: "Min" - minimum power usage + * @Max: "Max" - maximum power usage + * @Default: "Mod" + * + * Usage: Internal/External + * + * + */ + +#define CFG_POWER_USAGE CFG_INI_STRING( \ + "gPowerUsage", \ + 0, \ + 3, \ + "Mod", \ + "power usage") +/* + * + * TxPower2g - Limit power in case of 2.4ghz + * @Min: 0 + * @Max: 30 + * @Default: 30 + * + * Usage: Internal/External + * + * + */ + +#define CFG_SET_TXPOWER_LIMIT2G CFG_INI_UINT( \ + "TxPower2g", \ + 0, \ + 30, \ + 30, \ + CFG_VALUE_OR_DEFAULT, \ + "power limit 2g") +/* + * + * TxPower5g - Limit power in case of 5ghz + * @Min: 0 + * @Max: 30 + * @Default: 30 + * + * Usage: Internal/External + * + * + */ + +#define CFG_SET_TXPOWER_LIMIT5G CFG_INI_UINT( \ + "TxPower5g", \ + 0, \ + 30, \ + 30, \ + CFG_VALUE_OR_DEFAULT, \ + "power limit 5g") + +/* + * + * gTxPowerCap - WLAN max tx power + * @Min: 0 + * @Max: 128 + * @Default: 128 + * + * This ini is used to configure the device max tx power. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_MAX_TX_POWER CFG_INI_UINT( \ + "gTxPowerCap", \ + 0, \ + 128, \ + 128, \ + CFG_VALUE_OR_DEFAULT, \ + "WLAN max tx power") + +/* + * + * current_tx_power_level - current tx power level + * @Min: 0 + * @Max: 128 + * @Default: 27 + */ +#define CFG_CURRENT_TX_POWER_LEVEL CFG_UINT( \ + "current_tx_power_level", \ + 0, \ + 128, \ + 27, \ + CFG_VALUE_OR_DEFAULT, \ + "current tx power level") + +/* + * + * local_power_constraint - local power constraint + * @Min: 0 + * @Max: 255 + * @Default: 0 + */ +#define CFG_LOCAL_POWER_CONSTRAINT CFG_UINT( \ + "local_power_constraint", \ + 0, \ + 255, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "local power constraint") + +#define CFG_MLME_POWER_ALL \ + CFG(CFG_MAX_TX_POWER_2_4) \ + CFG(CFG_MAX_TX_POWER_5) \ + CFG(CFG_POWER_USAGE) \ + CFG(CFG_SET_TXPOWER_LIMIT2G) \ + CFG(CFG_SET_TXPOWER_LIMIT5G) \ + CFG(CFG_MAX_TX_POWER) \ + CFG(CFG_CURRENT_TX_POWER_LEVEL) \ + CFG(CFG_LOCAL_POWER_CONSTRAINT) + +#endif /* __CFG_MLME_POWER_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_powersave.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_powersave.h new file mode 100644 index 0000000000000000000000000000000000000000..2780ae9403c6446f5d7ec5909fd7f2d1c98e291e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_powersave.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of power save related + * converged configurations. + */ + +#ifndef __CFG_MLME_POWERSAVE_H +#define __CFG_MLME_POWERSAVE_H + +/* + * + * gEnableImps - Enable/Disable IMPS + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/Disable IMPS(IdleModePowerSave) Mode + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_ENABLE_IMPS CFG_INI_BOOL( \ + "gEnableImps", \ + 1,\ + "Enable/disable IMPS") + +/* + * + * gEnableBmps - Enable/Disable BMPS + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/Disable BMPS(BeaconModePowerSave) Mode + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_ENABLE_PS CFG_INI_BOOL( \ + "gEnableBmps", \ + 1,\ + "Enable/disable BMPS") + +/* + * + * gAutoBmpsTimerValue - Set Auto BMPS Timer value + * @Min: 0 + * @Max: 1000 + * @Default: 600 + * + * This ini is used to set Auto BMPS Timer value in seconds + * + * Related: gEnableBmps + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_AUTO_BMPS_ENABLE_TIMER CFG_INI_UINT( \ + "gAutoBmpsTimerValue", \ + 0, \ + 1000, \ + 600, \ + CFG_VALUE_OR_DEFAULT, \ + "Auto BMPS Timer value") + +/* + * + * gBmpsMinListenInterval - Set BMPS Minimum Listen Interval + * @Min: 1 + * @Max: 65535 + * @Default: 1 + * + * This ini is used to set BMPS Minimum Listen Interval. If gPowerUsage + * is set "Min", this INI need to be set. + * + * Related: gEnableBmps, gPowerUsage + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_BMPS_MINIMUM_LI CFG_INI_UINT( \ + "gBmpsMinListenInterval", \ + 1, \ + 65535, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "BMPS Minimum Listen Interval") + +/* + * + * gBmpsMaxListenInterval - Set BMPS Maximum Listen Interval + * @Min: 1 + * @Max: 65535 + * @Default: 1 + * + * This ini is used to set BMPS Maximum Listen Interval. If gPowerUsage + * is set "Max", this INI need to be set. + * + * Related: gEnableBmps, gPowerUsage + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_BMPS_MAXIMUM_LI CFG_INI_UINT( \ + "gBmpsMaxListenInterval", \ + 1, \ + 65535, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "BMPS Maximum Listen Interval") + +/* + * + * gEnableDTIMSelectionDiversity - Enable/Disable chain + * selection optimization for one chain dtim + * @Min: 0 + * @Max: 30 + * @Default: 5 + * + * Usage: External + * + * + */ +#define CFG_DTIM_SELECTION_DIVERSITY CFG_INI_UINT( \ + "gEnableDTIMSelectionDiversity", \ + 0, \ + 30, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Chain selection diversity value") + +#define CFG_POWERSAVE_ALL \ + CFG(CFG_ENABLE_IMPS) \ + CFG(CFG_ENABLE_PS) \ + CFG(CFG_AUTO_BMPS_ENABLE_TIMER) \ + CFG(CFG_BMPS_MINIMUM_LI) \ + CFG(CFG_BMPS_MAXIMUM_LI) \ + CFG(CFG_DTIM_SELECTION_DIVERSITY) + +#endif /* __CFG_MLME_POWERSAVE_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_rates.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_rates.h new file mode 100644 index 0000000000000000000000000000000000000000..d3786f9a7cd1b20f89ec5146588e135cd4d15678 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_rates.h @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_RATES_H +#define __CFG_MLME_RATES_H + +#define CFG_SUPPORTED_RATES_11B_LEN 4 +#define CFG_SUPPORTED_RATES_11A_LEN 8 +#define CFG_OPERATIONAL_RATE_SET_LEN 12 +#define CFG_EXTENDED_OPERATIONAL_RATE_SET_LEN 8 +#define CFG_SUPPORTED_MCS_SET_LEN 16 +#define CFG_BASIC_MCS_SET_LEN 16 +#define CFG_CURRENT_MCS_SET_LEN 16 +#define CFG_MLME_RATE_MASK_LEN 4 + +/* + * + * gMaxHTMCSForTxData - max HT mcs for TX + * @Min: 0 + * @Max: 383 + * @Default: 0 + * + * This ini is used to configure the max HT mcs + * for tx data. + * + * Usage: External + * + * bits 0-15: max HT mcs + * bits 16-31: zero to disable, otherwise enable. + * + * + */ +#define CFG_MAX_HT_MCS_FOR_TX_DATA CFG_INI_UINT( \ + "gMaxHTMCSForTxData", \ + 0, \ + 0x17f, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Max HT Mcs for Tx Data") + +/* + * + * gDisableABGRateForTxData - disable abg rate for tx data + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to disable abg rate for tx data. + * + * Usage: External + * + * + */ +#define CFG_DISABLE_ABG_RATE_FOR_TX_DATA CFG_INI_BOOL( \ + "gDisableABGRateForTxData", \ + 0, \ + "Disable ABG RATE for TX Data") + +/* + * + * gSapMaxMCSForTxData - sap 11n max mcs + * @Min: 0 + * @Max: 383 + * @Default: 0 + * + * This ini configure SAP 11n max mcs + * + * Usage: External + * + * + */ +#define CFG_SAP_MAX_MCS_FOR_TX_DATA CFG_INI_UINT( \ + "gSapMaxMCSForTxData", \ + 0, \ + 383, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "SAP Max MCS for TX Data") + +/* + * + * disable_high_ht_mcs_2x2 - disable high mcs index for 2nd stream in 2.4G + * @Min: 0 + * @Max: 8 + * @Default: 0 + * + * This ini is used to disable high HT MCS index for 2.4G STA connection. + * It has been introduced to resolve IOT issue with one of the vendor. + * + * Note: This INI is not useful with 1x1 setting. If some platform supports + * only 1x1 then this INI is not useful. + * + * 0 - It won't disable any HT MCS index (just like normal HT MCS) + * 1 - It will disable 15th bit from HT RX MCS set (from 8-15 bits slot) + * 2 - It will disable 14th & 15th bits from HT RX MCS set + * 3 - It will disable 13th, 14th, & 15th bits from HT RX MCS set + * and so on. + * + * Related: STA + * + * Supported Feature: 11n + * + * Usage: External + */ +#define CFG_DISABLE_HIGH_HT_RX_MCS_2x2 CFG_INI_UINT( \ + "disable_high_ht_mcs_2x2", \ + 0, \ + 8, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Disable high MCS index for 2x2") + +#define CFG_CFP_PERIOD CFG_UINT( \ + "cfpPeriod", \ + 0, \ + 255, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "CFP Period") + +#define CFG_CFP_MAX_DURATION CFG_UINT( \ + "cfpMaxDuration", \ + 0, \ + 65535, \ + 30000, \ + CFG_VALUE_OR_DEFAULT, \ + "CFP Max Duration") +/* + * + * supported_rates_11b - supported rates for 11b + * @Min: 0 minimum length of supported rates + * @Max: default data length of supported rates in string format + * @Default: 2, 4, 11, 22 + */ +#define CFG_SUPPORTED_RATES_11B_DATA "2, 4, 11, 22" +#define CFG_SUPPORTED_RATES_11B CFG_STRING( \ + "supported_rates_11b", \ + 0, \ + sizeof(CFG_SUPPORTED_RATES_11B_DATA) - 1, \ + CFG_SUPPORTED_RATES_11B_DATA, \ + "Supported rates for 11B") + +/* + * + * supported_rates_11a - supported rates for 11a + * @Min: 0 minimum length of supported rates + * @Max: default data length of supported rates in string format + * @Default: 12, 18, 24, 36, 48, 72, 96, 108 + */ +#define CFG_SUPPORTED_RATES_11A_DATA "12, 18, 24, 36, 48, 72, 96, 108" +#define CFG_SUPPORTED_RATES_11A CFG_STRING( \ + "supported_rates_11a", \ + 0, \ + sizeof(CFG_SUPPORTED_RATES_11A_DATA) - 1, \ + CFG_SUPPORTED_RATES_11A_DATA, \ + "Supported rates for 11A") + +/* + * + * supported_mcs_set - supported MCS set data + * @Min: 0 minimum length of supported MCS set + * @Max: default data length of supported mcs set in string format + * @Default: 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + * 0x0, 0x0, 0x0 + */ +#define CFG_SUPPORTED_MCS_SET_DATA "0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0" +#define CFG_SUPPORTED_MCS_SET CFG_STRING( \ + "supported_mcs_set", \ + 0, \ + sizeof(CFG_SUPPORTED_MCS_SET_DATA) - 1, \ + CFG_SUPPORTED_MCS_SET_DATA, \ + "supported MCS set") + +/* + * + * basic_mcs_set - basic MCS set data + * @Min: 0 minimum length of basic MCS set + * @Max: default data length of basic mcs set in string format + * @Default: 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + * 0x0, 0x0, 0x0 + */ +#define CFG_BASIC_MCS_SET_DATA "0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0" +#define CFG_BASIC_MCS_SET CFG_STRING( \ + "basic_mcs_set", \ + 0, \ + sizeof(CFG_BASIC_MCS_SET_DATA) - 1, \ + CFG_BASIC_MCS_SET_DATA, \ + "basic MCS set") + +/* + * + * current_mcs_set - current MCS set data + * @Min: 0 minimum length of current MCS set + * @Max: default data length of current mcs set in string format + * @Default: 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + * 0x0, 0x0, 0x0 + */ +#define CFG_CURRENT_MCS_SET_DATA "0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0" +#define CFG_CURRENT_MCS_SET CFG_STRING( \ + "current_mcs_set", \ + 0, \ + sizeof(CFG_CURRENT_MCS_SET_DATA) - 1, \ + CFG_CURRENT_MCS_SET_DATA, \ + "current MCS set") + +/* + * + * ratemask_type - PHY type for the ratemask. + * @Min: 0 No rate mask set defined - disabled the configuration + * @Max: 4 + * @Default: 0 + * + * This ini is used to set the PHY type for ratemask in rate selection. + * + * 0 = Disables the configuration + * 1 = The rate mask specified is for CCK/OFDM configuration + * 2 = The rate mask specified is for HT configuration + * 3 = The rate mask specified is for VHT configuration + * 4 = The rate mask specified is for HE/11ax configuration + * + * Related: CFG_RATEMASK_SET + * + * Usage: External + */ +#define CFG_RATEMASK_TYPE CFG_INI_UINT( \ + "ratemask_type", \ + 0, \ + 4, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Ratemask type") + +/* + * + * ratemask_set - ratemasks for a PHY type used in rate selection + * @Min: default data length of ratemask in string format + * @Max: default data length of ratemask in string format + * @Default: 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, 0xFFFFFFFF + * + * This is used to set the rate mask value to be used in rate selection. + * Each of the four words must be configured. + * A bit value of 1 represents rate is enabled + * A bit value of 0 represents rate is disabled + * + * [b31-b0],[b63-b32],[b95-b64],[b127-b96] + * For HE targets, 12 bits correpond to one NSS setting. Ex: + * b0-13 => NSS1, MCS 0-13 + * b14-27 => NSS2, MCS 0-13 and so on for other NSS. + * Note that the bit representation is continuous. + * + * For VHT targets, 12 bits correspond to one NSS setting. + * b0-11 => NSS1, MCS 0-11 + * b12-23 => NSS2, MCS 0-11 and so on for other NSS. + * + * For HT targets, 8 bits correspond to one NSS setting. + * b0-7 => NSS1, MCS 0-7 + * b8-15 => NSS2, MCS 0-7 and so on for other NSS. + * + * For OFDM/CCK targets, 8 bits correspond to one NSS setting. + * Bit position |-b3-|-b2-|-b1-|-b0-| + * Rates in Mbps |-1 -|-2 -|-5.5|-11-| CCK Rates + * + * Bit position |-b11-|-b10-|-b09-|-b08-|-b07-|-b06-|-b05-|-b04-| + * Rates in Mbps |- 9 -|- 18-|-36 -|-54 -|- 6 -|-12 -| -24-|-48- | OFDM Rates + * + * Related: CFG_RATEMASK_TYPE + * + * Usage: External + */ +#define CFG_RATEMASK_DATA "0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF" +#define CFG_RATEMASK_SET CFG_INI_STRING( \ + "ratemask_set", \ + 0, \ + sizeof(CFG_RATEMASK_DATA) - 1, \ + CFG_RATEMASK_DATA, \ + "Ratemasks for rate selection") + +#define CFG_RATES_ALL \ + CFG(CFG_MAX_HT_MCS_FOR_TX_DATA) \ + CFG(CFG_DISABLE_ABG_RATE_FOR_TX_DATA) \ + CFG(CFG_SAP_MAX_MCS_FOR_TX_DATA) \ + CFG(CFG_DISABLE_HIGH_HT_RX_MCS_2x2) \ + CFG(CFG_CFP_PERIOD) \ + CFG(CFG_CFP_MAX_DURATION) \ + CFG(CFG_SUPPORTED_RATES_11B) \ + CFG(CFG_SUPPORTED_RATES_11A) \ + CFG(CFG_SUPPORTED_MCS_SET) \ + CFG(CFG_BASIC_MCS_SET) \ + CFG(CFG_CURRENT_MCS_SET) \ + CFG(CFG_RATEMASK_TYPE) \ + CFG(CFG_RATEMASK_SET) + +#endif /* __CFG_MLME_RATES_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_reg.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_reg.h new file mode 100644 index 0000000000000000000000000000000000000000..4da66ab7aa7f4ab1c0ebb6ed4025bee2c19fc359 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_reg.h @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains configuration definitions for MLME REG. + */ + +#ifndef CFG_MLME_REG_H__ +#define CFG_MLME_REG_H__ + +#define VALID_CHANNEL_LIST_DEFAULT "36, 40, 44, 48, 52, 56, 60, 64, 1, 6, 11, 34, 38, 42, 46, 2, 3, 4, 5, 7, 8, 9, 10, 12, 13, 14, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 149, 151, 153, 155, 157, 159, 161, 50, 54, 58, 62, 240, 242, 244, 246, 248, 250, 252" + +/* + * + * gSelfGenFrmPwr - self-generated frame power in tx chain mask + * for CCK rates + * @Min: 0 + * @Max: 0xffff + * @Default: 0 + * + * gSelfGenFrmPwr is to set self-generated frame power in tx chain mask + * for CCK rates + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_SELF_GEN_FRM_PWR CFG_INI_UINT( \ + "gSelfGenFrmPwr", \ + 0, \ + 0xffff, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "set the self gen power value") + +/* + * + * enable_11d_in_world_mode - enable 11d in world mode + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini enables 11d in world mode, irrespective of value of + * g11dSupportEnabled + * + * Usage: External + * + * + */ +#define CFG_ENABLE_11D_IN_WORLD_MODE CFG_INI_BOOL( \ + "enable_11d_in_world_mode", \ + 0, \ + "enable 11d in world mode") + +/* + * + * etsi_srd_chan_in_master_mode - Enable/disable ETSI SRD channels in + * master mode PCL and ACS functionality + * @Min: 0 + * @Max: 0xFF + * @Default: 6 + * + * etsi_srd_chan_in_master_mode is to enable/disable ETSI SRD channels in + * master mode PCL and ACS functionality + * Bit map for enabling the SRD mode in various modes are as follows:- + * BIT 0:- Enable/Disable SRD channels for SAP. + * BIT 1:- Enable/Disable SRD channels for P2P-GO. + * BIT 2:- Enable/Disable SRD channels for NAN. + * Rest of the bits are currently reserved for future SRD channel support for + * other vdevs. + * + * Related: None + * + * Supported Feature: SAP/P2P-GO + * + * Usage: Internal/External + * + * + */ +#define CFG_ETSI_SRD_CHAN_IN_MASTER_MODE CFG_INI_UINT( \ + "etsi13_srd_chan_in_master_mode", \ + 0, \ + 0xff, \ + 6, \ + CFG_VALUE_OR_DEFAULT, \ + "enable/disable ETSI SRD channels in master mode") + +/* + * + * enable_nan_indoor_channel - Enable Indoor channels for NAN + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to support to indoor channels for NAN interface + * Customer can config this item to enable/disable NAN in indoor channel + * + * Related: None + * + * Supported Feature: NAN + * + * Usage: External + * + * + */ +#define CFG_INDOOR_CHANNEL_SUPPORT_FOR_NAN CFG_INI_BOOL( \ + "enable_nan_indoor_channel", \ + 0, \ + "enable/disable indoor channels for NAN") + +#ifdef SAP_AVOID_ACS_FREQ_LIST +#define SAP_AVOID_ACS_FREQ_LIST_DEFAULT "" + +/* + * + * sap_avoid_acs_freq_list - Avoid configured frequencies from acs + * @Default: No frequencies are configured, it means consider all + * the frequencies for acs + * + * This ini is to configure the frequencies which needs to be + * avoided during acs and sap will not come up on these channels + * Ex: sap_avoid_acs_freq_list=2412,2417,2422,2427,2467,2472 + * + * Related: Feature flag SAP_AVOID_ACS_FREQ_LIST + * + * Supported Feature: SAP + * + * Usage: External + * + * + */ + +#define CFG_SAP_AVOID_ACS_FREQ_LIST CFG_INI_STRING( \ + "sap_avoid_acs_freq_list", \ + 0, \ + CFG_VALID_CHANNEL_LIST_STRING_LEN, \ + SAP_AVOID_ACS_FREQ_LIST_DEFAULT, \ + "Avoid configured frequencies during acs") +#define CFG_SAP_AVOID_ACS_FREQ_LIST_ALL CFG(CFG_SAP_AVOID_ACS_FREQ_LIST) +#else +#define CFG_SAP_AVOID_ACS_FREQ_LIST_ALL +#endif + +/* + * + * restart_beaconing_on_chan_avoid_event - control the beaconing entity to move + * away from active LTE channels + * @Min: 0 + * @Max: 2 + * @Default: 1 + * + * This ini is used to control the beaconing entity (SAP/GO) to move away from + * active LTE channels when channel avoidance event is received + * restart_beaconing_on_chan_avoid_event=0: Don't allow beaconing entity move + * from active LTE channels + * restart_beaconing_on_chan_avoid_event=1: Allow beaconing entity move from + * active LTE channels + * restart_beaconing_on_chan_avoid_event=2: Allow beaconing entity move from + * 2.4G active LTE channels only + * + * Related: None + * + * Supported Feature: channel avoidance + * + * Usage: Internal/External + * + * + */ +#define CFG_RESTART_BEACONING_ON_CH_AVOID CFG_INI_UINT( \ + "restart_beaconing_on_chan_avoid_event", \ + 0, \ + 2, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "control the beaconing entity to move away from active LTE channels") + +/* + * + * gindoor_channel_support - support to start sap in indoor channel + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is to support to start sap in indoor channel. + * Customer can config this item to enable/disable sap in indoor channel + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: External + * + * + */ +#define CFG_INDOOR_CHANNEL_SUPPORT CFG_INI_BOOL( \ + "gindoor_channel_support", \ + 0, \ + "enable/disable sap in indoor channel") + +/* + * + * scan_11d_interval - 11d scan interval in ms + * @Min: 1 sec + * @Max: 10 hr + * @Default: 1 hr + * + * This ini sets the 11d scan interval in FW + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ + +#define CFG_SCAN_11D_INTERVAL CFG_INI_UINT( \ + "scan_11d_interval", \ + 1000, \ + 36000000, \ + 3600000, \ + CFG_VALUE_OR_DEFAULT, \ + "set the 11d scan interval in FW") + + /* + * valid_chan_list - Configure valid channel list + * @Default: VALID_CHANNEL_LIST_DEFAULT + * + * This ini is used to configure valid channel list + * + * Usage: Internal + * + */ +#define CFG_VALID_CHANNEL_LIST CFG_STRING( \ + "valid_chan_list", \ + 0, \ + CFG_VALID_CHANNEL_LIST_STRING_LEN, \ + VALID_CHANNEL_LIST_DEFAULT, \ + "valid channel list") + + /* + * country_code - Set country code + * @Default: NA + * + * This ini is used to set country code + * + * Usage: Internal + * + */ +#define CFG_COUNTRY_CODE CFG_STRING( \ + "country_code", \ + 0, \ + CFG_COUNTRY_CODE_LEN, \ + "", \ + "country code") + +/* + * + * ignore_fw_reg_offload_ind - If set, Ignore the FW offload indication + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to ignore regdb offload indication from FW and + * regulatory will be treated as non offload. + * There is a case where FW is sending the offload indication in + * service ready event but not sending the cc list event + * WMI_REG_CHAN_LIST_CC_EVENTID and because of this driver is not + * able to populate the channel list. To address this issue, this ini + * is added. If this ini is enabled, regulatory will always be treated as + * non offload solution. + * + * This ini should only be enabled to circumvent the above mentioned firmware + * bug. + * + * Related: None + * + * Supported Feature: STA/AP + * + * Usage: External + * + * + */ +#define CFG_IGNORE_FW_REG_OFFLOAD_IND CFG_INI_BOOL( \ + "ignore_fw_reg_offload_ind", \ + 0, \ + "Ignore Regulatory offloads Indication from FW") + +/* + * + * enable_pending_list_req - Sets Pending channel List Req. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This option enables/disables SCAN_CHAN_LIST_CMDID channel list command to FW + * till the current scan is complete. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_ENABLE_PENDING_CHAN_LIST_REQ CFG_INI_BOOL( \ + "enable_pending_list_req", \ + 0, \ + "Enable Pending list req") + +/* + * + * retain_nol_across_regdmn - Retain NOL across reg domain + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set if NOL needs to be retained + * on the reg domain change. + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: External + * + * + */ +#define CFG_RETAIN_NOL_ACROSS_REG_DOMAIN CFG_INI_BOOL( \ + "retain_nol_across_regdmn", \ + 1, \ + "Retain NOL even if the regdomain changes") + +#define CFG_REG_ALL \ + CFG(CFG_SELF_GEN_FRM_PWR) \ + CFG(CFG_ENABLE_PENDING_CHAN_LIST_REQ) \ + CFG(CFG_ENABLE_11D_IN_WORLD_MODE) \ + CFG(CFG_ETSI_SRD_CHAN_IN_MASTER_MODE) \ + CFG(CFG_INDOOR_CHANNEL_SUPPORT_FOR_NAN) \ + CFG(CFG_RESTART_BEACONING_ON_CH_AVOID) \ + CFG(CFG_INDOOR_CHANNEL_SUPPORT) \ + CFG(CFG_SCAN_11D_INTERVAL) \ + CFG(CFG_VALID_CHANNEL_LIST) \ + CFG(CFG_COUNTRY_CODE) \ + CFG(CFG_IGNORE_FW_REG_OFFLOAD_IND) \ + CFG(CFG_RETAIN_NOL_ACROSS_REG_DOMAIN) \ + CFG_SAP_AVOID_ACS_FREQ_LIST_ALL + +#endif /* CFG_MLME_REG_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_sap.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_sap.h new file mode 100644 index 0000000000000000000000000000000000000000..4e0dc86e8e2388c9efc0407790f4f7d1cdad743f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_sap.h @@ -0,0 +1,805 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_SAP_H +#define __CFG_MLME_SAP_H + +#define STR_SSID_DEFAULT "1234567890" +#define STR_SSID_DEFAULT_LEN sizeof(STR_SSID_DEFAULT) + +#define CFG_SSID CFG_STRING( \ + "cfg_ssid", \ + 0, \ + STR_SSID_DEFAULT_LEN, \ + STR_SSID_DEFAULT, \ + "CFG_SSID") + +#define CFG_BEACON_INTERVAL CFG_INI_UINT( \ + "gBeaconInterval", \ + 0, \ + 65535, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "CFG_BEACON_INTERVAL") + +#define CFG_DTIM_PERIOD CFG_UINT( \ + "cfg_dtim_period", \ + 0, \ + 65535, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "CFG_DTIM_PERIOD") + +#define CFG_LISTEN_INTERVAL CFG_UINT( \ + "cfg_listen_interval", \ + 0, \ + 65535, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "CFG_LISTEN_INTERVAL") + +#define CFG_11G_ONLY_POLICY CFG_UINT( \ + "cfg_11g_only_policy", \ + 0, \ + 1, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "CFG_11G_ONLY_POLICY") + +#define CFG_ASSOC_STA_LIMIT CFG_UINT( \ + "cfg_assoc_sta_limit", \ + 1, \ + 32, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "CFG_ASSOC_STA_LIMIT") + +/* + * + * cfg_enable_lte_coex - enable LTE COEX + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable LTE COEX + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + #define CFG_ENABLE_LTE_COEX CFG_INI_BOOL( \ + "gEnableLTECoex", \ + 0, \ + "enabled lte coex") + +#define CFG_RMC_ACTION_PERIOD_FREQUENCY CFG_UINT( \ + "cfg_rcm_action_period_frequency", \ + 100, \ + 1000, \ + 300, \ + CFG_VALUE_OR_DEFAULT, \ + "CFG_RMC_ACTION_PERIOD_FREQUENCY") + +/* + * + * cfg_rate_for_tx_mgmt - Set rate for tx mgmt + * @Min: 0 + * @Max: 0xFF + * @Default: 0xFF + * + * This ini is used to set rate for tx mgmt + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + #define CFG_RATE_FOR_TX_MGMT CFG_INI_UINT( \ + "gRateForTxMgmt", \ + 0, \ + 0xFF, \ + 0xFF, \ + CFG_VALUE_OR_DEFAULT, \ + "set rate for mgmt tx") + +/* + * + * cfg_rate_for_tx_mgmt_2g - Set rate for tx mgmt 2g + * @Min: 0 + * @Max: 255 + * @Default: 255 + * + * This ini is used to set rate for tx mgmt 2g + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + #define CFG_RATE_FOR_TX_MGMT_2G CFG_INI_UINT( \ + "gRateForTxMgmt2G", \ + 0, \ + 255, \ + 255, \ + CFG_VALUE_OR_DEFAULT, \ + "set rate for mgmt tx 2g") + +/* + * + * cfg_rate_for_tx_mgmt_5g - Set rate for tx mgmt 5g + * @Min: 0 + * @Max: 255 + * @Default: 255 + * + * This ini is used to set rate for tx mgmt 5g + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + #define CFG_RATE_FOR_TX_MGMT_5G CFG_INI_UINT( \ + "gRateForTxMgmt5G", \ + 0, \ + 255, \ + 255, \ + CFG_VALUE_OR_DEFAULT, \ + "set rate for mgmt tx 5g") + +/* + * + * gTelescopicBeaconWakeupEn - Set teles copic beacon wakeup + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set default teles copic beacon wakeup + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_TELE_BCN_WAKEUP_EN CFG_INI_BOOL( \ + "gTelescopicBeaconWakeupEn", \ + 0, \ + "set tescopic beacon wakeup") + +/* + * + * telescopicBeaconMaxListenInterval - Set teles scopic beacon max listen value + * @Min: 0 + * @Max: 7 + * @Default: 5 + * + * This ini is used to set teles scopic beacon max listen interval value + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + #define CFG_TELE_BCN_MAX_LI CFG_INI_UINT( \ + "telescopicBeaconMaxListenInterval", \ + 0, \ + 7, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "set telescopic beacon max listen") + +/* + * + * gSapGetPeerInfo - Enable/Disable remote peer info query support + * @Min: 0 - Disable remote peer info query support + * @Max: 1 - Enable remote peer info query support + * @Default: 1 + * + * This ini is used to enable/disable remote peer info query support + * + * Usage: External + * + * + */ + #define CFG_SAP_GET_PEER_INFO CFG_INI_BOOL( \ + "gSapGetPeerInfo", \ + 1, \ + "sap get peer info") + +/* + * + * gSapAllowAllChannel - Sap allow all channels + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to allow all channels for SAP + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ + #define CFG_SAP_ALLOW_ALL_CHANNEL_PARAM CFG_INI_BOOL( \ + "gSapAllowAllChannel", \ + 0, \ + "sap allow all channel params") + +/* + * + * gSoftApMaxPeers - Set Max peers connected for SAP + * @Min: 1 + * @Max: 32 + * @Default: 10 + * + * This ini is used to set Max peers connected for SAP + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ + #define CFG_SAP_MAX_NO_PEERS CFG_INI_UINT( \ + "gSoftApMaxPeers", \ + 1, \ + 32, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "max no of peers") + +/* + * + * gMaxOffloadPeers - Set max offload peers + * @Min: 2 + * @Max: 5 + * @Default: 2 + * + * This ini is used to set default teles copic beacon wakeup + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + #define CFG_SAP_MAX_OFFLOAD_PEERS CFG_INI_UINT( \ + "gMaxOffloadPeers", \ + 2, \ + 5, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "max offload peers") + +/* + * + * gMaxOffloadReorderBuffs - Set max offload reorder buffs + * @Min: 0 + * @Max: 3 + * @Default: 2 + * + * This ini is used to set max offload reorder buffs + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + #define CFG_SAP_MAX_OFFLOAD_REORDER_BUFFS CFG_INI_UINT( \ + "gMaxOffloadReorderBuffs", \ + 0, \ + 3, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "sap max offload reorder buffs") + +/* + * + * g_sap_chanswitch_beacon_cnt - Set channel switch beacon count + * @Min: 1 + * @Max: 10 + * @Default: 10 + * + * This ini is used to set channel switch beacon count + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + #define CFG_SAP_CH_SWITCH_BEACON_CNT CFG_INI_UINT( \ + "g_sap_chanswitch_beacon_cnt", \ + 1, \ + 10, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "set channel switch beacon count") + +/* + * + * g_sap_chanswitch_mode - channel switch mode + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to configure the value of channel switch mode, which is + * contained in the Channel Switch Announcement(CSA) information element sent + * by an SAP. + * + * 0 - CSA receiving STA doesn't need to do anything + * 1 - CSA receiving STA shall not transmit any more frames on the channel + * until the scheduled channel switch occurs + * + * Related: none + * + * Supported Feature: SAP + * + * Usage: External + * + * + */ +#define CFG_SAP_CH_SWITCH_MODE CFG_INI_BOOL( \ + "g_sap_chanswitch_mode", \ + 1, \ + "sap channel switch mode") + +/* + * + * gEnableSapInternalRestart - Sap internal restart name + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used for sap internal restart name + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ + #define CFG_SAP_INTERNAL_RESTART CFG_INI_BOOL( \ + "gEnableSapInternalRestart", \ + 1, \ + "sap internal restart") + +/* + * + * gChanSwitchHostapdRateEnabled - Enable channale switch hostapd rate + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable channale switch hostapd rate + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ + #define CFG_CHAN_SWITCH_HOSTAPD_RATE_ENABLED_NAME CFG_INI_BOOL( \ + "gChanSwitchHostapdRateEnabled", \ + 0, \ + "chan switch hostapd rate enabled") + +/* + * + * gReducedBeaconInterval - beacon interval reduced + * @Min: 0 + * @Max: 100 + * @Default: 0 + * + * This ini is used to reduce beacon interval before channel + * switch (when val great than 0, or the feature is disabled). + * It would reduce the downtime on the STA side which is + * waiting for beacons from the AP to resume back transmission. + * Switch back the beacon_interval to its original value after + * channel switch based on the timeout. + * + * Related: none + * + * Usage: External + * + * + */ +#define CFG_REDUCED_BEACON_INTERVAL CFG_INI_UINT( \ + "gReducedBeaconInterval", \ + 0, \ + 100, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "reduced beacon interval") +/* + * + * gMaxLIModulatedDTIM - Set MaxLIModulate Dtim + * @Min: 1 + * @Max: 10 + * @Default: 10 + * + * This ini is used to set default MaxLIModulatedDTIM + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_MAX_LI_MODULATED_DTIM CFG_INI_UINT( \ + "gMaxLIModulatedDTIM", \ + 1, \ + 10, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Max modulated dtim") + +/* + * + * gCountryCodePriority - Priority to set country code + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set default gCountryCodePriority + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_COUNTRY_CODE_PRIORITY CFG_INI_BOOL( \ + "gCountryCodePriority", \ + 0, \ + "Country code priority") + +/* + * + * gSapPreferredChanLocation - Restrict channel switches between ondoor and + * outdoor. + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used for restricting channel switches between Indoor and outdoor + * channels after radar detection. + * 0- No preferred channel location + * 1- Use indoor channels only + * 2- Use outdoor channels only + * Related: NA. + * + * Supported Feature: DFS + * + * Usage: Internal/External + * + * + */ +#define CFG_SAP_PREF_CHANNEL_LOCATION CFG_INI_UINT( \ + "gSapPreferredChanLocation", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Sap preferred channel location") +/* + * + * gSapForce11NFor11AC - Restrict SAP to 11n if set 1 even though + * hostapd.conf request for 11ac. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Restrict SAP to 11n if set 1 even though hostapd.conf request for 11ac. + * + * 0- Do not force 11n for 11ac. + * 1- Force 11n for 11ac. + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_SAP_FORCE_11N_FOR_11AC CFG_INI_BOOL( \ + "gSapForce11NFor11AC", \ + 0, \ + "Sap force 11n for 11ac") + +/* + * + * gGoForce11NFor11AC - Restrict GO to 11n if set 1 even though + * hostapd.conf request for 11ac. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Restrict GO to 11n if set 1 even though hostapd.conf request for 11ac. + * + * 0- Do not force 11n for 11ac. + * 1- Force 11n for 11ac. + * + * Supported Feature: GO + * + * Usage: Internal/External + * + * + */ +#define CFG_GO_FORCE_11N_FOR_11AC CFG_INI_BOOL( \ + "gGoForce11NFor11AC", \ + 0, \ + "GO force 11n for 11ac") + + +/* + * + * gEnableApRandomBssid - Create ramdom BSSID + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to create a random BSSID in SoftAP mode to meet + * the Android requirement. + * + * Related: None. + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_AP_ENABLE_RANDOM_BSSID CFG_INI_BOOL( \ + "gEnableApRandomBssid", \ + 0, \ + "Create ramdom BSSID") + +/* + * + * gSapChannelAvoidance - SAP MCC channel avoidance. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to sets sap mcc channel avoidance. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_SAP_MCC_CHANNEL_AVOIDANCE CFG_INI_UINT( \ + "gSapChannelAvoidance", \ + 0, \ + 1, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "SAP MCC channel avoidance") + +/* + * + * gSAP11ACOverride - Override bw to 11ac for SAP in driver even if supplicant + * or hostapd configures HT. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable 11AC override for SAP. + * Android UI does not provide advanced configuration options + * for SoftAP for Android O and below. + * Default override disabled for android. Can be enabled from + * ini for Android O and below. + * + * + * Supported Feature: SAP + * + * + * Usage: Internal/External + * + * + */ +#define CFG_SAP_11AC_OVERRIDE CFG_INI_BOOL( \ + "gSAP11ACOverride", \ + 0, \ + "Override bw to 11ac for SAP") + +/* + * + * gGO11ACOverride - Override bw to 11ac for P2P GO + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable 11AC override for GO. + * P2P GO also follows start_bss and since P2P GO could not be + * configured to setup VHT channel width in wpa_supplicant, driver + * can override 11AC. + * + * + * Supported Feature: P2P + * + * + * Usage: Internal/External + * + * + */ +#define CFG_GO_11AC_OVERRIDE CFG_INI_BOOL( \ + "gGO11ACOverride", \ + 1, \ + "Override bw to 11ac for P2P GO") + +/* + * + * + * enable_bcast_deauth_for_sap - Enable/Disable broadcast deauth support + * in driver for SAP + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable broadcast deauth support in driver + * for sap mode. + * + * Related: None + * + * Supported Feature: SAP + * Usage: External + * + * + */ +#define CFG_IS_SAP_BCAST_DEAUTH_ENABLED CFG_INI_BOOL( \ + "enable_bcast_deauth_for_sap", \ + 0, \ + "Enable/Disable bcast deauth for SAP") + +#ifdef WLAN_FEATURE_SAE +/* + * + * + * enable_sae_for_sap - Enable/Disable SAE support in driver for SAP + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable SAE support in driver for SAP mode + * Driver will process/drop the SAE authentication frames based on this config. + * + * Related: None + * + * Supported Feature: SAE + * Usage: External + * + * + */ +#define CFG_IS_SAP_SAE_ENABLED CFG_INI_BOOL( \ + "enable_sae_for_sap", \ + 1, \ + "Enable/Disable SAE support for SAP") + +#define CFG_SAP_SAE CFG(CFG_IS_SAP_SAE_ENABLED) + +#else +#define CFG_SAP_SAE +#endif /* WLAN_FEATURE_SAE */ + +/* + * + * + * enable_sap_fils_discovery - Enable/Disable fils discovery for 6Ghz SAP + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Enable: 6Ghz SAP transmits fils discovery frame at every 20ms + * Disable: 6Ghz SAP transmits probe response frame at every 20ms + * + * Related: None + * + * Supported Feature: SAP + * Usage: External + * + * + */ +#define CFG_6G_SAP_FILS_DISCOVERY_ENABLED CFG_INI_BOOL( \ + "enable_6g_sap_fils_discovery", \ + 1, \ + "Enable/Disable fils discovery for SAP") + +#define CFG_SAP_ALL \ + CFG_SAP_SAE \ + CFG(CFG_AP_ENABLE_RANDOM_BSSID) \ + CFG(CFG_SSID) \ + CFG(CFG_BEACON_INTERVAL) \ + CFG(CFG_DTIM_PERIOD) \ + CFG(CFG_LISTEN_INTERVAL) \ + CFG(CFG_11G_ONLY_POLICY) \ + CFG(CFG_ASSOC_STA_LIMIT) \ + CFG(CFG_ENABLE_LTE_COEX) \ + CFG(CFG_RMC_ACTION_PERIOD_FREQUENCY) \ + CFG(CFG_RATE_FOR_TX_MGMT) \ + CFG(CFG_RATE_FOR_TX_MGMT_2G) \ + CFG(CFG_RATE_FOR_TX_MGMT_5G) \ + CFG(CFG_TELE_BCN_WAKEUP_EN) \ + CFG(CFG_TELE_BCN_MAX_LI) \ + CFG(CFG_SAP_MCC_CHANNEL_AVOIDANCE) \ + CFG(CFG_SAP_GET_PEER_INFO) \ + CFG(CFG_SAP_ALLOW_ALL_CHANNEL_PARAM) \ + CFG(CFG_SAP_MAX_NO_PEERS) \ + CFG(CFG_SAP_MAX_OFFLOAD_PEERS) \ + CFG(CFG_SAP_MAX_OFFLOAD_REORDER_BUFFS) \ + CFG(CFG_SAP_CH_SWITCH_BEACON_CNT) \ + CFG(CFG_SAP_CH_SWITCH_MODE) \ + CFG(CFG_SAP_INTERNAL_RESTART) \ + CFG(CFG_CHAN_SWITCH_HOSTAPD_RATE_ENABLED_NAME) \ + CFG(CFG_REDUCED_BEACON_INTERVAL) \ + CFG(CFG_MAX_LI_MODULATED_DTIM) \ + CFG(CFG_COUNTRY_CODE_PRIORITY) \ + CFG(CFG_SAP_PREF_CHANNEL_LOCATION) \ + CFG(CFG_SAP_FORCE_11N_FOR_11AC) \ + CFG(CFG_SAP_11AC_OVERRIDE) \ + CFG(CFG_GO_FORCE_11N_FOR_11AC) \ + CFG(CFG_GO_11AC_OVERRIDE) \ + CFG(CFG_IS_SAP_BCAST_DEAUTH_ENABLED) \ + CFG(CFG_6G_SAP_FILS_DISCOVERY_ENABLED) + +#endif /* __CFG_MLME_SAP_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_scoring.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_scoring.h new file mode 100644 index 0000000000000000000000000000000000000000..9744e1a2726f77836674232fa20c35ee81a0455a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_scoring.h @@ -0,0 +1,1361 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains MLME SCORING related CFG/INI Items. + */ + +#ifndef __CFG_MLME_SCORING_H +#define __CFG_MLME_SCORING_H + +/* + * + * rssi_weightage - RSSI Weightage to calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 20 + * + * This ini is used to increase/decrease RSSI weightage in best candidate + * selection. AP with better RSSI will get more weightage. + * + * Related: None + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_RSSI_WEIGHTAGE CFG_INI_UINT( \ + "rssi_weightage", \ + 0, \ + 100, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI Weightage") + +/* + * + * ht_caps_weightage - HT caps weightage to calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 2 + * + * This ini is used to increase/decrease HT caps weightage in best candidate + * selection. If AP supports HT caps, AP will get additional Weightage with + * this param. Weightage will be given only if dot11mode is HT capable. + * + * Related: None + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_HT_CAPS_WEIGHTAGE CFG_INI_UINT( \ + "ht_caps_weightage", \ + 0, \ + 100, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "HT Caps Weightage") + +/* + * + * vht_caps_weightage - VHT caps Weightage to calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 1 + * + * This ini is used to increase/decrease VHT caps weightage in best candidate + * selection. If AP supports VHT caps, AP will get additional weightage with + * this param. Weightage will be given only if dot11mode is VHT capable. + * + * Related: None + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_VHT_CAPS_WEIGHTAGE CFG_INI_UINT( \ + "vht_caps_weightage", \ + 0, \ + 100, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "HT Caps Weightage") + +/* + * + * he_caps_weightage - HE caps Weightage to calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 2 + * + * This ini is used to increase/decrease HE caps weightage in best candidate + * selection. If AP supports HE caps, AP will get additional weightage with + * this param. Weightage will be given only if dot11mode is HE capable. + * + * Related: None + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_HE_CAPS_WEIGHTAGE CFG_INI_UINT( \ + "he_caps_weightage", \ + 0, \ + 100, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "HE Caps Weightage") + +/* + * + * chan_width_weightage - Channel Width Weightage to calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 12 + * + * This ini is used to increase/decrease Channel Width weightage in best + * candidate selection. AP with Higher channel width will get higher weightage + * + * Related: bandwidth_weight_per_index + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_CHAN_WIDTH_WEIGHTAGE CFG_INI_UINT( \ + "chan_width_weightage", \ + 0, \ + 100, \ + 12, \ + CFG_VALUE_OR_DEFAULT, \ + "Channel width weightage") + +/* + * + * chan_band_weightage - Channel Band perferance to 5GHZ to + * calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 2 + * + * This ini is used to increase/decrease Channel Band Preference weightage + * in best candidate selection. 5GHZ AP get this additional boost compare to + * 2GHZ AP before rssi_pref_5g_rssi_thresh and 2.4Ghz get weightage after + * rssi_pref_5g_rssi_thresh. + * + * Related: rssi_pref_5g_rssi_thresh, band_weight_per_index + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_CHAN_BAND_WEIGHTAGE CFG_INI_UINT( \ + "chan_band_weightage", \ + 0, \ + 100, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "Channel Band Weightage") + +/* + * + * nss_weightage - NSS Weightage to calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 16 + * + * This ini is used to increase/decrease NSS weightage in best candidate + * selection. If there are two AP, one AP supports 2x2 and another one supports + * 1x1 and station supports 2X2, first A will get this additional weightage + * depending on self-capability. + * + * Related: nss_weight_per_index + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_NSS_WEIGHTAGE CFG_INI_UINT( \ + "nss_weightage", \ + 0, \ + 100, \ + 16, \ + CFG_VALUE_OR_DEFAULT, \ + "NSS Weightage") +/* + * + * beamforming_cap_weightage - Beam Forming Weightage to + * calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 2 + * + * This ini is used to increase/decrease Beam forming Weightage if some AP + * support Beam forming or not. If AP supports Beam forming, that AP will get + * additional boost of this weightage. + * + * Related: None + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_BEAMFORM_CAP_WEIGHTAGE CFG_INI_UINT( \ + "beamforming_cap_weightage", \ + 0, \ + 100, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "Beamforming Cap Weightage") + +/* + * + * pcl_weightage - PCL Weightage to calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 10 + * + * This ini is used to increase/decrease PCL weightage in best candidate + * selection. If some APs are in PCL list, those AP will get addition + * weightage. + * + * Related: None + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_PCL_WEIGHTAGE CFG_INI_UINT( \ + "pcl_weightage", \ + 0, \ + 100, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "PCL Weightage") + +/* + * + * channel_congestion_weightage - channel Congestion Weightage to + * calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 25 + * + * This ini is used to increase/decrease channel congestion weightage in + * candidate selection. Congestion is measured with the help of ESP/QBSS load. + * + * Related: num_esp_qbss_slots + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_CHAN_CONGESTION_WEIGHTAGE CFG_INI_UINT( \ + "channel_congestion_weightage", \ + 0, \ + 100, \ + 25, \ + CFG_VALUE_OR_DEFAULT, \ + "Channel Congestion Weightage") + +/* + * + * oce_wan_weightage - OCE WAN DL capacity Weightage to calculate best candidate + * @Min: 0 + * @Max: 100 + * @Default: 2 + * + * This ini is used to increase/decrease OCE WAN caps weightage in best + * candidate selection. If AP have OCE WAN information, give weightage depending + * on the downaload available capacity. + * + * Related: num_oce_wan_slots + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_OCE_WAN_WEIGHTAGE CFG_INI_UINT( \ + "oce_wan_weightage", \ + 0, \ + 100, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "OCE WAN Weightage") + +/* + * + * sae_pk_ap_weightage - update scoring param based on SAE PK ap weightage + * @Min: 0 + * @Max: 10 + * @Default: 3 + * + * This ini is used to calculate SAE PK ap weightage in roam score. SAE Public + * Key (SAE-PK) authentication is an extension of SAE that is intended for use + * cases where authentication is based on a password that might be + * distributed to or obtained by a potential adversary. With SAE-PK, the AP in + * an infrastructure network is additionally authenticated based on a static + * public/private key pair. This ini is also used for WFA certification. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_SAE_PK_AP_WEIGHTAGE CFG_INI_UINT( \ + "sae_pk_ap_weightage", \ + 0, \ + 10, \ + PLATFORM_VALUE(3, 0), \ + CFG_VALUE_OR_DEFAULT,\ + "SAE-PK AP weightage") + +/* + * + * best_rssi_threshold - Best Rssi for score calculation + * @Min: 0 + * @Max: 96 + * @Default: 55 + * + * This ini tells limit for best RSSI. RSSI better than this limit are + * considered as best RSSI. The best RSSI is given full rssi_weightage. + * + * Related: rssi_weightage + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_BEST_RSSI_THRESHOLD CFG_INI_UINT( \ + "best_rssi_threshold", \ + 0, \ + 96, \ + 55, \ + CFG_VALUE_OR_DEFAULT, \ + "Best RSSI threshold") + +/* + * + * good_rssi_threshold - Good Rssi for score calculation + * @Min: 0 + * @Max: 96 + * @Default: 70 + * + * This ini tells limit for good RSSI. RSSI better than this limit and less + * than best_rssi_threshold is considered as good RSSI. + * + * Related: rssi_weightage, best_rssi_threshold + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_GOOD_RSSI_THRESHOLD CFG_INI_UINT( \ + "good_rssi_threshold", \ + 0, \ + 96, \ + 70, \ + CFG_VALUE_OR_DEFAULT, \ + "Good RSSI threshold") + +/* + * + * bad_rssi_threshold - Bad Rssi for score calculation + * @Min: 0 + * @Max: 96 + * @Default: 80 + * + * This ini tells limit for Bad RSSI. RSSI greater then bad_rssi_threshold + * is considered as bad RSSI. + * + * Related: rssi_weightage, good_rssi_threshold + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_BAD_RSSI_THRESHOLD CFG_INI_UINT( \ + "bad_rssi_threshold", \ + 0, \ + 96, \ + 80, \ + CFG_VALUE_OR_DEFAULT, \ + "Bad RSSI threshold") + +/* + * + * good_rssi_pcnt - Percent Score to Good RSSI out of total RSSI score. + * @Min: 0 + * @Max: 100 + * @Default: 80 + * + * This ini tells about how much percent should be given to good RSSI(RSSI + * between best_rssi_threshold and good_rssi_threshold) out of RSSI weightage. + * + * Related: rssi_weightage, best_rssi_threshold, good_rssi_threshold + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_GOOD_RSSI_PERCENT CFG_INI_UINT( \ + "good_rssi_pcnt", \ + 0, \ + 100, \ + 80, \ + CFG_VALUE_OR_DEFAULT, \ + "Good RSSI Percent") + +/* + * + * bad_rssi_pcnt - Percent Score to BAD RSSI out of total RSSI score. + * @Min: 0 + * @Max: 100 + * @Default: 25 + * + * This ini tells about how much percent should be given to bad RSSI (RSSI + * between good_rssi_threshold and bad_rssi_threshold) out of RSSI weightage. + * + * Related: rssi_weightage, good_rssi_threshold, bad_rssi_threshold + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_BAD_RSSI_PERCENT CFG_INI_UINT( \ + "bad_rssi_pcnt", \ + 0, \ + 100, \ + 25, \ + CFG_VALUE_OR_DEFAULT, \ + "Bad RSSI Percent") + +/* + * + * good_rssi_bucket_size - Bucket size between best and good RSSI to score. + * @Min: 1 + * @Max: 10 + * @Default: 5 + * + * This ini tells about bucket size for scoring between best and good RSSI. + * Below Best RSSI, 100% score will be given. Between best and good RSSI, RSSI + * is divided in buckets and score will be assigned bucket wise starting from + * good_rssi_pcnt. + * + * Related: rssi_weightage, good_rssi_pcnt + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_GOOD_RSSI_BUCKET_SIZE CFG_INI_UINT( \ + "good_rssi_bucket_size", \ + 1, \ + 10, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Good RSSI Bucket Size") + +/* + * + * bad_rssi_bucket_size - Bucket size between good and bad RSSI to score. + * @Min: 1 + * @Max: 10 + * @Default: 5 + * + * This ini tells about bucket size for scoring between good and bad RSSI. + * Between good and bad RSSI, RSSI is divided in buckets and score will be + * assigned bucket wise starting from bad_rssi_pcnt. + * + * Related: rssi_weightage, bad_rssi_pcnt + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_BAD_RSSI_BUCKET_SIZE CFG_INI_UINT( \ + "bad_rssi_bucket_size", \ + 1, \ + 10, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Bad RSSI Bucket Size") + +/* + * + * rssi_pref_5g_rssi_thresh - A RSSI threshold above which 5 GHz is not favored + * @Min: 0 + * @Max: 96 + * @Default: 76 + * + * 5G AP are given chan_band_weightage. This ini tells about RSSI threshold + * above which 5GHZ is not favored. + * + * Related: chan_band_weightage + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_RSSI_PREF_5G_THRESHOLD CFG_INI_UINT( \ + "rssi_pref_5g_rssi_thresh", \ + 0, \ + 96, \ + 76, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI Pref 5G Threshold") + +/* + * + * bandwidth_weight_per_index - percentage as per bandwidth + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x6432190C + * + * This INI give percentage value of chan_width_weightage to be used as per + * peer bandwidth. Self BW is also considered while calculating score. Eg if + * self BW is 20 MHZ 10% will be given for all AP irrespective of the AP + * capability. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): 20 MHz - Def 12% + * 1 Index (BITS 8-15): 40 MHz - Def 25% + * 2 Index (BITS 16-23): 80 MHz - Def 50% + * 3 Index (BITS 24-31): 160 MHz - Def 100% + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: chan_width_weightage + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_BW_WEIGHT_PER_IDX CFG_INI_UINT( \ + "bandwidth_weight_per_index", \ + 0x00000000, \ + 0x64646464, \ + 0x6432190C, \ + CFG_VALUE_OR_DEFAULT, \ + "Bandwidth weight per index") + +/* + * + * nss_weight_per_index - percentage as per NSS + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x6432190C + * + * This INI give percentage value of nss_weightage to be used as per peer NSS. + * Self NSS capability is also considered. Eg if self NSS is 1x1 10% will be + * given for all AP irrespective of the AP capability. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): 1X1- Def 12% + * 1 Index (BITS 8-15): 2X2- Def 25% + * 2 Index (BITS 16-23): 3X3- Def 50% + * 3 Index (BITS 24-31): 4X4- Def 100% + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: nss_weightage + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_NSS_WEIGHT_PER_IDX CFG_INI_UINT( \ + "nss_weight_per_index", \ + 0x00000000, \ + 0x64646464, \ + 0x6432190C, \ + CFG_VALUE_OR_DEFAULT, \ + "NSS weight per index") + +/* + * + * band_weight_per_index - percentage as per band + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x0000644B + * + * This INI give percentage value of chan_band_weightage to be used as per band. + * If RSSI is greater than rssi_pref_5g_rssi_thresh preference is given for 5Ghz + * else, it's given for 2.4Ghz. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): 2.4GHz - Def 10% + * 1 Index (BITS 8-15): 5GHz - Def 20% + * 2 Index (BITS 16-23): Reserved + * 3 Index (BITS 24-31): Reserved + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: chan_band_weightage, rssi_pref_5g_rssi_thresh + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_BAND_WEIGHT_PER_IDX CFG_INI_UINT( \ + "band_weight_per_index", \ + 0x00000000, \ + 0x64646464, \ + 0x0000644B, \ + CFG_VALUE_OR_DEFAULT, \ + "Band weight per index") + +/* + * + * num_esp_qbss_slots - number of slots in which the esp/qbss load will + * be divided + * + * @Min: 1 + * @Max: 15 + * @Default: 4 + * + * Number of slots in which the esp/qbss load will be divided. Max 15. index 0 + * is used for 'not_present. Num_slot will equally divide 100. e.g, if + * num_slot = 4 slot 1 = 0-25% load, slot 2 = 26-50% load, slot 3 = 51-75% load, + * slot 4 = 76-100% load. Remaining unused index can be 0. + * + * Usage: External + * + * + */ +#define CFG_SCORING_NUM_ESP_QBSS_SLOTS CFG_INI_UINT( \ + "num_esp_qbss_slots", \ + 1, \ + 15, \ + 4, \ + CFG_VALUE_OR_DEFAULT, \ + "Num ESP QPSS Slots") + +/* + * + * esp_qbss_score_idx3_to_0 - percentage for esp/qbss load for slots 0-3 + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x19326432 + * + * This INI give percentage value of channel_congestion_weightage to be used as + * index in which the load value falls. Index 0 is for percentage when ESP/QBSS + * is not present. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): when ESP/QBSS is not present + * 1 Index (BITS 8-15): SLOT_1 + * 2 Index (BITS 16-23): SLOT_2 + * 3 Index (BITS 24-31): SLOT_3 + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: channel_congestion_weightage, num_esp_qbss_slots + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_ESP_QBSS_SCORE_IDX_3_TO_0 CFG_INI_UINT( \ + "esp_qbss_score_idx3_to_0", \ + 0x00000000, \ + 0x64646464, \ + 0x19326432, \ + CFG_VALUE_OR_DEFAULT, \ + "ESP QPSS Score Index 3 to 0") + +/* + * + * esp_qbss_score_idx7_to_4 - percentage for esp/qbss load for slots 4-7 + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x0000000A + * + * This INI give percentage value of channel_congestion_weightage to be used as + * index in which the load value falls. Used only if num_esp_qbss_slots is + * greater than 3. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): SLOT_4 + * 1 Index (BITS 8-15): SLOT_5 + * 2 Index (BITS 16-23): SLOT_6 + * 3 Index (BITS 24-31): SLOT_7 + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: channel_congestion_weightage, num_esp_qbss_slots + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_ESP_QBSS_SCORE_IDX_7_TO_4 CFG_INI_UINT( \ + "esp_qbss_score_idx7_to_4", \ + 0x00000000, \ + 0x64646464, \ + 0x0000000A, \ + CFG_VALUE_OR_DEFAULT, \ + "ESP QPSS Score Index 7 to 4") + +/* + * + * esp_qbss_score_idx11_to_8 - percentage for esp/qbss load for slots 8-11 + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x00000000 + * + * This INI give percentage value of channel_congestion_weightage to be used as + * index in which the load value falls. Used only if num_esp_qbss_slots is + * greater than 7. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): SLOT_8 + * 1 Index (BITS 8-15): SLOT_9 + * 2 Index (BITS 16-23): SLOT_10 + * 3 Index (BITS 24-31): SLOT_11 + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: channel_congestion_weightage, num_esp_qbss_slots + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_ESP_QBSS_SCORE_IDX_11_TO_8 CFG_INI_UINT( \ + "esp_qbss_score_idx11_to_8", \ + 0x00000000, \ + 0x64646464, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "ESP QPSS Score Index 11 to 8") + +/* + * + * esp_qbss_score_idx15_to_12 - percentage for esp/qbss load for slots 12-15 + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x00000000 + * + * This INI give percentage value of channel_congestion_weightage to be used as + * index in which the load value falls. Used only if num_esp_qbss_slots is + * greater than 11. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): SLOT_12 + * 1 Index (BITS 8-15): SLOT_13 + * 2 Index (BITS 16-23): SLOT_14 + * 3 Index (BITS 24-31): SLOT_15 + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: channel_congestion_weightage, num_esp_qbss_slots + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_ESP_QBSS_SCORE_IDX_15_TO_12 CFG_INI_UINT( \ + "esp_qbss_score_idx15_to_12", \ + 0x00000000, \ + 0x64646464, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "ESP QPSS Score Index 15 to 12") + +/* + * + * num_oce_wan_slots - number of slots in which the oce wan metrics will + * be divided + * + * @Min: 1 + * @Max: 15 + * @Default: 8 + * + * Number of slots in which the oce wan metrics will be divided. Max 15. index 0 + * is used for not_present. Num_slot will equally divide 100. e.g, if + * num_slot = 4 slot 1 = 0-3 DL CAP, slot 2 = 4-7 DL CAP, slot 3 = 8-11 DL CAP, + * slot 4 = 12-15 DL CAP. Remaining unused index can be 0. + * + * Related: oce_wan_weightage + * + * Usage: External + * + * + */ +#define CFG_SCORING_NUM_OCE_WAN_SLOTS CFG_INI_UINT( \ + "num_oce_wan_slots", \ + 1, \ + 15, \ + 15, \ + CFG_VALUE_OR_DEFAULT, \ + "Num OCE WAN Slots") + +/* + * + * oce_wan_score_idx3_to_0 - percentage for OCE WAN metrics score for slots 0-3 + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x00000032 + * + * This INI give percentage value of OCE WAN metrics DL CAP, to be used as + * index in which the DL CAP value falls. Index 0 is for percentage when + * OCE WAN metrics DL CAP is not present. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): when OCE WAN metrics DL CAP is not present + * 1 Index (BITS 8-15): SLOT_1 + * 2 Index (BITS 16-23): SLOT_2 + * 3 Index (BITS 24-31): SLOT_3 + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: num_oce_wan_slots, oce_wan_weightage + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_OCE_WAN_SCORE_IDX_3_TO_0 CFG_INI_UINT( \ + "oce_wan_score_idx3_to_0", \ + 0x00000000, \ + 0x64646464, \ + 0x00000032, \ + CFG_VALUE_OR_DEFAULT, \ + "OCE WAN Score Index 3 to 0") + +/* + * + * oce_wan_score_idx7_to_4 - percentage for OCE WAN metrics score for slots 4-7 + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x00000000 + * + * This INI give percentage value of OCE WAN metrics DL CAP, to be used as + * index in which the DL CAP value falls. Used only if num_oce_wan_slots is + * greater than 3. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): SLOT_4 + * 1 Index (BITS 8-15): SLOT_5 + * 2 Index (BITS 16-23): SLOT_6 + * 3 Index (BITS 24-31): SLOT_7 + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: num_oce_wan_slots, oce_wan_weightage + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_OCE_WAN_SCORE_IDX_7_TO_4 CFG_INI_UINT( \ + "oce_wan_score_idx7_to_4", \ + 0x00000000, \ + 0x64646464, \ + 0x00000000, \ + CFG_VALUE_OR_DEFAULT, \ + "OCE WAN Score Index 7 to 4") + +/* + * + * oce_wan_score_idx11_to_8 - percentage for OCE WAN metrics score for slot 8-11 + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x06030000 + * + * This INI give percentage value of OCE WAN metrics DL CAP, to be used as + * index in which the DL CAP value falls. Used only if num_oce_wan_slots is + * greater than 7. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): SLOT_8 + * 1 Index (BITS 8-15): SLOT_9 + * 2 Index (BITS 16-23): SLOT_10 + * 3 Index (BITS 24-31): SLOT_11 + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: num_oce_wan_slots, oce_wan_weightage + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_OCE_WAN_SCORE_IDX_11_TO_8 CFG_INI_UINT( \ + "oce_wan_score_idx11_to_8", \ + 0x00000000, \ + 0x64646464, \ + 0x06030000, \ + CFG_VALUE_OR_DEFAULT, \ + "OCE WAN Score Index 11 to 8") + +/* + * + * oce_wan_score_idx15_to_12 - % for OCE WAN metrics score for slot 12-15 + * @Min: 0x00000000 + * @Max: 0x64646464 + * @Default: 0x6432190C + * + * This INI give percentage value of OCE WAN metrics DL CAP, to be used as + * index in which the DL CAP value falls. Used only if num_oce_wan_slots is + * greater than 11. + * + * Indexes are defined in this way. + * 0 Index (BITS 0-7): SLOT_12 + * 1 Index (BITS 8-15): SLOT_13 + * 2 Index (BITS 16-23): SLOT_14 + * 3 Index (BITS 24-31): SLOT_15 + * These percentage values are stored in HEX. For any index max value, can be 64 + * + * Related: num_oce_wan_slots, oce_wan_weightage + * + * Supported Feature: STA Candidate selection + * + * Usage: External + * + * + */ +#define CFG_SCORING_OCE_WAN_SCORE_IDX_15_TO_12 CFG_INI_UINT( \ + "oce_wan_score_idx15_to_12", \ + 0x00000000, \ + 0x64646464, \ + 0x6432190C, \ + CFG_VALUE_OR_DEFAULT, \ + "OCE WAN Score Index 15 to 12") + +/* + * + * roam_score_delta_bitmap - bitmap to enable roam triggers on + * which roam score delta is to be applied during roam candidate + * selection + * @Min: 0 + * @Max: 0xffffffff + * @Default: 0xffffffff + * + * Bitmap value of the following roam triggers: + * ROAM_TRIGGER_REASON_NONE - B0, + * ROAM_TRIGGER_REASON_PER - B1, + * ROAM_TRIGGER_REASON_BMISS - B2, + * ROAM_TRIGGER_REASON_LOW_RSSI - B3, + * ROAM_TRIGGER_REASON_HIGH_RSSI - B4, + * ROAM_TRIGGER_REASON_PERIODIC - B5, + * ROAM_TRIGGER_REASON_MAWC - B6, + * ROAM_TRIGGER_REASON_DENSE - B7, + * ROAM_TRIGGER_REASON_BACKGROUND - B8, + * ROAM_TRIGGER_REASON_FORCED - B9, + * ROAM_TRIGGER_REASON_BTM - B10, + * ROAM_TRIGGER_REASON_UNIT_TEST - B11, + * ROAM_TRIGGER_REASON_BSS_LOAD - B12 + * ROAM_TRIGGER_REASON_DISASSOC - B13 + * ROAM_TRIGGER_REASON_IDLE_ROAM - B14 + * + * When the bit corresponding to a particular roam trigger reason + * is set, the value of "roam_score_delta" is expected over the + * roam score of the current connected AP, for that triggered roam + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_ROAM_SCORE_DELTA_TRIGGER_BITMAP CFG_INI_UINT( \ + "roam_score_delta_bitmap", \ + 0, \ + 0xFFFFFFFF, \ + 0xFFFFFFFF, \ + CFG_VALUE_OR_DEFAULT, \ + "Bitmap for various roam triggers") + +/* + * + * roam_score_delta - Percentage increment in roam score value + * that is expected from a roaming candidate AP. + * @Min: 0 + * @Max: 100 + * @Default: 0 + * + * This ini is used to provide the percentage increment value over roam + * score for the candidate APs so that they can be preferred over current + * AP for roaming. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_ROAM_SCORE_DELTA CFG_INI_UINT( \ + "roam_score_delta", \ + 0, \ + 100, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "candidate AP's percentage roam score delta") + +/* + * + * min_roam_score_delta - Difference of roam score values between connected + * AP and roam candidate AP. + * @Min: 0 + * @Max: 10000 + * @Default: 0 + * + * This ini is used during CU and low rssi based roam triggers, consider + * AP as roam candidate only if its roam score is better than connected + * AP score by at least min_roam_score_delta. + * If user configured "roam_score_delta" and "min_roam_score_delta" both, + * then firmware selects roam candidate AP by considering values of both + * INIs. + * Example: If DUT is connected with AP1 and roam candidate AP2 has roam + * score greater than roam_score_delta and min_roam_score_delta then only + * firmware will trigger roaming to AP2. + * + * Related: roam_score_delta + * + * Supported Feature: Roaming + * + * Usage: Internal + * + * + */ +#define CFG_CAND_MIN_ROAM_SCORE_DELTA CFG_INI_UINT( \ + "min_roam_score_delta", \ + 0, \ + 10000, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Diff between connected AP's and candidate AP's roam score") + +/* + * + * enable_scoring_for_roam - enable/disable scoring logic in FW for candidate + * selection during roaming + * + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable scoring logic in FW for candidate + * selection during roaming. + * + * Supported Feature: STA Candidate selection by FW during roaming based on + * scoring logic. + * + * Usage: External + * + * + */ +#define CFG_ENABLE_SCORING_FOR_ROAM CFG_INI_BOOL( \ + "enable_scoring_for_roam", \ + 1, \ + "Enable Scoring for Roam") + +/* + * + * apsd_enabled - Enable automatic power save delivery + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Supported Feature: Power save + * + * Usage: Internal + * + * + */ +#define CFG_APSD_ENABLED CFG_BOOL( \ + "apsd_enabled", \ + 0, \ + "Enable APSD") + +/* + * + * candidate_min_rssi_for_disconnect - Candidate AP minimum RSSI in + * idle roam trigger(in dBm). + * @Min: -120 + * @Max: 0 + * @Default: -70 + * + * Minimum RSSI value of the candidate AP to consider it as candidate for + * roaming when roam trigger is Deauthentication/Disconnection from current + * AP. This value will be sent to firmware over the WMI_ROAM_AP_PROFILE + * wmi command in the roam_min_rssi_param_list tlv. + * + * Related: enable_idle_roam. + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_DISCONNECT_ROAM_TRIGGER_MIN_RSSI CFG_INI_INT( \ + "candidate_min_rssi_for_disconnect", \ + -120, \ + 0, \ + -70, \ + CFG_VALUE_OR_DEFAULT, \ + "Minimum RSSI of candidate AP for Disconnect roam trigger") + +/* + * + * candidate_min_rssi_for_beacon_miss - Candidate AP minimum RSSI for beacon + * miss roam trigger (in dBm) + * @Min: -120 + * @Max: 0 + * @Default: -70 + * + * Minimum RSSI value of the candidate AP to consider it as candidate for + * roaming when roam trigger is disconnection from current AP due to beacon + * miss. This value will be sent to firmware over the WMI_ROAM_AP_PROFILE + * wmi command in the roam_min_rssi_param_list tlv. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_BMISS_ROAM_MIN_RSSI CFG_INI_INT( \ + "candidate_min_rssi_for_beacon_miss", \ + -120, \ + 0, \ + -70, \ + CFG_VALUE_OR_DEFAULT, \ + "Minimum RSSI of candidate AP for Bmiss roam trigger") + +/* + * + * min_rssi_for_2g_to_5g_roam - Candidate AP minimum RSSI for + * 2G to 5G roam trigger (in dBm) + * @Min: -120 + * @Max: 0 + * @Default: -70 + * + * Minimum RSSI value of the candidate AP to consider it as candidate + * for 2G to 5G roam. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: Internal/External + * + * + */ +#define CFG_2G_TO_5G_ROAM_MIN_RSSI CFG_INI_INT( \ + "min_rssi_for_2g_to_5g_roam", \ + -120, \ + 0, \ + -70, \ + CFG_VALUE_OR_DEFAULT, \ + "Minimum RSSI of candidate AP for 2G to 5G roam trigger") + +/* + * + * idle_roam_score_delta - Roam score delta value in percentage for idle roam. + * @Min: 0 + * @Max: 100 + * @Default: 0 + * + * This ini is used to configure the minimum change in roam score + * value of the AP to consider it as candidate for + * roaming when roam trigger is due to idle state of sta. + * This value will be sent to firmware over the WMI_ROAM_AP_PROFILE wmi + * command in the roam_score_delta_param_list tlv. + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_IDLE_ROAM_SCORE_DELTA CFG_INI_UINT( \ + "idle_roam_score_delta", \ + 0, \ + 100, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Roam score delta for Idle roam trigger") + +/* + * + * btm_roam_score_delta - Roam score delta value in percentage for BTM triggered + * roaming. + * @Min: 0 + * @Max: 100 + * @Default: 0 + * + * This ini is used to configure the minimum change in roam score + * value of the AP to consider it as candidate when the sta is disconnected + * from the current AP due to BTM kickout. + * This value will be sent to firmware over the WMI_ROAM_AP_PROFILE wmi + * command in the roam_score_delta_param_list tlv. + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_BTM_ROAM_SCORE_DELTA CFG_INI_UINT( \ + "btm_roam_score_delta", \ + 0, \ + 100, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Roam score delta for BTM roam trigger") + +/* + * + * vendor_roam_score_algorithm - Algorithm to calculate AP score + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * By default the value is 0 and default roam algorithm will be used. + * When the value is 1, the V2 roaming algorithm will be used: + * For this V2 algo, AP score calculation is based on below equation: + * AP Score = (RSSIfactor * rssiweight(0.65)) + (CUfactor *cuweight(0.35)) + * + * Related: None + * + * Supported Feature: roam score algorithm + * + * Usage: External + * + * + */ +#define CFG_VENDOR_ROAM_SCORE_ALGORITHM CFG_INI_UINT( \ + "vendor_roam_score_algorithm", \ + 0, \ + 1, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Roam candidate selection score algorithm") + +#define CFG_SCORING_ALL \ + CFG(CFG_SCORING_RSSI_WEIGHTAGE) \ + CFG(CFG_SCORING_HT_CAPS_WEIGHTAGE) \ + CFG(CFG_SCORING_VHT_CAPS_WEIGHTAGE) \ + CFG(CFG_SCORING_HE_CAPS_WEIGHTAGE) \ + CFG(CFG_SCORING_CHAN_WIDTH_WEIGHTAGE) \ + CFG(CFG_SCORING_CHAN_BAND_WEIGHTAGE) \ + CFG(CFG_SCORING_NSS_WEIGHTAGE) \ + CFG(CFG_SCORING_BEAMFORM_CAP_WEIGHTAGE) \ + CFG(CFG_SCORING_PCL_WEIGHTAGE) \ + CFG(CFG_SCORING_CHAN_CONGESTION_WEIGHTAGE) \ + CFG(CFG_SCORING_OCE_WAN_WEIGHTAGE) \ + CFG(CFG_SAE_PK_AP_WEIGHTAGE) \ + CFG(CFG_SCORING_BEST_RSSI_THRESHOLD) \ + CFG(CFG_SCORING_GOOD_RSSI_THRESHOLD) \ + CFG(CFG_SCORING_BAD_RSSI_THRESHOLD) \ + CFG(CFG_SCORING_GOOD_RSSI_PERCENT) \ + CFG(CFG_SCORING_BAD_RSSI_PERCENT) \ + CFG(CFG_SCORING_GOOD_RSSI_BUCKET_SIZE) \ + CFG(CFG_SCORING_BAD_RSSI_BUCKET_SIZE) \ + CFG(CFG_SCORING_RSSI_PREF_5G_THRESHOLD) \ + CFG(CFG_SCORING_BW_WEIGHT_PER_IDX) \ + CFG(CFG_SCORING_NSS_WEIGHT_PER_IDX) \ + CFG(CFG_SCORING_BAND_WEIGHT_PER_IDX) \ + CFG(CFG_SCORING_NUM_ESP_QBSS_SLOTS) \ + CFG(CFG_SCORING_ESP_QBSS_SCORE_IDX_3_TO_0) \ + CFG(CFG_SCORING_ESP_QBSS_SCORE_IDX_7_TO_4) \ + CFG(CFG_SCORING_ESP_QBSS_SCORE_IDX_11_TO_8) \ + CFG(CFG_SCORING_ESP_QBSS_SCORE_IDX_15_TO_12) \ + CFG(CFG_SCORING_NUM_OCE_WAN_SLOTS) \ + CFG(CFG_SCORING_OCE_WAN_SCORE_IDX_3_TO_0) \ + CFG(CFG_SCORING_OCE_WAN_SCORE_IDX_7_TO_4) \ + CFG(CFG_SCORING_OCE_WAN_SCORE_IDX_11_TO_8) \ + CFG(CFG_SCORING_OCE_WAN_SCORE_IDX_15_TO_12) \ + CFG(CFG_ROAM_SCORE_DELTA_TRIGGER_BITMAP) \ + CFG(CFG_ROAM_SCORE_DELTA) \ + CFG(CFG_CAND_MIN_ROAM_SCORE_DELTA) \ + CFG(CFG_ENABLE_SCORING_FOR_ROAM) \ + CFG(CFG_APSD_ENABLED) \ + CFG(CFG_DISCONNECT_ROAM_TRIGGER_MIN_RSSI) \ + CFG(CFG_BMISS_ROAM_MIN_RSSI) \ + CFG(CFG_2G_TO_5G_ROAM_MIN_RSSI) \ + CFG(CFG_IDLE_ROAM_SCORE_DELTA) \ + CFG(CFG_BTM_ROAM_SCORE_DELTA) \ + CFG(CFG_VENDOR_ROAM_SCORE_ALGORITHM) \ + +#endif /* __CFG_MLME_SCORING_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_sta.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_sta.h new file mode 100644 index 0000000000000000000000000000000000000000..3d3e068a3d18f6de8663b2e42e8e0cd5fc92c2e8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_sta.h @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains configuration definitions for MLME STA. + */ + +#ifndef CFG_MLME_STA_H__ +#define CFG_MLME_STA_H__ + +#include "wlan_mlme_public_struct.h" + +/* + * + * gStaKeepAlivePeriod - STA keep alive period + * + * + * @Min: 0 + * @Max: 1000 + * @Default: 30 + * + * This ini is used to control how frequently STA should send NULL frames to AP + * (period in seconds) to notify AP of its existence. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_INFRA_STA_KEEP_ALIVE_PERIOD CFG_INI_UINT( \ + "gStaKeepAlivePeriod", \ + 0, \ + 1000, \ + 30, \ + CFG_VALUE_OR_DEFAULT, \ + "send default NULL frame to AP") + +/* + * + * tgt_gtx_usr_cfg - target gtx user config + * @Min: 0 + * @Max: 32 + * @Default: 32 + * + * This ini is used to set target gtx user config. + * + * Related: None + * + * Usage: Internal/External + * + * + */ +#define CFG_TGT_GTX_USR_CFG CFG_INI_UINT( \ + "tgt_gtx_usr_cfg", \ + 0, \ + 32, \ + 32, \ + CFG_VALUE_OR_DEFAULT, \ + "target gtx user config") + +/* + * + * pmkidModes - Enable PMKID modes + * This INI is used to enable PMKID feature options + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * Related: None + * + * Supported Feature: Scan + * + * Usage: External + * + * + */ +#define CFG_PMKID_MODES CFG_INI_UINT( \ + "pmkidModes", \ + 0, \ + 3, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "PMKID feature options") + +/* + * + * gIgnorePeerErpInfo - Ignore peer information + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to ignore default peer info + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_IGNORE_PEER_ERP_INFO CFG_INI_BOOL( \ + "gIgnorePeerErpInfo", \ + 0, \ + "ignore default peer info") + +/* + * + * gStaPrefer80MHzOver160MHz - set sta preference to connect in 80HZ/160HZ + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set sta preference to connect in 80HZ/160HZ + * + * 0 - Connects in 160MHz 1x1 when AP is 160MHz 2x2 + * 1 - Connects in 80MHz 2x2 when AP is 160MHz 2x2 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_STA_PREFER_80MHZ_OVER_160MHZ CFG_INI_BOOL( \ + "gStaPrefer80MHzOver160MHz", \ + 0, \ + "Sta preference to connect in 80HZ/160HZ") + +/* + * + * gEnable5gEBT - Enables/disables 5G early beacon termination. When enabled + * terminate the reception of beacon if the TIM element is + * clear for the power saving + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set default 5G early beacon termination + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_PPS_ENABLE_5G_EBT CFG_INI_BOOL( \ + "gEnable5gEBT", \ + 1, \ + "5G early beacon termination") + +/* + * + * gSendDeauthBeforeCon - Send deauth before connection or not + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set whether send deauth before connection or + * not. If last disconnection was due to HB failure and we reconnect + * to same AP next time, send deauth before starting connection. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_DEAUTH_BEFORE_CONNECTION CFG_INI_BOOL( \ + "gSendDeauthBeforeCon", \ + 0, \ + "send deauth before connection") + +/* + * + * deauth_retry_cnt- No. of deauth retries if the Tx is failed + * @Min: 0 + * @Max: 4 + * @Default: 2 + * + * This ini is used to set retry deauth if Tx is not success. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_DEAUTH_RETRY_CNT CFG_INI_UINT( \ + "deauth_retry_cnt", \ + 0, \ + 4, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "Set Deauth retry count") + +/* + * + * gDot11PMode - 802.11p mode + * @Min: CFG_11P_DISABLED + * @Max: CFG_11P_CONCURRENT + * @Default: CFG_11P_DISABLED + * + * This ini used to set 802.11p mode. + * + * + * Usage: Internal/External + * + * + */ +#define CFG_DOT11P_MODE CFG_INI_UINT( \ + "gDot11PMode", \ + CFG_11P_DISABLED, \ + CFG_11P_CONCURRENT, \ + CFG_11P_DISABLED, \ + CFG_VALUE_OR_DEFAULT, \ + "802.11p mode") + +/* + * + * gEnable_go_cts2self_for_sta - Indicate firmware to stop NOA and + * start using cts2self + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * When gEnable_go_cts2self_for_sta is enabled then if a legacy + * client connects to P2P GO, Host will send a WMI VDEV command + * to FW to stop using NOA for P2P GO + * and start using CTS2SELF. + * + * + * Supported Feature: P2P + * + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_GO_CTS2SELF_FOR_STA CFG_INI_BOOL( \ + "gEnable_go_cts2self_for_sta", \ + 0, \ + "firmware to stop NOA and start using cts2self") + +/* + * + * g_qcn_ie_support - QCN IE support + * @Min: 0 (disabled) + * @Max: 1 (enabled) + * @Default: 1 (enabled) + * + * This config item is used to support QCN IE in probe/assoc/reassoc request + * for STA mode. QCN IE support is not added for SAP mode. + * + * Related: N/A + * + * Supported Feature: N/A + * + * Usage: Internal/External + * + * + */ +#define CFG_QCN_IE_SUPPORT CFG_INI_BOOL( \ + "g_qcn_ie_support", \ + 1, \ + "QCN IE support") + +/* + * + * g_fils_max_chan_guard_time - Set maximum channel guard time(ms) + * @Min: 0 + * @Max: 10 + * @Default: 0 + * + * This ini is used to set maximum channel guard time in milliseconds. + * + * Related: None + * + * Supported Feature: FILS + * + * Usage: External + * + * + */ +#define CFG_FILS_MAX_CHAN_GUARD_TIME CFG_INI_UINT( \ + "g_fils_max_chan_guard_time", \ + 0, \ + 10, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Set maximum channel guard time") + +/* + * + * SingleTIDRC - Set replay counter for all TID's + * @Min: 0 Separate replay counter for all TID + * @Max: 1 Single replay counter for all TID + * @Default: 1 + * + * This ini is used to set replay counter for all TID's + * + * 0 - Separate replay counter for all TID + * 1 - Single replay counter for all TID + * + * Related: None. + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define CFG_SINGLE_TID_RC CFG_INI_BOOL( \ + "SingleTIDRC", \ + 1, \ + "replay counter for all TID") + +/* + * wait_cnf_timeout - Wait assoc cnf timeout + * @Min: 10 + * @Max: 3000 + * @Default: 1000 + * + * This is internal configure for waiting assoc cnf timeout + * + * Related: None + * + * Usage: Internal + * + */ +#define CFG_WT_CNF_TIMEOUT CFG_UINT( \ + "wait_cnf_timeout", \ + 10, \ + 3000, \ + 1000, \ + CFG_VALUE_OR_DEFAULT, \ + "Wait confirm timeout") + +/* + * + * gStaMiracastMccRestTimeVal - Rest time when Miracast is running. + * @Min: 100 + * @Max: 500 + * @Default: 400 + * + * This ini is used to set rest time for home channel for Miracast before + * going for scan. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ + +#define CFG_STA_MCAST_MCC_REST_TIME CFG_INI_UINT( \ + "gStaMiracastMccRestTimeVal", \ + 100, \ + 500, \ + 400, \ + CFG_VALUE_OR_DEFAULT, \ + "Rest time when Miracast is running") + +/* + * current_rssi - current rssi + * @Min: 0 + * @Max: 127 + * @Default: 0 + * + * This is internal configure for current rssi + * + * Related: None + * + * Usage: Internal + * + */ +#define CFG_CURRENT_RSSI CFG_UINT( \ + "current_rssi", \ + 0, \ + 127, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Current RSSI") + +/* + * + * gAllowTPCfromAP - Support for AP power constraint + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini controls driver to honor/dishonor power constraint from AP. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_TX_POWER_CTRL CFG_INI_BOOL( \ + "gAllowTPCfromAP", \ + 1, \ + "Support for AP power constraint") + +/* + * + * gStaKeepAliveMethod - Which keepalive method to use + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini determines which keepalive method to use for station interfaces + * 1) Use null data packets + * 2) Use gratuitous ARP packets + * + * Related: gStaKeepAlivePeriod, gApKeepAlivePeriod, gGoKeepAlivePeriod + * + * Supported Feature: STA, Keepalive + * + * Usage: External + * + * + */ +#define CFG_STA_KEEPALIVE_METHOD CFG_INI_INT( \ + "gStaKeepAliveMethod", \ + MLME_STA_KEEPALIVE_NULL_DATA, \ + MLME_STA_KEEPALIVE_COUNT - 1, \ + MLME_STA_KEEPALIVE_GRAT_ARP, \ + CFG_VALUE_OR_DEFAULT, \ + "Which keepalive method to use") + +#define CFG_STA_ALL \ + CFG(CFG_INFRA_STA_KEEP_ALIVE_PERIOD) \ + CFG(CFG_TGT_GTX_USR_CFG) \ + CFG(CFG_PMKID_MODES) \ + CFG(CFG_IGNORE_PEER_ERP_INFO) \ + CFG(CFG_STA_PREFER_80MHZ_OVER_160MHZ) \ + CFG(CFG_PPS_ENABLE_5G_EBT) \ + CFG(CFG_ENABLE_DEAUTH_BEFORE_CONNECTION) \ + CFG(CFG_DOT11P_MODE) \ + CFG(CFG_DEAUTH_RETRY_CNT) \ + CFG(CFG_ENABLE_GO_CTS2SELF_FOR_STA) \ + CFG(CFG_QCN_IE_SUPPORT) \ + CFG(CFG_STA_MCAST_MCC_REST_TIME) \ + CFG(CFG_FILS_MAX_CHAN_GUARD_TIME) \ + CFG(CFG_SINGLE_TID_RC) \ + CFG(CFG_STA_KEEPALIVE_METHOD) \ + CFG(CFG_WT_CNF_TIMEOUT) \ + CFG(CFG_CURRENT_RSSI) \ + CFG(CFG_TX_POWER_CTRL) + +#endif /* CFG_MLME_STA_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_stats.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..613cb68770a29083e7876d0a261e94cedd84b97f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_stats.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_STATS_H +#define __CFG_MLME_STATS_H + +enum mlme_stats_link_speed_rpt_type { + CFG_STATS_LINK_SPEED_REPORT_ACTUAL = 0, + CFG_STATS_LINK_SPEED_REPORT_MAX = 1, + CFG_STATS_LINK_SPEED_REPORT_MAX_SCALED = 2, +}; + +/* + * + * periodic_stats_display_time - time(seconds) after which stats will be printed + * @Min: 0 + * @Max: 256 + * @Default: 10 + * + * This values specifies the recurring time period after which stats will be + * printed in wlan driver logs. + * + * Usage: Internal / External + * + * + */ +#define CFG_PERIODIC_STATS_DISPLAY_TIME CFG_INI_UINT( \ + "periodic_stats_display_time", \ + 0, \ + 256, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "time after which stats will be printed") + +/* + * + * gLinkSpeedRssiMed - Used when eHDD_LINK_SPEED_REPORT_SCALED is selected + * @Min: -127 + * @Max: 0 + * @Default: -65 + * + * This ini is used to set medium rssi link speed + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal / External + * + * + */ +#define CFG_LINK_SPEED_RSSI_MID CFG_INI_INT( \ + "gLinkSpeedRssiMed", \ + -127, \ + 0, \ + -65, \ + CFG_VALUE_OR_DEFAULT, \ + "medium rssi link speed") + +/* + * + * gReportMaxLinkSpeed - Max link speed + * @Min: CFG_STATS_LINK_SPEED_REPORT_ACTUAL + * @Max: CFG_STATS_LINK_SPEED_REPORT_MAX_SCALED + * @Default: CFG_STATS_LINK_SPEED_REPORT_ACTUAL + * + * This ini is used to set Max link speed + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal / External + * + * + */ +#define CFG_REPORT_MAX_LINK_SPEED CFG_INI_UINT( \ + "gReportMaxLinkSpeed", \ + CFG_STATS_LINK_SPEED_REPORT_ACTUAL, \ + CFG_STATS_LINK_SPEED_REPORT_MAX_SCALED, \ + CFG_STATS_LINK_SPEED_REPORT_MAX_SCALED, \ + CFG_VALUE_OR_DEFAULT, \ + "Max link speed") + +/* + * + * gLinkSpeedRssiLow - Used when eHDD_LINK_SPEED_REPORT_SCALED is selected + * @Min: -127 + * @Max: 0 + * @Default: -80 + * + * This ini is used to set low rssi link speed + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal / External + * + * + */ +#define CFG_LINK_SPEED_RSSI_LOW CFG_INI_INT( \ + "gLinkSpeedRssiLow", \ + -127, \ + 0, \ + -80, \ + CFG_VALUE_OR_DEFAULT, \ + "low rssi link speed") + +/* + * + * gLinkSpeedRssiHigh - Report the max possible speed with RSSI scaling + * @Min: -127 + * @Max: 0 + * @Default: -55 + * + * This ini is used to set default eHDD_LINK_SPEED_REPORT + * Used when eHDD_LINK_SPEED_REPORT_SCALED is selected + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal / External + * + * + */ +#define CFG_LINK_SPEED_RSSI_HIGH CFG_INI_INT( \ + "gLinkSpeedRssiHigh", \ + -127, \ + 0, \ + -55, \ + CFG_VALUE_OR_DEFAULT, \ + "max possible rssi link speed") + +#define CFG_STATS_ALL \ + CFG(CFG_PERIODIC_STATS_DISPLAY_TIME) \ + CFG(CFG_LINK_SPEED_RSSI_HIGH) \ + CFG(CFG_LINK_SPEED_RSSI_MID) \ + CFG(CFG_LINK_SPEED_RSSI_LOW) \ + CFG(CFG_REPORT_MAX_LINK_SPEED) + +#endif /* __CFG_MLME_STATS_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_threshold.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_threshold.h new file mode 100644 index 0000000000000000000000000000000000000000..8134c3fd1da22232b29f7d86f327a526a347c2cf --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_threshold.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_THRESHOLD_H +#define __CFG_MLME_THRESHOLD_H + +#include "wni_cfg.h" + +/* + * + * RTSThreshold - Will provide RTSThreshold + * @Min: 0 + * @Max: 1048576 + * @Default: 2347 + * + * This ini is used to set default RTSThreshold + * If minimum value 0 is selectd then it will use always RTS + * max is the max frame size + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + +#define CFG_RTS_THRESHOLD CFG_INI_UINT( \ + "RTSThreshold", \ + 0, \ + 1048576, \ + 2347, \ + CFG_VALUE_OR_DEFAULT, \ + "Default RTS Threshold") + +/* + * + * gFragmentationThreshold - It will set fragmentation threshold + * @Min: 256 + * @Max: 8000 + * @Default: 8000 + * + * This ini is used to indicate default fragmentation threshold + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + +#define CFG_FRAG_THRESHOLD CFG_INI_UINT( \ + "gFragmentationThreshold", \ + 256, \ + 8000, \ + 8000, \ + CFG_VALUE_OR_DEFAULT, \ + "Default Fragmentation Threshold") + +#define CFG_THRESHOLD_ALL \ + CFG(CFG_RTS_THRESHOLD) \ + CFG(CFG_FRAG_THRESHOLD) + +#endif /* __CFG_MLME_MAIN_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_timeout.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_timeout.h new file mode 100644 index 0000000000000000000000000000000000000000..cab06f33f3bc731fce34617b06e09e104a18146f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_timeout.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_TIMEOUT_H +#define __CFG_MLME_TIMEOUT_H + +/* + * + * join_failure_timeout - Join failure timeout value + * @Min: 0 + * @Max: 65535 + * @Default: 3000 + * + * This cfg is used to configure the join failure timeout. + * + * Usage: Internal + * + * + */ +#define CFG_JOIN_FAILURE_TIMEOUT CFG_INI_UINT( \ + "join_failure_timeout", \ + 0, \ + 65535, \ + 3000, \ + CFG_VALUE_OR_DEFAULT, \ + "Join failure timeout") + +/* + * + * auth_failure_timeout - Auth failure timeout value + * @Min: 0 + * @Max: 65535 + * @Default: 1000 + * + * This cfg is used to configure the auth failure timeout. + * + * Usage: Internal + * + * + */ +#define CFG_AUTH_FAILURE_TIMEOUT CFG_INI_UINT( \ + "auth_failure_timeout", \ + 500, \ + 5000, \ + 1000, \ + CFG_VALUE_OR_DEFAULT, \ + "auth failure timeout") + +/* + * + * auth_rsp_timeout - Auth response timeout value + * @Min: 0 + * @Max: 65535 + * @Default: 1000 + * + * This cfg is used to configure the auth response timeout. + * + * Usage: Internal + * + * + */ +#define CFG_AUTH_RSP_TIMEOUT CFG_INI_UINT( \ + "auth_rsp_timeout", \ + 0, \ + 65535, \ + 1000, \ + CFG_VALUE_OR_DEFAULT, \ + "auth rsp timeout") + +/* + * + * assoc_failure_timeout - Assoc failure timeout value + * @Min: 0 + * @Max: 65535 + * @Default: 2000 + * + * This cfg is used to configure the assoc failure timeout. + * + * Usage: Internal + * + * + */ +#define CFG_ASSOC_FAILURE_TIMEOUT CFG_INI_UINT( \ + "assoc_failure_timeout", \ + 0, \ + 65535, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "assoc failure timeout") + +/* + * + * reassoc_failure_timeout - Re-Assoc failure timeout value + * @Min: 0 + * @Max: 65535 + * @Default: 1000 + * + * This cfg is used to configure the re-assoc failure timeout. + * + * Usage: Internal + * + * + */ +#define CFG_REASSOC_FAILURE_TIMEOUT CFG_INI_UINT( \ + "reassoc_failure_timeout", \ + 0, \ + 65535, \ + 1000, \ + CFG_VALUE_OR_DEFAULT, \ + "reassoc failure timeout") + +/* + * + * probe_after_hb_fail_timeout - Probe after HB failure timeout value + * @Min: 10 + * @Max: 10000 + * @Default: 70 + * + * This cfg is used to configure the Probe after HB failure timeout. + * + * Usage: Internal + * + * + */ +#define CFG_PROBE_AFTER_HB_FAIL_TIMEOUT CFG_INI_UINT( \ + "probe_after_hb_fail_timeout", \ + 10, \ + 10000, \ + 70, \ + CFG_VALUE_OR_DEFAULT, \ + "probe after HB fail timeout") + +/* + * + * olbc_detect_timeout - olbc detect timeout value + * @Min: 1000 + * @Max: 30000 + * @Default: 10000 + * + * This cfg is used to configure the olbc detect timeout. + * + * Usage: Internal + * + * + */ +#define CFG_OLBC_DETECT_TIMEOUT CFG_INI_UINT( \ + "olbc_detect_timeout", \ + 1000, \ + 30000, \ + 10000, \ + CFG_VALUE_OR_DEFAULT, \ + "OLBC detect timeout") + +/* + * + * addts_rsp_timeout - addts response timeout value + * @Min: 0 + * @Max: 65535 + * @Default: 1000 + * + * This cfg is used to configure the addts response timeout. + * + * Usage: Internal + * + * + */ +#define CFG_ADDTS_RSP_TIMEOUT CFG_INI_UINT( \ + "addts_rsp_timeout", \ + 0, \ + 65535, \ + 1000, \ + CFG_VALUE_OR_DEFAULT, \ + "ADDTS RSP timeout") + +/* + * + * gHeartbeat24 - Heart beat threashold value + * @Min: 0 + * @Max: 65535 + * @Default: 40 + * + * This cfg is used to configure the Heart beat threashold. + * + * Usage: Internal/External + * + * + */ +#define CFG_HEART_BEAT_THRESHOLD CFG_INI_UINT( \ + "gHeartbeat24", \ + 0, \ + 65535, \ + 40, \ + CFG_VALUE_OR_DEFAULT, \ + "Heart beat threashold") + +/* + * + * gApKeepAlivePeriod - AP keep alive period + * @Min: 1 + * @Max: 65535 + * @Default: 20 + * + * This ini is used to set keep alive period(in seconds) of AP + * + * Related: None. + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_AP_KEEP_ALIVE_TIMEOUT CFG_INI_UINT( \ + "gApKeepAlivePeriod", \ + 1, \ + 65535, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "AP keep alive timeout") + +/* + * + * gApLinkMonitorPeriod - AP keep alive period + * @Min: 3 + * @Max: 50 + * @Default: 10 + * + * This ini is used to configure AP link monitor timeout value + * + * Related: None. + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_AP_LINK_MONITOR_TIMEOUT CFG_INI_UINT( \ + "gApLinkMonitorPeriod", \ + 3, \ + 50, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "AP link monitor timeout") + +/* + * + * gDataInactivityTimeout - Data inactivity timeout for non wow mode. + * @Min: 1 + * @Max: 255 + * @Default: 200 + * + * This ini is used to set data inactivity timeout value, in milliseconds, of + * non wow mode. + * + * Supported Feature: inactivity timeout in non wow mode + * + * Usage: External + * + * + */ +#define CFG_PS_DATA_INACTIVITY_TIMEOUT CFG_INI_UINT( \ + "gDataInactivityTimeout", \ + 1, \ + 255, \ + 200, \ + CFG_VALUE_OR_DEFAULT, \ + "PS data inactivity timeout") + +/* + * + * wmi_wq_watchdog - Sets timeout period for wmi watchdog bite. + * @Min: 0 + * @Max: 30 + * @Default: 20 + * + * This ini is used to set timeout period for wmi watchdog bite. If it is + * 0 then wmi watchdog bite is disabled. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_WMI_WQ_WATCHDOG CFG_INI_UINT( \ + "wmi_wq_watchdog", \ + 0, \ + 30, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "timeout period for wmi watchdog bite") + +#define CFG_TIMEOUT_ALL \ + CFG(CFG_JOIN_FAILURE_TIMEOUT) \ + CFG(CFG_AUTH_FAILURE_TIMEOUT) \ + CFG(CFG_AUTH_RSP_TIMEOUT) \ + CFG(CFG_ASSOC_FAILURE_TIMEOUT) \ + CFG(CFG_REASSOC_FAILURE_TIMEOUT) \ + CFG(CFG_PROBE_AFTER_HB_FAIL_TIMEOUT) \ + CFG(CFG_OLBC_DETECT_TIMEOUT) \ + CFG(CFG_ADDTS_RSP_TIMEOUT) \ + CFG(CFG_HEART_BEAT_THRESHOLD) \ + CFG(CFG_AP_KEEP_ALIVE_TIMEOUT) \ + CFG(CFG_AP_LINK_MONITOR_TIMEOUT) \ + CFG(CFG_WMI_WQ_WATCHDOG) \ + CFG(CFG_PS_DATA_INACTIVITY_TIMEOUT) + +#endif /* __CFG_MLME_TIMEOUT_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_twt.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_twt.h new file mode 100644 index 0000000000000000000000000000000000000000..2bbaf6a94af26b49e93e23a8a246b7e94d45e414 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_twt.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_TWT_H +#define __CFG_MLME_TWT_H + +/* + * + * twt_requestor - twt requestor. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This cfg is used to store twt requestor config. + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: Internal + * + * + */ +#define CFG_TWT_REQUESTOR CFG_INI_BOOL( \ + "twt_requestor", \ + 0, \ + "TWT requestor") +/* + * + * twt_responder - twt responder. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This cfg is used to store twt responder config. + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: Internal + * + * + */ +#define CFG_TWT_RESPONDER CFG_INI_BOOL( \ + "twt_responder", \ + 0, \ + "TWT responder") + +/* + * + * bcast_twt - to bcast twt capability. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This cfg is used to bcast twt capability. + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: Internal + * + * + */ +#define CFG_BCAST_TWT CFG_INI_BOOL( \ + "bcast_twt", \ + 0, \ + "Bcast TWT") + +/* + * + * enable_twt - Enable Target Wake Time support. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable or disable TWT support. + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: External + * + * + */ +#define CFG_ENABLE_TWT CFG_INI_BOOL( \ + "enable_twt", \ + 1, \ + "TWT support") + +/* + * + * twt_congestion_timeout - Target wake time congestion timeout. + * @Min: 0 + * @Max: 10000 + * @Default: 100 + * + * STA uses this timer to continuously monitor channel congestion levels to + * decide whether to start or stop TWT. This ini is used to configure the + * target wake time congestion timeout value in the units of milliseconds. + * A value of Zero indicates that this is a host triggered TWT and all the + * necessary configuration for TWT will be directed from the host. + * + * Related: NA + * + * Supported Feature: 11AX + * + * Usage: External + * + * + */ +#define CFG_TWT_CONGESTION_TIMEOUT CFG_INI_UINT( \ + "twt_congestion_timeout", \ + 0, \ + 10000, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "twt congestion timeout") + +#define CFG_TWT_ALL \ + CFG(CFG_BCAST_TWT) \ + CFG(CFG_ENABLE_TWT) \ + CFG(CFG_TWT_REQUESTOR) \ + CFG(CFG_TWT_RESPONDER) \ + CFG(CFG_TWT_CONGESTION_TIMEOUT) + +#endif /* __CFG_MLME_TWT_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_vht_caps.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_vht_caps.h new file mode 100644 index 0000000000000000000000000000000000000000..cc4bb01740fcb8f984774dcdec58ba01ef62fb0c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_vht_caps.h @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_VHT_CAPS_H +#define __CFG_MLME_VHT_CAPS_H + +#define CFG_VHT_SUPP_CHAN_WIDTH CFG_UINT( \ + "supp_chan_width", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT SUPPORTED CHAN WIDTH SET") + +/* + * + * gTxBFCsnValue - ini to set VHT/HE STS Caps field + * @Min: 0 + * @Max: 7 + * @Default: 7 + * + * This ini is used to configure the STS capability shown in AC/AX mode + * MGMT frame IE, the final STS field shown in VHT/HE IE will be calculated + * by MIN of (INI set, target report value). Only if gTxBFEnable is enabled + * and SU/MU BEAMFORMEE Caps is shown, then STS Caps make sense. + * + * Related: gTxBFEnable. + * + * Supported Feature: STA/SAP + * + * Usage: Internal + * + * + */ +#define CFG_VHT_BEAMFORMEE_ANT_SUPP CFG_INI_UINT( \ + "txBFCsnValue", \ + 0, \ + 7, \ + 7, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT BEAMFORMEE ANTENNA SUPPORTED CAP") + +/* + * + * gEnableTxSUBeamformer - Enables TX Su beam former + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_TX_SU_BEAM_FORMER CFG_INI_BOOL( \ + "gEnableTxSUBeamformer", \ + 0, \ + "vht tx su beam former") + +#define CFG_VHT_NUM_SOUNDING_DIMENSIONS CFG_UINT( \ + "num_soundingdim", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT NUMBER OF SOUNDING DIMENSIONS") + +#define CFG_VHT_HTC_VHTC CFG_BOOL( \ + "htc_vhtc", \ + 0, \ + "VHT HTC VHTC") + +#define CFG_VHT_LINK_ADAPTATION_CAP CFG_UINT( \ + "link_adap_cap", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT LINK ADAPTATION CAP") + +#define CFG_VHT_RX_ANT_PATTERN CFG_BOOL( \ + "rx_antpattern", \ + 1, \ + "VHT RX ANTENNA PATTERN CAP") + +#define CFG_VHT_TX_ANT_PATTERN CFG_BOOL( \ + "tx_antpattern", \ + 1, \ + "VHT TX ANTENNA PATTERN CAP") + +#define CFG_VHT_RX_SUPP_DATA_RATE CFG_UINT( \ + "rx_supp_data_rate", \ + 0, \ + 866, \ + 866, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT RX SUPP DATA RATE") + +#define CFG_VHT_TX_SUPP_DATA_RATE CFG_UINT( \ + "tx_supp_data_rate", \ + 0, \ + 866, \ + 866, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT TX SUPP DATA RATE") + +#define CFG_TX_BF_CAP CFG_UINT( \ + "tx_bf_cap", \ + 0, \ + 4294967295, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "TX BF CAP") + +#define CFG_AS_CAP CFG_UINT( \ + "as_cap", \ + 0, \ + 255, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "AS CAP") + +/* + * + * gDisableLDPCWithTxbfAP - Disable LDPC with tx bf AP + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_DISABLE_LDPC_WITH_TXBF_AP CFG_INI_BOOL( \ + "gDisableLDPCWithTxbfAP", \ + 0, \ + "Disable LDPC with tx bf AP") + +/* + * + * gTxBFEnable - Enables SU beamformee caps + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_SU_BEAMFORMEE_CAP CFG_INI_BOOL( \ + "gTxBFEnable", \ + 1, \ + "VHT SU BEAMFORMEE CAPABILITY") + +/* + * + * gEnableTxBFin20MHz - Enables TXBF in 20mhz + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_TXBF_IN_20MHZ CFG_INI_BOOL( \ + "gEnableTxBFin20MHz", \ + 0, \ + "VHT ENABLE TXBF 20MHZ") + +#define CFG_VHT_MU_BEAMFORMER_CAP CFG_BOOL( \ + "mu_bformer", \ + 0, \ + "VHT MU BEAMFORMER CAP") + +#define CFG_VHT_TXOP_PS CFG_BOOL( \ + "txop_ps", \ + 0, \ + "VHT TXOP PS") + +/* + * + * gVhtChannelWidth - Channel width capability for 11ac + * @Min: 0 + * @Max: 4 + * @Default: 2 + * + * This ini is used to set channel width capability for 11AC. + * eHT_CHANNEL_WIDTH_20MHZ = 0, + * eHT_CHANNEL_WIDTH_40MHZ = 1, + * eHT_CHANNEL_WIDTH_80MHZ = 2, + * eHT_CHANNEL_WIDTH_160MHZ = 3, + * eHT_CHANNEL_WIDTH_80P80MHZ = 4, + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_CHANNEL_WIDTH CFG_INI_UINT( \ + "gVhtChannelWidth", \ + 0, \ + 4, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "Channel width capability for 11ac") + +/* + * + * gVhtRxMCS - VHT Rx MCS capability for 1x1 mode + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to set VHT Rx MCS capability for 1x1 mode. + * 0, MCS0-7 + * 1, MCS0-8 + * 2, MCS0-9 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: Internal/External + * + * + */ +#define CFG_VHT_ENABLE_RX_MCS_8_9 CFG_INI_UINT( \ + "gVhtRxMCS", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT Rx MCS") + +/* + * + * gVhtTxMCS - VHT Tx MCS capability for 1x1 mode + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to set VHT Tx MCS capability for 1x1 mode. + * 0, MCS0-7 + * 1, MCS0-8 + * 2, MCS0-9 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: Internal/External + * + * + */ +#define CFG_VHT_ENABLE_TX_MCS_8_9 CFG_INI_UINT( \ + "gVhtTxMCS", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT Tx MCS") + +/* + * + * gVhtRxMCS2x2 - VHT Rx MCS capability for 2x2 mode + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to set VHT Rx MCS capability for 2x2 mode. + * 0, MCS0-7 + * 1, MCS0-8 + * 2, MCS0-9 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_RX_MCS2x2_8_9 CFG_INI_UINT( \ + "gVhtRxMCS2x2", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT Rx MCS 2x2") + +/* + * + * gVhtTxMCS2x2 - VHT Tx MCS capability for 2x2 mode + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to set VHT Tx MCS capability for 2x2 mode. + * 0, MCS0-7 + * 1, MCS0-8 + * 2, MCS0-9 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_TX_MCS2x2_8_9 CFG_INI_UINT( \ + "gVhtTxMCS2x2", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT Tx MCS 2x2") + +/* + * + * enable_vht20_mcs9 - Enables VHT MCS9 in 20M BW operation + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_ENABLE_VHT20_MCS9 CFG_INI_BOOL( \ + "enable_vht20_mcs9", \ + 1, \ + "Enables VHT MCS9 in 20M BW") + +/* + * + * gEnable2x2 - Enables/disables VHT Tx/Rx MCS values for 2x2 + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini disables/enables 2x2 mode. If this is zero then DUT operates as 1x1 + * + * 0, Disable + * 1, Enable + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_2x2_CAP_FEATURE CFG_INI_BOOL( \ + "gEnable2x2", \ + 0, \ + "VHT Enable 2x2") + +/* + * + * gEnableMuBformee - Enables/disables multi-user (MU) beam formee capability + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini enables/disables multi-user (MU) beam formee + * capability + * + * Change MU Bformee only when gTxBFEnable is enabled. + * When gTxBFEnable and gEnableMuBformee are set, MU beam formee capability is + * enabled. + * Related: gTxBFEnable + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_MU_BFORMEE_CAP_FEATURE CFG_INI_BOOL( \ + "gEnableMuBformee", \ + 0, \ + "VHT Enable MU Beamformee") + +/* + * + * gEnablePAID - VHT partial AID feature + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This option enables/disables VHT partial AID feature. + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_PAID_FEATURE CFG_INI_BOOL( \ + "gEnablePAID", \ + 0, \ + "VHT Enable PAID") + +/* + * + * gEnableGID - VHT Group ID feature + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This option enables/disables VHT Group ID feature. + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_GID_FEATURE CFG_INI_BOOL( \ + "gEnableGID", \ + 0, \ + "VHT Enable GID") + +/* + * + * gEnableVhtFor24GHzBand - Enable VHT for 2.4GHZ in SAP mode + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_ENABLE_VHT_FOR_24GHZ CFG_INI_BOOL( \ + "gEnableVhtFor24GHzBand", \ + 0, \ + "VHT Enable for 24GHz") + +/* + * gEnableVendorVhtFor24GHzBand - Parameter to control VHT support + * based on vendor ie in 2.4 GHz band + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This parameter will enable SAP to read VHT capability in vendor ie in Assoc + * Req and send VHT caps in Resp to establish connection in VHT Mode. + * Supported Feature: SAP + * + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_VENDOR_VHT_FOR_24GHZ CFG_INI_BOOL( \ + "gEnableVendorVhtFor24GHzBand", \ + 1, \ + "VHT Enable Vendor for 24GHz") + +/* + * + * gVhtAmpduLenExponent - maximum receive AMPDU size configuration + * @Min: 0 + * @Max: 7 + * @Default: 3 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_AMPDU_LEN_EXPONENT CFG_INI_UINT( \ + "gVhtAmpduLenExponent", \ + 0, \ + 7, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT AMPDU Len in Exponent") + +/* + * + * gVhtMpduLen - VHT MPDU length + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_MPDU_LEN CFG_INI_UINT( \ + "gVhtMpduLen", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "VHT MPDU Length") + +/* + * + * gEnableTxBFeeSAP - Enable / Disable Tx beamformee in SAP mode + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: NA + * + * Supported Feature: 11AC + * + * Usage: External + * + * + */ +#define CFG_VHT_ENABLE_TXBF_SAP_MODE CFG_INI_BOOL( \ + "gEnableTxBFeeSAP", \ + 0, \ + "Enable tx bf sap mode") + +/* + * + * enable_subfee_vendor_vhtie - ini to enable/disable SU Bformee in vendor VHTIE + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable SU Bformee in vendor vht ie if gTxBFEnable + * is enabled. if gTxBFEnable is 0 this will not have any effect. + * + * Related: gTxBFEnable. + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_ENABLE_SUBFEE_IN_VENDOR_VHTIE CFG_INI_BOOL( \ + "enable_subfee_vendor_vhtie", \ + 0, \ + "Enable subfee in vendor vht ie") + +/* + * + * enable_vhtmcs_10_11_support - Enable/Disable vht mcs 10, 11 support + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable mcs 10, 11 support. + * + * Related: NA + * + * Usage: Internal + * + * + */ +#define CFG_ENABLE_VHT_MCS_10_11 CFG_INI_BOOL( \ + "enable_vhtmcs_10_11_support", \ + 1, \ + "Enable/Disable vht mcs 10, 11 support") + +#define CFG_VHT_CAPS_ALL \ + CFG(CFG_VHT_SUPP_CHAN_WIDTH) \ + CFG(CFG_VHT_SU_BEAMFORMEE_CAP) \ + CFG(CFG_VHT_BEAMFORMEE_ANT_SUPP) \ + CFG(CFG_VHT_ENABLE_TX_SU_BEAM_FORMER) \ + CFG(CFG_VHT_NUM_SOUNDING_DIMENSIONS) \ + CFG(CFG_VHT_MU_BEAMFORMER_CAP) \ + CFG(CFG_VHT_TXOP_PS) \ + CFG(CFG_VHT_HTC_VHTC) \ + CFG(CFG_VHT_LINK_ADAPTATION_CAP) \ + CFG(CFG_VHT_RX_ANT_PATTERN) \ + CFG(CFG_VHT_TX_ANT_PATTERN) \ + CFG(CFG_VHT_RX_SUPP_DATA_RATE) \ + CFG(CFG_VHT_TX_SUPP_DATA_RATE) \ + CFG(CFG_VHT_ENABLE_TXBF_IN_20MHZ) \ + CFG(CFG_VHT_CHANNEL_WIDTH) \ + CFG(CFG_VHT_ENABLE_RX_MCS_8_9) \ + CFG(CFG_VHT_ENABLE_TX_MCS_8_9) \ + CFG(CFG_VHT_ENABLE_RX_MCS2x2_8_9) \ + CFG(CFG_VHT_ENABLE_TX_MCS2x2_8_9) \ + CFG(CFG_ENABLE_VHT20_MCS9) \ + CFG(CFG_VHT_ENABLE_2x2_CAP_FEATURE) \ + CFG(CFG_VHT_ENABLE_MU_BFORMEE_CAP_FEATURE) \ + CFG(CFG_VHT_ENABLE_PAID_FEATURE) \ + CFG(CFG_VHT_ENABLE_GID_FEATURE) \ + CFG(CFG_ENABLE_VHT_FOR_24GHZ) \ + CFG(CFG_ENABLE_VENDOR_VHT_FOR_24GHZ) \ + CFG(CFG_VHT_AMPDU_LEN_EXPONENT) \ + CFG(CFG_VHT_MPDU_LEN) \ + CFG(CFG_VHT_ENABLE_TXBF_SAP_MODE) \ + CFG(CFG_ENABLE_SUBFEE_IN_VENDOR_VHTIE) \ + CFG(CFG_TX_BF_CAP) \ + CFG(CFG_AS_CAP) \ + CFG(CFG_DISABLE_LDPC_WITH_TXBF_AP) \ + CFG(CFG_ENABLE_VHT_MCS_10_11) + +#endif /* __CFG_MLME_VHT_CAPS_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_wep_params.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_wep_params.h new file mode 100644 index 0000000000000000000000000000000000000000..ddd5f93c54a128d82a24b449bd47d7e774b3dd7c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_wep_params.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of WEP parameters related + * converged configuration. + */ + +#ifndef __CFG_MLME_WEP_PARAMS_H +#define __CFG_MLME_WEP_PARAMS_H + +#define CFG_WEP_DEFAULT_KEYID CFG_UINT( \ + "wep_default_key_id", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "wep default key id") + +#define CFG_SHARED_KEY_AUTH_ENABLE CFG_BOOL( \ + "shared_key_auth", \ + 1, \ + "shared key authentication") + +#define CFG_OPEN_SYSTEM_AUTH_ENABLE CFG_BOOL( \ + "open_system_auth", \ + 1, \ + "Open system authentication") + +#define CFG_AUTHENTICATION_TYPE CFG_UINT( \ + "auth_type", \ + 0, \ + 3, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "authentication type") + +#define CFG_PRIVACY_ENABLED CFG_BOOL( \ + "privacy_enabled", \ + 0, \ + "wep privacy") + +#define CFG_WEP_PARAMS_ALL \ + CFG(CFG_WEP_DEFAULT_KEYID) \ + CFG(CFG_SHARED_KEY_AUTH_ENABLE) \ + CFG(CFG_OPEN_SYSTEM_AUTH_ENABLE) \ + CFG(CFG_AUTHENTICATION_TYPE) \ + CFG(CFG_PRIVACY_ENABLED) + +#endif /* __CFG_MLME_WEP_PARAMS_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_wifi_pos.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_wifi_pos.h new file mode 100644 index 0000000000000000000000000000000000000000..519d8e9f35f1e1816a64f71799a257ffab0df4da --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_wifi_pos.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_WIFI_POS_H +#define __CFG_MLME_WIFI_POS_H + +/* + * + * gfine_time_meas_cap - fine timing measurement capability information + * @Min: 0x0000 + * @Max: 0x00BD + * @Default: 0x000D + * + * fine timing measurement capability information + * + * <----- fine_time_meas_cap (in bits) -----> + * +---------+-----+-----+-----+-----+------+------+-------+-------+-----+-----+ + * | 10-31 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * +---------+-----+-----+-----+-----+------+------+-------+-------+-----+-----+ + * | reserved| NAN | NAN | SAP | SAP |P2P-GO|P2P-GO|P2P-CLI|P2P-CLI| STA | STA | + * | | resp|init |resp |init |resp |init |resp |init |resp |init | + * +---------+-----+-----+-----+-----+------+------+-------+-------+-----+-----+ + * + * resp - responder role; init- initiator role + * + * CFG_FINE_TIME_MEAS_CAPABILITY_MAX computed based on the table + * +-----------------+-----------------+-----------+ + * | Device Role | Initiator | Responder | + * +-----------------+-----------------+-----------+ + * | Station | Y | N | + * | P2P-CLI | Y | Y | + * | P2P-GO | Y | Y | + * | SAP | N | Y | + * +-----------------+-----------------+-----------+ + * + * Related: None + * + * Supported Feature: WIFI POS + * + * Usage: Internal/External + * + * + */ +#define CFG_FINE_TIME_MEAS_CAPABILITY CFG_INI_UINT( \ + "gfine_time_meas_cap", \ + 0x0000, \ + 0x003BD, \ + 0x0030D, \ + CFG_VALUE_OR_DEFAULT, \ + "fine timing measurement capability") + +/* + * + * oem_6g_support_disable - oem 6g support is disabled + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to show OEM is 6Ghz disabled. For legacy OEM apps + * having no support for 6Ghz, the default value is 1 and thus driver will + * not serve 6Ghz info to legacy oem application. + * OEM apps supporting 6Ghz sets the ini value to 0 to get 6Ghz + * information from driver. + * + * Related: None + * + * Supported Feature: WIFI POS + * + * Usage: Internal/External + * + * + */ +#define CFG_OEM_SIXG_SUPPORT_DISABLE CFG_INI_BOOL( \ + "oem_6g_support_disable", \ + 1, \ + "oem 6Ghz support Enabled/disabled") + +#define CFG_WIFI_POS_ALL \ + CFG(CFG_FINE_TIME_MEAS_CAPABILITY) \ + CFG(CFG_OEM_SIXG_SUPPORT_DISABLE) + +#endif /* __CFG_MLME_WIFI_POS_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_wps_params.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_wps_params.h new file mode 100644 index 0000000000000000000000000000000000000000..43a093503a43d43ad155cdb05d0599c9e390b038 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_wps_params.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_WPS_PARAMS_H +#define __CFG_MLME_WPS_PARAMS_H + +#define CFG_WPS_ENABLE CFG_UINT( \ + "enable_wps", \ + 0, \ + 255, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "enable wps") + +#define CFG_WPS_STATE CFG_UINT( \ + "wps_state", \ + 0, \ + 255, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "wps_state") + +#define CFG_WPS_VERSION CFG_UINT( \ + "wps_version", \ + 0, \ + 255, \ + 16, \ + CFG_VALUE_OR_DEFAULT, \ + "wps version") + +#define CFG_WPS_CFG_METHOD CFG_UINT( \ + "wps_cfg_method", \ + 0, \ + 4294967295, \ + 8, \ + CFG_VALUE_OR_DEFAULT, \ + "wps cfg method") + +#define CFG_WPS_PRIMARY_DEVICE_CATEGORY CFG_UINT( \ + "wps_primary_device_category", \ + 0, \ + 65535, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "wps primary device category") + +#define CFG_WPS_PIMARY_DEVICE_OUI CFG_UINT( \ + "wps_primary_device_oui", \ + 0, \ + 4294967295, \ + 5304836, \ + CFG_VALUE_OR_DEFAULT, \ + "wps primary device oui") + +#define CFG_WPS_DEVICE_SUB_CATEGORY CFG_UINT( \ + "wps_device_sub_category", \ + 0, \ + 65535, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "wps device sub category") + +#define CFG_WPS_DEVICE_PASSWORD_ID CFG_UINT( \ + "wps_device_password_id", \ + 0, \ + 4294967295, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "wps device password id") + +#define WPS_UUID_DEF_STR "0xa, 0xb, 0xc, 0xd, 0xe, 0xf" +#define WPS_UUID_DEF_LEN (sizeof(WPS_UUID_DEF_STR) - 1) + +#define CFG_WPS_UUID CFG_STRING( \ + "wps_uuid", \ + 0, \ + WPS_UUID_DEF_LEN, \ + WPS_UUID_DEF_STR, \ + "wps uuid") + +#define CFG_WPS_ALL \ + CFG(CFG_WPS_ENABLE) \ + CFG(CFG_WPS_STATE) \ + CFG(CFG_WPS_VERSION) \ + CFG(CFG_WPS_CFG_METHOD) \ + CFG(CFG_WPS_PRIMARY_DEVICE_CATEGORY) \ + CFG(CFG_WPS_PIMARY_DEVICE_OUI) \ + CFG(CFG_WPS_DEVICE_SUB_CATEGORY) \ + CFG(CFG_WPS_DEVICE_PASSWORD_ID) \ + CFG(CFG_WPS_UUID) + +#endif /* __CFG_MLME_WPS_PARAMS_H */ + diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_qos.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_qos.h new file mode 100644 index 0000000000000000000000000000000000000000..6d35ce2d96b74d871a118b17d581ffe252063c63 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_qos.h @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of QOS related + * converged configurations. + */ + +#ifndef __CFG_MLME_QOS_H +#define __CFG_MLME_QOS_H + +/* + * + * gTxAggregationSize - Gives an option to configure Tx aggregation size + * in no of MPDUs + * @Min: 0 + * @Max: 64 + * @Default: 64 + * + * gTxAggregationSize gives an option to configure Tx aggregation size + * in no of MPDUs.This can be useful in debugging throughput issues + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_TX_AGGREGATION_SIZE CFG_INI_UINT( \ + "gTxAggregationSize", \ + 0, \ + 64, \ + 64, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx Aggregation size value") + +/* + * + * gTxAggregationSizeBE - To configure Tx aggregation size for BE queue + * in no of MPDUs + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxAggregationSizeBE gives an option to configure Tx aggregation size + * for BE queue in no of MPDUs.This can be useful in debugging + * throughput issues + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_AGGREGATION_SIZEBE CFG_INI_UINT( \ + "gTxAggregationSizeBE", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx Aggregation size value BE") + +/* + * + * gTxAggregationSizeBK - To configure Tx aggregation size for BK queue + * in no of MPDUs + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxAggregationSizeBK gives an option to configure Tx aggregation size + * for BK queue in no of MPDUs.This can be useful in debugging + * throughput issues + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_AGGREGATION_SIZEBK CFG_INI_UINT( \ + "gTxAggregationSizeBK", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx Aggregation size value BK") + +/* + * + * gTxAggregationSizeVI - To configure Tx aggregation size for VI queue + * in no of MPDUs + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxAggregationSizeVI gives an option to configure Tx aggregation size + * for VI queue in no of MPDUs.This can be useful in debugging + * throughput issues + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_AGGREGATION_SIZEVI CFG_INI_UINT( \ + "gTxAggregationSizeVI", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx Aggregation size value for VI") + +/* + * + * gTxAggregationSizeVO - To configure Tx aggregation size for VO queue + * in no of MPDUs + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxAggregationSizeVO gives an option to configure Tx aggregation size + * for BE queue in no of MPDUs.This can be useful in debugging + * throughput issues + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_AGGREGATION_SIZEVO CFG_INI_UINT( \ + "gTxAggregationSizeVO", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx Aggregation size value for VO") + +/* + * + * gRxAggregationSize - Gives an option to configure Rx aggregation size + * in no of MPDUs + * @Min: 1 + * @Max: 64 + * @Default: 64 + * + * gRxAggregationSize gives an option to configure Rx aggregation size + * in no of MPDUs. This can be useful in debugging throughput issues + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_RX_AGGREGATION_SIZE CFG_INI_UINT( \ + "gRxAggregationSize", \ + 1, \ + 64, \ + 64, \ + CFG_VALUE_OR_DEFAULT, \ + "Rx Aggregation size value") + +/* + * + * gTxAggSwRetryBE - Configure Tx aggregation sw retry for BE + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxAggSwRetryBE gives an option to configure Tx aggregation sw + * retry for BE. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_AGGR_SW_RETRY_BE CFG_INI_UINT( \ + "gTxAggSwRetryBE", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx aggregation retry value for BE") + +/* + * + * gTxAggSwRetryBK - Configure Tx aggregation sw retry for BK + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxAggSwRetryBK gives an option to configure Tx aggregation sw + * retry for BK. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_AGGR_SW_RETRY_BK CFG_INI_UINT( \ + "gTxAggSwRetryBK", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx aggregation retry value for BK") + +/* + * + * gTxAggSwRetryVI - Configure Tx aggregation sw retry for VI + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxAggSwRetryVI gives an option to configure Tx aggregation sw + * retry for VI. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_AGGR_SW_RETRY_VI CFG_INI_UINT( \ + "gTxAggSwRetryVI", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx aggregation retry value for VI") + +/* + * + * gTxAggSwRetryVO - Configure Tx aggregation sw retry for VO + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxAggSwRetryVO gives an option to configure Tx aggregation sw + * retry for VO. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_AGGR_SW_RETRY_VO CFG_INI_UINT( \ + "gTxAggSwRetryVO", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx aggregation retry value for VO") + +/* + * + * gTxNonAggSwRetryBE - Configure Tx non aggregation sw retry for BE + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxNonAggSwRetryBE gives an option to configure Tx non aggregation sw + * retry for BE. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_NON_AGGR_SW_RETRY_BE CFG_INI_UINT( \ + "gTxNonAggSwRetryBE", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx non aggregation retry value for BE") + +/* + * + * gTxNonAggSwRetryBK - Configure Tx non aggregation sw retry for BK + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxNonAggSwRetryBK gives an option to configure Tx non aggregation sw + * retry for BK. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_NON_AGGR_SW_RETRY_BK CFG_INI_UINT( \ + "gTxNonAggSwRetryBK", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx non aggregation retry value for BK") + +/* + * + * gTxNonAggSwRetryVI - Configure Tx non aggregation sw retry for VI + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxNonAggSwRetryVI gives an option to configure Tx non aggregation sw + * retry for VI. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_NON_AGGR_SW_RETRY_VI CFG_INI_UINT( \ + "gTxNonAggSwRetryVI", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx non aggregation retry value for VI") + +/* + * + * gTxNonAggSwRetryVO - Configure Tx non aggregation sw retry for VO + * @Min: 0 + * @Max: 64 + * @Default: 0 + * + * gTxNonAggSwRetryVO gives an option to configure Tx non aggregation sw + * retry for VO. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_TX_NON_AGGR_SW_RETRY_VO CFG_INI_UINT( \ + "gTxNonAggSwRetryVO", \ + 0, \ + 64, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx non aggregation retry value for VO") +/* + * + * gTxAggSwRetry - Configure Tx aggregation sw retry + * @Min: 0 + * @Max: 64 + * @Default: 16 + * + * gTxAggSwRetry gives an option to configure Tx aggregation sw + * retry. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_TX_AGGR_SW_RETRY CFG_INI_UINT( \ + "gTxAggSwRetry", \ + 0, \ + 64, \ + 16, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx aggregation retry value") +/* + * + * gTxNonAggSwRetry - Configure Tx non aggregation sw retry + * @Min: 0 + * @Max: 64 + * @Default: 16 + * + * gTxNonAggSwRetry gives an option to configure Tx non aggregation sw + * retry. This can be useful in debugging throughput issues. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_TX_NON_AGGR_SW_RETRY CFG_INI_UINT( \ + "gTxNonAggSwRetry", \ + 0, \ + 64, \ + 16, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx non aggregation retry value") +/* + * + * gSapMaxInactivityOverride - Configure + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This parameter will avoid updating ap_sta_inactivity from hostapd.conf + * file. If a station does not send anything in ap_max_inactivity seconds, an + * empty data frame is sent to it in order to verify whether it is + * still in range. If this frame is not ACKed, the station will be + * disassociated and then deauthenticated. This feature is used to + * clear station table of old entries when the STAs move out of the + * range. + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + */ +#define CFG_SAP_MAX_INACTIVITY_OVERRIDE CFG_INI_BOOL( \ + "gSapMaxInactivityOverride", \ + 0, \ + "SAP maximum inactivity override flag") + +/* + * + * gEnableApUapsd - Enable/disable UAPSD for SoftAP + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to setup setup U-APSD for Acs at association + * + * Related: None. + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_SAP_QOS_UAPSD CFG_INI_BOOL( \ + "gEnableApUapsd", \ + 1, \ + "Enable UAPSD for SAP") + +#define CFG_QOS_ALL \ + CFG(CFG_SAP_MAX_INACTIVITY_OVERRIDE) \ + CFG(CFG_TX_AGGREGATION_SIZE) \ + CFG(CFG_TX_AGGREGATION_SIZEBE) \ + CFG(CFG_TX_AGGREGATION_SIZEBK) \ + CFG(CFG_TX_AGGREGATION_SIZEVI) \ + CFG(CFG_TX_AGGREGATION_SIZEVO) \ + CFG(CFG_RX_AGGREGATION_SIZE) \ + CFG(CFG_TX_AGGR_SW_RETRY_BE) \ + CFG(CFG_TX_AGGR_SW_RETRY_BK) \ + CFG(CFG_TX_AGGR_SW_RETRY_VI) \ + CFG(CFG_TX_AGGR_SW_RETRY_VO) \ + CFG(CFG_TX_AGGR_SW_RETRY) \ + CFG(CFG_TX_NON_AGGR_SW_RETRY_BE) \ + CFG(CFG_TX_NON_AGGR_SW_RETRY_BK) \ + CFG(CFG_TX_NON_AGGR_SW_RETRY_VI) \ + CFG(CFG_TX_NON_AGGR_SW_RETRY_VO) \ + CFG(CFG_TX_NON_AGGR_SW_RETRY) \ + CFG(CFG_SAP_QOS_UAPSD) + +#endif /* __CFG_MLME_QOS_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_sap_protection.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_sap_protection.h new file mode 100644 index 0000000000000000000000000000000000000000..445439230326e1e0c5ce42919339f686853eda4e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_sap_protection.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of sap erp protection related + * converged configurations. + */ + +#ifndef __CFG_MLME_SAP_PROTECTION_H +#define __CFG_MLME_SAP_PROTECTION_H + +#define CFG_PROTECTION_ENABLED CFG_UINT( \ + "protection_enabled", \ + 0, \ + 65535, \ + 65535, \ + CFG_VALUE_OR_DEFAULT, \ + "sap protection enabled") + +#define CFG_FORCE_POLICY_PROTECTION CFG_UINT( \ + "protection_force_policy", \ + 0, \ + 5, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "force policy protection") + +/* + * + * gignore_peer_ht_opmode + * + * @min 0 + * @max 1 + * @default 1 + * + * Enabling gignore_peer_ht_opmode will enable 11g + * protection only when there is a 11g AP in vicinity. + * + * Related: None + * + * Supported Feature: SAP Protection + * + */ +#define CFG_IGNORE_PEER_HT_MODE CFG_INI_BOOL( \ + "gignore_peer_ht_opmode", \ + false, \ + "ignore the peer ht mode") + +/* + * + * gEnableApProt - Enable/Disable AP protection + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable AP protection + * + * Related: None. + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_AP_ENABLE_PROTECTION_MODE CFG_INI_BOOL( \ + "gEnableApProt", \ + true, \ + "enable protection on sap") + +/* + * + * gApProtection - Set AP protection parameter + * @Min: 0x0 + * @Max: 0xFFFF + * @Default: 0xBFFF + * + * This ini is used to set AP protection parameter + * Bit map for CFG_AP_PROTECTION_MODE_DEFAULT + * LOWER byte for associated stations + * UPPER byte for overlapping stations + * each byte will have the following info + * bit15 bit14 bit13 bit12 bit11 bit10 bit9 bit8 + * OBSS RIFS LSIG_TXOP NON_GF HT20 FROM_11G FROM_11B FROM_11A + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * OBSS RIFS LSIG_TXOP NON_GF HT_20 FROM_11G FROM_11B FROM_11A + * + * Related: None. + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_AP_PROTECTION_MODE CFG_INI_UINT( \ + "gApProtection", \ + 0x0, \ + 0xFFFF, \ + 0xBFFF, \ + CFG_VALUE_OR_DEFAULT, \ + "AP protection mode bitmap") + +/* + * + * gEnableApOBSSProt - Enable/Disable AP OBSS protection + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable AP OBSS protection + * + * Related: None. + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ +#define CFG_AP_OBSS_PROTECTION_ENABLE CFG_INI_BOOL( \ + "gEnableApOBSSProt", \ + false, \ + "Enable/Disable AP OBSS protection") + +#define CFG_SAP_PROTECTION_ALL \ + CFG(CFG_PROTECTION_ENABLED) \ + CFG(CFG_FORCE_POLICY_PROTECTION) \ + CFG(CFG_IGNORE_PEER_HT_MODE) \ + CFG(CFG_AP_ENABLE_PROTECTION_MODE) \ + CFG(CFG_AP_PROTECTION_MODE) \ + CFG(CFG_AP_OBSS_PROTECTION_ENABLE) + +#endif /* __CFG_MLME_SAP_PROTECTION_H */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_ext_mlme_obj_types.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_ext_mlme_obj_types.h new file mode 100644 index 0000000000000000000000000000000000000000..73e9e5424a5fabd7f9fc703e30b089bf5bd133e3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_ext_mlme_obj_types.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: wlan_ext_mlme_obj_types.h + * + * This header file is included by the converged control path mlme + * component to map legacy private structures to the external + * (ext)typedefs used by the converged code. This mechanism allows the + * legacy structs to be included as part of the global objmgr objects. + */ + +#ifndef __WLAN_EXT_MLME_OBJ_TYPE_H__ +#define __WLAN_EXT_MLME_OBJ_TYPE_H__ + +/** + * typedef mlme_pdev_ext_t - Opaque definition of pdev mlme pointer + * Define ext_pdev_ptr from external umac/mlme component point to this type + */ +struct opaque_mlme_pdev_ext; +typedef struct opaque_mlme_pdev_ext mlme_pdev_ext_t; + +/** + * typedef mlme_vdev_ext_t - Definition of vdev mlme pointer + * Define ext_vdev_ptr from external umac/mlme component point to this type + */ +typedef struct mlme_legacy_priv mlme_vdev_ext_t; + +/** + * typedef mlme_psoc_ext_t - Definition of psoc mlme pointer + * Define ext_psoc_ptr from external umac/mlme component point to this type + */ +typedef struct wlan_mlme_psoc_ext_obj mlme_psoc_ext_t; + +#endif diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_api.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_api.h new file mode 100644 index 0000000000000000000000000000000000000000..6139aface233bd1391525ff5b481cf96a5e028c6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_api.h @@ -0,0 +1,2581 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare public APIs exposed by the mlme component + */ + +#ifndef _WLAN_MLME_API_H_ +#define _WLAN_MLME_API_H_ + +#include +#include +#include +#include "sme_api.h" + +/** + * wlan_mlme_get_cfg_str() - Copy the uint8_t array for a particular CFG + * @dst: pointer to the destination buffer. + * @cfg_str: pointer to the cfg string structure + * @len: length to be copied + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_E_FAILURE + */ +QDF_STATUS wlan_mlme_get_cfg_str(uint8_t *dst, struct mlme_cfg_str *cfg_str, + qdf_size_t *len); + +/** + * wlan_mlme_set_cfg_str() - Set values for a particular CFG + * @src: pointer to the source buffer. + * @dst_cfg_str: pointer to the cfg string structure to be modified + * @len: length to be written + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_E_FAILURE + */ +QDF_STATUS wlan_mlme_set_cfg_str(uint8_t *src, struct mlme_cfg_str *dst_cfg_str, + qdf_size_t len); + +/** + * wlan_mlme_get_edca_params() - get the EDCA parameters corresponding to the + * edca profile access category + * @edca_params: pointer to mlme edca parameters structure + * @data: data to which the parameter is to be copied + * @edca_ac: edca ac type enum passed to get the cfg value + * + * Return QDF_STATUS_SUCCESS or QDF_STATUS_E_FAILURE + * + */ +QDF_STATUS wlan_mlme_get_edca_params(struct wlan_mlme_edca_params *edca_params, + uint8_t *data, enum e_edca_type edca_ac); + +/** + * wlan_mlme_update_cfg_with_tgt_caps() - Update mlme cfg with tgt caps + * @psoc: pointer to psoc object + * @tgt_caps: Pointer to the mlme related capability structure + * + * Return: None + */ +void +wlan_mlme_update_cfg_with_tgt_caps(struct wlan_objmgr_psoc *psoc, + struct mlme_tgt_caps *tgt_caps); + +/* + * mlme_get_wep_key() - get the wep key to process during auth frame + * @vdev: VDEV object for which the wep key is being requested + * @wep_params: cfg wep parameters structure + * @wep_key_id: default key number + * @default_key: default key to be copied + * @key_len: length of the key to copy + * + * Return QDF_STATUS + */ +QDF_STATUS mlme_get_wep_key(struct wlan_objmgr_vdev *vdev, + struct wlan_mlme_wep_cfg *wep_params, + enum wep_key_id wep_keyid, uint8_t *default_key, + qdf_size_t *key_len); + +/** + * mlme_set_wep_key() - set the wep keys during auth + * @wep_params: cfg wep parametrs structure + * @wep_key_id: default key number that needs to be copied + * @key_to_set: destination buffer to be copied + * @len: size to be copied + */ +QDF_STATUS mlme_set_wep_key(struct wlan_mlme_wep_cfg *wep_params, + enum wep_key_id wep_keyid, uint8_t *key_to_set, + qdf_size_t len); + +/** + * wlan_mlme_get_tx_power() - Get the max tx power in particular band + * @psoc: pointer to psoc object + * @band: 2ghz/5ghz band + * + * Return: value of tx power in the respective band + */ +uint8_t wlan_mlme_get_tx_power(struct wlan_objmgr_psoc *psoc, + enum band_info band); + +/** + * wlan_mlme_get_power_usage() - Get the power usage info + * @psoc: pointer to psoc object + * + * Return: pointer to character array of power usage + */ +char *wlan_mlme_get_power_usage(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_mlme_get_ht_cap_info() - Get the HT cap info config + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_ht_cap_info(struct wlan_objmgr_psoc *psoc, + struct mlme_ht_capabilities_info + *ht_cap_info); + +/** + * wlan_mlme_get_manufacturer_name() - get manufacturer name + * @psoc: pointer to psoc object + * @pbuf: pointer of the buff which will be filled for the caller + * @plen: pointer of max buffer length + * actual length will be returned at this address + * This function gets manufacturer name + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS +wlan_mlme_get_manufacturer_name(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen); + +/** + * wlan_mlme_get_model_number() - get model number + * @psoc: pointer to psoc object + * @pbuf: pointer of the buff which will be filled for the caller + * @plen: pointer of max buffer length + * actual length will be returned at this address + * This function gets model number + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS +wlan_mlme_get_model_number(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen); + +/** + * wlan_mlme_get_model_name() - get model name + * @psoc: pointer to psoc object + * @pbuf: pointer of the buff which will be filled for the caller + * @plen: pointer of max buffer length + * actual length will be returned at this address + * This function gets model name + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS +wlan_mlme_get_model_name(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen); + +/** + * wlan_mlme_get_manufacture_product_name() - get manufacture product name + * @psoc: pointer to psoc object + * @pbuf: pointer of the buff which will be filled for the caller + * @plen: pointer of max buffer length + * actual length will be returned at this address + * This function gets manufacture product name + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS +wlan_mlme_get_manufacture_product_name(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen); + +/** + * wlan_mlme_get_manufacture_product_version() - get manufacture product version + * @psoc: pointer to psoc object + * @pbuf: pointer of the buff which will be filled for the caller + * @plen: pointer of max buffer length + * actual length will be returned at this address + * This function gets manufacture product version + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS +wlan_mlme_get_manufacture_product_version(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen); + +/** + * wlan_mlme_set_ht_cap_info() - Set the HT cap info config + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_ht_cap_info(struct wlan_objmgr_psoc *psoc, + struct mlme_ht_capabilities_info + ht_cap_info); + +/** + * wlan_mlme_get_max_amsdu_num() - get the max amsdu num + * @psoc: pointer to psoc object + * @value: pointer to the value where the max_amsdu num is to be filled + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_get_max_amsdu_num(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_set_max_amsdu_num() - set the max amsdu num + * @psoc: pointer to psoc object + * @value: value to be set for max_amsdu_num + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_set_max_amsdu_num(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * wlan_mlme_get_ht_mpdu_density() - get the ht mpdu density + * @psoc: pointer to psoc object + * @value: pointer to the value where the ht mpdu density is to be filled + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_get_ht_mpdu_density(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_set_ht_mpdu_density() - set the ht mpdu density + * @psoc: pointer to psoc object + * @value: value to be set for ht mpdu density + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_set_ht_mpdu_density(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * wlan_mlme_get_band_capability() - Get the Band capability config + * @psoc: pointer to psoc object + * @band_capability: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_band_capability(struct wlan_objmgr_psoc *psoc, + uint32_t *band_capability); + +/** + * wlan_mlme_set_band_capability() - Set the Band capability config + * @psoc: pointer to psoc object + * @band_capability: Value to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_band_capability(struct wlan_objmgr_psoc *psoc, + uint32_t band_capability); + +/** + * wlan_mlme_get_prevent_link_down() - Get the prevent link down config + * @psoc: pointer to psoc object + * @prevent_link_down: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_prevent_link_down(struct wlan_objmgr_psoc *psoc, + bool *prevent_link_down); + +/** + * wlan_mlme_get_select_5ghz_margin() - Get the select 5Ghz margin config + * @psoc: pointer to psoc object + * @select_5ghz_margin: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_select_5ghz_margin(struct wlan_objmgr_psoc *psoc, + uint8_t *select_5ghz_margin); + +/** + * wlan_mlme_get_rtt_mac_randomization() - Get the RTT MAC randomization config + * @psoc: pointer to psoc object + * @rtt_mac_randomization: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_rtt_mac_randomization(struct wlan_objmgr_psoc *psoc, + bool *rtt_mac_randomization); + +/** + * wlan_mlme_get_crash_inject() - Get the crash inject config + * @psoc: pointer to psoc object + * @crash_inject: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_crash_inject(struct wlan_objmgr_psoc *psoc, + bool *crash_inject); + +/** + * wlan_mlme_get_lpass_support() - Get the LPASS Support config + * @psoc: pointer to psoc object + * @lpass_support: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_lpass_support(struct wlan_objmgr_psoc *psoc, + bool *lpass_support); + +/** + * wlan_mlme_get_self_recovery() - Get the self recovery config + * @psoc: pointer to psoc object + * @self_recovery: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_self_recovery(struct wlan_objmgr_psoc *psoc, + bool *self_recovery); + +/** + * wlan_mlme_get_sub_20_chan_width() - Get the sub 20 chan width config + * @psoc: pointer to psoc object + * @sub_20_chan_width: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sub_20_chan_width(struct wlan_objmgr_psoc *psoc, + uint8_t *sub_20_chan_width); + +/** + * wlan_mlme_get_fw_timeout_crash() - Get the fw timeout crash config + * @psoc: pointer to psoc object + * @fw_timeout_crash: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_fw_timeout_crash(struct wlan_objmgr_psoc *psoc, + bool *fw_timeout_crash); + +/** + * wlan_mlme_get_ito_repeat_count() - Get the fw timeout crash config + * @psoc: pointer to psoc object + * @ito_repeat_count: Pointer to the variable from caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_ito_repeat_count(struct wlan_objmgr_psoc *psoc, + uint8_t *ito_repeat_count); + +/** + * wlan_mlme_get_acs_with_more_param() - Get the acs_with_more_param flag + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_acs_with_more_param(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_auto_channel_weight() - Get the auto channel weight + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_auto_channel_weight(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_vendor_acs_support() - Get the vendor based channel selece + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ + +QDF_STATUS wlan_mlme_get_vendor_acs_support(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_acs_support_for_dfs_ltecoex() - Get the flag for + * acs support for dfs ltecoex + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_acs_support_for_dfs_ltecoex(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_external_acs_policy() - Get the flag for external acs policy + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_external_acs_policy(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * + * wlan_mlme_get_sap_inactivity_override() - Check if sap max inactivity + * override flag is set. + * @psoc: pointer to psoc object + * @sme_config - Sme config struct + * + * Return: QDF Status + */ +void wlan_mlme_get_sap_inactivity_override(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_ignore_peer_ht_mode() - Get the ignore peer ht opmode flag + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_ignore_peer_ht_mode(struct wlan_objmgr_psoc *psoc, + bool *value); +/** + * wlan_mlme_get_tx_chainmask_cck() - Get the tx_chainmask_cfg value + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +QDF_STATUS wlan_mlme_get_tx_chainmask_cck(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_tx_chainmask_1ss() - Get the tx_chainmask_1ss value + * @psoc: pointer to psoc object + * @value: Value that caller needs to get + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +QDF_STATUS wlan_mlme_get_tx_chainmask_1ss(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_get_num_11b_tx_chains() - Get the number of 11b only tx chains + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +QDF_STATUS wlan_mlme_get_num_11b_tx_chains(struct wlan_objmgr_psoc *psoc, + uint16_t *value); + +/** + * wlan_mlme_get_num_11ag_tx_chains() - get the total number of 11a/g tx chains + * @psoc: pointer to psoc object + * @value: Value that caller needs to get + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +QDF_STATUS wlan_mlme_get_num_11ag_tx_chains(struct wlan_objmgr_psoc *psoc, + uint16_t *value); + +/** + * wlan_mlme_get_bt_chain_separation_flag() - get the enable_bt_chain_separation + * flag + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +QDF_STATUS wlan_mlme_get_bt_chain_separation_flag(struct wlan_objmgr_psoc *psoc, + bool *value); +/** + * wlan_mlme_configure_chain_mask() - configure chainmask parameters + * @psoc: pointer to psoc object + * @session_id: vdev_id + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +QDF_STATUS wlan_mlme_configure_chain_mask(struct wlan_objmgr_psoc *psoc, + uint8_t session_id); + +/** + * wlan_mlme_get_listen_interval() - Get listen interval + * @psoc: pointer to psoc object + * @value: Pointer to value that needs to be filled by MLME + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_listen_interval(struct wlan_objmgr_psoc *psoc, + int *value); + +/** + * wlan_mlme_set_sap_listen_interval() - Set the sap listen interval + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_sap_listen_interval(struct wlan_objmgr_psoc *psoc, + int value); + +/** + * wlan_mlme_set_assoc_sta_limit() - Set the assoc sta limit + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_assoc_sta_limit(struct wlan_objmgr_psoc *psoc, + int value); + +/** + * wlan_mlme_get_assoc_sta_limit() - Get the assoc sta limit + * @psoc: pointer to psoc object + * @value: Pointer to value that needs to be filled by MLME + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_assoc_sta_limit(struct wlan_objmgr_psoc *psoc, + int *value); + +/** + * wlan_mlme_set_rmc_action_period_freq() - Set the rmc action period frequency + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_rmc_action_period_freq(struct wlan_objmgr_psoc *psoc, + int value); + +/** + * wlan_mlme_set_sap_get_peer_info() - get the sap get peer info + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_get_peer_info(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_set_sap_get_peer_info() - set the sap get peer info + * @psoc: pointer to psoc object + * @value: value to overwrite the sap get peer info + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_sap_get_peer_info(struct wlan_objmgr_psoc *psoc, + bool value); + +/** + * wlan_mlme_get_sap_bcast_deauth_enabled() - get the enable/disable value + * for broadcast deauth in sap + * @psoc: pointer to psoc object + * @value: Value that needs to get from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_sap_bcast_deauth_enabled(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_sap_allow_all_channels() - get the value of sap allow all + * channels + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_allow_all_channels(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_is_6g_sap_fd_enabled() - get the enable/disable value + * for 6g sap fils discovery + * @psoc: pointer to psoc object + * @value: Value that needs to get from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_is_6g_sap_fd_enabled(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_sap_allow_all_channels() - get the value sap max peers + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_max_peers(struct wlan_objmgr_psoc *psoc, + int *value); + +/** + * wlan_mlme_set_sap_max_peers() - set the value sap max peers + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_sap_max_peers(struct wlan_objmgr_psoc *psoc, + int value); + +/** + * wlan_mlme_get_sap_max_offload_peers() - get the value sap max offload peers + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_max_offload_peers(struct wlan_objmgr_psoc *psoc, + int *value); + +/** + * wlan_mlme_get_sap_max_offload_reorder_buffs() - get the value sap max offload + * reorder buffs. + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_max_offload_reorder_buffs(struct wlan_objmgr_psoc + *psoc, int *value); + +/** + * wlan_mlme_get_sap_chn_switch_bcn_count() - get the value sap max channel + * switch beacon count + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_chn_switch_bcn_count(struct wlan_objmgr_psoc *psoc, + int *value); + +/** + * wlan_mlme_get_sap_chn_switch_mode() - get the sap channel + * switch mode + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_chn_switch_mode(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_sap_internal_restart() - get the sap internal + * restart + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_internal_restart(struct wlan_objmgr_psoc *psoc, + bool *value); +/** + * wlan_mlme_get_sap_max_modulated_dtim() - get the max modulated dtim + * restart + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_max_modulated_dtim(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_get_sap_chan_pref_location() - get the sap chan pref location + * restart + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_chan_pref_location(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_get_sap_country_priority() - get the sap country code priority + * restart + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_country_priority(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_sap_reduced_beacon_interval() - get the sap reduced + * beacon interval + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_reduced_beacon_interval(struct wlan_objmgr_psoc + *psoc, int *value); + +/** + * wlan_mlme_get_sap_chan_switch_rate_enabled() - get the sap rate hostapd + * enabled beacon interval + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_chan_switch_rate_enabled(struct wlan_objmgr_psoc + *psoc, bool *value); + +/** + * wlan_mlme_get_sap_force_11n_for_11ac() - get the sap 11n for 11ac + * + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_sap_force_11n_for_11ac(struct wlan_objmgr_psoc + *psoc, bool *value); + +/** + * wlan_mlme_get_go_force_11n_for_11ac() - get the go 11n for 11ac + * + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_go_force_11n_for_11ac(struct wlan_objmgr_psoc + *psoc, bool *value); + +/** + * wlan_mlme_is_go_11ac_override() - Override 11ac bandwdith for P2P GO + * + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_is_go_11ac_override(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_is_sap_11ac_override() - Override 11ac bandwdith for SAP + * + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_is_sap_11ac_override(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_set_go_11ac_override() - set override 11ac bandwdith for P2P GO + * + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_go_11ac_override(struct wlan_objmgr_psoc *psoc, + bool value); + +/** + * wlan_mlme_set_sap_11ac_override() - set override 11ac bandwdith for SAP + * + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_sap_11ac_override(struct wlan_objmgr_psoc *psoc, + bool value); + +/** + * wlan_mlme_get_oce_sta_enabled_info() - Get the OCE feature enable + * info for STA + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_oce_sta_enabled_info(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_host_scan_abort_support() - Get support for stop all host + * scans service capability. + * @psoc: PSOC object pointer + * + * Return: True if capability is supported, else False + */ +bool wlan_mlme_get_host_scan_abort_support(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_mlme_get_oce_sap_enabled_info() - Get the OCE feature enable + * info for SAP + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_oce_sap_enabled_info(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_update_oce_flags() - Update the oce flags to FW + * @pdev: pointer to pdev object + * + * Return: void + */ +void wlan_mlme_update_oce_flags(struct wlan_objmgr_pdev *pdev); + +#ifdef WLAN_FEATURE_11AX +/** + * wlan_mlme_cfg_get_he_ul_mumimo() - Get the HE Ul Mumio + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_cfg_get_he_ul_mumimo(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_cfg_set_he_ul_mumimo() - Set the HE Ul Mumio + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_cfg_set_he_ul_mumimo(struct wlan_objmgr_psoc *psoc, + uint32_t value); + +/** + * mlme_cfg_get_he_caps() - Get the HE capability info + * @psoc: pointer to psoc object + * @he_cap: Caps that needs to be filled. + * + * Return: QDF Status + */ +QDF_STATUS mlme_cfg_get_he_caps(struct wlan_objmgr_psoc *psoc, + tDot11fIEhe_cap *he_cap); + +/** + * wlan_mlme_cfg_get_enable_ul_mimo() - Get the HE Ul mimo + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_cfg_get_enable_ul_mimo(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_cfg_get_enable_ul_ofdm() - Get enable ul ofdm + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_cfg_get_enable_ul_ofdm(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * mlme_update_tgt_he_caps_in_cfg() - Update tgt he cap in mlme component + * + * @psoc: pointer to psoc object + * @cfg: pointer to config params from target + * + * This api to be used by callers to update + * he caps in mlme. + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +QDF_STATUS mlme_update_tgt_he_caps_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wma_tgt_cfg *cfg); +#endif + +/** + * wlan_mlme_is_ap_prot_enabled() - check if sap protection is enabled + * @psoc: pointer to psoc object + * + * Return: is_ap_prot_enabled flag + */ +bool wlan_mlme_is_ap_prot_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_mlme_get_ap_protection_mode() - Get ap_protection_mode value + * @psoc: pointer to psoc object + * @value: pointer to the value which needs to be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_ap_protection_mode(struct wlan_objmgr_psoc *psoc, + uint16_t *value); + +/** + * wlan_mlme_is_ap_obss_prot_enabled() - Get ap_obss_protection is + * enabled/disabled + * @psoc: pointer to psoc object + * @value: pointer to the value which needs to be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_is_ap_obss_prot_enabled(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_rts_threshold() - Get the RTS threshold config + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_rts_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_set_rts_threshold() - Set the RTS threshold config + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_rts_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t value); + +/** + * wlan_mlme_get_frag_threshold() - Get the Fragmentation threshold + * config + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_frag_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_set_frag_threshold() - Set the Fragmentation threshold + * config + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_frag_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t value); + +/** + * wlan_mlme_get_fils_enabled_info() - Get the fils enable info for driver + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_fils_enabled_info(struct wlan_objmgr_psoc *psoc, + bool *value); +/** + * wlan_mlme_set_fils_enabled_info() - Set the fils enable info for driver + * @psoc: pointer to psoc object + * @value: value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_fils_enabled_info(struct wlan_objmgr_psoc *psoc, + bool value); + +/** + * wlan_mlme_get_tl_delayed_trgr_frm_int() - Get delay interval(in ms) + * of UAPSD auto trigger + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: None + */ +void wlan_mlme_get_tl_delayed_trgr_frm_int(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_dir_ac_vi() - Get TSPEC direction + * for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_dir_ac_vi(struct wlan_objmgr_psoc *psoc, uint8_t *value); + +/** + * wlan_mlme_get_wmm_nom_msdu_size_ac_vi() - Get normal + * MSDU size for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_nom_msdu_size_ac_vi(struct wlan_objmgr_psoc *psoc, + uint16_t *value); + +/** + * wlan_mlme_get_wmm_mean_data_rate_ac_vi() - mean data + * rate for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_mean_data_rate_ac_vi(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_min_phy_rate_ac_vi() - min PHY + * rate for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_min_phy_rate_ac_vi(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_sba_ac_vi() - surplus bandwidth + * allowance for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_sba_ac_vi(struct wlan_objmgr_psoc *psoc, uint16_t *value); + +/** + * wlan_mlme_get_wmm_uapsd_vi_srv_intv() - Get Uapsd service + * interval for video + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_uapsd_vi_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_uapsd_vi_sus_intv() - Get Uapsd suspension + * interval for video + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_uapsd_vi_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_dir_ac_be() - Get TSPEC direction + * for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_dir_ac_be(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_get_wmm_nom_msdu_size_ac_be() - Get normal + * MSDU size for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_nom_msdu_size_ac_be(struct wlan_objmgr_psoc *psoc, + uint16_t *value); + +/** + * wlan_mlme_get_wmm_mean_data_rate_ac_be() - mean data + * rate for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_mean_data_rate_ac_be(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_min_phy_rate_ac_be() - min PHY + * rate for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_min_phy_rate_ac_be(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_sba_ac_be() - surplus bandwidth + * allowance for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_sba_ac_be(struct wlan_objmgr_psoc *psoc, uint16_t *value); + +/** + * wlan_mlme_get_wmm_uapsd_be_srv_intv() - Get Uapsd service + * interval for BE + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_uapsd_be_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_uapsd_be_sus_intv() - Get Uapsd suspension + * interval for BE + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_uapsd_be_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_dir_ac_bk() - Get TSPEC direction + * for BK + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_dir_ac_bk(struct wlan_objmgr_psoc *psoc, uint8_t *value); + +/** + * wlan_mlme_get_wmm_nom_msdu_size_ac_bk() - Get normal + * MSDU size for BK + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_nom_msdu_size_ac_bk(struct wlan_objmgr_psoc *psoc, + uint16_t *value); + +/** + * wlan_mlme_get_wmm_mean_data_rate_ac_bk() - mean data + * rate for BK + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_mean_data_rate_ac_bk(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_min_phy_rate_ac_bk() - min PHY + * rate for BK + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_min_phy_rate_ac_bk(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_sba_ac_bk() - surplus bandwidth + * allowance for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_sba_ac_bk(struct wlan_objmgr_psoc *psoc, uint16_t *value); + +/** + * wlan_mlme_get_wmm_uapsd_bk_srv_intv() - Get Uapsd service + * interval for BK + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_uapsd_bk_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_uapsd_bk_sus_intv() - Get Uapsd suspension + * interval for BK + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_uapsd_bk_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_mode() - Enable WMM feature + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_mode(struct wlan_objmgr_psoc *psoc, uint8_t *value); + +/** + * wlan_mlme_get_80211e_is_enabled() - Enable 802.11e feature + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_80211e_is_enabled(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_get_wmm_uapsd_mask() - setup U-APSD mask for ACs + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_uapsd_mask(struct wlan_objmgr_psoc *psoc, uint8_t *value); + +/** + * wlan_mlme_get_implicit_qos_is_enabled() - Enable implicit QOS + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_implicit_qos_is_enabled(struct wlan_objmgr_psoc *psoc, + bool *value); + +#ifdef FEATURE_WLAN_ESE +/** + * wlan_mlme_get_inactivity_interval() - Infra Inactivity Interval + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: None + */ +void +wlan_mlme_get_inactivity_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value); +#endif + +/** + * wlan_mlme_get_is_ts_burst_size_enable() - Get TS burst size flag + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: None + */ +void wlan_mlme_get_is_ts_burst_size_enable(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_ts_info_ack_policy() - Get TS ack policy + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: None + */ +void wlan_mlme_get_ts_info_ack_policy(struct wlan_objmgr_psoc *psoc, + enum mlme_ts_info_ack_policy *value); + +/** + * wlan_mlme_get_ts_acm_value_for_ac() - Get ACM value for AC + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_ts_acm_value_for_ac(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_get_wmm_dir_ac_vo() - Get TSPEC direction + * for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_dir_ac_vo(struct wlan_objmgr_psoc *psoc, uint8_t *value); + +/** + * wlan_mlme_get_wmm_nom_msdu_size_ac_vo() - Get normal + * MSDU size for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_nom_msdu_size_ac_vo(struct wlan_objmgr_psoc *psoc, + uint16_t *value); + +/** + * wlan_mlme_get_wmm_mean_data_rate_ac_vo() - mean data rate for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_mean_data_rate_ac_vo(struct wlan_objmgr_psoc *psoc, + uint32_t *value); +/** + * wlan_mlme_get_wmm_min_phy_rate_ac_vo() - min PHY + * rate for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_wmm_min_phy_rate_ac_vo(struct wlan_objmgr_psoc *psoc, + uint32_t *value); +/** + * wlan_mlme_get_wmm_sba_ac_vo() - surplus bandwidth allowance for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_sba_ac_vo(struct wlan_objmgr_psoc *psoc, uint16_t *value); + +/** + * wlan_mlme_set_enable_bcast_probe_rsp() - Set enable bcast probe resp info + * @psoc: pointer to psoc object + * @value: value that needs to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_set_enable_bcast_probe_rsp(struct wlan_objmgr_psoc *psoc, + bool value); + +/** + * wlan_mlme_get_wmm_uapsd_vo_srv_intv() - Get Uapsd service + * interval for voice + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_uapsd_vo_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_wmm_uapsd_vo_sus_intv() - Get Uapsd suspension + * interval for voice + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_wmm_uapsd_vo_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_cfg_get_vht_max_mpdu_len() - gets vht max mpdu length from cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_max_mpdu_len(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_cfg_set_vht_max_mpdu_len() - sets vht max mpdu length into cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_max_mpdu_len(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * wlan_mlme_cfg_get_vht_chan_width() - gets vht supported channel width from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_get_vht_chan_width(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_cfg_set_vht_chan_width() - sets vht supported channel width into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_set_vht_chan_width(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * wlan_mlme_cfg_get_vht_chan_width() - sets vht supported channel width into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_get_vht_chan_width(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_cfg_get_vht_ldpc_coding_cap() - gets vht ldpc coding cap from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_get_vht_ldpc_coding_cap(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_cfg_set_vht_ldpc_coding_cap() - sets vht ldpc coding cap into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_set_vht_ldpc_coding_cap(struct wlan_objmgr_psoc *psoc, + bool value); + +/** + * wlan_mlme_cfg_get_vht_short_gi_80mhz() - gets vht short gi 80MHz from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_get_vht_short_gi_80mhz(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_cfg_set_vht_short_gi_80mhz() - sets vht short gi 80MHz into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_set_vht_short_gi_80mhz(struct wlan_objmgr_psoc *psoc, + bool value); + +/** + * wlan_mlme_cfg_get_short_gi_160_mhz() - gets vht short gi 160MHz from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_short_gi_160_mhz(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_cfg_set_short_gi_160_mhz() - sets vht short gi 160MHz into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_short_gi_160_mhz(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_cfg_get_vht_tx_stbc() - gets vht tx stbc from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_tx_stbc(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_cfg_get_vht_rx_stbc() - gets vht rx stbc from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_rx_stbc(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_cfg_set_vht_tx_stbc() - sets vht tx stbc into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_tx_stbc(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_cfg_get_vht_rx_stbc() - gets vht rx stbc from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_rx_stbc(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_cfg_set_vht_rx_stbc() - sets vht rx stbc into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_rx_stbc(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_cfg_get_vht_su_bformer() - gets vht su beam former cap from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_su_bformer(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_cfg_set_vht_su_bformer() - sets vht su beam former cap into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_su_bformer(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_cfg_set_vht_su_bformee() - sets vht su beam formee cap into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_su_bformee(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_cfg_set_vht_tx_bfee_ant_supp() - sets vht Beamformee antenna + * support cap + * into cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_set_vht_tx_bfee_ant_supp(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * wlan_mlme_cfg_get_vht_tx_bfee_ant_supp() - Gets vht Beamformee antenna + * support cap into cfg item + * + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_get_vht_tx_bfee_ant_supp(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_cfg_set_vht_num_sounding_dim() - sets vht no of sounding dimensions + * into cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_set_vht_num_sounding_dim(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * wlan_mlme_cfg_get_vht_mu_bformer() - gets vht mu beam former cap from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_mu_bformer(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_cfg_set_vht_mu_bformer() - sets vht mu beam former cap into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_mu_bformer(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_cfg_get_vht_mu_bformee() - gets vht mu beam formee cap from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_mu_bformee(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_cfg_set_vht_mu_bformee() - sets vht mu beam formee cap into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_mu_bformee(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_cfg_get_vht_txop_ps() - gets vht tx ops ps cap from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_txop_ps(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_cfg_set_vht_txop_ps() - sets vht tx ops ps cap into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_txop_ps(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_cfg_get_vht_ampdu_len_exp() - gets vht max AMPDU length exponent from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_ampdu_len_exp(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_cfg_set_vht_ampdu_len_exp() - sets vht max AMPDU length exponent into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_ampdu_len_exp(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * wlan_mlme_cfg_get_vht_rx_mcs_map() - gets vht rx mcs map from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_get_vht_rx_mcs_map(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_cfg_set_vht_rx_mcs_map() - sets rx mcs map into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_rx_mcs_map(struct wlan_objmgr_psoc *psoc, uint32_t value); + +/** + * wlan_mlme_cfg_get_vht_tx_mcs_map() - gets vht tx mcs map from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_get_vht_tx_mcs_map(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_cfg_set_vht_tx_mcs_map() - sets tx mcs map into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_cfg_set_vht_tx_mcs_map(struct wlan_objmgr_psoc *psoc, + uint32_t value); + +/** + * wlan_mlme_cfg_set_vht_rx_supp_data_rate() - sets rx supported data rate into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_rx_supp_data_rate(struct wlan_objmgr_psoc *psoc, + uint32_t value); + +/** + * wlan_mlme_cfg_set_vht_tx_supp_data_rate() - sets tx supported data rate into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_tx_supp_data_rate(struct wlan_objmgr_psoc *psoc, + uint32_t value); + +/** + * wlan_mlme_cfg_get_vht_basic_mcs_set() - gets basic mcs set from + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_get_vht_basic_mcs_set(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_cfg_set_vht_basic_mcs_set() - sets basic mcs set into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_cfg_set_vht_basic_mcs_set(struct wlan_objmgr_psoc *psoc, + uint32_t value); + +/** + * wlan_mlme_get_vht_enable_tx_bf() - Get vht enable tx bf + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_vht_enable_tx_bf(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_get_vht_tx_su_beamformer() - VHT enable tx su beamformer + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_vht_tx_su_beamformer(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_get_vht_channel_width() - gets Channel width capability + * for 11ac + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_get_vht_channel_width(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_get_vht_rx_mcs_8_9() - VHT Rx MCS capability for 1x1 mode + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_get_vht_rx_mcs_8_9(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_get_vht_tx_mcs_8_9() - VHT Tx MCS capability for 1x1 mode + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_vht_tx_mcs_8_9(struct wlan_objmgr_psoc *psoc, uint8_t *value); + +/** + * wlan_mlme_get_vht_rx_mcs_2x2() - VHT Rx MCS capability for 2x2 mode + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_get_vht_rx_mcs_2x2(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_get_vht_tx_mcs_2x2() - VHT Tx MCS capability for 2x2 mode + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_get_vht_tx_mcs_2x2(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_get_vht20_mcs9() - Enables VHT MCS9 in 20M BW operation + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_vht20_mcs9(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_get_srd_master_mode_for_vdev - Get SRD master mode for vdev + * @psoc: pointer to psoc object + * @vdev_opmode: vdev operating mode + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_srd_master_mode_for_vdev(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE vdev_opmode, + bool *value); + +/** + * wlan_mlme_get_indoor_support_for_nan - Get indoor channel support for NAN + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_indoor_support_for_nan(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_vht_enable2x2() - Enables/disables VHT Tx/Rx MCS values for 2x2 + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_vht_enable2x2(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_set_vht_enable2x2() - Enables/disables VHT Tx/Rx MCS values for 2x2 + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_set_vht_enable2x2(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_get_vht_enable_paid() - Enables/disables paid feature + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_vht_enable_paid(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_get_vht_enable_gid() - Enables/disables VHT GID feature + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_vht_enable_gid(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_get_vht_for_24ghz() - Enables/disables VHT for 24 ghz + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_vht_for_24ghz(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_set_vht_for_24ghz() - Enables/disables VHT for 24 ghz + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_set_vht_for_24ghz(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_get_vendor_vht_for_24ghz() - nables/disables vendor VHT for 24 ghz + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_vendor_vht_for_24ghz(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * mlme_update_vht_cap() - update vht capabilities + * @psoc: psoc context + * @cfg: data to be set + * + * Return: QDF_STATUS + */ +QDF_STATUS +mlme_update_vht_cap(struct wlan_objmgr_psoc *psoc, struct wma_tgt_vht_cap *cfg); + +/** + * mlme_update_nss_vht_cap() - Update the number of spatial + * streams supported for vht + * @psoc: psoc context + * + * Return: QDF_STATUS + */ +QDF_STATUS mlme_update_nss_vht_cap(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_mlme_is_sap_uapsd_enabled() - Get if SAP UAPSD is enabled/disabled + * @psoc: psoc context + * @value: value to be filled for caller + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_is_sap_uapsd_enabled(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_set_sap_uapsd_flag() - Enable/Disable SAP UAPSD + * @psoc: psoc context + * @value: Enable/Disable control value for sap_uapsd_enabled field + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_mlme_set_sap_uapsd_flag(struct wlan_objmgr_psoc *psoc, + bool value); +/** + * wlan_mlme_is_11h_enabled() - Get the 11h flag + * @psoc: psoc context + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_is_11h_enabled(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_set_11h_enabled() - Set the 11h flag + * @psoc: psoc context + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_set_11h_enabled(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_is_11d_enabled() - Get the 11d flag + * @psoc: psoc context + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_is_11d_enabled(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_ibss_power_save_setup() - Set IBSS power save params + * @psoc: pointer to psoc object + * @vdev_id: IBSS Vdev ID + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_ibss_power_save_setup(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id); + +/** + * wlan_mlme_set_11d_enabled() - Set the 11h flag + * @psoc: psoc context + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_set_11d_enabled(struct wlan_objmgr_psoc *psoc, bool value); + +/** + * wlan_mlme_get_sta_miracast_mcc_rest_time() - Get STA/MIRACAST MCC rest time + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * This API gives rest time to be used when STA and MIRACAST MCC conc happens + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_sta_miracast_mcc_rest_time(struct wlan_objmgr_psoc *psoc, + uint32_t *value); +/** + * wlan_mlme_get_sap_mcc_chnl_avoid() - Check if SAP MCC needs to be avoided + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * This API fetches the user setting to determine if SAP MCC with other persona + * to be avoided. + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_sap_mcc_chnl_avoid(struct wlan_objmgr_psoc *psoc, + uint8_t *value); +/** + * wlan_mlme_get_mcc_bcast_prob_resp() - Get broadcast probe rsp in MCC + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * To get INI value which helps to determe whether to enable/disable use of + * broadcast probe response to increase the detectability of SAP in MCC mode. + * + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_mcc_bcast_prob_resp(struct wlan_objmgr_psoc *psoc, + uint8_t *value); +/** + * wlan_mlme_get_mcc_rts_cts_prot() - To get RTS-CTS protection in MCC. + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * To get INI value which helps to determine whether to enable/disable + * use of long duration RTS-CTS protection when SAP goes off + * channel in MCC mode. + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_mcc_rts_cts_prot(struct wlan_objmgr_psoc *psoc, + uint8_t *value); +/** + * wlan_mlme_get_mcc_feature() - To find out to enable/disable MCC feature + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * To get INI value which helps to determine whether to enable MCC feature + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_mcc_feature(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * wlan_mlme_get_rrm_enabled() - Get the RRM enabled ini + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS wlan_mlme_get_rrm_enabled(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_dtim_selection_diversity() - get dtim selection diversity + * bitmap + * @psoc: pointer to psoc object + * @dtim_selection_div: value that is requested by the caller + * This function gets the dtim selection diversity bitmap to be + * sent to the firmware + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS wlan_mlme_get_dtim_selection_diversity(struct wlan_objmgr_psoc *psoc, + uint32_t *dtim_selection_div); + +/** + * wlan_mlme_get_bmps_min_listen_interval() - get beacon mode powersave + * minimum listen interval value + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS wlan_mlme_get_bmps_min_listen_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_bmps_max_listen_interval() - get beacon mode powersave + * maximum listen interval value + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS wlan_mlme_get_bmps_max_listen_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_auto_bmps_timer_value() - get bmps timer value + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS wlan_mlme_get_auto_bmps_timer_value(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_is_bmps_enabled() - check if beacon mode powersave is + * enabled/disabled + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS wlan_mlme_is_bmps_enabled(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_override_bmps_imps() - disable imps/bmps + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS wlan_mlme_override_bmps_imps(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_mlme_is_imps_enabled() - check if idle mode powersave is + * enabled/disabled + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS wlan_mlme_is_imps_enabled(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * wlan_mlme_get_wps_uuid() - get the wps uuid string + * @wps_params: pointer to mlme wps parameters structure + * @data: data to which the parameter is to be copied + * + * Return None + * + */ +void +wlan_mlme_get_wps_uuid(struct wlan_mlme_wps_params *wps_params, uint8_t *data); + +/** + * wlan_mlme_get_self_gen_frm_pwr() - get self gen frm pwr + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_self_gen_frm_pwr(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * wlan_mlme_get_4way_hs_offload() - get 4-way hs offload to fw cfg + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_4way_hs_offload(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * wlan_mlme_get_bmiss_skip_full_scan_value() - To get value of + * bmiss_skip_full_scan ini + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_bmiss_skip_full_scan_value(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * mlme_get_peer_phymode() - get phymode of peer + * @psoc: pointer to psoc object + * @mac: Pointer to the mac addr of the peer + * @peer_phymode: phymode + * + * Return: QDF Status + */ +QDF_STATUS +mlme_get_peer_phymode(struct wlan_objmgr_psoc *psoc, uint8_t *mac, + enum wlan_phymode *peer_phymode); + +/** + * mlme_set_tgt_wpa3_roam_cap() - Set the target WPA3 roam support + * to mlme + * @psoc: pointer to PSOC object + * @akm_bitmap: Bitmap of akm suites supported for roaming by the firmware + * + * Return: QDF Status + */ +QDF_STATUS mlme_set_tgt_wpa3_roam_cap(struct wlan_objmgr_psoc *psoc, + uint32_t akm_bitmap); +/** + * wlan_mlme_get_ignore_fw_reg_offload_ind() - Get the + * ignore_fw_reg_offload_ind ini + * @psoc: pointer to psoc object + * @disabled: output pointer to hold user config + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_ignore_fw_reg_offload_ind(struct wlan_objmgr_psoc *psoc, + bool *disabled); + +/** + * mlme_get_roam_trigger_str() - Get the string for enum + * WMI_ROAM_TRIGGER_REASON_ID reason. + * @roam_scan_trigger: roam scan trigger ID + * + * Return: Meaningful string from enum WMI_ROAM_TRIGGER_REASON_ID + */ +char *mlme_get_roam_trigger_str(uint32_t roam_scan_trigger); + +/** + * mlme_get_converted_timestamp() - Return time of the day + * from timestamp + * @timestamp: Timestamp value in milliseconds + * @time: Output buffer to fill time into + * + * Return: Time of the day in [HH:MM:SS.uS] + */ +void mlme_get_converted_timestamp(uint32_t timestamp, char *time); + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * wlan_mlme_set_sae_single_pmk_bss_cap - API to set WPA3 single pmk AP IE + * @psoc: Pointer to psoc object + * @vdev_id: vdev id + * @val: value to be set + * + * Return : None + */ +void wlan_mlme_set_sae_single_pmk_bss_cap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool val); + +/** + * wlan_mlme_update_sae_single_pmk - API to update mlme_pmkid_info + * @vdev: vdev object + * @sae_single_pmk: pointer to sae_single_pmk_info struct + * + * Return : None + */ +void +wlan_mlme_update_sae_single_pmk(struct wlan_objmgr_vdev *vdev, + struct mlme_pmk_info *sae_single_pmk); + +/** + * wlan_mlme_get_sae_single_pmk_info - API to get mlme_pmkid_info + * @vdev: vdev object + * @pmksa: pointer to PMKSA struct + * + * Return : None + */ +void +wlan_mlme_get_sae_single_pmk_info(struct wlan_objmgr_vdev *vdev, + struct wlan_mlme_sae_single_pmk *pmksa); +/** + * wlan_mlme_clear_sae_single_pmk_info - API to clear mlme_pmkid_info ap caps + * @vdev: vdev object + * @pmk : pmk info to clear + * + * Return : None + */ +void wlan_mlme_clear_sae_single_pmk_info(struct wlan_objmgr_vdev *vdev, + struct mlme_pmk_info *pmk); +#else +static inline void +wlan_mlme_set_sae_single_pmk_bss_cap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool val) +{ +} + +static inline void +wlan_mlme_update_sae_single_pmk(struct wlan_objmgr_vdev *vdev, + struct mlme_pmk_info *sae_single_pmk) +{ +} + +static inline void +wlan_mlme_get_sae_single_pmk_info(struct wlan_objmgr_vdev *vdev, + struct wlan_mlme_sae_single_pmk *pmksa) +{ +} + +static inline +void wlan_mlme_clear_sae_single_pmk_info(struct wlan_objmgr_vdev *vdev, + struct mlme_pmk_info *pmk) +{ +} +#endif + +/** + * mlme_get_roam_fail_reason_str() - Get fail string from enum + * WMI_ROAM_FAIL_REASON_ID + * @result: Roam fail reason + * + * Return: Meaningful string from enum + */ +char *mlme_get_roam_fail_reason_str(uint32_t result); + +/** + * mlme_get_sub_reason_str() - Get roam trigger sub reason from enum + * WMI_ROAM_TRIGGER_SUB_REASON_ID + * @sub_reason: Sub reason value + * + * Return: Meaningful string from enum WMI_ROAM_TRIGGER_SUB_REASON_ID + */ +char *mlme_get_sub_reason_str(uint32_t sub_reason); + +/** + * wlan_mlme_get_mgmt_max_retry() - Get the + * max mgmt retry + * @psoc: pointer to psoc object + * @max_retry: output pointer to hold user config + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_mgmt_max_retry(struct wlan_objmgr_psoc *psoc, + uint8_t *max_retry); + +/** + * wlan_mlme_get_status_ring_buffer() - Get the + * status of ring buffer + * @psoc: pointer to psoc object + * @enable_ring_buffer: output pointer to point the configured value of + * ring buffer + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_status_ring_buffer(struct wlan_objmgr_psoc *psoc, + bool *enable_ring_buffer); + +/** + * wlan_mlme_get_peer_unmap_conf() - Indicate if peer unmap confirmation + * support is enabled or disabled + * @psoc: pointer to psoc object + * + * Return: true if peer unmap confirmation support is enabled, else false + */ +bool wlan_mlme_get_peer_unmap_conf(struct wlan_objmgr_psoc *psoc); + +#ifdef WLAN_FEATURE_SAE +/** + * wlan_mlme_get_sae_assoc_retry_count() - Get the sae assoc retry count + * @psoc: pointer to psoc object + * @retry_count: assoc retry count + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_sae_assoc_retry_count(struct wlan_objmgr_psoc *psoc, + uint8_t *retry_count); +/** + * wlan_mlme_get_sae_assoc_retry_count() - Get the sae auth retry count + * @psoc: pointer to psoc object + * @retry_count: auth retry count + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_sae_auth_retry_count(struct wlan_objmgr_psoc *psoc, + uint8_t *retry_count); + +/** + * wlan_mlme_get_sae_roam_auth_retry_count() - Get the sae roam auth retry count + * @psoc: pointer to psoc object + * @retry_count: auth retry count + * + * Return: QDF Status + */ +QDF_STATUS +wlan_mlme_get_sae_roam_auth_retry_count(struct wlan_objmgr_psoc *psoc, + uint8_t *retry_count); + +#else +static inline QDF_STATUS +wlan_mlme_get_sae_assoc_retry_count(struct wlan_objmgr_psoc *psoc, + uint8_t *retry_count) +{ + *retry_count = 0; + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +wlan_mlme_get_sae_auth_retry_count(struct wlan_objmgr_psoc *psoc, + uint8_t *retry_count) +{ + *retry_count = 0; + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +wlan_mlme_get_sae_roam_auth_retry_count(struct wlan_objmgr_psoc *psoc, + uint8_t *retry_count) +{ + *retry_count = 0; + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * wlan_mlme_get_roam_reason_vsie_status() - Indicate if roam reason + * vsie is enabled or disabled + * @psoc: pointer to psoc object + * @roam_reason_vsie_enabled: pointer to hold value of roam reason + * vsie + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_get_roam_reason_vsie_status(struct wlan_objmgr_psoc *psoc, + uint8_t *roam_reason_vsie_enabled); + +/** + * wlan_mlme_set_roam_reason_vsie_status() - Update roam reason vsie status + * @psoc: pointer to psoc object + * @roam_reason_vsie_enabled: value of roam reason vsie + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_mlme_set_roam_reason_vsie_status(struct wlan_objmgr_psoc *psoc, + uint8_t roam_reason_vsie_enabled); + +/** + * wlan_mlme_get_roaming_triggers - Get the roaming triggers bitmap + * @psoc: Pointer to PSOC object + * + * Return: Roaming triggers value + */ +uint32_t wlan_mlme_get_roaming_triggers(struct wlan_objmgr_psoc *psoc); +#else +static inline QDF_STATUS +wlan_mlme_get_roam_reason_vsie_status(struct wlan_objmgr_psoc *psoc, + uint8_t *roam_reason_vsie_enable) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +wlan_mlme_set_roam_reason_vsie_status(struct wlan_objmgr_psoc *psoc, + uint8_t roam_reason_vsie_enable) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline +uint32_t wlan_mlme_get_roaming_triggers(struct wlan_objmgr_psoc *psoc) +{ + return 0xFFFF; +} +#endif + +#endif /* _WLAN_MLME_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_product_details_cfg.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_product_details_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..fb2b01e2ee49e503897b0ff977e3564454c4d98b --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_product_details_cfg.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_MLME_PRODUCT_DETAILS_H__ +#define WLAN_MLME_PRODUCT_DETAILS_H__ + +/* + * manufacturer_name - Set manufacture Name + * @Min_len: 0 + * @Max_len: 63 + * @Default: Qualcomm Atheros + * + * This internal CFG is used to set manufacture name + * + * Related: None + * + * Supported Feature: product details + * + * Usage: Internal + * + */ +#define CFG_MFR_NAME CFG_STRING( \ + "manufacturer_name", \ + 0, \ + WLAN_CFG_MFR_NAME_LEN, \ + "Qualcomm Atheros", \ + "Manufacture name") + +/* + * model_number - Set model number + * @Min_len: 0 + * @Max_len: 31 + * @Default: MN1234 + * + * This internal CFG is used to set model number + * + * Related: None + * + * Supported Feature: product details + * + * Usage: Internal + * + */ +#define CFG_MODEL_NUMBER CFG_STRING( \ + "model_number", \ + 0, \ + WLAN_CFG_MODEL_NUMBER_LEN, \ + "MN1234", \ + "model number") + +/* + * model_name - Set model name + * @Min_len: 0 + * @Max_len: 31 + * @Default: WFR4031 + * + * This internal CFG is used to set model name + * + * Related: None + * + * Supported Feature: product details + * + * Usage: Internal + * + */ +#define CFG_MODEL_NAME CFG_STRING( \ + "model_name", \ + 0, \ + WLAN_CFG_MODEL_NAME_LEN, \ + "WFR4031", \ + "model name") + +/* + * manufacture_product_name - Set manufacture product name + * @Min_len: 0 + * @Max_len: 31 + * @Default: 11n-AP + * + * This internal CFG is used to set manufacture product name + * + * Related: None + * + * Supported Feature: product details + * + * Usage: Internal + * + */ +#define CFG_MFR_PRODUCT_NAME CFG_STRING( \ + "manufacture_product_name", \ + 0, \ + WLAN_CFG_MFR_PRODUCT_NAME_LEN, \ + "11n-AP", \ + "manufacture product name") + +/* + * model_number - Set manufacture product version + * @Min_len: 0 + * @Max_len: 31 + * @Default: SN1234 + * + * This internal CFG is used to set manufacture product version + * + * Related: None + * + * Supported Feature: product details + * + * Usage: Internal + * + */ +#define CFG_MFR_PRODUCT_VERSION CFG_STRING( \ + "manufacture_product_version", \ + 0, \ + WLAN_CFG_MFR_PRODUCT_VERSION_LEN, \ + "SN1234", \ + "manufacture product version") + +#define CFG_MLME_PRODUCT_DETAILS_ALL \ + CFG(CFG_MFR_NAME) \ + CFG(CFG_MODEL_NUMBER) \ + CFG(CFG_MODEL_NAME) \ + CFG(CFG_MFR_PRODUCT_NAME) \ + CFG(CFG_MFR_PRODUCT_VERSION) + +#endif + diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..adfcd8b680823e919da8f063ae698bbacbeb8e2a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h @@ -0,0 +1,2495 @@ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: contains mlme structure definations + */ + +#ifndef _WLAN_MLME_STRUCT_H_ +#define _WLAN_MLME_STRUCT_H_ + +#include +#include +#include +#include + +#define OWE_TRANSITION_OUI_TYPE "\x50\x6f\x9a\x1c" +#define OWE_TRANSITION_OUI_SIZE 4 + +#define CFG_PMKID_MODES_OKC (0x1) +#define CFG_PMKID_MODES_PMKSA_CACHING (0x2) + +#define CFG_VHT_BASIC_MCS_SET_STADEF 0xFFFE + +#define CFG_VHT_RX_MCS_MAP_STAMIN 0 +#define CFG_VHT_RX_MCS_MAP_STAMAX 0xFFFF +#define CFG_VHT_RX_MCS_MAP_STADEF 0xFFFE + +#define CFG_VHT_TX_MCS_MAP_STAMIN 0 +#define CFG_VHT_TX_MCS_MAP_STAMAX 0xFFFF +#define CFG_VHT_TX_MCS_MAP_STADEF 0xFFFE + +#define STA_DOT11_MODE_INDX 0 +#define P2P_DEV_DOT11_MODE_INDX 4 +#define NAN_DISC_DOT11_MODE_INDX 8 +#define OCB_DOT11_MODE_INDX 12 +#define TDLS_DOT11_MODE_INDX 16 +#define NDI_DOT11_MODE_INDX 20 + +/* Roam debugging related macro defines */ +#define MAX_ROAM_DEBUG_BUF_SIZE 250 +#define MAX_ROAM_EVENTS_SUPPORTED 5 +#define ROAM_FAILURE_BUF_SIZE 60 +#define TIME_STRING_LEN 24 + +#define ROAM_CHANNEL_BUF_SIZE 300 +#define LINE_STR "==============================================================" +/* + * MLME_CFG_VHT_CSN_BEAMFORMEE_ANT_SUPPORTED_FW_DEF + 1 is + * assumed to be the default fw supported BF antennas, if fw + * says it supports 8 antennas in rx ready event and if + * gTxBFCsnValue INI value is configured above 3, set + * the same to MLME_CFG_VHT_CSN_BEAMFORMEE_ANT_SUPPORTED. + * Otherwise, fall back and set fw default value[3]. + */ +#define MLME_VHT_CSN_BEAMFORMEE_ANT_SUPPORTED_FW_DEF 3 + +#define CFG_STR_DATA_LEN 17 +#define CFG_EDCA_DATA_LEN 17 +#define CFG_MAX_TX_POWER_2_4_LEN 128 +#define CFG_MAX_TX_POWER_5_LEN 256 +#define CFG_POWER_USAGE_MAX_LEN 4 +#define CFG_MAX_STR_LEN 256 +#define MAX_VENDOR_IES_LEN 1532 + +#define CFG_MAX_PMK_LEN 64 + +#define CFG_VALID_CHANNEL_LIST_STRING_LEN (CFG_VALID_CHANNEL_LIST_LEN * 4) + +#define DEFAULT_ROAM_TRIGGER_BITMAP 0xFFFFFFFF +/** + * struct mlme_cfg_str - generic structure for all mlme CFG string items + * + * @max_len: maximum data length allowed + * @len: valid no. of elements of the data + * @data: uint8_t array to store values + */ +struct mlme_cfg_str { + qdf_size_t max_len; + qdf_size_t len; + uint8_t data[CFG_STR_DATA_LEN]; +}; + +/** + * enum e_edca_type - to index edca params for edca profile + * EDCA profile AC unicast/bcast + * @edca_ani_acbe_local: ani BE unicast + * @edca_ani_acbk_local: ani BK unicast + * @edca_ani_acvi_local: ani VI unicast + * @edca_ani_acvo_local: ani VO unicast + * @edca_ani_acbe_bcast: ani BE bcast + * @edca_ani_acbk_bcast: ani BK bcast + * @edca_ani_acvi_bcast: ani VI bcast + * @edca_ani_acvo_bcast: ani VO bcast + * @edca_wme_acbe_local: wme BE unicast + * @edca_wme_acbk_local: wme BK unicast + * @edca_wme_acvi_local: wme VI unicast + * @edca_wme_acvo_local: wme VO unicast + * @edca_wme_acbe_bcast: wme BE bcast + * @edca_wme_acbk_bcast: wme BK bcast + * @edca_wme_acvi_bcast: wme VI bcast + * @edca_wme_acvo_bcast: wme VO bcast + * @edca_etsi_acbe_local: etsi BE unicast + * @edca_etsi_acbk_local: etsi BK unicast + * @edca_etsi_acvi_local: etsi VI unicast + * @edca_etsi_acvo_local: etsi VO unicast + * @edca_etsi_acbe_bcast: etsi BE bcast + * @edca_etsi_acbk_bcast: etsi BK bcast + * @edca_etsi_acvi_bcast: etsi VI bcast + * @edca_etsi_acvo_bcast: etsi VO bcast + */ +enum e_edca_type { + edca_ani_acbe_local, + edca_ani_acbk_local, + edca_ani_acvi_local, + edca_ani_acvo_local, + edca_ani_acbe_bcast, + edca_ani_acbk_bcast, + edca_ani_acvi_bcast, + edca_ani_acvo_bcast, + edca_wme_acbe_local, + edca_wme_acbk_local, + edca_wme_acvi_local, + edca_wme_acvo_local, + edca_wme_acbe_bcast, + edca_wme_acbk_bcast, + edca_wme_acvi_bcast, + edca_wme_acvo_bcast, + edca_etsi_acbe_local, + edca_etsi_acbk_local, + edca_etsi_acvi_local, + edca_etsi_acvo_local, + edca_etsi_acbe_bcast, + edca_etsi_acbk_bcast, + edca_etsi_acvi_bcast, + edca_etsi_acvo_bcast +}; + +#define CFG_EDCA_PROFILE_ACM_IDX 0 +#define CFG_EDCA_PROFILE_AIFSN_IDX 1 +#define CFG_EDCA_PROFILE_CWMINA_IDX 2 +#define CFG_EDCA_PROFILE_CWMAXA_IDX 4 +#define CFG_EDCA_PROFILE_TXOPA_IDX 6 +#define CFG_EDCA_PROFILE_CWMINB_IDX 7 +#define CFG_EDCA_PROFILE_CWMAXB_IDX 9 +#define CFG_EDCA_PROFILE_TXOPB_IDX 11 +#define CFG_EDCA_PROFILE_CWMING_IDX 12 +#define CFG_EDCA_PROFILE_CWMAXG_IDX 14 +#define CFG_EDCA_PROFILE_TXOPG_IDX 16 + +/** + * struct mlme_edca_ac_vo - cwmin, cwmax and aifs value for edca_ac_vo + * + * @vo_cwmin: cwmin value for voice + * @vo_cwmax: cwmax value for voice + * @vo_aifs: aifs value for voice + */ +struct mlme_edca_ac_vo { + uint32_t vo_cwmin; + uint32_t vo_cwmax; + uint32_t vo_aifs; +}; + +/** + * enum dot11_mode - Dot11 mode of the vdev + * MLME_DOT11_MODE_ALL: vdev supports all dot11 modes + * MLME_DOT11_MODE_11A: vdev just supports 11A mode + * MLME_DOT11_MODE_11B: vdev supports 11B mode, and modes above it + * MLME_DOT11_MODE_11G: vdev supports 11G mode, and modes above it + * MLME_DOT11_MODE_11N: vdev supports 11N mode, and modes above it + * MLME_DOT11_MODE_11G_ONLY: vdev just supports 11G mode + * MLME_DOT11_MODE_1N_ONLYA: vdev just supports 11N mode + * MLME_DOT11_MODE_11AC: vdev supports 11AC mode, and modes above it + * MLME_DOT11_MODE_11AC_ONLY: vdev just supports 11AC mode + * MLME_DOT11_MODE_11AX: vdev supports 11AX mode, and modes above it + * MLME_DOT11_MODE_11AX_ONLY: vdev just supports 11AX mode + */ +enum mlme_dot11_mode { + MLME_DOT11_MODE_ALL, + MLME_DOT11_MODE_11A, + MLME_DOT11_MODE_11B, + MLME_DOT11_MODE_11G, + MLME_DOT11_MODE_11N, + MLME_DOT11_MODE_11G_ONLY, + MLME_DOT11_MODE_11N_ONLY, + MLME_DOT11_MODE_11AC, + MLME_DOT11_MODE_11AC_ONLY, + MLME_DOT11_MODE_11AX, + MLME_DOT11_MODE_11AX_ONLY +}; + +/** + * enum mlme_vdev_dot11_mode - Dot11 mode of the vdev + * MLME_VDEV_DOT11_MODE_AUTO: vdev uses mlme_dot11_mode + * MLME_VDEV_DOT11_MODE_11N: vdev supports 11N mode + * MLME_VDEV_DOT11_MODE_11AC: vdev supports 11AC mode + * MLME_VDEV_DOT11_MODE_11AX: vdev supports 11AX mode + */ +enum mlme_vdev_dot11_mode { + MLME_VDEV_DOT11_MODE_AUTO, + MLME_VDEV_DOT11_MODE_11N, + MLME_VDEV_DOT11_MODE_11AC, + MLME_VDEV_DOT11_MODE_11AX, +}; + +/** + * struct wlan_mlme_dot11_mode - dot11 mode + * + * @dot11_mode: dot11 mode supported + * @vdev_type_dot11_mode: dot11 mode supported by different vdev types + */ +struct wlan_mlme_dot11_mode { + enum mlme_dot11_mode dot11_mode; + uint32_t vdev_type_dot11_mode; +}; + +/** + * enum roam_invoke_source_entity - Source of invoking roam invoke command. + * @USERSPACE_INITIATED: Userspace (supplicant) + * @CONNECTION_MGR_INITIATED: connection mgr initiated. + */ +enum roam_invoke_source_entity { + USERSPACE_INITIATED, + CONNECTION_MGR_INITIATED, +}; + +/** + * struct mlme_roam_after_data_stall - roam invoke entity params + * @roam_invoke_in_progress: is roaming already in progress. + * @source: source of the roam invoke command. + */ +struct mlme_roam_after_data_stall { + bool roam_invoke_in_progress; + enum roam_invoke_source_entity source; +}; + +/** + * struct mlme_edca_ac_vi - cwmin, cwmax and aifs value for edca_ac_vi + * + * @vi_cwmin: cwmin value for video + * @vi_cwmax: cwmax value for video + * @vi_aifs: aifs value for video + */ +struct mlme_edca_ac_vi { + uint32_t vi_cwmin; + uint32_t vi_cwmax; + uint32_t vi_aifs; +}; + +/** + * struct mlme_edca_ac_bk - cwmin, cwmax and aifs value for edca_ac_bk + * + * @bk_cwmin: cwmin value for background + * @bk_cwmax: cwmax value for background + * @bk_aifs: aifs value for background + */ +struct mlme_edca_ac_bk { + uint32_t bk_cwmin; + uint32_t bk_cwmax; + uint32_t bk_aifs; +}; + +/** + * struct mlme_edca_ac_be - cwmin, cwmax and aifs value for edca_ac_be + * + * @be_cwmin: cwmin value for best effort + * @be_cwmax: cwmax value for best effort + * @be_aifs: aifs value for best effort + */ +struct mlme_edca_ac_be { + uint32_t be_cwmin; + uint32_t be_cwmax; + uint32_t be_aifs; +}; + +/** + * enum mlme_ts_info_ack_policy - TS Info Ack Policy + * @TS_INFO_ACK_POLICY_NORMAL_ACK:normal ack + * @TS_INFO_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK: HT immediate block ack + */ +enum mlme_ts_info_ack_policy { + TS_INFO_ACK_POLICY_NORMAL_ACK = 0, + TS_INFO_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK = 1, +}; + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * enum roam_control_requestor - Driver disabled roaming requestor that will + * request the roam module to disable roaming based on the mlme operation + * @RSO_INVALID_REQUESTOR: invalid requestor + * @RSO_START_BSS: disable roaming temporarily due to start bss + * @RSO_CHANNEL_SWITCH: disable roaming due to STA channel switch + * @RSO_CONNECT_START: disable roaming temporarily due to connect + * @RSO_SAP_CHANNEL_CHANGE: disable roaming due to SAP channel change + * @RSO_NDP_CON_ON_NDI: disable roaming due to NDP connection on NDI + */ +enum roam_control_requestor { + RSO_INVALID_REQUESTOR, + RSO_START_BSS = BIT(0), + RSO_CHANNEL_SWITCH = BIT(1), + RSO_CONNECT_START = BIT(2), + RSO_SAP_CHANNEL_CHANGE = BIT(3), + RSO_NDP_CON_ON_NDI = BIT(4), +}; + +/** + * enum roam_offload_state - Roaming module state for each STA vdev. + * @ROAM_DEINIT: Roaming module is not initialized at the + * firmware. + * @ROAM_INIT: Roaming module initialized at the firmware. + * @ROAM_RSO_STARTED: RSO started, firmware can roam to different AP. + * @ROAM_RSO_STOPPED: RSO stopped - roaming module is initialized at firmware, + * but firmware cannot do roaming due to supplicant disabled roaming/driver + * disabled roaming. + */ +enum roam_offload_state { + ROAM_DEINIT, + ROAM_INIT, + ROAM_RSO_STARTED, + ROAM_RSO_STOPPED +}; +#endif + +/** + * struct mlme_edca_params - EDCA pramaters related config items + * + * @ani_acbk_l: EDCA parameters for ANI local access category background + * @ani_acbe_l: EDCA parameters for ANI local access category best effort + * @ani_acvi_l: EDCA parameters for ANI local access category video + * @ani_acvo_l: EDCA parameters for ANI local access category voice + * @ani_acbk_b: EDCA parameters for ANI bcast access category background + * @ani_acbe_b: EDCA parameters for ANI bcast access category best effort + * @ani_acvi_b: EDCA parameters for ANI bcast access category video + * @ani_acvo_b: EDCA parameters for ANI bcast access category voice + * @wme_acbk_l: EDCA parameters for WME local access category background + * @wme_acbe_l: EDCA parameters for WME local access category best effort + * @wme_acvi_l: EDCA parameters for WME local access category video + * @wme_acvo_l: EDCA parameters for WME local access category voice + * @wme_acbk_b: EDCA parameters for WME bcast access category background + * @wme_acbe_b: EDCA parameters for WME bcast access category best effort + * @wme_acvi_b: EDCA parameters for WME bcast access category video + * @wme_acvo_b: EDCA parameters for WME bcast access category voice + * @etsi_acbk_l: EDCA parameters for ETSI local access category background + * @etsi_acbe_l: EDCA parameters for ETSI local access category best effort + * @etsi_acvi_l: EDCA parameters for ETSI local access category video + * @etsi_acvo_l: EDCA parameters for ETSI local access category voice + * @etsi_acbk_b: EDCA parameters for ETSI bcast access category background + * @etsi_acbe_b: EDCA parameters for ETSI bcast access category best effort + * @etsi_acvi_b: EDCA parameters for ETSI bcast access category video + * @etsi_acvo_b: EDCA parameters for ETSI bcast access category voice + * @enable_edca_params: Enable edca parameter + * @mlme_edca_ac_vo: value for edca_ac_vo + * @mlme_edca_ac_vi: value for edca_ac_vi + * @mlme_edca_ac_bk: value for edca_ac_bk + * @mlme_edca_ac_be: value for edca_ac_be + */ +struct wlan_mlme_edca_params { + struct mlme_cfg_str ani_acbk_l; + struct mlme_cfg_str ani_acbe_l; + struct mlme_cfg_str ani_acvi_l; + struct mlme_cfg_str ani_acvo_l; + struct mlme_cfg_str ani_acbk_b; + struct mlme_cfg_str ani_acbe_b; + struct mlme_cfg_str ani_acvi_b; + struct mlme_cfg_str ani_acvo_b; + + struct mlme_cfg_str wme_acbk_l; + struct mlme_cfg_str wme_acbe_l; + struct mlme_cfg_str wme_acvi_l; + struct mlme_cfg_str wme_acvo_l; + struct mlme_cfg_str wme_acbk_b; + struct mlme_cfg_str wme_acbe_b; + struct mlme_cfg_str wme_acvi_b; + struct mlme_cfg_str wme_acvo_b; + + struct mlme_cfg_str etsi_acbk_l; + struct mlme_cfg_str etsi_acbe_l; + struct mlme_cfg_str etsi_acvi_l; + struct mlme_cfg_str etsi_acvo_l; + struct mlme_cfg_str etsi_acbk_b; + struct mlme_cfg_str etsi_acbe_b; + struct mlme_cfg_str etsi_acvi_b; + struct mlme_cfg_str etsi_acvo_b; + + bool enable_edca_params; + struct mlme_edca_ac_vo edca_ac_vo; + struct mlme_edca_ac_vi edca_ac_vi; + struct mlme_edca_ac_bk edca_ac_bk; + struct mlme_edca_ac_be edca_ac_be; +}; + +#define WLAN_CFG_MFR_NAME_LEN (63) +#define WLAN_CFG_MODEL_NUMBER_LEN (31) +#define WLAN_CFG_MODEL_NAME_LEN (31) +#define WLAN_CFG_MFR_PRODUCT_NAME_LEN (31) +#define WLAN_CFG_MFR_PRODUCT_VERSION_LEN (31) + +#define MLME_NUM_WLM_LATENCY_LEVEL 4 +#define MLME_RMENABLEDCAP_MAX_LEN 5 + +/** + * struct mlme_ht_capabilities_info - HT Capabilities Info + * @l_sig_tx_op_protection: L-SIG TXOP Protection Mechanism support + * @stbc_control_frame: STBC Control frame support + * @psmp: PSMP Support + * @dsss_cck_mode_40_mhz: To indicate use of DSSS/CCK in 40Mhz + * @maximal_amsdu_size: Maximum AMSDU Size - 0:3839 octes, 1:7935 octets + * @delayed_ba: Support of Delayed Block Ack + * @rx_stbc: Rx STBC Support - 0:Not Supported, 1: 1SS, 2: 1,2SS, 3: 1,2,3SS + * @tx_stbc: Tx STBC Support + * @short_gi_40_mhz: Short GI Support for HT40 + * @short_gi_20_mhz: Short GI support for HT20 + * @green_field: Support for HT Greenfield PPDUs + * @mimo_power_save: SM Power Save Mode - 0:Static, 1:Dynamic, 3:Disabled, 2:Res + * @supported_channel_width_set: Supported Chan Width - 0:20Mhz, 1:20Mhz & 40Mhz + * @adv_coding_cap: Rx LDPC support + */ +#ifndef ANI_LITTLE_BIT_ENDIAN +struct mlme_ht_capabilities_info { + uint16_t l_sig_tx_op_protection:1; + uint16_t stbc_control_frame:1; + uint16_t psmp:1; + uint16_t dsss_cck_mode_40_mhz:1; + uint16_t maximal_amsdu_size:1; + uint16_t delayed_ba:1; + uint16_t rx_stbc:2; + uint16_t tx_stbc:1; + uint16_t short_gi_40_mhz:1; + uint16_t short_gi_20_mhz:1; + uint16_t green_field:1; + uint16_t mimo_power_save:2; + uint16_t supported_channel_width_set:1; + uint16_t adv_coding_cap:1; +} qdf_packed; +#else +struct mlme_ht_capabilities_info { + uint16_t adv_coding_cap:1; + uint16_t supported_channel_width_set:1; + uint16_t mimo_power_save:2; + uint16_t green_field:1; + uint16_t short_gi_20_mhz:1; + uint16_t short_gi_40_mhz:1; + uint16_t tx_stbc:1; + uint16_t rx_stbc:2; + uint16_t delayed_ba:1; + uint16_t maximal_amsdu_size:1; + uint16_t dsss_cck_mode_40_mhz:1; + uint16_t psmp:1; + uint16_t stbc_control_frame:1; + uint16_t l_sig_tx_op_protection:1; +} qdf_packed; +#endif + +/** + * struct mlme_ht_param_info - HT AMPDU Parameters Info + * @reserved: reserved bits + * @mpdu_density: MPDU Density + * @max_rx_ampdu_factor: Max Rx AMPDU Factor + */ +#ifndef ANI_LITTLE_BIT_ENDIAN +struct mlme_ht_param_info { + uint8_t reserved:3; + uint8_t mpdu_density:3; + uint8_t max_rx_ampdu_factor:2; +} qdf_packed; +#else +struct mlme_ht_param_info { + uint8_t max_rx_ampdu_factor:2; + uint8_t mpdu_density:3; + uint8_t reserved:3; +#endif +} qdf_packed; + +/** + * struct mlme_ht_ext_cap_info - Extended HT Capabilities Info + * reserved_2: Reserved Bits + * mcs_feedback: MCS Feedback Capability + * reserved_1: Reserved Bits + * transition_time: Time needed for transition between 20Mhz and 40 Mhz + * pco: PCO (Phased Coexistence Operation) Support + */ +#ifndef ANI_LITTLE_BIT_ENDIAN +struct mlme_ht_ext_cap_info { + uint16_t reserved_2:6; + uint16_t mcs_feedback:2; + uint16_t reserved_1:5; + uint16_t transition_time:2; + uint16_t pco:1; +} qdf_packed; +#else +struct mlme_ht_ext_cap_info { + uint16_t pco:1; + uint16_t transition_time:2; + uint16_t reserved1:5; + uint16_t mcs_feedback:2; + uint16_t reserved2:6; +} qdf_packed; +#endif + +/** + * struct mlme_ht_info_field_1 - Additional HT IE Field1 + * @service_interval_granularity: Shortest Service Interval + * @controlled_access_only: Access Control for assoc requests + * @rifs_mode: Reduced Interframe Spacing mode + * @recommended_tx_width_set: Recommended Tx Channel Width + * @secondary_channel_offset: Secondary Channel Offset + */ +#ifndef ANI_LITTLE_BIT_ENDIAN +struct mlme_ht_info_field_1 { + uint8_t service_interval_granularity:3; + uint8_t controlled_access_only:1; + uint8_t rifs_mode:1; + uint8_t recommended_tx_width_set:1; + uint8_t secondary_channel_offset:2; +} qdf_packed; +#else +struct mlme_ht_info_field_1 { + uint8_t secondary_channel_offset:2; + uint8_t recommended_tx_width_set:1; + uint8_t rifs_mode:1; + uint8_t controlled_access_only:1; + uint8_t service_interval_granularity:3; +} qdf_packed; +#endif + +/* struct mlme_ht_info_field_2 - Additional HT IE Field2 + * @reserved: reserved bits + * @obss_non_ht_sta_present: Protection for non-HT STAs by Overlapping BSS + * @transmit_burst_limit: Transmit Burst Limit + * @non_gf_devices_present: Non Greenfield devices present + * @op_mode: Operation Mode + */ +#ifndef ANI_LITTLE_BIT_ENDIAN +struct mlme_ht_info_field_2 { + uint16_t reserved:11; + uint16_t obss_non_ht_sta_present:1; + uint16_t transmit_burst_limit:1; + uint16_t non_gf_devices_present:1; + uint16_t op_mode:2; +} qdf_packed; +#else +struct mlme_ht_info_field_2 { + uint16_t op_mode:2; + uint16_t non_gf_devices_present:1; + uint16_t transmit_burst_limit:1; + uint16_t obss_non_ht_sta_present:1; + uint16_t reserved:11; +} qdf_packed; +#endif + +/** + * struct mlme_ht_info_field_3 - Additional HT IE Field3 + * @reserved: reserved bits + * @pco_phase: PCO Phase + * @pco_active: PCO state + * @lsig_txop_protection_full_support: L-Sig TXOP Protection Full Support + * @secondary_beacon: Beacon ID + * @dual_cts_protection: Dual CTS protection Required + * @basic_stbc_mcs: Basic STBC MCS + */ +#ifndef ANI_LITTLE_BIT_ENDIAN +struct mlme_ht_info_field_3 { + uint16_t reserved:4; + uint16_t pco_phase:1; + uint16_t pco_active:1; + uint16_t lsig_txop_protection_full_support:1; + uint16_t secondary_beacon:1; + uint16_t dual_cts_protection:1; + uint16_t basic_stbc_mcs:7; +} qdf_packed; +#else +struct mlme_ht_info_field_3 { + uint16_t basic_stbc_mcs:7; + uint16_t dual_cts_protection:1; + uint16_t secondary_beacon:1; + uint16_t lsig_txop_protection_full_support:1; + uint16_t pco_active:1; + uint16_t pco_phase:1; + uint16_t reserved:4; +} qdf_packed; +#endif + +/** + * struct wlan_mlme_ht_caps - HT Capabilities related config items + * @ht_cap_info: HT capabilities Info Structure + * @ampdu_params: AMPDU parameters + * @ext_cap_info: HT EXT capabilities info + * @info_field_1: HT Information Subset 1 + * @info_field_2: HT Information Subset 2 + * @info_field_3: HT Information Subset 3 + * @short_preamble: Short Preamble support + * @enable_ampdu_ps: Enable AMPDU Power Save + * @enable_smps: Enabled SM Power Save + * @smps : SM Power Save mode + * @max_num_amsdu: Max number of AMSDU + * @tx_ldpc_enable: Enable Tx LDPC + * @short_slot_time_enabled: Enabled/disable short slot time + */ +struct wlan_mlme_ht_caps { + struct mlme_ht_capabilities_info ht_cap_info; + struct mlme_ht_param_info ampdu_params; + struct mlme_ht_ext_cap_info ext_cap_info; + struct mlme_ht_info_field_1 info_field_1; + struct mlme_ht_info_field_2 info_field_2; + struct mlme_ht_info_field_3 info_field_3; + bool short_preamble; + bool enable_ampdu_ps; + bool enable_smps; + uint8_t smps; + uint8_t max_num_amsdu; + uint8_t tx_ldpc_enable; + bool short_slot_time_enabled; +}; + +#define MLME_CFG_WPS_UUID_MAX_LEN 16 +/* + * struct wlan_mlme_wps_params - All wps based related cfg items + * + * @enable_wps - to enable wps + * @wps_state - current wps state + * @wps_version - wps version + * @wps_cfg_method - wps config method + * @wps_primary_device_category - wps primary device category + * @wps_primary_device_oui - primary device OUI + * @wps_device_sub_category - device sub category + * @wps_device_password_id - password id of device + * @wps_uuid - wps uuid to be sent in probe + */ +struct wlan_mlme_wps_params { + uint8_t enable_wps; + uint8_t wps_state; + uint8_t wps_version; + uint32_t wps_cfg_method; + uint32_t wps_primary_device_category; + uint32_t wps_primary_device_oui; + uint16_t wps_device_sub_category; + uint32_t wps_device_password_id; + struct mlme_cfg_str wps_uuid; +}; + +#define MLME_CFG_LISTEN_INTERVAL 1 +#define MLME_CFG_BEACON_INTERVAL_DEF 100 +#define MLME_CFG_TX_MGMT_RATE_DEF 0xFF +#define MLME_CFG_TX_MGMT_2G_RATE_DEF 0xFF +#define MLME_CFG_TX_MGMT_5G_RATE_DEF 0xFF + +/** + * struct wlan_mlme_cfg_sap - SAP related config items + * @cfg_ssid: SSID to be configured + * @beacon_interval: beacon interval + * @dtim_interval: dtim interval + * @listen_interval: listen interval + * @sap_11g_policy: Check if 11g support is enabled + * @assoc_sta_limit: Limit on number of STA associated to SAP + * @enable_lte_coex: Flag for LTE coexistence + * @rmc_action_period_freq: rmc action period frequency + * @rate_tx_mgmt: mgmt frame tx rate + * @rate_tx_mgmt_2g: mgmt frame tx rate for 2G band + * @rate_tx_mgmt_5g: mgmt frame tx rate for 5G band + * @tele_bcn_wakeup_en: beacon wakeup enable/disable + * @tele_bcn_max_li: max listen interval + * @sap_get_peer_info: get peer info + * @sap_allow_all_chan_param_name: allow all channels + * @sap_max_no_peers: Maximum number of peers + * @sap_max_offload_peers: Maximum number of peer offloads + * @sap_max_offload_reorder_buffs: Maximum offload reorder buffs + * @sap_ch_switch_beacon_cnt: Number of beacons to be sent out during CSA + * @sap_internal_restart: flag to check if sap restart is in progress + * @sap_ch_switch_mode: Channel switch test mode enable/disable + * @chan_switch_hostapd_rate_enabled_name: enable/disable skip hostapd rate + * @reduced_beacon_interval: reduced beacon interval value + * @max_li_modulated_dtim_time: Max modulated DTIM time. + * @country_code_priority: Country code priority. + * @sap_pref_chan_location: SAP Preferred channel location. + * @sap_mcc_chnl_avoid: SAP MCC channel avoidance flag + * @sap_11ac_override: Overrirde SAP bandwidth to 11ac + * @go_11ac_override: Override GO bandwidth to 11ac + * @sap_sae_enabled: enable sae in sap mode + * @is_sap_bcast_deauth_enabled: enable bcast deauth for sap + * @is_6g_sap_fd_enabled: enable fils discovery on sap + */ +struct wlan_mlme_cfg_sap { + uint8_t cfg_ssid[WLAN_SSID_MAX_LEN]; + uint8_t cfg_ssid_len; + uint16_t beacon_interval; + uint16_t dtim_interval; + uint16_t listen_interval; + bool sap_11g_policy; + uint8_t assoc_sta_limit; + bool enable_lte_coex; + uint16_t rmc_action_period_freq; + uint8_t rate_tx_mgmt; + uint8_t rate_tx_mgmt_2g; + uint8_t rate_tx_mgmt_5g; + bool tele_bcn_wakeup_en; + uint8_t tele_bcn_max_li; + bool sap_get_peer_info; + bool sap_allow_all_chan_param_name; + uint8_t sap_max_no_peers; + uint8_t sap_max_offload_peers; + uint8_t sap_max_offload_reorder_buffs; + uint8_t sap_ch_switch_beacon_cnt; + bool sap_internal_restart; + bool sap_ch_switch_mode; + bool chan_switch_hostapd_rate_enabled_name; + uint8_t reduced_beacon_interval; + uint8_t max_li_modulated_dtim_time; + bool country_code_priority; + uint8_t sap_pref_chan_location; + bool sap_force_11n_for_11ac; + bool go_force_11n_for_11ac; + bool ap_random_bssid_enable; + uint8_t sap_mcc_chnl_avoid; + bool sap_11ac_override; + bool go_11ac_override; + bool sap_sae_enabled; + bool is_sap_bcast_deauth_enabled; + bool is_6g_sap_fd_enabled; +}; + +/** + * struct wlan_mlme_dfs_cfg - DFS Capabilities related config items + * @dfs_master_capable: Is DFS master mode support enabled + * @dfs_disable_channel_switch: disable channel switch on radar detection + * @dfs_ignore_cac: Disable cac + * @dfs_filter_offload: dfs filter offloaad + * @dfs_beacon_tx_enhanced: enhance dfs beacon tx + * @dfs_prefer_non_dfs: perefer non dfs channel after radar + * @dfs_disable_japan_w53: Disable W53 channels + * @sap_tx_leakage_threshold: sap tx leakage threshold + * @dfs_pri_multiplier: dfs_pri_multiplier for handle missing pulses + */ +struct wlan_mlme_dfs_cfg { + bool dfs_master_capable; + bool dfs_disable_channel_switch; + bool dfs_ignore_cac; + bool dfs_filter_offload; + bool dfs_beacon_tx_enhanced; + bool dfs_prefer_non_dfs; + bool dfs_disable_japan_w53; + uint32_t sap_tx_leakage_threshold; + uint32_t dfs_pri_multiplier; +}; + +/** + * struct wlan_mlme_mbo - Multiband Operation related ini configs + * @mbo_candidate_rssi_thres: candidate AP's min rssi to accept it + * @mbo_current_rssi_thres: Connected AP's rssi threshold below which + * transition is considered + * @mbo_current_rssi_mcc_thres: connected AP's RSSI threshold value to prefer + * against MCC + * @mbo_candidate_rssi_btc_thres: Candidate AP's minimum RSSI threshold to + * prefer it even in BT coex. + */ +struct wlan_mlme_mbo { + int8_t mbo_candidate_rssi_thres; + int8_t mbo_current_rssi_thres; + int8_t mbo_current_rssi_mcc_thres; + int8_t mbo_candidate_rssi_btc_thres; +}; + +/** + * struct wlan_mlme_powersave - Powersave related ini configs + * @is_imps_enabled: flag to enable/disable IMPS + * @is_bmps_enabled: flag to enable/disable BMPS + * @auto_bmps_timer: auto BMPS timer value + * @bmps_min_listen_interval: BMPS listen inteval minimum value + * @bmps_max_listen_interval: BMPS listen interval maximum value + * @dtim_selection_diversity: dtim selection diversity value to be sent to fw + */ +struct wlan_mlme_powersave { + bool is_imps_enabled; + bool is_bmps_enabled; + uint32_t auto_bmps_timer_val; + uint32_t bmps_min_listen_interval; + uint32_t bmps_max_listen_interval; + uint32_t dtim_selection_diversity; +}; + +/** + * struct wlan_mlme_vht_caps - MLME VHT config items + * @supp_chan_width: Supported Channel Width + * @ldpc_coding_cap: LDPC Coding Capability + * @short_gi_80mhz: 80MHz Short Guard Interval + * @short_gi_160mhz: 160MHz Short Guard Interval + * @tx_stbc: Tx STBC cap + * @rx_stbc: Rx STBC cap + * @su_bformer: SU Beamformer cap + * @su_bformee: SU Beamformee cap + * @tx_bfee_ant_supp: Tx beamformee anti supp + * @num_soundingdim: Number of sounding dimensions + * @mu_bformer: MU Beamformer cap + * @txop_ps: Tx OPs in power save + * @htc_vhtc: htc_vht capability + * @link_adap_cap: Link adaptation capability + * @rx_antpattern: Rx Antenna Pattern cap + * @tx_antpattern: Tx Antenna Pattern cap + * @rx_mcs_map: Rx MCS Map + * @tx_mcs_map: Tx MCS Map + * @rx_supp_data_rate: Rx highest supported data rate + * @tx_supp_data_rate: Tx highest supported data rate + * @basic_mcs_set: Basic MCS set + * @enable_txbf_20mhz: enable tx bf for 20mhz + * @channel_width: Channel width capability for 11ac + * @rx_mcs: VHT Rx MCS capability for 1x1 mode + * @tx_mcs: VHT Tx MCS capability for 1x1 mode + * @rx_mcs2x2: VHT Rx MCS capability for 2x2 mode + * @tx_mcs2x2: VHT Tx MCS capability for 2x2 mode + * @enable_vht20_mcs9: Enables VHT MCS9 in 20M BW operation + * @enable2x2: Enables/disables VHT Tx/Rx MCS values for 2x2 + * @enable_mu_bformee: Enables/disables multi-user (MU) + * beam formee capability + * @enable_paid: Enables/disables paid + * @enable_gid: Enables/disables gid + * @b24ghz_band: To control VHT support in 2.4 GHz band + * @vendor_24ghz_band: to control VHT support based on vendor + * ie in 2.4 GHz band + * @ampdu_len_exponent: To handle maximum receive AMPDU ampdu len exponent + * @ampdu_len: To handle maximum receive AMPDU ampdu len + * @tx_bfee_sap: enable tx bfee SAp + * @subfee_vendor_vhtie: enable subfee vendor vht ie + * @tx_bf_cap: Transmit bf capability + * @as_cap: Antenna sharing capability info + * @disable_ldpc_with_txbf_ap: Disable ldpc capability + * @vht_mcs_10_11_supp: VHT MCS 10 & 11 support + */ +struct mlme_vht_capabilities_info { + uint8_t supp_chan_width; + bool ldpc_coding_cap; + bool short_gi_80mhz; + bool short_gi_160mhz; + bool tx_stbc; + bool rx_stbc; + bool su_bformer; + bool su_bformee; + uint8_t tx_bfee_ant_supp; + uint8_t num_soundingdim; + bool mu_bformer; + bool txop_ps; + bool htc_vhtc; + uint8_t link_adap_cap; + bool rx_antpattern; + bool tx_antpattern; + uint32_t rx_mcs_map; + uint32_t tx_mcs_map; + uint32_t rx_supp_data_rate; + uint32_t tx_supp_data_rate; + uint32_t basic_mcs_set; + bool enable_txbf_20mhz; + uint8_t channel_width; + uint32_t rx_mcs; + uint32_t tx_mcs; + uint8_t rx_mcs2x2; + uint8_t tx_mcs2x2; + bool enable_vht20_mcs9; + bool enable2x2; + bool enable_mu_bformee; + bool enable_paid; + bool enable_gid; + bool b24ghz_band; + bool vendor_24ghz_band; + uint8_t ampdu_len_exponent; + uint8_t ampdu_len; + bool tx_bfee_sap; + bool vendor_vhtie; + uint8_t tx_bf_cap; + uint8_t as_cap; + bool disable_ldpc_with_txbf_ap; + bool vht_mcs_10_11_supp; + uint8_t extended_nss_bw_supp; + uint8_t vht_extended_nss_bw_cap; + uint8_t max_nsts_total; + bool restricted_80p80_bw_supp; +}; + +/** + * struct wlan_mlme_vht_caps - VHT Capabilities related config items + * @vht_cap_info: VHT capabilities Info Structure + */ +struct wlan_mlme_vht_caps { + struct mlme_vht_capabilities_info vht_cap_info; +}; + +/** + * struct wlan_mlme_qos - QOS TX/RX aggregation related CFG items + * @tx_aggregation_size: TX aggr size in number of MPDUs + * @tx_aggregation_size_be: No. of MPDUs for BE queue for TX aggr + * @tx_aggregation_size_bk: No. of MPDUs for BK queue for TX aggr + * @tx_aggregation_size_vi: No. of MPDUs for VI queue for TX aggr + * @tx_aggregation_size_vo: No. of MPDUs for VO queue for TX aggr + * @rx_aggregation_size: No. of MPDUs for RX aggr + * @tx_aggr_sw_retry_threshold_be: aggr sw retry threshold for BE + * @tx_aggr_sw_retry_threshold_bk: aggr sw retry threshold for BK + * @tx_aggr_sw_retry_threshold_vi: aggr sw retry threshold for VI + * @tx_aggr_sw_retry_threshold_vo: aggr sw retry threshold for VO + * @tx_aggr_sw_retry_threshold: aggr sw retry threshold + * @tx_non_aggr_sw_retry_threshold_be: non aggr sw retry threshold for BE + * @tx_non_aggr_sw_retry_threshold_bk: non aggr sw retry threshold for BK + * @tx_non_aggr_sw_retry_threshold_vi: non aggr sw retry threshold for VI + * @tx_non_aggr_sw_retry_threshold_vo: non aggr sw retry threshold for VO + * @tx_non_aggr_sw_retry_threshold: non aggr sw retry threshold + * @sap_max_inactivity_override: Override updating ap_sta_inactivity from + * hostapd.conf + * @sap_uapsd_enabled: Flag to enable/disable UAPSD for SAP + */ +struct wlan_mlme_qos { + uint32_t tx_aggregation_size; + uint32_t tx_aggregation_size_be; + uint32_t tx_aggregation_size_bk; + uint32_t tx_aggregation_size_vi; + uint32_t tx_aggregation_size_vo; + uint32_t rx_aggregation_size; + uint32_t tx_aggr_sw_retry_threshold_be; + uint32_t tx_aggr_sw_retry_threshold_bk; + uint32_t tx_aggr_sw_retry_threshold_vi; + uint32_t tx_aggr_sw_retry_threshold_vo; + uint32_t tx_aggr_sw_retry_threshold; + uint32_t tx_non_aggr_sw_retry_threshold_be; + uint32_t tx_non_aggr_sw_retry_threshold_bk; + uint32_t tx_non_aggr_sw_retry_threshold_vi; + uint32_t tx_non_aggr_sw_retry_threshold_vo; + uint32_t tx_non_aggr_sw_retry_threshold; + bool sap_max_inactivity_override; + bool sap_uapsd_enabled; +}; + +#ifdef WLAN_FEATURE_11AX +#define MLME_HE_PPET_LEN 25 +#define WNI_CFG_HE_OPS_BSS_COLOR_MAX 0x3F + +/** + * struct wlan_mlme_he_caps - HE Capabilities related config items + */ +struct wlan_mlme_he_caps { + tDot11fIEhe_cap dot11_he_cap; + tDot11fIEhe_cap he_cap_orig; + uint8_t he_ppet_2g[MLME_HE_PPET_LEN]; + uint8_t he_ppet_5g[MLME_HE_PPET_LEN]; + uint32_t he_ops_basic_mcs_nss; + uint8_t he_dynamic_fragmentation; + uint8_t enable_ul_mimo; + uint8_t enable_ul_ofdm; + uint32_t he_sta_obsspd; +}; +#endif + +/** + * struct wlan_mlme_chain_cfg - Chain info related structure + * @max_tx_chains_2g: max tx chains supported in 2.4ghz band + * @max_rx_chains_2g: max rx chains supported in 2.4ghz band + * @max_tx_chains_5g: max tx chains supported in 5ghz band + * @max_rx_chains_5g: max rx chains supported in 5ghz band + */ +struct wlan_mlme_chain_cfg { + uint8_t max_tx_chains_2g; + uint8_t max_rx_chains_2g; + uint8_t max_tx_chains_5g; + uint8_t max_rx_chains_5g; +}; + +/** + * struct mlme_tgt_caps - mlme related capability coming from target (FW) + * @data_stall_recovery_fw_support: does target supports data stall recovery. + * @stop_all_host_scan_support: Target capability that indicates if the target + * supports stop all host scan request type. + * + * Add all the mlme-tgt related capablities here, and the public API would fill + * the related capability in the required mlme cfg structure. + */ +struct mlme_tgt_caps { + bool data_stall_recovery_fw_support; + bool stop_all_host_scan_support; +}; + +/** + * struct wlan_mlme_rates - RATES related config items + * @cfpPeriod: cfp period info + * @cfpMaxDuration: cfp Max duration info + * @max_htmcs_txdata: max HT mcs info for Tx + * @disable_abg_rate_txdata: disable abg rate info for tx data + * @sap_max_mcs_txdata: sap max mcs info + * @disable_high_ht_mcs_2x2: disable high mcs for 2x2 info + * @supported_11b: supported 11B rates + * @supported_11a: supported 11A rates + * @opr_rate_set: operational rates set + * @ext_opr_rate_set: extended operational rates set + * @supported_mcs_set: supported MCS set + * @basic_mcs_set: basic MCS set + * @current_mcs_set: current MCS set + */ +struct wlan_mlme_rates { + uint8_t cfp_period; + uint16_t cfp_max_duration; + uint16_t max_htmcs_txdata; + bool disable_abg_rate_txdata; + uint16_t sap_max_mcs_txdata; + uint8_t disable_high_ht_mcs_2x2; + struct mlme_cfg_str supported_11b; + struct mlme_cfg_str supported_11a; + struct mlme_cfg_str opr_rate_set; + struct mlme_cfg_str ext_opr_rate_set; + struct mlme_cfg_str supported_mcs_set; + struct mlme_cfg_str basic_mcs_set; + struct mlme_cfg_str current_mcs_set; +}; + + +/* Flags for gLimProtectionControl that is updated in pe session*/ +#define MLME_FORCE_POLICY_PROTECTION_DISABLE 0 +#define MLME_FORCE_POLICY_PROTECTION_CTS 1 +#define MLME_FORCE_POLICY_PROTECTION_RTS 2 +#define MLME_FORCE_POLICY_PROTECTION_DUAL_CTS 3 +#define MLME_FORCE_POLICY_PROTECTION_RTS_ALWAYS 4 +#define MLME_FORCE_POLICY_PROTECTION_AUTO 5 + +/* protection_enabled bits*/ +#define MLME_PROTECTION_ENABLED_FROM_llA 0 +#define MLME_PROTECTION_ENABLED_FROM_llB 1 +#define MLME_PROTECTION_ENABLED_FROM_llG 2 +#define MLME_PROTECTION_ENABLED_HT_20 3 +#define MLME_PROTECTION_ENABLED_NON_GF 4 +#define MLME_PROTECTION_ENABLED_LSIG_TXOP 5 +#define MLME_PROTECTION_ENABLED_RIFS 6 +#define MLME_PROTECTION_ENABLED_OBSS 7 +#define MLME_PROTECTION_ENABLED_OLBC_FROM_llA 8 +#define MLME_PROTECTION_ENABLED_OLBC_FROM_llB 9 +#define MLME_PROTECTION_ENABLED_OLBC_FROM_llG 10 +#define MLME_PROTECTION_ENABLED_OLBC_HT20 11 +#define MLME_PROTECTION_ENABLED_OLBC_NON_GF 12 +#define MLME_PROTECTION_ENABLED_OLBC_LSIG_TXOP 13 +#define MLME_PROTECTION_ENABLED_OLBC_RIFS 14 +#define MLME_PROTECTION_ENABLED_OLBC_OBSS 15 + +/** + * struct wlan_mlme_feature_flag - feature related information + * @accept_short_slot_assoc: enable short sloc feature + * @enable_hcf: enable HCF feature + * @enable_rsn: enable RSN for connection + * @enable_short_preamble_11g: enable short preamble for 11g + * @enable_short_slot_time_11g + * @enable_ampdu: enable AMPDU feature + * @enable_mcc: enable MCC feature + * @mcc_rts_cts_prot: RTS-CTS protection in MCC + * @mcc_bcast_prob_rsp: broadcast Probe Response in MCC + * @channel_bonding_mode: channel bonding mode + * @enable_block_ack: enable block ack feature + * @channel_bonding_mode_24ghz: configures Channel Bonding in 24 GHz + * @channel_bonding_mode_5ghz: configures Channel Bonding in 5 GHz + */ +struct wlan_mlme_feature_flag { + bool accept_short_slot_assoc; + bool enable_hcf; + bool enable_rsn; + bool enable_short_preamble_11g; + bool enable_short_slot_time_11g; + bool enable_ampdu; + bool enable_mcc; + uint8_t mcc_rts_cts_prot; + uint8_t mcc_bcast_prob_rsp; + uint32_t channel_bonding_mode; + uint32_t enable_block_ack; + uint32_t channel_bonding_mode_24ghz; + uint32_t channel_bonding_mode_5ghz; +}; + +/* + * struct wlan_mlme_sap_protection_cfg - SAP erp protection config items + * @ignore_peer_ht_opmode: Ignore the ht opmode of the peer. Dynamic via INI + * @enable_ap_obss_protection: enable/disable AP OBSS protection + * @protection_force_policy: Protection force policy. Static via cfg + * @is_ap_prot_enabled: Enable/disable SAP protection + * @ap_protection_mode: AP protection bitmap + * @protection_enabled: Force enable protection. static via cfg + */ +struct wlan_mlme_sap_protection { + bool ignore_peer_ht_opmode; + bool enable_ap_obss_protection; + uint8_t protection_force_policy; + bool is_ap_prot_enabled; + uint16_t ap_protection_mode; + uint32_t protection_enabled; +}; + +/* + * struct wlan_mlme_chainmask - All chainmask related cfg items + * @txchainmask1x1: To set transmit chainmask + * @rxchainmask1x1: To set rx chainmask + * @tx_chain_mask_cck: Used to enable/disable Cck ChainMask + * @tx_chain_mask_1ss: Enables/disables tx chain Mask1ss + * @num_11b_tx_chains: Number of Tx Chains in 11b mode + * @num_11ag_tx_chains: Number of Tx Chains in 11ag mode + * @tx_chain_mask_2g: Tx chain mask for 2g + * @rx_chain_mask_2g: Tx chain mask for 2g + * @tx_chain_mask_5g: Tx chain mask for 5g + * @rx_chain_mask_5g: Rx chain mask for 5g + * @enable_bt_chain_separation: Enable/Disable BT/WLAN Host chain seperation + */ +struct wlan_mlme_chainmask { + uint8_t txchainmask1x1; + uint8_t rxchainmask1x1; + bool tx_chain_mask_cck; + uint8_t tx_chain_mask_1ss; + uint16_t num_11b_tx_chains; + uint16_t num_11ag_tx_chains; + uint8_t tx_chain_mask_2g; + uint8_t rx_chain_mask_2g; + uint8_t tx_chain_mask_5g; + uint8_t rx_chain_mask_5g; + bool enable_bt_chain_separation; +}; + +/** + * enum wlan_mlme_ratemask_type: Type of PHY for ratemask + * @WLAN_MLME_RATEMASK_TYPE_NO_MASK: no ratemask set + * @WLAN_MLME_RATEMASK_TYPE_CCK: CCK/OFDM rate + * @WLAN_MLEM_RATEMASK_TYPE_HT: HT rate + * @WLAN_MLME_RATEMASK_TYPE_VHT: VHT rate + * @WLAN_MLME_RATEMASK_TYPE_HE: HE rate + * + * This is used for 'type' values in wlan_mlme_ratemask + */ +enum wlan_mlme_ratemask_type { + WLAN_MLME_RATEMASK_TYPE_NO_MASK = 0, + WLAN_MLME_RATEMASK_TYPE_CCK = 1, + WLAN_MLME_RATEMASK_TYPE_HT = 2, + WLAN_MLME_RATEMASK_TYPE_VHT = 3, + WLAN_MLME_RATEMASK_TYPE_HE = 4, + /* keep this last */ + WLAN_MLME_RATEMASK_TYPE_MAX, +}; + +/** + * struct wlan_mlme_ratemask - ratemask config parameters + * @type: Type of PHY the mask to be applied + * @lower32: Lower 32 bits in the 1st 64-bit value + * @higher32: Higher 32 bits in the 1st 64-bit value + * @lower32_2: Lower 32 bits in the 2nd 64-bit value + * @higher32_2: Higher 32 bits in the 2nd 64-bit value + */ +struct wlan_mlme_ratemask { + enum wlan_mlme_ratemask_type type; + uint32_t lower32; + uint32_t higher32; + uint32_t lower32_2; + uint32_t higher32_2; +}; + +/* struct wlan_mlme_generic - Generic CFG config items + * + * @band_capability: HW Band Capability - Both or 2.4G only or 5G only + * @band: Current Band - Internal variable, initialized to INI and updated later + * @select_5ghz_margin: RSSI margin to select 5Ghz over 2.4 Ghz + * @sub_20_chan_width: Sub 20Mhz Channel Width + * @ito_repeat_count: ITO Repeat Count + * @pmf_sa_query_max_retries: PMF query max retries for SAP + * @pmf_sa_query_retry_interval: PMF query retry interval for SAP + * @dropped_pkt_disconnect_thresh: Threshold for dropped pkts before disconnect + * @rtt_mac_randomization: Enable/Disable RTT MAC randomization + * @rtt3_enabled: RTT3 enable or disable info + * @prevent_link_down: Enable/Disable prevention of link down + * @memory_deep_sleep: Enable/Disable memory deep sleep + * @cck_tx_fir_override: Enable/Disable CCK Tx FIR Override + * @crash_inject: Enable/Disable Crash Inject + * @lpass_support: Enable/Disable LPASS Support + * @self_recovery: Enable/Disable Self Recovery + * @sap_dot11mc: Enable/Disable SAP 802.11mc support + * @fatal_event_trigger: Enable/Disable Fatal Events Trigger + * @optimize_ca_event: Enable/Disable Optimization of CA events + * @fw_timeout_crash: Enable/Disable FW Timeout Crash * + * @debug_packet_log: Debug packet log flags + * @enabled_11h: enable 11h flag + * @enabled_11d: enable 11d flag + * @enable_beacon_reception_stats: enable beacon reception stats + * @enable_remove_time_stamp_sync_cmd: Enable remove time stamp sync cmd + * @data_stall_recovery_fw_support: whether FW supports Data stall recovery. + * @enable_change_channel_bandwidth: enable/disable change channel bw in mission + * mode + * @disable_4way_hs_offload: enable/disable 4 way handshake offload to firmware + * @as_enabled: antenna sharing enabled or not (FW capability) + * @mgmt_retry_max: maximum retries for management frame + * @bmiss_skip_full_scan: Decide if full scan can be skipped in firmware if no + * candidate is found in partial scan based on channel map + * @enable_ring_buffer: Decide to enable/disable ring buffer for bug report + * @enable_peer_unmap_conf_support: Indicate whether to send conf for peer unmap + * @stop_all_host_scan_support: Target capability that indicates if the target + * supports stop all host scan request type. + * @sae_connect_retries: sae connect retry bitmask + */ +struct wlan_mlme_generic { + uint32_t band_capability; + uint32_t band; + uint8_t select_5ghz_margin; + uint8_t sub_20_chan_width; + uint8_t ito_repeat_count; + uint8_t pmf_sa_query_max_retries; + uint16_t pmf_sa_query_retry_interval; + uint16_t dropped_pkt_disconnect_thresh; + bool rtt_mac_randomization; + bool rtt3_enabled; + bool prevent_link_down; + bool memory_deep_sleep; + bool cck_tx_fir_override; + bool crash_inject; + bool lpass_support; + bool self_recovery; + bool sap_dot11mc; + bool fatal_event_trigger; + bool optimize_ca_event; + bool fw_timeout_crash; + uint8_t debug_packet_log; + bool enabled_11h; + bool enabled_11d; + bool enable_deauth_to_disassoc_map; + bool enable_beacon_reception_stats; + bool enable_remove_time_stamp_sync_cmd; + bool data_stall_recovery_fw_support; + bool disable_4way_hs_offload; + bool as_enabled; + uint8_t mgmt_retry_max; + bool bmiss_skip_full_scan; + bool enable_ring_buffer; + bool enable_peer_unmap_conf_support; + bool stop_all_host_scan_support; + uint32_t sae_connect_retries; +}; + +/* + * struct wlan_mlme_product_details_cfg - product details config items + * @manufacturer_name: manufacture name + * @model_number: model number + * @model_name: model name + * @manufacture_product_name: manufacture product name + * @manufacture_product_version: manufacture product version + */ +struct wlan_mlme_product_details_cfg { + char manufacturer_name[WLAN_CFG_MFR_NAME_LEN + 1]; + char model_number[WLAN_CFG_MODEL_NUMBER_LEN + 1]; + char model_name[WLAN_CFG_MODEL_NAME_LEN + 1]; + char manufacture_product_name[WLAN_CFG_MFR_PRODUCT_NAME_LEN + 1]; + char manufacture_product_version[WLAN_CFG_MFR_PRODUCT_VERSION_LEN + 1]; +}; + +/* + * struct acs_weight - Normalize ACS weight for mentioned channels + * @chan_freq: frequency of the channel + * @normalize_weight: Normalization factor of the frequency + */ +struct acs_weight { + uint32_t chan_freq; + uint8_t normalize_weight; +}; + +/* + * struct acs_weight_range - Normalize ACS weight for mentioned channel range + * @start_freq: frequency of the start channel + * @end_freq: frequency of the end channel + * @normalize_weight: Normalization factor for freq range + */ +struct acs_weight_range { + uint32_t start_freq; + uint32_t end_freq; + uint8_t normalize_weight; +}; + +#define MAX_ACS_WEIGHT_RANGE 10 +#define MLME_GET_DFS_CHAN_WEIGHT(np_chan_weight) (np_chan_weight & 0x000000FF) + +/* + * struct wlan_mlme_acs - All acs related cfg items + * @is_acs_with_more_param - to enable acs with more param + * @auto_channel_select_weight - to set acs channel weight + * @is_vendor_acs_support - enable application based channel selection + * @is_acs_support_for_dfs_ltecoex - enable channel for dfs and lte coex + * @is_external_acs_policy - control external policy + * @normalize_weight_chan: Weight factor to be considered in ACS + * @normalize_weight_num_chan: Number of freq items for normalization. + * @normalize_weight_range: Frequency range for weight normalization + * @num_weight_range: num of ranges provided by user + * @force_sap_start: Force SAP start when no channel is found suitable + * @np_chan_weightage: Weightage to be given to non preferred channels. + */ +struct wlan_mlme_acs { + bool is_acs_with_more_param; + uint32_t auto_channel_select_weight; + bool is_vendor_acs_support; + bool is_acs_support_for_dfs_ltecoex; + bool is_external_acs_policy; + struct acs_weight normalize_weight_chan[NUM_CHANNELS]; + uint16_t normalize_weight_num_chan; + struct acs_weight_range normalize_weight_range[MAX_ACS_WEIGHT_RANGE]; + uint16_t num_weight_range; + bool force_sap_start; + uint32_t np_chan_weightage; +}; + +/* + * struct wlan_mlme_cfg_twt - All twt related cfg items + * @is_twt_bcast_enabled: twt capability for the session + * @is_twt_enabled: global twt configuration + * @is_twt_responder_enabled: twt responder + * @is_twt_requestor_enabled: twt requestor + * @twt_congestion_timeout: congestion timeout value + */ +struct wlan_mlme_cfg_twt { + bool is_twt_bcast_enabled; + bool is_twt_enabled; + bool is_twt_responder_enabled; + bool is_twt_requestor_enabled; + uint32_t twt_congestion_timeout; +}; + +/** + * struct wlan_mlme_obss_ht40 - OBSS HT40 config items + * @active_dwelltime: obss active dwelltime + * @passive_dwelltime: obss passive dwelltime + * @width_trigger_interval: obss trigger interval + * @passive_per_channel: obss scan passive total duration per channel + * @active_per_channel: obss scan active total duration per channel + * @width_trans_delay: obss width transition delay + * @scan_activity_threshold: obss scan activity threshold + * @is_override_ht20_40_24g: use channel bonding in 2.4 GHz + * @obss_detection_offload_enabled: Enable OBSS detection offload + * @obss_color_collision_offload_enabled: Enable obss color collision + * @bss_color_collision_det_sta: STA BSS color collision detection offload + */ +struct wlan_mlme_obss_ht40 { + uint32_t active_dwelltime; + uint32_t passive_dwelltime; + uint32_t width_trigger_interval; + uint32_t passive_per_channel; + uint32_t active_per_channel; + uint32_t width_trans_delay; + uint32_t scan_activity_threshold; + bool is_override_ht20_40_24g; + bool obss_detection_offload_enabled; + bool obss_color_collision_offload_enabled; + bool bss_color_collision_det_sta; +}; + +/** + * enum dot11p_mode - The 802.11p mode of operation + * @WLAN_HDD_11P_DISABLED: 802.11p mode is disabled + * @WLAN_HDD_11P_STANDALONE: 802.11p-only operation + * @WLAN_HDD_11P_CONCURRENT: 802.11p and WLAN operate concurrently + */ +enum dot11p_mode { + CFG_11P_DISABLED = 0, + CFG_11P_STANDALONE, + CFG_11P_CONCURRENT, +}; + +#define MAX_VDEV_NSS 2 +#define MAX_VDEV_CHAINS 2 + +/** + * struct wlan_mlme_nss_chains - MLME vdev config of nss, and chains + * @num_tx_chains: tx chains of vdev config + * @num_rx_chains: rx chains of vdev config + * @tx_nss: tx nss of vdev config + * @rx_nss: rx nss of vdev config + * @num_tx_chains_11b: number of tx chains in 11b mode + * @num_tx_chains_11g: number of tx chains in 11g mode + * @num_tx_chains_11a: number of tx chains in 11a mode + * @disable_rx_mrc: disable 2 rx chains, in rx nss 1 mode + * @disable_tx_mrc: disable 2 tx chains, in tx nss 1 mode + */ +struct wlan_mlme_nss_chains { + uint32_t num_tx_chains[NSS_CHAINS_BAND_MAX]; + uint32_t num_rx_chains[NSS_CHAINS_BAND_MAX]; + uint32_t tx_nss[NSS_CHAINS_BAND_MAX]; + uint32_t rx_nss[NSS_CHAINS_BAND_MAX]; + uint32_t num_tx_chains_11b; + uint32_t num_tx_chains_11g; + uint32_t num_tx_chains_11a; + bool disable_rx_mrc[NSS_CHAINS_BAND_MAX]; + bool disable_tx_mrc[NSS_CHAINS_BAND_MAX]; +}; + +/** + * enum station_keepalive_method - available keepalive methods for stations + * @MLME_STA_KEEPALIVE_NULL_DATA: null data packet + * @MLME_STA_KEEPALIVE_GRAT_ARP: gratuitous ARP packet + * @MLME_STA_KEEPALIVE_COUNT: number of method options available + */ +enum station_keepalive_method { + MLME_STA_KEEPALIVE_NULL_DATA, + MLME_STA_KEEPALIVE_GRAT_ARP, + /* keep at the end */ + MLME_STA_KEEPALIVE_COUNT +}; + +/** + * struct wlan_mlme_sta_cfg - MLME STA configuration items + * @sta_keep_alive_period: Sends NULL frame to AP period + * @tgt_gtx_usr_cfg: Target gtx user config + * @pmkid_modes: Enable PMKID modes + * @wait_cnf_timeout: Wait assoc cnf timeout + * @sta_miracast_mcc_rest_time: STA+MIRACAST(P2P) MCC rest time + * @dot11p_mode: Set 802.11p mode + * @fils_max_chan_guard_time: Set maximum channel guard time + * @current_rssi: Current rssi + * @deauth_retry_cnt: Deauth retry count + * @ignore_peer_erp_info: Ignore peer infrormation + * @sta_prefer_80mhz_over_160mhz: Set Sta preference to connect in 80HZ/160HZ + * @enable_5g_ebt: Set default 5G early beacon termination + * @deauth_before_connection: Send deauth before connection or not + * @enable_go_cts2self_for_sta: Stop NOA and start using cts2self + * @qcn_ie_support: QCN IE support + * @force_rsne_override: Force rsnie override from user + * @single_tid: Set replay counter for all TID + * @allow_tpc_from_ap: Support for AP power constraint + */ +struct wlan_mlme_sta_cfg { + uint32_t sta_keep_alive_period; + uint32_t tgt_gtx_usr_cfg; + uint32_t pmkid_modes; + uint32_t wait_cnf_timeout; + uint32_t sta_miracast_mcc_rest_time; + enum dot11p_mode dot11p_mode; + uint8_t fils_max_chan_guard_time; + uint8_t current_rssi; + uint8_t deauth_retry_cnt; + bool ignore_peer_erp_info; + bool sta_prefer_80mhz_over_160mhz; + bool enable_5g_ebt; + bool deauth_before_connection; + bool enable_go_cts2self_for_sta; + bool qcn_ie_support; + bool single_tid; + bool allow_tpc_from_ap; + enum station_keepalive_method sta_keepalive_method; +}; + +/** + * struct wlan_mlme_stats_cfg - MLME stats configuration items + * @stats_periodic_display_time: time after which stats will be printed + * @stats_link_speed_rssi_high: rssi link speed, high + * @stats_link_speed_rssi_med: medium rssi link speed + * @stats_link_speed_rssi_low: rssi link speed, low + * @stats_report_max_link_speed_rssi: report speed limit + */ +struct wlan_mlme_stats_cfg { + uint32_t stats_periodic_display_time; + int stats_link_speed_rssi_high; + int stats_link_speed_rssi_med; + int stats_link_speed_rssi_low; + uint32_t stats_report_max_link_speed_rssi; +}; + +/** + * enum roaming_dfs_channel_type - Allow dfs channel in roam + * @CFG_ROAMING_DFS_CHANNEL_DISABLED: Disallow dfs channel in roam + * @CFG_ROAMING_DFS_CHANNEL_ENABLED_NORMAL: Allow dfs channel + * @CFG_ROAMING_DFS_CHANNEL_ENABLED_ACTIVE: Allow dfs channel with active scan + */ +enum roaming_dfs_channel_type { + ROAMING_DFS_CHANNEL_DISABLED, + ROAMING_DFS_CHANNEL_ENABLED_NORMAL, + ROAMING_DFS_CHANNEL_ENABLED_ACTIVE, +}; + +/* + * struct bss_load_trigger - parameters related to bss load triggered roam + * @enabled: flag to check if this trigger is enabled/disabled + * @threshold: Bss load threshold value above which roaming should start + * @sample_time: Time duration in milliseconds for which the bss load value + * should be monitored + * @rssi_threshold_5ghz: RSSI threshold of the current connected AP below which + * roam should be triggered if bss load threshold exceeds the configured value. + * This value is applicable only when we are connected in 5GHz band. + * @rssi_threshold_24ghz: RSSI threshold of the current connected AP below which + * roam should be triggered if bss load threshold exceeds the configured value. + * This value is applicable only when we are connected in 2.4 GHz band. + */ +struct bss_load_trigger { + bool enabled; + uint32_t threshold; + uint32_t sample_time; + int32_t rssi_threshold_5ghz; + int32_t rssi_threshold_24ghz; +}; + +/* + * AKM suites supported by firmware for roaming + */ +#define AKM_FT_SAE 0 +#define AKM_FT_SUITEB_SHA384 1 +#define AKM_FT_FILS 2 +#define AKM_SAE 3 +#define AKM_OWE 4 + +#define LFR3_STA_ROAM_DISABLE_BY_P2P BIT(0) +#define LFR3_STA_ROAM_DISABLE_BY_NAN BIT(1) + +/* + * @mawc_roam_enabled: Enable/Disable MAWC during roaming + * @enable_fast_roam_in_concurrency:Enable LFR roaming on STA during concurrency + * @lfr3_roaming_offload: Enable/disable roam offload feature + * @enable_self_bss_roam: enable roaming to connected BSSID + * @enable_disconnect_roam_offload: enable disassoc/deauth roam scan. + * @enable_idle_roam: flag to enable/disable idle roam in fw + * @idle_roam_rssi_delta: rssi delta of connected ap which is used to + * identify if the AP is idle or in motion + * @idle_roam_inactive_time: Timeout value in seconds, above which the + * connection is idle + * @idle_data_packet_count: data packet count measured during inactive time, + * below which the connection is idle. + * @idle_roam_min_rssi: Minimum rssi of connected AP to be considered for + * idle roam trigger. + * @enable_roam_reason_vsie: Enable/disable incluison of roam reason + * vsie in Re(assoc) frame + * @roam_trigger_bitmap: Bitmap of roaming triggers. + * @sta_roam_disable STA roaming disabled by interfaces + * @early_stop_scan_enable: Set early stop scan + * @enable_5g_band_pref: Enable preference for 5G from INI + * @ese_enabled: Enable ESE feature + * @lfr_enabled: Enable fast roaming + * @mawc_enabled: Enable MAWC + * @fast_transition_enabled: Enable fast transition + * @wes_mode_enabled: Enable WES mode + * @mawc_roam_traffic_threshold: Configure traffic threshold + * @mawc_roam_ap_rssi_threshold: Best AP RSSI threshold + * @mawc_roam_rssi_high_adjust: Adjust MAWC roam high RSSI + * @mawc_roam_rssi_low_adjust: Adjust MAWC roam low RSSI + * @roam_rssi_abs_threshold: The min RSSI of the candidate AP + * @rssi_threshold_offset_5g: Lookup threshold offset for 5G band + * @early_stop_scan_min_threshold: Set early stop scan min + * @early_stop_scan_max_threshold: Set early stop scan max + * @first_scan_bucket_threshold: Set first scan bucket + * @roam_dense_traffic_threshold: Dense traffic threshold + * @roam_dense_rssi_thre_offset: Sets dense roam RSSI threshold diff + * @roam_dense_min_aps: Sets minimum number of AP for dense roam + * @roam_bg_scan_bad_rssi_threshold:RSSI threshold for background roam + * @roam_bg_scan_client_bitmap: Bitmap used to identify the scan clients + * @roam_bg_scan_bad_rssi_offset_2g:RSSI threshold offset for 2G to 5G roam + * @roam_data_rssi_threshold_triggers: triggers of bad data RSSI threshold to + * roam + * @roam_data_rssi_threshold: Bad data RSSI threshold to roam + * @rx_data_inactivity_time: Rx duration to check data RSSI + * @adaptive_roamscan_dwell_mode: Sets dwell time adaptive mode + * @per_roam_enable: To enabled/disable PER based roaming in FW + * @per_roam_config_high_rate_th: Rate at which PER based roam will stop + * @per_roam_config_low_rate_th: Rate at which PER based roam will start + * @per_roam_config_rate_th_percent:Percentage at which FW will issue roam scan + * @per_roam_rest_time: FW will wait once it issues a roam scan. + * @per_roam_monitor_time: Min time to be considered as valid scenario + * @per_roam_min_candidate_rssi: Min roamable AP RSSI for candidate selection + * @lfr3_disallow_duration: Disallow duration before roaming + * @lfr3_rssi_channel_penalization: RSSI penalization + * @lfr3_num_disallowed_aps: Max number of AP's to maintain in LCA list + * @rssi_boost_threshold_5g: Boost threshold above which 5 GHz is favored + * @rssi_boost_factor_5g: Factor by which 5GHz RSSI is boosted + * @max_rssi_boost_5g: Maximum boost that can be applied to 5G RSSI + * @rssi_penalize_threshold_5g: Penalize thres above which 5G isn't favored + * @rssi_penalize_factor_5g: Factor by which 5GHz RSSI is penalizeed + * @max_rssi_penalize_5g: Max penalty that can be applied to 5G RSSI + * @max_num_pre_auth: Configure max number of pre-auth + * @roam_preauth_retry_count: Configure the max number of preauth retry + * @roam_preauth_no_ack_timeout: Configure the no ack timeout period + * @roam_rssi_diff: Enable roam based on rssi + * @roam_scan_offload_enabled: Enable Roam Scan Offload + * @neighbor_scan_timer_period: Neighbor scan timer period + * @neighbor_scan_min_timer_period: Min neighbor scan timer period + * @neighbor_lookup_rssi_threshold: Neighbor lookup rssi threshold + * @opportunistic_scan_threshold_diff: Set oppurtunistic threshold diff + * @roam_rescan_rssi_diff: Sets RSSI for Scan trigger in firmware + * @neighbor_scan_min_chan_time: Neighbor scan channel min time + * @neighbor_scan_max_chan_time: Neighbor scan channel max time + * @neighbor_scan_results_refresh_period: Neighbor scan refresh period + * @empty_scan_refresh_period: Empty scan refresh period + * @roam_bmiss_first_bcnt: First beacon miss count + * @roam_bmiss_final_bcnt: Final beacon miss count + * @roam_beacon_rssi_weight: Beacon miss weight + * @roaming_dfs_channel: Allow dfs channel in roam + * @roam_scan_hi_rssi_maxcount: 5GHz maximum scan count + * @roam_scan_hi_rssi_delta: RSSI Delta for scan trigger + * @roam_scan_hi_rssi_delay: Minimum delay between 5GHz scans + * @roam_scan_hi_rssi_ub: Upper bound after which 5GHz scan + * @roam_prefer_5ghz: Prefer roaming to 5GHz Bss + * @roam_intra_band: Prefer roaming within Band + * @enable_adaptive_11r Flag to check if adaptive 11r ini is enabled + * @tgt_adaptive_11r_cap: Flag to check if target supports adaptive + * 11r + * @enable_ft_im_roaming: Flag to enable/disable FT-IM + * @roam_scan_home_away_time: The home away time to firmware + * @roam_scan_n_probes: The number of probes to be sent for firmware roaming + * @delay_before_vdev_stop:Wait time for tx complete before vdev stop + * @neighbor_scan_channel_list: Neighbor scan channel list + * @neighbor_scan_channel_list_num: Neighbor scan channel list number + * @enable_lfr_subnet_detection: Enable LFR3 subnet detection + * @ho_delay_for_rx: Delay hand-off by this duration to receive + * @min_delay_btw_roam_scans: Min duration + * @roam_trigger_reason_bitmask: Contains roam_trigger_reasons + * @enable_ftopen: Enable/disable FT open feature + * @roam_force_rssi_trigger: Force RSSI trigger or not + * @roaming_scan_policy: Config roaming scan policy in fw + * @roam_scan_inactivity_time: Device inactivity monitoring time in + * milliseconds for which the device is considered to be inactive. + * @roam_inactive_data_packet_count: Maximum allowed data packets count + * during roam_scan_inactivity_time. + * @roam_scan_period_after_inactivity: Roam scan period after device was in + * inactive state + * @fw_akm_bitmap: Supported Akm suites of firmware + * @roam_full_scan_period: Idle period in seconds between two successive + * full channel roam scans + * @sae_single_pmk_feature_enabled: Contains value of ini + * sae_single_pmk_feature_enabled + */ +struct wlan_mlme_lfr_cfg { + bool mawc_roam_enabled; + bool enable_fast_roam_in_concurrency; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + bool lfr3_roaming_offload; + bool enable_self_bss_roam; + bool enable_disconnect_roam_offload; + bool enable_idle_roam; + uint32_t idle_roam_rssi_delta; + uint32_t idle_roam_inactive_time; + uint32_t idle_data_packet_count; + uint32_t idle_roam_band; + int32_t idle_roam_min_rssi; + bool enable_roam_reason_vsie; + uint32_t roam_trigger_bitmap; + uint32_t sta_roam_disable; +#endif + bool early_stop_scan_enable; + bool enable_5g_band_pref; +#ifdef FEATURE_WLAN_ESE + bool ese_enabled; +#endif + bool lfr_enabled; + bool mawc_enabled; + bool fast_transition_enabled; + bool wes_mode_enabled; + uint32_t mawc_roam_traffic_threshold; + uint32_t mawc_roam_ap_rssi_threshold; + uint32_t mawc_roam_rssi_high_adjust; + uint32_t mawc_roam_rssi_low_adjust; + uint32_t roam_rssi_abs_threshold; + uint8_t rssi_threshold_offset_5g; + uint8_t early_stop_scan_min_threshold; + uint8_t early_stop_scan_max_threshold; + uint8_t first_scan_bucket_threshold; + uint32_t roam_dense_traffic_threshold; + uint32_t roam_dense_rssi_thre_offset; + uint32_t roam_dense_min_aps; + uint32_t roam_bg_scan_bad_rssi_threshold; + uint32_t roam_bg_scan_client_bitmap; + uint32_t roam_bg_scan_bad_rssi_offset_2g; + uint32_t roam_data_rssi_threshold_triggers; + int32_t roam_data_rssi_threshold; + uint32_t rx_data_inactivity_time; + uint32_t adaptive_roamscan_dwell_mode; + uint32_t per_roam_enable; + uint32_t per_roam_config_high_rate_th; + uint32_t per_roam_config_low_rate_th; + uint32_t per_roam_config_rate_th_percent; + uint32_t per_roam_rest_time; + uint32_t per_roam_monitor_time; + uint32_t per_roam_min_candidate_rssi; + uint32_t lfr3_disallow_duration; + uint32_t lfr3_rssi_channel_penalization; + uint32_t lfr3_num_disallowed_aps; + uint32_t rssi_boost_threshold_5g; + uint32_t rssi_boost_factor_5g; + uint32_t max_rssi_boost_5g; + uint32_t rssi_penalize_threshold_5g; + uint32_t rssi_penalize_factor_5g; + uint32_t max_rssi_penalize_5g; + uint32_t max_num_pre_auth; + uint32_t roam_preauth_retry_count; + uint32_t roam_preauth_no_ack_timeout; + uint8_t roam_rssi_diff; + bool roam_scan_offload_enabled; + uint32_t neighbor_scan_timer_period; + uint32_t neighbor_scan_min_timer_period; + uint32_t neighbor_lookup_rssi_threshold; + uint32_t opportunistic_scan_threshold_diff; + uint32_t roam_rescan_rssi_diff; + uint16_t neighbor_scan_min_chan_time; + uint16_t neighbor_scan_max_chan_time; + uint32_t neighbor_scan_results_refresh_period; + uint32_t empty_scan_refresh_period; + uint8_t roam_bmiss_first_bcnt; + uint8_t roam_bmiss_final_bcnt; + uint32_t roam_beacon_rssi_weight; + enum roaming_dfs_channel_type roaming_dfs_channel; + uint32_t roam_scan_hi_rssi_maxcount; + uint32_t roam_scan_hi_rssi_delta; + uint32_t roam_scan_hi_rssi_delay; + uint32_t roam_scan_hi_rssi_ub; + bool roam_prefer_5ghz; + bool roam_intra_band; +#ifdef WLAN_ADAPTIVE_11R + bool enable_adaptive_11r; + bool tgt_adaptive_11r_cap; +#endif + bool enable_ft_im_roaming; + uint16_t roam_scan_home_away_time; + uint32_t roam_scan_n_probes; + uint8_t delay_before_vdev_stop; + uint8_t neighbor_scan_channel_list[CFG_VALID_CHANNEL_LIST_LEN]; + uint8_t neighbor_scan_channel_list_num; +#ifdef FEATURE_LFR_SUBNET_DETECTION + bool enable_lfr_subnet_detection; +#endif + uint8_t ho_delay_for_rx; + uint8_t min_delay_btw_roam_scans; + uint32_t roam_trigger_reason_bitmask; + bool enable_ftopen; + bool roam_force_rssi_trigger; + struct bss_load_trigger bss_load_trig; + bool roaming_scan_policy; + uint32_t roam_scan_inactivity_time; + uint32_t roam_inactive_data_packet_count; + uint32_t roam_scan_period_after_inactivity; + uint32_t fw_akm_bitmap; + uint32_t roam_full_scan_period; +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) + bool sae_single_pmk_feature_enabled; +#endif +}; + +/** + * struct wlan_mlme_wmm_config - WMM configuration + * @wmm_mode: Enable WMM feature + * @b80211e_is_enabled: Enable 802.11e feature + * @uapsd_mask: what ACs to setup U-APSD for at assoc + * @bimplicit_qos_enabled: Enable implicit QOS + */ +struct wlan_mlme_wmm_config { + uint8_t wmm_mode; + bool b80211e_is_enabled; + uint8_t uapsd_mask; + bool bimplicit_qos_enabled; +}; + +/** + * struct wlan_mlme_wmm_tspec_element - Default TSPEC parameters + * from the wmm spec + * @inactivity_interval: inactivity_interval as per wmm spec + * @burst_size_def: TS burst size + * @ts_ack_policy: TS Info ACK policy + * @ts_acm_is_off: ACM is off for AC + */ +struct wlan_mlme_wmm_tspec_element { +#ifdef FEATURE_WLAN_ESE + uint32_t inactivity_intv; +#endif + bool burst_size_def; + enum mlme_ts_info_ack_policy ts_ack_policy; + bool ts_acm_is_off; +}; + +/** + * struct wlan_mlme_wmm_ac_vo - Default TSPEC parameters + * for AC_VO + * @dir_ac_vo: TSPEC direction for VO + * @nom_msdu_size_ac_vo: normal MSDU size for VO + * @mean_data_rate_ac_vo: mean data rate for VO + * @min_phy_rate_ac_vo: min PHY rate for VO + * @sba_ac_vo: surplus bandwidth allowance for VO + * @uapsd_vo_srv_intv: Uapsd service interval for voice + * @uapsd_vo_sus_intv: Uapsd suspension interval for voice + */ +struct wlan_mlme_wmm_ac_vo { + uint8_t dir_ac_vo; + uint16_t nom_msdu_size_ac_vo; + uint32_t mean_data_rate_ac_vo; + uint32_t min_phy_rate_ac_vo; + uint16_t sba_ac_vo; + uint32_t uapsd_vo_srv_intv; + uint32_t uapsd_vo_sus_intv; +}; + +/** + * struct wlan_mlme_wmm_ac_vi - Default TSPEC parameters + * for AC_VI + * @dir_ac_vi: TSPEC direction for VI + * @nom_msdu_size_ac_vi: normal MSDU size for VI + * @mean_data_rate_ac_vi: mean data rate for VI + * @min_phy_rate_ac_vi: min PHY rate for VI + * @sba_ac_vi: surplus bandwidth allowance for VI + * @uapsd_vo_srv_intv: Uapsd service interval for VI + * @uapsd_vo_sus_intv: Uapsd suspension interval for VI + */ +struct wlan_mlme_wmm_ac_vi { + uint8_t dir_ac_vi; + uint16_t nom_msdu_size_ac_vi; + uint32_t mean_data_rate_ac_vi; + uint32_t min_phy_rate_ac_vi; + uint16_t sba_ac_vi; + uint32_t uapsd_vi_srv_intv; + uint32_t uapsd_vi_sus_intv; +}; + +/** + * struct wlan_mlme_wmm_ac_be - Default TSPEC parameters + * for AC_BE + * @dir_ac_be: TSPEC direction for BE + * @nom_msdu_size_ac_be: normal MSDU size for BE + * @mean_data_rate_ac_be: mean data rate for BE + * @min_phy_rate_ac_be: min PHY rate for BE + * @sba_ac_be: surplus bandwidth allowance for BE + * @uapsd_be_srv_intv: Uapsd service interval for BE + * @uapsd_be_sus_intv: Uapsd suspension interval for BE + + */ +struct wlan_mlme_wmm_ac_be { + uint8_t dir_ac_be; + uint16_t nom_msdu_size_ac_be; + uint32_t mean_data_rate_ac_be; + uint32_t min_phy_rate_ac_be; + uint16_t sba_ac_be; + uint32_t uapsd_be_srv_intv; + uint32_t uapsd_be_sus_intv; + +}; + +/** + * struct wlan_mlme_wmm_ac_bk - Default TSPEC parameters + * for AC_BK + * @dir_ac_bk: TSPEC direction for BK + * @nom_msdu_size_ac_bk: normal MSDU size for BK + * @mean_data_rate_ac_bk: mean data rate for BK + * @min_phy_rate_ac_bk: min PHY rate for BK + * @sba_ac_bk: surplus bandwidth allowance for BK + * @uapsd_bk_srv_intv: Uapsd service interval for BK + * @uapsd_bk_sus_intv: Uapsd suspension interval for BK + */ +struct wlan_mlme_wmm_ac_bk { + uint8_t dir_ac_bk; + uint16_t nom_msdu_size_ac_bk; + uint32_t mean_data_rate_ac_bk; + uint32_t min_phy_rate_ac_bk; + uint16_t sba_ac_bk; + uint32_t uapsd_bk_srv_intv; + uint32_t uapsd_bk_sus_intv; +}; + +/** + * struct wlan_mlme_wmm_params - WMM CFG Items + * @qos_enabled: AP is enabled with 11E + * @wme_enabled: AP is enabled with WMM + * @max_sp_length: Maximum SP Length + * @wsm_enabled: AP is enabled with WSM + * @edca_profile: WMM Edca profile + * @wmm_config: WMM configuration + * @wmm_tspec_element: Default TSPEC parameters + * @ac_vo: Default TSPEC parameters for AC_VO + * @ac_vi: Default TSPEC parameters for AC_VI + * @ac_be: Default TSPEC parameters for AC_BE + * @ac_bk: Default TSPEC parameters for AC_BK + * @delayed_trigger_frm_int: delay int(in ms) of UAPSD auto trigger + */ +struct wlan_mlme_wmm_params { + bool qos_enabled; + bool wme_enabled; + uint8_t max_sp_length; + bool wsm_enabled; + uint32_t edca_profile; + struct wlan_mlme_wmm_config wmm_config; + struct wlan_mlme_wmm_tspec_element wmm_tspec_element; + struct wlan_mlme_wmm_ac_vo ac_vo; + struct wlan_mlme_wmm_ac_vi ac_vi; + struct wlan_mlme_wmm_ac_be ac_be; + struct wlan_mlme_wmm_ac_bk ac_bk; + uint32_t delayed_trigger_frm_int; +}; + +/** + * struct wlan_mlme_weight_config - weight params to + * calculate best candidate + * + * @rssi_weightage: RSSI weightage + * @ht_caps_weightage: HT caps weightage + * @vht_caps_weightage: VHT caps weightage + * @he_caps_weightage: HE caps weightage + * @chan_width_weightage: Channel width weightage + * @chan_band_weightage: Channel band weightage + * @nss_weightage: NSS weightage + * @beamforming_cap_weightage: Beamforming caps weightage + * @pcl_weightage: PCL weightage + * @channel_congestion_weightage: channel congestion weightage + * @oce_wan_weightage: OCE WAN metrics weightage + * @sae_pk_ap_weightage: SAE-PK AP weigtage + */ +struct wlan_mlme_weight_config { + uint8_t rssi_weightage; + uint8_t ht_caps_weightage; + uint8_t vht_caps_weightage; + uint8_t he_caps_weightage; + uint8_t chan_width_weightage; + uint8_t chan_band_weightage; + uint8_t nss_weightage; + uint8_t beamforming_cap_weightage; + uint8_t pcl_weightage; + uint8_t channel_congestion_weightage; + uint8_t oce_wan_weightage; + uint8_t sae_pk_ap_weightage; +}; + +/** + * struct wlan_mlme_rssi_cfg_score - RSSI params to + * calculate best candidate + * + * @best_rssi_threshold: Best RSSI threshold + * @good_rssi_threshold: Good RSSI threshold + * @bad_rssi_threshold: Bad RSSI threshold + * @good_rssi_pcnt: Good RSSI Percentage + * @bad_rssi_pcnt: Bad RSSI Percentage + * @good_rssi_bucket_size: Good RSSI Bucket Size + * @bad_rssi_bucket_size: Bad RSSI Bucket Size + * @rssi_pref_5g_rssi_thresh: Preffered 5G RSSI threshold + */ +struct wlan_mlme_rssi_cfg_score { + uint32_t best_rssi_threshold; + uint32_t good_rssi_threshold; + uint32_t bad_rssi_threshold; + uint32_t good_rssi_pcnt; + uint32_t bad_rssi_pcnt; + uint32_t good_rssi_bucket_size; + uint32_t bad_rssi_bucket_size; + uint32_t rssi_pref_5g_rssi_thresh; +}; + +/** + * struct wlan_mlme_per_slot_scoring - define % score for differents slots + * for a scoring param. + * num_slot: number of slots in which the param will be divided. + * Max 15. index 0 is used for 'not_present. Num_slot will + * equally divide 100. e.g, if num_slot = 4 slot 0 = 0-25%, slot + * 1 = 26-50% slot 2 = 51-75%, slot 3 = 76-100% + * score_pcnt3_to_0: Conatins score percentage for slot 0-3 + * BITS 0-7 :- the scoring pcnt when not present + * BITS 8-15 :- SLOT_1 + * BITS 16-23 :- SLOT_2 + * BITS 24-31 :- SLOT_3 + * score_pcnt7_to_4: Conatins score percentage for slot 4-7 + * BITS 0-7 :- SLOT_4 + * BITS 8-15 :- SLOT_5 + * BITS 16-23 :- SLOT_6 + * BITS 24-31 :- SLOT_7 + * score_pcnt11_to_8: Conatins score percentage for slot 8-11 + * BITS 0-7 :- SLOT_8 + * BITS 8-15 :- SLOT_9 + * BITS 16-23 :- SLOT_10 + * BITS 24-31 :- SLOT_11 + * score_pcnt15_to_12: Conatins score percentage for slot 12-15 + * BITS 0-7 :- SLOT_12 + * BITS 8-15 :- SLOT_13 + * BITS 16-23 :- SLOT_14 + * BITS 24-31 :- SLOT_15 + */ +struct wlan_mlme_per_slot_scoring { + uint32_t num_slot; + uint32_t score_pcnt3_to_0; + uint32_t score_pcnt7_to_4; + uint32_t score_pcnt11_to_8; + uint32_t score_pcnt15_to_12; +}; + +/* + * struct wlan_mlme_score_config - MLME BSS Scoring related config + * @enable_scoring_for_roam: Enable/disable BSS Scoring for Roaming + * @weight_cfg: Various Weight related Scoring Configs + * @rssi_score: RSSI Scoring related thresholds/percentages config + * @esp_qbss_scoring: ESP QBSS Scoring configs + * @oce_wan_scoring: OCE WAN Scoring Configs + * @bandwidth_weight_per_index: Bandwidth weight per index for scoring logic + * @nss_weight_per_index: NSS weight per index for scoring logic + * @band_weight_per_index: Band weight per index for scoring logic + * @roam_trigger_bitmap: bitmap for various roam triggers + * @roam_score_delta: percentage delta in roam score + * @apsd_enabled: Enable automatic power save delivery + * @vendor_roam_score_algorithm: Preferred vendor roam score algorithm + * @min_roam_score_delta: Minimum difference between connected AP's and + * candidate AP's roam score to start roaming. + */ +struct wlan_mlme_scoring_cfg { + bool enable_scoring_for_roam; + struct wlan_mlme_weight_config weight_cfg; + struct wlan_mlme_rssi_cfg_score rssi_score; + struct wlan_mlme_per_slot_scoring esp_qbss_scoring; + struct wlan_mlme_per_slot_scoring oce_wan_scoring; + uint32_t bandwidth_weight_per_index; + uint32_t nss_weight_per_index; + uint32_t band_weight_per_index; + uint32_t roam_trigger_bitmap; + uint32_t roam_score_delta; + bool apsd_enabled; + uint32_t vendor_roam_score_algorithm; + uint32_t min_roam_score_delta; +}; + +/* struct wlan_mlme_threshold - Threshold related config items + * @rts_threshold: set rts threshold + * @frag_threshold: set fragmentation threshold + */ +struct wlan_mlme_threshold { + uint32_t rts_threshold; + uint32_t frag_threshold; +}; + +/* struct mlme_max_tx_power_24 - power related items + * @max_len: max length of string + * @len: actual len of string + * @data: Data in string format + */ +struct mlme_max_tx_power_24 { + qdf_size_t max_len; + qdf_size_t len; + uint8_t data[CFG_MAX_TX_POWER_2_4_LEN]; +}; + +/* struct mlme_max_tx_power_5 - power related items + * @max_len: max length of string + * @len: actual len of string + * @data: Data in string format + */ +struct mlme_max_tx_power_5 { + qdf_size_t max_len; + qdf_size_t len; + uint8_t data[CFG_MAX_TX_POWER_5_LEN]; +}; + +/* struct mlme_power_usage - power related items + * @max_len: max length of string + * @len: actual len of string + * @data: Data in string format + */ +struct mlme_power_usage { + qdf_size_t max_len; + qdf_size_t len; + char data[CFG_POWER_USAGE_MAX_LEN]; +}; + +/* + * struct wlan_mlme_power - power related config items + * @max_tx_power_24: max power Tx for 2.4 ghz, this is based on frequencies + * @max_tx_power_5: max power Tx for 5 ghz, this is based on frequencies + * @max_tx_power_24_chan: max power Tx for 2.4 ghz, this is based on channel + * numbers, this is added to parse the ini values to maintain the backward + * compatibility, these channel numbers are converted to frequencies and copied + * to max_tx_power_24 structure, once this conversion is done this structure + * should not be used. + * @max_tx_power_5_chan: max power Tx for 5 ghz, this is based on channel + * numbers, this is added to parse the ini values to maintain the backward + * compatibility, these channel numbers are converted to frequencies and copied + * to max_tx_power_24 structure, once this conversion is done this structure + * should not be used. + * @power_usage: power usage mode, min, max, mod + * @tx_power_2g: limit tx power in 2.4 ghz + * @tx_power_5g: limit tx power in 5 ghz + * @max_tx_power: WLAN max tx power + * @current_tx_power_level: current tx power level + * @local_power_constraint: local power constraint + */ +struct wlan_mlme_power { + struct mlme_max_tx_power_24 max_tx_power_24; + struct mlme_max_tx_power_5 max_tx_power_5; + struct mlme_max_tx_power_24 max_tx_power_24_chan; + struct mlme_max_tx_power_5 max_tx_power_5_chan; + struct mlme_power_usage power_usage; + uint8_t tx_power_2g; + uint8_t tx_power_5g; + uint8_t max_tx_power; + uint8_t current_tx_power_level; + uint8_t local_power_constraint; +}; + +/* + * struct wlan_mlme_timeout - mlme timeout related config items + * @join_failure_timeout: join failure timeout + * @auth_failure_timeout: authenticate failure timeout + * @auth_rsp_timeout: authenticate response timeout + * @assoc_failure_timeout: assoc failure timeout + * @reassoc_failure_timeout: re-assoc failure timeout + * @probe_after_hb_fail_timeout: Probe after HB fail timeout + * @olbc_detect_timeout: OLBC detect timeout + * @addts_rsp_timeout: ADDTS rsp timeout value + * @heart_beat_threshold: Heart beat threshold + * @ap_keep_alive_timeout: AP keep alive timeout value + * @ap_link_monitor_timeout: AP link monitor timeout value + * @ps_data_inactivity_timeout: PS data inactivity timeout + * @wmi_wq_watchdog_timeout: timeout period for wmi watchdog bite + */ +struct wlan_mlme_timeout { + uint32_t join_failure_timeout; + uint32_t auth_failure_timeout; + uint32_t auth_rsp_timeout; + uint32_t assoc_failure_timeout; + uint32_t reassoc_failure_timeout; + uint32_t probe_after_hb_fail_timeout; + uint32_t olbc_detect_timeout; + uint32_t addts_rsp_timeout; + uint32_t heart_beat_threshold; + uint32_t ap_keep_alive_timeout; + uint32_t ap_link_monitor_timeout; + uint32_t ps_data_inactivity_timeout; + uint32_t wmi_wq_watchdog_timeout; +}; + +/** + * struct wlan_mlme_oce - OCE related config items + * @enable_bcast_probe_rsp: enable broadcast probe response + * @oce_sta_enabled: enable/disable oce feature for sta + * @oce_sap_enabled: enable/disable oce feature for sap + * @fils_enabled: enable/disable fils support + * @feature_bitmap: oce feature bitmap + * + */ +struct wlan_mlme_oce { + bool enable_bcast_probe_rsp; + bool oce_sta_enabled; + bool oce_sap_enabled; + bool fils_enabled; + uint8_t feature_bitmap; +}; + +/** + * enum wep_key_id - values passed to get/set wep default keys + * @MLME_WEP_DEFAULT_KEY_1: wep default key 1 + * @MLME_WEP_DEFAULT_KEY_2: wep default key 2 + * @MLME_WEP_DEFAULT_KEY_3: wep default key 3 + * @MLME_WEP_DEFAULT_KEY_4: wep default key 4 + */ +enum wep_key_id { + MLME_WEP_DEFAULT_KEY_1 = 0, + MLME_WEP_DEFAULT_KEY_2, + MLME_WEP_DEFAULT_KEY_3, + MLME_WEP_DEFAULT_KEY_4 +}; + +/** + * struct wlan_mlme_wep_cfg - WEP related configs + * @is_privacy_enabled: Flag to check if encryption is enabled + * @is_shared_key_auth: Flag to check if the auth type is shared key + * @is_auth_open_system: Flag to check if the auth type is open + * @auth_type: Authentication type value + * @wep_default_key_id: Default WEP key id + * @wep_default_key_1: WEP encryption key 1 + * @wep_default_key_2: WEP encryption key 2 + * @wep_default_key_3: WEP encryption key 3 + * @wep_default_key_4: WEP encryption key 4 + */ +struct wlan_mlme_wep_cfg { + bool is_privacy_enabled; + bool is_shared_key_auth; + bool is_auth_open_system; + uint8_t auth_type; + uint8_t wep_default_key_id; + struct mlme_cfg_str wep_default_key_1; + struct mlme_cfg_str wep_default_key_2; + struct mlme_cfg_str wep_default_key_3; + struct mlme_cfg_str wep_default_key_4; +}; + +/** + * struct wlan_mlme_wifi_pos_cfg - WIFI POS configs + * @fine_time_meas_cap: fine timing measurement capability information + * @oem_6g_support_disable: oem is 6Ghz disabled if set + */ +struct wlan_mlme_wifi_pos_cfg { + uint32_t fine_time_meas_cap; + bool oem_6g_support_disable; +}; + +#define MLME_SET_BIT(value, bit_offset) ((value) |= (1 << (bit_offset))) + +/* Mask to check if BTM offload is enabled/disabled*/ +#define BTM_OFFLOAD_ENABLED_MASK 0x01 + +#define BTM_OFFLOAD_CONFIG_BIT_8 8 +#define BTM_OFFLOAD_CONFIG_BIT_7 7 + +/* + * struct wlan_mlme_btm - BTM related configs + * @prefer_btm_query: flag to prefer btm query over 11k + * @abridge_flag: set this flag to enable firmware to sort candidates based on + * roam score rather than selecting preferred APs. + * @btm_offload_config: configure btm offload + * @btm_solicited_timeout: configure timeout value for waiting BTM request + * @btm_max_attempt_cnt: configure maximum attempt for sending BTM query to ESS + * @btm_sticky_time: configure Stick time after roaming to new AP by BTM + * @rct_validity_timer: Timeout values for roam cache table entries + * @disassoc_timer_threshold: Disassociation timeout till which roam scan need + * not be triggered + * @btm_query_bitmask: Bitmask to send BTM query with candidate list on + * various roam + * @btm_trig_min_candidate_score: Minimum score to consider the AP as candidate + * when the roam trigger is BTM. + */ +struct wlan_mlme_btm { + bool prefer_btm_query; + bool abridge_flag; + uint32_t btm_offload_config; + uint32_t btm_solicited_timeout; + uint32_t btm_max_attempt_cnt; + uint32_t btm_sticky_time; + uint32_t rct_validity_timer; + uint32_t disassoc_timer_threshold; + uint32_t btm_query_bitmask; + uint32_t btm_trig_min_candidate_score; +}; + +/** + * struct wlan_mlme_fe_wlm - WLM related configs + * @latency_enable: Flag to check if latency is enabled + * @latency_level: WLM latency level + * @latency_flags: WLM latency flags setting + */ +struct wlan_mlme_fe_wlm { + bool latency_enable; + bool latency_reset; + uint8_t latency_level; + uint32_t latency_flags[MLME_NUM_WLM_LATENCY_LEVEL]; +}; + +/** + * struct wlan_mlme_fe_rrm - RRM related configs + * @rrm_enabled: Flag to check if RRM is enabled for STA + * @sap_rrm_enabled: Flag to check if RRM is enabled for SAP + * @rrm_rand_interval: RRM randomization interval + * @rm_capability: RM enabled capabilities IE + */ +struct wlan_mlme_fe_rrm { + bool rrm_enabled; + bool sap_rrm_enabled; + uint8_t rrm_rand_interval; + uint8_t rm_capability[MLME_RMENABLEDCAP_MAX_LEN]; +}; + +#ifdef MWS_COEX +/* + * struct wlan_mlme_mwc - MWC related configs + * @mws_coex_4g_quick_tdm: bitmap to set mws-coex 5g-nr power limit + * @mws_coex_5g_nr_pwr_limit: bitmap to set mws-coex 5g-nr power limit + * @mws_coex_pcc_channel_avoid_delay: PCC avoidance delay in seconds + * @mws_coex_scc_channel_avoid_delay: SCC avoidance delay in seconds + **/ +struct wlan_mlme_mwc { + uint32_t mws_coex_4g_quick_tdm; + uint32_t mws_coex_5g_nr_pwr_limit; + uint32_t mws_coex_pcc_channel_avoid_delay; + uint32_t mws_coex_scc_channel_avoid_delay; +}; +#else +struct wlan_mlme_mwc { +}; +#endif + +/** + * enum mlme_reg_srd_master_modes - Bitmap of SRD master modes supported + * @MLME_SRD_MASTER_MODE_SAP: SRD master mode for SAP + * @MLME_SRD_MASTER_MODE_P2P_GO: SRD master mode for P2P-GO + * @MLME_SRD_MASTER_MODE_NAN: SRD master mode for NAN + */ +enum mlme_reg_srd_master_modes { + MLME_SRD_MASTER_MODE_SAP = 1, + MLME_SRD_MASTER_MODE_P2P_GO = 2, + MLME_SRD_MASTER_MODE_NAN = 4, +}; + +/** + * struct wlan_mlme_reg - REG related configs + * @self_gen_frm_pwr: self-generated frame power in tx chain mask + * for CCK rates + * @etsi_srd_chan_in_master_mode: etsi srd chan in master mode + * @restart_beaconing_on_ch_avoid: restart beaconing on ch avoid + * @indoor_channel_support: indoor channel support + * @scan_11d_interval: scan 11d interval + * @valid_channel_freq_list: array for valid channel list + * @valid_channel_list_num: valid channel list number + * @country_code: country code + * @country_code_len: country code length + * @enable_11d_in_world_mode: Whether to enable 11d scan in world mode or not + * @avoid_acs_freq_list: List of the frequencies which need to be avoided + * during acs + * @avoid_acs_freq_list_num: Number of the frequencies to be avoided during acs + * @ignore_fw_reg_offload_ind: Ignore fw regulatory offload indication + * @enable_pending_chan_list_req: enables/disables scan channel + * list command to FW till the current scan is complete. + * @retain_nol_across_regdmn_update: Retain the NOL list across the regdomain. + * @enable_nan_on_indoor_channels: Enable nan on Indoor channels + */ +struct wlan_mlme_reg { + uint32_t self_gen_frm_pwr; + uint8_t etsi_srd_chan_in_master_mode; + enum restart_beaconing_on_ch_avoid_rule + restart_beaconing_on_ch_avoid; + bool indoor_channel_support; + uint32_t scan_11d_interval; + uint32_t valid_channel_freq_list[CFG_VALID_CHANNEL_LIST_LEN]; + uint32_t valid_channel_list_num; + uint8_t country_code[CFG_COUNTRY_CODE_LEN + 1]; + uint8_t country_code_len; + bool enable_11d_in_world_mode; +#ifdef SAP_AVOID_ACS_FREQ_LIST + uint16_t avoid_acs_freq_list[CFG_VALID_CHANNEL_LIST_LEN]; + uint8_t avoid_acs_freq_list_num; +#endif + bool ignore_fw_reg_offload_ind; + bool enable_pending_chan_list_req; + bool retain_nol_across_regdmn_update; + bool enable_nan_on_indoor_channels; +}; + +/** + * struct wlan_mlme_ibss_cfg - IBSS config params + * @auto_bssid: Enable Auto BSSID for IBSS + * @atim_win_size: Set IBSS ATIM window size + * @adhoc_ch_5g: Default 5Ghz IBSS channel if not provided by supplicant + * @adhoc_ch_2g: Default 2.4Ghz IBSS channel if not provided by supplicant + * @coalesing_enable: IBSS coalesing control param + * @power_save_allow: IBSS Power Save control + * @power_collapse_allow: IBSS Power collapse control + * @awake_on_tx_rx: IBSS sta power save mode on TX/RX activity + * @inactivity_bcon_count: No of Beacons of data inactivity for power save + * @txsp_end_timeout: TX service period inactivity timeout + * @ps_warm_up_time: IBSS Power save skip time + * @ps_1rx_chain_atim_win: Control IBSS Power save in 1RX chain during ATIM + * @bssid: BSSID Mac address: IBSS BSSID if not provided by supplicant + */ +struct wlan_mlme_ibss_cfg { + bool auto_bssid; + uint32_t atim_win_size; + uint32_t adhoc_ch_5g; + uint32_t adhoc_ch_2g; + bool coalesing_enable; + bool power_save_allow; + bool power_collapse_allow; + bool awake_on_tx_rx; + uint32_t inactivity_bcon_count; + uint32_t txsp_end_timeout; + uint32_t ps_warm_up_time; + uint32_t ps_1rx_chain_atim_win; + struct qdf_mac_addr bssid; +}; + +/** + * struct wlan_mlme_cfg - MLME config items + * @chainmask_cfg: VHT chainmask related cfg items + * @edca_params: edca related CFG items + * @gen: Generic CFG items + * @ht_caps: HT related CFG Items + * @he_caps: HE related cfg items + * @lfr: LFR related CFG Items + * @ibss: IBSS related CFG items + * @obss_ht40:obss ht40 CFG Items + * @mbo_cfg: Multiband Operation related CFG items + * @vht_caps: VHT related CFG Items + * @qos_mlme_params: QOS CFG Items + * @rates: Rates related cfg items + * @product_details: product details related CFG Items + * @dfs_cfg: DFS related CFG Items + * @sap_protection_cfg: SAP erp protection related CFG items + * @sap_cfg: sap CFG items + * @nss_chains_ini_cfg: Per vdev nss, chains related CFG items + * @sta: sta CFG Items + * @stats: stats CFG Items + * @scoring: BSS Scoring related CFG Items + * @oce: OCE related CFG items + * @threshold: threshold related cfg items + * @timeouts: mlme timeout related CFG items + * @twt_cfg: TWT CFG Items + * @wlan_mlme_power: power related items + * @acs: ACS related CFG items + * @feature_flags: Feature flag config items + * @ps_params: Powersave related ini configs + * @wep_params: WEP related config items + * @wifi_pos_cfg: WIFI POS config + * @wmm_params: WMM related CFG & INI Items + * @wps_params: WPS related CFG itmes + * @btm: BTM related CFG itmes + * @wlm_config: WLM related CFG items + * @rrm_config: RRM related CFG items + * @mwc: MWC related CFG items + * @dot11_mode: dot11 mode supported + * @reg: REG related CFG itmes + * @trig_score_delta: Roam score delta value for various roam triggers + * @trig_min_rssi: Expected minimum RSSI value of candidate AP for + * various roam triggers + */ +struct wlan_mlme_cfg { + struct wlan_mlme_chainmask chainmask_cfg; + struct wlan_mlme_edca_params edca_params; + struct wlan_mlme_generic gen; + struct wlan_mlme_ht_caps ht_caps; +#ifdef WLAN_FEATURE_11AX + struct wlan_mlme_he_caps he_caps; +#endif + struct wlan_mlme_lfr_cfg lfr; + struct wlan_mlme_ibss_cfg ibss; + struct wlan_mlme_obss_ht40 obss_ht40; + struct wlan_mlme_mbo mbo_cfg; + struct wlan_mlme_vht_caps vht_caps; + struct wlan_mlme_qos qos_mlme_params; + struct wlan_mlme_rates rates; + struct wlan_mlme_product_details_cfg product_details; + struct wlan_mlme_dfs_cfg dfs_cfg; + struct wlan_mlme_sap_protection sap_protection_cfg; + struct wlan_mlme_cfg_sap sap_cfg; + struct wlan_mlme_nss_chains nss_chains_ini_cfg; + struct wlan_mlme_sta_cfg sta; + struct wlan_mlme_stats_cfg stats; + struct wlan_mlme_scoring_cfg scoring; + struct wlan_mlme_oce oce; + struct wlan_mlme_threshold threshold; + struct wlan_mlme_timeout timeouts; + struct wlan_mlme_cfg_twt twt_cfg; + struct wlan_mlme_power power; + struct wlan_mlme_acs acs; + struct wlan_mlme_feature_flag feature_flags; + struct wlan_mlme_powersave ps_params; + struct wlan_mlme_wep_cfg wep_params; + struct wlan_mlme_wifi_pos_cfg wifi_pos_cfg; + struct wlan_mlme_wmm_params wmm_params; + struct wlan_mlme_wps_params wps_params; + struct wlan_mlme_btm btm; + struct wlan_mlme_fe_wlm wlm_config; + struct wlan_mlme_fe_rrm rrm_config; + struct wlan_mlme_mwc mwc; + struct wlan_mlme_dot11_mode dot11_mode; + struct wlan_mlme_reg reg; + struct roam_trigger_score_delta trig_score_delta[NUM_OF_ROAM_TRIGGERS]; + struct roam_trigger_min_rssi trig_min_rssi[NUM_OF_ROAM_MIN_RSSI]; + struct wlan_mlme_ratemask ratemask_cfg; +}; + +enum pkt_origin { + FW, + HOST +}; + +/** + * struct mlme_pmk_info - SAE Roaming using single pmk info + * @pmk: pmk + * @pmk_len: pmk length + */ +struct mlme_pmk_info { + uint8_t pmk[CFG_MAX_PMK_LEN]; + uint8_t pmk_len; +}; + +/** + * struct wlan_mlme_sae_single_pmk - SAE Roaming using single pmk configuration + * structure + * @sae_single_pmk_ap: Current connected AP has VSIE or not + * @pmk_info: pmk information + */ +struct wlan_mlme_sae_single_pmk { + bool sae_single_pmk_ap; + struct mlme_pmk_info pmk_info; +}; + +/** + * struct mlme_roam_debug_info - Roam debug information storage structure. + * @trigger: Roam trigger related data + * @scan: Roam scan related data structure. + * @result: Roam result parameters. + * @data_11kv: Neighbor report/BTM parameters. + */ +struct mlme_roam_debug_info { + struct wmi_roam_trigger_info trigger; + struct wmi_roam_scan_data scan; + struct wmi_roam_result result; + struct wmi_neighbor_report_data data_11kv; +}; + +/** + * struct wlan_ies - Generic WLAN Information Element(s) format + * @len: Total length of the IEs + * @data: IE data + */ +struct wlan_ies { + uint16_t len; + uint8_t *data; +}; +#endif diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_ucfg_api.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..b91b7ea8b5cef82daed50377b26a87f3b980634d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_ucfg_api.h @@ -0,0 +1,4182 @@ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare UCFG APIs exposed by the mlme component + */ + +#ifndef _WLAN_MLME_UCFG_API_H_ +#define _WLAN_MLME_UCFG_API_H_ + +#include +#include +#include +#include +#include +#include +#include "wma_tgt_cfg.h" + +/** + * ucfg_mlme_init() - initialize mlme_ctx context. + * + * This function initializes the mlme context. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS ucfg_mlme_init(void); + +/** + * ucfg_mlme_deinit() - De initialize mlme_ctx context. + * + * This function De initializes mlme contex. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS ucfg_mlme_deinit(void); + +/** + * ucfg_mlme_psoc_open() - MLME component Open + * @psoc: pointer to psoc object + * + * Open the MLME component and initialize the MLME strucutre + * + * Return: QDF Status + */ +QDF_STATUS ucfg_mlme_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_mlme_psoc_close() - MLME component close + * @psoc: pointer to psoc object + * + * Close the MLME component and clear the MLME structures + * + * Return: None + */ +void ucfg_mlme_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_mlme_pdev_open() - MLME component pdev Open + * @pdev: pointer to pdev object + * + * Open the MLME component and initialize the MLME pdev strucutre + * + * Return: QDF Status + */ +QDF_STATUS ucfg_mlme_pdev_open(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_mlme_pdev_close() - MLME component pdev close + * @pdev: pointer to pdev object + * + * close the MLME pdev information + * + * Return: QDF Status + */ +QDF_STATUS ucfg_mlme_pdev_close(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_mlme_global_init() - initialize global mlme ops and structure + * + * Return: QDF Status + */ +QDF_STATUS ucfg_mlme_global_init(void); +/** + * ucfg_mlme_global_deinit() - deinitialize global mlme ops and structure + * + * Return: QDF Status + */ +QDF_STATUS ucfg_mlme_global_deinit(void); + +/** + * ucfg_mlme_cfg_chan_to_freq() - convert channel numbers to frequencies + * @pdev: pointer to pdev object + * + * convert the channels numbers received as part of cfg items to + * frequencies. + * + * Return: None + */ +void ucfg_mlme_cfg_chan_to_freq(struct wlan_objmgr_pdev *pdev); + +/** + * wlan_mlme_get_power_usage() - Get the power usage info + * @psoc: pointer to psoc object + * + * Return: pointer to character array of power usage + */ +static inline +char *ucfg_mlme_get_power_usage(struct wlan_objmgr_psoc *psoc) +{ + return wlan_mlme_get_power_usage(psoc); +} + +/** + * ucfg_get_tx_power() - Get the max tx power in particular band + * @psoc: pointer to psoc object + * @band: 2ghz/5ghz band + * + * Return: value of tx power in the respective band + */ +static inline +uint8_t ucfg_get_tx_power(struct wlan_objmgr_psoc *psoc, uint8_t band) +{ + return wlan_mlme_get_tx_power(psoc, band); +} + +/** + * ucfg_mlme_get_ht_cap_info() - Get the HT cap info config + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_ht_cap_info(struct wlan_objmgr_psoc *psoc, + struct mlme_ht_capabilities_info + *ht_cap_info) +{ + return wlan_mlme_get_ht_cap_info(psoc, ht_cap_info); +} + +/** + * ucfg_mlme_set_ht_cap_info() - Set the HT cap info config + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_ht_cap_info(struct wlan_objmgr_psoc *psoc, + struct mlme_ht_capabilities_info + ht_cap_info) +{ + return wlan_mlme_set_ht_cap_info(psoc, ht_cap_info); +} + +/** + * ucfg_mlme_get_max_amsdu_num() - get the max amsdu num + * @psoc: pointer to psoc object + * @value: pointer to the value where the max_amsdu num is to be filled + * + * Return: QDF_STATUS + */ +static inline +QDF_STATUS ucfg_mlme_get_max_amsdu_num(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_max_amsdu_num(psoc, value); +} + +/** + * ucfg_mlme_set_max_amsdu_num() - set the max amsdu num + * @psoc: pointer to psoc object + * @value: value to be set for max_amsdu_num + * + * Return: QDF_STATUS + */ +static inline +QDF_STATUS ucfg_mlme_set_max_amsdu_num(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + return wlan_mlme_set_max_amsdu_num(psoc, value); +} + +/** + * ucfg_mlme_get_ht_mpdu_density() - get the ht mpdu density + * @psoc: pointer to psoc object + * @value: pointer to the value where the ht mpdu density is to be filled + * + * Return: QDF_STATUS + */ +static inline +QDF_STATUS ucfg_mlme_get_ht_mpdu_density(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_ht_mpdu_density(psoc, value); +} + +/** + * ucfg_mlme_set_ht_mpdu_density() - set the ht mpdu density + * @psoc: pointer to psoc object + * @value: value to be set for ht mpdu density + * + * Return: QDF_STATUS + */ +static inline +QDF_STATUS ucfg_mlme_set_ht_mpdu_density(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + return wlan_mlme_set_ht_mpdu_density(psoc, value); +} + +/** + * ucfg_mlme_get_band_capability() - Get the Band capability config + * @psoc: pointer to psoc object + * @band_capability: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_band_capability(struct wlan_objmgr_psoc *psoc, + uint32_t *band_capability) +{ + return wlan_mlme_get_band_capability(psoc, band_capability); +} + +/** + * ucfg_mlme_set_band_capability() - Set the Band capability config + * @psoc: pointer to psoc object + * @band_capability: Value to be set from the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_band_capability(struct wlan_objmgr_psoc *psoc, + uint32_t band_capability) +{ + return wlan_mlme_set_band_capability(psoc, band_capability); +} + +/** + * ucfg_mlme_get_prevent_link_down() - Get the prevent link down config + * @psoc: pointer to psoc object + * @prevent_link_down: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_prevent_link_down(struct wlan_objmgr_psoc *psoc, + bool *prevent_link_down) +{ + return wlan_mlme_get_prevent_link_down(psoc, prevent_link_down); +} + +/** + * ucfg_mlme_get_select_5ghz_margin() - Get the select 5Ghz margin config + * @psoc: pointer to psoc object + * @select_5ghz_margin: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_select_5ghz_margin(struct wlan_objmgr_psoc *psoc, + uint8_t *select_5ghz_margin) +{ + return wlan_mlme_get_select_5ghz_margin(psoc, select_5ghz_margin); +} + +/** + * ucfg_mlme_get_rtt_mac_randomization() - Get the RTT MAC randomization config + * @psoc: pointer to psoc object + * @rtt_mac_randomization: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_rtt_mac_randomization(struct wlan_objmgr_psoc *psoc, + bool *rtt_mac_randomization) +{ + return wlan_mlme_get_rtt_mac_randomization(psoc, rtt_mac_randomization); +} + +/** + * ucfg_mlme_get_crash_inject() - Get the crash inject config + * @psoc: pointer to psoc object + * @crash_inject: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_crash_inject(struct wlan_objmgr_psoc *psoc, + bool *crash_inject) +{ + return wlan_mlme_get_crash_inject(psoc, crash_inject); +} + +/** + * ucfg_mlme_get_lpass_support() - Get the LPASS Support config + * @psoc: pointer to psoc object + * @lpass_support: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_lpass_support(struct wlan_objmgr_psoc *psoc, + bool *lpass_support) +{ + return wlan_mlme_get_lpass_support(psoc, lpass_support); +} + +/** + * ucfg_mlme_get_self_recovery() - Get the self recovery config + * @psoc: pointer to psoc object + * @self_recovery: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_self_recovery(struct wlan_objmgr_psoc *psoc, + bool *self_recovery) +{ + return wlan_mlme_get_self_recovery(psoc, self_recovery); +} + +/** + * ucfg_mlme_get_sub_20_chan_width() - Get the sub 20 chan width config + * @psoc: pointer to psoc object + * @sub_20_chan_width: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sub_20_chan_width(struct wlan_objmgr_psoc *psoc, + uint8_t *sub_20_chan_width) +{ + return wlan_mlme_get_sub_20_chan_width(psoc, sub_20_chan_width); +} + +/** + * ucfg_mlme_get_fw_timeout_crash() - Get the fw timeout crash config + * @psoc: pointer to psoc object + * @fw_timeout_crash: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_fw_timeout_crash(struct wlan_objmgr_psoc *psoc, + bool *fw_timeout_crash) +{ + return wlan_mlme_get_fw_timeout_crash(psoc, fw_timeout_crash); +} + +/** + * ucfg_mlme_get_ito_repeat_count() - Get the fw timeout crash config + * @psoc: pointer to psoc object + * @ito_repeat_count: Pointer to the variable from caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_ito_repeat_count(struct wlan_objmgr_psoc *psoc, + uint8_t *ito_repeat_count) +{ + return wlan_mlme_get_ito_repeat_count(psoc, ito_repeat_count); +} + +/** + * ucfg_mlme_get_acs_with_more_param() - Get the flag for acs with + * more param + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_acs_with_more_param(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_acs_with_more_param(psoc, value); +} + +/** + * ucfg_mlme_get_auto_channel_weight() - Get the auto channel select weight + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_auto_channel_weight(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_auto_channel_weight(psoc, value); +} + +/** + * ucfg_mlme_get_vendor_acs_support() - Get the flag for + * vendor acs support + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_vendor_acs_support(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_vendor_acs_support(psoc, value); +} + +/** + * ucfg_mlme_get_external_acs_policy() - Get flag for external control + * acs policy + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_external_acs_policy(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_external_acs_policy(psoc, value); +} + +/** + * ucfg_mlme_set_ht_cap_info() - Set the HT cap info config + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS +ucfg_mlme_get_acs_support_for_dfs_ltecoex(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_acs_support_for_dfs_ltecoex(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_dir_ac_vo() - Get TSPEC direction for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_dir_ac_vo(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_wmm_dir_ac_vo(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_nom_msdu_size_ac_vo() - Get normal + * MSDU size for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_nom_msdu_size_ac_vo(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + return wlan_mlme_get_wmm_nom_msdu_size_ac_vo(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_mean_data_rate_ac_vo() - mean data rate for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_mean_data_rate_ac_vo(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_mean_data_rate_ac_vo(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_min_phy_rate_ac_vo() - min PHY + * rate for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_min_phy_rate_ac_vo(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_min_phy_rate_ac_vo(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_sba_ac_vo() - surplus bandwidth + * allowance for VO + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_sba_ac_vo(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + return wlan_mlme_get_wmm_sba_ac_vo(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_uapsd_vo_srv_intv() - Get Uapsd service + * interval for voice + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_uapsd_vo_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_uapsd_vo_srv_intv(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_uapsd_vo_sus_intv() - Get Uapsd suspension + * interval for voice + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_uapsd_vo_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_uapsd_vo_sus_intv(psoc, value); +} + +/** + * + * ucfg_mlme_get_sap_inactivity_override() - Check if sap max inactivity + * override flag is set. + * @psoc: pointer to psoc object + * @sme_config - Sme config struct + * + * Inline UCFG API to be used by HDD/OSIF callers to call + * the mlme function wlan_mlme_get_sap_inactivity_override + * + * Return: QDF Status + */ +static inline +void ucfg_mlme_get_sap_inactivity_override(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + wlan_mlme_get_sap_inactivity_override(psoc, value); +} + +/** + * ucfg_mlme_get_tx_chainmask_1ss() - Get the tx_chainmask_1ss value + * + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +static inline +QDF_STATUS ucfg_mlme_get_tx_chainmask_1ss(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_tx_chainmask_1ss(psoc, value); +} + +/** + * ucfg_mlme_get_num_11b_tx_chains() - Get the number of 11b only tx chains + * + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +static inline +QDF_STATUS ucfg_mlme_get_num_11b_tx_chains(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + return wlan_mlme_get_num_11b_tx_chains(psoc, value); +} + +/** + * ucfg_mlme_get_num_11ag_tx_chains() - get the total number of 11a/g tx chains + * + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +static inline +QDF_STATUS ucfg_mlme_get_num_11ag_tx_chains(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + return wlan_mlme_get_num_11ag_tx_chains(psoc, value); +} + +/** + * ucfg_mlme_get_bt_chain_separation_flag() - bt chain separation enable/disable + * @psoc: pointer to psoc object + * @value: Value that needs to be got for the caller + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +static inline +QDF_STATUS ucfg_mlme_get_bt_chain_separation_flag(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_bt_chain_separation_flag(psoc, value); +} + +/** + * ucfg_mlme_configure_chain_mask() - configure chainmask parameters + * + * @psoc: pointer to psoc object + * @session_id: vdev_id + * + * Return: QDF_STATUS_FAILURE or QDF_STATUS_SUCCESS + */ +static inline +QDF_STATUS ucfg_mlme_configure_chain_mask(struct wlan_objmgr_psoc *psoc, + uint8_t session_id) +{ + return wlan_mlme_configure_chain_mask(psoc, session_id); +} + +/* + * ucfg_mlme_get_sta_keep_alive_period() - Get the sta keep alive period + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_sta_keep_alive_period(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/* + * ucfg_mlme_get_dfs_master_capability() - Get the dfs master capability + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_dfs_master_capability(struct wlan_objmgr_psoc *psoc, + bool *val); + +/* + * ucfg_mlme_get_dfs_disable_channel_switch() - Get the dfs channel switch + * @psoc: pointer to psoc object + * @dfs_disable_channel_switch: Pointer to the value which will be filled + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_dfs_disable_channel_switch(struct wlan_objmgr_psoc *psoc, + bool *dfs_disable_channel_switch); + +/* + * ucfg_mlme_set_dfs_disable_channel_switch() - Set the dfs channel switch + * @psoc: pointer to psoc object + * @dfs_disable_channel_switch: Value that needs to be set. + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_dfs_disable_channel_switch(struct wlan_objmgr_psoc *psoc, + bool dfs_disable_channel_switch); +/* + * ucfg_mlme_get_dfs_ignore_cac() - GSet the dfs ignore cac + * @psoc: pointer to psoc object + * @dfs_ignore_cac: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_dfs_ignore_cac(struct wlan_objmgr_psoc *psoc, + bool *dfs_ignore_cac); + +/* + * ucfg_mlme_set_dfs_ignore_cac() - Set the dfs ignore cac + * @psoc: pointer to psoc object + * @dfs_ignore_cac: Value that needs to be set. + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_dfs_ignore_cac(struct wlan_objmgr_psoc *psoc, + bool dfs_ignore_cac); + +/* + * ucfg_mlme_get_sap_tx_leakage_threshold() - Get sap tx leakage threshold + * @psoc: pointer to psoc object + * @sap_tx_leakage_threshold: Pointer to the value which will be filled + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_sap_tx_leakage_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *sap_tx_leakage_threshold); + +/* + * ucfg_mlme_set_sap_tx_leakage_threshold() - Set sap tx leakage threshold + * @psoc: pointer to psoc object + * @sap_tx_leakage_threshold: Value that needs to be set. + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_sap_tx_leakage_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t sap_tx_leakage_threshold); + +/* + * ucfg_mlme_get_dfs_pri_multiplier() - Get dfs pri multiplier + * @psoc: pointer to psoc object + * @dfs_pri_multiplier: Pointer to the value which will be filled + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_dfs_pri_multiplier(struct wlan_objmgr_psoc *psoc, + uint32_t *dfs_pri_multiplier); + +/* + * ucfg_mlme_set_dfs_pri_multiplier() - Set dfs pri multiplier + * @psoc: pointer to psoc object + * @dfs_pri_multiplier: Value that needs to be set. + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_dfs_pri_multiplier(struct wlan_objmgr_psoc *psoc, + uint32_t dfs_pri_multiplier); + +/* + * ucfg_mlme_get_dfs_filter_offload() - Get the dfs filter offload + * @psoc: pointer to psoc object + * @dfs_filter_offload: Pointer to the value which will be filled + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_dfs_filter_offload(struct wlan_objmgr_psoc *psoc, + bool *dfs_filter_offload); + +/* + * ucfg_mlme_set_dfs_filter_offload() - Set the dfs filter offload + * @psoc: pointer to psoc object + * @dfs_filter_offload: Value that needs to be set. + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_dfs_filter_offload(struct wlan_objmgr_psoc *psoc, + bool dfs_filter_offload); + +/** + * ucfg_mlme_get_oem_6g_supported() - Get oem 6Ghz supported + * @psoc: pointer to psoc object + * @oem_6g_supported: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_oem_6g_supported(struct wlan_objmgr_psoc *psoc, + bool *oem_6g_supported); + +/** + * ucfg_mlme_get_fine_time_meas_cap() - Get fine timing measurement capability + * @psoc: pointer to psoc object + * @fine_time_meas_cap: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_fine_time_meas_cap(struct wlan_objmgr_psoc *psoc, + uint32_t *fine_time_meas_cap); + +/** + * ucfg_mlme_set_fine_time_meas_cap() - Set fine timing measurement capability + * @psoc: pointer to psoc object + * @fine_time_meas_cap: Value to be set + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_fine_time_meas_cap(struct wlan_objmgr_psoc *psoc, + uint32_t fine_time_meas_cap); + +/** + * ucfg_mlme_get_pmkid_modes() - Get PMKID modes + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_pmkid_modes(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * ucfg_mlme_set_pmkid_modes() - Set PMKID modes + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_pmkid_modes(struct wlan_objmgr_psoc *psoc, + uint32_t val); + +/** + * ucfg_mlme_get_twt_requestor() - Get twt requestor + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_twt_requestor(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * ucfg_mlme_set_twt_requestor() - Set twt requestor + * @psoc: pointer to psoc object + * @val: value that needs to set to this config + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_twt_requestor(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * ucfg_mlme_get_twt_responder() - Get twt responder + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_twt_responder(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * ucfg_mlme_set_twt_responder() - Set twt responder + * @psoc: pointer to psoc object + * @val: value that needs to set to this config + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_twt_responder(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * ucfg_mlme_get_bcast_twt() - Get bcast twt + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_bcast_twt(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * ucfg_mlme_set_bcast_twt() - Set bcast twt + * @psoc: pointer to psoc object + * @val: value that needs to set to this config + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_bcast_twt(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * ucfg_mlme_get_twt_congestion_timeout() - Get twt congestion timeout + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_twt_congestion_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * ucfg_mlme_set_twt_congestion_timeout() - Set twt congestion timeout + * @psoc: pointer to psoc object + * @val: value that needs to set to this config + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_twt_congestion_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t val); + +/** + * ucfg_mlme_get_enable_twt() - Get global twt config support + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_enable_twt(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * ucfg_mlme_set_enable_twt() - Set global twt config support + * @psoc: pointer to psoc object + * @val: value that needs to set to this config + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_enable_twt(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * ucfg_mlme_get_dot11p_mode() - Get the setting about 802.11p mode + * @psoc: pointer to psoc object + * @out_mode: Pointer to the mode which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_dot11p_mode(struct wlan_objmgr_psoc *psoc, + enum dot11p_mode *out_mode); + +/** + * ucfg_mlme_get_go_cts2self_for_sta() - Stop NOA and start using cts2self + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_go_cts2self_for_sta(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * ucfg_mlme_get_qcn_ie_support() - QCN IE support or not + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_qcn_ie_support(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * ucfg_mlme_get_tgt_gtx_usr_cfg() - Get the target gtx user config + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_tgt_gtx_usr_cfg(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * ucfg_mlme_is_override_ht20_40_24g() - use channel bonding in 2.4 GHz or not + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_is_override_ht20_40_24g(struct wlan_objmgr_psoc *psoc, bool *val); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * ucfg_mlme_get_roam_disable_config() - Get sta roam disable value + * @psoc: pointer to psoc object + * @val: Pointer to bitmap of interfaces for those sta roaming is disabled + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_roam_disable_config(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * ucfg_mlme_get_roaming_offload() - Get roaming offload setting + * @psoc: pointer to psoc object + * @val: Pointer to enable/disable roaming offload + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_roaming_offload(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * ucfg_mlme_set_roaming_offload() - Enable/disable roaming offload + * @psoc: pointer to psoc object + * @val: enable/disable roaming offload + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_roaming_offload(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * ucfg_mlme_get_roaming_triggers() - Get roaming triggers bitmap + * value + * @psoc: pointer to psoc object + * + * Return: Roaming triggers value + */ +static inline uint32_t +ucfg_mlme_get_roaming_triggers(struct wlan_objmgr_psoc *psoc) +{ + return wlan_mlme_get_roaming_triggers(psoc); +} +#else +static inline QDF_STATUS +ucfg_mlme_get_roam_disable_config(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +ucfg_mlme_get_roaming_offload(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_mlme_set_roaming_offload(struct wlan_objmgr_psoc *psoc, + bool val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline uint32_t +ucfg_mlme_get_roaming_triggers(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} +#endif + +/** + * ucfg_mlme_get_first_scan_bucket_threshold() - Get first scan bucket thre + * @psoc: pointer to psoc object + * @val: first scan bucket threshold + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_first_scan_bucket_threshold(struct wlan_objmgr_psoc *psoc, + uint8_t *val); + +/** + * ucfg_mlme_is_mawc_enabled() - MAWC enabled or not + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_is_mawc_enabled(struct wlan_objmgr_psoc *psoc, bool *val); + +/** + * ucfg_mlme_set_mawc_enabled() - Set MAWC enable or disable + * @psoc: pointer to psoc object + * @val: enable or disable MAWC + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_mawc_enabled(struct wlan_objmgr_psoc *psoc, bool val); + +/** + * ucfg_mlme_is_fast_transition_enabled() - Fast transition enable or not + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_is_fast_transition_enabled(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * ucfg_mlme_set_fast_transition_enabled() - Set fast transition enable + * @psoc: pointer to psoc object + * @val: Fast transition enable or disable + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_fast_transition_enabled(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * ucfg_mlme_is_roam_scan_offload_enabled() - Roam scan offload enable or not + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_is_roam_scan_offload_enabled(struct wlan_objmgr_psoc *psoc, + bool *val); + +#ifdef WLAN_ADAPTIVE_11R +/** + * ucfg_mlme_set_tgt_adaptive_11r_cap() - Set adaptive 11r target service + * capability + * @psoc: pointer to psoc object + * @val: Target capability of adaptive 11r + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_tgt_adaptive_11r_cap(struct wlan_objmgr_psoc *psoc, + bool val); +#else +static inline QDF_STATUS +ucfg_mlme_set_tgt_adaptive_11r_cap(struct wlan_objmgr_psoc *psoc, + bool val) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * ucfg_mlme_set_roam_scan_offload_enabled() - Set roam scan offload enable + * @psoc: pointer to psoc object + * @val: Roam scan offload enable or disable + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_roam_scan_offload_enabled(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * ucfg_mlme_get_neighbor_scan_max_chan_time() - Get neighbor scan max + * channel time + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_neighbor_scan_max_chan_time(struct wlan_objmgr_psoc *psoc, + uint16_t *val); + +/** + * ucfg_mlme_get_neighbor_scan_min_chan_time() - Get neighbor scan min + * channel time + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_neighbor_scan_min_chan_time(struct wlan_objmgr_psoc *psoc, + uint16_t *val); + +/** + * ucfg_mlme_get_delay_before_vdev_stop() - Get the delay before vdev stop + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_delay_before_vdev_stop(struct wlan_objmgr_psoc *psoc, + uint8_t *val); + +/** + * ucfg_mlme_get_roam_bmiss_final_bcnt() - Get roam bmiss first count + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_roam_bmiss_final_bcnt(struct wlan_objmgr_psoc *psoc, + uint8_t *val); + +/** + * ucfg_mlme_get_roam_bmiss_first_bcnt() - Get roam bmiss final count + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_roam_bmiss_first_bcnt(struct wlan_objmgr_psoc *psoc, + uint8_t *val); + +/** + * ucfg_mlme_is_lfr_enabled() - LFR enable or not + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_is_lfr_enabled(struct wlan_objmgr_psoc *psoc, bool *val); + +/** + * ucfg_mlme_set_lfr_enabled() - Enable or disable LFR + * @psoc: pointer to psoc object + * @val: Enable or disable LFR + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_lfr_enabled(struct wlan_objmgr_psoc *psoc, bool val); + +/** + * ucfg_mlme_is_roam_prefer_5ghz() - prefer 5ghz or not + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_is_roam_prefer_5ghz(struct wlan_objmgr_psoc *psoc, bool *val); + +/** + * ucfg_mlme_set_roam_intra_band() - Set roam intra modes + * @psoc: pointer to psoc object + * @val: roam intra modes or not + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_roam_intra_band(struct wlan_objmgr_psoc *psoc, bool val); + +/** + * ucfg_mlme_get_home_away_time() - Get home away time + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_home_away_time(struct wlan_objmgr_psoc *psoc, uint16_t *val); + +/** + * ucfg_mlme_set_fast_roam_in_concurrency_enabled() - Enable fast roam in + * concurrency + * @psoc: pointer to psoc object + * @val: Enable or disable fast roam in concurrency + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_fast_roam_in_concurrency_enabled(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * ucfg_mlme_get_wmi_wq_watchdog_timeout() - Get timeout for wmi watchdog bite + * @psoc: pointer to psoc object + * @wmi_wq_watchdog_timeout: buffer to hold value + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_wmi_wq_watchdog_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t *wmi_wq_watchdog_timeout); + +/** + * ucfg_mlme_set_wmi_wq_watchdog_timeout() - Set timeout for wmi watchdog bite + * @psoc: pointer to psoc object + * @wmi_wq_watchdog_timeout: value to be set + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_wmi_wq_watchdog_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t wmi_wq_watchdog_timeout); + +/** + * ucfg_mlme_get_ps_data_inactivity_timeout() - Get data inactivity timeout + * @psoc: pointer to psoc object + * @inactivity_timeout: buffer to hold value + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_ps_data_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t *inactivity_timeout); + +/** + * ucfg_mlme_set_ps_data_inactivity_timeout() - Set data inactivity timeout + * @psoc: pointer to psoc object + * @inactivity_timeout: value to be set + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_ps_data_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t inactivity_timeout); + +/** + * ucfg_mlme_set_sap_listen_interval() - Set the Sap listen interval + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_sap_listen_interval(struct wlan_objmgr_psoc *psoc, + int value) +{ + return wlan_mlme_set_sap_listen_interval(psoc, value); +} + +/** + * ucfg_mlme_set_assoc_sta_limit() - Set the assoc sta limit + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_assoc_sta_limit(struct wlan_objmgr_psoc *psoc, + int value) +{ + return wlan_mlme_set_assoc_sta_limit(psoc, value); +} + +/** + * ucfg_mlme_get_assoc_sta_limit() - Get the assoc sta limit + * @psoc: pointer to psoc object + * @value: Pointer to variable that needs to be filled by MLME + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_assoc_sta_limit(struct wlan_objmgr_psoc *psoc, + int *value) +{ + return wlan_mlme_get_assoc_sta_limit(psoc, value); +} + +/** + * ucfg_mlme_set_rmc_action_period_freq() - Set the rmc action period frequency + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_rmc_action_period_freq(struct wlan_objmgr_psoc *psoc, + int value) +{ + return wlan_mlme_set_rmc_action_period_freq(psoc, value); +} + +/** + * ucfg_mlme_get_listen_interval() - Get listen interval + * @psoc: pointer to psoc object + * @value: Pointer to variable that needs to be filled by MLME + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_listen_interval(struct wlan_objmgr_psoc *psoc, + int *value) +{ + return wlan_mlme_get_listen_interval(psoc, value); +} + + +/** + * ucfg_mlme_get_sap_get_peer_info() - get the sap get peer info + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_get_peer_info(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_sap_get_peer_info(psoc, value); +} + +/** + * ucfg_mlme_set_sap_get_peer_info() - set the sap get peer info + * @psoc: pointer to psoc object + * @value: value to overwrite the sap get peer info + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_sap_get_peer_info(struct wlan_objmgr_psoc *psoc, + bool value) +{ + return wlan_mlme_set_sap_get_peer_info(psoc, value); +} + +/** + * ucfg_mlme_get_sap_bcast_deauth_enabled() - get the sap bcast deauth + * enabled value + * @psoc: pointer to psoc object + * @value: Value that needs to be get from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_sap_bcast_deauth_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_sap_bcast_deauth_enabled(psoc, value); +} + +/** + * ucfg_mlme_is_6g_sap_fd_enabled() - get the sap fils discovery + * enabled value + * @psoc: pointer to psoc object + * @value: Value that needs to be get from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_is_6g_sap_fd_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_is_6g_sap_fd_enabled(psoc, value); +} + +/** + * ucfg_mlme_get_sap_allow_all_channels() - get the sap allow all channels + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_allow_all_channels(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_sap_allow_all_channels(psoc, value); +} + +/** + * ucfg_mlme_get_sap_max_peers() - get the sap max peers + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_max_peers(struct wlan_objmgr_psoc *psoc, + int *value) +{ + return wlan_mlme_get_sap_max_peers(psoc, value); +} + +/** + * ucfg_mlme_set_sap_max_peers() - Set the sap max peers + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_sap_max_peers(struct wlan_objmgr_psoc *psoc, int value) +{ + return wlan_mlme_set_sap_max_peers(psoc, value); +} + +/** + * ucfg_mlme_get_sap_max_offload_peers() - get the sap max offload peers + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_max_offload_peers(struct wlan_objmgr_psoc *psoc, + int *value) +{ + return wlan_mlme_get_sap_max_offload_peers(psoc, value); +} + +/** + * ucfg_mlme_get_sap_max_offload_reorder_buffs() - get the sap max offload + * reorder buffs + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_max_offload_reorder_buffs(struct wlan_objmgr_psoc + *psoc, int *value) +{ + return wlan_mlme_get_sap_max_offload_reorder_buffs(psoc, value); +} + +/** + * ucfg_mlme_get_sap_chn_switch_bcn_count() - get the sap channel + * switch beacon count + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_chn_switch_bcn_count(struct wlan_objmgr_psoc *psoc, + int *value) +{ + return wlan_mlme_get_sap_chn_switch_bcn_count(psoc, value); +} + +/** + * ucfg_mlme_get_sap_channel_switch_mode() - get the sap channel switch mode + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_channel_switch_mode(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_sap_chn_switch_mode(psoc, value); +} + +/** + * ucfg_mlme_get_sap_internal_restart() - get sap internal restart value + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_internal_restart(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_sap_internal_restart(psoc, value); +} + +/** + * ucfg_mlme_get_sap_max_modulated_dtim() - get sap max modulated dtim + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_max_modulated_dtim(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_sap_max_modulated_dtim(psoc, value); +} + +/** + * ucfg_mlme_get_pref_chan_location() - get sap pref chan location + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_pref_chan_location(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_sap_chan_pref_location(psoc, value); +} + +/** + * ucfg_mlme_get_sap_country_priority() - get sap country code priority + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_country_priority(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_sap_country_priority(psoc, value); +} + +/** + * ucfg_mlme_get_sap_reduces_beacon_interval() - get the sap reduces beacon + * interval + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_reduces_beacon_interval(struct wlan_objmgr_psoc + *psoc, int *value) +{ + return wlan_mlme_get_sap_reduced_beacon_interval(psoc, value); +} + +/** + * ucfg_mlme_get_sap_chan_switch_rate_enabled() - get the sap channel + * switch rate enabled. + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_chan_switch_rate_enabled(struct wlan_objmgr_psoc + *psoc, bool *value) +{ + return wlan_mlme_get_sap_chan_switch_rate_enabled(psoc, value); +} + +/** + * ucfg_mlme_get_sap_force_11n_for_11ac() - get the sap 11n for 11ac + * + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_sap_force_11n_for_11ac(struct wlan_objmgr_psoc + *psoc, bool *value) +{ + return wlan_mlme_get_sap_force_11n_for_11ac(psoc, value); +} + +/** + * ucfg_mlme_get_go_force_11n_for_11ac() - get the GO 11n for 11ac + * + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_go_force_11n_for_11ac(struct wlan_objmgr_psoc + *psoc, bool *value) +{ + return wlan_mlme_get_go_force_11n_for_11ac(psoc, value); +} + +/** + * ucfg_mlme_is_sap_11ac_override() - Override 11ac bandwdith for SAP + * + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_is_sap_11ac_override(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_is_sap_11ac_override(psoc, value); +} + +/** + * ucfg_mlme_is_go_11ac_override() - Override 11ac bandwdith for P2P GO + * + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_is_go_11ac_override(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_is_go_11ac_override(psoc, value); +} + +/** + * ucfg_mlme_set_sap_11ac_override() - Set override 11ac bandwdith for SAP + * + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_sap_11ac_override(struct wlan_objmgr_psoc *psoc, + bool value) +{ + return wlan_mlme_set_sap_11ac_override(psoc, value); +} + +/** + * ucfg_mlme_set_go_11ac_override() - Set override 11ac bandwdith for P2P GO + * + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_go_11ac_override(struct wlan_objmgr_psoc *psoc, + bool value) +{ + return wlan_mlme_set_go_11ac_override(psoc, value); +} + +/** + * ucfg_mlme_get_oce_sta_enabled_info() - Get OCE feature enable/disable + * info for STA + * + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * OCE STA feature enable value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_get_oce_sta_enabled_info(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_oce_sta_enabled_info(psoc, value); +} + +/** + * ucfg_mlme_get_oce_sap_enabled_info() - Get OCE feature enable/disable + * info for SAP + * + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * OCE SAP feature enable value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_get_oce_sap_enabled_info(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_oce_sap_enabled_info(psoc, value); +} + +/** + * ucfg_mlme_update_oce_flags: Update the OCE flags + * + * @pdev: pointer to pdev object + * + * Inline UCFG API to be used by HDD/OSIF callers to update the + * OCE feature flags + * + * Return: void + */ +static inline +void ucfg_mlme_update_oce_flags(struct wlan_objmgr_pdev *pdev) +{ + wlan_mlme_update_oce_flags(pdev); +} + +/** + * ucfg_mlme_is_ap_prot_enabled() - Check if sap is enabled + * @psoc: pointer to psoc object + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * sap protection enabled/disabled + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +bool ucfg_mlme_is_ap_prot_enabled(struct wlan_objmgr_psoc *psoc) +{ + return wlan_mlme_is_ap_prot_enabled(psoc); +} + +/** + * ucfg_mlme_get_ap_protection_mode() - Get ap protection mode info + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ap protection mode value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_get_ap_protection_mode(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + return wlan_mlme_get_ap_protection_mode(psoc, value); +} + +/** + * ucfg_mlme_is_ap_obss_prot_enabled() - Get ap obss protection enable/disable + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * obss protection enable value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_is_ap_obss_prot_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_is_ap_obss_prot_enabled(psoc, value); +} + +/** + * ucfg_mlme_get_rts_threshold() - Get the rts threshold config + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_rts_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_rts_threshold(psoc, value); +} + +/** + * ucfg_mlme_set_rts_threshold() - Set the rts threshold config + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_rts_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + return wlan_mlme_set_rts_threshold(psoc, value); +} + +/** + * ucfg_mlme_get_frag_threshold() - Get the fragmentation threshold + * config + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_frag_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_frag_threshold(psoc, value); +} + +/** + * ucfg_mlme_set_frag_threshold() - set the frag threshold config + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_set_frag_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + return wlan_mlme_set_frag_threshold(psoc, value); +} + +/** + * ucfg_mlme_get_fils_enabled_info() - Get fils enable/disable info + * + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * fils enable value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_get_fils_enabled_info(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_fils_enabled_info(psoc, value); +} + +/** + * ucfg_mlme_set_fils_enabled_info() - Set fils enable info + * + * @psoc: pointer to psoc object + * @value: value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers to set the + * fils enable value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_set_fils_enabled_info(struct wlan_objmgr_psoc *psoc, + bool value) +{ + return wlan_mlme_set_fils_enabled_info(psoc, value); +} + +/** + * ucfg_mlme_set_enable_bcast_probe_rsp() - Set enable bcast probe resp info + * @psoc: pointer to psoc object + * @value: value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers to set the + * enable bcast probe resp info + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_set_enable_bcast_probe_rsp(struct wlan_objmgr_psoc *psoc, + bool value) +{ + return wlan_mlme_set_enable_bcast_probe_rsp(psoc, value); +} + +/** + * ucfg_mlme_set_vht_ch_width() - set the vht supported channel width cfg + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_set_vht_ch_width(struct wlan_objmgr_psoc *psoc, uint8_t value) +{ + return wlan_mlme_cfg_set_vht_chan_width(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_vht_chan_width() - gets vht supported channel width into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_get_vht_chan_width(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_cfg_get_vht_chan_width(psoc, value); +} + +/** + * ucfg_mlme_cfg_set_vht_ldpc_coding_cap() - sets vht ldpc coding cap into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_set_vht_ldpc_coding_cap(struct wlan_objmgr_psoc *psoc, bool value) +{ + return wlan_mlme_cfg_set_vht_ldpc_coding_cap(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_short_gi_160_mhz() - Get SHORT GI 160MHZ from cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_get_short_gi_160_mhz(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_cfg_get_short_gi_160_mhz(psoc, value); +} + +/** + * ucfg_mlme_cfg_set_short_gi_160_mhz() - sets basic set SHORT GI 160MHZ into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_set_short_gi_160_mhz(struct wlan_objmgr_psoc *psoc, bool value) +{ + return wlan_mlme_cfg_set_short_gi_160_mhz(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_vht_tx_stbc() - gets vht tx stbc from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_get_vht_tx_stbc(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_cfg_get_vht_tx_stbc(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_vht_rx_stbc() - gets vht rx stbc from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_get_vht_rx_stbc(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_cfg_get_vht_rx_stbc(psoc, value); +} + +/** + * ucfg_mlme_cfg_set_vht_tx_bfee_ant_supp() - sets vht Beamformee antenna + * support cap into cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_cfg_set_vht_tx_bfee_ant_supp(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + return wlan_mlme_cfg_set_vht_tx_bfee_ant_supp(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_vht_tx_bfee_ant_supp() - gets vht Beamformee antenna + * support cap into cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_cfg_get_vht_tx_bfee_ant_supp(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_cfg_get_vht_tx_bfee_ant_supp(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_vht_rx_mcs_map() - gets vht rx mcs map from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_get_vht_rx_mcs_map(struct wlan_objmgr_psoc *psoc, uint32_t *value) +{ + return wlan_mlme_cfg_get_vht_rx_mcs_map(psoc, value); +} + +/** + * ucfg_mlme_cfg_set_vht_rx_mcs_map() - sets rx mcs map into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_set_vht_rx_mcs_map(struct wlan_objmgr_psoc *psoc, uint32_t value) +{ + return wlan_mlme_cfg_set_vht_rx_mcs_map(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_vht_tx_mcs_map() - gets vht tx mcs map from + * cfg item + * @psoc: psoc context + * @value: pointer to get required data + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_get_vht_tx_mcs_map(struct wlan_objmgr_psoc *psoc, uint32_t *value) +{ + return wlan_mlme_cfg_get_vht_tx_mcs_map(psoc, value); +} + +/** + * ucfg_mlme_cfg_set_vht_tx_mcs_map() - sets tx mcs map into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_set_vht_tx_mcs_map(struct wlan_objmgr_psoc *psoc, uint32_t value) +{ + return wlan_mlme_cfg_set_vht_tx_mcs_map(psoc, value); +} + +/** + * ucfg_mlme_cfg_set_vht_rx_supp_data_rate() - sets rx supported data + * rate into cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_set_vht_rx_supp_data_rate(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + return wlan_mlme_cfg_set_vht_rx_supp_data_rate(psoc, value); +} + +/** + * ucfg_mlme_cfg_set_vht_tx_supp_data_rate() - sets tx supported data rate into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_set_vht_tx_supp_data_rate(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + return wlan_mlme_cfg_set_vht_tx_supp_data_rate(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_vht_basic_mcs_set() - gets basic mcs set from + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_get_vht_basic_mcs_set(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_cfg_get_vht_basic_mcs_set(psoc, value); +} + +/** + * ucfg_mlme_cfg_set_vht_basic_mcs_set() - sets basic mcs set into + * cfg item + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_cfg_set_vht_basic_mcs_set(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + return wlan_mlme_cfg_set_vht_basic_mcs_set(psoc, value); +} + +/** + * ucfg_mlme_get_vht_enable_tx_bf() - gets enable TXBF for 20MHZ + * for 11ac + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_enable_tx_bf(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_vht_enable_tx_bf(psoc, value); +} + +/** + * ucfg_mlme_get_vht_tx_su_beamformer() - gets enable tx_su_beamformer + * for 11ac + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_tx_su_beamformer(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_vht_tx_su_beamformer(psoc, value); +} + +/** + * ucfg_mlme_get_vht_channel_width() - gets Channel width capability + * for 11ac + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_channel_width(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_vht_channel_width(psoc, value); +} + +/** + * ucfg_mlme_get_vht_rx_mcs_8_9() - VHT Rx MCS capability for 1x1 mode + * for 11ac + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_rx_mcs_8_9(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_vht_rx_mcs_8_9(psoc, value); +} + +/** + * ucfg_mlme_get_vht_tx_mcs_8_9() - VHT Tx MCS capability for 1x1 mode + * for 11ac + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_tx_mcs_8_9(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_vht_tx_mcs_8_9(psoc, value); +} + +/** + * ucfg_mlme_get_vht_rx_mcs_2x2() - VHT Rx MCS capability for 2x2 mode + * for 11ac + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_rx_mcs_2x2(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_vht_rx_mcs_2x2(psoc, value); +} + +/** + * ucfg_mlme_get_vht_tx_mcs_2x2() - VHT Tx MCS capability for 2x2 mode + * for 11ac + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_tx_mcs_2x2(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_vht_tx_mcs_2x2(psoc, value); +} + +/** + * ucfg_mlme_get_ini_vdev_config() - get the ini capability of vdev + * @vdev: pointer to the vdev obj + * + * This API will get the ini config of the vdev related to + * the nss, chains params + * + * Return: pointer to the nss, chain param ini cfg structure + */ +static inline struct wlan_mlme_nss_chains * +ucfg_mlme_get_ini_vdev_config(struct wlan_objmgr_vdev *vdev) +{ + return mlme_get_ini_vdev_config(vdev); +} + +/** + * ucfg_mlme_get_dynamic_vdev_config() - get the dynamic capability of vdev + * @vdev: pointer to the vdev obj + * + * This API will get the dynamic config of the vdev related to nss, + * chains params + * + * Return: pointer to the nss, chain param dynamic cfg structure + */ +static inline struct wlan_mlme_nss_chains * +ucfg_mlme_get_dynamic_vdev_config(struct wlan_objmgr_vdev *vdev) +{ + return mlme_get_dynamic_vdev_config(vdev); +} + +/** + * ucfg_mlme_get_vht20_mcs9() - Enables VHT MCS9 in 20M BW operation + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht20_mcs9(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_vht20_mcs9(psoc, value); +} + +/** + * ucfg_mlme_get_vht_enable2x2() - Enables/disables VHT Tx/Rx MCS values for 2x2 + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_enable2x2(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_vht_enable2x2(psoc, value); +} + +/** + * ucfg_mlme_set_vht_enable2x2() - Enables/disables VHT Tx/Rx MCS values for 2x2 + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_set_vht_enable2x2(struct wlan_objmgr_psoc *psoc, bool value) +{ + return wlan_mlme_set_vht_enable2x2(psoc, value); +} + +/** + * ucfg_mlme_get_vht_enable_paid() - Enables/disables paid feature + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_enable_paid(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_vht_enable_paid(psoc, value); +} + +/** + * ucfg_mlme_get_vht_enable_gid() - Enables/disables gid feature + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_enable_gid(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_vht_enable_gid(psoc, value); +} + +/** + * ucfg_mlme_get_vht_for_24ghz() - Get mlme cfg of vht for 24ghz + * @psoc: psoc context + * @value: data to get + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vht_for_24ghz(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_vht_for_24ghz(psoc, value); +} + +/** + * ucfg_mlme_set_vht_for_24ghz() - Enables/disables vht for 24ghz + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_set_vht_for_24ghz(struct wlan_objmgr_psoc *psoc, bool value) +{ + return wlan_mlme_set_vht_for_24ghz(psoc, value); +} + +/** + * ucfg_mlme_get_vendor_vht_for_24ghz() - Get mlme cfg of vendor vht for 24ghz + * @psoc: psoc context + * @value: data to be set + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_get_vendor_vht_for_24ghz(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_vendor_vht_for_24ghz(psoc, value); +} + +/** + * ucfg_mlme_update_vht_cap() - Update vht capabilities + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline +QDF_STATUS ucfg_mlme_update_vht_cap(struct wlan_objmgr_psoc *psoc, + struct wma_tgt_vht_cap *cfg) +{ + return mlme_update_vht_cap(psoc, cfg); +} + +/** + * ucfg_mlme_update_nss_vht_cap() -Update the number of spatial + * streams supported for vht + * @psoc: psoc context + * @value: data to be set + * + * Inline UCFG API to be used by HDD/OSIF callers to get the + * ignore_peer_ht_opmode flag value + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_update_nss_vht_cap(struct wlan_objmgr_psoc *psoc) +{ + return mlme_update_nss_vht_cap(psoc); +} + +/** + * ucfg_mlme_is_11h_enabled() - Get 11h flag + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_is_11h_enabled(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_is_11h_enabled(psoc, value); +} + +/** + * ucfg_mlme_set_11h_enabled() - Set 11h flag + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_set_11h_enabled(struct wlan_objmgr_psoc *psoc, bool value) +{ + return wlan_mlme_set_11h_enabled(psoc, value); +} + +/** + * ucfg_mlme_is_11d_enabled() - Get 11d flag + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_is_11d_enabled(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_is_11d_enabled(psoc, value); +} + +/** + * ucfg_mlme_set_11d_enabled() - Set 11d flag + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_set_11d_enabled(struct wlan_objmgr_psoc *psoc, bool value) +{ + return wlan_mlme_set_11d_enabled(psoc, value); +} + +/** + * ucfg_mlme_get_opr_rate_set() - Get operational rate set + * @psoc: pointer to psoc object + * @buf: buffer to get rates set + * @len: length of the buffer + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_opr_rate_set(struct wlan_objmgr_psoc *psoc, uint8_t *buf, + qdf_size_t *len); + +/** + * ucfg_mlme_get_ext_opr_rate_set() - Get operational rate set + * @psoc: pointer to psoc object + * @buf: buffer to get rates set + * @len: length of the buffer + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_ext_opr_rate_set(struct wlan_objmgr_psoc *psoc, uint8_t *buf, + qdf_size_t *len); + +/** + * ucfg_mlme_get_supported_mcs_set() - Get Supported MCS set + * @psoc: pointer to psoc object + * @buf: caller buffer to copy mcs set info + * @len: length of the buffer + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_supported_mcs_set(struct wlan_objmgr_psoc *psoc, uint8_t *buf, + qdf_size_t *len); + +/** + * ucfg_mlme_set_supported_mcs_set() - Get Supported MCS set + * @psoc: pointer to psoc object + * @buf: caller buffer having mcs set info + * @len: length of the buffer + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_supported_mcs_set(struct wlan_objmgr_psoc *psoc, uint8_t *buf, + qdf_size_t len); + +/** + * ucfg_mlme_get_current_mcs_set() - Get current MCS set + * @psoc: pointer to psoc object + * @buf: caller buffer to copy mcs set info + * @len: length of the buffer + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_current_mcs_set(struct wlan_objmgr_psoc *psoc, uint8_t *buf, + qdf_size_t *len); + +/** + * ucfg_mlme_get_sta_keepalive_method() - Get sta_keepalive_method + * @psoc: pointer to psoc object + * @val: Value to pass to the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_sta_keepalive_method(struct wlan_objmgr_psoc *psoc, + enum station_keepalive_method *val); + +/** + * ucfg_mlme_stats_get_periodic_display_time() - get display time + * @psoc: pointer to psoc object + * @periodic_display_time: buffer to hold value + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_stats_get_periodic_display_time(struct wlan_objmgr_psoc *psoc, + uint32_t *periodic_display_time); + +/** + * ucfg_mlme_stats_get_cfg_values() - get stats cfg values + * @psoc: pointer to psoc object + * @link_speed_rssi_high: link speed high limit + * @link_speed_rssi_mid: link speed high mid + * @link_speed_rssi_low: link speed high low + * @link_speed_rssi_report: link speed report limit + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_stats_get_cfg_values(struct wlan_objmgr_psoc *psoc, + int *link_speed_rssi_high, + int *link_speed_rssi_mid, + int *link_speed_rssi_low, + uint32_t *link_speed_rssi_report); + +/** + * ucfg_mlme_stats_is_link_speed_report_actual() - is link speed report set + * actual + * @psoc: pointer to psoc object + * + * Return: True is report set to actual + */ +bool +ucfg_mlme_stats_is_link_speed_report_actual(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_mlme_stats_is_link_speed_report_max() - is link speed report set + * max + * @psoc: pointer to psoc object + * + * Return: True is report set to max + */ +bool +ucfg_mlme_stats_is_link_speed_report_max(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_mlme_stats_is_link_speed_report_max_scaled() - is link speed report set + * max scaled + * @psoc: pointer to psoc object + * + * Return: True is report set to max scaled + */ +bool +ucfg_mlme_stats_is_link_speed_report_max_scaled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_mlme_get_ibss_cfg() - Get IBSS config params data structure + * @psoc: pointer to psoc object + * @auto_bssid: Pointer to return the IBSS config data structure + * + * Return: QDF Status + */ +QDF_STATUS ucfg_mlme_get_ibss_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_ibss_cfg *ibss_cfg); + +/** + * ucfg_mlme_set_ibss_auto_bssid() - Set IBSS Auto BSSID config + * @psoc: pointer to psoc object + * @auto_bssid: IBSS Auto BSSID config value + * + * Return: QDF Status + */ +QDF_STATUS ucfg_mlme_set_ibss_auto_bssid(struct wlan_objmgr_psoc *psoc, + uint32_t auto_bssid); + +/** + * ucfg_mlme_ibss_power_save_setup() - Set IBSS power save params + * @psoc: pointer to psoc object + * @vdev_id: IBSS Vdev ID + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_ibss_power_save_setup(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id) +{ + return wlan_mlme_ibss_power_save_setup(psoc, vdev_id); +} + +/** + * ucfg_mlme_get_tl_delayed_trgr_frm_int() - Get delay interval(in ms) + * of UAPSD auto trigger. + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: None + */ +static inline +void ucfg_mlme_get_tl_delayed_trgr_frm_int(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + wlan_mlme_get_tl_delayed_trgr_frm_int(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_dir_ac_vi() - Get TSPEC direction + * for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_dir_ac_vi(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_wmm_dir_ac_vi(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_nom_msdu_size_ac_vi() - Get normal + * MSDU size for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_wmm_nom_msdu_size_ac_vi(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + return wlan_mlme_get_wmm_nom_msdu_size_ac_vi(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_mean_data_rate_ac_vi() - mean data + * rate for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_wmm_mean_data_rate_ac_vi(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_mean_data_rate_ac_vi(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_min_phy_rate_ac_vi() - min PHY + * rate for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_wmm_min_phy_rate_ac_vi(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_min_phy_rate_ac_vi(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_sba_ac_vi() - surplus bandwidth + * allowance for VI + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_sba_ac_vi(struct wlan_objmgr_psoc *psoc, uint16_t *value) +{ + return wlan_mlme_get_wmm_sba_ac_vi(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_uapsd_vi_srv_intv() - Get Uapsd service + * interval for video + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_uapsd_vi_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_uapsd_vi_srv_intv(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_uapsd_vi_sus_intv() - Get Uapsd suspension + * interval for video + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_uapsd_vi_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_uapsd_vi_sus_intv(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_dir_ac_be() - Get TSPEC direction + * for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_dir_ac_be(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_wmm_dir_ac_be(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_nom_msdu_size_ac_be() - Get normal + * MSDU size for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_wmm_nom_msdu_size_ac_be(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + return wlan_mlme_get_wmm_nom_msdu_size_ac_be(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_mean_data_rate_ac_be() - mean data + * rate for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_wmm_mean_data_rate_ac_be(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_mean_data_rate_ac_be(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_min_phy_rate_ac_be() - min PHY + * rate for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_wmm_min_phy_rate_ac_be(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_min_phy_rate_ac_be(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_sba_ac_be() - surplus bandwidth + * allowance for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_sba_ac_be(struct wlan_objmgr_psoc *psoc, uint16_t *value) +{ + return wlan_mlme_get_wmm_sba_ac_be(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_uapsd_be_srv_intv() - Get Uapsd service + * interval for BE + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_uapsd_be_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_uapsd_be_srv_intv(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_uapsd_be_sus_intv() - Get Uapsd suspension + * interval for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_uapsd_be_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_uapsd_be_sus_intv(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_dir_ac_bk() - Get TSPEC direction + * for BK + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_dir_ac_bk(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_wmm_dir_ac_bk(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_nom_msdu_size_ac_be() - Get normal + * MSDU size for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_wmm_nom_msdu_size_ac_bk(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + return wlan_mlme_get_wmm_nom_msdu_size_ac_bk(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_mean_data_rate_ac_bk() - mean data + * rate for BK + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_wmm_mean_data_rate_ac_bk(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_mean_data_rate_ac_bk(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_min_phy_rate_ac_bk() - min PHY + * rate for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_wmm_min_phy_rate_ac_bk(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_min_phy_rate_ac_bk(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_sba_ac_bk() - surplus bandwidth + * allowance for BE + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_sba_ac_bk(struct wlan_objmgr_psoc *psoc, uint16_t *value) +{ + return wlan_mlme_get_wmm_sba_ac_bk(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_uapsd_bk_srv_intv() - Get Uapsd service + * interval for BK + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_uapsd_bk_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_uapsd_bk_srv_intv(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_uapsd_bk_sus_intv() - Get Uapsd suspension + * interval for BK + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_uapsd_bk_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_wmm_uapsd_bk_sus_intv(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_mode() - Enable WMM feature + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_mode(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_wmm_mode(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_wlm_level() - Get the WLM level value + * @psoc: pointer to psoc object + * @level: level that needs to be filled. + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_cfg_get_wlm_level(struct wlan_objmgr_psoc *psoc, + uint8_t *level) +{ + return mlme_get_cfg_wlm_level(psoc, level); +} + +/** + * ucfg_mlme_cfg_get_wlm_reset() - Get the WLM reset flag + * @psoc: pointer to psoc object + * @reset: reset that needs to be filled. + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_cfg_get_wlm_reset(struct wlan_objmgr_psoc *psoc, + bool *reset) +{ + return mlme_get_cfg_wlm_reset(psoc, reset); +} + +#ifdef WLAN_FEATURE_11AX +/** + * ucfg_mlme_update_tgt_he_cap() - Update tgt he cap in mlme component + * + * @psoc: pointer to psoc object + * @cfg: pointer to config params from target + * + * Inline UCFG API to be used by HDD/OSIF callers to update + * he caps in mlme. + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_FAILURE + */ +static inline QDF_STATUS +ucfg_mlme_update_tgt_he_cap(struct wlan_objmgr_psoc *psoc, + struct wma_tgt_cfg *cfg) +{ + return mlme_update_tgt_he_caps_in_cfg(psoc, cfg); +} + +/** + * ucfg_mlme_cfg_get_he_caps() - Get the HE capability info + * @psoc: pointer to psoc object + * @he_cap: Caps that needs to be filled. + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_cfg_get_he_caps(struct wlan_objmgr_psoc *psoc, + tDot11fIEhe_cap *he_cap) +{ + return mlme_cfg_get_he_caps(psoc, he_cap); +} + +/** + * ucfg_mlme_cfg_get_he_ul_mumimo() - Get the HE Ul Mumio + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_cfg_get_he_ul_mumimo(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_cfg_get_he_ul_mumimo(psoc, value); +} + +/** + * ucfg_mlme_cfg_set_he_ul_mumimo() - Set the HE Ul Mumio + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_cfg_set_he_ul_mumimo(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + return wlan_mlme_cfg_set_he_ul_mumimo(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_enable_ul_mimo() - Get the HE Ul mimo + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_cfg_get_enable_ul_mimo(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_cfg_get_enable_ul_mimo(psoc, value); +} + +/** + * ucfg_mlme_cfg_get_enable_ul_ofdm() - Get enable ul ofdm + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_cfg_get_enable_ul_ofdm(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_cfg_get_enable_ul_ofdm(psoc, value); +} +#endif + +/** + * ucfg_mlme_get_80211e_is_enabled() - Enable 802.11e feature + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_80211e_is_enabled(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_80211e_is_enabled(psoc, value); +} + +/** + * ucfg_mlme_get_wmm_uapsd_mask() - setup U-APSD mask for ACs + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_wmm_uapsd_mask(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + return wlan_mlme_get_wmm_uapsd_mask(psoc, value); +} + +/** + * ucfg_mlme_get_implicit_qos_is_enabled() - Enable implicit QOS + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_mlme_get_implicit_qos_is_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_implicit_qos_is_enabled(psoc, value); +} + +#ifdef FEATURE_WLAN_ESE +/** + * ucfg_mlme_get_inactivity_interval() - Infra Inactivity Interval + * @psoc: pointer to psoc object + * @value: Value that needs to be get from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: None + */ +static inline void +ucfg_mlme_get_inactivity_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + wlan_mlme_get_inactivity_interval(psoc, value); +} + +/** + * ucfg_mlme_is_ese_enabled() - ese feature enable or not + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_is_ese_enabled(struct wlan_objmgr_psoc *psoc, bool *val); +#endif /* FEATURE_WLAN_ESE */ + +/** + * ucfg_mlme_get_is_ts_burst_size_enable() - Get TS burst size flag + * @psoc: pointer to psoc object + * @value: Value that needs to be get from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: None + */ +static inline +void ucfg_mlme_get_is_ts_burst_size_enable(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + wlan_mlme_get_is_ts_burst_size_enable(psoc, value); +} + +/** + * ucfg_mlme_get_ts_info_ack_policy() - Get TS ack policy + * @psoc: pointer to psoc object + * @value: Value that needs to be get from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: None + */ +static inline void +ucfg_mlme_get_ts_info_ack_policy(struct wlan_objmgr_psoc *psoc, + enum mlme_ts_info_ack_policy *value) +{ + wlan_mlme_get_ts_info_ack_policy(psoc, value); +} + +/** + * ucfg_mlme_get_ts_acm_value_for_ac() - Get ACM value for AC + * @psoc: pointer to psoc object + * @value: Value that needs to be get from the caller + * + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_ts_acm_value_for_ac(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_get_ts_acm_value_for_ac(psoc, value); +} + +/* + * ucfg_mlme_is_sap_uapsd_enabled() - SAP UAPSD enabled status. + * @psoc: pointer to psoc object + * @value: sap uapsd enabled flag value requested from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_is_sap_uapsd_enabled(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_is_sap_uapsd_enabled(psoc, value); +} + +/* + * ucfg_mlme_set_sap_uapsd_flag() - SAP UAPSD enabled status. + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_set_sap_uapsd_flag(struct wlan_objmgr_psoc *psoc, bool value) +{ + return wlan_mlme_set_sap_uapsd_flag(psoc, value); +} + +/** + * ucfg_mlme_get_enable_deauth_to_disassoc_map() - Enable deauth_to_disassoc_map + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_enable_deauth_to_disassoc_map(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * ucfg_mlme_get_ap_random_bssid_enable() - Enable random bssid + * @psoc: pointer to psoc object + * @value: Value that needs to be set from the caller + * + * UCFG API to be used by HDD/OSIF callers + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_ap_random_bssid_enable(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * ucfg_mlme_get_sta_miracast_mcc_rest_time() - Get STA/MIRACAST MCC rest time + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * This API gives rest time to be used when STA and MIRACAST MCC conc happens + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +ucfg_mlme_get_sta_miracast_mcc_rest_time(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_sta_miracast_mcc_rest_time(psoc, value); +} + +/** + * ucfg_mlme_get_sap_mcc_chnl_avoid() - Check if SAP MCC needs to be avoided + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * This API fetches the user setting to determine if SAP MCC with other persona + * to be avoided. + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +ucfg_mlme_get_sap_mcc_chnl_avoid(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_sap_mcc_chnl_avoid(psoc, value); +} + +/** + * ucfg_mlme_get_mcc_bcast_prob_resp() - Get broadcast probe rsp in MCC + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * To get INI value which helps to determe whether to enable/disable use of + * broadcast probe response to increase the detectability of SAP in MCC mode. + * + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +ucfg_mlme_get_mcc_bcast_prob_resp(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_mcc_bcast_prob_resp(psoc, value); +} + +/** + * ucfg_mlme_get_mcc_rts_cts_prot() - To get RTS-CTS protection in MCC. + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * To get INI value which helps to determine whether to enable/disable + * use of long duration RTS-CTS protection when SAP goes off + * channel in MCC mode. + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +ucfg_mlme_get_mcc_rts_cts_prot(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_mcc_rts_cts_prot(psoc, value); +} + +/** + * ucfg_mlme_get_mcc_feature() - To find out to enable/disable MCC feature + * + * @psoc: pointer to psoc object + * @value: value which needs to filled by API + * + * To get INI value which helps to determine whether to enable MCC feature + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +ucfg_mlme_get_mcc_feature(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + return wlan_mlme_get_mcc_feature(psoc, value); +} + +/** + * ucfg_wlan_mlme_get_rrm_enabled() - Get the rrm enabled + * @psoc: pointer to psoc object + * @value: Value that needs to be get from the caller + * + * Return: QDF Status + */ +static inline +QDF_STATUS ucfg_wlan_mlme_get_rrm_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + return wlan_mlme_get_rrm_enabled(psoc, value); +} + +/** + * ucfg_mlme_get_latency_enable() - Get the latency_enable + * @psoc: pointer to psoc object + * @value: Value that needs to be get from the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_latency_enable(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * ucfg_mlme_get_dtim_selection_diversity() - get dtim selection diversity + * bitmap + * @psoc: pointer to psoc object + * @dtim_selection_div: value that is requested by the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static inline QDF_STATUS +ucfg_mlme_get_dtim_selection_diversity(struct wlan_objmgr_psoc *psoc, + uint32_t *dtim_selection_div) +{ + return wlan_mlme_get_dtim_selection_diversity(psoc, dtim_selection_div); +} + +/** + * ucfg_mlme_get_bmps_min_listen_interval() - get beacon mode powersave + * minimum listen interval value + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static inline QDF_STATUS +ucfg_mlme_get_bmps_min_listen_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_bmps_min_listen_interval(psoc, value); +} + +/** + * ucfg_mlme_get_bmps_max_listen_interval() - get beacon mode powersave + * maximum listen interval value + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static inline QDF_STATUS +ucfg_mlme_get_bmps_max_listen_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_bmps_max_listen_interval(psoc, value); +} + +/** + * ucfg_mlme_get_auto_bmps_timer_value() - get bmps timer value + * minimum listen interval value + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static inline QDF_STATUS +ucfg_mlme_get_auto_bmps_timer_value(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + return wlan_mlme_get_auto_bmps_timer_value(psoc, value); +} + +/** + * ucfg_mlme_is_bmps_enabled() - check if beacon mode powersave is + * enabled/disabled + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static inline QDF_STATUS +ucfg_mlme_is_bmps_enabled(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_is_bmps_enabled(psoc, value); +} + +/** + * ucfg_mlme_is_imps_enabled() - check if idle mode powersave is + * enabled/disabled + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static inline QDF_STATUS +ucfg_mlme_is_imps_enabled(struct wlan_objmgr_psoc *psoc, bool *value) +{ + return wlan_mlme_is_imps_enabled(psoc, value); +} + +/** + * ucfg_mlme_override_bmps_imps() - disable imps/bmps as part of + * override to disable all ps features + * @psoc: pointer to psoc object + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static inline QDF_STATUS +ucfg_mlme_override_bmps_imps(struct wlan_objmgr_psoc *psoc) +{ + return wlan_mlme_override_bmps_imps(psoc); +} + +#ifdef MWS_COEX +/** + * ucfg_mlme_get_mws_coex_4g_quick_tdm() - Get mws coex 4g quick tdm + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_mws_coex_4g_quick_tdm(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * ucfg_mlme_get_mws_coex_5g_nr_pwr_limit() - Get mws coex 5g nr pwr limit + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_mws_coex_5g_nr_pwr_limit(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * ucfg_mlme_get_mws_coex_pcc_channel_avoid_delay() - Get mws coex pcc + * avoid channel delay + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_mws_coex_pcc_channel_avoid_delay(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * ucfg_mlme_get_mws_coex_scc_channel_avoid_delay() - Get mws coex scc + * avoidance channel delay + * @psoc: pointer to psoc object + * @val: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_mws_coex_scc_channel_avoid_delay(struct wlan_objmgr_psoc *psoc, + uint32_t *val); +#endif + +/** + * ucfg_mlme_get_etsi_srd_chan_in_master_mode - get etsi srd chan + * in master mode + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_etsi_srd_chan_in_master_mode(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * ucfg_mlme_get_srd_master_mode_for_vdev() - Get SRD master mode for vdev + * @psoc: pointer to psoc object + * @vdev_opmode: vdev opmode + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_srd_master_mode_for_vdev(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE vdev_opmode, + bool *value); + +#ifdef SAP_AVOID_ACS_FREQ_LIST +/** + * ucfg_mlme_get_acs_avoid_freq_list - get acs avoid frequency list + * @psoc: pointer to psoc object + * @freq_list: Pointer to output freq list + * @freq_list_num: Pointer to the output number of frequencies filled + * in the freq_list + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_acs_avoid_freq_list(struct wlan_objmgr_psoc *psoc, + uint16_t *freq_list, uint8_t *freq_list_num); + +#else +static inline QDF_STATUS +ucfg_mlme_get_acs_avoid_freq_list(struct wlan_objmgr_psoc *psoc, + uint16_t *freq_list, uint8_t *freq_list_num) +{ + *freq_list_num = 0; + return QDF_STATUS_E_INVAL; +} +#endif + +/** + * ucfg_mlme_get_11d_in_world_mode - get whether 11d is enabled in world mode + * in master mode + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_11d_in_world_mode(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * ucfg_mlme_restart_beaconing_on_ch_avoid() - get restart beaconing on ch avoid + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_restart_beaconing_on_ch_avoid(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * ucfg_mlme_get_indoor_channel_support() - get indoor channel support + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_indoor_channel_support(struct wlan_objmgr_psoc *psoc, + bool *value); + +/** + * ucfg_mlme_get_scan_11d_interval() - get scan 11d interval + * @psoc: pointer to psoc object + * @value: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_scan_11d_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * ucfg_mlme_get_nol_across_regdmn() - get scan 11d interval + * @psoc: pointer to psoc object + * @value: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ + +QDF_STATUS +ucfg_mlme_get_nol_across_regdmn(struct wlan_objmgr_psoc *psoc, bool *value); + +/** + * ucfg_mlme_get_valid_channel_freq_list() - get valid channel + * list + * @psoc: pointer to psoc object + * @channel_list: pointer to return channel list + * @channel_list_num: pointer to return channel list number + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_valid_channel_freq_list(struct wlan_objmgr_psoc *psoc, + uint32_t *channel_list, + uint32_t *channel_list_num); + +#ifdef FEATURE_LFR_SUBNET_DETECTION +/** + * ucfg_mlme_is_subnet_detection_enabled() - check if sub net detection is + * enabled/disabled + * @psoc: pointer to psoc object + * @value: value that is requested by the caller + * + * Inline UCFG API to be used by HDD/OSIF callers + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS +ucfg_mlme_is_subnet_detection_enabled(struct wlan_objmgr_psoc *psoc, bool *val); +#else +static QDF_STATUS +ucfg_mlme_is_subnet_detection_enabled(struct wlan_objmgr_psoc *psoc, bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_LFR_SUBNET_DETECTION */ + +/** + * ucfg_mlme_set_current_tx_power_level() - set current tx power level + * @psoc: pointer to psoc object + * @value: data to be set + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_current_tx_power_level(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * ucfg_mlme_get_current_tx_power_level() - get current tx power level + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_current_tx_power_level(struct wlan_objmgr_psoc *psoc, + uint8_t *value); + +/** + * ucfg_mlme_set_obss_detection_offload_enabled() - Enable obss offload + * @psoc: pointer to psoc object + * @value: enable or disable + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_obss_detection_offload_enabled(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * ucfg_mlme_set_obss_color_collision_offload_enabled() - Enable obss color + * collision offload + * @psoc: pointer to psoc object + * @value: enable or disable + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_obss_color_collision_offload_enabled( + struct wlan_objmgr_psoc *psoc, uint8_t value); + +/** + * ucfg_mlme_set_bss_color_collision_det_sta() - Enable bss color + * collision detection offload for STA mode + * @psoc: pointer to psoc object + * @value: enable or disable + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_bss_color_collision_det_sta(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * ucfg_mlme_set_restricted_80p80_bw_supp() - Set the restricted 80p80 support + * @psoc: pointer to psoc object + * @restricted_80p80_supp: Value to be set from the caller + * + * Return: QDF Status + */ +QDF_STATUS ucfg_mlme_set_restricted_80p80_bw_supp(struct wlan_objmgr_psoc *psoc, + bool restricted_80p80_supp); + +/** + * ucfg_mlme_get_restricted_80p80_bw_supp() - Get the restricted 80p80 support + * @psoc: pointer to psoc object + * + * Return: true or false + */ +bool ucfg_mlme_get_restricted_80p80_bw_supp(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_mlme_get_channel_bonding_24ghz() - get channel bonding mode of 24ghz + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_channel_bonding_24ghz(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * ucfg_mlme_set_channel_bonding_24ghz() - set channel bonding mode for 24ghz + * @psoc: pointer to psoc object + * @value: channel bonding mode + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_channel_bonding_24ghz(struct wlan_objmgr_psoc *psoc, + uint32_t value); +/** + * ucfg_mlme_get_channel_bonding_5ghz() - get channel bonding mode of 5ghz + * @psoc: pointer to psoc object + * @value: pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_channel_bonding_5ghz(struct wlan_objmgr_psoc *psoc, + uint32_t *value); + +/** + * ucfg_mlme_set_channel_bonding_5ghz() - set channel bonding mode for 5ghz + * @psoc: pointer to psoc object + * @value: channel bonding mode + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_channel_bonding_5ghz(struct wlan_objmgr_psoc *psoc, + uint32_t value); + +/** + * ucfg_mlme_get_peer_phymode() - get phymode of peer + * @psoc: pointer to psoc object + * @mac: Pointer to the mac addr of the peer + * @peer_phymode: phymode + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_peer_phymode(struct wlan_objmgr_psoc *psoc, uint8_t *mac, + enum wlan_phymode *peer_phymode) +{ + return mlme_get_peer_phymode(psoc, mac, peer_phymode); +} + +/** + * ucfg_mlme_validate_full_roam_scan_period() - Validate full roam scan period + * @full_roam_scan_period: Idle period in seconds between two successive + * full channel roam scans + * + * Return: True if full_roam_scan_period is in expected range, false otherwise. + */ +bool ucfg_mlme_validate_full_roam_scan_period(uint32_t full_roam_scan_period); + +/** + * ucfg_mlme_validate_scan_period() - Validate if scan period is in valid range + * @value: Scan period in msec + * + * Return: True if roam_scan_period is in expected range, false otherwise. + */ +bool ucfg_mlme_validate_scan_period(uint32_t roam_scan_period); +/** + * ucfg_mlme_get_ignore_fw_reg_offload_ind() - Get the + * ignore_fw_reg_offload_ind ini + * @psoc: pointer to psoc object + * @disabled: output pointer to hold user config + * + * Return: QDF Status + */ +static inline QDF_STATUS +ucfg_mlme_get_ignore_fw_reg_offload_ind(struct wlan_objmgr_psoc *psoc, + bool *disabled) +{ + return wlan_mlme_get_ignore_fw_reg_offload_ind(psoc, disabled); +} + +/** + * ucfg_mlme_get_peer_unmap_conf() - Indicate if peer unmap confirmation + * support is enabled or disabled + * @psoc: pointer to psoc object + * + * Return: true if peer unmap confirmation support is enabled, else false + */ +static inline +QDF_STATUS ucfg_mlme_get_peer_unmap_conf(struct wlan_objmgr_psoc *psoc) +{ + return wlan_mlme_get_peer_unmap_conf(psoc); +} + +/** + * ucfg_mlme_get_discon_reason_n_from_ap() - Get disconnect reason and from ap + * @psoc: PSOC pointer + * @vdev_id: vdev id + * @from_ap: Get the from_ap cached through mlme_set_discon_reason_n_from_ap + * and copy to this buffer. + * @reason_code: Get the reason_code cached through + * mlme_set_discon_reason_n_from_ap and copy to this buffer. + * + * Fetch the contents of from_ap and reason_codes. + * + * Return: void + */ +static inline void +ucfg_mlme_get_discon_reason_n_from_ap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool *from_ap, + uint32_t *reason_code) +{ + mlme_get_discon_reason_n_from_ap(psoc, vdev_id, from_ap, reason_code); +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * ucfg_mlme_get_roam_reason_vsie_status() - Get roam reason vsie is + * enabled or disabled + * @psoc: pointer to psoc object + * @roam_reason_vsie_enabled: pointer to hold value of roam reason vsie + * + * Return: Success if able to get bcn rpt err vsie value, else failure + */ +static inline QDF_STATUS +ucfg_mlme_get_roam_reason_vsie_status(struct wlan_objmgr_psoc *psoc, + uint8_t *roam_reason_vsie_enabled) +{ + return wlan_mlme_get_roam_reason_vsie_status(psoc, + roam_reason_vsie_enabled); +} + +/** + * ucfg_mlme_set_roam_reason_vsie_status() - Update roam reason vsie status + * value with user configured value + * @psoc: pointer to psoc object + * @roam_reason_vsie_enabled: value of roam reason vsie status + * + * Return: Success if able to get bcn rpt err vsie value, else failure + */ +static inline QDF_STATUS +ucfg_mlme_set_roam_reason_vsie_status(struct wlan_objmgr_psoc *psoc, + uint8_t roam_reason_vsie_enabled) +{ + return wlan_mlme_set_roam_reason_vsie_status(psoc, + roam_reason_vsie_enabled); +} + +#endif + +/** + * ucfg_is_roaming_enabled() - Check if roaming enabled + * to firmware. + * @psoc: psoc context + * @vdev_id: vdev id + * + * Return: True if Roam state machine is in + * WLAN_ROAM_RSO_ENABLED/WLAN_ROAMING_IN_PROG/WLAN_ROAM_SYNCH_IN_PROG + */ +bool ucfg_is_roaming_enabled(struct wlan_objmgr_pdev *pdev, uint8_t vdev_id); +#endif /* _WLAN_MLME_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/src/wlan_mlme_api.c b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/src/wlan_mlme_api.c new file mode 100644 index 0000000000000000000000000000000000000000..2fa9d7df4928ec4b77678af20671e9218d0705c9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/src/wlan_mlme_api.c @@ -0,0 +1,4086 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define public APIs exposed by the mlme component + */ + +#include "cfg_ucfg_api.h" +#include "wlan_mlme_main.h" +#include "wlan_mlme_ucfg_api.h" +#include "wma_types.h" +#include "wmi_unified.h" +#include "wma.h" +#include "wma_internal.h" +#include "wlan_crypto_global_api.h" +#include "wlan_utility.h" +#include "wlan_policy_mgr_ucfg.h" + +QDF_STATUS wlan_mlme_get_cfg_str(uint8_t *dst, struct mlme_cfg_str *cfg_str, + qdf_size_t *len) +{ + if (*len < cfg_str->len) { + mlme_legacy_err("Invalid len %zd", *len); + return QDF_STATUS_E_INVAL; + } + + *len = cfg_str->len; + qdf_mem_copy(dst, cfg_str->data, *len); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_cfg_str(uint8_t *src, struct mlme_cfg_str *dst_cfg_str, + qdf_size_t len) +{ + if (len > dst_cfg_str->max_len) { + mlme_legacy_err("Invalid len %zd (>%zd)", len, + dst_cfg_str->max_len); + return QDF_STATUS_E_INVAL; + } + + dst_cfg_str->len = len; + qdf_mem_copy(dst_cfg_str->data, src, len); + + return QDF_STATUS_SUCCESS; +} + +uint8_t wlan_mlme_get_tx_power(struct wlan_objmgr_psoc *psoc, + enum band_info band) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return 0; + + switch (band) { + case BAND_2G: + return mlme_obj->cfg.power.tx_power_2g; + case BAND_5G: + return mlme_obj->cfg.power.tx_power_5g; + default: + break; + } + return 0; +} + +char *wlan_mlme_get_power_usage(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return NULL; + + return mlme_obj->cfg.power.power_usage.data; +} + +QDF_STATUS wlan_mlme_get_ht_cap_info(struct wlan_objmgr_psoc *psoc, + struct mlme_ht_capabilities_info + *ht_cap_info) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *ht_cap_info = mlme_obj->cfg.ht_caps.ht_cap_info; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_ht_cap_info(struct wlan_objmgr_psoc *psoc, + struct mlme_ht_capabilities_info + ht_cap_info) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.ht_caps.ht_cap_info = ht_cap_info; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_max_amsdu_num(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.ht_caps.max_num_amsdu; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_max_amsdu_num(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + if (!cfg_in_range(CFG_MAX_AMSDU_NUM, value)) { + mlme_legacy_err("Error in setting Max AMSDU Num"); + return QDF_STATUS_E_INVAL; + } + + mlme_obj->cfg.ht_caps.max_num_amsdu = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_ht_mpdu_density(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = (uint8_t)mlme_obj->cfg.ht_caps.ampdu_params.mpdu_density; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_ht_mpdu_density(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + if (!cfg_in_range(CFG_MPDU_DENSITY, value)) { + mlme_legacy_err("Invalid value %d", value); + return QDF_STATUS_E_INVAL; + } + + mlme_obj->cfg.ht_caps.ampdu_params.mpdu_density = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_band_capability(struct wlan_objmgr_psoc *psoc, + uint32_t *band_capability) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *band_capability = mlme_obj->cfg.gen.band_capability; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_band_capability(struct wlan_objmgr_psoc *psoc, + uint32_t band_capability) + +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.gen.band_capability = band_capability; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_prevent_link_down(struct wlan_objmgr_psoc *psoc, + bool *prevent_link_down) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *prevent_link_down = mlme_obj->cfg.gen.prevent_link_down; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_select_5ghz_margin(struct wlan_objmgr_psoc *psoc, + uint8_t *select_5ghz_margin) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *select_5ghz_margin = mlme_obj->cfg.gen.select_5ghz_margin; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_rtt_mac_randomization(struct wlan_objmgr_psoc *psoc, + bool *rtt_mac_randomization) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *rtt_mac_randomization = mlme_obj->cfg.gen.rtt_mac_randomization; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_crash_inject(struct wlan_objmgr_psoc *psoc, + bool *crash_inject) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *crash_inject = mlme_obj->cfg.gen.crash_inject; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_lpass_support(struct wlan_objmgr_psoc *psoc, + bool *lpass_support) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *lpass_support = mlme_obj->cfg.gen.lpass_support; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_self_recovery(struct wlan_objmgr_psoc *psoc, + bool *self_recovery) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *self_recovery = mlme_obj->cfg.gen.self_recovery; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sub_20_chan_width(struct wlan_objmgr_psoc *psoc, + uint8_t *sub_20_chan_width) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *sub_20_chan_width = mlme_obj->cfg.gen.sub_20_chan_width; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_fw_timeout_crash(struct wlan_objmgr_psoc *psoc, + bool *fw_timeout_crash) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *fw_timeout_crash = mlme_obj->cfg.gen.fw_timeout_crash; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_ito_repeat_count(struct wlan_objmgr_psoc *psoc, + uint8_t *ito_repeat_count) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *ito_repeat_count = mlme_obj->cfg.gen.ito_repeat_count; + + return QDF_STATUS_SUCCESS; +} + +void wlan_mlme_get_sap_inactivity_override(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return; + + *val = mlme_obj->cfg.qos_mlme_params.sap_max_inactivity_override; +} + +QDF_STATUS wlan_mlme_get_acs_with_more_param(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.acs.is_acs_with_more_param; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_auto_channel_weight(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) { + *value = cfg_default(CFG_AUTO_CHANNEL_SELECT_WEIGHT); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.acs.auto_channel_select_weight; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_vendor_acs_support(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.acs.is_vendor_acs_support; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_acs_support_for_dfs_ltecoex(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.acs.is_acs_support_for_dfs_ltecoex; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_external_acs_policy(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.acs.is_external_acs_policy; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_tx_chainmask_cck(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.chainmask_cfg.tx_chain_mask_cck; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_tx_chainmask_1ss(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.chainmask_cfg.tx_chain_mask_1ss; + return QDF_STATUS_SUCCESS; +} + +void +wlan_mlme_update_cfg_with_tgt_caps(struct wlan_objmgr_psoc *psoc, + struct mlme_tgt_caps *tgt_caps) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return; + + /* Update the mlme cfg according to the tgt capability received */ + + mlme_obj->cfg.gen.data_stall_recovery_fw_support = + tgt_caps->data_stall_recovery_fw_support; + mlme_obj->cfg.gen.stop_all_host_scan_support = + tgt_caps->stop_all_host_scan_support; +} + +#ifdef WLAN_FEATURE_11AX +QDF_STATUS wlan_mlme_cfg_get_he_ul_mumimo(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.he_caps.dot11_he_cap.ul_mu; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_cfg_get_he_caps(struct wlan_objmgr_psoc *psoc, + tDot11fIEhe_cap *he_cap) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *he_cap = mlme_obj->cfg.he_caps.he_cap_orig; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_cfg_set_he_ul_mumimo(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + if (!cfg_in_range(CFG_HE_UL_MUMIMO, value)) { + mlme_legacy_debug("Failed to set CFG_HE_UL_MUMIMO with %d", + value); + return QDF_STATUS_E_FAILURE; + } + + mlme_obj->cfg.he_caps.dot11_he_cap.ul_mu = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_cfg_get_enable_ul_mimo(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.he_caps.enable_ul_mimo; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_cfg_get_enable_ul_ofdm(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + + *value = mlme_obj->cfg.he_caps.enable_ul_ofdm; + + return QDF_STATUS_SUCCESS; +} + +/* mlme_get_min_rate_cap() - get minimum capability for HE-MCS between + * ini value and fw capability. + * + * Rx HE-MCS Map and Tx HE-MCS Map subfields format where 2-bit indicates + * 0 indicates support for HE-MCS 0-7 for n spatial streams + * 1 indicates support for HE-MCS 0-9 for n spatial streams + * 2 indicates support for HE-MCS 0-11 for n spatial streams + * 3 indicates that n spatial streams is not supported for HE PPDUs + * + */ +static uint16_t mlme_get_min_rate_cap(uint16_t val1, uint16_t val2) +{ + uint16_t ret = 0, i; + + for (i = 0; i < 8; i++) { + if (((val1 >> (2 * i)) & 0x3) == 0x3 || + ((val2 >> (2 * i)) & 0x3) == 0x3) { + ret |= 0x3 << (2 * i); + continue; + } + ret |= QDF_MIN((val1 >> (2 * i)) & 0x3, + (val2 >> (2 * i)) & 0x3) << (2 * i); + } + return ret; +} + +QDF_STATUS mlme_update_tgt_he_caps_in_cfg(struct wlan_objmgr_psoc *psoc, + struct wma_tgt_cfg *wma_cfg) +{ + uint8_t chan_width; + QDF_STATUS status = QDF_STATUS_SUCCESS; + tDot11fIEhe_cap *he_cap = &wma_cfg->he_cap; + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + uint8_t value; + uint16_t tx_mcs_map = 0; + uint16_t rx_mcs_map = 0; + uint8_t nss; + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.he_caps.dot11_he_cap.present = 1; + mlme_obj->cfg.he_caps.dot11_he_cap.htc_he = he_cap->htc_he; + + mlme_obj->cfg.he_caps.dot11_he_cap.twt_request = + he_cap->twt_request; + mlme_obj->cfg.he_caps.dot11_he_cap.twt_responder = + he_cap->twt_responder; + + value = QDF_MIN( + he_cap->fragmentation, + mlme_obj->cfg.he_caps.he_dynamic_fragmentation); + + if (cfg_in_range(CFG_HE_FRAGMENTATION, value)) + mlme_obj->cfg.he_caps.dot11_he_cap.fragmentation = value; + + if (cfg_in_range(CFG_HE_MAX_FRAG_MSDU, + he_cap->max_num_frag_msdu_amsdu_exp)) + mlme_obj->cfg.he_caps.dot11_he_cap.max_num_frag_msdu_amsdu_exp = + he_cap->max_num_frag_msdu_amsdu_exp; + if (cfg_in_range(CFG_HE_MIN_FRAG_SIZE, he_cap->min_frag_size)) + mlme_obj->cfg.he_caps.dot11_he_cap.min_frag_size = + he_cap->min_frag_size; + if (cfg_in_range(CFG_HE_TRIG_PAD, he_cap->trigger_frm_mac_pad)) + mlme_obj->cfg.he_caps.dot11_he_cap.trigger_frm_mac_pad = + he_cap->trigger_frm_mac_pad; + if (cfg_in_range(CFG_HE_MTID_AGGR_RX, he_cap->multi_tid_aggr_rx_supp)) + mlme_obj->cfg.he_caps.dot11_he_cap.multi_tid_aggr_rx_supp = + he_cap->multi_tid_aggr_rx_supp; + if (cfg_in_range(CFG_HE_MTID_AGGR_TX, he_cap->multi_tid_aggr_tx_supp)) + mlme_obj->cfg.he_caps.dot11_he_cap.multi_tid_aggr_tx_supp = + he_cap->multi_tid_aggr_tx_supp; + if (cfg_in_range(CFG_HE_LINK_ADAPTATION, he_cap->he_link_adaptation)) + mlme_obj->cfg.he_caps.dot11_he_cap.he_link_adaptation = + he_cap->he_link_adaptation; + mlme_obj->cfg.he_caps.dot11_he_cap.all_ack = he_cap->all_ack; + mlme_obj->cfg.he_caps.dot11_he_cap.trigd_rsp_sched = + he_cap->trigd_rsp_sched; + mlme_obj->cfg.he_caps.dot11_he_cap.a_bsr = he_cap->a_bsr; + mlme_obj->cfg.he_caps.dot11_he_cap.broadcast_twt = + he_cap->broadcast_twt; + mlme_obj->cfg.he_caps.dot11_he_cap.ba_32bit_bitmap = + he_cap->ba_32bit_bitmap; + mlme_obj->cfg.he_caps.dot11_he_cap.mu_cascade = he_cap->mu_cascade; + mlme_obj->cfg.he_caps.dot11_he_cap.ack_enabled_multitid = + he_cap->ack_enabled_multitid; + mlme_obj->cfg.he_caps.dot11_he_cap.omi_a_ctrl = he_cap->omi_a_ctrl; + mlme_obj->cfg.he_caps.dot11_he_cap.ofdma_ra = he_cap->ofdma_ra; + if (cfg_in_range(CFG_HE_MAX_AMPDU_LEN, he_cap->max_ampdu_len_exp_ext)) + mlme_obj->cfg.he_caps.dot11_he_cap.max_ampdu_len_exp_ext = + he_cap->max_ampdu_len_exp_ext; + mlme_obj->cfg.he_caps.dot11_he_cap.amsdu_frag = he_cap->amsdu_frag; + mlme_obj->cfg.he_caps.dot11_he_cap.flex_twt_sched = + he_cap->flex_twt_sched; + mlme_obj->cfg.he_caps.dot11_he_cap.rx_ctrl_frame = + he_cap->rx_ctrl_frame; + mlme_obj->cfg.he_caps.dot11_he_cap.bsrp_ampdu_aggr = + he_cap->bsrp_ampdu_aggr; + mlme_obj->cfg.he_caps.dot11_he_cap.qtp = he_cap->qtp; + mlme_obj->cfg.he_caps.dot11_he_cap.a_bqr = he_cap->a_bqr; + mlme_obj->cfg.he_caps.dot11_he_cap.spatial_reuse_param_rspder = + he_cap->spatial_reuse_param_rspder; + mlme_obj->cfg.he_caps.dot11_he_cap.ndp_feedback_supp = + he_cap->ndp_feedback_supp; + mlme_obj->cfg.he_caps.dot11_he_cap.ops_supp = he_cap->ops_supp; + mlme_obj->cfg.he_caps.dot11_he_cap.amsdu_in_ampdu = + he_cap->amsdu_in_ampdu; + mlme_obj->cfg.he_caps.dot11_he_cap.he_sub_ch_sel_tx_supp = + he_cap->he_sub_ch_sel_tx_supp; + mlme_obj->cfg.he_caps.dot11_he_cap.ul_2x996_tone_ru_supp = + he_cap->ul_2x996_tone_ru_supp; + mlme_obj->cfg.he_caps.dot11_he_cap.om_ctrl_ul_mu_data_dis_rx = + he_cap->om_ctrl_ul_mu_data_dis_rx; + mlme_obj->cfg.he_caps.dot11_he_cap.he_dynamic_smps = + he_cap->he_dynamic_smps; + mlme_obj->cfg.he_caps.dot11_he_cap.punctured_sounding_supp = + he_cap->punctured_sounding_supp; + mlme_obj->cfg.he_caps.dot11_he_cap.ht_vht_trg_frm_rx_supp = + he_cap->ht_vht_trg_frm_rx_supp; + + chan_width = HE_CH_WIDTH_COMBINE(he_cap->chan_width_0, + he_cap->chan_width_1, + he_cap->chan_width_2, + he_cap->chan_width_3, + he_cap->chan_width_4, + he_cap->chan_width_5, + he_cap->chan_width_6); + if (cfg_in_range(CFG_HE_CHAN_WIDTH, chan_width)) { + mlme_obj->cfg.he_caps.dot11_he_cap.chan_width_0 = + he_cap->chan_width_0; + mlme_obj->cfg.he_caps.dot11_he_cap.chan_width_1 = + he_cap->chan_width_1; + mlme_obj->cfg.he_caps.dot11_he_cap.chan_width_2 = + he_cap->chan_width_2; + mlme_obj->cfg.he_caps.dot11_he_cap.chan_width_3 = + he_cap->chan_width_3; + mlme_obj->cfg.he_caps.dot11_he_cap.chan_width_4 = + he_cap->chan_width_4; + mlme_obj->cfg.he_caps.dot11_he_cap.chan_width_5 = + he_cap->chan_width_5; + mlme_obj->cfg.he_caps.dot11_he_cap.chan_width_6 = + he_cap->chan_width_6; + } + if (cfg_in_range(CFG_HE_RX_PREAM_PUNC, he_cap->rx_pream_puncturing)) + mlme_obj->cfg.he_caps.dot11_he_cap.rx_pream_puncturing = + he_cap->rx_pream_puncturing; + mlme_obj->cfg.he_caps.dot11_he_cap.device_class = he_cap->device_class; + mlme_obj->cfg.he_caps.dot11_he_cap.ldpc_coding = he_cap->ldpc_coding; + if (cfg_in_range(CFG_HE_LTF_PPDU, he_cap->he_1x_ltf_800_gi_ppdu)) + mlme_obj->cfg.he_caps.dot11_he_cap.he_1x_ltf_800_gi_ppdu = + he_cap->he_1x_ltf_800_gi_ppdu; + if (cfg_in_range(CFG_HE_MIDAMBLE_RX_MAX_NSTS, + he_cap->midamble_tx_rx_max_nsts)) + mlme_obj->cfg.he_caps.dot11_he_cap.midamble_tx_rx_max_nsts = + he_cap->midamble_tx_rx_max_nsts; + mlme_obj->cfg.he_caps.dot11_he_cap.he_4x_ltf_3200_gi_ndp = + he_cap->he_4x_ltf_3200_gi_ndp; + if (mlme_obj->cfg.vht_caps.vht_cap_info.rx_stbc) { + mlme_obj->cfg.he_caps.dot11_he_cap.rx_stbc_lt_80mhz = + he_cap->rx_stbc_lt_80mhz; + mlme_obj->cfg.he_caps.dot11_he_cap.rx_stbc_gt_80mhz = + he_cap->rx_stbc_gt_80mhz; + } else { + mlme_obj->cfg.he_caps.dot11_he_cap.rx_stbc_lt_80mhz = 0; + mlme_obj->cfg.he_caps.dot11_he_cap.rx_stbc_gt_80mhz = 0; + } + if (mlme_obj->cfg.vht_caps.vht_cap_info.tx_stbc) { + mlme_obj->cfg.he_caps.dot11_he_cap.tb_ppdu_tx_stbc_lt_80mhz = + he_cap->tb_ppdu_tx_stbc_lt_80mhz; + mlme_obj->cfg.he_caps.dot11_he_cap.tb_ppdu_tx_stbc_gt_80mhz = + he_cap->tb_ppdu_tx_stbc_gt_80mhz; + } else { + mlme_obj->cfg.he_caps.dot11_he_cap.tb_ppdu_tx_stbc_lt_80mhz = 0; + mlme_obj->cfg.he_caps.dot11_he_cap.tb_ppdu_tx_stbc_gt_80mhz = 0; + } + + if (cfg_in_range(CFG_HE_DOPPLER, he_cap->doppler)) + mlme_obj->cfg.he_caps.dot11_he_cap.doppler = he_cap->doppler; + if (cfg_in_range(CFG_HE_DCM_TX, he_cap->dcm_enc_tx)) + mlme_obj->cfg.he_caps.dot11_he_cap.dcm_enc_tx = + he_cap->dcm_enc_tx; + if (cfg_in_range(CFG_HE_DCM_RX, he_cap->dcm_enc_rx)) + mlme_obj->cfg.he_caps.dot11_he_cap.dcm_enc_rx = + he_cap->dcm_enc_rx; + mlme_obj->cfg.he_caps.dot11_he_cap.ul_he_mu = he_cap->ul_he_mu; + if (mlme_obj->cfg.vht_caps.vht_cap_info.su_bformer) { + mlme_obj->cfg.he_caps.dot11_he_cap.su_beamformer = + he_cap->su_beamformer; + if (cfg_in_range(CFG_HE_NUM_SOUND_LT80, + he_cap->num_sounding_lt_80)) + mlme_obj->cfg.he_caps.dot11_he_cap.num_sounding_lt_80 = + he_cap->num_sounding_lt_80; + if (cfg_in_range(CFG_HE_NUM_SOUND_GT80, + he_cap->num_sounding_gt_80)) + mlme_obj->cfg.he_caps.dot11_he_cap.num_sounding_gt_80 = + he_cap->num_sounding_gt_80; + mlme_obj->cfg.he_caps.dot11_he_cap.mu_beamformer = + he_cap->mu_beamformer; + + } else { + mlme_obj->cfg.he_caps.dot11_he_cap.su_beamformer = 0; + mlme_obj->cfg.he_caps.dot11_he_cap.num_sounding_lt_80 = 0; + mlme_obj->cfg.he_caps.dot11_he_cap.num_sounding_gt_80 = 0; + mlme_obj->cfg.he_caps.dot11_he_cap.mu_beamformer = 0; + } + + if (mlme_obj->cfg.vht_caps.vht_cap_info.su_bformee) { + mlme_obj->cfg.he_caps.dot11_he_cap.su_beamformee = + he_cap->su_beamformee; + if (cfg_in_range(CFG_HE_BFEE_STS_LT80, he_cap->bfee_sts_lt_80)) + mlme_obj->cfg.he_caps.dot11_he_cap.bfee_sts_lt_80 = + he_cap->bfee_sts_lt_80; + if (cfg_in_range(CFG_HE_BFEE_STS_GT80, he_cap->bfee_sts_gt_80)) + mlme_obj->cfg.he_caps.dot11_he_cap.bfee_sts_gt_80 = + he_cap->bfee_sts_gt_80; + + } else { + mlme_obj->cfg.he_caps.dot11_he_cap.su_beamformee = 0; + mlme_obj->cfg.he_caps.dot11_he_cap.bfee_sts_lt_80 = 0; + mlme_obj->cfg.he_caps.dot11_he_cap.bfee_sts_gt_80 = 0; + } + mlme_obj->cfg.he_caps.dot11_he_cap.ul_mu = he_cap->ul_mu; + mlme_obj->cfg.he_caps.dot11_he_cap.su_feedback_tone16 = + he_cap->su_feedback_tone16; + mlme_obj->cfg.he_caps.dot11_he_cap.mu_feedback_tone16 = + he_cap->mu_feedback_tone16; + mlme_obj->cfg.he_caps.dot11_he_cap.codebook_su = he_cap->codebook_su; + mlme_obj->cfg.he_caps.dot11_he_cap.codebook_mu = he_cap->codebook_mu; + if (cfg_in_range(CFG_HE_BFRM_FEED, he_cap->beamforming_feedback)) + mlme_obj->cfg.he_caps.dot11_he_cap.beamforming_feedback = + he_cap->beamforming_feedback; + mlme_obj->cfg.he_caps.dot11_he_cap.he_er_su_ppdu = + he_cap->he_er_su_ppdu; + mlme_obj->cfg.he_caps.dot11_he_cap.dl_mu_mimo_part_bw = + he_cap->dl_mu_mimo_part_bw; + mlme_obj->cfg.he_caps.dot11_he_cap.ppet_present = he_cap->ppet_present; + mlme_obj->cfg.he_caps.dot11_he_cap.srp = he_cap->srp; + mlme_obj->cfg.he_caps.dot11_he_cap.power_boost = he_cap->power_boost; + mlme_obj->cfg.he_caps.dot11_he_cap.he_ltf_800_gi_4x = + he_cap->he_ltf_800_gi_4x; + if (cfg_in_range(CFG_HE_MAX_NC, he_cap->max_nc)) + mlme_obj->cfg.he_caps.dot11_he_cap.max_nc = he_cap->max_nc; + mlme_obj->cfg.he_caps.dot11_he_cap.er_he_ltf_800_gi_4x = + he_cap->er_he_ltf_800_gi_4x; + mlme_obj->cfg.he_caps.dot11_he_cap.he_ppdu_20_in_40Mhz_2G = + he_cap->he_ppdu_20_in_40Mhz_2G; + mlme_obj->cfg.he_caps.dot11_he_cap.he_ppdu_20_in_160_80p80Mhz = + he_cap->he_ppdu_20_in_160_80p80Mhz; + mlme_obj->cfg.he_caps.dot11_he_cap.he_ppdu_80_in_160_80p80Mhz = + he_cap->he_ppdu_80_in_160_80p80Mhz; + mlme_obj->cfg.he_caps.dot11_he_cap.er_1x_he_ltf_gi = + he_cap->er_1x_he_ltf_gi; + mlme_obj->cfg.he_caps.dot11_he_cap.midamble_tx_rx_1x_he_ltf = + he_cap->midamble_tx_rx_1x_he_ltf; + if (cfg_in_range(CFG_HE_DCM_MAX_BW, he_cap->dcm_max_bw)) + mlme_obj->cfg.he_caps.dot11_he_cap.dcm_max_bw = + he_cap->dcm_max_bw; + mlme_obj->cfg.he_caps.dot11_he_cap.longer_than_16_he_sigb_ofdm_sym = + he_cap->longer_than_16_he_sigb_ofdm_sym; + mlme_obj->cfg.he_caps.dot11_he_cap.tx_1024_qam_lt_242_tone_ru = + he_cap->tx_1024_qam_lt_242_tone_ru; + mlme_obj->cfg.he_caps.dot11_he_cap.rx_1024_qam_lt_242_tone_ru = + he_cap->rx_1024_qam_lt_242_tone_ru; + mlme_obj->cfg.he_caps.dot11_he_cap.non_trig_cqi_feedback = + he_cap->non_trig_cqi_feedback; + mlme_obj->cfg.he_caps.dot11_he_cap.rx_full_bw_su_he_mu_compress_sigb = + he_cap->rx_full_bw_su_he_mu_compress_sigb; + mlme_obj->cfg.he_caps.dot11_he_cap.rx_full_bw_su_he_mu_non_cmpr_sigb = + he_cap->rx_full_bw_su_he_mu_non_cmpr_sigb; + + tx_mcs_map = mlme_get_min_rate_cap( + mlme_obj->cfg.he_caps.dot11_he_cap.tx_he_mcs_map_lt_80, + he_cap->tx_he_mcs_map_lt_80); + rx_mcs_map = mlme_get_min_rate_cap( + mlme_obj->cfg.he_caps.dot11_he_cap.rx_he_mcs_map_lt_80, + he_cap->rx_he_mcs_map_lt_80); + if (!mlme_obj->cfg.vht_caps.vht_cap_info.enable2x2) { + nss = 2; + tx_mcs_map = HE_SET_MCS_4_NSS(tx_mcs_map, HE_MCS_DISABLE, nss); + rx_mcs_map = HE_SET_MCS_4_NSS(rx_mcs_map, HE_MCS_DISABLE, nss); + } + + if (cfg_in_range(CFG_HE_RX_MCS_MAP_LT_80, rx_mcs_map)) + mlme_obj->cfg.he_caps.dot11_he_cap.rx_he_mcs_map_lt_80 = + rx_mcs_map; + if (cfg_in_range(CFG_HE_TX_MCS_MAP_LT_80, tx_mcs_map)) + mlme_obj->cfg.he_caps.dot11_he_cap.tx_he_mcs_map_lt_80 = + tx_mcs_map; + tx_mcs_map = mlme_get_min_rate_cap( + *((uint16_t *)mlme_obj->cfg.he_caps.dot11_he_cap.tx_he_mcs_map_160), + *((uint16_t *)he_cap->tx_he_mcs_map_160)); + rx_mcs_map = mlme_get_min_rate_cap( + *((uint16_t *)mlme_obj->cfg.he_caps.dot11_he_cap.rx_he_mcs_map_160), + *((uint16_t *)he_cap->rx_he_mcs_map_160)); + + if (!mlme_obj->cfg.vht_caps.vht_cap_info.enable2x2) { + nss = 2; + tx_mcs_map = HE_SET_MCS_4_NSS(tx_mcs_map, HE_MCS_DISABLE, nss); + rx_mcs_map = HE_SET_MCS_4_NSS(rx_mcs_map, HE_MCS_DISABLE, nss); + } + + if (cfg_in_range(CFG_HE_RX_MCS_MAP_160, rx_mcs_map)) + qdf_mem_copy(mlme_obj->cfg.he_caps.dot11_he_cap. + rx_he_mcs_map_160, + &rx_mcs_map, sizeof(uint16_t)); + + if (cfg_in_range(CFG_HE_TX_MCS_MAP_160, tx_mcs_map)) + qdf_mem_copy(mlme_obj->cfg.he_caps.dot11_he_cap. + tx_he_mcs_map_160, + &tx_mcs_map, sizeof(uint16_t)); + + if (cfg_in_range(CFG_HE_RX_MCS_MAP_80_80, + *((uint16_t *)he_cap->rx_he_mcs_map_80_80))) + qdf_mem_copy(mlme_obj->cfg.he_caps.dot11_he_cap. + rx_he_mcs_map_80_80, + he_cap->rx_he_mcs_map_80_80, sizeof(uint16_t)); + + if (cfg_in_range(CFG_HE_TX_MCS_MAP_80_80, + *((uint16_t *)he_cap->tx_he_mcs_map_80_80))) + qdf_mem_copy(mlme_obj->cfg.he_caps.dot11_he_cap. + tx_he_mcs_map_80_80, + he_cap->tx_he_mcs_map_80_80, sizeof(uint16_t)); + + qdf_mem_copy(mlme_obj->cfg.he_caps.he_ppet_2g, wma_cfg->ppet_2g, + HE_MAX_PPET_SIZE); + + qdf_mem_copy(mlme_obj->cfg.he_caps.he_ppet_5g, wma_cfg->ppet_5g, + HE_MAX_PPET_SIZE); + + mlme_obj->cfg.he_caps.he_cap_orig = mlme_obj->cfg.he_caps.dot11_he_cap; + return status; +} +#endif + +QDF_STATUS wlan_mlme_get_num_11b_tx_chains(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.chainmask_cfg.num_11b_tx_chains; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_bt_chain_separation_flag(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.chainmask_cfg.enable_bt_chain_separation; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_num_11ag_tx_chains(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.chainmask_cfg.num_11ag_tx_chains; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_configure_chain_mask(struct wlan_objmgr_psoc *psoc, + uint8_t session_id) +{ + int ret_val; + uint8_t ch_msk_val; + struct wma_caps_per_phy non_dbs_phy_cap = {0}; + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + QDF_STATUS status; + bool enable2x2, as_enabled, enable_bt_chain_sep; + uint8_t dual_mac_feature; + bool hw_dbs_2x2_cap, mrc_disabled_2g_rx, mrc_disabled_2g_tx; + bool mrc_disabled_5g_rx, mrc_disabled_5g_tx; + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + status = wma_get_caps_for_phyidx_hwmode(&non_dbs_phy_cap, + HW_MODE_DBS_NONE, + CDS_BAND_ALL); + if (QDF_IS_STATUS_ERROR(status)) { + mlme_legacy_err("couldn't get phy caps. skip chain mask programming"); + return status; + } + + if (non_dbs_phy_cap.tx_chain_mask_2G < 3 || + non_dbs_phy_cap.rx_chain_mask_2G < 3 || + non_dbs_phy_cap.tx_chain_mask_5G < 3 || + non_dbs_phy_cap.rx_chain_mask_5G < 3) { + mlme_legacy_debug("firmware not capable. skip chain mask programming"); + return 0; + } + + enable2x2 = mlme_obj->cfg.vht_caps.vht_cap_info.enable2x2; + enable_bt_chain_sep = + mlme_obj->cfg.chainmask_cfg.enable_bt_chain_separation; + as_enabled = mlme_obj->cfg.gen.as_enabled; + ucfg_policy_mgr_get_dual_mac_feature(psoc, &dual_mac_feature); + + hw_dbs_2x2_cap = policy_mgr_is_hw_dbs_2x2_capable(psoc); + mrc_disabled_2g_rx = + mlme_obj->cfg.nss_chains_ini_cfg.disable_rx_mrc[NSS_CHAINS_BAND_2GHZ]; + mrc_disabled_2g_tx = + mlme_obj->cfg.nss_chains_ini_cfg.disable_tx_mrc[NSS_CHAINS_BAND_2GHZ]; + mrc_disabled_5g_rx = + mlme_obj->cfg.nss_chains_ini_cfg.disable_rx_mrc[NSS_CHAINS_BAND_5GHZ]; + mrc_disabled_5g_tx = + mlme_obj->cfg.nss_chains_ini_cfg.disable_tx_mrc[NSS_CHAINS_BAND_5GHZ]; + + mlme_legacy_debug("enable2x2 %d enable_bt_chain_sep %d dual mac feature %d antenna sharing %d HW 2x2 cap %d", + enable2x2, enable_bt_chain_sep, dual_mac_feature, + as_enabled, hw_dbs_2x2_cap); + + mlme_legacy_debug("MRC values TX:- 2g %d 5g %d RX:- 2g %d 5g %d", + mrc_disabled_2g_tx, mrc_disabled_5g_tx, + mrc_disabled_2g_rx, mrc_disabled_5g_rx); + + mlme_legacy_debug("txchainmask1x1: %d rxchainmask1x1: %d", + mlme_obj->cfg.chainmask_cfg.txchainmask1x1, + mlme_obj->cfg.chainmask_cfg.rxchainmask1x1); + mlme_legacy_debug("tx_chain_mask_2g: %d, rx_chain_mask_2g: %d", + mlme_obj->cfg.chainmask_cfg.tx_chain_mask_2g, + mlme_obj->cfg.chainmask_cfg.rx_chain_mask_2g); + mlme_legacy_debug("tx_chain_mask_5g: %d, rx_chain_mask_5g: %d", + mlme_obj->cfg.chainmask_cfg.tx_chain_mask_5g, + mlme_obj->cfg.chainmask_cfg.rx_chain_mask_5g); + + if ((enable2x2 && !enable_bt_chain_sep) || as_enabled || + (!hw_dbs_2x2_cap && dual_mac_feature != DISABLE_DBS_CXN_AND_SCAN)) { + mlme_legacy_debug("Cannot configure chainmask to FW"); + return QDF_STATUS_E_FAILURE; + } + + if (mlme_obj->cfg.chainmask_cfg.txchainmask1x1) { + ch_msk_val = mlme_obj->cfg.chainmask_cfg.txchainmask1x1; + ret_val = wma_cli_set_command(session_id, + WMI_PDEV_PARAM_TX_CHAIN_MASK, + ch_msk_val, PDEV_CMD); + if (ret_val) + return QDF_STATUS_E_FAILURE; + } + + if (mlme_obj->cfg.chainmask_cfg.rxchainmask1x1) { + ch_msk_val = mlme_obj->cfg.chainmask_cfg.rxchainmask1x1; + ret_val = wma_cli_set_command(session_id, + WMI_PDEV_PARAM_RX_CHAIN_MASK, + ch_msk_val, PDEV_CMD); + if (ret_val) + return QDF_STATUS_E_FAILURE; + } + + if (mlme_obj->cfg.chainmask_cfg.txchainmask1x1 || + mlme_obj->cfg.chainmask_cfg.rxchainmask1x1) { + mlme_legacy_debug("band agnostic tx/rx chain mask set. skip per band chain mask"); + return QDF_STATUS_SUCCESS; + } + + if (mlme_obj->cfg.chainmask_cfg.tx_chain_mask_2g && + mrc_disabled_2g_tx) { + ch_msk_val = mlme_obj->cfg.chainmask_cfg.tx_chain_mask_2g; + ret_val = wma_cli_set_command(session_id, + WMI_PDEV_PARAM_TX_CHAIN_MASK_2G, + ch_msk_val, PDEV_CMD); + if (0 != ret_val) + return QDF_STATUS_E_FAILURE; + } + + if (mlme_obj->cfg.chainmask_cfg.rx_chain_mask_2g && + mrc_disabled_2g_rx) { + ch_msk_val = mlme_obj->cfg.chainmask_cfg.rx_chain_mask_2g; + ret_val = wma_cli_set_command(session_id, + WMI_PDEV_PARAM_RX_CHAIN_MASK_2G, + ch_msk_val, PDEV_CMD); + if (0 != ret_val) + return QDF_STATUS_E_FAILURE; + } + + if (mlme_obj->cfg.chainmask_cfg.tx_chain_mask_5g && + mrc_disabled_5g_tx) { + ch_msk_val = mlme_obj->cfg.chainmask_cfg.tx_chain_mask_5g; + ret_val = wma_cli_set_command(session_id, + WMI_PDEV_PARAM_TX_CHAIN_MASK_5G, + ch_msk_val, PDEV_CMD); + if (0 != ret_val) + return QDF_STATUS_E_FAILURE; + } + + if (mlme_obj->cfg.chainmask_cfg.rx_chain_mask_5g && + mrc_disabled_5g_rx) { + ch_msk_val = mlme_obj->cfg.chainmask_cfg.rx_chain_mask_5g; + ret_val = wma_cli_set_command(session_id, + WMI_PDEV_PARAM_RX_CHAIN_MASK_5G, + ch_msk_val, PDEV_CMD); + if (0 != ret_val) + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_manufacturer_name(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *plen = qdf_str_lcopy(pbuf, + mlme_obj->cfg.product_details.manufacturer_name, + *plen); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_model_number(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *plen = qdf_str_lcopy(pbuf, + mlme_obj->cfg.product_details.model_number, + *plen); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_model_name(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *plen = qdf_str_lcopy(pbuf, + mlme_obj->cfg.product_details.model_name, + *plen); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_manufacture_product_version(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *plen = qdf_str_lcopy(pbuf, + mlme_obj->cfg.product_details.manufacture_product_version, + *plen); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_manufacture_product_name(struct wlan_objmgr_psoc *psoc, + uint8_t *pbuf, uint32_t *plen) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *plen = qdf_str_lcopy(pbuf, + mlme_obj->cfg.product_details.manufacture_product_name, + *plen); + return QDF_STATUS_SUCCESS; +} + + +void wlan_mlme_get_tl_delayed_trgr_frm_int(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_TL_DELAYED_TRGR_FRM_INTERVAL); + return; + } + + *value = mlme_obj->cfg.wmm_params.delayed_trigger_frm_int; +} + + +QDF_STATUS wlan_mlme_get_wmm_dir_ac_vo(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vo.dir_ac_vo; + + return QDF_STATUS_SUCCESS; +} + + +QDF_STATUS wlan_mlme_get_wmm_nom_msdu_size_ac_vo(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vo.nom_msdu_size_ac_vo; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_mean_data_rate_ac_vo(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vo.mean_data_rate_ac_vo; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_min_phy_rate_ac_vo(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vo.min_phy_rate_ac_vo; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_sba_ac_vo(struct wlan_objmgr_psoc *psoc, uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.wmm_params.ac_vo.sba_ac_vo; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_uapsd_vo_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vo.uapsd_vo_srv_intv; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_uapsd_vo_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vo.uapsd_vo_sus_intv; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_dir_ac_vi(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vi.dir_ac_vi; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_nom_msdu_size_ac_vi(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = + mlme_obj->cfg.wmm_params.ac_vi.nom_msdu_size_ac_vi; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_mean_data_rate_ac_vi(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vi.mean_data_rate_ac_vi; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_min_phy_rate_ac_vi(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vi.min_phy_rate_ac_vi; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_sba_ac_vi(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vi.sba_ac_vi; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_uapsd_vi_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vi.uapsd_vi_srv_intv; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_uapsd_vi_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_vi.uapsd_vi_sus_intv; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_dir_ac_be(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_be.dir_ac_be; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_nom_msdu_size_ac_be(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_be.nom_msdu_size_ac_be; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_mean_data_rate_ac_be(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_be.mean_data_rate_ac_be; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_min_phy_rate_ac_be(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_be.min_phy_rate_ac_be; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_sba_ac_be(struct wlan_objmgr_psoc *psoc, uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_be.sba_ac_be; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_uapsd_be_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_be.uapsd_be_srv_intv; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_uapsd_be_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_be.uapsd_be_sus_intv; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_dir_ac_bk(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_bk.dir_ac_bk; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_nom_msdu_size_ac_bk(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_bk.nom_msdu_size_ac_bk; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_mean_data_rate_ac_bk(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_bk.mean_data_rate_ac_bk; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_wmm_min_phy_rate_ac_bk(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_bk.min_phy_rate_ac_bk; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_sba_ac_bk(struct wlan_objmgr_psoc *psoc, uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_bk.sba_ac_bk; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_uapsd_bk_srv_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_bk.uapsd_bk_srv_intv; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_uapsd_bk_sus_intv(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.ac_bk.uapsd_bk_sus_intv; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_mode(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.wmm_config.wmm_mode; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_80211e_is_enabled(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.wmm_config.b80211e_is_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_wmm_uapsd_mask(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.wmm_config.uapsd_mask; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_implicit_qos_is_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.wmm_config.bimplicit_qos_enabled; + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_ESE +void wlan_mlme_get_inactivity_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_QOS_WMM_INACTIVITY_INTERVAL); + return; + } + + *value = mlme_obj->cfg.wmm_params.wmm_tspec_element.inactivity_intv; +} +#endif + +void wlan_mlme_get_is_ts_burst_size_enable(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_QOS_WMM_BURST_SIZE_DEFN); + return; + } + + *value = mlme_obj->cfg.wmm_params.wmm_tspec_element.burst_size_def; +} + +void wlan_mlme_get_ts_info_ack_policy(struct wlan_objmgr_psoc *psoc, + enum mlme_ts_info_ack_policy *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_QOS_WMM_TS_INFO_ACK_POLICY); + return; + } + + *value = mlme_obj->cfg.wmm_params.wmm_tspec_element.ts_ack_policy; + +} + +QDF_STATUS +wlan_mlme_get_ts_acm_value_for_ac(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.wmm_params.wmm_tspec_element.ts_acm_is_off; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_listen_interval(struct wlan_objmgr_psoc *psoc, + int *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.listen_interval; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_sap_listen_interval(struct wlan_objmgr_psoc *psoc, + int value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + if (cfg_in_range(CFG_LISTEN_INTERVAL, value)) + mlme_obj->cfg.sap_cfg.listen_interval = value; + else + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_assoc_sta_limit(struct wlan_objmgr_psoc *psoc, + int value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + if (cfg_in_range(CFG_ASSOC_STA_LIMIT, value)) + mlme_obj->cfg.sap_cfg.assoc_sta_limit = value; + else + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_assoc_sta_limit(struct wlan_objmgr_psoc *psoc, + int *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.assoc_sta_limit; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_rmc_action_period_freq(struct wlan_objmgr_psoc *psoc, + int value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + if (cfg_in_range(CFG_RMC_ACTION_PERIOD_FREQUENCY, value)) + mlme_obj->cfg.sap_cfg.rmc_action_period_freq = value; + else + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_get_peer_info(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_get_peer_info; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_sap_get_peer_info(struct wlan_objmgr_psoc *psoc, + bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.sap_cfg.sap_get_peer_info = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_sap_bcast_deauth_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.is_sap_bcast_deauth_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_is_6g_sap_fd_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.is_6g_sap_fd_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_allow_all_channels(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_allow_all_chan_param_name; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_max_peers(struct wlan_objmgr_psoc *psoc, + int *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_max_no_peers; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_sap_max_peers(struct wlan_objmgr_psoc *psoc, + int value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + if (cfg_in_range(CFG_RMC_ACTION_PERIOD_FREQUENCY, value)) + mlme_obj->cfg.sap_cfg.sap_max_no_peers = value; + else + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_max_offload_peers(struct wlan_objmgr_psoc *psoc, + int *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_max_offload_peers; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_max_offload_reorder_buffs(struct wlan_objmgr_psoc + *psoc, int *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_max_offload_reorder_buffs; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_chn_switch_bcn_count(struct wlan_objmgr_psoc *psoc, + int *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_ch_switch_beacon_cnt; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_chn_switch_mode(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_ch_switch_mode; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_internal_restart(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_internal_restart; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_max_modulated_dtim(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.max_li_modulated_dtim_time; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_chan_pref_location(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_pref_chan_location; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_country_priority(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.country_code_priority; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_reduced_beacon_interval(struct wlan_objmgr_psoc + *psoc, int *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_REDUCED_BEACON_INTERVAL); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.sap_cfg.reduced_beacon_interval; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_chan_switch_rate_enabled(struct wlan_objmgr_psoc + *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.chan_switch_hostapd_rate_enabled_name; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_sap_force_11n_for_11ac(struct wlan_objmgr_psoc + *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_force_11n_for_11ac; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_go_force_11n_for_11ac(struct wlan_objmgr_psoc + *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.go_force_11n_for_11ac; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_is_sap_11ac_override(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_SAP_11AC_OVERRIDE); + return QDF_STATUS_E_FAILURE; + } + *value = mlme_obj->cfg.sap_cfg.sap_11ac_override; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_is_go_11ac_override(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_GO_11AC_OVERRIDE); + return QDF_STATUS_E_FAILURE; + } + *value = mlme_obj->cfg.sap_cfg.go_11ac_override; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_sap_11ac_override(struct wlan_objmgr_psoc *psoc, + bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + mlme_obj->cfg.sap_cfg.sap_11ac_override = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_go_11ac_override(struct wlan_objmgr_psoc *psoc, + bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + mlme_obj->cfg.sap_cfg.go_11ac_override = value; + + return QDF_STATUS_SUCCESS; +} + +bool wlan_mlme_get_host_scan_abort_support(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return false; + + return mlme_obj->cfg.gen.stop_all_host_scan_support; +} + +QDF_STATUS wlan_mlme_get_oce_sta_enabled_info(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.oce.oce_sta_enabled; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_oce_sap_enabled_info(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.oce.oce_sap_enabled; + return QDF_STATUS_SUCCESS; +} + +/** + * wlan_mlme_send_oce_flags_fw() - Send the oce flags to FW + * @pdev: pointer to pdev object + * @object: vdev object + * @arg: Arguments to the handler + * + * Return: void + */ +static void wlan_mlme_send_oce_flags_fw(struct wlan_objmgr_pdev *pdev, + void *object, void *arg) +{ + struct wlan_objmgr_vdev *vdev = object; + uint8_t *updated_fw_value = arg; + uint8_t *dynamic_fw_value = 0; + uint8_t vdev_id; + + if (wlan_vdev_mlme_get_opmode(vdev) == QDF_STA_MODE) { + dynamic_fw_value = mlme_get_dynamic_oce_flags(vdev); + if (!dynamic_fw_value) + return; + if (*updated_fw_value == *dynamic_fw_value) { + mlme_legacy_debug("Current FW flags matches with updated value."); + return; + } + *dynamic_fw_value = *updated_fw_value; + vdev_id = wlan_vdev_get_id(vdev); + if (wma_cli_set_command(vdev_id, + WMI_VDEV_PARAM_ENABLE_DISABLE_OCE_FEATURES, + *updated_fw_value, VDEV_CMD)) + mlme_legacy_err("Failed to send OCE update to FW"); + } +} + +void wlan_mlme_update_oce_flags(struct wlan_objmgr_pdev *pdev) +{ + uint16_t sap_connected_peer, go_connected_peer; + struct wlan_objmgr_psoc *psoc = NULL; + struct wlan_mlme_psoc_ext_obj *mlme_obj; + uint8_t updated_fw_value = 0; + + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) + return; + + sap_connected_peer = + wlan_util_get_peer_count_for_mode(pdev, QDF_SAP_MODE); + go_connected_peer = + wlan_util_get_peer_count_for_mode(pdev, QDF_P2P_GO_MODE); + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return; + + if (sap_connected_peer || go_connected_peer) { + updated_fw_value = mlme_obj->cfg.oce.feature_bitmap; + updated_fw_value &= + ~(WMI_VDEV_OCE_PROBE_REQUEST_RATE_FEATURE_BITMAP); + updated_fw_value &= + ~(WMI_VDEV_OCE_PROBE_REQUEST_DEFERRAL_FEATURE_BITMAP); + mlme_legacy_debug("Disable STA OCE probe req rate and defferal updated_fw_value :%d", + updated_fw_value); + } else { + updated_fw_value = mlme_obj->cfg.oce.feature_bitmap; + mlme_legacy_debug("Update the STA OCE flags to default INI updated_fw_value :%d", + updated_fw_value); + } + + wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP, + wlan_mlme_send_oce_flags_fw, + &updated_fw_value, 0, WLAN_MLME_NB_ID); +} + +bool wlan_mlme_is_ap_prot_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return false; + + return mlme_obj->cfg.sap_protection_cfg.is_ap_prot_enabled; +} + +QDF_STATUS wlan_mlme_get_ap_protection_mode(struct wlan_objmgr_psoc *psoc, + uint16_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_protection_cfg.ap_protection_mode; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_is_ap_obss_prot_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_protection_cfg.enable_ap_obss_protection; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_rts_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.threshold.rts_threshold; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_rts_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + tp_wma_handle wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s: wma_handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.threshold.rts_threshold = value; + wma_update_rts_params(wma_handle, value); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_frag_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.threshold.frag_threshold; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_frag_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + tp_wma_handle wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s: wma_handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.threshold.frag_threshold = value; + wma_update_frag_params(wma_handle, + value); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_fils_enabled_info(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.oce.fils_enabled; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_fils_enabled_info(struct wlan_objmgr_psoc *psoc, + bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.oce.fils_enabled = value; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_enable_bcast_probe_rsp(struct wlan_objmgr_psoc *psoc, + bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.oce.enable_bcast_probe_rsp = value; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_sta_miracast_mcc_rest_time(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sta.sta_miracast_mcc_rest_time; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_sap_mcc_chnl_avoid(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.sap_cfg.sap_mcc_chnl_avoid; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_mcc_bcast_prob_resp(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.feature_flags.mcc_bcast_prob_rsp; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_mcc_rts_cts_prot(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.feature_flags.mcc_rts_cts_prot; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_mcc_feature(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.feature_flags.enable_mcc; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_edca_params(struct wlan_mlme_edca_params *edca_params, + uint8_t *data, enum e_edca_type edca_ac) +{ + qdf_size_t len; + + switch (edca_ac) { + case edca_ani_acbe_local: + len = edca_params->ani_acbe_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->ani_acbe_l, &len); + break; + + case edca_ani_acbk_local: + len = edca_params->ani_acbk_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->ani_acbk_l, &len); + break; + + case edca_ani_acvi_local: + len = edca_params->ani_acvi_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->ani_acvi_l, &len); + break; + + case edca_ani_acvo_local: + len = edca_params->ani_acvo_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->ani_acvo_l, &len); + break; + + case edca_ani_acbk_bcast: + len = edca_params->ani_acbk_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->ani_acbk_b, &len); + break; + + case edca_ani_acbe_bcast: + len = edca_params->ani_acbe_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->ani_acbe_b, &len); + break; + + case edca_ani_acvi_bcast: + len = edca_params->ani_acvi_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->ani_acvi_b, &len); + break; + + case edca_ani_acvo_bcast: + len = edca_params->ani_acvo_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->ani_acvo_b, &len); + break; + + case edca_wme_acbe_local: + len = edca_params->wme_acbe_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->wme_acbe_l, &len); + break; + + case edca_wme_acbk_local: + len = edca_params->wme_acbk_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->wme_acbk_l, &len); + break; + + case edca_wme_acvi_local: + len = edca_params->wme_acvi_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->wme_acvi_l, &len); + break; + + case edca_wme_acvo_local: + len = edca_params->wme_acvo_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->wme_acvo_l, &len); + break; + + case edca_wme_acbe_bcast: + len = edca_params->wme_acbe_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->wme_acbe_b, &len); + break; + + case edca_wme_acbk_bcast: + len = edca_params->wme_acbk_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->wme_acbk_b, &len); + break; + + case edca_wme_acvi_bcast: + len = edca_params->wme_acvi_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->wme_acvi_b, &len); + break; + + case edca_wme_acvo_bcast: + len = edca_params->wme_acvo_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->wme_acvo_b, &len); + break; + + case edca_etsi_acbe_local: + len = edca_params->etsi_acbe_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->etsi_acbe_l, &len); + break; + + case edca_etsi_acbk_local: + len = edca_params->etsi_acbk_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->etsi_acbk_l, &len); + break; + + case edca_etsi_acvi_local: + len = edca_params->etsi_acvi_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->etsi_acvi_l, &len); + break; + + case edca_etsi_acvo_local: + len = edca_params->etsi_acvo_l.len; + wlan_mlme_get_cfg_str(data, &edca_params->etsi_acvo_l, &len); + break; + + case edca_etsi_acbe_bcast: + len = edca_params->etsi_acbe_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->etsi_acbe_b, &len); + break; + + case edca_etsi_acbk_bcast: + len = edca_params->etsi_acbk_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->etsi_acbk_b, &len); + break; + + case edca_etsi_acvi_bcast: + len = edca_params->etsi_acvi_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->etsi_acvi_b, &len); + break; + + case edca_etsi_acvo_bcast: + len = edca_params->etsi_acvo_b.len; + wlan_mlme_get_cfg_str(data, &edca_params->etsi_acvo_b, &len); + break; + default: + mlme_legacy_err("Invalid edca access category"); + return QDF_STATUS_E_INVAL; + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_get_wep_key(struct wlan_objmgr_vdev *vdev, + struct wlan_mlme_wep_cfg *wep_params, + enum wep_key_id wep_keyid, uint8_t *default_key, + qdf_size_t *key_len) +{ + struct wlan_crypto_key *crypto_key = NULL; + + if (wep_keyid >= WLAN_CRYPTO_MAXKEYIDX) { + mlme_legacy_err("Incorrect wep key index %d", wep_keyid); + return QDF_STATUS_E_INVAL; + } + crypto_key = wlan_crypto_get_key(vdev, wep_keyid); + if (!crypto_key) { + mlme_legacy_err("Crypto KEY not present"); + return QDF_STATUS_E_INVAL; + } + + if (crypto_key->keylen > WLAN_CRYPTO_KEY_WEP104_LEN) { + mlme_legacy_err("Key too large to hold"); + return QDF_STATUS_E_INVAL; + } + *key_len = crypto_key->keylen; + qdf_mem_copy(default_key, &crypto_key->keyval, crypto_key->keylen); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_set_wep_key(struct wlan_mlme_wep_cfg *wep_params, + enum wep_key_id wep_keyid, uint8_t *key_to_set, + qdf_size_t len) +{ + if (len == 0) + return QDF_STATUS_E_FAILURE; + + mlme_legacy_debug("WEP set key for key_id:%d key_len:%zd", + wep_keyid, len); + switch (wep_keyid) { + case MLME_WEP_DEFAULT_KEY_1: + wlan_mlme_set_cfg_str(key_to_set, + &wep_params->wep_default_key_1, + len); + break; + + case MLME_WEP_DEFAULT_KEY_2: + wlan_mlme_set_cfg_str(key_to_set, + &wep_params->wep_default_key_2, + len); + break; + + case MLME_WEP_DEFAULT_KEY_3: + wlan_mlme_set_cfg_str(key_to_set, + &wep_params->wep_default_key_3, + len); + break; + + case MLME_WEP_DEFAULT_KEY_4: + wlan_mlme_set_cfg_str(key_to_set, + &wep_params->wep_default_key_4, + len); + break; + + default: + mlme_legacy_err("Invalid key id:%d", wep_keyid); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_is_11h_enabled(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.gen.enabled_11h; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_set_11h_enabled(struct wlan_objmgr_psoc *psoc, bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.gen.enabled_11h = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_is_11d_enabled(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.gen.enabled_11d; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_set_11d_enabled(struct wlan_objmgr_psoc *psoc, bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.gen.enabled_11d = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_set_vht_chan_width(struct wlan_objmgr_psoc *psoc, uint8_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.supp_chan_width = value; + if (value == VHT_CAP_160_AND_80P80_SUPP || + value == VHT_CAP_160_SUPP) { + mlme_obj->cfg.vht_caps.vht_cap_info.vht_extended_nss_bw_cap = 1; + mlme_obj->cfg.vht_caps.vht_cap_info.extended_nss_bw_supp = 0; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_get_vht_chan_width(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.supp_chan_width; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_cfg_set_vht_ldpc_coding_cap(struct wlan_objmgr_psoc *psoc, + bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.ldpc_coding_cap = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_set_short_gi_160_mhz(struct wlan_objmgr_psoc *psoc, + bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.short_gi_160mhz = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_get_short_gi_160_mhz(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.short_gi_160mhz; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_get_vht_tx_stbc(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.tx_stbc; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_get_vht_rx_stbc(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.rx_stbc; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_set_vht_tx_bfee_ant_supp(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.tx_bfee_ant_supp = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_get_vht_tx_bfee_ant_supp(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.tx_bfee_ant_supp; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_cfg_get_vht_rx_mcs_map(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.rx_mcs_map; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_cfg_set_vht_rx_mcs_map(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.rx_mcs_map = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_cfg_get_vht_tx_mcs_map(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.tx_mcs_map; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_cfg_set_vht_tx_mcs_map(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.tx_mcs_map = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_set_vht_rx_supp_data_rate(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.rx_supp_data_rate = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_set_vht_tx_supp_data_rate(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.tx_supp_data_rate = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_get_vht_basic_mcs_set(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.basic_mcs_set; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_cfg_set_vht_basic_mcs_set(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.basic_mcs_set = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_enable_tx_bf(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.su_bformee; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_tx_su_beamformer(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.su_bformer; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_channel_width(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.channel_width; + + return QDF_STATUS_SUCCESS; +} + + +QDF_STATUS +wlan_mlme_get_vht_rx_mcs_8_9(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.rx_mcs; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_tx_mcs_8_9(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.tx_mcs; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_rx_mcs_2x2(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.rx_mcs2x2; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_tx_mcs_2x2(struct wlan_objmgr_psoc *psoc, uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.tx_mcs2x2; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht20_mcs9(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.enable_vht20_mcs9; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_indoor_support_for_nan(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = false; + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_INVAL; + } + + *value = mlme_obj->cfg.reg.enable_nan_on_indoor_channels; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_srd_master_mode_for_vdev(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE vdev_opmode, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = false; + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_INVAL; + } + + switch (vdev_opmode) { + case QDF_SAP_MODE: + *value = mlme_obj->cfg.reg.etsi_srd_chan_in_master_mode & + MLME_SRD_MASTER_MODE_SAP; + break; + case QDF_P2P_GO_MODE: + *value = mlme_obj->cfg.reg.etsi_srd_chan_in_master_mode & + MLME_SRD_MASTER_MODE_P2P_GO; + break; + case QDF_NAN_DISC_MODE: + *value = mlme_obj->cfg.reg.etsi_srd_chan_in_master_mode & + MLME_SRD_MASTER_MODE_NAN; + break; + default: + mlme_legacy_err("Unexpected opmode %d", vdev_opmode); + *value = false; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_enable2x2(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.enable2x2; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_set_vht_enable2x2(struct wlan_objmgr_psoc *psoc, bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.enable2x2 = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_enable_paid(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.enable_paid; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_enable_gid(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.enable_gid; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vht_for_24ghz(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.b24ghz_band; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_set_vht_for_24ghz(struct wlan_objmgr_psoc *psoc, bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.vht_caps.vht_cap_info.b24ghz_band = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_vendor_vht_for_24ghz(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.vht_caps.vht_cap_info.vendor_24ghz_band; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +mlme_update_vht_cap(struct wlan_objmgr_psoc *psoc, struct wma_tgt_vht_cap *cfg) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + struct mlme_vht_capabilities_info *vht_cap_info; + uint32_t value = 0; + bool hw_rx_ldpc_enabled; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + vht_cap_info = &mlme_obj->cfg.vht_caps.vht_cap_info; + + /* + * VHT max MPDU length: + * override if user configured value is too high + * that the target cannot support + */ + if (vht_cap_info->ampdu_len > cfg->vht_max_mpdu) + vht_cap_info->ampdu_len = cfg->vht_max_mpdu; + + value = (CFG_VHT_BASIC_MCS_SET_STADEF & VHT_MCS_1x1) | + vht_cap_info->basic_mcs_set; + if (vht_cap_info->enable2x2) + value = (value & VHT_MCS_2x2) | (vht_cap_info->rx_mcs2x2 << 2); + vht_cap_info->basic_mcs_set = value; + + value = (CFG_VHT_RX_MCS_MAP_STADEF & VHT_MCS_1x1) | + vht_cap_info->rx_mcs; + + if (vht_cap_info->enable2x2) + value = (value & VHT_MCS_2x2) | (vht_cap_info->rx_mcs2x2 << 2); + vht_cap_info->rx_mcs_map = value; + + value = (CFG_VHT_TX_MCS_MAP_STADEF & VHT_MCS_1x1) | + vht_cap_info->tx_mcs; + if (vht_cap_info->enable2x2) + value = (value & VHT_MCS_2x2) | (vht_cap_info->tx_mcs2x2 << 2); + vht_cap_info->tx_mcs_map = value; + + /* Set HW RX LDPC capability */ + hw_rx_ldpc_enabled = !!cfg->vht_rx_ldpc; + if (hw_rx_ldpc_enabled != vht_cap_info->ldpc_coding_cap) + vht_cap_info->ldpc_coding_cap = hw_rx_ldpc_enabled; + + /* set the Guard interval 80MHz */ + if (vht_cap_info->short_gi_80mhz && !cfg->vht_short_gi_80) + vht_cap_info->short_gi_80mhz = cfg->vht_short_gi_80; + + /* Set VHT TX STBC cap */ + if (vht_cap_info->tx_stbc && !cfg->vht_tx_stbc) + vht_cap_info->tx_stbc = cfg->vht_tx_stbc; + + /* Set VHT RX STBC cap */ + if (vht_cap_info->rx_stbc && !cfg->vht_rx_stbc) + vht_cap_info->rx_stbc = cfg->vht_rx_stbc; + + /* Set VHT SU Beamformer cap */ + if (vht_cap_info->su_bformer && !cfg->vht_su_bformer) + vht_cap_info->su_bformer = cfg->vht_su_bformer; + + /* check and update SU BEAMFORMEE capabality */ + if (vht_cap_info->su_bformee && !cfg->vht_su_bformee) + vht_cap_info->su_bformee = cfg->vht_su_bformee; + + /* Set VHT MU Beamformer cap */ + if (vht_cap_info->mu_bformer && !cfg->vht_mu_bformer) + vht_cap_info->mu_bformer = cfg->vht_mu_bformer; + + /* Set VHT MU Beamformee cap */ + if (vht_cap_info->enable_mu_bformee && !cfg->vht_mu_bformee) + vht_cap_info->enable_mu_bformee = cfg->vht_mu_bformee; + + /* + * VHT max AMPDU len exp: + * override if user configured value is too high + * that the target cannot support. + * Even though Rome publish ampdu_len=7, it can + * only support 4 because of some h/w bug. + */ + if (vht_cap_info->ampdu_len_exponent > cfg->vht_max_ampdu_len_exp) + vht_cap_info->ampdu_len_exponent = cfg->vht_max_ampdu_len_exp; + + /* Set VHT TXOP PS CAP */ + if (vht_cap_info->txop_ps && !cfg->vht_txop_ps) + vht_cap_info->txop_ps = cfg->vht_txop_ps; + + /* set the Guard interval 160MHz */ + if (vht_cap_info->short_gi_160mhz && !cfg->vht_short_gi_160) + vht_cap_info->short_gi_160mhz = cfg->vht_short_gi_160; + + if (cfg_get(psoc, CFG_ENABLE_VHT_MCS_10_11)) + vht_cap_info->vht_mcs_10_11_supp = cfg->vht_mcs_10_11_supp; + + mlme_legacy_debug("vht_mcs_10_11_supp %d", + vht_cap_info->vht_mcs_10_11_supp); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_update_nss_vht_cap(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + struct mlme_vht_capabilities_info *vht_cap_info; + uint32_t temp = 0; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + vht_cap_info = &mlme_obj->cfg.vht_caps.vht_cap_info; + + temp = vht_cap_info->basic_mcs_set; + temp = (temp & 0xFFFC) | vht_cap_info->rx_mcs; + if (vht_cap_info->enable2x2) + temp = (temp & 0xFFF3) | (vht_cap_info->rx_mcs2x2 << 2); + else + temp |= 0x000C; + + vht_cap_info->basic_mcs_set = temp; + + temp = vht_cap_info->rx_mcs_map; + temp = (temp & 0xFFFC) | vht_cap_info->rx_mcs; + if (vht_cap_info->enable2x2) + temp = (temp & 0xFFF3) | (vht_cap_info->rx_mcs2x2 << 2); + else + temp |= 0x000C; + + vht_cap_info->rx_mcs_map = temp; + + temp = vht_cap_info->tx_mcs_map; + temp = (temp & 0xFFFC) | vht_cap_info->tx_mcs; + if (vht_cap_info->enable2x2) + temp = (temp & 0xFFF3) | (vht_cap_info->tx_mcs2x2 << 2); + else + temp |= 0x000C; + + vht_cap_info->tx_mcs_map = temp; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_is_sap_uapsd_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.qos_mlme_params.sap_uapsd_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_dtim_selection_diversity(struct wlan_objmgr_psoc *psoc, + uint32_t *dtim_selection_div) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *dtim_selection_div = cfg_default(CFG_DTIM_SELECTION_DIVERSITY); + return QDF_STATUS_E_FAILURE; + } + + *dtim_selection_div = mlme_obj->cfg.ps_params.dtim_selection_diversity; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_bmps_min_listen_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_BMPS_MINIMUM_LI); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.ps_params.bmps_min_listen_interval; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_bmps_max_listen_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_BMPS_MAXIMUM_LI); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.ps_params.bmps_max_listen_interval; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_set_sap_uapsd_flag(struct wlan_objmgr_psoc *psoc, + bool value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.qos_mlme_params.sap_uapsd_enabled &= value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_rrm_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *value = mlme_obj->cfg.rrm_config.rrm_enabled; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_get_auto_bmps_timer_value(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_AUTO_BMPS_ENABLE_TIMER); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.ps_params.auto_bmps_timer_val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_is_bmps_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_ENABLE_PS); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.ps_params.is_bmps_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_is_imps_enabled(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_ENABLE_IMPS); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.ps_params.is_imps_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_override_bmps_imps(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.ps_params.is_imps_enabled = 0; + mlme_obj->cfg.ps_params.is_bmps_enabled = 0; + + return QDF_STATUS_SUCCESS; +} + +void wlan_mlme_get_wps_uuid(struct wlan_mlme_wps_params *wps_params, + uint8_t *data) +{ + qdf_size_t len = wps_params->wps_uuid.len; + + wlan_mlme_get_cfg_str(data, &wps_params->wps_uuid, &len); +} + +QDF_STATUS +wlan_mlme_get_self_gen_frm_pwr(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_SELF_GEN_FRM_PWR); + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.reg.self_gen_frm_pwr; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_mlme_ibss_power_save_setup(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id) +{ + struct wlan_mlme_ibss_cfg *ibss_cfg; + int ret; + struct wlan_mlme_psoc_ext_obj *mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + ibss_cfg = &mlme_obj->cfg.ibss; + + ret = wma_cli_set_command(vdev_id, + WMA_VDEV_IBSS_SET_ATIM_WINDOW_SIZE, + ibss_cfg->atim_win_size, + VDEV_CMD); + if (ret) { + mlme_legacy_err("atim window set failed: %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_cli_set_command(vdev_id, + WMA_VDEV_IBSS_SET_POWER_SAVE_ALLOWED, + ibss_cfg->power_save_allow, + VDEV_CMD); + if (ret) { + mlme_legacy_err("power save allow failed %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_cli_set_command(vdev_id, + WMA_VDEV_IBSS_SET_POWER_COLLAPSE_ALLOWED, + ibss_cfg->power_collapse_allow, + VDEV_CMD); + if (ret) { + mlme_legacy_err("power collapse allow failed %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_cli_set_command(vdev_id, + WMA_VDEV_IBSS_SET_AWAKE_ON_TX_RX, + ibss_cfg->awake_on_tx_rx, + VDEV_CMD); + if (ret) { + mlme_legacy_err("set awake on tx/rx failed %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_cli_set_command(vdev_id, + WMA_VDEV_IBSS_SET_INACTIVITY_TIME, + ibss_cfg->inactivity_bcon_count, + VDEV_CMD); + if (ret) { + mlme_legacy_err("set inactivity time failed %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_cli_set_command(vdev_id, + WMA_VDEV_IBSS_SET_TXSP_END_INACTIVITY_TIME, + ibss_cfg->txsp_end_timeout, + VDEV_CMD); + if (ret) { + mlme_legacy_err("set txsp end failed %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_cli_set_command(vdev_id, + WMA_VDEV_IBSS_PS_SET_WARMUP_TIME_SECS, + ibss_cfg->ps_warm_up_time, + VDEV_CMD); + if (ret) { + mlme_legacy_err("set ps warmup failed %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_cli_set_command(vdev_id, + WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW, + ibss_cfg->ps_1rx_chain_atim_win, + VDEV_CMD); + if (ret) { + mlme_legacy_err("set 1rx chain atim failed %d", ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_4way_hs_offload(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_DISABLE_4WAY_HS_OFFLOAD); + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.gen.disable_4way_hs_offload; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_bmiss_skip_full_scan_value(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_BMISS_SKIP_FULL_SCAN); + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_FAILURE; + } + + *value = mlme_obj->cfg.gen.bmiss_skip_full_scan; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_get_peer_phymode(struct wlan_objmgr_psoc *psoc, uint8_t *mac, + enum wlan_phymode *peer_phymode) +{ + struct wlan_objmgr_peer *peer; + + peer = wlan_objmgr_get_peer_by_mac(psoc, mac, WLAN_MLME_NB_ID); + if (!peer) { + mlme_legacy_err("peer object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + *peer_phymode = wlan_peer_get_phymode(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_MLME_NB_ID); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mlme_set_tgt_wpa3_roam_cap(struct wlan_objmgr_psoc *psoc, + uint32_t akm_bitmap) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.lfr.fw_akm_bitmap |= akm_bitmap; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_ignore_fw_reg_offload_ind(struct wlan_objmgr_psoc *psoc, + bool *disabled) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + *disabled = mlme_obj->cfg.reg.ignore_fw_reg_offload_ind; + return QDF_STATUS_SUCCESS; +} + +char *mlme_get_roam_trigger_str(uint32_t roam_scan_trigger) +{ + switch (roam_scan_trigger) { + case WMI_ROAM_TRIGGER_REASON_PER: + return "PER"; + case WMI_ROAM_TRIGGER_REASON_BMISS: + return "BEACON MISS"; + case WMI_ROAM_TRIGGER_REASON_LOW_RSSI: + return "LOW RSSI"; + case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI: + return "HIGH RSSI"; + case WMI_ROAM_TRIGGER_REASON_PERIODIC: + return "PERIODIC SCAN"; + case WMI_ROAM_TRIGGER_REASON_MAWC: + return "MAWC"; + case WMI_ROAM_TRIGGER_REASON_DENSE: + return "DENSE ENVIRONMENT"; + case WMI_ROAM_TRIGGER_REASON_BACKGROUND: + return "BACKGROUND SCAN"; + case WMI_ROAM_TRIGGER_REASON_FORCED: + return "FORCED SCAN"; + case WMI_ROAM_TRIGGER_REASON_BTM: + return "BTM TRIGGER"; + case WMI_ROAM_TRIGGER_REASON_UNIT_TEST: + return "TEST COMMMAND"; + case WMI_ROAM_TRIGGER_REASON_BSS_LOAD: + return "HIGH BSS LOAD"; + case WMI_ROAM_TRIGGER_REASON_DEAUTH: + return "DEAUTH RECEIVED"; + case WMI_ROAM_TRIGGER_REASON_IDLE: + return "IDLE STATE SCAN"; + case WMI_ROAM_TRIGGER_REASON_STA_KICKOUT: + return "STA KICKOUT"; + case WMI_ROAM_TRIGGER_REASON_NONE: + return "NONE"; + default: + return "UNKNOWN"; + } +} + +void mlme_get_converted_timestamp(uint32_t timestamp, char *time) +{ + uint32_t hr, mins, secs; + + secs = timestamp / 1000; + mins = secs / 60; + hr = mins / 60; + qdf_snprintf(time, TIME_STRING_LEN, "[%02d:%02d:%02d.%06u]", + (hr % 24), (mins % 60), (secs % 60), + (timestamp % 1000) * 1000); +} + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +void wlan_mlme_set_sae_single_pmk_bss_cap(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool val) +{ + struct mlme_legacy_priv *mlme_priv; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_MLME_OBJMGR_ID); + + if (!vdev) { + mlme_err("get vdev failed"); + return; + } + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + mlme_priv->mlme_roam.sae_single_pmk.sae_single_pmk_ap = val; + + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_OBJMGR_ID); +} + +void wlan_mlme_update_sae_single_pmk(struct wlan_objmgr_vdev *vdev, + struct mlme_pmk_info *sae_single_pmk) +{ + struct mlme_legacy_priv *mlme_priv; + uint32_t keymgmt; + bool is_sae_connection = false; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + keymgmt = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT); + + if (keymgmt & (1 << WLAN_CRYPTO_KEY_MGMT_SAE)) + is_sae_connection = true; + + if (mlme_priv->mlme_roam.sae_single_pmk.sae_single_pmk_ap && + is_sae_connection) + mlme_priv->mlme_roam.sae_single_pmk.pmk_info = *sae_single_pmk; +} + +void wlan_mlme_get_sae_single_pmk_info(struct wlan_objmgr_vdev *vdev, + struct wlan_mlme_sae_single_pmk *pmksa) +{ + struct mlme_legacy_priv *mlme_priv; + struct mlme_pmk_info pmk_info; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + pmk_info = mlme_priv->mlme_roam.sae_single_pmk.pmk_info; + + pmksa->sae_single_pmk_ap = + mlme_priv->mlme_roam.sae_single_pmk.sae_single_pmk_ap; + + if (pmk_info.pmk_len) { + qdf_mem_copy(pmksa->pmk_info.pmk, pmk_info.pmk, + pmk_info.pmk_len); + pmksa->pmk_info.pmk_len = pmk_info.pmk_len; + return; + } + + qdf_mem_zero(pmksa->pmk_info.pmk, sizeof(*pmksa->pmk_info.pmk)); + pmksa->pmk_info.pmk_len = 0; +} + +void wlan_mlme_clear_sae_single_pmk_info(struct wlan_objmgr_vdev *vdev, + struct mlme_pmk_info *pmk_recv) +{ + struct mlme_legacy_priv *mlme_priv; + struct wlan_mlme_sae_single_pmk *sae_single_pmk; + + mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); + if (!mlme_priv) { + mlme_legacy_err("vdev legacy private object is NULL"); + return; + } + + sae_single_pmk = &mlme_priv->mlme_roam.sae_single_pmk; + + if (!pmk_recv) { + /* Process flush pmk cmd */ + mlme_legacy_debug("Flush sae_single_pmk info"); + qdf_mem_zero(&sae_single_pmk->pmk_info, + sizeof(sae_single_pmk->pmk_info)); + } else if (pmk_recv->pmk_len != sae_single_pmk->pmk_info.pmk_len) { + mlme_legacy_debug("Invalid pmk len"); + return; + } else if (!qdf_mem_cmp(&sae_single_pmk->pmk_info.pmk, pmk_recv->pmk, + pmk_recv->pmk_len)) { + /* Process delete pmk cmd */ + mlme_legacy_debug("Clear sae_single_pmk info"); + qdf_mem_zero(&sae_single_pmk->pmk_info, + sizeof(sae_single_pmk->pmk_info)); + } +} +#endif + +char *mlme_get_roam_fail_reason_str(uint32_t result) +{ + switch (result) { + case WMI_ROAM_FAIL_REASON_NO_SCAN_START: + return "SCAN NOT STARTED"; + case WMI_ROAM_FAIL_REASON_NO_AP_FOUND: + return "NO AP FOUND"; + case WMI_ROAM_FAIL_REASON_NO_CAND_AP_FOUND: + return "NO CANDIDATE FOUND"; + case WMI_ROAM_FAIL_REASON_HOST: + return "HOST ABORTED"; + case WMI_ROAM_FAIL_REASON_AUTH_SEND: + return "Send AUTH Failed"; + case WMI_ROAM_FAIL_REASON_AUTH_RECV: + return "Received AUTH with FAILURE Status"; + case WMI_ROAM_FAIL_REASON_NO_AUTH_RESP: + return "No Auth response from AP"; + case WMI_ROAM_FAIL_REASON_REASSOC_SEND: + return "Send Re-assoc request failed"; + case WMI_ROAM_FAIL_REASON_REASSOC_RECV: + return "Received Re-Assoc resp with Failure status"; + case WMI_ROAM_FAIL_REASON_NO_REASSOC_RESP: + return "No Re-assoc response from AP"; + case WMI_ROAM_FAIL_REASON_EAPOL_M1_TIMEOUT: + return "EAPOL M1 timed out"; + case WMI_ROAM_FAIL_REASON_MLME: + return "MLME error"; + case WMI_ROAM_FAIL_REASON_INTERNAL_ABORT: + return "Fw aborted roam"; + case WMI_ROAM_FAIL_REASON_SCAN_START: + return "Unable to start roam scan"; + case WMI_ROAM_FAIL_REASON_AUTH_NO_ACK: + return "No ACK for Auth req"; + case WMI_ROAM_FAIL_REASON_AUTH_INTERNAL_DROP: + return "Auth req dropped internally"; + case WMI_ROAM_FAIL_REASON_REASSOC_NO_ACK: + return "No ACK for Re-assoc req"; + case WMI_ROAM_FAIL_REASON_REASSOC_INTERNAL_DROP: + return "Re-assoc dropped internally"; + case WMI_ROAM_FAIL_REASON_EAPOL_M2_SEND: + return "Unable to send M2 frame"; + case WMI_ROAM_FAIL_REASON_EAPOL_M2_INTERNAL_DROP: + return "M2 Frame dropped internally"; + case WMI_ROAM_FAIL_REASON_EAPOL_M2_NO_ACK: + return "No ACK for M2 frame"; + case WMI_ROAM_FAIL_REASON_EAPOL_M3_TIMEOUT: + return "EAPOL M3 timed out"; + case WMI_ROAM_FAIL_REASON_EAPOL_M4_SEND: + return "Unable to send M4 frame"; + case WMI_ROAM_FAIL_REASON_EAPOL_M4_INTERNAL_DROP: + return "M4 frame dropped internally"; + case WMI_ROAM_FAIL_REASON_EAPOL_M4_NO_ACK: + return "No ACK for M4 frame"; + case WMI_ROAM_FAIL_REASON_NO_SCAN_FOR_FINAL_BMISS: + return "No scan on final BMISS"; + case WMI_ROAM_FAIL_REASON_DISCONNECT: + return "Disconnect received during handoff"; + case WMI_ROAM_FAIL_REASON_SYNC: + return "Previous roam sync pending"; + case WMI_ROAM_FAIL_REASON_SAE_INVALID_PMKID: + return "Reason assoc reject - invalid PMKID"; + case WMI_ROAM_FAIL_REASON_SAE_PREAUTH_TIMEOUT: + return "SAE preauth timed out"; + case WMI_ROAM_FAIL_REASON_SAE_PREAUTH_FAIL: + return "SAE preauth failed"; + case WMI_ROAM_FAIL_REASON_UNABLE_TO_START_ROAM_HO: + return "Start handoff failed- internal error"; + default: + return "UNKNOWN"; + } +} + +char *mlme_get_sub_reason_str(uint32_t sub_reason) +{ + switch (sub_reason) { + case WMI_ROAM_TRIGGER_SUB_REASON_PERIODIC_TIMER: + return "PERIODIC TIMER"; + case WMI_ROAM_TRIGGER_SUB_REASON_LOW_RSSI_PERIODIC: + return "LOW RSSI PERIODIC TIMER1"; + case WMI_ROAM_TRIGGER_SUB_REASON_BTM_DI_TIMER: + return "BTM DISASSOC IMMINENT TIMER"; + case WMI_ROAM_TRIGGER_SUB_REASON_FULL_SCAN: + return "FULL SCAN"; + case WMI_ROAM_TRIGGER_SUB_REASON_CU_PERIODIC: + return "CU PERIODIC Timer1"; + case WMI_ROAM_TRIGGER_SUB_REASON_INACTIVITY_TIMER_LOW_RSSI: + return "LOW RSSI INACTIVE TIMER"; + case WMI_ROAM_TRIGGER_SUB_REASON_PERIODIC_TIMER_AFTER_INACTIVITY_CU: + return "CU PERIODIC TIMER2"; + case WMI_ROAM_TRIGGER_SUB_REASON_PERIODIC_TIMER_AFTER_INACTIVITY_LOW_RSSI: + return "LOW RSSI PERIODIC TIMER2"; + case WMI_ROAM_TRIGGER_SUB_REASON_INACTIVITY_TIMER_CU: + return "CU INACTIVITY TIMER"; + default: + return "NONE"; + } +} + +QDF_STATUS +wlan_mlme_get_mgmt_max_retry(struct wlan_objmgr_psoc *psoc, + uint8_t *max_retry) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) { + *max_retry = cfg_default(CFG_MGMT_RETRY_MAX); + return QDF_STATUS_E_FAILURE; + } + + *max_retry = mlme_obj->cfg.gen.mgmt_retry_max; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_status_ring_buffer(struct wlan_objmgr_psoc *psoc, + bool *enable_ring_buffer) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) { + *enable_ring_buffer = cfg_default(CFG_ENABLE_RING_BUFFER); + return QDF_STATUS_E_FAILURE; + } + + *enable_ring_buffer = mlme_obj->cfg.gen.enable_ring_buffer; + return QDF_STATUS_SUCCESS; +} + +bool wlan_mlme_get_peer_unmap_conf(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return false; + + return mlme_obj->cfg.gen.enable_peer_unmap_conf_support; +} + +#ifdef WLAN_FEATURE_SAE + +#define NUM_RETRY_BITS 3 +#define ROAM_AUTH_INDEX 2 +#define ASSOC_INDEX 1 +#define AUTH_INDEX 0 +#define MAX_RETRIES 2 +#define MAX_ROAM_AUTH_RETRIES 1 +#define MAX_AUTH_RETRIES 3 + +QDF_STATUS +wlan_mlme_get_sae_assoc_retry_count(struct wlan_objmgr_psoc *psoc, + uint8_t *retry_count) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) { + *retry_count = 0; + return QDF_STATUS_E_FAILURE; + } + + *retry_count = + WLAN_GET_BITS(mlme_obj->cfg.gen.sae_connect_retries, + ASSOC_INDEX * NUM_RETRY_BITS, NUM_RETRY_BITS); + + *retry_count = QDF_MIN(MAX_RETRIES, *retry_count); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_sae_auth_retry_count(struct wlan_objmgr_psoc *psoc, + uint8_t *retry_count) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) { + *retry_count = 0; + return QDF_STATUS_E_FAILURE; + } + + *retry_count = + WLAN_GET_BITS(mlme_obj->cfg.gen.sae_connect_retries, + AUTH_INDEX * NUM_RETRY_BITS, NUM_RETRY_BITS); + + *retry_count = QDF_MIN(MAX_AUTH_RETRIES, *retry_count); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_get_sae_roam_auth_retry_count(struct wlan_objmgr_psoc *psoc, + uint8_t *retry_count) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) { + *retry_count = 0; + return QDF_STATUS_E_FAILURE; + } + + *retry_count = + WLAN_GET_BITS(mlme_obj->cfg.gen.sae_connect_retries, + ROAM_AUTH_INDEX * NUM_RETRY_BITS, NUM_RETRY_BITS); + + *retry_count = QDF_MIN(MAX_ROAM_AUTH_RETRIES, *retry_count); + + return QDF_STATUS_SUCCESS; +} + +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS +wlan_mlme_get_roam_reason_vsie_status(struct wlan_objmgr_psoc *psoc, + uint8_t *roam_reason_vsie_enable) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *roam_reason_vsie_enable = + cfg_default(CFG_ENABLE_ROAM_REASON_VSIE); + return QDF_STATUS_E_FAILURE; + } + + *roam_reason_vsie_enable = mlme_obj->cfg.lfr.enable_roam_reason_vsie; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_mlme_set_roam_reason_vsie_status(struct wlan_objmgr_psoc *psoc, + uint8_t roam_reason_vsie_enable) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_FAILURE; + + mlme_obj->cfg.lfr.enable_roam_reason_vsie = roam_reason_vsie_enable; + return QDF_STATUS_SUCCESS; +} + +uint32_t wlan_mlme_get_roaming_triggers(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return cfg_default(CFG_ROAM_TRIGGER_BITMAP); + + return mlme_obj->cfg.lfr.roam_trigger_bitmap; +} +#endif diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/src/wlan_mlme_ucfg_api.c b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/src/wlan_mlme_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..bdbc2aaa4243c5b1928d4c0e373c41cc76e0d5f8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/src/wlan_mlme_ucfg_api.c @@ -0,0 +1,1947 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define UCFG APIs exposed by the mlme component + */ + +#include "cfg_ucfg_api.h" +#include "cfg_mlme_sta.h" +#include "wlan_mlme_main.h" +#include "wlan_mlme_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_mlme_vdev_mgr_interface.h" +#include +#include "wlan_pdev_mlme_api.h" + +QDF_STATUS ucfg_mlme_global_init(void) +{ + mlme_register_mlme_ext_ops(); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mlme_global_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mlme_init(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_register_peer_create_handler( + WLAN_UMAC_COMP_MLME, + mlme_peer_object_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + mlme_legacy_err("peer create register notification failed"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_register_peer_destroy_handler( + WLAN_UMAC_COMP_MLME, + mlme_peer_object_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + mlme_legacy_err("peer destroy register notification failed"); + return QDF_STATUS_E_FAILURE; + } + + return status; +} + +QDF_STATUS ucfg_mlme_deinit(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_unregister_peer_destroy_handler( + WLAN_UMAC_COMP_MLME, + mlme_peer_object_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) + mlme_legacy_err("unable to unregister peer destroy handle"); + + status = wlan_objmgr_unregister_peer_create_handler( + WLAN_UMAC_COMP_MLME, + mlme_peer_object_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) + mlme_legacy_err("unable to unregister peer create handle"); + + return status; +} + +QDF_STATUS ucfg_mlme_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + + status = mlme_cfg_on_psoc_enable(psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) + mlme_legacy_err("Failed to initialize MLME CFG"); + + return status; +} + +void ucfg_mlme_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + /* Clear the MLME CFG Structure */ +} + +QDF_STATUS ucfg_mlme_pdev_open(struct wlan_objmgr_pdev *pdev) +{ + struct pdev_mlme_obj *pdev_mlme; + + pdev_mlme = wlan_pdev_mlme_get_cmpt_obj(pdev); + if (!pdev_mlme) { + mlme_legacy_err(" PDEV MLME is NULL"); + return QDF_STATUS_E_FAILURE; + } + pdev_mlme->mlme_register_ops = mlme_register_vdev_mgr_ops; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mlme_pdev_close(struct wlan_objmgr_pdev *pdev) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ucfg_mlme_convert_power_cfg_chan_to_freq() - converts channel numbers to + * frequencies and copies the triplets to power_freq_data array + * @pdev: pointer to pdev object + * @max_length: Max length of the power chan data array + * @length: length of the data present in power_chan_data array + * @power_chan_data: Power data array from which channel numbers needs to be + * converted to frequencies + * @power_freq_data: Power data array in which the power data needs to be copied + * after conversion of channel numbers to frequencies + * + * power_data is received in the form of (first_channel_number, + * number_of_channels, max_tx_power) triplet, convert the channel numbers from + * the power_chan_data array to frequencies and copy the triplets + * (first_frequency, number_of_channels, max_tx_power) values to + * the power_freq_data array + * + * Return: Number of bytes filled in power_freq_data + */ + +static uint32_t ucfg_mlme_convert_power_cfg_chan_to_freq( + struct wlan_objmgr_pdev *pdev, + uint32_t max_length, + qdf_size_t length, + uint8_t *power_chan_data, + uint8_t *power_freq_data) +{ + uint32_t count = 0, rem_length = length, copied_length = 0, i = 0; + tSirMacChanInfo *pwr_cfg_data; + + pwr_cfg_data = qdf_mem_malloc(max_length); + if (!pwr_cfg_data) + return 0; + + mlme_legacy_debug("max_length %d length %zu", max_length, length); + while ((rem_length >= 3) && + (copied_length <= (max_length - (sizeof(tSirMacChanInfo))))) { + pwr_cfg_data[i].first_freq = wlan_reg_chan_to_freq( + pdev, + power_chan_data[count++]); + pwr_cfg_data[i].numChannels = power_chan_data[count++]; + pwr_cfg_data[i].maxTxPower = power_chan_data[count++]; + copied_length += sizeof(tSirMacChanInfo); + rem_length -= 3; + mlme_legacy_debug("First freq %d num channels %d max tx power %d", + pwr_cfg_data[i].first_freq, + pwr_cfg_data[i].numChannels, + pwr_cfg_data[i].maxTxPower); + i++; + } + + qdf_mem_zero(power_freq_data, max_length); + qdf_mem_copy(power_freq_data, pwr_cfg_data, copied_length); + qdf_mem_free(pwr_cfg_data); + return copied_length; +} + +void ucfg_mlme_cfg_chan_to_freq(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_objmgr_psoc *psoc = wlan_pdev_get_psoc(pdev); + struct wlan_mlme_psoc_ext_obj *mlme_obj; + struct wlan_mlme_cfg *mlme_cfg; + uint32_t converted_data_len = 0; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return; + + mlme_cfg = &mlme_obj->cfg; + + mlme_cfg->power.max_tx_power_24.max_len = CFG_MAX_TX_POWER_2_4_LEN; + converted_data_len = ucfg_mlme_convert_power_cfg_chan_to_freq( + pdev, + mlme_cfg->power.max_tx_power_24_chan.max_len, + mlme_cfg->power.max_tx_power_24_chan.len, + mlme_cfg->power.max_tx_power_24_chan.data, + mlme_cfg->power.max_tx_power_24.data); + if (!converted_data_len) { + mlme_legacy_err("mlme cfg power 2_4 data chan number to freq failed"); + return; + } + + mlme_cfg->power.max_tx_power_24.len = converted_data_len; + + mlme_cfg->power.max_tx_power_5.max_len = CFG_MAX_TX_POWER_5_LEN; + converted_data_len = ucfg_mlme_convert_power_cfg_chan_to_freq( + pdev, + mlme_cfg->power.max_tx_power_5_chan.max_len, + mlme_cfg->power.max_tx_power_5_chan.len, + mlme_cfg->power.max_tx_power_5_chan.data, + mlme_cfg->power.max_tx_power_5.data); + if (!converted_data_len) { + mlme_legacy_err("mlme cfg power 5 data chan number to freq failed"); + return; + } + mlme_cfg->power.max_tx_power_5.len = converted_data_len; +} + +QDF_STATUS +ucfg_mlme_get_sta_keep_alive_period(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_INFRA_STA_KEEP_ALIVE_PERIOD); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.sta.sta_keep_alive_period; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_dfs_master_capability(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_ENABLE_DFS_MASTER_CAPABILITY); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.dfs_cfg.dfs_master_capable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_oem_6g_supported(struct wlan_objmgr_psoc *psoc, + bool *oem_6g_disable) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *oem_6g_disable = + cfg_default(CFG_OEM_SIXG_SUPPORT_DISABLE); + return QDF_STATUS_E_INVAL; + } + + *oem_6g_disable = mlme_obj->cfg.wifi_pos_cfg.oem_6g_support_disable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_fine_time_meas_cap(struct wlan_objmgr_psoc *psoc, + uint32_t *fine_time_meas_cap) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *fine_time_meas_cap = + cfg_default(CFG_FINE_TIME_MEAS_CAPABILITY); + return QDF_STATUS_E_INVAL; + } + + *fine_time_meas_cap = mlme_obj->cfg.wifi_pos_cfg.fine_time_meas_cap; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_fine_time_meas_cap(struct wlan_objmgr_psoc *psoc, + uint32_t fine_time_meas_cap) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.wifi_pos_cfg.fine_time_meas_cap = fine_time_meas_cap; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_dfs_disable_channel_switch(struct wlan_objmgr_psoc *psoc, + bool *dfs_disable_channel_switch) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *dfs_disable_channel_switch = + cfg_default(CFG_DISABLE_DFS_CH_SWITCH); + return QDF_STATUS_E_INVAL; + } + + *dfs_disable_channel_switch = + mlme_obj->cfg.dfs_cfg.dfs_disable_channel_switch; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_dfs_disable_channel_switch(struct wlan_objmgr_psoc *psoc, + bool dfs_disable_channel_switch) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + return QDF_STATUS_E_INVAL; + } + + mlme_obj->cfg.dfs_cfg.dfs_disable_channel_switch = + dfs_disable_channel_switch; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_dfs_ignore_cac(struct wlan_objmgr_psoc *psoc, + bool *dfs_ignore_cac) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *dfs_ignore_cac = cfg_default(CFG_IGNORE_CAC); + return QDF_STATUS_E_INVAL; + } + + *dfs_ignore_cac = mlme_obj->cfg.dfs_cfg.dfs_ignore_cac; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_dfs_ignore_cac(struct wlan_objmgr_psoc *psoc, + bool dfs_ignore_cac) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.dfs_cfg.dfs_ignore_cac = dfs_ignore_cac; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_sap_tx_leakage_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *sap_tx_leakage_threshold) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *sap_tx_leakage_threshold = + cfg_default(CFG_SAP_TX_LEAKAGE_THRESHOLD); + return QDF_STATUS_E_INVAL; + } + + *sap_tx_leakage_threshold = + mlme_obj->cfg.dfs_cfg.sap_tx_leakage_threshold; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_sap_tx_leakage_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t sap_tx_leakage_threshold) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.dfs_cfg.sap_tx_leakage_threshold = + sap_tx_leakage_threshold; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_dfs_pri_multiplier(struct wlan_objmgr_psoc *psoc, + uint32_t *dfs_pri_multiplier) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *dfs_pri_multiplier = + cfg_default(CFG_DFS_RADAR_PRI_MULTIPLIER); + return QDF_STATUS_E_INVAL; + } + + *dfs_pri_multiplier = + mlme_obj->cfg.dfs_cfg.dfs_pri_multiplier; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_dfs_pri_multiplier(struct wlan_objmgr_psoc *psoc, + uint32_t dfs_pri_multiplier) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.dfs_cfg.dfs_pri_multiplier = + dfs_pri_multiplier; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_dfs_filter_offload(struct wlan_objmgr_psoc *psoc, + bool *dfs_filter_offload) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *dfs_filter_offload = + cfg_default(CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD); + return QDF_STATUS_E_INVAL; + } + + *dfs_filter_offload = mlme_obj->cfg.dfs_cfg.dfs_filter_offload; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_dfs_filter_offload(struct wlan_objmgr_psoc *psoc, + bool dfs_filter_offload) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.dfs_cfg.dfs_filter_offload = dfs_filter_offload; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_pmkid_modes(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_PMKID_MODES); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.sta.pmkid_modes; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_pmkid_modes(struct wlan_objmgr_psoc *psoc, + uint32_t val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.sta.pmkid_modes = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_twt_requestor(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_TWT_REQUESTOR); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.twt_cfg.is_twt_requestor_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_twt_requestor(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.twt_cfg.is_twt_requestor_enabled = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_twt_responder(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_TWT_RESPONDER); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.twt_cfg.is_twt_responder_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_twt_responder(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.twt_cfg.is_twt_responder_enabled = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_bcast_twt(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_BCAST_TWT); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.twt_cfg.is_twt_bcast_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_bcast_twt(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.twt_cfg.is_twt_bcast_enabled = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_twt_congestion_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_TWT_CONGESTION_TIMEOUT); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.twt_cfg.twt_congestion_timeout; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_twt_congestion_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.twt_cfg.twt_congestion_timeout = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_enable_twt(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_ENABLE_TWT); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.twt_cfg.is_twt_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_enable_twt(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.twt_cfg.is_twt_enabled = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_dot11p_mode(struct wlan_objmgr_psoc *psoc, + enum dot11p_mode *out_mode) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *out_mode = cfg_default(CFG_DOT11P_MODE); + return QDF_STATUS_E_INVAL; + } + + *out_mode = mlme_obj->cfg.sta.dot11p_mode; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_go_cts2self_for_sta(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_ENABLE_GO_CTS2SELF_FOR_STA); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.sta.enable_go_cts2self_for_sta; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_qcn_ie_support(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_QCN_IE_SUPPORT); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.sta.qcn_ie_support; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_tgt_gtx_usr_cfg(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_TGT_GTX_USR_CFG); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.sta.tgt_gtx_usr_cfg; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_is_override_ht20_40_24g(struct wlan_objmgr_psoc *psoc, bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_OBSS_HT40_OVERRIDE_HT40_20_24GHZ); + return QDF_STATUS_E_INVAL; + } + *val = mlme_obj->cfg.obss_ht40.is_override_ht20_40_24g; + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS +ucfg_mlme_get_roam_disable_config(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_STA_DISABLE_ROAM); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.sta_roam_disable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_roaming_offload(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR3_ROAMING_OFFLOAD); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.lfr3_roaming_offload; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_roaming_offload(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.lfr.lfr3_roaming_offload = val; + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS +ucfg_mlme_get_first_scan_bucket_threshold(struct wlan_objmgr_psoc *psoc, + uint8_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_FIRST_SCAN_BUCKET_THRESHOLD); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.first_scan_bucket_threshold; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_is_mawc_enabled(struct wlan_objmgr_psoc *psoc, bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_MAWC_FEATURE_ENABLED); + return QDF_STATUS_E_INVAL; + } + *val = mlme_obj->cfg.lfr.mawc_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_mawc_enabled(struct wlan_objmgr_psoc *psoc, bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.lfr.mawc_enabled = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_is_fast_transition_enabled(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_FAST_TRANSITION_ENABLED); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.fast_transition_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_fast_transition_enabled(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.lfr.fast_transition_enabled = val; + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_ADAPTIVE_11R +QDF_STATUS +ucfg_mlme_set_tgt_adaptive_11r_cap(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.lfr.tgt_adaptive_11r_cap = val; + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS +ucfg_mlme_is_roam_scan_offload_enabled(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_ROAM_SCAN_OFFLOAD_ENABLED); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.roam_scan_offload_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_roam_scan_offload_enabled(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.lfr.roam_scan_offload_enabled = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_neighbor_scan_max_chan_time(struct wlan_objmgr_psoc *psoc, + uint16_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.neighbor_scan_max_chan_time; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_neighbor_scan_min_chan_time(struct wlan_objmgr_psoc *psoc, + uint16_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.neighbor_scan_min_chan_time; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_delay_before_vdev_stop(struct wlan_objmgr_psoc *psoc, + uint8_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_DELAY_BEFORE_VDEV_STOP); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.delay_before_vdev_stop; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_roam_bmiss_final_bcnt(struct wlan_objmgr_psoc *psoc, + uint8_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_ROAM_BMISS_FINAL_BCNT); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.roam_bmiss_final_bcnt; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_roam_bmiss_first_bcnt(struct wlan_objmgr_psoc *psoc, + uint8_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_ROAM_BMISS_FIRST_BCNT); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.roam_bmiss_first_bcnt; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_is_lfr_enabled(struct wlan_objmgr_psoc *psoc, bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_FEATURE_ENABLED); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.lfr_enabled; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_lfr_enabled(struct wlan_objmgr_psoc *psoc, bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.lfr.lfr_enabled = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_is_roam_prefer_5ghz(struct wlan_objmgr_psoc *psoc, bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_ROAM_PREFER_5GHZ); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.roam_prefer_5ghz; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_roam_intra_band(struct wlan_objmgr_psoc *psoc, bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.lfr.roam_intra_band = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_home_away_time(struct wlan_objmgr_psoc *psoc, uint16_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.roam_scan_home_away_time; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_fast_roam_in_concurrency_enabled(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.lfr.enable_fast_roam_in_concurrency = val; + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_ESE +QDF_STATUS +ucfg_mlme_is_ese_enabled(struct wlan_objmgr_psoc *psoc, bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR_ESE_FEATURE_ENABLED); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.lfr.ese_enabled; + + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_WLAN_ESE */ + +QDF_STATUS +ucfg_mlme_get_opr_rate_set(struct wlan_objmgr_psoc *psoc, + uint8_t *buf, qdf_size_t *len) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + return wlan_mlme_get_cfg_str(buf, &mlme_obj->cfg.rates.opr_rate_set, + len); +} + +QDF_STATUS +ucfg_mlme_get_ext_opr_rate_set(struct wlan_objmgr_psoc *psoc, + uint8_t *buf, qdf_size_t *len) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + return wlan_mlme_get_cfg_str(buf, &mlme_obj->cfg.rates.ext_opr_rate_set, + len); +} + +QDF_STATUS +ucfg_mlme_get_supported_mcs_set(struct wlan_objmgr_psoc *psoc, + uint8_t *buf, qdf_size_t *len) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + return wlan_mlme_get_cfg_str(buf, + &mlme_obj->cfg.rates.supported_mcs_set, + len); +} + +QDF_STATUS +ucfg_mlme_set_supported_mcs_set(struct wlan_objmgr_psoc *psoc, + uint8_t *buf, qdf_size_t len) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + return wlan_mlme_set_cfg_str(buf, + &mlme_obj->cfg.rates.supported_mcs_set, + len); +} + +QDF_STATUS +ucfg_mlme_get_current_mcs_set(struct wlan_objmgr_psoc *psoc, + uint8_t *buf, qdf_size_t *len) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + return wlan_mlme_get_cfg_str(buf, + &mlme_obj->cfg.rates.current_mcs_set, + len); +} + +QDF_STATUS +ucfg_mlme_get_wmi_wq_watchdog_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t *wmi_wq_watchdog_timeout) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *wmi_wq_watchdog_timeout = cfg_default(CFG_WMI_WQ_WATCHDOG); + return QDF_STATUS_E_INVAL; + } + + *wmi_wq_watchdog_timeout = + mlme_obj->cfg.timeouts.wmi_wq_watchdog_timeout; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_wmi_wq_watchdog_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t wmi_wq_watchdog_timeout) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + if (!cfg_in_range(CFG_WMI_WQ_WATCHDOG, wmi_wq_watchdog_timeout)) { + mlme_legacy_err("wmi watchdog bite timeout is invalid %d", + wmi_wq_watchdog_timeout); + return QDF_STATUS_E_INVAL; + } + + mlme_obj->cfg.timeouts.wmi_wq_watchdog_timeout = + wmi_wq_watchdog_timeout; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_stats_get_periodic_display_time(struct wlan_objmgr_psoc *psoc, + uint32_t *periodic_display_time) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *periodic_display_time = + cfg_default(CFG_PERIODIC_STATS_DISPLAY_TIME); + return QDF_STATUS_E_INVAL; + } + + *periodic_display_time = + mlme_obj->cfg.stats.stats_periodic_display_time; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_stats_get_cfg_values(struct wlan_objmgr_psoc *psoc, + int *link_speed_rssi_high, + int *link_speed_rssi_mid, + int *link_speed_rssi_low, + uint32_t *link_speed_rssi_report) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *link_speed_rssi_high = + cfg_default(CFG_LINK_SPEED_RSSI_HIGH); + *link_speed_rssi_mid = + cfg_default(CFG_LINK_SPEED_RSSI_MID); + *link_speed_rssi_low = + cfg_default(CFG_LINK_SPEED_RSSI_LOW); + *link_speed_rssi_report = + cfg_default(CFG_REPORT_MAX_LINK_SPEED); + return QDF_STATUS_E_INVAL; + } + + *link_speed_rssi_high = + mlme_obj->cfg.stats.stats_link_speed_rssi_high; + *link_speed_rssi_mid = + mlme_obj->cfg.stats.stats_link_speed_rssi_med; + *link_speed_rssi_low = + mlme_obj->cfg.stats.stats_link_speed_rssi_low; + *link_speed_rssi_report = + mlme_obj->cfg.stats.stats_report_max_link_speed_rssi; + + return QDF_STATUS_SUCCESS; +} + +bool ucfg_mlme_stats_is_link_speed_report_actual(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + int report_link_speed = 0; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + report_link_speed = cfg_default(CFG_REPORT_MAX_LINK_SPEED); + else + report_link_speed = + mlme_obj->cfg.stats.stats_report_max_link_speed_rssi; + + return (report_link_speed == CFG_STATS_LINK_SPEED_REPORT_ACTUAL); +} + +bool ucfg_mlme_stats_is_link_speed_report_max(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + int report_link_speed = 0; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + report_link_speed = cfg_default(CFG_REPORT_MAX_LINK_SPEED); + else + report_link_speed = + mlme_obj->cfg.stats.stats_report_max_link_speed_rssi; + + return (report_link_speed == CFG_STATS_LINK_SPEED_REPORT_MAX); +} + +bool +ucfg_mlme_stats_is_link_speed_report_max_scaled(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + int report_link_speed = 0; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + report_link_speed = cfg_default(CFG_REPORT_MAX_LINK_SPEED); + else + report_link_speed = + mlme_obj->cfg.stats.stats_report_max_link_speed_rssi; + + return (report_link_speed == CFG_STATS_LINK_SPEED_REPORT_MAX_SCALED); +} + +QDF_STATUS +ucfg_mlme_get_ps_data_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t *inactivity_timeout) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *inactivity_timeout = + cfg_default(CFG_PS_DATA_INACTIVITY_TIMEOUT); + return QDF_STATUS_E_FAILURE; + } + *inactivity_timeout = mlme_obj->cfg.timeouts.ps_data_inactivity_timeout; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_ps_data_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t inactivity_timeout) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + if (!cfg_in_range(CFG_PS_DATA_INACTIVITY_TIMEOUT, inactivity_timeout)) { + mlme_legacy_err("inactivity timeout set value is invalid %d", + inactivity_timeout); + return QDF_STATUS_E_INVAL; + } + mlme_obj->cfg.timeouts.ps_data_inactivity_timeout = inactivity_timeout; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_sta_keepalive_method(struct wlan_objmgr_psoc *psoc, + enum station_keepalive_method *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + *val = mlme_obj->cfg.sta.sta_keepalive_method; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_enable_deauth_to_disassoc_map(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + *value = mlme_obj->cfg.gen.enable_deauth_to_disassoc_map; + return QDF_STATUS_SUCCESS; +} + + +QDF_STATUS +ucfg_mlme_get_ap_random_bssid_enable(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + *value = mlme_obj->cfg.sap_cfg.ap_random_bssid_enable; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_latency_enable(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + mlme_legacy_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + *value = mlme_obj->cfg.wlm_config.latency_enable; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mlme_get_ibss_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_ibss_cfg *ibss_cfg) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + if (!ibss_cfg) + return QDF_STATUS_E_FAILURE; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + mlme_legacy_err("MLME Obj null on get IBSS config"); + mlme_init_ibss_cfg(psoc, ibss_cfg); + return QDF_STATUS_E_INVAL; + } + *ibss_cfg = mlme_obj->cfg.ibss; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mlme_set_ibss_auto_bssid(struct wlan_objmgr_psoc *psoc, + uint32_t auto_bssid) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + mlme_legacy_err("MLME Obj null on get IBSS config"); + return QDF_STATUS_E_INVAL; + } + mlme_obj->cfg.ibss.auto_bssid = auto_bssid; + return QDF_STATUS_SUCCESS; +} + +#ifdef MWS_COEX +QDF_STATUS +ucfg_mlme_get_mws_coex_4g_quick_tdm(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_MWS_COEX_4G_QUICK_FTDM); + mlme_legacy_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.mwc.mws_coex_4g_quick_tdm; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_mws_coex_5g_nr_pwr_limit(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_MWS_COEX_5G_NR_PWR_LIMIT); + mlme_legacy_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + *val = mlme_obj->cfg.mwc.mws_coex_5g_nr_pwr_limit; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_mws_coex_pcc_channel_avoid_delay(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_MWS_COEX_PCC_CHANNEL_AVOID_DELAY); + mlme_legacy_err("mlme obj null"); + return QDF_STATUS_SUCCESS; + } + + *val = mlme_obj->cfg.mwc.mws_coex_pcc_channel_avoid_delay; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_mws_coex_scc_channel_avoid_delay(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_MWS_COEX_SCC_CHANNEL_AVOID_DELAY); + mlme_legacy_err("mlme obj null"); + return QDF_STATUS_SUCCESS; + } + + *val = mlme_obj->cfg.mwc.mws_coex_scc_channel_avoid_delay; + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS +ucfg_mlme_get_etsi_srd_chan_in_master_mode(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_ETSI_SRD_CHAN_IN_MASTER_MODE); + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_INVAL; + } + + *value = mlme_obj->cfg.reg.etsi_srd_chan_in_master_mode; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_srd_master_mode_for_vdev(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE vdev_opmode, + bool *value) +{ + return wlan_mlme_get_srd_master_mode_for_vdev(psoc, vdev_opmode, value); +} + +#ifdef SAP_AVOID_ACS_FREQ_LIST +QDF_STATUS +ucfg_mlme_get_acs_avoid_freq_list(struct wlan_objmgr_psoc *psoc, + uint16_t *freq_list, uint8_t *freq_list_num) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + qdf_size_t avoid_acs_freq_list_num; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + qdf_uint16_array_parse( + cfg_default(CFG_SAP_AVOID_ACS_FREQ_LIST), + freq_list, CFG_VALID_CHANNEL_LIST_LEN, + &avoid_acs_freq_list_num); + *freq_list_num = avoid_acs_freq_list_num; + + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_INVAL; + } + + *freq_list_num = mlme_obj->cfg.reg.avoid_acs_freq_list_num; + qdf_mem_copy(freq_list, mlme_obj->cfg.reg.avoid_acs_freq_list, + *freq_list_num * sizeof(uint16_t)); + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS +ucfg_mlme_get_11d_in_world_mode(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_ENABLE_11D_IN_WORLD_MODE); + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_INVAL; + } + + *value = mlme_obj->cfg.reg.enable_11d_in_world_mode; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_restart_beaconing_on_ch_avoid(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_RESTART_BEACONING_ON_CH_AVOID); + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_INVAL; + } + + *value = mlme_obj->cfg.reg.restart_beaconing_on_ch_avoid; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_indoor_channel_support(struct wlan_objmgr_psoc *psoc, + bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_INDOOR_CHANNEL_SUPPORT); + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_INVAL; + } + + *value = mlme_obj->cfg.reg.indoor_channel_support; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_scan_11d_interval(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_SCAN_11D_INTERVAL); + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_INVAL; + } + + *value = mlme_obj->cfg.reg.scan_11d_interval; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_nol_across_regdmn(struct wlan_objmgr_psoc *psoc, bool *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_RETAIN_NOL_ACROSS_REG_DOMAIN); + mlme_legacy_err("Failed to get MLME Obj"); + return QDF_STATUS_E_INVAL; + } + + *value = mlme_obj->cfg.reg.retain_nol_across_regdmn_update; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_valid_channel_freq_list(struct wlan_objmgr_psoc *psoc, + uint32_t *channel_list, + uint32_t *channel_list_num) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + qdf_size_t valid_channel_list_num = 0; + uint8_t tmp_channel_list[CFG_VALID_CHANNEL_LIST_LEN]; + uint8_t i; + struct wlan_objmgr_pdev *pdev = NULL; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + qdf_uint8_array_parse(cfg_default(CFG_VALID_CHANNEL_LIST), + tmp_channel_list, + CFG_VALID_CHANNEL_LIST_LEN, + &valid_channel_list_num); + *channel_list_num = (uint8_t)valid_channel_list_num; + mlme_legacy_err("Failed to get MLME Obj"); + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_MLME_NB_ID); + if (!pdev) { + mlme_legacy_err("null pdev"); + return QDF_STATUS_E_INVAL; + } + + for (i = 0; i < valid_channel_list_num; i++) { + channel_list[i] = + wlan_reg_chan_to_freq(pdev, tmp_channel_list[i]); + } + + wlan_objmgr_pdev_release_ref(pdev, WLAN_MLME_NB_ID); + + return QDF_STATUS_E_INVAL; + } + + *channel_list_num = (uint32_t)mlme_obj->cfg.reg.valid_channel_list_num; + for (i = 0; i < *channel_list_num; i++) + channel_list[i] = mlme_obj->cfg.reg.valid_channel_freq_list[i]; + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_LFR_SUBNET_DETECTION +QDF_STATUS +ucfg_mlme_is_subnet_detection_enabled(struct wlan_objmgr_psoc *psoc, bool *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_LFR3_ENABLE_SUBNET_DETECTION); + return QDF_STATUS_E_INVAL; + } + *val = mlme_obj->cfg.lfr.enable_lfr_subnet_detection; + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS +ucfg_mlme_set_current_tx_power_level(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.power.current_tx_power_level = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_current_tx_power_level(struct wlan_objmgr_psoc *psoc, + uint8_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_CURRENT_TX_POWER_LEVEL); + return QDF_STATUS_E_INVAL; + } + + *value = mlme_obj->cfg.power.current_tx_power_level; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_obss_detection_offload_enabled(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.obss_ht40.obss_detection_offload_enabled = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_bss_color_collision_det_sta(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.obss_ht40.bss_color_collision_det_sta = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_obss_color_collision_offload_enabled( + struct wlan_objmgr_psoc *psoc, uint8_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.obss_ht40.obss_color_collision_offload_enabled = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mlme_set_restricted_80p80_bw_supp(struct wlan_objmgr_psoc *psoc, + bool restricted_80p80_supp) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.vht_caps.vht_cap_info.restricted_80p80_bw_supp = + restricted_80p80_supp; + + return QDF_STATUS_SUCCESS; +} + +bool ucfg_mlme_get_restricted_80p80_bw_supp(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + + if (!mlme_obj) + return true; + + return mlme_obj->cfg.vht_caps.vht_cap_info.restricted_80p80_bw_supp; +} + +QDF_STATUS +ucfg_mlme_get_channel_bonding_24ghz(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *val = cfg_default(CFG_CHANNEL_BONDING_MODE_24GHZ); + return QDF_STATUS_E_INVAL; + } + *val = mlme_obj->cfg.feature_flags.channel_bonding_mode_24ghz; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_channel_bonding_24ghz(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.feature_flags.channel_bonding_mode_24ghz = value; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_channel_bonding_5ghz(struct wlan_objmgr_psoc *psoc, + uint32_t *value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) { + *value = cfg_default(CFG_CHANNEL_BONDING_MODE_5GHZ); + return QDF_STATUS_E_INVAL; + } + *value = mlme_obj->cfg.feature_flags.channel_bonding_mode_5ghz; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_channel_bonding_5ghz(struct wlan_objmgr_psoc *psoc, + uint32_t value) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.feature_flags.channel_bonding_mode_5ghz = value; + + return QDF_STATUS_SUCCESS; +} + +bool ucfg_mlme_validate_full_roam_scan_period(uint32_t full_roam_scan_period) +{ + bool is_valid = true; + uint32_t min, max; + + if (!cfg_in_range(CFG_LFR_FULL_ROAM_SCAN_REFRESH_PERIOD, + full_roam_scan_period)) { + min = (cfg_min(CFG_LFR_FULL_ROAM_SCAN_REFRESH_PERIOD)); + max = (cfg_max(CFG_LFR_FULL_ROAM_SCAN_REFRESH_PERIOD)); + mlme_legacy_err("Full roam scan period value %d is out of range (Min: %d Max: %d)", + full_roam_scan_period, min, max); + is_valid = false; + } + + return is_valid; +} + +bool ucfg_mlme_validate_scan_period(uint32_t roam_scan_period) +{ + bool is_valid = true; + + if (!cfg_in_range(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD, + roam_scan_period)) { + mlme_legacy_err("Roam scan period value %d msec is out of range (Min: %d msec Max: %d msec)", + roam_scan_period, + cfg_min(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD), + cfg_max(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD)); + is_valid = false; + } + + return is_valid; +} + +bool ucfg_is_roaming_enabled(struct wlan_objmgr_pdev *pdev, uint8_t vdev_id) +{ + struct wlan_objmgr_psoc *psoc = wlan_pdev_get_psoc(pdev); + + if (mlme_get_roam_state(psoc, vdev_id) == ROAM_RSO_STARTED) + return true; + + return false; +} diff --git a/drivers/staging/qcacld-3.0/components/nan/core/inc/nan_public_structs.h b/drivers/staging/qcacld-3.0/components/nan/core/inc/nan_public_structs.h new file mode 100644 index 0000000000000000000000000000000000000000..e8e189cb31b47f63e62fde5a917b8464226c8244 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/core/inc/nan_public_structs.h @@ -0,0 +1,855 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains nan definitions exposed to other modules + */ + +#ifndef _NAN_PUBLIC_STRUCTS_H_ +#define _NAN_PUBLIC_STRUCTS_H_ + +#include "qdf_types.h" +#include "qdf_status.h" +#include "scheduler_api.h" +#if defined(CONFIG_HL_SUPPORT) +#include "wlan_tgt_def_config_hl.h" +#else +#include "wlan_tgt_def_config.h" +#endif + +struct wlan_objmgr_psoc; +struct wlan_objmgr_vdev; + +#define IFACE_NAME_SIZE 64 +#define NDP_QOS_INFO_LEN 255 +#define NDP_APP_INFO_LEN 255 +#define NDP_PMK_LEN 32 +#define NDP_SCID_BUF_LEN 256 +#define NDP_NUM_INSTANCE_ID 255 +#define NAN_MAX_SERVICE_NAME_LEN 255 +#define NAN_PASSPHRASE_MIN_LEN 8 +#define NAN_PASSPHRASE_MAX_LEN 63 +#define NAN_CH_INFO_MAX_CHANNELS 4 +#define WLAN_WAIT_TIME_NDP_END 2000 + +#define NAN_PSEUDO_VDEV_ID CFG_TGT_NUM_VDEV + +#define NAN_SER_CMD_TIMEOUT 4000 + +/** + * enum nan_discovery_msg_type - NAN msg type + * @NAN_GENERIC_REQ: Type for all the NAN requests other than enable/disable + * @NAN_ENABLE_REQ: Request type for enabling the NAN Discovery + * @NAN_DISABLE_REQ: Request type for disabling the NAN Discovery + */ +enum nan_discovery_msg_type { + NAN_GENERIC_REQ = 0, + NAN_ENABLE_REQ = 1, + NAN_DISABLE_REQ = 2, +}; + +/** + * enum nan_datapath_msg_type - NDP msg type + * @NAN_DATAPATH_INF_CREATE_REQ: ndi create request + * @NAN_DATAPATH_INF_CREATE_RSP: ndi create response + * @NAN_DATAPATH_INF_DELETE_REQ: ndi delete request + * @NAN_DATAPATH_INF_DELETE_RSP: ndi delete response + * @NDP_INITIATOR_REQ: ndp initiator request + * @NDP_INITIATOR_RSP: ndp initiator response + * @NDP_RESPONDER_REQ: ndp responder request + * @NDP_RESPONDER_RSP: ndp responder response + * @NDP_END_REQ: ndp end request + * @NDP_END_RSP: ndp end response + * @NDP_INDICATION: ndp indication + * @NDP_CONFIRM: ndp confirm + * @NDP_END_IND: ndp end indication + * @NDP_NEW_PEER: ndp new peer created + * @NDP_PEER_DEPARTED: ndp peer departed/deleted + * @NDP_SCHEDULE_UPDATE: ndp schedule update + * @NDP_END_ALL: end all NDPs request + * @NDP_HOST_UPDATE: update host about ndp status + */ +enum nan_datapath_msg_type { + NAN_DATAPATH_INF_CREATE_REQ = 0, + NAN_DATAPATH_INF_CREATE_RSP = 1, + NAN_DATAPATH_INF_DELETE_REQ = 2, + NAN_DATAPATH_INF_DELETE_RSP = 3, + NDP_INITIATOR_REQ = 4, + NDP_INITIATOR_RSP = 5, + NDP_RESPONDER_REQ = 6, + NDP_RESPONDER_RSP = 7, + NDP_END_REQ = 8, + NDP_END_RSP = 9, + NDP_INDICATION = 10, + NDP_CONFIRM = 11, + NDP_END_IND = 12, + NDP_NEW_PEER = 13, + NDP_PEER_DEPARTED = 14, + NDP_SCHEDULE_UPDATE = 15, + NDP_END_ALL = 16, + NDP_HOST_UPDATE = 17, +}; + +/** + * enum nan_datapath_status_type - NDP status type + * @NAN_DATAPATH_RSP_STATUS_SUCCESS: request was successful + * @NAN_DATAPATH_RSP_STATUS_ERROR: request failed + */ +enum nan_datapath_status_type { + NAN_DATAPATH_RSP_STATUS_SUCCESS = 0x00, + NAN_DATAPATH_RSP_STATUS_ERROR = 0x01, +}; + +/** + * enum nan_datapath_reason_code - NDP command rsp reason code value + * @NDP_UNSUPPORTED_CONCURRENCY: Will be used in unsupported concurrency cases + * @NDP_NAN_DATA_IFACE_CREATE_FAILED: ndi create failed + * @NDP_NAN_DATA_IFACE_DELETE_FAILED: ndi delete failed + * @NDP_DATA_INITIATOR_REQ_FAILED: data initiator request failed + * @NDP_DATA_RESPONDER_REQ_FAILED: data responder request failed + * @NDP_INVALID_SERVICE_INSTANCE_ID: invalid service instance id + * @NDP_INVALID_NDP_INSTANCE_ID: invalid ndp instance id + * @NDP_INVALID_RSP_CODE: invalid response code in ndp responder request + * @NDP_INVALID_APP_INFO_LEN: invalid app info length + * @NDP_NMF_REQ_FAIL: OTA nan mgmt frame failure for data request + * @NDP_NMF_RSP_FAIL: OTA nan mgmt frame failure for data response + * @NDP_NMF_CNF_FAIL: OTA nan mgmt frame failure for confirm + * @NDP_END_FAILED: ndp end failed + * @NDP_NMF_END_REQ_FAIL: OTA nan mgmt frame failure for data end + * @NDP_VENDOR_SPECIFIC_ERROR: other vendor specific failures + */ +enum nan_datapath_reason_code { + NAN_DATAPATH_UNSUPPORTED_CONCURRENCY = 9000, + NAN_DATAPATH_NAN_DATA_IFACE_CREATE_FAILED = 9001, + NAN_DATAPATH_NAN_DATA_IFACE_DELETE_FAILED = 9002, + NAN_DATAPATH_DATA_INITIATOR_REQ_FAILED = 9003, + NAN_DATAPATH_DATA_RESPONDER_REQ_FAILED = 9004, + NAN_DATAPATH_INVALID_SERVICE_INSTANCE_ID = 9005, + NAN_DATAPATH_INVALID_NDP_INSTANCE_ID = 9006, + NAN_DATAPATH_INVALID_RSP_CODE = 9007, + NAN_DATAPATH_INVALID_APP_INFO_LEN = 9008, + NAN_DATAPATH_NMF_REQ_FAIL = 9009, + NAN_DATAPATH_NMF_RSP_FAIL = 9010, + NAN_DATAPATH_NMF_CNF_FAIL = 9011, + NAN_DATAPATH_END_FAILED = 9012, + NAN_DATAPATH_NMF_END_REQ_FAIL = 9013, + /* 9500 onwards vendor specific error codes */ + NAN_DATAPATH_VENDOR_SPECIFIC_ERROR = 9500, +}; + +/** + * enum nan_datapath_response_code - responder's response code to nan data path + * request + * @NAN_DATAPATH_RESPONSE_ACCEPT: ndp request accepted + * @NAN_DATAPATH_RESPONSE_REJECT: ndp request rejected + * @NAN_DATAPATH_RESPONSE_DEFER: ndp request deferred until later (response to + * follow any time later) + */ +enum nan_datapath_response_code { + NAN_DATAPATH_RESPONSE_ACCEPT = 0, + NAN_DATAPATH_RESPONSE_REJECT = 1, + NAN_DATAPATH_RESPONSE_DEFER = 2, +}; + +/** + * enum nan_datapath_accept_policy - nan data path accept policy + * @NAN_DATAPATH_ACCEPT_POLICY_NONE: the framework will decide the policy + * @NAN_DATAPATH_ACCEPT_POLICY_ALL: accept policy offloaded to fw + */ +enum nan_datapath_accept_policy { + NAN_DATAPATH_ACCEPT_POLICY_NONE = 0, + NAN_DATAPATH_ACCEPT_POLICY_ALL = 1, +}; + +/** + * enum nan_datapath_self_role - nan data path role + * @NAN_DATAPATH_ROLE_INITIATOR: initiator of nan data path request + * @NAN_DATAPATH_ROLE_RESPONDER: responder to nan data path request + */ +enum nan_datapath_self_role { + NAN_DATAPATH_ROLE_INITIATOR = 0, + NAN_DATAPATH_ROLE_RESPONDER = 1, +}; + +/** + * enum nan_datapath_end_type - NDP end type + * @NAN_DATAPATH_END_TYPE_UNSPECIFIED: type is unspecified + * @NAN_DATAPATH_END_TYPE_PEER_UNAVAILABLE: type is peer unavailable + * @NAN_DATAPATH_END_TYPE_OTA_FRAME: NDP end frame received from peer + */ +enum nan_datapath_end_type { + NAN_DATAPATH_END_TYPE_UNSPECIFIED = 0x00, + NAN_DATAPATH_END_TYPE_PEER_UNAVAILABLE = 0x01, + NAN_DATAPATH_END_TYPE_OTA_FRAME = 0x02, +}; + +/** + * enum nan_datapath_end_reason_code - NDP end reason code + * @NAN_DATAPATH_END_REASON_UNSPECIFIED: reason is unspecified + * @NAN_DATAPATH_END_REASON_INACTIVITY: reason is peer inactivity + * @NAN_DATAPATH_END_REASON_PEER_DATA_END: data end indication received from + * peer + */ +enum nan_datapath_end_reason_code { + NAN_DATAPATH_END_REASON_UNSPECIFIED = 0x00, + NAN_DATAPATH_END_REASON_INACTIVITY = 0x01, + NAN_DATAPATH_END_REASON_PEER_DATA_END = 0x02, +}; + +/** + * enum nan_datapath_state - NAN datapath states + * @NAN_DATA_NDI_CREATING_STATE: NDI create is in progress + * @NAN_DATA_NDI_CREATED_STATE: NDI successfully crated + * @NAN_DATA_NDI_DELETING_STATE: NDI delete is in progress + * @NAN_DATA_NDI_DELETED_STATE: NDI delete is in progress + * @NAN_DATA_PEER_CREATE_STATE: Peer create is in progress + * @NAN_DATA_PEER_DELETE_STATE: Peer delete is in progrss + * @NAN_DATA_CONNECTING_STATE: Data connection in progress + * @NAN_DATA_CONNECTED_STATE: Data connection successful + * @NAN_DATA_END_STATE: NDP end is in progress + * @NAN_DATA_DISCONNECTED_STATE: NDP is in disconnected state + */ +enum nan_datapath_state { + NAN_DATA_INVALID_STATE = -1, + NAN_DATA_NDI_CREATING_STATE = 0, + NAN_DATA_NDI_CREATED_STATE = 1, + NAN_DATA_NDI_DELETING_STATE = 2, + NAN_DATA_NDI_DELETED_STATE = 3, + NAN_DATA_PEER_CREATE_STATE = 4, + NAN_DATA_PEER_DELETE_STATE = 5, + NAN_DATA_CONNECTING_STATE = 6, + NAN_DATA_CONNECTED_STATE = 7, + NAN_DATA_END_STATE = 8, + NAN_DATA_DISCONNECTED_STATE = 9, +}; + +/** + * struct nan_datapath_app_info - application info shared during ndp setup + * @ndp_app_info_len: ndp app info length + * @ndp_app_info: variable length application information + */ +struct nan_datapath_app_info { + uint32_t ndp_app_info_len; + uint8_t ndp_app_info[NDP_APP_INFO_LEN]; +}; + +/** + * struct nan_datapath_cfg - ndp configuration + * @ndp_cfg_len: ndp configuration length + * @ndp_cfg: variable length ndp configuration + */ +struct nan_datapath_cfg { + uint32_t ndp_cfg_len; + uint8_t ndp_cfg[NDP_QOS_INFO_LEN]; +}; + +/** + * struct nan_datapath_pmk - structure to hold pairwise master key + * @pmk_len: length of pairwise master key + * @pmk: buffer containing pairwise master key + */ +struct nan_datapath_pmk { + uint32_t pmk_len; + uint8_t pmk[NDP_PMK_LEN]; +}; + +/** + * struct nan_datapath_scid - structure to hold sceurity context identifier + * @scid_len: length of scid + * @scid: scid + */ +struct nan_datapath_scid { + uint32_t scid_len; + uint8_t scid[NDP_SCID_BUF_LEN]; +}; + +/** + * struct ndp_passphrase - structure to hold passphrase + * @passphrase_len: length of passphrase + * @passphrase: buffer containing passphrase + */ +struct ndp_passphrase { + uint32_t passphrase_len; + uint8_t passphrase[NAN_PASSPHRASE_MAX_LEN]; +}; + +/** + * struct ndp_service_name - structure to hold service_name + * @service_name_len: length of service_name + * @service_name: buffer containing service_name + */ +struct ndp_service_name { + uint32_t service_name_len; + uint8_t service_name[NAN_MAX_SERVICE_NAME_LEN]; +}; + +/** + * struct peer_nan_datapath_map - mapping of NDP instances to peer to VDEV + * @vdev_id: session id of the interface over which ndp is being created + * @peer_ndi_mac_addr: peer NDI mac address + * @num_active_ndp_sessions: number of active NDP sessions on the peer + * @type: NDP end indication type + * @reason_code: NDP end indication reason code + * @ndp_instance_id: NDP instance ID + */ +struct peer_nan_datapath_map { + uint32_t vdev_id; + struct qdf_mac_addr peer_ndi_mac_addr; + uint32_t num_active_ndp_sessions; + enum nan_datapath_end_type type; + enum nan_datapath_end_reason_code reason_code; + uint32_t ndp_instance_id; +}; + +/** + * struct nan_datapath_channel_info - ndp channel and channel bandwidth + * @freq: channel freq in mhz of the ndp connection + * @ch_width: channel width (wmi_channel_width) of the ndp connection + * @nss: nss used for ndp connection + * @mac_id: MAC ID associated with the NDP channel + */ +struct nan_datapath_channel_info { + uint32_t freq; + uint32_t ch_width; + uint32_t nss; + uint8_t mac_id; +}; + +#define NAN_CH_INFO_MAX_LEN \ + (NAN_CH_INFO_MAX_CHANNELS * sizeof(struct nan_datapath_channel_info)) + +/** + * struct nan_datapath_inf_create_req - ndi create request params + * @transaction_id: unique identifier + * @iface_name: interface name + */ +struct nan_datapath_inf_create_req { + uint32_t transaction_id; + char iface_name[IFACE_NAME_SIZE]; +}; + +/** + * struct nan_datapath_inf_create_rsp - ndi create response params + * @status: request status + * @reason: reason if any + */ +struct nan_datapath_inf_create_rsp { + uint32_t status; + uint32_t reason; + uint8_t sta_id; +}; + +/** + * struct nan_datapath_inf_delete_rsp - ndi delete response params + * @status: request status + * @reason: reason if any + */ +struct nan_datapath_inf_delete_rsp { + uint32_t status; + uint32_t reason; +}; + +/** + * struct nan_datapath_initiator_req - ndp initiator request params + * @vdev: pointer to vdev object + * @transaction_id: unique identifier + * @channel: suggested channel for ndp creation + * @channel_cfg: channel config, 0=no channel, 1=optional, 2=mandatory + * @service_instance_id: Service identifier + * @peer_discovery_mac_addr: Peer's discovery mac address + * @self_ndi_mac_addr: self NDI mac address + * @ndp_config: ndp configuration params + * @ndp_info: ndp application info + * @ncs_sk_type: indicates NCS_SK_128 or NCS_SK_256 + * @pmk: pairwise master key + * @passphrase: passphrase + * @service_name: service name + * @is_ipv6_addr_present: indicates if following ipv6 address is valid + * @ipv6_addr: ipv6 address address used by ndp + */ +struct nan_datapath_initiator_req { + struct wlan_objmgr_vdev *vdev; + uint32_t transaction_id; + uint32_t channel; + uint32_t channel_cfg; + uint32_t service_instance_id; + uint32_t ncs_sk_type; + struct qdf_mac_addr peer_discovery_mac_addr; + struct qdf_mac_addr self_ndi_mac_addr; + struct nan_datapath_cfg ndp_config; + struct nan_datapath_app_info ndp_info; + struct nan_datapath_pmk pmk; + struct ndp_passphrase passphrase; + struct ndp_service_name service_name; + bool is_ipv6_addr_present; + uint8_t ipv6_addr[QDF_IPV6_ADDR_SIZE]; +}; + +/** + * struct nan_datapath_initiator_rsp - response event from FW + * @vdev: pointer to vdev object + * @transaction_id: unique identifier + * @ndp_instance_id: locally created NDP instance ID + * @status: status of the ndp request + * @reason: reason for failure if any + */ +struct nan_datapath_initiator_rsp { + struct wlan_objmgr_vdev *vdev; + uint32_t transaction_id; + uint32_t ndp_instance_id; + uint32_t status; + uint32_t reason; +}; + +/** + * struct nan_datapath_responder_req - responder's response to ndp create + * request + * @vdev: pointer to vdev object + * @transaction_id: unique identifier + * @ndp_instance_id: locally created NDP instance ID + * @ndp_rsp: response to the ndp create request + * @ndp_config: ndp configuration params + * @ndp_info: ndp application info + * @pmk: pairwise master key + * @ncs_sk_type: indicates NCS_SK_128 or NCS_SK_256 + * @passphrase: passphrase + * @service_name: service name + * @is_ipv6_addr_present: indicates if following ipv6 address is valid + * @ipv6_addr: ipv6 address address used by ndp + * @is_port_present: indicates if following port is valid + * @port: port specified by for this NDP + * @is_protocol_present: indicates if following protocol is valid + * @protocol: protocol used by this NDP + */ +struct nan_datapath_responder_req { + struct wlan_objmgr_vdev *vdev; + uint32_t transaction_id; + uint32_t ndp_instance_id; + enum nan_datapath_response_code ndp_rsp; + struct nan_datapath_cfg ndp_config; + struct nan_datapath_app_info ndp_info; + struct nan_datapath_pmk pmk; + uint32_t ncs_sk_type; + struct ndp_passphrase passphrase; + struct ndp_service_name service_name; + bool is_ipv6_addr_present; + uint8_t ipv6_addr[QDF_IPV6_ADDR_SIZE]; + bool is_port_present; + uint16_t port; + bool is_protocol_present; + uint8_t protocol; +}; + +/** + * struct nan_datapath_responder_rsp - response to responder's request + * @vdev: pointer to vdev object + * @transaction_id: unique identifier + * @status: command status + * @reason: reason for failure if any + * @peer_mac_addr: Peer's mac address + * @create_peer: Flag to indicate to create peer + */ +struct nan_datapath_responder_rsp { + struct wlan_objmgr_vdev *vdev; + uint32_t transaction_id; + uint32_t status; + uint32_t reason; + struct qdf_mac_addr peer_mac_addr; + bool create_peer; +}; + +/** + * struct nan_datapath_end_req - ndp end request + * @vdev: pointer to vdev object + * @transaction_id: unique transaction identifier + * @num_ndp_instances: number of ndp instances to be terminated + * @ndp_ids: array of ndp_instance_id to be terminated + */ +struct nan_datapath_end_req { + struct wlan_objmgr_vdev *vdev; + uint32_t transaction_id; + uint32_t num_ndp_instances; + uint32_t ndp_ids[NDP_NUM_INSTANCE_ID]; +}; + +/** + * struct nan_datapath_end_all_ndps - Datapath termination request to end all + * NDPs on the given vdev + * @vdev: pointer to vdev object + */ +struct nan_datapath_end_all_ndps { + struct wlan_objmgr_vdev *vdev; +}; + +/** + * enum nan_event_id_types - NAN event ID types + * @nan_event_id_error_rsp: NAN event indicating error + * @nan_event_id_enable_rsp: NAN Enable Response event ID + * @nan_event_id_disable_ind: NAN Disable Indication event ID + * @nan_event_id_generic_rsp: All remaining NAN events, treated as passthrough + */ +enum nan_event_id_types { + nan_event_id_error_rsp = 0, + nan_event_id_enable_rsp, + nan_event_id_disable_ind, + nan_event_id_generic_rsp, +}; + +/** + * struct nan_event_params - NAN event received from the Target + * @psoc: Pointer to the psoc object + * @evt_type: NAN Discovery event type + * @is_nan_enable_success: Status from the NAN Enable Response event + * @mac_id: MAC ID associated with NAN Discovery from NAN Enable Response event + * @vdev_id: vdev id of the interface created for NAN discovery + * @buf_len: Event buffer length + * @buf: Event buffer starts here + */ +struct nan_event_params { + struct wlan_objmgr_psoc *psoc; + enum nan_event_id_types evt_type; + bool is_nan_enable_success; + uint8_t mac_id; + uint8_t vdev_id; + uint32_t buf_len; + /* Variable length, do not add anything after this */ + uint8_t buf[]; +}; + +#define NAN_MSG_ID_DISABLE_INDICATION 26 +/** + * struct nan_msg_hdr - NAN msg header to be sent to userspace + * @msg_version: NAN msg version + * @msg_id: NAN message id + * @reserved: Reserved for now to avoid padding + * + * 8-byte control message header used by NAN + * + */ +struct nan_msg_hdr { + uint16_t msg_version:4; + uint16_t msg_id:12; + uint16_t reserved[3]; +}; + +#define NAN_STATUS_SUCCESS 0 +#define NAN_STATUS_UNSUPPORTED_CONCURRENCY_NAN_DISABLED 12 + +/** + * struct nan_disable_ind_msg - NAN disable ind params + * @msg_hdr: NAN msg header + * @reason: NAN disable reason, below are valid reasons for NAN disable ind + * NAN_STATUS_SUCCESS + * NAN_STATUS_UNSUPPORTED_CONCURRENCY_NAN_DISABLED + * @reserved: Reserved for now to avoid padding + */ +struct nan_disable_ind_msg { + struct nan_msg_hdr msg_hdr; + uint16_t reason; + uint16_t reserved; +}; + +/** + * struct nan_msg_params - NAN request params + * @request_data_len: request data length + * @request_data: request data + * @rtt_cap: indicate if responder/initiator role is supported + */ +struct nan_msg_params { + uint16_t request_data_len; + uint32_t rtt_cap; + /* Variable length, do not add anything after this */ + uint8_t request_data[]; +}; + +/** + * struct nan_generic_req - A NAN request for the Target + * @psoc: Pointer to the psoc object + * @params: NAN request structure containing message fr the Target + */ +struct nan_generic_req { + struct wlan_objmgr_psoc *psoc; + /* Variable length, do not add anything after this */ + struct nan_msg_params params; +}; + +/** + * struct nan_disable_req - NAN request to disable NAN Discovery + * @psoc: Pointer to the psoc object + * @disable_2g_discovery: Flag for disabling Discovery in 2G band + * @disable_5g_discovery: Flag for disabling Discovery in 5G band + * @params: NAN request structure containing message for the target + */ +struct nan_disable_req { + struct wlan_objmgr_psoc *psoc; + bool disable_2g_discovery; + bool disable_5g_discovery; + /* Variable length, do not add anything after this */ + struct nan_msg_params params; +}; + +/** + * struct nan_enable_req - NAN request to enable NAN Discovery + * @psoc: Pointer to the psoc object + * @social_chan_2g_freq: Social channel in 2G band for the NAN Discovery + * @social_chan_5g_freq: Social channel in 5G band for the NAN Discovery + * @params: NAN request structure containing message for the target + */ +struct nan_enable_req { + struct wlan_objmgr_psoc *psoc; + uint32_t social_chan_2g_freq; + uint32_t social_chan_5g_freq; + /* Variable length, do not add anything after this */ + struct nan_msg_params params; +}; + +/** + * struct nan_datapath_end_rsp_event - firmware response to ndp end request + * @vdev: pointer to vdev object + * @transaction_id: unique identifier for the request + * @status: status of operation + * @reason: reason(opaque to host driver) + */ +struct nan_datapath_end_rsp_event { + struct wlan_objmgr_vdev *vdev; + uint32_t transaction_id; + uint32_t status; + uint32_t reason; +}; + +/** + * struct nan_datapath_end_indication_event - ndp termination notification from + * FW + * @vdev: pointer to vdev object + * @num_ndp_ids: number of NDP ids + * @ndp_map: mapping of NDP instances to peer and vdev + */ +struct nan_datapath_end_indication_event { + struct wlan_objmgr_vdev *vdev; + uint32_t num_ndp_ids; + struct peer_nan_datapath_map ndp_map[]; +}; + +/** + * struct nan_datapath_peer_ind - ndp peer indication + * @msg: msg received by FW + * @data_len: data length + * + */ +struct nan_dump_msg { + uint8_t *msg; + uint32_t data_len; +}; + +/** + * struct nan_datapath_confirm_event - ndp confirmation event from FW + * @vdev: pointer to vdev object + * @ndp_instance_id: ndp instance id for which confirm is being generated + * @reason_code : reason code(opaque to driver) + * @num_active_ndps_on_peer: number of ndp instances on peer + * @peer_ndi_mac_addr: peer NDI mac address + * @rsp_code: ndp response code + * @num_channels: num channels + * @ch: channel info struct array + * @ndp_info: ndp application info + * @is_ipv6_addr_present: indicates if following ipv6 address is valid + * @ipv6_addr: ipv6 address address used by ndp + * @is_port_present: indicates if following port is valid + * @port: port specified by for this NDP + * @is_protocol_present: indicates if following protocol is valid + * @protocol: protocol used by this NDP + */ +struct nan_datapath_confirm_event { + struct wlan_objmgr_vdev *vdev; + uint32_t ndp_instance_id; + uint32_t reason_code; + uint32_t num_active_ndps_on_peer; + struct qdf_mac_addr peer_ndi_mac_addr; + enum nan_datapath_response_code rsp_code; + uint32_t num_channels; + struct nan_datapath_channel_info ch[NAN_CH_INFO_MAX_CHANNELS]; + struct nan_datapath_app_info ndp_info; + bool is_ipv6_addr_present; + uint8_t ipv6_addr[QDF_IPV6_ADDR_SIZE]; + bool is_port_present; + uint16_t port; + bool is_protocol_present; + uint8_t protocol; +}; + +/** + * struct nan_datapath_indication_event - create ndp indication on the responder + * @vdev: pointer to vdev object + * @service_instance_id: Service identifier + * @peer_discovery_mac_addr: Peer's discovery mac address + * @peer_mac_addr: Peer's NDI mac address + * @ndp_initiator_mac_addr: NDI mac address of the peer initiating NDP + * @ndp_instance_id: locally created NDP instance ID + * @role: self role for NDP + * @ndp_accept_policy: accept policy configured by the upper layer + * @ndp_config: ndp configuration params + * @ndp_info: ndp application info + * @ncs_sk_type: indicates NCS_SK_128 or NCS_SK_256 + * @scid: security context identifier + * @is_ipv6_addr_present: indicates if following ipv6 address is valid + * @ipv6_addr: ipv6 address address used by ndp + */ +struct nan_datapath_indication_event { + struct wlan_objmgr_vdev *vdev; + uint32_t service_instance_id; + struct qdf_mac_addr peer_discovery_mac_addr; + struct qdf_mac_addr peer_mac_addr; + uint32_t ndp_instance_id; + enum nan_datapath_self_role role; + enum nan_datapath_accept_policy policy; + struct nan_datapath_cfg ndp_config; + struct nan_datapath_app_info ndp_info; + uint32_t ncs_sk_type; + struct nan_datapath_scid scid; + bool is_ipv6_addr_present; + uint8_t ipv6_addr[QDF_IPV6_ADDR_SIZE]; +}; + +/** + * struct nan_datapath_peer_ind - ndp peer indication + * @peer_mac_addr: peer mac address + * @sta_id: station id + * + */ +struct nan_datapath_peer_ind { + struct qdf_mac_addr peer_mac_addr; + uint16_t sta_id; +}; + +/** + * struct nan_datapath_sch_update_event - ndp schedule update indication + * @vdev: vdev schedule update was received + * @peer_addr: peer for which schedule update was received + * @flags: reason for sch update (opaque to driver) + * @num_channels: num of channels + * @num_ndp_instances: num of ndp instances + * @ch: channel info array + * @ndp_instances: array of ndp instances + */ +struct nan_datapath_sch_update_event { + struct wlan_objmgr_vdev *vdev; + struct qdf_mac_addr peer_addr; + uint32_t flags; + uint32_t num_channels; + uint32_t num_ndp_instances; + struct nan_datapath_channel_info ch[NAN_CH_INFO_MAX_CHANNELS]; + uint32_t ndp_instances[NDP_NUM_INSTANCE_ID]; +}; + +/** + * struct nan_datapath_host_event - ndp host event parameters + * @vdev: vdev obj associated with the ndp + * @ndp_termination_in_progress: flag that indicates whether NDPs associated + * with the given vdev are being terminated + */ +struct nan_datapath_host_event { + struct wlan_objmgr_vdev *vdev; + bool ndp_termination_in_progress; +}; + +/** + * struct nan_callbacks - struct containing callback to non-converged driver + * @os_if_nan_event_handler: OS IF Callback for handling NAN Discovery events + * @os_if_ndp_event_handler: OS IF Callback for handling NAN Datapath events + * @ucfg_nan_request_process_cb: Callback to indicate NAN enable/disable + * request processing is complete + * @ndi_open: HDD callback for creating the NAN Datapath Interface + * @ndi_start: HDD callback for starting the NAN Datapath Interface + * @ndi_close: HDD callback for closing the NAN Datapath Interface + * @ndi_delete: HDD callback for deleting the NAN Datapath Interface + * @drv_ndi_create_rsp_handler: HDD callback for handling NDI interface creation + * @drv_ndi_delete_rsp_handler: HDD callback for handling NDI interface deletion + * @new_peer_ind: HDD callback for handling new NDP peer + * @add_ndi_peer: LIM callback for adding NDP peer + * @peer_departed_ind: HDD callback for handling departing of NDP peer + * @ndp_delete_peers: LIM callback for deleting NDP peer + * @delete_peers_by_addr: LIM callback for deleting peer by MAC address + * @update_ndi_conn: WMA callback to update NDI's connection info + */ +struct nan_callbacks { + /* callback to os_if layer from umac */ + void (*os_if_nan_event_handler)(struct nan_event_params *nan_event); + void (*os_if_ndp_event_handler)(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + uint32_t type, void *msg); + void (*ucfg_nan_request_process_cb)(void *cookie); + int (*ndi_open)(char *iface_name); + int (*ndi_start)(char *iface_name, uint16_t); + void (*ndi_close)(uint8_t); + int (*ndi_delete)(uint8_t, char *iface_name, uint16_t transaction_id); + void (*drv_ndi_create_rsp_handler) + (uint8_t, struct nan_datapath_inf_create_rsp *); + void (*drv_ndi_delete_rsp_handler)(uint8_t); + int (*new_peer_ind)(uint8_t, uint16_t, struct qdf_mac_addr *, bool); + QDF_STATUS (*add_ndi_peer)(uint32_t, struct qdf_mac_addr); + void (*peer_departed_ind)(uint8_t, uint16_t, struct qdf_mac_addr *, + bool); + void (*ndp_delete_peers)(struct peer_nan_datapath_map*, uint8_t); + void (*delete_peers_by_addr)(uint8_t, struct qdf_mac_addr); + QDF_STATUS (*update_ndi_conn)(uint8_t vdev_id, + struct nan_datapath_channel_info + *chan_info); +}; + +/** + * struct wlan_nan_tx_ops - structure of tx function pointers for nan component + * @nan_discovery_req_tx: Msg handler for TX operations for the NAN Discovery + * @nan_datapath_req_tx: Msg handler for TX operations for the NAN Datapath + */ +struct wlan_nan_tx_ops { + QDF_STATUS (*nan_discovery_req_tx)(void *nan_req, uint32_t req_type); + QDF_STATUS (*nan_datapath_req_tx)(void *req, uint32_t req_id); +}; + +/** + * struct wlan_nan_rx_ops - structure of rx function pointers for nan component + * @nan_discovery_event_rx: Evt handler for RX operations for the NAN Discovery + * @nan_datapath_event_rx: Evt handler for RX operations for the NAN Datapath + */ +struct wlan_nan_rx_ops { + QDF_STATUS (*nan_discovery_event_rx)(struct scheduler_msg *event); + QDF_STATUS (*nan_datapath_event_rx)(struct scheduler_msg *event); +}; + +/** + * struct nan_tgt_caps - NAN Target capabilities + * @nan_disable_supported: Target supports disabling NAN Discovery + * @nan_dbs_supported: Target supports NAN Discovery with DBS + * @ndi_dbs_supported: Target supports NAN Datapath with DBS + * @nan_sap_supported: Target supports NAN Discovery with SAP concurrency + * @ndi_sap_supported: Target supports NAN Datapth with SAP concurrency + * @nan_vdev_allowed: Allow separate vdev creation for NAN discovery + * @sta_nan_ndi_ndi_allowed: 4 port concurrency of STA+NAN+NDI+NDI is supported + * by Fw or not. + */ +struct nan_tgt_caps { + uint32_t nan_disable_supported:1; + uint32_t nan_dbs_supported:1; + uint32_t ndi_dbs_supported:1; + uint32_t nan_sap_supported:1; + uint32_t ndi_sap_supported:1; + uint32_t nan_vdev_allowed:1; + uint32_t sta_nan_ndi_ndi_allowed:1; +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/components/nan/core/inc/wlan_nan_api.h b/drivers/staging/qcacld-3.0/components/nan/core/inc/wlan_nan_api.h new file mode 100644 index 0000000000000000000000000000000000000000..9cba0fffd401aac67fd9be08aaabf5a6fe427c5f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/core/inc/wlan_nan_api.h @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains nan definitions exposed to other modules + */ + +#ifndef _WLAN_NAN_API_H_ +#define _WLAN_NAN_API_H_ + +#include "wlan_objmgr_peer_obj.h" +#include "wlan_policy_mgr_public_struct.h" +#include "qdf_status.h" + +#ifdef WLAN_FEATURE_NAN + +#include "../src/nan_main_i.h" + +struct wlan_objmgr_psoc; + +/** + * nan_init: initializes NAN component, called by dispatcher init + * + * Return: status of operation + */ +QDF_STATUS nan_init(void); + +/** + * nan_deinit: de-initializes NAN component, called by dispatcher init + * + * Return: status of operation + */ +QDF_STATUS nan_deinit(void); + +/** + * nan_psoc_enable: psoc enable API for NANitioning component + * @psoc: pointer to PSOC + * + * Return: status of operation + */ +QDF_STATUS nan_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * nan_psoc_disable: psoc disable API for NANitioning component + * @psoc: pointer to PSOC + * + * Return: status of operation + */ +QDF_STATUS nan_psoc_disable(struct wlan_objmgr_psoc *psoc); + +/** + * nan_get_peer_priv_obj: get NAN priv object from peer object + * @peer: pointer to peer object + * + * Return: pointer to NAN peer private object + */ +static inline +struct nan_peer_priv_obj *nan_get_peer_priv_obj(struct wlan_objmgr_peer *peer) +{ + struct nan_peer_priv_obj *obj; + + if (!peer) { + nan_err("peer is null"); + return NULL; + } + obj = wlan_objmgr_peer_get_comp_private_obj(peer, WLAN_UMAC_COMP_NAN); + + return obj; +} + +/** + * nan_get_vdev_priv_obj: get NAN priv object from vdev object + * @vdev: pointer to vdev object + * + * Return: pointer to NAN vdev private object + */ +static inline +struct nan_vdev_priv_obj *nan_get_vdev_priv_obj(struct wlan_objmgr_vdev *vdev) +{ + struct nan_vdev_priv_obj *obj; + + if (!vdev) { + nan_err("vdev is null"); + return NULL; + } + obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, WLAN_UMAC_COMP_NAN); + + return obj; +} + +/** + * nan_get_psoc_priv_obj: get NAN priv object from psoc object + * @psoc: pointer to psoc object + * + * Return: pointer to NAN psoc private object + */ +static inline +struct nan_psoc_priv_obj *nan_get_psoc_priv_obj(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *obj; + + if (!psoc) { + nan_err("psoc is null"); + return NULL; + } + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_NAN); + + return obj; +} + +/** + * nan_psoc_get_tx_ops: get TX ops from the NAN private object + * @psoc: pointer to psoc object + * + * Return: pointer to TX op callback + */ +static inline +struct wlan_nan_tx_ops *nan_psoc_get_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *nan_priv; + + if (!psoc) { + nan_err("psoc is null"); + return NULL; + } + + nan_priv = nan_get_psoc_priv_obj(psoc); + if (!nan_priv) { + nan_err("psoc private object is null"); + return NULL; + } + + return &nan_priv->tx_ops; +} + +/** + * nan_psoc_get_rx_ops: get RX ops from the NAN private object + * @psoc: pointer to psoc object + * + * Return: pointer to RX op callback + */ +static inline +struct wlan_nan_rx_ops *nan_psoc_get_rx_ops(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *nan_priv; + + if (!psoc) { + nan_err("psoc is null"); + return NULL; + } + + nan_priv = nan_get_psoc_priv_obj(psoc); + if (!nan_priv) { + nan_err("psoc private object is null"); + return NULL; + } + + return &nan_priv->rx_ops; +} + +/** + * wlan_nan_get_connection_info: Get NAN related connection info + * @psoc: pointer to psoc object + * @conn_info: Coonection info structure pointer + * + * Return: status of operation + */ +QDF_STATUS +wlan_nan_get_connection_info(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_vdev_entry_info *conn_info); + +/** + * wlan_nan_get_disc_5g_ch_freq: Get NAN Disc 5G channel frequency + * @psoc: pointer to psoc object + * + * Return: NAN Disc 5G channel frequency + */ +uint32_t wlan_nan_get_disc_5g_ch_freq(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_nan_get_sap_conc_support: Get NAN+SAP conc support + * @psoc: pointer to psoc object + * + * Return: True if NAN+SAP supported else false + */ +bool wlan_nan_get_sap_conc_support(struct wlan_objmgr_psoc *psoc); + +/** + * nan_disable_cleanup: Cleanup NAN state upon NAN disable + * @psoc: pointer to psoc object + * + * Return: Cleanup NAN state upon NAN disable + */ +QDF_STATUS nan_disable_cleanup(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_is_nan_allowed_on_freq() - Check if NAN is allowed on given freq + * @pdev: pdev context + * @freq: Frequency to be checked + * + * Check if NAN/NDP can be enabled on given frequency. + * + * Return: True if NAN is allowed on the given frequency + */ +bool wlan_is_nan_allowed_on_freq(struct wlan_objmgr_pdev *pdev, uint32_t freq); + +#else /* WLAN_FEATURE_NAN */ +static inline QDF_STATUS nan_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS nan_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS nan_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS nan_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +wlan_nan_get_connection_info(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_vdev_entry_info *conn_info) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline uint32_t +wlan_nan_get_disc_5g_ch_freq(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline +bool wlan_nan_get_sap_conc_support(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline +QDF_STATUS nan_disable_cleanup(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline +bool wlan_is_nan_allowed_on_freq(struct wlan_objmgr_pdev *pdev, uint32_t freq) +{ + return false; +} +#endif /* WLAN_FEATURE_NAN */ +#endif /* _WLAN_NAN_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/nan/core/src/nan_api.c b/drivers/staging/qcacld-3.0/components/nan/core/src/nan_api.c new file mode 100644 index 0000000000000000000000000000000000000000..64c3bd3aaaf5a838a123e29a5d017d30343de424 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/core/src/nan_api.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains nan public API function definitions + */ + +#include "nan_main_i.h" +#include "wlan_nan_api.h" +#include "target_if_nan.h" +#include "nan_public_structs.h" +#include "wlan_objmgr_cmn.h" +#include "wlan_objmgr_global_obj.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_vdev_obj.h" +#include "nan_ucfg_api.h" +#include + +static QDF_STATUS nan_psoc_obj_created_notification( + struct wlan_objmgr_psoc *psoc, void *arg_list) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct nan_psoc_priv_obj *nan_obj; + + nan_debug("nan_psoc_create_notif called"); + nan_obj = qdf_mem_malloc(sizeof(*nan_obj)); + if (!nan_obj) { + nan_alert("malloc failed for nan prv obj"); + return QDF_STATUS_E_NOMEM; + } + + qdf_spinlock_create(&nan_obj->lock); + status = wlan_objmgr_psoc_component_obj_attach(psoc, WLAN_UMAC_COMP_NAN, + nan_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + nan_alert("obj attach with psoc failed"); + goto nan_psoc_notif_failed; + } + + target_if_nan_register_tx_ops(&nan_obj->tx_ops); + target_if_nan_register_rx_ops(&nan_obj->rx_ops); + + return QDF_STATUS_SUCCESS; + +nan_psoc_notif_failed: + + qdf_spinlock_destroy(&nan_obj->lock); + qdf_mem_free(nan_obj); + return status; +} + +static QDF_STATUS nan_psoc_obj_destroyed_notification( + struct wlan_objmgr_psoc *psoc, void *arg_list) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct nan_psoc_priv_obj *nan_obj = nan_get_psoc_priv_obj(psoc); + + nan_debug("nan_psoc_delete_notif called"); + if (!nan_obj) { + nan_err("nan_obj is NULL"); + return QDF_STATUS_E_FAULT; + } + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_NAN, + nan_obj); + if (QDF_IS_STATUS_ERROR(status)) + nan_err("nan_obj detach failed"); + + nan_debug("nan_obj deleted with status %d", status); + qdf_spinlock_destroy(&nan_obj->lock); + qdf_mem_free(nan_obj); + + return status; +} + +static QDF_STATUS nan_vdev_obj_created_notification( + struct wlan_objmgr_vdev *vdev, void *arg_list) +{ + struct nan_vdev_priv_obj *nan_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_objmgr_psoc *psoc; + + nan_debug("nan_vdev_create_notif called"); + if (ucfg_is_nan_vdev(vdev)) { + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + nan_err("psoc is NULL"); + return QDF_STATUS_E_INVAL; + } + target_if_nan_set_vdev_feature_config(psoc, + wlan_vdev_get_id(vdev)); + } + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_NDI_MODE) { + nan_debug("not a ndi vdev. do nothing"); + return QDF_STATUS_SUCCESS; + } + + nan_obj = qdf_mem_malloc(sizeof(*nan_obj)); + if (!nan_obj) { + nan_err("malloc failed for nan prv obj"); + return QDF_STATUS_E_NOMEM; + } + + qdf_spinlock_create(&nan_obj->lock); + status = wlan_objmgr_vdev_component_obj_attach(vdev, WLAN_UMAC_COMP_NAN, + (void *)nan_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + nan_alert("obj attach with vdev failed"); + goto nan_vdev_notif_failed; + } + + return QDF_STATUS_SUCCESS; + +nan_vdev_notif_failed: + + qdf_spinlock_destroy(&nan_obj->lock); + qdf_mem_free(nan_obj); + return status; +} + +static QDF_STATUS nan_vdev_obj_destroyed_notification( + struct wlan_objmgr_vdev *vdev, void *arg_list) +{ + struct nan_vdev_priv_obj *nan_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + nan_debug("nan_vdev_delete_notif called"); + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_NDI_MODE) { + nan_debug("not a ndi vdev. do nothing"); + return QDF_STATUS_SUCCESS; + } + + nan_obj = nan_get_vdev_priv_obj(vdev); + if (!nan_obj) { + nan_err("nan_obj is NULL"); + return QDF_STATUS_E_FAULT; + } + + status = wlan_objmgr_vdev_component_obj_detach(vdev, WLAN_UMAC_COMP_NAN, + nan_obj); + if (QDF_IS_STATUS_ERROR(status)) + nan_err("nan_obj detach failed"); + + nan_debug("nan_obj deleted with status %d", status); + qdf_spinlock_destroy(&nan_obj->lock); + qdf_mem_free(nan_obj); + + return status; +} + +/** + * nan_peer_obj_created_notification() - Handler for peer object creation + * notification event + * @peer: Pointer to the PEER Object + * @arg_list: Pointer to private argument - NULL + * + * This function gets called from object manager when peer is being + * created. + * + * Return: QDF_STATUS + */ +static QDF_STATUS nan_peer_obj_created_notification( + struct wlan_objmgr_peer *peer, void *arg_list) +{ + struct nan_peer_priv_obj *nan_peer_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + nan_peer_obj = qdf_mem_malloc(sizeof(*nan_peer_obj)); + if (!nan_peer_obj) { + nan_err("malloc failed for nan prv obj"); + return QDF_STATUS_E_NOMEM; + } + + qdf_spinlock_create(&nan_peer_obj->lock); + status = wlan_objmgr_peer_component_obj_attach(peer, WLAN_UMAC_COMP_NAN, + (void *)nan_peer_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + nan_alert("obj attach with peer failed"); + goto nan_peer_notif_failed; + } + + return QDF_STATUS_SUCCESS; + +nan_peer_notif_failed: + + qdf_spinlock_destroy(&nan_peer_obj->lock); + qdf_mem_free(nan_peer_obj); + return status; +} + +/** + * nan_peer_obj_destroyed_notification() - Handler for peer object deletion + * notification event + * @peer: Pointer to the PEER Object + * @arg_list: Pointer to private argument - NULL + * + * This function gets called from object manager when peer is being destroyed. + * + * Return: QDF_STATUS + */ +static QDF_STATUS nan_peer_obj_destroyed_notification( + struct wlan_objmgr_peer *peer, void *arg_list) +{ + struct nan_peer_priv_obj *nan_peer_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + nan_peer_obj = nan_get_peer_priv_obj(peer); + if (!nan_peer_obj) { + nan_err("nan_peer_obj is NULL"); + return QDF_STATUS_E_FAULT; + } + + status = wlan_objmgr_peer_component_obj_detach(peer, WLAN_UMAC_COMP_NAN, + nan_peer_obj); + if (QDF_IS_STATUS_ERROR(status)) + nan_err("nan_peer_obj detach failed"); + + nan_debug("nan_peer_obj deleted with status %d", status); + qdf_spinlock_destroy(&nan_peer_obj->lock); + qdf_mem_free(nan_peer_obj); + + return status; +} + +QDF_STATUS nan_init(void) +{ + QDF_STATUS status; + + /* register psoc create handler functions. */ + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_NAN, + nan_psoc_obj_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_register_psoc_create_handler failed"); + return status; + } + + /* register psoc delete handler functions. */ + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_NAN, + nan_psoc_obj_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_register_psoc_destroy_handler failed"); + goto err_psoc_destroy_reg; + } + + /* register vdev create handler functions. */ + status = wlan_objmgr_register_vdev_create_handler( + WLAN_UMAC_COMP_NAN, + nan_vdev_obj_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_register_psoc_create_handler failed"); + goto err_vdev_create_reg; + } + + /* register vdev delete handler functions. */ + status = wlan_objmgr_register_vdev_destroy_handler( + WLAN_UMAC_COMP_NAN, + nan_vdev_obj_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_register_psoc_destroy_handler failed"); + goto err_vdev_destroy_reg; + } + + /* register peer create handler functions. */ + status = wlan_objmgr_register_peer_create_handler( + WLAN_UMAC_COMP_NAN, + nan_peer_obj_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_register_peer_create_handler failed"); + goto err_peer_create_reg; + } + + /* register peer delete handler functions. */ + status = wlan_objmgr_register_peer_destroy_handler( + WLAN_UMAC_COMP_NAN, + nan_peer_obj_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) + nan_err("wlan_objmgr_register_peer_destroy_handler failed"); + else + return QDF_STATUS_SUCCESS; + + wlan_objmgr_unregister_peer_create_handler(WLAN_UMAC_COMP_NAN, + nan_peer_obj_created_notification, + NULL); +err_peer_create_reg: + wlan_objmgr_unregister_vdev_destroy_handler(WLAN_UMAC_COMP_NAN, + nan_vdev_obj_destroyed_notification, + NULL); +err_vdev_destroy_reg: + wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_NAN, + nan_vdev_obj_created_notification, + NULL); +err_vdev_create_reg: + wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_NAN, + nan_psoc_obj_destroyed_notification, + NULL); +err_psoc_destroy_reg: + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_NAN, + nan_psoc_obj_created_notification, + NULL); + + return status; +} + +QDF_STATUS nan_deinit(void) +{ + QDF_STATUS ret = QDF_STATUS_SUCCESS, status; + + /* register psoc create handler functions. */ + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_NAN, + nan_psoc_obj_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_unregister_psoc_create_handler failed"); + ret = status; + } + + /* register vdev create handler functions. */ + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_NAN, + nan_psoc_obj_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_deregister_psoc_destroy_handler failed"); + ret = status; + } + + /* de-register vdev create handler functions. */ + status = wlan_objmgr_unregister_vdev_create_handler( + WLAN_UMAC_COMP_NAN, + nan_vdev_obj_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_unregister_psoc_create_handler failed"); + ret = status; + } + + /* de-register vdev delete handler functions. */ + status = wlan_objmgr_unregister_vdev_destroy_handler( + WLAN_UMAC_COMP_NAN, + nan_vdev_obj_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_deregister_psoc_destroy_handler failed"); + ret = status; + } + + /* de-register peer create handler functions. */ + status = wlan_objmgr_unregister_peer_create_handler( + WLAN_UMAC_COMP_NAN, + nan_peer_obj_created_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_unregister_peer_create_handler failed"); + ret = status; + } + + /* de-register peer delete handler functions. */ + status = wlan_objmgr_unregister_peer_destroy_handler( + WLAN_UMAC_COMP_NAN, + nan_peer_obj_destroyed_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("wlan_objmgr_deregister_peer_destroy_handler failed"); + ret = status; + } + + return ret; +} + +QDF_STATUS nan_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status = target_if_nan_register_events(psoc); + + if (QDF_IS_STATUS_ERROR(status)) + nan_err("target_if_nan_register_events failed"); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS nan_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status = target_if_nan_deregister_events(psoc); + + if (QDF_IS_STATUS_ERROR(status)) + nan_err("target_if_nan_deregister_events failed"); + + return QDF_STATUS_SUCCESS; +} + +bool wlan_is_nan_allowed_on_freq(struct wlan_objmgr_pdev *pdev, uint32_t freq) +{ + bool nan_allowed = true; + + /* Check for SRD channels */ + if (wlan_reg_is_etsi13_srd_chan_for_freq(pdev, freq)) + wlan_mlme_get_srd_master_mode_for_vdev(wlan_pdev_get_psoc(pdev), + QDF_NAN_DISC_MODE, + &nan_allowed); + + /* Check for Indoor channels */ + if (wlan_reg_is_freq_indoor(pdev, freq)) + wlan_mlme_get_indoor_support_for_nan(wlan_pdev_get_psoc(pdev), + &nan_allowed); + /* Check for dfs only if channel is not indoor */ + else if (wlan_reg_is_dfs_for_freq(pdev, freq)) + nan_allowed = false; + + return nan_allowed; +} diff --git a/drivers/staging/qcacld-3.0/components/nan/core/src/nan_main.c b/drivers/staging/qcacld-3.0/components/nan/core/src/nan_main.c new file mode 100644 index 0000000000000000000000000000000000000000..4e806a8a10a6da06a1ff1d2b4d63d5c51f4b4bac --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/core/src/nan_main.c @@ -0,0 +1,1398 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains core nan function definitions + */ + +#include "wlan_utility.h" +#include "nan_ucfg_api.h" +#include "wlan_nan_api.h" +#include "target_if_nan.h" +#include "scheduler_api.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_osif_request_manager.h" +#include "wlan_serialization_api.h" +#include "wlan_objmgr_cmn.h" +#include "wlan_tdls_ucfg_api.h" +#include "wlan_objmgr_global_obj.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_vdev_obj.h" +#include "qdf_platform.h" +#include "wlan_osif_request_manager.h" + +QDF_STATUS nan_set_discovery_state(struct wlan_objmgr_psoc *psoc, + enum nan_disc_state new_state) +{ + enum nan_disc_state cur_state; + struct nan_psoc_priv_obj *psoc_priv = nan_get_psoc_priv_obj(psoc); + bool nan_state_change_allowed = false; + QDF_STATUS status = QDF_STATUS_E_INVAL; + + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock_bh(&psoc_priv->lock); + cur_state = psoc_priv->disc_state; + if (cur_state == new_state) { + qdf_spin_unlock_bh(&psoc_priv->lock); + nan_err("curr_state: %u and new state: %u are same", + cur_state, new_state); + return status; + } + + switch (new_state) { + case NAN_DISC_DISABLED: + nan_state_change_allowed = true; + break; + case NAN_DISC_ENABLE_IN_PROGRESS: + if (cur_state == NAN_DISC_DISABLED) + nan_state_change_allowed = true; + break; + case NAN_DISC_ENABLED: + if (cur_state == NAN_DISC_ENABLE_IN_PROGRESS) + nan_state_change_allowed = true; + break; + case NAN_DISC_DISABLE_IN_PROGRESS: + if (cur_state == NAN_DISC_ENABLE_IN_PROGRESS || + cur_state == NAN_DISC_ENABLED) + nan_state_change_allowed = true; + break; + default: + break; + } + + if (nan_state_change_allowed) { + psoc_priv->disc_state = new_state; + status = QDF_STATUS_SUCCESS; + } + + qdf_spin_unlock_bh(&psoc_priv->lock); + + nan_debug("NAN State transitioned from %d -> %d", cur_state, + psoc_priv->disc_state); + + return status; +} + +enum nan_disc_state nan_get_discovery_state(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_priv = nan_get_psoc_priv_obj(psoc); + + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return NAN_DISC_DISABLED; + } + + return psoc_priv->disc_state; +} + +void nan_release_cmd(void *in_req, uint32_t cmdtype) +{ + struct wlan_objmgr_vdev *vdev = NULL; + + if (!in_req) + return; + + switch (cmdtype) { + case WLAN_SER_CMD_NDP_INIT_REQ: { + struct nan_datapath_initiator_req *req = in_req; + + vdev = req->vdev; + break; + } + case WLAN_SER_CMD_NDP_RESP_REQ: { + struct nan_datapath_responder_req *req = in_req; + + vdev = req->vdev; + break; + } + case WLAN_SER_CMD_NDP_DATA_END_INIT_REQ: { + struct nan_datapath_end_req *req = in_req; + + vdev = req->vdev; + break; + } + case WLAN_SER_CMD_NDP_END_ALL_REQ: { + struct nan_datapath_end_all_ndps *req = in_req; + + vdev = req->vdev; + break; + } + default: + nan_err("invalid req type: %d", cmdtype); + break; + } + + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + else + nan_err("vdev is null"); + + qdf_mem_free(in_req); +} + +static void nan_req_incomplete(void *req, uint32_t cmdtype) +{ + /* send msg to userspace if needed that cmd got incomplete */ +} + +static void nan_req_activated(void *in_req, uint32_t cmdtype) +{ + uint32_t req_type; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct wlan_nan_tx_ops *tx_ops; + struct nan_psoc_priv_obj *psoc_nan_obj; + + switch (cmdtype) { + case WLAN_SER_CMD_NDP_INIT_REQ: { + struct nan_datapath_initiator_req *req = in_req; + + vdev = req->vdev; + req_type = NDP_INITIATOR_REQ; + break; + } + case WLAN_SER_CMD_NDP_RESP_REQ: { + struct nan_datapath_responder_req *req = in_req; + + vdev = req->vdev; + req_type = NDP_RESPONDER_REQ; + break; + } + case WLAN_SER_CMD_NDP_DATA_END_INIT_REQ: { + struct nan_datapath_end_req *req = in_req; + + vdev = req->vdev; + req_type = NDP_END_REQ; + break; + } + case WLAN_SER_CMD_NDP_END_ALL_REQ: { + struct nan_datapath_end_all_ndps *req = in_req; + + vdev = req->vdev; + req_type = NDP_END_ALL; + break; + } + default: + nan_alert("in correct cmdtype: %d", cmdtype); + return; + } + + if (!vdev) { + nan_alert("vdev is null"); + return; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + nan_alert("psoc is null"); + return; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return; + } + + tx_ops = &psoc_nan_obj->tx_ops; + if (!tx_ops) { + nan_alert("tx_ops is null"); + return; + } + + /* send ndp_intiator_req/responder_req/end_req to FW */ + tx_ops->nan_datapath_req_tx(in_req, req_type); +} + +static QDF_STATUS nan_serialized_cb(struct wlan_serialization_command *ser_cmd, + enum wlan_serialization_cb_reason reason) +{ + void *req; + + if (!ser_cmd || !ser_cmd->umac_cmd) { + nan_alert("cmd or umac_cmd is null"); + return QDF_STATUS_E_NULL_VALUE; + } + req = ser_cmd->umac_cmd; + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + nan_req_activated(req, ser_cmd->cmd_type); + break; + case WLAN_SER_CB_CANCEL_CMD: + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + nan_req_incomplete(req, ser_cmd->cmd_type); + break; + case WLAN_SER_CB_RELEASE_MEM_CMD: + nan_release_cmd(req, ser_cmd->cmd_type); + break; + default: + /* Do nothing but logging */ + nan_alert("invalid serialized cb reason: %d", reason); + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS nan_scheduled_msg_handler(struct scheduler_msg *msg) +{ + enum wlan_serialization_status status = 0; + struct wlan_serialization_command cmd = {0}; + + if (!msg || !msg->bodyptr) { + nan_alert("msg or bodyptr is null"); + return QDF_STATUS_E_NULL_VALUE; + } + switch (msg->type) { + case NDP_INITIATOR_REQ: { + struct nan_datapath_initiator_req *req = msg->bodyptr; + + cmd.cmd_type = WLAN_SER_CMD_NDP_INIT_REQ; + cmd.vdev = req->vdev; + break; + } + case NDP_RESPONDER_REQ: { + struct nan_datapath_responder_req *req = msg->bodyptr; + + cmd.cmd_type = WLAN_SER_CMD_NDP_RESP_REQ; + cmd.vdev = req->vdev; + break; + } + case NDP_END_REQ: { + struct nan_datapath_end_req *req = msg->bodyptr; + + cmd.cmd_type = WLAN_SER_CMD_NDP_DATA_END_INIT_REQ; + cmd.vdev = req->vdev; + break; + } + case NDP_END_ALL: { + struct nan_datapath_end_all_ndps *req = msg->bodyptr; + + cmd.cmd_type = WLAN_SER_CMD_NDP_END_ALL_REQ; + cmd.vdev = req->vdev; + break; + } + default: + nan_err("wrong request type: %d", msg->type); + return QDF_STATUS_E_INVAL; + } + + /* TBD - support more than one req of same type or avoid */ + cmd.cmd_id = 0; + cmd.cmd_cb = nan_serialized_cb; + cmd.umac_cmd = msg->bodyptr; + cmd.source = WLAN_UMAC_COMP_NAN; + cmd.is_high_priority = false; + cmd.cmd_timeout_duration = NAN_SER_CMD_TIMEOUT; + nan_debug("cmd_type: %d", cmd.cmd_type); + cmd.is_blocking = true; + + status = wlan_serialization_request(&cmd); + /* following is TBD */ + if (status != WLAN_SER_CMD_ACTIVE && status != WLAN_SER_CMD_PENDING) { + nan_err("unable to serialize command"); + wlan_objmgr_vdev_release_ref(cmd.vdev, WLAN_NAN_ID); + return QDF_STATUS_E_INVAL; + } + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +nan_increment_ndp_sessions(struct wlan_objmgr_psoc *psoc, + struct qdf_mac_addr *peer_ndi_mac, + struct nan_datapath_channel_info *ndp_chan_info) +{ + struct wlan_objmgr_peer *peer; + struct nan_peer_priv_obj *peer_nan_obj; + + peer = wlan_objmgr_get_peer_by_mac(psoc, + peer_ndi_mac->bytes, + WLAN_NAN_ID); + + if (!peer) { + nan_err("peer object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + peer_nan_obj = nan_get_peer_priv_obj(peer); + if (!peer_nan_obj) { + nan_err("peer_nan_obj is null"); + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + return QDF_STATUS_E_NULL_VALUE; + } + qdf_spin_lock_bh(&peer_nan_obj->lock); + + /* + * Store the first channel info in NDP Confirm as the home channel info + * and store it in the peer private object. + */ + if (!peer_nan_obj->active_ndp_sessions) + qdf_mem_copy(&peer_nan_obj->home_chan_info, ndp_chan_info, + sizeof(struct nan_datapath_channel_info)); + + peer_nan_obj->active_ndp_sessions++; + nan_debug("Number of active session = %d for peer:"QDF_MAC_ADDR_FMT"", + peer_nan_obj->active_ndp_sessions, + QDF_MAC_ADDR_REF(peer_ndi_mac->bytes)); + qdf_spin_unlock_bh(&peer_nan_obj->lock); + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS nan_decrement_ndp_sessions(struct wlan_objmgr_psoc *psoc, + struct qdf_mac_addr *peer_ndi_mac) +{ + struct wlan_objmgr_peer *peer; + struct nan_peer_priv_obj *peer_nan_obj; + + peer = wlan_objmgr_get_peer_by_mac(psoc, + peer_ndi_mac->bytes, + WLAN_NAN_ID); + + if (!peer) { + nan_err("peer object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + peer_nan_obj = nan_get_peer_priv_obj(peer); + if (!peer_nan_obj) { + nan_err("peer_nan_obj is null"); + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_spin_lock_bh(&peer_nan_obj->lock); + if (!peer_nan_obj->active_ndp_sessions) { + qdf_spin_unlock_bh(&peer_nan_obj->lock); + nan_err("Active NDP sessions already zero!"); + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + return QDF_STATUS_E_FAILURE; + } + peer_nan_obj->active_ndp_sessions--; + nan_debug("Number of active session = %d for peer:"QDF_MAC_ADDR_FMT"", + peer_nan_obj->active_ndp_sessions, + QDF_MAC_ADDR_REF(peer_ndi_mac->bytes)); + qdf_spin_unlock_bh(&peer_nan_obj->lock); + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +ndi_remove_and_update_primary_connection(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev) +{ + struct nan_vdev_priv_obj *vdev_nan_obj; + struct nan_psoc_priv_obj *psoc_nan_obj; + struct nan_peer_priv_obj *peer_nan_obj = NULL; + struct wlan_objmgr_peer *peer, *peer_next; + qdf_list_t *peer_list; + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("Invalid psoc nan private obj"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev_nan_obj = nan_get_vdev_priv_obj(vdev); + if (!vdev_nan_obj) { + nan_err("Invalid vdev nan private obj"); + return QDF_STATUS_E_NULL_VALUE; + } + + peer_list = &vdev->vdev_objmgr.wlan_peer_list; + if (!peer_list) { + nan_err("Peer list for vdev obj is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + peer = wlan_vdev_peer_list_peek_active_head(vdev, peer_list, + WLAN_NAN_ID); + + while (peer) { + peer_nan_obj = nan_get_peer_priv_obj(peer); + if (!peer_nan_obj) + nan_err("NAN peer object for Peer " QDF_MAC_ADDR_FMT " is NULL", + QDF_MAC_ADDR_REF(wlan_peer_get_macaddr(peer))); + else if (peer_nan_obj->active_ndp_sessions) + break; + + peer_next = wlan_peer_get_next_active_peer_of_vdev(vdev, + peer_list, + peer, + WLAN_NAN_ID); + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + peer = peer_next; + } + + if (!peer && psoc_nan_obj->nan_caps.ndi_dbs_supported) { + policy_mgr_decr_session_set_pcl(psoc, QDF_NDI_MODE, + wlan_vdev_get_id(vdev)); + vdev_nan_obj->ndp_init_done = false; + return QDF_STATUS_SUCCESS; + } + + if (peer_nan_obj && psoc_nan_obj->nan_caps.ndi_dbs_supported) { + psoc_nan_obj->cb_obj.update_ndi_conn(wlan_vdev_get_id(vdev), + &peer_nan_obj->home_chan_info); + policy_mgr_update_connection_info(psoc, wlan_vdev_get_id(vdev)); + qdf_mem_copy(vdev_nan_obj->primary_peer_mac.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + policy_mgr_check_n_start_opportunistic_timer(psoc); + } + + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +ndi_update_ndp_session(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *peer_ndi_mac, + struct nan_datapath_channel_info *ndp_chan_info) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_peer *peer; + struct nan_psoc_priv_obj *psoc_nan_obj; + struct nan_vdev_priv_obj *vdev_nan_obj; + struct nan_peer_priv_obj *peer_nan_obj; + + psoc = wlan_vdev_get_psoc(vdev); + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev_nan_obj = nan_get_vdev_priv_obj(vdev); + if (!vdev_nan_obj) { + nan_err("NAN vdev private object is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + peer = wlan_objmgr_get_peer_by_mac(psoc, + peer_ndi_mac->bytes, + WLAN_NAN_ID); + + if (!peer) { + nan_err("peer object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + peer_nan_obj = nan_get_peer_priv_obj(peer); + if (!peer_nan_obj) { + nan_err("peer_nan_obj is null"); + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_spin_lock_bh(&peer_nan_obj->lock); + qdf_mem_copy(&peer_nan_obj->home_chan_info, ndp_chan_info, + sizeof(*ndp_chan_info)); + psoc_nan_obj->cb_obj.update_ndi_conn(wlan_vdev_get_id(vdev), + &peer_nan_obj->home_chan_info); + qdf_spin_unlock_bh(&peer_nan_obj->lock); + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + + if (qdf_is_macaddr_equal(&vdev_nan_obj->primary_peer_mac, + peer_ndi_mac)) { + /* TODO: Update policy mgr with connection info */ + } + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS nan_handle_confirm( + struct nan_datapath_confirm_event *confirm) +{ + uint8_t vdev_id, channel; + struct wlan_objmgr_psoc *psoc; + struct nan_psoc_priv_obj *psoc_nan_obj; + struct nan_vdev_priv_obj *vdev_nan_obj; + struct wlan_objmgr_peer *peer; + QDF_STATUS status; + + vdev_id = wlan_vdev_get_id(confirm->vdev); + psoc = wlan_vdev_get_psoc(confirm->vdev); + if (!psoc) { + nan_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + peer = wlan_objmgr_get_peer_by_mac(psoc, + confirm->peer_ndi_mac_addr.bytes, + WLAN_NAN_ID); + if (!peer) { + nan_debug("Drop NDP confirm as peer isn't available"); + return QDF_STATUS_E_NULL_VALUE; + } + wlan_objmgr_peer_release_ref(peer, WLAN_NAN_ID); + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev_nan_obj = nan_get_vdev_priv_obj(confirm->vdev); + if (!vdev_nan_obj) { + nan_err("vdev_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (confirm->rsp_code != NAN_DATAPATH_RESPONSE_ACCEPT && + confirm->num_active_ndps_on_peer == 0) { + /* + * This peer was created at ndp_indication but + * confirm failed, so it needs to be deleted + */ + nan_err("NDP confirm with reject and no active ndp sessions. deleting peer: "QDF_MAC_ADDR_FMT" on vdev_id: %d", + QDF_MAC_ADDR_REF(confirm->peer_ndi_mac_addr.bytes), + vdev_id); + psoc_nan_obj->cb_obj.delete_peers_by_addr(vdev_id, + confirm->peer_ndi_mac_addr); + } + + /* Increment NDP sessions for the Peer */ + if (confirm->rsp_code == NAN_DATAPATH_RESPONSE_ACCEPT) + nan_increment_ndp_sessions(psoc, &confirm->peer_ndi_mac_addr, + &confirm->ch[0]); + + psoc_nan_obj->cb_obj.os_if_ndp_event_handler(psoc, confirm->vdev, + NDP_CONFIRM, confirm); + + if (confirm->rsp_code == NAN_DATAPATH_RESPONSE_ACCEPT && + !vdev_nan_obj->ndp_init_done) { + /* + * If this is the NDI's first NDP, store the NDP instance in + * vdev object as its primary connection. If this instance ends + * the second NDP should take its place. + */ + qdf_mem_copy(vdev_nan_obj->primary_peer_mac.bytes, + &confirm->peer_ndi_mac_addr, QDF_MAC_ADDR_SIZE); + + psoc_nan_obj->cb_obj.update_ndi_conn(vdev_id, &confirm->ch[0]); + + if (psoc_nan_obj->nan_caps.ndi_dbs_supported) { + channel = wlan_freq_to_chan(confirm->ch[0].freq); + status = policy_mgr_reset_connection_update(psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + nan_err("Policy mgr reset connection failed-%d", + status); + return status; + } + + status = policy_mgr_current_connections_update( + psoc, vdev_id, wlan_chan_to_freq(channel), + POLICY_MGR_UPDATE_REASON_NDP_UPDATE); + if (QDF_STATUS_E_FAILURE == status) { + nan_err("connections update failed!!"); + return status; + } + + if (QDF_STATUS_SUCCESS == status) { + status = policy_mgr_wait_for_connection_update( + psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + nan_err("qdf wait for event failed!!"); + return status; + } + } + + policy_mgr_incr_active_session(psoc, QDF_NDI_MODE, + vdev_id); + vdev_nan_obj->ndp_init_done = true; + } + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS nan_handle_initiator_rsp( + struct nan_datapath_initiator_rsp *rsp, + struct wlan_objmgr_vdev **vdev) +{ + struct wlan_objmgr_psoc *psoc; + struct nan_psoc_priv_obj *psoc_nan_obj; + + *vdev = rsp->vdev; + psoc = wlan_vdev_get_psoc(rsp->vdev); + if (!psoc) { + nan_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj->cb_obj.os_if_ndp_event_handler(psoc, rsp->vdev, + NDP_INITIATOR_RSP, rsp); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS nan_handle_ndp_ind( + struct nan_datapath_indication_event *ndp_ind) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct nan_psoc_priv_obj *psoc_nan_obj; + + vdev_id = wlan_vdev_get_id(ndp_ind->vdev); + psoc = wlan_vdev_get_psoc(ndp_ind->vdev); + if (!psoc) { + nan_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + nan_debug("role: %d, vdev: %d, csid: %d, peer_mac_addr " + QDF_MAC_ADDR_FMT, + ndp_ind->role, vdev_id, ndp_ind->ncs_sk_type, + QDF_MAC_ADDR_REF(ndp_ind->peer_mac_addr.bytes)); + + if ((ndp_ind->role == NAN_DATAPATH_ROLE_INITIATOR) || + ((NAN_DATAPATH_ROLE_RESPONDER == ndp_ind->role) && + (NAN_DATAPATH_ACCEPT_POLICY_ALL == ndp_ind->policy))) { + status = psoc_nan_obj->cb_obj.add_ndi_peer(vdev_id, + ndp_ind->peer_mac_addr); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("Couldn't add ndi peer, ndp_role: %d", + ndp_ind->role); + return status; + } + } + psoc_nan_obj->cb_obj.os_if_ndp_event_handler(psoc, + ndp_ind->vdev, + NDP_INDICATION, + ndp_ind); + + return status; +} + +static QDF_STATUS nan_handle_responder_rsp( + struct nan_datapath_responder_rsp *rsp, + struct wlan_objmgr_vdev **vdev) +{ + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct nan_psoc_priv_obj *psoc_nan_obj; + + *vdev = rsp->vdev; + psoc = wlan_vdev_get_psoc(rsp->vdev); + if (!psoc) { + nan_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (QDF_IS_STATUS_SUCCESS(rsp->status) && rsp->create_peer) { + status = psoc_nan_obj->cb_obj.add_ndi_peer( + wlan_vdev_get_id(rsp->vdev), + rsp->peer_mac_addr); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("Couldn't add ndi peer"); + rsp->status = QDF_STATUS_E_FAILURE; + } + } + psoc_nan_obj->cb_obj.os_if_ndp_event_handler(psoc, rsp->vdev, + NDP_RESPONDER_RSP, rsp); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS nan_handle_ndp_end_rsp( + struct nan_datapath_end_rsp_event *rsp, + struct wlan_objmgr_vdev **vdev) +{ + struct wlan_objmgr_psoc *psoc; + struct nan_psoc_priv_obj *psoc_nan_obj; + struct osif_request *request; + + *vdev = rsp->vdev; + psoc = wlan_vdev_get_psoc(rsp->vdev); + if (!psoc) { + nan_err("psoc is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + /* Unblock the wait here if NDP_END request is a failure */ + if (rsp->status != 0) { + request = osif_request_get(psoc_nan_obj->request_context); + if (request) { + osif_request_complete(request); + osif_request_put(request); + } + } + psoc_nan_obj->cb_obj.os_if_ndp_event_handler(psoc, rsp->vdev, + NDP_END_RSP, rsp); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS nan_handle_end_ind( + struct nan_datapath_end_indication_event *ind) +{ + uint32_t i; + struct wlan_objmgr_psoc *psoc; + struct nan_psoc_priv_obj *psoc_nan_obj; + struct wlan_objmgr_vdev *vdev_itr; + struct nan_vdev_priv_obj *vdev_nan_obj; + struct osif_request *request; + + psoc = wlan_vdev_get_psoc(ind->vdev); + if (!psoc) { + nan_err("psoc is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + /* Decrement NDP sessions for all Peers in the event */ + for (i = 0; i < ind->num_ndp_ids; i++) + nan_decrement_ndp_sessions(psoc, + &ind->ndp_map[i].peer_ndi_mac_addr); + + for (i = 0; i < ind->num_ndp_ids; i++) { + vdev_itr = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + ind->ndp_map[i].vdev_id, + WLAN_NAN_ID); + if (!vdev_itr) { + nan_err("NAN vdev object is NULL"); + continue; + } + + vdev_nan_obj = nan_get_vdev_priv_obj(vdev_itr); + if (!vdev_nan_obj) { + wlan_objmgr_vdev_release_ref(vdev_itr, WLAN_NAN_ID); + nan_err("NAN vdev private object is NULL"); + continue; + } + + if (qdf_is_macaddr_equal(&vdev_nan_obj->primary_peer_mac, + &ind->ndp_map[i].peer_ndi_mac_addr)) + ndi_remove_and_update_primary_connection(psoc, + vdev_itr); + + wlan_objmgr_vdev_release_ref(vdev_itr, WLAN_NAN_ID); + } + + psoc_nan_obj->cb_obj.ndp_delete_peers(ind->ndp_map, ind->num_ndp_ids); + psoc_nan_obj->cb_obj.os_if_ndp_event_handler(psoc, ind->vdev, + NDP_END_IND, ind); + + /* Unblock the NDP_END wait */ + request = osif_request_get(psoc_nan_obj->request_context); + if (request) { + osif_request_complete(request); + osif_request_put(request); + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS nan_handle_enable_rsp(struct nan_event_params *nan_event) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + void (*call_back)(void *cookie); + uint8_t vdev_id; + + psoc = nan_event->psoc; + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (nan_event->is_nan_enable_success) { + status = nan_set_discovery_state(psoc, NAN_DISC_ENABLED); + + if (QDF_IS_STATUS_SUCCESS(status)) { + psoc_nan_obj->nan_disc_mac_id = nan_event->mac_id; + vdev_id = nan_event->vdev_id; + if (!ucfg_nan_is_vdev_creation_allowed(psoc)) { + vdev_id = NAN_PSEUDO_VDEV_ID; + } else if (vdev_id >= WLAN_MAX_VDEVS) { + nan_err("Invalid NAN vdev_id: %u", vdev_id); + goto fail; + } + nan_debug("NAN vdev_id: %u", vdev_id); + policy_mgr_incr_active_session(psoc, QDF_NAN_DISC_MODE, + vdev_id); + policy_mgr_nan_sap_post_enable_conc_check(psoc); + + } else { + /* + * State set to DISABLED OR DISABLE_IN_PROGRESS, try to + * restore the single MAC mode. + */ + psoc_nan_obj->nan_social_ch_2g_freq = 0; + psoc_nan_obj->nan_social_ch_5g_freq = 0; + policy_mgr_check_n_start_opportunistic_timer(psoc); + } + goto done; + } else { + nan_info("NAN enable has failed"); + /* NAN Enable has failed, restore changes */ + goto fail; + } +fail: + psoc_nan_obj->nan_social_ch_2g_freq = 0; + psoc_nan_obj->nan_social_ch_5g_freq = 0; + nan_set_discovery_state(psoc, NAN_DISC_DISABLED); + policy_mgr_check_n_start_opportunistic_timer(psoc); + +done: + call_back = psoc_nan_obj->cb_obj.ucfg_nan_request_process_cb; + if (call_back) + call_back(psoc_nan_obj->request_context); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS nan_disable_cleanup(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + QDF_STATUS status; + uint8_t vdev_id; + + if (!psoc) { + nan_err("psoc is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = nan_set_discovery_state(psoc, NAN_DISC_DISABLED); + if (QDF_IS_STATUS_SUCCESS(status)) { + void (*call_back)(void *cookie); + + call_back = psoc_nan_obj->cb_obj.ucfg_nan_request_process_cb; + vdev_id = policy_mgr_mode_specific_vdev_id(psoc, + PM_NAN_DISC_MODE); + nan_debug("NAN vdev_id: %u", vdev_id); + policy_mgr_decr_session_set_pcl(psoc, QDF_NAN_DISC_MODE, + vdev_id); + if (psoc_nan_obj->is_explicit_disable && call_back) + call_back(psoc_nan_obj->request_context); + + policy_mgr_nan_sap_post_disable_conc_check(psoc); + } else { + /* Should not happen, NAN state can always be disabled */ + nan_err("Cannot set NAN state to disabled!"); + return QDF_STATUS_E_FAILURE; + } + + return status; +} + +static QDF_STATUS nan_handle_disable_ind(struct nan_event_params *nan_event) +{ + return nan_disable_cleanup(nan_event->psoc); +} + +static QDF_STATUS nan_handle_schedule_update( + struct nan_datapath_sch_update_event *ind) +{ + struct wlan_objmgr_psoc *psoc; + struct nan_psoc_priv_obj *psoc_nan_obj; + + psoc = wlan_vdev_get_psoc(ind->vdev); + if (!psoc) { + nan_err("psoc is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + ndi_update_ndp_session(ind->vdev, &ind->peer_addr, &ind->ch[0]); + psoc_nan_obj->cb_obj.os_if_ndp_event_handler(psoc, ind->vdev, + NDP_SCHEDULE_UPDATE, ind); + + return QDF_STATUS_SUCCESS; +} + +/** + * nan_handle_host_update() - Updates Host about NAN Datapath status + * @evt: Event data received from firmware + * @vdev: pointer to vdev + * + * Return: status of operation + */ +static QDF_STATUS nan_handle_host_update(struct nan_datapath_host_event *evt, + struct wlan_objmgr_vdev **vdev) +{ + struct wlan_objmgr_psoc *psoc; + struct nan_psoc_priv_obj *psoc_nan_obj; + + *vdev = evt->vdev; + psoc = wlan_vdev_get_psoc(evt->vdev); + if (!psoc) { + nan_err("psoc is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj->cb_obj.os_if_ndp_event_handler(psoc, evt->vdev, + NDP_HOST_UPDATE, evt); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS nan_discovery_event_handler(struct scheduler_msg *msg) +{ + struct nan_event_params *nan_event; + struct nan_psoc_priv_obj *psoc_nan_obj; + + if (!msg || !msg->bodyptr) { + nan_err("msg body is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + nan_event = msg->bodyptr; + if (!nan_event->psoc) { + nan_err("psoc is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(nan_event->psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + switch (msg->type) { + case nan_event_id_enable_rsp: + nan_handle_enable_rsp(nan_event); + break; + case nan_event_id_disable_ind: + nan_handle_disable_ind(nan_event); + break; + case nan_event_id_generic_rsp: + case nan_event_id_error_rsp: + break; + default: + nan_err("Unknown event ID type - %d", msg->type); + break; + } + + psoc_nan_obj->cb_obj.os_if_nan_event_handler(nan_event); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS nan_datapath_event_handler(struct scheduler_msg *pe_msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_serialization_queued_cmd_info cmd; + + cmd.requestor = WLAN_UMAC_COMP_NAN; + cmd.cmd_id = 0; + cmd.req_type = WLAN_SER_CANCEL_NON_SCAN_CMD; + cmd.queue_type = WLAN_SERIALIZATION_ACTIVE_QUEUE; + + if (!pe_msg->bodyptr) { + nan_err("msg body is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + switch (pe_msg->type) { + case NDP_CONFIRM: { + nan_handle_confirm(pe_msg->bodyptr); + break; + } + case NDP_INITIATOR_RSP: { + nan_handle_initiator_rsp(pe_msg->bodyptr, &cmd.vdev); + cmd.cmd_type = WLAN_SER_CMD_NDP_INIT_REQ; + wlan_serialization_remove_cmd(&cmd); + break; + } + case NDP_INDICATION: { + nan_handle_ndp_ind(pe_msg->bodyptr); + break; + } + case NDP_RESPONDER_RSP: + nan_handle_responder_rsp(pe_msg->bodyptr, &cmd.vdev); + cmd.cmd_type = WLAN_SER_CMD_NDP_RESP_REQ; + wlan_serialization_remove_cmd(&cmd); + break; + case NDP_END_RSP: + nan_handle_ndp_end_rsp(pe_msg->bodyptr, &cmd.vdev); + cmd.cmd_type = WLAN_SER_CMD_NDP_DATA_END_INIT_REQ; + wlan_serialization_remove_cmd(&cmd); + break; + case NDP_END_IND: + nan_handle_end_ind(pe_msg->bodyptr); + break; + case NDP_SCHEDULE_UPDATE: + nan_handle_schedule_update(pe_msg->bodyptr); + break; + case NDP_HOST_UPDATE: + nan_handle_host_update(pe_msg->bodyptr, &cmd.vdev); + cmd.cmd_type = WLAN_SER_CMD_NDP_END_ALL_REQ; + wlan_serialization_remove_cmd(&cmd); + break; + default: + nan_alert("Unhandled NDP event: %d", pe_msg->type); + status = QDF_STATUS_E_NOSUPPORT; + break; + } + return status; +} + +bool nan_is_enable_allowed(struct wlan_objmgr_psoc *psoc, uint32_t nan_ch_freq) +{ + if (!psoc) { + nan_err("psoc object object is NULL"); + return false; + } + + return (NAN_DISC_DISABLED == nan_get_discovery_state(psoc) && + policy_mgr_allow_concurrency(psoc, PM_NAN_DISC_MODE, + nan_ch_freq, HW_MODE_20_MHZ)); +} + +bool nan_is_disc_active(struct wlan_objmgr_psoc *psoc) +{ + if (!psoc) { + nan_err("psoc object object is NULL"); + return false; + } + + return (NAN_DISC_ENABLED == nan_get_discovery_state(psoc) || + NAN_DISC_ENABLE_IN_PROGRESS == nan_get_discovery_state(psoc)); +} + +QDF_STATUS nan_discovery_pre_enable(struct wlan_objmgr_psoc *psoc, + uint32_t nan_ch_freq) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + struct wlan_objmgr_pdev *pdev = NULL; + struct wlan_objmgr_vdev *vdev = NULL; + uint8_t vdev_id; + + status = nan_set_discovery_state(psoc, NAN_DISC_ENABLE_IN_PROGRESS); + + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("Unable to set NAN Disc State to ENABLE_IN_PROGRESS"); + goto pre_enable_failure; + } + + policy_mgr_stop_opportunistic_timer(psoc); + + if (policy_mgr_is_hw_mode_change_in_progress(psoc)) { + status = policy_mgr_wait_for_connection_update(psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + nan_err("Failed to wait for connection update"); + goto pre_enable_failure; + } + } + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_NAN_ID); + if (!pdev) { + nan_err("null pdev"); + status = QDF_STATUS_E_INVAL; + goto pre_enable_failure; + } + + if (!policy_mgr_nan_sap_pre_enable_conc_check(psoc, PM_NAN_DISC_MODE, + nan_ch_freq)) { + nan_debug("NAN not enabled due to concurrency constraints"); + status = QDF_STATUS_E_INVAL; + goto pre_enable_failure; + } + + /* Piggyback on any available vdev for policy manager update */ + vdev = wlan_objmgr_pdev_get_first_vdev(pdev, WLAN_NAN_ID); + if (!vdev) { + nan_err("No vdev is up yet, unable to proceed!"); + status = QDF_STATUS_E_INVAL; + goto pre_enable_failure; + } + vdev_id = wlan_vdev_get_id(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + + status = policy_mgr_update_and_wait_for_connection_update(psoc, vdev_id, + nan_ch_freq, + POLICY_MGR_UPDATE_REASON_NAN_DISCOVERY); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("Failed to set or wait for HW mode change"); + goto pre_enable_failure; + } + + /* Try to teardown TDLS links, but do not wait */ + status = ucfg_tdls_teardown_links(psoc); + if (QDF_IS_STATUS_ERROR(status)) + nan_err("Failed to teardown TDLS links"); + +pre_enable_failure: + if (pdev) + wlan_objmgr_pdev_release_ref(pdev, WLAN_NAN_ID); + + if (QDF_IS_STATUS_ERROR(status)) + nan_set_discovery_state(psoc, NAN_DISC_DISABLED); + + return status; +} + +static QDF_STATUS nan_discovery_disable_req(struct nan_disable_req *req) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + struct wlan_nan_tx_ops *tx_ops; + + /* + * State was already set to Disabled by failed Enable + * request OR by the Disable Indication event, drop the + * Disable request. + */ + if (NAN_DISC_DISABLED == nan_get_discovery_state(req->psoc)) + return QDF_STATUS_SUCCESS; + + psoc_nan_obj = nan_get_psoc_priv_obj(req->psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + tx_ops = &psoc_nan_obj->tx_ops; + if (!tx_ops->nan_discovery_req_tx) { + nan_err("NAN Discovery tx op is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + return tx_ops->nan_discovery_req_tx(req, NAN_DISABLE_REQ); +} + +static QDF_STATUS nan_discovery_enable_req(struct nan_enable_req *req) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + struct wlan_nan_tx_ops *tx_ops; + + /* + * State was already set to Disable in progress by a disable request, + * drop the Enable request, start opportunistic timer and move back to + * the Disabled state. + */ + if (NAN_DISC_DISABLE_IN_PROGRESS == + nan_get_discovery_state(req->psoc)) { + policy_mgr_check_n_start_opportunistic_timer(req->psoc); + return nan_set_discovery_state(req->psoc, NAN_DISC_DISABLED); + } + + psoc_nan_obj = nan_get_psoc_priv_obj(req->psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_nan_obj->nan_social_ch_2g_freq = req->social_chan_2g_freq; + psoc_nan_obj->nan_social_ch_5g_freq = req->social_chan_5g_freq; + + tx_ops = &psoc_nan_obj->tx_ops; + if (!tx_ops->nan_discovery_req_tx) { + nan_err("NAN Discovery tx op is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + return tx_ops->nan_discovery_req_tx(req, NAN_ENABLE_REQ); +} + +static QDF_STATUS nan_discovery_generic_req(struct nan_generic_req *req) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + struct wlan_nan_tx_ops *tx_ops; + + psoc_nan_obj = nan_get_psoc_priv_obj(req->psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + tx_ops = &psoc_nan_obj->tx_ops; + if (!tx_ops->nan_discovery_req_tx) { + nan_err("NAN Discovery tx op is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + return tx_ops->nan_discovery_req_tx(req, NAN_GENERIC_REQ); +} + +void nan_discovery_flush_callback(struct scheduler_msg *msg) +{ + struct wlan_objmgr_psoc *psoc; + + if (!msg || !msg->bodyptr) { + nan_err("Null pointer for NAN Discovery message"); + return; + } + + switch (msg->type) { + case NAN_ENABLE_REQ: + psoc = ((struct nan_enable_req *)msg->bodyptr)->psoc; + break; + case NAN_DISABLE_REQ: + psoc = ((struct nan_disable_req *)msg->bodyptr)->psoc; + break; + case NAN_GENERIC_REQ: + psoc = ((struct nan_generic_req *)msg->bodyptr)->psoc; + break; + default: + nan_err("Unsupported request type: %d", msg->type); + qdf_mem_free(msg->bodyptr); + return; + } + + wlan_objmgr_psoc_release_ref(psoc, WLAN_NAN_ID); + qdf_mem_free(msg->bodyptr); +} + +QDF_STATUS nan_discovery_scheduled_handler(struct scheduler_msg *msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!msg || !msg->bodyptr) { + nan_alert("msg or bodyptr is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + switch (msg->type) { + case NAN_ENABLE_REQ: + status = nan_discovery_enable_req(msg->bodyptr); + break; + case NAN_DISABLE_REQ: + status = nan_discovery_disable_req(msg->bodyptr); + break; + case NAN_GENERIC_REQ: + status = nan_discovery_generic_req(msg->bodyptr); + break; + default: + nan_err("Unsupported request type: %d", msg->type); + qdf_mem_free(msg->bodyptr); + return QDF_STATUS_E_FAILURE; + } + + nan_discovery_flush_callback(msg); + return status; +} + +QDF_STATUS +wlan_nan_get_connection_info(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_vdev_entry_info *conn_info) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + + if (!psoc) { + nan_err("psoc obj is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (nan_get_discovery_state(psoc) != NAN_DISC_ENABLED) { + nan_err("NAN State needs to be Enabled"); + return QDF_STATUS_E_INVAL; + } + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + /* For policy_mgr use NAN mandatory Social ch 6 */ + conn_info->mhz = psoc_nan_obj->nan_social_ch_2g_freq; + conn_info->mac_id = psoc_nan_obj->nan_disc_mac_id; + conn_info->chan_width = CH_WIDTH_20MHZ; + conn_info->type = WMI_VDEV_TYPE_NAN; + + return QDF_STATUS_SUCCESS; +} + +uint32_t wlan_nan_get_disc_5g_ch_freq(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return 0; + } + + if (nan_get_discovery_state(psoc) != NAN_DISC_ENABLED) + return 0; + + return psoc_nan_obj->nan_social_ch_5g_freq; +} + +bool wlan_nan_get_sap_conc_support(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return 0; + } + + return (psoc_nan_obj->nan_caps.nan_sap_supported && + psoc_nan_obj->nan_caps.nan_dbs_supported && + psoc_nan_obj->nan_caps.nan_disable_supported); +} diff --git a/drivers/staging/qcacld-3.0/components/nan/core/src/nan_main_i.h b/drivers/staging/qcacld-3.0/components/nan/core/src/nan_main_i.h new file mode 100644 index 0000000000000000000000000000000000000000..2053d1549e13675d4f3c99263a31da025d842587 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/core/src/nan_main_i.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains declaration of common utility APIs and private structs to be + * used in NAN modules + */ + +#ifdef WLAN_FEATURE_NAN +#ifndef _WLAN_NAN_MAIN_I_H_ +#define _WLAN_NAN_MAIN_I_H_ + +#include "qdf_types.h" +#include "qdf_status.h" +#include "nan_public_structs.h" +#include "wlan_objmgr_cmn.h" + +struct wlan_objmgr_vdev; +struct wlan_objmgr_psoc; +struct scheduler_msg; + +#define nan_alert(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_NAN, params) +#define nan_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_NAN, params) +#define nan_warn(params...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_NAN, params) +#define nan_notice(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_NAN, params) +#define nan_info(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_NAN, params) +#define nan_debug(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_NAN, params) + +#define nan_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_NAN, params) +#define nan_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_NAN, params) +#define nan_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_NAN, params) +#define nan_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_NAN, params) +#define nan_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_NAN, params) + +/** + * enum nan_disc_state - NAN Discovery states + * @NAN_DISC_DISABLED: NAN Discovery is disabled + * @NAN_DISC_ENABLE_IN_PROGRESS: NAN Discovery enable is in progress + * @NAN_DISC_ENABLED: NAN Discovery is enabled + * @NAN_DISC_DISABLE_IN_PROGRESS: NAN Discovery disable is in progress + */ +enum nan_disc_state { + NAN_DISC_DISABLED, + NAN_DISC_ENABLE_IN_PROGRESS, + NAN_DISC_ENABLED, + NAN_DISC_DISABLE_IN_PROGRESS, +}; + +/** + * struct nan_cfg_params - NAN INI config params + * @enable: NAN feature enable + * @dp_enable: NAN Datapath feature enable + * @ndi_mac_randomize: Randomize NAN datapath interface MAC + * @ndp_inactivity_timeout: NDP inactivity timeout + * @nan_separate_iface_support: To supports separate iface creation for NAN + * @ndp_keep_alive_period: To configure duration of how many seconds to + * wait to kickout peer if peer is not reachable + * @support_mp0_discovery: To support discovery of NAN cluster with Master + * Preference (MP) as 0 when a new device is enabling NAN + * @max_ndp_sessions: max ndp sessions host supports + * @max_ndi: max number of ndi host supports + * @nan_feature_config: Bitmap to enable/disable a particular NAN feature + * configuration in firmware. It's sent to firmware through + * WMI_VDEV_PARAM_ENABLE_DISABLE_NAN_CONFIG_FEATURES + */ +struct nan_cfg_params { + bool enable; + bool dp_enable; + bool ndi_mac_randomize; + uint16_t ndp_inactivity_timeout; + bool nan_separate_iface_support; + uint16_t ndp_keep_alive_period; + bool support_mp0_discovery; + uint32_t max_ndp_sessions; + uint32_t max_ndi; + uint32_t nan_feature_config; +}; + +/** + * struct nan_psoc_priv_obj - nan private psoc obj + * @lock: lock to be acquired before reading or writing to object + * @cb_obj: struct contaning callback pointers + * @cfg_param: NAN Config parameters in INI + * @nan_caps: NAN Target capabilities + * @tx_ops: Tx ops registered with Target IF interface + * @rx_ops: Rx ops registered with Target IF interface + * @disc_state: Present NAN Discovery state + * @nan_social_ch_2g_freq: NAN 2G Social channel for discovery + * @nan_social_ch_5g_freq: NAN 5G Social channel for discovery + * @nan_disc_mac_id: MAC id used for NAN Discovery + * @is_explicit_disable: Flag to indicate that NAN is being explicitly + * disabled by driver or user-space + * @request_context: NAN enable/disable request context + */ +struct nan_psoc_priv_obj { + qdf_spinlock_t lock; + struct nan_callbacks cb_obj; + struct nan_cfg_params cfg_param; + struct nan_tgt_caps nan_caps; + struct wlan_nan_tx_ops tx_ops; + struct wlan_nan_rx_ops rx_ops; + enum nan_disc_state disc_state; + uint32_t nan_social_ch_2g_freq; + uint32_t nan_social_ch_5g_freq; + uint8_t nan_disc_mac_id; + bool is_explicit_disable; + void *request_context; +}; + +/** + * struct nan_vdev_priv_obj - nan private vdev obj + * @lock: lock to be acquired before reading or writing to object + * @state: Current state of NDP + * @active_ndp_peers: number of active ndp peers + * @ndp_create_transaction_id: transaction id for create req + * @ndp_delete_transaction_id: transaction id for delete req + * @ndi_delete_rsp_reason: reason code for ndi_delete rsp + * @ndi_delete_rsp_status: status for ndi_delete rsp + * @primary_peer_mac: Primary NDP Peer mac address for the vdev + * @disable_context: Disable all NDP's operation context + * @ndp_init_done: Flag to indicate NDP initialization complete after first peer + * connection. + */ +struct nan_vdev_priv_obj { + qdf_spinlock_t lock; + enum nan_datapath_state state; + uint32_t active_ndp_peers; + uint16_t ndp_create_transaction_id; + uint16_t ndp_delete_transaction_id; + uint32_t ndi_delete_rsp_reason; + uint32_t ndi_delete_rsp_status; + struct qdf_mac_addr primary_peer_mac; + void *disable_context; + bool ndp_init_done; +}; + +/** + * struct nan_peer_priv_obj - nan private peer obj + * @lock: lock to be acquired before reading or writing to object + * @active_ndp_sessions: number of active ndp sessions for this peer + * @home_chan_info: Home channel info for the NDP associated with the Peer + */ +struct nan_peer_priv_obj { + qdf_spinlock_t lock; + uint32_t active_ndp_sessions; + struct nan_datapath_channel_info home_chan_info; +}; + +/** + * nan_release_cmd: frees resources for NAN command. + * @in_req: pointer to msg buffer to be freed + * @req_type: type of request + * + * Return: None + */ +void nan_release_cmd(void *in_req, uint32_t req_type); + +/** + * nan_scheduled_msg_handler: callback pointer to be called when scheduler + * starts executing enqueued NAN command. + * @msg: pointer to msg + * + * Return: status of operation + */ +QDF_STATUS nan_scheduled_msg_handler(struct scheduler_msg *msg); + +/** + * nan_discovery_flush_callback: callback to flush the NAN scheduler msg + * @msg: pointer to msg + * + * Return: None + */ +void nan_discovery_flush_callback(struct scheduler_msg *msg); + +/** + * nan_discovery_scheduled_handler: callback pointer to be called when scheduler + * starts executing enqueued NAN command. + * @msg: pointer to msg + * + * Return: status of operation + */ +QDF_STATUS nan_discovery_scheduled_handler(struct scheduler_msg *msg); + +/* + * nan_discovery_event_handler: function to process NAN events from firmware + * @msg: message received from Target IF + * + * Return: status of operation + */ +QDF_STATUS nan_discovery_event_handler(struct scheduler_msg *msg); + +/* + * nan_datapath_event_handler: function to process NDP events from firmware + * @msg: message received from Target IF + * + * Return: status of operation + */ +QDF_STATUS nan_datapath_event_handler(struct scheduler_msg *msg); + +/* + * nan_set_discovery_state: Attempts to set NAN Discovery state as the given one + * @psoc: PSOC object + * @new_state: Attempting to this NAN discovery state + * + * Return: status of operation + */ +QDF_STATUS nan_set_discovery_state(struct wlan_objmgr_psoc *psoc, + enum nan_disc_state new_state); + +/* + * nan_discovery_pre_enable: Takes steps before sending NAN Enable to Firmware + * @psoc: PSOC object + * @nan_ch_freq: Primary social channel for NAN Discovery + * + * Return: status of operation + */ +QDF_STATUS nan_discovery_pre_enable(struct wlan_objmgr_psoc *psoc, + uint32_t nan_ch_freq); + +/* + * nan_get_discovery_state: Returns the current NAN Discovery state + * @psoc: PSOC object + * + * Return: Current NAN Discovery state + */ +enum nan_disc_state nan_get_discovery_state(struct wlan_objmgr_psoc *psoc); + +/* + * nan_is_enable_allowed: Queries whether NAN Discovery is allowed + * @psoc: PSOC object + * @nan_ch_freq: Possible primary social channel for NAN Discovery + * + * Return: True if NAN Enable is allowed on given channel, False otherwise + */ +bool nan_is_enable_allowed(struct wlan_objmgr_psoc *psoc, uint32_t nan_ch_freq); + +/* + * nan_is_disc_active: Queries whether NAN Discovery is active + * @psoc: PSOC object + * + * Return: True if NAN Disc is active, False otherwise + */ +bool nan_is_disc_active(struct wlan_objmgr_psoc *psoc); + +/* + * nan_get_connection_info: Gets connection info of the NAN Discovery interface + * @psoc: PSOC object + * @chan: NAN Social channel to be returned + * @mac_if: MAC ID associated with NAN Discovery + * + * Return: QDF_STATUS + */ +QDF_STATUS +nan_get_connection_info(struct wlan_objmgr_psoc *psoc, uint8_t *chan, + uint8_t *mac_id); + +#endif /* _WLAN_NAN_MAIN_I_H_ */ +#endif /* WLAN_FEATURE_NAN */ diff --git a/drivers/staging/qcacld-3.0/components/nan/dispatcher/inc/cfg_nan.h b/drivers/staging/qcacld-3.0/components/nan/dispatcher/inc/cfg_nan.h new file mode 100644 index 0000000000000000000000000000000000000000..08396eb8b29f1dd9b813170708cd41ffbc00bcaa --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/dispatcher/inc/cfg_nan.h @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__NAN_CFG_H__) +#define __NAN_CFG_H__ + +/** + * + * DOC: nan_cfg.h + * + * NAN feature INI configuration parameter definitions + */ +#include "cfg_define.h" +#include "cfg_converged.h" +#include "qdf_types.h" + +/* + * + * gEnableNanSupport - NAN feature support configuration + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * When set to 1 NAN feature will be enabled. + * + * Related: None + * + * Supported Feature: NAN + * + * Usage: External + * + * + */ +#define CFG_NAN_ENABLE CFG_INI_BOOL("gEnableNanSupport", \ + 0, \ + "Enable NAN Support") + +/* + * + * nan_separate_iface_support: Separate iface creation for NAN + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Value is 1 when Host HDD supports separate iface creation + * for NAN. + * + * Related: None + * + * Supported Feature: NAN + * + * Usage: External + * + * + */ +#define CFG_NAN_SEPARATE_IFACE_SUPP CFG_INI_BOOL("nan_separate_iface_support", \ + 1, \ + "Seperate iface for NAN") + + +/* + * + * genable_nan_datapath - Enable NaN data path feature. NaN data path + * enables NAN supported devices to exchange + * data over TCP/UDP network stack. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * When set to 1 NAN Datapath feature will be enabled. + * + * Related: gEnableNanSupport + * + * Supported Feature: NAN + * + * Usage: External + * + * + */ +#define CFG_NAN_DATAPATH_ENABLE CFG_INI_BOOL("genable_nan_datapath", \ + 0, \ + "Enable NAN Datapath support") + +/* + * + * gEnableNDIMacRandomization - When enabled this will randomize NDI Mac + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * When enabled this will randomize NDI Mac + * + * Related: gEnableNanSupport + * + * Supported Feature: NAN + * + * Usage: External + * + * + */ +#define CFG_NAN_RANDOMIZE_NDI_MAC CFG_INI_BOOL("gEnableNDIMacRandomization", \ + 1, \ + "Enable NAN MAC Randomization") + +/* + * + * ndp_inactivity_timeout - To configure duration of how many seconds + * without TX/RX data traffic, NDI vdev can kickout the connected + * peer(i.e. NDP Termination). + * + * @Min: 0 + * @Max: 1800 + * @Default: 60 + * + * Related: None + * + * Supported Feature: NAN + * + * Usage: External + * + * + */ +#define CFG_NAN_NDP_INACTIVITY_TIMEOUT CFG_INI_UINT("ndp_inactivity_timeout", \ + 0, \ + 1800, \ + 60, \ + CFG_VALUE_OR_DEFAULT, \ + "NDP Auto Terminate time") + +/* + * + * gNdpKeepAlivePeriod - To configure duration of how many seconds + * to wait to kickout peer if peer is not reachable. + * + * @Min: 10 + * @Max: 30 + * @Default: 14 + * + * Related: None + * + * Supported Feature: NAN + * + * Usage: External + * + * + */ + +#define CFG_NDP_KEEP_ALIVE_PERIOD CFG_INI_UINT( \ + "gNdpKeepAlivePeriod", \ + 10, \ + 30, \ + 14, \ + CFG_VALUE_OR_DEFAULT, \ + "Keep alive timeout of a peer") + +/* + * + * ndp_max_sessions - To configure max ndp sessions + * supported by host. + * + * @Min: 1 + * @Max: 8 + * @Default: 8 + * + * Related: None + * + * Supported Feature: NAN + * + * Usage: Internal + * + * + */ + +#define CFG_NDP_MAX_SESSIONS CFG_INI_UINT( \ + "ndp_max_sessions", \ + 1, \ + 8, \ + 8, \ + CFG_VALUE_OR_DEFAULT, \ + "max ndp sessions host supports") + +/* + * + * gSupportMp0Discovery - To support discovery of NAN cluster with + * Master Preference (MP) as 0 when a new device is enabling NAN. + * + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: None + * + * Supported Feature: NAN + * + * Usage: External + * + * + */ +#define CFG_SUPPORT_MP0_DISCOVERY CFG_INI_BOOL( \ + "gSupportMp0Discovery", \ + 1, \ + "Enable/Disable discovery of NAN cluster with Master Preference (MP) as 0") +/* + * + * ndi_max_support - To configure max number of ndi host supports + * + * @Min: 1 + * @Max: 2 + * @Default: 1 + * + * Related: None + * + * Supported Feature: NAN + * + * Usage: Internal + * + * + */ +#define CFG_NDI_MAX_SUPPORT CFG_INI_UINT( \ + "ndi_max_support", \ + 1, \ + 2, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Max number of NDI host supports") + +/* + * + * nan_feature_config - Bitmap to enable/disable a particular NAN/NDP feature + * + * @Min: 0 + * @Max: 0xFFFF + * @Default: 0x1 + * + * This parameter helps to enable/disable a particular feature config by setting + * corresponding bit and send to firmware through the VDEV param + * WMI_VDEV_PARAM_ENABLE_DISABLE_NAN_CONFIG_FEATURES + * Acceptable values for this: + * BIT(0): Allow DW configuration from framework in sync role. + * If this is not set, firmware shall follow the spec/default behavior. + * BIT(1) to BIT(31): Reserved + * + * Related: None + * + * Supported Feature: NAN + * + * Usage: External + * + * + */ +#define CFG_NAN_FEATURE_CONFIG CFG_INI_UINT( \ + "nan_feature_config", \ + 0, \ + 0xFFFF, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable the specified NAN features in firmware") + +#ifdef WLAN_FEATURE_NAN +#define CFG_NAN_DISC CFG(CFG_NAN_ENABLE) \ + CFG(CFG_NDP_KEEP_ALIVE_PERIOD) \ + CFG(CFG_SUPPORT_MP0_DISCOVERY) +#define CFG_NAN_DP CFG(CFG_NAN_DATAPATH_ENABLE) \ + CFG(CFG_NAN_RANDOMIZE_NDI_MAC) \ + CFG(CFG_NAN_NDP_INACTIVITY_TIMEOUT) \ + CFG(CFG_NAN_SEPARATE_IFACE_SUPP) +#else +#define CFG_NAN_DISC +#define CFG_NAN_DP +#endif + +#define CFG_NAN_ALL CFG_NAN_DISC \ + CFG_NAN_DP \ + CFG(CFG_NDP_MAX_SESSIONS) \ + CFG(CFG_NDI_MAX_SUPPORT) \ + CFG(CFG_NAN_FEATURE_CONFIG) + +#endif diff --git a/drivers/staging/qcacld-3.0/components/nan/dispatcher/inc/cfg_nan_api.h b/drivers/staging/qcacld-3.0/components/nan/dispatcher/inc/cfg_nan_api.h new file mode 100644 index 0000000000000000000000000000000000000000..c2af195324d7e56b66a8aa92da1c2b914282d2d8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/dispatcher/inc/cfg_nan_api.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__NAN_CFG_API_H__) +#define __NAN_CFG_API_H__ + +/** + * + * DOC: nan_cfg_api.h + * + * NAN feature INI configuration parameters get/set APIs + */ +#include "qdf_types.h" + +struct wlan_objmgr_psoc; + +#ifdef WLAN_FEATURE_NAN +/** + * cfg_nan_get_enable() - get NAN support enable status + * @psoc: pointer to psoc object + * + * This function returns NAN enable status + */ +bool cfg_nan_get_enable(struct wlan_objmgr_psoc *psoc); + +/** + * cfg_nan_get_datapath_enable() - get NAN Datapath support enable status + * @psoc: pointer to psoc object + * + * This function returns NAN Datapath enable status + */ +bool cfg_nan_get_datapath_enable(struct wlan_objmgr_psoc *psoc); + +/** + * cfg_nan_get_ndi_mac_randomize() - get NDI MAC randomize enable status + * @psoc: pointer to psoc object + * + * This function returns NAN Datapath Interface MAC randomization status + */ +bool cfg_nan_get_ndi_mac_randomize(struct wlan_objmgr_psoc *psoc); + +/** + * cfg_nan_get_ndp_inactivity_timeout() - get NDP inactivity timeout value + * @psoc: pointer to psoc object + * @val: pointer to the value where inactivity timeout has to be copied to + * + * Return: QDF_STATUS + */ +QDF_STATUS cfg_nan_get_ndp_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint16_t *val); + +/** + * cfg_nan_get_ndp_keepalive_period() - get NDP keepalive period + * @psoc: pointer to psoc object + * @val: pointer to the value where keepalive period has to be copied to + * + * Return: QDF_STATUS + */ +QDF_STATUS cfg_nan_get_ndp_keepalive_period(struct wlan_objmgr_psoc *psoc, + uint16_t *val); + +/** + * cfg_nan_get_ndp_max_sessions() - get NDP max sessions host supports + * @psoc: pointer to psoc object + * @val: pointer to hold max ndp sessions + * + * Return: QDF_STATUS + */ +QDF_STATUS cfg_nan_get_ndp_max_sessions(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * cfg_nan_get_max_ndi() - get max number of ndi host supports + * @psoc: pointer to psoc object + * @val: pointer to hold max number of ndi + * + * Return: QDF_STATUS + */ +QDF_STATUS cfg_nan_get_max_ndi(struct wlan_objmgr_psoc *psoc, uint32_t *val); + +/** + * cfg_nan_get_support_mp0_discovery() - get value of config support mp0 + * discovery + * @psoc: pointer to psoc object + * + * Return: Value of config join clustur with mp + */ +bool cfg_nan_get_support_mp0_discovery(struct wlan_objmgr_psoc *psoc); + +/** + * cfg_nan_is_roam_config_disabled() - get value of nan config roam disable + * discovery + * @psoc: pointer to psoc object + * + * Return: true on sta roam disable by nan else false + */ +bool cfg_nan_is_roam_config_disabled(struct wlan_objmgr_psoc *psoc); +#else +static inline +bool cfg_nan_is_roam_config_disabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline bool cfg_nan_get_enable(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline bool cfg_nan_get_datapath_enable(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline bool cfg_nan_get_ndi_mac_randomize(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline +QDF_STATUS cfg_nan_get_ndp_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint16_t *val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS cfg_nan_get_ndp_keepalive_period(struct wlan_objmgr_psoc *psoc, + uint16_t *val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS cfg_nan_get_ndp_max_sessions(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS cfg_nan_get_max_ndi(struct wlan_objmgr_psoc *psoc, uint32_t *val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline bool cfg_nan_get_support_mp0_discovery( + struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +#endif + +#endif + diff --git a/drivers/staging/qcacld-3.0/components/nan/dispatcher/inc/nan_ucfg_api.h b/drivers/staging/qcacld-3.0/components/nan/dispatcher/inc/nan_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..4504b592acce30edb5f208fc9393d41737831459 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/dispatcher/inc/nan_ucfg_api.h @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains interface prototypes for OS_IF layer + */ + +#ifndef _NAN_UCFG_API_H_ +#define _NAN_UCFG_API_H_ + +#include "wlan_objmgr_cmn.h" +#include "nan_public_structs.h" + +#ifdef WLAN_FEATURE_NAN +#define ucfg_nan_set_ndi_state(vdev, state) \ + __ucfg_nan_set_ndi_state(vdev, state, __func__) + +/** + * ucfg_nan_set_ndi_state: set ndi state + * @vdev: pointer to vdev object + * @state: value to set + * @func: Caller of this API + * + * Return: status of operation + */ +QDF_STATUS __ucfg_nan_set_ndi_state(struct wlan_objmgr_vdev *vdev, + enum nan_datapath_state state, + const char *func); + +/** + * ucfg_nan_psoc_open: Setup NAN priv object params on PSOC open + * @psoc: Pointer to PSOC object + * + * Return: QDF Status of operation + */ +QDF_STATUS ucfg_nan_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_nan_psoc_close: Clean up NAN priv data on PSOC close + * @psoc: Pointer to PSOC object + * + * Return: None + */ +void ucfg_nan_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_nan_get_ndi_state: get ndi state from vdev obj + * @vdev: pointer to vdev object + * + * Return: ndi state + */ +enum nan_datapath_state ucfg_nan_get_ndi_state(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_nan_set_active_peers: set active ndi peer + * @vdev: pointer to vdev object + * @val: value to set + * + * Return: status of operation + */ +QDF_STATUS ucfg_nan_set_active_peers(struct wlan_objmgr_vdev *vdev, + uint32_t val); + +/** + * ucfg_nan_get_active_peers: get active ndi peer from vdev obj + * @vdev: pointer to vdev object + * + * Return: active ndi peer + */ +uint32_t ucfg_nan_get_active_peers(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_nan_set_ndp_create_transaction_id: set ndp create transaction id + * @vdev: pointer to vdev object + * @val: value to set + * + * Return: status of operation + */ +QDF_STATUS ucfg_nan_set_ndp_create_transaction_id(struct wlan_objmgr_vdev *vdev, + uint16_t val); + +/** + * ucfg_nan_get_ndp_create_transaction_id: get ndp create transaction id + * vdev obj + * @vdev: pointer to vdev object + * + * Return: ndp create transaction_id + */ +uint16_t ucfg_nan_get_ndp_create_transaction_id(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_nan_set_ndp_delete_transaction_id: set ndp delete transaction id + * @vdev: pointer to vdev object + * @val: value to set + * + * Return: status of operation + */ +QDF_STATUS ucfg_nan_set_ndp_delete_transaction_id(struct wlan_objmgr_vdev *vdev, + uint16_t val); + +/** + * ucfg_nan_get_ndp_delete_transaction_id: get ndp delete transaction id from + * vdev obj + * @vdev: pointer to vdev object + * + * Return: ndp delete transaction_id + */ +uint16_t ucfg_nan_get_ndp_delete_transaction_id(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_nan_set_ndi_delete_rsp_reason: set ndi delete response reason + * @vdev: pointer to vdev object + * @val: value to set + * + * Return: status of operation + */ +QDF_STATUS ucfg_nan_set_ndi_delete_rsp_reason(struct wlan_objmgr_vdev *vdev, + uint32_t val); + +/** + * ucfg_nan_get_ndi_delete_rsp_reason: get ndi delete response reason from vdev + * obj + * @vdev: pointer to vdev object + * + * Return: ndi delete rsp reason + */ +uint32_t ucfg_nan_get_ndi_delete_rsp_reason(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_nan_set_ndi_delete_rsp_status: set ndi delete response reason + * @vdev: pointer to vdev object + * @val: value to set + * + * Return: status of operation + */ +QDF_STATUS ucfg_nan_set_ndi_delete_rsp_status(struct wlan_objmgr_vdev *vdev, + uint32_t val); + +/** + * ucfg_nan_get_ndi_delete_rsp_status: get ndi delete response status from vdev + * obj + * @vdev: pointer to vdev object + * + * Return: ndi delete rsp status + */ +uint32_t ucfg_nan_get_ndi_delete_rsp_status(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_nan_get_callbacks: ucfg API to return callbacks + * @psoc: pointer to psoc object + * @cb_obj: callback struct to populate + * + * Return: callback struct on success, NULL otherwise + */ +QDF_STATUS ucfg_nan_get_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj); + +/** + * ucfg_nan_req_processor: ucfg API to be called from HDD/OS_IF to + * process nan datapath initiator request from userspace + * @vdev: nan vdev pointer + * @in_req: NDP request + * @psoc: pointer to psoc object + * @req_type: type of request + * + * Return: status of operation + */ +QDF_STATUS ucfg_nan_req_processor(struct wlan_objmgr_vdev *vdev, + void *in_req, uint32_t req_type); + +/** + * ucfg_nan_datapath_event_handler: ucfg API to be called from legacy code to + * post events to os_if/hdd layer + * @psoc: pointer to psoc object + * @vdev: pointer to vdev object + * @type: message type + * @msg: msg buffer + * + * Return: None + */ +void ucfg_nan_datapath_event_handler(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + uint32_t type, void *msg); + +/** + * ucfg_nan_register_hdd_callbacks: ucfg API to set hdd callbacks + * @psoc: pointer to psoc object + * @cb_obj: structs containing callbacks + * @os_if_event_handler: os if event handler callback + * + * Return: status of operation + */ +int ucfg_nan_register_hdd_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj); + +/* + * ucfg_nan_register_lim_callbacks: ucfg API to set lim callbacks + * @psoc: pointer to psoc object + * @cb_obj: structs containing callbacks + * + * Return: status of operation + */ +int ucfg_nan_register_lim_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj); + +/** + * ucfg_nan_get_callbacks: ucfg API to return callbacks + * @psoc: pointer to psoc object + * @cb_obj: callback struct to populate + * + * Return: callback struct on success, NULL otherwise + */ +QDF_STATUS ucfg_nan_get_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj); + +/** + * ucfg_nan_discovery_req: ucfg API for NAN Discovery related requests + * @in_req: NAN request + * @req_type: Request type + * + * Return: status of operation + */ +QDF_STATUS ucfg_nan_discovery_req(void *in_req, uint32_t req_type); + +/** + * ucfg_is_nan_disable_supported() - ucfg API to query NAN Disable support + * @psoc: pointer to psoc object + * + * This function returns NAN Disable support status + * + * Return: True if NAN Disable is supported, False otherwise + */ +bool ucfg_is_nan_disable_supported(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_is_nan_dbs_supported() - ucfg API to query NAN DBS support + * @psoc: pointer to psoc object + * + * This function returns NAN DBS support status + * + * Return: True if NAN DBS is supported, False otherwise + */ +bool ucfg_is_nan_dbs_supported(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_is_ndi_dbs_supported() - ucfg API to query NAN Datapath DBS support + * @psoc: pointer to psoc object + * + * This function returns NDI DBS support status + * + * Return: True if NDI DBS is supported, False otherwise + */ +bool ucfg_is_ndi_dbs_supported(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_is_nan_dbs_supported() - ucfg API to query NAN SAP support + * @psoc: pointer to psoc object + * + * This function returns NAN SAP support status + * + * Return: True if NAN SAP is supported, False otherwise + */ +bool ucfg_is_nan_sap_supported(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_is_nan_enable_allowed() - ucfg API to query if NAN Discovery is + * allowed + * @psoc: pointer to psoc object + * @nan_ch_freq: NAN Discovery primary social channel + * + * Return: True if NAN Discovery enable is allowed, False otherwise + */ +bool ucfg_is_nan_enable_allowed(struct wlan_objmgr_psoc *psoc, + uint32_t nan_ch_freq); + +/** + * ucfg_is_nan_disc_active() - ucfg API to query if NAN Discovery is + * active + * @psoc: pointer to psoc object + * + * Return: True if NAN Discovery is active, False otherwise + */ +bool ucfg_is_nan_disc_active(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_nan_set_tgt_caps: ucfg API to set the NAN capabilities of the Target + * @psoc: pointer to psoc object + * @nan_caps: pointer to the structure of NAN capability bits + * + * Return: status of operation + */ +void ucfg_nan_set_tgt_caps(struct wlan_objmgr_psoc *psoc, + struct nan_tgt_caps *nan_caps); + +/** + * ucfg_nan_disable_concurrency: ucfg API to explicitly disable NAN Discovery + * @psoc: pointer to psoc object + * + * Return: None + */ +void ucfg_nan_disable_concurrency(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_nan_register_wma_callbacks: ucfg API to register WMA callbacks + * @psoc: pointer to psoc object + * @cb_obj: Pointer to NAN callback structure + * + * Return: status of operation + */ +int ucfg_nan_register_wma_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj); +/** + * ucfg_nan_check_and_disable_unsupported_ndi: ucfg API to check if NAN Datapath + * is active on multiple NDI's and disable the unsupported concurrencies. + * @psoc: pointer to psoc object + * @force: When set forces NDI disable + * + * Return: QDF_STATUS + */ +QDF_STATUS +ucfg_nan_check_and_disable_unsupported_ndi(struct wlan_objmgr_psoc *psoc, + bool force); + +/** + * ucfg_ndi_remove_entry_from_policy_mgr() - API to remove NDI entry from + * policy manager. + * @vdev: vdev pointer for NDI interface + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_ndi_remove_entry_from_policy_mgr(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_nan_is_enable_disable_in_progress() - Is NAN enable/disable in progress + * @psoc: Pointer to PSOC object + * + * Return: True if NAN discovery enable/disable is in progress, false otherwise + */ +bool ucfg_nan_is_enable_disable_in_progress(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_nan_is_sta_ndp_concurrency_allowed() - Indicates if NDP is allowed + * @psoc: pointer to psoc object + * @vdev: pointer to vdev object + * + * If STA+NDI(NDPs) exist and another NDI tries to establish + * NDP, then reject the second NDI(NDP). + * + * Return: true if allowed, false otherwise + */ +bool ucfg_nan_is_sta_ndp_concurrency_allowed(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_nan_set_vdev_creation_supp_by_fw()- Set the NAN separate vdev psoc param + * @psoc: pointer to psoc object + * @set: True if firmware supports NAN separate vdev feature + * + * Cache the value of set in NAN psoc object param. + * + * Return: None + */ +void +ucfg_nan_set_vdev_creation_supp_by_fw(struct wlan_objmgr_psoc *psoc, bool set); + +/** + * ucfg_nan_is_vdev_creation_allowed()- Get support for NAN vdev creation + * @psoc: pointer to psoc object + * + * Return: True if NAN vdev creation is allowed by host and firmware else false + */ +bool ucfg_nan_is_vdev_creation_allowed(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_nan_is_sta_nan_ndi_4_port_allowed- Get support for 4 port (STA + + * NAN Disc + NDI + NDI) + * @psoc: pointer to psoc object + * + * Return: True if 4 port concurrency allowed or not. + */ +bool ucfg_nan_is_sta_nan_ndi_4_port_allowed(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_disable_nan_discovery() - Disable NAN discovery + * @psoc: pointer to psoc object + * @data: Data to be sent to NAN discovery engine, which runs in firmware + * @data_len: Length of the data + * + * Send NAN disable request to firmware by setting the mandatory + * params(disable_2g_discovery, disable_5g_discovery) along + * with the data, if provided. + * + * Return: status of operation + */ +QDF_STATUS ucfg_disable_nan_discovery(struct wlan_objmgr_psoc *psoc, + uint8_t *data, uint32_t data_len); + +/** + * ucfg_nan_disable_ndi() - Disable the NDI with given vdev_id + * @psoc: pointer to psoc object + * @ndi_vdev_id: vdev_id of the NDI to be disabled + * + * Disable all the NDPs present on the given NDI by sending NDP_END_ALL + * to firmware. Firmwere sends an immediate response(NDP_HOST_UPDATE) with + * ndp_disable param as 1 followed by NDP_END indication for all the NDPs. + * + * Return: status of operation + */ +QDF_STATUS +ucfg_nan_disable_ndi(struct wlan_objmgr_psoc *psoc, uint32_t ndi_vdev_id); + +/** + * ucfg_get_nan_feature_config() - Get NAN feature bitmap + * @psoc: pointer to psoc object + * @nan_feature_config: NAN feature config bitmap to be enabled in firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_get_nan_feature_config(struct wlan_objmgr_psoc *psoc, + uint32_t *nan_feature_config); + +/** + * ucfg_is_nan_vdev() - Check if the current vdev supports NAN or not + * @vdev: pointer to vdev object + * + * Return true + * 1. If the VDEV type is NAN_DISC or + * 2. If the VDEV type is STA and nan_separate_iface feature is not supported + * + * Return: Bool + */ +bool ucfg_is_nan_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_nan_disable_ind_to_userspace() - Send NAN disble ind to userspace + * @psoc: pointer to psoc object + * + * Prepare NAN disable indication and send it to userspace + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_nan_disable_ind_to_userspace(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_is_nan_allowed_on_freq() - Check if NAN is allowed on given freq + * @pdev: pdev context + * @freq: Frequency to be checked + * + * Check if NAN/NDP can be enabled on given frequency. + * Validate SRD channels based on the ini and reg domain. Assume rest of the + * channels support NAN/NDP for now. + * + * Return: True if NAN is allowed on the given frequency + */ +bool ucfg_is_nan_allowed_on_freq(struct wlan_objmgr_pdev *pdev, uint32_t freq); +#else /* WLAN_FEATURE_NAN */ + +static inline +void ucfg_nan_set_tgt_caps(struct wlan_objmgr_psoc *psoc, + struct nan_tgt_caps *nan_caps) +{ +} + +static inline void ucfg_nan_disable_concurrency(struct wlan_objmgr_psoc *psoc) +{ +} + +static inline QDF_STATUS +ucfg_nan_check_and_disable_unsupported_ndi(struct wlan_objmgr_psoc *psoc, + bool force) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS ucfg_nan_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void ucfg_nan_psoc_close(struct wlan_objmgr_psoc *psoc) +{ +} + +static inline bool ucfg_is_nan_disc_active(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline +enum nan_datapath_state ucfg_nan_get_ndi_state(struct wlan_objmgr_vdev *vdev) +{ + return NAN_DATA_INVALID_STATE; +} + +static inline +bool ucfg_nan_is_enable_disable_in_progress(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline +bool ucfg_nan_is_sta_ndp_concurrency_allowed(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev) +{ + return false; +} + +static inline void +ucfg_nan_set_vdev_creation_supp_by_fw(struct wlan_objmgr_psoc *psoc, bool set) +{ +} + +static inline +bool ucfg_nan_is_vdev_creation_allowed(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline +bool ucfg_nan_is_sta_nan_ndi_4_port_allowed(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline +QDF_STATUS ucfg_disable_nan_discovery(struct wlan_objmgr_psoc *psoc, + uint8_t *data, uint32_t data_len) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS +ucfg_nan_disable_ndi(struct wlan_objmgr_psoc *psoc, uint32_t ndi_vdev_id) +{ + return QDF_STATUS_E_INVAL; +} + +static inline +bool ucfg_is_nan_disable_supported(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline +QDF_STATUS ucfg_get_nan_feature_config(struct wlan_objmgr_psoc *psoc, + uint32_t *nan_feature_config) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +bool ucfg_is_nan_vdev(struct wlan_objmgr_vdev *vdev) +{ + return false; +} + +static inline +QDF_STATUS ucfg_nan_disable_ind_to_userspace(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +bool ucfg_is_nan_allowed_on_freq(struct wlan_objmgr_pdev *pdev, uint32_t freq) +{ + return false; +} +#endif /* WLAN_FEATURE_NAN */ +#endif /* _NAN_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/nan/dispatcher/src/cfg_nan.c b/drivers/staging/qcacld-3.0/components/nan/dispatcher/src/cfg_nan.c new file mode 100644 index 0000000000000000000000000000000000000000..efc2fdd5a52babe8843ac613e43edab654484805 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/dispatcher/src/cfg_nan.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains NAN INI configurations + */ + +#include "wlan_objmgr_psoc_obj.h" +#include "cfg_nan_api.h" +#include "../../core/src/nan_main_i.h" +#include "wlan_mlme_ucfg_api.h" +#include "cfg_ucfg_api.h" +#include "cfg_nan.h" + +static inline struct nan_psoc_priv_obj + *cfg_nan_get_priv_obj(struct wlan_objmgr_psoc *psoc) +{ + if (!psoc) { + nan_err("PSOC obj null"); + return NULL; + } + return wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_NAN); +} + +bool cfg_nan_get_enable(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *nan_obj = cfg_nan_get_priv_obj(psoc); + + if (!nan_obj) { + nan_err("NAN obj null"); + return false; + } + return nan_obj->cfg_param.enable; +} + +bool cfg_nan_get_datapath_enable(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *nan_obj = cfg_nan_get_priv_obj(psoc); + + if (!nan_obj) { + nan_err("NAN obj null"); + return false; + } + return nan_obj->cfg_param.dp_enable; +} + +bool cfg_nan_get_ndi_mac_randomize(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *nan_obj = cfg_nan_get_priv_obj(psoc); + + if (!nan_obj) { + nan_err("NAN obj null"); + return false; + } + return nan_obj->cfg_param.ndi_mac_randomize; +} + +QDF_STATUS cfg_nan_get_ndp_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint16_t *val) +{ + struct nan_psoc_priv_obj *nan_obj = cfg_nan_get_priv_obj(psoc); + + if (!nan_obj) { + nan_err("NAN obj null"); + return QDF_STATUS_E_INVAL; + } + + *val = nan_obj->cfg_param.ndp_inactivity_timeout; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS cfg_nan_get_ndp_keepalive_period(struct wlan_objmgr_psoc *psoc, + uint16_t *val) +{ + struct nan_psoc_priv_obj *nan_obj = cfg_nan_get_priv_obj(psoc); + + if (!nan_obj) { + nan_err("NAN obj null"); + return QDF_STATUS_E_INVAL; + } + + *val = nan_obj->cfg_param.ndp_keep_alive_period; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS cfg_nan_get_ndp_max_sessions(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct nan_psoc_priv_obj *nan_obj = cfg_nan_get_priv_obj(psoc); + + if (!nan_obj) { + nan_err("NAN obj null"); + *val = cfg_default(CFG_NDP_MAX_SESSIONS); + return QDF_STATUS_E_INVAL; + } + + *val = nan_obj->cfg_param.max_ndp_sessions; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS cfg_nan_get_max_ndi(struct wlan_objmgr_psoc *psoc, uint32_t *val) +{ + struct nan_psoc_priv_obj *nan_obj = cfg_nan_get_priv_obj(psoc); + + if (!nan_obj) { + nan_err("NAN obj null"); + *val = cfg_default(CFG_NDI_MAX_SUPPORT); + return QDF_STATUS_E_INVAL; + } + + *val = nan_obj->cfg_param.max_ndi; + return QDF_STATUS_SUCCESS; +} + +bool cfg_nan_get_support_mp0_discovery(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *nan_obj = cfg_nan_get_priv_obj(psoc); + + if (!nan_obj) { + nan_err("NAN obj null"); + return false; + } + + return nan_obj->cfg_param.support_mp0_discovery; +} + +bool cfg_nan_is_roam_config_disabled(struct wlan_objmgr_psoc *psoc) +{ + uint32_t sta_roam_disable; + + if (ucfg_mlme_get_roam_disable_config(psoc, &sta_roam_disable) == + QDF_STATUS_SUCCESS) + return sta_roam_disable & LFR3_STA_ROAM_DISABLE_BY_NAN; + + return false; +} diff --git a/drivers/staging/qcacld-3.0/components/nan/dispatcher/src/nan_ucfg_api.c b/drivers/staging/qcacld-3.0/components/nan/dispatcher/src/nan_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..5c150af68f738bee8a87666f3a5f2f1c2698774f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/nan/dispatcher/src/nan_ucfg_api.c @@ -0,0 +1,1280 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains interface definitions for OS_IF layer + */ + +#include "nan_ucfg_api.h" +#include "nan_public_structs.h" +#include "wlan_nan_api.h" +#include "../../core/src/nan_main_i.h" +#include "scheduler_api.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_osif_request_manager.h" +#include "wlan_policy_mgr_api.h" +#include "cfg_ucfg_api.h" +#include "cfg_nan.h" +#include "wlan_mlme_api.h" + +struct wlan_objmgr_psoc; +struct wlan_objmgr_vdev; + +#ifdef WLAN_FEATURE_NAN +/** + * nan_cfg_init() - Initialize NAN config params + * @psoc: Pointer to PSOC Object + * @nan_obj: Pointer to NAN private object + * + * This function initialize NAN config params + */ +static void nan_cfg_init(struct wlan_objmgr_psoc *psoc, + struct nan_psoc_priv_obj *nan_obj) +{ + nan_obj->cfg_param.enable = cfg_get(psoc, CFG_NAN_ENABLE); + nan_obj->cfg_param.support_mp0_discovery = + cfg_get(psoc, + CFG_SUPPORT_MP0_DISCOVERY); + nan_obj->cfg_param.ndp_keep_alive_period = + cfg_get(psoc, + CFG_NDP_KEEP_ALIVE_PERIOD); + nan_obj->cfg_param.max_ndp_sessions = cfg_get(psoc, + CFG_NDP_MAX_SESSIONS); + nan_obj->cfg_param.max_ndi = cfg_get(psoc, CFG_NDI_MAX_SUPPORT); + nan_obj->cfg_param.nan_feature_config = + cfg_get(psoc, CFG_NAN_FEATURE_CONFIG); +} + +/** + * nan_cfg_dp_init() - Initialize NAN Datapath config params + * @psoc: Pointer to PSOC Object + * @nan_obj: Pointer to NAN private object + * + * This function initialize NAN config params + */ +static void nan_cfg_dp_init(struct wlan_objmgr_psoc *psoc, + struct nan_psoc_priv_obj *nan_obj) +{ + nan_obj->cfg_param.dp_enable = cfg_get(psoc, + CFG_NAN_DATAPATH_ENABLE); + nan_obj->cfg_param.ndi_mac_randomize = + cfg_get(psoc, CFG_NAN_RANDOMIZE_NDI_MAC); + nan_obj->cfg_param.ndp_inactivity_timeout = + cfg_get(psoc, CFG_NAN_NDP_INACTIVITY_TIMEOUT); + nan_obj->cfg_param.nan_separate_iface_support = + cfg_get(psoc, CFG_NAN_SEPARATE_IFACE_SUPP); + +} +#else +static void nan_cfg_init(struct wlan_objmgr_psoc *psoc, + struct nan_psoc_priv_obj *nan_obj) +{ +} + +static void nan_cfg_dp_init(struct wlan_objmgr_psoc *psoc, + struct nan_psoc_priv_obj *nan_obj) +{ +} +#endif + +QDF_STATUS ucfg_nan_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *nan_obj = nan_get_psoc_priv_obj(psoc); + + if (!nan_obj) { + nan_err("nan psoc priv object is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + nan_cfg_init(psoc, nan_obj); + nan_cfg_dp_init(psoc, nan_obj); + + return QDF_STATUS_SUCCESS; +} + +void ucfg_nan_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + /* No cleanup required on psoc close for NAN */ +} + +inline QDF_STATUS __ucfg_nan_set_ndi_state(struct wlan_objmgr_vdev *vdev, + enum nan_datapath_state state, + const char *func) +{ + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + enum nan_datapath_state current_state; + + if (!priv_obj) { + nan_err("priv_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + qdf_spin_lock_bh(&priv_obj->lock); + current_state = priv_obj->state; + priv_obj->state = state; + qdf_spin_unlock_bh(&priv_obj->lock); + nan_nofl_debug("%s: ndi state: current: %u, new: %u", func, + current_state, state); + + return QDF_STATUS_SUCCESS; +} + +inline enum nan_datapath_state ucfg_nan_get_ndi_state( + struct wlan_objmgr_vdev *vdev) +{ + enum nan_datapath_state val; + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return NAN_DATA_INVALID_STATE; + } + + qdf_spin_lock_bh(&priv_obj->lock); + val = priv_obj->state; + qdf_spin_unlock_bh(&priv_obj->lock); + + return val; +} + +inline QDF_STATUS ucfg_nan_set_active_peers(struct wlan_objmgr_vdev *vdev, + uint32_t val) +{ + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_spin_lock_bh(&priv_obj->lock); + priv_obj->active_ndp_peers = val; + qdf_spin_unlock_bh(&priv_obj->lock); + + return QDF_STATUS_SUCCESS; +} + +inline uint32_t ucfg_nan_get_active_peers(struct wlan_objmgr_vdev *vdev) +{ + uint32_t val; + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return 0; + } + + qdf_spin_lock_bh(&priv_obj->lock); + val = priv_obj->active_ndp_peers; + qdf_spin_unlock_bh(&priv_obj->lock); + + return val; +} +inline QDF_STATUS ucfg_nan_set_ndp_create_transaction_id( + struct wlan_objmgr_vdev *vdev, uint16_t val) +{ + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_spin_lock_bh(&priv_obj->lock); + priv_obj->ndp_create_transaction_id = val; + qdf_spin_unlock_bh(&priv_obj->lock); + + return QDF_STATUS_SUCCESS; +} + +inline uint16_t ucfg_nan_get_ndp_create_transaction_id( + struct wlan_objmgr_vdev *vdev) +{ + uint16_t val; + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return 0; + } + + qdf_spin_lock_bh(&priv_obj->lock); + val = priv_obj->ndp_create_transaction_id; + qdf_spin_unlock_bh(&priv_obj->lock); + + return val; +} + +inline QDF_STATUS ucfg_nan_set_ndp_delete_transaction_id( + struct wlan_objmgr_vdev *vdev, uint16_t val) +{ + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_spin_lock_bh(&priv_obj->lock); + priv_obj->ndp_delete_transaction_id = val; + qdf_spin_unlock_bh(&priv_obj->lock); + + return QDF_STATUS_SUCCESS; +} + +inline uint16_t ucfg_nan_get_ndp_delete_transaction_id( + struct wlan_objmgr_vdev *vdev) +{ + uint16_t val; + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return 0; + } + + qdf_spin_lock_bh(&priv_obj->lock); + val = priv_obj->ndp_delete_transaction_id; + qdf_spin_unlock_bh(&priv_obj->lock); + + return val; +} + +inline QDF_STATUS ucfg_nan_set_ndi_delete_rsp_reason( + struct wlan_objmgr_vdev *vdev, uint32_t val) +{ + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_spin_lock_bh(&priv_obj->lock); + priv_obj->ndi_delete_rsp_reason = val; + qdf_spin_unlock_bh(&priv_obj->lock); + + return QDF_STATUS_SUCCESS; +} + +inline uint32_t ucfg_nan_get_ndi_delete_rsp_reason( + struct wlan_objmgr_vdev *vdev) +{ + uint32_t val; + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return 0; + } + + qdf_spin_lock_bh(&priv_obj->lock); + val = priv_obj->ndi_delete_rsp_reason; + qdf_spin_unlock_bh(&priv_obj->lock); + + return val; +} + +inline QDF_STATUS ucfg_nan_set_ndi_delete_rsp_status( + struct wlan_objmgr_vdev *vdev, uint32_t val) +{ + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_spin_lock_bh(&priv_obj->lock); + priv_obj->ndi_delete_rsp_status = val; + qdf_spin_unlock_bh(&priv_obj->lock); + + return QDF_STATUS_SUCCESS; +} + +inline uint32_t ucfg_nan_get_ndi_delete_rsp_status( + struct wlan_objmgr_vdev *vdev) +{ + uint32_t val; + struct nan_vdev_priv_obj *priv_obj = nan_get_vdev_priv_obj(vdev); + + if (!priv_obj) { + nan_err("priv_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_spin_lock_bh(&priv_obj->lock); + val = priv_obj->ndi_delete_rsp_status; + qdf_spin_unlock_bh(&priv_obj->lock); + + return val; +} + +inline QDF_STATUS ucfg_nan_get_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj) +{ + struct nan_psoc_priv_obj *psoc_obj = nan_get_psoc_priv_obj(psoc); + + if (!psoc_obj) { + nan_err("nan psoc priv object is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + qdf_spin_lock_bh(&psoc_obj->lock); + qdf_mem_copy(cb_obj, &psoc_obj->cb_obj, sizeof(*cb_obj)); + qdf_spin_unlock_bh(&psoc_obj->lock); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS ucfg_nan_sch_msg_flush_cb(struct scheduler_msg *msg) +{ + struct wlan_objmgr_vdev *vdev = NULL; + + if (!msg || !msg->bodyptr) + return QDF_STATUS_E_NULL_VALUE; + + switch (msg->type) { + case NDP_INITIATOR_REQ: + vdev = ((struct nan_datapath_initiator_req *) + msg->bodyptr)->vdev; + break; + case NDP_RESPONDER_REQ: + vdev = ((struct nan_datapath_responder_req *) + msg->bodyptr)->vdev; + break; + case NDP_END_REQ: + vdev = ((struct nan_datapath_end_req *)msg->bodyptr)->vdev; + break; + case NDP_END_ALL: + vdev = ((struct nan_datapath_end_all_ndps *)msg->bodyptr)->vdev; + break; + default: + nan_err("Invalid NAN msg type during sch flush"); + return QDF_STATUS_E_INVAL; + } + + if (vdev) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + qdf_mem_free(msg->bodyptr); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_nan_req_processor(struct wlan_objmgr_vdev *vdev, + void *in_req, uint32_t req_type) +{ + uint32_t len; + QDF_STATUS status; + struct scheduler_msg msg = {0}; + int err; + struct nan_psoc_priv_obj *psoc_obj = NULL; + struct osif_request *request; + static const struct osif_request_params params = { + .priv_size = 0, + .timeout_ms = WLAN_WAIT_TIME_NDP_END, + }; + + if (!in_req) { + nan_alert("req is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + switch (req_type) { + case NDP_INITIATOR_REQ: + len = sizeof(struct nan_datapath_initiator_req); + break; + case NDP_RESPONDER_REQ: + len = sizeof(struct nan_datapath_responder_req); + break; + case NDP_END_REQ: + len = sizeof(struct nan_datapath_end_req); + psoc_obj = nan_get_psoc_priv_obj(wlan_vdev_get_psoc(vdev)); + if (!psoc_obj) { + nan_err("nan psoc priv object is NULL"); + return QDF_STATUS_E_INVAL; + } + break; + case NDP_END_ALL: + len = sizeof(struct nan_datapath_end_all_ndps); + break; + default: + nan_err("in correct message req type: %d", req_type); + return QDF_STATUS_E_INVAL; + } + + msg.bodyptr = qdf_mem_malloc(len); + if (!msg.bodyptr) { + nan_err("malloc failed"); + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(msg.bodyptr, in_req, len); + msg.type = req_type; + msg.callback = nan_scheduled_msg_handler; + msg.flush_callback = ucfg_nan_sch_msg_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_NAN, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("failed to post msg to NAN component, status: %d", + status); + qdf_mem_free(msg.bodyptr); + return status; + } + + if (req_type == NDP_END_REQ) { + /* Wait for NDP_END indication */ + if (!psoc_obj) { + nan_err("nan psoc priv object is NULL"); + return QDF_STATUS_E_INVAL; + } + request = osif_request_alloc(¶ms); + if (!request) { + nan_err("Request allocation failure"); + return QDF_STATUS_E_NOMEM; + } + psoc_obj->request_context = osif_request_cookie(request); + + nan_debug("Wait for NDP END indication"); + err = osif_request_wait_for_response(request); + if (err) + nan_debug("NAN request timed out: %d", err); + osif_request_put(request); + psoc_obj->request_context = NULL; + } + + return QDF_STATUS_SUCCESS; +} + +void ucfg_nan_datapath_event_handler(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + uint32_t type, void *msg) +{ + struct nan_psoc_priv_obj *psoc_obj = nan_get_psoc_priv_obj(psoc); + + if (!psoc_obj) { + nan_err("nan psoc priv object is NULL"); + return; + } + + psoc_obj->cb_obj.os_if_ndp_event_handler(psoc, vdev, type, msg); +} + +static void ucfg_nan_request_process_cb(void *cookie) +{ + struct osif_request *request; + + request = osif_request_get(cookie); + if (request) { + osif_request_complete(request); + osif_request_put(request); + } else { + nan_debug("Obsolete request (cookie:0x%pK), do nothing", + cookie); + } +} + +int ucfg_nan_register_hdd_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj) +{ + struct nan_psoc_priv_obj *psoc_obj = nan_get_psoc_priv_obj(psoc); + + if (!psoc_obj) { + nan_err("nan psoc priv object is NULL"); + return -EINVAL; + } + + psoc_obj->cb_obj.ndi_open = cb_obj->ndi_open; + psoc_obj->cb_obj.ndi_start = cb_obj->ndi_start; + psoc_obj->cb_obj.ndi_delete = cb_obj->ndi_delete; + psoc_obj->cb_obj.ndi_close = cb_obj->ndi_close; + psoc_obj->cb_obj.drv_ndi_create_rsp_handler = + cb_obj->drv_ndi_create_rsp_handler; + psoc_obj->cb_obj.drv_ndi_delete_rsp_handler = + cb_obj->drv_ndi_delete_rsp_handler; + + psoc_obj->cb_obj.new_peer_ind = cb_obj->new_peer_ind; + psoc_obj->cb_obj.peer_departed_ind = cb_obj->peer_departed_ind; + psoc_obj->cb_obj.os_if_ndp_event_handler = + cb_obj->os_if_ndp_event_handler; + psoc_obj->cb_obj.os_if_nan_event_handler = + cb_obj->os_if_nan_event_handler; + psoc_obj->cb_obj.ucfg_nan_request_process_cb = + ucfg_nan_request_process_cb; + + return 0; +} + +int ucfg_nan_register_lim_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj) +{ + struct nan_psoc_priv_obj *psoc_obj = nan_get_psoc_priv_obj(psoc); + + if (!psoc_obj) { + nan_err("nan psoc priv object is NULL"); + return -EINVAL; + } + + psoc_obj->cb_obj.add_ndi_peer = cb_obj->add_ndi_peer; + psoc_obj->cb_obj.ndp_delete_peers = cb_obj->ndp_delete_peers; + psoc_obj->cb_obj.delete_peers_by_addr = cb_obj->delete_peers_by_addr; + + return 0; +} + +int ucfg_nan_register_wma_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj) +{ + struct nan_psoc_priv_obj *psoc_obj = nan_get_psoc_priv_obj(psoc); + + if (!psoc_obj) { + nan_err("nan psoc priv object is NULL"); + return -EINVAL; + } + + psoc_obj->cb_obj.update_ndi_conn = cb_obj->update_ndi_conn; + + return 0; +} + +void ucfg_nan_set_tgt_caps(struct wlan_objmgr_psoc *psoc, + struct nan_tgt_caps *nan_caps) +{ + struct nan_psoc_priv_obj *psoc_priv = nan_get_psoc_priv_obj(psoc); + + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return; + } + + psoc_priv->nan_caps = *nan_caps; +} + +bool ucfg_is_nan_disable_supported(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_priv; + + psoc_priv = nan_get_psoc_priv_obj(psoc); + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return false; + } + + return (psoc_priv->nan_caps.nan_disable_supported == 1); +} + +bool ucfg_is_nan_dbs_supported(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_priv; + + psoc_priv = nan_get_psoc_priv_obj(psoc); + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return false; + } + + return (psoc_priv->nan_caps.nan_dbs_supported == 1); +} + +bool ucfg_is_ndi_dbs_supported(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_priv; + + psoc_priv = nan_get_psoc_priv_obj(psoc); + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return false; + } + + return (psoc_priv->nan_caps.ndi_dbs_supported == 1); +} + +bool ucfg_is_nan_enable_allowed(struct wlan_objmgr_psoc *psoc, + uint32_t nan_ch_freq) +{ + return nan_is_enable_allowed(psoc, nan_ch_freq); +} + +bool ucfg_is_nan_disc_active(struct wlan_objmgr_psoc *psoc) +{ + return nan_is_disc_active(psoc); +} + +QDF_STATUS ucfg_nan_discovery_req(void *in_req, uint32_t req_type) +{ + struct wlan_objmgr_psoc *psoc; + struct scheduler_msg msg = {0}; + uint32_t len; + QDF_STATUS status; + struct nan_psoc_priv_obj *psoc_priv; + struct osif_request *request = NULL; + static const struct osif_request_params params = { + .priv_size = 0, + .timeout_ms = 4000, + }; + int err; + + if (!in_req) { + nan_alert("NAN Discovery req is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + switch (req_type) { + case NAN_ENABLE_REQ: { + struct nan_enable_req *req = in_req; + + psoc = req->psoc; + psoc_priv = nan_get_psoc_priv_obj(psoc); + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return QDF_STATUS_E_INVAL; + } + + /* + * Take a psoc reference while it is being used by the + * NAN requests. + */ + status = wlan_objmgr_psoc_try_get_ref(psoc, + WLAN_NAN_ID); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("Couldn't obtain psoc ref"); + return status; + } + + status = nan_discovery_pre_enable(psoc, + req->social_chan_2g_freq); + if (QDF_IS_STATUS_SUCCESS(status)) { + len = sizeof(struct nan_enable_req) + + req->params.request_data_len; + } else { + wlan_objmgr_psoc_release_ref(psoc, + WLAN_NAN_ID); + return status; + } + break; + } + case NAN_DISABLE_REQ: { + struct nan_disable_req *req = in_req; + + psoc = req->psoc; + psoc_priv = nan_get_psoc_priv_obj(psoc); + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return QDF_STATUS_E_INVAL; + } + + status = wlan_objmgr_psoc_try_get_ref(psoc, + WLAN_NAN_ID); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("Couldn't obtain psoc ref"); + return status; + } + + status = + nan_set_discovery_state(req->psoc, + NAN_DISC_DISABLE_IN_PROGRESS); + if (QDF_IS_STATUS_SUCCESS(status)) { + len = sizeof(struct nan_disable_req) + + req->params.request_data_len; + } else { + wlan_objmgr_psoc_release_ref(psoc, + WLAN_NAN_ID); + return status; + } + break; + } + case NAN_GENERIC_REQ: { + struct nan_generic_req *req = in_req; + + psoc = req->psoc; + psoc_priv = nan_get_psoc_priv_obj(psoc); + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return QDF_STATUS_E_INVAL; + } + + status = wlan_objmgr_psoc_try_get_ref(psoc, + WLAN_NAN_ID); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("Couldn't obtain psoc ref"); + return status; + } + len = sizeof(struct nan_generic_req) + + req->params.request_data_len; + break; + } + default: + nan_err("in correct message req type: %d", req_type); + return QDF_STATUS_E_INVAL; + } + + msg.bodyptr = qdf_mem_malloc(len); + if (!msg.bodyptr) { + wlan_objmgr_psoc_release_ref(psoc, WLAN_NAN_ID); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_copy(msg.bodyptr, in_req, len); + msg.type = req_type; + msg.callback = nan_discovery_scheduled_handler; + msg.flush_callback = nan_discovery_flush_callback; + + if (req_type == NAN_GENERIC_REQ) + goto post_msg; + + request = osif_request_alloc(¶ms); + if (!request) { + nan_err("Request allocation failure"); + nan_discovery_flush_callback(&msg); + return QDF_STATUS_E_NOMEM; + } + + psoc_priv->request_context = osif_request_cookie(request); + if (req_type == NAN_DISABLE_REQ) + psoc_priv->is_explicit_disable = true; + +post_msg: + status = scheduler_post_message(QDF_MODULE_ID_NAN, + QDF_MODULE_ID_NAN, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("failed to post msg to NAN component, status: %d", + status); + nan_discovery_flush_callback(&msg); + } + + if (req_type != NAN_GENERIC_REQ) { + err = osif_request_wait_for_response(request); + if (err) { + nan_debug("NAN request: %u timed out: %d", + req_type, err); + + if (req_type == NAN_ENABLE_REQ) { + nan_set_discovery_state(psoc, + NAN_DISC_DISABLED); + policy_mgr_check_n_start_opportunistic_timer( + psoc); + } else if (req_type == NAN_DISABLE_REQ) + nan_disable_cleanup(psoc); + } + if (req_type == NAN_DISABLE_REQ) + psoc_priv->is_explicit_disable = false; + osif_request_put(request); + } + + return status; +} + +void ucfg_nan_disable_concurrency(struct wlan_objmgr_psoc *psoc) +{ + struct nan_disable_req nan_req = {0}; + enum nan_disc_state curr_nan_state; + struct nan_psoc_priv_obj *psoc_priv; + QDF_STATUS status; + + if (!psoc) { + nan_err("psoc object is NULL, no action will be taken"); + return; + } + + psoc_priv = nan_get_psoc_priv_obj(psoc); + if (!psoc_priv) { + nan_err("nan psoc priv object is NULL"); + return; + } + + if (!ucfg_is_nan_disable_supported(psoc)) + return; + + qdf_spin_lock_bh(&psoc_priv->lock); + curr_nan_state = nan_get_discovery_state(psoc); + + if (curr_nan_state == NAN_DISC_DISABLED || + curr_nan_state == NAN_DISC_DISABLE_IN_PROGRESS) { + qdf_spin_unlock_bh(&psoc_priv->lock); + return; + } + qdf_spin_unlock_bh(&psoc_priv->lock); + + nan_req.psoc = psoc; + nan_req.disable_2g_discovery = true; + nan_req.disable_5g_discovery = true; + + status = ucfg_nan_discovery_req(&nan_req, NAN_DISABLE_REQ); + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("Unable to disable NAN Discovery"); + return; + } + + nan_debug("NAN Disabled successfully"); +} + +QDF_STATUS +ucfg_nan_disable_ndi(struct wlan_objmgr_psoc *psoc, uint32_t ndi_vdev_id) +{ + enum nan_datapath_state curr_ndi_state; + struct nan_datapath_host_event *event; + struct nan_vdev_priv_obj *ndi_vdev_priv; + struct nan_datapath_end_all_ndps req = {0}; + struct wlan_objmgr_vdev *ndi_vdev; + struct osif_request *request; + QDF_STATUS status; + int err; + static const struct osif_request_params params = { + .priv_size = sizeof(struct nan_datapath_host_event), + .timeout_ms = 1000, + }; + + if (!ucfg_is_ndi_dbs_supported(psoc)) + return QDF_STATUS_SUCCESS; + + ndi_vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, ndi_vdev_id, + WLAN_NAN_ID); + if (!ndi_vdev) { + nan_err("Cannot obtain NDI vdev object!"); + return QDF_STATUS_E_INVAL; + } + + ndi_vdev_priv = nan_get_vdev_priv_obj(ndi_vdev); + if (!ndi_vdev_priv) { + nan_err("ndi vdev priv object is NULL"); + wlan_objmgr_vdev_release_ref(ndi_vdev, WLAN_NAN_ID); + return QDF_STATUS_E_INVAL; + } + curr_ndi_state = ucfg_nan_get_ndi_state(ndi_vdev); + + /* + * Nothing to do if NDI is in DATA_END state. + * Continue cleanup in NAN_DATA_NDI_DELETING_STATE as this API + * can be called from hdd_ndi_delete. + */ + if (curr_ndi_state == NAN_DATA_END_STATE) { + wlan_objmgr_vdev_release_ref(ndi_vdev, WLAN_NAN_ID); + return QDF_STATUS_SUCCESS; + } + ucfg_nan_set_ndi_state(ndi_vdev, NAN_DATA_END_STATE); + + request = osif_request_alloc(¶ms); + if (!request) { + nan_err("Request allocation failure"); + status = QDF_STATUS_E_NOMEM; + goto cleanup; + } + ndi_vdev_priv->disable_context = osif_request_cookie(request); + + req.vdev = ndi_vdev; + status = ucfg_nan_req_processor(NULL, &req, NDP_END_ALL); + + if (QDF_IS_STATUS_ERROR(status)) { + nan_err("Unable to disable NDP's on NDI"); + wlan_objmgr_vdev_release_ref(ndi_vdev, WLAN_NAN_ID); + goto cleanup; + } + + nan_debug("Disabling all NDP's on NDI vdev id - %d", ndi_vdev_id); + + err = osif_request_wait_for_response(request); + if (err) { + nan_err("Disabling NDP's timed out waiting for confirmation"); + status = QDF_STATUS_E_TIMEOUT; + goto cleanup; + } + + event = osif_request_priv(request); + if (!event->ndp_termination_in_progress) { + nan_err("Failed to terminate NDP's on NDI"); + status = QDF_STATUS_E_FAILURE; + } else { + /* + * Host can assume NDP delete is successful and + * remove policy mgr entry + */ + policy_mgr_decr_session_set_pcl(psoc, QDF_NDI_MODE, + ndi_vdev_id); + } + +cleanup: + /* Restore original NDI state in case of failure */ + if (QDF_IS_STATUS_SUCCESS(status)) + ucfg_nan_set_ndi_state(ndi_vdev, NAN_DATA_DISCONNECTED_STATE); + else + ucfg_nan_set_ndi_state(ndi_vdev, curr_ndi_state); + + if (request) + osif_request_put(request); + + return status; +} + +QDF_STATUS +ucfg_nan_check_and_disable_unsupported_ndi(struct wlan_objmgr_psoc *psoc, + bool force) +{ + uint32_t ndi_count, first_ndi_vdev_id, i; + QDF_STATUS status; + + if (!psoc) { + nan_err("psoc object is NULL, no action will be taken"); + return QDF_STATUS_E_INVAL; + } + + if (!ucfg_is_ndi_dbs_supported(psoc)) + return QDF_STATUS_SUCCESS; + + ndi_count = policy_mgr_mode_specific_connection_count(psoc, PM_NDI_MODE, + NULL); + /* NDP force disable is done for unsupported concurrencies: NDI+SAP */ + if (force) { + nan_debug("Force disable all NDPs"); + for (i = 0; i < ndi_count; i++) { + first_ndi_vdev_id = + policy_mgr_mode_specific_vdev_id(psoc, + PM_NDI_MODE); + status = ucfg_nan_disable_ndi(psoc, first_ndi_vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + return status; + } + return QDF_STATUS_SUCCESS; + } + + if (ndi_count < 2) { + nan_debug("No more than one NDI is active, nothing to do..."); + return QDF_STATUS_SUCCESS; + } + + /* + * At least 2 NDI active concurrencies exist. Disable all NDP's on the + * first NDI to support an incoming connection. + */ + first_ndi_vdev_id = policy_mgr_mode_specific_vdev_id(psoc, PM_NDI_MODE); + status = ucfg_nan_disable_ndi(psoc, first_ndi_vdev_id); + + return status; +} + +QDF_STATUS ucfg_ndi_remove_entry_from_policy_mgr(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_psoc *psoc; + struct nan_psoc_priv_obj *psoc_priv_obj; + struct nan_vdev_priv_obj *vdev_priv_obj = nan_get_vdev_priv_obj(vdev); + enum nan_datapath_state state; + uint32_t active_ndp_peers; + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + nan_err("can't get psoc"); + return QDF_STATUS_E_FAILURE; + } + + psoc_priv_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_priv_obj) { + nan_err("psoc_priv_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!vdev_priv_obj) { + nan_err("priv_obj is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_spin_lock_bh(&vdev_priv_obj->lock); + state = vdev_priv_obj->state; + active_ndp_peers = vdev_priv_obj->active_ndp_peers; + qdf_spin_unlock_bh(&vdev_priv_obj->lock); + + if (state == NAN_DATA_NDI_DELETED_STATE && + psoc_priv_obj->nan_caps.ndi_dbs_supported && + active_ndp_peers) { + nan_info("Delete NDP peers: %u and remove NDI from policy mgr", + active_ndp_peers); + policy_mgr_decr_session_set_pcl(psoc, QDF_NDI_MODE, + wlan_vdev_get_id(vdev)); + } + + return QDF_STATUS_SUCCESS; +} + +bool ucfg_nan_is_enable_disable_in_progress(struct wlan_objmgr_psoc *psoc) +{ + enum nan_disc_state nan_state; + + nan_state = nan_get_discovery_state(psoc); + if (nan_state == NAN_DISC_ENABLE_IN_PROGRESS || + nan_state == NAN_DISC_DISABLE_IN_PROGRESS) { + nan_info("NAN enable/disable is in progress, state: %u", + nan_state); + return true; + } + + return false; +} + +#ifdef NDP_SAP_CONCURRENCY_ENABLE +/** + * is_sap_ndp_concurrency_allowed() - Is SAP+NDP allowed + * + * Return: True if the NDP_SAP_CONCURRENCY_ENABLE feature define + * is enabled, false otherwise. + */ +static inline bool is_sap_ndp_concurrency_allowed(void) +{ + return true; +} +#else +static inline bool is_sap_ndp_concurrency_allowed(void) +{ + return false; +} +#endif + +bool ucfg_nan_is_sta_ndp_concurrency_allowed(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev) +{ + uint8_t vdev_id_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t freq_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t ndi_cnt, sta_cnt, id; + + sta_cnt = policy_mgr_mode_specific_connection_count(psoc, + PM_STA_MODE, NULL); + /* Allow if STA is not in connected state */ + if (!sta_cnt) + return true; + + /* Reject if STA+STA is present */ + if (sta_cnt > 1) { + nan_err("STA+STA+NDP concurrency is not allowed"); + return false; + } + + /* + * SAP+NDP concurrency is already validated in hdd_is_ndp_allowed(). + * If SAP+NDP concurrency is enabled, return true from here to avoid + * failure. + */ + if (is_sap_ndp_concurrency_allowed()) + return true; + + ndi_cnt = policy_mgr_get_mode_specific_conn_info(psoc, + freq_list, + vdev_id_list, + PM_NDI_MODE); + + /* Allow if no other NDP peers are present on the NDIs */ + if (!ndi_cnt) + return true; + + /* + * Allow NDP creation if the current NDP request is on + * the NDI which already has an NDP by checking the vdev id of + * the NDIs + */ + for (id = 0; id < ndi_cnt; id++) + if (wlan_vdev_get_id(vdev) == vdev_id_list[id]) + return true; + + /* If the flow reaches here then it is 4th NDI with STA */ + if (!ucfg_nan_is_sta_nan_ndi_4_port_allowed(psoc)) + return false; + + /* The final freq would be provided by FW, it is not known now */ + return policy_mgr_allow_concurrency(psoc, PM_NDI_MODE, 0, + HW_MODE_20_MHZ); +} + +bool +ucfg_nan_is_sta_nan_ndi_4_port_allowed(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return false; + } + + return psoc_nan_obj->nan_caps.sta_nan_ndi_ndi_allowed; +} + +static inline bool +ucfg_is_nan_enabled(struct nan_psoc_priv_obj *psoc_nan_obj) +{ + return psoc_nan_obj->cfg_param.enable; +} + +static inline bool +ucfg_nan_is_vdev_creation_supp_by_fw(struct nan_psoc_priv_obj *psoc_nan_obj) +{ + return psoc_nan_obj->nan_caps.nan_vdev_allowed; +} + +static inline bool +ucfg_nan_is_vdev_creation_supp_by_host(struct nan_psoc_priv_obj *nan_obj) +{ + return nan_obj->cfg_param.nan_separate_iface_support; +} + +static void ucfg_nan_cleanup_all_ndps(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + uint32_t ndi_count, vdev_id, i; + + ndi_count = policy_mgr_mode_specific_connection_count(psoc, PM_NDI_MODE, + NULL); + for (i = 0; i < ndi_count; i++) { + vdev_id = policy_mgr_mode_specific_vdev_id(psoc, PM_NDI_MODE); + status = ucfg_nan_disable_ndi(psoc, vdev_id); + if (status == QDF_STATUS_E_TIMEOUT) + policy_mgr_decr_session_set_pcl(psoc, QDF_NDI_MODE, + vdev_id); + } +} + +QDF_STATUS ucfg_disable_nan_discovery(struct wlan_objmgr_psoc *psoc, + uint8_t *data, uint32_t data_len) +{ + struct nan_disable_req *nan_req; + QDF_STATUS status; + + ucfg_nan_cleanup_all_ndps(psoc); + + nan_req = qdf_mem_malloc(sizeof(*nan_req) + data_len); + if (!nan_req) + return -ENOMEM; + + nan_req->psoc = psoc; + nan_req->disable_2g_discovery = true; + nan_req->disable_5g_discovery = true; + if (data_len && data) { + nan_req->params.request_data_len = data_len; + qdf_mem_copy(nan_req->params.request_data, data, data_len); + } + + status = ucfg_nan_discovery_req(nan_req, NAN_DISABLE_REQ); + + if (QDF_IS_STATUS_SUCCESS(status)) + nan_debug("Successfully sent NAN Disable request"); + else + nan_debug("Unable to send NAN Disable request: %u", status); + + qdf_mem_free(nan_req); + return status; +} + +bool ucfg_nan_is_vdev_creation_allowed(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + bool host_support, fw_support; + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return false; + } + + if (!ucfg_is_nan_enabled(psoc_nan_obj)) { + nan_debug("NAN is not enabled"); + return false; + } + + host_support = ucfg_nan_is_vdev_creation_supp_by_host(psoc_nan_obj); + fw_support = ucfg_nan_is_vdev_creation_supp_by_fw(psoc_nan_obj); + if (!host_support || !fw_support) { + nan_debug("NAN separate vdev%s supported by host,%s supported by firmware", + host_support ? "" : " not", fw_support ? "" : " not"); + return false; + } + + return true; +} + +void +ucfg_nan_set_vdev_creation_supp_by_fw(struct wlan_objmgr_psoc *psoc, bool set) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return; + } + + psoc_nan_obj->nan_caps.nan_vdev_allowed = set; +} + +QDF_STATUS ucfg_get_nan_feature_config(struct wlan_objmgr_psoc *psoc, + uint32_t *nan_feature_config) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + *nan_feature_config = cfg_default(CFG_NAN_FEATURE_CONFIG); + return QDF_STATUS_E_INVAL; + } + + *nan_feature_config = psoc_nan_obj->cfg_param.nan_feature_config; + return QDF_STATUS_SUCCESS; +} + +bool ucfg_is_nan_vdev(struct wlan_objmgr_vdev *vdev) +{ + if (wlan_vdev_mlme_get_opmode(vdev) == QDF_NAN_DISC_MODE || + (!ucfg_nan_is_vdev_creation_allowed(wlan_vdev_get_psoc(vdev)) && + wlan_vdev_mlme_get_opmode(vdev) == QDF_STA_MODE)) + return true; + + return false; +} + +QDF_STATUS ucfg_nan_disable_ind_to_userspace(struct wlan_objmgr_psoc *psoc) +{ + struct nan_psoc_priv_obj *psoc_nan_obj; + struct nan_event_params *disable_ind; + struct nan_disable_ind_msg msg = { + .msg_hdr.msg_id = NAN_MSG_ID_DISABLE_INDICATION, + .reason = 0, /* success */ }; + + psoc_nan_obj = nan_get_psoc_priv_obj(psoc); + if (!psoc_nan_obj) { + nan_err("psoc_nan_obj is null"); + return QDF_STATUS_E_INVAL; + } + + disable_ind = qdf_mem_malloc(sizeof(struct nan_event_params) + + sizeof(msg)); + if (!disable_ind) { + nan_err("failed to alloc disable_ind"); + return QDF_STATUS_E_NOMEM; + } + disable_ind->psoc = psoc, + disable_ind->evt_type = nan_event_id_disable_ind; + disable_ind->buf_len = sizeof(msg); + qdf_mem_copy(disable_ind->buf, &msg, disable_ind->buf_len); + + psoc_nan_obj->cb_obj.os_if_nan_event_handler(disable_ind); + + qdf_mem_free(disable_ind); + return QDF_STATUS_SUCCESS; +} + +bool ucfg_is_nan_allowed_on_freq(struct wlan_objmgr_pdev *pdev, uint32_t freq) +{ + return wlan_is_nan_allowed_on_freq(pdev, freq); +} diff --git a/drivers/staging/qcacld-3.0/components/ocb/core/inc/wlan_ocb_main.h b/drivers/staging/qcacld-3.0/components/ocb/core/inc/wlan_ocb_main.h new file mode 100644 index 0000000000000000000000000000000000000000..e392a9e0d1145e582fcf5760ea78c33957444286 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ocb/core/inc/wlan_ocb_main.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: contains ocb init/deinit public api + */ + +#ifndef _WLAN_OCB_MAIN_API_H_ +#define _WLAN_OCB_MAIN_API_H_ + +#include +#include +#include +#include +#include + +#define ocb_alert_rl(params...) QDF_TRACE_FATAL_RL(QDF_MODULE_ID_OCB, params) +#define ocb_err_rl(params...) QDF_TRACE_ERROR_RL(QDF_MODULE_ID_OCB, params) +#define ocb_warn_rl(params...) QDF_TRACE_WARN_RL(QDF_MODULE_ID_OCB, params) +#define ocb_info_rl(params...) QDF_TRACE_INFO_RL(QDF_MODULE_ID_OCB, params) +#define ocb_debug_rl(params...) QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_OCB, params) + +#define ocb_alert(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_OCB, params) +#define ocb_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_OCB, params) +#define ocb_warn(params...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_OCB, params) +#define ocb_notice(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_OCB, params) +#define ocb_info(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_OCB, params) +#define ocb_debug(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_OCB, params) + +#define ocb_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_OCB, params) +#define ocb_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_OCB, params) +#define ocb_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_OCB, params) +#define ocb_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_OCB, params) +#define ocb_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_OCB, params) + +/** + * enum ocb_southbound_event - OCB south bound event type + * @OCB_CHANNEL_CONFIG_STATUS: set channel config response + * @OCB_TSF_TIMER: get TSF timer response + * @OCB_DCC_STATS_RESPONSE: get DCC stats response + * @OCB_NDL_RESPONSE: NDL update response + * @OCB_DCC_INDICATION: DCC stats indication + */ +enum ocb_southbound_event { + OCB_CHANNEL_CONFIG_STATUS, + OCB_TSF_TIMER, + OCB_DCC_STATS_RESPONSE, + OCB_NDL_RESPONSE, + OCB_DCC_INDICATION, +}; + +/** + * struct ocb_pdev_obj - ocb pdev object + * @pdev: pdev handle + * @ocb_mac: MAC address for different channels + * @ocb_channel_count: channel count + * @channel_config: current channel configurations + * @dp_soc: psoc data path handle + * @dp_pdev_id: pdev data path ID + * @ocb_cbs: legacy callback functions + * @ocb_txops: tx opertions for target interface + * @ocb_rxops: rx opertions for target interface + */ +struct ocb_pdev_obj { + struct wlan_objmgr_pdev *pdev; + struct qdf_mac_addr ocb_mac[QDF_MAX_CONCURRENCY_PERSONA]; + uint32_t ocb_channel_count; + struct ocb_config *channel_config; + void *dp_soc; + uint8_t dp_pdev_id; + struct ocb_callbacks ocb_cbs; + struct wlan_ocb_tx_ops ocb_txops; + struct wlan_ocb_rx_ops ocb_rxops; +}; + +/** + * struct ocb_rx_event - event from south bound + * @psoc: psoc handle + * @vdev: vdev handle + * @evt_id: event ID + * @channel_cfg_rsp: set channel config status + * @tsf_timer: get TSF timer response + * @ndl: NDL DCC response + * @dcc_stats: DCC stats + */ +struct ocb_rx_event { + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + uint32_t evt_id; + union event { + struct ocb_set_config_response channel_cfg_rsp; + struct ocb_get_tsf_timer_response tsf_timer; + struct ocb_dcc_update_ndl_response ndl; + struct ocb_dcc_get_stats_response dcc_stats; + } rsp; +}; + +/** + * wlan_get_pdev_ocb_obj() - private API to get ocb pdev object + * @pdev: pdev object + * + * Return: ocb object + */ +static inline struct ocb_pdev_obj * +wlan_get_pdev_ocb_obj(struct wlan_objmgr_pdev *pdev) +{ + struct ocb_pdev_obj *pdev_obj; + + pdev_obj = (struct ocb_pdev_obj *) + wlan_objmgr_pdev_get_comp_private_obj(pdev, + WLAN_UMAC_COMP_OCB); + + return pdev_obj; +} + +/** + * wlan_ocb_get_callbacks() - get legacy layer callbacks + * @pdev: pdev handle + * + * Return: legacy layer callbacks + */ +static inline struct ocb_callbacks * +wlan_ocb_get_callbacks(struct wlan_objmgr_pdev *pdev) +{ + struct ocb_pdev_obj *pdev_obj; + + pdev_obj = wlan_get_pdev_ocb_obj(pdev); + + if (pdev_obj) + return &pdev_obj->ocb_cbs; + else + return NULL; +} + +/** + * wlan_pdev_get_ocb_tx_ops() - get OCB tx operations + * @pdev: pdev handle + * + * Return: fps to OCB tx operations + */ +static inline struct wlan_ocb_tx_ops * +wlan_pdev_get_ocb_tx_ops(struct wlan_objmgr_pdev *pdev) +{ + struct ocb_pdev_obj *ocb_obj; + + ocb_obj = wlan_get_pdev_ocb_obj(pdev); + + return &ocb_obj->ocb_txops; +} + +/** + * wlan_ocb_release_rx_event() - Release OCB RX event + * @event: OCB RX event + * + * Return: none + */ +static inline void wlan_ocb_release_rx_event(struct ocb_rx_event *event) +{ + + if (!event) { + ocb_err("event is NULL"); + return; + } + + if (event->vdev) + wlan_objmgr_vdev_release_ref(event->vdev, WLAN_OCB_SB_ID); + if (event->psoc) + wlan_objmgr_psoc_release_ref(event->psoc, WLAN_OCB_SB_ID); + qdf_mem_free(event); +} + +/** + * ocb_pdev_obj_create_notification() - OCB pdev object creation notification + * @pdev: pdev handle + * @arg_list: arguments list + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ocb_pdev_obj_create_notification(struct wlan_objmgr_pdev *pdev, + void *arg_list); + +/** + * ocb_pdev_obj_destroy_notification() - OCB pdev object destroy notification + * @pdev: pdev handle + * @arg_list: arguments list + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ocb_pdev_obj_destroy_notification(struct wlan_objmgr_pdev *pdev, + void *arg_list); + +/** + * ocb_config_new() - Creates a new OCB configuration + * @num_channels: the number of channels + * @num_schedule: the schedule size + * @ndl_chan_list_len: length in bytes of the NDL chan blob + * @ndl_active_state_list_len: length in bytes of the active state blob + * + * Return: A pointer to the OCB configuration struct, NULL on failure. + */ +struct ocb_config *ocb_config_new(uint32_t num_channels, + uint32_t num_schedule, + uint32_t ndl_chan_list_len, + uint32_t ndl_active_state_list_len); + +/** + * ocb_copy_config() - Backup current config parameters + * @src: current config parameters + * + * Return: A pointer to the OCB configuration struct, NULL on failure. + */ +struct ocb_config *ocb_copy_config(struct ocb_config *src); + +/** + * ocb_process_evt() - API to process event from south bound + * @msg: south bound message + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ocb_process_evt(struct scheduler_msg *msg); + +/** + * ocb_vdev_start() - start OCB vdev + * @ocb_obj: OCB object + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ocb_vdev_start(struct ocb_pdev_obj *ocb_obj); +#endif diff --git a/drivers/staging/qcacld-3.0/components/ocb/core/src/wlan_ocb_main.c b/drivers/staging/qcacld-3.0/components/ocb/core/src/wlan_ocb_main.c new file mode 100644 index 0000000000000000000000000000000000000000..bca6ff0adcb20ac1bf88c8d20fb0c227d2d75522 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ocb/core/src/wlan_ocb_main.c @@ -0,0 +1,565 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains core ocb function definitions + */ + +#include +#include "scheduler_api.h" +#include "wlan_objmgr_cmn.h" +#include "wlan_objmgr_global_obj.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_vdev_obj.h" +#include +#include +#include +#include "wlan_ocb_main.h" +#include "wlan_ocb_tgt_api.h" +#include "target_if_ocb.h" + +/** + * ocb_get_cmd_type_str() - parse cmd to string + * @cmd_type: ocb cmd type + * + * This function parse ocb cmd to string. + * + * Return: command string + */ +static const char *ocb_get_evt_type_str(enum ocb_southbound_event evt_type) +{ + switch (evt_type) { + case OCB_CHANNEL_CONFIG_STATUS: + return "OCB channel config status"; + case OCB_TSF_TIMER: + return "OCB TSF timer"; + case OCB_DCC_STATS_RESPONSE: + return "OCB DCC get stats response"; + case OCB_NDL_RESPONSE: + return "OCB NDL indication"; + case OCB_DCC_INDICATION: + return "OCB DCC stats indication"; + default: + return "Invalid OCB command"; + } +} + +/** + * ocb_set_chan_info() - Set channel info to dp + * @dp_soc: data path soc handle + * @dp_pdev: data path pdev handle + * @vdev_id: OCB vdev_id + * @config: channel config parameters + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS ocb_set_chan_info(void *dp_soc, + void *dp_pdev, + uint32_t vdev_id, + struct ocb_config *config) +{ + struct ol_txrx_ocb_set_chan ocb_set_chan; + struct ol_txrx_ocb_chan_info *ocb_channel_info; + + if (!dp_soc || !dp_pdev) { + ocb_err("DP global handle is null"); + return QDF_STATUS_E_INVAL; + } + + ocb_set_chan.ocb_channel_count = config->channel_count; + + /* release old settings */ + ocb_channel_info = cdp_get_ocb_chan_info(dp_soc, vdev_id); + if (ocb_channel_info) + qdf_mem_free(ocb_channel_info); + + if (config->channel_count) { + int i, buf_size; + + buf_size = sizeof(*ocb_channel_info) * config->channel_count; + ocb_set_chan.ocb_channel_info = qdf_mem_malloc(buf_size); + if (!ocb_set_chan.ocb_channel_info) { + ocb_err("Failed to allocate buffer for chan info"); + return QDF_STATUS_E_NOMEM; + } + ocb_channel_info = ocb_set_chan.ocb_channel_info; + for (i = 0; i < config->channel_count; i++) { + ocb_channel_info[i].chan_freq = + config->channels[i].chan_freq; + if (config->channels[i].flags & + OCB_CHANNEL_FLAG_DISABLE_RX_STATS_HDR) + ocb_channel_info[i].disable_rx_stats_hdr = 1; + } + } else { + ocb_debug("No channel config to dp"); + ocb_set_chan.ocb_channel_info = NULL; + } + ocb_debug("Sync channel config to dp"); + cdp_set_ocb_chan_info(dp_soc, vdev_id, ocb_set_chan); + + return QDF_STATUS_SUCCESS; +} + +/** + * ocb_channel_config_status() - Process set channel config response + * @evt: response event + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS ocb_channel_config_status(struct ocb_rx_event *evt) +{ + QDF_STATUS status; + uint32_t vdev_id; + struct ocb_rx_event *event; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + struct ocb_pdev_obj *ocb_obj; + struct ocb_callbacks *cbs; + struct ocb_set_config_response config_rsp; + + if (!evt) { + ocb_err("Event buffer is NULL"); + return QDF_STATUS_E_FAILURE; + } + event = evt; + psoc = event->psoc; + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_OCB_SB_ID); + if (!pdev) { + ocb_alert("Pdev is NULL"); + return QDF_STATUS_E_FAILURE; + } + + ocb_obj = wlan_get_pdev_ocb_obj(pdev); + if (!ocb_obj) { + ocb_err("Pdev object is NULL"); + status = QDF_STATUS_E_INVAL; + goto exit; + } + + cbs = &ocb_obj->ocb_cbs; + if (ocb_obj->channel_config) { + vdev_id = ocb_obj->channel_config->vdev_id; + config_rsp = event->rsp.channel_cfg_rsp; + + /* Sync channel status to data path */ + if (config_rsp.status == OCB_CHANNEL_CONFIG_SUCCESS) + ocb_set_chan_info(ocb_obj->dp_soc, + ocb_obj->pdev, + vdev_id, + ocb_obj->channel_config); + qdf_mem_free(ocb_obj->channel_config); + ocb_obj->channel_config = NULL; + } else { + ocb_err("Failed to sync channel info to DP"); + config_rsp.status = OCB_CHANNEL_CONFIG_FAIL; + } + + if (cbs->ocb_set_config_callback) { + cbs->ocb_set_config_callback(cbs->ocb_set_config_context, + &config_rsp); + status = QDF_STATUS_SUCCESS; + } else { + ocb_err("ocb_set_config_resp_cb is NULL"); + status = QDF_STATUS_E_INVAL; + } +exit: + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID); + + return status; +} + +/** + * ocb_tsf_timer() - Process get TSF timer response + * @evt: repsonse event + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS ocb_tsf_timer(struct ocb_rx_event *evt) +{ + QDF_STATUS status; + struct ocb_rx_event *event; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_pdev *pdev; + struct ocb_callbacks *cbs; + struct ocb_get_tsf_timer_response *tsf_timer; + + if (!evt) { + ocb_err("Event buffer is NULL"); + return QDF_STATUS_E_FAILURE; + } + event = evt; + vdev = event->vdev; + pdev = wlan_vdev_get_pdev(vdev); + cbs = wlan_ocb_get_callbacks(pdev); + tsf_timer = &event->rsp.tsf_timer; + ocb_debug("TSF timer low=%d, high=%d", + tsf_timer->timer_low, tsf_timer->timer_high); + if (cbs && cbs->ocb_get_tsf_timer_callback) { + ocb_debug("%s: send TSF timer.", __func__); + cbs->ocb_get_tsf_timer_callback(cbs->ocb_get_tsf_timer_context, + tsf_timer); + status = QDF_STATUS_SUCCESS; + } else { + ocb_err("ocb_get_tsf_timer_cb is NULL"); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +/** + * ocb_dcc_stats_response() - Process get DCC stats response + * @evt: repsonse event + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS ocb_dcc_stats_response(struct ocb_rx_event *evt) +{ + QDF_STATUS status; + struct ocb_rx_event *event; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_pdev *pdev; + struct ocb_callbacks *cbs; + struct ocb_dcc_get_stats_response *dcc_stats; + + if (!evt) { + ocb_err("Event buffer is NULL"); + return QDF_STATUS_E_FAILURE; + } + + event = evt; + vdev = event->vdev; + pdev = wlan_vdev_get_pdev(vdev); + cbs = wlan_ocb_get_callbacks(pdev); + dcc_stats = &event->rsp.dcc_stats; + if (cbs && cbs->ocb_dcc_get_stats_callback) { + ocb_debug("%s: send DCC stats", __func__); + cbs->ocb_dcc_get_stats_callback(cbs->ocb_dcc_get_stats_context, + dcc_stats); + status = QDF_STATUS_SUCCESS; + } else { + ocb_err("dcc_get_stats_cb is NULL"); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +/** + * ocb_ndl_response() - Process NDL update response + * @evt: repsonse event + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS ocb_ndl_response(struct ocb_rx_event *evt) +{ + QDF_STATUS status; + struct ocb_rx_event *event; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_pdev *pdev; + struct ocb_callbacks *cbs; + struct ocb_dcc_update_ndl_response *ndl; + + if (!evt) { + ocb_err("Event buffer is NULL"); + return QDF_STATUS_E_FAILURE; + } + event = evt; + vdev = event->vdev; + pdev = wlan_vdev_get_pdev(vdev); + cbs = wlan_ocb_get_callbacks(pdev); + ndl = &event->rsp.ndl; + if (cbs && cbs->ocb_dcc_update_ndl_callback) { + ocb_debug("%s: NDL update response", __func__); + cbs->ocb_dcc_update_ndl_callback( + cbs->ocb_dcc_update_ndl_context, ndl); + status = QDF_STATUS_SUCCESS; + } else { + ocb_err("dcc_update_ndl is NULL"); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +/** + * ocb_dcc_indication() - Process DCC stats indication + * @evt: repsonse event + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS ocb_dcc_indication(struct ocb_rx_event *evt) +{ + QDF_STATUS status; + struct ocb_rx_event *event; + struct wlan_objmgr_vdev *vdev; + struct ocb_callbacks *cbs; + struct wlan_objmgr_pdev *pdev; + struct ocb_dcc_get_stats_response *dcc_stats; + + if (!evt) { + ocb_err("Event buffer is NULL"); + return QDF_STATUS_E_FAILURE; + } + + event = evt; + vdev = event->vdev; + pdev = wlan_vdev_get_pdev(vdev); + cbs = wlan_ocb_get_callbacks(pdev); + dcc_stats = &event->rsp.dcc_stats; + if (cbs && cbs->ocb_dcc_stats_event_callback) { + ocb_debug("%s: DCC stats indication", __func__); + cbs->ocb_dcc_stats_event_callback( + cbs->ocb_dcc_stats_event_context, dcc_stats); + status = QDF_STATUS_SUCCESS; + } else { + ocb_err("dcc_get_stats_cb is NULL"); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +/** + * ocb_flush_start_msg() - Flush ocb start message + * @msg: OCB start vdev message + * + * Return: QDF_STATUS_SUCCESS on success. + */ +static QDF_STATUS ocb_flush_start_msg(struct scheduler_msg *msg) +{ + struct ocb_pdev_obj *ocb_obj; + + if (!msg) { + ocb_err("Null point for OCB message"); + return QDF_STATUS_E_INVAL; + } + + ocb_obj = msg->bodyptr; + if (ocb_obj && ocb_obj->channel_config) { + ocb_info("release the backed config parameters"); + qdf_mem_free(ocb_obj->channel_config); + ocb_obj->channel_config = NULL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * ocb_process_start_vdev_msg() - Handler for OCB vdev start message + * @msg: OCB start vdev message + * + * Return: QDF_STATUS_SUCCESS on success. + */ +static QDF_STATUS ocb_process_start_vdev_msg(struct scheduler_msg *msg) +{ + QDF_STATUS status; + struct ocb_config *config; + struct ocb_pdev_obj *ocb_obj; + struct ocb_callbacks *ocb_cbs; + struct wlan_objmgr_vdev *vdev; + + if (!msg || !msg->bodyptr) { + ocb_err("invalid message"); + return QDF_STATUS_E_INVAL; + } + + ocb_obj = msg->bodyptr; + ocb_cbs = &ocb_obj->ocb_cbs; + if (!ocb_cbs->start_ocb_vdev) { + ocb_err("No callback to start ocb vdev"); + return QDF_STATUS_E_FAILURE; + } + + config = ocb_obj->channel_config; + if (!config) { + ocb_err("NULL config parameters"); + return QDF_STATUS_E_INVAL; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_pdev(ocb_obj->pdev, + config->vdev_id, + WLAN_OCB_SB_ID); + if (!vdev) { + ocb_err("Cannot get vdev"); + return QDF_STATUS_E_FAILURE; + } + + ocb_debug("Start to send OCB vdev start cmd"); + status = ocb_cbs->start_ocb_vdev(config); + wlan_objmgr_vdev_release_ref(vdev, WLAN_OCB_SB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + ocb_err("Failed to start OCB vdev"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ocb_vdev_start(struct ocb_pdev_obj *ocb_obj) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0}; + + msg.bodyptr = ocb_obj; + msg.callback = ocb_process_start_vdev_msg; + msg.flush_callback = ocb_flush_start_msg; + status = scheduler_post_message(QDF_MODULE_ID_OCB, + QDF_MODULE_ID_OCB, + QDF_MODULE_ID_TARGET_IF, &msg); + + return status; +} + +QDF_STATUS ocb_process_evt(struct scheduler_msg *msg) +{ + QDF_STATUS status; + struct ocb_rx_event *event; + + ocb_debug("msg type %d, %s", msg->type, + ocb_get_evt_type_str(msg->type)); + + if (!(msg->bodyptr)) { + ocb_err("Invalid message body"); + return QDF_STATUS_E_INVAL; + } + event = msg->bodyptr; + switch (msg->type) { + case OCB_CHANNEL_CONFIG_STATUS: + status = ocb_channel_config_status(event); + break; + case OCB_TSF_TIMER: + status = ocb_tsf_timer(event); + break; + case OCB_DCC_STATS_RESPONSE: + status = ocb_dcc_stats_response(event); + break; + case OCB_NDL_RESPONSE: + status = ocb_ndl_response(event); + break; + case OCB_DCC_INDICATION: + status = ocb_dcc_indication(event); + break; + default: + status = QDF_STATUS_E_INVAL; + break; + } + + wlan_ocb_release_rx_event(event); + msg->bodyptr = NULL; + + return status; +} + +struct ocb_config *ocb_copy_config(struct ocb_config *src) +{ + struct ocb_config *dst; + uint32_t length; + uint8_t *cursor; + + length = sizeof(*src) + + src->channel_count * sizeof(*src->channels) + + src->schedule_size * sizeof(*src->schedule) + + src->dcc_ndl_chan_list_len + + src->dcc_ndl_active_state_list_len; + + dst = qdf_mem_malloc(length); + if (!dst) + return NULL; + + *dst = *src; + + cursor = (uint8_t *)dst; + cursor += sizeof(*dst); + dst->channels = (struct ocb_config_chan *)cursor; + cursor += src->channel_count * sizeof(*dst->channels); + qdf_mem_copy(dst->channels, src->channels, + src->channel_count * sizeof(*dst->channels)); + dst->schedule = (struct ocb_config_schdl *)cursor; + cursor += src->schedule_size * sizeof(*dst->schedule); + qdf_mem_copy(dst->schedule, src->schedule, + src->schedule_size * sizeof(*dst->schedule)); + dst->dcc_ndl_chan_list = cursor; + cursor += src->dcc_ndl_chan_list_len; + qdf_mem_copy(dst->dcc_ndl_chan_list, src->dcc_ndl_chan_list, + src->dcc_ndl_chan_list_len); + dst->dcc_ndl_active_state_list = cursor; + cursor += src->dcc_ndl_active_state_list_len; + qdf_mem_copy(dst->dcc_ndl_active_state_list, + src->dcc_ndl_active_state_list, + src->dcc_ndl_active_state_list_len); + + return dst; +} + +QDF_STATUS ocb_pdev_obj_create_notification(struct wlan_objmgr_pdev *pdev, + void *arg_list) +{ + QDF_STATUS status; + struct ocb_pdev_obj *ocb_obj; + + ocb_notice("ocb pdev created"); + ocb_obj = qdf_mem_malloc(sizeof(*ocb_obj)); + if (!ocb_obj) { + ocb_err("Failed to allocate memory for ocb pdev object"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_pdev_component_obj_attach(pdev, + WLAN_UMAC_COMP_OCB, + (void *)ocb_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + ocb_err("Failed to attach pdev ocb component"); + qdf_mem_free(ocb_obj); + return status; + } + ocb_obj->pdev = pdev; + + /* register OCB tx/rx ops */ + tgt_ocb_register_rx_ops(&ocb_obj->ocb_rxops); + target_if_ocb_register_tx_ops(&ocb_obj->ocb_txops); + ocb_notice("ocb pdev attached"); + + return status; +} + +QDF_STATUS ocb_pdev_obj_destroy_notification(struct wlan_objmgr_pdev *pdev, + void *arg_list) +{ + QDF_STATUS status; + struct ocb_pdev_obj *ocb_obj; + + ocb_obj = wlan_objmgr_pdev_get_comp_private_obj(pdev, + WLAN_UMAC_COMP_OCB); + if (!ocb_obj) { + ocb_err("Failed to get ocb pdev object"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_pdev_component_obj_detach(pdev, + WLAN_UMAC_COMP_OCB, + ocb_obj); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to detatch ocb pdev object"); + + qdf_mem_free(ocb_obj); + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/ocb/dispatcher/inc/wlan_ocb_public_structs.h b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/inc/wlan_ocb_public_structs.h new file mode 100644 index 0000000000000000000000000000000000000000..9aa2e48cb1cfde9b23e66c0d47709a57d5bc49ff --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/inc/wlan_ocb_public_structs.h @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: contains ocb structure definitions + */ + +#ifndef _WLAN_OCB_STRUCTS_H_ +#define _WLAN_OCB_STRUCTS_H_ +#include +#include "qca_vendor.h" + +/* Don't add the RX stats header to packets received on this channel */ +#define OCB_CHANNEL_FLAG_DISABLE_RX_STATS_HDR (1 << 0) + +/* The size of the utc time in bytes. */ +#define OCB_SIZE_UTC_TIME (10) + +/* The size of the utc time error in bytes. */ +#define OCB_SIZE_UTC_TIME_ERROR (5) + +/** + * struct ocb_utc_param - parameters to set UTC time + * @vdev_id: vdev id + * @utc_time: number of nanoseconds from Jan 1st 1958 + * @time_error: the error in the UTC time. All 1's for unknown + */ +struct ocb_utc_param { + uint32_t vdev_id; + uint8_t utc_time[OCB_SIZE_UTC_TIME]; + uint8_t time_error[OCB_SIZE_UTC_TIME_ERROR]; +}; + +/** + * struct ocb_timing_advert_param - parameters to start/stop + * timing advertisement + * @vdev_id: vdev id + * @chan_freq: frequency on which to advertise (unit in Mhz) + * @repeat_rate: the number of times it will send TA in 5 seconds + * @timestamp_offset: offset of the timestamp field in the TA frame + * @time_value_offset: offset of the time_value field in the TA frame + * @template_length: size in bytes of the TA frame + * @template_value: the TA frame + */ +struct ocb_timing_advert_param { + uint32_t vdev_id; + uint32_t chan_freq; + uint32_t repeat_rate; + uint32_t timestamp_offset; + uint32_t time_value_offset; + uint32_t template_length; + uint8_t *template_value; +}; + +/** + * struct ocb_dcc_get_stats_param - parameters to get DCC stats + * @vdev_id: vdev id + * @channel_count: number of dcc channels + * @request_array_len: size in bytes of the request array + * @request_array: the request array + */ +struct ocb_dcc_get_stats_param { + uint32_t vdev_id; + uint32_t channel_count; + uint32_t request_array_len; + void *request_array; +}; + +/** + * struct ocb_dcc_update_ndl_param - parameters to update NDL + * @vdev_id: vdev id + * @channel_count: number of channels to be updated + * @dcc_ndl_chan_list_len: size in bytes of the ndl_chan array + * @dcc_ndl_chan_list: the ndl_chan array + * @dcc_ndl_active_state_list_len: size in bytes of the active_state array + * @dcc_ndl_active_state_list: the active state array + */ +struct ocb_dcc_update_ndl_param { + uint32_t vdev_id; + uint32_t channel_count; + uint32_t dcc_ndl_chan_list_len; + void *dcc_ndl_chan_list; + uint32_t dcc_ndl_active_state_list_len; + void *dcc_ndl_active_state_list; +}; + +/** + * struct ocb_config_schdl - parameters for channel scheduling + * @chan_freq: frequency of the channel (unit in Mhz) + * @total_duration: duration of the schedule (unit in ms) + * @guard_interval: guard interval on the start of the schedule (unit in ms) + */ +struct ocb_config_schdl { + uint32_t chan_freq; + uint32_t total_duration; + uint32_t guard_interval; +}; + +/** + * struct ocb_wmm_param - WMM parameters + * @aifsn: AIFS number + * @cwmin: value of CWmin + * @cwmax: value of CWmax + */ +struct ocb_wmm_param { + uint8_t aifsn; + uint8_t cwmin; + uint8_t cwmax; +}; + +/** + * struct ocb_config_chan - parameters to configure a channel + * @chan_freq: frequency of the channel (unit in MHz) + * @bandwidth: bandwidth of the channel, either 10 or 20 MHz + * @mac_address: MAC address assigned to this channel + * @qos_params: QoS parameters + * @max_pwr: maximum transmit power of the channel (dBm) + * @min_pwr: minimum transmit power of the channel (dBm) + * @reg_pwr: maximum transmit power specified by the regulatory domain (dBm) + * @antenna_max: maximum antenna gain specified by the regulatory domain (dB) + * @flags: bit0: 0 enable RX stats on this channel; 1 disable RX stats + * bit1: flag to indicate TSF expiry time in TX control. + * 0 relative time is used. 1 absolute time is used. + * bit2: Frame mode from user layer. + * 0 for 802.3 frame, 1 for 802.11 frame. + * @ch_mode: channel mode + */ +struct ocb_config_chan { + uint32_t chan_freq; + uint32_t bandwidth; + struct qdf_mac_addr mac_address; + struct ocb_wmm_param qos_params[QCA_WLAN_AC_ALL]; + uint32_t max_pwr; + uint32_t min_pwr; + uint8_t reg_pwr; + uint8_t antenna_max; + uint16_t flags; + uint32_t ch_mode; +}; + +/** + * struct ocb_config - parameters for OCB vdev config + * @vdev_id: vdev id + * @channel_count: number of channels + * @schedule_size: size of the channel schedule + * @flags: reserved + * @channels: array of OCB channels + * @schedule: array of OCB schedule elements + * @dcc_ndl_chan_list_len: size in bytes of the ndl_chan array + * @dcc_ndl_chan_list: array of dcc channel info + * @dcc_ndl_active_state_list_len: size in bytes of the active state array + * @dcc_ndl_active_state_list: array of active states + */ +struct ocb_config { + uint32_t vdev_id; + uint32_t channel_count; + uint32_t schedule_size; + uint32_t flags; + struct ocb_config_chan *channels; + struct ocb_config_schdl *schedule; + uint32_t dcc_ndl_chan_list_len; + void *dcc_ndl_chan_list; + uint32_t dcc_ndl_active_state_list_len; + void *dcc_ndl_active_state_list; +}; + +/** + * enum ocb_channel_config_status - ocb config status + * @OCB_CHANNEL_CONFIG_SUCCESS: success + * @OCB_CHANNEL_CONFIG_FAIL: failure + * @OCB_CHANNEL_CONFIG_STATUS_MAX: place holder, not a real status + */ +enum ocb_channel_config_status { + OCB_CHANNEL_CONFIG_SUCCESS = 0, + OCB_CHANNEL_CONFIG_FAIL, + OCB_CHANNEL_CONFIG_STATUS_MAX +}; + +/** + * struct ocb_set_config_response - ocb config status + * @status: response status. OCB_CHANNEL_CONFIG_SUCCESS for success. + */ +struct ocb_set_config_response { + enum ocb_channel_config_status status; +}; + +/** + * struct ocb_get_tsf_timer_response - TSF timer response + * @vdev_id: vdev id + * @timer_high: higher 32-bits of the timer + * @timer_low: lower 32-bits of the timer + */ +struct ocb_get_tsf_timer_response { + uint32_t vdev_id; + uint32_t timer_high; + uint32_t timer_low; +}; + +/** + * struct ocb_get_tsf_timer_param - parameters to get tsf timer + * @vdev_id: vdev id + */ +struct ocb_get_tsf_timer_param { + uint32_t vdev_id; +}; + +/** + * struct ocb_dcc_get_stats_response - DCC stats response + * @vdev_id: vdev id + * @num_channels: number of dcc channels + * @channel_stats_array_len: size in bytes of the stats array + * @channel_stats_array: the stats array + */ +struct ocb_dcc_get_stats_response { + uint32_t vdev_id; + uint32_t num_channels; + uint32_t channel_stats_array_len; + void *channel_stats_array; +}; + +/** + * struct ocb_dcc_clear_stats_param - parameters to clear DCC stats + * @vdev_id: vdev id + * @dcc_stats_bitmap: bitmap of clear option + */ +struct ocb_dcc_clear_stats_param { + uint32_t vdev_id; + uint32_t dcc_stats_bitmap; +}; + +/** + * struct ocb_dcc_update_ndl_response - NDP update response + * @vdev_id: vdev id + * @status: response status + */ +struct ocb_dcc_update_ndl_response { + uint32_t vdev_id; + uint32_t status; +}; + +/** + * struct wlan_ocb_rx_ops - structure containing rx ops for OCB + * @ocb_set_config_status: fp to get channel config status + * @ocb_tsf_timer: fp to get TSF timer + * @ocb_dcc_ndl_update: fp to get NDL update status + * @ocb_dcc_stats_indicate: fp to get DCC stats + */ +struct wlan_ocb_rx_ops { + QDF_STATUS (*ocb_set_config_status)(struct wlan_objmgr_psoc *psoc, + uint32_t status); + QDF_STATUS (*ocb_tsf_timer)(struct wlan_objmgr_psoc *psoc, + struct ocb_get_tsf_timer_response *response); + QDF_STATUS (*ocb_dcc_ndl_update)(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_update_ndl_response *resp); + QDF_STATUS (*ocb_dcc_stats_indicate)(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_get_stats_response *response, bool indicate); +}; + +/** + * struct wlan_ocb_tx_ops - structures containing tx ops for OCB + * @ocb_set_config: fp to set channel config + * @ocb_set_utc_time: fp to set utc time + * @ocb_start_timing_advert: fp to start timing advertisement + * @ocb_stop_timing_advert: fp to stop timing advertisement + * @ocb_get_tsf_timer: fp to get tsf timer + * @ocb_dcc_get_stats: fp to get DCC stats + * @ocb_dcc_clear_stats: fp to clear DCC stats + * @ocb_dcc_update_ndl: fp to update ndl + * @ocb_reg_ev_handler: fp to register event handler + * @ocb_unreg_ev_handler: fp to unregister event handler + */ +struct wlan_ocb_tx_ops { + QDF_STATUS (*ocb_set_config)(struct wlan_objmgr_psoc *psoc, + struct ocb_config *config); + QDF_STATUS (*ocb_set_utc_time)(struct wlan_objmgr_psoc *psoc, + struct ocb_utc_param *utc); + QDF_STATUS (*ocb_start_timing_advert)(struct wlan_objmgr_psoc *psoc, + struct ocb_timing_advert_param *timing_advert); + QDF_STATUS (*ocb_stop_timing_advert)(struct wlan_objmgr_psoc *psoc, + struct ocb_timing_advert_param *timing_advert); + QDF_STATUS (*ocb_get_tsf_timer)(struct wlan_objmgr_psoc *psoc, + struct ocb_get_tsf_timer_param *request); + QDF_STATUS (*ocb_dcc_get_stats)(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_get_stats_param *get_stats_param); + QDF_STATUS (*ocb_dcc_clear_stats)(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_clear_stats_param *clear_stats); + QDF_STATUS (*ocb_dcc_update_ndl)(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_update_ndl_param *update_ndl_param); + QDF_STATUS (*ocb_reg_ev_handler)(struct wlan_objmgr_psoc *psoc, + void *arg); + QDF_STATUS (*ocb_unreg_ev_handler)(struct wlan_objmgr_psoc *psoc, + void *arg); +}; + +typedef void (*ocb_sync_callback)(void *context, void *response); + +/** + * struct ocb_callback - structure containing callback to legacy driver + * @ocb_set_config_context: context for set channel config callback + * @ocb_set_config_callback: set channel config callback + * @ocb_get_tsf_timer_context: context for get tsf timer callback + * @ocb_get_tsf_timer_callback: get tsf timer callback + * @ocb_dcc_get_stats_context: context for get DCC stats callback + * @ocb_dcc_get_stats_callback: get DCC stats callback + * @ocb_dcc_update_ndl_context: context for NDL update callback + * @ocb_dcc_update_ndl_callback: NDL update callback + * @ocb_dcc_stats_event_context: context for DCC stats event callback + * @ocb_dcc_stats_event_callback: DCC stats event callback + * @start_ocb_vdev: start ocb callback + */ +struct ocb_callbacks { + void *ocb_set_config_context; + ocb_sync_callback ocb_set_config_callback; + void *ocb_get_tsf_timer_context; + ocb_sync_callback ocb_get_tsf_timer_callback; + void *ocb_dcc_get_stats_context; + ocb_sync_callback ocb_dcc_get_stats_callback; + void *ocb_dcc_update_ndl_context; + ocb_sync_callback ocb_dcc_update_ndl_callback; + void *ocb_dcc_stats_event_context; + ocb_sync_callback ocb_dcc_stats_event_callback; + QDF_STATUS (*start_ocb_vdev)(struct ocb_config *config); +}; +#endif diff --git a/drivers/staging/qcacld-3.0/components/ocb/dispatcher/inc/wlan_ocb_tgt_api.h b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/inc/wlan_ocb_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..4456f603b944bcd56be9517edee000a017d18197 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/inc/wlan_ocb_tgt_api.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: OCB south bound interface declaration + */ +#ifndef _WLAN_OCB_TGT_API_H +#define _WLAN_OCB_TGT_API_H +#include + +/** + * tgt_ocb_register_ev_handler() - register south bound event handler + * @pdev: pdev handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS tgt_ocb_register_ev_handler(struct wlan_objmgr_pdev *pdev); + +/** + * tgt_ocb_unregister_ev_handler() - unregister south bound event handler + * @pdev: pdev handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS tgt_ocb_unregister_ev_handler(struct wlan_objmgr_pdev *pdev); + +/** + * tgt_ocb_register_rx_ops() - register OCB rx operations + * @ocb_rxops: fps to rx operations + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS tgt_ocb_register_rx_ops(struct wlan_ocb_rx_ops *ocb_rxops); +#endif diff --git a/drivers/staging/qcacld-3.0/components/ocb/dispatcher/inc/wlan_ocb_ucfg_api.h b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/inc/wlan_ocb_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..121c2a25948bb2cb8aa2aa9acb9ed4493f143ce9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/inc/wlan_ocb_ucfg_api.h @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains OCB north bound interface definitions + */ + +#ifndef _WLAN_OCB_UCFG_API_H_ +#define _WLAN_OCB_UCFG_API_H_ +#include +#include "wlan_ocb_public_structs.h" + +#ifdef WLAN_FEATURE_DSRC +/** + * ucfg_ocb_set_channel_config() - send OCB config request + * @vdev: vdev handle + * @config: config parameters + * @set_config_cb: callback for set channel config + * @arg: arguments for the callback + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_set_channel_config(struct wlan_objmgr_vdev *vdev, + struct ocb_config *config, + ocb_sync_callback set_config_cb, + void *arg); + +/** + * ucfg_ocb_set_utc_time() - UCFG API to set UTC time + * @vdev: vdev handle + * @utc: UTC time + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_set_utc_time(struct wlan_objmgr_vdev *vdev, + struct ocb_utc_param *utc); + +/** + * ucfg_ocb_start_timing_advert() - ucfg API to start TA + * @vdev: vdev handle + * @ta: timing advertisement parameters + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_start_timing_advert(struct wlan_objmgr_vdev *vdev, + struct ocb_timing_advert_param *ta); + +/** + * ucfg_ocb_stop_timing_advert() - ucfg API to stop TA + * @vdev: vdev handle + * @ta: timing advertisement parameters + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_stop_timing_advert(struct wlan_objmgr_vdev *vdev, + struct ocb_timing_advert_param *ta); + +/** + * ucfg_ocb_get_tsf_timer() - ucfg API to get tsf timer + * @vdev: vdev handle + * @request: request for TSF timer + * @get_tsf_cb: callback for TSF timer response + * @arg: argument for the ccallback + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_get_tsf_timer(struct wlan_objmgr_vdev *vdev, + struct ocb_get_tsf_timer_param *request, + ocb_sync_callback get_tsf_cb, + void *arg); + +/** + * ucfg_ocb_dcc_get_stats() - get DCC stats + * @vdev: vdev handle + * @request: request for dcc stats + * @dcc_get_stats_cb: callback for get dcc stats response + * @arg: argument for the ccallback + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_dcc_get_stats(struct wlan_objmgr_vdev *vdev, + struct ocb_dcc_get_stats_param *request, + ocb_sync_callback dcc_get_stats_cb, + void *arg); + +/** + * ucfg_ocb_dcc_clear_stats() - Clear DCC stats + * @vdev: vdev handle + * @vdev_id: vdev ID + * @bitmap: bitmap for stats to be cleared + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_dcc_clear_stats(struct wlan_objmgr_vdev *vdev, + uint16_t vdev_id, + uint32_t bitmap); + +/** + * ucfg_ocb_dcc_update_ndl() - ucfg API to update NDL + * @vdev: vdev handle + * @request: request parameters + * @dcc_update_ndl_cb: callback for update resposne + * @arg: argument for the callback + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_dcc_update_ndl(struct wlan_objmgr_vdev *vdev, + struct ocb_dcc_update_ndl_param *request, + ocb_sync_callback dcc_update_ndl_cb, + void *arg); + +/** + * ucfg_ocb_register_for_dcc_stats_event() - register dcc stats + * events callback + * @pdev: pdev handle + * @context: argument for the callback + * @dcc_stats_cb: callback for dcc stats event + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_register_for_dcc_stats_event(struct wlan_objmgr_pdev *pdev, + void *ctx, ocb_sync_callback dcc_stats_cb); +/** + * ucfg_ocb_init() - OCB module initialization + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_init(void); + +/** + * ucfg_ocb_deinit() - OCB module deinitialization + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_deinit(void); + +/** + * ucfg_ocb_config_channel() - Set channel config using after OCB started + * @pdev: pdev handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_config_channel(struct wlan_objmgr_pdev *pdev); + +/** + * ucfg_ocb_register_vdev_start() - register callback to start ocb vdev + * @pdev: pdev handle + * @ocb_start: legacy callback to start ocb vdev + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_register_vdev_start(struct wlan_objmgr_pdev *pdev, + QDF_STATUS (*ocb_start)(struct ocb_config *)); + +/** + * ocb_psoc_enable() - Trigger psoc enable for ocb + * @psoc: objmgr psoc object + * + * Return: QDF status success or failure + */ +QDF_STATUS ocb_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * ocb_psoc_disable() - Trigger psoc disable for ocb + * @psoc: objmgr psoc object + * + * Return: QDF status success or failure + */ +QDF_STATUS ocb_psoc_disable(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_ocb_update_dp_handle() - register DP handle + * @soc: soc handle + * @dp_soc: data path soc handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_update_dp_handle(struct wlan_objmgr_psoc *soc, + void *dp_soc); + +/** + * ucfg_ocb_set_txrx_pdev_id() - register txrx pdev id + * @soc: soc handle + * @pdev_id: data path pdev ID + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS ucfg_ocb_set_txrx_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t pdev_id); +#else +/** + * ucfg_ocb_init() - OCB module initialization + * + * Return: QDF_STATUS_SUCCESS on success + */ +static inline QDF_STATUS ucfg_ocb_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ucfg_ocb_deinit() - OCB module deinitialization + * + * Return: QDF_STATUS_SUCCESS on success + */ +static inline QDF_STATUS ucfg_ocb_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ucfg_ocb_config_channel() - Set channel config using after OCB started + * @pdev: pdev handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +static inline QDF_STATUS +ucfg_ocb_config_channel(struct wlan_objmgr_pdev *pdev) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ocb_psoc_enable() - Trigger psoc enable for ocb + * @psoc: objmgr psoc object + * + * Return: QDF status success or failure + */ +static inline +QDF_STATUS ocb_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ocb_psoc_disable() - Trigger psoc disable for ocb + * @psoc: objmgr psoc object + * + * Return: QDF status success or failure + */ +static inline +QDF_STATUS ocb_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ucfg_ocb_update_dp_handle() - register DP handle + * @soc: soc handle + * @dp_soc: data path soc handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +static inline +QDF_STATUS ucfg_ocb_update_dp_handle(struct wlan_objmgr_psoc *soc, + void *dp_soc) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ucfg_ocb_set_txrx_pdev_id() - register txrx pdev id + * @soc: soc handle + * @pdev_id: data path pdev ID + * + * Return: QDF_STATUS_SUCCESS on success + */ +static inline +QDF_STATUS ucfg_ocb_set_txrx_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t pdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/components/ocb/dispatcher/src/wlan_ocb_tgt_api.c b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/src/wlan_ocb_tgt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..e55027efad78dadc86bf24bd013d62a43f480992 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/src/wlan_ocb_tgt_api.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains ocb south bound interface definitions + */ + +#include +#include +#include +#include +#include +#include "wlan_ocb_public_structs.h" +#include "wlan_ocb_ucfg_api.h" +#include "wlan_ocb_tgt_api.h" +#include "wlan_ocb_main.h" + +/** + * wlan_ocb_flush_callback() - OCB message flash callback + * @msg: OCB message + * + * Return: QDF_STATUS_SUCCESS on success. + */ +static QDF_STATUS wlan_ocb_flush_callback(struct scheduler_msg *msg) +{ + struct ocb_rx_event *event; + + if (!msg) { + ocb_err("Null point for OCB message"); + return QDF_STATUS_E_INVAL; + } + + event = msg->bodyptr; + wlan_ocb_release_rx_event(event); + + return QDF_STATUS_SUCCESS; +} + +/** + * tgt_ocb_channel_config_status() - handler for channel config response + * @psoc: psoc handle + * @status: status for last channel config + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +tgt_ocb_channel_config_status(struct wlan_objmgr_psoc *psoc, + uint32_t status) +{ + QDF_STATUS qdf_status; + struct scheduler_msg msg = {0}; + struct ocb_rx_event *event; + + event = qdf_mem_malloc(sizeof(*event)); + if (!event) { + ocb_err("Memory malloc failed for channel config status"); + return QDF_STATUS_E_NOMEM; + } + + qdf_status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_OCB_SB_ID); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + ocb_err("Failed to get psoc ref"); + wlan_ocb_release_rx_event(event); + return qdf_status; + } + event->psoc = psoc; + event->rsp.channel_cfg_rsp.status = status; + event->evt_id = OCB_CHANNEL_CONFIG_STATUS; + msg.type = OCB_CHANNEL_CONFIG_STATUS; + msg.bodyptr = event; + msg.callback = ocb_process_evt; + msg.flush_callback = wlan_ocb_flush_callback; + qdf_status = scheduler_post_message(QDF_MODULE_ID_OCB, + QDF_MODULE_ID_OCB, + QDF_MODULE_ID_TARGET_IF, &msg); + + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + return QDF_STATUS_SUCCESS; + + ocb_err("failed to send OCB_CHANNEL_CONFIG_STATUS msg"); + wlan_ocb_release_rx_event(event); + + return qdf_status; +} + +/** + * tgt_ocb_get_tsf_timer() - handle for TSF timer response + * @psoc: psoc handle + * @response: TSF timer response + * + * Return: QDF_STATUS_SUCCESS on succcess + */ +static QDF_STATUS +tgt_ocb_get_tsf_timer(struct wlan_objmgr_psoc *psoc, + struct ocb_get_tsf_timer_response *response) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0}; + struct ocb_rx_event *event; + + event = qdf_mem_malloc(sizeof(*event)); + if (!event) { + ocb_err("Memory malloc failed for tsf timer"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_OCB_SB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + ocb_err("Failed to get psoc ref"); + goto flush_ref; + } + event->psoc = psoc; + event->vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + response->vdev_id, + WLAN_OCB_SB_ID); + if (!event->vdev) { + ocb_err("Cannot get vdev handle"); + status = QDF_STATUS_E_FAILURE; + goto flush_ref; + } + + event->evt_id = OCB_TSF_TIMER; + event->rsp.tsf_timer.vdev_id = response->vdev_id; + event->rsp.tsf_timer.timer_high = response->timer_high; + event->rsp.tsf_timer.timer_low = response->timer_low; + msg.type = OCB_TSF_TIMER; + msg.bodyptr = event; + msg.callback = ocb_process_evt; + msg.flush_callback = wlan_ocb_flush_callback; + + status = scheduler_post_message(QDF_MODULE_ID_OCB, + QDF_MODULE_ID_OCB, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + ocb_err("failed to send OCB_TSF_TIMER msg"); +flush_ref: + wlan_ocb_release_rx_event(event); + + return status; +} + +/** + * tgt_ocb_dcc_ndl_update() - handler for NDL update response + * @psoc: psoc handle + * @resp: NDL update response + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +tgt_ocb_dcc_ndl_update(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_update_ndl_response *resp) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0}; + struct ocb_rx_event *event; + + event = qdf_mem_malloc(sizeof(*event)); + if (!event) { + ocb_err("Memory malloc failed for ndl update response"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_OCB_SB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + ocb_err("Failed to get psoc ref"); + goto flush_ref; + } + event->psoc = psoc; + event->vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + resp->vdev_id, + WLAN_OCB_SB_ID); + if (!event->vdev) { + ocb_err("Cannot get vdev handle"); + status = QDF_STATUS_E_FAILURE; + goto flush_ref; + } + + event->evt_id = OCB_NDL_RESPONSE; + qdf_mem_copy(&event->rsp.ndl, resp, sizeof(*resp)); + msg.type = OCB_NDL_RESPONSE; + msg.bodyptr = event; + msg.callback = ocb_process_evt; + msg.flush_callback = wlan_ocb_flush_callback; + + status = scheduler_post_message(QDF_MODULE_ID_OCB, + QDF_MODULE_ID_OCB, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + ocb_err("failed to send OCB_NDL_RESPONSE msg"); +flush_ref: + wlan_ocb_release_rx_event(event); + + return status; +} + +/** + * tgt_ocb_dcc_stats_indicate() - handler for DCC stats indication + * @psoc: psoc handle + * @response: DCC stats + * @bool: true for active query, false for passive indicate + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +tgt_ocb_dcc_stats_indicate(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_get_stats_response *response, + bool active) +{ + QDF_STATUS status; + uint8_t *buf; + uint32_t size; + struct scheduler_msg msg = {0}; + struct ocb_rx_event *event; + + size = sizeof(*event) + + response->channel_stats_array_len; + buf = qdf_mem_malloc(size); + if (!buf) { + ocb_err("Memory malloc failed for dcc indication"); + return QDF_STATUS_E_NOMEM; + } + event = (struct ocb_rx_event *)buf; + status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_OCB_SB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + ocb_err("Failed to get psoc ref"); + goto flush_ref; + } + event->psoc = psoc; + event->vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + response->vdev_id, + WLAN_OCB_SB_ID); + if (!event->vdev) { + ocb_err("Cannot get vdev handle"); + status = QDF_STATUS_E_FAILURE; + goto flush_ref; + } + + event->rsp.dcc_stats.channel_stats_array = + (uint8_t *)&event->rsp.dcc_stats + + sizeof(struct ocb_dcc_get_stats_response); + event->rsp.dcc_stats.vdev_id = response->vdev_id; + event->rsp.dcc_stats.num_channels = response->num_channels; + event->rsp.dcc_stats.channel_stats_array_len = + response->channel_stats_array_len; + qdf_mem_copy(event->rsp.dcc_stats.channel_stats_array, + response->channel_stats_array, + response->channel_stats_array_len); + ocb_debug("Message type is %s", + active ? "Get stats response" : "DCC stats indication"); + if (active) + msg.type = OCB_DCC_STATS_RESPONSE; + else + msg.type = OCB_DCC_INDICATION; + event->evt_id = msg.type; + msg.bodyptr = event; + msg.callback = ocb_process_evt; + msg.flush_callback = wlan_ocb_flush_callback; + + status = scheduler_post_message(QDF_MODULE_ID_OCB, + QDF_MODULE_ID_OCB, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + ocb_err("failed to send DCC stats msg(%d)", msg.type); +flush_ref: + wlan_ocb_release_rx_event(event); + + return status; +} + +QDF_STATUS tgt_ocb_register_ev_handler(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_ocb_tx_ops *ocb_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) { + ocb_err("Null soc handle"); + return QDF_STATUS_E_INVAL; + } + ocb_ops = wlan_pdev_get_ocb_tx_ops(pdev); + if (ocb_ops && ocb_ops->ocb_reg_ev_handler) { + status = ocb_ops->ocb_reg_ev_handler(psoc, NULL); + ocb_debug("register ocb event, status:%d", status); + } else { + ocb_alert("No ocb objects or ocb_reg_ev_handler"); + } + + return status; +} + +QDF_STATUS tgt_ocb_unregister_ev_handler(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_ocb_tx_ops *ocb_ops; + QDF_STATUS status; + + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) { + ocb_err("Null soc handle"); + return QDF_STATUS_E_INVAL; + } + ocb_ops = wlan_pdev_get_ocb_tx_ops(pdev); + if (ocb_ops && ocb_ops->ocb_unreg_ev_handler) { + status = ocb_ops->ocb_unreg_ev_handler(psoc, NULL); + ocb_debug("unregister ocb event, status:%d", status); + } else { + ocb_alert("No ocb objects or ocb_unreg_ev_handler"); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +QDF_STATUS tgt_ocb_register_rx_ops(struct wlan_ocb_rx_ops *ocb_rxops) +{ + ocb_rxops->ocb_set_config_status = tgt_ocb_channel_config_status; + ocb_rxops->ocb_tsf_timer = tgt_ocb_get_tsf_timer; + ocb_rxops->ocb_dcc_ndl_update = tgt_ocb_dcc_ndl_update; + ocb_rxops->ocb_dcc_stats_indicate = tgt_ocb_dcc_stats_indicate; + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/ocb/dispatcher/src/wlan_ocb_ucfg_api.c b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/src/wlan_ocb_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..4595342544bfda5960d2b274f96ef86994bf458c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/ocb/dispatcher/src/wlan_ocb_ucfg_api.c @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains ocb north bound interface definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_ocb_main.h" + +/** + * wlan_ocb_get_tx_ops() - get target interface tx operations + * @pdev: pdev handle + * + * Return: fp to target interface operations + */ +static struct wlan_ocb_tx_ops * +wlan_ocb_get_tx_ops(struct wlan_objmgr_pdev *pdev) +{ + struct ocb_pdev_obj *ocb_obj; + + ocb_obj = wlan_get_pdev_ocb_obj(pdev); + if (!ocb_obj) { + ocb_err("failed to get OCB pdev object"); + return NULL; + } + + return &ocb_obj->ocb_txops; +} + +QDF_STATUS ucfg_ocb_init(void) +{ + QDF_STATUS status; + + ocb_notice("ocb module dispatcher init"); + status = wlan_objmgr_register_pdev_create_handler(WLAN_UMAC_COMP_OCB, + ocb_pdev_obj_create_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + ocb_err("Failed to register pdev create handler for ocb"); + + return status; + } + + status = wlan_objmgr_register_pdev_destroy_handler(WLAN_UMAC_COMP_OCB, + ocb_pdev_obj_destroy_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + ocb_err("Failed to register pdev destroy handler for ocb"); + goto fail_delete_pdev; + } + + return status; + +fail_delete_pdev: + wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_OCB, + ocb_pdev_obj_create_notification, NULL); + + return status; +} + +QDF_STATUS ucfg_ocb_deinit(void) +{ + QDF_STATUS status; + + ocb_notice("ocb module dispatcher deinit"); + status = wlan_objmgr_unregister_pdev_destroy_handler(WLAN_UMAC_COMP_OCB, + ocb_pdev_obj_destroy_notification, NULL); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to unregister pdev destroy handler"); + + status = wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_OCB, + ocb_pdev_obj_create_notification, NULL); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to unregister pdev create handler"); + + return status; +} + +QDF_STATUS ocb_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_objmgr_pdev *pdev; + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_OCB_NB_ID); + if (!pdev) { + ocb_err("Failed to get pdev handle"); + + return QDF_STATUS_E_FAILURE; + } + tgt_ocb_register_ev_handler(pdev); + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_NB_ID); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ocb_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_objmgr_pdev *pdev; + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_OCB_NB_ID); + if (!pdev) { + ocb_err("Failed to get pdev handle"); + return QDF_STATUS_E_FAILURE; + } + tgt_ocb_unregister_ev_handler(pdev); + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_NB_ID); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_ocb_set_txrx_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t pdev_id) +{ + struct wlan_objmgr_pdev *pdev; + struct ocb_pdev_obj *ocb_obj; + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_OCB_NB_ID); + if (!pdev) { + ocb_err("Failed to get pdev handle"); + return QDF_STATUS_E_FAILURE; + } + ocb_obj = wlan_get_pdev_ocb_obj(pdev); + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_NB_ID); + if (!ocb_obj) { + ocb_err("OCB object is NULL"); + return QDF_STATUS_E_FAILURE; + } + ocb_obj->dp_pdev_id = pdev_id; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_ocb_update_dp_handle(struct wlan_objmgr_psoc *psoc, + void *dp_soc) +{ + struct wlan_objmgr_pdev *pdev; + struct ocb_pdev_obj *ocb_obj; + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_OCB_NB_ID); + if (!pdev) { + ocb_err("Failed to get pdev handle"); + return QDF_STATUS_E_FAILURE; + } + ocb_obj = wlan_get_pdev_ocb_obj(pdev); + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_NB_ID); + if (!ocb_obj) { + ocb_err("OCB object is NULL"); + return QDF_STATUS_E_FAILURE; + } + ocb_obj->dp_soc = dp_soc; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_ocb_config_channel(struct wlan_objmgr_pdev *pdev) +{ + QDF_STATUS status; + struct ocb_config *config; + struct ocb_pdev_obj *ocb_obj; + struct wlan_objmgr_psoc *psoc; + struct wlan_ocb_tx_ops *tx_ops; + + ocb_obj = wlan_get_pdev_ocb_obj(pdev); + if (!ocb_obj || !ocb_obj->channel_config) { + ocb_alert("The request could not be found"); + return QDF_STATUS_E_FAILURE; + } + + config = ocb_obj->channel_config; + ocb_debug("Set config to vdev%d", config->vdev_id); + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) { + ocb_err("Null pointer for psoc"); + return QDF_STATUS_E_INVAL; + } + tx_ops = wlan_ocb_get_tx_ops(pdev); + if (tx_ops && tx_ops->ocb_set_config) + status = tx_ops->ocb_set_config(psoc, ocb_obj->channel_config); + else + status = QDF_STATUS_E_IO; + + if (QDF_IS_STATUS_SUCCESS(status)) { + ocb_debug("Set channel cmd is sent to southbound"); + } else { + ocb_err("Failed to set channel config to southbound"); + + if (ocb_obj->channel_config) { + /* + * On success case, backup parameters will be released + * after channel info is synced to DP + */ + ocb_info("release the backed config parameters"); + qdf_mem_free(ocb_obj->channel_config); + ocb_obj->channel_config = NULL; + } + } + + return status; +} + +QDF_STATUS ucfg_ocb_set_channel_config(struct wlan_objmgr_vdev *vdev, + struct ocb_config *config, + ocb_sync_callback set_config_cb, + void *arg) +{ + QDF_STATUS status; + enum wlan_vdev_state state; + uint32_t i; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + struct wlan_ocb_tx_ops *tx_ops; + struct ocb_pdev_obj *ocb_obj; + struct ocb_callbacks *ocb_cbs; + + pdev = wlan_vdev_get_pdev(vdev); + if (!pdev) { + ocb_err("Null pointer for pdev"); + return QDF_STATUS_E_INVAL; + } + ocb_obj = wlan_get_pdev_ocb_obj(pdev); + if (!ocb_obj) { + ocb_alert("Failed to get OCB vdev object"); + return QDF_STATUS_E_IO; + } + + if (!config) { + ocb_err("Invalid config input"); + return QDF_STATUS_E_FAILURE; + } + + for (i = 0; i < config->channel_count; i++) { + if (WLAN_REG_CHAN_TO_BAND(wlan_reg_freq_to_chan(pdev, + config->channels[i].chan_freq)) + == BAND_2G) + config->channels[i].ch_mode = MODE_11G; + else + config->channels[i].ch_mode = MODE_11A; + } + + /* + * backup the new configuration, + * it will be released after target's response + */ + ocb_obj->channel_config = ocb_copy_config(config); + if (!ocb_obj->channel_config) { + ocb_err("Failed to backup config"); + return QDF_STATUS_E_NOMEM; + } + + ocb_cbs = &ocb_obj->ocb_cbs; + ocb_cbs->ocb_set_config_callback = set_config_cb; + ocb_cbs->ocb_set_config_context = arg; + + state = wlan_vdev_mlme_get_state(vdev); + if (state != WLAN_VDEV_S_START) { + /* Vdev is not started, start it */ + ocb_debug("OCB vdev%d is not up", config->vdev_id); + status = ocb_vdev_start(ocb_obj); + } else { + psoc = wlan_vdev_get_psoc(vdev); + tx_ops = wlan_ocb_get_tx_ops(pdev); + if (tx_ops && tx_ops->ocb_set_config) + status = tx_ops->ocb_set_config(psoc, config); + else + status = QDF_STATUS_E_IO; + + ocb_debug("Set config to vdev%d", config->vdev_id); + if (QDF_IS_STATUS_SUCCESS(status)) + ocb_debug("Set channel cmd is sent to southbound"); + else + ocb_err("Failed to set channel config to southbound"); + } + + if (QDF_IS_STATUS_ERROR(status) && ocb_obj->channel_config) { + /* + * On success case, backup parameters will be released + * after channel info is synced to DP + */ + ocb_info("release the backed config parameters"); + qdf_mem_free(ocb_obj->channel_config); + ocb_obj->channel_config = NULL; + } + + return status; +} + +QDF_STATUS ucfg_ocb_set_utc_time(struct wlan_objmgr_vdev *vdev, + struct ocb_utc_param *utc) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + struct wlan_ocb_tx_ops *tx_ops; + + psoc = wlan_vdev_get_psoc(vdev); + pdev = wlan_vdev_get_pdev(vdev); + if (!psoc || !pdev) { + ocb_err("Null pointer for psoc/pdev"); + return QDF_STATUS_E_INVAL; + } + tx_ops = wlan_ocb_get_tx_ops(pdev); + + if (!tx_ops) { + ocb_alert("tx_ops is null"); + return QDF_STATUS_E_IO; + } + if (!tx_ops->ocb_set_utc_time) { + ocb_alert("ocb_set_utc_time is null"); + return QDF_STATUS_E_IO; + } + + status = tx_ops->ocb_set_utc_time(psoc, utc); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to set UTC time to southbound"); + else + ocb_debug("UTC time is sent to southbound"); + + return status; +} + +QDF_STATUS ucfg_ocb_start_timing_advert(struct wlan_objmgr_vdev *vdev, + struct ocb_timing_advert_param *ta) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + struct wlan_ocb_tx_ops *tx_ops; + + psoc = wlan_vdev_get_psoc(vdev); + pdev = wlan_vdev_get_pdev(vdev); + if (!psoc || !pdev) { + ocb_err("Null pointer for psoc/pdev"); + return QDF_STATUS_E_INVAL; + } + tx_ops = wlan_ocb_get_tx_ops(pdev); + if (!tx_ops) { + ocb_alert("tx_ops is null"); + return QDF_STATUS_E_IO; + } + if (!tx_ops->ocb_start_timing_advert) { + ocb_alert("ocb_start_timing_advert is null"); + return QDF_STATUS_E_IO; + } + status = tx_ops->ocb_start_timing_advert(psoc, ta); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to sent start timing advert to southbound"); + else + ocb_debug("Start timing advert is sent to southbound"); + + return status; +} + +QDF_STATUS ucfg_ocb_stop_timing_advert(struct wlan_objmgr_vdev *vdev, + struct ocb_timing_advert_param *ta) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + struct wlan_ocb_tx_ops *tx_ops; + + psoc = wlan_vdev_get_psoc(vdev); + pdev = wlan_vdev_get_pdev(vdev); + if (!psoc || !pdev) { + ocb_err("Null pointer for psoc/pdev"); + return QDF_STATUS_E_INVAL; + } + tx_ops = wlan_ocb_get_tx_ops(pdev); + if (!tx_ops) { + ocb_alert("tx_ops is null"); + return QDF_STATUS_E_IO; + } + if (!tx_ops->ocb_stop_timing_advert) { + ocb_alert("ocb_stop_timing_advert is null"); + return QDF_STATUS_E_IO; + } + status = tx_ops->ocb_stop_timing_advert(psoc, ta); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to sent start timing advert to southbound"); + else + ocb_debug("Start timing advert is sent to southbound"); + + return status; +} + +QDF_STATUS ucfg_ocb_get_tsf_timer(struct wlan_objmgr_vdev *vdev, + struct ocb_get_tsf_timer_param *req, + ocb_sync_callback get_tsf_cb, + void *arg) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct ocb_get_tsf_timer_param request; + struct wlan_ocb_tx_ops *tx_ops; + struct ocb_callbacks *ocb_cbs; + struct wlan_objmgr_pdev *pdev; + + pdev = wlan_vdev_get_pdev(vdev); + if (!pdev) { + ocb_err("Null pointer for pdev"); + return QDF_STATUS_E_INVAL; + } + + tx_ops = wlan_ocb_get_tx_ops(pdev); + if (!tx_ops) { + ocb_alert("tx_ops is null"); + return QDF_STATUS_E_IO; + } + if (!tx_ops->ocb_get_tsf_timer) { + ocb_alert("ocb_get_tsf_timer is null"); + return QDF_STATUS_E_IO; + } + + ocb_cbs = wlan_ocb_get_callbacks(pdev); + ocb_cbs->ocb_get_tsf_timer_context = arg; + ocb_cbs->ocb_get_tsf_timer_callback = get_tsf_cb; + request.vdev_id = req->vdev_id; + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + ocb_err("Null pointer for psoc"); + return QDF_STATUS_E_INVAL; + } + status = tx_ops->ocb_get_tsf_timer(psoc, &request); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to sent get tsf timer to southbound"); + else + ocb_debug("Get tsf timer is sent to southbound"); + + return status; +} + +QDF_STATUS ucfg_ocb_dcc_get_stats(struct wlan_objmgr_vdev *vdev, + struct ocb_dcc_get_stats_param *request, + ocb_sync_callback dcc_get_stats_cb, + void *arg) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + struct ocb_callbacks *ocb_cbs; + struct wlan_ocb_tx_ops *tx_ops; + + pdev = wlan_vdev_get_pdev(vdev); + if (!pdev) { + ocb_err("Null pointer for pdev"); + return QDF_STATUS_E_INVAL; + } + ocb_cbs = wlan_ocb_get_callbacks(pdev); + ocb_cbs->ocb_dcc_get_stats_context = arg; + ocb_cbs->ocb_dcc_get_stats_callback = dcc_get_stats_cb; + + tx_ops = wlan_ocb_get_tx_ops(pdev); + if (!tx_ops) { + ocb_alert("tx_ops is null"); + return QDF_STATUS_E_IO; + } + + if (!tx_ops->ocb_dcc_get_stats) { + ocb_alert("ocb_dcc_get_stats is null"); + return QDF_STATUS_E_IO; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + ocb_err("Null pointer for psoc"); + return QDF_STATUS_E_INVAL; + } + status = tx_ops->ocb_dcc_get_stats(psoc, request); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to sent get dcc stats to southbound"); + else + ocb_debug("Get dcc stats is sent to southbound"); + + return status; +} + +QDF_STATUS ucfg_ocb_dcc_clear_stats(struct wlan_objmgr_vdev *vdev, + uint16_t vdev_id, + uint32_t bitmap) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + struct wlan_ocb_tx_ops *tx_ops; + struct ocb_dcc_clear_stats_param clear_stats_param; + + pdev = wlan_vdev_get_pdev(vdev); + if (!pdev) { + ocb_err("Null pointer for pdev"); + return QDF_STATUS_E_INVAL; + } + tx_ops = wlan_ocb_get_tx_ops(pdev); + if (!tx_ops) { + ocb_alert("tx_ops is null"); + return QDF_STATUS_E_IO; + } + + if (!tx_ops->ocb_dcc_clear_stats) { + ocb_alert("ocb_dcc_clear_stats is null"); + return QDF_STATUS_E_IO; + } + + clear_stats_param.vdev_id = vdev_id; + clear_stats_param.dcc_stats_bitmap = bitmap; + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + ocb_err("Null pointer for psoc"); + return QDF_STATUS_E_INVAL; + } + status = tx_ops->ocb_dcc_clear_stats(psoc, &clear_stats_param); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to sent clear dcc stats to southbound"); + else + ocb_debug("clear dcc stats is sent to southbound"); + + return status; +} + +QDF_STATUS ucfg_ocb_dcc_update_ndl(struct wlan_objmgr_vdev *vdev, + struct ocb_dcc_update_ndl_param *request, + ocb_sync_callback dcc_update_ndl_cb, + void *arg) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct ocb_callbacks *ocb_cbs; + struct wlan_objmgr_pdev *pdev; + struct wlan_ocb_tx_ops *tx_ops; + + pdev = wlan_vdev_get_pdev(vdev); + if (!pdev) { + ocb_err("Null pointer for pdev"); + return QDF_STATUS_E_INVAL; + } + ocb_cbs = wlan_ocb_get_callbacks(pdev); + ocb_cbs->ocb_dcc_update_ndl_context = arg; + ocb_cbs->ocb_dcc_update_ndl_callback = dcc_update_ndl_cb; + + tx_ops = wlan_ocb_get_tx_ops(pdev); + if (!tx_ops) { + ocb_alert("tx_ops is null"); + return QDF_STATUS_E_IO; + } + if (!tx_ops->ocb_dcc_update_ndl) { + ocb_alert("dcc_update_ndl is null"); + return QDF_STATUS_E_IO; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + ocb_err("Null pointer for psoc"); + return QDF_STATUS_E_INVAL; + } + status = tx_ops->ocb_dcc_update_ndl(psoc, request); + if (QDF_IS_STATUS_ERROR(status)) + ocb_err("Failed to sent update ndl to southbound"); + else + ocb_debug("Update ndl is sent to southbound"); + + return status; +} + +QDF_STATUS ucfg_ocb_register_for_dcc_stats_event(struct wlan_objmgr_pdev *pdev, + void *ctx, ocb_sync_callback dcc_stats_cb) +{ + struct ocb_callbacks *ocb_cbs; + + if (!pdev) { + ocb_err("Null pointer for pdev"); + return QDF_STATUS_E_INVAL; + } + ocb_cbs = wlan_ocb_get_callbacks(pdev); + + if (!ocb_cbs) { + ocb_err("Failed to register dcc stats callback"); + return QDF_STATUS_E_FAILURE; + } + ocb_cbs->ocb_dcc_stats_event_context = ctx; + ocb_cbs->ocb_dcc_stats_event_callback = dcc_stats_cb; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_ocb_register_vdev_start(struct wlan_objmgr_pdev *pdev, + QDF_STATUS (*ocb_start)(struct ocb_config *)) +{ + struct ocb_callbacks *ocb_cbs; + + if (!pdev) { + ocb_err("Null pointer for pdev"); + return QDF_STATUS_E_INVAL; + } + ocb_cbs = wlan_ocb_get_callbacks(pdev); + + if (!ocb_cbs) { + ocb_err("Failed to register dcc stats callback"); + return QDF_STATUS_E_FAILURE; + } + ocb_cbs->start_ocb_vdev = ocb_start; + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_main.c b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_main.c new file mode 100644 index 0000000000000000000000000000000000000000..4c835df29231a93947fc69558202ff2b2c5dd8ee --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_main.c @@ -0,0 +1,1513 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains main P2P function definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_main.h" +#include "wlan_p2p_roc.h" +#include "wlan_p2p_off_chan_tx.h" +#include "wlan_p2p_cfg.h" +#include "cfg_ucfg_api.h" + +/** + * p2p_get_cmd_type_str() - parse cmd to string + * @cmd_type: P2P cmd type + * + * This function parse P2P cmd to string. + * + * Return: command string + */ +static char *p2p_get_cmd_type_str(enum p2p_cmd_type cmd_type) +{ + switch (cmd_type) { + case P2P_ROC_REQ: + return "P2P roc request"; + case P2P_CANCEL_ROC_REQ: + return "P2P cancel roc request"; + case P2P_MGMT_TX: + return "P2P mgmt tx request"; + case P2P_MGMT_TX_CANCEL: + return "P2P cancel mgmt tx request"; + case P2P_CLEANUP_ROC: + return "P2P cleanup roc"; + case P2P_CLEANUP_TX: + return "P2P cleanup tx"; + case P2P_SET_RANDOM_MAC: + return "P2P set random mac"; + default: + return "Invalid P2P command"; + } +} + +/** + * p2p_get_event_type_str() - parase event to string + * @event_type: P2P event type + * + * This function parse P2P event to string. + * + * Return: event string + */ +static char *p2p_get_event_type_str(enum p2p_event_type event_type) +{ + switch (event_type) { + case P2P_EVENT_SCAN_EVENT: + return "P2P scan event"; + case P2P_EVENT_MGMT_TX_ACK_CNF: + return "P2P mgmt tx ack event"; + case P2P_EVENT_RX_MGMT: + return "P2P mgmt rx event"; + case P2P_EVENT_LO_STOPPED: + return "P2P lo stop event"; + case P2P_EVENT_NOA: + return "P2P noa event"; + case P2P_EVENT_ADD_MAC_RSP: + return "P2P add mac filter resp event"; + default: + return "Invalid P2P event"; + } +} + +/** + * p2p_psoc_obj_create_notification() - Function to allocate per P2P + * soc private object + * @soc: soc context + * @data: Pointer to data + * + * This function gets called from object manager when psoc is being + * created and creates p2p soc context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_psoc_obj_create_notification( + struct wlan_objmgr_psoc *soc, void *data) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = qdf_mem_malloc(sizeof(*p2p_soc_obj)); + if (!p2p_soc_obj) { + p2p_err("Failed to allocate p2p soc private object"); + return QDF_STATUS_E_NOMEM; + } + + p2p_soc_obj->soc = soc; + + status = wlan_objmgr_psoc_component_obj_attach(soc, + WLAN_UMAC_COMP_P2P, p2p_soc_obj, + QDF_STATUS_SUCCESS); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(p2p_soc_obj); + p2p_err("Failed to attach p2p component, %d", status); + return status; + } + + p2p_debug("p2p soc object create successful, %pK", p2p_soc_obj); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_psoc_obj_destroy_notification() - Free soc private object + * @soc: soc context + * @data: Pointer to data + * + * This function gets called from object manager when psoc is being + * deleted and delete p2p soc context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_psoc_obj_destroy_notification( + struct wlan_objmgr_psoc *soc, void *data) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_soc_obj->soc = NULL; + + status = wlan_objmgr_psoc_component_obj_detach(soc, + WLAN_UMAC_COMP_P2P, p2p_soc_obj); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to detach p2p component, %d", status); + return status; + } + + p2p_debug("destroy p2p soc object, %pK", p2p_soc_obj); + + qdf_mem_free(p2p_soc_obj); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_vdev_obj_create_notification() - Allocate per p2p vdev object + * @vdev: vdev context + * @data: Pointer to data + * + * This function gets called from object manager when vdev is being + * created and creates p2p vdev context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_vdev_obj_create_notification( + struct wlan_objmgr_vdev *vdev, void *data) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj; + QDF_STATUS status; + enum QDF_OPMODE mode; + + if (!vdev) { + p2p_err("vdev context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + if (mode != QDF_P2P_GO_MODE && + mode != QDF_STA_MODE && + mode != QDF_P2P_CLIENT_MODE && + mode != QDF_P2P_DEVICE_MODE) { + p2p_debug("won't create p2p vdev private object for mode %d", + mode); + return QDF_STATUS_SUCCESS; + } + + p2p_vdev_obj = + qdf_mem_malloc(sizeof(*p2p_vdev_obj)); + if (!p2p_vdev_obj) { + p2p_err("Failed to allocate p2p vdev object"); + return QDF_STATUS_E_NOMEM; + } + + p2p_vdev_obj->vdev = vdev; + p2p_vdev_obj->noa_status = true; + p2p_vdev_obj->non_p2p_peer_count = 0; + p2p_init_random_mac_vdev(p2p_vdev_obj); + + status = wlan_objmgr_vdev_component_obj_attach(vdev, + WLAN_UMAC_COMP_P2P, p2p_vdev_obj, + QDF_STATUS_SUCCESS); + if (status != QDF_STATUS_SUCCESS) { + p2p_deinit_random_mac_vdev(p2p_vdev_obj); + qdf_mem_free(p2p_vdev_obj); + p2p_err("Failed to attach p2p component to vdev, %d", + status); + return status; + } + + p2p_debug("p2p vdev object create successful, %pK", p2p_vdev_obj); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_vdev_obj_destroy_notification() - Free per P2P vdev object + * @vdev: vdev context + * @data: Pointer to data + * + * This function gets called from object manager when vdev is being + * deleted and delete p2p vdev context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_vdev_obj_destroy_notification( + struct wlan_objmgr_vdev *vdev, void *data) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj; + QDF_STATUS status; + enum QDF_OPMODE mode; + + if (!vdev) { + p2p_err("vdev context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + if (mode != QDF_P2P_GO_MODE && + mode != QDF_STA_MODE && + mode != QDF_P2P_CLIENT_MODE && + mode != QDF_P2P_DEVICE_MODE){ + p2p_debug("no p2p vdev private object for mode %d", mode); + return QDF_STATUS_SUCCESS; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_debug("p2p vdev object is NULL"); + return QDF_STATUS_SUCCESS; + } + p2p_deinit_random_mac_vdev(p2p_vdev_obj); + + p2p_vdev_obj->vdev = NULL; + + status = wlan_objmgr_vdev_component_obj_detach(vdev, + WLAN_UMAC_COMP_P2P, p2p_vdev_obj); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to detach p2p component, %d", status); + return status; + } + + p2p_debug("destroy p2p vdev object, p2p vdev obj:%pK, noa info:%pK", + p2p_vdev_obj, p2p_vdev_obj->noa_info); + + if (p2p_vdev_obj->noa_info) + qdf_mem_free(p2p_vdev_obj->noa_info); + + qdf_mem_free(p2p_vdev_obj); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_peer_obj_create_notification() - manages peer details per vdev + * @peer: peer object + * @arg: Pointer to private argument - NULL + * + * This function gets called from object manager when peer is being + * created. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_peer_obj_create_notification( + struct wlan_objmgr_peer *peer, void *arg) +{ + struct wlan_objmgr_vdev *vdev; + enum QDF_OPMODE mode; + + if (!peer) { + p2p_err("peer context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + vdev = wlan_peer_get_vdev(peer); + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_GO_MODE) + return QDF_STATUS_SUCCESS; + + p2p_debug("p2p peer object create successful"); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_peer_obj_destroy_notification() - clears peer details per vdev + * @peer: peer object + * @arg: Pointer to private argument - NULL + * + * This function gets called from object manager when peer is being + * destroyed. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_peer_obj_destroy_notification( + struct wlan_objmgr_peer *peer, void *arg) +{ + struct wlan_objmgr_vdev *vdev; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_psoc *psoc; + enum QDF_OPMODE mode; + enum wlan_peer_type peer_type; + uint8_t vdev_id; + + if (!peer) { + p2p_err("peer context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + vdev = wlan_peer_get_vdev(peer); + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_GO_MODE) + return QDF_STATUS_SUCCESS; + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + psoc = wlan_vdev_get_psoc(vdev); + if (!p2p_vdev_obj || !psoc) { + p2p_debug("p2p_vdev_obj:%pK psoc:%pK", p2p_vdev_obj, psoc); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + + peer_type = wlan_peer_get_peer_type(peer); + + if ((peer_type == WLAN_PEER_STA) && (mode == QDF_P2P_GO_MODE)) { + + p2p_vdev_obj->non_p2p_peer_count--; + + if (!p2p_vdev_obj->non_p2p_peer_count && + (p2p_vdev_obj->noa_status == false)) { + + vdev_id = wlan_vdev_get_id(vdev); + + if (ucfg_p2p_set_noa(psoc, vdev_id, + false) == QDF_STATUS_SUCCESS) + p2p_vdev_obj->noa_status = true; + else + p2p_vdev_obj->noa_status = false; + + p2p_debug("Non p2p peer disconnected from GO,NOA status: %d.", + p2p_vdev_obj->noa_status); + } + p2p_debug("Non P2P peer count: %d", + p2p_vdev_obj->non_p2p_peer_count); + } + p2p_debug("p2p peer object destroy successful"); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_send_noa_to_pe() - send noa information to pe + * @noa_info: vdev context + * + * This function sends noa information to pe since MCL layer need noa + * event. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_send_noa_to_pe(struct p2p_noa_info *noa_info) +{ + struct p2p_noa_attr *noa_attr; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + if (!noa_info) { + p2p_err("noa info is null"); + return QDF_STATUS_E_INVAL; + } + + noa_attr = qdf_mem_malloc(sizeof(*noa_attr)); + if (!noa_attr) { + p2p_err("Failed to allocate memory for tSirP2PNoaAttr"); + return QDF_STATUS_E_NOMEM; + } + + noa_attr->index = noa_info->index; + noa_attr->opps_ps = noa_info->opps_ps; + noa_attr->ct_win = noa_info->ct_window; + if (!noa_info->num_desc) { + p2p_debug("Zero noa descriptors"); + } else { + p2p_debug("%d noa descriptors", noa_info->num_desc); + + noa_attr->noa1_count = + noa_info->noa_desc[0].type_count; + noa_attr->noa1_duration = + noa_info->noa_desc[0].duration; + noa_attr->noa1_interval = + noa_info->noa_desc[0].interval; + noa_attr->noa1_start_time = + noa_info->noa_desc[0].start_time; + if (noa_info->num_desc > 1) { + noa_attr->noa2_count = + noa_info->noa_desc[1].type_count; + noa_attr->noa2_duration = + noa_info->noa_desc[1].duration; + noa_attr->noa2_interval = + noa_info->noa_desc[1].interval; + noa_attr->noa2_start_time = + noa_info->noa_desc[1].start_time; + } + } + + p2p_debug("Sending P2P_NOA_ATTR_IND to pe"); + + msg.type = P2P_NOA_ATTR_IND; + msg.bodyval = 0; + msg.bodyptr = noa_attr; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_PE, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(noa_attr); + p2p_err("post msg fail:%d", status); + } + + return status; +} + +/** + * process_peer_for_noa() - disable NoA + * @vdev: vdev object + * @psoc: soc object + * @peer: peer object + * + * This function disables NoA + * + * + * Return: QDF_STATUS + */ +static QDF_STATUS process_peer_for_noa(struct wlan_objmgr_vdev *vdev, + struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj = NULL; + enum QDF_OPMODE mode; + enum wlan_peer_type peer_type; + bool disable_noa; + uint8_t vdev_id; + + if (!vdev || !psoc || !peer) { + p2p_err("vdev:%pK psoc:%pK peer:%pK", vdev, psoc, peer); + return QDF_STATUS_E_INVAL; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) + return QDF_STATUS_E_INVAL; + + mode = wlan_vdev_mlme_get_opmode(vdev); + + peer_type = wlan_peer_get_peer_type(peer); + if (peer_type == WLAN_PEER_STA) + p2p_vdev_obj->non_p2p_peer_count++; + + disable_noa = ((mode == QDF_P2P_GO_MODE) + && p2p_vdev_obj->non_p2p_peer_count + && p2p_vdev_obj->noa_status); + + if (disable_noa && (peer_type == WLAN_PEER_STA)) { + + vdev_id = wlan_vdev_get_id(vdev); + + if (ucfg_p2p_set_noa(psoc, vdev_id, + true) == QDF_STATUS_SUCCESS) { + p2p_vdev_obj->noa_status = false; + } else { + p2p_vdev_obj->noa_status = true; + } + p2p_debug("NoA status: %d", p2p_vdev_obj->noa_status); + } + p2p_debug("process_peer_for_noa"); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_object_init_params() - init parameters for p2p object + * @psoc: pointer to psoc object + * @p2p_soc_obj: pointer to p2p psoc object + * + * This function init parameters for p2p object + */ +static QDF_STATUS p2p_object_init_params( + struct wlan_objmgr_psoc *psoc, + struct p2p_soc_priv_obj *p2p_soc_obj) +{ + if (!psoc || !p2p_soc_obj) { + p2p_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj->param.go_keepalive_period = + cfg_get(psoc, CFG_GO_KEEP_ALIVE_PERIOD); + p2p_soc_obj->param.go_link_monitor_period = + cfg_get(psoc, CFG_GO_LINK_MONITOR_PERIOD); + p2p_soc_obj->param.p2p_device_addr_admin = + cfg_get(psoc, CFG_P2P_DEVICE_ADDRESS_ADMINISTRATED); + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * wlan_p2p_init_connection_status() - init connection status + * @p2p_soc_obj: pointer to p2p psoc object + * + * This function initial p2p connection status. + * + * Return: None + */ +static void wlan_p2p_init_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj) +{ + if (!p2p_soc_obj) { + p2p_err("invalid p2p soc obj"); + return; + } + + p2p_soc_obj->connection_status = P2P_NOT_ACTIVE; +} +#else +static void wlan_p2p_init_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj) +{ +} +#endif /* WLAN_FEATURE_P2P_DEBUG */ + +QDF_STATUS p2p_component_init(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p obj create handler"); + goto err_reg_psoc_create; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p obj delete handler"); + goto err_reg_psoc_delete; + } + + status = wlan_objmgr_register_vdev_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p vdev create handler"); + goto err_reg_vdev_create; + } + + status = wlan_objmgr_register_vdev_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p vdev delete handler"); + goto err_reg_vdev_delete; + } + + status = wlan_objmgr_register_peer_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_peer_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p peer create handler"); + goto err_reg_peer_create; + } + + status = wlan_objmgr_register_peer_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_peer_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p peer destroy handler"); + goto err_reg_peer_destroy; + } + + p2p_debug("Register p2p obj handler successful"); + + return QDF_STATUS_SUCCESS; +err_reg_peer_destroy: + wlan_objmgr_unregister_peer_create_handler(WLAN_UMAC_COMP_P2P, + p2p_peer_obj_create_notification, NULL); +err_reg_peer_create: + wlan_objmgr_unregister_vdev_destroy_handler(WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_destroy_notification, NULL); +err_reg_vdev_delete: + wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_create_notification, NULL); +err_reg_vdev_create: + wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_destroy_notification, NULL); +err_reg_psoc_delete: + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_create_notification, NULL); +err_reg_psoc_create: + return status; +} + +QDF_STATUS p2p_component_deinit(void) +{ + QDF_STATUS status; + QDF_STATUS ret_status = QDF_STATUS_SUCCESS; + + status = wlan_objmgr_unregister_vdev_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to unregister p2p vdev create handler, %d", + status); + ret_status = status; + } + + status = wlan_objmgr_unregister_vdev_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to unregister p2p vdev delete handler, %d", + status); + ret_status = status; + } + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to unregister p2p obj create handler, %d", + status); + ret_status = status; + } + + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to unregister p2p obj delete handler, %d", + status); + ret_status = status; + } + + p2p_debug("Unregister p2p obj handler complete"); + + return ret_status; +} + +QDF_STATUS p2p_psoc_object_open(struct wlan_objmgr_psoc *soc) +{ + QDF_STATUS status; + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc priviate object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_object_init_params(soc, p2p_soc_obj); + qdf_list_create(&p2p_soc_obj->roc_q, MAX_QUEUE_LENGTH); + qdf_list_create(&p2p_soc_obj->tx_q_roc, MAX_QUEUE_LENGTH); + qdf_list_create(&p2p_soc_obj->tx_q_ack, MAX_QUEUE_LENGTH); + + status = qdf_event_create(&p2p_soc_obj->cleanup_roc_done); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to create cleanup roc done event"); + goto fail_cleanup_roc; + } + + status = qdf_event_create(&p2p_soc_obj->cleanup_tx_done); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to create cleanup roc done event"); + goto fail_cleanup_tx; + } + + qdf_runtime_lock_init(&p2p_soc_obj->roc_runtime_lock); + p2p_soc_obj->cur_roc_vdev_id = P2P_INVALID_VDEV_ID; + qdf_idr_create(&p2p_soc_obj->p2p_idr); + + p2p_debug("p2p psoc object open successful"); + + return QDF_STATUS_SUCCESS; + +fail_cleanup_tx: + qdf_event_destroy(&p2p_soc_obj->cleanup_roc_done); + +fail_cleanup_roc: + qdf_list_destroy(&p2p_soc_obj->tx_q_ack); + qdf_list_destroy(&p2p_soc_obj->tx_q_roc); + qdf_list_destroy(&p2p_soc_obj->roc_q); + + return status; +} + +QDF_STATUS p2p_psoc_object_close(struct wlan_objmgr_psoc *soc) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + qdf_idr_destroy(&p2p_soc_obj->p2p_idr); + qdf_runtime_lock_deinit(&p2p_soc_obj->roc_runtime_lock); + qdf_event_destroy(&p2p_soc_obj->cleanup_tx_done); + qdf_event_destroy(&p2p_soc_obj->cleanup_roc_done); + qdf_list_destroy(&p2p_soc_obj->tx_q_ack); + qdf_list_destroy(&p2p_soc_obj->tx_q_roc); + qdf_list_destroy(&p2p_soc_obj->roc_q); + + p2p_debug("p2p psoc object close successful"); + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +static inline void p2p_init_lo_event(struct p2p_start_param *start_param, + struct p2p_start_param *req) +{ + start_param->lo_event_cb = req->lo_event_cb; + start_param->lo_event_cb_data = req->lo_event_cb_data; +} +#else +static inline void p2p_init_lo_event(struct p2p_start_param *start_param, + struct p2p_start_param *req) +{ +} +#endif + +QDF_STATUS p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("P2P soc object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + start_param = qdf_mem_malloc(sizeof(*start_param)); + if (!start_param) { + p2p_err("Failed to allocate start params"); + return QDF_STATUS_E_NOMEM; + } + start_param->rx_cb = req->rx_cb; + start_param->rx_cb_data = req->rx_cb_data; + start_param->event_cb = req->event_cb; + start_param->event_cb_data = req->event_cb_data; + start_param->tx_cnf_cb = req->tx_cnf_cb; + start_param->tx_cnf_cb_data = req->tx_cnf_cb_data; + p2p_init_lo_event(start_param, req); + p2p_soc_obj->start_param = start_param; + + wlan_p2p_init_connection_status(p2p_soc_obj); + + /* register p2p lo stop and noa event */ + tgt_p2p_register_lo_ev_handler(soc); + tgt_p2p_register_noa_ev_handler(soc); + tgt_p2p_register_macaddr_rx_filter_evt_handler(soc, true); + + /* register scan request id */ + p2p_soc_obj->scan_req_id = ucfg_scan_register_requester( + soc, P2P_MODULE_NAME, tgt_p2p_scan_event_cb, + p2p_soc_obj); + + /* register rx action frame */ + p2p_mgmt_rx_action_ops(soc, true); + + p2p_debug("p2p psoc start successful, scan request id:%d", + p2p_soc_obj->scan_req_id); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_psoc_stop(struct wlan_objmgr_psoc *soc) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("P2P soc object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + start_param = p2p_soc_obj->start_param; + p2p_soc_obj->start_param = NULL; + if (!start_param) { + p2p_err("start parameters is NULL"); + return QDF_STATUS_E_FAILURE; + } + + /* unregister rx action frame */ + p2p_mgmt_rx_action_ops(soc, false); + + /* clean up queue of p2p psoc private object */ + p2p_cleanup_tx_sync(p2p_soc_obj, NULL); + p2p_cleanup_roc_sync(p2p_soc_obj, NULL); + + /* unrgister scan request id*/ + ucfg_scan_unregister_requester(soc, p2p_soc_obj->scan_req_id); + + /* unregister p2p lo stop and noa event */ + tgt_p2p_register_macaddr_rx_filter_evt_handler(soc, false); + tgt_p2p_unregister_lo_ev_handler(soc); + tgt_p2p_unregister_noa_ev_handler(soc); + + start_param->rx_cb = NULL; + start_param->rx_cb_data = NULL; + start_param->event_cb = NULL; + start_param->event_cb_data = NULL; + start_param->tx_cnf_cb = NULL; + start_param->tx_cnf_cb_data = NULL; + qdf_mem_free(start_param); + + p2p_debug("p2p psoc stop successful"); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_cmd(struct scheduler_msg *msg) +{ + QDF_STATUS status; + + p2p_debug("msg type %d, %s", msg->type, + p2p_get_cmd_type_str(msg->type)); + + if (!(msg->bodyptr)) { + p2p_err("Invalid message body"); + return QDF_STATUS_E_INVAL; + } + switch (msg->type) { + case P2P_ROC_REQ: + status = p2p_process_roc_req( + (struct p2p_roc_context *) + msg->bodyptr); + break; + case P2P_CANCEL_ROC_REQ: + status = p2p_process_cancel_roc_req( + (struct cancel_roc_context *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case P2P_MGMT_TX: + status = p2p_process_mgmt_tx( + (struct tx_action_context *) + msg->bodyptr); + break; + case P2P_MGMT_TX_CANCEL: + status = p2p_process_mgmt_tx_cancel( + (struct cancel_roc_context *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case P2P_CLEANUP_ROC: + status = p2p_process_cleanup_roc_queue( + (struct p2p_cleanup_param *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case P2P_CLEANUP_TX: + status = p2p_process_cleanup_tx_queue( + (struct p2p_cleanup_param *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case P2P_SET_RANDOM_MAC: + status = p2p_process_set_rand_mac(msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + + default: + p2p_err("drop unexpected message received %d", + msg->type); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +QDF_STATUS p2p_process_evt(struct scheduler_msg *msg) +{ + QDF_STATUS status; + + p2p_debug("msg type %d, %s", msg->type, + p2p_get_event_type_str(msg->type)); + + if (!(msg->bodyptr)) { + p2p_err("Invalid message body"); + return QDF_STATUS_E_INVAL; + } + + switch (msg->type) { + case P2P_EVENT_MGMT_TX_ACK_CNF: + status = p2p_process_mgmt_tx_ack_cnf( + (struct p2p_tx_conf_event *) + msg->bodyptr); + break; + case P2P_EVENT_RX_MGMT: + status = p2p_process_rx_mgmt( + (struct p2p_rx_mgmt_event *) + msg->bodyptr); + break; + case P2P_EVENT_LO_STOPPED: + status = p2p_process_lo_stop( + (struct p2p_lo_stop_event *) + msg->bodyptr); + break; + case P2P_EVENT_NOA: + status = p2p_process_noa( + (struct p2p_noa_event *) + msg->bodyptr); + break; + case P2P_EVENT_ADD_MAC_RSP: + status = p2p_process_set_rand_mac_rsp( + (struct p2p_mac_filter_rsp *) + msg->bodyptr); + break; + default: + p2p_err("Drop unexpected message received %d", + msg->type); + status = QDF_STATUS_E_INVAL; + break; + } + + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + + return status; +} + +QDF_STATUS p2p_msg_flush_callback(struct scheduler_msg *msg) +{ + struct tx_action_context *tx_action; + + if (!msg || !(msg->bodyptr)) { + p2p_err("invalid msg"); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("flush msg, type:%d", msg->type); + switch (msg->type) { + case P2P_MGMT_TX: + tx_action = (struct tx_action_context *)msg->bodyptr; + qdf_mem_free(tx_action->buf); + qdf_mem_free(tx_action); + break; + default: + qdf_mem_free(msg->bodyptr); + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_event_flush_callback(struct scheduler_msg *msg) +{ + struct p2p_noa_event *noa_event; + struct p2p_rx_mgmt_event *rx_mgmt_event; + struct p2p_tx_conf_event *tx_conf_event; + struct p2p_lo_stop_event *lo_stop_event; + + if (!msg || !(msg->bodyptr)) { + p2p_err("invalid msg"); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("flush event, type:%d", msg->type); + switch (msg->type) { + case P2P_EVENT_NOA: + noa_event = (struct p2p_noa_event *)msg->bodyptr; + qdf_mem_free(noa_event->noa_info); + qdf_mem_free(noa_event); + break; + case P2P_EVENT_RX_MGMT: + rx_mgmt_event = (struct p2p_rx_mgmt_event *)msg->bodyptr; + qdf_mem_free(rx_mgmt_event->rx_mgmt); + qdf_mem_free(rx_mgmt_event); + break; + case P2P_EVENT_MGMT_TX_ACK_CNF: + tx_conf_event = (struct p2p_tx_conf_event *)msg->bodyptr; + qdf_nbuf_free(tx_conf_event->nbuf); + qdf_mem_free(tx_conf_event); + break; + case P2P_EVENT_LO_STOPPED: + lo_stop_event = (struct p2p_lo_stop_event *)msg->bodyptr; + qdf_mem_free(lo_stop_event->lo_event); + qdf_mem_free(lo_stop_event); + break; + default: + qdf_mem_free(msg->bodyptr); + break; + } + + return QDF_STATUS_SUCCESS; +} + +bool p2p_check_oui_and_force_1x1(uint8_t *assoc_ie, uint32_t assoc_ie_len) +{ + const uint8_t *vendor_ie, *p2p_ie, *pos; + uint8_t rem_len, attr; + uint16_t attr_len; + + vendor_ie = (uint8_t *)p2p_get_p2pie_ptr(assoc_ie, assoc_ie_len); + if (!vendor_ie) { + p2p_debug("P2P IE not found"); + return false; + } + + rem_len = vendor_ie[1]; + if (rem_len < (2 + OUI_SIZE_P2P) || rem_len > WLAN_MAX_IE_LEN) { + p2p_err("Invalid IE len %d", rem_len); + return false; + } + + p2p_ie = vendor_ie + HEADER_LEN_P2P_IE; + rem_len -= OUI_SIZE_P2P; + + while (rem_len) { + attr = p2p_ie[0]; + attr_len = LE_READ_2(&p2p_ie[1]); + if (attr_len > rem_len) { + p2p_err("Invalid len %d for elem:%d", attr_len, attr); + return false; + } + + switch (attr) { + case P2P_ATTR_CAPABILITY: + case P2P_ATTR_DEVICE_ID: + case P2P_ATTR_GROUP_OWNER_INTENT: + case P2P_ATTR_STATUS: + case P2P_ATTR_LISTEN_CHANNEL: + case P2P_ATTR_OPERATING_CHANNEL: + case P2P_ATTR_GROUP_INFO: + case P2P_ATTR_MANAGEABILITY: + case P2P_ATTR_CHANNEL_LIST: + break; + + case P2P_ATTR_DEVICE_INFO: + if (attr_len < (QDF_MAC_ADDR_SIZE + + MAX_CONFIG_METHODS_LEN + 8 + + DEVICE_CATEGORY_MAX_LEN)) { + p2p_err("Invalid Device info attr len %d", + attr_len); + return false; + } + + /* move by attr id and 2 bytes of attr len */ + pos = p2p_ie + 3; + + /* + * the P2P Device info is of format: + * attr_id - 1 byte + * attr_len - 2 bytes + * device mac addr - 6 bytes + * config methods - 2 bytes + * primary device type - 8bytes + * -primary device type category - 1 byte + * -primary device type oui - 4bytes + * number of secondary device type - 2 bytes + */ + pos += ETH_ALEN + MAX_CONFIG_METHODS_LEN + + DEVICE_CATEGORY_MAX_LEN; + + if (!qdf_mem_cmp(pos, P2P_1X1_WAR_OUI, + P2P_1X1_OUI_LEN)) + return true; + + break; + default: + p2p_err("Invalid P2P attribute"); + break; + } + p2p_ie += (3 + attr_len); + rem_len -= (3 + attr_len); + } + + return false; +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +QDF_STATUS p2p_process_lo_stop( + struct p2p_lo_stop_event *lo_stop_event) +{ + struct p2p_lo_event *lo_evt; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + if (!lo_stop_event) { + p2p_err("invalid lo stop event"); + return QDF_STATUS_E_INVAL; + } + + lo_evt = lo_stop_event->lo_event; + if (!lo_evt) { + p2p_err("invalid lo event"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = lo_stop_event->p2p_soc_obj; + + p2p_debug("vdev_id %d, reason %d", + lo_evt->vdev_id, lo_evt->reason_code); + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid p2p soc object or start parameters"); + qdf_mem_free(lo_evt); + return QDF_STATUS_E_INVAL; + } + start_param = p2p_soc_obj->start_param; + if (start_param->lo_event_cb) + start_param->lo_event_cb( + start_param->lo_event_cb_data, lo_evt); + else + p2p_err("Invalid p2p soc obj or hdd lo event callback"); + + qdf_mem_free(lo_evt); + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS p2p_process_noa(struct p2p_noa_event *noa_event) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct p2p_noa_info *noa_info; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + enum QDF_OPMODE mode; + + if (!noa_event) { + p2p_err("invalid noa event"); + return QDF_STATUS_E_INVAL; + } + noa_info = noa_event->noa_info; + p2p_soc_obj = noa_event->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + + p2p_debug("psoc:%pK, index:%d, opps_ps:%d, ct_window:%d, num_desc:%d, vdev_id:%d", + psoc, noa_info->index, noa_info->opps_ps, + noa_info->ct_window, noa_info->num_desc, + noa_info->vdev_id); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + noa_info->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev obj is NULL"); + qdf_mem_free(noa_event->noa_info); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + if (mode != QDF_P2P_GO_MODE) { + p2p_err("invalid p2p vdev mode:%d", mode); + status = QDF_STATUS_E_INVAL; + goto fail; + } + + /* must send noa to pe since of limitation*/ + p2p_send_noa_to_pe(noa_info); + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + if (!(p2p_vdev_obj->noa_info)) { + p2p_vdev_obj->noa_info = + qdf_mem_malloc(sizeof(struct p2p_noa_info)); + if (!(p2p_vdev_obj->noa_info)) { + p2p_err("Failed to allocate p2p noa info"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + } + qdf_mem_copy(p2p_vdev_obj->noa_info, noa_info, + sizeof(struct p2p_noa_info)); +fail: + qdf_mem_free(noa_event->noa_info); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +void p2p_peer_authorized(struct wlan_objmgr_vdev *vdev, uint8_t *mac_addr) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_peer *peer; + uint8_t pdev_id; + + if (!vdev) { + p2p_err("vdev:%pK", vdev); + return; + } + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + p2p_err("psoc:%pK", psoc); + return; + } + pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev)); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_P2P_ID); + if (!peer) { + p2p_debug("peer info not found"); + return; + } + status = process_peer_for_noa(vdev, psoc, peer); + wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID); + + if (status != QDF_STATUS_SUCCESS) + return; + + p2p_debug("peer is authorized"); +} + +#ifdef WLAN_FEATURE_P2P_DEBUG +static struct p2p_soc_priv_obj * +get_p2p_soc_obj_by_vdev(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct wlan_objmgr_psoc *soc; + + if (!vdev) { + p2p_err("vdev context passed is NULL"); + return NULL; + } + + soc = wlan_vdev_get_psoc(vdev); + if (!soc) { + p2p_err("soc context is NULL"); + return NULL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) + p2p_err("P2P soc context is NULL"); + + return p2p_soc_obj; +} + +QDF_STATUS p2p_status_scan(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_CLIENT_MODE && + mode != QDF_P2P_DEVICE_MODE) { + p2p_debug("this is not P2P CLIENT or DEVICE, mode:%d", + mode); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + switch (p2p_soc_obj->connection_status) { + case P2P_GO_NEG_COMPLETED: + case P2P_GO_NEG_PROCESS: + p2p_soc_obj->connection_status = + P2P_CLIENT_CONNECTING_STATE_1; + p2p_debug("[P2P State] Changing state from Go nego completed to Connection is started"); + p2p_debug("P2P Scanning is started for 8way Handshake"); + break; + case P2P_CLIENT_DISCONNECTED_STATE: + p2p_soc_obj->connection_status = + P2P_CLIENT_CONNECTING_STATE_2; + p2p_debug("[P2P State] Changing state from Disconnected state to Connection is started"); + p2p_debug("P2P Scanning is started for 4way Handshake"); + break; + default: + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_status_connect(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_CLIENT_MODE) + return QDF_STATUS_SUCCESS; + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + switch (p2p_soc_obj->connection_status) { + case P2P_CLIENT_CONNECTING_STATE_1: + p2p_soc_obj->connection_status = + P2P_CLIENT_CONNECTED_STATE_1; + p2p_debug("[P2P State] Changing state from Connecting state to Connected State for 8-way Handshake"); + break; + case P2P_CLIENT_DISCONNECTED_STATE: + p2p_debug("No scan before 4-way handshake"); + /* + * Fall thru since no scan before 4-way handshake and + * won't enter state P2P_CLIENT_CONNECTING_STATE_2: + */ + case P2P_CLIENT_CONNECTING_STATE_2: + p2p_soc_obj->connection_status = + P2P_CLIENT_COMPLETED_STATE; + p2p_debug("[P2P State] Changing state from Connecting state to P2P Client Connection Completed"); + break; + default: + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_status_disconnect(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_CLIENT_MODE) + return QDF_STATUS_SUCCESS; + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + switch (p2p_soc_obj->connection_status) { + case P2P_CLIENT_CONNECTED_STATE_1: + p2p_soc_obj->connection_status = + P2P_CLIENT_DISCONNECTED_STATE; + p2p_debug("[P2P State] 8 way Handshake completed and moved to disconnected state"); + break; + case P2P_CLIENT_COMPLETED_STATE: + p2p_soc_obj->connection_status = P2P_NOT_ACTIVE; + p2p_debug("[P2P State] P2P Client is removed and moved to inactive state"); + break; + default: + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_status_start_bss(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_GO_MODE) { + p2p_debug("this is not P2P GO, mode:%d", mode); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + switch (p2p_soc_obj->connection_status) { + case P2P_GO_NEG_COMPLETED: + p2p_soc_obj->connection_status = + P2P_GO_COMPLETED_STATE; + p2p_debug("[P2P State] From Go nego completed to Non-autonomous Group started"); + break; + case P2P_NOT_ACTIVE: + p2p_soc_obj->connection_status = + P2P_GO_COMPLETED_STATE; + p2p_debug("[P2P State] From Inactive to Autonomous Group started"); + break; + default: + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_GO_MODE) { + p2p_debug("this is not P2P GO, mode:%d", mode); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + if (p2p_soc_obj->connection_status == P2P_GO_COMPLETED_STATE) { + p2p_soc_obj->connection_status = P2P_NOT_ACTIVE; + p2p_debug("[P2P State] From GO completed to Inactive state GO got removed"); + } + + return QDF_STATUS_SUCCESS; +} + +#endif /* WLAN_FEATURE_P2P_DEBUG */ diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_main.h b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_main.h new file mode 100644 index 0000000000000000000000000000000000000000..1336cf42d341b51464ed470aa682d59c152b86a6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_main.h @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Defines main P2P functions & structures + */ + +#ifndef _WLAN_P2P_MAIN_H_ +#define _WLAN_P2P_MAIN_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_QUEUE_LENGTH 20 +#define P2P_NOA_ATTR_IND 0x1090 +#define P2P_MODULE_NAME "P2P" +#define P2P_INVALID_VDEV_ID 0xFFFFFFFF +#define MAX_RANDOM_MAC_ADDRS 4 + +#define p2p_debug(params ...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_P2P, params) +#define p2p_info(params ...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_P2P, params) +#define p2p_warn(params ...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_P2P, params) +#define p2p_err(params ...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_P2P, params) +#define p2p_debug_rl(params...) \ + QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_P2P, params) +#define p2p_info_rl(params...) \ + QDF_TRACE_INFO_RL(QDF_MODULE_ID_P2P, params) + +#define p2p_alert(params ...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_P2P, params) + +#define p2p_nofl_debug(params ...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_info(params ...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_warn(params ...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_err(params ...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_alert(params ...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_P2P, params) + +struct scheduler_msg; +struct p2p_tx_cnf; +struct p2p_rx_mgmt_frame; +struct p2p_lo_event; +struct p2p_start_param; +struct p2p_noa_info; +struct tx_action_context; + +/** + * enum p2p_cmd_type - P2P request type + * @P2P_ROC_REQ: P2P roc request + * @P2P_CANCEL_ROC_REQ: Cancel P2P roc request + * @P2P_MGMT_TX: P2P tx action frame request + * @P2P_MGMT_TX_CANCEL: Cancel tx action frame request + * @P2P_CLEANUP_ROC: Cleanup roc queue + * @P2P_CLEANUP_TX: Cleanup tx mgmt queue + * @P2P_SET_RANDOM_MAC: Set Random MAC addr filter request + */ +enum p2p_cmd_type { + P2P_ROC_REQ = 0, + P2P_CANCEL_ROC_REQ, + P2P_MGMT_TX, + P2P_MGMT_TX_CANCEL, + P2P_CLEANUP_ROC, + P2P_CLEANUP_TX, + P2P_SET_RANDOM_MAC, +}; + +/** + * enum p2p_event_type - P2P event type + * @P2P_EVENT_SCAN_EVENT: P2P scan event + * @P2P_EVENT_MGMT_TX_ACK_CNF: P2P mgmt tx confirm frame + * @P2P_EVENT_RX_MGMT: P2P rx mgmt frame + * @P2P_EVENT_LO_STOPPED: P2P listen offload stopped event + * @P2P_EVENT_NOA: P2P noa event + * @P2P_EVENT_ADD_MAC_RSP: Set Random MAC addr event + */ +enum p2p_event_type { + P2P_EVENT_SCAN_EVENT = 0, + P2P_EVENT_MGMT_TX_ACK_CNF, + P2P_EVENT_RX_MGMT, + P2P_EVENT_LO_STOPPED, + P2P_EVENT_NOA, + P2P_EVENT_ADD_MAC_RSP, +}; + +/** + * struct p2p_tx_conf_event - p2p tx confirm event + * @p2p_soc_obj: p2p soc private object + * @buf: buffer address + * @status: tx status + */ +struct p2p_tx_conf_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + qdf_nbuf_t nbuf; + uint32_t status; +}; + +/** + * struct p2p_rx_mgmt_event - p2p rx mgmt frame event + * @p2p_soc_obj: p2p soc private object + * @rx_mgmt: p2p rx mgmt frame structure + */ +struct p2p_rx_mgmt_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_rx_mgmt_frame *rx_mgmt; +}; + +/** + * struct p2p_lo_stop_event - p2p listen offload stop event + * @p2p_soc_obj: p2p soc private object + * @lo_event: p2p lo stop structure + */ +struct p2p_lo_stop_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_lo_event *lo_event; +}; + +/** + * struct p2p_noa_event - p2p noa event + * @p2p_soc_obj: p2p soc private object + * @noa_info: p2p noa information structure + */ +struct p2p_noa_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_noa_info *noa_info; +}; + +/** + * struct p2p_mac_filter_rsp - p2p set mac filter respone + * @p2p_soc_obj: p2p soc private object + * @vdev_id: vdev id + * @status: successfully(1) or not (0) + */ +struct p2p_mac_filter_rsp { + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; + uint32_t status; +}; + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * enum p2p_connection_status - p2p connection status + * @P2P_NOT_ACTIVE: P2P not active status + * @P2P_GO_NEG_PROCESS: P2P GO negotiation in process + * @P2P_GO_NEG_COMPLETED: P2P GO negotiation complete + * @P2P_CLIENT_CONNECTING_STATE_1: P2P client connecting state 1 + * @P2P_GO_COMPLETED_STATE: P2P GO complete state + * @P2P_CLIENT_CONNECTED_STATE_1: P2P client connected state 1 + * @P2P_CLIENT_DISCONNECTED_STATE: P2P client disconnected state + * @P2P_CLIENT_CONNECTING_STATE_2: P2P client connecting state 2 + * @P2P_CLIENT_COMPLETED_STATE: P2P client complete state + */ +enum p2p_connection_status { + P2P_NOT_ACTIVE, + P2P_GO_NEG_PROCESS, + P2P_GO_NEG_COMPLETED, + P2P_CLIENT_CONNECTING_STATE_1, + P2P_GO_COMPLETED_STATE, + P2P_CLIENT_CONNECTED_STATE_1, + P2P_CLIENT_DISCONNECTED_STATE, + P2P_CLIENT_CONNECTING_STATE_2, + P2P_CLIENT_COMPLETED_STATE +}; +#endif + +/** + * struct p2p_param - p2p parameters to be used + * @go_keepalive_period: P2P GO keep alive period + * @go_link_monitor_period: period where link is idle and + * where we send NULL frame + * @p2p_device_addr_admin: enable/disable to derive the P2P + * MAC address from the primary MAC address + * @skip_dfs_channel_p2p_search: skip DFS Channel in case of P2P Search + */ +struct p2p_param { + uint32_t go_keepalive_period; + uint32_t go_link_monitor_period; + bool p2p_device_addr_admin; +}; + +/** + * struct p2p_soc_priv_obj - Per SoC p2p private object + * @soc: Pointer to SoC context + * @roc_q: Queue for pending roc requests + * @tx_q_roc: Queue for tx frames waiting for RoC + * @tx_q_ack: Queue for tx frames waiting for ack + * @scan_req_id: Scan requestor id + * @start_param: Start parameters, include callbacks and user + * data to HDD + * @cancel_roc_done: Cancel roc done event + * @cleanup_roc_done: Cleanup roc done event + * @cleanup_tx_done: Cleanup tx done event + * @roc_runtime_lock: Runtime lock for roc request + * @p2p_cb: Callbacks to protocol stack + * @cur_roc_vdev_id: Vdev id of current roc + * @p2p_idr: p2p idr + * @param: p2p parameters to be used + * @connection_status:Global P2P connection status + */ +struct p2p_soc_priv_obj { + struct wlan_objmgr_psoc *soc; + qdf_list_t roc_q; + qdf_list_t tx_q_roc; + qdf_list_t tx_q_ack; + wlan_scan_requester scan_req_id; + struct p2p_start_param *start_param; + qdf_event_t cleanup_roc_done; + qdf_event_t cleanup_tx_done; + qdf_runtime_lock_t roc_runtime_lock; + struct p2p_protocol_callbacks p2p_cb; + uint32_t cur_roc_vdev_id; + qdf_idr p2p_idr; + struct p2p_param param; +#ifdef WLAN_FEATURE_P2P_DEBUG + enum p2p_connection_status connection_status; +#endif +}; + +/** + * struct action_frame_cookie - Action frame cookie item in cookie list + * @cookie_node: qdf_list_node + * @cookie: Cookie value + */ +struct action_frame_cookie { + qdf_list_node_t cookie_node; + uint64_t cookie; +}; + +/** + * struct action_frame_random_mac - Action Frame random mac addr & + * related attrs + * @p2p_vdev_obj: p2p vdev private obj ptr + * @in_use: Checks whether random mac is in use + * @addr: Contains random mac addr + * @freq: Channel frequency + * @clear_timer: timer to clear random mac filter + * @cookie_list: List of cookies tied with random mac + */ +struct action_frame_random_mac { + struct p2p_vdev_priv_obj *p2p_vdev_obj; + bool in_use; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + uint32_t freq; + qdf_mc_timer_t clear_timer; + qdf_list_t cookie_list; +}; + +/** + * p2p_request_mgr_callback_t() - callback to process set mac filter result + * @result: bool + * @context: callback context. + * + * Return: void + */ +typedef void (*p2p_request_mgr_callback_t)(bool result, void *context); + +/** + * struct random_mac_priv - request private data struct + * @result: result of request. + */ +struct random_mac_priv { + bool result; +}; + +/** + * struct p2p_set_mac_filter_req - set mac addr filter cmd data structure + * @soc: soc object + * @vdev_id: vdev id + * @mac: mac address to be set + * @freq: frequency + * @set: set or clear + * @cb: callback func to be called when the request completion + * @req_cookie: cookie to be used when request completed + */ +struct p2p_set_mac_filter_req { + struct wlan_objmgr_psoc *soc; + uint32_t vdev_id; + uint8_t mac[QDF_MAC_ADDR_SIZE]; + uint32_t freq; + bool set; + p2p_request_mgr_callback_t cb; + void *req_cookie; +}; + +/** + * struct p2p_vdev_priv_obj - Per vdev p2p private object + * @vdev: Pointer to vdev context + * @noa_info: NoA information + * @noa_status: NoA status i.e. Enabled / Disabled (TRUE/FALSE) + * @non_p2p_peer_count: Number of legacy stations connected to this GO + * @random_mac_lock: lock for random_mac list + * @random_mac: active random mac filter lists + * @pending_req: pending set mac filter request. + */ +struct p2p_vdev_priv_obj { + struct wlan_objmgr_vdev *vdev; + struct p2p_noa_info *noa_info; + bool noa_status; + uint16_t non_p2p_peer_count; + + /* random address management for management action frames */ + qdf_spinlock_t random_mac_lock; + struct action_frame_random_mac random_mac[MAX_RANDOM_MAC_ADDRS]; + struct p2p_set_mac_filter_req pending_req; +}; + +/** + * struct p2p_noa_attr - p2p noa attribute + * @rsvd1: reserved bits 1 + * @opps_ps: opps ps state of the AP + * @ct_win: ct window in TUs + * @index: identifies instance of NOA su element + * @rsvd2: reserved bits 2 + * @noa1_count: interval count of noa1 + * @noa1_duration: absent period duration of noa1 + * @noa1_interval: absent period interval of noa1 + * @noa1_start_time: 32 bit tsf time of noa1 + * @rsvd3: reserved bits 3 + * @noa2_count: interval count of noa2 + * @noa2_duration: absent period duration of noa2 + * @noa2_interval: absent period interval of noa2 + * @noa2_start_time: 32 bit tsf time of noa2 + */ +struct p2p_noa_attr { + uint32_t rsvd1:16; + uint32_t ct_win:7; + uint32_t opps_ps:1; + uint32_t index:8; + uint32_t rsvd2:24; + uint32_t noa1_count:8; + uint32_t noa1_duration; + uint32_t noa1_interval; + uint32_t noa1_start_time; + uint32_t rsvd3:24; + uint32_t noa2_count:8; + uint32_t noa2_duration; + uint32_t noa2_interval; + uint32_t noa2_start_time; +}; + +/** + * p2p_component_init() - P2P component initialization + * + * This function registers psoc/vdev create/delete handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_component_init(void); + +/** + * p2p_component_deinit() - P2P component de-init + * + * This function deregisters psoc/vdev create/delete handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_component_deinit(void); + +/** + * p2p_psoc_object_open() - Open P2P component + * @soc: soc context + * + * This function initialize p2p psoc object + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_object_open(struct wlan_objmgr_psoc *soc); + +/** + * p2p_psoc_object_close() - Close P2P component + * @soc: soc context + * + * This function de-init p2p psoc object. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_object_close(struct wlan_objmgr_psoc *soc); + +/** + * p2p_psoc_start() - Start P2P component + * @soc: soc context + * @req: P2P start parameters + * + * This function sets up layer call back in p2p psoc object + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req); + +/** + * p2p_psoc_stop() - Stop P2P component + * @soc: soc context + * + * This function clears up layer call back in p2p psoc object. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_stop(struct wlan_objmgr_psoc *soc); + +/** + * p2p_process_cmd() - Process P2P messages in OS interface queue + * @msg: message information + * + * This function is main handler for P2P messages in OS interface + * queue, it gets called by message scheduler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cmd(struct scheduler_msg *msg); + +/** + * p2p_process_evt() - Process P2P messages in target interface queue + * @msg: message information + * + * This function is main handler for P2P messages in target interface + * queue, it gets called by message scheduler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_evt(struct scheduler_msg *msg); + +/** + * p2p_msg_flush_callback() - Callback used to flush P2P messages + * @msg: message information + * + * This callback will be called when scheduler flush some of P2P messages. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_msg_flush_callback(struct scheduler_msg *msg); + +/** + * p2p_event_flush_callback() - Callback used to flush P2P events + * @msg: event information + * + * This callback will be called when scheduler flush some of P2P events. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_event_flush_callback(struct scheduler_msg *msg); + +/** + * p2p_check_oui_and_force_1x1() - Function to get P2P client device + * attributes from assoc request frame IE passed in. + * @assoc_ie: Pointer to the IEs in the association req frame + * @assoc_ie_len: Total length of the IE in association req frame + * + * Return: true if the OUI is present else false + */ +bool p2p_check_oui_and_force_1x1(uint8_t *assoc_ie, uint32_t assoc_ie_len); + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * p2p_process_lo_stop() - Process lo stop event + * @lo_stop_event: listen offload stop event information + * + * This function handles listen offload stop event and deliver this + * event to HDD layer by registered callback. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_lo_stop( + struct p2p_lo_stop_event *lo_stop_event); +#else +static inline QDF_STATUS p2p_process_lo_stop( + struct p2p_lo_stop_event *lo_stop_event) +{ + return QDF_STATUS_SUCCESS; +} +#endif +/** + * p2p_process_noa() - Process noa event + * @noa_event: noa event information + * + * This function handles noa event and save noa information in p2p + * vdev object. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_noa(struct p2p_noa_event *noa_event); + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * p2p_status_scan() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when scanning + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_scan(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_connect() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when connecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_connect(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_disconnect() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when disconnecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_disconnect(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_start_bss() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when starting BSS. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_start_bss(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_stop_bss() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when stopping BSS. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev); +#else +static inline QDF_STATUS p2p_status_scan(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_connect(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_disconnect(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_start_bss(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_P2P_DEBUG */ +#endif /* _WLAN_P2P_MAIN_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.c b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.c new file mode 100644 index 0000000000000000000000000000000000000000..60be2c6cdea206a05d8d510cc7d928263a2fb8a0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.c @@ -0,0 +1,3240 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains off channel tx API definitions + */ + +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_p2p_roc.h" +#include "wlan_p2p_main.h" +#include "wlan_p2p_off_chan_tx.h" +#include "wlan_osif_request_manager.h" +#include + +/** + * p2p_psoc_get_tx_ops() - get p2p tx ops + * @psoc: psoc object + * + * This function returns p2p tx ops callbacks. + * + * Return: wlan_lmac_if_p2p_tx_ops + */ +static inline struct wlan_lmac_if_p2p_tx_ops * +p2p_psoc_get_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &psoc->soc_cb.tx_ops.p2p; +} + +/** + * p2p_tx_context_check_valid() - check tx action context + * @tx_ctx: tx context + * + * This function check if tx action context and parameters are valid. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_tx_context_check_valid(struct tx_action_context *tx_ctx) +{ + struct wlan_objmgr_psoc *psoc; + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!tx_ctx) { + p2p_err("null tx action context"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + if (!p2p_soc_obj) { + p2p_err("null p2p soc private object"); + return QDF_STATUS_E_INVAL; + } + + psoc = p2p_soc_obj->soc; + if (!psoc) { + p2p_err("null p2p soc object"); + return QDF_STATUS_E_INVAL; + } + + if (!tx_ctx->buf) { + p2p_err("null tx buffer"); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_vdev_check_valid() - check vdev and vdev mode + * @tx_ctx: tx context + * + * This function check if vdev and vdev mode are valid. It will drop + * probe response in sta mode. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +#ifdef SUPPORT_P2P_BY_ONE_INTF_WLAN +static QDF_STATUS p2p_vdev_check_valid(struct tx_action_context *tx_ctx) +{ + enum QDF_OPMODE mode; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, tx_ctx->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("null vdev object"); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + + /* drop probe response/disassoc/deauth for go, sap */ + if ((mode == QDF_SAP_MODE || + mode == QDF_P2P_GO_MODE) && + ((tx_ctx->frame_info.sub_type == P2P_MGMT_PROBE_RSP) || + (tx_ctx->frame_info.sub_type == P2P_MGMT_DISASSOC) || + (tx_ctx->frame_info.sub_type == P2P_MGMT_DEAUTH))) { + p2p_debug("drop frame, mode:%d, sub type:%d", mode, + tx_ctx->frame_info.sub_type); + status = QDF_STATUS_E_FAILURE; + } + + /* drop action frame for sap */ + if ((mode == QDF_SAP_MODE) && + (tx_ctx->frame_info.sub_type == P2P_MGMT_ACTION) && + (tx_ctx->frame_info.public_action_type == + P2P_PUBLIC_ACTION_NOT_SUPPORT) && + (tx_ctx->frame_info.action_type == P2P_ACTION_NOT_SUPPORT)) { + p2p_debug("drop action frame for SAP"); + status = QDF_STATUS_E_FAILURE; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} +#else +static QDF_STATUS p2p_vdev_check_valid(struct tx_action_context *tx_ctx) +{ + enum QDF_OPMODE mode; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, tx_ctx->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("null vdev object"); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + + /* drop probe response/disassoc/deauth for sta, go, sap */ + if ((mode == QDF_STA_MODE && + tx_ctx->frame_info.sub_type == P2P_MGMT_PROBE_RSP) || + ((mode == QDF_SAP_MODE || mode == QDF_P2P_GO_MODE) && + ((tx_ctx->frame_info.sub_type == P2P_MGMT_PROBE_RSP) || + (tx_ctx->frame_info.sub_type == P2P_MGMT_DISASSOC) || + (tx_ctx->frame_info.sub_type == P2P_MGMT_DEAUTH)))) { + p2p_debug("drop frame, mode:%d, sub type:%d", mode, + tx_ctx->frame_info.sub_type); + status = QDF_STATUS_E_FAILURE; + } + + /* drop ation frame for sap */ + if ((mode == QDF_SAP_MODE) && + (tx_ctx->frame_info.sub_type == P2P_MGMT_ACTION) && + (tx_ctx->frame_info.public_action_type == + P2P_PUBLIC_ACTION_NOT_SUPPORT) && + (tx_ctx->frame_info.action_type == P2P_ACTION_NOT_SUPPORT)) { + p2p_debug("drop action frame for SAP"); + status = QDF_STATUS_E_FAILURE; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} +#endif /* SUPPORT_P2P_BY_ONE_INTF_WLAN */ + +/** + * p2p_check_and_update_channel() - check and update tx channel + * @tx_ctx: tx context + * + * This function checks and updates tx channel if channel is 0 in tx context. + * It will update channel to current roc channel if vdev mode is + * P2P DEVICE/CLIENT/GO. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_check_and_update_channel(struct tx_action_context *tx_ctx) +{ + enum QDF_OPMODE mode; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + + if (!tx_ctx || tx_ctx->chan) { + p2p_err("NULL tx ctx or channel valid"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, tx_ctx->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("null vdev object"); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + + if (curr_roc_ctx && + (mode == QDF_P2P_DEVICE_MODE || + mode == QDF_P2P_CLIENT_MODE || + mode == QDF_P2P_GO_MODE)) + tx_ctx->chan = curr_roc_ctx->chan; + + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_get_p2pie_ptr() - get the pointer to p2p ie + * @ie: source ie + * @ie_len: source ie length + * + * This function finds out p2p ie by p2p oui and return the pointer. + * + * Return: pointer to p2p ie + */ +const uint8_t *p2p_get_p2pie_ptr(const uint8_t *ie, uint16_t ie_len) +{ + return wlan_get_vendor_ie_ptr_from_oui(P2P_OUI, + P2P_OUI_SIZE, ie, ie_len); +} + +/** + * p2p_get_p2pie_from_probe_rsp() - get the pointer to p2p ie from + * probe response + * @tx_ctx: tx context + * + * This function finds out p2p ie and return the pointer if it is a + * probe response frame. + * + * Return: pointer to p2p ie + */ +static const uint8_t *p2p_get_p2pie_from_probe_rsp( + struct tx_action_context *tx_ctx) +{ + const uint8_t *ie; + const uint8_t *p2p_ie; + const uint8_t *tmp_p2p_ie = NULL; + uint16_t ie_len; + + if (tx_ctx->buf_len <= PROBE_RSP_IE_OFFSET) { + p2p_err("Invalid header len for probe response"); + return NULL; + } + + ie = tx_ctx->buf + PROBE_RSP_IE_OFFSET; + ie_len = tx_ctx->buf_len - PROBE_RSP_IE_OFFSET; + p2p_ie = p2p_get_p2pie_ptr(ie, ie_len); + while ((p2p_ie) && + (WLAN_MAX_IE_LEN == p2p_ie[1])) { + ie_len = tx_ctx->buf_len - (p2p_ie - tx_ctx->buf); + if (ie_len > 2) { + ie = p2p_ie + WLAN_MAX_IE_LEN + 2; + tmp_p2p_ie = p2p_get_p2pie_ptr(ie, ie_len); + } + + if (tmp_p2p_ie) { + p2p_ie = tmp_p2p_ie; + tmp_p2p_ie = NULL; + } else { + break; + } + } + + return p2p_ie; +} + +/** + * p2p_get_presence_noa_attr() - get the pointer to noa attr + * @pies: source ie + * @length: source ie length + * + * This function finds out noa attr by noa eid and return the pointer. + * + * Return: pointer to noa attr + */ +static const uint8_t *p2p_get_presence_noa_attr(const uint8_t *pies, int length) +{ + int left = length; + const uint8_t *ptr = pies; + uint8_t elem_id; + uint16_t elem_len; + + p2p_debug("pies:%pK, length:%d", pies, length); + + while (left >= 3) { + elem_id = ptr[0]; + elem_len = ((uint16_t) ptr[1]) | (ptr[2] << 8); + + left -= 3; + if (elem_len > left) { + p2p_err("****Invalid IEs, elem_len=%d left=%d*****", + elem_len, left); + return NULL; + } + if (elem_id == P2P_NOA_ATTR) + return ptr; + + left -= elem_len; + ptr += (elem_len + 3); + } + + return NULL; +} + +/** + * p2p_get_noa_attr_stream_in_mult_p2p_ies() - get the pointer to noa + * attr from multi p2p ie + * @noa_stream: noa stream + * @noa_len: noa stream length + * @overflow_len: overflow length + * + * This function finds out noa attr from multi p2p ies. + * + * Return: noa length + */ +static uint8_t p2p_get_noa_attr_stream_in_mult_p2p_ies(uint8_t *noa_stream, + uint8_t noa_len, uint8_t overflow_len) +{ + uint8_t overflow_p2p_stream[P2P_MAX_NOA_ATTR_LEN]; + + p2p_debug("noa_stream:%pK, noa_len:%d, overflow_len:%d", + noa_stream, noa_len, overflow_len); + if ((noa_len <= (P2P_MAX_NOA_ATTR_LEN + P2P_IE_HEADER_LEN)) && + (noa_len >= overflow_len) && + (overflow_len <= P2P_MAX_NOA_ATTR_LEN)) { + qdf_mem_copy(overflow_p2p_stream, + noa_stream + noa_len - overflow_len, + overflow_len); + noa_stream[noa_len - overflow_len] = + P2P_EID_VENDOR; + noa_stream[noa_len - overflow_len + 1] = + overflow_len + P2P_OUI_SIZE; + qdf_mem_copy(noa_stream + noa_len - overflow_len + 2, + P2P_OUI, P2P_OUI_SIZE); + qdf_mem_copy(noa_stream + noa_len + 2 + P2P_OUI_SIZE - + overflow_len, overflow_p2p_stream, + overflow_len); + } + + return noa_len + P2P_IE_HEADER_LEN; +} + +/** + * p2p_get_vdev_noa_info() - get vdev noa information + * @tx_ctx: tx context + * + * This function gets vdev noa information + * + * Return: pointer to noa information + */ +static struct p2p_noa_info *p2p_get_vdev_noa_info( + struct tx_action_context *tx_ctx) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + enum QDF_OPMODE mode; + struct p2p_noa_info *noa_info = NULL; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_ctx->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev obj is NULL"); + return NULL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + if (mode != QDF_P2P_GO_MODE) { + p2p_debug("invalid p2p vdev mode:%d", mode); + goto fail; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + + if (!p2p_vdev_obj || !(p2p_vdev_obj->noa_info)) { + p2p_debug("null noa info"); + goto fail; + } + + noa_info = p2p_vdev_obj->noa_info; + +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return noa_info; +} + +/** + * p2p_get_noa_attr_stream() - get noa stream from p2p vdev object + * @tx_ctx: tx context + * @pnoa_stream: pointer to noa stream + * + * This function finds out noa stream from p2p vdev object + * + * Return: noa stream length + */ +static uint8_t p2p_get_noa_attr_stream( + struct tx_action_context *tx_ctx, uint8_t *pnoa_stream) +{ + struct p2p_noa_info *noa_info; + struct noa_descriptor *noa_desc_0; + struct noa_descriptor *noa_desc_1; + uint8_t *pbody = pnoa_stream; + uint8_t len = 0; + + noa_info = p2p_get_vdev_noa_info(tx_ctx); + if (!noa_info) { + p2p_debug("not valid noa information"); + return 0; + } + + noa_desc_0 = &(noa_info->noa_desc[0]); + noa_desc_1 = &(noa_info->noa_desc[1]); + if ((!(noa_desc_0->duration)) && + (!(noa_desc_1->duration)) && + (!noa_info->opps_ps)) { + p2p_debug("opps ps and duration are 0"); + return 0; + } + + pbody[0] = P2P_NOA_ATTR; + pbody[3] = noa_info->index; + pbody[4] = noa_info->ct_window | (noa_info->opps_ps << 7); + len = 5; + pbody += len; + + if (noa_desc_0->duration) { + *pbody = noa_desc_0->type_count; + pbody += 1; + len += 1; + + *((uint32_t *) (pbody)) = noa_desc_0->duration; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_0->interval; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_0->start_time; + pbody += sizeof(uint32_t); + len += 4; + } + + if (noa_desc_1->duration) { + *pbody = noa_desc_1->type_count; + pbody += 1; + len += 1; + + *((uint32_t *) (pbody)) = noa_desc_1->duration; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_1->interval; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_1->start_time; + pbody += sizeof(uint32_t); + len += 4; + } + + pbody = pnoa_stream + 1; + /* one byte for Attr and 2 bytes for length */ + *((uint16_t *) (pbody)) = len - 3; + + return len; +} + +/** + * p2p_update_noa_stream() - update noa stream + * @tx_ctx: tx context + * @p2p_ie: pointer to p2p ie + * @noa_attr: pointer to noa attr + * @total_len: pointer to total length of ie + * + * This function updates noa stream. + * + * Return: noa stream length + */ +static uint16_t p2p_update_noa_stream(struct tx_action_context *tx_ctx, + uint8_t *p2p_ie, const uint8_t *noa_attr, uint32_t *total_len, + uint8_t *noa_stream) +{ + uint16_t noa_len; + uint16_t overflow_len; + uint8_t orig_len; + uint32_t nbytes_copy; + uint32_t buf_len = *total_len; + + noa_len = p2p_get_noa_attr_stream(tx_ctx, noa_stream); + if (noa_len <= 0) { + p2p_debug("do not find out noa attr"); + return 0; + } + + orig_len = p2p_ie[1]; + if (noa_attr) { + noa_len = noa_attr[1] | (noa_attr[2] << 8); + orig_len -= (noa_len + 1 + 2); + buf_len -= (noa_len + 1 + 2); + p2p_ie[1] = orig_len; + } + + if ((p2p_ie[1] + noa_len) > WLAN_MAX_IE_LEN) { + overflow_len = p2p_ie[1] + noa_len - + WLAN_MAX_IE_LEN; + noa_len = p2p_get_noa_attr_stream_in_mult_p2p_ies( + noa_stream, noa_len, overflow_len); + p2p_ie[1] = WLAN_MAX_IE_LEN; + } else { + /* increment the length of P2P IE */ + p2p_ie[1] += noa_len; + } + + *total_len = buf_len; + nbytes_copy = (p2p_ie + orig_len + 2) - tx_ctx->buf; + + p2p_debug("noa_len=%d orig_len=%d p2p_ie=%pK buf_len=%d nbytes copy=%d ", + noa_len, orig_len, p2p_ie, buf_len, nbytes_copy); + + return noa_len; +} + +/** + * p2p_set_ht_caps() - set ht capability + * @tx_ctx: tx context + * @num_bytes: number bytes + * + * This function sets ht capability + * + * Return: None + */ +static void p2p_set_ht_caps(struct tx_action_context *tx_ctx, + uint32_t num_bytes) +{ +} + +/** + * p2p_populate_mac_header() - update sequence number + * @tx_ctx: tx context + * + * This function updates sequence number of this mgmt frame + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_populate_mac_header( + struct tx_action_context *tx_ctx) +{ + struct wlan_seq_ctl *seq_ctl; + struct wlan_frame_hdr *wh; + struct wlan_objmgr_peer *peer; + struct wlan_objmgr_psoc *psoc; + void *mac_addr; + uint16_t seq_num; + uint8_t pdev_id; + struct wlan_objmgr_vdev *vdev; + + psoc = tx_ctx->p2p_soc_obj->soc; + + wh = (struct wlan_frame_hdr *)tx_ctx->buf; + /* + * Remove the WEP bit if already set, p2p_populate_rmf_field will set it + * if required. + */ + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + mac_addr = wh->i_addr1; + pdev_id = wlan_get_pdev_id_from_vdev_id(psoc, tx_ctx->vdev_id, + WLAN_P2P_ID); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_P2P_ID); + if (!peer) { + mac_addr = wh->i_addr2; + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + } + if (!peer && tx_ctx->rand_mac_tx) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_ctx->vdev_id, + WLAN_P2P_ID); + if (vdev) { + mac_addr = wlan_vdev_mlme_get_macaddr(vdev); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + } + } + if (!peer) { + p2p_err("no valid peer"); + return QDF_STATUS_E_INVAL; + } + seq_num = (uint16_t)wlan_peer_mlme_get_next_seq_num(peer); + seq_ctl = (struct wlan_seq_ctl *)(tx_ctx->buf + + WLAN_SEQ_CTL_OFFSET); + seq_ctl->seq_num_lo = (seq_num & WLAN_LOW_SEQ_NUM_MASK); + seq_ctl->seq_num_hi = ((seq_num & WLAN_HIGH_SEQ_NUM_MASK) >> + WLAN_HIGH_SEQ_NUM_OFFSET); + p2p_debug("seq num: %d", seq_num); + + wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_get_frame_type_str() - parse frame type to string + * @frame_info: frame information + * + * This function parse frame type to string. + * + * Return: command string + */ +static char *p2p_get_frame_type_str(struct p2p_frame_info *frame_info) +{ + if (frame_info->type == P2P_FRAME_NOT_SUPPORT) + return "Not support frame"; + + if (frame_info->sub_type == P2P_MGMT_NOT_SUPPORT) + return "Not support sub frame"; + + if (frame_info->action_type == P2P_ACTION_PRESENCE_REQ) + return "P2P action presence request"; + if (frame_info->action_type == P2P_ACTION_PRESENCE_RSP) + return "P2P action presence response"; + + switch (frame_info->public_action_type) { + case P2P_PUBLIC_ACTION_NEG_REQ: + return "GO negotiation request frame"; + case P2P_PUBLIC_ACTION_NEG_RSP: + return "GO negotiation response frame"; + case P2P_PUBLIC_ACTION_NEG_CNF: + return "GO negotiation confirm frame"; + case P2P_PUBLIC_ACTION_INVIT_REQ: + return "P2P invitation request"; + case P2P_PUBLIC_ACTION_INVIT_RSP: + return "P2P invitation response"; + case P2P_PUBLIC_ACTION_DEV_DIS_REQ: + return "Device discoverability request"; + case P2P_PUBLIC_ACTION_DEV_DIS_RSP: + return "Device discoverability response"; + case P2P_PUBLIC_ACTION_PROV_DIS_REQ: + return "Provision discovery request"; + case P2P_PUBLIC_ACTION_PROV_DIS_RSP: + return "Provision discovery response"; + case P2P_PUBLIC_ACTION_GAS_INIT_REQ: + return "GAS init request"; + case P2P_PUBLIC_ACTION_GAS_INIT_RSP: + return "GAS init response"; + case P2P_PUBLIC_ACTION_GAS_COMB_REQ: + return "GAS come back request"; + case P2P_PUBLIC_ACTION_GAS_COMB_RSP: + return "GAS come back response"; + default: + return "Other frame"; + } +} + +/** + * p2p_init_frame_info() - init frame information structure + * @frame_info: pointer to frame information + * + * This function init frame information structure. + * + * Return: None + */ +static void p2p_init_frame_info(struct p2p_frame_info *frame_info) +{ + frame_info->type = P2P_FRAME_NOT_SUPPORT; + frame_info->sub_type = P2P_MGMT_NOT_SUPPORT; + frame_info->public_action_type = + P2P_PUBLIC_ACTION_NOT_SUPPORT; + frame_info->action_type = P2P_ACTION_NOT_SUPPORT; +} + +/** + * p2p_get_frame_info() - get frame information from packet + * @data_buf: data buffer address + * @length: buffer length + * @frame_info: frame information + * + * This function gets frame information from packet. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_get_frame_info(uint8_t *data_buf, uint32_t length, + struct p2p_frame_info *frame_info) +{ + uint8_t type; + uint8_t sub_type; + uint8_t action_type; + uint8_t *buf = data_buf; + + p2p_init_frame_info(frame_info); + + if (length < P2P_ACTION_OFFSET + 1) { + p2p_err("invalid p2p mgmt hdr len"); + return QDF_STATUS_E_INVAL; + } + + type = P2P_GET_TYPE_FRM_FC(buf[0]); + sub_type = P2P_GET_SUBTYPE_FRM_FC(buf[0]); + if (type != P2P_FRAME_MGMT) { + p2p_err("just support mgmt frame"); + return QDF_STATUS_E_FAILURE; + } + + frame_info->type = type; + frame_info->sub_type = sub_type; + + if (sub_type != P2P_MGMT_ACTION) + return QDF_STATUS_SUCCESS; + + buf += P2P_ACTION_OFFSET; + if (length > P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET && + buf[0] == P2P_PUBLIC_ACTION_FRAME && + buf[1] == P2P_PUBLIC_ACTION_VENDOR_SPECIFIC && + !qdf_mem_cmp(&buf[2], P2P_OUI, P2P_OUI_SIZE)) { + buf = data_buf + + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + action_type = buf[0]; + if (action_type > P2P_PUBLIC_ACTION_PROV_DIS_RSP) + frame_info->public_action_type = + P2P_PUBLIC_ACTION_NOT_SUPPORT; + else + frame_info->public_action_type = action_type; + } else if (length > P2P_ACTION_FRAME_TYPE_OFFSET && + buf[0] == P2P_ACTION_VENDOR_SPECIFIC_CATEGORY && + !qdf_mem_cmp(&buf[1], P2P_OUI, P2P_OUI_SIZE)) { + buf = data_buf + + P2P_ACTION_FRAME_TYPE_OFFSET; + action_type = buf[0]; + if (action_type == P2P_ACTION_PRESENCE_REQ) + frame_info->action_type = + P2P_ACTION_PRESENCE_REQ; + if (action_type == P2P_ACTION_PRESENCE_RSP) + frame_info->action_type = + P2P_ACTION_PRESENCE_RSP; + } else { + p2p_debug("this is not vendor specific p2p action frame"); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("%s", p2p_get_frame_type_str(frame_info)); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * p2p_tx_update_connection_status() - Update P2P connection status + * with tx frame + * @p2p_soc_obj: P2P soc private object + * @tx_frame_info: frame information + * @mac_to: Pointer to dest MAC address + * + * This function updates P2P connection status with tx frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_tx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *tx_frame_info, + uint8_t *mac_to) +{ + if (!p2p_soc_obj || !tx_frame_info || !mac_to) { + p2p_err("invalid p2p_soc_obj:%pK or tx_frame_info:%pK or mac_to:%pK", + p2p_soc_obj, tx_frame_info, mac_to); + return QDF_STATUS_E_INVAL; + } + + if (tx_frame_info->public_action_type != + P2P_PUBLIC_ACTION_NOT_SUPPORT) + p2p_debug("%s ---> OTA to " QDF_MAC_ADDR_FMT, + p2p_get_frame_type_str(tx_frame_info), + QDF_MAC_ADDR_REF(mac_to)); + + if ((tx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_PROV_DIS_REQ) && + (p2p_soc_obj->connection_status == P2P_NOT_ACTIVE)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_PROCESS; + p2p_debug("[P2P State]Inactive state to GO negotiation progress state"); + } else if ((tx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_NEG_CNF) && + (p2p_soc_obj->connection_status == + P2P_GO_NEG_PROCESS)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_COMPLETED; + p2p_debug("[P2P State]GO nego progress to GO nego completed state"); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_rx_update_connection_status() - Update P2P connection status + * with rx frame + * @p2p_soc_obj: P2P soc private object + * @rx_frame_info: frame information + * @mac_from: Pointer to source MAC address + * + * This function updates P2P connection status with rx frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_rx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *rx_frame_info, + uint8_t *mac_from) +{ + if (!p2p_soc_obj || !rx_frame_info || !mac_from) { + p2p_err("invalid p2p_soc_obj:%pK or rx_frame_info:%pK, mac_from:%pK", + p2p_soc_obj, rx_frame_info, mac_from); + return QDF_STATUS_E_INVAL; + } + + if (rx_frame_info->public_action_type != + P2P_PUBLIC_ACTION_NOT_SUPPORT) + p2p_info("%s <--- OTA from " QDF_MAC_ADDR_FMT, + p2p_get_frame_type_str(rx_frame_info), + QDF_MAC_ADDR_REF(mac_from)); + + if ((rx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_PROV_DIS_REQ) && + (p2p_soc_obj->connection_status == P2P_NOT_ACTIVE)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_PROCESS; + p2p_info("[P2P State]Inactive state to GO negotiation progress state"); + } else if ((rx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_NEG_CNF) && + (p2p_soc_obj->connection_status == + P2P_GO_NEG_PROCESS)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_COMPLETED; + p2p_info("[P2P State]GO negotiation progress to GO negotiation completed state"); + } else if ((rx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_INVIT_REQ) && + (p2p_soc_obj->connection_status == P2P_NOT_ACTIVE)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_COMPLETED; + p2p_info("[P2P State]Inactive state to GO negotiation completed state Autonomous GO formation"); + } + + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS p2p_tx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *tx_frame_info, + uint8_t *mac_to) +{ + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS p2p_rx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *rx_frame_info, + uint8_t *mac_from) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * p2p_packet_alloc() - allocate qdf nbuf + * @size: buffe size + * @data: pointer to qdf nbuf data point + * @ppPacket: pointer to qdf nbuf point + * + * This function allocates qdf nbuf. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_packet_alloc(uint16_t size, void **data, + qdf_nbuf_t *ppPacket) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + qdf_nbuf_t nbuf; + + nbuf = qdf_nbuf_alloc(NULL, + roundup(size + P2P_TX_PKT_MIN_HEADROOM, 4), + P2P_TX_PKT_MIN_HEADROOM, sizeof(uint32_t), + false); + + if (nbuf) { + qdf_nbuf_put_tail(nbuf, size); + qdf_nbuf_set_protocol(nbuf, ETH_P_CONTROL); + *ppPacket = nbuf; + *data = qdf_nbuf_data(nbuf); + qdf_mem_zero(*data, size); + status = QDF_STATUS_SUCCESS; + } + + return status; +} + +/** + * p2p_send_tx_conf() - send tx confirm + * @tx_ctx: tx context + * @status: tx status + * + * This function send tx confirm to osif + * + * Return: QDF_STATUS_SUCCESS - pointer to tx context + */ +static QDF_STATUS p2p_send_tx_conf(struct tx_action_context *tx_ctx, + bool status) +{ + struct p2p_tx_cnf tx_cnf; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid p2p soc object or start parameters"); + return QDF_STATUS_E_INVAL; + } + + start_param = p2p_soc_obj->start_param; + if (!(start_param->tx_cnf_cb)) { + p2p_err("no tx confirm callback"); + return QDF_STATUS_E_INVAL; + } + + if (tx_ctx->no_ack) + tx_cnf.action_cookie = 0; + else + tx_cnf.action_cookie = (uint64_t)tx_ctx->id; + + tx_cnf.vdev_id = tx_ctx->vdev_id; + tx_cnf.buf = tx_ctx->buf; + tx_cnf.buf_len = tx_ctx->buf_len; + tx_cnf.status = status ? 0 : 1; + + p2p_debug("soc:%pK, vdev_id:%d, action_cookie:%llx, len:%d, status:%d, buf:%pK", + p2p_soc_obj->soc, tx_cnf.vdev_id, + tx_cnf.action_cookie, tx_cnf.buf_len, + tx_cnf.status, tx_cnf.buf); + + p2p_rand_mac_tx_done(p2p_soc_obj->soc, tx_ctx); + + start_param->tx_cnf_cb(start_param->tx_cnf_cb_data, &tx_cnf); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_mgmt_tx() - call mgmt tx api + * @tx_ctx: tx context + * @buf_len: buffer length + * @packet: pointer to qdf nbuf + * @frame: pointer to qdf nbuf data + * + * This function call mgmt tx api to tx this action frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_mgmt_tx(struct tx_action_context *tx_ctx, + uint32_t buf_len, qdf_nbuf_t packet, uint8_t *frame) +{ + QDF_STATUS status; + mgmt_tx_download_comp_cb tx_comp_cb; + mgmt_ota_comp_cb tx_ota_comp_cb; + struct wlan_frame_hdr *wh; + struct wlan_objmgr_peer *peer; + struct wmi_mgmt_params mgmt_param = { 0 }; + struct wlan_objmgr_psoc *psoc; + void *mac_addr; + uint8_t pdev_id; + struct wlan_objmgr_vdev *vdev; + uint16_t chanfreq = 0; + + psoc = tx_ctx->p2p_soc_obj->soc; + mgmt_param.tx_frame = packet; + mgmt_param.frm_len = buf_len; + mgmt_param.vdev_id = tx_ctx->vdev_id; + mgmt_param.pdata = frame; + if (tx_ctx->chan) + chanfreq = (uint16_t)wlan_chan_to_freq(tx_ctx->chan); + mgmt_param.chanfreq = chanfreq; + + mgmt_param.qdf_ctx = wlan_psoc_get_qdf_dev(psoc); + if (!(mgmt_param.qdf_ctx)) { + p2p_err("qdf ctx is null"); + return QDF_STATUS_E_INVAL; + } + + wh = (struct wlan_frame_hdr *)frame; + mac_addr = wh->i_addr1; + pdev_id = wlan_get_pdev_id_from_vdev_id(psoc, tx_ctx->vdev_id, + WLAN_P2P_ID); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_P2P_ID); + if (!peer) { + mac_addr = wh->i_addr2; + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + } + if (!peer && tx_ctx->rand_mac_tx) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, tx_ctx->vdev_id, WLAN_P2P_ID); + if (vdev) { + mac_addr = wlan_vdev_mlme_get_macaddr(vdev); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + } + } + + if (!peer) { + p2p_err("no valid peer"); + return QDF_STATUS_E_INVAL; + } + + if (tx_ctx->no_ack) { + tx_comp_cb = tgt_p2p_mgmt_download_comp_cb; + tx_ota_comp_cb = NULL; + } else { + tx_comp_cb = NULL; + tx_ota_comp_cb = tgt_p2p_mgmt_ota_comp_cb; + } + + p2p_debug("length:%d, chanfreq:%d", mgmt_param.frm_len, + mgmt_param.chanfreq); + + tx_ctx->nbuf = packet; + + status = wlan_mgmt_txrx_mgmt_frame_tx(peer, tx_ctx->p2p_soc_obj, + packet, tx_comp_cb, tx_ota_comp_cb, + WLAN_UMAC_COMP_P2P, &mgmt_param); + + wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_roc_req_for_tx_action() - new a roc request for tx + * @tx_ctx: tx context + * + * This function new a roc request for tx and call roc api to process + * this new roc request. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_roc_req_for_tx_action( + struct tx_action_context *tx_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *roc_ctx; + QDF_STATUS status; + + roc_ctx = qdf_mem_malloc(sizeof(struct p2p_roc_context)); + if (!roc_ctx) { + p2p_err("Failed to allocate p2p roc context"); + return QDF_STATUS_E_NOMEM; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + roc_ctx->p2p_soc_obj = p2p_soc_obj; + roc_ctx->vdev_id = tx_ctx->vdev_id; + roc_ctx->chan = tx_ctx->chan; + roc_ctx->duration = tx_ctx->duration; + roc_ctx->roc_state = ROC_STATE_IDLE; + roc_ctx->roc_type = OFF_CHANNEL_TX; + roc_ctx->tx_ctx = tx_ctx; + roc_ctx->id = tx_ctx->id; + tx_ctx->roc_cookie = (uintptr_t)roc_ctx; + + p2p_debug("create roc request for off channel tx, tx ctx:%pK, roc ctx:%pK", + tx_ctx, roc_ctx); + + status = p2p_process_roc_req(roc_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("request roc for tx action frrame fail"); + return status; + } + + status = qdf_list_insert_back(&p2p_soc_obj->tx_q_roc, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to insert off chan tx context to wait roc req queue"); + + return status; +} + +/** + * p2p_find_tx_ctx() - find tx context by cookie + * @p2p_soc_obj: p2p soc object + * @cookie: cookie to this p2p tx context + * @is_roc_q: it is in waiting for roc queue + * @is_ack_q: it is in waiting for ack queue + * + * This function finds out tx context by cookie. + * + * Return: pointer to tx context + */ +static struct tx_action_context *p2p_find_tx_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie, + bool *is_roc_q, bool *is_ack_q) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + *is_roc_q = false; + *is_ack_q = false; + + p2p_debug("Start to find tx ctx, p2p soc_obj:%pK, cookie:%llx", + p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + if ((uintptr_t) cur_tx_ctx == cookie) { + *is_roc_q = true; + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + } + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + if ((uintptr_t) cur_tx_ctx == cookie) { + *is_ack_q = true; + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + } + + return NULL; +} + +/** + * p2p_find_tx_ctx_by_roc() - find tx context by roc + * @p2p_soc_obj: p2p soc object + * @cookie: cookie to roc context + * + * This function finds out tx context by roc context. + * + * Return: pointer to tx context + */ +static struct tx_action_context *p2p_find_tx_ctx_by_roc( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("Start to find tx ctx, p2p soc_obj:%pK, cookie:%llx", + p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + if (cur_tx_ctx->roc_cookie == cookie) { + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + } + + return NULL; +} + +/** + * p2p_move_tx_context_to_ack_queue() - move tx context to tx_q_ack + * @tx_ctx: tx context + * + * This function moves tx context to waiting for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_move_tx_context_to_ack_queue( + struct tx_action_context *tx_ctx) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct p2p_soc_priv_obj *p2p_soc_obj = tx_ctx->p2p_soc_obj; + struct tx_action_context *cur_tx_ctx; + QDF_STATUS status; + + cur_tx_ctx = p2p_find_tx_ctx(p2p_soc_obj, (uintptr_t)tx_ctx, + &is_roc_q, &is_ack_q); + if (cur_tx_ctx) { + if (is_roc_q) { + p2p_debug("find in wait for roc queue"); + status = qdf_list_remove_node( + &p2p_soc_obj->tx_q_roc, + (qdf_list_node_t *)tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to remove off chan tx context from wait roc req queue"); + } + + if (is_ack_q) { + p2p_debug("Already in waiting for ack queue"); + return QDF_STATUS_SUCCESS; + } + } + + status = qdf_list_insert_back( + &p2p_soc_obj->tx_q_ack, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to insert off chan tx context to wait ack req queue"); + + return status; +} + +/** + * p2p_extend_roc_timer() - extend roc timer + * @p2p_soc_obj: p2p soc private object + * @frame_info: pointer to frame information + * + * This function extends roc timer for some of p2p public action frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_extend_roc_timer( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *frame_info) +{ + struct p2p_roc_context *curr_roc_ctx; + uint32_t extend_time; + + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (!curr_roc_ctx) { + p2p_debug("no running roc request currently"); + return QDF_STATUS_SUCCESS; + } + + if (!frame_info) { + p2p_err("invalid frame information"); + return QDF_STATUS_E_INVAL; + } + + switch (frame_info->public_action_type) { + case P2P_PUBLIC_ACTION_NEG_REQ: + case P2P_PUBLIC_ACTION_NEG_RSP: + extend_time = 2 * P2P_ACTION_FRAME_DEFAULT_WAIT; + break; + case P2P_PUBLIC_ACTION_INVIT_REQ: + case P2P_PUBLIC_ACTION_DEV_DIS_REQ: + extend_time = P2P_ACTION_FRAME_DEFAULT_WAIT; + break; + default: + extend_time = 0; + break; + } + + if (extend_time) { + p2p_debug("extend roc timer, duration:%d", extend_time); + curr_roc_ctx->duration = extend_time; + return p2p_restart_roc_timer(curr_roc_ctx); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_adjust_tx_wait() - adjust tx wait + * @tx_ctx: tx context + * + * This function adjust wait time of this tx context + * + * Return: None + */ +static void p2p_adjust_tx_wait(struct tx_action_context *tx_ctx) +{ + struct p2p_frame_info *frame_info; + + frame_info = &(tx_ctx->frame_info); + switch (frame_info->public_action_type) { + case P2P_PUBLIC_ACTION_NEG_RSP: + case P2P_PUBLIC_ACTION_PROV_DIS_RSP: + tx_ctx->duration += P2P_ACTION_FRAME_RSP_WAIT; + break; + case P2P_PUBLIC_ACTION_NEG_CNF: + case P2P_PUBLIC_ACTION_INVIT_RSP: + tx_ctx->duration += P2P_ACTION_FRAME_ACK_WAIT; + break; + default: + break; + } +} + +/** + * p2p_remove_tx_context() - remove tx ctx from queue + * @tx_ctx: tx context + * + * This function remove tx context from waiting for roc queue or + * waiting for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_remove_tx_context( + struct tx_action_context *tx_ctx) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct tx_action_context *cur_tx_ctx; + uint64_t cookie = (uintptr_t)tx_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj = tx_ctx->p2p_soc_obj; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_debug("tx context:%pK", tx_ctx); + + cur_tx_ctx = p2p_find_tx_ctx(p2p_soc_obj, cookie, &is_roc_q, + &is_ack_q); + + /* for not off channel tx case, won't find from queue */ + if (!cur_tx_ctx) { + p2p_debug("Do not find tx context from queue"); + goto end; + } + + if (is_roc_q) { + status = qdf_list_remove_node( + &p2p_soc_obj->tx_q_roc, + (qdf_list_node_t *)cur_tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to tx context from wait roc req queue"); + } + + if (is_ack_q) { + status = qdf_list_remove_node( + &p2p_soc_obj->tx_q_ack, + (qdf_list_node_t *)cur_tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to tx context from wait ack req queue"); + } + +end: + if (!tx_ctx->roc_cookie) + qdf_idr_remove(&p2p_soc_obj->p2p_idr, tx_ctx->id); + qdf_mem_free(tx_ctx->buf); + qdf_mem_free(tx_ctx); + + return status; +} + +/** + * p2p_tx_timeout() - Callback for tx timeout + * @pdata: pointer to tx context + * + * This function is callback for tx time out. + * + * Return: None + */ +static void p2p_tx_timeout(void *pdata) +{ + QDF_STATUS status, ret; + qdf_list_node_t *p_node; + struct tx_action_context *tx_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_info("pdata:%pK", pdata); + p2p_soc_obj = (struct p2p_soc_priv_obj *)pdata; + if (!p2p_soc_obj) { + p2p_err("null p2p soc obj"); + return; + } + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + if (QDF_TIMER_STATE_STOPPED == + qdf_mc_timer_get_current_state(&tx_ctx->tx_timer)) { + ret = qdf_list_remove_node(&p2p_soc_obj->tx_q_ack, + &tx_ctx->node); + if (ret == QDF_STATUS_SUCCESS) { + qdf_mc_timer_destroy(&tx_ctx->tx_timer); + p2p_send_tx_conf(tx_ctx, false); + qdf_mem_free(tx_ctx->buf); + qdf_mem_free(tx_ctx); + } else + p2p_err("remove %pK from roc_q fail", + tx_ctx); + } + } +} + +/** + * p2p_enable_tx_timer() - enable tx timer + * @tx_ctx: tx context + * + * This function enable tx timer for action frame required ota tx. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_enable_tx_timer(struct tx_action_context *tx_ctx) +{ + QDF_STATUS status; + + status = qdf_mc_timer_init(&tx_ctx->tx_timer, + QDF_TIMER_TYPE_SW, p2p_tx_timeout, + tx_ctx->p2p_soc_obj); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to init tx timer tx_ctx:%pK", tx_ctx); + return status; + } + + status = qdf_mc_timer_start(&tx_ctx->tx_timer, + P2P_ACTION_FRAME_TX_TIMEOUT); + if (status != QDF_STATUS_SUCCESS) + p2p_err("tx timer start failed tx_ctx:%pK", tx_ctx); + + return status; +} + +/** + * p2p_disable_tx_timer() - disable tx timer + * @tx_ctx: tx context + * + * This function disable tx timer for action frame required ota tx. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_disable_tx_timer(struct tx_action_context *tx_ctx) +{ + QDF_STATUS status; + + p2p_debug("tx context:%pK", tx_ctx); + + status = qdf_mc_timer_stop(&tx_ctx->tx_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop tx timer, status:%d", status); + + status = qdf_mc_timer_destroy(&tx_ctx->tx_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to destroy tx timer, status:%d", status); + + return status; +} + +/** + * is_rmf_mgmt_action_frame() - check RMF action frame by category + * @action_category: action frame actegory + * + * This function check the frame is robust mgmt action frame or not + * + * Return: true - if category is robust mgmt type + */ +static bool is_rmf_mgmt_action_frame(uint8_t action_category) +{ + switch (action_category) { + case ACTION_CATEGORY_SPECTRUM_MGMT: + case ACTION_CATEGORY_QOS: + case ACTION_CATEGORY_DLS: + case ACTION_CATEGORY_BACK: + case ACTION_CATEGORY_RRM: + case ACTION_FAST_BSS_TRNST: + case ACTION_CATEGORY_SA_QUERY: + case ACTION_CATEGORY_PROTECTED_DUAL_OF_PUBLIC_ACTION: + case ACTION_CATEGORY_WNM: + case ACTION_CATEGORY_MESH_ACTION: + case ACTION_CATEGORY_MULTIHOP_ACTION: + case ACTION_CATEGORY_DMG: + case ACTION_CATEGORY_FST: + case ACTION_CATEGORY_VENDOR_SPECIFIC_PROTECTED: + return true; + default: + break; + } + return false; +} + +/** + * p2p_populate_rmf_field() - populate unicast rmf frame + * @tx_ctx: tx_action_context + * @size: input size of frame, and output new size + * @ppbuf: input frame ptr, and output new frame + * @ppkt: input pkt, output new pkt. + * + * This function allocates new pkt for rmf frame. The + * new frame has extra space for ccmp field. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_populate_rmf_field(struct tx_action_context *tx_ctx, + uint32_t *size, uint8_t **ppbuf, qdf_nbuf_t *ppkt) +{ + struct wlan_frame_hdr *wh, *rmf_wh; + struct action_frm_hdr *action_hdr; + QDF_STATUS status = QDF_STATUS_SUCCESS; + qdf_nbuf_t pkt = NULL; + uint8_t *frame; + uint32_t frame_len; + struct p2p_soc_priv_obj *p2p_soc_obj; + uint8_t action_category; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + if (tx_ctx->frame_info.sub_type != P2P_MGMT_ACTION || + !p2p_soc_obj->p2p_cb.is_mgmt_protected) + return QDF_STATUS_SUCCESS; + if (*size < (sizeof(struct wlan_frame_hdr) + + sizeof(struct action_frm_hdr))) { + return QDF_STATUS_E_INVAL; + } + + wh = (struct wlan_frame_hdr *)(*ppbuf); + action_hdr = (struct action_frm_hdr *)(*ppbuf + sizeof(*wh)); + + /* + * For Action frame which are not handled, the resp is sent back to the + * source without change, except that MSB of the Category set to 1, so + * to get the actual action category we need to ignore the MSB. + */ + action_category = action_hdr->action_category & 0x7f; + if (!is_rmf_mgmt_action_frame(action_category)) { + p2p_debug("non rmf act frame 0x%x category %x", + tx_ctx->frame_info.sub_type, + action_hdr->action_category); + return QDF_STATUS_SUCCESS; + } + + if (!p2p_soc_obj->p2p_cb.is_mgmt_protected( + tx_ctx->vdev_id, wh->i_addr1)) { + p2p_debug("non rmf connection vdev %d "QDF_MAC_ADDR_FMT, + tx_ctx->vdev_id, QDF_MAC_ADDR_REF(wh->i_addr1)); + return QDF_STATUS_SUCCESS; + } + if (!qdf_is_macaddr_group((struct qdf_mac_addr *)wh->i_addr1) && + !qdf_is_macaddr_broadcast((struct qdf_mac_addr *)wh->i_addr1)) { + uint8_t mic_len, mic_hdr_len, pdev_id; + + pdev_id = + wlan_get_pdev_id_from_vdev_id(tx_ctx->p2p_soc_obj->soc, + tx_ctx->vdev_id, + WLAN_P2P_ID); + status = mlme_get_peer_mic_len(p2p_soc_obj->soc, pdev_id, + wh->i_addr1, &mic_len, + &mic_hdr_len); + if (QDF_IS_STATUS_ERROR(status)) { + p2p_err("Failed to get peer mic length."); + return status; + } + + frame_len = *size + mic_hdr_len + mic_len; + status = p2p_packet_alloc((uint16_t)frame_len, (void **)&frame, + &pkt); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to allocate %d bytes for rmf frame.", + frame_len); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_copy(frame, wh, sizeof(*wh)); + qdf_mem_copy(frame + sizeof(*wh) + mic_hdr_len, + *ppbuf + sizeof(*wh), + *size - sizeof(*wh)); + rmf_wh = (struct wlan_frame_hdr *)frame; + (rmf_wh)->i_fc[1] |= IEEE80211_FC1_WEP; + p2p_debug("set protection 0x%x cat %d "QDF_MAC_ADDR_FMT, + tx_ctx->frame_info.sub_type, + action_hdr->action_category, + QDF_MAC_ADDR_REF(wh->i_addr1)); + + qdf_nbuf_free(*ppkt); + *ppbuf = frame; + *ppkt = pkt; + *size = frame_len; + /* + * Some target which support sending mgmt frame based on htt + * would DMA write this PMF tx frame buffer, it may cause smmu + * check permission fault, set a flag to do bi-direction DMA + * map, normal tx unmap is enough for this case. + */ + QDF_NBUF_CB_TX_DMA_BI_MAP(pkt) = 1; + } + + return status; +} + +/** + * p2p_execute_tx_action_frame() - execute tx action frame + * @tx_ctx: tx context + * + * This function modify p2p ie and tx this action frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_execute_tx_action_frame( + struct tx_action_context *tx_ctx) +{ + uint8_t *frame; + qdf_nbuf_t packet; + QDF_STATUS status; + uint8_t noa_len = 0; + uint8_t noa_stream[P2P_NOA_STREAM_ARR_SIZE]; + uint8_t orig_len = 0; + const uint8_t *ie; + uint8_t ie_len; + uint8_t *p2p_ie = NULL; + const uint8_t *presence_noa_attr = NULL; + uint32_t nbytes_copy; + uint32_t buf_len = tx_ctx->buf_len; + struct p2p_frame_info *frame_info; + + frame_info = &(tx_ctx->frame_info); + if (frame_info->sub_type == P2P_MGMT_PROBE_RSP) { + p2p_ie = (uint8_t *)p2p_get_p2pie_from_probe_rsp(tx_ctx); + } else if (frame_info->action_type == + P2P_ACTION_PRESENCE_RSP) { + ie = tx_ctx->buf + + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + ie_len = tx_ctx->buf_len - + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + p2p_ie = (uint8_t *)p2p_get_p2pie_ptr(ie, ie_len); + if (p2p_ie) { + /* extract the presence of NoA attribute inside + * P2P IE */ + ie = p2p_ie + P2P_IE_HEADER_LEN; + ie_len = p2p_ie[1]; + presence_noa_attr = p2p_get_presence_noa_attr( + ie, ie_len); + } + } + + if ((frame_info->sub_type != P2P_MGMT_NOT_SUPPORT) && + p2p_ie) { + orig_len = p2p_ie[1]; + noa_len = p2p_update_noa_stream(tx_ctx, p2p_ie, + presence_noa_attr, &buf_len, + noa_stream); + buf_len += noa_len; + } + + if (frame_info->sub_type == P2P_MGMT_PROBE_RSP) + p2p_set_ht_caps(tx_ctx, buf_len); + + /* Ok-- try to allocate some memory: */ + status = p2p_packet_alloc((uint16_t) buf_len, (void **)&frame, + &packet); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to allocate %d bytes for a Probe Request.", + buf_len); + return status; + } + + /* + * Add sequence number to action frames + * Frames are handed over in .11 format by supplicant already + */ + p2p_populate_mac_header(tx_ctx); + + if ((noa_len > 0) && p2p_ie + && (noa_len < (P2P_MAX_NOA_ATTR_LEN + + P2P_IE_HEADER_LEN))) { + /* Add 2 bytes for length and Arribute field */ + nbytes_copy = (p2p_ie + orig_len + 2) - tx_ctx->buf; + qdf_mem_copy(frame, tx_ctx->buf, nbytes_copy); + qdf_mem_copy((frame + nbytes_copy), noa_stream, + noa_len); + qdf_mem_copy((frame + nbytes_copy + noa_len), + tx_ctx->buf + nbytes_copy, + buf_len - nbytes_copy - noa_len); + } else { + qdf_mem_copy(frame, tx_ctx->buf, buf_len); + } + + status = p2p_populate_rmf_field(tx_ctx, &buf_len, &frame, &packet); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to populate rmf frame"); + qdf_nbuf_free(packet); + return status; + } + status = p2p_mgmt_tx(tx_ctx, buf_len, packet, frame); + if (status == QDF_STATUS_SUCCESS) { + if (tx_ctx->no_ack) { + p2p_send_tx_conf(tx_ctx, true); + p2p_remove_tx_context(tx_ctx); + } else { + p2p_enable_tx_timer(tx_ctx); + p2p_move_tx_context_to_ack_queue(tx_ctx); + } + } else { + p2p_err("failed to tx mgmt frame"); + qdf_nbuf_free(packet); + } + + return status; +} + +struct tx_action_context *p2p_find_tx_ctx_by_nbuf( + struct p2p_soc_priv_obj *p2p_soc_obj, void *nbuf) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + if (!p2p_soc_obj) { + p2p_err("invalid p2p soc object"); + return NULL; + } + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = + qdf_container_of(p_node, struct tx_action_context, node); + if (cur_tx_ctx->nbuf == nbuf) { + p2p_debug("find tx ctx, nbuf:%pK", nbuf); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + } + + return NULL; +} + +void p2p_dump_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj) +{ + struct tx_action_context *tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("dump tx queue wait for roc, p2p soc obj:%pK, size:%d", + p2p_soc_obj, qdf_list_size(&p2p_soc_obj->tx_q_roc)); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + p2p_debug("p2p soc object:%pK, tx ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj, tx_ctx, + tx_ctx->vdev_id, tx_ctx->scan_id, + tx_ctx->roc_cookie, tx_ctx->chan, + tx_ctx->buf, tx_ctx->buf_len, + tx_ctx->off_chan, tx_ctx->no_cck, + tx_ctx->no_ack, tx_ctx->duration); + + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + } + + p2p_debug("dump tx queue wait for ack, size:%d", + qdf_list_size(&p2p_soc_obj->tx_q_ack)); + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + p2p_debug("p2p soc object:%pK, tx_ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj, tx_ctx, + tx_ctx->vdev_id, tx_ctx->scan_id, + tx_ctx->roc_cookie, tx_ctx->chan, + tx_ctx->buf, tx_ctx->buf_len, + tx_ctx->off_chan, tx_ctx->no_cck, + tx_ctx->no_ack, tx_ctx->duration); + + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + } +} + +QDF_STATUS p2p_ready_to_tx_frame(struct p2p_soc_priv_obj *p2p_soc_obj, + uint64_t cookie) +{ + struct tx_action_context *cur_tx_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + cur_tx_ctx = p2p_find_tx_ctx_by_roc(p2p_soc_obj, cookie); + + while (cur_tx_ctx) { + p2p_debug("tx_ctx:%pK", cur_tx_ctx); + status = p2p_execute_tx_action_frame(cur_tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_send_tx_conf(cur_tx_ctx, false); + p2p_remove_tx_context(cur_tx_ctx); + } + cur_tx_ctx = p2p_find_tx_ctx_by_roc(p2p_soc_obj, cookie); + } + + return status; +} + +QDF_STATUS p2p_cleanup_tx_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev) +{ + struct scheduler_msg msg = {0}; + struct p2p_cleanup_param *param; + QDF_STATUS status; + uint32_t vdev_id; + + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_debug("p2p_soc_obj:%pK, vdev:%pK", p2p_soc_obj, vdev); + param = qdf_mem_malloc(sizeof(*param)); + if (!param) { + p2p_err("failed to allocate cleanup param"); + return QDF_STATUS_E_NOMEM; + } + + param->p2p_soc_obj = p2p_soc_obj; + if (vdev) + vdev_id = (uint32_t)wlan_vdev_get_id(vdev); + else + vdev_id = P2P_INVALID_VDEV_ID; + param->vdev_id = vdev_id; + qdf_event_reset(&p2p_soc_obj->cleanup_tx_done); + msg.type = P2P_CLEANUP_TX; + msg.bodyptr = param; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, &msg); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(param); + return status; + } + + status = qdf_wait_single_event( + &p2p_soc_obj->cleanup_tx_done, + P2P_WAIT_CLEANUP_ROC); + + if (status != QDF_STATUS_SUCCESS) + p2p_err("wait for cleanup tx timeout, %d", status); + + return status; +} + +QDF_STATUS p2p_process_cleanup_tx_queue(struct p2p_cleanup_param *param) +{ + struct tx_action_context *curr_tx_ctx; + qdf_list_node_t *p_node; + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; + QDF_STATUS status, ret; + + if (!param || !(param->p2p_soc_obj)) { + p2p_err("Invalid cleanup param"); + return QDF_STATUS_E_FAILURE; + } + + p2p_soc_obj = param->p2p_soc_obj; + vdev_id = param->vdev_id; + + p2p_debug("clean up tx queue wait for roc, size:%d, vdev_id:%d", + qdf_list_size(&p2p_soc_obj->tx_q_roc), vdev_id); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + if ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == curr_tx_ctx->vdev_id)) { + ret = qdf_list_remove_node(&p2p_soc_obj->tx_q_roc, + &curr_tx_ctx->node); + if (ret == QDF_STATUS_SUCCESS) { + p2p_send_tx_conf(curr_tx_ctx, false); + qdf_mem_free(curr_tx_ctx->buf); + qdf_mem_free(curr_tx_ctx); + } else + p2p_err("remove %pK from roc_q fail", + curr_tx_ctx); + } + } + + p2p_debug("clean up tx queue wait for ack, size:%d", + qdf_list_size(&p2p_soc_obj->tx_q_ack)); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + if ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == curr_tx_ctx->vdev_id)) { + ret = qdf_list_remove_node(&p2p_soc_obj->tx_q_ack, + &curr_tx_ctx->node); + if (ret == QDF_STATUS_SUCCESS) { + p2p_disable_tx_timer(curr_tx_ctx); + p2p_send_tx_conf(curr_tx_ctx, false); + qdf_mem_free(curr_tx_ctx->buf); + qdf_mem_free(curr_tx_ctx); + } else + p2p_err("remove %pK from roc_q fail", + curr_tx_ctx); + } + } + + qdf_event_set(&p2p_soc_obj->cleanup_tx_done); + + return QDF_STATUS_SUCCESS; +} + +bool p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr) +{ + uint32_t i = 0; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + return false; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("p2p vdev object is NULL"); + return false; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + if ((p2p_vdev_obj->random_mac[i].in_use) && + (!qdf_mem_cmp(p2p_vdev_obj->random_mac[i].addr, + random_mac_addr, QDF_MAC_ADDR_SIZE))) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + return true; + } + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return false; +} + +/** + * find_action_frame_cookie() - Checks for action cookie in cookie list + * @cookie_list: List of cookies + * @rnd_cookie: Cookie to be searched + * + * Return: If search is successful return pointer to action_frame_cookie + * object in which cookie item is encapsulated. + */ +static struct action_frame_cookie * +find_action_frame_cookie(qdf_list_t *cookie_list, uint64_t rnd_cookie) +{ + struct action_frame_cookie *action_cookie; + + qdf_list_for_each(cookie_list, action_cookie, cookie_node) { + if (action_cookie->cookie == rnd_cookie) + return action_cookie; + } + + return NULL; +} + +/** + * allocate_action_frame_cookie() - Allocate and add action cookie to + * given list + * @cookie_list: List of cookies + * @rnd_cookie: Cookie to be added + * + * Return: If allocation and addition is successful return pointer to + * action_frame_cookie object in which cookie item is encapsulated. + */ +static struct action_frame_cookie * +allocate_action_frame_cookie(qdf_list_t *cookie_list, uint64_t rnd_cookie) +{ + struct action_frame_cookie *action_cookie; + + action_cookie = qdf_mem_malloc(sizeof(*action_cookie)); + if (!action_cookie) + return NULL; + + action_cookie->cookie = rnd_cookie; + qdf_list_insert_front(cookie_list, &action_cookie->cookie_node); + + return action_cookie; +} + +/** + * delete_action_frame_cookie() - Delete the cookie from given list + * @cookie_list: List of cookies + * @action_cookie: Cookie to be deleted + * + * This function deletes the cookie item from given list and corresponding + * object in which it is encapsulated. + * + * Return: None + */ +static void +delete_action_frame_cookie(qdf_list_t *cookie_list, + struct action_frame_cookie *action_cookie) +{ + qdf_list_remove_node(cookie_list, &action_cookie->cookie_node); + qdf_mem_free(action_cookie); +} + +/** + * delete_all_action_frame_cookie() - Delete all the cookies to given list + * @cookie_list: List of cookies + * + * This function deletes all the cookies from from given list. + * + * Return: None + */ +static void +delete_all_action_frame_cookie(qdf_list_t *cookie_list) +{ + qdf_list_node_t *node = NULL; + + p2p_debug("Delete cookie list %pK, size %d", cookie_list, + qdf_list_size(cookie_list)); + + while (!qdf_list_empty(cookie_list)) { + qdf_list_remove_front(cookie_list, &node); + qdf_mem_free(node); + } +} + +/** + * append_action_frame_cookie() - Append action cookie to given list + * @cookie_list: List of cookies + * @rnd_cookie: Cookie to be append + * + * This is a wrapper function which invokes allocate_action_frame_cookie + * if the cookie to be added is not duplicate + * + * Return: true - for successful case + * false - failed. + */ +static bool +append_action_frame_cookie(qdf_list_t *cookie_list, uint64_t rnd_cookie) +{ + struct action_frame_cookie *action_cookie; + + /* + * There should be no mac entry with empty cookie list, + * check and ignore if duplicate + */ + action_cookie = find_action_frame_cookie(cookie_list, rnd_cookie); + if (action_cookie) + /* random mac address is already programmed */ + return true; + + /* insert new cookie in cookie list */ + action_cookie = allocate_action_frame_cookie(cookie_list, rnd_cookie); + if (!action_cookie) + return false; + + return true; +} + +/** + * p2p_add_random_mac() - add or append random mac to given vdev rand mac list + * @soc: soc object + * @vdev_id: vdev id + * @mac: mac addr to be added or append + * @freq: frequency + * @rnd_cookie: random mac mgmt tx cookie + * + * This function will add or append the mac addr entry to vdev random mac list. + * Once the mac addr filter is not needed, it can be removed by + * p2p_del_random_mac. + * + * Return: QDF_STATUS_E_EXISTS - append to existing list + * QDF_STATUS_SUCCESS - add a new entry. + * other : failed to add the mac address entry. + */ +static QDF_STATUS +p2p_add_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, uint64_t rnd_cookie) +{ + uint32_t i; + uint32_t first_unused = MAX_RANDOM_MAC_ADDRS; + struct action_frame_cookie *action_cookie; + int32_t append_ret; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + p2p_debug("random_mac:vdev %d mac_addr:"QDF_MAC_ADDR_FMT" rnd_cookie=%llu freq = %u", + vdev_id, QDF_MAC_ADDR_REF(mac), rnd_cookie, freq); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + + return QDF_STATUS_E_INVAL; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_debug("random_mac:p2p vdev object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + /* + * Following loop checks whether random mac entry is already + * present, if present get the index of matched entry else + * get the first unused slot to store this new random mac + */ + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + if (!p2p_vdev_obj->random_mac[i].in_use) { + if (first_unused == MAX_RANDOM_MAC_ADDRS) + first_unused = i; + continue; + } + + if (!qdf_mem_cmp(p2p_vdev_obj->random_mac[i].addr, mac, + QDF_MAC_ADDR_SIZE)) + break; + } + + if (i != MAX_RANDOM_MAC_ADDRS) { + append_ret = append_action_frame_cookie( + &p2p_vdev_obj->random_mac[i].cookie_list, + rnd_cookie); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:append %d vdev %d freq %d "QDF_MAC_ADDR_FMT" rnd_cookie %llu", + append_ret, vdev_id, freq, QDF_MAC_ADDR_REF(mac), rnd_cookie); + if (!append_ret) { + p2p_debug("random_mac:failed to append rnd_cookie"); + return QDF_STATUS_E_NOMEM; + } + + return QDF_STATUS_E_EXISTS; + } + + if (first_unused == MAX_RANDOM_MAC_ADDRS) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:Reached the limit of Max random addresses"); + + return QDF_STATUS_E_RESOURCES; + } + + /* get the first unused buf and store new random mac */ + i = first_unused; + + action_cookie = allocate_action_frame_cookie( + &p2p_vdev_obj->random_mac[i].cookie_list, + rnd_cookie); + if (!action_cookie) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_err("random_mac:failed to alloc rnd cookie"); + + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(p2p_vdev_obj->random_mac[i].addr, mac, QDF_MAC_ADDR_SIZE); + p2p_vdev_obj->random_mac[i].in_use = true; + p2p_vdev_obj->random_mac[i].freq = freq; + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:add vdev %d freq %d "QDF_MAC_ADDR_FMT" rnd_cookie %llu", + vdev_id, freq, QDF_MAC_ADDR_REF(mac), rnd_cookie); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +p2p_del_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint64_t rnd_cookie) +{ + uint32_t i; + struct action_frame_cookie *action_cookie; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + p2p_debug("random_mac:vdev %d cookie %llu", vdev_id, rnd_cookie); + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + return QDF_STATUS_E_INVAL; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("p2p vdev object is NULL"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + struct action_frame_random_mac *random_mac; + qdf_freq_t freq; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + + random_mac = &p2p_vdev_obj->random_mac[i]; + if (!random_mac->in_use) + continue; + + action_cookie = find_action_frame_cookie( + &random_mac->cookie_list, rnd_cookie); + if (!action_cookie) + continue; + + delete_action_frame_cookie( + &random_mac->cookie_list, + action_cookie); + + if (qdf_list_empty(&random_mac->cookie_list)) { + random_mac->in_use = false; + freq = random_mac->freq; + qdf_mem_copy(addr, random_mac->addr, QDF_MAC_ADDR_SIZE); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + if (qdf_mc_timer_get_current_state( + &random_mac->clear_timer) == + QDF_TIMER_STATE_RUNNING) { + p2p_debug("random_mac:stop timer on vdev %d addr " QDF_MAC_ADDR_FMT, + vdev_id, QDF_MAC_ADDR_REF(addr)); + qdf_mc_timer_stop(&random_mac->clear_timer); + } + + p2p_clear_mac_filter( + wlan_vdev_get_psoc(p2p_vdev_obj->vdev), + vdev_id, addr, freq); + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + p2p_debug("random_mac:noref on vdev %d addr "QDF_MAC_ADDR_FMT, + vdev_id, QDF_MAC_ADDR_REF(addr)); + } + break; + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +p2p_random_mac_handle_tx_done(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint64_t rnd_cookie, uint32_t duration) +{ + uint32_t i; + struct action_frame_cookie *action_cookie; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + p2p_debug("random_mac:vdev %d cookie %llu duration %d", vdev_id, + rnd_cookie, duration); + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + return QDF_STATUS_E_INVAL; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("p2p vdev object is NULL"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + struct action_frame_random_mac *random_mac; + qdf_freq_t freq; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + + random_mac = &p2p_vdev_obj->random_mac[i]; + if (!random_mac->in_use) + continue; + action_cookie = find_action_frame_cookie( + &random_mac->cookie_list, rnd_cookie); + if (!action_cookie) + continue; + + /* If duration is zero then remove the cookie and also remove + * the filter from firmware. + */ + if (!duration) { + delete_action_frame_cookie(&random_mac->cookie_list, + action_cookie); + p2p_debug("random mac:clear mac addr on vdev %d addr " QDF_MAC_ADDR_FMT, + vdev_id, QDF_MAC_ADDR_REF(random_mac->addr)); + + if (qdf_list_empty(&random_mac->cookie_list)) { + random_mac->in_use = false; + freq = random_mac->freq; + qdf_mem_copy(addr, random_mac->addr, + QDF_MAC_ADDR_SIZE); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + p2p_clear_mac_filter( + wlan_vdev_get_psoc(p2p_vdev_obj->vdev), + vdev_id, addr, freq); + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + } + } else { + /* As duration is non zero start the timer for this + * duration. while the timer is running if tx cancel + * comes from supplicant then cookie will be removed + * and random mac filter will be removed from firmware. + * same thing will happen if timer expires without tx + * cancel from supplicant + */ + qdf_mem_copy(addr, random_mac->addr, QDF_MAC_ADDR_SIZE); + + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + if (qdf_mc_timer_get_current_state( + &random_mac->clear_timer) == + QDF_TIMER_STATE_RUNNING) + qdf_mc_timer_stop(&random_mac->clear_timer); + qdf_mc_timer_start(&random_mac->clear_timer, duration); + p2p_debug("random_mac:start timer on vdev %d addr " QDF_MAC_ADDR_FMT, + vdev_id, QDF_MAC_ADDR_REF(addr)); + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + } + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +void p2p_del_all_rand_mac_vdev(struct wlan_objmgr_vdev *vdev) +{ + int32_t i; + uint32_t freq; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + + if (!vdev) + return; + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) + return; + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + struct action_frame_cookie *action_cookie; + struct action_frame_cookie *action_cookie_next; + + if (!p2p_vdev_obj->random_mac[i].in_use) + continue; + + /* empty the list and clear random addr */ + qdf_list_for_each_del(&p2p_vdev_obj->random_mac[i].cookie_list, + action_cookie, action_cookie_next, + cookie_node) { + qdf_list_remove_node( + &p2p_vdev_obj->random_mac[i].cookie_list, + &action_cookie->cookie_node); + qdf_mem_free(action_cookie); + } + + p2p_vdev_obj->random_mac[i].in_use = false; + freq = p2p_vdev_obj->random_mac[i].freq; + qdf_mem_copy(addr, p2p_vdev_obj->random_mac[i].addr, + QDF_MAC_ADDR_SIZE); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + qdf_mc_timer_stop(&p2p_vdev_obj->random_mac[i].clear_timer); + p2p_clear_mac_filter(wlan_vdev_get_psoc(vdev), + wlan_vdev_get_id(vdev), addr, freq); + p2p_debug("random_mac:delall vdev %d freq %d addr "QDF_MAC_ADDR_FMT, + wlan_vdev_get_id(vdev), freq, QDF_MAC_ADDR_REF(addr)); + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); +} + +static void +p2p_del_rand_mac_vdev_enum_handler(struct wlan_objmgr_psoc *psoc, + void *obj, void *arg) +{ + struct wlan_objmgr_vdev *vdev = obj; + + if (!vdev) { + p2p_err("random_mac:invalid vdev"); + return; + } + + if (!p2p_is_vdev_support_rand_mac(vdev)) + return; + + p2p_del_all_rand_mac_vdev(vdev); +} + +void p2p_del_all_rand_mac_soc(struct wlan_objmgr_psoc *soc) +{ + if (!soc) { + p2p_err("random_mac:soc object is NULL"); + return; + } + + wlan_objmgr_iterate_obj_list(soc, WLAN_VDEV_OP, + p2p_del_rand_mac_vdev_enum_handler, + NULL, 0, WLAN_P2P_ID); +} + +/** + * p2p_is_random_mac() - check mac addr is random mac for vdev + * @soc: soc object + * @vdev_id: vdev id + * @mac: mac addr to be added or append + * + * This function will check the source mac addr same as vdev's mac addr or not. + * If not same, then the source mac addr should be random mac addr. + * + * Return: true if mac is random mac, otherwise false + */ +static bool +p2p_is_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, uint8_t *mac) +{ + bool ret = false; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("random_mac:vdev is null"); + return false; + } + + if (qdf_mem_cmp(wlan_vdev_mlme_get_macaddr(vdev), + mac, QDF_MAC_ADDR_SIZE)) + ret = true; + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return ret; +} + +static void p2p_set_mac_filter_callback(bool result, void *context) +{ + struct osif_request *request; + struct random_mac_priv *priv; + + p2p_debug("random_mac:set random mac filter result %d", result); + request = osif_request_get(context); + if (!request) { + p2p_err("random_mac:invalid response"); + return; + } + + priv = osif_request_priv(request); + priv->result = result; + + osif_request_complete(request); + osif_request_put(request); +} + +QDF_STATUS p2p_process_set_rand_mac_rsp(struct p2p_mac_filter_rsp *resp) +{ + struct wlan_objmgr_psoc *soc; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + if (!resp || !resp->p2p_soc_obj || !resp->p2p_soc_obj->soc) { + p2p_debug("random_mac:set_filter_req is null"); + return QDF_STATUS_E_INVAL; + } + p2p_debug("random_mac:process rsp on vdev %d status %d", resp->vdev_id, + resp->status); + soc = resp->p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, resp->vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_debug("random_mac:vdev is null vdev %d", resp->vdev_id); + return QDF_STATUS_E_INVAL; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:p2p_vdev_obj is null vdev %d", + resp->vdev_id); + return QDF_STATUS_E_INVAL; + } + if (!p2p_vdev_obj->pending_req.soc) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:no pending set req for vdev %d", + resp->vdev_id); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("random_mac:get pending req on vdev %d set %d mac filter "QDF_MAC_ADDR_FMT" freq %d", + p2p_vdev_obj->pending_req.vdev_id, + p2p_vdev_obj->pending_req.set, + QDF_MAC_ADDR_REF(p2p_vdev_obj->pending_req.mac), + p2p_vdev_obj->pending_req.freq); + if (p2p_vdev_obj->pending_req.cb) + p2p_vdev_obj->pending_req.cb( + !!resp->status, p2p_vdev_obj->pending_req.req_cookie); + + qdf_mem_zero(&p2p_vdev_obj->pending_req, + sizeof(p2p_vdev_obj->pending_req)); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +p2p_process_set_rand_mac(struct p2p_set_mac_filter_req *set_filter_req) +{ + struct wlan_objmgr_psoc *soc; + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct p2p_set_mac_filter param; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + if (!set_filter_req || !set_filter_req->soc) { + p2p_debug("random_mac:set_filter_req is null"); + return QDF_STATUS_E_INVAL; + } + p2p_debug("random_mac:vdev %d set %d mac filter "QDF_MAC_ADDR_FMT" freq %d", + set_filter_req->vdev_id, set_filter_req->set, + QDF_MAC_ADDR_REF(set_filter_req->mac), set_filter_req->freq); + + soc = set_filter_req->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + soc, set_filter_req->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("random_mac:vdev is null vdev %d", + set_filter_req->vdev_id); + goto get_vdev_failed; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_debug("random_mac:p2p_vdev_obj is null vdev %d", + set_filter_req->vdev_id); + goto get_p2p_obj_failed; + } + if (p2p_vdev_obj->pending_req.soc) { + p2p_debug("random_mac:Busy on vdev %d set %d mac filter "QDF_MAC_ADDR_FMT" freq %d", + p2p_vdev_obj->pending_req.vdev_id, + p2p_vdev_obj->pending_req.set, + QDF_MAC_ADDR_REF(p2p_vdev_obj->pending_req.mac), + p2p_vdev_obj->pending_req.freq); + goto get_p2p_obj_failed; + } + + p2p_ops = p2p_psoc_get_tx_ops(soc); + if (p2p_ops && p2p_ops->set_mac_addr_rx_filter_cmd) { + qdf_mem_zero(¶m, sizeof(param)); + param.vdev_id = set_filter_req->vdev_id; + qdf_mem_copy(param.mac, set_filter_req->mac, + QDF_MAC_ADDR_SIZE); + param.freq = set_filter_req->freq; + param.set = set_filter_req->set; + status = p2p_ops->set_mac_addr_rx_filter_cmd(soc, ¶m); + if (status == QDF_STATUS_SUCCESS && set_filter_req->set) + qdf_mem_copy(&p2p_vdev_obj->pending_req, + set_filter_req, sizeof(*set_filter_req)); + p2p_debug("random_mac:p2p set mac addr rx filter, status:%d", + status); + } + +get_p2p_obj_failed: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + +get_vdev_failed: + if (status != QDF_STATUS_SUCCESS && + set_filter_req->cb) + set_filter_req->cb(false, set_filter_req->req_cookie); + + return status; +} + +/** + * p2p_set_mac_filter() - send set mac addr filter cmd + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * @set: set or clear + * @cb: callback func to be called when the request completed. + * @req_cookie: cookie to be returned + * + * This function send set random mac addr filter command to p2p component + * msg core + * + * Return: QDF_STATUS_SUCCESS - if sent successfully. + * otherwise : failed. + */ +static QDF_STATUS +p2p_set_mac_filter(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, bool set, + p2p_request_mgr_callback_t cb, void *req_cookie) +{ + struct p2p_set_mac_filter_req *set_filter_req; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + p2p_debug("random_mac:vdev %d freq %d set %d "QDF_MAC_ADDR_FMT, + vdev_id, freq, set, QDF_MAC_ADDR_REF(mac)); + + set_filter_req = qdf_mem_malloc(sizeof(*set_filter_req)); + if (!set_filter_req) + return QDF_STATUS_E_NOMEM; + + set_filter_req->soc = soc; + set_filter_req->vdev_id = vdev_id; + set_filter_req->freq = freq; + qdf_mem_copy(set_filter_req->mac, mac, QDF_MAC_ADDR_SIZE); + set_filter_req->set = set; + set_filter_req->cb = cb; + set_filter_req->req_cookie = req_cookie; + + msg.type = P2P_SET_RANDOM_MAC; + msg.bodyptr = set_filter_req; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_P2P, QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, &msg); + if (status != QDF_STATUS_SUCCESS) + qdf_mem_free(set_filter_req); + + return status; +} + +QDF_STATUS +p2p_clear_mac_filter(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq) +{ + return p2p_set_mac_filter(soc, vdev_id, mac, freq, false, NULL, NULL); +} + +bool +p2p_is_vdev_support_rand_mac(struct wlan_objmgr_vdev *vdev) +{ + enum QDF_OPMODE mode; + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode == QDF_STA_MODE || + mode == QDF_P2P_CLIENT_MODE || + mode == QDF_P2P_DEVICE_MODE) + return true; + return false; +} + +/** + * p2p_is_vdev_support_rand_mac_by_id() - check vdev type support random mac + * mgmt tx or not + * @soc: soc obj + * @vdev_id: vdev id + * + * Return: true: support random mac mgmt tx + * false: not support random mac mgmt tx. + */ +static bool +p2p_is_vdev_support_rand_mac_by_id(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + bool ret = false; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) + return false; + ret = p2p_is_vdev_support_rand_mac(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return ret; +} + +/** + * p2p_set_rand_mac() - set random mac address rx filter + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * @rnd_cookie: cookie to be returned + * + * This function will post msg to p2p core to set random mac addr rx filter. + * It will wait the respone and return the result to caller. + * + * Return: true: set successfully + * false: failed + */ +static bool +p2p_set_rand_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, uint64_t rnd_cookie) +{ + bool ret = false; + int err; + QDF_STATUS status; + struct osif_request *request; + static const struct osif_request_params params = { + .priv_size = sizeof(struct random_mac_priv), + .timeout_ms = WLAN_WAIT_TIME_SET_RND, + }; + void *req_cookie; + struct random_mac_priv *priv; + + request = osif_request_alloc(¶ms); + if (!request) { + p2p_err("Request allocation failure"); + return false; + } + + req_cookie = osif_request_cookie(request); + + status = p2p_set_mac_filter(soc, vdev_id, mac, freq, true, + p2p_set_mac_filter_callback, req_cookie); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("random_mac:set mac fitler failure %d", status); + } else { + err = osif_request_wait_for_response(request); + if (err) { + p2p_err("random_mac:timeout for set mac fitler %d", + err); + } else { + priv = osif_request_priv(request); + ret = priv->result; + p2p_debug("random_mac:vdev %d freq %d result %d "QDF_MAC_ADDR_FMT" rnd_cookie %llu", + vdev_id, freq, priv->result, + QDF_MAC_ADDR_REF(mac), rnd_cookie); + } + } + osif_request_put(request); + + return ret; +} + +/** + * p2p_mac_clear_timeout() - clear random mac filter timeout + * @context: timer context + * + * This function will clear the mac addr rx filter in target if no + * reference to it. + * + * Return: void + */ +static void p2p_mac_clear_timeout(void *context) +{ + struct action_frame_random_mac *random_mac = context; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + uint32_t freq; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + uint32_t vdev_id; + + if (!random_mac || !random_mac->p2p_vdev_obj) { + p2p_err("invalid context for mac_clear timeout"); + return; + } + p2p_vdev_obj = random_mac->p2p_vdev_obj; + if (!p2p_vdev_obj || !p2p_vdev_obj->vdev) + return; + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + + delete_all_action_frame_cookie(&random_mac->cookie_list); + random_mac->in_use = false; + freq = random_mac->freq; + qdf_mem_copy(addr, random_mac->addr, QDF_MAC_ADDR_SIZE); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + + vdev_id = wlan_vdev_get_id(p2p_vdev_obj->vdev); + p2p_debug("random_mac:clear timeout vdev %d " QDF_MAC_ADDR_FMT " freq %d", + vdev_id, QDF_MAC_ADDR_REF(addr), freq); + + p2p_clear_mac_filter(wlan_vdev_get_psoc(p2p_vdev_obj->vdev), + vdev_id, addr, freq); +} + +/** + * p2p_request_random_mac() - request random mac mgmt tx + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * @rnd_cookie: cookie to be returned + * @duration: duration of tx timeout + * + * This function will add/append the random mac addr filter entry to vdev. + * If it is new added entry, it will request to set filter in target. + * + * Return: QDF_STATUS_SUCCESS: request successfully + * other: failed + */ +static QDF_STATUS +p2p_request_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, uint64_t rnd_cookie, + uint32_t duration) +{ + QDF_STATUS status; + uint32_t i; + struct wlan_objmgr_vdev *vdev; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + + status = p2p_add_random_mac(soc, vdev_id, mac, freq, rnd_cookie); + if (status == QDF_STATUS_E_EXISTS) + return QDF_STATUS_SUCCESS; + + else if (status != QDF_STATUS_SUCCESS) + return status; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + return QDF_STATUS_E_INVAL; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_debug("random_mac:p2p vdev object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + return QDF_STATUS_E_INVAL; + } + + if (!p2p_set_rand_mac(soc, vdev_id, mac, freq, rnd_cookie)) { + p2p_debug("random mac: failed to set rand mac address"); + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + if (!qdf_mem_cmp(p2p_vdev_obj->random_mac[i].addr, mac, + QDF_MAC_ADDR_SIZE)) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + p2p_mac_clear_timeout( + &p2p_vdev_obj->random_mac[i]); + status = QDF_STATUS_SUCCESS; + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + break; + } + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + return status; +} + +void p2p_rand_mac_tx(struct tx_action_context *tx_action) +{ + struct wlan_objmgr_psoc *soc; + QDF_STATUS status; + + if (!tx_action || !tx_action->p2p_soc_obj || + !tx_action->p2p_soc_obj->soc) + return; + soc = tx_action->p2p_soc_obj->soc; + + if (!tx_action->no_ack && tx_action->chan && + tx_action->buf_len > MIN_MAC_HEADER_LEN && + p2p_is_vdev_support_rand_mac_by_id(soc, tx_action->vdev_id) && + p2p_is_random_mac(soc, tx_action->vdev_id, + &tx_action->buf[SRC_MAC_ADDR_OFFSET])) { + status = p2p_request_random_mac( + soc, tx_action->vdev_id, + &tx_action->buf[SRC_MAC_ADDR_OFFSET], + wlan_chan_to_freq(tx_action->chan), + tx_action->id, + tx_action->duration); + if (status == QDF_STATUS_SUCCESS) + tx_action->rand_mac_tx = true; + else + tx_action->rand_mac_tx = false; + } +} + +void +p2p_rand_mac_tx_done(struct wlan_objmgr_psoc *soc, + struct tx_action_context *tx_ctx) +{ + if (!tx_ctx || !tx_ctx->rand_mac_tx || !soc) + return; + + p2p_random_mac_handle_tx_done(soc, tx_ctx->vdev_id, tx_ctx->id, + tx_ctx->duration); +} + +void p2p_init_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj) +{ + int32_t i; + + qdf_spinlock_create(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + qdf_mem_zero(&p2p_vdev_obj->random_mac[i], + sizeof(struct action_frame_random_mac)); + p2p_vdev_obj->random_mac[i].in_use = false; + p2p_vdev_obj->random_mac[i].p2p_vdev_obj = p2p_vdev_obj; + qdf_list_create(&p2p_vdev_obj->random_mac[i].cookie_list, 0); + qdf_mc_timer_init(&p2p_vdev_obj->random_mac[i].clear_timer, + QDF_TIMER_TYPE_SW, p2p_mac_clear_timeout, + &p2p_vdev_obj->random_mac[i]); + } +} + +void p2p_deinit_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj) +{ + int32_t i; + + p2p_del_all_rand_mac_vdev(p2p_vdev_obj->vdev); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + qdf_mc_timer_destroy(&p2p_vdev_obj->random_mac[i].clear_timer); + qdf_list_destroy(&p2p_vdev_obj->random_mac[i].cookie_list); + } + qdf_spinlock_destroy(&p2p_vdev_obj->random_mac_lock); +} + +QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + uint8_t *mac_to; + QDF_STATUS status; + + status = p2p_tx_context_check_valid(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("invalid tx action context"); + if (tx_ctx) { + if (tx_ctx->buf) { + p2p_send_tx_conf(tx_ctx, false); + qdf_mem_free(tx_ctx->buf); + } + qdf_mem_free(tx_ctx); + } + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + p2p_debug("soc:%pK, tx_ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj->soc, tx_ctx, tx_ctx->vdev_id, + tx_ctx->scan_id, tx_ctx->roc_cookie, tx_ctx->chan, + tx_ctx->buf, tx_ctx->buf_len, tx_ctx->off_chan, + tx_ctx->no_cck, tx_ctx->no_ack, tx_ctx->duration); + + status = p2p_get_frame_info(tx_ctx->buf, tx_ctx->buf_len, + &(tx_ctx->frame_info)); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("unsupport frame"); + status = QDF_STATUS_E_INVAL; + goto fail; + } + + /* update P2P connection status with tx frame info */ + mac_to = &(tx_ctx->buf[DST_MAC_ADDR_OFFSET]); + p2p_tx_update_connection_status(p2p_soc_obj, + &(tx_ctx->frame_info), mac_to); + + status = p2p_vdev_check_valid(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_debug("invalid vdev or vdev mode"); + status = QDF_STATUS_E_INVAL; + goto fail; + } + + /* Do not wait for ack for probe response */ + if (tx_ctx->frame_info.sub_type == P2P_MGMT_PROBE_RSP && + !(tx_ctx->no_ack)) { + p2p_debug("Force set no ack to 1"); + tx_ctx->no_ack = 1; + } + + if (!tx_ctx->off_chan || !tx_ctx->chan) { + if (!tx_ctx->chan) + p2p_check_and_update_channel(tx_ctx); + status = p2p_execute_tx_action_frame(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("execute tx fail"); + goto fail; + } else + return QDF_STATUS_SUCCESS; + } + + /* For off channel tx case */ + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (curr_roc_ctx && (curr_roc_ctx->chan == tx_ctx->chan)) { + if ((curr_roc_ctx->roc_state == ROC_STATE_REQUESTED) || + (curr_roc_ctx->roc_state == ROC_STATE_STARTED)) { + tx_ctx->roc_cookie = (uintptr_t)curr_roc_ctx; + status = qdf_list_insert_back( + &p2p_soc_obj->tx_q_roc, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to insert off chan tx context to wait roc req queue"); + goto fail; + } else + return QDF_STATUS_SUCCESS; + } else if (curr_roc_ctx->roc_state == ROC_STATE_ON_CHAN) { + p2p_adjust_tx_wait(tx_ctx); + status = p2p_restart_roc_timer(curr_roc_ctx); + curr_roc_ctx->tx_ctx = tx_ctx; + if (status != QDF_STATUS_SUCCESS) { + p2p_err("restart roc timer fail"); + goto fail; + } + status = p2p_execute_tx_action_frame(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("execute tx fail"); + goto fail; + } else + return QDF_STATUS_SUCCESS; + } + } + + curr_roc_ctx = p2p_find_roc_by_chan(p2p_soc_obj, tx_ctx->chan); + if (curr_roc_ctx && (curr_roc_ctx->roc_state == ROC_STATE_IDLE)) { + tx_ctx->roc_cookie = (uintptr_t)curr_roc_ctx; + status = qdf_list_insert_back( + &p2p_soc_obj->tx_q_roc, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to insert off chan tx context to wait roc req queue"); + goto fail; + } else { + return QDF_STATUS_SUCCESS; + } + } + + if (!tx_ctx->duration) { + tx_ctx->duration = P2P_ACTION_FRAME_DEFAULT_WAIT; + p2p_debug("use default wait %d", + P2P_ACTION_FRAME_DEFAULT_WAIT); + } + status = p2p_roc_req_for_tx_action(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to request roc before off chan tx"); + goto fail; + } + + return QDF_STATUS_SUCCESS; + +fail: + p2p_send_tx_conf(tx_ctx, false); + qdf_idr_remove(&p2p_soc_obj->p2p_idr, tx_ctx->id); + qdf_mem_free(tx_ctx->buf); + qdf_mem_free(tx_ctx); + + return status; +} + +QDF_STATUS p2p_process_mgmt_tx_cancel( + struct cancel_roc_context *cancel_tx) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct tx_action_context *cur_tx_ctx; + struct p2p_roc_context *cur_roc_ctx; + struct cancel_roc_context cancel_roc; + + if (!cancel_tx || !(cancel_tx->cookie)) { + p2p_info("invalid cancel info"); + return QDF_STATUS_SUCCESS; + } + + cur_tx_ctx = p2p_find_tx_ctx(cancel_tx->p2p_soc_obj, + cancel_tx->cookie, &is_roc_q, &is_ack_q); + if (cur_tx_ctx) { + if (is_roc_q) { + cancel_roc.p2p_soc_obj = + cancel_tx->p2p_soc_obj; + cancel_roc.cookie = + cur_tx_ctx->roc_cookie; + p2p_remove_tx_context(cur_tx_ctx); + return p2p_process_cancel_roc_req(&cancel_roc); + } + if (is_ack_q) { + /*Has tx action frame, waiting for ack*/ + p2p_debug("Waiting for ack, cookie %llx", + cancel_tx->cookie); + } + } else { + p2p_debug("Failed to find tx ctx by cookie, cookie %llx", + cancel_tx->cookie); + + cur_roc_ctx = p2p_find_roc_by_tx_ctx(cancel_tx->p2p_soc_obj, + cancel_tx->cookie); + if (cur_roc_ctx) { + p2p_debug("tx ctx:%llx, roc:%pK", + cancel_tx->cookie, cur_roc_ctx); + cancel_roc.p2p_soc_obj = + cancel_tx->p2p_soc_obj; + cancel_roc.cookie = (uintptr_t) cur_roc_ctx; + return p2p_process_cancel_roc_req(&cancel_roc); + } + + p2p_debug("Failed to find roc by tx ctx"); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_mgmt_tx_ack_cnf( + struct p2p_tx_conf_event *tx_cnf_event) +{ + struct p2p_tx_cnf tx_cnf; + struct tx_action_context *tx_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + p2p_soc_obj = tx_cnf_event->p2p_soc_obj; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + qdf_nbuf_free(tx_cnf_event->nbuf); + p2p_err("Invalid p2p soc object or start parameters"); + return QDF_STATUS_E_INVAL; + } + + tx_ctx = p2p_find_tx_ctx_by_nbuf(p2p_soc_obj, tx_cnf_event->nbuf); + qdf_nbuf_free(tx_cnf_event->nbuf); + if (!tx_ctx) { + p2p_err("can't find tx_ctx, tx ack comes late"); + return QDF_STATUS_SUCCESS; + } + + /* disable tx timer */ + p2p_disable_tx_timer(tx_ctx); + tx_cnf.vdev_id = tx_ctx->vdev_id; + tx_cnf.action_cookie = (uint64_t)tx_ctx->id; + tx_cnf.buf = tx_ctx->buf; + tx_cnf.buf_len = tx_ctx->buf_len; + tx_cnf.status = tx_cnf_event->status; + + p2p_debug("soc:%pK, vdev_id:%d, action_cookie:%llx, len:%d, status:%d, buf:%pK", + p2p_soc_obj->soc, tx_cnf.vdev_id, + tx_cnf.action_cookie, tx_cnf.buf_len, + tx_cnf.status, tx_cnf.buf); + + p2p_rand_mac_tx_done(p2p_soc_obj->soc, tx_ctx); + + start_param = p2p_soc_obj->start_param; + if (start_param->tx_cnf_cb) + start_param->tx_cnf_cb(start_param->tx_cnf_cb_data, + &tx_cnf); + else + p2p_debug("Got tx conf, but no valid up layer callback"); + + p2p_remove_tx_context(tx_ctx); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_rx_mgmt( + struct p2p_rx_mgmt_event *rx_mgmt_event) +{ + struct p2p_rx_mgmt_frame *rx_mgmt; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + struct p2p_frame_info frame_info; + uint8_t *mac_from; + + p2p_soc_obj = rx_mgmt_event->p2p_soc_obj; + rx_mgmt = rx_mgmt_event->rx_mgmt; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid psoc object or start parameters"); + qdf_mem_free(rx_mgmt); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("soc:%pK, frame_len:%d, rx_freq:%d, vdev_id:%d, frm_type:%d, rx_rssi:%d, buf:%pK", + p2p_soc_obj->soc, rx_mgmt->frame_len, + rx_mgmt->rx_freq, rx_mgmt->vdev_id, rx_mgmt->frm_type, + rx_mgmt->rx_rssi, rx_mgmt->buf); + + if (rx_mgmt->frm_type == MGMT_ACTION_VENDOR_SPECIFIC) { + p2p_get_frame_info(rx_mgmt->buf, rx_mgmt->frame_len, + &frame_info); + + /* update P2P connection status with rx frame info */ + mac_from = &(rx_mgmt->buf[SRC_MAC_ADDR_OFFSET]); + p2p_rx_update_connection_status(p2p_soc_obj, + &frame_info, mac_from); + + p2p_debug("action_sub_type %u, action_type %d", + frame_info.public_action_type, + frame_info.action_type); + + if ((frame_info.public_action_type == + P2P_PUBLIC_ACTION_NOT_SUPPORT) && + (frame_info.action_type == + P2P_ACTION_NOT_SUPPORT)) { + p2p_debug("non-p2p frame, drop it"); + qdf_mem_free(rx_mgmt); + return QDF_STATUS_SUCCESS; + } else { + p2p_debug("p2p frame, extend roc accordingly"); + p2p_extend_roc_timer(p2p_soc_obj, &frame_info); + } + } + + if (rx_mgmt->frm_type == MGMT_ACTION_CATEGORY_VENDOR_SPECIFIC) + p2p_get_frame_info(rx_mgmt->buf, rx_mgmt->frame_len, + &frame_info); + + start_param = p2p_soc_obj->start_param; + if (start_param->rx_cb) + start_param->rx_cb(start_param->rx_cb_data, rx_mgmt); + else + p2p_debug("rx mgmt, but no valid up layer callback"); + + qdf_mem_free(rx_mgmt); + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.h b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.h new file mode 100644 index 0000000000000000000000000000000000000000..08f0606eddf935ca8e779d311bc5669dedc995b2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.h @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Defines off channel tx API & structures + */ + +#ifndef _WLAN_P2P_OFF_CHAN_TX_H_ +#define _WLAN_P2P_OFF_CHAN_TX_H_ + +#include +#include +#include + +#define P2P_EID_VENDOR 0xdd +#define P2P_ACTION_VENDOR_SPECIFIC_CATEGORY 0x7F +#define P2P_PUBLIC_ACTION_FRAME 0x4 +#define P2P_MAC_MGMT_ACTION 0xD +#define P2P_PUBLIC_ACTION_VENDOR_SPECIFIC 0x9 +#define P2P_NOA_ATTR 0xC + +#define P2P_MAX_NOA_ATTR_LEN 31 +#define P2P_IE_HEADER_LEN 6 +#define P2P_ACTION_OFFSET 24 +#define P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET 30 +#define P2P_ACTION_FRAME_TYPE_OFFSET 29 +#define PROBE_RSP_IE_OFFSET 36 + +#define P2P_TX_PKT_MIN_HEADROOM (64) + +#define P2P_OUI "\x50\x6f\x9a\x09" +#define P2P_OUI_SIZE 4 + +#define P2P_ACTION_FRAME_RSP_WAIT 500 +#define P2P_ACTION_FRAME_ACK_WAIT 300 +#define P2P_ACTION_FRAME_TX_TIMEOUT 2000 + +#define DST_MAC_ADDR_OFFSET 4 +#define SRC_MAC_ADDR_OFFSET (DST_MAC_ADDR_OFFSET + QDF_MAC_ADDR_SIZE) + +#define P2P_NOA_STREAM_ARR_SIZE (P2P_MAX_NOA_ATTR_LEN + (2 * P2P_IE_HEADER_LEN)) + +#define P2P_GET_TYPE_FRM_FC(__fc__) (((__fc__) & 0x0F) >> 2) +#define P2P_GET_SUBTYPE_FRM_FC(__fc__) (((__fc__) & 0xF0) >> 4) + +#define WLAN_WAIT_TIME_SET_RND 100 + +struct p2p_soc_priv_obj; +struct cancel_roc_context; +struct p2p_tx_conf_event; +struct p2p_rx_mgmt_event; + +/** + * enum p2p_frame_type - frame type + * @P2P_FRAME_MGMT: mgmt frame + * @P2P_FRAME_NOT_SUPPORT: not support frame type + */ +enum p2p_frame_type { + P2P_FRAME_MGMT = 0, + P2P_FRAME_NOT_SUPPORT, +}; + +/** + * enum p2p_frame_sub_type - frame sub type + * @P2P_MGMT_PROBE_REQ: probe request frame + * @P2P_MGMT_PROBE_RSP: probe response frame + * @P2P_MGMT_DISASSOC: disassociation frame + * @P2P_MGMT_AUTH: authentication frame + * @P2P_MGMT_DEAUTH: deauthentication frame + * @P2P_MGMT_ACTION: action frame + * @P2P_MGMT_NOT_SUPPORT: not support sub frame type + */ +enum p2p_frame_sub_type { + P2P_MGMT_PROBE_REQ = 4, + P2P_MGMT_PROBE_RSP, + P2P_MGMT_DISASSOC = 10, + P2P_MGMT_AUTH, + P2P_MGMT_DEAUTH, + P2P_MGMT_ACTION, + P2P_MGMT_NOT_SUPPORT, +}; + +/** + * enum p2p_public_action_type - public action frame type + * @P2P_PUBLIC_ACTION_NEG_REQ: go negotiation request frame + * @P2P_PUBLIC_ACTION_NEG_RSP: go negotiation response frame + * @P2P_PUBLIC_ACTION_NEG_CNF: go negotiation confirm frame + * @P2P_PUBLIC_ACTION_INVIT_REQ: p2p invitation request frame + * @P2P_PUBLIC_ACTION_INVIT_RSP: p2p invitation response frame + * @P2P_PUBLIC_ACTION_DEV_DIS_REQ: device discoverability request + * @P2P_PUBLIC_ACTION_DEV_DIS_RSP: device discoverability response + * @P2P_PUBLIC_ACTION_PROV_DIS_REQ: provision discovery request + * @P2P_PUBLIC_ACTION_PROV_DIS_RSP: provision discovery response + * @P2P_PUBLIC_ACTION_GAS_INIT_REQ: gas initial request, + * @P2P_PUBLIC_ACTION_GAS_INIT_RSP: gas initial response + * @P2P_PUBLIC_ACTION_GAS_COMB_REQ: gas comeback request + * @P2P_PUBLIC_ACTION_GAS_COMB_RSP: gas comeback response + * @P2P_PUBLIC_ACTION_NOT_SUPPORT: not support p2p public action frame + */ +enum p2p_public_action_type { + P2P_PUBLIC_ACTION_NEG_REQ = 0, + P2P_PUBLIC_ACTION_NEG_RSP, + P2P_PUBLIC_ACTION_NEG_CNF, + P2P_PUBLIC_ACTION_INVIT_REQ, + P2P_PUBLIC_ACTION_INVIT_RSP, + P2P_PUBLIC_ACTION_DEV_DIS_REQ, + P2P_PUBLIC_ACTION_DEV_DIS_RSP, + P2P_PUBLIC_ACTION_PROV_DIS_REQ, + P2P_PUBLIC_ACTION_PROV_DIS_RSP, + P2P_PUBLIC_ACTION_GAS_INIT_REQ = 10, + P2P_PUBLIC_ACTION_GAS_INIT_RSP, + P2P_PUBLIC_ACTION_GAS_COMB_REQ, + P2P_PUBLIC_ACTION_GAS_COMB_RSP, + P2P_PUBLIC_ACTION_NOT_SUPPORT, +}; + +/** + * enum p2p_action_type - p2p action frame type + * @P2P_ACTION_PRESENCE_REQ: presence request frame + * @P2P_ACTION_PRESENCE_RSP: presence response frame + * @P2P_ACTION_NOT_SUPPORT: not support action frame type + */ +enum p2p_action_type { + P2P_ACTION_PRESENCE_REQ = 1, + P2P_ACTION_PRESENCE_RSP = 2, + P2P_ACTION_NOT_SUPPORT, +}; + +struct p2p_frame_info { + enum p2p_frame_type type; + enum p2p_frame_sub_type sub_type; + enum p2p_public_action_type public_action_type; + enum p2p_action_type action_type; +}; + +/** + * struct tx_action_context - tx action frame context + * @node: Node for next element in the list + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @vdev_id: Vdev id on which this request has come + * @scan_id: Scan id given by scan component for this roc req + * @roc_cookie: Cookie for remain on channel request + * @id: Identifier of this tx context + * @chan: Chan for which this tx has been requested + * @buf: tx buffer + * @buf_len: Length of tx buffer + * @off_chan: Is this off channel tx + * @no_cck: Required cck or not + * @no_ack: Required ack or not + * @duration: Duration for the RoC + * @tx_timer: RoC timer + * @frame_info: Frame type information + */ +struct tx_action_context { + qdf_list_node_t node; + struct p2p_soc_priv_obj *p2p_soc_obj; + int vdev_id; + int scan_id; + uint64_t roc_cookie; + int32_t id; + uint8_t chan; + uint8_t *buf; + int buf_len; + bool off_chan; + bool no_cck; + bool no_ack; + bool rand_mac_tx; + uint32_t duration; + qdf_mc_timer_t tx_timer; + struct p2p_frame_info frame_info; + qdf_nbuf_t nbuf; +}; + +/** + * p2p_rand_mac_tx_done() - process random mac mgmt tx done + * @soc: soc + * @tx_ctx: tx context + * + * This function will remove the random mac addr filter reference. + * + * Return: void + */ +void +p2p_rand_mac_tx_done(struct wlan_objmgr_psoc *soc, + struct tx_action_context *tx_ctx); + +/** + * p2p_clear_mac_filter() - send clear mac addr filter cmd + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * + * This function send clear random mac addr filter command to p2p component + * msg core + * + * Return: QDF_STATUS_SUCCESS - if sent successfully. + * otherwise: failed. + */ +QDF_STATUS +p2p_clear_mac_filter(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq); + +/** + * p2p_is_vdev_support_rand_mac() - check vdev type support random mac mgmt + * tx or not + * @vdev: vdev object + * + * Return: true: support random mac mgmt tx + * false: not support random mac mgmt tx. + */ +bool +p2p_is_vdev_support_rand_mac(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_dump_tx_queue() - dump tx queue + * @p2p_soc_obj: p2p soc private object + * + * This function dumps tx queue and output details about tx context in + * queue. + * + * Return: None + */ +void p2p_dump_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj); + +/** + * p2p_ready_to_tx_frame() - dump tx queue + * @p2p_soc_obj: p2p soc private object + * @cookie: cookie is pointer to roc + * + * This function find out the tx context in wait for roc queue and tx + * this frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_ready_to_tx_frame(struct p2p_soc_priv_obj *p2p_soc_obj, + uint64_t cookie); + +/** + * p2p_cleanup_tx_sync() - Cleanup tx queue + * @p2p_soc_obj: p2p psoc private object + * @vdev: vdev object + * + * This function cleanup tx context in queue until cancellation done. + * To avoid deadlock, don't call from scheduler thread. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_cleanup_tx_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev); + +/** + * p2p_process_cleanup_tx_queue() - process the message to cleanup tx + * @param: pointer to cleanup parameters + * + * This function cleanup wait for roc queue and wait for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cleanup_tx_queue( + struct p2p_cleanup_param *param); + +/** + * p2p_process_mgmt_tx() - Process mgmt frame tx request + * @tx_ctx: tx context + * + * This function handles mgmt frame tx request. It will call API from + * mgmt txrx component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx); + +/** + * p2p_process_mgmt_tx_cancel() - Process cancel mgmt frame tx request + * @cancel_tx: cancel tx context + * + * This function cancel mgmt frame tx request by cookie. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_mgmt_tx_cancel( + struct cancel_roc_context *cancel_tx); + +/** + * p2p_process_mgmt_tx_ack_cnf() - Process tx ack event + * @tx_cnf_event: tx confirmation event information + * + * This function mgmt frame tx confirmation. It will deliver this + * event to up layer + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_mgmt_tx_ack_cnf( + struct p2p_tx_conf_event *tx_cnf_event); + +/** + * p2p_process_rx_mgmt() - Process rx mgmt frame event + * @rx_mgmt_event: rx mgmt frame event information + * + * This function mgmt frame rx mgmt frame event. It will deliver this + * event to up layer + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_rx_mgmt( + struct p2p_rx_mgmt_event *rx_mgmt_event); + +/** + * p2p_find_tx_ctx_by_nbuf() - find tx context by nbuf + * @p2p_soc_obj: p2p soc object + * @nbuf: pointer to nbuf + * + * This function finds out tx context by nbuf. + * + * Return: pointer to tx context + */ +struct tx_action_context *p2p_find_tx_ctx_by_nbuf( + struct p2p_soc_priv_obj *p2p_soc_obj, void *nbuf); + +#define P2P_80211_FRM_SA_OFFSET 10 + +/** + * p2p_del_random_mac() - del mac fitler from given vdev rand mac list + * @soc: soc object + * @vdev_id: vdev id + * @rnd_cookie: random mac mgmt tx cookie + * + * This function will del the mac addr filter from vdev random mac addr list. + * If there is no reference to mac addr, it will set a clear timer to flush it + * in target finally. + * + * Return: QDF_STATUS_SUCCESS - del successfully. + * other : failed to del the mac address entry. + */ +QDF_STATUS +p2p_del_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint64_t rnd_cookie); + +/** + * p2p_random_mac_handle_tx_done() - del mac filter from given vdev rand mac + * list when mgmt tx done + * @soc: soc object + * @vdev_id: vdev id + * @rnd_cookie: random mac mgmt tx cookie + * @duration: timeout value to flush the addr in target. + * + * This function will del the mac addr filter from vdev random mac addr list + * and also remove the filter from firmware if duration is zero else start + * the timer for that duration. + * + * Return: QDF_STATUS_SUCCESS - del successfully. + * other : failed to del the mac address entry. + */ +QDF_STATUS +p2p_random_mac_handle_tx_done(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint64_t rnd_cookie, uint32_t duration); + +/** + * p2p_check_random_mac() - check random mac addr or not + * @soc: soc context + * @vdev_id: vdev id + * @random_mac_addr: mac addr to be checked + * + * This function check the input addr is random mac addr or not for vdev. + * + * Return: true if addr is random mac address else false. + */ +bool p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr); + +/** + * p2p_process_set_rand_mac() - process the set random mac command + * @set_filter_req: request data + * + * This function will process the set mac addr filter command. + * + * Return: QDF_STATUS_SUCCESS: if process successfully + * other: failed. + */ +QDF_STATUS p2p_process_set_rand_mac( + struct p2p_set_mac_filter_req *set_filter_req); + +/** + * p2p_process_set_rand_mac_rsp() - process the set random mac response + * @resp: response date + * + * This function will process the set mac addr filter event. + * + * Return: QDF_STATUS_SUCCESS: if process successfully + * other: failed. + */ +QDF_STATUS p2p_process_set_rand_mac_rsp(struct p2p_mac_filter_rsp *resp); + +/** + * p2p_del_all_rand_mac_vdev() - del all random mac filter in vdev + * @vdev: vdev object + * + * This function will del all random mac filter in vdev + * + * Return: void + */ +void p2p_del_all_rand_mac_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_del_all_rand_mac_soc() - del all random mac filter in soc + * @soc: soc object + * + * This function will del all random mac filter in all vdev of soc + * + * Return: void + */ +void p2p_del_all_rand_mac_soc(struct wlan_objmgr_psoc *soc); + +/** + * p2p_rand_mac_tx() - handle random mac mgmt tx + * @tx_action: tx action context + * + * This function will check whether need to set random mac tx filter for a + * given mgmt tx request and do the mac addr filter process as needed. + * + * Return: void + */ +void p2p_rand_mac_tx(struct tx_action_context *tx_action); + +/** + * p2p_init_random_mac_vdev() - Init random mac data for vdev + * @p2p_vdev_obj: p2p vdev private object + * + * This function will init the per vdev random mac data structure. + * + * Return: void + */ +void p2p_init_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj); + +/** + * p2p_deinit_random_mac_vdev() - Init random mac data for vdev + * @p2p_vdev_obj: p2p vdev private object + * + * This function will deinit the per vdev random mac data structure. + * + * Return: void + */ +void p2p_deinit_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj); + +/** + * p2p_get_p2pie_ptr() - get the pointer to p2p ie + * @ie: source ie + * @ie_len: source ie length + * + * This function finds out p2p ie by p2p oui and return the pointer. + * + * Return: pointer to p2p ie + */ +const uint8_t *p2p_get_p2pie_ptr(const uint8_t *ie, uint16_t ie_len); + +#endif /* _WLAN_P2P_OFF_CHAN_TX_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.c b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.c new file mode 100644 index 0000000000000000000000000000000000000000..c7668842e5e93839cd9c3cab53b3556e29b43431 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.c @@ -0,0 +1,962 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains RoC API definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_p2p_roc.h" +#include "wlan_p2p_main.h" +#include "wlan_p2p_off_chan_tx.h" + +/** + * p2p_mgmt_rx_ops() - register or unregister rx callback + * @psoc: psoc object + * @isregister: register if true, unregister if false + * + * This function registers or unregisters rx callback to mgmt txrx + * component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_mgmt_rx_ops(struct wlan_objmgr_psoc *psoc, + bool isregister) +{ + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info; + QDF_STATUS status; + + p2p_debug("psoc:%pK, is register rx:%d", psoc, isregister); + + frm_cb_info.frm_type = MGMT_PROBE_REQ; + frm_cb_info.mgmt_rx_cb = tgt_p2p_mgmt_frame_rx_cb; + + if (isregister) + status = wlan_mgmt_txrx_register_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, &frm_cb_info, 1); + else + status = wlan_mgmt_txrx_deregister_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, &frm_cb_info, 1); + + return status; +} + +/** + * p2p_scan_start() - Start scan + * @roc_ctx: remain on channel request + * + * This function trigger a start scan request to scan component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_scan_start(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct scan_start_request *req; + struct wlan_objmgr_vdev *vdev; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + uint32_t go_num; + uint8_t ndp_num = 0, nan_disc_enabled_num = 0; + bool is_dbs; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + p2p_soc_obj->soc, roc_ctx->vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + p2p_err("failed to alloc scan start request"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + + ucfg_scan_init_default_params(vdev, req); + + if (!roc_ctx->duration) { + roc_ctx->duration = P2P_ROC_DEFAULT_DURATION; + p2p_debug("use default duration %d", + P2P_ROC_DEFAULT_DURATION); + } + + roc_ctx->scan_id = ucfg_scan_get_scan_id(p2p_soc_obj->soc); + req->vdev = vdev; + req->scan_req.scan_id = roc_ctx->scan_id; + req->scan_req.scan_type = SCAN_TYPE_P2P_LISTEN; + req->scan_req.scan_req_id = p2p_soc_obj->scan_req_id; + req->scan_req.chan_list.num_chan = 1; + req->scan_req.chan_list.chan[0].freq = wlan_chan_to_freq(roc_ctx->chan); + req->scan_req.dwell_time_passive = roc_ctx->duration; + req->scan_req.dwell_time_active = 0; + req->scan_req.scan_priority = SCAN_PRIORITY_HIGH; + req->scan_req.num_bssid = 1; + qdf_set_macaddr_broadcast(&req->scan_req.bssid_list[0]); + + if (req->scan_req.dwell_time_passive < P2P_MAX_ROC_DURATION) { + go_num = policy_mgr_mode_specific_connection_count( + p2p_soc_obj->soc, PM_P2P_GO_MODE, NULL); + policy_mgr_mode_specific_num_active_sessions(p2p_soc_obj->soc, + QDF_NDI_MODE, + &ndp_num); + policy_mgr_mode_specific_num_active_sessions(p2p_soc_obj->soc, + QDF_NAN_DISC_MODE, + &nan_disc_enabled_num); + p2p_debug("present go number:%d, NDP number:%d, NAN number:%d", + go_num, ndp_num, nan_disc_enabled_num); + + is_dbs = policy_mgr_is_hw_dbs_capable(p2p_soc_obj->soc); + + if (go_num) + req->scan_req.dwell_time_passive *= + P2P_ROC_DURATION_MULTI_GO_PRESENT; + else + req->scan_req.dwell_time_passive *= + P2P_ROC_DURATION_MULTI_GO_ABSENT; + /* this is to protect too huge value if some customers + * give a higher value from supplicant + */ + + if (go_num && req->scan_req.dwell_time_passive > + P2P_MAX_ROC_DURATION_GO_PRESENT) { + req->scan_req.dwell_time_passive = + P2P_MAX_ROC_DURATION_GO_PRESENT; + } else if (ndp_num) { + if (is_dbs && req->scan_req.dwell_time_passive > + P2P_MAX_ROC_DURATION_DBS_NDP_PRESENT) + req->scan_req.dwell_time_passive = + P2P_MAX_ROC_DURATION_DBS_NDP_PRESENT; + else if (!is_dbs && req->scan_req.dwell_time_passive > + P2P_MAX_ROC_DURATION_NON_DBS_NDP_PRESENT) + req->scan_req.dwell_time_passive = + P2P_MAX_ROC_DURATION_NON_DBS_NDP_PRESENT; + } else if (nan_disc_enabled_num) { + if (is_dbs && req->scan_req.dwell_time_passive > + P2P_MAX_ROC_DURATION_DBS_NAN_PRESENT) + req->scan_req.dwell_time_passive = + P2P_MAX_ROC_DURATION_DBS_NAN_PRESENT; + else if (!is_dbs && req->scan_req.dwell_time_passive > + P2P_MAX_ROC_DURATION_NON_DBS_NAN_PRESENT) + req->scan_req.dwell_time_passive = + P2P_MAX_ROC_DURATION_NON_DBS_NAN_PRESENT; + } else if (req->scan_req.dwell_time_passive > + P2P_MAX_ROC_DURATION) { + req->scan_req.dwell_time_passive = P2P_MAX_ROC_DURATION; + } + } + p2p_debug("FW requested roc duration is:%d", + req->scan_req.dwell_time_passive); + + status = ucfg_scan_start(req); + + p2p_debug("start scan, scan req id:%d, scan id:%d, status:%d", + p2p_soc_obj->scan_req_id, roc_ctx->scan_id, status); +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_scan_abort() - Abort scan + * @roc_ctx: remain on channel request + * + * This function trigger an abort scan request to scan component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_scan_abort(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct scan_cancel_request *req; + struct wlan_objmgr_vdev *vdev; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("abort scan, scan req id:%d, scan id:%d", + p2p_soc_obj->scan_req_id, roc_ctx->scan_id); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + p2p_soc_obj->soc, roc_ctx->vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + p2p_err("failed to alloc scan cancel request"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + + req->vdev = vdev; + req->cancel_req.requester = p2p_soc_obj->scan_req_id; + req->cancel_req.scan_id = roc_ctx->scan_id; + req->cancel_req.vdev_id = roc_ctx->vdev_id; + req->cancel_req.req_type = WLAN_SCAN_CANCEL_SINGLE; + + qdf_mtrace(QDF_MODULE_ID_P2P, QDF_MODULE_ID_SCAN, + req->cancel_req.req_type, + req->vdev->vdev_objmgr.vdev_id, req->cancel_req.scan_id); + status = ucfg_scan_cancel(req); + + p2p_debug("abort scan, scan req id:%d, scan id:%d, status:%d", + p2p_soc_obj->scan_req_id, roc_ctx->scan_id, status); +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_send_roc_event() - Send roc event + * @roc_ctx: remain on channel request + * @evt: roc event information + * + * This function send out roc event to up layer. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_send_roc_event( + struct p2p_roc_context *roc_ctx, enum p2p_roc_event evt) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_event p2p_evt; + struct p2p_start_param *start_param; + + p2p_soc_obj = roc_ctx->p2p_soc_obj; + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid p2p soc object or start parameters"); + return QDF_STATUS_E_INVAL; + } + start_param = p2p_soc_obj->start_param; + if (!(start_param->event_cb)) { + p2p_err("Invalid p2p event callback to up layer"); + return QDF_STATUS_E_INVAL; + } + + p2p_evt.vdev_id = roc_ctx->vdev_id; + p2p_evt.roc_event = evt; + p2p_evt.cookie = (uint64_t)roc_ctx->id; + p2p_evt.chan = roc_ctx->chan; + p2p_evt.duration = roc_ctx->duration; + + p2p_debug("roc_event: %d, cookie:%llx", p2p_evt.roc_event, + p2p_evt.cookie); + + start_param->event_cb(start_param->event_cb_data, &p2p_evt); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_destroy_roc_ctx() - destroy roc ctx + * @roc_ctx: remain on channel request + * @up_layer_event: if send uplayer event + * @in_roc_queue: if roc context in roc queue + * + * This function destroy roc context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_destroy_roc_ctx(struct p2p_roc_context *roc_ctx, + bool up_layer_event, bool in_roc_queue) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p_soc_obj:%pK, roc_ctx:%pK, up_layer_event:%d, in_roc_queue:%d vdev_id:%d chan:%d duration:%d", + p2p_soc_obj, roc_ctx, up_layer_event, in_roc_queue, + roc_ctx->vdev_id, roc_ctx->chan, roc_ctx->duration); + + if (up_layer_event) { + if (roc_ctx->roc_state < ROC_STATE_ON_CHAN) + p2p_send_roc_event(roc_ctx, ROC_EVENT_READY_ON_CHAN); + p2p_send_roc_event(roc_ctx, ROC_EVENT_COMPLETED); + } + + if (in_roc_queue) { + status = qdf_list_remove_node(&p2p_soc_obj->roc_q, + (qdf_list_node_t *)roc_ctx); + if (QDF_STATUS_SUCCESS != status) + p2p_err("Failed to remove roc req, status %d", status); + } + + qdf_idr_remove(&p2p_soc_obj->p2p_idr, roc_ctx->id); + qdf_mem_free(roc_ctx); + + return status; +} + +/** + * p2p_execute_cancel_roc_req() - Execute cancel roc request + * @roc_ctx: remain on channel request + * + * This function stop roc timer, abort scan and unregister mgmt rx + * callbak. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_execute_cancel_roc_req( + struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p execute cancel roc req"); + + roc_ctx->roc_state = ROC_STATE_CANCEL_IN_PROG; + + status = qdf_mc_timer_stop_sync(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop roc timer, roc %pK", roc_ctx); + + status = p2p_scan_abort(roc_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to abort scan, status:%d, destroy roc %pK", + status, roc_ctx); + qdf_mc_timer_destroy(&roc_ctx->roc_timer); + p2p_mgmt_rx_ops(p2p_soc_obj->soc, false); + p2p_destroy_roc_ctx(roc_ctx, true, true); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_roc_timeout() - Callback for roc timeout + * @pdata: pointer to p2p soc private object + * + * This function is callback for roc time out. + * + * Return: None + */ +static void p2p_roc_timeout(void *pdata) +{ + struct p2p_roc_context *roc_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK", pdata); + + p2p_soc_obj = pdata; + if (!p2p_soc_obj) { + p2p_err("Invalid p2p soc object"); + return; + } + + roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (!roc_ctx) { + p2p_debug("No P2P roc is pending"); + return; + } + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + if (roc_ctx->roc_state == ROC_STATE_CANCEL_IN_PROG) { + p2p_err("Cancellation already in progress"); + return; + } + p2p_execute_cancel_roc_req(roc_ctx); +} + +/** + * p2p_execute_roc_req() - Execute roc request + * @roc_ctx: remain on channel request + * + * This function init roc timer, start scan and register mgmt rx + * callbak. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_execute_roc_req(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + /* prevent runtime suspend */ + qdf_runtime_pm_prevent_suspend(&p2p_soc_obj->roc_runtime_lock); + + status = qdf_mc_timer_init(&roc_ctx->roc_timer, + QDF_TIMER_TYPE_SW, p2p_roc_timeout, + p2p_soc_obj); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to init roc timer, status:%d", status); + goto fail; + } + + roc_ctx->roc_state = ROC_STATE_REQUESTED; + status = p2p_scan_start(roc_ctx); + if (status != QDF_STATUS_SUCCESS) { + qdf_mc_timer_destroy(&roc_ctx->roc_timer); + p2p_err("Failed to start scan, status:%d", status); + goto fail; + } + +fail: + if (status != QDF_STATUS_SUCCESS) { + p2p_destroy_roc_ctx(roc_ctx, true, true); + qdf_runtime_pm_allow_suspend( + &p2p_soc_obj->roc_runtime_lock); + return status; + } + + p2p_soc_obj->cur_roc_vdev_id = roc_ctx->vdev_id; + status = p2p_mgmt_rx_ops(p2p_soc_obj->soc, true); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to register mgmt rx callback, status:%d", + status); + + return status; +} + +/** + * p2p_find_roc_ctx() - Find out roc context by cookie + * @p2p_soc_obj: p2p psoc private object + * @cookie: cookie is the key to find out roc context + * + * This function find out roc context by cookie from p2p psoc private + * object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +static struct p2p_roc_context *p2p_find_roc_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie) +{ + struct p2p_roc_context *curr_roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("p2p soc obj:%pK, cookie:%llx", p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + if ((uintptr_t) curr_roc_ctx == cookie) + return curr_roc_ctx; + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +/** + * p2p_process_scan_start_evt() - Process scan start event + * @roc_ctx: remain on channel request + * + * This function process scan start event. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_process_scan_start_evt( + struct p2p_roc_context *roc_ctx) +{ + roc_ctx->roc_state = ROC_STATE_STARTED; + p2p_debug("scan started, scan id:%d", roc_ctx->scan_id); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_process_ready_on_channel_evt() - Process ready on channel event + * @roc_ctx: remain on channel request + * + * This function process ready on channel event. Starts roc timer. + * Indicates this event to up layer if this is user request roc. Sends + * mgmt frame if this is off channel rx roc. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_process_ready_on_channel_evt( + struct p2p_roc_context *roc_ctx) +{ + uint64_t cookie; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + p2p_soc_obj = roc_ctx->p2p_soc_obj; + roc_ctx->roc_state = ROC_STATE_ON_CHAN; + + p2p_debug("scan_id:%d, roc_state:%d", roc_ctx->scan_id, + roc_ctx->roc_state); + + status = qdf_mc_timer_start(&roc_ctx->roc_timer, + (roc_ctx->duration + P2P_EVENT_PROPAGATE_TIME)); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Remain on Channel timer start failed"); + if (roc_ctx->roc_type == USER_REQUESTED) { + p2p_debug("user required roc, send roc event"); + status = p2p_send_roc_event(roc_ctx, + ROC_EVENT_READY_ON_CHAN); + } + + cookie = (uintptr_t)roc_ctx; + /* ready to tx frame */ + p2p_ready_to_tx_frame(p2p_soc_obj, cookie); + + return status; +} + +/** + * p2p_process_scan_complete_evt() - Process scan complete event + * @roc_ctx: remain on channel request + * + * This function process scan complete event. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_process_scan_complete_evt( + struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + qdf_list_node_t *next_node; + uint32_t size; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("vdev_id:%d scan_id:%d", roc_ctx->vdev_id, roc_ctx->scan_id); + + /* allow runtime suspend */ + qdf_runtime_pm_allow_suspend(&p2p_soc_obj->roc_runtime_lock); + + + status = qdf_mc_timer_stop_sync(&roc_ctx->roc_timer); + if (QDF_IS_STATUS_ERROR(status)) + p2p_err("Failed to stop roc timer"); + + status = qdf_mc_timer_destroy(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to destroy roc timer"); + + status = p2p_mgmt_rx_ops(p2p_soc_obj->soc, false); + p2p_soc_obj->cur_roc_vdev_id = P2P_INVALID_VDEV_ID; + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to deregister mgmt rx callback"); + + if (roc_ctx->roc_type == USER_REQUESTED) + status = p2p_send_roc_event(roc_ctx, + ROC_EVENT_COMPLETED); + + p2p_destroy_roc_ctx(roc_ctx, false, true); + qdf_event_set(&p2p_soc_obj->cleanup_roc_done); + + size = qdf_list_size(&p2p_soc_obj->roc_q); + + if (size > 0) { + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, + &next_node); + if (QDF_STATUS_SUCCESS != status) { + p2p_err("Failed to peek roc req from front, status %d", + status); + return status; + } + roc_ctx = qdf_container_of(next_node, + struct p2p_roc_context, node); + status = p2p_execute_roc_req(roc_ctx); + } + return status; +} + +QDF_STATUS p2p_mgmt_rx_action_ops(struct wlan_objmgr_psoc *psoc, + bool isregister) +{ + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info[2]; + QDF_STATUS status; + + p2p_debug("psoc:%pK, is register rx:%d", psoc, isregister); + + frm_cb_info[0].frm_type = MGMT_ACTION_VENDOR_SPECIFIC; + frm_cb_info[0].mgmt_rx_cb = tgt_p2p_mgmt_frame_rx_cb; + frm_cb_info[1].frm_type = MGMT_ACTION_CATEGORY_VENDOR_SPECIFIC; + frm_cb_info[1].mgmt_rx_cb = tgt_p2p_mgmt_frame_rx_cb; + + if (isregister) + status = wlan_mgmt_txrx_register_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, frm_cb_info, 2); + else + status = wlan_mgmt_txrx_deregister_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, frm_cb_info, 2); + + return status; +} + +struct p2p_roc_context *p2p_find_current_roc_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj) +{ + struct p2p_roc_context *roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + if (roc_ctx->roc_state != ROC_STATE_IDLE) { + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id" + ":%d, scan_id:%d, tx ctx:%pK, chan:" + "%d, phy_mode:%d, duration:%d, " + "roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + return roc_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +struct p2p_roc_context *p2p_find_roc_by_tx_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie) +{ + struct p2p_roc_context *curr_roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("p2p soc obj:%pK, cookie:%llx", p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + if ((uintptr_t) curr_roc_ctx->tx_ctx == cookie) + return curr_roc_ctx; + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +struct p2p_roc_context *p2p_find_roc_by_chan( + struct p2p_soc_priv_obj *p2p_soc_obj, uint8_t chan) +{ + struct p2p_roc_context *roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, + node); + if (roc_ctx->chan == chan) { + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + return roc_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +QDF_STATUS p2p_restart_roc_timer(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&roc_ctx->roc_timer)) { + p2p_debug("roc timer is running"); + status = qdf_mc_timer_stop_sync(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to stop roc timer"); + return status; + } + + status = qdf_mc_timer_start(&roc_ctx->roc_timer, + roc_ctx->duration); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Remain on Channel timer start failed"); + } + + return status; +} + +QDF_STATUS p2p_cleanup_roc_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev) +{ + struct scheduler_msg msg = {0}; + struct p2p_cleanup_param *param; + QDF_STATUS status; + uint32_t vdev_id; + + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_debug("p2p_soc_obj:%pK, vdev:%pK", p2p_soc_obj, vdev); + param = qdf_mem_malloc(sizeof(*param)); + if (!param) { + p2p_err("failed to allocate cleanup param"); + return QDF_STATUS_E_NOMEM; + } + + param->p2p_soc_obj = p2p_soc_obj; + if (vdev) + vdev_id = (uint32_t)wlan_vdev_get_id(vdev); + else + vdev_id = P2P_INVALID_VDEV_ID; + param->vdev_id = vdev_id; + qdf_event_reset(&p2p_soc_obj->cleanup_roc_done); + msg.type = P2P_CLEANUP_ROC; + msg.bodyptr = param; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, &msg); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(param); + return status; + } + + status = qdf_wait_single_event( + &p2p_soc_obj->cleanup_roc_done, + P2P_WAIT_CLEANUP_ROC); + + if (status != QDF_STATUS_SUCCESS) + p2p_err("wait for cleanup roc timeout, %d", status); + + return status; +} + +QDF_STATUS p2p_process_cleanup_roc_queue( + struct p2p_cleanup_param *param) +{ + uint32_t vdev_id; + uint8_t count = 0; + QDF_STATUS status, ret; + struct p2p_roc_context *roc_ctx; + qdf_list_node_t *p_node; + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!param || !(param->p2p_soc_obj)) { + p2p_err("Invalid cleanup param"); + return QDF_STATUS_E_FAILURE; + } + + p2p_soc_obj = param->p2p_soc_obj; + vdev_id = param->vdev_id; + + p2p_debug("clean up idle roc request, roc queue size:%d, vdev id:%d", + qdf_list_size(&p2p_soc_obj->roc_q), vdev_id); + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + if ((roc_ctx->roc_state == ROC_STATE_IDLE) && + ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == roc_ctx->vdev_id))) { + ret = qdf_list_remove_node( + &p2p_soc_obj->roc_q, + (qdf_list_node_t *)roc_ctx); + if (ret == QDF_STATUS_SUCCESS) + qdf_mem_free(roc_ctx); + else + p2p_err("Failed to remove roc ctx from queue"); + } + } + + p2p_debug("clean up started roc request, roc queue size:%d", + qdf_list_size(&p2p_soc_obj->roc_q)); + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + if ((roc_ctx->roc_state != ROC_STATE_IDLE) && + ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == roc_ctx->vdev_id))) { + if (roc_ctx->roc_state != + ROC_STATE_CANCEL_IN_PROG) + p2p_execute_cancel_roc_req(roc_ctx); + + count++; + } + } + + p2p_debug("count %d", count); + if (!count) + qdf_event_set(&p2p_soc_obj->cleanup_roc_done); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_roc_req(struct p2p_roc_context *roc_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + QDF_STATUS status; + uint32_t size; + + p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx_ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + status = qdf_list_insert_back(&p2p_soc_obj->roc_q, + &roc_ctx->node); + if (QDF_STATUS_SUCCESS != status) { + p2p_destroy_roc_ctx(roc_ctx, true, false); + p2p_debug("Failed to insert roc req, status %d", status); + return status; + } + + size = qdf_list_size(&p2p_soc_obj->roc_q); + if (size == 1) { + status = p2p_execute_roc_req(roc_ctx); + } else if (size > 1) { + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + /*TODO, to handle extend roc */ + } + + return status; +} + +QDF_STATUS p2p_process_cancel_roc_req( + struct cancel_roc_context *cancel_roc_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + QDF_STATUS status; + + p2p_soc_obj = cancel_roc_ctx->p2p_soc_obj; + curr_roc_ctx = p2p_find_roc_ctx(p2p_soc_obj, + cancel_roc_ctx->cookie); + + if (!curr_roc_ctx) { + p2p_debug("Failed to find roc req by cookie, cookie %llx", + cancel_roc_ctx->cookie); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("roc ctx:%pK vdev_id:%d, scan_id:%d, roc_type:%d, roc_state:%d", + curr_roc_ctx, curr_roc_ctx->vdev_id, curr_roc_ctx->scan_id, + curr_roc_ctx->roc_type, curr_roc_ctx->roc_state); + + if (curr_roc_ctx->roc_state == ROC_STATE_IDLE) { + status = p2p_destroy_roc_ctx(curr_roc_ctx, true, true); + } else if (curr_roc_ctx->roc_state == + ROC_STATE_CANCEL_IN_PROG) { + p2p_debug("Receive cancel roc req when roc req is canceling, cookie %llx", + cancel_roc_ctx->cookie); + status = QDF_STATUS_SUCCESS; + } else { + status = p2p_execute_cancel_roc_req(curr_roc_ctx); + } + + return status; +} + +void p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + + p2p_debug("soc:%pK, scan event:%d", arg, event->type); + + p2p_soc_obj = (struct p2p_soc_priv_obj *)arg; + if (!p2p_soc_obj) { + p2p_err("Invalid P2P context"); + return; + } + + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (!curr_roc_ctx) { + p2p_err("Failed to find valid P2P roc context"); + return; + } + + qdf_mtrace(QDF_MODULE_ID_SCAN, QDF_MODULE_ID_P2P, event->type, + event->vdev_id, event->scan_id); + switch (event->type) { + case SCAN_EVENT_TYPE_STARTED: + p2p_process_scan_start_evt(curr_roc_ctx); + break; + case SCAN_EVENT_TYPE_FOREIGN_CHANNEL: + p2p_process_ready_on_channel_evt(curr_roc_ctx); + break; + case SCAN_EVENT_TYPE_COMPLETED: + case SCAN_EVENT_TYPE_DEQUEUED: + case SCAN_EVENT_TYPE_START_FAILED: + p2p_process_scan_complete_evt(curr_roc_ctx); + break; + default: + p2p_debug("drop scan event, %d", event->type); + } +} diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.h b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.h new file mode 100644 index 0000000000000000000000000000000000000000..8ed91f7eb08ef331b540d8dbff6ec5467f206610 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Defines RoC API & structures + */ + +#ifndef _WLAN_P2P_ROC_H_ +#define _WLAN_P2P_ROC_H_ + +#include +#include +#include + +#define P2P_EVENT_PROPAGATE_TIME 10 +#define P2P_WAIT_CANCEL_ROC 1000 +#define P2P_WAIT_CLEANUP_ROC 2000 +#define P2P_MAX_ROC_DURATION 1500 +#define P2P_MAX_ROC_DURATION_GO_PRESENT 600 +#define P2P_MAX_ROC_DURATION_DBS_NDP_PRESENT 400 +#define P2P_MAX_ROC_DURATION_NON_DBS_NDP_PRESENT 250 +#define P2P_MAX_ROC_DURATION_DBS_NAN_PRESENT 450 +#define P2P_MAX_ROC_DURATION_NON_DBS_NAN_PRESENT 300 + +#define P2P_ROC_DURATION_MULTI_GO_PRESENT 6 +#define P2P_ROC_DURATION_MULTI_GO_ABSENT 10 +#define P2P_ACTION_FRAME_DEFAULT_WAIT 200 +#define P2P_ROC_DEFAULT_DURATION 200 + +struct wlan_objmgr_vdev; +struct scan_event; + +/** + * enum roc_type - user requested or off channel tx + * @USER_REQUESTED: Requested by supplicant + * @OFF_CHANNEL_TX: Issued internally for off channel tx + */ +enum roc_type { + USER_REQUESTED, + OFF_CHANNEL_TX, +}; + +/** + * enum roc_state - P2P RoC state + * @ROC_STATE_IDLE: RoC not yet started or completed + * @ROC_STATE_REQUESTED: Sent scan command to scan manager + * @ROC_STATE_STARTED: Got started event from scan manager + * @ROC_STATE_ON_CHAN: Got foreign channel event from SCM + * @ROC_STATE_CANCEL_IN_PROG: Requested abort scan to SCM + * @ROC_STATE_INVALID: We should not come to this state + */ +enum roc_state { + ROC_STATE_IDLE = 0, + ROC_STATE_REQUESTED, + ROC_STATE_STARTED, + ROC_STATE_ON_CHAN, + ROC_STATE_CANCEL_IN_PROG, + ROC_STATE_INVALID, +}; + +/** + * struct p2p_roc_context - RoC request context + * @node: Node for next element in the list + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @vdev_id: Vdev id on which this request has come + * @scan_id: Scan id given by scan component for this roc req + * @tx_ctx: TX context if this ROC is for tx MGMT + * @chan: Chan for which this RoC has been requested + * @phy_mode: PHY mode + * @duration: Duration for the RoC + * @roc_type: RoC type User requested or internal + * @roc_timer: RoC timer + * @roc_state: Roc state + * @id: identifier of roc + */ +struct p2p_roc_context { + qdf_list_node_t node; + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; + uint32_t scan_id; + void *tx_ctx; + uint8_t chan; + uint8_t phy_mode; + uint32_t duration; + enum roc_type roc_type; + qdf_mc_timer_t roc_timer; + enum roc_state roc_state; + int32_t id; +}; + +/** + * struct cancel_roc_context - p2p cancel roc context + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @cookie: Cookie which is given by supplicant + */ +struct cancel_roc_context { + struct p2p_soc_priv_obj *p2p_soc_obj; + uint64_t cookie; +}; + +/** + * struct p2p_cleanup_param - p2p cleanup parameters + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @vdev_id: vdev id + */ +struct p2p_cleanup_param { + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; +}; + +/** + * p2p_mgmt_rx_action_ops() - register or unregister rx action callback + * @psoc: psoc object + * @isregister: register if true, unregister if false + * + * This function registers or unregisters rx action frame callback to + * mgmt txrx component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_mgmt_rx_action_ops(struct wlan_objmgr_psoc *psoc, + bool isregister); + +/** + * p2p_find_current_roc_ctx() - Find out roc context in progressing + * @p2p_soc_obj: p2p psoc private object + * + * This function finds out roc context in progressing from p2p psoc + * private object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +struct p2p_roc_context *p2p_find_current_roc_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj); + +/** + * p2p_find_roc_by_tx_ctx() - Find out roc context by tx context + * @p2p_soc_obj: p2p psoc private object + * @cookie: cookie is the key to find out roc context + * + * This function finds out roc context by tx context from p2p psoc + * private object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +struct p2p_roc_context *p2p_find_roc_by_tx_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie); + +/** + * p2p_find_roc_by_chan() - Find out roc context by channel + * @p2p_soc_obj: p2p psoc private object + * @chan: channel of the ROC + * + * This function finds out roc context by channel from p2p psoc + * private object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +struct p2p_roc_context *p2p_find_roc_by_chan( + struct p2p_soc_priv_obj *p2p_soc_obj, uint8_t chan); + +/** + * p2p_restart_roc_timer() - Restarts roc timer + * @roc_ctx: remain on channel context + * + * This function restarts roc timer with updated duration. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_restart_roc_timer(struct p2p_roc_context *roc_ctx); + +/** + * p2p_cleanup_roc_sync() - Cleanup roc context in queue + * @p2p_soc_obj: p2p psoc private object + * @vdev: vdev object + * + * This function cleanup roc context in queue, include the roc + * context in progressing until cancellation done. To avoid deadlock, + * don't call from scheduler thread. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_cleanup_roc_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev); + +/** + * p2p_process_cleanup_roc_queue() - process the message to cleanup roc + * @param: pointer to cleanup parameters + * + * This function process the message to cleanup roc context in queue, + * include the roc context in progressing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cleanup_roc_queue( + struct p2p_cleanup_param *param); + +/** + * p2p_process_roc_req() - Process roc request + * @roc_ctx: roc request context + * + * This function handles roc request. It will call API from scan/mgmt + * txrx component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_roc_req(struct p2p_roc_context *roc_ctx); + +/** + * p2p_process_cancel_roc_req() - Process cancel roc request + * @cancel_roc_ctx: cancel roc request context + * + * This function cancel roc request by cookie. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cancel_roc_req( + struct cancel_roc_context *cancel_roc_ctx); + +/** + * p2p_scan_event_cb() - Process scan event + * @vdev: vdev associated to this scan event + * @event: event information + * @arg: registered arguments + * + * This function handles P2P scan event and deliver P2P event to HDD + * layer by registered callback. + * + * Return: None + */ +void p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg); + +#endif /* _WLAN_P2P_ROC_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_api.h b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_api.h new file mode 100644 index 0000000000000000000000000000000000000000..d9d0757d7b74a516f73d93634c9ac1f008e06c1d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_api.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p public data structure definitions + */ + +#ifndef _WLAN_P2P_API_H_ +#define _WLAN_P2P_API_H_ + +#include + +/** + * wlan_p2p_check_oui_and_force_1x1() - Function to get P2P client device + * attributes from assoc request frames + * @assoc_ie: pointer to assoc request frame IEs + * @ie_len: length of the assoc request frame IE + * + * When assoc request is received from P2P STA device, this function checks + * for specific OUI present in the P2P device info attribute. The caller should + * take care of checking if this is called only in P2P GO mode. + * + * Return: True if OUI is present, else false. + */ +bool wlan_p2p_check_oui_and_force_1x1(uint8_t *assoc_ie, uint32_t ie_len); +#endif diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_cfg.h b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..04a5b6c85e5a9a8fe187eace793ddf15abe226d2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_cfg.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(CONFIG_P2P_H__) +#define CONFIG_P2P_H__ + +#include "cfg_define.h" +#include "cfg_converged.h" +#include "qdf_types.h" + +/* + * + * gGoKeepAlivePeriod - P2P GO keep alive period. + * @Min: 1 + * @Max: 65535 + * @Default: 20 + * + * This is P2P GO keep alive period. + * + * Related: None. + * + * Supported Feature: P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_GO_KEEP_ALIVE_PERIOD CFG_INI_UINT( \ + "gGoKeepAlivePeriod", \ + 1, \ + 65535, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "P2P GO keep alive period") + +/* + * + * gGoLinkMonitorPeriod - period where link is idle and where + * we send NULL frame + * @Min: 3 + * @Max: 50 + * @Default: 10 + * + * This is period where link is idle and where we send NULL frame for P2P GO. + * + * Related: None. + * + * Supported Feature: P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_GO_LINK_MONITOR_PERIOD CFG_INI_UINT( \ + "gGoLinkMonitorPeriod", \ + 3, \ + 50, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "period where link is idle and where we send NULL frame") + +/* + * + * isP2pDeviceAddrAdministrated - Enables to derive the P2P MAC address from + * the primary MAC address + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable to derive the P2P MAC address from the + * primary MAC address. + * + * Related: None. + * + * Supported Feature: P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_P2P_DEVICE_ADDRESS_ADMINISTRATED CFG_INI_BOOL( \ + "isP2pDeviceAddrAdministrated", \ + 1, \ + "derive the P2P MAC address from the primary MAC address") + +#define CFG_P2P_ALL \ + CFG(CFG_GO_KEEP_ALIVE_PERIOD) \ + CFG(CFG_GO_LINK_MONITOR_PERIOD) \ + CFG(CFG_P2P_DEVICE_ADDRESS_ADMINISTRATED) + +#endif diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_cfg_api.h b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_cfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..72e864833a97988dc694de990b2ad00dd789e707 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_cfg_api.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p configures interface definitions + */ + +#ifndef _WLAN_P2P_CFG_API_H_ +#define _WLAN_P2P_CFG_API_H_ + +#include + +struct wlan_objmgr_psoc; + +/** + * cfg_p2p_get_go_keepalive_period() - get go keepalive period + * @psoc: pointer to psoc object + * @period: go keepalive period + * + * This function gets go keepalive period to p2p component + */ +QDF_STATUS +cfg_p2p_get_go_keepalive_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period); + +/** + * cfg_p2p_get_go_link_monitor_period() - get go link monitor period + * @psoc: pointer to psoc object + * @period: go link monitor period + * + * This function gets go link monitor period to p2p component + */ +QDF_STATUS +cfg_p2p_get_go_link_monitor_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period); + +/** + * cfg_p2p_get_device_addr_admin() - get enable/disable p2p device + * addr administrated + * @psoc: pointer to psoc object + * @enable: enable/disable p2p device addr administrated + * + * This function gets enable/disable p2p device addr administrated + */ +QDF_STATUS +cfg_p2p_get_device_addr_admin(struct wlan_objmgr_psoc *psoc, + bool *enable); + +/** + * cfg_p2p_is_roam_config_disabled() - get disable roam config on sta interface + * during p2p connection + * @psoc: pointer to psoc object + * + * Get disable roam configuration during p2p connection + */ +bool cfg_p2p_is_roam_config_disabled(struct wlan_objmgr_psoc *psoc); + +#endif /* _WLAN_P2P_CFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..33fd71c70b400bd08289242bf0d118278471f365 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p public data structure definations + */ + +#ifndef _WLAN_P2P_PUBLIC_STRUCT_H_ +#define _WLAN_P2P_PUBLIC_STRUCT_H_ + +#include + +#define P2P_MAX_NOA_DESC 4 + +#define HEADER_LEN_P2P_IE 6 +#define OUI_SIZE_P2P 4 + +#define P2P_1X1_WAR_OUI "\x00\x50\xf2\x04" +#define P2P_1X1_OUI_LEN 4 + +#define MAX_CONFIG_METHODS_LEN 2 +#define DEVICE_CATEGORY_MAX_LEN 1 + +/** + * struct p2p_ps_params - P2P powersave related params + * @opp_ps: opportunistic power save + * @ctwindow: CT window + * @count: count + * @duration: duration + * @interval: interval + * @single_noa_duration: single shot noa duration + * @ps_selection: power save selection + * @session_id: session id + */ +struct p2p_ps_params { + uint8_t opp_ps; + uint32_t ctwindow; + uint8_t count; + uint32_t duration; + uint32_t interval; + uint32_t single_noa_duration; + uint8_t ps_selection; + uint8_t session_id; +}; + +/** + * struct p2p_roc_req - P2P roc request + * @vdev_id: Vdev id on which this request has come + * @chan: Chan for which this RoC has been requested + * @phy_mode: PHY mode + * @duration: Duration for the RoC + */ +struct p2p_roc_req { + uint32_t vdev_id; + uint32_t chan; + uint32_t phy_mode; + uint32_t duration; +}; + +/** + * enum p2p_roc_event - P2P RoC event + * @ROC_EVENT_READY_ON_CHAN: RoC has started now + * @ROC_EVENT_COMPLETED: RoC has been completed + * @ROC_EVENT_INAVLID: Invalid event + */ +enum p2p_roc_event { + ROC_EVENT_READY_ON_CHAN = 0, + ROC_EVENT_COMPLETED, + ROC_EVENT_INAVLID, +}; + +/** + * struct p2p_event - p2p event + * @vdev_id: Vdev id + * @roc_event: RoC event + * @cookie: Cookie which is given to supplicant for this roc req + * @chan: Chan for which this RoC has been requested + * @duration: Duration for the RoC + */ +struct p2p_event { + uint32_t vdev_id; + enum p2p_roc_event roc_event; + uint64_t cookie; + uint32_t chan; + uint32_t duration; +}; + +/** + * struct p2p_rx_mgmt_frame - rx mgmt frame structure + * @frame_len: Frame length + * @rx_freq: RX Frequency + * @vdev_id: Vdev id + * @frm_type: Frame type + * @rx_rssi: RX rssi + * @buf: Buffer address + */ +struct p2p_rx_mgmt_frame { + uint32_t frame_len; + uint32_t rx_freq; + uint32_t vdev_id; + uint32_t frm_type; + uint32_t rx_rssi; + uint8_t buf[1]; +}; + +/** + * struct p2p_tx_cnf - tx confirm structure + * @vdev_id: Vdev id + * @action_cookie: TX cookie for this action frame + * @buf_len: Frame length + * @status: TX status + * @buf: Buffer address + */ +struct p2p_tx_cnf { + uint32_t vdev_id; + uint64_t action_cookie; + uint32_t buf_len; + uint32_t status; + uint8_t *buf; +}; + +/** + * struct p2p_mgmt_tx - p2p mgmt tx structure + * @vdev_id: Vdev id + * @chan: Chan for which this RoC has been requested + * @wait: Duration for the RoC + * @len: Length of tx buffer + * @no_cck: Required cck or not + * @dont_wait_for_ack: Wait for ack or not + * @off_chan: Off channel tx or not + * @buf: TX buffer + */ +struct p2p_mgmt_tx { + uint32_t vdev_id; + uint32_t chan; + uint32_t wait; + uint32_t len; + uint32_t no_cck; + uint32_t dont_wait_for_ack; + uint32_t off_chan; + const uint8_t *buf; +}; + +/** + * struct p2p_set_mac_filter + * @vdev_id: Vdev id + * @mac: mac addr + * @freq: frequency + * @set: set or clear + */ +struct p2p_set_mac_filter { + uint32_t vdev_id; + uint8_t mac[QDF_MAC_ADDR_SIZE]; + uint32_t freq; + bool set; +}; + +/** + * struct p2p_set_mac_filter_evt + * @vdev_id: Vdev id + * @status: target reported result of set mac addr filter + */ +struct p2p_set_mac_filter_evt { + uint32_t vdev_id; + uint32_t status; +}; + +/** + * struct p2p_ps_config + * @vdev_id: Vdev id + * @opp_ps: Opportunistic power save + * @ct_window: CT window + * @count: Count + * @duration: Duration + * @interval: Interval + * @single_noa_duration: Single shot noa duration + * @ps_selection: power save selection + */ +struct p2p_ps_config { + uint32_t vdev_id; + uint32_t opp_ps; + uint32_t ct_window; + uint32_t count; + uint32_t duration; + uint32_t interval; + uint32_t single_noa_duration; + uint32_t ps_selection; +}; + +/** + * struct p2p_lo_start - p2p listen offload start + * @vdev_id: Vdev id + * @ctl_flags: Control flag + * @freq: P2P listen frequency + * @period: Listen offload period + * @interval: Listen offload interval + * @count: Number listen offload intervals + * @dev_types_len: Device types length + * @probe_resp_len: Probe response template length + * @device_types: Device types + * @probe_resp_tmplt: Probe response template + */ +struct p2p_lo_start { + uint32_t vdev_id; + uint32_t ctl_flags; + uint32_t freq; + uint32_t period; + uint32_t interval; + uint32_t count; + uint32_t dev_types_len; + uint32_t probe_resp_len; + uint8_t *device_types; + uint8_t *probe_resp_tmplt; +}; + +/** + * struct p2p_lo_event + * @vdev_id: vdev id + * @reason_code: reason code + */ +struct p2p_lo_event { + uint32_t vdev_id; + uint32_t reason_code; +}; + +/** + * struct noa_descriptor - noa descriptor + * @type_count: 255: continuous schedule, 0: reserved + * @duration: Absent period duration in micro seconds + * @interval: Absent period interval in micro seconds + * @start_time: 32 bit tsf time when in starts + */ +struct noa_descriptor { + uint32_t type_count; + uint32_t duration; + uint32_t interval; + uint32_t start_time; +}; + +/** + * struct p2p_noa_info - p2p noa information + * @index: identifies instance of NOA su element + * @opps_ps: opps ps state of the AP + * @ct_window: ct window in TUs + * @vdev_id: vdev id + * @num_descriptors: number of NOA descriptors + * @noa_desc: noa descriptors + */ +struct p2p_noa_info { + uint32_t index; + uint32_t opps_ps; + uint32_t ct_window; + uint32_t vdev_id; + uint32_t num_desc; + struct noa_descriptor noa_desc[P2P_MAX_NOA_DESC]; +}; + +/** + * struct p2p_protocol_callbacks - callback to non-converged driver + * @is_mgmt_protected: func to get 11w mgmt protection status + */ +struct p2p_protocol_callbacks { + bool (*is_mgmt_protected)(uint32_t vdev_id, const uint8_t *peer_addr); +}; + +/** + * enum p2p_attr_id - enum for P2P attributes ID in P2P IE + * @P2P_ATTR_STATUS - Attribute Status none + * @P2P_ATTR_MINOR_REASON_CODE: Minor reason code attribute + * @P2P_ATTR_CAPABILITY: Capability attribute + * @P2P_ATTR_DEVICE_ID: device ID attribute + * @P2P_ATTR_GROUP_OWNER_INTENT: Group owner intent attribute + * @P2P_ATTR_CONFIGURATION_TIMEOUT: Config timeout attribute + * @P2P_ATTR_LISTEN_CHANNEL: listen channel attribute + * @P2P_ATTR_GROUP_BSSID: Group BSSID attribute + * @P2P_ATTR_EXT_LISTEN_TIMING: Listen timing attribute + * @P2P_ATTR_INTENDED_INTERFACE_ADDR: Intended interface address attribute + * @P2P_ATTR_MANAGEABILITY: Manageability attribute + * @P2P_ATTR_CHANNEL_LIST: Channel list attribute + * @P2P_ATTR_NOTICE_OF_ABSENCE: Notice of Absence attribute + * @P2P_ATTR_DEVICE_INFO: Device Info attribute + * @P2P_ATTR_GROUP_INFO: Group Info attribute + * @P2P_ATTR_GROUP_ID: Group ID attribute + * @P2P_ATTR_INTERFACE: Interface attribute + * @P2P_ATTR_OPERATING_CHANNEL: Operating channel attribute + * @P2P_ATTR_INVITATION_FLAGS: Invitation flags attribute + * @P2P_ATTR_OOB_GO_NEG_CHANNEL: GO neg channel attribute + * @P2P_ATTR_SERVICE_HASH: Service HASH attribute + * @P2P_ATTR_SESSION_INFORMATION_DATA: Session Info data attribute + * @P2P_ATTR_CONNECTION_CAPABILITY = Connection capability attribute + * @P2P_ATTR_ADVERTISEMENT_ID = Advertisement ID attribute + * @P2P_ATTR_ADVERTISED_SERVICE = Advertised Service attribute + * @P2P_ATTR_SESSION_ID = Session ID attribute + * @P2P_ATTR_FEATURE_CAPABILITY = Feature capability attribute + * @P2P_ATTR_PERSISTENT_GROUP -Persistent group attribute + * @P2P_ATTR_VENDOR_SPECIFIC - Vendor specific attribute + */ +enum p2p_attr_id { + P2P_ATTR_STATUS = 0, + P2P_ATTR_MINOR_REASON_CODE = 1, + P2P_ATTR_CAPABILITY = 2, + P2P_ATTR_DEVICE_ID = 3, + P2P_ATTR_GROUP_OWNER_INTENT = 4, + P2P_ATTR_CONFIGURATION_TIMEOUT = 5, + P2P_ATTR_LISTEN_CHANNEL = 6, + P2P_ATTR_GROUP_BSSID = 7, + P2P_ATTR_EXT_LISTEN_TIMING = 8, + P2P_ATTR_INTENDED_INTERFACE_ADDR = 9, + P2P_ATTR_MANAGEABILITY = 10, + P2P_ATTR_CHANNEL_LIST = 11, + P2P_ATTR_NOTICE_OF_ABSENCE = 12, + P2P_ATTR_DEVICE_INFO = 13, + P2P_ATTR_GROUP_INFO = 14, + P2P_ATTR_GROUP_ID = 15, + P2P_ATTR_INTERFACE = 16, + P2P_ATTR_OPERATING_CHANNEL = 17, + P2P_ATTR_INVITATION_FLAGS = 18, + P2P_ATTR_OOB_GO_NEG_CHANNEL = 19, + P2P_ATTR_SERVICE_HASH = 21, + P2P_ATTR_SESSION_INFORMATION_DATA = 22, + P2P_ATTR_CONNECTION_CAPABILITY = 23, + P2P_ATTR_ADVERTISEMENT_ID = 24, + P2P_ATTR_ADVERTISED_SERVICE = 25, + P2P_ATTR_SESSION_ID = 26, + P2P_ATTR_FEATURE_CAPABILITY = 27, + P2P_ATTR_PERSISTENT_GROUP = 28, + P2P_ATTR_VENDOR_SPECIFIC = 221 +}; +#endif /* _WLAN_P2P_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_tgt_api.h b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..f5938d6981bc4a8d092ce9b132713dfb2933c982 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_tgt_api.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p south bound interface definitions + */ + +#ifndef _WLAN_P2P_TGT_API_H_ +#define _WLAN_P2P_TGT_API_H_ + +#include +#include + +struct scan_event; +struct wlan_objmgr_psoc; +struct wlan_objmgr_peer; +struct p2p_noa_info; +struct p2p_lo_event; +struct mgmt_rx_event_params; +enum mgmt_frame_type; +struct p2p_set_mac_filter_evt; + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD + +/** + * tgt_p2p_lo_event_cb() - Listen offload stop request + * @psoc: soc object + * @event_info: lo stop event buffer + * + * This function gets called from target interface. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_lo_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_event *event_info); + +/** + * tgt_p2p_register_lo_ev_handler() - register lo event + * @psoc: soc object + * + * p2p tgt api to register listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_register_lo_ev_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * tgt_p2p_unregister_lo_ev_handler() - unregister lo event + * @psoc: soc object + * + * p2p tgt api to unregister listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_unregister_lo_ev_handler( + struct wlan_objmgr_psoc *psoc); +#else +static inline QDF_STATUS tgt_p2p_register_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS tgt_p2p_unregister_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * tgt_p2p_register_macaddr_rx_filter_evt_handler() - register add mac rx + * filter status event + * @psoc: soc object + * @register: register or unregister + * + * p2p tgt api to register add mac rx filter status event + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_register_macaddr_rx_filter_evt_handler( + struct wlan_objmgr_psoc *psoc, bool register); + +/** + * tgt_p2p_register_noa_ev_handler() - register noa event + * @psoc: soc object + * + * p2p tgt api to register noa event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_register_noa_ev_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * tgt_p2p_unregister_noa_ev_handler() - unregister noa event + * @psoc: soc object + * + * p2p tgt api to unregister noa event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_unregister_noa_ev_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * tgt_p2p_scan_event_cb() - Callback for scan event + * @vdev: vdev object + * @event: event information + * @arg: registered arguments + * + * This function gets called from scan component when getting P2P + * scan event. + * + * Return: None + */ +void tgt_p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg); + +/** + * tgt_p2p_mgmt_download_comp_cb() - Callback for mgmt frame tx + * complete + * @context: tx context + * @buf: buffer address + * @free: need to free or not + * + * This function gets called from mgmt tx/rx component when mgmt + * frame tx complete. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_mgmt_download_comp_cb(void *context, + qdf_nbuf_t buf, bool free); + +/** + * tgt_p2p_mgmt_ota_comp_cb() - Callback for mgmt frame tx ack + * @context: tx context + * @buf: buffer address + * @status: tx status + * @tx_compl_params: tx complete parameters + * + * This function gets called from mgmt tx/rx component when getting + * mgmt frame tx ack. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_mgmt_ota_comp_cb(void *context, qdf_nbuf_t buf, + uint32_t status, void *tx_compl_params); + +/** + * tgt_p2p_mgmt_frame_rx_cb() - Callback for rx mgmt frame + * @psoc: soc context + * @peer: peer context + * @buf: rx buffer + * @mgmt_rx_params: mgmt rx parameters + * @frm_type: frame type + * + * This function gets called from mgmt tx/rx component when rx mgmt + * frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_mgmt_frame_rx_cb(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type); +/** + * tgt_p2p_noa_event_cb() - Callback for noa event + * @psoc: soc object + * @event_info: noa event information + * + * This function gets called from target interface. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_noa_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_noa_info *event_info); + +/** + * tgt_p2p_add_mac_addr_status_event_cb() - Callback for set mac addr filter evt + * @psoc: soc object + * @event_info: event information type of p2p_set_mac_filter_evt + * + * This function gets called from target interface. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS +tgt_p2p_add_mac_addr_status_event_cb( + struct wlan_objmgr_psoc *psoc, + struct p2p_set_mac_filter_evt *event_info); + +#endif /* _WLAN_P2P_TGT_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..c855e7fd0b8b8d3132fcfdf8799a9004efd296f0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2017-2018,2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p north bound interface definitions + */ + +#ifndef _WLAN_P2P_UCFG_API_H_ +#define _WLAN_P2P_UCFG_API_H_ + +#include "wlan_p2p_cfg_api.h" +#include + +struct wlan_objmgr_psoc; +struct p2p_roc_req; +struct p2p_event; +struct p2p_rx_mgmt_frame; +struct p2p_tx_cnf; +struct p2p_mgmt_tx; +struct p2p_ps_config; +struct p2p_lo_start; +struct p2p_lo_event; +struct p2p_protocol_callbacks; + +/** + * p2p_rx_callback() - Callback for rx mgmt frame + * @user_data: user data associated to this rx mgmt frame. + * @rx_frame: RX mgmt frame + * + * This callback will be used to give rx frames to hdd. + * + * Return: None + */ +typedef void (*p2p_rx_callback)(void *user_data, + struct p2p_rx_mgmt_frame *rx_frame); + +/** + * p2p_action_tx_cnf_callback() - Callback for tx confirmation + * @user_data: user data associated to this tx confirmation + * @tx_cnf: tx confirmation information + * + * This callback will be used to give tx mgmt frame confirmation to + * hdd. + * + * Return: None + */ +typedef void (*p2p_action_tx_cnf_callback)(void *user_data, + struct p2p_tx_cnf *tx_cnf); + +/** + * p2p_lo_event_callback() - Callback for listen offload event + * @user_data: user data associated to this lo event + * @p2p_lo_event: listen offload event information + * + * This callback will be used to give listen offload event to hdd. + * + * Return: None + */ +typedef void (*p2p_lo_event_callback)(void *user_data, + struct p2p_lo_event *p2p_lo_event); + +/** + * p2p_event_callback() - Callback for P2P event + * @user_data: user data associated to this p2p event + * @p2p_event: p2p event information + * + * This callback will be used to give p2p event to hdd. + * + * Return: None + */ +typedef void (*p2p_event_callback)(void *user_data, + struct p2p_event *p2p_event); + +/** + * struct p2p_start_param - p2p soc start parameters. Below callbacks + * will be registered by the HDD + * @rx_callback: Function pointer to hdd rx callback. This + * function will be used to give rx frames to hdd + * @rx_cb_data: RX callback user data + * @event_cb: Founction pointer to hdd p2p event callback. + * This function will be used to give p2p event + * to hdd + * @event_cb_data: Pointer to p2p event callback user data + * @tx_cnf_cb: Function pointer to hdd tx confirm callback. + * This function will be used to give tx confirm + * to hdd + * @tx_cnf_cb_data: Pointer to p2p tx confirm callback user data + * @lo_event_cb: Founction pointer to p2p listen offload + * callback. This function will be used to give + * listen offload stopped event to hdd + * @lo_event_cb_data: Pointer to p2p listen offload callback user data + */ +struct p2p_start_param { + p2p_rx_callback rx_cb; + void *rx_cb_data; + p2p_event_callback event_cb; + void *event_cb_data; + p2p_action_tx_cnf_callback tx_cnf_cb; + void *tx_cnf_cb_data; +#ifdef FEATURE_P2P_LISTEN_OFFLOAD + p2p_lo_event_callback lo_event_cb; + void *lo_event_cb_data; +#endif +}; + +/** + * ucfg_p2p_init() - P2P component initialization + * + * This function gets called when dispatcher initializing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_init(void); + +/** + * ucfg_p2p_deinit() - P2P component de-init + * + * This function gets called when dispatcher de-init. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_deinit(void); + +/** + * ucfg_p2p_psoc_open() - Open P2P component + * @soc: soc context + * + * This function gets called when dispatcher opening. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_open(struct wlan_objmgr_psoc *soc); + +/** + * ucfg_p2p_psoc_close() - Close P2P component + * @soc: soc context + * + * This function gets called when dispatcher closing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_close(struct wlan_objmgr_psoc *soc); + +/** + * ucfg_p2p_psoc_start() - Start P2P component + * @soc: soc context + * @req: P2P start parameters + * + * This function gets called when up layer starting up. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req); + +/** + * ucfg_p2p_psoc_stop() - Stop P2P component + * @soc: soc context + * + * This function gets called when up layer exit. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_stop(struct wlan_objmgr_psoc *soc); + +/** + * ucfg_p2p_roc_req() - Roc request + * @soc: soc context + * @roc_req: Roc request parameters + * @cookie: return cookie to caller + * + * This function delivers roc request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_roc_req(struct wlan_objmgr_psoc *soc, + struct p2p_roc_req *roc_req, uint64_t *cookie); + +/** + * ucfg_p2p_roc_cancel_req() - Cancel roc request + * @soc: soc context + * @cookie: Find out the roc request by cookie + * + * This function delivers cancel roc request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_roc_cancel_req(struct wlan_objmgr_psoc *soc, + uint64_t cookie); + +/** + * ucfg_p2p_cleanup_roc_by_vdev() - Cleanup roc request by vdev + * @vdev: pointer to vdev object + * + * This function call P2P API to cleanup roc request by vdev + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_roc_by_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_cleanup_roc_by_poc() - Cleanup roc request by psoc + * @psoc: pointer to psoc object + * + * This function call P2P API to cleanup roc request by psoc + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_roc_by_psoc(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_p2p_cleanup_tx_by_vdev() - Cleanup tx request by vdev + * @vdev: pointer to vdev object + * + * This function call P2P API to cleanup tx action frame request by vdev + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_tx_by_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_cleanup_tx_by_poc() - Cleanup tx request by psoc + * @psoc: pointer to psoc object + * + * This function call P2P API to cleanup tx action frame request by psoc + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_tx_by_psoc(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_p2p_mgmt_tx() - Mgmt frame tx request + * @soc: soc context + * @mgmt_frm: TX mgmt frame parameters + * @cookie: Return the cookie to caller + * + * This function delivers mgmt frame tx request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_mgmt_tx(struct wlan_objmgr_psoc *soc, + struct p2p_mgmt_tx *mgmt_frm, uint64_t *cookie); + +/** + * ucfg_p2p_mgmt_tx_cancel() - Cancel mgmt frame tx request + * @soc: soc context + * @vdev: vdev object + * @cookie: Find out the mgmt tx request by cookie + * + * This function delivers cancel mgmt frame tx request request to P2P + * component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_mgmt_tx_cancel(struct wlan_objmgr_psoc *soc, + struct wlan_objmgr_vdev *vdev, uint64_t cookie); + +/** + * ucfg_p2p_set_ps() - P2P set power save + * @soc: soc context + * @ps_config: power save configure + * + * This function delivers p2p power save request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_set_ps(struct wlan_objmgr_psoc *soc, + struct p2p_ps_config *ps_config); + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * ucfg_p2p_lo_start() - Listen offload start request + * @soc: soc context + * @p2p_lo_start: lo start parameters + * + * This function delivers listen offload start request to P2P + * component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_lo_start(struct wlan_objmgr_psoc *soc, + struct p2p_lo_start *p2p_lo_start); + +/** + * ucfg_p2p_lo_stop() - Listen offload stop request + * @soc: soc context + * @vdev_id: vdev id + * + * This function delivers listen offload stop request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_lo_stop(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id); +#endif + +/** + * p2p_peer_authorized() - Process peer authorized event + * @vdev: vdev structure to which peer is associated + * @mac_addr: peer mac address + * + * This function handles disables noa whenever a legacy station + * complete 4-way handshake after association. + * + * Return: void + */ +void p2p_peer_authorized(struct wlan_objmgr_vdev *vdev, uint8_t *mac_addr); + +/** + * ucfg_p2p_set_noa() - Disable/Enable NOA + * @soc: soc context + * @vdev_id: vdev id + * @disable_noa: TRUE - Disable NoA, FALSE - Enable NoA + * + * This function send wmi command to enable / disable NoA. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_set_noa(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id, bool disable_noa); + +/** + * ucfg_p2p_check_random_mac() - check random mac addr or not + * @soc: soc context + * @vdev_id: vdev id + * @random_mac_addr: mac addr to be checked + * + * This function check the input addr is random mac addr or not for vdev. + * + * Return: true if addr is random mac address else false. + */ +bool ucfg_p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr); + +/** + * ucfg_p2p_register_callbacks() - register p2p callbacks + * @soc: soc context + * @cb_obj: p2p_protocol_callbacks struct + * + * This function registers lim callbacks to p2p components to provide + * protocol information. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_register_callbacks(struct wlan_objmgr_psoc *soc, + struct p2p_protocol_callbacks *cb_obj); + +/** + * ucfg_p2p_status_scan() - Show P2P connection status when scanning + * @vdev: vdev context + * + * This function shows P2P connection status when scanning. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_scan(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_connect() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when connecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_connect(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_disconnect() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when disconnecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_disconnect(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_start_bss() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when starting bss. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_start_bss(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_stop_bss() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when stopping bss. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_is_roam_config_disabled() - Roam disable config during p2p + * connection + * @psoc: psoc context + * + * During P2P connection disable roam on STA interface + * + * Return: p2p disable roam - in case of success else false + */ +static inline +bool ucfg_p2p_is_roam_config_disabled(struct wlan_objmgr_psoc *psoc) +{ + return cfg_p2p_is_roam_config_disabled(psoc); +} +#endif /* _WLAN_P2P_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_api.c b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_api.c new file mode 100644 index 0000000000000000000000000000000000000000..63f04cdd27d6a57b62818dfbe75c5dfb17737a47 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_api.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains P2P public API's exposed. + */ + +#include "wlan_p2p_api.h" +#include +#include "wlan_p2p_public_struct.h" +#include "../../core/src/wlan_p2p_main.h" + +bool wlan_p2p_check_oui_and_force_1x1(uint8_t *assoc_ie, uint32_t assoc_ie_len) +{ + if (!assoc_ie || !assoc_ie_len) + return false; + + return p2p_check_oui_and_force_1x1(assoc_ie, assoc_ie_len); +} diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_cfg.c b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_cfg.c new file mode 100644 index 0000000000000000000000000000000000000000..f272f8b5cbfbd661216ad80a6813a84a91cdc9a4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_cfg.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains p2p configures interface definitions + */ + +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_cfg_api.h" +#include "../../core/src/wlan_p2p_main.h" +#include "wlan_mlme_ucfg_api.h" + +static inline struct p2p_soc_priv_obj * +wlan_psoc_get_p2p_object(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); +} + +QDF_STATUS +cfg_p2p_get_go_keepalive_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *period = 0; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *period = p2p_soc_obj->param.go_keepalive_period; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_p2p_get_go_link_monitor_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *period = 0; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *period = p2p_soc_obj->param.go_link_monitor_period; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_p2p_get_device_addr_admin(struct wlan_objmgr_psoc *psoc, + bool *enable) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *enable = false; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *enable = p2p_soc_obj->param.p2p_device_addr_admin; + + return QDF_STATUS_SUCCESS; +} + +bool cfg_p2p_is_roam_config_disabled(struct wlan_objmgr_psoc *psoc) +{ + uint32_t sta_roam_disable = 0; + + if (ucfg_mlme_get_roam_disable_config(psoc, &sta_roam_disable) == + QDF_STATUS_SUCCESS) + return sta_roam_disable & LFR3_STA_ROAM_DISABLE_BY_P2P; + + return false; +} diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_tgt_api.c b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_tgt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..3416c87685aa99ece591513ec09d3c9ac3b9444e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_tgt_api.c @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains p2p south bound interface definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_public_struct.h" +#include "../../core/src/wlan_p2p_main.h" +#include "../../core/src/wlan_p2p_roc.h" +#include "../../core/src/wlan_p2p_off_chan_tx.h" + +#define IEEE80211_FC0_TYPE_MASK 0x0c +#define P2P_NOISE_FLOOR_DBM_DEFAULT (-96) + +static inline struct wlan_lmac_if_p2p_tx_ops * +wlan_psoc_get_p2p_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &(psoc->soc_cb.tx_ops.p2p); +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +QDF_STATUS tgt_p2p_register_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->reg_lo_ev_handler) { + status = p2p_ops->reg_lo_ev_handler(psoc, NULL); + p2p_debug("register lo event, status:%d", status); + } + + return status; +} + +QDF_STATUS tgt_p2p_unregister_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->unreg_lo_ev_handler) { + status = p2p_ops->unreg_lo_ev_handler(psoc, NULL); + p2p_debug("unregister lo event, status:%d", status); + } + + return status; +} + +QDF_STATUS tgt_p2p_lo_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_event *event_info) +{ + struct p2p_lo_stop_event *lo_stop_event; + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + p2p_debug("soc:%pK, event_info:%pK", psoc, event_info); + + if (!psoc) { + p2p_err("psoc context passed is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc object is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + if (!event_info) { + p2p_err("invalid lo stop event information"); + return QDF_STATUS_E_INVAL; + } + + lo_stop_event = qdf_mem_malloc(sizeof(*lo_stop_event)); + if (!lo_stop_event) { + p2p_err("Failed to allocate p2p lo stop event"); + qdf_mem_free(event_info); + return QDF_STATUS_E_NOMEM; + } + + lo_stop_event->p2p_soc_obj = p2p_soc_obj; + lo_stop_event->lo_event = event_info; + msg.type = P2P_EVENT_LO_STOPPED; + msg.bodyptr = lo_stop_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(lo_stop_event->lo_event); + qdf_mem_free(lo_stop_event); + p2p_err("post msg fail:%d", status); + } + + return status; +} +#endif /* FEATURE_P2P_LISTEN_OFFLOAD */ + +QDF_STATUS +tgt_p2p_add_mac_addr_status_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_set_mac_filter_evt *event_info) +{ + struct p2p_mac_filter_rsp *mac_filter_rsp; + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + if (!psoc) { + p2p_err("random_mac:psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + if (!event_info) { + p2p_err("random_mac:invalid event_info"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj( + psoc, WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("random_mac:p2p soc object is NULL"); + return QDF_STATUS_E_INVAL; + } + + mac_filter_rsp = qdf_mem_malloc(sizeof(*mac_filter_rsp)); + if (!mac_filter_rsp) { + p2p_err("random_mac:Failed to allocate mac_filter_rsp"); + return QDF_STATUS_E_NOMEM; + } + + mac_filter_rsp->p2p_soc_obj = p2p_soc_obj; + mac_filter_rsp->vdev_id = event_info->vdev_id; + mac_filter_rsp->status = event_info->status; + + msg.type = P2P_EVENT_ADD_MAC_RSP; + msg.bodyptr = mac_filter_rsp; + msg.callback = p2p_process_evt; + status = scheduler_post_message(QDF_MODULE_ID_P2P, QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(mac_filter_rsp); + + return status; +} + +QDF_STATUS tgt_p2p_register_macaddr_rx_filter_evt_handler( + struct wlan_objmgr_psoc *psoc, bool reg) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->reg_mac_addr_rx_filter_handler) { + status = p2p_ops->reg_mac_addr_rx_filter_handler(psoc, reg); + p2p_debug("register mac addr rx filter event, register %d status:%d", + reg, status); + } + + return status; +} + +QDF_STATUS tgt_p2p_register_noa_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->reg_noa_ev_handler) { + status = p2p_ops->reg_noa_ev_handler(psoc, NULL); + p2p_debug("register noa event, status:%d", status); + } + + return status; +} + +QDF_STATUS tgt_p2p_unregister_noa_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->unreg_noa_ev_handler) { + status = p2p_ops->unreg_noa_ev_handler(psoc, NULL); + p2p_debug("unregister noa event, status:%d", status); + } + + return status; +} + +void tgt_p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{ + p2p_scan_event_cb(vdev, event, arg); +} + +QDF_STATUS tgt_p2p_mgmt_download_comp_cb(void *context, + qdf_nbuf_t buf, bool free) +{ + p2p_debug("conext:%pK, buf:%pK, free:%d", context, + qdf_nbuf_data(buf), free); + + qdf_nbuf_free(buf); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_p2p_mgmt_ota_comp_cb(void *context, qdf_nbuf_t buf, + uint32_t status, void *tx_compl_params) +{ + struct p2p_tx_conf_event *tx_conf_event; + struct scheduler_msg msg = {0}; + QDF_STATUS ret; + + p2p_debug("context:%pK, buf:%pK, status:%d, tx complete params:%pK", + context, buf, status, tx_compl_params); + + if (!context) { + p2p_err("invalid context"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_INVAL; + } + + tx_conf_event = qdf_mem_malloc(sizeof(*tx_conf_event)); + if (!tx_conf_event) { + p2p_err("Failed to allocate tx cnf event"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + tx_conf_event->status = status; + tx_conf_event->nbuf = buf; + tx_conf_event->p2p_soc_obj = (struct p2p_soc_priv_obj *)context; + msg.type = P2P_EVENT_MGMT_TX_ACK_CNF; + msg.bodyptr = tx_conf_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + ret = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(ret)) { + qdf_mem_free(tx_conf_event); + qdf_nbuf_free(buf); + p2p_err("post msg fail:%d", status); + } + + return ret; +} + +QDF_STATUS tgt_p2p_mgmt_frame_rx_cb(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type) +{ + struct p2p_rx_mgmt_frame *rx_mgmt; + struct p2p_rx_mgmt_event *rx_mgmt_event; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct scheduler_msg msg = {0}; + struct wlan_objmgr_vdev *vdev; + uint32_t vdev_id; + uint8_t *pdata; + QDF_STATUS status; + + p2p_debug("psoc:%pK, peer:%pK, type:%d", psoc, peer, frm_type); + + if (!mgmt_rx_params) { + p2p_err("mgmt rx params is NULL"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p ctx is NULL, drop this frame"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + if (!peer) { + if (p2p_soc_obj->cur_roc_vdev_id == P2P_INVALID_VDEV_ID) { + p2p_err("vdev id of current roc invalid"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } else { + vdev_id = p2p_soc_obj->cur_roc_vdev_id; + } + } else { + vdev = wlan_peer_get_vdev(peer); + if (!vdev) { + p2p_err("vdev is NULL in peer, drop this frame"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } + vdev_id = wlan_vdev_get_id(vdev); + } + + rx_mgmt_event = qdf_mem_malloc_atomic(sizeof(*rx_mgmt_event)); + if (!rx_mgmt_event) { + p2p_debug_rl("Failed to allocate rx mgmt event"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + rx_mgmt = qdf_mem_malloc_atomic(sizeof(*rx_mgmt) + + mgmt_rx_params->buf_len); + if (!rx_mgmt) { + p2p_debug_rl("Failed to allocate rx mgmt frame"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + pdata = (uint8_t *)qdf_nbuf_data(buf); + rx_mgmt->frame_len = mgmt_rx_params->buf_len; + rx_mgmt->rx_freq = mgmt_rx_params->chan_freq; + rx_mgmt->vdev_id = vdev_id; + rx_mgmt->frm_type = frm_type; + rx_mgmt->rx_rssi = mgmt_rx_params->snr + + P2P_NOISE_FLOOR_DBM_DEFAULT; + rx_mgmt_event->rx_mgmt = rx_mgmt; + rx_mgmt_event->p2p_soc_obj = p2p_soc_obj; + qdf_mem_copy(rx_mgmt->buf, pdata, mgmt_rx_params->buf_len); + msg.type = P2P_EVENT_RX_MGMT; + msg.bodyptr = rx_mgmt_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(rx_mgmt_event->rx_mgmt); + qdf_mem_free(rx_mgmt_event); + p2p_err("post msg fail:%d", status); + } + qdf_nbuf_free(buf); + + return status; +} + +QDF_STATUS tgt_p2p_noa_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_noa_info *event_info) +{ + struct p2p_noa_event *noa_event; + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + p2p_debug("soc:%pK, event_info:%pK", psoc, event_info); + + if (!psoc) { + p2p_err("psoc context passed is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc object is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + if (!event_info) { + p2p_err("invalid noa event information"); + return QDF_STATUS_E_INVAL; + } + + noa_event = qdf_mem_malloc(sizeof(*noa_event)); + if (!noa_event) { + p2p_err("Failed to allocate p2p noa event"); + qdf_mem_free(event_info); + return QDF_STATUS_E_NOMEM; + } + + noa_event->p2p_soc_obj = p2p_soc_obj; + noa_event->noa_info = event_info; + msg.type = P2P_EVENT_NOA; + msg.bodyptr = noa_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(noa_event->noa_info); + qdf_mem_free(noa_event); + p2p_err("post msg fail:%d", status); + } + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..b15f078acd2a1f9df9467a1b752b5b07fed3a2ac --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c @@ -0,0 +1,653 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains p2p north bound interface definitions + */ + +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_ucfg_api.h" +#include "../../core/src/wlan_p2p_main.h" +#include "../../core/src/wlan_p2p_roc.h" +#include "../../core/src/wlan_p2p_off_chan_tx.h" + +static inline struct wlan_lmac_if_p2p_tx_ops * +ucfg_p2p_psoc_get_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &(psoc->soc_cb.tx_ops.p2p); +} + +/** + * is_p2p_ps_allowed() - If P2P power save is allowed or not + * @vdev: vdev object + * @id: umac component id + * + * This function returns TRUE if P2P power-save is allowed + * else returns FALSE. + * + * Return: bool + */ +static bool is_p2p_ps_allowed(struct wlan_objmgr_vdev *vdev, + enum wlan_umac_comp_id id) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj; + uint8_t is_p2pgo = 0; + + if (!vdev) { + p2p_err("vdev:%pK", vdev); + return true; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + + if (wlan_vdev_mlme_get_opmode(vdev) == QDF_P2P_GO_MODE) + is_p2pgo = 1; + + if (!p2p_vdev_obj || !is_p2pgo) { + p2p_err("p2p_vdev_obj:%pK is_p2pgo:%u", + p2p_vdev_obj, is_p2pgo); + return false; + } + if (p2p_vdev_obj->non_p2p_peer_count && + p2p_vdev_obj->noa_status == false) { + p2p_debug("non_p2p_peer_count: %u, noa_status: %d", + p2p_vdev_obj->non_p2p_peer_count, + p2p_vdev_obj->noa_status); + return false; + } + + return true; +} + +QDF_STATUS ucfg_p2p_init(void) +{ + return p2p_component_init(); +} + +QDF_STATUS ucfg_p2p_deinit(void) +{ + return p2p_component_deinit(); +} + +QDF_STATUS ucfg_p2p_psoc_open(struct wlan_objmgr_psoc *soc) +{ + return p2p_psoc_object_open(soc); +} + +QDF_STATUS ucfg_p2p_psoc_close(struct wlan_objmgr_psoc *soc) +{ + return p2p_psoc_object_close(soc); +} + +QDF_STATUS ucfg_p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req) +{ + return p2p_psoc_start(soc, req); +} + +QDF_STATUS ucfg_p2p_psoc_stop(struct wlan_objmgr_psoc *soc) +{ + return p2p_psoc_stop(soc); +} + +QDF_STATUS ucfg_p2p_roc_req(struct wlan_objmgr_psoc *soc, + struct p2p_roc_req *roc_req, uint64_t *cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *roc_ctx; + QDF_STATUS status; + int32_t id; + + p2p_debug("soc:%pK, vdev_id:%d, chan:%d, phy_mode:%d, duration:%d", + soc, roc_req->vdev_id, roc_req->chan, + roc_req->phy_mode, roc_req->duration); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("P2P soc object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + roc_ctx = qdf_mem_malloc(sizeof(*roc_ctx)); + if (!roc_ctx) { + p2p_err("failed to allocate p2p roc context"); + return QDF_STATUS_E_NOMEM; + } + + status = qdf_idr_alloc(&p2p_soc_obj->p2p_idr, roc_ctx, &id); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(roc_ctx); + p2p_err("failed to alloc idr, status %d", status); + return status; + } + + *cookie = (uint64_t)id; + roc_ctx->p2p_soc_obj = p2p_soc_obj; + roc_ctx->vdev_id = roc_req->vdev_id; + roc_ctx->chan = roc_req->chan; + roc_ctx->phy_mode = roc_req->phy_mode; + roc_ctx->duration = roc_req->duration; + roc_ctx->roc_state = ROC_STATE_IDLE; + roc_ctx->roc_type = USER_REQUESTED; + roc_ctx->id = id; + msg.type = P2P_ROC_REQ; + msg.bodyptr = roc_ctx; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(roc_ctx); + qdf_idr_remove(&p2p_soc_obj->p2p_idr, id); + p2p_err("post msg fail:%d", status); + } + p2p_debug("cookie = 0x%llx", *cookie); + + return status; +} + +QDF_STATUS ucfg_p2p_roc_cancel_req(struct wlan_objmgr_psoc *soc, + uint64_t cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct cancel_roc_context *cancel_roc; + void *roc_ctx = NULL; + QDF_STATUS status; + + p2p_debug("soc:%pK, cookie:0x%llx", soc, cookie); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_idr_find(&p2p_soc_obj->p2p_idr, + cookie, &roc_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + p2p_debug("invalid id for cookie 0x%llx", cookie); + return QDF_STATUS_E_INVAL; + } + + cancel_roc = qdf_mem_malloc(sizeof(*cancel_roc)); + if (!cancel_roc) + return QDF_STATUS_E_NOMEM; + + + cancel_roc->p2p_soc_obj = p2p_soc_obj; + cancel_roc->cookie = (uintptr_t)roc_ctx; + msg.type = P2P_CANCEL_ROC_REQ; + msg.bodyptr = cancel_roc; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(cancel_roc); + p2p_err("post msg fail:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_cleanup_roc_by_vdev(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct wlan_objmgr_psoc *psoc; + + p2p_debug("vdev:%pK", vdev); + + if (!vdev) { + p2p_debug("null vdev"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return p2p_cleanup_roc_sync(p2p_soc_obj, vdev); +} + +QDF_STATUS ucfg_p2p_cleanup_roc_by_psoc(struct wlan_objmgr_psoc *psoc) +{ + struct p2p_soc_priv_obj *obj; + + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_P2P); + if (!obj) { + p2p_err("null p2p soc obj"); + return QDF_STATUS_E_FAILURE; + } + + return p2p_cleanup_roc_sync(obj, NULL); +} + +QDF_STATUS ucfg_p2p_cleanup_tx_by_vdev(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *obj; + struct wlan_objmgr_psoc *psoc; + + if (!vdev) { + p2p_debug("null vdev"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_P2P); + if (!obj) { + p2p_err("null p2p soc obj"); + return QDF_STATUS_E_FAILURE; + } + p2p_del_all_rand_mac_vdev(vdev); + + return p2p_cleanup_tx_sync(obj, vdev); +} + +QDF_STATUS ucfg_p2p_cleanup_tx_by_psoc(struct wlan_objmgr_psoc *psoc) +{ + struct p2p_soc_priv_obj *obj; + + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_P2P); + if (!obj) { + p2p_err("null p2p soc obj"); + return QDF_STATUS_E_FAILURE; + } + p2p_del_all_rand_mac_soc(psoc); + + return p2p_cleanup_tx_sync(obj, NULL); +} + +QDF_STATUS ucfg_p2p_mgmt_tx(struct wlan_objmgr_psoc *soc, + struct p2p_mgmt_tx *mgmt_frm, uint64_t *cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct tx_action_context *tx_action; + QDF_STATUS status; + int32_t id; + + p2p_debug("soc:%pK, vdev_id:%d, chan:%d, wait:%d, buf_len:%d, cck:%d, no ack:%d, off chan:%d", + soc, mgmt_frm->vdev_id, mgmt_frm->chan, + mgmt_frm->wait, mgmt_frm->len, mgmt_frm->no_cck, + mgmt_frm->dont_wait_for_ack, mgmt_frm->off_chan); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + tx_action = qdf_mem_malloc(sizeof(*tx_action)); + if (!tx_action) { + p2p_err("Failed to allocate tx action context"); + return QDF_STATUS_E_NOMEM; + } + + /* return cookie just for ota ack frames */ + if (mgmt_frm->dont_wait_for_ack) + id = 0; + else { + status = qdf_idr_alloc(&p2p_soc_obj->p2p_idr, + tx_action, &id); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(tx_action); + p2p_err("failed to alloc idr, status :%d", status); + return status; + } + } + + *cookie = (uint64_t)id; + tx_action->p2p_soc_obj = p2p_soc_obj; + tx_action->vdev_id = mgmt_frm->vdev_id; + tx_action->chan = mgmt_frm->chan; + tx_action->duration = mgmt_frm->wait; + tx_action->buf_len = mgmt_frm->len; + tx_action->no_cck = mgmt_frm->no_cck; + tx_action->no_ack = mgmt_frm->dont_wait_for_ack; + tx_action->off_chan = mgmt_frm->off_chan; + tx_action->buf = qdf_mem_malloc(tx_action->buf_len); + if (!(tx_action->buf)) { + p2p_err("Failed to allocate buffer for action frame"); + qdf_mem_free(tx_action); + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(tx_action->buf, mgmt_frm->buf, tx_action->buf_len); + tx_action->nbuf = NULL; + tx_action->id = id; + + p2p_rand_mac_tx(tx_action); + + msg.type = P2P_MGMT_TX; + msg.bodyptr = tx_action; + msg.callback = p2p_process_cmd; + msg.flush_callback = p2p_msg_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + if (id) + qdf_idr_remove(&p2p_soc_obj->p2p_idr, id); + qdf_mem_free(tx_action->buf); + qdf_mem_free(tx_action); + p2p_err("post msg fail:%d", status); + } + + p2p_debug("cookie = 0x%llx", *cookie); + + return status; +} + +QDF_STATUS ucfg_p2p_mgmt_tx_cancel(struct wlan_objmgr_psoc *soc, + struct wlan_objmgr_vdev *vdev, uint64_t cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct cancel_roc_context *cancel_tx; + void *tx_ctx; + QDF_STATUS status; + + p2p_debug("soc:%pK, cookie:0x%llx", soc, cookie); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_idr_find(&p2p_soc_obj->p2p_idr, + (int32_t)cookie, &tx_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + p2p_debug("invalid id for cookie 0x%llx", cookie); + return QDF_STATUS_E_INVAL; + } + p2p_del_random_mac(soc, wlan_vdev_get_id(vdev), cookie); + + cancel_tx = qdf_mem_malloc(sizeof(*cancel_tx)); + if (!cancel_tx) { + p2p_err("Failed to allocate cancel p2p roc"); + return QDF_STATUS_E_NOMEM; + } + + cancel_tx->p2p_soc_obj = p2p_soc_obj; + cancel_tx->cookie = (uintptr_t)tx_ctx; + msg.type = P2P_MGMT_TX_CANCEL; + msg.bodyptr = cancel_tx; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(cancel_tx); + p2p_err("post msg fail: %d", status); + } + + return status; +} + +bool ucfg_p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr) +{ + return p2p_check_random_mac(soc, vdev_id, random_mac_addr); +} + +QDF_STATUS ucfg_p2p_set_ps(struct wlan_objmgr_psoc *soc, + struct p2p_ps_config *ps_config) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint16_t obj_id; + struct wlan_objmgr_vdev *vdev; + struct p2p_ps_config go_ps_config; + + p2p_debug("soc:%pK, vdev_id:%d, opp_ps:%d, ct_window:%d, count:%d, duration:%d, duration:%d, ps_selection:%d", + soc, ps_config->vdev_id, ps_config->opp_ps, + ps_config->ct_window, ps_config->count, + ps_config->duration, ps_config->single_noa_duration, + ps_config->ps_selection); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + for (obj_id = 0; obj_id < WLAN_UMAC_PSOC_MAX_VDEVS; obj_id++) { + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, obj_id, + WLAN_P2P_ID); + if (vdev) { + if (is_p2p_ps_allowed(vdev, WLAN_UMAC_COMP_P2P)) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + break; + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("skip p2p set ps vdev %d, NoA is disabled as legacy STA is connected to GO.", + obj_id); + } + } + if (obj_id >= WLAN_UMAC_PSOC_MAX_VDEVS) { + p2p_debug("No GO found!"); + return QDF_STATUS_E_INVAL; + } + go_ps_config = *ps_config; + go_ps_config.vdev_id = obj_id; + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->set_ps) { + status = p2p_ops->set_ps(soc, &go_ps_config); + p2p_debug("p2p set ps vdev %d, status:%d", obj_id, status); + } + + return status; +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +QDF_STATUS ucfg_p2p_lo_start(struct wlan_objmgr_psoc *soc, + struct p2p_lo_start *p2p_lo_start) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_debug("soc:%pK, vdev_id:%d, ctl_flags:%d, freq:%d, period:%d, interval:%d, count:%d, dev_types_len:%d, probe_resp_len:%d, device_types:%pK, probe_resp_tmplt:%pK", + soc, p2p_lo_start->vdev_id, p2p_lo_start->ctl_flags, + p2p_lo_start->freq, p2p_lo_start->period, + p2p_lo_start->interval, p2p_lo_start->count, + p2p_lo_start->dev_types_len, p2p_lo_start->probe_resp_len, + p2p_lo_start->device_types, p2p_lo_start->probe_resp_tmplt); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->lo_start) { + status = p2p_ops->lo_start(soc, p2p_lo_start); + p2p_debug("p2p lo start, status:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_lo_stop(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_debug("soc:%pK, vdev_id:%d", soc, vdev_id); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->lo_stop) { + status = p2p_ops->lo_stop(soc, vdev_id); + p2p_debug("p2p lo stop, status:%d", status); + } + + return status; +} +#endif + +QDF_STATUS ucfg_p2p_set_noa(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id, bool disable_noa) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_INVAL; + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->set_noa) { + status = p2p_ops->set_noa(soc, vdev_id, disable_noa); + p2p_debug("p2p set noa, status:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_register_callbacks(struct wlan_objmgr_psoc *soc, + struct p2p_protocol_callbacks *cb_obj) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!soc || !cb_obj) { + p2p_err("psoc: %pK or cb_obj: %pK context passed is NULL", + soc, cb_obj); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + p2p_soc_obj->p2p_cb = *cb_obj; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_p2p_status_scan(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_scan(vdev); +} + +QDF_STATUS ucfg_p2p_status_connect(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_connect(vdev); +} + +QDF_STATUS ucfg_p2p_status_disconnect(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_disconnect(vdev); +} + +QDF_STATUS ucfg_p2p_status_start_bss(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_start_bss(vdev); +} + +QDF_STATUS ucfg_p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_stop_bss(vdev); +} diff --git a/drivers/staging/qcacld-3.0/components/pkt_capture/core/inc/wlan_pkt_capture_main.h b/drivers/staging/qcacld-3.0/components/pkt_capture/core/inc/wlan_pkt_capture_main.h new file mode 100644 index 0000000000000000000000000000000000000000..cf92a568940ad4056d2dc4e3be21e2a345946c06 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pkt_capture/core/inc/wlan_pkt_capture_main.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare private API which shall be used internally only + * in pkt_capture component. This file shall include prototypes of + * various notification handlers and logging functions. + * + * Note: This API should be never accessed out of pkt_capture component. + */ + +#ifndef _WLAN_PKT_CAPTURE_MAIN_H_ +#define _WLAN_PKT_CAPTURE_MAIN_H_ + +#include +#include "wlan_pkt_capture_priv.h" +#include "wlan_pkt_capture_objmgr.h" + +#define pkt_capture_log(level, args...) \ + QDF_TRACE(QDF_MODULE_ID_PKT_CAPTURE, level, ## args) + +#define pkt_capture_logfl(level, format, args...) \ + pkt_capture_log(level, FL(format), ## args) + +#define pkt_capture_fatal(format, args...) \ + pkt_capture_logfl(QDF_TRACE_LEVEL_FATAL, format, ## args) +#define pkt_capture_err(format, args...) \ + pkt_capture_logfl(QDF_TRACE_LEVEL_ERROR, format, ## args) +#define pkt_capture_warn(format, args...) \ + pkt_capture_logfl(QDF_TRACE_LEVEL_WARN, format, ## args) +#define pkt_capture_info(format, args...) \ + pkt_capture_logfl(QDF_TRACE_LEVEL_INFO, format, ## args) +#define pkt_capture_debug(format, args...) \ + pkt_capture_logfl(QDF_TRACE_LEVEL_DEBUG, format, ## args) + +#define PKT_CAPTURE_ENTER() pkt_capture_debug("enter") +#define PKT_CAPTURE_EXIT() pkt_capture_debug("exit") + +/** + * pkt_capture_vdev_create_notification() - Handler for vdev create notify. + * @vdev: vdev which is going to be created by objmgr + * @arg: argument for notification handler. + * + * Allocate and attach vdev private object. + * + * Return: QDF_STATUS + */ +QDF_STATUS +pkt_capture_vdev_create_notification(struct wlan_objmgr_vdev *vdev, void *arg); + +/** + * pkt_capture_vdev_destroy_notification() - Handler for vdev destroy notify. + * @vdev: vdev which is going to be destroyed by objmgr + * @arg: argument for notification handler. + * + * Deallocate and detach vdev private object. + * + * Return: QDF_STATUS + */ +QDF_STATUS +pkt_capture_vdev_destroy_notification(struct wlan_objmgr_vdev *vdev, void *arg); + +/** + * pkt_capture_get_mode() - get packet capture mode + * @psoc: pointer to psoc object + * + * Return: enum pkt_capture_mode + */ +enum pkt_capture_mode pkt_capture_get_mode(struct wlan_objmgr_psoc *psoc); + +/** + * pkt_capture_psoc_create_notification() - Handler for psoc create notify. + * @psoc: psoc which is going to be created by objmgr + * @arg: argument for notification handler. + * + * Allocate and attach psoc private object. + * + * Return: QDF_STATUS + */ +QDF_STATUS +pkt_capture_psoc_create_notification(struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * pkt_capture_psoc_destroy_notification() - Handler for psoc destroy notify. + * @psoc: psoc which is going to be destroyed by objmgr + * @arg: argument for notification handler. + * + * Deallocate and detach psoc private object. + * + * Return: QDF_STATUS + */ +QDF_STATUS +pkt_capture_psoc_destroy_notification(struct wlan_objmgr_psoc *psoc, void *arg); +#endif /* end of _WLAN_PKT_CAPTURE_MAIN_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pkt_capture/core/inc/wlan_pkt_capture_objmgr.h b/drivers/staging/qcacld-3.0/components/pkt_capture/core/inc/wlan_pkt_capture_objmgr.h new file mode 100644 index 0000000000000000000000000000000000000000..4da27aed72f29258a7c1c993f82d3e08f9f6b532 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pkt_capture/core/inc/wlan_pkt_capture_objmgr.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains various object manager related wrappers and helpers + */ + +#ifndef _WLAN_PKT_CAPTURE_OBJMGR_H +#define _WLAN_PKT_CAPTURE_OBJMGR_H + +#include "wlan_cmn.h" +#include "wlan_objmgr_cmn.h" +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_objmgr_global_obj.h" + +/** + * pkt_capture_vdev_get_ref() - Wrapper to increment pkt_capture ref count + * @vdev: vdev object + * + * Wrapper for pkt_capture to increment ref count after checking valid + * object state. + * + * Return: SUCCESS/FAILURE + */ +static inline +QDF_STATUS pkt_capture_vdev_get_ref(struct wlan_objmgr_vdev *vdev) +{ + return wlan_objmgr_vdev_try_get_ref(vdev, WLAN_PKT_CAPTURE_ID); +} + +/** + * pkt_capture_vdev_put_ref() - Wrapper to decrement pkt_capture ref count + * @vdev: vdev object + * + * Wrapper for pkt_capture to decrement ref count of vdev. + * + * Return: SUCCESS/FAILURE + */ +static inline +void pkt_capture_vdev_put_ref(struct wlan_objmgr_vdev *vdev) +{ + wlan_objmgr_vdev_release_ref(vdev, WLAN_PKT_CAPTURE_ID); +} + +/** + * pkt_capture_vdev_get_priv() - Wrapper to retrieve vdev priv obj + * @vdev: vdev pointer + * + * Wrapper for pkt_capture to get vdev private object pointer. + * + * Return: Private object of vdev + */ +static inline struct pkt_capture_vdev_priv * +pkt_capture_vdev_get_priv(struct wlan_objmgr_vdev *vdev) +{ + struct pkt_capture_vdev_priv *vdev_priv; + + vdev_priv = wlan_objmgr_vdev_get_comp_private_obj( + vdev, + WLAN_UMAC_COMP_PKT_CAPTURE); + QDF_BUG(vdev_priv); + + return vdev_priv; +} + +/** + * pkt_capture_psoc_get_ref() - Wrapper to increment pkt_capture ref count + * @psoc: psoc object + * + * Wrapper for pkt_capture to increment ref count after checking valid + * object state. + * + * Return: QDF_STATUS + */ +static inline +QDF_STATUS pkt_capture_psoc_get_ref(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_try_get_ref(psoc, WLAN_PKT_CAPTURE_ID); +} + +/** + * pkt_capture_psoc_put_ref() - Wrapper to decrement pkt_capture ref count + * @psoc: psoc object + * + * Wrapper for pkt_capture to decrement ref count of psoc. + * + * Return: None + */ +static inline +void pkt_capture_psoc_put_ref(struct wlan_objmgr_psoc *psoc) +{ + wlan_objmgr_psoc_release_ref(psoc, WLAN_PKT_CAPTURE_ID); +} + +/** + * pkt_capture_psoc_get_priv(): Wrapper to retrieve psoc priv obj + * @psoc: psoc pointer + * + * Wrapper for pkt_capture to get psoc private object pointer. + * + * Return: pkt_capture psoc private object + */ +static inline struct pkt_psoc_priv * +pkt_capture_psoc_get_priv(struct wlan_objmgr_psoc *psoc) +{ + struct pkt_psoc_priv *psoc_priv; + + psoc_priv = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_PKT_CAPTURE); + QDF_BUG(psoc_priv); + + return psoc_priv; +} +#endif /* _WLAN_PKT_CAPTURE_OBJMGR_H */ diff --git a/drivers/staging/qcacld-3.0/components/pkt_capture/core/inc/wlan_pkt_capture_priv.h b/drivers/staging/qcacld-3.0/components/pkt_capture/core/inc/wlan_pkt_capture_priv.h new file mode 100644 index 0000000000000000000000000000000000000000..a83b4f5557633e271693f2de72d8a4de5a68e4cd --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pkt_capture/core/inc/wlan_pkt_capture_priv.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare private API which shall be used internally only + * in pkt_capture component. This file shall include prototypes of + * pkt_capture parsing and send logic. + * + * Note: This API should be never accessed out of pkt_capture component. + */ + +#ifndef _WLAN_PKT_CAPTURE_PRIV_STRUCT_H_ +#define _WLAN_PKT_CAPTURE_PRIV_STRUCT_H_ + +#include "wlan_pkt_capture_objmgr.h" +#include "wlan_pkt_capture_public_structs.h" + +/** + * struct pkt_capture_cfg - packet capture cfg to store ini values + * @pkt_capture_mode: packet capture mode + */ +struct pkt_capture_cfg { + enum pkt_capture_mode pkt_capture_mode; +}; + +/** + * struct pkt_capture_vdev_priv - Private object to be stored in vdev + * @vdev: pointer to vdev object + */ +struct pkt_capture_vdev_priv { + struct wlan_objmgr_vdev *vdev; +}; + +/** + * struct pkt_psoc_priv - Private object to be stored in psoc + * @psoc: pointer to psoc object + * @cfg_param: INI config params for packet capture + */ +struct pkt_psoc_priv { + struct wlan_objmgr_psoc *psoc; + struct pkt_capture_cfg cfg_param; +}; +#endif /* End of _WLAN_PKT_CAPTURE_PRIV_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pkt_capture/core/src/wlan_pkt_capture_main.c b/drivers/staging/qcacld-3.0/components/pkt_capture/core/src/wlan_pkt_capture_main.c new file mode 100644 index 0000000000000000000000000000000000000000..d64934664f20889b5ecbb9ebe42b1f3876af496e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pkt_capture/core/src/wlan_pkt_capture_main.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implement various notification handlers which are accessed + * internally in pkt_capture component only. + */ + +#include "wlan_pkt_capture_main.h" +#include "cfg_ucfg_api.h" + +enum pkt_capture_mode pkt_capture_get_mode(struct wlan_objmgr_psoc *psoc) +{ + struct pkt_psoc_priv *psoc_priv; + + if (!psoc) { + pkt_capture_err("psoc is NULL"); + return PACKET_CAPTURE_MODE_DISABLE; + } + + psoc_priv = pkt_capture_psoc_get_priv(psoc); + if (!psoc_priv) { + pkt_capture_err("psoc_priv is NULL"); + return PACKET_CAPTURE_MODE_DISABLE; + } + + return psoc_priv->cfg_param.pkt_capture_mode; +} + +/** + * pkt_capture_cfg_init() - Initialize packet capture cfg ini params + * @psoc_priv: psoc private object + * + * Return: None + */ +static void +pkt_capture_cfg_init(struct pkt_psoc_priv *psoc_priv) +{ + struct pkt_capture_cfg *cfg_param; + + cfg_param = &psoc_priv->cfg_param; + + cfg_param->pkt_capture_mode = cfg_get(psoc_priv->psoc, + CFG_PKT_CAPTURE_MODE); +} + +QDF_STATUS +pkt_capture_vdev_create_notification(struct wlan_objmgr_vdev *vdev, void *arg) +{ + struct pkt_capture_vdev_priv *vdev_priv; + QDF_STATUS status; + + vdev_priv = qdf_mem_malloc(sizeof(*vdev_priv)); + if (!vdev_priv) + return QDF_STATUS_E_NOMEM; + + status = wlan_objmgr_vdev_component_obj_attach( + vdev, + WLAN_UMAC_COMP_PKT_CAPTURE, + vdev_priv, QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + pkt_capture_err("Failed to attach vdev component obj"); + goto free_vdev_priv; + } + + vdev_priv->vdev = vdev; + return status; + +free_vdev_priv: + qdf_mem_free(vdev_priv); + return status; +} + +QDF_STATUS +pkt_capture_vdev_destroy_notification(struct wlan_objmgr_vdev *vdev, void *arg) +{ + struct pkt_capture_vdev_priv *vdev_priv; + QDF_STATUS status; + + vdev_priv = pkt_capture_vdev_get_priv(vdev); + if (!vdev_priv) { + pkt_capture_err("vdev priv is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_vdev_component_obj_detach( + vdev, + WLAN_UMAC_COMP_PKT_CAPTURE, + vdev_priv); + if (QDF_IS_STATUS_ERROR(status)) + pkt_capture_err("Failed to detach vdev component obj"); + + qdf_mem_free(vdev_priv); + return status; +} + +QDF_STATUS +pkt_capture_psoc_create_notification(struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct pkt_psoc_priv *psoc_priv; + QDF_STATUS status; + + psoc_priv = qdf_mem_malloc(sizeof(*psoc_priv)); + if (!psoc_priv) + return QDF_STATUS_E_NOMEM; + + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_PKT_CAPTURE, + psoc_priv, QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + pkt_capture_err("Failed to attach psoc component obj"); + goto free_psoc_priv; + } + + psoc_priv->psoc = psoc; + pkt_capture_cfg_init(psoc_priv); + + return status; + +free_psoc_priv: + qdf_mem_free(psoc_priv); + return status; +} + +QDF_STATUS +pkt_capture_psoc_destroy_notification(struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct pkt_psoc_priv *psoc_priv; + QDF_STATUS status; + + psoc_priv = pkt_capture_psoc_get_priv(psoc); + if (!psoc_priv) { + pkt_capture_err("psoc priv is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_PKT_CAPTURE, + psoc_priv); + if (QDF_IS_STATUS_ERROR(status)) { + pkt_capture_err("Failed to detach psoc component obj"); + return status; + } + + qdf_mem_free(psoc_priv); + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/inc/cfg_pkt_capture.h b/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/inc/cfg_pkt_capture.h new file mode 100644 index 0000000000000000000000000000000000000000..08a2f116b6939b6358bd9a6e5f2164483ad35324 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/inc/cfg_pkt_capture.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CFG_PKT_CAPTURE_H_ +#define _CFG_PKT_CAPTURE_H_ + +#ifdef WLAN_FEATURE_PKT_CAPTURE + +#define CFG_PKT_CAPTURE_MODE_DEFAULT (0) +#define CFG_PKT_CAPTURE_MODE_MGMT_PKT BIT(0) +#define CFG_PKT_CAPTURE_MODE_DATA_PKT BIT(1) +#define CFG_PKT_CAPTURE_MODE_MAX (CFG_PKT_CAPTURE_MODE_MGMT_PKT | \ + CFG_PKT_CAPTURE_MODE_DATA_PKT) + +/* + * + * packet_capture_mode - Packet capture mode + * @Min: 0 + * @Max: 3 + * Default: 0 - Capture no packets + * + * This ini is used to decide packet capture mode + * + * packet_capture_mode = 0 - Capture no packets + * packet_capture_mode = 1 - Capture management packets only + * packet_capture_mode = 2 - Capture data packets only + * packet_capture_mode = 3 - Capture both data and management packets + * + * Supported Feature: packet capture + * + * Usage: External + * + * + */ +#define CFG_PKT_CAPTURE_MODE \ + CFG_INI_UINT("packet_capture_mode", \ + 0, \ + CFG_PKT_CAPTURE_MODE_MAX, \ + CFG_PKT_CAPTURE_MODE_DEFAULT, \ + CFG_VALUE_OR_DEFAULT, \ + "Value for packet capture mode") + +#define CFG_PKT_CAPTURE_MODE_ALL \ + CFG(CFG_PKT_CAPTURE_MODE) +#else +#define CFG_PKT_CAPTURE_MODE_ALL +#endif /* WLAN_FEATURE_PKT_CAPTURE */ +#endif /* _CFG_PKT_CAPTURE_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/inc/wlan_pkt_capture_public_structs.h b/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/inc/wlan_pkt_capture_public_structs.h new file mode 100644 index 0000000000000000000000000000000000000000..86f286c7bd4375a187efc34815f513f767e749c0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/inc/wlan_pkt_capture_public_structs.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_PKT_CAPTURE_PUBLIC_STRUCTS_H_ +#define _WLAN_PKT_CAPTURE_PUBLIC_STRUCTS_H_ + +/** + * enum pkt_capture_mode - packet capture modes + * @PACKET_CAPTURE_MODE_DISABLE: packet capture mode disable + * @PACKET_CAPTURE_MODE_MGMT_ONLY: capture mgmt packets only + * @PACKET_CAPTURE_MODE_DATA_ONLY: capture data packets only + * @PACKET_CAPTURE_MODE_DATA_MGMT: capture both data and mgmt packets + */ +enum pkt_capture_mode { + PACKET_CAPTURE_MODE_DISABLE = 0, + PACKET_CAPTURE_MODE_MGMT_ONLY, + PACKET_CAPTURE_MODE_DATA_ONLY, + PACKET_CAPTURE_MODE_DATA_MGMT, +}; + +#endif /* _WLAN_PKT_CAPTURE_PUBLIC_STRUCTS_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/inc/wlan_pkt_capture_ucfg_api.h b/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/inc/wlan_pkt_capture_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..bc12c61e66face9d4a09049ad1127183515d3ddf --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/inc/wlan_pkt_capture_ucfg_api.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare public API related to the pkt_capture called by north bound + * HDD/OSIF/LIM + */ + +#ifndef _WLAN_PKT_CAPTURE_UCFG_API_H_ +#define _WLAN_PKT_CAPTURE_UCFG_API_H_ + +#include +#include +#include "wlan_pkt_capture_objmgr.h" +#include "wlan_pkt_capture_public_structs.h" + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * ucfg_pkt_capture_init() - Packet capture component initialization. + * + * This function gets called when packet capture initializing. + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_pkt_capture_init(void); + +/** + * ucfg_pkt_capture_deinit() - Packet capture component de-init. + * + * This function gets called when packet capture de-init. + * + * Return: None + */ +void ucfg_pkt_capture_deinit(void); + +/** + * ucfg_pkt_capture_get_mode() - get packet capture mode + * @psoc: pointer to psoc object + * + * Return: enum pkt_capture_mode + */ +enum pkt_capture_mode ucfg_pkt_capture_get_mode(struct wlan_objmgr_psoc *psoc); +#else +static inline +QDF_STATUS ucfg_pkt_capture_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void ucfg_pkt_capture_deinit(void) +{ +} + +static inline +enum pkt_capture_mode ucfg_pkt_capture_get_mode(struct wlan_objmgr_psoc *psoc) +{ + return PACKET_CAPTURE_MODE_DISABLE; +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ +#endif /* _WLAN_PKT_CAPTURE_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/src/wlan_pkt_capture_ucfg_api.c b/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/src/wlan_pkt_capture_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..e16e09dbde0abb11b94fab1ced4b02946bde67d3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pkt_capture/dispatcher/src/wlan_pkt_capture_ucfg_api.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Public API implementation of pkt_capture called by north bound HDD/OSIF. + */ + +#include "wlan_pkt_capture_objmgr.h" +#include "wlan_pkt_capture_main.h" +#include "wlan_pkt_capture_ucfg_api.h" + +enum pkt_capture_mode ucfg_pkt_capture_get_mode(struct wlan_objmgr_psoc *psoc) +{ + return pkt_capture_get_mode(psoc); +} + +QDF_STATUS ucfg_pkt_capture_init(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_psoc_create_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + pkt_capture_err("Failed to register psoc create handler"); + return status; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_psoc_destroy_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) { + pkt_capture_err("Failed to register psoc delete handler"); + goto fail_destroy_psoc; + } + + status = wlan_objmgr_register_vdev_create_handler( + WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_vdev_create_notification, NULL); + if (QDF_IS_STATUS_ERROR(status)) { + pkt_capture_err("Failed to register vdev create handler"); + goto fail_create_vdev; + } + + status = wlan_objmgr_register_vdev_destroy_handler( + WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_vdev_destroy_notification, NULL); + if (QDF_IS_STATUS_ERROR(status)) { + pkt_capture_err("Failed to register vdev destroy handler"); + goto fail_destroy_vdev; + } + return status; + +fail_destroy_vdev: + wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_vdev_create_notification, NULL); + +fail_create_vdev: + wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_psoc_destroy_notification, NULL); + +fail_destroy_psoc: + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_psoc_create_notification, NULL); + + return status; +} + +void ucfg_pkt_capture_deinit(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_unregister_vdev_destroy_handler( + WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_vdev_destroy_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) + pkt_capture_err("Failed to unregister vdev destroy handler"); + + status = wlan_objmgr_unregister_vdev_create_handler( + WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_vdev_create_notification, NULL); + if (QDF_IS_STATUS_ERROR(status)) + pkt_capture_err("Failed to unregister vdev create handler"); + + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_psoc_destroy_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) + pkt_capture_err("Failed to unregister psoc destroy handler"); + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_PKT_CAPTURE, + pkt_capture_psoc_create_notification, + NULL); + if (QDF_IS_STATUS_ERROR(status)) + pkt_capture_err("Failed to unregister psoc create handler"); +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_apf.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_apf.h new file mode 100644 index 0000000000000000000000000000000000000000..64dfa341d922fe686b6a5a1405f35971699766ed --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_apf.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Android Packet Filter (APF) headers for PMO + */ + +#ifndef __WLAN_PMO_APF_H +#define __WLAN_PMO_APF_H + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "qdf_types.h" +#include "wlan_objmgr_psoc_obj.h" + +/** + * pmo_get_apf_instruction_size() - get the current APF instruction size + * @psoc: the psoc to query + * + * Return: APF instruction size + */ +uint32_t pmo_get_apf_instruction_size(struct wlan_objmgr_psoc *psoc); + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* __WLAN_PMO_APF_H */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_arp.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_arp.h new file mode 100644 index 0000000000000000000000000000000000000000..f5035b4fcc92fa573a0a44c456771bf6c31036f9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_arp.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare arp offload feature API's + */ + +#ifndef _WLAN_PMO_ARP_H_ +#define _WLAN_PMO_ARP_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_arp_public_struct.h" + +/** + * pmo_core_cache_arp_offload_req() - API to cache arp req in pmo vdev priv ctx + * @arp_req: arp offload request + * + * API To cache ARP offload in pmo vdev priv ctx + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_cache_arp_offload_req(struct pmo_arp_req *arp_req); + +/** + * pmo_core_arp_check_offload(): API to check if arp offload cache/send is req + * @psoc: objmgr psoc handle + * @trigger: trigger reason + * @vdev_id: vdev id + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_arp_check_offload(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id); + +/** + * pmo_core_flush_arp_offload_req() - API to flush arp req from pmo vdev ctx + * @vdev: objmgr vdev + * + * API To flush saved ARP request from pmo vdev prov ctx + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_flush_arp_offload_req(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_enable_arp_offload_in_fwr() - API to enable arp offload in fwr + * @vdev: objmgr vdev + * @trigger: trigger reason + * + * API to enable arp offload in fwr from vdev priv ctx + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_enable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger); + +/** + * pmo_core_disable_arp_offload_in_fwr() - API to disable arp offload in fwr + * @vdev: objmgr vdev + * @trigger: trigger reason + * + * API to disable arp offload in fwr + * + * Return: QQDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_disable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger); + +/** + * pmo_core_get_arp_offload_params() - API to get arp offload params + * @vdev: objmgr vdev + * @params: output pointer to hold offload params + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS +pmo_core_get_arp_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *params); + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_ARP_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_gtk.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_gtk.h new file mode 100644 index 0000000000000000000000000000000000000000..44774872e9de4412665944314356844086d52c65 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_gtk.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare gtk offload feature API's + */ + +#ifndef _WLAN_PMO_GTK_H_ +#define _WLAN_PMO_GTK_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_gtk_public_struct.h" + +/** + * pmo_core_cache_gtk_offload_req(): API to cache gtk req in pmo vdev priv obj + * @vdev: objmgr vdev handle + * @gtk_req: pmo gtk req param + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_cache_gtk_offload_req(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_req); + +/** + * pmo_core_flush_gtk_offload_req(): Flush saved gtk req from pmo vdev priv obj + * @vdev: objmgr vdev handle + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_flush_gtk_offload_req(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_enable_gtk_offload_in_fwr(): enable cached gtk request in fwr + * @vdev: objmgr vdev handle + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_enable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_disable_gtk_offload_in_fwr(): disable cached gtk request in fwr + * @vdev: objmgr vdev handle + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_disable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_get_gtk_rsp(): API to send gtk response request to fwr + * @vdev: objmgr vdev handle + * @gtk_rsp: pmo gtk response request + * + * This api will send gtk response request to fwr + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_get_gtk_rsp(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_rsp_req *gtk_rsp_req); + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_GTK_H_ */ + diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_hw_filter.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_hw_filter.h new file mode 100644 index 0000000000000000000000000000000000000000..6739cd0cf0de6ab847618242cea9c30504773aae --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_hw_filter.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare hardware filter offload feature APIs + */ + +#ifndef _WLAN_PMO_HW_FILTER_H_ +#define _WLAN_PMO_HW_FILTER_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "qdf_status.h" +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_pmo_hw_filter_public_struct.h" + +/** + * pmo_core_enable_hw_filter_in_fwr() - enable previously configured hw filter + * @vdev: objmgr vdev to configure + * + * Return: QDF_STATUS + */ +QDF_STATUS pmo_core_enable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_disable_hw_filter_in_fwr() - disable previously configured hw filter + * @vdev: objmgr vdev to configure + * + * Return: QDF_STATUS + */ +QDF_STATUS pmo_core_disable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev); + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* _WLAN_PMO_HW_FILTER_H_*/ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_lphb.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_lphb.h new file mode 100644 index 0000000000000000000000000000000000000000..00cacccb9fd4d7942b227a157a5efb0e259fab05 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_lphb.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare low power heart beat offload feature API's + */ + +#ifndef _WLAN_PMO_LPHB_H_ +#define _WLAN_PMO_LPHB_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_lphb_public_struct.h" + +/** + * pmo_core_lphb_config_req() - API to configure lphb request + * @psoc: objmgr psoc handle + * @lphb_req: low power heart beat configuration request + * @lphb_cb_ctx: low power heart beat context + * @callback: osif callback which need to be called when host get lphb event + * + * API to configure lphb request + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_lphb_config_req(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_req *lphb_req, void *lphb_cb_ctx, + pmo_lphb_callback callback); + +/** + * pmo_core_apply_lphb(): apply cached LPHB settings + * @psoc: objmgr psoc handle + * + * LPHB cache, if any item was enabled, should be + * applied. + */ +void pmo_core_apply_lphb(struct wlan_objmgr_psoc *psoc); + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_LPHB_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_main.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_main.h new file mode 100644 index 0000000000000000000000000000000000000000..fea451cfb9720ed19356bd7a5b4c5b1c48fcbb5a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_main.h @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare various api which shall be used by + * pmo user configuration and target interface + */ + +#ifndef _WLAN_PMO_MAIN_H_ +#define _WLAN_PMO_MAIN_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_priv.h" +#include "wlan_pmo_objmgr.h" + +#define pmo_fatal(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_PMO, params) +#define pmo_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_PMO, params) +#define pmo_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_PMO, params) +#define pmo_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_PMO, params) +#define pmo_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_PMO, params) + +#define pmo_nofl_fatal(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_PMO, params) +#define pmo_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_PMO, params) +#define pmo_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_PMO, params) +#define pmo_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_PMO, params) +#define pmo_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_PMO, params) + +#define pmo_enter() QDF_TRACE_ENTER(QDF_MODULE_ID_PMO, "enter") +#define pmo_exit() QDF_TRACE_EXIT(QDF_MODULE_ID_PMO, "exit") + +#define PMO_VDEV_IN_STA_MODE(mode) \ + ((mode) == QDF_STA_MODE || (mode) == QDF_P2P_CLIENT_MODE ? 1 : 0) + +static inline enum QDF_OPMODE pmo_get_vdev_opmode(struct wlan_objmgr_vdev *vdev) +{ + return wlan_vdev_mlme_get_opmode(vdev); +} + +/** + * pmo_allocate_ctx() - Api to allocate pmo ctx + * + * Helper function to allocate pmo ctx + * + * Return: Success or failure. + */ +QDF_STATUS pmo_allocate_ctx(void); + +/** + * pmo_free_ctx() - to free pmo context + * + * Helper function to free pmo context + * + * Return: None. + */ +void pmo_free_ctx(void); + +/** + * pmo_get_context() - to get pmo context + * + * Helper function to get pmo context + * + * Return: pmo context. + */ +struct wlan_pmo_ctx *pmo_get_context(void); + +/** + * pmo_psoc_open() - pmo psoc object open + * @psoc: objmgr vdev + *. + * This function used to open pmo psoc object + * + * Return: Success or failure + */ +QDF_STATUS pmo_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_psoc_close() - pmo psoc object close + * @psoc: objmgr vdev + *. + * This function used to close pmo psoc object + * + * Return: Success or failure + */ +QDF_STATUS pmo_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_get_vdev_bss_peer_mac_addr() - API to get bss peer mac address + * @vdev: objmgr vdev + * @bss_peer_mac_address: bss peer mac address + *. + * Helper function to get bss peer mac address + * + * Return: if success pmo vdev ctx else NULL + */ +QDF_STATUS pmo_get_vdev_bss_peer_mac_addr(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *bss_peer_mac_address); + +/** + * pmo_is_vdev_in_beaconning_mode() - check if vdev is in a beaconning mode + * @vdev_opmode: vdev opmode + * + * Helper function to know whether given vdev + * is in a beaconning mode or not. + * + * Return: True if vdev needs to beacon. + */ +bool pmo_is_vdev_in_beaconning_mode(enum QDF_OPMODE vdev_opmode); + +/** + * pmo_core_is_ap_mode_supports_arp_ns() - To check ap mode supports arp/ns + * @vdev_opmode: vdev opmode + * + * API to check if ap mode supports arp/ns offload + * + * Return: True if ap mode supports arp/ns offload + */ + +bool pmo_core_is_ap_mode_supports_arp_ns(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE vdev_opmode); + +/** + * pmo_core_is_vdev_supports_offload() - Check offload is supported on vdev + * @vdev: objmgr vdev + * + * Return: true in case success else false + */ +bool pmo_core_is_vdev_supports_offload(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_get_psoc_config(): API to get the psoc user configurations of pmo + * @psoc: objmgr psoc handle + * @psoc_cfg: fill the current psoc user configurations. + * + * Return pmo psoc configurations + */ +QDF_STATUS pmo_core_get_psoc_config(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg); + +/** + * pmo_core_update_psoc_config(): API to update the psoc user configurations + * @psoc: objmgr psoc handle + * @psoc_cfg: pmo psoc configurations + * + * This api shall be used for soc config initialization as well update. + * In case of update caller must first call pmo_get_psoc_cfg to get + * current config and then apply changes on top of current config. + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS pmo_core_update_psoc_config(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg); + +/** + * pmo_psoc_set_caps() - overwrite configured device capability flags + * @psoc: the psoc for which the capabilities apply + * @caps: the cabability information to configure + * + * Return: None + */ +void pmo_psoc_set_caps(struct wlan_objmgr_psoc *psoc, + struct pmo_device_caps *caps); + +/** + * pmo_core_get_vdev_op_mode(): API to get the vdev operation mode + * @vdev: objmgr vdev handle + * + * API to get the vdev operation mode + * + * Return QDF_MAX_NO_OF_MODE - in case of error else return vdev opmode + */ +static inline enum QDF_OPMODE pmo_core_get_vdev_op_mode( + struct wlan_objmgr_vdev *vdev) +{ + enum QDF_OPMODE op_mode = QDF_MAX_NO_OF_MODE; + + if (!vdev) + return op_mode; + op_mode = wlan_vdev_mlme_get_opmode(vdev); + + return op_mode; +} + +/** + * pmo_core_psoc_update_dp_handle() - update psoc data path handle + * @psoc: objmgr psoc handle + * @dp_hdl: psoc data path handle + * + * Return: None + */ +static inline void +pmo_core_psoc_update_dp_handle(struct wlan_objmgr_psoc *psoc, void *dp_hdl) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->dp_hdl = dp_hdl; + } +} + +/** + * pmo_core_psoc_get_dp_handle() - Get psoc data path handle + * @psoc: objmgr psoc handle + * + * Return: psoc data path handle + */ +static inline void *pmo_core_psoc_get_dp_handle(struct wlan_objmgr_psoc *psoc) +{ + void *dp_hdl = NULL; + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + dp_hdl = psoc_ctx->dp_hdl; + } + + return dp_hdl; +} + +/** + * pmo_core_vdev_get_dp_handle() - Get vdev data path handle + * @psoc_ctx: pmo psoc private context + * @vdev_id: vdev id config to get data path handle + * + * Return: Vdev data path handle + */ +static inline +struct cdp_vdev *pmo_core_vdev_get_dp_handle(struct pmo_psoc_priv_obj *psoc_ctx, + uint8_t vdev_id) +{ + struct cdp_vdev *dp_hdl = NULL; + pmo_get_vdev_dp_handle handler; + + qdf_spin_lock_bh(&psoc_ctx->lock); + handler = psoc_ctx->get_vdev_dp_handle; + qdf_spin_unlock_bh(&psoc_ctx->lock); + + if (handler) + dp_hdl = handler(vdev_id); + + return dp_hdl; +} + +/** + * pmo_core_psoc_update_htc_handle() - update psoc htc layer handle + * @psoc: objmgr psoc handle + * @htc_hdl: psoc htc layer handle + * + * Return: None + */ +static inline void +pmo_core_psoc_update_htc_handle(struct wlan_objmgr_psoc *psoc, void *htc_hdl) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->htc_hdl = htc_hdl; + } +} + +/** + * pmo_core_psoc_get_htc_handle() - Get psoc htc layer handle + * @psoc: objmgr psoc handle + * + * Return: psoc htc layer handle + */ +static inline void *pmo_core_psoc_get_htc_handle(struct wlan_objmgr_psoc *psoc) +{ + void *htc_hdl = NULL; + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + htc_hdl = psoc_ctx->htc_hdl; + } + + return htc_hdl; +} + +/** + * pmo_core_psoc_set_hif_handle() - update psoc hif layer handle + * @psoc: objmgr psoc handle + * @hif_hdl: hif context handle + * + * Return: None + */ +void pmo_core_psoc_set_hif_handle(struct wlan_objmgr_psoc *psoc, + void *hif_hdl); + +/** + * pmo_core_psoc_get_hif_handle() - Get psoc hif layer handle + * @psoc: objmgr psoc handle + * + * Return: psoc hif layer handle + */ +void *pmo_core_psoc_get_hif_handle(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_core_psoc_set_txrx_pdev_id() - update psoc pdev txrx layer handle + * @psoc: objmgr psoc handle + * @txrx_pdev_id: txrx pdev identifier + * + * Return: None + */ +void pmo_core_psoc_set_txrx_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t txrx_pdev_id); + +/** + * pmo_core_psoc_get_txrx_handle() - Get psoc pdev txrx handle + * @psoc: objmgr psoc handle + * + * Return: txrx pdev identifier + */ +uint8_t pmo_core_psoc_get_txrx_handle(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_intersect_arp_ns_offload() - intersect config and firmware capability for + * the ARP/NS Offload feature + * @psoc_ctx: A PMO psoc context + * + * Note: The caller is expected to grab the PMO context lock. + * + * Return: True if firmware supports and configuration has enabled the feature + */ +static inline bool +pmo_intersect_arp_ns_offload(struct pmo_psoc_priv_obj *psoc_ctx) +{ + struct pmo_psoc_cfg *cfg = &psoc_ctx->psoc_cfg; + bool arp_ns_enabled = + cfg->ns_offload_enable_static || + cfg->ns_offload_enable_dynamic || + cfg->arp_offload_enable; + + return arp_ns_enabled && psoc_ctx->caps.arp_ns_offload; +} + +/** + * pmo_intersect_apf() - intersect config and firmware capability for + * the APF feature + * @psoc_ctx: A PMO psoc context + * + * Note: The caller is expected to grab the PMO context lock. + * + * Return: True if firmware supports and configuration has enabled the feature + */ +static inline bool pmo_intersect_apf(struct pmo_psoc_priv_obj *psoc_ctx) +{ + return psoc_ctx->psoc_cfg.apf_enable && psoc_ctx->caps.apf; +} + +/** + * pmo_intersect_packet_filter() - intersect config and firmware capability for + * the APF feature + * @psoc_ctx: A PMO psoc context + * + * Note: The caller is expected to grab the PMO context lock. + * + * Return: True if firmware supports and configuration has enabled the feature + */ +static inline bool +pmo_intersect_packet_filter(struct pmo_psoc_priv_obj *psoc_ctx) +{ + return psoc_ctx->psoc_cfg.packet_filter_enabled && + psoc_ctx->caps.packet_filter; +} + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_MAIN_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_mc_addr_filtering.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_mc_addr_filtering.h new file mode 100644 index 0000000000000000000000000000000000000000..fd0cca9fba4489a15cbe091a5cf34a4ce1d721f1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_mc_addr_filtering.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare mc addr filtering offload feature API's + */ + +#ifndef _WLAN_PMO_MC_ADDR_FILTERING_H_ +#define _WLAN_PMO_MC_ADDR_FILTERING_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_mc_addr_filtering_public_struct.h" + +/** + * pmo_core_set_mc_filter_req() -send mc filter set request + * @vdev: objmgr vdev + * @mc_list: a list of mc addresses to set in fwr + * + * Return: QDF_STATUS_SUCCESS in success else error codes + */ +QDF_STATUS pmo_core_set_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list); + +/** + * pmo_clear_mc_filter_req() -send mc filter clear request + * @vdev: objmgr vdev + * @mc_list: a list of mc addresses to clear in fwr + * + * Return: QDF_STATUS_SUCCESS in success else error codes + */ +QDF_STATUS pmo_core_clear_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list); + +/** + * pmo_core_cache_mc_addr_list(): API to cache mc addr list in pmo vdev priv obj + * @psoc: objmgr psoc handle + * @vdev_id: vdev id + * @gtk_req: pmo gtk req param + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_cache_mc_addr_list( + struct pmo_mc_addr_list_params *mc_list_config); + +/** + * pmo_core_flush_mc_addr_list(): API to flush mc addr list in pmo vdev priv obj + * @psoc: objmgr psoc handle + * @vdev_id: vdev id + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_flush_mc_addr_list(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * pmo_core_enhance_mc_filter_enable() - enable enhanced multicast filtering + * @vdev: the vdev to enable enhanced multicast filtering for + * + * Return: QDF_STATUS + */ +QDF_STATUS pmo_core_enhanced_mc_filter_enable(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_enhance_mc_filter_disable() - disable enhanced multicast filtering + * @vdev: the vdev to disable enhanced multicast filtering for + * + * Return: QDF_STATUS + */ +QDF_STATUS pmo_core_enhanced_mc_filter_disable(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_enable_mc_addr_filtering_in_fwr(): Enable cached mc add list in fwr + * @psoc: objmgr psoc handle + * @vdev_id: vdev id + * @gtk_req: pmo gtk req param + * @action: true for enable els false + * + * API to enable cached mc add list in fwr + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_enable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger); + +/** + * pmo_core_disable_mc_addr_filtering_in_fwr(): Disable cached mc addr list + * @psoc: objmgr psoc handle + * @vdev_id: vdev id + * @gtk_req: pmo gtk req param + * @action: true for enable els false + * + * API to disable cached mc add list in fwr + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS pmo_core_disable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger); + +/** + * pmo_core_get_mc_addr_list_count() -set mc address count + * @psoc: objmgr psoc + * @vdev_id: vdev id + * + * Return: set mc address count + */ +void pmo_core_set_mc_addr_list_count(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t count); + +/** + * pmo_core_get_mc_addr_list_count() -get current mc address count + * @psoc: objmgr psoc + * @vdev_id: vdev id + * + * Return: current mc address count + */ +int pmo_core_get_mc_addr_list_count(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * pmo_core_max_mc_addr_supported() -get max supported mc addresses + * @psoc: objmgr psoc + * + * Return: max supported mc addresses + */ +uint8_t pmo_core_max_mc_addr_supported(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_core_get_mc_addr_list() - Get mc addr list configured + * @psoc: objmgr psoc + * @vdev_id: vdev identifier + * @mc_list_req: output pointer to hold mc addr list params + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS +pmo_core_get_mc_addr_list(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + struct pmo_mc_addr_list *mc_list_req); + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_MC_ADDR_FILTERING_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_ns.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_ns.h new file mode 100644 index 0000000000000000000000000000000000000000..c53d4c67bf20f1223427c60646fbe3c2bc190849 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_ns.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare ns offload feature API's + */ + +#ifndef _WLAN_PMO_NS_H_ +#define _WLAN_PMO_NS_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_ns_public_struct.h" + +/** + * pmo_core_cache_ns_offload_req() - API to cache ns req in pmo vdev priv ctx + * @ns_req: ns offload request + * + * API to cache ns offload in pmo vdev priv ctx + * + * Return:QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_cache_ns_offload_req(struct pmo_ns_req *ns_req); + +/** + * pmo_core_ns_check_offload() - API to check if offload cache/send is required + * @psoc: objmgr psoc handle + * @trigger: trigger reason enable ns offload + * @vdev_id: vdev id + * + * Return:QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_ns_check_offload(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id); + +/** + * pmo_core_flush_ns_offload_req() - API to flush ns req from pmo vdev priv ctx + * @vdev: vdev objmgr handle + * + * API to flush ns offload from pmo vdev priv ctx + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_flush_ns_offload_req(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_enable_ns_offload_in_fwr() - API to enable ns offload in fwr + * @vdev: objmgr vdev + * @trigger: trigger reason enable ns offload + * + * API to enable ns offload in fwr from vdev priv ctx + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_enable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger); + +/** + * pmo_core_disable_ns_offload_in_fwr() - API to disable ns offload in fwr + * @vdev: objmgr vdev + * @trigger: trigger reason disable ns offload + * + * API to disable arp offload in fwr + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_disable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger); + +/** + * pmo_core_get_ns_offload_params() - API to get ns offload params + * @vdev: objmgr vdev + * @params: output pointer to hold offload params + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS +pmo_core_get_ns_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_ns_offload_params *params); + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_NS_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_objmgr.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_objmgr.h new file mode 100644 index 0000000000000000000000000000000000000000..44600253e9a863a27ad0be11fb23eff3a7393d4d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_objmgr.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains various object manager related wrappers and helpers + */ + +#ifndef _WLAN_PMO_OBJMGR_H +#define _WLAN_PMO_OBJMGR_H + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_cmn.h" +#include "wlan_objmgr_cmn.h" +#include "wlan_objmgr_peer_obj.h" +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_utility.h" + + +/* Get/Put Ref */ + +#define pmo_peer_get_ref(peer) wlan_objmgr_peer_try_get_ref(peer, WLAN_PMO_ID) +#define pmo_peer_put_ref(peer) wlan_objmgr_peer_release_ref(peer, WLAN_PMO_ID) + +#define pmo_vdev_get_ref(vdev) wlan_objmgr_vdev_try_get_ref(vdev, WLAN_PMO_ID) +#define pmo_vdev_put_ref(vdev) wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID) + +#define pmo_pdev_get_ref(pdev) wlan_objmgr_pdev_try_get_ref(pdev, WLAN_PMO_ID) +#define pmo_pdev_put_ref(pdev) wlan_objmgr_pdev_release_ref(pdev, WLAN_PMO_ID) + +#define pmo_psoc_get_ref(psoc) wlan_objmgr_psoc_try_get_ref(psoc, WLAN_PMO_ID) +#define pmo_psoc_put_ref(psoc) wlan_objmgr_psoc_release_ref(psoc, WLAN_PMO_ID) + +/* Private Data */ + +#define pmo_vdev_get_priv_nolock(vdev) \ + wlan_objmgr_vdev_get_comp_private_obj(vdev, WLAN_UMAC_COMP_PMO) +#define pmo_psoc_get_priv_nolock(psoc) \ + wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_PMO) + +/* Ids */ + +static inline uint8_t +pmo_vdev_get_id(struct wlan_objmgr_vdev *vdev) +{ + uint8_t vdev_id; + + vdev_id = wlan_vdev_get_id(vdev); + QDF_BUG(vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS); + + return vdev_id; +} + +/* Tree Navigation */ + +/** + * !PLEASE READ! + * + * The following are objmgr naviation helpers for traversing objmgr object + * trees. + * + * Objmgr ensures parents of an objmgr object cannot be freed while a valid + * reference to one of its children is held. Based on this fact, all of these + * navigation helpers make the following assumptions to ensure safe usage: + * + * 1) The caller must hold a valid reference to the input objmgr object! + * E.g. Use pmo_[peer|vdev|pdev|psoc]_get_ref() on the input objmgr object + * before using these APIs + * 2) Given assumption #1, the caller does not need to hold a reference to the + * parents of the input objmgr object + * 3) Given assumption #1, parents of the input objmgr object cannot be null + * 4) Given assumption #1, private contexts of any parent of the input objmgr + * object cannot be null + * + * These characteristics remove the need for most sanity checks when dealing + * with objmgr objects. However, please note that if you ever walk the tree + * from parent to child, references must be acquired all the way down! + * + * Example #1: + * + * psoc = pmo_vdev_get_psoc(vdev); + * if (!psoc) + * // this is dead code + * + * Example #2: + * + * psoc_priv = pmo_psoc_get_priv(psoc); + * if (!psoci_priv) + * // this is dead code + * + * Example #3: + * + * status = pmo_vdev_get_ref(vdev); + * + * ... + * + * psoc = pmo_vdev_get_psoc(vdev); + * + * // the next line is redundant, don't do it! + * status = pmo_psoc_get_ref(psoc); + */ + +/* Tree Navigation: psoc */ + +static inline struct wlan_objmgr_vdev * +pmo_psoc_get_vdev(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + + if (vdev_id >= WLAN_UMAC_PSOC_MAX_VDEVS) + return NULL; + + wlan_psoc_obj_lock(psoc); + vdev = psoc->soc_objmgr.wlan_vdev_list[vdev_id]; + wlan_psoc_obj_unlock(psoc); + + return vdev; +} + +static inline struct pmo_psoc_priv_obj * +pmo_psoc_get_priv(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_priv; + + psoc_priv = pmo_psoc_get_priv_nolock(psoc); + QDF_BUG(psoc_priv); + + return psoc_priv; +} + +static inline bool __pmo_spinlock_bh_safe(struct pmo_psoc_priv_obj *psoc_ctx) +{ + if (!psoc_ctx) + return false; + + qdf_spin_lock_bh(&psoc_ctx->lock); + + return true; +} + +#define pmo_psoc_with_ctx(psoc, cursor) \ + for (cursor = pmo_psoc_get_priv(psoc); \ + __pmo_spinlock_bh_safe(cursor); \ + qdf_spin_unlock_bh(&cursor->lock), cursor = NULL) + +/* Tree Navigation: pdev */ + +static inline struct wlan_objmgr_psoc * +pmo_pdev_get_psoc(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_objmgr_psoc *psoc; + + psoc = wlan_pdev_get_psoc(pdev); + QDF_BUG(psoc); + + return psoc; +} + +static inline struct pmo_psoc_priv_obj * +pmo_pdev_get_psoc_priv(struct wlan_objmgr_pdev *pdev) +{ + return pmo_psoc_get_priv(pmo_pdev_get_psoc(pdev)); +} + +/* Tree Navigation: vdev */ + +static inline struct pmo_vdev_priv_obj * +pmo_vdev_get_priv(struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_priv; + + vdev_priv = pmo_vdev_get_priv_nolock(vdev); + QDF_BUG(vdev_priv); + + return vdev_priv; +} + +static inline struct wlan_objmgr_pdev * +pmo_vdev_get_pdev(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_pdev *pdev; + + pdev = wlan_vdev_get_pdev(vdev); + QDF_BUG(pdev); + + return pdev; +} + +static inline struct wlan_objmgr_psoc * +pmo_vdev_get_psoc(struct wlan_objmgr_vdev *vdev) +{ + return pmo_pdev_get_psoc(pmo_vdev_get_pdev(vdev)); +} + +static inline struct pmo_psoc_priv_obj * +pmo_vdev_get_psoc_priv(struct wlan_objmgr_vdev *vdev) +{ + return pmo_psoc_get_priv(pmo_pdev_get_psoc(pmo_vdev_get_pdev(vdev))); +} + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* _WLAN_PMO_OBJMGR_H */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_pkt_filter.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_pkt_filter.h new file mode 100644 index 0000000000000000000000000000000000000000..306412290916196b6beb46dc3d68259b292945f7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_pkt_filter.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare packet filter feature API's + */ + +#ifndef _WLAN_PMO_PKT_FILTER_H_ +#define _WLAN_PMO_PKT_FILTER_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_pkt_filter_public_struct.h" + +struct wlan_objmgr_psoc; + +/** + * pmo_get_num_packet_filters() - get the number of packet filters + * @psoc: the psoc to query + * + * Return: number of packet filters + */ +uint32_t pmo_get_num_packet_filters(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_set_pkt_fltr_req() - Set packet filter + * @psoc: objmgr psoc + * @pmo_set_pkt_fltr_req: packet filter set param + * @vdev_id: vdev id + * + * API to set packet filter + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_set_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req, + uint8_t vdev_id); + +/** + * pmo_core_clear_pkt_filter() - Clear packet filter + * @psoc: objmgr psoc + * @pmo_clr_pkt_fltr_param: packet filter clear param + * @vdev_id: vdev id + * + * API to clear packet filter + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_core_clear_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_clear_param *pmo_clr_pkt_fltr_param, + uint8_t vdev_id); + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* _WLAN_PMO_PKT_FILTER_H_ */ + diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_priv.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_priv.h new file mode 100644 index 0000000000000000000000000000000000000000..1bbf04d24c1f51788328639fbec45fd3b02992e2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_priv.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + /** + * DOC: Declare various struct, macros which are used for private to PMO. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_PMO_PRIV_STRUCT_H_ +#define _WLAN_PMO_PRIV_STRUCT_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_arp_public_struct.h" +#include "wlan_pmo_ns_public_struct.h" +#include "wlan_pmo_gtk_public_struct.h" +#include "wlan_pmo_wow_public_struct.h" +#include "wlan_pmo_mc_addr_filtering_public_struct.h" + +/** + * struct pmo_psoc_priv_obj - psoc related data require for pmo + * @psoc_cfg: place holder for psoc configuration + * @pmo_tx_ops: transmit ops for PMO + * @wow: wow configuration + * @caps: PMO specific device capability bits + * @dp_hdl: psoc data path handle + * @htc_hdl: htc layer handle + * @hif_hdl: hif layer handle + * @txrx_pdev_id: txrx pdev identifier + * @pause_bitmap_notifier: registered callback to update pause bitmap value + * @pmo_get_pause_bitmap: registered callback to get pause bitmap value + * @get_dtim_period: register callback to get dtim period from mlme + * @get_beacon_interval: register callback to get beacon interval from mlme + * @lock: spin lock for pmo psoc + */ +struct pmo_psoc_priv_obj { + struct pmo_psoc_cfg psoc_cfg; + struct wlan_pmo_tx_ops pmo_tx_ops; + struct pmo_wow wow; + struct pmo_device_caps caps; + void *dp_hdl; + void *htc_hdl; + void *hif_hdl; + uint8_t txrx_pdev_id; + pmo_notify_pause_bitmap pause_bitmap_notifier; + pmo_get_pause_bitmap get_pause_bitmap; + pmo_get_vdev_dp_handle get_vdev_dp_handle; + pmo_is_device_in_low_pwr_mode is_device_in_low_pwr_mode; + pmo_get_dtim_period get_dtim_period; + pmo_get_beacon_interval get_beacon_interval; + qdf_spinlock_t lock; +}; + +/** + * struct wlan_pmo_ctx -offload mgr context + * @psoc_context: psoc context + * @pmo_suspend_handler: suspend handler table for all componenets + * @pmo_suspend_handler_arg: suspend handler argument sfor all componenets + * @pmo_resume_handler: resume handler table for all componenets + * @pmo_resume_handler_arg: resume handler argument for all componenets + * @lock: lock for global pmo ctx + */ +struct wlan_pmo_ctx { + pmo_psoc_suspend_handler + pmo_suspend_handler[WLAN_UMAC_MAX_COMPONENTS]; + void *pmo_suspend_handler_arg[WLAN_UMAC_MAX_COMPONENTS]; + pmo_psoc_resume_handler + pmo_resume_handler[WLAN_UMAC_MAX_COMPONENTS]; + void *pmo_resume_handler_arg[WLAN_UMAC_MAX_COMPONENTS]; + qdf_spinlock_t lock; +}; + +/** + * struct pmo_vdev_priv_obj -vdev specific user configuration required for pmo + * @pmo_psoc_ctx: pmo psoc ctx + * @vdev_arp_req: place holder for arp request for vdev + * @vdev_ns_req: place holder for ns request for vdev + * @vdev_mc_list_req: place holder for mc addr list for vdev + * @addr_filter_pattern: addr filter pattern for vdev + * @vdev_gtk_params: place holder for gtk request for vdev + * @gtk_err_enable: gtk error is enabled or not + * @vdev_bpf_req: place holder for apf/bpf for vdev + * @vdev_pkt_filter: place holder for vdev packet filter + * @ptrn_match_enable: true when pattern match is enabled else false + * @num_wow_default_patterns: number of wow default patterns for vdev + * @num_wow_user_patterns: number of user wow patterns for vdev + * @extscan_in_progress: true when extscan in progress else false + * @p2plo_in_progress: true when p2plo_in_progress in progress else false + * @dtim_period: dtim period for vdev + * @beacon_interval: vdev beacon interval + * @dyn_modulated_dtim: dynamically configured modulated dtim value + * @dyn_modulated_dtim_enabled: if dynamically modulated dtim is set or not + * @dyn_listen_interval: dynamically user configured listen interval + * @restore_dtim_setting: DTIM settings restore flag + * @pmo_vdev_lock: spin lock for pmo vdev priv ctx + */ +struct pmo_vdev_priv_obj { + struct pmo_psoc_priv_obj *pmo_psoc_ctx; + struct pmo_arp_offload_params vdev_arp_req; + struct pmo_ns_offload_params vdev_ns_req; + struct pmo_mc_addr_list vdev_mc_list_req; + uint8_t addr_filter_pattern; + struct pmo_gtk_req vdev_gtk_req; + struct pmo_gtk_rsp_req vdev_gtk_rsp_req; + qdf_atomic_t gtk_err_enable; + bool ptrn_match_enable; + uint8_t num_wow_default_patterns; + uint8_t num_wow_user_patterns; + bool extscan_in_progress; + bool p2plo_in_progress; + uint8_t dtim_period; + uint8_t beacon_interval; + uint32_t dyn_modulated_dtim; + bool dyn_modulated_dtim_enabled; + uint32_t dyn_listen_interval; + bool restore_dtim_setting; + qdf_spinlock_t pmo_vdev_lock; +}; + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ +#endif /* end of _WLAN_PMO_PRIV_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_static_config.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_static_config.h new file mode 100644 index 0000000000000000000000000000000000000000..fed6b472d8f292a2e011080e6f1fa06aab5c8fae --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_static_config.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare static configuration on vdev attach + */ + +#ifndef _WLAN_PMO_STATIC_CONFIG_H_ +#define _WLAN_PMO_STATIC_CONFIG_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_wow.h" + +/** + * pmo_register_wow_wakeup_events() - register vdev specific wake events with fw + * @vdev: objmgr vdev + * + * WoW wake up event rule is following: + * 1) STA mode and P2P CLI mode wake up events are same + * 2) SAP mode and P2P GO mode wake up events are same + * 3) IBSS mode wake events are same as STA mode plus WOW_BEACON_EVENT + * + * Return: none + */ +void pmo_register_wow_wakeup_events(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_register_wow_default_patterns() - register default wow patterns with fw + * @vdev_id: vdev id + * + * WoW default wake up pattern rule is: + * - For STA & P2P CLI mode register for same STA specific wow patterns + * - For SAP/P2P GO & IBSS mode register for same SAP specific wow patterns + * + * Return: none + */ +void pmo_register_wow_default_patterns(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_register_action_frame_patterns() - register action frame map to fw + * @vdev: objmgr vdev + * @suspend_type: suspend mode runtime pm suspend or normal suspend. + * + * This is called to push action frames wow patterns from local + * cache to firmware. + * + * Return: QDF_STATUS + */ +QDF_STATUS +pmo_register_action_frame_patterns(struct wlan_objmgr_vdev *vdev, + enum qdf_suspend_type suspend_type); + +/** + * pmo_clear_action_frame_patterns() - clear the action frame + * pattern bitmap in firmware + * @vdev: objmgr vdev + * + * Return: QDF_STATUS + */ +QDF_STATUS pmo_clear_action_frame_patterns(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_set_wow_event_bitmap() - Assign bitmask with wow event + * @event: wow event + * @wow_bitmap_size: wow bitmask size + * @bitmask: wow bitmask field + * + * Return: none + */ +void pmo_set_wow_event_bitmap(WOW_WAKE_EVENT_TYPE event, + uint32_t wow_bitmap_size, + uint32_t *bitmask); + +/** + * pmo_set_sta_wow_bitmask() - set predefined STA wow wakeup events + * @bitmask: bitmask field + * @wow_bitmask_size: bitmask field size + * + * Return: none + */ +void pmo_set_sta_wow_bitmask(uint32_t *bitmask, uint32_t wow_bitmask_size); + +/** + * pmo_set_sap_wow_bitmask() - set predefined SAP wow wakeup events + * @bitmask: bitmask field + * @wow_bitmask_size: bitmask field size + * + * Return: none + */ +void pmo_set_sap_wow_bitmask(uint32_t *bitmask, uint32_t wow_bitmask_size); + +#ifdef WLAN_FEATURE_NAN +/** + * pmo_set_ndp_wow_bitmask() - set predefined NDP wow wakeup events + * @bitmask: bitmask field + * @wow_bitmask_size: bitmask field size + * + * Return: none + */ +void pmo_set_ndp_wow_bitmask(uint32_t *bitmask, uint32_t wow_bitmask_size); +#else +static inline +void pmo_set_ndp_wow_bitmask(uint32_t *bitmask, uint32_t wow_bitmask_size) +{ +} +#endif + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_STATIC_CONFIG_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_suspend_resume.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_suspend_resume.h new file mode 100644 index 0000000000000000000000000000000000000000..78325d9f7257b1b98c2fd82e4bfdeca917736d07 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_suspend_resume.h @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare suspend / resume related API's + */ + +#ifndef _WLAN_PMO_SUSPEND_RESUME_H_ +#define _WLAN_PMO_SUSPEND_RESUME_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_wow.h" + +/** + * pmo_core_configure_dynamic_wake_events(): configure dyanmic wake events + * @wma: wma handle + * + * Some wake events need to be enabled dynamically. Control those here. + * + * Return: none + */ +void pmo_core_configure_dynamic_wake_events(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_core_get_wow_bus_suspend(): API to get wow bus is suspended or not + * @psoc: objmgr psoc handle + * + * Return: True if bus suspende else false + */ +static inline bool pmo_core_get_wow_bus_suspend(struct wlan_objmgr_psoc *psoc) +{ + bool value = false; + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + value = psoc_ctx->wow.is_wow_bus_suspended; + } + + return value; +} + +/** + * pmo_core_psoc_user_space_suspend_req() - Core handle user space suspend req + * @psoc: objmgr psoc handle + * @type: type of suspend + * + * Pmo core Handles user space suspend request for psoc + * + * Return: QDF status + */ +QDF_STATUS pmo_core_psoc_user_space_suspend_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type); + +/** + * pmo_core_psoc_user_space_resume_req() - Core handle user space resume req + * @psoc: objmgr psoc handle + * @type: type of suspend from resume required + * + * Pmo core Handles user space resume request for psoc + * + * Return: QDF status + */ +QDF_STATUS pmo_core_psoc_user_space_resume_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type); + +/** + * pmo_core_psoc_bus_suspend_req(): handles bus suspend for psoc + * @psoc: objmgr psoc + * @type: is this suspend part of runtime suspend or system suspend? + * @wow_params: collection of wow enable override parameters + * + * Bails if a scan is in progress. + * Calls the appropriate handlers based on configuration and event. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS pmo_core_psoc_bus_suspend_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type, + struct pmo_wow_enable_params *wow_params); + +#ifdef FEATURE_RUNTIME_PM +/** + * pmo_core_psoc_bus_runtime_suspend(): handles bus runtime suspend + * @psoc: objmgr psoc + * @pld_cb: callback to do link auto suspend + * + * Suspend the wlan bus without apps suspend. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS pmo_core_psoc_bus_runtime_suspend(struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_suspend_cb pld_cb); + +/** + * pmo_core_psoc_bus_runtime_resume(): handles bus runtime resume + * @psoc: objmgr psoc + * @pld_cb: callback to do link auto resume + * + * Resume the wlan bus from runtime suspend. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS pmo_core_psoc_bus_runtime_resume(struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_resume_cb pld_cb); +#endif + +/** + * pmo_core_psoc_suspend_target() -Send suspend target command + * @psoc: objmgr psoc handle + * @disable_target_intr: disable target interrupt + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS pmo_core_psoc_suspend_target(struct wlan_objmgr_psoc *psoc, + int disable_target_intr); + +/** + * pmo_core_psoc_bus_resume() -handle bus resume request for psoc + * @psoc: objmgr psoc handle + * @type: is this suspend part of runtime suspend or system suspend? + * + * Return:QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS pmo_core_psoc_bus_resume_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type); + +/** + * pmo_core_vdev_set_restore_dtim() - vdev dtim restore setting value + * @vdev: objmgr vdev handle + * @value: dtim restore policy value + * + * Return: None + */ +static inline +void pmo_core_vdev_set_restore_dtim(struct wlan_objmgr_vdev *vdev, + bool value) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->restore_dtim_setting = value; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); +} + +/** + * pmo_core_vdev_get_restore_dtim() - Get vdev restore dtim setting + * @vdev: objmgr vdev handle + * + * Return: dtim restore policy + */ +static inline +bool pmo_core_vdev_get_restore_dtim(struct wlan_objmgr_vdev *vdev) +{ + bool value; + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + value = vdev_ctx->restore_dtim_setting; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + return value; +} + +/** + * pmo_core_update_power_save_mode() - update power save mode + * @vdev: objmgr vdev handle + * @value:describe vdev power save mode + * + * Return: None + */ +static inline void +pmo_core_psoc_update_power_save_mode(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->psoc_cfg.power_save_mode = value; + } +} + +/** + * pmo_core_psoc_get_power_save_mode() - Get psoc power save mode + * @psoc: objmgr psoc handle + * + * Return: vdev psoc power save mode value + */ +static inline uint8_t +pmo_core_psoc_get_power_save_mode(struct wlan_objmgr_psoc *psoc) +{ + uint8_t value = 0; + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + value = psoc_ctx->psoc_cfg.power_save_mode; + } + + return value; +} + +/** + * pmo_core_vdev_get_pause_bitmap() - Get vdev pause bitmap + * @psoc_ctx: psoc priv ctx + * @vdev_id: vdev id + * + * Return: vdev pause bitmap + */ +static inline +uint16_t pmo_core_vdev_get_pause_bitmap(struct pmo_psoc_priv_obj *psoc_ctx, + uint8_t vdev_id) +{ + uint16_t value = 0; + pmo_get_pause_bitmap handler; + + qdf_spin_lock_bh(&psoc_ctx->lock); + handler = psoc_ctx->get_pause_bitmap; + qdf_spin_unlock_bh(&psoc_ctx->lock); + + if (handler) + value = handler(vdev_id); + + return value; +} + +/** + * wma_is_vdev_in_ap_mode() - check that vdev is in ap mode or not + * @wma: wma handle + * @vdev_id: vdev id + * + * Helper function to know whether given vdev id + * is in AP mode or not. + * + * Return: True/False + */ +static inline +bool pmo_is_vdev_in_ap_mode(struct wlan_objmgr_vdev *vdev) +{ + enum QDF_OPMODE mode; + + mode = pmo_get_vdev_opmode(vdev); + + return (mode == QDF_SAP_MODE || mode == QDF_P2P_GO_MODE) == 1 ? 1 : 0; +} + +#ifdef QCA_IBSS_SUPPORT +/** + * pmo_is_vdev_in_ibss_mode() - check that vdev is in ibss mode or not + * @vdev: objmgr vdev handle + * @vdev_id: vdev id + * + * Helper function to know whether given vdev id + * is in IBSS mode or not. + * + * Return: True/False + */ +static inline +bool pmo_is_vdev_in_ibss_mode(struct wlan_objmgr_vdev *vdev) +{ + enum QDF_OPMODE mode; + + mode = pmo_get_vdev_opmode(vdev); + + return (mode == QDF_IBSS_MODE) ? true : false; +} +#else +static inline bool pmo_is_vdev_in_ibss_mode(struct wlan_objmgr_vdev *vdev) +{ + return false; +} +#endif /* QCA_IBSS_SUPPORT */ + +/** + * pmo_handle_initial_wake_up() - handle initial wake up + * @cb_ctx: callback context + * + * Return: None + */ +void pmo_core_psoc_handle_initial_wake_up(void *cb_ctx); + +/** + * pmo_core_psoc_is_target_wake_up_received() - check for initial wake up + * + * Check if target initial wake up is received and fail PM suspend gracefully + * + * Return: -EAGAIN if initial wake up is received else 0 + */ +int pmo_core_psoc_is_target_wake_up_received(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_core_psoc_clear_target_wake_up() - clear initial wake up + * + * Clear target initial wake up reason + * + * Return: 0 for success and negative error code for failure + */ +int pmo_core_psoc_clear_target_wake_up(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_core_psoc_target_suspend_acknowledge() - update target susspend status + * @context: HTC_INIT_INFO->context + * @wow_nack: true when wow is rejected + * + * Return: none + */ +void pmo_core_psoc_target_suspend_acknowledge(void *context, bool wow_nack); + +/** + * pmo_core_psoc_wakeup_host_event_received() - received host wake up event + * @psoc: objmgr psoc handle + * + * Return: None + */ +void pmo_core_psoc_wakeup_host_event_received(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_core_config_listen_interval() - function to dynamically configure + * listen interval + * @vdev: objmgr vdev + * @listen_interval: new listen interval passed by user + * + * This function allows user to configure listen interval dynamically + * + * Return: QDF_STATUS + */ +QDF_STATUS pmo_core_config_listen_interval(struct wlan_objmgr_vdev *vdev, + uint32_t listen_interval); + +/** + * pmo_core_config_modulated_dtim() - function to configure modulated dtim + * @vdev: objmgr vdev handle + * @mod_dtim: New modulated dtim value passed by user + * + * This function configures the modulated dtim in firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS pmo_core_config_modulated_dtim(struct wlan_objmgr_vdev *vdev, + uint32_t mod_dtim); + +#ifdef SYSTEM_PM_CHECK +/** + * pmo_core_system_resume() - function to handle system resume notification + * @psoc: objmgr psoc handle + * + * Return: None + */ +void pmo_core_system_resume(struct wlan_objmgr_psoc *psoc); +#else +static inline void pmo_core_system_resume(struct wlan_objmgr_psoc *psoc) +{ +} +#endif +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_SUSPEND_RESUME_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_wow.h b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_wow.h new file mode 100644 index 0000000000000000000000000000000000000000..1927dcb872afffeb1e91e40f6b3738f61b580758 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/inc/wlan_pmo_wow.h @@ -0,0 +1,670 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare API's for wow pattern addition and deletion in fwr + */ + +#ifndef _WLAN_PMO_WOW_H_ +#define _WLAN_PMO_WOW_H_ + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +#include "wlan_pmo_main.h" +#include "wlan_pmo_wow_public_struct.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +/** + * DOC: wlan_pmo_wowl + * + * This module houses all the logic for WOW(wake on wireless) in + * PMO(Power Management and Offload). + * + * It provides the following APIs + * + * - Ability to enable/disable following WoWL modes + * 1) Magic packet (MP) mode + * 2) Pattern Byte Matching (PBM) mode + * - Ability to add/remove patterns for PBM + * + * A Magic Packet is a packet that contains 6 0xFFs followed by 16 + * contiguous copies of the receiving NIC's Ethernet address. There is + * no API to configure Magic Packet Pattern. + * + * Wakeup pattern (used for PBM) is defined as following: + * struct + * { + * U8 PatternSize; // Non-Zero pattern size + * U8 PatternMaskSize; // Non-zero pattern mask size + * U8 PatternMask[PatternMaskSize]; // Pattern mask + * U8 Pattern[PatternSize]; // Pattern + * } hdd_wowl_ptrn_t; + * + * PatternSize and PatternMaskSize indicate size of the variable + * length Pattern and PatternMask. PatternMask indicates which bytes + * of an incoming packet should be compared with corresponding bytes + * in the pattern. + * + * Maximum allowed pattern size is 128 bytes. Maximum allowed + * PatternMaskSize is 16 bytes. + * + * Maximum number of patterns that can be configured is 8 + * + * PMO will add following 2 commonly used patterns for PBM by default: + * 1) ARP Broadcast Pattern + * 2) Unicast Pattern + * + * However note that WoWL will not be enabled by default by PMO. WoWL + * needs to enabled explcitly by exercising the iwpriv command. + * + * PMO will expose an API that accepts patterns as Hex string in the + * following format: + * "PatternSize:PatternMaskSize:PatternMask:Pattern" + * + * Multiple patterns can be specified by deleimiting each pattern with + * the ';' token: + * "PatternSize1:PatternMaskSize1:PatternMask1:Pattern1;PatternSize2:..." + * + * Patterns can be configured dynamically via iwpriv cmd or statically + * via qcom_cfg.ini file + * + * PBM (when enabled) can perform filtering on unicast data or + * broadcast data or both. These configurations are part of factory + * default (cfg.dat) and the default behavior is to perform filtering + * on both unicast and data frames. + * + * MP filtering (when enabled) is performed ALWAYS on both unicast and + * broadcast data frames. + * + * Management frames are not subjected to WoWL filtering and are + * discarded when WoWL is enabled. + * + * Whenever a patern match succeeds, RX path is restored and packets + * (both management and data) will be pushed to the host from that + * point onwards. Therefore, exit from WoWL is implicit and happens + * automatically when the first packet match succeeds. + * + * WoWL works on top of BMPS. So when WoWL is requested, SME will + * attempt to put the device in BMPS mode (if not already in BMPS). If + * attempt to BMPS fails, request for WoWL will be rejected. + */ + +#define PMO_WOW_MAX_EVENT_BM_LEN 4 + +#define PMO_WOW_FILTERS_ARP_NS 2 +#define PMO_WOW_FILTERS_PKT_OR_APF 5 + +/** + * pmo_get_and_increment_wow_default_ptrn() -Get and increment wow default ptrn + * @vdev_ctx: pmo vdev priv ctx + * + * API to get and increment wow default ptrn + * + * Return: current wow default ptrn count + */ +static inline uint8_t pmo_get_and_increment_wow_default_ptrn( + struct pmo_vdev_priv_obj *vdev_ctx) +{ + uint8_t count; + + if (vdev_ctx->pmo_psoc_ctx->caps.unified_wow) { + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + count = vdev_ctx->num_wow_default_patterns++; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + } else { + qdf_spin_lock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + count = vdev_ctx->pmo_psoc_ctx->wow.ptrn_id_def++; + qdf_spin_unlock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + } + + return count; +} + +/** + * pmo_increment_wow_default_ptrn() -increment wow default ptrn + * @vdev_ctx: pmo vdev priv ctx + * + * API to increment wow default ptrn + * + * Return: None + */ +static inline void pmo_increment_wow_default_ptrn( + struct pmo_vdev_priv_obj *vdev_ctx) +{ + if (vdev_ctx->pmo_psoc_ctx->caps.unified_wow) { + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->num_wow_default_patterns++; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + } else { + qdf_spin_lock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + vdev_ctx->pmo_psoc_ctx->wow.ptrn_id_def++; + qdf_spin_unlock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + } +} + +/** + * pmo_decrement_wow_default_ptrn() -decrement wow default ptrn + * @vdev_ctx: pmo vdev priv ctx + * + * API to decrement wow default ptrn + * + * Return: None + */ +static inline void pmo_decrement_wow_default_ptrn( + struct pmo_vdev_priv_obj *vdev_ctx) +{ + if (vdev_ctx->pmo_psoc_ctx->caps.unified_wow) { + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->num_wow_default_patterns--; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + } else { + qdf_spin_lock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + vdev_ctx->pmo_psoc_ctx->wow.ptrn_id_def--; + qdf_spin_unlock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + } +} + +/** + * pmo_get_wow_default_ptrn() -Get wow default ptrn + * @vdev_ctx: pmo vdev priv ctx + * + * API to get wow default ptrn + * + * Return: current wow default ptrn count + */ +static inline uint8_t pmo_get_wow_default_ptrn( + struct pmo_vdev_priv_obj *vdev_ctx) +{ + uint8_t count; + + if (vdev_ctx->pmo_psoc_ctx->caps.unified_wow) { + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + count = vdev_ctx->num_wow_default_patterns; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + } else { + qdf_spin_lock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + count = vdev_ctx->pmo_psoc_ctx->wow.ptrn_id_def; + qdf_spin_unlock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + } + + return count; +} + +/** + * pmo_get_wow_default_ptrn() -Set wow default ptrn + * @vdev_ctx: pmo vdev priv ctx + * + * API to set wow default ptrn + * + * Return: Set wow default ptrn count + */ +static inline void pmo_set_wow_default_ptrn( + struct pmo_vdev_priv_obj *vdev_ctx, uint8_t value) +{ + if (vdev_ctx->pmo_psoc_ctx->caps.unified_wow) { + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->num_wow_default_patterns = value; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + } else { + qdf_spin_lock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + vdev_ctx->pmo_psoc_ctx->wow.ptrn_id_def = value; + qdf_spin_unlock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + } +} + +/** + * pmo_increment_wow_user_ptrn() -increment wow user ptrn + * @vdev_ctx: pmo vdev priv ctx + * + * API to increment wow user ptrn + * + * Return: None + */ +static inline void pmo_increment_wow_user_ptrn( + struct pmo_vdev_priv_obj *vdev_ctx) +{ + if (vdev_ctx->pmo_psoc_ctx->caps.unified_wow) { + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->num_wow_user_patterns++; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + } else { + qdf_spin_lock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + vdev_ctx->pmo_psoc_ctx->wow.ptrn_id_usr++; + qdf_spin_unlock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + } +} + +/** + * pmo_decrement_wow_user_ptrn() -decrement wow user ptrn + * @vdev_ctx: pmo vdev priv ctx + * + * API to decrement wow user ptrn + * + * Return: None + */ +static inline void pmo_decrement_wow_user_ptrn( + struct pmo_vdev_priv_obj *vdev_ctx) +{ + if (vdev_ctx->pmo_psoc_ctx->caps.unified_wow) { + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->num_wow_user_patterns--; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + } else { + qdf_spin_lock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + vdev_ctx->pmo_psoc_ctx->wow.ptrn_id_usr--; + qdf_spin_unlock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + } +} + +/** + * pmo_get_wow_user_ptrn() -Get wow user ptrn + * @vdev_ctx: pmo vdev priv ctx + * + * API to Get wow user ptrn + * + * Return: None + */ +static inline uint8_t pmo_get_wow_user_ptrn( + struct pmo_vdev_priv_obj *vdev_ctx) +{ + uint8_t count; + + if (vdev_ctx->pmo_psoc_ctx->caps.unified_wow) { + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + count = vdev_ctx->num_wow_user_patterns; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + } else { + qdf_spin_lock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + count = vdev_ctx->pmo_psoc_ctx->wow.ptrn_id_usr; + qdf_spin_unlock_bh(&vdev_ctx->pmo_psoc_ctx->lock); + } + + return count; +} + +/** + * pmo_core_del_wow_pattern() - Function which will delete the WoWL pattern + * @vdev: pointer to the vdev + * + * This function deletes all the user WoWl patterns and default WoWl patterns + * + * Return: error if any errors encountered, QDF_STATUS_SUCCESS otherwise + */ + +QDF_STATUS pmo_core_del_wow_pattern(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_core_add_wow_user_pattern() - Function which will add the WoWL pattern + * to be used when PBM filtering is enabled + * @vdev: pointer to the vdev + * @ptrn: pointer to the pattern string to be added + * + * Return: false if any errors encountered, QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS pmo_core_add_wow_user_pattern(struct wlan_objmgr_vdev *vdev, + struct pmo_wow_add_pattern *ptrn); + +/** + * pmo_core_del_wow_user_pattern() - Function which will delete the WoWL pattern + * @vdev: pointer to the vdev + * @ptrn: pointer to the pattern string to be delete + * + * Return: error if any errors encountered, QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS pmo_core_del_wow_user_pattern(struct wlan_objmgr_vdev *vdev, + uint8_t pattern_id); + +/** + * pmo_core_enable_wakeup_event() - enable wow wakeup events + * @psoc: objmgr psoc + * @vdev_id: vdev id + * @wow_event: wow event to enable + * + * Return: none + */ +void pmo_core_enable_wakeup_event(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + WOW_WAKE_EVENT_TYPE wow_event); + +/** + * pmo_core_disable_wakeup_event() - disable wow wakeup events + * @psoc: objmgr psoc + * @vdev_id: vdev id + * @wow_event: wow event to disable + * + * Return: none + */ +void pmo_core_disable_wakeup_event(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + WOW_WAKE_EVENT_TYPE wow_event); + +/** + * pmo_is_wow_applicable(): should enable wow + * @psoc: objmgr psoc object + * + * Enable WOW if any one of the condition meets, + * 1) Is any one of vdev in beaconning mode (in AP mode) ? + * 2) Is any one of vdev in connected state (in STA mode) ? + * 3) Is PNO in progress in any one of vdev ? + * 4) Is Extscan in progress in any one of vdev ? + * 5) Is P2P listen offload in any one of vdev? + * 6) Is any vdev in NAN data mode? BSS is already started at the + * the time of device creation. It is ready to accept data + * requests. + * 7) If LPASS feature is enabled + * 8) If NaN feature is enabled + * If none of above conditions is true then return false + * + * Return: true if wma needs to configure wow false otherwise. + */ +bool pmo_core_is_wow_applicable(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_core_update_wow_enable() - update wow enable flag + * @psoc_ctx: Pointer to objmgr psoc handle + * @value: true if wow mode enable else false + * + * Return: None + */ +static inline +void pmo_core_update_wow_enable(struct pmo_psoc_priv_obj *psoc_ctx, + bool value) +{ + qdf_spin_lock_bh(&psoc_ctx->lock); + psoc_ctx->wow.wow_enable = value; + qdf_spin_unlock_bh(&psoc_ctx->lock); +} + +/** + * pmo_core_is_wow_mode_enabled() - check if wow needs to be enabled in fw + * @psoc_ctx: Pointer to objmgr psoc handle + * + * API to check if wow mode is enabled in fwr as part of apps suspend or not + * + * Return: true is wow mode is enabled else false + */ +static inline +bool pmo_core_is_wow_enabled(struct pmo_psoc_priv_obj *psoc_ctx) +{ + bool value; + + if (!psoc_ctx) { + pmo_err("psoc_ctx is null"); + return false; + } + + qdf_spin_lock_bh(&psoc_ctx->lock); + value = psoc_ctx->wow.wow_enable; + qdf_spin_unlock_bh(&psoc_ctx->lock); + pmo_debug("WoW enable %d", value); + + return value; +} + +/** + * pmo_core_set_wow_nack() - Set wow nack flag + * @psoc_ctx: Pointer to objmgr psoc handle + * @value: true if received wow nack from else false + * + * Return: None + */ +static inline +void pmo_core_set_wow_nack(struct pmo_psoc_priv_obj *psoc_ctx, bool value) +{ + qdf_spin_lock_bh(&psoc_ctx->lock); + psoc_ctx->wow.wow_nack = value; + qdf_spin_unlock_bh(&psoc_ctx->lock); +} + +/** + * pmo_core_get_wow_nack() - Get wow nack flag + * @psoc_ctx: Pointer to objmgr psoc handle + * + * Return: wow nack flag + */ +static inline +bool pmo_core_get_wow_nack(struct pmo_psoc_priv_obj *psoc_ctx) +{ + bool value; + + qdf_spin_lock_bh(&psoc_ctx->lock); + value = psoc_ctx->wow.wow_nack; + qdf_spin_unlock_bh(&psoc_ctx->lock); + + return value; +} +/** + * pmo_core_update_wow_enable_cmd_sent() - update wow enable cmd sent flag + * @psoc_ctx: Pointer to objmgr psoc handle + * @value: true if wow enable cmd sent else false + * + * Return: None + */ +static inline +void pmo_core_update_wow_enable_cmd_sent(struct pmo_psoc_priv_obj *psoc_ctx, + bool value) +{ + qdf_spin_lock_bh(&psoc_ctx->lock); + psoc_ctx->wow.wow_enable_cmd_sent = value; + qdf_spin_unlock_bh(&psoc_ctx->lock); +} + +/** + * pmo_core_get_wow_enable_cmd_sent() - Get wow enable cmd sent flag + * @psoc_ctx: Pointer to objmgr psoc handle + * + * Return: return true if wow enable cmd sent else false + */ +static inline +bool pmo_core_get_wow_enable_cmd_sent(struct pmo_psoc_priv_obj *psoc_ctx) +{ + bool value; + + qdf_spin_lock_bh(&psoc_ctx->lock); + value = psoc_ctx->wow.wow_enable_cmd_sent; + qdf_spin_unlock_bh(&psoc_ctx->lock); + + return value; +} + +/** + * pmo_core_update_wow_initial_wake_up() - update wow initial wake up + * @psoc_ctx: Pointer to objmgr psoc handle + * @value: set to 1 if wow initial wake up is received; + * if clean state, reset it to 0; + * + * Return: None + */ +static inline +void pmo_core_update_wow_initial_wake_up(struct pmo_psoc_priv_obj *psoc_ctx, + int value) +{ + qdf_atomic_set(&psoc_ctx->wow.wow_initial_wake_up, value); +} + +/** + * pmo_core_get_wow_initial_wake_up() - Get wow initial wake up + * @psoc_ctx: Pointer to objmgr psoc handle + * + * Return: 1 if wow initial wake up is received; + * 0 if wow iniital wake up is not received; + */ +static inline +int pmo_core_get_wow_initial_wake_up(struct pmo_psoc_priv_obj *psoc_ctx) +{ + return qdf_atomic_read(&psoc_ctx->wow.wow_initial_wake_up); +} + +#ifdef FEATURE_WLAN_EXTSCAN +/** + * pmo_core_is_extscan_in_progress(): check if a extscan is in progress + * @vdev: objmgr vdev handle + * + * Return: TRUE/FALSE + */ +static inline +bool pmo_core_is_extscan_in_progress(struct wlan_objmgr_vdev *vdev) +{ + bool extscan_in_progress; + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + extscan_in_progress = vdev_ctx->extscan_in_progress; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + return extscan_in_progress; +} + +/** + * pmo_core_update_extscan_in_progress(): update extscan is in progress flags + * @vdev: objmgr vdev handle + * @value:true if extscan is in progress else false + * + * Return: TRUE/FALSE + */ +static inline +void pmo_core_update_extscan_in_progress(struct wlan_objmgr_vdev *vdev, + bool value) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->extscan_in_progress = value; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); +} +#else +static inline +bool pmo_core_is_extscan_in_progress(struct wlan_objmgr_vdev *vdev) +{ + return false; +} + +static inline +void pmo_core_update_extscan_in_progress(struct wlan_objmgr_vdev *vdev, + bool value) +{ +} +#endif + +/** + * pmo_core_is_p2plo_in_progress(): check if p2plo is in progress + * @vdev: objmgr vdev handle + * + * Return: TRUE/FALSE + */ +static inline +bool pmo_core_is_p2plo_in_progress(struct wlan_objmgr_vdev *vdev) +{ + bool p2plo_in_progress; + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + p2plo_in_progress = vdev_ctx->p2plo_in_progress; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + return p2plo_in_progress; +} + +/** + * pmo_core_update_p2plo_in_progress(): update p2plo is in progress flags + * @vdev: objmgr vdev handle + * @value:true if p2plo is in progress else false + * + * Return: TRUE/FALSE + */ +static inline +void pmo_core_update_p2plo_in_progress(struct wlan_objmgr_vdev *vdev, + bool value) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->p2plo_in_progress = value; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); +} + +#ifdef WLAN_FEATURE_LPSS +/** + * pmo_core_is_lpass_enabled() - check if lpass is enabled + * @posc: objmgr psoc object + * + * WoW is needed if LPASS or NaN feature is enabled in INI because + * target can't wake up itself if its put in PDEV suspend when LPASS + * or NaN features are supported + * + * Return: true if lpass is enabled else false + */ +static inline +bool pmo_core_is_lpass_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.lpass_enable; +} +#else +static inline +bool pmo_core_is_lpass_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} +#endif + +/** + * pmo_get_event_bitmap_idx() - get indices for extended wow bitmaps + * @event: wow event + * @wow_bitmap_size: WOW bitmap size + * @bit_idx: bit index + * @idx: byte index + * + * Return: none + */ +static inline void pmo_get_event_bitmap_idx(WOW_WAKE_EVENT_TYPE event, + uint32_t wow_bitmap_size, + uint32_t *bit_idx, + uint32_t *idx) +{ + + if (!bit_idx || !idx || wow_bitmap_size == 0) { + pmo_err("bit_idx:%pK idx:%pK wow_bitmap_size:%u", + bit_idx, idx, wow_bitmap_size); + return; + } + if (event == 0) { + *idx = *bit_idx = 0; + } else { + *idx = event / (wow_bitmap_size * 8); + *bit_idx = event % (wow_bitmap_size * 8); + } +} + +/** + * pmo_get_num_wow_filters() - get the supported number of WoW filters + * @psoc: the psoc to query + * + * Return: number of WoW filters supported + */ +uint8_t pmo_get_num_wow_filters(struct wlan_objmgr_psoc *psoc); + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_WOW_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_apf.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_apf.c new file mode 100644 index 0000000000000000000000000000000000000000..8acc3c11f9c81732fe64f96b92c5c41c468a2620 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_apf.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: PMO implementations for Android Packet Filter (APF) functions + */ + +#include "qdf_types.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_pmo_apf.h" +#include "wlan_pmo_main.h" + +#define PMO_APF_SIZE_AUTO 0 +#define PMO_APF_SIZE_DISABLE 0xffffffff + +uint32_t pmo_get_apf_instruction_size(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + bool apf = false; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + apf = pmo_intersect_apf(psoc_ctx); + } + + return apf ? PMO_APF_SIZE_AUTO : PMO_APF_SIZE_DISABLE; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_arp.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_arp.c new file mode 100644 index 0000000000000000000000000000000000000000..358a48b9526a2d37bc0ad970586f29f967a6f732 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_arp.c @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Implements arp offload feature API's + */ + +#include "wlan_pmo_arp.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +static QDF_STATUS pmo_core_cache_arp_in_vdev_priv( + struct pmo_arp_req *arp_req, + struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_arp_offload_params *request = NULL; + struct pmo_vdev_priv_obj *vdev_ctx; + int index; + struct qdf_mac_addr peer_bssid; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + request = qdf_mem_malloc(sizeof(*request)); + if (!request) { + pmo_err("cannot allocate arp request"); + status = QDF_STATUS_E_NOMEM; + goto exit_with_status; + } + + status = pmo_get_vdev_bss_peer_mac_addr(vdev, &peer_bssid); + if (status != QDF_STATUS_SUCCESS) + goto free_req; + + qdf_mem_copy(&request->bssid.bytes, &peer_bssid.bytes, + QDF_MAC_ADDR_SIZE); + pmo_debug("vdev self mac addr: "QDF_MAC_ADDR_FMT" bss peer mac addr: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)), + QDF_MAC_ADDR_REF(peer_bssid.bytes)); + + request->enable = PMO_OFFLOAD_ENABLE; + request->is_offload_applied = false; + /* converting u32 to IPV4 address */ + for (index = 0; index < QDF_IPV4_ADDR_SIZE; index++) + request->host_ipv4_addr[index] = + (arp_req->ipv4_addr >> (index * 8)) & 0xFF; + + /* cache arp request */ + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_copy(&vdev_ctx->vdev_arp_req, request, + sizeof(vdev_ctx->vdev_arp_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_debug("cached arp offload; addr:" QDF_IPV4_ADDR_STR ", enable:%d", + QDF_IPV4_ADDR_ARRAY(request->host_ipv4_addr), + request->enable); + +free_req: + qdf_mem_free(request); + +exit_with_status: + + return status; +} + +static QDF_STATUS pmo_core_flush_arp_from_vdev_priv( + struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + pmo_enter(); + + vdev_ctx = pmo_vdev_get_priv(vdev); + + /* clear arp request */ + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_zero(&vdev_ctx->vdev_arp_req, sizeof(vdev_ctx->vdev_arp_req)); + vdev_ctx->vdev_arp_req.enable = PMO_OFFLOAD_DISABLE; + vdev_ctx->vdev_arp_req.is_offload_applied = false; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_exit(); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +pmo_core_do_enable_arp_offload(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status; + struct pmo_psoc_priv_obj *psoc_ctx; + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + psoc_ctx = vdev_ctx->pmo_psoc_ctx; + if (!psoc_ctx) { + pmo_err("psoc_ctx is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + switch (trigger) { + case pmo_ipv4_change_notify: + if (!psoc_ctx->psoc_cfg.active_mode_offload) { + pmo_debug("active offload is disabled, skip in mode %d", + trigger); + status = QDF_STATUS_SUCCESS; + goto out; + } + /* enable arp when active offload is true (ipv4 notifier) */ + status = pmo_tgt_enable_arp_offload_req(vdev, vdev_id); + break; + case pmo_apps_suspend: + /* enable arp when active offload is false (apps suspend) */ + status = pmo_tgt_enable_arp_offload_req(vdev, vdev_id); + break; + default: + status = QDF_STATUS_E_INVAL; + pmo_err("invalid pmo trigger"); + break; + } +out: + + return status; +} + +static QDF_STATUS pmo_core_do_disable_arp_offload(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id, enum pmo_offload_trigger trigger) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_psoc_priv_obj *psoc_ctx; + struct pmo_vdev_priv_obj *vdev_ctx; + + pmo_enter(); + + vdev_ctx = pmo_vdev_get_priv(vdev); + + psoc_ctx = vdev_ctx->pmo_psoc_ctx; + if (!psoc_ctx) { + pmo_err("psoc_ctx is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + switch (trigger) { + case pmo_apps_resume: + /* disable arp on apps resume when active offload is disable */ + status = pmo_tgt_disable_arp_offload_req(vdev, vdev_id); + break; + default: + status = QDF_STATUS_E_INVAL; + pmo_err("invalid pmo trigger"); + break; + } +out: + pmo_exit(); + + return status; +} + +static QDF_STATUS pmo_core_arp_offload_sanity( + struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + if (!vdev) { + pmo_err("vdev is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev_ctx = pmo_vdev_get_priv(vdev); + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.arp_offload_enable) { + pmo_err("user disabled arp offload using ini"); + return QDF_STATUS_E_INVAL; + } + + if (!pmo_core_is_vdev_supports_offload(vdev)) { + pmo_debug("vdev in invalid opmode for arp offload %d", + pmo_get_vdev_opmode(vdev)); + return QDF_STATUS_E_INVAL; + } + + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) + return QDF_STATUS_E_INVAL; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_core_arp_check_offload(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_psoc_priv_obj *psoc_ctx; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_objmgr_vdev *vdev; + bool active_offload_cond, is_applied_cond; + enum QDF_OPMODE opmode; + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + opmode = pmo_get_vdev_opmode(vdev); + if (opmode == QDF_NDI_MODE) { + pmo_debug("ARP offload is not supported in NaN mode"); + pmo_vdev_put_ref(vdev); + return QDF_STATUS_E_INVAL; + } + + vdev_ctx = pmo_vdev_get_priv(vdev); + psoc_ctx = vdev_ctx->pmo_psoc_ctx; + + if (trigger == pmo_apps_suspend || trigger == pmo_apps_resume) { + active_offload_cond = psoc_ctx->psoc_cfg.active_mode_offload; + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + is_applied_cond = vdev_ctx->vdev_arp_req.enable && + vdev_ctx->vdev_arp_req.is_offload_applied; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + if (active_offload_cond && is_applied_cond) { + pmo_debug("active offload is enabled and offload already sent"); + pmo_vdev_put_ref(vdev); + return QDF_STATUS_E_INVAL; + } + } + pmo_vdev_put_ref(vdev); +out: + return status; +} + +QDF_STATUS pmo_core_cache_arp_offload_req(struct pmo_arp_req *arp_req) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + if (!arp_req) { + pmo_err("arp_req is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + if (!arp_req->psoc) { + pmo_err("psoc is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + vdev = pmo_psoc_get_vdev(arp_req->psoc, arp_req->vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + status = pmo_core_arp_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + + pmo_debug("Cache arp for vdev id: %d psoc: %pK vdev: %pK", + arp_req->vdev_id, arp_req->psoc, vdev); + + status = pmo_core_cache_arp_in_vdev_priv(arp_req, vdev); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + + return status; +} + +QDF_STATUS pmo_core_flush_arp_offload_req(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + uint8_t vdev_id; + + pmo_enter(); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + status = pmo_core_arp_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto def_ref; + + vdev_id = pmo_vdev_get_id(vdev); + pmo_debug("Flush arp for vdev id: %d vdev: %pK", vdev_id, vdev); + + status = pmo_core_flush_arp_from_vdev_priv(vdev); +def_ref: + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_enable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status; + uint8_t vdev_id; + + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + status = pmo_core_arp_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto put_ref; + + vdev_id = pmo_vdev_get_id(vdev); + pmo_debug("Enable arp offload in fwr vdev id: %d vdev: %pK", + vdev_id, vdev); + + status = pmo_core_do_enable_arp_offload(vdev, vdev_id, trigger); + +put_ref: + pmo_vdev_put_ref(vdev); +out:; + + return status; +} + +QDF_STATUS pmo_core_disable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status; + uint8_t vdev_id; + + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + status = pmo_core_arp_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto def_ref; + + vdev_id = pmo_vdev_get_id(vdev); + pmo_debug("Disable arp offload in fwr vdev id: %d vdev: %pK", + vdev_id, vdev); + + status = pmo_core_do_disable_arp_offload(vdev, vdev_id, trigger); +def_ref: + pmo_vdev_put_ref(vdev); +out: + + return status; +} + +QDF_STATUS +pmo_core_get_arp_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *params) +{ + QDF_STATUS status; + struct pmo_vdev_priv_obj *vdev_ctx; + uint8_t vdev_id; + + pmo_enter(); + + if (!params) + return QDF_STATUS_E_INVAL; + + qdf_mem_zero(params, sizeof(*params)); + + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + status = pmo_core_arp_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto put_ref; + + vdev_id = pmo_vdev_get_id(vdev); + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + *params = vdev_ctx->vdev_arp_req; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + +put_ref: + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_gtk.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_gtk.c new file mode 100644 index 0000000000000000000000000000000000000000..413bc3d5693dcb6b39512fd0abe0df70e3d61c2f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_gtk.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements gtk offload feature API's + */ + +#include "wlan_pmo_gtk.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +static QDF_STATUS pmo_core_cache_gtk_req_in_vdev_priv( + struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_req) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + QDF_STATUS status; + struct qdf_mac_addr peer_bssid; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + status = pmo_get_vdev_bss_peer_mac_addr(vdev, &peer_bssid); + if (status != QDF_STATUS_SUCCESS) + return status; + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_copy(&vdev_ctx->vdev_gtk_req, gtk_req, + sizeof(vdev_ctx->vdev_gtk_req)); + qdf_mem_copy(&vdev_ctx->vdev_gtk_req.bssid, + &peer_bssid, QDF_MAC_ADDR_SIZE); + vdev_ctx->vdev_gtk_req.flags = PMO_GTK_OFFLOAD_ENABLE; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS pmo_core_flush_gtk_req_from_vdev_priv( + struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_zero(&vdev_ctx->vdev_gtk_req, sizeof(vdev_ctx->vdev_gtk_req)); + vdev_ctx->vdev_gtk_req.flags = PMO_GTK_OFFLOAD_DISABLE; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS pmo_core_do_enable_gtk_offload( + struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx, + struct pmo_gtk_req *op_gtk_req) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t vdev_id; + enum QDF_OPMODE op_mode; + + op_mode = pmo_get_vdev_opmode(vdev); + if (QDF_NDI_MODE == op_mode) { + pmo_debug("gtk offload is not supported in NaN mode"); + return QDF_STATUS_E_INVAL; + } + + if (!pmo_core_is_vdev_supports_offload(vdev)) { + pmo_debug("vdev in invalid opmode for gtk offload %d", + pmo_get_vdev_opmode(vdev)); + return QDF_STATUS_E_INVAL; + } + + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) + return QDF_STATUS_E_INVAL; + + vdev_id = pmo_vdev_get_id(vdev); + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_copy(op_gtk_req, &vdev_ctx->vdev_gtk_req, + sizeof(*op_gtk_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + if ((op_gtk_req->flags == PMO_GTK_OFFLOAD_ENABLE) && + (qdf_atomic_read(&vdev_ctx->gtk_err_enable) == 1)) { + pmo_debug("GTK Offload already enabled, Disabling vdev_id: %d", + vdev_id); + op_gtk_req->flags = PMO_GTK_OFFLOAD_DISABLE; + status = pmo_tgt_send_gtk_offload_req(vdev, op_gtk_req); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to disable GTK Offload"); + goto out; + } + pmo_debug("Enable GTK Offload again with updated inputs"); + op_gtk_req->flags = PMO_GTK_OFFLOAD_ENABLE; + } + + status = pmo_tgt_send_gtk_offload_req(vdev, op_gtk_req); +out: + + return status; +} + +static QDF_STATUS pmo_core_is_gtk_enabled_in_fwr( + struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx) +{ + QDF_STATUS status; + struct qdf_mac_addr peer_bssid; + + if (!pmo_core_is_vdev_supports_offload(vdev)) { + pmo_debug("vdev in invalid opmode for gtk offload enable %d", + pmo_get_vdev_opmode(vdev)); + return QDF_STATUS_E_INVAL; + } + + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) + return QDF_STATUS_E_INVAL; + + status = pmo_get_vdev_bss_peer_mac_addr(vdev, + &peer_bssid); + if (status != QDF_STATUS_SUCCESS) + return QDF_STATUS_E_INVAL; + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + if (qdf_mem_cmp(&vdev_ctx->vdev_gtk_req.bssid, + &peer_bssid, QDF_MAC_ADDR_SIZE)) { + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + pmo_err("cache request mac:"QDF_MAC_ADDR_FMT", peer mac:"QDF_MAC_ADDR_FMT" are not same", + QDF_MAC_ADDR_REF(vdev_ctx->vdev_gtk_req.bssid.bytes), + QDF_MAC_ADDR_REF(peer_bssid.bytes)); + return QDF_STATUS_E_INVAL; + } + + if (vdev_ctx->vdev_gtk_req.flags != PMO_GTK_OFFLOAD_ENABLE) { + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + pmo_err("gtk flag is disabled hence no gtk rsp required"); + return QDF_STATUS_E_INVAL; + } + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS pmo_core_do_disable_gtk_offload( + struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx, + struct pmo_gtk_req *op_gtk_req) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + enum QDF_OPMODE op_mode; + + op_mode = pmo_get_vdev_opmode(vdev); + if (QDF_NDI_MODE == op_mode) { + pmo_debug("gtk offload is not supported in NaN mode"); + return QDF_STATUS_E_INVAL; + } + + status = pmo_core_is_gtk_enabled_in_fwr(vdev, vdev_ctx); + if (status != QDF_STATUS_SUCCESS) + return status; + + op_gtk_req->flags = PMO_GTK_OFFLOAD_DISABLE; + status = pmo_tgt_send_gtk_offload_req(vdev, op_gtk_req); + + return status; +} + +QDF_STATUS pmo_core_cache_gtk_offload_req(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_req) +{ + QDF_STATUS status; + enum QDF_OPMODE opmode; + uint8_t vdev_id; + + if (!gtk_req) { + pmo_err("gtk_req is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + opmode = pmo_get_vdev_opmode(vdev); + vdev_id = pmo_vdev_get_id(vdev); + pmo_debug("vdev opmode: %d vdev_id: %d", opmode, vdev_id); + if (!pmo_core_is_vdev_supports_offload(vdev)) { + pmo_debug("vdev in invalid opmode for caching gtk request %d", + opmode); + status = QDF_STATUS_E_INVAL; + goto dec_ref; + } + + status = pmo_core_cache_gtk_req_in_vdev_priv(vdev, gtk_req); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + + return status; +} + +QDF_STATUS pmo_core_flush_gtk_offload_req(struct wlan_objmgr_vdev *vdev) +{ + enum QDF_OPMODE opmode; + uint8_t vdev_id; + QDF_STATUS status; + + if (!vdev) { + pmo_err("psoc is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + opmode = pmo_get_vdev_opmode(vdev); + vdev_id = pmo_vdev_get_id(vdev); + pmo_debug("vdev opmode: %d vdev_id: %d", opmode, vdev_id); + if (!pmo_core_is_vdev_supports_offload(vdev)) { + pmo_debug("vdev in invalid opmode for flushing gtk request %d", + opmode); + status = QDF_STATUS_E_INVAL; + goto dec_ref; + } + + status = pmo_core_flush_gtk_req_from_vdev_priv(vdev); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + + return status; +} + +QDF_STATUS pmo_core_enable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + struct pmo_vdev_priv_obj *vdev_ctx; + struct pmo_gtk_req *op_gtk_req = NULL; + + pmo_enter(); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + op_gtk_req = qdf_mem_malloc(sizeof(*op_gtk_req)); + if (!op_gtk_req) { + pmo_err("op_gtk_req is NULL"); + status = QDF_STATUS_E_INVAL; + goto dec_ref; + } + status = pmo_core_do_enable_gtk_offload(vdev, vdev_ctx, op_gtk_req); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + if (op_gtk_req) + qdf_mem_free(op_gtk_req); + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_disable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + struct pmo_vdev_priv_obj *vdev_ctx; + struct pmo_gtk_req *op_gtk_req = NULL; + + pmo_enter(); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + op_gtk_req = qdf_mem_malloc(sizeof(*op_gtk_req)); + if (!op_gtk_req) { + pmo_err("op_gtk_req is NULL"); + status = QDF_STATUS_E_NOMEM; + goto dec_ref; + } + + status = pmo_core_do_disable_gtk_offload(vdev, vdev_ctx, op_gtk_req); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + if (op_gtk_req) + qdf_mem_free(op_gtk_req); + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_get_gtk_rsp(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_rsp_req *gtk_rsp_req) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_vdev_priv_obj *vdev_ctx; + + pmo_enter(); + if (!gtk_rsp_req || !vdev) { + pmo_err("%s is null", !vdev ? "vdev":"gtk_rsp_req"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + status = pmo_core_is_gtk_enabled_in_fwr(vdev, vdev_ctx); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + + /* cache gtk rsp request */ + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_copy(&vdev_ctx->vdev_gtk_rsp_req, gtk_rsp_req, + sizeof(vdev_ctx->vdev_gtk_rsp_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + /* send cmd to fwr */ + status = pmo_tgt_get_gtk_rsp(vdev); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_hw_filter.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_hw_filter.c new file mode 100644 index 0000000000000000000000000000000000000000..328872a3ac1cd6673829372bd6a0cca150864f1f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_hw_filter.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements arp offload feature API's + */ + +#include "qdf_lock.h" +#include "wlan_pmo_hw_filter.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +QDF_STATUS pmo_core_enable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + struct pmo_psoc_priv_obj *psoc_priv; + enum pmo_hw_filter_mode mode_bitmap; + struct pmo_hw_filter_params req = {0}; + + pmo_enter(); + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto exit_with_status; + + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) { + status = QDF_STATUS_E_NOSUPPORT; + pmo_vdev_put_ref(vdev); + goto exit_with_status; + } + + psoc_priv = pmo_vdev_get_psoc_priv(vdev); + qdf_spin_lock_bh(&psoc_priv->lock); + mode_bitmap = psoc_priv->psoc_cfg.hw_filter_mode_bitmap; + qdf_spin_unlock_bh(&psoc_priv->lock); + + req.vdev_id = pmo_vdev_get_id(vdev); + req.mode_bitmap = psoc_priv->psoc_cfg.hw_filter_mode_bitmap; + req.enable = true; + status = pmo_tgt_conf_hw_filter(pmo_vdev_get_psoc(vdev), &req); + + pmo_vdev_put_ref(vdev); + +exit_with_status: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_disable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + struct pmo_psoc_priv_obj *psoc_priv; + enum pmo_hw_filter_mode mode_bitmap; + struct pmo_hw_filter_params req = {0}; + + pmo_enter(); + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto exit_with_status; + + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) { + status = QDF_STATUS_E_NOSUPPORT; + pmo_vdev_put_ref(vdev); + goto exit_with_status; + } + + psoc_priv = pmo_vdev_get_psoc_priv(vdev); + qdf_spin_lock_bh(&psoc_priv->lock); + mode_bitmap = psoc_priv->psoc_cfg.hw_filter_mode_bitmap; + qdf_spin_unlock_bh(&psoc_priv->lock); + + req.vdev_id = pmo_vdev_get_id(vdev); + req.mode_bitmap = mode_bitmap; + req.enable = false; + status = pmo_tgt_conf_hw_filter(pmo_vdev_get_psoc(vdev), &req); + + pmo_vdev_put_ref(vdev); + +exit_with_status: + pmo_exit(); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_lphb.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_lphb.c new file mode 100644 index 0000000000000000000000000000000000000000..9ca0be70d13dd10464bd3c3ae03af84d54e6cf4c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_lphb.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Implements low power heart beat offload feature API's + */ + +#include "wlan_pmo_main.h" +#include "wlan_pmo_lphb.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +#ifdef FEATURE_WLAN_LPHB +/** + * pmo_core_send_lphb_enable() - enable command of LPHB configuration requests + * @psoc: objmgr psoc handle + * @psoc_ctx: pmo private psoc ctx + * @lphb_conf_req: lphb request which need s to configure in fwr + * @by_user: whether this call is from user or cached resent + * + * Return: QDF status + */ +static QDF_STATUS pmo_core_send_lphb_enable(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_priv_obj *psoc_ctx, + struct pmo_lphb_req *lphb_conf_req, bool by_user) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct pmo_lphb_enable_req *ts_lphb_enable; + int i; + + if (!lphb_conf_req) { + pmo_err("LPHB configuration is NULL"); + return QDF_STATUS_E_FAILURE; + } + + ts_lphb_enable = &(lphb_conf_req->params.lphb_enable_req); + qdf_status = pmo_tgt_send_lphb_enable(psoc, ts_lphb_enable); + if (qdf_status != QDF_STATUS_SUCCESS) + goto out; + + /* No need to cache non user request */ + if (!by_user) { + qdf_status = QDF_STATUS_SUCCESS; + goto out; + } + + /* target already configured, now cache command status */ + if (ts_lphb_enable->enable && ts_lphb_enable->item > 0) { + i = ts_lphb_enable->item - 1; + qdf_spin_lock_bh(&psoc_ctx->lock); + psoc_ctx->wow.lphb_cache[i].cmd + = pmo_lphb_set_en_param_indid; + psoc_ctx->wow.lphb_cache[i].params.lphb_enable_req.enable = + ts_lphb_enable->enable; + psoc_ctx->wow.lphb_cache[i].params.lphb_enable_req.item = + ts_lphb_enable->item; + psoc_ctx->wow.lphb_cache[i].params.lphb_enable_req.session = + ts_lphb_enable->session; + qdf_spin_unlock_bh(&psoc_ctx->lock); + pmo_debug("cached LPHB status in WMA context for item %d", i); + } else { + qdf_spin_lock_bh(&psoc_ctx->lock); + qdf_mem_zero((void *)&psoc_ctx->wow.lphb_cache, + sizeof(psoc_ctx->wow.lphb_cache)); + qdf_spin_unlock_bh(&psoc_ctx->lock); + pmo_debug("cleared all cached LPHB status in WMA context"); + } + +out: + return qdf_status; +} + +/** + * pmo_core_send_lphb_tcp_params() - Send tcp params of LPHB requests + * @psoc: objmgr psoc handle + * @lphb_conf_req: lphb request which needs to be configured in fwr + * + * Return: QDF status + */ +static +QDF_STATUS pmo_core_send_lphb_tcp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_req *lphb_conf_req) +{ + return pmo_tgt_send_lphb_tcp_params(psoc, + &lphb_conf_req->params.lphb_tcp_params); + +} + +/** + * pmo_core_send_lphb_tcp_pkt_filter() - Send tcp packet filter command of LPHB + * @psoc: objmgr psoc handle + * @lphb_conf_req: lphb request which needs to be configured in fwr + * + * Return: QDF status + */ +static +QDF_STATUS pmo_core_send_lphb_tcp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_req *lphb_conf_req) +{ + return pmo_tgt_send_lphb_tcp_pkt_filter(psoc, + &lphb_conf_req->params.lphb_tcp_filter_req); +} + +/** + * pmo_core_send_lphb_udp_params() - Send udp param command of LPHB + * @psoc: objmgr psoc handle + * @lphb_conf_req: lphb request which needs to be configured in fwr + * + * Return: QDF status + */ +static +QDF_STATUS pmo_core_send_lphb_udp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_req *lphb_conf_req) +{ + return pmo_tgt_send_lphb_udp_params(psoc, + &lphb_conf_req->params.lphb_udp_params); +} + +/** + * pmo_core_send_lphb_udp_pkt_filter() - Send udp pkt filter command of LPHB + * @psoc: objmgr psoc handle + * @lphb_conf_req: lphb request which need s to configure in fwr + * + * Return: QDF status + */ +static +QDF_STATUS pmo_core_send_lphb_udp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_req *lphb_conf_req) +{ + return pmo_tgt_send_lphb_udp_pkt_filter(psoc, + &lphb_conf_req->params.lphb_udp_filter_req); +} + +/** + * pmo_process_lphb_conf_req() - handle LPHB configuration requests + * @psoc: objmgr psoc handle + * @psoc_ctx: pmo private psoc ctx + * @lphb_conf_req: lphb request which needs to be configured in fwr + * + * Return: QDF status + */ +static QDF_STATUS pmo_process_lphb_conf_req(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_priv_obj *psoc_ctx, + struct pmo_lphb_req *lphb_conf_req) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_debug("LPHB configuration cmd id is %d", lphb_conf_req->cmd); + switch (lphb_conf_req->cmd) { + case pmo_lphb_set_en_param_indid: + status = pmo_core_send_lphb_enable(psoc, psoc_ctx, + lphb_conf_req, true); + break; + + case pmo_lphb_set_tcp_pararm_indid: + status = pmo_core_send_lphb_tcp_params(psoc, lphb_conf_req); + break; + + case pmo_lphb_set_tcp_pkt_filter_indid: + status = pmo_core_send_lphb_tcp_pkt_filter(psoc, lphb_conf_req); + break; + + case pmo_lphb_set_udp_pararm_indid: + status = pmo_core_send_lphb_udp_params(psoc, lphb_conf_req); + break; + + case pmo_lphb_set_udp_pkt_filter_indid: + status = pmo_core_send_lphb_udp_pkt_filter(psoc, lphb_conf_req); + break; + + case pmo_lphb_set_network_info_indid: + default: + break; + } + + return status; +} + +void pmo_core_apply_lphb(struct wlan_objmgr_psoc *psoc) +{ + int i; + struct pmo_psoc_priv_obj *psoc_ctx; + + psoc_ctx = pmo_psoc_get_priv(psoc); + + pmo_debug("checking LPHB cache"); + for (i = 0; i < 2; i++) { + if (psoc_ctx->wow.lphb_cache[i].params.lphb_enable_req.enable) { + pmo_debug("LPHB cache for item %d is marked as enable", + i + 1); + pmo_core_send_lphb_enable(psoc, psoc_ctx, + &(psoc_ctx->wow.lphb_cache[i]), false); + } + } +} + +QDF_STATUS pmo_core_lphb_config_req(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_req *lphb_req, void *lphb_cb_ctx, + pmo_lphb_callback callback) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + if (!lphb_req) { + pmo_err("LPHB configuration is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + psoc_ctx = pmo_psoc_get_priv(psoc); + + if (pmo_lphb_set_en_param_indid == lphb_req->cmd) { + if (!lphb_cb_ctx) { + pmo_err("lphb callback context is null"); + return QDF_STATUS_E_NULL_VALUE; + } + if (!callback) { + pmo_err("lphb callback function is null"); + return QDF_STATUS_E_NULL_VALUE; + } + qdf_spin_lock_bh(&psoc_ctx->lock); + psoc_ctx->wow.lphb_cb_ctx = lphb_cb_ctx; + psoc_ctx->wow.lphb_cb = callback; + qdf_spin_unlock_bh(&psoc_ctx->lock); + } + + return pmo_process_lphb_conf_req(psoc, psoc_ctx, lphb_req); +} + +#endif /* FEATURE_WLAN_LPHB */ + diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_main.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_main.c new file mode 100644 index 0000000000000000000000000000000000000000..f11f4d931c8575f15e91f41f772ead74766b0192 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_main.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Implement various api / helper function which shall be used + * PMO user and target interface. + */ + +#include "wlan_pmo_main.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_cfg.h" +#include "cfg_ucfg_api.h" +#include "wlan_fwol_ucfg_api.h" + +static struct wlan_pmo_ctx *gp_pmo_ctx; + +QDF_STATUS pmo_allocate_ctx(void) +{ + /* If it is already created, ignore */ + if (gp_pmo_ctx) { + pmo_debug("already allocated pmo_ctx"); + return QDF_STATUS_SUCCESS; + } + + /* allocate offload mgr ctx */ + gp_pmo_ctx = (struct wlan_pmo_ctx *)qdf_mem_malloc( + sizeof(*gp_pmo_ctx)); + if (!gp_pmo_ctx) { + pmo_err("unable to allocate pmo_ctx"); + QDF_ASSERT(0); + return QDF_STATUS_E_NOMEM; + } + qdf_spinlock_create(&gp_pmo_ctx->lock); + + return QDF_STATUS_SUCCESS; +} + +void pmo_free_ctx(void) +{ + if (!gp_pmo_ctx) { + pmo_err("pmo ctx is already freed"); + QDF_ASSERT(0); + return; + } + qdf_spinlock_destroy(&gp_pmo_ctx->lock); + qdf_mem_free(gp_pmo_ctx); + gp_pmo_ctx = NULL; +} + +struct wlan_pmo_ctx *pmo_get_context(void) +{ + return gp_pmo_ctx; +} + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +static void wlan_extwow_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + psoc_cfg->extwow_goto_suspend = + cfg_get(psoc, CFG_EXTWOW_GOTO_SUSPEND); + psoc_cfg->extwow_app1_wakeup_pin_num = + cfg_get(psoc, CFG_EXTWOW_APP1_WAKE_PIN_NUMBER); + psoc_cfg->extwow_app2_wakeup_pin_num = + cfg_get(psoc, CFG_EXTWOW_APP2_WAKE_PIN_NUMBER); + psoc_cfg->extwow_app2_init_ping_interval = + cfg_get(psoc, CFG_EXTWOW_KA_INIT_PING_INTERVAL); + psoc_cfg->extwow_app2_min_ping_interval = + cfg_get(psoc, CFG_EXTWOW_KA_MIN_PING_INTERVAL); + psoc_cfg->extwow_app2_max_ping_interval = + cfg_get(psoc, CFG_EXTWOW_KA_MAX_PING_INTERVAL); + psoc_cfg->extwow_app2_inc_ping_interval = + cfg_get(psoc, CFG_EXTWOW_KA_INC_PING_INTERVAL); + psoc_cfg->extwow_app2_tcp_src_port = + cfg_get(psoc, CFG_EXTWOW_TCP_SRC_PORT); + psoc_cfg->extwow_app2_tcp_dst_port = + cfg_get(psoc, CFG_EXTWOW_TCP_DST_PORT); + psoc_cfg->extwow_app2_tcp_tx_timeout = + cfg_get(psoc, CFG_EXTWOW_TCP_TX_TIMEOUT); + psoc_cfg->extwow_app2_tcp_rx_timeout = + cfg_get(psoc, CFG_EXTWOW_TCP_RX_TIMEOUT); +} +#else +static void wlan_extwow_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ +} +#endif + +#ifdef WLAN_FEATURE_WOW_PULSE +static void wlan_pmo_wow_pulse_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + psoc_cfg->is_wow_pulse_supported = + cfg_get(psoc, CFG_PMO_WOW_PULSE_ENABLE); + psoc_cfg->wow_pulse_pin = cfg_get(psoc, CFG_PMO_WOW_PULSE_PIN); + psoc_cfg->wow_pulse_interval_high = + cfg_get(psoc, CFG_PMO_WOW_PULSE_HIGH); + psoc_cfg->wow_pulse_interval_low = + cfg_get(psoc, CFG_PMO_WOW_PULSE_LOW); +} +#else +static void wlan_pmo_wow_pulse_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ +} +#endif + +#ifdef WLAN_FEATURE_PACKET_FILTERING +static void wlan_pmo_pkt_filter_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + psoc_cfg->packet_filters_bitmap = cfg_get(psoc, CFG_PMO_PKT_FILTER); + psoc_cfg->packet_filter_enabled = + !cfg_get(psoc, CFG_PMO_DISABLE_PKT_FILTER); +} +#else +static void wlan_pmo_pkt_filter_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + psoc_cfg->packet_filter_enabled = + !cfg_get(psoc, CFG_PMO_DISABLE_PKT_FILTER); +} +#endif + +#ifdef FEATURE_RUNTIME_PM +static void wlan_pmo_runtime_pm_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + psoc_cfg->runtime_pm_delay = cfg_get(psoc, CFG_PMO_RUNTIME_PM_DELAY); +} +#else +static void wlan_pmo_runtime_pm_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ +} +#endif + +#if FEATURE_WLAN_RA_FILTERING +static void wlan_pmo_ra_filtering_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + bool ra_enable; + + if (QDF_IS_STATUS_SUCCESS(ucfg_fwol_get_is_rate_limit_enabled(psoc, + &ra_enable))) + psoc_cfg->ra_ratelimit_enable = ra_enable; + + psoc_cfg->ra_ratelimit_interval = + cfg_get(psoc, CFG_RA_RATE_LIMIT_INTERVAL); +} +#else +static void wlan_pmo_ra_filtering_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ +} +#endif + +static void wlan_pmo_init_cfg(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + psoc_cfg->arp_offload_enable = + cfg_get(psoc, CFG_PMO_ENABLE_HOST_ARPOFFLOAD); + psoc_cfg->hw_filter_mode_bitmap = cfg_get(psoc, CFG_PMO_HW_FILTER_MODE); + psoc_cfg->ssdp = cfg_get(psoc, CFG_PMO_ENABLE_HOST_SSDP); + psoc_cfg->ns_offload_enable_static = + cfg_get(psoc, CFG_PMO_ENABLE_HOST_NSOFFLOAD); + psoc_cfg->ns_offload_enable_dynamic = + cfg_get(psoc, CFG_PMO_ENABLE_HOST_NSOFFLOAD); + psoc_cfg->sta_dynamic_dtim = cfg_get(psoc, CFG_PMO_ENABLE_DYNAMIC_DTIM); + psoc_cfg->sta_mod_dtim = cfg_get(psoc, CFG_PMO_ENABLE_MODULATED_DTIM); + psoc_cfg->enable_mc_list = cfg_get(psoc, CFG_PMO_MC_ADDR_LIST_ENABLE); + psoc_cfg->power_save_mode = cfg_get(psoc, CFG_PMO_POWERSAVE_MODE); + psoc_cfg->is_mod_dtim_on_sys_suspend_enabled = + cfg_get(psoc, CFG_PMO_MOD_DTIM_ON_SYS_SUSPEND); + psoc_cfg->max_ps_poll = cfg_get(psoc, CFG_PMO_MAX_PS_POLL); + + psoc_cfg->wow_enable = cfg_get(psoc, CFG_PMO_WOW_ENABLE); + psoc_cfg->wowlan_deauth_enable = + cfg_get(psoc, CFG_PMO_WOWLAN_DEAUTH_ENABLE); + psoc_cfg->wowlan_disassoc_enable = + cfg_get(psoc, CFG_PMO_WOWLAN_DISASSOC_ENABLE); + + wlan_extwow_init_cfg(psoc, psoc_cfg); + psoc_cfg->apf_enable = cfg_get(psoc, CFG_PMO_APF_ENABLE); + psoc_cfg->active_mode_offload = cfg_get(psoc, CFG_PMO_ACTIVE_MODE); + wlan_pmo_wow_pulse_init_cfg(psoc, psoc_cfg); + wlan_pmo_pkt_filter_init_cfg(psoc, psoc_cfg); + wlan_pmo_runtime_pm_init_cfg(psoc, psoc_cfg); + psoc_cfg->auto_power_save_fail_mode = + cfg_get(psoc, CFG_PMO_PWR_FAILURE); + psoc_cfg->enable_sap_suspend = cfg_get(psoc, CFG_ENABLE_SAP_SUSPEND); + psoc_cfg->wow_data_inactivity_timeout = + cfg_get(psoc, CFG_PMO_WOW_DATA_INACTIVITY_TIMEOUT); + psoc_cfg->ps_data_inactivity_timeout = + cfg_get(psoc, CFG_PS_DATA_INACTIVITY_TIMEOUT); + psoc_cfg->active_uc_apf_mode = + cfg_get(psoc, CFG_ACTIVE_UC_APF_MODE); + psoc_cfg->active_mc_bc_apf_mode = + cfg_get(psoc, CFG_ACTIVE_MC_BC_APF_MODE); + psoc_cfg->ito_repeat_count = cfg_get(psoc, CFG_ITO_REPEAT_COUNT); + wlan_pmo_ra_filtering_init_cfg(psoc, psoc_cfg); +} + +QDF_STATUS pmo_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx; + + if (!psoc) { + pmo_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + wlan_pmo_init_cfg(psoc, &pmo_psoc_ctx->psoc_cfg); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +bool pmo_is_vdev_in_beaconning_mode(enum QDF_OPMODE vdev_opmode) +{ + switch (vdev_opmode) { + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + case QDF_IBSS_MODE: + return true; + default: + return false; + } +} + +QDF_STATUS pmo_get_vdev_bss_peer_mac_addr(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *bss_peer_mac_address) +{ + struct wlan_objmgr_peer *peer; + + if (!vdev) { + pmo_err("vdev is null"); + return QDF_STATUS_E_INVAL; + } + + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_PMO_ID); + if (!peer) { + pmo_err("peer is null"); + return QDF_STATUS_E_INVAL; + } + wlan_peer_obj_lock(peer); + qdf_mem_copy(bss_peer_mac_address->bytes, wlan_peer_get_macaddr(peer), + QDF_MAC_ADDR_SIZE); + wlan_peer_obj_unlock(peer); + + wlan_objmgr_peer_release_ref(peer, WLAN_PMO_ID); + + return QDF_STATUS_SUCCESS; +} + +bool pmo_core_is_ap_mode_supports_arp_ns(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE vdev_opmode) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + psoc_ctx = pmo_psoc_get_priv(psoc); + + if ((vdev_opmode == QDF_SAP_MODE || + vdev_opmode == QDF_P2P_GO_MODE) && + !psoc_ctx->psoc_cfg.ap_arpns_support) { + pmo_debug("ARP/NS Offload is not supported in SAP/P2PGO mode"); + return false; + } + + return true; +} + +bool pmo_core_is_vdev_supports_offload(struct wlan_objmgr_vdev *vdev) +{ + enum QDF_OPMODE opmode; + bool val; + + opmode = pmo_get_vdev_opmode(vdev); + pmo_debug("vdev opmode: %d", opmode); + switch (opmode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_NDI_MODE: + val = true; + break; + default: + val = false; + break; + } + + return val; +} + +QDF_STATUS pmo_core_get_psoc_config(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_enter(); + if (!psoc || !psoc_cfg) { + pmo_err("%s is null", !psoc ? "psoc":"psoc_cfg"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + qdf_mem_copy(psoc_cfg, &psoc_ctx->psoc_cfg, sizeof(*psoc_cfg)); + } + +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_update_psoc_config(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_enter(); + if (!psoc || !psoc_cfg) { + pmo_err("%s is null", !psoc ? "psoc":"psoc_cfg"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + qdf_mem_copy(&psoc_ctx->psoc_cfg, psoc_cfg, sizeof(*psoc_cfg)); + } + +out: + pmo_exit(); + + return status; +} + +void pmo_psoc_set_caps(struct wlan_objmgr_psoc *psoc, + struct pmo_device_caps *caps) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + qdf_mem_copy(&psoc_ctx->caps, caps, sizeof(psoc_ctx->caps)); + } +} + +void pmo_core_psoc_set_hif_handle(struct wlan_objmgr_psoc *psoc, + void *hif_hdl) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->hif_hdl = hif_hdl; + } +} + +void *pmo_core_psoc_get_hif_handle(struct wlan_objmgr_psoc *psoc) +{ + void *hif_hdl = NULL; + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + hif_hdl = psoc_ctx->hif_hdl; + } + + return hif_hdl; +} + +void pmo_core_psoc_set_txrx_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t txrx_pdev_id) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->txrx_pdev_id = txrx_pdev_id; + } +} + +uint8_t pmo_core_psoc_get_txrx_handle(struct wlan_objmgr_psoc *psoc) +{ + uint8_t txrx_pdev_id = OL_TXRX_INVALID_PDEV_ID; + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + txrx_pdev_id = psoc_ctx->txrx_pdev_id; + } + + return txrx_pdev_id; +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_mc_addr_filtering.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_mc_addr_filtering.c new file mode 100644 index 0000000000000000000000000000000000000000..f25fae44936443316255dc7ae71335d09d1c1171 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_mc_addr_filtering.c @@ -0,0 +1,663 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements mc addr filtering offload feature API's + */ + +#include "wlan_pmo_mc_addr_filtering.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +#define PMO_INVALID_MC_ADDR_COUNT (-1) + +static void pmo_core_fill_mc_list(struct pmo_vdev_priv_obj **vdev_ctx, + struct pmo_mc_addr_list_params *ip) +{ + struct pmo_mc_addr_list *op_list; + int i, j = 0; + static const uint8_t ipv6_rs[] = { + 0x33, 0x33, 0x00, 0x00, 0x00, 0x02}; + struct pmo_vdev_priv_obj *temp_ctx; + uint8_t addr_fp; + + temp_ctx = *vdev_ctx; + addr_fp = temp_ctx->addr_filter_pattern; + op_list = &temp_ctx->vdev_mc_list_req; + + qdf_spin_lock_bh(&temp_ctx->pmo_vdev_lock); + op_list->mc_cnt = ip->count; + qdf_spin_unlock_bh(&temp_ctx->pmo_vdev_lock); + + for (i = 0; i < ip->count; i++) { + /* + * Skip following addresses: + * 1)IPv6 router solicitation address + * 2)Any other address pattern if its set during + * RXFILTER REMOVE driver command based on + * addr_filter_pattern + */ + if ((!qdf_mem_cmp(ip->mc_addr[i].bytes, ipv6_rs, + QDF_MAC_ADDR_SIZE)) || + (addr_fp && + (!qdf_mem_cmp(ip->mc_addr[i].bytes, &addr_fp, 1)))) { + pmo_debug("MC/BC filtering Skip addr "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(ip->mc_addr[i].bytes)); + qdf_spin_lock_bh(&temp_ctx->pmo_vdev_lock); + op_list->mc_cnt--; + qdf_spin_unlock_bh(&temp_ctx->pmo_vdev_lock); + continue; + } + qdf_spin_lock_bh(&temp_ctx->pmo_vdev_lock); + qdf_mem_zero(&op_list->mc_addr[j].bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(&op_list->mc_addr[j].bytes, + ip->mc_addr[i].bytes, QDF_MAC_ADDR_SIZE); + qdf_spin_unlock_bh(&temp_ctx->pmo_vdev_lock); + pmo_debug("Index = %d, mac["QDF_MAC_ADDR_FMT"]", j, + QDF_MAC_ADDR_REF(op_list->mc_addr[i].bytes)); + j++; + } +} + +static QDF_STATUS pmo_core_cache_mc_addr_list_in_vdev_priv( + struct pmo_mc_addr_list_params *mc_list_config, + struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + pmo_core_fill_mc_list(&vdev_ctx, mc_list_config); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS pmo_core_flush_mc_addr_list_from_vdev_priv( + struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_zero(&vdev_ctx->vdev_mc_list_req, + sizeof(vdev_ctx->vdev_mc_list_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_core_enhanced_mc_filter_enable(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto exit_with_status; + + pmo_tgt_send_enhance_multicast_offload_req(vdev, true); + + pmo_vdev_put_ref(vdev); + +exit_with_status: + + return status; +} + +QDF_STATUS pmo_core_enhanced_mc_filter_disable(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + + pmo_enter(); + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto exit_with_status; + + pmo_tgt_send_enhance_multicast_offload_req(vdev, false); + + pmo_vdev_put_ref(vdev); + +exit_with_status: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_set_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list) +{ + int i; + + if (pmo_tgt_get_multiple_mc_filter_support(vdev)) { + pmo_debug("FW supports multiple mcast filter"); + pmo_tgt_set_multiple_mc_filter_req(vdev, mc_list); + } else { + pmo_debug("FW does not support multiple mcast filter"); + for (i = 0; i < mc_list->mc_cnt; i++) + pmo_tgt_set_mc_filter_req(vdev, mc_list->mc_addr[i]); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_core_clear_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list) +{ + int i; + + if (pmo_tgt_get_multiple_mc_filter_support(vdev)) { + pmo_debug("FW supports multiple mcast filter"); + pmo_tgt_clear_multiple_mc_filter_req(vdev, mc_list); + } else { + pmo_debug("FW does not support multiple mcast filter"); + for (i = 0; i < mc_list->mc_cnt; i++) + pmo_tgt_clear_mc_filter_req(vdev, mc_list->mc_addr[i]); + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS pmo_core_do_enable_mc_addr_list(struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx, + struct pmo_mc_addr_list *op_mc_list_req) +{ + QDF_STATUS status; + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + if (!vdev_ctx->vdev_mc_list_req.mc_cnt) { + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + pmo_err("mc_cnt is zero so skip to add mc list"); + status = QDF_STATUS_E_INVAL; + goto out; + } + qdf_mem_copy(op_mc_list_req, &vdev_ctx->vdev_mc_list_req, + sizeof(*op_mc_list_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + status = pmo_core_set_mc_filter_req(vdev, op_mc_list_req); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("cannot apply mc filter request"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->vdev_mc_list_req.is_filter_applied = true; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); +out: + + return status; +} + +static QDF_STATUS pmo_core_do_disable_mc_addr_list( + struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx, + struct pmo_mc_addr_list *op_mc_list_req) +{ + QDF_STATUS status; + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + /* validate filter is applied before clearing in fwr */ + if (!vdev_ctx->vdev_mc_list_req.is_filter_applied) { + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + pmo_debug("mc filter is not applied in fwr"); + status = QDF_STATUS_E_INVAL; + goto out; + } + qdf_mem_copy(op_mc_list_req, &vdev_ctx->vdev_mc_list_req, + sizeof(*op_mc_list_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + status = pmo_core_clear_mc_filter_req(vdev, op_mc_list_req); + if (status != QDF_STATUS_SUCCESS) { + pmo_debug("cannot apply mc filter request"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->vdev_mc_list_req.is_filter_applied = false; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); +out: + + return status; +} + +uint8_t pmo_core_max_mc_addr_supported(struct wlan_objmgr_psoc *psoc) +{ + return PMO_MAX_MC_ADDR_LIST; +} + +int pmo_core_get_mc_addr_list_count(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + struct pmo_vdev_priv_obj *vdev_ctx; + uint8_t mc_cnt; + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + return PMO_INVALID_MC_ADDR_COUNT; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_warn("failed to get vdev reference"); + return PMO_INVALID_MC_ADDR_COUNT; + } + + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + mc_cnt = vdev_ctx->vdev_mc_list_req.mc_cnt; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_vdev_put_ref(vdev); + + return mc_cnt; +} + +void pmo_core_set_mc_addr_list_count(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t count) +{ + QDF_STATUS status; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_objmgr_vdev *vdev; + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + return; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_warn("failed to get vdev reference"); + return; + } + + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->vdev_mc_list_req.mc_cnt = count; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_vdev_put_ref(vdev); +} + +static QDF_STATUS pmo_core_mc_addr_flitering_sanity( + struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + if (!vdev) { + pmo_err("vdev is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev_ctx = pmo_vdev_get_priv(vdev); + + /* Check if INI is enabled or not, otherwise just return */ + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.enable_mc_list) { + pmo_debug("user disabled mc_addr_list using INI"); + return QDF_STATUS_E_INVAL; + } + + if (!pmo_core_is_vdev_supports_offload(vdev)) { + pmo_debug("vdev in invalid opmode for mc addr filtering %d", + pmo_get_vdev_opmode(vdev)); + return QDF_STATUS_E_INVAL; + } + + if (vdev_ctx->vdev_mc_list_req.mc_cnt > PMO_MAX_MC_ADDR_LIST) { + pmo_debug("Passed more than max supported MC address count :%d", + vdev_ctx->vdev_mc_list_req.mc_cnt); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} +QDF_STATUS pmo_core_cache_mc_addr_list( + struct pmo_mc_addr_list_params *mc_list_config) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + if (!mc_list_config->psoc) { + pmo_err("psoc is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + vdev = pmo_psoc_get_vdev(mc_list_config->psoc, mc_list_config->vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_warn("failed to get vdev reference"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_core_mc_addr_flitering_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + + pmo_debug("Cache mc addr list for vdev id: %d psoc: %pK", + mc_list_config->vdev_id, mc_list_config->psoc); + + status = pmo_core_cache_mc_addr_list_in_vdev_priv(mc_list_config, vdev); + +dec_ref: + pmo_vdev_put_ref(vdev); +out: + + return status; +} + +QDF_STATUS pmo_core_flush_mc_addr_list(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!psoc) { + pmo_err("psoc is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_warn("failed to get vdev reference"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_core_mc_addr_flitering_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + + pmo_debug("Flush mc addr list for vdev id: %d psoc: %pK", + vdev_id, psoc); + + status = pmo_core_flush_mc_addr_list_from_vdev_priv(vdev); + +dec_ref: + pmo_vdev_put_ref(vdev); +out: + + return status; +} + +static QDF_STATUS pmo_core_handle_enable_mc_list_trigger( + struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_mc_addr_list *op_mc_list_req; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + op_mc_list_req = qdf_mem_malloc(sizeof(*op_mc_list_req)); + if (!op_mc_list_req) { + pmo_err("op_mc_list_req is NULL"); + status = QDF_STATUS_E_NOMEM; + goto exit_with_status; + } + + switch (trigger) { + case pmo_mc_list_change_notify: + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.active_mode_offload) { + pmo_debug("active offload is disabled, skip in mode %d", + trigger); + goto free_req; + } + status = pmo_core_do_enable_mc_addr_list(vdev, vdev_ctx, + op_mc_list_req); + break; + case pmo_apps_suspend: + if (vdev_ctx->pmo_psoc_ctx->psoc_cfg.active_mode_offload) { + pmo_debug("active offload is enabled, skip in mode %d", + trigger); + goto free_req; + } + status = pmo_core_do_enable_mc_addr_list(vdev, vdev_ctx, + op_mc_list_req); + break; + default: + status = QDF_STATUS_E_INVAL; + pmo_err("invalid pmo trigger for enable mc list"); + break; + } + +free_req: + qdf_mem_free(op_mc_list_req); + +exit_with_status: + + return status; +} + +QDF_STATUS pmo_core_enable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + status = pmo_psoc_get_ref(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto exit_with_status; + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto put_psoc; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto put_psoc; + + status = pmo_core_mc_addr_flitering_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto put_vdev; + + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) { + status = QDF_STATUS_E_INVAL; + goto put_vdev; + } + + pmo_debug("enable mclist trigger: %d", trigger); + status = pmo_core_handle_enable_mc_list_trigger(vdev, trigger); + +put_vdev: + pmo_vdev_put_ref(vdev); + +put_psoc: + pmo_psoc_put_ref(psoc); + +exit_with_status: + + return status; +} + +static QDF_STATUS pmo_core_handle_disable_mc_list_trigger( + struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_vdev_priv_obj *vdev_ctx; + struct pmo_mc_addr_list *op_mc_list_req; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + op_mc_list_req = qdf_mem_malloc(sizeof(*op_mc_list_req)); + if (!op_mc_list_req) { + pmo_err("out of memory"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + switch (trigger) { + case pmo_peer_disconnect: + case pmo_mc_list_change_notify: + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.active_mode_offload) { + pmo_debug("active offload is disabled, skip in mode %d", + trigger); + goto free_req; + } + status = pmo_core_do_disable_mc_addr_list(vdev, vdev_ctx, + op_mc_list_req); + break; + case pmo_apps_resume: + if (vdev_ctx->pmo_psoc_ctx->psoc_cfg.active_mode_offload) { + pmo_debug("active offload is enabled, skip in mode %d", + trigger); + goto free_req; + } + status = pmo_core_do_disable_mc_addr_list(vdev, vdev_ctx, + op_mc_list_req); + break; + default: + status = QDF_STATUS_E_INVAL; + pmo_err("invalid pmo trigger for disable mc list"); + break; + } + +free_req: + qdf_mem_free(op_mc_list_req); + +out: + return status; +} + +QDF_STATUS pmo_core_disable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + if (!psoc) { + pmo_err("psoc is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + status = pmo_core_mc_addr_flitering_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto put_ref; + + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) { + status = QDF_STATUS_E_INVAL; + goto put_ref; + } + + pmo_debug("disable mclist trigger: %d", trigger); + + status = pmo_core_handle_disable_mc_list_trigger(vdev, trigger); + +put_ref: + pmo_vdev_put_ref(vdev); + +out: + + return status; +} + +QDF_STATUS +pmo_core_get_mc_addr_list(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + struct pmo_mc_addr_list *mc_list_req) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + struct pmo_vdev_priv_obj *vdev_ctx; + + pmo_enter(); + + if (!mc_list_req) + return QDF_STATUS_E_INVAL; + + qdf_mem_zero(mc_list_req, sizeof(*mc_list_req)); + + status = pmo_psoc_get_ref(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto exit_with_status; + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto put_psoc; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto put_psoc; + + status = pmo_core_mc_addr_flitering_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto put_vdev; + + vdev_ctx = pmo_vdev_get_priv(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + *mc_list_req = vdev_ctx->vdev_mc_list_req; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + +put_vdev: + pmo_vdev_put_ref(vdev); + +put_psoc: + pmo_psoc_put_ref(psoc); + +exit_with_status: + pmo_exit(); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_ns.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_ns.c new file mode 100644 index 0000000000000000000000000000000000000000..1942be8131cda65168a52dbb4f69aa07584f4f9e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_ns.c @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements ns offload feature API's + */ + +#include "wlan_pmo_ns.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +static void pmo_core_fill_ns_addr(struct pmo_ns_offload_params *request, + struct pmo_ns_req *ns_req) +{ + int i; + + for (i = 0; i < ns_req->count; i++) { + /* + * Filling up the request structure + * Filling the selfIPv6Addr with solicited address + * A Solicited-Node multicast address is created by + * taking the last 24 bits of a unicast or anycast + * address and appending them to the prefix + * + * FF02:0000:0000:0000:0000:0001:FFXX:XXXX + * + * here XX is the unicast/anycast bits + */ + request->self_ipv6_addr[i][0] = 0xFF; + request->self_ipv6_addr[i][1] = 0x02; + request->self_ipv6_addr[i][11] = 0x01; + request->self_ipv6_addr[i][12] = 0xFF; + request->self_ipv6_addr[i][13] = + ns_req->ipv6_addr[i][13]; + request->self_ipv6_addr[i][14] = + ns_req->ipv6_addr[i][14]; + request->self_ipv6_addr[i][15] = + ns_req->ipv6_addr[i][15]; + request->slot_idx = i; + qdf_mem_copy(&request->target_ipv6_addr[i], + &ns_req->ipv6_addr[i][0], QDF_IPV6_ADDR_SIZE); + + request->target_ipv6_addr_valid[i] = + PMO_IPV6_ADDR_VALID; + request->target_ipv6_addr_ac_type[i] = + ns_req->ipv6_addr_type[i]; + + request->scope[i] = ns_req->scope[i]; + + pmo_debug("NSoffload solicitIp: %pI6 targetIp: %pI6 Index: %d", + &request->self_ipv6_addr[i], + &request->target_ipv6_addr[i], i); + } +} + +static QDF_STATUS pmo_core_cache_ns_in_vdev_priv( + struct pmo_ns_req *ns_req, + struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_vdev_priv_obj *vdev_ctx; + struct pmo_ns_offload_params request; + struct wlan_objmgr_peer *peer; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + qdf_mem_zero(&request, sizeof(request)); + pmo_core_fill_ns_addr(&request, ns_req); + + request.enable = PMO_OFFLOAD_ENABLE; + request.is_offload_applied = false; + qdf_mem_copy(&request.self_macaddr.bytes, + wlan_vdev_mlme_get_macaddr(vdev), + QDF_MAC_ADDR_SIZE); + + /* set number of ns offload address count */ + request.num_ns_offload_count = ns_req->count; + + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_PMO_ID); + if (!peer) { + pmo_err("peer is null"); + status = QDF_STATUS_E_INVAL; + goto out; + } + pmo_debug("vdev self mac addr: "QDF_MAC_ADDR_FMT" bss peer mac addr: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)), + QDF_MAC_ADDR_REF(wlan_peer_get_macaddr(peer))); + /* get peer and peer mac accdress aka ap mac address */ + qdf_mem_copy(&request.bssid, + wlan_peer_get_macaddr(peer), + QDF_MAC_ADDR_SIZE); + wlan_objmgr_peer_release_ref(peer, WLAN_PMO_ID); + /* cache ns request */ + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_copy(&vdev_ctx->vdev_ns_req, &request, + sizeof(vdev_ctx->vdev_ns_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); +out: + + return status; +} + +static QDF_STATUS pmo_core_flush_ns_from_vdev_priv( + struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + pmo_enter(); + + vdev_ctx = pmo_vdev_get_priv(vdev); + + /* clear ns request */ + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_zero(&vdev_ctx->vdev_ns_req, sizeof(vdev_ctx->vdev_ns_req)); + vdev_ctx->vdev_ns_req.enable = PMO_OFFLOAD_DISABLE; + vdev_ctx->vdev_ns_req.is_offload_applied = false; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_exit(); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS pmo_core_do_enable_ns_offload(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id, enum pmo_offload_trigger trigger) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_psoc_priv_obj *psoc_ctx; + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + psoc_ctx = vdev_ctx->pmo_psoc_ctx; + if (!psoc_ctx) { + pmo_err("psoc_ctx is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + switch (trigger) { + case pmo_ipv6_change_notify: + case pmo_ns_offload_dynamic_update: + if (!psoc_ctx->psoc_cfg.active_mode_offload) { + pmo_debug("active offload is disabled, skip in mode:%d", + trigger); + goto out; + } + /* enable arp when active offload is true (ipv6 notifier) */ + status = pmo_tgt_enable_ns_offload_req(vdev, vdev_id); + break; + case pmo_apps_suspend: + /* enable arp when active offload is false (apps suspend) */ + status = pmo_tgt_enable_ns_offload_req(vdev, vdev_id); + break; + default: + status = QDF_STATUS_E_INVAL; + pmo_err("invalid pmo trigger"); + break; + } +out: + + return status; +} + +static QDF_STATUS pmo_core_do_disable_ns_offload(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id, enum pmo_offload_trigger trigger) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_enter(); + + psoc_ctx = pmo_vdev_get_psoc_priv(vdev); + + switch (trigger) { + case pmo_ipv6_change_notify: + case pmo_ns_offload_dynamic_update: + if (!psoc_ctx->psoc_cfg.active_mode_offload) { + pmo_debug("active offload is disabled, skip in mode:%d", + trigger); + goto out; + } + /* config ns when active offload is enable */ + status = pmo_tgt_disable_ns_offload_req(vdev, vdev_id); + break; + case pmo_apps_resume: + status = pmo_tgt_disable_ns_offload_req(vdev, vdev_id); + break; + default: + status = QDF_STATUS_E_INVAL; + pmo_err("invalid pmo trigger"); + break; + } +out: + pmo_exit(); + + return status; +} + + +static QDF_STATUS pmo_core_ns_offload_sanity(struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.ns_offload_enable_static) { + pmo_debug("ns offload statically disable"); + return QDF_STATUS_E_INVAL; + } + + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic) { + pmo_debug("ns offload dynamically disable"); + return QDF_STATUS_E_INVAL; + } + + if (!pmo_core_is_vdev_supports_offload(vdev)) { + pmo_debug("vdev in invalid opmode for ns offload %d", + pmo_get_vdev_opmode(vdev)); + return QDF_STATUS_E_INVAL; + } + + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) + return QDF_STATUS_E_INVAL; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_core_ns_check_offload(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pmo_psoc_priv_obj *psoc_ctx; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_objmgr_vdev *vdev; + bool active_offload_cond, is_applied_cond; + enum QDF_OPMODE opmode; + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + opmode = pmo_get_vdev_opmode(vdev); + if (opmode == QDF_NDI_MODE) { + pmo_debug("NS offload not supported in NaN mode"); + pmo_vdev_put_ref(vdev); + return QDF_STATUS_E_INVAL; + } + + vdev_ctx = pmo_vdev_get_priv(vdev); + psoc_ctx = vdev_ctx->pmo_psoc_ctx; + + if (trigger == pmo_apps_suspend || trigger == pmo_apps_resume) { + active_offload_cond = psoc_ctx->psoc_cfg.active_mode_offload; + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + is_applied_cond = vdev_ctx->vdev_ns_req.enable && + vdev_ctx->vdev_ns_req.is_offload_applied; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + if (active_offload_cond && is_applied_cond) { + pmo_debug("active offload is enabled and offload already sent"); + pmo_vdev_put_ref(vdev); + return QDF_STATUS_E_INVAL; + } + } + pmo_vdev_put_ref(vdev); +out: + return status; +} + +QDF_STATUS pmo_core_cache_ns_offload_req(struct pmo_ns_req *ns_req) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + if (!ns_req) { + pmo_err("ns is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + if (!ns_req->psoc) { + pmo_err("psoc is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + vdev = pmo_psoc_get_vdev(ns_req->psoc, ns_req->vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + status = pmo_core_ns_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + + if (ns_req->count == 0) { + pmo_debug("skip ns offload caching as ns count is 0"); + status = QDF_STATUS_E_INVAL; + goto dec_ref; + } + + status = pmo_core_cache_ns_in_vdev_priv(ns_req, vdev); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + + return status; +} + +QDF_STATUS pmo_core_flush_ns_offload_req(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + uint8_t vdev_id; + + pmo_enter(); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + status = pmo_core_ns_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + + vdev_id = pmo_vdev_get_id(vdev); + pmo_debug("Flush ns offload on vdev id: %d vdev: %pK", vdev_id, vdev); + + status = pmo_core_flush_ns_from_vdev_priv(vdev); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_enable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status; + struct pmo_vdev_priv_obj *vdev_ctx; + uint8_t vdev_id; + + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + status = pmo_core_ns_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + + if (trigger == pmo_ns_offload_dynamic_update) { + /* + * user disable ns offload using ioctl/vendor cmd dynamically. + */ + vdev_ctx->pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic = + true; + goto skip_ns_dynamic_check; + } + + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic) { + pmo_debug("ns offload dynamically disable"); + goto dec_ref; + } + +skip_ns_dynamic_check: + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + if (vdev_ctx->vdev_ns_req.num_ns_offload_count == 0) { + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + pmo_debug("skip ns offload enable as ns count is 0"); + status = QDF_STATUS_E_INVAL; + goto dec_ref; + } + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + vdev_id = pmo_vdev_get_id(vdev); + pmo_debug("Enable ns offload in fwr vdev id: %d vdev: %pK trigger: %d", + vdev_id, vdev, trigger); + status = pmo_core_do_enable_ns_offload(vdev, vdev_id, trigger); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + + return status; +} + +QDF_STATUS pmo_core_disable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status; + uint8_t vdev_id; + struct pmo_vdev_priv_obj *vdev_ctx; + + pmo_enter(); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + status = pmo_core_ns_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + + if (trigger == pmo_ns_offload_dynamic_update) { + /* + * user disable ns offload using ioctl/vendor cmd dynamically. + */ + vdev_ctx->pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic = + false; + goto skip_ns_dynamic_check; + } + + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic) { + pmo_debug("ns offload dynamically disable"); + goto dec_ref; + } + +skip_ns_dynamic_check: + vdev_id = pmo_vdev_get_id(vdev); + pmo_debug("disable ns offload in fwr vdev id: %d vdev: %pK trigger: %d", + vdev_id, vdev, trigger); + + status = pmo_core_do_disable_ns_offload(vdev, vdev_id, trigger); +dec_ref: + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS +pmo_core_get_ns_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_ns_offload_params *params) +{ + QDF_STATUS status; + struct pmo_vdev_priv_obj *vdev_ctx; + + pmo_enter(); + + if (!params) + return QDF_STATUS_E_INVAL; + + qdf_mem_zero(params, sizeof(*params)); + + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + status = pmo_core_ns_offload_sanity(vdev); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + *params = vdev_ctx->vdev_ns_req; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + +dec_ref: + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_pkt_filter.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_pkt_filter.c new file mode 100644 index 0000000000000000000000000000000000000000..2624c5cabf92f2c831d7370ae46132d3208c9ae5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_pkt_filter.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Implements Packet filter feature API's + */ + +#include "wlan_pmo_pkt_filter.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +#define PMO_PKT_FILTERS_DEFAULT 12 +#define PMO_PKT_FILTERS_DISABLED 0xffffffff + +uint32_t pmo_get_num_packet_filters(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + bool pkt_filter = false; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + pkt_filter = pmo_intersect_packet_filter(psoc_ctx); + } + + return pkt_filter ? PMO_PKT_FILTERS_DEFAULT : PMO_PKT_FILTERS_DISABLED; +} + +QDF_STATUS pmo_core_set_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req, + uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + pmo_enter(); + + if (!psoc) { + pmo_err("psoc is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, WLAN_PMO_ID); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tgt_set_pkt_filter(vdev, pmo_set_pkt_fltr_req, vdev_id); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID); +out: + pmo_exit(); + + return status; + +} + +QDF_STATUS pmo_core_clear_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_clear_param *pmo_clr_pkt_fltr_param, + uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + pmo_enter(); + + if (!psoc) { + pmo_err("psoc is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, WLAN_PMO_ID); + if (!vdev) { + pmo_err("vdev is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tgt_clear_pkt_filter(vdev, pmo_clr_pkt_fltr_param, + vdev_id); + if (status != QDF_STATUS_SUCCESS) + goto dec_ref; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID); +out: + pmo_exit(); + + return status; + +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_static_config.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_static_config.c new file mode 100644 index 0000000000000000000000000000000000000000..4003d072ae805c483a3f896b86da3ee0e7fe44ed --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_static_config.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Implements static configuration on vdev attach + */ + +#include "wlan_pmo_static_config.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_wow.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +static const uint8_t arp_ptrn[] = {0x08, 0x06}; +static const uint8_t arp_mask[] = {0xff, 0xff}; +static const uint8_t ns_ptrn[] = {0x86, 0xDD}; +static const uint8_t discvr_ptrn[] = {0xe0, 0x00, 0x00, 0xf8}; +static const uint8_t discvr_mask[] = {0xf0, 0x00, 0x00, 0xf8}; + +void pmo_register_wow_wakeup_events(struct wlan_objmgr_vdev *vdev) +{ + uint32_t event_bitmap[PMO_WOW_MAX_EVENT_BM_LEN] = {0}; + uint8_t vdev_id; + enum QDF_OPMODE vdev_opmode; + struct pmo_psoc_priv_obj *psoc_ctx; + pmo_is_device_in_low_pwr_mode is_low_pwr_mode; + + vdev_opmode = pmo_get_vdev_opmode(vdev); + vdev_id = pmo_vdev_get_id(vdev); + pmo_debug("vdev_opmode %d vdev_id %d", vdev_opmode, vdev_id); + + switch (vdev_opmode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + /* set power on failure event only for STA and P2P_CLI mode*/ + psoc_ctx = pmo_vdev_get_psoc_priv(vdev); + if (psoc_ctx->psoc_cfg.auto_power_save_fail_mode == + PMO_FW_TO_SEND_WOW_IND_ON_PWR_FAILURE){ + qdf_spin_lock(&psoc_ctx->lock); + is_low_pwr_mode = psoc_ctx->is_device_in_low_pwr_mode; + qdf_spin_unlock(&psoc_ctx->lock); + if (is_low_pwr_mode && is_low_pwr_mode(vdev_id)) + pmo_set_wow_event_bitmap( + WOW_CHIP_POWER_FAILURE_DETECT_EVENT, + PMO_WOW_MAX_EVENT_BM_LEN, + event_bitmap); + } + + /* fallthrough */ + case QDF_P2P_DEVICE_MODE: + case QDF_OCB_MODE: + case QDF_MONITOR_MODE: + pmo_set_sta_wow_bitmask(event_bitmap, PMO_WOW_MAX_EVENT_BM_LEN); + break; + + case QDF_IBSS_MODE: + pmo_set_sta_wow_bitmask(event_bitmap, PMO_WOW_MAX_EVENT_BM_LEN); + pmo_set_wow_event_bitmap(WOW_BEACON_EVENT, + PMO_WOW_MAX_EVENT_BM_LEN, + event_bitmap); + break; + + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + pmo_set_sap_wow_bitmask(event_bitmap, PMO_WOW_MAX_EVENT_BM_LEN); + break; + + case QDF_NDI_MODE: + pmo_set_ndp_wow_bitmask(event_bitmap, PMO_WOW_MAX_EVENT_BM_LEN); + break; + + default: + pmo_err("Skipping wake event configuration for vdev_opmode %d", + vdev_opmode); + return; + } + + pmo_tgt_enable_wow_wakeup_event(vdev, event_bitmap); +} + +/** + * pmo_configure_wow_ap() - set WOW patterns in ap mode + * @vdev: objmgr vdev handle + * + * Configures default WOW pattern for the given vdev_id which is in AP mode. + * + * Return: QDF status + */ +static QDF_STATUS pmo_configure_wow_ap(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS ret; + uint8_t arp_offset = 20; + uint8_t mac_mask[QDF_MAC_ADDR_SIZE]; + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + /* + * Setup unicast pkt pattern + * WoW pattern id should be unique for each vdev + * WoW pattern id can be same on 2 different VDEVs + */ + qdf_mem_set(&mac_mask, QDF_MAC_ADDR_SIZE, 0xFF); + ret = pmo_tgt_send_wow_patterns_to_fw(vdev, + pmo_get_and_increment_wow_default_ptrn(vdev_ctx), + wlan_vdev_mlme_get_macaddr(vdev), + QDF_MAC_ADDR_SIZE, 0, mac_mask, + QDF_MAC_ADDR_SIZE, false); + if (ret != QDF_STATUS_SUCCESS) { + pmo_err("Failed to add WOW unicast pattern ret %d", ret); + return ret; + } + + /* + * Setup all ARP pkt pattern. This is dummy pattern hence the length + * is zero. Pattern ID should be unique per vdev. + */ + ret = pmo_tgt_send_wow_patterns_to_fw(vdev, + pmo_get_and_increment_wow_default_ptrn(vdev_ctx), + arp_ptrn, 0, arp_offset, arp_mask, 0, false); + if (ret != QDF_STATUS_SUCCESS) + pmo_err("Failed to add WOW ARP pattern ret %d", ret); + + return ret; +} + +/** + * pmo_configure_mc_ssdp() - API to configure SSDP address as MC list + * @vdev: objmgr vdev handle. + * + * SSDP address 239.255.255.250 is converted to Multicast Mac address + * and configure it to FW. Firmware will apply this pattern on the incoming + * packets to filter them out during chatter/wow mode. + * + * Return: Success/Failure + */ +static QDF_STATUS pmo_configure_mc_ssdp( + struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_psoc *psoc; + const uint8_t ssdp_addr[QDF_MAC_ADDR_SIZE] = { + 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xfa }; + struct qdf_mac_addr multicast_addr; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + psoc = pmo_vdev_get_psoc(vdev); + + qdf_mem_copy(&multicast_addr.bytes, &ssdp_addr, QDF_MAC_ADDR_SIZE); + status = pmo_tgt_set_mc_filter_req(vdev, + multicast_addr); + if (status != QDF_STATUS_SUCCESS) + pmo_err("unable to set ssdp as mc addr list filter"); + + return status; +} + +/** + * pmo_configure_wow_ssdp() - API to configure WoW SSDP + *@vdev: objmgr vdev handle + * + * API to configure SSDP pattern as WoW pattern + * + * Return: Success/Failure + */ +static QDF_STATUS pmo_configure_wow_ssdp( + struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t discvr_offset = 30; + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + /* + * WoW pattern ID should be unique for each vdev + * Different WoW patterns can use same pattern ID + */ + status = pmo_tgt_send_wow_patterns_to_fw(vdev, + pmo_get_and_increment_wow_default_ptrn(vdev_ctx), + discvr_ptrn, sizeof(discvr_ptrn), discvr_offset, + discvr_mask, sizeof(discvr_ptrn), false); + + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to add WOW mDNS/SSDP/LLMNR pattern"); + + return status; +} + +/** + * pmo_configure_ssdp() - API to Configure SSDP pattern to FW + *@vdev: objmgr vdev handle + * + * Setup multicast pattern for mDNS 224.0.0.251, SSDP 239.255.255.250 and LLMNR + * 224.0.0.252 + * + * Return: Success/Failure. + */ +static QDF_STATUS pmo_configure_ssdp(struct wlan_objmgr_vdev *vdev) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.ssdp) { + pmo_debug("mDNS, SSDP, LLMNR patterns are disabled from ini"); + return QDF_STATUS_SUCCESS; + } + + pmo_debug("enable_mc_list:%d", + vdev_ctx->pmo_psoc_ctx->psoc_cfg.enable_mc_list); + + if (vdev_ctx->pmo_psoc_ctx->psoc_cfg.enable_mc_list) + return pmo_configure_mc_ssdp(vdev); + + return pmo_configure_wow_ssdp(vdev); +} + +/** + * pmo_configure_wow_sta() - set WOW patterns in sta mode + * @vdev: objmgr vdev handle + * + * Configures default WOW pattern for the given vdev_id which is in sta mode. + * + * Return: QDF status + */ +static QDF_STATUS pmo_configure_wow_sta(struct wlan_objmgr_vdev *vdev) +{ + uint8_t arp_offset = 12; + uint8_t mac_mask[QDF_MAC_ADDR_SIZE]; + QDF_STATUS ret = QDF_STATUS_SUCCESS; + struct pmo_vdev_priv_obj *vdev_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + qdf_mem_set(&mac_mask, QDF_MAC_ADDR_SIZE, 0xFF); + /* + * Set up unicast wow pattern + * WoW pattern ID should be unique for each vdev + * Different WoW patterns can use same pattern ID + */ + ret = pmo_tgt_send_wow_patterns_to_fw(vdev, + pmo_get_and_increment_wow_default_ptrn(vdev_ctx), + wlan_vdev_mlme_get_macaddr(vdev), + QDF_MAC_ADDR_SIZE, 0, mac_mask, + QDF_MAC_ADDR_SIZE, false); + if (ret != QDF_STATUS_SUCCESS) { + pmo_err("Failed to add WOW unicast pattern ret %d", ret); + return ret; + } + + ret = pmo_configure_ssdp(vdev); + if (ret != QDF_STATUS_SUCCESS) + pmo_err("Failed to configure SSDP patterns to FW"); + + /* + * when arp offload or ns offloaded is disabled + * from ini file, configure broad cast arp pattern + * to fw, so that host can wake up + */ + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.arp_offload_enable) { + /* Setup all ARP pkt pattern */ + pmo_debug("ARP offload is disabled in INI enable WoW for ARP"); + ret = pmo_tgt_send_wow_patterns_to_fw(vdev, + pmo_get_and_increment_wow_default_ptrn( + vdev_ctx), + arp_ptrn, sizeof(arp_ptrn), arp_offset, + arp_mask, sizeof(arp_mask), false); + if (ret != QDF_STATUS_SUCCESS) { + pmo_err("Failed to add WOW ARP pattern"); + return ret; + } + } + /* for NS or NDP offload packets */ + if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.ns_offload_enable_static) { + /* Setup all NS pkt pattern */ + pmo_debug("NS offload is disabled in INI enable WoW for NS"); + ret = pmo_tgt_send_wow_patterns_to_fw(vdev, + pmo_get_and_increment_wow_default_ptrn( + vdev_ctx), + ns_ptrn, sizeof(arp_ptrn), arp_offset, + arp_mask, sizeof(arp_mask), false); + if (ret != QDF_STATUS_SUCCESS) { + pmo_err("Failed to add WOW NS pattern"); + return ret; + } + } + + return ret; +} + +void pmo_register_wow_default_patterns(struct wlan_objmgr_vdev *vdev) +{ + enum QDF_OPMODE vdev_opmode = QDF_MAX_NO_OF_MODE; + struct pmo_vdev_priv_obj *vdev_ctx; + uint8_t vdev_id; + struct pmo_psoc_priv_obj *psoc_ctx; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + vdev_id = pmo_vdev_get_id(vdev); + if (vdev_id > WLAN_UMAC_PSOC_MAX_VDEVS) { + pmo_err("Invalid vdev id %d", vdev_id); + return; + } + + vdev_opmode = pmo_get_vdev_opmode(vdev); + if (vdev_opmode == QDF_MAX_NO_OF_MODE) { + pmo_err("Invalid vdev opmode %d", vdev_id); + return; + } + + if (!vdev_ctx->ptrn_match_enable) { + pmo_err("ptrn_match is disable for vdev %d", vdev_id); + return; + } + + if (pmo_is_vdev_in_beaconning_mode(vdev_opmode)) { + /* Configure SAP/GO/IBSS mode default wow patterns */ + pmo_debug("Config SAP default wow patterns vdev_id %d", + vdev_id); + pmo_configure_wow_ap(vdev); + } else { + /* Configure STA/P2P CLI mode default wow patterns */ + pmo_debug("Config STA default wow patterns vdev_id %d", + vdev_id); + pmo_configure_wow_sta(vdev); + psoc_ctx = vdev_ctx->pmo_psoc_ctx; + if (!psoc_ctx) { + pmo_err("PMO PSOC Context is NULL!"); + return; + } + + /* + * No need for configuring RA filter while APF is enabled, since + * APF internally handles RA filtering. + */ + if (psoc_ctx->psoc_cfg.ra_ratelimit_enable && + !pmo_intersect_apf(psoc_ctx)) { + pmo_debug("Config STA RA wow pattern vdev_id %d", + vdev_id); + pmo_tgt_send_ra_filter_req(vdev); + } + } + +} + +/** + * set_action_id_drop_pattern_for_spec_mgmt() - Set action id of action + * frames for spectrum mgmt frames to be droppped in fw. + * + * @action_id_per_category: Pointer to action id bitmaps. + */ +static void set_action_id_drop_pattern_for_spec_mgmt( + uint32_t *action_id_per_category) +{ + action_id_per_category[PMO_MAC_ACTION_SPECTRUM_MGMT] + = DROP_SPEC_MGMT_ACTION_FRAME_BITMAP; +} + +/** + * set_action_id_drop_pattern_for_public_action() - Set action id of action + * frames for public action frames to be droppped in fw. + * + * @action_id_per_category: Pointer to action id bitmaps. + */ +static void set_action_id_drop_pattern_for_public_action( + uint32_t *action_id_per_category) +{ + action_id_per_category[PMO_MAC_ACTION_PUBLIC_USAGE] + = DROP_PUBLIC_ACTION_FRAME_BITMAP; +} + +QDF_STATUS +pmo_register_action_frame_patterns(struct wlan_objmgr_vdev *vdev, + enum qdf_suspend_type suspend_type) +{ + + struct pmo_action_wakeup_set_params cmd = {0}; + int i = 0; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + cmd.vdev_id = pmo_vdev_get_id(vdev); + cmd.operation = pmo_action_wakeup_set; + + if (suspend_type == QDF_SYSTEM_SUSPEND) + cmd.action_category_map[i++] = + SYSTEM_SUSPEND_ALLOWED_ACTION_FRAMES_BITMAP0; + else + cmd.action_category_map[i++] = + RUNTIME_PM_ALLOWED_ACTION_FRAMES_BITMAP0; + + cmd.action_category_map[i++] = ALLOWED_ACTION_FRAMES_BITMAP1; + cmd.action_category_map[i++] = ALLOWED_ACTION_FRAMES_BITMAP2; + cmd.action_category_map[i++] = ALLOWED_ACTION_FRAMES_BITMAP3; + cmd.action_category_map[i++] = ALLOWED_ACTION_FRAMES_BITMAP4; + cmd.action_category_map[i++] = ALLOWED_ACTION_FRAMES_BITMAP5; + cmd.action_category_map[i++] = ALLOWED_ACTION_FRAMES_BITMAP6; + cmd.action_category_map[i++] = ALLOWED_ACTION_FRAMES_BITMAP7; + + set_action_id_drop_pattern_for_spec_mgmt(cmd.action_per_category); + set_action_id_drop_pattern_for_public_action(cmd.action_per_category); + + for (i = 0; i < PMO_SUPPORTED_ACTION_CATE_ELE_LIST; i++) { + if (i < ALLOWED_ACTION_FRAME_MAP_WORDS) + pmo_debug("%s: %d action Wakeup pattern 0x%x in fw", + __func__, i, cmd.action_category_map[i]); + else + cmd.action_category_map[i] = 0; + } + + pmo_debug("Spectrum mgmt action id drop bitmap: 0x%x", + cmd.action_per_category[PMO_MAC_ACTION_SPECTRUM_MGMT]); + pmo_debug("Public action id drop bitmap: 0x%x", + cmd.action_per_category[PMO_MAC_ACTION_PUBLIC_USAGE]); + + /* config action frame patterns */ + status = pmo_tgt_send_action_frame_pattern_req(vdev, &cmd); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to config wow action frame map, ret %d", + status); + + return status; +} + +QDF_STATUS +pmo_clear_action_frame_patterns(struct wlan_objmgr_vdev *vdev) +{ + struct pmo_action_wakeup_set_params cmd = {0}; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + cmd.vdev_id = pmo_vdev_get_id(vdev); + cmd.operation = pmo_action_wakeup_reset; + + /* clear action frame pattern */ + status = pmo_tgt_send_action_frame_pattern_req(vdev, &cmd); + if (QDF_IS_STATUS_ERROR(status)) + pmo_err("Failed to clear wow action frame map, ret %d", + status); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_suspend_resume.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_suspend_resume.c new file mode 100644 index 0000000000000000000000000000000000000000..1cc81f558a313a9c175553603b6094dfe5c652f8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_suspend_resume.c @@ -0,0 +1,1690 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Define API's for suspend / resume handling + */ + +#include "wlan_pmo_wow.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_lphb.h" +#include "wlan_pmo_hw_filter.h" +#include "wlan_pmo_suspend_resume.h" +#include "cdp_txrx_ops.h" +#include "cdp_txrx_misc.h" +#include "cdp_txrx_flow_ctrl_legacy.h" +#include "hif.h" +#include "htc_api.h" +#include "wlan_pmo_obj_mgmt_api.h" +#include +#include "cds_api.h" +#include "wlan_pmo_static_config.h" +#include "wlan_mlme_ucfg_api.h" +#include "cfg_mlme_sap.h" +#include "cfg_ucfg_api.h" +#include "cdp_txrx_bus.h" + +/** + * pmo_core_get_vdev_dtim_period() - Get vdev dtim period + * @vdev: objmgr vdev handle + * + * Return: Vdev dtim period + */ +static uint8_t pmo_core_get_vdev_dtim_period(struct wlan_objmgr_vdev *vdev) +{ + uint8_t dtim_period = 0; + struct pmo_psoc_priv_obj *psoc_ctx; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS ret = QDF_STATUS_E_FAILURE; + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + if (psoc_ctx->get_dtim_period) + ret = psoc_ctx->get_dtim_period(pmo_vdev_get_id(vdev), + &dtim_period); + } + + if (QDF_IS_STATUS_ERROR(ret)) + pmo_err("Failed to get to dtim period for vdevId %d", + pmo_vdev_get_id(vdev)); + + return dtim_period; +} + +/** + * pmo_core_get_vdev_beacon_interval() - Get vdev beacon interval + * @vdev: objmgr vdev handle + * + * Return: Vdev beacon interval + */ +static uint16_t pmo_core_get_vdev_beacon_interval(struct wlan_objmgr_vdev *vdev) +{ + uint16_t beacon_interval = 0; + struct pmo_psoc_priv_obj *psoc_ctx; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS ret = QDF_STATUS_E_FAILURE; + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + if (psoc_ctx->get_beacon_interval) + ret = psoc_ctx->get_beacon_interval( + pmo_vdev_get_id(vdev), + &beacon_interval); + } + + if (QDF_IS_STATUS_ERROR(ret)) + pmo_err("Failed to get beacon interval for vdev id %d", + pmo_vdev_get_id(vdev)); + + return beacon_interval; +} + +/** + * pmo_core_calculate_listen_interval() - Calculate vdev listen interval + * @vdev: objmgr vdev handle + * @vdev_ctx: pmo vdev priv ctx + * @listen_interval: listen interval which is computed for vdev + * + * Return: QDF_STATUS + */ +static QDF_STATUS pmo_core_calculate_listen_interval( + struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx, + uint32_t *listen_interval) +{ + uint32_t max_mod_dtim, max_dtim; + uint32_t beacon_interval_mod; + struct pmo_psoc_cfg *psoc_cfg = &vdev_ctx->pmo_psoc_ctx->psoc_cfg; + struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); + + if (psoc_cfg->sta_dynamic_dtim) { + *listen_interval = psoc_cfg->sta_dynamic_dtim; + } else if ((psoc_cfg->sta_mod_dtim) && + (psoc_cfg->sta_max_li_mod_dtim)) { + /* + * When the system is in suspend + * (maximum beacon will be at 1s == 10) + * If maxModulatedDTIM ((MAX_LI_VAL = 10) / AP_DTIM) + * equal or larger than MDTIM + * (configured in WCNSS_qcom_cfg.ini) + * Set LI to MDTIM * AP_DTIM + * If Dtim = 2 and Mdtim = 2 then LI is 4 + * Else + * Set LI to maxModulatedDTIM * AP_DTIM + */ + beacon_interval_mod = + pmo_core_get_vdev_beacon_interval(vdev) / 100; + if (beacon_interval_mod == 0) + beacon_interval_mod = 1; + + max_dtim = pmo_core_get_vdev_dtim_period(vdev) * + beacon_interval_mod; + + if (!max_dtim) { + pmo_err("Invalid dtim period"); + return QDF_STATUS_E_INVAL; + } + + max_mod_dtim = psoc_cfg->sta_max_li_mod_dtim / max_dtim; + + if (max_mod_dtim <= 0) + max_mod_dtim = 1; + + if (max_mod_dtim >= psoc_cfg->sta_mod_dtim) { + *listen_interval = + (psoc_cfg->sta_mod_dtim * + pmo_core_get_vdev_dtim_period(vdev)); + } else { + *listen_interval = + (max_mod_dtim * + pmo_core_get_vdev_dtim_period(vdev)); + } + } else { + /* Get Listen Interval */ + if (QDF_IS_STATUS_ERROR(ucfg_mlme_get_listen_interval(psoc, + listen_interval))) { + pmo_err("Failed to get value for listen interval"); + *listen_interval = cfg_default(CFG_LISTEN_INTERVAL); + } + } + return QDF_STATUS_SUCCESS; +} + +static void pmo_configure_vdev_suspend_params( + struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx) +{ + QDF_STATUS ret; + uint8_t vdev_id; + enum QDF_OPMODE opmode = pmo_core_get_vdev_op_mode(vdev); + struct pmo_psoc_cfg *psoc_cfg = &vdev_ctx->pmo_psoc_ctx->psoc_cfg; + uint8_t ito_repeat_count_value = 0; + uint32_t non_wow_inactivity_time, wow_inactivity_time; + + pmo_enter(); + + vdev_id = pmo_vdev_get_id(vdev); + if (!PMO_VDEV_IN_STA_MODE(opmode)) + return; + ret = pmo_tgt_send_vdev_sta_ps_param(vdev, + pmo_sta_ps_param_inactivity_time, + psoc_cfg->wow_data_inactivity_timeout); + if (QDF_IS_STATUS_ERROR(ret)) { + pmo_debug("Failed to Set wow inactivity timeout vdevId %d", + vdev_id); + } + + non_wow_inactivity_time = psoc_cfg->ps_data_inactivity_timeout; + wow_inactivity_time = psoc_cfg->wow_data_inactivity_timeout; + /* + * To keep ito repeat count same in wow mode as in non wow mode, + * modulating ito repeat count value. + */ + ito_repeat_count_value = (non_wow_inactivity_time / + wow_inactivity_time) * + psoc_cfg->ito_repeat_count; + if (ito_repeat_count_value) + ret = pmo_tgt_send_vdev_sta_ps_param(vdev, + pmo_sta_ps_param_ito_repeat_count, + psoc_cfg->wow_data_inactivity_timeout); + if (QDF_IS_STATUS_ERROR(ret)) { + pmo_err("Failed to Set ito repeat count vdevId %d", + vdev_id); + } + + pmo_exit(); +} + +static void pmo_configure_vdev_resume_params( + struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx) +{ + QDF_STATUS ret; + uint8_t vdev_id; + enum QDF_OPMODE opmode = pmo_core_get_vdev_op_mode(vdev); + struct pmo_psoc_cfg *psoc_cfg = &vdev_ctx->pmo_psoc_ctx->psoc_cfg; + + pmo_enter(); + + vdev_id = pmo_vdev_get_id(vdev); + if (!PMO_VDEV_IN_STA_MODE(opmode)) + return; + ret = pmo_tgt_send_vdev_sta_ps_param(vdev, + pmo_sta_ps_param_inactivity_time, + psoc_cfg->ps_data_inactivity_timeout); + if (QDF_IS_STATUS_ERROR(ret)) { + pmo_debug("Failed to Set inactivity timeout vdevId %d", + vdev_id); + } + + pmo_exit(); +} + +/** + * pmo_core_set_vdev_suspend_dtim() - set suspend dtim parameters in fw + * @psoc: objmgr psoc handle + * @vdev: objmgr vdev handle + * @vdev_ctx: pmo vdev priv ctx + * + * Return: none + */ +static void pmo_core_set_vdev_suspend_dtim(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx) +{ + QDF_STATUS ret; + uint8_t vdev_id; + enum QDF_OPMODE opmode = pmo_core_get_vdev_op_mode(vdev); + uint32_t listen_interval = cfg_default(CFG_LISTEN_INTERVAL); + + vdev_id = pmo_vdev_get_id(vdev); + if (PMO_VDEV_IN_STA_MODE(opmode) && + pmo_core_get_vdev_dtim_period(vdev) != 0) { + /* calculate listen interval */ + ret = pmo_core_calculate_listen_interval(vdev, vdev_ctx, + &listen_interval); + if (ret != QDF_STATUS_SUCCESS) { + /* even it fails continue fwr will take default LI */ + pmo_debug("Fail to calculate listen interval"); + } + ret = pmo_tgt_vdev_update_param_req(vdev, + pmo_vdev_param_listen_interval, + listen_interval); + if (QDF_IS_STATUS_ERROR(ret)) { + /* even it fails continue fwr will take default LI */ + pmo_debug("Failed to Set Listen Interval vdevId %d", + vdev_id); + } + pmo_debug("Set Listen Interval vdevId %d Listen Intv %d", + vdev_id, listen_interval); + + pmo_core_vdev_set_restore_dtim(vdev, true); + } +} + +/* + * pmo_is_listen_interval_user_set() - Check if listen interval is configured + * by user or not + * @vdev_ctx: PMO vdev private object + * + * Return: true if listen interval is user configured else false + */ +static inline +bool pmo_is_listen_interval_user_set(struct pmo_vdev_priv_obj *vdev_ctx) +{ + bool retval; + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + retval = vdev_ctx->dyn_modulated_dtim_enabled + || vdev_ctx->dyn_listen_interval; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + return retval; +} + +/** + * pmo_core_set_suspend_dtim() - set suspend dtim + * @psoc: objmgr psoc handle + * + * Return: none + */ +static void pmo_core_set_suspend_dtim(struct wlan_objmgr_psoc *psoc) +{ + uint8_t vdev_id; + struct wlan_objmgr_vdev *vdev; + struct pmo_vdev_priv_obj *vdev_ctx; + struct pmo_psoc_priv_obj *psoc_ctx; + bool li_offload_support = false; + QDF_STATUS status; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + li_offload_support = psoc_ctx->caps.li_offload; + } + + if (li_offload_support) + pmo_debug("listen interval offload support is enabled"); + + /* Iterate through VDEV list */ + for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) { + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) + continue; + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + continue; + + vdev_ctx = pmo_vdev_get_priv(vdev); + if (!pmo_is_listen_interval_user_set(vdev_ctx) + && !li_offload_support) + pmo_core_set_vdev_suspend_dtim(psoc, vdev, vdev_ctx); + pmo_configure_vdev_suspend_params(psoc, vdev, vdev_ctx); + pmo_vdev_put_ref(vdev); + } +} + +/** + * pmo_core_update_wow_bus_suspend() - set wow bus suspend flag + * @psoc: objmgr psoc handle + * @psoc_ctx: pmo psoc priv ctx + * @val: true for enable else false + * Return: none + */ +static inline +void pmo_core_update_wow_bus_suspend(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_priv_obj *psoc_ctx, int val) +{ + qdf_spin_lock_bh(&psoc_ctx->lock); + psoc_ctx->wow.is_wow_bus_suspended = val; + qdf_spin_unlock_bh(&psoc_ctx->lock); + pmo_tgt_psoc_update_wow_bus_suspend_state(psoc, val); +} + +/* Define for conciseness */ +#define BM_LEN PMO_WOW_MAX_EVENT_BM_LEN +#define EV_NLO WOW_NLO_SCAN_COMPLETE_EVENT +#define EV_PWR WOW_CHIP_POWER_FAILURE_DETECT_EVENT + +void pmo_core_configure_dynamic_wake_events(struct wlan_objmgr_psoc *psoc) +{ + int vdev_id; + uint32_t adapter_type; + uint32_t enable_mask[BM_LEN]; + uint32_t disable_mask[BM_LEN]; + struct wlan_objmgr_vdev *vdev; + struct pmo_psoc_priv_obj *psoc_ctx; + bool enable_configured; + bool disable_configured; + + /* Iterate through VDEV list */ + for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) { + + enable_configured = false; + disable_configured = false; + + qdf_mem_zero(enable_mask, sizeof(uint32_t) * BM_LEN); + qdf_mem_zero(disable_mask, sizeof(uint32_t) * BM_LEN); + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) + continue; + + if (ucfg_scan_get_pno_in_progress(vdev)) { + if (ucfg_scan_get_pno_match(vdev)) { + pmo_set_wow_event_bitmap(EV_NLO, + BM_LEN, + enable_mask); + enable_configured = true; + } else { + pmo_set_wow_event_bitmap(EV_NLO, + BM_LEN, + disable_mask); + disable_configured = true; + } + } + + adapter_type = pmo_get_vdev_opmode(vdev); + + psoc_ctx = pmo_psoc_get_priv(psoc); + + if (psoc_ctx->psoc_cfg.auto_power_save_fail_mode == + PMO_FW_TO_SEND_WOW_IND_ON_PWR_FAILURE && + (adapter_type == QDF_STA_MODE || + adapter_type == QDF_P2P_CLIENT_MODE)) { + if (psoc_ctx->is_device_in_low_pwr_mode && + psoc_ctx->is_device_in_low_pwr_mode(vdev_id)) { + pmo_set_wow_event_bitmap(EV_PWR, + BM_LEN, + enable_mask); + enable_configured = true; + } + } + + if (enable_configured) + pmo_tgt_enable_wow_wakeup_event(vdev, enable_mask); + if (disable_configured) + pmo_tgt_disable_wow_wakeup_event(vdev, disable_mask); + } + +} + +static void pmo_core_enable_runtime_pm_offloads(struct wlan_objmgr_psoc *psoc) +{ + uint8_t vdev_id; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + /* Iterate through VDEV list */ + for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) { + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) + continue; + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + continue; + + pmo_register_action_frame_patterns(vdev, QDF_RUNTIME_SUSPEND); + pmo_vdev_put_ref(vdev); + } +} + +static void pmo_core_disable_runtime_pm_offloads(struct wlan_objmgr_psoc *psoc) +{ + uint8_t vdev_id; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + /* Iterate through VDEV list */ + for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) { + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) + continue; + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + continue; + + pmo_clear_action_frame_patterns(vdev); + pmo_vdev_put_ref(vdev); + } +} + +/** + * pmo_core_psoc_configure_suspend(): configure suspend req events + * @psoc: objmgr psoc + * @is_runtime_pm: indicate if it is used by runtime PM + * + * Responsibility of the caller to take the psoc reference. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +static QDF_STATUS pmo_core_psoc_configure_suspend(struct wlan_objmgr_psoc *psoc, + bool is_runtime_pm) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_enter(); + + psoc_ctx = pmo_psoc_get_priv(psoc); + + if (is_runtime_pm) + pmo_core_enable_runtime_pm_offloads(psoc); + + if (pmo_core_is_wow_applicable(psoc)) { + pmo_debug("WOW Suspend"); + pmo_core_apply_lphb(psoc); + /* + * Dynamic wake events should not be needed for runtime PM. + * Any wake events can be configed by default if they are + * really needed for runtime PM. In fact, most of them are + * only needed for system suspend. + */ + if (!is_runtime_pm) + pmo_core_configure_dynamic_wake_events(psoc); + pmo_core_update_wow_enable(psoc_ctx, true); + pmo_core_update_wow_enable_cmd_sent(psoc_ctx, false); + } else { + pmo_debug("Non WOW PDEV Suspend"); + pmo_core_update_wow_enable(psoc_ctx, false); + } + + /* + * For runtime PM, since system is awake, DTIM related commands + * do not have to be sent with WOW sequence. They can be sent + * through other paths which will just trigger a runtime resume. + */ + if (!is_runtime_pm) + pmo_core_set_suspend_dtim(psoc); + + /* + * To handle race between hif_pci_suspend and unpause/pause tx handler. + * This happens when host sending WMI_WOW_ENABLE_CMDID to FW and receive + * WMI_TX_PAUSE_EVENT with ACTON_UNPAUSE almost at same time. + */ + pmo_core_update_wow_bus_suspend(psoc, psoc_ctx, true); + + pmo_exit(); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_core_psoc_user_space_suspend_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + QDF_STATUS status; + + pmo_enter(); + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + goto out; + } + + status = pmo_core_psoc_configure_suspend(psoc, false); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to configure suspend"); + + pmo_psoc_put_ref(psoc); +out: + pmo_exit(); + + return status; +} + +/** + * pmo_core_set_vdev_resume_dtim() - set resume dtim parameters in fw + * @psoc: objmgr psoc handle + * @vdev: objmgr vdev handle + * @vdev_ctx: pmo vdev priv ctx + * + * Return: none + */ +static void pmo_core_set_vdev_resume_dtim(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + struct pmo_vdev_priv_obj *vdev_ctx) +{ + QDF_STATUS ret; + uint8_t vdev_id; + enum QDF_OPMODE opmode = pmo_core_get_vdev_op_mode(vdev); + int32_t cfg_data_val = 0; + + vdev_id = pmo_vdev_get_id(vdev); + if ((PMO_VDEV_IN_STA_MODE(opmode)) && + (pmo_core_vdev_get_restore_dtim(vdev))) { + /* Get Listen Interval */ + if (QDF_IS_STATUS_ERROR(ucfg_mlme_get_listen_interval(psoc, + &cfg_data_val))) { + pmo_err("Failed to get value for listen interval"); + cfg_data_val = cfg_default(CFG_LISTEN_INTERVAL); + } + + ret = pmo_tgt_vdev_update_param_req(vdev, + pmo_vdev_param_listen_interval, cfg_data_val); + if (QDF_IS_STATUS_ERROR(ret)) { + /* Even it fails continue Fw will take default LI */ + pmo_err("Failed to Set Listen Interval vdevId %d", + vdev_id); + } + pmo_debug("Set Listen Interval vdevId %d Listen Intv %d", + vdev_id, cfg_data_val); + pmo_core_vdev_set_restore_dtim(vdev, false); + } +} + +/** + * pmo_core_set_resume_dtim() - set resume time dtim + * @psoc: objmgr psoc handle + * + * Return: none + */ +static void pmo_core_set_resume_dtim(struct wlan_objmgr_psoc *psoc) +{ + uint8_t vdev_id; + struct wlan_objmgr_vdev *vdev; + struct pmo_vdev_priv_obj *vdev_ctx; + struct pmo_psoc_priv_obj *psoc_ctx; + bool li_offload_support = false; + QDF_STATUS status; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + li_offload_support = psoc_ctx->caps.li_offload; + } + + if (li_offload_support) + pmo_debug("listen interval offload support is enabled"); + + /* Iterate through VDEV list */ + for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) { + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) + continue; + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + continue; + + vdev_ctx = pmo_vdev_get_priv(vdev); + if (!pmo_is_listen_interval_user_set(vdev_ctx) + && !li_offload_support) + pmo_core_set_vdev_resume_dtim(psoc, vdev, vdev_ctx); + pmo_configure_vdev_resume_params(psoc, vdev, vdev_ctx); + pmo_vdev_put_ref(vdev); + } +} + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2) +/** + * pmo_unpause_vdev - unpause all vdev + * @psoc: objmgr psoc handle + * + * unpause all vdev aftter resume/coming out of wow mode + * + * Return: none + */ +static void pmo_unpause_all_vdev(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_priv_obj *psoc_ctx) +{ + uint8_t vdev_id; + + /* Iterate through VDEV list */ + for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) { + /* When host resumes, by default unpause all active vdev */ + if (pmo_core_vdev_get_pause_bitmap(psoc_ctx, vdev_id)) { + cdp_fc_vdev_unpause(pmo_core_psoc_get_dp_handle(psoc), + vdev_id, + 0xffffffff, 0); + if (psoc_ctx->pause_bitmap_notifier) + psoc_ctx->pause_bitmap_notifier(vdev_id, 0); + } + } +} +#else +static inline void pmo_unpause_all_vdev(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_priv_obj *psoc_ctx) +{ +} +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + +/** + * pmo_core_psoc_configure_resume(): configure events after bus resume + * @psoc: objmgr psoc + * @is_runtime_pm: indicate if it is used by runtime PM + * + * Responsibility of the caller to take the psoc reference. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +static QDF_STATUS pmo_core_psoc_configure_resume(struct wlan_objmgr_psoc *psoc, + bool is_runtime_pm) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_enter(); + + psoc_ctx = pmo_psoc_get_priv(psoc); + if (is_runtime_pm) + pmo_core_disable_runtime_pm_offloads(psoc); + + /* + * For runtime PM, since system is awake, DTIM related commands + * do not have to be sent with WOW sequence. They can be sent + * through other paths which will just trigger a runtime resume. + */ + if (!is_runtime_pm) + pmo_core_set_resume_dtim(psoc); + pmo_core_update_wow_bus_suspend(psoc, psoc_ctx, false); + pmo_unpause_all_vdev(psoc, psoc_ctx); + + pmo_exit(); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_core_psoc_user_space_resume_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_enter(); + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + goto out; + } + + status = pmo_core_psoc_configure_resume(psoc, false); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to configure resume"); + + pmo_psoc_put_ref(psoc); +out: + pmo_exit(); + + return status; +} + +/** + * pmo_core_enable_wow_in_fw() - enable wow in fw + * @psoc: objmgr psoc handle + * @psoc_ctx: pmo psoc private ctx + * @wow_params: collection of wow enable override parameters + * @type: type of wow suspend + * + * Return: QDF status + */ +static QDF_STATUS +pmo_core_enable_wow_in_fw(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_priv_obj *psoc_ctx, + struct pmo_wow_enable_params *wow_params, + enum qdf_suspend_type type) +{ + int host_credits, wmi_pending_cmds; + struct pmo_wow_cmd_params param = {0}; + struct pmo_psoc_cfg *psoc_cfg = &psoc_ctx->psoc_cfg; + QDF_STATUS status; + + pmo_enter(); + qdf_event_reset(&psoc_ctx->wow.target_suspend); + pmo_core_set_wow_nack(psoc_ctx, false); + host_credits = pmo_tgt_psoc_get_host_credits(psoc); + wmi_pending_cmds = pmo_tgt_psoc_get_pending_cmnds(psoc); + pmo_debug("Credits:%d; Pending_Cmds: %d", + host_credits, wmi_pending_cmds); + + param.enable = true; + if (wow_params->is_unit_test) + param.flags = WMI_WOW_FLAG_UNIT_TEST_ENABLE; + + switch (wow_params->interface_pause) { + default: + pmo_err("Invalid interface pause setting: %d", + wow_params->interface_pause); + /* intentional fall-through to default */ + case PMO_WOW_INTERFACE_PAUSE_DEFAULT: + param.can_suspend_link = + htc_can_suspend_link( + pmo_core_psoc_get_htc_handle(psoc)); + break; + case PMO_WOW_INTERFACE_PAUSE_ENABLE: + param.can_suspend_link = true; + break; + case PMO_WOW_INTERFACE_PAUSE_DISABLE: + param.can_suspend_link = false; + break; + } + + switch (wow_params->resume_trigger) { + default: + pmo_err("Invalid resume trigger setting: %d", + wow_params->resume_trigger); + /* intentional fall-through to default */ + case PMO_WOW_RESUME_TRIGGER_DEFAULT: + case PMO_WOW_RESUME_TRIGGER_GPIO: + /* + * GPIO is currently implicit. This means you can't actually + * force GPIO if a platform's default wake trigger is HTC wakeup + */ + break; + case PMO_WOW_RESUME_TRIGGER_HTC_WAKEUP: + param.flags |= WMI_WOW_FLAG_DO_HTC_WAKEUP; + break; + } + + if (psoc_ctx->psoc_cfg.d0_wow_supported && + !psoc_ctx->caps.unified_wow && + !param.can_suspend_link) { + psoc_ctx->wow.wow_state = pmo_wow_state_legacy_d0; + } else if (param.can_suspend_link) { + psoc_ctx->wow.wow_state = pmo_wow_state_unified_d3; + } else { + psoc_ctx->wow.wow_state = pmo_wow_state_unified_d0; + } + + if (htc_can_suspend_link(pmo_core_psoc_get_htc_handle(psoc))) { + if (qdf_is_drv_connected()) { + pmo_info("drv wow is enabled"); + param.flags |= WMI_WOW_FLAG_ENABLE_DRV_PCIE_L1SS_SLEEP; + } else { + pmo_info("non-drv wow is enabled"); + } + } else { + pmo_info("Prevent link down, non-drv wow is enabled"); + } + + if (type == QDF_SYSTEM_SUSPEND) { + pmo_info("system suspend wow"); + param.flags |= WMI_WOW_FLAG_SYSTEM_SUSPEND_WOW; + } else { + pmo_info("RTPM wow"); + } + + if ((psoc_cfg) && + (psoc_cfg->is_mod_dtim_on_sys_suspend_enabled)) { + pmo_info("mod DTIM enabled"); + param.flags |= WMI_WOW_FLAG_MOD_DTIM_ON_SYS_SUSPEND; + } + + status = pmo_tgt_psoc_send_wow_enable_req(psoc, ¶m); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to enable wow in fw"); + goto out; + } + + pmo_tgt_update_target_suspend_flag(psoc, true); + + status = qdf_wait_for_event_completion(&psoc_ctx->wow.target_suspend, + PMO_TARGET_SUSPEND_TIMEOUT); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_err("Failed to receive WoW Enable Ack from FW"); + pmo_err("Credits:%d; Pending_Cmds: %d", + pmo_tgt_psoc_get_host_credits(psoc), + pmo_tgt_psoc_get_pending_cmnds(psoc)); + pmo_tgt_update_target_suspend_flag(psoc, false); + qdf_trigger_self_recovery(psoc, QDF_SUSPEND_TIMEOUT); + goto out; + } + + if (pmo_core_get_wow_nack(psoc_ctx)) { + pmo_err("FW not ready to WOW"); + pmo_tgt_update_target_suspend_flag(psoc, false); + status = QDF_STATUS_E_AGAIN; + goto out; + } + + host_credits = pmo_tgt_psoc_get_host_credits(psoc); + wmi_pending_cmds = pmo_tgt_psoc_get_pending_cmnds(psoc); + + if (host_credits < PMO_WOW_REQUIRED_CREDITS) { + pmo_err("No Credits after HTC ACK:%d, pending_cmds:%d," + "cannot resume back", host_credits, wmi_pending_cmds); + htc_dump_counter_info(pmo_core_psoc_get_htc_handle(psoc)); + qdf_trigger_self_recovery(psoc, QDF_SUSPEND_NO_CREDIT); + } + pmo_debug("WOW enabled successfully in fw: credits:%d pending_cmds: %d", + host_credits, wmi_pending_cmds); + + pmo_core_update_wow_enable_cmd_sent(psoc_ctx, true); + +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_psoc_suspend_target(struct wlan_objmgr_psoc *psoc, + int disable_target_intr) +{ + QDF_STATUS status; + struct pmo_suspend_params param; + struct pmo_psoc_priv_obj *psoc_ctx; + void *dp_soc = pmo_core_psoc_get_dp_handle(psoc); + + pmo_enter(); + + psoc_ctx = pmo_psoc_get_priv(psoc); + + cdp_process_target_suspend_req(dp_soc, OL_TXRX_PDEV_ID); + qdf_event_reset(&psoc_ctx->wow.target_suspend); + param.disable_target_intr = disable_target_intr; + status = pmo_tgt_psoc_send_supend_req(psoc, ¶m); + if (status != QDF_STATUS_SUCCESS) + goto out; + + pmo_tgt_update_target_suspend_flag(psoc, true); + + status = qdf_wait_for_event_completion(&psoc_ctx->wow.target_suspend, + PMO_TARGET_SUSPEND_TIMEOUT); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_err("Failed to get ACK from firmware for pdev suspend"); + pmo_tgt_update_target_suspend_flag(psoc, false); + qdf_trigger_self_recovery(psoc, QDF_SUSPEND_TIMEOUT); + } + +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_psoc_bus_suspend_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type, + struct pmo_wow_enable_params *wow_params) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + bool wow_mode_selected = false; + qdf_time_t begin, end; + + pmo_enter(); + if (!psoc) { + pmo_err("psoc is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + if (!wow_params) { + pmo_err("wow_params is NULL"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + goto out; + } + + psoc_ctx = pmo_psoc_get_priv(psoc); + + wow_mode_selected = pmo_core_is_wow_enabled(psoc_ctx); + pmo_debug("wow mode selected %d", wow_mode_selected); + + begin = qdf_get_log_timestamp_usecs(); + if (wow_mode_selected) + status = pmo_core_enable_wow_in_fw(psoc, psoc_ctx, + wow_params, + type); + else + status = pmo_core_psoc_suspend_target(psoc, 0); + end = qdf_get_log_timestamp_usecs(); + pmo_debug("fw took total time %lu microseconds to enable wow", + end - begin); + + pmo_psoc_put_ref(psoc); +out: + pmo_exit(); + + return status; +} + +#ifdef FEATURE_RUNTIME_PM +#define PMO_CORE_PSOC_RUNTIME_PM_QDF_BUG(__condition) ({ \ + typeof(__condition) condition = __condition; \ + if (condition && !qdf_is_fw_down()) \ + QDF_BUG(0); \ +}) + +QDF_STATUS pmo_core_psoc_bus_runtime_suspend(struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_suspend_cb pld_cb) +{ + void *hif_ctx; + void *dp_soc; + uint8_t pdev_id; + void *htc_ctx; + QDF_STATUS status; + int ret; + struct pmo_wow_enable_params wow_params = {0}; + qdf_time_t begin, end; + int pending; + + pmo_enter(); + + if (!psoc) { + pmo_err("psoc is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + goto out; + } + + hif_ctx = pmo_core_psoc_get_hif_handle(psoc); + dp_soc = pmo_core_psoc_get_dp_handle(psoc); + pdev_id = pmo_core_psoc_get_txrx_handle(psoc); + htc_ctx = pmo_core_psoc_get_htc_handle(psoc); + if (!hif_ctx || !dp_soc || !htc_ctx || + pdev_id == OL_TXRX_INVALID_PDEV_ID) { + pmo_err("Invalid hif: %pK, dp: %pK, pdev_id: %d, htc: %pK", + hif_ctx, dp_soc, pdev_id, htc_ctx); + status = QDF_STATUS_E_INVAL; + goto dec_psoc_ref; + } + + wow_params.interface_pause = PMO_WOW_INTERFACE_PAUSE_ENABLE; + wow_params.resume_trigger = PMO_WOW_RESUME_TRIGGER_GPIO; + + ret = hif_pre_runtime_suspend(hif_ctx); + if (ret) { + status = qdf_status_from_os_return(ret); + goto runtime_failure; + } + + status = cdp_runtime_suspend(dp_soc, pdev_id); + if (status != QDF_STATUS_SUCCESS) + goto runtime_failure; + + ret = htc_runtime_suspend(htc_ctx); + if (ret) { + status = qdf_status_from_os_return(ret); + goto cdp_runtime_resume; + } + + status = pmo_tgt_psoc_set_runtime_pm_inprogress(psoc, true); + if (status != QDF_STATUS_SUCCESS) + goto resume_htc; + + status = pmo_core_psoc_configure_suspend(psoc, true); + if (status != QDF_STATUS_SUCCESS) + goto resume_htc; + + hif_pm_set_link_state(hif_ctx, HIF_PM_LINK_STATE_DOWN); + + status = pmo_core_psoc_bus_suspend_req(psoc, QDF_RUNTIME_SUSPEND, + &wow_params); + if (status != QDF_STATUS_SUCCESS) + goto pmo_resume_configure; + + ret = hif_runtime_suspend(hif_ctx); + if (ret) { + status = qdf_status_from_os_return(ret); + goto pmo_bus_resume; + } + + pending = cdp_rx_get_pending(cds_get_context(QDF_MODULE_ID_SOC)); + if (pending) { + pmo_debug("Prevent suspend, RX frame pending %d", pending); + status = QDF_STATUS_E_BUSY; + goto resume_hif; + } + + if (pld_cb) { + begin = qdf_get_log_timestamp_usecs(); + ret = pld_cb(); + end = qdf_get_log_timestamp_usecs(); + pmo_debug("runtime pci bus suspend took total time %lu microseconds", + end - begin); + + if (ret) { + status = qdf_status_from_os_return(ret); + goto resume_hif; + } + } + + hif_process_runtime_suspend_success(hif_ctx); + + goto dec_psoc_ref; + +resume_hif: + PMO_CORE_PSOC_RUNTIME_PM_QDF_BUG(hif_runtime_resume(hif_ctx)); + +pmo_bus_resume: + PMO_CORE_PSOC_RUNTIME_PM_QDF_BUG(QDF_STATUS_SUCCESS != + pmo_core_psoc_bus_resume_req(psoc, QDF_RUNTIME_SUSPEND)); + +pmo_resume_configure: + PMO_CORE_PSOC_RUNTIME_PM_QDF_BUG(QDF_STATUS_SUCCESS != + pmo_core_psoc_configure_resume(psoc, true)); + + hif_pm_set_link_state(hif_ctx, HIF_PM_LINK_STATE_UP); + +resume_htc: + PMO_CORE_PSOC_RUNTIME_PM_QDF_BUG(QDF_STATUS_SUCCESS != + pmo_tgt_psoc_set_runtime_pm_inprogress(psoc, false)); + PMO_CORE_PSOC_RUNTIME_PM_QDF_BUG(htc_runtime_resume(htc_ctx)); + +cdp_runtime_resume: + PMO_CORE_PSOC_RUNTIME_PM_QDF_BUG(QDF_STATUS_SUCCESS != + cdp_runtime_resume(dp_soc, pdev_id)); + +runtime_failure: + hif_process_runtime_suspend_failure(hif_ctx); + +dec_psoc_ref: + pmo_psoc_put_ref(psoc); + +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_psoc_bus_runtime_resume(struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_resume_cb pld_cb) +{ + int ret; + void *hif_ctx; + void *dp_soc; + uint8_t pdev_id; + void *htc_ctx; + QDF_STATUS status; + qdf_time_t begin, end; + + pmo_enter(); + + if (!psoc) { + pmo_err("psoc is NULL"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + goto out; + } + + hif_ctx = pmo_core_psoc_get_hif_handle(psoc); + dp_soc = pmo_core_psoc_get_dp_handle(psoc); + pdev_id = pmo_core_psoc_get_txrx_handle(psoc); + htc_ctx = pmo_core_psoc_get_htc_handle(psoc); + if (!hif_ctx || !dp_soc || !htc_ctx || + pdev_id == OL_TXRX_INVALID_PDEV_ID) { + pmo_err("Invalid hif: %pK, dp: %pK, pdev_id: %d, htc: %pK", + hif_ctx, dp_soc, pdev_id, htc_ctx); + status = QDF_STATUS_E_INVAL; + goto dec_psoc_ref; + } + + hif_pre_runtime_resume(hif_ctx); + if (pld_cb) { + begin = qdf_get_log_timestamp_usecs(); + ret = pld_cb(); + end = qdf_get_log_timestamp_usecs(); + pmo_debug("pci bus resume took total time %lu microseconds", + end - begin); + if (ret) { + status = QDF_STATUS_E_FAILURE; + goto fail; + } + } + + if (hif_runtime_resume(hif_ctx)) { + status = QDF_STATUS_E_FAILURE; + goto fail; + } + + status = pmo_core_psoc_bus_resume_req(psoc, QDF_RUNTIME_SUSPEND); + if (status != QDF_STATUS_SUCCESS) + goto fail; + + hif_pm_set_link_state(hif_ctx, HIF_PM_LINK_STATE_UP); + + status = pmo_core_psoc_configure_resume(psoc, true); + if (status != QDF_STATUS_SUCCESS) + goto fail; + + status = pmo_tgt_psoc_set_runtime_pm_inprogress(psoc, false); + if (status != QDF_STATUS_SUCCESS) + goto fail; + + if (htc_runtime_resume(htc_ctx)) { + status = QDF_STATUS_E_FAILURE; + goto fail; + } + + status = cdp_runtime_resume(dp_soc, pdev_id); + if (status != QDF_STATUS_SUCCESS) + goto fail; + + hif_process_runtime_resume_success(hif_ctx); + +fail: + if (status != QDF_STATUS_SUCCESS) + qdf_trigger_self_recovery(psoc, QDF_RESUME_TIMEOUT); + +dec_psoc_ref: + pmo_psoc_put_ref(psoc); + +out: + pmo_exit(); + + return status; +} +#endif + +/** + * pmo_core_psoc_send_host_wakeup_ind_to_fw() - send wakeup ind to fw + * @psoc: objmgr psoc handle + * @psoc_ctx: pmo psoc private context + * + * Sends host wakeup indication to FW. On receiving this indication, + * FW will come out of WOW. + * + * Return: QDF status + */ +static +QDF_STATUS pmo_core_psoc_send_host_wakeup_ind_to_fw( + struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_priv_obj *psoc_ctx) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_enter(); + qdf_event_reset(&psoc_ctx->wow.target_resume); + + status = pmo_tgt_psoc_send_host_wakeup_ind(psoc); + if (status) { + status = QDF_STATUS_E_FAILURE; + goto out; + } + pmo_info("Host wakeup indication sent to fw"); + + status = qdf_wait_for_event_completion(&psoc_ctx->wow.target_resume, + PMO_RESUME_TIMEOUT); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Timeout waiting for resume event from FW"); + pmo_err("Pending commands %d credits %d", + pmo_tgt_psoc_get_pending_cmnds(psoc), + pmo_tgt_psoc_get_host_credits(psoc)); + qdf_trigger_self_recovery(psoc, QDF_RESUME_TIMEOUT); + } else { + pmo_debug("Host wakeup received"); + } + + if (status == QDF_STATUS_SUCCESS) + pmo_tgt_update_target_suspend_flag(psoc, false); +out: + pmo_exit(); + + return status; +} + +/** + * pmo_core_psoc_disable_wow_in_fw() - Disable wow in bus resume context. + * @psoc: objmgr psoc handle + * @psoc_ctx: pmo psoc private context + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +static +QDF_STATUS pmo_core_psoc_disable_wow_in_fw(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_priv_obj *psoc_ctx) +{ + QDF_STATUS ret; + + pmo_enter(); + ret = pmo_core_psoc_send_host_wakeup_ind_to_fw(psoc, psoc_ctx); + if (ret != QDF_STATUS_SUCCESS) + goto out; + + pmo_core_update_wow_enable_cmd_sent(psoc_ctx, false); + + /* To allow the tx pause/unpause events */ + pmo_core_update_wow_bus_suspend(psoc, psoc_ctx, false); + /* Unpause the vdev as we are resuming */ + pmo_unpause_all_vdev(psoc, psoc_ctx); +out: + pmo_exit(); + + return ret; +} + +/** + * pmo_core_psoc_resume_target() - resume target + * @psoc: objmgr psoc handle + * @psoc_ctx: pmo psoc private context + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +static +QDF_STATUS pmo_core_psoc_resume_target(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_priv_obj *psoc_ctx) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_enter(); + qdf_event_reset(&psoc_ctx->wow.target_resume); + + status = pmo_tgt_psoc_send_target_resume_req(psoc); + if (status != QDF_STATUS_SUCCESS) { + status = QDF_STATUS_E_FAILURE; + goto out; + } + + status = qdf_wait_single_event(&psoc_ctx->wow.target_resume, + PMO_RESUME_TIMEOUT); + if (status != QDF_STATUS_SUCCESS) { + pmo_fatal("Timeout waiting for resume event from FW"); + pmo_fatal("Pending commands %d credits %d", + pmo_tgt_psoc_get_pending_cmnds(psoc), + pmo_tgt_psoc_get_host_credits(psoc)); + qdf_trigger_self_recovery(psoc, QDF_RESUME_TIMEOUT); + } else { + pmo_debug("Host wakeup received"); + } + + if (status == QDF_STATUS_SUCCESS) + pmo_tgt_update_target_suspend_flag(psoc, false); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_psoc_bus_resume_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + bool wow_mode; + QDF_STATUS status; + qdf_time_t begin, end; + + pmo_enter(); + if (!psoc) { + pmo_err("psoc is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + goto out; + } + + psoc_ctx = pmo_psoc_get_priv(psoc); + wow_mode = pmo_core_is_wow_enabled(psoc_ctx); + pmo_debug("wow mode %d", wow_mode); + + pmo_core_update_wow_initial_wake_up(psoc_ctx, 0); + + /* If target was not suspended, bail out */ + if (qdf_is_fw_down() || !pmo_tgt_is_target_suspended(psoc)) { + pmo_psoc_put_ref(psoc); + goto out; + } + + begin = qdf_get_log_timestamp_usecs(); + if (wow_mode) + status = pmo_core_psoc_disable_wow_in_fw(psoc, psoc_ctx); + else + status = pmo_core_psoc_resume_target(psoc, psoc_ctx); + end = qdf_get_log_timestamp_usecs(); + pmo_debug("fw took total time %lu microseconds to disable wow", + end - begin); + + pmo_psoc_put_ref(psoc); + +out: + pmo_exit(); + + return status; +} + +void pmo_core_psoc_target_suspend_acknowledge(void *context, bool wow_nack) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + struct wlan_objmgr_psoc *psoc = (struct wlan_objmgr_psoc *)context; + void *dp_soc = pmo_core_psoc_get_dp_handle(psoc); + QDF_STATUS status; + + pmo_enter(); + if (!psoc) { + pmo_err("psoc is null"); + goto out; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to get psoc reference"); + goto out; + } + + psoc_ctx = pmo_psoc_get_priv(psoc); + + pmo_core_set_wow_nack(psoc_ctx, wow_nack); + qdf_event_set(&psoc_ctx->wow.target_suspend); + if (!pmo_tgt_psoc_get_runtime_pm_in_progress(psoc)) { + if (wow_nack) + qdf_wake_lock_timeout_acquire( + &psoc_ctx->wow.wow_wake_lock, + PMO_WAKE_LOCK_TIMEOUT); + else + cdp_process_wow_ack_rsp(dp_soc, OL_TXRX_PDEV_ID); + } + + pmo_psoc_put_ref(psoc); +out: + pmo_exit(); +} + +void pmo_core_psoc_wakeup_host_event_received(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + pmo_enter(); + if (!psoc) { + pmo_err("psoc is null"); + goto out; + } + + psoc_ctx = pmo_psoc_get_priv(psoc); + psoc_ctx->wow.wow_state = pmo_wow_state_none; + qdf_event_set(&psoc_ctx->wow.target_resume); +out: + pmo_exit(); +} + +int pmo_core_psoc_is_target_wake_up_received(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + int ret = 0; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is NULL"); + ret = -EAGAIN; + goto out; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to get psoc reference"); + ret = -EAGAIN; + goto out; + } + + psoc_ctx = pmo_psoc_get_priv(psoc); + if (pmo_core_get_wow_initial_wake_up(psoc_ctx)) { + pmo_err("Target initial wake up received try again"); + ret = -EAGAIN; + } + + pmo_psoc_put_ref(psoc); +out: + pmo_exit(); + + return ret; +} + + +int pmo_core_psoc_clear_target_wake_up(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + int ret = 0; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is NULL"); + ret = -EAGAIN; + goto out; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to get psoc reference"); + ret = -EAGAIN; + goto out; + } + + psoc_ctx = pmo_psoc_get_priv(psoc); + pmo_core_update_wow_initial_wake_up(psoc_ctx, 0); + + pmo_psoc_put_ref(psoc); +out: + pmo_exit(); + + return ret; +} + +void pmo_core_psoc_handle_initial_wake_up(void *cb_ctx) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + struct wlan_objmgr_psoc *psoc = (struct wlan_objmgr_psoc *)cb_ctx; + + pmo_enter(); + if (!psoc) { + pmo_err("cb ctx/psoc is null"); + goto out; + } + + psoc_ctx = pmo_psoc_get_priv(psoc); + pmo_core_update_wow_initial_wake_up(psoc_ctx, 1); + +out: + pmo_exit(); +} + +QDF_STATUS pmo_core_config_listen_interval(struct wlan_objmgr_vdev *vdev, + uint32_t new_li) +{ + QDF_STATUS status; + uint8_t vdev_id; + uint32_t listen_interval; + struct pmo_vdev_priv_obj *vdev_ctx; + struct pmo_psoc_priv_obj *psoc_ctx; + struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); + + pmo_enter(); + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + vdev_id = pmo_vdev_get_id(vdev); + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + if (vdev_ctx->dyn_listen_interval == new_li) { + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + status = QDF_STATUS_SUCCESS; + pmo_debug("Listen Interval(%d) already set for vdev id %d", + new_li, vdev_id); + goto dec_ref; + } + + vdev_ctx->dyn_listen_interval = new_li; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + listen_interval = new_li ? new_li : cfg_default(CFG_LISTEN_INTERVAL); + + if (!new_li) { + /* Configure default LI as we do on resume */ + pmo_psoc_with_ctx(pmo_vdev_get_psoc(vdev), psoc_ctx) { + if (QDF_IS_STATUS_ERROR( + ucfg_mlme_get_listen_interval(psoc, + &listen_interval))) { + pmo_err("Failed to get listen interval"); + listen_interval = + cfg_default(CFG_LISTEN_INTERVAL); + } + } + } + + pmo_debug("Set Listen Interval %d for vdevId %d", listen_interval, + vdev_id); + status = pmo_tgt_vdev_update_param_req(vdev, + pmo_vdev_param_listen_interval, + listen_interval); + if (QDF_IS_STATUS_ERROR(status)) { + /* even it fails continue fwr will take default LI */ + pmo_err("Failed to Set Listen Interval"); + } + + /* Set it to Normal DTIM */ + status = pmo_tgt_vdev_update_param_req(vdev, + pmo_vdev_param_dtim_policy, + pmo_normal_dtim); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_err("Failed to set Normal DTIM for vdev id %d", vdev_id); + } else { + pmo_debug("Set DTIM Policy to Normal for vdev id %d", vdev_id); + pmo_core_vdev_set_restore_dtim(vdev, true); + } + +dec_ref: + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_config_modulated_dtim(struct wlan_objmgr_vdev *vdev, + uint32_t mod_dtim) +{ + struct pmo_vdev_priv_obj *vdev_ctx; + struct pmo_psoc_cfg *psoc_cfg; + bool prev_dtim_enabled; + uint32_t listen_interval; + uint32_t beacon_interval_mod; + uint32_t max_mod_dtim; + QDF_STATUS status; + uint8_t vdev_id; + uint32_t max_dtim; + + pmo_enter(); + + status = pmo_vdev_get_ref(vdev); + if (status != QDF_STATUS_SUCCESS) + goto out; + + vdev_id = pmo_vdev_get_id(vdev); + vdev_ctx = pmo_vdev_get_priv(vdev); + psoc_cfg = &vdev_ctx->pmo_psoc_ctx->psoc_cfg; + + /* Calculate Maximum allowed modulated DTIM */ + beacon_interval_mod = + pmo_core_get_vdev_beacon_interval(vdev) / 100; + if (!beacon_interval_mod) + beacon_interval_mod = 1; + + max_dtim = (pmo_core_get_vdev_dtim_period(vdev) + * beacon_interval_mod); + + if (!max_dtim) { + pmo_err("Invalid dtim period"); + pmo_vdev_put_ref(vdev); + return QDF_STATUS_E_INVAL; + } + + max_mod_dtim = psoc_cfg->sta_max_li_mod_dtim / + max_dtim; + + if (!max_mod_dtim) + max_mod_dtim = 1; + + /* Calculate Listen Interval from provided mod DTIM */ + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->dyn_modulated_dtim = mod_dtim; + prev_dtim_enabled = vdev_ctx->dyn_modulated_dtim_enabled; + vdev_ctx->dyn_modulated_dtim_enabled = mod_dtim != 1; + if (vdev_ctx->dyn_modulated_dtim > max_mod_dtim) { + listen_interval = max_mod_dtim * + pmo_core_get_vdev_dtim_period(vdev); + } else { + listen_interval = vdev_ctx->dyn_modulated_dtim * + pmo_core_get_vdev_dtim_period(vdev); + } + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + if (prev_dtim_enabled || mod_dtim != 1) { + status = pmo_tgt_vdev_update_param_req(vdev, + pmo_vdev_param_listen_interval, + listen_interval); + if (QDF_IS_STATUS_ERROR(status)) + /* even it fails continue fwr will take default LI */ + pmo_err("Failed to set Listen Interval for vdev id %d", + vdev_id); + else + pmo_debug("Set Listen Interval %d for vdev id %d", + listen_interval, vdev_id); + + status = pmo_tgt_vdev_update_param_req(vdev, + pmo_vdev_param_dtim_policy, + pmo_normal_dtim); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_err("Failed to set Normal DTIM for vdev id %d", + vdev_id); + } else { + pmo_debug("Set DTIM Policy to Normal for vdev id %d", + vdev_id); + pmo_core_vdev_set_restore_dtim(vdev, true); + } + } + + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + return status; +} + +#ifdef SYSTEM_PM_CHECK +void pmo_core_system_resume(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is NULL"); + return; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return; + } + + psoc_ctx = pmo_psoc_get_priv(psoc); + + htc_system_resume(psoc_ctx->htc_hdl); + + pmo_psoc_put_ref(psoc); +} +#endif diff --git a/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_wow.c b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_wow.c new file mode 100644 index 0000000000000000000000000000000000000000..3f481a32dc44866d527d94f7d29cfc57a061edbc --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/core/src/wlan_pmo_wow.c @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Define API's for wow pattern addition and deletion in fwr + */ + +#include "wlan_pmo_wow.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include +#include "wlan_pmo_static_config.h" +#include "wlan_reg_services_api.h" +#include "cfg_nan_api.h" +#include "wlan_utility.h" + +void pmo_set_wow_event_bitmap(WOW_WAKE_EVENT_TYPE event, + uint32_t wow_bitmap_size, + uint32_t *bitmask) +{ + uint32_t bit_idx = 0, idx = 0; + + if (!bitmask || wow_bitmap_size < PMO_WOW_MAX_EVENT_BM_LEN) { + pmo_err("wow bitmask length shorter than %d", + PMO_WOW_MAX_EVENT_BM_LEN); + return; + } + pmo_get_event_bitmap_idx(event, wow_bitmap_size, &bit_idx, &idx); + bitmask[idx] |= 1 << bit_idx; +} + +QDF_STATUS pmo_core_del_wow_pattern(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + uint8_t id; + uint8_t pattern_count; + struct pmo_vdev_priv_obj *vdev_ctx; + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + pattern_count = pmo_get_wow_default_ptrn(vdev_ctx); + /* clear all default patterns cofigured by pmo */ + for (id = 0; id < pattern_count; id++) + status = pmo_tgt_del_wow_pattern(vdev, id, false); + + /* clear all user patterns cofigured by pmo */ + pattern_count = pmo_get_wow_user_ptrn(vdev_ctx); + for (id = 0; id < pattern_count; id++) + status = pmo_tgt_del_wow_pattern(vdev, id, true); + + pmo_vdev_put_ref(vdev); +out: + return status; +} + +QDF_STATUS pmo_core_add_wow_user_pattern(struct wlan_objmgr_vdev *vdev, + struct pmo_wow_add_pattern *ptrn) +{ + QDF_STATUS status; + uint8_t id; + uint8_t bit_to_check, pos; + uint8_t new_mask[PMO_WOWL_BCAST_PATTERN_MAX_SIZE]; + struct pmo_vdev_priv_obj *vdev_ctx; + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + /* clear all default patterns cofigured by pmo */ + for (id = 0; id < pmo_get_wow_default_ptrn(vdev_ctx); id++) + pmo_tgt_del_wow_pattern(vdev, id, false); + + pmo_set_wow_default_ptrn(vdev_ctx, 0); + + pmo_debug("Add user passed wow pattern id %d vdev id %d", + ptrn->pattern_id, wlan_vdev_get_id(vdev)); + /* + * Convert received pattern mask value from bit representation + * to byte representation. + * + * For example, received value from umac, + * + * Mask value : A1 (equivalent binary is "1010 0001") + * Pattern value : 12:00:13:00:00:00:00:44 + * + * The value which goes to FW after the conversion from this + * function (1 in mask value will become FF and 0 will + * become 00), + * + * Mask value : FF:00:FF:00:00:00:00:FF + * Pattern value : 12:00:13:00:00:00:00:44 + */ + qdf_mem_zero(new_mask, sizeof(new_mask)); + for (pos = 0; pos < ptrn->pattern_size; pos++) { + bit_to_check = (PMO_NUM_BITS_IN_BYTE - 1) - + (pos % PMO_NUM_BITS_IN_BYTE); + bit_to_check = 0x1 << bit_to_check; + if (ptrn->pattern_mask[pos / PMO_NUM_BITS_IN_BYTE] & + bit_to_check) + new_mask[pos] = PMO_WOW_PTRN_MASK_VALID; + } + + status = pmo_tgt_send_wow_patterns_to_fw(vdev, + ptrn->pattern_id, + ptrn->pattern, + ptrn->pattern_size, + ptrn->pattern_byte_offset, + new_mask, + ptrn->pattern_size, true); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to add wow pattern %d", ptrn->pattern_id); + + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_core_del_wow_user_pattern(struct wlan_objmgr_vdev *vdev, + uint8_t pattern_id) +{ + QDF_STATUS status; + struct pmo_vdev_priv_obj *vdev_ctx; + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + if (pmo_get_wow_user_ptrn(vdev_ctx) <= 0) { + pmo_err("No valid user pattern. Num user pattern %u", + pmo_get_wow_user_ptrn(vdev_ctx)); + status = QDF_STATUS_E_INVAL; + goto rel_ref; + } + + pmo_debug("Delete user passed wow pattern id %d total user pattern %d", + pattern_id, pmo_get_wow_user_ptrn(vdev_ctx)); + + pmo_tgt_del_wow_pattern(vdev, pattern_id, true); + + /* configure default patterns once all user patterns are deleted */ + if (!pmo_get_wow_user_ptrn(vdev_ctx)) + pmo_register_wow_default_patterns(vdev); +rel_ref: + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} + +void pmo_core_enable_wakeup_event(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + WOW_WAKE_EVENT_TYPE wow_event) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + uint32_t bitmap[PMO_WOW_MAX_EVENT_BM_LEN] = {0}; + + pmo_enter(); + + if (!psoc) { + pmo_err("psoc is null"); + goto out; + } + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + pmo_set_wow_event_bitmap(wow_event, PMO_WOW_MAX_EVENT_BM_LEN, bitmap); + + pmo_tgt_enable_wow_wakeup_event(vdev, bitmap); + + pmo_vdev_put_ref(vdev); + +out: + pmo_exit(); +} + +void pmo_core_disable_wakeup_event(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + WOW_WAKE_EVENT_TYPE wow_event) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + uint32_t bitmap[PMO_WOW_MAX_EVENT_BM_LEN] = {0}; + + if (!psoc) { + pmo_err("psoc is null"); + return; + } + + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) { + pmo_err("vdev is NULL"); + return; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + return; + + pmo_set_wow_event_bitmap(wow_event, PMO_WOW_MAX_EVENT_BM_LEN, bitmap); + + pmo_tgt_disable_wow_wakeup_event(vdev, bitmap); + + pmo_vdev_put_ref(vdev); +} + +/** + * pmo_is_beaconing_vdev_up(): check if a beaconning vdev is up + * @psoc: objmgr psoc handle + * + * Return TRUE if beaconning vdev is up + */ +static +bool pmo_is_beaconing_vdev_up(struct wlan_objmgr_psoc *psoc) +{ + int vdev_id; + struct wlan_objmgr_vdev *vdev; + enum QDF_OPMODE vdev_opmode; + bool is_beaconing; + QDF_STATUS status; + + /* Iterate through VDEV list */ + for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) { + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) + continue; + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + continue; + + vdev_opmode = pmo_get_vdev_opmode(vdev); + is_beaconing = pmo_is_vdev_in_beaconning_mode(vdev_opmode) && + QDF_IS_STATUS_SUCCESS(wlan_vdev_is_up(vdev)); + + pmo_vdev_put_ref(vdev); + + if (is_beaconing) + return true; + } + + return false; +} + +/** + * pmo_support_wow_for_beaconing: wow query for beaconning + * @psoc: objmgr psoc handle + * + * Need to configure wow to enable beaconning offload when + * a beaconing vdev is up and beaonning offload is configured. + * + * Return: true if we need to enable wow for beaconning offload + */ +static +bool pmo_support_wow_for_beaconing(struct wlan_objmgr_psoc *psoc) +{ + /* + * if (wmi_service_enabled(wma->wmi_handle, + * wmi_service_beacon_offload)) + */ + return pmo_is_beaconing_vdev_up(psoc); +} + +bool pmo_core_is_wow_applicable(struct wlan_objmgr_psoc *psoc) +{ + int vdev_id; + struct wlan_objmgr_vdev *vdev; + bool is_wow_applicable = false; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return false; + } + + if (pmo_support_wow_for_beaconing(psoc)) { + pmo_debug("one of vdev is in beaconning mode, enabling wow"); + return true; + } + + if (wlan_reg_is_11d_scan_inprogress(psoc)) { + pmo_debug("11d scan is in progress, enabling wow"); + return true; + } + + if (pmo_core_is_lpass_enabled(psoc)) { + pmo_info("lpass enabled, enabling wow"); + return true; + } + + if (cfg_nan_get_enable(psoc)) { + pmo_debug("nan enabled, enabling wow"); + return true; + } + + /* Iterate through VDEV list */ + for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) { + vdev = pmo_psoc_get_vdev(psoc, vdev_id); + if (!vdev) + continue; + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + continue; + + if (wlan_vdev_is_up(vdev) == QDF_STATUS_SUCCESS) { + pmo_debug("STA is connected, enabling wow"); + is_wow_applicable = true; + } else if (ucfg_scan_get_pno_in_progress(vdev)) { + pmo_debug("NLO is in progress, enabling wow"); + is_wow_applicable = true; + } else if (pmo_core_is_extscan_in_progress(vdev)) { + pmo_debug("EXT is in progress, enabling wow"); + is_wow_applicable = true; + } else if (pmo_core_is_p2plo_in_progress(vdev)) { + pmo_debug("P2P LO is in progress, enabling wow"); + is_wow_applicable = true; + } else if (pmo_core_get_vdev_op_mode(vdev) == QDF_NDI_MODE) { + pmo_debug("vdev %d is in NAN data mode, enabling wow", + vdev_id); + is_wow_applicable = true; + } + + pmo_vdev_put_ref(vdev); + + if (is_wow_applicable) + return true; + } + + pmo_debug("All vdev are in disconnected state\n" + "and pno/extscan is not in progress, skipping wow"); + + return false; +} + +void pmo_set_sta_wow_bitmask(uint32_t *bitmask, uint32_t wow_bitmap_size) +{ + + pmo_set_wow_event_bitmap(WOW_CSA_IE_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_CLIENT_KICKOUT_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_PATTERN_MATCH_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_MAGIC_PKT_RECVD_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_DEAUTH_RECVD_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_DISASSOC_RECVD_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_BMISS_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_GTK_ERR_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_BETTER_AP_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_HTT_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_RA_MATCH_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_NLO_DETECTED_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_EXTSCAN_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_OEM_RESPONSE_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_TDLS_CONN_TRACKER_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_11D_SCAN_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_NLO_SCAN_COMPLETE_EVENT, + wow_bitmap_size, + bitmask); + /* + * WPA3 roaming offloads SAE authentication to wpa_supplicant + * Firmware will send WMI_ROAM_PREAUTH_STATUS_CMDID + */ + pmo_set_wow_event_bitmap(WOW_ROAM_PREAUTH_START_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_ROAM_PMKID_REQUEST_EVENT, + wow_bitmap_size, + bitmask); +} + +void pmo_set_sap_wow_bitmask(uint32_t *bitmask, uint32_t wow_bitmap_size) +{ + pmo_set_wow_event_bitmap(WOW_CLIENT_KICKOUT_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_PROBE_REQ_WPS_IE_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_PATTERN_MATCH_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_AUTH_REQ_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_ASSOC_REQ_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_DEAUTH_RECVD_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_DISASSOC_RECVD_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_HTT_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_SAP_OBSS_DETECTION_EVENT, + wow_bitmap_size, + bitmask); + pmo_set_wow_event_bitmap(WOW_BSS_COLOR_COLLISION_DETECT_EVENT, + wow_bitmap_size, + bitmask); +} + +uint8_t pmo_get_num_wow_filters(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + bool apf = false; + bool pkt_filter = false; + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + apf = pmo_intersect_apf(psoc_ctx); + pkt_filter = pmo_intersect_packet_filter(psoc_ctx); + } + + if (!apf && !pkt_filter) + return PMO_WOW_FILTERS_MAX; + + return PMO_WOW_FILTERS_PKT_OR_APF; +} + +#ifdef WLAN_FEATURE_NAN +void pmo_set_ndp_wow_bitmask(uint32_t *bitmask, uint32_t wow_bitmap_size) +{ + /* wake up host when Nan Management Frame is received */ + pmo_set_wow_event_bitmap(WOW_NAN_DATA_EVENT, + wow_bitmap_size, + bitmask); + /* wake up host when NDP data packet is received */ + pmo_set_wow_event_bitmap(WOW_PATTERN_MATCH_EVENT, + wow_bitmap_size, + bitmask); +} +#endif diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_apf_cfg.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_apf_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..8612b727e9e55338b37052b60f0377415f501aae --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_apf_cfg.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_PMO_APF_CFG_H__ +#define WLAN_PMO_APF_CFG_H__ + +/* + * + * gBpfFilterEnable - APF feature support configuration + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * When set to 1 APF feature will be enabled. + * + * Supported Feature: Android packet filter + * + * Usage: External + * + * + */ +#define CFG_PMO_APF_ENABLE CFG_INI_BOOL("gBpfFilterEnable", \ + 1, \ + "Enable APF Support") + +/* + * + * gActiveUcBpfMode - Control UC active APF mode + * @Min: 0 (disabled) + * @Max: 2 (adaptive) + * @Default: 0 (disabled) + * + * This config item controls UC APF in active mode. There are 3 modes: + * 0) disabled - APF is disabled in active mode + * 1) enabled - APF is enabled for all packets in active mode + * 2) adaptive - APF is enabled for packets up to some throughput threshold + * + * Related: gActiveMcBcBpfMode + * + * Supported Feature: Active Mode APF + * + * Usage: Internal/External + * + */ +#define CFG_ACTIVE_UC_APF_MODE CFG_INI_UINT( \ + "gActiveUcBpfMode", \ + ACTIVE_APF_DISABLED, \ + (ACTIVE_APF_MODE_COUNT - 1), \ + ACTIVE_APF_DISABLED, \ + CFG_VALUE_OR_DEFAULT, \ + "Control UC active APF mode") + +/* + * + * gActiveMcBcBpfMode - Control MC/BC active APF mode + * @Min: 0 (disabled) + * @Max: 1 (enabled) + * @Default: 0 (disabled) + * + * This config item controls MC/BC APF in active mode. There are 3 modes: + * 0) disabled - APF is disabled in active mode + * 1) enabled - APF is enabled for all packets in active mode + * 2) adaptive - APF is enabled for packets up to some throughput threshold + * + * Related: gActiveUcBpfMode + * + * Supported Feature: Active Mode APF + * + * Usage: Internal/External + * + */ +#define CFG_ACTIVE_MC_BC_APF_MODE CFG_INI_UINT( \ + "gActiveMcBcBpfMode", \ + ACTIVE_APF_DISABLED, \ + ACTIVE_APF_ENABLED, \ + ACTIVE_APF_DISABLED, \ + CFG_VALUE_OR_DEFAULT, \ + "Control MC/BC active APF mode") + +#define CFG_PMO_APF_ALL \ + CFG(CFG_PMO_APF_ENABLE) \ + CFG(CFG_ACTIVE_UC_APF_MODE) \ + CFG(CFG_ACTIVE_MC_BC_APF_MODE) + +#endif /* WLAN_PMO_APF_CFG_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_arp_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_arp_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..305afcf0fc0047c81af93d04b718d21b94a11f36 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_arp_public_struct.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which shall be used in + * pmo arp offload feature. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_PMO_ARP_PUBLIC_STRUCT_H_ +#define _WLAN_PMO_ARP_PUBLIC_STRUCT_H_ + +#include "wlan_pmo_common_public_struct.h" + +/** + * struct pmo_arp_req - pmo arp request + * @psoc: objmgr psoc + * @vdev_id: vdev id on which arp offload needed + * @ipv4_addr: ipv4 address for the interface + * @trigger: context from where arp offload triggered + */ +struct pmo_arp_req { + struct wlan_objmgr_psoc *psoc; + uint8_t vdev_id; + uint32_t ipv4_addr; + enum pmo_offload_trigger trigger; +}; + +/** + * struct pmo_arp_req - pmo arp offload param for target interface + * @enable: true when arp offload is enabled else false + * @host_ipv4_addr: host interface ipv4 address + * @bssid: peer ap address + */ +struct pmo_arp_offload_params { + uint8_t enable; + uint8_t host_ipv4_addr[QDF_IPV4_ADDR_SIZE]; + struct qdf_mac_addr bssid; + bool is_offload_applied; +}; + +#endif /* end of _WLAN_PMO_ARP_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_cfg.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..f51a027c63ad5e888480a064376ef302df6b93dd --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_cfg.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_PMO_CFG_H__ +#define WLAN_PMO_CFG_H__ + +#include "wlan_pmo_apf_cfg.h" +#include "wlan_pmo_common_cfg.h" +#include "wlan_pmo_extwow_cfg.h" +#include "wlan_pmo_pkt_filter_cfg.h" +#include "wlan_pmo_runtime_pm_cfg.h" +#include "wlan_pmo_wow_pulse_cfg.h" + +#define CFG_PMO_ALL \ + CFG_EXTWOW_ALL \ + CFG_PACKET_FILTER_ALL \ + CFG_PMO_APF_ALL \ + CFG_PMO_COMMON_ALL \ + CFG_RUNTIME_PM_ALL \ + CFG_WOW_PULSE_ALL + +#endif /* WLAN_PMO_CFG_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_common_cfg.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_common_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..d15706de88dbe8f13f5ffe672511244c9d96401d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_common_cfg.h @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_PMO_COMMON_CFG_H__ +#define WLAN_PMO_COMMON_CFG_H__ + +#include "wlan_pmo_common_public_struct.h" + +/* + * + * hostArpOffload - Enable/disable host ARP offload + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable firmware's capability of sending ARP + * response to clients. + * + * Usage: External + * + * + */ +#define CFG_PMO_ENABLE_HOST_ARPOFFLOAD CFG_INI_BOOL( \ + "hostArpOffload", \ + 1, \ + "enable/disable host ARP offload") + +/* + * + * gHwFilterMode - configure hardware filter for DTIM mode + * @Min: 0 + * @Max: 3 + * @Default: 1 + * + * The hardware filter is only effective in DTIM mode. Use this configuration + * to blanket drop broadcast/multicast packets at the hardware level, without + * waking up the firmware + * + * Takes a bitmap of frame types to drop + * @E.g. + * # disable feature + * gHwFilterMode=0 + * # drop all broadcast frames, except ARP (default) + * gHwFilterMode=1 + * # drop all multicast frames, except ICMPv6 + * gHwFilterMode=2 + * # drop all broadcast and multicast frames, except ARP and ICMPv6 + * gHwFilterMode=3 + * + * Related: N/A + * + * Usage: Internal/External + * + * + */ +#define CFG_PMO_HW_FILTER_MODE CFG_INI_UINT( \ + "gHwFilterMode", \ + 0, \ + 3, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "hardware filter for DTIM mode") + +/* + * + * ssdp - Enable/disable SSDP + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable Simple Service Discovery Protocol(SSDP). + * + * Usage: External + * + * + */ +#define CFG_PMO_ENABLE_HOST_SSDP CFG_INI_BOOL( \ + "ssdp", \ + 1, \ + "Enable/disable ssdp") + +/* + * + * hostNSOffload - Enable/disable NS offload + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable NS offload. + * + * Usage: External + * + * + */ +#define CFG_PMO_ENABLE_HOST_NSOFFLOAD CFG_INI_BOOL( \ + "hostNSOffload", \ + 1, \ + "Enable/disable NS offload") + +/* + * + * gEnableDynamicDTIM - Enable Dynamic DTIM + * @Min: 0 + * @Max: 10 + * @Default: 0 + * + * This ini is used to enable/disable dynamic DTIM. + * + * 0 - Disable Dynamic DTIM + * 1 to 10 - SLM will switch to DTIM specified here when host suspends and + * switch DTIM1 when host resumes + * + * Usage: External + * + * + */ +#define CFG_PMO_ENABLE_DYNAMIC_DTIM CFG_INI_UINT( \ + "gEnableDynamicDTIM", \ + 0, \ + 10, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable Dynamic DTIM") + +/* + * + * gEnableModulatedDTIM - Enable/Disable modulated DTIM feature + * @Min: 0 + * @Max: 5 + * @Default: 0 + * + * This ini is used to enable/disable modulated DTIM feature. + * + * 0 - Disable modulated DTIM. + * 1 to 5 - The maximum No. of modulated DTIM period used for calculating the + * target listen interval. + * + * The target listen interval will be updated to firmware when host driver is + * setting the suspend DTIM parameters. + * + * This configuration will be ignored when dynamic DTIM is enabled(by + * gEnableDynamicDTIM). + * + * Usage: External + * + * + */ +#define CFG_PMO_ENABLE_MODULATED_DTIM CFG_INI_UINT( \ + "gEnableModulatedDTIM", \ + 0, \ + 5, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable/disable modulated DTIM feature") + +/* + * + * gMCAddrListEnable - Enable/disable multicast MAC address list feature + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable multicast MAC address list feature. + * Default: Enable + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_PMO_MC_ADDR_LIST_ENABLE CFG_INI_BOOL( \ + "gMCAddrListEnable", \ + 1, \ + "Enable/disable multicast MAC address list feature") + +/* + * + * gOptimizedPowerManagement - Optimized Power Management + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set Optimized Power Management configuration: + * Current values of gOptimizedPowerManagement: + * 0 -> Disable optimized power management + * 1 -> Enable optimized power management + * + * Related: None + * + * Supported Feature: Optimized Power Management + * + * Usage: External + * + * + */ +#define CFG_PMO_POWERSAVE_MODE CFG_INI_UINT( \ + "gOptimizedPowerManagement", \ + 0, \ + 1, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Optimized Power Management") + +/* + * + * enable_mod_dtim_on_system_suspend - enable modulated DTIM + * on system suspend display off case + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set modulated DTIM configuration: + * Current values of enable_mod_dtim_on_system_suspend: + * 0 -> Modulated DTIM will be enabled for every wow entry + * (RTPM wow + System suspend wow) + * 1 -> Enable modulated DTIM only for System suspend wow. + * For RTPM wow, the device will stay in DTIM 1 (non-modulated DTIM) + * + * Related: None + * + * Supported Feature: Modulated DTIM + * + * Usage: External + * + * + */ +#define CFG_PMO_MOD_DTIM_ON_SYS_SUSPEND CFG_INI_BOOL( \ + "enable_mod_dtim_on_system_suspend", \ + 0, \ + "Modulated DTIM on System suspend wow") + +/* + * + * gMaxPsPoll - Max powersave poll + * @Min: 0 + * @Max: 255 + * @Default: 0 + * + * This ini is used to set max powersave poll. + * + * Usage: External + * + * + */ +#define CFG_PMO_MAX_PS_POLL CFG_INI_UINT( \ + "gMaxPsPoll", \ + 0, \ + 255, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Max powersave poll") + +/* + * + * gEnableWoW - Enable/Disable WoW + * @Min: 0 + * @Max: 3 + * @Default: 3 + * + * This ini is used to enable/disable WoW. Configurations are as follows: + * 0 - Disable both magic pattern match and pattern byte match. + * 1 - Enable magic pattern match on all interfaces. + * 2 - Enable pattern byte match on all interfaces. + * 3 - Enable both magic patter and pattern byte match on all interfaces. + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_PMO_WOW_ENABLE CFG_INI_UINT("gEnableWoW", \ + 0, 3, 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable WoW Support") +/* + * + * wowlan_deauth_enable - Enable/Disable wowlan deauth enable + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable wowlan deauth enable. + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_PMO_WOWLAN_DEAUTH_ENABLE CFG_INI_BOOL("wowlan_deauth_enable", \ + 1, \ + "Enable WoWLan deauth") +/* + * + * wowlan_disassoc_enable - Enable/Disable wowlan disassoc enable + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable wowlan disassoc enable. + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_PMO_WOWLAN_DISASSOC_ENABLE CFG_INI_BOOL("wowlan_disassoc_enable", \ + 1, \ + "Enable WoW Support") + +/* + * + * gActiveModeOffload - Active offload mode configuration + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * When set to 1 active mode offload will be enabled. + * + * If active mode offload is enabled then all applicable data offload/filtering + * is enabled immediately in FW once config is available in WLAN driver and FW + * caches this configuration across suspend/resume; + * If active mode offload is disabled then all applicable data offload/filtering + * is enabled during cfg80211 suspend and disabled during cfg80211 resume. + * + * Supported Feature: Active mode offload + * + * Usage: External + * + * + */ +#define CFG_PMO_ACTIVE_MODE CFG_INI_BOOL("gActiveModeOffload", \ + 1, \ + "Enable active mode offload") + +/* + * + * g_auto_detect_power_failure_mode - Auto detect power save failure mode + * @Min: PMO_FW_TO_CRASH_ON_PWR_FAILURE + * @Max: PMO_AUTO_PWR_FAILURE_DETECT_DISABLE + * @Default: PMO_FW_TO_CRASH_ON_PWR_FAILURE + * + * Specifies the behavior of FW in case of CHIP_POWER_SAVE_FAIL_DETECTED event + * + * Supported Feature: Auto detect power save failure + * + * Usage: External + * + * + */ +#define CFG_PMO_PWR_FAILURE CFG_INI_UINT("g_auto_detect_power_failure_mode", \ + PMO_FW_TO_CRASH_ON_PWR_FAILURE, \ + PMO_AUTO_PWR_FAILURE_DETECT_DISABLE, \ + PMO_FW_TO_CRASH_ON_PWR_FAILURE, \ + CFG_VALUE_OR_DEFAULT, \ + "Auto detect power save failure mode") + +/* + * + * gEnableSapSuspend - Enable/disable SAP Suspend + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * + * Related: None + * + * Supported Feature: SAP + * + * Usage: External + * + * + */ +#define CFG_ENABLE_SAP_SUSPEND CFG_INI_BOOL( \ + "gEnableSapSuspend", \ + 1, \ + "Enable/disable SAP Suspend") + +/* + * + * g_wow_data_inactivity_timeout - Data activity timeout in wow mode. + * @Min: 1 + * @Max: 255 + * @Default: 50 + * + * This ini is used to set data inactivity timeout in wow mode. + * + * Supported Feature: inactivity timeout in wow mode + * + * Usage: External + * + * + */ +#define CFG_PMO_WOW_DATA_INACTIVITY_TIMEOUT CFG_INI_UINT( \ + "g_wow_data_inactivity_timeout", \ + 1, \ + 255, \ + 50, \ + CFG_VALUE_OR_DEFAULT, \ + "Data activity timeout in wow mode") +/* + * + * gRArateLimitInterval - RA rate limit interval + * @Min: 60 + * @Max: 3600 + * @Default: 60 + * This ini is used to set RA rate limit interval. + * + * Usage: External + * + * + */ +#define CFG_RA_RATE_LIMIT_INTERVAL CFG_INI_UINT( \ + "gRArateLimitInterval", \ + 60, \ + 3600, \ + 60, \ + CFG_VALUE_OR_DEFAULT, \ + "RA rate limit interval") + +#define CFG_PMO_COMMON_ALL \ + CFG(CFG_ENABLE_SAP_SUSPEND) \ + CFG(CFG_PMO_ENABLE_HOST_ARPOFFLOAD) \ + CFG(CFG_PMO_HW_FILTER_MODE) \ + CFG(CFG_PMO_ENABLE_HOST_SSDP) \ + CFG(CFG_PMO_ENABLE_HOST_NSOFFLOAD) \ + CFG(CFG_PMO_ENABLE_DYNAMIC_DTIM) \ + CFG(CFG_PMO_ENABLE_MODULATED_DTIM) \ + CFG(CFG_PMO_MC_ADDR_LIST_ENABLE) \ + CFG(CFG_PMO_POWERSAVE_MODE) \ + CFG(CFG_PMO_MAX_PS_POLL) \ + CFG(CFG_PMO_WOWLAN_DEAUTH_ENABLE) \ + CFG(CFG_PMO_WOWLAN_DISASSOC_ENABLE) \ + CFG(CFG_PMO_WOW_ENABLE) \ + CFG(CFG_PMO_ACTIVE_MODE) \ + CFG(CFG_PMO_PWR_FAILURE) \ + CFG(CFG_PMO_WOW_DATA_INACTIVITY_TIMEOUT) \ + CFG(CFG_RA_RATE_LIMIT_INTERVAL) \ + CFG(CFG_PMO_MOD_DTIM_ON_SYS_SUSPEND) + +#endif /* WLAN_PMO_COMMON_CFG_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_common_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_common_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..d905c0dc0e168bbfba97e8f376d3c11fbdbdce3e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_common_public_struct.h @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which are common for + * various pmo related features. + * + * Note: This file shall not contain public API's prototype/declartions. + * + */ + +#ifndef _WLAN_PMO_COMMONP_PUBLIC_STRUCT_H_ +#define _WLAN_PMO_COMMONP_PUBLIC_STRUCT_H_ + +#include "wlan_cmn.h" +#include "wlan_objmgr_cmn.h" +#include "wlan_objmgr_global_obj.h" +#include "wmi_unified.h" +#include "qdf_status.h" +#include "qdf_lock.h" +#include "qdf_event.h" +#include "wlan_pmo_hw_filter_public_struct.h" + + +#define PMO_IPV4_ARP_REPLY_OFFLOAD 0 +#define PMO_IPV6_NEIGHBOR_DISCOVERY_OFFLOAD 1 +#define PMO_IPV6_NS_OFFLOAD 2 +#define PMO_OFFLOAD_DISABLE 0 +#define PMO_OFFLOAD_ENABLE 1 + +#define PMO_MAC_NS_OFFLOAD_SIZE 1 +#define PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA 16 +#define PMO_IPV6_ADDR_VALID 1 +#define PMO_IPV6_ADDR_UC_TYPE 0 +#define PMO_IPV6_ADDR_AC_TYPE 1 + + +#define PMO_WOW_REQUIRED_CREDITS 1 + +/** + * enum pmo_vdev_param_id: tell vdev param id + * @pmo_vdev_param_listen_interval: vdev listen interval param id + * @pmo_vdev_param_dtim_policy: vdev param dtim policy + * @pmo_vdev_max_param: Max vdev param id + */ +enum pmo_vdev_param_id { + pmo_vdev_param_listen_interval = 0, + pmo_vdev_param_dtim_policy, + pmo_vdev_max_param +}; + +/** + * enum pmo_beacon_dtim_policy: tell vdev beacon policy + * @pmo_ignore_dtim: fwr need to igonre dtime policy + * @pmo_normal_dtim: fwr need to use normal dtime policy + * @pmo_stick_dtim: fwr need to use stick dtime policy + * @auto_dtim: fwr need to auto dtime policy + */ +enum pmo_beacon_dtim_policy { + pmo_ignore_dtim = 0x01, + pmo_normal_dtim = 0x02, + pmo_stick_dtim = 0x03, + pmo_auto_dtim = 0x04, +}; + +/** + * @pmo_sta_ps_param_rx_wake_policy: Controls how frames are retrievd from AP + * while STA is sleeping. + * @pmo_sta_ps_param_tx_wake_threshold: STA will go active after this many TX + * @pmo_sta_ps_param_pspoll_count:No of PS-Poll to send before STA wakes up + * @pmo_sta_ps_param_inactivity_time: TX/RX inactivity time in msec before + going to sleep. + * @pmo_sta_ps_param_uapsd: Set uapsd configuration. + * @pmo_sta_ps_param_advanced_power_pspoll_count: No of PS-Poll to send before + STA wakes up in Advanced Power Save Mode. + * @pmo_sta_ps_enable_advanced_power: Enable Advanced Power Save + * @pmo_sta_ps_param_advanced_power_max_tx_before_wake: Number of TX frames + before the entering the Active state + * @pmo_sta_ps_param_ito_repeat_count: Indicates ito repeated count + */ +enum pmo_sta_powersave_param { + pmo_sta_ps_param_rx_wake_policy = 0, + pmo_sta_ps_param_tx_wake_threshold = 1, + pmo_sta_ps_param_pspoll_count = 2, + pmo_sta_ps_param_inactivity_time = 3, + pmo_sta_ps_param_uapsd = 4, + pmo_sta_ps_param_advanced_power_pspoll_count = 5, + pmo_sta_ps_enable_advanced_power = 6, + pmo_sta_ps_param_advanced_power_max_tx_before_wake = 7, + pmo_sta_ps_param_ito_repeat_count = 8, +}; + +/** + * enum wow_resume_trigger - resume trigger override setting values + * @PMO_WOW_RESUME_TRIGGER_DEFAULT: fw to use platform default resume trigger + * @PMO_WOW_RESUME_TRIGGER_HTC_WAKEUP: force fw to use HTC Wakeup to resume + * @PMO_WOW_RESUME_TRIGGER_GPIO: force fw to use GPIO to resume + * @PMO_WOW_RESUME_TRIGGER_COUNT: number of resume trigger options + */ +enum pmo_wow_resume_trigger { + /* always first */ + PMO_WOW_RESUME_TRIGGER_DEFAULT = 0, + PMO_WOW_RESUME_TRIGGER_HTC_WAKEUP, + PMO_WOW_RESUME_TRIGGER_GPIO, + /* always last */ + PMO_WOW_RESUME_TRIGGER_COUNT +}; + +/** + * enum wow_interface_pause - interface pause override setting values + * @PMO_WOW_INTERFACE_PAUSE_DEFAULT: use platform default iface pause setting + * @PMO_WOW_INTERFACE_PAUSE_ENABLE: force interface pause setting to enabled + * @PMO_WOW_INTERFACE_PAUSE_DISABLE: force interface pause setting to disabled + * @PMO_WOW_INTERFACE_PAUSE_COUNT: number of interface pause options + */ +enum pmo_wow_interface_pause { + /* always first */ + PMO_WOW_INTERFACE_PAUSE_DEFAULT = 0, + PMO_WOW_INTERFACE_PAUSE_ENABLE, + PMO_WOW_INTERFACE_PAUSE_DISABLE, + /* always last */ + PMO_WOW_INTERFACE_PAUSE_COUNT +}; + +/** + * enum wow_enable_type - used to enable/disable WoW. + * @PMO_WOW_DISABLE_BOTH: Disable both magic pattern match and pattern + * byte match. + * @PMO_WOW_ENABLE_MAGIC_PATTERN: Enable magic pattern match on all interfaces. + * @PMO_WOW_ENABLE_PATTERN_BYTE: Enable pattern byte match on all interfaces. + * @PMO_WOW_ENABLE_BOTH: Enable both magic patter and pattern byte match on + * all interfaces. + */ +enum pmo_wow_enable_type { + PMO_WOW_DISABLE_BOTH = 0, + PMO_WOW_ENABLE_MAGIC_PATTERN, + PMO_WOW_ENABLE_PATTERN_BYTE, + PMO_WOW_ENABLE_BOTH +}; + +/** + * enum powersave_mode - powersave_mode + * @PMO_PS_ADVANCED_POWER_SAVE_DISABLE: Disable advanced power save mode + * @PMO_PS_ADVANCED_POWER_SAVE_ENABLE: Enable power save mode + */ +enum powersave_mode { + PMO_PS_ADVANCED_POWER_SAVE_DISABLE = 0, + PMO_PS_ADVANCED_POWER_SAVE_ENABLE = 1 +}; + +#define PMO_TARGET_SUSPEND_TIMEOUT (4000) +#define PMO_WAKE_LOCK_TIMEOUT 1000 +#define PMO_RESUME_TIMEOUT (4000) + +/** + * struct wow_enable_params - A collection of wow enable override parameters + * @is_unit_test: true to notify fw this is a unit-test suspend + * @interface_pause: used to override the interface pause indication sent to fw + * @resume_trigger: used to force fw to use a particular resume method + */ +struct pmo_wow_enable_params { + bool is_unit_test; + enum pmo_wow_interface_pause interface_pause; + enum pmo_wow_resume_trigger resume_trigger; +}; + +/** + * typedef for psoc suspend handler + */ +typedef QDF_STATUS(*pmo_psoc_suspend_handler) + (struct wlan_objmgr_psoc *psoc, void *arg); +/** + * typedef for psoc resume handler + */ +typedef QDF_STATUS(*pmo_psoc_resume_handler) + (struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * enum pmo_offload_trigger: trigger information + * @pmo_apps_suspend: trigger is apps suspend + * @pmo_apps_resume: trigger is apps resume + * @pmo_runtime_suspend: trigger is runtime suspend + * @pmo_runtime_resume: trigger is runtime resume + * @pmo_ipv4_change_notify: trigger is ipv4 change handler + * @pmo_ipv6_change_notify: trigger is ipv6 change handler + * @pmo_ns_offload_dynamic_update: enable/disable ns offload on the fly + * @pmo_peer_disconnect: trigger is peer disconnect + * @pmo_mcbc_setting_dynamic_update: mcbc value update on the fly + * + * @pmo_offload_trigger_max: Max trigger value + */ +enum pmo_offload_trigger { + pmo_apps_suspend = 0, + pmo_apps_resume, + pmo_runtime_suspend, + pmo_runtime_resume, + pmo_ipv4_change_notify, + pmo_ipv6_change_notify, + pmo_mc_list_change_notify, + pmo_ns_offload_dynamic_update, + pmo_peer_disconnect, + pmo_mcbc_setting_dynamic_update, + + pmo_offload_trigger_max, +}; + +/** + * enum pmo_auto_pwr_detect_failure_mode_t - auto detect failure modes + * @PMO_FW_TO_CRASH_ON_PWR_FAILURE: Don't register wow wakeup event and FW + * crashes on power failure + * @PMO_FW_TO_SEND_WOW_IND_ON_PWR_FAILURE: Register wow wakeup event and FW + * sends failure event to host on power failure + * @PMO_FW_TO_REJUVENATE_ON_PWR_FAILURE: Don't register wow wakeup event and + * FW silently rejuvenate on power failure + * @PMO_AUTO_PWR_FAILURE_DETECT_DISABLE: Don't register wow wakeup event and the + * auto power failure detect feature is disabled in FW. + */ +enum pmo_auto_pwr_detect_failure_mode { + PMO_FW_TO_CRASH_ON_PWR_FAILURE, + PMO_FW_TO_SEND_WOW_IND_ON_PWR_FAILURE, + PMO_FW_TO_REJUVENATE_ON_PWR_FAILURE, + PMO_AUTO_PWR_FAILURE_DETECT_DISABLE +}; + +/** + * enum active_apf_mode - the modes active APF can operate in + * @ACTIVE_APF_DISABLED: APF is disabled in active mode + * @ACTIVE_APF_ENABLED: APF is enabled for all packets + * @ACTIVE_APF_ADAPTIVE: APF is enabled for packets up to some threshold + * @ACTIVE_APF_MODE_COUNT: The number of active APF modes + */ +enum active_apf_mode { + ACTIVE_APF_DISABLED = 0, + ACTIVE_APF_ENABLED, + ACTIVE_APF_ADAPTIVE, + ACTIVE_APF_MODE_COUNT +}; + +/** + * struct pmo_psoc_cfg - user configuration required for pmo + * @ptrn_match_enable_all_vdev: true when pattern match is enable for all vdev + * @apf_enable: true if psoc supports apf else false + * @arp_offload_enable: true if arp offload is supported for psoc else false + * @hw_filter_mode_bitmap: which mode the hardware filter should use during DTIM + * @ns_offload_enable_static: true if psoc supports ns offload in ini else false + * @ns_offload_enable_dynamic: to enable / disable the ns offload using + * ioctl or vendor command. + * @packet_filter_enabled: true if feature is enabled by configuration + * @ssdp: true if psoc supports if ssdp configuration in wow mode + * @enable_mc_list: true if psoc supports mc addr list else false + * @active_mode_offload: true if psoc supports active mode offload else false + * @ap_arpns_support: true if psoc supports arp ns for ap mode + * @d0_wow_supported: true if psoc supports D0 wow command + * @ra_ratelimit_enable: true when ra filtering ins eanbled else false + * @ra_ratelimit_interval: ra packets interval + * @magic_ptrn_enable: true when magic pattern is enabled else false + * @deauth_enable: true when wake up on deauth is enabled else false + * @disassoc_enable: true when wake up on disassoc is enabled else false + * @lpass_enable: true when lpass is enabled else false + * @max_ps:poll: max power save poll + * @sta_dynamic_dtim: station dynamic DTIM value + * @sta_mod_dtim: station modulated DTIM value + * @sta_max_li_mod_dtim: station max listen interval DTIM value + * @wow_enable: enable wow with majic pattern match or pattern byte match + * @power_save_mode: power save mode for psoc + * @runtime_pm_delay: set runtime pm's inactivity timer + * @extwow_goto_suspend: true when extended WoW enabled else false + * @extwow_app1_wakeup_pin_num: set wakeup1 PIN number + * @extwow_app2_wakeup_pin_num: set wakeup2 PIN number + * @extwow_app2_init_ping_interval: set keep alive init ping interval + * @extwow_app2_min_ping_interval: set keep alive minimum ping interval + * @extwow_app2_max_ping_interval: set keep alive maximum ping interval + * @extwow_app2_inc_ping_interval: set keep alive increment ping interval + * @extwow_app2_tcp_src_port: set TCP source port + * @extwow_app2_tcp_dst_port: set TCP dest port + * @extwow_app2_tcp_tx_timeout: set TCP TX timeout + * @extwow_app2_tcp_rx_timeout: set TCP RX timeout + * @auto_power_save_fail_mode: auto detect power save failure + * @is_wow_pulse_supported: true when wow pulse feature is enabled else false + * @wow_pulse_pin: GPIO pin of wow pulse feature + * @wow_pulse_interval_high: The interval of high level in the pulse + * @wow_pulse_interval_low: The interval of low level in the pulse + * @packet_filters_bitmap: Packet filter bitmap configuration + * @wow_data_inactivity_timeout: power save wow data inactivity timeout + * @ps_data_inactivity_timeout: Power save data inactivity timeout for non + * wow mode + * @active_uc_apf_mode: Setting that determines how APF is applied in active + * mode for uc packets + * @active_mc_bc_apf_mode: Setting that determines how APF is applied in + * active mode for MC/BC packets + * @ito_repeat_count: Indicates ito repeated count + * @is_mod_dtim_on_sys_suspend_enabled: true when mod dtim is enabled for + * system suspend wow else false + */ +struct pmo_psoc_cfg { + bool ptrn_match_enable_all_vdev; + bool apf_enable; + bool arp_offload_enable; + enum pmo_hw_filter_mode hw_filter_mode_bitmap; + bool ns_offload_enable_static; + bool ns_offload_enable_dynamic; + bool packet_filter_enabled; + bool ssdp; + bool enable_mc_list; + bool active_mode_offload; + bool ap_arpns_support; + bool d0_wow_supported; + bool ra_ratelimit_enable; +#if FEATURE_WLAN_RA_FILTERING + uint16_t ra_ratelimit_interval; +#endif + bool magic_ptrn_enable; + bool deauth_enable; + bool disassoc_enable; + bool lpass_enable; + bool wowlan_deauth_enable; + bool wowlan_disassoc_enable; + uint8_t max_ps_poll; + uint8_t sta_dynamic_dtim; + uint8_t sta_mod_dtim; + uint8_t sta_max_li_mod_dtim; + enum pmo_wow_enable_type wow_enable; + enum powersave_mode power_save_mode; +#ifdef FEATURE_RUNTIME_PM + uint32_t runtime_pm_delay; +#endif +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + bool extwow_goto_suspend; + uint8_t extwow_app1_wakeup_pin_num; + uint8_t extwow_app2_wakeup_pin_num; + uint32_t extwow_app2_init_ping_interval; + uint32_t extwow_app2_min_ping_interval; + uint32_t extwow_app2_max_ping_interval; + uint32_t extwow_app2_inc_ping_interval; + uint16_t extwow_app2_tcp_src_port; + uint16_t extwow_app2_tcp_dst_port; + uint32_t extwow_app2_tcp_tx_timeout; + uint32_t extwow_app2_tcp_rx_timeout; +#endif + enum pmo_auto_pwr_detect_failure_mode auto_power_save_fail_mode; +#ifdef WLAN_FEATURE_WOW_PULSE + bool is_wow_pulse_supported; + uint8_t wow_pulse_pin; + uint16_t wow_pulse_interval_high; + uint16_t wow_pulse_interval_low; +#endif +#ifdef WLAN_FEATURE_PACKET_FILTERING + uint8_t packet_filters_bitmap; +#endif + bool enable_sap_suspend; + uint8_t wow_data_inactivity_timeout; + uint8_t ps_data_inactivity_timeout; + enum active_apf_mode active_uc_apf_mode; + enum active_apf_mode active_mc_bc_apf_mode; + uint8_t ito_repeat_count; + bool is_mod_dtim_on_sys_suspend_enabled; +}; + +/** + * pmo_device_caps - device capability flags (true if feature is supported) + * @apf: Android Packet Filter (aka BPF) + * @arp_ns_offload: APR/NS offload + * @packet_filter: Legacy "Packet Filter" + * @unified_wow: Firmware supports "interface pause" flag in WoW command. + * This allows both D0-WoW (bus up) and Non-D0-WoW (bus down) to use one + * unified command + * @li_offload: Firmware has listen interval offload support + */ +struct pmo_device_caps { + bool apf; + bool arp_ns_offload; + bool packet_filter; + bool unified_wow; + bool li_offload; +}; + +#endif /* end of _WLAN_PMO_COMMONP_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_extwow_cfg.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_extwow_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..94f01304c5c5281228ff094bb049a23457bf86ae --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_extwow_cfg.h @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_PMO_EXTWOW_CFG_H__ +#define WLAN_PMO_EXTWOW_CFG_H__ + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +/* + * + * gExtWoWgotoSuspend - Enable/Disable Extended WoW + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable Extended WoW. + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_GOTO_SUSPEND CFG_INI_BOOL("gExtWoWgotoSuspend", \ + 1, \ + "Enable Ext WoW goto support") +/* + * + * gExtWowApp1WakeupPinNumber - Set wakeup1 PIN number + * @Min: 0 + * @Max: 255 + * @Default: 12 + * + * This ini is used to set EXT WOW APP1 wakeup PIN number + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_APP1_WAKE_PIN_NUMBER \ + CFG_INI_UINT("gExtWowApp1WakeupPinNumber", \ + 0, 255, 12, \ + CFG_VALUE_OR_DEFAULT, \ + "Set wakeup1 PIN number") +/* + * + * gExtWowApp2WakeupPinNumber - Set wakeup2 PIN number + * @Min: 0 + * @Max: 255 + * @Default: 16 + * + * This ini is used to set EXT WOW APP2 wakeup PIN number + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_APP2_WAKE_PIN_NUMBER \ + CFG_INI_UINT("gExtWowApp2WakeupPinNumber", \ + 0, 255, 16, \ + CFG_VALUE_OR_DEFAULT, \ + "Set wakeup2 PIN number") +/* + * + * gExtWoWApp2KAInitPingInterval - Set Keep Alive Init Ping Interval + * @Min: 0 + * @Max: 0xffffffff + * @Default: 240 + * + * This ini is used to set Keep Alive Init Ping Interval for EXT WOW + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_KA_INIT_PING_INTERVAL \ + CFG_INI_UINT("gExtWoWApp2KAInitPingInterval", \ + 0, 0xffffffff, 240, \ + CFG_VALUE_OR_DEFAULT, \ + "Set Keep Alive Init Ping Interval") +/* + * + * gExtWoWApp2KAMinPingInterval - Set Keep Alive Minimum Ping Interval + * @Min: 0 + * @Max: 0xffffffff + * @Default: 240 + * + * This ini is used to set Keep Alive Minimum Ping Interval for EXT WOW + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_KA_MIN_PING_INTERVAL \ + CFG_INI_UINT("gExtWoWApp2KAMinPingInterval", \ + 0, 0xffffffff, 240, \ + CFG_VALUE_OR_DEFAULT, \ + "Set Keep Alive Minimum Ping Interval") +/* + * + * gExtWoWApp2KAMaxPingInterval - Set Keep Alive Maximum Ping Interval + * @Min: 0 + * @Max: 0xffffffff + * @Default: 1280 + * + * This ini is used to set Keep Alive Maximum Ping Interval for EXT WOW + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_KA_MAX_PING_INTERVAL \ + CFG_INI_UINT("gExtWoWApp2KAMaxPingInterval", \ + 0, 0xffffffff, 1280, \ + CFG_VALUE_OR_DEFAULT, \ + "Set Keep Alive Maximum Ping Interval") +/* + * + * gExtWoWApp2KAIncPingInterval - Set Keep Alive increment of Ping Interval + * @Min: 0 + * @Max: 0xffffffff + * @Default: 4 + * + * This ini is used to set Keep Alive increment of Ping Interval for EXT WOW + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_KA_INC_PING_INTERVAL \ + CFG_INI_UINT("gExtWoWApp2KAIncPingInterval", \ + 0, 0xffffffff, 4, \ + CFG_VALUE_OR_DEFAULT, \ + "Set Keep Alive increment of Ping Interval") +/* + * + * gExtWoWApp2TcpSrcPort - Set TCP source port + * @Min: 0 + * @Max: 65535 + * @Default: 5000 + * + * This ini is used to set TCP source port when EXT WOW is enabled + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_TCP_SRC_PORT \ + CFG_INI_UINT("gExtWoWApp2TcpSrcPort", \ + 0, 65535, 5000, \ + CFG_VALUE_OR_DEFAULT, \ + "Set TCP source port") +/* + * + * gExtWoWApp2TcpDstPort - Set TCP Destination port + * @Min: 0 + * @Max: 65535 + * @Default: 5001 + * + * This ini is used to set TCP Destination port when EXT WOW is enabled + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_TCP_DST_PORT \ + CFG_INI_UINT("gExtWoWApp2TcpDstPort", \ + 0, 65535, 5001, \ + CFG_VALUE_OR_DEFAULT, \ + "Set TCP Destination port") +/* + * + * gExtWoWApp2TcpTxTimeout - Set TCP tx timeout + * @Min: 0 + * @Max: 0xffffffff + * @Default: 200 + * + * This ini is used to set TCP Tx timeout when EXT WOW is enabled + * + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_TCP_TX_TIMEOUT \ + CFG_INI_UINT("gExtWoWApp2TcpTxTimeout", \ + 0, 0xffffffff, 200, \ + CFG_VALUE_OR_DEFAULT, \ + "Set TCP tx timeout") + +/* + * + * gExtWoWApp2TcpRxTimeout - Set TCP rx timeout + * @Min: 0 + * @Max: 0xffffffff + * @Default: 200 + * + * This ini is used to set TCP Rx timeout when EXT WOW is enabled + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_EXTWOW_TCP_RX_TIMEOUT \ + CFG_INI_UINT("gExtWoWApp2TcpRxTimeout", \ + 0, 0xffffffff, 200, \ + CFG_VALUE_OR_DEFAULT, \ + "ExtWow App2 tcp rx timeout") + +#define CFG_EXTWOW_ALL \ + CFG(CFG_EXTWOW_GOTO_SUSPEND) \ + CFG(CFG_EXTWOW_APP1_WAKE_PIN_NUMBER) \ + CFG(CFG_EXTWOW_APP2_WAKE_PIN_NUMBER) \ + CFG(CFG_EXTWOW_KA_INIT_PING_INTERVAL) \ + CFG(CFG_EXTWOW_KA_MIN_PING_INTERVAL) \ + CFG(CFG_EXTWOW_KA_MAX_PING_INTERVAL) \ + CFG(CFG_EXTWOW_KA_INC_PING_INTERVAL) \ + CFG(CFG_EXTWOW_TCP_SRC_PORT) \ + CFG(CFG_EXTWOW_TCP_DST_PORT) \ + CFG(CFG_EXTWOW_TCP_TX_TIMEOUT) \ + CFG(CFG_EXTWOW_TCP_RX_TIMEOUT) +#else +#define CFG_EXTWOW_ALL +#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */ +#endif /* WLAN_PMO_EXTWOW_CFG_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_gtk_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_gtk_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..41482d9779c3a941fd8c29cb3566ab7fea31960b --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_gtk_public_struct.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which shall be used in + * pmo gtk related feature. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_PMO_GTK_PUBLIC_STRUCT_H +#define _WLAN_PMO_GTK_PUBLIC_STRUCT_H + +#include "wlan_pmo_common_public_struct.h" + +#define PMO_GTK_OFFLOAD_ENABLE 0 +#define PMO_GTK_OFFLOAD_DISABLE 1 +#define PMO_KEK_LEN 64 +#define PMO_KCK_LEN 32 +#define PMO_REPLAY_COUNTER_LEN 8 +#define PMO_MAC_MAX_KEY_LENGTH 32 +#define PMO_IGTK_PN_SIZE 6 + +/** + * struct pmo_gtk_req - pmo gtk request + * @flags: optional flags + * @kck: Key confirmation key + * @kck_len: Key confirmation key length + * @kek: key encryption key + * @kek_len: KEK Length + * @replay_counter: replay_counter + * @bssid: bssid + * @is_fils_connection: is current connection with peer FILS or not. + */ +struct pmo_gtk_req { + uint32_t flags; + uint8_t kck[PMO_KCK_LEN]; + uint8_t kck_len; + uint8_t kek[PMO_KEK_LEN]; + uint32_t kek_len; + uint64_t replay_counter; + struct qdf_mac_addr bssid; + bool is_fils_connection; +}; + +/** + * struct pmo_gtk_rsp_params - pmo gtk response + * @psoc: objmgr psoc + * @vdev_id: vdev id on which arp offload needed + * @status_flag: status flags + * @refresh_cnt: number of successful GTK refresh exchanges since SET operation + * @igtk_key_index: igtk key index + * @igtk_key_length: igtk key length + * @igtk_key_rsc: igtk key index + * @igtk_key: igtk key length + */ +struct pmo_gtk_rsp_params { + uint8_t vdev_id; + uint32_t status_flag; + uint32_t refresh_cnt; + uint64_t replay_counter; + uint8_t igtk_key_index; + uint8_t igtk_key_length; + uint8_t igtk_key_rsc[PMO_IGTK_PN_SIZE]; + uint8_t igtk_key[PMO_MAC_MAX_KEY_LENGTH]; + struct qdf_mac_addr bssid; +}; + +/** + * typedef for gtk response callback + */ +typedef void (*pmo_gtk_rsp_callback)(void *callback_context, + struct pmo_gtk_rsp_params *gtk_rsp); + +/** + * struct pmo_gtk_rsp_req -gtk respsonse request + * @callback: client callback for providing gtk resposne when fwr send event + * @callback_context: client callback response + */ +struct pmo_gtk_rsp_req { + pmo_gtk_rsp_callback callback; + void *callback_context; +}; + +#endif /* end of _WLAN_PMO_GTK_PUBLIC_STRUCT_H */ + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_hw_filter_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_hw_filter_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..8315bb0d207bc9808f4fee2d15ec7db41115055b --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_hw_filter_public_struct.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file shall contain all public parameter (struct/macro/enum) + * definitions to support hardware filtering configuration. No APIs, or + * implememtations of APIs, shall be contained within. + */ + +#ifndef _WLAN_PMO_HW_FILTER_PUBLIC_STRUCT_H +#define _WLAN_PMO_HW_FILTER_PUBLIC_STRUCT_H + +/** + * pmo_hw_filter_mode - bitmap for enabled hardware filters + * @HW_FILTER_DISABLED: hardware filter is completely disabled + * @HW_FILTER_NON_ARP_BC: drop all broadcast frames, except ARP + * @HW_FILTER_NON_ICMPV6_MC: drop all multicast frames, except ICMPv6 + * + * The hardware filter is only effective in DTIM mode. Use this configuration + * to blanket drop broadcast/multicast packets at the hardware level, without + * waking up the firmware. + */ +enum pmo_hw_filter_mode { + PMO_HW_FILTER_DISABLED = 0, + PMO_HW_FILTER_NON_ARP_BC = (1 << 0), + PMO_HW_FILTER_NON_ICMPV6_MC = (1 << 1), +}; + +/** + * struct pmo_hw_filter_params - hardware filter configuration parameters + * @vdev_id: Id of the virtual device to configure + * @enable: Enable/Disable the given hw filter modes + * @mode_bitmap: the hardware filter mode bitmap to configure + */ +struct pmo_hw_filter_params { + uint8_t vdev_id; + bool enable; + enum pmo_hw_filter_mode mode_bitmap; +}; + +#endif /* _WLAN_PMO_HW_FILTER_PUBLIC_STRUCT_H */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_lphb_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_lphb_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..4cd0df3a85f8c95d49272368c56011d4c0d229f9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_lphb_public_struct.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which shall be used in + * pmo lphb offload feature. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_PMO_LPHB_PUBLIC_STRUCT_H_ +#define _WLAN_PMO_LPHB_PUBLIC_STRUCT_H_ + +#include "wlan_pmo_common_public_struct.h" + +#define PMO_SIR_LPHB_FILTER_LEN 64 + +/** + * enum lphb_ind_type -Low power heart beat indication type + * @pmo_lphb_set_en_param_indid: lphb enable indication + * @pmo_lphb_set_tcp_pararm_indid: lphb tcp param indication + * @pmo_lphb_set_tcp_pkt_filter_indid: lphb tcp packet filter indication + * @pmo_lphb_set_udp_pararm_indid: lphb udp param indication + * @pmo_lphb_set_udp_pkt_filter_indid: lphb udp packet filter indication + * @pmo_lphb_set_network_info_indid: lphb network information indication + */ +enum lphb_ind_type { + pmo_lphb_set_en_param_indid, + pmo_lphb_set_tcp_pararm_indid, + pmo_lphb_set_tcp_pkt_filter_indid, + pmo_lphb_set_udp_pararm_indid, + pmo_lphb_set_udp_pkt_filter_indid, + pmo_lphb_set_network_info_indid, +}; + +/** + * struct pmo_lphb_enable_req -Low power heart beat enable request + * @enable: lphb enable request + * @item: request item + * @session: lphb session + */ +struct pmo_lphb_enable_req { + uint8_t enable; + uint8_t item; + uint8_t session; +}; + +/** + * struct pmo_lphb_tcp_params - Low power heart beat tcp params + * @srv_ip: source ip address + * @dev_ip: destination ip address + * @src_port: source port + * @dst_port: destination port + * @timeout: tcp timeout value + * @session: session on which lphb needs to be configured + * @gateway_mac: gateway mac address + * @time_period_sec: time period in seconds + * @tcp_sn: tcp sequence number + */ +struct pmo_lphb_tcp_params { + uint32_t srv_ip; + uint32_t dev_ip; + uint16_t src_port; + uint16_t dst_port; + uint16_t timeout; + uint8_t session; + struct qdf_mac_addr gateway_mac; + uint16_t time_period_sec; + uint32_t tcp_sn; +}; + +/** + * struct pmo_lphb_tcp_filter_req - Low power heart beat tcp filter request + * @length: length of filter + * @offset: offset of filter + * @session: session on which lphb needs to be configured + * @filter: filter buffer + */ +struct pmo_lphb_tcp_filter_req { + uint16_t length; + uint8_t offset; + uint8_t session; + uint8_t filter[PMO_SIR_LPHB_FILTER_LEN]; +}; + +/** + * struct pmo_lphb_udp_params - Low power heart beat udp params + * @srv_ip: source ip address + * @dev_ip: destination ip address + * @src_port: source port + * @dst_port: destination port + * @timeout: tcp timeout value + * @session: session on which lphb needs to be configured + * @gateway_mac: gateway mac address + * @time_period_sec: time period in seconds + */ +struct pmo_lphb_udp_params { + uint32_t srv_ip; + uint32_t dev_ip; + uint16_t src_port; + uint16_t dst_port; + uint16_t interval; + uint16_t timeout; + uint8_t session; + struct qdf_mac_addr gateway_mac; +}; + +/** + * struct pmo_lphb_udp_filter_req - Low power heart beat udp filter request + * @length: length of filter + * @offset: offset of filter + * @session: session on which lphb needs to be configured + * @filter: filter buffer + */ +struct pmo_lphb_udp_filter_req { + uint16_t length; + uint8_t offset; + uint8_t session; + uint8_t filter[PMO_SIR_LPHB_FILTER_LEN]; +}; + +/** + * struct pmo_lphb_req - Low power heart beat request + * @cmd: lphb command type + * @dummy: whether dummy or not + * @params: based on command lphb request buffer + */ +struct pmo_lphb_req { + uint16_t cmd; + uint16_t dummy; + union { + struct pmo_lphb_enable_req lphb_enable_req; + struct pmo_lphb_tcp_params lphb_tcp_params; + struct pmo_lphb_tcp_filter_req lphb_tcp_filter_req; + struct pmo_lphb_udp_params lphb_udp_params; + struct pmo_lphb_udp_filter_req lphb_udp_filter_req; + } params; +}; + +/** + * struct pmo_lphb_rsp - Low power heart beat response + * @session_idx: session id + * @protocol_type: tell protocol type + * @event_reason: carry reason of lphb event + */ +struct pmo_lphb_rsp { + uint8_t session_idx; + uint8_t protocol_type; /*TCP or UDP */ + uint8_t event_reason; +}; + +/* + * Define typedef for lphb callback when fwr send response + */ +typedef +void (*pmo_lphb_callback)(void *cb_ctx, struct pmo_lphb_rsp *ind_param); + +#endif /* end of _WLAN_PMO_LPHB_PUBLIC_STRUCT_H_ */ + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_mc_addr_filtering_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_mc_addr_filtering_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..6f922991d03f8e040b104d52925de96f350bcad4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_mc_addr_filtering_public_struct.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which shall be used in + * pmo mc address filterign related features. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_PMO_MC_ADDR_FILTERING_STRUCT_H_ +#define _WLAN_PMO_MC_ADDR_FILTERING_STRUCT_H_ + +#include "wlan_pmo_common_public_struct.h" + +#define PMO_MAX_MC_ADDR_LIST 32 +#define PMO_MAX_NUM_MULTICAST_ADDRESS 240 + +/** + * struct pmo_mc_addr_list_params -pmo mc address list request params + * @psoc: objmgr psoc + * @vdev_id: vdev id on which arp offload needed + * @count: multicast address count + * @mc_addr: multicast address array + */ +struct pmo_mc_addr_list_params { + struct wlan_objmgr_psoc *psoc; + uint8_t vdev_id; + uint8_t count; + struct qdf_mac_addr mc_addr[PMO_MAX_MC_ADDR_LIST]; +}; + +/** + * struct pmo_mc_addr_list -pmo mc address list params for vdev + * @is_filter_applied: is mc list filter applied on vdev + * @mc_cnt: mc address count + * @mc_addr:mc address list + */ +struct pmo_mc_addr_list { + uint8_t is_filter_applied; + uint8_t mc_cnt; + struct qdf_mac_addr mc_addr[PMO_MAX_MC_ADDR_LIST]; +}; + +/** + * struct mcast_filter_params - mcast filter parameters + * @multicast_addr_cnt: num of addresses + * @multicast_addr: address array + * @action: operation to perform + */ +struct pmo_mcast_filter_params { + uint32_t multicast_addr_cnt; + struct qdf_mac_addr multicast_addr[PMO_MAX_NUM_MULTICAST_ADDRESS]; + uint8_t action; +}; +#endif /* end of _WLAN_PMO_MC_ADDR_FILTERING_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_ns_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_ns_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..2028d7bab546cb2ee98838263048196f4b4d7b0e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_ns_public_struct.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which shall be used in + * pmo ns offload feature. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_PMO_NS_PUBLIC_STRUCT_H_ +#define _WLAN_PMO_NS_PUBLIC_STRUCT_H_ + +#include "wlan_pmo_common_public_struct.h" + +/** + * enum pmo_ns_addr_scope - Internal identification of IPv6 addr scope + * @PMO_NS_ADDR_SCOPE_INVALID: invalid scope + * @PMO_NS_ADDR_SCOPE_NODELOCAL: node local scope + * @PMO_NS_ADDR_SCOPE_LINKLOCAL: link local scope + * @PMO_NS_ADDR_SCOPE_SITELOCAL: site local scope + * @PMO_NS_ADDR_SCOPE_ORGLOCAL: org local scope + * @PMO_NS_ADDR_SCOPE_GLOBAL: global scope + */ +enum pmo_ns_addr_scope { + PMO_NS_ADDR_SCOPE_INVALID = 0, + PMO_NS_ADDR_SCOPE_NODELOCAL = 1, + PMO_NS_ADDR_SCOPE_LINKLOCAL = 2, + PMO_NS_ADDR_SCOPE_SITELOCAL = 3, + PMO_NS_ADDR_SCOPE_ORGLOCAL = 4, + PMO_NS_ADDR_SCOPE_GLOBAL = 5 +}; + +/** + * struct pmo_ns_offload_params - pmo ns offload parameters + * @enable: true when ns offload enable + * @num_ns_offload_count: total ns entries + * @src_ipv6_addr: in request source ipv 6 address + * @self_ipv6_addr: self ipv6 address + * @target_ipv6_addr: target ipv6 address + * @self_macaddr: self mac address + * @src_ipv6_addr_valid: true if source ipv6 address is valid else false + * @target_ipv6_addr_valid: target ipv6 address are valid or not + * @target_ipv6_addr_ac_type: target ipv6 address type (unicast or anycast) + * @slot_idx: slot index + */ +struct pmo_ns_offload_params { + uint8_t enable; + uint32_t num_ns_offload_count; + uint8_t src_ipv6_addr[QDF_IPV6_ADDR_SIZE]; + uint8_t self_ipv6_addr[PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA] + [QDF_IPV6_ADDR_SIZE]; + uint8_t target_ipv6_addr[PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA] + [QDF_IPV6_ADDR_SIZE]; + struct qdf_mac_addr self_macaddr; + uint8_t src_ipv6_addr_valid; + uint8_t target_ipv6_addr_valid[PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA]; + uint8_t target_ipv6_addr_ac_type[PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA]; + uint8_t slot_idx; + struct qdf_mac_addr bssid; + enum pmo_ns_addr_scope scope[PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA]; + bool is_offload_applied; +}; + +/** + * struct pmo_ns_req - pmo ns request + * @psoc: objmgr psoc + * @vdev_id: vdev id on which arp offload needed + * @trigger: context from where arp offload triggered + * @count: ns entries count + * @ipv6_addr: ipv6 address array + * @ipv6_addr_type: ipv6 address type (unicast/anycast) array + */ +struct pmo_ns_req { + struct wlan_objmgr_psoc *psoc; + uint8_t vdev_id; + enum pmo_offload_trigger trigger; + uint32_t count; + uint8_t ipv6_addr[PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA] + [QDF_IPV6_ADDR_SIZE]; + uint8_t ipv6_addr_type[PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA]; + enum pmo_ns_addr_scope scope[PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA]; +}; +#endif /* end of _WLAN_PMO_NS_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_obj_mgmt_api.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_obj_mgmt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..2ff6c1f000046191e962ee2a5be5cf4d885b5964 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_obj_mgmt_api.h @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: declare utility API related to the pmo component + * called by other components + */ + +#ifndef _WLAN_PMO_OBJ_MGMT_API_H_ +#define _WLAN_PMO_OBJ_MGMT_API_H_ + +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD +/** + * pmo_init() - initialize pmo_ctx context. + * + * This function initializes the power manager offloads (a.k.a pmo) context. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS pmo_init(void); + +/** + * pmo_deinit() - De initialize pmo_ctx context. + * + * This function De initializes power manager offloads (a.k.a pmo) contex. + * + * Return: QDF_STATUS_SUCCESS - in case of success else return error + */ +QDF_STATUS pmo_deinit(void); + +/** + * pmo_psoc_object_created_notification(): pmo psoc create handler + * @psoc: psoc which is going to created by objmgr + * @arg: argument for vdev create handler + * + * PMO, register this api with objmgr to detect psoc is created in fwr + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS pmo_psoc_object_created_notification(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * pmo_psoc_object_destroyed_notification(): pmo psoc delete handler + * @psco: psoc which is going to delete by objmgr + * @arg: argument for vdev delete handler + * + * PMO, register this api with objmgr to detect psoc is deleted in fwr + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS pmo_psoc_object_destroyed_notification(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * pmo_vdev_object_created_notification(): pmo vdev create handler + * @vdev: vdev which is going to created by objmgr + * @arg: argument for vdev create handler + * + * PMO, register this api with objmgr to detect vdev is created in fwr + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS pmo_vdev_object_created_notification(struct wlan_objmgr_vdev *vdev, + void *arg); + +/** + * pmo_vdev_ready() - handles vdev ready in firmware event + * @vdev: vdev which is ready in firmware + * + * Objmgr vdev_create event does not guarantee vdev creation in firmware. + * Any logic that would normally go in the vdev_create event, but needs to + * communicate with firmware, needs to go here instead. + * + * Return QDF_STATUS + */ +QDF_STATUS pmo_vdev_ready(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_vdev_object_destroyed_notification(): pmo vdev delete handler + * @vdev: vdev which is going to delete by objmgr + * @arg: argument for vdev delete handler + * + * PMO, register this api with objmgr to detect vdev is deleted in fwr + * + * Return QDF_STATUS status in case of success else return error + */ +QDF_STATUS pmo_vdev_object_destroyed_notification(struct wlan_objmgr_vdev *vdev, + void *arg); + +/** + * pmo_register_suspend_handler(): register suspend handler for components + * @id: component id + * @handler: resume handler for the mention component + * @arg: argument to pass while calling resume handler + * + * Return QDF_STATUS status -in case of success else return error + */ +QDF_STATUS pmo_register_suspend_handler(enum wlan_umac_comp_id id, + pmo_psoc_suspend_handler handler, + void *arg); + +/** + * pmo_unregister_suspend_handler():unregister suspend handler for components + * @id: component id + * @handler: resume handler for the mention component + * + * Return QDF_STATUS status -in case of success else return error + */ +QDF_STATUS pmo_unregister_suspend_handler(enum wlan_umac_comp_id id, + pmo_psoc_suspend_handler handler); + +/** + * pmo_register_resume_handler(): API to register resume handler for components + * @id: component id + * @handler: resume handler for the mention component + * @arg: argument to pass while calling resume handler + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS pmo_register_resume_handler(enum wlan_umac_comp_id id, + pmo_psoc_resume_handler handler, + void *arg); + +/** + * pmo_unregister_resume_handler(): unregister resume handler for components + * @id: component id + * @handler: resume handler for the mention component + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS pmo_unregister_resume_handler(enum wlan_umac_comp_id id, + pmo_psoc_resume_handler handler); + +/** + * pmo_suspend_all_components(): API to suspend all component + * @psoc:objmgr psoc + * @suspend_type: Tell suspend type (apps suspend / runtime suspend) + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS pmo_suspend_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type suspend_type); + +/** + * pmo_resume_all_components(): API to resume all component + * @psoc:objmgr psoc + * @suspend_type: Tell suspend type from which resume is required + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS pmo_resume_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type suspend_type); + +/** + * pmo_register_pause_bitmap_notifier(): API to register pause bitmap notifier + * @psoc: objmgr psoc handle + * @handler: pause bitmap updated notifier + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS pmo_register_pause_bitmap_notifier(struct wlan_objmgr_psoc *psoc, + pmo_notify_pause_bitmap handler); + +/** + * pmo_unregister_pause_bitmap_notifier(): API to unregister pause bitmap + * notifier + * @psoc: objmgr psoc handle + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS pmo_unregister_pause_bitmap_notifier(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_register_get_pause_bitmap(): API to get register pause bitmap notifier + * @psoc: objmgr psoc handle + * @handler: pause bitmap updated notifier + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS pmo_register_get_pause_bitmap(struct wlan_objmgr_psoc *psoc, + pmo_get_pause_bitmap handler); + +/** + * pmo_unregister_get_pause_bitmap(): API to unregister get pause bitmap + * callback + * @psoc: objmgr psoc handle + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS pmo_unregister_get_pause_bitmap(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_register_get_dtim_period_callback(): API to register callback that gets + * dtim period from mlme + * @psoc: objmgr psoc handle + * @handler: pointer to the callback function + * + * Return: QDF_STATUS_SUCCESS in case of success else error + */ +QDF_STATUS pmo_register_get_dtim_period_callback(struct wlan_objmgr_psoc *psoc, + pmo_get_dtim_period handler); + +/** + * pmo_unregister_get_dtim_period_callback(): API to unregister callback that + * gets dtim period from mlme + * @psoc: objmgr psoc handle + * + * Return: QDF_STATUS_SUCCESS in case of success else error + */ +QDF_STATUS +pmo_unregister_get_dtim_period_callback(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_register_get_beacon_interval_callback(): API to register callback that + * gets beacon interval from mlme + * @psoc: objmgr psoc handle + * @handler: pointer to the callback function + * + * Return: QDF_STATUS_SUCCESS in case of success else error + */ +QDF_STATUS +pmo_register_get_beacon_interval_callback(struct wlan_objmgr_psoc *psoc, + pmo_get_beacon_interval handler); + +/** + * pmo_unregister_get_beacon_interval_callback(): API to unregister callback + * that gets beacon interval from mlme + * @psoc: objmgr psoc handle + * + * Return: QDF_STATUS_SUCCESS in case of success else error + */ +QDF_STATUS +pmo_unregister_get_beacon_interval_callback(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_register_is_device_in_low_pwr_mode(): API to get register device power + * save check notifier. + * @psoc: objmgr psoc handle + * @handler: device power save check notifier + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS pmo_register_is_device_in_low_pwr_mode(struct wlan_objmgr_psoc *psoc, + pmo_is_device_in_low_pwr_mode handler); + +/** + * pmo_unregister_is_device_in_low_pwr_mode(): API to unregister device power + * save check notifier. + * @psoc: objmgr psoc handle + * + * Return QDF_STATUS status - in case of success else return error + */ +QDF_STATUS +pmo_unregister_is_device_in_low_pwr_mode(struct wlan_objmgr_psoc *psoc); + +#else /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +static inline QDF_STATUS pmo_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS pmo_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_psoc_object_created_notification(struct wlan_objmgr_psoc *psoc, void *arg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_psoc_object_destroyed_notification(struct wlan_objmgr_psoc *psoc, void *arg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_vdev_object_created_notification(struct wlan_objmgr_vdev *vdev, void *arg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_vdev_ready(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_vdev_object_destroyed_notification(struct wlan_objmgr_vdev *vdev, void *arg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_register_suspend_handler(enum wlan_umac_comp_id id, + pmo_psoc_suspend_handler handler, + void *arg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_unregister_suspend_handler(enum wlan_umac_comp_id id, + pmo_psoc_suspend_handler handler) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_register_resume_handler(enum wlan_umac_comp_id id, + pmo_psoc_resume_handler handler, + void *arg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_unregister_resume_handler(enum wlan_umac_comp_id id, + pmo_psoc_resume_handler handler) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_suspend_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type suspend_type) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_resume_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type suspend_type) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_register_pause_bitmap_notifier(struct wlan_objmgr_psoc *psoc, + pmo_notify_pause_bitmap handler) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_unregister_pause_bitmap_notifier(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_register_get_pause_bitmap(struct wlan_objmgr_psoc *psoc, + pmo_get_pause_bitmap handler) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_unregister_get_pause_bitmap(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_register_is_device_in_low_pwr_mode(struct wlan_objmgr_psoc *psoc, + pmo_is_device_in_low_pwr_mode handler) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_unregister_is_device_in_low_pwr_mode(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_register_get_dtim_period_callback(struct wlan_objmgr_psoc *psoc, + pmo_get_dtim_period handler) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_unregister_get_dtim_period_callback(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_register_get_beacon_interval_callback(struct wlan_objmgr_psoc *psoc, + pmo_get_beacon_interval handler) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +pmo_unregister_get_beacon_interval_callback(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#endif /* end of _WLAN_PMO_OBJ_MGMT_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_obj_mgmt_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_obj_mgmt_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..2e394fe5b7384e48471317004162e3d94468c7aa --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_obj_mgmt_public_struct.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which are used for object mgmt in pmo. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_PMO_OBJ_MGMT_PUBLIC_STRUCT_H_ +#define _WLAN_PMO_OBJ_MGMT_PUBLIC_STRUCT_H_ + +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_arp_public_struct.h" +#include "wlan_pmo_ns_public_struct.h" +#include "wlan_pmo_gtk_public_struct.h" +#include "wlan_pmo_wow_public_struct.h" +#include "wlan_pmo_mc_addr_filtering_public_struct.h" +#include "wlan_pmo_hw_filter_public_struct.h" +#include "wlan_pmo_pkt_filter_public_struct.h" +#include "wlan_pmo_lphb_public_struct.h" + +/** + * typedef for vdev notifying the vdev pause bitmap new value to mlme + */ +typedef void (*pmo_notify_pause_bitmap)(uint8_t vdev_id, uint16_t value); + +/** + * typedef for function that gets cfg integer from mlme + */ +typedef QDF_STATUS (*pmo_get_cfg_int)(int cfg_id, int *value); + +/** + * typedef for function that gets dtim period from mlme + */ +typedef QDF_STATUS (*pmo_get_dtim_period)(uint8_t vdev_id, uint8_t *value); + +/** + * typedef for function that gets beacon interval from mlme + */ +typedef QDF_STATUS (*pmo_get_beacon_interval)(uint8_t vdev_id, uint16_t *value); + +/** + * typedef for getting vdev pause bitmap + */ +typedef uint16_t(*pmo_get_pause_bitmap)(uint8_t vdev_id); + +/** + * typedef for getting vdev datapath handle + */ +typedef struct cdp_vdev * (*pmo_get_vdev_dp_handle)(uint8_t vdev_id); + +/** + * typedef to know is deviec is in power save mode + */ +typedef bool (*pmo_is_device_in_low_pwr_mode)(uint8_t vdev_id); + +/* + * typedef for pld auto suspend callback during runtime suspend + */ +typedef int (*pmo_pld_auto_suspend_cb)(void); + +/* + * typedef for pld auto resume callback during runtime resume + */ +typedef int (*pmo_pld_auto_resume_cb)(void); + +/** + * struct wlan_pmo_tx_ops - structure of tx function + * pointers for pmo component + * @send_arp_offload_req: fp to send arp offload request + * @send_ns_offload_req: fp to send ns offload request + * @send_non_arp_bcast_filter_req: for enable/disable broadcast filter + * @send_set_pkt_filter: send set packet filter + * @send_clear_pkt_filter: send clear packet filter + * @send_enable_wakeup_event_req: fp to send enable wow wakeup events req + * @send_disable_wakeup_event_req: fp to send disable wow wakeup events req + * @send_add_wow_pattern: fp to send wow pattern request + * @del_wow_pattern: fp to delete wow pattern from firmware + * @send_enhance_mc_offload_req: fp to send enhanced multicast offload request + * @send_set_mc_filter_req: fp to send set mc filter request + * @send_clear_mc_filter_req: fp to send clear mc filter request + * @get_multiple_mc_filter_support: fp to get mc filter support + * @send_set_multiple_mc_filter_req: fp to send set multiple mc filter request + * @send_clear_multiple_mc_filter_req: fp to send clear multiple mc filter req + * @send_ra_filter_req: fp to send ra filter request + * @send_gtk_offload_req: fp to send gtk offload request command + * @send_get_gtk_rsp_cmd: fp to send get gtk response request cmd to firmware + * @send_action_frame_pattern_req: fp to send wow action frame patterns request + * @send_lphb_enable: fp to send lphb enable request command + * @send_lphb_tcp_params: fp to send lphb tcp params request command + * @send_lphb_tcp_filter_req: fp to send lphb tcp packet filter request command + * @send_lphb_upd_params: fp to send lphb udp params request command + * @send_lphb_udp_filter_req: fp to send lphb udp packet filter request command + * @send_vdev_param_update_req: fp to send vdev param request + * @send_vdev_set_sta_ps_param: fp to send sta vdev ps power set req + * @psoc_update_wow_bus_suspend: fp to update bus suspend req flag at wmi + * @psoc_get_host_credits: fp to get the host credits + * @psoc_get_pending_cmnds: fp to get the host pending wmi commands + * @update_target_suspend_flag: fp to update target suspend flag at wmi + * @psoc_send_wow_enable_req: fp to send wow enable request + * @psoc_send_supend_req: fp to send target suspend request + * @psoc_set_runtime_pm_in_progress: fp to set runtime pm is in progress status + * @psoc_get_runtime_pm_in_progress: fp to get runtime pm is in progress status + * @psoc_send_host_wakeup_ind: fp tp send host wake indication to fwr + * @psoc_send_target_resume_req: fp to send target resume request + * @psoc_send_d0wow_enable_req: fp to send D0 WOW enable request + * @psoc_send_d0wow_disable_req: fp to send D0 WOW disable request + * @psoc_send_idle_roam_suspend_mode: fp to send suspend mode for + * idle roam trigger to firmware. + */ +struct wlan_pmo_tx_ops { + QDF_STATUS (*send_arp_offload_req)(struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *arp_offload_req, + struct pmo_ns_offload_params *ns_offload_req); + QDF_STATUS (*send_conf_hw_filter_req)( + struct wlan_objmgr_psoc *psoc, + struct pmo_hw_filter_params *req); + QDF_STATUS (*send_ns_offload_req)(struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *arp_offload_req, + struct pmo_ns_offload_params *ns_offload_req); +#ifdef WLAN_FEATURE_PACKET_FILTERING + QDF_STATUS(*send_set_pkt_filter)(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req); + QDF_STATUS(*send_clear_pkt_filter)(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_clear_param + *pmo_clr_pkt_fltr_param); +#endif + QDF_STATUS (*send_enable_wow_wakeup_event_req)( + struct wlan_objmgr_vdev *vdev, + uint32_t *bitmap); + QDF_STATUS (*send_disable_wow_wakeup_event_req)( + struct wlan_objmgr_vdev *vdev, + uint32_t *bitmap); + QDF_STATUS (*send_add_wow_pattern)( + struct wlan_objmgr_vdev *vdev, + uint8_t ptrn_id, const uint8_t *ptrn, uint8_t ptrn_len, + uint8_t ptrn_offset, const uint8_t *mask, + uint8_t mask_len, bool user); + QDF_STATUS (*del_wow_pattern)( + struct wlan_objmgr_vdev *vdev, uint8_t ptrn_id); + QDF_STATUS (*send_enhance_mc_offload_req)( + struct wlan_objmgr_vdev *vdev, bool enable); + QDF_STATUS (*send_set_mc_filter_req)( + struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr); + QDF_STATUS (*send_clear_mc_filter_req)( + struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr); + bool (*get_multiple_mc_filter_support)( + struct wlan_objmgr_psoc *psoc); + QDF_STATUS(*send_set_multiple_mc_filter_req)( + struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list); + QDF_STATUS(*send_clear_multiple_mc_filter_req)( + struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list); + QDF_STATUS (*send_ra_filter_req)( + struct wlan_objmgr_vdev *vdev, + uint8_t default_pattern, uint16_t rate_limit_interval); + QDF_STATUS (*send_gtk_offload_req)( + struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_offload_req); + QDF_STATUS (*send_get_gtk_rsp_cmd)(struct wlan_objmgr_vdev *vdev); + QDF_STATUS (*send_action_frame_pattern_req)( + struct wlan_objmgr_vdev *vdev, + struct pmo_action_wakeup_set_params *ip_cmd); + QDF_STATUS (*send_lphb_enable)( + struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_enable_req *ts_lphb_enable); + QDF_STATUS (*send_lphb_tcp_params)( + struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_params *ts_lphb_tcp_param); + QDF_STATUS (*send_lphb_tcp_filter_req)( + struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_filter_req *ts_lphb_tcp_filter); + QDF_STATUS (*send_lphb_upd_params)( + struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_params *ts_lphb_udp_param); + QDF_STATUS (*send_lphb_udp_filter_req)( + struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_filter_req *ts_lphb_udp_filter); + QDF_STATUS (*send_vdev_param_update_req)( + struct wlan_objmgr_vdev *vdev, + uint32_t param_id, uint32_t param_value); + QDF_STATUS (*send_vdev_sta_ps_param_req)( + struct wlan_objmgr_vdev *vdev, + uint32_t ps_mode, uint32_t value); + void (*psoc_update_wow_bus_suspend)( + struct wlan_objmgr_psoc *psoc, uint8_t value); + int (*psoc_get_host_credits)( + struct wlan_objmgr_psoc *psoc); + int (*psoc_get_pending_cmnds)( + struct wlan_objmgr_psoc *psoc); + void (*update_target_suspend_flag)( + struct wlan_objmgr_psoc *psoc, uint8_t value); + bool (*is_target_suspended)(struct wlan_objmgr_psoc *psoc); + QDF_STATUS (*psoc_send_wow_enable_req)(struct wlan_objmgr_psoc *psoc, + struct pmo_wow_cmd_params *param); + QDF_STATUS (*psoc_send_supend_req)(struct wlan_objmgr_psoc *psoc, + struct pmo_suspend_params *param); + void (*psoc_set_runtime_pm_in_progress)(struct wlan_objmgr_psoc *psoc, + bool value); + bool (*psoc_get_runtime_pm_in_progress)(struct wlan_objmgr_psoc *psoc); + QDF_STATUS (*psoc_send_host_wakeup_ind)(struct wlan_objmgr_psoc *psoc); + QDF_STATUS (*psoc_send_target_resume_req)( + struct wlan_objmgr_psoc *psoc); + QDF_STATUS (*psoc_send_d0wow_enable_req)( + struct wlan_objmgr_psoc *psoc); + QDF_STATUS (*psoc_send_d0wow_disable_req)( + struct wlan_objmgr_psoc *psoc); + QDF_STATUS (*psoc_send_idle_roam_suspend_mode)( + struct wlan_objmgr_psoc *psoc, uint8_t val); + +}; + +#endif /* end of _WLAN_PMO_OBJ_MGMT_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_pkt_filter_cfg.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_pkt_filter_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..3b1a28d530424213b3f3332cd798c896d17771e3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_pkt_filter_cfg.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_PMO_PACKET_FILTER_CFG_H__ +#define WLAN_PMO_PACKET_FILTER_CFG_H__ + +/* + * + * gDisablePacketFilter - Disable packet filter disable + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * + * Related: None + * + * Usage: External + * + * + */ +#define CFG_PMO_DISABLE_PKT_FILTER CFG_INI_BOOL( \ + "gDisablePacketFilter", \ + true, \ + "Disable packet filter feature") + +#ifdef WLAN_FEATURE_PACKET_FILTERING +/* + * + * g_enable_packet_filter_bitmap - Packet filters configuration + * @Min: 0 + * @Max: 63 + * @Default: 0 + * + * To enable packet filters when target goes to suspend, clear when resume: + * bit-0 : IPv6 multicast + * bit-1 : IPv4 multicast + * bit-2 : IPv4 broadcast + * bit-3 : XID - Exchange station Identification packet, solicits the + * identification of the receiving station + * bit-4 : STP - Spanning Tree Protocol, builds logical loop free topology + * bit-5 : DTP/LLC/CDP + * DTP - Dynamic Trunking Protocol is used by Cisco switches to + * negotiate whether an interconnection between two switches + * should be put into access or trunk mode + * LLC - Logical link control, used for multiplexing, flow & error + * control + * CDP - Cisco Discovery Protocol packet contains information + * about the cisco devices in the network + * + * Supported Feature: Packet filtering + * + * Usage: Internal/External + * + * + */ +#define CFG_PMO_PKT_FILTER CFG_INI_UINT( \ + "g_enable_packet_filter_bitmap", \ + 0, 63, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Packet filter bitmap configure") + +#define CFG_PACKET_FILTER_ALL \ + CFG(CFG_PMO_PKT_FILTER) \ + CFG(CFG_PMO_DISABLE_PKT_FILTER) +#else +#define CFG_PACKET_FILTER_ALL \ + CFG(CFG_PMO_DISABLE_PKT_FILTER) +#endif /* WLAN_FEATURE_PACKET_FILTERING */ +#endif /* WLAN_PMO_PACKET_FILTER_CFG_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_pkt_filter_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_pkt_filter_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..6ed76bd4a2a10c19e358c00e59dc6088cc5e748e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_pkt_filter_public_struct.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which shall be used in + * pmo packet filter feature. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_PMO_PKT_FILTER_PUBLIC_STRUCT_H_ +#define _WLAN_PMO_PKT_FILTER_PUBLIC_STRUCT_H_ + +#include "qdf_types.h" + +#define PMO_MAX_FILTER_TEST_DATA_LEN 8 +#define PMO_MAX_NUM_TESTS_PER_FILTER 10 + +/** + * enum pmo_rcv_pkt_fltr_type: Receive Filter Parameters + * @PMO_RCV_FILTER_TYPE_INVALID: invalied filter type + * @PMO_RCV_FILTER_TYPE_FILTER_PKT: packet filter + * @PMO_RCV_FILTER_TYPE_BUFFER_PKT: buffer packet + * @PMO_RCV_FILTER_TYPE_MAX_ENUM_SIZE: max filter + */ +enum pmo_rcv_pkt_fltr_type { + PMO_RCV_FILTER_TYPE_INVALID, + PMO_RCV_FILTER_TYPE_FILTER_PKT, + PMO_RCV_FILTER_TYPE_BUFFER_PKT, + PMO_RCV_FILTER_TYPE_MAX_ENUM_SIZE +}; + +/** + * enum pmo_rcv_pkt_fltr_flag_type: Receive Filter flags + * @PMO_FILTER_CMP_TYPE_INVALID: invalied flag + * @PMO_FILTER_CMP_TYPE_EQUAL: equal + * @PMO_FILTER_CMP_TYPE_MASK_EQUAL: mask + * @PMO_FILTER_CMP_TYPE_NOT_EQUAL: not equal + * @PMO_FILTER_CMP_TYPE_MASK_NOT_EQUAL: mask not equal + * @PMO_FILTER_CMP_TYPE_MAX: max size of flag + */ +enum pmo_rcv_pkt_fltr_flag_type { + PMO_FILTER_CMP_TYPE_INVALID, + PMO_FILTER_CMP_TYPE_EQUAL, + PMO_FILTER_CMP_TYPE_MASK_EQUAL, + PMO_FILTER_CMP_TYPE_NOT_EQUAL, + PMO_FILTER_CMP_TYPE_MASK_NOT_EQUAL, + PMO_FILTER_CMP_TYPE_MAX +}; + +/** + * enum pmo_rcv_pkt_fltr_protocol_params: Receive Filter protocal parameters + * @PMO_FILTER_HDR_TYPE_INVALID: invalied type + * @PMO_FILTER_HDR_TYPE_MAC: mac protocol + * @PMO_FILTER_HDR_TYPE_ARP: arp protocol + * @PMO_FILTER_HDR_TYPE_IPV4: ipv4 protocol + * @PMO_FILTER_HDR_TYPE_IPV6: ipv6 protocol + * @PMO_FILTER_HDR_TYPE_UDP: udp protocol + * @PMO_FILTER_HDR_TYPE_MAX: max of type of protocol + */ +enum pmo_rcv_pkt_fltr_protocol_params { + PMO_FILTER_HDR_TYPE_INVALID, + PMO_FILTER_HDR_TYPE_MAC, + PMO_FILTER_HDR_TYPE_ARP, + PMO_FILTER_HDR_TYPE_IPV4, + PMO_FILTER_HDR_TYPE_IPV6, + PMO_FILTER_HDR_TYPE_UDP, + PMO_FILTER_HDR_TYPE_MAX +}; + +/** + * struct pmo_rcv_pkt_fltr_field_params - pmo packet filter field parameters + * @protocol_layer: Protocol layer + * @compare_flag: Comparison flag + * @data_length: Length of the data to compare + * @data_offset: from start of the respective frame header + * @reserved: Reserved field + * @compare_data: Data to compare + * @data_mask: Mask to be applied on the received packet data before compare + */ +struct pmo_rcv_pkt_fltr_field_params { + enum pmo_rcv_pkt_fltr_protocol_params protocol_layer; + enum pmo_rcv_pkt_fltr_flag_type compare_flag; + uint16_t data_length; + uint8_t data_offset; + uint8_t reserved; + uint8_t compare_data[PMO_MAX_FILTER_TEST_DATA_LEN]; + uint8_t data_mask[PMO_MAX_FILTER_TEST_DATA_LEN]; +}; + +/** + * struct pmo_rcv_pkt_fltr_cfg - pmo packet filter config + * @filter_id: filter id + * @filter_type: filter type + * @num_params: number of parameters + * @coalesce_time: time + * @self_macaddr: mac address + * @bssid: Bssid of the connected AP + * @params_data: data + */ +struct pmo_rcv_pkt_fltr_cfg { + uint8_t filter_id; + enum pmo_rcv_pkt_fltr_type filter_type; + uint32_t num_params; + uint32_t coalesce_time; + struct qdf_mac_addr self_macaddr; + struct qdf_mac_addr bssid; + struct pmo_rcv_pkt_fltr_field_params + params_data[PMO_MAX_NUM_TESTS_PER_FILTER]; +}; + +/** + * struct pmo_rcv_pkt_fltr_cfg - pmo receive Filter Clear Parameters + * @status: only valid for response message + * @filter_id: + * @self_macaddr: + * @bssid: peer ap address + */ +struct pmo_rcv_pkt_fltr_clear_param { + uint32_t status; + uint8_t filter_id; + struct qdf_mac_addr self_macaddr; + struct qdf_mac_addr bssid; +}; + +#endif /* end of _WLAN_PMO_PKT_FILTER_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_runtime_pm_cfg.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_runtime_pm_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..4b03cdf498dbf5cc3fba3e5a7f2cd215d3311374 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_runtime_pm_cfg.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_PMO_RUNTIME_PM_CFG_H__ +#define WLAN_PMO_RUNTIME_PM_CFG_H__ + +#ifdef FEATURE_RUNTIME_PM +/* + * + * gRuntimePMDelay - Set runtime pm's inactivity timer + * @Min: 100 + * @Max: 10000 + * @Default: 500 + * + * This ini is used to set runtime pm's inactivity timer value. + * the wlan driver will wait for this number of milliseconds of + * inactivity before performing a runtime suspend. + * + * Related: gRuntimePM + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_PMO_RUNTIME_PM_DELAY CFG_INI_UINT( \ + "gRuntimePMDelay", \ + 100, \ + 10000, \ + 500, \ + CFG_VALUE_OR_DEFAULT, \ + "Set runtime pm's inactivity timer") + +#define CFG_RUNTIME_PM_ALL \ + CFG(CFG_PMO_RUNTIME_PM_DELAY) +#else +#define CFG_RUNTIME_PM_ALL +#endif /* FEATURE_RUNTIME_PM */ +#endif /* WLAN_PMO_RUNTIME_PM_CFG_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_tgt_api.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..e08560b0e3aef872464ba6f2c888f8fb32455ac2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_tgt_api.h @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare public API for pmo to interact with target/WMI + */ + +#ifndef _WLAN_PMO_TGT_API_H_ +#define _WLAN_PMO_TGT_API_H_ + +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_arp_public_struct.h" +#include "wlan_pmo_ns_public_struct.h" +#include "wlan_pmo_gtk_public_struct.h" +#include "wlan_pmo_wow_public_struct.h" +#include "wlan_pmo_mc_addr_filtering_public_struct.h" +#include "wlan_pmo_hw_filter_public_struct.h" +#include "wlan_pmo_pkt_filter_public_struct.h" + +#define GET_PMO_TX_OPS_FROM_PSOC(psoc) \ + (pmo_psoc_get_priv(psoc)->pmo_tx_ops) + +/** + * pmo_tgt_conf_hw_filter() - configure hardware filter mode in firmware + * @psoc: the psoc to use to communicate with firmware + * @req: the configuration request + * + * Return: QDF_STATUS + */ +QDF_STATUS pmo_tgt_conf_hw_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_hw_filter_params *req); + +/** + * pmo_tgt_set_pkt_filter() - Set packet filter + * @vdev: objmgr vdev + * @pmo_set_pkt_fltr_req: + * @vdev_id: vdev id + * + * API to set packet filter + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_tgt_set_pkt_filter(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req, + uint8_t vdev_id); + +/** + * pmo_tgt_clear_pkt_filter() - Clear packet filter + * @vdev: objmgr vdev + * @pmo_clr_pkt_fltr_param: + * @vdev_id: vdev id + * + * API to clear packet filter + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS pmo_tgt_clear_pkt_filter(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_clear_param *pmo_clr_pkt_fltr_param, + uint8_t vdev_id); + +/** + * pmo_tgt_enable_arp_offload_req() - Enable arp offload req to target + * @vdev: objmgr vdev + * @vdev_id: vdev id + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_enable_arp_offload_req(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id); + +/** + * pmo_tgt_disable_arp_offload_req() - Disable arp offload req to target + * @vdev: objmgr vdev + * @vdev_id: vdev id + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_disable_arp_offload_req(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id); + +#ifdef WLAN_NS_OFFLOAD +/** + * pmo_tgt_enable_ns_offload_req() - Send ns offload req to targe + * @vdev: objmgr vdev + * @vdev_id: vdev id + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_enable_ns_offload_req(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id); + +/** + * pmo_tgt_disable_ns_offload_req() - Disable arp offload req to target + * @vdev: objmgr vdev + * @vdev_id: vdev id + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_disable_ns_offload_req(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id); +#endif /* WLAN_NS_OFFLOAD */ + +/** + * pmo_tgt_enable_wow_wakeup_event() - Send Enable wow wakeup events req to fwr + * @vdev: objmgr vdev handle + * @bitmap: Event bitmap + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_enable_wow_wakeup_event(struct wlan_objmgr_vdev *vdev, + uint32_t *bitmap); + +/** + * pmo_tgt_disable_wow_wakeup_event() - Send Disable wow wakeup events to fwr + * @vdev: objmgr vdev handle + * @bitmap: Event bitmap + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_disable_wow_wakeup_event(struct wlan_objmgr_vdev *vdev, + uint32_t *bitmap); + +/** + * pmo_tgt_send_wow_patterns_to_fw() - Sends WOW patterns to FW. + * @vdev: objmgr vdev + * @ptrn_id: pattern id + * @ptrn: pattern + * @ptrn_len: pattern length + * @ptrn_offset: pattern offset + * @mask: mask + * @mask_len: mask length + * @user: true for user configured pattern and false for default pattern + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_send_wow_patterns_to_fw(struct wlan_objmgr_vdev *vdev, + uint8_t ptrn_id, const uint8_t *ptrn, uint8_t ptrn_len, + uint8_t ptrn_offset, const uint8_t *mask, + uint8_t mask_len, bool user); + +QDF_STATUS pmo_tgt_del_wow_pattern( + struct wlan_objmgr_vdev *vdev, uint8_t ptrn_id, + bool user); + +/** + * pmo_tgt_set_mc_filter_req() - Set mcast filter command to fw + * @vdev: objmgr vdev + * @multicastAddr: mcast address + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS pmo_tgt_set_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr); + +/** + * pmo_tgt_clear_mc_filter_req() - Clear mcast filter command to fw + * @vdev: objmgr vdev + * @multicastAddr: mcast address + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS pmo_tgt_clear_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr); + +/** + * pmo_tgt_get_multiple_mc_filter_support() - get multiple mcast filter support + * @vdev: objmgr vdev + * + * Return: true if FW supports else false + */ +bool pmo_tgt_get_multiple_mc_filter_support(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_tgt_set_multiple_mc_filter_req() - Set multiple mcast filter cmd to fw + * @vdev: objmgr vdev + * @mc_list: mcast address list + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS pmo_tgt_set_multiple_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list); + +/** + * pmo_tgt_clear_multiple_mc_filter_req() - clear multiple mcast filter + * to fw + * @vdev: objmgr vdev + * @mc_list: mcast address list + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS pmo_tgt_clear_multiple_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list); + +/** + * pmo_tgt_send_enhance_multicast_offload_req() - send enhance mc offload req + * @vdev: the vdev to configure + * @action: enable or disable enhance multicast offload + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS pmo_tgt_send_enhance_multicast_offload_req( + struct wlan_objmgr_vdev *vdev, + uint8_t action); + +/** + * pmo_tgt_send_ra_filter_req() - send ra filter request to target + * @vdev: objmgr vdev handle + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS pmo_tgt_send_ra_filter_req(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_tgt_send_action_frame_pattern_req - send wow action frame patterns req + * @vdev: objmgr vdev handle + * @cmd: action frame pattern cmd + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS pmo_tgt_send_action_frame_pattern_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_action_wakeup_set_params *cmd); + +/** + * pmo_tgt_send_gtk_offload_req() - send GTK offload command to fw + * @vdev: objmgr vdev + * @gtk_req: pmo gtk req + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_send_gtk_offload_req(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_req); + +/** + * pmo_tgt_get_gtk_rsp() - send get gtk rsp command to fw + * @vdev: objmgr vdev + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_get_gtk_rsp(struct wlan_objmgr_vdev *vdev); + +/** + * pmo_tgt_gtk_rsp_evt() - receive gtk rsp event from fwr + * @psoc: objmgr psoc + * @gtk_rsp_param: gtk response parameters + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_gtk_rsp_evt(struct wlan_objmgr_psoc *psoc, + struct pmo_gtk_rsp_params *rsp_param); + +/** + * pmo_tgt_send_lphb_enable() - enable command of LPHB configuration requests + * @psoc: objmgr psoc handle + * @ts_lphb_enable: lphb enable request which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_send_lphb_enable(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_enable_req *ts_lphb_enable); + +/** + * pmo_tgt_send_lphb_tcp_params() - set tcp params of LPHB configuration req + * @psoc: objmgr psoc handle + * @ts_lphb_tcp_param: lphb tcp params which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_send_lphb_tcp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_params *ts_lphb_tcp_param); + +/** + * pmo_tgt_send_lphb_tcp_pkt_filter() - send tcp packet filter command of LPHB + * @psoc: objmgr psoc handle + * @ts_lphb_tcp_filter: lphb tcp filter request which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_send_lphb_tcp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_filter_req *ts_lphb_tcp_filter); + +/** + * pmo_tgt_send_lphb_udp_params() - Send udp param command of LPHB + * @psoc: objmgr psoc handle + * @ts_lphb_udp_param: lphb udp params which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_send_lphb_udp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_params *ts_lphb_udp_param); + +/** + * pmo_tgt_send_lphb_udp_pkt_filter() - Send udp pkt filter command of LPHB + * @psoc: objmgr psoc handle + * @ts_lphb_udp_filter: lphb udp filter request which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_send_lphb_udp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_filter_req *ts_lphb_udp_filter); + + +/** + * pmo_tgt_lphb_rsp_evt() - receive lphb rsp event from fwr + * @psoc: objmgr psoc + * @rsp_param: lphb response parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS pmo_tgt_lphb_rsp_evt(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_rsp *rsp_param); + +/** + * pmo_tgt_vdev_update_param_req() - Update vdev param value to fwr + * @vdev: objmgr vdev + * @param_id: tell vdev param id which needs to be updated in fwr + * @param_value: vdev parameter value + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_vdev_update_param_req(struct wlan_objmgr_vdev *vdev, + enum pmo_vdev_param_id param_id, uint32_t param_value); + +/** + * pmo_tgt_send_vdev_sta_ps_param() - Send vdev sta power save param to fwr + * @vdev: objmgr vdev + * @ps_param: sta mode ps power save params type + * @param_value: power save parameter value + * + * Return: QDF status + */ +QDF_STATUS pmo_tgt_send_vdev_sta_ps_param(struct wlan_objmgr_vdev *vdev, + enum pmo_sta_powersave_param ps_param, uint32_t param_value); + +/** + * pmo_tgt_update_wow_bus_suspend_state() - update wow bus suspend state flag + * @psoc: objmgr psoc + * @val: true for setting wow suspend flag to set else false + * + * Return: None + */ +void pmo_tgt_psoc_update_wow_bus_suspend_state(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * pmo_tgt_get_host_credits() - Get host credits + * @psoc: objmgr psoc + * + * Return: Pending WMI commands on success else EAGAIN on error + */ +int pmo_tgt_psoc_get_host_credits(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_tgt_get_pending_cmnds() - Get pending commands + * @psoc: objmgr psoc + * + * Return: Pending WMI commands on success else EAGAIN on error + */ +int pmo_tgt_psoc_get_pending_cmnds(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_tgt_update_target_suspend_flag() - Set WMI target Suspend flag + * @psoc: objmgr psoc + * @val: true on suspend false for resume + * + * Return: Pending WMI commands on success else EAGAIN on error + */ +void pmo_tgt_update_target_suspend_flag(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * pmo_tgt_is_target_suspended() - Get WMI target Suspend flag + * @psoc: objmgr psoc + * + * Return: true if target suspended, false otherwise. + */ +bool pmo_tgt_is_target_suspended(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_tgt_psoc_send_wow_enable_req() -Send wow enable request + * @psoc: objmgr psoc + * @param: WOW enable request buffer + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS pmo_tgt_psoc_send_wow_enable_req(struct wlan_objmgr_psoc *psoc, + struct pmo_wow_cmd_params *param); + +/** + * pmo_tgt_psoc_send_supend_req() -Send target suspend request to fwr + * @psoc: objmgr psoc + * @param: suspend request buffer + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS pmo_tgt_psoc_send_supend_req(struct wlan_objmgr_psoc *psoc, + struct pmo_suspend_params *param); + +/** + * pmo_tgt_psoc_set_runtime_pm_inprogress() -set runtime status + * @psoc: objmgr psoc + * @value: set runtime pm in progress true or false + * + * Return: none + */ +QDF_STATUS pmo_tgt_psoc_set_runtime_pm_inprogress(struct wlan_objmgr_psoc *psoc, + bool value); + +/** + * pmo_tgt_psoc_get_runtime_pm_in_progress() -get runtime status + * @psoc: objmgr psoc + * + * Return: true if runtime pm is in progress else false + */ +bool pmo_tgt_psoc_get_runtime_pm_in_progress(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_tgt_psoc_send_host_wakeup_ind() -Send host wake up indication to fwr + * @psoc: objmgr psoc + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS pmo_tgt_psoc_send_host_wakeup_ind(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_tgt_psoc_send_target_resume_req() -Send target resume request + * @psoc: objmgr psoc + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS pmo_tgt_psoc_send_target_resume_req(struct wlan_objmgr_psoc *psoc); + +/** + * pmo_tgt_psoc_send_idle_roam_monitor() - Send idle roam set suspend mode + * command to firmware + * @psoc: objmgr psoc + * @val: Set suspend mode value + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS pmo_tgt_psoc_send_idle_roam_monitor(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +#endif /* end of _WLAN_PMO_TGT_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_ucfg_api.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..7d7d06c57bf4dc6b63e62264a97cbb74fddaa9ec --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_ucfg_api.h @@ -0,0 +1,1975 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare public API related to the pmo called by north bound HDD/OSIF + */ + +#ifndef _WLAN_PMO_UCFG_API_H_ +#define _WLAN_PMO_UCFG_API_H_ + +#include "wlan_pmo_arp_public_struct.h" +#include "wlan_pmo_ns_public_struct.h" +#include "wlan_pmo_gtk_public_struct.h" +#include "wlan_pmo_mc_addr_filtering.h" +#include "wlan_pmo_mc_addr_filtering_public_struct.h" +#include "wlan_pmo_wow_public_struct.h" +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_obj_mgmt_api.h" +#include "wlan_pmo_pkt_filter_public_struct.h" +#include "wlan_pmo_hw_filter_public_struct.h" + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD + +/** + * ucfg_pmo_psoc_open() - pmo psoc object open + * @psoc: objmgr vdev + *. + * This function used to open pmo psoc object by user space + * + * Return: true in case success else false + */ +QDF_STATUS ucfg_pmo_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_psoc_open() - pmo psoc object close + * @psoc: objmgr vdev + *. + * This function used to close pmo psoc object by user space + * + * Return: true in case success else false + */ +QDF_STATUS ucfg_pmo_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_apf_instruction_size() - get the current APF instruction size + * @psoc: the psoc to query + * + * Return: APF instruction size + */ +uint32_t ucfg_pmo_get_apf_instruction_size(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_num_wow_filters() - get the supported number of WoW filters + * @psoc: the psoc to query + * + * Return: number of WoW filters supported + */ +uint8_t ucfg_pmo_get_num_wow_filters(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_is_ap_mode_supports_arp_ns() - Check ap mode support arp&ns offload + * @psoc: objmgr psoc + * @vdev_opmode: vdev opmode + * + * Return: true in case support else false + */ +bool ucfg_pmo_is_ap_mode_supports_arp_ns(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE vdev_opmode); + +/** + * ucfg_pmo_is_vdev_connected() - to check whether peer is associated or not + * @vdev: objmgr vdev + * + * Return: true in case success else false + */ +bool ucfg_pmo_is_vdev_connected(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_pmo_is_vdev_supports_offload() - check offload is supported on vdev + * @vdev: objmgr vdev + * + * Return: true in case success else false + */ +bool ucfg_pmo_is_vdev_supports_offload(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_pmo_get_psoc_config(): API to get the psoc user configurations of pmo + * @psoc: objmgr psoc handle + * @psoc_cfg: fill the current psoc user configurations. + * + * Return pmo psoc configurations + */ +QDF_STATUS ucfg_pmo_get_psoc_config(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg); + +/** + * ucfg_pmo_update_psoc_config(): API to update the psoc user configurations + * @psoc: objmgr psoc handle + * @psoc_cfg: pmo psoc configurations + * + * This api shall be used for soc config initialization as well update. + * In case of update caller must first call pmo_get_psoc_cfg to get + * current config and then apply changes on top of current config. + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_update_psoc_config(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg); + +/** + * ucfg_pmo_psoc_set_caps() - overwrite configured device capability flags + * @psoc: the psoc for which the capabilities apply + * @caps: the cabability information to configure + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_pmo_psoc_set_caps(struct wlan_objmgr_psoc *psoc, + struct pmo_device_caps *caps); + +/** + * ucfg_pmo_is_arp_offload_enabled() - Get arp offload enable or not + * @psoc: pointer to psoc object + * + * Return: arp offload enable or not + */ +bool +ucfg_pmo_is_arp_offload_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_set_arp_offload_enabled() - Set arp offload enable or not + * @psoc: pointer to psoc object + * @val: enable/disable arp offload + * + * Return: None + */ +void +ucfg_pmo_set_arp_offload_enabled(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * ucfg_pmo_is_ssdp_enabled() - Get ssdp enable or not + * @psoc: pointer to psoc object + * + * Return: enable/disable ssdp + */ +bool +ucfg_pmo_is_ssdp_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_is_ns_offloaded() - Get ns offload support or not + * @psoc: pointer to psoc object + * + * Return: ns offload or not + */ +bool +ucfg_pmo_is_ns_offloaded(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_sta_dynamic_dtim() - Get dynamic dtim + * @psoc: pointer to psoc object + * + * Return: dynamic dtim + */ +uint8_t +ucfg_pmo_get_sta_dynamic_dtim(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_sta_mod_dtim() - Get modulated dtim + * @psoc: pointer to psoc object + * + * Return: modulated dtim + */ +uint8_t +ucfg_pmo_get_sta_mod_dtim(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_set_sta_mod_dtim() - Set modulated dtim + * @psoc: pointer to psoc object + * @val: modulated dtim + * + * Return: None + */ +void +ucfg_pmo_set_sta_mod_dtim(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * ucfg_pmo_is_mc_addr_list_enabled() - Get multicast address list enable or not + * @psoc: pointer to psoc object + * + * Return: multicast address list enable or not + */ +bool +ucfg_pmo_is_mc_addr_list_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_power_save_mode() - Get power save mode + * @psoc: pointer to psoc object + * + * Return: power save mode + */ +enum powersave_mode +ucfg_pmo_get_power_save_mode(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_set_power_save_mode() - Set power save mode + * @psoc: pointer to psoc object + * @val: power save mode + * + * Return: None + */ +void +ucfg_pmo_set_power_save_mode(struct wlan_objmgr_psoc *psoc, + enum powersave_mode val); + +/** + * ucfg_pmo_get_max_ps_poll() - Get max power save poll + * @psoc: pointer to psoc object + * + * Return: power save poll + */ +uint8_t +ucfg_pmo_get_max_ps_poll(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_power_save_offload_enabled() - Get power save offload enabled type + * @psoc: pointer to psoc object + * + * Return: power save offload enabled type + */ +uint8_t +ucfg_pmo_power_save_offload_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_tgt_psoc_send_idle_roam_suspend_mode() - Send suspend mode to + * firmware + * @psoc: pointer to psoc object + * @val: Set suspend mode on/off sent from userspace + * + * Return: QDF_STATUS_SUCCESS if suspend mode is sent to fw else return + * corresponding QDF_STATUS failure code. + */ +QDF_STATUS +ucfg_pmo_tgt_psoc_send_idle_roam_suspend_mode(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * ucfg_pmo_enable_wakeup_event() - enable wow wakeup events + * @psoc: objmgr psoc + * @vdev_id: vdev id + * @wow_event: wow event to enable + * + * Return: none + */ +void ucfg_pmo_enable_wakeup_event(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + WOW_WAKE_EVENT_TYPE wow_event); + +/** + * ucfg_pmo_disable_wakeup_event() - disable wow wakeup events + * @psoc: objmgr psoc + * @vdev_id: vdev id + * @wow_event: wow event to disable + * + * Return: none + */ +void ucfg_pmo_disable_wakeup_event(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + WOW_WAKE_EVENT_TYPE wow_event); + +/** + * ucfg_pmo_cache_arp_offload_req(): API to cache arp req in pmo vdev priv ctx + * @arp_req: pmo arp req param + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_cache_arp_offload_req(struct pmo_arp_req *arp_req); + +/** + * ucfg_pmo_check_arp_offload(): API to check if arp offload cache/send is req + * @psoc: objmgr psoc handle + * @trigger: trigger reason + * @vdev_id: vdev_id + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_check_arp_offload(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id); + +/** + * ucfg_pmo_flush_arp_offload_req(): API to flush arp req from pmo vdev priv ctx + * @vdev: objmgr vdev param + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_flush_arp_offload_req(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_pmo_enable_arp_offload_in_fwr(): API to enable arp req in fwr + * @vdev: objmgr vdev param + * @trigger: triger reason for enable arp offload + * + * API to enable cache arp req in fwr from pmo vdev priv ctx + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS +ucfg_pmo_enable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger); + +/** + * ucfg_pmo_disable_arp_offload_in_fwr(): API to disable arp req in fwr + * @vdev: objmgr vdev param + * @trigger: triger reason for disable arp offload + * API to disable cache arp req in fwr + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS +ucfg_pmo_disable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger); + +/** + * ucfg_pmo_get_arp_offload_params() - API to get arp offload params + * @vdev: objmgr vdev + * @params: output pointer to hold offload params + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS +ucfg_pmo_get_arp_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *params); + +/** + * ucfg_pmo_cache_ns_offload_req(): API to cache ns req in pmo vdev priv ctx + * @ns_req: pmo ns req param + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_cache_ns_offload_req(struct pmo_ns_req *ns_req); + +/** + * ucfg_pmo_ns_offload_check(): API to check if offload cache/send is required + * @psoc: pbjmgr psoc handle + * @trigger: trigger reason to enable ns offload + * @vdev_id: vdev id + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_ns_offload_check(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id); + +/** + * ucfg_pmo_flush_ns_offload_req(): API to flush ns req from pmo vdev priv ctx + * @vdev: vdev ojbmgr handle + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_flush_ns_offload_req(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_pmo_enable_ns_offload_in_fwr(): API to enable ns req in fwr + * @arp_req: pmo arp req param + * @trigger: trigger reason to enable ns offload + * + * API to enable cache ns req in fwr from pmo vdev priv ctx + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS +ucfg_pmo_enable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger); + +/** + * ucfg_pmo_disable_ns_offload_in_fwr(): API to disable ns req in fwr + * @arp_req: pmo arp req param + * @trigger: trigger reason to disable ns offload + * + * API to disable ns req in fwr + * + * Return QDF_STATUS -in case of success else return error + */ +QDF_STATUS +ucfg_pmo_disable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger); + +/** + * ucfg_pmo_get_ns_offload_params() - API to get ns offload params + * @vdev: objmgr vdev + * @params: output pointer to hold offload params + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS +ucfg_pmo_get_ns_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_ns_offload_params *params); + +/** + * ucfg_pmo_ns_addr_scope() - Convert linux specific IPv6 addr scope to + * WLAN driver specific value + * @scope: linux specific IPv6 addr scope + * + * Return: PMO identifier of linux IPv6 addr scope + */ +enum pmo_ns_addr_scope +ucfg_pmo_ns_addr_scope(uint32_t ipv6_scope); + +/** + * ucfg_pmo_enable_hw_filter_in_fwr() - enable previously configured hw filter + * @vdev: objmgr vdev to configure + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_pmo_enable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_pmo_enable_action_frame_patterns() - enable the action frame wake up + * patterns as part of the enable host offloads. + * @vdev: objmgr vdev to configure + * @suspend_type: Suspend type. Runtime PM or System Suspend mode + * + * Return: QDF_STATUS + */ +QDF_STATUS +ucfg_pmo_enable_action_frame_patterns(struct wlan_objmgr_vdev *vdev, + enum qdf_suspend_type suspend_type); + +/** + * ucfg_pmo_disable_action_frame_patterns() - Reset the action frame wake up + * patterns as a part of suspend resume. + * @vdev: objmgr vdev to configure + * + * Return: QDF_STATUS + */ +QDF_STATUS +ucfg_pmo_disable_action_frame_patterns(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_pmo_disable_hw_filter_in_fwr() - disable previously configured hw filter + * @vdev: objmgr vdev to configure + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_pmo_disable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_pmo_max_mc_addr_supported() - to get max support mc address + * @psoc: objmgr psoc + * + * Return: max mc addr supported count for all vdev in corresponding psoc + */ +uint8_t ucfg_pmo_max_mc_addr_supported(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_cache_mc_addr_list(): API to cache mc addr list in pmo vdev priv obj + * @psoc: objmgr psoc handle + * @vdev_id: vdev id + * @gtk_req: pmo gtk req param + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_cache_mc_addr_list( + struct pmo_mc_addr_list_params *mc_list_config); + +/** + * ucfg_pmo_flush_mc_addr_list(): API to flush mc addr list in pmo vdev priv obj + * @psoc: objmgr psoc handle + * @vdev_id: vdev id + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_flush_mc_addr_list(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * ucfg_pmo_enhance_mc_filter_enable() - enable enhanced multicast filtering + * @vdev: the vdev to enable enhanced multicast filtering for + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +ucfg_pmo_enhanced_mc_filter_enable(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_enhanced_mc_filter_enable(vdev); +} + +/** + * ucfg_pmo_enhance_mc_filter_disable() - disable enhanced multicast filtering + * @vdev: the vdev to disable enhanced multicast filtering for + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +ucfg_pmo_enhanced_mc_filter_disable(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_enhanced_mc_filter_disable(vdev); +} + +/** + * ucfg_pmo_enable_mc_addr_filtering_in_fwr(): Enable cached mc add list in fwr + * @psoc: objmgr psoc handle + * @vdev_id: vdev id + * @gtk_req: pmo gtk req param + * @action: true for enable els false + * + * API to enable cached mc add list in fwr + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_enable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger); + +/** + * ucfg_pmo_disable_mc_addr_filtering_in_fwr(): Disable cached mc addr list + * @psoc: objmgr psoc handle + * @vdev_id: vdev id + * @gtk_req: pmo gtk req param + * @action: true for enable els false + * + * API to disable cached mc add list in fwr + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_disable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger); + +/** + * ucfg_pmo_get_mc_addr_list() - API to get mc addr list configured + * @psoc: objmgr psoc + * @vdev_id: vdev identifier + * @mc_list_req: output pointer to hold mc addr list params + * + * Return: QDF_STATUS_SUCCESS in case of success else return error + */ +QDF_STATUS +ucfg_pmo_get_mc_addr_list(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + struct pmo_mc_addr_list *mc_list_req); + +/** + * ucfg_pmo_cache_gtk_offload_req(): API to cache gtk req in pmo vdev priv obj + * @vdev: objmgr vdev handle + * @gtk_req: pmo gtk req param + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_cache_gtk_offload_req(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_req); + +/** + * ucfg_pmo_flush_gtk_offload_req(): Flush saved gtk req from pmo vdev priv obj + * @vdev: objmgr vdev handle + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_flush_gtk_offload_req(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_pmo_enable_gtk_offload_in_fwr(): enable cached gtk request in fwr + * @vdev: objmgr vdev handle + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_enable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_pmo_disable_gtk_offload_in_fwr(): disable cached gtk request in fwr + * @vdev: objmgr vdev handle + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_disable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev); + +#ifdef WLAN_FEATURE_PACKET_FILTERING +/** + * ucfg_pmo_get_pkt_filter_bitmap() - get default packet filters bitmap + * @psoc: the psoc to query + * + * Return: retrieve packet filter bitmap configuration + */ +uint8_t ucfg_pmo_get_pkt_filter_bitmap(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_num_packet_filters() - get the number of packet filters + * @psoc: the psoc to query + * + * Return: number of packet filters + */ +uint32_t ucfg_pmo_get_num_packet_filters(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_set_pkt_filter() - Set packet filter + * @psoc: objmgr psoc handle + * @pmo_set_pkt_fltr_req: packet filter set param + * @vdev_id: vdev id + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS +ucfg_pmo_set_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req, + uint8_t vdev_id); + +/** + * ucfg_pmo_clear_pkt_filter() - Clear packet filter + * @psoc: objmgr psoc handle + * @pmo_clr_pkt_fltr_param: packet filter clear param + * @vdev_id: vdev id + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS ucfg_pmo_clear_pkt_filter( + struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_clear_param *pmo_clr_pkt_fltr_param, + uint8_t vdev_id); +#else +static inline uint8_t +ucfg_pmo_get_pkt_filter_bitmap(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint32_t +ucfg_pmo_get_num_packet_filters(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline QDF_STATUS +ucfg_pmo_set_pkt_filter( + struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_clear_pkt_filter( + struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_clear_param *pmo_clr_pkt_fltr_param, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * ucfg_pmo_get_wow_enable() - Get wow enable type + * @psoc: pointer to psoc object + * + * Return: wow enable type + */ +enum pmo_wow_enable_type +ucfg_pmo_get_wow_enable(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_set_wow_enable() - Set wow enable type + * @psoc: pointer to psoc object + * @val: wow enalbe value + * + * Return: None + */ +void +ucfg_pmo_set_wow_enable(struct wlan_objmgr_psoc *psoc, + enum pmo_wow_enable_type val); + +/** + * ucfg_pmo_is_wowlan_deauth_enabled() - Get wowlan deauth enable + * @psoc: pointer to psoc object + * + * Return: wowlan deauth enable or not + */ +bool +ucfg_pmo_is_wowlan_deauth_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_is_wowlan_disassoc_enabled() - Get wowlan disassoc enable + * @psoc: pointer to psoc object + * + * Return: wowlan disassoc enable + */ +bool +ucfg_pmo_is_wowlan_disassoc_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_gtk_rsp(): API to send gtk response request to fwr + * @vdev: objmgr vdev handle + * @gtk_rsp: pmo gtk response request + * + * This api will send gtk response request to fwr + * + * Return QDF_STATUS_SUCCESS -in case of success else return error + */ +QDF_STATUS +ucfg_pmo_get_gtk_rsp(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_rsp_req *gtk_rsp_req); + +/** + * ucfg_pmo_update_extscan_in_progress(): update extscan is in progress flags + * @vdev: objmgr vdev handle + * @value:true if extscan is in progress else false + * + * Return: TRUE/FALSE + */ +void ucfg_pmo_update_extscan_in_progress(struct wlan_objmgr_vdev *vdev, + bool value); + +/** + * ucfg_pmo_update_p2plo_in_progress(): update p2plo is in progress flags + * @vdev: objmgr vdev handle + * @value:true if p2plo is in progress else false + * + * Return: TRUE/FALSE + */ +void ucfg_pmo_update_p2plo_in_progress(struct wlan_objmgr_vdev *vdev, + bool value); + +/** + * ucfg_pmo_lphb_config_req() - Handles lphb config request for psoc + * @psoc: objmgr psoc handle + * @lphb_req: low power heart beat request + * @lphb_cb_ctx: Context which needs to pass to soif when lphb callback called + * @callback: upon receiving of lphb indication from fwr call lphb callback + * + * Return: QDF status + */ +QDF_STATUS ucfg_pmo_lphb_config_req(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_req *lphb_req, + void *lphb_cb_ctx, + pmo_lphb_callback callback); + +/** + * ucfg_pmo_psoc_update_power_save_mode() - update power save mode + * @vdev: objmgr vdev handle + * @value:vdev power save mode + * + * Return: None + */ +void ucfg_pmo_psoc_update_power_save_mode(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * ucfg_pmo_psoc_update_dp_handle() - update psoc data path handle + * @psoc: objmgr psoc handle + * @dp_hdl: psoc data path handle + * + * Return: None + */ +void ucfg_pmo_psoc_update_dp_handle(struct wlan_objmgr_psoc *psoc, + void *dp_hdl); + +/** + * ucfg_pmo_psoc_update_htc_handle() - update psoc htc layer handle + * @psoc: objmgr psoc handle + * @htc_handle: psoc host-to-tagret layer (htc) handle + * + * Return: None + */ +void ucfg_pmo_psoc_update_htc_handle(struct wlan_objmgr_psoc *psoc, + void *htc_handle); + +/** + * ucfg_pmo_psoc_set_hif_handle() - Set psoc hif layer handle + * @psoc: objmgr psoc handle + * @hif_handle: hif context handle + * + * Return: None + */ +void ucfg_pmo_psoc_set_hif_handle(struct wlan_objmgr_psoc *psoc, + void *hif_handle); + +/** + * ucfg_pmo_psoc_set_txrx_pdev_id() - Set psoc pdev txrx layer handle + * @psoc: objmgr psoc handle + * @txrx_pdev_id: txrx pdev identifier + * + * Return: None + */ +void ucfg_pmo_psoc_set_txrx_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t txrx_pdev_id); + +/** + * ucfg_pmo_psoc_user_space_suspend_req() - Handles user space suspend req + * @psoc: objmgr psoc handle + * @type: type of suspend + * + * Handles user space suspend indication for psoc + * + * Return: QDF status + */ +QDF_STATUS ucfg_pmo_psoc_user_space_suspend_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type); + +/** + * ucfg_pmo_psoc_user_space_resume_req() - Handles user space resume req + * @psoc: objmgr psoc handle + * @type: type of suspend from which resume needed + * + * Handles user space resume indication for psoc + * + * Return: QDF status + */ +QDF_STATUS ucfg_pmo_psoc_user_space_resume_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type); + +/** + * ucfg_pmo_suspend_all_components() - Suspend all components + * @psoc: objmgr psoc handle + * @type: type of suspend + * + * Suspend all components registered to pmo + * + * Return: QDF status + */ +QDF_STATUS ucfg_pmo_suspend_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type); + +/** + * ucfg_pmo_resume_all_components() - Resume all components + * @psoc: objmgr psoc handle + * @type: type of suspend from which resume needed + * + * Resume all components registered to pmo + * + * Return: QDF status + */ +QDF_STATUS ucfg_pmo_resume_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type); + +/** + * ucfg_pmo_psoc_bus_suspend_req(): handles bus suspend for psoc + * @psoc: objmgr psoc + * @type: is this suspend part of runtime suspend or system suspend? + * @wow_params: collection of wow enable override parameters + * + * Bails if a scan is in progress. + * Calls the appropriate handlers based on configuration and event. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS ucfg_pmo_psoc_bus_suspend_req( + struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type, + struct pmo_wow_enable_params *wow_params); + +#ifdef FEATURE_RUNTIME_PM +/** + * ucfg_pmo_psoc_bus_runtime_suspend(): handles bus runtime suspend for psoc + * @psoc: objmgr psoc + * @pld_cb: callback to call link auto suspend + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS ucfg_pmo_psoc_bus_runtime_suspend(struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_suspend_cb pld_cb); + +/** + * ucfg_pmo_psoc_bus_runtime_resume(): handles bus runtime resume for psoc + * @psoc: objmgr psoc + * @pld_cb: callback to call link auto resume + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS ucfg_pmo_psoc_bus_runtime_resume(struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_resume_cb pld_cb); +#endif + +/** + * ucfg_pmo_psoc_suspend_target() -Send suspend target command + * @psoc: objmgr psoc handle + * @disable_target_intr: disable target interrupt + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS +ucfg_pmo_psoc_suspend_target(struct wlan_objmgr_psoc *psoc, + int disable_target_intr); + +QDF_STATUS +ucfg_pmo_add_wow_user_pattern(struct wlan_objmgr_vdev *vdev, + struct pmo_wow_add_pattern *ptrn); + +/** + * ucfg_pmo_del_wow_pattern() - Delete WoWl patterns + * @vdev: objmgr vdev + * + * Return:QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS +ucfg_pmo_del_wow_pattern(struct wlan_objmgr_vdev *vdev); + +QDF_STATUS +ucfg_pmo_del_wow_user_pattern(struct wlan_objmgr_vdev *vdev, + uint8_t pattern_id); + +/** + * ucfg_pmo_psoc_bus_resume() -handle bus resume request for psoc + * @psoc: objmgr psoc handle + * @type: is this suspend part of runtime suspend or system suspend? + * + * Return:QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS ucfg_pmo_psoc_bus_resume_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type); + +/** + * ucfg_pmo_get_wow_bus_suspend(): API to check if wow bus is suspended or not + * @psoc: objmgr psoc handle + * + * Return: True if bus suspende else false + */ +bool ucfg_pmo_get_wow_bus_suspend(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_psoc_handle_initial_wake_up() - update initial wake up + * @cb_ctx: objmgr psoc handle as void * due to htc layer is not aware psoc + * + * Return: None + */ +void ucfg_pmo_psoc_handle_initial_wake_up(void *cb_ctx); + +/** + * ucfg_pmo_psoc_is_target_wake_up_received() - Get initial wake up status + * @psoc: objmgr psoc handle + * + * Return: 0 on success else error code + */ +int ucfg_pmo_psoc_is_target_wake_up_received(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_psoc_is_target_wake_up_received() - Clear initial wake up status + * @psoc: objmgr psoc handle + * + * Return: 0 on success else error code + */ +int ucfg_pmo_psoc_clear_target_wake_up(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_psoc_target_suspend_acknowledge() - Clear initial wake up status + * @psoc: objmgr psoc handle + * + * Return: None + */ +void ucfg_pmo_psoc_target_suspend_acknowledge(void *context, bool wow_nack); + +/** + * ucfg_pmo_psoc_wakeup_host_event_received() - got host wake up evennt from fwr + * @psoc: objmgr psoc handle + * + * Return: None + */ +void ucfg_pmo_psoc_wakeup_host_event_received(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_config_listen_interval() - function to configure listen interval + * @vdev: objmgr vdev + * @listen_interval: new listen interval passed by user + * + * This function allows user to configure listen interval dynamically + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_pmo_config_listen_interval(struct wlan_objmgr_vdev *vdev, + uint32_t listen_interval); + +/** + * ucfg_pmo_config_modulated_dtim() - function to configure modulated dtim + * @vdev: objmgr vdev handle + * @param_value: New modulated dtim value passed by user + * + * This function configures the modulated dtim in firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_pmo_config_modulated_dtim(struct wlan_objmgr_vdev *vdev, + uint32_t mod_dtim); + +#ifdef WLAN_FEATURE_WOW_PULSE +/** + * ucfg_pmo_is_wow_pulse_enabled() - to get wow pulse enable configuration + * @psoc: objmgr psoc handle + * + * Return: wow pulse enable configuration + */ +bool ucfg_pmo_is_wow_pulse_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_wow_pulse_pin() - to get wow pulse pin configuration + * @psoc: objmgr psoc handle + * + * Return: wow pulse pin configuration + */ +uint8_t ucfg_pmo_get_wow_pulse_pin(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_wow_pulse_interval_high() - to get wow pulse interval high + * @psoc: objmgr psoc handle + * + * Return: wow pulse interval high configuration + */ +uint16_t ucfg_pmo_get_wow_pulse_interval_high(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_wow_pulse_interval_low() - to get wow pulse interval low + * @psoc: objmgr psoc handle + * + * Return: wow pulse interval high configuration + */ +uint16_t ucfg_pmo_get_wow_pulse_interval_low(struct wlan_objmgr_psoc *psoc); +#else +static inline bool +ucfg_pmo_is_wow_pulse_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline uint8_t +ucfg_pmo_get_wow_pulse_pin(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint16_t +ucfg_pmo_get_wow_pulse_interval_high(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} +#endif + +/** + * ucfg_pmo_is_active_mode_offloaded() - get active mode offload configuration + * @psoc: objmgr psoc handle + * + * Return: retrieve active mode offload configuration + */ +bool ucfg_pmo_is_active_mode_offloaded(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_auto_power_fail_mode() - to get auto power save failure mode + * @psoc: objmgr psoc handle + * + * Return: auto power save failure mode configuration + */ +enum pmo_auto_pwr_detect_failure_mode +ucfg_pmo_get_auto_power_fail_mode(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_set_wow_data_inactivity_timeout() - Set wow data inactivity timeout + * @psoc: pointer to psoc object + * @val: wow data inactivity timeout value + * + * Return: None + */ +void +ucfg_pmo_set_wow_data_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * ucfg_pmo_is_pkt_filter_enabled() - pmo packet filter feature enable or not + * @psoc: objmgr psoc handle + * + * Return: pmo packet filter feature enable/disable + */ +bool ucfg_pmo_is_pkt_filter_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_active_uc_apf_mode() - to get the modes active APF + * for MC/BC packets + * @psoc: objmgr psoc handle + * + * Return: the modes active APF + */ +enum active_apf_mode +ucfg_pmo_get_active_uc_apf_mode(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_get_active_mc_bc_apf_mode() - to get the modes active APF + * for uc packets + * @psoc: objmgr psoc handle + * + * Return: the modes active APF + */ +enum active_apf_mode +ucfg_pmo_get_active_mc_bc_apf_mode(struct wlan_objmgr_psoc *psoc); +#ifdef FEATURE_WLAN_APF +/** + * ucfg_pmo_is_apf_enabled() - to get apf configuration + * @psoc: objmgr psoc handle + * + * Return: true if enabled, it is intersection of ini and target cap + */ +bool ucfg_pmo_is_apf_enabled(struct wlan_objmgr_psoc *psoc); +#else +static inline bool ucfg_pmo_is_apf_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} +#endif + +#else /* WLAN_POWER_MANAGEMENT_OFFLOAD */ +static inline QDF_STATUS +ucfg_pmo_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline uint32_t +ucfg_pmo_get_apf_instruction_size(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint8_t +ucfg_pmo_get_pkt_filter_bitmap(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint32_t +ucfg_pmo_get_num_packet_filters(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint8_t +ucfg_pmo_get_num_wow_filters(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline QDF_STATUS +ucfg_pmo_get_psoc_config( + struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_update_psoc_config( + struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_psoc_set_caps( + struct wlan_objmgr_psoc *psoc, + struct pmo_device_caps *caps) +{ + return QDF_STATUS_SUCCESS; +} + +static inline bool +ucfg_pmo_is_ap_mode_supports_arp_ns( + struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE vdev_opmode) +{ + return true; +} + +static inline bool +ucfg_pmo_is_vdev_connected(struct wlan_objmgr_vdev *vdev) +{ + return true; +} + +static inline bool +ucfg_pmo_is_vdev_supports_offload(struct wlan_objmgr_vdev *vdev) +{ + return true; +} + +static inline void +ucfg_pmo_enable_wakeup_event( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint32_t *bitmap) +{ +} + +static inline void +ucfg_pmo_disable_wakeup_event( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint32_t bitmap) +{ +} + +static inline QDF_STATUS +ucfg_pmo_cache_arp_offload_req(struct pmo_arp_req *arp_req) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_pmo_check_arp_offload(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_flush_arp_offload_req(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_enable_arp_offload_in_fwr( + struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_disable_arp_offload_in_fwr( + struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_get_arp_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *params) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_cache_ns_offload_req(struct pmo_ns_req *ns_req) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_pmo_ns_offload_check(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_flush_ns_offload_req(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_enable_ns_offload_in_fwr( + struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_disable_ns_offload_in_fwr( + struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_get_ns_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_ns_offload_params *params) +{ + return QDF_STATUS_SUCCESS; +} + +static inline enum pmo_ns_addr_scope +ucfg_pmo_ns_addr_scope(uint32_t ipv6_scope) +{ + return PMO_NS_ADDR_SCOPE_INVALID; +} + +static inline QDF_STATUS +ucfg_pmo_cache_mc_addr_list( + struct pmo_mc_addr_list_params *mc_list_config) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_flush_mc_addr_list( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_enable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_disable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger) +{ + return QDF_STATUS_SUCCESS; +} + +static inline uint8_t +ucfg_pmo_max_mc_addr_supported(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline QDF_STATUS +ucfg_pmo_get_mc_addr_list(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + struct pmo_mc_addr_list *mc_list_req) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_cache_gtk_offload_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_req) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_flush_gtk_offload_req(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_enable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_disable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_set_pkt_filter( + struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_clear_pkt_filter( + struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_clear_param *pmo_clr_pkt_fltr_param, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_get_gtk_rsp( + struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_rsp_req *gtk_rsp_req) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void +ucfg_pmo_update_extscan_in_progress( + struct wlan_objmgr_vdev *vdev, + bool value) +{ +} + +static inline void +ucfg_pmo_update_p2plo_in_progress( + struct wlan_objmgr_vdev *vdev, + bool value) +{ +} + +static inline QDF_STATUS +ucfg_pmo_lphb_config_req( + struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_req *lphb_req, void *lphb_cb_ctx, + pmo_lphb_callback callback) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void +ucfg_pmo_psoc_update_power_save_mode( + struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ +} + +static inline void +ucfg_pmo_psoc_update_dp_handle( + struct wlan_objmgr_psoc *psoc, + void *dp_handle) +{ +} + +static inline void +ucfg_pmo_psoc_update_htc_handle( + struct wlan_objmgr_psoc *psoc, + void *htc_handle) +{ +} + +static inline void +ucfg_pmo_psoc_set_hif_handle( + struct wlan_objmgr_psoc *psoc, + void *hif_handle) +{ +} + +static inline void +ucfg_pmo_psoc_set_txrx_pdev_id( + struct wlan_objmgr_psoc *psoc, + uint8_t txrx_pdev_id) +{ +} + +static inline void +ucfg_pmo_psoc_handle_initial_wake_up(void *cb_ctx) +{ +} + +static inline QDF_STATUS +ucfg_pmo_psoc_user_space_suspend_req( + struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_psoc_user_space_resume_req( + struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_suspend_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_resume_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_psoc_bus_suspend_req( + struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type, + struct pmo_wow_enable_params *wow_params) +{ + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_RUNTIME_PM +static inline QDF_STATUS +ucfg_pmo_psoc_bus_runtime_suspend( + struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_suspend_cb pld_cb) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_psoc_bus_runtime_resume( + struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_suspend_cb pld_cb) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +static inline QDF_STATUS +ucfg_pmo_psoc_suspend_target( + struct wlan_objmgr_psoc *psoc, + int disable_target_intr) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_add_wow_user_pattern( + struct wlan_objmgr_vdev *vdev, + struct pmo_wow_add_pattern *ptrn) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_del_wow_user_pattern( + struct wlan_objmgr_vdev *vdev, + uint8_t pattern_id) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_pmo_del_wow_pattern(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_psoc_bus_resume_req( + struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return QDF_STATUS_SUCCESS; +} + +static inline bool +ucfg_pmo_get_wow_bus_suspend(struct wlan_objmgr_psoc *psoc) +{ + return true; +} + +static inline int +ucfg_pmo_psoc_is_target_wake_up_received(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline int +ucfg_pmo_psoc_clear_target_wake_up(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline void +ucfg_pmo_psoc_target_suspend_acknowledge(void *context, bool wow_nack) +{ +} + +static inline void +ucfg_pmo_psoc_wakeup_host_event_received(struct wlan_objmgr_psoc *psoc) +{ +} + +static inline QDF_STATUS +ucfg_pmo_enable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_disable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_enhanced_mc_filter_enable(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_enhanced_mc_filter_disable(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_config_listen_interval(struct wlan_objmgr_vdev *vdev, + uint32_t listen_interval) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_pmo_config_modulated_dtim(struct wlan_objmgr_vdev *vdev, + uint32_t mod_dtim) +{ + return QDF_STATUS_SUCCESS; +} + +static inline bool +ucfg_pmo_is_arp_offload_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline void +ucfg_pmo_set_arp_offload_enabled(struct wlan_objmgr_psoc *psoc, + bool val) +{ +} + +static inline bool +ucfg_pmo_is_wow_pulse_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline uint8_t +ucfg_pmo_get_wow_pulse_pin(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint16_t +ucfg_pmo_get_wow_pulse_interval_high(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint16_t +ucfg_pmo_get_wow_pulse_interval_low(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline bool +ucfg_pmo_is_active_mode_offloaded(struct wlan_objmgr_psoc *psoc) +{ + return true; +} + +static inline enum pmo_auto_pwr_detect_failure_mode +ucfg_pmo_get_auto_power_fail_mode(struct wlan_objmgr_psoc *psoc) +{ + return PMO_FW_TO_CRASH_ON_PWR_FAILURE; +} + +static inline bool ucfg_pmo_is_apf_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline bool ucfg_pmo_is_ssdp_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline bool ucfg_pmo_is_ns_offloaded(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline uint8_t +ucfg_pmo_get_sta_dynamic_dtim(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint8_t +ucfg_pmo_get_sta_mod_dtim(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline void +ucfg_pmo_set_sta_mod_dtim(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ +} + +static inline bool +ucfg_pmo_is_mc_addr_list_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline enum powersave_mode +ucfg_pmo_get_power_save_mode(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline void +ucfg_pmo_set_power_save_mode(struct wlan_objmgr_psoc *psoc, + enum powersave_mode val) +{ +} + +static inline uint8_t +ucfg_pmo_get_max_ps_poll(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint8_t +ucfg_pmo_power_save_offload_enabled(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline void +ucfg_pmo_set_wow_data_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ +} + +static inline bool +ucfg_pmo_is_pkt_filter_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +enum active_apf_mode +ucfg_pmo_get_active_uc_apf_mode(struct wlan_objmgr_psoc *psoc); +{ + return 0; +} + +enum active_apf_mode +ucfg_pmo_get_active_mc_bc_apf_mode(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +/** + * ucfg_pmo_extwow_is_goto_suspend_enabled() - Get extwow goto suspend enable + * @psoc: pointer to psoc object + * + * Return: extend wow goto suspend enable or not + */ +bool +ucfg_pmo_extwow_is_goto_suspend_enabled(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app1_wakeup_pin_num() - Get wakeup1 PIN number + * @psoc: pointer to psoc object + * + * Return: wakeup1 PIN number + */ +uint8_t +ucfg_pmo_extwow_app1_wakeup_pin_num(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app2_wakeup_pin_num() - Get wakeup2 PIN number + * @psoc: pointer to psoc object + * + * Return: wakeup2 PIN number + */ +uint8_t +ucfg_pmo_extwow_app2_wakeup_pin_num(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app2_init_ping_interval() - Get keep alive init ping interval + * @psoc: pointer to psoc object + * + * Return: keep alive init ping interval + */ +uint32_t +ucfg_pmo_extwow_app2_init_ping_interval(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app2_min_ping_interval() - Get keep alive min ping interval + * @psoc: pointer to psoc object + * + * Return: keep alive min ping interval + */ +uint32_t +ucfg_pmo_extwow_app2_min_ping_interval(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app2_max_ping_interval() - Get keep alive max ping interval + * @psoc: pointer to psoc object + * + * Return: keep alive max ping interval + */ +uint32_t +ucfg_pmo_extwow_app2_max_ping_interval(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app2_inc_ping_interval() - Get keep alive inc ping interval + * @psoc: pointer to psoc object + * + * Return: keep alive inc ping interval + */ +uint32_t +ucfg_pmo_extwow_app2_inc_ping_interval(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app2_tcp_src_port() - Get TCP source port + * @psoc: pointer to psoc object + * + * Return: TCP source port + */ +uint16_t +ucfg_pmo_extwow_app2_tcp_src_port(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app2_tcp_dst_port() - Get TCP Destination port + * @psoc: pointer to psoc object + * + * Return: TCP Destination port + */ +uint16_t +ucfg_pmo_extwow_app2_tcp_dst_port(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app2_tcp_tx_timeout() - Get TCP Tx timeout + * @psoc: pointer to psoc object + * + * Return: TCP Tx timeout + */ +uint32_t +ucfg_pmo_extwow_app2_tcp_tx_timeout(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_pmo_extwow_app2_tcp_rx_timeout() - to get extwow tcp rx timeout + * @psoc: objmgr psoc handle + * + * Return: retrieve extwow app2 tcp rx timeout configuration + */ +uint32_t +ucfg_pmo_extwow_app2_tcp_rx_timeout(struct wlan_objmgr_psoc *psoc); + +#else +static inline bool +ucfg_pmo_extwow_is_goto_suspend_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + +static inline uint32_t +ucfg_pmo_extwow_app1_wakeup_pin_num(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint32_t +ucfg_pmo_extwow_app2_wakeup_pin_num(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint32_t +ucfg_pmo_extwow_app2_init_ping_interval(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint32_t +ucfg_pmo_extwow_app2_min_ping_interval(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint32_t +ucfg_pmo_extwow_app2_max_ping_interval(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint32_t +ucfg_pmo_extwow_app2_inc_ping_interval(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint16_t +ucfg_pmo_extwow_app2_tcp_src_port(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint16_t +ucfg_pmo_extwow_app2_tcp_dst_port(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint32_t +ucfg_pmo_extwow_app2_tcp_tx_timeout(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} + +static inline uint32_t +ucfg_pmo_extwow_app2_tcp_rx_timeout(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} +#endif + +#ifdef FEATURE_RUNTIME_PM +/** + * ucfg_pmo_get_runtime_pm_delay() - Get runtime pm's inactivity timer + * @psoc: pointer to psoc object + * + * Return: runtime pm's inactivity timer + */ +uint32_t +ucfg_pmo_get_runtime_pm_delay(struct wlan_objmgr_psoc *psoc); +#else +static inline uint32_t +ucfg_pmo_get_runtime_pm_delay(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} +#endif /* FEATURE_RUNTIME_PM */ + +/** + * ucfg_pmo_get_enable_sap_suspend - Return enable_sap_suspend value to caller + * @psoc: Pointer to psoc object + * + * Return: The value of enable_sap_suspend as stored in CFG + */ +bool +ucfg_pmo_get_enable_sap_suspend(struct wlan_objmgr_psoc *psoc); + +#ifdef SYSTEM_PM_CHECK +/** + * ucfg_pmo_notify_system_resume() - system resume notification to pmo + * @psoc: pointer to psoc object + * + * Return: None + */ +void +ucfg_pmo_notify_system_resume(struct wlan_objmgr_psoc *psoc); +#else +static inline +void ucfg_pmo_notify_system_resume(struct wlan_objmgr_psoc *psoc) +{ +} +#endif +#endif /* end of _WLAN_PMO_UCFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_wow_public_struct.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_wow_public_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..0f236723b965859d093e691b0d20b8f4e01133d1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_wow_public_struct.h @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various struct, macros which shall be used in + * pmo wow related features. + * + * Note: This file shall not contain public API's prototype/declarations. + * + */ + +#ifndef _WLAN_PMO_WOW_PUBLIC_STRUCT_H_ +#include "wlan_pmo_lphb_public_struct.h" + +#define _WLAN_PMO_WOW_PUBLIC_STRUCT_H_ + +#ifndef PMO_WOW_FILTERS_MAX +#define PMO_WOW_FILTERS_MAX 22 +#endif + +#define PMO_WOWL_PTRN_MAX_SIZE 146 +#define PMO_WOWL_PTRN_MASK_MAX_SIZE 19 +#define PMO_WOWL_BCAST_PATTERN_MAX_SIZE 146 + +#define PMO_WOW_INTER_PTRN_TOKENIZER ';' +#define PMO_WOW_INTRA_PTRN_TOKENIZER ':' + +#define PMO_WOW_PTRN_MASK_VALID 0xFF +#define PMO_NUM_BITS_IN_BYTE 8 + + +/* Action frame categories */ + +#define PMO_MAC_ACTION_SPECTRUM_MGMT 0 +#define PMO_MAC_ACTION_QOS_MGMT 1 +#define PMO_MAC_ACTION_DLP 2 +#define PMO_MAC_ACTION_BLKACK 3 +#define PMO_MAC_ACTION_PUBLIC_USAGE 4 +#define PMO_MAC_ACTION_RRM 5 +#define PMO_MAC_ACTION_FAST_BSS_TRNST 6 +#define PMO_MAC_ACTION_HT 7 +#define PMO_MAC_ACTION_SA_QUERY 8 +#define PMO_MAC_ACTION_PROT_DUAL_PUB 9 +#define PMO_MAC_ACTION_WNM 10 +#define PMO_MAC_ACTION_UNPROT_WNM 11 +#define PMO_MAC_ACTION_TDLS 12 +#define PMO_MAC_ACITON_MESH 13 +#define PMO_MAC_ACTION_MHF 14 +#define PMO_MAC_SELF_PROTECTED 15 +#define PMO_MAC_ACTION_WME 17 +#define PMO_MAC_ACTION_FST 18 +#define PMO_MAC_ACTION_VHT 21 +#define PMO_MAC_ACTION_MAX 256 + +/* + * ALLOWED_ACTION_FRAMES_BITMAP + * + * Bitmask is based on the below. The frames with 0's + * set to their corresponding bit can be dropped in FW. + * + * -----------------------------+-----+-------+ + * Type | Bit | Allow | + * -----------------------------+-----+-------+ + * PMO_ACTION_SPECTRUM_MGMT 0 1 + * PMO_ACTION_QOS_MGMT 1 1 + * PMO_ACTION_DLP 2 0 + * PMO_ACTION_BLKACK 3 0 + * PMO_ACTION_PUBLIC_USAGE 4 1 + * PMO_ACTION_RRM 5 0 + * PMO_ACTION_FAST_BSS_TRNST 6 0 + * PMO_ACTION_HT 7 0 + * PMO_ACTION_SA_QUERY 8 1 + * PMO_ACTION_PROT_DUAL_PUB 9 1 + * PMO_ACTION_WNM 10 1 + * PMO_ACTION_UNPROT_WNM 11 0 + * PMO_ACTION_TDLS 12 0 + * PMO_ACITON_MESH 13 0 + * PMO_ACTION_MHF 14 0 + * PMO_SELF_PROTECTED 15 0 + * PMO_ACTION_WME 17 1 + * PMO_ACTION_FST 18 1 + * PMO_ACTION_VHT 21 1 + * ----------------------------+------+-------+ + */ +#define SYSTEM_SUSPEND_ALLOWED_ACTION_FRAMES_BITMAP0 \ + ((1 << PMO_MAC_ACTION_SPECTRUM_MGMT) | \ + (1 << PMO_MAC_ACTION_QOS_MGMT) | \ + (1 << PMO_MAC_ACTION_PUBLIC_USAGE) | \ + (1 << PMO_MAC_ACTION_SA_QUERY) | \ + (1 << PMO_MAC_ACTION_PROT_DUAL_PUB) | \ + (1 << PMO_MAC_ACTION_WNM) | \ + (1 << PMO_MAC_ACTION_WME) | \ + (1 << PMO_MAC_ACTION_FST) | \ + (1 << PMO_MAC_ACTION_VHT)) + +#define ALLOWED_ACTION_FRAMES_BITMAP1 0x0 +#define ALLOWED_ACTION_FRAMES_BITMAP2 0x0 +#define ALLOWED_ACTION_FRAMES_BITMAP3 0x0 +#define ALLOWED_ACTION_FRAMES_BITMAP4 0x0 +#define ALLOWED_ACTION_FRAMES_BITMAP5 0x0 +#define ALLOWED_ACTION_FRAMES_BITMAP6 0x0 +#define ALLOWED_ACTION_FRAMES_BITMAP7 0x0 + +#define ALLOWED_ACTION_FRAME_MAP_WORDS (PMO_MAC_ACTION_MAX / 32) + +#define RUNTIME_PM_ALLOWED_ACTION_FRAMES_BITMAP0 \ + ((1 << PMO_MAC_ACTION_SPECTRUM_MGMT) | \ + (1 << PMO_MAC_ACTION_QOS_MGMT) | \ + (1 << PMO_MAC_ACTION_PUBLIC_USAGE) | \ + (1 << PMO_MAC_ACTION_RRM) | \ + (1 << PMO_MAC_ACTION_SA_QUERY) | \ + (1 << PMO_MAC_ACTION_PROT_DUAL_PUB) | \ + (1 << PMO_MAC_ACTION_WNM) | \ + (1 << PMO_MAC_ACTION_WME) | \ + (1 << PMO_MAC_ACTION_FST) | \ + (1 << PMO_MAC_ACTION_VHT)) + +/* Public Action for 20/40 BSS Coexistence */ +#define PMO_MAC_ACTION_MEASUREMENT_PILOT 7 + +#define DROP_PUBLIC_ACTION_FRAME_BITMAP \ + (1 << PMO_MAC_ACTION_MEASUREMENT_PILOT) + +#ifndef ANI_SUPPORT_11H +/* + * DROP_SPEC_MGMT_ACTION_FRAME_BITMAP + * + * Bitmask is based on the below. The frames with 1's + * set to their corresponding bit can be dropped in FW. + * + * ----------------------------------+-----+------+ + * Type | Bit | Drop | + * ----------------------------------+-----+------+ + * ACTION_SPCT_MSR_REQ 0 1 + * ACTION_SPCT_TPC_REQ 2 1 + * ----------------------------------+-----+------+ + */ +#define DROP_SPEC_MGMT_ACTION_FRAME_BITMAP \ + ((1 << ACTION_SPCT_MSR_REQ) |\ + (1 << ACTION_SPCT_TPC_REQ)) +#else +/* + * If 11H support is defined, dont drop the above action category of + * spectrum mgmt action frames as host driver is processing them. + */ +#define DROP_SPEC_MGMT_ACTION_FRAME_BITMAP 0 +#endif /* ANI_SUPPORT_11H */ + +#define PMO_SUPPORTED_ACTION_CATE 256 +#define PMO_SUPPORTED_ACTION_CATE_ELE_LIST (PMO_SUPPORTED_ACTION_CATE/32) + +/** + * struct pmo_action_wakeup_set_params - action wakeup set params + * @vdev_id: virtual device id + * @operation: 0 reset to fw default, 1 set the bits, + * 2 add the setting bits, 3 delete the setting bits + * @action_category_map: bit mapping. + * @action_per_category: bitmap per action category + */ +struct pmo_action_wakeup_set_params { + uint32_t vdev_id; + uint32_t operation; + uint32_t action_category_map[PMO_SUPPORTED_ACTION_CATE_ELE_LIST]; + uint32_t action_per_category[PMO_SUPPORTED_ACTION_CATE]; +}; + +/** + * enum pmo_wow_action_wakeup_opertion: describe action wakeup operation + * @pmo_action_wakeup_reset: reset + * @pmo_action_wakeup_set: set + * @pmo_action_wakeup_add_set: add and set + * @pmo_action_wakeup_del_set: delete and set + */ +enum pmo_wow_action_wakeup_opertion { + pmo_action_wakeup_reset = 0, + pmo_action_wakeup_set, + pmo_action_wakeup_add_set, + pmo_action_wakeup_del_set, +}; + +/** + * enum pmo_wow_state: enumeration of wow state + * @pmo_wow_state_none: not in wow state + * @pmo_wow_state_legacy_d0: in d0 wow state trigger by legacy d0 wow command + * @pmo_wow_state_unified_d0: in d0 wow state trigger by unified wow command + * @pmo_wow_state_unified_d3: in d3 wow state trigger by unified wow command + */ +enum pmo_wow_state { + pmo_wow_state_none = 0, + pmo_wow_state_legacy_d0, + pmo_wow_state_unified_d0, + pmo_wow_state_unified_d3, +}; + +/** + * struct pmo_wow - store wow patterns + * @wow_enable: wow enable/disable + * @wow_enable_cmd_sent: is wow enable command sent to fw + * @is_wow_bus_suspended: true if bus is suspended + * @wow_state: state of wow + * @target_suspend: target suspend event + * @target_resume: target resume event + * @wow_nack: wow negative ack flag + * @wow_initial_wake_up: target initial wake up is received + * @wow_wake_lock: wow wake lock + * @lphb_cache: lphb cache + * @lphb_cb_ctx: callback context for lphb, kept as void* as + * osif structures are opaque to pmo. + * @pmo_lphb_callback: registered os if calllback function + * @ptrn_id_def: default pattern id counter for legacy firmware + * @ptrn_id_usr: user pattern id counter for legacy firmware + * + * This structure stores wow patterns and + * wow related parameters in host. + */ +struct pmo_wow { + bool wow_enable; + bool wow_enable_cmd_sent; + bool is_wow_bus_suspended; + enum pmo_wow_state wow_state; + qdf_event_t target_suspend; + qdf_event_t target_resume; + int wow_nack; + atomic_t wow_initial_wake_up; + qdf_wake_lock_t wow_wake_lock; + /* + * currently supports only vdev 0. + * cache has two entries: one for TCP and one for UDP. + */ + struct pmo_lphb_req lphb_cache[2]; + void *lphb_cb_ctx; + pmo_lphb_callback lphb_cb; + + uint8_t ptrn_id_def; + uint8_t ptrn_id_usr; +}; + +/* WOW related structures */ +/** + * struct pmo_wow_add_pattern - wow pattern add structure + * @pattern_id: pattern id + * @pattern_byte_offset: pattern byte offset from beginning of the 802.11 + * packet to start of the wake-up pattern + * @pattern_size: pattern size + * @pattern: pattern byte stream + * @pattern_mask_size: pattern mask size + * @pattern_mask: pattern mask + */ +struct pmo_wow_add_pattern { + uint8_t pattern_id; + uint8_t pattern_byte_offset; + uint8_t pattern_size; + uint8_t pattern[PMO_WOWL_BCAST_PATTERN_MAX_SIZE]; + uint8_t pattern_mask_size; + uint8_t pattern_mask[PMO_WOWL_BCAST_PATTERN_MAX_SIZE]; +}; + +/** + * struct pmo_wow_cmd_params - wow cmd parameter + * @enable: wow enable or disable flag + * @can_suspend_link: flag to indicate if link can be suspended + * @pause_iface_config: interface config + */ +struct pmo_wow_cmd_params { + bool enable; + bool can_suspend_link; + uint8_t pause_iface_config; + uint32_t flags; +}; + +/** + * struct pmo_suspend_params - suspend cmd parameter + * @disable_target_intr: disable target interrupt + */ +struct pmo_suspend_params { + uint8_t disable_target_intr; +}; + +#endif /* end of _WLAN_PMO_WOW_PUBLIC_STRUCT_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_wow_pulse_cfg.h b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_wow_pulse_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..6dfaf67da49c5badaba22cadd8fcab02e9741ed1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/inc/wlan_pmo_wow_pulse_cfg.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_PMO_WOW_PULSE_CFG_H__ +#define WLAN_PMO_WOW_PULSE_CFG_H__ + +#ifdef WLAN_FEATURE_WOW_PULSE +/* + * + * gwow_pulse_support - WOW pulse feature configuration + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * When set to 1 WOW pulse feature will be enabled. + * + * Related: gwow_pulse_pin, gwow_pulse_interval_low, gwow_pulse_interval_high + * + * Supported Feature: WOW pulse + * + * Usage: External + * + * + */ +#define CFG_PMO_WOW_PULSE_ENABLE CFG_INI_BOOL("gwow_pulse_support", \ + 0, \ + "Enable wow pulse") + +/* + * + * gwow_pulse_pin - GPIO pin for WOW pulse + * @Min: 0 + * @Max: 254 + * @Default: 35 + * + * Which PIN to send the Pulse + * + * Supported Feature: WOW pulse + * + * Usage: External + * + * + */ +#define CFG_PMO_WOW_PULSE_PIN CFG_INI_UINT("gwow_pulse_pin", \ + 0, 254, 35, \ + CFG_VALUE_OR_DEFAULT, \ + "Pin for wow pulse") + +/* + * + * gwow_pulse_interval_low - Pulse interval low + * @Min: 160 + * @Max: 480 + * @Default: 180 + * + * The interval of low level in the pulse + * + * Supported Feature: WOW pulse + * + * Usage: External + * + * + */ +#define CFG_PMO_WOW_PULSE_LOW CFG_INI_UINT("gwow_pulse_interval_low", \ + 160, 480, 180, \ + CFG_VALUE_OR_DEFAULT, \ + "Interval of low pulse") + +/* + * + * gwow_pulse_interval_high - Pulse interval high + * @Min: 20 + * @Max: 40 + * @Default: 20 + * + * The interval of high level in the pulse + * + * Supported Feature: WOW pulse + * + * Usage: External + * + * + */ +#define CFG_PMO_WOW_PULSE_HIGH CFG_INI_UINT("gwow_pulse_interval_high", \ + 20, 40, 20, \ + CFG_VALUE_OR_DEFAULT, \ + "Interval of high pulse") + +#define CFG_WOW_PULSE_ALL \ + CFG(CFG_PMO_WOW_PULSE_ENABLE) \ + CFG(CFG_PMO_WOW_PULSE_PIN) \ + CFG(CFG_PMO_WOW_PULSE_LOW) \ + CFG(CFG_PMO_WOW_PULSE_HIGH) +#else +#define CFG_WOW_PULSE_ALL +#endif /* WLAN_FEATURE_WOW_PULSE */ +#endif /* WLAN_PMO_WOW_PULSE_CFG_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_obj_mgmt_api.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_obj_mgmt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..b6a458cf80c4da8473ab086803ab8a38d9e3a667 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_obj_mgmt_api.c @@ -0,0 +1,846 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: define utility API related to the pmo component + * called by other components + */ + +#include "wlan_pmo_obj_mgmt_api.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_static_config.h" +#include "wlan_pmo_main.h" +#include "target_if_pmo.h" + +QDF_STATUS pmo_init(void) +{ + QDF_STATUS status; + struct wlan_pmo_ctx *pmo_ctx; + + pmo_enter(); + if (pmo_allocate_ctx() != QDF_STATUS_SUCCESS) { + pmo_err("unable to allocate psoc ctx"); + status = QDF_STATUS_E_FAULT; + goto out; + } + + pmo_ctx = pmo_get_context(); + if (!pmo_ctx) { + pmo_err("unable to get pmo ctx"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_PMO, + pmo_psoc_object_created_notification, + (void *)pmo_ctx); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("unable to register psoc create handle"); + goto out; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_PMO, + pmo_psoc_object_destroyed_notification, + (void *)pmo_ctx); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("unable to register psoc create handle"); + goto out; + } + + status = wlan_objmgr_register_vdev_create_handler( + WLAN_UMAC_COMP_PMO, + pmo_vdev_object_created_notification, + (void *)pmo_ctx); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("unable to register vdev create handle"); + goto out; + } + + status = wlan_objmgr_register_vdev_destroy_handler( + WLAN_UMAC_COMP_PMO, + pmo_vdev_object_destroyed_notification, + (void *)pmo_ctx); + if (status != QDF_STATUS_SUCCESS) + pmo_err("unable to register vdev create handle"); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_deinit(void) +{ + QDF_STATUS status; + struct wlan_pmo_ctx *pmo_ctx; + + pmo_enter(); + pmo_ctx = pmo_get_context(); + if (!pmo_ctx) { + pmo_err("unable to get pmo ctx"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_PMO, + pmo_psoc_object_created_notification, + (void *)pmo_ctx); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("unable to unregister psoc create handle"); + goto out; + } + + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_PMO, + pmo_psoc_object_destroyed_notification, + (void *)pmo_ctx); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("unable to unregister psoc create handle"); + goto out; + } + + status = wlan_objmgr_unregister_vdev_create_handler( + WLAN_UMAC_COMP_PMO, + pmo_vdev_object_created_notification, + (void *)pmo_ctx); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("unable to unregister vdev create handle"); + goto out; + } + + status = wlan_objmgr_unregister_vdev_destroy_handler( + WLAN_UMAC_COMP_PMO, + pmo_vdev_object_destroyed_notification, + (void *)pmo_ctx); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("unable to unregister vdev create handle"); + goto out; + } + +out: + pmo_free_ctx(); + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_psoc_object_created_notification( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct pmo_psoc_priv_obj *psoc_ctx = NULL; + QDF_STATUS status; + struct wlan_pmo_ctx *pmo_ctx; + + pmo_enter(); + pmo_ctx = pmo_get_context(); + if (!pmo_ctx) { + QDF_ASSERT(0); + pmo_err("unable to get pmo ctx"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + psoc_ctx = qdf_mem_malloc(sizeof(*psoc_ctx)); + if (!psoc_ctx) { + pmo_err("Failed to allocate pmo_psoc"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_PMO, + psoc_ctx, + QDF_STATUS_SUCCESS); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to attach psoc_ctx with psoc"); + qdf_mem_free(psoc_ctx); + status = QDF_STATUS_E_FAILURE; + goto out; + } + qdf_spinlock_create(&psoc_ctx->lock); + qdf_wake_lock_create(&psoc_ctx->wow.wow_wake_lock, "pmo_wow_wl"); + status = qdf_event_create(&psoc_ctx->wow.target_suspend); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("target suspend event initialization failed"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + status = qdf_event_create(&psoc_ctx->wow.target_resume); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("target resume event initialization failed"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + qdf_atomic_init(&psoc_ctx->wow.wow_initial_wake_up); + /* Register PMO tx ops*/ + target_if_pmo_register_tx_ops(&psoc_ctx->pmo_tx_ops); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_psoc_object_destroyed_notification( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + struct pmo_psoc_priv_obj *psoc_ctx = NULL; + QDF_STATUS status; + + pmo_enter(); + + psoc_ctx = pmo_psoc_get_priv(psoc); + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_PMO, + psoc_ctx); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to detach psoc_ctx from psoc"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + qdf_spinlock_destroy(&psoc_ctx->lock); + qdf_event_destroy(&psoc_ctx->wow.target_suspend); + qdf_event_destroy(&psoc_ctx->wow.target_resume); + qdf_wake_lock_destroy(&psoc_ctx->wow.wow_wake_lock); + qdf_mem_zero(psoc_ctx, sizeof(*psoc_ctx)); + qdf_mem_free(psoc_ctx); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_vdev_object_created_notification( + struct wlan_objmgr_vdev *vdev, void *arg) +{ + struct pmo_psoc_priv_obj *psoc_ctx = NULL; + struct wlan_objmgr_psoc *psoc; + struct pmo_vdev_priv_obj *vdev_ctx; + QDF_STATUS status; + + pmo_enter(); + + psoc = pmo_vdev_get_psoc(vdev); + + psoc_ctx = pmo_psoc_get_priv(psoc); + + vdev_ctx = qdf_mem_malloc(sizeof(*vdev_ctx)); + if (!vdev_ctx) { + pmo_err("Failed to allocate vdev_ctx"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + status = wlan_objmgr_vdev_component_obj_attach(vdev, + WLAN_UMAC_COMP_PMO, + (void *)vdev_ctx, QDF_STATUS_SUCCESS); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to attach vdev_ctx with vdev"); + qdf_mem_free(vdev_ctx); + goto out; + } + + qdf_spinlock_create(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->ptrn_match_enable = + psoc_ctx->psoc_cfg.ptrn_match_enable_all_vdev; + vdev_ctx->pmo_psoc_ctx = psoc_ctx; + qdf_atomic_init(&vdev_ctx->gtk_err_enable); + +out: + pmo_exit(); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_vdev_ready(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + /* Register static configuration with firmware */ + pmo_register_wow_wakeup_events(vdev); + + /* Register default wow patterns with firmware */ + pmo_register_wow_default_patterns(vdev); + + pmo_vdev_put_ref(vdev); + + /* + * The above APIs should return a status but don't. + * Just return success for now. + */ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_vdev_object_destroyed_notification( + struct wlan_objmgr_vdev *vdev, void *arg) +{ + struct pmo_vdev_priv_obj *vdev_ctx = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + status = wlan_objmgr_vdev_component_obj_detach(vdev, + WLAN_UMAC_COMP_PMO, + (void *)vdev_ctx); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to detach vdev_ctx with vdev"); + + qdf_spinlock_destroy(&vdev_ctx->pmo_vdev_lock); + qdf_mem_free(vdev_ctx); + + return status; +} + +QDF_STATUS pmo_register_suspend_handler( + enum wlan_umac_comp_id id, + pmo_psoc_suspend_handler handler, + void *arg) +{ + struct wlan_pmo_ctx *pmo_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_enter(); + pmo_ctx = pmo_get_context(); + if (!pmo_ctx) { + QDF_ASSERT(0); + pmo_err("unable to get pmo ctx"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + if (id < 0 || id >= WLAN_UMAC_MAX_COMPONENTS) { + pmo_err("component id: %d is %s then valid components id", + id, id < 0 ? "Less" : "More"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + qdf_spin_lock_bh(&pmo_ctx->lock); + pmo_ctx->pmo_suspend_handler[id] = handler; + pmo_ctx->pmo_suspend_handler_arg[id] = arg; + qdf_spin_unlock_bh(&pmo_ctx->lock); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_unregister_suspend_handler( + enum wlan_umac_comp_id id, + pmo_psoc_suspend_handler handler) +{ + struct wlan_pmo_ctx *pmo_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_enter(); + pmo_ctx = pmo_get_context(); + if (!pmo_ctx) { + QDF_ASSERT(0); + pmo_err("unable to get pmo ctx"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + if (id < 0 || id >= WLAN_UMAC_MAX_COMPONENTS) { + pmo_err("component id: %d is %s then valid components id", + id, id < 0 ? "Less" : "More"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + qdf_spin_lock_bh(&pmo_ctx->lock); + if (pmo_ctx->pmo_suspend_handler[id] == handler) { + pmo_ctx->pmo_suspend_handler[id] = NULL; + pmo_ctx->pmo_suspend_handler_arg[id] = NULL; + qdf_spin_unlock_bh(&pmo_ctx->lock); + } else { + qdf_spin_unlock_bh(&pmo_ctx->lock); + pmo_err("can't find suspend handler for component id: %d ", id); + status = QDF_STATUS_E_FAILURE; + } +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_register_resume_handler( + enum wlan_umac_comp_id id, + pmo_psoc_resume_handler handler, + void *arg) +{ + struct wlan_pmo_ctx *pmo_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_enter(); + pmo_ctx = pmo_get_context(); + if (!pmo_ctx) { + pmo_err("unable to get pmo ctx"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + if (id < 0 || id >= WLAN_UMAC_MAX_COMPONENTS) { + pmo_err("component id: %d is %s then valid components id", + id, id < 0 ? "Less" : "More"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + qdf_spin_lock_bh(&pmo_ctx->lock); + pmo_ctx->pmo_resume_handler[id] = handler; + pmo_ctx->pmo_resume_handler_arg[id] = arg; + qdf_spin_unlock_bh(&pmo_ctx->lock); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_unregister_resume_handler( + enum wlan_umac_comp_id id, + pmo_psoc_resume_handler handler) +{ + struct wlan_pmo_ctx *pmo_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + pmo_enter(); + pmo_ctx = pmo_get_context(); + if (!pmo_ctx) { + pmo_err("unable to get pmo ctx"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + if (id < 0 || id >= WLAN_UMAC_MAX_COMPONENTS) { + pmo_err("component id: %d is %s then valid components id", + id, id < 0 ? "Less" : "More"); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + qdf_spin_lock_bh(&pmo_ctx->lock); + if (pmo_ctx->pmo_resume_handler[id] == handler) { + pmo_ctx->pmo_resume_handler[id] = NULL; + pmo_ctx->pmo_resume_handler_arg[id] = NULL; + qdf_spin_unlock_bh(&pmo_ctx->lock); + } else { + qdf_spin_unlock_bh(&pmo_ctx->lock); + pmo_err("can't find resume handler for component id: %d ", id); + status = QDF_STATUS_E_FAILURE; + } +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_suspend_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type suspend_type) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS resume_status; + struct wlan_pmo_ctx *pmo_ctx; + int i; + pmo_psoc_suspend_handler handler; + void *arg; + + pmo_enter(); + + pmo_ctx = pmo_get_context(); + if (!pmo_ctx) { + pmo_err("unable to get pmo ctx"); + QDF_ASSERT(0); + status = QDF_STATUS_E_FAILURE; + goto exit_with_status; + } + + /* call each component's suspend handler */ + for (i = 0; i < WLAN_UMAC_MAX_COMPONENTS; i++) { + qdf_spin_lock_bh(&pmo_ctx->lock); + handler = pmo_ctx->pmo_suspend_handler[i]; + arg = pmo_ctx->pmo_suspend_handler_arg[i]; + qdf_spin_unlock_bh(&pmo_ctx->lock); + + if (!handler) + continue; + + status = handler(psoc, arg); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_err("component %d failed to suspend; status: %d", + i, status); + QDF_ASSERT(0); + goto suspend_recovery; + } + } + + goto exit_with_status; + +suspend_recovery: + /* resume, starting with the last successfully suspended component */ + for (i -= 1; i >= 0; i--) { + qdf_spin_lock_bh(&pmo_ctx->lock); + handler = pmo_ctx->pmo_resume_handler[i]; + arg = pmo_ctx->pmo_resume_handler_arg[i]; + qdf_spin_unlock_bh(&pmo_ctx->lock); + + if (!handler) + continue; + + resume_status = handler(psoc, arg); + if (QDF_IS_STATUS_ERROR(resume_status)) + QDF_DEBUG_PANIC("component %d failed resume; status:%d", + i, resume_status); + } + +exit_with_status: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_resume_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type suspend_type) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_pmo_ctx *pmo_ctx; + uint8_t i; + pmo_psoc_suspend_handler handler; + void *arg; + + pmo_enter(); + + pmo_ctx = pmo_get_context(); + if (!pmo_ctx) { + pmo_err("unable to get pmo ctx"); + QDF_ASSERT(0); + status = QDF_STATUS_E_FAILURE; + goto exit_with_status; + } + + /* call each component's resume handler */ + for (i = 0; i < WLAN_UMAC_MAX_COMPONENTS; i++) { + qdf_spin_lock_bh(&pmo_ctx->lock); + handler = pmo_ctx->pmo_resume_handler[i]; + arg = pmo_ctx->pmo_resume_handler_arg[i]; + qdf_spin_unlock_bh(&pmo_ctx->lock); + + if (!handler) + continue; + + status = handler(psoc, arg); + if (QDF_IS_STATUS_ERROR(status)) { + pmo_fatal("Non-recoverable failure occurred!"); + pmo_fatal("component %d failed to resume; status: %d", + i, status); + QDF_BUG(0); + } + } + +exit_with_status: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_register_pause_bitmap_notifier(struct wlan_objmgr_psoc *psoc, + pmo_notify_pause_bitmap handler) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!handler) { + pmo_err("pmo_notify_vdev_pause_bitmap is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->pause_bitmap_notifier = handler; + } + + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_unregister_pause_bitmap_notifier(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->pause_bitmap_notifier = NULL; + } + + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_register_get_pause_bitmap(struct wlan_objmgr_psoc *psoc, + pmo_get_pause_bitmap handler) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!handler) { + pmo_err("pmo_get_pause_bitmap is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->get_pause_bitmap = handler; + } + + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_unregister_get_pause_bitmap(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->get_pause_bitmap = NULL; + } + + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_register_is_device_in_low_pwr_mode(struct wlan_objmgr_psoc *psoc, + pmo_is_device_in_low_pwr_mode handler) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!handler) { + pmo_err("pmo_get_pause_bitmap is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->is_device_in_low_pwr_mode = handler; + } + + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +pmo_unregister_is_device_in_low_pwr_mode(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->is_device_in_low_pwr_mode = NULL; + } + + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS pmo_register_get_dtim_period_callback(struct wlan_objmgr_psoc *psoc, + pmo_get_dtim_period handler) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!handler) { + pmo_err("pmo_get_dtim_period is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->get_dtim_period = handler; + } + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +pmo_unregister_get_dtim_period_callback(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->get_dtim_period = NULL; + } + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +pmo_register_get_beacon_interval_callback(struct wlan_objmgr_psoc *psoc, + pmo_get_beacon_interval handler) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!handler) { + pmo_err("pmo_get_beacon_interval is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->get_beacon_interval = handler; + } + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +pmo_unregister_get_beacon_interval_callback(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + QDF_STATUS status; + + if (!psoc) { + pmo_err("psoc is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = pmo_psoc_get_ref(psoc); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("pmo cannot get the reference out of psoc"); + return status; + } + + pmo_psoc_with_ctx(psoc, psoc_ctx) { + psoc_ctx->get_beacon_interval = NULL; + } + pmo_psoc_put_ref(psoc); + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_arp.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_arp.c new file mode 100644 index 0000000000000000000000000000000000000000..13dcca43c67842eaa55222710cb7367802631791 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_arp.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for pmo to interact with target/WMI + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_arp_public_struct.h" +#include "wlan_pmo_ns_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_enable_arp_offload_req(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id) +{ + struct pmo_arp_offload_params *arp_offload_req = NULL; + struct pmo_ns_offload_params *ns_offload_req = NULL; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + psoc = pmo_vdev_get_psoc(vdev); + + arp_offload_req = qdf_mem_malloc(sizeof(*arp_offload_req)); + if (!arp_offload_req) { + pmo_err("unable to allocate arp_offload_req"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + ns_offload_req = qdf_mem_malloc(sizeof(*ns_offload_req)); + if (!ns_offload_req) { + pmo_err("unable to allocate ns_offload_req"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_copy(arp_offload_req, &vdev_ctx->vdev_arp_req, + sizeof(*arp_offload_req)); + qdf_mem_copy(ns_offload_req, &vdev_ctx->vdev_ns_req, + sizeof(*ns_offload_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_debug("vdev_id: %d: ARP offload %d NS offload %d ns_count %u", + vdev_id, arp_offload_req->enable, ns_offload_req->enable, + ns_offload_req->num_ns_offload_count); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_arp_offload_req) { + pmo_err("send_arp_offload_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_arp_offload_req( + vdev, arp_offload_req, ns_offload_req); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to send ARP offload"); + goto out; + } + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + if (vdev_ctx->vdev_arp_req.enable) + vdev_ctx->vdev_arp_req.is_offload_applied = true; + if (vdev_ctx->vdev_ns_req.enable) + vdev_ctx->vdev_ns_req.is_offload_applied = true; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + +out: + if (arp_offload_req) + qdf_mem_free(arp_offload_req); + if (ns_offload_req) + qdf_mem_free(ns_offload_req); + + return status; +} + +QDF_STATUS pmo_tgt_disable_arp_offload_req(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id) +{ + struct pmo_arp_offload_params *arp_offload_req = NULL; + struct pmo_ns_offload_params *ns_offload_req = NULL; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + + vdev_ctx = pmo_vdev_get_priv(vdev); + + psoc = pmo_vdev_get_psoc(vdev); + + arp_offload_req = qdf_mem_malloc(sizeof(*arp_offload_req)); + if (!arp_offload_req) { + pmo_err("unable to allocate arp_offload_req"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + ns_offload_req = qdf_mem_malloc(sizeof(*ns_offload_req)); + if (!ns_offload_req) { + pmo_err("unable to allocate ns_offload_req"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_copy(arp_offload_req, &vdev_ctx->vdev_arp_req, + sizeof(*arp_offload_req)); + qdf_mem_copy(ns_offload_req, &vdev_ctx->vdev_ns_req, + sizeof(*ns_offload_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_debug("ARP Offload vdev_id: %d enable: %d", + vdev_id, + arp_offload_req->enable); + pmo_debug("NS Offload vdev_id: %d enable: %d ns_count: %u", + vdev_id, + ns_offload_req->enable, + ns_offload_req->num_ns_offload_count); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_arp_offload_req) { + pmo_err("send_arp_offload_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_arp_offload_req( + vdev, arp_offload_req, ns_offload_req); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to send ARP offload"); + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->vdev_arp_req.is_offload_applied = false; + vdev_ctx->vdev_ns_req.is_offload_applied = false; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + +out: + if (arp_offload_req) + qdf_mem_free(arp_offload_req); + if (ns_offload_req) + qdf_mem_free(ns_offload_req); + pmo_exit(); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_gtk.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_gtk.c new file mode 100644 index 0000000000000000000000000000000000000000..d88c60b3745e321bef46edeb715f2dc2c97c1da5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_gtk.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for PMO GTK offload feature to interact + * with target/wmi. + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_gtk_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_send_gtk_offload_req(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_req) +{ + struct pmo_gtk_req *op_gtk_req = NULL; + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + pmo_err("Failed to find psoc from from vdev:%pK", + vdev); + status = QDF_STATUS_E_INVAL; + goto out; + } + + vdev_ctx = pmo_vdev_get_priv(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_gtk_offload_req) { + pmo_err("send_gtk_offload_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + op_gtk_req = qdf_mem_malloc(sizeof(*op_gtk_req)); + if (!op_gtk_req) { + pmo_err("cannot allocate op_gtk_req "); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + if (gtk_req->flags == PMO_GTK_OFFLOAD_ENABLE) { + qdf_atomic_set(&vdev_ctx->gtk_err_enable, true); + qdf_mem_copy(op_gtk_req->kck, gtk_req->kck, + gtk_req->kck_len); + qdf_mem_copy(op_gtk_req->kek, gtk_req->kek, + PMO_KEK_LEN); + qdf_mem_copy(&op_gtk_req->replay_counter, + >k_req->replay_counter, PMO_REPLAY_COUNTER_LEN); + } else { + qdf_atomic_set(&vdev_ctx->gtk_err_enable, false); + } + + pmo_debug("replay counter %llu", op_gtk_req->replay_counter); + op_gtk_req->flags = gtk_req->flags; + status = pmo_tx_ops.send_gtk_offload_req(vdev, op_gtk_req); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to send gtk offload req event"); +out: + if (op_gtk_req) + qdf_mem_free(op_gtk_req); + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_get_gtk_rsp(struct wlan_objmgr_vdev *vdev) +{ + + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + pmo_err("Failed to find psoc from from vdev:%pK", + vdev); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_get_gtk_rsp_cmd) { + pmo_err("send_get_gtk_rsp_cmd is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_get_gtk_rsp_cmd(vdev); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to send_get_gtk_rsp_cmd event"); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_gtk_rsp_evt(struct wlan_objmgr_psoc *psoc, + struct pmo_gtk_rsp_params *rsp_param) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + struct pmo_vdev_priv_obj *vdev_ctx; + + pmo_enter(); + if (!rsp_param) { + pmo_err("gtk rsp param is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + vdev = pmo_psoc_get_vdev(psoc, rsp_param->vdev_id); + if (!vdev) { + pmo_err("vdev is null vdev_id:%d psoc:%pK", + rsp_param->vdev_id, psoc); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_vdev_get_ref(vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + status = pmo_get_vdev_bss_peer_mac_addr(vdev, &rsp_param->bssid); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("cannot find peer mac address"); + goto dec_ref; + } + + /* update cached replay counter */ + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->vdev_gtk_req.replay_counter = rsp_param->replay_counter; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + if (vdev_ctx->vdev_gtk_rsp_req.callback) { + pmo_debug("callback:%pK context:%pK psoc:%pK vdev_id:%d", + vdev_ctx->vdev_gtk_rsp_req.callback, + vdev_ctx->vdev_gtk_rsp_req.callback_context, + psoc, rsp_param->vdev_id); + vdev_ctx->vdev_gtk_rsp_req.callback( + vdev_ctx->vdev_gtk_rsp_req.callback_context, + rsp_param); + } else { + pmo_err("gtk rsp callback is null for vdev_id:%d psoc %pK", + rsp_param->vdev_id, + psoc); + } + +dec_ref: + pmo_vdev_put_ref(vdev); +out: + pmo_exit(); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_hw_filter.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_hw_filter.c new file mode 100644 index 0000000000000000000000000000000000000000..396d3074dfd1840e35ce822e25569e5166042daa --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_hw_filter.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for pmo to interact with target/WMI + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_hw_filter_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_conf_hw_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_hw_filter_params *req) +{ + QDF_STATUS status; + struct wlan_pmo_tx_ops ops; + + pmo_enter(); + + pmo_debug("Send %s hw filter mode 0x%X for vdev_id %u", + req->enable ? "Enable" : "Disable", req->mode_bitmap, + req->vdev_id); + + ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!ops.send_conf_hw_filter_req) { + pmo_err("send_conf_hw_filter_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto exit_with_status; + } + + status = ops.send_conf_hw_filter_req(psoc, req); + +exit_with_status: + pmo_exit(); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_lphb.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_lphb.c new file mode 100644 index 0000000000000000000000000000000000000000..f363e5614a4c847f185b2ec14b338951bd9dbdf4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_lphb.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for pmo low power hear beat feature + * to interact with target/WMI. + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_lphb_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_send_lphb_enable(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_enable_req *ts_lphb_enable) +{ + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_lphb_enable) { + pmo_err("send_lphb_enable is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_lphb_enable(psoc, ts_lphb_enable); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to send lphb enable"); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_send_lphb_tcp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_params *ts_lphb_tcp_param) +{ + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_lphb_tcp_params) { + pmo_err("send_lphb_tcp_params is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_lphb_tcp_params(psoc, ts_lphb_tcp_param); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to send lphb tcp params"); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_send_lphb_tcp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_filter_req *ts_lphb_tcp_filter) +{ + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_lphb_tcp_filter_req) { + pmo_err("send_lphb_tcp_filter_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_lphb_tcp_filter_req(psoc, ts_lphb_tcp_filter); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to send lphb tcp filter req"); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_send_lphb_udp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_params *ts_lphb_udp_param) +{ + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_lphb_upd_params) { + pmo_err("send_lphb_upd_params is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_lphb_upd_params(psoc, ts_lphb_udp_param); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to send lphb udp param"); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_send_lphb_udp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_filter_req *ts_lphb_udp_filter) +{ + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_lphb_udp_filter_req) { + pmo_err("send_lphb_udp_filter_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_lphb_udp_filter_req(psoc, ts_lphb_udp_filter); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to send lphb udp filter req"); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_lphb_rsp_evt(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_rsp *rsp_param) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + + psoc_ctx = pmo_psoc_get_priv(psoc); + if (psoc_ctx->wow.lphb_cb && psoc_ctx->wow.lphb_cb_ctx) { + psoc_ctx->wow.lphb_cb(psoc_ctx->wow.lphb_cb_ctx, rsp_param); + } else { + pmo_err("lphb rsp callback/context is null for psoc %pK", + psoc); + } + + return QDF_STATUS_SUCCESS; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_mc_addr_filtering.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_mc_addr_filtering.c new file mode 100644 index 0000000000000000000000000000000000000000..e8118f81ca7061e1f78c72b577455b010ce575ba --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_mc_addr_filtering.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for pmo to interact with target/WMI + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_mc_addr_filtering_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_set_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_set_mc_filter_req) { + pmo_err("send_add_clear_mcbc_filter_request is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_set_mc_filter_req( + vdev, multicast_addr); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to add/clear mc filter"); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_clear_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_clear_mc_filter_req) { + pmo_err("send_add_clear_mcbc_filter_request is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_clear_mc_filter_req( + vdev, multicast_addr); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to add/clear mc filter"); +out: + pmo_exit(); + + return status; +} + +bool pmo_tgt_get_multiple_mc_filter_support(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_pmo_tx_ops pmo_tx_ops; + struct wlan_objmgr_psoc *psoc; + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.get_multiple_mc_filter_support) { + pmo_err("get_multiple_mc_filter_support is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + return pmo_tx_ops.get_multiple_mc_filter_support(psoc); +} + +QDF_STATUS pmo_tgt_set_multiple_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_set_multiple_mc_filter_req) { + pmo_err("send_set_multiple_mc_filter_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_set_multiple_mc_filter_req( + vdev, mc_list); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to add/clear multiple mc filter"); +out: + + return status; +} + +QDF_STATUS pmo_tgt_clear_multiple_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_clear_multiple_mc_filter_req) { + pmo_err("send_clear_multiple_mc_filter_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_clear_multiple_mc_filter_req( + vdev, mc_list); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to add/clear multiple mc filter"); +out: + + return status; +} + + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_ns.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_ns.c new file mode 100644 index 0000000000000000000000000000000000000000..df08c5dca11d99ecf118a89d96f15336ac511ebc --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_ns.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for PMO NS offload feature to interact + * with target/wmi. + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_arp_public_struct.h" +#include "wlan_pmo_ns_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_enable_ns_offload_req(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id) +{ + struct pmo_arp_offload_params *arp_offload_req = NULL; + struct pmo_ns_offload_params *ns_offload_req = NULL; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + vdev_ctx = pmo_vdev_get_priv(vdev); + + psoc = pmo_vdev_get_psoc(vdev); + + arp_offload_req = qdf_mem_malloc(sizeof(*arp_offload_req)); + if (!arp_offload_req) { + pmo_err("unable to allocate arp_offload_req"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + ns_offload_req = qdf_mem_malloc(sizeof(*ns_offload_req)); + if (!ns_offload_req) { + pmo_err("unable to allocate ns_offload_req"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_copy(arp_offload_req, &vdev_ctx->vdev_arp_req, + sizeof(*arp_offload_req)); + qdf_mem_copy(ns_offload_req, &vdev_ctx->vdev_ns_req, + sizeof(*ns_offload_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_debug("vdev_id: %d: ARP offload %d NS offload %d ns_count %u", + vdev_id, + arp_offload_req->enable, ns_offload_req->enable, + ns_offload_req->num_ns_offload_count); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_ns_offload_req) { + pmo_err("send_ns_offload_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_ns_offload_req( + vdev, arp_offload_req, ns_offload_req); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to send NS offload"); + goto out; + } + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + if (vdev_ctx->vdev_arp_req.enable) + vdev_ctx->vdev_arp_req.is_offload_applied = true; + if (vdev_ctx->vdev_ns_req.enable) + vdev_ctx->vdev_ns_req.is_offload_applied = true; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + +out: + if (arp_offload_req) + qdf_mem_free(arp_offload_req); + if (ns_offload_req) + qdf_mem_free(ns_offload_req); + + return status; +} + +QDF_STATUS pmo_tgt_disable_ns_offload_req(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id) +{ + struct pmo_arp_offload_params *arp_offload_req = NULL; + struct pmo_ns_offload_params *ns_offload_req = NULL; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + + vdev_ctx = pmo_vdev_get_priv(vdev); + + psoc = pmo_vdev_get_psoc(vdev); + + arp_offload_req = qdf_mem_malloc(sizeof(*arp_offload_req)); + if (!arp_offload_req) { + pmo_err("unable to allocate arp_offload_req"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + ns_offload_req = qdf_mem_malloc(sizeof(*ns_offload_req)); + if (!ns_offload_req) { + pmo_err("unable to allocate ns_offload_req"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + qdf_mem_copy(arp_offload_req, &vdev_ctx->vdev_arp_req, + sizeof(*arp_offload_req)); + qdf_mem_copy(ns_offload_req, &vdev_ctx->vdev_ns_req, + sizeof(*ns_offload_req)); + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_debug("ARP Offload vdev_id: %d enable: %d", + vdev_id, + arp_offload_req->enable); + pmo_debug("NS Offload vdev_id: %d enable: %d ns_count: %u", + vdev_id, + ns_offload_req->enable, + ns_offload_req->num_ns_offload_count); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_ns_offload_req) { + pmo_err("send_ns_offload_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_ns_offload_req( + vdev, arp_offload_req, ns_offload_req); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to send NS offload"); + + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + vdev_ctx->vdev_arp_req.is_offload_applied = false; + vdev_ctx->vdev_ns_req.is_offload_applied = false; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + +out: + if (arp_offload_req) + qdf_mem_free(arp_offload_req); + if (ns_offload_req) + qdf_mem_free(ns_offload_req); + pmo_exit(); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_pkt_filter.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_pkt_filter.c new file mode 100644 index 0000000000000000000000000000000000000000..5094fd735d5d9168a23cae09a8771bd567acd61a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_pkt_filter.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for pmo to interact with target/WMI + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_pkt_filter_public_struct.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_set_pkt_filter(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req, + uint8_t vdev_id) +{ + QDF_STATUS status; + struct pmo_rcv_pkt_fltr_cfg *request_buf = NULL; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + struct qdf_mac_addr peer_bssid; + + pmo_enter(); + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + pmo_err("psoc unavailable for vdev %pK", vdev); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + pmo_debug("filter_type=%d, filter_id = %d", + pmo_set_pkt_fltr_req->filter_type, + pmo_set_pkt_fltr_req->filter_id); + + request_buf = qdf_mem_malloc(sizeof(*request_buf)); + + if (!request_buf) { + pmo_err("Not able to allocate memory for Receive Filter Set Filter request"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + status = pmo_get_vdev_bss_peer_mac_addr(vdev, + &peer_bssid); + if (status != QDF_STATUS_SUCCESS) { + status = QDF_STATUS_E_INVAL; + goto out; + } + + qdf_mem_copy(request_buf, pmo_set_pkt_fltr_req, sizeof(*request_buf)); + + qdf_mem_copy(&request_buf->self_macaddr.bytes, + wlan_vdev_mlme_get_macaddr(vdev), + QDF_MAC_ADDR_SIZE); + + qdf_copy_macaddr(&pmo_set_pkt_fltr_req->bssid, + &peer_bssid); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_set_pkt_filter) { + pmo_err("send_set_pkt_filter is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_set_pkt_filter(vdev, request_buf); + if (status != QDF_STATUS_SUCCESS) + goto out; + +out: + if (request_buf) + qdf_mem_free(request_buf); + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_clear_pkt_filter(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_clear_param *pmo_clr_pkt_fltr_param, + uint8_t vdev_id) +{ + QDF_STATUS status; + struct pmo_rcv_pkt_fltr_clear_param *request_buf = NULL; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + struct qdf_mac_addr peer_bssid; + + pmo_enter(); + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + pmo_err("psoc unavailable for vdev %pK", vdev); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + pmo_debug("filter_id = %d", pmo_clr_pkt_fltr_param->filter_id); + + request_buf = qdf_mem_malloc(sizeof(*request_buf)); + + if (!request_buf) { + pmo_err("Not able to allocate memory for Receive Filter Set Filter request"); + status = QDF_STATUS_E_NOMEM; + goto out; + } + + status = pmo_get_vdev_bss_peer_mac_addr(vdev, + &peer_bssid); + if (status != QDF_STATUS_SUCCESS) { + status = QDF_STATUS_E_INVAL; + goto out; + } + + qdf_mem_copy(request_buf, pmo_clr_pkt_fltr_param, sizeof(*request_buf)); + + qdf_mem_copy(&request_buf->self_macaddr.bytes, + wlan_vdev_mlme_get_macaddr(vdev), + QDF_MAC_ADDR_SIZE); + + qdf_copy_macaddr(&pmo_clr_pkt_fltr_param->bssid, + &peer_bssid); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_clear_pkt_filter) { + pmo_err("send_clear_pkt_filter is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_clear_pkt_filter(vdev, request_buf); + if (status != QDF_STATUS_SUCCESS) + goto out; + +out: + if (request_buf) + qdf_mem_free(request_buf); + pmo_exit(); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_static_config.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_static_config.c new file mode 100644 index 0000000000000000000000000000000000000000..04e63775e49a2ef99660b46161f2a82e975f2aed --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_static_config.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for pmo to interact with target/WMI + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_wow.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_send_enhance_multicast_offload_req( + struct wlan_objmgr_vdev *vdev, + uint8_t action) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_enhance_mc_offload_req) { + pmo_err("send_enhance_multicast_offload is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_enhance_mc_offload_req(vdev, action); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to config enhance multicast offload"); +out: + + return status; +} + +QDF_STATUS pmo_tgt_send_ra_filter_req(struct wlan_objmgr_vdev *vdev) +{ + + QDF_STATUS status; + uint8_t default_pattern; + uint16_t ra_interval; + struct pmo_vdev_priv_obj *vdev_ctx; + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + + psoc = pmo_vdev_get_psoc(vdev); + + vdev_ctx = pmo_vdev_get_priv(vdev); + + vdev_id = pmo_vdev_get_id(vdev); + qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); + ra_interval = vdev_ctx->pmo_psoc_ctx->psoc_cfg.ra_ratelimit_interval; + qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); + + pmo_debug("send RA rate limit [%d] to fw vdev = %d", + ra_interval, vdev_id); + + default_pattern = pmo_get_and_increment_wow_default_ptrn(vdev_ctx); + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_ra_filter_req) { + pmo_err("send_ra_filter_cmd is null"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + status = pmo_tx_ops.send_ra_filter_req( + vdev, default_pattern, ra_interval); + if (status != QDF_STATUS_SUCCESS) { + pmo_err("Failed to send RA rate limit to fw"); + pmo_decrement_wow_default_ptrn(vdev_ctx); + } +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_send_action_frame_pattern_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_action_wakeup_set_params *cmd) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_action_frame_pattern_req) { + pmo_err("send_add_action_frame_pattern_cmd is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_action_frame_pattern_req(vdev, cmd); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to add filter"); +out: + pmo_exit(); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_suspend_resume.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_suspend_resume.c new file mode 100644 index 0000000000000000000000000000000000000000..1c08a0de9f16d202a47afcf1391d6e32f4713f65 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_suspend_resume.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for pmo to interact with target/WMI + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_wow.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_vdev_update_param_req(struct wlan_objmgr_vdev *vdev, + enum pmo_vdev_param_id param_id, uint32_t param_value) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_vdev_param_update_req) { + pmo_err("send_vdev_param_update_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_vdev_param_update_req(vdev, param_id, + param_value); +out: + pmo_exit(); + + return status; +} + +QDF_STATUS pmo_tgt_send_vdev_sta_ps_param(struct wlan_objmgr_vdev *vdev, + enum pmo_sta_powersave_param ps_param, uint32_t param_value) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_enter(); + + psoc = pmo_vdev_get_psoc(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_vdev_sta_ps_param_req) { + pmo_err("send_vdev_param_set_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + status = pmo_tx_ops.send_vdev_sta_ps_param_req(vdev, ps_param, + param_value); +out: + pmo_exit(); + + return status; +} + +void pmo_tgt_psoc_update_wow_bus_suspend_state(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.psoc_update_wow_bus_suspend) { + pmo_err("psoc_update_wow_bus_suspend is null"); + return; + } + pmo_tx_ops.psoc_update_wow_bus_suspend(psoc, val); +} + +int pmo_tgt_psoc_get_host_credits(struct wlan_objmgr_psoc *psoc) +{ + + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.psoc_get_host_credits) { + pmo_err("psoc_get_host_credits is null"); + return 0; + } + + return pmo_tx_ops.psoc_get_host_credits(psoc); +} + +int pmo_tgt_psoc_get_pending_cmnds(struct wlan_objmgr_psoc *psoc) +{ + + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.psoc_get_pending_cmnds) { + pmo_err("psoc_get_pending_cmnds is null"); + return -EAGAIN; + } + + return pmo_tx_ops.psoc_get_pending_cmnds(psoc); +} + +void pmo_tgt_update_target_suspend_flag(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.update_target_suspend_flag) { + pmo_err("update_target_suspend_flag is null"); + return; + } + pmo_tx_ops.update_target_suspend_flag(psoc, val); +} + +bool pmo_tgt_is_target_suspended(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.is_target_suspended) { + pmo_err("is_target_suspended is null"); + return false; + } + return pmo_tx_ops.is_target_suspended(psoc); +} + +QDF_STATUS pmo_tgt_psoc_send_wow_enable_req(struct wlan_objmgr_psoc *psoc, + struct pmo_wow_cmd_params *param) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + struct wlan_pmo_tx_ops pmo_tx_ops; + + psoc_ctx = pmo_psoc_get_priv(psoc); + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + + if (psoc_ctx->wow.wow_state == pmo_wow_state_legacy_d0) { + if (!pmo_tx_ops.psoc_send_d0wow_enable_req) { + pmo_err("psoc_send_d0wow_enable_req is null"); + return QDF_STATUS_E_NULL_VALUE; + } + pmo_debug("Sending D0WOW enable command..."); + return pmo_tx_ops.psoc_send_d0wow_enable_req(psoc); + } + + if (!pmo_tx_ops.psoc_send_wow_enable_req) { + pmo_err("psoc_send_wow_enable_req is null"); + return QDF_STATUS_E_NULL_VALUE; + } + return pmo_tx_ops.psoc_send_wow_enable_req(psoc, param); +} + +QDF_STATUS pmo_tgt_psoc_send_supend_req(struct wlan_objmgr_psoc *psoc, + struct pmo_suspend_params *param) +{ + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.psoc_send_supend_req) { + pmo_err("psoc_send_supend_req is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + return pmo_tx_ops.psoc_send_supend_req(psoc, param); +} + +QDF_STATUS pmo_tgt_psoc_set_runtime_pm_inprogress(struct wlan_objmgr_psoc *psoc, + bool value) +{ + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.psoc_set_runtime_pm_in_progress) { + pmo_err("pmo ops is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + pmo_tx_ops.psoc_set_runtime_pm_in_progress(psoc, value); + + return QDF_STATUS_SUCCESS; +} + +bool pmo_tgt_psoc_get_runtime_pm_in_progress(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.psoc_get_runtime_pm_in_progress) { + pmo_err("psoc_get_runtime_pm_in_progress is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + return pmo_tx_ops.psoc_get_runtime_pm_in_progress(psoc); +} + +QDF_STATUS pmo_tgt_psoc_send_host_wakeup_ind(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *psoc_ctx; + struct wlan_pmo_tx_ops pmo_tx_ops; + + psoc_ctx = pmo_psoc_get_priv(psoc); + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + + if (psoc_ctx->psoc_cfg.d0_wow_supported && + psoc_ctx->wow.wow_state == pmo_wow_state_legacy_d0) { + if (!pmo_tx_ops.psoc_send_d0wow_disable_req) { + pmo_err("psoc_send_d0wow_disable_req is null"); + return QDF_STATUS_E_NULL_VALUE; + } + pmo_debug("Sending D0WOW disable command..."); + return pmo_tx_ops.psoc_send_d0wow_disable_req(psoc); + } + + if (!pmo_tx_ops.psoc_send_host_wakeup_ind) { + pmo_err("psoc_send_host_wakeup_ind is null"); + return QDF_STATUS_E_NULL_VALUE; + } + return pmo_tx_ops.psoc_send_host_wakeup_ind(psoc); +} + +QDF_STATUS pmo_tgt_psoc_send_target_resume_req(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.psoc_send_target_resume_req) { + pmo_err("send_target_resume_req is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + return pmo_tx_ops.psoc_send_target_resume_req(psoc); +} + +QDF_STATUS pmo_tgt_psoc_send_idle_roam_monitor(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + struct wlan_pmo_tx_ops pmo_tx_ops; + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.psoc_send_idle_roam_suspend_mode) { + pmo_err("NULL fp"); + return QDF_STATUS_E_NULL_VALUE; + } + return pmo_tx_ops.psoc_send_idle_roam_suspend_mode(psoc, val); +} diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_wow.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_wow.c new file mode 100644 index 0000000000000000000000000000000000000000..15996ac45483b0d10fcb6040d79dec0a689f3f3f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_tgt_wow.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements public API for pmo to interact with target/WMI + */ + +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_wow.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" +#include "wlan_pmo_main.h" + +QDF_STATUS pmo_tgt_enable_wow_wakeup_event( + struct wlan_objmgr_vdev *vdev, + uint32_t *bitmap) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + int vdev_id; + + psoc = pmo_vdev_get_psoc(vdev); + vdev_id = pmo_vdev_get_id(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_enable_wow_wakeup_event_req) { + pmo_err("send_enable_wow_wakeup_event_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + pmo_debug("Enable wakeup events 0x%08x%08x%08x%08x for vdev_id %d", + bitmap[3], bitmap[2], bitmap[1], bitmap[0], vdev_id); + + status = pmo_tx_ops.send_enable_wow_wakeup_event_req(vdev, bitmap); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to enable wow wakeup event"); +out: + + return status; +} + +QDF_STATUS pmo_tgt_disable_wow_wakeup_event( + struct wlan_objmgr_vdev *vdev, + uint32_t *bitmap) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + int vdev_id; + + psoc = pmo_vdev_get_psoc(vdev); + vdev_id = pmo_vdev_get_id(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_disable_wow_wakeup_event_req) { + pmo_err("send_disable_wow_wakeup_event_req is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + pmo_debug("Disable wakeup events 0x%x%x%x%x for vdev_id %d", + bitmap[3], bitmap[2], bitmap[1], bitmap[0], vdev_id); + + status = pmo_tx_ops.send_disable_wow_wakeup_event_req(vdev, bitmap); + if (status != QDF_STATUS_SUCCESS) + pmo_err("Failed to disable wow wakeup event"); +out: + + return status; +} + +QDF_STATUS pmo_tgt_send_wow_patterns_to_fw( + struct wlan_objmgr_vdev *vdev, uint8_t ptrn_id, + const uint8_t *ptrn, uint8_t ptrn_len, + uint8_t ptrn_offset, const uint8_t *mask, + uint8_t mask_len, bool user) +{ + QDF_STATUS status; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + psoc = pmo_vdev_get_psoc(vdev); + + vdev_ctx = pmo_vdev_get_priv(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.send_add_wow_pattern) { + pmo_err("send_add_wow_pattern is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.send_add_wow_pattern( + vdev, ptrn_id, ptrn, + ptrn_len, ptrn_offset, mask, + mask_len, user); + if (status != QDF_STATUS_SUCCESS) { + if (!user) + pmo_decrement_wow_default_ptrn(vdev_ctx); + pmo_err("Failed to send wow pattern event"); + goto out; + } + + if (user) + pmo_increment_wow_user_ptrn(vdev_ctx); +out: + + return status; +} + +QDF_STATUS pmo_tgt_del_wow_pattern( + struct wlan_objmgr_vdev *vdev, uint8_t ptrn_id, + bool user) +{ + QDF_STATUS status; + struct pmo_vdev_priv_obj *vdev_ctx; + struct wlan_objmgr_psoc *psoc; + struct wlan_pmo_tx_ops pmo_tx_ops; + + psoc = pmo_vdev_get_psoc(vdev); + vdev_ctx = pmo_vdev_get_priv(vdev); + + pmo_tx_ops = GET_PMO_TX_OPS_FROM_PSOC(psoc); + if (!pmo_tx_ops.del_wow_pattern) { + pmo_err("del_wow_pattern is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + status = pmo_tx_ops.del_wow_pattern(vdev, ptrn_id); + if (status) { + status = QDF_STATUS_E_FAILURE; + goto out; + } + + if (user) + pmo_decrement_wow_user_ptrn(vdev_ctx); +out: + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_ucfg_api.c b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..641052d94949cb488f55ade98a775824d582e9b9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/pmo/dispatcher/src/wlan_pmo_ucfg_api.c @@ -0,0 +1,872 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: public API related to the pmo called by north bound HDD/OSIF + */ + +#include "wlan_pmo_ucfg_api.h" +#include "wlan_pmo_apf.h" +#include "wlan_pmo_arp.h" +#include "wlan_pmo_ns.h" +#include "wlan_pmo_gtk.h" +#include "wlan_pmo_wow.h" +#include "wlan_pmo_mc_addr_filtering.h" +#include "wlan_pmo_main.h" +#include "wlan_pmo_lphb.h" +#include "wlan_pmo_suspend_resume.h" +#include "wlan_pmo_pkt_filter.h" +#include "wlan_pmo_hw_filter.h" +#include "wlan_pmo_cfg.h" +#include "wlan_pmo_static_config.h" +#include "cfg_ucfg_api.h" + +QDF_STATUS ucfg_pmo_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + return pmo_psoc_open(psoc); +} + +QDF_STATUS ucfg_pmo_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + return pmo_psoc_close(psoc); +} + +uint32_t ucfg_pmo_get_apf_instruction_size(struct wlan_objmgr_psoc *psoc) +{ + QDF_BUG(psoc); + if (!psoc) + return 0; + + return pmo_get_apf_instruction_size(psoc); +} + +uint8_t ucfg_pmo_get_num_wow_filters(struct wlan_objmgr_psoc *psoc) +{ + QDF_BUG(psoc); + if (!psoc) + return 0; + + return pmo_get_num_wow_filters(psoc); +} + +QDF_STATUS ucfg_pmo_get_psoc_config(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + return pmo_core_get_psoc_config(psoc, psoc_cfg); +} + +QDF_STATUS ucfg_pmo_update_psoc_config(struct wlan_objmgr_psoc *psoc, + struct pmo_psoc_cfg *psoc_cfg) +{ + return pmo_core_update_psoc_config(psoc, psoc_cfg); +} + +QDF_STATUS ucfg_pmo_psoc_set_caps(struct wlan_objmgr_psoc *psoc, + struct pmo_device_caps *caps) +{ + QDF_BUG(psoc); + if (!psoc) + return QDF_STATUS_E_INVAL; + + QDF_BUG(caps); + if (!caps) + return QDF_STATUS_E_INVAL; + + pmo_psoc_set_caps(psoc, caps); + + return QDF_STATUS_SUCCESS; +} + +bool ucfg_pmo_is_ap_mode_supports_arp_ns(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE vdev_opmode) +{ + return pmo_core_is_ap_mode_supports_arp_ns(psoc, vdev_opmode); +} + +bool ucfg_pmo_is_vdev_connected(struct wlan_objmgr_vdev *vdev) +{ + if (wlan_vdev_is_up(vdev) == QDF_STATUS_SUCCESS) + return true; + else + return false; +} + +bool ucfg_pmo_is_vdev_supports_offload(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_is_vdev_supports_offload(vdev); +} + +void ucfg_pmo_enable_wakeup_event(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + WOW_WAKE_EVENT_TYPE wow_event) +{ + pmo_core_enable_wakeup_event(psoc, vdev_id, wow_event); +} + +void ucfg_pmo_disable_wakeup_event(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + WOW_WAKE_EVENT_TYPE wow_event) +{ + pmo_core_disable_wakeup_event(psoc, vdev_id, wow_event); +} + +QDF_STATUS ucfg_pmo_cache_arp_offload_req(struct pmo_arp_req *arp_req) +{ + return pmo_core_cache_arp_offload_req(arp_req); +} + +QDF_STATUS ucfg_pmo_check_arp_offload(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id) +{ + return pmo_core_arp_check_offload(psoc, trigger, vdev_id); +} + +QDF_STATUS ucfg_pmo_flush_arp_offload_req(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_flush_arp_offload_req(vdev); +} + +QDF_STATUS ucfg_pmo_enable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + return pmo_core_enable_arp_offload_in_fwr(vdev, trigger); +} + +QDF_STATUS +ucfg_pmo_disable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + return pmo_core_disable_arp_offload_in_fwr(vdev, trigger); +} + +QDF_STATUS +ucfg_pmo_get_arp_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *params) +{ + return pmo_core_get_arp_offload_params(vdev, params); +} + +#ifdef WLAN_NS_OFFLOAD +QDF_STATUS ucfg_pmo_cache_ns_offload_req(struct pmo_ns_req *ns_req) +{ + return pmo_core_cache_ns_offload_req(ns_req); +} + +QDF_STATUS ucfg_pmo_ns_offload_check(struct wlan_objmgr_psoc *psoc, + enum pmo_offload_trigger trigger, + uint8_t vdev_id) +{ + return pmo_core_ns_check_offload(psoc, trigger, vdev_id); +} + +QDF_STATUS ucfg_pmo_flush_ns_offload_req(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_flush_ns_offload_req(vdev); +} + +QDF_STATUS +ucfg_pmo_enable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + return pmo_core_enable_ns_offload_in_fwr(vdev, trigger); +} + +QDF_STATUS +ucfg_pmo_disable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev, + enum pmo_offload_trigger trigger) +{ + return pmo_core_disable_ns_offload_in_fwr(vdev, trigger); +} +#endif /* WLAN_NS_OFFLOAD */ + +QDF_STATUS +ucfg_pmo_get_ns_offload_params(struct wlan_objmgr_vdev *vdev, + struct pmo_ns_offload_params *params) +{ + return pmo_core_get_ns_offload_params(vdev, params); +} + +enum pmo_ns_addr_scope +ucfg_pmo_ns_addr_scope(uint32_t ipv6_scope) +{ + switch (ipv6_scope) { + case IPV6_ADDR_SCOPE_NODELOCAL: + return PMO_NS_ADDR_SCOPE_NODELOCAL; + case IPV6_ADDR_SCOPE_LINKLOCAL: + return PMO_NS_ADDR_SCOPE_LINKLOCAL; + case IPV6_ADDR_SCOPE_SITELOCAL: + return PMO_NS_ADDR_SCOPE_SITELOCAL; + case IPV6_ADDR_SCOPE_ORGLOCAL: + return PMO_NS_ADDR_SCOPE_ORGLOCAL; + case IPV6_ADDR_SCOPE_GLOBAL: + return PMO_NS_ADDR_SCOPE_GLOBAL; + } + + return PMO_NS_ADDR_SCOPE_INVALID; +} + +QDF_STATUS ucfg_pmo_cache_mc_addr_list( + struct pmo_mc_addr_list_params *mc_list_config) +{ + return pmo_core_cache_mc_addr_list(mc_list_config); +} + +QDF_STATUS ucfg_pmo_flush_mc_addr_list(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + return pmo_core_flush_mc_addr_list(psoc, vdev_id); +} + +QDF_STATUS ucfg_pmo_enable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger) +{ + return pmo_core_enable_mc_addr_filtering_in_fwr(psoc, + vdev_id, trigger); +} + +QDF_STATUS ucfg_pmo_disable_mc_addr_filtering_in_fwr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum pmo_offload_trigger trigger) +{ + return pmo_core_disable_mc_addr_filtering_in_fwr(psoc, + vdev_id, trigger); +} + +uint8_t ucfg_pmo_max_mc_addr_supported(struct wlan_objmgr_psoc *psoc) +{ + return pmo_core_max_mc_addr_supported(psoc); +} + +QDF_STATUS +ucfg_pmo_get_mc_addr_list(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + struct pmo_mc_addr_list *mc_list_req) +{ + return pmo_core_get_mc_addr_list(psoc, vdev_id, mc_list_req); +} + +QDF_STATUS +ucfg_pmo_cache_gtk_offload_req(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_req) +{ + return pmo_core_cache_gtk_offload_req(vdev, gtk_req); +} + +QDF_STATUS ucfg_pmo_flush_gtk_offload_req(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_flush_gtk_offload_req(vdev); +} + +QDF_STATUS ucfg_pmo_enable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_enable_gtk_offload_in_fwr(vdev); +} + +QDF_STATUS ucfg_pmo_disable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_disable_gtk_offload_in_fwr(vdev); +} + +#ifdef WLAN_FEATURE_PACKET_FILTERING +uint8_t ucfg_pmo_get_pkt_filter_bitmap(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.packet_filters_bitmap; +} + +uint32_t ucfg_pmo_get_num_packet_filters(struct wlan_objmgr_psoc *psoc) +{ + QDF_BUG(psoc); + if (!psoc) + return 0; + + return pmo_get_num_packet_filters(psoc); +} + +QDF_STATUS +ucfg_pmo_set_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req, + uint8_t vdev_id) +{ + return pmo_core_set_pkt_filter(psoc, pmo_set_pkt_fltr_req, vdev_id); +} + +QDF_STATUS ucfg_pmo_clear_pkt_filter( + struct wlan_objmgr_psoc *psoc, + struct pmo_rcv_pkt_fltr_clear_param *pmo_clr_pkt_fltr_param, + uint8_t vdev_id) +{ + return pmo_core_clear_pkt_filter(psoc, + pmo_clr_pkt_fltr_param, vdev_id); +} +#endif + +QDF_STATUS ucfg_pmo_get_gtk_rsp(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_rsp_req *gtk_rsp_req) +{ + return pmo_core_get_gtk_rsp(vdev, gtk_rsp_req); +} + +void ucfg_pmo_update_extscan_in_progress(struct wlan_objmgr_vdev *vdev, + bool value) +{ + pmo_core_update_extscan_in_progress(vdev, value); +} + +void ucfg_pmo_update_p2plo_in_progress(struct wlan_objmgr_vdev *vdev, + bool value) +{ + pmo_core_update_p2plo_in_progress(vdev, value); +} + +QDF_STATUS ucfg_pmo_lphb_config_req(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_req *lphb_req, + void *lphb_cb_ctx, + pmo_lphb_callback callback) +{ + return pmo_core_lphb_config_req(psoc, lphb_req, lphb_cb_ctx, callback); +} + +void ucfg_pmo_psoc_update_power_save_mode(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + pmo_core_psoc_update_power_save_mode(psoc, value); +} + +void ucfg_pmo_psoc_update_dp_handle(struct wlan_objmgr_psoc *psoc, + void *dp_handle) +{ + pmo_core_psoc_update_dp_handle(psoc, dp_handle); +} + +void ucfg_pmo_psoc_update_htc_handle(struct wlan_objmgr_psoc *psoc, + void *htc_handle) +{ + pmo_core_psoc_update_htc_handle(psoc, htc_handle); +} + +void ucfg_pmo_psoc_set_hif_handle(struct wlan_objmgr_psoc *psoc, + void *hif_handle) +{ + pmo_core_psoc_set_hif_handle(psoc, hif_handle); +} + +void ucfg_pmo_psoc_set_txrx_pdev_id(struct wlan_objmgr_psoc *psoc, + uint8_t txrx_pdev_id) +{ + pmo_core_psoc_set_txrx_pdev_id(psoc, txrx_pdev_id); +} + +void ucfg_pmo_psoc_handle_initial_wake_up(void *cb_ctx) +{ + return pmo_core_psoc_handle_initial_wake_up(cb_ctx); +} + +QDF_STATUS +ucfg_pmo_psoc_user_space_suspend_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return pmo_core_psoc_user_space_suspend_req(psoc, type); +} + +QDF_STATUS +ucfg_pmo_psoc_user_space_resume_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return pmo_core_psoc_user_space_resume_req(psoc, type); +} + +QDF_STATUS ucfg_pmo_suspend_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return pmo_suspend_all_components(psoc, type); +} + +QDF_STATUS ucfg_pmo_resume_all_components(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return pmo_resume_all_components(psoc, type); +} + +QDF_STATUS +ucfg_pmo_psoc_bus_suspend_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type, + struct pmo_wow_enable_params *wow_params) +{ + return pmo_core_psoc_bus_suspend_req(psoc, type, wow_params); +} + +#ifdef FEATURE_RUNTIME_PM +QDF_STATUS ucfg_pmo_psoc_bus_runtime_suspend(struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_suspend_cb pld_cb) +{ + return pmo_core_psoc_bus_runtime_suspend(psoc, pld_cb); +} + +QDF_STATUS ucfg_pmo_psoc_bus_runtime_resume(struct wlan_objmgr_psoc *psoc, + pmo_pld_auto_suspend_cb pld_cb) +{ + return pmo_core_psoc_bus_runtime_resume(psoc, pld_cb); +} +#endif + +QDF_STATUS +ucfg_pmo_psoc_suspend_target(struct wlan_objmgr_psoc *psoc, + int disable_target_intr) +{ + return pmo_core_psoc_suspend_target(psoc, disable_target_intr); +} + +QDF_STATUS +ucfg_pmo_add_wow_user_pattern(struct wlan_objmgr_vdev *vdev, + struct pmo_wow_add_pattern *ptrn) +{ + return pmo_core_add_wow_user_pattern(vdev, ptrn); +} + +QDF_STATUS +ucfg_pmo_del_wow_pattern(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_del_wow_pattern(vdev); +} + +QDF_STATUS +ucfg_pmo_del_wow_user_pattern(struct wlan_objmgr_vdev *vdev, + uint8_t pattern_id) +{ + return pmo_core_del_wow_user_pattern(vdev, pattern_id); +} + +QDF_STATUS +ucfg_pmo_psoc_bus_resume_req(struct wlan_objmgr_psoc *psoc, + enum qdf_suspend_type type) +{ + return pmo_core_psoc_bus_resume_req(psoc, type); +} + +bool ucfg_pmo_get_wow_bus_suspend(struct wlan_objmgr_psoc *psoc) +{ + return pmo_core_get_wow_bus_suspend(psoc); +} + +int ucfg_pmo_psoc_is_target_wake_up_received(struct wlan_objmgr_psoc *psoc) +{ + return pmo_core_psoc_is_target_wake_up_received(psoc); +} + +int ucfg_pmo_psoc_clear_target_wake_up(struct wlan_objmgr_psoc *psoc) +{ + return pmo_core_psoc_clear_target_wake_up(psoc); +} + +void ucfg_pmo_psoc_target_suspend_acknowledge(void *context, bool wow_nack) +{ + pmo_core_psoc_target_suspend_acknowledge(context, wow_nack); +} + +void ucfg_pmo_psoc_wakeup_host_event_received(struct wlan_objmgr_psoc *psoc) +{ + pmo_core_psoc_wakeup_host_event_received(psoc); +} + +QDF_STATUS ucfg_pmo_enable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_enable_hw_filter_in_fwr(vdev); +} + +QDF_STATUS +ucfg_pmo_enable_action_frame_patterns(struct wlan_objmgr_vdev *vdev, + enum qdf_suspend_type suspend_type) +{ + return pmo_register_action_frame_patterns(vdev, suspend_type); +} + +QDF_STATUS ucfg_pmo_disable_action_frame_patterns(struct wlan_objmgr_vdev *vdev) +{ + return pmo_clear_action_frame_patterns(vdev); +} + +QDF_STATUS ucfg_pmo_disable_hw_filter_in_fwr(struct wlan_objmgr_vdev *vdev) +{ + return pmo_core_disable_hw_filter_in_fwr(vdev); +} + +QDF_STATUS ucfg_pmo_config_listen_interval(struct wlan_objmgr_vdev *vdev, + uint32_t listen_interval) +{ + return pmo_core_config_listen_interval(vdev, listen_interval); +} + +QDF_STATUS ucfg_pmo_config_modulated_dtim(struct wlan_objmgr_vdev *vdev, + uint32_t mod_dtim) +{ + return pmo_core_config_modulated_dtim(vdev, mod_dtim); +} + +enum pmo_wow_enable_type +ucfg_pmo_get_wow_enable(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.wow_enable; +} + +void +ucfg_pmo_set_wow_enable(struct wlan_objmgr_psoc *psoc, + enum pmo_wow_enable_type val) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + pmo_psoc_ctx->psoc_cfg.wow_enable = val; +} + +bool +ucfg_pmo_is_wowlan_deauth_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.wowlan_deauth_enable; +} + +bool +ucfg_pmo_is_wowlan_disassoc_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.wowlan_disassoc_enable; +} + +bool +ucfg_pmo_is_arp_offload_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.arp_offload_enable; +} + +void +ucfg_pmo_set_arp_offload_enabled(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + pmo_psoc_ctx->psoc_cfg.arp_offload_enable = val; +} + +enum pmo_auto_pwr_detect_failure_mode +ucfg_pmo_get_auto_power_fail_mode(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.auto_power_save_fail_mode; +} + +#ifdef WLAN_FEATURE_WOW_PULSE +bool ucfg_pmo_is_wow_pulse_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.is_wow_pulse_supported; +} + +uint8_t ucfg_pmo_get_wow_pulse_pin(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.wow_pulse_pin; +} + +uint16_t ucfg_pmo_get_wow_pulse_interval_high(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.wow_pulse_interval_high; +} + +uint16_t ucfg_pmo_get_wow_pulse_interval_low(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.wow_pulse_interval_low; +} +#endif + +bool ucfg_pmo_is_active_mode_offloaded(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.active_mode_offload; +} + +#ifdef FEATURE_WLAN_APF +bool ucfg_pmo_is_apf_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_intersect_apf(pmo_psoc_ctx); +} +#endif + +bool +ucfg_pmo_is_ssdp_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.ssdp; +} + +#ifdef FEATURE_RUNTIME_PM +uint32_t +ucfg_pmo_get_runtime_pm_delay(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.runtime_pm_delay; +} +#endif /* FEATURE_RUNTIME_PM */ + +bool +ucfg_pmo_is_ns_offloaded(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.ns_offload_enable_static; +} + +uint8_t +ucfg_pmo_get_sta_dynamic_dtim(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.sta_dynamic_dtim; +} + +uint8_t +ucfg_pmo_get_sta_mod_dtim(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.sta_mod_dtim; +} + +void +ucfg_pmo_set_sta_mod_dtim(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + pmo_psoc_ctx->psoc_cfg.sta_mod_dtim = val; +} + +bool +ucfg_pmo_is_mc_addr_list_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.enable_mc_list; +} + +enum powersave_mode +ucfg_pmo_get_power_save_mode(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.power_save_mode; +} + +void +ucfg_pmo_set_power_save_mode(struct wlan_objmgr_psoc *psoc, + enum powersave_mode val) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + pmo_psoc_ctx->psoc_cfg.power_save_mode = val; +} + +uint8_t +ucfg_pmo_get_max_ps_poll(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.max_ps_poll; +} + +uint8_t +ucfg_pmo_power_save_offload_enabled(struct wlan_objmgr_psoc *psoc) +{ + uint8_t powersave_offload_enabled = PMO_PS_ADVANCED_POWER_SAVE_ENABLE; + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + if (!pmo_psoc_ctx->psoc_cfg.max_ps_poll || + !pmo_psoc_ctx->psoc_cfg.power_save_mode) + powersave_offload_enabled = + pmo_psoc_ctx->psoc_cfg.power_save_mode; + + pmo_debug("powersave offload enabled type:%d", + powersave_offload_enabled); + + return powersave_offload_enabled; +} + +QDF_STATUS +ucfg_pmo_tgt_psoc_send_idle_roam_suspend_mode(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + return pmo_tgt_psoc_send_idle_roam_monitor(psoc, val); +} + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +bool +ucfg_pmo_extwow_is_goto_suspend_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_goto_suspend; +} + +uint8_t +ucfg_pmo_extwow_app1_wakeup_pin_num(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app1_wakeup_pin_num; +} + +uint8_t +ucfg_pmo_extwow_app2_wakeup_pin_num(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app2_wakeup_pin_num; +} + +uint32_t +ucfg_pmo_extwow_app2_init_ping_interval(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app2_init_ping_interval; +} + +uint32_t +ucfg_pmo_extwow_app2_min_ping_interval(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app2_min_ping_interval; +} + +uint32_t +ucfg_pmo_extwow_app2_max_ping_interval(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app2_max_ping_interval; +} + +uint32_t +ucfg_pmo_extwow_app2_inc_ping_interval(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app2_inc_ping_interval; +} + +uint16_t +ucfg_pmo_extwow_app2_tcp_src_port(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app2_tcp_src_port; +} + +uint16_t +ucfg_pmo_extwow_app2_tcp_dst_port(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app2_tcp_dst_port; +} + +uint32_t +ucfg_pmo_extwow_app2_tcp_tx_timeout(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app2_tcp_tx_timeout; +} + +uint32_t +ucfg_pmo_extwow_app2_tcp_rx_timeout(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.extwow_app2_tcp_rx_timeout; +} +#endif + +bool +ucfg_pmo_get_enable_sap_suspend(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.enable_sap_suspend; +} + +void +ucfg_pmo_set_wow_data_inactivity_timeout(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + pmo_psoc_ctx->psoc_cfg.wow_data_inactivity_timeout = val; +} + +bool ucfg_pmo_is_pkt_filter_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.packet_filter_enabled; +} + +enum active_apf_mode +ucfg_pmo_get_active_uc_apf_mode(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.active_uc_apf_mode; +} + +enum active_apf_mode +ucfg_pmo_get_active_mc_bc_apf_mode(struct wlan_objmgr_psoc *psoc) +{ + struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc); + + return pmo_psoc_ctx->psoc_cfg.active_mc_bc_apf_mode; +} + +#ifdef SYSTEM_PM_CHECK +void ucfg_pmo_notify_system_resume(struct wlan_objmgr_psoc *psoc) +{ + pmo_core_system_resume(psoc); +} +#endif diff --git a/drivers/staging/qcacld-3.0/components/target_if/action_oui/inc/target_if_action_oui.h b/drivers/staging/qcacld-3.0/components/target_if/action_oui/inc/target_if_action_oui.h new file mode 100644 index 0000000000000000000000000000000000000000..4bd65b7d0672da92152bb288b21311681c0054ff --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/action_oui/inc/target_if_action_oui.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare various api/struct which shall be used + * by action_oui component for wmi command path. + */ + +#ifndef _TARGET_IF_ACTION_OUI_H_ +#define _TARGET_IF_ACTION_OUI_H_ + +#include "target_if.h" +#include +#include +#include +#include "wlan_action_oui_tgt_api.h" +#include "wlan_action_oui_public_struct.h" + +/** + * target_if_action_oui_register_tx_ops() - Register action_oui component TX OPS + * @tx_ops: action_oui transmit ops + * + * Return: None + */ +void +target_if_action_oui_register_tx_ops(struct action_oui_tx_ops *tx_ops); + +#endif /* _TARGET_IF_ACTION_OUI_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/target_if/action_oui/src/target_if_action_oui.c b/drivers/staging/qcacld-3.0/components/target_if/action_oui/src/target_if_action_oui.c new file mode 100644 index 0000000000000000000000000000000000000000..4893b633e16cfbd48c28a94aaca636b38c44beab --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/action_oui/src/target_if_action_oui.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Target interface file for action_oui component to + * Implement api's which shall be used by action_oui component + * in target_if internally. + */ + +#include "target_if_action_oui.h" +#include "wlan_action_oui_tgt_api.h" +#include "wlan_action_oui_public_struct.h" + +static QDF_STATUS +target_if_action_oui_send_req(struct wlan_objmgr_psoc *psoc, + struct action_oui_request *req) +{ + void *wmi_hdl; + + wmi_hdl = GET_WMI_HDL_FROM_PSOC(psoc); + if (!wmi_hdl) + return QDF_STATUS_E_FAILURE; + + return wmi_unified_send_action_oui_cmd(wmi_hdl, req); +} + +void +target_if_action_oui_register_tx_ops(struct action_oui_tx_ops *tx_ops) +{ + if (!tx_ops) { + target_if_err("action_oui tx_ops is null"); + return; + } + + tx_ops->send_req = target_if_action_oui_send_req; +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/blacklist_mgr/inc/target_if_blm.h b/drivers/staging/qcacld-3.0/components/target_if/blacklist_mgr/inc/target_if_blm.h new file mode 100644 index 0000000000000000000000000000000000000000..5125896b4e73dc4298c76a8c784b28f00d40b08d --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/blacklist_mgr/inc/target_if_blm.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Target interface file for blacklist manager component to + * declare api's which shall be used by blacklist manager component + * in target if internally. + */ + +#ifndef __TARGET_IF_BLM_H +#define __TARGET_IF_BLM_H + +#include "wlan_blm_public_struct.h" + +/** + * target_if_blm_send_reject_ap_list() - API to send reject ap list to FW + * @pdev: pdev object + * @reject_params: This contains the reject ap list, and the num of BSSIDs + * + * This API will send the reject ap list to the target for it to handle roaming + * case scenarios. + * + * Return: Qdf status + */ +QDF_STATUS +target_if_blm_send_reject_ap_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_params *reject_params); + +/** + * target_if_blm_register_tx_ops() - Register blm tx ops + * @blm_tx_ops: BLM tx ops + * + * This API will register the tx ops used by the BLM to send commands to the + * target. + * + * Return: void + */ +void target_if_blm_register_tx_ops(struct wlan_blm_tx_ops *blm_tx_ops); + +#endif diff --git a/drivers/staging/qcacld-3.0/components/target_if/blacklist_mgr/src/target_if_blm.c b/drivers/staging/qcacld-3.0/components/target_if/blacklist_mgr/src/target_if_blm.c new file mode 100644 index 0000000000000000000000000000000000000000..d7be87c17a35e0efbc9c08f0fc3b11693554413c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/blacklist_mgr/src/target_if_blm.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Target interface file for blacklist manager component to + * Implement api's which shall be used by blacklist manager component + * in target if internally. + */ + +#include +#include "target_if.h" + +QDF_STATUS +target_if_blm_send_reject_ap_list(struct wlan_objmgr_pdev *pdev, + struct reject_ap_params *reject_params) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_pdev(pdev); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_send_reject_ap_list(wmi_handle, reject_params); +} + +void target_if_blm_register_tx_ops(struct wlan_blm_tx_ops *blm_tx_ops) +{ + if (!blm_tx_ops) { + target_if_err("blm_tx_ops is null"); + return; + } + + blm_tx_ops->blm_send_reject_ap_list = target_if_blm_send_reject_ap_list; +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/disa/inc/target_if_disa.h b/drivers/staging/qcacld-3.0/components/target_if/disa/inc/target_if_disa.h new file mode 100644 index 0000000000000000000000000000000000000000..c501b6cb6e7a9ac85fab7d94caab139f7f2b5d8a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/disa/inc/target_if_disa.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various api/struct which shall be used + * by disa component for wmi cmd (tx path) and + * event (rx) handling. + */ + +#ifndef _TARGET_IF_DISA_H_ +#define _TARGET_IF_DISA_H_ + +#include +#include +#include "wlan_disa_obj_mgmt_public_struct.h" + +/** + * target_if_disa_encrypt_decrypt_req() - Send encrypt/decrypt request to + * target. + * @psoc: objmgr psoc handle + * @req: Encrypt/decrypt request params + * + * Return: QDF status + */ +QDF_STATUS target_if_disa_encrypt_decrypt_req(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_req_params *req); + +/** + * target_if_encrypt_decrypt_event_handler() - Collect encrypt/decrypt request + * event from the target and pass on the data to tgt api of DISA. + * @scn_handle: target handle + * @data: event data + * @data_len: data length + * + * Return: QDF status + */ +int target_if_encrypt_decrypt_event_handler(ol_scn_t scn_handle, uint8_t *data, + uint32_t data_len); + +/** + * target_if_disa_register_tx_ops() - Register DISA component TX OPS + * @tx_ops: DISA if transmit ops + * + * Return: None + */ +void target_if_disa_register_tx_ops(struct wlan_disa_tx_ops *tx_ops); + +/** + * target_if_disa_register_ev_handler() - Register disa event handlers. + * @psoc:objmgr psoc handle + * + * Return: QDF status + */ +QDF_STATUS +target_if_disa_register_ev_handlers(struct wlan_objmgr_psoc *psoc); + +/** + * target_if_disa_register_ev_handler() - Unregister disa event handlers. + * @psoc:objmgr psoc handle + * + * Return: QDF status + */ +QDF_STATUS +target_if_disa_unregister_ev_handlers(struct wlan_objmgr_psoc *psoc); +#endif + diff --git a/drivers/staging/qcacld-3.0/components/target_if/disa/src/target_if_disa.c b/drivers/staging/qcacld-3.0/components/target_if/disa/src/target_if_disa.c new file mode 100644 index 0000000000000000000000000000000000000000..5e37fac75fa771f49a71fa37e9e454bf77eb0ebe --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/disa/src/target_if_disa.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Target interface file for disa component to + * Implement api's which shall be used by disa component + * in target if internally. + */ + +#include "target_if.h" +#include "target_if_disa.h" +#include "wlan_disa_tgt_api.h" +#include "wlan_disa_public_struct.h" +#include + +int +target_if_encrypt_decrypt_event_handler(ol_scn_t scn_handle, uint8_t *data, + uint32_t data_len) +{ + struct disa_encrypt_decrypt_resp_params resp; + struct wlan_objmgr_psoc *psoc; + wmi_unified_t wmi_handle; + + if (!data) { + target_if_err("%s: invalid pointer", __func__); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn_handle); + if (!psoc) { + target_if_err("psoc ptr is NULL"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return -EINVAL; + } + + if (wmi_extract_encrypt_decrypt_resp_params(wmi_handle, data, &resp) != + QDF_STATUS_SUCCESS) { + target_if_err("Extraction of encrypt decrypt resp params failed"); + return -EINVAL; + } + + tgt_disa_encrypt_decrypt_resp(psoc, &resp); + + return 0; +} + +QDF_STATUS +target_if_disa_register_ev_handlers(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_register_event(wmi_handle, + wmi_vdev_encrypt_decrypt_data_rsp_event_id, + target_if_encrypt_decrypt_event_handler); + if (status) { + target_if_err("Failed to register Scan match event cb"); + return QDF_STATUS_E_FAILURE; + } + + return status; +} + +QDF_STATUS +target_if_disa_unregister_ev_handlers(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_unregister_event(wmi_handle, + wmi_vdev_encrypt_decrypt_data_rsp_event_id); + if (status) { + target_if_err("Failed to unregister Scan match event cb"); + return QDF_STATUS_E_FAILURE; + } + + return status; +} + +QDF_STATUS +target_if_disa_encrypt_decrypt_req(struct wlan_objmgr_psoc *psoc, + struct disa_encrypt_decrypt_req_params *req) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_encrypt_decrypt_send_cmd(wmi_handle, req); +} + + +void target_if_disa_register_tx_ops(struct wlan_disa_tx_ops *disa_tx_ops) +{ + if (!disa_tx_ops) { + target_if_err("disa_tx_ops is null"); + return; + } + + disa_tx_ops->disa_encrypt_decrypt_req = + target_if_disa_encrypt_decrypt_req; + disa_tx_ops->disa_register_ev_handlers = + target_if_disa_register_ev_handlers; + disa_tx_ops->disa_unregister_ev_handlers = + target_if_disa_unregister_ev_handlers; +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/ftm_time_sync/inc/target_if_ftm_time_sync.h b/drivers/staging/qcacld-3.0/components/target_if/ftm_time_sync/inc/target_if_ftm_time_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..566577e4af26659ee2f58986c20f25542e6b88a1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/ftm_time_sync/inc/target_if_ftm_time_sync.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare various api/struct which shall be used + * by FTM time sync component for wmi cmd (tx path) and + * event (rx) handling. + */ + +#ifndef _TARGET_IF_FTM_TIME_SYNC_H_ +#define _TARGET_IF_FTM_TIME_SYNC_H_ + +#include +#include +#include "wlan_ftm_time_sync_public_struct.h" + +/** + * target_if_ftm_time_sync_register_rx_ops() - Register FTM TIME SYNC component + * RX ops + * @rx_ops: FTM time sync component reception ops + * + * Return: None + */ +void target_if_ftm_time_sync_register_rx_ops(struct wlan_ftm_timesync_rx_ops + *rx_ops); + +/** + * target_if_ftm_time_sync_register_tx_ops() - Register FTM TIME SYNC component + * TX OPS + * @tx_ops: FTM time sync component transmit ops + * + * Return: None + */ +void target_if_ftm_time_sync_register_tx_ops(struct wlan_ftm_timesync_tx_ops + *tx_ops); +#endif /*_TARGET_IF_FTM_TIME_SYNC_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/target_if/ftm_time_sync/src/target_if_ftm_time_sync.c b/drivers/staging/qcacld-3.0/components/target_if/ftm_time_sync/src/target_if_ftm_time_sync.c new file mode 100644 index 0000000000000000000000000000000000000000..54b82943ceba64867a0f7b881ce583dcceeaae60 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/ftm_time_sync/src/target_if_ftm_time_sync.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Target interface file for ftm time sync component to + * Implement api's which shall be used by ftm time sync component + * in target_if internally. + */ + +#include "target_if.h" +#include "target_if_ftm_time_sync.h" +#include "wlan_ftm_time_sync_public_struct.h" +#include + +static QDF_STATUS +target_if_ftm_time_sync_send_qtime(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint64_t lpass_ts) +{ + wmi_unified_t wmi_hdl; + + wmi_hdl = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_hdl) + return QDF_STATUS_E_FAILURE; + + return wmi_unified_send_wlan_time_sync_qtime(wmi_hdl, vdev_id, + lpass_ts); +} + +static QDF_STATUS +target_if_ftm_time_sync_send_trigger(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, bool mode) +{ + wmi_unified_t wmi_hdl; + + wmi_hdl = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_hdl) + return QDF_STATUS_E_FAILURE; + + return wmi_unified_send_wlan_time_sync_ftm_trigger(wmi_hdl, vdev_id, + mode); +} + +static int +target_if_time_sync_ftm_start_stop_event_handler(ol_scn_t scn_handle, + uint8_t *data, uint32_t len) +{ + struct ftm_time_sync_start_stop_params param; + struct wlan_objmgr_psoc *psoc; + wmi_unified_t wmi_handle; + + if (!data) { + target_if_err("%s: invalid pointer", __func__); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn_handle); + if (!psoc) { + target_if_err("psoc ptr is NULL"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return -EINVAL; + } + + if (wmi_unified_extract_time_sync_ftm_start_stop_params( + wmi_handle, data, ¶m) != QDF_STATUS_SUCCESS) { + target_if_err("Extraction of time sync ftm start stop failed"); + return -EINVAL; + } + + return 0; +} + +static QDF_STATUS +target_if_ftm_time_sync_start_stop_event(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_register_event( + wmi_handle, wmi_wlan_time_sync_ftm_start_stop_event_id, + target_if_time_sync_ftm_start_stop_event_handler); + if (status) { + target_if_err("Ftm timesync start stop event register failed"); + return QDF_STATUS_E_FAILURE; + } + + return status; +} + +static int +target_if_time_sync_master_slave_offset_event_handler(ol_scn_t scn_handle, + uint8_t *data, + uint32_t len) +{ + struct ftm_time_sync_offset param; + struct wlan_objmgr_psoc *psoc; + wmi_unified_t wmi_handle; + + if (!data) { + target_if_err("%s: invalid pointer", __func__); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn_handle); + if (!psoc) { + target_if_err("psoc ptr is NULL"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return -EINVAL; + } + + if (wmi_unified_extract_time_sync_ftm_offset( + wmi_handle, data, ¶m) != QDF_STATUS_SUCCESS) { + target_if_err("Extraction of timesync ftm offset param failed"); + return -EINVAL; + } + + return 0; +} + +static QDF_STATUS +target_if_ftm_time_sync_master_slave_offset(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_register_event( + wmi_handle, + wmi_wlan_time_sync_q_master_slave_offset_eventid, + target_if_time_sync_master_slave_offset_event_handler); + if (status) { + target_if_err("Ftm timesync offset event register failed"); + return QDF_STATUS_E_FAILURE; + } + + return status; +} + +void +target_if_ftm_time_sync_register_rx_ops(struct wlan_ftm_timesync_rx_ops *rx_ops) +{ + if (!rx_ops) { + target_if_err("FTM timesync rx_ops is null"); + return; + } + + rx_ops->ftm_timesync_register_start_stop = + target_if_ftm_time_sync_start_stop_event; + rx_ops->ftm_timesync_regiser_master_slave_offset = + target_if_ftm_time_sync_master_slave_offset; +} + +void +target_if_ftm_time_sync_register_tx_ops(struct wlan_ftm_timesync_tx_ops *tx_ops) +{ + if (!tx_ops) { + target_if_err("FTM timesync tx_ops is null"); + return; + } + + tx_ops->ftm_time_sync_send_qtime = target_if_ftm_time_sync_send_qtime; + tx_ops->ftm_time_sync_send_trigger = + target_if_ftm_time_sync_send_trigger; +} + diff --git a/drivers/staging/qcacld-3.0/components/target_if/fw_offload/inc/target_if_fwol.h b/drivers/staging/qcacld-3.0/components/target_if/fw_offload/inc/target_if_fwol.h new file mode 100644 index 0000000000000000000000000000000000000000..7a6ab166904a2fcbd2208c80b710bd9b04133e1f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/fw_offload/inc/target_if_fwol.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: target interface APIs for fw offload + * + */ + +#ifndef __TARGET_IF_FWOL_H__ +#define __TARGET_IF_FWOL_H__ + +/** + * target_if_fwol_register_event_handler() - register fw offload event handler + * @psoc: psoc object + * @arg: argument passed to lmac + * + * Return: QDF_STATUS + */ +QDF_STATUS target_if_fwol_register_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * target_if_fwol_unregister_event_handler() - unregister fw offload event + * handler + * @psoc: psoc object + * @arg: argument passed to lmac + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_fwol_unregister_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * target_if_fwol_register_tx_ops() - register fw offload tx ops callback + * functions + * @tx_ops: fw offload tx operations + * + * Return: QDF_STATUS + */ +QDF_STATUS target_if_fwol_register_tx_ops(struct wlan_fwol_tx_ops *tx_ops); + +#endif /* __TARGET_IF_FWOL_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/target_if/fw_offload/src/target_if_fwol.c b/drivers/staging/qcacld-3.0/components/target_if/fw_offload/src/target_if_fwol.c new file mode 100644 index 0000000000000000000000000000000000000000..11b651068c8f61f103c5c776edcffd9819045273 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/fw_offload/src/target_if_fwol.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: target interface APIs for fw offload + * + */ + +#include "qdf_mem.h" +#include "target_if.h" +#include "qdf_status.h" +#include "wmi_unified_api.h" +#include "wmi_unified_priv.h" +#include "wmi_unified_param.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_utility.h" +#include "wlan_defs.h" +#include "wlan_fwol_public_structs.h" +#include "wlan_fw_offload_main.h" +#include "target_if_fwol.h" + +#ifdef WLAN_FEATURE_ELNA +/** + * target_if_fwol_set_elna_bypass() - send set eLNA bypass request to FW + * @psoc: pointer to PSOC object + * @req: set eLNA bypass request + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_fwol_set_elna_bypass(struct wlan_objmgr_psoc *psoc, + struct set_elna_bypass_request *req) +{ + QDF_STATUS status; + wmi_unified_t wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi_handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_send_set_elna_bypass_cmd(wmi_handle, req); + if (status) + target_if_err("Failed to set eLNA bypass %d", status); + + return status; +} + +/** + * target_if_fwol_get_elna_bypass() - send get eLNA bypass request to FW + * @psoc: pointer to PSOC object + * @req: get eLNA bypass request + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_fwol_get_elna_bypass(struct wlan_objmgr_psoc *psoc, + struct get_elna_bypass_request *req) +{ + QDF_STATUS status; + wmi_unified_t wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi_handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_send_get_elna_bypass_cmd(wmi_handle, req); + if (status) + target_if_err("Failed to set eLNA bypass %d", status); + + return status; +} + +/** + * target_if_fwol_get_elna_bypass_resp() - handler for get eLNA bypass response + * @scn: scn handle + * @event_buf: pointer to the event buffer + * @len: length of the buffer + * + * Return: 0 on success + */ +static int target_if_fwol_get_elna_bypass_resp(ol_scn_t scn, uint8_t *event_buf, + uint32_t len) +{ + QDF_STATUS status; + struct get_elna_bypass_response resp; + struct wlan_objmgr_psoc *psoc; + wmi_unified_t wmi_handle; + struct wlan_fwol_psoc_obj *fwol_obj; + struct wlan_fwol_rx_ops *rx_ops; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", scn, event_buf, len); + if (!scn || !event_buf) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi_handle"); + return -EINVAL; + } + + fwol_obj = fwol_get_psoc_obj(psoc); + if (!fwol_obj) { + target_if_err("Failed to get FWOL Obj"); + return -EINVAL; + } + + rx_ops = &fwol_obj->rx_ops; + if (rx_ops->get_elna_bypass_resp) { + status = wmi_extract_get_elna_bypass_resp(wmi_handle, + event_buf, &resp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("Failed to extract eLNA bypass"); + return -EINVAL; + } + status = rx_ops->get_elna_bypass_resp(psoc, &resp); + if (status != QDF_STATUS_SUCCESS) { + target_if_err("get_elna_bypass_resp failed."); + return -EINVAL; + } + } else { + target_if_fatal("No get_elna_bypass_resp callback"); + return -EINVAL; + } + + return 0; +}; + +static void +target_if_fwol_register_elna_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + int rc; + + rc = wmi_unified_register_event(get_wmi_unified_hdl_from_psoc(psoc), + wmi_get_elna_bypass_event_id, + target_if_fwol_get_elna_bypass_resp); + if (rc) + target_if_debug("Failed to register get eLNA bypass event cb"); +} + +static void +target_if_fwol_unregister_elna_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + int rc; + + rc = wmi_unified_unregister_event_handler( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_get_elna_bypass_event_id); + if (rc) + target_if_debug("Failed to unregister get eLNA bypass event cb"); +} + +static void +target_if_fwol_register_elna_tx_ops(struct wlan_fwol_tx_ops *tx_ops) +{ + tx_ops->set_elna_bypass = target_if_fwol_set_elna_bypass; + tx_ops->get_elna_bypass = target_if_fwol_get_elna_bypass; +} +#else +static void +target_if_fwol_register_elna_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ +} + +static void +target_if_fwol_unregister_elna_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ +} + +static void +target_if_fwol_register_elna_tx_ops(struct wlan_fwol_tx_ops *tx_ops) +{ +} +#endif /* WLAN_FEATURE_ELNA */ + +#ifdef WLAN_SEND_DSCP_UP_MAP_TO_FW +/** + * target_if_fwol_send_dscp_up_map_to_fw() - send dscp up map to FW + * @psoc: pointer to PSOC object + * @dscp_to_up_map: DSCP to UP map array + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_fwol_send_dscp_up_map_to_fw(struct wlan_objmgr_psoc *psoc, + uint32_t *dscp_to_up_map) +{ + QDF_STATUS status; + wmi_unified_t wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi_handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_send_dscp_tip_map_cmd(wmi_handle, dscp_to_up_map); + if (status) + target_if_err("Failed to send dscp_up_map_to_fw %d", status); + + return status; +} + +static void +target_if_fwol_register_dscp_up_tx_ops(struct wlan_fwol_tx_ops *tx_ops) +{ + tx_ops->send_dscp_up_map_to_fw = target_if_fwol_send_dscp_up_map_to_fw; +} +#else +static void +target_if_fwol_register_dscp_up_tx_ops(struct wlan_fwol_tx_ops *tx_ops) +{ +} +#endif + +QDF_STATUS target_if_fwol_register_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + target_if_fwol_register_elna_event_handler(psoc, arg); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +target_if_fwol_unregister_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + target_if_fwol_unregister_elna_event_handler(psoc, arg); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS target_if_fwol_register_tx_ops(struct wlan_fwol_tx_ops *tx_ops) +{ + target_if_fwol_register_elna_tx_ops(tx_ops); + target_if_fwol_register_dscp_up_tx_ops(tx_ops); + + tx_ops->reg_evt_handler = target_if_fwol_register_event_handler; + tx_ops->unreg_evt_handler = target_if_fwol_unregister_event_handler; + + return QDF_STATUS_SUCCESS; +} + diff --git a/drivers/staging/qcacld-3.0/components/target_if/interop_issues_ap/inc/target_if_interop_issues_ap.h b/drivers/staging/qcacld-3.0/components/target_if/interop_issues_ap/inc/target_if_interop_issues_ap.h new file mode 100644 index 0000000000000000000000000000000000000000..b65314a0d69eda3d3eb10e6b1ae83d4a97ef1fc2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/interop_issues_ap/inc/target_if_interop_issues_ap.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs for interop issues ap + */ +#ifndef __TARGET_IF_INTEROP_ISSUES_AP_H__ +#define __TARGET_IF_INTEROP_ISSUES_AP_H__ + +#ifdef WLAN_FEATURE_INTEROP_ISSUES_AP +#include +#include +#include +#include +#include + +/** + * target_if_interop_issues_ap_register_event_handler() - register callback + * @psoc: the pointer to psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_interop_issues_ap_register_event_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_interop_issues_ap_unregister_event_handler() - unregister callback + * @psoc: the pointer to psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_interop_issues_ap_unregister_event_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_interop_issues_ap_register_tx_ops() - register tx ops funcs + * @psoc: the pointer of psoc object + * @tx_ops: pointer to rap_ps tx ops + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS +target_if_interop_issues_ap_register_tx_ops(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_tx_ops *tx_ops); +/** + * target_if_interop_issues_ap_unregister_tx_ops() - unregister tx ops funcs + * @psoc: the pointer of psoc object + * @tx_ops: pointer to rap_ps tx ops + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS +target_if_interop_issues_ap_unregister_tx_ops(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_tx_ops *tx_ops); +#endif +#endif /* __TARGET_IF_INTEROP_ISSUES_AP_H__ */ diff --git a/drivers/staging/qcacld-3.0/components/target_if/interop_issues_ap/src/target_if_interop_issues_ap.c b/drivers/staging/qcacld-3.0/components/target_if/interop_issues_ap/src/target_if_interop_issues_ap.c new file mode 100644 index 0000000000000000000000000000000000000000..1b178b5d4101f683d3fb91e0ee64bd618b3ad785 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/interop_issues_ap/src/target_if_interop_issues_ap.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: target_if_interop_issues_ap.c + * + * This file provide definition for APIs registered through lmac Tx Ops + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * target_if_interop_issues_ap_event_handler() - callback for event + * @event: firmware event + * @len: the event length + * + * Return: 0 or error status + */ +static int target_if_interop_issues_ap_event_handler(ol_scn_t sc, + uint8_t *event, + uint32_t len) +{ + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct wlan_interop_issues_ap_event data = {0}; + int ret; + + TARGET_IF_ENTER(); + + psoc = target_if_get_psoc_from_scn_hdl(sc); + if (!psoc) { + target_if_err("psoc ptr is NULL"); + return -EINVAL; + } + data.psoc = psoc; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return -EINVAL; + } + + ret = wmi_extract_interop_issues_ap_ev_param(wmi_handle, event, &data); + if (ret) + return -EINVAL; + + target_if_debug("interop issues ap macaddr: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(data.rap_addr.bytes)); + + return tgt_interop_issues_ap_info_callback(psoc, &data); +} + +/** + * target_if_interop_issues_ap_register_event_handler() - register callback + * @psoc: the pointer to psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_interop_issues_ap_register_event_handler( + struct wlan_objmgr_psoc *psoc) +{ + int ret_val; + struct wmi_unified *wmi_handle; + + if (!psoc) { + target_if_err("PSOC is NULL!"); + return QDF_STATUS_E_NULL_VALUE; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null"); + return QDF_STATUS_E_INVAL; + } + ret_val = + wmi_unified_register_event_handler(wmi_handle, + wmi_pdev_interop_issues_ap_event_id, + target_if_interop_issues_ap_event_handler, + WMI_RX_WORK_CTX); + if (ret_val) + target_if_err("Failed to register event cb"); + + return qdf_status_from_os_return(ret_val); +} + +/** + * target_if_interop_issues_ap_unregister_event_handler() - unregister callback + * @psoc: the pointer to psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_interop_issues_ap_unregister_event_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wmi_unified *wmi_handle; + + if (!psoc) { + target_if_err("PSOC is NULL!"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null"); + return QDF_STATUS_E_INVAL; + } + wmi_unified_unregister_event_handler(wmi_handle, + wmi_pdev_interop_issues_ap_event_id); + + return QDF_STATUS_SUCCESS; +} + +/** + * target_if_set_interop_issues_ap_req() - API to send stats request to wmi + * @psoc: pointer to psoc object + * @raq: pointer to interop issues ap info + * + * Return: status of operation. + */ +static QDF_STATUS +target_if_set_interop_issues_ap_req(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_info *rap) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + return wmi_unified_set_rap_ps_cmd(wmi_handle, rap); +} + +QDF_STATUS +target_if_interop_issues_ap_register_tx_ops(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_tx_ops *tx_ops) +{ + if (!tx_ops) { + target_if_err("tx ops is NULL!"); + return QDF_STATUS_E_INVAL; + } + + tx_ops->set_rap_ps = target_if_set_interop_issues_ap_req; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +target_if_interop_issues_ap_unregister_tx_ops(struct wlan_objmgr_psoc *psoc, + struct wlan_interop_issues_ap_tx_ops *tx_ops) +{ + if (!tx_ops) { + target_if_err("tx ops is NULL!"); + return QDF_STATUS_E_INVAL; + } + + tx_ops->set_rap_ps = NULL; + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/ipa/inc/target_if_ipa.h b/drivers/staging/qcacld-3.0/components/target_if/ipa/inc/target_if_ipa.h new file mode 100644 index 0000000000000000000000000000000000000000..3055bf0b4d2e4713a13ed7019c4f0a3f2c90f48c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/ipa/inc/target_if_ipa.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Declare various api/struct which shall be used + * by ipa component for wmi cmd (tx path) + */ + +#ifndef _TARGET_IF_IPA_H_ +#define _TARGET_IF_IPA_H_ + +#ifdef IPA_OFFLOAD + +#include "wlan_ipa_public_struct.h" + +/** + * target_if_ipa_register_tx_ops() - Register IPA component TX OPS + * @ipa_tx_op: IPA if transmit op + * + * Return: None + */ +void target_if_ipa_register_tx_ops(ipa_uc_offload_control_req *ipa_tx_op); + +#endif /* IPA_OFFLOAD */ +#endif /* _TARGET_IF_IPA_H_ */ + diff --git a/drivers/staging/qcacld-3.0/components/target_if/ipa/src/target_if_ipa.c b/drivers/staging/qcacld-3.0/components/target_if/ipa/src/target_if_ipa.c new file mode 100644 index 0000000000000000000000000000000000000000..bde980809f0f801927a03fd6b07a2fea137a680e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/ipa/src/target_if_ipa.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Target interface file for disa component to + * Implement api's which shall be used by ipa component + * in target if internally. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * target_if_ipa_uc_offload_control_req() - send IPA offload control to FW + * @psoc: pointer to PSOC object + * @req: IPA UC offload enable/disable control param + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_ipa_uc_offload_control_req(struct wlan_objmgr_psoc *psoc, + struct ipa_uc_offload_control_params *req) +{ + return wmi_unified_ipa_offload_control_cmd( + get_wmi_unified_hdl_from_psoc(psoc), req); +} + +void target_if_ipa_register_tx_ops(ipa_uc_offload_control_req *ipa_tx_op) +{ + *ipa_tx_op = target_if_ipa_uc_offload_control_req; +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/nan/inc/target_if_nan.h b/drivers/staging/qcacld-3.0/components/target_if/nan/inc/target_if_nan.h new file mode 100644 index 0000000000000000000000000000000000000000..38370cdde96326006c124f542ce71996041227a4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/nan/inc/target_if_nan.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains nan target if declarations + */ + +#ifndef _WLAN_NAN_TGT_IF_H_ +#define _WLAN_NAN_TGT_IF_H_ + +#include "qdf_types.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "nan_public_structs.h" + +struct wlan_objmgr_psoc; + +/** + * target_if_nan_get_tx_ops() - retrieve the nan tx_ops + * @psoc: psoc context + * + * API to retrieve the nan tx_ops from the psoc context + * + * Return: nan tx_ops pointer + */ +struct wlan_nan_tx_ops *target_if_nan_get_tx_ops(struct wlan_objmgr_psoc *psoc); + +/** + * target_if_nan_get_rx_ops() - retrieve the nan rx_ops + * @psoc: psoc context + * + * API to retrieve the nan rx_ops from the psoc context + * + * Return: nan rx_ops pointer + */ +struct wlan_nan_rx_ops *target_if_nan_get_rx_ops(struct wlan_objmgr_psoc *psoc); + +/** + * target_if_nan_register_tx_ops() - registers nan tx ops + * @tx_ops: tx ops + * + * Return: none + */ +void target_if_nan_register_tx_ops(struct wlan_nan_tx_ops *tx_ops); + +/** + * target_if_nan_register_rx_ops() - registers nan rx ops + * @tx_ops: rx ops + * + * Return: none + */ +void target_if_nan_register_rx_ops(struct wlan_nan_rx_ops *rx_ops); + +/** + * target_if_nan_register_events() - registers with NDP events + * @psoc: pointer to psoc object + * + * Return: status of operation + */ +QDF_STATUS target_if_nan_register_events(struct wlan_objmgr_psoc *psoc); + +/** + * target_if_nan_deregister_events() - registers nan rx ops + * @psoc: pointer to psoc object + * + * Return: status of operation + */ +QDF_STATUS target_if_nan_deregister_events(struct wlan_objmgr_psoc *psoc); + +/** + * target_if_nan_rsp_handler() - Target IF handler for NAN Discovery events + * @scn: target handle + * @data: event buffer + * @len: event buffer length + * + * Return: 0 for success or error code + */ +int target_if_nan_rsp_handler(ol_scn_t scn, uint8_t *data, uint32_t len); + +/** + * target_if_nan_set_vdev_feature_config() - Init NAN feature config params + * @psoc: Pointer to PSOC Object + * @vdev_id: vdev_id of the current vdev + * + * This function updates NAN feature config bitmap to firmware + */ +void target_if_nan_set_vdev_feature_config(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); +#endif /* _WIFI_POS_TGT_IF_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/target_if/nan/src/target_if_nan.c b/drivers/staging/qcacld-3.0/components/target_if/nan/src/target_if_nan.c new file mode 100644 index 0000000000000000000000000000000000000000..8cad50898833d074c8e22134af03cd114e1c3105 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/nan/src/target_if_nan.c @@ -0,0 +1,1190 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains nan target if functions + */ + +#include "../../../nan/core/src/nan_main_i.h" +#include "nan_public_structs.h" +#include "nan_ucfg_api.h" +#include "target_if_nan.h" +#include "wlan_nan_api.h" +#include "wmi_unified_api.h" +#include "scheduler_api.h" +#include + +static void target_if_nan_event_flush_cb(struct scheduler_msg *msg) +{ + struct wlan_objmgr_psoc *psoc; + + if (!msg || !msg->bodyptr) { + target_if_err("Empty message for NAN Discovery event"); + return; + } + + psoc = ((struct nan_event_params *)msg->bodyptr)->psoc; + wlan_objmgr_psoc_release_ref(psoc, WLAN_NAN_ID); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; +} + +static QDF_STATUS target_if_nan_event_dispatcher(struct scheduler_msg *msg) +{ + struct wlan_nan_rx_ops *nan_rx_ops; + struct nan_event_params *nan_rsp; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + + nan_rsp = msg->bodyptr; + psoc = nan_rsp->psoc; + + nan_rx_ops = nan_psoc_get_rx_ops(psoc); + if (!nan_rx_ops) { + target_if_err("nan_rx_ops is null"); + status = QDF_STATUS_E_NULL_VALUE; + } else { + status = nan_rx_ops->nan_discovery_event_rx(msg); + } + + target_if_nan_event_flush_cb(msg); + return status; +} + +static QDF_STATUS target_if_ndp_event_flush_cb(struct scheduler_msg *msg) +{ + void *ptr = msg->bodyptr; + struct wlan_objmgr_vdev *vdev = NULL; + + switch (msg->type) { + case NDP_INITIATOR_RSP: + vdev = ((struct nan_datapath_initiator_rsp *)ptr)->vdev; + break; + case NDP_INDICATION: + vdev = ((struct nan_datapath_indication_event *)ptr)->vdev; + break; + case NDP_CONFIRM: + vdev = ((struct nan_datapath_confirm_event *)ptr)->vdev; + break; + case NDP_RESPONDER_RSP: + vdev = ((struct nan_datapath_responder_rsp *)ptr)->vdev; + break; + case NDP_END_RSP: + vdev = ((struct nan_datapath_end_rsp_event *)ptr)->vdev; + break; + case NDP_END_IND: + vdev = ((struct nan_datapath_end_indication_event *)ptr)->vdev; + break; + case NDP_SCHEDULE_UPDATE: + vdev = ((struct nan_datapath_sch_update_event *)ptr)->vdev; + break; + case NDP_HOST_UPDATE: + vdev = ((struct nan_datapath_host_event *)ptr)->vdev; + break; + default: + break; + } + + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS target_if_ndp_event_dispatcher(struct scheduler_msg *msg) +{ + QDF_STATUS status; + void *ptr = msg->bodyptr; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev = NULL; + struct wlan_nan_rx_ops *nan_rx_ops; + + switch (msg->type) { + case NDP_INITIATOR_RSP: + vdev = ((struct nan_datapath_initiator_rsp *)ptr)->vdev; + break; + case NDP_INDICATION: + vdev = ((struct nan_datapath_indication_event *)ptr)->vdev; + break; + case NDP_CONFIRM: + vdev = ((struct nan_datapath_confirm_event *)ptr)->vdev; + break; + case NDP_RESPONDER_RSP: + vdev = ((struct nan_datapath_responder_rsp *)ptr)->vdev; + break; + case NDP_END_RSP: + vdev = ((struct nan_datapath_end_rsp_event *)ptr)->vdev; + break; + case NDP_END_IND: + vdev = ((struct nan_datapath_end_indication_event *)ptr)->vdev; + break; + case NDP_SCHEDULE_UPDATE: + vdev = ((struct nan_datapath_sch_update_event *)ptr)->vdev; + break; + case NDP_HOST_UPDATE: + vdev = ((struct nan_datapath_host_event *)ptr)->vdev; + break; + default: + target_if_err("invalid msg type %d", msg->type); + status = QDF_STATUS_E_INVAL; + goto free_res; + } + + if (!vdev) { + target_if_err("vdev is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto free_res; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + target_if_err("psoc is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto free_res; + } + + nan_rx_ops = nan_psoc_get_rx_ops(psoc); + if (!nan_rx_ops) { + target_if_err("nan_rx_ops is null"); + status = QDF_STATUS_E_NULL_VALUE; + goto free_res; + } + + status = nan_rx_ops->nan_datapath_event_rx(msg); +free_res: + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + return status; +} + +static QDF_STATUS target_if_nan_ndp_initiator_req( + struct nan_datapath_initiator_req *ndp_req) +{ + QDF_STATUS status; + struct wmi_unified *wmi_handle; + struct wlan_objmgr_psoc *psoc; + struct scheduler_msg pe_msg = {0}; + struct wlan_nan_rx_ops *nan_rx_ops; + struct nan_datapath_initiator_rsp ndp_rsp = {0}; + + if (!ndp_req) { + target_if_err("ndp_req is null."); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(ndp_req->vdev); + if (!psoc) { + target_if_err("psoc is null."); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return QDF_STATUS_E_INVAL; + } + + nan_rx_ops = nan_psoc_get_rx_ops(psoc); + if (!nan_rx_ops) { + target_if_err("nan_rx_ops is null."); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_ndp_initiator_req_cmd_send(wmi_handle, ndp_req); + if (QDF_IS_STATUS_SUCCESS(status)) + return status; + + ndp_rsp.vdev = ndp_req->vdev; + ndp_rsp.transaction_id = ndp_req->transaction_id; + ndp_rsp.ndp_instance_id = ndp_req->service_instance_id; + ndp_rsp.status = NAN_DATAPATH_DATA_INITIATOR_REQ_FAILED; + pe_msg.type = NDP_INITIATOR_RSP; + pe_msg.bodyptr = &ndp_rsp; + if (nan_rx_ops->nan_datapath_event_rx) + nan_rx_ops->nan_datapath_event_rx(&pe_msg); + + return status; +} + +static int target_if_nan_dmesg_handler(ol_scn_t scn, uint8_t *data, + uint32_t data_len) +{ + QDF_STATUS status; + struct nan_dump_msg msg; + struct wmi_unified *wmi_handle; + struct wlan_objmgr_psoc *psoc; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null"); + return -EINVAL; + } + + status = wmi_extract_nan_msg(wmi_handle, data, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + return -EINVAL; + } + + if (!msg.msg) { + target_if_err("msg not present %d", msg.data_len); + return -EINVAL; + } + + target_if_info("%s", msg.msg); + + return 0; +} + +static int target_if_ndp_initiator_rsp_handler(ol_scn_t scn, uint8_t *data, + uint32_t len) +{ + QDF_STATUS status; + struct wmi_unified *wmi_handle; + struct wlan_objmgr_psoc *psoc; + struct scheduler_msg msg = {0}; + struct nan_datapath_initiator_rsp *rsp; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null"); + return -EINVAL; + } + + rsp = qdf_mem_malloc(sizeof(*rsp)); + if (!rsp) { + target_if_err("malloc failed"); + return -ENOMEM; + } + + status = wmi_extract_ndp_initiator_rsp(wmi_handle, data, rsp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + qdf_mem_free(rsp); + return -EINVAL; + } + + msg.bodyptr = rsp; + msg.type = NDP_INITIATOR_RSP; + msg.callback = target_if_ndp_event_dispatcher; + msg.flush_callback = target_if_ndp_event_flush_cb; + target_if_debug("NDP_INITIATOR_RSP sent: %d", msg.type); + status = scheduler_post_message(QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_ndp_event_flush_cb(&msg); + return -EINVAL; + } + + return 0; +} + +static int target_if_ndp_ind_handler(ol_scn_t scn, uint8_t *data, + uint32_t data_len) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct scheduler_msg msg = {0}; + struct nan_datapath_indication_event *rsp; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null"); + return -EINVAL; + } + + rsp = qdf_mem_malloc(sizeof(*rsp)); + if (!rsp) { + target_if_err("malloc failed"); + return -ENOMEM; + } + + status = wmi_extract_ndp_ind(wmi_handle, data, rsp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + qdf_mem_free(rsp); + return -EINVAL; + } + + msg.bodyptr = rsp; + msg.type = NDP_INDICATION; + msg.callback = target_if_ndp_event_dispatcher; + msg.flush_callback = target_if_ndp_event_flush_cb; + target_if_debug("NDP_INDICATION sent: %d", msg.type); + status = scheduler_post_message(QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_ndp_event_flush_cb(&msg); + return -EINVAL; + } + + return 0; +} + +static int target_if_ndp_confirm_handler(ol_scn_t scn, uint8_t *data, + uint32_t data_len) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct scheduler_msg msg = {0}; + struct nan_datapath_confirm_event *rsp; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null"); + return -EINVAL; + } + + rsp = qdf_mem_malloc(sizeof(*rsp)); + if (!rsp) { + target_if_err("malloc failed"); + return -ENOMEM; + } + + status = wmi_extract_ndp_confirm(wmi_handle, data, rsp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + qdf_mem_free(rsp); + return -EINVAL; + } + + msg.bodyptr = rsp; + msg.type = NDP_CONFIRM; + msg.callback = target_if_ndp_event_dispatcher; + msg.flush_callback = target_if_ndp_event_flush_cb; + target_if_debug("NDP_CONFIRM sent: %d", msg.type); + status = scheduler_post_message(QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_ndp_event_flush_cb(&msg); + return -EINVAL; + } + + return 0; +} + +static QDF_STATUS target_if_nan_ndp_responder_req( + struct nan_datapath_responder_req *req) +{ + QDF_STATUS status; + struct wmi_unified *wmi_handle; + struct wlan_objmgr_psoc *psoc; + struct scheduler_msg pe_msg = {0}; + struct wlan_nan_rx_ops *nan_rx_ops; + struct nan_datapath_responder_rsp rsp = {0}; + + if (!req) { + target_if_err("Invalid req."); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(req->vdev); + if (!psoc) { + target_if_err("psoc is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + nan_rx_ops = nan_psoc_get_rx_ops(psoc); + if (!nan_rx_ops) { + target_if_err("nan_rx_ops is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + status = wmi_unified_ndp_responder_req_cmd_send(wmi_handle, req); + if (QDF_IS_STATUS_SUCCESS(status)) + return status; + + rsp.vdev = req->vdev; + rsp.transaction_id = req->transaction_id; + rsp.status = NAN_DATAPATH_RSP_STATUS_ERROR; + rsp.reason = NAN_DATAPATH_DATA_RESPONDER_REQ_FAILED; + pe_msg.bodyptr = &rsp; + pe_msg.type = NDP_RESPONDER_RSP; + if (nan_rx_ops->nan_datapath_event_rx) + nan_rx_ops->nan_datapath_event_rx(&pe_msg); + + return status; +} + +static int target_if_ndp_responder_rsp_handler(ol_scn_t scn, uint8_t *data, + uint32_t len) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct scheduler_msg msg = {0}; + struct nan_datapath_responder_rsp *rsp; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return -EINVAL; + } + + rsp = qdf_mem_malloc(sizeof(*rsp)); + if (!rsp) { + target_if_err("malloc failed"); + return -ENOMEM; + } + + status = wmi_extract_ndp_responder_rsp(wmi_handle, data, rsp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + qdf_mem_free(rsp); + return -EINVAL; + } + + msg.bodyptr = rsp; + msg.type = NDP_RESPONDER_RSP; + msg.callback = target_if_ndp_event_dispatcher; + msg.flush_callback = target_if_ndp_event_flush_cb; + target_if_debug("NDP_INITIATOR_RSP sent: %d", msg.type); + status = scheduler_post_message(QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_ndp_event_flush_cb(&msg); + return -EINVAL; + } + + return 0; +} + +static QDF_STATUS target_if_nan_ndp_end_req(struct nan_datapath_end_req *req) +{ + QDF_STATUS status; + struct wmi_unified *wmi_handle; + struct wlan_objmgr_psoc *psoc; + struct scheduler_msg msg = {0}; + struct wlan_nan_rx_ops *nan_rx_ops; + struct nan_datapath_end_rsp_event end_rsp = {0}; + + if (!req) { + target_if_err("req is null"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(req->vdev); + if (!psoc) { + target_if_err("psoc is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + nan_rx_ops = nan_psoc_get_rx_ops(psoc); + if (!nan_rx_ops) { + target_if_err("nan_rx_ops is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + status = wmi_unified_ndp_end_req_cmd_send(wmi_handle, req); + if (QDF_IS_STATUS_SUCCESS(status)) + return status; + + end_rsp.vdev = req->vdev; + msg.type = NDP_END_RSP; + end_rsp.status = NAN_DATAPATH_RSP_STATUS_ERROR; + end_rsp.reason = NAN_DATAPATH_END_FAILED; + end_rsp.transaction_id = req->transaction_id; + msg.bodyptr = &end_rsp; + + if (nan_rx_ops->nan_datapath_event_rx) + nan_rx_ops->nan_datapath_event_rx(&msg); + + return status; +} + +static int target_if_ndp_end_rsp_handler(ol_scn_t scn, uint8_t *data, + uint32_t data_len) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct scheduler_msg msg = {0}; + struct nan_datapath_end_rsp_event *end_rsp; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return -EINVAL; + } + + end_rsp = qdf_mem_malloc(sizeof(*end_rsp)); + if (!end_rsp) { + target_if_err("malloc failed"); + return -ENOMEM; + } + + status = wmi_extract_ndp_end_rsp(wmi_handle, data, end_rsp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + qdf_mem_free(end_rsp); + return -EINVAL; + } + + msg.bodyptr = end_rsp; + msg.type = NDP_END_RSP; + msg.callback = target_if_ndp_event_dispatcher; + msg.flush_callback = target_if_ndp_event_flush_cb; + target_if_debug("NDP_END_RSP sent: %d", msg.type); + status = scheduler_post_message(QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_ndp_event_flush_cb(&msg); + return -EINVAL; + } + + return 0; +} + +static int target_if_ndp_end_ind_handler(ol_scn_t scn, uint8_t *data, + uint32_t data_len) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct scheduler_msg msg = {0}; + struct nan_datapath_end_indication_event *rsp = NULL; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return -EINVAL; + } + + status = wmi_extract_ndp_end_ind(wmi_handle, data, &rsp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + return -EINVAL; + } + + rsp->vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc( + wmi_handle->soc->wmi_psoc, QDF_NDI_MODE, WLAN_NAN_ID); + if (!rsp->vdev) { + target_if_err("vdev is null"); + qdf_mem_free(rsp); + return -EINVAL; + } + + msg.bodyptr = rsp; + msg.type = NDP_END_IND; + msg.callback = target_if_ndp_event_dispatcher; + msg.flush_callback = target_if_ndp_event_flush_cb; + target_if_debug("NDP_END_IND sent: %d", msg.type); + status = scheduler_post_message(QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_ndp_event_flush_cb(&msg); + return -EINVAL; + } + + return 0; +} + +static int target_if_ndp_sch_update_handler(ol_scn_t scn, uint8_t *data, + uint32_t data_len) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct scheduler_msg msg = {0}; + struct nan_datapath_sch_update_event *rsp; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return -EINVAL; + } + + rsp = qdf_mem_malloc(sizeof(*rsp)); + if (!rsp) { + target_if_err("malloc failed"); + return -ENOMEM; + } + + status = wmi_extract_ndp_sch_update(wmi_handle, data, rsp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + qdf_mem_free(rsp); + return -EINVAL; + } + + msg.bodyptr = rsp; + msg.type = NDP_SCHEDULE_UPDATE; + msg.callback = target_if_ndp_event_dispatcher; + msg.flush_callback = target_if_ndp_event_flush_cb; + target_if_debug("NDP_SCHEDULE_UPDATE sent: %d", msg.type); + status = scheduler_post_message(QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_ndp_event_flush_cb(&msg); + return -EINVAL; + } + + return 0; +} + +static QDF_STATUS target_if_nan_end_all_ndps_req(void *req) +{ + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + uint8_t vdev_id; + + vdev = ((struct nan_datapath_end_all_ndps *)req)->vdev; + if (!vdev) { + target_if_err("vdev object is NULL!"); + return QDF_STATUS_E_INVAL; + } + vdev_id = wlan_vdev_get_id(vdev); + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + target_if_err("psoc is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + return wmi_unified_terminate_all_ndps_req_cmd(wmi_handle, vdev_id); +} + +static int target_if_ndp_host_event_handler(ol_scn_t scn, uint8_t *data, + uint32_t data_len) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct scheduler_msg msg = {0}; + struct nan_datapath_host_event *host_evt = NULL; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return -EINVAL; + } + + host_evt = qdf_mem_malloc(sizeof(*host_evt)); + if (!host_evt) { + target_if_err("malloc failed"); + return -ENOMEM; + } + + status = wmi_extract_ndp_host_event(wmi_handle, data, host_evt); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + qdf_mem_free(host_evt); + return -EINVAL; + } + + if (!host_evt->vdev) { + target_if_err("vdev is null"); + qdf_mem_free(host_evt); + return -EINVAL; + } + + msg.bodyptr = host_evt; + msg.type = NDP_HOST_UPDATE; + msg.callback = target_if_ndp_event_dispatcher; + msg.flush_callback = target_if_ndp_event_flush_cb; + target_if_debug("NDP_HOST_UPDATE sent: %d", msg.type); + status = scheduler_post_message(QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("failed to post msg, status: %d", status); + target_if_ndp_event_flush_cb(&msg); + return -EINVAL; + } + + return 0; +} + +static QDF_STATUS target_if_nan_datapath_req(void *req, uint32_t req_type) +{ + /* send cmd to fw */ + switch (req_type) { + case NDP_INITIATOR_REQ: + target_if_nan_ndp_initiator_req(req); + break; + case NDP_RESPONDER_REQ: + target_if_nan_ndp_responder_req(req); + break; + case NDP_END_REQ: + target_if_nan_ndp_end_req(req); + break; + case NDP_END_ALL: + target_if_nan_end_all_ndps_req(req); + break; + default: + target_if_err("invalid req type"); + break; + } + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS target_if_nan_generic_req(struct wlan_objmgr_psoc *psoc, + void *nan_req) +{ + struct wmi_unified *wmi_handle; + + if (!psoc) { + target_if_err("psoc is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!nan_req) { + target_if_err("Invalid req."); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + return wmi_unified_nan_req_cmd(wmi_handle, nan_req); +} + +static QDF_STATUS target_if_nan_disable_req(struct nan_disable_req *nan_req) +{ + struct wmi_unified *wmi_handle; + struct wlan_objmgr_psoc *psoc; + + if (!nan_req) { + target_if_err("Invalid req."); + return QDF_STATUS_E_INVAL; + } + psoc = nan_req->psoc; + + if (!psoc) { + target_if_err("psoc is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null."); + return QDF_STATUS_E_NULL_VALUE; + } + + return wmi_unified_nan_disable_req_cmd(wmi_handle, nan_req); +} + +static QDF_STATUS target_if_nan_discovery_req(void *req, uint32_t req_type) +{ + QDF_STATUS status; + + if (!req) { + target_if_err("Invalid req."); + return QDF_STATUS_E_INVAL; + } + + switch (req_type) { + case NAN_DISABLE_REQ: + status = target_if_nan_disable_req(req); + break; + case NAN_GENERIC_REQ: { + struct nan_generic_req *nan_req = req; + + status = target_if_nan_generic_req(nan_req->psoc, + &nan_req->params); + break; + } + case NAN_ENABLE_REQ: { + struct nan_enable_req *nan_req = req; + + status = target_if_nan_generic_req(nan_req->psoc, + &nan_req->params); + break; + } + default: + target_if_err("Invalid NAN req type"); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +void target_if_nan_register_tx_ops(struct wlan_nan_tx_ops *tx_ops) +{ + tx_ops->nan_discovery_req_tx = target_if_nan_discovery_req; + tx_ops->nan_datapath_req_tx = target_if_nan_datapath_req; +} + +void target_if_nan_register_rx_ops(struct wlan_nan_rx_ops *rx_ops) +{ + rx_ops->nan_discovery_event_rx = nan_discovery_event_handler; + rx_ops->nan_datapath_event_rx = nan_datapath_event_handler; +} + +int target_if_nan_rsp_handler(ol_scn_t scn, uint8_t *data, uint32_t len) +{ + struct nan_event_params *nan_rsp, temp_evt_params = {0}; + struct scheduler_msg msg = {0}; + struct wmi_unified *wmi_handle; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + uint8_t *buf_ptr; + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("psoc is null"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("wmi_handle is null"); + return -EINVAL; + } + + status = wmi_extract_nan_event_rsp(wmi_handle, data, &temp_evt_params, + &buf_ptr); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("parsing of event failed, %d", status); + return -EINVAL; + } + + nan_rsp = qdf_mem_malloc(sizeof(*nan_rsp) + temp_evt_params.buf_len); + if (!nan_rsp) { + target_if_err("malloc failed"); + return -ENOMEM; + } + qdf_mem_copy(nan_rsp, &temp_evt_params, sizeof(*nan_rsp)); + + status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_NAN_ID); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("Failed to obtain psoc ref"); + return -EACCES; + } + + nan_rsp->psoc = psoc; + qdf_mem_copy(nan_rsp->buf, buf_ptr, nan_rsp->buf_len); + + msg.bodyptr = nan_rsp; + msg.type = nan_rsp->evt_type; + msg.callback = target_if_nan_event_dispatcher; + msg.flush_callback = target_if_nan_event_flush_cb; + target_if_debug("NAN Event sent: %d", msg.type); + status = scheduler_post_message(QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("failed to post msg, status: %d", status); + target_if_nan_event_flush_cb(&msg); + return -EINVAL; + } + + return 0; +} + +QDF_STATUS target_if_nan_register_events(struct wlan_objmgr_psoc *psoc) +{ + int ret; + wmi_unified_t handle = get_wmi_unified_hdl_from_psoc(psoc); + + if (!handle) { + target_if_err("handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + /* Register for nan response event */ + ret = wmi_unified_register_event_handler(handle, wmi_nan_event_id, + target_if_nan_rsp_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wmi_unified_register_event_handler(handle, + wmi_ndp_initiator_rsp_event_id, + target_if_ndp_initiator_rsp_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wmi_unified_register_event_handler(handle, wmi_nan_dmesg_event_id, + target_if_nan_dmesg_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + target_if_nan_deregister_events(psoc); + return QDF_STATUS_E_FAILURE; + } + + ret = wmi_unified_register_event_handler(handle, + wmi_ndp_indication_event_id, + target_if_ndp_ind_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + target_if_nan_deregister_events(psoc); + return QDF_STATUS_E_FAILURE; + } + + ret = wmi_unified_register_event_handler(handle, + wmi_ndp_confirm_event_id, + target_if_ndp_confirm_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + target_if_nan_deregister_events(psoc); + return QDF_STATUS_E_FAILURE; + } + + ret = wmi_unified_register_event_handler(handle, + wmi_ndp_responder_rsp_event_id, + target_if_ndp_responder_rsp_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + target_if_nan_deregister_events(psoc); + return QDF_STATUS_E_FAILURE; + } + + ret = wmi_unified_register_event_handler(handle, + wmi_ndp_end_indication_event_id, + target_if_ndp_end_ind_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + target_if_nan_deregister_events(psoc); + return QDF_STATUS_E_FAILURE; + } + + ret = wmi_unified_register_event_handler(handle, + wmi_ndp_end_rsp_event_id, + target_if_ndp_end_rsp_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + target_if_nan_deregister_events(psoc); + return QDF_STATUS_E_FAILURE; + } + + ret = wmi_unified_register_event_handler(handle, + wmi_ndl_schedule_update_event_id, + target_if_ndp_sch_update_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + target_if_nan_deregister_events(psoc); + return QDF_STATUS_E_FAILURE; + } + + ret = wmi_unified_register_event_handler(handle, wmi_ndp_event_id, + target_if_ndp_host_event_handler, + WMI_RX_UMAC_CTX); + if (ret) { + target_if_err("wmi event registration failed, ret: %d", ret); + target_if_nan_deregister_events(psoc); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS target_if_nan_deregister_events(struct wlan_objmgr_psoc *psoc) +{ + int ret, status = 0; + wmi_unified_t handle = get_wmi_unified_hdl_from_psoc(psoc); + + if (!handle) { + target_if_err("handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + ret = wmi_unified_unregister_event_handler(handle, + wmi_ndl_schedule_update_event_id); + if (ret) { + target_if_err("wmi event deregistration failed, ret: %d", ret); + status = ret; + } + + ret = wmi_unified_unregister_event_handler(handle, + wmi_ndp_end_rsp_event_id); + if (ret) { + target_if_err("wmi event deregistration failed, ret: %d", ret); + status = ret; + } + + ret = wmi_unified_unregister_event_handler(handle, + wmi_ndp_end_indication_event_id); + if (ret) { + target_if_err("wmi event deregistration failed, ret: %d", ret); + status = ret; + } + + ret = wmi_unified_unregister_event_handler(handle, + wmi_ndp_responder_rsp_event_id); + if (ret) { + target_if_err("wmi event deregistration failed, ret: %d", ret); + status = ret; + } + + ret = wmi_unified_unregister_event_handler(handle, + wmi_ndp_confirm_event_id); + if (ret) { + target_if_err("wmi event deregistration failed, ret: %d", ret); + status = ret; + } + + ret = wmi_unified_unregister_event_handler(handle, + wmi_ndp_indication_event_id); + if (ret) { + target_if_err("wmi event deregistration failed, ret: %d", ret); + status = ret; + } + + ret = wmi_unified_unregister_event_handler(handle, + wmi_nan_dmesg_event_id); + if (ret) { + target_if_err("wmi event deregistration failed, ret: %d", ret); + status = ret; + } + + ret = wmi_unified_unregister_event_handler(handle, + wmi_ndp_initiator_rsp_event_id); + if (ret) { + target_if_err("wmi event deregistration failed, ret: %d", ret); + status = ret; + } + + ret = wmi_unified_unregister_event_handler(handle, wmi_ndp_event_id); + if (ret) { + target_if_err("wmi event deregistration failed, ret: %d", ret); + status = ret; + } + + if (status) + return QDF_STATUS_E_FAILURE; + else + return QDF_STATUS_SUCCESS; +} + +void target_if_nan_set_vdev_feature_config(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + QDF_STATUS status; + uint32_t nan_features; + struct vdev_set_params param; + wmi_unified_t wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return; + } + + ucfg_get_nan_feature_config(psoc, &nan_features); + target_if_debug("vdev_id:%d NAN features:0x%x", vdev_id, nan_features); + + param.vdev_id = vdev_id; + param.param_id = WMI_VDEV_PARAM_ENABLE_DISABLE_NAN_CONFIG_FEATURES; + param.param_value = nan_features; + + status = wmi_unified_vdev_set_param_send(wmi_handle, ¶m); + if (QDF_IS_STATUS_ERROR(status)) + target_if_err("failed to set NAN_CONFIG_FEATURES(status = %d)", + status); +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/ocb/inc/target_if_ocb.h b/drivers/staging/qcacld-3.0/components/target_if/ocb/inc/target_if_ocb.h new file mode 100644 index 0000000000000000000000000000000000000000..13f9afb9c85053ebcadecc31129da86531d01736 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/ocb/inc/target_if_ocb.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs for ocb + * + */ + +#ifndef __TARGET_IF_OCB_H__ +#define __TARGET_IF_OCB_H__ + +/** + * target_if_ocb_register_event_handler() - lmac handler to register ocb event + * handler + * @psoc : psoc object + * @arg: argument passed to lmac + * + * Return: QDF_STATUS + */ +QDF_STATUS target_if_ocb_register_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * target_if_ocb_unregister_event_handler() - lmac handler to unregister ocb + * event handler + * @psoc : psoc object + * @arg: argument passed to lmac + * + * Return: QDF_STATUS + */ +QDF_STATUS target_if_ocb_unregister_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg); +/** + * target_if_ocb_register_tx_ops() - lmac handler to register ocb tx ops + * callback functions + * @tx_ops: ocb tx operations + * + * Return: QDF_STATUS + */ +QDF_STATUS target_if_ocb_register_tx_ops( + struct wlan_ocb_tx_ops *tx_ops); + +#endif diff --git a/drivers/staging/qcacld-3.0/components/target_if/ocb/src/target_if_ocb.c b/drivers/staging/qcacld-3.0/components/target_if/ocb/src/target_if_ocb.c new file mode 100644 index 0000000000000000000000000000000000000000..e727a9b781feab2c468708b4074ad0a7948a238c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/ocb/src/target_if_ocb.c @@ -0,0 +1,699 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs for ocb + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * target_if_ocb_get_rx_ops() - get target interface RX operations + * @pdev: pdev handle + * + * Return: fp to target interface RX operations + */ +static inline struct wlan_ocb_rx_ops * +target_if_ocb_get_rx_ops(struct wlan_objmgr_pdev *pdev) +{ + struct ocb_pdev_obj *ocb_obj; + + ocb_obj = wlan_get_pdev_ocb_obj(pdev); + + return &ocb_obj->ocb_rxops; +} + +/** + * target_if_ocb_set_config() - send the OCB config to the FW + * @psoc: pointer to PSOC object + * @config: OCB channel configuration + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS target_if_ocb_set_config(struct wlan_objmgr_psoc *psoc, + struct ocb_config *config) +{ + QDF_STATUS status; + + status = wmi_unified_ocb_set_config(get_wmi_unified_hdl_from_psoc(psoc), + config); + if (status) + target_if_err("Failed to set OCB config %d", status); + + return status; +} + +/** + * target_if_ocb_set_utc_time() - send the UTC time to the firmware + * @psoc: pointer to PSOC object + * @utc: pointer to the UTC time structure + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS target_if_ocb_set_utc_time(struct wlan_objmgr_psoc *psoc, + struct ocb_utc_param *utc) +{ + QDF_STATUS status; + + status = wmi_unified_ocb_set_utc_time_cmd( + get_wmi_unified_hdl_from_psoc(psoc), utc); + if (status) + target_if_err("Failed to set OCB UTC time %d", status); + + return status; +} + +/** + * target_if_ocb_start_timing_advert() - start sending the timing + * advertisement frame on a channel + * @psoc: pointer to PSOC object + * @ta: pointer to the timing advertisement + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_ocb_start_timing_advert(struct wlan_objmgr_psoc *psoc, + struct ocb_timing_advert_param *ta) +{ + QDF_STATUS status; + + status = wmi_unified_ocb_start_timing_advert( + get_wmi_unified_hdl_from_psoc(psoc), ta); + if (status) + target_if_err("Failed to start OCB timing advert %d", status); + + return status; +} + +/** + * target_if_ocb_stop_timing_advert() - stop sending the timing + * advertisement frame on a channel + * @psoc: pointer to PSOC object + * @ta: pointer to the timing advertisement + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_ocb_stop_timing_advert(struct wlan_objmgr_psoc *psoc, + struct ocb_timing_advert_param *ta) +{ + QDF_STATUS status; + + status = + wmi_unified_ocb_stop_timing_advert( + get_wmi_unified_hdl_from_psoc(psoc), ta); + if (status) + target_if_err("Failed to stop OCB timing advert %d", status); + + return status; +} + +/** + * target_if_ocb_get_tsf_timer() - get tsf timer + * @psoc: pointer to PSOC object + * @request: pointer to the request + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_ocb_get_tsf_timer(struct wlan_objmgr_psoc *psoc, + struct ocb_get_tsf_timer_param *request) +{ + QDF_STATUS status; + + status = wmi_unified_ocb_get_tsf_timer( + get_wmi_unified_hdl_from_psoc(psoc), request); + if (status) + target_if_err("Failed to send get tsf timer cmd: %d", status); + + return status; +} + +/** + * target_if_dcc_get_stats() - get the DCC channel stats + * @psoc: pointer to PSOC object + * @get_stats_param: pointer to the dcc stats request + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_dcc_get_stats(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_get_stats_param *get_stats_param) +{ + QDF_STATUS status; + + status = wmi_unified_dcc_get_stats_cmd( + get_wmi_unified_hdl_from_psoc(psoc), get_stats_param); + if (status) + target_if_err("Failed to send get DCC stats cmd: %d", status); + + return status; +} + +/** + * target_if_dcc_clear_stats() - send command to clear the DCC stats + * @psoc: pointer to PSOC object + * @clear_stats_param: parameters to the command + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_dcc_clear_stats(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_clear_stats_param *clear_stats_param) +{ + QDF_STATUS status; + + status = wmi_unified_dcc_clear_stats( + get_wmi_unified_hdl_from_psoc(psoc), clear_stats_param); + if (status) + target_if_err("Failed to send clear DCC stats cmd: %d", status); + + return status; +} + +/** + * target_if_dcc_update_ndl() - command to update the NDL data + * @psoc: pointer to PSOC object + * @update_ndl_param: pointer to the request parameters + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS +target_if_dcc_update_ndl(struct wlan_objmgr_psoc *psoc, + struct ocb_dcc_update_ndl_param *update_ndl_param) +{ + QDF_STATUS status; + + /* Send the WMI command */ + status = wmi_unified_dcc_update_ndl(get_wmi_unified_hdl_from_psoc(psoc), + update_ndl_param); + if (status) + target_if_err("Failed to send NDL update cmd: %d", status); + + return status; +} + +/** + * target_if_ocb_set_config_resp() - handler for channel config response + * @scn: scn handle + * @event_buf: pointer to the event buffer + * @len: length of the buffer + * + * Return: 0 on success + */ +static int +target_if_ocb_set_config_resp(ol_scn_t scn, uint8_t *event_buf, + uint32_t len) +{ + int rc; + QDF_STATUS status; + uint32_t resp; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + struct wlan_ocb_rx_ops *ocb_rx_ops; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", + scn, event_buf, len); + if (!scn || !event_buf) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_OCB_SB_ID); + if (!pdev) { + target_if_err("pdev is NULL"); + return -EINVAL; + } + + ocb_rx_ops = target_if_ocb_get_rx_ops(pdev); + if (ocb_rx_ops->ocb_set_config_status) { + status = wmi_extract_ocb_set_channel_config_resp( + get_wmi_unified_hdl_from_psoc(psoc), + event_buf, &resp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("Failed to extract config status"); + rc = -EINVAL; + goto exit; + } + status = ocb_rx_ops->ocb_set_config_status(psoc, resp); + if (status != QDF_STATUS_SUCCESS) { + target_if_err("ocb_set_config_status failed."); + rc = -EINVAL; + goto exit; + } + rc = 0; + } else { + target_if_fatal("No ocb_set_config_status callback"); + rc = -EINVAL; + } +exit: + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID); + + return rc; +}; + +/** + * target_if_ocb_get_tsf_timer_resp() - handler for TSF timer response + * @scn: scn handle + * @event_buf: pointer to the event buffer + * @len: length of the buffer + * + * Return: 0 on success + */ +static int target_if_ocb_get_tsf_timer_resp(ol_scn_t scn, + uint8_t *event_buf, + uint32_t len) +{ + int rc; + QDF_STATUS status; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + struct ocb_get_tsf_timer_response response; + struct wlan_ocb_rx_ops *ocb_rx_ops; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", + scn, event_buf, len); + + if (!scn || !event_buf) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_OCB_SB_ID); + if (!pdev) { + target_if_err("pdev is NULL"); + return -EINVAL; + } + + ocb_rx_ops = target_if_ocb_get_rx_ops(pdev); + if (ocb_rx_ops->ocb_tsf_timer) { + status = wmi_extract_ocb_tsf_timer( + get_wmi_unified_hdl_from_psoc(psoc), + event_buf, &response); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("Failed to extract tsf timer"); + rc = -EINVAL; + goto exit; + } + status = ocb_rx_ops->ocb_tsf_timer(psoc, &response); + if (status != QDF_STATUS_SUCCESS) { + target_if_err("ocb_tsf_timer failed."); + rc = -EINVAL; + goto exit; + } + rc = 0; + } else { + target_if_fatal("No ocb_tsf_timer callback"); + rc = -EINVAL; + } +exit: + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID); + + return rc; +} + +/** + * target_if_dcc_update_ndl_resp() - handler for update NDL response + * @scn: scn handle + * @event_buf: pointer to the event buffer + * @len: length of the buffer + * + * Return: 0 on success + */ +static int target_if_dcc_update_ndl_resp(ol_scn_t scn, + uint8_t *event_buf, + uint32_t len) +{ + int rc; + QDF_STATUS status; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + struct ocb_dcc_update_ndl_response *resp; + struct wlan_ocb_rx_ops *ocb_rx_ops; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", + scn, event_buf, len); + + if (!scn || !event_buf) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_OCB_SB_ID); + if (!pdev) { + target_if_err("pdev is NULL"); + return -EINVAL; + } + + /* Allocate and populate the response */ + resp = qdf_mem_malloc(sizeof(*resp)); + if (!resp) { + target_if_err("Error allocating memory for the response."); + rc = -ENOMEM; + goto exit; + } + + ocb_rx_ops = target_if_ocb_get_rx_ops(pdev); + if (ocb_rx_ops->ocb_dcc_ndl_update) { + status = wmi_extract_dcc_update_ndl_resp( + get_wmi_unified_hdl_from_psoc(psoc), + event_buf, resp); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("Failed to extract ndl status"); + rc = -EINVAL; + goto exit; + } + status = ocb_rx_ops->ocb_dcc_ndl_update(psoc, resp); + if (status != QDF_STATUS_SUCCESS) { + target_if_err("dcc_ndl_update failed."); + rc = -EINVAL; + goto exit; + } + rc = 0; + } else { + target_if_fatal("No dcc_ndl_update callback"); + rc = -EINVAL; + } +exit: + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID); + if (resp) + qdf_mem_free(resp); + + return rc; +} + +/** + * target_if_dcc_get_stats_resp() - handler for get stats response + * @scn: scn handle + * @event_buf: pointer to the event buffer + * @len: length of the buffer + * + * Return: 0 on success + */ +static int target_if_dcc_get_stats_resp(ol_scn_t scn, + uint8_t *event_buf, + uint32_t len) +{ + int rc; + QDF_STATUS status; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + struct ocb_dcc_get_stats_response *response; + struct wlan_ocb_rx_ops *ocb_rx_ops; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", + scn, event_buf, len); + + if (!scn || !event_buf) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_OCB_SB_ID); + if (!pdev) { + target_if_err("pdev is NULL"); + return -EINVAL; + } + + ocb_rx_ops = target_if_ocb_get_rx_ops(pdev); + if (ocb_rx_ops->ocb_dcc_stats_indicate) { + status = wmi_extract_dcc_stats( + get_wmi_unified_hdl_from_psoc(psoc), + event_buf, &response); + if (!response || QDF_IS_STATUS_ERROR(status)) { + target_if_err("Cannot get DCC stats"); + rc = -ENOMEM; + goto exit; + } + + status = ocb_rx_ops->ocb_dcc_stats_indicate(psoc, + response, + true); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("dcc_stats_indicate failed."); + rc = -EINVAL; + goto exit; + } + rc = 0; + } else { + target_if_fatal("No dcc_stats_indicate callback"); + rc = -EINVAL; + } +exit: + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID); + if (response) + qdf_mem_free(response); + + return rc; +} + +/** + * target_if_dcc_stats_resp() - handler for DCC stats indication event + * @scn: scn handle + * @event_buf: pointer to the event buffer + * @len: length of the buffer + * + * Return: 0 on success + */ +static int target_if_dcc_stats_resp(ol_scn_t scn, uint8_t *event_buf, + uint32_t len) +{ + int rc; + QDF_STATUS status; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + struct ocb_dcc_get_stats_response *response; + struct wlan_ocb_rx_ops *ocb_rx_ops; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", + scn, event_buf, len); + + if (!scn || !event_buf) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, + WLAN_OCB_SB_ID); + if (!pdev) { + target_if_err("pdev is NULL"); + return -EINVAL; + } + + ocb_rx_ops = target_if_ocb_get_rx_ops(pdev); + if (ocb_rx_ops->ocb_dcc_stats_indicate) { + status = wmi_extract_dcc_stats( + get_wmi_unified_hdl_from_psoc(psoc), + event_buf, &response); + if (!response || QDF_IS_STATUS_ERROR(status)) { + target_if_err("Cannot get DCC stats"); + rc = -ENOMEM; + goto exit; + } + status = ocb_rx_ops->ocb_dcc_stats_indicate(psoc, + response, + false); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("dcc_stats_indicate failed."); + rc = -EINVAL; + goto exit; + } + rc = 0; + } else { + target_if_fatal("dcc_stats_indicate failed."); + response = NULL; + rc = -EINVAL; + } +exit: + wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID); + if (response) + qdf_mem_free(response); + + return rc; +} + +QDF_STATUS target_if_ocb_register_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + int rc; + + /* Initialize the members in WMA used by wma_ocb */ + rc = wmi_unified_register_event(get_wmi_unified_hdl_from_psoc(psoc), + wmi_ocb_set_config_resp_event_id, + target_if_ocb_set_config_resp); + if (rc) { + target_if_err("Failed to register OCB config resp event cb"); + return QDF_STATUS_E_FAILURE; + } + + rc = wmi_unified_register_event( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_ocb_get_tsf_timer_resp_event_id, + target_if_ocb_get_tsf_timer_resp); + if (rc) { + target_if_err("Failed to register OCB TSF resp event cb"); + goto unreg_set_config; + } + + rc = wmi_unified_register_event( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_dcc_get_stats_resp_event_id, + target_if_dcc_get_stats_resp); + if (rc) { + target_if_err("Failed to register DCC get stats resp event cb"); + goto unreg_tsf_timer; + } + + rc = wmi_unified_register_event( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_dcc_update_ndl_resp_event_id, + target_if_dcc_update_ndl_resp); + if (rc) { + target_if_err("Failed to register NDL update event cb"); + goto unreg_get_stats; + } + + rc = wmi_unified_register_event(get_wmi_unified_hdl_from_psoc(psoc), + wmi_dcc_stats_event_id, + target_if_dcc_stats_resp); + if (rc) { + target_if_err("Failed to register DCC stats event cb"); + goto unreg_ndl; + } + + return QDF_STATUS_SUCCESS; + +unreg_ndl: + wmi_unified_unregister_event_handler( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_dcc_update_ndl_resp_event_id); +unreg_get_stats: + wmi_unified_unregister_event_handler( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_dcc_get_stats_resp_event_id); +unreg_tsf_timer: + wmi_unified_unregister_event_handler( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_ocb_get_tsf_timer_resp_event_id); +unreg_set_config: + wmi_unified_unregister_event(get_wmi_unified_hdl_from_psoc(psoc), + wmi_ocb_set_config_resp_event_id); + + return QDF_STATUS_E_FAILURE; +} + +QDF_STATUS +target_if_ocb_unregister_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + int rc; + + rc = wmi_unified_unregister_event_handler( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_dcc_stats_event_id); + if (rc) + target_if_err("Failed to unregister DCC stats event cb"); + + rc = wmi_unified_unregister_event_handler( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_dcc_update_ndl_resp_event_id); + if (rc) + target_if_err("Failed to unregister NDL update event cb"); + + rc = wmi_unified_unregister_event_handler( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_dcc_get_stats_resp_event_id); + if (rc) + target_if_err("Failed to unregister DCC get stats resp cb"); + + rc = wmi_unified_unregister_event_handler( + get_wmi_unified_hdl_from_psoc(psoc), + wmi_ocb_get_tsf_timer_resp_event_id); + if (rc) + target_if_err("Failed to unregister OCB TSF resp event cb"); + + rc = wmi_unified_unregister_event(get_wmi_unified_hdl_from_psoc(psoc), + wmi_ocb_set_config_resp_event_id); + if (rc) + target_if_err("Failed to unregister OCB config resp event cb"); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +target_if_ocb_register_tx_ops(struct wlan_ocb_tx_ops *ocb_txops) +{ + ocb_txops->ocb_set_config = target_if_ocb_set_config; + ocb_txops->ocb_set_utc_time = target_if_ocb_set_utc_time; + ocb_txops->ocb_start_timing_advert = target_if_ocb_start_timing_advert; + ocb_txops->ocb_stop_timing_advert = target_if_ocb_stop_timing_advert; + ocb_txops->ocb_get_tsf_timer = target_if_ocb_get_tsf_timer; + ocb_txops->ocb_dcc_get_stats = target_if_dcc_get_stats; + ocb_txops->ocb_dcc_clear_stats = target_if_dcc_clear_stats; + ocb_txops->ocb_dcc_update_ndl = target_if_dcc_update_ndl; + ocb_txops->ocb_reg_ev_handler = target_if_ocb_register_event_handler; + ocb_txops->ocb_unreg_ev_handler = + target_if_ocb_unregister_event_handler; + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/p2p/inc/target_if_p2p.h b/drivers/staging/qcacld-3.0/components/target_if/p2p/inc/target_if_p2p.h new file mode 100644 index 0000000000000000000000000000000000000000..1306e781554be7fe5cb28767fdfe9199565f4448 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/p2p/inc/target_if_p2p.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs for P2P + */ + +#ifndef _TARGET_IF_P2P_H_ +#define _TARGET_IF_P2P_H_ + +#include + +struct wlan_objmgr_psoc; +struct p2p_ps_config; +struct p2p_lo_start; + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD + +/** + * target_if_p2p_register_lo_event_handler() - Register lo event handler + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to register P2P listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_register_lo_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * target_if_p2p_unregister_lo_event_handler() - Unregister lo event handler + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to unregister P2P listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_unregister_lo_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * target_if_p2p_lo_start() - Start listen offload + * @psoc: soc object + * @lo_start: lo start information + * + * Target interface API to start listen offload. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_lo_start(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_start *lo_start); + +/** + * target_if_p2p_lo_stop() - Stop listen offload + * @psoc: soc object + * @vdev_id: vdev id + * + * Target interface API to stop listen offload. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_lo_stop(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id); +#endif + +/** + * target_if_p2p_register_tx_ops() - Register P2P component TX OPS + * @tx_ops: lmac if transmit ops + * + * Return: None + */ +void target_if_p2p_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops); + +/** + * target_if_p2p_register_noa_event_handler() - Register noa event handler + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to register P2P noa event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_register_noa_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * target_if_p2p_unregister_noa_event_handler() - Unregister noa event handler + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to unregister P2P listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_unregister_noa_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * target_if_p2p_set_ps() - Set power save + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to set power save. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_set_ps(struct wlan_objmgr_psoc *psoc, + struct p2p_ps_config *ps_config); + +/** + * target_if_p2p_set_noa() - Disable / Enable NOA + * @psoc: soc object + * @vdev_id: vdev id + * @disable_noa: TRUE - Disable NoA, FALSE - Enable NoA + * + * Target interface API to Disable / Enable P2P NOA. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_set_noa(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, bool disable_noa); + +#endif /* _TARGET_IF_P2P_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/target_if/p2p/src/target_if_p2p.c b/drivers/staging/qcacld-3.0/components/target_if/p2p/src/target_if_p2p.c new file mode 100644 index 0000000000000000000000000000000000000000..df39148aa095e6005d4f4454351d34ad42454569 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/p2p/src/target_if_p2p.c @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs definitions for P2P + */ + +#include +#include +#include "target_if.h" +#include "target_if_p2p.h" +#include "init_deinit_lmac.h" + +static inline struct wlan_lmac_if_p2p_rx_ops * +target_if_psoc_get_p2p_rx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &(psoc->soc_cb.rx_ops.p2p); +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +static inline void +target_if_p2p_lo_register_tx_ops(struct wlan_lmac_if_p2p_tx_ops *p2p_tx_ops) +{ + p2p_tx_ops->lo_start = target_if_p2p_lo_start; + p2p_tx_ops->lo_stop = target_if_p2p_lo_stop; + p2p_tx_ops->reg_lo_ev_handler = + target_if_p2p_register_lo_event_handler; + p2p_tx_ops->unreg_lo_ev_handler = + target_if_p2p_unregister_lo_event_handler; +} + +/** + * target_p2p_lo_event_handler() - WMI callback for lo stop event + * @scn: pointer to scn + * @event_buf: event buffer + * @len: buffer length + * + * This function gets called from WMI when triggered wmi event + * wmi_p2p_lo_stop_event_id. + * + * Return: 0 - success + * others - failure + */ +static int target_p2p_lo_event_handler(ol_scn_t scn, uint8_t *data, + uint32_t datalen) +{ + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct p2p_lo_event *event_info; + struct wlan_lmac_if_p2p_rx_ops *p2p_rx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", scn, data, datalen); + + if (!scn || !data) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, data); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi handle"); + return -EINVAL; + } + + event_info = qdf_mem_malloc(sizeof(*event_info)); + if (!event_info) { + target_if_err("Failed to allocate p2p lo event"); + return -ENOMEM; + } + + if (wmi_extract_p2p_lo_stop_ev_param(wmi_handle, data, + event_info)) { + target_if_err("Failed to extract wmi p2p lo stop event"); + qdf_mem_free(event_info); + return -EINVAL; + } + + p2p_rx_ops = target_if_psoc_get_p2p_rx_ops(psoc); + if (p2p_rx_ops->lo_ev_handler) { + status = p2p_rx_ops->lo_ev_handler(psoc, event_info); + target_if_debug("call lo event handler, status:%d", + status); + } else { + qdf_mem_free(event_info); + target_if_debug("no valid lo event handler"); + } + + return qdf_status_to_os_return(status); +} + +QDF_STATUS target_if_p2p_register_lo_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, arg:%pK", psoc, arg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_register_event(wmi_handle, + wmi_p2p_lo_stop_event_id, + target_p2p_lo_event_handler); + + target_if_debug("wmi register lo event handle, status:%d", status); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +QDF_STATUS target_if_p2p_unregister_lo_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, arg:%pK", psoc, arg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_unregister_event(wmi_handle, + wmi_p2p_lo_stop_event_id); + + target_if_debug("wmi unregister lo event handle, status:%d", status); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +QDF_STATUS target_if_p2p_lo_start(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_start *lo_start) +{ + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + if (!lo_start) { + target_if_err("lo start parameters is null"); + return QDF_STATUS_E_INVAL; + } + target_if_debug("psoc:%pK, vdev_id:%d", psoc, lo_start->vdev_id); + + return wmi_unified_p2p_lo_start_cmd(wmi_handle, lo_start); +} + +QDF_STATUS target_if_p2p_lo_stop(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id) +{ + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, vdev_id:%d", psoc, vdev_id); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_p2p_lo_stop_cmd(wmi_handle, + (uint8_t)vdev_id); +} +#else +static inline void +target_if_p2p_lo_register_tx_ops(struct wlan_lmac_if_p2p_tx_ops *p2p_tx_ops) +{ +} +#endif /* FEATURE_P2P_LISTEN_OFFLOAD */ + +/** + * target_p2p_noa_event_handler() - WMI callback for noa event + * @scn: pointer to scn + * @event_buf: event buffer + * @len: buffer length + * + * This function gets called from WMI when triggered WMI event + * wmi_p2p_noa_event_id. + * + * Return: 0 - success + * others - failure + */ +static int target_p2p_noa_event_handler(ol_scn_t scn, uint8_t *data, + uint32_t datalen) +{ + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct p2p_noa_info *event_info; + struct wlan_lmac_if_p2p_rx_ops *p2p_rx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", scn, data, datalen); + + if (!scn || !data) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, data); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi handle"); + return -EINVAL; + } + + event_info = qdf_mem_malloc(sizeof(*event_info)); + if (!event_info) { + target_if_err("failed to allocate p2p noa information"); + return -ENOMEM; + } + + if (wmi_extract_p2p_noa_ev_param(wmi_handle, data, + event_info)) { + target_if_err("failed to extract wmi p2p noa event"); + qdf_mem_free(event_info); + return -EINVAL; + } + + p2p_rx_ops = target_if_psoc_get_p2p_rx_ops(psoc); + if (p2p_rx_ops->noa_ev_handler) { + status = p2p_rx_ops->noa_ev_handler(psoc, event_info); + target_if_debug("call noa event handler, status:%d", + status); + } else { + qdf_mem_free(event_info); + target_if_debug("no valid noa event handler"); + } + + return qdf_status_to_os_return(status); +} + +QDF_STATUS target_if_p2p_register_noa_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, arg:%pK", psoc, arg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_register_event(wmi_handle, + wmi_p2p_noa_event_id, + target_p2p_noa_event_handler); + + target_if_debug("wmi register noa event handle, status:%d", + status); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +QDF_STATUS target_if_p2p_unregister_noa_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, arg:%pK", psoc, arg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_unregister_event(wmi_handle, + wmi_p2p_noa_event_id); + + target_if_debug("wmi unregister noa event handle, status:%d", + status); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +QDF_STATUS target_if_p2p_set_ps(struct wlan_objmgr_psoc *psoc, + struct p2p_ps_config *ps_config) +{ + struct p2p_ps_params cmd; + QDF_STATUS status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + if (!ps_config) { + target_if_err("ps config parameters is null"); + return QDF_STATUS_E_INVAL; + } + + target_if_debug("psoc:%pK, vdev_id:%d, opp_ps:%d", psoc, + ps_config->vdev_id, ps_config->opp_ps); + + cmd.opp_ps = ps_config->opp_ps; + cmd.ctwindow = ps_config->ct_window; + cmd.count = ps_config->count; + cmd.duration = ps_config->duration; + cmd.interval = ps_config->interval; + cmd.single_noa_duration = ps_config->single_noa_duration; + cmd.ps_selection = ps_config->ps_selection; + cmd.session_id = ps_config->vdev_id; + + if (ps_config->opp_ps) + status = wmi_unified_set_p2pgo_oppps_req(wmi_handle, + &cmd); + else + status = wmi_unified_set_p2pgo_noa_req_cmd(wmi_handle, + &cmd); + + if (status != QDF_STATUS_SUCCESS) + target_if_err("Failed to send set uapsd param, %d", + status); + + return status; +} + +QDF_STATUS target_if_p2p_set_noa(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, bool disable_noa) +{ + struct vdev_set_params param; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + target_if_debug("psoc:%pK, vdev_id:%d disable_noa:%d", + psoc, vdev_id, disable_noa); + param.vdev_id = vdev_id; + param.param_id = WMI_VDEV_PARAM_DISABLE_NOA_P2P_GO; + param.param_value = (uint32_t)disable_noa; + + return wmi_unified_vdev_set_param_send(wmi_handle, ¶m); +} + +static int target_p2p_mac_rx_filter_event_handler(ol_scn_t scn, uint8_t *data, + uint32_t datalen) +{ + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct p2p_set_mac_filter_evt event_info; + struct wlan_lmac_if_p2p_rx_ops *p2p_rx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!scn || !data) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, data); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi handle"); + return -EINVAL; + } + + if (wmi_extract_mac_addr_rx_filter_evt_param(wmi_handle, data, + &event_info)) { + target_if_err("failed to extract wmi p2p noa event"); + return -EINVAL; + } + target_if_debug("vdev_id %d status %d", event_info.vdev_id, + event_info.status); + p2p_rx_ops = target_if_psoc_get_p2p_rx_ops(psoc); + if (p2p_rx_ops && p2p_rx_ops->add_mac_addr_filter_evt_handler) + status = p2p_rx_ops->add_mac_addr_filter_evt_handler( + psoc, &event_info); + else + target_if_debug("no add mac addr filter event handler"); + + return qdf_status_to_os_return(status); +} + +static QDF_STATUS target_if_p2p_register_macaddr_rx_filter_evt_handler( + struct wlan_objmgr_psoc *psoc, bool reg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, register %d mac addr rx evt", psoc, reg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + if (reg) + status = wmi_unified_register_event( + wmi_handle, + wmi_vdev_add_macaddr_rx_filter_event_id, + target_p2p_mac_rx_filter_event_handler); + else + status = wmi_unified_unregister_event( + wmi_handle, + wmi_vdev_add_macaddr_rx_filter_event_id); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +static QDF_STATUS target_if_p2p_set_mac_addr_rx_filter_cmd( + struct wlan_objmgr_psoc *psoc, struct p2p_set_mac_filter *param) +{ + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_send_set_mac_addr_rx_filter_cmd(wmi_handle, param); +} + +void target_if_p2p_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_tx_ops; + + if (!tx_ops) { + target_if_err("lmac tx_ops is null"); + return; + } + + p2p_tx_ops = &tx_ops->p2p; + p2p_tx_ops->set_ps = target_if_p2p_set_ps; + p2p_tx_ops->set_noa = target_if_p2p_set_noa; + p2p_tx_ops->reg_noa_ev_handler = + target_if_p2p_register_noa_event_handler; + p2p_tx_ops->unreg_noa_ev_handler = + target_if_p2p_unregister_noa_event_handler; + p2p_tx_ops->reg_mac_addr_rx_filter_handler = + target_if_p2p_register_macaddr_rx_filter_evt_handler; + p2p_tx_ops->set_mac_addr_rx_filter_cmd = + target_if_p2p_set_mac_addr_rx_filter_cmd; + /* register P2P listen offload callbacks */ + target_if_p2p_lo_register_tx_ops(p2p_tx_ops); +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/inc/target_if_pmo.h b/drivers/staging/qcacld-3.0/components/target_if/pmo/inc/target_if_pmo.h new file mode 100644 index 0000000000000000000000000000000000000000..d2991ea42934b484dc5d393611da549242731799 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/inc/target_if_pmo.h @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Declare various api/struct which shall be used + * by pmo component for wmi cmd (tx path) and + * event (rx) handling. + */ + +#ifndef _TARGET_IF_PMO_H_ +#define _TARGET_IF_PMO_H_ + +#include "target_if.h" +#include "wlan_pmo_tgt_api.h" +#include "wlan_pmo_obj_mgmt_public_struct.h" + +/** + * target_if_pmo_enable_wow_wakeup_event() - Enable wow wakeup events. + * @vdev:objmgr vdev handle + * @bitmap: Event bitmap + * @enable: enable/disable + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_enable_wow_wakeup_event(struct wlan_objmgr_vdev *vdev, + uint32_t *bitmap); + +/** + * target_if_pmo_disable_wow_wakeup_event() - Disable wow wakeup events. + * @vdev:objmgr vdev handle + * @bitmap: Event bitmap + * @enable: enable/disable + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_disable_wow_wakeup_event( + struct wlan_objmgr_vdev *vdev, uint32_t *bitmap); + +/** + * target_if_pmo_send_wow_patterns_to_fw() - Sends WOW patterns to FW. + * @vdev: objmgr vdev handle + * @ptrn_id: pattern id + * @ptrn: pattern + * @ptrn_len: pattern length + * @ptrn_offset: pattern offset + * @mask: mask + * @mask_len: mask length + * @user: true for user configured pattern and false for default pattern + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_wow_patterns_to_fw(struct wlan_objmgr_vdev *vdev, + uint8_t ptrn_id, + const uint8_t *ptrn, uint8_t ptrn_len, + uint8_t ptrn_offset, const uint8_t *mask, + uint8_t mask_len, bool user); + +QDF_STATUS target_if_pmo_del_wow_patterns_to_fw(struct wlan_objmgr_vdev *vdev, + uint8_t ptrn_id); + +/** + * target_if_pmo_send_enhance_mc_offload_req() - send enhance mc offload req + * @vdev: objmgr vdev + * @action: enable or disable enhance multicast offload + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS target_if_pmo_send_enhance_mc_offload_req( + struct wlan_objmgr_vdev *vdev, + bool enable); + +/** + * target_if_pmo_set_mc_filter_req() - set mcast filter command to fw + * @vdev: objmgr vdev handle + * @multicastAddr: mcast address + * + * Return: 0 for success or error code + */ +QDF_STATUS target_if_pmo_set_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr); + +/** + * target_if_pmo_clear_mc_filter_req() - clear mcast filter command to fw + * @vdev: objmgr vdev handle + * @multicastAddr: mcast address + * + * Return: 0 for success or error code + */ +QDF_STATUS target_if_pmo_clear_mc_filter_req(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr); + +/** + * target_if_pmo_get_multiple_mc_filter_support() - get multiple mc filter + * request fw support + * @psoc: the psoc containing the vdev to configure + * + * Return: true if fw supports else false + */ +bool target_if_pmo_get_multiple_mc_filter_support( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_pmo_set_multiple_mc_filter_req() - set multiple mcast filter + * command to fw + * @vdev: objmgr vdev handle + * @multicastAddr: mcast address + * + * Return: 0 for success or error code + */ +QDF_STATUS target_if_pmo_set_multiple_mc_filter_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list); + +/** + * target_if_pmo_clear_multiple_mc_filter_req() - clear multiple mcast + * filter command to fw + * @vdev: objmgr vdev handle + * @multicastAddr: mcast address + * + * Return: 0 for success or error code + */ +QDF_STATUS target_if_pmo_clear_multiple_mc_filter_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list); + +/** + * target_if_pmo_send_ra_filter_req() - set RA filter pattern in fw + * @vdev: objmgr vdev handle + * @default_pattern: default pattern id + * @rate_limit_interval: ra rate limit interval + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_ra_filter_req(struct wlan_objmgr_vdev *vdev, + uint8_t default_pattern, uint16_t rate_limit_interval); + +/** + * target_if_pmo_send_action_frame_patterns() - register action frame map to fw + * @handle: Pointer to wma handle + * @vdev_id: VDEV ID + * + * This is called to push action frames wow patterns from local + * cache to firmware. + * + * Return: QDF_STATUS + */ +QDF_STATUS target_if_pmo_send_action_frame_patterns( + struct wlan_objmgr_vdev *vdev, + struct pmo_action_wakeup_set_params *ip_cmd); + +/** + * target_if_pmo_conf_hw_filter() - configure hardware filter in DTIM mode + * @psoc: the psoc containing the vdev to configure + * @req: the request parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS target_if_pmo_conf_hw_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_hw_filter_params *req); + +#ifdef WLAN_FEATURE_PACKET_FILTERING +/** + * target_if_pmo_send_pkt_filter_req() - enable packet filter + * @vdev: objmgr vdev + * @rcv_filter_param: filter params + * + * This function enable packet filter + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS target_if_pmo_send_pkt_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_cfg *rcv_filter_param); + +/** + * target_if_pmo_clear_pkt_filter_req() - disable packet filter + * @vdev: objmgr vdev + * @rcv_clear_param: filter params + * + * This function disable packet filter + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS target_if_pmo_clear_pkt_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_clear_param *rcv_clear_param); +#endif + +/** + * target_if_pmo_send_arp_offload_req() - sends arp request to fwr + * @vdev: objmgr vdev + * @arp_offload_req: arp offload req + * @ns_offload_req: ns offload request + * + * This functions sends arp request to fwr. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS target_if_pmo_send_arp_offload_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *arp_offload_req, + struct pmo_ns_offload_params *ns_offload_req); + +#ifdef WLAN_NS_OFFLOAD +/** + * target_if_pmo_send_ns_offload_req() - sends ns request to fwr + * @vdev: objmgr vdev + * @arp_offload_req: arp offload req + * @ns_offload_req: ns offload request + * + * This functions sends ns request to fwr. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS target_if_pmo_send_ns_offload_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *arp_offload_req, + struct pmo_ns_offload_params *ns_offload_req); +#else /* WLAN_NS_OFFLOAD */ +static inline QDF_STATUS +target_if_pmo_send_ns_offload_req(struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *arp_offload_req, + struct pmo_ns_offload_params *ns_offload_req) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_NS_OFFLOAD */ +/** + * target_if_pmo_send_gtk_offload_req() - send gtk offload request in fwr + * @vdev: objmgr vdev handle + * @gtk_offload_req: gtk offload request + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_gtk_offload_req(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_offload_req); + +/** + * target_if_pmo_send_gtk_response_req() - send gtk response request in fwr + * @vdev: objmgr vdev handle + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_gtk_response_req(struct wlan_objmgr_vdev *vdev); + +/** + * target_if_pmo_gtk_offload_status_event() - GTK offload status event handler + * @scn_handle: scn handle + * @event: event buffer + * @len: buffer length + * + * Return: 0 for success or error code + */ +int target_if_pmo_gtk_offload_status_event(void *scn_handle, + uint8_t *event, uint32_t len); + +/** + * target_if_pmo_send_lphb_enable() - enable command of LPHB config req + * @psoc: objmgr psoc handle + * @ts_lphb_enable: lphb enable request which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_lphb_enable(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_enable_req *ts_lphb_enable); + +/** + * target_if_pmo_send_lphb_tcp_params() - set lphb tcp params config request + * @psoc: objmgr psoc handle + * @ts_lphb_tcp_param: lphb tcp params which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_lphb_tcp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_params *ts_lphb_tcp_param); + +/** + * target_if_pmo_send_lphb_tcp_pkt_filter() - send lphb tcp packet filter req + * @psoc: objmgr psoc handle + * @ts_lphb_tcp_filter: lphb tcp filter request which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_lphb_tcp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_filter_req *ts_lphb_tcp_filter); + +/** + * target_if_pmo_send_lphb_udp_params() - Send udp param command of LPHB + * @psoc: objmgr psoc handle + * @ts_lphb_udp_param: lphb udp params which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_lphb_udp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_params *ts_lphb_udp_param); + +/** + * target_if_pmo_send_lphb_udp_pkt_filter() - Send lphb udp pkt filter cmd req + * @psoc: objmgr psoc handle + * @ts_lphb_udp_filter: lphb udp filter request which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_lphb_udp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_filter_req *ts_lphb_udp_filter); + +/** + * target_if_pmo_lphb_evt_handler() - send LPHB indication to os if /HDD + * @psoc: objmgr psoc handle + * @event: lphb event buffer + * + * Return: QDF_STATUS_SUCCESS for success else error code + */ +QDF_STATUS target_if_pmo_lphb_evt_handler(struct wlan_objmgr_psoc *psoc, + uint8_t *event); + +/** + * target_if_pmo_send_vdev_update_param_req() - Send vdev param value to fwr + * @vdev: objmgr vdev + * @param_id: tell vdev param id which needs to be updated in fwr + * @param_value: vdev parameter value + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_vdev_update_param_req( + struct wlan_objmgr_vdev *vdev, + uint32_t param_id, uint32_t param_value); + +/** + * target_if_pmo_send_vdev_ps_param_req() - Send vdev ps param value to fwr + * @vdev: objmgr vdev + * @param_id: tell vdev param id which needs to be updated in fwr + * @param_value: vdev parameter value + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_vdev_ps_param_req( + struct wlan_objmgr_vdev *vdev, + uint32_t param_id, + uint32_t param_value); + +/** + * target_if_pmo_psoc_update_bus_suspend() - update wmi bus suspend flag + * @psoc: objmgr psoc + * @value: bus suspend value + * + * Return: None + */ +void target_if_pmo_psoc_update_bus_suspend(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * target_if_pmo_psoc_get_host_credits() - get available host credits + * @psoc: objmgr psoc + * + * Return: return host credits + */ +int target_if_pmo_psoc_get_host_credits(struct wlan_objmgr_psoc *psoc); + +/** + * target_if_pmo_psoc_get_pending_cmnds() - get wmi pending commands + * @psoc: objmgr psoc + * + * Return: return wmi pending commands + */ +int target_if_pmo_psoc_get_pending_cmnds(struct wlan_objmgr_psoc *psoc); + +/** + * target_if_pmo_update_target_suspend_flag() - set wmi target suspend flag + * @psoc: objmgr psoc + * @value: value + * + * Return: return wmi pending commands + */ +void target_if_pmo_update_target_suspend_flag(struct wlan_objmgr_psoc *psoc, + uint8_t value); + +/** + * target_if_pmo_is_target_suspended() - get wmi target suspend flag + * @psoc: objmgr psoc + * + * Return: true if target suspended, false otherwise + */ +bool target_if_pmo_is_target_suspended(struct wlan_objmgr_psoc *psoc); + +/** + * target_if_pmo_psoc_send_wow_enable_req() -send wow enable request + * @psoc: objmgr psoc + * @param: wow command params + * + * Return: return QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS target_if_pmo_psoc_send_wow_enable_req(struct wlan_objmgr_psoc *psoc, + struct pmo_wow_cmd_params *param); + +/** + * target_if_pmo_psoc_send_suspend_req() - fp to send suspend request + * @psoc: objmgr psoc + * @param: target suspend params + * + * Return: return QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS target_if_pmo_psoc_send_suspend_req(struct wlan_objmgr_psoc *psoc, + struct pmo_suspend_params *param); + +/** + * target_if_pmo_set_runtime_pm_in_progress() - set runtime pm status + * @psoc: objmgr psoc + * @value: set runtime pm status + * + * Return: none + */ +void target_if_pmo_set_runtime_pm_in_progress(struct wlan_objmgr_psoc *psoc, + bool value); + +/** + * target_if_pmo_get_runtime_pm_in_progress() - fp to get runtime pm status + * @psoc: objmgr psoc + * + * Return: true if runtime pm in progress else false + */ +bool target_if_pmo_get_runtime_pm_in_progress(struct wlan_objmgr_psoc *psoc); + +/** + * target_if_pmo_psoc_send_host_wakeup_ind() - send host wake ind to fwr + * @psoc: objmgr psoc + * + * Return: return QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS target_if_pmo_psoc_send_host_wakeup_ind( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_pmo_psoc_send_target_resume_req() -send target resume request + * @psoc: objmgr psoc + * + * Return: return QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS target_if_pmo_psoc_send_target_resume_req( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_pmo_psoc_send_d0wow_enable_req() - send d0 wow enable request + * @psoc: objmgr psoc + * + * Return: return QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS target_if_pmo_psoc_send_d0wow_enable_req( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_pmo_psoc_send_d0wow_disable_req() - send d0 wow disable request + * @psoc: objmgr psoc + * + * Return: return QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS target_if_pmo_psoc_send_d0wow_disable_req( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_pmo_psoc_send_idle_monitor_cmd() - send screen status to firmware + * @psoc: objmgr psoc + * @val: Idle monitor value + * + * Return: QDF_STATUS_SUCCESS on success else error code + */ +QDF_STATUS +target_if_pmo_psoc_send_idle_monitor_cmd(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * target_if_pmo_register_tx_ops() - Register PMO component TX OPS + * @tx_ops: PMO if transmit ops + * + * Return: None + */ +void target_if_pmo_register_tx_ops(struct wlan_pmo_tx_ops *tx_ops); + +#endif + diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_arp.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_arp.c new file mode 100644 index 0000000000000000000000000000000000000000..c8e4aefe4c904c2d225dd2a672280c021c8e3617 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_arp.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_arp.c + * + * Target interface file for pmo component to + * send arp offload related cmd and process event. + */ + +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_api.h" + +QDF_STATUS target_if_pmo_send_arp_offload_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *arp_offload_req, + struct pmo_ns_offload_params *ns_offload_req) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_enable_arp_ns_offload_cmd(wmi_handle, + arp_offload_req, + ns_offload_req, + vdev_id); + if (status != QDF_STATUS_SUCCESS) + target_if_err("Failed to enable ARP NDP/NSffload"); + + return status; +} + + diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_gtk.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_gtk.c new file mode 100644 index 0000000000000000000000000000000000000000..d279984e2de1bea5acbbbc9c5d17d250d8b5269a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_gtk.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_gtk.c + * + * Target interface file for pmo component to + * send gtk offload related cmd and process event. + */ + +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_api.h" + +QDF_STATUS target_if_pmo_send_gtk_offload_req(struct wlan_objmgr_vdev *vdev, + struct pmo_gtk_req *gtk_req) +{ + uint8_t vdev_id; + QDF_STATUS status; + uint32_t gtk_offload_opcode; + struct wlan_objmgr_psoc *psoc; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + if (gtk_req->flags == PMO_GTK_OFFLOAD_ENABLE) + gtk_offload_opcode = GTK_OFFLOAD_ENABLE_OPCODE; + else + gtk_offload_opcode = GTK_OFFLOAD_DISABLE_OPCODE; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_send_gtk_offload_cmd(wmi_handle, + vdev_id, + gtk_req, + gtk_req->flags, + gtk_offload_opcode); + if (status) + target_if_err("Failed to send gtk offload cmd to fw"); + + return status; +} + +QDF_STATUS target_if_pmo_send_gtk_response_req(struct wlan_objmgr_vdev *vdev) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t offload_req_opcode; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + /* Request for GTK offload status */ + offload_req_opcode = GTK_OFFLOAD_REQUEST_STATUS_OPCODE; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + /* send the wmi command */ + status = wmi_unified_process_gtk_offload_getinfo_cmd(wmi_handle, + vdev_id, offload_req_opcode); + + return status; +} + +int target_if_pmo_gtk_offload_status_event(void *scn_handle, + uint8_t *event, uint32_t len) +{ + struct pmo_gtk_rsp_params *gtk_rsp_param; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS ret; + wmi_unified_t wmi_handle; + + TARGET_IF_ENTER(); + psoc = target_if_get_psoc_from_scn_hdl(scn_handle); + if (!psoc) { + target_if_err("psoc ptr is NULL"); + ret = -EINVAL; + goto out; + } + + gtk_rsp_param = qdf_mem_malloc(sizeof(*gtk_rsp_param)); + if (!gtk_rsp_param) { + target_if_err("memory allocation failed"); + ret = -ENOMEM; + goto out; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + qdf_mem_free(gtk_rsp_param); + ret = -EINVAL; + goto out; + } + + if (wmi_extract_gtk_rsp_event(wmi_handle, event, gtk_rsp_param, len) != + QDF_STATUS_SUCCESS) { + target_if_err("Extraction of gtk rsp event failed"); + qdf_mem_free(gtk_rsp_param); + ret = -EINVAL; + goto out; + } + + ret = pmo_tgt_gtk_rsp_evt(psoc, (void *)gtk_rsp_param); + if (ret != QDF_STATUS_SUCCESS) { + target_if_err("Failed to rx_gtk_rsp_event"); + ret = -EINVAL; + } + qdf_mem_free(gtk_rsp_param); +out: + TARGET_IF_EXIT(); + + return ret; +} + diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_hw_filter.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_hw_filter.c new file mode 100644 index 0000000000000000000000000000000000000000..d378f1cdc44f7f846a3eb5874420200b45c6ec59 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_hw_filter.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_non_arp_bcast_fltr.c + * + * Target interface file for pmo component to + * send non arp hw bcast filtering related cmd and process event. + */ + +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_api.h" +#include "wlan_pmo_hw_filter_public_struct.h" + +QDF_STATUS target_if_pmo_conf_hw_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_hw_filter_params *req) +{ + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!psoc) { + target_if_err("psoc is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_conf_hw_filter_cmd(wmi_handle, req); + + if (QDF_IS_STATUS_ERROR(status)) + target_if_err("Failed to configure HW Filter"); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_lphb.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_lphb.c new file mode 100644 index 0000000000000000000000000000000000000000..0657676bf341ac0742b042ce0dbe1ccb25913c7f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_lphb.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_lphb.c + * + * Target interface file for pmo component to + * send lphb offload related cmd and process event. + */ +#ifdef FEATURE_WLAN_LPHB + +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_pmo_api.h" + +QDF_STATUS target_if_pmo_send_lphb_enable(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_enable_req *ts_lphb_enable) +{ + wmi_hb_set_enable_cmd_fixed_param hb_enable_fp; + wmi_unified_t wmi_handle; + + if (!ts_lphb_enable) { + target_if_err("LPHB Enable configuration is NULL"); + return QDF_STATUS_E_FAILURE; + } + + target_if_info("PMO_HB_SET_ENABLE enable=%d, item=%d, session=%d", + ts_lphb_enable->enable, + ts_lphb_enable->item, ts_lphb_enable->session); + + if ((ts_lphb_enable->item != 1) && (ts_lphb_enable->item != 2)) { + target_if_err("LPHB configuration wrong item %d", + ts_lphb_enable->item); + return QDF_STATUS_E_FAILURE; + } + + /* fill in values */ + hb_enable_fp.vdev_id = ts_lphb_enable->session; + hb_enable_fp.enable = ts_lphb_enable->enable; + hb_enable_fp.item = ts_lphb_enable->item; + hb_enable_fp.session = ts_lphb_enable->session; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_lphb_config_hbenable_cmd(wmi_handle, &hb_enable_fp); +} + +QDF_STATUS target_if_pmo_send_lphb_tcp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_params *ts_lphb_tcp_param) +{ + wmi_hb_set_tcp_params_cmd_fixed_param hb_tcp_params_fp = {0}; + wmi_unified_t wmi_handle; + + if (!ts_lphb_tcp_param) { + target_if_err("TCP params LPHB configuration is NULL"); + return QDF_STATUS_E_FAILURE; + } + + target_if_info("PMO --> WMI_HB_SET_TCP_PARAMS srv_ip=%08x, " + "dev_ip=%08x, src_port=%d, dst_port=%d, timeout=%d, " + "session=%d, gateway_mac= "QDF_MAC_ADDR_FMT", time_period_sec=%d," + "tcp_sn=%d", ts_lphb_tcp_param->srv_ip, + ts_lphb_tcp_param->dev_ip, ts_lphb_tcp_param->src_port, + ts_lphb_tcp_param->dst_port, ts_lphb_tcp_param->timeout, + ts_lphb_tcp_param->session, + QDF_MAC_ADDR_REF(ts_lphb_tcp_param->gateway_mac.bytes), + ts_lphb_tcp_param->time_period_sec, ts_lphb_tcp_param->tcp_sn); + + /* fill in values */ + hb_tcp_params_fp.vdev_id = ts_lphb_tcp_param->session; + hb_tcp_params_fp.srv_ip = ts_lphb_tcp_param->srv_ip; + hb_tcp_params_fp.dev_ip = ts_lphb_tcp_param->dev_ip; + hb_tcp_params_fp.seq = ts_lphb_tcp_param->tcp_sn; + hb_tcp_params_fp.src_port = ts_lphb_tcp_param->src_port; + hb_tcp_params_fp.dst_port = ts_lphb_tcp_param->dst_port; + hb_tcp_params_fp.interval = ts_lphb_tcp_param->time_period_sec; + hb_tcp_params_fp.timeout = ts_lphb_tcp_param->timeout; + hb_tcp_params_fp.session = ts_lphb_tcp_param->session; + WMI_CHAR_ARRAY_TO_MAC_ADDR(ts_lphb_tcp_param->gateway_mac.bytes, + &hb_tcp_params_fp.gateway_mac); + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_lphb_config_tcp_params_cmd(wmi_handle, + &hb_tcp_params_fp); +} + +QDF_STATUS target_if_pmo_send_lphb_tcp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_tcp_filter_req *ts_lphb_tcp_filter) +{ + wmi_hb_set_tcp_pkt_filter_cmd_fixed_param hb_tcp_filter_fp = {0}; + wmi_unified_t wmi_handle; + + if (!ts_lphb_tcp_filter) { + target_if_err("TCP PKT FILTER LPHB configuration is NULL"); + return QDF_STATUS_E_FAILURE; + } + + target_if_info("SET_TCP_PKT_FILTER length=%d, offset=%d, session=%d, " + "filter=%2x:%2x:%2x:%2x:%2x:%2x ...", + ts_lphb_tcp_filter->length, ts_lphb_tcp_filter->offset, + ts_lphb_tcp_filter->session, ts_lphb_tcp_filter->filter[0], + ts_lphb_tcp_filter->filter[1], ts_lphb_tcp_filter->filter[2], + ts_lphb_tcp_filter->filter[3], ts_lphb_tcp_filter->filter[4], + ts_lphb_tcp_filter->filter[5]); + + /* fill in values */ + hb_tcp_filter_fp.vdev_id = ts_lphb_tcp_filter->session; + hb_tcp_filter_fp.length = ts_lphb_tcp_filter->length; + hb_tcp_filter_fp.offset = ts_lphb_tcp_filter->offset; + hb_tcp_filter_fp.session = ts_lphb_tcp_filter->session; + memcpy((void *)&hb_tcp_filter_fp.filter, + (void *)&ts_lphb_tcp_filter->filter, + WMI_WLAN_HB_MAX_FILTER_SIZE); + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_lphb_config_tcp_pkt_filter_cmd(wmi_handle, + &hb_tcp_filter_fp); +} + +QDF_STATUS target_if_pmo_send_lphb_udp_params(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_params *ts_lphb_udp_param) +{ + wmi_hb_set_udp_params_cmd_fixed_param hb_udp_params_fp = {0}; + wmi_unified_t wmi_handle; + + if (!ts_lphb_udp_param) { + target_if_err("UDP param for LPHB configuration is NULL"); + return QDF_STATUS_E_FAILURE; + } + + target_if_info("HB_SET_UDP_PARAMS srv_ip=%d, dev_ip=%d, src_port=%d, " + "dst_port=%d, interval=%d, timeout=%d, session=%d, " + "gateway_mac= "QDF_MAC_ADDR_FMT, + ts_lphb_udp_param->srv_ip, ts_lphb_udp_param->dev_ip, + ts_lphb_udp_param->src_port, ts_lphb_udp_param->dst_port, + ts_lphb_udp_param->interval, ts_lphb_udp_param->timeout, + ts_lphb_udp_param->session, + QDF_MAC_ADDR_REF(ts_lphb_udp_param->gateway_mac.bytes)); + + /* fill in values */ + hb_udp_params_fp.vdev_id = ts_lphb_udp_param->session; + hb_udp_params_fp.srv_ip = ts_lphb_udp_param->srv_ip; + hb_udp_params_fp.dev_ip = ts_lphb_udp_param->dev_ip; + hb_udp_params_fp.src_port = ts_lphb_udp_param->src_port; + hb_udp_params_fp.dst_port = ts_lphb_udp_param->dst_port; + hb_udp_params_fp.interval = ts_lphb_udp_param->interval; + hb_udp_params_fp.timeout = ts_lphb_udp_param->timeout; + hb_udp_params_fp.session = ts_lphb_udp_param->session; + WMI_CHAR_ARRAY_TO_MAC_ADDR(ts_lphb_udp_param->gateway_mac.bytes, + &hb_udp_params_fp.gateway_mac); + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_lphb_config_udp_params_cmd(wmi_handle, + &hb_udp_params_fp); +} + +/** + * target_if_pmo_lphb_send_udp_pkt_filter() - Send LPHB udp pkt filter req + * @psoc: objmgr psoc handle + * @ts_lphb_udp_filter: lphb udp filter request which needs to configure in fwr + * + * Return: QDF status + */ +QDF_STATUS target_if_pmo_send_lphb_udp_pkt_filter(struct wlan_objmgr_psoc *psoc, + struct pmo_lphb_udp_filter_req *ts_lphb_udp_filter) +{ + wmi_hb_set_udp_pkt_filter_cmd_fixed_param hb_udp_filter_fp = {0}; + wmi_unified_t wmi_handle; + + if (!ts_lphb_udp_filter) { + target_if_err("LPHB UDP packet filter configuration is NULL"); + return QDF_STATUS_E_FAILURE; + } + + target_if_info("SET_UDP_PKT_FILTER length=%d, offset=%d, session=%d, " + "filter=%2x:%2x:%2x:%2x:%2x:%2x ...", + ts_lphb_udp_filter->length, ts_lphb_udp_filter->offset, + ts_lphb_udp_filter->session, ts_lphb_udp_filter->filter[0], + ts_lphb_udp_filter->filter[1], ts_lphb_udp_filter->filter[2], + ts_lphb_udp_filter->filter[3], ts_lphb_udp_filter->filter[4], + ts_lphb_udp_filter->filter[5]); + + /* fill in values */ + hb_udp_filter_fp.vdev_id = ts_lphb_udp_filter->session; + hb_udp_filter_fp.length = ts_lphb_udp_filter->length; + hb_udp_filter_fp.offset = ts_lphb_udp_filter->offset; + hb_udp_filter_fp.session = ts_lphb_udp_filter->session; + qdf_mem_copy(&hb_udp_filter_fp.filter, &ts_lphb_udp_filter->filter, + WMI_WLAN_HB_MAX_FILTER_SIZE); + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_lphb_config_udp_pkt_filter_cmd(wmi_handle, + &hb_udp_filter_fp); +} + +QDF_STATUS target_if_pmo_lphb_evt_handler(struct wlan_objmgr_psoc *psoc, + uint8_t *event) +{ + wmi_hb_ind_event_fixed_param *hb_fp; + struct pmo_lphb_rsp *slphb_indication = NULL; + QDF_STATUS qdf_status; + + TARGET_IF_ENTER(); + if (!psoc) { + target_if_err("psoc ptr is NULL"); + qdf_status = QDF_STATUS_E_NULL_VALUE; + goto out; + } + + hb_fp = (wmi_hb_ind_event_fixed_param *) event; + if (!hb_fp) { + target_if_err("Invalid wmi_hb_ind_event_fixed_param buffer"); + qdf_status = QDF_STATUS_E_INVAL; + goto out; + } + + target_if_debug("lphb indication received with\n" + "vdev_id=%d, session=%d, reason=%d", + hb_fp->vdev_id, hb_fp->session, hb_fp->reason); + + slphb_indication = (struct pmo_lphb_rsp *)qdf_mem_malloc( + sizeof(struct pmo_lphb_rsp)); + + if (!slphb_indication) { + target_if_err("Invalid LPHB indication buffer"); + qdf_status = QDF_STATUS_E_NOMEM; + goto out; + } + + slphb_indication->session_idx = hb_fp->session; + slphb_indication->protocol_type = hb_fp->reason; + slphb_indication->event_reason = hb_fp->reason; + + qdf_status = pmo_tgt_lphb_rsp_evt(psoc, slphb_indication); + if (qdf_status != QDF_STATUS_SUCCESS) + target_if_err("Failed to lphb_rsp_event"); +out: + if (slphb_indication) + qdf_mem_free(slphb_indication); + + return qdf_status; +} +#endif + diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_main.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_main.c new file mode 100644 index 0000000000000000000000000000000000000000..c769ce46828cd606c091c966355319fb9d22f28e --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_main.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: Target interface file for pmo component to + * Implement api's which shall be used by pmo component + * in target if internally. + */ + +#include "target_if_pmo.h" +#include "wlan_pmo_common_public_struct.h" + +#ifdef WLAN_FEATURE_PACKET_FILTERING +static inline +void tgt_if_pmo_reg_pkt_filter_ops(struct wlan_pmo_tx_ops *pmo_tx_ops) +{ + pmo_tx_ops->send_set_pkt_filter = + target_if_pmo_send_pkt_filter_req; + pmo_tx_ops->send_clear_pkt_filter = + target_if_pmo_clear_pkt_filter_req; +} +#else +static inline +void tgt_if_pmo_reg_pkt_filter_ops(struct wlan_pmo_tx_ops *pmo_tx_ops) +{ +} +#endif + +void target_if_pmo_register_tx_ops(struct wlan_pmo_tx_ops *pmo_tx_ops) +{ + if (!pmo_tx_ops) { + target_if_err("pmo_tx_ops is null"); + return; + } + + pmo_tx_ops->send_arp_offload_req = + target_if_pmo_send_arp_offload_req; + pmo_tx_ops->send_conf_hw_filter_req = + target_if_pmo_conf_hw_filter; + pmo_tx_ops->send_ns_offload_req = + target_if_pmo_send_ns_offload_req; + pmo_tx_ops->send_enable_wow_wakeup_event_req = + target_if_pmo_enable_wow_wakeup_event; + pmo_tx_ops->send_disable_wow_wakeup_event_req = + target_if_pmo_disable_wow_wakeup_event; + pmo_tx_ops->send_add_wow_pattern = + target_if_pmo_send_wow_patterns_to_fw; + pmo_tx_ops->del_wow_pattern = + target_if_pmo_del_wow_patterns_to_fw; + pmo_tx_ops->send_enhance_mc_offload_req = + target_if_pmo_send_enhance_mc_offload_req; + pmo_tx_ops->send_set_mc_filter_req = + target_if_pmo_set_mc_filter_req; + pmo_tx_ops->send_clear_mc_filter_req = + target_if_pmo_clear_mc_filter_req; + pmo_tx_ops->get_multiple_mc_filter_support = + target_if_pmo_get_multiple_mc_filter_support; + pmo_tx_ops->send_set_multiple_mc_filter_req = + target_if_pmo_set_multiple_mc_filter_req; + pmo_tx_ops->send_clear_multiple_mc_filter_req = + target_if_pmo_clear_multiple_mc_filter_req; + pmo_tx_ops->send_ra_filter_req = + target_if_pmo_send_ra_filter_req; + pmo_tx_ops->send_gtk_offload_req = + target_if_pmo_send_gtk_offload_req; + pmo_tx_ops->send_get_gtk_rsp_cmd = + target_if_pmo_send_gtk_response_req; + pmo_tx_ops->send_action_frame_pattern_req = + target_if_pmo_send_action_frame_patterns; + pmo_tx_ops->send_lphb_enable = + target_if_pmo_send_lphb_enable; + pmo_tx_ops->send_lphb_tcp_params = + target_if_pmo_send_lphb_tcp_params; + pmo_tx_ops->send_lphb_tcp_filter_req = + target_if_pmo_send_lphb_tcp_pkt_filter; + pmo_tx_ops->send_lphb_upd_params = + target_if_pmo_send_lphb_udp_params; + pmo_tx_ops->send_lphb_udp_filter_req = + target_if_pmo_send_lphb_udp_pkt_filter; + pmo_tx_ops->send_vdev_param_update_req = + target_if_pmo_send_vdev_update_param_req; + pmo_tx_ops->send_vdev_sta_ps_param_req = + target_if_pmo_send_vdev_ps_param_req; + pmo_tx_ops->psoc_update_wow_bus_suspend = + target_if_pmo_psoc_update_bus_suspend; + pmo_tx_ops->psoc_get_host_credits = + target_if_pmo_psoc_get_host_credits; + pmo_tx_ops->psoc_get_pending_cmnds = + target_if_pmo_psoc_get_pending_cmnds; + pmo_tx_ops->update_target_suspend_flag = + target_if_pmo_update_target_suspend_flag; + pmo_tx_ops->is_target_suspended = + target_if_pmo_is_target_suspended; + pmo_tx_ops->psoc_send_wow_enable_req = + target_if_pmo_psoc_send_wow_enable_req; + pmo_tx_ops->psoc_send_supend_req = + target_if_pmo_psoc_send_suspend_req; + pmo_tx_ops->psoc_set_runtime_pm_in_progress = + target_if_pmo_set_runtime_pm_in_progress; + pmo_tx_ops->psoc_get_runtime_pm_in_progress = + target_if_pmo_get_runtime_pm_in_progress; + pmo_tx_ops->psoc_send_host_wakeup_ind = + target_if_pmo_psoc_send_host_wakeup_ind; + pmo_tx_ops->psoc_send_target_resume_req = + target_if_pmo_psoc_send_target_resume_req; + pmo_tx_ops->psoc_send_d0wow_enable_req = + target_if_pmo_psoc_send_d0wow_enable_req; + pmo_tx_ops->psoc_send_d0wow_disable_req = + target_if_pmo_psoc_send_d0wow_disable_req; + pmo_tx_ops->psoc_send_idle_roam_suspend_mode = + target_if_pmo_psoc_send_idle_monitor_cmd; + tgt_if_pmo_reg_pkt_filter_ops(pmo_tx_ops); +} + diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_mc_addr_filtering.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_mc_addr_filtering.c new file mode 100644 index 0000000000000000000000000000000000000000..8d0f768856cffac7996f4dc8fa94a4f61bbeabb3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_mc_addr_filtering.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_mc_addr_filtering.c + * + * Target interface file for pmo component to + * send mc address filtering offload related cmd and process event. + */ + + +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_api.h" + +QDF_STATUS target_if_pmo_set_mc_filter_req( + struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_add_clear_mcbc_filter_cmd(wmi_handle, vdev_id, + multicast_addr, false); + if (status) + target_if_err("Failed to send add/clear mcbc filter cmd"); + + return status; +} + +QDF_STATUS target_if_pmo_clear_mc_filter_req( + struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr multicast_addr) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_add_clear_mcbc_filter_cmd(wmi_handle, vdev_id, + multicast_addr, true); + if (status) + target_if_err("Failed to send add/clear mcbc filter cmd"); + + return status; + +} + +bool target_if_pmo_get_multiple_mc_filter_support( + struct wlan_objmgr_psoc *psoc) +{ + wmi_unified_t wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return false; + } + + return wmi_service_enabled(wmi_handle, + wmi_service_multiple_mcast_filter_set); +} + +QDF_STATUS target_if_pmo_set_multiple_mc_filter_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + struct pmo_mcast_filter_params filter_params; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + filter_params.multicast_addr_cnt = mc_list->mc_cnt; + qdf_mem_copy(filter_params.multicast_addr, + mc_list->mc_addr, + mc_list->mc_cnt * ATH_MAC_LEN); + /* add one/multiple mc list */ + filter_params.action = 1; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_multiple_add_clear_mcbc_filter_cmd(wmi_handle, + vdev_id, + &filter_params); + if (status) + target_if_err("Failed to send add/clear mcbc filter cmd"); + + return status; +} + +QDF_STATUS target_if_pmo_clear_multiple_mc_filter_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_mc_addr_list *mc_list) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + struct pmo_mcast_filter_params filter_params; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + filter_params.multicast_addr_cnt = mc_list->mc_cnt; + qdf_mem_copy(filter_params.multicast_addr, + mc_list->mc_addr, + mc_list->mc_cnt * ATH_MAC_LEN); + /* delete one/multiple mc list */ + filter_params.action = 0; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_multiple_add_clear_mcbc_filter_cmd(wmi_handle, + vdev_id, + &filter_params); + if (status) + target_if_err("Failed to send add/clear mcbc filter cmd"); + + return status; +} + + diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_ns.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_ns.c new file mode 100644 index 0000000000000000000000000000000000000000..0f23bb1905d7dac3cb26aee854feb9ab50c32053 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_ns.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_ns.c + * + * Target interface file for pmo component to + * send ns offload related cmd and process event. + */ + +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_api.h" + +QDF_STATUS target_if_pmo_send_ns_offload_req( + struct wlan_objmgr_vdev *vdev, + struct pmo_arp_offload_params *arp_offload_req, + struct pmo_ns_offload_params *ns_offload_req) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_enable_arp_ns_offload_cmd(wmi_handle, + arp_offload_req, + ns_offload_req, + vdev_id); + if (status != QDF_STATUS_SUCCESS) + target_if_err("Failed to enable ARP NDP/NSffload"); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_pkt_filter.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_pkt_filter.c new file mode 100644 index 0000000000000000000000000000000000000000..85725b936de2094e7a02cd8df61366ed771cf4a5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_pkt_filter.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_pkt_filter.c + * + * Target interface file for pmo component to + * send packet filter related cmd and process event. + */ + +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_pmo_api.h" +#include "wlan_pmo_pkt_filter_public_struct.h" + +QDF_STATUS target_if_pmo_send_pkt_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_cfg *rcv_filter_param) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + /* send the command along with data */ + status = wmi_unified_config_packet_filter_cmd(wmi_handle, vdev_id, + rcv_filter_param, + rcv_filter_param->filter_id, true); + if (status) { + target_if_err("Failed to send pkt_filter cmd"); + return QDF_STATUS_E_INVAL; + } + + /* Enable packet filter */ + status = wmi_unified_enable_disable_packet_filter_cmd(wmi_handle, + vdev_id, true); + if (status) + target_if_err("Failed to send packet filter wmi cmd to fw"); + + return status; +} + +QDF_STATUS target_if_pmo_clear_pkt_filter_req(struct wlan_objmgr_vdev *vdev, + struct pmo_rcv_pkt_fltr_clear_param *rcv_clear_param) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + /* send the command along with data */ + status = wmi_unified_config_packet_filter_cmd(wmi_handle, vdev_id, NULL, + rcv_clear_param->filter_id, false); + + if (status) + target_if_err("Failed to clear filter cmd"); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_static_config.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_static_config.c new file mode 100644 index 0000000000000000000000000000000000000000..79d11386ab0bd63b1b4372695f9484c3d2fc950f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_static_config.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_static.c + * + * Target interface file for pmo component to + * send wow related cmd and process event. + */ + + +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_api.h" + +QDF_STATUS target_if_pmo_send_ra_filter_req(struct wlan_objmgr_vdev *vdev, + uint8_t default_pattern, uint16_t rate_limit_interval) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_wow_sta_ra_filter_cmd(wmi_handle, + vdev_id, + default_pattern, + rate_limit_interval); + if (status) + target_if_err("Failed to send RA rate limit to fw"); + + return status; +} + +QDF_STATUS target_if_pmo_send_action_frame_patterns( + struct wlan_objmgr_vdev *vdev, + struct pmo_action_wakeup_set_params *ip_cmd) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_action_frame_patterns_cmd(wmi_handle, ip_cmd); + if (status != QDF_STATUS_SUCCESS) + target_if_err("Failed to config wow action frame map, ret %d", + status); + + return status; +} + +QDF_STATUS target_if_pmo_send_enhance_mc_offload_req( + struct wlan_objmgr_vdev *vdev, bool enable) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_enable_enhance_multicast_offload_cmd(wmi_handle, + vdev_id, + enable); + if (status) + target_if_err("Failed to config wow wakeup event"); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_suspend_resume.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_suspend_resume.c new file mode 100644 index 0000000000000000000000000000000000000000..a1fc8a76c7c302bf22cab5954f1d09fbc3eea884 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_suspend_resume.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_static.c + * + * Target interface file for pmo component to + * send suspend / resume related cmd and process event. + */ + +#include "wma.h" +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_api.h" + +#define TGT_WILDCARD_PDEV_ID 0x0 + +QDF_STATUS target_if_pmo_send_vdev_update_param_req( + struct wlan_objmgr_vdev *vdev, + uint32_t param_id, uint32_t param_value) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + struct vdev_set_params param = {0}; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + /* Any new param_id added here please also add it to + * wmi_tag_vdev_set_cmd to be tagged for runtime PM feature + * so that it will not invoke runtime PM "get" which will + * result resume right after suspend (WOW_ENABLE). + */ + + switch (param_id) { + case pmo_vdev_param_listen_interval: + param_id = WMI_VDEV_PARAM_LISTEN_INTERVAL; + break; + case pmo_vdev_param_dtim_policy: + param_id = WMI_VDEV_PARAM_DTIM_POLICY; + break; + default: + target_if_err("invalid vdev param id %d", param_id); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + param.vdev_id = vdev_id; + param.param_id = param_id; + param.param_value = param_value; + target_if_debug("set vdev param vdev_id: %d value: %d for param_id: %d", + vdev_id, param_value, param_id); + return wmi_unified_vdev_set_param_send(wmi_handle, ¶m); +} + +QDF_STATUS target_if_pmo_send_vdev_ps_param_req( + struct wlan_objmgr_vdev *vdev, + uint32_t param_id, + uint32_t param_value) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + struct sta_ps_params sta_ps_param = {0}; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + /* + * Any new param_id added here must be added to + * wmi_tag_sta_powersave_cmd() to be tagged for runtime PM feature + * so that it will not invoke runtime PM "get" which will + * result resume right after suspend (WOW_ENABLE). + */ + switch (param_id) { + case pmo_sta_ps_enable_advanced_power: + param_id = WMI_STA_PS_ENABLE_QPOWER; + break; + case pmo_sta_ps_param_inactivity_time: + param_id = WMI_STA_PS_PARAM_INACTIVITY_TIME; + break; + case pmo_sta_ps_param_ito_repeat_count: + param_id = WMI_STA_PS_PARAM_MAX_RESET_ITO_COUNT_ON_TIM_NO_TXRX; + break; + default: + target_if_err("invalid vdev param id %d", param_id); + return QDF_STATUS_E_INVAL; + } + + sta_ps_param.vdev_id = vdev_id; + sta_ps_param.param_id = param_id; + sta_ps_param.value = param_value; + target_if_debug("set vdev param vdev_id: %d value: %d for param_id: %d", + vdev_id, param_value, param_id); + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_sta_ps_cmd_send(wmi_handle, &sta_ps_param); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + return status; +} + +void target_if_pmo_psoc_update_bus_suspend(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return; + } + + wmi_set_is_wow_bus_suspended(wmi_handle, value); +} + +int target_if_pmo_psoc_get_host_credits(struct wlan_objmgr_psoc *psoc) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return 0; + } + + return wmi_get_host_credits(wmi_handle); +} + +int target_if_pmo_psoc_get_pending_cmnds(struct wlan_objmgr_psoc *psoc) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return 0; + } + + return wmi_get_pending_cmds(wmi_handle); +} + +void target_if_pmo_update_target_suspend_flag(struct wlan_objmgr_psoc *psoc, + uint8_t value) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return; + } + + wmi_set_target_suspend(wmi_handle, value); +} + +bool target_if_pmo_is_target_suspended(struct wlan_objmgr_psoc *psoc) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return false; + } + + return wmi_is_target_suspended(wmi_handle); +} + +QDF_STATUS target_if_pmo_psoc_send_wow_enable_req( + struct wlan_objmgr_psoc *psoc, + struct pmo_wow_cmd_params *param) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + wma_check_and_set_wake_timer(SIR_INSTALL_KEY_TIMEOUT_MS); + return wmi_unified_wow_enable_send(wmi_handle, + (struct wow_cmd_params *)param, + TGT_WILDCARD_PDEV_ID); +} + +QDF_STATUS target_if_pmo_psoc_send_suspend_req( + struct wlan_objmgr_psoc *psoc, + struct pmo_suspend_params *param) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_suspend_send(wmi_handle, + (struct suspend_params *) param, + TGT_WILDCARD_PDEV_ID); +} + +void target_if_pmo_set_runtime_pm_in_progress(struct wlan_objmgr_psoc *psoc, + bool value) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return; + } + + return wmi_set_runtime_pm_inprogress(wmi_handle, value); +} + +bool target_if_pmo_get_runtime_pm_in_progress( + struct wlan_objmgr_psoc *psoc) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return false; + } + + return wmi_get_runtime_pm_inprogress(wmi_handle); +} + +QDF_STATUS target_if_pmo_psoc_send_host_wakeup_ind( + struct wlan_objmgr_psoc *psoc) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_host_wakeup_ind_to_fw_cmd(wmi_handle); +} + +QDF_STATUS target_if_pmo_psoc_send_target_resume_req( + struct wlan_objmgr_psoc *psoc) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_resume_send(wmi_handle, TGT_WILDCARD_PDEV_ID); +} + +QDF_STATUS +target_if_pmo_psoc_send_idle_monitor_cmd(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_send_idle_trigger_monitor(wmi_handle, val); +} + +#ifdef FEATURE_WLAN_D0WOW +QDF_STATUS target_if_pmo_psoc_send_d0wow_enable_req( + struct wlan_objmgr_psoc *psoc) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_d0wow_enable_send(wmi_handle, TGT_WILDCARD_PDEV_ID); +} + +QDF_STATUS target_if_pmo_psoc_send_d0wow_disable_req( + struct wlan_objmgr_psoc *psoc) +{ + wmi_unified_t wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_d0wow_disable_send(wmi_handle, TGT_WILDCARD_PDEV_ID); +} +#else +QDF_STATUS target_if_pmo_psoc_send_d0wow_enable_req( + struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_E_INVAL; +} + +QDF_STATUS target_if_pmo_psoc_send_d0wow_disable_req( + struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_E_INVAL; +} +#endif diff --git a/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_wow.c b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_wow.c new file mode 100644 index 0000000000000000000000000000000000000000..6c2481eeb8fc5409e70ec128a39af69e04328625 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/pmo/src/target_if_pmo_wow.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/** + * DOC: target_if_pmo_wow.c + * + * Target interface file for pmo component to + * send wow related cmd and process event. + */ + + +#include "target_if.h" +#include "target_if_pmo.h" +#include "wmi_unified_api.h" + +QDF_STATUS target_if_pmo_enable_wow_wakeup_event(struct wlan_objmgr_vdev *vdev, + uint32_t *bitmap) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_add_wow_wakeup_event_cmd(wmi_handle, vdev_id, + bitmap, true); + if (status) + target_if_err("Failed to config wow wakeup event"); + + return status; +} + +QDF_STATUS target_if_pmo_disable_wow_wakeup_event(struct wlan_objmgr_vdev *vdev, + uint32_t *bitmap) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_add_wow_wakeup_event_cmd(wmi_handle, vdev_id, + bitmap, false); + if (status) + target_if_err("Failed to config wow wakeup event"); + + return status; +} + +QDF_STATUS target_if_pmo_send_wow_patterns_to_fw(struct wlan_objmgr_vdev *vdev, + uint8_t ptrn_id, + const uint8_t *ptrn, uint8_t ptrn_len, + uint8_t ptrn_offset, const uint8_t *mask, + uint8_t mask_len, bool user) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_wow_patterns_to_fw_cmd(wmi_handle, + vdev_id, ptrn_id, ptrn, + ptrn_len, ptrn_offset, + mask, mask_len, user, 0); + + return status; +} + +QDF_STATUS target_if_pmo_del_wow_patterns_to_fw(struct wlan_objmgr_vdev *vdev, + uint8_t ptrn_id) +{ + uint8_t vdev_id; + struct wlan_objmgr_psoc *psoc; + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!vdev) { + target_if_err("vdev ptr passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + target_if_err("psoc handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_wow_delete_pattern_cmd(wmi_handle, ptrn_id, + vdev_id); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/components/target_if/tdls/inc/target_if_tdls.h b/drivers/staging/qcacld-3.0/components/target_if/tdls/inc/target_if_tdls.h new file mode 100644 index 0000000000000000000000000000000000000000..be52c1ebb53e21acacf09fdcd741ddce33743df3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/tdls/inc/target_if_tdls.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs for tdls + * + */ + +#ifndef __TARGET_IF_TDLS_H__ +#define __TARGET_IF_TDLS_H__ + +struct tdls_info; +struct wlan_objmgr_psoc; +struct tdls_peer_update_state; +struct tdls_channel_switch_params; +struct sta_uapsd_trig_params; + +/** + * target_if_tdls_update_fw_state() - lmac handler to update tdls fw state + * @psoc: psoc object + * @param: tdls state parameter + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_update_fw_state(struct wlan_objmgr_psoc *psoc, + struct tdls_info *param); + +/** + * target_if_tdls_update_peer_state() - lmac handler to update tdls peer state + * @psoc: psoc object + * @peer_params: tdls peer state params + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_update_peer_state(struct wlan_objmgr_psoc *psoc, + struct tdls_peer_update_state *peer_params); + +/** + * target_if_tdls_set_offchan_mode() - lmac handler to set tdls off channel mode + * @psoc: psoc object + * @params: tdls channel swithc params + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *params); + +/** + * target_if_tdls_register_event_handler() - lmac handler to register tdls event + * handler + * @psoc : psoc object + * @arg: argument passed to lmac + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_register_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * target_if_tdls_unregister_event_handler() - lmac handler to unregister tdls + * event handler + * @psoc : psoc object + * @arg: argument passed to lmac + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_unregister_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * target_if_tdls_register_tx_ops() - lmac handler to register tdls tx ops + * callback functions + * @tx_ops: wlan_lmac_if_tx_ops object + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops); +#endif diff --git a/drivers/staging/qcacld-3.0/components/target_if/tdls/src/target_if_tdls.c b/drivers/staging/qcacld-3.0/components/target_if/tdls/src/target_if_tdls.c new file mode 100644 index 0000000000000000000000000000000000000000..9ecbdc5632767327359cbf3d8b4898f84ee4ae26 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/target_if/tdls/src/target_if_tdls.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs for tdls + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline struct wlan_lmac_if_tdls_rx_ops * +target_if_tdls_get_rx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &psoc->soc_cb.rx_ops.tdls_rx_ops; +} + +static int +target_if_tdls_event_handler(ol_scn_t scn, uint8_t *data, uint32_t datalen) +{ + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct wlan_lmac_if_tdls_rx_ops *tdls_rx_ops; + struct tdls_event_info info; + QDF_STATUS status; + + if (!scn || !data) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, data); + return -EINVAL; + } + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + + if (!wmi_handle) { + target_if_err("null wmi_handle"); + return -EINVAL; + } + + if (wmi_extract_vdev_tdls_ev_param(wmi_handle, data, &info)) { + target_if_err("Failed to extract wmi tdls event"); + return -EINVAL; + } + + tdls_rx_ops = target_if_tdls_get_rx_ops(psoc); + if (tdls_rx_ops && tdls_rx_ops->tdls_ev_handler) { + status = tdls_rx_ops->tdls_ev_handler(psoc, &info); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("fail to handle tdls event"); + return -EINVAL; + } + } + + return 0; +} + +QDF_STATUS +target_if_tdls_update_fw_state(struct wlan_objmgr_psoc *psoc, + struct tdls_info *param) +{ + QDF_STATUS status; + enum wmi_tdls_state tdls_state; + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid WMI handle"); + return QDF_STATUS_E_FAILURE; + } + + if (TDLS_SUPPORT_EXP_TRIG_ONLY == param->tdls_state) + tdls_state = WMI_TDLS_ENABLE_PASSIVE; + else if (TDLS_SUPPORT_IMP_MODE == param->tdls_state || + TDLS_SUPPORT_EXT_CONTROL == param->tdls_state) + tdls_state = WMI_TDLS_ENABLE_CONNECTION_TRACKER_IN_HOST; + else + tdls_state = WMI_TDLS_DISABLE; + + status = wmi_unified_update_fw_tdls_state_cmd(wmi_handle, + param, tdls_state); + + target_if_debug("vdev_id %d", param->vdev_id); + return status; +} + +QDF_STATUS +target_if_tdls_update_peer_state(struct wlan_objmgr_psoc *psoc, + struct tdls_peer_update_state *peer_params) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +target_if_tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *params) +{ + QDF_STATUS status; + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid WMI handle"); + return QDF_STATUS_E_FAILURE; + } + status = wmi_unified_set_tdls_offchan_mode_cmd(wmi_handle, + params); + + return status; +} + +QDF_STATUS +target_if_tdls_register_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi_handle"); + return QDF_STATUS_E_INVAL; + } + return wmi_unified_register_event(wmi_handle, + wmi_tdls_peer_event_id, + target_if_tdls_event_handler); +} + +QDF_STATUS +target_if_tdls_unregister_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi_handle"); + return QDF_STATUS_E_INVAL; + } + return wmi_unified_unregister_event(wmi_handle, + wmi_tdls_peer_event_id); +} + +QDF_STATUS +target_if_tdls_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_txops; + + tdls_txops = &tx_ops->tdls_tx_ops; + + tdls_txops->update_fw_state = target_if_tdls_update_fw_state; + tdls_txops->update_peer_state = target_if_tdls_update_peer_state; + tdls_txops->set_offchan_mode = target_if_tdls_set_offchan_mode; + tdls_txops->tdls_reg_ev_handler = target_if_tdls_register_event_handler; + tdls_txops->tdls_unreg_ev_handler = + target_if_tdls_unregister_event_handler; + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_cmds_process.c b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_cmds_process.c new file mode 100644 index 0000000000000000000000000000000000000000..f5c1f4da59e0b0748a4f8d918daa741fff7e2cf3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_cmds_process.c @@ -0,0 +1,2363 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_cmds_process.c + * + * TDLS north bound commands implementation + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_tdls_main.h" +#include "wlan_tdls_peer.h" +#include "wlan_tdls_ct.h" +#include "wlan_tdls_mgmt.h" +#include "wlan_tdls_cmds_process.h" +#include "wlan_tdls_tgt_api.h" +#include "wlan_policy_mgr_api.h" +#include "nan_ucfg_api.h" + +static uint16_t tdls_get_connected_peer(struct tdls_soc_priv_obj *soc_obj) +{ + return soc_obj->connected_peer_count; +} + +/** + * tdls_decrement_peer_count() - decrement connected TDLS peer counter + * @soc_obj: TDLS soc object + * + * Used in scheduler thread context, no lock needed. + * + * Return: None. + */ +void tdls_decrement_peer_count(struct tdls_soc_priv_obj *soc_obj) +{ + if (soc_obj->connected_peer_count) + soc_obj->connected_peer_count--; + + tdls_debug("Connected peer count %d", soc_obj->connected_peer_count); +} + +/** + * tdls_increment_peer_count() - increment connected TDLS peer counter + * @soc_obj: TDLS soc object + * + * Used in scheduler thread context, no lock needed. + * + * Return: None. + */ +static void tdls_increment_peer_count(struct tdls_soc_priv_obj *soc_obj) +{ + soc_obj->connected_peer_count++; + tdls_debug("Connected peer count %d", soc_obj->connected_peer_count); +} + +/** + * tdls_validate_current_mode() - check current TDL mode + * @soc_obj: TDLS soc object + * + * Return: QDF_STATUS_SUCCESS if TDLS enabled, other for disabled + */ +static QDF_STATUS tdls_validate_current_mode(struct tdls_soc_priv_obj *soc_obj) +{ + if (soc_obj->tdls_current_mode == TDLS_SUPPORT_DISABLED || + soc_obj->tdls_current_mode == TDLS_SUPPORT_SUSPENDED) { + tdls_err("TDLS mode disabled OR not enabled, current mode %d", + soc_obj->tdls_current_mode); + return QDF_STATUS_E_NOSUPPORT; + } + return QDF_STATUS_SUCCESS; +} + +static char *tdls_get_ser_cmd_str(enum wlan_serialization_cmd_type type) +{ + switch (type) { + case WLAN_SER_CMD_TDLS_ADD_PEER: + return "TDLS_ADD_PEER_CMD"; + case WLAN_SER_CMD_TDLS_DEL_PEER: + return "TDLS_DEL_PEER_CMD"; + case WLAN_SER_CMD_TDLS_SEND_MGMT: + return "TDLS_SEND_MGMT_CMD"; + default: + return "UNKNOWN"; + } +} + +void +tdls_release_serialization_command(struct wlan_objmgr_vdev *vdev, + enum wlan_serialization_cmd_type type) +{ + struct wlan_serialization_queued_cmd_info cmd = {0}; + + cmd.cmd_type = type; + cmd.cmd_id = 0; + cmd.vdev = vdev; + + tdls_debug("release %s", tdls_get_ser_cmd_str(type)); + /* Inform serialization for command completion */ + wlan_serialization_remove_cmd(&cmd); +} + +/** + * tdls_pe_add_peer() - send TDLS add peer request to PE + * @req: TDL add peer request + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +static QDF_STATUS tdls_pe_add_peer(struct tdls_add_peer_request *req) +{ + struct tdls_add_sta_req *addstareq; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct scheduler_msg msg = {0,}; + QDF_STATUS status; + + addstareq = qdf_mem_malloc(sizeof(*addstareq)); + if (!addstareq) { + tdls_err("allocate failed"); + return QDF_STATUS_E_NOMEM; + } + vdev = req->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("NULL tdls soc object"); + status = QDF_STATUS_E_INVAL; + goto error; + } + + addstareq->tdls_oper = TDLS_OPER_ADD; + addstareq->transaction_id = 0; + + addstareq->session_id = wlan_vdev_get_id(vdev); + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_TDLS_NB_ID); + if (!peer) { + tdls_err("bss peer is NULL"); + status = QDF_STATUS_E_INVAL; + goto error; + } + wlan_peer_obj_lock(peer); + qdf_mem_copy(addstareq->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + wlan_peer_obj_unlock(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); + qdf_mem_copy(addstareq->peermac.bytes, req->add_peer_req.peer_addr, + QDF_MAC_ADDR_SIZE); + + tdls_debug("for " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(addstareq->peermac.bytes)); + msg.type = soc_obj->tdls_add_sta_req; + msg.bodyptr = addstareq; + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("fail to post pe msg to add peer"); + goto error; + } + return status; +error: + qdf_mem_free(addstareq); + return status; +} + +/** + * tdls_pe_del_peer() - send TDLS delete peer request to PE + * @req: TDLS delete peer request + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_pe_del_peer(struct tdls_del_peer_request *req) +{ + struct tdls_del_sta_req *delstareq; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct scheduler_msg msg = {0,}; + QDF_STATUS status; + + delstareq = qdf_mem_malloc(sizeof(*delstareq)); + if (!delstareq) { + tdls_err("allocate failed"); + return QDF_STATUS_E_NOMEM; + } + vdev = req->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("NULL tdls soc object"); + status = QDF_STATUS_E_INVAL; + goto error; + } + + delstareq->transaction_id = 0; + + delstareq->session_id = wlan_vdev_get_id(vdev); + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_TDLS_NB_ID); + if (!peer) { + tdls_err("bss peer is NULL"); + status = QDF_STATUS_E_INVAL; + goto error; + } + + wlan_peer_obj_lock(peer); + qdf_mem_copy(delstareq->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + wlan_peer_obj_unlock(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); + qdf_mem_copy(delstareq->peermac.bytes, req->del_peer_req.peer_addr, + QDF_MAC_ADDR_SIZE); + + tdls_debug("for " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(delstareq->peermac.bytes)); + msg.type = soc_obj->tdls_del_sta_req; + msg.bodyptr = delstareq; + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("fail to post pe msg to del peer"); + goto error; + } + return status; +error: + qdf_mem_free(delstareq); + return status; +} + +/** + * tdls_pe_update_peer() - send TDLS update peer request to PE + * @req: TDLS update peer request + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +static QDF_STATUS tdls_pe_update_peer(struct tdls_update_peer_request *req) +{ + struct tdls_add_sta_req *addstareq; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct scheduler_msg msg = {0,}; + struct tdls_update_peer_params *update_peer; + QDF_STATUS status; + + addstareq = qdf_mem_malloc(sizeof(*addstareq)); + if (!addstareq) { + tdls_err("allocate failed"); + return QDF_STATUS_E_NOMEM; + } + vdev = req->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("NULL tdls soc object"); + status = QDF_STATUS_E_INVAL; + goto error; + } + update_peer = &req->update_peer_req; + + addstareq->tdls_oper = TDLS_OPER_UPDATE; + addstareq->transaction_id = 0; + + addstareq->session_id = wlan_vdev_get_id(vdev); + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_TDLS_NB_ID); + if (!peer) { + tdls_err("bss peer is NULL"); + status = QDF_STATUS_E_INVAL; + goto error; + } + wlan_peer_obj_lock(peer); + qdf_mem_copy(addstareq->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + wlan_peer_obj_unlock(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); + qdf_mem_copy(addstareq->peermac.bytes, update_peer->peer_addr, + QDF_MAC_ADDR_SIZE); + addstareq->capability = update_peer->capability; + addstareq->uapsd_queues = update_peer->uapsd_queues; + addstareq->max_sp = update_peer->max_sp; + + qdf_mem_copy(addstareq->extn_capability, + update_peer->extn_capability, WLAN_MAC_MAX_EXTN_CAP); + addstareq->htcap_present = update_peer->htcap_present; + qdf_mem_copy(&addstareq->ht_cap, + &update_peer->ht_cap, + sizeof(update_peer->ht_cap)); + addstareq->vhtcap_present = update_peer->vhtcap_present; + qdf_mem_copy(&addstareq->vht_cap, + &update_peer->vht_cap, + sizeof(update_peer->vht_cap)); + addstareq->supported_rates_length = update_peer->supported_rates_len; + addstareq->is_pmf = update_peer->is_pmf; + qdf_mem_copy(&addstareq->supported_rates, + update_peer->supported_rates, + update_peer->supported_rates_len); + tdls_debug("for " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(addstareq->peermac.bytes)); + + msg.type = soc_obj->tdls_add_sta_req; + msg.bodyptr = addstareq; + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("fail to post pe msg to update peer"); + goto error; + } + return status; +error: + qdf_mem_free(addstareq); + return status; +} + +static QDF_STATUS +tdls_internal_add_peer_rsp(struct tdls_add_peer_request *req, + QDF_STATUS status) +{ + struct tdls_soc_priv_obj *soc_obj; + struct wlan_objmgr_vdev *vdev; + struct tdls_osif_indication ind; + QDF_STATUS ret; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + return QDF_STATUS_E_INVAL; + } + vdev = req->vdev; + ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_SB_ID); + if (QDF_IS_STATUS_ERROR(ret)) { + tdls_err("can't get vdev object"); + return ret; + } + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = vdev; + ind.status = status; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +tdls_internal_update_peer_rsp(struct tdls_update_peer_request *req, + QDF_STATUS status) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_osif_indication ind; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS ret; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + return QDF_STATUS_E_INVAL; + } + vdev = req->vdev; + ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_SB_ID); + if (QDF_IS_STATUS_ERROR(ret)) { + tdls_err("can't get vdev object"); + return ret; + } + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = vdev; + ind.status = status; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_internal_del_peer_rsp(struct tdls_oper_request *req) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_osif_indication ind; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + return QDF_STATUS_E_INVAL; + } + vdev = req->vdev; + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev object"); + return status; + } + + soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = req->vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_DEL_PEER, &ind); + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_activate_add_peer(struct tdls_add_peer_request *req) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_peer *peer; + uint16_t curr_tdls_peers; + const uint8_t *mac; + struct tdls_osif_indication ind; + + if (!req->vdev) { + tdls_err("vdev null when add tdls peer"); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + mac = req->add_peer_req.peer_addr; + soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(req->vdev); + + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK", soc_obj, vdev_obj); + return QDF_STATUS_E_INVAL; + } + status = tdls_validate_current_mode(soc_obj); + if (QDF_IS_STATUS_ERROR(status)) + goto addrsp; + + peer = tdls_get_peer(vdev_obj, mac); + if (!peer) { + tdls_err("peer: " QDF_MAC_ADDR_FMT " not exist. invalid", + QDF_MAC_ADDR_REF(mac)); + status = QDF_STATUS_E_INVAL; + goto addrsp; + } + + /* in add station, we accept existing valid sta_id if there is */ + if ((peer->link_status > TDLS_LINK_CONNECTING) || + (peer->valid_entry)) { + tdls_notice("link_status %d add peer ignored", + peer->link_status); + status = QDF_STATUS_SUCCESS; + goto addrsp; + } + + /* when others are on-going, we want to change link_status to idle */ + if (tdls_is_progress(vdev_obj, mac, true)) { + tdls_notice(QDF_MAC_ADDR_FMT " TDLS setuping. Req declined.", + QDF_MAC_ADDR_REF(mac)); + status = QDF_STATUS_E_PERM; + goto setlink; + } + + /* first to check if we reached to maximum supported TDLS peer. */ + curr_tdls_peers = tdls_get_connected_peer(soc_obj); + if (soc_obj->max_num_tdls_sta <= curr_tdls_peers) { + tdls_err(QDF_MAC_ADDR_FMT + " Request declined. Current %d, Max allowed %d.", + QDF_MAC_ADDR_REF(mac), curr_tdls_peers, + soc_obj->max_num_tdls_sta); + status = QDF_STATUS_E_PERM; + goto setlink; + } + + tdls_set_peer_link_status(peer, + TDLS_LINK_CONNECTING, TDLS_LINK_SUCCESS); + + status = tdls_pe_add_peer(req); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err(QDF_MAC_ADDR_FMT " add peer failed with status %d", + QDF_MAC_ADDR_REF(mac), status); + goto setlink; + } + + return QDF_STATUS_SUCCESS; + +setlink: + tdls_set_link_status(vdev_obj, mac, TDLS_LINK_IDLE, + TDLS_LINK_UNSPECIFIED); +addrsp: + if (soc_obj->tdls_event_cb) { + ind.status = status; + ind.vdev = req->vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + + return QDF_STATUS_E_PERM; +} + +static QDF_STATUS +tdls_add_peer_serialize_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason) +{ + struct tdls_add_peer_request *req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!cmd || !cmd->umac_cmd) { + tdls_err("cmd: %pK, reason: %d", cmd, reason); + return QDF_STATUS_E_NULL_VALUE; + } + + req = cmd->umac_cmd; + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + /* command moved to active list + */ + status = tdls_activate_add_peer(req); + break; + + case WLAN_SER_CB_CANCEL_CMD: + /* command removed from pending list. + * notify os interface the status + */ + status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + /* active command time out. */ + status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_RELEASE_MEM_CMD: + /* command successfully completed. + * release memory & vdev reference count + */ + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + break; + + default: + /* Do nothing but logging */ + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +void tdls_reset_nss(struct tdls_soc_priv_obj *tdls_soc, + uint8_t action_code) +{ + if (!tdls_soc) + return; + + if (TDLS_TEARDOWN != action_code || + !tdls_soc->tdls_nss_switch_in_progress) + return; + + if (tdls_soc->tdls_teardown_peers_cnt != 0) + tdls_soc->tdls_teardown_peers_cnt--; + if (tdls_soc->tdls_teardown_peers_cnt == 0) { + if (tdls_soc->tdls_nss_transition_mode == + TDLS_NSS_TRANSITION_S_1x1_to_2x2) { + /* TDLS NSS switch is fully completed, so + * reset the flags. + */ + tdls_notice("TDLS NSS switch is fully completed"); + tdls_soc->tdls_nss_switch_in_progress = false; + tdls_soc->tdls_nss_teardown_complete = false; + } else { + /* TDLS NSS switch is not yet completed, but + * tdls teardown is completed for all the + * peers. + */ + tdls_notice("teardown done & NSS switch in progress"); + tdls_soc->tdls_nss_teardown_complete = true; + } + tdls_soc->tdls_nss_transition_mode = + TDLS_NSS_TRANSITION_S_UNKNOWN; + } + +} + +/** + * tdls_set_cap() - set TDLS capability type + * @tdls_vdev: tdls vdev object + * @mac: peer mac address + * @cap: TDLS capability type + * + * Return: 0 if successful or negative errno otherwise + */ +int tdls_set_cap(struct tdls_vdev_priv_obj *tdls_vdev, const uint8_t *mac, + enum tdls_peer_capab cap) +{ + struct tdls_peer *curr_peer; + + curr_peer = tdls_get_peer(tdls_vdev, mac); + if (!curr_peer) { + tdls_err("curr_peer is NULL"); + return -EINVAL; + } + + curr_peer->tdls_support = cap; + return 0; +} + +static int tdls_validate_setup_frames(struct tdls_soc_priv_obj *tdls_soc, + struct tdls_validate_action_req *tdls_validate) +{ + /* supplicant still sends tdls_mgmt(SETUP_REQ) + * even after we return error code at + * 'add_station()'. Hence we have this check + * again in addition to add_station(). Anyway, + * there is no harm to double-check. + */ + if (TDLS_SETUP_REQUEST == tdls_validate->action_code) { + tdls_err(QDF_MAC_ADDR_FMT " TDLS Max peer already connected. action (%d) declined. Num of peers (%d), Max allowed (%d).", + QDF_MAC_ADDR_REF(tdls_validate->peer_mac), + tdls_validate->action_code, + tdls_soc->connected_peer_count, + tdls_soc->max_num_tdls_sta); + return -EINVAL; + } + /* maximum reached. tweak to send + * error code to peer and return error + * code to supplicant + */ + tdls_validate->status_code = QDF_STATUS_E_RESOURCES; + tdls_err(QDF_MAC_ADDR_FMT " TDLS Max peer already connected, send response status (%d). Num of peers (%d), Max allowed (%d).", + QDF_MAC_ADDR_REF(tdls_validate->peer_mac), + tdls_validate->action_code, + tdls_soc->connected_peer_count, + tdls_soc->max_num_tdls_sta); + + return -EPERM; +} + +int tdls_validate_mgmt_request(struct tdls_action_frame_request *tdls_mgmt_req) +{ + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc; + struct tdls_peer *curr_peer; + struct tdls_peer *temp_peer; + QDF_STATUS status; + uint8_t vdev_id; + + struct wlan_objmgr_vdev *vdev = tdls_mgmt_req->vdev; + struct tdls_validate_action_req *tdls_validate = + &tdls_mgmt_req->chk_frame; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, + &tdls_vdev, + &tdls_soc)) + return -ENOTSUPP; + + /* + * STA or P2P client should be connected and authenticated before + * sending any TDLS frames + */ + if ((wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) || + !tdls_is_vdev_authenticated(vdev)) { + tdls_err("STA is not connected or not authenticated."); + return -EAGAIN; + } + + /* other than teardown frame, mgmt frames are not sent if disabled */ + if (TDLS_TEARDOWN != tdls_validate->action_code) { + if (ucfg_is_nan_disc_active(tdls_soc->soc)) { + tdls_err("NAN active. NAN+TDLS not supported"); + return -EPERM; + } + + if (!tdls_check_is_tdls_allowed(vdev)) { + tdls_err("TDLS not allowed, reject MGMT, action = %d", + tdls_validate->action_code); + return -EPERM; + } + /* if tdls_mode is disabled, then decline the peer's request */ + if (TDLS_SUPPORT_DISABLED == tdls_soc->tdls_current_mode || + TDLS_SUPPORT_SUSPENDED == tdls_soc->tdls_current_mode) { + tdls_notice(QDF_MAC_ADDR_FMT + " TDLS mode is disabled. action %d declined.", + QDF_MAC_ADDR_REF(tdls_validate->peer_mac), + tdls_validate->action_code); + return -ENOTSUPP; + } + if (tdls_soc->tdls_nss_switch_in_progress) { + tdls_err("nss switch in progress, action %d declined " + QDF_MAC_ADDR_FMT, + tdls_validate->action_code, + QDF_MAC_ADDR_REF(tdls_validate->peer_mac)); + return -EAGAIN; + } + } + + if (TDLS_IS_SETUP_ACTION(tdls_validate->action_code)) { + if (tdls_is_progress(tdls_vdev, + tdls_validate->peer_mac, true)) { + tdls_err("setup is ongoing. action %d declined for " + QDF_MAC_ADDR_FMT, + tdls_validate->action_code, + QDF_MAC_ADDR_REF(tdls_validate->peer_mac)); + return -EPERM; + } + } + + /* call hdd_wmm_is_acm_allowed() */ + vdev_id = wlan_vdev_get_id(vdev); + if (!tdls_soc->tdls_wmm_cb(vdev_id)) { + tdls_debug("admission ctrl set to VI, send the frame with least AC (BK) for action %d", + tdls_validate->action_code); + tdls_mgmt_req->use_default_ac = false; + } else { + tdls_mgmt_req->use_default_ac = true; + } + + if (TDLS_SETUP_REQUEST == tdls_validate->action_code || + TDLS_SETUP_RESPONSE == tdls_validate->action_code) { + if (tdls_soc->max_num_tdls_sta <= + tdls_soc->connected_peer_count) { + status = tdls_validate_setup_frames(tdls_soc, + tdls_validate); + if (QDF_STATUS_SUCCESS != status) + return status; + /* fall through to send setup resp + * with failure status code + */ + } else { + curr_peer = + tdls_find_peer(tdls_vdev, + tdls_validate->peer_mac); + if (curr_peer) { + if (TDLS_IS_LINK_CONNECTED(curr_peer)) { + tdls_err(QDF_MAC_ADDR_FMT " already connected action %d declined.", + QDF_MAC_ADDR_REF( + tdls_validate->peer_mac), + tdls_validate->action_code); + + return -EPERM; + } + } + } + } + + tdls_debug("tdls_mgmt" QDF_MAC_ADDR_FMT " action %d, dialog_token %d status %d, len = %zu", + QDF_MAC_ADDR_REF(tdls_validate->peer_mac), + tdls_validate->action_code, tdls_validate->dialog_token, + tdls_validate->status_code, tdls_validate->len); + + /*Except teardown responder will not be used so just make 0 */ + tdls_validate->responder = 0; + if (TDLS_TEARDOWN == tdls_validate->action_code) { + temp_peer = tdls_find_peer(tdls_vdev, tdls_validate->peer_mac); + if (!temp_peer) { + tdls_err(QDF_MAC_ADDR_FMT " peer doesn't exist", + QDF_MAC_ADDR_REF( + tdls_validate->peer_mac)); + return -EPERM; + } + + if (TDLS_IS_LINK_CONNECTED(temp_peer)) + tdls_validate->responder = temp_peer->is_responder; + else { + tdls_err(QDF_MAC_ADDR_FMT " peer doesn't exist or not connected %d dialog_token %d status %d, tdls_validate->len = %zu", + QDF_MAC_ADDR_REF(tdls_validate->peer_mac), + temp_peer->link_status, + tdls_validate->dialog_token, + tdls_validate->status_code, + tdls_validate->len); + return -EPERM; + } + } + + /* For explicit trigger of DIS_REQ come out of BMPS for + * successfully receiving DIS_RSP from peer. + */ + if ((TDLS_SETUP_RESPONSE == tdls_validate->action_code) || + (TDLS_SETUP_CONFIRM == tdls_validate->action_code) || + (TDLS_DISCOVERY_RESPONSE == tdls_validate->action_code) || + (TDLS_DISCOVERY_REQUEST == tdls_validate->action_code)) { + /* Fw will take care if PS offload is enabled. */ + if (TDLS_DISCOVERY_REQUEST != tdls_validate->action_code) + tdls_set_cap(tdls_vdev, tdls_validate->peer_mac, + TDLS_CAP_SUPPORTED); + } + return 0; +} + +QDF_STATUS tdls_process_add_peer(struct tdls_add_peer_request *req) +{ + struct wlan_serialization_command cmd = {0,}; + enum wlan_serialization_status ser_cmd_status; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_E_INVAL; + struct wlan_objmgr_psoc *psoc; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + goto free_req; + } + vdev = req->vdev; + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + tdls_err("can't get psoc"); + goto error; + } + if (ucfg_is_nan_disc_active(psoc)) { + tdls_err("NAN active. NAN+TDLS not supported"); + goto error; + } + status = QDF_STATUS_SUCCESS; + + cmd.cmd_type = WLAN_SER_CMD_TDLS_ADD_PEER; + cmd.cmd_id = 0; + cmd.cmd_cb = tdls_add_peer_serialize_callback; + cmd.umac_cmd = req; + cmd.source = WLAN_UMAC_COMP_TDLS; + cmd.is_high_priority = false; + cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; + cmd.vdev = vdev; + cmd.is_blocking = true; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("req: 0x%pK wlan_serialization_request status:%d", req, + ser_cmd_status); + + switch (ser_cmd_status) { + case WLAN_SER_CMD_PENDING: + /* command moved to pending list. Do nothing */ + break; + case WLAN_SER_CMD_ACTIVE: + /* command moved to active list. Do nothing */ + break; + default: + goto error; + } + + return status; +error: + /* notify os interface about internal error*/ + status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); +free_req: + qdf_mem_free(req); + return status; +} + +static QDF_STATUS +tdls_activate_update_peer(struct tdls_update_peer_request *req) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct wlan_objmgr_vdev *vdev; + struct tdls_peer *curr_peer; + uint16_t curr_tdls_peers; + const uint8_t *mac; + struct tdls_update_peer_params *update_peer; + struct tdls_osif_indication ind; + + if (!req->vdev) { + tdls_err("vdev object NULL when add TDLS peer"); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + mac = req->update_peer_req.peer_addr; + vdev = req->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK", soc_obj, vdev_obj); + return QDF_STATUS_E_INVAL; + } + + status = tdls_validate_current_mode(soc_obj); + if (QDF_IS_STATUS_ERROR(status)) + goto updatersp; + + curr_peer = tdls_find_peer(vdev_obj, mac); + if (!curr_peer) { + tdls_err(QDF_MAC_ADDR_FMT " not exist. return invalid", + QDF_MAC_ADDR_REF(mac)); + status = QDF_STATUS_E_INVAL; + goto updatersp; + } + + /* in change station, we accept only when sta_id is valid */ + if (curr_peer->link_status == TDLS_LINK_TEARING || + !curr_peer->valid_entry) { + tdls_err(QDF_MAC_ADDR_FMT " link %d. update peer rejected", + QDF_MAC_ADDR_REF(mac), curr_peer->link_status); + status = QDF_STATUS_E_PERM; + goto updatersp; + } + + if (curr_peer->link_status == TDLS_LINK_CONNECTED && + curr_peer->valid_entry) { + tdls_err(QDF_MAC_ADDR_FMT " link %d. update peer is igonored as tdls state is already connected ", + QDF_MAC_ADDR_REF(mac), curr_peer->link_status); + status = QDF_STATUS_SUCCESS; + goto updatersp; + } + + /* when others are on-going, we want to change link_status to idle */ + if (tdls_is_progress(vdev_obj, mac, true)) { + tdls_notice(QDF_MAC_ADDR_FMT " TDLS setuping. Req declined.", + QDF_MAC_ADDR_REF(mac)); + status = QDF_STATUS_E_PERM; + goto setlink; + } + + curr_tdls_peers = tdls_get_connected_peer(soc_obj); + if (soc_obj->max_num_tdls_sta <= curr_tdls_peers) { + tdls_err(QDF_MAC_ADDR_FMT + " Request declined. Current: %d, Max allowed: %d.", + QDF_MAC_ADDR_REF(mac), curr_tdls_peers, + soc_obj->max_num_tdls_sta); + status = QDF_STATUS_E_PERM; + goto setlink; + } + update_peer = &req->update_peer_req; + + if (update_peer->htcap_present) + curr_peer->spatial_streams = update_peer->ht_cap.mcsset[1]; + + tdls_set_peer_caps(vdev_obj, mac, &req->update_peer_req); + status = tdls_pe_update_peer(req); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err(QDF_MAC_ADDR_FMT " update peer failed with status %d", + QDF_MAC_ADDR_REF(mac), status); + goto setlink; + } + + return QDF_STATUS_SUCCESS; + +setlink: + tdls_set_link_status(vdev_obj, mac, TDLS_LINK_IDLE, + TDLS_LINK_UNSPECIFIED); +updatersp: + if (soc_obj->tdls_event_cb) { + ind.status = status; + ind.vdev = vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + + return QDF_STATUS_E_PERM; +} + +static QDF_STATUS +tdls_update_peer_serialize_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason) +{ + struct tdls_update_peer_request *req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!cmd || !cmd->umac_cmd) { + tdls_err("cmd: %pK, reason: %d", cmd, reason); + return QDF_STATUS_E_NULL_VALUE; + } + + req = cmd->umac_cmd; + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + /* command moved to active list + */ + status = tdls_activate_update_peer(req); + break; + + case WLAN_SER_CB_CANCEL_CMD: + /* command removed from pending list. + * notify os interface the status + */ + status = tdls_internal_update_peer_rsp(req, + QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + /* active command time out. */ + status = tdls_internal_update_peer_rsp(req, + QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_RELEASE_MEM_CMD: + /* command successfully completed. + * release memory & release reference count + */ + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + break; + + default: + /* Do nothing but logging */ + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +QDF_STATUS tdls_process_update_peer(struct tdls_update_peer_request *req) +{ + struct wlan_serialization_command cmd = {0,}; + enum wlan_serialization_status ser_cmd_status; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + status = QDF_STATUS_E_FAILURE; + goto free_req; + } + + vdev = req->vdev; + cmd.cmd_type = WLAN_SER_CMD_TDLS_ADD_PEER; + cmd.cmd_id = 0; + cmd.cmd_cb = tdls_update_peer_serialize_callback; + cmd.umac_cmd = req; + cmd.source = WLAN_UMAC_COMP_TDLS; + cmd.is_high_priority = false; + cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; + cmd.vdev = req->vdev; + cmd.is_blocking = true; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("req: 0x%pK wlan_serialization_request status:%d", req, + ser_cmd_status); + + switch (ser_cmd_status) { + case WLAN_SER_CMD_PENDING: + /* command moved to pending list. Do nothing */ + break; + case WLAN_SER_CMD_ACTIVE: + /* command moved to active list. Do nothing */ + break; + default: + goto error; + } + + return status; +error: + /* notify os interface about internal error*/ + status = tdls_internal_update_peer_rsp(req, QDF_STATUS_E_FAILURE); + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); +free_req: + qdf_mem_free(req); + return status; +} + +static QDF_STATUS tdls_activate_del_peer(struct tdls_oper_request *req) +{ + struct tdls_del_peer_request request = {0,}; + + request.vdev = req->vdev; + request.del_peer_req.peer_addr = req->peer_addr; + + return tdls_pe_del_peer(&request); +} + +static QDF_STATUS +tdls_del_peer_serialize_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason) +{ + struct tdls_oper_request *req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!cmd || !cmd->umac_cmd) { + tdls_err("cmd: %pK, reason: %d", cmd, reason); + return QDF_STATUS_E_NULL_VALUE; + } + + req = cmd->umac_cmd; + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + /* command moved to active list + */ + status = tdls_activate_del_peer(req); + break; + + case WLAN_SER_CB_CANCEL_CMD: + /* command removed from pending list. + * notify os interface the status + */ + status = tdls_internal_del_peer_rsp(req); + break; + + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + /* active command time out. */ + status = tdls_internal_del_peer_rsp(req); + break; + + case WLAN_SER_CB_RELEASE_MEM_CMD: + /* command successfully completed. + * release memory & vdev reference count + */ + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + break; + + default: + /* Do nothing but logging */ + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +QDF_STATUS tdls_process_del_peer(struct tdls_oper_request *req) +{ + struct wlan_serialization_command cmd = {0,}; + enum wlan_serialization_status ser_cmd_status; + struct wlan_objmgr_vdev *vdev; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj; + uint8_t *mac; + struct tdls_peer *peer; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + status = QDF_STATUS_E_INVAL; + goto free_req; + } + + vdev = req->vdev; + + /* vdev reference cnt is acquired in ucfg_tdls_oper */ + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + + if (!vdev_obj || !soc_obj) { + tdls_err("tdls vdev_obj: %pK soc_obj: %pK", vdev_obj, soc_obj); + status = QDF_STATUS_E_NULL_VALUE; + goto error; + } + + mac = req->peer_addr; + peer = tdls_find_peer(vdev_obj, mac); + if (!peer) { + tdls_err(QDF_MAC_ADDR_FMT + " not found, ignore NL80211_TDLS_ENABLE_LINK", + QDF_MAC_ADDR_REF(mac)); + status = QDF_STATUS_E_INVAL; + goto error; + } + + if (!peer->valid_entry) { + tdls_err("invalid peer:" QDF_MAC_ADDR_FMT " link state %d", + QDF_MAC_ADDR_REF(mac), peer->link_status); + status = QDF_STATUS_E_INVAL; + goto error; + } + + if (soc_obj->tdls_dp_vdev_update) + soc_obj->tdls_dp_vdev_update(&soc_obj->soc, + wlan_vdev_get_id(vdev), + soc_obj->tdls_update_dp_vdev_flags, + false); + + cmd.cmd_type = WLAN_SER_CMD_TDLS_DEL_PEER; + cmd.cmd_id = 0; + cmd.cmd_cb = tdls_del_peer_serialize_callback; + cmd.umac_cmd = req; + cmd.source = WLAN_UMAC_COMP_TDLS; + cmd.is_high_priority = false; + cmd.cmd_timeout_duration = TDLS_DELETE_PEER_CMD_TIMEOUT; + cmd.vdev = vdev; + cmd.is_blocking = true; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("req: 0x%pK wlan_serialization_request status:%d", req, + ser_cmd_status); + + switch (ser_cmd_status) { + case WLAN_SER_CMD_PENDING: + /* command moved to pending list. Do nothing */ + break; + case WLAN_SER_CMD_ACTIVE: + /* command moved to active list. Do nothing */ + break; + default: + goto error; + } + + return status; +error: + /* notify os interface about internal error*/ + status = tdls_internal_del_peer_rsp(req); + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); +free_req: + qdf_mem_free(req); + return status; +} + +/** + * tdls_process_add_peer_rsp() - handle response for update TDLS peer + * @rsp: TDLS add peer response + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +static QDF_STATUS tdls_update_peer_rsp(struct tdls_add_sta_rsp *rsp) +{ + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_soc_priv_obj *soc_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_osif_indication ind; + + psoc = rsp->psoc; + if (!psoc) { + tdls_err("psoc is NULL"); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, + WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("invalid vdev: %d", rsp->session_id); + status = QDF_STATUS_E_INVAL; + goto error; + } + + tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_ADD_PEER); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); +error: + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (soc_obj && soc_obj->tdls_event_cb) { + ind.status = rsp->status_code; + ind.vdev = vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + qdf_mem_free(rsp); + + return status; +} + +/** + * tdls_process_send_mgmt_rsp() - handle response for send mgmt + * @rsp: TDLS send mgmt response + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_process_send_mgmt_rsp(struct tdls_send_mgmt_rsp *rsp) +{ + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_osif_indication ind; + + psoc = rsp->psoc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->vdev_id, + WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("invalid vdev"); + status = QDF_STATUS_E_INVAL; + qdf_mem_free(rsp); + return status; + } + tdls_soc = wlan_psoc_get_tdls_soc_obj(psoc); + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_soc || !tdls_vdev) { + tdls_err("soc object:%pK, vdev object:%pK", tdls_soc, tdls_vdev); + status = QDF_STATUS_E_FAILURE; + } + + tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_SEND_MGMT); + + if (legacy_result_success == rsp->status_code) + goto free_rsp; + tdls_err("send mgmt failed. status code(=%d)", rsp->status_code); + status = QDF_STATUS_E_FAILURE; + + if (tdls_soc && tdls_soc->tdls_event_cb) { + ind.vdev = vdev; + ind.status = status; + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_MGMT_TX_ACK_CNF, &ind); + } + +free_rsp: + qdf_mem_free(rsp); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + return status; +} + +/** + * tdls_send_mgmt_tx_completion() - process tx completion + * @tx_complete: TDLS mgmt completion info + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_send_mgmt_tx_completion( + struct tdls_mgmt_tx_completion_ind *tx_complete) +{ + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_osif_indication ind; + + psoc = tx_complete->psoc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_complete->vdev_id, + WLAN_TDLS_SB_ID); + + if (!vdev) { + tdls_err("invalid vdev"); + status = QDF_STATUS_E_INVAL; + goto free_tx_complete; + } + + tdls_soc = wlan_psoc_get_tdls_soc_obj(psoc); + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + + if (!tdls_soc || !tdls_vdev) { + tdls_err("soc object:%pK, vdev object:%pK", tdls_soc, tdls_vdev); + status = QDF_STATUS_E_FAILURE; + } + + if (tdls_soc && tdls_soc->tdls_event_cb) { + ind.vdev = vdev; + ind.status = tx_complete->tx_complete_status; + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_MGMT_TX_ACK_CNF, &ind); + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); +free_tx_complete: + qdf_mem_free(tx_complete); + return status; +} + +/** + * tdls_add_peer_rsp() - handle response for add TDLS peer + * @rsp: TDLS add peer response + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +static QDF_STATUS tdls_add_peer_rsp(struct tdls_add_sta_rsp *rsp) +{ + uint8_t sta_idx; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj = NULL; + struct tdls_conn_info *conn_rec; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_osif_indication ind; + + psoc = rsp->psoc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, + WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("invalid vdev: %d", rsp->session_id); + status = QDF_STATUS_E_INVAL; + goto error; + } + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!soc_obj || !vdev_obj) { + tdls_err("soc object:%pK, vdev object:%pK", soc_obj, vdev_obj); + status = QDF_STATUS_E_FAILURE; + goto cmddone; + } + if (rsp->status_code) { + tdls_err("add sta failed. status code(=%d)", rsp->status_code); + status = QDF_STATUS_E_FAILURE; + } else { + conn_rec = soc_obj->tdls_conn_info; + for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; + sta_idx++) { + if (!conn_rec[sta_idx].valid_entry) { + conn_rec[sta_idx].session_id = rsp->session_id; + conn_rec[sta_idx].valid_entry = true; + conn_rec[sta_idx].index = sta_idx; + qdf_copy_macaddr(&conn_rec[sta_idx].peer_mac, + &rsp->peermac); + tdls_debug("TDLS: Add sta mac at idx %d" + QDF_MAC_ADDR_FMT, sta_idx, + QDF_MAC_ADDR_REF + (rsp->peermac.bytes)); + break; + } + } + + if (sta_idx < soc_obj->max_num_tdls_sta) { + status = tdls_set_valid(vdev_obj, rsp->peermac.bytes); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("set staid failed"); + status = QDF_STATUS_E_FAILURE; + } + } else { + status = QDF_STATUS_E_FAILURE; + } + } + +cmddone: + tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_ADD_PEER); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); +error: + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = vdev; + ind.status = rsp->status_code; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + qdf_mem_free(rsp); + + return status; +} + +QDF_STATUS tdls_process_add_peer_rsp(struct tdls_add_sta_rsp *rsp) +{ + tdls_debug("peer oper %d", rsp->tdls_oper); + + if (rsp->tdls_oper == TDLS_OPER_ADD) + return tdls_add_peer_rsp(rsp); + else if (rsp->tdls_oper == TDLS_OPER_UPDATE) + return tdls_update_peer_rsp(rsp); + + return QDF_STATUS_E_INVAL; +} + +QDF_STATUS tdls_process_del_peer_rsp(struct tdls_del_sta_rsp *rsp) +{ + uint8_t sta_idx, id; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj = NULL; + struct tdls_conn_info *conn_rec; + struct tdls_peer *curr_peer = NULL; + const uint8_t *macaddr; + struct tdls_osif_indication ind; + + tdls_debug("del peer rsp: vdev %d peer " QDF_MAC_ADDR_FMT, + rsp->session_id, QDF_MAC_ADDR_REF(rsp->peermac.bytes)); + psoc = rsp->psoc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, + WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("invalid vdev: %d", rsp->session_id); + status = QDF_STATUS_E_INVAL; + goto error; + } + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!soc_obj || !vdev_obj) { + tdls_err("soc object:%pK, vdev object:%pK", soc_obj, vdev_obj); + status = QDF_STATUS_E_FAILURE; + goto cmddone; + } + + conn_rec = soc_obj->tdls_conn_info; + for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; sta_idx++) { + if (conn_rec[sta_idx].session_id != rsp->session_id || + qdf_mem_cmp(conn_rec[sta_idx].peer_mac.bytes, + rsp->peermac.bytes, QDF_MAC_ADDR_SIZE)) + continue; + + macaddr = rsp->peermac.bytes; + tdls_debug("TDLS: del STA with sta_idx %d", sta_idx); + curr_peer = tdls_find_peer(vdev_obj, macaddr); + if (curr_peer) { + tdls_debug(QDF_MAC_ADDR_FMT " status is %d", + QDF_MAC_ADDR_REF(macaddr), + curr_peer->link_status); + + id = wlan_vdev_get_id(vdev); + + if (TDLS_IS_LINK_CONNECTED(curr_peer)) + tdls_decrement_peer_count(soc_obj); + } + tdls_reset_peer(vdev_obj, macaddr); + conn_rec[sta_idx].valid_entry = false; + conn_rec[sta_idx].session_id = 0xff; + conn_rec[sta_idx].index = INVALID_TDLS_PEER_INDEX; + qdf_mem_zero(&conn_rec[sta_idx].peer_mac, + QDF_MAC_ADDR_SIZE); + + status = QDF_STATUS_SUCCESS; + break; + } + macaddr = rsp->peermac.bytes; + if (!curr_peer) { + curr_peer = tdls_find_peer(vdev_obj, macaddr); + + if (curr_peer) + tdls_set_peer_link_status(curr_peer, TDLS_LINK_IDLE, + (curr_peer->link_status == + TDLS_LINK_TEARING) ? + TDLS_LINK_UNSPECIFIED : + TDLS_LINK_DROPPED_BY_REMOTE); + } + +cmddone: + tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_DEL_PEER); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); +error: + + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_DEL_PEER, &ind); + } + qdf_mem_free(rsp); + + return status; +} + +static QDF_STATUS +tdls_wma_update_peer_state(struct tdls_soc_priv_obj *soc_obj, + struct tdls_peer_update_state *peer_state) +{ + struct scheduler_msg msg = {0,}; + QDF_STATUS status; + + tdls_debug("update TDLS peer " QDF_MAC_ADDR_FMT " vdev %d, state %d", + QDF_MAC_ADDR_REF(peer_state->peer_macaddr), + peer_state->vdev_id, peer_state->peer_state); + msg.type = soc_obj->tdls_update_peer_state; + msg.reserved = 0; + msg.bodyptr = peer_state; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("scheduler_post_msg failed"); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +QDF_STATUS tdls_process_enable_link(struct tdls_oper_request *req) +{ + struct tdls_peer *peer; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj; + struct wlan_objmgr_vdev *vdev; + uint8_t *mac; + struct tdls_peer_update_state *peer_update_param; + QDF_STATUS status; + uint32_t feature; + uint8_t id; + + vdev = req->vdev; + if (!vdev) { + tdls_err("NULL vdev object"); + qdf_mem_free(req); + return QDF_STATUS_E_NULL_VALUE; + } + + /* vdev reference cnt is acquired in ucfg_tdls_oper */ + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + + if (!vdev_obj || !soc_obj) { + tdls_err("tdls vdev_obj: %pK soc_obj: %pK", vdev_obj, soc_obj); + status = QDF_STATUS_E_NULL_VALUE; + goto error; + } + + mac = req->peer_addr; + peer = tdls_find_peer(vdev_obj, mac); + if (!peer) { + tdls_err(QDF_MAC_ADDR_FMT + " not found, ignore NL80211_TDLS_ENABLE_LINK", + QDF_MAC_ADDR_REF(mac)); + status = QDF_STATUS_E_INVAL; + goto error; + } + + tdls_debug("enable link for peer " QDF_MAC_ADDR_FMT " link state %d", + QDF_MAC_ADDR_REF(mac), peer->link_status); + if (!peer->valid_entry) { + tdls_err("invalid entry " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac)); + status = QDF_STATUS_E_INVAL; + goto error; + } + + peer->tdls_support = TDLS_CAP_SUPPORTED; + if (TDLS_LINK_CONNECTED != peer->link_status) + tdls_set_peer_link_status(peer, TDLS_LINK_CONNECTED, + TDLS_LINK_SUCCESS); + + id = wlan_vdev_get_id(vdev); + status = soc_obj->tdls_reg_peer(soc_obj->tdls_peer_context, + id, mac, peer->qos); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("TDLS register peer fail, status %d", status); + goto error; + } + + peer_update_param = qdf_mem_malloc(sizeof(*peer_update_param)); + if (!peer_update_param) { + tdls_err("memory allocation failed"); + status = QDF_STATUS_E_NOMEM; + goto error; + } + + tdls_extract_peer_state_param(peer_update_param, peer); + + status = tdls_wma_update_peer_state(soc_obj, peer_update_param); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(peer_update_param); + status = QDF_STATUS_E_PERM; + goto error; + } + + tdls_increment_peer_count(soc_obj); + feature = soc_obj->tdls_configs.tdls_feature_flags; + + if (soc_obj->tdls_dp_vdev_update) + soc_obj->tdls_dp_vdev_update( + &soc_obj->soc, + wlan_vdev_get_id(vdev), + soc_obj->tdls_update_dp_vdev_flags, + ((peer->link_status == TDLS_LINK_CONNECTED) ? + true : false)); + + tdls_debug("TDLS buffer sta: %d, uapsd_mask %d", + TDLS_IS_BUFFER_STA_ENABLED(feature), + soc_obj->tdls_configs.tdls_uapsd_mask); + +error: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + + return status; +} + +/** + * tdls_config_force_peer() - configure an externally controllable TDLS peer + * @req: TDLS operation request + * + * This is not the tdls_process_cmd function. No need to acquire the reference + * count, release reference count and free the request, the caller handle it + * correctly. + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +static QDF_STATUS tdls_config_force_peer( + struct tdls_oper_config_force_peer_request *req) +{ + struct tdls_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_vdev *vdev; + const uint8_t *macaddr; + uint32_t feature; + QDF_STATUS status; + uint32_t chan_freq; + struct tdls_peer_update_state *peer_update_param; + + macaddr = req->peer_addr; + + vdev = req->vdev; + pdev = wlan_vdev_get_pdev(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!pdev || !vdev_obj || !soc_obj) { + tdls_err("pdev: %pK, vdev_obj: %pK, soc_obj: %pK", + pdev, vdev_obj, soc_obj); + return QDF_STATUS_E_INVAL; + } + + feature = soc_obj->tdls_configs.tdls_feature_flags; + if (!(TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature) || + TDLS_IS_LIBERAL_EXTERNAL_CONTROL_ENABLED(feature)) || + !TDLS_IS_IMPLICIT_TRIG_ENABLED(feature)) { + tdls_err("TDLS ext ctrl or Imp Trig not enabled, %x", feature); + return QDF_STATUS_E_NOSUPPORT; + } + + /* + * In case of liberal external mode, supplicant will provide peer mac + * address but driver has to behave similar to implict mode ie + * establish tdls link with any peer that supports tdls and meets stats + */ + if (TDLS_IS_LIBERAL_EXTERNAL_CONTROL_ENABLED(feature)) { + tdls_debug("liberal mode set"); + return QDF_STATUS_SUCCESS; + } + + peer_update_param = qdf_mem_malloc(sizeof(*peer_update_param)); + if (!peer_update_param) { + tdls_err("memory allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + peer = tdls_get_peer(vdev_obj, macaddr); + if (!peer) { + tdls_err("peer " QDF_MAC_ADDR_FMT " does not exist", + QDF_MAC_ADDR_REF(macaddr)); + status = QDF_STATUS_E_NULL_VALUE; + goto error; + } + status = tdls_set_force_peer(vdev_obj, macaddr, true); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("set force peer failed"); + goto error; + } + + /* Update the peer mac to firmware, so firmware could update the + * connection table + */ + peer_update_param->vdev_id = wlan_vdev_get_id(vdev); + qdf_mem_copy(peer_update_param->peer_macaddr, + macaddr, QDF_MAC_ADDR_SIZE); + peer_update_param->peer_state = TDLS_PEER_ADD_MAC_ADDR; + + status = tdls_wma_update_peer_state(soc_obj, peer_update_param); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("update peer state failed"); + goto error; + } + + soc_obj->tdls_external_peer_count++; + chan_freq = wlan_reg_legacy_chan_to_freq(pdev, req->chan); + + /* Validate if off channel is DFS channel */ + if (wlan_reg_is_dfs_for_freq(pdev, chan_freq)) { + tdls_err("Resetting TDLS off-channel from %d to %d", + req->chan, WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF); + req->chan = WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF; + } + tdls_set_extctrl_param(peer, req->chan, req->max_latency, req->op_class, + req->min_bandwidth); + + tdls_set_callback(peer, req->callback); + + tdls_set_ct_mode(soc_obj->soc); + if (soc_obj->enable_tdls_connection_tracker) + tdls_implicit_enable(vdev_obj); + + return status; +error: + qdf_mem_free(peer_update_param); + return status; +} + +/** + * tdls_process_setup_peer() - process configure an externally + * controllable TDLS peer + * @req: TDLS operation request + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_process_setup_peer(struct tdls_oper_request *req) +{ + struct tdls_oper_config_force_peer_request peer_req; + struct tdls_soc_priv_obj *soc_obj; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + tdls_debug("Configure external TDLS peer " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(req->peer_addr)); + + /* reference cnt is acquired in ucfg_tdls_oper */ + vdev = req->vdev; + if (!vdev) { + tdls_err("NULL vdev object"); + status = QDF_STATUS_E_NULL_VALUE; + goto freereq; + } + + qdf_mem_zero(&peer_req, sizeof(peer_req)); + peer_req.vdev = vdev; + qdf_mem_copy(peer_req.peer_addr, req->peer_addr, QDF_MAC_ADDR_SIZE); + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("NULL soc object"); + status = QDF_STATUS_E_INVAL; + goto error; + } + + peer_req.chan = soc_obj->tdls_configs.tdls_pre_off_chan_num; + + status = tdls_config_force_peer(&peer_req); +error: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +freereq: + qdf_mem_free(req); + + return status; +} + +QDF_STATUS tdls_process_remove_force_peer(struct tdls_oper_request *req) +{ + struct tdls_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct wlan_objmgr_vdev *vdev; + const uint8_t *macaddr; + uint32_t feature; + QDF_STATUS status; + struct tdls_peer_update_state *peer_update_param; + struct tdls_osif_indication ind; + + macaddr = req->peer_addr; + tdls_debug("NL80211_TDLS_TEARDOWN for " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(macaddr)); + + vdev = req->vdev; + if (!vdev) { + tdls_err("NULL vdev object"); + qdf_mem_free(req); + return QDF_STATUS_E_NULL_VALUE; + } + + /* reference cnt is acquired in ucfg_tdls_oper */ + vdev_obj = wlan_vdev_get_tdls_vdev_obj(req->vdev); + soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK", soc_obj, vdev_obj); + status = QDF_STATUS_E_INVAL; + goto error; + } + + feature = soc_obj->tdls_configs.tdls_feature_flags; + if (!(TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature) || + TDLS_IS_LIBERAL_EXTERNAL_CONTROL_ENABLED(feature)) || + !TDLS_IS_IMPLICIT_TRIG_ENABLED(feature)) { + tdls_err("TDLS ext ctrl or Imp Trig not enabled, %x", feature); + status = QDF_STATUS_E_NOSUPPORT; + goto error; + } + + peer = tdls_find_peer(vdev_obj, macaddr); + if (!peer) { + tdls_err("peer matching " QDF_MAC_ADDR_FMT " not found", + QDF_MAC_ADDR_REF(macaddr)); + status = QDF_STATUS_E_NULL_VALUE; + goto error; + } + if (peer->link_status == TDLS_LINK_CONNECTED) + tdls_set_peer_link_status(peer, TDLS_LINK_TEARING, + TDLS_LINK_UNSPECIFIED); + + if (soc_obj->tdls_dp_vdev_update) + soc_obj->tdls_dp_vdev_update( + &soc_obj->soc, + wlan_vdev_get_id(vdev), + soc_obj->tdls_update_dp_vdev_flags, + false); + + if (soc_obj->tdls_event_cb) { + qdf_mem_copy(ind.peer_mac, macaddr, QDF_MAC_ADDR_SIZE); + ind.vdev = vdev; + ind.reason = TDLS_TEARDOWN_PEER_UNSPEC_REASON; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_TEARDOWN_REQ, &ind); + } + + status = tdls_set_force_peer(vdev_obj, macaddr, false); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("set force peer failed"); + status = QDF_STATUS_E_INVAL; + goto error; + } + + if (soc_obj->tdls_external_peer_count) + soc_obj->tdls_external_peer_count--; + + tdls_set_callback(peer, NULL); + peer_update_param = qdf_mem_malloc(sizeof(*peer_update_param)); + if (!peer_update_param) { + tdls_err("memory allocation failed"); + status = QDF_STATUS_E_NOMEM; + goto error; + } + + peer_update_param->vdev_id = wlan_vdev_get_id(vdev); + qdf_mem_copy(peer_update_param->peer_macaddr, + macaddr, QDF_MAC_ADDR_SIZE); + peer_update_param->peer_state = TDLS_PEER_REMOVE_MAC_ADDR; + status = tdls_wma_update_peer_state(soc_obj, peer_update_param); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(peer_update_param); + goto error; + } + tdls_set_ct_mode(soc_obj->soc); + if (!soc_obj->enable_tdls_connection_tracker) + tdls_implicit_disable(vdev_obj); + +error: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + + return status; +} + +static const char *tdls_evt_to_str(enum tdls_event_msg_type type) +{ + switch (type) { + case TDLS_SHOULD_DISCOVER: + return "SHOULD_DISCOVER"; + case TDLS_SHOULD_TEARDOWN: + return "SHOULD_TEARDOWN"; + case TDLS_PEER_DISCONNECTED: + return "SHOULD_PEER_DISCONNECTED"; + case TDLS_CONNECTION_TRACKER_NOTIFY: + return "CONNECTION_TRACKER_NOTIFICATION"; + default: + return "INVALID_TYPE"; + } +} + +QDF_STATUS tdls_process_should_discover(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_peer *curr_peer; + uint32_t feature; + uint16_t type; + + /*TODO ignore this if any concurrency detected*/ + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + type = evt->message_type; + + tdls_debug("TDLS %s: " QDF_MAC_ADDR_FMT "reason %d", + tdls_evt_to_str(type), + QDF_MAC_ADDR_REF(evt->peermac.bytes), + evt->peer_reason); + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK, ignore %s", + soc_obj, vdev_obj, tdls_evt_to_str(type)); + return QDF_STATUS_E_NULL_VALUE; + } + if (soc_obj->tdls_nss_switch_in_progress) { + tdls_err("TDLS antenna switching, ignore %s", + tdls_evt_to_str(type)); + return QDF_STATUS_SUCCESS; + } + + curr_peer = tdls_get_peer(vdev_obj, evt->peermac.bytes); + if (!curr_peer) { + tdls_notice("curr_peer is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (TDLS_LINK_CONNECTED == curr_peer->link_status) { + tdls_err("TDLS link status is connected, ignore"); + return QDF_STATUS_SUCCESS; + } + + feature = soc_obj->tdls_configs.tdls_feature_flags; + if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature) && + !curr_peer->is_forced_peer) { + tdls_debug("curr_peer is not forced, ignore %s", + tdls_evt_to_str(type)); + return QDF_STATUS_SUCCESS; + } + + tdls_debug("initiate TDLS setup on %s, ext: %d, force: %d, reason: %d", + tdls_evt_to_str(type), + TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature), + curr_peer->is_forced_peer, evt->peer_reason); + vdev_obj->curr_candidate = curr_peer; + tdls_implicit_send_discovery_request(vdev_obj); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_process_should_teardown(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_peer *curr_peer; + uint32_t reason; + uint16_t type; + + type = evt->message_type; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + + tdls_debug("TDLS %s: " QDF_MAC_ADDR_FMT "reason %d", + tdls_evt_to_str(type), + QDF_MAC_ADDR_REF(evt->peermac.bytes), evt->peer_reason); + + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK, ignore %s", + soc_obj, vdev_obj, tdls_evt_to_str(type)); + return QDF_STATUS_E_NULL_VALUE; + } + + curr_peer = tdls_find_peer(vdev_obj, evt->peermac.bytes); + if (!curr_peer) { + tdls_notice("curr_peer is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + reason = evt->peer_reason; + if (TDLS_LINK_CONNECTED == curr_peer->link_status) { + tdls_err("%s reason: %d for" QDF_MAC_ADDR_FMT, + tdls_evt_to_str(type), evt->peer_reason, + QDF_MAC_ADDR_REF(evt->peermac.bytes)); + if (reason == TDLS_TEARDOWN_RSSI || + reason == TDLS_DISCONNECTED_PEER_DELETE || + reason == TDLS_TEARDOWN_PTR_TIMEOUT || + reason == TDLS_TEARDOWN_NO_RSP) + reason = TDLS_TEARDOWN_PEER_UNREACHABLE; + else + reason = TDLS_TEARDOWN_PEER_UNSPEC_REASON; + + tdls_indicate_teardown(vdev_obj, curr_peer, reason); + } else { + tdls_err("TDLS link is not connected, ignore %s", + tdls_evt_to_str(type)); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_process_connection_tracker_notify(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + uint16_t type; + + type = evt->message_type; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK, ignore %s", + soc_obj, vdev_obj, tdls_evt_to_str(type)); + return QDF_STATUS_E_NULL_VALUE; + } + + /*TODO connection tracker update*/ + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_process_set_responder() - Set/clear TDLS peer's responder role + * @set_req: set responder request + * + * Return: 0 for success or -EINVAL otherwise + */ +static +int tdls_process_set_responder(struct tdls_set_responder_req *set_req) +{ + struct tdls_peer *curr_peer; + struct tdls_vdev_priv_obj *tdls_vdev; + + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(set_req->vdev); + if (!tdls_vdev) { + tdls_err("tdls vdev obj is NULL"); + return -EINVAL; + } + curr_peer = tdls_get_peer(tdls_vdev, set_req->peer_mac); + if (!curr_peer) { + tdls_err("curr_peer is NULL"); + return -EINVAL; + } + + curr_peer->is_responder = set_req->responder; + return 0; +} + + +/** + * tdls_set_responder() - Set/clear TDLS peer's responder role + * @set_req: set responder request + * + * Return: 0 for success or -EINVAL otherwise + */ +int tdls_set_responder(struct tdls_set_responder_req *set_req) +{ + QDF_STATUS status; + + if (!set_req || !set_req->vdev) { + tdls_err("Invalid input params %pK", set_req); + return -EINVAL; + } + + status = wlan_objmgr_vdev_try_get_ref(set_req->vdev, WLAN_TDLS_NB_ID); + if (QDF_STATUS_SUCCESS != status) { + tdls_err("vdev object is deleted"); + return -EINVAL; + } + + status = tdls_process_set_responder(set_req); + + wlan_objmgr_vdev_release_ref(set_req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(set_req); + return status; +} + +static int tdls_teardown_links(struct tdls_soc_priv_obj *soc_obj, uint32_t mode) +{ + uint8_t staidx; + struct tdls_peer *curr_peer; + struct tdls_conn_info *conn_rec; + int ret = 0; + + conn_rec = soc_obj->tdls_conn_info; + for (staidx = 0; staidx < soc_obj->max_num_tdls_sta; staidx++) { + if (!conn_rec[staidx].valid_entry) + continue; + + curr_peer = tdls_find_all_peer(soc_obj, + conn_rec[staidx].peer_mac.bytes); + if (!curr_peer) + continue; + + /* if supported only 1x1, skip it */ + if (curr_peer->spatial_streams == HW_MODE_SS_1x1) + continue; + + tdls_debug("Indicate TDLS teardown peer bssid " + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF( + curr_peer->peer_mac.bytes)); + tdls_indicate_teardown(curr_peer->vdev_priv, curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + + soc_obj->tdls_teardown_peers_cnt++; + } + + if (soc_obj->tdls_teardown_peers_cnt >= 1) { + soc_obj->tdls_nss_switch_in_progress = true; + tdls_debug("TDLS peers to be torn down = %d", + soc_obj->tdls_teardown_peers_cnt); + + /* set the antenna switch transition mode */ + if (mode == HW_MODE_SS_1x1) { + soc_obj->tdls_nss_transition_mode = + TDLS_NSS_TRANSITION_S_2x2_to_1x1; + ret = -EAGAIN; + } else { + soc_obj->tdls_nss_transition_mode = + TDLS_NSS_TRANSITION_S_1x1_to_2x2; + ret = 0; + } + tdls_debug("TDLS teardown for antenna switch operation starts"); + } + + return ret; +} + +QDF_STATUS tdls_process_antenna_switch(struct tdls_antenna_switch_request *req) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct wlan_objmgr_vdev *vdev = NULL; + uint32_t vdev_nss; + int ant_switch_state = 0; + uint32_t vdev_id; + enum QDF_OPMODE opmode; + uint8_t channel; + struct tdls_osif_indication ind; + + if (!req) { + tdls_err("null req"); + return QDF_STATUS_E_INVAL; + } + + vdev = req->vdev; + if (!vdev) { + tdls_err("null vdev"); + qdf_mem_free(req); + return QDF_STATUS_E_INVAL; + } + + status = tdls_get_vdev_objects(vdev, &vdev_obj, &soc_obj); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev_obj & soc_obj"); + goto get_obj_err; + } + + if (soc_obj->connected_peer_count == 0) + goto ant_sw_done; + + if (soc_obj->tdls_nss_switch_in_progress) { + if (!soc_obj->tdls_nss_teardown_complete) { + tdls_err("TDLS antenna switch is in progress"); + goto ant_sw_in_progress; + } else { + goto ant_sw_done; + } + } + + vdev_id = wlan_vdev_get_id(vdev); + opmode = wlan_vdev_mlme_get_opmode(vdev); + channel = wlan_freq_to_chan( + policy_mgr_get_channel( + soc_obj->soc, + policy_mgr_convert_device_mode_to_qdf_type(opmode), + &vdev_id)); + + /* Check supported nss for TDLS, if is 1x1, no need to teardown links */ + if (WLAN_REG_IS_24GHZ_CH(channel)) + vdev_nss = soc_obj->tdls_configs.tdls_vdev_nss_2g; + else + vdev_nss = soc_obj->tdls_configs.tdls_vdev_nss_5g; + + if (vdev_nss == HW_MODE_SS_1x1) { + tdls_debug("Supported NSS is 1x1, no need to teardown TDLS links"); + goto ant_sw_done; + } + + if (tdls_teardown_links(soc_obj, req->mode) == 0) + goto ant_sw_done; + +ant_sw_in_progress: + ant_switch_state = -EAGAIN; +ant_sw_done: + if (soc_obj->tdls_event_cb) { + ind.vdev = vdev; + ind.status = ant_switch_state; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ANTENNA_SWITCH, &ind); + } + + if (soc_obj->tdls_nss_switch_in_progress && + soc_obj->tdls_nss_teardown_complete) { + soc_obj->tdls_nss_switch_in_progress = false; + soc_obj->tdls_nss_teardown_complete = false; + } + tdls_debug("tdls_nss_switch_in_progress: %d tdls_nss_teardown_complete: %d", + soc_obj->tdls_nss_switch_in_progress, + soc_obj->tdls_nss_teardown_complete); + +get_obj_err: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + + return status; +} + +QDF_STATUS tdls_antenna_switch_flush_callback(struct scheduler_msg *msg) +{ + struct tdls_antenna_switch_request *req; + + if (!msg || !msg->bodyptr) { + tdls_err("msg: 0x%pK", msg); + return QDF_STATUS_E_NULL_VALUE; + } + req = msg->bodyptr; + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + + return QDF_STATUS_SUCCESS; +} + +void wlan_tdls_offchan_parms_callback(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + tdls_err("vdev is NULL"); + return; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +} + +int tdls_process_set_offchannel(struct tdls_set_offchannel *req) +{ + int status; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + if (tdls_get_vdev_objects(req->vdev, &tdls_vdev_obj, &tdls_soc_obj) != + QDF_STATUS_SUCCESS) { + status = -ENOTSUPP; + goto free; + } + + tdls_debug("TDLS offchannel to be configured %d", req->offchannel); + + if (req->offchannel) + status = tdls_set_tdls_offchannel(tdls_soc_obj, + req->offchannel); + else + status = -ENOTSUPP; + +free: + + if (req->callback) + req->callback(req->vdev); + qdf_mem_free(req); + + return status; +} + +int tdls_process_set_offchan_mode(struct tdls_set_offchanmode *req) +{ + int status; + + tdls_debug("TDLS offchan mode to be configured %d", req->offchan_mode); + status = tdls_set_tdls_offchannelmode(req->vdev, req->offchan_mode); + + if (req->callback) + req->callback(req->vdev); + qdf_mem_free(req); + + return status; +} + +int tdls_process_set_secoffchanneloffset( + struct tdls_set_secoffchanneloffset *req) +{ + int status; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + if (tdls_get_vdev_objects(req->vdev, &tdls_vdev_obj, &tdls_soc_obj) != + QDF_STATUS_SUCCESS) { + status = -ENOTSUPP; + goto free; + } + + tdls_debug("TDLS offchannel offset to be configured %d", + req->offchan_offset); + status = tdls_set_tdls_secoffchanneloffset(tdls_soc_obj, + req->offchan_offset); + +free: + + if (req->callback) + req->callback(req->vdev); + qdf_mem_free(req); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_cmds_process.h b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_cmds_process.h new file mode 100644 index 0000000000000000000000000000000000000000..c51b3b0a501ec7e80cdb4632eca37526fadba892 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_cmds_process.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_cmds_process.h + * + * TDLS north bound commands include file + */ + +#ifndef _WLAN_TDLS_CMDS_PROCESS_H_ +#define _WLAN_TDLS_CMDS_PROCESS_H_ + +#define TDLS_IS_SETUP_ACTION(action) \ + ((TDLS_SETUP_REQUEST <= action) && \ + (TDLS_SETUP_CONFIRM >= action)) + +/** + * tdls_process_add_peer() - add TDLS peer + * @req: TDLS add peer request + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed + */ +QDF_STATUS tdls_process_add_peer(struct tdls_add_peer_request *req); + +/** + * tdls_process_del_peer() - del TDLS peer + * @req: TDLS del peer request + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed + */ +QDF_STATUS tdls_process_del_peer(struct tdls_oper_request *req); + +/** + * tdls_process_enable_link() - enable TDLS link + * @req: TDLS enable link request + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed + */ +QDF_STATUS tdls_process_enable_link(struct tdls_oper_request *req); + +/** + * tdls_process_setup_peer() - process configure an externally + * controllable TDLS peer + * @req: TDLS configure force peer request + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_process_setup_peer(struct tdls_oper_request *req); + +/** + * tdls_process_remove_force_peer() - process remove an externally controllable + * TDLS peer + * @req: TDLS operation request + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_process_remove_force_peer(struct tdls_oper_request *req); + +/** + * tdls_process_update_peer() - update TDLS peer + * @req: TDLS update peer request + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed + */ +QDF_STATUS tdls_process_update_peer(struct tdls_update_peer_request *req); + +/** + * tdls_process_antenna_switch() - handle TDLS antenna switch + * @req: TDLS antenna switch request + * + * Rely on callback to indicate the antenna switch state to caller. + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed. + */ +QDF_STATUS tdls_process_antenna_switch(struct tdls_antenna_switch_request *req); + +/** + * tdls_antenna_switch_flush_callback() - flush TDLS antenna switch request + * @msg: scheduler message contains tdls antenna switch event + * + * This function call is invoked when scheduler thread is going down + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_antenna_switch_flush_callback(struct scheduler_msg *msg); + +/** + * tdls_pe_del_peer() - send TDLS delete peer request to PE + * @req: TDLS delete peer request + * + * Return: QDF status + */ +QDF_STATUS tdls_pe_del_peer(struct tdls_del_peer_request *req); + +/** + * tdls_process_add_peer_rsp() - handle response for add or update TDLS peer + * @rsp: TDLS add peer response + * + * Return: QDF status + */ +QDF_STATUS tdls_process_add_peer_rsp(struct tdls_add_sta_rsp *rsp); + +/** + * tdls_reset_nss() - reset tdls nss parameters + * @tdls_soc: TDLS soc object + * @action_code: action code + * + * Return: None + */ +void tdls_reset_nss(struct tdls_soc_priv_obj *tdls_soc, + uint8_t action_code); + +/** + * tdls_release_serialization_command() - TDLS wrapper to + * relases serialization command. + * @vdev: Object manager vdev + * @type: command to release. + * + * Return: None + */ + +void +tdls_release_serialization_command(struct wlan_objmgr_vdev *vdev, + enum wlan_serialization_cmd_type type); + +/** + * tdls_set_cap() - set TDLS capability type + * @tdls_vdev: tdls vdev object + * @mac: peer mac address + * @cap: TDLS capability type + * + * Return: 0 if successful or negative errno otherwise + */ +int tdls_set_cap(struct tdls_vdev_priv_obj *tdls_vdev, const uint8_t *mac, + enum tdls_peer_capab cap); + +/** + * tdls_process_send_mgmt_rsp() - handle response for send mgmt + * @rsp: TDLS send mgmt response + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_process_send_mgmt_rsp(struct tdls_send_mgmt_rsp *rsp); + +/** + * tdls_send_mgmt_tx_completion() - process tx completion + * @tx_complete: TDLS mgmt completion info + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_send_mgmt_tx_completion( + struct tdls_mgmt_tx_completion_ind *tx_complete); + +/** + * tdls_process_add_peer_rsp() - handle response for delete TDLS peer + * @rsp: TDLS delete peer response + * + * Return: QDF status + */ +QDF_STATUS tdls_process_del_peer_rsp(struct tdls_del_sta_rsp *rsp); + +/** + * tdls_process_should_discover() - handle tdls should_discover event + * @vdev: vdev object + * @evt: event info + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_should_discover(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt); + +/** + * tdls_process_should_teardown() - handle tdls should_teardown event + * @vdev: vdev object + * @evt: event info + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_should_teardown(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt); + +/** + * tdls_process_connection_tracker_notify() -handle tdls connect tracker notify + * @vdev: vdev object + * @evt: event info + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_connection_tracker_notify(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt); + +/** + * tdls_validate_mgmt_request() -validate mgmt request + * @tdls_validate: action frame request + * + * Return: 0 for success or -EINVAL otherwise + */ +int tdls_validate_mgmt_request(struct tdls_action_frame_request *tdls_mgmt_req); + +/** + * tdls_set_responder() - Set/clear TDLS peer's responder role + * @set_req: set responder request + * + * Return: 0 for success or -EINVAL otherwise + */ +int tdls_set_responder(struct tdls_set_responder_req *set_req); + +/** + * tdls_decrement_peer_count() - decrement connected TDLS peer counter + * @soc_obj: TDLS soc object + * + * Used in scheduler thread context, no lock needed. + * + * Return: None. + */ +void tdls_decrement_peer_count(struct tdls_soc_priv_obj *soc_obj); + +/** + * wlan_tdls_offchan_parms_callback() - Callback to release ref count + * @vdev: vdev object + * + * Return: none + */ +void wlan_tdls_offchan_parms_callback(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_process_set_offchannel() - Handle set offchannel request for TDLS + * @req: TDLS set offchannel request + * + * Return: int status + */ +int tdls_process_set_offchannel(struct tdls_set_offchannel *req); + +/** + * tdls_process_set_offchan_mode() - Handle set offchan mode request for TDLS + * @req: TDLS set offchannel mode request + * + * Return: int status + */ +int tdls_process_set_offchan_mode(struct tdls_set_offchanmode *req); + +/** + * tdls_process_set_secoffchanneloffset() - Handle set sec offchannel + * offset request for TDLS + * @req: TDLS set secoffchannel offchannel request + * + * Return: int status + */ +int tdls_process_set_secoffchanneloffset( + struct tdls_set_secoffchanneloffset *req); + +#endif diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_ct.c b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_ct.c new file mode 100644 index 0000000000000000000000000000000000000000..291f76e46c3cf09b1fbba606f6b29c6b429c01f8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_ct.c @@ -0,0 +1,1345 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_ct.c + * + * TDLS connection tracker function definitions + */ + +#include "wlan_tdls_main.h" +#include "wlan_tdls_peer.h" +#include "wlan_tdls_ct.h" +#include "wlan_tdls_cmds_process.h" + +bool tdls_is_vdev_authenticated(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_peer *peer; + bool is_authenticated = false; + + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_TDLS_NB_ID); + if (!peer) { + tdls_err("peer is null"); + return false; + } + + is_authenticated = wlan_peer_mlme_get_auth_state(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); + return is_authenticated; +} + +/** + * tdls_peer_reset_discovery_processed() - reset discovery status + * @tdls_vdev: TDLS vdev object + * + * This function resets discovery processing bit for all TDLS peers + * + * Caller has to take the lock before calling this function + * + * Return: 0 + */ +static int32_t tdls_peer_reset_discovery_processed( + struct tdls_vdev_priv_obj *tdls_vdev) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *peer; + QDF_STATUS status; + + tdls_vdev->discovery_peer_cnt = 0; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->peer_list[i]; + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + peer->discovery_processed = 0; + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + + return 0; +} + +void tdls_discovery_timeout_peer_cb(void *user_data) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *peer; + QDF_STATUS status; + struct tdls_vdev_priv_obj *tdls_vdev; + struct wlan_objmgr_vdev *vdev; + + if (!user_data) { + tdls_err("discovery time out data is null"); + return; + } + + vdev = tdls_get_vdev(user_data, WLAN_TDLS_NB_ID); + if (!vdev) + return; + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->peer_list[i]; + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, + node); + if (TDLS_LINK_DISCOVERING != peer->link_status) { + status = qdf_list_peek_next(head, p_node, + &p_node); + continue; + } + tdls_debug(QDF_MAC_ADDR_FMT " to idle state", + QDF_MAC_ADDR_REF(peer->peer_mac.bytes)); + tdls_set_peer_link_status(peer, + TDLS_LINK_IDLE, + TDLS_LINK_NOT_SUPPORTED); + } + } + tdls_vdev->discovery_sent_cnt = 0; + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + + /* add tdls power save prohibited */ + + return; +} + +/** + * tdls_reset_tx_rx() - reset tx/rx counters for all tdls peers + * @tdls_vdev: TDLS vdev object + * + * Caller has to take the TDLS lock before calling this function + * + * Return: Void + */ +static void tdls_reset_tx_rx(struct tdls_vdev_priv_obj *tdls_vdev) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *peer; + QDF_STATUS status; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->peer_list[i]; + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + peer->tx_pkt = 0; + peer->rx_pkt = 0; + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + return; +} + +void tdls_implicit_disable(struct tdls_vdev_priv_obj *tdls_vdev) +{ + tdls_debug("Disable Implicit TDLS"); + tdls_timers_stop(tdls_vdev); +} + +/** + * tdls_implicit_enable() - enable implicit tdls triggering + * @tdls_vdev: TDLS vdev + * + * Return: Void + */ +void tdls_implicit_enable(struct tdls_vdev_priv_obj *tdls_vdev) +{ + tdls_debug("Enable Implicit TDLS"); + if (!tdls_vdev) + return; + + tdls_peer_reset_discovery_processed(tdls_vdev); + tdls_reset_tx_rx(tdls_vdev); + /* TODO check whether tdls power save prohibited */ + + /* Restart the connection tracker timer */ + tdls_timer_restart(tdls_vdev->vdev, &tdls_vdev->peer_update_timer, + tdls_vdev->threshold_config.tx_period_t); +} + +/** + * tdls_ct_sampling_tx_rx() - collect tx/rx traffic sample + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * Function to update data traffic information in tdls connection + * tracker data structure for connection tracker operation + * + * Return: None + */ +static void tdls_ct_sampling_tx_rx(struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdls_soc) +{ + struct tdls_peer *curr_peer; + uint8_t mac[QDF_MAC_ADDR_SIZE]; + uint8_t mac_cnt; + uint8_t mac_entries; + struct tdls_conn_tracker_mac_table mac_table[WLAN_TDLS_CT_TABLE_SIZE]; + + qdf_spin_lock_bh(&tdls_soc->tdls_ct_spinlock); + + if (0 == tdls_vdev->valid_mac_entries) { + qdf_spin_unlock_bh(&tdls_soc->tdls_ct_spinlock); + return; + } + + mac_entries = QDF_MIN(tdls_vdev->valid_mac_entries, + WLAN_TDLS_CT_TABLE_SIZE); + + qdf_mem_copy(mac_table, tdls_vdev->ct_peer_table, + (sizeof(struct tdls_conn_tracker_mac_table)) * mac_entries); + + qdf_mem_zero(tdls_vdev->ct_peer_table, + (sizeof(struct tdls_conn_tracker_mac_table)) * mac_entries); + + tdls_vdev->valid_mac_entries = 0; + + qdf_spin_unlock_bh(&tdls_soc->tdls_ct_spinlock); + + for (mac_cnt = 0; mac_cnt < mac_entries; mac_cnt++) { + qdf_mem_copy(mac, mac_table[mac_cnt].mac_address.bytes, + QDF_MAC_ADDR_SIZE); + curr_peer = tdls_get_peer(tdls_vdev, mac); + if (curr_peer) { + curr_peer->tx_pkt = + mac_table[mac_cnt].tx_packet_cnt; + curr_peer->rx_pkt = + mac_table[mac_cnt].rx_packet_cnt; + } + } +} + +void tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr, + struct qdf_mac_addr *dest_mac_addr) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint8_t mac_cnt; + uint8_t valid_mac_entries; + struct tdls_conn_tracker_mac_table *mac_table; + struct wlan_objmgr_peer *bss_peer; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) + return; + + if (!tdls_soc_obj->enable_tdls_connection_tracker) + return; + + if (qdf_is_macaddr_group(mac_addr)) + return; + + if (qdf_is_macaddr_group(dest_mac_addr)) + return; + + if (!qdf_mem_cmp(vdev->vdev_mlme.macaddr, mac_addr, + QDF_MAC_ADDR_SIZE)) + return; + + bss_peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_TDLS_NB_ID); + if (bss_peer) { + if (!qdf_mem_cmp(bss_peer->macaddr, mac_addr, + QDF_MAC_ADDR_SIZE)) { + wlan_objmgr_peer_release_ref(bss_peer, WLAN_TDLS_NB_ID); + return; + } + wlan_objmgr_peer_release_ref(bss_peer, WLAN_TDLS_NB_ID); + } + qdf_spin_lock_bh(&tdls_soc_obj->tdls_ct_spinlock); + valid_mac_entries = tdls_vdev_obj->valid_mac_entries; + mac_table = tdls_vdev_obj->ct_peer_table; + + for (mac_cnt = 0; mac_cnt < valid_mac_entries; mac_cnt++) { + if (qdf_mem_cmp(mac_table[mac_cnt].mac_address.bytes, + mac_addr, QDF_MAC_ADDR_SIZE) == 0) { + mac_table[mac_cnt].rx_packet_cnt++; + goto rx_cnt_return; + } + } + + /* If we have more than 8 peers within 30 mins. we will + * stop tracking till the old entries are removed + */ + if (mac_cnt < WLAN_TDLS_CT_TABLE_SIZE) { + qdf_mem_copy(mac_table[mac_cnt].mac_address.bytes, + mac_addr, QDF_MAC_ADDR_SIZE); + tdls_vdev_obj->valid_mac_entries = mac_cnt+1; + mac_table[mac_cnt].rx_packet_cnt = 1; + } + +rx_cnt_return: + qdf_spin_unlock_bh(&tdls_soc_obj->tdls_ct_spinlock); + return; +} + +void tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint8_t mac_cnt; + uint8_t valid_mac_entries; + struct tdls_conn_tracker_mac_table *mac_table; + struct wlan_objmgr_peer *bss_peer; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) + return; + + if (!tdls_soc_obj->enable_tdls_connection_tracker) + return; + + if (qdf_is_macaddr_group(mac_addr)) + return; + + if (!qdf_mem_cmp(vdev->vdev_mlme.macaddr, mac_addr, + QDF_MAC_ADDR_SIZE)) + return; + bss_peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_TDLS_NB_ID); + if (bss_peer) { + if (!qdf_mem_cmp(bss_peer->macaddr, mac_addr, + QDF_MAC_ADDR_SIZE)) { + wlan_objmgr_peer_release_ref(bss_peer, WLAN_TDLS_NB_ID); + return; + } + wlan_objmgr_peer_release_ref(bss_peer, WLAN_TDLS_NB_ID); + } + + qdf_spin_lock_bh(&tdls_soc_obj->tdls_ct_spinlock); + mac_table = tdls_vdev_obj->ct_peer_table; + + valid_mac_entries = tdls_vdev_obj->valid_mac_entries; + + for (mac_cnt = 0; mac_cnt < valid_mac_entries; mac_cnt++) { + if (qdf_mem_cmp(mac_table[mac_cnt].mac_address.bytes, + mac_addr, QDF_MAC_ADDR_SIZE) == 0) { + mac_table[mac_cnt].tx_packet_cnt++; + goto tx_cnt_return; + } + } + + /* If we have more than 8 peers within 30 mins. we will + * stop tracking till the old entries are removed + */ + if (mac_cnt < WLAN_TDLS_CT_TABLE_SIZE) { + qdf_mem_copy(mac_table[mac_cnt].mac_address.bytes, + mac_addr, QDF_MAC_ADDR_SIZE); + mac_table[mac_cnt].tx_packet_cnt = 1; + tdls_vdev_obj->valid_mac_entries++; + } + +tx_cnt_return: + qdf_spin_unlock_bh(&tdls_soc_obj->tdls_ct_spinlock); + return; +} + +void tdls_implicit_send_discovery_request( + struct tdls_vdev_priv_obj *tdls_vdev_obj) +{ + struct tdls_peer *curr_peer; + struct tdls_peer *temp_peer; + struct tdls_soc_priv_obj *tdls_psoc; + struct tdls_osif_indication tdls_ind; + + if (!tdls_vdev_obj) { + tdls_notice("tdls_vdev_obj is NULL"); + return; + } + + tdls_psoc = wlan_vdev_get_tdls_soc_obj(tdls_vdev_obj->vdev); + + if (!tdls_psoc) { + tdls_notice("tdls_psoc_obj is NULL"); + return; + } + + curr_peer = tdls_vdev_obj->curr_candidate; + + if (!curr_peer) { + tdls_err("curr_peer is NULL"); + return; + } + + /* This function is called in mutex_lock */ + temp_peer = tdls_is_progress(tdls_vdev_obj, NULL, 0); + if (temp_peer) { + tdls_notice(QDF_MAC_ADDR_FMT " ongoing. pre_setup ignored", + QDF_MAC_ADDR_REF(temp_peer->peer_mac.bytes)); + goto done; + } + + if (TDLS_CAP_UNKNOWN != curr_peer->tdls_support) + tdls_set_peer_link_status(curr_peer, + TDLS_LINK_DISCOVERING, + TDLS_LINK_SUCCESS); + + qdf_mem_copy(tdls_ind.peer_mac, curr_peer->peer_mac.bytes, + QDF_MAC_ADDR_SIZE); + + tdls_ind.vdev = tdls_vdev_obj->vdev; + + tdls_debug("Implicit TDLS, Send Discovery request event"); + + tdls_psoc->tdls_event_cb(tdls_psoc->tdls_evt_cb_data, + TDLS_EVENT_DISCOVERY_REQ, &tdls_ind); + + tdls_vdev_obj->discovery_sent_cnt++; + + tdls_timer_restart(tdls_vdev_obj->vdev, + &tdls_vdev_obj->peer_discovery_timer, + tdls_vdev_obj->threshold_config.tx_period_t - + TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE); + + tdls_debug("discovery count %u timeout %u msec", + tdls_vdev_obj->discovery_sent_cnt, + tdls_vdev_obj->threshold_config.tx_period_t - + TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE); +done: + tdls_vdev_obj->curr_candidate = NULL; + tdls_vdev_obj->magic = 0; + return; +} + +int tdls_recv_discovery_resp(struct tdls_vdev_priv_obj *tdls_vdev, + const uint8_t *mac) +{ + struct tdls_peer *curr_peer; + struct tdls_soc_priv_obj *tdls_soc; + struct tdls_osif_indication indication; + struct tdls_config_params *tdls_cfg; + int status = 0; + + if (!tdls_vdev) + return -EINVAL; + + tdls_soc = wlan_vdev_get_tdls_soc_obj(tdls_vdev->vdev); + if (!tdls_soc) { + tdls_err("tdls soc is NULL"); + return -EINVAL; + } + + curr_peer = tdls_get_peer(tdls_vdev, mac); + if (!curr_peer) { + tdls_err("curr_peer is NULL"); + return -EINVAL; + } + + if (tdls_vdev->discovery_sent_cnt) + tdls_vdev->discovery_sent_cnt--; + + if (0 == tdls_vdev->discovery_sent_cnt) + qdf_mc_timer_stop(&tdls_vdev->peer_discovery_timer); + + tdls_debug("Discovery(%u) Response from " QDF_MAC_ADDR_FMT + " link_status %d", tdls_vdev->discovery_sent_cnt, + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes), + curr_peer->link_status); + + tdls_cfg = &tdls_vdev->threshold_config; + if (TDLS_LINK_DISCOVERING == curr_peer->link_status) { + /* Since we are here, it means Throughput threshold is + * already met. Make sure RSSI threshold is also met + * before setting up TDLS link. + */ + if ((int32_t) curr_peer->rssi > + (int32_t) tdls_cfg->rssi_trigger_threshold) { + tdls_set_peer_link_status(curr_peer, + TDLS_LINK_DISCOVERED, + TDLS_LINK_SUCCESS); + tdls_debug("Rssi Threshold met: " QDF_MAC_ADDR_FMT + " rssi = %d threshold= %d", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes), + curr_peer->rssi, + tdls_cfg->rssi_trigger_threshold); + + qdf_mem_copy(indication.peer_mac, mac, + QDF_MAC_ADDR_SIZE); + + indication.vdev = tdls_vdev->vdev; + + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_SETUP_REQ, + &indication); + } else { + tdls_debug("Rssi Threshold not met: " QDF_MAC_ADDR_FMT + " rssi = %d threshold = %d ", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes), + curr_peer->rssi, + tdls_cfg->rssi_trigger_threshold); + + tdls_set_peer_link_status(curr_peer, + TDLS_LINK_IDLE, + TDLS_LINK_UNSPECIFIED); + + /* if RSSI threshold is not met then allow + * further discovery attempts by decrementing + * count for the last attempt + */ + if (curr_peer->discovery_attempt) + curr_peer->discovery_attempt--; + } + } + + curr_peer->tdls_support = TDLS_CAP_SUPPORTED; + + return status; +} + +void tdls_indicate_teardown(struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_peer *curr_peer, + uint16_t reason) +{ + struct tdls_soc_priv_obj *tdls_soc; + struct tdls_osif_indication indication; + + if (!tdls_vdev || !curr_peer) { + tdls_err("tdls_vdev: %pK, curr_peer: %pK", + tdls_vdev, curr_peer); + return; + } + + tdls_soc = wlan_vdev_get_tdls_soc_obj(tdls_vdev->vdev); + if (!tdls_soc) { + tdls_err("tdls_soc: %pK", tdls_soc); + return; + } + + if (curr_peer->link_status != TDLS_LINK_CONNECTED) { + tdls_err("link state %d peer:" QDF_MAC_ADDR_FMT, + curr_peer->link_status, + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); + return; + } + + tdls_set_peer_link_status(curr_peer, + TDLS_LINK_TEARING, + TDLS_LINK_UNSPECIFIED); + tdls_notice("Teardown reason %d", reason); + + if (tdls_soc->tdls_dp_vdev_update) + tdls_soc->tdls_dp_vdev_update( + &tdls_soc->soc, + wlan_vdev_get_id(tdls_vdev->vdev), + tdls_soc->tdls_update_dp_vdev_flags, + false); + + indication.reason = reason; + indication.vdev = tdls_vdev->vdev; + qdf_mem_copy(indication.peer_mac, curr_peer->peer_mac.bytes, + QDF_MAC_ADDR_SIZE); + + if (tdls_soc->tdls_event_cb) + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_TEARDOWN_REQ, &indication); +} + +/** + * tdls_get_conn_info() - get the tdls connection information. + * @tdls_soc: tdls soc object + * @peer_mac: peer MAC address + * + * Function to check tdls sta index + * + * Return: tdls connection information + */ +static struct tdls_conn_info * +tdls_get_conn_info(struct tdls_soc_priv_obj *tdls_soc, + struct qdf_mac_addr *peer_mac) +{ + uint8_t sta_idx; + /* check if there is available index for this new TDLS STA */ + for (sta_idx = 0; sta_idx < WLAN_TDLS_STA_MAX_NUM; sta_idx++) { + if (!qdf_mem_cmp( + tdls_soc->tdls_conn_info[sta_idx].peer_mac.bytes, + peer_mac->bytes, QDF_MAC_ADDR_SIZE)) { + tdls_debug("tdls peer exists idx %d " QDF_MAC_ADDR_FMT, + sta_idx, + QDF_MAC_ADDR_REF(peer_mac->bytes)); + tdls_soc->tdls_conn_info[sta_idx].index = sta_idx; + return &tdls_soc->tdls_conn_info[sta_idx]; + } + } + + tdls_err("tdls peer does not exists"); + return NULL; +} + +static void +tdls_ct_process_idle_handler(struct wlan_objmgr_vdev *vdev, + struct tdls_conn_info *tdls_info) +{ + struct tdls_peer *curr_peer; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) + return; + + if (!tdls_info->valid_entry) { + tdls_err("peer doesn't exists"); + return; + } + + curr_peer = tdls_find_peer(tdls_vdev_obj, + (u8 *) &tdls_info->peer_mac.bytes[0]); + + if (!curr_peer) { + tdls_err("Invalid tdls idle timer expired"); + return; + } + + tdls_debug(QDF_MAC_ADDR_FMT + " tx_pkt: %d, rx_pkt: %d, idle_packet_n: %d", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes), + curr_peer->tx_pkt, + curr_peer->rx_pkt, + tdls_vdev_obj->threshold_config.idle_packet_n); + + /* Check tx/rx statistics on this tdls link for recent activities and + * then decide whether to tear down the link or keep it. + */ + if ((curr_peer->tx_pkt >= + tdls_vdev_obj->threshold_config.idle_packet_n) || + (curr_peer->rx_pkt >= + tdls_vdev_obj->threshold_config.idle_packet_n)) { + /* this tdls link got back to normal, so keep it */ + tdls_debug("tdls link to " QDF_MAC_ADDR_FMT + " back to normal, will stay", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); + } else { + /* this tdls link needs to get torn down */ + tdls_notice("trigger tdls link to "QDF_MAC_ADDR_FMT" down", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); + tdls_indicate_teardown(tdls_vdev_obj, + curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + } + + return; +} + +void tdls_ct_idle_handler(void *user_data) +{ + struct wlan_objmgr_vdev *vdev; + struct tdls_conn_info *tdls_info; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint32_t idx; + + tdls_info = (struct tdls_conn_info *)user_data; + if (!tdls_info) + return; + + idx = tdls_info->index; + if (idx == INVALID_TDLS_PEER_INDEX || idx >= WLAN_TDLS_STA_MAX_NUM) { + tdls_debug("invalid peer index %d" QDF_MAC_ADDR_FMT, idx, + QDF_MAC_ADDR_REF(tdls_info->peer_mac.bytes)); + return; + } + + tdls_soc_obj = qdf_container_of(tdls_info, struct tdls_soc_priv_obj, + tdls_conn_info[idx]); + + vdev = tdls_get_vdev(tdls_soc_obj->soc, WLAN_TDLS_NB_ID); + if (!vdev) { + tdls_err("Unable to fetch the vdev"); + return; + } + + tdls_ct_process_idle_handler(vdev, tdls_info); + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); +} + +/** + * tdls_ct_process_idle_and_discovery() - process the traffic data + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * Function to check the peer traffic data in idle link and tdls + * discovering link + * + * Return: None + */ +static void +tdls_ct_process_idle_and_discovery(struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev_obj, + struct tdls_soc_priv_obj *tdls_soc_obj) +{ + uint16_t valid_peers; + + valid_peers = tdls_soc_obj->connected_peer_count; + + if ((curr_peer->tx_pkt + curr_peer->rx_pkt) >= + tdls_vdev_obj->threshold_config.tx_packet_n) { + if (WLAN_TDLS_STA_MAX_NUM > valid_peers) { + tdls_notice("Tput trigger TDLS pre-setup"); + tdls_vdev_obj->curr_candidate = curr_peer; + tdls_implicit_send_discovery_request(tdls_vdev_obj); + } else { + tdls_notice("Maximum peers connected already! %d", + valid_peers); + } + } +} + +/** + * tdls_ct_process_connected_link() - process the traffic + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev + * @tdls_soc_obj: tdls soc context + * + * Function to check the peer traffic data in active STA + * session + * + * Return: None + */ +static void tdls_ct_process_connected_link( + struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdls_soc) +{ + /* Don't trigger low rssi tear down here since FW will do it */ + /* Only teardown based on non zero idle packet threshold, to address + * a use case where this threshold does not get consider for TEAR DOWN + */ + if ((0 != tdls_vdev->threshold_config.idle_packet_n) && + ((curr_peer->tx_pkt < + tdls_vdev->threshold_config.idle_packet_n) && + (curr_peer->rx_pkt < + tdls_vdev->threshold_config.idle_packet_n))) { + if (!curr_peer->is_peer_idle_timer_initialised) { + struct tdls_conn_info *tdls_info; + tdls_info = tdls_get_conn_info(tdls_soc, + &curr_peer->peer_mac); + qdf_mc_timer_init(&curr_peer->peer_idle_timer, + QDF_TIMER_TYPE_SW, + tdls_ct_idle_handler, + (void *)tdls_info); + curr_peer->is_peer_idle_timer_initialised = true; + } + if (QDF_TIMER_STATE_RUNNING != + curr_peer->peer_idle_timer.state) { + tdls_warn("Tx/Rx Idle timer start: " + QDF_MAC_ADDR_FMT "!", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); + tdls_timer_restart(tdls_vdev->vdev, + &curr_peer->peer_idle_timer, + tdls_vdev->threshold_config.idle_timeout_t); + } + } else if (QDF_TIMER_STATE_RUNNING == + curr_peer->peer_idle_timer.state) { + tdls_warn("Tx/Rx Idle timer stop: " QDF_MAC_ADDR_FMT "!", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); + qdf_mc_timer_stop(&curr_peer->peer_idle_timer); + } +} + +/** + * tdls_ct_process_cap_supported() - process TDLS supported peer. + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev context + * @tdls_soc_obj: tdls soc context + * + * Function to check the peer traffic data for tdls supported peer + * + * Return: None + */ +static void tdls_ct_process_cap_supported(struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdls_soc_obj) +{ + if (curr_peer->rx_pkt || curr_peer->tx_pkt) + tdls_debug(QDF_MAC_ADDR_FMT "link_status %d tdls_support %d tx %d rx %d rssi %d", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes), + curr_peer->link_status, curr_peer->tdls_support, + curr_peer->tx_pkt, curr_peer->rx_pkt, + curr_peer->rssi); + + switch (curr_peer->link_status) { + case TDLS_LINK_IDLE: + case TDLS_LINK_DISCOVERING: + if (TDLS_IS_EXTERNAL_CONTROL_ENABLED( + tdls_soc_obj->tdls_configs.tdls_feature_flags) && + (!curr_peer->is_forced_peer)) + break; + tdls_ct_process_idle_and_discovery(curr_peer, tdls_vdev, + tdls_soc_obj); + break; + case TDLS_LINK_CONNECTED: + tdls_ct_process_connected_link(curr_peer, tdls_vdev, + tdls_soc_obj); + break; + default: + break; + } +} + +/** + * tdls_ct_process_cap_unknown() - process unknown peer + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * Function check the peer traffic data , when tdls capability is unknown + * + * Return: None + */ +static void tdls_ct_process_cap_unknown(struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdlsa_soc) +{ + if (TDLS_IS_EXTERNAL_CONTROL_ENABLED( + tdlsa_soc->tdls_configs.tdls_feature_flags) && + (!curr_peer->is_forced_peer)) + return; + + if (curr_peer->rx_pkt || curr_peer->tx_pkt) + tdls_debug(QDF_MAC_ADDR_FMT "link_status %d tdls_support %d tx %d rx %d", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes), + curr_peer->link_status, curr_peer->tdls_support, + curr_peer->tx_pkt, curr_peer->rx_pkt); + + if (!TDLS_IS_LINK_CONNECTED(curr_peer) && + ((curr_peer->tx_pkt + curr_peer->rx_pkt) >= + tdls_vdev->threshold_config.tx_packet_n)) { + /* Ignore discovery attempt if External Control is enabled, that + * is, peer is forced. In that case, continue discovery attempt + * regardless attempt count + */ + tdls_debug("TDLS UNKNOWN pre discover "); + if (curr_peer->is_forced_peer || + curr_peer->discovery_attempt++ < + tdls_vdev->threshold_config.discovery_tries_n) { + tdls_debug("TDLS UNKNOWN discover "); + tdls_vdev->curr_candidate = curr_peer; + tdls_implicit_send_discovery_request(tdls_vdev); + } else { + if (curr_peer->link_status != TDLS_LINK_CONNECTING) { + curr_peer->tdls_support = + TDLS_CAP_NOT_SUPPORTED; + tdls_set_peer_link_status( + curr_peer, + TDLS_LINK_IDLE, + TDLS_LINK_NOT_SUPPORTED); + } + } + } +} + +/** + * tdls_ct_process_peers() - process the peer + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * This function check the peer capability and process the metadata from + * the peer + * + * Return: None + */ +static void tdls_ct_process_peers(struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev_obj, + struct tdls_soc_priv_obj *tdls_soc_obj) +{ + switch (curr_peer->tdls_support) { + case TDLS_CAP_SUPPORTED: + tdls_ct_process_cap_supported(curr_peer, tdls_vdev_obj, + tdls_soc_obj); + break; + + case TDLS_CAP_UNKNOWN: + tdls_ct_process_cap_unknown(curr_peer, tdls_vdev_obj, + tdls_soc_obj); + break; + default: + break; + } + +} + +static void tdls_ct_process_handler(struct wlan_objmgr_vdev *vdev) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *list_node; + struct tdls_peer *curr_peer; + QDF_STATUS status; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) + return; + + /* If any concurrency is detected */ + if (!tdls_soc_obj->enable_tdls_connection_tracker) { + tdls_notice("Connection tracker is disabled"); + return; + } + + /* Update tx rx traffic sample in tdls data structures */ + tdls_ct_sampling_tx_rx(tdls_vdev_obj, tdls_soc_obj); + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev_obj->peer_list[i]; + status = qdf_list_peek_front(head, &list_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_peer = qdf_container_of(list_node, + struct tdls_peer, node); + tdls_ct_process_peers(curr_peer, tdls_vdev_obj, + tdls_soc_obj); + curr_peer->tx_pkt = 0; + curr_peer->rx_pkt = 0; + status = qdf_list_peek_next(head, + list_node, &list_node); + } + } + + tdls_timer_restart(tdls_vdev_obj->vdev, + &tdls_vdev_obj->peer_update_timer, + tdls_vdev_obj->threshold_config.tx_period_t); + +} + +void tdls_ct_handler(void *user_data) +{ + struct wlan_objmgr_vdev *vdev; + + if (!user_data) + return; + + vdev = tdls_get_vdev(user_data, WLAN_TDLS_NB_ID); + if (!vdev) + return; + + tdls_ct_process_handler(vdev); + + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); +} + +int tdls_set_tdls_offchannel(struct tdls_soc_priv_obj *tdls_soc, + int offchannel) +{ + uint32_t tdls_feature_flags; + + tdls_feature_flags = tdls_soc->tdls_configs.tdls_feature_flags; + + if (TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) && + (TDLS_SUPPORT_EXP_TRIG_ONLY == tdls_soc->tdls_current_mode || + TDLS_SUPPORT_IMP_MODE == tdls_soc->tdls_current_mode || + TDLS_SUPPORT_EXT_CONTROL == tdls_soc->tdls_current_mode)) { + if (offchannel < TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN || + offchannel > TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX) { + tdls_err("Invalid tdls off channel %u", offchannel); + return -EINVAL; + } + } else { + tdls_err("Either TDLS or TDLS Off-channel is not enabled"); + return -ENOTSUPP; + } + tdls_notice("change tdls off channel from %d to %d", + tdls_soc->tdls_off_channel, offchannel); + tdls_soc->tdls_off_channel = offchannel; + return 0; +} + +int tdls_set_tdls_secoffchanneloffset(struct tdls_soc_priv_obj *tdls_soc, + int offchanoffset) +{ + uint32_t tdls_feature_flags; + + tdls_feature_flags = tdls_soc->tdls_configs.tdls_feature_flags; + + if (!TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) || + TDLS_SUPPORT_SUSPENDED >= tdls_soc->tdls_current_mode) { + tdls_err("Either TDLS or TDLS Off-channel is not enabled"); + return -ENOTSUPP; + } + + tdls_soc->tdls_channel_offset = BW_INVALID; + + switch (offchanoffset) { + case TDLS_SEC_OFFCHAN_OFFSET_0: + tdls_soc->tdls_channel_offset = BW20; + break; + case TDLS_SEC_OFFCHAN_OFFSET_40PLUS: + tdls_soc->tdls_channel_offset = BW40_HIGH_PRIMARY; + break; + case TDLS_SEC_OFFCHAN_OFFSET_40MINUS: + tdls_soc->tdls_channel_offset = BW40_LOW_PRIMARY; + break; + case TDLS_SEC_OFFCHAN_OFFSET_80: + tdls_soc->tdls_channel_offset = BW80; + break; + case TDLS_SEC_OFFCHAN_OFFSET_160: + tdls_soc->tdls_channel_offset = BWALL; + break; + default: + tdls_err("Invalid tdls secondary off channel offset %d", + offchanoffset); + return -EINVAL; + } /* end switch */ + + tdls_notice("change tdls secondary off channel offset to 0x%x", + tdls_soc->tdls_channel_offset); + return 0; +} + +int tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev, + int offchanmode) +{ + struct tdls_peer *conn_peer = NULL; + struct tdls_channel_switch_params chan_switch_params = {0}; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + int ret_value = 0; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc; + uint32_t tdls_feature_flags; + + + status = tdls_get_vdev_objects(vdev, &tdls_vdev, &tdls_soc); + + if (status != QDF_STATUS_SUCCESS) + return -EINVAL; + + + if (offchanmode < ENABLE_CHANSWITCH || + offchanmode > DISABLE_CHANSWITCH) { + tdls_err("Invalid tdls off channel mode %d", offchanmode); + return -EINVAL; + } + + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) { + tdls_err("tdls off channel req in not associated state %d", + offchanmode); + return -EPERM; + } + + tdls_feature_flags = tdls_soc->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) || + TDLS_SUPPORT_SUSPENDED >= tdls_soc->tdls_current_mode) { + tdls_err("Either TDLS or TDLS Off-channel is not enabled"); + return -ENOTSUPP; + } + + conn_peer = tdls_find_first_connected_peer(tdls_vdev); + if (!conn_peer) { + tdls_err("No TDLS Connected Peer"); + return -EPERM; + } + + tdls_notice("TDLS Channel Switch in swmode=%d tdls_off_channel %d offchanoffset %d", + offchanmode, tdls_soc->tdls_off_channel, + tdls_soc->tdls_channel_offset); + + switch (offchanmode) { + case ENABLE_CHANSWITCH: + if (tdls_soc->tdls_off_channel && + tdls_soc->tdls_channel_offset != BW_INVALID) { + chan_switch_params.tdls_off_ch = + tdls_soc->tdls_off_channel; + chan_switch_params.tdls_off_ch_bw_offset = + tdls_soc->tdls_channel_offset; + chan_switch_params.oper_class = + tdls_find_opclass(tdls_soc->soc, + chan_switch_params.tdls_off_ch, + chan_switch_params.tdls_off_ch_bw_offset); + if (!chan_switch_params.oper_class) { + if (chan_switch_params.tdls_off_ch_bw_offset == + BW40_HIGH_PRIMARY) + chan_switch_params.oper_class = + tdls_find_opclass(tdls_soc->soc, + chan_switch_params.tdls_off_ch, + BW40_LOW_PRIMARY); + else if (chan_switch_params. + tdls_off_ch_bw_offset == + BW40_LOW_PRIMARY) + chan_switch_params.oper_class = + tdls_find_opclass(tdls_soc->soc, + chan_switch_params.tdls_off_ch, + BW40_HIGH_PRIMARY); + tdls_debug("oper_class:%d", + chan_switch_params.oper_class); + } + } else if (conn_peer->off_channel_capable && + conn_peer->pref_off_chan_num) { + chan_switch_params.tdls_off_ch = + conn_peer->pref_off_chan_num; + chan_switch_params.oper_class = + tdls_get_opclass_from_bandwidth( + tdls_soc, conn_peer->pref_off_chan_num, + tdls_soc->tdls_configs.tdls_pre_off_chan_bw, + &chan_switch_params.tdls_off_ch_bw_offset); + } else { + tdls_err("TDLS off-channel parameters are not set yet!!!"); + return -EINVAL; + + } + break; + case DISABLE_CHANSWITCH: + chan_switch_params.tdls_off_ch = 0; + chan_switch_params.tdls_off_ch_bw_offset = 0; + chan_switch_params.oper_class = 0; + break; + default: + tdls_err("Incorrect Parameters mode: %d tdls_off_channel: %d offchanoffset: %d", + offchanmode, tdls_soc->tdls_off_channel, + tdls_soc->tdls_channel_offset); + return -EINVAL; + } /* end switch */ + + chan_switch_params.vdev_id = tdls_vdev->session_id; + chan_switch_params.tdls_sw_mode = offchanmode; + chan_switch_params.is_responder = + conn_peer->is_responder; + qdf_mem_copy(&chan_switch_params.peer_mac_addr, + &conn_peer->peer_mac.bytes, + QDF_MAC_ADDR_SIZE); + tdls_notice("Peer " QDF_MAC_ADDR_FMT " vdevId: %d, off channel: %d, offset: %d, mode: %d, is_responder: %d", + QDF_MAC_ADDR_REF(chan_switch_params.peer_mac_addr), + chan_switch_params.vdev_id, + chan_switch_params.tdls_off_ch, + chan_switch_params.tdls_off_ch_bw_offset, + chan_switch_params.tdls_sw_mode, + chan_switch_params.is_responder); + + status = tdls_set_offchan_mode(tdls_soc->soc, + &chan_switch_params); + + if (status != QDF_STATUS_SUCCESS) { + tdls_err("Failed to send channel switch request to wmi"); + return -EINVAL; + } + + tdls_soc->tdls_fw_off_chan_mode = offchanmode; + + if (ENABLE_CHANSWITCH == offchanmode) { + conn_peer = tdls_find_first_connected_peer(tdls_vdev); + if (!conn_peer) { + tdls_err("No TDLS Connected Peer"); + return -EPERM; + } + conn_peer->pref_off_chan_num = + chan_switch_params.tdls_off_ch; + conn_peer->op_class_for_pref_off_chan = + chan_switch_params.oper_class; + } + + return ret_value; +} + +static QDF_STATUS tdls_delete_all_tdls_peers_flush_cb(struct scheduler_msg *msg) +{ + if (msg && msg->bodyptr) { + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + } + + return QDF_STATUS_SUCCESS; +} +/** + * tdls_delete_all_tdls_peers(): send request to delete tdls peers + * @vdev: vdev object + * @tdls_soc: tdls soc object + * + * This function sends request to lim to delete tdls peers + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_delete_all_tdls_peers(struct wlan_objmgr_vdev *vdev, + struct tdls_soc_priv_obj *tdls_soc) +{ + struct wlan_objmgr_peer *peer; + struct tdls_del_all_tdls_peers *del_msg; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_TDLS_SB_ID); + if (!peer) { + tdls_err("bss peer is null"); + return QDF_STATUS_E_FAILURE; + } + + del_msg = qdf_mem_malloc(sizeof(*del_msg)); + if (!del_msg) { + tdls_err("memory alloc failed"); + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_SB_ID); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_copy(del_msg->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_SB_ID); + + del_msg->msg_type = tdls_soc->tdls_del_all_peers; + del_msg->msg_len = (uint16_t) sizeof(*del_msg); + + /* Send the request to PE. */ + qdf_mem_zero(&msg, sizeof(msg)); + + tdls_debug("sending delete all peers req to PE "); + + msg.type = del_msg->msg_type; + msg.bodyptr = del_msg; + msg.flush_callback = tdls_delete_all_tdls_peers_flush_cb; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post delete all peer req failed, status %d", status); + qdf_mem_free(del_msg); + } + + return status; +} + +void tdls_disable_offchan_and_teardown_links( + struct wlan_objmgr_vdev *vdev) +{ + uint16_t connected_tdls_peers = 0; + uint8_t staidx; + struct tdls_peer *curr_peer = NULL; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc; + QDF_STATUS status; + uint8_t vdev_id; + bool tdls_in_progress = false; + + status = tdls_get_vdev_objects(vdev, &tdls_vdev, &tdls_soc); + if (QDF_STATUS_SUCCESS != status) { + tdls_err("tdls objects are NULL "); + return; + } + + if (TDLS_SUPPORT_SUSPENDED >= tdls_soc->tdls_current_mode) { + tdls_debug("TDLS mode %d is disabled OR not suspended now", + tdls_soc->tdls_current_mode); + return; + } + + connected_tdls_peers = tdls_soc->connected_peer_count; + if (tdls_is_progress(tdls_vdev, NULL, 0)) + tdls_in_progress = true; + + if (!(connected_tdls_peers || tdls_in_progress)) { + tdls_debug("No TDLS connected/progress peers to delete"); + vdev_id = vdev->vdev_objmgr.vdev_id; + if (tdls_soc->set_state_info.set_state_cnt > 0) { + tdls_debug("Disable the tdls in FW as second interface is coming up"); + tdls_send_update_to_fw(tdls_vdev, tdls_soc, true, + true, false, vdev_id); + } + return; + } + + /* TDLS is not supported in case of concurrency. + * Disable TDLS Offchannel in FW to avoid more + * than two concurrent channels and generate TDLS + * teardown indication to supplicant. + * Below function Finds the first connected peer and + * disables TDLS offchannel for that peer. + * FW enables TDLS offchannel only when there is + * one TDLS peer. When there are more than one TDLS peer, + * there will not be TDLS offchannel in FW. + * So to avoid sending multiple request to FW, for now, + * just invoke offchannel mode functions only once + */ + tdls_set_tdls_offchannel(tdls_soc, + tdls_soc->tdls_configs.tdls_pre_off_chan_num); + tdls_set_tdls_secoffchanneloffset(tdls_soc, + TDLS_SEC_OFFCHAN_OFFSET_40PLUS); + tdls_set_tdls_offchannelmode(vdev, DISABLE_CHANSWITCH); + + /* Send Msg to PE for deleting all the TDLS peers */ + tdls_delete_all_tdls_peers(vdev, tdls_soc); + + for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta; + staidx++) { + if (!tdls_soc->tdls_conn_info[staidx].valid_entry) + continue; + + curr_peer = tdls_find_all_peer(tdls_soc, + tdls_soc->tdls_conn_info[staidx].peer_mac.bytes); + if (!curr_peer) + continue; + + tdls_notice("indicate TDLS teardown "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); + + /* Indicate teardown to supplicant */ + tdls_indicate_teardown(tdls_vdev, + curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + + /* + * Del Sta happened already as part of tdls_delete_all_tdls_peers + * Hence clear tdls vdev data structure. + */ + tdls_reset_peer(tdls_vdev, curr_peer->peer_mac.bytes); + + tdls_decrement_peer_count(tdls_soc); + tdls_soc->tdls_conn_info[staidx].valid_entry = false; + tdls_soc->tdls_conn_info[staidx].session_id = 255; + tdls_soc->tdls_conn_info[staidx].index = + INVALID_TDLS_PEER_INDEX; + + qdf_mem_zero(&tdls_soc->tdls_conn_info[staidx].peer_mac, + sizeof(struct qdf_mac_addr)); + } +} + +void tdls_teardown_connections(struct wlan_objmgr_psoc *psoc) +{ + struct tdls_osif_indication indication; + struct tdls_soc_priv_obj *tdls_soc; + struct wlan_objmgr_vdev *tdls_vdev; + + + tdls_soc = wlan_psoc_get_tdls_soc_obj(psoc); + if (!tdls_soc) + return; + + /* Get the tdls specific vdev and clear the links */ + tdls_vdev = tdls_get_vdev(psoc, WLAN_TDLS_SB_ID); + if (!tdls_vdev) { + tdls_err("Unable get the vdev"); + return; + } + tdls_disable_offchan_and_teardown_links(tdls_vdev); + + indication.vdev = tdls_vdev; + + if (tdls_soc->tdls_event_cb) + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_TEARDOWN_LINKS_DONE, + &indication); + wlan_objmgr_vdev_release_ref(tdls_vdev, WLAN_TDLS_SB_ID); +} diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_ct.h b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_ct.h new file mode 100644 index 0000000000000000000000000000000000000000..48718e628f970d0cbb68372332d2a3ba7f796b78 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_ct.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_ct.h + * + * TDLS connection tracker declarations + */ + +#ifndef _WLAN_TDLS_CT_H_ +#define _WLAN_TDLS_CT_H_ + + /* + * Before UpdateTimer expires, we want to timeout discovery response + * should not be more than 2000. + */ +#define TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE 1000 + +#define TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN 1 +#define TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX 165 +#define TDLS_PREFERRED_OFF_CHANNEL_NUM_DEFAULT 36 + +/** + * tdls_implicit_enable() - enable implicit tdls triggering + * @tdls_vdev: TDLS vdev + * + * Return: Void + */ +void tdls_implicit_enable(struct tdls_vdev_priv_obj *tdls_vdev); + +/** + * tdls_update_rx_pkt_cnt() - Update rx packet count + * @vdev: vdev object manager + * @mac_addr: mac address of the data + * @dest_mac_addr: dest mac address of the data + * + * Increase the rx packet count, if the sender is not bssid and the packet is + * not broadcast and multicast packet + * + * This sampling information will be used in TDLS connection tracker + * + * This function expected to be called in an atomic context so blocking APIs + * not allowed + * + * Return: None + */ +void tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr, + struct qdf_mac_addr *dest_mac_addr); + +/** + * tdls_update_tx_pkt_cnt() - update tx packet + * @vdev: vdev object + * @mac_addr: mac address of the data + * + * Increase the tx packet count, if the sender is not bssid and the packet is + * not broadcast and multicast packet + * + * This sampling information will be used in TDLS connection tracker + * + * This function expected to be called in an atomic context so blocking APIs + * not allowed + * + * Return: None + */ +void tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr); + +/** + * wlan_hdd_tdls_implicit_send_discovery_request() - send discovery request + * @tdls_vdev_obj: tdls vdev object + * + * Return: None + */ +void tdls_implicit_send_discovery_request( + struct tdls_vdev_priv_obj *tdls_vdev_obj); + +/** + * tdls_recv_discovery_resp() - handling of tdls discovery response + * @soc: object manager + * @mac: mac address of peer from which the response was received + * + * Return: 0 for success or negative errno otherwise + */ +int tdls_recv_discovery_resp(struct tdls_vdev_priv_obj *tdls_vdev, + const uint8_t *mac); + +/** + * tdls_indicate_teardown() - indicate teardown to upper layer + * @tdls_vdev: tdls vdev object + * @curr_peer: teardown peer + * @reason: teardown reason + * + * Return: Void + */ +void tdls_indicate_teardown(struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_peer *curr_peer, + uint16_t reason); + +/** + * tdls_ct_handler() - TDLS connection tracker handler + * @user_data: user data from timer + * + * tdls connection tracker timer starts, when the STA connected to AP + * and it's scan the traffic between two STA peers and make TDLS + * connection and teardown, based on the traffic threshold + * + * Return: None + */ +void tdls_ct_handler(void *user_data); + +/** + * tdls_ct_idle_handler() - Check tdls idle traffic + * @user_data: data from tdls idle timer + * + * Function to check the tdls idle traffic and make a decision about + * tdls teardown + * + * Return: None + */ +void tdls_ct_idle_handler(void *user_data); + +/** + * tdls_discovery_timeout_peer_cb() - tdls discovery timeout callback + * @userData: tdls vdev + * + * Return: None + */ +void tdls_discovery_timeout_peer_cb(void *user_data); + +/** + * tdls_implicit_disable() - disable implicit tdls triggering + * @pHddTdlsCtx: TDLS context + * + * Return: Void + */ +void tdls_implicit_disable(struct tdls_vdev_priv_obj *tdls_vdev); + +/** + * tdls_is_vdev_authenticated() -check the vdev authentication state + * @vdev: vdev oobject + * + * Return: true or false + */ +bool tdls_is_vdev_authenticated(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_teardown_connections() -teardown and delete all the tdls peers + * @psoc: psoc object + * + * Return: true or false + */ +void tdls_teardown_connections(struct wlan_objmgr_psoc *psoc); + +/** + * tdls_disable_offchan_and_teardown_links - Disable offchannel + * and teardown TDLS links + * @tdls_soc : tdls soc object + * + * Return: None + */ +void tdls_disable_offchan_and_teardown_links( + struct wlan_objmgr_vdev *vdev); + +/** + * tdls_delete_all_tdls_peers(): send request to delete tdls peers + * @vdev: vdev object + * @tdls_soc: tdls soc object + * + * This function sends request to lim to delete tdls peers + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_delete_all_tdls_peers(struct wlan_objmgr_vdev *vdev, + struct tdls_soc_priv_obj *tdls_soc); + +/** + * tdls_set_tdls_offchannel() - set tdls off-channel number + * @tdls_soc: tdls soc object + * @offchannel: tdls off-channel number + * + * This function sets tdls off-channel number + * + * Return: 0 on success; negative errno otherwise + */ +int tdls_set_tdls_offchannel(struct tdls_soc_priv_obj *tdls_soc, + int offchannel); + +/** + * tdls_set_tdls_offchannelmode() - set tdls off-channel mode + * @adapter: Pointer to the HDD adapter + * @offchanmode: tdls off-channel mode + * + * This function sets tdls off-channel mode + * + * Return: 0 on success; negative errno otherwise + */ + +int tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev, + int offchanmode); + +/** + * tdls_set_tdls_secoffchanneloffset() - set secondary tdls off-channel offset + * @tdls_soc: tdls soc object + * @offchanoffset: tdls off-channel offset + * + * This function sets secondary tdls off-channel offset + * + * Return: 0 on success; negative errno otherwise + */ + +int tdls_set_tdls_secoffchanneloffset(struct tdls_soc_priv_obj *tdls_soc, + int offchanoffset); + +#endif diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_main.c b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_main.c new file mode 100644 index 0000000000000000000000000000000000000000..0a8e15aeea2341a17cf35ce28d4c67611a464364 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_main.c @@ -0,0 +1,1792 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_main.c + * + * TDLS core function definitions + */ + +#include "wlan_tdls_main.h" +#include "wlan_tdls_cmds_process.h" +#include "wlan_tdls_peer.h" +#include "wlan_tdls_ct.h" +#include "wlan_tdls_mgmt.h" +#include "wlan_tdls_tgt_api.h" +#include "wlan_policy_mgr_public_struct.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_scan_ucfg_api.h" + + +/* Global tdls soc pvt object + * this is useful for some functions which does not receive either vdev or psoc + * objects. + */ +static struct tdls_soc_priv_obj *tdls_soc_global; + +#ifdef WLAN_DEBUG +/** + * tdls_get_cmd_type_str() - parse cmd to string + * @cmd_type: tdls cmd type + * + * This function parse tdls cmd to string. + * + * Return: command string + */ +static char *tdls_get_cmd_type_str(enum tdls_command_type cmd_type) +{ + switch (cmd_type) { + case TDLS_CMD_TX_ACTION: + return "TDLS_CMD_TX_ACTION"; + case TDLS_CMD_ADD_STA: + return "TDLS_CMD_ADD_STA"; + case TDLS_CMD_CHANGE_STA: + return "TDLS_CMD_CHANGE_STA"; + case TDLS_CMD_ENABLE_LINK: + return "TDLS_CMD_ENABLE_LINK"; + case TDLS_CMD_DISABLE_LINK: + return "TDLS_CMD_DISABLE_LINK"; + case TDLS_CMD_CONFIG_FORCE_PEER: + return "TDLS_CMD_CONFIG_FORCE_PEER"; + case TDLS_CMD_REMOVE_FORCE_PEER: + return "TDLS_CMD_REMOVE_FORCE_PEER"; + case TDLS_CMD_STATS_UPDATE: + return "TDLS_CMD_STATS_UPDATE"; + case TDLS_CMD_CONFIG_UPDATE: + return "TDLS_CMD_CONFIG_UPDATE"; + case TDLS_CMD_SET_RESPONDER: + return "TDLS_CMD_SET_RESPONDER"; + case TDLS_CMD_SCAN_DONE: + return "TDLS_CMD_SCAN_DONE"; + case TDLS_NOTIFY_STA_CONNECTION: + return "TDLS_NOTIFY_STA_CONNECTION"; + case TDLS_NOTIFY_STA_DISCONNECTION: + return "TDLS_NOTIFY_STA_DISCONNECTION"; + case TDLS_CMD_SET_TDLS_MODE: + return "TDLS_CMD_SET_TDLS_MODE"; + case TDLS_CMD_SESSION_DECREMENT: + return "TDLS_CMD_SESSION_DECREMENT"; + case TDLS_CMD_SESSION_INCREMENT: + return "TDLS_CMD_SESSION_INCREMENT"; + case TDLS_CMD_TEARDOWN_LINKS: + return "TDLS_CMD_TEARDOWN_LINKS"; + case TDLS_NOTIFY_RESET_ADAPTERS: + return "TDLS_NOTIFY_RESET_ADAPTERS"; + case TDLS_CMD_ANTENNA_SWITCH: + return "TDLS_CMD_ANTENNA_SWITCH"; + + default: + return "Invalid TDLS command"; + } +} + +/** + * tdls_get_event_type_str() - parase event to string + * @event_type: tdls event type + * + * This function parse tdls event to string. + * + * Return: event string + */ +static char *tdls_get_event_type_str(enum tdls_event_type event_type) +{ + switch (event_type) { + case TDLS_SHOULD_DISCOVER: + return "TDLS_SHOULD_DISCOVER"; + case TDLS_SHOULD_TEARDOWN: + return "TDLS_SHOULD_TEARDOWN"; + case TDLS_PEER_DISCONNECTED: + return "TDLS_PEER_DISCONNECTED"; + case TDLS_CONNECTION_TRACKER_NOTIFY: + return "TDLS_CONNECTION_TRACKER_NOTIFY"; + + default: + return "Invalid TDLS event"; + } +} +#else +static char *tdls_get_cmd_type_str(enum tdls_command_type cmd_type) +{ + return ""; +} + +static char *tdls_get_event_type_str(enum tdls_event_type event_type) +{ + return ""; +} +#endif + +QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *tdls_soc_obj; + + tdls_soc_obj = qdf_mem_malloc(sizeof(*tdls_soc_obj)); + if (!tdls_soc_obj) { + tdls_err("Failed to allocate memory for tdls object"); + return QDF_STATUS_E_NOMEM; + } + + tdls_soc_obj->soc = psoc; + + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_TDLS, + (void *)tdls_soc_obj, + QDF_STATUS_SUCCESS); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to attach psoc tdls component"); + qdf_mem_free(tdls_soc_obj); + return status; + } + + tdls_soc_global = tdls_soc_obj; + tdls_notice("TDLS obj attach to psoc successfully"); + + return status; +} + +QDF_STATUS tdls_psoc_obj_destroy_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *tdls_soc_obj; + + tdls_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!tdls_soc_obj) { + tdls_err("Failed to get tdls obj in psoc"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_TDLS, + tdls_soc_obj); + + if (QDF_IS_STATUS_ERROR(status)) + tdls_err("Failed to detach psoc tdls component"); + qdf_mem_free(tdls_soc_obj); + + return status; +} + +static QDF_STATUS tdls_vdev_init(struct tdls_vdev_priv_obj *vdev_obj) +{ + uint8_t i; + struct tdls_config_params *config; + struct tdls_user_config *user_config; + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("tdls soc obj NULL"); + return QDF_STATUS_E_FAILURE; + } + + config = &vdev_obj->threshold_config; + user_config = &soc_obj->tdls_configs; + config->tx_period_t = user_config->tdls_tx_states_period; + config->tx_packet_n = user_config->tdls_tx_pkt_threshold; + config->discovery_tries_n = user_config->tdls_max_discovery_attempt; + config->idle_timeout_t = user_config->tdls_idle_timeout; + config->idle_packet_n = user_config->tdls_idle_pkt_threshold; + config->rssi_trigger_threshold = + user_config->tdls_rssi_trigger_threshold; + config->rssi_teardown_threshold = + user_config->tdls_rssi_teardown_threshold; + config->rssi_delta = user_config->tdls_rssi_delta; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + qdf_list_create(&vdev_obj->peer_list[i], + WLAN_TDLS_PEER_SUB_LIST_SIZE); + } + qdf_mc_timer_init(&vdev_obj->peer_update_timer, QDF_TIMER_TYPE_SW, + tdls_ct_handler, soc_obj->soc); + qdf_mc_timer_init(&vdev_obj->peer_discovery_timer, QDF_TIMER_TYPE_SW, + tdls_discovery_timeout_peer_cb, soc_obj->soc); + + return QDF_STATUS_SUCCESS; +} + +static void tdls_vdev_deinit(struct tdls_vdev_priv_obj *vdev_obj) +{ + qdf_mc_timer_stop_sync(&vdev_obj->peer_update_timer); + qdf_mc_timer_stop_sync(&vdev_obj->peer_discovery_timer); + + qdf_mc_timer_destroy(&vdev_obj->peer_update_timer); + qdf_mc_timer_destroy(&vdev_obj->peer_discovery_timer); + + tdls_peer_idle_timers_destroy(vdev_obj); + tdls_free_peer_list(vdev_obj); +} + +QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev, + void *arg) +{ + QDF_STATUS status; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct wlan_objmgr_pdev *pdev; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint32_t tdls_feature_flags; + + tdls_debug("tdls vdev mode %d", wlan_vdev_mlme_get_opmode(vdev)); + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE && + wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE) + return QDF_STATUS_SUCCESS; + + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!tdls_soc_obj) { + tdls_err("get soc by vdev failed"); + return QDF_STATUS_E_NOMEM; + } + + tdls_feature_flags = tdls_soc_obj->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_ENABLED(tdls_feature_flags)) { + tdls_debug("disabled in ini"); + return QDF_STATUS_E_NOSUPPORT; + } + + if (tdls_soc_obj->tdls_osif_init_cb) { + status = tdls_soc_obj->tdls_osif_init_cb(vdev); + if (QDF_IS_STATUS_ERROR(status)) + return status; + } + + /* TODO: Add concurrency check */ + + tdls_vdev_obj = qdf_mem_malloc(sizeof(*tdls_vdev_obj)); + if (!tdls_vdev_obj) { + tdls_err("Failed to allocate memory for tdls vdev object"); + status = QDF_STATUS_E_NOMEM; + goto err; + } + + status = wlan_objmgr_vdev_component_obj_attach(vdev, + WLAN_UMAC_COMP_TDLS, + (void *)tdls_vdev_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to attach vdev tdls component"); + goto err; + } + tdls_vdev_obj->vdev = vdev; + status = tdls_vdev_init(tdls_vdev_obj); + if (QDF_IS_STATUS_ERROR(status)) + goto err; + + pdev = wlan_vdev_get_pdev(vdev); + + status = ucfg_scan_register_event_handler(pdev, + tdls_scan_complete_event_handler, + tdls_soc_obj); + + if (QDF_STATUS_SUCCESS != status) { + tdls_err("scan event register failed "); + tdls_vdev_deinit(tdls_vdev_obj); + goto err; + } + + tdls_debug("tdls object attach to vdev successfully"); + return status; +err: + if (tdls_soc_obj->tdls_osif_deinit_cb) + tdls_soc_obj->tdls_osif_deinit_cb(vdev); + if (tdls_vdev_obj) { + qdf_mem_free(tdls_vdev_obj); + tdls_vdev_obj = NULL; + } + return status; +} + +QDF_STATUS tdls_vdev_obj_destroy_notification(struct wlan_objmgr_vdev *vdev, + void *arg) +{ + QDF_STATUS status; + void *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint32_t tdls_feature_flags; + + tdls_debug("tdls vdev mode %d", wlan_vdev_mlme_get_opmode(vdev)); + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE && + wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE) + return QDF_STATUS_SUCCESS; + + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!tdls_soc_obj) { + tdls_err("get soc by vdev failed"); + return QDF_STATUS_E_NOMEM; + } + + tdls_feature_flags = tdls_soc_obj->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_ENABLED(tdls_feature_flags)) { + tdls_debug("disabled in ini"); + return QDF_STATUS_E_NOSUPPORT; + } + + tdls_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + if (!tdls_vdev_obj) { + tdls_err("Failed to get tdls vdev object"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_vdev_component_obj_detach(vdev, + WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj); + if (QDF_IS_STATUS_ERROR(status)) + tdls_err("Failed to detach vdev tdls component"); + + tdls_vdev_deinit(tdls_vdev_obj); + qdf_mem_free(tdls_vdev_obj); + if (tdls_soc_obj->tdls_osif_deinit_cb) + tdls_soc_obj->tdls_osif_deinit_cb(vdev); + + return status; +} + +/** + * __tdls_get_all_peers_from_list() - get all the tdls peers from the list + * @get_tdls_peers: get_tdls_peers object + * + * Return: int + */ +static int __tdls_get_all_peers_from_list( + struct tdls_get_all_peers *get_tdls_peers) +{ + int i; + int len, init_len; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *curr_peer; + char *buf; + int buf_len; + struct tdls_vdev_priv_obj *tdls_vdev; + QDF_STATUS status; + + tdls_notice("Enter "); + + buf = get_tdls_peers->buf; + buf_len = get_tdls_peers->buf_len; + + if (wlan_vdev_is_up(get_tdls_peers->vdev) != QDF_STATUS_SUCCESS) { + len = qdf_scnprintf(buf, buf_len, + "\nSTA is not associated\n"); + return len; + } + + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(get_tdls_peers->vdev); + + if (!tdls_vdev) { + len = qdf_scnprintf(buf, buf_len, "TDLS not enabled\n"); + return len; + } + + init_len = buf_len; + len = qdf_scnprintf(buf, buf_len, + "\n%-18s%-3s%-4s%-3s%-5s\n", + "MAC", "Id", "cap", "up", "RSSI"); + buf += len; + buf_len -= len; + len = qdf_scnprintf(buf, buf_len, + "---------------------------------\n"); + buf += len; + buf_len -= len; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->peer_list[i]; + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_peer = qdf_container_of(p_node, + struct tdls_peer, node); + if (buf_len < 32 + 1) + break; + len = qdf_scnprintf(buf, buf_len, + QDF_MAC_ADDR_FMT "%4s%3s%5d\n", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes), + (curr_peer->tdls_support == + TDLS_CAP_SUPPORTED) ? "Y" : "N", + TDLS_IS_LINK_CONNECTED(curr_peer) ? "Y" : + "N", curr_peer->rssi); + buf += len; + buf_len -= len; + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + + tdls_notice("Exit "); + return init_len - buf_len; +} + +/** + * tdls_get_all_peers_from_list() - get all the tdls peers from the list + * @get_tdls_peers: get_tdls_peers object + * + * Return: None + */ +static void tdls_get_all_peers_from_list( + struct tdls_get_all_peers *get_tdls_peers) +{ + int32_t len; + struct tdls_soc_priv_obj *tdls_soc_obj; + struct tdls_osif_indication indication; + + if (!get_tdls_peers->vdev) { + qdf_mem_free(get_tdls_peers); + return; + } + len = __tdls_get_all_peers_from_list(get_tdls_peers); + + indication.status = len; + indication.vdev = get_tdls_peers->vdev; + + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(get_tdls_peers->vdev); + if (tdls_soc_obj && tdls_soc_obj->tdls_event_cb) + tdls_soc_obj->tdls_event_cb(tdls_soc_obj->tdls_evt_cb_data, + TDLS_EVENT_USER_CMD, &indication); + + qdf_mem_free(get_tdls_peers); +} + +/** + * tdls_process_reset_all_peers() - Reset all tdls peers + * @delete_all_peers_ind: Delete all peers indication + * + * This function is called to reset all tdls peers and + * notify upper layers of teardown inidcation + * + * Return: QDF_STATUS + */ + +static QDF_STATUS tdls_process_reset_all_peers(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t staidx; + struct tdls_peer *curr_peer = NULL; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc; + uint8_t reset_session_id; + + status = tdls_get_vdev_objects(vdev, &tdls_vdev, &tdls_soc); + if (QDF_STATUS_SUCCESS != status) { + tdls_err("tdls objects are NULL "); + return status; + } + + reset_session_id = tdls_vdev->session_id; + for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta; + staidx++) { + if (!tdls_soc->tdls_conn_info[staidx].valid_entry) + continue; + if (tdls_soc->tdls_conn_info[staidx].session_id != + reset_session_id) + continue; + + curr_peer = + tdls_find_all_peer(tdls_soc, + tdls_soc->tdls_conn_info[staidx]. + peer_mac.bytes); + if (!curr_peer) + continue; + + tdls_notice("indicate TDLS teardown "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); + + /* Indicate teardown to supplicant */ + tdls_indicate_teardown(tdls_vdev, + curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + + tdls_reset_peer(tdls_vdev, curr_peer->peer_mac.bytes); + + tdls_decrement_peer_count(tdls_soc); + tdls_soc->tdls_conn_info[staidx].valid_entry = false; + tdls_soc->tdls_conn_info[staidx].session_id = 255; + tdls_soc->tdls_conn_info[staidx].index = + INVALID_TDLS_PEER_INDEX; + + qdf_mem_zero(&tdls_soc->tdls_conn_info[staidx].peer_mac, + sizeof(struct qdf_mac_addr)); + } + return status; +} + +/** + * tdls_reset_all_peers() - Reset all tdls peers + * @delete_all_peers_ind: Delete all peers indication + * + * This function is called to reset all tdls peers and + * notify upper layers of teardown inidcation + * + * Return: QDF_STATUS + */ +static QDF_STATUS tdls_reset_all_peers( + struct tdls_delete_all_peers_params *delete_all_peers_ind) +{ + QDF_STATUS status; + + if (!delete_all_peers_ind || !delete_all_peers_ind->vdev) { + tdls_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + status = tdls_process_reset_all_peers(delete_all_peers_ind->vdev); + + wlan_objmgr_vdev_release_ref(delete_all_peers_ind->vdev, + WLAN_TDLS_SB_ID); + qdf_mem_free(delete_all_peers_ind); + + return status; +} + +QDF_STATUS tdls_process_cmd(struct scheduler_msg *msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!msg || !msg->bodyptr) { + tdls_err("msg: 0x%pK", msg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("TDLS process command: %s(%d)", + tdls_get_cmd_type_str(msg->type), msg->type); + + switch (msg->type) { + case TDLS_CMD_TX_ACTION: + tdls_process_mgmt_req(msg->bodyptr); + break; + case TDLS_CMD_ADD_STA: + tdls_process_add_peer(msg->bodyptr); + break; + case TDLS_CMD_CHANGE_STA: + tdls_process_update_peer(msg->bodyptr); + break; + case TDLS_CMD_ENABLE_LINK: + tdls_process_enable_link(msg->bodyptr); + break; + case TDLS_CMD_DISABLE_LINK: + tdls_process_del_peer(msg->bodyptr); + break; + case TDLS_CMD_CONFIG_FORCE_PEER: + tdls_process_setup_peer(msg->bodyptr); + break; + case TDLS_CMD_REMOVE_FORCE_PEER: + tdls_process_remove_force_peer(msg->bodyptr); + break; + case TDLS_CMD_STATS_UPDATE: + break; + case TDLS_CMD_CONFIG_UPDATE: + break; + case TDLS_CMD_SET_RESPONDER: + tdls_set_responder(msg->bodyptr); + break; + case TDLS_CMD_SCAN_DONE: + tdls_scan_done_callback(msg->bodyptr); + break; + case TDLS_NOTIFY_STA_CONNECTION: + tdls_notify_sta_connect(msg->bodyptr); + break; + case TDLS_NOTIFY_STA_DISCONNECTION: + tdls_notify_sta_disconnect(msg->bodyptr); + break; + case TDLS_CMD_SET_TDLS_MODE: + tdls_set_operation_mode(msg->bodyptr); + break; + case TDLS_CMD_SESSION_DECREMENT: + tdls_process_decrement_active_session(msg->bodyptr); + /*Fall through to take decision on connection tracker.*/ + case TDLS_CMD_SESSION_INCREMENT: + tdls_process_policy_mgr_notification(msg->bodyptr); + break; + case TDLS_CMD_TEARDOWN_LINKS: + tdls_teardown_connections(msg->bodyptr); + break; + case TDLS_NOTIFY_RESET_ADAPTERS: + tdls_notify_reset_adapter(msg->bodyptr); + break; + case TDLS_CMD_ANTENNA_SWITCH: + tdls_process_antenna_switch(msg->bodyptr); + break; + case TDLS_CMD_GET_ALL_PEERS: + tdls_get_all_peers_from_list(msg->bodyptr); + break; + case TDLS_CMD_SET_OFFCHANNEL: + tdls_process_set_offchannel(msg->bodyptr); + break; + case TDLS_CMD_SET_OFFCHANMODE: + tdls_process_set_offchan_mode(msg->bodyptr); + break; + case TDLS_CMD_SET_SECOFFCHANOFFSET: + tdls_process_set_secoffchanneloffset(msg->bodyptr); + break; + case TDLS_DELETE_ALL_PEERS_INDICATION: + tdls_reset_all_peers(msg->bodyptr); + break; + default: + break; + } + + return status; +} + +QDF_STATUS tdls_process_evt(struct scheduler_msg *msg) +{ + struct wlan_objmgr_vdev *vdev; + struct tdls_event_notify *notify; + struct tdls_event_info *event; + + if (!msg || !msg->bodyptr) { + tdls_err("msg is not valid: %pK", msg); + return QDF_STATUS_E_NULL_VALUE; + } + notify = msg->bodyptr; + vdev = notify->vdev; + if (!vdev) { + tdls_err("NULL vdev object"); + qdf_mem_free(notify); + return QDF_STATUS_E_NULL_VALUE; + } + event = ¬ify->event; + + tdls_debug("evt type: %s(%d)", + tdls_get_event_type_str(event->message_type), + event->message_type); + + switch (event->message_type) { + case TDLS_SHOULD_DISCOVER: + tdls_process_should_discover(vdev, event); + break; + case TDLS_SHOULD_TEARDOWN: + case TDLS_PEER_DISCONNECTED: + tdls_process_should_teardown(vdev, event); + break; + case TDLS_CONNECTION_TRACKER_NOTIFY: + tdls_process_connection_tracker_notify(vdev, event); + break; + default: + break; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(notify); + + return QDF_STATUS_SUCCESS; +} + +void tdls_timer_restart(struct wlan_objmgr_vdev *vdev, + qdf_mc_timer_t *timer, + uint32_t expiration_time) +{ + if (QDF_TIMER_STATE_RUNNING != + qdf_mc_timer_get_current_state(timer)) + qdf_mc_timer_start(timer, expiration_time); +} + +/** + * wlan_hdd_tdls_monitor_timers_stop() - stop all monitoring timers + * @hdd_tdls_ctx: TDLS context + * + * Return: none + */ +static void tdls_monitor_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev) +{ + qdf_mc_timer_stop(&tdls_vdev->peer_discovery_timer); +} + +/** + * tdls_peer_idle_timers_stop() - stop peer idle timers + * @tdls_vdev: TDLS vdev object + * + * Loop through the idle peer list and stop their timers + * + * Return: None + */ +static void tdls_peer_idle_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *curr_peer; + QDF_STATUS status; + + tdls_vdev->discovery_peer_cnt = 0; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->peer_list[i]; + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_peer = qdf_container_of(p_node, struct tdls_peer, + node); + if (curr_peer->is_peer_idle_timer_initialised) + qdf_mc_timer_stop(&curr_peer->peer_idle_timer); + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + +} + +/** + * wlan_hdd_tdls_ct_timers_stop() - stop tdls connection tracker timers + * @tdls_vdev: TDLS vdev + * + * Return: None + */ +static void tdls_ct_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev) +{ + qdf_mc_timer_stop(&tdls_vdev->peer_update_timer); + tdls_peer_idle_timers_stop(tdls_vdev); +} + +/** + * wlan_hdd_tdls_timers_stop() - stop all the tdls timers running + * @tdls_vdev: TDLS vdev + * + * Return: none + */ +void tdls_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev) +{ + tdls_monitor_timers_stop(tdls_vdev); + tdls_ct_timers_stop(tdls_vdev); +} + +QDF_STATUS tdls_get_vdev_objects(struct wlan_objmgr_vdev *vdev, + struct tdls_vdev_priv_obj **tdls_vdev_obj, + struct tdls_soc_priv_obj **tdls_soc_obj) +{ + enum QDF_OPMODE device_mode; + + if (!vdev) + return QDF_STATUS_E_FAILURE; + + *tdls_vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + if (NULL == (*tdls_vdev_obj)) + return QDF_STATUS_E_FAILURE; + + *tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (NULL == (*tdls_soc_obj)) + return QDF_STATUS_E_FAILURE; + + device_mode = wlan_vdev_mlme_get_opmode(vdev); + + if (device_mode != QDF_STA_MODE && + device_mode != QDF_P2P_CLIENT_MODE) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *param) +{ + QDF_STATUS status; + + /* wmi_unified_set_tdls_offchan_mode_cmd() will be called directly */ + status = tgt_tdls_set_offchan_mode(psoc, param); + + if (!QDF_IS_STATUS_SUCCESS(status)) + status = QDF_STATUS_E_FAILURE; + + return status; +} + +/** + * tdls_update_fw_tdls_state() - update tdls status info + * @tdls_soc_obj: TDLS soc object + * @tdls_info_to_fw: TDLS state info to update in f/w. + * + * send message to WMA to set TDLS state in f/w + * + * Return: QDF_STATUS. + */ +static +QDF_STATUS tdls_update_fw_tdls_state(struct tdls_soc_priv_obj *tdls_soc_obj, + struct tdls_info *tdls_info_to_fw) +{ + QDF_STATUS status; + + /* wmi_unified_update_fw_tdls_state_cmd() will be called directly */ + status = tgt_tdls_set_fw_state(tdls_soc_obj->soc, tdls_info_to_fw); + + if (!QDF_IS_STATUS_SUCCESS(status)) + status = QDF_STATUS_E_FAILURE; + + return status; +} + +bool tdls_check_is_tdls_allowed(struct wlan_objmgr_vdev *vdev) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + bool state = false; + + if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev, + WLAN_TDLS_NB_ID)) + return state; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) { + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); + return state; + } + + if (policy_mgr_get_connection_count(tdls_soc_obj->soc) == 1) + state = true; + else + tdls_warn("Concurrent sessions are running or TDLS disabled"); + /* If any concurrency is detected */ + /* print session information */ + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); + return state; +} + +/** + * cds_set_tdls_ct_mode() - Set the tdls connection tracker mode + * @hdd_ctx: hdd context + * + * This routine is called to set the tdls connection tracker operation status + * + * Return: NONE + */ +void tdls_set_ct_mode(struct wlan_objmgr_psoc *psoc) +{ + bool state = false; + struct tdls_soc_priv_obj *tdls_soc_obj; + + tdls_soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!tdls_soc_obj) + return; + + /* If any concurrency is detected, skip tdls pkt tracker */ + if (policy_mgr_get_connection_count(psoc) > 1) { + state = false; + goto set_state; + } + + if (TDLS_SUPPORT_DISABLED == tdls_soc_obj->tdls_current_mode || + TDLS_SUPPORT_SUSPENDED == tdls_soc_obj->tdls_current_mode || + !TDLS_IS_IMPLICIT_TRIG_ENABLED( + tdls_soc_obj->tdls_configs.tdls_feature_flags)) { + state = false; + goto set_state; + } else if (policy_mgr_mode_specific_connection_count(psoc, + PM_STA_MODE, + NULL) == 1) { + state = true; + } else if (policy_mgr_mode_specific_connection_count(psoc, + PM_P2P_CLIENT_MODE, + NULL) == 1){ + state = true; + } else { + state = false; + goto set_state; + } + + /* In case of TDLS external control, peer should be added + * by the user space to start connection tracker. + */ + if (TDLS_IS_EXTERNAL_CONTROL_ENABLED( + tdls_soc_obj->tdls_configs.tdls_feature_flags)) { + if (tdls_soc_obj->tdls_external_peer_count) + state = true; + else + state = false; + } + +set_state: + tdls_soc_obj->enable_tdls_connection_tracker = state; + + tdls_debug("enable_tdls_connection_tracker %d", + tdls_soc_obj->enable_tdls_connection_tracker); +} + +QDF_STATUS +tdls_process_policy_mgr_notification(struct wlan_objmgr_psoc *psoc) +{ + struct tdls_vdev_priv_obj *tdls_priv_vdev; + struct wlan_objmgr_vdev *tdls_obj_vdev; + struct tdls_soc_priv_obj *tdls_priv_soc; + + if (!psoc) { + tdls_err("psoc: %pK", psoc); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_obj_vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID); + tdls_debug("enter "); + tdls_set_ct_mode(psoc); + if (tdls_obj_vdev && (tdls_get_vdev_objects(tdls_obj_vdev, + &tdls_priv_vdev, &tdls_priv_soc) == QDF_STATUS_SUCCESS) && + tdls_priv_soc->enable_tdls_connection_tracker) + tdls_implicit_enable(tdls_priv_vdev); + + if (tdls_obj_vdev) + wlan_objmgr_vdev_release_ref(tdls_obj_vdev, WLAN_TDLS_NB_ID); + + tdls_debug("exit "); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +tdls_process_decrement_active_session(struct wlan_objmgr_psoc *psoc) +{ + struct tdls_soc_priv_obj *tdls_priv_soc; + struct tdls_vdev_priv_obj *tdls_priv_vdev; + struct wlan_objmgr_vdev *tdls_obj_vdev; + uint8_t vdev_id; + + tdls_debug("Enter"); + if (!psoc) + return QDF_STATUS_E_NULL_VALUE; + if(!policy_mgr_is_hw_dbs_2x2_capable(psoc) && + !policy_mgr_is_hw_dbs_required_for_band( + psoc, HW_MODE_MAC_BAND_2G) && + policy_mgr_is_current_hwmode_dbs(psoc)) { + tdls_debug("Current HW mode is 1*1 DBS. Wait for Opportunistic timer to expire to enable TDLS in FW"); + return QDF_STATUS_SUCCESS; + } + tdls_obj_vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID); + if (tdls_obj_vdev) { + tdls_debug("Enable TDLS in FW and host as only one active sta/p2p_cli interface is present"); + vdev_id = wlan_vdev_get_id(tdls_obj_vdev); + if (tdls_get_vdev_objects(tdls_obj_vdev, &tdls_priv_vdev, + &tdls_priv_soc) == QDF_STATUS_SUCCESS) + tdls_send_update_to_fw(tdls_priv_vdev, tdls_priv_soc, + false, false, true, vdev_id); + wlan_objmgr_vdev_release_ref(tdls_obj_vdev, WLAN_TDLS_NB_ID); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_get_vdev() - Get tdls specific vdev object manager + * @psoc: wlan psoc object manager + * @dbg_id: debug id + * + * If TDLS possible, return the corresponding vdev + * to enable TDLS in the system. + * + * Return: vdev manager pointer or NULL. + */ +struct wlan_objmgr_vdev *tdls_get_vdev(struct wlan_objmgr_psoc *psoc, + wlan_objmgr_ref_dbgid dbg_id) +{ + uint32_t vdev_id; + + if (policy_mgr_get_connection_count(psoc) > 1) + return NULL; + + vdev_id = policy_mgr_mode_specific_vdev_id(psoc, PM_STA_MODE); + + if (WLAN_INVALID_VDEV_ID != vdev_id) + return wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, + dbg_id); + + vdev_id = policy_mgr_mode_specific_vdev_id(psoc, PM_P2P_CLIENT_MODE); + + if (WLAN_INVALID_VDEV_ID != vdev_id) + return wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, + dbg_id); + + return NULL; +} + +static QDF_STATUS tdls_post_msg_flush_cb(struct scheduler_msg *msg) +{ + void *ptr = msg->bodyptr; + struct wlan_objmgr_vdev *vdev = NULL; + + switch (msg->type) { + case TDLS_NOTIFY_STA_DISCONNECTION: + vdev = ((struct tdls_sta_notify_params *)ptr)->vdev; + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(ptr); + break; + + case TDLS_DELETE_ALL_PEERS_INDICATION: + vdev = ((struct tdls_delete_all_peers_params *)ptr)->vdev; + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(ptr); + break; + + case TDLS_CMD_SCAN_DONE: + case TDLS_CMD_SESSION_INCREMENT: + case TDLS_CMD_SESSION_DECREMENT: + break; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_process_session_update() - update session count information + * @psoc: soc object + * @notification: TDLS os if notification + * + * update the session information in connection tracker + * + * Return: None + */ +static void tdls_process_session_update(struct wlan_objmgr_psoc *psoc, + enum tdls_command_type cmd_type) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + msg.bodyptr = psoc; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_post_msg_flush_cb; + msg.type = (uint16_t)cmd_type; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) + tdls_alert("message post failed "); +} + +void tdls_notify_increment_session(struct wlan_objmgr_psoc *psoc) +{ + tdls_process_session_update(psoc, TDLS_CMD_SESSION_INCREMENT); +} + +void tdls_notify_decrement_session(struct wlan_objmgr_psoc *psoc) +{ + tdls_process_session_update(psoc, TDLS_CMD_SESSION_DECREMENT); +} + +void tdls_send_update_to_fw(struct tdls_vdev_priv_obj *tdls_vdev_obj, + struct tdls_soc_priv_obj *tdls_soc_obj, + bool tdls_prohibited, + bool tdls_chan_swit_prohibited, + bool sta_connect_event, + uint8_t session_id) +{ + struct tdls_info *tdls_info_to_fw; + struct tdls_config_params *threshold_params; + uint32_t tdls_feature_flags; + QDF_STATUS status; + uint8_t set_state_cnt; + + tdls_feature_flags = tdls_soc_obj->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_ENABLED(tdls_feature_flags)) { + tdls_debug("TDLS mode is not enabled"); + return; + } + + set_state_cnt = tdls_soc_obj->set_state_info.set_state_cnt; + if ((set_state_cnt == 0 && !sta_connect_event) || + (set_state_cnt && sta_connect_event)) { + tdls_debug("FW TDLS state is already in requested state"); + return; + } + + /* If AP or caller indicated TDLS Prohibited then disable tdls mode */ + if (sta_connect_event) { + if (tdls_prohibited) { + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_DISABLED; + } else { + if (!TDLS_IS_IMPLICIT_TRIG_ENABLED(tdls_feature_flags)) + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_EXP_TRIG_ONLY; + else if (TDLS_IS_EXTERNAL_CONTROL_ENABLED( + tdls_feature_flags)) + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_EXT_CONTROL; + else + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_IMP_MODE; + } + } else { + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_DISABLED; + } + + tdls_info_to_fw = qdf_mem_malloc(sizeof(struct tdls_info)); + + if (!tdls_info_to_fw) { + tdls_err("memory allocation failed for tdlsParams"); + QDF_ASSERT(0); + return; + } + + threshold_params = &tdls_vdev_obj->threshold_config; + + tdls_info_to_fw->notification_interval_ms = + threshold_params->tx_period_t; + tdls_info_to_fw->tx_discovery_threshold = + threshold_params->tx_packet_n; + tdls_info_to_fw->tx_teardown_threshold = + threshold_params->idle_packet_n; + tdls_info_to_fw->rssi_teardown_threshold = + threshold_params->rssi_teardown_threshold; + tdls_info_to_fw->rssi_delta = threshold_params->rssi_delta; + tdls_info_to_fw->vdev_id = session_id; + + /* record the session id in vdev context */ + tdls_vdev_obj->session_id = session_id; + tdls_info_to_fw->tdls_state = tdls_soc_obj->tdls_current_mode; + tdls_info_to_fw->tdls_options = 0; + + /* Do not enable TDLS offchannel, if AP prohibited TDLS + * channel switch + */ + if (TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) && + (!tdls_chan_swit_prohibited)) + tdls_info_to_fw->tdls_options = ENA_TDLS_OFFCHAN; + + if (TDLS_IS_BUFFER_STA_ENABLED(tdls_feature_flags)) + tdls_info_to_fw->tdls_options |= ENA_TDLS_BUFFER_STA; + if (TDLS_IS_SLEEP_STA_ENABLED(tdls_feature_flags)) + tdls_info_to_fw->tdls_options |= ENA_TDLS_SLEEP_STA; + + + tdls_info_to_fw->peer_traffic_ind_window = + tdls_soc_obj->tdls_configs.tdls_uapsd_pti_window; + tdls_info_to_fw->peer_traffic_response_timeout = + tdls_soc_obj->tdls_configs.tdls_uapsd_ptr_timeout; + tdls_info_to_fw->puapsd_mask = + tdls_soc_obj->tdls_configs.tdls_uapsd_mask; + tdls_info_to_fw->puapsd_inactivity_time = + tdls_soc_obj->tdls_configs.tdls_uapsd_inactivity_time; + tdls_info_to_fw->puapsd_rx_frame_threshold = + tdls_soc_obj->tdls_configs.tdls_rx_pkt_threshold; + tdls_info_to_fw->teardown_notification_ms = + tdls_soc_obj->tdls_configs.tdls_idle_timeout; + tdls_info_to_fw->tdls_peer_kickout_threshold = + tdls_soc_obj->tdls_configs.tdls_peer_kickout_threshold; + tdls_info_to_fw->tdls_discovery_wake_timeout = + tdls_soc_obj->tdls_configs.tdls_discovery_wake_timeout; + + status = tdls_update_fw_tdls_state(tdls_soc_obj, tdls_info_to_fw); + if (QDF_STATUS_SUCCESS != status) + goto done; + + if (sta_connect_event) { + tdls_soc_obj->set_state_info.set_state_cnt++; + tdls_soc_obj->set_state_info.vdev_id = session_id; + } else { + tdls_soc_obj->set_state_info.set_state_cnt--; + } + + tdls_debug("TDLS Set state cnt %d", + tdls_soc_obj->set_state_info.set_state_cnt); +done: + qdf_mem_free(tdls_info_to_fw); + return; +} + +static QDF_STATUS +tdls_process_sta_connect(struct tdls_sta_notify_params *notify) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(notify->vdev, + &tdls_vdev_obj, + &tdls_soc_obj)) + return QDF_STATUS_E_INVAL; + + + if (policy_mgr_get_connection_count(tdls_soc_obj->soc) > 1) { + tdls_debug("Concurrent sessions exist, TDLS can't be enabled"); + return QDF_STATUS_SUCCESS; + } + + /* Association event */ + if (!tdls_soc_obj->tdls_disable_in_progress) { + tdls_send_update_to_fw(tdls_vdev_obj, + tdls_soc_obj, + notify->tdls_prohibited, + notify->tdls_chan_swit_prohibited, + true, + notify->session_id); + } + + /* check and set the connection tracker */ + tdls_set_ct_mode(tdls_soc_obj->soc); + if (tdls_soc_obj->enable_tdls_connection_tracker) + tdls_implicit_enable(tdls_vdev_obj); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_notify_sta_connect(struct tdls_sta_notify_params *notify) +{ + QDF_STATUS status; + + if (!notify || !notify->vdev) { + tdls_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + status = tdls_process_sta_connect(notify); + + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + + return status; +} + +static QDF_STATUS +tdls_process_sta_disconnect(struct tdls_sta_notify_params *notify) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_vdev_priv_obj *curr_tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc_obj; + struct tdls_soc_priv_obj *curr_tdls_soc; + struct wlan_objmgr_vdev *temp_vdev = NULL; + uint8_t vdev_id; + + + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(notify->vdev, + &tdls_vdev_obj, + &tdls_soc_obj)) + return QDF_STATUS_E_INVAL; + + /* if the disconnect comes from user space, we have to delete all the + * tdls peers before sending the set state cmd. + */ + if (notify->user_disconnect) + return tdls_delete_all_tdls_peers(notify->vdev, tdls_soc_obj); + + tdls_debug("Check and update TDLS state"); + + curr_tdls_vdev = tdls_vdev_obj; + curr_tdls_soc = tdls_soc_obj; + + /* Disassociation event */ + if (!tdls_soc_obj->tdls_disable_in_progress) + tdls_send_update_to_fw(tdls_vdev_obj, tdls_soc_obj, false, + false, false, notify->session_id); + + /* If concurrency is not marked, then we have to + * check, whether TDLS could be enabled in the + * system after this disassoc event. + */ + if (!notify->lfr_roam && !tdls_soc_obj->tdls_disable_in_progress) { + temp_vdev = tdls_get_vdev(tdls_soc_obj->soc, WLAN_TDLS_NB_ID); + if (temp_vdev) { + vdev_id = wlan_vdev_get_id(temp_vdev); + status = tdls_get_vdev_objects(temp_vdev, + &tdls_vdev_obj, + &tdls_soc_obj); + if (QDF_STATUS_SUCCESS == status) { + tdls_send_update_to_fw(tdls_vdev_obj, + tdls_soc_obj, + false, + false, + true, + vdev_id); + curr_tdls_vdev = tdls_vdev_obj; + curr_tdls_soc = tdls_soc_obj; + } + } + } + + /* Check and set the connection tracker and implicit timers */ + tdls_set_ct_mode(curr_tdls_soc->soc); + if (curr_tdls_soc->enable_tdls_connection_tracker) + tdls_implicit_enable(curr_tdls_vdev); + else + tdls_implicit_disable(curr_tdls_vdev); + + /* release the vdev ref , if temp vdev was acquired */ + if (temp_vdev) + wlan_objmgr_vdev_release_ref(temp_vdev, + WLAN_TDLS_NB_ID); + + return status; +} + +QDF_STATUS tdls_notify_sta_disconnect(struct tdls_sta_notify_params *notify) +{ + QDF_STATUS status; + + if (!notify || !notify->vdev) { + tdls_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + status = tdls_process_sta_disconnect(notify); + + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + + return status; +} + +static void tdls_process_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ + struct tdls_vdev_priv_obj *tdls_vdev; + + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_vdev) + return; + tdls_timers_stop(tdls_vdev); +} + +void tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + QDF_ASSERT(0); + return; + } + + if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev, + WLAN_TDLS_NB_ID)) + return; + + tdls_process_reset_adapter(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +} + +QDF_STATUS tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + struct scheduler_msg msg = {0, }; + struct tdls_sta_notify_params *notify; + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + notify = qdf_mem_malloc(sizeof(*notify)); + if (!notify) { + tdls_err("memory allocation failed !!!"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, + WLAN_TDLS_NB_ID); + + if (!vdev) { + tdls_err("vdev not exist for the vdev id %d", + vdev_id); + qdf_mem_free(notify); + return QDF_STATUS_E_INVAL; + } + + notify->lfr_roam = true; + notify->tdls_chan_swit_prohibited = false; + notify->tdls_prohibited = false; + notify->session_id = vdev_id; + notify->vdev = vdev; + notify->user_disconnect = false; + + msg.bodyptr = notify; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_post_msg_flush_cb; + msg.type = TDLS_NOTIFY_STA_DISCONNECTION; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + tdls_alert("message post failed "); + + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_delete_all_peers_indication(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + struct scheduler_msg msg = {0, }; + struct tdls_delete_all_peers_params *indication; + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + indication = qdf_mem_malloc(sizeof(*indication)); + if (!indication) { + tdls_err("memory allocation failed !!!"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, + WLAN_TDLS_SB_ID); + + if (!vdev) { + tdls_err("vdev not exist for the session id %d", + vdev_id); + qdf_mem_free(indication); + return QDF_STATUS_E_INVAL; + } + + indication->vdev = vdev; + + msg.bodyptr = indication; + msg.callback = tdls_process_cmd; + msg.type = TDLS_DELETE_ALL_PEERS_INDICATION; + msg.flush_callback = tdls_post_msg_flush_cb; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(indication); + tdls_alert("message post failed "); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_set_mode_in_vdev() - set TDLS mode + * @tdls_vdev: tdls vdev object + * @tdls_soc: tdls soc object + * @tdls_mode: TDLS mode + * @source: TDLS disable source enum values + * + * Return: Void + */ +static void tdls_set_mode_in_vdev(struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdls_soc, + enum tdls_feature_mode tdls_mode, + enum tdls_disable_sources source) +{ + if (!tdls_vdev) + return; + tdls_debug("enter tdls mode is %d", tdls_mode); + + if (TDLS_SUPPORT_IMP_MODE == tdls_mode || + TDLS_SUPPORT_EXT_CONTROL == tdls_mode) { + clear_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + /* + * Check if any TDLS source bit is set and if + * bitmap is not zero then we should not + * enable TDLS + */ + if (tdls_soc->tdls_source_bitmap) { + tdls_notice("Don't enable TDLS, source bitmap: %lu", + tdls_soc->tdls_source_bitmap); + return; + } + tdls_implicit_enable(tdls_vdev); + /* tdls implicit mode is enabled, so + * enable the connection tracker + */ + tdls_soc->enable_tdls_connection_tracker = + true; + } else if (TDLS_SUPPORT_DISABLED == tdls_mode) { + set_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + tdls_implicit_disable(tdls_vdev); + /* If tdls implicit mode is disabled, then + * stop the connection tracker. + */ + tdls_soc->enable_tdls_connection_tracker = + false; + } else if (TDLS_SUPPORT_EXP_TRIG_ONLY == + tdls_mode) { + clear_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + tdls_implicit_disable(tdls_vdev); + /* If tdls implicit mode is disabled, then + * stop the connection tracker. + */ + tdls_soc->enable_tdls_connection_tracker = + false; + + /* + * Check if any TDLS source bit is set and if + * bitmap is not zero then we should not + * enable TDLS + */ + if (tdls_soc->tdls_source_bitmap) + return; + } + tdls_debug("exit "); + +} + +/** + * tdls_set_current_mode() - set TDLS mode + * @tdls_soc: tdls soc object + * @tdls_mode: TDLS mode + * @update_last: indicate to record the last tdls mode + * @source: TDLS disable source enum values + * + * Return: Void + */ +static void tdls_set_current_mode(struct tdls_soc_priv_obj *tdls_soc, + enum tdls_feature_mode tdls_mode, + bool update_last, + enum tdls_disable_sources source) +{ + struct wlan_objmgr_vdev *vdev; + struct tdls_vdev_priv_obj *tdls_vdev; + + if (!tdls_soc) + return; + + tdls_debug("mode %d", (int)tdls_mode); + + if (update_last) + tdls_soc->tdls_last_mode = tdls_mode; + + if (tdls_soc->tdls_current_mode == tdls_mode) { + tdls_debug("already in mode %d", tdls_mode); + + switch (tdls_mode) { + /* TDLS is already enabled hence clear source mask, return */ + case TDLS_SUPPORT_IMP_MODE: + case TDLS_SUPPORT_EXP_TRIG_ONLY: + case TDLS_SUPPORT_EXT_CONTROL: + clear_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + tdls_debug("clear source mask:%d", source); + return; + /* TDLS is already disabled hence set source mask, return */ + case TDLS_SUPPORT_DISABLED: + set_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + tdls_debug("set source mask:%d", source); + return; + default: + return; + } + } + + /* get sta vdev */ + vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc(tdls_soc->soc, + QDF_STA_MODE, + WLAN_TDLS_NB_ID); + if (vdev) { + tdls_debug("set mode in tdls vdev "); + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_vdev) + tdls_set_mode_in_vdev(tdls_vdev, tdls_soc, + tdls_mode, source); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + } + + /* get p2p client vdev */ + vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc(tdls_soc->soc, + QDF_P2P_CLIENT_MODE, + WLAN_TDLS_NB_ID); + if (vdev) { + tdls_debug("set mode in tdls vdev "); + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_vdev) + tdls_set_mode_in_vdev(tdls_vdev, tdls_soc, + tdls_mode, source); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + } + + if (!update_last) + tdls_soc->tdls_last_mode = tdls_soc->tdls_current_mode; + + tdls_soc->tdls_current_mode = tdls_mode; + +} + +QDF_STATUS tdls_set_operation_mode(struct tdls_set_mode_params *tdls_set_mode) +{ + struct tdls_soc_priv_obj *tdls_soc; + struct tdls_vdev_priv_obj *tdls_vdev; + QDF_STATUS status; + + if (!tdls_set_mode || !tdls_set_mode->vdev) + return QDF_STATUS_E_INVAL; + + status = tdls_get_vdev_objects(tdls_set_mode->vdev, + &tdls_vdev, &tdls_soc); + + if (QDF_IS_STATUS_ERROR(status)) + goto release_mode_ref; + + tdls_set_current_mode(tdls_soc, + tdls_set_mode->tdls_mode, + tdls_set_mode->update_last, + tdls_set_mode->source); + +release_mode_ref: + wlan_objmgr_vdev_release_ref(tdls_set_mode->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(tdls_set_mode); + return status; +} + +/** + * wlan_hdd_tdls_scan_done_callback() - callback for tdls scan done event + * @pAdapter: HDD adapter + * + * Return: Void + */ +void tdls_scan_done_callback(struct tdls_soc_priv_obj *tdls_soc) +{ + if (!tdls_soc) + return; + + if (TDLS_SUPPORT_DISABLED == tdls_soc->tdls_current_mode) { + tdls_debug_rl("TDLS mode is disabled OR not enabled"); + return; + } + + /* if tdls was enabled before scan, re-enable tdls mode */ + if (TDLS_SUPPORT_IMP_MODE == tdls_soc->tdls_last_mode || + TDLS_SUPPORT_EXT_CONTROL == tdls_soc->tdls_last_mode || + TDLS_SUPPORT_EXP_TRIG_ONLY == tdls_soc->tdls_last_mode) + tdls_set_current_mode(tdls_soc, tdls_soc->tdls_last_mode, + false, TDLS_SET_MODE_SOURCE_SCAN); +} + +/** + * tdls_post_scan_done_msg() - post scan done message to tdls cmd queue + * @tdls_soc: tdls soc object + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_E_NULL_VALUE + */ +static QDF_STATUS tdls_post_scan_done_msg(struct tdls_soc_priv_obj *tdls_soc) +{ + struct scheduler_msg msg = {0, }; + + if (!tdls_soc) { + tdls_err("tdls_soc: %pK ", tdls_soc); + return QDF_STATUS_E_NULL_VALUE; + } + + msg.bodyptr = tdls_soc; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_post_msg_flush_cb; + msg.type = TDLS_CMD_SCAN_DONE; + + return scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); +} + +void tdls_scan_complete_event_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, + void *arg) +{ + enum QDF_OPMODE device_mode; + struct tdls_soc_priv_obj *tdls_soc; + + if (!vdev || !event || !arg) + return; + + if (SCAN_EVENT_TYPE_COMPLETED != event->type) + return; + + device_mode = wlan_vdev_mlme_get_opmode(vdev); + + if (device_mode != QDF_STA_MODE && + device_mode != QDF_P2P_CLIENT_MODE) + return; + tdls_soc = (struct tdls_soc_priv_obj *) arg; + tdls_post_scan_done_msg(tdls_soc); +} + +QDF_STATUS tdls_scan_callback(struct tdls_soc_priv_obj *tdls_soc) +{ + struct tdls_vdev_priv_obj *tdls_vdev; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* if tdls is not enabled, then continue scan */ + if (TDLS_SUPPORT_DISABLED == tdls_soc->tdls_current_mode) + return status; + + /* Get the vdev based on vdev operating mode*/ + vdev = tdls_get_vdev(tdls_soc->soc, WLAN_TDLS_NB_ID); + if (!vdev) + return status; + + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_vdev) + goto return_success; + + if (tdls_is_progress(tdls_vdev, NULL, 0)) { + if (tdls_soc->scan_reject_count++ >= TDLS_SCAN_REJECT_MAX) { + tdls_notice("Allow this scan req. as already max no of scan's are rejected"); + tdls_soc->scan_reject_count = 0; + status = QDF_STATUS_SUCCESS; + + } else { + tdls_warn("tdls in progress. scan rejected %d", + tdls_soc->scan_reject_count); + status = QDF_STATUS_E_BUSY; + } + } +return_success: + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); + return status; +} + +void tdls_scan_serialization_comp_info_cb(struct wlan_objmgr_vdev *vdev, + union wlan_serialization_rules_info *comp_info) +{ + struct tdls_soc_priv_obj *tdls_soc; + QDF_STATUS status; + if (!comp_info) + return; + + tdls_soc = tdls_soc_global; + comp_info->scan_info.is_tdls_in_progress = false; + status = tdls_scan_callback(tdls_soc); + if (QDF_STATUS_E_BUSY == status) + comp_info->scan_info.is_tdls_in_progress = true; +} + + +uint8_t tdls_get_opclass_from_bandwidth(struct tdls_soc_priv_obj *soc_obj, + uint8_t channel, uint8_t bw_offset, + uint8_t *reg_bw_offset) +{ + uint8_t opclass; + + if (bw_offset & (1 << BW_80_OFFSET_BIT)) { + opclass = tdls_find_opclass(soc_obj->soc, + channel, BW80); + *reg_bw_offset = BW80; + } else if (bw_offset & (1 << BW_40_OFFSET_BIT)) { + opclass = tdls_find_opclass(soc_obj->soc, + channel, BW40_LOW_PRIMARY); + *reg_bw_offset = BW40_LOW_PRIMARY; + if (!opclass) { + opclass = tdls_find_opclass(soc_obj->soc, + channel, BW40_HIGH_PRIMARY); + *reg_bw_offset = BW40_HIGH_PRIMARY; + } + } else if (bw_offset & (1 << BW_20_OFFSET_BIT)) { + opclass = tdls_find_opclass(soc_obj->soc, + channel, BW20); + *reg_bw_offset = BW20; + } else { + opclass = tdls_find_opclass(soc_obj->soc, + channel, BWALL); + *reg_bw_offset = BWALL; + } + + return opclass; +} diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_main.h b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_main.h new file mode 100644 index 0000000000000000000000000000000000000000..f9df6d91fc9a43d0b2e1467c70a99cdddd6bbc0a --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_main.h @@ -0,0 +1,773 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_main.h + * + * TDLS core function declaration + */ + +#if !defined(_WLAN_TDLS_MAIN_H_) +#define _WLAN_TDLS_MAIN_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_serialization_api.h" +#include + + +/* Bit mask flag for tdls_option to FW */ +#define ENA_TDLS_OFFCHAN (1 << 0) /* TDLS Off Channel support */ +#define ENA_TDLS_BUFFER_STA (1 << 1) /* TDLS Buffer STA support */ +#define ENA_TDLS_SLEEP_STA (1 << 2) /* TDLS Sleep STA support */ + +#define BW_20_OFFSET_BIT 0 +#define BW_40_OFFSET_BIT 1 +#define BW_80_OFFSET_BIT 2 +#define BW_160_OFFSET_BIT 3 + +#define TDLS_SEC_OFFCHAN_OFFSET_0 0 +#define TDLS_SEC_OFFCHAN_OFFSET_40PLUS 40 +#define TDLS_SEC_OFFCHAN_OFFSET_40MINUS (-40) +#define TDLS_SEC_OFFCHAN_OFFSET_80 80 +#define TDLS_SEC_OFFCHAN_OFFSET_160 160 +/* + * Before UpdateTimer expires, we want to timeout discovery response + * should not be more than 2000. + */ +#define TDLS_DISCOVERY_TIMEOUT_BEFORE_UPDATE 1000 +#define TDLS_SCAN_REJECT_MAX 5 + +#define tdls_debug(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_TDLS, params) +#define tdls_debug_rl(params...) \ + QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_TDLS, params) + +#define tdls_notice(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_TDLS, params) +#define tdls_warn(params...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_TDLS, params) +#define tdls_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_TDLS, params) +#define tdls_alert(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_TDLS, params) + +#define tdls_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_TDLS, params) +#define tdls_nofl_notice(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_TDLS, params) +#define tdls_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_TDLS, params) +#define tdls_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_TDLS, params) +#define tdls_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_TDLS, params) + +#define TDLS_IS_LINK_CONNECTED(peer) \ + ((TDLS_LINK_CONNECTED == (peer)->link_status) || \ + (TDLS_LINK_TEARING == (peer)->link_status)) + +#define SET_BIT(value, mask) ((value) |= (1 << (mask))) +#define CLEAR_BIT(value, mask) ((value) &= ~(1 << (mask))) +#define CHECK_BIT(value, mask) ((value) & (1 << (mask))) +/** + * struct tdls_conn_info - TDLS connection record + * @session_id: session id + * @valid_entry: valid entry(set to true upon peer create resp from firmware) + * @peer_mac: peer address + * @index: index to store array offset. + */ +struct tdls_conn_info { + uint8_t session_id; + bool valid_entry; + uint8_t index; + struct qdf_mac_addr peer_mac; +}; + +/** + * enum tdls_nss_transition_state - TDLS NSS transition states + * @TDLS_NSS_TRANSITION_UNKNOWN: default state + * @TDLS_NSS_TRANSITION_2x2_to_1x1: transition from 2x2 to 1x1 stream + * @TDLS_NSS_TRANSITION_1x1_to_2x2: transition from 1x1 to 2x2 stream + */ +enum tdls_nss_transition_state { + TDLS_NSS_TRANSITION_S_UNKNOWN = 0, + TDLS_NSS_TRANSITION_S_2x2_to_1x1, + TDLS_NSS_TRANSITION_S_1x1_to_2x2, +}; + +/** + * struct tdls_conn_tracker_mac_table - connection tracker peer table + * @mac_address: peer mac address + * @tx_packet_cnt: number of tx pkts + * @rx_packet_cnt: number of rx pkts + * @peer_timestamp_ms: time stamp of latest peer traffic + */ +struct tdls_conn_tracker_mac_table { + struct qdf_mac_addr mac_address; + uint32_t tx_packet_cnt; + uint32_t rx_packet_cnt; + uint32_t peer_timestamp_ms; +}; + +/** + * struct tdls_set_state_db - to record set tdls state command, we need to + * set correct tdls state to firmware: + * 1. enable tdls in firmware before tdls connection; + * 2. disable tdls if concurrency happen, before disable tdls, all active peer + * should be deleted in firmware. + * + * @set_state_cnt: tdls set state count + * @vdev_id: vdev id of last set state command + */ +struct tdls_set_state_info { + uint8_t set_state_cnt; + uint8_t vdev_id; +}; + +/** + * struct tdls_psoc_priv_ctx - tdls context + * @soc: objmgr psoc + * @tdls_current_mode: current tdls mode + * @tdls_last_mode: last tdls mode + * @scan_reject_count: number of times scan rejected due to TDLS + * @tdls_source_bitmap: bit map to set/reset TDLS by different sources + * @tdls_conn_info: this tdls_conn_info can be removed and we can use peer type + * of peer object to get the active tdls peers + * @tdls_configs: tdls user configure + * @max_num_tdls_sta: maximum TDLS station number allowed upon runtime condition + * @connected_peer_count: tdls peer connected count + * @tdls_off_channel: tdls off channel number + * @tdls_channel_offset: tdls channel offset + * @tdls_fw_off_chan_mode: tdls fw off channel mode + * @enable_tdls_connection_tracker: enable tdls connection tracker + * @tdls_external_peer_count: external tdls peer count + * @tdls_nss_switch_in_progress: tdls antenna switch in progress + * @tdls_nss_teardown_complete: tdls tear down complete + * @tdls_disable_in_progress: tdls is disable in progress + * @tdls_nss_transition_mode: tdls nss transition mode + * @tdls_teardown_peers_cnt: tdls tear down peer count + * @set_state_info: set tdls state info + * @tdls_event_cb: tdls event callback + * @tdls_evt_cb_data: tdls event user data + * @tdls_peer_context: userdata for register/deregister TDLS peer + * @tdls_reg_peer: register tdls peer with datapath + * @tx_q_ack: queue for tx frames waiting for ack + * @tdls_con_cap: tdls concurrency support + * @tdls_send_mgmt_req: store eWNI_SME_TDLS_SEND_MGMT_REQ value + * @tdls_add_sta_req: store eWNI_SME_TDLS_ADD_STA_REQ value + * @tdls_del_sta_req: store eWNI_SME_TDLS_DEL_STA_REQ value + * @tdls_update_peer_state: store WMA_UPDATE_TDLS_PEER_STATE value + * @tdls_del_all_peers:store eWNI_SME_DEL_ALL_TDLS_PEERS + * @tdls_update_dp_vdev_flags store CDP_UPDATE_TDLS_FLAGS + * @tdls_idle_peer_data: provide information about idle peer + * @tdls_ct_spinlock: connection tracker spin lock + * @is_prevent_suspend: prevent suspend or not + * @is_drv_supported: platform supports drv or not, enable/disable tdls wow + * based on this flag. + * @wake_lock: wake lock + * @runtime_lock: runtime lock + * @tdls_osif_init_cb: Callback to initialize the tdls private + * @tdls_osif_deinit_cb: Callback to deinitialize the tdls private + */ +struct tdls_soc_priv_obj { + struct wlan_objmgr_psoc *soc; + enum tdls_feature_mode tdls_current_mode; + enum tdls_feature_mode tdls_last_mode; + int scan_reject_count; + unsigned long tdls_source_bitmap; + struct tdls_conn_info tdls_conn_info[WLAN_TDLS_STA_MAX_NUM]; + struct tdls_user_config tdls_configs; + uint16_t max_num_tdls_sta; + uint16_t connected_peer_count; + uint8_t tdls_off_channel; + uint16_t tdls_channel_offset; + int32_t tdls_fw_off_chan_mode; + bool enable_tdls_connection_tracker; + uint8_t tdls_external_peer_count; + bool tdls_nss_switch_in_progress; + bool tdls_nss_teardown_complete; + bool tdls_disable_in_progress; + enum tdls_nss_transition_state tdls_nss_transition_mode; + int32_t tdls_teardown_peers_cnt; + struct tdls_set_state_info set_state_info; + tdls_rx_callback tdls_rx_cb; + void *tdls_rx_cb_data; + tdls_wmm_check tdls_wmm_cb; + void *tdls_wmm_cb_data; + tdls_evt_callback tdls_event_cb; + void *tdls_evt_cb_data; + void *tdls_peer_context; + tdls_register_peer_callback tdls_reg_peer; + tdls_dp_vdev_update_flags_callback tdls_dp_vdev_update; + qdf_list_t tx_q_ack; + enum tdls_conc_cap tdls_con_cap; + uint16_t tdls_send_mgmt_req; + uint16_t tdls_add_sta_req; + uint16_t tdls_del_sta_req; + uint16_t tdls_update_peer_state; + uint16_t tdls_del_all_peers; + uint32_t tdls_update_dp_vdev_flags; + qdf_spinlock_t tdls_ct_spinlock; +#ifdef TDLS_WOW_ENABLED + bool is_prevent_suspend; + bool is_drv_supported; + qdf_wake_lock_t wake_lock; + qdf_runtime_lock_t runtime_lock; +#endif + tdls_vdev_init_cb tdls_osif_init_cb; + tdls_vdev_deinit_cb tdls_osif_deinit_cb; +}; + +/** + * struct tdls_vdev_priv_obj - tdls private vdev object + * @vdev: vdev objmgr object + * @peer_list: tdls peer list on this vdev + * @peer_update_timer: connection tracker timer + * @peer_dicovery_timer: peer discovery timer + * @threshold_config: threshold config + * @discovery_peer_cnt: discovery peer count + * @discovery_sent_cnt: discovery sent count + * @curr_candidate: current candidate + * @ct_peer_table: linear mac address table for counting the packets + * @valid_mac_entries: number of valid mac entry in @ct_peer_mac_table + * @magic: magic + * @tx_queue: tx frame queue + */ +struct tdls_vdev_priv_obj { + struct wlan_objmgr_vdev *vdev; + qdf_list_t peer_list[WLAN_TDLS_PEER_LIST_SIZE]; + qdf_mc_timer_t peer_update_timer; + qdf_mc_timer_t peer_discovery_timer; + struct tdls_config_params threshold_config; + int32_t discovery_peer_cnt; + uint32_t discovery_sent_cnt; + struct tdls_peer *curr_candidate; + struct tdls_conn_tracker_mac_table + ct_peer_table[WLAN_TDLS_CT_TABLE_SIZE]; + uint8_t valid_mac_entries; + uint32_t magic; + uint8_t session_id; + qdf_list_t tx_queue; +}; + +/** + * struct tdls_peer_mlme_info - tdls peer mlme info + **/ +struct tdls_peer_mlme_info { +}; + +/** + * struct tdls_peer - tdls peer data + * @node: node + * @vdev_priv: tdls vdev priv obj + * @peer_mac: peer mac address + * @valid_entry: entry valid or not (set to true when peer create resp is + * received from FW) + * @rssi: rssi + * @tdls_support: tdls support + * @link_status: tdls link status + * @is_responder: is responder + * @discovery_processed: dicovery processed + * @discovery_attempt: discovery attempt + * @tx_pkt: tx packet + * @rx_pkt: rx packet + * @uapsd_queues: uapsd queues + * @max_sp: max sp + * @buf_sta_capable: is buffer sta + * @off_channel_capable: is offchannel supported flag + * @supported_channels_len: supported channels length + * @supported_channels: supported channels + * @supported_oper_classes_len: supported operation classes length + * @supported_oper_classes: supported operation classes + * @is_forced_peer: is forced peer + * @op_class_for_pref_off_chan: op class for preferred off channel + * @pref_off_chan_num: preferred off channel number + * @peer_idle_timer: time to check idle traffic in tdls peers + * @is_peer_idle_timer_initialised: Flag to check idle timer init + * @spatial_streams: Number of TX/RX spatial streams for TDLS + * @reason: reason + * @state_change_notification: state change notification + * @qos: QOS capability of TDLS link + */ +struct tdls_peer { + qdf_list_node_t node; + struct tdls_vdev_priv_obj *vdev_priv; + struct qdf_mac_addr peer_mac; + bool valid_entry; + int8_t rssi; + enum tdls_peer_capab tdls_support; + enum tdls_link_state link_status; + uint8_t is_responder; + uint8_t discovery_processed; + uint16_t discovery_attempt; + uint16_t tx_pkt; + uint16_t rx_pkt; + uint8_t uapsd_queues; + uint8_t max_sp; + uint8_t buf_sta_capable; + uint8_t off_channel_capable; + uint8_t supported_channels_len; + uint8_t supported_channels[WLAN_MAC_MAX_SUPP_CHANNELS]; + uint8_t supported_oper_classes_len; + uint8_t supported_oper_classes[WLAN_MAX_SUPP_OPER_CLASSES]; + bool is_forced_peer; + uint8_t op_class_for_pref_off_chan; + uint8_t pref_off_chan_num; + qdf_mc_timer_t peer_idle_timer; + bool is_peer_idle_timer_initialised; + uint8_t spatial_streams; + enum tdls_link_state_reason reason; + tdls_state_change_callback state_change_notification; + uint8_t qos; + struct tdls_peer_mlme_info *tdls_info; +}; + +/** + * struct tdls_os_if_event - TDLS os event info + * @type: type of event + * @info: pointer to event information + */ +struct tdls_os_if_event { + uint32_t type; + void *info; +}; + +/** + * enum tdls_os_if_notification - TDLS notification from OS IF + * @TDLS_NOTIFY_STA_SESSION_INCREMENT: sta session count incremented + * @TDLS_NOTIFY_STA_SESSION_DECREMENT: sta session count decremented + */ +enum tdls_os_if_notification { + TDLS_NOTIFY_STA_SESSION_INCREMENT, + TDLS_NOTIFY_STA_SESSION_DECREMENT +}; +/** + * wlan_vdev_get_tdls_soc_obj - private API to get tdls soc object from vdev + * @vdev: vdev object + * + * Return: tdls soc object + */ +static inline struct tdls_soc_priv_obj * +wlan_vdev_get_tdls_soc_obj(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_psoc *psoc; + struct tdls_soc_priv_obj *soc_obj; + + if (!vdev) { + tdls_err("NULL vdev"); + return NULL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + tdls_err("can't get psoc"); + return NULL; + } + + soc_obj = (struct tdls_soc_priv_obj *) + wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + + return soc_obj; +} + +/** + * wlan_psoc_get_tdls_soc_obj - private API to get tdls soc object from psoc + * @psoc: psoc object + * + * Return: tdls soc object + */ +static inline struct tdls_soc_priv_obj * +wlan_psoc_get_tdls_soc_obj(struct wlan_objmgr_psoc *psoc) +{ + struct tdls_soc_priv_obj *soc_obj; + if (!psoc) { + tdls_err("NULL psoc"); + return NULL; + } + soc_obj = (struct tdls_soc_priv_obj *) + wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + + return soc_obj; +} + +/** + * wlan_vdev_get_tdls_vdev_obj - private API to get tdls vdev object from vdev + * @vdev: vdev object + * + * Return: tdls vdev object + */ +static inline struct tdls_vdev_priv_obj * +wlan_vdev_get_tdls_vdev_obj(struct wlan_objmgr_vdev *vdev) +{ + struct tdls_vdev_priv_obj *vdev_obj; + + if (!vdev) { + tdls_err("NULL vdev"); + return NULL; + } + + vdev_obj = (struct tdls_vdev_priv_obj *) + wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + + return vdev_obj; +} + +/** + * tdls_set_link_status - tdls set link status + * @vdev: vdev object + * @mac: mac address of tdls peer + * @link_state: tdls link state + * @link_reason: reason + */ +void tdls_set_link_status(struct tdls_vdev_priv_obj *vdev, + const uint8_t *mac, + enum tdls_link_state link_state, + enum tdls_link_state_reason link_reason); +/** + * tdls_psoc_obj_create_notification() - tdls psoc create notification handler + * @psoc: psoc object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list); + +/** + * tdls_psoc_obj_destroy_notification() - tdls psoc destroy notification handler + * @psoc: psoc object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_psoc_obj_destroy_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list); + +/** + * tdls_vdev_obj_create_notification() - tdls vdev create notification handler + * @vdev: vdev object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev, + void *arg_list); + +/** + * tdls_vdev_obj_destroy_notification() - tdls vdev destroy notification handler + * @vdev: vdev object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_vdev_obj_destroy_notification(struct wlan_objmgr_vdev *vdev, + void *arg_list); + +/** + * tdls_process_cmd() - tdls main command process function + * @msg: scheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_cmd(struct scheduler_msg *msg); + +/** + * tdls_process_evt() - tdls main event process function + * @msg: scheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_evt(struct scheduler_msg *msg); + +/** + * tdls_timer_restart() - restart TDLS timer + * @vdev: VDEV object manager + * @timer: timer to restart + * @expiration_time: new expiration time to set for the timer + * + * Return: Void + */ +void tdls_timer_restart(struct wlan_objmgr_vdev *vdev, + qdf_mc_timer_t *timer, + uint32_t expiration_time); + +/** + * wlan_hdd_tdls_timers_stop() - stop all the tdls timers running + * @tdls_vdev: TDLS vdev + * + * Return: none + */ +void tdls_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev); + +/** + * tdls_get_vdev_objects() - Get TDLS private objects + * @vdev: VDEV object manager + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_get_vdev_objects(struct wlan_objmgr_vdev *vdev, + struct tdls_vdev_priv_obj **tdls_vdev_obj, + struct tdls_soc_priv_obj **tdls_soc_obj); + +/** + * cds_set_tdls_ct_mode() - Set the tdls connection tracker mode + * @hdd_ctx: hdd context + * + * This routine is called to set the tdls connection tracker operation status + * + * Return: NONE + */ +void tdls_set_ct_mode(struct wlan_objmgr_psoc *psoc); + +/** + * tdls_set_operation_mode() - set tdls operating mode + * @tdls_set_mode: tdls mode set params + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_set_operation_mode(struct tdls_set_mode_params *tdls_set_mode); + +/** + * tdls_notify_sta_connect() - Update tdls state for every + * connect event. + * @notify: sta connect params + * + * After every connect event in the system, check whether TDLS + * can be enabled in the system. If TDLS can be enabled, update the + * TDLS state as needed. + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_notify_sta_connect(struct tdls_sta_notify_params *notify); + +/** + * tdls_notify_sta_disconnect() - Update tdls state for every + * disconnect event. + * @notify: sta disconnect params + * + * After every disconnect event in the system, check whether TDLS + * can be disabled/enabled in the system and update the + * TDLS state as needed. + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_notify_sta_disconnect(struct tdls_sta_notify_params *notify); + +/** + * tdls_notify_reset_adapter() - notify reset adapter + * @vdev: vdev object + * + * Notify TDLS about the adapter reset + * + * Return: None + */ +void tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_peers_deleted_notification() - peer delete notification + * @psoc: soc object + * @vdev_id: vdev id + * + * Legacy lim layer will delete tdls peers for roaming and heart beat failures + * and notify the component about the delete event to update the tdls. + * state. + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * tdls_notify_decrement_session() - Notify the session decrement + * @psoc: psoc object manager + * + * Policy manager notify TDLS about session decrement + * + * Return: None + */ +void tdls_notify_decrement_session(struct wlan_objmgr_psoc *psoc); + +/** + * tdls_send_update_to_fw - update tdls status info + * @tdls_vdev_obj: tdls vdev private object. + * @tdls_prohibited: indicates whether tdls is prohibited. + * @tdls_chan_swit_prohibited: indicates whether tdls channel switch + * is prohibited. + * @sta_connect_event: indicate sta connect or disconnect event + * @session_id: session id + * + * Normally an AP does not influence TDLS connection between STAs + * associated to it. But AP may set bits for TDLS Prohibited or + * TDLS Channel Switch Prohibited in Extended Capability IE in + * Assoc/Re-assoc response to STA. So after STA is connected to + * an AP, call this function to update TDLS status as per those + * bits set in Ext Cap IE in received Assoc/Re-assoc response + * from AP. + * + * Return: None. + */ +void tdls_send_update_to_fw(struct tdls_vdev_priv_obj *tdls_vdev_obj, + struct tdls_soc_priv_obj *tdls_soc_obj, + bool tdls_prohibited, + bool tdls_chan_swit_prohibited, + bool sta_connect_event, + uint8_t session_id); + +/** + * tdls_notify_increment_session() - Notify the session increment + * @psoc: psoc object manager + * + * Policy manager notify TDLS about session increment + * + * Return: None + */ +void tdls_notify_increment_session(struct wlan_objmgr_psoc *psoc); + +/** + * tdls_check_is_tdls_allowed() - check is tdls allowed or not + * @vdev: vdev object + * + * Function determines the whether TDLS allowed in the system + * + * Return: true or false + */ +bool tdls_check_is_tdls_allowed(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_get_vdev() - Get tdls specific vdev object manager + * @psoc: wlan psoc object manager + * @dbg_id: debug id + * + * If TDLS possible, return the corresponding vdev + * to enable TDLS in the system. + * + * Return: vdev manager pointer or NULL. + */ +struct wlan_objmgr_vdev *tdls_get_vdev(struct wlan_objmgr_psoc *psoc, + wlan_objmgr_ref_dbgid dbg_id); + +/** + * tdls_process_policy_mgr_notification() - process policy manager notification + * @psoc: soc object manager + * + * Return: QDF_STATUS + */ +QDF_STATUS +tdls_process_policy_mgr_notification(struct wlan_objmgr_psoc *psoc); + +/** + * tdls_process_decrement_active_session() - process policy manager decrement + * sessions. + * @psoc: soc object manager + * + * Return: QDF_STATUS + */ +QDF_STATUS +tdls_process_decrement_active_session(struct wlan_objmgr_psoc *psoc); +/** + * tdls_scan_complete_event_handler() - scan complete event handler for tdls + * @vdev: vdev object + * @event: scan event + * @arg: tdls soc object + * + * Return: None + */ +void tdls_scan_complete_event_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, + void *arg); + +/** + * tdls_scan_callback() - callback for TDLS scan operation + * @soc: tdls soc pvt object + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_scan_callback(struct tdls_soc_priv_obj *tdls_soc); + +/** + * wlan_hdd_tdls_scan_done_callback() - callback for tdls scan done event + * @tdls_soc: tdls soc object + * + * Return: Void + */ +void tdls_scan_done_callback(struct tdls_soc_priv_obj *tdls_soc); + +/** + * tdls_scan_serialization_comp_info_cb() - callback for scan start + * @vdev: VDEV on which the scan command is being processed + * @comp_info: serialize rules info + * + * Return: negative = caller should stop and return error code immediately + * 1 = caller can continue to scan + */ +void tdls_scan_serialization_comp_info_cb(struct wlan_objmgr_vdev *vdev, + union wlan_serialization_rules_info *comp_info); + +/** + * tdls_set_offchan_mode() - update tdls status info + * @psoc: soc object + * @param: channel switch params + * + * send message to WMI to set TDLS off channel in f/w + * + * Return: QDF_STATUS. + */ +QDF_STATUS tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *param); + +/** + * tdls_delete_all_peers_indication() - update tdls status info + * @psoc: soc object + * @vdev_id: vdev id + * + * Notify tdls component to cleanup all peers + * + * Return: QDF_STATUS. + */ + +QDF_STATUS tdls_delete_all_peers_indication(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * tdls_get_opclass_from_bandwidth() - Return opclass for corresponding BW and + * channel. + * @soc_obj: tdls soc object. + * @channel: Channel number. + * @bw_offset: Bandwidth offset. + * @reg_bw_offset: enum offset_t type bandwidth + * + * To return the opclas. + * + * Return: opclass + */ +uint8_t tdls_get_opclass_from_bandwidth(struct tdls_soc_priv_obj *soc_obj, + uint8_t channel, uint8_t bw_offset, + uint8_t *reg_bw_offset); +#endif diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_mgmt.c b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_mgmt.c new file mode 100644 index 0000000000000000000000000000000000000000..266f176dc97e23be033b8769358b28abc681d0c3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_mgmt.c @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_mgmt.c + * + * TDLS management frames implementation + */ + +#include "wlan_tdls_main.h" +#include "wlan_tdls_tgt_api.h" +#include +#include "wlan_mgmt_txrx_utils_api.h" +#include "wlan_tdls_peer.h" +#include "wlan_tdls_ct.h" +#include "wlan_tdls_cmds_process.h" +#include "wlan_tdls_mgmt.h" + +static +const char *const tdls_action_frames_type[] = { "TDLS Setup Request", + "TDLS Setup Response", + "TDLS Setup Confirm", + "TDLS Teardown", + "TDLS Peer Traffic Indication", + "TDLS Channel Switch Request", + "TDLS Channel Switch Response", + "TDLS Peer PSM Request", + "TDLS Peer PSM Response", + "TDLS Peer Traffic Response", + "TDLS Discovery Request"}; + +QDF_STATUS tdls_set_rssi(struct wlan_objmgr_vdev *vdev, + uint8_t *mac, int8_t rssi) +{ + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_peer *curr_peer; + + if (!vdev || !mac) { + tdls_err("null pointer"); + return QDF_STATUS_E_INVAL; + } + + tdls_debug("rssi %d, peer " QDF_MAC_ADDR_FMT, + rssi, QDF_MAC_ADDR_REF(mac)); + + tdls_vdev = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_TDLS); + + if (!tdls_vdev) { + tdls_err("null tdls vdev"); + return QDF_STATUS_E_EXISTS; + } + + curr_peer = tdls_find_peer(tdls_vdev, mac); + if (!curr_peer) { + tdls_debug("null peer"); + return QDF_STATUS_E_EXISTS; + } + + curr_peer->rssi = rssi; + + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_process_rx_mgmt() - process tdls rx mgmt frames + * @rx_mgmt_event: tdls rx mgmt event + * @tdls_vdev: tdls vdev object + * + * Return: QDF_STATUS + */ +static QDF_STATUS tdls_process_rx_mgmt( + struct tdls_rx_mgmt_event *rx_mgmt_event, + struct tdls_vdev_priv_obj *tdls_vdev) +{ + struct tdls_rx_mgmt_frame *rx_mgmt; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint8_t *mac; + enum tdls_actioncode action_frame_type; + + if (!rx_mgmt_event) + return QDF_STATUS_E_INVAL; + + tdls_soc_obj = rx_mgmt_event->tdls_soc_obj; + rx_mgmt = rx_mgmt_event->rx_mgmt; + + if (!tdls_soc_obj || !rx_mgmt) { + tdls_err("invalid psoc object or rx mgmt"); + return QDF_STATUS_E_INVAL; + } + + tdls_debug("soc:%pK, frame_len:%d, rx_freq:%d, vdev_id:%d, frm_type:%d, rx_rssi:%d, buf:%pK", + tdls_soc_obj->soc, rx_mgmt->frame_len, + rx_mgmt->rx_freq, rx_mgmt->vdev_id, rx_mgmt->frm_type, + rx_mgmt->rx_rssi, rx_mgmt->buf); + + if (rx_mgmt->buf[TDLS_PUBLIC_ACTION_FRAME_OFFSET + 1] == + TDLS_PUBLIC_ACTION_DISC_RESP) { + mac = &rx_mgmt->buf[TDLS_80211_PEER_ADDR_OFFSET]; + tdls_notice("[TDLS] TDLS Discovery Response," + QDF_MAC_ADDR_FMT " RSSI[%d] <--- OTA", + QDF_MAC_ADDR_REF(mac), rx_mgmt->rx_rssi); + tdls_recv_discovery_resp(tdls_vdev, mac); + tdls_set_rssi(tdls_vdev->vdev, mac, rx_mgmt->rx_rssi); + } + + if (rx_mgmt->buf[TDLS_PUBLIC_ACTION_FRAME_OFFSET] == + TDLS_ACTION_FRAME) { + action_frame_type = + rx_mgmt->buf[TDLS_PUBLIC_ACTION_FRAME_OFFSET + 1]; + if (action_frame_type >= TDLS_ACTION_FRAME_TYPE_MAX) { + tdls_debug("[TDLS] unknown[%d] <--- OTA", + action_frame_type); + } else { + tdls_notice("[TDLS] %s <--- OTA", + tdls_action_frames_type[action_frame_type]); + } + } + + /* tdls_soc_obj->tdls_rx_cb ==> wlan_cfg80211_tdls_rx_callback() */ + if (tdls_soc_obj && tdls_soc_obj->tdls_rx_cb) + tdls_soc_obj->tdls_rx_cb(tdls_soc_obj->tdls_rx_cb_data, + rx_mgmt); + else + tdls_debug("rx mgmt, but no valid up layer callback"); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_process_rx_frame(struct scheduler_msg *msg) +{ + struct wlan_objmgr_vdev *vdev; + struct tdls_rx_mgmt_event *tdls_rx; + struct tdls_vdev_priv_obj *tdls_vdev; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!(msg->bodyptr)) { + tdls_err("invalid message body"); + return QDF_STATUS_E_INVAL; + } + + tdls_rx = (struct tdls_rx_mgmt_event *) msg->bodyptr; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(tdls_rx->tdls_soc_obj->soc, + tdls_rx->rx_mgmt->vdev_id, WLAN_TDLS_NB_ID); + + if (vdev) { + tdls_debug("tdls rx mgmt frame received"); + tdls_vdev = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + if (tdls_vdev) + status = tdls_process_rx_mgmt(tdls_rx, tdls_vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + } + + qdf_mem_free(tdls_rx->rx_mgmt); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + + return status; +} + +QDF_STATUS tdls_mgmt_rx_ops(struct wlan_objmgr_psoc *psoc, + bool isregister) +{ + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info; + QDF_STATUS status; + int num_of_entries; + + tdls_debug("psoc:%pK, is register rx:%d", psoc, isregister); + + frm_cb_info.frm_type = MGMT_ACTION_TDLS_DISCRESP; + frm_cb_info.mgmt_rx_cb = tgt_tdls_mgmt_frame_rx_cb; + num_of_entries = 1; + + if (isregister) + status = wlan_mgmt_txrx_register_rx_cb(psoc, + WLAN_UMAC_COMP_TDLS, &frm_cb_info, + num_of_entries); + else + status = wlan_mgmt_txrx_deregister_rx_cb(psoc, + WLAN_UMAC_COMP_TDLS, &frm_cb_info, + num_of_entries); + + return status; +} + +static QDF_STATUS +tdls_internal_send_mgmt_tx_done(struct tdls_action_frame_request *req, + QDF_STATUS status) +{ + struct tdls_soc_priv_obj *tdls_soc_obj; + struct tdls_osif_indication indication; + + if (!req || !req->vdev) + return QDF_STATUS_E_NULL_VALUE; + + indication.status = status; + indication.vdev = req->vdev; + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); + if (tdls_soc_obj && tdls_soc_obj->tdls_event_cb) + tdls_soc_obj->tdls_event_cb(tdls_soc_obj->tdls_evt_cb_data, + TDLS_EVENT_MGMT_TX_ACK_CNF, &indication); + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_activate_send_mgmt_request_flush_cb( + struct scheduler_msg *msg) +{ + struct tdls_send_mgmt_request *tdls_mgmt_req; + + tdls_mgmt_req = msg->bodyptr; + + qdf_mem_free(tdls_mgmt_req); + msg->bodyptr = NULL; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_activate_send_mgmt_request( + struct tdls_action_frame_request *action_req) +{ + struct wlan_objmgr_peer *peer; + struct tdls_soc_priv_obj *tdls_soc_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0}; + struct tdls_send_mgmt_request *tdls_mgmt_req; + + if (!action_req || !action_req->vdev) + return QDF_STATUS_E_NULL_VALUE; + + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(action_req->vdev); + if (!tdls_soc_obj) { + status = QDF_STATUS_E_NULL_VALUE; + goto release_cmd; + } + + tdls_mgmt_req = qdf_mem_malloc(sizeof(struct tdls_send_mgmt_request) + + action_req->tdls_mgmt.len); + if (!tdls_mgmt_req) { + status = QDF_STATUS_E_NOMEM; + tdls_err("mem alloc failed "); + QDF_ASSERT(0); + goto release_cmd; + } + + tdls_debug("session_id %d " + "tdls_mgmt.dialog %d " + "tdls_mgmt.frame_type %d " + "tdls_mgmt.status_code %d " + "tdls_mgmt.responder %d " + "tdls_mgmt.peer_capability %d", + action_req->session_id, + action_req->tdls_mgmt.dialog, + action_req->tdls_mgmt.frame_type, + action_req->tdls_mgmt.status_code, + action_req->tdls_mgmt.responder, + action_req->tdls_mgmt.peer_capability); + + tdls_mgmt_req->session_id = action_req->session_id; + tdls_mgmt_req->req_type = action_req->tdls_mgmt.frame_type; + tdls_mgmt_req->dialog = action_req->tdls_mgmt.dialog; + tdls_mgmt_req->status_code = action_req->tdls_mgmt.status_code; + tdls_mgmt_req->responder = action_req->tdls_mgmt.responder; + tdls_mgmt_req->peer_capability = action_req->tdls_mgmt.peer_capability; + + peer = wlan_objmgr_vdev_try_get_bsspeer(action_req->vdev, + WLAN_TDLS_SB_ID); + if (!peer) { + tdls_err("bss peer is null"); + qdf_mem_free(tdls_mgmt_req); + goto release_cmd; + } + + qdf_mem_copy(tdls_mgmt_req->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_SB_ID); + + qdf_mem_copy(tdls_mgmt_req->peer_mac.bytes, + action_req->tdls_mgmt.peer_mac.bytes, QDF_MAC_ADDR_SIZE); + + if (action_req->tdls_mgmt.len) { + qdf_mem_copy(tdls_mgmt_req->add_ie, action_req->tdls_mgmt.buf, + action_req->tdls_mgmt.len); + } + + tdls_mgmt_req->length = sizeof(struct tdls_send_mgmt_request) + + action_req->tdls_mgmt.len; + if (action_req->use_default_ac) + tdls_mgmt_req->ac = WIFI_AC_VI; + else + tdls_mgmt_req->ac = WIFI_AC_BK; + + /* Send the request to PE. */ + qdf_mem_zero(&msg, sizeof(msg)); + + tdls_debug("sending TDLS Mgmt Frame req to PE "); + tdls_mgmt_req->message_type = tdls_soc_obj->tdls_send_mgmt_req; + + msg.type = tdls_soc_obj->tdls_send_mgmt_req; + msg.bodyptr = tdls_mgmt_req; + msg.flush_callback = tdls_activate_send_mgmt_request_flush_cb; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(tdls_mgmt_req); + +release_cmd: + /*update tdls nss infornation based on action code */ + tdls_reset_nss(tdls_soc_obj, action_req->chk_frame.action_code); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_internal_send_mgmt_tx_done(action_req, status); + tdls_release_serialization_command(action_req->vdev, + WLAN_SER_CMD_TDLS_SEND_MGMT); + } + + return status; +} + +static QDF_STATUS +tdls_send_mgmt_serialize_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason) +{ + struct tdls_action_frame_request *req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!cmd || !cmd->umac_cmd) { + tdls_err("invalid params cmd: %pK, ", cmd); + return QDF_STATUS_E_NULL_VALUE; + } + req = cmd->umac_cmd; + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + /* command moved to active list */ + status = tdls_activate_send_mgmt_request(req); + break; + + case WLAN_SER_CB_CANCEL_CMD: + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + /* command removed from pending list. + * notify status complete with failure + */ + status = tdls_internal_send_mgmt_tx_done(req, + QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_RELEASE_MEM_CMD: + /* command successfully completed. + * release tdls_action_frame_request memory + */ + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + break; + + default: + /* Do nothing but logging */ + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +QDF_STATUS tdls_process_mgmt_req( + struct tdls_action_frame_request *tdls_mgmt_req) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_serialization_command cmd = {0, }; + enum wlan_serialization_status ser_cmd_status; + + /* If connected and in Infra. Only then allow this */ + status = tdls_validate_mgmt_request(tdls_mgmt_req); + if (status != QDF_STATUS_SUCCESS) { + status = tdls_internal_send_mgmt_tx_done(tdls_mgmt_req, + status); + goto error_mgmt; + } + + /* update the responder, status code information + * after the cmd validation + */ + tdls_mgmt_req->tdls_mgmt.responder = + !tdls_mgmt_req->chk_frame.responder; + tdls_mgmt_req->tdls_mgmt.status_code = + tdls_mgmt_req->chk_frame.status_code; + + cmd.cmd_type = WLAN_SER_CMD_TDLS_SEND_MGMT; + /* Cmd Id not applicable for non scan cmds */ + cmd.cmd_id = 0; + cmd.cmd_cb = tdls_send_mgmt_serialize_callback; + cmd.umac_cmd = tdls_mgmt_req; + cmd.source = WLAN_UMAC_COMP_TDLS; + cmd.is_high_priority = false; + cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; + + cmd.vdev = tdls_mgmt_req->vdev; + cmd.is_blocking = true; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("wlan_serialization_request status:%d", ser_cmd_status); + + switch (ser_cmd_status) { + case WLAN_SER_CMD_PENDING: + /* command moved to pending list.Do nothing */ + break; + case WLAN_SER_CMD_ACTIVE: + /* command moved to active list. Do nothing */ + break; + case WLAN_SER_CMD_DENIED_LIST_FULL: + case WLAN_SER_CMD_DENIED_RULES_FAILED: + case WLAN_SER_CMD_DENIED_UNSPECIFIED: + status = QDF_STATUS_E_FAILURE; + goto error_mgmt; + default: + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + goto error_mgmt; + } + return status; + +error_mgmt: + wlan_objmgr_vdev_release_ref(tdls_mgmt_req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(tdls_mgmt_req); + return status; +} diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_mgmt.h b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_mgmt.h new file mode 100644 index 0000000000000000000000000000000000000000..0a41cc4d6c5c04ab0008230af810b4cb3a0ee277 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_mgmt.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_mgmt.h + * + * TDLS management frames include file + */ + +#ifndef _WLAN_TDLS_MGMT_H_ +#define _WLAN_TDLS_MGMT_H_ + +#define TDLS_PUBLIC_ACTION_FRAME_OFFSET 24 +#define TDLS_PUBLIC_ACTION_FRAME 4 +#define TDLS_PUBLIC_ACTION_DISC_RESP 14 +#define TDLS_ACTION_FRAME 12 +#define TDLS_80211_PEER_ADDR_OFFSET (TDLS_PUBLIC_ACTION_FRAME + \ + QDF_MAC_ADDR_SIZE) +#define TDLS_ACTION_FRAME_TYPE_MAX 11 + +/** + * struct tdls_rx_mgmt_event - tdls rx mgmt frame event + * @tdls_soc_obj: tdls soc private object + * @rx_mgmt: tdls rx mgmt frame structure + */ +struct tdls_rx_mgmt_event { + struct tdls_soc_priv_obj *tdls_soc_obj; + struct tdls_rx_mgmt_frame *rx_mgmt; +}; + +/** + * tdls_process_mgmt_req() - send a TDLS mgmt request to serialize module + * @tdls_mgmt_req: tdls management request + * + * TDLS request API, called from cfg80211 to send a TDLS frame in + * serialized manner to PE + * + *Return: QDF_STATUS + */ +QDF_STATUS tdls_process_mgmt_req( + struct tdls_action_frame_request *tdls_mgmt_req); + +/** + * tdls_mgmt_rx_ops() - register or unregister rx callback + * @psoc: psoc object + * @isregister: register if true, unregister if false + * + * This function registers or unregisters rx callback to mgmt txrx + * component. + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_mgmt_rx_ops(struct wlan_objmgr_psoc *psoc, + bool isregister); + +/** + * tdls_process_rx_frame() - process tdls rx frames + * @msg: scheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_rx_frame(struct scheduler_msg *msg); + +/** + * tdls_set_rssi() - Set TDLS RSSI on peer given by mac + * @vdev: vdev object + * @mac: MAC address of Peer + * @rssi: rssi value + * + * Set RSSI on TDSL peer + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_set_rssi(struct wlan_objmgr_vdev *vdev, + uint8_t *mac, int8_t rssi); +#endif + diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_peer.c b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_peer.c new file mode 100644 index 0000000000000000000000000000000000000000..d370322703231ada652f7752ea466477fd3f2894 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_peer.c @@ -0,0 +1,939 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_peer.c + * + * TDLS peer basic operations + */ +#include "wlan_tdls_main.h" +#include "wlan_tdls_peer.h" +#include +#include +#include +#include "wlan_reg_ucfg_api.h" +#include + +static uint8_t calculate_hash_key(const uint8_t *macaddr) +{ + uint8_t i, key; + + for (i = 0, key = 0; i < 6; i++) + key ^= macaddr[i]; + + return key % WLAN_TDLS_PEER_LIST_SIZE; +} + +struct tdls_peer *tdls_find_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr) +{ + uint8_t key; + QDF_STATUS status; + struct tdls_peer *peer; + qdf_list_t *head; + qdf_list_node_t *p_node; + + key = calculate_hash_key(macaddr); + head = &vdev_obj->peer_list[key]; + + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + if (WLAN_ADDR_EQ(&peer->peer_mac, macaddr) + == QDF_STATUS_SUCCESS) { + return peer; + } + status = qdf_list_peek_next(head, p_node, &p_node); + } + + tdls_debug("no tdls peer " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(macaddr)); + return NULL; +} + +/** + * tdls_find_peer_handler() - helper function for tdls_find_all_peer + * @psoc: soc object + * @obj: vdev object + * @arg: used to keep search peer parameters + * + * Return: None. + */ +static void +tdls_find_peer_handler(struct wlan_objmgr_psoc *psoc, void *obj, void *arg) +{ + struct wlan_objmgr_vdev *vdev = obj; + struct tdls_search_peer_param *tdls_param = arg; + struct tdls_vdev_priv_obj *vdev_obj; + + if (tdls_param->peer) + return; + + if (!vdev) { + tdls_err("invalid vdev"); + return; + } + + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE && + wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE) + return; + + vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + if (!vdev_obj) + return; + + tdls_param->peer = tdls_find_peer(vdev_obj, tdls_param->macaddr); +} + +struct tdls_peer * +tdls_find_all_peer(struct tdls_soc_priv_obj *soc_obj, const uint8_t *macaddr) +{ + struct tdls_search_peer_param tdls_search_param; + struct wlan_objmgr_psoc *psoc; + + if (!soc_obj) { + tdls_err("tdls soc object is NULL"); + return NULL; + } + + psoc = soc_obj->soc; + if (!psoc) { + tdls_err("psoc is NULL"); + return NULL; + } + tdls_search_param.macaddr = macaddr; + tdls_search_param.peer = NULL; + + wlan_objmgr_iterate_obj_list(psoc, WLAN_VDEV_OP, + tdls_find_peer_handler, + &tdls_search_param, 0, WLAN_TDLS_NB_ID); + + return tdls_search_param.peer; +} + +uint8_t tdls_find_opclass(struct wlan_objmgr_psoc *psoc, uint8_t channel, + uint8_t bw_offset) +{ + char country[REG_ALPHA2_LEN + 1]; + QDF_STATUS status; + + if (!psoc) { + tdls_err("psoc is NULL"); + return 0; + } + + status = wlan_reg_read_default_country(psoc, country); + if (QDF_IS_STATUS_ERROR(status)) + return 0; + + return wlan_reg_dmn_get_opclass_from_channel(country, channel, + bw_offset); +} + +/** + * tdls_add_peer() - add TDLS peer in TDLS vdev object + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of peer + * + * Allocate memory for the new peer, and add it to hash table. + * + * Return: new added TDLS peer, NULL if failed. + */ +static struct tdls_peer *tdls_add_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr) +{ + struct tdls_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + uint8_t key = 0; + qdf_list_t *head; + uint8_t reg_bw_offset; + + peer = qdf_mem_malloc(sizeof(*peer)); + if (!peer) { + tdls_err("add tdls peer malloc memory failed!"); + return NULL; + } + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL tdls soc object"); + return NULL; + } + + key = calculate_hash_key(macaddr); + head = &vdev_obj->peer_list[key]; + + qdf_mem_copy(&peer->peer_mac, macaddr, sizeof(peer->peer_mac)); + peer->vdev_priv = vdev_obj; + + peer->pref_off_chan_num = + soc_obj->tdls_configs.tdls_pre_off_chan_num; + peer->op_class_for_pref_off_chan = + tdls_get_opclass_from_bandwidth( + soc_obj, peer->pref_off_chan_num, + soc_obj->tdls_configs.tdls_pre_off_chan_bw, + ®_bw_offset); + + peer->valid_entry = false; + + qdf_list_insert_back(head, &peer->node); + + tdls_debug("add tdls peer: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(macaddr)); + return peer; +} + +struct tdls_peer *tdls_get_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr) +{ + struct tdls_peer *peer; + + peer = tdls_find_peer(vdev_obj, macaddr); + if (!peer) + peer = tdls_add_peer(vdev_obj, macaddr); + + return peer; +} + +static struct tdls_peer * +tdls_find_progress_peer_in_list(qdf_list_t *head, + const uint8_t *macaddr, uint8_t skip_self) +{ + QDF_STATUS status; + struct tdls_peer *peer; + qdf_list_node_t *p_node; + + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + if (skip_self && macaddr && + WLAN_ADDR_EQ(&peer->peer_mac, macaddr) + == QDF_STATUS_SUCCESS) { + status = qdf_list_peek_next(head, p_node, &p_node); + continue; + } else if (TDLS_LINK_CONNECTING == peer->link_status) { + tdls_debug(QDF_MAC_ADDR_FMT " TDLS_LINK_CONNECTING", + QDF_MAC_ADDR_REF(peer->peer_mac.bytes)); + return peer; + } + status = qdf_list_peek_next(head, p_node, &p_node); + } + + return NULL; +} + +/** + * tdls_find_progress_peer() - find the peer with ongoing TDLS progress + * on present vdev + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of peer, if NULL check for all the peer list + * @skip_self: If true, skip this macaddr. Otherwise, check all the peer list. + * if macaddr is NULL, this argument is ignored, and check for all + * the peer list. + * + * Return: Pointer to tdls_peer if TDLS is ongoing. Otherwise return NULL. + */ +static struct tdls_peer * +tdls_find_progress_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t skip_self) +{ + uint8_t i; + struct tdls_peer *peer; + qdf_list_t *head; + + if (!vdev_obj) { + tdls_err("invalid tdls vdev object"); + return NULL; + } + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &vdev_obj->peer_list[i]; + + peer = tdls_find_progress_peer_in_list(head, macaddr, + skip_self); + if (peer) + return peer; + } + + return NULL; +} + +/** + * tdls_find_progress_peer_handler() - helper function for tdls_is_progress + * @psoc: soc object + * @obj: vdev object + * @arg: used to keep search peer parameters + * + * Return: None. + */ +static void +tdls_find_progress_peer_handler(struct wlan_objmgr_psoc *psoc, + void *obj, void *arg) +{ + struct wlan_objmgr_vdev *vdev = obj; + struct tdls_search_progress_param *tdls_progress = arg; + struct tdls_vdev_priv_obj *vdev_obj; + + if (tdls_progress->peer) + return; + + if (!vdev) { + tdls_err("invalid vdev"); + return; + } + + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE && + wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE) + return; + + vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + + tdls_progress->peer = tdls_find_progress_peer(vdev_obj, + tdls_progress->macaddr, + tdls_progress->skip_self); +} + +struct tdls_peer *tdls_is_progress(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t skip_self) +{ + struct tdls_search_progress_param tdls_progress; + struct wlan_objmgr_psoc *psoc; + + if (!vdev_obj) { + tdls_err("invalid tdls vdev object"); + return NULL; + } + + psoc = wlan_vdev_get_psoc(vdev_obj->vdev); + if (!psoc) { + tdls_err("invalid psoc"); + return NULL; + } + tdls_progress.macaddr = macaddr; + tdls_progress.skip_self = skip_self; + tdls_progress.peer = NULL; + + wlan_objmgr_iterate_obj_list(psoc, WLAN_VDEV_OP, + tdls_find_progress_peer_handler, + &tdls_progress, 0, WLAN_TDLS_NB_ID); + + return tdls_progress.peer; +} + +struct tdls_peer * +tdls_find_first_connected_peer(struct tdls_vdev_priv_obj *vdev_obj) +{ + uint16_t i; + struct tdls_peer *peer; + qdf_list_t *head; + qdf_list_node_t *p_node; + QDF_STATUS status; + + if (!vdev_obj) { + tdls_err("invalid tdls vdev object"); + return NULL; + } + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &vdev_obj->peer_list[i]; + + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + + if (peer && TDLS_LINK_CONNECTED == peer->link_status) { + tdls_debug(QDF_MAC_ADDR_FMT + " TDLS_LINK_CONNECTED", + QDF_MAC_ADDR_REF( + peer->peer_mac.bytes)); + return peer; + } + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + + return NULL; +} + +/** + * tdls_determine_channel_opclass() - determine channel and opclass + * @soc_obj: TDLS soc object + * @vdev_obj: TDLS vdev object + * @peer: TDLS peer + * @channel: pointer to channel + * @opclass: pinter to opclass + * + * Function determines the channel and operating class + * + * Return: None. + */ +static void tdls_determine_channel_opclass(struct tdls_soc_priv_obj *soc_obj, + struct tdls_vdev_priv_obj *vdev_obj, + struct tdls_peer *peer, + uint32_t *channel, uint32_t *opclass) +{ + uint32_t vdev_id; + enum QDF_OPMODE opmode; + /* + * If tdls offchannel is not enabled then we provide base channel + * and in that case pass opclass as 0 since opclass is mainly needed + * for offchannel cases. + */ + if (!(TDLS_IS_OFF_CHANNEL_ENABLED( + soc_obj->tdls_configs.tdls_feature_flags)) || + soc_obj->tdls_fw_off_chan_mode != ENABLE_CHANSWITCH) { + vdev_id = wlan_vdev_get_id(vdev_obj->vdev); + opmode = wlan_vdev_mlme_get_opmode(vdev_obj->vdev); + + *channel = wlan_freq_to_chan( + policy_mgr_get_channel( + soc_obj->soc, + policy_mgr_convert_device_mode_to_qdf_type(opmode), + &vdev_id)); + *opclass = 0; + } else { + *channel = peer->pref_off_chan_num; + *opclass = peer->op_class_for_pref_off_chan; + } + tdls_debug("channel:%d opclass:%d", *channel, *opclass); +} + +/** + * tdls_get_wifi_hal_state() - get TDLS wifi hal state on current peer + * @peer: TDLS peer + * @state: output parameter to store the TDLS wifi hal state + * @reason: output parameter to store the reason of the current peer + * + * Return: None. + */ +static void tdls_get_wifi_hal_state(struct tdls_peer *peer, uint32_t *state, + int32_t *reason) +{ + struct wlan_objmgr_vdev *vdev; + struct tdls_soc_priv_obj *soc_obj; + + vdev = peer->vdev_priv->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("can't get tdls object"); + return; + } + + *reason = peer->reason; + + switch (peer->link_status) { + case TDLS_LINK_IDLE: + case TDLS_LINK_DISCOVERED: + case TDLS_LINK_DISCOVERING: + case TDLS_LINK_CONNECTING: + *state = QCA_WIFI_HAL_TDLS_S_ENABLED; + break; + case TDLS_LINK_CONNECTED: + if ((TDLS_IS_OFF_CHANNEL_ENABLED( + soc_obj->tdls_configs.tdls_feature_flags)) && + (soc_obj->tdls_fw_off_chan_mode == ENABLE_CHANSWITCH)) + *state = QCA_WIFI_HAL_TDLS_S_ESTABLISHED_OFF_CHANNEL; + else + *state = QCA_WIFI_HAL_TDLS_S_ENABLED; + break; + case TDLS_LINK_TEARING: + *state = QCA_WIFI_HAL_TDLS_S_DROPPED; + break; + } +} + +/** + * tdls_extract_peer_state_param() - extract peer update params from TDLS peer + * @peer_param: output peer update params + * @peer: TDLS peer + * + * This is used when enable TDLS link + * + * Return: None. + */ +void tdls_extract_peer_state_param(struct tdls_peer_update_state *peer_param, + struct tdls_peer *peer) +{ + uint16_t i, num; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj; + enum channel_state ch_state; + struct wlan_objmgr_pdev *pdev; + uint8_t chan_id; + uint32_t cur_band; + qdf_freq_t ch_freq; + + vdev_obj = peer->vdev_priv; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + pdev = wlan_vdev_get_pdev(vdev_obj->vdev); + if (!soc_obj || !pdev) { + tdls_err("soc_obj: %pK, pdev: %pK", soc_obj, pdev); + return; + } + + qdf_mem_zero(peer_param, sizeof(*peer_param)); + peer_param->vdev_id = wlan_vdev_get_id(vdev_obj->vdev); + + qdf_mem_copy(peer_param->peer_macaddr, + peer->peer_mac.bytes, QDF_MAC_ADDR_SIZE); + peer_param->peer_state = TDLS_PEER_STATE_CONNECTED; + peer_param->peer_cap.is_peer_responder = peer->is_responder; + peer_param->peer_cap.peer_uapsd_queue = peer->uapsd_queues; + peer_param->peer_cap.peer_max_sp = peer->max_sp; + peer_param->peer_cap.peer_buff_sta_support = peer->buf_sta_capable; + peer_param->peer_cap.peer_off_chan_support = + peer->off_channel_capable; + peer_param->peer_cap.peer_curr_operclass = 0; + peer_param->peer_cap.self_curr_operclass = 0; + peer_param->peer_cap.pref_off_channum = peer->pref_off_chan_num; + peer_param->peer_cap.pref_off_chan_bandwidth = + soc_obj->tdls_configs.tdls_pre_off_chan_bw; + peer_param->peer_cap.opclass_for_prefoffchan = + peer->op_class_for_pref_off_chan; + + if (QDF_STATUS_SUCCESS != ucfg_reg_get_band(pdev, &cur_band)) { + tdls_err("not able get the current frequency band"); + return; + } + + if (BIT(REG_BAND_2G) == cur_band) { + tdls_err("sending the offchannel value as 0 as only 2g is supported"); + peer_param->peer_cap.pref_off_channum = 0; + peer_param->peer_cap.opclass_for_prefoffchan = 0; + } + + ch_freq = wlan_reg_legacy_chan_to_freq(pdev, + peer_param->peer_cap.pref_off_channum); + if (wlan_reg_is_dfs_for_freq(pdev, ch_freq)) { + tdls_err("Resetting TDLS off-channel from %d to %d", + peer_param->peer_cap.pref_off_channum, + WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF); + peer_param->peer_cap.pref_off_channum = + WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF; + } + + num = 0; + for (i = 0; i < peer->supported_channels_len; i++) { + chan_id = peer->supported_channels[i]; + ch_state = wlan_reg_get_channel_state(pdev, chan_id); + + if (CHANNEL_STATE_INVALID != ch_state && + CHANNEL_STATE_DFS != ch_state && + !wlan_reg_is_dsrc_chan(pdev, chan_id)) { + peer_param->peer_cap.peer_chan[num].chan_id = chan_id; + peer_param->peer_cap.peer_chan[num].pwr = + wlan_reg_get_channel_reg_power(pdev, chan_id); + peer_param->peer_cap.peer_chan[num].dfs_set = false; + peer_param->peer_cap.peer_chanlen++; + num++; + } + } + + peer_param->peer_cap.peer_oper_classlen = + peer->supported_oper_classes_len; + for (i = 0; i < peer->supported_oper_classes_len; i++) + peer_param->peer_cap.peer_oper_class[i] = + peer->supported_oper_classes[i]; +} + +#ifdef TDLS_WOW_ENABLED +/** + * tdls_prevent_suspend(): Prevent suspend for TDLS + * + * Acquire wake lock and prevent suspend for TDLS + * + * Return None + */ +static void tdls_prevent_suspend(struct tdls_soc_priv_obj *tdls_soc) +{ + if (tdls_soc->is_prevent_suspend) + return; + + qdf_wake_lock_acquire(&tdls_soc->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_TDLS); + qdf_runtime_pm_prevent_suspend(&tdls_soc->runtime_lock); + tdls_soc->is_prevent_suspend = true; +} + +/** + * tdls_allow_suspend(): Allow suspend for TDLS + * + * Release wake lock and allow suspend for TDLS + * + * Return None + */ +static void tdls_allow_suspend(struct tdls_soc_priv_obj *tdls_soc) +{ + if (!tdls_soc->is_prevent_suspend) + return; + + qdf_wake_lock_release(&tdls_soc->wake_lock, + WIFI_POWER_EVENT_WAKELOCK_TDLS); + qdf_runtime_pm_allow_suspend(&tdls_soc->runtime_lock); + tdls_soc->is_prevent_suspend = false; +} + +/** + * tdls_update_pmo_status() - Update PMO status by TDLS status + * @tdls_vdev: TDLS vdev object + * @old_status: old link status + * @new_status: new link status + * + * Return: None. + */ +static void tdls_update_pmo_status(struct tdls_vdev_priv_obj *tdls_vdev, + enum tdls_link_state old_status, + enum tdls_link_state new_status) +{ + struct tdls_soc_priv_obj *tdls_soc; + + tdls_soc = wlan_vdev_get_tdls_soc_obj(tdls_vdev->vdev); + if (!tdls_soc) { + tdls_err("NULL psoc object"); + return; + } + + if (tdls_soc->is_drv_supported) + return; + + if ((old_status < TDLS_LINK_CONNECTING) && + (new_status == TDLS_LINK_CONNECTING)) + tdls_prevent_suspend(tdls_soc); + + if ((old_status > TDLS_LINK_IDLE) && + (new_status == TDLS_LINK_IDLE) && + (!tdls_soc->connected_peer_count) && + (!tdls_is_progress(tdls_vdev, NULL, 0))) + tdls_allow_suspend(tdls_soc); +} +#else +static void tdls_update_pmo_status(struct tdls_vdev_priv_obj *tdls_vdev, + enum tdls_link_state old_status, + enum tdls_link_state new_status) +{ +} +#endif + +/** + * tdls_set_link_status() - set link statue for TDLS peer + * @vdev_obj: TDLS vdev object + * @mac: MAC address of current TDLS peer + * @link_status: link status + * @link_reason: reason with link status + * + * Return: None. + */ +void tdls_set_link_status(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *mac, + enum tdls_link_state link_status, + enum tdls_link_state_reason link_reason) +{ + uint32_t state = 0; + int32_t res = 0; + uint32_t op_class = 0; + uint32_t channel = 0; + struct tdls_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + enum tdls_link_state old_status; + + peer = tdls_find_peer(vdev_obj, mac); + if (!peer) { + tdls_err("peer is NULL, can't set link status %d, reason %d", + link_status, link_reason); + return; + } + + old_status = peer->link_status; + peer->link_status = link_status; + tdls_update_pmo_status(vdev_obj, old_status, link_status); + + if (link_status >= TDLS_LINK_DISCOVERED) + peer->discovery_attempt = 0; + + if (peer->is_forced_peer && peer->state_change_notification) { + peer->reason = link_reason; + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL psoc object"); + return; + } + + tdls_determine_channel_opclass(soc_obj, vdev_obj, + peer, &channel, &op_class); + tdls_get_wifi_hal_state(peer, &state, &res); + peer->state_change_notification(mac, op_class, channel, + state, res, soc_obj->soc); + } +} + +void tdls_set_peer_link_status(struct tdls_peer *peer, + enum tdls_link_state link_status, + enum tdls_link_state_reason link_reason) +{ + uint32_t state = 0; + int32_t res = 0; + uint32_t op_class = 0; + uint32_t channel = 0; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + enum tdls_link_state old_status; + + tdls_debug("state %d reason %d peer:" QDF_MAC_ADDR_FMT, + link_status, link_reason, + QDF_MAC_ADDR_REF(peer->peer_mac.bytes)); + + vdev_obj = peer->vdev_priv; + old_status = peer->link_status; + peer->link_status = link_status; + tdls_update_pmo_status(vdev_obj, old_status, link_status); + + if (link_status >= TDLS_LINK_DISCOVERED) + peer->discovery_attempt = 0; + + if (peer->is_forced_peer && peer->state_change_notification) { + peer->reason = link_reason; + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL psoc object"); + return; + } + + tdls_determine_channel_opclass(soc_obj, vdev_obj, + peer, &channel, &op_class); + tdls_get_wifi_hal_state(peer, &state, &res); + peer->state_change_notification(peer->peer_mac.bytes, + op_class, channel, state, + res, soc_obj->soc); + } +} + +void tdls_set_peer_caps(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, + struct tdls_update_peer_params *req_info) +{ + uint8_t is_buffer_sta = 0; + uint8_t is_off_channel_supported = 0; + uint8_t is_qos_wmm_sta = 0; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_peer *curr_peer; + uint32_t feature; + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL psoc object"); + return; + } + + curr_peer = tdls_find_peer(vdev_obj, macaddr); + if (!curr_peer) { + tdls_err("NULL tdls peer"); + return; + } + + feature = soc_obj->tdls_configs.tdls_feature_flags; + if ((1 << 4) & req_info->extn_capability[3]) + is_buffer_sta = 1; + + if ((1 << 6) & req_info->extn_capability[3]) + is_off_channel_supported = 1; + + if (TDLS_IS_WMM_ENABLED(feature) && req_info->is_qos_wmm_sta) + is_qos_wmm_sta = 1; + + curr_peer->uapsd_queues = req_info->uapsd_queues; + curr_peer->max_sp = req_info->max_sp; + curr_peer->buf_sta_capable = is_buffer_sta; + curr_peer->off_channel_capable = is_off_channel_supported; + + qdf_mem_copy(curr_peer->supported_channels, + req_info->supported_channels, + req_info->supported_channels_len); + + curr_peer->supported_channels_len = req_info->supported_channels_len; + + qdf_mem_copy(curr_peer->supported_oper_classes, + req_info->supported_oper_classes, + req_info->supported_oper_classes_len); + + curr_peer->supported_oper_classes_len = + req_info->supported_oper_classes_len; + + curr_peer->qos = is_qos_wmm_sta; +} + +QDF_STATUS tdls_set_valid(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr) +{ + struct tdls_peer *peer; + + peer = tdls_find_peer(vdev_obj, macaddr); + if (!peer) { + tdls_err("peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + + peer->valid_entry = true; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_set_force_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, bool forcepeer) +{ + struct tdls_peer *peer; + + peer = tdls_find_peer(vdev_obj, macaddr); + if (!peer) { + tdls_err("peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + peer->is_forced_peer = forcepeer; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_set_callback(struct tdls_peer *peer, + tdls_state_change_callback callback) +{ + if (!peer) { + tdls_err("peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + peer->state_change_notification = callback; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_set_extctrl_param(struct tdls_peer *peer, uint32_t chan, + uint32_t max_latency, uint32_t op_class, + uint32_t min_bandwidth) +{ + if (!peer) { + tdls_err("peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + peer->op_class_for_pref_off_chan = (uint8_t)op_class; + peer->pref_off_chan_num = (uint8_t)chan; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_reset_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_peer *curr_peer; + struct tdls_user_config *config; + uint8_t reg_bw_offset; + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL psoc object"); + return QDF_STATUS_E_FAILURE; + } + + curr_peer = tdls_find_peer(vdev_obj, macaddr); + if (!curr_peer) { + tdls_err("NULL tdls peer"); + return QDF_STATUS_E_FAILURE; + } + + if (!curr_peer->is_forced_peer) { + config = &soc_obj->tdls_configs; + curr_peer->pref_off_chan_num = config->tdls_pre_off_chan_num; + curr_peer->op_class_for_pref_off_chan = + tdls_get_opclass_from_bandwidth( + soc_obj, curr_peer->pref_off_chan_num, + soc_obj->tdls_configs.tdls_pre_off_chan_bw, + ®_bw_offset); + } + + if (curr_peer->is_peer_idle_timer_initialised) { + tdls_debug(QDF_MAC_ADDR_FMT ": destroy idle timer ", + QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); + qdf_mc_timer_stop(&curr_peer->peer_idle_timer); + qdf_mc_timer_destroy(&curr_peer->peer_idle_timer); + curr_peer->is_peer_idle_timer_initialised = false; + } + + tdls_set_peer_link_status(curr_peer, TDLS_LINK_IDLE, + TDLS_LINK_UNSPECIFIED); + curr_peer->valid_entry = false; + + return QDF_STATUS_SUCCESS; +} + +void tdls_peer_idle_timers_destroy(struct tdls_vdev_priv_obj *vdev_obj) +{ + uint16_t i; + struct tdls_peer *peer; + qdf_list_t *head; + qdf_list_node_t *p_node; + QDF_STATUS status; + + if (!vdev_obj) { + tdls_err("NULL tdls vdev object"); + return; + } + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &vdev_obj->peer_list[i]; + + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + if (peer && peer->is_peer_idle_timer_initialised) { + tdls_debug(QDF_MAC_ADDR_FMT + ": destroy idle timer ", + QDF_MAC_ADDR_REF( + peer->peer_mac.bytes)); + qdf_mc_timer_stop(&peer->peer_idle_timer); + qdf_mc_timer_destroy(&peer->peer_idle_timer); + } + status = qdf_list_peek_next(head, p_node, &p_node); + } + } +} + +void tdls_free_peer_list(struct tdls_vdev_priv_obj *vdev_obj) +{ + uint16_t i; + struct tdls_peer *peer; + qdf_list_t *head; + qdf_list_node_t *p_node; + + if (!vdev_obj) { + tdls_err("NULL tdls vdev object"); + return; + } + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &vdev_obj->peer_list[i]; + + while (QDF_IS_STATUS_SUCCESS( + qdf_list_remove_front(head, &p_node))) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + qdf_mem_free(peer); + } + qdf_list_destroy(head); + } +} diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_peer.h b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_peer.h new file mode 100644 index 0000000000000000000000000000000000000000..aa6f339eaba5c92538a1181c32bc5c19eb35baab --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_peer.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_peer.h + * + * TDLS peer function declaration + */ + +#if !defined(_WLAN_TDLS_PEER_H_) +#define _WLAN_TDLS_PEER_H_ + +/** + * struct tdls_search_peer_param - used to search TDLS peer + * @macaddr: MAC address of peer + * @peer: pointer to the found peer + */ +struct tdls_search_peer_param { + const uint8_t *macaddr; + struct tdls_peer *peer; +}; + +/** + * struct tdls_progress_param - used to search progress TDLS peer + * @skip_self: skip self peer + * @macaddr: MAC address of peer + * @peer: pointer to the found peer + */ +struct tdls_search_progress_param { + uint8_t skip_self; + const uint8_t *macaddr; + struct tdls_peer *peer; +}; + +/** + * tdls_get_peer() - find or add an TDLS peer in TDLS vdev object + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of peer + * + * Search the TDLS peer in the hash table and create a new one if not found. + * + * Return: Pointer to tdls_peer, NULL if failed. + */ +struct tdls_peer *tdls_get_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr); + +/** + * tdls_find_peer() - find TDLS peer in TDLS vdev object + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of peer + * + * This is in scheduler thread context, no lock required. + * + * Return: If peer is found, then it returns pointer to tdls_peer; + * otherwise, it returns NULL. + */ +struct tdls_peer *tdls_find_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr); + +/** + * tdls_find_all_peer() - find peer matching the input MACaddr in soc range + * @soc_obj: TDLS soc object + * @macaddr: MAC address of TDLS peer + * + * This is in scheduler thread context, no lock required. + * + * Return: TDLS peer if a matching is detected; NULL otherwise + */ +struct tdls_peer * +tdls_find_all_peer(struct tdls_soc_priv_obj *soc_obj, const uint8_t *macaddr); + +/** + * tdls_find_all_peer() - find peer matching the input MACaddr in soc range + * @soc_obj: TDLS soc object + * @channel:channel number + * @bw_offset: offset to bandwidth + * + * This is in scheduler thread context, no lock required. + * + * Return: Operating class + */ +uint8_t tdls_find_opclass(struct wlan_objmgr_psoc *psoc, + uint8_t channel, + uint8_t bw_offset); + +/** + * tdls_find_first_connected_peer() - find the 1st connected tdls peer from vdev + * @vdev_obj: tdls vdev object + * + * This function searches for the 1st connected TDLS peer + * + * Return: The 1st connected TDLS peer if found; NULL otherwise + */ +struct tdls_peer * +tdls_find_first_connected_peer(struct tdls_vdev_priv_obj *vdev_obj); + +/** + * tdls_is_progress() - find the peer with ongoing TDLS progress on present psoc + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of the peer + * @skip_self: if 1, skip checking self. If 0, search include self + * + * This is used in scheduler thread context, no lock required. + * + * Return: TDLS peer if found; NULL otherwise + */ +struct tdls_peer *tdls_is_progress(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t skip_self); + +/** + * tdls_extract_peer_state_param() - extract peer update params from TDL peer + * @peer_param: output peer update params + * @peer: TDLS peer + * + * This is used when enable TDLS link + * + * Return: None. + */ +void tdls_extract_peer_state_param(struct tdls_peer_update_state *peer_param, + struct tdls_peer *peer); + +/** + * tdls_set_link_status() - set link statue for TDLS peer + * @peer: TDLS peer + * @link_state: link state + * @link_reason: reason with link status + * + * This is in scheduler thread context, no lock required. + * + * Return: None. + */ +void tdls_set_peer_link_status(struct tdls_peer *peer, + enum tdls_link_state link_state, + enum tdls_link_state_reason link_reason); + +/** + * tdls_set_peer_caps() - set capability for TDLS peer + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address for the TDLS peer + * @req_info: parameters to update peer capability + * + * This is in scheduler thread context, no lock required. + * + * Return: None. + */ +void tdls_set_peer_caps(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, + struct tdls_update_peer_params *req_info); + +/** + * tdls_set_valid() - set station ID on a TDLS peer + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of the TDLS peer + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_set_valid(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr); + +/** + * tdls_set_force_peer() - set/clear is_forced_peer flag on peer + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of TDLS peer + * @forcepeer: value used to set is_forced_peer flag + * + * This is used in scheduler thread context, no lock required. + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_set_force_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, bool forcepeer); + +/** + * tdls_set_callback() - set state change callback on current TDLS peer + * @peer: TDLS peer + * @callback: state change callback + * + * This is used in scheduler thread context, no lock required. + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_set_callback(struct tdls_peer *peer, + tdls_state_change_callback callback); + +/** + * tdls_set_extctrl_param() - set external control parameter on TDLS peer + * @peer: TDLS peer + * @chan: channel + * @max_latency: maximum latency + * @op_class: operation class + * @min_bandwidth: minimal bandwidth + * + * This is used in scheduler thread context, no lock required. + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_set_extctrl_param(struct tdls_peer *peer, uint32_t chan, + uint32_t max_latency, uint32_t op_class, + uint32_t min_bandwidth); + +/** + * tdls_reset_peer() - reset TDLS peer identified by MAC address + * @vdev_obj: TDLS vdev object + * @mac: MAC address of the peer + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_reset_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *mac); + +/** + * tdls_peer_idle_timers_destroy() - destroy peer idle timers + * @vdev_obj: TDLS vdev object + * + * Loop through the idle peer list and destroy their timers + * + * Return: None + */ +void tdls_peer_idle_timers_destroy(struct tdls_vdev_priv_obj *vdev_obj); + +/** + * tdls_free_peer_list() - free TDLS peer list + * @vdev_obj: TDLS vdev object + * + * Free all the tdls peers + * + * Return: None + */ +void tdls_free_peer_list(struct tdls_vdev_priv_obj *vdev_obj); +#endif diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_txrx.c b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_txrx.c new file mode 100644 index 0000000000000000000000000000000000000000..6ded57c974f8b268666fcf6abfbc9aa457afa549 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_txrx.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_txrx.c + * + * TDLS txrx function definitions + */ diff --git a/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_txrx.h b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_txrx.h new file mode 100644 index 0000000000000000000000000000000000000000..b001ffe745992a340bc03d431fbbb3dce69a546c --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/core/src/wlan_tdls_txrx.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_txrx.h + * + * TDLS txrx api declaration + */ diff --git a/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_cfg.h b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..5b9c728993d80a2cc0306c90b8ae28be561e493b --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_cfg.h @@ -0,0 +1,746 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(CONFIG_TDLS_H__) +#define CONFIG_TDLS_H__ + +#include "cfg_define.h" +#include "cfg_converged.h" +#include "qdf_types.h" + +/* + * + * gTDLSUapsdMask - ACs to setup U-APSD for TDLS Sta. + * @Min: 0 + * @Max: 0x0F + * @Default: 0x0F + * + * This ini is used to configure the ACs for which mask needs to be enabled. + * 0x1: Background 0x2: Best effort + * 0x4: Video 0x8: Voice + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_QOS_WMM_UAPSD_MASK CFG_INI_UINT( \ + "gTDLSUapsdMask", \ + 0, \ + 0x0F, \ + 0x0F, \ + CFG_VALUE_OR_DEFAULT, \ + "ACs to setup U-APSD for TDLS Sta") + +/* + * + * gEnableTDLSBufferSta - Controls the TDLS buffer. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to control the TDLS buffer. + * Buffer STA is not enabled in CLD 2.0 yet. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_BUF_STA_ENABLED CFG_INI_BOOL( \ + "gEnableTDLSBufferSta", \ + 1, \ + "Controls the TDLS buffer") + +/* + * + * gTDLSPuapsdInactivityTime - Peer UAPSD Inactivity time. + * @Min: 0 + * @Max: 10 + * @Default: 0 + * + * This ini is used to configure peer uapsd inactivity time(in ms). + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PUAPSD_INACT_TIME CFG_INI_UINT( \ + "gTDLSPuapsdInactivityTime", \ + 0, \ + 10, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Peer UAPSD Inactivity time") + +/* + * + * gTDLSPuapsdRxFrameThreshold - Peer UAPSD Rx frame threshold. + * @Min: 10 + * @Max: 20 + * @Default: 10 + * + * This ini is used to configure maximum Rx frame during SP. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_RX_FRAME_THRESHOLD CFG_INI_UINT( \ + "gTDLSPuapsdRxFrameThreshold", \ + 10, \ + 20, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Peer UAPSD Rx frame threshold") + +/* + * + * gEnableTDLSOffChannel - Enables off-channel support for TDLS link. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable off-channel support for TDLS link. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_OFF_CHANNEL_ENABLED CFG_INI_BOOL( \ + "gEnableTDLSOffChannel", \ + 0, \ + "Enables off-channel support for TDLS") + +/* + * + * gEnableTDLSSupport - Enable support for TDLS. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable TDLS support. + * + * Related: None. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_SUPPORT_ENABLE CFG_INI_BOOL( \ + "gEnableTDLSSupport", \ + 1, \ + "enable/disable TDLS support") + +/* + * + * gEnableTDLSImplicitTrigger - Enable Implicit TDLS. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable implicit TDLS. + * CLD driver initiates TDLS Discovery towards a peer whenever TDLS Setup + * criteria (throughput and RSSI thresholds) is met and then it tears down + * TDLS when teardown criteria (idle packet count and RSSI) is met. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_IMPLICIT_TRIGGER CFG_INI_BOOL( \ + "gEnableTDLSImplicitTrigger", \ + 1, \ + "enable/disable implicit TDLS") + +/* + * + * gTDLSTxStatsPeriod - TDLS TX statistics time period. + * @Min: 1000 + * @Max: 4294967295 + * @Default: 2000 + * + * This ini is used to configure the time period (in ms) to evaluate whether + * the number of Tx/Rx packets exceeds TDLSTxPacketThreshold and triggers a + * TDLS Discovery request. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_TX_STATS_PERIOD CFG_INI_UINT( \ + "gTDLSTxStatsPeriod", \ + 1000, \ + 4294967295UL, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "TDLS TX statistics time period") + +/* + * + * gTDLSTxPacketThreshold - Tx/Rx Packet threshold for initiating TDLS. + * @Min: 0 + * @Max: 4294967295 + * @Default: 40 + * + * This ini is used to configure the number of Tx/Rx packets during the + * period of gTDLSTxStatsPeriod when exceeded, a TDLS Discovery request + * is triggered. + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_TX_PACKET_THRESHOLD CFG_INI_UINT( \ + "gTDLSTxPacketThreshold", \ + 0, \ + 4294967295UL, \ + 40, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx/Rx Packet threshold for initiating TDLS") + +/* + * + * gTDLSMaxDiscoveryAttempt - Attempts for sending TDLS discovery requests. + * @Min: 1 + * @Max: 100 + * @Default: 5 + * + * This ini is used to configure the number of failures of discover request, + * when exceeded, the peer is assumed to be not TDLS capable and no further + * TDLS Discovery request is made. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_MAX_DISCOVERY_ATTEMPT CFG_INI_UINT( \ + "gTDLSMaxDiscoveryAttempt", \ + 1, \ + 100, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Attempts for sending TDLS discovery requests") + +/* + * gTDLSMaxPeerCount - Max TDLS connected peer count + * @Min: 1 + * @Max: 8 + * @Default: 8 + * + * This ini is used to configure the max connected TDLS peer count. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: External + * + * + */ +#define CFG_TDLS_MAX_PEER_COUNT CFG_INI_UINT( \ + "gTDLSMaxPeerCount", \ + 1, \ + 8, \ + 8, \ + CFG_VALUE_OR_DEFAULT, \ + "Max TDLS peer count") + +/* + * + * gTDLSIdleTimeout - Duration within which number of TX / RX frames meet the + * criteria for TDLS teardown. + * @Min: 500 + * @Max: 40000 + * @Default: 5000 + * + * This ini is used to configure the time period (in ms) to evaluate whether + * the number of Tx/Rx packets exceeds gTDLSIdlePacketThreshold and thus meets + * criteria for TDLS teardown. + * Teardown notification interval (gTDLSIdleTimeout) should be multiple of + * setup notification (gTDLSTxStatsPeriod) interval. + * e.g. + * if setup notification (gTDLSTxStatsPeriod) interval = 500, then + * teardown notification (gTDLSIdleTimeout) interval should be 1000, + * 1500, 2000, 2500... + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + */ +#define CFG_TDLS_IDLE_TIMEOUT CFG_INI_UINT( \ + "gTDLSIdleTimeout", \ + 500, \ + 40000, \ + 5000, \ + CFG_VALUE_OR_DEFAULT, \ + "this is idle time period") + +/* + * + * gTDLSIdlePacketThreshold - Number of idle packet. + * @Min: 0 + * @Max: 40000 + * @Default: 3 + * + * This ini is used to configure the number of Tx/Rx packet, below which + * within last gTDLSTxStatsPeriod period is considered as idle condition. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_IDLE_PACKET_THRESHOLD CFG_INI_UINT( \ + "gTDLSIdlePacketThreshold", \ + 0, \ + 40000, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Number of idle packet") + +/* + * + * gTDLSRSSITriggerThreshold - RSSI threshold for TDLS connection. + * @Min: -120 + * @Max: 0 + * @Default: -75 + * + * This ini is used to configure the absolute value (in dB) of the peer RSSI, + * below which a TDLS setup request is triggered. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_RSSI_TRIGGER_THRESHOLD CFG_INI_INT( \ + "gTDLSRSSITriggerThreshold", \ + -120, \ + 0, \ + -75, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI threshold for TDLS connection") + +/* + * + * gTDLSRSSITeardownThreshold - RSSI threshold for TDLS teardown. + * @Min: -120 + * @Max: 0 + * @Default: -75 + * + * This ini is used to configure the absolute value (in dB) of the peer RSSI, + * when exceed, a TDLS teardown is triggered. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_RSSI_TEARDOWN_THRESHOLD CFG_INI_INT( \ + "gTDLSRSSITeardownThreshold", \ + -120, \ + 0, \ + -75, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI threshold for TDLS teardown") + +/* + * + * gTDLSRSSIDelta - Delta value for the peer RSSI that can trigger teardown. + * @Min: -30 + * @Max: 0 + * @Default: -20 + * + * This ini is used to configure delta for peer RSSI such that if Peer RSSI + * is less than AP RSSI plus delta will trigger a teardown. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_RSSI_DELTA CFG_INI_INT( \ + "gTDLSRSSIDelta", \ + -30, \ + 0, \ + -20, \ + CFG_VALUE_OR_DEFAULT, \ + "Delta value for the peer RSSI that can trigger teardown") + +/* + * + * gTDLSPrefOffChanNum - Preferred TDLS channel number when off-channel support + * is enabled. + * @Min: 1 + * @Max: 165 + * @Default: 36 + * + * This ini is used to configure preferred TDLS channel number when off-channel + * support is enabled. + * + * Related: gEnableTDLSSupport, gEnableTDLSOffChannel. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM CFG_INI_UINT( \ + "gTDLSPrefOffChanNum", \ + 1, \ + 165, \ + 36, \ + CFG_VALUE_OR_DEFAULT, \ + "Preferred TDLS channel number") + +/* + * + * gTDLSPrefOffChanBandwidth - Preferred TDLS channel bandwidth when + * off-channel support is enabled. + * @Min: 0x01 + * @Max: 0x0F + * @Default: 0x07 + * + * This ini is used to configure preferred TDLS channel bandwidth when + * off-channel support is enabled. + * 0x1: 20 MHz 0x2: 40 MHz 0x4: 80 MHz 0x8: 160 MHz + * When more than one bits are set then firmware starts from the highest and + * selects one based on capability of peer. + * + * Related: gEnableTDLSSupport, gEnableTDLSOffChannel. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PREFERRED_OFF_CHANNEL_BW CFG_INI_UINT( \ + "gTDLSPrefOffChanBandwidth", \ + 1, \ + 15, \ + 7, \ + CFG_VALUE_OR_DEFAULT, \ + "Preferred TDLS channel bandwidth") + +/* + * + * gTDLSPuapsdPTIWindow - This ini is used to configure peer traffic indication + * window. + * @Min: 1 + * @Max: 5 + * @Default: 2 + * + * This ini is used to configure buffering time in number of beacon intervals. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PUAPSD_PEER_TRAFFIC_IND_WINDOW CFG_INI_UINT( \ + "gTDLSPuapsdPTIWindow", \ + 1, \ + 5, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "This ini is used to configure peer traffic indication") + +/* + * + * gTDLSPuapsdPTIWindow - This ini is used to configure peer traffic indication + * window. + * @Min: 1 + * @Max: 5 + * @Default: 2 + * + * This ini is used to configure buffering time in number of beacon intervals. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PUAPSD_PEER_TRAFFIC_RSP_TIMEOUT CFG_INI_UINT( \ + "gTDLSPuapsdPTRTimeout", \ + 0, \ + 10000, \ + 5000, \ + CFG_VALUE_OR_DEFAULT, \ + "Peer Traffic Response timer duration in ms") + +/* + * + * gTDLSExternalControl - Enable external TDLS control. + * @Min: 0 + * @Max: 2 + * @Default: 1 + * + * This ini is used to enable/disable external TDLS control. + * TDLS external control works with TDLS implicit trigger. TDLS external + * control allows a user to add a MAC address of potential TDLS peers so + * that the CLD driver can initiate implicit TDLS setup to only those peers + * when criteria for TDLS setup (throughput and RSSI threshold) is met. + * There are two flavors of external control supported. If control default + * is set 1 it means strict external control where only for configured + * tdls peer mac address tdls link will be established. If control default + * is set 2 liberal tdls external control is needed which means + * tdls link will be established with configured peer mac address as well + * as any other peer which supports tdls. + * + * Related: gEnableTDLSSupport, gEnableTDLSImplicitTrigger. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_EXTERNAL_CONTROL CFG_INI_UINT( \ + "gTDLSExternalControl", \ + 0, \ + 2, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable external TDLS control") + +/* + * + * gEnableTDLSWmmMode - Enables WMM support over TDLS link. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable WMM support over TDLS link. + * This is required to be set to 1 for any TDLS and uAPSD functionality. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_WMM_MODE_ENABLE CFG_INI_BOOL( \ + "gEnableTDLSWmmMode", \ + 1, \ + "Enables WMM support over TDLS link") + +/* + * + * gEnableTDLSScan - Allow scan and maintain TDLS link. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable TDLS scan. + * 0: If peer is not buffer STA capable and device is not sleep STA + * capable, then teardown TDLS link when scan is initiated. If peer + * is buffer STA and we can be sleep STA then TDLS link is maintained + * during scan. + * 1: Maintain TDLS link and allow scan even if peer is not buffer STA + * capable and device is not sleep STA capable. There will be loss of + * Rx pkts since peer would not know when device moves away from tdls + * channel. Tx on TDLS link would stop when device moves away from tdls + * channel. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_SCAN_ENABLE CFG_INI_BOOL( \ + "gEnableTDLSScan", \ + 1, \ + "Allow scan and maintain TDLS link") + +/* + * + * gTDLSPeerKickoutThreshold - TDLS peer kick out threshold to firmware. + * @Min: 10 + * @Max: 5000 + * @Default: 96 + * + * This ini is used to configure TDLS peer kick out threshold to firmware. + * Firmware will use this value to determine, when to send TDLS + * peer kick out event to host. + * E.g. + * if peer kick out threshold is 10, then firmware will wait for 10 + * consecutive packet failures and then send TDLS kick out + * notification to host driver + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PEER_KICKOUT_THRESHOLD CFG_INI_UINT( \ + "gTDLSPeerKickoutThreshold", \ + 10, \ + 5000, \ + 96, \ + CFG_VALUE_OR_DEFAULT, \ + "TDLS peer kick out threshold to firmware") +/* + * + * gTDLSDiscoveryWakeTimeout - TDLS discovery WAKE timeout in ms. + * @Min: 10 + * @Max: 5000 + * @Default: 96 + * + * DUT will wake until this timeout to receive TDLS discovery response + * from peer. If tdls_discovery_wake_timeout is 0x0, the DUT will + * choose autonomously what wake timeout value to use. + * + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: External + * + * + */ + #define CFG_TDLS_DISCOVERY_WAKE_TIMEOUT CFG_INI_UINT( \ + "gTDLSDiscoveryWakeTimeout", \ + 0, \ + 2000, \ + 200, \ + CFG_VALUE_OR_DEFAULT, \ + "TDLS peer kick out threshold to firmware") + +/* + * + * gTDLSEnableDeferTime - Timer to defer for enabling TDLS on P2P listen. + * @Min: 500 + * @Max: 6000 + * @Default: 2000 + * + * This ini is used to set the timer to defer for enabling TDLS on P2P + * listen (value in milliseconds). + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_ENABLE_DEFER_TIMER CFG_INI_UINT( \ + "gTDLSEnableDeferTime", \ + 500, \ + 6000, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "Timer to defer for enabling TDLS on P2P listen") + +#define CFG_TDLS_ALL \ + CFG(CFG_TDLS_QOS_WMM_UAPSD_MASK) \ + CFG(CFG_TDLS_BUF_STA_ENABLED) \ + CFG(CFG_TDLS_PUAPSD_INACT_TIME) \ + CFG(CFG_TDLS_RX_FRAME_THRESHOLD) \ + CFG(CFG_TDLS_OFF_CHANNEL_ENABLED) \ + CFG(CFG_TDLS_SUPPORT_ENABLE) \ + CFG(CFG_TDLS_IMPLICIT_TRIGGER) \ + CFG(CFG_TDLS_TX_STATS_PERIOD) \ + CFG(CFG_TDLS_TX_PACKET_THRESHOLD) \ + CFG(CFG_TDLS_MAX_DISCOVERY_ATTEMPT) \ + CFG(CFG_TDLS_MAX_PEER_COUNT) \ + CFG(CFG_TDLS_IDLE_TIMEOUT) \ + CFG(CFG_TDLS_IDLE_PACKET_THRESHOLD) \ + CFG(CFG_TDLS_RSSI_TRIGGER_THRESHOLD) \ + CFG(CFG_TDLS_RSSI_TEARDOWN_THRESHOLD) \ + CFG(CFG_TDLS_RSSI_DELTA) \ + CFG(CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM) \ + CFG(CFG_TDLS_PREFERRED_OFF_CHANNEL_BW) \ + CFG(CFG_TDLS_PUAPSD_PEER_TRAFFIC_IND_WINDOW) \ + CFG(CFG_TDLS_PUAPSD_PEER_TRAFFIC_RSP_TIMEOUT) \ + CFG(CFG_TDLS_EXTERNAL_CONTROL) \ + CFG(CFG_TDLS_WMM_MODE_ENABLE) \ + CFG(CFG_TDLS_SCAN_ENABLE) \ + CFG(CFG_TDLS_PEER_KICKOUT_THRESHOLD) \ + CFG(CFG_TDLS_DISCOVERY_WAKE_TIMEOUT) \ + CFG(CFG_TDLS_ENABLE_DEFER_TIMER) + +#endif diff --git a/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_cfg_api.h b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_cfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..cd85ceb4d260b1a127daba72dd6114abe7d53d35 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_cfg_api.h @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p configures interface definitions + */ + +#ifndef _WLAN_TDLS_CFG_API_H_ +#define _WLAN_TDLS_CFG_API_H_ + +#include + +struct wlan_objmgr_psoc; + +#ifdef FEATURE_WLAN_TDLS +/** + * cfg_tdls_get_support_enable() - get tdls support enable + * @psoc: pointer to psoc object + * @val: pointer to tdls support enable/disable + * + * This function gets tdls support enable + */ +QDF_STATUS +cfg_tdls_get_support_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_support_enable() - set tdls support enable + * @psoc: pointer to psoc object + * @val: set tdls support enable/disable + * + * This function sets tdls support enable + */ +QDF_STATUS +cfg_tdls_set_support_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * cfg_tdls_get_external_control() - get tdls external control + * @psoc: pointer to psoc object + * @val: pointer to tdls external control enable/disable + * + * This function gets tdls external control + */ +QDF_STATUS +cfg_tdls_get_external_control(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_get_uapsd_mask() - get tdls uapsd mask + * @psoc: pointer to psoc object + * @val: pointer to tdls uapsd mask + * + * This function gets tdls uapsd mask + */ +QDF_STATUS +cfg_tdls_get_uapsd_mask(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * cfg_tdls_get_buffer_sta_enable() - get tdls buffer sta enable + * @psoc: pointer to psoc object + * @val: pointer to tdls buffer sta enable + * + * This function gets tdls buffer sta enable + */ +QDF_STATUS +cfg_tdls_get_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_buffer_sta_enable() - set tdls buffer sta enable + * @psoc: pointer to psoc object + * @val: tdls buffer sta enable + * + * This function sets tdls buffer sta enable + */ +QDF_STATUS +cfg_tdls_set_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * cfg_tdls_get_uapsd_inactivity_time() - get tdls uapsd inactivity time + * @psoc: pointer to psoc object + * @val: pointer to tdls uapsd inactivity time + * + * This function gets tdls uapsd inactivity time + */ +QDF_STATUS +cfg_tdls_get_uapsd_inactivity_time(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * cfg_tdls_get_rx_pkt_threshold() - get tdls rx pkt threshold + * @psoc: pointer to psoc object + * @val: pointer to tdls tdls rx pkt threshold + * + * This function gets tdls rx pkt threshold + */ +QDF_STATUS +cfg_tdls_get_rx_pkt_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * cfg_tdls_get_off_channel_enable() - get tdls off channel enable + * @psoc: pointer to psoc object + * @val: pointer to tdls off channel enable + * + * This function gets tdls off channel enable + */ +QDF_STATUS +cfg_tdls_get_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_off_channel_enable() - set tdls off channel enable + * @psoc: pointer to psoc object + * @val: tdls off channel enable + * + * This function sets tdls off channel enable + */ +QDF_STATUS +cfg_tdls_set_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * cfg_tdls_get_off_channel_enable_orig() - get tdls off channel enable orig + * @psoc: pointer to psoc object + * @val: pointer to tdls off channel enable + * + * This function gets tdls off channel enable orig + */ +QDF_STATUS +cfg_tdls_get_off_channel_enable_orig(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_restore_off_channel_enable() - set tdls off channel enable to + * tdls_off_chan_enable_orig + * @psoc: pointer to psoc object + * + * Return: NULL + */ +void cfg_tdls_restore_off_channel_enable(struct wlan_objmgr_psoc *psoc); + +/** + * cfg_tdls_store_off_channel_enable() - save tdls off channel enable to + * tdls_off_chan_enable_orig + * @psoc: pointer to psoc object + * + * Return: NULL + */ +void cfg_tdls_store_off_channel_enable(struct wlan_objmgr_psoc *psoc); + +/** + * cfg_tdls_get_wmm_mode_enable() - get tdls wmm mode enable + * @psoc: pointer to psoc object + * @val: pointer to tdls wmm mode enable + * + * This function gets tdls wmm mode enable + */ +QDF_STATUS +cfg_tdls_get_wmm_mode_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_vdev_nss_2g() - set tdls vdev nss 2g + * @psoc: pointer to psoc object + * @val: tdls vdev nss 2g + * + * This function sets tdls vdev nss 2g + */ +QDF_STATUS +cfg_tdls_set_vdev_nss_2g(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * cfg_tdls_set_vdev_nss_5g() - set tdls vdev nss 5g + * @psoc: pointer to psoc object + * @val: tdls vdev nss 5g + * + * This function sets tdls vdev nss 5g + */ +QDF_STATUS +cfg_tdls_set_vdev_nss_5g(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * cfg_tdls_get_sleep_sta_enable() - get tdls sleep sta enable + * @psoc: pointer to psoc object + * @val: pointer to tdls sleep sta enable + * + * This function gets tdls sleep sta enable + */ +QDF_STATUS +cfg_tdls_get_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_sleep_sta_enable() - set tdls sleep sta enable + * @psoc: pointer to psoc object + * @val: tdls sleep sta enable + * + * This function sets tdls sleep sta enable + */ +QDF_STATUS +cfg_tdls_set_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * cfg_tdls_get_scan_enable() - get tdls scan enable + * @psoc: pointer to psoc object + * @val: pointer to tdls scan enable + * + * This function gets tdls scan enable + */ +QDF_STATUS +cfg_tdls_get_scan_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_scan_enable() - set tdls scan enable + * @psoc: pointer to psoc object + * @val: tdls scan enable + * + * This function sets tdls scan enable + */ +QDF_STATUS +cfg_tdls_set_scan_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * cfg_tdls_get_max_peer_count() - get tdls max peer count + * @psoc: pointer to psoc object + * + * This function gets tdls max peer count + */ +uint16_t cfg_tdls_get_max_peer_count(struct wlan_objmgr_psoc *psoc); +#else +static inline QDF_STATUS +cfg_tdls_get_support_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_set_support_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_get_external_control(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_get_uapsd_mask(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + *val = 0; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_get_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_set_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_get_uapsd_inactivity_time(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + *val = 0; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_get_rx_pkt_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + *val = 0; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_get_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_set_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_get_off_channel_enable_orig(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} + +static inline void +cfg_tdls_restore_off_channel_enable(struct wlan_objmgr_psoc *psoc) +{ +} + +static inline void +cfg_tdls_store_off_channel_enable(struct wlan_objmgr_psoc *psoc) +{ +} + +static inline QDF_STATUS +cfg_tdls_get_wmm_mode_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_set_vdev_nss_2g(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_set_vdev_nss_5g(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_get_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_set_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_get_scan_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + *val = false; + + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +cfg_tdls_set_scan_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + return QDF_STATUS_SUCCESS; +} + +static inline uint16_t +cfg_tdls_get_max_peer_count(struct wlan_objmgr_psoc *psoc) +{ + return 0; +} +#endif /* FEATURE_WLAN_TDLS */ +#endif /* _WLAN_TDLS_CFG_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h new file mode 100644 index 0000000000000000000000000000000000000000..b73866b415cb435386401713475ac3d830d4c19f --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h @@ -0,0 +1,1340 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_public_structs.h + * + * TDLS public structure definations + */ + +#ifndef _WLAN_TDLS_STRUCTS_H_ +#define _WLAN_TDLS_STRUCTS_H_ +#include +#include +#include +#include +#include +#ifdef FEATURE_RUNTIME_PM +#include +#endif + +#define WLAN_TDLS_STA_MAX_NUM 8 +#define WLAN_TDLS_STA_P_UAPSD_OFFCHAN_MAX_NUM 1 +#define WLAN_TDLS_PEER_LIST_SIZE 16 +#define WLAN_TDLS_CT_TABLE_SIZE 8 +#define WLAN_TDLS_PEER_SUB_LIST_SIZE 10 +#define WLAN_MAC_MAX_EXTN_CAP 8 +#define WLAN_MAC_MAX_SUPP_CHANNELS 100 +#define WLAN_MAC_WMI_MAX_SUPP_CHANNELS 128 +#define WLAN_MAX_SUPP_OPER_CLASSES 32 +#define WLAN_MAC_MAX_SUPP_RATES 32 +#define WLAN_CHANNEL_14 14 +#define ENABLE_CHANSWITCH 1 +#define DISABLE_CHANSWITCH 2 +#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN 1 +#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX 165 +#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF 36 + +#define AC_PRIORITY_NUM 4 + +/* Default tdls serialize timeout is set to 4 (peer delete) + 1 secs */ +#ifdef FEATURE_RUNTIME_PM +/* Add extra PMO_RESUME_TIMEOUT for runtime PM resume timeout */ +#define TDLS_DELETE_PEER_CMD_TIMEOUT (4000 + 1000 + PMO_RESUME_TIMEOUT) +#else +#define TDLS_DELETE_PEER_CMD_TIMEOUT (4000 + 1000) +#endif + +/** Maximum time(ms) to wait for tdls del sta to complete **/ +#define WAIT_TIME_TDLS_DEL_STA (TDLS_DELETE_PEER_CMD_TIMEOUT + 1000) + +#define TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT (4000) + +/** Maximum time(ms) to wait for tdls add sta to complete **/ +#define WAIT_TIME_TDLS_ADD_STA (TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT + 1000) + +/** Maximum time(ms) to wait for Link Establish Req to complete **/ +#define WAIT_TIME_TDLS_LINK_ESTABLISH_REQ 1500 + +/** Maximum time(ms) to wait for tdls mgmt to complete **/ +#define WAIT_TIME_FOR_TDLS_MGMT 11000 + +/** Maximum time(ms) to wait for tdls mgmt to complete **/ +#define WAIT_TIME_FOR_TDLS_USER_CMD 11000 + +/** Maximum waittime for TDLS teardown links **/ +#define WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS 10000 + +/** Maximum waittime for TDLS antenna switch **/ +#define WAIT_TIME_FOR_TDLS_ANTENNA_SWITCH 1000 + +#define TDLS_TEARDOWN_PEER_UNREACHABLE 25 +#define TDLS_TEARDOWN_PEER_UNSPEC_REASON 26 + +#define INVALID_TDLS_PEER_INDEX 0xFF + +/** + * enum tdls_add_oper - add peer type + * @TDLS_OPER_NONE: none + * @TDLS_OPER_ADD: add new peer + * @TDLS_OPER_UPDATE: used to update peer + */ +enum tdls_add_oper { + TDLS_OPER_NONE, + TDLS_OPER_ADD, + TDLS_OPER_UPDATE +}; + +/** + * enum tdls_conc_cap - tdls concurrency support + * @TDLS_SUPPORTED_ONLY_ON_STA: only support sta tdls + * @TDLS_SUPPORTED_ONLY_ON_P2P_CLIENT: only support p2p client tdls + */ +enum tdls_conc_cap { + TDLS_SUPPORTED_ONLY_ON_STA = 0, + TDLS_SUPPORTED_ONLY_ON_P2P_CLIENT, +}; + +/** + * enum tdls_peer_capab - tdls capability type + * @TDLS_CAP_NOT_SUPPORTED: tdls not supported + * @TDLS_CAP_UNKNOWN: unknown capability + * @TDLS_CAP_SUPPORTED: tdls capability supported + */ +enum tdls_peer_capab { + TDLS_CAP_NOT_SUPPORTED = -1, + TDLS_CAP_UNKNOWN = 0, + TDLS_CAP_SUPPORTED = 1, +}; + +/** + * enum tdls_peer_state - tdls peer state + * @TDLS_PEER_STATE_PEERING: tdls connection in progress + * @TDLS_PEER_STATE_CONNECTED: tdls peer is connected + * @TDLS_PEER_STATE_TEARDOWN: tdls peer is tear down + * @TDLS_PEER_ADD_MAC_ADDR: add peer mac into connection table + * @TDLS_PEER_REMOVE_MAC_ADDR: remove peer mac from connection table + */ +enum tdls_peer_state { + TDLS_PEER_STATE_PEERING, + TDLS_PEER_STATE_CONNECTED, + TDLS_PEER_STATE_TEARDOWN, + TDLS_PEER_ADD_MAC_ADDR, + TDLS_PEER_REMOVE_MAC_ADDR +}; + +/** + * enum tdls_link_state - tdls link state + * @TDLS_LINK_IDLE: tdls link idle + * @TDLS_LINK_DISCOVERING: tdls link discovering + * @TDLS_LINK_DISCOVERED: tdls link discovered + * @TDLS_LINK_CONNECTING: tdls link connecting + * @TDLS_LINK_CONNECTED: tdls link connected + * @TDLS_LINK_TEARING: tdls link tearing + */ +enum tdls_link_state { + TDLS_LINK_IDLE = 0, + TDLS_LINK_DISCOVERING, + TDLS_LINK_DISCOVERED, + TDLS_LINK_CONNECTING, + TDLS_LINK_CONNECTED, + TDLS_LINK_TEARING, +}; + +/** + * enum tdls_link_state_reason - tdls link reason + * @TDLS_LINK_SUCCESS: Success + * @TDLS_LINK_UNSPECIFIED: Unspecified reason + * @TDLS_LINK_NOT_SUPPORTED: Remote side doesn't support TDLS + * @TDLS_LINK_UNSUPPORTED_BAND: Remote side doesn't support this band + * @TDLS_LINK_NOT_BENEFICIAL: Going to AP is better than direct + * @TDLS_LINK_DROPPED_BY_REMOTE: Remote side doesn't want it anymore + */ +enum tdls_link_state_reason { + TDLS_LINK_SUCCESS, + TDLS_LINK_UNSPECIFIED = -1, + TDLS_LINK_NOT_SUPPORTED = -2, + TDLS_LINK_UNSUPPORTED_BAND = -3, + TDLS_LINK_NOT_BENEFICIAL = -4, + TDLS_LINK_DROPPED_BY_REMOTE = -5, +}; + +/** + * enum tdls_feature_mode - TDLS support mode + * @TDLS_SUPPORT_DISABLED: Disabled in ini or FW + * @TDLS_SUPPORT_SUSPENDED: TDLS supported by ini and FW, but disabled + * temporarily due to off-channel operations or due to other reasons + * @TDLS_SUPPORT_EXP_TRIG_ONLY: Explicit trigger mode + * @TDLS_SUPPORT_IMP_MODE: Implicit mode + * @TDLS_SUPPORT_EXT_CONTROL: External control mode + */ +enum tdls_feature_mode { + TDLS_SUPPORT_DISABLED = 0, + TDLS_SUPPORT_SUSPENDED, + TDLS_SUPPORT_EXP_TRIG_ONLY, + TDLS_SUPPORT_IMP_MODE, + TDLS_SUPPORT_EXT_CONTROL, +}; + +/** + * enum tdls_command_type - TDLS command type + * @TDLS_CMD_TX_ACTION: send tdls action frame + * @TDLS_CMD_ADD_STA: add tdls peer + * @TDLS_CMD_CHANGE_STA: change tdls peer + * @TDLS_CMD_ENABLE_LINK: enable tdls link + * @TDLS_CMD_DISABLE_LINK: disable tdls link + * @TDLS_CMD_CONFIG_FORCE_PEER: config external peer + * @TDLS_CMD_REMOVE_FORCE_PEER: remove external peer + * @TDLS_CMD_STATS_UPDATE: update tdls stats + * @TDLS_CMD_CONFIG_UPDATE: config tdls + * @TDLS_CMD_SCAN_DONE: scon done event + * @TDLS_CMD_SET_RESPONDER: responder event + * @TDLS_NOTIFY_STA_CONNECTION: notify sta connection + * @TDLS_NOTIFY_STA_DISCONNECTION: notify sta disconnection + * @TDLS_CMD_SET_TDLS_MODE: set the tdls mode + * @TDLS_CMD_SESSION_INCREMENT: notify session increment + * @TDLS_CMD_SESSION_DECREMENT: notify session decrement + * @TDLS_CMD_TEARDOWN_LINKS: notify teardown + * @TDLS_NOTIFY_RESET_ADAPTERS: notify adapter reset + * @TDLS_CMD_GET_ALL_PEERS: get all the tdls peers from the list + * @TDLS_CMD_ANTENNA_SWITCH: dynamic tdls antenna switch + * @TDLS_CMD_SET_OFFCHANNEL: tdls offchannel + * @TDLS_CMD_SET_OFFCHANMODE: tdls offchannel mode + * @TDLS_CMD_SET_SECOFFCHANOFFSET: tdls secondary offchannel offset + * @TDLS_DELETE_ALL_PEERS_INDICATION: tdls delete all peers indication + */ +enum tdls_command_type { + TDLS_CMD_TX_ACTION = 1, + TDLS_CMD_ADD_STA, + TDLS_CMD_CHANGE_STA, + TDLS_CMD_ENABLE_LINK, + TDLS_CMD_DISABLE_LINK, + TDLS_CMD_CONFIG_FORCE_PEER, + TDLS_CMD_REMOVE_FORCE_PEER, + TDLS_CMD_STATS_UPDATE, + TDLS_CMD_CONFIG_UPDATE, + TDLS_CMD_SCAN_DONE, + TDLS_CMD_SET_RESPONDER, + TDLS_NOTIFY_STA_CONNECTION, + TDLS_NOTIFY_STA_DISCONNECTION, + TDLS_CMD_SET_TDLS_MODE, + TDLS_CMD_SESSION_INCREMENT, + TDLS_CMD_SESSION_DECREMENT, + TDLS_CMD_TEARDOWN_LINKS, + TDLS_NOTIFY_RESET_ADAPTERS, + TDLS_CMD_GET_ALL_PEERS, + TDLS_CMD_ANTENNA_SWITCH, + TDLS_CMD_SET_OFFCHANNEL, + TDLS_CMD_SET_OFFCHANMODE, + TDLS_CMD_SET_SECOFFCHANOFFSET, + TDLS_DELETE_ALL_PEERS_INDICATION +}; + +/** + * enum tdls_event_type - TDLS event type + * @TDLS_EVENT_VDEV_STATE_CHANGE: umac connect/disconnect event + * @TDLS_EVENT_MGMT_TX_ACK_CNF: tx tdls frame ack event + * @TDLS_EVENT_RX_MGMT: rx discovery response frame + * @TDLS_EVENT_ADD_PEER: add peer or update peer + * @TDLS_EVENT_DEL_PEER: delete peer + * @TDLS_EVENT_DISCOVERY_REQ: dicovery request + * @TDLS_EVENT_TEARDOWN_REQ: teardown request + * @TDLS_EVENT_SETUP_REQ: setup request + * @TDLS_EVENT_TEARDOWN_LINKS_DONE: teardown completion event + * @TDLS_EVENT_USER_CMD: tdls user command + * @TDLS_EVENT_ANTENNA_SWITCH: antenna switch event + */ +enum tdls_event_type { + TDLS_EVENT_VDEV_STATE_CHANGE = 0, + TDLS_EVENT_MGMT_TX_ACK_CNF, + TDLS_EVENT_RX_MGMT, + TDLS_EVENT_ADD_PEER, + TDLS_EVENT_DEL_PEER, + TDLS_EVENT_DISCOVERY_REQ, + TDLS_EVENT_TEARDOWN_REQ, + TDLS_EVENT_SETUP_REQ, + TDLS_EVENT_TEARDOWN_LINKS_DONE, + TDLS_EVENT_USER_CMD, + TDLS_EVENT_ANTENNA_SWITCH, +}; + +/** + * enum tdls_state_t - tdls state + * @QCA_WIFI_HAL_TDLS_DISABLED: TDLS is not enabled, or is disabled now + * @QCA_WIFI_HAL_TDLS_ENABLED: TDLS is enabled, but not yet tried + * @QCA_WIFI_HAL_TDLS_ESTABLISHED: Direct link is established + * @QCA_WIFI_HAL_TDLS_ESTABLISHED_OFF_CHANNEL: Direct link established using MCC + * @QCA_WIFI_HAL_TDLS_DROPPED: Direct link was established, but is now dropped + * @QCA_WIFI_HAL_TDLS_FAILED: Direct link failed + */ +enum tdls_state_t { + QCA_WIFI_HAL_TDLS_S_DISABLED = 1, + QCA_WIFI_HAL_TDLS_S_ENABLED, + QCA_WIFI_HAL_TDLS_S_ESTABLISHED, + QCA_WIFI_HAL_TDLS_S_ESTABLISHED_OFF_CHANNEL, + QCA_WIFI_HAL_TDLS_S_DROPPED, + QCA_WIFI_HAL_TDLS_S_FAILED, +}; + +/** + * enum tdls_off_chan_mode - mode for WMI_TDLS_SET_OFFCHAN_MODE_CMDID + * @TDLS_ENABLE_OFFCHANNEL: enable off channel + * @TDLS_DISABLE_OFFCHANNEL: disable off channel + */ +enum tdls_off_chan_mode { + TDLS_ENABLE_OFFCHANNEL, + TDLS_DISABLE_OFFCHANNEL +}; + +/** + * enum tdls_event_msg_type - TDLS event message type + * @TDLS_SHOULD_DISCOVER: should do discover for peer (based on tx bytes per + * second > tx_discover threshold) + * @TDLS_SHOULD_TEARDOWN: recommend teardown the link for peer due to tx bytes + * per second below tx_teardown_threshold + * @TDLS_PEER_DISCONNECTED: tdls peer disconnected + * @TDLS_CONNECTION_TRACKER_NOTIFY: TDLS/BT role change notification for + * connection tracker + */ +enum tdls_event_msg_type { + TDLS_SHOULD_DISCOVER = 0, + TDLS_SHOULD_TEARDOWN, + TDLS_PEER_DISCONNECTED, + TDLS_CONNECTION_TRACKER_NOTIFY +}; + +/** + * enum tdls_event_reason - TDLS event reason + * @TDLS_TEARDOWN_TX: tdls teardown recommended due to low transmits + * @TDLS_TEARDOWN_RSSI: tdls link tear down recommended due to poor RSSI + * @TDLS_TEARDOWN_SCAN: tdls link tear down recommended due to offchannel scan + * @TDLS_TEARDOWN_PTR_TIMEOUT: tdls peer disconnected due to PTR timeout + * @TDLS_TEARDOWN_BAD_PTR: tdls peer disconnected due wrong PTR format + * @TDLS_TEARDOWN_NO_RSP: tdls peer not responding + * @TDLS_DISCONNECTED_PEER_DELETE: tdls peer disconnected due to peer deletion + * @TDLS_PEER_ENTER_BUF_STA: tdls entered buffer STA role, TDLS connection + * tracker needs to handle this + * @TDLS_PEER_EXIT_BUF_STA: tdls exited buffer STA role, TDLS connection tracker + * needs to handle this + * @TDLS_ENTER_BT_BUSY: BT entered busy mode, TDLS connection tracker needs to + * handle this + * @TDLS_EXIT_BT_BUSY: BT exited busy mode, TDLS connection tracker needs to + * handle this + * @DLS_SCAN_STARTED: TDLS module received a scan start event, TDLS connection + * tracker needs to handle this + * @TDLS_SCAN_COMPLETED: TDLS module received a scan complete event, TDLS + * connection tracker needs to handle this + */ +enum tdls_event_reason { + TDLS_TEARDOWN_TX, + TDLS_TEARDOWN_RSSI, + TDLS_TEARDOWN_SCAN, + TDLS_TEARDOWN_PTR_TIMEOUT, + TDLS_TEARDOWN_BAD_PTR, + TDLS_TEARDOWN_NO_RSP, + TDLS_DISCONNECTED_PEER_DELETE, + TDLS_PEER_ENTER_BUF_STA, + TDLS_PEER_EXIT_BUF_STA, + TDLS_ENTER_BT_BUSY, + TDLS_EXIT_BT_BUSY, + TDLS_SCAN_STARTED, + TDLS_SCAN_COMPLETED, +}; + +/** + * enum tdls_disable_sources - TDLS disable sources + * @TDLS_SET_MODE_SOURCE_USER: disable from user + * @TDLS_SET_MODE_SOURCE_SCAN: disable during scan + * @TDLS_SET_MODE_SOURCE_OFFCHANNEL: disable during offchannel + * @TDLS_SET_MODE_SOURCE_BTC: disable during bluetooth + * @TDLS_SET_MODE_SOURCE_P2P: disable during p2p + */ +enum tdls_disable_sources { + TDLS_SET_MODE_SOURCE_USER = 0, + TDLS_SET_MODE_SOURCE_SCAN, + TDLS_SET_MODE_SOURCE_OFFCHANNEL, + TDLS_SET_MODE_SOURCE_BTC, + TDLS_SET_MODE_SOURCE_P2P, +}; + +/** + * struct tdls_osif_indication - tdls indication to os if layer + * @vdev: vdev object + * @reason: used with teardown indication + * @peer_mac: MAC address of the TDLS peer + */ +struct tdls_osif_indication { + struct wlan_objmgr_vdev *vdev; + uint16_t reason; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + QDF_STATUS status; +}; + +/** + * struct tx_frame - tx frame + * @buf: frame buffer + * @buf_len: buffer length + * @tx_timer: tx send timer + */ +struct tx_frame { + uint8_t *buf; + size_t buf_len; + qdf_timer_t tx_timer; +}; + +enum tdls_configured_external_control { + TDLS_STRICT_EXTERNAL_CONTROL = 1, + TDLS_LIBERAL_EXTERNAL_CONTROL = 2, +}; + +/** + * enum tdls_feature_bit + * @TDLS_FEATURE_OFF_CHANNEL: tdls off channel + * @TDLS_FEATURE_WMM: tdls wmm + * @TDLS_FEATURE_BUFFER_STA: tdls buffer sta + * @TDLS_FEATURE_SLEEP_STA: tdls sleep sta feature + * @TDLS_FEATURE_SCAN: tdls scan + * @TDLS_FEATURE_ENABLE: tdls enabled + * @TDLS_FEAUTRE_IMPLICIT_TRIGGER: tdls implicit trigger + * @TDLS_FEATURE_EXTERNAL_CONTROL: enforce strict tdls external control + * @TDLS_FEATURE_LIBERAL_EXTERNAL_CONTROL: liberal external tdls control + */ +enum tdls_feature_bit { + TDLS_FEATURE_OFF_CHANNEL, + TDLS_FEATURE_WMM, + TDLS_FEATURE_BUFFER_STA, + TDLS_FEATURE_SLEEP_STA, + TDLS_FEATURE_SCAN, + TDLS_FEATURE_ENABLE, + TDLS_FEAUTRE_IMPLICIT_TRIGGER, + TDLS_FEATURE_EXTERNAL_CONTROL, + TDLS_FEATURE_LIBERAL_EXTERNAL_CONTROL, +}; + +#define TDLS_IS_OFF_CHANNEL_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_OFF_CHANNEL) +#define TDLS_IS_WMM_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_WMM) +#define TDLS_IS_BUFFER_STA_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_BUFFER_STA) +#define TDLS_IS_SLEEP_STA_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_SLEEP_STA) +#define TDLS_IS_SCAN_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_SCAN) +#define TDLS_IS_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_ENABLE) +#define TDLS_IS_IMPLICIT_TRIG_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEAUTRE_IMPLICIT_TRIGGER) +#define TDLS_IS_EXTERNAL_CONTROL_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_EXTERNAL_CONTROL) +#define TDLS_IS_LIBERAL_EXTERNAL_CONTROL_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_LIBERAL_EXTERNAL_CONTROL) + +/** + * struct tdls_user_config - TDLS user configuration + * @tdls_tx_states_period: tdls tx states period + * @tdls_tx_pkt_threshold: tdls tx packets threshold + * @tdls_rx_pkt_threshold: tdls rx packets threshold + * @tdls_max_discovery_attempt: tdls discovery max times + * @tdls_idle_timeout: tdls idle timeout + * @tdls_idle_pkt_threshold: tdls idle packets threshold + * @tdls_rssi_trigger_threshold: tdls rssi trigger threshold + * @tdls_rssi_teardown_threshold: tdls rssi tear down threshold + * @tdls_rssi_delta: tdls rssi delta + * @tdls_uapsd_mask: tdls uapsd mask + * @tdls_uapsd_inactivity_time: tdls uapsd inactivity time + * @tdls_uapsd_pti_window: tdls peer traffic indication window + * @tdls_uapsd_ptr_timeout: tdls peer response timeout + * @tdls_feature_flags: tdls feature flags + * @tdls_pre_off_chan_num: tdls off channel number + * @tdls_pre_off_chan_bw: tdls off channel bandwidth + * @tdls_peer_kickout_threshold: sta kickout threshold for tdls peer + * @tdls_discovery_wake_timeout: tdls discovery wake timeout + * @delayed_trig_framint: delayed trigger frame interval + * @tdls_vdev_nss_2g: tdls NSS setting for 2G band + * @tdls_vdev_nss_5g: tdls NSS setting for 5G band + * @tdls_buffer_sta_enable: tdls buffer station enable + * @tdls_off_chan_enable: tdls off channel enable + * @tdls_off_chan_enable_orig: original tdls off channel enable + * @tdls_wmm_mode_enable: tdls wmm mode enable + * @tdls_external_control: tdls external control enable + * @tdls_implicit_trigger_enable: tdls implicit trigger enable + * @tdls_scan_enable: tdls scan enable + * @tdls_sleep_sta_enable: tdls sleep sta enable + * @tdls_support_enable: tdls support enable + */ +struct tdls_user_config { + uint32_t tdls_tx_states_period; + uint32_t tdls_tx_pkt_threshold; + uint32_t tdls_rx_pkt_threshold; + uint32_t tdls_max_discovery_attempt; + uint32_t tdls_idle_timeout; + uint32_t tdls_idle_pkt_threshold; + int32_t tdls_rssi_trigger_threshold; + int32_t tdls_rssi_teardown_threshold; + uint32_t tdls_rssi_delta; + uint32_t tdls_uapsd_mask; + uint32_t tdls_uapsd_inactivity_time; + uint32_t tdls_uapsd_pti_window; + uint32_t tdls_uapsd_ptr_timeout; + uint32_t tdls_feature_flags; + uint32_t tdls_pre_off_chan_num; + uint32_t tdls_pre_off_chan_bw; + uint32_t tdls_peer_kickout_threshold; + uint32_t tdls_discovery_wake_timeout; + uint32_t delayed_trig_framint; + uint8_t tdls_vdev_nss_2g; + uint8_t tdls_vdev_nss_5g; + bool tdls_buffer_sta_enable; + bool tdls_off_chan_enable; + bool tdls_off_chan_enable_orig; + bool tdls_wmm_mode_enable; + uint8_t tdls_external_control; + bool tdls_implicit_trigger_enable; + bool tdls_scan_enable; + bool tdls_sleep_sta_enable; + bool tdls_support_enable; +}; + +/** + * struct tdls_config_params - tdls configure paramets + * @tdls: tdls support mode + * @tx_period_t: tdls tx stats period + * @tx_packet_n: tdls tx packets number threshold + * @discovery_tries_n: tdls max discovery attempt count + * @idle_timeout_t: tdls idle time timeout + * @idle_packet_n: tdls idle pkt threshold + * @rssi_trigger_threshold: tdls rssi trigger threshold, checked before setup + * @rssi_teardown_threshold: tdls rssi teardown threshold + * @rssi_delta: rssi delta + */ +struct tdls_config_params { + uint32_t tdls; + uint32_t tx_period_t; + uint32_t tx_packet_n; + uint32_t discovery_tries_n; + uint32_t idle_timeout_t; + uint32_t idle_packet_n; + int32_t rssi_trigger_threshold; + int32_t rssi_teardown_threshold; + int32_t rssi_delta; +}; + +/** + * struct tdls_tx_cnf: tdls tx ack + * @vdev_id: vdev id + * @action_cookie: frame cookie + * @buf: frame buf + * @buf_len: buffer length + * @status: tx send status + */ +struct tdls_tx_cnf { + int vdev_id; + uint64_t action_cookie; + void *buf; + size_t buf_len; + int status; +}; + +/** + * struct tdls_rx_mgmt_frame - rx mgmt frame structure + * @frame_len: frame length + * @rx_freq: rx freq + * @vdev_id: vdev id + * @frm_type: frame type + * @rx_rssi: rx rssi + * @buf: buffer address + */ +struct tdls_rx_mgmt_frame { + uint32_t frame_len; + uint32_t rx_freq; + uint32_t vdev_id; + uint32_t frm_type; + uint32_t rx_rssi; + uint8_t buf[1]; +}; + +/** + * tdls_rx_callback() - Callback for rx mgmt frame + * @user_data: user data associated to this rx mgmt frame. + * @rx_frame: RX mgmt frame + * + * This callback will be used to give rx frames to hdd. + * + * Return: None + */ +typedef void (*tdls_rx_callback)(void *user_data, + struct tdls_rx_mgmt_frame *rx_frame); + +/** + * tdls_wmm_check() - Callback for wmm info + * @psoc: psoc object + * + * This callback will be used to check wmm information + * + * Return: true or false + */ +typedef bool (*tdls_wmm_check)(uint8_t vdev_id); + + +/* This callback is used to report state change of peer to wpa_supplicant */ +typedef int (*tdls_state_change_callback)(const uint8_t *mac, + uint32_t opclass, + uint32_t channel, + uint32_t state, + int32_t reason, void *ctx); + +/* This callback is used to report events to os_if layer */ +typedef void (*tdls_evt_callback) (void *data, + enum tdls_event_type ev_type, + struct tdls_osif_indication *event); + +/* This callback is used to register TDLS peer with the datapath */ +typedef QDF_STATUS (*tdls_register_peer_callback)(void *userdata, + uint32_t vdev_id, + const uint8_t *mac, + uint8_t qos); + +/* This callback is used to deregister TDLS peer from the datapath */ +typedef QDF_STATUS +(*tdls_deregister_peer_callback)(void *userdata, + uint32_t vdev_id, + struct qdf_mac_addr *peer_mac); + +/* This callback is used to update datapath vdev flags */ +typedef QDF_STATUS +(*tdls_dp_vdev_update_flags_callback)(void *cbk_data, + uint8_t vdev_id, + uint32_t vdev_param, + bool is_link_up); + +/* This callback is to release vdev ref for tdls offchan param related msg */ +typedef void (*tdls_offchan_parms_callback)(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_vdev_init_cb() - Callback for initializing the tdls private structure + * @vdev: vdev object + * + * This callback will be used to create the vdev private object and store + * in os_priv. + * + * Return: QDF_STATUS + */ +typedef QDF_STATUS (*tdls_vdev_init_cb)(struct wlan_objmgr_vdev *vdev); +/** + * tdls_vdev_deinit_cb() - Callback for deinitializing the tdls private struct + * @vdev: vdev object + * + * This callback will be used to destroy the vdev private object. + * + * Return: None + */ +typedef void (*tdls_vdev_deinit_cb)(struct wlan_objmgr_vdev *vdev); + +/** + * struct tdls_start_params - tdls start params + * @config: tdls user config + * @tdls_send_mgmt_req: pass eWNI_SME_TDLS_SEND_MGMT_REQ value + * @tdls_add_sta_req: pass eWNI_SME_TDLS_ADD_STA_REQ value + * @tdls_del_sta_req: pass eWNI_SME_TDLS_DEL_STA_REQ value + * @tdls_update_peer_state: pass WMA_UPDATE_TDLS_PEER_STATE value + * @tdls_del_all_peers: pass eWNI_SME_DEL_ALL_TDLS_PEERS + * @tdls_update_dp_vdev_flags: pass CDP_UPDATE_TDLS_FLAGS + * @tdls_event_cb: tdls event callback + * @tdls_evt_cb_data: tdls event data + * @tdls_peer_context: userdata for register/deregister TDLS peer + * @tdls_reg_peer: register tdls peer with datapath + * @tdls_dp_vdev_update: update vdev flags in datapath + * @tdls_osif_init_cb: callback to initialize the tdls priv + * @tdls_osif_deinit_cb: callback to deinitialize the tdls priv + */ +struct tdls_start_params { + struct tdls_user_config config; + uint16_t tdls_send_mgmt_req; + uint16_t tdls_add_sta_req; + uint16_t tdls_del_sta_req; + uint16_t tdls_update_peer_state; + uint16_t tdls_del_all_peers; + uint32_t tdls_update_dp_vdev_flags; + tdls_rx_callback tdls_rx_cb; + void *tdls_rx_cb_data; + tdls_wmm_check tdls_wmm_cb; + void *tdls_wmm_cb_data; + tdls_evt_callback tdls_event_cb; + void *tdls_evt_cb_data; + void *tdls_peer_context; + tdls_register_peer_callback tdls_reg_peer; + tdls_dp_vdev_update_flags_callback tdls_dp_vdev_update; + tdls_vdev_init_cb tdls_osif_init_cb; + tdls_vdev_deinit_cb tdls_osif_deinit_cb; +}; + +/** + * struct tdls_add_peer_params - add peer request parameter + * @peer_addr: peer mac addr + * @peer_type: peer type + * @vdev_id: vdev id + */ +struct tdls_add_peer_params { + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; + uint32_t peer_type; + uint32_t vdev_id; +}; + +/** + * struct tdls_add_peer_request - peer add request + * @vdev: vdev + * @add_peer_req: add peer request parameters + */ +struct tdls_add_peer_request { + struct wlan_objmgr_vdev *vdev; + struct tdls_add_peer_params add_peer_req; +}; + +/** + * struct tdls_del_peer_params - delete peer request parameter + * @peer_addr: peer mac addr + * @peer_type: peer type + * @vdev_id: vdev id + */ +struct tdls_del_peer_params { + const uint8_t *peer_addr; + uint32_t peer_type; + uint32_t vdev_id; +}; + +/** + * struct tdls_del_peer_request - peer delete request + * @vdev: vdev + * @del_peer_req: delete peer request parameters + */ +struct tdls_del_peer_request { + struct wlan_objmgr_vdev *vdev; + struct tdls_del_peer_params del_peer_req; +}; + +/** + * struct vhgmcsinfo - VHT MCS information + * @rx_mcs_map: RX MCS map 2 bits for each stream, total 8 streams + * @rx_highest: Indicates highest long GI VHT PPDU data rate + * STA can receive. Rate expressed in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest RX data rate supported. + * @tx_mcs_map: TX MCS map 2 bits for each stream, total 8 streams + * @tx_highest: Indicates highest long GI VHT PPDU data rate + * STA can transmit. Rate expressed in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest TX data rate supported. + */ +struct vhtmcsinfo { + uint16_t rx_mcs_map; + uint16_t rx_highest; + uint16_t tx_mcs_map; + uint16_t tx_highest; +}; + +/** + * struct vhtcap - VHT capabilities + * + * This structure is the "VHT capabilities element" as + * described in 802.11ac D3.0 8.4.2.160 + * @vht_cap_info: VHT capability info + * @supp_mcs: VHT MCS supported rates + */ +struct vhtcap { + uint32_t vht_capinfo; + struct vhtmcsinfo supp_mcs; +}; + +struct tdls_update_peer_params { + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; + uint32_t peer_type; + uint32_t vdev_id; + uint16_t capability; + uint8_t extn_capability[WLAN_MAC_MAX_EXTN_CAP]; + uint8_t supported_rates_len; + uint8_t supported_rates[WLAN_MAC_MAX_SUPP_RATES]; + uint8_t htcap_present; + struct htcap_cmn_ie ht_cap; + uint8_t vhtcap_present; + struct vhtcap vht_cap; + uint8_t uapsd_queues; + uint8_t max_sp; + uint8_t supported_channels_len; + uint8_t supported_channels[WLAN_MAC_MAX_SUPP_CHANNELS]; + uint8_t supported_oper_classes_len; + uint8_t supported_oper_classes[WLAN_MAX_SUPP_OPER_CLASSES]; + bool is_qos_wmm_sta; + bool is_pmf; +}; + +struct tdls_update_peer_request { + struct wlan_objmgr_vdev *vdev; + struct tdls_update_peer_params update_peer_req; +}; + +/** + * struct tdls_oper_request - tdls operation request + * @vdev: vdev object + * @peer_addr: MAC address of the TDLS peer + */ +struct tdls_oper_request { + struct wlan_objmgr_vdev *vdev; + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; +}; + +/** + * struct tdls_oper_config_force_peer_request - tdls enable force peer request + * @vdev: vdev object + * @peer_addr: MAC address of the TDLS peer + * @chan: channel + * @max_latency: maximum latency + * @op_class: operation class + * @min_bandwidth: minimal bandwidth + * @callback: state change callback + */ +struct tdls_oper_config_force_peer_request { + struct wlan_objmgr_vdev *vdev; + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; + uint32_t chan; + uint32_t max_latency; + uint32_t op_class; + uint32_t min_bandwidth; + tdls_state_change_callback callback; +}; + +/** + * struct tdls_info - tdls info + * + * @vdev_id: vdev id + * @tdls_state: tdls state + * @notification_interval_ms: notification interval in ms + * @tx_discovery_threshold: tx discovery threshold + * @tx_teardown_threshold: tx teardown threshold + * @rssi_teardown_threshold: rx teardown threshold + * @rssi_delta: rssi delta + * @tdls_options: tdls options + * @peer_traffic_ind_window: peer traffic indication window + * @peer_traffic_response_timeout: peer traffic response timeout + * @puapsd_mask: puapsd mask + * @puapsd_inactivity_time: puapsd inactivity time + * @puapsd_rx_frame_threshold: puapsd rx frame threshold + * @teardown_notification_ms: tdls teardown notification interval + * @tdls_peer_kickout_threshold: tdls packets threshold + * for peer kickout operation + * @tdls_discovery_wake_timeout: tdls discovery wake timeout + */ +struct tdls_info { + uint32_t vdev_id; + uint32_t tdls_state; + uint32_t notification_interval_ms; + uint32_t tx_discovery_threshold; + uint32_t tx_teardown_threshold; + int32_t rssi_teardown_threshold; + int32_t rssi_delta; + uint32_t tdls_options; + uint32_t peer_traffic_ind_window; + uint32_t peer_traffic_response_timeout; + uint32_t puapsd_mask; + uint32_t puapsd_inactivity_time; + uint32_t puapsd_rx_frame_threshold; + uint32_t teardown_notification_ms; + uint32_t tdls_peer_kickout_threshold; + uint32_t tdls_discovery_wake_timeout; +}; + +/** + * struct tdls_ch_params - channel parameters + * @chan_id: ID of the channel + * @pwr: power level + * @dfs_set: is dfs supported or not + * @half_rate: is the channel operating at 10MHz + * @quarter_rate: is the channel operating at 5MHz + */ +struct tdls_ch_params { + uint8_t chan_id; + uint8_t pwr; + bool dfs_set; + bool half_rate; + bool quarter_rate; +}; + +/** + * struct tdls_peer_params - TDLS peer capablities parameters + * @is_peer_responder: is peer responder or not + * @peer_uapsd_queue: peer uapsd queue + * @peer_max_sp: peer max SP value + * @peer_buff_sta_support: peer buffer sta supported or not + * @peer_off_chan_support: peer offchannel support + * @peer_curr_operclass: peer current operating class + * @self_curr_operclass: self current operating class + * @peer_chanlen: peer channel length + * @peer_chan: peer channel list + * @peer_oper_classlen: peer operating class length + * @peer_oper_class: peer operating class + * @pref_off_channum: preferred offchannel number + * @pref_off_chan_bandwidth: peer offchannel bandwidth + * @opclass_for_prefoffchan: operating class for offchannel + * @pref_offchan_freq: preferred offchannel frequency + */ +struct tdls_peer_params { + uint8_t is_peer_responder; + uint8_t peer_uapsd_queue; + uint8_t peer_max_sp; + uint8_t peer_buff_sta_support; + uint8_t peer_off_chan_support; + uint8_t peer_curr_operclass; + uint8_t self_curr_operclass; + uint8_t peer_chanlen; + struct tdls_ch_params peer_chan[WLAN_MAC_WMI_MAX_SUPP_CHANNELS]; + uint8_t peer_oper_classlen; + uint8_t peer_oper_class[WLAN_MAX_SUPP_OPER_CLASSES]; + uint8_t pref_off_channum; + uint8_t pref_off_chan_bandwidth; + uint8_t opclass_for_prefoffchan; + uint32_t pref_offchan_freq; +}; + +/** + * struct tdls_peer_update_state - TDLS peer state parameters + * @vdev_id: vdev id + * @peer_macaddr: peer mac address + * @peer_cap: peer capabality + * @resp_reqd: response needed + */ +struct tdls_peer_update_state { + uint32_t vdev_id; + uint8_t peer_macaddr[QDF_MAC_ADDR_SIZE]; + uint32_t peer_state; + struct tdls_peer_params peer_cap; + bool resp_reqd; +}; + +/** + * struct tdls_channel_switch_params - channel switch parameter structure + * @vdev_id: vdev ID + * @peer_mac_addr: Peer mac address + * @tdls_off_ch_bw_offset: Target off-channel bandwitdh offset + * @tdls_off_ch: Target Off Channel + * @oper_class: Operating class for target channel + * @is_responder: Responder or initiator + * @tdls_off_chan_freq: Target Off Channel frequency + */ +struct tdls_channel_switch_params { + uint32_t vdev_id; + uint8_t peer_mac_addr[QDF_MAC_ADDR_SIZE]; + uint8_t tdls_off_ch_bw_offset; + uint8_t tdls_off_ch; + uint8_t tdls_sw_mode; + uint8_t oper_class; + uint8_t is_responder; + uint32_t tdls_off_chan_freq; +}; + +/** + * enum uapsd_access_cat - U-APSD Access Categories + * @UAPSD_AC_BE: best effort + * @UAPSD_AC_BK: back ground + * @UAPSD_AC_VI: video + * @UAPSD_AC_VO: voice + */ +enum uapsd_access_cat { + UAPSD_AC_BE, + UAPSD_AC_BK, + UAPSD_AC_VI, + UAPSD_AC_VO +}; + +/** + * enum tspec_dir_type - TSPEC Direction type + * @TX_DIR: uplink + * @RX_DIR: downlink + * @BI_DIR: bidirectional + */ +enum tspec_dir_type { + TX_DIR = 0, + RX_DIR = 1, + BI_DIR = 2, +}; + +/** + * struct tdls_event_info - firmware tdls event + * @vdev_id: vdev id + * @peermac: peer mac address + * @message_type: message type + * @peer_reason: reason + */ +struct tdls_event_info { + uint8_t vdev_id; + struct qdf_mac_addr peermac; + uint16_t message_type; + uint32_t peer_reason; +}; + +/** + * struct tdls_event_notify - tdls event notify + * @vdev: vdev object + * @event: tdls event + */ +struct tdls_event_notify { + struct wlan_objmgr_vdev *vdev; + struct tdls_event_info event; +}; + +/** + * struct tdls_event_notify - tdls event notify + * @peer_mac: peer's mac address + * @frame_type: Type of TDLS mgmt frame to be sent + * @dialog: dialog token used in the frame. + * @status_code: status to be incuded in the frame + * @responder: Tdls request type + * @peer_capability: peer cpabilities + * @len: length of additional Ies + * @buf: additional IEs to be included + */ +struct tdls_send_mgmt { + struct qdf_mac_addr peer_mac; + uint8_t frame_type; + uint8_t dialog; + uint16_t status_code; + uint8_t responder; + uint32_t peer_capability; + uint8_t len; + /* Variable length, do not add anything after this */ + uint8_t buf[]; +}; + +/** + * struct tdls_validate_action_req - tdls validate mgmt request + * @action_code: action code + * @peer_mac: peer mac address + * @dialog_token: dialog code + * @status_code: status code to add + * @len: len of the frame + * @responder: whether to respond or not + */ +struct tdls_validate_action_req { + uint8_t action_code; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + uint8_t dialog_token; + uint8_t status_code; + size_t len; + int responder; +}; + +/** + * struct tdls_get_all_peers - get all peers from the list + * @vdev: vdev object + * @buf: output string buffer to hold the peer info + * @buf_len: the size of output string buffer + */ +struct tdls_get_all_peers { + struct wlan_objmgr_vdev *vdev; + char *buf; + int buf_len; +}; + +/** + * struct tdls_send_action_frame_request - tdls send mgmt request + * @vdev: vdev object + * @chk_frame: This struct used to validate mgmt frame + * @session_id: session id + * @vdev_id: vdev id + * @cmd_buf: cmd buffer + * @len: length of the frame + * @use_default_ac: access category + * @tdls_mgmt: tdls management + */ +struct tdls_action_frame_request { + struct wlan_objmgr_vdev *vdev; + struct tdls_validate_action_req chk_frame; + uint8_t session_id; + uint8_t vdev_id; + const uint8_t *cmd_buf; + uint8_t len; + bool use_default_ac; + /* Variable length, do not add anything after this */ + struct tdls_send_mgmt tdls_mgmt; +}; + +/** + * struct tdls_set_responder_req - tdls set responder in peer + * @vdev: vdev object + * @peer_mac: peer mac address + * @responder: whether to respond or not + */ +struct tdls_set_responder_req { + struct wlan_objmgr_vdev *vdev; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + uint8_t responder; +}; + +/** + * struct tdls_sta_notify_params - STA connection notify info + * @vdev: vdev object + * @tdls_prohibited: peer mac addr + * @tdls_chan_swit_prohibited: peer type + * @lfr_roam: is trigger due to lfr + * @session_id: session id + */ +struct tdls_sta_notify_params { + struct wlan_objmgr_vdev *vdev; + bool tdls_prohibited; + bool tdls_chan_swit_prohibited; + bool lfr_roam; + bool user_disconnect; + uint8_t session_id; +}; + +/** + * struct tdls_delete_all_peers_params - TDLS set mode params + * @vdev: vdev object + */ +struct tdls_delete_all_peers_params { + struct wlan_objmgr_vdev *vdev; +}; + +/** + * struct tdls_set_mode_params - TDLS set mode params + * @vdev: vdev object + * @tdls_mode: tdls mode to set + * @update_last: inform to update last tdls mode + * @source: mode change requester + */ +struct tdls_set_mode_params { + struct wlan_objmgr_vdev *vdev; + enum tdls_feature_mode tdls_mode; + bool update_last; + enum tdls_disable_sources source; +}; + +/** + * struct tdls_del_all_tdls_peers - delete all tdls peers + * @msg_type: type of message + * @msg_len: length of message + * @bssid: bssid of peer device + */ +struct tdls_del_all_tdls_peers { + uint16_t msg_type; + uint16_t msg_len; + struct qdf_mac_addr bssid; +}; + +/** + * struct tdls_antenna_switch_request - TDLS antenna switch request + * @vdev: vdev object + * @mode: antenna mode, 1x1 or 2x2 + */ +struct tdls_antenna_switch_request { + struct wlan_objmgr_vdev *vdev; + uint32_t mode; +}; + +/** + * struct tdls_set_offchannel - TDLS set offchannel + * @vdev: vdev object + * @offchannel: Updated tdls offchannel value. + * @callback: callback to release vdev ref. + */ +struct tdls_set_offchannel { + struct wlan_objmgr_vdev *vdev; + uint16_t offchannel; + tdls_offchan_parms_callback callback; +}; + +/** + * struct tdls_set_offchan_mode - TDLS set offchannel mode + * @vdev: vdev object + * @offchan_mode: Updated tdls offchannel mode value. + * @callback: callback to release vdev ref. + */ +struct tdls_set_offchanmode { + struct wlan_objmgr_vdev *vdev; + uint8_t offchan_mode; + tdls_offchan_parms_callback callback; +}; + +/** + * struct tdls_set_offchan_offset - TDLS set offchannel mode + * @vdev: vdev object + * @offchan_offset: Offchan offset value. + * @callback: callback to release vdev ref. + */ +struct tdls_set_secoffchanneloffset { + struct wlan_objmgr_vdev *vdev; + int offchan_offset; + tdls_offchan_parms_callback callback; +}; + +/** + * enum legacy_result_code - defined to comply with tSirResultCodes, need refine + * when mlme converged. + * @legacy_result_success: success + * @legacy_result_max: max result value + */ +enum legacy_result_code { + legacy_result_success, + legacy_result_max = 0x7FFFFFFF +}; + +/** + * struct tdls_send_mgmt_rsp - TDLS Response struct PE --> TDLS module + * @vdev_id: vdev id + * @status_code: status code as tSirResultCodes + * @psoc: soc object + */ +struct tdls_send_mgmt_rsp { + uint8_t vdev_id; + enum legacy_result_code status_code; + struct wlan_objmgr_psoc *psoc; +}; + +/** + * struct tdls_mgmt_tx_completion_ind - TDLS TX completion PE --> TDLS module + * @vdev_id: vdev_id + * @tx_complete_status: tx complete status + * @psoc: soc object + */ +struct tdls_mgmt_tx_completion_ind { + uint8_t vdev_id; + uint32_t tx_complete_status; + struct wlan_objmgr_psoc *psoc; +}; + +/** + * struct tdls_add_sta_rsp - TDLS Response struct PE --> TDLS module + * @status_code: status code as tSirResultCodes + * @peermac: MAC address of the TDLS peer + * @session_id: session id + * @sta_type: sta type + * @tdls_oper: add peer type + * @psoc: soc object + */ +struct tdls_add_sta_rsp { + QDF_STATUS status_code; + struct qdf_mac_addr peermac; + uint8_t session_id; + uint16_t sta_type; + enum tdls_add_oper tdls_oper; + struct wlan_objmgr_psoc *psoc; +}; + +/** + * struct tdls_del_sta_rsp - TDLS Response struct PE --> TDLS module + * @session_id: session id + * @status_code: status code as tSirResultCodes + * @peermac: MAC address of the TDLS peer + * @psoc: soc object + */ +struct tdls_del_sta_rsp { + uint8_t session_id; + QDF_STATUS status_code; + struct qdf_mac_addr peermac; + struct wlan_objmgr_psoc *psoc; +}; + +/* + * struct tdls_send_mgmt_request - tdls management request + * @message_type: type of pe message + * @length: length of the frame. + * @session_id: session id + * @req_type: type of action frame + * @dialog: dialog token used in the frame. + * @status_code: status to be incuded in the frame. + * @responder: tdls request type + * @peer_capability: peer capability information + * @bssid: bssid + * @peer_mac: mac address of the peer + * @add_ie: additional ie's to be included + */ +struct tdls_send_mgmt_request { + uint16_t message_type; + uint16_t length; + uint8_t session_id; + uint8_t req_type; + uint8_t dialog; + uint16_t status_code; + uint8_t responder; + uint32_t peer_capability; + struct qdf_mac_addr bssid; + struct qdf_mac_addr peer_mac; + enum wifi_traffic_ac ac; + /* Variable length. Dont add any field after this. */ + uint8_t add_ie[1]; +}; + +/** + * struct tdls_add_sta_req - TDLS request struct TDLS module --> PE + * @message_type: eWNI_SME_TDLS_ADD_STA_REQ + * @length: message length + * @session_id: session id + * @transaction_id: transaction id for cmd + * @bssid: bssid + * @tdls_oper: add peer type + * @peermac: MAC address for TDLS peer + * @capability: mac capability as sSirMacCapabilityInfo + * @extn_capability: extent capability + * @supported_rates_length: rates length + * @supported_rates: supported rates + * @htcap_present: ht capability present + * @ht_cap: ht capability + * @vhtcap_present: vht capability present + * @vht_cap: vht capability + * @uapsd_queues: uapsd queue as sSirMacQosInfoStation + * @max_sp: maximum service period + */ +struct tdls_add_sta_req { + uint16_t message_type; + uint16_t length; + uint8_t session_id; + uint16_t transaction_id; + struct qdf_mac_addr bssid; + enum tdls_add_oper tdls_oper; + struct qdf_mac_addr peermac; + uint16_t capability; + uint8_t extn_capability[WLAN_MAC_MAX_EXTN_CAP]; + uint8_t supported_rates_length; + uint8_t supported_rates[WLAN_MAC_MAX_SUPP_RATES]; + uint8_t htcap_present; + struct htcap_cmn_ie ht_cap; + uint8_t vhtcap_present; + struct vhtcap vht_cap; + uint8_t uapsd_queues; + uint8_t max_sp; + bool is_pmf; +}; + +/** + * struct tdls_del_sta_req - TDLS Request struct TDLS module --> PE + * @message_type: message type eWNI_SME_TDLS_DEL_STA_REQ + * @length: message length + * @session_id: session id + * @transaction_id: transaction id for cmd + * @bssid: bssid + * @peermac: MAC address of the TDLS peer + */ +struct tdls_del_sta_req { + uint16_t message_type; + uint16_t length; + uint8_t session_id; + uint16_t transaction_id; + struct qdf_mac_addr bssid; + struct qdf_mac_addr peermac; +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_tgt_api.h b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_tgt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..6e07e3164a94dd40f0688eca4b4e4223e97ca7a7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_tgt_api.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_tgt_api.h + * + * TDLS south bound interface declaration + */ + +#ifndef _WLAN_TDLS_TGT_API_H_ +#define _WLAN_TDLS_TGT_API_H_ +#include +#include "../../core/src/wlan_tdls_main.h" + +/** + * tgt_tdls_set_fw_state() - invoke lmac tdls update fw + * @psoc: soc object + * @tdls_param: update tdls state parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_set_fw_state(struct wlan_objmgr_psoc *psoc, + struct tdls_info *tdls_param); + +/** + * tgt_tdls_set_peer_state() - invoke lmac tdls update peer state + * @psoc: soc object + * @peer_param: update tdls peer parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_set_peer_state(struct wlan_objmgr_psoc *psoc, + struct tdls_peer_update_state *peer_param); + +/** + * tgt_tdls_set_offchan_mode() - invoke lmac tdls set off-channel mode + * @psoc: soc object + * @param: set tdls off channel parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *param); + +/** + * tgt_tdls_send_mgmt_rsp() - process tdls mgmt response + * @pmsg: sheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_send_mgmt_rsp(struct scheduler_msg *pmsg); + +/** + * tgt_tdls_send_mgmt_tx_completion() -process tx completion message + * @pmsg: sheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_send_mgmt_tx_completion(struct scheduler_msg *pmsg); + +/** + * tgt_tdls_del_peer_rsp() - handle TDLS del peer response + * @pmsg: sheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_del_peer_rsp(struct scheduler_msg *pmsg); + +/** + * tgt_tdls_add_peer_rsp() - handle TDLS add peer response + * @pmsg: sheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_add_peer_rsp(struct scheduler_msg *pmsg); + +/** + * tgt_tdls_register_ev_handler() - invoke lmac register tdls event handler + * @psoc: soc object + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS tgt_tdls_register_ev_handler(struct wlan_objmgr_psoc *psoc); + +/** + * tgt_tdls_unregister_ev_handler() - invoke lmac unregister tdls event handler + * @psoc: soc object + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS tgt_tdls_unregister_ev_handler(struct wlan_objmgr_psoc *psoc); + +/** + * tgt_tdls_event_handler() - The callback registered to WMI for tdls events + * @psoc: psoc object + * @info: tdls event info + * + * The callback is registered by tgt as tdls rx ops handler. + * + * Return: 0 for success or err code. + */ +QDF_STATUS +tgt_tdls_event_handler(struct wlan_objmgr_psoc *psoc, + struct tdls_event_info *info); + +/** + * tgt_tdls_mgmt_frame_rx_cb() - callback for rx mgmt frame + * @psoc: soc context + * @peer: peer context + * @buf: rx buffer + * @mgmt_rx_params: mgmt rx parameters + * @frm_type: frame type + * + * This function gets called from mgmt tx/rx component when rx mgmt + * received. + * + * Return: QDF_STATUS_SUCCESS + */ +QDF_STATUS tgt_tdls_mgmt_frame_rx_cb(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type); + +/** + * tgt_tdls_peers_deleted_notification()- notification from legacy lim + * @psoc: soc object + * @session_id: session id + * + * This function called from legacy lim to notify tdls peer deletion + * + * Return: None + */ +void tgt_tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc, + uint32_t session_id); + +/** + * tgt_tdls_delete_all_peers_indication()- Indication to tdls component + * @psoc: soc object + * @session_id: session id + * + * This function called from legacy lim to tdls component to delete tdls peers. + * + * Return: None + */ +void tgt_tdls_delete_all_peers_indication(struct wlan_objmgr_psoc *psoc, + uint32_t session_id); + +#endif diff --git a/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h new file mode 100644 index 0000000000000000000000000000000000000000..ec0067c9af7eb5ecfdb988fe39511c8ac7697433 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_ucfg_api.h + * + * TDLS north bound interface declaration + */ + +#if !defined(_WLAN_TDLS_UCFG_API_H_) +#define _WLAN_TDLS_UCFG_API_H_ + +#include +#include +#include +#include +#include +#include + +#ifdef FEATURE_WLAN_TDLS + +/** + * ucfg_tdls_init() - TDLS module initialization API + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_init(void); + +/** + * ucfg_tdls_deinit() - TDLS module deinitialization API + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_deinit(void); + +/** + * ucfg_tdls_psoc_open() - TDLS module psoc open API + * @psoc: psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_tdls_psoc_close() - TDLS module psoc close API + * @psoc: psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_tdls_psoc_start() - TDLS module start + * @psoc: psoc object + * @req: tdls start paramets + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc, + struct tdls_start_params *req); + +/** + * ucfg_tdls_psoc_enable() - TDLS module enable API + * @psoc: psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_tdls_psoc_disable() - TDLS moudle disable API + * @psoc: psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_psoc_disable(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_tdls_add_peer() - handle TDLS add peer + * @vdev: vdev object + * @add_peer_req: add peer request parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_add_peer(struct wlan_objmgr_vdev *vdev, + struct tdls_add_peer_params *add_peer_req); + +/** + * ucfg_tdls_update_peer() - handle TDLS update peer + * @vdev: vdev object + * @update_peer: update TDLS request parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_update_peer(struct wlan_objmgr_vdev *vdev, + struct tdls_update_peer_params *update_peer); + +/** + * ucfg_tdls_oper() - handle TDLS oper functions + * @vdev: vdev object + * @macaddr: MAC address of TDLS peer + * @cmd: oper cmd + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_oper(struct wlan_objmgr_vdev *vdev, + const uint8_t *macaddr, enum tdls_command_type cmd); + +/** + * ucfg_tdls_get_all_peers() - get all tdls peers from the list + * @vdev: vdev object + * @buf: output buffer + * @buflen: length of written data + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev, + char *buf, int buflen); + +/** + * ucfg_tdls_send_mgmt_frame() - send TDLS mgmt frame + * @mgmt_req: pointer to TDLS action frame request struct + * + * This will TDLS action frames to peer + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_send_mgmt_frame( + struct tdls_action_frame_request *mgmt_req); + +/** + * ucfg_tdls_responder() - set responder in TDLS peer + * @msg_req: responder msg + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_responder(struct tdls_set_responder_req *msg_req); + +/** + * ucfg_tdls_teardown_links() - teardown all TDLS links + * @psoc: psoc object + * + * Return: None + */ +QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_tdls_notify_reset_adapter() - notify reset adapter + * @vdev: vdev object manager + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_tdls_notify_sta_connect() - notify sta connect + * @notify_info: sta notification info + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_notify_sta_connect( + struct tdls_sta_notify_params *notify_info); + +/** + * ucfg_tdls_notify_sta_disconnect() - notify sta disconnect + * @notify_info: sta notification info + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_notify_sta_disconnect( + struct tdls_sta_notify_params *notify_info); + +/** + * ucfg_tdls_set_operating_mode() - set operating mode + * @set_mode_params: set mode params + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_set_operating_mode( + struct tdls_set_mode_params *set_mode_params); + +/** + * ucfg_tdls_update_rx_pkt_cnt() - update rx pkt count + * @vdev: tdls vdev object + * @mac_addr: peer mac address + * @dest_mac_addr: dest mac address + * + * Return: None + */ +void ucfg_tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr, + struct qdf_mac_addr *dest_mac_addr); + +/** + * ucfg_tdls_update_tx_pkt_cnt() - update tx pkt count + * @vdev: tdls vdev object + * @mac_addr: peer mac address + * + * Return: None + */ +void ucfg_tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr); + +/** + * ucfg_tdls_antenna_switch() - tdls antenna switch + * @vdev: tdls vdev object + * @mode: antenna mode + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, + uint32_t mode); + +/** + * ucfg_set_tdls_offchannel() - Handle TDLS set offchannel + * @vdev: vdev object + * @offchannel: updated offchannel + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_set_tdls_offchannel(struct wlan_objmgr_vdev *vdev, + int offchannel); + +/** + * ucfg_set_tdls_offchan_mode() - Handle TDLS set offchannel mode + * @vdev: vdev object + * @offchanmode: updated off-channel mode + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_set_tdls_offchan_mode(struct wlan_objmgr_vdev *vdev, + int offchanmode); + +/** + * ucfg_set_tdls_secoffchanneloffset() - Handle TDLS set offchannel offset + * @vdev: vdev object + * @offchanoffset: tdls off-channel offset + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_set_tdls_secoffchanneloffset(struct wlan_objmgr_vdev *vdev, + int offchanoffset); + +/** + * ucfg_tdls_set_rssi() - API to set TDLS RSSI on peer given by mac + * @vdev: vdev object + * @mac: MAC address of Peer + * @rssi: rssi value + * + * Set RSSI on TDLS peer + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_set_rssi(struct wlan_objmgr_vdev *vdev, + uint8_t *mac, int8_t rssi); + +/** + * ucfg_tdls_notify_connect_failure() - This api is called if STA/P2P + * connection fails on one iface and to enable/disable TDLS on the other + * STA/P2P iface which is already connected. + * @psoc: psoc object + * + * Return: void + */ +void ucfg_tdls_notify_connect_failure(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_get_tdls_vdev() - Ucfg api to get tdls specific vdev object + * @psoc: wlan psoc object manager + * @dbg_id: debug id + * + * If TDLS is enabled on any vdev then return the corresponding vdev. + * + * This api increases the ref count of the returned vdev. + * Return: vdev manager pointer or NULL. + */ +struct wlan_objmgr_vdev *ucfg_get_tdls_vdev(struct wlan_objmgr_psoc *psoc, + wlan_objmgr_ref_dbgid dbg_id); + +#else + +static inline +QDF_STATUS ucfg_tdls_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_tdls_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_tdls_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_tdls_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_tdls_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void ucfg_tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr, + struct qdf_mac_addr *dest_mac_addr) +{ +} + +static inline +void ucfg_tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr) +{ +} + +static inline +QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS ucfg_tdls_set_rssi(struct wlan_objmgr_vdev *vdev, + uint8_t *mac, int8_t rssi) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void ucfg_tdls_notify_connect_failure(struct wlan_objmgr_psoc *psoc) +{ +} + +static inline +struct wlan_objmgr_vdev *ucfg_get_tdls_vdev(struct wlan_objmgr_psoc *psoc, + wlan_objmgr_ref_dbgid dbg_id) +{ + return NULL; +} + +#endif /* FEATURE_WLAN_TDLS */ +#endif diff --git a/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_cfg.c b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_cfg.c new file mode 100644 index 0000000000000000000000000000000000000000..7d3a617eee595490d87871d07d1c7ce74661bf34 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_cfg.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains TDLS configures interface definitions + */ + +#include +#include "wlan_tdls_cfg_api.h" +#include "../../core/src/wlan_tdls_main.h" + +QDF_STATUS +cfg_tdls_get_support_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_support_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_support_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_support_enable = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_external_control(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_external_control; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_uapsd_mask(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = 0; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_uapsd_mask; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_buffer_sta_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_buffer_sta_enable = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_uapsd_inactivity_time(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = 0; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_uapsd_inactivity_time; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_rx_pkt_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = 0; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_rx_pkt_threshold; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_off_chan_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_off_chan_enable = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_off_channel_enable_orig(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_off_chan_enable_orig; + + return QDF_STATUS_SUCCESS; +} + +void cfg_tdls_store_off_channel_enable(struct wlan_objmgr_psoc *psoc) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return; + } + + soc_obj->tdls_configs.tdls_off_chan_enable_orig = + soc_obj->tdls_configs.tdls_off_chan_enable; +} + +void cfg_tdls_restore_off_channel_enable(struct wlan_objmgr_psoc *psoc) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return; + } + + soc_obj->tdls_configs.tdls_off_chan_enable = + soc_obj->tdls_configs.tdls_off_chan_enable_orig; + soc_obj->tdls_configs.tdls_off_chan_enable_orig = false; +} + +QDF_STATUS +cfg_tdls_get_wmm_mode_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_wmm_mode_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_vdev_nss_2g(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_vdev_nss_2g = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_vdev_nss_5g(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_vdev_nss_5g = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_sleep_sta_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_sleep_sta_enable = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_scan_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_scan_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_scan_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_scan_enable = val; + + return QDF_STATUS_SUCCESS; +} + +uint16_t +cfg_tdls_get_max_peer_count(struct wlan_objmgr_psoc *psoc) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return 0; + } + + return soc_obj->max_num_tdls_sta; +} diff --git a/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_tgt_api.c b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_tgt_api.c new file mode 100644 index 0000000000000000000000000000000000000000..95d489afa4e66517423c214866b5a7a3b5e53641 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_tgt_api.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_tgt_api.c + * + * TDLS south bound interface definitions + */ + +#include "qdf_status.h" +#include +#include "../../core/src/wlan_tdls_main.h" +#include "../../core/src/wlan_tdls_cmds_process.h" +#include "../../core/src/wlan_tdls_mgmt.h" + +static inline struct wlan_lmac_if_tdls_tx_ops * +wlan_psoc_get_tdls_txops(struct wlan_objmgr_psoc *psoc) +{ + return &psoc->soc_cb.tx_ops.tdls_tx_ops; +} + +static inline struct wlan_lmac_if_tdls_rx_ops * +wlan_psoc_get_tdls_rxops(struct wlan_objmgr_psoc *psoc) +{ + return &psoc->soc_cb.rx_ops.tdls_rx_ops; +} + +QDF_STATUS tgt_tdls_set_fw_state(struct wlan_objmgr_psoc *psoc, + struct tdls_info *tdls_param) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops && tdls_ops->update_fw_state) + return tdls_ops->update_fw_state(psoc, tdls_param); + else + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_tdls_set_peer_state(struct wlan_objmgr_psoc *psoc, + struct tdls_peer_update_state *peer_param) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops && tdls_ops->update_peer_state) + return tdls_ops->update_peer_state(psoc, peer_param); + else + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *param) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops && tdls_ops->set_offchan_mode) + return tdls_ops->set_offchan_mode(psoc, param); + else + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_tdls_send_mgmt_tx_completion(struct scheduler_msg *pmsg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pmsg || !pmsg->bodyptr) { + tdls_err("msg: 0x%pK", pmsg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + status = tdls_send_mgmt_tx_completion(pmsg->bodyptr); + + return status; +} + +QDF_STATUS tgt_tdls_send_mgmt_rsp(struct scheduler_msg *pmsg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pmsg || !pmsg->bodyptr) { + tdls_err("msg: 0x%pK", pmsg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + status = tdls_process_send_mgmt_rsp(pmsg->bodyptr); + + return status; +} + +QDF_STATUS tgt_tdls_add_peer_rsp(struct scheduler_msg *pmsg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pmsg || !pmsg->bodyptr) { + tdls_err("msg: 0x%pK", pmsg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + status = tdls_process_add_peer_rsp(pmsg->bodyptr); + + return status; +} + +QDF_STATUS tgt_tdls_del_peer_rsp(struct scheduler_msg *pmsg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pmsg || !pmsg->bodyptr) { + tdls_err("msg: 0x%pK", pmsg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + status = tdls_process_del_peer_rsp(pmsg->bodyptr); + + return status; +} + +QDF_STATUS tgt_tdls_register_ev_handler(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops && tdls_ops->tdls_reg_ev_handler) + return tdls_ops->tdls_reg_ev_handler(psoc, NULL); + else + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_tdls_unregister_ev_handler(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops->tdls_unreg_ev_handler) + return tdls_ops->tdls_unreg_ev_handler(psoc, NULL); + else + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tgt_tdls_event_flush_cb(struct scheduler_msg *msg) +{ + struct tdls_event_notify *notify; + + notify = msg->bodyptr; + if (notify && notify->vdev) { + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(notify); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +tgt_tdls_event_handler(struct wlan_objmgr_psoc *psoc, + struct tdls_event_info *info) +{ + struct scheduler_msg msg = {0,}; + struct tdls_event_notify *notify; + uint8_t vdev_id; + QDF_STATUS status; + + if (!psoc || !info) { + tdls_err("psoc: 0x%pK, info: 0x%pK", psoc, info); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("vdev: %d, type: %d, reason: %d" QDF_MAC_ADDR_FMT, + info->vdev_id, info->message_type, info->peer_reason, + QDF_MAC_ADDR_REF(info->peermac.bytes)); + notify = qdf_mem_malloc(sizeof(*notify)); + if (!notify) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + vdev_id = info->vdev_id; + notify->vdev = + wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, WLAN_TDLS_SB_ID); + if (!notify->vdev) { + tdls_err("null vdev, vdev_id: %d, psoc: 0x%pK", vdev_id, psoc); + return QDF_STATUS_E_INVAL; + } + qdf_mem_copy(¬ify->event, info, sizeof(*info)); + + msg.bodyptr = notify; + msg.callback = tdls_process_evt; + msg.flush_callback = tgt_tdls_event_flush_cb; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't post msg to handle tdls event"); + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(notify); + } + + return status; +} + +static QDF_STATUS tgt_tdls_mgmt_frame_rx_flush_cb(struct scheduler_msg *msg) +{ + struct tdls_rx_mgmt_event *rx_mgmt_event; + + rx_mgmt_event = msg->bodyptr; + + if (rx_mgmt_event) { + if (rx_mgmt_event->rx_mgmt) + qdf_mem_free(rx_mgmt_event->rx_mgmt); + + qdf_mem_free(rx_mgmt_event); + } + msg->bodyptr = NULL; + + return QDF_STATUS_SUCCESS; +} + +static +QDF_STATUS tgt_tdls_mgmt_frame_process_rx_cb( + struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, + qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type) +{ + struct tdls_rx_mgmt_frame *rx_mgmt; + struct tdls_rx_mgmt_event *rx_mgmt_event; + struct tdls_soc_priv_obj *tdls_soc_obj; + struct scheduler_msg msg = {0}; + struct wlan_objmgr_vdev *vdev; + uint32_t vdev_id; + uint8_t *pdata; + QDF_STATUS status; + + tdls_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!tdls_soc_obj) { + tdls_err("tdls ctx is NULL, drop this frame"); + return QDF_STATUS_E_FAILURE; + } + + if (!peer) { + vdev = tdls_get_vdev(psoc, WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("current tdls vdev is null, can't get vdev id"); + return QDF_STATUS_E_FAILURE; + } + vdev_id = wlan_vdev_get_id(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + } else { + vdev = wlan_peer_get_vdev(peer); + if (!vdev) { + tdls_err("vdev is NULL in peer, drop this frame"); + return QDF_STATUS_E_FAILURE; + } + vdev_id = wlan_vdev_get_id(vdev); + } + + rx_mgmt_event = qdf_mem_malloc_atomic(sizeof(*rx_mgmt_event)); + if (!rx_mgmt_event) { + tdls_debug_rl("Failed to allocate rx mgmt event"); + return QDF_STATUS_E_NOMEM; + } + + rx_mgmt = qdf_mem_malloc_atomic(sizeof(*rx_mgmt) + + mgmt_rx_params->buf_len); + if (!rx_mgmt) { + tdls_debug_rl("Failed to allocate rx mgmt frame"); + qdf_mem_free(rx_mgmt_event); + return QDF_STATUS_E_NOMEM; + } + + pdata = (uint8_t *)qdf_nbuf_data(buf); + rx_mgmt->frame_len = mgmt_rx_params->buf_len; + rx_mgmt->rx_freq = mgmt_rx_params->chan_freq; + rx_mgmt->vdev_id = vdev_id; + rx_mgmt->frm_type = frm_type; + rx_mgmt->rx_rssi = mgmt_rx_params->rssi; + + rx_mgmt_event->rx_mgmt = rx_mgmt; + rx_mgmt_event->tdls_soc_obj = tdls_soc_obj; + qdf_mem_copy(rx_mgmt->buf, pdata, mgmt_rx_params->buf_len); + msg.type = TDLS_EVENT_RX_MGMT; + msg.bodyptr = rx_mgmt_event; + msg.callback = tdls_process_rx_frame; + msg.flush_callback = tgt_tdls_mgmt_frame_rx_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(rx_mgmt); + qdf_mem_free(rx_mgmt_event); + } + + qdf_nbuf_free(buf); + + return status; +} + +QDF_STATUS tgt_tdls_mgmt_frame_rx_cb( + struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, + qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type) +{ + QDF_STATUS status; + + tdls_debug("psoc:%pK, peer:%pK, type:%d", psoc, peer, frm_type); + + + if (!buf) { + tdls_err("rx frame buff is null buf:%pK", buf); + return QDF_STATUS_E_INVAL; + } + + if (!mgmt_rx_params || !psoc) { + tdls_err("input is NULL mgmt_rx_params:%pK psoc:%pK, peer:%pK", + mgmt_rx_params, psoc, peer); + status = QDF_STATUS_E_INVAL; + goto release_nbuf; + } + + status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_SB_ID); + if (QDF_STATUS_SUCCESS != status) + goto release_nbuf; + + status = tgt_tdls_mgmt_frame_process_rx_cb(psoc, peer, buf, + mgmt_rx_params, frm_type); + + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_SB_ID); + + if (QDF_STATUS_SUCCESS != status) +release_nbuf: + qdf_nbuf_free(buf); + return status; +} + +void tgt_tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc, + uint32_t session_id) +{ + tdls_peers_deleted_notification(psoc, session_id); +} + +void tgt_tdls_delete_all_peers_indication(struct wlan_objmgr_psoc *psoc, + uint32_t session_id) +{ + + tdls_delete_all_peers_indication(psoc, session_id); +} diff --git a/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c new file mode 100644 index 0000000000000000000000000000000000000000..116f86fcc9632fddeb4a7bb0d1c5602260ff3034 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c @@ -0,0 +1,1178 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_ucfg_api.c + * + * TDLS north bound interface definitions + */ + +#include +#include +#include "../../core/src/wlan_tdls_main.h" +#include "../../core/src/wlan_tdls_cmds_process.h" +#include "../../core/src/wlan_tdls_ct.h" +#include "../../core/src/wlan_tdls_mgmt.h" +#include +#include +#include "wlan_policy_mgr_api.h" +#include "wlan_scan_ucfg_api.h" +#include "wlan_tdls_cfg.h" +#include "cfg_ucfg_api.h" + +QDF_STATUS ucfg_tdls_init(void) +{ + QDF_STATUS status; + + tdls_notice("tdls module dispatcher init"); + status = wlan_objmgr_register_psoc_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_create_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to register psoc create handler for tdls"); + return status; + } + + status = wlan_objmgr_register_psoc_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_destroy_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to register psoc delete handler for tdls"); + goto fail_delete_psoc; + } + + status = wlan_objmgr_register_vdev_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_create_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to register vdev create handler for tdls"); + goto fail_create_vdev; + } + + status = wlan_objmgr_register_vdev_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_destroy_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to register vdev create handler for tdls"); + goto fail_delete_vdev; + } + tdls_notice("tdls module dispatcher init done"); + + return status; +fail_delete_vdev: + wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_create_notification, NULL); + +fail_create_vdev: + wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_destroy_notification, NULL); + +fail_delete_psoc: + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_create_notification, NULL); + + return status; +} + +QDF_STATUS ucfg_tdls_deinit(void) +{ + QDF_STATUS ret; + + tdls_notice("tdls module dispatcher deinit"); + ret = wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_create_notification, NULL); + if (QDF_IS_STATUS_ERROR(ret)) + tdls_err("Failed to unregister psoc create handler"); + + ret = wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_destroy_notification, NULL); + if (QDF_IS_STATUS_ERROR(ret)) + tdls_err("Failed to unregister psoc delete handler"); + + ret = wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_create_notification, NULL); + if (QDF_IS_STATUS_ERROR(ret)) + tdls_err("Failed to unregister vdev create handler"); + + ret = wlan_objmgr_unregister_vdev_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_destroy_notification, NULL); + + if (QDF_IS_STATUS_ERROR(ret)) + tdls_err("Failed to unregister vdev delete handler"); + + return ret; +} + +/** + * tdls_update_feature_flag() - update tdls feature flag + * @tdls_soc_obj: pointer to tdls psoc object + * + * This function updates tdls feature flag + */ +static void +tdls_update_feature_flag(struct tdls_soc_priv_obj *tdls_soc_obj) +{ + tdls_soc_obj->tdls_configs.tdls_feature_flags = + ((tdls_soc_obj->tdls_configs.tdls_off_chan_enable ? + 1 << TDLS_FEATURE_OFF_CHANNEL : 0) | + (tdls_soc_obj->tdls_configs.tdls_wmm_mode_enable ? + 1 << TDLS_FEATURE_WMM : 0) | + (tdls_soc_obj->tdls_configs.tdls_buffer_sta_enable ? + 1 << TDLS_FEATURE_BUFFER_STA : 0) | + (tdls_soc_obj->tdls_configs.tdls_sleep_sta_enable ? + 1 << TDLS_FEATURE_SLEEP_STA : 0) | + (tdls_soc_obj->tdls_configs.tdls_scan_enable ? + 1 << TDLS_FEATURE_SCAN : 0) | + (tdls_soc_obj->tdls_configs.tdls_support_enable ? + 1 << TDLS_FEATURE_ENABLE : 0) | + (tdls_soc_obj->tdls_configs.tdls_implicit_trigger_enable ? + 1 << TDLS_FEAUTRE_IMPLICIT_TRIGGER : 0) | + (tdls_soc_obj->tdls_configs.tdls_external_control & + TDLS_STRICT_EXTERNAL_CONTROL ? + 1 << TDLS_FEATURE_EXTERNAL_CONTROL : 0) | + (tdls_soc_obj->tdls_configs.tdls_external_control & + TDLS_LIBERAL_EXTERNAL_CONTROL ? + 1 << TDLS_FEATURE_LIBERAL_EXTERNAL_CONTROL : 0)); +} + +/** + * tdls_object_init_params() - init parameters for tdls object + * @tdls_soc_obj: pointer to tdls psoc object + * + * This function init parameters for tdls object + */ +static QDF_STATUS tdls_object_init_params( + struct tdls_soc_priv_obj *tdls_soc_obj) +{ + struct wlan_objmgr_psoc *psoc; + + if (!tdls_soc_obj) { + tdls_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + psoc = tdls_soc_obj->soc; + if (!psoc) { + tdls_err("invalid psoc object"); + return QDF_STATUS_E_INVAL; + } + + tdls_soc_obj->tdls_configs.tdls_tx_states_period = + cfg_get(psoc, CFG_TDLS_TX_STATS_PERIOD); + tdls_soc_obj->tdls_configs.tdls_tx_pkt_threshold = + cfg_get(psoc, CFG_TDLS_TX_PACKET_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_rx_pkt_threshold = + cfg_get(psoc, CFG_TDLS_RX_FRAME_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_max_discovery_attempt = + cfg_get(psoc, CFG_TDLS_MAX_DISCOVERY_ATTEMPT); + tdls_soc_obj->tdls_configs.tdls_idle_timeout = + cfg_get(psoc, CFG_TDLS_IDLE_TIMEOUT); + tdls_soc_obj->tdls_configs.tdls_idle_pkt_threshold = + cfg_get(psoc, CFG_TDLS_IDLE_PACKET_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_rssi_trigger_threshold = + cfg_get(psoc, CFG_TDLS_RSSI_TRIGGER_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_rssi_teardown_threshold = + cfg_get(psoc, CFG_TDLS_RSSI_TEARDOWN_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_rssi_delta = + cfg_get(psoc, CFG_TDLS_RSSI_DELTA); + tdls_soc_obj->tdls_configs.tdls_uapsd_mask = + cfg_get(psoc, CFG_TDLS_QOS_WMM_UAPSD_MASK); + tdls_soc_obj->tdls_configs.tdls_uapsd_inactivity_time = + cfg_get(psoc, CFG_TDLS_PUAPSD_INACT_TIME); + tdls_soc_obj->tdls_configs.tdls_uapsd_pti_window = + cfg_get(psoc, CFG_TDLS_PUAPSD_PEER_TRAFFIC_IND_WINDOW); + tdls_soc_obj->tdls_configs.tdls_uapsd_ptr_timeout = + cfg_get(psoc, CFG_TDLS_PUAPSD_PEER_TRAFFIC_RSP_TIMEOUT); + tdls_soc_obj->tdls_configs.tdls_pre_off_chan_num = + cfg_get(psoc, CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM); + tdls_soc_obj->tdls_configs.tdls_pre_off_chan_bw = + cfg_get(psoc, CFG_TDLS_PREFERRED_OFF_CHANNEL_BW); + tdls_soc_obj->tdls_configs.tdls_peer_kickout_threshold = + cfg_get(psoc, CFG_TDLS_PEER_KICKOUT_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_discovery_wake_timeout = + cfg_get(psoc, CFG_TDLS_DISCOVERY_WAKE_TIMEOUT); + tdls_soc_obj->tdls_configs.delayed_trig_framint = + cfg_get(psoc, CFG_TL_DELAYED_TRGR_FRM_INTERVAL); + tdls_soc_obj->tdls_configs.tdls_wmm_mode_enable = + cfg_get(psoc, CFG_TDLS_WMM_MODE_ENABLE); + tdls_soc_obj->tdls_configs.tdls_off_chan_enable = + cfg_get(psoc, CFG_TDLS_OFF_CHANNEL_ENABLED); + tdls_soc_obj->tdls_configs.tdls_buffer_sta_enable = + cfg_get(psoc, CFG_TDLS_BUF_STA_ENABLED); + tdls_soc_obj->tdls_configs.tdls_scan_enable = + cfg_get(psoc, CFG_TDLS_SCAN_ENABLE); + tdls_soc_obj->tdls_configs.tdls_support_enable = + cfg_get(psoc, CFG_TDLS_SUPPORT_ENABLE); + tdls_soc_obj->tdls_configs.tdls_implicit_trigger_enable = + cfg_get(psoc, CFG_TDLS_IMPLICIT_TRIGGER); + tdls_soc_obj->tdls_configs.tdls_external_control = + cfg_get(psoc, CFG_TDLS_EXTERNAL_CONTROL); + tdls_soc_obj->max_num_tdls_sta = + cfg_get(psoc, CFG_TDLS_MAX_PEER_COUNT); + + tdls_update_feature_flag(tdls_soc_obj); + + return QDF_STATUS_SUCCESS; +} + +#ifdef TDLS_WOW_ENABLED +/** + * tdls_wow_init(): Create/init wake lock for TDLS + * + * Create/init wake lock for TDLS if DVR isn't supported + * + * Return None + */ +static void tdls_wow_init(struct tdls_soc_priv_obj *soc_obj) +{ + soc_obj->is_prevent_suspend = false; + soc_obj->is_drv_supported = qdf_is_drv_supported(); + if (!soc_obj->is_drv_supported) { + qdf_wake_lock_create(&soc_obj->wake_lock, "wlan_tdls"); + qdf_runtime_lock_init(&soc_obj->runtime_lock); + } +} + +/** + * tdls_wow_deinit(): Destroy/deinit wake lock for TDLS + * + * Destroy/deinit wake lock for TDLS if DVR isn't supported + * + * Return None + */ +static void tdls_wow_deinit(struct tdls_soc_priv_obj *soc_obj) +{ + if (!soc_obj->is_drv_supported) { + qdf_runtime_lock_deinit(&soc_obj->runtime_lock); + qdf_wake_lock_destroy(&soc_obj->wake_lock); + } +} +#else +static void tdls_wow_init(struct tdls_soc_priv_obj *soc_obj) +{ +} + +static void tdls_wow_deinit(struct tdls_soc_priv_obj *soc_obj) +{ +} +#endif + +static QDF_STATUS tdls_global_init(struct tdls_soc_priv_obj *soc_obj) +{ + tdls_object_init_params(soc_obj); + soc_obj->connected_peer_count = 0; + soc_obj->tdls_nss_switch_in_progress = false; + soc_obj->tdls_teardown_peers_cnt = 0; + soc_obj->tdls_nss_teardown_complete = false; + soc_obj->tdls_nss_transition_mode = TDLS_NSS_TRANSITION_S_UNKNOWN; + soc_obj->enable_tdls_connection_tracker = false; + soc_obj->tdls_external_peer_count = 0; + soc_obj->tdls_disable_in_progress = false; + + qdf_spinlock_create(&soc_obj->tdls_ct_spinlock); + tdls_wow_init(soc_obj); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_global_deinit(struct tdls_soc_priv_obj *soc_obj) +{ + tdls_wow_deinit(soc_obj); + qdf_spinlock_destroy(&soc_obj->tdls_ct_spinlock); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_tdls_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj; + + tdls_debug("tdls psoc open"); + soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!soc_obj) { + tdls_err("Failed to get tdls psoc component"); + return QDF_STATUS_E_FAILURE; + } + + status = tdls_global_init(soc_obj); + + return status; +} + +QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc, + struct tdls_start_params *req) +{ + struct tdls_soc_priv_obj *soc_obj; + uint32_t tdls_feature_flags; + struct policy_mgr_tdls_cbacks tdls_pm_call_backs; + uint8_t sta_idx; + + tdls_debug("tdls update config "); + if (!psoc || !req) { + tdls_err("psoc: 0x%pK, req: 0x%pK", psoc, req); + return QDF_STATUS_E_FAILURE; + } + + soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!soc_obj) { + tdls_err("Failed to get tdls psoc component"); + return QDF_STATUS_E_FAILURE; + } + + soc_obj->tdls_rx_cb = req->tdls_rx_cb; + soc_obj->tdls_rx_cb_data = req->tdls_rx_cb_data; + + soc_obj->tdls_wmm_cb = req->tdls_wmm_cb; + soc_obj->tdls_wmm_cb_data = req->tdls_wmm_cb_data; + + soc_obj->tdls_event_cb = req->tdls_event_cb; + soc_obj->tdls_evt_cb_data = req->tdls_evt_cb_data; + + /* Save callbacks to register/deregister TDLS sta with datapath */ + soc_obj->tdls_reg_peer = req->tdls_reg_peer; + soc_obj->tdls_peer_context = req->tdls_peer_context; + + /* Save legacy PE/WMA commands in TDLS soc object */ + soc_obj->tdls_send_mgmt_req = req->tdls_send_mgmt_req; + soc_obj->tdls_add_sta_req = req->tdls_add_sta_req; + soc_obj->tdls_del_sta_req = req->tdls_del_sta_req; + soc_obj->tdls_update_peer_state = req->tdls_update_peer_state; + soc_obj->tdls_del_all_peers = req->tdls_del_all_peers; + soc_obj->tdls_update_dp_vdev_flags = req->tdls_update_dp_vdev_flags; + soc_obj->tdls_dp_vdev_update = req->tdls_dp_vdev_update; + soc_obj->tdls_osif_init_cb = req->tdls_osif_init_cb; + soc_obj->tdls_osif_deinit_cb = req->tdls_osif_deinit_cb; + tdls_pm_call_backs.tdls_notify_increment_session = + tdls_notify_increment_session; + + tdls_pm_call_backs.tdls_notify_decrement_session = + tdls_notify_decrement_session; + if (QDF_STATUS_SUCCESS != policy_mgr_register_tdls_cb( + psoc, &tdls_pm_call_backs)) { + tdls_err("policy manager callback registration failed "); + return QDF_STATUS_E_FAILURE; + } + + tdls_update_feature_flag(soc_obj); + tdls_feature_flags = soc_obj->tdls_configs.tdls_feature_flags; + + if (!TDLS_IS_IMPLICIT_TRIG_ENABLED(tdls_feature_flags)) + soc_obj->tdls_current_mode = TDLS_SUPPORT_EXP_TRIG_ONLY; + else if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(tdls_feature_flags)) + soc_obj->tdls_current_mode = TDLS_SUPPORT_EXT_CONTROL; + else + soc_obj->tdls_current_mode = TDLS_SUPPORT_IMP_MODE; + + soc_obj->tdls_last_mode = soc_obj->tdls_current_mode; + if (TDLS_IS_BUFFER_STA_ENABLED(tdls_feature_flags) || + TDLS_IS_SLEEP_STA_ENABLED(tdls_feature_flags) || + TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags)) + soc_obj->max_num_tdls_sta = + WLAN_TDLS_STA_P_UAPSD_OFFCHAN_MAX_NUM; + + for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; sta_idx++) { + soc_obj->tdls_conn_info[sta_idx].valid_entry = false; + soc_obj->tdls_conn_info[sta_idx].index = + INVALID_TDLS_PEER_INDEX; + soc_obj->tdls_conn_info[sta_idx].session_id = 255; + qdf_mem_zero(&soc_obj->tdls_conn_info[sta_idx].peer_mac, + QDF_MAC_ADDR_SIZE); + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_tdls_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + + tdls_debug("psoc tdls enable: 0x%pK", psoc); + if (!psoc) { + tdls_err("NULL psoc"); + return QDF_STATUS_E_FAILURE; + } + + status = tgt_tdls_register_ev_handler(psoc); + + if (status != QDF_STATUS_SUCCESS) + return status; + + status = wlan_serialization_register_comp_info_cb(psoc, + WLAN_UMAC_COMP_TDLS, + WLAN_SER_CMD_SCAN, + tdls_scan_serialization_comp_info_cb); + if (QDF_STATUS_SUCCESS != status) { + tdls_err("Serialize scan cmd register failed "); + return status; + } + + /* register callbacks with tx/rx mgmt */ + status = tdls_mgmt_rx_ops(psoc, true); + if (status != QDF_STATUS_SUCCESS) + tdls_err("Failed to register mgmt rx callback, status:%d", + status); + return status; +} + +QDF_STATUS ucfg_tdls_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj = NULL; + + tdls_debug("psoc tdls disable: 0x%pK", psoc); + if (!psoc) { + tdls_err("NULL psoc"); + return QDF_STATUS_E_FAILURE; + } + + status = tgt_tdls_unregister_ev_handler(psoc); + if (QDF_IS_STATUS_ERROR(status)) + tdls_err("Failed to unregister tdls event handler"); + + status = tdls_mgmt_rx_ops(psoc, false); + if (QDF_IS_STATUS_ERROR(status)) + tdls_err("Failed to unregister mgmt rx callback"); + + soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!soc_obj) { + tdls_err("Failed to get tdls psoc component"); + return QDF_STATUS_E_FAILURE; + } + + soc_obj->tdls_event_cb = NULL; + soc_obj->tdls_evt_cb_data = NULL; + + return status; +} + +QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_soc_priv_obj *tdls_soc; + + tdls_debug("tdls psoc close"); + tdls_soc = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!tdls_soc) { + tdls_err("Failed to get tdls psoc component"); + return QDF_STATUS_E_FAILURE; + } + + status = tdls_global_deinit(tdls_soc); + + return status; +} + +QDF_STATUS ucfg_tdls_add_peer(struct wlan_objmgr_vdev *vdev, + struct tdls_add_peer_params *add_peer_req) +{ + struct scheduler_msg msg = {0, }; + struct tdls_add_peer_request *req; + QDF_STATUS status; + + if (!vdev || !add_peer_req) { + tdls_err("vdev: %pK, req %pK", vdev, add_peer_req); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("vdevid: %d, peertype: %d", + add_peer_req->vdev_id, add_peer_req->peer_type); + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + return status; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + status = QDF_STATUS_E_NOMEM; + goto dec_ref; + } + + qdf_mem_copy(&req->add_peer_req, add_peer_req, sizeof(*add_peer_req)); + req->vdev = vdev; + + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_ADD_STA; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post add peer msg fail"); + qdf_mem_free(req); + goto dec_ref; + } + + return status; +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + return status; +} + +QDF_STATUS ucfg_tdls_update_peer(struct wlan_objmgr_vdev *vdev, + struct tdls_update_peer_params *update_peer) +{ + struct scheduler_msg msg = {0,}; + struct tdls_update_peer_request *req; + QDF_STATUS status; + + if (!vdev || !update_peer) { + tdls_err("vdev: %pK, update_peer: %pK", vdev, update_peer); + return QDF_STATUS_E_NULL_VALUE; + } + + tdls_debug("vdev_id: %d, peertype: %d", + update_peer->vdev_id, update_peer->peer_type); + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + return status; + } + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + status = QDF_STATUS_E_NOMEM; + goto dec_ref; + } + qdf_mem_copy(&req->update_peer_req, update_peer, sizeof(*update_peer)); + req->vdev = vdev; + + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_CHANGE_STA; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post update peer msg fail"); + qdf_mem_free(req); + goto dec_ref; + } + + return status; +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + return status; +} + +static char *tdls_get_oper_str(enum tdls_command_type cmd_type) +{ + switch (cmd_type) { + case TDLS_CMD_ENABLE_LINK: + return "Enable_TDLS_LINK"; + case TDLS_CMD_DISABLE_LINK: + return "DISABLE_TDLS_LINK"; + case TDLS_CMD_REMOVE_FORCE_PEER: + return "REMOVE_FORCE_PEER"; + case TDLS_CMD_CONFIG_FORCE_PEER: + return "CONFIG_FORCE_PEER"; + default: + return "ERR:UNKNOWN OPER"; + } +} + +QDF_STATUS ucfg_tdls_oper(struct wlan_objmgr_vdev *vdev, + const uint8_t *macaddr, enum tdls_command_type cmd) +{ + struct scheduler_msg msg = {0,}; + struct tdls_oper_request *req; + QDF_STATUS status; + + if (!vdev || !macaddr) { + tdls_err("vdev: %pK, mac %pK", vdev, macaddr); + return QDF_STATUS_E_NULL_VALUE; + } + + tdls_debug("%s for peer " QDF_MAC_ADDR_FMT, + tdls_get_oper_str(cmd), + QDF_MAC_ADDR_REF(macaddr)); + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("%s: mem allocate fail", tdls_get_oper_str(cmd)); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto error; + } + + qdf_mem_copy(req->peer_addr, macaddr, QDF_MAC_ADDR_SIZE); + req->vdev = vdev; + + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post msg for %s fail", tdls_get_oper_str(cmd)); + goto dec_ref; + } + + return status; +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +error: + qdf_mem_free(req); + return status; +} + +QDF_STATUS ucfg_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev, + char *buf, int buflen) +{ + struct scheduler_msg msg = {0, }; + struct tdls_get_all_peers *tdls_peers; + QDF_STATUS status; + + tdls_peers = qdf_mem_malloc(sizeof(*tdls_peers)); + + if (!tdls_peers) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + tdls_peers->vdev = vdev; + tdls_peers->buf_len = buflen; + tdls_peers->buf = buf; + + msg.bodyptr = tdls_peers; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_GET_ALL_PEERS; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + + if (status != QDF_STATUS_SUCCESS) + qdf_mem_free(tdls_peers); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_send_mgmt_frame_flush_callback(struct scheduler_msg *msg) +{ + struct tdls_action_frame_request *req; + + if (!msg || !msg->bodyptr) { + tdls_err("msg or msg->bodyptr is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + req = msg->bodyptr; + if (req->vdev) + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + + qdf_mem_free(req); + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS ucfg_tdls_post_msg_flush_cb(struct scheduler_msg *msg) +{ + void *ptr = msg->bodyptr; + struct wlan_objmgr_vdev *vdev = NULL; + + switch (msg->type) { + case TDLS_CMD_TEARDOWN_LINKS: + case TDLS_NOTIFY_RESET_ADAPTERS: + ptr = NULL; + break; + case TDLS_NOTIFY_STA_CONNECTION: + vdev = ((struct tdls_sta_notify_params *)ptr)->vdev; + break; + case TDLS_NOTIFY_STA_DISCONNECTION: + vdev = ((struct tdls_sta_notify_params *)ptr)->vdev; + break; + case TDLS_CMD_SET_TDLS_MODE: + vdev = ((struct tdls_set_mode_params *)ptr)->vdev; + break; + case TDLS_CMD_TX_ACTION: + case TDLS_CMD_SET_RESPONDER: + break; + } + + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + + if (ptr) + qdf_mem_free(ptr); + + msg->bodyptr = NULL; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_tdls_send_mgmt_frame( + struct tdls_action_frame_request *req) +{ + struct scheduler_msg msg = {0, }; + struct tdls_action_frame_request *mgmt_req; + QDF_STATUS status; + + if (!req || !req->vdev) { + tdls_err("Invalid mgmt req params %pK", req); + return QDF_STATUS_E_NULL_VALUE; + } + + mgmt_req = qdf_mem_malloc(sizeof(*mgmt_req) + + req->len); + if (!mgmt_req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_copy(mgmt_req, req, sizeof(*req)); + + /*populate the additional IE's */ + if ((0 != req->len) && (req->cmd_buf)) { + qdf_mem_copy(mgmt_req->tdls_mgmt.buf, req->cmd_buf, + req->len); + mgmt_req->tdls_mgmt.len = req->len; + } else { + mgmt_req->tdls_mgmt.len = 0; + } + + tdls_debug("vdev id: %d, session id : %d", mgmt_req->vdev_id, + mgmt_req->session_id); + status = wlan_objmgr_vdev_try_get_ref(req->vdev, WLAN_TDLS_NB_ID); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Unable to get vdev reference for tdls module"); + goto mem_free; + } + + msg.bodyptr = mgmt_req; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_send_mgmt_frame_flush_callback; + msg.type = TDLS_CMD_TX_ACTION; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) + goto release_ref; + + return status; + +release_ref: + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); +mem_free: + qdf_mem_free(mgmt_req); + return status; +} + +QDF_STATUS ucfg_tdls_responder(struct tdls_set_responder_req *req) +{ + struct scheduler_msg msg = {0, }; + struct tdls_set_responder_req *msg_req; + QDF_STATUS status; + + if (!req || !req->vdev) { + tdls_err("invalid input %pK", req); + return QDF_STATUS_E_NULL_VALUE; + } + + msg_req = qdf_mem_malloc(sizeof(*msg_req)); + if (!msg_req) + return QDF_STATUS_E_NULL_VALUE; + + msg_req->responder = req->responder; + msg_req->vdev = req->vdev; + qdf_mem_copy(msg_req->peer_mac, req->peer_mac, QDF_MAC_ADDR_SIZE); + + msg.bodyptr = msg_req; + msg.callback = tdls_process_cmd; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + msg.type = TDLS_CMD_SET_RESPONDER; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(msg_req); + + return status; +} + +QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0, }; + + tdls_debug("Enter "); + + msg.bodyptr = psoc; + msg.callback = tdls_process_cmd; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + msg.type = TDLS_CMD_TEARDOWN_LINKS; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + + tdls_debug("Exit "); + return status; +} + +QDF_STATUS ucfg_tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0, }; + + if (!vdev) { + tdls_err("vdev is NULL "); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("Enter "); + msg.bodyptr = vdev; + msg.callback = tdls_process_cmd; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + msg.type = TDLS_NOTIFY_RESET_ADAPTERS; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + return status; +} + +QDF_STATUS ucfg_tdls_notify_sta_connect( + struct tdls_sta_notify_params *notify_info) +{ + struct scheduler_msg msg = {0, }; + struct tdls_sta_notify_params *notify; + QDF_STATUS status; + + if (!notify_info || !notify_info->vdev) { + tdls_err("notify_info %pK", notify_info); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("Enter "); + + notify = qdf_mem_malloc(sizeof(*notify)); + if (!notify) { + wlan_objmgr_vdev_release_ref(notify_info->vdev, + WLAN_TDLS_NB_ID); + return QDF_STATUS_E_NULL_VALUE; + } + + *notify = *notify_info; + + msg.bodyptr = notify; + msg.callback = tdls_process_cmd; + msg.type = TDLS_NOTIFY_STA_CONNECTION; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + } + + tdls_debug("Exit "); + return status; +} + +QDF_STATUS ucfg_tdls_notify_sta_disconnect( + struct tdls_sta_notify_params *notify_info) +{ + struct scheduler_msg msg = {0, }; + struct tdls_sta_notify_params *notify; + QDF_STATUS status; + + if (!notify_info || !notify_info->vdev) { + tdls_err("notify_info %pK", notify_info); + return QDF_STATUS_E_NULL_VALUE; + } + + tdls_debug("Enter "); + + notify = qdf_mem_malloc(sizeof(*notify)); + if (!notify) { + wlan_objmgr_vdev_release_ref(notify_info->vdev, WLAN_TDLS_NB_ID); + return QDF_STATUS_E_NULL_VALUE; + } + + *notify = *notify_info; + + msg.bodyptr = notify; + msg.callback = tdls_process_cmd; + msg.type = TDLS_NOTIFY_STA_DISCONNECTION; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + } + + tdls_debug("Exit "); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_tdls_set_operating_mode( + struct tdls_set_mode_params *set_mode_params) +{ + struct scheduler_msg msg = {0, }; + struct tdls_set_mode_params *set_mode; + QDF_STATUS status; + + if (!set_mode_params || !set_mode_params->vdev) { + tdls_err("set_mode_params %pK", set_mode_params); + return QDF_STATUS_E_NULL_VALUE; + } + + tdls_debug("Enter "); + + set_mode = qdf_mem_malloc(sizeof(*set_mode)); + if (!set_mode) { + tdls_err("memory allocate fail"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = wlan_objmgr_vdev_try_get_ref(set_mode->vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("failed to get vdev ref"); + qdf_mem_free(set_mode); + return status; + } + + set_mode->source = set_mode_params->source; + set_mode->tdls_mode = set_mode_params->tdls_mode; + set_mode->update_last = set_mode_params->update_last; + set_mode->vdev = set_mode_params->vdev; + + msg.bodyptr = set_mode; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_SET_TDLS_MODE; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_objmgr_vdev_release_ref(set_mode->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(set_mode); + } + + tdls_debug("Exit "); + + return QDF_STATUS_SUCCESS; +} + +void ucfg_tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr, + struct qdf_mac_addr *dest_mac_addr) +{ + tdls_update_rx_pkt_cnt(vdev, mac_addr, dest_mac_addr); + +} + +void ucfg_tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr) +{ + tdls_update_tx_pkt_cnt(vdev, mac_addr); +} + +QDF_STATUS ucfg_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, + uint32_t mode) +{ + QDF_STATUS status; + struct tdls_antenna_switch_request *req; + struct scheduler_msg msg = {0, }; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto error; + } + + req->vdev = vdev; + req->mode = mode; + + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_antenna_switch_flush_callback; + msg.type = TDLS_CMD_ANTENNA_SWITCH; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post antenna switch msg fail"); + goto dec_ref; + } + + return status; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +error: + qdf_mem_free(req); + return status; +} + +QDF_STATUS ucfg_set_tdls_offchannel(struct wlan_objmgr_vdev *vdev, + int offchannel) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0, }; + struct tdls_set_offchannel *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto free; + } + + req->offchannel = offchannel; + req->vdev = vdev; + req->callback = wlan_tdls_offchan_parms_callback; + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_SET_OFFCHANNEL; + status = scheduler_post_message(QDF_MODULE_ID_HDD, QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post set tdls offchannel msg fail"); + goto dec_ref; + } + + return status; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + +free: + qdf_mem_free(req); + return status; +} + +QDF_STATUS ucfg_set_tdls_offchan_mode(struct wlan_objmgr_vdev *vdev, + int offchanmode) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0, }; + struct tdls_set_offchanmode *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto free; + } + + req->offchan_mode = offchanmode; + req->vdev = vdev; + req->callback = wlan_tdls_offchan_parms_callback; + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_SET_OFFCHANMODE; + status = scheduler_post_message(QDF_MODULE_ID_HDD, QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post set offchanmode msg fail"); + goto dec_ref; + } + + return status; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + +free: + qdf_mem_free(req); + return status; +} + +QDF_STATUS ucfg_set_tdls_secoffchanneloffset(struct wlan_objmgr_vdev *vdev, + int offchanoffset) +{ + int status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0, }; + struct tdls_set_secoffchanneloffset *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto free; + } + + req->offchan_offset = offchanoffset; + req->vdev = vdev; + req->callback = wlan_tdls_offchan_parms_callback; + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_SET_SECOFFCHANOFFSET; + status = scheduler_post_message(QDF_MODULE_ID_HDD, QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post set secoffchan offset msg fail"); + goto dec_ref; + } + return status; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + +free: + qdf_mem_free(req); + return status; +} + +QDF_STATUS ucfg_tdls_set_rssi(struct wlan_objmgr_vdev *vdev, + uint8_t *mac, int8_t rssi) +{ + return tdls_set_rssi(vdev, mac, rssi); +} + +void ucfg_tdls_notify_connect_failure(struct wlan_objmgr_psoc *psoc) +{ + return tdls_notify_decrement_session(psoc); +} + +struct wlan_objmgr_vdev *ucfg_get_tdls_vdev(struct wlan_objmgr_psoc *psoc, + wlan_objmgr_ref_dbgid dbg_id) +{ + return tdls_get_vdev(psoc, dbg_id); +} diff --git a/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_utils_api.c b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_utils_api.c new file mode 100644 index 0000000000000000000000000000000000000000..2e9b2ad43ecd12b8d4da10e0a4765920dc5fdd50 --- /dev/null +++ b/drivers/staging/qcacld-3.0/components/tdls/dispatcher/src/wlan_tdls_utils_api.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_utils_api.c + * + * TDLS utility functions definitions + */ diff --git a/drivers/staging/qcacld-3.0/configs/default_defconfig b/drivers/staging/qcacld-3.0/configs/default_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..96ea66d3e50d347184ba59252f08d5a53f62f220 --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/default_defconfig @@ -0,0 +1,1074 @@ +ifeq ($(CONFIG_CNSS_QCA6290), y) + ifeq ($(CONFIG_CNSS_EMULATION), y) + CONFIG_QCA_WIFI_NAPIER_EMULATION := y + endif + CONFIG_LITHIUM := y + CONFIG_QCA6290_11AX := y + CONFIG_WLAN_FEATURE_11AX := y + CONFIG_WLAN_FEATURE_DFS_OFFLOAD := y + CONFIG_IPA3 := n +endif + +ifeq ($(CONFIG_CNSS_QCA6390), y) + ifeq ($(CONFIG_CNSS_EMULATION), y) + CONFIG_QCA_WIFI_NAPIER_EMULATION := y + endif + CONFIG_LITHIUM := y + CONFIG_WLAN_FEATURE_11AX := y + CONFIG_WLAN_FEATURE_DFS_OFFLOAD := y + ifeq ($(CONFIG_ARCH_SDM845), y) + CONFIG_IPA3 := n + endif + CONFIG_SCALE_INCLUDES := y + CONFIG_HASTINGS_BT_WAR := y + CONFIG_WDI3_IPA_OVER_GSI :=y + CONFIG_DIRECT_BUF_RX_ENABLE := y + CONFIG_WMI_DBR_SUPPORT := y + CONFIG_MORE_TX_DESC := y + CONFIG_FIX_TXDMA_ALIGNMENT := y +endif + +ifeq ($(CONFIG_CNSS_QCA6490), y) + CONFIG_DIRECT_BUF_RX_ENABLE := y + CONFIG_WMI_DBR_SUPPORT := y + CONFIG_WLAN_CFR_ENABLE := y + CONFIG_WLAN_ENH_CFR_ENABLE := y + ifeq ($(CONFIG_CNSS_EMULATION), y) + CONFIG_QCA_WIFI_NAPIER_EMULATION := y + endif + CONFIG_LITHIUM := y + CONFIG_WLAN_FEATURE_11AX := y + CONFIG_WLAN_FEATURE_DFS_OFFLOAD := y + ifeq ($(CONFIG_ARCH_SDM845), y) + CONFIG_IPA3 := n + endif + CONFIG_SCALE_INCLUDES := y + CONFIG_HASTINGS_BT_WAR := y + CONFIG_WDI3_IPA_OVER_GSI :=y + CONFIG_GENERIC_SHADOW_REGISTER_ACCESS_ENABLE :=y + CONFIG_DUMP_REO_QUEUE_INFO_IN_DDR :=y +endif + +ifeq ($(CONFIG_CNSS_QCA6750), y) + ifeq ($(CONFIG_CNSS_EMULATION), y) + CONFIG_QCA_WIFI_NAPIER_EMULATION := y + endif + CONFIG_LITHIUM := y + CONFIG_WLAN_FEATURE_11AX := y + CONFIG_WLAN_FEATURE_DFS_OFFLOAD := y + ifeq ($(CONFIG_ARCH_SDM845), y) + CONFIG_IPA3 := n + endif + CONFIG_SCALE_INCLUDES := y + CONFIG_HASTINGS_BT_WAR := y + CONFIG_WDI3_IPA_OVER_GSI :=y +endif + +ifeq ($(CONFIG_CLD_HL_SDIO_CORE), y) + CONFIG_QCA_WIFI_SDIO := y +ifndef CONFIG_SDIO_TRANSFER + CONFIG_SDIO_TRANSFER = mailbox +endif +endif + +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) + CONFIG_ROME_IF = sdio +endif + +ifdef CONFIG_ICNSS + CONFIG_ROME_IF = snoc +endif + +ifdef CONFIG_ICNSS2 + CONFIG_ROME_IF = ipci +endif + +ifeq (y,$(findstring y,$(CONFIG_CNSS) $(CONFIG_CNSS2))) +ifndef CONFIG_ROME_IF + #use pci as default interface + CONFIG_ROME_IF = pci +endif +endif + +ifeq (m,$(findstring m,$(CONFIG_CNSS2))) +ifndef CONFIG_ROME_IF + #use pci as default interface + CONFIG_ROME_IF = pci +endif +endif + +# Make WLAN as open-source driver by default +WLAN_OPEN_SOURCE := y + +ifeq ($(CONFIG_ICNSS), y) + CONFIG_HELIUMPLUS := y + CONFIG_64BIT_PADDR := y + CONFIG_FEATURE_TSO := y + CONFIG_FEATURE_TSO_DEBUG := y + ifeq ($(CONFIG_INET_LRO), y) + CONFIG_WLAN_LRO := y + else + CONFIG_WLAN_LRO := n + endif +endif + +ifeq ($(CONFIG_ARCH_MDM9630), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_MDM9640), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_SDX20), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_SDXPRAIRIE), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_MSM8917), y) + ifeq ($(CONFIG_ROME_IF), sdio) + CONFIG_WLAN_SYNC_TSF_PLUS := y + endif +endif + +ifeq (y,$(findstring y,$(CONFIG_ARCH_QCS405) $(CONFIG_ARCH_QCS403))) + CONFIG_ARCH_QCS40X := y +endif + +ifeq ($(CONFIG_ARCH_QCS40X), y) + CONFIG_WLAN_SYNC_TSF_PLUS := y + CONFIG_WLAN_SYNC_TSF_PLUS_NOIRQ := y + CONFIG_RX_PERFORMANCE := y + CONFIG_TGT_NUM_MSDU_DESC := 900 + CONFIG_MULTI_IF_LOG := y + CONFIG_DFS_PRI_MULTIPLIER := y + CONFIG_DFS_OVERRIDE_RF_THRESHOLD := y +endif +CONFIG_WLAN_FEATURE_MBSSID := y + +#Flag to enable Legacy Fast Roaming3(LFR3) +ifeq (y,$(findstring y,$(CONFIG_HELIUMPLUS) $(CONFIG_LITHIUM))) + CONFIG_QCACLD_WLAN_LFR3 := y +else + CONFIG_QCACLD_WLAN_LFR2 := y +endif + +ifneq ($(CONFIG_MOBILE_ROUTER), y) +#Flag to enable TDLS feature +CONFIG_QCOM_TDLS := y + +CONFIG_WLAN_SYSFS := y +CONFIG_WLAN_POWER_DEBUG := y +#Enable Beacon Reception Stats +CONFIG_FEATURE_BECN_STATS := y +endif + +#Disable the Export Symbol config +CONFIG_WLAN_DISABLE_EXPORT_SYMBOL := y + +CONFIG_QCACLD_FEATURE_GREEN_AP := y +#Flag to enable Android Packet Filtering +CONFIG_QCACLD_FEATURE_APF := y + +#Flag to enable SARv1 -> SARv2 conversion +CONFIG_WLAN_FEATURE_SARV1_TO_SARV2 := n + +#Flag to enable get firmware state +CONFIG_QCACLD_FEATURE_FW_STATE := y + +#Flag to enable set coex configuration +CONFIG_QCACLD_FEATURE_COEX_CONFIG := n + +#Flag to enable get hw capability +ifeq ($(CONFIG_ARCH_QCS40X), y) +CONFIG_QCACLD_FEATURE_HW_CAPABILITY := y +endif + +ifeq ($(CONFIG_ARCH_MSM8998), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SDM845), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SM8150), y) +CONFIG_QCACLD_FEATURE_METERING := y +CONFIG_DYNAMIC_RX_AGGREGATION := y +endif + +ifeq ($(CONFIG_HELIUMPLUS), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_KONA), y) +CONFIG_QCACLD_FEATURE_METERING := y +CONFIG_WDI3_STATS_UPDATE := y +CONFIG_WLAN_SYNC_TSF_TIMER := y +endif + +#Flag to enable Fast Transition (11r) feature +CONFIG_QCOM_VOWIFI_11R := y + +#Flag to enable disable ACTION OUI feature +CONFIG_WLAN_FEATURE_ACTION_OUI := y + +#Flag to enable Adaptive 11r feature +CONFIG_ADAPTIVE_11R := y + +#Flag to enable sae single pmk feature +CONFIG_SAE_SINGLE_PMK := y + +#Flag to enable FILS Feature (11ai) +CONFIG_WLAN_FEATURE_FILS := y +ifneq ($(CONFIG_QCA_CLD_WLAN),) + ifeq (y,$(findstring y,$(CONFIG_CNSS) $(CONFIG_CNSS2) $(CONFIG_ICNSS)$(CONFIG_ICNSS2))) + #Flag to enable Protected Management Frames (11w) feature + CONFIG_WLAN_FEATURE_11W := y + #Flag to enable LTE CoEx feature + CONFIG_QCOM_LTE_COEX := y + ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable LPSS feature + CONFIG_WLAN_FEATURE_LPSS := y + endif + endif + + ifeq (m,$(findstring m,$(CONFIG_CNSS2) $(CONFIG_ICNSS2))) + #Flag to enable Protected Management Frames (11w) feature + CONFIG_WLAN_FEATURE_11W := y + #Flag to enable LTE CoEx feature + CONFIG_QCOM_LTE_COEX := y + ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable LPSS feature + CONFIG_WLAN_FEATURE_LPSS := y + endif + endif +endif + +#Flag to enable Protected Management Frames (11w) feature +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_WLAN_FEATURE_11W := y +endif +ifeq ($(CONFIG_ROME_IF),sdio) + CONFIG_WLAN_FEATURE_11W := y +endif + +#Flag to enable the tx desc sanity check +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_QCA_TXDESC_SANITY_CHECKS := y +endif + +ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable NAN + CONFIG_QCACLD_FEATURE_NAN := y +endif + +#Flag to enable Linux QCMBR feature as default feature +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_LINUX_QCMBR :=y +endif + + +CONFIG_FEATURE_EPPING := y + +#Flag to enable offload packets feature +CONFIG_WLAN_OFFLOAD_PACKETS := y + +#enable TSF get feature +CONFIG_WLAN_SYNC_TSF := y +#Enable DSRC feature +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) +CONFIG_WLAN_FEATURE_DSRC := y +endif + +ifneq ($(CONFIG_ROME_IF),usb) + #Flag to enable SAE + CONFIG_WLAN_FEATURE_SAE := y + +ifneq ($(CONFIG_ROME_IF),sdio) + #Flag to enable DISA + CONFIG_WLAN_FEATURE_DISA := y + + #Flag to enable FIPS + CONFIG_WLAN_FEATURE_FIPS := y + + #Flag to enable Fast Path feature + ifneq ($(CONFIG_LITHIUM), y) + CONFIG_WLAN_FASTPATH := y + endif + + # Flag to enable NAPI + CONFIG_WLAN_NAPI := y + CONFIG_WLAN_NAPI_DEBUG := n + + # Flag to enable FW based TX Flow control + ifeq ($(CONFIG_LITHIUM), y) + CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y + else + CONFIG_WLAN_TX_FLOW_CONTROL_V2 := n + endif + +endif +endif + +CONFIG_POWER_MANAGEMENT_OFFLOAD := y + +ifeq ($(CONFIG_ROME_IF), snoc) + CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y +endif + +ifeq ($(CONFIG_ARCH_QCS40X), y) +CONFIG_WLAN_TX_FLOW_CONTROL_V2 := n +# Flag to improve TCP TX throughput for both +# CONFIG_WLAN_TX_FLOW_CONTROL_LEGACY and CONFIG_WLAN_TX_FLOW_CONTROL_V2 +# disabled platform, avoid frame drop in driver +CONFIG_WLAN_PDEV_TX_FLOW_CONTROL := y +endif + +# Flag to enable LFR Subnet Detection +CONFIG_LFR_SUBNET_DETECTION := y + +# Flag to enable MCC to SCC switch feature +CONFIG_MCC_TO_SCC_SWITCH := y + +ifeq ($(CONFIG_SLUB_DEBUG), y) + # Enable Obj Mgr Degug services if slub build + CONFIG_WLAN_OBJMGR_DEBUG:= y + CONFIG_MPC_UT_FRAMEWORK := y + CONFIG_LOCK_STATS_ON:= y +endif + +ifeq (y,$(findstring y,$(CONFIG_QCA_WIFI_SDIO) $(CONFIG_HIF_USB))) +CONFIG_HL_DP_SUPPORT := y +else +CONFIG_LL_DP_SUPPORT := y +endif + +ifeq ($(CONFIG_ROME_IF),pci) +ifneq ($(CONFIG_WLAN_TX_FLOW_CONTROL_V2), y) +ifneq ($(CONFIG_LITHIUM), y) +CONFIG_WLAN_TX_FLOW_CONTROL_LEGACY := y +endif +endif +endif + +#Whether have QMI support +CONFIG_QMI_SUPPORT := y + +ifeq ($(CONFIG_ICNSS), y) +CONFIG_WIFI_3_0_ADRASTEA := y +CONFIG_ADRASTEA_RRI_ON_DDR := y +# Enable athdiag procfs debug support for adrastea +CONFIG_ATH_PROCFS_DIAG_SUPPORT := y +# Enable 11AC TX compact feature for adrastea +CONFIG_ATH_11AC_TXCOMPACT := y +ifeq ($(CONFIG_QMI_SUPPORT), y) +CONFIG_ADRASTEA_SHADOW_REGISTERS := y +endif +endif + +# Enable fw stats version 2 +ifeq (y,$(findstring y,$(CONFIG_HELIUMPLUS) $(CONFIG_LITHIUM))) +CONFIG_AR900B := y +endif + +# NOTE: CONFIG_64BIT_PADDR requires CONFIG_HELIUMPLUS +ifeq ($(CONFIG_HELIUMPLUS), y) + +ifeq ($(CONFIG_64BIT_PADDR), y) +CONFIG_HTT_PADDR64 := y +endif + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) +CONFIG_OL_RX_INDICATION_RECORD := y +CONFIG_TSOSEG_DEBUG := y +CONFIG_HIF_LARGE_CE_RING_HISTORY := 8192 +endif + +endif #CONFIG_HELIUMPLUS + +ifeq ($(CONFIG_LITHIUM), y) +CONFIG_RX_DEFRAG_DO_NOT_REINJECT := y +CONFIG_IPA_SET_RESET_TX_DB_PA := y +# +# Enable Shadow V2 for all lithium platform +# +CONFIG_SHADOW_V2 := y + +ifeq ($(CONFIG_CNSS_QCA6290), y) + CONFIG_QCA6290_HEADERS_DEF := y + CONFIG_QCA_WIFI_QCA6290 := y +endif +ifeq ($(CONFIG_CNSS_QCA6390), y) + CONFIG_QCA6390_HEADERS_DEF := y + CONFIG_QCA_WIFI_QCA6390 := y +endif + +ifeq ($(CONFIG_CNSS_QCA6490), y) + CONFIG_QCA6490_HEADERS_DEF := y + CONFIG_QCA_WIFI_QCA6490 := y +endif + +ifeq ($(CONFIG_CNSS_QCA6750), y) + CONFIG_QCA6750_HEADERS_DEF := y + CONFIG_QCA_WIFI_QCA6750 := y +endif + +ifeq ($(CONFIG_LITHIUM), y) +# +# Enable VERBOSE debug INI mechanism +# +CONFIG_VERBOSE_DEBUG := y +CONFIG_RX_DESC_SANITY_WAR := y +ifeq ($(CONFIG_PCI_MSM), y) + CONFIG_FEATURE_HAL_DELAYED_REG_WRITE := y +ifeq ($(CONFIG_CNSS_QCA6390), y) + CONFIG_SHADOW_WRITE_DELAY := y +endif +endif +endif + +CONFIG_QCA_WIFI_QCA8074 := y +CONFIG_QCA_WIFI_QCA8074_VP := y +CONFIG_DP_INTR_POLL_BASED := y +CONFIG_TX_PER_PDEV_DESC_POOL := y +CONFIG_DP_TRACE := y +CONFIG_FEATURE_TSO := y +CONFIG_FEATURE_FORCE_WAKE := y +CONFIG_DP_LFR := y +CONFIG_DUP_RX_DESC_WAR := y +CONFIG_HTT_PADDR64 := y +CONFIG_RX_OL := y +CONFIG_TX_TID_OVERRIDE := y +CONFIG_DP_TXRX_SOC_ATTACH := y +CONFIG_WLAN_CLD_PM_QOS := y +CONFIG_DISABLE_DP_STATS := n +CONFIG_MAX_ALLOC_PAGE_SIZE := y +CONFIG_REO_DESC_DEFER_FREE := y +CONFIG_RXDMA_ERR_PKT_DROP := y +CONFIG_DELIVERY_TO_STACK_STATUS_CHECK := y +CONFIG_DP_MEM_PRE_ALLOC := y + +ifeq ($(CONFIG_FEATURE_TSO), y) + CONFIG_FEATURE_TSO_STATS := y + CONFIG_TSO_DEBUG_LOG_ENABLE := y +endif + +ifeq ($(CONFIG_DISABLE_DP_STATS), y) + CONFIG_FEATURE_TSO_STATS := n +endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG=y + +# CONFIG_SLUB_DEBUG_ON=y + CONFIG_PAGE_POISONING=y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QCOMs internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. +ifneq ($(TARGET_BUILD_VARIANT),user) + ifeq ($(CONFIG_LITHIUM), y) + CONFIG_FEATURE_PKTLOG := n + else + CONFIG_FEATURE_PKTLOG := y + endif + CONFIG_WLAN_DEBUG_CRASH_INJECT := y +endif + +#Enable WLAN/Power debugfs feature only if debug_fs is enabled +ifeq ($(CONFIG_DEBUG_FS), y) + # Flag to enable debugfs. Depends on CONFIG_DEBUG_FS in kernel + # configuration. + CONFIG_WLAN_DEBUGFS := y + + # Flag to enable streamfs. Depends on CONFIG_DEBUG_FS and + # CONFIG_RELAY in kernel configuration. +ifeq ($(CONFIG_RELAY), y) + CONFIG_WLAN_STREAMFS := y +endif +endif + +ifeq ($(CONFIG_WLAN_DEBUGFS), y) + CONFIG_WLAN_MWS_INFO_DEBUGFS := y + CONFIG_WLAN_FEATURE_MIB_STATS := y +else + CONFIG_WLAN_MWS_INFO_DEBUGFS := n + CONFIG_WLAN_FEATURE_MIB_STATS := n +endif + +# Feature flags which are not (currently) configurable via Kconfig + +#Whether to build debug version +BUILD_DEBUG_VERSION := y + +#Enable this flag to build driver in diag version +BUILD_DIAG_VERSION := y + +ifeq ($(CONFIG_SLUB_DEBUG), y) + PANIC_ON_BUG := y + WLAN_WARN_ON_ASSERT := y +else ifeq ($(CONFIG_PERF_DEBUG), y) + PANIC_ON_BUG := y + WLAN_WARN_ON_ASSERT := y +else + PANIC_ON_BUG := n + WLAN_WARN_ON_ASSERT := n +endif + +# Compile all log levels by default +CONFIG_WLAN_LOG_FATAL := y +CONFIG_WLAN_LOG_ERROR := y +CONFIG_WLAN_LOG_WARN := y +CONFIG_WLAN_LOG_INFO := y +CONFIG_WLAN_LOG_DEBUG := y +CONFIG_WLAN_LOG_ENTER := y +CONFIG_WLAN_LOG_EXIT := y + +#Enable OL debug and wmi unified functions +CONFIG_ATH_PERF_PWR_OFFLOAD := y + +#Disable packet log +CONFIG_REMOVE_PKT_LOG := n + +#Enable 11AC TX +ifeq ($(CONFIG_ROME_IF),pci) + CONFIG_ATH_11AC_TXCOMPACT := y +endif + +ifeq ($(CONFIG_ROME_IF),ipci) + CONFIG_ATH_11AC_TXCOMPACT := y +endif + +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_ATH_11AC_TXCOMPACT := n +endif + +#Enable PCI specific APIS (dma, etc) +ifeq ($(CONFIG_ROME_IF),pci) + CONFIG_HIF_PCI := y +endif + +ifeq ($(CONFIG_ROME_IF),ipci) + CONFIG_HIF_IPCI := y +endif + +#Enable USB specific APIS +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_HIF_USB := y +endif + +#Enable SDIO specific APIS +ifeq ($(CONFIG_ROME_IF),sdio) + CONFIG_HIF_SDIO := y + CONFIG_TX_DESC_HI_PRIO_RESERVE := y + CONFIG_FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL := y + CONFIG_TGT_NUM_MSDU_DESC := 0 +endif + +ifeq ($(CONFIG_ROME_IF),snoc) + CONFIG_HIF_SNOC:= y +endif + +# enable/disable feature flags based upon mobile router profile +ifeq ($(CONFIG_MOBILE_ROUTER), y) +CONFIG_FEATURE_WLAN_MCC_TO_SCC_SWITCH := y +CONFIG_FEATURE_WLAN_AUTO_SHUTDOWN := y +CONFIG_FEATURE_WLAN_AP_AP_ACS_OPTIMIZE := y +CONFIG_FEATURE_WLAN_STA_4ADDR_SCHEME := y +CONFIG_MDM_PLATFORM := y +ifneq ($(CONFIG_ARCH_SDXPRAIRIE), y) +CONFIG_FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE := y +endif +CONFIG_FEATURE_AP_MCC_CH_AVOIDANCE := y +else +CONFIG_QCOM_ESE := y +CONFIG_FEATURE_WLAN_RMC := y +CONFIG_WLAN_OPEN_P2P_INTERFACE := y +CONFIG_WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY := y +endif + +#enable 4addr support for QCS40X +ifeq ($(CONFIG_ARCH_QCS40X), y) +CONFIG_FEATURE_WLAN_STA_4ADDR_SCHEME := y +endif + +#Enable power management suspend/resume functionality to PCI +CONFIG_ATH_BUS_PM := y + +#Enable FLOWMAC module support +CONFIG_ATH_SUPPORT_FLOWMAC_MODULE := n + +#Enable spectral support +CONFIG_ATH_SUPPORT_SPECTRAL := n + +#Define the legacy pktlog +ifeq ($(CONFIG_REMOVE_PKT_LOG), n) +ifeq ($(CONFIG_HELIUMPLUS), y) +CONFIG_PKTLOG_LEGACY := y +endif + +#Customize DSCP_to-UP map based on RFC8325 +ifeq ($(CONFIG_HELIUMPLUS), y) +CONFIG_WLAN_CUSTOM_DSCP_UP_MAP := y +CONFIG_WLAN_SEND_DSCP_UP_MAP_TO_FW := y +endif +ifeq ($(CONFIG_LITHIUM), y) +CONFIG_WLAN_CUSTOM_DSCP_UP_MAP := y +endif + +ifeq ($(CONFIG_ARCH_BENGAL), y) +CONFIG_SMMU_S1_UNMAP := y +endif + +ifeq ($(CONFIG_ROME_IF), sdio) +CONFIG_PKTLOG_LEGACY := y +endif + +ifeq ($(CONFIG_ROME_IF), pci) +CONFIG_PKTLOG_LEGACY := y +endif + +ifeq ($(CONFIG_ROME_IF), usb) +CONFIG_PKTLOG_LEGACY := y +endif + +ifeq ($(CONFIG_ROME_IF), snoc) +CONFIG_PKTLOG_LEGACY := y +endif +endif + +ifeq ($(CONFIG_LITHIUM), y) +CONFIG_PKTLOG_LEGACY := n +endif + +ifeq ($(CONFIG_LITHIUM), y) +CONFIG_WAPI_BIG_ENDIAN := y +else +CONFIG_WAPI_BIG_ENDIAN := n +endif + +#Enable WDI Event support +ifeq ($(CONFIG_REMOVE_PKT_LOG), y) +CONFIG_WDI_EVENT_ENABLE := n +else +CONFIG_WDI_EVENT_ENABLE := y +endif + +#Enable the type_specific_data in the ath_pktlog_arg +ifeq ($(CONFIG_REMOVE_PKT_LOG), n) +ifeq ($(CONFIG_LITHIUM), y) +CONFIG_PKTLOG_HAS_SPECIFIC_DATA := y +endif + +ifeq ($(CONFIG_HELIUMPLUS), y) +CONFIG_PKTLOG_HAS_SPECIFIC_DATA := y +endif +endif + +#Endianness selection +CONFIG_LITTLE_ENDIAN := y + +#Enable TX reclaim support +CONFIG_TX_CREDIT_RECLAIM_SUPPORT := n + +#Enable FTM support +CONFIG_QCA_WIFI_FTM := y + +ifeq ($(CONFIG_QCA_WIFI_FTM), y) + +ifeq ($(CONFIG_NL80211_TESTMODE), y) + QCA_WIFI_FTM_NL80211 :=y +else + QCA_WIFI_FTM_NL80211 :=n +endif + CONFIG_LINUX_QCMBR :=y + +else + QCA_WIFI_FTM_NL80211 :=n + CONFIG_LINUX_QCMBR :=n +endif + + +#Enable Checksum Offload +CONFIG_CHECKSUM_OFFLOAD := y + +#Enable GTK offload +CONFIG_GTK_OFFLOAD := y + +#Enable EXT WOW +ifeq ($(CONFIG_HIF_PCI), y) + CONFIG_EXT_WOW := y +endif + +#Set this to 1 to catch erroneous Target accesses during debug. +CONFIG_ATH_PCIE_ACCESS_DEBUG := n + +#Enable IPA offload +ifeq ($(CONFIG_IPA), y) +CONFIG_IPA_OFFLOAD := y +endif +ifeq ($(CONFIG_IPA3), y) +CONFIG_IPA_OFFLOAD := y +endif + +#Flag to enable SMMU S1 support +ifeq ($(CONFIG_ARCH_SDM845), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif + +ifeq ($(CONFIG_ARCH_SM8150), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +ifneq ($(CONFIG_LITHIUM), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif +endif + +ifeq ($(CONFIG_ARCH_SDXPRAIRIE), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif + +ifeq ($(CONFIG_ARCH_KONA), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +CONFIG_SMMU_S1_UNMAP := y +endif +endif + +ifeq ($(CONFIG_HELIUMPLUS), y) +ifneq ($(CONFIG_ARCH_SDM630), y) +ifneq ($(CONFIG_ARCH_SDM660), y) +ifneq ($(CONFIG_ARCH_MSM8998), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif +endif +endif +endif + +ifeq ($(CONFIG_ARCH_SDX20), y) +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) +ifeq ($(CONFIG_WCNSS_SKB_PRE_ALLOC), y) +CONFIG_FEATURE_SKB_PRE_ALLOC := y +endif +endif +endif + +#Enable Signed firmware support for split binary format +CONFIG_QCA_SIGNED_SPLIT_BINARY_SUPPORT := n + +#Enable single firmware binary format +CONFIG_QCA_SINGLE_BINARY_SUPPORT := n + +#Enable collecting target RAM dump after kernel panic +CONFIG_TARGET_RAMDUMP_AFTER_KERNEL_PANIC := y + +#Flag to enable/disable secure firmware feature +CONFIG_FEATURE_SECURE_FIRMWARE := n + +#Flag to enable Stats Ext implementation +CONFIG_FEATURE_STATS_EXT := y + +#Flag to allocate memory dynamically for different buffers +CONFIG_WLAN_LOGGING_BUFFERS_DYNAMICALLY := n +CONFIG_WLAN_DFS_STATIC_MEM_ALLOC := y + +#Flag to enable HTC credit history feature +CONFIG_FEATURE_HTC_CREDIT_HISTORY := y + +#Flag to enable MTRACE feature +CONFIG_TRACE_RECORD_FEATURE := y + +#Flag to enable p2p debug feature +CONFIG_WLAN_FEATURE_P2P_DEBUG := y + +#Flag to enable roam debug log +CONFIG_FEATURE_ROAM_DEBUG := y + +#Flag to enable DFS Master feature +CONFIG_WLAN_DFS_MASTER_ENABLE := y + +#Flag to enable WEXT support for STA/AP/P2P interfaces +CONFIG_WLAN_WEXT_SUPPORT_ENABLE := y + +#Flag to enable/disable MTRACE feature +CONFIG_ENABLE_MTRACE_LOG := y + +#Flag to enable nud tracking feature +CONFIG_WLAN_NUD_TRACKING := y + +#Flag to enable/Disable Function call trace +CONFIG_FUNC_CALL_MAP := n + +#Flag to enable wbuff feature +CONFIG_WLAN_WBUFF := y + +#Flag to enable set and get disable channel list feature +CONFIG_DISABLE_CHANNEL_LIST :=y + +#Flag to enable beacon receive feature +CONFIG_WLAN_BCN_RECV_FEATURE := y + +#Flag to enable LTE COEX feature +CONFIG_LTE_COEX := y + +#Flag to enable/disable TARGET 11d scan +CONFIG_TARGET_11D_SCAN := y + +#Flag to enable/disable Avoid acs freq feature +CONFIG_SAP_AVOID_ACS_FREQ_LIST := y + +#Flag to enable HOST OPCLASS feature +CONFIG_HOST_OPCLASS := y + +#Flag to enable Dynamic Voltage WDCVS (Config Voltage Mode) +CONFIG_WLAN_DYNAMIC_CVM := y + +#Flag to enable SAR Safety Feature +CONFIG_SAR_SAFETY_FEATURE := y + +CONFIG_WIFI_POS_CONVERGED := y +ifneq ($(CONFIG_WIFI_POS_CONVERGED), y) +CONFIG_WIFI_POS_LEGACY := y +endif + +CONFIG_CP_STATS := y +CONFIG_FEATURE_INTEROP_ISSUES_AP := y + +CONFIG_FEATURE_WLAN_WAPI := y + +CONFIG_AGEIE_ON_SCAN_RESULTS := y + +#Flag to enable FW log parsing support feature +CONFIG_FEATURE_FW_LOG_PARSING := y + +CONFIG_PTT_SOCK_SVC_ENABLE := y +CONFIG_SOFTAP_CHANNEL_RANGE := y +CONFIG_FEATURE_WLAN_SCAN_PNO := y +CONFIG_WLAN_FEATURE_PACKET_FILTERING := y +CONFIG_WLAN_NS_OFFLOAD := y +CONFIG_FEATURE_WLAN_RA_FILTERING:= y +CONFIG_FEATURE_WLAN_LPHB := y +CONFIG_QCA_SUPPORT_TX_THROTTLE := y +CONFIG_WMI_INTERFACE_EVENT_LOGGING := y +CONFIG_WLAN_FEATURE_LINK_LAYER_STATS := y +CONFIG_FEATURE_WLAN_EXTSCAN := n +CONFIG_WMI_BCN_OFFLOAD := y +CONFIG_160MHZ_SUPPORT := y +CONFIG_MCL := y +CONFIG_REG_CLIENT := y +CONFIG_WLAN_PMO_ENABLE := y +CONFIG_CONVERGED_P2P_ENABLE := y +CONFIG_WLAN_POLICY_MGR_ENABLE := y +CONFIG_FEATURE_BLACKLIST_MGR := y +CONFIG_FOURTH_CONNECTION := y +CONFIG_SUPPORT_11AX := y +CONFIG_HDD_INIT_WITH_RTNL_LOCK := y +CONFIG_WLAN_CONV_SPECTRAL_ENABLE := y +CONFIG_WLAN_SPECTRAL_ENABLE := y +CONFIG_WMI_CMD_STRINGS := y +CONFIG_FEATURE_MONITOR_MODE_SUPPORT := y +CONFIG_WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY := n +CONFIG_WLAN_FEATURE_TWT := y +CONFIG_FW_THERMAL_THROTTLE := y + +ifeq (y,$(findstring y,$(CONFIG_LITHIUM) $(CONFIG_ICNSS))) +CONFIG_WLAN_FEATURE_BMI := n +else +CONFIG_WLAN_FEATURE_BMI := y +endif + +#Flags to enable/disable vendor commands +CONFIG_FEATURE_RSSI_MONITOR := y +CONFIG_FEATURE_BSS_TRANSITION := y +CONFIG_FEATURE_STATION_INFO := y +CONFIG_FEATURE_TX_POWER := y +CONFIG_FEATURE_OTA_TEST := y +CONFIG_FEATURE_ACTIVE_TOS := y +CONFIG_FEATURE_SAR_LIMITS := y +CONFIG_FEATURE_CONCURRENCY_MATRIX := y +CONFIG_FEATURE_SAP_COND_CHAN_SWITCH := y +CONFIG_FEATURE_P2P_LISTEN_OFFLOAD := y +CONFIG_QCACLD_FEATURE_MPTA_HELPER := n +CONFIG_QCACLD_RX_DESC_MULTI_PAGE_ALLOC := y + +#Flags to enable/disable WMI APIs +CONFIG_WMI_ROAM_SUPPORT := y +CONFIG_WMI_CONCURRENCY_SUPPORT := y +CONFIG_WMI_STA_SUPPORT := y + +CONFIG_DP_TRACE := y + +ifeq ($(CONFIG_HELIUMPLUS), y) +ifneq ($(CONFIG_FORCE_ALLOC_FROM_DMA_ZONE), y) +CONFIG_ENABLE_DEBUG_ADDRESS_MARKING := y +endif +CONFIG_RX_OL := y +endif + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) + CONFIG_DSC_DEBUG := y + CONFIG_DESC_TIMESTAMP_DEBUG_INFO := y + CONFIG_FEATURE_UNIT_TEST_SUSPEND := y + CONFIG_LEAK_DETECTION := y + CONFIG_MAX_LOGS_PER_SEC := 500 + CONFIG_SCHED_HISTORY_SIZE := 256 + CONFIG_TALLOC_DEBUG := y + CONFIG_UNIT_TEST := y + CONFIG_REGISTER_OP_DEBUG := y + CONFIG_ENABLE_QDF_PTR_HASH_DEBUG := y +endif + +ifeq ($(CONFIG_UNIT_TEST), y) + CONFIG_DSC_TEST := y + CONFIG_QDF_TEST := y + CONFIG_FEATURE_WLM_STATS := y +endif + +ifeq ($(CONFIG_LITHIUM), y) + ifeq ($(CONFIG_SLUB_DEBUG_ON), y) + CONFIG_WLAN_FEATURE_DP_EVENT_HISTORY := y + CONFIG_HIF_CE_DEBUG_DATA_BUF := y + CONFIG_WLAN_RECORD_RX_PADDR := y + CONFIG_HIF_CPU_PERF_AFFINE_MASK := y + CONFIG_WLAN_FEATURE_DP_RX_RING_HISTORY := y + CONFIG_ALLOW_PKT_DROPPING := y + endif + CONFIG_RX_DESC_DEBUG_CHECK:= y + CONFIG_WLAN_SUPPORT_DATA_STALL := y + CONFIG_WLAN_DP_PER_RING_TYPE_CONFIG := y + CONFIG_WLAN_CE_INTERRUPT_THRESHOLD_CONFIG := y + #Enable WMI TX/RX over QMI + CONFIG_WMI_SEND_RECV_QMI := y + CONFIG_WLAN_DP_PENDING_MEM_FLUSH := y + CONFIG_DYNAMIC_RX_AGGREGATION := y +endif + +#Flag to enable hdd memory dump feature +CONFIG_FEATURE_MEMDUMP_ENABLE := y + +#Flag to enable/disable WLAN D0-WOW +ifeq ($(CONFIG_PCI_MSM), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_FEATURE_WLAN_D0WOW := y +endif +endif + +ifeq ($(CONFIG_ARCH_MDM9607), y) +CONFIG_TUFELLO_DUAL_FW_SUPPORT := y +endif + +ifeq ($(CONFIG_ARCH_MSM8996), y) +CONFIG_CHANNEL_HOPPING_ALL_BANDS := y +endif + +ifeq ($(CONFIG_ARCH_SDXPRAIRIE), y) + ifneq ($(CONFIG_SLUB_DEBUG), y) + CONFIG_DP_TRACE := n + endif + + CONFIG_DIRECT_BUF_RX_ENABLE := n + CONFIG_WMI_DBR_SUPPORT := n +endif + +ifneq ($(CONFIG_HIF_USB), y) +CONFIG_WLAN_LOGGING_SOCK_SVC := y +endif + +ifneq ($(TARGET_BUILD_VARIANT),user) +CONFIG_DESC_DUP_DETECT_DEBUG := y +CONFIG_DEBUG_RX_RING_BUFFER := y +endif + +ifeq ($(CONFIG_CNSS), y) +ifeq ($(CONFIG_CNSS_SDIO), y) +CONFIG_PLD_SDIO_CNSS_FLAG := y +endif +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +endif +endif + +ifeq ($(CONFIG_ICNSS2), y) +CONFIG_PLD_IPCI_ICNSS_FLAG := y +endif + +ifeq ($(CONFIG_CNSS2), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +ifeq ($(CONFIG_CNSS2), m) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +#Enable STATE MACHINE HISTORY +CONFIG_SM_ENG_HIST := n + +#Enable OEM DATA feature +CONFIG_FEATURE_OEM_DATA := y + +#Enable FW Offload +CONFIG_WLAN_FW_OFFLOAD := y + +#Enable eLNA bypass feature +ifeq ($(CONFIG_WLAN_FW_OFFLOAD), y) +CONFIG_WLAN_FEATURE_ELNA := y +endif + +#Enable DP Bus Vote +CONFIG_WLAN_FEATURE_DP_BUS_BANDWIDTH := y + +ifeq ($(CONFIG_CNSS_QCA6490), y) + +#Enable 6 GHz Band +CONFIG_BAND_6GHZ := y +CONFIG_RX_FISA := y +CONFIG_MORE_TX_DESC := y + +endif + +CONFIG_HANDLE_BC_EAP_TX_FRM := y + +ifeq ($(CONFIG_BAND_6GHZ), y) + +CONFIG_6G_SCAN_CHAN_SORT_ALGO := y + +endif + +CONFIG_SAP_DHCP_FW_IND := y + +#Enable support to get ANI level +CONFIG_ANI_LEVEL_REQUEST := y + +ifeq ($(CONFIG_ARCH_QCS405), y) +CONFIG_FEATURE_WLAN_TIME_SYNC_FTM := y +endif + +CONFIG_WLAN_HANG_EVENT := y + +CONFIG_FEATURE_GPIO_CFG := y + +#Enable VDEV Response wakelock feature +CONFIG_FEATURE_VDEV_RSP_WAKELOCK := y diff --git a/drivers/staging/qcacld-3.0/configs/genoa.common b/drivers/staging/qcacld-3.0/configs/genoa.common new file mode 100644 index 0000000000000000000000000000000000000000..e4980ca651b35bab23844fa8588a86af2f34b507 --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/genoa.common @@ -0,0 +1,233 @@ +# Protocol specific features + +#features not required for GENOA IOT, compilation errors are there. +CONFIG_SUPPORT_11AX := y +CONFIG_160MHZ_SUPPORT := y +CONFIG_FEATURE_STATS_EXT := n +CONFIG_QCA_IBSS_SUPPORT := y +CONFIG_FEATURE_WLAN_FT_IEEE8021X := y +CONFIG_FEATURE_WLAN_FT_PSK := y + +#required features +CONFIG_FEATURE_WLAN_RMC := n +CONFIG_QCACLD_WLAN_LFR2 := y +CONFIG_QCACLD_WLAN_LFR3 := n +CONFIG_QCOM_TDLS := y +CONFIG_QCACLD_FEATURE_GREEN_AP := n +CONFIG_CRYPTO_COMPONENT := y +CONFIG_QCOM_VOWIFI_11R := y +CONFIG_WLAN_FEATURE_FILS := y +CONFIG_WLAN_FEATURE_11W := y +CONFIG_QCOM_LTE_COEX := n +CONFIG_WLAN_FEATURE_LPSS := n +CONFIG_QCACLD_FEATURE_NAN := y +CONFIG_POWER_MANAGEMENT_OFFLOAD := y +CONFIG_LFR_SUBNET_DETECTION := y +CONFIG_MCC_TO_SCC_SWITCH := y +CONFIG_QCOM_ESE := n +CONFIG_WLAN_OPEN_P2P_INTERFACE := y +CONFIG_WLAN_CONV_SPECTRAL_ENABLE := n +CONFIG_WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY := y +CONFIG_WLAN_DFS_MASTER_ENABLE := y +CONFIG_WIFI_POS_CONVERGED := y +CONFIG_WIFI_POS_LEGACY := n +CONFIG_FEATURE_WLAN_WAPI := y +CONFIG_AGEIE_ON_SCAN_RESULTS := y +CONFIG_WLAN_PMO_ENABLE := y +CONFIG_CONVERGED_P2P_ENABLE := y +CONFIG_WLAN_POLICY_MGR_ENABLE := y +CONFIG_HDD_INIT_WITH_RTNL_LOCK := y +CONFIG_WLAN_FEATURE_TWT := n +CONFIG_WMI_CMD_STRINGS := y +CONFIG_SOFTAP_CHANNEL_RANGE := y +CONFIG_FEATURE_WLAN_SCAN_PNO := y +CONFIG_WLAN_FEATURE_DSRC := n +CONFIG_FEATURE_MONITOR_MODE_SUPPORT := n +CONFIG_DP_TRACE := y +CONFIG_QCACLD_FEATURE_METERING := n + +#Flag to enable Dynamic Voltage WDCVS (Config Voltage Mode) +CONFIG_WLAN_DYNAMIC_CVM := n + +#We might need to disable WEXT support in perf builds in future +CONFIG_WLAN_WEXT_SUPPORT_ENABLE := y +CONFIG_HOST_11D_SCAN := y + +#Flag to enable/disable Avoid acs freq feature +CONFIG_SAP_AVOID_ACS_FREQ_LIST := n + +ifeq ($(CONFIG_ARCH_MSM8996), y) +CONFIG_CHANNEL_HOPPING_ALL_BANDS := y +endif + +# Debug specific features +CONFIG_WLAN_LOGGING_BUFFERS_DYNAMICALLY := y +BUILD_DEBUG_VERSION := y +BUILD_DIAG_VERSION := n + +CONFIG_REMOVE_PKT_LOG := y +CONFIG_FEATURE_HTC_CREDIT_HISTORY := y +CONFIG_TRACE_RECORD_FEATURE := y +CONFIG_WLAN_NUD_TRACKING := n +CONFIG_CP_STATS := y +CONFIG_FEATURE_FW_LOG_PARSING := n +CONFIG_PTT_SOCK_SVC_ENABLE := y +CONFIG_WMI_INTERFACE_EVENT_LOGGING := y +CONFIG_WLAN_FEATURE_LINK_LAYER_STATS := n +CONFIG_FEATURE_ROAM_DEBUG := y + +CONFIG_WLAN_LOG_FATAL := y +CONFIG_WLAN_LOG_ERROR := y +CONFIG_WLAN_LOG_WARN := y +CONFIG_WLAN_LOG_INFO := y +CONFIG_WLAN_LOG_DEBUG := y + +ifeq ($(CONFIG_REMOVE_PKT_LOG), y) +CONFIG_WDI_EVENT_ENABLE := n +else +CONFIG_WDI_EVENT_ENABLE := y +endif + +ifeq ($(CONFIG_REMOVE_PKT_LOG), n) +CONFIG_PKTLOG_LEGACY := y +endif + +#Enable the type_specific_data in the ath_pktlog_arg +ifeq ($(CONFIG_REMOVE_PKT_LOG), n) +CONFIG_PKTLOG_HAS_SPECIFIC_DATA := y +endif + +PANIC_ON_BUG := y +WLAN_WARN_ON_ASSERT := y +CONFIG_WLAN_LOGGING_SOCK_SVC := y + +#Flag to enable wbuff feature +CONFIG_WLAN_WBUFF := n + +# other features +WLAN_OPEN_SOURCE := y +CONFIG_ATH_PERF_PWR_OFFLOAD := y +CONFIG_ATH_BUS_PM := n +CONFIG_ATH_SUPPORT_FLOWMAC_MODULE := n +CONFIG_ATH_SUPPORT_SPECTRAL := n +CONFIG_LITTLE_ENDIAN := y +CONFIG_ATH_PCIE_ACCESS_DEBUG := n +CONFIG_QCA_SIGNED_SPLIT_BINARY_SUPPORT := n +CONFIG_QCA_SINGLE_BINARY_SUPPORT := n +CONFIG_TARGET_RAMDUMP_AFTER_KERNEL_PANIC := y +CONFIG_FEATURE_SECURE_FIRMWARE := n +CONFIG_WLAN_FEATURE_PACKET_FILTERING := y +CONFIG_WLAN_NS_OFFLOAD := y +CONFIG_FEATURE_WLAN_RA_FILTERING:= y +ifeq ($(CONFIG_POWER_MANAGEMENT_OFFLOAD), y) +CONFIG_FEATURE_WLAN_LPHB := y +endif +CONFIG_FEATURE_WLAN_EXTSCAN := n +CONFIG_MCL := y +CONFIG_REG_CLIENT := y +CONFIG_WLAN_OFFLOAD_PACKETS := y +CONFIG_WLAN_SYNC_TSF := y +CONFIG_WLAN_FEATURE_DISA := n +CONFIG_WLAN_FEATURE_FIPS := y +CONFIG_WLAN_FEATURE_SAE := y +CONFIG_CHNL_MATRIX_RESTRICTION := n +CONFIG_WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY := y +CONFIG_WLAN_FEATURE_BMI := n +# Enable FW stats version 2 +CONFIG_AR900B := y + +#Flags to enable/disable vendor commands +CONFIG_FEATURE_RSSI_MONITOR := n +CONFIG_FEATURE_BSS_TRANSITION := n +CONFIG_FEATURE_STATION_INFO := n +CONFIG_FEATURE_TX_POWER := n +CONFIG_FEATURE_OTA_TEST := n +CONFIG_FEATURE_ACTIVE_TOS := n +CONFIG_FEATURE_SAR_LIMITS := y +CONFIG_FEATURE_CONCURRENCY_MATRIX := n +CONFIG_FEATURE_SAP_COND_CHAN_SWITCH := n +CONFIG_FEATURE_P2P_LISTEN_OFFLOAD := n + +#Flags to enable/disable WMI APIs +CONFIG_WMI_ROAM_SUPPORT := y +CONFIG_WMI_CONCURRENCY_SUPPORT := y +CONFIG_WMI_STA_SUPPORT := y + +ifeq ($(CONFIG_ARCH_SDM845), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif + +#Data Path specific features +CONFIG_TX_CREDIT_RECLAIM_SUPPORT := n +CONFIG_CHECKSUM_OFFLOAD := y +CONFIG_QCA_SUPPORT_TX_THROTTLE := y + +ifeq ($(CONFIG_POWER_MANAGEMENT_OFFLOAD), y) +CONFIG_GTK_OFFLOAD := y +endif +CONFIG_QCACLD_FEATURE_APF := y + +#Flag to enable SARv1 -> SARv2 conversion +CONFIG_WLAN_FEATURE_SARV1_TO_SARV2 := n + +ifneq ($(DEVELOPER_DISABLE_BUILD_TIMESTAMP), y) +ifneq ($(WLAN_DISABLE_BUILD_TAG), y) +CONFIG_BUILD_TAG := y +endif +endif + +#Buffer allocations dynamically +ifeq ($(CONFIG_WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY), y) +CONFIG_WLAN_DFS_STATIC_MEM_ALLOC := n +endif + +# WoW filer configs +CONFIG_CFG_PMO_WOW_FILTERS_MAX := 16 + +# Offload configs +CONFIG_CFG_GTK_OFFLOAD_MAX_VDEV := 2 +CONFIG_CFG_ROAM_OFFLOAD_MAX_VDEV := 1 + +# Max Periodic Tx Pattern Config +CONFIG_CFG_MAX_PERIODIC_TX_PTRNS := 2 + +# Max Sta Vdev Config +CONFIG_CFG_MAX_STA_VDEVS := 2 + +# Additional peers sent to firmware +CONFIG_CFG_NUM_OF_ADDITIONAL_FW_PEERS := 0 + +# Number of TDLS peers that each Tdls vdev can track +CONFIG_CFG_NUM_OF_TDLS_CONN_TABLE_ENTRIES := 4 + +# Number of vdevs supported at one time, used for allocating memory +CONFIG_WLAN_MAX_VDEVS := 3 + +#Number of STA sessions max connected to our SAP, used for allocating memory +#should never be less then number of max peers - INI +CONFIG_SIR_SAP_MAX_NUM_PEERS := 10 + +#Max no of offloaded beaconing entities supported +CONFIG_BEACON_TX_OFFLOAD_MAX_VDEV := 3 + +#Beacon offload config +CONFIG_WMI_BCN_OFFLOAD := y + +#Flag to enable Supported Operating class +CONFIG_HOST_OPCLASS := y + +#Enable STATE MACHINE HISTORY +CONFIG_SM_ENG_HIST := n + +ifeq (y,$(findstring y,$(CONFIG_ARCH_MSM) $(CONFIG_ARCH_QCOM) $(CONFIG_QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK))) +CONFIG_WLAN_FEATURE_DP_BUS_BANDWIDTH := y +endif + +CONFIG_SAP_DHCP_FW_IND := n + +#Enable FW Offload +CONFIG_WLAN_FW_OFFLOAD := y + +################################### diff --git a/drivers/staging/qcacld-3.0/configs/genoa.pci.debug_defconfig b/drivers/staging/qcacld-3.0/configs/genoa.pci.debug_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..b5e415ef924e864ebd9c9bd6fe438147e2176123 --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/genoa.pci.debug_defconfig @@ -0,0 +1,131 @@ + +include $(WLAN_ROOT)/configs/genoa.common + +CONFIG_ROME_PCIE := n + +# Interface specific features +CONFIG_ROME_IF = pci +CONFIG_QMI_SUPPORT := y +CONFIG_HIF_PCI := y +CONFIG_EXT_WOW := y + +ifeq ($(CONFIG_PCI_MSM), y) +CONFIG_FEATURE_WLAN_D0WOW := y +endif + +# Data Path specific features +CONFIG_WLAN_FASTPATH := y +CONFIG_WLAN_NAPI := y +CONFIG_ATH_11AC_TXCOMPACT := y +CONFIG_FEATURE_MONITOR_MODE_SUPPORT := y +CONFIG_QCA_LL_TX_FLOW_CONTROL_RESIZE := y +CONFIG_LL_DP_SUPPORT := y +CONFIG_DATA_CE_SW_INDEX_NO_INLINE_UPDATE := y + +ifeq ($(CONFIG_IPA), y) +CONFIG_IPA_OFFLOAD := y +endif +ifeq ($(CONFIG_IPA3), y) +CONFIG_IPA_OFFLOAD := y +endif + +# Debug specific features +CONFIG_MPC_UT_FRAMEWORK := y +CONFIG_FEATURE_EPPING := y +CONFIG_WLAN_NAPI_DEBUG := n +CONFIG_WLAN_FEATURE_P2P_DEBUG := y +CONFIG_WLAN_LOG_ENTER := y +CONFIG_WLAN_LOG_EXIT := y + +# Features gets enabled on slub debug +CONFIG_FEATURE_PKTLOG := y +CONFIG_WLAN_DEBUG_CRASH_INJECT := y +CONFIG_FEATURE_MEMDUMP_ENABLE := y + +ifeq ($(CONFIG_DEBUG_FS), y) + CONFIG_WLAN_DEBUGFS := y + CONFIG_WLAN_POWER_DEBUGFS := y +endif + +ifeq ($(CONFIG_NETWORK_PHY_TIMESTAMPING), y) + CONFIG_WLAN_SYNC_TSF_PLUS := y + CONFIG_WLAN_SYNC_TSF_PTP := y + CONFIG_WLAN_SYNC_TSF_PLUS_EXT_GPIO_IRQ := y +endif + +ifeq ($(CONFIG_WLAN_DEBUGFS), y) + CONFIG_WLAN_FEATURE_MIB_STATS := y +endif + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) +CONFIG_DESC_DUP_DETECT_DEBUG := y +CONFIG_DEBUG_RX_RING_BUFFER := y +CONFIG_WLAN_OBJMGR_DEBUG:= y +CONFIG_FEATURE_UNIT_TEST_SUSPEND := y +CONFIG_LEAK_DETECTION := y +endif + +# Genoa features vs Rome PCIe +ifeq ($(CONFIG_ROME_PCIE), y) +CONFIG_CHNL_MATRIX_RESTRICTION := y +CONFIG_WLAN_TX_FLOW_CONTROL_LEGACY := y +else +CONFIG_OL_RX_INDICATION_RECORD := y +CONFIG_TSOSEG_DEBUG := y +CONFIG_FEATURE_TSO_DEBUG := y +CONFIG_FEATURE_TSO := y +CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y +CONFIG_HELIUMPLUS := y +CONFIG_64BIT_PADDR := y +CONFIG_WIFI_3_0_ADRASTEA := y +CONFIG_ADRASTEA_RRI_ON_DDR := y +CONFIG_ATH_PROCFS_DIAG_SUPPORT := y +CONFIG_ADRASTEA_SHADOW_REGISTERS := y +CONFIG_HTT_PADDR64 := y +CONFIG_RX_OL := y + +CONFIG_QCN7605_SUPPORT := y +CONFIG_HIF_REG_WINDOW_SUPPORT := y + +ifneq ($(CONFIG_X86), y) +ifeq ($(CONFIG_INET_LRO), y) +CONFIG_WLAN_LRO := y +else +CONFIG_WLAN_LRO := n +endif +endif + +ifneq ($(CONFIG_FORCE_ALLOC_FROM_DMA_ZONE), y) +CONFIG_ENABLE_DEBUG_ADDRESS_MARKING := y +endif + +endif # CONFIG_ROME_PCIE + +ifeq ($(CONFIG_CNSS), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +endif +endif + +ifeq ($(CONFIG_CNSS2), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +ifeq ($(CONFIG_CNSS2), m) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +CONFIG_QCA_WIFI_FTM := y +ifeq ($(CONFIG_NL80211_TESTMODE), y) + QCA_WIFI_FTM_NL80211 :=y +else + QCA_WIFI_FTM_NL80211 :=n +endif +CONFIG_LINUX_QCMBR :=y +################################### diff --git a/drivers/staging/qcacld-3.0/configs/genoa.pci.perf_defconfig b/drivers/staging/qcacld-3.0/configs/genoa.pci.perf_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..b9b0a35e0eb64e9c5c3e2f4b58e5c39ea042614d --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/genoa.pci.perf_defconfig @@ -0,0 +1,130 @@ + +include $(WLAN_ROOT)/configs/genoa.common + +CONFIG_ROME_PCIE := n + +# Interface specific features +CONFIG_ROME_IF = pci +CONFIG_QMI_SUPPORT := y +CONFIG_HIF_PCI := y +CONFIG_EXT_WOW := y + +ifeq ($(CONFIG_PCI_MSM), y) +CONFIG_FEATURE_WLAN_D0WOW := y +endif + +# Data Path specific features +CONFIG_WLAN_FASTPATH := y +CONFIG_WLAN_NAPI := y +CONFIG_ATH_11AC_TXCOMPACT := y +CONFIG_QCA_LL_TX_FLOW_CONTROL_RESIZE := y +CONFIG_LL_DP_SUPPORT := y +CONFIG_DATA_CE_SW_INDEX_NO_INLINE_UPDATE := y + +ifeq ($(CONFIG_IPA), y) +CONFIG_IPA_OFFLOAD := y +endif +ifeq ($(CONFIG_IPA3), y) +CONFIG_IPA_OFFLOAD := y +endif + +# Debug specific features +CONFIG_MPC_UT_FRAMEWORK := n +CONFIG_FEATURE_EPPING := n +CONFIG_WLAN_NAPI_DEBUG := n +CONFIG_WLAN_FEATURE_P2P_DEBUG := n +CONFIG_WLAN_LOG_ENTER := n +CONFIG_WLAN_LOG_EXIT := n + +# Features gets enabled on slub debug +CONFIG_FEATURE_PKTLOG := y +CONFIG_WLAN_DEBUG_CRASH_INJECT := n +CONFIG_FEATURE_MEMDUMP_ENABLE := n + +ifeq ($(CONFIG_DEBUG_FS), y) + CONFIG_WLAN_DEBUGFS := n + CONFIG_WLAN_POWER_DEBUGFS := n +endif + +ifeq ($(CONFIG_NETWORK_PHY_TIMESTAMPING), y) + CONFIG_WLAN_SYNC_TSF_PLUS := y + CONFIG_WLAN_SYNC_TSF_PTP := y + CONFIG_WLAN_SYNC_TSF_PLUS_EXT_GPIO_IRQ := y +endif + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) +CONFIG_DESC_DUP_DETECT_DEBUG := n +CONFIG_DEBUG_RX_RING_BUFFER := n +CONFIG_WLAN_OBJMGR_DEBUG:= n +CONFIG_FEATURE_UNIT_TEST_SUSPEND := n +CONFIG_LEAK_DETECTION := n +endif + +# Genoa features vs Rome PCIe +ifeq ($(CONFIG_ROME_PCIE), y) +CONFIG_CHNL_MATRIX_RESTRICTION := y +CONFIG_WLAN_TX_FLOW_CONTROL_LEGACY := y +else +CONFIG_OL_RX_INDICATION_RECORD := y +CONFIG_TSOSEG_DEBUG := n +CONFIG_FEATURE_TSO_DEBUG := n +CONFIG_FEATURE_TSO := y +CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y +CONFIG_HELIUMPLUS := y +CONFIG_64BIT_PADDR := y +CONFIG_WIFI_3_0_ADRASTEA := y +CONFIG_ADRASTEA_RRI_ON_DDR := y +CONFIG_ATH_PROCFS_DIAG_SUPPORT := y +CONFIG_ADRASTEA_SHADOW_REGISTERS := y +CONFIG_HTT_PADDR64 := y +CONFIG_RX_OL := y + +CONFIG_QCN7605_SUPPORT := y +CONFIG_HIF_REG_WINDOW_SUPPORT := y + +ifneq ($(CONFIG_X86), y) +ifeq ($(CONFIG_INET_LRO), y) +CONFIG_WLAN_LRO := y +else +CONFIG_WLAN_LRO := n +endif +endif + +ifneq ($(CONFIG_FORCE_ALLOC_FROM_DMA_ZONE), y) +CONFIG_ENABLE_DEBUG_ADDRESS_MARKING := y +endif + +endif # CONFIG_ROME_PCIE + +ifeq ($(CONFIG_CNSS), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +endif +endif + +ifeq ($(CONFIG_CNSS2), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +CONFIG_QCA_WIFI_FTM := n +QCA_WIFI_FTM_NL80211 := n +CONFIG_LINUX_QCMBR := n + +################################### +CONFIG_ENABLE_SIZE_OPTIMIZE := y + +# configure log buffer size +CONFIG_CFG_NUM_DP_TRACE_RECORD := 1000 +CONFIG_CFG_NUM_HTC_CREDIT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_MGMT_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_TX_RX_HISTOGRAM := 16 +CONFIG_CFG_NUM_RX_IND_RECORD := 1024 + +ifeq ($(CONFIG_FEATURE_ROAM_DEBUG), y) +CONFIG_CFG_NUM_ROAM_DEBUG_RECORD := 64 +endif +################################### diff --git a/drivers/staging/qcacld-3.0/configs/genoa.sdio.debug_defconfig b/drivers/staging/qcacld-3.0/configs/genoa.sdio.debug_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..bc5038ece82f9cc3722d0515baa363824d5bb4b3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/genoa.sdio.debug_defconfig @@ -0,0 +1,61 @@ + +include $(WLAN_ROOT)/configs/genoa.common + +# Interface specific features +CONFIG_CLD_HL_SDIO_CORE := y +CONFIG_QCA_WIFI_SDIO := y +CONFIG_ROME_IF = sdio +CONFIG_HIF_SDIO := y +CONFIG_LINUX_QCMBR := y +CONFIG_SDIO_TRANSFER = adma +CONFIG_PLD_SDIO_CNSS2 := y + +# Data Path specific features +CONFIG_ATH_11AC_TXCOMPACT := n +CONFIG_QCA_HL_NETDEV_FLOW_CONTROL := y +CONFIG_TX_RESOURCE_HIGH_TH_IN_PER := 8 +CONFIG_TX_RESOURCE_LOW_TH_IN_PER := 2 +CONFIG_FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL := y +CONFIG_FEATURE_HL_DBS_GROUP_CREDIT_SHARING := y +CONFIG_CREDIT_REP_THROUGH_CREDIT_UPDATE := y +CONFIG_HL_DP_SUPPORT := y + +# Debug specific features +CONFIG_MPC_UT_FRAMEWORK := y +CONFIG_FEATURE_EPPING := y +CONFIG_WLAN_FEATURE_P2P_DEBUG := y +CONFIG_WLAN_LOG_ENTER := y +CONFIG_WLAN_LOG_EXIT := y + +ifeq ($(CONFIG_DEBUG_FS), y) + CONFIG_WLAN_DEBUGFS := y + CONFIG_WLAN_POWER_DEBUGFS := y +endif + +# Features gets enabled on slub debug +CONFIG_FEATURE_PKTLOG := y +CONFIG_WLAN_DEBUG_CRASH_INJECT := y +CONFIG_FEATURE_MEMDUMP_ENABLE := y + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) +CONFIG_WLAN_OBJMGR_DEBUG:= y +CONFIG_FEATURE_UNIT_TEST_SUSPEND := y +CONFIG_LEAK_DETECTION := y +endif + +# Genoa features vs Rome +CONFIG_HTT_PADDR64 := y + +CONFIG_QCA_WIFI_FTM := y +ifeq ($(CONFIG_NL80211_TESTMODE), y) + QCA_WIFI_FTM_NL80211 :=y +else + QCA_WIFI_FTM_NL80211 :=n +endif +CONFIG_LINUX_QCMBR :=y +CONFIG_TGT_NUM_MSDU_DESC := 0 +CONFIG_RX_PN_CHECK_OFFLOAD := y +CONFIG_QCN7605_SUPPORT := y +CONFIG_QCA_TX_PADDING_CREDIT_SUPPORT := y +CONFIG_CFG_TGT_AST_SKID_LIMIT := 16 +################################### diff --git a/drivers/staging/qcacld-3.0/configs/genoa.sdio.perf_defconfig b/drivers/staging/qcacld-3.0/configs/genoa.sdio.perf_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..7faff21bd5519fd586661bb02c3aba879622d8c6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/genoa.sdio.perf_defconfig @@ -0,0 +1,66 @@ + +include $(WLAN_ROOT)/configs/genoa.common + +# Interface specific features +CONFIG_CLD_HL_SDIO_CORE := y +CONFIG_QCA_WIFI_SDIO := y +CONFIG_ROME_IF = sdio +CONFIG_HIF_SDIO := y +CONFIG_LINUX_QCMBR := y +CONFIG_SDIO_TRANSFER = adma +CONFIG_PLD_SDIO_CNSS2 := y + +# Data Path specific features +CONFIG_ATH_11AC_TXCOMPACT := n +CONFIG_QCA_HL_NETDEV_FLOW_CONTROL := y +CONFIG_TX_RESOURCE_HIGH_TH_IN_PER := 8 +CONFIG_TX_RESOURCE_LOW_TH_IN_PER := 2 +CONFIG_FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL := y +CONFIG_FEATURE_HL_DBS_GROUP_CREDIT_SHARING := y +CONFIG_CREDIT_REP_THROUGH_CREDIT_UPDATE := y +CONFIG_HL_DP_SUPPORT := y + +# Debug specific features +CONFIG_MPC_UT_FRAMEWORK := n +CONFIG_FEATURE_EPPING := n +CONFIG_WLAN_FEATURE_P2P_DEBUG := n +CONFIG_WLAN_LOG_ENTER := n +CONFIG_WLAN_LOG_EXIT := n + +# Features gets enabled on slub debug +CONFIG_FEATURE_PKTLOG := y +CONFIG_WLAN_DEBUG_CRASH_INJECT := n +CONFIG_FEATURE_MEMDUMP_ENABLE := n + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) +CONFIG_WLAN_OBJMGR_DEBUG:= n +CONFIG_FEATURE_UNIT_TEST_SUSPEND := n +CONFIG_LEAK_DETECTION := n +endif + +# Genoa features vs Rome +CONFIG_HTT_PADDR64 := y + +CONFIG_ENABLE_SIZE_OPTIMIZE := y + +# configure log buffer size +CONFIG_CFG_NUM_DP_TRACE_RECORD := 1000 +CONFIG_CFG_NUM_HTC_CREDIT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_MGMT_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_TX_RX_HISTOGRAM := 16 +CONFIG_CFG_NUM_RX_IND_RECORD := 1024 + +ifeq ($(CONFIG_FEATURE_ROAM_DEBUG), y) +CONFIG_CFG_NUM_ROAM_DEBUG_RECORD := 64 +endif + +CONFIG_QCA_WIFI_FTM := n +QCA_WIFI_FTM_NL80211 := n +CONFIG_LINUX_QCMBR := n +CONFIG_TGT_NUM_MSDU_DESC := 0 +CONFIG_RX_PN_CHECK_OFFLOAD := y +CONFIG_QCN7605_SUPPORT := y +CONFIG_QCA_TX_PADDING_CREDIT_SUPPORT := y +CONFIG_CFG_TGT_AST_SKID_LIMIT := 16 +################################### diff --git a/drivers/staging/qcacld-3.0/configs/genoa.snoc.debug_defconfig b/drivers/staging/qcacld-3.0/configs/genoa.snoc.debug_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..00193403998cab07197e18afa89aa585dd4d160f --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/genoa.snoc.debug_defconfig @@ -0,0 +1,88 @@ + +include $(WLAN_ROOT)/configs/genoa.common + +# Interface specific features +CONFIG_ROME_IF = snoc +CONFIG_HELIUMPLUS := y +CONFIG_64BIT_PADDR := y +CONFIG_QMI_SUPPORT := y +CONFIG_WIFI_3_0_ADRASTEA := y +CONFIG_ADRASTEA_RRI_ON_DDR := y +CONFIG_ATH_PROCFS_DIAG_SUPPORT := y +CONFIG_ADRASTEA_SHADOW_REGISTERS := y +CONFIG_HTT_PADDR64 := y +CONFIG_HIF_SNOC:= y + +# Genoa specific features +CONFIG_QCA_LL_TX_FLOW_CONTROL_RESIZE := y + +# Data Path specific features +CONFIG_WLAN_FASTPATH := y +CONFIG_FEATURE_TSO := y +CONFIG_WLAN_NAPI := y +CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y +CONFIG_ATH_11AC_TXCOMPACT := y +CONFIG_RX_OL := y +CONFIG_DESC_DUP_DETECT_DEBUG := y +CONFIG_DEBUG_RX_RING_BUFFER := y +CONFIG_LL_DP_SUPPORT := y + +ifeq ($(CONFIG_INET_LRO), y) +CONFIG_WLAN_LRO := y +else +CONFIG_WLAN_LRO := n +endif + +ifeq ($(CONFIG_IPA), y) +CONFIG_IPA_OFFLOAD := y +endif +ifeq ($(CONFIG_IPA3), y) +CONFIG_IPA_OFFLOAD := y +endif + +ifneq ($(CONFIG_FORCE_ALLOC_FROM_DMA_ZONE), y) +CONFIG_ENABLE_DEBUG_ADDRESS_MARKING := y +endif + +# Debug specific features +CONFIG_FEATURE_TSO_DEBUG := y +CONFIG_WLAN_NAPI_DEBUG := n +CONFIG_WLAN_FEATURE_P2P_DEBUG := y +CONFIG_MPC_UT_FRAMEWORK := y +CONFIG_FEATURE_EPPING := y +CONFIG_WLAN_LOG_ENTER := y +CONFIG_WLAN_LOG_EXIT := y + +# Features gets enabled on slub debug +CONFIG_WLAN_OBJMGR_DEBUG:= y +CONFIG_OL_RX_INDICATION_RECORD := y +CONFIG_TSOSEG_DEBUG := y +CONFIG_FEATURE_PKTLOG := y +CONFIG_WLAN_DEBUG_CRASH_INJECT := y +CONFIG_FEATURE_MEMDUMP_ENABLE := y +CONFIG_FEATURE_UNIT_TEST_SUSPEND := y +CONFIG_LEAK_DETECTION := y + +ifeq ($(CONFIG_DEBUG_FS), y) + CONFIG_WLAN_DEBUGFS := y + CONFIG_WLAN_POWER_DEBUGFS := y +endif + +# other features +CONFIG_QCA_WIFI_FTM := y +ifeq ($(CONFIG_NL80211_TESTMODE), y) + QCA_WIFI_FTM_NL80211 :=y +else + QCA_WIFI_FTM_NL80211 :=n +endif +CONFIG_LINUX_QCMBR :=y + +#Flag to enable Dynamic Voltage WDCVS (Config Voltage Mode) +CONFIG_WLAN_DYNAMIC_CVM := y +ifeq ($(CONFIG_CNSS2), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif +################################### diff --git a/drivers/staging/qcacld-3.0/configs/genoa.snoc.perf_defconfig b/drivers/staging/qcacld-3.0/configs/genoa.snoc.perf_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..800c77405dff01196c6135fb443007bc0427556b --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/genoa.snoc.perf_defconfig @@ -0,0 +1,97 @@ + +include $(WLAN_ROOT)/configs/genoa.common + +# Interface specific features +CONFIG_ROME_IF = snoc +CONFIG_HELIUMPLUS := y +CONFIG_64BIT_PADDR := y +CONFIG_QMI_SUPPORT := y +CONFIG_WIFI_3_0_ADRASTEA := y +CONFIG_ADRASTEA_RRI_ON_DDR := y +CONFIG_ATH_PROCFS_DIAG_SUPPORT := y +CONFIG_ADRASTEA_SHADOW_REGISTERS := y +CONFIG_HTT_PADDR64 := y +CONFIG_HIF_SNOC:= y + +# Genoa specific features +CONFIG_QCA_LL_TX_FLOW_CONTROL_RESIZE := y + +# Data Path specific features +CONFIG_WLAN_FASTPATH := y +CONFIG_FEATURE_TSO := y +CONFIG_WLAN_NAPI := y +CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y +CONFIG_ATH_11AC_TXCOMPACT := y +CONFIG_RX_OL := y +CONFIG_DESC_DUP_DETECT_DEBUG := n +CONFIG_DEBUG_RX_RING_BUFFER := n +CONFIG_LL_DP_SUPPORT := y + +ifeq ($(CONFIG_INET_LRO), y) +CONFIG_WLAN_LRO := y +else +CONFIG_WLAN_LRO := n +endif + +ifeq ($(CONFIG_IPA), y) +CONFIG_IPA_OFFLOAD := y +endif +ifeq ($(CONFIG_IPA3), y) +CONFIG_IPA_OFFLOAD := y +endif + +ifneq ($(CONFIG_FORCE_ALLOC_FROM_DMA_ZONE), y) +CONFIG_ENABLE_DEBUG_ADDRESS_MARKING := y +endif + +CONFIG_FEATURE_TSO_DEBUG := n +CONFIG_WLAN_NAPI_DEBUG := n +CONFIG_WLAN_FEATURE_P2P_DEBUG := n +CONFIG_MPC_UT_FRAMEWORK := n +CONFIG_FEATURE_EPPING := n +CONFIG_WLAN_LOG_ENTER := n +CONFIG_WLAN_LOG_EXIT := n + +CONFIG_QCA_WIFI_FTM := n +QCA_WIFI_FTM_NL80211 := n +CONFIG_LINUX_QCMBR := n + +#Flag to enable Dynamic Voltage WDCVS (Config Voltage Mode) +CONFIG_WLAN_DYNAMIC_CVM := y + +# Features gets enabled on slub debug +CONFIG_WLAN_OBJMGR_DEBUG:= n +CONFIG_FEATURE_MEMDUMP_ENABLE := n +CONFIG_FEATURE_UNIT_TEST_SUSPEND := n +CONFIG_OL_RX_INDICATION_RECORD := n +CONFIG_TSOSEG_DEBUG := n +CONFIG_FEATURE_PKTLOG := n +CONFIG_WLAN_DEBUG_CRASH_INJECT := n +CONFIG_LEAK_DETECTION := n + +ifeq ($(CONFIG_DEBUG_FS), y) + CONFIG_WLAN_DEBUGFS := n + CONFIG_WLAN_POWER_DEBUGFS := n +endif + +ifeq ($(CONFIG_CNSS2), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +CONFIG_ENABLE_SIZE_OPTIMIZE := y + +# configure log buffer size +CONFIG_CFG_NUM_DP_TRACE_RECORD := 1000 +CONFIG_CFG_NUM_HTC_CREDIT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_MGMT_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_TX_RX_HISTOGRAM := 16 +CONFIG_CFG_NUM_RX_IND_RECORD := 1024 + +ifeq ($(CONFIG_FEATURE_ROAM_DEBUG), y) +CONFIG_CFG_NUM_ROAM_DEBUG_RECORD := 64 +endif +################################### diff --git a/drivers/staging/qcacld-3.0/configs/genoa.usb.debug_defconfig b/drivers/staging/qcacld-3.0/configs/genoa.usb.debug_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..9c9ff3b2f83c03f69faf45eb1af2f4d0ff67f53b --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/genoa.usb.debug_defconfig @@ -0,0 +1,70 @@ + +include $(WLAN_ROOT)/configs/genoa.common + +# Interface specific features +CONFIG_ROME_IF := usb +CONFIG_HIF_USB := y +CONFIG_LINUX_QCMBR := y +CONFIG_PLD_USB_CNSS := y + +# Data Path specific features +CONFIG_ATH_11AC_TXCOMPACT := n +CONFIG_QCA_HL_NETDEV_FLOW_CONTROL := y +CONFIG_TX_RESOURCE_HIGH_TH_IN_PER := 8 +CONFIG_TX_RESOURCE_LOW_TH_IN_PER := 2 +CONFIG_FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL := y +CONFIG_FEATURE_HL_DBS_GROUP_CREDIT_SHARING := y +CONFIG_CREDIT_REP_THROUGH_CREDIT_UPDATE := y +CONFIG_WLAN_SUPPORT_TXRX_HL_BUNDLE := y +CONFIG_HL_DP_SUPPORT := y + +# Enable Motion Detection Feature +CONFIG_FEATURE_MOTION_DETECTION := y + +# Debug specific features +CONFIG_MPC_UT_FRAMEWORK := y +CONFIG_FEATURE_EPPING := y +CONFIG_WLAN_FEATURE_P2P_DEBUG := y +CONFIG_WLAN_LOG_ENTER := y +CONFIG_WLAN_LOG_EXIT := y + +ifeq ($(CONFIG_DEBUG_FS), y) + CONFIG_WLAN_DEBUGFS := y + CONFIG_WLAN_POWER_DEBUGFS := y +endif + +ifeq ($(CONFIG_NETWORK_PHY_TIMESTAMPING), y) + CONFIG_WLAN_SYNC_TSF_PTP := y +endif + +CONFIG_WLAN_SYNC_TSF_PLUS := y +CONFIG_WLAN_SYNC_TSF_PLUS_EXT_GPIO_SYNC := y + +# Features gets enabled on slub debug +CONFIG_WLAN_DEBUG_CRASH_INJECT := y +CONFIG_FEATURE_MEMDUMP_ENABLE := y + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) +CONFIG_WLAN_OBJMGR_DEBUG:= y +CONFIG_FEATURE_UNIT_TEST_SUSPEND := y +CONFIG_LEAK_DETECTION := y +endif + +# Genoa features vs Rome +CONFIG_RX_PN_CHECK_OFFLOAD := y +CONFIG_HTT_PADDR64 := y + +CONFIG_QCA_WIFI_FTM := y +ifeq ($(CONFIG_NL80211_TESTMODE), y) + QCA_WIFI_FTM_NL80211 :=y +else + QCA_WIFI_FTM_NL80211 :=n +endif +CONFIG_LINUX_QCMBR :=y +CONFIG_QCN7605_SUPPORT := y +CONFIG_TGT_NUM_MSDU_DESC := 0 +# For OOB wakeup +CONFIG_WLAN_FEATURE_WOW_PULSE := y +CONFIG_HTC_MAX_MSG_PER_BUNDLE_TX := 48 +CONFIG_CFG_TGT_AST_SKID_LIMIT := 16 +################################### diff --git a/drivers/staging/qcacld-3.0/configs/genoa.usb.perf_defconfig b/drivers/staging/qcacld-3.0/configs/genoa.usb.perf_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..2fc9780f327b190cb30a1241d4ef901c9f24c7f3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/genoa.usb.perf_defconfig @@ -0,0 +1,80 @@ + +include $(WLAN_ROOT)/configs/genoa.common + +# Interface specific features +CONFIG_ROME_IF := usb +CONFIG_HIF_USB := y +CONFIG_LINUX_QCMBR := y +CONFIG_PLD_USB_CNSS := y + +# Data Path specific features +CONFIG_ATH_11AC_TXCOMPACT := n +CONFIG_QCA_HL_NETDEV_FLOW_CONTROL := y +CONFIG_TX_RESOURCE_HIGH_TH_IN_PER := 8 +CONFIG_TX_RESOURCE_LOW_TH_IN_PER := 2 +CONFIG_FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL := y +CONFIG_FEATURE_HL_DBS_GROUP_CREDIT_SHARING := y +CONFIG_CREDIT_REP_THROUGH_CREDIT_UPDATE := y +CONFIG_WLAN_SUPPORT_TXRX_HL_BUNDLE := y +CONFIG_HL_DP_SUPPORT := y + +# Enable Motion Detection Feature +CONFIG_FEATURE_MOTION_DETECTION := y + +# Debug specific features +CONFIG_MPC_UT_FRAMEWORK := n +CONFIG_FEATURE_EPPING := n +CONFIG_WLAN_FEATURE_P2P_DEBUG := n +CONFIG_WLAN_LOG_ENTER := n +CONFIG_WLAN_LOG_EXIT := n + +ifeq ($(CONFIG_DEBUG_FS), y) + CONFIG_WLAN_DEBUGFS := y + CONFIG_WLAN_POWER_DEBUGFS := y +endif + +# Features gets enabled on slub debug +CONFIG_WLAN_DEBUG_CRASH_INJECT := n +CONFIG_FEATURE_MEMDUMP_ENABLE := n + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) +CONFIG_WLAN_OBJMGR_DEBUG:= n +CONFIG_FEATURE_UNIT_TEST_SUSPEND := n +CONFIG_LEAK_DETECTION := n +endif + +ifeq ($(CONFIG_NETWORK_PHY_TIMESTAMPING), y) + CONFIG_WLAN_SYNC_TSF_PTP := y +endif + +CONFIG_WLAN_SYNC_TSF_PLUS := y +CONFIG_WLAN_SYNC_TSF_PLUS_EXT_GPIO_SYNC := y + +CONFIG_ENABLE_SIZE_OPTIMIZE := y + +# configure log buffer size +CONFIG_CFG_NUM_DP_TRACE_RECORD := 1000 +CONFIG_CFG_NUM_HTC_CREDIT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_MGMT_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_TX_RX_HISTOGRAM := 16 +CONFIG_CFG_NUM_RX_IND_RECORD := 1024 + +ifeq ($(CONFIG_FEATURE_ROAM_DEBUG), y) +CONFIG_CFG_NUM_ROAM_DEBUG_RECORD := 64 +endif + +# Genoa features vs Rome +CONFIG_RX_PN_CHECK_OFFLOAD := y +CONFIG_HTT_PADDR64 := y + +CONFIG_QCA_WIFI_FTM := n +QCA_WIFI_FTM_NL80211 := n +CONFIG_LINUX_QCMBR := n +CONFIG_QCN7605_SUPPORT := y +CONFIG_TGT_NUM_MSDU_DESC := 0 +# For OOB wakeup +CONFIG_WLAN_FEATURE_WOW_PULSE := y +CONFIG_HTC_MAX_MSG_PER_BUNDLE_TX := 48 +CONFIG_CFG_TGT_AST_SKID_LIMIT := 16 +################################### diff --git a/drivers/staging/qcacld-3.0/configs/qca6174_defconfig b/drivers/staging/qcacld-3.0/configs/qca6174_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..ebca507edc941212b3f711d6096a2a5777b83e1f --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/qca6174_defconfig @@ -0,0 +1,752 @@ +CONFIG_AR6320_SUPPORT := y + +ifeq ($(CONFIG_CLD_HL_SDIO_CORE), y) + CONFIG_QCA_WIFI_SDIO := y +ifndef CONFIG_SDIO_TRANSFER + CONFIG_SDIO_TRANSFER = mailbox +endif +endif + +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) + CONFIG_ROME_IF = sdio +endif + +ifdef CONFIG_ICNSS + CONFIG_ROME_IF = snoc +endif + +ifeq (y,$(findstring y,$(CONFIG_CNSS) $(CONFIG_CNSS2))) +ifndef CONFIG_ROME_IF + #use pci as default interface + CONFIG_ROME_IF = pci +endif +endif + +ifeq (m,$(findstring m,$(CONFIG_CNSS) $(CONFIG_CNSS2))) +ifndef CONFIG_ROME_IF + #use pci as default interface + CONFIG_ROME_IF = pci +endif +endif + +# Enable FCC TYPE4 DURATION CHECK +CONFIG_DFS_FCC_TYPE4_DURATION_CHECK := y + +# Make WLAN as open-source driver by default +WLAN_OPEN_SOURCE := y + +ifeq ($(CONFIG_ICNSS), y) + CONFIG_HELIUMPLUS := y + CONFIG_64BIT_PADDR := y + CONFIG_FEATURE_TSO := y + CONFIG_FEATURE_TSO_DEBUG := y + ifeq ($(CONFIG_INET_LRO), y) + CONFIG_WLAN_LRO := y + else + CONFIG_WLAN_LRO := n + endif +endif + +ifneq ($(DEVELOPER_DISABLE_BUILD_TIMESTAMP), y) +ifneq ($(WLAN_DISABLE_BUILD_TAG), y) +CONFIG_BUILD_TAG := y +endif +endif + +ifeq ($(CONFIG_ARCH_MDM9630), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_MDM9640), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_SDX20), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_SDXPRAIRIE), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_MSM8917), y) + ifeq ($(CONFIG_ROME_IF), sdio) + CONFIG_WLAN_SYNC_TSF_PLUS := y + endif +endif + +ifeq ($(CONFIG_ARCH_QCS405), y) + CONFIG_WLAN_SYNC_TSF_PLUS := y + CONFIG_WLAN_SYNC_TSF_PLUS_NOIRQ := y +endif + +#Flag to enable Legacy Fast Roaming3(LFR3) +ifeq (y,$(findstring y,$(CONFIG_HELIUMPLUS) $(CONFIG_LITHIUM))) + CONFIG_QCACLD_WLAN_LFR3 := y +else + CONFIG_QCACLD_WLAN_LFR2 := y +endif + +ifneq ($(CONFIG_MOBILE_ROUTER), y) +#Flag to enable TDLS feature +CONFIG_QCOM_TDLS := y + +CONFIG_WLAN_SYSFS := y +endif + +CONFIG_QCACLD_FEATURE_GREEN_AP := y +#Flag to enable Android Packet Filtering +CONFIG_QCACLD_FEATURE_APF := y + +#Flag to enable SARv1 -> SARv2 conversion +CONFIG_WLAN_FEATURE_SARV1_TO_SARV2 := n + +ifeq ($(CONFIG_ARCH_MSM8998), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SDM660), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SDM630), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SDM845), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SM8150), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SDM670), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SM6150), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +#Flag to enable Fast Transition (11r) feature +CONFIG_QCOM_VOWIFI_11R := y + +#Flag to enable disable ACTION OUI feature +CONFIG_WLAN_FEATURE_ACTION_OUI := y + +#Flag to enable FILS Feature (11ai) +CONFIG_WLAN_FEATURE_FILS := y +ifneq ($(CONFIG_QCA_CLD_WLAN),) + ifeq (y,$(findstring y,$(CONFIG_CNSS) $(CONFIG_CNSS2) $(CONFIG_ICNSS))) + #Flag to enable Protected Management Frames (11w) feature + CONFIG_WLAN_FEATURE_11W := y + #Flag to enable LTE CoEx feature + CONFIG_QCOM_LTE_COEX := y + ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable LPSS feature + CONFIG_WLAN_FEATURE_LPSS := y + endif + endif + + ifeq (m,$(findstring m,$(CONFIG_CNSS2))) + #Flag to enable Protected Management Frames (11w) feature + CONFIG_WLAN_FEATURE_11W := y + #Flag to enable LTE CoEx feature + CONFIG_QCOM_LTE_COEX := y + ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable LPSS feature + CONFIG_WLAN_FEATURE_LPSS := y + endif + endif +endif + +#Flag to enable Protected Management Frames (11w) feature +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_WLAN_FEATURE_11W := y +endif +ifeq ($(CONFIG_ROME_IF),sdio) + CONFIG_WLAN_FEATURE_11W := y +endif + +#Flag to enable the tx desc sanity check +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_QCA_TXDESC_SANITY_CHECKS := y +endif + +#Flag to disable NAN +CONFIG_QCACLD_FEATURE_NAN := n + +ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable NAN Data path + CONFIG_WLAN_FEATURE_NAN_DATAPATH := y + CONFIG_NAN_CONVERGENCE := y +endif + +#Flag to enable Linux QCMBR feature as default feature +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_LINUX_QCMBR :=y +endif + + +CONFIG_FEATURE_EPPING := y + +#Flag to enable offload packets feature +CONFIG_WLAN_OFFLOAD_PACKETS := y + +#enable TSF get feature +CONFIG_WLAN_SYNC_TSF := y +#Enable DSRC feature +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) +CONFIG_WLAN_FEATURE_DSRC := y +endif + +ifneq ($(CONFIG_ROME_IF),usb) + #Flag to enable SAE + CONFIG_WLAN_FEATURE_SAE := y + +ifneq ($(CONFIG_ROME_IF),sdio) + #Flag to enable DISA + CONFIG_WLAN_FEATURE_DISA := y + + #Flag to enable FIPS + CONFIG_WLAN_FEATURE_FIPS := y + + # Flag to enable NAPI + CONFIG_WLAN_NAPI := y + CONFIG_WLAN_NAPI_DEBUG := n + + # Flag to enable FW based TX Flow control + ifeq ($(CONFIG_LITHIUM), y) + CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y + else + CONFIG_WLAN_TX_FLOW_CONTROL_V2 := n + endif + +endif +endif + +CONFIG_POWER_MANAGEMENT_OFFLOAD := y + +ifeq ($(CONFIG_ROME_IF), snoc) + CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y +endif + +# Flag to enable LFR Subnet Detection +CONFIG_LFR_SUBNET_DETECTION := y + +# Flag to enable MCC to SCC switch feature +CONFIG_MCC_TO_SCC_SWITCH := y + +ifeq ($(CONFIG_SLUB_DEBUG), y) + # Enable Obj Mgr Degug services if slub build + CONFIG_WLAN_OBJMGR_DEBUG:= y + CONFIG_MPC_UT_FRAMEWORK := y + CONFIG_LOCK_STATS_ON:= y +endif + +ifeq (y,$(findstring y,$(CONFIG_QCA_WIFI_SDIO) $(CONFIG_HIF_USB))) +CONFIG_HL_DP_SUPPORT := y +else +CONFIG_LL_DP_SUPPORT := y +endif + +ifeq ($(CONFIG_ROME_IF),pci) +ifneq ($(CONFIG_WLAN_TX_FLOW_CONTROL_V2), y) +ifneq ($(CONFIG_LITHIUM), y) +CONFIG_WLAN_TX_FLOW_CONTROL_LEGACY := y +endif +endif +endif + +#Whether have QMI support +CONFIG_QMI_SUPPORT := y + +ifeq ($(CONFIG_ICNSS), y) +CONFIG_WIFI_3_0_ADRASTEA := y +CONFIG_ADRASTEA_RRI_ON_DDR := y +# Enable athdiag procfs debug support for adrastea +CONFIG_ATH_PROCFS_DIAG_SUPPORT := y +# Enable 11AC TX compact feature for adrastea +CONFIG_ATH_11AC_TXCOMPACT := y +ifeq ($(CONFIG_QMI_SUPPORT), y) +CONFIG_ADRASTEA_SHADOW_REGISTERS := y +endif +endif + +# Enable fw stats version 2 +ifeq (y,$(findstring y,$(CONFIG_HELIUMPLUS) $(CONFIG_LITHIUM))) +CONFIG_AR900B := y +endif + +# NOTE: CONFIG_64BIT_PADDR requires CONFIG_HELIUMPLUS +ifeq ($(CONFIG_HELIUMPLUS), y) + +ifeq ($(CONFIG_64BIT_PADDR), y) +CONFIG_HTT_PADDR64 := y +endif + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) +CONFIG_OL_RX_INDICATION_RECORD := y +CONFIG_TSOSEG_DEBUG := y +endif + +endif #CONFIG_HELIUMPLUS + +ifeq ($(CONFIG_LITHIUM), y) +# +# Enable Shadow V2 for all lithium platform +# +CONFIG_SHADOW_V2 := y + +CONFIG_QCA_WIFI_QCA8074 := y +CONFIG_QCA_WIFI_QCA8074_VP := y +CONFIG_DP_INTR_POLL_BASED := y +CONFIG_TX_PER_PDEV_DESC_POOL := y +CONFIG_DP_TRACE := y +CONFIG_FEATURE_TSO := y +CONFIG_TSO_DEBUG_LOG_ENABLE := y +CONFIG_DP_LFR := y +CONFIG_HTT_PADDR64 := y +CONFIG_RX_OL := y +CONFIG_TX_TID_OVERRIDE := y +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG=y + +# CONFIG_SLUB_DEBUG_ON=y + CONFIG_PAGE_POISONING=y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QCOMs internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. +ifneq ($(TARGET_BUILD_VARIANT),user) + ifeq ($(CONFIG_LITHIUM), y) + CONFIG_FEATURE_PKTLOG := n + else + CONFIG_FEATURE_PKTLOG := y + endif + CONFIG_WLAN_DEBUG_CRASH_INJECT := y +endif + +#Enable WLAN/Power debugfs feature only if debug_fs is enabled +ifeq ($(CONFIG_DEBUG_FS), y) + # Flag to enable debugfs. Depends on CONFIG_DEBUG_FS in kernel + # configuration. + CONFIG_WLAN_DEBUGFS := y + + CONFIG_WLAN_POWER_DEBUGFS := y +endif + +# Feature flags which are not (currently) configurable via Kconfig + +#Whether to build debug version +BUILD_DEBUG_VERSION := y + +#Enable this flag to build driver in diag version +BUILD_DIAG_VERSION := y + +ifeq ($(CONFIG_SLUB_DEBUG), y) + PANIC_ON_BUG := y + WLAN_WARN_ON_ASSERT := y +else ifeq ($(CONFIG_PERF_DEBUG), y) + PANIC_ON_BUG := y + WLAN_WARN_ON_ASSERT := y +else + PANIC_ON_BUG := n + WLAN_WARN_ON_ASSERT := n +endif + +# Compile all log levels by default +CONFIG_WLAN_LOG_FATAL := y +CONFIG_WLAN_LOG_ERROR := y +CONFIG_WLAN_LOG_WARN := y +CONFIG_WLAN_LOG_INFO := y +CONFIG_WLAN_LOG_DEBUG := y +CONFIG_WLAN_LOG_ENTER := y +CONFIG_WLAN_LOG_EXIT := y + +#Enable OL debug and wmi unified functions +CONFIG_ATH_PERF_PWR_OFFLOAD := y + +#Disable packet log +CONFIG_REMOVE_PKT_LOG := n + +#Enable 11AC TX +ifeq ($(CONFIG_ROME_IF),pci) + CONFIG_ATH_11AC_TXCOMPACT := y +endif + +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_ATH_11AC_TXCOMPACT := n +endif + +#Enable PCI specific APIS (dma, etc) +ifeq ($(CONFIG_ROME_IF),pci) + CONFIG_HIF_PCI := y +endif + +#Enable USB specific APIS +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_HIF_USB := y +endif + +#Enable SDIO specific APIS +ifeq ($(CONFIG_ROME_IF),sdio) + CONFIG_HIF_SDIO := y + CONFIG_TX_DESC_HI_PRIO_RESERVE := y + CONFIG_PER_VDEV_TX_DESC_POOL := y + CONFIG_FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL := y +endif + +ifeq ($(CONFIG_ROME_IF),snoc) + CONFIG_HIF_SNOC:= y +endif + +# enable/disable feature flags based upon mobile router profile +ifeq ($(CONFIG_MOBILE_ROUTER), y) +CONFIG_FEATURE_WLAN_MCC_TO_SCC_SWITCH := y +CONFIG_FEATURE_WLAN_AUTO_SHUTDOWN := y +CONFIG_FEATURE_WLAN_AP_AP_ACS_OPTIMIZE := y +CONFIG_FEATURE_WLAN_STA_4ADDR_SCHEME := y +CONFIG_MDM_PLATFORM := y +CONFIG_FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE := y +CONFIG_FEATURE_AP_MCC_CH_AVOIDANCE := y +else +CONFIG_QCOM_ESE := y +CONFIG_FEATURE_WLAN_RMC := y +CONFIG_WLAN_OPEN_P2P_INTERFACE := y +CONFIG_WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY := y +endif + +#enable 4addr support for QCS405 +ifeq ($(CONFIG_ARCH_QCS405), y) +CONFIG_FEATURE_WLAN_STA_4ADDR_SCHEME := y +endif + +#Enable power management suspend/resume functionality to PCI +CONFIG_ATH_BUS_PM := y + +#Enable FLOWMAC module support +CONFIG_ATH_SUPPORT_FLOWMAC_MODULE := n + +#Enable spectral support +CONFIG_ATH_SUPPORT_SPECTRAL := n + +#Enable WDI Event support +ifeq ($(CONFIG_REMOVE_PKT_LOG), y) +CONFIG_WDI_EVENT_ENABLE := n +else +CONFIG_WDI_EVENT_ENABLE := y +endif + +ifeq ($(CONFIG_REMOVE_PKT_LOG), n) +CONFIG_PKTLOG_LEGACY := y +endif + +#Endianness selection +CONFIG_LITTLE_ENDIAN := y + +#Enable TX reclaim support +CONFIG_TX_CREDIT_RECLAIM_SUPPORT := n + +#Enable FTM support +CONFIG_QCA_WIFI_FTM := y + +ifeq ($(CONFIG_QCA_WIFI_FTM), y) + +ifeq ($(CONFIG_NL80211_TESTMODE), y) + QCA_WIFI_FTM_NL80211 :=y +else + QCA_WIFI_FTM_NL80211 :=n +endif + CONFIG_LINUX_QCMBR :=y + +else + QCA_WIFI_FTM_NL80211 :=n + CONFIG_LINUX_QCMBR :=n +endif + + +#Enable Checksum Offload +CONFIG_CHECKSUM_OFFLOAD := y + +#Enable GTK offload +CONFIG_GTK_OFFLOAD := y + +#Enable EXT WOW +ifeq ($(CONFIG_HIF_PCI), y) + CONFIG_EXT_WOW := y +endif + +#Set this to 1 to catch erroneous Target accesses during debug. +CONFIG_ATH_PCIE_ACCESS_DEBUG := n + +#Enable IPA offload +ifeq ($(CONFIG_IPA), y) +CONFIG_IPA_OFFLOAD := y +endif +ifeq ($(CONFIG_IPA3), y) +CONFIG_IPA_OFFLOAD := y +endif + +#Set MAX IPA Offload Interface +ifeq ($(CONFIG_ARCH_SDXPOORWILLS), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_NUM_IPA_IFACE := 2 +endif +endif + +#Flag to enable SMMU S1 support +ifeq ($(CONFIG_ARCH_SDM845), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif + +ifeq ($(CONFIG_ARCH_SM8150), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif + +ifeq ($(CONFIG_ARCH_SDXPOORWILLS), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif + +ifeq ($(CONFIG_ARCH_SDX20), y) +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) +ifeq ($(CONFIG_WCNSS_SKB_PRE_ALLOC), y) +CONFIG_FEATURE_SKB_PRE_ALLOC := y +endif +endif +endif + +#Enable Signed firmware support for split binary format +CONFIG_QCA_SIGNED_SPLIT_BINARY_SUPPORT := n + +#Enable single firmware binary format +CONFIG_QCA_SINGLE_BINARY_SUPPORT := n + +#Enable collecting target RAM dump after kernel panic +CONFIG_TARGET_RAMDUMP_AFTER_KERNEL_PANIC := y + +#Flag to enable/disable secure firmware feature +CONFIG_FEATURE_SECURE_FIRMWARE := n + +#Flag to enable Stats Ext implementation +CONFIG_FEATURE_STATS_EXT := y + +#Flag to allocate memory dynamically for different buffers +CONFIG_WLAN_LOGGING_BUFFERS_DYNAMICALLY := n +CONFIG_WLAN_DFS_STATIC_MEM_ALLOC := y + +#Flag to enable HTC credit history feature +CONFIG_FEATURE_HTC_CREDIT_HISTORY := y + +#Flag to enable MTRACE feature +CONFIG_TRACE_RECORD_FEATURE := y + +#Flag to enable p2p debug feature +CONFIG_WLAN_FEATURE_P2P_DEBUG := y + +#Flag to enable roam debug log +CONFIG_FEATURE_ROAM_DEBUG := y + +#Flag to enable DFS Master feature +CONFIG_WLAN_DFS_MASTER_ENABLE := y + +#Flag to enable WEXT support for STA/AP/P2P interfaces +CONFIG_WLAN_WEXT_SUPPORT_ENABLE := y + +#Flag to enable/disable MTRACE feature +CONFIG_ENABLE_MTRACE_LOG := y + +#Flag to enable nud tracking feature +CONFIG_WLAN_NUD_TRACKING := y + +#Flag to enable wbuff feature +CONFIG_WLAN_WBUFF := y + +#Flag to enable set and get disable channel list feature +CONFIG_DISABLE_CHANNEL_LIST :=y + +#Flag to enable Dynamic Voltage WDCVS (Config Voltage Mode) +CONFIG_WLAN_DYNAMIC_CVM := y + +CONFIG_WIFI_POS_CONVERGED := y +ifneq ($(CONFIG_WIFI_POS_CONVERGED), y) +CONFIG_WIFI_POS_LEGACY := y +endif + +CONFIG_CP_STATS := y + +CONFIG_FEATURE_WLAN_WAPI := y + +CONFIG_AGEIE_ON_SCAN_RESULTS := y + +#Flag to enable FW log parsing support feature +CONFIG_FEATURE_FW_LOG_PARSING := y + +CONFIG_PTT_SOCK_SVC_ENABLE := y +CONFIG_SOFTAP_CHANNEL_RANGE := y +CONFIG_FEATURE_WLAN_SCAN_PNO := y +CONFIG_WLAN_FEATURE_PACKET_FILTERING := y +CONFIG_WLAN_NS_OFFLOAD := y +CONFIG_FEATURE_WLAN_RA_FILTERING:= y +CONFIG_FEATURE_WLAN_LPHB := y +CONFIG_QCA_SUPPORT_TX_THROTTLE := y +CONFIG_WMI_INTERFACE_EVENT_LOGGING := y +CONFIG_WLAN_FEATURE_LINK_LAYER_STATS := y +CONFIG_FEATURE_WLAN_EXTSCAN := n +CONFIG_WMI_BCN_OFFLOAD := y +CONFIG_160MHZ_SUPPORT := y +CONFIG_MCL := y +CONFIG_REG_CLIENT := y +CONFIG_WLAN_PMO_ENABLE := y +CONFIG_CONVERGED_P2P_ENABLE := y +CONFIG_WLAN_POLICY_MGR_ENABLE := y +CONFIG_SUPPORT_11AX := y +CONFIG_HDD_INIT_WITH_RTNL_LOCK := y +CONFIG_WLAN_CONV_SPECTRAL_ENABLE := y +CONFIG_WLAN_SPECTRAL_ENABLE := y +CONFIG_WMI_CMD_STRINGS := y +CONFIG_FEATURE_MONITOR_MODE_SUPPORT := y +CONFIG_WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY := n +CONFIG_WLAN_FEATURE_TWT := y +CONFIG_WLAN_FEATURE_BMI := y + +#Flags to enable/disable vendor commands +CONFIG_FEATURE_RSSI_MONITOR := y +CONFIG_FEATURE_BSS_TRANSITION := y +CONFIG_FEATURE_STATION_INFO := y +CONFIG_FEATURE_TX_POWER := y +CONFIG_FEATURE_OTA_TEST := y +CONFIG_FEATURE_ACTIVE_TOS := y +CONFIG_FEATURE_SAR_LIMITS := y +CONFIG_FEATURE_CONCURRENCY_MATRIX := y +CONFIG_FEATURE_SAP_COND_CHAN_SWITCH := y +CONFIG_FEATURE_P2P_LISTEN_OFFLOAD := y + +#Flags to enable/disable WMI APIs +CONFIG_WMI_ROAM_SUPPORT := y +CONFIG_WMI_CONCURRENCY_SUPPORT := y +CONFIG_WMI_STA_SUPPORT := y + +#Flag to enable LTE COEX feature +CONFIG_LTE_COEX := y + +#Flag to enable HOST OPCLASS feature +CONFIG_HOST_OPCLASS := y + +ifeq ($(CONFIG_HELIUMPLUS), y) +ifneq ($(CONFIG_FORCE_ALLOC_FROM_DMA_ZONE), y) +CONFIG_ENABLE_DEBUG_ADDRESS_MARKING := y +endif +CONFIG_RX_OL := y +endif + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) + CONFIG_DSC_DEBUG := y + CONFIG_DESC_TIMESTAMP_DEBUG_INFO := y + CONFIG_FEATURE_UNIT_TEST_SUSPEND := y + CONFIG_LEAK_DETECTION := y + CONFIG_MAX_LOGS_PER_SEC := 500 + CONFIG_SCHED_HISTORY_SIZE := 256 + CONFIG_TALLOC_DEBUG := y + CONFIG_UNIT_TEST := y +endif + +ifeq ($(CONFIG_UNIT_TEST), y) + CONFIG_DSC_TEST := y + CONFIG_QDF_TEST := y +endif + +# enable unit-test suspend for napier builds +ifeq ($(CONFIG_LITHIUM), y) + CONFIG_FEATURE_UNIT_TEST_SUSPEND := y +endif + +#Flag to enable hdd memory dump feature +CONFIG_FEATURE_MEMDUMP_ENABLE := y + +#Flag to enable/disable WLAN D0-WOW +ifeq ($(CONFIG_PCI_MSM), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_FEATURE_WLAN_D0WOW := y +endif +endif + +#Enable Channel Matrix restriction for all Rome only targets +ifneq ($(CONFIG_ICNSS), y) +CONFIG_CHNL_MATRIX_RESTRICTION := y +endif + +ifeq ($(CONFIG_ARCH_MDM9607), y) +CONFIG_TUFELLO_DUAL_FW_SUPPORT := y +endif + +ifeq ($(CONFIG_ARCH_MSM8996), y) +CONFIG_CHANNEL_HOPPING_ALL_BANDS := y +endif + +ifneq ($(CONFIG_HIF_USB), y) +CONFIG_WLAN_LOGGING_SOCK_SVC := y +endif + +ifneq ($(TARGET_BUILD_VARIANT),user) +CONFIG_DESC_DUP_DETECT_DEBUG := y +CONFIG_DEBUG_RX_RING_BUFFER := y +endif + +CONFIG_DP_TRACE := y + +ifeq ($(CONFIG_CNSS), y) +ifeq ($(CONFIG_CNSS_SDIO), y) +CONFIG_PLD_SDIO_CNSS_FLAG := y +endif +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +endif +endif + +ifeq ($(CONFIG_CNSS2), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +ifeq ($(CONFIG_CNSS2), m) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +#Enable STATE MACHINE HISTORY +CONFIG_SM_ENG_HIST := n + +#Enable Beacon Reception Stats +ifeq ($(CONFIG_WLAN_SYSFS), y) +CONFIG_FEATURE_BECN_STATS := y +endif + +ifeq (y,$(findstring y,$(CONFIG_ARCH_MSM) $(CONFIG_ARCH_QCOM))) +CONFIG_WLAN_FEATURE_DP_BUS_BANDWIDTH := y +endif + +CONFIG_SAP_DHCP_FW_IND := y + +#Enable FW Offload +CONFIG_WLAN_FW_OFFLOAD := y + diff --git a/drivers/staging/qcacld-3.0/configs/qca6390_defconfig b/drivers/staging/qcacld-3.0/configs/qca6390_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..15bbe73a0d8af8ba92a2975b328f8736e0cfc97c --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/qca6390_defconfig @@ -0,0 +1,792 @@ +CONFIG_CNSS_QCA6390 := y +CONFIG_BUS_AUTO_SUSPEND := y +CONFIG_DIRECT_BUF_RX_ENABLE := y +CONFIG_WMI_DBR_SUPPORT := y + +ifeq ($(CONFIG_CNSS_QCA6390), y) + ifeq ($(CONFIG_CNSS_EMULATION), y) + CONFIG_QCA_WIFI_NAPIER_EMULATION := y + endif + CONFIG_LITHIUM := y + CONFIG_WLAN_FEATURE_11AX := y + CONFIG_WLAN_FEATURE_DFS_OFFLOAD := y + CONFIG_IPA3 := n + CONFIG_SCALE_INCLUDES := y +endif + +ifeq ($(CONFIG_ENABLE_IPA), y) + CONFIG_IPA3 := y + CONFIG_WDI3_IPA_OVER_GSI := y +else + ifeq ($(CONFIG_ENABLE_IPA), n) + CONFIG_IPA3 := n + endif +endif + +ifeq ($(CONFIG_CLD_HL_SDIO_CORE), y) + CONFIG_QCA_WIFI_SDIO := y +ifndef CONFIG_SDIO_TRANSFER + CONFIG_SDIO_TRANSFER = mailbox +endif +endif + +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) + CONFIG_ROME_IF = sdio +endif + +ifdef CONFIG_ICNSS + CONFIG_ROME_IF = snoc +endif + +ifeq (y,$(findstring y,$(CONFIG_CNSS) $(CONFIG_CNSS2))) +ifndef CONFIG_ROME_IF + #use pci as default interface + CONFIG_ROME_IF = pci +endif +endif + +ifeq (m,$(findstring m,$(CONFIG_CNSS) $(CONFIG_CNSS2))) +ifndef CONFIG_ROME_IF + #use pci as default interface + CONFIG_ROME_IF = pci +endif +endif + +# Make WLAN as open-source driver by default +WLAN_OPEN_SOURCE := y + +ifeq ($(CONFIG_ICNSS), y) + CONFIG_HELIUMPLUS := y + CONFIG_64BIT_PADDR := y + CONFIG_FEATURE_TSO := y + CONFIG_FEATURE_TSO_DEBUG := y + ifeq ($(CONFIG_INET_LRO), y) + CONFIG_WLAN_LRO := y + else + CONFIG_WLAN_LRO := n + endif +endif + +ifneq ($(DEVELOPER_DISABLE_BUILD_TIMESTAMP), y) +ifneq ($(WLAN_DISABLE_BUILD_TAG), y) +CONFIG_BUILD_TAG := y +endif +endif + +ifeq ($(CONFIG_ARCH_MDM9630), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_MDM9640), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_SDX20), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_SDXPRAIRIE), y) +CONFIG_MOBILE_ROUTER := y +endif + +ifeq ($(CONFIG_ARCH_MSM8917), y) + ifeq ($(CONFIG_ROME_IF), sdio) + CONFIG_WLAN_SYNC_TSF_PLUS := y + endif +endif + +ifeq ($(CONFIG_ARCH_QCS405), y) + CONFIG_WLAN_SYNC_TSF_PLUS := y + CONFIG_WLAN_SYNC_TSF_PLUS_NOIRQ := y +endif + +#Flag to enable Legacy Fast Roaming3(LFR3) +ifeq (y,$(findstring y,$(CONFIG_HELIUMPLUS) $(CONFIG_LITHIUM))) + CONFIG_QCACLD_WLAN_LFR3 := y +else + CONFIG_QCACLD_WLAN_LFR2 := y +endif + +ifneq ($(CONFIG_MOBILE_ROUTER), y) +#Flag to enable TDLS feature +CONFIG_QCOM_TDLS := y + +CONFIG_WLAN_SYSFS := y +endif + +CONFIG_QCACLD_FEATURE_GREEN_AP := y +#Flag to enable Android Packet Filtering +CONFIG_QCACLD_FEATURE_APF := y + +#Flag to enable SARv1 -> SARv2 conversion +CONFIG_WLAN_FEATURE_SARV1_TO_SARV2 := n + +ifeq ($(CONFIG_ARCH_MSM8998), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SDM660), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SDM630), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SDM845), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SM8150), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SDM670), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +ifeq ($(CONFIG_ARCH_SM6150), y) +CONFIG_QCACLD_FEATURE_METERING := y +endif + +#Flag to enable Fast Transition (11r) feature +CONFIG_QCOM_VOWIFI_11R := y + +#Flag to enable disable ACTION OUI feature +CONFIG_WLAN_FEATURE_ACTION_OUI := y + +#Flag to enable FILS Feature (11ai) +CONFIG_WLAN_FEATURE_FILS := y +ifneq ($(CONFIG_QCA_CLD_WLAN),) + ifeq (y,$(findstring y,$(CONFIG_CNSS) $(CONFIG_CNSS2) $(CONFIG_ICNSS))) + #Flag to enable Protected Management Frames (11w) feature + CONFIG_WLAN_FEATURE_11W := y + #Flag to enable LTE CoEx feature + CONFIG_QCOM_LTE_COEX := y + ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable LPSS feature + CONFIG_WLAN_FEATURE_LPSS := y + endif + endif + + ifeq (m,$(findstring m,$(CONFIG_CNSS2))) + #Flag to enable Protected Management Frames (11w) feature + CONFIG_WLAN_FEATURE_11W := y + #Flag to enable LTE CoEx feature + CONFIG_QCOM_LTE_COEX := y + ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable LPSS feature + CONFIG_WLAN_FEATURE_LPSS := y + endif + endif +endif + +#Flag to enable Protected Management Frames (11w) feature +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_WLAN_FEATURE_11W := y +endif +ifeq ($(CONFIG_ROME_IF),sdio) + CONFIG_WLAN_FEATURE_11W := y +endif + +#Flag to enable the tx desc sanity check +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_QCA_TXDESC_SANITY_CHECKS := y +endif + +ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable NAN + CONFIG_QCACLD_FEATURE_NAN := y + CONFIG_NDP_SAP_CONCURRENCY_ENABLE := y +endif + +ifneq ($(CONFIG_MOBILE_ROUTER), y) + #Flag to enable NAN Data path + CONFIG_WLAN_FEATURE_NAN_DATAPATH := y + CONFIG_NAN_CONVERGENCE := y +endif + +#Flag to enable Linux QCMBR feature as default feature +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_LINUX_QCMBR :=y +endif + + +CONFIG_FEATURE_EPPING := y + +#Flag to enable offload packets feature +CONFIG_WLAN_OFFLOAD_PACKETS := y + +#enable TSF get feature +CONFIG_WLAN_SYNC_TSF := y +#Enable DSRC feature +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) +CONFIG_WLAN_FEATURE_DSRC := y +endif + +ifneq ($(CONFIG_ROME_IF),usb) + #Flag to enable SAE + CONFIG_WLAN_FEATURE_SAE := y + +ifneq ($(CONFIG_ROME_IF),sdio) + #Flag to enable DISA + CONFIG_WLAN_FEATURE_DISA := y + + #Flag to enable FIPS + CONFIG_WLAN_FEATURE_FIPS := y + + #Flag to enable Fast Path feature + ifneq ($(CONFIG_LITHIUM), y) + CONFIG_WLAN_FASTPATH := y + endif + + # Flag to enable NAPI + CONFIG_WLAN_NAPI := y + CONFIG_WLAN_NAPI_DEBUG := n + + # Flag to enable FW based TX Flow control + ifeq ($(CONFIG_LITHIUM), y) + CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y + else + CONFIG_WLAN_TX_FLOW_CONTROL_V2 := n + endif + +endif +endif + +CONFIG_POWER_MANAGEMENT_OFFLOAD := y + +ifeq ($(CONFIG_ROME_IF), snoc) + CONFIG_WLAN_TX_FLOW_CONTROL_V2 := y +endif + +# Flag to enable LFR Subnet Detection +CONFIG_LFR_SUBNET_DETECTION := y + +# Flag to enable MCC to SCC switch feature +CONFIG_MCC_TO_SCC_SWITCH := y + +ifeq ($(CONFIG_SLUB_DEBUG), y) + # Enable Obj Mgr Degug services if slub build + CONFIG_WLAN_OBJMGR_DEBUG:= y + CONFIG_MPC_UT_FRAMEWORK := y + CONFIG_LOCK_STATS_ON:= y +endif + +ifeq (y,$(findstring y,$(CONFIG_QCA_WIFI_SDIO) $(CONFIG_HIF_USB))) +CONFIG_HL_DP_SUPPORT := y +else +CONFIG_LL_DP_SUPPORT := y +endif + +ifeq ($(CONFIG_ROME_IF),pci) +ifneq ($(CONFIG_WLAN_TX_FLOW_CONTROL_V2), y) +ifneq ($(CONFIG_LITHIUM), y) +CONFIG_WLAN_TX_FLOW_CONTROL_LEGACY := y +endif +endif +endif + +#Whether have QMI support +CONFIG_QMI_SUPPORT := y + +ifeq ($(CONFIG_ICNSS), y) +CONFIG_WIFI_3_0_ADRASTEA := y +CONFIG_ADRASTEA_RRI_ON_DDR := y +# Enable athdiag procfs debug support for adrastea +CONFIG_ATH_PROCFS_DIAG_SUPPORT := y +# Enable 11AC TX compact feature for adrastea +CONFIG_ATH_11AC_TXCOMPACT := y +ifeq ($(CONFIG_QMI_SUPPORT), y) +CONFIG_ADRASTEA_SHADOW_REGISTERS := y +endif +endif + +# Enable fw stats version 2 +ifeq (y,$(findstring y,$(CONFIG_HELIUMPLUS) $(CONFIG_LITHIUM))) +CONFIG_AR900B := y +endif + +# NOTE: CONFIG_64BIT_PADDR requires CONFIG_HELIUMPLUS +ifeq ($(CONFIG_HELIUMPLUS), y) + +ifeq ($(CONFIG_64BIT_PADDR), y) +CONFIG_HTT_PADDR64 := y +endif + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) +CONFIG_OL_RX_INDICATION_RECORD := y +CONFIG_TSOSEG_DEBUG := y +endif + +endif #CONFIG_HELIUMPLUS + +ifeq ($(CONFIG_LITHIUM), y) +# +# Enable Shadow V2 for all lithium platform +# +CONFIG_SHADOW_V2 := y + +ifeq ($(CONFIG_CNSS_QCA6390), y) + CONFIG_QCA6390_HEADERS_DEF := y + CONFIG_QCA_WIFI_QCA6390 := y +endif + +CONFIG_QCA_WIFI_QCA8074 := y +CONFIG_QCA_WIFI_QCA8074_VP := y +CONFIG_DP_INTR_POLL_BASED := y +CONFIG_TX_PER_PDEV_DESC_POOL := y +CONFIG_DP_TRACE := y +CONFIG_FEATURE_TSO := y +CONFIG_TSO_DEBUG_LOG_ENABLE := y +CONFIG_DP_LFR := y +CONFIG_HTT_PADDR64 := y +CONFIG_RX_OL := y +CONFIG_TX_TID_OVERRIDE := y +CONFIG_DP_TXRX_SOC_ATTACH := y +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG=y + +# CONFIG_SLUB_DEBUG_ON=y + CONFIG_PAGE_POISONING=y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QCOMs internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. +ifneq ($(TARGET_BUILD_VARIANT),user) + ifeq ($(CONFIG_LITHIUM), y) + CONFIG_FEATURE_PKTLOG := n + else + CONFIG_FEATURE_PKTLOG := y + endif + CONFIG_WLAN_DEBUG_CRASH_INJECT := y +endif + +#Enable WLAN/Power debugfs feature only if debug_fs is enabled +ifeq ($(CONFIG_DEBUG_FS), y) + # Flag to enable debugfs. Depends on CONFIG_DEBUG_FS in kernel + # configuration. + CONFIG_WLAN_DEBUGFS := y + + CONFIG_WLAN_POWER_DEBUGFS := y +endif + +# Feature flags which are not (currently) configurable via Kconfig + +#Whether to build debug version +BUILD_DEBUG_VERSION := y + +#Enable this flag to build driver in diag version +BUILD_DIAG_VERSION := y + +ifeq ($(CONFIG_SLUB_DEBUG), y) + PANIC_ON_BUG := y + WLAN_WARN_ON_ASSERT := y +else ifeq ($(CONFIG_PERF_DEBUG), y) + PANIC_ON_BUG := y + WLAN_WARN_ON_ASSERT := y +else + PANIC_ON_BUG := n + WLAN_WARN_ON_ASSERT := n +endif + +# Compile all log levels by default +CONFIG_WLAN_LOG_FATAL := y +CONFIG_WLAN_LOG_ERROR := y +CONFIG_WLAN_LOG_WARN := y +CONFIG_WLAN_LOG_INFO := y +CONFIG_WLAN_LOG_DEBUG := y +CONFIG_WLAN_LOG_ENTER := y +CONFIG_WLAN_LOG_EXIT := y + +#Enable OL debug and wmi unified functions +CONFIG_ATH_PERF_PWR_OFFLOAD := y + +#Disable packet log +CONFIG_REMOVE_PKT_LOG := n + +#Enable 11AC TX +ifeq ($(CONFIG_ROME_IF),pci) + CONFIG_ATH_11AC_TXCOMPACT := y +endif + +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_ATH_11AC_TXCOMPACT := n +endif + +#Enable PCI specific APIS (dma, etc) +ifeq ($(CONFIG_ROME_IF),pci) + CONFIG_HIF_PCI := y +endif + +#Enable USB specific APIS +ifeq ($(CONFIG_ROME_IF),usb) + CONFIG_HIF_USB := y +endif + +#Enable SDIO specific APIS +ifeq ($(CONFIG_ROME_IF),sdio) + CONFIG_HIF_SDIO := y + CONFIG_TX_DESC_HI_PRIO_RESERVE := y + CONFIG_PER_VDEV_TX_DESC_POOL := y + CONFIG_FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL := y +endif + +ifeq ($(CONFIG_ROME_IF),snoc) + CONFIG_HIF_SNOC:= y +endif + +# enable/disable feature flags based upon mobile router profile +ifeq ($(CONFIG_MOBILE_ROUTER), y) +CONFIG_FEATURE_WLAN_MCC_TO_SCC_SWITCH := y +CONFIG_FEATURE_WLAN_AUTO_SHUTDOWN := y +CONFIG_FEATURE_WLAN_AP_AP_ACS_OPTIMIZE := y +CONFIG_FEATURE_WLAN_STA_4ADDR_SCHEME := y +CONFIG_MDM_PLATFORM := y +CONFIG_FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE := y +CONFIG_FEATURE_AP_MCC_CH_AVOIDANCE := y +else +CONFIG_QCOM_ESE := y +CONFIG_QCA_IBSS_SUPPORT := y +CONFIG_FEATURE_WLAN_RMC := y +CONFIG_WLAN_OPEN_P2P_INTERFACE := y +CONFIG_WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY := y +endif + +#enable 4addr support for QCS405 +ifeq ($(CONFIG_ARCH_QCS405), y) +CONFIG_FEATURE_WLAN_STA_4ADDR_SCHEME := y +endif + +#Enable power management suspend/resume functionality to PCI +CONFIG_ATH_BUS_PM := y + +#Enable FLOWMAC module support +CONFIG_ATH_SUPPORT_FLOWMAC_MODULE := n + +#Enable spectral support +CONFIG_ATH_SUPPORT_SPECTRAL := n + +#Enable WDI Event support +ifeq ($(CONFIG_REMOVE_PKT_LOG), y) +CONFIG_WDI_EVENT_ENABLE := n +else +CONFIG_WDI_EVENT_ENABLE := y +endif + +#Enable the type_specific_data in the ath_pktlog_arg +ifeq ($(CONFIG_REMOVE_PKT_LOG), n) +CONFIG_PKTLOG_HAS_SPECIFIC_DATA := y +endif + +#Endianness selection +CONFIG_LITTLE_ENDIAN := y + +#Enable TX reclaim support +CONFIG_TX_CREDIT_RECLAIM_SUPPORT := n + +#Enable FTM support +CONFIG_QCA_WIFI_FTM := y + +ifeq ($(CONFIG_QCA_WIFI_FTM), y) + +ifeq ($(CONFIG_NL80211_TESTMODE), y) + QCA_WIFI_FTM_NL80211 :=y +else + QCA_WIFI_FTM_NL80211 :=n +endif + CONFIG_LINUX_QCMBR :=y + +else + QCA_WIFI_FTM_NL80211 :=n + CONFIG_LINUX_QCMBR :=n +endif + + +#Enable Checksum Offload +CONFIG_CHECKSUM_OFFLOAD := y + +#Enable GTK offload +CONFIG_GTK_OFFLOAD := y + +#Enable EXT WOW +ifeq ($(CONFIG_HIF_PCI), y) + CONFIG_EXT_WOW := y +endif + +#Set this to 1 to catch erroneous Target accesses during debug. +CONFIG_ATH_PCIE_ACCESS_DEBUG := n + +#Enable IPA offload +ifeq ($(CONFIG_IPA), y) +CONFIG_IPA_OFFLOAD := y +endif +ifeq ($(CONFIG_IPA3), y) +CONFIG_IPA_OFFLOAD := y +endif + +#Flag to enable SMMU S1 support +ifeq ($(CONFIG_ARCH_SDM845), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif + +ifeq ($(CONFIG_ARCH_SM8150), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif + +#Flag to enable SMMU S1 support +ifeq ($(CONFIG_ARCH_SDXPRAIRIE), y) +ifeq ($(CONFIG_IPA_OFFLOAD), y) +CONFIG_ENABLE_SMMU_S1_TRANSLATION := y +endif +endif + +ifeq ($(CONFIG_ARCH_SDX20), y) +ifeq ($(CONFIG_QCA_WIFI_SDIO), y) +ifeq ($(CONFIG_WCNSS_SKB_PRE_ALLOC), y) +CONFIG_FEATURE_SKB_PRE_ALLOC := y +endif +endif +endif + +#Enable Signed firmware support for split binary format +CONFIG_QCA_SIGNED_SPLIT_BINARY_SUPPORT := n + +#Enable single firmware binary format +CONFIG_QCA_SINGLE_BINARY_SUPPORT := n + +#Enable collecting target RAM dump after kernel panic +CONFIG_TARGET_RAMDUMP_AFTER_KERNEL_PANIC := y + +#Flag to enable/disable secure firmware feature +CONFIG_FEATURE_SECURE_FIRMWARE := n + +#Flag to enable Stats Ext implementation +CONFIG_FEATURE_STATS_EXT := y + +#Flag to allocate memory dynamically for different buffers +CONFIG_WLAN_LOGGING_BUFFERS_DYNAMICALLY := n +CONFIG_WLAN_DFS_STATIC_MEM_ALLOC := y + +#Flag to enable HTC credit history feature +CONFIG_FEATURE_HTC_CREDIT_HISTORY := y + +#Flag to enable MTRACE feature +CONFIG_TRACE_RECORD_FEATURE := y + +#Flag to enable p2p debug feature +CONFIG_WLAN_FEATURE_P2P_DEBUG := y + +#Flag to enable roam debug log +CONFIG_FEATURE_ROAM_DEBUG := y + +#Flag to enable DFS Master feature +CONFIG_WLAN_DFS_MASTER_ENABLE := y + +#Flag to enable WEXT support for STA/AP/P2P interfaces +CONFIG_WLAN_WEXT_SUPPORT_ENABLE := y + +#Flag to enable/disable MTRACE feature +CONFIG_ENABLE_MTRACE_LOG := y + +#Flag to enable nud tracking feature +CONFIG_WLAN_NUD_TRACKING := y + +#Flag to enable wbuff feature +CONFIG_WLAN_WBUFF := y + +#Flag to enable set and get disable channel list feature +CONFIG_DISABLE_CHANNEL_LIST :=y + +#Flag to enable Dynamic Voltage WDCVS (Config Voltage Mode) +CONFIG_WLAN_DYNAMIC_CVM := y + +CONFIG_WIFI_POS_CONVERGED := y +ifneq ($(CONFIG_WIFI_POS_CONVERGED), y) +CONFIG_WIFI_POS_LEGACY := y +endif + +CONFIG_CP_STATS := y + +CONFIG_FEATURE_WLAN_WAPI := y + +CONFIG_AGEIE_ON_SCAN_RESULTS := y + +#Flag to enable FW log parsing support feature +CONFIG_FEATURE_FW_LOG_PARSING := y + +CONFIG_PTT_SOCK_SVC_ENABLE := y +CONFIG_SOFTAP_CHANNEL_RANGE := y +CONFIG_FEATURE_WLAN_SCAN_PNO := y +CONFIG_WLAN_FEATURE_PACKET_FILTERING := y +CONFIG_WLAN_NS_OFFLOAD := y +CONFIG_FEATURE_WLAN_RA_FILTERING:= y +CONFIG_FEATURE_WLAN_LPHB := y +CONFIG_QCA_SUPPORT_TX_THROTTLE := y +CONFIG_WMI_INTERFACE_EVENT_LOGGING := y +CONFIG_WLAN_FEATURE_LINK_LAYER_STATS := y +CONFIG_WLAN_FEATURE_MIB_STATS := y +CONFIG_FEATURE_WLAN_EXTSCAN := n +CONFIG_WMI_BCN_OFFLOAD := y +CONFIG_160MHZ_SUPPORT := y +CONFIG_MCL := y +CONFIG_REG_CLIENT := y +CONFIG_WLAN_PMO_ENABLE := y +CONFIG_CONVERGED_P2P_ENABLE := y +CONFIG_WLAN_POLICY_MGR_ENABLE := y +CONFIG_FEATURE_BLACKLIST_MGR := y +CONFIG_SUPPORT_11AX := y +CONFIG_HDD_INIT_WITH_RTNL_LOCK := y +CONFIG_WLAN_CONV_SPECTRAL_ENABLE := y +CONFIG_WLAN_SPECTRAL_ENABLE := y +CONFIG_WMI_CMD_STRINGS := y +CONFIG_FEATURE_MONITOR_MODE_SUPPORT := y +CONFIG_WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY := n +CONFIG_WLAN_FEATURE_TWT := y + +ifeq (y,$(findstring y,$(CONFIG_LITHIUM) $(CONFIG_ICNSS))) +CONFIG_WLAN_FEATURE_BMI := n +else +CONFIG_WLAN_FEATURE_BMI := y +endif + +#Flags to enable/disable vendor commands +CONFIG_FEATURE_RSSI_MONITOR := y +CONFIG_FEATURE_BSS_TRANSITION := y +CONFIG_FEATURE_STATION_INFO := y +CONFIG_FEATURE_TX_POWER := y +CONFIG_FEATURE_OTA_TEST := y +CONFIG_FEATURE_ACTIVE_TOS := y +CONFIG_FEATURE_SAR_LIMITS := y +CONFIG_FEATURE_CONCURRENCY_MATRIX := y +CONFIG_FEATURE_SAP_COND_CHAN_SWITCH := y +CONFIG_FEATURE_P2P_LISTEN_OFFLOAD := y + +#Flags to enable/disable WMI APIs +CONFIG_WMI_ROAM_SUPPORT := y +CONFIG_WMI_CONCURRENCY_SUPPORT := y +CONFIG_WMI_STA_SUPPORT := y + +#Flag to enable LTE COEX feature +CONFIG_LTE_COEX := y + +#Flag to enable HOST OPCLASS feature +CONFIG_HOST_OPCLASS := y + +ifeq ($(CONFIG_HELIUMPLUS), y) +ifneq ($(CONFIG_FORCE_ALLOC_FROM_DMA_ZONE), y) +CONFIG_ENABLE_DEBUG_ADDRESS_MARKING := y +endif +CONFIG_RX_OL := y +endif + +ifeq ($(CONFIG_SLUB_DEBUG_ON), y) + CONFIG_DSC_DEBUG := y + CONFIG_DESC_TIMESTAMP_DEBUG_INFO := y + CONFIG_FEATURE_UNIT_TEST_SUSPEND := y + CONFIG_LEAK_DETECTION := y + CONFIG_MAX_LOGS_PER_SEC := 500 + CONFIG_SCHED_HISTORY_SIZE := 256 + CONFIG_TALLOC_DEBUG := y + CONFIG_UNIT_TEST := y +endif + +ifeq ($(CONFIG_UNIT_TEST), y) + CONFIG_DSC_TEST := y + CONFIG_QDF_TEST := y +endif + +# enable unit-test suspend for napier builds +ifeq ($(CONFIG_LITHIUM), y) + CONFIG_FEATURE_UNIT_TEST_SUSPEND := y +endif + +#Flag to enable hdd memory dump feature +CONFIG_FEATURE_MEMDUMP_ENABLE := y + +#Flag to enable/disable WLAN D0-WOW +ifeq ($(CONFIG_PCI_MSM), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_FEATURE_WLAN_D0WOW := y +endif +endif + +ifeq ($(CONFIG_ARCH_MDM9607), y) +CONFIG_TUFELLO_DUAL_FW_SUPPORT := y +endif + +ifeq ($(CONFIG_ARCH_MSM8996), y) +CONFIG_CHANNEL_HOPPING_ALL_BANDS := y +endif + +ifeq ($(CONFIG_ARCH_SDXPRAIRIE), y) + CONFIG_FEATURE_MONITOR_MODE_SUPPORT := n + ifneq ($(CONFIG_SLUB_DEBUG), y) + CONFIG_DP_TRACE := n + endif +endif + +ifneq ($(CONFIG_HIF_USB), y) +CONFIG_WLAN_LOGGING_SOCK_SVC := y +endif + +ifneq ($(TARGET_BUILD_VARIANT),user) +CONFIG_DESC_DUP_DETECT_DEBUG := y +CONFIG_DEBUG_RX_RING_BUFFER := y +endif + + +ifeq ($(CONFIG_CNSS), y) +ifeq ($(CONFIG_CNSS_SDIO), y) +CONFIG_PLD_SDIO_CNSS_FLAG := y +endif +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +endif +endif + +ifeq ($(CONFIG_CNSS2), y) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +ifeq ($(CONFIG_CNSS2), m) +ifeq ($(CONFIG_HIF_PCI), y) +CONFIG_PLD_PCIE_CNSS_FLAG := y +CONFIG_PLD_PCIE_INIT_FLAG := y +endif +endif + +#Enable STATE MACHINE HISTORY +CONFIG_SM_ENG_HIST := n + +ifeq ($(CONFIG_WLAN_SYSFS), y) +#Enable Beacon Reception Stats +CONFIG_FEATURE_BECN_STATS := y +endif + +ifeq (y,$(findstring y,$(CONFIG_ARCH_MSM) $(CONFIG_ARCH_QCOM))) +CONFIG_WLAN_FEATURE_DP_BUS_BANDWIDTH := y +endif + +CONFIG_FOURTH_CONNECTION := y +CONFIG_FOURTH_CONNECTION_AUTO := y +CONFIG_SAP_DHCP_FW_IND := y + +#Enable FW Offload +CONFIG_WLAN_FW_OFFLOAD := y + diff --git a/drivers/staging/qcacld-3.0/configs/qcn7605_defconfig b/drivers/staging/qcacld-3.0/configs/qcn7605_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..8c2f24056df1c44d80867552e342e2051c600f83 --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/qcn7605_defconfig @@ -0,0 +1 @@ +include $(WLAN_ROOT)/configs/genoa.pci.debug_defconfig diff --git a/drivers/staging/qcacld-3.0/configs/qcs40x.snoc.perf_defconfig b/drivers/staging/qcacld-3.0/configs/qcs40x.snoc.perf_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..0dd909d814c07e82f70332460b122831ac7cfb23 --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/qcs40x.snoc.perf_defconfig @@ -0,0 +1,213 @@ +# Protocol specific features +CONFIG_QCACLD_WLAN_LFR2 := y +CONFIG_QCACLD_WLAN_LFR3 := y +CONFIG_QCOM_TDLS := y +CONFIG_QCACLD_FEATURE_GREEN_AP := y +CONFIG_QCOM_VOWIFI_11R := y +CONFIG_WLAN_FEATURE_FILS := y +CONFIG_WLAN_FEATURE_11W := y +CONFIG_QCOM_LTE_COEX := n +CONFIG_WLAN_FEATURE_LPSS := n +CONFIG_QCACLD_FEATURE_NAN := n +CONFIG_WLAN_FEATURE_NAN_DATAPATH := n +CONFIG_NAN_CONVERGENCE := n +CONFIG_POWER_MANAGEMENT_OFFLOAD := y +CONFIG_LFR_SUBNET_DETECTION := y +CONFIG_MCC_TO_SCC_SWITCH := y +CONFIG_QCOM_ESE := y +CONFIG_QCA_IBSS_SUPPORT := n +CONFIG_WLAN_OPEN_P2P_INTERFACE := y +CONFIG_WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY := y +CONFIG_WLAN_DFS_MASTER_ENABLE := y +CONFIG_WIFI_POS_CONVERGED := y +CONFIG_WIFI_POS_LEGACY := n +CONFIG_FEATURE_WLAN_WAPI := y +CONFIG_AGEIE_ON_SCAN_RESULTS := y +CONFIG_CONVERGED_P2P_ENABLE := y +CONFIG_WLAN_POLICY_MGR_ENABLE := y +CONFIG_SUPPORT_11AX := n +CONFIG_HOST_OPCLASS := y +CONFIG_HDD_INIT_WITH_RTNL_LOCK := y +CONFIG_CONVERGED_TDLS_ENABLE := y +CONFIG_WLAN_CONV_SPECTRAL_ENABLE := y +CONFIG_WLAN_SPECTRAL_ENABLE := y +CONFIG_WMI_CMD_STRINGS := n +CONFIG_SOFTAP_CHANNEL_RANGE := y +CONFIG_FEATURE_WLAN_SCAN_PNO := y +CONFIG_DFS_PRI_MULTIPLIER := y +CONFIG_DFS_OVERRIDE_RF_THRESHOLD := y + +# Interface specific features +CONFIG_ROME_IF = snoc +CONFIG_HELIUMPLUS := y +CONFIG_64BIT_PADDR := y +CONFIG_QMI_SUPPORT := y +CONFIG_WIFI_3_0_ADRASTEA := y +CONFIG_ADRASTEA_RRI_ON_DDR := y +CONFIG_ATH_PROCFS_DIAG_SUPPORT := y +CONFIG_ADRASTEA_SHADOW_REGISTERS := y +CONFIG_HTT_PADDR64 := y +CONFIG_AR900B := y +CONFIG_HIF_SNOC:= y + +# Data Path specific features +CONFIG_WLAN_FASTPATH := y +CONFIG_FEATURE_TSO := y +CONFIG_WLAN_NAPI := y +CONFIG_WLAN_TX_FLOW_CONTROL_V2 := n +CONFIG_ATH_11AC_TXCOMPACT := y +CONFIG_TX_CREDIT_RECLAIM_SUPPORT := n +CONFIG_CHECKSUM_OFFLOAD := y +CONFIG_QCA_SUPPORT_TX_THROTTLE := y +CONFIG_RX_OL := y +CONFIG_FEATURE_MONITOR_MODE_SUPPORT := n +CONFIG_DESC_DUP_DETECT_DEBUG := n +CONFIG_DEBUG_RX_RING_BUFFER := n +CONFIG_RX_PERFORMANCE := y +CONFIG_SLUB_MEM_OPTIMIZE := y +CONFIG_TGT_NUM_MSDU_DESC := 900 +CONFIG_WLAN_PDEV_TX_FLOW_CONTROL := y +CONFIG_FEATURE_WLAN_STA_4ADDR_SCHEME := y + +ifeq ($(CONFIG_INET_LRO), y) +CONFIG_WLAN_LRO := y +else +CONFIG_WLAN_LRO := n +endif + +ifeq ($(CONFIG_IPA), y) +CONFIG_IPA_OFFLOAD := y +endif +ifeq ($(CONFIG_IPA3), y) +CONFIG_IPA_OFFLOAD := y +endif + +ifneq ($(CONFIG_FORCE_ALLOC_FROM_DMA_ZONE), y) +CONFIG_ENABLE_DEBUG_ADDRESS_MARKING := y +endif + +# Debug specific features +BUILD_DEBUG_VERSION := n +BUILD_DIAG_VERSION := y +CONFIG_FEATURE_TSO_DEBUG := y +CONFIG_MPC_UT_FRAMEWORK := n +CONFIG_FEATURE_EPPING := n +CONFIG_WLAN_NAPI_DEBUG := n +CONFIG_REMOVE_PKT_LOG := y +CONFIG_FEATURE_STATS_EXT := y +CONFIG_FEATURE_HTC_CREDIT_HISTORY := y +CONFIG_TRACE_RECORD_FEATURE := y +CONFIG_WLAN_FEATURE_P2P_DEBUG := n +CONFIG_WLAN_NUD_TRACKING := n +CONFIG_CP_STATS := n +CONFIG_FEATURE_FW_LOG_PARSING := y +CONFIG_PTT_SOCK_SVC_ENABLE := y +CONFIG_WMI_INTERFACE_EVENT_LOGGING := y +CONFIG_WLAN_FEATURE_LINK_LAYER_STATS := y +CONFIG_DP_TRACE := n +CONFIG_QCACLD_FEATURE_HW_CAPABILITY := y + +CONFIG_WLAN_LOG_FATAL := y +CONFIG_WLAN_LOG_ERROR := y +CONFIG_WLAN_LOG_WARN := y +CONFIG_WLAN_LOG_INFO := y +CONFIG_WLAN_LOG_DEBUG := y + +ifeq ($(CONFIG_REMOVE_PKT_LOG), y) +CONFIG_WDI_EVENT_ENABLE := n +else +CONFIG_WDI_EVENT_ENABLE := y +endif + +# Features gets enabled on slub debug +CONFIG_WLAN_OBJMGR_DEBUG:= n +CONFIG_OL_RX_INDICATION_RECORD := n +CONFIG_TSOSEG_DEBUG := n +CONFIG_FEATURE_PKTLOG := n +CONFIG_WLAN_DEBUG_CRASH_INJECT := n +PANIC_ON_BUG := y +WLAN_WARN_ON_ASSERT := y +CONFIG_FEATURE_MEMDUMP_ENABLE := n +CONFIG_WLAN_LOGGING_SOCK_SVC := n +CONFIG_FEATURE_UNIT_TEST_SUSPEND := n +CONFIG_LEAK_DETECTION := n + +ifeq ($(CONFIG_DEBUG_FS), y) + CONFIG_WLAN_DEBUGFS := n + CONFIG_WLAN_POWER_DEBUGFS := n +endif + +# other features +WLAN_OPEN_SOURCE := y +CONFIG_ATH_PERF_PWR_OFFLOAD := y +CONFIG_ATH_BUS_PM := y +CONFIG_ATH_SUPPORT_FLOWMAC_MODULE := n +CONFIG_ATH_SUPPORT_SPECTRAL := n +CONFIG_LITTLE_ENDIAN := y +CONFIG_QCA_WIFI_FTM := y +CONFIG_ATH_PCIE_ACCESS_DEBUG := n +CONFIG_QCA_SIGNED_SPLIT_BINARY_SUPPORT := n +CONFIG_QCA_SINGLE_BINARY_SUPPORT := n +CONFIG_TARGET_RAMDUMP_AFTER_KERNEL_PANIC := y +CONFIG_FEATURE_SECURE_FIRMWARE := n +CONFIG_WLAN_FEATURE_PACKET_FILTERING := y +CONFIG_WLAN_NS_OFFLOAD := y +CONFIG_FEATURE_WLAN_RA_FILTERING:= y +CONFIG_FEATURE_WLAN_LPHB := y +CONFIG_FEATURE_WLAN_EXTSCAN := n +CONFIG_160MHZ_SUPPORT := y +CONFIG_MCL := y +CONFIG_MCL_REGDB := y +CONFIG_WLAN_OFFLOAD_PACKETS := y +CONFIG_WLAN_SYNC_TSF := y +CONFIG_WLAN_SYNC_TSF_PLUS := y +CONFIG_WLAN_SYNC_TSF_PLUS_NOIRQ := y +CONFIG_WLAN_FEATURE_DISA := n +CONFIG_WLAN_FEATURE_FIPS := n +CONFIG_WLAN_FEATURE_SAE := y +CONFIG_GTK_OFFLOAD := y +CONFIG_QCACLD_FEATURE_COEX_CONFIG := y +CONFIG_QCACLD_FEATURE_MPTA_HELPER := y +CONFIG_WMI_ROAM_SUPPORT := y +CONFIG_WMI_STA_SUPPORT := y +CONFIG_REG_CLIENT := y +CONFIG_WLAN_FEATURE_DP_BUS_BANDWIDTH := y +CONFIG_WMI_CONCURRENCY_SUPPORT := y +CONFIG_LL_DP_SUPPORT := y + +ifeq ($(CONFIG_QCA_WIFI_FTM), y) + +ifeq ($(CONFIG_NL80211_TESTMODE), y) + QCA_WIFI_FTM_NL80211 :=y +else + QCA_WIFI_FTM_NL80211 :=n +endif + CONFIG_LINUX_QCMBR :=y + +else + QCA_WIFI_FTM_NL80211 :=n + CONFIG_LINUX_QCMBR :=n +endif + +ifneq ($(DEVELOPER_DISABLE_BUILD_TIMESTAMP), y) +ifneq ($(WLAN_DISABLE_BUILD_TAG), y) +CONFIG_BUILD_TAG := y +endif +endif + +#Enable FW Offload +CONFIG_WLAN_FW_OFFLOAD := y + +CONFIG_ENABLE_SIZE_OPTIMIZE := y +CONFIG_FEATURE_WLAN_TIME_SYNC_FTM := y + +# configure log buffer size +CONFIG_CFG_NUM_DP_TRACE_RECORD := 1000 +CONFIG_CFG_NUM_HTC_CREDIT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_WMI_MGMT_EVENT_HISTORY := 16 +CONFIG_CFG_NUM_TX_RX_HISTOGRAM := 16 +# CONFIG_CFG_NUM_RX_IND_RECORD := 1024 + +CONFIG_SAP_DHCP_FW_IND := y +################################### diff --git a/drivers/staging/qcacld-3.0/configs/whunt_defconfig b/drivers/staging/qcacld-3.0/configs/whunt_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..80f896793a2e06d9d905b25331d2a43dcc3ce7b0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/configs/whunt_defconfig @@ -0,0 +1,13 @@ +#Enable 6 GHz Band +CONFIG_BAND_6GHZ := y + +#Enable 11AX Feature +CONFIG_WLAN_FEATURE_11AX := y + +include $(WLAN_ROOT)/configs/default_defconfig + +#Enable BUS bandwidth Feature +CONFIG_WLAN_FEATURE_DP_BUS_BANDWIDTH := y + +#Disable PM_QOS not supported in WHUNT +CONFIG_WLAN_CLD_PM_QOS := n diff --git a/drivers/staging/qcacld-3.0/core/bmi/inc/bmi.h b/drivers/staging/qcacld-3.0/core/bmi/inc/bmi.h new file mode 100644 index 0000000000000000000000000000000000000000..586d6355ebf242b05f94da4825e4d808c02b8289 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/bmi/inc/bmi.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* ================================================================ */ +/* BMI declarations and prototypes */ +/* */ +/* ================================================================= */ + +#ifndef _BMI_H_ +#define _BMI_H_ +#include "bmi_msg.h" +#include "qdf_trace.h" +#include "ol_if_athvar.h" +#include "hif.h" + +struct ol_context; + +/** + * struct hif_config_info - Place Holder for hif confiruation + * @enable_uart_print: UART Print + * @enable_self_recovery: Self Recovery + * @enable_fw_log: To Enable FW LOG + * @enable_lpass_support: LPASS support + * @enable_ramdump_collection: Ramdump Collection + * + * Structure for holding ini parameters. + */ + +struct ol_config_info { + bool enable_uart_print; + bool enable_self_recovery; + uint8_t enable_fw_log; + bool enable_lpass_support; + bool enable_ramdump_collection; +}; + +#ifdef WLAN_FEATURE_BMI +QDF_STATUS ol_cds_init(qdf_device_t qdf_dev, void *hif_ctx); +void ol_cds_free(void); +void ol_init_ini_config(struct ol_context *ol_ctx, + struct ol_config_info *cfg); +/** + * ol_set_fw_crashed_cb() - set firmware crashed callback + * @ol_ctx: ol context + * @callback_fn: fw crashed callback function + * + * Return: None + */ +void ol_set_fw_crashed_cb(struct ol_context *ol_ctx, + void (*callback_fn)(void)); +void bmi_cleanup(struct ol_context *scn); +QDF_STATUS bmi_done(struct ol_context *ol_ctx); +void bmi_target_ready(struct hif_opaque_softc *scn, void *cfg_ctx); +QDF_STATUS bmi_download_firmware(struct ol_context *ol_ctx); + +#else /* WLAN_FEATURE_BMI */ + +static inline QDF_STATUS +ol_cds_init(qdf_device_t qdf_dev, void *hif_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void ol_cds_free(void) +{ +} + +static inline void +ol_init_ini_config(struct ol_context *ol_ctx, struct ol_config_info *cfg) +{ +} + +static inline void +ol_set_fw_crashed_cb(struct ol_context *ol_ctx, void (*callback_fn)(void)) +{ +} + +static inline void bmi_cleanup(struct ol_context *scn) +{ +} + +static inline QDF_STATUS bmi_done(struct ol_context *ol_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void +bmi_target_ready(struct hif_opaque_softc *scn, void *cfg_ctx) +{ +} + +static inline QDF_STATUS +bmi_download_firmware(struct ol_context *ol_ctx) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_BMI */ + +#endif /* _BMI_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/bmi/inc/ol_fw.h b/drivers/staging/qcacld-3.0/core/bmi/inc/ol_fw.h new file mode 100644 index 0000000000000000000000000000000000000000..13d4cd65a7ff63140375ddab4eeca54fda784dbb --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/bmi/inc/ol_fw.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_FW_H_ +#define _OL_FW_H_ + +#include "qdf_types.h" +#include "hif.h" +#include "hif_hw_version.h" +#include "bmi.h" + +#define AR6320_REV2_VERSION AR6320_REV1_1_VERSION +#define AR6320_REV4_VERSION AR6320_REV2_1_VERSION +#define SIGN_HEADER_MAGIC 0x454D4F52 +#define HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET (1 << 0) +#define HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_SET (1 << 1) +#define HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE (1 << 2) + +#define HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_FW_ACK (1 << 16) +#define HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_FW_ACK (1 << 17) + +#ifdef WLAN_FEATURE_BMI +void ol_target_failure(void *instance, QDF_STATUS status); + +void ol_target_ready(struct hif_opaque_softc *scn, void *cfg_ctx); +QDF_STATUS ol_extra_initialization(struct ol_context *ol_ctx); +#else /* WLAN_FEATURE_BMI */ +static inline void ol_target_failure(void *instance, QDF_STATUS status) {} +#endif /* WLAN_FEATURE_BMI */ +#endif /* _OL_FW_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/bmi/inc/ol_if_athvar.h b/drivers/staging/qcacld-3.0/core/bmi/inc/ol_if_athvar.h new file mode 100644 index 0000000000000000000000000000000000000000..dc2e0009b52b85eddb5a0df9fcb4f413d5ce83b0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/bmi/inc/ol_if_athvar.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Definitions for the Atheros Wireless LAN controller driver. + */ +#ifndef _DEV_OL_ATH_ATHVAR_H +#define _DEV_OL_ATH_ATHVAR_H + +#include "htc_api.h" +#include "bmi_msg.h" + +#endif /* _DEV_OL_ATH_ATHVAR_H */ diff --git a/drivers/staging/qcacld-3.0/core/bmi/src/bmi.c b/drivers/staging/qcacld-3.0/core/bmi/src/bmi.c new file mode 100644 index 0000000000000000000000000000000000000000..ea8daac9f134303f0e03381b4ae8d3c58a154186 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/bmi/src/bmi.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "i_bmi.h" +#include "cds_api.h" + +/* APIs visible to the driver */ + +QDF_STATUS bmi_init(struct ol_context *ol_ctx) +{ + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + struct hif_opaque_softc *scn = ol_ctx->scn; + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + + if (!scn) { + BMI_ERR("Invalid scn Context"); + bmi_assert(0); + return QDF_STATUS_NOT_INITIALIZED; + } + + if (!qdf_dev->dev) { + BMI_ERR("%s: Invalid Device Pointer", __func__); + return QDF_STATUS_NOT_INITIALIZED; + } + + info->bmi_done = false; + + if (!info->bmi_cmd_buff) { + info->bmi_cmd_buff = + qdf_mem_alloc_consistent(qdf_dev, qdf_dev->dev, + MAX_BMI_CMDBUF_SZ, + &info->bmi_cmd_da); + if (!info->bmi_cmd_buff) { + BMI_ERR("No Memory for BMI Command"); + return QDF_STATUS_E_NOMEM; + } + } + + if (!info->bmi_rsp_buff) { + info->bmi_rsp_buff = + qdf_mem_alloc_consistent(qdf_dev, qdf_dev->dev, + MAX_BMI_CMDBUF_SZ, + &info->bmi_rsp_da); + if (!info->bmi_rsp_buff) { + BMI_ERR("No Memory for BMI Response"); + goto end; + } + } + return QDF_STATUS_SUCCESS; +end: + qdf_mem_free_consistent(qdf_dev, qdf_dev->dev, MAX_BMI_CMDBUF_SZ, + info->bmi_cmd_buff, info->bmi_cmd_da, 0); + info->bmi_cmd_buff = NULL; + return QDF_STATUS_E_NOMEM; +} + +void bmi_cleanup(struct ol_context *ol_ctx) +{ + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + qdf_device_t qdf_dev; + + if (!info || !ol_ctx) { + BMI_WARN("%s: no bmi to cleanup", __func__); + return; + } + + qdf_dev = ol_ctx->qdf_dev; + if (!qdf_dev || !qdf_dev->dev) { + BMI_ERR("%s: Invalid Device Pointer", __func__); + return; + } + + if (info->bmi_cmd_buff) { + qdf_mem_free_consistent(qdf_dev, qdf_dev->dev, + MAX_BMI_CMDBUF_SZ, + info->bmi_cmd_buff, info->bmi_cmd_da, 0); + info->bmi_cmd_buff = NULL; + info->bmi_cmd_da = 0; + } + + if (info->bmi_rsp_buff) { + qdf_mem_free_consistent(qdf_dev, qdf_dev->dev, + MAX_BMI_CMDBUF_SZ, + info->bmi_rsp_buff, info->bmi_rsp_da, 0); + info->bmi_rsp_buff = NULL; + info->bmi_rsp_da = 0; + } +} + +/** + * bmi_done() - finish the bmi opperation + * @ol_ctx: the bmi context + * + * does some sanity checking. + * exchanges one last message with firmware. + * frees some buffers. + * + * Return: QDF_STATUS_SUCCESS if bmi isn't needed. + * QDF_STATUS_SUCCESS if bmi finishes. + * otherwise returns failure. + */ +QDF_STATUS bmi_done(struct ol_context *ol_ctx) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (NO_BMI) + return QDF_STATUS_SUCCESS; + + if (!ol_ctx) { + BMI_ERR("%s: null context", __func__); + return QDF_STATUS_E_NOMEM; + } + hif_claim_device(ol_ctx->scn); + + if (!hif_needs_bmi(ol_ctx->scn)) + return QDF_STATUS_SUCCESS; + + status = bmi_done_local(ol_ctx); + if (status != QDF_STATUS_SUCCESS) + BMI_ERR("BMI_DONE Failed status:%d", status); + + return status; +} + +void bmi_target_ready(struct hif_opaque_softc *scn, void *cfg_ctx) +{ + ol_target_ready(scn, cfg_ctx); +} + +static QDF_STATUS +bmi_get_target_info_message_based(struct bmi_target_info *targ_info, + struct ol_context *ol_ctx) +{ + int status = 0; + struct hif_opaque_softc *scn = ol_ctx->scn; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + uint8_t *bmi_cmd_buff = info->bmi_cmd_buff; + uint8_t *bmi_rsp_buff = info->bmi_rsp_buff; + uint32_t cid, length; + qdf_dma_addr_t cmd = info->bmi_cmd_da; + qdf_dma_addr_t rsp = info->bmi_rsp_da; + + if (!bmi_cmd_buff || !bmi_rsp_buff) { + BMI_ERR("%s:BMI CMD/RSP Buffer is NULL", __func__); + return QDF_STATUS_NOT_INITIALIZED; + } + + cid = BMI_GET_TARGET_INFO; + + qdf_mem_copy(bmi_cmd_buff, &cid, sizeof(cid)); + length = sizeof(struct bmi_target_info); + + status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff, sizeof(cid), + (uint8_t *)bmi_rsp_buff, &length, + BMI_EXCHANGE_TIMEOUT_MS); + if (status) { + BMI_ERR("Failed to target info: status:%d", status); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_copy(targ_info, bmi_rsp_buff, length); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +bmi_get_target_info(struct bmi_target_info *targ_info, + struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + QDF_STATUS status; + + if (info->bmi_done) { + BMI_ERR("BMI Phase is Already Done"); + return QDF_STATUS_E_PERM; + } + + switch (hif_get_bus_type(scn)) { + case QDF_BUS_TYPE_PCI: + case QDF_BUS_TYPE_SNOC: + case QDF_BUS_TYPE_USB: + status = bmi_get_target_info_message_based(targ_info, ol_ctx); + break; +#ifdef HIF_SDIO + case QDF_BUS_TYPE_SDIO: + status = hif_reg_based_get_target_info(scn, targ_info); + break; +#endif + default: + status = QDF_STATUS_E_FAILURE; + break; + } + return status; +} + +QDF_STATUS bmi_download_firmware(struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn; + + if (!ol_ctx) { + if (NO_BMI) { + /* ol_ctx is not allocated in NO_BMI case */ + return QDF_STATUS_SUCCESS; + } + + BMI_ERR("ol_ctx is NULL"); + bmi_assert(0); + return QDF_STATUS_NOT_INITIALIZED; + } + + scn = ol_ctx->scn; + + if (!scn) { + BMI_ERR("Invalid scn context"); + bmi_assert(0); + return QDF_STATUS_NOT_INITIALIZED; + } + + if (!hif_needs_bmi(scn)) + return QDF_STATUS_SUCCESS; + else + hif_register_bmi_callbacks(scn); + + return bmi_firmware_download(ol_ctx); +} + +QDF_STATUS bmi_read_soc_register(uint32_t address, uint32_t *param, + struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + uint32_t cid; + int status; + uint32_t offset, param_len; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + uint8_t *bmi_cmd_buff = info->bmi_cmd_buff; + uint8_t *bmi_rsp_buff = info->bmi_rsp_buff; + qdf_dma_addr_t cmd = info->bmi_cmd_da; + qdf_dma_addr_t rsp = info->bmi_rsp_da; + + bmi_assert(BMI_COMMAND_FITS(sizeof(cid) + sizeof(address))); + qdf_mem_zero(bmi_cmd_buff, sizeof(cid) + sizeof(address)); + qdf_mem_zero(bmi_rsp_buff, sizeof(cid) + sizeof(address)); + + if (info->bmi_done) { + BMI_DBG("Command disallowed"); + return QDF_STATUS_E_PERM; + } + + BMI_DBG("BMI Read SOC Register:device: 0x%pK, address: 0x%x", + scn, address); + + cid = BMI_READ_SOC_REGISTER; + + offset = 0; + qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); + offset += sizeof(cid); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &address, sizeof(address)); + offset += sizeof(address); + param_len = sizeof(*param); + status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff, offset, + bmi_rsp_buff, ¶m_len, BMI_EXCHANGE_TIMEOUT_MS); + if (status) { + BMI_DBG("Unable to read from the device; status:%d", status); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(param, bmi_rsp_buff, sizeof(*param)); + + BMI_DBG("BMI Read SOC Register: Exit value: %d", *param); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS bmi_write_soc_register(uint32_t address, uint32_t param, + struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + uint32_t cid; + int status; + uint32_t offset; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + uint8_t *bmi_cmd_buff = info->bmi_cmd_buff; + uint32_t size = sizeof(cid) + sizeof(address) + sizeof(param); + qdf_dma_addr_t cmd = info->bmi_cmd_da; + qdf_dma_addr_t rsp = info->bmi_rsp_da; + + bmi_assert(BMI_COMMAND_FITS(size)); + qdf_mem_zero(bmi_cmd_buff, size); + + if (info->bmi_done) { + BMI_DBG("Command disallowed"); + return QDF_STATUS_E_FAILURE; + } + + BMI_DBG("SOC Register Write:device:0x%pK, addr:0x%x, param:%d", + scn, address, param); + + cid = BMI_WRITE_SOC_REGISTER; + + offset = 0; + qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); + offset += sizeof(cid); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &address, sizeof(address)); + offset += sizeof(address); + qdf_mem_copy(&(bmi_cmd_buff[offset]), ¶m, sizeof(param)); + offset += sizeof(param); + status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff, offset, + NULL, NULL, 0); + if (status) { + BMI_ERR("Unable to write to the device: status:%d", status); + return QDF_STATUS_E_FAILURE; + } + + BMI_DBG("BMI Read SOC Register: Exit"); + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +bmilz_data(uint8_t *buffer, uint32_t length, struct ol_context *ol_ctx) +{ + uint32_t cid; + int status; + uint32_t offset; + uint32_t remaining, txlen; + const uint32_t header = sizeof(cid) + sizeof(length); + struct hif_opaque_softc *scn = ol_ctx->scn; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + uint8_t *bmi_cmd_buff = info->bmi_cmd_buff; + qdf_dma_addr_t cmd = info->bmi_cmd_da; + qdf_dma_addr_t rsp = info->bmi_rsp_da; + + bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + header)); + qdf_mem_zero(bmi_cmd_buff, BMI_DATASZ_MAX + header); + + if (info->bmi_done) { + BMI_ERR("Command disallowed"); + return QDF_STATUS_E_PERM; + } + + BMI_DBG("BMI Send LZ Data: device: 0x%pK, length: %d", + scn, length); + + cid = BMI_LZ_DATA; + + remaining = length; + while (remaining) { + txlen = (remaining < (BMI_DATASZ_MAX - header)) ? + remaining : (BMI_DATASZ_MAX - header); + offset = 0; + qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); + offset += sizeof(cid); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &txlen, sizeof(txlen)); + offset += sizeof(txlen); + qdf_mem_copy(&(bmi_cmd_buff[offset]), + &buffer[length - remaining], txlen); + offset += txlen; + status = hif_exchange_bmi_msg(scn, cmd, rsp, + bmi_cmd_buff, offset, + NULL, NULL, 0); + if (status) { + BMI_ERR("Failed to write to the device: status:%d", + status); + return QDF_STATUS_E_FAILURE; + } + remaining -= txlen; + } + + BMI_DBG("BMI LZ Data: Exit"); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS bmi_sign_stream_start(uint32_t address, uint8_t *buffer, + uint32_t length, struct ol_context *ol_ctx) +{ + uint32_t cid; + int status; + uint32_t offset; + const uint32_t header = sizeof(cid) + sizeof(address) + sizeof(length); + uint8_t aligned_buf[BMI_DATASZ_MAX + 4]; + uint8_t *src; + struct hif_opaque_softc *scn = ol_ctx->scn; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + uint8_t *bmi_cmd_buff = info->bmi_cmd_buff; + uint32_t remaining, txlen; + qdf_dma_addr_t cmd = info->bmi_cmd_da; + qdf_dma_addr_t rsp = info->bmi_rsp_da; + + bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + header)); + qdf_mem_zero(bmi_cmd_buff, BMI_DATASZ_MAX + header); + + if (info->bmi_done) { + BMI_ERR("Command disallowed"); + return QDF_STATUS_E_PERM; + } + + BMI_ERR("Sign Stream start:device:0x%pK, addr:0x%x, length:%d", + scn, address, length); + + cid = BMI_SIGN_STREAM_START; + remaining = length; + while (remaining) { + src = &buffer[length - remaining]; + if (remaining < (BMI_DATASZ_MAX - header)) { + if (remaining & 0x3) { + memcpy(aligned_buf, src, remaining); + remaining = remaining + (4 - (remaining & 0x3)); + src = aligned_buf; + } + txlen = remaining; + } else { + txlen = (BMI_DATASZ_MAX - header); + } + + offset = 0; + qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); + offset += sizeof(cid); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &address, + sizeof(address)); + offset += sizeof(offset); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &txlen, sizeof(txlen)); + offset += sizeof(txlen); + qdf_mem_copy(&(bmi_cmd_buff[offset]), src, txlen); + offset += txlen; + status = hif_exchange_bmi_msg(scn, cmd, rsp, + bmi_cmd_buff, offset, NULL, + NULL, BMI_EXCHANGE_TIMEOUT_MS); + if (status) { + BMI_ERR("Unable to write to the device: status:%d", + status); + return QDF_STATUS_E_FAILURE; + } + remaining -= txlen; + } + BMI_DBG("BMI SIGN Stream Start: Exit"); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +bmilz_stream_start(uint32_t address, struct ol_context *ol_ctx) +{ + uint32_t cid; + int status; + uint32_t offset; + struct hif_opaque_softc *scn = ol_ctx->scn; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + uint8_t *bmi_cmd_buff = info->bmi_cmd_buff; + qdf_dma_addr_t cmd = info->bmi_cmd_da; + qdf_dma_addr_t rsp = info->bmi_rsp_da; + + bmi_assert(BMI_COMMAND_FITS(sizeof(cid) + sizeof(address))); + qdf_mem_zero(bmi_cmd_buff, sizeof(cid) + sizeof(address)); + + if (info->bmi_done) { + BMI_DBG("Command disallowed"); + return QDF_STATUS_E_PERM; + } + BMI_DBG("BMI LZ Stream Start: (device: 0x%pK, address: 0x%x)", + scn, address); + + cid = BMI_LZ_STREAM_START; + offset = 0; + qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); + offset += sizeof(cid); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &address, sizeof(address)); + offset += sizeof(address); + status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff, offset, + NULL, NULL, 0); + if (status) { + BMI_ERR("Unable to Start LZ Stream to the device status:%d", + status); + return QDF_STATUS_E_FAILURE; + } + BMI_DBG("BMI LZ Stream: Exit"); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +bmi_fast_download(uint32_t address, uint8_t *buffer, + uint32_t length, struct ol_context *ol_ctx) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t last_word = 0; + uint32_t last_word_offset = length & ~0x3; + uint32_t unaligned_bytes = length & 0x3; + + status = bmilz_stream_start(address, ol_ctx); + if (status != QDF_STATUS_SUCCESS) + goto end; + + /* copy the last word into a zero padded buffer */ + if (unaligned_bytes) + qdf_mem_copy(&last_word, &buffer[last_word_offset], + unaligned_bytes); + + status = bmilz_data(buffer, last_word_offset, ol_ctx); + + if (status != QDF_STATUS_SUCCESS) + goto end; + + if (unaligned_bytes) + status = bmilz_data((uint8_t *) &last_word, 4, ol_ctx); + + if (status != QDF_STATUS_SUCCESS) + /* + * Close compressed stream and open a new (fake) one. + * This serves mainly to flush Target caches. + */ + status = bmilz_stream_start(0x00, ol_ctx); +end: + return status; +} + +/** + * ol_cds_init() - API to initialize global CDS OL Context + * @qdf_dev: QDF Device + * @hif_ctx: HIF Context + * + * Return: Success/Failure + */ +QDF_STATUS ol_cds_init(qdf_device_t qdf_dev, void *hif_ctx) +{ + struct ol_context *ol_info; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (NO_BMI) + return QDF_STATUS_SUCCESS; /* no BMI for Q6 bring up */ + + status = cds_alloc_context(QDF_MODULE_ID_BMI, + (void **)&ol_info, sizeof(*ol_info)); + + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("%s: CDS Allocation failed for ol_bmi context", + __func__); + return status; + } + + ol_info->qdf_dev = qdf_dev; + ol_info->scn = hif_ctx; + ol_info->tgt_def.targetdef = hif_get_targetdef(hif_ctx); + + qdf_create_work(qdf_dev, &ol_info->ramdump_work, + ramdump_work_handler, ol_info); + qdf_create_work(qdf_dev, &ol_info->fw_indication_work, + fw_indication_work_handler, ol_info); + + return status; +} + +/** + * ol_cds_free() - API to free the global CDS OL Context + * + * Return: void + */ +void ol_cds_free(void) +{ + struct ol_context *ol_info = cds_get_context(QDF_MODULE_ID_BMI); + + if (NO_BMI) + return; + + cds_free_context(QDF_MODULE_ID_BMI, ol_info); +} diff --git a/drivers/staging/qcacld-3.0/core/bmi/src/bmi_1.c b/drivers/staging/qcacld-3.0/core/bmi/src/bmi_1.c new file mode 100644 index 0000000000000000000000000000000000000000..b24225e6c7ce21f6c2392a12bac44819bfb20969 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/bmi/src/bmi_1.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "i_bmi.h" +#include "cds_api.h" + +/* APIs visible to the driver */ + +QDF_STATUS +bmi_read_memory(uint32_t address, + uint8_t *buffer, uint32_t length, struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + uint32_t cid; + int status; + uint32_t offset; + uint32_t remaining, rxlen; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + uint8_t *bmi_cmd_buff = info->bmi_cmd_buff; + uint8_t *bmi_rsp_buff = info->bmi_rsp_buff; + uint32_t align; + qdf_dma_addr_t cmd = info->bmi_cmd_da; + qdf_dma_addr_t rsp = info->bmi_rsp_da; + + if (info->bmi_done) { + BMI_DBG("command disallowed"); + return QDF_STATUS_E_PERM; + } + + if (!info->bmi_cmd_buff || !info->bmi_rsp_buff) { + BMI_ERR("BMI Initialization hasn't done"); + return QDF_STATUS_NOT_INITIALIZED; + } + + bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + sizeof(cid) + + sizeof(address) + sizeof(length))); + qdf_mem_zero(bmi_cmd_buff, BMI_DATASZ_MAX + sizeof(cid) + + sizeof(address) + sizeof(length)); + qdf_mem_zero(bmi_rsp_buff, BMI_DATASZ_MAX + sizeof(cid) + + sizeof(address) + sizeof(length)); + + cid = BMI_READ_MEMORY; + align = 0; + remaining = length; + + while (remaining) { + rxlen = (remaining < BMI_DATASZ_MAX) ? + remaining : BMI_DATASZ_MAX; + offset = 0; + qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); + offset += sizeof(cid); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &address, + sizeof(address)); + offset += sizeof(address); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &rxlen, sizeof(rxlen)); + offset += sizeof(length); + + /* note we reuse the same buffer to receive on */ + status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff, + offset, bmi_rsp_buff, &rxlen, + BMI_EXCHANGE_TIMEOUT_MS); + if (status) { + BMI_ERR("Unable to read from the device"); + return QDF_STATUS_E_FAILURE; + } + if (remaining == rxlen) { + qdf_mem_copy(&buffer[length - remaining + align], + bmi_rsp_buff, rxlen - align); + /* last align bytes are invalid */ + } else { + qdf_mem_copy(&buffer[length - remaining + align], + bmi_rsp_buff, rxlen); + } + remaining -= rxlen; + address += rxlen; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS bmi_write_memory(uint32_t address, uint8_t *buffer, uint32_t length, + struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + uint32_t cid; + int status; + uint32_t offset; + uint32_t remaining, txlen; + const uint32_t header = sizeof(cid) + sizeof(address) + sizeof(length); + uint8_t aligned_buffer[BMI_DATASZ_MAX]; + uint8_t *src; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + uint8_t *bmi_cmd_buff = info->bmi_cmd_buff; + qdf_dma_addr_t cmd = info->bmi_cmd_da; + qdf_dma_addr_t rsp = info->bmi_rsp_da; + + if (info->bmi_done) { + BMI_ERR("Command disallowed"); + return QDF_STATUS_E_PERM; + } + + if (!bmi_cmd_buff) { + BMI_ERR("BMI initialization hasn't done"); + return QDF_STATUS_E_PERM; + } + + bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + header)); + qdf_mem_zero(bmi_cmd_buff, BMI_DATASZ_MAX + header); + + cid = BMI_WRITE_MEMORY; + + remaining = length; + while (remaining) { + src = &buffer[length - remaining]; + if (remaining < (BMI_DATASZ_MAX - header)) { + if (remaining & 3) { + /* align it with 4 bytes */ + remaining = remaining + (4 - (remaining & 3)); + memcpy(aligned_buffer, src, remaining); + src = aligned_buffer; + } + txlen = remaining; + } else { + txlen = (BMI_DATASZ_MAX - header); + } + offset = 0; + qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); + offset += sizeof(cid); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &address, + sizeof(address)); + offset += sizeof(address); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &txlen, sizeof(txlen)); + offset += sizeof(txlen); + qdf_mem_copy(&(bmi_cmd_buff[offset]), src, txlen); + offset += txlen; + status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff, + offset, NULL, NULL, + BMI_EXCHANGE_TIMEOUT_MS); + if (status) { + BMI_ERR("Unable to write to the device; status:%d", + status); + return QDF_STATUS_E_FAILURE; + } + remaining -= txlen; + address += txlen; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +bmi_execute(uint32_t address, A_UINT32 *param, struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + uint32_t cid; + int status; + uint32_t offset; + uint32_t param_len; + struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx); + uint8_t *bmi_cmd_buff = info->bmi_cmd_buff; + uint8_t *bmi_rsp_buff = info->bmi_rsp_buff; + uint32_t size = sizeof(cid) + sizeof(address) + sizeof(param); + qdf_dma_addr_t cmd = info->bmi_cmd_da; + qdf_dma_addr_t rsp = info->bmi_rsp_da; + + if (info->bmi_done) { + BMI_ERR("Command disallowed"); + return QDF_STATUS_E_PERM; + } + + if (!bmi_cmd_buff || !bmi_rsp_buff) { + BMI_ERR("%s:BMI CMD/RSP Buffer is NULL", __func__); + return QDF_STATUS_NOT_INITIALIZED; + } + + bmi_assert(BMI_COMMAND_FITS(size)); + qdf_mem_zero(bmi_cmd_buff, size); + qdf_mem_zero(bmi_rsp_buff, size); + + + BMI_DBG("BMI Execute: device: 0x%pK, address: 0x%x, param: %d", + scn, address, *param); + + cid = BMI_EXECUTE; + + offset = 0; + qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); + offset += sizeof(cid); + qdf_mem_copy(&(bmi_cmd_buff[offset]), &address, sizeof(address)); + offset += sizeof(address); + qdf_mem_copy(&(bmi_cmd_buff[offset]), param, sizeof(*param)); + offset += sizeof(*param); + param_len = sizeof(*param); + status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff, offset, + bmi_rsp_buff, ¶m_len, 0); + if (status) { + BMI_ERR("Unable to read from the device status:%d", status); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_copy(param, bmi_rsp_buff, sizeof(*param)); + + BMI_DBG("BMI Execute: Exit (param: %d)", *param); + return QDF_STATUS_SUCCESS; +} + +inline QDF_STATUS +bmi_no_command(struct ol_context *ol_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +bmi_firmware_download(struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + QDF_STATUS status; + struct bmi_target_info targ_info; + struct hif_target_info *tgt_info = hif_get_target_info_handle(scn); + + qdf_mem_zero(&targ_info, sizeof(targ_info)); + /* Initialize BMI */ + status = bmi_init(ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("BMI Initialization Failed err:%d", status); + return status; + } + + /* Get target information */ + status = bmi_get_target_info(&targ_info, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("BMI Target Info get failed: status:%d", status); + return status; + } + + tgt_info->target_type = targ_info.target_type; + tgt_info->target_version = targ_info.target_ver; + /* Configure target */ + status = ol_configure_target(ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("BMI Configure Target Failed status:%d", status); + return status; + } + status = ol_download_firmware(ol_ctx); + if (status != QDF_STATUS_SUCCESS) + BMI_ERR("BMI Download Firmware Failed Status:%d", status); + + return status; +} + +QDF_STATUS bmi_done_local(struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + int status; + uint32_t cid; + struct bmi_info *info; + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + qdf_dma_addr_t cmd, rsp; + + if (!scn) { + BMI_ERR("Invalid scn context"); + bmi_assert(0); + return QDF_STATUS_NOT_INITIALIZED; + } + + if (!qdf_dev->dev) { + BMI_ERR("%s: Invalid device pointer", __func__); + return QDF_STATUS_NOT_INITIALIZED; + } + + info = GET_BMI_CONTEXT(ol_ctx); + if (info->bmi_done) { + BMI_DBG(FL("skipped")); + return QDF_STATUS_E_PERM; + } + + cmd = info->bmi_cmd_da; + rsp = info->bmi_rsp_da; + + BMI_DBG("BMI Done: Enter (device: 0x%pK)", scn); + + info->bmi_done = true; + cid = BMI_DONE; + + if (!info->bmi_cmd_buff) { + BMI_ERR("Invalid scn BMICmdBuff"); + bmi_assert(0); + return QDF_STATUS_NOT_INITIALIZED; + } + + qdf_mem_copy(info->bmi_cmd_buff, &cid, sizeof(cid)); + + status = hif_exchange_bmi_msg(scn, cmd, rsp, info->bmi_cmd_buff, + sizeof(cid), NULL, NULL, 0); + if (status) { + BMI_ERR("Failed to write to the device; status:%d", status); + return QDF_STATUS_E_FAILURE; + } + + if (info->bmi_cmd_buff) { + qdf_mem_free_consistent(qdf_dev, qdf_dev->dev, + MAX_BMI_CMDBUF_SZ, + info->bmi_cmd_buff, info->bmi_cmd_da, 0); + info->bmi_cmd_buff = NULL; + info->bmi_cmd_da = 0; + } + + if (info->bmi_rsp_buff) { + qdf_mem_free_consistent(qdf_dev, qdf_dev->dev, + MAX_BMI_CMDBUF_SZ, + info->bmi_rsp_buff, info->bmi_rsp_da, 0); + info->bmi_rsp_buff = NULL; + info->bmi_rsp_da = 0; + } + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/core/bmi/src/i_ar6320v2_regtable.h b/drivers/staging/qcacld-3.0/core/bmi/src/i_ar6320v2_regtable.h new file mode 100644 index 0000000000000000000000000000000000000000..4bdf553cfe4dc1f9dcf270f1bd35c6ebcb383d06 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/bmi/src/i_ar6320v2_regtable.h @@ -0,0 +1,872 @@ +/* + * Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _AR6320V2_DBG_REGTABLE_H_ +#define _AR6320V2_DBG_REGTABLE_H_ + +#include "regtable.h" + +#define AR6320_REV2_1_REG_SIZE 0x0007F820 +#define AR6320_REV3_REG_SIZE 0x0007F820 + +/* + * Redefine the register list. To minimize the size of the array, the list must + * obey the below format. {start0, end0}, {start1, end1}, {start2, end2}....... + * The value below must obey to "start0 < end0 < start1 < end1 < start2 < ...", + * otherwise we may encouter error in the dump processing. + */ + +static const struct tgt_reg_section ar6320v2_reg_table[] = { + {0x800, 0x810}, + {0x820, 0x82C}, + {0x830, 0x8F4}, + {0x90C, 0x91C}, + {0xA14, 0xA18}, + {0xA84, 0xA94}, + {0xAA8, 0xAD4}, + {0xADC, 0xB40}, + {0x1000, 0x10A4}, + {0x10BC, 0x111C}, + {0x1134, 0x1138}, + {0x1144, 0x114C}, + {0x1150, 0x115C}, + {0x1160, 0x1178}, + {0x1240, 0x1260}, + {0x2000, 0x207C}, + {0x3000, 0x3014}, + {0x4000, 0x4014}, + {0x5000, 0x5124}, + {0x6000, 0x6040}, + {0x6080, 0x60CC}, + {0x6100, 0x611C}, + {0x6140, 0x61D8}, + {0x6200, 0x6238}, + {0x6240, 0x628C}, + {0x62C0, 0x62EC}, + {0x6380, 0x63E8}, + {0x6400, 0x6440}, + {0x6480, 0x64CC}, + {0x6500, 0x651C}, + {0x6540, 0x6580}, + {0x6600, 0x6638}, + {0x6640, 0x668C}, + {0x66C0, 0x66EC}, + {0x6780, 0x67E8}, + {0x7080, 0x708C}, + {0x70C0, 0x70C8}, + {0x7400, 0x741C}, + {0x7440, 0x7454}, + {0x7800, 0x7818}, + {0x8000, 0x8004}, + {0x8010, 0x8064}, + {0x8080, 0x8084}, + {0x80A0, 0x80A4}, + {0x80C0, 0x80C4}, + {0x80E0, 0x80F4}, + {0x8100, 0x8104}, + {0x8110, 0x812C}, + {0x9000, 0x9004}, + {0x9800, 0x982C}, + {0x9830, 0x9838}, + {0x9840, 0x986C}, + {0x9870, 0x9898}, + {0x9A00, 0x9C00}, + {0xD580, 0xD59C}, + {0xF000, 0xF0E0}, + {0xF140, 0xF190}, + {0xF250, 0xF25C}, + {0xF260, 0xF268}, + {0xF26C, 0xF2A8}, + {0x10008, 0x1000C}, + {0x10014, 0x10018}, + {0x1001C, 0x10020}, + {0x10024, 0x10028}, + {0x10030, 0x10034}, + {0x10040, 0x10054}, + {0x10058, 0x1007C}, + {0x10080, 0x100C4}, + {0x100C8, 0x10114}, + {0x1012C, 0x10130}, + {0x10138, 0x10144}, + {0x10200, 0x10220}, + {0x10230, 0x10250}, + {0x10260, 0x10280}, + {0x10290, 0x102B0}, + {0x102C0, 0x102DC}, + {0x102E0, 0x102F4}, + {0x102FC, 0x1037C}, + {0x10380, 0x10390}, + {0x10800, 0x10828}, + {0x10840, 0x10844}, + {0x10880, 0x10884}, + {0x108C0, 0x108E8}, + {0x10900, 0x10928}, + {0x10940, 0x10944}, + {0x10980, 0x10984}, + {0x109C0, 0x109E8}, + {0x10A00, 0x10A28}, + {0x10A40, 0x10A50}, + {0x11000, 0x11028}, + {0x11030, 0x11034}, + {0x11038, 0x11068}, + {0x11070, 0x11074}, + {0x11078, 0x110A8}, + {0x110B0, 0x110B4}, + {0x110B8, 0x110E8}, + {0x110F0, 0x110F4}, + {0x110F8, 0x11128}, + {0x11138, 0x11144}, + {0x11178, 0x11180}, + {0x111B8, 0x111C0}, + {0x111F8, 0x11200}, + {0x11238, 0x1123C}, + {0x11270, 0x11274}, + {0x11278, 0x1127C}, + {0x112B0, 0x112B4}, + {0x112B8, 0x112BC}, + {0x112F0, 0x112F4}, + {0x112F8, 0x112FC}, + {0x11338, 0x1133C}, + {0x11378, 0x1137C}, + {0x113B8, 0x113BC}, + {0x113F8, 0x113FC}, + {0x11438, 0x11440}, + {0x11478, 0x11480}, + {0x114B8, 0x114BC}, + {0x114F8, 0x114FC}, + {0x11538, 0x1153C}, + {0x11578, 0x1157C}, + {0x115B8, 0x115BC}, + {0x115F8, 0x115FC}, + {0x11638, 0x1163C}, + {0x11678, 0x1167C}, + {0x116B8, 0x116BC}, + {0x116F8, 0x116FC}, + {0x11738, 0x1173C}, + {0x11778, 0x1177C}, + {0x117B8, 0x117BC}, + {0x117F8, 0x117FC}, + {0x17000, 0x1701C}, + {0x17020, 0x170AC}, + {0x18000, 0x18050}, + {0x18054, 0x18074}, + {0x18080, 0x180D4}, + {0x180DC, 0x18104}, + {0x18108, 0x1813C}, + {0x18144, 0x18148}, + {0x18168, 0x18174}, + {0x18178, 0x18180}, + {0x181C8, 0x181E0}, + {0x181E4, 0x181E8}, + {0x181EC, 0x1820C}, + {0x1825C, 0x18280}, + {0x18284, 0x18290}, + {0x18294, 0x182A0}, + {0x18300, 0x18304}, + {0x18314, 0x18320}, + {0x18328, 0x18350}, + {0x1835C, 0x1836C}, + {0x18370, 0x18390}, + {0x18398, 0x183AC}, + {0x183BC, 0x183D8}, + {0x183DC, 0x183F4}, + {0x18400, 0x186F4}, + {0x186F8, 0x1871C}, + {0x18720, 0x18790}, + {0x19800, 0x19830}, + {0x19834, 0x19840}, + {0x19880, 0x1989C}, + {0x198A4, 0x198B0}, + {0x198BC, 0x19900}, + {0x19C00, 0x19C88}, + {0x19D00, 0x19D20}, + {0x19E00, 0x19E7C}, + {0x19E80, 0x19E94}, + {0x19E98, 0x19EAC}, + {0x19EB0, 0x19EBC}, + {0x19F70, 0x19F74}, + {0x19F80, 0x19F8C}, + {0x19FA0, 0x19FB4}, + {0x19FC0, 0x19FD8}, + {0x1A000, 0x1A200}, + {0x1A204, 0x1A210}, + {0x1A228, 0x1A22C}, + {0x1A230, 0x1A248}, + {0x1A250, 0x1A270}, + {0x1A280, 0x1A290}, + {0x1A2A0, 0x1A2A4}, + {0x1A2C0, 0x1A2EC}, + {0x1A300, 0x1A3BC}, + {0x1A3F0, 0x1A3F4}, + {0x1A3F8, 0x1A434}, + {0x1A438, 0x1A444}, + {0x1A448, 0x1A468}, + {0x1A580, 0x1A58C}, + {0x1A644, 0x1A654}, + {0x1A670, 0x1A698}, + {0x1A6AC, 0x1A6B0}, + {0x1A6D0, 0x1A6D4}, + {0x1A6EC, 0x1A70C}, + {0x1A710, 0x1A738}, + {0x1A7C0, 0x1A7D0}, + {0x1A7D4, 0x1A7D8}, + {0x1A7DC, 0x1A7E4}, + {0x1A7F0, 0x1A7F8}, + {0x1A888, 0x1A89C}, + {0x1A8A8, 0x1A8AC}, + {0x1A8C0, 0x1A8DC}, + {0x1A8F0, 0x1A8FC}, + {0x1AE04, 0x1AE08}, + {0x1AE18, 0x1AE24}, + {0x1AF80, 0x1AF8C}, + {0x1AFA0, 0x1AFB4}, + {0x1B000, 0x1B200}, + {0x1B284, 0x1B288}, + {0x1B2D0, 0x1B2D8}, + {0x1B2DC, 0x1B2EC}, + {0x1B300, 0x1B340}, + {0x1B374, 0x1B378}, + {0x1B380, 0x1B384}, + {0x1B388, 0x1B38C}, + {0x1B404, 0x1B408}, + {0x1B420, 0x1B428}, + {0x1B440, 0x1B444}, + {0x1B448, 0x1B44C}, + {0x1B450, 0x1B458}, + {0x1B45C, 0x1B468}, + {0x1B584, 0x1B58C}, + {0x1B68C, 0x1B690}, + {0x1B6AC, 0x1B6B0}, + {0x1B7F0, 0x1B7F8}, + {0x1C800, 0x1CC00}, + {0x1CE00, 0x1CE04}, + {0x1CF80, 0x1CF84}, + {0x1D200, 0x1D800}, + {0x1E000, 0x20014}, + {0x20100, 0x20124}, + {0x21400, 0x217A8}, + {0x21800, 0x21BA8}, + {0x21C00, 0x21FA8}, + {0x22000, 0x223A8}, + {0x22400, 0x227A8}, + {0x22800, 0x22BA8}, + {0x22C00, 0x22FA8}, + {0x23000, 0x233A8}, + {0x24000, 0x24034}, + + /* + * EFUSE0,1,2 is disabled here + * because it's state may be reset + * + * {0x24800, 0x24804}, + * {0x25000, 0x25004}, + * {0x25800, 0x25804}, + */ + + {0x26000, 0x26064}, + {0x27000, 0x27024}, + {0x34000, 0x3400C}, + {0x34400, 0x3445C}, + {0x34800, 0x3485C}, + {0x34C00, 0x34C5C}, + {0x35000, 0x3505C}, + {0x35400, 0x3545C}, + {0x35800, 0x3585C}, + {0x35C00, 0x35C5C}, + {0x36000, 0x3605C}, + {0x38000, 0x38064}, + {0x38070, 0x380E0}, + {0x3A000, 0x3A064}, + + /* DBI windows is skipped here, it can be only accessed when pcie + * is active (not in reset) and CORE_CTRL_PCIE_LTSSM_EN = 0 && + * PCIE_CTRL_APP_LTSSM_ENALBE=0. + * {0x3C000 , 0x3C004}, + */ + + {0x40000, 0x400A4}, + + /* + * SI register is skiped here. + * Because it will cause bus hang + * + * {0x50000, 0x50018}, + */ + + {0x80000, 0x8000C}, + {0x80010, 0x80020}, +}; + +#ifdef HIF_SDIO +static const struct tgt_reg_section ar6320v3_reg_table[] = { + {0x800, 0x810}, + {0x820, 0x82C}, + {0x830, 0x8F4}, + {0x90C, 0x91C}, + {0xA14, 0xA18}, + {0xA84, 0xA94}, + {0xAA8, 0xAD4}, + {0xADC, 0xB40}, + {0x1000, 0x10A4}, + {0x10BC, 0x111C}, + {0x1134, 0x1138}, + {0x1144, 0x114C}, + {0x1150, 0x115C}, + {0x1160, 0x1178}, + {0x1240, 0x1260}, + {0x2000, 0x207C}, + {0x3000, 0x3014}, + {0x4000, 0x4014}, + {0x5000, 0x5124}, + {0x6000, 0x6040}, + {0x6080, 0x60CC}, + {0x6100, 0x611C}, + {0x6140, 0x61D8}, + {0x6200, 0x6238}, + {0x6240, 0x628C}, + {0x62C0, 0x62EC}, + {0x6380, 0x63E8}, + {0x6400, 0x6440}, + {0x6480, 0x64CC}, + {0x6500, 0x651C}, + {0x6540, 0x6580}, + {0x6600, 0x6638}, + {0x6640, 0x668C}, + {0x66C0, 0x66EC}, + {0x6780, 0x67E8}, + {0x7080, 0x708C}, + {0x70C0, 0x70C8}, + {0x7400, 0x741C}, + {0x7440, 0x7454}, + {0x7800, 0x7818}, + {0x8010, 0x8060}, + {0x8080, 0x8084}, + {0x80A0, 0x80A4}, + {0x80C0, 0x80C4}, + {0x80E0, 0x80ec}, + {0x8110, 0x8128}, + {0x9000, 0x9004}, + {0xF000, 0xF0E0}, + {0xF140, 0xF190}, + {0xF250, 0xF25C}, + {0xF260, 0xF268}, + {0xF26C, 0xF2A8}, + {0x10008, 0x1000C}, + {0x10014, 0x10018}, + {0x1001C, 0x10020}, + {0x10024, 0x10028}, + {0x10030, 0x10034}, + {0x10040, 0x10054}, + {0x10058, 0x1007C}, + {0x10080, 0x100C4}, + {0x100C8, 0x10114}, + {0x1012C, 0x10130}, + {0x10138, 0x10144}, + {0x10200, 0x10220}, + {0x10230, 0x10250}, + {0x10260, 0x10280}, + {0x10290, 0x102B0}, + {0x102C0, 0x102DC}, + {0x102E0, 0x102F4}, + {0x102FC, 0x1037C}, + {0x10380, 0x10390}, + {0x10800, 0x10828}, + {0x10840, 0x10844}, + {0x10880, 0x10884}, + {0x108C0, 0x108E8}, + {0x10900, 0x10928}, + {0x10940, 0x10944}, + {0x10980, 0x10984}, + {0x109C0, 0x109E8}, + {0x10A00, 0x10A28}, + {0x10A40, 0x10A50}, + {0x11000, 0x11028}, + {0x11030, 0x11034}, + {0x11038, 0x11068}, + {0x11070, 0x11074}, + {0x11078, 0x110A8}, + {0x110B0, 0x110B4}, + {0x110B8, 0x110E8}, + {0x110F0, 0x110F4}, + {0x110F8, 0x11128}, + {0x11138, 0x11144}, + {0x11178, 0x11180}, + {0x111B8, 0x111C0}, + {0x111F8, 0x11200}, + {0x11238, 0x1123C}, + {0x11270, 0x11274}, + {0x11278, 0x1127C}, + {0x112B0, 0x112B4}, + {0x112B8, 0x112BC}, + {0x112F0, 0x112F4}, + {0x112F8, 0x112FC}, + {0x11338, 0x1133C}, + {0x11378, 0x1137C}, + {0x113B8, 0x113BC}, + {0x113F8, 0x113FC}, + {0x11438, 0x11440}, + {0x11478, 0x11480}, + {0x114B8, 0x114BC}, + {0x114F8, 0x114FC}, + {0x11538, 0x1153C}, + {0x11578, 0x1157C}, + {0x115B8, 0x115BC}, + {0x115F8, 0x115FC}, + {0x11638, 0x1163C}, + {0x11678, 0x1167C}, + {0x116B8, 0x116BC}, + {0x116F8, 0x116FC}, + {0x11738, 0x1173C}, + {0x11778, 0x1177C}, + {0x117B8, 0x117BC}, + {0x117F8, 0x117FC}, + {0x17000, 0x1701C}, + {0x17020, 0x170AC}, + {0x18000, 0x18050}, + {0x18054, 0x18074}, + {0x18080, 0x180D4}, + {0x180DC, 0x18104}, + {0x18108, 0x1813C}, + {0x18144, 0x18148}, + {0x18168, 0x18174}, + {0x18178, 0x18180}, + {0x181C8, 0x181E0}, + {0x181E4, 0x181E8}, + {0x181EC, 0x1820C}, + {0x1825C, 0x18280}, + {0x18284, 0x18290}, + {0x18294, 0x182A0}, + {0x18300, 0x18304}, + {0x18314, 0x18320}, + {0x18328, 0x18350}, + {0x1835C, 0x1836C}, + {0x18370, 0x18390}, + {0x18398, 0x183AC}, + {0x183BC, 0x183D8}, + {0x183DC, 0x183F4}, + {0x18400, 0x186F4}, + {0x186F8, 0x1871C}, + {0x18720, 0x18790}, + {0x19800, 0x19830}, + {0x19834, 0x19840}, + {0x19880, 0x1989C}, + {0x198A4, 0x198B0}, + {0x198BC, 0x19900}, + {0x19C00, 0x19C88}, + {0x19D00, 0x19D20}, + {0x19E00, 0x19E7C}, + {0x19E80, 0x19E94}, + {0x19E98, 0x19EAC}, + {0x19EB0, 0x19EBC}, + {0x19F70, 0x19F74}, + {0x19F80, 0x19F8C}, + {0x19FA0, 0x19FB4}, + {0x19FC0, 0x19FD8}, + {0x1A000, 0x1A200}, + {0x1A204, 0x1A210}, + {0x1A228, 0x1A22C}, + {0x1A230, 0x1A248}, + {0x1A250, 0x1A270}, + {0x1A280, 0x1A290}, + {0x1A2A0, 0x1A2A4}, + {0x1A2C0, 0x1A2EC}, + {0x1A300, 0x1A3BC}, + {0x1A3F0, 0x1A3F4}, + {0x1A3F8, 0x1A434}, + {0x1A438, 0x1A444}, + {0x1A448, 0x1A468}, + {0x1A580, 0x1A58C}, + {0x1A644, 0x1A654}, + {0x1A670, 0x1A698}, + {0x1A6AC, 0x1A6B0}, + {0x1A6D0, 0x1A6D4}, + {0x1A6EC, 0x1A70C}, + {0x1A710, 0x1A738}, + {0x1A7C0, 0x1A7D0}, + {0x1A7D4, 0x1A7D8}, + {0x1A7DC, 0x1A7E4}, + {0x1A7F0, 0x1A7F8}, + {0x1A888, 0x1A89C}, + {0x1A8A8, 0x1A8AC}, + {0x1A8C0, 0x1A8DC}, + {0x1A8F0, 0x1A8FC}, + {0x1AE04, 0x1AE08}, + {0x1AE18, 0x1AE24}, + {0x1AF80, 0x1AF8C}, + {0x1AFA0, 0x1AFB4}, + {0x1B000, 0x1B200}, + {0x1B284, 0x1B288}, + {0x1B2D0, 0x1B2D8}, + {0x1B2DC, 0x1B2EC}, + {0x1B300, 0x1B340}, + {0x1B374, 0x1B378}, + {0x1B380, 0x1B384}, + {0x1B388, 0x1B38C}, + {0x1B404, 0x1B408}, + {0x1B420, 0x1B428}, + {0x1B440, 0x1B444}, + {0x1B448, 0x1B44C}, + {0x1B450, 0x1B458}, + {0x1B45C, 0x1B468}, + {0x1B584, 0x1B58C}, + {0x1B68C, 0x1B690}, + {0x1B6AC, 0x1B6B0}, + {0x1B7F0, 0x1B7F8}, + {0x1C800, 0x1CC00}, + {0x1CE00, 0x1CE04}, + {0x1CF80, 0x1CF84}, + {0x1D200, 0x1D800}, + {0x1E000, 0x20014}, + {0x20100, 0x20124}, + {0x21400, 0x217A8}, + {0x21800, 0x21BA8}, + {0x21C00, 0x21FA8}, + {0x22000, 0x223A8}, + {0x22400, 0x227A8}, + {0x22800, 0x22BA8}, + {0x22C00, 0x22FA8}, + {0x23000, 0x233A8}, + {0x24000, 0x24034}, + + /* + * EFUSE0,1,2 is disabled here + * because it's state may be reset + * + * {0x24800, 0x24804}, + * {0x25000, 0x25004}, + * {0x25800, 0x25804}, + */ + + {0x26000, 0x26064}, + {0x27000, 0x27024}, + {0x34000, 0x3400C}, + {0x34400, 0x3445C}, + {0x34800, 0x3485C}, + {0x34C00, 0x34C5C}, + {0x35000, 0x3505C}, + {0x35400, 0x3545C}, + {0x35800, 0x3585C}, + {0x35C00, 0x35C5C}, + {0x36000, 0x3605C}, + {0x38000, 0x38064}, + {0x38070, 0x380E0}, + {0x3A000, 0x3A074}, + + /* + * DBI windows is skipped here, it can be only accessed when pcie + * is active (not in reset) and CORE_CTRL_PCIE_LTSSM_EN = 0 && + * PCIE_CTRL_APP_LTSSM_ENALBE=0. + * {0x3C000 , 0x3C004}, + */ + + {0x40000, 0x400A4}, + + /* + * SI register is skiped here. + * Because it will cause bus hang + * + * {0x50000, 0x50018}, + */ + + {0x80000, 0x8000C}, + {0x80010, 0x80020}, +}; +#else +static const struct tgt_reg_section ar6320v3_reg_table[] = { + {0x800, 0x810}, + {0x820, 0x82C}, + {0x830, 0x8F4}, + {0x90C, 0x91C}, + {0xA14, 0xA18}, + {0xA84, 0xA94}, + {0xAA8, 0xAD4}, + {0xADC, 0xB40}, + {0x1000, 0x10A4}, + {0x10BC, 0x111C}, + {0x1134, 0x1138}, + {0x1144, 0x114C}, + {0x1150, 0x115C}, + {0x1160, 0x1178}, + {0x1240, 0x1260}, + {0x2000, 0x207C}, + {0x3000, 0x3014}, + {0x4000, 0x4014}, + {0x5000, 0x5124}, + {0x6000, 0x6040}, + {0x6080, 0x60CC}, + {0x6100, 0x611C}, + {0x6140, 0x61D8}, + {0x6200, 0x6238}, + {0x6240, 0x628C}, + {0x62C0, 0x62EC}, + {0x6380, 0x63E8}, + {0x6400, 0x6440}, + {0x6480, 0x64CC}, + {0x6500, 0x651C}, + {0x6540, 0x6580}, + {0x6600, 0x6638}, + {0x6640, 0x668C}, + {0x66C0, 0x66EC}, + {0x6780, 0x67E8}, + {0x7080, 0x708C}, + {0x70C0, 0x70C8}, + {0x7400, 0x741C}, + {0x7440, 0x7454}, + {0x7800, 0x7818}, + {0x8000, 0x8004}, + {0x8010, 0x8064}, + {0x8080, 0x8084}, + {0x80A0, 0x80A4}, + {0x80C0, 0x80C4}, + {0x80E0, 0x80F4}, + {0x8100, 0x8104}, + {0x8110, 0x812C}, + {0x9000, 0x9004}, + {0x9800, 0x982C}, + {0x9830, 0x9838}, + {0x9840, 0x986C}, + {0x9870, 0x9898}, + {0x9A00, 0x9C00}, + {0xD580, 0xD59C}, + {0xF000, 0xF0E0}, + {0xF140, 0xF190}, + {0xF250, 0xF25C}, + {0xF260, 0xF268}, + {0xF26C, 0xF2A8}, + {0x10008, 0x1000C}, + {0x10014, 0x10018}, + {0x1001C, 0x10020}, + {0x10024, 0x10028}, + {0x10030, 0x10034}, + {0x10040, 0x10054}, + {0x10058, 0x1007C}, + {0x10080, 0x100C4}, + {0x100C8, 0x10114}, + {0x1012C, 0x10130}, + {0x10138, 0x10144}, + {0x10200, 0x10220}, + {0x10230, 0x10250}, + {0x10260, 0x10280}, + {0x10290, 0x102B0}, + {0x102C0, 0x102DC}, + {0x102E0, 0x102F4}, + {0x102FC, 0x1037C}, + {0x10380, 0x10390}, + {0x10800, 0x10828}, + {0x10840, 0x10844}, + {0x10880, 0x10884}, + {0x108C0, 0x108E8}, + {0x10900, 0x10928}, + {0x10940, 0x10944}, + {0x10980, 0x10984}, + {0x109C0, 0x109E8}, + {0x10A00, 0x10A28}, + {0x10A40, 0x10A50}, + {0x11000, 0x11028}, + {0x11030, 0x11034}, + {0x11038, 0x11068}, + {0x11070, 0x11074}, + {0x11078, 0x110A8}, + {0x110B0, 0x110B4}, + {0x110B8, 0x110E8}, + {0x110F0, 0x110F4}, + {0x110F8, 0x11128}, + {0x11138, 0x11144}, + {0x11178, 0x11180}, + {0x111B8, 0x111C0}, + {0x111F8, 0x11200}, + {0x11238, 0x1123C}, + {0x11270, 0x11274}, + {0x11278, 0x1127C}, + {0x112B0, 0x112B4}, + {0x112B8, 0x112BC}, + {0x112F0, 0x112F4}, + {0x112F8, 0x112FC}, + {0x11338, 0x1133C}, + {0x11378, 0x1137C}, + {0x113B8, 0x113BC}, + {0x113F8, 0x113FC}, + {0x11438, 0x11440}, + {0x11478, 0x11480}, + {0x114B8, 0x114BC}, + {0x114F8, 0x114FC}, + {0x11538, 0x1153C}, + {0x11578, 0x1157C}, + {0x115B8, 0x115BC}, + {0x115F8, 0x115FC}, + {0x11638, 0x1163C}, + {0x11678, 0x1167C}, + {0x116B8, 0x116BC}, + {0x116F8, 0x116FC}, + {0x11738, 0x1173C}, + {0x11778, 0x1177C}, + {0x117B8, 0x117BC}, + {0x117F8, 0x117FC}, + {0x17000, 0x1701C}, + {0x17020, 0x170AC}, + {0x18000, 0x18050}, + {0x18054, 0x18074}, + {0x18080, 0x180D4}, + {0x180DC, 0x18104}, + {0x18108, 0x1813C}, + {0x18144, 0x18148}, + {0x18168, 0x18174}, + {0x18178, 0x18180}, + {0x181C8, 0x181E0}, + {0x181E4, 0x181E8}, + {0x181EC, 0x1820C}, + {0x1825C, 0x18280}, + {0x18284, 0x18290}, + {0x18294, 0x182A0}, + {0x18300, 0x18304}, + {0x18314, 0x18320}, + {0x18328, 0x18350}, + {0x1835C, 0x1836C}, + {0x18370, 0x18390}, + {0x18398, 0x183AC}, + {0x183BC, 0x183D8}, + {0x183DC, 0x183F4}, + {0x18400, 0x186F4}, + {0x186F8, 0x1871C}, + {0x18720, 0x18790}, + {0x19800, 0x19830}, + {0x19834, 0x19840}, + {0x19880, 0x1989C}, + {0x198A4, 0x198B0}, + {0x198BC, 0x19900}, + {0x19C00, 0x19C88}, + {0x19D00, 0x19D20}, + {0x19E00, 0x19E7C}, + {0x19E80, 0x19E94}, + {0x19E98, 0x19EAC}, + {0x19EB0, 0x19EBC}, + {0x19F70, 0x19F74}, + {0x19F80, 0x19F8C}, + {0x19FA0, 0x19FB4}, + {0x19FC0, 0x19FD8}, + {0x1A000, 0x1A200}, + {0x1A204, 0x1A210}, + {0x1A228, 0x1A22C}, + {0x1A230, 0x1A248}, + {0x1A250, 0x1A270}, + {0x1A280, 0x1A290}, + {0x1A2A0, 0x1A2A4}, + {0x1A2C0, 0x1A2EC}, + {0x1A300, 0x1A3BC}, + {0x1A3F0, 0x1A3F4}, + {0x1A3F8, 0x1A434}, + {0x1A438, 0x1A444}, + {0x1A448, 0x1A468}, + {0x1A580, 0x1A58C}, + {0x1A644, 0x1A654}, + {0x1A670, 0x1A698}, + {0x1A6AC, 0x1A6B0}, + {0x1A6D0, 0x1A6D4}, + {0x1A6EC, 0x1A70C}, + {0x1A710, 0x1A738}, + {0x1A7C0, 0x1A7D0}, + {0x1A7D4, 0x1A7D8}, + {0x1A7DC, 0x1A7E4}, + {0x1A7F0, 0x1A7F8}, + {0x1A888, 0x1A89C}, + {0x1A8A8, 0x1A8AC}, + {0x1A8C0, 0x1A8DC}, + {0x1A8F0, 0x1A8FC}, + {0x1AE04, 0x1AE08}, + {0x1AE18, 0x1AE24}, + {0x1AF80, 0x1AF8C}, + {0x1AFA0, 0x1AFB4}, + {0x1B000, 0x1B200}, + {0x1B284, 0x1B288}, + {0x1B2D0, 0x1B2D8}, + {0x1B2DC, 0x1B2EC}, + {0x1B300, 0x1B340}, + {0x1B374, 0x1B378}, + {0x1B380, 0x1B384}, + {0x1B388, 0x1B38C}, + {0x1B404, 0x1B408}, + {0x1B420, 0x1B428}, + {0x1B440, 0x1B444}, + {0x1B448, 0x1B44C}, + {0x1B450, 0x1B458}, + {0x1B45C, 0x1B468}, + {0x1B584, 0x1B58C}, + {0x1B68C, 0x1B690}, + {0x1B6AC, 0x1B6B0}, + {0x1B7F0, 0x1B7F8}, + {0x1C800, 0x1CC00}, + {0x1CE00, 0x1CE04}, + {0x1CF80, 0x1CF84}, + {0x1D200, 0x1D800}, + {0x1E000, 0x20014}, + {0x20100, 0x20124}, + {0x21400, 0x217A8}, + {0x21800, 0x21BA8}, + {0x21C00, 0x21FA8}, + {0x22000, 0x223A8}, + {0x22400, 0x227A8}, + {0x22800, 0x22BA8}, + {0x22C00, 0x22FA8}, + {0x23000, 0x233A8}, + {0x24000, 0x24034}, + + /* + * EFUSE0,1,2 is disabled here + * because it's state may be reset + * + * {0x24800, 0x24804}, + * {0x25000, 0x25004}, + * {0x25800, 0x25804}, + */ + + {0x26000, 0x26064}, + {0x27000, 0x27024}, + {0x34000, 0x3400C}, + {0x34400, 0x3445C}, + {0x34800, 0x3485C}, + {0x34C00, 0x34C5C}, + {0x35000, 0x3505C}, + {0x35400, 0x3545C}, + {0x35800, 0x3585C}, + {0x35C00, 0x35C5C}, + {0x36000, 0x3605C}, + {0x38000, 0x38064}, + {0x38070, 0x380E0}, + {0x3A000, 0x3A074}, + + /* + * DBI windows is skipped here, it can be only accessed when pcie + * is active (not in reset) and CORE_CTRL_PCIE_LTSSM_EN = 0 && + * PCIE_CTRL_APP_LTSSM_ENALBE=0. + * {0x3C000 , 0x3C004}, + */ + + {0x40000, 0x400A4}, + + /* + * SI register is skiped here. + * Because it will cause bus hang + * + * {0x50000, 0x50018}, + */ + + {0x80000, 0x8000C}, + {0x80010, 0x80020}, +}; +#endif +#endif /* #ifndef _AR6320V2_DBG_REGTABLE_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/bmi/src/i_bmi.h b/drivers/staging/qcacld-3.0/core/bmi/src/i_bmi.h new file mode 100644 index 0000000000000000000000000000000000000000..bde7417987a7c6b8f91b79ce1c35f50cf7f70341 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/bmi/src/i_bmi.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* =================================================================== + * Internal BMI Header File + */ + +#ifndef _I_BMI_H_ +#define _I_BMI_H_ + +#include "qdf_types.h" +#include "qdf_defer.h" +#include "hif.h" +#include "bmi_msg.h" +#include "bmi.h" +#include "ol_fw.h" +#include "pld_common.h" + +/* + * Note that not all the register locations are accessible. + * A list of accessible target registers are specified with + * their start and end addresses in a table for given target + * version. We should NOT access other locations as either + * they are invalid locations or host does not have read + * access to it or the value of the particular register + * read might change + */ +#define REGISTER_LOCATION 0x00000800 + +#define DRAM_LOCATION 0x00400000 +#ifdef HIF_PCI +#define DRAM_SIZE 0x000a8000 +#else +#define DRAM_SIZE 0x00098000 +#endif +/* The local base addr is used to read the target dump using pcie I/O reads */ +#define DRAM_LOCAL_BASE_ADDR (0x100000) + +/* Target IRAM config */ +#define FW_RAM_CONFIG_ADDRESS 0x0018 +#define IRAM1_LOCATION 0x00980000 +#define IRAM1_SIZE 0x00080000 +#define IRAM2_LOCATION 0x00a00000 +#define IRAM2_SIZE 0x00040000 +#ifdef HIF_SDIO +#define IRAM_LOCATION 0x00980000 +#define IRAM_SIZE 0x000C0000 +#else +#define IRAM_LOCATION 0x00980000 +#define IRAM_SIZE 0x00038000 +#endif + +#define AXI_LOCATION 0x000a0000 +#ifdef HIF_PCI +#define AXI_SIZE 0x00018000 +#else +#define AXI_SIZE 0x00020000 +#endif + +#define PCIE_READ_LIMIT 0x00005000 + +#define SHA256_DIGEST_SIZE 32 + +/* BMI LOGGING WRAPPERS */ + +#define BMI_LOG(level, args...) QDF_TRACE(QDF_MODULE_ID_BMI, \ + level, ##args) +#define BMI_ERR(args ...) BMI_LOG(QDF_TRACE_LEVEL_ERROR, args) +#define BMI_DBG(args ...) BMI_LOG(QDF_TRACE_LEVEL_DEBUG, args) +#define BMI_WARN(args ...) BMI_LOG(QDF_TRACE_LEVEL_WARN, args) +#define BMI_INFO(args ...) BMI_LOG(QDF_TRACE_LEVEL_INFO, args) +/* End of BMI Logging Wrappers */ + +/* BMI Assert Wrappers */ +#define bmi_assert QDF_BUG +/* + * Although we had envisioned BMI to run on top of HTC, this is not how the + * final implementation ended up. On the Target side, BMI is a part of the BSP + * and does not use the HTC protocol nor even DMA -- it is intentionally kept + * very simple. + */ + +#define MAX_BMI_CMDBUF_SZ (BMI_DATASZ_MAX + \ + sizeof(uint32_t) /* cmd */ + \ + sizeof(uint32_t) /* addr */ + \ + sizeof(uint32_t)) /* length */ +#define BMI_COMMAND_FITS(sz) ((sz) <= MAX_BMI_CMDBUF_SZ) +#define BMI_EXCHANGE_TIMEOUT_MS 1000 + +struct hash_fw { + u8 qwlan[SHA256_DIGEST_SIZE]; + u8 otp[SHA256_DIGEST_SIZE]; + u8 bdwlan[SHA256_DIGEST_SIZE]; + u8 utf[SHA256_DIGEST_SIZE]; +}; + +enum ATH_BIN_FILE { + ATH_OTP_FILE, + ATH_FIRMWARE_FILE, + ATH_PATCH_FILE, + ATH_BOARD_DATA_FILE, + ATH_FLASH_FILE, + ATH_SETUP_FILE, +}; + +#if defined(QCA_WIFI_3_0_ADRASTEA) +#define NO_BMI 1 +#else +#define NO_BMI 0 +#endif + +/** + * struct bmi_info - Structure to hold BMI Specific information + * @bmi_cmd_buff - BMI Command Buffer + * @bmi_rsp_buff - BMI Response Buffer + * @bmi_cmd_da - BMI Command Physical address + * @bmi_rsp_da - BMI Response Physical address + * @bmi_done - Flag to check if BMI Phase is complete + * @board_id - board ID + * @fw_files - FW files + * + */ +struct bmi_info { + uint8_t *bmi_cmd_buff; + uint8_t *bmi_rsp_buff; + dma_addr_t bmi_cmd_da; + dma_addr_t bmi_rsp_da; + bool bmi_done; + uint16_t board_id; + struct pld_fw_files fw_files; +}; + +/** + * struct ol_context - Structure to hold OL context + * @bmi: BMI info + * @cal_in_flash: For Firmware Flash Download + * @qdf_dev: QDF Device + * @scn: HIF Context + * @ramdump_work: Work for Ramdump collection + * @fw_indication_work: Work for Fw inciation + * @tgt_def: Target Defnition pointer + * @fw_crashed_cb: Callback for firmware crashed ind + * + * Structure to hold all ol BMI/Ramdump info + */ +struct ol_context { + struct bmi_info bmi; + struct ol_config_info cfg_info; + uint8_t *cal_in_flash; + qdf_device_t qdf_dev; + qdf_work_t ramdump_work; + qdf_work_t fw_indication_work; + struct hif_opaque_softc *scn; + struct targetdef_t { + struct targetdef_s *targetdef; + } tgt_def; + void (*fw_crashed_cb)(void); +}; + +#define GET_BMI_CONTEXT(ol_ctx) ((struct bmi_info *)ol_ctx) + +QDF_STATUS bmi_execute(uint32_t address, uint32_t *param, + struct ol_context *ol_ctx); +QDF_STATUS bmi_init(struct ol_context *ol_ctx); +QDF_STATUS bmi_no_command(struct ol_context *ol_ctx); +QDF_STATUS bmi_read_memory(uint32_t address, uint8_t *buffer, uint32_t length, + struct ol_context *ol_ctx); +QDF_STATUS bmi_write_memory(uint32_t address, uint8_t *buffer, uint32_t length, + struct ol_context *ol_ctx); +QDF_STATUS bmi_fast_download(uint32_t address, uint8_t *buffer, uint32_t length, + struct ol_context *ol_ctx); +QDF_STATUS bmi_read_soc_register(uint32_t address, + uint32_t *param, struct ol_context *ol_ctx); +QDF_STATUS bmi_write_soc_register(uint32_t address, uint32_t param, + struct ol_context *ol_ctx); +QDF_STATUS bmi_get_target_info(struct bmi_target_info *targ_info, + struct ol_context *ol_ctx); +QDF_STATUS bmi_firmware_download(struct ol_context *ol_ctx); +QDF_STATUS bmi_done_local(struct ol_context *ol_ctx); +QDF_STATUS ol_download_firmware(struct ol_context *ol_ctx); +QDF_STATUS ol_configure_target(struct ol_context *ol_ctx); +QDF_STATUS bmi_sign_stream_start(uint32_t address, uint8_t *buffer, + uint32_t length, struct ol_context *ol_ctx); +void ramdump_work_handler(void *arg); +void fw_indication_work_handler(void *arg); +struct ol_config_info *ol_get_ini_handle(struct ol_context *ol_ctx); + +#ifdef HIF_SDIO +QDF_STATUS hif_reg_based_get_target_info(struct hif_opaque_softc *hif_ctx, + struct bmi_target_info *targ_info); +#endif +#if defined(HIF_PCI) || defined(HIF_SNOC) || defined(HIF_AHB) || defined(HIF_USB) +static inline QDF_STATUS +hif_reg_based_get_target_info(struct hif_opaque_softc *hif_ctx, + struct bmi_target_info *targ_info) +{ + return QDF_STATUS_SUCCESS; +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/bmi/src/ol_fw.c b/drivers/staging/qcacld-3.0/core/bmi/src/ol_fw.c new file mode 100644 index 0000000000000000000000000000000000000000..8dbc58beba2927463e0e75b22919e5a439592f19 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/bmi/src/ol_fw.c @@ -0,0 +1,1948 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "ol_if_athvar.h" +#include "qdf_time.h" +#include "targaddrs.h" +#include "ol_cfg.h" +#include "cds_api.h" +#include "wma_api.h" +#include "wma.h" +#include "bin_sig.h" +#include "i_ar6320v2_regtable.h" +#include "epping_main.h" +#ifdef HIF_PCI +#include "ce_reg.h" +#endif +#if defined(HIF_SDIO) +#include "if_sdio.h" +#include "regtable_sdio.h" +#endif +#if defined(HIF_USB) +#include "if_usb.h" +#include "regtable_usb.h" +#endif +#include "pld_common.h" +#include "hif_main.h" + +#include "i_bmi.h" +#include "qwlan_version.h" +#include "wlan_policy_mgr_api.h" +#include "dbglog_host.h" + +#ifdef FEATURE_SECURE_FIRMWARE +static struct hash_fw fw_hash; +#endif + +static uint32_t refclk_speed_to_hz[] = { + 48000000, /* SOC_REFCLK_48_MHZ */ + 19200000, /* SOC_REFCLK_19_2_MHZ */ + 24000000, /* SOC_REFCLK_24_MHZ */ + 26000000, /* SOC_REFCLK_26_MHZ */ + 37400000, /* SOC_REFCLK_37_4_MHZ */ + 38400000, /* SOC_REFCLK_38_4_MHZ */ + 40000000, /* SOC_REFCLK_40_MHZ */ + 52000000, /* SOC_REFCLK_52_MHZ */ +}; + +static int ol_target_coredump(void *inst, void *memory_block, + uint32_t block_len); + +#ifdef FEATURE_SECURE_FIRMWARE +static int ol_check_fw_hash(struct device *dev, const u8 *data, + u32 fw_size, enum ATH_BIN_FILE file) +{ + u8 *hash = NULL; + u8 *fw_mem = NULL; + u8 digest[SHA256_DIGEST_SIZE]; + u8 temp[SHA256_DIGEST_SIZE] = { }; + int ret = 0; + + switch (file) { + case ATH_BOARD_DATA_FILE: + hash = fw_hash.bdwlan; + break; + case ATH_OTP_FILE: + hash = fw_hash.otp; + break; + case ATH_FIRMWARE_FILE: +#ifdef QCA_WIFI_FTM + if (cds_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hash = fw_hash.utf; + break; + } +#endif + hash = fw_hash.qwlan; + default: + break; + } + + if (!hash) { + BMI_INFO("No entry for file:%d Download FW in non-secure mode", + file); + goto end; + } + + if (qdf_mem_cmp(hash, temp, SHA256_DIGEST_SIZE)) { + BMI_INFO("Download FW in non-secure mode:%d", file); + goto end; + } + + fw_mem = pld_get_fw_ptr(dev); + if (!fw_mem || (fw_size > MAX_FIRMWARE_SIZE)) { + BMI_ERR("No Memory to copy FW data"); + ret = -1; + goto end; + } + qdf_mem_copy(fw_mem, data, fw_size); + + ret = pld_get_sha_hash(dev, fw_mem, fw_size, "sha256", digest); + + if (ret) { + BMI_ERR("Sha256 Hash computation failed err:%d", ret); + goto end; + } + + if (qdf_mem_cmp(hash, digest, SHA256_DIGEST_SIZE)) { + BMI_ERR("Hash Mismatch"); + qdf_trace_hex_dump(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL, + digest, SHA256_DIGEST_SIZE); + qdf_trace_hex_dump(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL, + hash, SHA256_DIGEST_SIZE); + ret = QDF_STATUS_E_FAILURE; + } +end: + return ret; +} +#endif + +/** + * ol_board_id_to_filename() - Auto BDF board_id to filename conversion + * @old_name: name of the default board data file + * @board_id: board ID + * + * The API return board filename based on the board_id and chip_id. + * eg: input = "bdwlan30.bin", board_id = 0x01, board_file = "bdwlan30.b01" + * Return: The buffer with the formated board filename. + */ +static char *ol_board_id_to_filename(const char *old_name, + uint16_t board_id) +{ + int name_len; + char *new_name; + + name_len = strlen(old_name); + new_name = qdf_mem_malloc(name_len + 1); + + if (!new_name) + goto out; + + if (board_id > 0xFF) + board_id = 0x0; + + qdf_mem_copy(new_name, old_name, name_len); + snprintf(&new_name[name_len - 2], 3, "%.2x", board_id); +out: + return new_name; +} + +#ifdef QCA_SIGNED_SPLIT_BINARY_SUPPORT +#define SIGNED_SPLIT_BINARY_VALUE true +#else +#define SIGNED_SPLIT_BINARY_VALUE false +#endif + +static int +__ol_transfer_bin_file(struct ol_context *ol_ctx, enum ATH_BIN_FILE file, + uint32_t address, bool compressed) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + int status = 0; + const char *filename; + const struct firmware *fw_entry; + uint32_t fw_entry_size; + uint8_t *temp_eeprom; + uint32_t board_data_size; + bool bin_sign = false; + int bin_off, bin_len; + SIGN_HEADER_T *sign_header; + struct hif_target_info *tgt_info = hif_get_target_info_handle(scn); + uint32_t target_type = tgt_info->target_type; + struct bmi_info *bmi_ctx = GET_BMI_CONTEXT(ol_ctx); + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + int i; + + /* + * If there is no board data file bases on board id, the default + * board data file should be used. + * For factory mode, the sequence for file selection should be + * utfbd.board_id -> utfbd.bin -> bd.board_id -> bd.bin. So we + * need to cache 4 file names. + */ + uint32_t bd_files = 1; + char *bd_id_filename[2] = {NULL, NULL}; + const char *bd_filename[2] = {NULL, NULL}; + + switch (file) { + default: + BMI_ERR("%s: Unknown file type", __func__); + return -EINVAL; + case ATH_OTP_FILE: + filename = bmi_ctx->fw_files.otp_data; + if (SIGNED_SPLIT_BINARY_VALUE) + bin_sign = true; + + break; + case ATH_FIRMWARE_FILE: + if (QDF_IS_EPPING_ENABLED(cds_get_conparam())) { + filename = bmi_ctx->fw_files.epping_file; + BMI_INFO("%s: Loading epping firmware file %s", + __func__, filename); + break; + } +#ifdef QCA_WIFI_FTM + if (cds_get_conparam() == QDF_GLOBAL_FTM_MODE) { + filename = bmi_ctx->fw_files.utf_file; + if (SIGNED_SPLIT_BINARY_VALUE) + bin_sign = true; + BMI_INFO("%s: Loading firmware file %s", + __func__, filename); + break; + } +#endif + if (cds_get_conparam() == QDF_GLOBAL_IBSS_MODE && + (bmi_ctx->fw_files.ibss_image_file[0] != '\0')) { + filename = bmi_ctx->fw_files.ibss_image_file; + } else { + filename = bmi_ctx->fw_files.image_file; + } + + if (SIGNED_SPLIT_BINARY_VALUE) + bin_sign = true; + break; + case ATH_PATCH_FILE: + BMI_INFO("%s: no Patch file defined", __func__); + return 0; + case ATH_BOARD_DATA_FILE: + filename = bmi_ctx->fw_files.board_data; +#ifdef QCA_WIFI_FTM + if (cds_get_conparam() == QDF_GLOBAL_FTM_MODE) { + filename = bmi_ctx->fw_files.utf_board_data; + if (SIGNED_SPLIT_BINARY_VALUE) + bin_sign = true; + + BMI_INFO("%s: Loading board data file %s", + __func__, filename); + + /* + * In FTM mode, if utf files do not exit. + * bdwlan should be used. + */ + bd_files = 2; + } +#endif /* QCA_WIFI_FTM */ + if (SIGNED_SPLIT_BINARY_VALUE) + bin_sign = false; + + bd_filename[0] = filename; + + /* + * For factory mode, we should cache 2 group of file names. + * For mission mode, bd_files==1, only one group of file names. + */ + bd_filename[bd_files - 1] = + bmi_ctx->fw_files.board_data; + for (i = 0; i < bd_files; i++) { + bd_id_filename[i] = + ol_board_id_to_filename(bd_filename[i], + bmi_ctx->board_id); + if (bd_id_filename[i]) { + BMI_INFO("%s: board data file is %s", + __func__, bd_id_filename[i]); + } else { + BMI_ERR("%s: Fail to allocate board filename", + __func__); + } + } + break; + case ATH_SETUP_FILE: + if (cds_get_conparam() != QDF_GLOBAL_FTM_MODE && + !QDF_IS_EPPING_ENABLED(cds_get_conparam())) { + filename = bmi_ctx->fw_files.setup_file; + if (filename[0] == 0) { + BMI_INFO("%s: no Setup file defined", __func__); + return -EPERM; + } + + if (SIGNED_SPLIT_BINARY_VALUE) + bin_sign = true; + + BMI_INFO("%s: Loading setup file %s", + __func__, filename); + } else { + BMI_INFO("%s: no Setup file needed", __func__); + return -EPERM; + } + break; + } + + /* For FTM mode. bd.bin is used if there is no utf.bin */ + if (file == ATH_BOARD_DATA_FILE) { + for (i = 0; i < bd_files; i++) { + if (bd_id_filename[i]) { + BMI_DBG("%s: Trying to load %s", + __func__, bd_id_filename[i]); + status = request_firmware(&fw_entry, + bd_id_filename[i], + qdf_dev->dev); + if (!status) + break; + BMI_ERR("%s: Failed to get %s:%d", + __func__, bd_id_filename[i], + status); + } + + /* bd.board_id not exits, using bd.bin */ + BMI_DBG("%s: Trying to load default %s", + __func__, bd_filename[i]); + status = request_firmware(&fw_entry, bd_filename[i], + qdf_dev->dev); + if (!status) + break; + BMI_ERR("%s: Failed to get default %s:%d", + __func__, bd_filename[i], status); + } + } else { + status = request_firmware(&fw_entry, filename, qdf_dev->dev); + } + + if (status) { + BMI_ERR("%s: Failed to get %s", __func__, filename); + status = -ENOENT; + goto release_fw; + } + + if (!fw_entry || !fw_entry->data) { + BMI_ERR("Invalid fw_entries"); + status = -ENOENT; + goto release_fw; + } + + fw_entry_size = fw_entry->size; + temp_eeprom = NULL; + +#ifdef FEATURE_SECURE_FIRMWARE + if (ol_check_fw_hash(qdf_dev->dev, fw_entry->data, + fw_entry_size, file)) { + BMI_ERR("Hash Check failed for file:%s", filename); + status = -EINVAL; + goto end; + } +#endif + + if (file == ATH_BOARD_DATA_FILE) { + uint32_t board_ext_address = 0; + int32_t board_ext_data_size; + + temp_eeprom = qdf_mem_malloc(fw_entry_size); + if (!temp_eeprom) { + BMI_ERR("%s: Memory allocation failed", __func__); + status = -ENOMEM; + goto release_fw; + } + + qdf_mem_copy(temp_eeprom, (uint8_t *) fw_entry->data, + fw_entry_size); + + switch (target_type) { + case TARGET_TYPE_AR6004: + board_data_size = AR6004_BOARD_DATA_SZ; + board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ; + break; + case TARGET_TYPE_AR9888: + board_data_size = AR9888_BOARD_DATA_SZ; + board_ext_data_size = AR9888_BOARD_EXT_DATA_SZ; + break; + default: + board_data_size = 0; + board_ext_data_size = 0; + break; + } + + /* Determine where in Target RAM to write Board Data */ + bmi_read_memory(HOST_INTEREST_ITEM_ADDRESS(target_type, + hi_board_ext_data), + (uint8_t *) &board_ext_address, 4, ol_ctx); + BMI_INFO("Board extended Data download address: 0x%x", + board_ext_address); + + /* Check whether the target has allocated memory for extended + * board data and file contains extended board data + */ + + if ((board_ext_address) + && (fw_entry_size == + (board_data_size + board_ext_data_size))) { + uint32_t param; + + status = bmi_write_memory(board_ext_address, + (uint8_t *)(temp_eeprom + + board_data_size), + board_ext_data_size, ol_ctx); + + if (status) + goto end; + + /* Record extended board Data initialized */ + param = (board_ext_data_size << 16) | 1; + bmi_write_memory( + HOST_INTEREST_ITEM_ADDRESS(target_type, + hi_board_ext_data_config), + (uint8_t *)¶m, 4, ol_ctx); + + fw_entry_size = board_data_size; + } + } + + if (bin_sign && SIGNED_SPLIT_BINARY_VALUE) { + uint32_t chip_id; + + if (fw_entry_size < sizeof(SIGN_HEADER_T)) { + BMI_ERR("Invalid binary size %d", fw_entry_size); + status = -EINVAL; + goto end; + } + + sign_header = (SIGN_HEADER_T *) fw_entry->data; + chip_id = cpu_to_le32(sign_header->product_id); + if (sign_header->magic_num == SIGN_HEADER_MAGIC + && (chip_id == AR6320_REV1_1_VERSION + || chip_id == AR6320_REV1_3_VERSION + || chip_id == AR6320_REV2_1_VERSION)) { + + bin_off = sizeof(SIGN_HEADER_T); + status = bmi_sign_stream_start(address, + (uint8_t *)fw_entry->data, + bin_off, ol_ctx); + if (status) { + BMI_ERR("unable to start sign stream"); + status = -EINVAL; + goto end; + } + + bin_len = sign_header->rampatch_len - bin_off; + if (bin_len <= 0 || bin_len > fw_entry_size - bin_off) { + BMI_ERR("Invalid sign header"); + status = -EINVAL; + goto end; + } + } else { + bin_sign = false; + bin_off = 0; + bin_len = fw_entry_size; + } + } else { + bin_len = fw_entry_size; + bin_off = 0; + } + + if (compressed) { + status = bmi_fast_download(address, + (uint8_t *) fw_entry->data + bin_off, + bin_len, ol_ctx); + } else { + if (file == ATH_BOARD_DATA_FILE && fw_entry->data) { + status = bmi_write_memory(address, + (uint8_t *) temp_eeprom, + fw_entry_size, ol_ctx); + } else { + status = bmi_write_memory(address, + (uint8_t *) fw_entry->data + + bin_off, bin_len, ol_ctx); + } + } + + if (bin_sign && SIGNED_SPLIT_BINARY_VALUE) { + bin_off += bin_len; + bin_len = sign_header->total_len - sign_header->rampatch_len; + + if (bin_len > 0 && bin_len <= fw_entry_size - bin_off) { + status = bmi_sign_stream_start(0, + (uint8_t *)fw_entry->data + + bin_off, bin_len, ol_ctx); + if (status) + BMI_ERR("sign stream error"); + } + } + +end: + if (temp_eeprom) + qdf_mem_free(temp_eeprom); + +release_fw: + if (fw_entry) + release_firmware(fw_entry); + + for (i = 0; i < bd_files; i++) { + if (bd_id_filename[i]) { + qdf_mem_free(bd_id_filename[i]); + bd_id_filename[i] = NULL; + } + } + + if (status) + BMI_ERR("%s, BMI operation failed: %d", __func__, __LINE__); + else + BMI_INFO("transferring file: %s size %d bytes done!", + (filename) ? filename : " ", fw_entry_size); + return status; +} + +static int +ol_transfer_bin_file(struct ol_context *ol_ctx, enum ATH_BIN_FILE file, + uint32_t address, bool compressed) +{ + int ret; + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + + /* Wait until suspend and resume are completed before loading FW */ + pld_lock_pm_sem(qdf_dev->dev); + + ret = __ol_transfer_bin_file(ol_ctx, file, address, compressed); + + pld_release_pm_sem(qdf_dev->dev); + + return ret; +} + +/** + * struct ramdump_info: Structure to hold ramdump information + * @base: Base address for Ramdump collection + * @size: Size of the dump + * + * Ramdump information. + */ +struct ramdump_info { + void *base; + unsigned long size; +}; + +/** + * if have platform driver support, reinit will be called by CNSS. + * recovery flag will be cleaned and CRASHED indication will be sent + * to user space by reinit function. If not support, clean recovery + * flag and send CRASHED indication in CLD driver. + */ +static inline void ol_check_clean_recovery_flag(struct ol_context *ol_ctx) +{ + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + + if (!pld_have_platform_driver_support(qdf_dev->dev)) { + cds_set_recovery_in_progress(false); + if (ol_ctx->fw_crashed_cb) + ol_ctx->fw_crashed_cb(); + } +} + +#if !defined(QCA_WIFI_3_0) +static inline void ol_get_ramdump_mem(struct device *dev, + struct ramdump_info *info) +{ + info->base = pld_get_virt_ramdump_mem(dev, &info->size); +} + +static inline void ol_release_ramdump_mem(struct device *dev, + struct ramdump_info *info) +{ + pld_release_virt_ramdump_mem(dev, info->base); +} +#else +static inline void ol_get_ramdump_mem(struct device *dev, + struct ramdump_info *info) { } +static inline void ol_release_ramdump_mem(struct device *dev, + struct ramdump_info *info) { } +#endif + +int ol_copy_ramdump(struct hif_opaque_softc *scn) +{ + int ret = -1; + struct ramdump_info *info; + qdf_device_t qdf_dev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!qdf_dev) { + BMI_ERR("%s qdf_dev is NULL", __func__); + return -EINVAL; + } + if (pld_is_fw_dump_skipped(qdf_dev->dev)) { + BMI_INFO("%s ssr enabled, skip ramdump", __func__); + return 0; + } + info = qdf_mem_malloc(sizeof(struct ramdump_info)); + if (!info) { + BMI_ERR("%s Memory for Ramdump Allocation failed", __func__); + return -ENOMEM; + } + + ol_get_ramdump_mem(qdf_dev->dev, info); + + if (!info->base || !info->size) { + BMI_ERR("%s:ramdump collection fail", __func__); + qdf_mem_free(info); + return -EACCES; + } + + ret = ol_target_coredump(scn, info->base, info->size); + + ol_release_ramdump_mem(qdf_dev->dev, info); + qdf_mem_free(info); + return ret; +} + +static void __ramdump_work_handler(void *data) +{ + int ret; + uint32_t host_interest_address; + uint32_t dram_dump_values[4]; + uint32_t target_type; + struct hif_target_info *tgt_info; + struct ol_context *ol_ctx = data; + struct hif_opaque_softc *ramdump_scn = ol_ctx->scn; + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + struct ol_config_info *ini_cfg = ol_get_ini_handle(ol_ctx); + + if (!ramdump_scn) { + BMI_ERR("%s:Ramdump_scn is null:", __func__); + goto out_fail; + } + tgt_info = hif_get_target_info_handle(ramdump_scn); + target_type = tgt_info->target_type; +#ifdef WLAN_DEBUG + ret = hif_check_soc_status(ramdump_scn); + if (ret) + goto out_fail; + + ret = hif_dump_registers(ramdump_scn); + if (ret) + goto out_fail; + +#endif + + if (hif_diag_read_mem(ramdump_scn, + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_failure_state)), + (uint8_t *)&host_interest_address, + sizeof(uint32_t)) != QDF_STATUS_SUCCESS) { + BMI_ERR("HifDiagReadiMem FW Dump Area Pointer failed!"); + ol_copy_ramdump(ramdump_scn); + pld_device_crashed(qdf_dev->dev); + ol_check_clean_recovery_flag(ol_ctx); + + return; + } + + BMI_ERR("Host interest item address: 0x%08x", host_interest_address); + + if (hif_diag_read_mem(ramdump_scn, host_interest_address, + (uint8_t *) &dram_dump_values[0], + 4 * sizeof(uint32_t)) != QDF_STATUS_SUCCESS) { + BMI_ERR("HifDiagReadiMem FW Dump Area failed!"); + goto out_fail; + } + BMI_ERR("FW Assertion at PC: 0x%08x BadVA: 0x%08x TargetID: 0x%08x", + dram_dump_values[2], dram_dump_values[3], dram_dump_values[0]); + + if (ol_copy_ramdump(ramdump_scn)) + goto out_fail; + + BMI_ERR("%s: RAM dump collecting completed!", __func__); + + /* + * if unloading is in progress, then skip SSR, + * otherwise notify SSR framework the target has crashed. + */ + if (cds_is_load_or_unload_in_progress()) + cds_set_recovery_in_progress(false); + else { + pld_device_crashed(qdf_dev->dev); + ol_check_clean_recovery_flag(ol_ctx); + } + return; + +out_fail: + /* Silent SSR on dump failure */ + if (ini_cfg->enable_self_recovery) + pld_device_self_recovery(qdf_dev->dev, + PLD_REASON_DEFAULT); + else + pld_device_crashed(qdf_dev->dev); + + ol_check_clean_recovery_flag(ol_ctx); +} + +void ramdump_work_handler(void *data) +{ + struct qdf_op_sync *op_sync; + + if (qdf_op_protect(&op_sync)) + return; + + __ramdump_work_handler(data); + + qdf_op_unprotect(op_sync); +} + +void fw_indication_work_handler(void *data) +{ + struct ol_context *ol_ctx = data; + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + + pld_device_self_recovery(qdf_dev->dev, + PLD_REASON_DEFAULT); + + ol_check_clean_recovery_flag(ol_ctx); +} + +void ol_target_failure(void *instance, QDF_STATUS status) +{ + struct ol_context *ol_ctx = instance; + struct hif_opaque_softc *scn = ol_ctx->scn; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct ol_config_info *ini_cfg = ol_get_ini_handle(ol_ctx); + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + int ret; + enum hif_target_status target_status = hif_get_target_status(scn); + + if (hif_get_bus_type(scn) == QDF_BUS_TYPE_SNOC) { + BMI_ERR("SNOC doesn't suppor this code path!"); + return; + } + + qdf_event_set(&wma->recovery_event); + + if (TARGET_STATUS_RESET == target_status) { + BMI_ERR("Target is already asserted, ignore!"); + return; + } + + hif_set_target_status(scn, TARGET_STATUS_RESET); + + if (hif_get_bus_type(scn) == QDF_BUS_TYPE_USB) { + if (status == QDF_STATUS_E_USB_ERROR) + hif_ramdump_handler(scn); + return; + } + + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + BMI_ERR("%s: Recovery in progress, ignore!\n", __func__); + return; + } + + if (cds_is_load_or_unload_in_progress()) { + BMI_ERR("%s: Loading/Unloading is in progress, ignore!", + __func__); + return; + } + cds_set_target_ready(false); + cds_set_recovery_in_progress(true); + + ret = hif_check_fw_reg(scn); + if (0 == ret) { + if (ini_cfg->enable_self_recovery) { + qdf_sched_work(0, &ol_ctx->fw_indication_work); + return; + } + } else if (-1 == ret) { + return; + } + + BMI_ERR("XXX TARGET ASSERTED XXX"); + + cds_svc_fw_shutdown_ind(qdf_dev->dev); + /* Collect the RAM dump through a workqueue */ + if (ini_cfg->enable_ramdump_collection) + qdf_sched_work(0, &ol_ctx->ramdump_work); + else + pr_debug("%s: athdiag read for target reg\n", __func__); +} + +#ifdef CONFIG_DISABLE_CDC_MAX_PERF_WAR +static QDF_STATUS ol_disable_cdc_max_perf(struct ol_context *ol_ctx) +{ + uint32_t param; + struct hif_opaque_softc *scn = ol_ctx->scn; + struct hif_target_info *tgt_info = hif_get_target_info_handle(scn); + uint32_t target_type = tgt_info->target_type; + + /* set the firmware to disable CDC max perf WAR */ + if (bmi_read_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag2)), + (uint8_t *) ¶m, 4, ol_ctx) != QDF_STATUS_SUCCESS) { + BMI_ERR("BMI READ for setting cdc max perf failed"); + return QDF_STATUS_E_FAILURE; + } + + param |= HI_OPTION_DISABLE_CDC_MAX_PERF_WAR; + if (bmi_write_memory( + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag2)), + (uint8_t *)¶m, 4, ol_ctx) != QDF_STATUS_SUCCESS) { + BMI_ERR("setting cdc max perf failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#else +static QDF_STATUS ol_disable_cdc_max_perf(struct ol_context *ol_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +#endif + +#ifdef WLAN_FEATURE_LPSS +static QDF_STATUS ol_set_lpass_support(struct ol_context *ol_ctx) +{ + uint32_t param; + struct hif_opaque_softc *scn = ol_ctx->scn; + struct hif_target_info *tgt_info = hif_get_target_info_handle(scn); + struct ol_config_info *ini_cfg = ol_get_ini_handle(ol_ctx); + uint32_t target_type = tgt_info->target_type; + + if (ini_cfg->enable_lpass_support) { + if (bmi_read_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag2)), + (uint8_t *) ¶m, 4, ol_ctx) != QDF_STATUS_SUCCESS) { + BMI_ERR("BMI READ:Setting LPASS Support failed"); + return QDF_STATUS_E_FAILURE; + } + + param |= HI_OPTION_DBUART_SUPPORT; + if (bmi_write_memory( + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag2)), + (uint8_t *)¶m, 4, ol_ctx) != QDF_STATUS_SUCCESS) { + BMI_ERR("BMI_READ for setting LPASS Support fail"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +#else +static QDF_STATUS ol_set_lpass_support(struct ol_context *ol_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +#endif + + +QDF_STATUS ol_configure_target(struct ol_context *ol_ctx) +{ + uint32_t param; + struct pld_platform_cap cap = {0}; + int ret; + struct hif_opaque_softc *scn = ol_ctx->scn; + struct hif_target_info *tgt_info = hif_get_target_info_handle(scn); + uint32_t target_type = tgt_info->target_type; + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + + /* Tell target which HTC version it is used */ + param = HTC_PROTOCOL_VERSION; + if (bmi_write_memory( + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_app_host_interest)), + (uint8_t *) ¶m, 4, ol_ctx) != QDF_STATUS_SUCCESS) { + BMI_ERR("bmi_write_memory for htc version failed"); + return QDF_STATUS_E_FAILURE; + } + + /* set the firmware mode to STA/IBSS/AP */ + { + if (bmi_read_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag)), + (uint8_t *)¶m, 4, ol_ctx) != QDF_STATUS_SUCCESS) { + BMI_ERR("bmi_read_memory for setting fwmode failed"); + return QDF_STATUS_E_FAILURE; + } + + /* TODO following parameters need to be re-visited. */ + param |= (1 << HI_OPTION_NUM_DEV_SHIFT); /* num_device */ + /* Firmware mode ?? */ + param |= (HI_OPTION_FW_MODE_AP << HI_OPTION_FW_MODE_SHIFT); + /* mac_addr_method */ + param |= (1 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); + /* firmware_bridge */ + param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); + /* fwsubmode */ + param |= (0 << HI_OPTION_FW_SUBMODE_SHIFT); + + BMI_INFO("NUM_DEV=%d FWMODE=0x%x FWSUBMODE=0x%x FWBR_BUF %d", + 1, HI_OPTION_FW_MODE_AP, 0, 0); + + if (bmi_write_memory( + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag)), + (uint8_t *)¶m, 4, ol_ctx) != QDF_STATUS_SUCCESS) { + BMI_ERR("BMI WRITE for setting fwmode failed"); + return QDF_STATUS_E_FAILURE; + } + } + if (hif_get_bus_type(scn) == QDF_BUS_TYPE_PCI) { + if (ol_disable_cdc_max_perf(ol_ctx)) + return QDF_STATUS_E_FAILURE; + + qdf_mem_zero(&cap, sizeof(cap)); + + ret = pld_get_platform_cap(qdf_dev->dev, &cap); + if (ret) + BMI_ERR("platform capability info not available"); + + if (!ret && cap.cap_flag & PLD_HAS_EXTERNAL_SWREG) { + if (bmi_read_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, + hi_option_flag2)), + (uint8_t *)¶m, 4, ol_ctx) != + QDF_STATUS_SUCCESS) { + BMI_ERR("BMI READ failed for external SWREG"); + return QDF_STATUS_E_FAILURE; + } + + param |= HI_OPTION_USE_EXT_LDO; + if (bmi_write_memory( + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, + hi_option_flag2)), + (uint8_t *)¶m, 4, ol_ctx) != + QDF_STATUS_SUCCESS) { + BMI_ERR("BMI WRITE failed for external SWREG"); + return QDF_STATUS_E_FAILURE; + } + } + + if (ol_set_lpass_support(ol_ctx)) + return QDF_STATUS_E_FAILURE; + } + + /* If host is running on a BE CPU, set the host interest area */ + { +#ifdef BIG_ENDIAN_HOST + param = 1; +#else + param = 0; +#endif + if (bmi_write_memory( + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_be)), + (uint8_t *) ¶m, 4, ol_ctx) != QDF_STATUS_SUCCESS) { + BMI_ERR("setting host CPU BE mode failed"); + return QDF_STATUS_E_FAILURE; + } + } + + /* FW descriptor/Data swap flags */ + param = 0; + if (bmi_write_memory( + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_fw_swap)), + (uint8_t *) ¶m, 4, ol_ctx) != QDF_STATUS_SUCCESS) { + BMI_ERR("BMI WRITE failed setting FW data/desc swap flags"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +static int +ol_check_dataset_patch(struct hif_opaque_softc *scn, uint32_t *address) +{ + /* Check if patch file needed for this target type/version. */ + return 0; +} + +static QDF_STATUS ol_fw_populate_clk_settings(enum a_refclk_speed_t refclk, + struct cmnos_clock_s *clock_s) +{ + if (!clock_s) + return QDF_STATUS_E_FAILURE; + + switch (refclk) { + case SOC_REFCLK_48_MHZ: + clock_s->wlan_pll.div = 0xE; + clock_s->wlan_pll.rnfrac = 0x2AAA8; + clock_s->pll_settling_time = 2400; + break; + case SOC_REFCLK_19_2_MHZ: + clock_s->wlan_pll.div = 0x24; + clock_s->wlan_pll.rnfrac = 0x2AAA8; + clock_s->pll_settling_time = 960; + break; + case SOC_REFCLK_24_MHZ: + clock_s->wlan_pll.div = 0x1D; + clock_s->wlan_pll.rnfrac = 0x15551; + clock_s->pll_settling_time = 1200; + break; + case SOC_REFCLK_26_MHZ: + clock_s->wlan_pll.div = 0x1B; + clock_s->wlan_pll.rnfrac = 0x4EC4; + clock_s->pll_settling_time = 1300; + break; + case SOC_REFCLK_37_4_MHZ: + clock_s->wlan_pll.div = 0x12; + clock_s->wlan_pll.rnfrac = 0x34B49; + clock_s->pll_settling_time = 1870; + break; + case SOC_REFCLK_38_4_MHZ: + clock_s->wlan_pll.div = 0x12; + clock_s->wlan_pll.rnfrac = 0x15551; + clock_s->pll_settling_time = 1920; + break; + case SOC_REFCLK_40_MHZ: + clock_s->wlan_pll.div = 0x11; + clock_s->wlan_pll.rnfrac = 0x26665; + clock_s->pll_settling_time = 2000; + break; + case SOC_REFCLK_52_MHZ: + clock_s->wlan_pll.div = 0x1B; + clock_s->wlan_pll.rnfrac = 0x4EC4; + clock_s->pll_settling_time = 2600; + break; + case SOC_REFCLK_UNKNOWN: + clock_s->wlan_pll.refdiv = 0; + clock_s->wlan_pll.div = 0; + clock_s->wlan_pll.rnfrac = 0; + clock_s->wlan_pll.outdiv = 0; + clock_s->pll_settling_time = 1024; + clock_s->refclk_hz = 0; + default: + return QDF_STATUS_E_FAILURE; + } + + clock_s->refclk_hz = refclk_speed_to_hz[refclk]; + clock_s->wlan_pll.refdiv = 0; + clock_s->wlan_pll.outdiv = 1; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS ol_patch_pll_switch(struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *hif = ol_ctx->scn; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t addr = 0; + uint32_t reg_val = 0; + uint32_t mem_val = 0; + struct cmnos_clock_s clock_s; + uint32_t cmnos_core_clk_div_addr = 0; + uint32_t cmnos_cpu_pll_init_done_addr = 0; + uint32_t cmnos_cpu_speed_addr = 0; + struct hif_target_info *tgt_info = hif_get_target_info_handle(hif); + uint32_t target_version = tgt_info->target_version; + struct targetdef_t *scn = &ol_ctx->tgt_def; + + switch (target_version) { + case AR6320_REV1_1_VERSION: + cmnos_core_clk_div_addr = AR6320_CORE_CLK_DIV_ADDR; + cmnos_cpu_pll_init_done_addr = AR6320_CPU_PLL_INIT_DONE_ADDR; + cmnos_cpu_speed_addr = AR6320_CPU_SPEED_ADDR; + break; + case AR6320_REV1_3_VERSION: + case AR6320_REV2_1_VERSION: + cmnos_core_clk_div_addr = AR6320V2_CORE_CLK_DIV_ADDR; + cmnos_cpu_pll_init_done_addr = AR6320V2_CPU_PLL_INIT_DONE_ADDR; + cmnos_cpu_speed_addr = AR6320V2_CPU_SPEED_ADDR; + break; + case AR6320_REV3_VERSION: + case AR6320_REV3_2_VERSION: + case QCA9379_REV1_VERSION: + case QCA9377_REV1_1_VERSION: + cmnos_core_clk_div_addr = AR6320V3_CORE_CLK_DIV_ADDR; + cmnos_cpu_pll_init_done_addr = AR6320V3_CPU_PLL_INIT_DONE_ADDR; + cmnos_cpu_speed_addr = AR6320V3_CPU_SPEED_ADDR; + break; + default: + BMI_ERR("%s: Unsupported target version %x", __func__, + target_version); + goto end; + } + + addr = (RTC_SOC_BASE_ADDRESS | EFUSE_OFFSET); + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read EFUSE Addr"); + goto end; + } + + status = ol_fw_populate_clk_settings(EFUSE_XTAL_SEL_GET(reg_val), + &clock_s); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to set clock settings"); + goto end; + } + BMI_DBG("crystal_freq: %dHz", clock_s.refclk_hz); + + /* ------Step 1---- */ + reg_val = 0; + addr = (RTC_SOC_BASE_ADDRESS | BB_PLL_CONFIG_OFFSET); + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read PLL_CONFIG Addr"); + goto end; + } + BMI_DBG("Step 1a: %8X", reg_val); + + reg_val &= ~(BB_PLL_CONFIG_FRAC_MASK | BB_PLL_CONFIG_OUTDIV_MASK); + reg_val |= (BB_PLL_CONFIG_FRAC_SET(clock_s.wlan_pll.rnfrac) | + BB_PLL_CONFIG_OUTDIV_SET(clock_s.wlan_pll.outdiv)); + status = bmi_write_soc_register(addr, reg_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write PLL_CONFIG Addr"); + goto end; + } + + reg_val = 0; + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read back PLL_CONFIG Addr"); + goto end; + } + BMI_DBG("Step 1b: %8X", reg_val); + + /* ------Step 2---- */ + reg_val = 0; + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_SETTLE_OFFSET); + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read PLL_SETTLE Addr"); + goto end; + } + BMI_DBG("Step 2a: %8X", reg_val); + + reg_val &= ~WLAN_PLL_SETTLE_TIME_MASK; + reg_val |= WLAN_PLL_SETTLE_TIME_SET(clock_s.pll_settling_time); + status = bmi_write_soc_register(addr, reg_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write PLL_SETTLE Addr"); + goto end; + } + + reg_val = 0; + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read back PLL_SETTLE Addr"); + goto end; + } + BMI_DBG("Step 2b: %8X", reg_val); + + /* ------Step 3---- */ + reg_val = 0; + addr = (RTC_SOC_BASE_ADDRESS | SOC_CORE_CLK_CTRL_OFFSET); + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read CLK_CTRL Addr"); + goto end; + } + BMI_DBG("Step 3a: %8X", reg_val); + + reg_val &= ~SOC_CORE_CLK_CTRL_DIV_MASK; + reg_val |= SOC_CORE_CLK_CTRL_DIV_SET(1); + status = bmi_write_soc_register(addr, reg_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write CLK_CTRL Addr"); + goto end; + } + + reg_val = 0; + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read back CLK_CTRL Addr"); + goto end; + } + BMI_DBG("Step 3b: %8X", reg_val); + + /* ------Step 4----- */ + mem_val = 1; + status = bmi_write_memory(cmnos_core_clk_div_addr, + (uint8_t *) &mem_val, 4, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write CLK_DIV Addr"); + goto end; + } + + /* ------Step 5----- */ + reg_val = 0; + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET); + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read PLL_CTRL Addr"); + goto end; + } + BMI_DBG("Step 5a: %8X", reg_val); + + reg_val &= ~(WLAN_PLL_CONTROL_REFDIV_MASK | WLAN_PLL_CONTROL_DIV_MASK | + WLAN_PLL_CONTROL_NOPWD_MASK); + reg_val |= (WLAN_PLL_CONTROL_REFDIV_SET(clock_s.wlan_pll.refdiv) | + WLAN_PLL_CONTROL_DIV_SET(clock_s.wlan_pll.div) | + WLAN_PLL_CONTROL_NOPWD_SET(1)); + status = bmi_write_soc_register(addr, reg_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write PLL_CTRL Addr"); + goto end; + } + + reg_val = 0; + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read back PLL_CTRL Addr"); + goto end; + } + qdf_udelay(100); + BMI_DBG("Step 5b: %8X", reg_val); + + /* ------Step 6------- */ + do { + reg_val = 0; + status = bmi_read_soc_register((RTC_WMAC_BASE_ADDRESS | + RTC_SYNC_STATUS_OFFSET), ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read RTC_SYNC_STATUS Addr"); + goto end; + } + } while (RTC_SYNC_STATUS_PLL_CHANGING_GET(reg_val)); + + /* ------Step 7------- */ + reg_val = 0; + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET); + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read PLL_CTRL Addr for CTRL_BYPASS"); + goto end; + } + BMI_DBG("Step 7a: %8X", reg_val); + + reg_val &= ~WLAN_PLL_CONTROL_BYPASS_MASK; + reg_val |= WLAN_PLL_CONTROL_BYPASS_SET(0); + status = bmi_write_soc_register(addr, reg_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write PLL_CTRL Addr for CTRL_BYPASS"); + goto end; + } + + reg_val = 0; + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read back PLL_CTRL Addr for CTRL_BYPASS"); + goto end; + } + BMI_DBG("Step 7b: %8X", reg_val); + + /* ------Step 8-------- */ + do { + reg_val = 0; + status = bmi_read_soc_register((RTC_WMAC_BASE_ADDRESS | + RTC_SYNC_STATUS_OFFSET), ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read SYNC_STATUS Addr"); + goto end; + } + } while (RTC_SYNC_STATUS_PLL_CHANGING_GET(reg_val)); + + /* ------Step 9-------- */ + reg_val = 0; + addr = (RTC_SOC_BASE_ADDRESS | SOC_CPU_CLOCK_OFFSET); + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read CPU_CLK Addr"); + goto end; + } + BMI_DBG("Step 9a: %8X", reg_val); + + reg_val &= ~SOC_CPU_CLOCK_STANDARD_MASK; + reg_val |= SOC_CPU_CLOCK_STANDARD_SET(1); + status = bmi_write_soc_register(addr, reg_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write CPU_CLK Addr"); + goto end; + } + + reg_val = 0; + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read back CPU_CLK Addr"); + goto end; + } + BMI_DBG("Step 9b: %8X", reg_val); + + /* ------Step 10------- */ + reg_val = 0; + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET); + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read PLL_CTRL Addr for NOPWD"); + goto end; + } + BMI_DBG("Step 10a: %8X", reg_val); + + reg_val &= ~WLAN_PLL_CONTROL_NOPWD_MASK; + status = bmi_write_soc_register(addr, reg_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write PLL_CTRL Addr for NOPWD"); + goto end; + } + reg_val = 0; + status = bmi_read_soc_register(addr, ®_val, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to read back PLL_CTRL Addr for NOPWD"); + goto end; + } + BMI_DBG("Step 10b: %8X", reg_val); + + /* ------Step 11------- */ + mem_val = 1; + status = bmi_write_memory(cmnos_cpu_pll_init_done_addr, + (uint8_t *) &mem_val, 4, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write PLL_INIT Addr"); + goto end; + } + + mem_val = TARGET_CPU_FREQ; + status = bmi_write_memory(cmnos_cpu_speed_addr, + (uint8_t *) &mem_val, 4, ol_ctx); + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("Failed to write CPU_SPEED Addr"); + goto end; + } + +end: + return status; +} + +QDF_STATUS ol_download_firmware(struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + uint32_t param, address = 0; + QDF_STATUS status = !QDF_STATUS_SUCCESS; + QDF_STATUS ret; + struct hif_target_info *tgt_info = hif_get_target_info_handle(scn); + struct ol_config_info *ini_cfg = ol_get_ini_handle(ol_ctx); + uint32_t target_type = tgt_info->target_type; + uint32_t target_version = tgt_info->target_version; + struct bmi_info *bmi_ctx = GET_BMI_CONTEXT(ol_ctx); + qdf_device_t qdf_dev = ol_ctx->qdf_dev; + + if (0 != pld_get_fw_files_for_target(qdf_dev->dev, + &bmi_ctx->fw_files, + target_type, + target_version)) { + BMI_ERR("%s: No FW files from platform driver", __func__); + return QDF_STATUS_E_FAILURE; + } + + /* Transfer Board Data from Target EEPROM to Target RAM */ + /* Determine where in Target RAM to write Board Data */ + bmi_read_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_board_data)), + (uint8_t *)&address, 4, ol_ctx); + + if (!address) { + address = AR6004_REV5_BOARD_DATA_ADDRESS; + BMI_DBG("%s: Target address not known! Using 0x%x", + __func__, address); + } + + if (hif_get_bus_type(scn) != QDF_BUS_TYPE_USB) { + ret = ol_patch_pll_switch(ol_ctx); + if (ret != QDF_STATUS_SUCCESS) { + BMI_ERR("pll switch failed. status %d", ret); + return ret; + } + } + + if (ol_ctx->cal_in_flash) { + /* Write EEPROM or Flash data to Target RAM */ + status = ol_transfer_bin_file(ol_ctx, ATH_FLASH_FILE, + address, false); + } + + if (!status) { + /* Record the fact that Board Data is initialized */ + param = 1; + bmi_write_memory( + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, + hi_board_data_initialized)), + (uint8_t *) ¶m, 4, ol_ctx); + } else { + /* Transfer One Time Programmable data */ + address = BMI_SEGMENTED_WRITE_ADDR; + BMI_INFO("%s: Using 0x%x for the remainder of init", + __func__, address); + + status = ol_transfer_bin_file(ol_ctx, ATH_OTP_FILE, + address, true); + /* Execute the OTP code only if entry found and downloaded */ + if (!status) { + uint16_t board_id = 0xffff; + /* get board id */ + param = 0x10; + bmi_execute(address, ¶m, ol_ctx); + if (!(param & 0xff)) + board_id = (param >> 8) & 0xffff; + BMI_INFO("%s: board ID is 0x%0x", __func__, board_id); + bmi_ctx->board_id = board_id; + } else if (status < 0) { + return status; + } + + bmi_read_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, + hi_board_data)), + (uint8_t *)&address, 4, ol_ctx); + + if (!address) { + address = AR6004_REV5_BOARD_DATA_ADDRESS; + pr_err("%s: Target address not known! Using 0x%x\n", + __func__, address); + } + + /* Flash is either not available or invalid */ + if (ol_transfer_bin_file(ol_ctx, ATH_BOARD_DATA_FILE, + address, false)) { + return QDF_STATUS_E_FAILURE; + } + + /* Record the fact that Board Data is initialized */ + param = 1; + bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, + hi_board_data_initialized)), + (uint8_t *) ¶m, 4, ol_ctx); + address = BMI_SEGMENTED_WRITE_ADDR; + param = 0; + bmi_execute(address, ¶m, ol_ctx); + } + + if (!ol_transfer_bin_file(ol_ctx, ATH_SETUP_FILE, + BMI_SEGMENTED_WRITE_ADDR, true)) { + param = 0; + bmi_execute(address, ¶m, ol_ctx); + } + + /* Download Target firmware + * TODO point to target specific files in runtime + */ + address = BMI_SEGMENTED_WRITE_ADDR; + if (ol_transfer_bin_file(ol_ctx, ATH_FIRMWARE_FILE, + address, true)) { + return QDF_STATUS_E_FAILURE; + } + + /* Apply the patches */ + if (ol_check_dataset_patch(scn, &address)) { + if (ol_transfer_bin_file(ol_ctx, ATH_PATCH_FILE, address, + false)) { + return QDF_STATUS_E_FAILURE; + } + bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_dset_list_head)), + (uint8_t *) &address, 4, ol_ctx); + } + + switch (target_version) { + case AR6004_VERSION_REV1_3: + param = 11; + break; + case AR6320_REV1_VERSION: + case AR6320_REV2_VERSION: + case AR6320_REV3_VERSION: + case AR6320_REV3_2_VERSION: + case QCA9377_REV1_1_VERSION: + case QCA9379_REV1_VERSION: + case AR6320_REV4_VERSION: + case AR6320_DEV_VERSION: + /* + * In sdio interface chip, both sdio_data2 and uart_tx pin + * will use GPIO6. It is set by fw rom code, which will cause + * sdio CRC error when there is sdio transaction. + * Override uart tx pin to avoid side effect to sdio pin. + */ + if (hif_get_bus_type(scn) == QDF_BUS_TYPE_SDIO) + param = 19; + else + param = 6; + break; + default: + /* Configure GPIO AR9888 UART */ + param = 7; + } + + bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_dbg_uart_txpin)), + (uint8_t *)¶m, 4, ol_ctx); + + if (ini_cfg->enable_uart_print) { + param = 1; + bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_serial_enable)), + (uint8_t *)¶m, 4, ol_ctx); + } else { + /* + * Explicitly setting UART prints to zero as target turns it on + * based on scratch registers. + */ + param = 0; + bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_serial_enable)), + (uint8_t *)¶m, 4, ol_ctx); + } + + if (ini_cfg->enable_fw_log) { + bmi_read_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag)), + (uint8_t *)¶m, 4, ol_ctx); + + param &= ~(HI_OPTION_DISABLE_DBGLOG); + bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag)), + (uint8_t *)¶m, 4, ol_ctx); + } else { + /* + * Explicitly setting fwlog prints to zero as target turns it on + * based on scratch registers. + */ + bmi_read_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag)), + (uint8_t *)¶m, 4, ol_ctx); + + param |= HI_OPTION_DISABLE_DBGLOG; + bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_option_flag)), + (uint8_t *) ¶m, 4, ol_ctx); + } + status = ol_extra_initialization(ol_ctx); + + return status; +} + +static int ol_diag_read(struct hif_opaque_softc *scn, uint8_t *buffer, + uint32_t pos, size_t count) +{ + int result = 0; + + if ((4 == count) && ((pos & 3) == 0)) { + result = hif_diag_read_access(scn, pos, + (uint32_t *) buffer); + } else { + size_t amount_read = 0; + size_t readSize = PCIE_READ_LIMIT; + size_t remainder = 0; + + if (count > PCIE_READ_LIMIT) { + while ((amount_read < count) && (0 == result)) { + result = hif_diag_read_mem(scn, pos, + buffer, readSize); + if (0 == result) { + buffer += readSize; + pos += readSize; + amount_read += readSize; + remainder = count - amount_read; + if (remainder < PCIE_READ_LIMIT) + readSize = remainder; + } + } + } else { + result = hif_diag_read_mem(scn, pos, + buffer, count); + } + } + + if (!result) + return count; + else + return -EIO; +} + +static int ol_ath_get_reg_table(struct hif_opaque_softc *scn, + uint32_t target_version, + struct tgt_reg_table *reg_table) +{ + int section_len = 0; + + if (!reg_table) { + qdf_assert(0); + return section_len; + } + + if (hif_get_bus_type(scn) != QDF_BUS_TYPE_PCI && + hif_get_bus_type(scn) != QDF_BUS_TYPE_SDIO) + return section_len; + + switch (target_version) { + case AR6320_REV2_1_VERSION: + reg_table->section = ar6320v2_reg_table; + reg_table->section_size = ARRAY_SIZE(ar6320v2_reg_table); + section_len = AR6320_REV2_1_REG_SIZE; + break; + case AR6320_REV3_VERSION: + case AR6320_REV3_2_VERSION: + case QCA9379_REV1_VERSION: + case QCA9377_REV1_1_VERSION: + reg_table->section = ar6320v3_reg_table; + reg_table->section_size = ARRAY_SIZE(ar6320v3_reg_table); + section_len = AR6320_REV3_REG_SIZE; + break; + default: + reg_table->section = NULL; + reg_table->section_size = 0; + section_len = 0; + } + + return section_len; +} + +static int ol_diag_read_reg_loc(struct hif_opaque_softc *scn, uint8_t *buffer, + uint32_t buffer_len) +{ + int i, len, section_len, fill_len; + int dump_len, result = 0; + struct tgt_reg_table reg_table; + const struct tgt_reg_section *curr_sec, *next_sec; + struct hif_target_info *tgt_info = hif_get_target_info_handle(scn); + uint32_t target_version = tgt_info->target_version; + + reg_table.section = NULL; + reg_table.section_size = 0; + + section_len = ol_ath_get_reg_table(scn, target_version, ®_table); + + if (!reg_table.section || !reg_table.section_size || !section_len) { + BMI_ERR("%s: failed to get reg table", __func__); + result = -EIO; + goto out; + } + + curr_sec = reg_table.section; + for (i = 0; i < reg_table.section_size; i++) { + + dump_len = curr_sec->end_addr - curr_sec->start_addr; + + if ((buffer_len - result) < dump_len) { + BMI_ERR("No buffer to dump regs:%d: 0x%08x-0x%08x", + i, curr_sec->start_addr, curr_sec->end_addr); + goto out; + } + + len = ol_diag_read(scn, buffer, curr_sec->start_addr, dump_len); + + if (len != -EIO) { + buffer += len; + result += len; + } else { + BMI_ERR("%s: can't read reg 0x%08x len = %d", + __func__, curr_sec->start_addr, dump_len); + result = -EIO; + goto out; + } + + if (result < section_len) { + next_sec = (struct tgt_reg_section *) ((uint8_t *) + curr_sec + sizeof(*curr_sec)); + fill_len = next_sec->start_addr - curr_sec->end_addr; + if ((buffer_len - result) < fill_len) { + BMI_ERR("No buf to fill regs:%d: 0x%08x-0x%08x", + i, curr_sec->end_addr, + next_sec->start_addr); + goto out; + } + + if (fill_len) { + buffer += fill_len; + result += fill_len; + } + } + curr_sec++; + } + +out: + return result; +} + +static +void ol_dump_target_memory(struct hif_opaque_softc *scn, void *memory_block) +{ + char *buffer_loc = memory_block; + u_int32_t section_count = 0; + u_int32_t address = 0; + u_int32_t size = 0; + + if (hif_get_bus_type(scn) == QDF_BUS_TYPE_SDIO || + hif_get_bus_type(scn) == QDF_BUS_TYPE_USB) + return; + + for (; section_count < 2; section_count++) { + switch (section_count) { + case 0: + address = DRAM_LOCAL_BASE_ADDR; + size = DRAM_SIZE; + break; + case 1: + address = AXI_LOCATION; + size = AXI_SIZE; + default: + break; + } + hif_dump_target_memory(scn, buffer_loc, address, size); + buffer_loc += size; + } +} + +static int +ol_dump_ce_register(struct hif_opaque_softc *scn, void *memory_block) +{ + int ret; + + BMI_ERR("Could not read dump section!"); + + if (hif_get_bus_type(scn) == QDF_BUS_TYPE_SDIO || + hif_get_bus_type(scn) == QDF_BUS_TYPE_USB) + return 0; + + if (hif_dump_registers(scn)) + BMI_ERR("Failed to dump bus registers"); + + ol_dump_target_memory(scn, memory_block); + ret = -EACCES; + + return ret; +} + +static inline uint32_t +ol_get_max_section_count(struct hif_opaque_softc *scn) +{ + if (hif_get_bus_type(scn) == QDF_BUS_TYPE_PCI) + return 5; + else + return 4; +} + +/** + * ol_set_ram_config_reg() - set target RAM configuration register + * @sc: pointer of hif_softc context + * @config: value to be written to the register + * + * This function will write the given value to target RAM configuration + * register which is bit[23-20] of target CPU inbound address in order to + * provide correct address mapping. + * + * Return: 0 for success or reasons for failure + */ +static int ol_set_ram_config_reg(struct hif_opaque_softc *scn, uint32_t config) +{ + QDF_STATUS status; + uint32_t val; + struct targetdef_s *targetdef = + (struct targetdef_s *)hif_get_targetdef(scn); + uint32_t ram_config_addr = + targetdef->d_SOC_CORE_BASE_ADDRESS + FW_RAM_CONFIG_ADDRESS; + + if (hif_get_bus_type(scn) != QDF_BUS_TYPE_PCI) + return -EACCES; + + status = hif_diag_write_access(scn, ram_config_addr, config); + if (QDF_IS_STATUS_ERROR(status)) { + return -EACCES; + } + status = hif_diag_read_access(scn, ram_config_addr, &val); + if (QDF_IS_STATUS_ERROR(status)) { + return -EACCES; + } + if (val != config) { + BMI_ERR("%s: Failed to set RAM config reg from 0x%x to 0x%x", + __func__, val, config); + return -EACCES; + } + return 0; +} + +static int +ol_get_iram_len_and_pos(struct hif_opaque_softc *scn, uint32_t *pos, + uint32_t *len, uint32_t section) +{ + enum hif_target_status status; + uint32_t iram_addr, iram_size; + int ret; + + if (hif_get_bus_type(scn) != QDF_BUS_TYPE_PCI) { + *pos = IRAM_LOCATION; + *len = IRAM_SIZE; + BMI_ERR("%s: Dumping IRAM Section", __func__); + return 0; + } + + status = hif_get_target_status(scn); + if (status != TARGET_STATUS_RESET) { + BMI_ERR("%s: Target status invalid: %d", __func__, status); + return -EBUSY; + } + + switch (section) { + case 3: + BMI_ERR("%s: Dumping IRAM1 section", __func__); + iram_addr = IRAM1_LOCATION; + iram_size = IRAM1_SIZE; + break; + case 4: + BMI_ERR("%s: Dumping IRAM2 section", __func__); + iram_addr = IRAM2_LOCATION; + iram_size = IRAM2_SIZE; + break; + default: + BMI_ERR("%s: Invalid input iram section %d", + __func__, section); + return A_EINVAL; + } + + ret = ol_set_ram_config_reg(scn, iram_addr >> 20); + if (ret) { + BMI_ERR("%s: Skip IRAM1 ret:%d", __func__, ret); + return -EBUSY; + } + + *pos = iram_addr; + *len = iram_size; + return 0; +} + +/** + * ol_target_coredump() - API to collect target ramdump + * @inst - private context + * @memory_block - non-NULL reserved memory location + * @block_len - size of the dump to collect + * + * Function to perform core dump for the target. + * + * Return: int + */ +static int ol_target_coredump(void *inst, void *memory_block, + uint32_t block_len) +{ + struct hif_opaque_softc *scn = (struct hif_opaque_softc *)inst; + int8_t *buffer_loc = memory_block; + int result = 0; + int ret = 0; + uint32_t amount_read = 0; + uint32_t section_count = 0; + uint32_t pos = 0; + uint32_t read_len = 0; + uint32_t max_count = ol_get_max_section_count(scn); + + while ((section_count < max_count) && (amount_read < block_len)) { + switch (section_count) { + case 0: + pos = DRAM_LOCATION; + read_len = DRAM_SIZE; + BMI_ERR("%s: Dumping DRAM section...", __func__); + break; + case 1: + pos = AXI_LOCATION; + read_len = AXI_SIZE; + BMI_ERR("%s: Dumping AXI section...", __func__); + break; + case 2: + pos = REGISTER_LOCATION; + /* ol_diag_read_reg_loc checks for buffer overrun */ + read_len = 0; + BMI_ERR("%s: Dumping Register section...", __func__); + break; + case 3: + case 4: + ret = ol_get_iram_len_and_pos(scn, &pos, &read_len, + section_count); + if (ret) { + BMI_ERR("%s: Fail to Dump IRAM Section " + "ret:%d", __func__, ret); + return ret; + } + break; + default: + BMI_ERR("%s: INVALID SECTION_:%d", __func__, + section_count); + return 0; + } + + if (block_len - amount_read < read_len) { + BMI_ERR("%s: No memory to dump section:%d buffer!", + __func__, section_count); + return -ENOMEM; + } + + if (((hif_get_bus_type(scn) == QDF_BUS_TYPE_PCI) || + (hif_get_bus_type(scn) == QDF_BUS_TYPE_SDIO)) && + pos == REGISTER_LOCATION) + result = ol_diag_read_reg_loc(scn, buffer_loc, + block_len - amount_read); + else + result = ol_diag_read(scn, buffer_loc, pos, read_len); + + if (result == -EIO) + return ol_dump_ce_register(scn, memory_block); + + BMI_INFO("%s: Section:%d Bytes Read:%0x", __func__, + section_count, result); + + amount_read += result; + buffer_loc += result; + section_count++; + } + return ret; +} + +/** + * ol_get_ini_handle() - API to get Ol INI configuration + * @ol_ctx: OL Context + * + * Return: pointer to OL configuration + */ +struct ol_config_info *ol_get_ini_handle(struct ol_context *ol_ctx) +{ + return &ol_ctx->cfg_info; +} + +/** + * ol_init_ini_config() - API to initialize INI configuration + * @ol_ctx: OL Context + * @cfg: OL ini configuration + * + * Return: void + */ +void ol_init_ini_config(struct ol_context *ol_ctx, + struct ol_config_info *cfg) +{ + qdf_mem_copy(&ol_ctx->cfg_info, cfg, sizeof(struct ol_config_info)); +} + +void ol_set_fw_crashed_cb(struct ol_context *ol_ctx, + void (*callback_fn)(void)) +{ + ol_ctx->fw_crashed_cb = callback_fn; +} diff --git a/drivers/staging/qcacld-3.0/core/bmi/src/ol_fw_common.c b/drivers/staging/qcacld-3.0/core/bmi/src/ol_fw_common.c new file mode 100644 index 0000000000000000000000000000000000000000..7756e46370c66e8e3ea21d72ee49b9644a7ae043 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/bmi/src/ol_fw_common.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ol_if_athvar.h" +#include "targaddrs.h" +#include "ol_cfg.h" +#include "i_ar6320v2_regtable.h" +#include "ol_fw.h" +#ifdef HIF_PCI +#include "ce_reg.h" +#endif +#if defined(HIF_SDIO) +#include "regtable_sdio.h" +#endif +#if defined(HIF_USB) +#include "regtable_usb.h" +#endif +#if defined(CONFIG_CNSS) +#include +#endif +#include "i_bmi.h" +#include "cds_api.h" + +#ifdef CONFIG_DISABLE_SLEEP_BMI_OPTION +static inline void ol_sdio_disable_sleep(struct ol_context *ol_ctx) +{ + uint32_t value; + + BMI_ERR("prevent ROME from sleeping"); + bmi_read_soc_register(MBOX_BASE_ADDRESS + LOCAL_SCRATCH_OFFSET, + /* this address should be 0x80C0 for ROME*/ + &value, + ol_ctx); + + value |= SOC_OPTION_SLEEP_DISABLE; + + bmi_write_soc_register(MBOX_BASE_ADDRESS + LOCAL_SCRATCH_OFFSET, + value, + ol_ctx); +} + +#else +static inline void ol_sdio_disable_sleep(struct ol_context *ol_ctx) +{ +} + +#endif + +/** + * ol_usb_extra_initialization() - USB extra initialization + * @ol_ctx: pointer to ol_context + * + * USB specific initialization after firmware download + * + * Return: QDF_STATUS_SUCCESS on success and error QDF status on failure + */ +static QDF_STATUS +ol_usb_extra_initialization(struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + struct hif_target_info *tgt_info = + hif_get_target_info_handle(scn); + QDF_STATUS status = !QDF_STATUS_SUCCESS; + u_int32_t param = 0; + + param |= HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE; + status = bmi_write_memory( + hif_hia_item_address(tgt_info->target_type, + offsetof(struct host_interest_s, + hi_acs_flags)), + (u_int8_t *)¶m, 4, ol_ctx); + + return status; +} + +/*Setting SDIO block size, mbox ISR yield limit for SDIO based HIF*/ +static +QDF_STATUS ol_sdio_extra_initialization(struct ol_context *ol_ctx) +{ + + QDF_STATUS status; + uint32_t param; + uint32_t blocksizes[HTC_MAILBOX_NUM_MAX]; + uint32_t MboxIsrYieldValue = 99; + struct hif_opaque_softc *scn = ol_ctx->scn; + struct hif_target_info *tgt_info = hif_get_target_info_handle(scn); + uint32_t target_type = tgt_info->target_type; + + /* get the block sizes */ + status = hif_get_config_item(scn, + HIF_DEVICE_GET_BLOCK_SIZE, + blocksizes, sizeof(blocksizes)); + if (status) { + BMI_ERR("Failed to get block size info from HIF layer"); + goto exit; + } + /* note: we actually get the block size for mailbox 1, + * for SDIO the block size on mailbox 0 is artificially + * set to 1 must be a power of 2 + */ + qdf_assert((blocksizes[1] & (blocksizes[1] - 1)) == 0); + + /* set the host interest area for the block size */ + status = bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, + hi_mbox_io_block_sz)), + (uint8_t *)&blocksizes[1], + 4, + ol_ctx); + + if (status) { + BMI_ERR("BMIWriteMemory for IO block size failed"); + goto exit; + } + + if (MboxIsrYieldValue != 0) { + /* set the host for the mbox ISR yield limit */ + status = + bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, + hi_mbox_isr_yield_limit)), + (uint8_t *)&MboxIsrYieldValue, + 4, + ol_ctx); + + if (status) { + BMI_ERR("BMI write for yield limit failed\n"); + goto exit; + } + } + ol_sdio_disable_sleep(ol_ctx); + status = bmi_read_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, + hi_acs_flags)), + (uint8_t *)¶m, + 4, + ol_ctx); + if (status) { + BMI_ERR("BMIReadMemory for hi_acs_flags failed"); + goto exit; + } + + param |= HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE; + + /* disable swap mailbox for FTM */ + if (cds_get_conparam() != QDF_GLOBAL_FTM_MODE) + param |= HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET; + + if (!cds_is_ptp_tx_opt_enabled()) + param |= HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_SET; + + /* enable TX completion to collect tx_desc for pktlog */ + if (cds_is_packet_log_enabled()) + param &= ~HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_SET; + + bmi_write_memory(hif_hia_item_address(target_type, + offsetof(struct host_interest_s, + hi_acs_flags)), + (uint8_t *)¶m, 4, ol_ctx); +exit: + return status; +} + +/** + * ol_extra_initialization() - OL extra initialization + * @ol_ctx: pointer to ol_context + * + * Bus specific initialization after firmware download + * + * Return: QDF_STATUS_SUCCESS on success and error QDF status on failure + */ +QDF_STATUS ol_extra_initialization(struct ol_context *ol_ctx) +{ + struct hif_opaque_softc *scn = ol_ctx->scn; + + if (hif_get_bus_type(scn) == QDF_BUS_TYPE_SDIO) + return ol_sdio_extra_initialization(ol_ctx); + else if (hif_get_bus_type(scn) == QDF_BUS_TYPE_USB) + return ol_usb_extra_initialization(ol_ctx); + + return QDF_STATUS_SUCCESS; +} + +void ol_target_ready(struct hif_opaque_softc *scn, void *cfg_ctx) +{ + uint32_t value = 0; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct hif_target_info *tgt_info = hif_get_target_info_handle(scn); + uint32_t target_type = tgt_info->target_type; + + if (hif_get_bus_type(scn) != QDF_BUS_TYPE_SDIO) + return; + status = hif_diag_read_mem(scn, + hif_hia_item_address(target_type, + offsetof(struct host_interest_s, hi_acs_flags)), + (uint8_t *)&value, sizeof(u_int32_t)); + + if (status != QDF_STATUS_SUCCESS) { + BMI_ERR("HIFDiagReadMem failed"); + return; + } + + if (value & HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_FW_ACK) { + BMI_ERR("MAILBOX SWAP Service is enabled!"); + hif_set_mailbox_swap(scn); + } + + if (value & HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_FW_ACK) { + BMI_ERR("Reduced Tx Complete service is enabled!"); + ol_cfg_set_tx_free_at_download(cfg_ctx); + } +} diff --git a/drivers/staging/qcacld-3.0/core/cds/inc/cds_api.h b/drivers/staging/qcacld-3.0/core/cds/inc/cds_api.h new file mode 100644 index 0000000000000000000000000000000000000000..3c43e6dad6832480dfa0321db44c5a70c8de7f75 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/inc/cds_api.h @@ -0,0 +1,594 @@ +/* + * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__CDS_API_H) +#define __CDS_API_H + +/** + * DOC: cds_api.h + * + * Connectivity driver services public API + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdf_platform.h" +#include "qdf_cpuhp.h" +#include +#include "reg_services_public_struct.h" +#include +#include +#include +#include +#include +#include +#include + +/* Amount of time to wait for WMA to perform an asynchronous activity. + * This value should be larger than the timeout used by WMI to wait for + * a response from target + */ +#define CDS_WMA_TIMEOUT (15000) + +/** + * enum cds_driver_state - Driver state + * @CDS_DRIVER_STATE_UNINITIALIZED: Driver is in uninitialized state. + * CDS_DRIVER_STATE_LOADED: Driver is loaded and functional. + * CDS_DRIVER_STATE_LOADING: Driver probe is in progress. + * CDS_DRIVER_STATE_UNLOADING: Driver remove is in progress. + * CDS_DRIVER_STATE_RECOVERING: Recovery in progress. + * CDS_DRIVER_STATE_BAD: Driver in bad state. + * CDS_DRIVER_STATE_MODULE_STOPPING: Module stop in progress. + */ +enum cds_driver_state { + CDS_DRIVER_STATE_UNINITIALIZED = 0, + CDS_DRIVER_STATE_LOADED = BIT(0), + CDS_DRIVER_STATE_LOADING = BIT(1), + CDS_DRIVER_STATE_UNLOADING = BIT(2), + CDS_DRIVER_STATE_RECOVERING = BIT(3), + CDS_DRIVER_STATE_BAD = BIT(4), + CDS_DRIVER_STATE_FW_READY = BIT(5), + CDS_DRIVER_STATE_MODULE_STOPPING = BIT(6), +}; + +/** + * struce cds_vdev_dp_stats - vdev stats populated from DP + * @tx_retries: packet number of successfully transmitted after more + * than one retransmission attempt + */ +struct cds_vdev_dp_stats { + uint32_t tx_retries; +}; + +#define __CDS_IS_DRIVER_STATE(_state, _mask) (((_state) & (_mask)) == (_mask)) + +void cds_set_driver_state(enum cds_driver_state); +void cds_clear_driver_state(enum cds_driver_state); +enum cds_driver_state cds_get_driver_state(void); + +/** + * cds_is_driver_loading() - Is driver load in progress + * + * Return: true if driver is loading and false otherwise. + */ +static inline bool cds_is_driver_loading(void) +{ + enum cds_driver_state state = cds_get_driver_state(); + + return __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_LOADING); +} + +/** + * cds_is_driver_unloading() - Is driver unload in progress + * + * Return: true if driver is unloading and false otherwise. + */ +static inline bool cds_is_driver_unloading(void) +{ + enum cds_driver_state state = cds_get_driver_state(); + + return __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_UNLOADING); +} + +/** + * cds_is_driver_recovering() - Is recovery in progress + * + * Return: true if recovery in progress and false otherwise. + */ +static inline bool cds_is_driver_recovering(void) +{ + enum cds_driver_state state = cds_get_driver_state(); + + return __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_RECOVERING); +} + +/** + * cds_is_driver_in_bad_state() - is driver in bad state + * + * Return: true if driver is in bad state and false otherwise. + */ +static inline bool cds_is_driver_in_bad_state(void) +{ + enum cds_driver_state state = cds_get_driver_state(); + + return __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_BAD); +} + +/** + * cds_is_load_or_unload_in_progress() - Is driver load OR unload in progress + * + * Return: true if driver is loading OR unloading and false otherwise. + */ +static inline bool cds_is_load_or_unload_in_progress(void) +{ + enum cds_driver_state state = cds_get_driver_state(); + + return __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_LOADING) || + __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_UNLOADING); +} + +/** + * cds_is_target_ready() - Is target is in ready state + * + * Return: true if target is in ready state and false otherwise. + */ +static inline bool cds_is_target_ready(void) +{ + enum cds_driver_state state = cds_get_driver_state(); + + return __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_FW_READY); +} + +/** + * cds_set_recovery_in_progress() - Set recovery in progress + * @value: value to set + * + * Return: none + */ +static inline void cds_set_recovery_in_progress(uint8_t value) +{ + if (value) + cds_set_driver_state(CDS_DRIVER_STATE_RECOVERING); + else + cds_clear_driver_state(CDS_DRIVER_STATE_RECOVERING); +} + +/** + * cds_set_driver_in_bad_state() - Set driver state + * @value: value to set + * + * Return: none + */ +static inline void cds_set_driver_in_bad_state(uint8_t value) +{ + if (value) + cds_set_driver_state(CDS_DRIVER_STATE_BAD); + else + cds_clear_driver_state(CDS_DRIVER_STATE_BAD); +} + +/** + * cds_set_target_ready() - Set target ready state + * @value: value to set + * + * Return: none + */ +static inline void cds_set_target_ready(uint8_t value) +{ + if (value) + cds_set_driver_state(CDS_DRIVER_STATE_FW_READY); + else + cds_clear_driver_state(CDS_DRIVER_STATE_FW_READY); +} + +/** + * cds_set_load_in_progress() - Set load in progress + * @value: value to set + * + * Return: none + */ +static inline void cds_set_load_in_progress(uint8_t value) +{ + if (value) + cds_set_driver_state(CDS_DRIVER_STATE_LOADING); + else + cds_clear_driver_state(CDS_DRIVER_STATE_LOADING); +} + +/** + * cds_set_driver_loaded() - Set load completed + * @value: value to set + * + * Return: none + */ +static inline void cds_set_driver_loaded(uint8_t value) +{ + if (value) + cds_set_driver_state(CDS_DRIVER_STATE_LOADED); + else + cds_clear_driver_state(CDS_DRIVER_STATE_LOADED); +} + +/** + * cds_set_unload_in_progress() - Set unload in progress + * @value: value to set + * + * Return: none + */ +static inline void cds_set_unload_in_progress(uint8_t value) +{ + if (value) + cds_set_driver_state(CDS_DRIVER_STATE_UNLOADING); + else + cds_clear_driver_state(CDS_DRIVER_STATE_UNLOADING); +} + +/** + * cds_set_module_stop_in_progress() - Setting module stop in progress + * + * @value: value to set + * + * Return: none + */ +static inline void cds_set_module_stop_in_progress(bool value) +{ + if (value) + cds_set_driver_state(CDS_DRIVER_STATE_MODULE_STOPPING); + else + cds_clear_driver_state(CDS_DRIVER_STATE_MODULE_STOPPING); +} + +/** + * cds_is_driver_loaded() - Is driver loaded + * + * Return: true if driver is loaded or false otherwise. + */ +static inline bool cds_is_driver_loaded(void) +{ + enum cds_driver_state state = cds_get_driver_state(); + + return __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_LOADED); +} + +/** + * cds_init() - Initialize CDS + * + * This function allocates the resource required for CDS, but does not + * initialize all the members. This overall initialization will happen at + * cds_open(). + * + * Return: QDF_STATUS_SUCCESS if CDS was initialized and an error on failure + */ +QDF_STATUS cds_init(void); + +void cds_deinit(void); + +QDF_STATUS cds_pre_enable(void); + +QDF_STATUS cds_open(struct wlan_objmgr_psoc *psoc); + +/** + * cds_dp_open() - Open datapath module + * @psoc - object manager soc handle + * + * API to map the datapath rings to interrupts + * and also open the datapath pdev module. + * + * Return: QDF status + */ +QDF_STATUS cds_dp_open(struct wlan_objmgr_psoc *psoc); + +/** + * cds_enable() - start/enable cds module + * @psoc: Psoc pointer + * + * Return: QDF status + */ +QDF_STATUS cds_enable(struct wlan_objmgr_psoc *psoc); + +QDF_STATUS cds_disable(struct wlan_objmgr_psoc *psoc); + +QDF_STATUS cds_post_disable(void); + +QDF_STATUS cds_close(struct wlan_objmgr_psoc *psoc); + +/** + * cds_dp_close() - Close datapath module + * @psoc: Object manager soc handle + * + * API used to detach interrupts assigned to service + * datapath rings and close pdev module + * + * Return: Status + */ +QDF_STATUS cds_dp_close(struct wlan_objmgr_psoc *psoc); + +void *cds_get_context(QDF_MODULE_ID module_id); + +void *cds_get_global_context(void); + +QDF_STATUS cds_alloc_context(QDF_MODULE_ID module_id, void **module_context, + uint32_t size); + +QDF_STATUS cds_free_context(QDF_MODULE_ID module_id, void *module_context); + +QDF_STATUS cds_set_context(QDF_MODULE_ID module_id, void *context); + +void cds_flush_work(void *work); +void cds_flush_delayed_work(void *dwork); + +#ifdef REMOVE_PKT_LOG +static inline +bool cds_is_packet_log_enabled(void) +{ + return false; +} +#else +bool cds_is_packet_log_enabled(void); +#endif + +/** + * cds_get_recovery_reason() - get self recovery reason + * @reason: cds hang reason + * + * Return: None + */ +void cds_get_recovery_reason(enum qdf_hang_reason *reason); + +/** + * cds_reset_recovery_reason() - reset the reason to unspecified + * + * Return: None + */ +void cds_reset_recovery_reason(void); + +/** + * cds_trigger_recovery() - trigger self recovery + * @reason: recovery reason + * + * Return: none + */ +#define cds_trigger_recovery(reason) \ + __cds_trigger_recovery(reason, __func__, __LINE__) + +void cds_trigger_recovery_psoc(void *psoc, enum qdf_hang_reason reason, + const char *func, const uint32_t line); + +void __cds_trigger_recovery(enum qdf_hang_reason reason, const char *func, + const uint32_t line); + +void cds_set_wakelock_logging(bool value); +bool cds_is_wakelock_enabled(void); +void cds_set_ring_log_level(uint32_t ring_id, uint32_t log_level); +enum wifi_driver_log_level cds_get_ring_log_level(uint32_t ring_id); +void cds_set_multicast_logging(uint8_t value); +uint8_t cds_is_multicast_logging(void); +QDF_STATUS cds_set_log_completion(uint32_t is_fatal, + uint32_t type, + uint32_t sub_type, + bool recovery_needed); +void cds_get_and_reset_log_completion(uint32_t *is_fatal, + uint32_t *type, + uint32_t *sub_type, + bool *recovery_needed); +bool cds_is_log_report_in_progress(void); +bool cds_is_fatal_event_enabled(void); + +#ifdef WLAN_FEATURE_TSF_PLUS +bool cds_is_ptp_rx_opt_enabled(void); +bool cds_is_ptp_tx_opt_enabled(void); +#else +static inline bool cds_is_ptp_rx_opt_enabled(void) +{ + return false; +} + +static inline bool cds_is_ptp_tx_opt_enabled(void) +{ + return false; +} +#endif + +uint32_t cds_get_log_indicator(void); +void cds_set_fatal_event(bool value); +void cds_wlan_flush_host_logs_for_fatal(void); + +void cds_init_log_completion(void); +QDF_STATUS cds_flush_logs(uint32_t is_fatal, + uint32_t indicator, + uint32_t reason_code, + bool dump_mac_trace, + bool recovery_needed); +void cds_logging_set_fw_flush_complete(void); +void cds_svc_fw_shutdown_ind(struct device *dev); +#ifdef FEATURE_WLAN_DIAG_SUPPORT +void cds_tdls_tx_rx_mgmt_event(uint8_t event_id, uint8_t tx_rx, + uint8_t type, uint8_t sub_type, uint8_t *peer_mac); +#else +static inline +void cds_tdls_tx_rx_mgmt_event(uint8_t event_id, uint8_t tx_rx, + uint8_t type, uint8_t sub_type, uint8_t *peer_mac) + +{ +} +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +int cds_get_radio_index(void); +QDF_STATUS cds_set_radio_index(int radio_index); +void cds_init_ini_config(struct cds_config_info *cds_cfg); +void cds_deinit_ini_config(void); +struct cds_config_info *cds_get_ini_config(void); + +bool cds_is_5_mhz_enabled(void); +bool cds_is_10_mhz_enabled(void); +bool cds_is_sub_20_mhz_enabled(void); +bool cds_is_self_recovery_enabled(void); +bool cds_is_fw_down(void); +enum QDF_GLOBAL_MODE cds_get_conparam(void); + +#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE +void cds_pkt_stats_to_logger_thread(void *pl_hdr, void *pkt_dump, void *data); +#else +static inline +void cds_pkt_stats_to_logger_thread(void *pl_hdr, void *pkt_dump, void *data) +{ +} +#endif + +#ifdef FEATURE_HTC_CREDIT_HISTORY +/** + * cds_print_htc_credit_history() - Helper function to copy HTC credit + * history via htc_print_credit_history() + * + * @count: Number of lines to be copied + * @print: Print callback to print in the buffer + * + * Return: none + */ +void cds_print_htc_credit_history(uint32_t count, + qdf_abstract_print * print, + void *print_priv); +#else + +static inline +void cds_print_htc_credit_history(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ +} +#endif +/** + * cds_is_group_addr() - checks whether addr is multi cast + * @mac_addr: address to be checked for multicast + * + * Check if the input mac addr is multicast addr + * + * Return: true if multicast addr else false + */ +static inline +bool cds_is_group_addr(uint8_t *mac_addr) +{ + if (mac_addr[0] & 0x01) + return true; + else + return false; +} + +/** + * cds_get_arp_stats_gw_ip() - get arp stats track IP + * @context: osif dev + * + * Return: ARP stats IP to track. + */ +uint32_t cds_get_arp_stats_gw_ip(void *context); +/** + * cds_get_connectivity_stats_pkt_bitmap() - get pkt-type bitmap + * @context: osif dev context + * + * Return: pkt bitmap to track + */ +uint32_t cds_get_connectivity_stats_pkt_bitmap(void *context); +void cds_incr_arp_stats_tx_tgt_delivered(void); +void cds_incr_arp_stats_tx_tgt_acked(void); + +#ifdef FEATURE_ALIGN_STATS_FROM_DP +/** + * cds_dp_get_vdev_stats() - get vdev stats from DP + * @vdev_id: vdev id + * @stats: structure of counters which CP is interested in + * + * Return: if get vdev stats from DP success, return true otherwise false + */ +bool cds_dp_get_vdev_stats(uint8_t vdev_id, struct cds_vdev_dp_stats *stats); +#else +static inline bool +cds_dp_get_vdev_stats(uint8_t vdev_id, struct cds_vdev_dp_stats *stats) +{ + return false; +} +#endif + +/** + * cds_smmu_mem_map_setup() - Check SMMU S1 stage enable + * status and setup wlan driver + * @osdev: Parent device instance + * @ipa_present: IPA HW support flag + * + * This API checks if SMMU S1 translation is enabled in + * platform driver or not and sets it accordingly in driver. + * + * Return: QDF_STATUS + */ +QDF_STATUS cds_smmu_mem_map_setup(qdf_device_t osdev, bool ipa_present); + +/** + * cds_smmu_map_unmap() - Map / Unmap DMA buffer to IPA UC + * @map: Map / unmap operation + * @num_buf: Number of buffers in array + * @buf_arr: Buffer array of DMA mem mapping info + * + * This API maps/unmaps WLAN-IPA buffers if SMMU S1 translation + * is enabled. + * + * Return: Status of map operation + */ +int cds_smmu_map_unmap(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr); + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * cds_is_pktcapture_enabled() - is packet capture support enabled + * + * Check is packet capture mode enabled from ini + * + * Return: 0 - disable, 1 - enable + */ +bool cds_is_pktcapture_enabled(void); + +/** + * cds_get_pktcapture_mode() - get pktcapture mode value + * + * Get the pktcapture mode value from hdd context + * + * Return: 0 - disable + * 1 - Mgmt packets + * 2 - Data packets + * 3 - Both Mgmt and Data packets + */ +uint8_t cds_get_pktcapture_mode(void); +#else +static inline +bool cds_is_pktcapture_enabled(void) +{ + return false; +} + +static inline +uint8_t cds_get_pktcapture_mode(void) +{ + return 0; +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +#endif /* if !defined __CDS_API_H */ diff --git a/drivers/staging/qcacld-3.0/core/cds/inc/cds_config.h b/drivers/staging/qcacld-3.0/core/cds/inc/cds_config.h new file mode 100644 index 0000000000000000000000000000000000000000..75b30bce1c66bf40ace71e0eea36ffa5ff20fadd --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/inc/cds_config.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: cds_config.h + * + * Defines the configuration Information for various modules. Default values + * are read from the INI file and saved into cds_config_info which are passed + * to various modules for the initialization. + */ + +#if !defined(__CDS_CONFIG_H) +#define __CDS_CONFIG_H + +#include "osdep.h" +#include "cdp_txrx_mob_def.h" +#include "wlan_cmn.h" +#include "wlan_cmn_ieee80211.h" +#include "wlan_pmo_common_public_struct.h" +#include "qca_vendor.h" + +/** + * enum cfg_sub_20_channel_width: ini values for su 20 mhz channel width + * @WLAN_SUB_20_CH_WIDTH_5: Use 5 mhz channel width + * @WLAN_SUB_20_CH_WIDTH_10: Use 10 mhz channel width + */ +enum cfg_sub_20_channel_width { + WLAN_SUB_20_CH_WIDTH_NONE = 0, + WLAN_SUB_20_CH_WIDTH_5 = 1, + WLAN_SUB_20_CH_WIDTH_10 = 2, +}; + +/** + * struct cds_config_info - Place Holder for cds configuration + * @max_station: Max station supported + * @max_bssid: Max Bssid Supported + * @sta_maxlimod_dtim: station max listen interval + * @driver_type: Enumeration of Driver Type whether FTM or Mission mode + * currently rest of bits are not used + * Indicates whether support is enabled or not + * @ap_disable_intrabss_fwd: pass intra-bss-fwd info to txrx module + * @ap_maxoffload_peers: max offload peer + * @ap_maxoffload_reorderbuffs: max offload reorder buffs + * @is_ra_ratelimit_enabled: Indicate RA rate limit enabled or not + * @reorder_offload: is RX re-ordering offloaded to the fw + * @dfs_pri_multiplier: dfs radar pri multiplier + * @uc_offload_enabled: IPA Micro controller data path offload enable flag + * @enable_rxthread: Rx processing in thread from TXRX + * @tx_flow_stop_queue_th: Threshold to stop queue in percentage + * @tx_flow_start_queue_offset: Start queue offset in percentage + * @enable_dp_rx_threads: enable dp rx threads + * @is_lpass_enabled: Indicate whether LPASS is enabled or not + * @tx_chain_mask_cck: Tx chain mask enabled or not + * @sub_20_channel_width: Sub 20 MHz ch width, ini intersected with fw cap + * @is_fw_timeout: Indicate whether crash host when fw timesout or not + * @ito_repeat_count: Indicates ito repeated count + * @force_target_assert_enabled: Indicate whether target assert enabled or not + * @bandcapability: Configured band by user + * @rps_enabled: RPS enabled in SAP mode + * Structure for holding cds ini parameters. + * @num_vdevs: Configured max number of VDEVs can be supported in the stack. + */ + +struct cds_config_info { + uint16_t max_station; + uint16_t max_bssid; + uint8_t sta_maxlimod_dtim; + enum qdf_driver_type driver_type; + uint8_t ap_maxoffload_peers; + uint8_t ap_maxoffload_reorderbuffs; + uint8_t reorder_offload; + uint8_t uc_offload_enabled; + bool enable_rxthread; +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) + uint32_t tx_flow_stop_queue_th; + uint32_t tx_flow_start_queue_offset; +#endif + uint8_t enable_dp_rx_threads; +#ifdef WLAN_FEATURE_LPSS + bool is_lpass_enabled; +#endif + enum cfg_sub_20_channel_width sub_20_channel_width; + uint8_t max_msdus_per_rxinorderind; + bool self_recovery_enabled; + bool fw_timeout_crash; + struct ol_tx_sched_wrr_ac_specs_t ac_specs[QCA_WLAN_AC_ALL]; + uint8_t ito_repeat_count; + bool force_target_assert_enabled; + uint8_t bandcapability; + bool rps_enabled; + uint32_t num_vdevs; + bool enable_tx_compl_tsf64; +}; + +#ifdef WLAN_FEATURE_FILS_SK +#define FILS_MAX_KEYNAME_NAI_LENGTH 253 +#define FILS_MAX_REALM_LEN 255 +#define FILS_MAX_RRK_LENGTH 64 +#define FILS_MAX_RIK_LENGTH FILS_MAX_RRK_LENGTH + +struct cds_fils_connection_info { + bool is_fils_connection; + uint8_t keyname_nai[FILS_MAX_KEYNAME_NAI_LENGTH]; + uint32_t key_nai_length; + uint16_t sequence_number; + uint8_t r_rk[FILS_MAX_RRK_LENGTH]; + uint32_t r_rk_length; + uint8_t realm[FILS_MAX_REALM_LEN]; + uint32_t realm_len; + uint8_t akm_type; + uint8_t auth_type; + uint8_t pmk[MAX_PMK_LEN]; + uint8_t pmk_len; + uint8_t pmkid[PMKID_LEN]; +}; +#endif +#endif /* !defined( __CDS_CONFIG_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/cds/inc/cds_crypto.h b/drivers/staging/qcacld-3.0/core/cds/inc/cds_crypto.h new file mode 100644 index 0000000000000000000000000000000000000000..0d507be22bb43bd41dbf3104f601a45de7f836b4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/inc/cds_crypto.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__CDS_CRYPTO_H) +#define __CDS_CRYPTO_H + +/** + * DOC: cds_crypto.h + * + * Crypto APIs + * + */ + +#include + +static inline struct crypto_cipher * +cds_crypto_alloc_cipher(const char *alg_name, u32 type, u32 mask) +{ + return crypto_alloc_cipher(alg_name, type, mask); +} + +static inline void cds_crypto_free_cipher(struct crypto_cipher *tfm) +{ + crypto_free_cipher(tfm); +} + +#endif /* if !defined __CDS_CRYPTO_H */ diff --git a/drivers/staging/qcacld-3.0/core/cds/inc/cds_ieee80211_common.h b/drivers/staging/qcacld-3.0/core/cds/inc/cds_ieee80211_common.h new file mode 100644 index 0000000000000000000000000000000000000000..9cc2f3c8beeb54fc0cd2e684bb29a95a76fda86d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/inc/cds_ieee80211_common.h @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2011,2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef EXTERNAL_USE_ONLY +#include "osdep.h" +#endif /* EXTERNAL_USE_ONLY */ +#include "cds_ieee80211_common_i.h" +#include "cdp_txrx_mob_def.h" + +#ifndef CDS_COMMON_IEEE80211_H_ +#define CDS_COMMON_IEEE80211_H_ + +/* + * generic definitions for IEEE 802.11 frames + */ +struct ieee80211_frame { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + union { + struct { + uint8_t i_addr1[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr2[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr3[QDF_MAC_ADDR_SIZE]; + }; + uint8_t i_addr_all[3 * QDF_MAC_ADDR_SIZE]; + }; + uint8_t i_seq[2]; + /* possibly followed by addr4[QDF_MAC_ADDR_SIZE]; */ + /* see below */ +} __packed; + +struct ieee80211_qosframe { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr2[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr3[QDF_MAC_ADDR_SIZE]; + uint8_t i_seq[2]; + uint8_t i_qos[2]; + /* possibly followed by addr4[QDF_MAC_ADDR_SIZE]; */ + /* see below */ +} __packed; + +struct ieee80211_frame_bar { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_ra[QDF_MAC_ADDR_SIZE]; + uint8_t i_ta[QDF_MAC_ADDR_SIZE]; + uint16_t i_ctl; + uint16_t i_seq; +/* FCS */ +} __packed; + +struct ieee80211_qoscntl { + uint8_t i_qos[2]; +}; + +struct ieee80211_frame_addr4 { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr2[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr3[QDF_MAC_ADDR_SIZE]; + uint8_t i_seq[2]; + uint8_t i_addr4[QDF_MAC_ADDR_SIZE]; +} __packed; + +struct ieee80211_qosframe_addr4 { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr2[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr3[QDF_MAC_ADDR_SIZE]; + uint8_t i_seq[2]; + uint8_t i_addr4[QDF_MAC_ADDR_SIZE]; + uint8_t i_qos[2]; +} __packed; + +/* HTC frame for TxBF*/ +/* for TxBF RC */ +struct ieee80211_frame_min_one { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[QDF_MAC_ADDR_SIZE]; +} __packed; /* For TxBF RC */ + +struct ieee80211_qosframe_htc_addr4 { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr2[QDF_MAC_ADDR_SIZE]; + uint8_t i_addr3[QDF_MAC_ADDR_SIZE]; + uint8_t i_seq[2]; + uint8_t i_addr4[QDF_MAC_ADDR_SIZE]; + uint8_t i_qos[2]; + uint8_t i_htc[4]; +} __packed; + +struct ieee80211_htc { + uint8_t i_htc[4]; +}; + +#define IEEE80211_FC0_VERSION_0 0x00 +#define IEEE80211_FC0_TYPE_MASK 0x0c +#define IEEE80211_FC0_TYPE_MGT 0x00 +#define IEEE80211_FC0_TYPE_CTL 0x04 +#define IEEE80211_FC0_TYPE_DATA 0x08 + +#define IEEE80211_FC0_SUBTYPE_MASK 0xf0 +#define IEEE80211_FC0_SUBTYPE_SHIFT 4 + +#define IEEE80211_FC1_DIR_MASK 0x03 +#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */ +#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */ +#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */ +#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */ + +#define IEEE80211_FC1_MORE_FRAG 0x04 +#define IEEE80211_FC1_RETRY 0x08 +#define IEEE80211_FC1_PWR_MGT 0x10 +#define IEEE80211_FC1_MORE_DATA 0x20 +#define IEEE80211_FC1_ORDER 0x80 + +#define IEEE80211_SEQ_FRAG_MASK 0x000f +#define IEEE80211_SEQ_FRAG_SHIFT 0 +#define IEEE80211_SEQ_SEQ_MASK 0xfff0 +#define IEEE80211_SEQ_SEQ_SHIFT 4 +#define IEEE80211_SEQ_MAX 4096 + +#define IEEE80211_QOS_AMSDU 0x80 +#define IEEE80211_QOS_ACKPOLICY_S 5 +#define IEEE80211_QOS_TID 0x0f + +#define IEEE80211_IS_DATA(_frame) (((_frame)->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) + +/* does frame have QoS sequence control data */ +#define IEEE80211_QOS_HAS_SEQ(wh) \ + (((wh)->i_fc[0] & \ + (IEEE80211_FC0_TYPE_MASK | QDF_IEEE80211_FC0_SUBTYPE_QOS)) == \ + (IEEE80211_FC0_TYPE_DATA | QDF_IEEE80211_FC0_SUBTYPE_QOS)) + +#define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13 + +struct ieee80211_channelswitch_ie { + uint8_t ie; /* IEEE80211_ELEMID_CHANSWITCHANN */ + uint8_t len; + uint8_t switchmode; + uint8_t newchannel; + uint8_t tbttcount; +} __packed; + +struct ieee80211_extendedchannelswitch_ie { + uint8_t ie; /* IEEE80211_ELEMID_EXTCHANSWITCHANN */ + uint8_t len; + uint8_t switchmode; + uint8_t newClass; + uint8_t newchannel; + uint8_t tbttcount; +} __packed; + +/* + * Reason codes + * + * Unlisted codes are reserved + */ +enum { + IEEE80211_REASON_UNSPECIFIED = 1, + IEEE80211_REASON_AUTH_EXPIRE = 2, + IEEE80211_REASON_AUTH_LEAVE = 3, + IEEE80211_REASON_ASSOC_EXPIRE = 4, + IEEE80211_REASON_ASSOC_TOOMANY = 5, + IEEE80211_REASON_NOT_AUTHED = 6, + IEEE80211_REASON_NOT_ASSOCED = 7, + IEEE80211_REASON_ASSOC_LEAVE = 8, + IEEE80211_REASON_ASSOC_NOT_AUTHED = 9, + + IEEE80211_REASON_RSN_REQUIRED = 11, + IEEE80211_REASON_RSN_INCONSISTENT = 12, + IEEE80211_REASON_IE_INVALID = 13, + IEEE80211_REASON_MIC_FAILURE = 14, + + IEEE80211_REASON_QOS = 32, + IEEE80211_REASON_QOS_BANDWITDH = 33, + IEEE80211_REASON_QOS_CH_CONDITIONS = 34, + IEEE80211_REASON_QOS_TXOP = 35, + IEEE80211_REASON_QOS_LEAVE = 36, + IEEE80211_REASON_QOS_DECLINED = 37, + IEEE80211_REASON_QOS_SETUP_REQUIRED = 38, + IEEE80211_REASON_QOS_TIMEOUT = 39, + IEEE80211_REASON_QOS_CIPHER = 45, + + IEEE80211_STATUS_SUCCESS = 0, + IEEE80211_STATUS_UNSPECIFIED = 1, + IEEE80211_STATUS_CAPINFO = 10, + IEEE80211_STATUS_NOT_ASSOCED = 11, + IEEE80211_STATUS_OTHER = 12, + IEEE80211_STATUS_ALG = 13, + IEEE80211_STATUS_SEQUENCE = 14, + IEEE80211_STATUS_CHALLENGE = 15, + IEEE80211_STATUS_TIMEOUT = 16, + IEEE80211_STATUS_TOOMANY = 17, + IEEE80211_STATUS_BASIC_RATE = 18, + IEEE80211_STATUS_SP_REQUIRED = 19, + IEEE80211_STATUS_PBCC_REQUIRED = 20, + IEEE80211_STATUS_CA_REQUIRED = 21, + IEEE80211_STATUS_TOO_MANY_STATIONS = 22, + IEEE80211_STATUS_RATES = 23, + IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25, + IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26, + IEEE80211_STATUS_NO_HT = 27, + IEEE80211_STATUS_REJECT_TEMP = 30, + IEEE80211_STATUS_MFP_VIOLATION = 31, + IEEE80211_STATUS_REFUSED = 37, + IEEE80211_STATUS_INVALID_PARAM = 38, + + IEEE80211_STATUS_DLS_NOT_ALLOWED = 48, +}; + +#define IEEE80211_WEP_IVLEN 3 /* 24bit */ +#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ +#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ + +/* + * 802.11i defines an extended IV for use with non-WEP ciphers. + * When the EXTIV bit is set in the key id byte an additional + * 4 bytes immediately follow the IV for TKIP. For CCMP the + * EXTIV bit is likewise set but the 8 bytes represent the + * CCMP header rather than IV+extended-IV. + */ +#define IEEE80211_WEP_EXTIV 0x20 +#define IEEE80211_WEP_EXTIVLEN 4 /* extended IV length */ +#define IEEE80211_WEP_MICLEN 8 /* trailing MIC */ + +/* + * 802.11w defines a MMIE chunk to be attached at the end of + * any outgoing broadcast or multicast robust management frame. + * MMIE field is total 18 bytes in size. Following the diagram of MMIE + * + * <------------ 18 Bytes MMIE -----------------------> + * +--------+---------+---------+-----------+---------+ + * |Element | Length | Key id | IPN | MIC | + * | id | | | | | + * +--------+---------+---------+-----------+---------+ + * bytes 1 1 2 6 8 + * + */ +#define IEEE80211_MMIE_LEN 18 +#define IEEE80211_MMIE_IPNLEN 6 +#define IEEE80211_MMIE_MICLEN 8 + +/* + * 802.11ac Wide Bandwidth Channel Switch Element + */ + +struct ieee80211_ie_wide_bw_switch { + uint8_t elem_id; + uint8_t elem_len; + uint8_t new_ch_width; /* New channel width */ + uint8_t new_ch_freq_seg1; /* Channel Center frequency 1 */ + uint8_t new_ch_freq_seg2; /* Channel Center frequency 2 */ +} __packed; + +#endif /* CDS_COMMON_IEEE80211_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/cds/inc/cds_packet.h b/drivers/staging/qcacld-3.0/core/cds/inc/cds_packet.h new file mode 100644 index 0000000000000000000000000000000000000000..e2bb13e2c61959e7be83b4ed259ea04417095fdf --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/inc/cds_packet.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__CDS_PKT_H) +#define __CDS_PKT_H + +/**========================================================================= + + \file cds_packet.h + + \brief Connectivity driver services (CDS) network Packet APIs + + Network Protocol packet/buffer support interfaces + + ========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include +#include + +/*-------------------------------------------------------------------------- + Preprocessor definitions and constants + ------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------- + Type declarations + ------------------------------------------------------------------------*/ +struct cds_pkt_t; +typedef struct cds_pkt_t cds_pkt_t; + +#include "qdf_nbuf.h" + +/** + * cds_pkt_return_packet Free the cds Packet + * @ cds Packet + */ +QDF_STATUS cds_pkt_return_packet(cds_pkt_t *packet); + +/** + * cds_pkt_get_packet_length Returns the packet length + * @ cds Packet + */ +QDF_STATUS cds_pkt_get_packet_length(cds_pkt_t *pPacket, + uint16_t *pPacketSize); + +/* + * TODO: Remove later + * All the below difinitions are not + * required for Host Driver 2.0 + * once corresponding references are removed + * from HDD and other layers + * below code will be removed + */ +#ifdef MEMORY_DEBUG +#define cds_packet_alloc(s, d, p) \ + cds_packet_alloc_debug(s, d, p, __FILE__, __LINE__) + +QDF_STATUS cds_packet_alloc_debug(uint16_t size, void **data, void **ppPacket, + uint8_t *file_name, uint32_t line_num); +#else +QDF_STATUS cds_packet_alloc(uint16_t size, void **data, void **ppPacket); +#endif + +void cds_packet_free(void *pPacket); + +#endif /* !defined( __CDS_PKT_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/cds/inc/cds_reg_service.h b/drivers/staging/qcacld-3.0/core/cds/inc/cds_reg_service.h new file mode 100644 index 0000000000000000000000000000000000000000..de2cd7ca7d72e0762335f30739758a89be62a7d7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/inc/cds_reg_service.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CDS_REG_SERVICE_H +#define __CDS_REG_SERVICE_H + +/**========================================================================= + + \file cds_reg_service.h + + \brief Connectivity driver services (CDS): Non-Volatile storage API + + ========================================================================*/ + +#include "qdf_status.h" +#include +#include + +#define CDS_COUNTRY_CODE_LEN 2 +#define CDS_MAC_ADDRESS_LEN 6 +#define HT40PLUS_2G_FCC_CH_END 7 +#define HT40PLUS_2G_EURJAP_CH_END 9 +#define HT40MINUS_2G_CH_START 5 +#define HT40MINUS_2G_CH_END 13 + +/** + * cds_get_vendor_reg_flags() - This API returns vendor specific regulatory + * channel flags + * @pdev: pdev pointer + * @chan: channel number + * @bandwidth: channel BW + * @is_ht_enabled: HT enabled/disabled flag + * @is_vht_enabled: VHT enabled/disabled flag + * @sub_20_channel_width: Sub 20 channel bandwidth + * Return: channel flags + */ +uint32_t cds_get_vendor_reg_flags(struct wlan_objmgr_pdev *pdev, uint32_t chan, + uint16_t bandwidth, + bool is_ht_enabled, bool is_vht_enabled, + uint8_t sub_20_channel_width); +#endif /* __CDS_REG_SERVICE_H */ diff --git a/drivers/staging/qcacld-3.0/core/cds/inc/cds_regdomain.h b/drivers/staging/qcacld-3.0/core/cds/inc/cds_regdomain.h new file mode 100644 index 0000000000000000000000000000000000000000..2c3d6cf47d69749f0d1cde1602a736f7abd41ff3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/inc/cds_regdomain.h @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2011, 2014-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Notifications and licenses are retained for attribution purposes only. + */ +/* + * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting + * Copyright (c) 2005-2006 Atheros Communications, Inc. + * Copyright (c) 2010, Atheros Communications Inc. + * + * Redistribution and use in source and binary forms are permitted + * provided that the following conditions are met: + * 1. The materials contained herein are unmodified and are used + * unmodified. + * 2. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following NO + * ''WARRANTY'' disclaimer below (''Disclaimer''), without + * modification. + * 3. Redistributions in binary form must reproduce at minimum a + * disclaimer similar to the Disclaimer below and any redistribution + * must be conditioned upon including a substantially similar + * Disclaimer requirement for further binary redistribution. + * 4. Neither the names of the above-listed copyright holders nor the + * names of any contributors may be used to endorse or promote + * product derived from this software without specific prior written + * permission. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE + * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + * + * This module contains the regulatory domain private structure definitions . + * + */ + +#ifndef __CDS_REGDOMAIN_H +#define __CDS_REGDOMAIN_H + +#include +#include + +#define MIN_TX_PWR_CAP 8 +#define MAX_TX_PWR_CAP 24 + +#define CTRY_DEFAULT 0 +#define CTRY_FLAG 0x8000 +#define WORLD_ROAMING_FLAG 0x4000 +#define WORLD_ROAMING_MASK 0x00F0 +#define WORLD_ROAMING_PREFIX 0x0060 + +enum country_code { + CTRY_AFGHANISTAN = 4, + CTRY_ALBANIA = 8, + CTRY_ALGERIA = 12, + CTRY_AMERICAN_SAMOA = 16, + CTRY_ANGUILLA = 660, + CTRY_ARGENTINA = 32, + CTRY_ARGENTINA_AP = 5003, + CTRY_ARMENIA = 51, + CTRY_ARUBA = 533, + CTRY_AUSTRALIA = 36, + CTRY_AUSTRALIA_AP = 5000, + CTRY_AUSTRIA = 40, + CTRY_AZERBAIJAN = 31, + CTRY_BAHAMAS = 44, + CTRY_BAHRAIN = 48, + CTRY_BANGLADESH = 50, + CTRY_BARBADOS = 52, + CTRY_BELARUS = 112, + CTRY_BELGIUM = 56, + CTRY_BELIZE = 84, + CTRY_BERMUDA = 60, + CTRY_BHUTAN = 64, + CTRY_BOLIVIA = 68, + CTRY_BOSNIA_HERZ = 70, + CTRY_BRAZIL = 76, + CTRY_BRUNEI_DARUSSALAM = 96, + CTRY_BULGARIA = 100, + CTRY_BURKINA_FASO = 854, + CTRY_CAMBODIA = 116, + CTRY_CANADA = 124, + CTRY_CANADA_AP = 5001, + CTRY_CAYMAN_ISLANDS = 136, + CTRY_CENTRAL_AFRICA_REPUBLIC = 140, + CTRY_CHAD = 148, + CTRY_CHILE = 152, + CTRY_CHINA = 156, + CTRY_CHRISTMAS_ISLAND = 162, + CTRY_COLOMBIA = 170, + CTRY_COSTA_RICA = 188, + CTRY_COTE_DIVOIRE = 384, + CTRY_CROATIA = 191, + CTRY_CYPRUS = 196, + CTRY_CZECH = 203, + CTRY_DENMARK = 208, + CTRY_DOMINICA = 212, + CTRY_DOMINICAN_REPUBLIC = 214, + CTRY_ECUADOR = 218, + CTRY_EGYPT = 818, + CTRY_EL_SALVADOR = 222, + CTRY_ESTONIA = 233, + CTRY_ETHIOPIA = 231, + CTRY_FINLAND = 246, + CTRY_FRANCE = 250, + CTRY_FRENCH_GUIANA = 254, + CTRY_FRENCH_POLYNESIA = 258, + CTRY_GEORGIA = 268, + CTRY_GERMANY = 276, + CTRY_GHANA = 288, + CTRY_GIBRALTAR = 292, + CTRY_GREECE = 300, + CTRY_GREENLAND = 304, + CTRY_GRENADA = 308, + CTRY_GUADELOUPE = 312, + CTRY_GUAM = 316, + CTRY_GUATEMALA = 320, + CTRY_GUYANA = 328, + CTRY_HAITI = 332, + CTRY_HONDURAS = 340, + CTRY_HONG_KONG = 344, + CTRY_HUNGARY = 348, + CTRY_ICELAND = 352, + CTRY_INDIA = 356, + CTRY_INDONESIA = 360, + CTRY_IRAQ = 368, + CTRY_IRELAND = 372, + CTRY_ISRAEL = 376, + CTRY_ITALY = 380, + CTRY_JAMAICA = 388, + CTRY_JORDAN = 400, + CTRY_KAZAKHSTAN = 398, + CTRY_KENYA = 404, + CTRY_KOREA_ROC = 410, + CTRY_KOREA_ROC_AP = 412, + CTRY_KUWAIT = 414, + CTRY_LATVIA = 428, + CTRY_LEBANON = 422, + CTRY_LESOTHO = 426, + CTRY_LIBYA = 434, + CTRY_LIECHTENSTEIN = 438, + CTRY_LITHUANIA = 440, + CTRY_LUXEMBOURG = 442, + CTRY_MACAU = 446, + CTRY_MACEDONIA = 807, + CTRY_MALAWI = 454, + CTRY_MALAYSIA = 458, + CTRY_MALDIVES = 462, + CTRY_MALTA = 470, + CTRY_MARSHALL_ISLANDS = 584, + CTRY_MARTINIQUE = 474, + CTRY_MAURITANIA = 478, + CTRY_MAURITIUS = 480, + CTRY_MAYOTTE = 175, + CTRY_MEXICO = 484, + CTRY_MICRONESIA = 583, + CTRY_MOLDOVA = 498, + CTRY_MONACO = 492, + CTRY_MONGOLIA = 496, + CTRY_MONTENEGRO = 499, + CTRY_MOROCCO = 504, + CTRY_NAMIBIA = 516, + CTRY_NEPAL = 524, + CTRY_NETHERLANDS = 528, + CTRY_NETHERLANDS_ANTILLES = 530, + CTRY_NEW_ZEALAND = 554, + CTRY_NIGERIA = 566, + CTRY_NORTHERN_MARIANA_ISLANDS = 580, + CTRY_NICARAGUA = 558, + CTRY_NORWAY = 578, + CTRY_OMAN = 512, + CTRY_PAKISTAN = 586, + CTRY_PALAU = 585, + CTRY_PANAMA = 591, + CTRY_PAPUA_NEW_GUINEA = 598, + CTRY_PARAGUAY = 600, + CTRY_PERU = 604, + CTRY_PHILIPPINES = 608, + CTRY_POLAND = 616, + CTRY_PORTUGAL = 620, + CTRY_PUERTO_RICO = 630, + CTRY_QATAR = 634, + CTRY_REUNION = 638, + CTRY_ROMANIA = 642, + CTRY_RUSSIA = 643, + CTRY_RWANDA = 646, + CTRY_SAINT_BARTHELEMY = 652, + CTRY_SAINT_KITTS_AND_NEVIS = 659, + CTRY_SAINT_LUCIA = 662, + CTRY_SAINT_MARTIN = 663, + CTRY_SAINT_PIERRE_AND_MIQUELON = 666, + CTRY_SAINT_VINCENT_AND_THE_GRENADIENS = 670, + CTRY_SAMOA = 882, + CTRY_SAUDI_ARABIA = 682, + CTRY_SENEGAL = 686, + CTRY_SERBIA = 688, + CTRY_SINGAPORE = 702, + CTRY_SLOVAKIA = 703, + CTRY_SLOVENIA = 705, + CTRY_SOUTH_AFRICA = 710, + CTRY_SPAIN = 724, + CTRY_SURINAME = 740, + CTRY_SRI_LANKA = 144, + CTRY_SWEDEN = 752, + CTRY_SWITZERLAND = 756, + CTRY_TAIWAN = 158, + CTRY_TANZANIA = 834, + CTRY_THAILAND = 764, + CTRY_TOGO = 768, + CTRY_TRINIDAD_Y_TOBAGO = 780, + CTRY_TUNISIA = 788, + CTRY_TURKEY = 792, + CTRY_TURKS_AND_CAICOS = 796, + CTRY_UGANDA = 800, + CTRY_UKRAINE = 804, + CTRY_UAE = 784, + CTRY_UNITED_KINGDOM = 826, + CTRY_UNITED_STATES = 840, + CTRY_UNITED_STATES_AP = 841, + CTRY_UNITED_STATES_AP2 = 843, + CTRY_UNITED_STATES_PS = 842, + CTRY_URUGUAY = 858, + CTRY_UZBEKISTAN = 860, + CTRY_VANUATU = 548, + CTRY_VENEZUELA = 862, + CTRY_VIET_NAM = 704, + CTRY_VIRGIN_ISLANDS = 850, + CTRY_WALLIS_AND_FUTUNA = 876, + CTRY_YEMEN = 887, + CTRY_ZIMBABWE = 716, + CTRY_JAPAN9 = 4009, + CTRY_JAPAN15 = 4015, + CTRY_JAPAN48 = 4048, + CTRY_JAPAN55 = 4055, + CTRY_JAPAN60 = 4060, + CTRY_XA = 4100, +}; + +enum reg_domain { + NO_ENUMRD = 0x00, + NULL1_WORLD = 0x03, + NULL1_ETSIB = 0x07, + NULL1_ETSIC = 0x08, + + FCC1_FCCA = 0x10, + FCC1_WORLD = 0x11, + FCC2_FCCA = 0x20, + FCC2_WORLD = 0x21, + FCC2_ETSIC = 0x22, + FCC3_FCCA = 0x3A, + FCC3_WORLD = 0x3B, + FCC3_ETSIC = 0x3F, + FCC4_FCCA = 0x12, + FCC5_FCCA = 0x13, + FCC6_FCCA = 0x14, + FCC7_FCCA = 0x15, + FCC8_FCCA = 0x16, + FCC6_WORLD = 0x23, + FCC9_FCCA = 0x17, + FCC10_FCCA = 0x18, + FCC11_WORLD = 0x19, + FCC13_WORLD = 0xE4, + FCC14_FCCB = 0xE6, + + ETSI1_WORLD = 0x37, + ETSI3_ETSIA = 0x32, + ETSI2_WORLD = 0x35, + ETSI3_WORLD = 0x36, + ETSI4_WORLD = 0x30, + ETSI4_ETSIC = 0x38, + ETSI5_WORLD = 0x39, + ETSI6_WORLD = 0x34, + ETSI_RESERVED = 0x33, + FRANCE_RES = 0x31, + ETSI7_WORLD = 0x3C, + ETSI8_WORLD = 0x3D, + ETSI9_WORLD = 0x3E, + ETSI10_WORLD = 0x24, + ETSI11_WORLD = 0x26, + + APL4_WORLD = 0x42, + APL3_FCCA = 0x50, + APL_RESERVED = 0x44, + APL2_WORLD = 0x45, + APL2_FCCA = 0x4D, + APL2_APLC = 0x46, + APL3_WORLD = 0x47, + APL2_APLD = 0x49, + APL1_WORLD = 0x52, + APL1_FCCA = 0x53, + APL1_APLA = 0x54, + APL1_ETSIC = 0x55, + APL2_ETSIC = 0x56, + APL5_WORLD = 0x58, + APL6_WORLD = 0x5B, + APL7_FCCA = 0x5C, + APL8_WORLD = 0x5D, + APL9_WORLD = 0x5E, + APL10_WORLD = 0x5F, + APL11_FCCA = 0x4F, + APL12_WORLD = 0x51, + APL13_WORLD = 0x5A, + APL14_WORLD = 0x57, + APL15_WORLD = 0x59, + APL16_WORLD = 0x70, + APL17_ETSID = 0xE0, + APL20_WORLD = 0xE5, + APL23_WORLD = 0xE3, + + WOR0_WORLD = 0x60, + WOR1_WORLD = 0x61, + WOR2_WORLD = 0x62, + WOR3_WORLD = 0x63, + WOR4_FCCA = 0x64, + WOR5_ETSIC = 0x65, + WOR01_WORLD = 0x66, + WOR02_WORLD = 0x67, + EU1_WORLD = 0x68, + WOR9_WORLD = 0x69, + WORA_WORLD = 0x6A, + WORB_WORLD = 0x6B, + WORC_WORLD = 0x6C, + + MKK3_MKKB = 0x80, + MKK3_MKKA2 = 0x81, + MKK3_MKKC = 0x82, + MKK4_MKKB = 0x83, + MKK4_MKKA2 = 0x84, + MKK4_MKKC = 0x85, + MKK5_MKKA = 0x99, + MKK5_FCCA = 0x9A, + MKK5_MKKB = 0x86, + MKK5_MKKA2 = 0x87, + MKK5_MKKC = 0x88, + MKK3_MKKA = 0xF0, + MKK3_MKKA1 = 0xF1, + MKK3_FCCA = 0xF2, + MKK4_MKKA = 0xF3, + MKK4_MKKA1 = 0xF4, + MKK4_FCCA = 0xF5, + MKK9_MKKA = 0xF6, + MKK9_FCCA = 0xFC, + MKK9_MKKA1 = 0xFD, + MKK9_MKKC = 0xFE, + MKK9_MKKA2 = 0xFF, + MKK10_MKKA = 0xF7, + MKK10_FCCA = 0xD0, + MKK10_MKKA1 = 0xD1, + MKK10_MKKC = 0xD2, + MKK10_MKKA2 = 0xD3, + MKK11_MKKA = 0xD4, + MKK11_FCCA = 0xD5, + MKK11_MKKA1 = 0xD6, + MKK11_MKKC = 0xD7, + MKK11_MKKA2 = 0xD8, + MKK16_MKKC = 0xDF, + + FCC1 = 0x0110, + FCC2 = 0x0120, + FCC3 = 0x0160, + FCC4 = 0x0165, + FCC5 = 0x0510, + FCC6 = 0x0610, + FCC7 = 0x0710, + FCC8 = 0x0810, + FCC9 = 0x0910, + FCC10 = 0x0B10, + FCC11 = 0x0B20, + FCC13 = 0x0B60, + FCC14 = 0x0B70, + + ETSI1 = 0x0130, + ETSI2 = 0x0230, + ETSI3 = 0x0330, + ETSI4 = 0x0430, + ETSI5 = 0x0530, + ETSI6 = 0x0630, + ETSI8 = 0x0830, + ETSI9 = 0x0930, + ETSI10 = 0x0D30, + ETSI11 = 0x0E30, + + APL1 = 0x0150, + APL2 = 0x0250, + APL3 = 0x0350, + APL4 = 0x0450, + APL5 = 0x0550, + APL6 = 0x0650, + APL7 = 0x0750, + APL8 = 0x0850, + APL9 = 0x0950, + APL10 = 0x1050, + APL11 = 0x1150, + APL12 = 0x1160, + APL13 = 0x1170, + APL14 = 0x1180, + APL15 = 0x1190, + APL16 = 0x1200, + APL17 = 0x1210, + APL23 = 0x1280, + APL20 = 0x1250, + + NULL1 = 0x0198, + MKK3 = 0x0340, + MKK5 = 0x0540, + MKK11 = 0x1140, + MKK16 = 0x1640, + + WORLD = 0x0199, + FCCA = 0x0A10, + FCCB = 0x0B90, + MKKA = 0x0A40, + MKKC = 0x0A50, + ETSIC = 0x0C30, + +}; + +/** + * struct reg_dmn_pair: regulatory domain pair + * @reg_dmn_pair: reg domain pair + * @reg_dmn_5ghz: 5G reg domain + * @reg_dmn_2ghz: 2G reg domain + * @single_cc: country with this reg domain + */ +struct reg_dmn_pair { + uint16_t reg_dmn_pair; + uint16_t reg_dmn_5ghz; + uint16_t reg_dmn_2ghz; + uint16_t single_cc; +}; + +/** + * struct country_code_to_reg_dmn: country code to reg domain mapping + * @country_code: country code + * @reg_dmn_pair: regulatory domain pair + * @alpha2: country alpha2 + * @name: country name + */ +struct country_code_to_reg_dmn { + uint16_t country_code; + uint16_t reg_dmn_pair; + const char *alpha2; + const char *name; +}; + +/** + * struct reg_dmn: regulatory domain structure + * @reg_dmn: regulatory domain + * @conformance_test_limit: CTL limit + */ +struct reg_dmn { + uint16_t reg_dmn; + uint8_t conformance_test_limit; +}; + +/** + * struct reg_dmn_tables: reg domain table + * @reg_dmn_pairs: list of reg domain pairs + * @all_countries: list of countries + * @reg_dmns: list of reg domains + * @reg_dmn_pairs_cnt: count of reg domain pairs + * @all_countries_cnt: count of countries + * @reg_dmns_cnt: count of reg domains + */ +struct reg_dmn_tables { + const struct reg_dmn_pair *reg_dmn_pairs; + const struct country_code_to_reg_dmn *all_countries; + const struct reg_dmn *reg_dmns; + uint16_t reg_dmn_pairs_cnt; + uint16_t all_countries_cnt; + uint16_t reg_dmns_cnt; +}; + +int32_t cds_fill_some_regulatory_info(struct regulatory *reg); +int32_t cds_get_country_from_alpha2(uint8_t *alpha2); +void cds_fill_and_send_ctl_to_fw(struct regulatory *reg); +/** + * cds_is_etsi_europe_country - check ETSI Europe country or not + * @country: country string with two Characters + * + * Return: true if country in ETSI Europe country list + */ +bool cds_is_etsi_europe_country(uint8_t *country); +#endif /* __CDS_REGDOMAIN_H */ diff --git a/drivers/staging/qcacld-3.0/core/cds/inc/cds_sched.h b/drivers/staging/qcacld-3.0/core/cds/inc/cds_sched.h new file mode 100644 index 0000000000000000000000000000000000000000..585d996b68a50f22f6648e7895233af0684d852e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/inc/cds_sched.h @@ -0,0 +1,800 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CDS_SCHED_H +#define __CDS_SCHED_H + +/**========================================================================= + + \file cds_sched.h + + \brief Connectivity driver services scheduler + + ========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include +#include +#include +#if defined(WLAN_OPEN_SOURCE) && defined(CONFIG_HAS_WAKELOCK) +#include +#endif +#include +#include "qdf_lock.h" +#include "qdf_mc_timer.h" +#include "cds_config.h" +#include "qdf_cpuhp.h" + +#define MC_SUSPEND_EVENT 0x002 +#define RX_POST_EVENT 0x001 +#define RX_SUSPEND_EVENT 0x002 +#define RX_VDEV_DEL_EVENT 0x004 +#define RX_SHUTDOWN_EVENT 0x010 + +#ifdef QCA_CONFIG_SMP +/* +** Maximum number of cds messages to be allocated for +** OL Rx thread. +*/ +#define CDS_MAX_OL_RX_PKT 4000 +#endif + +typedef void (*cds_ol_rx_thread_cb)(void *context, + qdf_nbuf_t rxpkt, + uint16_t staid); + +/* +** CDS message wrapper for data rx from TXRX +*/ +struct cds_ol_rx_pkt { + struct list_head list; + void *context; + + /* Rx skb */ + qdf_nbuf_t Rxpkt; + + /* Station id to which this packet is destined */ + uint16_t staId; + + /* Call back to further send this packet to txrx layer */ + cds_ol_rx_thread_cb callback; + +}; + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/* + * Maximum number of cds messages to be allocated for + * OL MON thread. + */ +#define CDS_MAX_OL_MON_PKT 4000 + +struct cds_sched_mon_context { + /* MON thread lock */ + spinlock_t ol_mon_thread_lock; + + /* OL MON thread handle */ + struct task_struct *ol_mon_thread; + + /* Handle of Event for MON thread to signal startup */ + struct completion ol_mon_start_event; + + /* Completion object to suspend OL MON thread */ + struct completion ol_suspend_mon_event; + + /* Completion objext to resume OL MON thread */ + struct completion ol_resume_mon_event; + + /* Completion object for OL MON thread shutdown */ + struct completion ol_mon_shutdown; + + /* Waitq for OL MON thread */ + wait_queue_head_t ol_mon_wait_queue; + + unsigned long ol_mon_event_flag; + + /* MON buffer queue */ + struct list_head ol_mon_thread_queue; + + /* Spinlock to synchronize between tasklet and thread */ + spinlock_t ol_mon_queue_lock; + + /* MON queue length */ + unsigned int ol_mon_queue_len; + + /* Lock to synchronize free buffer queue access */ + spinlock_t cds_ol_mon_pkt_freeq_lock; + + /* Free message queue for OL MON processing */ + struct list_head cds_ol_mon_pkt_freeq; + + /* MON thread affinity cpu */ + unsigned long mon_thread_cpu; + +}; + +#endif /* WLAN_FEATURE_PKT_CAPTURE */ +typedef void (*cds_ol_mon_thread_cb)( + void *context, void *monpkt, + uint8_t vdev_id, uint8_t tid, + uint8_t status, bool pkt_format); + +/* + * CDS message wrapper for mon data from TXRX + */ +struct cds_ol_mon_pkt { + struct list_head list; + void *context; + + /* mon skb */ + void *monpkt; + + /* vdev id to which this packet is destined */ + uint8_t vdev_id; + + uint8_t tid; + + /* Tx packet status */ + uint8_t status; + + /* 0 = 802.3 format , 1 = 802.11 format */ + bool pkt_format; + + /* Call back to further send this packet to txrx layer */ + cds_ol_mon_thread_cb callback; +}; + +/* +** CDS Scheduler context +** The scheduler context contains the following: +** ** the messages queues +** ** the handle to the tread +** ** pointer to the events that gracefully shutdown the MC and Tx threads +** +*/ +typedef struct _cds_sched_context { +#ifdef QCA_CONFIG_SMP + spinlock_t ol_rx_thread_lock; + + /* OL Rx thread handle */ + struct task_struct *ol_rx_thread; + + /* Handle of Event for Rx thread to signal startup */ + struct completion ol_rx_start_event; + + /* Completion object to suspend OL rx thread */ + struct completion ol_suspend_rx_event; + + /* Completion object to resume OL rx thread */ + struct completion ol_resume_rx_event; + + /* Completion object for OL Rxthread shutdown */ + struct completion ol_rx_shutdown; + + /* Waitq for OL Rx thread */ + wait_queue_head_t ol_rx_wait_queue; + + unsigned long ol_rx_event_flag; + + /* Rx buffer queue */ + struct list_head ol_rx_thread_queue; + + /* Spinlock to synchronize between tasklet and thread */ + spinlock_t ol_rx_queue_lock; + + /* Lock to synchronize free buffer queue access */ + spinlock_t cds_ol_rx_pkt_freeq_lock; + + /* Free message queue for OL Rx processing */ + struct list_head cds_ol_rx_pkt_freeq; + + /* The CPU hotplug event registration handle, used to unregister */ + struct qdf_cpuhp_handler *cpuhp_event_handle; + + /* affinity lock */ + struct mutex affinity_lock; + + /* Saved rx thread CPU affinity */ + struct cpumask rx_thread_cpu_mask; + + /* CPU affinity bitmask */ + uint8_t conf_rx_thread_cpu_mask; + + /* high throughput required */ + bool high_throughput_required; + + /* affinity requied during uplink traffic*/ + bool rx_affinity_required; + uint8_t conf_rx_thread_ul_affinity; +#endif + +#ifdef WLAN_FEATURE_PKT_CAPTURE + struct cds_sched_mon_context sched_mon_ctx; +#endif /* WLAN_FEATURE_PKT_CAPTURE */ +} cds_sched_context, *p_cds_sched_context; + +/** + * struct cds_log_complete - Log completion internal structure + * @is_fatal: Type is fatal or not + * @indicator: Source of bug report + * @reason_code: Reason code for bug report + * @is_report_in_progress: If bug report is in progress + * @recovery_needed: if recovery is needed after report completion + * + * This structure internally stores the log related params + */ +struct cds_log_complete { + uint32_t is_fatal; + uint32_t indicator; + uint32_t reason_code; + bool is_report_in_progress; + bool recovery_needed; +}; + +struct cds_context { + /* Scheduler Context */ + cds_sched_context qdf_sched; + + /* HDD Module Context */ + void *hdd_context; + + /* MAC Module Context */ + void *mac_context; + + uint32_t driver_state; + + qdf_event_t wma_complete_event; + + /* WMA Context */ + void *wma_context; + + void *hif_context; + + void *htc_ctx; + + void *g_ol_context; + /* + * qdf_ctx will be used by qdf + * while allocating dma memory + * to access dev information. + */ + qdf_device_t qdf_ctx; + + void *dp_soc; + + void *dp_mem_pre_alloc_ctx; + + /* Configuration handle used to get system configuration */ + struct cdp_cfg *cfg_ctx; + + /* radio index per driver */ + int radio_index; + + bool is_wakelock_log_enabled; + uint32_t wakelock_log_level; + uint32_t connectivity_log_level; + uint32_t packet_stats_log_level; + uint32_t driver_debug_log_level; + uint32_t fw_debug_log_level; + struct cds_log_complete log_complete; + qdf_spinlock_t bug_report_lock; + + bool enable_fatal_event; + struct cds_config_info *cds_cfg; + + struct ol_tx_sched_wrr_ac_specs_t ac_specs[QCA_WLAN_AC_ALL]; + qdf_work_t cds_recovery_work; + qdf_workqueue_t *cds_recovery_wq; + enum qdf_hang_reason recovery_reason; +}; + +/*--------------------------------------------------------------------------- + Function declarations and documenation + ---------------------------------------------------------------------------*/ +#ifdef QCA_CONFIG_SMP +int cds_sched_handle_cpu_hot_plug(void); +int cds_sched_handle_throughput_req(bool high_tput_required); + +/** + * cds_sched_handle_rx_thread_affinity_req - rx thread affinity req handler + * @high_tput_required: high throughput is required or not + * + * rx thread affinity handler will find online cores and + * will assign proper core based on perf requirement + * + * Return: None + */ +void cds_sched_handle_rx_thread_affinity_req(bool high_throughput); + +/** + * cds_set_rx_thread_ul_cpu_mask() - Rx_thread affinity for UL from INI + * @cpu_affinity_mask: CPU affinity bitmap + * + * Return:None + */ +void cds_set_rx_thread_ul_cpu_mask(uint8_t cpu_affinity_mask); + +/** + * cds_set_rx_thread_cpu_mask() - Rx_thread affinity from INI + * @cpu_affinity_mask: CPU affinity bitmap + * + * Return:None + */ +void cds_set_rx_thread_cpu_mask(uint8_t cpu_affinity_mask); + +/*--------------------------------------------------------------------------- + \brief cds_drop_rxpkt_by_staid() - API to drop pending Rx packets for a sta + The \a cds_drop_rxpkt_by_staid() drops queued packets for a station, to drop + all the pending packets the caller has to send WLAN_MAX_STA_COUNT as staId. + \param pSchedContext - pointer to the global CDS Sched Context + \param staId - Station Id + + \return Nothing + \sa cds_drop_rxpkt_by_staid() + -------------------------------------------------------------------------*/ +void cds_drop_rxpkt_by_staid(p_cds_sched_context pSchedContext, uint16_t staId); + +/*--------------------------------------------------------------------------- + \brief cds_indicate_rxpkt() - API to Indicate rx data packet + The \a cds_indicate_rxpkt() enqueues the rx packet onto ol_rx_thread_queue + and notifies cds_ol_rx_thread(). + \param Arg - pointer to the global CDS Sched Context + \param pkt - Vos data message buffer + + \return Nothing + \sa cds_indicate_rxpkt() + -------------------------------------------------------------------------*/ +void cds_indicate_rxpkt(p_cds_sched_context pSchedContext, + struct cds_ol_rx_pkt *pkt); + +/** + * cds_close_rx_thread() - close the Rx thread + * + * This api closes the Rx thread: + * + * Return: qdf status + */ +QDF_STATUS cds_close_rx_thread(void); + +/*--------------------------------------------------------------------------- + \brief cds_alloc_ol_rx_pkt() - API to return next available cds message + The \a cds_alloc_ol_rx_pkt() returns next available cds message buffer + used for Rx Data processing. + \param pSchedContext - pointer to the global CDS Sched Context + + \return pointer to cds message buffer + \sa cds_alloc_ol_rx_pkt() + -------------------------------------------------------------------------*/ +struct cds_ol_rx_pkt *cds_alloc_ol_rx_pkt(p_cds_sched_context pSchedContext); + +/*--------------------------------------------------------------------------- + \brief cds_free_ol_rx_pkt() - API to release cds message to the freeq + The \a cds_free_ol_rx_pkt() returns the cds message used for Rx data + to the free queue. + \param pSchedContext - pointer to the global CDS Sched Context + \param pkt - Vos message buffer to be returned to free queue. + + \return Nothing + \sa cds_free_ol_rx_pkt() + -------------------------------------------------------------------------*/ +void cds_free_ol_rx_pkt(p_cds_sched_context pSchedContext, + struct cds_ol_rx_pkt *pkt); +/*--------------------------------------------------------------------------- + \brief cds_free_ol_rx_pkt_freeq() - Free cdss buffer free queue + The \a cds_free_ol_rx_pkt_freeq() does mem free of the buffers + available in free cds buffer queue which is used for Data rx processing + from Tlshim. + \param pSchedContext - pointer to the global CDS Sched Context + + \return Nothing + \sa cds_free_ol_rx_pkt_freeq() + -------------------------------------------------------------------------*/ +void cds_free_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext); +#else +/** + * cds_sched_handle_rx_thread_affinity_req - rx thread affinity req handler + * @high_tput_required: high throughput is required or not + * + * rx thread affinity handler will find online cores and + * will assign proper core based on perf requirement + * + * Return: None + */ +static inline void cds_sched_handle_rx_thread_affinity_req( + bool high_throughput) {} + +/** + * cds_set_rx_thread_ul_cpu_mask() - Rx_thread affinity for UL from INI + * @cpu_affinity_mask: CPU affinity bitmap + * + * Return:None + */ +static inline void cds_set_rx_thread_ul_cpu_mask(uint8_t cpu_affinity_mask) {} + +/** + * cds_set_rx_thread_cpu_mask() - Rx_thread affinity from INI + * @cpu_affinity_mask: CPU affinity bitmap + * + * Return:None + */ +static inline void cds_set_rx_thread_cpu_mask(uint8_t cpu_affinity_mask) {} + +/** + * cds_drop_rxpkt_by_staid() - api to drop pending rx packets for a sta + * @pSchedContext: Pointer to the global CDS Sched Context + * @staId: Station Id + * + * This api drops queued packets for a station, to drop all the pending + * packets the caller has to send WLAN_MAX_STA_COUNT as staId. + * + * Return: none + */ +static inline +void cds_drop_rxpkt_by_staid(p_cds_sched_context pSchedContext, uint16_t staId) +{ +} + +/** + * cds_indicate_rxpkt() - API to Indicate rx data packet + * @pSchedContext: pointer to CDS Sched Context + * @pkt: CDS OL RX pkt pointer containing to RX data message buffer + * + * Return: none + */ +static inline +void cds_indicate_rxpkt(p_cds_sched_context pSchedContext, + struct cds_ol_rx_pkt *pkt) +{ +} + +/** + * cds_close_rx_thread() - close the Rx thread + * + * This api closes the Rx thread: + * + * Return: qdf status + */ +static inline +QDF_STATUS cds_close_rx_thread(void) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * cds_alloc_ol_rx_pkt() - API to return next available cds message + * @pSchedContext: pointer to CDS Sched Context + * + * Return: none + */ +static inline +struct cds_ol_rx_pkt *cds_alloc_ol_rx_pkt(p_cds_sched_context pSchedContext) +{ + return NULL; +} + +/** + * cds_free_ol_rx_pkt() - API to release cds message to the freeq + * @pSchedContext: pointer to CDS Sched Context + * @pkt: CDS message buffer to be returned to free queue + * + * Return: none + */ +static inline +void cds_free_ol_rx_pkt(p_cds_sched_context pSchedContext, + struct cds_ol_rx_pkt *pkt) +{ +} + +/** + * cds_free_ol_rx_pkt_freeq() - Free cds buffer free queue + * @pSchedContext: pointer to CDS Sched Context + * @pkt: CDS message buffer to be returned to free queue + * + * Return: none + */ +static inline +void cds_free_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext) +{ +} + +static inline int cds_sched_handle_throughput_req( + bool high_tput_required) +{ + return 0; +} + +#endif + +/*--------------------------------------------------------------------------- + + \brief cds_sched_open() - initialize the CDS Scheduler + + The \a cds_sched_open() function initializes the CDS Scheduler + Upon successful initialization: + + - All the message queues are initialized + + - The Main Controller thread is created and ready to receive and + dispatch messages. + + - The Tx thread is created and ready to receive and dispatch messages + + \param p_cds_context - pointer to the global QDF Context + + \param p_cds_sched_context - pointer to a previously allocated buffer big + enough to hold a scheduler context. + + \return QDF_STATUS_SUCCESS - Scheduler was successfully initialized and + is ready to be used. + + QDF_STATUS_E_RESOURCES - System resources (other than memory) + are unavailable to initialize the scheduler + + QDF_STATUS_E_NOMEM - insufficient memory exists to initialize + the scheduler + + QDF_STATUS_E_INVAL - Invalid parameter passed to the scheduler Open + function + + QDF_STATUS_E_FAILURE - Failure to initialize the scheduler/ + + \sa cds_sched_open() + + -------------------------------------------------------------------------*/ +QDF_STATUS cds_sched_open(void *p_cds_context, + p_cds_sched_context pSchedCxt, uint32_t SchedCtxSize); + +/*--------------------------------------------------------------------------- + + \brief cds_sched_close() - Close the CDS Scheduler + + The \a cds_sched_closes() function closes the CDS Scheduler + Upon successful closing: + + - All the message queues are flushed + + - The Main Controller thread is closed + + - The Tx thread is closed + + \return QDF_STATUS_SUCCESS - Scheduler was successfully initialized and + is ready to be used. + + QDF_STATUS_E_INVAL - Invalid parameter passed to the scheduler Open + function + + QDF_STATUS_E_FAILURE - Failure to initialize the scheduler/ + + \sa cds_sched_close() + + ---------------------------------------------------------------------------*/ +QDF_STATUS cds_sched_close(void); + +p_cds_sched_context get_cds_sched_ctxt(void); + +void qdf_timer_module_init(void); +void qdf_timer_module_deinit(void); +void cds_ssr_protect_init(void); +int cds_get_gfp_flags(void); + +/** + * cds_shutdown_notifier_register() - Register for shutdown notification + * @cb : Call back to be called + * @priv : Private pointer to be passed back to call back + * + * During driver remove or shutdown (recovery), external threads might be stuck + * waiting on some event from firmware at lower layers. Remove or shutdown can't + * proceed till the thread completes to avoid any race condition. Call backs can + * be registered here to get early notification of remove or shutdown so that + * waiting thread can be unblocked and hence remove or shutdown can proceed + * further as waiting there may not make sense when FW may already have been + * down. + * + * Return: CDS status + */ +QDF_STATUS cds_shutdown_notifier_register(void (*cb)(void *priv), void *priv); + +/** + * cds_shutdown_notifier_purge() - Purge all the notifiers + * + * Shutdown notifiers are added to provide the early notification of remove or + * shutdown being initiated. Adding this API to purge all the registered call + * backs as they are not useful any more while all the lower layers are being + * shutdown. + * + * Return: None + */ +void cds_shutdown_notifier_purge(void); + +/** + * cds_shutdown_notifier_call() - Call shutdown notifier call back + * + * Call registered shutdown notifier call back to indicate about remove or + * shutdown. + */ +void cds_shutdown_notifier_call(void); + +/** + * cds_resume_rx_thread() - resume rx thread by completing its resume event + * + * Resume RX thread by completing RX thread resume event + * + * Return: None + */ +void cds_resume_rx_thread(void); + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * cds_resume_mon_thread() - resume mon thread by completing its resume event + * + * Resume MON thread by completing RX thread resume event + * + * Return: None + */ +void cds_resume_mon_thread(void); + +/** + * cds_drop_monpkt() - API to drop pending mon packets + * @pschedcontext: Pointer to the global CDS Sched Context + * + * This api drops all the pending packets in the queue. + * + * Return: none + */ +void cds_drop_monpkt(p_cds_sched_context pschedcontext); + +/** + * cds_indicate_monpkt() - API to Indicate rx data packet + * @pschedcontext: pointer to CDS Sched Context + * @pkt: CDS OL MON pkt pointer containing to mon data message buffer + * + * Return: none + */ +void cds_indicate_monpkt(p_cds_sched_context pschedcontext, + struct cds_ol_mon_pkt *pkt); + +/** + * cds_wakeup_mon_thread() - wakeup mon thread + * @Arg: Pointer to the global CDS Sched Context + * + * This api wake up cds_ol_mon_thread() to process pkt + * + * Return: none + */ +void cds_wakeup_mon_thread(p_cds_sched_context pschedcontext); + +/** + * cds_close_mon_thread() - close the Tlshim MON thread + * + * This api closes the Tlshim MON thread: + * + * Return: qdf status + */ +QDF_STATUS cds_close_mon_thread(void); + +/** + * cds_open_mon_thread() - open the Tlshim MON thread + * @pSchedContext: Pointer to the global CDS Sched Context + * + * This api opens the Tlshim MON thread: + * + * Return: qdf status + */ +QDF_STATUS cds_open_mon_thread(p_cds_sched_context pschedcontext); + +/** + * cds_alloc_mon_thread() - alloc resources for MON thread + * @pSchedContext: Pointer to the global CDS Sched Context + * + * This api alloc resources for MON thread: + * + * Return: qdf status + */ +QDF_STATUS cds_alloc_mon_thread(p_cds_sched_context pschedcontext); + +/** + * cds_alloc_ol_mon_pkt() - API to return next available cds message + * @pSchedContext: Pointer to the global CDS Sched Context + * + * This api returns next available cds message buffer used for mon data + * processing + * + * Return: Pointer to cds message buffer + */ +struct cds_ol_mon_pkt *cds_alloc_ol_mon_pkt(p_cds_sched_context pschedcontext); + +/** + * cds_free_ol_mon_pkt() - api to release cds message to the freeq + * This api returns the cds message used for mon data to the free queue + * @pSchedContext: Pointer to the global CDS Sched Context + * @pkt: CDS message buffer to be returned to free queue. + * + * Return: none + */ +void cds_free_ol_mon_pkt(p_cds_sched_context pschedcontext, + struct cds_ol_mon_pkt *pkt); + +/** + * cds_free_ol_mon_pkt_freeq() - free cds buffer free queue + * @pSchedContext - pointer to the global CDS Sched Context + * + * This API does mem free of the buffers available in free cds buffer + * queue which is used for mon Data processing. + * + * Return: none + */ +void cds_free_ol_mon_pkt_freeq(p_cds_sched_context pschedcontext); +#else +static inline +void cds_resume_mon_thread(void) +{ +} + +static inline +void cds_drop_monpkt(p_cds_sched_context pschedcontext) +{ +} + +static inline +void cds_indicate_monpkt(p_cds_sched_context pschedcontext, + struct cds_ol_mon_pkt *pkt) +{ +} + +static inline +void cds_wakeup_mon_thread(p_cds_sched_context pschedcontext) +{ +} + +static inline +QDF_STATUS cds_close_mon_thread(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS cds_open_mon_thread(p_cds_sched_context pschedcontext) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS cds_alloc_mon_thread(p_cds_sched_context pschedcontext) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +struct cds_ol_mon_pkt *cds_alloc_ol_mon_pkt(p_cds_sched_context pschedcontext) +{ + return NULL; +} + +static inline +void cds_free_ol_mon_pkt(p_cds_sched_context pschedcontext, + struct cds_ol_mon_pkt *pkt) +{ +} + +static inline +void cds_free_ol_mon_pkt_freeq(p_cds_sched_context pschedcontext) +{ +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ +#endif /* #ifndef __CDS_SCHED_H */ diff --git a/drivers/staging/qcacld-3.0/core/cds/inc/cds_utils.h b/drivers/staging/qcacld-3.0/core/cds/inc/cds_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..bdef1514f6a0a1ec0a7a28b862041654ddc48c39 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/inc/cds_utils.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CDS_UTILS_H +#define __CDS_UTILS_H + +/**========================================================================= + + \file cds_utils.h + + \brief Connectivity driver services (CDS) utility APIs + + Various utility functions + + ========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include "ani_global.h" + +/*-------------------------------------------------------------------------- + Preprocessor definitions and constants + ------------------------------------------------------------------------*/ +#define CDS_24_GHZ_BASE_FREQ (2407) +#define CDS_5_GHZ_BASE_FREQ (5000) +#define CDS_24_GHZ_CHANNEL_1 (1) +#define CDS_24_GHZ_CHANNEL_14 (14) +#define CDS_24_GHZ_CHANNEL_15 (15) +#define CDS_24_GHZ_CHANNEL_27 (27) +#define CDS_5_GHZ_CHANNEL_165 (165) +#define CDS_5_GHZ_CHANNEL_170 (170) +#define CDS_CHAN_SPACING_5MHZ (5) +#define CDS_CHAN_SPACING_20MHZ (20) +#define CDS_CHAN_14_FREQ (2484) +#define CDS_CHAN_15_FREQ (2512) +#define CDS_CHAN_170_FREQ (5852) + +#define INVALID_SCAN_ID 0xFFFFFFFF + +#define CDS_DBS_SCAN_CLIENTS_MAX (7) +#define CDS_DBS_SCAN_PARAM_PER_CLIENT (3) + +#define cds_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_QDF, params) +#define cds_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_QDF, params) +#define cds_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_QDF, params) +#define cds_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_QDF, params) +#define cds_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_QDF, params) + +#define cds_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_QDF, params) +#define cds_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_QDF, params) +#define cds_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_QDF, params) +#define cds_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_QDF, params) +#define cds_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_QDF, params) + +#define cds_enter() QDF_TRACE_ENTER(QDF_MODULE_ID_QDF, "enter") +#define cds_exit() QDF_TRACE_EXIT(QDF_MODULE_ID_QDF, "exit") + +/** + * enum cds_band_type - Band type - 2g, 5g or all + * CDS_BAND_ALL: Both 2G and 5G are valid. + * CDS_BAND_2GHZ: only 2G is valid. + * CDS_BAND_5GHZ: only 5G is valid. + */ +enum cds_band_type { + CDS_BAND_ALL = 0, + CDS_BAND_2GHZ = 1, + CDS_BAND_5GHZ = 2 +}; + +/*------------------------------------------------------------------------- + Function declarations and documenation + ------------------------------------------------------------------------*/ + +uint32_t cds_chan_to_freq(uint8_t chan); +uint8_t cds_freq_to_chan(uint32_t freq); +enum cds_band_type cds_chan_to_band(uint32_t chan); + +#ifdef WLAN_FEATURE_11W +bool cds_is_mmie_valid(uint8_t *key, uint8_t *ipn, + uint8_t *frm, uint8_t *efrm); +bool cds_attach_mmie(uint8_t *igtk, uint8_t *ipn, uint16_t key_id, + uint8_t *frm, uint8_t *efrm, uint16_t frmLen); +uint8_t cds_get_mmie_size(void); +/** + * cds_is_gmac_mmie_valid: Validates GMAC MIC + * @igtk: integrity group temporal key + * @ipn: IGTK packet number + * @frm: IEEE 802.11 frame + * @efrm: End of frame + * @key_length: Length of IGTK + * + * Return: True if MIC validation is successful, false otherwise + */ +bool cds_is_gmac_mmie_valid(uint8_t *igtk, uint8_t *ipn, uint8_t *frm, + uint8_t *efrm, uint16_t key_length); + +/** + * cds_get_gmac_mmie_size: Gives length of GMAC MMIE size + * + * Return: Size of MMIE for GMAC + */ +uint8_t cds_get_gmac_mmie_size(void); + +#endif /* WLAN_FEATURE_11W */ +static inline void cds_host_diag_log_work(qdf_wake_lock_t *lock, uint32_t msec, + uint32_t reason) { + if (((cds_get_ring_log_level(RING_ID_WAKELOCK) >= WLAN_LOG_LEVEL_ACTIVE) + && (WIFI_POWER_EVENT_WAKELOCK_HOLD_RX == reason)) || + (WIFI_POWER_EVENT_WAKELOCK_HOLD_RX != reason)) { + host_diag_log_wlock(reason, qdf_wake_lock_name(lock), + msec, WIFI_POWER_EVENT_WAKELOCK_TAKEN); + } +} + +/** + * cds_copy_hlp_info() - Copy HLP info + * @input_dst_mac: input HLP destination MAC address + * @input_src_mac: input HLP source MAC address + * @input_hlp_data_len: input HLP data length + * @input_hlp_data: Pointer to input HLP data + * @output_dst_mac: output HLP destination MAC address + * @output_src_mac: output HLP source MAC address + * @output_hlp_data_len: Pointer to output HLP data length + * @output_hlp_data: output Pointer to HLP data + * + * Util API to copy HLP info from input to output + * + * Return: None + */ +void cds_copy_hlp_info(struct qdf_mac_addr *input_dst_mac, + struct qdf_mac_addr *input_src_mac, + uint16_t input_hlp_data_len, + uint8_t *input_hlp_data, + struct qdf_mac_addr *output_dst_mac, + struct qdf_mac_addr *output_src_mac, + uint16_t *output_hlp_data_len, + uint8_t *output_hlp_data); +#endif /* #ifndef __CDS_UTILS_H */ diff --git a/drivers/staging/qcacld-3.0/core/cds/src/cds_api.c b/drivers/staging/qcacld-3.0/core/cds/src/cds_api.c new file mode 100644 index 0000000000000000000000000000000000000000..d28382a58d43a88f597bc6fd32680da9021df4e7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/src/cds_api.c @@ -0,0 +1,3085 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: cds_api.c + * + * Connectivity driver services APIs + */ + +#include +#include "sir_types.h" +#include "sir_api.h" +#include "sir_mac_prot_def.h" +#include "sme_api.h" +#include "mac_init_api.h" +#include "wlan_qct_sys.h" +#include "i_cds_packet.h" +#include "cds_reg_service.h" +#include "wma_types.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_power.h" +#include "wlan_hdd_tsf.h" +#include +#include + +#include "pld_common.h" +#include "sap_api.h" +#include "bmi.h" +#include "ol_fw.h" +#include "ol_if_athvar.h" +#include "hif.h" +#include "wlan_policy_mgr_api.h" +#include "cds_utils.h" +#include "wlan_logging_sock_svc.h" +#include "wma.h" +#include "pktlog_ac.h" +#include "wlan_policy_mgr_api.h" + +#include +#include +#include +#include +#include +#include +#include +#include "target_type.h" +#include "wlan_ocb_ucfg_api.h" +#include "wlan_ipa_ucfg_api.h" +#include "dp_txrx.h" +#ifdef ENABLE_SMMU_S1_TRANSLATION +#include "pld_common.h" +#include +#include +#endif + +#ifdef QCA_WIFI_QCA8074 +#include +#endif +#include "wlan_mlme_ucfg_api.h" +#include "cfg_ucfg_api.h" +#include "wlan_cp_stats_mc_ucfg_api.h" +#include +#include +#include +#include + +/* Preprocessor Definitions and Constants */ + +/* Preprocessor Definitions and Constants */ + +/* Data definitions */ +static struct cds_context g_cds_context; +static struct cds_context *gp_cds_context; +static struct __qdf_device g_qdf_ctx; + +static uint8_t cds_multicast_logging; + +#define DRIVER_VER_LEN (11) +#define HANG_EVENT_VER_LEN (1) + +struct cds_hang_event_fixed_param { + uint16_t tlv_header; + uint8_t recovery_reason; + char driver_version[DRIVER_VER_LEN]; + char hang_event_version[HANG_EVENT_VER_LEN]; +} qdf_packed; + +#ifdef QCA_WIFI_QCA8074 +static inline int +cds_send_delba(struct cdp_ctrl_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t *peer_macaddr, + uint8_t tid, uint8_t reason_code) +{ + return wma_dp_send_delba_ind(vdev_id, peer_macaddr, tid, reason_code); +} + +static struct ol_if_ops dp_ol_if_ops = { + .peer_set_default_routing = target_if_peer_set_default_routing, + .peer_rx_reorder_queue_setup = target_if_peer_rx_reorder_queue_setup, + .peer_rx_reorder_queue_remove = target_if_peer_rx_reorder_queue_remove, + .is_hw_dbs_2x2_capable = policy_mgr_is_dp_hw_dbs_2x2_capable, + .lro_hash_config = target_if_lro_hash_config, + .rx_invalid_peer = wma_rx_invalid_peer_ind, + .is_roam_inprogress = wma_is_roam_in_progress, + .get_con_mode = cds_get_conparam, + .send_delba = cds_send_delba, +#ifdef DP_MEM_PRE_ALLOC + .dp_prealloc_get_context = dp_prealloc_get_context_memory, + .dp_prealloc_put_context = dp_prealloc_put_context_memory, + .dp_prealloc_get_consistent = dp_prealloc_get_coherent, + .dp_prealloc_put_consistent = dp_prealloc_put_coherent, + .dp_get_multi_pages = dp_prealloc_get_multi_pages, + .dp_put_multi_pages = dp_prealloc_put_multi_pages, +#endif + .dp_rx_get_pending = dp_rx_tm_get_pending, + /* TODO: Add any other control path calls required to OL_IF/WMA layer */ +}; +#else +static struct ol_if_ops dp_ol_if_ops; +#endif + +static void cds_trigger_recovery_work(void *param); + +/** + * struct cds_recovery_call_info - caller information for cds_trigger_recovery + * @func: caller's function name + * @line: caller's line number + */ +struct cds_recovery_call_info { + const char *func; + uint32_t line; +} __cds_recovery_caller; + +/** + * cds_recovery_work_init() - Initialize recovery work queue + * + * Return: none + */ +static QDF_STATUS cds_recovery_work_init(void) +{ + qdf_create_work(0, &gp_cds_context->cds_recovery_work, + cds_trigger_recovery_work, &__cds_recovery_caller); + gp_cds_context->cds_recovery_wq = + qdf_create_workqueue("cds_recovery_workqueue"); + if (!gp_cds_context->cds_recovery_wq) { + cds_err("Failed to create cds_recovery_workqueue"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_recovery_work_deinit() - Initialize recovery work queue + * + * Return: none + */ +static void cds_recovery_work_deinit(void) +{ + if (gp_cds_context->cds_recovery_wq) { + qdf_flush_workqueue(0, gp_cds_context->cds_recovery_wq); + qdf_destroy_workqueue(0, gp_cds_context->cds_recovery_wq); + } +} + +static bool cds_is_drv_connected(void) +{ + int ret; + qdf_device_t qdf_ctx; + + qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_ctx) { + cds_err("cds context is invalid"); + return false; + } + + ret = pld_is_drv_connected(qdf_ctx->dev); + + return ((ret > 0) ? true : false); +} + +static bool cds_is_drv_supported(void) +{ + qdf_device_t qdf_ctx; + struct pld_platform_cap cap = {0}; + + qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_ctx) { + cds_err("cds context is invalid"); + return false; + } + + pld_get_platform_cap(qdf_ctx->dev, &cap); + + return ((cap.cap_flag & PLD_HAS_DRV_SUPPORT) ? true : false); +} + +static QDF_STATUS cds_wmi_send_recv_qmi(void *buf, uint32_t len, void * cb_ctx, + qdf_wmi_recv_qmi_cb wmi_rx_cb) +{ + qdf_device_t qdf_ctx; + + qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_ctx) { + cds_err("cds context is invalid"); + return QDF_STATUS_E_INVAL; + } + + if (pld_qmi_send(qdf_ctx->dev, 0, buf, len, cb_ctx, wmi_rx_cb)) + return QDF_STATUS_E_INVAL; + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_update_recovery_reason() - update the recovery reason code + * @reason: recovery reason + * + * Return: None + */ +static void cds_update_recovery_reason(enum qdf_hang_reason recovery_reason) +{ + if (!gp_cds_context) { + cds_err("gp_cds_context is null"); + return; + } + + gp_cds_context->recovery_reason = recovery_reason; +} + +QDF_STATUS cds_init(void) +{ + QDF_STATUS status; + + gp_cds_context = &g_cds_context; + + status = cds_recovery_work_init(); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed to init recovery work; status:%u", status); + goto deinit; + } + + cds_ssr_protect_init(); + + gp_cds_context->qdf_ctx = &g_qdf_ctx; + + qdf_register_self_recovery_callback(cds_trigger_recovery_psoc); + qdf_register_fw_down_callback(cds_is_fw_down); + qdf_register_is_driver_unloading_callback(cds_is_driver_unloading); + qdf_register_recovering_state_query_callback(cds_is_driver_recovering); + qdf_register_drv_connected_callback(cds_is_drv_connected); + qdf_register_drv_supported_callback(cds_is_drv_supported); + qdf_register_wmi_send_recv_qmi_callback(cds_wmi_send_recv_qmi); + qdf_register_recovery_reason_update(cds_update_recovery_reason); + qdf_register_get_bus_reg_dump(pld_get_bus_reg_dump); + + return QDF_STATUS_SUCCESS; + +deinit: + gp_cds_context = NULL; + qdf_mem_zero(&g_cds_context, sizeof(g_cds_context)); + + return status; +} + +/** + * cds_deinit() - Deinitialize CDS + * + * This function frees the CDS resources + */ +void cds_deinit(void) +{ + QDF_BUG(gp_cds_context); + if (!gp_cds_context) + return; + + qdf_register_get_bus_reg_dump(NULL); + qdf_register_recovery_reason_update(NULL); + qdf_register_recovering_state_query_callback(NULL); + qdf_register_fw_down_callback(NULL); + qdf_register_is_driver_unloading_callback(NULL); + qdf_register_self_recovery_callback(NULL); + qdf_register_wmi_send_recv_qmi_callback(NULL); + + gp_cds_context->qdf_ctx = NULL; + qdf_mem_zero(&g_qdf_ctx, sizeof(g_qdf_ctx)); + + /* currently, no ssr_protect_deinit */ + + cds_recovery_work_deinit(); + + gp_cds_context = NULL; + qdf_mem_zero(&g_cds_context, sizeof(g_cds_context)); +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +/** + * cds_tdls_tx_rx_mgmt_event()- send tdls mgmt rx tx event + * @event_id: event id + * @tx_rx: tx or rx + * @type: type of frame + * @action_sub_type: action frame type + * @peer_mac: peer mac + * + * This Function sends tdls mgmt rx tx diag event + * + * Return: void. + */ +void cds_tdls_tx_rx_mgmt_event(uint8_t event_id, uint8_t tx_rx, + uint8_t type, uint8_t action_sub_type, uint8_t *peer_mac) +{ + WLAN_HOST_DIAG_EVENT_DEF(tdls_tx_rx_mgmt, + struct host_event_tdls_tx_rx_mgmt); + + tdls_tx_rx_mgmt.event_id = event_id; + tdls_tx_rx_mgmt.tx_rx = tx_rx; + tdls_tx_rx_mgmt.type = type; + tdls_tx_rx_mgmt.action_sub_type = action_sub_type; + qdf_mem_copy(tdls_tx_rx_mgmt.peer_mac, + peer_mac, CDS_MAC_ADDRESS_LEN); + WLAN_HOST_DIAG_EVENT_REPORT(&tdls_tx_rx_mgmt, + EVENT_WLAN_TDLS_TX_RX_MGMT); +} +#endif + +/** + * cds_cfg_update_ac_specs_params() - update ac_specs params + * @olcfg: cfg handle + * @mac_params: mac params + * + * Return: none + */ +static void +cds_cfg_update_ac_specs_params(struct txrx_pdev_cfg_param_t *olcfg, + struct cds_config_info *cds_cfg) +{ + int i; + + if (!olcfg) + return; + + if (!cds_cfg) + return; + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + olcfg->ac_specs[i].wrr_skip_weight = + cds_cfg->ac_specs[i].wrr_skip_weight; + olcfg->ac_specs[i].credit_threshold = + cds_cfg->ac_specs[i].credit_threshold; + olcfg->ac_specs[i].send_limit = + cds_cfg->ac_specs[i].send_limit; + olcfg->ac_specs[i].credit_reserve = + cds_cfg->ac_specs[i].credit_reserve; + olcfg->ac_specs[i].discard_weight = + cds_cfg->ac_specs[i].discard_weight; + } +} + +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +static inline void +cds_cdp_set_flow_control_params(struct wlan_objmgr_psoc *psoc, + struct txrx_pdev_cfg_param_t *cdp_cfg) +{ + cdp_cfg->tx_flow_stop_queue_th = + cfg_get(psoc, CFG_DP_TX_FLOW_STOP_QUEUE_TH); + cdp_cfg->tx_flow_start_queue_offset = + cfg_get(psoc, CFG_DP_TX_FLOW_START_QUEUE_OFFSET); +} +#else +static inline void +cds_cdp_set_flow_control_params(struct wlan_objmgr_psoc *psoc, + struct txrx_pdev_cfg_param_t *cdp_cfg) +{} +#endif + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK +static inline void +cds_cdp_update_del_ack_params(struct wlan_objmgr_psoc *psoc, + struct txrx_pdev_cfg_param_t *cdp_cfg) +{ + cdp_cfg->del_ack_enable = + cfg_get(psoc, CFG_DP_DRIVER_TCP_DELACK_ENABLE); + cdp_cfg->del_ack_pkt_count = + cfg_get(psoc, CFG_DP_DRIVER_TCP_DELACK_PKT_CNT); + cdp_cfg->del_ack_timer_value = + cfg_get(psoc, CFG_DP_DRIVER_TCP_DELACK_TIMER_VALUE); +} +#else +static inline void +cds_cdp_update_del_ack_params(struct wlan_objmgr_psoc *psoc, + struct txrx_pdev_cfg_param_t *cdp_cfg) +{} +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE +static inline void +cds_cdp_update_bundle_params(struct wlan_objmgr_psoc *psoc, + struct txrx_pdev_cfg_param_t *cdp_cfg) +{ + cdp_cfg->bundle_timer_value = + cfg_get(psoc, CFG_DP_HL_BUNDLE_TIMER_VALUE); + cdp_cfg->bundle_size = + cfg_get(psoc, CFG_DP_HL_BUNDLE_SIZE); +} +#else +static inline void +cds_cdp_update_bundle_params(struct wlan_objmgr_psoc *psoc, + struct txrx_pdev_cfg_param_t *cdp_cfg) +{ +} +#endif + +/** + * cds_cdp_cfg_attach() - attach data path config module + * @cds_cfg: generic platform level config instance + * + * Return: none + */ +static void cds_cdp_cfg_attach(struct wlan_objmgr_psoc *psoc) +{ + struct txrx_pdev_cfg_param_t cdp_cfg = {0}; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct hdd_context *hdd_ctx = gp_cds_context->hdd_context; + uint32_t gro_bit_set; + + cdp_cfg.is_full_reorder_offload = + cfg_get(psoc, CFG_DP_REORDER_OFFLOAD_SUPPORT); + cdp_cfg.is_uc_offload_enabled = ucfg_ipa_uc_is_enabled(); + cdp_cfg.uc_tx_buffer_count = cfg_get(psoc, CFG_DP_IPA_UC_TX_BUF_COUNT); + cdp_cfg.uc_tx_buffer_size = + cfg_get(psoc, CFG_DP_IPA_UC_TX_BUF_SIZE); + cdp_cfg.uc_rx_indication_ring_count = + cfg_get(psoc, CFG_DP_IPA_UC_RX_IND_RING_COUNT); + cdp_cfg.uc_tx_partition_base = + cfg_get(psoc, CFG_DP_IPA_UC_TX_PARTITION_BASE); + cdp_cfg.enable_rxthread = hdd_ctx->enable_rxthread; + cdp_cfg.ip_tcp_udp_checksum_offload = + cfg_get(psoc, CFG_DP_TCP_UDP_CKSUM_OFFLOAD); + cdp_cfg.nan_ip_tcp_udp_checksum_offload = + cfg_get(psoc, CFG_DP_NAN_TCP_UDP_CKSUM_OFFLOAD); + cdp_cfg.p2p_ip_tcp_udp_checksum_offload = + cfg_get(psoc, CFG_DP_P2P_TCP_UDP_CKSUM_OFFLOAD); + cdp_cfg.legacy_mode_csum_disable = + cfg_get(psoc, CFG_DP_LEGACY_MODE_CSUM_DISABLE); + cdp_cfg.ce_classify_enabled = + cfg_get(psoc, CFG_DP_CE_CLASSIFY_ENABLE); + cdp_cfg.tso_enable = cfg_get(psoc, CFG_DP_TSO); + cdp_cfg.lro_enable = cfg_get(psoc, CFG_DP_LRO); + cdp_cfg.sg_enable = cfg_get(psoc, CFG_DP_SG); + cdp_cfg.enable_data_stall_detection = + cfg_get(psoc, CFG_DP_ENABLE_DATA_STALL_DETECTION); + gro_bit_set = cfg_get(psoc, CFG_DP_GRO); + if (gro_bit_set & DP_GRO_ENABLE_BIT_SET) { + cdp_cfg.gro_enable = true; + if (gro_bit_set & DP_TC_BASED_DYNAMIC_GRO) + cdp_cfg.tc_based_dyn_gro = true; + } + cdp_cfg.tc_ingress_prio = cfg_get(psoc, CFG_DP_TC_INGRESS_PRIO); + cdp_cfg.enable_flow_steering = + cfg_get(psoc, CFG_DP_FLOW_STEERING_ENABLED); + cdp_cfg.disable_intra_bss_fwd = + cfg_get(psoc, CFG_DP_AP_STA_SECURITY_SEPERATION); + cdp_cfg.pktlog_buffer_size = + cfg_get(psoc, CFG_DP_PKTLOG_BUFFER_SIZE); + + cds_cdp_update_del_ack_params(psoc, &cdp_cfg); + + cds_cdp_update_bundle_params(psoc, &cdp_cfg); + + gp_cds_context->cfg_ctx = cdp_cfg_attach(soc, gp_cds_context->qdf_ctx, + (void *)(&cdp_cfg)); + if (!gp_cds_context->cfg_ctx) { + WMA_LOGD("%s: failed to init cfg handle", __func__); + return; + } + + /* Configure Receive flow steering */ + cdp_cfg_set_flow_steering(soc, gp_cds_context->cfg_ctx, + cfg_get(psoc, CFG_DP_FLOW_STEERING_ENABLED)); + + cds_cdp_set_flow_control_params(psoc, &cdp_cfg); + cdp_cfg_set_flow_control_parameters(soc, gp_cds_context->cfg_ctx, + (void *)&cdp_cfg); + + /* adjust the cfg_ctx default value based on setting */ + cdp_cfg_set_rx_fwd_disabled(soc, gp_cds_context->cfg_ctx, + cfg_get(psoc, + CFG_DP_AP_STA_SECURITY_SEPERATION)); + + /* + * adjust the packet log enable default value + * based on CFG INI setting + */ + cdp_cfg_set_packet_log_enabled(soc, gp_cds_context->cfg_ctx, + (uint8_t)cds_is_packet_log_enabled()); + + /* adjust the ptp rx option default value based on CFG INI setting */ + cdp_cfg_set_ptp_rx_opt_enabled(soc, gp_cds_context->cfg_ctx, + (uint8_t)cds_is_ptp_rx_opt_enabled()); +} +static QDF_STATUS cds_register_all_modules(void) +{ + QDF_STATUS status; + + scheduler_register_wma_legacy_handler(&wma_mc_process_handler); + scheduler_register_sys_legacy_handler(&sys_mc_process_handler); + + /* Register message queues in given order such that queue priority is + * intact: + * 1) QDF_MODULE_ID_SYS: Timer queue(legacy SYS queue) + * 2) QDF_MODULE_ID_TARGET_IF: Target interface queue + * 3) QDF_MODULE_ID_PE: Legacy PE message queue + * 4) QDF_MODULE_ID_SME: Legacy SME message queue + * 5) QDF_MODULE_ID_OS_IF: OS IF message queue for new components + */ + status = scheduler_register_module(QDF_MODULE_ID_SYS, + &scheduler_timer_q_mq_handler); + status = scheduler_register_module(QDF_MODULE_ID_TARGET_IF, + &scheduler_target_if_mq_handler); + status = scheduler_register_module(QDF_MODULE_ID_PE, + &pe_mc_process_handler); + status = scheduler_register_module(QDF_MODULE_ID_SME, + &sme_mc_process_handler); + status = scheduler_register_module(QDF_MODULE_ID_OS_IF, + &scheduler_os_if_mq_handler); + status = scheduler_register_module(QDF_MODULE_ID_SCAN, + &scheduler_scan_mq_handler); + return status; +} + +static QDF_STATUS cds_deregister_all_modules(void) +{ + QDF_STATUS status; + + scheduler_deregister_wma_legacy_handler(); + scheduler_deregister_sys_legacy_handler(); + status = scheduler_deregister_module(QDF_MODULE_ID_SCAN); + status = scheduler_deregister_module(QDF_MODULE_ID_SYS); + status = scheduler_deregister_module(QDF_MODULE_ID_TARGET_IF); + status = scheduler_deregister_module(QDF_MODULE_ID_PE); + status = scheduler_deregister_module(QDF_MODULE_ID_SME); + status = scheduler_deregister_module(QDF_MODULE_ID_OS_IF); + + return status; +} + +/** + * cds_set_ac_specs_params() - set ac_specs params in cds_config_info + * @cds_cfg: Pointer to cds_config_info + * @hdd_ctx: Pointer to hdd context + * + * Return: none + */ +static void +cds_set_ac_specs_params(struct cds_config_info *cds_cfg) +{ + int i; + struct cds_context *cds_ctx; + + if (!cds_cfg) + return; + + cds_ctx = cds_get_context(QDF_MODULE_ID_QDF); + + if (!cds_ctx) { + cds_err("Invalid CDS Context"); + return; + } + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + cds_cfg->ac_specs[i] = cds_ctx->ac_specs[i]; + } +} + +static int cds_hang_event_notifier_call(struct notifier_block *block, + unsigned long state, + void *data) +{ + struct qdf_notifer_data *cds_hang_data = data; + uint32_t total_len; + struct cds_hang_event_fixed_param *cmd; + uint8_t *cds_hang_evt_buff; + + if (!cds_hang_data) + return NOTIFY_STOP_MASK; + + cds_hang_evt_buff = cds_hang_data->hang_data; + + if (!cds_hang_evt_buff) + return NOTIFY_STOP_MASK; + + total_len = sizeof(*cmd); + if (cds_hang_data->offset + total_len > QDF_WLAN_HANG_FW_OFFSET) + return NOTIFY_STOP_MASK; + + cds_hang_evt_buff = cds_hang_data->hang_data + cds_hang_data->offset; + cmd = (struct cds_hang_event_fixed_param *)cds_hang_evt_buff; + QDF_HANG_EVT_SET_HDR(&cmd->tlv_header, HANG_EVT_TAG_CDS, + QDF_HANG_GET_STRUCT_TLVLEN(*cmd)); + + cmd->recovery_reason = gp_cds_context->recovery_reason; + + /* userspace expects a fixed format */ + qdf_mem_set(&cmd->driver_version, DRIVER_VER_LEN, ' '); + qdf_mem_copy(&cmd->driver_version, QWLAN_VERSIONSTR, + qdf_min(sizeof(QWLAN_VERSIONSTR) - 1, + (size_t)DRIVER_VER_LEN)); + + /* userspace expects a fixed format */ + qdf_mem_set(&cmd->hang_event_version, HANG_EVENT_VER_LEN, ' '); + qdf_mem_copy(&cmd->hang_event_version, QDF_HANG_EVENT_VERSION, + qdf_min(sizeof(QDF_HANG_EVENT_VERSION) - 1, + (size_t)HANG_EVENT_VER_LEN)); + + cds_hang_data->offset += total_len; + return NOTIFY_OK; +} + +static qdf_notif_block cds_hang_event_notifier = { + .notif_block.notifier_call = cds_hang_event_notifier_call, +}; + +/** + * cds_open() - open the CDS Module + * + * cds_open() function opens the CDS Scheduler + * Upon successful initialization: + * - All CDS submodules should have been initialized + * + * - The CDS scheduler should have opened + * + * - All the WLAN SW components should have been opened. This includes + * SYS, MAC, SME, WMA and TL. + * + * Return: QDF status + */ +QDF_STATUS cds_open(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct cds_config_info *cds_cfg; + qdf_device_t qdf_ctx; + struct htc_init_info htcInfo; + struct ol_context *ol_ctx; + struct hif_opaque_softc *scn; + void *HTCHandle; + struct hdd_context *hdd_ctx; + struct cds_context *cds_ctx; + mac_handle_t mac_handle; + + cds_debug("Opening CDS"); + + cds_ctx = cds_get_context(QDF_MODULE_ID_QDF); + if (!cds_ctx) { + cds_alert("Trying to open CDS without a PreOpen"); + return QDF_STATUS_E_FAILURE; + } + + /* Initialize the timer module */ + qdf_timer_module_init(); + + /* Initialize bug reporting structure */ + cds_init_log_completion(); + + status = qdf_event_create(&gp_cds_context->wma_complete_event); + if (QDF_IS_STATUS_ERROR(status)) { + cds_alert("Unable to init wma_complete_event"); + return status; + } + + hdd_ctx = gp_cds_context->hdd_context; + if (!hdd_ctx || !hdd_ctx->config) { + cds_err("Hdd Context is Null"); + + status = QDF_STATUS_E_FAILURE; + goto err_wma_complete_event; + } + + status = dispatcher_enable(); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed to enable dispatcher; status:%d", status); + goto err_wma_complete_event; + } + + /* Now Open the CDS Scheduler */ + status = cds_sched_open(gp_cds_context, + &gp_cds_context->qdf_sched, + sizeof(cds_sched_context)); + if (QDF_IS_STATUS_ERROR(status)) { + cds_alert("Failed to open CDS Scheduler"); + goto err_dispatcher_disable; + } + + scn = cds_get_context(QDF_MODULE_ID_HIF); + if (!scn) { + cds_alert("scn is null!"); + + status = QDF_STATUS_E_FAILURE; + goto err_sched_close; + } + + cds_cfg = cds_get_ini_config(); + if (!cds_cfg) { + cds_err("Cds config is NULL"); + + status = QDF_STATUS_E_FAILURE; + goto err_sched_close; + } + + hdd_enable_fastpath(hdd_ctx, scn); + + /* Initialize BMI and Download firmware */ + ol_ctx = cds_get_context(QDF_MODULE_ID_BMI); + status = bmi_download_firmware(ol_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + cds_alert("BMI FIALED status:%d", status); + goto err_bmi_close; + } + + hdd_wlan_update_target_info(hdd_ctx, scn); + + htcInfo.pContext = ol_ctx; + htcInfo.TargetFailure = ol_target_failure; + htcInfo.TargetSendSuspendComplete = + ucfg_pmo_psoc_target_suspend_acknowledge; + htcInfo.target_initial_wakeup_cb = ucfg_pmo_psoc_handle_initial_wake_up; + htcInfo.target_psoc = (void *)psoc; + htcInfo.cfg_wmi_credit_cnt = hdd_ctx->config->cfg_wmi_credit_cnt; + qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + /* Create HTC */ + gp_cds_context->htc_ctx = + htc_create(scn, &htcInfo, qdf_ctx, cds_get_conparam()); + if (!gp_cds_context->htc_ctx) { + cds_alert("Failed to Create HTC"); + + status = QDF_STATUS_E_FAILURE; + goto err_bmi_close; + } + ucfg_pmo_psoc_update_htc_handle(psoc, (void *)gp_cds_context->htc_ctx); + + status = bmi_done(ol_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + cds_alert("Failed to complete BMI phase"); + goto err_htc_close; + } + + /*Open the WMA module */ + status = wma_open(psoc, hdd_update_tgt_cfg, cds_cfg, + hdd_ctx->target_type); + if (QDF_IS_STATUS_ERROR(status)) { + cds_alert("Failed to open WMA module"); + goto err_htc_close; + } + + /* Number of peers limit differs in each chip version. If peer max + * limit configured in ini exceeds more than supported, WMA adjusts + * and keeps correct limit in cds_cfg.max_station. So, make sure + * config entry hdd_ctx->config->maxNumberOfPeers has adjusted value + */ + /* In FTM mode cds_cfg->max_stations will be zero. On updating same + * into hdd context config entry, leads to pe_open() to fail, if + * con_mode change happens from FTM mode to any other mode. + */ + if (QDF_DRIVER_TYPE_PRODUCTION == cds_cfg->driver_type) + ucfg_mlme_set_sap_max_peers(psoc, cds_cfg->max_station); + + HTCHandle = cds_get_context(QDF_MODULE_ID_HTC); + gp_cds_context->cfg_ctx = NULL; + if (!HTCHandle) { + cds_alert("HTCHandle is null!"); + + status = QDF_STATUS_E_FAILURE; + goto err_wma_close; + } + + status = htc_wait_target(HTCHandle); + if (QDF_IS_STATUS_ERROR(status)) { + cds_alert("Failed to complete BMI phase. status: %d", status); + QDF_BUG(status == QDF_STATUS_E_NOMEM || cds_is_fw_down()); + + goto err_wma_close; + } + + cds_debug("target_type %d 8074:%d 6290:%d 6390: %d 6490: %d 6750: %d", + hdd_ctx->target_type, + TARGET_TYPE_QCA8074, + TARGET_TYPE_QCA6290, + TARGET_TYPE_QCA6390, + TARGET_TYPE_QCA6490, + TARGET_TYPE_QCA6750); + + if (TARGET_TYPE_QCA6290 == hdd_ctx->target_type || + TARGET_TYPE_QCA6390 == hdd_ctx->target_type || + TARGET_TYPE_QCA6490 == hdd_ctx->target_type || + TARGET_TYPE_QCA6750 == hdd_ctx->target_type) + gp_cds_context->dp_soc = cdp_soc_attach(LITHIUM_DP, + gp_cds_context->hif_context, htcInfo.target_psoc, + gp_cds_context->htc_ctx, gp_cds_context->qdf_ctx, + &dp_ol_if_ops); + else + gp_cds_context->dp_soc = cdp_soc_attach(MOB_DRV_LEGACY_DP, + gp_cds_context->hif_context, htcInfo.target_psoc, + gp_cds_context->htc_ctx, gp_cds_context->qdf_ctx, + &dp_ol_if_ops); + + if (!gp_cds_context->dp_soc) { + status = QDF_STATUS_E_FAILURE; + goto err_wma_close; + } + + wlan_psoc_set_dp_handle(psoc, gp_cds_context->dp_soc); + ucfg_pmo_psoc_update_dp_handle(psoc, gp_cds_context->dp_soc); + ucfg_ocb_update_dp_handle(psoc, gp_cds_context->dp_soc); + + cds_set_ac_specs_params(cds_cfg); + cds_cfg_update_ac_specs_params((struct txrx_pdev_cfg_param_t *) + gp_cds_context->cfg_ctx, cds_cfg); + cds_cdp_cfg_attach(psoc); + + bmi_target_ready(scn, gp_cds_context->cfg_ctx); + + /* Now proceed to open the MAC */ + status = mac_open(psoc, &mac_handle, + gp_cds_context->hdd_context, cds_cfg); + + if (QDF_STATUS_SUCCESS != status) { + cds_alert("Failed to open MAC"); + goto err_soc_detach; + } + gp_cds_context->mac_context = mac_handle; + + /* Now proceed to open the SME */ + status = sme_open(mac_handle); + if (QDF_IS_STATUS_ERROR(status)) { + cds_alert("Failed to open SME"); + goto err_mac_close; + } + + cds_register_all_modules(); + + status = dispatcher_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) { + cds_alert("Failed to open PSOC Components"); + goto deregister_modules; + } + + ucfg_mc_cp_stats_register_pmo_handler(); + qdf_hang_event_register_notifier(&cds_hang_event_notifier); + + return QDF_STATUS_SUCCESS; + +deregister_modules: + cds_deregister_all_modules(); + sme_close(mac_handle); + +err_mac_close: + mac_close(mac_handle); + gp_cds_context->mac_context = NULL; + +err_soc_detach: + cdp_soc_detach(gp_cds_context->dp_soc); + gp_cds_context->dp_soc = NULL; + + ucfg_ocb_update_dp_handle(psoc, NULL); + ucfg_pmo_psoc_update_dp_handle(psoc, NULL); + wlan_psoc_set_dp_handle(psoc, NULL); + +err_wma_close: + cds_shutdown_notifier_purge(); + wma_close(); + wma_wmi_service_close(); + +err_htc_close: + if (gp_cds_context->htc_ctx) { + htc_destroy(gp_cds_context->htc_ctx); + gp_cds_context->htc_ctx = NULL; + ucfg_pmo_psoc_update_htc_handle(psoc, NULL); + } + +err_bmi_close: + bmi_cleanup(ol_ctx); + +err_sched_close: + if (QDF_IS_STATUS_ERROR(cds_sched_close())) + QDF_DEBUG_PANIC("Failed to close CDS Scheduler"); + +err_dispatcher_disable: + if (QDF_IS_STATUS_ERROR(dispatcher_disable())) + QDF_DEBUG_PANIC("Failed to disable dispatcher"); + +err_wma_complete_event: + qdf_event_destroy(&gp_cds_context->wma_complete_event); + + return status; +} /* cds_open() */ + +QDF_STATUS cds_dp_open(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS qdf_status; + struct dp_txrx_config dp_config; + struct hdd_context *hdd_ctx; + + hdd_ctx = gp_cds_context->hdd_context; + if (!hdd_ctx) { + cds_err("HDD context is null"); + return QDF_STATUS_E_FAILURE; + } + + qdf_status = cdp_pdev_attach(cds_get_context(QDF_MODULE_ID_SOC), + gp_cds_context->htc_ctx, + gp_cds_context->qdf_ctx, 0); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + /* Critical Error ... Cannot proceed further */ + cds_alert("Failed to open TXRX"); + QDF_ASSERT(0); + goto close; + } + + if (cdp_txrx_intr_attach(gp_cds_context->dp_soc) + != QDF_STATUS_SUCCESS) { + cds_alert("Failed to attach interrupts"); + goto pdev_detach; + } + + dp_config.enable_rx_threads = + (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) ? + false : gp_cds_context->cds_cfg->enable_dp_rx_threads; + + qdf_status = dp_txrx_init(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID, + &dp_config); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + goto intr_close; + + ucfg_pmo_psoc_set_txrx_pdev_id(psoc, OL_TXRX_PDEV_ID); + ucfg_ocb_set_txrx_pdev_id(psoc, OL_TXRX_PDEV_ID); + + cds_debug("CDS successfully Opened"); + + if (cdp_cfg_get(gp_cds_context->dp_soc, cfg_dp_tc_based_dyn_gro_enable)) + hdd_ctx->dp_agg_param.tc_based_dyn_gro = true; + else + hdd_ctx->dp_agg_param.tc_based_dyn_gro = false; + + hdd_ctx->dp_agg_param.tc_ingress_prio = + cdp_cfg_get(gp_cds_context->dp_soc, cfg_dp_tc_ingress_prio); + + return 0; + +intr_close: + cdp_txrx_intr_detach(gp_cds_context->dp_soc); + +pdev_detach: + cdp_pdev_detach(gp_cds_context->dp_soc, + OL_TXRX_PDEV_ID, false); + +close: + return QDF_STATUS_E_FAILURE; +} + +#ifdef HIF_USB +static inline void cds_suspend_target(tp_wma_handle wma_handle) +{ + QDF_STATUS status; + /* Suspend the target and disable interrupt */ + status = ucfg_pmo_psoc_suspend_target(wma_handle->psoc, 0); + if (status) + cds_err("Failed to suspend target, status = %d", status); +} +#else +static inline void cds_suspend_target(tp_wma_handle wma_handle) +{ + QDF_STATUS status; + /* Suspend the target and disable interrupt */ + status = ucfg_pmo_psoc_suspend_target(wma_handle->psoc, 1); + if (status) + cds_err("Failed to suspend target, status = %d", status); +} +#endif /* HIF_USB */ + +/** + * cds_pre_enable() - pre enable cds + * + * Return: QDF status + */ +QDF_STATUS cds_pre_enable(void) +{ + QDF_STATUS status; + int errno; + void *scn; + void *soc; + void *hif_ctx; + + cds_enter(); + + if (!gp_cds_context) { + cds_err("cds context is null"); + return QDF_STATUS_E_INVAL; + } + + if (!gp_cds_context->wma_context) { + cds_err("wma context is null"); + return QDF_STATUS_E_INVAL; + } + + scn = cds_get_context(QDF_MODULE_ID_HIF); + if (!scn) { + cds_err("hif context is null"); + return QDF_STATUS_E_INVAL; + } + + soc = cds_get_context(QDF_MODULE_ID_SOC); + if (!soc) { + cds_err("soc context is null"); + return QDF_STATUS_E_INVAL; + } + + /* call Packetlog connect service */ + if (QDF_GLOBAL_FTM_MODE != cds_get_conparam() && + QDF_GLOBAL_EPPING_MODE != cds_get_conparam()) + cdp_pkt_log_con_service(soc, OL_TXRX_PDEV_ID, + scn); + + /*call WMA pre start */ + status = wma_pre_start(); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed to WMA prestart"); + return QDF_STATUS_E_FAILURE; + } + + status = htc_start(gp_cds_context->htc_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed to Start HTC"); + goto exit_with_status; + } + + status = wma_wait_for_ready_event(gp_cds_context->wma_context); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed to wait for ready event; status: %u", status); + goto stop_wmi; + } + + errno = cdp_pdev_post_attach(soc, OL_TXRX_PDEV_ID); + if (errno) { + cds_err("Failed to attach pdev"); + status = qdf_status_from_os_return(errno); + goto stop_wmi; + } + + return QDF_STATUS_SUCCESS; + +stop_wmi: + /* Send pdev suspend to fw otherwise FW is not aware that + * host is freeing resources. + */ + if (!(cds_is_driver_recovering() || cds_is_driver_in_bad_state())) + cds_suspend_target(gp_cds_context->wma_context); + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) + cds_err("%s: Failed to get hif_handle!", __func__); + + wma_wmi_stop(); + + if (hif_ctx) { + cds_err("%s: Disable the isr & reset the soc!", __func__); + hif_disable_isr(hif_ctx); + hif_reset_soc(hif_ctx); + } + htc_stop(gp_cds_context->htc_ctx); + + wma_wmi_work_close(); +exit_with_status: + return status; +} + +QDF_STATUS cds_enable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS qdf_status; + struct mac_start_params mac_params; + int errno; + + /* We support only one instance for now ... */ + if (!gp_cds_context) { + cds_err("Invalid CDS context"); + return QDF_STATUS_E_FAILURE; + } + + if (!gp_cds_context->wma_context) { + cds_err("WMA NULL context"); + return QDF_STATUS_E_FAILURE; + } + + if (!gp_cds_context->mac_context) { + cds_err("MAC NULL context"); + return QDF_STATUS_E_FAILURE; + } + + /* Start the wma */ + qdf_status = wma_start(); + if (qdf_status != QDF_STATUS_SUCCESS) { + cds_err("Failed to start wma; status:%d", qdf_status); + return QDF_STATUS_E_FAILURE; + } + + /* Start the MAC */ + qdf_mem_zero(&mac_params, sizeof(mac_params)); + mac_params.driver_type = QDF_DRIVER_TYPE_PRODUCTION; + qdf_status = mac_start(gp_cds_context->mac_context, &mac_params); + + if (QDF_STATUS_SUCCESS != qdf_status) { + cds_err("Failed to start MAC; status:%d", qdf_status); + goto err_wma_stop; + } + + /* START SME */ + qdf_status = sme_start(gp_cds_context->mac_context); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to start SME; status:%d", qdf_status); + goto err_mac_stop; + } + + qdf_status = cdp_soc_attach_target(cds_get_context(QDF_MODULE_ID_SOC)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to attach soc target; status:%d", qdf_status); + goto err_sme_stop; + } + + errno = cdp_pdev_attach_target(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID); + if (errno) { + cds_err("Failed to attach pdev target; errno:%d", errno); + goto err_soc_target_detach; + } + + qdf_status = dispatcher_psoc_enable(psoc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("dispatcher_psoc_enable failed; status:%d", qdf_status); + goto err_soc_target_detach; + } + + /* Trigger psoc enable for CLD components */ + hdd_component_psoc_enable(psoc); + + return QDF_STATUS_SUCCESS; + +err_soc_target_detach: + /* NOOP */ + +err_sme_stop: + sme_stop(gp_cds_context->mac_context); + +err_mac_stop: + mac_stop(gp_cds_context->mac_context); + +err_wma_stop: + qdf_event_reset(&gp_cds_context->wma_complete_event); + qdf_status = wma_stop(); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to stop wma"); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + wma_setneedshutdown(); + } else { + qdf_status = + qdf_wait_for_event_completion( + &gp_cds_context->wma_complete_event, + CDS_WMA_TIMEOUT); + if (qdf_status != QDF_STATUS_SUCCESS) { + if (qdf_status == QDF_STATUS_E_TIMEOUT) { + cds_alert("Timeout occurred before WMA_stop complete"); + } else { + cds_alert("WMA_stop reporting other error"); + } + QDF_ASSERT(0); + wma_setneedshutdown(); + } + } + + return QDF_STATUS_E_FAILURE; +} /* cds_enable() */ + +/** + * cds_disable() - stop/disable cds module + * @psoc: Psoc pointer + * + * Return: QDF status + */ +QDF_STATUS cds_disable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS qdf_status; + void *handle; + + /* PSOC disable for all new components. It needs to happen before + * target is PDEV suspended such that a component can abort all its + * ongoing transaction with FW. Always keep it before wma_stop() as + * wma_stop() does target PDEV suspend. + */ + + /* Trigger psoc disable for CLD components */ + if (psoc) { + hdd_component_psoc_disable(psoc); + dispatcher_psoc_disable(psoc); + } + + qdf_status = wma_stop(); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to stop wma"); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + wma_setneedshutdown(); + } + + handle = cds_get_context(QDF_MODULE_ID_PE); + if (!handle) { + cds_err("Invalid PE context return!"); + return QDF_STATUS_E_INVAL; + } + + umac_stop(); + + return qdf_status; +} + +/** + * cds_post_disable() - post disable cds module + * + * Return: QDF status + */ +QDF_STATUS cds_post_disable(void) +{ + tp_wma_handle wma_handle; + struct hif_opaque_softc *hif_ctx; + struct scheduler_ctx *sched_ctx; + QDF_STATUS qdf_status; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + cds_err("Failed to get wma_handle!"); + return QDF_STATUS_E_INVAL; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + cds_err("Failed to get hif_handle!"); + return QDF_STATUS_E_INVAL; + } + + /* flush any unprocessed scheduler messages */ + sched_ctx = scheduler_get_context(); + if (sched_ctx) + scheduler_queues_flush(sched_ctx); + + /* + * With new state machine changes cds_close can be invoked without + * cds_disable. So, send the following clean up prerequisites to fw, + * So Fw and host are in sync for cleanup indication: + * - Send PDEV_SUSPEND indication to firmware + * - Disable HIF Interrupts. + * - Clean up CE tasklets. + */ + + cds_debug("send deinit sequence to firmware"); + if (!(cds_is_driver_recovering() || cds_is_driver_in_bad_state())) + cds_suspend_target(wma_handle); + hif_disable_isr(hif_ctx); + hif_reset_soc(hif_ctx); + + if (gp_cds_context->htc_ctx) { + wma_wmi_stop(); + htc_stop(gp_cds_context->htc_ctx); + } + + qdf_status = cds_close_rx_thread(); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to close RX thread!"); + return QDF_STATUS_E_INVAL; + } + + qdf_status = cds_close_mon_thread(); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to close MON thread!"); + return QDF_STATUS_E_INVAL; + } + + cdp_pdev_pre_detach(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID, 1); + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_close() - close cds module + * @psoc: Psoc pointer + * + * This API allows user to close modules registered + * with connectivity device services. + * + * Return: QDF status + */ +QDF_STATUS cds_close(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS qdf_status; + + qdf_hang_event_unregister_notifier(&cds_hang_event_notifier); + qdf_status = cds_sched_close(); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + if (QDF_IS_STATUS_ERROR(qdf_status)) + cds_err("Failed to close CDS Scheduler"); + + qdf_status = dispatcher_disable(); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + if (QDF_IS_STATUS_ERROR(qdf_status)) + cds_err("Failed to disable dispatcher; status:%d", qdf_status); + + dispatcher_psoc_close(psoc); + + qdf_flush_work(&gp_cds_context->cds_recovery_work); + + qdf_status = wma_wmi_work_close(); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to close wma_wmi_work"); + QDF_ASSERT(0); + } + + if (gp_cds_context->htc_ctx) { + htc_destroy(gp_cds_context->htc_ctx); + ucfg_pmo_psoc_update_htc_handle(psoc, NULL); + gp_cds_context->htc_ctx = NULL; + } + + qdf_status = sme_close(gp_cds_context->mac_context); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to close SME"); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + } + + qdf_status = mac_close(gp_cds_context->mac_context); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to close MAC"); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + } + + gp_cds_context->mac_context = NULL; + + cdp_soc_detach(gp_cds_context->dp_soc); + gp_cds_context->dp_soc = NULL; + + ucfg_pmo_psoc_update_dp_handle(psoc, NULL); + wlan_psoc_set_dp_handle(psoc, NULL); + + cds_shutdown_notifier_purge(); + + if (true == wma_needshutdown()) { + cds_err("Failed to shutdown wma"); + } else { + qdf_status = wma_close(); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to close wma"); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + } + } + + qdf_status = wma_wmi_service_close(); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("Failed to close wma_wmi_service"); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + } + + qdf_status = qdf_event_destroy(&gp_cds_context->wma_complete_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_err("failed to destroy wma_complete_event"); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + } + + cds_deinit_ini_config(); + qdf_timer_module_deinit(); + + cds_deregister_all_modules(); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS cds_dp_close(struct wlan_objmgr_psoc *psoc) +{ + cdp_txrx_intr_detach(gp_cds_context->dp_soc); + + dp_txrx_deinit(cds_get_context(QDF_MODULE_ID_SOC)); + + cdp_pdev_detach(cds_get_context(QDF_MODULE_ID_SOC), OL_TXRX_PDEV_ID, 1); + + ucfg_pmo_psoc_set_txrx_pdev_id(psoc, OL_TXRX_INVALID_PDEV_ID); + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_get_context() - get context data area + * + * @module_id: ID of the module who's context data is being retrieved. + * + * Each module in the system has a context / data area that is allocated + * and managed by CDS. This API allows any user to get a pointer to its + * allocated context data area from the CDS global context. + * + * Return: pointer to the context data area of the module ID + * specified, or NULL if the context data is not allocated for + * the module ID specified + */ +void *cds_get_context(QDF_MODULE_ID module_id) +{ + void *context = NULL; + + if (!gp_cds_context) { + cds_err("cds context pointer is null"); + return NULL; + } + + switch (module_id) { + case QDF_MODULE_ID_HDD: + { + context = gp_cds_context->hdd_context; + break; + } + + case QDF_MODULE_ID_SME: + case QDF_MODULE_ID_PE: + { + /* In all these cases, we just return the MAC Context */ + context = gp_cds_context->mac_context; + break; + } + + case QDF_MODULE_ID_WMA: + { + /* For wma module */ + context = gp_cds_context->wma_context; + break; + } + + case QDF_MODULE_ID_QDF: + { + /* For SYS this is CDS itself */ + context = gp_cds_context; + break; + } + + case QDF_MODULE_ID_HIF: + { + context = gp_cds_context->hif_context; + break; + } + + case QDF_MODULE_ID_HTC: + { + context = gp_cds_context->htc_ctx; + break; + } + + case QDF_MODULE_ID_QDF_DEVICE: + { + context = gp_cds_context->qdf_ctx; + break; + } + + case QDF_MODULE_ID_BMI: + { + context = gp_cds_context->g_ol_context; + break; + } + + case QDF_MODULE_ID_CFG: + { + context = gp_cds_context->cfg_ctx; + break; + } + + case QDF_MODULE_ID_SOC: + { + context = gp_cds_context->dp_soc; + break; + } + + default: + { + cds_err("Module ID %i does not have its context maintained by CDS", + module_id); + QDF_ASSERT(0); + return NULL; + } + } + + if (!context) + cds_err("Module ID %i context is Null", module_id); + + return context; +} /* cds_get_context() */ + +/** + * cds_get_global_context() - get CDS global Context + * + * This API allows any user to get the CDS Global Context pointer from a + * module context data area. + * + * Return: pointer to the CDS global context, NULL if the function is + * unable to retrieve the CDS context. + */ +void *cds_get_global_context(void) +{ + if (!gp_cds_context) { + /* + * To avoid recursive call, this should not change to + * QDF_TRACE(). + */ + pr_err("%s: global cds context is NULL", __func__); + } + + return gp_cds_context; +} /* cds_get_global_context() */ + +/** + * cds_get_driver_state() - Get current driver state + * + * This API returns current driver state stored in global context. + * + * Return: Driver state enum + */ +enum cds_driver_state cds_get_driver_state(void) +{ + if (!gp_cds_context) { + cds_err("global cds context is NULL"); + + return CDS_DRIVER_STATE_UNINITIALIZED; + } + + return gp_cds_context->driver_state; +} + +/** + * cds_set_driver_state() - Set current driver state + * @state: Driver state to be set to. + * + * This API sets driver state to state. This API only sets the state and doesn't + * clear states, please make sure to use cds_clear_driver_state to clear any + * state if required. + * + * Return: None + */ +void cds_set_driver_state(enum cds_driver_state state) +{ + if (!gp_cds_context) { + cds_err("global cds context is NULL: %x", state); + + return; + } + + gp_cds_context->driver_state |= state; +} + +/** + * cds_clear_driver_state() - Clear current driver state + * @state: Driver state to be cleared. + * + * This API clears driver state. This API only clears the state, please make + * sure to use cds_set_driver_state to set any new states. + * + * Return: None + */ +void cds_clear_driver_state(enum cds_driver_state state) +{ + if (!gp_cds_context) { + cds_err("global cds context is NULL: %x", state); + + return; + } + + gp_cds_context->driver_state &= ~state; +} + +/** + * cds_alloc_context() - allocate a context within the CDS global Context + * @module_id: module ID who's context area is being allocated. + * @module_context: pointer to location where the pointer to the + * allocated context is returned. Note this output pointer + * is valid only if the API returns QDF_STATUS_SUCCESS + * @param size: size of the context area to be allocated. + * + * This API allows any user to allocate a user context area within the + * CDS Global Context. + * + * Return: QDF status + */ +QDF_STATUS cds_alloc_context(QDF_MODULE_ID module_id, + void **module_context, uint32_t size) +{ + void **cds_mod_context = NULL; + + if (!gp_cds_context) { + cds_err("cds context is null"); + return QDF_STATUS_E_FAILURE; + } + + if (!module_context) { + cds_err("null param passed"); + return QDF_STATUS_E_FAILURE; + } + + switch (module_id) { + case QDF_MODULE_ID_WMA: + cds_mod_context = &gp_cds_context->wma_context; + break; + + case QDF_MODULE_ID_HIF: + cds_mod_context = &gp_cds_context->hif_context; + break; + + case QDF_MODULE_ID_BMI: + cds_mod_context = &gp_cds_context->g_ol_context; + break; + + default: + cds_err("Module ID %i does not have its context allocated by CDS", + module_id); + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + if (*cds_mod_context) { + /* Context has already been allocated! + * Prevent double allocation + */ + cds_err("Module ID %i context has already been allocated", + module_id); + return QDF_STATUS_E_EXISTS; + } + + /* Dynamically allocate the context for module */ + + *module_context = qdf_mem_malloc(size); + + if (!*module_context) { + cds_err("Failed to allocate Context for module ID %i", + module_id); + QDF_ASSERT(0); + return QDF_STATUS_E_NOMEM; + } + + *cds_mod_context = *module_context; + + return QDF_STATUS_SUCCESS; +} /* cds_alloc_context() */ + +/** + * cds_set_context() - API to set context in global CDS Context + * @module_id: Module ID + * @context: Pointer to the Module Context + * + * API to set a MODULE Context in global CDS Context + * + * Return: QDF_STATUS + */ +QDF_STATUS cds_set_context(QDF_MODULE_ID module_id, void *context) +{ + struct cds_context *p_cds_context = cds_get_global_context(); + + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return QDF_STATUS_NOT_INITIALIZED; + } + + switch (module_id) { + case QDF_MODULE_ID_HDD: + p_cds_context->hdd_context = context; + break; + case QDF_MODULE_ID_HIF: + p_cds_context->hif_context = context; + break; + default: + cds_err("Module ID %i does not have its context managed by CDS", + module_id); + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_free_context() - free an allocated context within the + * CDS global Context + * @module_id: module ID who's context area is being free + * @module_context: pointer to module context area to be free'd. + * + * This API allows a user to free the user context area within the + * CDS Global Context. + * + * Return: QDF status + */ +QDF_STATUS cds_free_context(QDF_MODULE_ID module_id, void *module_context) +{ + void **cds_mod_context = NULL; + + if (!gp_cds_context) { + cds_err("cds context is null"); + return QDF_STATUS_E_FAILURE; + } + + if (!module_context) { + cds_err("Null param"); + return QDF_STATUS_E_FAILURE; + } + + switch (module_id) { + case QDF_MODULE_ID_WMA: + cds_mod_context = &gp_cds_context->wma_context; + break; + + case QDF_MODULE_ID_HIF: + cds_mod_context = &gp_cds_context->hif_context; + break; + + case QDF_MODULE_ID_BMI: + cds_mod_context = &gp_cds_context->g_ol_context; + break; + + default: + cds_err("Module ID %i does not have its context allocated by CDS", + module_id); + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + if (!*cds_mod_context) { + /* Context has not been allocated or freed already! */ + cds_err("Module ID %i context has not been allocated or freed already", + module_id); + return QDF_STATUS_E_FAILURE; + } + + if (*cds_mod_context != module_context) { + cds_err("cds_mod_context != module_context"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_free(module_context); + + *cds_mod_context = NULL; + + return QDF_STATUS_SUCCESS; +} /* cds_free_context() */ + + +/** + * cds_flush_work() - flush pending works + * @work: pointer to work + * + * Return: none + */ +void cds_flush_work(void *work) +{ + cancel_work_sync(work); +} + +/** + * cds_flush_delayed_work() - flush delayed works + * @dwork: pointer to delayed work + * + * Return: none + */ +void cds_flush_delayed_work(void *dwork) +{ + cancel_delayed_work_sync(dwork); +} + +#ifndef REMOVE_PKT_LOG +/** + * cds_is_packet_log_enabled() - check if packet log is enabled + * + * Return: true if packet log is enabled else false + */ +bool cds_is_packet_log_enabled(void) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = gp_cds_context->hdd_context; + if ((!hdd_ctx) || (!hdd_ctx->config)) { + cds_alert("Hdd Context is Null"); + return false; + } + return hdd_ctx->config->enable_packet_log; +} +#endif + +static int cds_force_assert_target_via_pld(qdf_device_t qdf) +{ + int errno; + + errno = pld_force_assert_target(qdf->dev); + if (errno == -EOPNOTSUPP) + cds_info("PLD does not support target force assert"); + else if (errno) + cds_err("Failed PLD target force assert; errno %d", errno); + else + cds_info("Target force assert triggered via PLD"); + + return errno; +} + +static QDF_STATUS cds_force_assert_target_via_wmi(qdf_device_t qdf) +{ + QDF_STATUS status; + t_wma_handle *wma; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + cds_err("wma is null"); + return QDF_STATUS_E_INVAL; + } + + status = wma_crash_inject(wma, RECOVERY_SIM_SELF_RECOVERY, 0); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed target force assert; status %d", status); + return status; + } + + status = qdf_wait_for_event_completion(&wma->recovery_event, + WMA_CRASH_INJECT_TIMEOUT); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed target force assert wait; status %d", status); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_force_assert_target() - Send assert command to firmware + * @qdf: QDF device instance to assert + * + * An out-of-band recovery mechanism will cleanup and restart the entire wlan + * subsystem in the event of a firmware crash. This API injects a firmware + * crash to start this process when the wlan driver is known to be in a bad + * state. If a firmware assert inject fails, the wlan driver will schedule + * the driver recovery anyway, as a best effort attempt to return to a working + * state. + * + * Return: QDF_STATUS + */ +static QDF_STATUS cds_force_assert_target(qdf_device_t qdf) +{ + int errno; + QDF_STATUS status; + + /* first, try target assert inject via pld */ + errno = cds_force_assert_target_via_pld(qdf); + if (!errno) + return QDF_STATUS_SUCCESS; + if (errno != -EOPNOTSUPP) + return QDF_STATUS_E_FAILURE; + + /* pld assert is not supported, try target assert inject via wmi */ + status = cds_force_assert_target_via_wmi(qdf); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + /* wmi assert failed, start recovery without the firmware assert */ + cds_err("Scheduling recovery work without firmware assert"); + pld_schedule_recovery_work(qdf->dev, PLD_REASON_DEFAULT); + + return status; +} + +/** + * cds_trigger_recovery_handler() - handle a self recovery request + * @func: the name of the function that called cds_trigger_recovery + * @line: the line number of the call site which called cds_trigger_recovery + * + * Return: none + */ +static void cds_trigger_recovery_handler(const char *func, const uint32_t line) +{ + QDF_STATUS status; + qdf_runtime_lock_t rtl; + qdf_device_t qdf; + + /* NOTE! This code path is delicate! Think very carefully before + * modifying the content or order of the following. Please review any + * potential changes with someone closely familiar with this feature. + */ + + if (cds_is_driver_recovering()) { + cds_info("WLAN recovery already in progress"); + return; + } + + if (cds_is_driver_in_bad_state()) { + cds_info("WLAN has already failed recovery"); + return; + } + + if (cds_is_fw_down()) { + cds_info("Firmware has already initiated recovery"); + return; + } + + qdf = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf) { + cds_err("Qdf context is null"); + return; + } + + /* if *wlan* recovery is disabled, crash here for debugging */ + if (!cds_is_self_recovery_enabled()) { + QDF_DEBUG_PANIC("WLAN recovery is not enabled (via %s:%d)", + func, line); + return; + } + + /* ignore recovery if we are unloading; it would be a waste anyway */ + if (cds_is_driver_unloading()) { + cds_info("WLAN is unloading; ignore recovery"); + return; + } + + status = qdf_runtime_lock_init(&rtl); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("qdf_runtime_lock_init failed, status: %d", status); + return; + } + + status = qdf_runtime_pm_prevent_suspend(&rtl); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed to acquire runtime pm lock"); + goto deinit_rtl; + } + + cds_set_recovery_in_progress(true); + cds_force_assert_target(qdf); + + status = qdf_runtime_pm_allow_suspend(&rtl); + if (QDF_IS_STATUS_ERROR(status)) + cds_err("Failed to release runtime pm lock"); + +deinit_rtl: + qdf_runtime_lock_deinit(&rtl); +} + +static void cds_trigger_recovery_work(void *context) +{ + struct cds_recovery_call_info *call_info = context; + + cds_trigger_recovery_handler(call_info->func, call_info->line); +} + +void __cds_trigger_recovery(enum qdf_hang_reason reason, const char *func, + const uint32_t line) +{ + if (!gp_cds_context) { + cds_err("gp_cds_context is null"); + return; + } + + gp_cds_context->recovery_reason = reason; + + __cds_recovery_caller.func = func; + __cds_recovery_caller.line = line; + qdf_queue_work(0, gp_cds_context->cds_recovery_wq, + &gp_cds_context->cds_recovery_work); +} + +void cds_trigger_recovery_psoc(void *psoc, enum qdf_hang_reason reason, + const char *func, const uint32_t line) +{ + __cds_trigger_recovery(reason, func, line); +} + + +/** + * cds_get_recovery_reason() - get self recovery reason + * @reason: recovery reason + * + * Return: None + */ +void cds_get_recovery_reason(enum qdf_hang_reason *reason) +{ + if (!gp_cds_context) { + cds_err("gp_cds_context is null"); + return; + } + + *reason = gp_cds_context->recovery_reason; +} + +/** + * cds_reset_recovery_reason() - reset the reason to unspecified + * + * Return: None + */ +void cds_reset_recovery_reason(void) +{ + if (!gp_cds_context) { + cds_err("gp_cds_context is null"); + return; + } + + gp_cds_context->recovery_reason = QDF_REASON_UNSPECIFIED; +} + +/** + * cds_set_wakelock_logging() - Logging of wakelock enabled/disabled + * @value: Boolean value + * + * This function is used to set the flag which will indicate whether + * logging of wakelock is enabled or not + * + * Return: None + */ +void cds_set_wakelock_logging(bool value) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invald"); + return; + } + p_cds_context->is_wakelock_log_enabled = value; +} + +/** + * cds_is_wakelock_enabled() - Check if logging of wakelock is enabled/disabled + * @value: Boolean value + * + * This function is used to check whether logging of wakelock is enabled or not + * + * Return: true if logging of wakelock is enabled + */ +bool cds_is_wakelock_enabled(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invald"); + return false; + } + return p_cds_context->is_wakelock_log_enabled; +} + +/** + * cds_set_ring_log_level() - Sets the log level of a particular ring + * @ring_id: ring_id + * @log_levelvalue: Log level specificed + * + * This function converts HLOS values to driver log levels and sets the log + * level of a particular ring accordingly. + * + * Return: None + */ +void cds_set_ring_log_level(uint32_t ring_id, uint32_t log_level) +{ + struct cds_context *p_cds_context; + uint32_t log_val; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invald"); + return; + } + + switch (log_level) { + case LOG_LEVEL_NO_COLLECTION: + log_val = WLAN_LOG_LEVEL_OFF; + break; + case LOG_LEVEL_NORMAL_COLLECT: + log_val = WLAN_LOG_LEVEL_NORMAL; + break; + case LOG_LEVEL_ISSUE_REPRO: + log_val = WLAN_LOG_LEVEL_REPRO; + break; + case LOG_LEVEL_ACTIVE: + default: + log_val = WLAN_LOG_LEVEL_ACTIVE; + break; + } + + if (ring_id == RING_ID_WAKELOCK) { + p_cds_context->wakelock_log_level = log_val; + return; + } else if (ring_id == RING_ID_CONNECTIVITY) { + p_cds_context->connectivity_log_level = log_val; + return; + } else if (ring_id == RING_ID_PER_PACKET_STATS) { + p_cds_context->packet_stats_log_level = log_val; + return; + } else if (ring_id == RING_ID_DRIVER_DEBUG) { + p_cds_context->driver_debug_log_level = log_val; + return; + } else if (ring_id == RING_ID_FIRMWARE_DEBUG) { + p_cds_context->fw_debug_log_level = log_val; + return; + } +} + +/** + * cds_get_ring_log_level() - Get the a ring id's log level + * @ring_id: Ring id + * + * Fetch and return the log level corresponding to a ring id + * + * Return: Log level corresponding to the ring ID + */ +enum wifi_driver_log_level cds_get_ring_log_level(uint32_t ring_id) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invald"); + return WLAN_LOG_LEVEL_OFF; + } + + if (ring_id == RING_ID_WAKELOCK) + return p_cds_context->wakelock_log_level; + else if (ring_id == RING_ID_CONNECTIVITY) + return p_cds_context->connectivity_log_level; + else if (ring_id == RING_ID_PER_PACKET_STATS) + return p_cds_context->packet_stats_log_level; + else if (ring_id == RING_ID_DRIVER_DEBUG) + return p_cds_context->driver_debug_log_level; + else if (ring_id == RING_ID_FIRMWARE_DEBUG) + return p_cds_context->fw_debug_log_level; + + return WLAN_LOG_LEVEL_OFF; +} + +/** + * cds_set_multicast_logging() - Set mutlicast logging value + * @value: Value of multicast logging + * + * Set the multicast logging value which will indicate + * whether to multicast host and fw messages even + * without any registration by userspace entity + * + * Return: None + */ +void cds_set_multicast_logging(uint8_t value) +{ + cds_multicast_logging = value; +} + +/** + * cds_is_multicast_logging() - Get multicast logging value + * + * Get the multicast logging value which will indicate + * whether to multicast host and fw messages even + * without any registration by userspace entity + * + * Return: 0 - Multicast logging disabled, 1 - Multicast logging enabled + */ +uint8_t cds_is_multicast_logging(void) +{ + return cds_multicast_logging; +} + +/* + * cds_init_log_completion() - Initialize log param structure + * + * This function is used to initialize the logging related + * parameters + * + * Return: None + */ +void cds_init_log_completion(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return; + } + + p_cds_context->log_complete.is_fatal = WLAN_LOG_TYPE_NON_FATAL; + p_cds_context->log_complete.indicator = WLAN_LOG_INDICATOR_UNUSED; + p_cds_context->log_complete.reason_code = WLAN_LOG_REASON_CODE_UNUSED; + p_cds_context->log_complete.is_report_in_progress = false; +} + +/** + * cds_set_log_completion() - Store the logging params + * @is_fatal: Indicates if the event triggering bug report is fatal or not + * @indicator: Source which trigerred the bug report + * @reason_code: Reason for triggering bug report + * @recovery_needed: If recovery is needed after bug report + * + * This function is used to set the logging parameters based on the + * caller + * + * Return: 0 if setting of params is successful + */ +QDF_STATUS cds_set_log_completion(uint32_t is_fatal, + uint32_t indicator, + uint32_t reason_code, + bool recovery_needed) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return QDF_STATUS_E_FAILURE; + } + + qdf_spinlock_acquire(&p_cds_context->bug_report_lock); + p_cds_context->log_complete.is_fatal = is_fatal; + p_cds_context->log_complete.indicator = indicator; + p_cds_context->log_complete.reason_code = reason_code; + p_cds_context->log_complete.recovery_needed = recovery_needed; + p_cds_context->log_complete.is_report_in_progress = true; + qdf_spinlock_release(&p_cds_context->bug_report_lock); + cds_debug("is_fatal %d indicator %d reason_code %d recovery needed %d", + is_fatal, indicator, reason_code, recovery_needed); + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_get_and_reset_log_completion() - Get and reset logging related params + * @is_fatal: Indicates if the event triggering bug report is fatal or not + * @indicator: Source which trigerred the bug report + * @reason_code: Reason for triggering bug report + * @recovery_needed: If recovery is needed after bug report + * + * This function is used to get the logging related parameters + * + * Return: None + */ +void cds_get_and_reset_log_completion(uint32_t *is_fatal, + uint32_t *indicator, + uint32_t *reason_code, + bool *recovery_needed) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return; + } + + qdf_spinlock_acquire(&p_cds_context->bug_report_lock); + *is_fatal = p_cds_context->log_complete.is_fatal; + *indicator = p_cds_context->log_complete.indicator; + *reason_code = p_cds_context->log_complete.reason_code; + *recovery_needed = p_cds_context->log_complete.recovery_needed; + + /* reset */ + p_cds_context->log_complete.indicator = WLAN_LOG_INDICATOR_UNUSED; + p_cds_context->log_complete.is_fatal = WLAN_LOG_TYPE_NON_FATAL; + p_cds_context->log_complete.is_report_in_progress = false; + p_cds_context->log_complete.reason_code = WLAN_LOG_REASON_CODE_UNUSED; + p_cds_context->log_complete.recovery_needed = false; + qdf_spinlock_release(&p_cds_context->bug_report_lock); +} + +/** + * cds_is_log_report_in_progress() - Check if bug reporting is in progress + * + * This function is used to check if the bug reporting is already in progress + * + * Return: true if the bug reporting is in progress + */ +bool cds_is_log_report_in_progress(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return true; + } + return p_cds_context->log_complete.is_report_in_progress; +} + +/** + * cds_is_fatal_event_enabled() - Return if fatal event is enabled + * + * Return true if fatal event is enabled. + */ +bool cds_is_fatal_event_enabled(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return false; + } + + + return p_cds_context->enable_fatal_event; +} + +#ifdef WLAN_FEATURE_TSF_PLUS +bool cds_is_ptp_rx_opt_enabled(void) +{ + struct hdd_context *hdd_ctx; + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return false; + } + + hdd_ctx = (struct hdd_context *)(p_cds_context->hdd_context); + if ((!hdd_ctx) || (!hdd_ctx->config)) { + cds_err("Hdd Context is Null"); + return false; + } + + return hdd_tsf_is_rx_set(hdd_ctx); +} + +bool cds_is_ptp_tx_opt_enabled(void) +{ + struct hdd_context *hdd_ctx; + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return false; + } + + hdd_ctx = (struct hdd_context *)(p_cds_context->hdd_context); + if ((!hdd_ctx) || (!hdd_ctx->config)) { + cds_err("Hdd Context is Null"); + return false; + } + + return hdd_tsf_is_tx_set(hdd_ctx); +} +#endif + +/** + * cds_get_log_indicator() - Get the log flush indicator + * + * This function is used to get the log flush indicator + * + * Return: log indicator + */ +uint32_t cds_get_log_indicator(void) +{ + struct cds_context *p_cds_context; + uint32_t indicator; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return WLAN_LOG_INDICATOR_UNUSED; + } + + if (cds_is_load_or_unload_in_progress() || + cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + return WLAN_LOG_INDICATOR_UNUSED; + } + + qdf_spinlock_acquire(&p_cds_context->bug_report_lock); + indicator = p_cds_context->log_complete.indicator; + qdf_spinlock_release(&p_cds_context->bug_report_lock); + return indicator; +} + +/** + * cds_wlan_flush_host_logs_for_fatal() - Wrapper to flush host logs + * + * This function is used to send signal to the logger thread to + * flush the host logs. + * + * Return: None + * + */ +void cds_wlan_flush_host_logs_for_fatal(void) +{ + if (cds_is_log_report_in_progress()) + wlan_flush_host_logs_for_fatal(); +} + +/** + * cds_flush_logs() - Report fatal event to userspace + * @is_fatal: Indicates if the event triggering bug report is fatal or not + * @indicator: Source which trigerred the bug report + * @reason_code: Reason for triggering bug report + * @dump_mac_trace: If mac trace are needed in logs. + * @recovery_needed: If recovery is needed after bug report + * + * This function sets the log related params and send the WMI command to the + * FW to flush its logs. On receiving the flush completion event from the FW + * the same will be conveyed to userspace + * + * Return: 0 on success + */ +QDF_STATUS cds_flush_logs(uint32_t is_fatal, + uint32_t indicator, + uint32_t reason_code, + bool dump_mac_trace, + bool recovery_needed) +{ + QDF_STATUS status; + + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return QDF_STATUS_E_FAILURE; + } + if (!p_cds_context->enable_fatal_event) { + cds_err("Fatal event not enabled"); + return QDF_STATUS_E_FAILURE; + } + if (cds_is_load_or_unload_in_progress() || + cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + cds_err("un/Load/SSR in progress"); + return QDF_STATUS_E_FAILURE; + } + + if (cds_is_log_report_in_progress()) { + cds_err("Bug report already in progress - dropping! type:%d, indicator=%d reason_code=%d", + is_fatal, indicator, reason_code); + return QDF_STATUS_E_FAILURE; + } + + status = cds_set_log_completion(is_fatal, indicator, + reason_code, recovery_needed); + if (QDF_STATUS_SUCCESS != status) { + cds_err("Failed to set log trigger params"); + return QDF_STATUS_E_FAILURE; + } + + cds_debug("Triggering bug report: type:%d, indicator=%d reason_code=%d", + is_fatal, indicator, reason_code); + + if (dump_mac_trace) + qdf_trace_dump_all(p_cds_context->mac_context, 0, 0, 100, 0); + + if (WLAN_LOG_INDICATOR_HOST_ONLY == indicator) { + cds_wlan_flush_host_logs_for_fatal(); + return QDF_STATUS_SUCCESS; + } + + status = sme_send_flush_logs_cmd_to_fw(); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed to send flush FW log"); + cds_init_log_completion(); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_logging_set_fw_flush_complete() - Wrapper for FW log flush completion + * + * This function is used to send signal to the logger thread to indicate + * that the flushing of FW logs is complete by the FW + * + * Return: None + * + */ +void cds_logging_set_fw_flush_complete(void) +{ + if (cds_is_fatal_event_enabled()) + wlan_logging_set_fw_flush_complete(); +} + +/** + * cds_set_fatal_event() - set fatal event status + * @value: pending statue to set + * + * Return: None + */ +void cds_set_fatal_event(bool value) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + cds_err("cds context is Invalid"); + return; + } + p_cds_context->enable_fatal_event = value; +} + +/** + * cds_get_radio_index() - get radio index + * + * Return: radio index otherwise, -EINVAL + */ +int cds_get_radio_index(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + /* + * To avoid recursive call, this should not change to + * QDF_TRACE(). + */ + pr_err("%s: cds context is invalid\n", __func__); + return -EINVAL; + } + + return p_cds_context->radio_index; +} + +/** + * cds_set_radio_index() - set radio index + * @radio_index: the radio index to set + * + * Return: QDF status + */ +QDF_STATUS cds_set_radio_index(int radio_index) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + pr_err("%s: cds context is invalid\n", __func__); + return QDF_STATUS_E_FAILURE; + } + + p_cds_context->radio_index = radio_index; + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_init_ini_config() - API to initialize CDS configuration parameters + * @cfg: CDS Configuration + * + * Return: void + */ + +void cds_init_ini_config(struct cds_config_info *cfg) +{ + struct cds_context *cds_ctx; + + cds_ctx = cds_get_context(QDF_MODULE_ID_QDF); + if (!cds_ctx) { + cds_err("Invalid CDS Context"); + return; + } + + cds_ctx->cds_cfg = cfg; +} + +/** + * cds_deinit_ini_config() - API to free CDS configuration parameters + * + * Return: void + */ +void cds_deinit_ini_config(void) +{ + struct cds_context *cds_ctx; + struct cds_config_info *cds_cfg; + + cds_ctx = cds_get_context(QDF_MODULE_ID_QDF); + if (!cds_ctx) { + cds_err("Invalid CDS Context"); + return; + } + + cds_cfg = cds_ctx->cds_cfg; + cds_ctx->cds_cfg = NULL; + + if (cds_cfg) + qdf_mem_free(cds_cfg); +} + +/** + * cds_get_ini_config() - API to get CDS configuration parameters + * + * Return: cds config structure + */ +struct cds_config_info *cds_get_ini_config(void) +{ + struct cds_context *cds_ctx; + + cds_ctx = cds_get_context(QDF_MODULE_ID_QDF); + if (!cds_ctx) { + cds_err("Invalid CDS Context"); + return NULL; + } + + return cds_ctx->cds_cfg; +} + +/** + * cds_is_5_mhz_enabled() - API to get 5MHZ enabled + * + * Return: true if 5 mhz is enabled, false otherwise + */ +bool cds_is_5_mhz_enabled(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_context(QDF_MODULE_ID_QDF); + if (!p_cds_context) { + cds_err("%s: cds context is invalid", __func__); + return false; + } + + if (p_cds_context->cds_cfg) + return (p_cds_context->cds_cfg->sub_20_channel_width == + WLAN_SUB_20_CH_WIDTH_5); + + return false; +} + +/** + * cds_is_10_mhz_enabled() - API to get 10-MHZ enabled + * + * Return: true if 10 mhz is enabled, false otherwise + */ +bool cds_is_10_mhz_enabled(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_context(QDF_MODULE_ID_QDF); + if (!p_cds_context) { + cds_err("%s: cds context is invalid", __func__); + return false; + } + + if (p_cds_context->cds_cfg) + return (p_cds_context->cds_cfg->sub_20_channel_width == + WLAN_SUB_20_CH_WIDTH_10); + + return false; +} + +/** + * cds_is_sub_20_mhz_enabled() - API to get sub 20-MHZ enabled + * + * Return: true if 5 or 10 mhz is enabled, false otherwise + */ +bool cds_is_sub_20_mhz_enabled(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_context(QDF_MODULE_ID_QDF); + if (!p_cds_context) { + cds_err("%s: cds context is invalid", __func__); + return false; + } + + if (p_cds_context->cds_cfg) + return p_cds_context->cds_cfg->sub_20_channel_width; + + return false; +} + +/** + * cds_is_self_recovery_enabled() - API to get self recovery enabled + * + * Return: true if self recovery enabled, false otherwise + */ +bool cds_is_self_recovery_enabled(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_context(QDF_MODULE_ID_QDF); + if (!p_cds_context) { + cds_err("%s: cds context is invalid", __func__); + return false; + } + + if (p_cds_context->cds_cfg) + return p_cds_context->cds_cfg->self_recovery_enabled; + + return false; +} + +/** + * cds_is_fw_down() - Is FW down or not + * + * Return: true if FW is down and false otherwise. + */ +bool cds_is_fw_down(void) +{ + qdf_device_t qdf_ctx; + + qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_ctx) { + cds_err("cds context is invalid"); + return false; + } + + return pld_is_fw_down(qdf_ctx->dev); +} + +/** + * cds_svc_fw_shutdown_ind() - API to send userspace about FW crash + * + * @dev: Device Pointer + * + * Return: None + */ +void cds_svc_fw_shutdown_ind(struct device *dev) +{ + hdd_svc_fw_shutdown_ind(dev); +} + +#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE +/* + * cds_pkt_stats_to_logger_thread() - send pktstats to user + * @pl_hdr: Pointer to pl_hdr + * @pkt_dump: Pointer to pkt_dump data structure. + * @data: Pointer to data + * + * This function is used to send the pkt stats to SVC module. + * + * Return: None + */ +inline void cds_pkt_stats_to_logger_thread(void *pl_hdr, void *pkt_dump, + void *data) +{ + if (cds_get_ring_log_level(RING_ID_PER_PACKET_STATS) != + WLAN_LOG_LEVEL_ACTIVE) + return; + + wlan_pkt_stats_to_logger_thread(pl_hdr, pkt_dump, data); +} +#endif + +/** + * cds_get_conparam() - Get the connection mode parameters + * + * Return the connection mode parameter set by insmod or set during statically + * linked driver + * + * Return: enum QDF_GLOBAL_MODE + */ +enum QDF_GLOBAL_MODE cds_get_conparam(void) +{ + enum QDF_GLOBAL_MODE con_mode; + + con_mode = hdd_get_conparam(); + + return con_mode; +} + +#ifdef FEATURE_HTC_CREDIT_HISTORY +inline void +cds_print_htc_credit_history(uint32_t count, qdf_abstract_print *print, + void *print_priv) +{ + htc_print_credit_history(gp_cds_context->htc_ctx, count, + print, print_priv); +} +#endif + +uint32_t cds_get_connectivity_stats_pkt_bitmap(void *context) +{ + struct hdd_adapter *adapter = NULL; + + if (!context) + return 0; + + adapter = (struct hdd_adapter *)context; + if (unlikely(adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) { + cds_err("Magic cookie(%x) for adapter sanity verification is invalid", + adapter->magic); + return 0; + } + return adapter->pkt_type_bitmap; +} + +/** + * cds_get_arp_stats_gw_ip() - get arp stats track IP + * + * Return: ARP stats IP to track + */ +uint32_t cds_get_arp_stats_gw_ip(void *context) +{ + struct hdd_adapter *adapter = NULL; + + if (!context) + return 0; + + adapter = (struct hdd_adapter *)context; + + if (unlikely(adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) { + cds_err("Magic cookie(%x) for adapter sanity verification is invalid", + adapter->magic); + return 0; + } + + return adapter->track_arp_ip; +} + +/** + * cds_incr_arp_stats_tx_tgt_delivered() - increment ARP stats + * + * Return: none + */ +void cds_incr_arp_stats_tx_tgt_delivered(void) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_CDS_INCR_ARP_STATS_TX_TGT_DELIVERED; + + hdd_ctx = gp_cds_context->hdd_context; + if (!hdd_ctx) { + cds_err("Hdd Context is Null"); + return; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->device_mode == QDF_STA_MODE) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, dbgid); + break; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + if (adapter) + adapter->hdd_stats.hdd_arp_stats.tx_host_fw_sent++; +} + +/** + * cds_incr_arp_stats_tx_tgt_acked() - increment ARP stats + * + * Return: none + */ +void cds_incr_arp_stats_tx_tgt_acked(void) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_CDS_INCR_ARP_STATS_TX_TGT_ACKED; + + hdd_ctx = gp_cds_context->hdd_context; + if (!hdd_ctx) { + cds_err("Hdd Context is Null"); + return; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->device_mode == QDF_STA_MODE) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, dbgid); + break; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + if (adapter) + adapter->hdd_stats.hdd_arp_stats.tx_ack_cnt++; +} + +#ifdef FEATURE_ALIGN_STATS_FROM_DP +/** + * cds_get_cdp_vdev_stats() - Function which retrieves cdp vdev stats + * @vdev_id: vdev id + * @vdev_stats: cdp vdev stats retrieves from DP + * + * Return: If get cdp vdev stats success return true, otherwise return false + */ +static bool +cds_get_cdp_vdev_stats(uint8_t vdev_id, struct cdp_vdev_stats *vdev_stats) +{ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!vdev_stats) + return false; + + if (cdp_host_get_vdev_stats(soc, vdev_id, vdev_stats, true)) + return false; + + return true; +} + +bool +cds_dp_get_vdev_stats(uint8_t vdev_id, struct cds_vdev_dp_stats *stats) +{ + struct cdp_vdev_stats *vdev_stats; + bool ret = false; + + vdev_stats = qdf_mem_malloc(sizeof(*vdev_stats)); + if (!vdev_stats) + return false; + + if (cds_get_cdp_vdev_stats(vdev_id, vdev_stats)) { + stats->tx_retries = vdev_stats->tx.retries; + ret = true; + } + + qdf_mem_free(vdev_stats); + return ret; +} +#endif + +#ifdef ENABLE_SMMU_S1_TRANSLATION +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +QDF_STATUS cds_smmu_mem_map_setup(qdf_device_t osdev, bool ipa_present) +{ + struct iommu_domain *domain; + bool ipa_smmu_enabled; + bool wlan_smmu_enabled; + + domain = pld_smmu_get_domain(osdev->dev); + if (domain) { + int attr = 0; + int errno = iommu_domain_get_attr(domain, + DOMAIN_ATTR_S1_BYPASS, &attr); + + wlan_smmu_enabled = !errno && !attr; + } else { + cds_info("No SMMU mapping present"); + wlan_smmu_enabled = false; + } + + if (!wlan_smmu_enabled) { + osdev->smmu_s1_enabled = false; + goto exit_with_success; + } + + if (!ipa_present) { + osdev->smmu_s1_enabled = true; + goto exit_with_success; + } + + ipa_smmu_enabled = qdf_get_ipa_smmu_enabled(); + + osdev->smmu_s1_enabled = ipa_smmu_enabled && wlan_smmu_enabled; + if (ipa_smmu_enabled != wlan_smmu_enabled) { + cds_err("SMMU mismatch; IPA:%s, WLAN:%s", + ipa_smmu_enabled ? "enabled" : "disabled", + wlan_smmu_enabled ? "enabled" : "disabled"); + return QDF_STATUS_E_FAILURE; + } + +exit_with_success: + osdev->domain = domain; + + cds_info("SMMU S1 %s", osdev->smmu_s1_enabled ? "enabled" : "disabled"); + + return QDF_STATUS_SUCCESS; +} + +#else +QDF_STATUS cds_smmu_mem_map_setup(qdf_device_t osdev, bool ipa_present) +{ + struct dma_iommu_mapping *mapping; + bool ipa_smmu_enabled; + bool wlan_smmu_enabled; + + mapping = pld_smmu_get_mapping(osdev->dev); + if (mapping) { + int attr = 0; + int errno = iommu_domain_get_attr(mapping->domain, + DOMAIN_ATTR_S1_BYPASS, &attr); + + wlan_smmu_enabled = !errno && !attr; + } else { + cds_info("No SMMU mapping present"); + wlan_smmu_enabled = false; + } + + if (!wlan_smmu_enabled) { + osdev->smmu_s1_enabled = false; + goto exit_with_success; + } + + if (!ipa_present) { + osdev->smmu_s1_enabled = true; + goto exit_with_success; + } + + ipa_smmu_enabled = qdf_get_ipa_smmu_enabled(); + + osdev->smmu_s1_enabled = ipa_smmu_enabled && wlan_smmu_enabled; + if (ipa_smmu_enabled != wlan_smmu_enabled) { + cds_err("SMMU mismatch; IPA:%s, WLAN:%s", + ipa_smmu_enabled ? "enabled" : "disabled", + wlan_smmu_enabled ? "enabled" : "disabled"); + return QDF_STATUS_E_FAILURE; + } + +exit_with_success: + osdev->iommu_mapping = mapping; + + cds_info("SMMU S1 %s", osdev->smmu_s1_enabled ? "enabled" : "disabled"); + + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef IPA_OFFLOAD +int cds_smmu_map_unmap(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr) +{ + return ucfg_ipa_uc_smmu_map(map, num_buf, buf_arr); +} +#else +int cds_smmu_map_unmap(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr) +{ + return 0; +} +#endif + +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +QDF_STATUS cds_smmu_mem_map_setup(qdf_device_t osdev, bool ipa_present) +{ + osdev->smmu_s1_enabled = false; + osdev->domain = NULL; + return QDF_STATUS_SUCCESS; +} +#else +QDF_STATUS cds_smmu_mem_map_setup(qdf_device_t osdev, bool ipa_present) +{ + osdev->smmu_s1_enabled = false; + return QDF_STATUS_SUCCESS; +} +#endif + +int cds_smmu_map_unmap(bool map, uint32_t num_buf, qdf_mem_info_t *buf_arr) +{ + return 0; +} +#endif + +#ifdef WLAN_FEATURE_PKT_CAPTURE +bool cds_is_pktcapture_enabled(void) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = gp_cds_context->hdd_context; + if (!hdd_ctx) { + cds_err("HDD context is NULL"); + return false; + } + + return hdd_ctx->enable_pkt_capture_support; +} + +uint8_t cds_get_pktcapture_mode(void) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = gp_cds_context->hdd_context; + if (!hdd_ctx) { + cds_err("HDD context is NULL"); + return false; + } + + return hdd_ctx->val_pkt_capture_mode; +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ diff --git a/drivers/staging/qcacld-3.0/core/cds/src/cds_ieee80211_common_i.h b/drivers/staging/qcacld-3.0/core/cds/src/cds_ieee80211_common_i.h new file mode 100644 index 0000000000000000000000000000000000000000..00e9ad1df5ee1dce4fea66ae909b6486b0a5db4c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/src/cds_ieee80211_common_i.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2013-2017,2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CDS_COMMON__IEEE80211_I_H_ +#define CDS_COMMON__IEEE80211_I_H_ + +/** + * enum ieee80211_phymode - not really a mode; there are really multiple PHY's + * @IEEE80211_MODE_AUTO - autoselect + * @IEEE80211_MODE_11A - 5GHz, OFDM + * @IEEE80211_MODE_11B - 2GHz, CCK + * @IEEE80211_MODE_11G - 2GHz, OFDM + * @IEEE80211_MODE_FH - 2GHz, GFSK + * @IEEE80211_MODE_TURBO_A - 5GHz, OFDM, 2x clock dynamic turbo + * @IEEE80211_MODE_TURBO_G - 2GHz, OFDM, 2x clock dynamic turbo + * @IEEE80211_MODE_11NA_HT20 - 5Ghz, HT20 + * @IEEE80211_MODE_11NG_HT20 - 2Ghz, HT20 + * @IEEE80211_MODE_11NA_HT40PLUS - 5Ghz, HT40 (ext ch +1) + * @IEEE80211_MODE_11NA_HT40MINUS - 5Ghz, HT40 (ext ch -1) + * @IEEE80211_MODE_11NG_HT40PLUS - 2Ghz, HT40 (ext ch +1) + * @IEEE80211_MODE_11NG_HT40MINUS - 2Ghz, HT40 (ext ch -1) + * @IEEE80211_MODE_11NG_HT40 - 2Ghz, Auto HT40 + * @IEEE80211_MODE_11NA_HT40 - 2Ghz, Auto HT40 + * @IEEE80211_MODE_11AC_VHT20 - 5Ghz, VHT20 + * @IEEE80211_MODE_11AC_VHT40PLUS - 5Ghz, VHT40 (Ext ch +1) + * @IEEE80211_MODE_11AC_VHT40MINUS - 5Ghz VHT40 (Ext ch -1) + * @IEEE80211_MODE_11AC_VHT40 - 5Ghz, VHT40 + * @IEEE80211_MODE_11AC_VHT80 - 5Ghz, VHT80 + * @IEEE80211_MODE_2G_AUTO - 2G 11 b/g/n autoselect + * @IEEE80211_MODE_5G_AUTO - 5G 11 a/n/ac autoselect + * @IEEE80211_MODE_11AGN - Support 11N in both 2G and 5G + * @IEEE80211_MODE_11AX_HE20 - HE20 + * @IEEE80211_MODE_11AX_HE40 - HE40 + * @IEEE80211_MODE_11AX_HE40PLUS - HE40 (ext ch +1) + * @IEEE80211_MODE_11AX_HE40MINUS - HE40 (ext ch -1) + * @IEEE80211_MODE_11AX_HE80 - HE80 + * @IEEE80211_MODE_11AX_HE80P80 - HE 80P80 + * @IEEE80211_MODE_11AX_HE160 - HE160 + * @IEEE80211_MODE_MAX - Maximum possible value + */ +enum ieee80211_phymode { + IEEE80211_MODE_AUTO = 0, + IEEE80211_MODE_11A = 1, + IEEE80211_MODE_11B = 2, + IEEE80211_MODE_11G = 3, + IEEE80211_MODE_FH = 4, + IEEE80211_MODE_TURBO_A = 5, + IEEE80211_MODE_TURBO_G = 6, + IEEE80211_MODE_11NA_HT20 = 7, + IEEE80211_MODE_11NG_HT20 = 8, + IEEE80211_MODE_11NA_HT40PLUS = 9, + IEEE80211_MODE_11NA_HT40MINUS = 10, + IEEE80211_MODE_11NG_HT40PLUS = 11, + IEEE80211_MODE_11NG_HT40MINUS = 12, + IEEE80211_MODE_11NG_HT40 = 13, + IEEE80211_MODE_11NA_HT40 = 14, + IEEE80211_MODE_11AC_VHT20 = 15, + IEEE80211_MODE_11AC_VHT40PLUS = 16, + IEEE80211_MODE_11AC_VHT40MINUS = 17, + IEEE80211_MODE_11AC_VHT40 = 18, + IEEE80211_MODE_11AC_VHT80 = 19, + IEEE80211_MODE_2G_AUTO = 20, + IEEE80211_MODE_5G_AUTO = 21, + IEEE80211_MODE_11AGN = 22, + IEEE80211_MODE_11AX_HE20 = 23, + IEEE80211_MODE_11AX_HE40 = 24, + IEEE80211_MODE_11AX_HE40PLUS = 25, + IEEE80211_MODE_11AX_HE40MINUS = 26, + IEEE80211_MODE_11AX_HE80 = 27, + IEEE80211_MODE_11AX_HE80P80 = 28, + IEEE80211_MODE_11AX_HE160 = 29, + + /* Do not add after this line */ + IEEE80211_MODE_MAX = IEEE80211_MODE_11AX_HE160, +}; + +/* + * 802.11g protection mode. + */ +enum ieee80211_protmode { + IEEE80211_PROT_NONE = 0, /* no protection */ + IEEE80211_PROT_CTSONLY = 1, /* CTS to self */ + IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */ +}; + +/* bits 0-3 are for private use by drivers */ +/* channel attributes */ +#define IEEE80211_CHAN_TURBO 0x00000010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x00000020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x00000040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x00000080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x00000100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x00000200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x00000400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x00000800 /* GFSK channel (FHSS PHY) */ +#define IEEE80211_CHAN_RADAR_DFS 0x00001000 /* Radar found on channel */ +#define IEEE80211_CHAN_STURBO 0x00002000 /* 11a static turbo channel only */ +#define IEEE80211_CHAN_HALF 0x00004000 /* Half rate channel */ +#define IEEE80211_CHAN_QUARTER 0x00008000 /* Quarter rate channel */ +#define IEEE80211_CHAN_HT20 0x00010000 /* HT 20 channel */ +#define IEEE80211_CHAN_HT40PLUS 0x00020000 /* HT 40 with extension channel above */ +#define IEEE80211_CHAN_HT40MINUS 0x00040000 /* HT 40 with extension channel below */ +#define IEEE80211_CHAN_HT40INTOL 0x00080000 /* HT 40 Intolerant */ +#define IEEE80211_CHAN_VHT20 0x00100000 /* VHT 20 channel */ +#define IEEE80211_CHAN_VHT40PLUS 0x00200000 /* VHT 40 with extension channel above */ +#define IEEE80211_CHAN_VHT40MINUS 0x00400000 /* VHT 40 with extension channel below */ +#define IEEE80211_CHAN_VHT80 0x00800000 /* VHT 80 channel */ +/* channel temporarily blocked due to noise */ +#define IEEE80211_CHAN_BLOCKED 0x02000000 +/* VHT 160 channel */ +#define IEEE80211_CHAN_VHT160 0x04000000 +/* VHT 80_80 channel */ +#define IEEE80211_CHAN_VHT80_80 0x08000000 + +/* flagext */ +#define IEEE80211_CHAN_DFS 0x0002 /* DFS required on channel */ +/* DFS required on channel for 2nd band of 80+80*/ +#define IEEE80211_CHAN_DFS_CFREQ2 0x0004 + +#define IEEE80211_SEQ_MASK 0xfff /* sequence generator mask */ +#define MIN_SW_SEQ 0x100 /* minimum sequence for SW generate packect */ + +#endif /* CDS_COMMON__IEEE80211_I_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/cds/src/cds_packet.c b/drivers/staging/qcacld-3.0/core/cds/src/cds_packet.c new file mode 100644 index 0000000000000000000000000000000000000000..50e18eb5de81b822560d6d2a63fd6d3965730946 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/src/cds_packet.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014-2016, 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + \file cds_packet.c + + \brief Connectivity driver services (CDS) network Packet APIs + + Network Protocol packet/buffer support interfaces + + ========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include "qdf_nbuf.h" +#include "qdf_mem.h" +#include "cds_utils.h" + +#define TX_PKT_MIN_HEADROOM (64) + +/** + * cds_pkt_return_packet Free the cds Packet + * @ cds Packet + */ +QDF_STATUS cds_pkt_return_packet(cds_pkt_t *packet) +{ + /* Validate the input parameter pointer */ + if (unlikely(!packet)) { + return QDF_STATUS_E_INVAL; + } + + /* Free up the qdf nbuf */ + qdf_nbuf_free(packet->pkt_buf); + + packet->pkt_buf = NULL; + + /* Free up the Rx packet */ + qdf_mem_free(packet); + + return QDF_STATUS_SUCCESS; +} + +/**-------------------------------------------------------------------------- + + \brief cds_pkt_get_packet_length() - Get packet length for a cds Packet + + This API returns the total length of the data in a cds Packet. + + \param pPacket - the cds Packet to get the packet length from. + + \param pPacketSize - location to return the total size of the data contained + in the cds Packet. + \return + + \sa + + ---------------------------------------------------------------------------*/ +QDF_STATUS +cds_pkt_get_packet_length(cds_pkt_t *pPacket, uint16_t *pPacketSize) +{ + /* Validate the parameter pointers */ + if (unlikely((!pPacket) || (!pPacketSize)) || + (!pPacket->pkt_buf)) { + cds_alert("NULL pointer"); + return QDF_STATUS_E_INVAL; + } + /* return the requested information */ + *pPacketSize = qdf_nbuf_len(pPacket->pkt_buf); + return QDF_STATUS_SUCCESS; +} + +#ifdef MEMORY_DEBUG +/*--------------------------------------------------------------------------- +* @brief cds_packet_alloc_debug() - + Allocate a network buffer for TX + ---------------------------------------------------------------------------*/ +QDF_STATUS cds_packet_alloc_debug(uint16_t size, void **data, void **ppPacket, + uint8_t *file_name, uint32_t line_num) +{ + QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE; + qdf_nbuf_t nbuf; + + nbuf = qdf_nbuf_alloc_debug(NULL, + roundup(size + TX_PKT_MIN_HEADROOM, 4), + TX_PKT_MIN_HEADROOM, sizeof(uint32_t), false, + file_name, line_num); + + if (nbuf) { + qdf_nbuf_put_tail(nbuf, size); + qdf_nbuf_set_protocol(nbuf, ETH_P_CONTROL); + *ppPacket = nbuf; + *data = qdf_nbuf_data(nbuf); + qdf_ret_status = QDF_STATUS_SUCCESS; + } + + return qdf_ret_status; +} +#else +/*--------------------------------------------------------------------------- +* @brief cds_packet_alloc() - + Allocate a network buffer for TX + ---------------------------------------------------------------------------*/ +QDF_STATUS cds_packet_alloc(uint16_t size, void **data, void **ppPacket) +{ + QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE; + qdf_nbuf_t nbuf; + + nbuf = qdf_nbuf_alloc(NULL, roundup(size + TX_PKT_MIN_HEADROOM, 4), + TX_PKT_MIN_HEADROOM, sizeof(uint32_t), false); + + if (nbuf) { + qdf_nbuf_put_tail(nbuf, size); + qdf_nbuf_set_protocol(nbuf, ETH_P_CONTROL); + *ppPacket = nbuf; + *data = qdf_nbuf_data(nbuf); + qdf_ret_status = QDF_STATUS_SUCCESS; + } + + return qdf_ret_status; +} + +#endif +/*--------------------------------------------------------------------------- +* @brief cds_packet_free() - + Free input network buffer + ---------------------------------------------------------------------------*/ +void cds_packet_free(void *pPacket) +{ + qdf_nbuf_free((qdf_nbuf_t) pPacket); +} diff --git a/drivers/staging/qcacld-3.0/core/cds/src/cds_reg_service.c b/drivers/staging/qcacld-3.0/core/cds/src/cds_reg_service.c new file mode 100644 index 0000000000000000000000000000000000000000..5c93ada641deb3331e7a094ee91e89ef13f98ae5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/src/cds_reg_service.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*============================================================================ + FILE: cds_reg_service.c + OVERVIEW: This source file contains definitions for CDS regulatory APIs + DEPENDENCIES: None + ============================================================================*/ + +#include "qdf_types.h" +#include "qdf_trace.h" +#include +#include "wlan_reg_services_api.h" +#include "cds_reg_service.h" +#include "cds_ieee80211_common_i.h" +#include "cds_config.h" +#include "cds_utils.h" + +uint32_t cds_get_vendor_reg_flags(struct wlan_objmgr_pdev *pdev, + uint32_t chan, uint16_t bandwidth, + bool is_ht_enabled, bool is_vht_enabled, + uint8_t sub_20_channel_width) +{ + uint32_t flags = 0; + enum channel_state state; + struct ch_params ch_params; + int sec_channel; + + state = wlan_reg_get_channel_state(pdev, chan); + if (state == CHANNEL_STATE_INVALID) + return flags; + if (state == CHANNEL_STATE_DFS) { + flags |= IEEE80211_CHAN_PASSIVE; + } + if (state == CHANNEL_STATE_DISABLE) + flags |= IEEE80211_CHAN_BLOCKED; + + if (WLAN_REG_IS_24GHZ_CH(chan)) { + if ((bandwidth == CH_WIDTH_80P80MHZ) || + (bandwidth == CH_WIDTH_160MHZ) || + (bandwidth == CH_WIDTH_80MHZ)) { + bandwidth = CH_WIDTH_40MHZ; + } + flags |= IEEE80211_CHAN_2GHZ; + } else + flags |= IEEE80211_CHAN_5GHZ; + + switch (bandwidth) { + case CH_WIDTH_80P80MHZ: + if (wlan_reg_get_5g_bonded_channel_state(pdev, chan, + bandwidth) != CHANNEL_STATE_INVALID) { + if (is_vht_enabled) + flags |= IEEE80211_CHAN_VHT80_80; + } + bandwidth = CH_WIDTH_160MHZ; + /* FALLTHROUGH */ + case CH_WIDTH_160MHZ: + if (wlan_reg_get_5g_bonded_channel_state(pdev, chan, + bandwidth) != CHANNEL_STATE_INVALID) { + if (is_vht_enabled) + flags |= IEEE80211_CHAN_VHT160; + } + bandwidth = CH_WIDTH_80MHZ; + /* FALLTHROUGH */ + case CH_WIDTH_80MHZ: + if (wlan_reg_get_5g_bonded_channel_state(pdev, chan, + bandwidth) != CHANNEL_STATE_INVALID) { + if (is_vht_enabled) + flags |= IEEE80211_CHAN_VHT80; + } + bandwidth = CH_WIDTH_40MHZ; + /* FALLTHROUGH */ + case CH_WIDTH_40MHZ: + qdf_mem_zero(&ch_params, sizeof(ch_params)); + ch_params.ch_width = bandwidth; + wlan_reg_set_channel_params(pdev, chan, 0, &ch_params); + + if (ch_params.sec_ch_offset == LOW_PRIMARY_CH) + sec_channel = chan + 4; + else if (ch_params.sec_ch_offset == HIGH_PRIMARY_CH) + sec_channel = chan - 4; + else + sec_channel = 0; + + if (wlan_reg_get_bonded_channel_state(pdev, chan, bandwidth, + sec_channel) != + CHANNEL_STATE_INVALID) { + if (ch_params.sec_ch_offset == LOW_PRIMARY_CH) { + flags |= IEEE80211_CHAN_HT40PLUS; + if (is_vht_enabled) + flags |= IEEE80211_CHAN_VHT40PLUS; + } else if (ch_params.sec_ch_offset == + HIGH_PRIMARY_CH) { + flags |= IEEE80211_CHAN_HT40MINUS; + if (is_vht_enabled) + flags |= IEEE80211_CHAN_VHT40MINUS; + } + } + bandwidth = CH_WIDTH_20MHZ; + /* FALLTHROUGH */ + case CH_WIDTH_20MHZ: + if (is_vht_enabled) + flags |= IEEE80211_CHAN_VHT20; + if (is_ht_enabled) + flags |= IEEE80211_CHAN_HT20; + bandwidth = CH_WIDTH_10MHZ; + /* FALLTHROUGH */ + case CH_WIDTH_10MHZ: + if ((wlan_reg_get_bonded_channel_state(pdev, chan, bandwidth, + 0) != CHANNEL_STATE_INVALID) && + (sub_20_channel_width == + WLAN_SUB_20_CH_WIDTH_10)) + flags |= IEEE80211_CHAN_HALF; + bandwidth = CH_WIDTH_5MHZ; + /* FALLTHROUGH */ + case CH_WIDTH_5MHZ: + if ((wlan_reg_get_bonded_channel_state(pdev, chan, bandwidth, + 0) != CHANNEL_STATE_INVALID) && + (sub_20_channel_width == + WLAN_SUB_20_CH_WIDTH_5)) + flags |= IEEE80211_CHAN_QUARTER; + break; + default: + cds_info("invalid channel width value %d", bandwidth); + } + + return flags; +} + diff --git a/drivers/staging/qcacld-3.0/core/cds/src/cds_regdomain.c b/drivers/staging/qcacld-3.0/core/cds/src/cds_regdomain.c new file mode 100644 index 0000000000000000000000000000000000000000..32818b05544503570028c2d2d28604a1a33c97ae --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/src/cds_regdomain.c @@ -0,0 +1,713 @@ +/* + * Copyright (c) 2011,2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Notifications and licenses are retained for attribution purposes only. + */ +/* + * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting + * Copyright (c) 2005-2006 Atheros Communications, Inc. + * Copyright (c) 2010, Atheros Communications Inc. + * + * Redistribution and use in source and binary forms are permitted + * provided that the following conditions are met: + * 1. The materials contained herein are unmodified and are used + * unmodified. + * 2. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following NO + * ''WARRANTY'' disclaimer below (''Disclaimer''), without + * modification. + * 3. Redistributions in binary form must reproduce at minimum a + * disclaimer similar to the Disclaimer below and any redistribution + * must be conditioned upon including a substantially similar + * Disclaimer requirement for further binary redistribution. + * 4. Neither the names of the above-listed copyright holders nor the + * names of any contributors may be used to endorse or promote + * product derived from this software without specific prior written + * permission. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE + * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + */ + +#include "qdf_types.h" +#include "wma.h" +#include "cds_regdomain.h" + + +static const struct reg_dmn_pair g_reg_dmn_pairs[] = { + {NO_ENUMRD, FCC8, FCCA, CTRY_DEFAULT}, + {NULL1_WORLD, NULL1, WORLD, CTRY_DEFAULT}, + {FCC1_FCCA, FCC1, FCCA, CTRY_DEFAULT}, + {FCC1_WORLD, FCC1, WORLD, CTRY_DEFAULT}, + {FCC2_WORLD, FCC2, WORLD, CTRY_DEFAULT}, + {FCC2_ETSIC, FCC2, ETSIC, CTRY_DEFAULT}, + {FCC2_FCCA, FCC2, FCCA, CTRY_DEFAULT}, + {FCC3_FCCA, FCC3, FCCA, CTRY_DEFAULT}, + {FCC3_WORLD, FCC3, WORLD, CTRY_DEFAULT}, + {FCC3_ETSIC, FCC3, ETSIC, CTRY_DEFAULT}, + {FCC4_FCCA, FCC4, FCCA, CTRY_DEFAULT}, + {FCC5_FCCA, FCC5, FCCA, CTRY_DEFAULT}, + {FCC6_FCCA, FCC6, FCCA, CTRY_DEFAULT}, + {FCC7_FCCA, FCC7, FCCA, CTRY_DEFAULT}, + {FCC8_FCCA, FCC8, FCCA, CTRY_DEFAULT}, + {FCC6_WORLD, FCC6, WORLD, CTRY_DEFAULT}, + {FCC9_FCCA, FCC9, FCCA, CTRY_DEFAULT}, + {FCC10_FCCA, FCC10, FCCA, CTRY_DEFAULT}, + {FCC11_WORLD, FCC11, WORLD, CTRY_DEFAULT}, + {FCC13_WORLD, FCC13, WORLD, CTRY_DEFAULT}, + {FCC14_FCCB, FCC14, FCCB, CTRY_DEFAULT}, + {ETSI1_WORLD, ETSI1, WORLD, CTRY_DEFAULT}, + {ETSI3_WORLD, ETSI3, WORLD, CTRY_DEFAULT}, + {ETSI4_WORLD, ETSI4, WORLD, CTRY_DEFAULT}, + {ETSI7_WORLD, ETSI4, WORLD, CTRY_DEFAULT}, + {ETSI8_WORLD, ETSI8, WORLD, CTRY_DEFAULT}, + {ETSI9_WORLD, ETSI9, WORLD, CTRY_DEFAULT}, + {APL4_WORLD, APL4, WORLD, CTRY_DEFAULT}, + {APL2_WORLD, APL2, WORLD, CTRY_DEFAULT}, + {APL2_FCCA, APL2, FCCA, CTRY_DEFAULT}, + {APL2_ETSIC, APL2, ETSIC, CTRY_DEFAULT}, + {APL1_WORLD, APL1, WORLD, CTRY_DEFAULT}, + {APL1_ETSIC, APL1, ETSIC, CTRY_DEFAULT}, + {APL6_WORLD, APL6, WORLD, CTRY_DEFAULT}, + {APL7_FCCA, APL7, FCCA, CTRY_DEFAULT}, + {APL8_WORLD, APL8, WORLD, CTRY_DEFAULT}, + {APL9_WORLD, APL9, WORLD, CTRY_DEFAULT}, + {APL10_WORLD, APL10, WORLD, CTRY_DEFAULT}, + {APL12_WORLD, APL12, WORLD, CTRY_DEFAULT}, + {APL13_WORLD, APL13, WORLD, CTRY_DEFAULT}, + {APL14_WORLD, APL14, WORLD, CTRY_DEFAULT}, + {APL15_WORLD, APL15, WORLD, CTRY_DEFAULT}, + {APL16_WORLD, APL16, WORLD, CTRY_DEFAULT}, + {APL17_ETSID, APL17, WORLD, CTRY_DEFAULT}, + {APL20_WORLD, APL20, WORLD, CTRY_DEFAULT}, + {APL23_WORLD, APL23, WORLD, CTRY_DEFAULT}, + {WOR0_WORLD, WOR0_WORLD, WOR0_WORLD, CTRY_DEFAULT}, + {WOR1_WORLD, WOR1_WORLD, WOR1_WORLD, CTRY_DEFAULT}, + {WOR2_WORLD, WOR2_WORLD, WOR2_WORLD, CTRY_DEFAULT}, + {WOR3_WORLD, WOR3_WORLD, WOR3_WORLD, CTRY_DEFAULT}, + {WOR4_FCCA, WOR4_FCCA, WOR4_FCCA, CTRY_DEFAULT}, + {WOR5_ETSIC, WOR5_ETSIC, WOR5_ETSIC, CTRY_DEFAULT}, + {WOR01_WORLD, WOR01_WORLD, WOR01_WORLD, CTRY_DEFAULT}, + {WOR02_WORLD, WOR02_WORLD, WOR02_WORLD, CTRY_DEFAULT}, + {EU1_WORLD, EU1_WORLD, EU1_WORLD, CTRY_DEFAULT}, + {WOR9_WORLD, WOR9_WORLD, WOR9_WORLD, CTRY_DEFAULT}, + {WORA_WORLD, WORA_WORLD, WORA_WORLD, CTRY_DEFAULT}, + {WORB_WORLD, WORB_WORLD, WORB_WORLD, CTRY_DEFAULT}, + {WORC_WORLD, WORC_WORLD, WORC_WORLD, CTRY_DEFAULT}, + {MKK5_MKKC, MKK5, MKKC, CTRY_JAPAN15}, + {MKK5_MKKA2, MKK5, MKKA, CTRY_DEFAULT}, +}; + +static const struct country_code_to_reg_dmn g_all_countries[] = { + {CTRY_AFGHANISTAN, ETSI1_WORLD, "AF", "AFGHANISTAN"}, + {CTRY_ALBANIA, ETSI1_WORLD, "AL", "ALBANIA"}, + {CTRY_ALGERIA, APL13_WORLD, "DZ", "ALGERIA"}, + {CTRY_AMERICAN_SAMOA, FCC3_FCCA, "AS", "AMERICAN SAMOA"}, + {CTRY_ANGUILLA, ETSI1_WORLD, "AI", "ANGUILLA"}, + {CTRY_ARGENTINA, APL17_ETSID, "AR", "ARGENTINA"}, + {CTRY_ARMENIA, ETSI4_WORLD, "AM", "ARMENIA"}, + {CTRY_ARUBA, ETSI1_WORLD, "AW", "ARUBA"}, + {CTRY_AUSTRALIA, FCC6_WORLD, "AU", "AUSTRALIA"}, + {CTRY_AUSTRIA, ETSI1_WORLD, "AT", "AUSTRIA"}, + {CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ", "AZERBAIJAN"}, + {CTRY_BAHAMAS, FCC3_WORLD, "BS", "BAHAMAS"}, + {CTRY_BAHRAIN, APL15_WORLD, "BH", "BAHRAIN"}, + {CTRY_BANGLADESH, APL1_WORLD, "BD", "BANGLADESH"}, + {CTRY_BARBADOS, FCC2_WORLD, "BB", "BARBADOS"}, + {CTRY_BELARUS, ETSI1_WORLD, "BY", "BELARUS"}, + {CTRY_BELGIUM, ETSI1_WORLD, "BE", "BELGIUM"}, + {CTRY_BELIZE, ETSI8_WORLD, "BZ", "BELIZE"}, + {CTRY_BERMUDA, FCC3_FCCA, "BM", "BERMUDA"}, + {CTRY_BHUTAN, ETSI1_WORLD, "BT", "BHUTAN"}, + {CTRY_BOLIVIA, APL8_WORLD, "BO", "BOLIVIA"}, + {CTRY_BOSNIA_HERZ, ETSI1_WORLD, "BA", "BOSNIA AND HERZEGOVINA"}, + {CTRY_BRAZIL, FCC3_ETSIC, "BR", "BRAZIL"}, + {CTRY_BRUNEI_DARUSSALAM, APL6_WORLD, "BN", "BRUNEI DARUSSALAM"}, + {CTRY_BULGARIA, ETSI1_WORLD, "BG", "BULGARIA"}, + {CTRY_BURKINA_FASO, FCC3_WORLD, "BF", "BURKINA-FASO"}, + {CTRY_CAMBODIA, ETSI1_WORLD, "KH", "CAMBODIA"}, + {CTRY_CANADA, FCC3_FCCA, "CA", "CANADA"}, + {CTRY_CAYMAN_ISLANDS, FCC3_WORLD, "KY", "CAYMAN ISLANDS"}, + {CTRY_CENTRAL_AFRICA_REPUBLIC, FCC3_WORLD, "CF", "AFRICA REPUBLIC"}, + {CTRY_CHAD, ETSI1_WORLD, "TD", "CHAD"}, + {CTRY_CHILE, APL23_WORLD, "CL", "CHILE"}, + {CTRY_CHINA, APL14_WORLD, "CN", "CHINA"}, + {CTRY_CHRISTMAS_ISLAND, FCC3_WORLD, "CX", "CHRISTMAS ISLAND"}, + {CTRY_COLOMBIA, FCC3_WORLD, "CO", "COLOMBIA"}, + {CTRY_COSTA_RICA, FCC3_WORLD, "CR", "COSTA RICA"}, + {CTRY_COTE_DIVOIRE, FCC3_WORLD, "CI", "COTE DIVOIRE"}, + {CTRY_CROATIA, ETSI1_WORLD, "HR", "CROATIA"}, + {CTRY_CYPRUS, ETSI1_WORLD, "CY", "CYPRUS"}, + {CTRY_CZECH, ETSI1_WORLD, "CZ", "CZECH REPUBLIC"}, + {CTRY_DENMARK, ETSI1_WORLD, "DK", "DENMARK"}, + {CTRY_DOMINICA, FCC2_FCCA, "DM", "DOMINICA"}, + {CTRY_DOMINICAN_REPUBLIC, FCC2_FCCA, "DO", "DOMINICAN REPUBLIC"}, + {CTRY_ECUADOR, FCC3_WORLD, "EC", "ECUADOR"}, + {CTRY_EGYPT, ETSI3_WORLD, "EG", "EGYPT"}, + {CTRY_EL_SALVADOR, FCC2_WORLD, "SV", "EL SALVADOR"}, + {CTRY_ESTONIA, ETSI1_WORLD, "EE", "ESTONIA"}, + {CTRY_ETHIOPIA, ETSI1_WORLD, "ET", "ETHIOPIA"}, + {CTRY_FINLAND, ETSI1_WORLD, "FI", "FINLAND"}, + {CTRY_FRANCE, ETSI1_WORLD, "FR", "FRANCE"}, + {CTRY_FRENCH_GUIANA, ETSI1_WORLD, "GF", "FRENCH GUIANA"}, + {CTRY_FRENCH_POLYNESIA, ETSI1_WORLD, "PF", "FRENCH POLYNESIA"}, + {CTRY_GEORGIA, ETSI4_WORLD, "GE", "GEORGIA"}, + {CTRY_GERMANY, ETSI1_WORLD, "DE", "GERMANY"}, + {CTRY_GHANA, FCC3_WORLD, "GH", "GHANA"}, + {CTRY_GIBRALTAR, ETSI1_WORLD, "GI", "GIBRALTAR"}, + {CTRY_GREECE, ETSI1_WORLD, "GR", "GREECE"}, + {CTRY_GREENLAND, ETSI1_WORLD, "GL", "GREENLAND"}, + {CTRY_GRENADA, FCC3_FCCA, "GD", "GRENADA"}, + {CTRY_GUADELOUPE, ETSI1_WORLD, "GP", "GUADELOUPE"}, + {CTRY_GUAM, FCC3_FCCA, "GU", "GUAM"}, + {CTRY_GUATEMALA, ETSI1_WORLD, "GT", "GUATEMALA"}, + {CTRY_GUYANA, APL1_ETSIC, "GY", "GUYANA"}, + {CTRY_HAITI, FCC3_FCCA, "HT", "HAITI"}, + {CTRY_HONDURAS, FCC13_WORLD, "HN", "HONDURAS"}, + {CTRY_HONG_KONG, FCC3_WORLD, "HK", "HONG KONG"}, + {CTRY_HUNGARY, ETSI1_WORLD, "HU", "HUNGARY"}, + {CTRY_ICELAND, ETSI1_WORLD, "IS", "ICELAND"}, + {CTRY_INDIA, APL15_WORLD, "IN", "INDIA"}, + {CTRY_INDONESIA, APL2_ETSIC, "ID", "INDONESIA"}, + {CTRY_IRAQ, ETSI1_WORLD, "IQ", "IRAQ"}, + {CTRY_IRELAND, ETSI1_WORLD, "IE", "IRELAND"}, + {CTRY_ISRAEL, ETSI3_WORLD, "IL", "ISRAEL"}, + {CTRY_ITALY, ETSI1_WORLD, "IT", "ITALY"}, + {CTRY_JAMAICA, FCC13_WORLD, "JM", "JAMAICA"}, + {CTRY_JORDAN, APL4_WORLD, "JO", "JORDAN"}, + {CTRY_KAZAKHSTAN, NULL1_WORLD, "KZ", "KAZAKHSTAN"}, + {CTRY_KENYA, APL12_WORLD, "KE", "KENYA"}, + {CTRY_KOREA_ROC, APL9_WORLD, "KR", "KOREA REPUBLIC"}, + {CTRY_KUWAIT, ETSI3_WORLD, "KW", "KUWAIT"}, + {CTRY_LATVIA, ETSI1_WORLD, "LV", "LATVIA"}, + {CTRY_LEBANON, FCC3_WORLD, "LB", "LEBANON"}, + {CTRY_LESOTHO, ETSI1_WORLD, "LS", "LESOTHO"}, + {CTRY_LIECHTENSTEIN, ETSI1_WORLD, "LI", "LIECHTENSTEIN"}, + {CTRY_LITHUANIA, ETSI1_WORLD, "LT", "LITHUANIA"}, + {CTRY_LUXEMBOURG, ETSI1_WORLD, "LU", "LUXEMBOURG"}, + {CTRY_MACAU, FCC3_WORLD, "MO", "MACAU SAR"}, + {CTRY_MACEDONIA, ETSI1_WORLD, "MK", "MACEDONIA, FYRO"}, + {CTRY_MALAWI, ETSI1_WORLD, "MW", "MALAWI"}, + {CTRY_MALAYSIA, FCC11_WORLD, "MY", "MALAYSIA"}, + {CTRY_MALDIVES, APL6_WORLD, "MV", "MALDIVES"}, + {CTRY_MALTA, ETSI1_WORLD, "MT", "MALTA"}, + {CTRY_MARSHALL_ISLANDS, FCC3_FCCA, "MH", "MARSHALL ISLANDS"}, + {CTRY_MARTINIQUE, ETSI1_WORLD, "MQ", "MARTINIQUE"}, + {CTRY_MAURITANIA, ETSI1_WORLD, "MR", "MAURITANA"}, + {CTRY_MAURITIUS, FCC3_WORLD, "MU", "MAURITIUS"}, + {CTRY_MAYOTTE, ETSI1_WORLD, "YT", "MAYOTTE"}, + {CTRY_MEXICO, FCC3_ETSIC, "MX", "MEXICO"}, + {CTRY_MICRONESIA, FCC3_FCCA, "FM", "MICRONESIA"}, + {CTRY_MOLDOVA, ETSI1_WORLD, "MD", "MOLDOVA"}, + {CTRY_MONACO, ETSI1_WORLD, "MC", "MONACO"}, + {CTRY_MONGOLIA, FCC3_WORLD, "MN", "MONGOLIA"}, + {CTRY_MONTENEGRO, ETSI1_WORLD, "ME", "MONTENEGRO"}, + {CTRY_MOROCCO, ETSI3_WORLD, "MA", "MOROCCO"}, + {CTRY_NAMIBIA, APL20_WORLD, "NA", "NAMIBIA"}, + {CTRY_NEPAL, APL23_WORLD, "NP", "NEPAL"}, + {CTRY_NETHERLANDS, ETSI1_WORLD, "NL", "NETHERLANDS"}, + {CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN", "NETHERLANDS ANTILLES"}, + {CTRY_NEW_ZEALAND, FCC3_ETSIC, "NZ", "NEW ZEALAND"}, + {CTRY_NIGERIA, APL8_WORLD, "NG", "NIGERIA"}, + {CTRY_NORTHERN_MARIANA_ISLANDS, FCC3_FCCA, "MP", "MARIANA ISLANDS"}, + {CTRY_NICARAGUA, FCC3_FCCA, "NI", "NICARAGUA"}, + {CTRY_NORWAY, ETSI1_WORLD, "NO", "NORWAY"}, + {CTRY_OMAN, ETSI1_WORLD, "OM", "OMAN"}, + {CTRY_PAKISTAN, APL1_ETSIC, "PK", "PAKISTAN"}, + {CTRY_PALAU, FCC3_FCCA, "PW", "PALAU"}, + {CTRY_PANAMA, FCC14_FCCB, "PA", "PANAMA"}, + {CTRY_PAPUA_NEW_GUINEA, FCC3_WORLD, "PG", "PAPUA NEW GUINEA"}, + {CTRY_PARAGUAY, FCC3_WORLD, "PY", "PARAGUAY"}, + {CTRY_PERU, FCC3_WORLD, "PE", "PERU"}, + {CTRY_PHILIPPINES, FCC3_WORLD, "PH", "PHILIPPINES"}, + {CTRY_POLAND, ETSI1_WORLD, "PL", "POLAND"}, + {CTRY_PORTUGAL, ETSI1_WORLD, "PT", "PORTUGAL"}, + {CTRY_PUERTO_RICO, FCC3_FCCA, "PR", "PUERTO RICO"}, + {CTRY_QATAR, APL1_WORLD, "QA", "QATAR"}, + {CTRY_REUNION, ETSI1_WORLD, "RE", "REUNION"}, + {CTRY_ROMANIA, ETSI1_WORLD, "RO", "ROMANIA"}, + {CTRY_RUSSIA, ETSI8_WORLD, "RU", "RUSSIA"}, + {CTRY_RWANDA, FCC3_WORLD, "RW", "RWANDA"}, + {CTRY_SAINT_BARTHELEMY, ETSI1_WORLD, "BL", "SAINT BARTHELEMY"}, + {CTRY_SAINT_KITTS_AND_NEVIS, APL10_WORLD, "KN", "SAINT KITTS"}, + {CTRY_SAINT_LUCIA, APL10_WORLD, "LC", "SAINT LUCIA"}, + {CTRY_SAINT_MARTIN, ETSI1_WORLD, "MF", "SAINT MARTIN"}, + {CTRY_SAINT_PIERRE_AND_MIQUELON, ETSI1_WORLD, "PM", "SAINT PIERRE"}, + {CTRY_SAINT_VINCENT_AND_THE_GRENADIENS, ETSI1_WORLD, "VC", "VINCENT"}, + {CTRY_SAMOA, ETSI1_WORLD, "WS", "SAMOA"}, + {CTRY_SAUDI_ARABIA, ETSI1_WORLD, "SA", "SAUDI ARABIA"}, + {CTRY_SENEGAL, FCC13_WORLD, "SN", "SENEGAL"}, + {CTRY_SERBIA, ETSI1_WORLD, "RS", "REPUBLIC OF SERBIA"}, + {CTRY_SINGAPORE, FCC3_WORLD, "SG", "SINGAPORE"}, + {CTRY_SLOVAKIA, ETSI1_WORLD, "SK", "SLOVAKIA"}, + {CTRY_SLOVENIA, ETSI1_WORLD, "SI", "SLOVENIA"}, + {CTRY_SOUTH_AFRICA, FCC3_WORLD, "ZA", "SOUTH AFRICA"}, + {CTRY_SPAIN, ETSI1_WORLD, "ES", "SPAIN"}, + {CTRY_SURINAME, ETSI1_WORLD, "SR", "SURINAME"}, + {CTRY_SRI_LANKA, FCC3_WORLD, "LK", "SRI LANKA"}, + {CTRY_SWEDEN, ETSI1_WORLD, "SE", "SWEDEN"}, + {CTRY_SWITZERLAND, ETSI1_WORLD, "CH", "SWITZERLAND"}, + {CTRY_TAIWAN, FCC3_FCCA, "TW", "TAIWAN"}, + {CTRY_TANZANIA, APL1_WORLD, "TZ", "TANZANIA"}, + {CTRY_THAILAND, FCC3_WORLD, "TH", "THAILAND"}, + {CTRY_TOGO, ETSI1_WORLD, "TG", "TOGO"}, + {CTRY_TRINIDAD_Y_TOBAGO, FCC3_WORLD, "TT", "TRINIDAD AND TOBAGO"}, + {CTRY_TUNISIA, ETSI3_WORLD, "TN", "TUNISIA"}, + {CTRY_TURKEY, ETSI1_WORLD, "TR", "TURKEY"}, + {CTRY_TURKS_AND_CAICOS, FCC3_WORLD, "TC" "TURKS AND CAICOS"}, + {CTRY_UGANDA, FCC3_WORLD, "UG", "UGANDA"}, + {CTRY_UKRAINE, ETSI9_WORLD, "UA", "UKRAINE"}, + {CTRY_UAE, FCC3_WORLD, "AE", "UNITED ARAB EMIRATES"}, + {CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB", "UNITED KINGDOM"}, + {CTRY_UNITED_STATES, FCC8_FCCA, "US", "UNITED STATES"}, + {CTRY_URUGUAY, FCC2_WORLD, "UY", "URUGUAY"}, + {CTRY_UZBEKISTAN, ETSI3_WORLD, "UZ", "UZBEKISTAN"}, + {CTRY_VANUATU, FCC3_WORLD, "VU", "VANUATU"}, + {CTRY_VENEZUELA, FCC2_ETSIC, "VE", "VENEZUELA"}, + {CTRY_VIET_NAM, FCC3_WORLD, "VN", "VIETNAM"}, + {CTRY_VIRGIN_ISLANDS, FCC3_FCCA, "VI", "VIRGIN ISLANDS"}, + {CTRY_WALLIS_AND_FUTUNA, ETSI1_WORLD, "WF" "WALLIS"}, + {CTRY_YEMEN, NULL1_WORLD, "YE", "YEMEN"}, + {CTRY_ZIMBABWE, ETSI1_WORLD, "ZW", "ZIMBABWE"}, + {CTRY_JAPAN15, MKK5_MKKC, "JP", "JAPAN"}, + {CTRY_XA, MKK5_MKKA2, "XA", "JAPAN PASSIVE"} +}; + +static const struct reg_dmn g_reg_dmns[] = { + {FCC1, CTL_FCC}, + {FCC2, CTL_FCC}, + {FCC3, CTL_FCC}, + {FCC4, CTL_FCC}, + {FCC5, CTL_FCC}, + {FCC6, CTL_FCC}, + {FCC7, CTL_FCC}, + {FCC8, CTL_FCC}, + {FCC9, CTL_FCC}, + {FCC10, CTL_FCC}, + {FCC11, CTL_FCC}, + {FCC13, CTL_FCC}, + {FCC14, CTL_FCC}, + {ETSI1, CTL_ETSI}, + {ETSI2, CTL_ETSI}, + {ETSI3, CTL_ETSI}, + {ETSI4, CTL_ETSI}, + {ETSI5, CTL_ETSI}, + {ETSI6, CTL_ETSI}, + {ETSI8, CTL_ETSI}, + {ETSI9, CTL_ETSI}, + {ETSI10, CTL_ETSI}, + {ETSI11, CTL_ETSI}, + {APL1, CTL_ETSI}, + {APL2, CTL_ETSI}, + {APL3, CTL_ETSI}, + {APL4, CTL_ETSI}, + {APL5, CTL_ETSI}, + {APL6, CTL_ETSI}, + {APL7, CTL_ETSI}, + {APL8, CTL_ETSI}, + {APL9, CTL_ETSI}, + {APL10, CTL_ETSI}, + {APL11, CTL_ETSI}, + {APL12, CTL_ETSI}, + {APL13, CTL_ETSI}, + {APL14, CTL_FCC}, + {APL15, CTL_FCC}, + {APL16, CTL_FCC}, + {APL17, CTL_FCC}, + {APL20, CTL_ETSI}, + {APL23, CTL_ETSI}, + {NULL1, CTL_NONE}, + {MKK3, CTL_MKK}, + {MKK5, CTL_MKK}, + {MKK11, CTL_MKK}, + {WORLD, CTL_ETSI}, + {FCCA, CTL_FCC}, + {MKKA, CTL_MKK}, + {MKKC, CTL_MKK}, + {ETSIC, CTL_ETSI}, + {WOR0_WORLD, CTL_NONE}, + {WOR1_WORLD, CTL_NONE}, + {WOR2_WORLD, CTL_NONE}, + {WOR3_WORLD, CTL_NONE}, + {WOR4_FCCA, CTL_NONE}, + {WOR5_ETSIC, CTL_NONE}, + {WOR01_WORLD, CTL_NONE}, + {WOR02_WORLD, CTL_NONE}, + {EU1_WORLD, CTL_NONE}, + {WOR9_WORLD, CTL_NONE}, + {WORA_WORLD, CTL_NONE}, + {WORB_WORLD, CTL_NONE}, + {WORC_WORLD, CTL_NONE}, +}; + + +struct reg_dmn_tables g_reg_dmn_tbl = { + g_reg_dmn_pairs, + g_all_countries, + g_reg_dmns, + QDF_ARRAY_SIZE(g_reg_dmn_pairs), + QDF_ARRAY_SIZE(g_all_countries), + QDF_ARRAY_SIZE(g_reg_dmns), +}; + +/* + * ETSI is updating EN 301 893, which specifies 5 GHz channel access + * in Europe + */ +static const char etsi_europe_country[][2] = { + {'A', 'T'}, + {'B', 'E'}, + {'B', 'G'}, + {'C', 'Z'}, + {'D', 'K'}, + {'E', 'E'}, + {'F', 'R'}, + + {'D', 'E'}, + {'I', 'S'}, + {'I', 'E'}, + {'I', 'T'}, + {'E', 'L'}, + {'E', 'S'}, + {'C', 'Y'}, + + {'L', 'V'}, + {'L', 'I'}, + {'L', 'T'}, + {'L', 'U'}, + {'H', 'U'}, + {'M', 'T'}, + {'N', 'L'}, + + {'N', 'O'}, + {'P', 'L'}, + {'P', 'T'}, + {'R', 'O'}, + {'S', 'I'}, + {'S', 'K'}, + {'T', 'R'}, + + {'F', 'I'}, + {'S', 'E'}, + {'C', 'H'}, + {'U', 'K'}, + {'H', 'R'}, +}; + +bool cds_is_etsi_europe_country(uint8_t *country) +{ + int32_t i; + + for (i = 0; i < QDF_ARRAY_SIZE(etsi_europe_country); i++) { + if (country[0] == etsi_europe_country[i][0] && + country[1] == etsi_europe_country[i][1]) + return true; + } + + return false; +} + +/** + * get_bdf_reg_dmn() - get regulatory domain from BDF + * @reg_dmn: BDF regulatory domain + * + * Return: regulatory domain + */ +static uint16_t get_bdf_reg_dmn(uint16_t reg_dmn) +{ + return reg_dmn & ~WORLD_ROAMING_FLAG; +} + +/** + * is_reg_dmn_valid() - is regulatory domain valid + * @reg_dmn: regulatory domain + * + * Return: true or false + */ +static bool is_reg_dmn_valid(uint16_t reg_dmn) +{ + int32_t i; + + if (reg_dmn & CTRY_FLAG) { + uint16_t cc = reg_dmn & ~CTRY_FLAG; + + for (i = 0; i < g_reg_dmn_tbl.all_countries_cnt; i++) + if (g_reg_dmn_tbl.all_countries[i].country_code == cc) + return true; + } else { + for (i = 0; i < g_reg_dmn_tbl.reg_dmn_pairs_cnt; i++) + if (g_reg_dmn_tbl.reg_dmn_pairs[i].reg_dmn_pair + == reg_dmn) + return true; + } + + cds_err("invalid regulatory domain/country code 0x%x", reg_dmn); + + return false; +} + +/** + * find_country() - find country data + * @country_code: country code + * + * Return: country code data pointer + */ +static const struct country_code_to_reg_dmn *find_country(uint16_t country_code) +{ + int32_t i; + + for (i = 0; i < g_reg_dmn_tbl.all_countries_cnt; i++) { + if (g_reg_dmn_tbl.all_countries[i].country_code == country_code) + return &g_reg_dmn_tbl.all_countries[i]; + } + + return NULL; +} + +/** + * cds_get_country_from_alpha2() - get country from alpha2 + * @alpha2: country code alpha2 + * + * Return: country code + */ +int32_t cds_get_country_from_alpha2(uint8_t *alpha2) +{ + int32_t i; + + for (i = 0; i < g_reg_dmn_tbl.all_countries_cnt; i++) { + if (g_reg_dmn_tbl.all_countries[i].alpha2[0] == alpha2[0] && + g_reg_dmn_tbl.all_countries[i].alpha2[1] == alpha2[1]) + return g_reg_dmn_tbl.all_countries[i].country_code; + } + + return CTRY_DEFAULT; +} + +/** + * reg_dmn_get_default_country() - get default country for regulatory domain + * @reg_dmn: regulatory domain + * + * Return: default country + */ +static uint16_t reg_dmn_get_default_country(uint16_t reg_dmn) +{ + int32_t i; + const struct country_code_to_reg_dmn *country = NULL; + uint16_t cc = reg_dmn & ~CTRY_FLAG; + + if (reg_dmn & CTRY_FLAG) { + country = find_country(cc); + if (country) + return cc; + } + + for (i = 0; i < g_reg_dmn_tbl.reg_dmn_pairs_cnt; i++) { + if (g_reg_dmn_tbl.reg_dmn_pairs[i].reg_dmn_pair == reg_dmn) { + if (g_reg_dmn_tbl.reg_dmn_pairs[i].single_cc != 0) + return g_reg_dmn_tbl.reg_dmn_pairs[i].single_cc; + else + i = g_reg_dmn_tbl.reg_dmn_pairs_cnt; + } + } + + return CTRY_DEFAULT; +} + +/** + * get_reg_dmn_pair() - get regulatory domain pair pointer + * @reg_dmn: regulatory domain + * + * Return: pointer to regulatory domain pair data + */ +static const struct reg_dmn_pair *get_reg_dmn_pair(uint16_t reg_dmn) +{ + int32_t i; + + for (i = 0; i < g_reg_dmn_tbl.reg_dmn_pairs_cnt; i++) { + if (g_reg_dmn_tbl.reg_dmn_pairs[i].reg_dmn_pair == reg_dmn) + return &g_reg_dmn_tbl.reg_dmn_pairs[i]; + } + + return NULL; +} + +/** + * get_reg_dmn() - get regulatory domain pointer + * @reg_dmn: regulatory domain + * + * Return: pointer to regulatory domain data + */ +static const struct reg_dmn *get_reg_dmn(uint16_t reg_dmn) +{ + int32_t i; + + for (i = 0; i < g_reg_dmn_tbl.reg_dmns_cnt; i++) { + if (g_reg_dmn_tbl.reg_dmns[i].reg_dmn == reg_dmn) + return &g_reg_dmn_tbl.reg_dmns[i]; + } + + return NULL; +} + +/** + * get_country_from_rd() - get country from regulatory domain + * @reg_dmn: regulatory domain + * + * Return: country code enum + */ +static const struct country_code_to_reg_dmn *get_country_from_rd( + uint16_t reg_dmn) +{ + int32_t i; + + for (i = 0; i < g_reg_dmn_tbl.all_countries_cnt; i++) { + if (g_reg_dmn_tbl.all_countries[i].reg_dmn_pair == reg_dmn) + return &g_reg_dmn_tbl.all_countries[i]; + } + + return NULL; +} + +/** + * reg_dmn_sanitize() - sanitize regulatory domain + * @reg: regulatory data structure + * + * Return: none + */ +static void reg_dmn_sanitize(struct regulatory *reg) +{ + if (reg->reg_domain != CTRY_FLAG) + return; + + reg->reg_domain = WOR0_WORLD; +} + +/** + * cds_fill_some_regulatory_info() - fill regulatory information + * @reg: regulatory data structure + * + * Return: error code + */ +int32_t cds_fill_some_regulatory_info(struct regulatory *reg) +{ + uint16_t country_code; + uint16_t reg_dmn, rd; + const struct country_code_to_reg_dmn *country = NULL; + + reg_dmn_sanitize(reg); + rd = reg->reg_domain; + + if (!is_reg_dmn_valid(rd)) + return -EINVAL; + + reg_dmn = get_bdf_reg_dmn(rd); + + country_code = reg_dmn_get_default_country(reg_dmn); + if (country_code == CTRY_DEFAULT && reg_dmn == CTRY_DEFAULT) + country_code = CTRY_UNITED_STATES; + + if (country_code != CTRY_DEFAULT) { + country = find_country(country_code); + if (!country) { + cds_err("not a valid country code"); + return -EINVAL; + } + + reg_dmn = country->reg_dmn_pair; + } + + reg->regpair = get_reg_dmn_pair(reg_dmn); + if (!reg->regpair) { + cds_err("no regpair is found, can not proceeed"); + return -EINVAL; + } + + reg->country_code = country_code; + + if (!country) + country = get_country_from_rd(reg_dmn); + + if (country) { + reg->alpha2[0] = country->alpha2[0]; + reg->alpha2[1] = country->alpha2[1]; + } else { + reg->alpha2[0] = '0'; + reg->alpha2[1] = '0'; + } + + return 0; +} + +/** + * cds_fill_and_send_ctl_to_fw() - fill and send ctl to firmware + * @reg: the regulatory handle + * + * Return: none + */ +void cds_fill_and_send_ctl_to_fw(struct regulatory *reg) +{ + const struct reg_dmn *reg_dmn_2g = NULL; + const struct reg_dmn *reg_dmn_5g = NULL; + uint8_t ctl_2g, ctl_5g; + const struct reg_dmn_pair *regpair; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + cds_err("unable to get WMA handle"); + return; + } + + if (!reg->regpair) { + cds_err(FL("no regpair is found, can not proceed")); + return; + } + regpair = reg->regpair; + reg_dmn_2g = get_reg_dmn(regpair->reg_dmn_2ghz); + if (!reg_dmn_2g) { + cds_err("failed to get regdmn 2G"); + return; + } + + reg_dmn_5g = get_reg_dmn(regpair->reg_dmn_5ghz); + if (!reg_dmn_5g) { + cds_err("failed to get regdmn 5G"); + return; + } + + ctl_2g = reg_dmn_2g->conformance_test_limit; + ctl_5g = reg_dmn_5g->conformance_test_limit; + + + reg->ctl_5g = ctl_5g; + reg->ctl_2g = ctl_2g; + + wma_send_regdomain_info_to_fw(reg->reg_domain, regpair->reg_dmn_2ghz, + regpair->reg_dmn_5ghz, ctl_2g, ctl_5g); +} diff --git a/drivers/staging/qcacld-3.0/core/cds/src/cds_sched.c b/drivers/staging/qcacld-3.0/core/cds/src/cds_sched.c new file mode 100644 index 0000000000000000000000000000000000000000..b2f63ba0b9770c4b7ad01bb9b34e7498f6a054e1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/src/cds_sched.c @@ -0,0 +1,1534 @@ +/* + * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * File: cds_sched.c + * + * DOC: CDS Scheduler Implementation + */ + +#include +#include +#include +#include +#include +#include +#include +#include "cds_sched.h" +#include +#include "wma_types.h" +#include +#include +#include +#include +#ifdef RX_PERFORMANCE +#include +#endif + +static spinlock_t ssr_protect_lock; + +struct shutdown_notifier { + struct list_head list; + void (*cb)(void *priv); + void *priv; +}; + +struct list_head shutdown_notifier_head; + +enum notifier_state { + NOTIFIER_STATE_NONE, + NOTIFIER_STATE_NOTIFYING, +} notifier_state; + +static p_cds_sched_context gp_cds_sched_context; + +#ifdef WLAN_FEATURE_PKT_CAPTURE +static int cds_ol_mon_thread(void *arg); +static QDF_STATUS cds_alloc_ol_mon_pkt_freeq(p_cds_sched_context pschedcontext); +static inline +int cds_set_mon_cpus_allowed_ptr(struct task_struct *task, unsigned long cpu) +{ + return set_cpus_allowed_ptr(task, cpumask_of(cpu)); +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +#ifdef QCA_CONFIG_SMP +static int cds_ol_rx_thread(void *arg); +static uint32_t affine_cpu; +static QDF_STATUS cds_alloc_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext); + +#define CDS_CORE_PER_CLUSTER (4) +/*Maximum 2 clusters supported*/ +#define CDS_MAX_CPU_CLUSTERS 2 + +#define CDS_CPU_CLUSTER_TYPE_LITTLE 0 +#define CDS_CPU_CLUSTER_TYPE_PERF 1 + +static inline +int cds_set_cpus_allowed_ptr_with_cpu(struct task_struct *task, + unsigned long cpu) +{ + return set_cpus_allowed_ptr(task, cpumask_of(cpu)); +} + +static inline +int cds_set_cpus_allowed_ptr_with_mask(struct task_struct *task, + qdf_cpu_mask *new_mask) +{ + return set_cpus_allowed_ptr(task, new_mask); +} + +void cds_set_rx_thread_cpu_mask(uint8_t cpu_affinity_mask) +{ + p_cds_sched_context sched_context = get_cds_sched_ctxt(); + + if (!sched_context) { + qdf_err("invalid context"); + return; + } + sched_context->conf_rx_thread_cpu_mask = cpu_affinity_mask; +} + +void cds_set_rx_thread_ul_cpu_mask(uint8_t cpu_affinity_mask) +{ + p_cds_sched_context sched_context = get_cds_sched_ctxt(); + + if (!sched_context) { + qdf_err("invalid context"); + return; + } + sched_context->conf_rx_thread_ul_affinity = cpu_affinity_mask; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +/** + * cds_rx_thread_log_cpu_affinity_change - Log Rx thread affinity change + * @core_affine_cnt: Available cores + * @tput_req: Throughput request + * @old_mask: Old affinity mask + * @new_mask: New affinity mask + * + * Return: NONE + */ +static void cds_rx_thread_log_cpu_affinity_change(unsigned char core_affine_cnt, + int tput_req, + struct cpumask *old_mask, + struct cpumask *new_mask) +{ + char new_mask_str[10]; + char old_mask_str[10]; + + qdf_mem_zero(new_mask_str, sizeof(new_mask_str)); + qdf_mem_zero(new_mask_str, sizeof(old_mask_str)); + + cpumap_print_to_pagebuf(false, old_mask_str, old_mask); + cpumap_print_to_pagebuf(false, new_mask_str, new_mask); + + cds_debug("num online cores %d, high tput req %d, Rx_thread old mask %s new mask %s", + core_affine_cnt, tput_req, old_mask_str, new_mask_str); +} +#else +static void cds_rx_thread_log_cpu_affinity_change(unsigned char core_affine_cnt, + int tput_req, + struct cpumask *old_mask, + struct cpumask *new_mask) +{ +} +#endif + +/** + * cds_sched_find_attach_cpu - find available cores and attach to required core + * @pSchedContext: wlan scheduler context + * @high_throughput: high throughput is required or not + * + * Find current online cores. + * During high TPUT, + * 1) If user INI configured cores, affine to those cores + * 2) Otherwise perf cores. + * 3) Otherwise to all cores. + * + * During low TPUT, set affinity to any core, let system decide. + * + * Return: 0 success + * 1 fail + */ +static int cds_sched_find_attach_cpu(p_cds_sched_context pSchedContext, + bool high_throughput) +{ + unsigned char core_affine_count = 0; + qdf_cpu_mask new_mask; + unsigned long cpus; + struct cds_config_info *cds_cfg; + + cds_debug("num possible cpu %d", num_possible_cpus()); + + qdf_cpumask_clear(&new_mask); + + if (high_throughput) { + /* Get Online perf/pwr CPU count */ + for_each_online_cpu(cpus) { + if (topology_physical_package_id(cpus) > + CDS_MAX_CPU_CLUSTERS) { + cds_err("can handle max %d clusters, returning...", + CDS_MAX_CPU_CLUSTERS); + goto err; + } + + if (pSchedContext->conf_rx_thread_cpu_mask) { + if (pSchedContext->conf_rx_thread_cpu_mask & + (1 << cpus)) + qdf_cpumask_set_cpu(cpus, &new_mask); + } else if (topology_physical_package_id(cpus) == + CDS_CPU_CLUSTER_TYPE_PERF) { + qdf_cpumask_set_cpu(cpus, &new_mask); + } + + core_affine_count++; + } + } else { + /* Attach to all cores, let scheduler decide */ + qdf_cpumask_setall(&new_mask); + } + + cds_rx_thread_log_cpu_affinity_change(core_affine_count, + (int)pSchedContext->high_throughput_required, + &pSchedContext->rx_thread_cpu_mask, + &new_mask); + + if (!cpumask_equal(&pSchedContext->rx_thread_cpu_mask, &new_mask)) { + cds_cfg = cds_get_ini_config(); + cpumask_copy(&pSchedContext->rx_thread_cpu_mask, &new_mask); + if (cds_cfg && cds_cfg->enable_dp_rx_threads) + dp_txrx_set_cpu_mask(cds_get_context(QDF_MODULE_ID_SOC), + &new_mask); + else + cds_set_cpus_allowed_ptr_with_mask(pSchedContext->ol_rx_thread, + &new_mask); + } + + return 0; +err: + return 1; +} + +/** + * cds_sched_handle_cpu_hot_plug - cpu hotplug event handler + * + * cpu hotplug indication handler + * will find online cores and will assign proper core based on perf requirement + * + * Return: 0 success + * 1 fail + */ +int cds_sched_handle_cpu_hot_plug(void) +{ + p_cds_sched_context pSchedContext = get_cds_sched_ctxt(); + + if (!pSchedContext) { + cds_err("invalid context"); + return 1; + } + + if (cds_is_load_or_unload_in_progress()) + return 0; + + mutex_lock(&pSchedContext->affinity_lock); + if (cds_sched_find_attach_cpu(pSchedContext, + pSchedContext->high_throughput_required)) { + cds_err("handle hot plug fail"); + mutex_unlock(&pSchedContext->affinity_lock); + return 1; + } + mutex_unlock(&pSchedContext->affinity_lock); + return 0; +} + +void cds_sched_handle_rx_thread_affinity_req(bool high_throughput) +{ + p_cds_sched_context pschedcontext = get_cds_sched_ctxt(); + unsigned long cpus; + qdf_cpu_mask new_mask; + unsigned char core_affine_count = 0; + + if (!pschedcontext || !pschedcontext->ol_rx_thread) + return; + + if (cds_is_load_or_unload_in_progress()) { + cds_err("load or unload in progress"); + return; + } + + if (pschedcontext->rx_affinity_required == high_throughput) + return; + + pschedcontext->rx_affinity_required = high_throughput; + qdf_cpumask_clear(&new_mask); + if (!high_throughput) { + /* Attach to all cores, let scheduler decide */ + qdf_cpumask_setall(&new_mask); + goto affine_thread; + } + for_each_online_cpu(cpus) { + if (topology_physical_package_id(cpus) > + CDS_MAX_CPU_CLUSTERS) { + cds_err("can handle max %d clusters ", + CDS_MAX_CPU_CLUSTERS); + return; + } + if (pschedcontext->conf_rx_thread_ul_affinity && + (pschedcontext->conf_rx_thread_ul_affinity & + (1 << cpus))) + qdf_cpumask_set_cpu(cpus, &new_mask); + + core_affine_count++; + } + +affine_thread: + cds_rx_thread_log_cpu_affinity_change( + core_affine_count, + (int)pschedcontext->rx_affinity_required, + &pschedcontext->rx_thread_cpu_mask, + &new_mask); + + mutex_lock(&pschedcontext->affinity_lock); + if (!cpumask_equal(&pschedcontext->rx_thread_cpu_mask, &new_mask)) { + cpumask_copy(&pschedcontext->rx_thread_cpu_mask, &new_mask); + cds_set_cpus_allowed_ptr_with_mask(pschedcontext->ol_rx_thread, + &new_mask); + } + mutex_unlock(&pschedcontext->affinity_lock); +} + +/** + * cds_sched_handle_throughput_req - cpu throughput requirement handler + * @high_tput_required: high throughput is required or not + * + * high or low throughput indication ahndler + * will find online cores and will assign proper core based on perf requirement + * + * Return: 0 success + * 1 fail + */ +int cds_sched_handle_throughput_req(bool high_tput_required) +{ + p_cds_sched_context pSchedContext = get_cds_sched_ctxt(); + + if (!pSchedContext) { + cds_err("invalid context"); + return 1; + } + + if (cds_is_load_or_unload_in_progress()) { + cds_err("load or unload in progress"); + return 0; + } + + mutex_lock(&pSchedContext->affinity_lock); + if (pSchedContext->high_throughput_required != high_tput_required) { + pSchedContext->high_throughput_required = high_tput_required; + if (cds_sched_find_attach_cpu(pSchedContext, + high_tput_required)) { + mutex_unlock(&pSchedContext->affinity_lock); + return 1; + } + } + mutex_unlock(&pSchedContext->affinity_lock); + return 0; +} + +/** + * cds_cpu_hotplug_multi_cluster() - calls the multi-cluster hotplug handler, + * when on a multi-cluster platform + * + * Return: QDF_STATUS + */ +static QDF_STATUS cds_cpu_hotplug_multi_cluster(void) +{ + int cpus; + unsigned int multi_cluster = 0; + + for_each_online_cpu(cpus) { + multi_cluster = topology_physical_package_id(cpus); + } + + if (!multi_cluster) + return QDF_STATUS_E_NOSUPPORT; + + if (cds_sched_handle_cpu_hot_plug()) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +/** + * __cds_cpu_hotplug_notify() - CPU hotplug event handler + * @cpu: CPU Id of the CPU generating the event + * @cpu_up: true if the CPU is online + * + * Return: None + */ +static void __cds_cpu_hotplug_notify(uint32_t cpu, bool cpu_up) +{ + unsigned long pref_cpu = 0; + p_cds_sched_context pSchedContext = get_cds_sched_ctxt(); + int i; + + if (!pSchedContext || !pSchedContext->ol_rx_thread) + return; + + if (cds_is_load_or_unload_in_progress() || cds_is_driver_recovering()) + return; + + cds_debug("'%s' event on CPU %u (of %d); Currently affine to CPU %u", + cpu_up ? "Up" : "Down", cpu, num_possible_cpus(), affine_cpu); + + /* try multi-cluster scheduling first */ + if (QDF_IS_STATUS_SUCCESS(cds_cpu_hotplug_multi_cluster())) + return; + + if (cpu_up) { + if (affine_cpu != 0) + return; + + for_each_online_cpu(i) { + if (i == 0) + continue; + pref_cpu = i; + break; + } + } else { + if (cpu != affine_cpu) + return; + + affine_cpu = 0; + for_each_online_cpu(i) { + if (i == 0) + continue; + pref_cpu = i; + break; + } + } + + if (pref_cpu == 0) + return; + + if (pSchedContext->ol_rx_thread && + !cds_set_cpus_allowed_ptr_with_cpu(pSchedContext->ol_rx_thread, + pref_cpu)) + affine_cpu = pref_cpu; +} + +/** + * cds_cpu_hotplug_notify - cpu core up/down notification handler wrapper + * @cpu: CPU Id of the CPU generating the event + * @cpu_up: true if the CPU is online + * + * Return: None + */ +static void cds_cpu_hotplug_notify(uint32_t cpu, bool cpu_up) +{ + struct qdf_op_sync *op_sync; + + if (qdf_op_protect(&op_sync)) + return; + + __cds_cpu_hotplug_notify(cpu, cpu_up); + + qdf_op_unprotect(op_sync); +} + +static void cds_cpu_online_cb(void *context, uint32_t cpu) +{ + cds_cpu_hotplug_notify(cpu, true); +} + +static void cds_cpu_before_offline_cb(void *context, uint32_t cpu) +{ + cds_cpu_hotplug_notify(cpu, false); +} +#endif /* QCA_CONFIG_SMP */ + +/** + * cds_sched_open() - initialize the CDS Scheduler + * @p_cds_context: Pointer to the global CDS Context + * @pSchedContext: Pointer to a previously allocated buffer big + * enough to hold a scheduler context. + * @SchedCtxSize: CDS scheduler context size + * + * This function initializes the CDS Scheduler + * Upon successful initialization: + * - All the message queues are initialized + * - The Main Controller thread is created and ready to receive and + * dispatch messages. + * + * + * Return: QDF status + */ +QDF_STATUS cds_sched_open(void *p_cds_context, + p_cds_sched_context pSchedContext, + uint32_t SchedCtxSize) +{ + cds_debug("Opening the CDS Scheduler"); + /* Sanity checks */ + if ((!p_cds_context) || (!pSchedContext)) { + cds_err("Null params being passed"); + return QDF_STATUS_E_FAILURE; + } + if (sizeof(cds_sched_context) != SchedCtxSize) { + cds_debug("Incorrect CDS Sched Context size passed"); + return QDF_STATUS_E_INVAL; + } + qdf_mem_zero(pSchedContext, sizeof(cds_sched_context)); +#ifdef QCA_CONFIG_SMP + spin_lock_init(&pSchedContext->ol_rx_thread_lock); + init_waitqueue_head(&pSchedContext->ol_rx_wait_queue); + init_completion(&pSchedContext->ol_rx_start_event); + init_completion(&pSchedContext->ol_suspend_rx_event); + init_completion(&pSchedContext->ol_resume_rx_event); + init_completion(&pSchedContext->ol_rx_shutdown); + pSchedContext->ol_rx_event_flag = 0; + spin_lock_init(&pSchedContext->ol_rx_queue_lock); + spin_lock_init(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + INIT_LIST_HEAD(&pSchedContext->ol_rx_thread_queue); + spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + INIT_LIST_HEAD(&pSchedContext->cds_ol_rx_pkt_freeq); + spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + if (cds_alloc_ol_rx_pkt_freeq(pSchedContext) != QDF_STATUS_SUCCESS) + goto pkt_freeqalloc_failure; + qdf_cpuhp_register(&pSchedContext->cpuhp_event_handle, + NULL, + cds_cpu_online_cb, + cds_cpu_before_offline_cb); + mutex_init(&pSchedContext->affinity_lock); + pSchedContext->high_throughput_required = false; + pSchedContext->rx_affinity_required = false; +#endif + + if (QDF_STATUS_SUCCESS != cds_alloc_mon_thread(pSchedContext)) + goto mon_freeqalloc_failure; + + gp_cds_sched_context = pSchedContext; + +#ifdef QCA_CONFIG_SMP + pSchedContext->ol_rx_thread = kthread_create(cds_ol_rx_thread, + pSchedContext, + "cds_ol_rx_thread"); + if (IS_ERR(pSchedContext->ol_rx_thread)) { + + cds_alert("Could not Create CDS OL RX Thread"); + goto OL_RX_THREAD_START_FAILURE; + + } + wake_up_process(pSchedContext->ol_rx_thread); + cds_debug("CDS OL RX thread Created"); + wait_for_completion_interruptible(&pSchedContext->ol_rx_start_event); + cds_debug("CDS OL Rx Thread has started"); +#endif + + if (QDF_STATUS_SUCCESS != cds_open_mon_thread(pSchedContext)) + goto OL_MON_THREAD_START_FAILURE; + + /* We're good now: Let's get the ball rolling!!! */ + cds_debug("CDS Scheduler successfully Opened"); + return QDF_STATUS_SUCCESS; + +OL_MON_THREAD_START_FAILURE: +#ifdef QCA_CONFIG_SMP + /* Try and force the Main thread controller to exit */ + set_bit(RX_SHUTDOWN_EVENT, &pSchedContext->ol_rx_event_flag); + set_bit(RX_POST_EVENT, &pSchedContext->ol_rx_event_flag); + wake_up_interruptible(&pSchedContext->ol_rx_wait_queue); + /* Wait for RX Thread to exit */ + wait_for_completion(&pSchedContext->ol_rx_shutdown); +#endif + +#ifdef QCA_CONFIG_SMP +OL_RX_THREAD_START_FAILURE: +#endif + cds_free_ol_mon_pkt_freeq(gp_cds_sched_context); +mon_freeqalloc_failure: +#ifdef QCA_CONFIG_SMP + qdf_cpuhp_unregister(&pSchedContext->cpuhp_event_handle); + cds_free_ol_rx_pkt_freeq(gp_cds_sched_context); +pkt_freeqalloc_failure: +#endif + gp_cds_sched_context = NULL; + + return QDF_STATUS_E_RESOURCES; + +} /* cds_sched_open() */ + +#ifdef QCA_CONFIG_SMP +/** + * cds_free_ol_rx_pkt_freeq() - free cds buffer free queue + * @pSchedContext - pointer to the global CDS Sched Context + * + * This API does mem free of the buffers available in free cds buffer + * queue which is used for Data rx processing. + * + * Return: none + */ +void cds_free_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext) +{ + struct cds_ol_rx_pkt *pkt; + + spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + while (!list_empty(&pSchedContext->cds_ol_rx_pkt_freeq)) { + pkt = list_entry((&pSchedContext->cds_ol_rx_pkt_freeq)->next, + typeof(*pkt), list); + list_del(&pkt->list); + spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + qdf_mem_free(pkt); + spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + } + spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); +} + +/** + * cds_alloc_ol_rx_pkt_freeq() - Function to allocate free buffer queue + * @pSchedContext - pointer to the global CDS Sched Context + * + * This API allocates CDS_MAX_OL_RX_PKT number of cds message buffers + * which are used for Rx data processing. + * + * Return: status of memory allocation + */ +static QDF_STATUS cds_alloc_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext) +{ + struct cds_ol_rx_pkt *pkt, *tmp; + int i; + + for (i = 0; i < CDS_MAX_OL_RX_PKT; i++) { + pkt = qdf_mem_malloc(sizeof(*pkt)); + if (!pkt) { + cds_err("Vos packet allocation for ol rx thread failed"); + goto free; + } + spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + list_add_tail(&pkt->list, &pSchedContext->cds_ol_rx_pkt_freeq); + spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + } + + return QDF_STATUS_SUCCESS; + +free: + spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + list_for_each_entry_safe(pkt, tmp, &pSchedContext->cds_ol_rx_pkt_freeq, + list) { + list_del(&pkt->list); + spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + qdf_mem_free(pkt); + spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + } + spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + return QDF_STATUS_E_NOMEM; +} + +/** + * cds_free_ol_rx_pkt() - api to release cds message to the freeq + * This api returns the cds message used for Rx data to the free queue + * @pSchedContext: Pointer to the global CDS Sched Context + * @pkt: CDS message buffer to be returned to free queue. + * + * Return: none + */ +void +cds_free_ol_rx_pkt(p_cds_sched_context pSchedContext, + struct cds_ol_rx_pkt *pkt) +{ + memset(pkt, 0, sizeof(*pkt)); + spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + list_add_tail(&pkt->list, &pSchedContext->cds_ol_rx_pkt_freeq); + spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); +} + +/** + * cds_alloc_ol_rx_pkt() - API to return next available cds message + * @pSchedContext: Pointer to the global CDS Sched Context + * + * This api returns next available cds message buffer used for rx data + * processing + * + * Return: Pointer to cds message buffer + */ +struct cds_ol_rx_pkt *cds_alloc_ol_rx_pkt(p_cds_sched_context pSchedContext) +{ + struct cds_ol_rx_pkt *pkt; + + spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + if (list_empty(&pSchedContext->cds_ol_rx_pkt_freeq)) { + spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + return NULL; + } + pkt = list_first_entry(&pSchedContext->cds_ol_rx_pkt_freeq, + struct cds_ol_rx_pkt, list); + list_del(&pkt->list); + spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock); + return pkt; +} + +/** + * cds_indicate_rxpkt() - indicate rx data packet + * @Arg: Pointer to the global CDS Sched Context + * @pkt: CDS data message buffer + * + * This api enqueues the rx packet into ol_rx_thread_queue and notifies + * cds_ol_rx_thread() + * + * Return: none + */ +void +cds_indicate_rxpkt(p_cds_sched_context pSchedContext, + struct cds_ol_rx_pkt *pkt) +{ + spin_lock_bh(&pSchedContext->ol_rx_queue_lock); + list_add_tail(&pkt->list, &pSchedContext->ol_rx_thread_queue); + spin_unlock_bh(&pSchedContext->ol_rx_queue_lock); + set_bit(RX_POST_EVENT, &pSchedContext->ol_rx_event_flag); + wake_up_interruptible(&pSchedContext->ol_rx_wait_queue); +} + +/** + * cds_close_rx_thread() - close the Rx thread + * + * This api closes the Rx thread: + * + * Return: qdf status + */ +QDF_STATUS cds_close_rx_thread(void) +{ + cds_debug("invoked"); + + if (!gp_cds_sched_context) { + cds_err("!gp_cds_sched_context"); + return QDF_STATUS_E_FAILURE; + } + + if (!gp_cds_sched_context->ol_rx_thread) + return QDF_STATUS_SUCCESS; + + /* Shut down Tlshim Rx thread */ + set_bit(RX_SHUTDOWN_EVENT, &gp_cds_sched_context->ol_rx_event_flag); + set_bit(RX_POST_EVENT, &gp_cds_sched_context->ol_rx_event_flag); + wake_up_interruptible(&gp_cds_sched_context->ol_rx_wait_queue); + wait_for_completion(&gp_cds_sched_context->ol_rx_shutdown); + gp_cds_sched_context->ol_rx_thread = NULL; + cds_drop_rxpkt_by_staid(gp_cds_sched_context, WLAN_MAX_STA_COUNT); + cds_free_ol_rx_pkt_freeq(gp_cds_sched_context); + qdf_cpuhp_unregister(&gp_cds_sched_context->cpuhp_event_handle); + + return QDF_STATUS_SUCCESS; +} /* cds_close_rx_thread */ + +/** + * cds_drop_rxpkt_by_staid() - api to drop pending rx packets for a sta + * @pSchedContext: Pointer to the global CDS Sched Context + * @staId: Station Id + * + * This api drops queued packets for a station, to drop all the pending + * packets the caller has to send WLAN_MAX_STA_COUNT as staId. + * + * Return: none + */ +void cds_drop_rxpkt_by_staid(p_cds_sched_context pSchedContext, uint16_t staId) +{ + struct list_head local_list; + struct cds_ol_rx_pkt *pkt, *tmp; + qdf_nbuf_t buf, next_buf; + + INIT_LIST_HEAD(&local_list); + spin_lock_bh(&pSchedContext->ol_rx_queue_lock); + if (list_empty(&pSchedContext->ol_rx_thread_queue)) { + spin_unlock_bh(&pSchedContext->ol_rx_queue_lock); + return; + } + list_for_each_entry_safe(pkt, tmp, &pSchedContext->ol_rx_thread_queue, + list) { + if (pkt->staId == staId || staId == WLAN_MAX_STA_COUNT) + list_move_tail(&pkt->list, &local_list); + } + spin_unlock_bh(&pSchedContext->ol_rx_queue_lock); + + list_for_each_entry_safe(pkt, tmp, &local_list, list) { + list_del(&pkt->list); + buf = pkt->Rxpkt; + while (buf) { + next_buf = qdf_nbuf_queue_next(buf); + qdf_nbuf_free(buf); + buf = next_buf; + } + cds_free_ol_rx_pkt(pSchedContext, pkt); + } +} + +/** + * cds_rx_from_queue() - function to process pending Rx packets + * @pSchedContext: Pointer to the global CDS Sched Context + * + * This api traverses the pending buffer list and calling the callback. + * This callback would essentially send the packet to HDD. + * + * Return: none + */ +static void cds_rx_from_queue(p_cds_sched_context pSchedContext) +{ + struct cds_ol_rx_pkt *pkt; + uint16_t sta_id; + + spin_lock_bh(&pSchedContext->ol_rx_queue_lock); + while (!list_empty(&pSchedContext->ol_rx_thread_queue)) { + pkt = list_first_entry(&pSchedContext->ol_rx_thread_queue, + struct cds_ol_rx_pkt, list); + list_del(&pkt->list); + spin_unlock_bh(&pSchedContext->ol_rx_queue_lock); + sta_id = pkt->staId; + pkt->callback(pkt->context, pkt->Rxpkt, sta_id); + cds_free_ol_rx_pkt(pSchedContext, pkt); + spin_lock_bh(&pSchedContext->ol_rx_queue_lock); + } + spin_unlock_bh(&pSchedContext->ol_rx_queue_lock); +} + +/** + * cds_ol_rx_thread() - cds main tlshim rx thread + * @Arg: pointer to the global CDS Sched Context + * + * This api is the thread handler for Tlshim Data packet processing. + * + * Return: thread exit code + */ +static int cds_ol_rx_thread(void *arg) +{ + p_cds_sched_context pSchedContext = (p_cds_sched_context) arg; + bool shutdown = false; + int status; + +#ifdef RX_THREAD_PRIORITY + struct sched_param scheduler_params = {0}; + + scheduler_params.sched_priority = 1; + sched_setscheduler(current, SCHED_FIFO, &scheduler_params); +#else + set_user_nice(current, -1); +#endif + + qdf_set_wake_up_idle(true); + + complete(&pSchedContext->ol_rx_start_event); + + while (!shutdown) { + status = + wait_event_interruptible(pSchedContext->ol_rx_wait_queue, + test_bit(RX_POST_EVENT, + &pSchedContext->ol_rx_event_flag) + || test_bit(RX_SUSPEND_EVENT, + &pSchedContext->ol_rx_event_flag)); + if (status == -ERESTARTSYS) + break; + + clear_bit(RX_POST_EVENT, &pSchedContext->ol_rx_event_flag); + while (true) { + if (test_bit(RX_SHUTDOWN_EVENT, + &pSchedContext->ol_rx_event_flag)) { + clear_bit(RX_SHUTDOWN_EVENT, + &pSchedContext->ol_rx_event_flag); + if (test_bit(RX_SUSPEND_EVENT, + &pSchedContext->ol_rx_event_flag)) { + clear_bit(RX_SUSPEND_EVENT, + &pSchedContext->ol_rx_event_flag); + complete + (&pSchedContext->ol_suspend_rx_event); + } + cds_debug("Shutting down OL RX Thread"); + shutdown = true; + break; + } + cds_rx_from_queue(pSchedContext); + + if (test_bit(RX_SUSPEND_EVENT, + &pSchedContext->ol_rx_event_flag)) { + clear_bit(RX_SUSPEND_EVENT, + &pSchedContext->ol_rx_event_flag); + spin_lock(&pSchedContext->ol_rx_thread_lock); + INIT_COMPLETION + (pSchedContext->ol_resume_rx_event); + complete(&pSchedContext->ol_suspend_rx_event); + spin_unlock(&pSchedContext->ol_rx_thread_lock); + wait_for_completion_interruptible + (&pSchedContext->ol_resume_rx_event); + } + break; + } + } + + cds_debug("Exiting CDS OL rx thread"); + complete_and_exit(&pSchedContext->ol_rx_shutdown, 0); + + return 0; +} + +void cds_resume_rx_thread(void) +{ + p_cds_sched_context cds_sched_context; + + cds_sched_context = get_cds_sched_ctxt(); + if (!cds_sched_context) { + cds_err("cds_sched_context is NULL"); + return; + } + + complete(&cds_sched_context->ol_resume_rx_event); +} +#endif + +/** + * cds_sched_close() - close the cds scheduler + * + * This api closes the CDS Scheduler upon successful closing: + * - All the message queues are flushed + * - The Main Controller thread is closed + * - The Tx thread is closed + * + * + * Return: qdf status + */ +QDF_STATUS cds_sched_close(void) +{ + cds_debug("invoked"); + + if (!gp_cds_sched_context) { + cds_err("!gp_cds_sched_context"); + return QDF_STATUS_E_FAILURE; + } + + cds_close_rx_thread(); + + cds_close_mon_thread(); + + gp_cds_sched_context = NULL; + return QDF_STATUS_SUCCESS; +} /* cds_sched_close() */ + +/** + * get_cds_sched_ctxt() - get cds scheduler context + * + * Return: none + */ +p_cds_sched_context get_cds_sched_ctxt(void) +{ + /* Make sure that Vos Scheduler context has been initialized */ + if (!gp_cds_sched_context) + cds_err("!gp_cds_sched_context"); + + return gp_cds_sched_context; +} + +/** + * cds_ssr_protect_init() - initialize ssr protection debug functionality + * + * Return: + * void + */ +void cds_ssr_protect_init(void) +{ + spin_lock_init(&ssr_protect_lock); + INIT_LIST_HEAD(&shutdown_notifier_head); +} + +/** + * cds_shutdown_notifier_register() - Register for shutdown notification + * @cb : Call back to be called + * @priv : Private pointer to be passed back to call back + * + * During driver remove or shutdown (recovery), external threads might be stuck + * waiting on some event from firmware at lower layers. Remove or shutdown can't + * proceed till the thread completes to avoid any race condition. Call backs can + * be registered here to get early notification of remove or shutdown so that + * waiting thread can be unblocked and hence remove or shutdown can proceed + * further as waiting there may not make sense when FW may already have been + * down. + * + * This is intended for early notification of remove() or shutdown() only so + * that lower layers can take care of stuffs like external waiting thread. + * + * Return: CDS status + */ +QDF_STATUS cds_shutdown_notifier_register(void (*cb)(void *priv), void *priv) +{ + struct shutdown_notifier *notifier; + unsigned long irq_flags; + + notifier = qdf_mem_malloc(sizeof(*notifier)); + + if (!notifier) + return QDF_STATUS_E_NOMEM; + + /* + * This logic can be simpilfied if there is separate state maintained + * for shutdown and reinit. Right now there is only recovery in progress + * state and it doesn't help to check against it as during reinit some + * of the modules may need to register the call backs. + * For now this logic added to avoid notifier registration happen while + * this function is trying to call the call back with the notification. + */ + spin_lock_irqsave(&ssr_protect_lock, irq_flags); + if (notifier_state == NOTIFIER_STATE_NOTIFYING) { + spin_unlock_irqrestore(&ssr_protect_lock, irq_flags); + qdf_mem_free(notifier); + return -EINVAL; + } + + notifier->cb = cb; + notifier->priv = priv; + + list_add_tail(¬ifier->list, &shutdown_notifier_head); + spin_unlock_irqrestore(&ssr_protect_lock, irq_flags); + + return 0; +} + +/** + * cds_shutdown_notifier_purge() - Purge all the notifiers + * + * Shutdown notifiers are added to provide the early notification of remove or + * shutdown being initiated. Adding this API to purge all the registered call + * backs as they are not useful any more while all the lower layers are being + * shutdown. + * + * Return: None + */ +void cds_shutdown_notifier_purge(void) +{ + struct shutdown_notifier *notifier, *temp; + unsigned long irq_flags; + + spin_lock_irqsave(&ssr_protect_lock, irq_flags); + list_for_each_entry_safe(notifier, temp, + &shutdown_notifier_head, list) { + list_del(¬ifier->list); + spin_unlock_irqrestore(&ssr_protect_lock, irq_flags); + + qdf_mem_free(notifier); + + spin_lock_irqsave(&ssr_protect_lock, irq_flags); + } + + spin_unlock_irqrestore(&ssr_protect_lock, irq_flags); +} + +/** + * cds_shutdown_notifier_call() - Call shutdown notifier call back + * + * Call registered shutdown notifier call back to indicate about remove or + * shutdown. + */ +void cds_shutdown_notifier_call(void) +{ + struct shutdown_notifier *notifier; + unsigned long irq_flags; + + spin_lock_irqsave(&ssr_protect_lock, irq_flags); + notifier_state = NOTIFIER_STATE_NOTIFYING; + + list_for_each_entry(notifier, &shutdown_notifier_head, list) { + spin_unlock_irqrestore(&ssr_protect_lock, irq_flags); + + notifier->cb(notifier->priv); + + spin_lock_irqsave(&ssr_protect_lock, irq_flags); + } + + notifier_state = NOTIFIER_STATE_NONE; + spin_unlock_irqrestore(&ssr_protect_lock, irq_flags); +} + +/** + * cds_get_gfp_flags(): get GFP flags + * + * Based on the scheduled context, return GFP flags + * Return: gfp flags + */ +int cds_get_gfp_flags(void) +{ + int flags = GFP_KERNEL; + + if (in_interrupt() || in_atomic() || irqs_disabled()) + flags = GFP_ATOMIC; + + return flags; +} + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * cds_free_ol_mon_pkt_freeq() - free cds buffer free queue + * @pSchedContext - pointer to the global CDS Sched Context + * + * This API does mem free of the buffers available in free cds buffer + * queue which is used for mon Data processing. + * + * Return: none + */ +void cds_free_ol_mon_pkt_freeq(p_cds_sched_context pschedcontext) +{ + struct cds_ol_mon_pkt *pkt; + + if (!cds_is_pktcapture_enabled()) + return; + + spin_lock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + while (!list_empty(&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq)) { + pkt = list_entry((&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq)->next, + typeof(*pkt), list); + list_del(&pkt->list); + spin_unlock_bh(&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + qdf_mem_free(pkt); + spin_lock_bh(&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + } + spin_unlock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); +} + +/** + * cds_alloc_ol_mon_pkt_freeq() - Function to allocate free buffer queue + * @pSchedContext - pointer to the global CDS Sched Context + * + * This API allocates CDS_MAX_OL_MON_PKT number of cds message buffers + * which are used for mon data processing. + * + * Return: status of memory allocation + */ +static QDF_STATUS cds_alloc_ol_mon_pkt_freeq(p_cds_sched_context pschedcontext) +{ + struct cds_ol_mon_pkt *pkt, *tmp; + int i; + + for (i = 0; i < CDS_MAX_OL_MON_PKT; i++) { + pkt = qdf_mem_malloc(sizeof(*pkt)); + if (!pkt) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + "%s Vos packet allocation for ol mon thread failed", + __func__); + goto free; + } + spin_lock_bh(&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + list_add_tail(&pkt->list, &pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq); + spin_unlock_bh(&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + } + + return QDF_STATUS_SUCCESS; + +free: + spin_lock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + list_for_each_entry_safe(pkt, tmp, + &pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq, + list) { + list_del(&pkt->list); + spin_unlock_bh(&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + qdf_mem_free(pkt); + spin_lock_bh(&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + } + spin_unlock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + return QDF_STATUS_E_NOMEM; +} + +/** + * cds_free_ol_mon_pkt() - api to release cds message to the freeq + * This api returns the cds message used for mon data to the free queue + * @pSchedContext: Pointer to the global CDS Sched Context + * @pkt: CDS message buffer to be returned to free queue. + * + * Return: none + */ +void +cds_free_ol_mon_pkt(p_cds_sched_context pschedcontext, + struct cds_ol_mon_pkt *pkt) +{ + memset(pkt, 0, sizeof(*pkt)); + spin_lock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + list_add_tail(&pkt->list, + &pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq); + spin_unlock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); +} + +/** + * cds_alloc_ol_mon_pkt() - API to return next available cds message + * @pSchedContext: Pointer to the global CDS Sched Context + * + * This api returns next available cds message buffer used for mon data + * processing + * + * Return: Pointer to cds message buffer + */ +struct cds_ol_mon_pkt *cds_alloc_ol_mon_pkt(p_cds_sched_context pschedcontext) +{ + struct cds_ol_mon_pkt *pkt; + + spin_lock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + if (list_empty(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq)) { + spin_unlock_bh(&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + return NULL; + } + pkt = list_first_entry(&pschedcontext-> + sched_mon_ctx.cds_ol_mon_pkt_freeq, + struct cds_ol_mon_pkt, list); + list_del(&pkt->list); + spin_unlock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + return pkt; +} + +/** + * cds_indicate_monpkt() - indicate mon data packet + * @Arg: Pointer to the global CDS Sched Context + * @pkt: CDS data message buffer + * + * This api enqueues the mon packet into ol_mon_thread_queue and notifies + * cds_ol_mon_thread() + * + * Return: none + */ +void +cds_indicate_monpkt(p_cds_sched_context pschedcontext, + struct cds_ol_mon_pkt *pkt) +{ + spin_lock_bh(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); + list_add_tail(&pkt->list, &pschedcontext-> + sched_mon_ctx.ol_mon_thread_queue); + spin_unlock_bh(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); + set_bit(RX_POST_EVENT, &pschedcontext->sched_mon_ctx.ol_mon_event_flag); + wake_up_interruptible(&pschedcontext->sched_mon_ctx.ol_mon_wait_queue); +} + +/** + * cds_wakeup_mon_thread() - wakeup mon thread + * @Arg: Pointer to the global CDS Sched Context + * + * This api wake up cds_ol_mon_thread() to process pkt + * + * Return: none + */ +void +cds_wakeup_mon_thread(p_cds_sched_context pschedcontext) +{ + set_bit(RX_POST_EVENT, &pschedcontext->sched_mon_ctx.ol_mon_event_flag); + wake_up_interruptible(&pschedcontext->sched_mon_ctx.ol_mon_wait_queue); +} + +/** + * cds_close_mon_thread() - close the Tlshim Rx thread + * + * This api closes the Tlshim Rx thread: + * + * Return: qdf status + */ +QDF_STATUS cds_close_mon_thread(void) +{ + if (!cds_is_pktcapture_enabled()) + return QDF_STATUS_SUCCESS; + + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: invoked", __func__); + + if (!gp_cds_sched_context) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + "%s: gp_cds_sched_context == NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!gp_cds_sched_context->sched_mon_ctx.ol_mon_thread) + return QDF_STATUS_SUCCESS; + + /* Shut down Tlshim Rx thread */ + set_bit(RX_SHUTDOWN_EVENT, + &gp_cds_sched_context->sched_mon_ctx.ol_mon_event_flag); + set_bit(RX_POST_EVENT, + &gp_cds_sched_context->sched_mon_ctx.ol_mon_event_flag); + wake_up_interruptible(&gp_cds_sched_context-> + sched_mon_ctx.ol_mon_wait_queue); + wait_for_completion(&gp_cds_sched_context-> + sched_mon_ctx.ol_mon_shutdown); + gp_cds_sched_context->sched_mon_ctx.ol_mon_thread = NULL; + cds_drop_monpkt(gp_cds_sched_context); + cds_free_ol_mon_pkt_freeq(gp_cds_sched_context); + + return QDF_STATUS_SUCCESS; +} /* cds_close_mon_thread */ + +/** + * cds_open_mon_thread() - open the Tlshim Rx thread + * + * This api open the Tlshim Rx thread: + * + * Return: qdf status + */ +QDF_STATUS cds_open_mon_thread(p_cds_sched_context pschedcontext) +{ + if (!cds_is_pktcapture_enabled()) + return QDF_STATUS_SUCCESS; + + pschedcontext->sched_mon_ctx.ol_mon_thread = kthread_create( + cds_ol_mon_thread, + pschedcontext, + "cds_ol_mon_thread"); + if (IS_ERR(pschedcontext->sched_mon_ctx.ol_mon_thread)) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL, + "%s: Could not Create CDS OL MON Thread", + __func__); + return QDF_STATUS_E_FAILURE; + } + wake_up_process(pschedcontext->sched_mon_ctx.ol_mon_thread); + cds_debug("CDS OL MON thread Created"); + + wait_for_completion_interruptible( + &pschedcontext->sched_mon_ctx.ol_mon_start_event); + cds_debug("CDS OL MON Thread has started"); + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_drop_monpkt() - api to drop pending mon packets for a sta + * @pschedcontext: Pointer to the global CDS Sched Context + * + * This api drops all queued packets for a station. + * + * Return: none + */ +void cds_drop_monpkt(p_cds_sched_context pschedcontext) +{ + struct list_head local_list; + struct cds_ol_mon_pkt *pkt, *tmp; + qdf_nbuf_t buf, next_buf; + + INIT_LIST_HEAD(&local_list); + spin_lock_bh(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); + if (list_empty(&pschedcontext->sched_mon_ctx.ol_mon_thread_queue)) { + spin_unlock_bh(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); + return; + } + list_for_each_entry_safe(pkt, tmp, + &pschedcontext-> + sched_mon_ctx.ol_mon_thread_queue, + list) + list_move_tail(&pkt->list, &local_list); + + spin_unlock_bh(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); + + list_for_each_entry_safe(pkt, tmp, &local_list, list) { + list_del(&pkt->list); + buf = pkt->monpkt; + while (buf) { + next_buf = qdf_nbuf_queue_next(buf); + qdf_nbuf_free(buf); + buf = next_buf; + } + cds_free_ol_mon_pkt(pschedcontext, pkt); + } +} + +/** + * cds_mon_from_queue() - function to process pending mon packets + * @pschedcontext: Pointer to the global CDS Sched Context + * + * This api traverses the pending buffer list and calling the callback. + * This callback would essentially send the packet to HDD. + * + * Return: none + */ +static void cds_mon_from_queue(p_cds_sched_context pschedcontext) +{ + struct cds_ol_mon_pkt *pkt; + uint8_t vdev_id; + uint8_t tid; + + spin_lock_bh(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); + while (!list_empty(&pschedcontext->sched_mon_ctx.ol_mon_thread_queue)) { + pkt = list_first_entry(&pschedcontext-> + sched_mon_ctx.ol_mon_thread_queue, + struct cds_ol_mon_pkt, list); + list_del(&pkt->list); + spin_unlock_bh(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); + vdev_id = pkt->vdev_id; + tid = pkt->tid; + pkt->callback(pkt->context, pkt->monpkt, vdev_id, + tid, pkt->status, pkt->pkt_format); + cds_free_ol_mon_pkt(pschedcontext, pkt); + spin_lock_bh(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); + } + spin_unlock_bh(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); +} + +/** + * cds_ol_mon_thread() - cds main tlshim mon thread + * @Arg: pointer to the global CDS Sched Context + * + * This api is the thread handler for mon Data packet processing. + * + * Return: thread exit code + */ +static int cds_ol_mon_thread(void *arg) +{ + p_cds_sched_context pschedcontext = (p_cds_sched_context)arg; + unsigned long pref_cpu = 0; + bool shutdown = false; + int status, i; + + if (!arg) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + "%s: Bad Args passed", __func__); + return 0; + } + + set_user_nice(current, -1); +#ifdef MSM_PLATFORM + set_wake_up_idle(true); +#endif + + /** + * Find the available cpu core other than cpu 0 and + * bind the thread + */ + for_each_online_cpu(i) { + if (i == 0) + continue; + pref_cpu = i; + break; + } + + cds_set_mon_cpus_allowed_ptr(current, pref_cpu); + + complete(&pschedcontext->sched_mon_ctx.ol_mon_start_event); + + while (!shutdown) { + status = + wait_event_interruptible( + pschedcontext->sched_mon_ctx.ol_mon_wait_queue, + test_bit(RX_POST_EVENT, + &pschedcontext-> + sched_mon_ctx.ol_mon_event_flag) || + test_bit(RX_SUSPEND_EVENT, + &pschedcontext-> + sched_mon_ctx.ol_mon_event_flag)); + if (status == -ERESTARTSYS) + break; + + clear_bit(RX_POST_EVENT, + &pschedcontext->sched_mon_ctx.ol_mon_event_flag); + while (true) { + if (test_bit(RX_SHUTDOWN_EVENT, + &pschedcontext-> + sched_mon_ctx.ol_mon_event_flag)) { + clear_bit(RX_SHUTDOWN_EVENT, + &pschedcontext-> + sched_mon_ctx.ol_mon_event_flag); + if (test_bit( + RX_SUSPEND_EVENT, + &pschedcontext-> + sched_mon_ctx.ol_mon_event_flag)) { + clear_bit( + RX_SUSPEND_EVENT, + &pschedcontext-> + sched_mon_ctx.ol_mon_event_flag); + complete + (&pschedcontext-> + sched_mon_ctx.ol_suspend_mon_event); + } + QDF_TRACE(QDF_MODULE_ID_QDF, + QDF_TRACE_LEVEL_INFO, + "%s: Shutting down OL MON Thread", + __func__); + shutdown = true; + break; + } + cds_mon_from_queue(pschedcontext); + + if (test_bit(RX_SUSPEND_EVENT, + &pschedcontext-> + sched_mon_ctx.ol_mon_event_flag)) { + clear_bit(RX_SUSPEND_EVENT, + &pschedcontext-> + sched_mon_ctx.ol_mon_event_flag); + spin_lock(&pschedcontext-> + sched_mon_ctx.ol_mon_thread_lock); + INIT_COMPLETION + (pschedcontext-> + sched_mon_ctx.ol_resume_mon_event); + complete(&pschedcontext-> + sched_mon_ctx.ol_suspend_mon_event); + spin_unlock(&pschedcontext-> + sched_mon_ctx.ol_mon_thread_lock); + wait_for_completion_interruptible + (&pschedcontext-> + sched_mon_ctx.ol_resume_mon_event); + } + break; + } + } + + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_DEBUG, + "%s: Exiting CDS OL mon thread", __func__); + complete_and_exit(&pschedcontext->sched_mon_ctx.ol_mon_shutdown, 0); + + return 0; +} + +void cds_resume_mon_thread(void) +{ + p_cds_sched_context cds_sched_context; + + cds_sched_context = get_cds_sched_ctxt(); + if (!cds_sched_context) { + cds_err("cds_sched_context is NULL"); + return; + } + + complete(&cds_sched_context->sched_mon_ctx.ol_resume_mon_event); +} + +QDF_STATUS +cds_alloc_mon_thread(p_cds_sched_context pschedcontext) +{ + if (!cds_is_pktcapture_enabled()) + return QDF_STATUS_SUCCESS; + + spin_lock_init(&pschedcontext->sched_mon_ctx.ol_mon_thread_lock); + init_waitqueue_head(&pschedcontext->sched_mon_ctx.ol_mon_wait_queue); + init_completion(&pschedcontext->sched_mon_ctx.ol_mon_start_event); + init_completion(&pschedcontext->sched_mon_ctx.ol_suspend_mon_event); + init_completion(&pschedcontext->sched_mon_ctx.ol_resume_mon_event); + init_completion(&pschedcontext->sched_mon_ctx.ol_mon_shutdown); + pschedcontext->sched_mon_ctx.ol_mon_event_flag = 0; + spin_lock_init(&pschedcontext->sched_mon_ctx.ol_mon_queue_lock); + spin_lock_init(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + INIT_LIST_HEAD(&pschedcontext->sched_mon_ctx.ol_mon_thread_queue); + spin_lock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + INIT_LIST_HEAD(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq); + spin_unlock_bh(&pschedcontext->sched_mon_ctx.cds_ol_mon_pkt_freeq_lock); + + return cds_alloc_ol_mon_pkt_freeq(pschedcontext); +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ diff --git a/drivers/staging/qcacld-3.0/core/cds/src/cds_utils.c b/drivers/staging/qcacld-3.0/core/cds/src/cds_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..315e70f416bf0dcf1a60a027ffaec5c4db93480d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/src/cds_utils.c @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*============================================================================ + FILE: cds_utils.c + + OVERVIEW: This source file contains definitions for CDS crypto APIs + The four APIs mentioned in this file are used for + initializing, and de-initializing a crypto context, and + obtaining truly random data (for keys), as well as + SHA1 HMAC, and AES encrypt and decrypt routines. + + The routines include: + cds_crypto_init() - Initializes Crypto module + cds_crypto_deinit() - De-initializes Crypto module + cds_rand_get_bytes() - Generates random byte + + DEPENDENCIES: + ============================================================================*/ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ + +#include "qdf_trace.h" +#include "cds_utils.h" +#include "qdf_mem.h" +#include "cds_crypto.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cds_ieee80211_common.h" +#include + +/*---------------------------------------------------------------------------- + * Preprocessor Definitions and Constants + * -------------------------------------------------------------------------*/ +#define AAD_LEN 20 +#define CMAC_IPN_LEN 6 +#define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */ +#define GMAC_NONCE_LEN 12 + +/*---------------------------------------------------------------------------- + * Type Declarations + * -------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------- + * Global Data Definitions + * -------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------- + * Static Variable Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + Function Definitions and Documentation + * -------------------------------------------------------------------------*/ +#ifdef WLAN_FEATURE_11W +static inline void xor_128(const u8 *a, const u8 *b, u8 *out) +{ + u8 i; + + for (i = 0; i < AES_BLOCK_SIZE; i++) + out[i] = a[i] ^ b[i]; +} + +static inline void leftshift_onebit(const u8 *input, u8 *output) +{ + int i, overflow = 0; + + for (i = (AES_BLOCK_SIZE - 1); i >= 0; i--) { + output[i] = input[i] << 1; + output[i] |= overflow; + overflow = (input[i] & 0x80) ? 1 : 0; + } + return; +} + +static void generate_subkey(struct crypto_cipher *tfm, u8 *k1, u8 *k2) +{ + u8 l[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; + u8 const_rb[AES_BLOCK_SIZE] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 + }; + u8 const_zero[AES_BLOCK_SIZE] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + crypto_cipher_encrypt_one(tfm, l, const_zero); + + if ((l[0] & 0x80) == 0) { /* If MSB(l) = 0, then k1 = l << 1 */ + leftshift_onebit(l, k1); + } else { /* Else k1 = ( l << 1 ) (+) Rb */ + leftshift_onebit(l, tmp); + xor_128(tmp, const_rb, k1); + } + + if ((k1[0] & 0x80) == 0) { + leftshift_onebit(k1, k2); + } else { + leftshift_onebit(k1, tmp); + xor_128(tmp, const_rb, k2); + } +} + +static inline void padding(u8 *lastb, u8 *pad, u16 length) +{ + u8 j; + + /* original last block */ + for (j = 0; j < AES_BLOCK_SIZE; j++) { + if (j < length) + pad[j] = lastb[j]; + else if (j == length) + pad[j] = 0x80; + else + pad[j] = 0x00; + } +} + +static void cds_cmac_calc_mic(struct crypto_cipher *tfm, + u8 *m, u16 length, u8 *mac) +{ + u8 x[AES_BLOCK_SIZE], y[AES_BLOCK_SIZE]; + u8 m_last[AES_BLOCK_SIZE], padded[AES_BLOCK_SIZE]; + u8 k1[AES_KEYSIZE_128], k2[AES_KEYSIZE_128]; + int cmpBlk; + int i, nBlocks = (length + 15) / AES_BLOCK_SIZE; + + generate_subkey(tfm, k1, k2); + + if (nBlocks == 0) { + nBlocks = 1; + cmpBlk = 0; + } else { + cmpBlk = ((length % AES_BLOCK_SIZE) == 0) ? 1 : 0; + } + + if (cmpBlk) { /* Last block is complete block */ + xor_128(&m[AES_BLOCK_SIZE * (nBlocks - 1)], k1, m_last); + } else { /* Last block is not complete block */ + padding(&m[AES_BLOCK_SIZE * (nBlocks - 1)], padded, + length % AES_BLOCK_SIZE); + xor_128(padded, k2, m_last); + } + + for (i = 0; i < AES_BLOCK_SIZE; i++) + x[i] = 0; + + for (i = 0; i < (nBlocks - 1); i++) { + xor_128(x, &m[AES_BLOCK_SIZE * i], y); /* y = Mi (+) x */ + crypto_cipher_encrypt_one(tfm, x, y); /* x = AES-128(KEY, y) */ + } + + xor_128(x, m_last, y); + crypto_cipher_encrypt_one(tfm, x, y); + + memcpy(mac, x, CMAC_TLEN); +} +#endif + +#ifdef WLAN_FEATURE_11W +uint8_t cds_get_mmie_size(void) +{ + return sizeof(struct ieee80211_mmie); +} + +/*-------------------------------------------------------------------------- + + \brief cds_increase_seq() - Increase the IPN aka Sequence number by one unit + + The cds_increase_seq() function increases the IPN by one unit. + + \param ipn - pointer to the IPN aka Sequence number [6 bytes] + + --------------------------------------------------------------------------*/ +static void cds_increase_seq(uint8_t *ipn) +{ + uint64_t value = 0; + + if (ipn) { + value = (0xffffffffffff) & (*((uint64_t *) ipn)); + value = value + 1; + qdf_mem_copy(ipn, &value, IEEE80211_MMIE_IPNLEN); + } +} + +/*-------------------------------------------------------------------------- + + \brief cds_attach_mmie() - attches the complete MMIE at the end of frame + + The cds_attach_mmie() calculates the entire MMIE and attaches at the end + of Broadcast/Multicast robust management frames. + + \param igtk - pointer group key which will be used to calculate + the 8 byte MIC. + \param ipn - pointer ipn, it is also known as sequence number + \param key_id - key identication number + \param frm - pointer to the start of the frame. + \param efrm - pointer to the end of the frame. + \param frmLen - size of the entire frame. + + \return - this function will return true on success and false on + failure. + + --------------------------------------------------------------------------*/ + +bool +cds_attach_mmie(uint8_t *igtk, uint8_t *ipn, uint16_t key_id, + uint8_t *frm, uint8_t *efrm, uint16_t frmLen) +{ + struct ieee80211_mmie *mmie; + struct ieee80211_frame *wh; + uint8_t aad[AAD_LEN], mic[CMAC_TLEN], *input = NULL; + uint8_t previous_ipn[IEEE80211_MMIE_IPNLEN] = { 0 }; + uint16_t nBytes = 0; + int ret = 0; + struct crypto_cipher *tfm; + + /* This is how received frame look like + * + * <------------frmLen----------------------------> + * + * +---------------+----------------------+-------+ + * | 802.11 HEADER | Management framebody | MMIE | + * +---------------+----------------------+-------+ + * ^ + * | + * efrm + * This is how MMIE from above frame look like + * + * + * <------------ 18 Bytes-----------------------------> + * +--------+---------+---------+-----------+---------+ + * |Element | Length | Key id | IPN | MIC | + * | id | | | | | + * +--------+---------+---------+-----------+---------+ + * Octet 1 1 2 6 8 + * + */ + + /* Check if frame is invalid length */ + if (((efrm - frm) != frmLen) || (frmLen < sizeof(*wh))) { + cds_err("Invalid frame length"); + return false; + } + mmie = (struct ieee80211_mmie *)(efrm - sizeof(*mmie)); + + /* Copy Element id */ + mmie->element_id = WLAN_ELEMID_MMIE; + + /* Copy Length */ + mmie->length = sizeof(*mmie) - 2; + + /* Copy Key id */ + mmie->key_id = key_id; + + /* + * In case of error, revert back to original IPN + * to do that copy the original IPN into previous_ipn + */ + qdf_mem_copy(&previous_ipn[0], ipn, IEEE80211_MMIE_IPNLEN); + cds_increase_seq(ipn); + qdf_mem_copy(mmie->sequence_number, ipn, IEEE80211_MMIE_IPNLEN); + + /* + * Calculate MIC and then copy + */ + tfm = cds_crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + tfm = NULL; + cds_err("crypto_alloc_cipher failed (%d)", ret); + goto err_tfm; + } + + ret = crypto_cipher_setkey(tfm, igtk, AES_KEYSIZE_128); + if (ret) { + cds_err("crypto_cipher_setkey failed (%d)", ret); + goto err_tfm; + } + + /* Construct AAD */ + wh = (struct ieee80211_frame *)frm; + + /* Generate BIP AAD: FC(masked) || A1 || A2 || A3 */ + + /* FC type/subtype */ + aad[0] = wh->i_fc[0]; + /* Mask FC Retry, PwrMgt, MoreData flags to zero */ + aad[1] = wh->i_fc[1] & ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_PWR_MGT | + IEEE80211_FC1_MORE_DATA); + /* A1 || A2 || A3 */ + qdf_mem_copy(aad + 2, wh->i_addr_all, 3 * QDF_MAC_ADDR_SIZE); + + /* MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64) */ + nBytes = AAD_LEN + (frmLen - sizeof(struct ieee80211_frame)); + input = (uint8_t *) qdf_mem_malloc(nBytes); + if (!input) { + cds_err("Memory allocation failed"); + ret = QDF_STATUS_E_NOMEM; + goto err_tfm; + } + + /* + * Copy the AAD, Management frame body, and + * MMIE with 8 bit MIC zeroed out + */ + qdf_mem_copy(input, aad, AAD_LEN); + /* Copy Management Frame Body and MMIE without MIC */ + qdf_mem_copy(input + AAD_LEN, + (uint8_t *) (efrm - + (frmLen - sizeof(struct ieee80211_frame))), + nBytes - AAD_LEN - CMAC_TLEN); + + cds_cmac_calc_mic(tfm, input, nBytes, mic); + qdf_mem_free(input); + + cds_debug("CMAC(T)= %02X %02X %02X %02X %02X %02X %02X %02X", + mic[0], mic[1], mic[2], mic[3], + mic[4], mic[5], mic[6], mic[7]); + qdf_mem_copy(mmie->mic, mic, IEEE80211_MMIE_MICLEN); + +err_tfm: + if (ret) { + qdf_mem_copy(ipn, previous_ipn, IEEE80211_MMIE_IPNLEN); + } + + if (tfm) + cds_crypto_free_cipher(tfm); + return !ret ? true : false; +} + +bool +cds_is_mmie_valid(uint8_t *igtk, uint8_t *ipn, uint8_t *frm, uint8_t *efrm) +{ + struct ieee80211_mmie *mmie; + struct ieee80211_frame *wh; + uint8_t *rx_ipn, aad[AAD_LEN], mic[CMAC_TLEN], *input; + uint16_t nBytes = 0; + int ret = 0; + struct crypto_cipher *tfm; + + /* Check if frame is invalid length */ + if ((efrm < frm) || ((efrm - frm) < sizeof(*wh))) { + cds_err("Invalid frame length"); + return false; + } + + mmie = (struct ieee80211_mmie *)(efrm - sizeof(*mmie)); + + /* Check Element ID */ + if ((mmie->element_id != WLAN_ELEMID_MMIE) || + (mmie->length != (sizeof(*mmie) - 2))) { + cds_err("IE is not Mgmt MIC IE or Invalid length"); + /* IE is not Mgmt MIC IE or invalid length */ + return false; + } + + /* Validate IPN */ + rx_ipn = mmie->sequence_number; + if (qdf_mem_cmp(rx_ipn, ipn, CMAC_IPN_LEN) <= 0) { + /* Replay error */ + cds_err("Replay error mmie ipn %02X %02X %02X %02X %02X %02X" + " drvr ipn %02X %02X %02X %02X %02X %02X", + rx_ipn[0], rx_ipn[1], rx_ipn[2], rx_ipn[3], rx_ipn[4], + rx_ipn[5], ipn[0], ipn[1], ipn[2], ipn[3], ipn[4], + ipn[5]); + return false; + } + tfm = cds_crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + tfm = NULL; + cds_err("crypto_alloc_cipher failed (%d)", ret); + goto err_tfm; + } + + ret = crypto_cipher_setkey(tfm, igtk, AES_KEYSIZE_128); + if (ret) { + cds_err("crypto_cipher_setkey failed (%d)", ret); + goto err_tfm; + } + + /* Construct AAD */ + wh = (struct ieee80211_frame *)frm; + + /* Generate BIP AAD: FC(masked) || A1 || A2 || A3 */ + + /* FC type/subtype */ + aad[0] = wh->i_fc[0]; + /* Mask FC Retry, PwrMgt, MoreData flags to zero */ + aad[1] = wh->i_fc[1] & ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_PWR_MGT | + IEEE80211_FC1_MORE_DATA); + /* A1 || A2 || A3 */ + qdf_mem_copy(aad + 2, wh->i_addr_all, 3 * QDF_MAC_ADDR_SIZE); + + /* MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64) */ + nBytes = AAD_LEN + (efrm - (uint8_t *) (wh + 1)); + input = (uint8_t *) qdf_mem_malloc(nBytes); + if (!input) { + cds_err("Memory allocation failed"); + ret = QDF_STATUS_E_NOMEM; + goto err_tfm; + } + + /* Copy the AAD, MMIE with 8 bit MIC zeroed out */ + qdf_mem_copy(input, aad, AAD_LEN); + qdf_mem_copy(input + AAD_LEN, (uint8_t *) (wh + 1), + nBytes - AAD_LEN - CMAC_TLEN); + + cds_cmac_calc_mic(tfm, input, nBytes, mic); + qdf_mem_free(input); + + cds_err("CMAC(T)= %02X %02X %02X %02X %02X %02X %02X %02X", + mic[0], mic[1], mic[2], mic[3], + mic[4], mic[5], mic[6], mic[7]); + + if (qdf_mem_cmp(mic, mmie->mic, CMAC_TLEN) != 0) { + /* MMIE MIC mismatch */ + cds_err("BC/MC MGMT frame MMIE MIC check Failed" + " rmic %02X %02X %02X %02X %02X %02X %02X %02X" + " cmic %02X %02X %02X %02X %02X %02X %02X %02X", + mmie->mic[0], mmie->mic[1], mmie->mic[2], + mmie->mic[3], mmie->mic[4], mmie->mic[5], + mmie->mic[6], mmie->mic[7], mic[0], mic[1], mic[2], + mic[3], mic[4], mic[5], mic[6], mic[7]); + return false; + } + + /* Update IPN */ + qdf_mem_copy(ipn, rx_ipn, CMAC_IPN_LEN); + +err_tfm: + if (tfm) + cds_crypto_free_cipher(tfm); + + return !ret ? true : false; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +uint8_t cds_get_gmac_mmie_size(void) +{ + return sizeof(struct ieee80211_mmie_16); +} +#else +uint8_t cds_get_gmac_mmie_size(void) +{ + return 0; +} +#endif + +/** + * ipn_swap: Swaps ipn + * @d: destination pointer + * @s: source pointer + * + * Return: None + */ +static inline void ipn_swap(u8 *d, const u8 *s) +{ + *d++ = s[5]; + *d++ = s[4]; + *d++ = s[3]; + *d++ = s[2]; + *d++ = s[1]; + *d = s[0]; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +bool cds_is_gmac_mmie_valid(uint8_t *igtk, uint8_t *ipn, uint8_t *frm, + uint8_t *efrm, uint16_t key_length) +{ + struct ieee80211_mmie_16 *mmie; + struct ieee80211_frame *wh; + uint8_t rx_ipn[6], aad[AAD_LEN]; + uint8_t mic[IEEE80211_MMIE_GMAC_MICLEN] = {0}; + uint16_t data_len; + uint8_t gmac_nonce[GMAC_NONCE_LEN]; + uint8_t iv[AES_BLOCK_SIZE] = {0}; + int ret; + + /* Check if frame is invalid length */ + if ((efrm < frm) || ((efrm - frm) < sizeof(*wh))) { + cds_err("Invalid frame length"); + return false; + } + + mmie = (struct ieee80211_mmie_16 *)(efrm - sizeof(*mmie)); + + /* Check Element ID */ + if ((mmie->element_id != WLAN_ELEMID_MMIE) || + (mmie->length != (sizeof(*mmie) - 2))) { + cds_err("IE is not Mgmt MIC IE or Invalid length"); + /* IE is not Mgmt MIC IE or invalid length */ + return false; + } + + /* Validate IPN */ + ipn_swap(rx_ipn, mmie->sequence_number); + if (qdf_mem_cmp(rx_ipn, ipn, IEEE80211_MMIE_IPNLEN) <= 0) { + /* Replay error */ + cds_debug("Replay error mmie ipn %02X %02X %02X %02X %02X %02X" + " drvr ipn %02X %02X %02X %02X %02X %02X", + rx_ipn[0], rx_ipn[1], rx_ipn[2], rx_ipn[3], rx_ipn[4], + rx_ipn[5], ipn[0], ipn[1], ipn[2], ipn[3], ipn[4], + ipn[5]); + return false; + } + + /* Construct AAD */ + wh = (struct ieee80211_frame *)frm; + + /* Generate AAD: FC(masked) || A1 || A2 || A3 */ + /* FC type/subtype */ + aad[0] = wh->i_fc[0]; + /* Mask FC Retry, PwrMgt, MoreData flags to zero */ + aad[1] = wh->i_fc[1] & ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_PWR_MGT | + IEEE80211_FC1_MORE_DATA); + /* A1 || A2 || A3 */ + qdf_mem_copy(aad + 2, wh->i_addr_all, 3 * QDF_MAC_ADDR_SIZE); + + data_len = efrm - (uint8_t *) (wh + 1) - IEEE80211_MMIE_GMAC_MICLEN; + + /* IV */ + qdf_mem_copy(gmac_nonce, wh->i_addr2, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(gmac_nonce + QDF_MAC_ADDR_SIZE, rx_ipn, + IEEE80211_MMIE_IPNLEN); + qdf_mem_copy(iv, gmac_nonce, GMAC_NONCE_LEN); + iv[AES_BLOCK_SIZE - 1] = 0x01; + + ret = qdf_crypto_aes_gmac(igtk, key_length, iv, aad, + (uint8_t *) (wh + 1), data_len, mic); + if (ret) { + cds_err("qdf_crypto_aes_gmac failed %d", ret); + return false; + } + + if (qdf_mem_cmp(mic, mmie->mic, IEEE80211_MMIE_GMAC_MICLEN) != 0) { + /* MMIE MIC mismatch */ + cds_debug("BC/MC MGMT frame MMIE MIC check Failed" + " rmic %02X %02X %02X %02X %02X %02X %02X %02X" + " %02X %02X %02X %02X %02X %02X %02X %02X", + mmie->mic[0], mmie->mic[1], mmie->mic[2], + mmie->mic[3], mmie->mic[4], mmie->mic[5], + mmie->mic[6], mmie->mic[7], mmie->mic[8], + mmie->mic[9], mmie->mic[10], mmie->mic[11], + mmie->mic[12], mmie->mic[13], mmie->mic[14], + mmie->mic[15]); + return false; + } + + /* Update IPN */ + qdf_mem_copy(ipn, rx_ipn, IEEE80211_MMIE_IPNLEN); + + return true; +} +#else +bool cds_is_gmac_mmie_valid(uint8_t *igtk, uint8_t *ipn, uint8_t *frm, + uint8_t *efrm, uint16_t key_length) +{ + return false; +} +#endif + +#endif /* WLAN_FEATURE_11W */ + +uint32_t cds_chan_to_freq(uint8_t chan) +{ + if (chan < CDS_24_GHZ_CHANNEL_14) /* ch 0 - ch 13 */ + return CDS_24_GHZ_BASE_FREQ + chan * CDS_CHAN_SPACING_5MHZ; + else if (chan == CDS_24_GHZ_CHANNEL_14) /* ch 14 */ + return CDS_CHAN_14_FREQ; + else if (chan < CDS_24_GHZ_CHANNEL_27) /* ch 15 - ch 26 */ + return CDS_CHAN_15_FREQ + + (chan - CDS_24_GHZ_CHANNEL_15) * CDS_CHAN_SPACING_20MHZ; + else if (chan == CDS_5_GHZ_CHANNEL_170) + return CDS_CHAN_170_FREQ; + else + return CDS_5_GHZ_BASE_FREQ + chan * CDS_CHAN_SPACING_5MHZ; +} + +uint8_t cds_freq_to_chan(uint32_t freq) +{ + uint8_t chan; + + if (freq > CDS_24_GHZ_BASE_FREQ && freq < CDS_CHAN_14_FREQ) + chan = ((freq - CDS_24_GHZ_BASE_FREQ) / CDS_CHAN_SPACING_5MHZ); + else if (freq == CDS_CHAN_14_FREQ) + chan = CDS_24_GHZ_CHANNEL_14; + else if ((freq > CDS_24_GHZ_BASE_FREQ) && (freq < CDS_5_GHZ_BASE_FREQ)) + chan = (((freq - CDS_CHAN_15_FREQ) / CDS_CHAN_SPACING_20MHZ) + + CDS_24_GHZ_CHANNEL_15); + else + chan = (freq - CDS_5_GHZ_BASE_FREQ) / CDS_CHAN_SPACING_5MHZ; + return chan; +} + +enum cds_band_type cds_chan_to_band(uint32_t chan) +{ + if (chan <= CDS_24_GHZ_CHANNEL_14) + return CDS_BAND_2GHZ; + + return CDS_BAND_5GHZ; +} + +void cds_copy_hlp_info(struct qdf_mac_addr *input_dst_mac, + struct qdf_mac_addr *input_src_mac, + uint16_t input_hlp_data_len, + uint8_t *input_hlp_data, + struct qdf_mac_addr *output_dst_mac, + struct qdf_mac_addr *output_src_mac, + uint16_t *output_hlp_data_len, + uint8_t *output_hlp_data) +{ + if (!input_hlp_data_len) { + cds_debug("Input HLP data len zero\n"); + return; + } + + if (!input_dst_mac) { + cds_debug("HLP destination mac NULL"); + return; + } + + if (!input_src_mac) { + cds_debug("HLP source mac NULL"); + return; + } + qdf_copy_macaddr(output_dst_mac, input_dst_mac); + qdf_copy_macaddr(output_src_mac, input_src_mac); + *output_hlp_data_len = input_hlp_data_len; + qdf_mem_copy(output_hlp_data, input_hlp_data, input_hlp_data_len); +} diff --git a/drivers/staging/qcacld-3.0/core/cds/src/i_cds_packet.h b/drivers/staging/qcacld-3.0/core/cds/src/i_cds_packet.h new file mode 100644 index 0000000000000000000000000000000000000000..4bd19d1f553756d7a09f373c13660827a460bfea --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/cds/src/i_cds_packet.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014-2016, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__I_CDS_PACKET_H) +#define __I_CDS_PACKET_H + +/**========================================================================= + + \file i_cds_packet.h + + \brief Connectivity driver services network packet APIs + + Network Protocol packet/buffer internal include file + + ========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "qdf_types.h" +/** + * Rx Packet Struct + * Buffer for the packet received from WMA has pointers to 802.11 + * frame fields and additional information based on the type of frame. + * @frequency: Frequency + * @snr: Signal to noise ratio + * @rssi: Received signal strength indicator, normalized to -96 dBm as + * normal noise floor by adding -96 to snr. All the configured + * thresholds in the driver assume that noise floor is -96 dBm. + * @timestamp: System timestamp when frame was received. Set to jiffies. + * @mpdu_hdr_ptr: Pointer to beginning of 802.11 MPDU + * @mpdu_data_ptr: Pointer to beginning of payload + * @mpdu_len: Length of 802.11 MPDU + * @mpdu_hdr_len: Length of 802.11 MPDU header + * @mpdu_data_len: Length of 802.11 MPDU payload + * @offloadScanLearn: Bit set to 1 for beacons received during roaming scan + * @roamCandidateInd: Bit set to 1 when roaming candidate is found by fw + * @scan_src: Source of scan + * @dpuFeedback: DPU feedback for frame + * @session_id: PE session + * @tsf_delta: Delta between tsf in frame and local value of tsf + * @rssi_raw: rssi based on actual noise floor in hardware. + */ +typedef struct { + uint32_t frequency; + uint8_t snr; + uint32_t rssi; + uint32_t timestamp; + uint8_t *mpdu_hdr_ptr; + uint8_t *mpdu_data_ptr; + uint32_t mpdu_len; + uint32_t mpdu_hdr_len; + uint32_t mpdu_data_len; + uint8_t offloadScanLearn:1; + uint8_t roamCandidateInd:1; + uint8_t scan_src; + uint8_t dpuFeedback; + uint8_t session_id; + uint32_t tsf_delta; + uint32_t rssi_raw; +} t_packetmeta, *tp_packetmeta; + +/* implementation specific cds packet type */ +struct cds_pkt_t { + /* Packet Meta Information */ + t_packetmeta pkt_meta; + + /* Pointer to Packet */ + void *pkt_buf; +}; + +#endif /* !defined( __I_CDS_PACKET_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt.c b/drivers/staging/qcacld-3.0/core/dp/htt/htt.c new file mode 100644 index 0000000000000000000000000000000000000000..14e0174e679ce2aa61ef83ab169372e2f5589e19 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt.c @@ -0,0 +1,1019 @@ +/* + * Copyright (c) 2011, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file htt.c + * @brief Provide functions to create+init and destroy a HTT instance. + * @details + * This file contains functions for creating a HTT instance; initializing + * the HTT instance, e.g. by allocating a pool of HTT tx descriptors and + * connecting the HTT service with HTC; and deleting a HTT instance. + */ + +#include /* qdf_mem_malloc */ +#include /* qdf_device_t, qdf_print */ + +#include /* htt_tx_msdu_desc_t */ +#include +#include /* ol_tx_dowload_done_ll, etc. */ +#include + +#include +#include +#include +#include "hif.h" +#include +#include + +#define HTT_HTC_PKT_POOL_INIT_SIZE 100 /* enough for a large A-MPDU */ + +QDF_STATUS(*htt_h2t_rx_ring_cfg_msg)(struct htt_pdev_t *pdev); +QDF_STATUS(*htt_h2t_rx_ring_rfs_cfg_msg)(struct htt_pdev_t *pdev); + +#ifdef IPA_OFFLOAD +static QDF_STATUS htt_ipa_config(htt_pdev_handle pdev, QDF_STATUS status) +{ + if ((QDF_STATUS_SUCCESS == status) && + ol_cfg_ipa_uc_offload_enabled(pdev->ctrl_pdev)) + status = htt_h2t_ipa_uc_rsc_cfg_msg(pdev); + return status; +} + +#define HTT_IPA_CONFIG htt_ipa_config +#else +#define HTT_IPA_CONFIG(pdev, status) status /* no-op */ +#endif /* IPA_OFFLOAD */ + +struct htt_htc_pkt *htt_htc_pkt_alloc(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt_union *pkt = NULL; + + HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); + if (pdev->htt_htc_pkt_freelist) { + pkt = pdev->htt_htc_pkt_freelist; + pdev->htt_htc_pkt_freelist = pdev->htt_htc_pkt_freelist->u.next; + } + HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); + + if (!pkt) + pkt = qdf_mem_malloc(sizeof(*pkt)); + + if (!pkt) + return NULL; + + htc_packet_set_magic_cookie(&(pkt->u.pkt.htc_pkt), 0); + return &pkt->u.pkt; /* not actually a dereference */ +} + +void htt_htc_pkt_free(struct htt_pdev_t *pdev, struct htt_htc_pkt *pkt) +{ + struct htt_htc_pkt_union *u_pkt = (struct htt_htc_pkt_union *)pkt; + + if (!u_pkt) { + qdf_print("HTC packet is NULL"); + return; + } + + HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); + htc_packet_set_magic_cookie(&(u_pkt->u.pkt.htc_pkt), 0); + u_pkt->u.next = pdev->htt_htc_pkt_freelist; + pdev->htt_htc_pkt_freelist = u_pkt; + HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); +} + +void htt_htc_pkt_pool_free(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt_union *pkt, *next; + + HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); + pkt = pdev->htt_htc_pkt_freelist; + pdev->htt_htc_pkt_freelist = NULL; + HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); + + while (pkt) { + next = pkt->u.next; + qdf_mem_free(pkt); + pkt = next; + } +} + +#ifdef ATH_11AC_TXCOMPACT + +void +htt_htc_misc_pkt_list_trim(struct htt_pdev_t *pdev, int level) +{ + struct htt_htc_pkt_union *pkt, *next, *prev = NULL; + int i = 0; + qdf_nbuf_t netbuf; + + HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); + pkt = pdev->htt_htc_pkt_misclist; + while (pkt) { + next = pkt->u.next; + /* trim the out grown list*/ + if (++i > level) { + netbuf = + (qdf_nbuf_t)(pkt->u.pkt.htc_pkt.pNetBufContext); + qdf_nbuf_unmap(pdev->osdev, netbuf, QDF_DMA_TO_DEVICE); + qdf_nbuf_free(netbuf); + qdf_mem_free(pkt); + pkt = NULL; + if (prev) + prev->u.next = NULL; + } + prev = pkt; + pkt = next; + } + HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); +} + +void htt_htc_misc_pkt_list_add(struct htt_pdev_t *pdev, struct htt_htc_pkt *pkt) +{ + struct htt_htc_pkt_union *u_pkt = (struct htt_htc_pkt_union *)pkt; + int misclist_trim_level = htc_get_tx_queue_depth(pdev->htc_pdev, + pkt->htc_pkt.Endpoint) + + HTT_HTC_PKT_MISCLIST_SIZE; + + HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); + if (pdev->htt_htc_pkt_misclist) { + u_pkt->u.next = pdev->htt_htc_pkt_misclist; + pdev->htt_htc_pkt_misclist = u_pkt; + } else { + pdev->htt_htc_pkt_misclist = u_pkt; + } + HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); + + /* only ce pipe size + tx_queue_depth could possibly be in use + * free older packets in the msiclist + */ + htt_htc_misc_pkt_list_trim(pdev, misclist_trim_level); +} + +void htt_htc_misc_pkt_pool_free(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt_union *pkt, *next; + qdf_nbuf_t netbuf; + + HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); + pkt = pdev->htt_htc_pkt_misclist; + pdev->htt_htc_pkt_misclist = NULL; + HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); + + while (pkt) { + next = pkt->u.next; + if (htc_packet_get_magic_cookie(&(pkt->u.pkt.htc_pkt)) != + HTC_PACKET_MAGIC_COOKIE) { + QDF_ASSERT(0); + pkt = next; + continue; + } + + netbuf = (qdf_nbuf_t) (pkt->u.pkt.htc_pkt.pNetBufContext); + qdf_nbuf_unmap(pdev->osdev, netbuf, QDF_DMA_TO_DEVICE); + qdf_nbuf_free(netbuf); + qdf_mem_free(pkt); + pkt = next; + } +} +#endif + + +/* AR6004 don't need HTT layer. */ +#ifdef AR6004_HW +#define NO_HTT_NEEDED true +#else +#define NO_HTT_NEEDED false +#endif + +#if defined(QCA_TX_HTT2_SUPPORT) && defined(CONFIG_HL_SUPPORT) + +/** + * htt_htc_tx_htt2_service_start() - Start TX HTT2 service + * + * @pdev: pointer to htt device. + * @connect_req: pointer to service connection request information + * @connect_resp: pointer to service connection response information + * + * + * Return: None + */ +static void +htt_htc_tx_htt2_service_start(struct htt_pdev_t *pdev, + struct htc_service_connect_req *connect_req, + struct htc_service_connect_resp *connect_resp) +{ + QDF_STATUS status; + + qdf_mem_zero(connect_req, sizeof(struct htc_service_connect_req)); + qdf_mem_zero(connect_resp, sizeof(struct htc_service_connect_resp)); + + /* The same as HTT service but no RX. */ + connect_req->EpCallbacks.pContext = pdev; + connect_req->EpCallbacks.EpTxComplete = htt_h2t_send_complete; + connect_req->EpCallbacks.EpSendFull = htt_h2t_full; + connect_req->MaxSendQueueDepth = HTT_MAX_SEND_QUEUE_DEPTH; + /* Should NOT support credit flow control. */ + connect_req->ConnectionFlags |= + HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + /* Enable HTC schedule mechanism for TX HTT2 service. */ + connect_req->ConnectionFlags |= HTC_CONNECT_FLAGS_ENABLE_HTC_SCHEDULE; + + connect_req->service_id = HTT_DATA2_MSG_SVC; + + status = htc_connect_service(pdev->htc_pdev, connect_req, connect_resp); + + if (status != QDF_STATUS_SUCCESS) { + pdev->htc_tx_htt2_endpoint = ENDPOINT_UNUSED; + pdev->htc_tx_htt2_max_size = 0; + } else { + pdev->htc_tx_htt2_endpoint = connect_resp->Endpoint; + pdev->htc_tx_htt2_max_size = HTC_TX_HTT2_MAX_SIZE; + } + + qdf_print("TX HTT %s, ep %d size %d\n", + (status == QDF_STATUS_SUCCESS ? "ON" : "OFF"), + pdev->htc_tx_htt2_endpoint, + pdev->htc_tx_htt2_max_size); +} +#else + +static inline void +htt_htc_tx_htt2_service_start(struct htt_pdev_t *pdev, + struct htc_service_connect_req *connect_req, + struct htc_service_connect_resp *connect_resp) +{ +} +#endif + +/** + * htt_htc_credit_flow_disable() - disable flow control for + * HTT data message service + * + * @pdev: pointer to htt device. + * @connect_req: pointer to service connection request information + * + * HTC Credit mechanism is disabled based on + * default_tx_comp_req as throughput will be lower + * if we disable htc credit mechanism with default_tx_comp_req + * set since txrx download packet will be limited by ota + * completion. + * + * Return: None + */ +static +void htt_htc_credit_flow_disable(struct htt_pdev_t *pdev, + struct htc_service_connect_req *connect_req) +{ + if (pdev->osdev->bus_type == QDF_BUS_TYPE_SDIO) { + /* + * TODO:Conditional disabling will be removed once firmware + * with reduced tx completion is pushed into release builds. + */ + if (!pdev->cfg.default_tx_comp_req) + connect_req->ConnectionFlags |= + HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + } else { + connect_req->ConnectionFlags |= + HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + } +} + +#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT) + +/** + * htt_dump_bundle_stats() - dump wlan stats + * @pdev: handle to the HTT instance + * + * Return: None + */ +void htt_dump_bundle_stats(htt_pdev_handle pdev) +{ + htc_dump_bundle_stats(pdev->htc_pdev); +} + +/** + * htt_clear_bundle_stats() - clear wlan stats + * @pdev: handle to the HTT instance + * + * Return: None + */ +void htt_clear_bundle_stats(htt_pdev_handle pdev) +{ + htc_clear_bundle_stats(pdev->htc_pdev); +} +#endif + +#if defined(QCA_WIFI_3_0_ADRASTEA) +/** + * htt_htc_attach_all() - Connect to HTC service for HTT + * @pdev: pdev ptr + * + * Return: 0 for success or error code. + */ + +#if defined(QCN7605_SUPPORT) && defined(IPA_OFFLOAD) + +/* In case of QCN7605 with IPA offload only 2 CE + * are used for RFS + */ +static int +htt_htc_attach_all(struct htt_pdev_t *pdev) +{ + if (htt_htc_attach(pdev, HTT_DATA_MSG_SVC)) + goto flush_endpoint; + + if (htt_htc_attach(pdev, HTT_DATA2_MSG_SVC)) + goto flush_endpoint; + + return 0; + +flush_endpoint: + htc_flush_endpoint(pdev->htc_pdev, ENDPOINT_0, HTC_TX_PACKET_TAG_ALL); + + return -EIO; +} + +#else + +static int +htt_htc_attach_all(struct htt_pdev_t *pdev) +{ + if (htt_htc_attach(pdev, HTT_DATA_MSG_SVC)) + goto flush_endpoint; + + if (htt_htc_attach(pdev, HTT_DATA2_MSG_SVC)) + goto flush_endpoint; + + if (htt_htc_attach(pdev, HTT_DATA3_MSG_SVC)) + goto flush_endpoint; + + return 0; + +flush_endpoint: + htc_flush_endpoint(pdev->htc_pdev, ENDPOINT_0, HTC_TX_PACKET_TAG_ALL); + + return -EIO; +} + +#endif + +#else +/** + * htt_htc_attach_all() - Connect to HTC service for HTT + * @pdev: pdev ptr + * + * Return: 0 for success or error code. + */ +static int +htt_htc_attach_all(struct htt_pdev_t *pdev) +{ + return htt_htc_attach(pdev, HTT_DATA_MSG_SVC); +} +#endif + +/** + * htt_pdev_alloc() - allocate HTT pdev + * @txrx_pdev: txrx pdev + * @ctrl_pdev: cfg pdev + * @htc_pdev: HTC pdev + * @osdev: os device + * + * Return: HTT pdev handle + */ +htt_pdev_handle +htt_pdev_alloc(ol_txrx_pdev_handle txrx_pdev, + struct cdp_cfg *ctrl_pdev, + HTC_HANDLE htc_pdev, qdf_device_t osdev) +{ + struct htt_pdev_t *pdev; + struct hif_opaque_softc *osc = cds_get_context(QDF_MODULE_ID_HIF); + + if (!osc) + goto fail1; + + pdev = qdf_mem_malloc(sizeof(*pdev)); + if (!pdev) + goto fail1; + + pdev->osdev = osdev; + pdev->ctrl_pdev = ctrl_pdev; + pdev->txrx_pdev = txrx_pdev; + pdev->htc_pdev = htc_pdev; + + pdev->htt_htc_pkt_freelist = NULL; +#ifdef ATH_11AC_TXCOMPACT + pdev->htt_htc_pkt_misclist = NULL; +#endif + + /* for efficiency, store a local copy of the is_high_latency flag */ + pdev->cfg.is_high_latency = ol_cfg_is_high_latency(pdev->ctrl_pdev); + /* + * Credit reporting through HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND + * enabled or not. + */ + pdev->cfg.credit_update_enabled = + ol_cfg_is_credit_update_enabled(pdev->ctrl_pdev); + + pdev->cfg.request_tx_comp = cds_is_ptp_rx_opt_enabled() || + cds_is_packet_log_enabled(); + + pdev->cfg.default_tx_comp_req = + !ol_cfg_tx_free_at_download(pdev->ctrl_pdev); + + pdev->cfg.is_full_reorder_offload = + ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev); + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "full_reorder_offloaded %d", + (int)pdev->cfg.is_full_reorder_offload); + + pdev->cfg.ce_classify_enabled = + ol_cfg_is_ce_classify_enabled(ctrl_pdev); + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "ce_classify %d", + pdev->cfg.ce_classify_enabled); + + if (pdev->cfg.is_high_latency) { + qdf_atomic_init(&pdev->htt_tx_credit.target_delta); + qdf_atomic_init(&pdev->htt_tx_credit.bus_delta); + qdf_atomic_add(HTT_MAX_BUS_CREDIT, + &pdev->htt_tx_credit.bus_delta); + } + + pdev->targetdef = htc_get_targetdef(htc_pdev); +#if defined(HELIUMPLUS) + HTT_SET_WIFI_IP(pdev, 2, 0); +#endif /* defined(HELIUMPLUS) */ + + if (NO_HTT_NEEDED) + goto success; + /* + * Connect to HTC service. + * This has to be done before calling htt_rx_attach, + * since htt_rx_attach involves sending a rx ring configure + * message to the target. + */ + HTT_TX_MUTEX_INIT(&pdev->htt_tx_mutex); + HTT_TX_NBUF_QUEUE_MUTEX_INIT(pdev); + HTT_TX_MUTEX_INIT(&pdev->credit_mutex); + if (htt_htc_attach_all(pdev)) + goto htt_htc_attach_fail; + if (hif_ce_fastpath_cb_register(osc, htt_t2h_msg_handler_fast, pdev)) + qdf_print("failed to register fastpath callback\n"); + +success: + return pdev; + +htt_htc_attach_fail: + HTT_TX_MUTEX_DESTROY(&pdev->credit_mutex); + HTT_TX_MUTEX_DESTROY(&pdev->htt_tx_mutex); + HTT_TX_NBUF_QUEUE_MUTEX_DESTROY(pdev); + qdf_mem_free(pdev); + +fail1: + return NULL; + +} + +/** + * htt_attach() - Allocate and setup HTT TX/RX descriptors + * @pdev: pdev ptr + * @desc_pool_size: size of tx descriptors + * + * Return: 0 for success or error code. + */ +int +htt_attach(struct htt_pdev_t *pdev, int desc_pool_size) +{ + int i; + int ret = 0; + + pdev->is_ipa_uc_enabled = false; + if (ol_cfg_ipa_uc_offload_enabled(pdev->ctrl_pdev)) + pdev->is_ipa_uc_enabled = true; + + pdev->new_htt_format_enabled = false; + if (ol_cfg_is_htt_new_format_enabled(pdev->ctrl_pdev)) + pdev->new_htt_format_enabled = true; + + htc_enable_hdr_length_check(pdev->htc_pdev, + pdev->new_htt_format_enabled); + + ret = htt_tx_attach(pdev, desc_pool_size); + if (ret) + goto fail1; + + ret = htt_rx_attach(pdev); + if (ret) + goto fail2; + + /* pre-allocate some HTC_PACKET objects */ + for (i = 0; i < HTT_HTC_PKT_POOL_INIT_SIZE; i++) { + struct htt_htc_pkt_union *pkt; + + pkt = qdf_mem_malloc(sizeof(*pkt)); + if (!pkt) + break; + htt_htc_pkt_free(pdev, &pkt->u.pkt); + } + + if (pdev->cfg.is_high_latency) { + /* + * HL - download the whole frame. + * Specify a download length greater than the max MSDU size, + * so the downloads will be limited by the actual frame sizes. + */ + pdev->download_len = 5000; + + if (ol_cfg_tx_free_at_download(pdev->ctrl_pdev) && + !pdev->cfg.request_tx_comp) + pdev->tx_send_complete_part2 = + ol_tx_download_done_hl_free; + else + pdev->tx_send_complete_part2 = + ol_tx_download_done_hl_retain; + + /* + * CHECK THIS LATER: does the HL HTT version of + * htt_rx_mpdu_desc_list_next + * (which is not currently implemented) present the + * adf_nbuf_data(rx_ind_msg) + * as the abstract rx descriptor? + * If not, the rx_fw_desc_offset initialization + * here will have to be adjusted accordingly. + * NOTE: for HL, because fw rx desc is in ind msg, + * not in rx desc, so the + * offset should be negtive value + */ + pdev->rx_fw_desc_offset = + HTT_ENDIAN_BYTE_IDX_SWAP( + HTT_RX_IND_FW_RX_DESC_BYTE_OFFSET + - HTT_RX_IND_HL_BYTES); + + htt_h2t_rx_ring_cfg_msg = htt_h2t_rx_ring_cfg_msg_hl; + htt_h2t_rx_ring_rfs_cfg_msg = htt_h2t_rx_ring_rfs_cfg_msg_hl; + + /* initialize the txrx credit count */ + ol_tx_target_credit_update( + pdev->txrx_pdev, ol_cfg_target_tx_credit( + pdev->ctrl_pdev)); + DPTRACE(qdf_dp_trace_credit_record(QDF_HTT_ATTACH, + QDF_CREDIT_INC, + ol_cfg_target_tx_credit(pdev->ctrl_pdev), + qdf_atomic_read(&pdev->txrx_pdev->target_tx_credit), + qdf_atomic_read(&pdev->txrx_pdev->txq_grps[0].credit), + qdf_atomic_read(&pdev->txrx_pdev->txq_grps[1].credit))); + + } else { + enum wlan_frm_fmt frm_type; + + /* + * LL - download just the initial portion of the frame. + * Download enough to cover the encapsulation headers checked + * by the target's tx classification descriptor engine. + * + * For LL, the FW rx desc directly referenced at its location + * inside the rx indication message. + */ + + /* account for the 802.3 or 802.11 header */ + frm_type = ol_cfg_frame_type(pdev->ctrl_pdev); + + if (frm_type == wlan_frm_fmt_native_wifi) { + pdev->download_len = HTT_TX_HDR_SIZE_NATIVE_WIFI; + } else if (frm_type == wlan_frm_fmt_802_3) { + pdev->download_len = HTT_TX_HDR_SIZE_ETHERNET; + } else { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "Unexpected frame type spec: %d", frm_type); + HTT_ASSERT0(0); + } + + /* + * Account for the optional L2 / ethernet header fields: + * 802.1Q, LLC/SNAP + */ + pdev->download_len += + HTT_TX_HDR_SIZE_802_1Q + HTT_TX_HDR_SIZE_LLC_SNAP; + + /* + * Account for the portion of the L3 (IP) payload that the + * target needs for its tx classification. + */ + pdev->download_len += ol_cfg_tx_download_size(pdev->ctrl_pdev); + + /* + * Account for the HTT tx descriptor, including the + * HTC header + alignment padding. + */ + pdev->download_len += sizeof(struct htt_host_tx_desc_t); + + /* + * The TXCOMPACT htt_tx_sched function uses pdev->download_len + * to apply for all requeued tx frames. Thus, + * pdev->download_len has to be the largest download length of + * any tx frame that will be downloaded. + * This maximum download length is for management tx frames, + * which have an 802.11 header. + */ +#ifdef ATH_11AC_TXCOMPACT + pdev->download_len = sizeof(struct htt_host_tx_desc_t) + + HTT_TX_HDR_SIZE_OUTER_HDR_MAX /* worst case */ + + HTT_TX_HDR_SIZE_802_1Q + + HTT_TX_HDR_SIZE_LLC_SNAP + + ol_cfg_tx_download_size(pdev->ctrl_pdev); +#endif + pdev->tx_send_complete_part2 = ol_tx_download_done_ll; + + /* + * For LL, the FW rx desc is alongside the HW rx desc fields in + * the htt_host_rx_desc_base struct/. + */ + pdev->rx_fw_desc_offset = RX_STD_DESC_FW_MSDU_OFFSET; + + htt_h2t_rx_ring_cfg_msg = htt_h2t_rx_ring_cfg_msg_ll; + htt_h2t_rx_ring_rfs_cfg_msg = htt_h2t_rx_ring_rfs_cfg_msg_ll; + } + + return 0; + +fail2: + htt_tx_detach(pdev); + +fail1: + return ret; +} + +QDF_STATUS htt_attach_target(htt_pdev_handle pdev) +{ + QDF_STATUS status; + + status = htt_h2t_ver_req_msg(pdev); + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s:%d: could not send h2t_ver_req msg", + __func__, __LINE__); + return status; + } +#if defined(HELIUMPLUS) + /* + * Send the frag_desc info to target. + */ + status = htt_h2t_frag_desc_bank_cfg_msg(pdev); + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s:%d: could not send h2t_frag_desc_bank_cfg msg", + __func__, __LINE__); + return status; + } +#endif /* defined(HELIUMPLUS) */ + + + /* + * If applicable, send the rx ring config message to the target. + * The host could wait for the HTT version number confirmation message + * from the target before sending any further HTT messages, but it's + * reasonable to assume that the host and target HTT version numbers + * match, and proceed immediately with the remaining configuration + * handshaking. + */ + + status = htt_h2t_rx_ring_rfs_cfg_msg(pdev); + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s:%d: could not send h2t_rx_ring_rfs_cfg msg", + __func__, __LINE__); + return status; + } + + status = htt_h2t_rx_ring_cfg_msg(pdev); + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s:%d: could not send h2t_rx_ring_cfg msg", + __func__, __LINE__); + return status; + } + + status = HTT_IPA_CONFIG(pdev, status); + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s:%d: could not send h2t_ipa_uc_rsc_cfg msg", + __func__, __LINE__); + return status; + } + + return status; +} + +void htt_detach(htt_pdev_handle pdev) +{ + htt_rx_detach(pdev); + htt_tx_detach(pdev); + htt_htc_pkt_pool_free(pdev); +#ifdef ATH_11AC_TXCOMPACT + htt_htc_misc_pkt_pool_free(pdev); +#endif + HTT_TX_MUTEX_DESTROY(&pdev->credit_mutex); + HTT_TX_MUTEX_DESTROY(&pdev->htt_tx_mutex); + HTT_TX_NBUF_QUEUE_MUTEX_DESTROY(pdev); +} + +/** + * htt_pdev_free() - Free HTT pdev + * @pdev: htt pdev + * + * Return: none + */ +void htt_pdev_free(htt_pdev_handle pdev) +{ + qdf_mem_free(pdev); +} + +void htt_detach_target(htt_pdev_handle pdev) +{ +} + +static inline +int htt_update_endpoint(struct htt_pdev_t *pdev, + uint16_t service_id, HTC_ENDPOINT_ID ep) +{ + struct hif_opaque_softc *hif_ctx; + uint8_t ul = 0xff, dl = 0xff; + int ul_polled, dl_polled; + int tx_service = 0; + int rc = 0; + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (qdf_unlikely(!hif_ctx)) { + QDF_ASSERT(hif_ctx); + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s:%d: assuming non-tx service.", + __func__, __LINE__); + } else { + ul = dl = 0xff; + if (QDF_STATUS_SUCCESS != + hif_map_service_to_pipe(hif_ctx, service_id, + &ul, &dl, + &ul_polled, &dl_polled)) + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "%s:%d: assuming non-tx srv.", + __func__, __LINE__); + else + tx_service = (ul != 0xff); + } + if (tx_service) { + /* currently we have only one OUT htt tx service */ + QDF_BUG(service_id == HTT_DATA_MSG_SVC); + + pdev->htc_tx_endpoint = ep; + hif_save_htc_htt_config_endpoint(hif_ctx, ep); + rc = 1; + } + return rc; +} + +int htt_htc_attach(struct htt_pdev_t *pdev, uint16_t service_id) +{ + struct htc_service_connect_req connect; + struct htc_service_connect_resp response; + QDF_STATUS status; + + qdf_mem_zero(&connect, sizeof(connect)); + qdf_mem_zero(&response, sizeof(response)); + + connect.pMetaData = NULL; + connect.MetaDataLength = 0; + connect.EpCallbacks.pContext = pdev; + connect.EpCallbacks.EpTxComplete = htt_h2t_send_complete; + connect.EpCallbacks.EpTxCompleteMultiple = NULL; + connect.EpCallbacks.EpRecv = htt_t2h_msg_handler; + connect.EpCallbacks.ep_resume_tx_queue = htt_tx_resume_handler; + connect.EpCallbacks.ep_padding_credit_update = + htt_tx_padding_credit_update_handler; + + /* rx buffers currently are provided by HIF, not by EpRecvRefill */ + connect.EpCallbacks.EpRecvRefill = NULL; + connect.EpCallbacks.RecvRefillWaterMark = 1; + /* N/A, fill is done by HIF */ + + connect.EpCallbacks.EpSendFull = htt_h2t_full; + /* + * Specify how deep to let a queue get before htc_send_pkt will + * call the EpSendFull function due to excessive send queue depth. + */ + connect.MaxSendQueueDepth = HTT_MAX_SEND_QUEUE_DEPTH; + + /* disable flow control for HTT data message service */ + htt_htc_credit_flow_disable(pdev, &connect); + + /* connect to control service */ + connect.service_id = service_id; + + status = htc_connect_service(pdev->htc_pdev, &connect, &response); + + if (status != QDF_STATUS_SUCCESS) { + if (cds_is_fw_down()) + return -EIO; + + if (status == QDF_STATUS_E_NOMEM || + cds_is_self_recovery_enabled()) + return qdf_status_to_os_return(status); + + QDF_BUG(0); + } + + htt_update_endpoint(pdev, service_id, response.Endpoint); + + /* Start TX HTT2 service if the target support it. */ + htt_htc_tx_htt2_service_start(pdev, &connect, &response); + + return 0; /* success */ +} + +void htt_log_rx_ring_info(htt_pdev_handle pdev) +{ + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s: htt pdev is NULL", __func__); + return; + } + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_DEBUG, + "%s: Data Stall Detected with reason 4 (=FW_RX_REFILL_FAILED)." + "src htt rx ring: space for %d elements, filled with %d buffers, buffers in the ring %d, refill debt %d", + __func__, pdev->rx_ring.size, pdev->rx_ring.fill_level, + pdev->rx_ring.fill_cnt, + qdf_atomic_read(&pdev->rx_ring.refill_debt)); +} + +#if HTT_DEBUG_LEVEL > 5 +void htt_display(htt_pdev_handle pdev, int indent) +{ + qdf_print("%*s%s:\n", indent, " ", "HTT"); + qdf_print("%*stx desc pool: %d elems of %d bytes, %d allocated\n", + indent + 4, " ", + pdev->tx_descs.pool_elems, + pdev->tx_descs.size, pdev->tx_descs.alloc_cnt); + qdf_print("%*srx ring: space for %d elems, filled with %d buffers\n", + indent + 4, " ", + pdev->rx_ring.size, pdev->rx_ring.fill_level); + qdf_print("%*sat %pK (%llx paddr)\n", indent + 8, " ", + pdev->rx_ring.buf.paddrs_ring, + (unsigned long long)pdev->rx_ring.base_paddr); + qdf_print("%*snetbuf ring @ %pK\n", indent + 8, " ", + pdev->rx_ring.buf.netbufs_ring); + qdf_print("%*sFW_IDX shadow register: vaddr = %pK, paddr = %llx\n", + indent + 8, " ", + pdev->rx_ring.alloc_idx.vaddr, + (unsigned long long)pdev->rx_ring.alloc_idx.paddr); + qdf_print("%*sSW enqueue idx= %d, SW dequeue idx: desc= %d, buf= %d\n", + indent + 8, " ", *pdev->rx_ring.alloc_idx.vaddr, + pdev->rx_ring.sw_rd_idx.msdu_desc, + pdev->rx_ring.sw_rd_idx.msdu_payld); +} +#endif + +#ifdef IPA_OFFLOAD +/** + * htt_ipa_uc_attach() - Allocate UC data path resources + * @pdev: handle to the HTT instance + * + * Return: 0 success + * none 0 fail + */ +int htt_ipa_uc_attach(struct htt_pdev_t *pdev) +{ + int error; + + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_DEBUG, "%s: enter", + __func__); + + /* TX resource attach */ + error = htt_tx_ipa_uc_attach( + pdev, + ol_cfg_ipa_uc_tx_buf_size(pdev->ctrl_pdev), + ol_cfg_ipa_uc_tx_max_buf_cnt(pdev->ctrl_pdev), + ol_cfg_ipa_uc_tx_partition_base(pdev->ctrl_pdev)); + if (error) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "HTT IPA UC TX attach fail code %d", error); + HTT_ASSERT0(0); + return error; + } + + /* RX resource attach */ + error = htt_rx_ipa_uc_attach( + pdev, qdf_get_pwr2(pdev->rx_ring.fill_level)); + if (error) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "HTT IPA UC RX attach fail code %d", error); + htt_tx_ipa_uc_detach(pdev); + HTT_ASSERT0(0); + return error; + } + + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_DEBUG, "%s: exit", + __func__); + return 0; /* success */ +} + +/** + * htt_ipa_uc_attach() - Remove UC data path resources + * @pdev: handle to the HTT instance + * + * Return: None + */ +void htt_ipa_uc_detach(struct htt_pdev_t *pdev) +{ + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_DEBUG, "%s: enter", + __func__); + + /* TX IPA micro controller detach */ + htt_tx_ipa_uc_detach(pdev); + + /* RX IPA micro controller detach */ + htt_rx_ipa_uc_detach(pdev); + + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_DEBUG, "%s: exit", + __func__); +} + +int +htt_ipa_uc_get_resource(htt_pdev_handle pdev, + qdf_shared_mem_t **ce_sr, + qdf_shared_mem_t **tx_comp_ring, + qdf_shared_mem_t **rx_rdy_ring, + qdf_shared_mem_t **rx2_rdy_ring, + qdf_shared_mem_t **rx_proc_done_idx, + qdf_shared_mem_t **rx2_proc_done_idx, + uint32_t *ce_sr_ring_size, + qdf_dma_addr_t *ce_reg_paddr, + uint32_t *tx_num_alloc_buffer) +{ + /* Release allocated resource to client */ + *tx_comp_ring = pdev->ipa_uc_tx_rsc.tx_comp_ring; + *rx_rdy_ring = pdev->ipa_uc_rx_rsc.rx_ind_ring; + *rx2_rdy_ring = pdev->ipa_uc_rx_rsc.rx2_ind_ring; + *rx_proc_done_idx = pdev->ipa_uc_rx_rsc.rx_ipa_prc_done_idx; + *rx2_proc_done_idx = pdev->ipa_uc_rx_rsc.rx2_ipa_prc_done_idx; + *tx_num_alloc_buffer = (uint32_t)pdev->ipa_uc_tx_rsc.alloc_tx_buf_cnt; + + /* Get copy engine, bus resource */ + htc_ipa_get_ce_resource(pdev->htc_pdev, ce_sr, + ce_sr_ring_size, ce_reg_paddr); + + return 0; +} + +/** + * htt_ipa_uc_set_doorbell_paddr() - Propagate IPA doorbell address + * @pdev: handle to the HTT instance + * @ipa_uc_tx_doorbell_paddr: TX doorbell base physical address + * @ipa_uc_rx_doorbell_paddr: RX doorbell base physical address + * + * Return: 0 success + */ +int +htt_ipa_uc_set_doorbell_paddr(htt_pdev_handle pdev, + qdf_dma_addr_t ipa_uc_tx_doorbell_paddr, + qdf_dma_addr_t ipa_uc_rx_doorbell_paddr) +{ + pdev->ipa_uc_tx_rsc.tx_comp_idx_paddr = ipa_uc_tx_doorbell_paddr; + pdev->ipa_uc_rx_rsc.rx_rdy_idx_paddr = ipa_uc_rx_doorbell_paddr; + return 0; +} +#endif /* IPA_OFFLOAD */ + +/** + * htt_mark_first_wakeup_packet() - set flag to indicate that + * fw is compatible for marking first packet after wow wakeup + * @pdev: pointer to htt pdev + * @value: 1 for enabled/ 0 for disabled + * + * Return: None + */ +void htt_mark_first_wakeup_packet(htt_pdev_handle pdev, + uint8_t value) +{ + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s: htt pdev is NULL", __func__); + return; + } + + pdev->cfg.is_first_wakeup_packet = value; +} + diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_fw_stats.c b/drivers/staging/qcacld-3.0/core/dp/htt/htt_fw_stats.c new file mode 100644 index 0000000000000000000000000000000000000000..2fdd9f3a67b55ae38dfcc108fa923bbeb41573f2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_fw_stats.c @@ -0,0 +1,1344 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file htt_fw_stats.c + * @brief Provide functions to process FW status retrieved from FW. + */ + +#include /* HTC_PACKET */ +#include /* HTT_T2H_MSG_TYPE, etc. */ +#include /* qdf_nbuf_t */ +#include /* qdf_mem_set */ +#include /* ol_fw_tx_dbg_ppdu_base */ + +#include +#include /* htt_tx_status */ + +#include + +#include + +static char *bw_str_arr[] = {"20MHz", "40MHz", "80MHz", "160MHz"}; + +/* + * Defined the macro tx_rate_stats_print_cmn() + * so that this could be used in both + * htt_t2h_stats_tx_rate_stats_print() & + * htt_t2h_stats_tx_rate_stats_print_v2(). + * Each of these functions take a different structure as argument, + * but with common fields in the structures--so using a macro + * to bypass the strong type-checking of a function seems a simple + * trick to use to avoid the code duplication. + */ +#define tx_rate_stats_print_cmn(_tx_rate_info, _concise) \ + do { \ + qdf_nofl_info("TX Rate Info:"); \ + \ + /* MCS */ \ + qdf_nofl_info("%s: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",\ + "MCS counts (0..9)", \ + _tx_rate_info->mcs[0], \ + _tx_rate_info->mcs[1], \ + _tx_rate_info->mcs[2], \ + _tx_rate_info->mcs[3], \ + _tx_rate_info->mcs[4], \ + _tx_rate_info->mcs[5], \ + _tx_rate_info->mcs[6], \ + _tx_rate_info->mcs[7], \ + _tx_rate_info->mcs[8], \ + _tx_rate_info->mcs[9]); \ + \ + /* SGI */ \ + qdf_nofl_info("%s: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",\ + "SGI counts (0..9)", \ + _tx_rate_info->sgi[0], \ + _tx_rate_info->sgi[1], \ + _tx_rate_info->sgi[2], \ + _tx_rate_info->sgi[3], \ + _tx_rate_info->sgi[4], \ + _tx_rate_info->sgi[5], \ + _tx_rate_info->sgi[6], \ + _tx_rate_info->sgi[7], \ + _tx_rate_info->sgi[8], \ + _tx_rate_info->sgi[9]); \ + \ + /* NSS */ \ + qdf_nofl_info("NSS counts: 1x1 %d, 2x2 %d, 3x3 %d", \ + _tx_rate_info->nss[0], \ + _tx_rate_info->nss[1], _tx_rate_info->nss[2]);\ + \ + /* BW */ \ + if (ARRAY_SIZE(_tx_rate_info->bw) == 3) \ + qdf_nofl_info("BW counts: %s %d, %s %d, %s %d", \ + bw_str_arr[0], _tx_rate_info->bw[0], \ + bw_str_arr[1], _tx_rate_info->bw[1], \ + bw_str_arr[2], _tx_rate_info->bw[2]); \ + else if (ARRAY_SIZE(_tx_rate_info->bw) == 4) \ + qdf_nofl_info("BW counts: %s %d, %s %d, %s %d, %s %d", \ + bw_str_arr[0], _tx_rate_info->bw[0], \ + bw_str_arr[1], _tx_rate_info->bw[1], \ + bw_str_arr[2], _tx_rate_info->bw[2], \ + bw_str_arr[3], _tx_rate_info->bw[3]); \ + \ + \ + /* Preamble */ \ + qdf_nofl_info("Preamble (O C H V) counts: %d, %d, %d, %d",\ + _tx_rate_info->pream[0], \ + _tx_rate_info->pream[1], \ + _tx_rate_info->pream[2], \ + _tx_rate_info->pream[3]); \ + \ + /* STBC rate counts */ \ + qdf_nofl_info("%s: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",\ + "STBC rate counts (0..9)", \ + _tx_rate_info->stbc[0], \ + _tx_rate_info->stbc[1], \ + _tx_rate_info->stbc[2], \ + _tx_rate_info->stbc[3], \ + _tx_rate_info->stbc[4], \ + _tx_rate_info->stbc[5], \ + _tx_rate_info->stbc[6], \ + _tx_rate_info->stbc[7], \ + _tx_rate_info->stbc[8], \ + _tx_rate_info->stbc[9]); \ + \ + /* LDPC and TxBF counts */ \ + qdf_nofl_info("LDPC Counts: %d", _tx_rate_info->ldpc);\ + qdf_nofl_info("RTS Counts: %d", _tx_rate_info->rts_cnt);\ + /* RSSI Values for last ack frames */ \ + qdf_nofl_info("Ack RSSI: %d", _tx_rate_info->ack_rssi);\ + } while (0) + +static void htt_t2h_stats_tx_rate_stats_print(wlan_dbg_tx_rate_info_t * + tx_rate_info, int concise) +{ + tx_rate_stats_print_cmn(tx_rate_info, concise); +} + +static void htt_t2h_stats_tx_rate_stats_print_v2(wlan_dbg_tx_rate_info_v2_t * + tx_rate_info, int concise) +{ + tx_rate_stats_print_cmn(tx_rate_info, concise); +} + +/* + * Defined the macro rx_rate_stats_print_cmn() + * so that this could be used in both + * htt_t2h_stats_rx_rate_stats_print() & + * htt_t2h_stats_rx_rate_stats_print_v2(). + * Each of these functions take a different structure as argument, + * but with common fields in the structures -- so using a macro + * to bypass the strong type-checking of a function seems a simple + * trick to use to avoid the code duplication. + */ +#define rx_rate_stats_print_cmn(_rx_phy_info, _concise) \ + do { \ + qdf_nofl_info("RX Rate Info:"); \ + \ + /* MCS */ \ + qdf_nofl_info("%s: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",\ + "MCS counts (0..9)", \ + _rx_phy_info->mcs[0], \ + _rx_phy_info->mcs[1], \ + _rx_phy_info->mcs[2], \ + _rx_phy_info->mcs[3], \ + _rx_phy_info->mcs[4], \ + _rx_phy_info->mcs[5], \ + _rx_phy_info->mcs[6], \ + _rx_phy_info->mcs[7], \ + _rx_phy_info->mcs[8], \ + _rx_phy_info->mcs[9]); \ + \ + /* SGI */ \ + qdf_nofl_info("%s: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",\ + "SGI counts (0..9)", \ + _rx_phy_info->sgi[0], \ + _rx_phy_info->sgi[1], \ + _rx_phy_info->sgi[2], \ + _rx_phy_info->sgi[3], \ + _rx_phy_info->sgi[4], \ + _rx_phy_info->sgi[5], \ + _rx_phy_info->sgi[6], \ + _rx_phy_info->sgi[7], \ + _rx_phy_info->sgi[8], \ + _rx_phy_info->sgi[9]); \ + \ + /* + * NSS \ + * nss[0] just holds the count of non-stbc frames that were \ + * sent at 1x1 rates and nsts holds the count of frames sent \ + * with stbc. \ + * It was decided to not include PPDUs sent w/ STBC in nss[0] \ + * since it would be easier to change the value that needs to \ + * be printed (from stbc+non-stbc count to only non-stbc count)\ + * if needed in the future. Hence the addition in the host code\ + * at this line. + */ \ + qdf_nofl_info("NSS counts: 1x1 %d, 2x2 %d, 3x3 %d, 4x4 %d",\ + _rx_phy_info->nss[0] + _rx_phy_info->nsts,\ + _rx_phy_info->nss[1], \ + _rx_phy_info->nss[2], \ + _rx_phy_info->nss[3]); \ + \ + /* NSTS */ \ + qdf_nofl_info("NSTS count: %d", _rx_phy_info->nsts); \ + \ + /* BW */ \ + if (ARRAY_SIZE(_rx_phy_info->bw) == 3) \ + qdf_nofl_info("BW counts: %s %d, %s %d, %s %d", \ + bw_str_arr[0], _rx_phy_info->bw[0], \ + bw_str_arr[1], _rx_phy_info->bw[1], \ + bw_str_arr[2], _rx_phy_info->bw[2]); \ + else if (ARRAY_SIZE(_rx_phy_info->bw) == 4) \ + qdf_nofl_info("BW counts: %s %d, %s %d, %s %d, %s %d", \ + bw_str_arr[0], _rx_phy_info->bw[0], \ + bw_str_arr[1], _rx_phy_info->bw[1], \ + bw_str_arr[2], _rx_phy_info->bw[2], \ + bw_str_arr[3], _rx_phy_info->bw[3]); \ + \ + /* Preamble */ \ + qdf_nofl_info("Preamble counts: %d, %d, %d, %d, %d, %d",\ + _rx_phy_info->pream[0], \ + _rx_phy_info->pream[1], \ + _rx_phy_info->pream[2], \ + _rx_phy_info->pream[3], \ + _rx_phy_info->pream[4], \ + _rx_phy_info->pream[5]); \ + \ + /* STBC rate counts */ \ + qdf_nofl_info("%s: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",\ + "STBC rate counts (0..9)", \ + _rx_phy_info->stbc[0], \ + _rx_phy_info->stbc[1], \ + _rx_phy_info->stbc[2], \ + _rx_phy_info->stbc[3], \ + _rx_phy_info->stbc[4], \ + _rx_phy_info->stbc[5], \ + _rx_phy_info->stbc[6], \ + _rx_phy_info->stbc[7], \ + _rx_phy_info->stbc[8], \ + _rx_phy_info->stbc[9]); \ + \ + /* LDPC and TxBF counts */ \ + qdf_nofl_info("LDPC TXBF Counts: %d, %d", \ + _rx_phy_info->ldpc, _rx_phy_info->txbf);\ + /* RSSI Values for last received frames */ \ + qdf_nofl_info("RSSI (data, mgmt): %d, %d", _rx_phy_info->data_rssi,\ + _rx_phy_info->mgmt_rssi); \ + \ + qdf_nofl_info("RSSI Chain 0 (0x%02x 0x%02x 0x%02x 0x%02x)",\ + ((_rx_phy_info->rssi_chain0 >> 24) & 0xff),\ + ((_rx_phy_info->rssi_chain0 >> 16) & 0xff),\ + ((_rx_phy_info->rssi_chain0 >> 8) & 0xff),\ + ((_rx_phy_info->rssi_chain0 >> 0) & 0xff));\ + \ + qdf_nofl_info("RSSI Chain 1 (0x%02x 0x%02x 0x%02x 0x%02x)",\ + ((_rx_phy_info->rssi_chain1 >> 24) & 0xff),\ + ((_rx_phy_info->rssi_chain1 >> 16) & 0xff),\ + ((_rx_phy_info->rssi_chain1 >> 8) & 0xff),\ + ((_rx_phy_info->rssi_chain1 >> 0) & 0xff));\ + \ + qdf_nofl_info("RSSI Chain 2 (0x%02x 0x%02x 0x%02x 0x%02x)",\ + ((_rx_phy_info->rssi_chain2 >> 24) & 0xff),\ + ((_rx_phy_info->rssi_chain2 >> 16) & 0xff),\ + ((_rx_phy_info->rssi_chain2 >> 8) & 0xff),\ + ((_rx_phy_info->rssi_chain2 >> 0) & 0xff));\ + } while (0) + +static void htt_t2h_stats_rx_rate_stats_print(wlan_dbg_rx_rate_info_t * + rx_phy_info, int concise) +{ + rx_rate_stats_print_cmn(rx_phy_info, concise); +} + +static void htt_t2h_stats_rx_rate_stats_print_v2(wlan_dbg_rx_rate_info_v2_t * + rx_phy_info, int concise) +{ + rx_rate_stats_print_cmn(rx_phy_info, concise); +} + +static void +htt_t2h_stats_pdev_stats_print(struct wlan_dbg_stats *wlan_pdev_stats, + int concise) +{ + struct wlan_dbg_tx_stats *tx = &wlan_pdev_stats->tx; + struct wlan_dbg_rx_stats *rx = &wlan_pdev_stats->rx; + + qdf_nofl_info("WAL Pdev stats:"); + qdf_nofl_info("### Tx ###"); + + /* Num HTT cookies queued to dispatch list */ + qdf_nofl_info("comp_queued :%d", tx->comp_queued); + /* Num HTT cookies dispatched */ + qdf_nofl_info("comp_delivered :%d", tx->comp_delivered); + /* Num MSDU queued to WAL */ + qdf_nofl_info("msdu_enqued :%d", tx->msdu_enqued); + /* Num MPDU queued to WAL */ + qdf_nofl_info("mpdu_enqued :%d", tx->mpdu_enqued); + /* Num MSDUs dropped by WMM limit */ + qdf_nofl_info("wmm_drop :%d", tx->wmm_drop); + /* Num Local frames queued */ + qdf_nofl_info("local_enqued :%d", tx->local_enqued); + /* Num Local frames done */ + qdf_nofl_info("local_freed :%d", tx->local_freed); + /* Num queued to HW */ + qdf_nofl_info("hw_queued :%d", tx->hw_queued); + /* Num PPDU reaped from HW */ + qdf_nofl_info("hw_reaped :%d", tx->hw_reaped); + /* Num underruns */ + qdf_nofl_info("mac underrun :%d", tx->underrun); + /* Num underruns */ + qdf_nofl_info("phy underrun :%d", tx->phy_underrun); + /* Num PPDUs cleaned up in TX abort */ + qdf_nofl_info("tx_abort :%d", tx->tx_abort); + /* Num MPDUs requed by SW */ + qdf_nofl_info("mpdus_requed :%d", tx->mpdus_requed); + /* Excessive retries */ +#if defined(AR900B) + qdf_nofl_info("excess retries :%d", tx->tx_xretry); +#endif + /* last data rate */ + qdf_nofl_info("last rc :%d", tx->data_rc); + /* scheduler self triggers */ + qdf_nofl_info("sched self trig :%d", tx->self_triggers); + /* SW retry failures */ + qdf_nofl_info("ampdu retry failed:%d", tx->sw_retry_failure); + /* ilegal phy rate errirs */ + qdf_nofl_info("illegal rate errs :%d", tx->illgl_rate_phy_err); + /* pdev continuous excessive retries */ + qdf_nofl_info("pdev cont xretry :%d", tx->pdev_cont_xretry); + /* pdev continuous excessive retries */ + qdf_nofl_info("pdev tx timeout :%d", tx->pdev_tx_timeout); + /* pdev resets */ + qdf_nofl_info("pdev resets :%d", tx->pdev_resets); + /* PPDU > txop duration */ + qdf_nofl_info("ppdu txop ovf :%d", tx->txop_ovf); +#if defined(AR900B) + qdf_nofl_info("seq_posted :%d", tx->seq_posted); + qdf_nofl_info("seq_failed_queueing :%d", tx->seq_failed_queueing); + qdf_nofl_info("seq_completed :%d", tx->seq_completed); + qdf_nofl_info("seq_restarted :%d", tx->seq_restarted); + qdf_nofl_info("mu_seq_posted :%d", tx->mu_seq_posted); + qdf_nofl_info("mpdus_sw_flush :%d", tx->mpdus_sw_flush); + qdf_nofl_info("mpdus_hw_filter :%d", tx->mpdus_hw_filter); + qdf_nofl_info("mpdus_truncated :%d", tx->mpdus_truncated); + qdf_nofl_info("mpdus_ack_failed :%d", tx->mpdus_ack_failed); + qdf_nofl_info("mpdus_expired :%d", tx->mpdus_expired); +#endif + + qdf_nofl_info("### Rx ###"); + /* Cnts any change in ring routing mid-ppdu */ + qdf_nofl_info("ppdu_route_change :%d", rx->mid_ppdu_route_change); + /* Total number of statuses processed */ + qdf_nofl_info("status_rcvd :%d", rx->status_rcvd); + /* Extra frags on rings 0-3 */ + qdf_nofl_info("r0_frags :%d", rx->r0_frags); + qdf_nofl_info("r1_frags :%d", rx->r1_frags); + qdf_nofl_info("r2_frags :%d", rx->r2_frags); + qdf_nofl_info("r3_frags :%d", rx->r3_frags); + /* MSDUs / MPDUs delivered to HTT */ + qdf_nofl_info("htt_msdus :%d", rx->htt_msdus); + qdf_nofl_info("htt_mpdus :%d", rx->htt_mpdus); + /* MSDUs / MPDUs delivered to local stack */ + qdf_nofl_info("loc_msdus :%d", rx->loc_msdus); + qdf_nofl_info("loc_mpdus :%d", rx->loc_mpdus); + /* AMSDUs that have more MSDUs than the status ring size */ + qdf_nofl_info("oversize_amsdu :%d", rx->oversize_amsdu); + /* Number of PHY errors */ + qdf_nofl_info("phy_errs :%d", rx->phy_errs); + /* Number of PHY errors dropped */ + qdf_nofl_info("phy_errs dropped :%d", rx->phy_err_drop); + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + qdf_nofl_info("mpdu_errs :%d", rx->mpdu_errs); +#if defined(AR900B) + qdf_nofl_info("rx_ovfl_errs :%d", rx->rx_ovfl_errs); +#endif + +} + +static void +htt_t2h_stats_rx_reorder_stats_print(struct rx_reorder_stats *stats_ptr, + int concise) +{ + qdf_nofl_info("Rx reorder statistics:"); + qdf_nofl_info(" %u non-QoS frames received", + stats_ptr->deliver_non_qos); + qdf_nofl_info(" %u frames received in-order", + stats_ptr->deliver_in_order); + qdf_nofl_info(" %u frames flushed due to timeout", + stats_ptr->deliver_flush_timeout); + qdf_nofl_info(" %u frames flushed due to moving out of window", + stats_ptr->deliver_flush_oow); + qdf_nofl_info(" %u frames flushed due to receiving DELBA", + stats_ptr->deliver_flush_delba); + qdf_nofl_info(" %u frames discarded due to FCS error", + stats_ptr->fcs_error); + qdf_nofl_info(" %u frames discarded due to invalid peer", + stats_ptr->invalid_peer); + qdf_nofl_info + (" %u frames discarded due to duplication (non aggregation)", + stats_ptr->dup_non_aggr); + qdf_nofl_info(" %u frames discarded due to duplication in reorder queue", + stats_ptr->dup_in_reorder); + qdf_nofl_info(" %u frames discarded due to processed before", + stats_ptr->dup_past); + qdf_nofl_info(" %u times reorder timeout happened", + stats_ptr->reorder_timeout); + qdf_nofl_info(" %u times incorrect bar received", + stats_ptr->invalid_bar_ssn); + qdf_nofl_info(" %u times bar ssn reset happened", + stats_ptr->ssn_reset); + qdf_nofl_info(" %u times flushed due to peer delete", + stats_ptr->deliver_flush_delpeer); + qdf_nofl_info(" %u times flushed due to offload", + stats_ptr->deliver_flush_offload); + qdf_nofl_info(" %u times flushed due to ouf of buffer", + stats_ptr->deliver_flush_oob); + qdf_nofl_info(" %u MPDU's dropped due to PN check fail", + stats_ptr->pn_fail); + qdf_nofl_info(" %u MPDU's dropped due to lack of memory", + stats_ptr->store_fail); + qdf_nofl_info(" %u times tid pool alloc succeeded", + stats_ptr->tid_pool_alloc_succ); + qdf_nofl_info(" %u times MPDU pool alloc succeeded", + stats_ptr->mpdu_pool_alloc_succ); + qdf_nofl_info(" %u times MSDU pool alloc succeeded", + stats_ptr->msdu_pool_alloc_succ); + qdf_nofl_info(" %u times tid pool alloc failed", + stats_ptr->tid_pool_alloc_fail); + qdf_nofl_info(" %u times MPDU pool alloc failed", + stats_ptr->mpdu_pool_alloc_fail); + qdf_nofl_info(" %u times MSDU pool alloc failed", + stats_ptr->msdu_pool_alloc_fail); + qdf_nofl_info(" %u times tid pool freed", + stats_ptr->tid_pool_free); + qdf_nofl_info(" %u times MPDU pool freed", + stats_ptr->mpdu_pool_free); + qdf_nofl_info(" %u times MSDU pool freed", + stats_ptr->msdu_pool_free); + qdf_nofl_info(" %u MSDUs undelivered to HTT, queued to Rx MSDU free list", + stats_ptr->msdu_queued); + qdf_nofl_info(" %u MSDUs released from Rx MSDU list to MAC ring", + stats_ptr->msdu_recycled); + qdf_nofl_info(" %u MPDUs with invalid peer but A2 found in AST", + stats_ptr->invalid_peer_a2_in_ast); + qdf_nofl_info(" %u MPDUs with invalid peer but A3 found in AST", + stats_ptr->invalid_peer_a3_in_ast); + qdf_nofl_info(" %u MPDUs with invalid peer, Broadcast or Mulitcast frame", + stats_ptr->invalid_peer_bmc_mpdus); + qdf_nofl_info(" %u MSDUs with err attention word", + stats_ptr->rxdesc_err_att); + qdf_nofl_info(" %u MSDUs with flag of peer_idx_invalid", + stats_ptr->rxdesc_err_peer_idx_inv); + qdf_nofl_info(" %u MSDUs with flag of peer_idx_timeout", + stats_ptr->rxdesc_err_peer_idx_to); + qdf_nofl_info(" %u MSDUs with flag of overflow", + stats_ptr->rxdesc_err_ov); + qdf_nofl_info(" %u MSDUs with flag of msdu_length_err", + stats_ptr->rxdesc_err_msdu_len); + qdf_nofl_info(" %u MSDUs with flag of mpdu_length_err", + stats_ptr->rxdesc_err_mpdu_len); + qdf_nofl_info(" %u MSDUs with flag of tkip_mic_err", + stats_ptr->rxdesc_err_tkip_mic); + qdf_nofl_info(" %u MSDUs with flag of decrypt_err", + stats_ptr->rxdesc_err_decrypt); + qdf_nofl_info(" %u MSDUs with flag of fcs_err", + stats_ptr->rxdesc_err_fcs); + qdf_nofl_info(" %u Unicast frames with invalid peer handler", + stats_ptr->rxdesc_uc_msdus_inv_peer); + qdf_nofl_info(" %u unicast frame to DUT with invalid peer handler", + stats_ptr->rxdesc_direct_msdus_inv_peer); + qdf_nofl_info(" %u Broadcast/Multicast frames with invalid peer handler", + stats_ptr->rxdesc_bmc_msdus_inv_peer); + qdf_nofl_info(" %u MSDUs dropped due to no first MSDU flag", + stats_ptr->rxdesc_no_1st_msdu); + qdf_nofl_info(" %u MSDUs dropped due to ring overflow", + stats_ptr->msdu_drop_ring_ov); + qdf_nofl_info(" %u MSDUs dropped due to FC mismatch", + stats_ptr->msdu_drop_fc_mismatch); + qdf_nofl_info(" %u MSDUs dropped due to mgt frame in Remote ring", + stats_ptr->msdu_drop_mgmt_remote_ring); + qdf_nofl_info(" %u MSDUs dropped due to misc non error", + stats_ptr->msdu_drop_misc); + qdf_nofl_info(" %u MSDUs go to offload before reorder", + stats_ptr->offload_msdu_wal); + qdf_nofl_info(" %u data frame dropped by offload after reorder", + stats_ptr->offload_msdu_reorder); + qdf_nofl_info(" %u MPDUs with SN in the past & within BA window", + stats_ptr->dup_past_within_window); + qdf_nofl_info(" %u MPDUs with SN in the past & outside BA window", + stats_ptr->dup_past_outside_window); +} + +static void +htt_t2h_stats_rx_rem_buf_stats_print( + struct rx_remote_buffer_mgmt_stats *stats_ptr, int concise) +{ + qdf_nofl_info("Rx Remote Buffer Statistics:"); + qdf_nofl_info(" %u MSDU's reaped for Rx processing", + stats_ptr->remote_reaped); + qdf_nofl_info(" %u MSDU's recycled within firmware", + stats_ptr->remote_recycled); + qdf_nofl_info(" %u MSDU's stored by Data Rx", + stats_ptr->data_rx_msdus_stored); + qdf_nofl_info(" %u HTT indications from WAL Rx MSDU", + stats_ptr->wal_rx_ind); + qdf_nofl_info(" %u HTT indications unconsumed from WAL Rx MSDU", + stats_ptr->wal_rx_ind_unconsumed); + qdf_nofl_info(" %u HTT indications from Data Rx MSDU", + stats_ptr->data_rx_ind); + qdf_nofl_info(" %u HTT indications unconsumed from Data Rx MSDU", + stats_ptr->data_rx_ind_unconsumed); + qdf_nofl_info(" %u HTT indications from ATHBUF", + stats_ptr->athbuf_rx_ind); + qdf_nofl_info(" %u Remote buffers requested for refill", + stats_ptr->refill_buf_req); + qdf_nofl_info(" %u Remote buffers filled by host", + stats_ptr->refill_buf_rsp); + qdf_nofl_info(" %u times MAC has no buffers", + stats_ptr->mac_no_bufs); + qdf_nofl_info(" %u times f/w write & read indices on MAC ring are equal", + stats_ptr->fw_indices_equal); + qdf_nofl_info(" %u times f/w has no remote buffers to post to MAC", + stats_ptr->host_no_bufs); +} + +static void +htt_t2h_stats_txbf_info_buf_stats_print( + struct wlan_dbg_txbf_data_stats *stats_ptr) +{ + qdf_nofl_info("TXBF data Statistics:"); + qdf_nofl_info("tx_txbf_vht (0..9): %u, %u, %u, %u, %u, %u, %u, %u, %u, %d", + stats_ptr->tx_txbf_vht[0], + stats_ptr->tx_txbf_vht[1], + stats_ptr->tx_txbf_vht[2], + stats_ptr->tx_txbf_vht[3], + stats_ptr->tx_txbf_vht[4], + stats_ptr->tx_txbf_vht[5], + stats_ptr->tx_txbf_vht[6], + stats_ptr->tx_txbf_vht[7], + stats_ptr->tx_txbf_vht[8], + stats_ptr->tx_txbf_vht[9]); + qdf_nofl_info("rx_txbf_vht (0..9): %u, %u, %u, %u, %u, %u, %u, %u, %u, %u", + stats_ptr->rx_txbf_vht[0], + stats_ptr->rx_txbf_vht[1], + stats_ptr->rx_txbf_vht[2], + stats_ptr->rx_txbf_vht[3], + stats_ptr->rx_txbf_vht[4], + stats_ptr->rx_txbf_vht[5], + stats_ptr->rx_txbf_vht[6], + stats_ptr->rx_txbf_vht[7], + stats_ptr->rx_txbf_vht[8], + stats_ptr->rx_txbf_vht[9]); + qdf_nofl_info("tx_txbf_ht (0..7): %u, %u, %u, %u, %u, %u, %u, %u", + stats_ptr->tx_txbf_ht[0], + stats_ptr->tx_txbf_ht[1], + stats_ptr->tx_txbf_ht[2], + stats_ptr->tx_txbf_ht[3], + stats_ptr->tx_txbf_ht[4], + stats_ptr->tx_txbf_ht[5], + stats_ptr->tx_txbf_ht[6], + stats_ptr->tx_txbf_ht[7]); + qdf_nofl_info("tx_txbf_ofdm (0..7): %u, %u, %u, %u, %u, %u, %u, %u", + stats_ptr->tx_txbf_ofdm[0], + stats_ptr->tx_txbf_ofdm[1], + stats_ptr->tx_txbf_ofdm[2], + stats_ptr->tx_txbf_ofdm[3], + stats_ptr->tx_txbf_ofdm[4], + stats_ptr->tx_txbf_ofdm[5], + stats_ptr->tx_txbf_ofdm[6], + stats_ptr->tx_txbf_ofdm[7]); + qdf_nofl_info("tx_txbf_cck (0..6): %u, %u, %u, %u, %u, %u, %u", + stats_ptr->tx_txbf_cck[0], + stats_ptr->tx_txbf_cck[1], + stats_ptr->tx_txbf_cck[2], + stats_ptr->tx_txbf_cck[3], + stats_ptr->tx_txbf_cck[4], + stats_ptr->tx_txbf_cck[5], + stats_ptr->tx_txbf_cck[6]); +} + +static void +htt_t2h_stats_txbf_snd_buf_stats_print( + struct wlan_dbg_txbf_snd_stats *stats_ptr) +{ + qdf_nofl_info("TXBF snd Buffer Statistics:"); + qdf_nofl_info("cbf_20: %u, %u, %u, %u", + stats_ptr->cbf_20[0], + stats_ptr->cbf_20[1], + stats_ptr->cbf_20[2], + stats_ptr->cbf_20[3]); + qdf_nofl_info("cbf_40: %u, %u, %u, %u", + stats_ptr->cbf_40[0], + stats_ptr->cbf_40[1], + stats_ptr->cbf_40[2], + stats_ptr->cbf_40[3]); + qdf_nofl_info("cbf_80: %u, %u, %u, %u", + stats_ptr->cbf_80[0], + stats_ptr->cbf_80[1], + stats_ptr->cbf_80[2], + stats_ptr->cbf_80[3]); + qdf_nofl_info("sounding: %u, %u, %u, %u, %u, %u, %u, %u, %u", + stats_ptr->sounding[0], + stats_ptr->sounding[1], + stats_ptr->sounding[2], + stats_ptr->sounding[3], + stats_ptr->sounding[4], + stats_ptr->sounding[5], + stats_ptr->sounding[6], + stats_ptr->sounding[7], + stats_ptr->sounding[8]); +} + +static void +htt_t2h_stats_tx_selfgen_buf_stats_print( + struct wlan_dbg_tx_selfgen_stats *stats_ptr) +{ + qdf_nofl_info("Tx selfgen Buffer Statistics:"); + qdf_nofl_info(" %u su_ndpa", + stats_ptr->su_ndpa); + qdf_nofl_info(" %u mu_ndp", + stats_ptr->mu_ndp); + qdf_nofl_info(" %u mu_ndpa", + stats_ptr->mu_ndpa); + qdf_nofl_info(" %u mu_ndp", + stats_ptr->mu_ndp); + qdf_nofl_info(" %u mu_brpoll_1", + stats_ptr->mu_brpoll_1); + qdf_nofl_info(" %u mu_brpoll_2", + stats_ptr->mu_brpoll_2); + qdf_nofl_info(" %u mu_bar_1", + stats_ptr->mu_bar_1); + qdf_nofl_info(" %u mu_bar_2", + stats_ptr->mu_bar_2); + qdf_nofl_info(" %u cts_burst", + stats_ptr->cts_burst); + qdf_nofl_info(" %u su_ndp_err", + stats_ptr->su_ndp_err); + qdf_nofl_info(" %u su_ndpa_err", + stats_ptr->su_ndpa_err); + qdf_nofl_info(" %u mu_ndp_err", + stats_ptr->mu_ndp_err); + qdf_nofl_info(" %u mu_brp1_err", + stats_ptr->mu_brp1_err); + qdf_nofl_info(" %u mu_brp2_err", + stats_ptr->mu_brp2_err); +} + +static void +htt_t2h_stats_wifi2_error_stats_print( + struct wlan_dbg_wifi2_error_stats *stats_ptr) +{ + int i; + + qdf_nofl_info("Scheduler error Statistics:"); + qdf_nofl_info("urrn_stats: "); + qdf_nofl_info("urrn_stats: %d, %d, %d", + stats_ptr->urrn_stats[0], + stats_ptr->urrn_stats[1], + stats_ptr->urrn_stats[2]); + qdf_nofl_info("flush_errs (0..%d): ", + WHAL_DBG_FLUSH_REASON_MAXCNT); + for (i = 0; i < WHAL_DBG_FLUSH_REASON_MAXCNT; i++) + qdf_nofl_info(" %u", stats_ptr->flush_errs[i]); + qdf_nofl_info("\n"); + qdf_nofl_info("schd_stall_errs (0..3): "); + qdf_nofl_info("%d, %d, %d, %d", + stats_ptr->schd_stall_errs[0], + stats_ptr->schd_stall_errs[1], + stats_ptr->schd_stall_errs[2], + stats_ptr->schd_stall_errs[3]); + qdf_nofl_info("schd_cmd_result (0..%d): ", + WHAL_DBG_CMD_RESULT_MAXCNT); + for (i = 0; i < WHAL_DBG_CMD_RESULT_MAXCNT; i++) + qdf_nofl_info(" %u", stats_ptr->schd_cmd_result[i]); + qdf_nofl_info("\n"); + qdf_nofl_info("sifs_status (0..%d): ", + WHAL_DBG_SIFS_STATUS_MAXCNT); + for (i = 0; i < WHAL_DBG_SIFS_STATUS_MAXCNT; i++) + qdf_nofl_info(" %u", stats_ptr->sifs_status[i]); + qdf_nofl_info("\n"); + qdf_nofl_info("phy_errs (0..%d): ", + WHAL_DBG_PHY_ERR_MAXCNT); + for (i = 0; i < WHAL_DBG_PHY_ERR_MAXCNT; i++) + qdf_nofl_info(" %u", stats_ptr->phy_errs[i]); + qdf_nofl_info("\n"); + qdf_nofl_info(" %u rx_rate_inval", + stats_ptr->rx_rate_inval); +} + +static void +htt_t2h_rx_musu_ndpa_pkts_stats_print( + struct rx_txbf_musu_ndpa_pkts_stats *stats_ptr) +{ + qdf_nofl_info("Rx TXBF MU/SU Packets and NDPA Statistics:"); + qdf_nofl_info(" %u Number of TXBF MU packets received", + stats_ptr->number_mu_pkts); + qdf_nofl_info(" %u Number of TXBF SU packets received", + stats_ptr->number_su_pkts); + qdf_nofl_info(" %u Number of TXBF directed NDPA", + stats_ptr->txbf_directed_ndpa_count); + qdf_nofl_info(" %u Number of TXBF retried NDPA", + stats_ptr->txbf_ndpa_retry_count); + qdf_nofl_info(" %u Total number of TXBF NDPA", + stats_ptr->txbf_total_ndpa_count); +} + +#define HTT_TICK_TO_USEC(ticks, microsec_per_tick) (ticks * microsec_per_tick) +static inline int htt_rate_flags_to_mhz(uint8_t rate_flags) +{ + if (rate_flags & 0x20) + return 40; /* WHAL_RC_FLAG_40MHZ */ + if (rate_flags & 0x40) + return 80; /* WHAL_RC_FLAG_80MHZ */ + if (rate_flags & 0x80) + return 160; /* WHAL_RC_FLAG_160MHZ */ + return 20; +} + +#define HTT_FW_STATS_MAX_BLOCK_ACK_WINDOW 64 + +static void +htt_t2h_tx_ppdu_bitmaps_pr(uint32_t *queued_ptr, uint32_t *acked_ptr) +{ + char queued_str[HTT_FW_STATS_MAX_BLOCK_ACK_WINDOW + 1]; + char acked_str[HTT_FW_STATS_MAX_BLOCK_ACK_WINDOW + 1]; + int i, j, word; + + qdf_mem_set(queued_str, HTT_FW_STATS_MAX_BLOCK_ACK_WINDOW, '0'); + qdf_mem_set(acked_str, HTT_FW_STATS_MAX_BLOCK_ACK_WINDOW, '-'); + i = 0; + for (word = 0; word < 2; word++) { + uint32_t queued = *(queued_ptr + word); + uint32_t acked = *(acked_ptr + word); + + for (j = 0; j < 32; j++, i++) { + if (queued & (1 << j)) { + queued_str[i] = '1'; + acked_str[i] = (acked & (1 << j)) ? 'y' : 'N'; + } + } + } + queued_str[HTT_FW_STATS_MAX_BLOCK_ACK_WINDOW] = '\0'; + acked_str[HTT_FW_STATS_MAX_BLOCK_ACK_WINDOW] = '\0'; + qdf_nofl_info("%s\n", queued_str); + qdf_nofl_info("%s\n", acked_str); +} + +static inline uint16_t htt_msg_read16(uint16_t *p16) +{ +#ifdef BIG_ENDIAN_HOST + /* + * During upload, the bytes within each uint32_t word were + * swapped by the HIF HW. This results in the lower and upper bytes + * of each uint16_t to be in the correct big-endian order with + * respect to each other, but for each even-index uint16_t to + * have its position switched with its successor neighbor uint16_t. + * Undo this uint16_t position swapping. + */ + return (((size_t) p16) & 0x2) ? *(p16 - 1) : *(p16 + 1); +#else + return *p16; +#endif +} + +static inline uint8_t htt_msg_read8(uint8_t *p8) +{ +#ifdef BIG_ENDIAN_HOST + /* + * During upload, the bytes within each uint32_t word were + * swapped by the HIF HW. + * Undo this byte swapping. + */ + switch (((size_t) p8) & 0x3) { + case 0: + return *(p8 + 3); + case 1: + return *(p8 + 1); + case 2: + return *(p8 - 1); + default /* 3 */: + return *(p8 - 3); + } +#else + return *p8; +#endif +} + +static void htt_make_u8_list_str(uint32_t *aligned_data, + char *buffer, int space, int max_elems) +{ + uint8_t *p8 = (uint8_t *) aligned_data; + char *buf_p = buffer; + + while (max_elems-- > 0) { + int bytes; + uint8_t val; + + val = htt_msg_read8(p8); + if (val == 0) + /* not enough data to fill the reserved msg buffer*/ + break; + + bytes = qdf_snprint(buf_p, space, "%d,", val); + space -= bytes; + if (space > 0) + buf_p += bytes; + else /* not enough print buffer space for all the data */ + break; + p8++; + } + if (buf_p == buffer) + *buf_p = '\0'; /* nothing was written */ + else + *(buf_p - 1) = '\0'; /* erase the final comma */ + +} + +static void htt_make_u16_list_str(uint32_t *aligned_data, + char *buffer, int space, int max_elems) +{ + uint16_t *p16 = (uint16_t *) aligned_data; + char *buf_p = buffer; + + while (max_elems-- > 0) { + int bytes; + uint16_t val; + + val = htt_msg_read16(p16); + if (val == 0) + /* not enough data to fill the reserved msg buffer */ + break; + bytes = qdf_snprint(buf_p, space, "%d,", val); + space -= bytes; + if (space > 0) + buf_p += bytes; + else /* not enough print buffer space for all the data */ + break; + + p16++; + } + if (buf_p == buffer) + *buf_p = '\0'; /* nothing was written */ + else + *(buf_p - 1) = '\0'; /* erase the final comma */ +} + +static void +htt_t2h_tx_ppdu_log_print(struct ol_fw_tx_dbg_ppdu_msg_hdr *hdr, + struct ol_fw_tx_dbg_ppdu_base *record, + int length, int concise) +{ + int i; + int record_size; + int calculated_record_size; + int num_records; + + record_size = sizeof(*record); + calculated_record_size = record_size + + hdr->mpdu_bytes_array_len * sizeof(uint16_t); + if (calculated_record_size < record_size) { + qdf_err("Overflow due to record and hdr->mpdu_bytes_array_len %u", + hdr->mpdu_bytes_array_len); + return; + } + record_size = calculated_record_size; + calculated_record_size += hdr->mpdu_msdus_array_len * sizeof(uint8_t); + if (calculated_record_size < record_size) { + qdf_err("Overflow due to hdr->mpdu_msdus_array_len %u", + hdr->mpdu_msdus_array_len); + return; + } + record_size = calculated_record_size; + calculated_record_size += hdr->msdu_bytes_array_len * sizeof(uint16_t); + if (calculated_record_size < record_size) { + qdf_err("Overflow due to hdr->msdu_bytes_array_len %u", + hdr->msdu_bytes_array_len); + return; + } + record_size = calculated_record_size; + num_records = (length - sizeof(*hdr)) / record_size; + if (num_records < 0) { + qdf_err("Underflow due to length %d", length); + return; + } + qdf_nofl_info("Tx PPDU log elements: num_records %d", num_records); + + for (i = 0; i < num_records; i++) { + uint16_t start_seq_num; + uint16_t start_pn_lsbs; + uint8_t num_mpdus; + uint16_t peer_id; + uint8_t ext_tid; + uint8_t rate_code; + uint8_t rate_flags; + uint8_t tries; + uint8_t complete; + uint32_t time_enqueue_us; + uint32_t time_completion_us; + uint32_t *msg_word = (uint32_t *) record; + + /* fields used for both concise and complete printouts */ + start_seq_num = + ((*(msg_word + OL_FW_TX_DBG_PPDU_START_SEQ_NUM_WORD)) & + OL_FW_TX_DBG_PPDU_START_SEQ_NUM_M) >> + OL_FW_TX_DBG_PPDU_START_SEQ_NUM_S; + complete = + ((*(msg_word + OL_FW_TX_DBG_PPDU_COMPLETE_WORD)) & + OL_FW_TX_DBG_PPDU_COMPLETE_M) >> + OL_FW_TX_DBG_PPDU_COMPLETE_S; + + /* fields used only for complete printouts */ + if (!concise) { +#define BUF_SIZE 80 + char buf[BUF_SIZE]; + uint8_t *p8; + uint8_t *calculated_p8; + + time_enqueue_us = + HTT_TICK_TO_USEC(record->timestamp_enqueue, + hdr->microsec_per_tick); + time_completion_us = + HTT_TICK_TO_USEC(record->timestamp_completion, + hdr->microsec_per_tick); + + start_pn_lsbs = + ((*(msg_word + + OL_FW_TX_DBG_PPDU_START_PN_LSBS_WORD)) & + OL_FW_TX_DBG_PPDU_START_PN_LSBS_M) >> + OL_FW_TX_DBG_PPDU_START_PN_LSBS_S; + num_mpdus = + ((*(msg_word + + OL_FW_TX_DBG_PPDU_NUM_MPDUS_WORD))& + OL_FW_TX_DBG_PPDU_NUM_MPDUS_M) >> + OL_FW_TX_DBG_PPDU_NUM_MPDUS_S; + peer_id = + ((*(msg_word + + OL_FW_TX_DBG_PPDU_PEER_ID_WORD)) & + OL_FW_TX_DBG_PPDU_PEER_ID_M) >> + OL_FW_TX_DBG_PPDU_PEER_ID_S; + ext_tid = + ((*(msg_word + + OL_FW_TX_DBG_PPDU_EXT_TID_WORD)) & + OL_FW_TX_DBG_PPDU_EXT_TID_M) >> + OL_FW_TX_DBG_PPDU_EXT_TID_S; + rate_code = + ((*(msg_word + + OL_FW_TX_DBG_PPDU_RATE_CODE_WORD))& + OL_FW_TX_DBG_PPDU_RATE_CODE_M) >> + OL_FW_TX_DBG_PPDU_RATE_CODE_S; + rate_flags = + ((*(msg_word + + OL_FW_TX_DBG_PPDU_RATE_FLAGS_WORD))& + OL_FW_TX_DBG_PPDU_RATE_FLAGS_M) >> + OL_FW_TX_DBG_PPDU_RATE_FLAGS_S; + tries = + ((*(msg_word + + OL_FW_TX_DBG_PPDU_TRIES_WORD)) & + OL_FW_TX_DBG_PPDU_TRIES_M) >> + OL_FW_TX_DBG_PPDU_TRIES_S; + + qdf_nofl_info(" - PPDU tx to peer %d, TID %d", peer_id, + ext_tid); + qdf_nofl_info(" start seq num= %u, start PN LSBs= %#04x", + start_seq_num, start_pn_lsbs); + qdf_nofl_info(" PPDU: %d MPDUs, (?) MSDUs, %d bytes", + num_mpdus, + /* num_msdus-not yet computed in target */ + record->num_bytes); + if (complete) { + qdf_nofl_info(" enqueued: %u, completed: %u usec)", + time_enqueue_us, + time_completion_us); + qdf_nofl_info(" %d tries, last tx used rate %d ", + tries, rate_code); + qdf_nofl_info("on %d MHz chan (flags = %#x)", + htt_rate_flags_to_mhz + (rate_flags), rate_flags); + qdf_nofl_info(" enqueued and acked MPDU bitmaps:"); + htt_t2h_tx_ppdu_bitmaps_pr(msg_word + + OL_FW_TX_DBG_PPDU_ENQUEUED_LSBS_WORD, + msg_word + + OL_FW_TX_DBG_PPDU_BLOCK_ACK_LSBS_WORD); + } else { + qdf_nofl_info(" enqueued: %d us, not yet completed", + time_enqueue_us); + } + /* skip the regular msg fields to reach the tail area */ + p8 = (uint8_t *) record; + calculated_p8 = p8 + sizeof(struct ol_fw_tx_dbg_ppdu_base); + if (calculated_p8 < p8) { + qdf_err("Overflow due to record %pK", p8); + continue; + } + p8 = calculated_p8; + if (hdr->mpdu_bytes_array_len) { + htt_make_u16_list_str((uint32_t *) p8, buf, + BUF_SIZE, + hdr-> + mpdu_bytes_array_len); + qdf_nofl_info(" MPDU bytes: %s", buf); + } + calculated_p8 += hdr->mpdu_bytes_array_len * sizeof(uint16_t); + if (calculated_p8 < p8) { + qdf_err("Overflow due to hdr->mpdu_bytes_array_len %u", + hdr->mpdu_bytes_array_len); + continue; + } + p8 = calculated_p8; + if (hdr->mpdu_msdus_array_len) { + htt_make_u8_list_str((uint32_t *) p8, buf, + BUF_SIZE, + hdr->mpdu_msdus_array_len); + qdf_nofl_info(" MPDU MSDUs: %s", buf); + } + calculated_p8 += hdr->mpdu_msdus_array_len * sizeof(uint8_t); + if (calculated_p8 < p8) { + qdf_err("Overflow due to hdr->mpdu_msdus_array_len %u", + hdr->mpdu_msdus_array_len); + continue; + } + p8 = calculated_p8; + if (hdr->msdu_bytes_array_len) { + htt_make_u16_list_str((uint32_t *) p8, buf, + BUF_SIZE, + hdr-> + msdu_bytes_array_len); + qdf_nofl_info(" MSDU bytes: %s", buf); + } + } else { + /* concise */ + qdf_nofl_info("start seq num = %u ", start_seq_num); + qdf_nofl_info("enqueued and acked MPDU bitmaps:"); + if (complete) { + htt_t2h_tx_ppdu_bitmaps_pr(msg_word + + OL_FW_TX_DBG_PPDU_ENQUEUED_LSBS_WORD, + msg_word + + OL_FW_TX_DBG_PPDU_BLOCK_ACK_LSBS_WORD); + } else { + qdf_nofl_info("(not completed)"); + } + } + record = (struct ol_fw_tx_dbg_ppdu_base *) + (((uint8_t *) record) + record_size); + } +} + +static void htt_t2h_stats_tidq_stats_print( + struct wlan_dbg_tidq_stats *tidq_stats, int concise) +{ + qdf_nofl_info("TID QUEUE STATS:"); + qdf_nofl_info("tid_txq_stats: %u", tidq_stats->wlan_dbg_tid_txq_status); + qdf_nofl_info("num_pkts_queued(0..9):"); + qdf_nofl_info("%u, %u, %u, %u, %u, %u, %u, %u, %u, %u", + tidq_stats->txq_st.num_pkts_queued[0], + tidq_stats->txq_st.num_pkts_queued[1], + tidq_stats->txq_st.num_pkts_queued[2], + tidq_stats->txq_st.num_pkts_queued[3], + tidq_stats->txq_st.num_pkts_queued[4], + tidq_stats->txq_st.num_pkts_queued[5], + tidq_stats->txq_st.num_pkts_queued[6], + tidq_stats->txq_st.num_pkts_queued[7], + tidq_stats->txq_st.num_pkts_queued[8], + tidq_stats->txq_st.num_pkts_queued[9]); + qdf_nofl_info("tid_hw_qdepth(0..19):"); + qdf_nofl_info("%u, %u, %u, %u, %u, %u, %u, %u, %u, %u", + tidq_stats->txq_st.tid_hw_qdepth[0], + tidq_stats->txq_st.tid_hw_qdepth[1], + tidq_stats->txq_st.tid_hw_qdepth[2], + tidq_stats->txq_st.tid_hw_qdepth[3], + tidq_stats->txq_st.tid_hw_qdepth[4], + tidq_stats->txq_st.tid_hw_qdepth[5], + tidq_stats->txq_st.tid_hw_qdepth[6], + tidq_stats->txq_st.tid_hw_qdepth[7], + tidq_stats->txq_st.tid_hw_qdepth[8], + tidq_stats->txq_st.tid_hw_qdepth[9]); + qdf_nofl_info("%u, %u, %u, %u, %u, %u, %u, %u, %u, %u", + tidq_stats->txq_st.tid_hw_qdepth[10], + tidq_stats->txq_st.tid_hw_qdepth[11], + tidq_stats->txq_st.tid_hw_qdepth[12], + tidq_stats->txq_st.tid_hw_qdepth[13], + tidq_stats->txq_st.tid_hw_qdepth[14], + tidq_stats->txq_st.tid_hw_qdepth[15], + tidq_stats->txq_st.tid_hw_qdepth[16], + tidq_stats->txq_st.tid_hw_qdepth[17], + tidq_stats->txq_st.tid_hw_qdepth[18], + tidq_stats->txq_st.tid_hw_qdepth[19]); + qdf_nofl_info("tid_sw_qdepth(0..19):"); + qdf_nofl_info("%u, %u, %u, %u, %u, %u, %u, %u, %u, %u", + tidq_stats->txq_st.tid_sw_qdepth[0], + tidq_stats->txq_st.tid_sw_qdepth[1], + tidq_stats->txq_st.tid_sw_qdepth[2], + tidq_stats->txq_st.tid_sw_qdepth[3], + tidq_stats->txq_st.tid_sw_qdepth[4], + tidq_stats->txq_st.tid_sw_qdepth[5], + tidq_stats->txq_st.tid_sw_qdepth[6], + tidq_stats->txq_st.tid_sw_qdepth[7], + tidq_stats->txq_st.tid_sw_qdepth[8], + tidq_stats->txq_st.tid_sw_qdepth[9]); + qdf_nofl_info("%u, %u, %u, %u, %u, %u, %u, %u, %u, %u", + tidq_stats->txq_st.tid_sw_qdepth[10], + tidq_stats->txq_st.tid_sw_qdepth[11], + tidq_stats->txq_st.tid_sw_qdepth[12], + tidq_stats->txq_st.tid_sw_qdepth[13], + tidq_stats->txq_st.tid_sw_qdepth[14], + tidq_stats->txq_st.tid_sw_qdepth[15], + tidq_stats->txq_st.tid_sw_qdepth[16], + tidq_stats->txq_st.tid_sw_qdepth[17], + tidq_stats->txq_st.tid_sw_qdepth[18], + tidq_stats->txq_st.tid_sw_qdepth[19]); +} + +static void htt_t2h_stats_tx_mu_stats_print( + struct wlan_dbg_tx_mu_stats *tx_mu_stats, int concise) +{ + qdf_nofl_info("TX MU STATS:"); + qdf_nofl_info("mu_sch_nusers_2: %u", tx_mu_stats->mu_sch_nusers_2); + qdf_nofl_info("mu_sch_nusers_3: %u", tx_mu_stats->mu_sch_nusers_3); + qdf_nofl_info("mu_mpdus_queued_usr: %u, %u, %u, %u", + tx_mu_stats->mu_mpdus_queued_usr[0], + tx_mu_stats->mu_mpdus_queued_usr[1], + tx_mu_stats->mu_mpdus_queued_usr[2], + tx_mu_stats->mu_mpdus_queued_usr[3]); + qdf_nofl_info("mu_mpdus_tried_usr: %u, %u, %u, %u", + tx_mu_stats->mu_mpdus_tried_usr[0], + tx_mu_stats->mu_mpdus_tried_usr[1], + tx_mu_stats->mu_mpdus_tried_usr[2], + tx_mu_stats->mu_mpdus_tried_usr[3]); + qdf_nofl_info("mu_mpdus_failed_usr: %u, %u, %u, %u", + tx_mu_stats->mu_mpdus_failed_usr[0], + tx_mu_stats->mu_mpdus_failed_usr[1], + tx_mu_stats->mu_mpdus_failed_usr[2], + tx_mu_stats->mu_mpdus_failed_usr[3]); + qdf_nofl_info("mu_mpdus_requeued_usr: %u, %u, %u, %u", + tx_mu_stats->mu_mpdus_requeued_usr[0], + tx_mu_stats->mu_mpdus_requeued_usr[1], + tx_mu_stats->mu_mpdus_requeued_usr[2], + tx_mu_stats->mu_mpdus_requeued_usr[3]); + qdf_nofl_info("mu_err_no_ba_usr: %u, %u, %u, %u", + tx_mu_stats->mu_err_no_ba_usr[0], + tx_mu_stats->mu_err_no_ba_usr[1], + tx_mu_stats->mu_err_no_ba_usr[2], + tx_mu_stats->mu_err_no_ba_usr[3]); + qdf_nofl_info("mu_mpdu_underrun_usr: %u, %u, %u, %u", + tx_mu_stats->mu_mpdu_underrun_usr[0], + tx_mu_stats->mu_mpdu_underrun_usr[1], + tx_mu_stats->mu_mpdu_underrun_usr[2], + tx_mu_stats->mu_mpdu_underrun_usr[3]); + qdf_nofl_info("mu_ampdu_underrun_usr: %u, %u, %u, %u", + tx_mu_stats->mu_ampdu_underrun_usr[0], + tx_mu_stats->mu_ampdu_underrun_usr[1], + tx_mu_stats->mu_ampdu_underrun_usr[2], + tx_mu_stats->mu_ampdu_underrun_usr[3]); + +} + +static void htt_t2h_stats_sifs_resp_stats_print( + struct wlan_dbg_sifs_resp_stats *sifs_stats, int concise) +{ + qdf_nofl_info("SIFS RESP STATS:"); + qdf_nofl_info("num of ps-poll trigger frames: %u", + sifs_stats->ps_poll_trigger); + qdf_nofl_info("num of uapsd trigger frames: %u", + sifs_stats->uapsd_trigger); + qdf_nofl_info("num of data trigger frames: %u, %u", + sifs_stats->qb_data_trigger[0], + sifs_stats->qb_data_trigger[1]); + qdf_nofl_info("num of bar trigger frames: %u, %u", + sifs_stats->qb_bar_trigger[0], + sifs_stats->qb_bar_trigger[1]); + qdf_nofl_info("num of ppdu transmitted at SIFS interval: %u", + sifs_stats->sifs_resp_data); + qdf_nofl_info("num of ppdu failed to meet SIFS resp timing: %u", + sifs_stats->sifs_resp_err); +} + +void htt_t2h_stats_print(uint8_t *stats_data, int concise) +{ + uint32_t *msg_word = (uint32_t *) stats_data; + enum htt_dbg_stats_type type; + enum htt_dbg_stats_status status; + int length; + + type = HTT_T2H_STATS_CONF_TLV_TYPE_GET(*msg_word); + status = HTT_T2H_STATS_CONF_TLV_STATUS_GET(*msg_word); + length = HTT_T2H_STATS_CONF_TLV_LENGTH_GET(*msg_word); + + /* check that we've been given a valid stats type */ + if (status == HTT_DBG_STATS_STATUS_SERIES_DONE) { + return; + } else if (status == HTT_DBG_STATS_STATUS_INVALID) { + qdf_debug("Target doesn't support stats type %d", type); + return; + } else if (status == HTT_DBG_STATS_STATUS_ERROR) { + qdf_debug("Target couldn't upload stats type %d (no mem?)", + type); + return; + } + /* got valid (though perhaps partial) stats - process them */ + switch (type) { + case HTT_DBG_STATS_WAL_PDEV_TXRX: + { + struct wlan_dbg_stats *wlan_dbg_stats_ptr; + + wlan_dbg_stats_ptr = + (struct wlan_dbg_stats *)(msg_word + 1); + htt_t2h_stats_pdev_stats_print(wlan_dbg_stats_ptr, + concise); + break; + } + case HTT_DBG_STATS_RX_REORDER: + { + struct rx_reorder_stats *rx_reorder_stats_ptr; + + rx_reorder_stats_ptr = + (struct rx_reorder_stats *)(msg_word + 1); + htt_t2h_stats_rx_reorder_stats_print + (rx_reorder_stats_ptr, concise); + break; + } + + case HTT_DBG_STATS_RX_RATE_INFO: + { + wlan_dbg_rx_rate_info_t *rx_phy_info; + + rx_phy_info = (wlan_dbg_rx_rate_info_t *) (msg_word + 1); + htt_t2h_stats_rx_rate_stats_print(rx_phy_info, concise); + break; + } + case HTT_DBG_STATS_RX_RATE_INFO_V2: + { + wlan_dbg_rx_rate_info_v2_t *rx_phy_info; + + rx_phy_info = (wlan_dbg_rx_rate_info_v2_t *) (msg_word + 1); + htt_t2h_stats_rx_rate_stats_print_v2(rx_phy_info, concise); + break; + } + case HTT_DBG_STATS_TX_PPDU_LOG: + { + struct ol_fw_tx_dbg_ppdu_msg_hdr *hdr; + struct ol_fw_tx_dbg_ppdu_base *record; + + if (status == HTT_DBG_STATS_STATUS_PARTIAL + && length == 0) { + qdf_debug("HTT_DBG_STATS_TX_PPDU_LOG -- length = 0!"); + break; + } + hdr = (struct ol_fw_tx_dbg_ppdu_msg_hdr *)(msg_word + 1); + record = (struct ol_fw_tx_dbg_ppdu_base *)(hdr + 1); + htt_t2h_tx_ppdu_log_print(hdr, record, length, concise); + } + break; + case HTT_DBG_STATS_TX_RATE_INFO: + { + wlan_dbg_tx_rate_info_t *tx_rate_info; + + tx_rate_info = (wlan_dbg_tx_rate_info_t *) (msg_word + 1); + htt_t2h_stats_tx_rate_stats_print(tx_rate_info, concise); + break; + } + case HTT_DBG_STATS_TX_RATE_INFO_V2: + { + wlan_dbg_tx_rate_info_v2_t *tx_rate_info; + + tx_rate_info = (wlan_dbg_tx_rate_info_v2_t *) (msg_word + 1); + htt_t2h_stats_tx_rate_stats_print_v2(tx_rate_info, concise); + break; + } + case HTT_DBG_STATS_RX_REMOTE_RING_BUFFER_INFO: + { + struct rx_remote_buffer_mgmt_stats *rx_rem_buf; + + rx_rem_buf = + (struct rx_remote_buffer_mgmt_stats *)(msg_word + 1); + htt_t2h_stats_rx_rem_buf_stats_print(rx_rem_buf, concise); + break; + } + case HTT_DBG_STATS_TXBF_INFO: + { + struct wlan_dbg_txbf_data_stats *txbf_info_buf; + + txbf_info_buf = + (struct wlan_dbg_txbf_data_stats *)(msg_word + 1); + htt_t2h_stats_txbf_info_buf_stats_print(txbf_info_buf); + break; + } + case HTT_DBG_STATS_SND_INFO: + { + struct wlan_dbg_txbf_snd_stats *txbf_snd_buf; + + txbf_snd_buf = (struct wlan_dbg_txbf_snd_stats *)(msg_word + 1); + htt_t2h_stats_txbf_snd_buf_stats_print(txbf_snd_buf); + break; + } + case HTT_DBG_STATS_TX_SELFGEN_INFO: + { + struct wlan_dbg_tx_selfgen_stats *tx_selfgen_buf; + + tx_selfgen_buf = + (struct wlan_dbg_tx_selfgen_stats *)(msg_word + 1); + htt_t2h_stats_tx_selfgen_buf_stats_print(tx_selfgen_buf); + break; + } + case HTT_DBG_STATS_ERROR_INFO: + { + struct wlan_dbg_wifi2_error_stats *wifi2_error_buf; + + wifi2_error_buf = + (struct wlan_dbg_wifi2_error_stats *)(msg_word + 1); + htt_t2h_stats_wifi2_error_stats_print(wifi2_error_buf); + break; + } + case HTT_DBG_STATS_TXBF_MUSU_NDPA_PKT: + { + struct rx_txbf_musu_ndpa_pkts_stats *rx_musu_ndpa_stats; + + rx_musu_ndpa_stats = (struct rx_txbf_musu_ndpa_pkts_stats *) + (msg_word + 1); + htt_t2h_rx_musu_ndpa_pkts_stats_print(rx_musu_ndpa_stats); + break; + } + case HTT_DBG_STATS_TIDQ: + { + struct wlan_dbg_tidq_stats *tidq_stats; + + tidq_stats = (struct wlan_dbg_tidq_stats *)(msg_word + 1); + htt_t2h_stats_tidq_stats_print(tidq_stats, concise); + break; + } + case HTT_DBG_STATS_TX_MU_INFO: + { + struct wlan_dbg_tx_mu_stats *tx_mu_stats; + + tx_mu_stats = (struct wlan_dbg_tx_mu_stats *)(msg_word + 1); + htt_t2h_stats_tx_mu_stats_print(tx_mu_stats, concise); + break; + } + case HTT_DBG_STATS_SIFS_RESP_INFO: + { + struct wlan_dbg_sifs_resp_stats *sifs_stats; + + sifs_stats = (struct wlan_dbg_sifs_resp_stats *)(msg_word + 1); + htt_t2h_stats_sifs_resp_stats_print(sifs_stats, concise); + break; + } + default: + break; + } +} diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_h2t.c b/drivers/staging/qcacld-3.0/core/dp/htt/htt_h2t.c new file mode 100644 index 0000000000000000000000000000000000000000..a2a4051dc88edd28ee2a92fc2b10fdbef5735e67 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_h2t.c @@ -0,0 +1,1531 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file htt_h2t.c + * @brief Provide functions to send host->target HTT messages. + * @details + * This file contains functions related to host->target HTT messages. + * There are a couple aspects of this host->target messaging: + * 1. This file contains the function that is called by HTC when + * a host->target send completes. + * This send-completion callback is primarily relevant to HL, + * to invoke the download scheduler to set up a new download, + * and optionally free the tx frame whose download is completed. + * For both HL and LL, this completion callback frees up the + * HTC_PACKET object used to specify the download. + * 2. This file contains functions for creating messages to send + * from the host to the target. + */ + +#include /* qdf_mem_copy */ +#include /* qdf_nbuf_map_single */ +#include /* HTC_PACKET */ +#include /* HTC_HDR_ALIGNMENT_PADDING */ +#include /* HTT host->target msg defs */ +#include /* HTT host->target WDI IPA msg defs */ +#include /* ol_tx_completion_handler, htt_tx_status */ +#include +#include +#include +#include + +#include +#include + +#define HTT_MSG_BUF_SIZE(msg_bytes) \ + ((msg_bytes) + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING) + +#ifndef container_of +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr) - (char *)(&((type *)0)->member))) +#endif + +#ifdef ATH_11AC_TXCOMPACT +#define HTT_SEND_HTC_PKT(pdev, pkt) \ +do { \ + if (htc_send_pkt(pdev->htc_pdev, &pkt->htc_pkt) == \ + QDF_STATUS_SUCCESS) { \ + htt_htc_misc_pkt_list_add(pdev, pkt); \ + } else { \ + qdf_nbuf_free((qdf_nbuf_t)(pkt->htc_pkt.pNetBufContext)); \ + htt_htc_pkt_free(pdev, pkt); \ + } \ +} while (0) +#else +#define HTT_SEND_HTC_PKT(pdev, ppkt) \ + htc_send_pkt(pdev->htc_pdev, &pkt->htc_pkt); +#endif + + +static void +htt_h2t_send_complete_free_netbuf(void *pdev, QDF_STATUS status, + qdf_nbuf_t netbuf, uint16_t msdu_id) +{ + qdf_nbuf_free(netbuf); +} + +#ifndef QCN7605_SUPPORT +static void htt_t2h_adjust_bus_target_delta(struct htt_pdev_t *pdev) +{ + int32_t credit_delta; + + if (pdev->cfg.is_high_latency && !pdev->cfg.default_tx_comp_req) { + HTT_TX_MUTEX_ACQUIRE(&pdev->credit_mutex); + qdf_atomic_add(1, &pdev->htt_tx_credit.bus_delta); + credit_delta = htt_tx_credit_update(pdev); + HTT_TX_MUTEX_RELEASE(&pdev->credit_mutex); + + if (credit_delta) + ol_tx_credit_completion_handler(pdev->txrx_pdev, + credit_delta); + } +} +#else +static void htt_t2h_adjust_bus_target_delta(struct htt_pdev_t *pdev) +{ + /* UNPAUSE OS Q */ + ol_tx_flow_ct_unpause_os_q(pdev->txrx_pdev); +} +#endif + +void htt_h2t_send_complete(void *context, HTC_PACKET *htc_pkt) +{ + void (*send_complete_part2)(void *pdev, QDF_STATUS status, + qdf_nbuf_t msdu, uint16_t msdu_id); + struct htt_pdev_t *pdev = (struct htt_pdev_t *)context; + struct htt_htc_pkt *htt_pkt; + qdf_nbuf_t netbuf; + + send_complete_part2 = htc_pkt->pPktContext; + + htt_pkt = container_of(htc_pkt, struct htt_htc_pkt, htc_pkt); + + /* process (free or keep) the netbuf that held the message */ + netbuf = (qdf_nbuf_t) htc_pkt->pNetBufContext; + if (send_complete_part2) { + send_complete_part2(htt_pkt->pdev_ctxt, htc_pkt->Status, netbuf, + htt_pkt->msdu_id); + } + + htt_t2h_adjust_bus_target_delta(pdev); + /* free the htt_htc_pkt / HTC_PACKET object */ + htt_htc_pkt_free(pdev, htt_pkt); +} + +enum htc_send_full_action htt_h2t_full(void *context, HTC_PACKET *pkt) +{ +/* FIX THIS */ + return HTC_SEND_FULL_KEEP; +} + +#if defined(HELIUMPLUS) +QDF_STATUS htt_h2t_frag_desc_bank_cfg_msg(struct htt_pdev_t *pdev) +{ + QDF_STATUS rc = QDF_STATUS_SUCCESS; + + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + u_int32_t *msg_word; + struct htt_tx_frag_desc_bank_cfg_t *bank_cfg; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return QDF_STATUS_E_FAILURE; /* failure */ + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + msg = qdf_nbuf_alloc( + pdev->osdev, + HTT_MSG_BUF_SIZE(sizeof(struct htt_tx_frag_desc_bank_cfg_t)), + /* reserve room for the HTC header */ + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, true); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return QDF_STATUS_E_FAILURE; /* failure */ + } + + /* + * Set the length of the message. + * The contribution from the HTC_HDR_ALIGNMENT_PADDING is added + * separately during the below call to adf_nbuf_push_head. + * The contribution from the HTC header is added separately inside HTC. + */ + qdf_nbuf_put_tail(msg, sizeof(struct htt_tx_frag_desc_bank_cfg_t)); + + /* fill in the message contents */ + msg_word = (u_int32_t *) qdf_nbuf_data(msg); + + memset(msg_word, 0, sizeof(struct htt_tx_frag_desc_bank_cfg_t)); + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG); + + bank_cfg = (struct htt_tx_frag_desc_bank_cfg_t *)msg_word; + + /** @note @todo Hard coded to 0 Assuming just one pdev for now.*/ + HTT_H2T_FRAG_DESC_BANK_PDEVID_SET(*msg_word, 0); + /** @note Hard coded to 1.*/ + HTT_H2T_FRAG_DESC_BANK_NUM_BANKS_SET(*msg_word, 1); + HTT_H2T_FRAG_DESC_BANK_DESC_SIZE_SET(*msg_word, pdev->frag_descs.size); + HTT_H2T_FRAG_DESC_BANK_SWAP_SET(*msg_word, 0); + + /** Bank specific data structure.*/ +#if HTT_PADDR64 + bank_cfg->bank_base_address[0].lo = qdf_get_lower_32_bits( + pdev->frag_descs.desc_pages.dma_pages->page_p_addr); + bank_cfg->bank_base_address[0].hi = qdf_get_upper_32_bits( + pdev->frag_descs.desc_pages.dma_pages->page_p_addr); +#else /* ! HTT_PADDR64 */ + bank_cfg->bank_base_address[0] = + pdev->frag_descs.desc_pages.dma_pages->page_p_addr; +#endif /* HTT_PADDR64 */ + /* Logical Min index */ + HTT_H2T_FRAG_DESC_BANK_MIN_IDX_SET(bank_cfg->bank_info[0], 0); + /* Logical Max index */ + HTT_H2T_FRAG_DESC_BANK_MAX_IDX_SET(bank_cfg->bank_info[0], + pdev->frag_descs.pool_elems-1); + + SET_HTC_PACKET_INFO_TX( + &pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + + rc = htc_send_pkt(pdev->htc_pdev, &pkt->htc_pkt); +#ifdef ATH_11AC_TXCOMPACT + if (rc == QDF_STATUS_SUCCESS) { + htt_htc_misc_pkt_list_add(pdev, pkt); + } else { + qdf_nbuf_free(msg); + htt_htc_pkt_free(pdev, pkt); + } +#endif + + return rc; +} + +#endif /* defined(HELIUMPLUS) */ + +QDF_STATUS htt_h2t_ver_req_msg(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + uint32_t msg_size; + uint32_t max_tx_group; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return QDF_STATUS_E_FAILURE; /* failure */ + + max_tx_group = ol_tx_get_max_tx_groups_supported(pdev->txrx_pdev); + + if (max_tx_group) + msg_size = HTT_VER_REQ_BYTES + + sizeof(struct htt_option_tlv_mac_tx_queue_groups_t); + else + msg_size = HTT_VER_REQ_BYTES; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for the HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, HTT_MSG_BUF_SIZE(msg_size), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + true); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return QDF_STATUS_E_FAILURE; /* failure */ + } + + /* + * Set the length of the message. + * The contribution from the HTC_HDR_ALIGNMENT_PADDING is added + * separately during the below call to qdf_nbuf_push_head. + * The contribution from the HTC header is added separately inside HTC. + */ + qdf_nbuf_put_tail(msg, msg_size); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_VERSION_REQ); + + if (max_tx_group) { + *(msg_word + 1) = 0; + + /* Fill Group Info */ + HTT_OPTION_TLV_TAG_SET(*(msg_word+1), + HTT_OPTION_TLV_TAG_MAX_TX_QUEUE_GROUPS); + HTT_OPTION_TLV_LENGTH_SET(*(msg_word+1), + (sizeof(struct htt_option_tlv_mac_tx_queue_groups_t)/ + sizeof(uint32_t))); + HTT_OPTION_TLV_VALUE0_SET(*(msg_word+1), max_tx_group); + } + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + + ol_tx_deduct_one_credit(pdev->txrx_pdev); + + return QDF_STATUS_SUCCESS; +} + +#if defined(HELIUMPLUS) +/** + * htt_h2t_rx_ring_rfs_cfg_msg_ll() - Configure receive flow steering + * @pdev: handle to the HTT instance + * + * Return: QDF_STATUS_SUCCESS on success + * A_NO_MEMORY No memory fail + */ +QDF_STATUS htt_h2t_rx_ring_rfs_cfg_msg_ll(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + uint32_t msg_local; + struct cds_config_info *cds_cfg; + + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "Receive flow steering configuration, disable gEnableFlowSteering(=0) in ini if FW doesnot support it\n"); + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return QDF_STATUS_E_NOMEM; /* failure */ + + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for the HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_RFS_CFG_REQ_BYTES), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + true); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return QDF_STATUS_E_NOMEM; /* failure */ + } + /* + * Set the length of the message. + * The contribution from the HTC_HDR_ALIGNMENT_PADDING is added + * separately during the below call to qdf_nbuf_push_head. + * The contribution from the HTC header is added separately inside HTC. + */ + qdf_nbuf_put_tail(msg, HTT_RFS_CFG_REQ_BYTES); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + msg_local = 0; + HTT_H2T_MSG_TYPE_SET(msg_local, HTT_H2T_MSG_TYPE_RFS_CONFIG); + if (ol_cfg_is_flow_steering_enabled(pdev->ctrl_pdev)) { + HTT_RX_RFS_CONFIG_SET(msg_local, 1); + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "Enable Rx flow steering"); + } else { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "Disable Rx flow steering"); + } + cds_cfg = cds_get_ini_config(); + if (cds_cfg) { + msg_local |= ((cds_cfg->max_msdus_per_rxinorderind & 0xff) + << 16); + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "Updated maxMSDUsPerRxInd"); + } + + *msg_word = msg_local; + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "%s: Sending msg_word: 0x%08x", + __func__, *msg_word); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return QDF_STATUS_SUCCESS; +} +#else +/** + * htt_h2t_rx_ring_rfs_cfg_msg_ll() - Configure receive flow steering + * @pdev: handle to the HTT instance + * + * Return: QDF_STATUS_SUCCESS on success + * A_NO_MEMORY No memory fail + */ +QDF_STATUS htt_h2t_rx_ring_rfs_cfg_msg_ll(struct htt_pdev_t *pdev) +{ + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "Doesnot support receive flow steering configuration\n"); + return QDF_STATUS_SUCCESS; +} +#endif /* HELIUMPLUS */ + +QDF_STATUS htt_h2t_rx_ring_cfg_msg_ll(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + int enable_ctrl_data, enable_mgmt_data, + enable_null_data, enable_phy_data, enable_hdr, + enable_ppdu_start, enable_ppdu_end; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return QDF_STATUS_E_FAILURE; /* failure */ + + /* + * show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for the HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_RX_RING_CFG_BYTES(1)), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + true); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return QDF_STATUS_E_FAILURE; /* failure */ + } + /* + * Set the length of the message. + * The contribution from the HTC_HDR_ALIGNMENT_PADDING is added + * separately during the below call to qdf_nbuf_push_head. + * The contribution from the HTC header is added separately inside HTC. + */ + qdf_nbuf_put_tail(msg, HTT_RX_RING_CFG_BYTES(1)); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_RX_RING_CFG); + HTT_RX_RING_CFG_NUM_RINGS_SET(*msg_word, 1); + + msg_word++; + *msg_word = 0; +#if HTT_PADDR64 + HTT_RX_RING_CFG_IDX_SHADOW_REG_PADDR_LO_SET( + *msg_word, + qdf_get_lower_32_bits(pdev->rx_ring.alloc_idx.paddr)); + msg_word++; + HTT_RX_RING_CFG_IDX_SHADOW_REG_PADDR_HI_SET( + *msg_word, + qdf_get_upper_32_bits(pdev->rx_ring.alloc_idx.paddr)); +#else /* ! HTT_PADDR64 */ + HTT_RX_RING_CFG_IDX_SHADOW_REG_PADDR_SET(*msg_word, + pdev->rx_ring.alloc_idx.paddr); +#endif /* HTT_PADDR64 */ + + msg_word++; + *msg_word = 0; +#if HTT_PADDR64 + HTT_RX_RING_CFG_BASE_PADDR_LO_SET(*msg_word, + pdev->rx_ring.base_paddr); + { + uint32_t tmp; + + tmp = qdf_get_upper_32_bits(pdev->rx_ring.base_paddr); + if (tmp & 0xfffffe0) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "%s:%d paddr > 37 bits!. Trimmed.", + __func__, __LINE__); + tmp &= 0x01f; + } + + + msg_word++; + HTT_RX_RING_CFG_BASE_PADDR_HI_SET(*msg_word, tmp); + } +#else /* ! HTT_PADDR64 */ + HTT_RX_RING_CFG_BASE_PADDR_SET(*msg_word, pdev->rx_ring.base_paddr); +#endif /* HTT_PADDR64 */ + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_LEN_SET(*msg_word, pdev->rx_ring.size); + HTT_RX_RING_CFG_BUF_SZ_SET(*msg_word, HTT_RX_BUF_SIZE); + +/* FIX THIS: if the FW creates a complete translated rx descriptor, + * then the MAC DMA of the HW rx descriptor should be disabled. + */ + msg_word++; + *msg_word = 0; +#ifndef REMOVE_PKT_LOG + if (ol_cfg_is_packet_log_enabled(pdev->ctrl_pdev)) { + enable_ctrl_data = 1; + enable_mgmt_data = 1; + enable_null_data = 1; + enable_phy_data = 1; + enable_hdr = 1; + enable_ppdu_start = 1; + enable_ppdu_end = 1; + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "%s : %d Pkt log is enabled\n", __func__, __LINE__); + } else { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "%s : %d Pkt log is disabled\n", __func__, __LINE__); + enable_ctrl_data = 0; + enable_mgmt_data = 0; + enable_null_data = 0; + enable_phy_data = 0; + enable_hdr = 0; + enable_ppdu_start = 0; + enable_ppdu_end = 0; + } +#else + enable_ctrl_data = 0; + enable_mgmt_data = 0; + enable_null_data = 0; + enable_phy_data = 0; + enable_hdr = 0; + enable_ppdu_start = 0; + enable_ppdu_end = 0; +#endif + if (QDF_GLOBAL_MONITOR_MODE == cds_get_conparam()) { + enable_ctrl_data = 1; + enable_mgmt_data = 1; + enable_null_data = 1; + enable_phy_data = 1; + enable_hdr = 1; + enable_ppdu_start = 1; + enable_ppdu_end = 1; + /* Disable ASPM for monitor mode */ + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "%s : %d Monitor mode is enabled\n", + __func__, __LINE__); + } + + htt_rx_enable_ppdu_end(&enable_ppdu_end); + HTT_RX_RING_CFG_ENABLED_802_11_HDR_SET(*msg_word, enable_hdr); + HTT_RX_RING_CFG_ENABLED_MSDU_PAYLD_SET(*msg_word, 1); + HTT_RX_RING_CFG_ENABLED_PPDU_START_SET(*msg_word, enable_ppdu_start); + HTT_RX_RING_CFG_ENABLED_PPDU_END_SET(*msg_word, enable_ppdu_end); + HTT_RX_RING_CFG_ENABLED_MPDU_START_SET(*msg_word, 1); + HTT_RX_RING_CFG_ENABLED_MPDU_END_SET(*msg_word, 1); + HTT_RX_RING_CFG_ENABLED_MSDU_START_SET(*msg_word, 1); + HTT_RX_RING_CFG_ENABLED_MSDU_END_SET(*msg_word, 1); + HTT_RX_RING_CFG_ENABLED_RX_ATTN_SET(*msg_word, 1); + /* always present? */ + HTT_RX_RING_CFG_ENABLED_FRAG_INFO_SET(*msg_word, 1); + HTT_RX_RING_CFG_ENABLED_UCAST_SET(*msg_word, 1); + HTT_RX_RING_CFG_ENABLED_MCAST_SET(*msg_word, 1); + /* Must change to dynamic enable at run time + * rather than at compile time + */ + HTT_RX_RING_CFG_ENABLED_CTRL_SET(*msg_word, enable_ctrl_data); + HTT_RX_RING_CFG_ENABLED_MGMT_SET(*msg_word, enable_mgmt_data); + HTT_RX_RING_CFG_ENABLED_NULL_SET(*msg_word, enable_null_data); + HTT_RX_RING_CFG_ENABLED_PHY_SET(*msg_word, enable_phy_data); + HTT_RX_RING_CFG_IDX_INIT_VAL_SET(*msg_word, + *pdev->rx_ring.alloc_idx.vaddr); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_802_11_HDR_SET(*msg_word, + RX_DESC_HDR_STATUS_OFFSET32); + HTT_RX_RING_CFG_OFFSET_MSDU_PAYLD_SET(*msg_word, + HTT_RX_DESC_RESERVATION32); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_PPDU_START_SET(*msg_word, + RX_DESC_PPDU_START_OFFSET32); + HTT_RX_RING_CFG_OFFSET_PPDU_END_SET(*msg_word, + RX_DESC_PPDU_END_OFFSET32); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_MPDU_START_SET(*msg_word, + RX_DESC_MPDU_START_OFFSET32); + HTT_RX_RING_CFG_OFFSET_MPDU_END_SET(*msg_word, + RX_DESC_MPDU_END_OFFSET32); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_MSDU_START_SET(*msg_word, + RX_DESC_MSDU_START_OFFSET32); + HTT_RX_RING_CFG_OFFSET_MSDU_END_SET(*msg_word, + RX_DESC_MSDU_END_OFFSET32); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_RX_ATTN_SET(*msg_word, + RX_DESC_ATTN_OFFSET32); + HTT_RX_RING_CFG_OFFSET_FRAG_INFO_SET(*msg_word, + RX_DESC_FRAG_INFO_OFFSET32); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + HTC_TX_PACKET_TAG_RUNTIME_PUT); + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +htt_h2t_rx_ring_cfg_msg_hl(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + u_int32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return A_ERROR; /* failure */ + + /* + * show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + msg = qdf_nbuf_alloc( + pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_RX_RING_CFG_BYTES(1)), + /* reserve room for the HTC header */ + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, true); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return A_ERROR; /* failure */ + } + /* + * Set the length of the message. + * The contribution from the HTC_HDR_ALIGNMENT_PADDING is added + * separately during the below call to adf_nbuf_push_head. + * The contribution from the HTC header is added separately inside HTC. + */ + qdf_nbuf_put_tail(msg, HTT_RX_RING_CFG_BYTES(1)); + + /* fill in the message contents */ + msg_word = (u_int32_t *)qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_RX_RING_CFG); + HTT_RX_RING_CFG_NUM_RINGS_SET(*msg_word, 1); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_IDX_SHADOW_REG_PADDR_SET( + *msg_word, pdev->rx_ring.alloc_idx.paddr); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_BASE_PADDR_SET(*msg_word, pdev->rx_ring.base_paddr); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_LEN_SET(*msg_word, pdev->rx_ring.size); + HTT_RX_RING_CFG_BUF_SZ_SET(*msg_word, HTT_RX_BUF_SIZE); + + /* FIX THIS: if the FW creates a complete translated rx descriptor, + * then the MAC DMA of the HW rx descriptor should be disabled. */ + msg_word++; + *msg_word = 0; + + HTT_RX_RING_CFG_ENABLED_802_11_HDR_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_MSDU_PAYLD_SET(*msg_word, 1); + HTT_RX_RING_CFG_ENABLED_PPDU_START_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_PPDU_END_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_MPDU_START_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_MPDU_END_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_MSDU_START_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_MSDU_END_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_RX_ATTN_SET(*msg_word, 0); + /* always present? */ + HTT_RX_RING_CFG_ENABLED_FRAG_INFO_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_UCAST_SET(*msg_word, 1); + HTT_RX_RING_CFG_ENABLED_MCAST_SET(*msg_word, 1); + /* Must change to dynamic enable at run time + * rather than at compile time + */ + HTT_RX_RING_CFG_ENABLED_CTRL_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_MGMT_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_NULL_SET(*msg_word, 0); + HTT_RX_RING_CFG_ENABLED_PHY_SET(*msg_word, 0); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_802_11_HDR_SET(*msg_word, + 0); + HTT_RX_RING_CFG_OFFSET_MSDU_PAYLD_SET(*msg_word, + 0); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_PPDU_START_SET(*msg_word, + 0); + HTT_RX_RING_CFG_OFFSET_PPDU_END_SET(*msg_word, + 0); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_MPDU_START_SET(*msg_word, + 0); + HTT_RX_RING_CFG_OFFSET_MPDU_END_SET(*msg_word, + 0); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_MSDU_START_SET(*msg_word, + 0); + HTT_RX_RING_CFG_OFFSET_MSDU_END_SET(*msg_word, + 0); + + msg_word++; + *msg_word = 0; + HTT_RX_RING_CFG_OFFSET_RX_ATTN_SET(*msg_word, + 0); + HTT_RX_RING_CFG_OFFSET_FRAG_INFO_SET(*msg_word, + 0); + + SET_HTC_PACKET_INFO_TX( + &pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + +#ifdef ATH_11AC_TXCOMPACT + if (htc_send_pkt(pdev->htc_pdev, &pkt->htc_pkt) == QDF_STATUS_SUCCESS) { + htt_htc_misc_pkt_list_add(pdev, pkt); + } else { + qdf_nbuf_free(msg); + htt_htc_pkt_free(pdev, pkt); + } +#else + htc_send_pkt(pdev->htc_pdev, &pkt->htc_pkt); +#endif + + ol_tx_deduct_one_credit(pdev->txrx_pdev); + + return QDF_STATUS_SUCCESS; +} + +/** + * htt_h2t_rx_ring_rfs_cfg_msg_hl() - Configure receive flow steering + * @pdev: handle to the HTT instance + * + * Return: QDF_STATUS_SUCCESS on success + * A_NO_MEMORY No memory fail + */ +QDF_STATUS htt_h2t_rx_ring_rfs_cfg_msg_hl(struct htt_pdev_t *pdev) +{ + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "Doesnot support Receive flow steering configuration\n"); + return QDF_STATUS_SUCCESS; +} + +int +htt_h2t_dbg_stats_get(struct htt_pdev_t *pdev, + uint32_t stats_type_upload_mask, + uint32_t stats_type_reset_mask, + uint8_t cfg_stat_type, uint32_t cfg_val, uint8_t cookie) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + uint16_t htc_tag = 1; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -EINVAL; /* failure */ + + if (stats_type_upload_mask >= 1 << HTT_DBG_NUM_STATS || + stats_type_reset_mask >= 1 << HTT_DBG_NUM_STATS) { + /* FIX THIS - add more details? */ + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%#x %#x stats not supported\n", + stats_type_upload_mask, stats_type_reset_mask); + htt_htc_pkt_free(pdev, pkt); + return -EINVAL; /* failure */ + } + + if (stats_type_reset_mask) + htc_tag = HTC_TX_PACKET_TAG_RUNTIME_PUT; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_H2T_STATS_REQ_MSG_SZ), + /* reserve room for HTC header */ + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -EINVAL; /* failure */ + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_H2T_STATS_REQ_MSG_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_STATS_REQ); + HTT_H2T_STATS_REQ_UPLOAD_TYPES_SET(*msg_word, stats_type_upload_mask); + + msg_word++; + *msg_word = 0; + HTT_H2T_STATS_REQ_RESET_TYPES_SET(*msg_word, stats_type_reset_mask); + + msg_word++; + *msg_word = 0; + HTT_H2T_STATS_REQ_CFG_VAL_SET(*msg_word, cfg_val); + HTT_H2T_STATS_REQ_CFG_STAT_TYPE_SET(*msg_word, cfg_stat_type); + + /* cookie LSBs */ + msg_word++; + *msg_word = cookie; + + /* cookie MSBs */ + msg_word++; + *msg_word = 0; + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + htc_tag); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + +#ifdef ATH_11AC_TXCOMPACT + if (htc_send_pkt(pdev->htc_pdev, &pkt->htc_pkt) == QDF_STATUS_SUCCESS) { + htt_htc_misc_pkt_list_add(pdev, pkt); + } else { + qdf_nbuf_free(msg); + htt_htc_pkt_free(pdev, pkt); + } +#else + htc_send_pkt(pdev->htc_pdev, &pkt->htc_pkt); +#endif + + ol_tx_deduct_one_credit(pdev->txrx_pdev); + + return 0; +} + +A_STATUS htt_h2t_sync_msg(struct htt_pdev_t *pdev, uint8_t sync_cnt) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return A_NO_MEMORY; + + /* show that this is not a tx frame download + (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, HTT_MSG_BUF_SIZE(HTT_H2T_SYNC_MSG_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_H2T_SYNC_MSG_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_SYNC); + HTT_H2T_SYNC_COUNT_SET(*msg_word, sync_cnt); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + HTC_TX_PACKET_TAG_RUNTIME_PUT); + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + + ol_tx_deduct_one_credit(pdev->txrx_pdev); + + return A_OK; +} + +int +htt_h2t_aggr_cfg_msg(struct htt_pdev_t *pdev, + int max_subfrms_ampdu, int max_subfrms_amsdu) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -EINVAL; /* failure */ + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, HTT_MSG_BUF_SIZE(HTT_AGGR_CFG_MSG_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -EINVAL; /* failure */ + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_AGGR_CFG_MSG_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_AGGR_CFG); + + if (max_subfrms_ampdu && (max_subfrms_ampdu <= 64)) { + HTT_AGGR_CFG_MAX_NUM_AMPDU_SUBFRM_SET(*msg_word, + max_subfrms_ampdu); + } + + if (max_subfrms_amsdu && (max_subfrms_amsdu < 32)) { + HTT_AGGR_CFG_MAX_NUM_AMSDU_SUBFRM_SET(*msg_word, + max_subfrms_amsdu); + } + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + HTC_TX_PACKET_TAG_RUNTIME_PUT); + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + +#ifdef ATH_11AC_TXCOMPACT + if (htc_send_pkt(pdev->htc_pdev, &pkt->htc_pkt) == QDF_STATUS_SUCCESS) { + htt_htc_misc_pkt_list_add(pdev, pkt); + } else { + qdf_nbuf_free(msg); + htt_htc_pkt_free(pdev, pkt); + } +#else + htc_send_pkt(pdev->htc_pdev, &pkt->htc_pkt); +#endif + + ol_tx_deduct_one_credit(pdev->txrx_pdev); + + return 0; +} + +#ifdef IPA_OFFLOAD +/** + * htt_h2t_ipa_uc_rsc_cfg_msg() - Send WDI IPA config message to firmware + * @pdev: handle to the HTT instance + * + * Return: 0 success + * A_NO_MEMORY No memory fail + */ +#ifdef QCA_WIFI_3_0 +int htt_h2t_ipa_uc_rsc_cfg_msg(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, HTT_MSG_BUF_SIZE(HTT_WDI_IPA_CFG_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_CFG_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_WDI_IPA_CFG_TX_PKT_POOL_SIZE_SET(*msg_word, + pdev->ipa_uc_tx_rsc.alloc_tx_buf_cnt); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_CFG); + + msg_word++; + *msg_word = 0; + /* TX COMP RING BASE LO */ + HTT_WDI_IPA_CFG_TX_COMP_RING_BASE_ADDR_LO_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_tx_rsc.tx_comp_ring->mem_info)); + msg_word++; + *msg_word = 0; + /* TX COMP RING BASE HI, NONE */ + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_TX_COMP_RING_SIZE_SET(*msg_word, + (unsigned int)ol_cfg_ipa_uc_tx_max_buf_cnt(pdev->ctrl_pdev)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_TX_COMP_WR_IDX_ADDR_LO_SET(*msg_word, + (unsigned int)pdev->ipa_uc_tx_rsc.tx_comp_idx_paddr); + msg_word++; + *msg_word = 0; + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_TX_CE_WR_IDX_ADDR_LO_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_tx_rsc.tx_ce_idx->mem_info)); + msg_word++; + *msg_word = 0; + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_RING_BASE_ADDR_LO_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_rx_rsc.rx_ind_ring->mem_info)); + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_RING_BASE_ADDR_HI_SET(*msg_word, + 0); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_RING_SIZE_SET(*msg_word, + (unsigned int)qdf_get_pwr2(pdev->rx_ring.fill_level)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_RD_IDX_ADDR_LO_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_rx_rsc.rx_ipa_prc_done_idx->mem_info)); + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_RD_IDX_ADDR_HI_SET(*msg_word, + 0); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_WR_IDX_ADDR_LO_SET(*msg_word, + (unsigned int)pdev->ipa_uc_rx_rsc.rx_rdy_idx_paddr); + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_WR_IDX_ADDR_HI_SET(*msg_word, + 0); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_RING2_BASE_ADDR_LO_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_rx_rsc.rx2_ind_ring->mem_info)); + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_RING2_BASE_ADDR_HI_SET(*msg_word, + 0); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_RING2_SIZE_SET(*msg_word, + (unsigned int)qdf_get_pwr2(pdev->rx_ring.fill_level)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_RING2_RD_IDX_ADDR_LO_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_rx_rsc.rx2_ipa_prc_done_idx->mem_info)); + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_RING2_RD_IDX_ADDR_HI_SET(*msg_word, + 0); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_RING2_WR_IDX_ADDR_LO_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_rx_rsc.rx2_ipa_prc_done_idx->mem_info)); + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_RING2_WR_IDX_ADDR_HI_SET(*msg_word, + 0); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + HTC_TX_PACKET_TAG_RUNTIME_PUT); + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} +#else +/* Rome Support only WDI 1.0 */ +int htt_h2t_ipa_uc_rsc_cfg_msg(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, HTT_MSG_BUF_SIZE(HTT_WDI_IPA_CFG_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_CFG_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_WDI_IPA_CFG_TX_PKT_POOL_SIZE_SET(*msg_word, + pdev->ipa_uc_tx_rsc.alloc_tx_buf_cnt); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_CFG); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_TX_COMP_RING_BASE_ADDR_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_tx_rsc.tx_comp_ring->mem_info)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_TX_COMP_RING_SIZE_SET( + *msg_word, + (unsigned int)ol_cfg_ipa_uc_tx_max_buf_cnt(pdev->ctrl_pdev)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_TX_COMP_WR_IDX_ADDR_SET(*msg_word, + (unsigned int)pdev->ipa_uc_tx_rsc.tx_comp_idx_paddr); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_TX_CE_WR_IDX_ADDR_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_tx_rsc.tx_ce_idx->mem_info)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_RING_BASE_ADDR_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_rx_rsc.rx_ind_ring->mem_info)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_RING_SIZE_SET(*msg_word, + (unsigned int)qdf_get_pwr2(pdev->rx_ring.fill_level)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_RD_IDX_ADDR_SET(*msg_word, + (unsigned int)qdf_mem_get_dma_addr(pdev->osdev, + &pdev->ipa_uc_rx_rsc.rx_ipa_prc_done_idx->mem_info)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_CFG_RX_IND_WR_IDX_ADDR_SET(*msg_word, + (unsigned int)pdev->ipa_uc_rx_rsc.rx_rdy_idx_paddr); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + HTC_TX_PACKET_TAG_RUNTIME_PUT); + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} +#endif + +/** + * htt_h2t_ipa_uc_set_active() - Propagate WDI path enable/disable to firmware + * @pdev: handle to the HTT instance + * @uc_active: WDI UC path enable or not + * @is_tx: TX path or RX path + * + * Return: 0 success + * A_NO_MEMORY No memory fail + */ +int htt_h2t_ipa_uc_set_active(struct htt_pdev_t *pdev, + bool uc_active, bool is_tx) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + uint8_t active_target = 0; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQUEST_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_OP_REQUEST_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + if (uc_active && is_tx) + active_target = HTT_WDI_IPA_OPCODE_TX_RESUME; + else if (!uc_active && is_tx) + active_target = HTT_WDI_IPA_OPCODE_TX_SUSPEND; + else if (uc_active && !is_tx) + active_target = HTT_WDI_IPA_OPCODE_RX_RESUME; + else if (!uc_active && !is_tx) + active_target = HTT_WDI_IPA_OPCODE_RX_SUSPEND; + + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "%s: HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ (%d)\n", + __func__, active_target); + + HTT_WDI_IPA_OP_REQUEST_OP_CODE_SET(*msg_word, active_target); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} + +/** + * htt_h2t_ipa_uc_get_stats() - WDI UC state query request to firmware + * @pdev: handle to the HTT instance + * + * Return: 0 success + * A_NO_MEMORY No memory fail + */ +int htt_h2t_ipa_uc_get_stats(struct htt_pdev_t *pdev) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQUEST_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, + false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_OP_REQUEST_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_WDI_IPA_OP_REQUEST_OP_CODE_SET(*msg_word, + HTT_WDI_IPA_OPCODE_DBG_STATS); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} + +/** + * htt_h2t_ipa_uc_get_share_stats() - WDI UC wifi sharing state request to FW + * @pdev: handle to the HTT instance + * + * Return: A_OK success + * A_NO_MEMORY No memory fail + */ +int htt_h2t_ipa_uc_get_share_stats(struct htt_pdev_t *pdev, uint8_t reset_stats) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQUEST_SZ)+ + HTT_MSG_BUF_SIZE(WLAN_WDI_IPA_GET_SHARING_STATS_REQ_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_OP_REQUEST_SZ+ + WLAN_WDI_IPA_GET_SHARING_STATS_REQ_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_WDI_IPA_OP_REQUEST_OP_CODE_SET(*msg_word, + HTT_WDI_IPA_OPCODE_GET_SHARING_STATS); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ); + + msg_word++; + *msg_word = 0; + WLAN_WDI_IPA_GET_SHARING_STATS_REQ_RESET_STATS_SET(*msg_word, + reset_stats); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} + +/** + * htt_h2t_ipa_uc_set_quota() - WDI UC state query request to firmware + * @pdev: handle to the HTT instance + * + * Return: A_OK success + * A_NO_MEMORY No memory fail + */ +int htt_h2t_ipa_uc_set_quota(struct htt_pdev_t *pdev, uint64_t quota_bytes) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQUEST_SZ)+ + HTT_MSG_BUF_SIZE(WLAN_WDI_IPA_SET_QUOTA_REQ_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_OP_REQUEST_SZ+ + WLAN_WDI_IPA_SET_QUOTA_REQ_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_WDI_IPA_OP_REQUEST_OP_CODE_SET(*msg_word, + HTT_WDI_IPA_OPCODE_SET_QUOTA); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ); + + msg_word++; + *msg_word = 0; + WLAN_WDI_IPA_SET_QUOTA_REQ_SET_QUOTA_SET(*msg_word, quota_bytes > 0); + + msg_word++; + *msg_word = 0; + WLAN_WDI_IPA_SET_QUOTA_REQ_QUOTA_LO_SET(*msg_word, + (uint32_t)(quota_bytes & + WLAN_WDI_IPA_SET_QUOTA_REQ_QUOTA_LO_M)); + + msg_word++; + *msg_word = 0; + WLAN_WDI_IPA_SET_QUOTA_REQ_QUOTA_HI_SET(*msg_word, + (uint32_t)(quota_bytes>>32 & + WLAN_WDI_IPA_SET_QUOTA_REQ_QUOTA_HI_M)); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} +#endif /* IPA_OFFLOAD */ diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_internal.h b/drivers/staging/qcacld-3.0/core/dp/htt/htt_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..17bd2cbc9c6dfe6c39b93c91446f3d6cb638fc25 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_internal.h @@ -0,0 +1,1134 @@ +/* + * Copyright (c) 2011, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HTT_INTERNAL__H_ +#define _HTT_INTERNAL__H_ + +#include /* A_STATUS */ +#include /* qdf_nbuf_t */ +#include /* qdf_assert */ +#include /* HTC_PACKET */ + +#include + +/* htt_rx.c */ +#define RX_MSDU_END_4_FIRST_MSDU_MASK \ + (pdev->targetdef->d_RX_MSDU_END_4_FIRST_MSDU_MASK) +#define RX_MSDU_END_4_FIRST_MSDU_LSB \ + (pdev->targetdef->d_RX_MSDU_END_4_FIRST_MSDU_LSB) +#define RX_MPDU_START_0_RETRY_LSB \ + (pdev->targetdef->d_RX_MPDU_START_0_RETRY_LSB) +#define RX_MPDU_START_0_RETRY_MASK \ + (pdev->targetdef->d_RX_MPDU_START_0_RETRY_MASK) +#define RX_MPDU_START_0_SEQ_NUM_MASK \ + (pdev->targetdef->d_RX_MPDU_START_0_SEQ_NUM_MASK) +#define RX_MPDU_START_0_SEQ_NUM_LSB \ + (pdev->targetdef->d_RX_MPDU_START_0_SEQ_NUM_LSB) +#define RX_MPDU_START_2_PN_47_32_LSB \ + (pdev->targetdef->d_RX_MPDU_START_2_PN_47_32_LSB) +#define RX_MPDU_START_2_PN_47_32_MASK \ + (pdev->targetdef->d_RX_MPDU_START_2_PN_47_32_MASK) +#define RX_MPDU_START_2_TID_LSB \ + (pdev->targetdef->d_RX_MPDU_START_2_TID_LSB) +#define RX_MPDU_START_2_TID_MASK \ + (pdev->targetdef->d_RX_MPDU_START_2_TID_MASK) +#define RX_MSDU_END_1_KEY_ID_OCT_MASK \ + (pdev->targetdef->d_RX_MSDU_END_1_KEY_ID_OCT_MASK) +#define RX_MSDU_END_1_KEY_ID_OCT_LSB \ + (pdev->targetdef->d_RX_MSDU_END_1_KEY_ID_OCT_LSB) +#define RX_MSDU_END_1_EXT_WAPI_PN_63_48_MASK \ + (pdev->targetdef->d_RX_MSDU_END_1_EXT_WAPI_PN_63_48_MASK) +#define RX_MSDU_END_1_EXT_WAPI_PN_63_48_LSB \ + (pdev->targetdef->d_RX_MSDU_END_1_EXT_WAPI_PN_63_48_LSB) +#define RX_MSDU_END_4_LAST_MSDU_MASK \ + (pdev->targetdef->d_RX_MSDU_END_4_LAST_MSDU_MASK) +#define RX_MSDU_END_4_LAST_MSDU_LSB \ + (pdev->targetdef->d_RX_MSDU_END_4_LAST_MSDU_LSB) +#define RX_ATTENTION_0_MCAST_BCAST_MASK \ + (pdev->targetdef->d_RX_ATTENTION_0_MCAST_BCAST_MASK) +#define RX_ATTENTION_0_MCAST_BCAST_LSB \ + (pdev->targetdef->d_RX_ATTENTION_0_MCAST_BCAST_LSB) +#define RX_ATTENTION_0_FRAGMENT_MASK \ + (pdev->targetdef->d_RX_ATTENTION_0_FRAGMENT_MASK) +#define RX_ATTENTION_0_FRAGMENT_LSB \ + (pdev->targetdef->d_RX_ATTENTION_0_FRAGMENT_LSB) +#define RX_ATTENTION_0_MPDU_LENGTH_ERR_MASK \ + (pdev->targetdef->d_RX_ATTENTION_0_MPDU_LENGTH_ERR_MASK) +#define RX_FRAG_INFO_0_RING2_MORE_COUNT_MASK \ + (pdev->targetdef->d_RX_FRAG_INFO_0_RING2_MORE_COUNT_MASK) +#define RX_FRAG_INFO_0_RING2_MORE_COUNT_LSB \ + (pdev->targetdef->d_RX_FRAG_INFO_0_RING2_MORE_COUNT_LSB) +#define RX_MSDU_START_0_MSDU_LENGTH_MASK \ + (pdev->targetdef->d_RX_MSDU_START_0_MSDU_LENGTH_MASK) +#define RX_MSDU_START_0_MSDU_LENGTH_LSB \ + (pdev->targetdef->d_RX_MSDU_START_0_MSDU_LENGTH_LSB) +#define RX_MPDU_START_0_ENCRYPTED_MASK \ + (pdev->targetdef->d_RX_MPDU_START_0_ENCRYPTED_MASK) +#define RX_MPDU_START_0_ENCRYPTED_LSB \ + (pdev->targetdef->d_RX_MPDU_START_0_ENCRYPTED_LSB) +#define RX_ATTENTION_0_MORE_DATA_MASK \ + (pdev->targetdef->d_RX_ATTENTION_0_MORE_DATA_MASK) +#define RX_ATTENTION_0_MSDU_DONE_MASK \ + (pdev->targetdef->d_RX_ATTENTION_0_MSDU_DONE_MASK) +#define RX_ATTENTION_0_TCP_UDP_CHKSUM_FAIL_MASK \ + (pdev->targetdef->d_RX_ATTENTION_0_TCP_UDP_CHKSUM_FAIL_MASK) +#define RX_MSDU_START_2_DECAP_FORMAT_OFFSET \ + (pdev->targetdef->d_RX_MSDU_START_2_DECAP_FORMAT_OFFSET) +#define RX_MSDU_START_2_DECAP_FORMAT_LSB \ + (pdev->targetdef->d_RX_MSDU_START_2_DECAP_FORMAT_LSB) +#define RX_MSDU_START_2_DECAP_FORMAT_MASK \ + (pdev->targetdef->d_RX_MSDU_START_2_DECAP_FORMAT_MASK) +/* end */ + +#ifndef offsetof +#define offsetof(type, field) ((size_t)(&((type *)0)->field)) +#endif + +#undef MS +#define MS(_v, _f) (((_v) & _f ## _MASK) >> _f ## _LSB) +#undef SM +#define SM(_v, _f) (((_v) << _f ## _LSB) & _f ## _MASK) +#undef WO +#define WO(_f) ((_f ## _OFFSET) >> 2) + +#define GET_FIELD(_addr, _f) MS(*((A_UINT32 *)(_addr) + WO(_f)), _f) + +#include +#include /* struct rx_attention, etc */ + +struct htt_host_fw_desc_base { + union { + struct fw_rx_desc_base val; + A_UINT32 dummy_pad; /* make sure it is DOWRD aligned */ + } u; +}; + + +/* + * This struct defines the basic descriptor information used by host, + * which is written either by the 11ac HW MAC into the host Rx data + * buffer ring directly or generated by FW and copied from Rx indication + */ +struct htt_host_rx_desc_base { + struct htt_host_fw_desc_base fw_desc; + struct rx_attention attention; + struct rx_frag_info frag_info; + struct rx_mpdu_start mpdu_start; + struct rx_msdu_start msdu_start; + struct rx_msdu_end msdu_end; + struct rx_mpdu_end mpdu_end; + struct rx_ppdu_start ppdu_start; + struct rx_ppdu_end ppdu_end; +#ifdef QCA_WIFI_3_0_ADRASTEA +/* Increased to support some of offload features */ +#define RX_HTT_HDR_STATUS_LEN 256 +#else +#define RX_HTT_HDR_STATUS_LEN 64 +#endif + char rx_hdr_status[RX_HTT_HDR_STATUS_LEN]; +}; + +#define RX_DESC_ATTN_MPDU_LEN_ERR_BIT 0x08000000 + +#define RX_STD_DESC_ATTN_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, attention)) +#define RX_STD_DESC_FRAG_INFO_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, frag_info)) +#define RX_STD_DESC_MPDU_START_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, mpdu_start)) +#define RX_STD_DESC_MSDU_START_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, msdu_start)) +#define RX_STD_DESC_MSDU_END_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, msdu_end)) +#define RX_STD_DESC_MPDU_END_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, mpdu_end)) +#define RX_STD_DESC_PPDU_START_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, ppdu_start)) +#define RX_STD_DESC_PPDU_END_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, ppdu_end)) +#define RX_STD_DESC_HDR_STATUS_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, rx_hdr_status)) + +#define RX_STD_DESC_FW_MSDU_OFFSET \ + (offsetof(struct htt_host_rx_desc_base, fw_desc)) + +#define RX_STD_DESC_SIZE (sizeof(struct htt_host_rx_desc_base)) + +#define RX_DESC_ATTN_OFFSET32 (RX_STD_DESC_ATTN_OFFSET >> 2) +#define RX_DESC_FRAG_INFO_OFFSET32 (RX_STD_DESC_FRAG_INFO_OFFSET >> 2) +#define RX_DESC_MPDU_START_OFFSET32 (RX_STD_DESC_MPDU_START_OFFSET >> 2) +#define RX_DESC_MSDU_START_OFFSET32 (RX_STD_DESC_MSDU_START_OFFSET >> 2) +#define RX_DESC_MSDU_END_OFFSET32 (RX_STD_DESC_MSDU_END_OFFSET >> 2) +#define RX_DESC_MPDU_END_OFFSET32 (RX_STD_DESC_MPDU_END_OFFSET >> 2) +#define RX_DESC_PPDU_START_OFFSET32 (RX_STD_DESC_PPDU_START_OFFSET >> 2) +#define RX_DESC_PPDU_END_OFFSET32 (RX_STD_DESC_PPDU_END_OFFSET >> 2) +#define RX_DESC_HDR_STATUS_OFFSET32 (RX_STD_DESC_HDR_STATUS_OFFSET >> 2) + +#define RX_STD_DESC_SIZE_DWORD (RX_STD_DESC_SIZE >> 2) + +/* + * Make sure there is a minimum headroom provided in the rx netbufs + * for use by the OS shim and OS and rx data consumers. + */ +#define HTT_RX_BUF_OS_MIN_HEADROOM 32 +#define HTT_RX_STD_DESC_RESERVATION \ + ((HTT_RX_BUF_OS_MIN_HEADROOM > RX_STD_DESC_SIZE) ? \ + HTT_RX_BUF_OS_MIN_HEADROOM : RX_STD_DESC_SIZE) +#define HTT_RX_DESC_RESERVATION32 \ + (HTT_RX_STD_DESC_RESERVATION >> 2) + +#define HTT_RX_DESC_ALIGN_MASK 7 /* 8-byte alignment */ + +#ifdef DEBUG_RX_RING_BUFFER +#ifdef MSM_PLATFORM +#define HTT_ADDRESS_MASK 0xfffffffffffffffe +#else +#define HTT_ADDRESS_MASK 0xfffffffe +#endif /* MSM_PLATFORM */ + +/** + * rx_buf_debug: rx_ring history + * + * There are three types of entries in history: + * 1) rx-descriptors posted (and received) + * Both of these events are stored on the same entry + * @paddr : physical address posted on the ring + * @nbuf : virtual address of nbuf containing data + * @ndata : virual address of data (corresponds to physical address) + * @posted: time-stamp when the buffer is posted to the ring + * @recved: time-stamp when the buffer is received (rx_in_order_ind) + * : or 0, if the buffer has not been received yet + * 2) ring alloc-index (fill-index) updates + * @paddr : = 0 + * @nbuf : = 0 + * @ndata : = 0 + * posted : time-stamp when alloc index was updated + * recved : value of alloc index + * 3) htt_rx_in_order_indication reception + * @paddr : = 0 + * @nbuf : = 0 + * @ndata : msdu_cnt + * @posted: time-stamp when HTT message is recived + * @recvd : 0x48545452584D5367 ('HTTRXMSG') + */ +#define HTT_RX_RING_BUFF_DBG_LIST (8 * 1024) +struct rx_buf_debug { + qdf_dma_addr_t paddr; + qdf_nbuf_t nbuf; + void *nbuf_data; + uint64_t posted; /* timetamp */ + uint64_t recved; /* timestamp */ + int cpu; + +}; +#endif + +static inline struct htt_host_rx_desc_base *htt_rx_desc(qdf_nbuf_t msdu) +{ + return (struct htt_host_rx_desc_base *) + (((size_t) (qdf_nbuf_head(msdu) + HTT_RX_DESC_ALIGN_MASK)) & + ~HTT_RX_DESC_ALIGN_MASK); +} + +#if defined(HELIUMPLUS) +/** + * htt_print_rx_desc_lro() - print LRO information in the rx + * descriptor + * @rx_desc: HTT rx descriptor + * + * Prints the LRO related fields in the HTT rx descriptor + * + * Return: none + */ +static inline void htt_print_rx_desc_lro(struct htt_host_rx_desc_base *rx_desc) +{ + qdf_nofl_info + ("----------------------RX DESC LRO----------------------\n"); + qdf_nofl_info("msdu_end.lro_eligible:0x%x\n", + rx_desc->msdu_end.lro_eligible); + qdf_nofl_info("msdu_start.tcp_only_ack:0x%x\n", + rx_desc->msdu_start.tcp_only_ack); + qdf_nofl_info("msdu_end.tcp_udp_chksum:0x%x\n", + rx_desc->msdu_end.tcp_udp_chksum); + qdf_nofl_info("msdu_end.tcp_seq_number:0x%x\n", + rx_desc->msdu_end.tcp_seq_number); + qdf_nofl_info("msdu_end.tcp_ack_number:0x%x\n", + rx_desc->msdu_end.tcp_ack_number); + qdf_nofl_info("msdu_start.tcp_proto:0x%x\n", + rx_desc->msdu_start.tcp_proto); + qdf_nofl_info("msdu_start.ipv6_proto:0x%x\n", + rx_desc->msdu_start.ipv6_proto); + qdf_nofl_info("msdu_start.ipv4_proto:0x%x\n", + rx_desc->msdu_start.ipv4_proto); + qdf_nofl_info("msdu_start.l3_offset:0x%x\n", + rx_desc->msdu_start.l3_offset); + qdf_nofl_info("msdu_start.l4_offset:0x%x\n", + rx_desc->msdu_start.l4_offset); + qdf_nofl_info("msdu_start.flow_id_toeplitz:0x%x\n", + rx_desc->msdu_start.flow_id_toeplitz); + qdf_nofl_info + ("---------------------------------------------------------\n"); +} + +/** + * htt_print_rx_desc_lro() - extract LRO information from the rx + * descriptor + * @msdu: network buffer + * @rx_desc: HTT rx descriptor + * + * Extracts the LRO related fields from the HTT rx descriptor + * and stores them in the network buffer's control block + * + * Return: none + */ +static inline void htt_rx_extract_lro_info(qdf_nbuf_t msdu, + struct htt_host_rx_desc_base *rx_desc) +{ + if (rx_desc->attention.tcp_udp_chksum_fail) + QDF_NBUF_CB_RX_LRO_ELIGIBLE(msdu) = 0; + else + QDF_NBUF_CB_RX_LRO_ELIGIBLE(msdu) = + rx_desc->msdu_end.lro_eligible; + + if (QDF_NBUF_CB_RX_LRO_ELIGIBLE(msdu)) { + QDF_NBUF_CB_RX_TCP_PURE_ACK(msdu) = + rx_desc->msdu_start.tcp_only_ack; + QDF_NBUF_CB_RX_TCP_CHKSUM(msdu) = + rx_desc->msdu_end.tcp_udp_chksum; + QDF_NBUF_CB_RX_TCP_SEQ_NUM(msdu) = + rx_desc->msdu_end.tcp_seq_number; + QDF_NBUF_CB_RX_TCP_ACK_NUM(msdu) = + rx_desc->msdu_end.tcp_ack_number; + QDF_NBUF_CB_RX_TCP_WIN(msdu) = + rx_desc->msdu_end.window_size; + QDF_NBUF_CB_RX_TCP_PROTO(msdu) = + rx_desc->msdu_start.tcp_proto; + QDF_NBUF_CB_RX_IPV6_PROTO(msdu) = + rx_desc->msdu_start.ipv6_proto; + QDF_NBUF_CB_RX_TCP_OFFSET(msdu) = + rx_desc->msdu_start.l4_offset; + QDF_NBUF_CB_RX_FLOW_ID(msdu) = + rx_desc->msdu_start.flow_id_toeplitz; + } +} +#else +static inline void htt_print_rx_desc_lro(struct htt_host_rx_desc_base *rx_desc) +{} +static inline void htt_rx_extract_lro_info(qdf_nbuf_t msdu, + struct htt_host_rx_desc_base *rx_desc) {} +#endif /* HELIUMPLUS */ + +static inline void htt_print_rx_desc(struct htt_host_rx_desc_base *rx_desc) +{ + qdf_nofl_info + ("----------------------RX DESC----------------------------\n"); + qdf_nofl_info("attention: %#010x\n", + (unsigned int)(*(uint32_t *)&rx_desc->attention)); + qdf_nofl_info("frag_info: %#010x\n", + (unsigned int)(*(uint32_t *)&rx_desc->frag_info)); + qdf_nofl_info("mpdu_start: %#010x %#010x %#010x\n", + (unsigned int)(((uint32_t *)&rx_desc->mpdu_start)[0]), + (unsigned int)(((uint32_t *)&rx_desc->mpdu_start)[1]), + (unsigned int)(((uint32_t *)&rx_desc->mpdu_start)[2])); + qdf_nofl_info("msdu_start: %#010x %#010x %#010x\n", + (unsigned int)(((uint32_t *)&rx_desc->msdu_start)[0]), + (unsigned int)(((uint32_t *)&rx_desc->msdu_start)[1]), + (unsigned int)(((uint32_t *)&rx_desc->msdu_start)[2])); + qdf_nofl_info("msdu_end: %#010x %#010x %#010x %#010x %#010x\n", + (unsigned int)(((uint32_t *)&rx_desc->msdu_end)[0]), + (unsigned int)(((uint32_t *)&rx_desc->msdu_end)[1]), + (unsigned int)(((uint32_t *)&rx_desc->msdu_end)[2]), + (unsigned int)(((uint32_t *)&rx_desc->msdu_end)[3]), + (unsigned int)(((uint32_t *)&rx_desc->msdu_end)[4])); + qdf_nofl_info("mpdu_end: %#010x\n", + (unsigned int)(*(uint32_t *)&rx_desc->mpdu_end)); + qdf_nofl_info("ppdu_start: %#010x %#010x %#010x %#010x %#010x\n" + "%#010x %#010x %#010x %#010x %#010x\n", + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[0]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[1]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[2]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[3]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[4]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[5]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[6]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[7]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[8]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_start)[9])); + qdf_nofl_info("ppdu_end: %#010x %#010x %#010x %#010x %#010x\n" + "%#010x %#010x %#010x %#010x %#010x\n" + "%#010x,%#010x %#010x %#010x %#010x\n" + "%#010x %#010x %#010x %#010x %#010x\n" "%#010x %#010x\n", + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[0]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[1]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[2]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[3]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[4]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[5]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[6]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[7]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[8]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[9]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[10]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[11]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[12]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[13]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[14]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[15]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[16]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[17]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[18]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[19]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[20]), + (unsigned int)(((uint32_t *)&rx_desc->ppdu_end)[21])); + qdf_nofl_info + ("---------------------------------------------------------\n"); +} + +#ifndef HTT_ASSERT_LEVEL +#define HTT_ASSERT_LEVEL 3 +#endif + +#define HTT_ASSERT_ALWAYS(condition) qdf_assert_always((condition)) + +#define HTT_ASSERT0(condition) qdf_assert((condition)) +#if HTT_ASSERT_LEVEL > 0 +#define HTT_ASSERT1(condition) qdf_assert((condition)) +#else +#define HTT_ASSERT1(condition) +#endif + +#if HTT_ASSERT_LEVEL > 1 +#define HTT_ASSERT2(condition) qdf_assert((condition)) +#else +#define HTT_ASSERT2(condition) +#endif + +#if HTT_ASSERT_LEVEL > 2 +#define HTT_ASSERT3(condition) qdf_assert((condition)) +#else +#define HTT_ASSERT3(condition) +#endif + +/* + * HTT_MAX_SEND_QUEUE_DEPTH - + * How many packets HTC should allow to accumulate in a send queue + * before calling the EpSendFull callback to see whether to retain + * or drop packets. + * This is not relevant for LL, where tx descriptors should be immediately + * downloaded to the target. + * This is not very relevant for HL either, since it is anticipated that + * the HL tx download scheduler will not work this far in advance - rather, + * it will make its decisions just-in-time, so it can be responsive to + * changing conditions. + * Hence, this queue depth threshold spec is mostly just a formality. + */ +#define HTT_MAX_SEND_QUEUE_DEPTH 64 + +#define IS_PWR2(value) (((value) ^ ((value)-1)) == ((value) << 1) - 1) + +/* + * HTT_RX_PRE_ALLOC_POOL_SIZE - + * How many Rx Buffer should be there in pre-allocated pool of buffers. + * This is mainly for low memory condition where kernel fails to alloc + * SKB buffer to the Rx ring. + */ +#define HTT_RX_PRE_ALLOC_POOL_SIZE 64 +/* Max rx MSDU size including L2 headers */ +#define MSDU_SIZE 1560 +/* Rounding up to a cache line size. */ +#define HTT_RX_BUF_SIZE roundup(MSDU_SIZE + \ + sizeof(struct htt_host_rx_desc_base), \ + QDF_CACHE_LINE_SZ) +#define MAX_RX_PAYLOAD_SZ (HTT_RX_BUF_SIZE - RX_STD_DESC_SIZE) +/* + * DMA_MAP expects the buffer to be an integral number of cache lines. + * Rather than checking the actual cache line size, this code makes a + * conservative estimate of what the cache line size could be. + */ +#define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */ +#define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1) + +#ifdef BIG_ENDIAN_HOST +/* + * big-endian: bytes within a 4-byte "word" are swapped: + * pre-swap post-swap + * index index + * 0 3 + * 1 2 + * 2 1 + * 3 0 + * 4 7 + * 5 6 + * etc. + * To compute the post-swap index from the pre-swap index, compute + * the byte offset for the start of the word (index & ~0x3) and add + * the swapped byte offset within the word (3 - (index & 0x3)). + */ +#define HTT_ENDIAN_BYTE_IDX_SWAP(idx) (((idx) & ~0x3) + (3 - ((idx) & 0x3))) +#else +/* little-endian: no adjustment needed */ +#define HTT_ENDIAN_BYTE_IDX_SWAP(idx) idx +#endif + +#define HTT_TX_MUTEX_INIT(_mutex) \ + qdf_spinlock_create(_mutex) + +#define HTT_TX_MUTEX_ACQUIRE(_mutex) \ + qdf_spin_lock_bh(_mutex) + +#define HTT_TX_MUTEX_RELEASE(_mutex) \ + qdf_spin_unlock_bh(_mutex) + +#define HTT_TX_MUTEX_DESTROY(_mutex) \ + qdf_spinlock_destroy(_mutex) + +#ifdef ATH_11AC_TXCOMPACT + +#define HTT_TX_NBUF_QUEUE_MUTEX_INIT(_pdev) \ + qdf_spinlock_create(&_pdev->txnbufq_mutex) + +#define HTT_TX_NBUF_QUEUE_MUTEX_DESTROY(_pdev) \ + HTT_TX_MUTEX_DESTROY(&_pdev->txnbufq_mutex) + +#define HTT_TX_NBUF_QUEUE_REMOVE(_pdev, _msdu) do { \ + HTT_TX_MUTEX_ACQUIRE(&_pdev->txnbufq_mutex); \ + _msdu = qdf_nbuf_queue_remove(&_pdev->txnbufq);\ + HTT_TX_MUTEX_RELEASE(&_pdev->txnbufq_mutex); \ + } while (0) + +#define HTT_TX_NBUF_QUEUE_ADD(_pdev, _msdu) do { \ + HTT_TX_MUTEX_ACQUIRE(&_pdev->txnbufq_mutex); \ + qdf_nbuf_queue_add(&_pdev->txnbufq, _msdu); \ + HTT_TX_MUTEX_RELEASE(&_pdev->txnbufq_mutex); \ + } while (0) + +#define HTT_TX_NBUF_QUEUE_INSERT_HEAD(_pdev, _msdu) do { \ + HTT_TX_MUTEX_ACQUIRE(&_pdev->txnbufq_mutex); \ + qdf_nbuf_queue_insert_head(&_pdev->txnbufq, _msdu);\ + HTT_TX_MUTEX_RELEASE(&_pdev->txnbufq_mutex); \ + } while (0) +#else + +#define HTT_TX_NBUF_QUEUE_MUTEX_INIT(_pdev) +#define HTT_TX_NBUF_QUEUE_REMOVE(_pdev, _msdu) +#define HTT_TX_NBUF_QUEUE_ADD(_pdev, _msdu) +#define HTT_TX_NBUF_QUEUE_INSERT_HEAD(_pdev, _msdu) +#define HTT_TX_NBUF_QUEUE_MUTEX_DESTROY(_pdev) + +#endif + +#ifdef CONFIG_HL_SUPPORT + +static inline void htt_tx_resume_handler(void *context) +{ +} +#else + +void htt_tx_resume_handler(void *context); +#endif + +#ifdef ATH_11AC_TXCOMPACT +#define HTT_TX_SCHED htt_tx_sched +#else +#define HTT_TX_SCHED(pdev) /* no-op */ +#endif + +int htt_tx_attach(struct htt_pdev_t *pdev, int desc_pool_elems); + +void htt_tx_detach(struct htt_pdev_t *pdev); + +int htt_rx_attach(struct htt_pdev_t *pdev); + +#if defined(CONFIG_HL_SUPPORT) + +static inline void htt_rx_detach(struct htt_pdev_t *pdev) +{ +} +#else + +void htt_rx_detach(struct htt_pdev_t *pdev); +#endif + +int htt_htc_attach(struct htt_pdev_t *pdev, uint16_t service_id); + +void htt_t2h_msg_handler(void *context, HTC_PACKET *pkt); +#ifdef WLAN_FEATURE_FASTPATH +void htt_t2h_msg_handler_fast(void *htt_pdev, qdf_nbuf_t *cmpl_msdus, + uint32_t num_cmpls); +#else +static inline void htt_t2h_msg_handler_fast(void *htt_pdev, + qdf_nbuf_t *cmpl_msdus, + uint32_t num_cmpls) +{ +} +#endif + +void htt_h2t_send_complete(void *context, HTC_PACKET *pkt); + +QDF_STATUS htt_h2t_ver_req_msg(struct htt_pdev_t *pdev); + +int htt_tx_padding_credit_update_handler(void *context, int pad_credit); + +#if defined(HELIUMPLUS) +QDF_STATUS +htt_h2t_frag_desc_bank_cfg_msg(struct htt_pdev_t *pdev); +#endif /* defined(HELIUMPLUS) */ + +QDF_STATUS htt_h2t_rx_ring_cfg_msg_ll(struct htt_pdev_t *pdev); + +QDF_STATUS htt_h2t_rx_ring_rfs_cfg_msg_ll(struct htt_pdev_t *pdev); + +QDF_STATUS htt_h2t_rx_ring_rfs_cfg_msg_hl(struct htt_pdev_t *pdev); + +QDF_STATUS htt_h2t_rx_ring_cfg_msg_hl(struct htt_pdev_t *pdev); + +extern QDF_STATUS (*htt_h2t_rx_ring_cfg_msg)(struct htt_pdev_t *pdev); + +enum htc_send_full_action htt_h2t_full(void *context, HTC_PACKET *pkt); + +struct htt_htc_pkt *htt_htc_pkt_alloc(struct htt_pdev_t *pdev); + +void htt_htc_pkt_free(struct htt_pdev_t *pdev, struct htt_htc_pkt *pkt); + +void htt_htc_pkt_pool_free(struct htt_pdev_t *pdev); + +#ifdef ATH_11AC_TXCOMPACT +void htt_htc_misc_pkt_list_trim(struct htt_pdev_t *pdev, int level); + +void +htt_htc_misc_pkt_list_add(struct htt_pdev_t *pdev, struct htt_htc_pkt *pkt); + +void htt_htc_misc_pkt_pool_free(struct htt_pdev_t *pdev); +#endif + +#ifdef WLAN_FULL_REORDER_OFFLOAD +int +htt_rx_hash_list_insert(struct htt_pdev_t *pdev, + qdf_dma_addr_t paddr, + qdf_nbuf_t netbuf); +#else +static inline int +htt_rx_hash_list_insert(struct htt_pdev_t *pdev, + qdf_dma_addr_t paddr, + qdf_nbuf_t netbuf) +{ + return 0; +} +#endif + +qdf_nbuf_t +htt_rx_hash_list_lookup(struct htt_pdev_t *pdev, qdf_dma_addr_t paddr); + +#ifdef IPA_OFFLOAD +int +htt_tx_ipa_uc_attach(struct htt_pdev_t *pdev, + unsigned int uc_tx_buf_sz, + unsigned int uc_tx_buf_cnt, + unsigned int uc_tx_partition_base); + +int +htt_rx_ipa_uc_attach(struct htt_pdev_t *pdev, unsigned int rx_ind_ring_size); + +int htt_tx_ipa_uc_detach(struct htt_pdev_t *pdev); + +int htt_rx_ipa_uc_detach(struct htt_pdev_t *pdev); + +#else +/** + * htt_tx_ipa_uc_attach() - attach htt ipa uc tx resource + * @pdev: htt context + * @uc_tx_buf_sz: single tx buffer size + * @uc_tx_buf_cnt: total tx buffer count + * @uc_tx_partition_base: tx buffer partition start + * + * Return: 0 success + */ +static inline int +htt_tx_ipa_uc_attach(struct htt_pdev_t *pdev, + unsigned int uc_tx_buf_sz, + unsigned int uc_tx_buf_cnt, + unsigned int uc_tx_partition_base) +{ + return 0; +} + +/** + * htt_rx_ipa_uc_attach() - attach htt ipa uc rx resource + * @pdev: htt context + * @rx_ind_ring_size: rx ring size + * + * Return: 0 success + */ +static inline int +htt_rx_ipa_uc_attach(struct htt_pdev_t *pdev, unsigned int rx_ind_ring_size) +{ + return 0; +} + +static inline int htt_tx_ipa_uc_detach(struct htt_pdev_t *pdev) +{ + return 0; +} + +static inline int htt_rx_ipa_uc_detach(struct htt_pdev_t *pdev) +{ + return 0; +} + +#endif /* IPA_OFFLOAD */ + +/* Maximum Outstanding Bus Download */ +#define HTT_MAX_BUS_CREDIT 33 + +#ifdef CONFIG_HL_SUPPORT + +/** + * htt_tx_credit_update() - check for diff in bus delta and target delta + * @pdev: pointer to htt device. + * + * Return: min of bus delta and target delta + */ +int +htt_tx_credit_update(struct htt_pdev_t *pdev); +#else + +static inline int +htt_tx_credit_update(struct htt_pdev_t *pdev) +{ + return 0; +} +#endif + + +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + +#define HTT_TX_GROUP_INDEX_OFFSET \ +(sizeof(struct htt_txq_group) / sizeof(u_int32_t)) + +void htt_tx_group_credit_process(struct htt_pdev_t *pdev, u_int32_t *msg_word); +#else + +static inline +void htt_tx_group_credit_process(struct htt_pdev_t *pdev, u_int32_t *msg_word) +{ +} +#endif + +#ifdef DEBUG_RX_RING_BUFFER +/** + * htt_rx_dbg_rxbuf_init() - init debug rx buff list + * @pdev: pdev handle + * + * Allocation is done from bss segment. This uses vmalloc and has a bit + * of an overhead compared to kmalloc (which qdf_mem_alloc wraps). The impact + * of the overhead to performance will need to be quantified. + * + * Return: none + */ +static struct rx_buf_debug rx_buff_list_bss[HTT_RX_RING_BUFF_DBG_LIST]; +static inline +void htt_rx_dbg_rxbuf_init(struct htt_pdev_t *pdev) +{ + pdev->rx_buff_list = rx_buff_list_bss; + qdf_spinlock_create(&(pdev->rx_buff_list_lock)); + pdev->rx_buff_index = 0; + pdev->rx_buff_posted_cum = 0; + pdev->rx_buff_recvd_cum = 0; + pdev->rx_buff_recvd_err = 0; + pdev->refill_retry_timer_starts = 0; + pdev->refill_retry_timer_calls = 0; + pdev->refill_retry_timer_doubles = 0; +} + +/** + * htt_display_rx_buf_debug() - display debug rx buff list and some counters + * @pdev: pdev handle + * + * Return: Success + */ +static inline int htt_display_rx_buf_debug(struct htt_pdev_t *pdev) +{ + int i; + struct rx_buf_debug *buf; + + if ((pdev) && + (pdev->rx_buff_list)) { + buf = pdev->rx_buff_list; + for (i = 0; i < HTT_RX_RING_BUFF_DBG_LIST; i++) { + if (buf[i].posted != 0) + qdf_nofl_info("[%d][0x%x] %pK %lu %pK %llu %llu", + i, buf[i].cpu, + buf[i].nbuf_data, + (unsigned long)buf[i].paddr, + buf[i].nbuf, + buf[i].posted, + buf[i].recved); + } + + qdf_nofl_info("rxbuf_idx %d all_posted: %d all_recvd: %d recv_err: %d", + pdev->rx_buff_index, + pdev->rx_buff_posted_cum, + pdev->rx_buff_recvd_cum, + pdev->rx_buff_recvd_err); + + qdf_nofl_info("timer kicks :%d actual :%d restarts:%d debtors: %d fill_n: %d", + pdev->refill_retry_timer_starts, + pdev->refill_retry_timer_calls, + pdev->refill_retry_timer_doubles, + pdev->rx_buff_debt_invoked, + pdev->rx_buff_fill_n_invoked); + } else + return -EINVAL; + return 0; +} + +/** + * htt_rx_dbg_rxbuf_set() - set element of rx buff list + * @pdev: pdev handle + * @paddr: physical address of netbuf + * @rx_netbuf: received netbuf + * + * Return: none + */ +static inline +void htt_rx_dbg_rxbuf_set(struct htt_pdev_t *pdev, qdf_dma_addr_t paddr, + qdf_nbuf_t rx_netbuf) +{ + if (pdev->rx_buff_list) { + qdf_spin_lock_bh(&(pdev->rx_buff_list_lock)); + pdev->rx_buff_list[pdev->rx_buff_index].paddr = paddr; + pdev->rx_buff_list[pdev->rx_buff_index].nbuf = rx_netbuf; + pdev->rx_buff_list[pdev->rx_buff_index].nbuf_data = + rx_netbuf->data; + pdev->rx_buff_list[pdev->rx_buff_index].posted = + qdf_get_log_timestamp(); + pdev->rx_buff_posted_cum++; + pdev->rx_buff_list[pdev->rx_buff_index].recved = 0; + pdev->rx_buff_list[pdev->rx_buff_index].cpu = + (1 << qdf_get_cpu()); + QDF_NBUF_CB_RX_MAP_IDX(rx_netbuf) = pdev->rx_buff_index; + if (++pdev->rx_buff_index >= + HTT_RX_RING_BUFF_DBG_LIST) + pdev->rx_buff_index = 0; + qdf_spin_unlock_bh(&(pdev->rx_buff_list_lock)); + } +} + +/** + * htt_rx_dbg_rxbuf_set() - reset element of rx buff list + * @pdev: pdev handle + * @netbuf: rx sk_buff + * Return: none + */ +static inline +void htt_rx_dbg_rxbuf_reset(struct htt_pdev_t *pdev, + qdf_nbuf_t netbuf) +{ + uint32_t index; + + if (pdev->rx_buff_list) { + qdf_spin_lock_bh(&(pdev->rx_buff_list_lock)); + index = QDF_NBUF_CB_RX_MAP_IDX(netbuf); + if (index < HTT_RX_RING_BUFF_DBG_LIST) { + pdev->rx_buff_list[index].recved = + qdf_get_log_timestamp(); + pdev->rx_buff_recvd_cum++; + } else { + pdev->rx_buff_recvd_err++; + } + pdev->rx_buff_list[pdev->rx_buff_index].cpu |= + (1 << qdf_get_cpu()); + qdf_spin_unlock_bh(&(pdev->rx_buff_list_lock)); + } +} +/** + * htt_rx_dbg_rxbuf_indupd() - add a record for alloc index update + * @pdev: pdev handle + * @idx : value of the index + * + * Return: none + */ +static inline +void htt_rx_dbg_rxbuf_indupd(struct htt_pdev_t *pdev, int alloc_index) +{ + if (pdev->rx_buff_list) { + qdf_spin_lock_bh(&(pdev->rx_buff_list_lock)); + pdev->rx_buff_list[pdev->rx_buff_index].paddr = 0; + pdev->rx_buff_list[pdev->rx_buff_index].nbuf = 0; + pdev->rx_buff_list[pdev->rx_buff_index].nbuf_data = 0; + pdev->rx_buff_list[pdev->rx_buff_index].posted = + qdf_get_log_timestamp(); + pdev->rx_buff_list[pdev->rx_buff_index].recved = + (uint64_t)alloc_index; + pdev->rx_buff_list[pdev->rx_buff_index].cpu = + (1 << qdf_get_cpu()); + if (++pdev->rx_buff_index >= + HTT_RX_RING_BUFF_DBG_LIST) + pdev->rx_buff_index = 0; + qdf_spin_unlock_bh(&(pdev->rx_buff_list_lock)); + } +} +/** + * htt_rx_dbg_rxbuf_httrxind() - add a record for recipt of htt rx_ind msg + * @pdev: pdev handle + * + * Return: none + */ +static inline +void htt_rx_dbg_rxbuf_httrxind(struct htt_pdev_t *pdev, unsigned int msdu_cnt) +{ + if (pdev->rx_buff_list) { + qdf_spin_lock_bh(&(pdev->rx_buff_list_lock)); + pdev->rx_buff_list[pdev->rx_buff_index].paddr = msdu_cnt; + pdev->rx_buff_list[pdev->rx_buff_index].nbuf = 0; + pdev->rx_buff_list[pdev->rx_buff_index].nbuf_data = 0; + pdev->rx_buff_list[pdev->rx_buff_index].posted = + qdf_get_log_timestamp(); + pdev->rx_buff_list[pdev->rx_buff_index].recved = + (uint64_t)0x48545452584D5347; /* 'HTTRXMSG' */ + pdev->rx_buff_list[pdev->rx_buff_index].cpu = + (1 << qdf_get_cpu()); + if (++pdev->rx_buff_index >= + HTT_RX_RING_BUFF_DBG_LIST) + pdev->rx_buff_index = 0; + qdf_spin_unlock_bh(&(pdev->rx_buff_list_lock)); + } +} + +/** + * htt_rx_dbg_rxbuf_deinit() - deinit debug rx buff list + * @pdev: pdev handle + * + * Return: none + */ +static inline +void htt_rx_dbg_rxbuf_deinit(struct htt_pdev_t *pdev) +{ + if (pdev->rx_buff_list) + pdev->rx_buff_list = NULL; + qdf_spinlock_destroy(&(pdev->rx_buff_list_lock)); +} +#else +static inline +void htt_rx_dbg_rxbuf_init(struct htt_pdev_t *pdev) +{ +} +static inline int htt_display_rx_buf_debug(struct htt_pdev_t *pdev) +{ + return 0; +} + +static inline +void htt_rx_dbg_rxbuf_set(struct htt_pdev_t *pdev, + uint32_t paddr, + qdf_nbuf_t rx_netbuf) +{ +} +static inline +void htt_rx_dbg_rxbuf_reset(struct htt_pdev_t *pdev, + qdf_nbuf_t netbuf) +{ +} +static inline +void htt_rx_dbg_rxbuf_indupd(struct htt_pdev_t *pdev, + int alloc_index) +{ +} +static inline +void htt_rx_dbg_rxbuf_httrxind(struct htt_pdev_t *pdev, + unsigned int msdu_cnt) +{ +} +static inline +void htt_rx_dbg_rxbuf_deinit(struct htt_pdev_t *pdev) +{ + return; +} +#endif + +#ifndef HTT_RX_RING_SIZE_MIN +#define HTT_RX_RING_SIZE_MIN 128 /* slightly > than one large A-MPDU */ +#endif + +#ifndef HTT_RX_RING_SIZE_MAX +#define HTT_RX_RING_SIZE_MAX 2048 /* ~20 ms @ 1 Gbps of 1500B MSDUs */ +#endif + +#ifndef HTT_RX_AVG_FRM_BYTES +#define HTT_RX_AVG_FRM_BYTES 1000 +#endif + +#define HTT_FCS_LEN (4) + +#ifdef HTT_DEBUG_DATA +#define HTT_PKT_DUMP(x) x +#else +#define HTT_PKT_DUMP(x) /* no-op */ +#endif + +#ifdef RX_HASH_DEBUG +#define HTT_RX_CHECK_MSDU_COUNT(msdu_count) HTT_ASSERT_ALWAYS(msdu_count) +#else +#define HTT_RX_CHECK_MSDU_COUNT(msdu_count) /* no-op */ +#endif + +#if HTT_PADDR64 +#define NEXT_FIELD_OFFSET_IN32 2 +#else /* ! HTT_PADDR64 */ +#define NEXT_FIELD_OFFSET_IN32 1 +#endif /* HTT_PADDR64 */ + +#define RX_PADDR_MAGIC_PATTERN 0xDEAD0000 + +#if HTT_PADDR64 +static inline qdf_dma_addr_t htt_paddr_trim_to_37(qdf_dma_addr_t paddr) +{ + qdf_dma_addr_t ret = paddr; + + if (sizeof(paddr) > 4) + ret &= 0x1fffffffff; + return ret; +} +#else /* not 64 bits */ +static inline qdf_dma_addr_t htt_paddr_trim_to_37(qdf_dma_addr_t paddr) +{ + return paddr; +} +#endif /* HTT_PADDR64 */ + +#ifdef WLAN_FULL_REORDER_OFFLOAD +#ifdef ENABLE_DEBUG_ADDRESS_MARKING +static inline qdf_dma_addr_t +htt_rx_paddr_unmark_high_bits(qdf_dma_addr_t paddr) +{ + uint32_t markings; + + if (sizeof(qdf_dma_addr_t) > 4) { + markings = (uint32_t)((paddr >> 16) >> 16); + /* + * check if it is marked correctly: + * See the mark_high_bits function above for the expected + * pattern. + * the LS 5 bits are the high bits of physical address + * padded (with 0b0) to 8 bits + */ + if ((markings & 0xFFFF0000) != RX_PADDR_MAGIC_PATTERN) { + qdf_print("paddr not marked correctly: 0x%pK!\n", + (void *)paddr); + HTT_ASSERT_ALWAYS(0); + } + + /* clear markings for further use */ + paddr = htt_paddr_trim_to_37(paddr); + } + return paddr; +} + +static inline +qdf_dma_addr_t htt_rx_in_ord_paddr_get(uint32_t *u32p) +{ + qdf_dma_addr_t paddr = 0; + + paddr = (qdf_dma_addr_t)HTT_RX_IN_ORD_PADDR_IND_PADDR_GET(*u32p); + if (sizeof(qdf_dma_addr_t) > 4) { + u32p++; + /* 32 bit architectures dont like <<32 */ + paddr |= (((qdf_dma_addr_t) + HTT_RX_IN_ORD_PADDR_IND_PADDR_GET(*u32p)) + << 16 << 16); + } + paddr = htt_rx_paddr_unmark_high_bits(paddr); + + return paddr; +} +#else +#if HTT_PADDR64 +static inline +qdf_dma_addr_t htt_rx_in_ord_paddr_get(uint32_t *u32p) +{ + qdf_dma_addr_t paddr = 0; + + paddr = (qdf_dma_addr_t)HTT_RX_IN_ORD_PADDR_IND_PADDR_GET(*u32p); + if (sizeof(qdf_dma_addr_t) > 4) { + u32p++; + /* 32 bit architectures dont like <<32 */ + paddr |= (((qdf_dma_addr_t) + HTT_RX_IN_ORD_PADDR_IND_PADDR_GET(*u32p)) + << 16 << 16); + } + return paddr; +} +#else +static inline +qdf_dma_addr_t htt_rx_in_ord_paddr_get(uint32_t *u32p) +{ + return HTT_RX_IN_ORD_PADDR_IND_PADDR_GET(*u32p); +} +#endif +#endif /* ENABLE_DEBUG_ADDRESS_MARKING */ + +static inline +unsigned int htt_rx_in_order_ring_elems(struct htt_pdev_t *pdev) +{ + return (*pdev->rx_ring.alloc_idx.vaddr - + *pdev->rx_ring.target_idx.vaddr) & + pdev->rx_ring.size_mask; +} + +static inline qdf_nbuf_t +htt_rx_in_order_netbuf_pop(htt_pdev_handle pdev, qdf_dma_addr_t paddr) +{ + HTT_ASSERT1(htt_rx_in_order_ring_elems(pdev) != 0); + pdev->rx_ring.fill_cnt--; + paddr = htt_paddr_trim_to_37(paddr); + return htt_rx_hash_list_lookup(pdev, paddr); +} + +#else +static inline +qdf_dma_addr_t htt_rx_in_ord_paddr_get(uint32_t *u32p) +{ + return 0; +} + +static inline qdf_nbuf_t +htt_rx_in_order_netbuf_pop(htt_pdev_handle pdev, qdf_dma_addr_t paddr) +{ + return NULL; +} +#endif + +#if defined(FEATURE_MONITOR_MODE_SUPPORT) && defined(WLAN_FULL_REORDER_OFFLOAD) +int htt_rx_mon_amsdu_rx_in_order_pop_ll(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, + qdf_nbuf_t *tail_msdu, + uint32_t *replenish_cnt); +#else +static inline +int htt_rx_mon_amsdu_rx_in_order_pop_ll(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, + qdf_nbuf_t *tail_msdu, + uint32_t *replenish_cnt) +{ + return 0; +} +#endif + +#endif /* _HTT_INTERNAL__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_monitor_rx.c b/drivers/staging/qcacld-3.0/core/dp/htt/htt_monitor_rx.c new file mode 100644 index 0000000000000000000000000000000000000000..7fafa7e48a9a7cd8396f077aea639c2c04e4ccc8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_monitor_rx.c @@ -0,0 +1,1087 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_mem_malloc,free, etc. */ +#include /* qdf_print, bool */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_timer_free */ + +#include /* HTT_HL_RX_DESC_SIZE */ +#include +#include +#include +#include /* HTT_ASSERT, htt_pdev_t, HTT_RX_BUF_SIZE */ +#include "regtable.h" + +#include /* ieee80211_frame, ieee80211_qoscntl */ +#include +#include +#include "ol_txrx_types.h" +#ifdef DEBUG_DMA_DONE +#include +#include +#endif +#include + +#define HTT_FCS_LEN (4) + +enum { + HW_RX_DECAP_FORMAT_RAW = 0, + HW_RX_DECAP_FORMAT_NWIFI, + HW_RX_DECAP_FORMAT_8023, + HW_RX_DECAP_FORMAT_ETH2, +}; + +struct mon_rx_status g_ppdu_rx_status; + +/** + * htt_rx_mon_note_capture_channel() - Make note of channel to update in + * radiotap + * @pdev: handle to htt_pdev + * @mon_ch: capture channel number. + * + * Return: None + */ +void htt_rx_mon_note_capture_channel(htt_pdev_handle pdev, int mon_ch) +{ + struct mon_channel *ch_info = &pdev->mon_ch_info; + + ch_info->ch_num = mon_ch; + ch_info->ch_freq = cds_chan_to_freq(mon_ch); +} + +#ifndef CONFIG_HL_SUPPORT +/** + * htt_mon_rx_handle_amsdu_packet() - Handle consecutive fragments of amsdu + * @msdu: pointer to first msdu of amsdu + * @pdev: Handle to htt_pdev_handle + * @msg_word: Input and output variable, so pointer to HTT msg pointer + * @amsdu_len: remaining length of all N-1 msdu msdu's + * @frag_cnt: number of frags handled + * + * This function handles the (N-1) msdu's of amsdu, N'th msdu is already + * handled by calling function. N-1 msdu's are tied using frags_list. + * msdu_info field updated by FW indicates if this is last msdu. All the + * msdu's before last msdu will be of MAX payload. + * + * Return: 1 on success and 0 on failure. + */ +static +int htt_mon_rx_handle_amsdu_packet(qdf_nbuf_t msdu, htt_pdev_handle pdev, + uint32_t **msg_word, uint32_t amsdu_len, + uint32_t *frag_cnt) +{ + qdf_nbuf_t frag_nbuf; + qdf_nbuf_t prev_frag_nbuf; + uint32_t len; + uint32_t last_frag; + qdf_dma_addr_t paddr; + + *msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS; + paddr = htt_rx_in_ord_paddr_get(*msg_word); + frag_nbuf = htt_rx_in_order_netbuf_pop(pdev, paddr); + if (qdf_unlikely(!frag_nbuf)) { + qdf_print("netbuf pop failed!"); + return 0; + } + *frag_cnt = *frag_cnt + 1; + last_frag = ((struct htt_rx_in_ord_paddr_ind_msdu_t *)*msg_word)-> + msdu_info; + qdf_nbuf_append_ext_list(msdu, frag_nbuf, amsdu_len); + qdf_nbuf_set_pktlen(frag_nbuf, HTT_RX_BUF_SIZE); + qdf_nbuf_unmap(pdev->osdev, frag_nbuf, QDF_DMA_FROM_DEVICE); + /* For msdu's other than parent will not have htt_host_rx_desc_base */ + len = QDF_MIN(amsdu_len, HTT_RX_BUF_SIZE); + amsdu_len -= len; + qdf_nbuf_trim_tail(frag_nbuf, HTT_RX_BUF_SIZE - len); + + HTT_PKT_DUMP(qdf_trace_hex_dump(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO_HIGH, + qdf_nbuf_data(frag_nbuf), + qdf_nbuf_len(frag_nbuf))); + prev_frag_nbuf = frag_nbuf; + while (!last_frag) { + *msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS; + paddr = htt_rx_in_ord_paddr_get(*msg_word); + frag_nbuf = htt_rx_in_order_netbuf_pop(pdev, paddr); + last_frag = ((struct htt_rx_in_ord_paddr_ind_msdu_t *) + *msg_word)->msdu_info; + + if (qdf_unlikely(!frag_nbuf)) { + qdf_print("netbuf pop failed!"); + prev_frag_nbuf->next = NULL; + return 0; + } + *frag_cnt = *frag_cnt + 1; + qdf_nbuf_set_pktlen(frag_nbuf, HTT_RX_BUF_SIZE); + qdf_nbuf_unmap(pdev->osdev, frag_nbuf, QDF_DMA_FROM_DEVICE); + + len = QDF_MIN(amsdu_len, HTT_RX_BUF_SIZE); + amsdu_len -= len; + qdf_nbuf_trim_tail(frag_nbuf, HTT_RX_BUF_SIZE - len); + HTT_PKT_DUMP(qdf_trace_hex_dump(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO_HIGH, + qdf_nbuf_data(frag_nbuf), + qdf_nbuf_len(frag_nbuf))); + + qdf_nbuf_set_next(prev_frag_nbuf, frag_nbuf); + prev_frag_nbuf = frag_nbuf; + } + qdf_nbuf_set_next(prev_frag_nbuf, NULL); + return 1; +} + +#define SHORT_PREAMBLE 1 +#define LONG_PREAMBLE 0 +#ifdef HELIUMPLUS +/** + * htt_rx_get_rate() - get rate info in terms of 500Kbps from htt_rx_desc + * @l_sig_rate_select: OFDM or CCK rate + * @l_sig_rate: + * + * If l_sig_rate_select is 0: + * 0x8: OFDM 48 Mbps + * 0x9: OFDM 24 Mbps + * 0xA: OFDM 12 Mbps + * 0xB: OFDM 6 Mbps + * 0xC: OFDM 54 Mbps + * 0xD: OFDM 36 Mbps + * 0xE: OFDM 18 Mbps + * 0xF: OFDM 9 Mbps + * If l_sig_rate_select is 1: + * 0x1: DSSS 1 Mbps long preamble + * 0x2: DSSS 2 Mbps long preamble + * 0x3: CCK 5.5 Mbps long preamble + * 0x4: CCK 11 Mbps long preamble + * 0x5: DSSS 2 Mbps short preamble + * 0x6: CCK 5.5 Mbps + * 0x7: CCK 11 Mbps short preamble + * + * Return: rate interms of 500Kbps. + */ +static unsigned char htt_rx_get_rate(uint32_t l_sig_rate_select, + uint32_t l_sig_rate, uint8_t *preamble) +{ + char ret = 0x0; + *preamble = SHORT_PREAMBLE; + if (l_sig_rate_select == 0) { + switch (l_sig_rate) { + case 0x8: + ret = 0x60; + break; + case 0x9: + ret = 0x30; + break; + case 0xA: + ret = 0x18; + break; + case 0xB: + ret = 0x0c; + break; + case 0xC: + ret = 0x6c; + break; + case 0xD: + ret = 0x48; + break; + case 0xE: + ret = 0x24; + break; + case 0xF: + ret = 0x12; + break; + default: + break; + } + } else if (l_sig_rate_select == 1) { + switch (l_sig_rate) { + case 0x1: + ret = 0x2; + *preamble = LONG_PREAMBLE; + break; + case 0x2: + ret = 0x4; + *preamble = LONG_PREAMBLE; + break; + case 0x3: + ret = 0xB; + *preamble = LONG_PREAMBLE; + break; + case 0x4: + ret = 0x16; + *preamble = LONG_PREAMBLE; + break; + case 0x5: + ret = 0x4; + break; + case 0x6: + ret = 0xB; + break; + case 0x7: + ret = 0x16; + break; + default: + break; + } + } else { + qdf_print("Invalid rate info\n"); + } + return ret; +} +#else +/** + * htt_rx_get_rate() - get rate info in terms of 500Kbps from htt_rx_desc + * @l_sig_rate_select: OFDM or CCK rate + * @l_sig_rate: + * + * If l_sig_rate_select is 0: + * 0x8: OFDM 48 Mbps + * 0x9: OFDM 24 Mbps + * 0xA: OFDM 12 Mbps + * 0xB: OFDM 6 Mbps + * 0xC: OFDM 54 Mbps + * 0xD: OFDM 36 Mbps + * 0xE: OFDM 18 Mbps + * 0xF: OFDM 9 Mbps + * If l_sig_rate_select is 1: + * 0x8: CCK 11 Mbps long preamble + * 0x9: CCK 5.5 Mbps long preamble + * 0xA: CCK 2 Mbps long preamble + * 0xB: CCK 1 Mbps long preamble + * 0xC: CCK 11 Mbps short preamble + * 0xD: CCK 5.5 Mbps short preamble + * 0xE: CCK 2 Mbps short preamble + * + * Return: rate interms of 500Kbps. + */ +static unsigned char htt_rx_get_rate(uint32_t l_sig_rate_select, + uint32_t l_sig_rate, uint8_t *preamble) +{ + char ret = 0x0; + *preamble = SHORT_PREAMBLE; + if (l_sig_rate_select == 0) { + switch (l_sig_rate) { + case 0x8: + ret = 0x60; + break; + case 0x9: + ret = 0x30; + break; + case 0xA: + ret = 0x18; + break; + case 0xB: + ret = 0x0c; + break; + case 0xC: + ret = 0x6c; + break; + case 0xD: + ret = 0x48; + break; + case 0xE: + ret = 0x24; + break; + case 0xF: + ret = 0x12; + break; + default: + break; + } + } else if (l_sig_rate_select == 1) { + switch (l_sig_rate) { + case 0x8: + ret = 0x16; + *preamble = LONG_PREAMBLE; + break; + case 0x9: + ret = 0x0B; + *preamble = LONG_PREAMBLE; + break; + case 0xA: + ret = 0x4; + *preamble = LONG_PREAMBLE; + break; + case 0xB: + ret = 0x02; + *preamble = LONG_PREAMBLE; + break; + case 0xC: + ret = 0x16; + break; + case 0xD: + ret = 0x0B; + break; + case 0xE: + ret = 0x04; + break; + default: + break; + } + } else { + qdf_print("Invalid rate info\n"); + } + return ret; +} +#endif /* HELIUMPLUS */ + +/** + * htt_mon_rx_get_phy_info() - Get phy info + * @rx_desc: Pointer to struct htt_host_rx_desc_base + * @rx_status: Return variable updated with phy_info in rx_status + * + * Return: None + */ +static void htt_mon_rx_get_phy_info(struct htt_host_rx_desc_base *rx_desc, + struct mon_rx_status *rx_status) +{ + uint8_t preamble = 0; + uint8_t preamble_type = rx_desc->ppdu_start.preamble_type; + uint8_t mcs = 0, nss = 0, sgi = 0, bw = 0, beamformed = 0; + uint16_t vht_flags = 0, ht_flags = 0; + uint32_t l_sig_rate_select = rx_desc->ppdu_start.l_sig_rate_select; + uint32_t l_sig_rate = rx_desc->ppdu_start.l_sig_rate; + bool is_stbc = 0, ldpc = 0; + + switch (preamble_type) { + case 4: + /* legacy */ + rx_status->rate = htt_rx_get_rate(l_sig_rate_select, l_sig_rate, + &preamble); + break; + case 8: + is_stbc = ((VHT_SIG_A_2(rx_desc) >> 4) & 3); + /* fallthrough */ + case 9: + ht_flags = 1; + sgi = (VHT_SIG_A_2(rx_desc) >> 7) & 0x01; + bw = (VHT_SIG_A_1(rx_desc) >> 7) & 0x01; + mcs = (VHT_SIG_A_1(rx_desc) & 0x7f); + nss = mcs >> 3; + beamformed = + (VHT_SIG_A_2(rx_desc) >> 8) & 0x1; + break; + case 0x0c: + is_stbc = (VHT_SIG_A_2(rx_desc) >> 3) & 1; + ldpc = (VHT_SIG_A_2(rx_desc) >> 2) & 1; + /* fallthrough */ + case 0x0d: + { + uint8_t gid_in_sig = ((VHT_SIG_A_1(rx_desc) >> 4) & 0x3f); + + vht_flags = 1; + sgi = VHT_SIG_A_2(rx_desc) & 0x01; + bw = (VHT_SIG_A_1(rx_desc) & 0x03); + if (gid_in_sig == 0 || gid_in_sig == 63) { + /* SU case */ + mcs = (VHT_SIG_A_2(rx_desc) >> 4) & + 0xf; + nss = (VHT_SIG_A_1(rx_desc) >> 10) & + 0x7; + } else { + /* MU case */ + uint8_t sta_user_pos = + (uint8_t)((rx_desc->ppdu_start.reserved_4a >> 8) + & 0x3); + mcs = (rx_desc->ppdu_start.vht_sig_b >> 16); + if (bw >= 2) + mcs >>= 3; + else if (bw > 0) + mcs >>= 1; + mcs &= 0xf; + nss = (((VHT_SIG_A_1(rx_desc) >> 10) + + sta_user_pos * 3) & 0x7); + } + beamformed = (VHT_SIG_A_2(rx_desc) >> 8) & 0x1; + } + /* fallthrough */ + default: + break; + } + + rx_status->mcs = mcs; + rx_status->bw = bw; + rx_status->nr_ant = nss; + rx_status->is_stbc = is_stbc; + rx_status->sgi = sgi; + rx_status->ldpc = ldpc; + rx_status->beamformed = beamformed; + rx_status->vht_flag_values3[0] = mcs << 0x4 | (nss + 1); + if (ht_flags) + rx_status->ht_mcs = mcs; + rx_status->ht_flags = ht_flags; + rx_status->vht_flags = vht_flags; + rx_status->rtap_flags |= ((preamble == SHORT_PREAMBLE) ? BIT(1) : 0); + rx_status->vht_flag_values2 = bw; +} + +/** + * htt_mon_rx_get_rtap_flags() - Get radiotap flags + * @rx_desc: Pointer to struct htt_host_rx_desc_base + * + * Return: Bitmapped radiotap flags. + */ +static uint8_t htt_mon_rx_get_rtap_flags(struct htt_host_rx_desc_base *rx_desc) +{ + uint8_t rtap_flags = 0; + + /* WEP40 || WEP104 || WEP128 */ + if (rx_desc->mpdu_start.encrypt_type == 0 || + rx_desc->mpdu_start.encrypt_type == 1 || + rx_desc->mpdu_start.encrypt_type == 3) + rtap_flags |= BIT(2); + + /* IEEE80211_RADIOTAP_F_FRAG */ + if (rx_desc->attention.fragment) + rtap_flags |= BIT(3); + + /* IEEE80211_RADIOTAP_F_FCS */ + rtap_flags |= BIT(4); + + /* IEEE80211_RADIOTAP_F_BADFCS */ + if (rx_desc->mpdu_end.fcs_err) + rtap_flags |= BIT(6); + + return rtap_flags; +} + +/** + * htt_rx_mon_get_rx_status() - Update information about the rx status, + * which is used later for radiotap updation. + * @rx_desc: Pointer to struct htt_host_rx_desc_base + * @rx_status: Return variable updated with rx_status + * + * Return: None + */ +static void htt_rx_mon_get_rx_status(htt_pdev_handle pdev, + struct htt_host_rx_desc_base *rx_desc, + struct mon_rx_status *rx_status) +{ + uint16_t channel_flags = 0; + struct mon_channel *ch_info = &pdev->mon_ch_info; + + rx_status->tsft = (u_int64_t)TSF_TIMESTAMP(rx_desc); + rx_status->chan_freq = ch_info->ch_freq; + rx_status->chan_num = ch_info->ch_num; + htt_mon_rx_get_phy_info(rx_desc, rx_status); + rx_status->rtap_flags |= htt_mon_rx_get_rtap_flags(rx_desc); + channel_flags |= rx_desc->ppdu_start.l_sig_rate_select ? + IEEE80211_CHAN_CCK : IEEE80211_CHAN_OFDM; + channel_flags |= + (cds_chan_to_band(ch_info->ch_num) == CDS_BAND_2GHZ ? + IEEE80211_CHAN_2GHZ : IEEE80211_CHAN_5GHZ); + + rx_status->chan_flags = channel_flags; + rx_status->ant_signal_db = rx_desc->ppdu_start.rssi_comb; + rx_status->rssi_comb = rx_desc->ppdu_start.rssi_comb; + rx_status->chan_noise_floor = pdev->txrx_pdev->chan_noise_floor; +} + +/** + * htt_rx_mon_amsdu_rx_in_order_pop_ll() - Monitor mode HTT Rx in order pop + * function + * @pdev: Handle to htt_pdev_handle + * @rx_ind_msg: In order indication message. + * @head_msdu: Return variable pointing to head msdu. + * @tail_msdu: Return variable pointing to tail msdu. + * + * This function pops the msdu based on paddr:length of inorder indication + * message. + * + * Return: 1 for success, 0 on failure. + */ +int htt_rx_mon_amsdu_rx_in_order_pop_ll(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, + qdf_nbuf_t *tail_msdu, + uint32_t *replenish_cnt) +{ + qdf_nbuf_t msdu, next, prev = NULL; + uint8_t *rx_ind_data; + uint32_t *msg_word; + uint32_t msdu_count; + struct htt_host_rx_desc_base *rx_desc; + uint32_t amsdu_len; + uint32_t len; + uint32_t last_frag; + qdf_dma_addr_t paddr; + static uint8_t preamble_type; + static uint32_t vht_sig_a_1; + static uint32_t vht_sig_a_2; + + HTT_ASSERT1(htt_rx_in_order_ring_elems(pdev) != 0); + + rx_ind_data = qdf_nbuf_data(rx_ind_msg); + msg_word = (uint32_t *)rx_ind_data; + + *replenish_cnt = 0; + HTT_PKT_DUMP(qdf_trace_hex_dump(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO_HIGH, + (void *)rx_ind_data, + (int)qdf_nbuf_len(rx_ind_msg))); + + /* Get the total number of MSDUs */ + msdu_count = HTT_RX_IN_ORD_PADDR_IND_MSDU_CNT_GET(*(msg_word + 1)); + HTT_RX_CHECK_MSDU_COUNT(msdu_count); + + msg_word = (uint32_t *)(rx_ind_data + + HTT_RX_IN_ORD_PADDR_IND_HDR_BYTES); + paddr = htt_rx_in_ord_paddr_get(msg_word); + msdu = htt_rx_in_order_netbuf_pop(pdev, paddr); + + if (qdf_unlikely(!msdu)) { + qdf_print("netbuf pop failed!"); + *tail_msdu = NULL; + return 0; + } + *replenish_cnt = *replenish_cnt + 1; + + while (msdu_count > 0) { + msdu_count--; + /* + * Set the netbuf length to be the entire buffer length + * initially, so the unmap will unmap the entire buffer. + */ + qdf_nbuf_set_pktlen(msdu, HTT_RX_BUF_SIZE); + qdf_nbuf_unmap(pdev->osdev, msdu, QDF_DMA_FROM_DEVICE); + + /* + * cache consistency has been taken care of by the + * qdf_nbuf_unmap + */ + rx_desc = htt_rx_desc(msdu); + if ((unsigned int)(*(uint32_t *)&rx_desc->attention) & + RX_DESC_ATTN_MPDU_LEN_ERR_BIT) { + qdf_nbuf_free(msdu); + last_frag = ((struct htt_rx_in_ord_paddr_ind_msdu_t *) + msg_word)->msdu_info; + while (!last_frag) { + msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS; + paddr = htt_rx_in_ord_paddr_get(msg_word); + msdu = htt_rx_in_order_netbuf_pop(pdev, paddr); + last_frag = ((struct + htt_rx_in_ord_paddr_ind_msdu_t *) + msg_word)->msdu_info; + if (qdf_unlikely(!msdu)) { + qdf_print("netbuf pop failed!"); + return 0; + } + *replenish_cnt = *replenish_cnt + 1; + qdf_nbuf_unmap(pdev->osdev, msdu, + QDF_DMA_FROM_DEVICE); + qdf_nbuf_free(msdu); + } + msdu = prev; + goto next_pop; + } + + if (!prev) + (*head_msdu) = msdu; + prev = msdu; + + HTT_PKT_DUMP(htt_print_rx_desc(rx_desc)); + + /* + * Only the first mpdu has valid preamble type, so use it + * till the last mpdu is reached + */ + if (rx_desc->attention.first_mpdu) { + preamble_type = rx_desc->ppdu_start.preamble_type; + if (preamble_type == 8 || preamble_type == 9 || + preamble_type == 0x0c || preamble_type == 0x0d) { + vht_sig_a_1 = VHT_SIG_A_1(rx_desc); + vht_sig_a_2 = VHT_SIG_A_2(rx_desc); + } + } else { + rx_desc->ppdu_start.preamble_type = preamble_type; + if (preamble_type == 8 || preamble_type == 9 || + preamble_type == 0x0c || preamble_type == 0x0d) { + VHT_SIG_A_1(rx_desc) = vht_sig_a_1; + VHT_SIG_A_2(rx_desc) = vht_sig_a_2; + } + } + + if (rx_desc->attention.last_mpdu) { + preamble_type = 0; + vht_sig_a_1 = 0; + vht_sig_a_2 = 0; + } + + /* + * Make the netbuf's data pointer point to the payload rather + * than the descriptor. + */ + if (rx_desc->attention.first_mpdu) { + memset(&g_ppdu_rx_status, 0, + sizeof(struct mon_rx_status)); + htt_rx_mon_get_rx_status(pdev, rx_desc, + &g_ppdu_rx_status); + } + /* + * For certain platform, 350 bytes of headroom is already + * appended to accommodate radiotap header but + * qdf_nbuf_update_radiotap() API again will try to create + * a room for radiotap header. To make our design simple + * let qdf_nbuf_update_radiotap() API create a room for radiotap + * header and update it, do qdf_nbuf_pull_head() operation and + * pull 350 bytes of headroom. + * + * + * + * (SKB buffer) + * skb->head --> +-----------+ <-- skb->data + * | | (Before pulling headroom) + * | | + * | HEAD | 350 bytes of headroom + * | | + * | | + * +-----------+ <-- skb->data + * | | (After pulling headroom) + * | | + * | DATA | + * | | + * | | + * +-----------+ + * | | + * | | + * | TAIL | + * | | + * | | + * +-----------+ + * + */ + if (qdf_nbuf_head(msdu) == qdf_nbuf_data(msdu)) + qdf_nbuf_pull_head(msdu, HTT_RX_STD_DESC_RESERVATION); + qdf_nbuf_update_radiotap(&g_ppdu_rx_status, msdu, + HTT_RX_STD_DESC_RESERVATION); + amsdu_len = HTT_RX_IN_ORD_PADDR_IND_MSDU_LEN_GET(*(msg_word + + NEXT_FIELD_OFFSET_IN32)); + + /* + * MAX_RX_PAYLOAD_SZ when we have AMSDU packet. amsdu_len in + * which case is the total length of sum of all AMSDU's + */ + len = QDF_MIN(amsdu_len, MAX_RX_PAYLOAD_SZ); + amsdu_len -= len; + qdf_nbuf_trim_tail(msdu, HTT_RX_BUF_SIZE - + (RX_STD_DESC_SIZE + len)); + + HTT_PKT_DUMP(qdf_trace_hex_dump(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO_HIGH, + qdf_nbuf_data(msdu), + qdf_nbuf_len(msdu))); + last_frag = ((struct htt_rx_in_ord_paddr_ind_msdu_t *) + msg_word)->msdu_info; + + /* Handle amsdu packet */ + if (!last_frag) { + /* + * For AMSDU packet msdu->len is sum of all the msdu's + * length, msdu->data_len is sum of length's of + * remaining msdu's other than parent. + */ + if (!htt_mon_rx_handle_amsdu_packet(msdu, pdev, + &msg_word, + amsdu_len, + replenish_cnt)) { + qdf_print("failed to handle amsdu packet"); + return 0; + } + } + +next_pop: + /* check if this is the last msdu */ + if (msdu_count) { + msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS; + paddr = htt_rx_in_ord_paddr_get(msg_word); + next = htt_rx_in_order_netbuf_pop(pdev, paddr); + if (qdf_unlikely(!next)) { + qdf_print("netbuf pop failed!"); + *tail_msdu = NULL; + return 0; + } + *replenish_cnt = *replenish_cnt + 1; + if (msdu) + qdf_nbuf_set_next(msdu, next); + msdu = next; + } else { + *tail_msdu = msdu; + if (msdu) + qdf_nbuf_set_next(msdu, NULL); + } + } + + return 1; +} +#endif /* CONFIG_HL_SUPPORT */ + +#if defined(FEATURE_MONITOR_MODE_SUPPORT) +#if !defined(QCA6290_HEADERS_DEF) && !defined(QCA6390_HEADERS_DEF) && \ + !defined(QCA6490_HEADERS_DEF) && !defined(QCA6750_HEADERS_DEF) +static void +htt_rx_parse_ppdu_start_status(struct htt_host_rx_desc_base *rx_desc, + struct ieee80211_rx_status *rs) +{ + struct rx_ppdu_start *ppdu_start = &rx_desc->ppdu_start; + + /* RSSI */ + rs->rs_rssi = ppdu_start->rssi_comb; + + /* PHY rate */ + /* + * rs_ratephy coding + * [b3 - b0] + * 0 -> OFDM + * 1 -> CCK + * 2 -> HT + * 3 -> VHT + * OFDM / CCK + * [b7 - b4 ] => LSIG rate + * [b23 - b8 ] => service field + * (b'12 static/dynamic, + * b'14..b'13 BW for VHT) + * [b31 - b24 ] => Reserved + * HT / VHT + * [b15 - b4 ] => SIG A_2 12 LSBs + * [b31 - b16] => SIG A_1 16 LSBs + */ + if (ppdu_start->preamble_type == 0x4) { + rs->rs_ratephy = ppdu_start->l_sig_rate_select; + rs->rs_ratephy |= ppdu_start->l_sig_rate << 4; + rs->rs_ratephy |= ppdu_start->service << 8; + } else { + rs->rs_ratephy = (ppdu_start->preamble_type & 0x4) ? 3 : 2; +#ifdef HELIUMPLUS + rs->rs_ratephy |= + (ppdu_start->ht_sig_vht_sig_ah_sig_a_2 & 0xFFF) << 4; + rs->rs_ratephy |= + (ppdu_start->ht_sig_vht_sig_ah_sig_a_1 & 0xFFFF) << 16; +#else + rs->rs_ratephy |= (ppdu_start->ht_sig_vht_sig_a_2 & 0xFFF) << 4; + rs->rs_ratephy |= + (ppdu_start->ht_sig_vht_sig_a_1 & 0xFFFF) << 16; +#endif + } +} + +/* Util fake function that has same prototype as qdf_nbuf_clone that just + * returns the same nbuf + */ +static qdf_nbuf_t htt_rx_qdf_noclone_buf(qdf_nbuf_t buf) +{ + return buf; +} + +/* This function is used by montior mode code to restitch an MSDU list + * corresponding to an MPDU back into an MPDU by linking up the skbs. + */ +qdf_nbuf_t +htt_rx_restitch_mpdu_from_msdus(htt_pdev_handle pdev, + qdf_nbuf_t head_msdu, + struct ieee80211_rx_status *rx_status, + unsigned int clone_not_reqd) +{ + qdf_nbuf_t msdu, mpdu_buf, prev_buf, msdu_orig, head_frag_list_cloned; + unsigned int decap_format, wifi_hdr_len, sec_hdr_len, msdu_llc_len, + mpdu_buf_len, decap_hdr_pull_bytes, frag_list_sum_len, dir, + is_amsdu, is_first_frag, amsdu_pad, msdu_len; + struct htt_host_rx_desc_base *rx_desc; + char *hdr_desc; + unsigned char *dest; + struct ieee80211_frame *wh; + struct ieee80211_qoscntl *qos; + + /* The nbuf has been pulled just beyond the status and points to the + * payload + */ + msdu_orig = head_msdu; + rx_desc = htt_rx_desc(msdu_orig); + + /* Fill out the rx_status from the PPDU start and end fields */ + if (rx_desc->attention.first_mpdu) { + htt_rx_parse_ppdu_start_status(rx_desc, rx_status); + + /* The timestamp is no longer valid - It will be valid only for + * the last MPDU + */ + rx_status->rs_tstamp.tsf = ~0; + } + + decap_format = + GET_FIELD(&rx_desc->msdu_start, RX_MSDU_START_2_DECAP_FORMAT); + + head_frag_list_cloned = NULL; + + /* Easy case - The MSDU status indicates that this is a non-decapped + * packet in RAW mode. + * return + */ + if (decap_format == HW_RX_DECAP_FORMAT_RAW) { + /* Note that this path might suffer from headroom unavailabilty, + * but the RX status is usually enough + */ + if (clone_not_reqd) + mpdu_buf = htt_rx_qdf_noclone_buf(head_msdu); + else + mpdu_buf = qdf_nbuf_clone(head_msdu); + + if (!mpdu_buf) + goto mpdu_stitch_fail; + + prev_buf = mpdu_buf; + + frag_list_sum_len = 0; + is_first_frag = 1; + msdu_len = qdf_nbuf_len(mpdu_buf); + + /* Drop the zero-length msdu */ + if (!msdu_len) + goto mpdu_stitch_fail; + + msdu_orig = qdf_nbuf_next(head_msdu); + + while (msdu_orig) { + /* TODO: intra AMSDU padding - do we need it ??? */ + if (clone_not_reqd) + msdu = htt_rx_qdf_noclone_buf(msdu_orig); + else + msdu = qdf_nbuf_clone(msdu_orig); + + if (!msdu) + goto mpdu_stitch_fail; + + if (is_first_frag) { + is_first_frag = 0; + head_frag_list_cloned = msdu; + } + + msdu_len = qdf_nbuf_len(msdu); + /* Drop the zero-length msdu */ + if (!msdu_len) + goto mpdu_stitch_fail; + + frag_list_sum_len += msdu_len; + + /* Maintain the linking of the cloned MSDUS */ + qdf_nbuf_set_next_ext(prev_buf, msdu); + + /* Move to the next */ + prev_buf = msdu; + msdu_orig = qdf_nbuf_next(msdu_orig); + } + + /* The last msdu length need be larger than HTT_FCS_LEN */ + if (msdu_len < HTT_FCS_LEN) + goto mpdu_stitch_fail; + + qdf_nbuf_trim_tail(prev_buf, HTT_FCS_LEN); + + /* If there were more fragments to this RAW frame */ + if (head_frag_list_cloned) { + qdf_nbuf_append_ext_list(mpdu_buf, + head_frag_list_cloned, + frag_list_sum_len); + } + + goto mpdu_stitch_done; + } + + /* Decap mode: + * Calculate the amount of header in decapped packet to knock off based + * on the decap type and the corresponding number of raw bytes to copy + * status header + */ + + hdr_desc = &rx_desc->rx_hdr_status[0]; + + /* Base size */ + wifi_hdr_len = sizeof(struct ieee80211_frame); + wh = (struct ieee80211_frame *)hdr_desc; + + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + if (dir == IEEE80211_FC1_DIR_DSTODS) + wifi_hdr_len += 6; + + is_amsdu = 0; + if (wh->i_fc[0] & QDF_IEEE80211_FC0_SUBTYPE_QOS) { + qos = (struct ieee80211_qoscntl *) + (hdr_desc + wifi_hdr_len); + wifi_hdr_len += 2; + + is_amsdu = (qos->i_qos[0] & IEEE80211_QOS_AMSDU); + } + + /* TODO: Any security headers associated with MPDU */ + sec_hdr_len = 0; + + /* MSDU related stuff LLC - AMSDU subframe header etc */ + msdu_llc_len = is_amsdu ? (14 + 8) : 8; + + mpdu_buf_len = wifi_hdr_len + sec_hdr_len + msdu_llc_len; + + /* "Decap" header to remove from MSDU buffer */ + decap_hdr_pull_bytes = 14; + + /* Allocate a new nbuf for holding the 802.11 header retrieved from the + * status of the now decapped first msdu. Leave enough headroom for + * accomodating any radio-tap /prism like PHY header + */ +#define HTT_MAX_MONITOR_HEADER (512) + mpdu_buf = qdf_nbuf_alloc(pdev->osdev, + HTT_MAX_MONITOR_HEADER + mpdu_buf_len, + HTT_MAX_MONITOR_HEADER, 4, false); + + if (!mpdu_buf) + goto mpdu_stitch_fail; + + /* Copy the MPDU related header and enc headers into the first buffer + * - Note that there can be a 2 byte pad between heaader and enc header + */ + + prev_buf = mpdu_buf; + dest = qdf_nbuf_put_tail(prev_buf, wifi_hdr_len); + if (!dest) + goto mpdu_stitch_fail; + qdf_mem_copy(dest, hdr_desc, wifi_hdr_len); + hdr_desc += wifi_hdr_len; + + /* NOTE - This padding is present only in the RAW header status - not + * when the MSDU data payload is in RAW format. + */ + /* Skip the "IV pad" */ + if (wifi_hdr_len & 0x3) + hdr_desc += 2; + + /* The first LLC len is copied into the MPDU buffer */ + frag_list_sum_len = 0; + frag_list_sum_len -= msdu_llc_len; + + msdu_orig = head_msdu; + is_first_frag = 1; + amsdu_pad = 0; + + while (msdu_orig) { + /* TODO: intra AMSDU padding - do we need it ??? */ + if (clone_not_reqd) + msdu = htt_rx_qdf_noclone_buf(msdu_orig); + else + msdu = qdf_nbuf_clone(msdu_orig); + + if (!msdu) + goto mpdu_stitch_fail; + + if (is_first_frag) { + is_first_frag = 0; + head_frag_list_cloned = msdu; + } else { + /* Maintain the linking of the cloned MSDUS */ + qdf_nbuf_set_next_ext(prev_buf, msdu); + + /* Reload the hdr ptr only on non-first MSDUs */ + rx_desc = htt_rx_desc(msdu_orig); + hdr_desc = &rx_desc->rx_hdr_status[0]; + } + + /* Copy this buffers MSDU related status into the prev buffer */ + dest = qdf_nbuf_put_tail(prev_buf, msdu_llc_len + amsdu_pad); + dest += amsdu_pad; + qdf_mem_copy(dest, hdr_desc, msdu_llc_len); + + /* Push the MSDU buffer beyond the decap header */ + qdf_nbuf_pull_head(msdu, decap_hdr_pull_bytes); + frag_list_sum_len += + msdu_llc_len + qdf_nbuf_len(msdu) + amsdu_pad; + + /* + * Set up intra-AMSDU pad to be added to start of next buffer - + * AMSDU pad is 4 byte pad on AMSDU subframe + */ + amsdu_pad = (msdu_llc_len + qdf_nbuf_len(msdu)) & 0x3; + amsdu_pad = amsdu_pad ? (4 - amsdu_pad) : 0; + + /* + * TODO FIXME How do we handle MSDUs that have fraglist - Should + * probably iterate all the frags cloning them along the way and + * and also updating the prev_buf pointer + */ + + /* Move to the next */ + prev_buf = msdu; + msdu_orig = qdf_nbuf_next(msdu_orig); + } + + /* TODO: Convert this to suitable qdf routines */ + qdf_nbuf_append_ext_list(mpdu_buf, head_frag_list_cloned, + frag_list_sum_len); + +mpdu_stitch_done: + /* Check if this buffer contains the PPDU end status for TSF */ + if (rx_desc->attention.last_mpdu) +#ifdef HELIUMPLUS + rx_status->rs_tstamp.tsf = + rx_desc->ppdu_end.rx_pkt_end.phy_timestamp_1_lower_32; +#else + rx_status->rs_tstamp.tsf = rx_desc->ppdu_end.tsf_timestamp; +#endif + /* All the nbufs have been linked into the ext list + * and then unlink the nbuf list + */ + if (clone_not_reqd) { + msdu = head_msdu; + while (msdu) { + msdu_orig = msdu; + msdu = qdf_nbuf_next(msdu); + qdf_nbuf_set_next(msdu_orig, NULL); + } + } + + return mpdu_buf; + +mpdu_stitch_fail: + /* Free these alloced buffers and the orig buffers in non-clone case */ + if (!clone_not_reqd) { + /* Free the head buffer */ + if (mpdu_buf) + qdf_nbuf_free(mpdu_buf); + + /* Free the partial list */ + while (head_frag_list_cloned) { + msdu = head_frag_list_cloned; + head_frag_list_cloned = + qdf_nbuf_next_ext(head_frag_list_cloned); + qdf_nbuf_free(msdu); + } + } else { + /* Free the alloced head buffer */ + if (decap_format != HW_RX_DECAP_FORMAT_RAW) + if (mpdu_buf) + qdf_nbuf_free(mpdu_buf); + + /* Free the orig buffers */ + msdu = head_msdu; + while (msdu) { + msdu_orig = msdu; + msdu = qdf_nbuf_next(msdu); + qdf_nbuf_free(msdu_orig); + } + } + + return NULL; +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_rx.c b/drivers/staging/qcacld-3.0/core/dp/htt/htt_rx.c new file mode 100644 index 0000000000000000000000000000000000000000..81ab225a020ce9636c16bcfb5f060480a335e81d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_rx.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file htt_rx.c + * @brief Implement receive aspects of HTT. + * @details + * This file contains three categories of HTT rx code: + * 1. An abstraction of the rx descriptor, to hide the + * differences between the HL vs. LL rx descriptor. + * 2. Functions for providing access to the (series of) + * rx descriptor(s) and rx frame(s) associated with + * an rx indication message. + * 3. Functions for setting up and using the MAC DMA + * rx ring (applies to LL only). + */ + +#include /* qdf_mem_malloc,free, etc. */ +#include /* qdf_print, bool */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_timer_free */ + +#include /* HTT_HL_RX_DESC_SIZE */ +#include +#include +#include +#include /* HTT_ASSERT, htt_pdev_t, HTT_RX_BUF_SIZE */ +#include "regtable.h" + +#include /* ieee80211_frame, ieee80211_qoscntl */ +#include +#include +#include "ol_txrx_types.h" +#ifdef DEBUG_DMA_DONE +#include +#include +#endif +#include + +/** + * htt_rx_mpdu_wifi_hdr_retrieve() - retrieve 802.11 header + * @pdev - pdev handle + * @mpdu_desc - mpdu descriptor + * + * Return : pointer to 802.11 header + */ +char *htt_rx_mpdu_wifi_hdr_retrieve(htt_pdev_handle pdev, void *mpdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)mpdu_desc; + + if (!rx_desc) + return NULL; + else + return rx_desc->rx_hdr_status; +} + +/** + * htt_rx_mpdu_desc_tsf32() - Return the TSF timestamp indicating when + * a MPDU was received. + * @pdev - the HTT instance the rx data was received on + * @mpdu_desc - the abstract descriptor for the MPDU in question + * + * return : 32 LSBs of TSF time at which the MPDU's PPDU was received + */ +uint32_t htt_rx_mpdu_desc_tsf32(htt_pdev_handle pdev, void *mpdu_desc) +{ + return 0; +} + +static inline +uint8_t htt_rx_msdu_fw_desc_get(htt_pdev_handle pdev, void *msdu_desc) +{ + /* + * HL and LL use the same format for FW rx desc, but have the FW rx desc + * in different locations. + * In LL, the FW rx descriptor has been copied into the same + * htt_host_rx_desc_base struct that holds the HW rx desc. + * In HL, the FW rx descriptor, along with the MSDU payload, + * is in the same buffer as the rx indication message. + * + * Use the FW rx desc offset configured during startup to account for + * this difference between HL vs. LL. + * + * An optimization would be to define the LL and HL msdu_desc pointer + * in such a way that they both use the same offset to the FW rx desc. + * Then the following functions could be converted to macros, without + * needing to expose the htt_pdev_t definition outside HTT. + */ + return *(((uint8_t *)msdu_desc) + pdev->rx_fw_desc_offset); +} + +int htt_rx_msdu_discard(htt_pdev_handle pdev, void *msdu_desc) +{ + return htt_rx_msdu_fw_desc_get(pdev, msdu_desc) & FW_RX_DESC_DISCARD_M; +} + +int htt_rx_msdu_forward(htt_pdev_handle pdev, void *msdu_desc) +{ + return htt_rx_msdu_fw_desc_get(pdev, msdu_desc) & FW_RX_DESC_FORWARD_M; +} + +int htt_rx_msdu_inspect(htt_pdev_handle pdev, void *msdu_desc) +{ + return htt_rx_msdu_fw_desc_get(pdev, msdu_desc) & FW_RX_DESC_INSPECT_M; +} + +void +htt_rx_msdu_actions(htt_pdev_handle pdev, + void *msdu_desc, int *discard, int *forward, int *inspect) +{ + uint8_t rx_msdu_fw_desc = htt_rx_msdu_fw_desc_get(pdev, msdu_desc); +#ifdef HTT_DEBUG_DATA + HTT_PRINT("act:0x%x ", rx_msdu_fw_desc); +#endif + *discard = rx_msdu_fw_desc & FW_RX_DESC_DISCARD_M; + *forward = rx_msdu_fw_desc & FW_RX_DESC_FORWARD_M; + *inspect = rx_msdu_fw_desc & FW_RX_DESC_INSPECT_M; +} + +uint32_t htt_rx_amsdu_rx_in_order_get_pktlog(qdf_nbuf_t rx_ind_msg) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *)qdf_nbuf_data(rx_ind_msg); + return HTT_RX_IN_ORD_PADDR_IND_PKTLOG_GET(*msg_word); +} + +int16_t htt_rx_mpdu_desc_rssi_dbm(htt_pdev_handle pdev, void *mpdu_desc) +{ + /* + * Currently the RSSI is provided only as a field in the + * HTT_T2H_RX_IND message, rather than in each rx descriptor. + */ + return HTT_RSSI_INVALID; +} + +/* + * htt_rx_amsdu_pop - + * global function pointer that is programmed during attach to point + * to either htt_rx_amsdu_pop_ll or htt_rx_amsdu_rx_in_order_pop_ll. + */ +int (*htt_rx_amsdu_pop)(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, qdf_nbuf_t *tail_msdu, + uint32_t *msdu_count); + +/* + * htt_rx_frag_pop - + * global function pointer that is programmed during attach to point + * to either htt_rx_amsdu_pop_ll + */ +int (*htt_rx_frag_pop)(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, qdf_nbuf_t *tail_msdu, + uint32_t *msdu_count); + +int (*htt_rx_offload_msdu_cnt)(htt_pdev_handle pdev); + +int +(*htt_rx_offload_msdu_pop)(htt_pdev_handle pdev, + qdf_nbuf_t offload_deliver_msg, + int *vdev_id, + int *peer_id, + int *tid, + uint8_t *fw_desc, + qdf_nbuf_t *head_buf, qdf_nbuf_t *tail_buf); + +void * (*htt_rx_mpdu_desc_list_next)(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg); + +bool (*htt_rx_mpdu_desc_retry)(htt_pdev_handle pdev, void *mpdu_desc); + +uint16_t (*htt_rx_mpdu_desc_seq_num)(htt_pdev_handle pdev, void *mpdu_desc); + +void (*htt_rx_mpdu_desc_pn)(htt_pdev_handle pdev, + void *mpdu_desc, + union htt_rx_pn_t *pn, int pn_len_bits); + +uint8_t (*htt_rx_mpdu_desc_tid)(htt_pdev_handle pdev, void *mpdu_desc); + +bool (*htt_rx_msdu_desc_completes_mpdu)(htt_pdev_handle pdev, void *msdu_desc); + +bool (*htt_rx_msdu_first_msdu_flag)(htt_pdev_handle pdev, void *msdu_desc); + +int (*htt_rx_msdu_has_wlan_mcast_flag)(htt_pdev_handle pdev, void *msdu_desc); + +bool (*htt_rx_msdu_is_wlan_mcast)(htt_pdev_handle pdev, void *msdu_desc); + +int (*htt_rx_msdu_is_frag)(htt_pdev_handle pdev, void *msdu_desc); + +void * (*htt_rx_msdu_desc_retrieve)(htt_pdev_handle pdev, qdf_nbuf_t msdu); + +bool (*htt_rx_mpdu_is_encrypted)(htt_pdev_handle pdev, void *mpdu_desc); + +bool (*htt_rx_msdu_desc_key_id)(htt_pdev_handle pdev, + void *mpdu_desc, uint8_t *key_id); + +bool (*htt_rx_msdu_chan_info_present)( + htt_pdev_handle pdev, + void *mpdu_desc); + +bool (*htt_rx_msdu_center_freq)( + htt_pdev_handle pdev, + struct ol_txrx_peer_t *peer, + void *mpdu_desc, + uint16_t *primary_chan_center_freq_mhz, + uint16_t *contig_chan1_center_freq_mhz, + uint16_t *contig_chan2_center_freq_mhz, + uint8_t *phy_mode); + +void htt_rx_desc_frame_free(htt_pdev_handle htt_pdev, qdf_nbuf_t msdu) +{ + qdf_nbuf_free(msdu); +} + +void htt_rx_msdu_desc_free(htt_pdev_handle htt_pdev, qdf_nbuf_t msdu) +{ + /* + * The rx descriptor is in the same buffer as the rx MSDU payload, + * and does not need to be freed separately. + */ +} + +void htt_rx_msdu_buff_replenish(htt_pdev_handle pdev) +{ + if (qdf_atomic_dec_and_test(&pdev->rx_ring.refill_ref_cnt)) + htt_rx_fill_ring_count(pdev); + + qdf_atomic_inc(&pdev->rx_ring.refill_ref_cnt); +} + +#ifdef IPA_OFFLOAD +#ifdef QCA_WIFI_3_0 +/** + * htt_rx_ipa_uc_alloc_wdi2_rsc() - Allocate WDI2.0 resources + * @pdev: htt context + * @rx_ind_ring_elements: rx ring elements + * + * Return: 0 success + */ +static int htt_rx_ipa_uc_alloc_wdi2_rsc(struct htt_pdev_t *pdev, + unsigned int rx_ind_ring_elements) +{ + /* + * Allocate RX2 indication ring + * RX2 IND ring element + * 4bytes: pointer + * 2bytes: VDEV ID + * 2bytes: length + * + * RX indication ring size, by bytes + */ + pdev->ipa_uc_rx_rsc.rx2_ind_ring = + qdf_mem_shared_mem_alloc(pdev->osdev, + rx_ind_ring_elements * + sizeof(target_paddr_t)); + if (!pdev->ipa_uc_rx_rsc.rx2_ind_ring) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s: Unable to allocate memory for IPA rx2 ind ring", + __func__); + return 1; + } + + pdev->ipa_uc_rx_rsc.rx2_ipa_prc_done_idx = + qdf_mem_shared_mem_alloc(pdev->osdev, 4); + if (!pdev->ipa_uc_rx_rsc.rx2_ipa_prc_done_idx) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s: Unable to allocate memory for IPA rx proc done index", + __func__); + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_rx_rsc.rx2_ind_ring); + return 1; + } + + return 0; +} + +/** + * htt_rx_ipa_uc_free_wdi2_rsc() - Free WDI2.0 resources + * @pdev: htt context + * + * Return: None + */ +static void htt_rx_ipa_uc_free_wdi2_rsc(struct htt_pdev_t *pdev) +{ + qdf_mem_shared_mem_free(pdev->osdev, pdev->ipa_uc_rx_rsc.rx2_ind_ring); + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_rx_rsc.rx2_ipa_prc_done_idx); +} +#else +static int htt_rx_ipa_uc_alloc_wdi2_rsc(struct htt_pdev_t *pdev, + unsigned int rx_ind_ring_elements) +{ + return 0; +} + +static void htt_rx_ipa_uc_free_wdi2_rsc(struct htt_pdev_t *pdev) +{ +} +#endif + +/** + * htt_rx_ipa_uc_attach() - attach htt ipa uc rx resource + * @pdev: htt context + * @rx_ind_ring_size: rx ring size + * + * Return: 0 success + */ +int htt_rx_ipa_uc_attach(struct htt_pdev_t *pdev, + unsigned int rx_ind_ring_elements) +{ + int ret = 0; + + /* + * Allocate RX indication ring + * RX IND ring element + * 4bytes: pointer + * 2bytes: VDEV ID + * 2bytes: length + */ + pdev->ipa_uc_rx_rsc.rx_ind_ring = + qdf_mem_shared_mem_alloc(pdev->osdev, + rx_ind_ring_elements * + sizeof(struct ipa_uc_rx_ring_elem_t)); + if (!pdev->ipa_uc_rx_rsc.rx_ind_ring) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s: Unable to allocate memory for IPA rx ind ring", + __func__); + return 1; + } + + pdev->ipa_uc_rx_rsc.rx_ipa_prc_done_idx = + qdf_mem_shared_mem_alloc(pdev->osdev, 4); + if (!pdev->ipa_uc_rx_rsc.rx_ipa_prc_done_idx) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s: Unable to allocate memory for IPA rx proc done index", + __func__); + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_rx_rsc.rx_ind_ring); + return 1; + } + + ret = htt_rx_ipa_uc_alloc_wdi2_rsc(pdev, rx_ind_ring_elements); + if (ret) { + qdf_mem_shared_mem_free(pdev->osdev, pdev->ipa_uc_rx_rsc.rx_ind_ring); + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_rx_rsc.rx_ipa_prc_done_idx); + } + return ret; +} + +int htt_rx_ipa_uc_detach(struct htt_pdev_t *pdev) +{ + qdf_mem_shared_mem_free(pdev->osdev, pdev->ipa_uc_rx_rsc.rx_ind_ring); + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_rx_rsc.rx_ipa_prc_done_idx); + + htt_rx_ipa_uc_free_wdi2_rsc(pdev); + return 0; +} +#endif /* IPA_OFFLOAD */ + +#ifndef REMOVE_PKT_LOG +/** + * htt_register_rx_pkt_dump_callback() - registers callback to + * get rx pkt status and call callback to do rx packet dump + * + * @pdev: htt pdev handle + * @callback: callback to get rx pkt status and + * call callback to do rx packet dump + * + * This function is used to register the callback to get + * rx pkt status and call callback to do rx packet dump + * + * Return: None + * + */ +void htt_register_rx_pkt_dump_callback(struct htt_pdev_t *pdev, + tp_rx_pkt_dump_cb callback) +{ + if (!pdev) { + qdf_print("pdev is NULL"); + return; + } + pdev->rx_pkt_dump_cb = callback; +} + +/** + * htt_deregister_rx_pkt_dump_callback() - deregisters callback to + * get rx pkt status and call callback to do rx packet dump + * + * @pdev: htt pdev handle + * + * This function is used to deregister the callback to get + * rx pkt status and call callback to do rx packet dump + * + * Return: None + * + */ +void htt_deregister_rx_pkt_dump_callback(struct htt_pdev_t *pdev) +{ + if (!pdev) { + qdf_print("pdev is NULL"); + return; + } + pdev->rx_pkt_dump_cb = NULL; +} +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS +void htt_rx_enable_ppdu_end(int *enable_ppdu_end) +{ + *enable_ppdu_end = 1; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_rx_hl.c b/drivers/staging/qcacld-3.0/core/dp/htt/htt_rx_hl.c new file mode 100644 index 0000000000000000000000000000000000000000..fbd80bf1c2c025354629a8642306d788a87385ad --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_rx_hl.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_mem_malloc,free, etc. */ +#include /* qdf_print, bool */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_timer_free */ + +#include /* HTT_HL_RX_DESC_SIZE */ +#include +#include +#include +#include /* HTT_ASSERT, htt_pdev_t, HTT_RX_BUF_SIZE */ +#include "regtable.h" + +#include /* ieee80211_frame, ieee80211_qoscntl */ +#include +#include +#include "ol_txrx_types.h" + +/* + * This function is used both below within this file (which the compiler + * will hopefully inline), and out-line from other files via the + * htt_rx_msdu_first_msdu_flag function pointer. + */ +static inline bool +htt_rx_msdu_first_msdu_flag_hl(htt_pdev_handle pdev, void *msdu_desc) +{ + return ((u_int8_t *)msdu_desc - sizeof(struct hl_htt_rx_ind_base)) + [HTT_ENDIAN_BYTE_IDX_SWAP(HTT_RX_IND_HL_FLAG_OFFSET)] & + HTT_RX_IND_HL_FLAG_FIRST_MSDU ? true : false; +} + +u_int16_t +htt_rx_msdu_rx_desc_size_hl( + htt_pdev_handle pdev, + void *msdu_desc + ) +{ + return ((u_int8_t *)(msdu_desc) - HTT_RX_IND_HL_BYTES) + [HTT_ENDIAN_BYTE_IDX_SWAP(HTT_RX_IND_HL_RX_DESC_LEN_OFFSET)]; +} + +#ifdef CHECKSUM_OFFLOAD +static void +htt_set_checksum_result_hl(qdf_nbuf_t msdu, + struct htt_host_rx_desc_base *rx_desc) +{ + u_int8_t flag = ((u_int8_t *)rx_desc - + sizeof(struct hl_htt_rx_ind_base))[ + HTT_ENDIAN_BYTE_IDX_SWAP( + HTT_RX_IND_HL_FLAG_OFFSET)]; + + int is_ipv6 = flag & HTT_RX_IND_HL_FLAG_IPV6 ? 1 : 0; + int is_tcp = flag & HTT_RX_IND_HL_FLAG_TCP ? 1 : 0; + int is_udp = flag & HTT_RX_IND_HL_FLAG_UDP ? 1 : 0; + + qdf_nbuf_rx_cksum_t cksum = { + QDF_NBUF_RX_CKSUM_NONE, + QDF_NBUF_RX_CKSUM_NONE, + 0 + }; + + switch ((is_udp << 2) | (is_tcp << 1) | (is_ipv6 << 0)) { + case 0x4: + cksum.l4_type = QDF_NBUF_RX_CKSUM_UDP; + break; + case 0x2: + cksum.l4_type = QDF_NBUF_RX_CKSUM_TCP; + break; + case 0x5: + cksum.l4_type = QDF_NBUF_RX_CKSUM_UDPIPV6; + break; + case 0x3: + cksum.l4_type = QDF_NBUF_RX_CKSUM_TCPIPV6; + break; + default: + cksum.l4_type = QDF_NBUF_RX_CKSUM_NONE; + break; + } + if (cksum.l4_type != (qdf_nbuf_l4_rx_cksum_type_t) + QDF_NBUF_RX_CKSUM_NONE) { + cksum.l4_result = flag & HTT_RX_IND_HL_FLAG_C4_FAILED ? + QDF_NBUF_RX_CKSUM_NONE : + QDF_NBUF_RX_CKSUM_TCP_UDP_UNNECESSARY; + } + qdf_nbuf_set_rx_cksum(msdu, &cksum); +} +#else +static inline +void htt_set_checksum_result_hl(qdf_nbuf_t msdu, + struct htt_host_rx_desc_base *rx_desc) +{ +} +#endif + +/** + * htt_rx_fill_ring_count() - replenish rx msdu buffer + * @pdev: Handle (pointer) to HTT pdev. + * + * This funciton will replenish the rx buffer to the max number + * that can be kept in the ring + * + * Return: None + */ +void htt_rx_fill_ring_count(htt_pdev_handle pdev) +{ +} + +/** + * htt_rx_mpdu_desc_list_next_hl() - provides an abstract way to obtain + * the next MPDU descriptor + * @pdev: the HTT instance the rx data was received on + * @rx_ind_msg: the netbuf containing the rx indication message + * + * for HL, the returned value is not mpdu_desc, + * it's translated hl_rx_desc just after the hl_ind_msg + * for HL AMSDU, we can't point to payload now, because + * hl rx desc is not fixed, we can't retrieve the desc + * by minus rx_desc_size when release. keep point to hl rx desc + * now + * + * Return: next abstract rx descriptor from the series of MPDUs + * referenced by an rx ind msg + */ +static inline void * +htt_rx_mpdu_desc_list_next_hl(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg) +{ + void *mpdu_desc = (void *)qdf_nbuf_data(rx_ind_msg); + return mpdu_desc; +} + +/** + * htt_rx_msdu_desc_retrieve_hl() - Retrieve a previously-stored rx descriptor + * from a MSDU buffer + * @pdev: the HTT instance the rx data was received on + * @msdu - the buffer containing the MSDU payload + * + * currently for HL AMSDU, we don't point to payload. + * we shift to payload in ol_rx_deliver later + * + * Return: the corresponding abstract rx MSDU descriptor + */ +static inline void * +htt_rx_msdu_desc_retrieve_hl(htt_pdev_handle pdev, qdf_nbuf_t msdu) +{ + return qdf_nbuf_data(msdu); +} + +static +bool htt_rx_mpdu_is_encrypted_hl(htt_pdev_handle pdev, void *mpdu_desc) +{ + if (htt_rx_msdu_first_msdu_flag_hl(pdev, mpdu_desc) == true) { + /* Fix Me: only for little endian */ + struct hl_htt_rx_desc_base *rx_desc = + (struct hl_htt_rx_desc_base *)mpdu_desc; + + return HTT_WORD_GET(*(u_int32_t *)rx_desc, + HTT_HL_RX_DESC_MPDU_ENC); + } else { + /* not first msdu, no encrypt info for hl */ + qdf_print( + "Error: get encrypted from a not-first msdu.\n"); + qdf_assert(0); + return false; + } +} + +static inline bool +htt_rx_msdu_chan_info_present_hl(htt_pdev_handle pdev, void *mpdu_desc) +{ + if (htt_rx_msdu_first_msdu_flag_hl(pdev, mpdu_desc) == true && + HTT_WORD_GET(*(u_int32_t *)mpdu_desc, + HTT_HL_RX_DESC_CHAN_INFO_PRESENT)) + return true; + + return false; +} + +static bool +htt_rx_msdu_center_freq_hl(htt_pdev_handle pdev, + struct ol_txrx_peer_t *peer, + void *mpdu_desc, + uint16_t *primary_chan_center_freq_mhz, + uint16_t *contig_chan1_center_freq_mhz, + uint16_t *contig_chan2_center_freq_mhz, + uint8_t *phy_mode) +{ + int pn_len, index; + uint32_t *chan_info; + + index = htt_rx_msdu_is_wlan_mcast(pdev, mpdu_desc) ? + txrx_sec_mcast : txrx_sec_ucast; + + pn_len = (peer ? + pdev->txrx_pdev->rx_pn[peer->security[index].sec_type]. + len : 0); + chan_info = (uint32_t *)((uint8_t *)mpdu_desc + + HTT_HL_RX_DESC_PN_OFFSET + pn_len); + + if (htt_rx_msdu_chan_info_present_hl(pdev, mpdu_desc)) { + if (primary_chan_center_freq_mhz) + *primary_chan_center_freq_mhz = + HTT_WORD_GET( + *chan_info, + HTT_CHAN_INFO_PRIMARY_CHAN_CENTER_FREQ); + if (contig_chan1_center_freq_mhz) + *contig_chan1_center_freq_mhz = + HTT_WORD_GET( + *chan_info, + HTT_CHAN_INFO_CONTIG_CHAN1_CENTER_FREQ); + chan_info++; + if (contig_chan2_center_freq_mhz) + *contig_chan2_center_freq_mhz = + HTT_WORD_GET( + *chan_info, + HTT_CHAN_INFO_CONTIG_CHAN2_CENTER_FREQ); + if (phy_mode) + *phy_mode = + HTT_WORD_GET(*chan_info, + HTT_CHAN_INFO_PHY_MODE); + return true; + } + + if (primary_chan_center_freq_mhz) + *primary_chan_center_freq_mhz = 0; + if (contig_chan1_center_freq_mhz) + *contig_chan1_center_freq_mhz = 0; + if (contig_chan2_center_freq_mhz) + *contig_chan2_center_freq_mhz = 0; + if (phy_mode) + *phy_mode = 0; + return false; +} + +static bool +htt_rx_msdu_desc_key_id_hl(htt_pdev_handle htt_pdev, + void *mpdu_desc, u_int8_t *key_id) +{ + if (htt_rx_msdu_first_msdu_flag_hl(htt_pdev, mpdu_desc) == true) { + /* Fix Me: only for little endian */ + struct hl_htt_rx_desc_base *rx_desc = + (struct hl_htt_rx_desc_base *)mpdu_desc; + + *key_id = rx_desc->key_id_oct; + return true; + } + + return false; +} + +/** + * htt_rx_mpdu_desc_retry_hl() - Returns the retry bit from the Rx descriptor + * for the High Latency driver + * @pdev: Handle (pointer) to HTT pdev. + * @mpdu_desc: Void pointer to the Rx descriptor for MPDU + * before the beginning of the payload. + * + * This function returns the retry bit of the 802.11 header for the + * provided rx MPDU descriptor. For the high latency driver, this function + * pretends as if the retry bit is never set so that the mcast duplicate + * detection never fails. + * + * Return: boolean -- false always for HL + */ +static inline bool +htt_rx_mpdu_desc_retry_hl(htt_pdev_handle pdev, void *mpdu_desc) +{ + return false; +} + +static int +htt_rx_amsdu_pop_hl( + htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, + qdf_nbuf_t *tail_msdu, + uint32_t *msdu_count) +{ + pdev->rx_desc_size_hl = + (qdf_nbuf_data(rx_ind_msg)) + [HTT_ENDIAN_BYTE_IDX_SWAP( + HTT_RX_IND_HL_RX_DESC_LEN_OFFSET)]; + + /* point to the rx desc */ + qdf_nbuf_pull_head(rx_ind_msg, + sizeof(struct hl_htt_rx_ind_base)); + *head_msdu = *tail_msdu = rx_ind_msg; + + htt_set_checksum_result_hl(rx_ind_msg, + (struct htt_host_rx_desc_base *) + (qdf_nbuf_data(rx_ind_msg))); + + qdf_nbuf_set_next(*tail_msdu, NULL); + return 0; +} + +static int +htt_rx_frag_pop_hl( + htt_pdev_handle pdev, + qdf_nbuf_t frag_msg, + qdf_nbuf_t *head_msdu, + qdf_nbuf_t *tail_msdu, + uint32_t *msdu_count) +{ + qdf_nbuf_pull_head(frag_msg, HTT_RX_FRAG_IND_BYTES); + pdev->rx_desc_size_hl = + (qdf_nbuf_data(frag_msg)) + [HTT_ENDIAN_BYTE_IDX_SWAP( + HTT_RX_IND_HL_RX_DESC_LEN_OFFSET)]; + + /* point to the rx desc */ + qdf_nbuf_pull_head(frag_msg, + sizeof(struct hl_htt_rx_ind_base)); + *head_msdu = *tail_msdu = frag_msg; + + qdf_nbuf_set_next(*tail_msdu, NULL); + return 1; +} + +static inline int +htt_rx_offload_msdu_cnt_hl(htt_pdev_handle pdev) +{ + return 1; +} + +static inline int +htt_rx_offload_msdu_pop_hl(htt_pdev_handle pdev, + qdf_nbuf_t offload_deliver_msg, + int *vdev_id, + int *peer_id, + int *tid, + u_int8_t *fw_desc, + qdf_nbuf_t *head_buf, + qdf_nbuf_t *tail_buf) +{ + qdf_nbuf_t buf; + u_int32_t *msdu_hdr, msdu_len; + int ret = 0; + + *head_buf = *tail_buf = buf = offload_deliver_msg; + msdu_hdr = (u_int32_t *)qdf_nbuf_data(buf); + /* First dword */ + + /* Second dword */ + msdu_hdr++; + msdu_len = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_LEN_GET(*msdu_hdr); + *peer_id = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_PEER_ID_GET(*msdu_hdr); + + /* Third dword */ + msdu_hdr++; + *vdev_id = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_VDEV_ID_GET(*msdu_hdr); + *tid = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_TID_GET(*msdu_hdr); + *fw_desc = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_DESC_GET(*msdu_hdr); + + qdf_nbuf_pull_head(buf, HTT_RX_OFFLOAD_DELIVER_IND_MSDU_HDR_BYTES + + HTT_RX_OFFLOAD_DELIVER_IND_HDR_BYTES); + + if (msdu_len <= qdf_nbuf_len(buf)) { + qdf_nbuf_set_pktlen(buf, msdu_len); + } else { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "%s: drop frame with invalid msdu len %d %d", + __func__, msdu_len, (int)qdf_nbuf_len(buf)); + qdf_nbuf_free(offload_deliver_msg); + ret = -1; + } + + return ret; +} + +static uint16_t +htt_rx_mpdu_desc_seq_num_hl(htt_pdev_handle pdev, void *mpdu_desc) +{ + if (pdev->rx_desc_size_hl) { + return pdev->cur_seq_num_hl = + (u_int16_t)(HTT_WORD_GET(*(u_int32_t *)mpdu_desc, + HTT_HL_RX_DESC_MPDU_SEQ_NUM)); + } else { + return (u_int16_t)(pdev->cur_seq_num_hl); + } +} + +static void +htt_rx_mpdu_desc_pn_hl( + htt_pdev_handle pdev, + void *mpdu_desc, + union htt_rx_pn_t *pn, + int pn_len_bits) +{ + if (htt_rx_msdu_first_msdu_flag_hl(pdev, mpdu_desc) == true) { + /* Fix Me: only for little endian */ + struct hl_htt_rx_desc_base *rx_desc = + (struct hl_htt_rx_desc_base *)mpdu_desc; + u_int32_t *word_ptr = (u_int32_t *)pn->pn128; + + /* TODO: for Host of big endian */ + switch (pn_len_bits) { + case 128: + /* bits 128:64 */ + *(word_ptr + 3) = rx_desc->pn_127_96; + /* bits 63:0 */ + *(word_ptr + 2) = rx_desc->pn_95_64; + case 48: + /* bits 48:0 + * copy 64 bits + */ + *(word_ptr + 1) = rx_desc->u0.pn_63_32; + case 24: + /* bits 23:0 + * copy 32 bits + */ + *(word_ptr + 0) = rx_desc->pn_31_0; + break; + default: + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "Error: invalid length spec (%d bits) for PN", + pn_len_bits); + qdf_assert(0); + break; + }; + } else { + /* not first msdu, no pn info */ + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "Error: get pn from a not-first msdu."); + qdf_assert(0); + } +} + +/** + * htt_rx_mpdu_desc_tid_hl() - Returns the TID value from the Rx descriptor + * for High Latency driver + * @pdev: Handle (pointer) to HTT pdev. + * @mpdu_desc: Void pointer to the Rx descriptor for the MPDU + * before the beginning of the payload. + * + * This function returns the TID set in the 802.11 QoS Control for the MPDU + * in the packet header, by looking at the mpdu_start of the Rx descriptor. + * Rx descriptor gets a copy of the TID from the MAC. + * For the HL driver, this is currently uimplemented and always returns + * an invalid tid. It is the responsibility of the caller to make + * sure that return value is checked for valid range. + * + * Return: Invalid TID value (0xff) for HL driver. + */ +static inline uint8_t +htt_rx_mpdu_desc_tid_hl(htt_pdev_handle pdev, void *mpdu_desc) +{ + return 0xff; /* Invalid TID */ +} + +static inline bool +htt_rx_msdu_desc_completes_mpdu_hl(htt_pdev_handle pdev, void *msdu_desc) +{ + return ( + ((u_int8_t *)(msdu_desc) - sizeof(struct hl_htt_rx_ind_base)) + [HTT_ENDIAN_BYTE_IDX_SWAP(HTT_RX_IND_HL_FLAG_OFFSET)] + & HTT_RX_IND_HL_FLAG_LAST_MSDU) + ? true : false; +} + +static inline int +htt_rx_msdu_has_wlan_mcast_flag_hl(htt_pdev_handle pdev, void *msdu_desc) +{ + /* currently, only first msdu has hl rx_desc */ + return htt_rx_msdu_first_msdu_flag_hl(pdev, msdu_desc) == true; +} + +static inline bool +htt_rx_msdu_is_wlan_mcast_hl(htt_pdev_handle pdev, void *msdu_desc) +{ + struct hl_htt_rx_desc_base *rx_desc = + (struct hl_htt_rx_desc_base *)msdu_desc; + + return + HTT_WORD_GET(*(u_int32_t *)rx_desc, HTT_HL_RX_DESC_MCAST_BCAST); +} + +static inline int +htt_rx_msdu_is_frag_hl(htt_pdev_handle pdev, void *msdu_desc) +{ + struct hl_htt_rx_desc_base *rx_desc = + (struct hl_htt_rx_desc_base *)msdu_desc; + + return + HTT_WORD_GET(*(u_int32_t *)rx_desc, HTT_HL_RX_DESC_MCAST_BCAST); +} + +int htt_rx_attach(struct htt_pdev_t *pdev) +{ + pdev->rx_ring.size = HTT_RX_RING_SIZE_MIN; + HTT_ASSERT2(IS_PWR2(pdev->rx_ring.size)); + pdev->rx_ring.size_mask = pdev->rx_ring.size - 1; + /* host can force ring base address if it wish to do so */ + pdev->rx_ring.base_paddr = 0; + htt_rx_amsdu_pop = htt_rx_amsdu_pop_hl; + htt_rx_frag_pop = htt_rx_frag_pop_hl; + htt_rx_offload_msdu_cnt = htt_rx_offload_msdu_cnt_hl; + htt_rx_offload_msdu_pop = htt_rx_offload_msdu_pop_hl; + htt_rx_mpdu_desc_list_next = htt_rx_mpdu_desc_list_next_hl; + htt_rx_mpdu_desc_retry = htt_rx_mpdu_desc_retry_hl; + htt_rx_mpdu_desc_seq_num = htt_rx_mpdu_desc_seq_num_hl; + htt_rx_mpdu_desc_pn = htt_rx_mpdu_desc_pn_hl; + htt_rx_mpdu_desc_tid = htt_rx_mpdu_desc_tid_hl; + htt_rx_msdu_desc_completes_mpdu = htt_rx_msdu_desc_completes_mpdu_hl; + htt_rx_msdu_first_msdu_flag = htt_rx_msdu_first_msdu_flag_hl; + htt_rx_msdu_has_wlan_mcast_flag = htt_rx_msdu_has_wlan_mcast_flag_hl; + htt_rx_msdu_is_wlan_mcast = htt_rx_msdu_is_wlan_mcast_hl; + htt_rx_msdu_is_frag = htt_rx_msdu_is_frag_hl; + htt_rx_msdu_desc_retrieve = htt_rx_msdu_desc_retrieve_hl; + htt_rx_mpdu_is_encrypted = htt_rx_mpdu_is_encrypted_hl; + htt_rx_msdu_desc_key_id = htt_rx_msdu_desc_key_id_hl; + htt_rx_msdu_chan_info_present = htt_rx_msdu_chan_info_present_hl; + htt_rx_msdu_center_freq = htt_rx_msdu_center_freq_hl; + + /* + * HL case, the rx descriptor can be different sizes for + * different sub-types of RX_IND messages, e.g. for the + * initial vs. interior vs. final MSDUs within a PPDU. + * The size of each RX_IND message's rx desc is read from + * a field within the RX_IND message itself. + * In the meantime, until the rx_desc_size_hl variable is + * set to its real value based on the RX_IND message, + * initialize it to a reasonable value (zero). + */ + pdev->rx_desc_size_hl = 0; + return 0; /* success */ +} diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_rx_ll.c b/drivers/staging/qcacld-3.0/core/dp/htt/htt_rx_ll.c new file mode 100644 index 0000000000000000000000000000000000000000..210c3ef766a01b861b0803d689a681990875ae57 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_rx_ll.c @@ -0,0 +1,2413 @@ +/* + * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_mem_malloc,free, etc. */ +#include /* qdf_print, bool */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_timer_free */ + +#include /* HTT_HL_RX_DESC_SIZE */ +#include +#include +#include +#include /* HTT_ASSERT, htt_pdev_t, HTT_RX_BUF_SIZE */ +#include "regtable.h" + +#include /* ieee80211_frame, ieee80211_qoscntl */ +#include +#include +#include "ol_txrx_types.h" +#ifdef DEBUG_DMA_DONE +#include +#include +#endif +#include + +#ifdef DEBUG_DMA_DONE +#define MAX_DONE_BIT_CHECK_ITER 5 +#endif + +#ifdef HTT_DEBUG_DATA +#define HTT_PKT_DUMP(x) x +#else +#define HTT_PKT_DUMP(x) /* no-op */ +#endif + +/*--- setup / tear-down functions -------------------------------------------*/ + +#ifndef HTT_RX_HOST_LATENCY_MAX_MS +#define HTT_RX_HOST_LATENCY_MAX_MS 20 /* ms */ /* very conservative */ +#endif + + /* very conservative to ensure enough buffers are allocated */ +#ifndef HTT_RX_HOST_LATENCY_WORST_LIKELY_MS +#ifdef QCA_WIFI_3_0 +#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 20 +#else +#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10 +#endif +#endif + +#ifndef HTT_RX_RING_REFILL_RETRY_TIME_MS +#define HTT_RX_RING_REFILL_RETRY_TIME_MS 50 +#endif + +#define RX_PADDR_MAGIC_PATTERN 0xDEAD0000 + +#ifdef ENABLE_DEBUG_ADDRESS_MARKING +static qdf_dma_addr_t +htt_rx_paddr_mark_high_bits(qdf_dma_addr_t paddr) +{ + if (sizeof(qdf_dma_addr_t) > 4) { + /* clear high bits, leave lower 37 bits (paddr) */ + paddr &= 0x01FFFFFFFFF; + /* mark upper 16 bits of paddr */ + paddr |= (((uint64_t)RX_PADDR_MAGIC_PATTERN) << 32); + } + return paddr; +} +#else +static qdf_dma_addr_t +htt_rx_paddr_mark_high_bits(qdf_dma_addr_t paddr) +{ + return paddr; +} +#endif + +/** + * htt_get_first_packet_after_wow_wakeup() - get first packet after wow wakeup + * @msg_word: pointer to rx indication message word + * @buf: pointer to buffer + * + * Return: None + */ +static void +htt_get_first_packet_after_wow_wakeup(uint32_t *msg_word, qdf_nbuf_t buf) +{ + if (HTT_RX_IN_ORD_PADDR_IND_MSDU_INFO_GET(*msg_word) & + FW_MSDU_INFO_FIRST_WAKEUP_M) { + qdf_nbuf_mark_wakeup_frame(buf); + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "%s: First packet after WOW Wakeup rcvd", __func__); + } +} + +/** + * htt_rx_ring_smmu_mapped() - check if rx ring is smmu mapped or not + * @pdev: HTT pdev handle + * + * Return: true or false. + */ +static inline bool htt_rx_ring_smmu_mapped(htt_pdev_handle pdev) +{ + if (qdf_mem_smmu_s1_enabled(pdev->osdev) && + pdev->is_ipa_uc_enabled && + pdev->rx_ring.smmu_map) + return true; + else + return false; +} + +static inline qdf_nbuf_t htt_rx_netbuf_pop(htt_pdev_handle pdev) +{ + int idx; + qdf_nbuf_t msdu; + + HTT_ASSERT1(htt_rx_ring_elems(pdev) != 0); + +#ifdef DEBUG_DMA_DONE + pdev->rx_ring.dbg_ring_idx++; + pdev->rx_ring.dbg_ring_idx &= pdev->rx_ring.size_mask; +#endif + + idx = pdev->rx_ring.sw_rd_idx.msdu_payld; + msdu = pdev->rx_ring.buf.netbufs_ring[idx]; + idx++; + idx &= pdev->rx_ring.size_mask; + pdev->rx_ring.sw_rd_idx.msdu_payld = idx; + pdev->rx_ring.fill_cnt--; + return msdu; +} + +static inline unsigned int htt_rx_ring_elems(struct htt_pdev_t *pdev) +{ + return + (*pdev->rx_ring.alloc_idx.vaddr - + pdev->rx_ring.sw_rd_idx.msdu_payld) & pdev->rx_ring.size_mask; +} + +/** + * htt_rx_buff_pool_init() - initialize the pool of buffers + * @pdev: pointer to device + * + * Return: 0 - success, 1 - failure + */ +static int htt_rx_buff_pool_init(struct htt_pdev_t *pdev) +{ + qdf_nbuf_t net_buf; + int i; + + pdev->rx_buff_pool.netbufs_ring = + qdf_mem_malloc(HTT_RX_PRE_ALLOC_POOL_SIZE * sizeof(qdf_nbuf_t)); + + if (!pdev->rx_buff_pool.netbufs_ring) + return 1; /* failure */ + + qdf_atomic_init(&pdev->rx_buff_pool.fill_cnt); + qdf_atomic_init(&pdev->rx_buff_pool.refill_low_mem); + + for (i = 0; i < HTT_RX_PRE_ALLOC_POOL_SIZE; i++) { + net_buf = qdf_nbuf_alloc(pdev->osdev, + HTT_RX_BUF_SIZE, + 0, 4, false); + if (net_buf) { + qdf_atomic_inc(&pdev->rx_buff_pool.fill_cnt); + /* + * Mark this netbuf to differentiate it + * from other buf. If set 1, this buf + * is from pre allocated pool. + */ + QDF_NBUF_CB_RX_PACKET_BUFF_POOL(net_buf) = 1; + } + /* Allow NULL to be inserted. + * Taken care during alloc from this pool. + */ + pdev->rx_buff_pool.netbufs_ring[i] = net_buf; + } + QDF_TRACE(QDF_MODULE_ID_HTT, + QDF_TRACE_LEVEL_INFO, + "max pool size %d pool filled %d", + HTT_RX_PRE_ALLOC_POOL_SIZE, + qdf_atomic_read(&pdev->rx_buff_pool.fill_cnt)); + + qdf_spinlock_create(&pdev->rx_buff_pool.rx_buff_pool_lock); + return 0; +} + +/** + * htt_rx_buff_pool_deinit() - deinitialize the pool of buffers + * @pdev: pointer to device + * + * Return: none + */ +static void htt_rx_buff_pool_deinit(struct htt_pdev_t *pdev) +{ + qdf_nbuf_t net_buf; + int i; + + if (!pdev->rx_buff_pool.netbufs_ring) + return; + + qdf_spin_lock_bh(&pdev->rx_buff_pool.rx_buff_pool_lock); + for (i = 0; i < HTT_RX_PRE_ALLOC_POOL_SIZE; i++) { + net_buf = pdev->rx_buff_pool.netbufs_ring[i]; + if (!net_buf) + continue; + qdf_nbuf_free(net_buf); + qdf_atomic_dec(&pdev->rx_buff_pool.fill_cnt); + } + qdf_spin_unlock_bh(&pdev->rx_buff_pool.rx_buff_pool_lock); + QDF_TRACE(QDF_MODULE_ID_HTT, + QDF_TRACE_LEVEL_INFO, + "max pool size %d pool filled %d", + HTT_RX_PRE_ALLOC_POOL_SIZE, + qdf_atomic_read(&pdev->rx_buff_pool.fill_cnt)); + + qdf_mem_free(pdev->rx_buff_pool.netbufs_ring); + qdf_spinlock_destroy(&pdev->rx_buff_pool.rx_buff_pool_lock); +} + +/** + * htt_rx_buff_pool_refill() - refill the pool with new buf or reuse same buf + * @pdev: pointer to device + * @netbuf: netbuf to reuse + * + * Return: true - if able to alloc new buf and insert into pool, + * false - if need to reuse the netbuf or not able to insert into pool + */ +static bool htt_rx_buff_pool_refill(struct htt_pdev_t *pdev, qdf_nbuf_t netbuf) +{ + bool ret = false; + qdf_nbuf_t net_buf; + int i; + + net_buf = qdf_nbuf_alloc(pdev->osdev, + HTT_RX_BUF_SIZE, + 0, 4, false); + if (net_buf) { + /* able to alloc new net_buf. + * mark this netbuf as pool buf. + */ + QDF_NBUF_CB_RX_PACKET_BUFF_POOL(net_buf) = 1; + ret = true; + } else { + /* reuse the netbuf and + * reset all fields of this netbuf. + */ + net_buf = netbuf; + qdf_nbuf_reset(net_buf, 0, 4); + + /* mark this netbuf as pool buf */ + QDF_NBUF_CB_RX_PACKET_BUFF_POOL(net_buf) = 1; + } + + qdf_spin_lock_bh(&pdev->rx_buff_pool.rx_buff_pool_lock); + for (i = 0; i < HTT_RX_PRE_ALLOC_POOL_SIZE; i++) { + /* insert the netbuf in empty slot of pool */ + if (pdev->rx_buff_pool.netbufs_ring[i]) + continue; + + pdev->rx_buff_pool.netbufs_ring[i] = net_buf; + qdf_atomic_inc(&pdev->rx_buff_pool.fill_cnt); + break; + } + qdf_spin_unlock_bh(&pdev->rx_buff_pool.rx_buff_pool_lock); + + if (i == HTT_RX_PRE_ALLOC_POOL_SIZE) { + /* fail to insert into pool, free net_buf */ + qdf_nbuf_free(net_buf); + ret = false; + } + + return ret; +} + +/** + * htt_rx_buff_alloc() - alloc the net buf from the pool + * @pdev: pointer to device + * + * Return: nbuf or NULL + */ +static qdf_nbuf_t htt_rx_buff_alloc(struct htt_pdev_t *pdev) +{ + qdf_nbuf_t net_buf = NULL; + int i; + + if (!pdev->rx_buff_pool.netbufs_ring) + return net_buf; + + qdf_spin_lock_bh(&pdev->rx_buff_pool.rx_buff_pool_lock); + for (i = 0; i < HTT_RX_PRE_ALLOC_POOL_SIZE; i++) { + /* allocate the valid netbuf */ + if (!pdev->rx_buff_pool.netbufs_ring[i]) + continue; + + net_buf = pdev->rx_buff_pool.netbufs_ring[i]; + qdf_atomic_dec(&pdev->rx_buff_pool.fill_cnt); + pdev->rx_buff_pool.netbufs_ring[i] = NULL; + break; + } + qdf_spin_unlock_bh(&pdev->rx_buff_pool.rx_buff_pool_lock); + return net_buf; +} + +/** + * htt_rx_ring_buf_attach() - retrun net buf to attach in ring + * @pdev: pointer to device + * + * Return: nbuf or NULL + */ +static qdf_nbuf_t htt_rx_ring_buf_attach(struct htt_pdev_t *pdev) +{ + qdf_nbuf_t net_buf = NULL; + bool allocated = true; + + net_buf = + qdf_nbuf_alloc(pdev->osdev, HTT_RX_BUF_SIZE, + 0, 4, false); + if (!net_buf) { + if (pdev->rx_buff_pool.netbufs_ring && + qdf_atomic_read(&pdev->rx_buff_pool.refill_low_mem) && + qdf_atomic_read(&pdev->rx_buff_pool.fill_cnt)) + net_buf = htt_rx_buff_alloc(pdev); + + allocated = false; /* allocated from pool */ + } + + if (allocated || !qdf_atomic_read(&pdev->rx_buff_pool.fill_cnt)) + qdf_atomic_set(&pdev->rx_buff_pool.refill_low_mem, 0); + + return net_buf; +} + +/** + * htt_rx_ring_buff_free() - free the net buff or reuse it + * @pdev: pointer to device + * @netbuf: netbuf + * + * Return: none + */ +static void htt_rx_ring_buff_free(struct htt_pdev_t *pdev, qdf_nbuf_t netbuf) +{ + bool status = false; + + if (pdev->rx_buff_pool.netbufs_ring && + QDF_NBUF_CB_RX_PACKET_BUFF_POOL(netbuf)) { + int i; + + /* rest this netbuf before putting back into pool */ + qdf_nbuf_reset(netbuf, 0, 4); + + /* mark this netbuf as pool buf */ + QDF_NBUF_CB_RX_PACKET_BUFF_POOL(netbuf) = 1; + + qdf_spin_lock_bh(&pdev->rx_buff_pool.rx_buff_pool_lock); + for (i = 0; i < HTT_RX_PRE_ALLOC_POOL_SIZE; i++) { + /* insert the netbuf in empty slot of pool */ + if (!pdev->rx_buff_pool.netbufs_ring[i]) { + pdev->rx_buff_pool.netbufs_ring[i] = netbuf; + qdf_atomic_inc(&pdev->rx_buff_pool.fill_cnt); + status = true; /* valid insertion */ + break; + } + } + qdf_spin_unlock_bh(&pdev->rx_buff_pool.rx_buff_pool_lock); + } + if (!status) + qdf_nbuf_free(netbuf); +} + +/* full_reorder_offload case: this function is called with lock held */ +static int htt_rx_ring_fill_n(struct htt_pdev_t *pdev, int num) +{ + int idx; + QDF_STATUS status; + struct htt_host_rx_desc_base *rx_desc; + int filled = 0; + int debt_served = 0; + qdf_mem_info_t mem_map_table = {0}; + + idx = *pdev->rx_ring.alloc_idx.vaddr; + + if ((idx < 0) || (idx > pdev->rx_ring.size_mask) || + (num > pdev->rx_ring.size)) { + QDF_TRACE(QDF_MODULE_ID_HTT, + QDF_TRACE_LEVEL_ERROR, + "%s:rx refill failed!", __func__); + return filled; + } + +moretofill: + while (num > 0) { + qdf_dma_addr_t paddr, paddr_marked; + qdf_nbuf_t rx_netbuf; + int headroom; + + rx_netbuf = htt_rx_ring_buf_attach(pdev); + if (!rx_netbuf) { + qdf_timer_stop(&pdev->rx_ring. + refill_retry_timer); + /* + * Failed to fill it to the desired level - + * we'll start a timer and try again next time. + * As long as enough buffers are left in the ring for + * another A-MPDU rx, no special recovery is needed. + */ +#ifdef DEBUG_DMA_DONE + pdev->rx_ring.dbg_refill_cnt++; +#endif + pdev->refill_retry_timer_starts++; + qdf_timer_start( + &pdev->rx_ring.refill_retry_timer, + HTT_RX_RING_REFILL_RETRY_TIME_MS); + goto update_alloc_idx; + } + + /* Clear rx_desc attention word before posting to Rx ring */ + rx_desc = htt_rx_desc(rx_netbuf); + *(uint32_t *)&rx_desc->attention = 0; + +#ifdef DEBUG_DMA_DONE + *(uint32_t *)&rx_desc->msdu_end = 1; + +#define MAGIC_PATTERN 0xDEADBEEF + *(uint32_t *)&rx_desc->msdu_start = MAGIC_PATTERN; + + /* + * To ensure that attention bit is reset and msdu_end is set + * before calling dma_map + */ + smp_mb(); +#endif + /* + * Adjust qdf_nbuf_data to point to the location in the buffer + * where the rx descriptor will be filled in. + */ + headroom = qdf_nbuf_data(rx_netbuf) - (uint8_t *)rx_desc; + qdf_nbuf_push_head(rx_netbuf, headroom); + +#ifdef DEBUG_DMA_DONE + status = qdf_nbuf_map(pdev->osdev, rx_netbuf, + QDF_DMA_BIDIRECTIONAL); +#else + status = qdf_nbuf_map(pdev->osdev, rx_netbuf, + QDF_DMA_FROM_DEVICE); +#endif + if (status != QDF_STATUS_SUCCESS) { + htt_rx_ring_buff_free(pdev, rx_netbuf); + goto update_alloc_idx; + } + + paddr = qdf_nbuf_get_frag_paddr(rx_netbuf, 0); + paddr_marked = htt_rx_paddr_mark_high_bits(paddr); + if (pdev->cfg.is_full_reorder_offload) { + if (qdf_unlikely(htt_rx_hash_list_insert( + pdev, paddr_marked, rx_netbuf))) { + QDF_TRACE(QDF_MODULE_ID_HTT, + QDF_TRACE_LEVEL_ERROR, + "%s: hash insert failed!", __func__); +#ifdef DEBUG_DMA_DONE + qdf_nbuf_unmap(pdev->osdev, rx_netbuf, + QDF_DMA_BIDIRECTIONAL); +#else + qdf_nbuf_unmap(pdev->osdev, rx_netbuf, + QDF_DMA_FROM_DEVICE); +#endif + htt_rx_ring_buff_free(pdev, rx_netbuf); + + goto update_alloc_idx; + } + htt_rx_dbg_rxbuf_set(pdev, paddr_marked, rx_netbuf); + } else { + pdev->rx_ring.buf.netbufs_ring[idx] = rx_netbuf; + } + + /* Caller already protected this function with refill_lock */ + if (qdf_nbuf_is_rx_ipa_smmu_map(rx_netbuf)) { + qdf_update_mem_map_table(pdev->osdev, &mem_map_table, + paddr, HTT_RX_BUF_SIZE); + qdf_assert_always( + !cds_smmu_map_unmap(true, 1, &mem_map_table)); + } + + pdev->rx_ring.buf.paddrs_ring[idx] = paddr_marked; + pdev->rx_ring.fill_cnt++; + + num--; + idx++; + filled++; + idx &= pdev->rx_ring.size_mask; + } + + if (debt_served < qdf_atomic_read(&pdev->rx_ring.refill_debt)) { + num = qdf_atomic_read(&pdev->rx_ring.refill_debt) - debt_served; + debt_served += num; + goto moretofill; + } + +update_alloc_idx: + /* + * Make sure alloc index write is reflected correctly before FW polls + * remote ring write index as compiler can reorder the instructions + * based on optimizations. + */ + qdf_mb(); + *pdev->rx_ring.alloc_idx.vaddr = idx; + htt_rx_dbg_rxbuf_indupd(pdev, idx); + + return filled; +} + +static int htt_rx_ring_size(struct htt_pdev_t *pdev) +{ + int size; + + /* + * It is expected that the host CPU will typically be able to service + * the rx indication from one A-MPDU before the rx indication from + * the subsequent A-MPDU happens, roughly 1-2 ms later. + * However, the rx ring should be sized very conservatively, to + * accommodate the worst reasonable delay before the host CPU services + * a rx indication interrupt. + * The rx ring need not be kept full of empty buffers. In theory, + * the htt host SW can dynamically track the low-water mark in the + * rx ring, and dynamically adjust the level to which the rx ring + * is filled with empty buffers, to dynamically meet the desired + * low-water mark. + * In contrast, it's difficult to resize the rx ring itself, once + * it's in use. + * Thus, the ring itself should be sized very conservatively, while + * the degree to which the ring is filled with empty buffers should + * be sized moderately conservatively. + */ + size = + ol_cfg_max_thruput_mbps(pdev->ctrl_pdev) * + 1000 /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */ / + (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS; + + if (size < HTT_RX_RING_SIZE_MIN) + size = HTT_RX_RING_SIZE_MIN; + else if (size > HTT_RX_RING_SIZE_MAX) + size = HTT_RX_RING_SIZE_MAX; + + size = qdf_get_pwr2(size); + return size; +} + +static int htt_rx_ring_fill_level(struct htt_pdev_t *pdev) +{ + int size; + + size = ol_cfg_max_thruput_mbps(pdev->ctrl_pdev) * + 1000 /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */ / + (8 * HTT_RX_AVG_FRM_BYTES) * + HTT_RX_HOST_LATENCY_WORST_LIKELY_MS; + + size = qdf_get_pwr2(size); + /* + * Make sure the fill level is at least 1 less than the ring size. + * Leaving 1 element empty allows the SW to easily distinguish + * between a full ring vs. an empty ring. + */ + if (size >= pdev->rx_ring.size) + size = pdev->rx_ring.size - 1; + + return size; +} + +static void htt_rx_ring_refill_retry(void *arg) +{ + htt_pdev_handle pdev = (htt_pdev_handle)arg; + int filled = 0; + int num; + + pdev->refill_retry_timer_calls++; + qdf_spin_lock_bh(&pdev->rx_ring.refill_lock); + + num = qdf_atomic_read(&pdev->rx_ring.refill_debt); + qdf_atomic_sub(num, &pdev->rx_ring.refill_debt); + + qdf_atomic_set(&pdev->rx_buff_pool.refill_low_mem, 1); + + filled = htt_rx_ring_fill_n(pdev, num); + + if (filled > num) { + /* we served ourselves and some other debt */ + /* sub is safer than = 0 */ + qdf_atomic_sub(filled - num, &pdev->rx_ring.refill_debt); + } else if (num == filled) { /* nothing to be done */ + } else { + qdf_atomic_add(num - filled, &pdev->rx_ring.refill_debt); + /* we could not fill all, timer must have been started */ + pdev->refill_retry_timer_doubles++; + } + qdf_spin_unlock_bh(&pdev->rx_ring.refill_lock); +} + +/*--- rx descriptor field access functions ----------------------------------*/ +/* + * These functions need to use bit masks and shifts to extract fields + * from the rx descriptors, rather than directly using the bitfields. + * For example, use + * (desc & FIELD_MASK) >> FIELD_LSB + * rather than + * desc.field + * This allows the functions to work correctly on either little-endian + * machines (no endianness conversion needed) or big-endian machines + * (endianness conversion provided automatically by the HW DMA's + * byte-swizzling). + */ + +#ifdef CHECKSUM_OFFLOAD +static inline void +htt_set_checksum_result_ll(htt_pdev_handle pdev, qdf_nbuf_t msdu, + struct htt_host_rx_desc_base *rx_desc) +{ +#define MAX_IP_VER 2 +#define MAX_PROTO_VAL 4 + struct rx_msdu_start *rx_msdu = &rx_desc->msdu_start; + unsigned int proto = (rx_msdu->tcp_proto) | (rx_msdu->udp_proto << 1); + + /* + * HW supports TCP & UDP checksum offload for ipv4 and ipv6 + */ + static const qdf_nbuf_l4_rx_cksum_type_t + cksum_table[][MAX_PROTO_VAL][MAX_IP_VER] = { + { + /* non-fragmented IP packet */ + /* non TCP/UDP packet */ + {QDF_NBUF_RX_CKSUM_ZERO, QDF_NBUF_RX_CKSUM_ZERO}, + /* TCP packet */ + {QDF_NBUF_RX_CKSUM_TCP, QDF_NBUF_RX_CKSUM_TCPIPV6}, + /* UDP packet */ + {QDF_NBUF_RX_CKSUM_UDP, QDF_NBUF_RX_CKSUM_UDPIPV6}, + /* invalid packet type */ + {QDF_NBUF_RX_CKSUM_ZERO, QDF_NBUF_RX_CKSUM_ZERO}, + }, + { + /* fragmented IP packet */ + {QDF_NBUF_RX_CKSUM_ZERO, QDF_NBUF_RX_CKSUM_ZERO}, + {QDF_NBUF_RX_CKSUM_ZERO, QDF_NBUF_RX_CKSUM_ZERO}, + {QDF_NBUF_RX_CKSUM_ZERO, QDF_NBUF_RX_CKSUM_ZERO}, + {QDF_NBUF_RX_CKSUM_ZERO, QDF_NBUF_RX_CKSUM_ZERO}, + } + }; + + qdf_nbuf_rx_cksum_t cksum = { + cksum_table[rx_msdu->ip_frag][proto][rx_msdu->ipv6_proto], + QDF_NBUF_RX_CKSUM_NONE, + 0 + }; + + if (cksum.l4_type != + (qdf_nbuf_l4_rx_cksum_type_t)QDF_NBUF_RX_CKSUM_NONE) { + cksum.l4_result = + ((*(uint32_t *)&rx_desc->attention) & + RX_ATTENTION_0_TCP_UDP_CHKSUM_FAIL_MASK) ? + QDF_NBUF_RX_CKSUM_NONE : + QDF_NBUF_RX_CKSUM_TCP_UDP_UNNECESSARY; + } + qdf_nbuf_set_rx_cksum(msdu, &cksum); +#undef MAX_IP_VER +#undef MAX_PROTO_VAL +} + +#else + +static inline +void htt_set_checksum_result_ll(htt_pdev_handle pdev, qdf_nbuf_t msdu, + struct htt_host_rx_desc_base *rx_desc) +{ +} + +#endif + +static void *htt_rx_msdu_desc_retrieve_ll(htt_pdev_handle pdev, qdf_nbuf_t msdu) +{ + return htt_rx_desc(msdu); +} + +static bool htt_rx_mpdu_is_encrypted_ll(htt_pdev_handle pdev, void *mpdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)mpdu_desc; + + return (((*((uint32_t *)&rx_desc->mpdu_start)) & + RX_MPDU_START_0_ENCRYPTED_MASK) >> + RX_MPDU_START_0_ENCRYPTED_LSB) ? true : false; +} + +static +bool htt_rx_msdu_chan_info_present_ll(htt_pdev_handle pdev, void *mpdu_desc) +{ + return false; +} + +static bool htt_rx_msdu_center_freq_ll(htt_pdev_handle pdev, + struct ol_txrx_peer_t *peer, + void *mpdu_desc, + uint16_t *primary_chan_center_freq_mhz, + uint16_t *contig_chan1_center_freq_mhz, + uint16_t *contig_chan2_center_freq_mhz, + uint8_t *phy_mode) +{ + if (primary_chan_center_freq_mhz) + *primary_chan_center_freq_mhz = 0; + if (contig_chan1_center_freq_mhz) + *contig_chan1_center_freq_mhz = 0; + if (contig_chan2_center_freq_mhz) + *contig_chan2_center_freq_mhz = 0; + if (phy_mode) + *phy_mode = 0; + return false; +} + +static bool +htt_rx_msdu_first_msdu_flag_ll(htt_pdev_handle pdev, void *msdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)msdu_desc; + return (bool) + (((*(((uint32_t *)&rx_desc->msdu_end) + 4)) & + RX_MSDU_END_4_FIRST_MSDU_MASK) >> + RX_MSDU_END_4_FIRST_MSDU_LSB); +} + +static bool +htt_rx_msdu_desc_key_id_ll(htt_pdev_handle pdev, void *mpdu_desc, + uint8_t *key_id) +{ + struct htt_host_rx_desc_base *rx_desc = (struct htt_host_rx_desc_base *) + mpdu_desc; + + if (!htt_rx_msdu_first_msdu_flag_ll(pdev, mpdu_desc)) + return false; + + *key_id = ((*(((uint32_t *)&rx_desc->msdu_end) + 1)) & + (RX_MSDU_END_1_KEY_ID_OCT_MASK >> + RX_MSDU_END_1_KEY_ID_OCT_LSB)); + + return true; +} + +/** + * htt_rx_mpdu_desc_retry_ll() - Returns the retry bit from the Rx descriptor + * for the Low Latency driver + * @pdev: Handle (pointer) to HTT pdev. + * @mpdu_desc: Void pointer to the Rx descriptor for MPDU + * before the beginning of the payload. + * + * This function returns the retry bit of the 802.11 header for the + * provided rx MPDU descriptor. + * + * Return: boolean -- true if retry is set, false otherwise + */ +static bool +htt_rx_mpdu_desc_retry_ll(htt_pdev_handle pdev, void *mpdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)mpdu_desc; + + return + (bool)(((*((uint32_t *)&rx_desc->mpdu_start)) & + RX_MPDU_START_0_RETRY_MASK) >> + RX_MPDU_START_0_RETRY_LSB); +} + +static uint16_t htt_rx_mpdu_desc_seq_num_ll(htt_pdev_handle pdev, + void *mpdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)mpdu_desc; + + return + (uint16_t)(((*((uint32_t *)&rx_desc->mpdu_start)) & + RX_MPDU_START_0_SEQ_NUM_MASK) >> + RX_MPDU_START_0_SEQ_NUM_LSB); +} + +static void +htt_rx_mpdu_desc_pn_ll(htt_pdev_handle pdev, + void *mpdu_desc, union htt_rx_pn_t *pn, int pn_len_bits) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)mpdu_desc; + + switch (pn_len_bits) { + case 24: + /* bits 23:0 */ + pn->pn24 = rx_desc->mpdu_start.pn_31_0 & 0xffffff; + break; + case 48: + /* bits 31:0 */ + pn->pn48 = rx_desc->mpdu_start.pn_31_0; + /* bits 47:32 */ + pn->pn48 |= ((uint64_t) + ((*(((uint32_t *)&rx_desc->mpdu_start) + 2)) + & RX_MPDU_START_2_PN_47_32_MASK)) + << (32 - RX_MPDU_START_2_PN_47_32_LSB); + break; + case 128: + /* bits 31:0 */ + pn->pn128[0] = rx_desc->mpdu_start.pn_31_0; + /* bits 47:32 */ + pn->pn128[0] |= + ((uint64_t)((*(((uint32_t *)&rx_desc->mpdu_start) + 2)) + & RX_MPDU_START_2_PN_47_32_MASK)) + << (32 - RX_MPDU_START_2_PN_47_32_LSB); + /* bits 63:48 */ + pn->pn128[0] |= + ((uint64_t)((*(((uint32_t *)&rx_desc->msdu_end) + 2)) + & RX_MSDU_END_1_EXT_WAPI_PN_63_48_MASK)) + << (48 - RX_MSDU_END_1_EXT_WAPI_PN_63_48_LSB); + /* bits 95:64 */ + pn->pn128[1] = rx_desc->msdu_end.ext_wapi_pn_95_64; + /* bits 127:96 */ + pn->pn128[1] |= + ((uint64_t)rx_desc->msdu_end.ext_wapi_pn_127_96) << 32; + break; + default: + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "Error: invalid length spec (%d bits) for PN", + pn_len_bits); + }; +} + +/** + * htt_rx_mpdu_desc_tid_ll() - Returns the TID value from the Rx descriptor + * for Low Latency driver + * @pdev: Handle (pointer) to HTT pdev. + * @mpdu_desc: Void pointer to the Rx descriptor for the MPDU + * before the beginning of the payload. + * + * This function returns the TID set in the 802.11 QoS Control for the MPDU + * in the packet header, by looking at the mpdu_start of the Rx descriptor. + * Rx descriptor gets a copy of the TID from the MAC. + * + * Return: Actual TID set in the packet header. + */ +static uint8_t +htt_rx_mpdu_desc_tid_ll(htt_pdev_handle pdev, void *mpdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)mpdu_desc; + + return + (uint8_t)(((*(((uint32_t *)&rx_desc->mpdu_start) + 2)) & + RX_MPDU_START_2_TID_MASK) >> + RX_MPDU_START_2_TID_LSB); +} + +static bool htt_rx_msdu_desc_completes_mpdu_ll(htt_pdev_handle pdev, + void *msdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)msdu_desc; + return (bool) + (((*(((uint32_t *)&rx_desc->msdu_end) + 4)) & + RX_MSDU_END_4_LAST_MSDU_MASK) >> RX_MSDU_END_4_LAST_MSDU_LSB); +} + +static int htt_rx_msdu_has_wlan_mcast_flag_ll(htt_pdev_handle pdev, + void *msdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)msdu_desc; + /* + * HW rx desc: the mcast_bcast flag is only valid + * if first_msdu is set + */ + return ((*(((uint32_t *)&rx_desc->msdu_end) + 4)) & + RX_MSDU_END_4_FIRST_MSDU_MASK) >> RX_MSDU_END_4_FIRST_MSDU_LSB; +} + +static bool htt_rx_msdu_is_wlan_mcast_ll(htt_pdev_handle pdev, void *msdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)msdu_desc; + return ((*((uint32_t *)&rx_desc->attention)) & + RX_ATTENTION_0_MCAST_BCAST_MASK) + >> RX_ATTENTION_0_MCAST_BCAST_LSB; +} + +static int htt_rx_msdu_is_frag_ll(htt_pdev_handle pdev, void *msdu_desc) +{ + struct htt_host_rx_desc_base *rx_desc = + (struct htt_host_rx_desc_base *)msdu_desc; + return ((*((uint32_t *)&rx_desc->attention)) & + RX_ATTENTION_0_FRAGMENT_MASK) >> RX_ATTENTION_0_FRAGMENT_LSB; +} + +static inline int +htt_rx_offload_msdu_cnt_ll(htt_pdev_handle pdev) +{ + return htt_rx_ring_elems(pdev); +} + +static int +htt_rx_offload_msdu_pop_ll(htt_pdev_handle pdev, + qdf_nbuf_t offload_deliver_msg, + int *vdev_id, + int *peer_id, + int *tid, + uint8_t *fw_desc, + qdf_nbuf_t *head_buf, qdf_nbuf_t *tail_buf) +{ + qdf_nbuf_t buf; + uint32_t *msdu_hdr, msdu_len; + + *head_buf = *tail_buf = buf = htt_rx_netbuf_pop(pdev); + + if (qdf_unlikely(!buf)) { + qdf_print("netbuf pop failed!"); + return 1; + } + + /* Fake read mpdu_desc to keep desc ptr in sync */ + htt_rx_mpdu_desc_list_next(pdev, NULL); + qdf_nbuf_set_pktlen(buf, HTT_RX_BUF_SIZE); +#ifdef DEBUG_DMA_DONE + qdf_nbuf_unmap(pdev->osdev, buf, QDF_DMA_BIDIRECTIONAL); +#else + qdf_nbuf_unmap(pdev->osdev, buf, QDF_DMA_FROM_DEVICE); +#endif + msdu_hdr = (uint32_t *)qdf_nbuf_data(buf); + + /* First dword */ + msdu_len = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_LEN_GET(*msdu_hdr); + *peer_id = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_PEER_ID_GET(*msdu_hdr); + + /* Second dword */ + msdu_hdr++; + *vdev_id = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_VDEV_ID_GET(*msdu_hdr); + *tid = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_TID_GET(*msdu_hdr); + *fw_desc = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_DESC_GET(*msdu_hdr); + + qdf_nbuf_pull_head(buf, HTT_RX_OFFLOAD_DELIVER_IND_MSDU_HDR_BYTES); + qdf_nbuf_set_pktlen(buf, msdu_len); + return 0; +} + +int +htt_rx_offload_paddr_msdu_pop_ll(htt_pdev_handle pdev, + uint32_t *msg_word, + int msdu_iter, + int *vdev_id, + int *peer_id, + int *tid, + uint8_t *fw_desc, + qdf_nbuf_t *head_buf, qdf_nbuf_t *tail_buf) +{ + qdf_nbuf_t buf; + uint32_t *msdu_hdr, msdu_len; + uint32_t *curr_msdu; + qdf_dma_addr_t paddr; + + curr_msdu = + msg_word + (msdu_iter * HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS); + paddr = htt_rx_in_ord_paddr_get(curr_msdu); + *head_buf = *tail_buf = buf = htt_rx_in_order_netbuf_pop(pdev, paddr); + + if (qdf_unlikely(!buf)) { + qdf_print("netbuf pop failed!"); + return 1; + } + qdf_nbuf_set_pktlen(buf, HTT_RX_BUF_SIZE); +#ifdef DEBUG_DMA_DONE + qdf_nbuf_unmap(pdev->osdev, buf, QDF_DMA_BIDIRECTIONAL); +#else + qdf_nbuf_unmap(pdev->osdev, buf, QDF_DMA_FROM_DEVICE); +#endif + + if (pdev->cfg.is_first_wakeup_packet) + htt_get_first_packet_after_wow_wakeup( + msg_word + NEXT_FIELD_OFFSET_IN32, buf); + + msdu_hdr = (uint32_t *)qdf_nbuf_data(buf); + + /* First dword */ + msdu_len = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_LEN_GET(*msdu_hdr); + *peer_id = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_PEER_ID_GET(*msdu_hdr); + + /* Second dword */ + msdu_hdr++; + *vdev_id = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_VDEV_ID_GET(*msdu_hdr); + *tid = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_TID_GET(*msdu_hdr); + *fw_desc = HTT_RX_OFFLOAD_DELIVER_IND_MSDU_DESC_GET(*msdu_hdr); + + qdf_nbuf_pull_head(buf, HTT_RX_OFFLOAD_DELIVER_IND_MSDU_HDR_BYTES); + qdf_nbuf_set_pktlen(buf, msdu_len); + return 0; +} + +#ifdef WLAN_FULL_REORDER_OFFLOAD + +/* Number of buckets in the hash table */ +#define RX_NUM_HASH_BUCKETS 1024 /* This should always be a power of 2 */ +#define RX_NUM_HASH_BUCKETS_MASK (RX_NUM_HASH_BUCKETS - 1) + +/* Number of hash entries allocated per bucket */ +#define RX_ENTRIES_SIZE 10 + +#define RX_HASH_FUNCTION(a) \ + ((((a) >> 14) ^ ((a) >> 4)) & RX_NUM_HASH_BUCKETS_MASK) + +#ifdef RX_HASH_DEBUG_LOG +#define RX_HASH_LOG(x) x +#else +#define RX_HASH_LOG(x) /* no-op */ +#endif + +/* Return values: 1 - success, 0 - failure */ +#define RX_DESC_DISCARD_IS_SET ((*((u_int8_t *)&rx_desc->fw_desc.u.val)) & \ + FW_RX_DESC_DISCARD_M) +#define RX_DESC_MIC_ERR_IS_SET ((*((u_int8_t *)&rx_desc->fw_desc.u.val)) & \ + FW_RX_DESC_ANY_ERR_M) + +#define RX_RING_REFILL_DEBT_MAX 128 + +/* Initializes the circular linked list */ +static inline void htt_list_init(struct htt_list_node *head) +{ + head->prev = head; + head->next = head; +} + +/* Adds entry to the end of the linked list */ +static inline void htt_list_add_tail(struct htt_list_node *head, + struct htt_list_node *node) +{ + head->prev->next = node; + node->prev = head->prev; + node->next = head; + head->prev = node; +} + +/* Removes the entry corresponding to the input node from the linked list */ +static inline void htt_list_remove(struct htt_list_node *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; +} + +/* Helper macro to iterate through the linked list */ +#define HTT_LIST_ITER_FWD(iter, head) for (iter = (head)->next; \ + (iter) != (head); \ + (iter) = (iter)->next) \ + +#ifdef RX_HASH_DEBUG +/* Hash cookie related macros */ +#define HTT_RX_HASH_COOKIE 0xDEED + +#define HTT_RX_HASH_COOKIE_SET(hash_element) \ + ((hash_element)->cookie = HTT_RX_HASH_COOKIE) + +#define HTT_RX_HASH_COOKIE_CHECK(hash_element) \ + HTT_ASSERT_ALWAYS((hash_element)->cookie == HTT_RX_HASH_COOKIE) + +/* Hash count related macros */ +#define HTT_RX_HASH_COUNT_INCR(hash_bucket) \ + ((hash_bucket)->count++) + +#define HTT_RX_HASH_COUNT_DECR(hash_bucket) \ + ((hash_bucket)->count--) + +#define HTT_RX_HASH_COUNT_RESET(hash_bucket) ((hash_bucket)->count = 0) + +#define HTT_RX_HASH_COUNT_PRINT(hash_bucket) \ + RX_HASH_LOG(qdf_print(" count %d\n", (hash_bucket)->count)) +#else /* RX_HASH_DEBUG */ +/* Hash cookie related macros */ +#define HTT_RX_HASH_COOKIE_SET(hash_element) /* no-op */ +#define HTT_RX_HASH_COOKIE_CHECK(hash_element) /* no-op */ +/* Hash count related macros */ +#define HTT_RX_HASH_COUNT_INCR(hash_bucket) /* no-op */ +#define HTT_RX_HASH_COUNT_DECR(hash_bucket) /* no-op */ +#define HTT_RX_HASH_COUNT_PRINT(hash_bucket) /* no-op */ +#define HTT_RX_HASH_COUNT_RESET(hash_bucket) /* no-op */ +#endif /* RX_HASH_DEBUG */ + +/* + * Inserts the given "physical address - network buffer" pair into the + * hash table for the given pdev. This function will do the following: + * 1. Determine which bucket to insert the pair into + * 2. First try to allocate the hash entry for this pair from the pre-allocated + * entries list + * 3. If there are no more entries in the pre-allocated entries list, allocate + * the hash entry from the hash memory pool + * Note: this function is not thread-safe + * Returns 0 - success, 1 - failure + */ +int +htt_rx_hash_list_insert(struct htt_pdev_t *pdev, + qdf_dma_addr_t paddr, + qdf_nbuf_t netbuf) +{ + int i; + int rc = 0; + struct htt_rx_hash_entry *hash_element = NULL; + + qdf_spin_lock_bh(&pdev->rx_ring.rx_hash_lock); + + /* get rid of the marking bits if they are available */ + paddr = htt_paddr_trim_to_37(paddr); + + i = RX_HASH_FUNCTION(paddr); + + /* Check if there are any entries in the pre-allocated free list */ + if (pdev->rx_ring.hash_table[i]->freepool.next != + &pdev->rx_ring.hash_table[i]->freepool) { + hash_element = + (struct htt_rx_hash_entry *)( + (char *) + pdev->rx_ring.hash_table[i]->freepool.next - + pdev->rx_ring.listnode_offset); + if (qdf_unlikely(!hash_element)) { + HTT_ASSERT_ALWAYS(0); + rc = 1; + goto hli_end; + } + + htt_list_remove(pdev->rx_ring.hash_table[i]->freepool.next); + } else { + hash_element = qdf_mem_malloc(sizeof(struct htt_rx_hash_entry)); + if (qdf_unlikely(!hash_element)) { + HTT_ASSERT_ALWAYS(0); + rc = 1; + goto hli_end; + } + hash_element->fromlist = 0; + } + + hash_element->netbuf = netbuf; + hash_element->paddr = paddr; + HTT_RX_HASH_COOKIE_SET(hash_element); + + htt_list_add_tail(&pdev->rx_ring.hash_table[i]->listhead, + &hash_element->listnode); + + RX_HASH_LOG(qdf_print("rx hash: paddr 0x%x netbuf %pK bucket %d\n", + paddr, netbuf, (int)i)); + + if (htt_rx_ring_smmu_mapped(pdev)) { + if (qdf_unlikely(qdf_nbuf_is_rx_ipa_smmu_map(netbuf))) { + qdf_err("Already smmu mapped, nbuf: %pK", + netbuf); + qdf_assert_always(0); + } + qdf_nbuf_set_rx_ipa_smmu_map(netbuf, true); + } + + HTT_RX_HASH_COUNT_INCR(pdev->rx_ring.hash_table[i]); + HTT_RX_HASH_COUNT_PRINT(pdev->rx_ring.hash_table[i]); + +hli_end: + qdf_spin_unlock_bh(&pdev->rx_ring.rx_hash_lock); + return rc; +} + +/* + * Given a physical address this function will find the corresponding network + * buffer from the hash table. + * paddr is already stripped off of higher marking bits. + */ +qdf_nbuf_t htt_rx_hash_list_lookup(struct htt_pdev_t *pdev, + qdf_dma_addr_t paddr) +{ + uint32_t i; + struct htt_list_node *list_iter = NULL; + qdf_nbuf_t netbuf = NULL; + struct htt_rx_hash_entry *hash_entry; + + qdf_spin_lock_bh(&pdev->rx_ring.rx_hash_lock); + + if (!pdev->rx_ring.hash_table) { + qdf_spin_unlock_bh(&pdev->rx_ring.rx_hash_lock); + return NULL; + } + + i = RX_HASH_FUNCTION(paddr); + + HTT_LIST_ITER_FWD(list_iter, &pdev->rx_ring.hash_table[i]->listhead) { + hash_entry = (struct htt_rx_hash_entry *) + ((char *)list_iter - + pdev->rx_ring.listnode_offset); + + HTT_RX_HASH_COOKIE_CHECK(hash_entry); + + if (hash_entry->paddr == paddr) { + /* Found the entry corresponding to paddr */ + netbuf = hash_entry->netbuf; + /* set netbuf to NULL to trace if freed entry + * is getting unmapped in hash deinit. + */ + hash_entry->netbuf = NULL; + htt_list_remove(&hash_entry->listnode); + HTT_RX_HASH_COUNT_DECR(pdev->rx_ring.hash_table[i]); + /* + * if the rx entry is from the pre-allocated list, + * return it + */ + if (hash_entry->fromlist) + htt_list_add_tail( + &pdev->rx_ring.hash_table[i]->freepool, + &hash_entry->listnode); + else + qdf_mem_free(hash_entry); + + htt_rx_dbg_rxbuf_reset(pdev, netbuf); + break; + } + } + + if (netbuf && htt_rx_ring_smmu_mapped(pdev)) { + if (qdf_unlikely(!qdf_nbuf_is_rx_ipa_smmu_map(netbuf))) { + qdf_err("smmu not mapped nbuf: %pK", netbuf); + qdf_assert_always(0); + } + } + + RX_HASH_LOG(qdf_print("rx hash: paddr 0x%llx, netbuf %pK, bucket %d\n", + (unsigned long long)paddr, netbuf, (int)i)); + HTT_RX_HASH_COUNT_PRINT(pdev->rx_ring.hash_table[i]); + + qdf_spin_unlock_bh(&pdev->rx_ring.rx_hash_lock); + + if (!netbuf) { + qdf_print("rx hash: no entry found for %llx!\n", + (unsigned long long)paddr); + cds_trigger_recovery(QDF_RX_HASH_NO_ENTRY_FOUND); + } + + return netbuf; +} + +/* + * Initialization function of the rx buffer hash table. This function will + * allocate a hash table of a certain pre-determined size and initialize all + * the elements + */ +static int htt_rx_hash_init(struct htt_pdev_t *pdev) +{ + int i, j; + int rc = 0; + void *allocation; + + HTT_ASSERT2(QDF_IS_PWR2(RX_NUM_HASH_BUCKETS)); + + /* hash table is array of bucket pointers */ + pdev->rx_ring.hash_table = + qdf_mem_malloc(RX_NUM_HASH_BUCKETS * + sizeof(struct htt_rx_hash_bucket *)); + + if (!pdev->rx_ring.hash_table) + return 1; + + qdf_spinlock_create(&pdev->rx_ring.rx_hash_lock); + qdf_spin_lock_bh(&pdev->rx_ring.rx_hash_lock); + + for (i = 0; i < RX_NUM_HASH_BUCKETS; i++) { + qdf_spin_unlock_bh(&pdev->rx_ring.rx_hash_lock); + /* pre-allocate bucket and pool of entries for this bucket */ + allocation = qdf_mem_malloc((sizeof(struct htt_rx_hash_bucket) + + (RX_ENTRIES_SIZE * sizeof(struct htt_rx_hash_entry)))); + qdf_spin_lock_bh(&pdev->rx_ring.rx_hash_lock); + pdev->rx_ring.hash_table[i] = allocation; + + HTT_RX_HASH_COUNT_RESET(pdev->rx_ring.hash_table[i]); + + /* initialize the hash table buckets */ + htt_list_init(&pdev->rx_ring.hash_table[i]->listhead); + + /* initialize the hash table free pool per bucket */ + htt_list_init(&pdev->rx_ring.hash_table[i]->freepool); + + /* pre-allocate a pool of entries for this bucket */ + pdev->rx_ring.hash_table[i]->entries = + (struct htt_rx_hash_entry *) + ((uint8_t *)pdev->rx_ring.hash_table[i] + + sizeof(struct htt_rx_hash_bucket)); + + if (!pdev->rx_ring.hash_table[i]->entries) { + qdf_print("rx hash bucket %d entries alloc failed\n", + (int)i); + while (i) { + i--; + qdf_mem_free(pdev->rx_ring.hash_table[i]); + } + qdf_mem_free(pdev->rx_ring.hash_table); + pdev->rx_ring.hash_table = NULL; + rc = 1; + goto hi_end; + } + + /* initialize the free list with pre-allocated entries */ + for (j = 0; j < RX_ENTRIES_SIZE; j++) { + pdev->rx_ring.hash_table[i]->entries[j].fromlist = 1; + htt_list_add_tail( + &pdev->rx_ring.hash_table[i]->freepool, + &pdev->rx_ring.hash_table[i]->entries[j]. + listnode); + } + } + + pdev->rx_ring.listnode_offset = + qdf_offsetof(struct htt_rx_hash_entry, listnode); +hi_end: + qdf_spin_unlock_bh(&pdev->rx_ring.rx_hash_lock); + + return rc; +} + +/* De -initialization function of the rx buffer hash table. This function will + * free up the hash table which includes freeing all the pending rx buffers + */ +static void htt_rx_hash_deinit(struct htt_pdev_t *pdev) +{ + uint32_t i; + struct htt_rx_hash_entry *hash_entry; + struct htt_rx_hash_bucket **hash_table; + struct htt_list_node *list_iter = NULL; + qdf_mem_info_t mem_map_table = {0}; + bool ipa_smmu = false; + + if (!pdev->rx_ring.hash_table) + return; + + qdf_spin_lock_bh(&pdev->rx_ring.rx_hash_lock); + ipa_smmu = htt_rx_ring_smmu_mapped(pdev); + hash_table = pdev->rx_ring.hash_table; + pdev->rx_ring.hash_table = NULL; + qdf_spin_unlock_bh(&pdev->rx_ring.rx_hash_lock); + + for (i = 0; i < RX_NUM_HASH_BUCKETS; i++) { + /* Free the hash entries in hash bucket i */ + list_iter = hash_table[i]->listhead.next; + while (list_iter != &hash_table[i]->listhead) { + hash_entry = + (struct htt_rx_hash_entry *)((char *)list_iter - + pdev->rx_ring. + listnode_offset); + if (hash_entry->netbuf) { + if (ipa_smmu) { + if (qdf_unlikely( + !qdf_nbuf_is_rx_ipa_smmu_map( + hash_entry->netbuf))) { + qdf_err("nbuf: %pK NOT mapped", + hash_entry->netbuf); + qdf_assert_always(0); + } + qdf_nbuf_set_rx_ipa_smmu_map( + hash_entry->netbuf, + false); + qdf_update_mem_map_table(pdev->osdev, + &mem_map_table, + QDF_NBUF_CB_PADDR( + hash_entry->netbuf), + HTT_RX_BUF_SIZE); + + qdf_assert_always( + !cds_smmu_map_unmap( + false, 1, + &mem_map_table)); + } +#ifdef DEBUG_DMA_DONE + qdf_nbuf_unmap(pdev->osdev, hash_entry->netbuf, + QDF_DMA_BIDIRECTIONAL); +#else + qdf_nbuf_unmap(pdev->osdev, hash_entry->netbuf, + QDF_DMA_FROM_DEVICE); +#endif + qdf_nbuf_free(hash_entry->netbuf); + hash_entry->paddr = 0; + } + list_iter = list_iter->next; + + if (!hash_entry->fromlist) + qdf_mem_free(hash_entry); + } + + qdf_mem_free(hash_table[i]); + } + qdf_mem_free(hash_table); + + qdf_spinlock_destroy(&pdev->rx_ring.rx_hash_lock); +} + +int htt_rx_msdu_buff_in_order_replenish(htt_pdev_handle pdev, uint32_t num) +{ + int filled = 0; + + if (!qdf_spin_trylock_bh(&pdev->rx_ring.refill_lock)) { + if (qdf_atomic_read(&pdev->rx_ring.refill_debt) + < RX_RING_REFILL_DEBT_MAX) { + qdf_atomic_add(num, &pdev->rx_ring.refill_debt); + pdev->rx_buff_debt_invoked++; + return filled; /* 0 */ + } + /* + * else: + * If we have quite a debt, then it is better for the lock + * holder to finish its work and then acquire the lock and + * fill our own part. + */ + qdf_spin_lock_bh(&pdev->rx_ring.refill_lock); + } + pdev->rx_buff_fill_n_invoked++; + + filled = htt_rx_ring_fill_n(pdev, num); + + if (filled > num) { + /* we served ourselves and some other debt */ + /* sub is safer than = 0 */ + qdf_atomic_sub(filled - num, &pdev->rx_ring.refill_debt); + } else { + qdf_atomic_add(num - filled, &pdev->rx_ring.refill_debt); + } + qdf_spin_unlock_bh(&pdev->rx_ring.refill_lock); + + return filled; +} + +#if defined(WLAN_FEATURE_TSF_PLUS) && !defined(CONFIG_HL_SUPPORT) +/** + * htt_rx_tail_msdu_timestamp() - update tail msdu tsf64 timestamp + * @tail_rx_desc: pointer to tail msdu descriptor + * @timestamp_rx_desc: pointer to timestamp msdu descriptor + * + * Return: none + */ +static inline void htt_rx_tail_msdu_timestamp( + struct htt_host_rx_desc_base *tail_rx_desc, + struct htt_host_rx_desc_base *timestamp_rx_desc) +{ + if (tail_rx_desc) { + if (!timestamp_rx_desc) { + tail_rx_desc->ppdu_end.wb_timestamp_lower_32 = 0; + tail_rx_desc->ppdu_end.wb_timestamp_upper_32 = 0; + } else { + if (timestamp_rx_desc != tail_rx_desc) { + tail_rx_desc->ppdu_end.wb_timestamp_lower_32 = + timestamp_rx_desc->ppdu_end.wb_timestamp_lower_32; + tail_rx_desc->ppdu_end.wb_timestamp_upper_32 = + timestamp_rx_desc->ppdu_end.wb_timestamp_upper_32; + } + } + } +} +#else +static inline void htt_rx_tail_msdu_timestamp( + struct htt_host_rx_desc_base *tail_rx_desc, + struct htt_host_rx_desc_base *timestamp_rx_desc) +{ +} +#endif + +static int +htt_rx_amsdu_rx_in_order_pop_ll(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, qdf_nbuf_t *tail_msdu, + uint32_t *replenish_cnt) +{ + qdf_nbuf_t msdu, next, prev = NULL; + uint8_t *rx_ind_data; + uint32_t *msg_word; + uint32_t rx_ctx_id; + unsigned int msdu_count = 0; + uint8_t offload_ind, frag_ind; + uint8_t peer_id; + struct htt_host_rx_desc_base *rx_desc = NULL; + enum rx_pkt_fate status = RX_PKT_FATE_SUCCESS; + qdf_dma_addr_t paddr; + qdf_mem_info_t mem_map_table = {0}; + int ret = 1; + struct htt_host_rx_desc_base *timestamp_rx_desc = NULL; + + HTT_ASSERT1(htt_rx_in_order_ring_elems(pdev) != 0); + + rx_ind_data = qdf_nbuf_data(rx_ind_msg); + rx_ctx_id = QDF_NBUF_CB_RX_CTX_ID(rx_ind_msg); + msg_word = (uint32_t *)rx_ind_data; + peer_id = HTT_RX_IN_ORD_PADDR_IND_PEER_ID_GET( + *(u_int32_t *)rx_ind_data); + + offload_ind = HTT_RX_IN_ORD_PADDR_IND_OFFLOAD_GET(*msg_word); + frag_ind = HTT_RX_IN_ORD_PADDR_IND_FRAG_GET(*msg_word); + + /* Get the total number of MSDUs */ + msdu_count = HTT_RX_IN_ORD_PADDR_IND_MSDU_CNT_GET(*(msg_word + 1)); + HTT_RX_CHECK_MSDU_COUNT(msdu_count); + + ol_rx_update_histogram_stats(msdu_count, frag_ind, offload_ind); + htt_rx_dbg_rxbuf_httrxind(pdev, msdu_count); + + msg_word = + (uint32_t *)(rx_ind_data + HTT_RX_IN_ORD_PADDR_IND_HDR_BYTES); + if (offload_ind) { + ol_rx_offload_paddr_deliver_ind_handler(pdev, msdu_count, + msg_word); + *head_msdu = *tail_msdu = NULL; + ret = 0; + goto end; + } + + paddr = htt_rx_in_ord_paddr_get(msg_word); + (*head_msdu) = msdu = htt_rx_in_order_netbuf_pop(pdev, paddr); + + if (qdf_unlikely(!msdu)) { + qdf_print("netbuf pop failed!"); + *tail_msdu = NULL; + pdev->rx_ring.pop_fail_cnt++; + ret = 0; + goto end; + } + + while (msdu_count > 0) { + if (qdf_nbuf_is_rx_ipa_smmu_map(msdu)) { + /* + * nbuf was already detached from hash_entry, + * there is no parallel IPA context to access + * this nbuf for smmu map/unmap, so updating + * this flag here without lock. + * + * This flag was not updated in netbuf_pop context + * htt_rx_hash_list_lookup (where lock held), to + * differentiate whether this nbuf to be + * smmu unmapped or it was never mapped so far. + */ + qdf_nbuf_set_rx_ipa_smmu_map(msdu, false); + qdf_update_mem_map_table(pdev->osdev, &mem_map_table, + QDF_NBUF_CB_PADDR(msdu), + HTT_RX_BUF_SIZE); + qdf_assert_always( + !cds_smmu_map_unmap(false, 1, &mem_map_table)); + } + + /* + * Set the netbuf length to be the entire buffer length + * initially, so the unmap will unmap the entire buffer. + */ + qdf_nbuf_set_pktlen(msdu, HTT_RX_BUF_SIZE); +#ifdef DEBUG_DMA_DONE + qdf_nbuf_unmap(pdev->osdev, msdu, QDF_DMA_BIDIRECTIONAL); +#else + qdf_nbuf_unmap(pdev->osdev, msdu, QDF_DMA_FROM_DEVICE); +#endif + msdu_count--; + + if (pdev->rx_buff_pool.netbufs_ring && + QDF_NBUF_CB_RX_PACKET_BUFF_POOL(msdu) && + !htt_rx_buff_pool_refill(pdev, msdu)) { + if (!msdu_count) { + if (!prev) { + *head_msdu = *tail_msdu = NULL; + ret = 1; + goto end; + } + *tail_msdu = prev; + qdf_nbuf_set_next(prev, NULL); + goto end; + } else { + /* get the next msdu */ + msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS; + paddr = htt_rx_in_ord_paddr_get(msg_word); + next = htt_rx_in_order_netbuf_pop(pdev, paddr); + if (qdf_unlikely(!next)) { + qdf_print("netbuf pop failed!"); + *tail_msdu = NULL; + pdev->rx_ring.pop_fail_cnt++; + ret = 0; + goto end; + } + /* if this is not the first msdu, update the + * next pointer of the preceding msdu + */ + if (prev) { + qdf_nbuf_set_next(prev, next); + } else { + /* if this is the first msdu, update + * head pointer + */ + *head_msdu = next; + } + msdu = next; + continue; + } + } + + /* cache consistency has been taken care of by qdf_nbuf_unmap */ + rx_desc = htt_rx_desc(msdu); + htt_rx_extract_lro_info(msdu, rx_desc); + + /* check if the msdu is last mpdu */ + if (rx_desc->attention.last_mpdu) + timestamp_rx_desc = rx_desc; + + /* + * Make the netbuf's data pointer point to the payload rather + * than the descriptor. + */ + qdf_nbuf_pull_head(msdu, HTT_RX_STD_DESC_RESERVATION); + + QDF_NBUF_CB_DP_TRACE_PRINT(msdu) = false; + qdf_dp_trace_set_track(msdu, QDF_RX); + QDF_NBUF_CB_TX_PACKET_TRACK(msdu) = QDF_NBUF_TX_PKT_DATA_TRACK; + QDF_NBUF_CB_RX_CTX_ID(msdu) = rx_ctx_id; + + if (qdf_nbuf_is_ipv4_arp_pkt(msdu)) + QDF_NBUF_CB_GET_PACKET_TYPE(msdu) = + QDF_NBUF_CB_PACKET_TYPE_ARP; + + DPTRACE(qdf_dp_trace(msdu, + QDF_DP_TRACE_RX_HTT_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(msdu), + sizeof(qdf_nbuf_data(msdu)), QDF_RX)); + + qdf_nbuf_trim_tail(msdu, + HTT_RX_BUF_SIZE - + (RX_STD_DESC_SIZE + + HTT_RX_IN_ORD_PADDR_IND_MSDU_LEN_GET( + *(msg_word + NEXT_FIELD_OFFSET_IN32)))); +#if defined(HELIUMPLUS_DEBUG) + ol_txrx_dump_pkt(msdu, 0, 64); +#endif + *((uint8_t *)&rx_desc->fw_desc.u.val) = + HTT_RX_IN_ORD_PADDR_IND_FW_DESC_GET(*(msg_word + + NEXT_FIELD_OFFSET_IN32)); + + /* calling callback function for packet logging */ + if (pdev->rx_pkt_dump_cb) { + if (qdf_unlikely(RX_DESC_MIC_ERR_IS_SET && + !RX_DESC_DISCARD_IS_SET)) + status = RX_PKT_FATE_FW_DROP_INVALID; + pdev->rx_pkt_dump_cb(msdu, peer_id, status); + } + + if (pdev->cfg.is_first_wakeup_packet) + htt_get_first_packet_after_wow_wakeup( + msg_word + NEXT_FIELD_OFFSET_IN32, msdu); + + /* if discard flag is set (SA is self MAC), then + * don't check mic failure. + */ + if (qdf_unlikely(RX_DESC_MIC_ERR_IS_SET && + !RX_DESC_DISCARD_IS_SET)) { + uint8_t tid = + HTT_RX_IN_ORD_PADDR_IND_EXT_TID_GET( + *(u_int32_t *)rx_ind_data); + ol_rx_mic_error_handler(pdev->txrx_pdev, tid, peer_id, + rx_desc, msdu); + + htt_rx_desc_frame_free(pdev, msdu); + /* if this is the last msdu */ + if (!msdu_count) { + /* if this is the only msdu */ + if (!prev) { + *head_msdu = *tail_msdu = NULL; + ret = 0; + goto end; + } + *tail_msdu = prev; + qdf_nbuf_set_next(prev, NULL); + goto end; + } else { /* if this is not the last msdu */ + /* get the next msdu */ + msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS; + paddr = htt_rx_in_ord_paddr_get(msg_word); + next = htt_rx_in_order_netbuf_pop(pdev, paddr); + if (qdf_unlikely(!next)) { + qdf_print("netbuf pop failed!"); + *tail_msdu = NULL; + pdev->rx_ring.pop_fail_cnt++; + ret = 0; + goto end; + } + + /* if this is not the first msdu, update the + * next pointer of the preceding msdu + */ + if (prev) { + qdf_nbuf_set_next(prev, next); + } else { + /* if this is the first msdu, update the + * head pointer + */ + *head_msdu = next; + } + msdu = next; + continue; + } + } + /* check if this is the last msdu */ + if (msdu_count) { + msg_word += HTT_RX_IN_ORD_PADDR_IND_MSDU_DWORDS; + paddr = htt_rx_in_ord_paddr_get(msg_word); + next = htt_rx_in_order_netbuf_pop(pdev, paddr); + if (qdf_unlikely(!next)) { + qdf_print("netbuf pop failed!"); + *tail_msdu = NULL; + pdev->rx_ring.pop_fail_cnt++; + ret = 0; + goto end; + } + qdf_nbuf_set_next(msdu, next); + prev = msdu; + msdu = next; + } else { + *tail_msdu = msdu; + qdf_nbuf_set_next(msdu, NULL); + } + } + + htt_rx_tail_msdu_timestamp(rx_desc, timestamp_rx_desc); + +end: + return ret; +} + +static void *htt_rx_in_ord_mpdu_desc_list_next_ll(htt_pdev_handle pdev, + qdf_nbuf_t netbuf) +{ + return (void *)htt_rx_desc(netbuf); +} +#else + +static inline +int htt_rx_hash_init(struct htt_pdev_t *pdev) +{ + return 0; +} + +static inline +void htt_rx_hash_deinit(struct htt_pdev_t *pdev) +{ +} + +static inline int +htt_rx_amsdu_rx_in_order_pop_ll(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, qdf_nbuf_t *tail_msdu, + uint32_t *replenish_cnt) +{ + return 0; +} + +static inline +void *htt_rx_in_ord_mpdu_desc_list_next_ll(htt_pdev_handle pdev, + qdf_nbuf_t netbuf) +{ + return NULL; +} +#endif + +#ifdef WLAN_PARTIAL_REORDER_OFFLOAD + +/* AR9888v1 WORKAROUND for EV#112367 */ +/* FIX THIS - remove this WAR when the bug is fixed */ +#define PEREGRINE_1_0_ZERO_LEN_PHY_ERR_WAR + +static int +htt_rx_amsdu_pop_ll(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, qdf_nbuf_t *tail_msdu, + uint32_t *msdu_count) +{ + int msdu_len, msdu_chaining = 0; + qdf_nbuf_t msdu; + struct htt_host_rx_desc_base *rx_desc; + uint8_t *rx_ind_data; + uint32_t *msg_word, num_msdu_bytes; + qdf_dma_addr_t rx_desc_paddr; + enum htt_t2h_msg_type msg_type; + uint8_t pad_bytes = 0; + + HTT_ASSERT1(htt_rx_ring_elems(pdev) != 0); + rx_ind_data = qdf_nbuf_data(rx_ind_msg); + msg_word = (uint32_t *)rx_ind_data; + + msg_type = HTT_T2H_MSG_TYPE_GET(*msg_word); + + if (qdf_unlikely(msg_type == HTT_T2H_MSG_TYPE_RX_FRAG_IND)) { + num_msdu_bytes = HTT_RX_FRAG_IND_FW_RX_DESC_BYTES_GET( + *(msg_word + HTT_RX_FRAG_IND_HDR_PREFIX_SIZE32)); + } else { + num_msdu_bytes = HTT_RX_IND_FW_RX_DESC_BYTES_GET( + *(msg_word + + HTT_RX_IND_HDR_PREFIX_SIZE32 + + HTT_RX_PPDU_DESC_SIZE32)); + } + msdu = *head_msdu = htt_rx_netbuf_pop(pdev); + while (1) { + int last_msdu, msdu_len_invalid, msdu_chained; + int byte_offset; + qdf_nbuf_t next; + + /* + * Set the netbuf length to be the entire buffer length + * initially, so the unmap will unmap the entire buffer. + */ + qdf_nbuf_set_pktlen(msdu, HTT_RX_BUF_SIZE); +#ifdef DEBUG_DMA_DONE + qdf_nbuf_unmap(pdev->osdev, msdu, QDF_DMA_BIDIRECTIONAL); +#else + qdf_nbuf_unmap(pdev->osdev, msdu, QDF_DMA_FROM_DEVICE); +#endif + + /* cache consistency has been taken care of by qdf_nbuf_unmap */ + + /* + * Now read the rx descriptor. + * Set the length to the appropriate value. + * Check if this MSDU completes a MPDU. + */ + rx_desc = htt_rx_desc(msdu); +#if defined(HELIUMPLUS) + if (HTT_WIFI_IP(pdev, 2, 0)) + pad_bytes = rx_desc->msdu_end.l3_header_padding; +#endif /* defined(HELIUMPLUS) */ + + /* + * Save PADDR of descriptor and make the netbuf's data pointer + * point to the payload rather than the descriptor. + */ + rx_desc_paddr = QDF_NBUF_CB_PADDR(msdu); + qdf_nbuf_pull_head(msdu, HTT_RX_STD_DESC_RESERVATION + + pad_bytes); + + /* + * Sanity check - confirm the HW is finished filling in + * the rx data. + * If the HW and SW are working correctly, then it's guaranteed + * that the HW's MAC DMA is done before this point in the SW. + * To prevent the case that we handle a stale Rx descriptor, + * just assert for now until we have a way to recover. + */ + +#ifdef DEBUG_DMA_DONE + if (qdf_unlikely(!((*(uint32_t *)&rx_desc->attention) + & RX_ATTENTION_0_MSDU_DONE_MASK))) { + int dbg_iter = MAX_DONE_BIT_CHECK_ITER; + + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "malformed frame"); + + while (dbg_iter && + (!((*(uint32_t *)&rx_desc->attention) & + RX_ATTENTION_0_MSDU_DONE_MASK))) { + qdf_mdelay(1); + qdf_mem_dma_sync_single_for_cpu( + pdev->osdev, + rx_desc_paddr, + HTT_RX_STD_DESC_RESERVATION, + DMA_FROM_DEVICE); + + QDF_TRACE(QDF_MODULE_ID_HTT, + QDF_TRACE_LEVEL_INFO, + "debug iter %d success %d", dbg_iter, + pdev->rx_ring.dbg_sync_success); + + dbg_iter--; + } + + if (qdf_unlikely(!((*(uint32_t *)&rx_desc->attention) + & RX_ATTENTION_0_MSDU_DONE_MASK))) { +#ifdef HTT_RX_RESTORE + QDF_TRACE(QDF_MODULE_ID_HTT, + QDF_TRACE_LEVEL_ERROR, + "RX done bit error detected!"); + + qdf_nbuf_set_next(msdu, NULL); + *tail_msdu = msdu; + pdev->rx_ring.rx_reset = 1; + return msdu_chaining; +#else + wma_cli_set_command(0, GEN_PARAM_CRASH_INJECT, + 0, GEN_CMD); + HTT_ASSERT_ALWAYS(0); +#endif + } + pdev->rx_ring.dbg_sync_success++; + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "debug iter %d success %d", dbg_iter, + pdev->rx_ring.dbg_sync_success); + } +#else + HTT_ASSERT_ALWAYS((*(uint32_t *)&rx_desc->attention) & + RX_ATTENTION_0_MSDU_DONE_MASK); +#endif + /* + * Copy the FW rx descriptor for this MSDU from the rx + * indication message into the MSDU's netbuf. + * HL uses the same rx indication message definition as LL, and + * simply appends new info (fields from the HW rx desc, and the + * MSDU payload itself). + * So, the offset into the rx indication message only has to + * account for the standard offset of the per-MSDU FW rx + * desc info within the message, and how many bytes of the + * per-MSDU FW rx desc info have already been consumed. + * (And the endianness of the host, + * since for a big-endian host, the rx ind message contents, + * including the per-MSDU rx desc bytes, were byteswapped during + * upload.) + */ + if (pdev->rx_ind_msdu_byte_idx < num_msdu_bytes) { + if (qdf_unlikely + (msg_type == HTT_T2H_MSG_TYPE_RX_FRAG_IND)) + byte_offset = + HTT_ENDIAN_BYTE_IDX_SWAP + (HTT_RX_FRAG_IND_FW_DESC_BYTE_OFFSET); + else + byte_offset = + HTT_ENDIAN_BYTE_IDX_SWAP + (HTT_RX_IND_FW_RX_DESC_BYTE_OFFSET + + pdev->rx_ind_msdu_byte_idx); + + *((uint8_t *)&rx_desc->fw_desc.u.val) = + rx_ind_data[byte_offset]; + /* + * The target is expected to only provide the basic + * per-MSDU rx descriptors. Just to be sure, + * verify that the target has not attached + * extension data (e.g. LRO flow ID). + */ + /* + * The assertion below currently doesn't work for + * RX_FRAG_IND messages, since their format differs + * from the RX_IND format (no FW rx PPDU desc in + * the current RX_FRAG_IND message). + * If the RX_FRAG_IND message format is updated to match + * the RX_IND message format, then the following + * assertion can be restored. + */ + /* + * qdf_assert((rx_ind_data[byte_offset] & + * FW_RX_DESC_EXT_M) == 0); + */ + pdev->rx_ind_msdu_byte_idx += 1; + /* or more, if there's ext data */ + } else { + /* + * When an oversized AMSDU happened, FW will lost some + * of MSDU status - in this case, the FW descriptors + * provided will be less than the actual MSDUs + * inside this MPDU. + * Mark the FW descriptors so that it will still + * deliver to upper stack, if no CRC error for the MPDU. + * + * FIX THIS - the FW descriptors are actually for MSDUs + * in the end of this A-MSDU instead of the beginning. + */ + *((uint8_t *)&rx_desc->fw_desc.u.val) = 0; + } + + /* + * TCP/UDP checksum offload support + */ + htt_set_checksum_result_ll(pdev, msdu, rx_desc); + + msdu_len_invalid = (*(uint32_t *)&rx_desc->attention) & + RX_ATTENTION_0_MPDU_LENGTH_ERR_MASK; + msdu_chained = (((*(uint32_t *)&rx_desc->frag_info) & + RX_FRAG_INFO_0_RING2_MORE_COUNT_MASK) >> + RX_FRAG_INFO_0_RING2_MORE_COUNT_LSB); + msdu_len = + ((*((uint32_t *)&rx_desc->msdu_start)) & + RX_MSDU_START_0_MSDU_LENGTH_MASK) >> + RX_MSDU_START_0_MSDU_LENGTH_LSB; + + do { + if (!msdu_len_invalid && !msdu_chained) { +#if defined(PEREGRINE_1_0_ZERO_LEN_PHY_ERR_WAR) + if (msdu_len > 0x3000) + break; +#endif + qdf_nbuf_trim_tail(msdu, + HTT_RX_BUF_SIZE - + (RX_STD_DESC_SIZE + + msdu_len)); + } + } while (0); + + while (msdu_chained--) { + next = htt_rx_netbuf_pop(pdev); + qdf_nbuf_set_pktlen(next, HTT_RX_BUF_SIZE); + msdu_len -= HTT_RX_BUF_SIZE; + qdf_nbuf_set_next(msdu, next); + msdu = next; + msdu_chaining = 1; + + if (msdu_chained == 0) { + /* Trim the last one to the correct size - + * accounting for inconsistent HW lengths + * causing length overflows and underflows + */ + if (((unsigned int)msdu_len) > + ((unsigned int) + (HTT_RX_BUF_SIZE - RX_STD_DESC_SIZE))) { + msdu_len = + (HTT_RX_BUF_SIZE - + RX_STD_DESC_SIZE); + } + + qdf_nbuf_trim_tail(next, + HTT_RX_BUF_SIZE - + (RX_STD_DESC_SIZE + + msdu_len)); + } + } + + last_msdu = + ((*(((uint32_t *)&rx_desc->msdu_end) + 4)) & + RX_MSDU_END_4_LAST_MSDU_MASK) >> + RX_MSDU_END_4_LAST_MSDU_LSB; + + if (last_msdu) { + qdf_nbuf_set_next(msdu, NULL); + break; + } + + next = htt_rx_netbuf_pop(pdev); + qdf_nbuf_set_next(msdu, next); + msdu = next; + } + *tail_msdu = msdu; + + /* + * Don't refill the ring yet. + * First, the elements popped here are still in use - it is + * not safe to overwrite them until the matching call to + * mpdu_desc_list_next. + * Second, for efficiency it is preferable to refill the rx ring + * with 1 PPDU's worth of rx buffers (something like 32 x 3 buffers), + * rather than one MPDU's worth of rx buffers (sth like 3 buffers). + * Consequently, we'll rely on the txrx SW to tell us when it is done + * pulling all the PPDU's rx buffers out of the rx ring, and then + * refill it just once. + */ + return msdu_chaining; +} + +static +void *htt_rx_mpdu_desc_list_next_ll(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg) +{ + int idx = pdev->rx_ring.sw_rd_idx.msdu_desc; + qdf_nbuf_t netbuf = pdev->rx_ring.buf.netbufs_ring[idx]; + + pdev->rx_ring.sw_rd_idx.msdu_desc = pdev->rx_ring.sw_rd_idx.msdu_payld; + return (void *)htt_rx_desc(netbuf); +} + +#else + +static inline int +htt_rx_amsdu_pop_ll(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, qdf_nbuf_t *tail_msdu, + uint32_t *msdu_count) +{ + return 0; +} + +static inline +void *htt_rx_mpdu_desc_list_next_ll(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg) +{ + return NULL; +} +#endif + +/** + * htt_rx_fill_ring_count() - replenish rx msdu buffer + * @pdev: Handle (pointer) to HTT pdev. + * + * This funciton will replenish the rx buffer to the max number + * that can be kept in the ring + * + * Return: None + */ +void htt_rx_fill_ring_count(htt_pdev_handle pdev) +{ + int num_to_fill; + + num_to_fill = pdev->rx_ring.fill_level - pdev->rx_ring.fill_cnt; + htt_rx_ring_fill_n(pdev, num_to_fill /* okay if <= 0 */); +} + +int htt_rx_attach(struct htt_pdev_t *pdev) +{ + qdf_dma_addr_t paddr; + uint32_t ring_elem_size = sizeof(target_paddr_t); + + pdev->rx_ring.size = htt_rx_ring_size(pdev); + HTT_ASSERT2(QDF_IS_PWR2(pdev->rx_ring.size)); + pdev->rx_ring.size_mask = pdev->rx_ring.size - 1; + + /* + * Set the initial value for the level to which the rx ring + * should be filled, based on the max throughput and the worst + * likely latency for the host to fill the rx ring. + * In theory, this fill level can be dynamically adjusted from + * the initial value set here to reflect the actual host latency + * rather than a conservative assumption. + */ + pdev->rx_ring.fill_level = htt_rx_ring_fill_level(pdev); + + if (pdev->cfg.is_full_reorder_offload) { + if (htt_rx_hash_init(pdev)) + goto fail1; + + /* allocate the target index */ + pdev->rx_ring.target_idx.vaddr = + qdf_mem_alloc_consistent(pdev->osdev, pdev->osdev->dev, + sizeof(uint32_t), &paddr); + + if (!pdev->rx_ring.target_idx.vaddr) + goto fail2; + + pdev->rx_ring.target_idx.paddr = paddr; + *pdev->rx_ring.target_idx.vaddr = 0; + } else { + pdev->rx_ring.buf.netbufs_ring = + qdf_mem_malloc(pdev->rx_ring.size * sizeof(qdf_nbuf_t)); + if (!pdev->rx_ring.buf.netbufs_ring) + goto fail1; + + pdev->rx_ring.sw_rd_idx.msdu_payld = 0; + pdev->rx_ring.sw_rd_idx.msdu_desc = 0; + } + + pdev->rx_ring.buf.paddrs_ring = + qdf_mem_alloc_consistent( + pdev->osdev, pdev->osdev->dev, + pdev->rx_ring.size * ring_elem_size, + &paddr); + if (!pdev->rx_ring.buf.paddrs_ring) + goto fail3; + + pdev->rx_ring.base_paddr = paddr; + pdev->rx_ring.alloc_idx.vaddr = + qdf_mem_alloc_consistent( + pdev->osdev, pdev->osdev->dev, + sizeof(uint32_t), &paddr); + + if (!pdev->rx_ring.alloc_idx.vaddr) + goto fail4; + + pdev->rx_ring.alloc_idx.paddr = paddr; + *pdev->rx_ring.alloc_idx.vaddr = 0; + + if (htt_rx_buff_pool_init(pdev)) + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "HTT: pre allocated packet pool alloc failed"); + + /* + * Initialize the Rx refill reference counter to be one so that + * only one thread is allowed to refill the Rx ring. + */ + qdf_atomic_init(&pdev->rx_ring.refill_ref_cnt); + qdf_atomic_inc(&pdev->rx_ring.refill_ref_cnt); + + /* Initialize the refill_lock and debt (for rx-parallelization) */ + qdf_spinlock_create(&pdev->rx_ring.refill_lock); + qdf_atomic_init(&pdev->rx_ring.refill_debt); + + /* Initialize the Rx refill retry timer */ + qdf_timer_init(pdev->osdev, + &pdev->rx_ring.refill_retry_timer, + htt_rx_ring_refill_retry, (void *)pdev, + QDF_TIMER_TYPE_SW); + + pdev->rx_ring.fill_cnt = 0; + pdev->rx_ring.pop_fail_cnt = 0; +#ifdef DEBUG_DMA_DONE + pdev->rx_ring.dbg_ring_idx = 0; + pdev->rx_ring.dbg_refill_cnt = 0; + pdev->rx_ring.dbg_sync_success = 0; +#endif +#ifdef HTT_RX_RESTORE + pdev->rx_ring.rx_reset = 0; + pdev->rx_ring.htt_rx_restore = 0; +#endif + htt_rx_dbg_rxbuf_init(pdev); + htt_rx_ring_fill_n(pdev, pdev->rx_ring.fill_level); + + if (pdev->cfg.is_full_reorder_offload) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "HTT: full reorder offload enabled"); + htt_rx_amsdu_pop = htt_rx_amsdu_rx_in_order_pop_ll; + htt_rx_frag_pop = htt_rx_amsdu_rx_in_order_pop_ll; + htt_rx_mpdu_desc_list_next = + htt_rx_in_ord_mpdu_desc_list_next_ll; + } else { + htt_rx_amsdu_pop = htt_rx_amsdu_pop_ll; + htt_rx_frag_pop = htt_rx_amsdu_pop_ll; + htt_rx_mpdu_desc_list_next = htt_rx_mpdu_desc_list_next_ll; + } + + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + htt_rx_amsdu_pop = htt_rx_mon_amsdu_rx_in_order_pop_ll; + + htt_rx_offload_msdu_cnt = htt_rx_offload_msdu_cnt_ll; + htt_rx_offload_msdu_pop = htt_rx_offload_msdu_pop_ll; + htt_rx_mpdu_desc_retry = htt_rx_mpdu_desc_retry_ll; + htt_rx_mpdu_desc_seq_num = htt_rx_mpdu_desc_seq_num_ll; + htt_rx_mpdu_desc_pn = htt_rx_mpdu_desc_pn_ll; + htt_rx_mpdu_desc_tid = htt_rx_mpdu_desc_tid_ll; + htt_rx_msdu_desc_completes_mpdu = htt_rx_msdu_desc_completes_mpdu_ll; + htt_rx_msdu_first_msdu_flag = htt_rx_msdu_first_msdu_flag_ll; + htt_rx_msdu_has_wlan_mcast_flag = htt_rx_msdu_has_wlan_mcast_flag_ll; + htt_rx_msdu_is_wlan_mcast = htt_rx_msdu_is_wlan_mcast_ll; + htt_rx_msdu_is_frag = htt_rx_msdu_is_frag_ll; + htt_rx_msdu_desc_retrieve = htt_rx_msdu_desc_retrieve_ll; + htt_rx_mpdu_is_encrypted = htt_rx_mpdu_is_encrypted_ll; + htt_rx_msdu_desc_key_id = htt_rx_msdu_desc_key_id_ll; + htt_rx_msdu_chan_info_present = htt_rx_msdu_chan_info_present_ll; + htt_rx_msdu_center_freq = htt_rx_msdu_center_freq_ll; + + return 0; /* success */ + +fail4: + qdf_mem_free_consistent(pdev->osdev, pdev->osdev->dev, + pdev->rx_ring.size * sizeof(target_paddr_t), + pdev->rx_ring.buf.paddrs_ring, + pdev->rx_ring.base_paddr, + qdf_get_dma_mem_context((&pdev->rx_ring.buf), + memctx)); + +fail3: + if (pdev->cfg.is_full_reorder_offload) + qdf_mem_free_consistent(pdev->osdev, pdev->osdev->dev, + sizeof(uint32_t), + pdev->rx_ring.target_idx.vaddr, + pdev->rx_ring.target_idx.paddr, + qdf_get_dma_mem_context((&pdev-> + rx_ring. + target_idx), + memctx)); + else + qdf_mem_free(pdev->rx_ring.buf.netbufs_ring); + +fail2: + if (pdev->cfg.is_full_reorder_offload) + htt_rx_hash_deinit(pdev); + +fail1: + return 1; /* failure */ +} + +void htt_rx_detach(struct htt_pdev_t *pdev) +{ + bool ipa_smmu = false; + qdf_nbuf_t nbuf; + + qdf_timer_stop(&pdev->rx_ring.refill_retry_timer); + qdf_timer_free(&pdev->rx_ring.refill_retry_timer); + htt_rx_dbg_rxbuf_deinit(pdev); + + ipa_smmu = htt_rx_ring_smmu_mapped(pdev); + + if (pdev->cfg.is_full_reorder_offload) { + qdf_mem_free_consistent(pdev->osdev, pdev->osdev->dev, + sizeof(uint32_t), + pdev->rx_ring.target_idx.vaddr, + pdev->rx_ring.target_idx.paddr, + qdf_get_dma_mem_context((&pdev-> + rx_ring. + target_idx), + memctx)); + htt_rx_hash_deinit(pdev); + } else { + int sw_rd_idx = pdev->rx_ring.sw_rd_idx.msdu_payld; + qdf_mem_info_t mem_map_table = {0}; + + while (sw_rd_idx != *pdev->rx_ring.alloc_idx.vaddr) { + nbuf = pdev->rx_ring.buf.netbufs_ring[sw_rd_idx]; + if (ipa_smmu) { + if (qdf_unlikely( + !qdf_nbuf_is_rx_ipa_smmu_map(nbuf))) { + qdf_err("smmu not mapped, nbuf: %pK", + nbuf); + qdf_assert_always(0); + } + qdf_nbuf_set_rx_ipa_smmu_map(nbuf, false); + qdf_update_mem_map_table(pdev->osdev, + &mem_map_table, + QDF_NBUF_CB_PADDR(nbuf), + HTT_RX_BUF_SIZE); + qdf_assert_always( + !cds_smmu_map_unmap(false, 1, + &mem_map_table)); + } +#ifdef DEBUG_DMA_DONE + qdf_nbuf_unmap(pdev->osdev, nbuf, + QDF_DMA_BIDIRECTIONAL); +#else + qdf_nbuf_unmap(pdev->osdev, nbuf, + QDF_DMA_FROM_DEVICE); +#endif + qdf_nbuf_free(nbuf); + sw_rd_idx++; + sw_rd_idx &= pdev->rx_ring.size_mask; + } + qdf_mem_free(pdev->rx_ring.buf.netbufs_ring); + } + + htt_rx_buff_pool_deinit(pdev); + + qdf_mem_free_consistent(pdev->osdev, pdev->osdev->dev, + sizeof(uint32_t), + pdev->rx_ring.alloc_idx.vaddr, + pdev->rx_ring.alloc_idx.paddr, + qdf_get_dma_mem_context((&pdev->rx_ring. + alloc_idx), + memctx)); + + qdf_mem_free_consistent(pdev->osdev, pdev->osdev->dev, + pdev->rx_ring.size * sizeof(target_paddr_t), + pdev->rx_ring.buf.paddrs_ring, + pdev->rx_ring.base_paddr, + qdf_get_dma_mem_context((&pdev->rx_ring.buf), + memctx)); + + /* destroy the rx-parallelization refill spinlock */ + qdf_spinlock_destroy(&pdev->rx_ring.refill_lock); +} + +static QDF_STATUS htt_rx_hash_smmu_map(bool map, struct htt_pdev_t *pdev) +{ + uint32_t i; + struct htt_rx_hash_entry *hash_entry; + struct htt_rx_hash_bucket **hash_table; + struct htt_list_node *list_iter = NULL; + qdf_mem_info_t mem_map_table = {0}; + qdf_nbuf_t nbuf; + int ret; + + qdf_spin_lock_bh(&pdev->rx_ring.rx_hash_lock); + hash_table = pdev->rx_ring.hash_table; + + for (i = 0; i < RX_NUM_HASH_BUCKETS; i++) { + /* Free the hash entries in hash bucket i */ + list_iter = hash_table[i]->listhead.next; + while (list_iter != &hash_table[i]->listhead) { + hash_entry = + (struct htt_rx_hash_entry *)((char *)list_iter - + pdev->rx_ring. + listnode_offset); + nbuf = hash_entry->netbuf; + if (nbuf) { + if (qdf_unlikely(map == + qdf_nbuf_is_rx_ipa_smmu_map(nbuf))) { + qdf_err("map/unmap err:%d, nbuf:%pK", + map, nbuf); + list_iter = list_iter->next; + continue; + } + qdf_nbuf_set_rx_ipa_smmu_map(nbuf, map); + qdf_update_mem_map_table(pdev->osdev, + &mem_map_table, + QDF_NBUF_CB_PADDR(nbuf), + HTT_RX_BUF_SIZE); + ret = cds_smmu_map_unmap(map, 1, + &mem_map_table); + if (ret) { + qdf_nbuf_set_rx_ipa_smmu_map(nbuf, + !map); + qdf_err("map: %d failure, nbuf: %pK", + map, nbuf); + qdf_spin_unlock_bh( + &pdev->rx_ring.rx_hash_lock); + return QDF_STATUS_E_FAILURE; + } + } + list_iter = list_iter->next; + } + } + + pdev->rx_ring.smmu_map = map; + qdf_spin_unlock_bh(&pdev->rx_ring.rx_hash_lock); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS htt_rx_update_smmu_map(struct htt_pdev_t *pdev, bool map) +{ + QDF_STATUS status; + + if (!pdev->rx_ring.hash_table) + return QDF_STATUS_SUCCESS; + + if (!qdf_mem_smmu_s1_enabled(pdev->osdev) || !pdev->is_ipa_uc_enabled) + return QDF_STATUS_SUCCESS; + + qdf_spin_lock_bh(&pdev->rx_ring.refill_lock); + status = htt_rx_hash_smmu_map(map, pdev); + qdf_spin_unlock_bh(&pdev->rx_ring.refill_lock); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_t2h.c b/drivers/staging/qcacld-3.0/core/dp/htt/htt_t2h.c new file mode 100644 index 0000000000000000000000000000000000000000..b792a85eb5956f6b8bf60b9e4922a4e13edec227 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_t2h.c @@ -0,0 +1,1691 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file htt_t2h.c + * @brief Provide functions to process target->host HTT messages. + * @details + * This file contains functions related to target->host HTT messages. + * There are two categories of functions: + * 1. A function that receives a HTT message from HTC, and dispatches it + * based on the HTT message type. + * 2. functions that provide the info elements from specific HTT messages. + */ +#include +#include /* HTC_PACKET */ +#include /* HTT_T2H_MSG_TYPE, etc. */ +#include /* qdf_nbuf_t */ + +#include +#include +#include +#include /* htt_tx_status */ + +#include /* HTT_TX_SCHED, etc. */ +#include +#include +#include +#include +#include +#include "pktlog_ac.h" +#include +/*--- target->host HTT message dispatch function ----------------------------*/ + +#ifndef DEBUG_CREDIT +#define DEBUG_CREDIT 0 +#endif + +#if defined(CONFIG_HL_SUPPORT) + + + +/** + * htt_rx_frag_set_last_msdu() - set last msdu bit in rx descriptor + * for received frames + * @pdev: Handle (pointer) to HTT pdev. + * @msg: htt received msg + * + * Return: None + */ +static inline +void htt_rx_frag_set_last_msdu(struct htt_pdev_t *pdev, qdf_nbuf_t msg) +{ +} +#else + +static void htt_rx_frag_set_last_msdu(struct htt_pdev_t *pdev, qdf_nbuf_t msg) +{ + uint32_t *msg_word; + unsigned int num_msdu_bytes; + qdf_nbuf_t msdu; + struct htt_host_rx_desc_base *rx_desc; + int start_idx; + uint8_t *p_fw_msdu_rx_desc = 0; + + msg_word = (uint32_t *) qdf_nbuf_data(msg); + num_msdu_bytes = HTT_RX_FRAG_IND_FW_RX_DESC_BYTES_GET( + *(msg_word + HTT_RX_FRAG_IND_HDR_PREFIX_SIZE32)); + /* + * 1 word for the message header, + * 1 word to specify the number of MSDU bytes, + * 1 word for every 4 MSDU bytes (round up), + * 1 word for the MPDU range header + */ + pdev->rx_mpdu_range_offset_words = 3 + ((num_msdu_bytes + 3) >> 2); + pdev->rx_ind_msdu_byte_idx = 0; + + p_fw_msdu_rx_desc = ((uint8_t *) (msg_word) + + HTT_ENDIAN_BYTE_IDX_SWAP + (HTT_RX_FRAG_IND_FW_DESC_BYTE_OFFSET)); + + /* + * Fix for EV126710, in which BSOD occurs due to last_msdu bit + * not set while the next pointer is deliberately set to NULL + * before calling ol_rx_pn_check_base() + * + * For fragment frames, the HW may not have set the last_msdu bit + * in the rx descriptor, but the SW expects this flag to be set, + * since each fragment is in a separate MPDU. Thus, set the flag here, + * just in case the HW didn't. + */ + start_idx = pdev->rx_ring.sw_rd_idx.msdu_payld; + msdu = pdev->rx_ring.buf.netbufs_ring[start_idx]; + qdf_nbuf_set_pktlen(msdu, HTT_RX_BUF_SIZE); + qdf_nbuf_unmap(pdev->osdev, msdu, QDF_DMA_FROM_DEVICE); + rx_desc = htt_rx_desc(msdu); + *((uint8_t *) &rx_desc->fw_desc.u.val) = *p_fw_msdu_rx_desc; + rx_desc->msdu_end.last_msdu = 1; + qdf_nbuf_map(pdev->osdev, msdu, QDF_DMA_FROM_DEVICE); +} +#endif + +static uint8_t *htt_t2h_mac_addr_deswizzle(uint8_t *tgt_mac_addr, + uint8_t *buffer) +{ +#ifdef BIG_ENDIAN_HOST + /* + * The host endianness is opposite of the target endianness. + * To make uint32_t elements come out correctly, the target->host + * upload has swizzled the bytes in each uint32_t element of the + * message. + * For byte-array message fields like the MAC address, this + * upload swizzling puts the bytes in the wrong order, and needs + * to be undone. + */ + buffer[0] = tgt_mac_addr[3]; + buffer[1] = tgt_mac_addr[2]; + buffer[2] = tgt_mac_addr[1]; + buffer[3] = tgt_mac_addr[0]; + buffer[4] = tgt_mac_addr[7]; + buffer[5] = tgt_mac_addr[6]; + return buffer; +#else + /* + * The host endianness matches the target endianness - + * we can use the mac addr directly from the message buffer. + */ + return tgt_mac_addr; +#endif +} + +/** + * htt_ipa_op_response() - invoke an event handler from FW + * @pdev: Handle (pointer) to HTT pdev. + * @msg_word: htt msg + * + * Return: None + */ +#ifdef IPA_OFFLOAD +static void htt_ipa_op_response(struct htt_pdev_t *pdev, uint32_t *msg_word) +{ + uint8_t op_code; + uint16_t len; + uint8_t *op_msg_buffer; + uint8_t *msg_start_ptr; + + htc_pm_runtime_put(pdev->htc_pdev); + msg_start_ptr = (uint8_t *) msg_word; + op_code = + HTT_WDI_IPA_OP_RESPONSE_OP_CODE_GET(*msg_word); + msg_word++; + len = HTT_WDI_IPA_OP_RESPONSE_RSP_LEN_GET(*msg_word); + + op_msg_buffer = + qdf_mem_malloc(sizeof + (struct htt_wdi_ipa_op_response_t) + + len); + if (!op_msg_buffer) + return; + + qdf_mem_copy(op_msg_buffer, + msg_start_ptr, + sizeof(struct htt_wdi_ipa_op_response_t) + + len); + cdp_ipa_op_response(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID, op_msg_buffer); +} +#else +static void htt_ipa_op_response(struct htt_pdev_t *pdev, uint32_t *msg_word) +{ +} +#endif + +#ifndef QCN7605_SUPPORT +static int htt_t2h_adjust_bus_target_delta(struct htt_pdev_t *pdev, + int32_t htt_credit_delta) +{ + if (pdev->cfg.is_high_latency && !pdev->cfg.default_tx_comp_req) { + HTT_TX_MUTEX_ACQUIRE(&pdev->credit_mutex); + qdf_atomic_add(htt_credit_delta, + &pdev->htt_tx_credit.target_delta); + htt_credit_delta = htt_tx_credit_update(pdev); + HTT_TX_MUTEX_RELEASE(&pdev->credit_mutex); + } + return htt_credit_delta; +} +#else +static int htt_t2h_adjust_bus_target_delta(struct htt_pdev_t *pdev, + int32_t htt_credit_delta) +{ + return htt_credit_delta; +} +#endif + +#define MAX_TARGET_TX_CREDIT 204800 + +/* Target to host Msg/event handler for low priority messages*/ +static void htt_t2h_lp_msg_handler(void *context, qdf_nbuf_t htt_t2h_msg, + bool free_msg_buf) +{ + struct htt_pdev_t *pdev = (struct htt_pdev_t *)context; + uint32_t *msg_word; + enum htt_t2h_msg_type msg_type; + + msg_word = (uint32_t *) qdf_nbuf_data(htt_t2h_msg); + msg_type = HTT_T2H_MSG_TYPE_GET(*msg_word); + switch (msg_type) { + case HTT_T2H_MSG_TYPE_VERSION_CONF: + { + htc_pm_runtime_put(pdev->htc_pdev); + pdev->tgt_ver.major = HTT_VER_CONF_MAJOR_GET(*msg_word); + pdev->tgt_ver.minor = HTT_VER_CONF_MINOR_GET(*msg_word); + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "target uses HTT version %d.%d; host uses %d.%d", + pdev->tgt_ver.major, pdev->tgt_ver.minor, + HTT_CURRENT_VERSION_MAJOR, + HTT_CURRENT_VERSION_MINOR); + if (pdev->tgt_ver.major != HTT_CURRENT_VERSION_MAJOR) + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_WARN, + "*** Incompatible host/target HTT versions!"); + /* abort if the target is incompatible with the host */ + qdf_assert(pdev->tgt_ver.major == + HTT_CURRENT_VERSION_MAJOR); + if (pdev->tgt_ver.minor != HTT_CURRENT_VERSION_MINOR) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "*** Warning: host/target HTT versions are "); + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO_LOW, + "different, though compatible!"); + } + break; + } + case HTT_T2H_MSG_TYPE_RX_FLUSH: + { + uint16_t peer_id; + uint8_t tid; + uint16_t seq_num_start, seq_num_end; + enum htt_rx_flush_action action; + + if (qdf_nbuf_len(htt_t2h_msg) < HTT_RX_FLUSH_BYTES) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + + peer_id = HTT_RX_FLUSH_PEER_ID_GET(*msg_word); + tid = HTT_RX_FLUSH_TID_GET(*msg_word); + seq_num_start = + HTT_RX_FLUSH_SEQ_NUM_START_GET(*(msg_word + 1)); + seq_num_end = + HTT_RX_FLUSH_SEQ_NUM_END_GET(*(msg_word + 1)); + action = + HTT_RX_FLUSH_MPDU_STATUS_GET(*(msg_word + 1)) == + 1 ? htt_rx_flush_release : htt_rx_flush_discard; + ol_rx_flush_handler(pdev->txrx_pdev, peer_id, tid, + seq_num_start, seq_num_end, action); + break; + } + case HTT_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND: + { + uint16_t msdu_cnt; + + if (!pdev->cfg.is_high_latency && + pdev->cfg.is_full_reorder_offload) { + qdf_print("HTT_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND not "); + qdf_print("supported when full reorder offload is "); + qdf_print("enabled in the configuration.\n"); + break; + } + msdu_cnt = + HTT_RX_OFFLOAD_DELIVER_IND_MSDU_CNT_GET(*msg_word); + ol_rx_offload_deliver_ind_handler(pdev->txrx_pdev, + htt_t2h_msg, + msdu_cnt); + if (pdev->cfg.is_high_latency) { + /* + * return here for HL to avoid double free on + * htt_t2h_msg + */ + return; + } + break; + } + case HTT_T2H_MSG_TYPE_RX_FRAG_IND: + { + uint16_t peer_id; + uint8_t tid; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + if (msg_len < HTT_RX_FRAG_IND_BYTES) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + peer_id = HTT_RX_FRAG_IND_PEER_ID_GET(*msg_word); + tid = HTT_RX_FRAG_IND_EXT_TID_GET(*msg_word); + htt_rx_frag_set_last_msdu(pdev, htt_t2h_msg); + + /* If packet len is invalid, will discard this frame. */ + if (pdev->cfg.is_high_latency) { + u_int32_t rx_pkt_len = 0; + + rx_pkt_len = qdf_nbuf_len(htt_t2h_msg); + + if (rx_pkt_len < (HTT_RX_FRAG_IND_BYTES + + sizeof(struct hl_htt_rx_ind_base)+ + sizeof(struct ieee80211_frame))) { + + qdf_print("invalid packet len, %u", rx_pkt_len); + /* + * This buf will be freed before + * exiting this function. + */ + break; + } + } + + ol_rx_frag_indication_handler(pdev->txrx_pdev, + htt_t2h_msg, + peer_id, tid); + + if (pdev->cfg.is_high_latency) { + /* + * For high latency solution, + * HTT_T2H_MSG_TYPE_RX_FRAG_IND message and RX packet + * share the same buffer. All buffer will be freed by + * ol_rx_frag_indication_handler or upper layer to + * avoid double free issue. + * + */ + return; + } + + break; + } + case HTT_T2H_MSG_TYPE_RX_ADDBA: + { + uint16_t peer_id; + uint8_t tid; + uint8_t win_sz; + uint16_t start_seq_num; + + /* + * FOR NOW, the host doesn't need to know the initial + * sequence number for rx aggregation. + * Thus, any value will do - specify 0. + */ + start_seq_num = 0; + peer_id = HTT_RX_ADDBA_PEER_ID_GET(*msg_word); + tid = HTT_RX_ADDBA_TID_GET(*msg_word); + win_sz = HTT_RX_ADDBA_WIN_SIZE_GET(*msg_word); + ol_rx_addba_handler(pdev->txrx_pdev, peer_id, tid, + win_sz, start_seq_num, + 0 /* success */); + break; + } + case HTT_T2H_MSG_TYPE_RX_DELBA: + { + uint16_t peer_id; + uint8_t tid; + + peer_id = HTT_RX_DELBA_PEER_ID_GET(*msg_word); + tid = HTT_RX_DELBA_TID_GET(*msg_word); + ol_rx_delba_handler(pdev->txrx_pdev, peer_id, tid); + break; + } + case HTT_T2H_MSG_TYPE_PEER_MAP: + { + uint8_t mac_addr_deswizzle_buf[QDF_MAC_ADDR_SIZE]; + uint8_t *peer_mac_addr; + uint16_t peer_id; + uint8_t vdev_id; + + if (qdf_nbuf_len(htt_t2h_msg) < HTT_RX_PEER_MAP_BYTES) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + + peer_id = HTT_RX_PEER_MAP_PEER_ID_GET(*msg_word); + vdev_id = HTT_RX_PEER_MAP_VDEV_ID_GET(*msg_word); + peer_mac_addr = htt_t2h_mac_addr_deswizzle( + (uint8_t *) (msg_word + 1), + &mac_addr_deswizzle_buf[0]); + + if (peer_id > ol_cfg_max_peer_id(pdev->ctrl_pdev)) { + qdf_print("%s: HTT_T2H_MSG_TYPE_PEER_MAP," + "invalid peer_id, %u\n", + __FUNCTION__, + peer_id); + break; + } + + ol_rx_peer_map_handler(pdev->txrx_pdev, peer_id, + vdev_id, peer_mac_addr, + 1 /*can tx */); + break; + } + case HTT_T2H_MSG_TYPE_PEER_UNMAP: + { + uint16_t peer_id; + + if (qdf_nbuf_len(htt_t2h_msg) < HTT_RX_PEER_UNMAP_BYTES) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + + peer_id = HTT_RX_PEER_UNMAP_PEER_ID_GET(*msg_word); + if (peer_id > ol_cfg_max_peer_id(pdev->ctrl_pdev)) { + qdf_print("%s: HTT_T2H_MSG_TYPE_PEER_UNMAP," + "invalid peer_id, %u\n", + __FUNCTION__, + peer_id); + break; + } + + ol_rx_peer_unmap_handler(pdev->txrx_pdev, peer_id); + break; + } + case HTT_T2H_MSG_TYPE_SEC_IND: + { + uint16_t peer_id; + enum htt_sec_type sec_type; + int is_unicast; + + if (qdf_nbuf_len(htt_t2h_msg) < HTT_SEC_IND_BYTES) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + + peer_id = HTT_SEC_IND_PEER_ID_GET(*msg_word); + sec_type = HTT_SEC_IND_SEC_TYPE_GET(*msg_word); + is_unicast = HTT_SEC_IND_UNICAST_GET(*msg_word); + msg_word++; /* point to the first part of the Michael key */ + ol_rx_sec_ind_handler(pdev->txrx_pdev, peer_id, + sec_type, is_unicast, msg_word, + msg_word + 2); + break; + } + case HTT_T2H_MSG_TYPE_MGMT_TX_COMPL_IND: + { + struct htt_mgmt_tx_compl_ind *compl_msg; + int32_t credit_delta = 1; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + if (msg_len < (sizeof(struct htt_mgmt_tx_compl_ind) + sizeof(*msg_word))) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "Invalid msg_word length in HTT_T2H_MSG_TYPE_MGMT_TX_COMPL_IND"); + WARN_ON(1); + break; + } + + compl_msg = + (struct htt_mgmt_tx_compl_ind *)(msg_word + 1); + + if (pdev->cfg.is_high_latency) { + if (!pdev->cfg.default_tx_comp_req) { + HTT_TX_MUTEX_ACQUIRE(&pdev->credit_mutex); + qdf_atomic_add(credit_delta, + &pdev->htt_tx_credit. + target_delta); + credit_delta = htt_tx_credit_update(pdev); + HTT_TX_MUTEX_RELEASE(&pdev->credit_mutex); + } + if (credit_delta) + ol_tx_target_credit_update( + pdev->txrx_pdev, credit_delta); + } + ol_tx_desc_update_group_credit( + pdev->txrx_pdev, compl_msg->desc_id, 1, + 0, compl_msg->status); + + DPTRACE(qdf_dp_trace_credit_record(QDF_TX_COMP, QDF_CREDIT_INC, + 1, qdf_atomic_read(&pdev->txrx_pdev->target_tx_credit), + qdf_atomic_read(&pdev->txrx_pdev->txq_grps[0].credit), + qdf_atomic_read(&pdev->txrx_pdev->txq_grps[1].credit))); + + if (!ol_tx_get_is_mgmt_over_wmi_enabled()) { + ol_tx_single_completion_handler(pdev->txrx_pdev, + compl_msg->status, + compl_msg->desc_id); + htc_pm_runtime_put(pdev->htc_pdev); + HTT_TX_SCHED(pdev); + } else { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "Ignoring HTT_T2H_MSG_TYPE_MGMT_TX_COMPL_IND indication"); + } + break; + } + case HTT_T2H_MSG_TYPE_STATS_CONF: + { + uint8_t cookie; + uint8_t *stats_info_list; + + cookie = *(msg_word + 1); + + stats_info_list = (uint8_t *) (msg_word + 3); + htc_pm_runtime_put(pdev->htc_pdev); + ol_txrx_fw_stats_handler(pdev->txrx_pdev, cookie, + stats_info_list); + break; + } +#ifndef REMOVE_PKT_LOG + case HTT_T2H_MSG_TYPE_PKTLOG: + { + uint32_t len = qdf_nbuf_len(htt_t2h_msg); + + if (len < sizeof(*msg_word) + sizeof(uint32_t)) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + + /*len is reduced by sizeof(*msg_word)*/ + pktlog_process_fw_msg(OL_TXRX_PDEV_ID, msg_word + 1, + len - sizeof(*msg_word)); + break; + } +#endif + case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND: + { + uint32_t htt_credit_delta_abs; + int32_t htt_credit_delta; + int sign, old_credit; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + if (msg_len < HTT_TX_CREDIT_MSG_BYTES) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + + htt_credit_delta_abs = + HTT_TX_CREDIT_DELTA_ABS_GET(*msg_word); + sign = HTT_TX_CREDIT_SIGN_BIT_GET(*msg_word) ? -1 : 1; + htt_credit_delta = sign * htt_credit_delta_abs; + + old_credit = qdf_atomic_read(&pdev->htt_tx_credit.target_delta); + if (((old_credit + htt_credit_delta) > MAX_TARGET_TX_CREDIT) || + ((old_credit + htt_credit_delta) < -MAX_TARGET_TX_CREDIT)) { + qdf_err("invalid update,old_credit=%d, htt_credit_delta=%d", + old_credit, htt_credit_delta); + break; + } + htt_credit_delta = + htt_t2h_adjust_bus_target_delta(pdev, htt_credit_delta); + htt_tx_group_credit_process(pdev, msg_word); + DPTRACE(qdf_dp_trace_credit_record(QDF_TX_CREDIT_UPDATE, + QDF_CREDIT_INC, htt_credit_delta, + qdf_atomic_read(&pdev->txrx_pdev->target_tx_credit) + + htt_credit_delta, + qdf_atomic_read(&pdev->txrx_pdev->txq_grps[0].credit), + qdf_atomic_read(&pdev->txrx_pdev->txq_grps[1].credit))); + + ol_tx_credit_completion_handler(pdev->txrx_pdev, + htt_credit_delta); + break; + } + + case HTT_T2H_MSG_TYPE_WDI_IPA_OP_RESPONSE: + { + uint16_t len; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + len = HTT_WDI_IPA_OP_RESPONSE_RSP_LEN_GET(*(msg_word + 1)); + + if (sizeof(struct htt_wdi_ipa_op_response_t) + len > msg_len) { + qdf_print("Invalid buf len size %zu len %d, msg_len %d", + sizeof(struct htt_wdi_ipa_op_response_t), + len, msg_len); + WARN_ON(1); + break; + } + htt_ipa_op_response(pdev, msg_word); + break; + } + + case HTT_T2H_MSG_TYPE_FLOW_POOL_MAP: + { + uint8_t num_flows; + struct htt_flow_pool_map_payload_t *pool_map_payoad; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + num_flows = HTT_FLOW_POOL_MAP_NUM_FLOWS_GET(*msg_word); + + if (((HTT_FLOW_POOL_MAP_PAYLOAD_SZ / + HTT_FLOW_POOL_MAP_HEADER_SZ) * num_flows + 1) * sizeof(*msg_word) > msg_len) { + qdf_print("Invalid num_flows"); + WARN_ON(1); + break; + } + + msg_word++; + while (num_flows) { + pool_map_payoad = (struct htt_flow_pool_map_payload_t *) + msg_word; + ol_tx_flow_pool_map_handler(pool_map_payoad->flow_id, + pool_map_payoad->flow_type, + pool_map_payoad->flow_pool_id, + pool_map_payoad->flow_pool_size); + + msg_word += (HTT_FLOW_POOL_MAP_PAYLOAD_SZ / + HTT_FLOW_POOL_MAP_HEADER_SZ); + num_flows--; + } + break; + } + + case HTT_T2H_MSG_TYPE_FLOW_POOL_UNMAP: + { + struct htt_flow_pool_unmap_t *pool_numap_payload; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + if (msg_len < sizeof(struct htt_flow_pool_unmap_t)) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "Invalid msg_word length in HTT_T2H_MSG_TYPE_FLOW_POOL_UNMAP"); + WARN_ON(1); + break; + } + + pool_numap_payload = (struct htt_flow_pool_unmap_t *)msg_word; + ol_tx_flow_pool_unmap_handler(pool_numap_payload->flow_id, + pool_numap_payload->flow_type, + pool_numap_payload->flow_pool_id); + break; + } + + case HTT_T2H_MSG_TYPE_FLOW_POOL_RESIZE: + { + struct htt_flow_pool_resize_t *msg; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + if (msg_len < sizeof(struct htt_flow_pool_resize_t)) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_ERROR, + "Invalid msg_word length in HTT_T2H_MSG_TYPE_FLOW_POOL_RESIZE"); + WARN_ON(1); + break; + } + + msg = (struct htt_flow_pool_resize_t *)msg_word; + ol_tx_flow_pool_resize_handler(msg->flow_pool_id, + msg->flow_pool_new_size); + + break; + } + + case HTT_T2H_MSG_TYPE_RX_OFLD_PKT_ERR: + { + switch (HTT_RX_OFLD_PKT_ERR_MSG_SUB_TYPE_GET(*msg_word)) { + case HTT_RX_OFLD_PKT_ERR_TYPE_MIC_ERR: + { + struct ol_txrx_vdev_t *vdev; + struct ol_txrx_peer_t *peer; + uint64_t pn; + uint32_t key_id; + uint16_t peer_id; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + if (msg_len < HTT_RX_OFLD_PKT_ERR_MIC_ERR_BYTES) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + + peer_id = HTT_RX_OFLD_PKT_ERR_MIC_ERR_PEER_ID_GET + (*(msg_word + 1)); + + peer = ol_txrx_peer_find_by_id(pdev->txrx_pdev, + peer_id); + if (!peer) { + qdf_print("invalid peer id %d", peer_id); + qdf_assert(0); + break; + } + vdev = peer->vdev; + key_id = HTT_RX_OFLD_PKT_ERR_MIC_ERR_KEYID_GET + (*(msg_word + 1)); + qdf_mem_copy(&pn, (uint8_t *)(msg_word + 6), 6); + + ol_rx_send_mic_err_ind(vdev->pdev, vdev->vdev_id, + peer->mac_addr.raw, 0, 0, + OL_RX_ERR_TKIP_MIC, htt_t2h_msg, + &pn, key_id); + break; + } + default: + { + qdf_print("unhandled error type %d", + HTT_RX_OFLD_PKT_ERR_MSG_SUB_TYPE_GET(*msg_word)); + break; + } + } + } + + default: + break; + }; + /* Free the indication buffer */ + if (free_msg_buf) + qdf_nbuf_free(htt_t2h_msg); +} + +#define HTT_TX_COMPL_HEAD_SZ 4 +#define HTT_TX_COMPL_BYTES_PER_MSDU_ID 2 + +/** + * Generic Target to host Msg/event handler for low priority messages + * Low priority message are handler in a different handler called from + * this function . So that the most likely succes path like Rx and + * Tx comp has little code foot print + */ +void htt_t2h_msg_handler(void *context, HTC_PACKET *pkt) +{ + struct htt_pdev_t *pdev = (struct htt_pdev_t *)context; + qdf_nbuf_t htt_t2h_msg = (qdf_nbuf_t) pkt->pPktContext; + uint32_t *msg_word; + enum htt_t2h_msg_type msg_type; + + /* check for successful message reception */ + if (pkt->Status != QDF_STATUS_SUCCESS) { + if (pkt->Status != QDF_STATUS_E_CANCELED) + pdev->stats.htc_err_cnt++; + qdf_nbuf_free(htt_t2h_msg); + return; + } +#ifdef HTT_RX_RESTORE + if (qdf_unlikely(pdev->rx_ring.rx_reset)) { + qdf_print("rx restore ..\n"); + qdf_nbuf_free(htt_t2h_msg); + return; + } +#endif + + /* confirm alignment */ + HTT_ASSERT3((((unsigned long)qdf_nbuf_data(htt_t2h_msg)) & 0x3) == 0); + + msg_word = (uint32_t *) qdf_nbuf_data(htt_t2h_msg); + msg_type = HTT_T2H_MSG_TYPE_GET(*msg_word); + +#if defined(HELIUMPLUS_DEBUG) + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "%s %d: msg_word 0x%x msg_type %d", __func__, __LINE__, + *msg_word, msg_type); +#endif + + switch (msg_type) { + case HTT_T2H_MSG_TYPE_RX_IND: + { + unsigned int num_mpdu_ranges; + unsigned int num_msdu_bytes; + unsigned int calculated_msg_len; + unsigned int rx_mpdu_range_offset_bytes; + uint16_t peer_id; + uint8_t tid; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + if (qdf_unlikely(pdev->cfg.is_full_reorder_offload)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND not supported "); + qdf_print("with full reorder offload\n"); + break; + } + peer_id = HTT_RX_IND_PEER_ID_GET(*msg_word); + tid = HTT_RX_IND_EXT_TID_GET(*msg_word); + + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, invalid tid %d\n", + tid); + break; + } + if (msg_len < (2 + HTT_RX_PPDU_DESC_SIZE32 + 1) * sizeof(uint32_t)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, invalid msg_len\n"); + break; + } + num_msdu_bytes = + HTT_RX_IND_FW_RX_DESC_BYTES_GET( + *(msg_word + 2 + HTT_RX_PPDU_DESC_SIZE32)); + /* + * 1 word for the message header, + * HTT_RX_PPDU_DESC_SIZE32 words for the FW rx PPDU desc + * 1 word to specify the number of MSDU bytes, + * 1 word for every 4 MSDU bytes (round up), + * 1 word for the MPDU range header + */ + rx_mpdu_range_offset_bytes = + (HTT_RX_IND_HDR_BYTES + num_msdu_bytes + 3); + if (qdf_unlikely(num_msdu_bytes > + rx_mpdu_range_offset_bytes)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, invalid %s %u\n", + "num_msdu_bytes", + num_msdu_bytes); + WARN_ON(1); + break; + } + pdev->rx_mpdu_range_offset_words = + rx_mpdu_range_offset_bytes >> 2; + num_mpdu_ranges = + HTT_RX_IND_NUM_MPDU_RANGES_GET(*(msg_word + 1)); + pdev->rx_ind_msdu_byte_idx = 0; + if (qdf_unlikely(rx_mpdu_range_offset_bytes > + msg_len)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, invalid %s %d\n", + "rx_mpdu_range_offset_words", + pdev->rx_mpdu_range_offset_words); + WARN_ON(1); + break; + } + calculated_msg_len = rx_mpdu_range_offset_bytes + + (num_mpdu_ranges * (int)sizeof(uint32_t)); + /* + * Check that the addition and multiplication + * do not cause integer overflow + */ + if (qdf_unlikely(calculated_msg_len < + rx_mpdu_range_offset_bytes)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, invalid %s %u\n", + "num_mpdu_ranges", + (num_mpdu_ranges * (int)sizeof(uint32_t))); + WARN_ON(1); + break; + } + if (qdf_unlikely(calculated_msg_len > msg_len)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, invalid %s %u\n", + "offset_words + mpdu_ranges", + calculated_msg_len); + WARN_ON(1); + break; + } + ol_rx_indication_handler(pdev->txrx_pdev, + htt_t2h_msg, peer_id, + tid, num_mpdu_ranges); + + if (pdev->cfg.is_high_latency) + return; + + break; + } + case HTT_T2H_MSG_TYPE_TX_COMPL_IND: + { + int old_credit; + int num_msdus; + enum htt_tx_status status; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + /* status - no enum translation needed */ + status = HTT_TX_COMPL_IND_STATUS_GET(*msg_word); + num_msdus = HTT_TX_COMPL_IND_NUM_GET(*msg_word); + + /* + * each desc id will occupy 2 bytes. + * the 4 is for htt msg header + */ + if ((num_msdus * HTT_TX_COMPL_BYTES_PER_MSDU_ID + + HTT_TX_COMPL_HEAD_SZ) > msg_len) { + qdf_print("%s: num_msdus(%d) is invalid," + "adf_nbuf_len = %d\n", + __FUNCTION__, + num_msdus, + msg_len); + break; + } + + if (num_msdus & 0x1) { + struct htt_tx_compl_ind_base *compl = + (void *)msg_word; + + /* + * Host CPU endianness can be different from FW CPU. + * This can result in even and odd MSDU IDs being + * switched. If this happens, copy the switched final + * odd MSDU ID from location payload[size], to + * location payload[size-1], where the message + * handler function expects to find it + */ + if (compl->payload[num_msdus] != + HTT_TX_COMPL_INV_MSDU_ID) { + compl->payload[num_msdus - 1] = + compl->payload[num_msdus]; + } + } + + if (pdev->cfg.is_high_latency && + !pdev->cfg.credit_update_enabled) { + old_credit = qdf_atomic_read( + &pdev->htt_tx_credit.target_delta); + if (((old_credit + num_msdus) > MAX_TARGET_TX_CREDIT) || + ((old_credit + num_msdus) < -MAX_TARGET_TX_CREDIT)) { + qdf_err("invalid update,old_credit=%d, num_msdus=%d", + old_credit, num_msdus); + } else { + if (!pdev->cfg.default_tx_comp_req) { + int credit_delta; + + HTT_TX_MUTEX_ACQUIRE(&pdev->credit_mutex); + qdf_atomic_add(num_msdus, + &pdev->htt_tx_credit. + target_delta); + credit_delta = htt_tx_credit_update(pdev); + HTT_TX_MUTEX_RELEASE(&pdev->credit_mutex); + + if (credit_delta) { + ol_tx_target_credit_update( + pdev->txrx_pdev, + credit_delta); + } + } else { + ol_tx_target_credit_update(pdev->txrx_pdev, + num_msdus); + } + } + } + + ol_tx_completion_handler(pdev->txrx_pdev, num_msdus, + status, msg_word); + HTT_TX_SCHED(pdev); + break; + } + case HTT_T2H_MSG_TYPE_RX_PN_IND: + { + uint16_t peer_id; + uint8_t tid, pn_ie_cnt, *pn_ie = NULL; + uint16_t seq_num_start, seq_num_end; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + if (msg_len < HTT_RX_PN_IND_BYTES) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + + /*First dword */ + peer_id = HTT_RX_PN_IND_PEER_ID_GET(*msg_word); + tid = HTT_RX_PN_IND_EXT_TID_GET(*msg_word); + + msg_word++; + /*Second dword */ + seq_num_start = + HTT_RX_PN_IND_SEQ_NUM_START_GET(*msg_word); + seq_num_end = HTT_RX_PN_IND_SEQ_NUM_END_GET(*msg_word); + pn_ie_cnt = HTT_RX_PN_IND_PN_IE_CNT_GET(*msg_word); + + if (msg_len - HTT_RX_PN_IND_BYTES < + pn_ie_cnt * sizeof(uint8_t)) { + qdf_print("invalid pn_ie count"); + WARN_ON(1); + break; + } + + msg_word++; + /*Third dword */ + if (pn_ie_cnt) + pn_ie = (uint8_t *) msg_word; + + ol_rx_pn_ind_handler(pdev->txrx_pdev, peer_id, tid, + seq_num_start, seq_num_end, + pn_ie_cnt, pn_ie); + + break; + } + case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: + { + int num_msdus; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + num_msdus = HTT_TX_COMPL_IND_NUM_GET(*msg_word); + /* + * each desc id will occupy 2 bytes. + * the 4 is for htt msg header + */ + if ((num_msdus * HTT_TX_COMPL_BYTES_PER_MSDU_ID + + HTT_TX_COMPL_HEAD_SZ) > msg_len) { + qdf_print("%s: num_msdus(%d) is invalid," + "adf_nbuf_len = %d\n", + __FUNCTION__, + num_msdus, + msg_len); + break; + } + + if (num_msdus & 0x1) { + struct htt_tx_compl_ind_base *compl = + (void *)msg_word; + + /* + * Host CPU endianness can be different from FW CPU. + * This can result in even and odd MSDU IDs being + * switched. If this happens, copy the switched final + * odd MSDU ID from location payload[size], to + * location payload[size-1], where the message handler + * function expects to find it + */ + if (compl->payload[num_msdus] != + HTT_TX_COMPL_INV_MSDU_ID) { + compl->payload[num_msdus - 1] = + compl->payload[num_msdus]; + } + } + ol_tx_inspect_handler(pdev->txrx_pdev, num_msdus, + msg_word + 1); + HTT_TX_SCHED(pdev); + break; + } + case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: + { + uint16_t peer_id; + uint8_t tid; + uint8_t offload_ind, frag_ind; + + if (qdf_unlikely(!pdev->cfg.is_full_reorder_offload)) { + qdf_print("full reorder offload is disable"); + break; + } + + if (qdf_unlikely(pdev->cfg.is_high_latency)) { + qdf_print("full reorder offload not support in HL"); + break; + } + + peer_id = HTT_RX_IN_ORD_PADDR_IND_PEER_ID_GET(*msg_word); + tid = HTT_RX_IN_ORD_PADDR_IND_EXT_TID_GET(*msg_word); + offload_ind = HTT_RX_IN_ORD_PADDR_IND_OFFLOAD_GET(*msg_word); + frag_ind = HTT_RX_IN_ORD_PADDR_IND_FRAG_GET(*msg_word); + +#if defined(HELIUMPLUS_DEBUG) + qdf_print("peerid %d tid %d offloadind %d fragind %d", + peer_id, tid, offload_ind, + frag_ind); +#endif + if (qdf_unlikely(frag_ind)) { + ol_rx_frag_indication_handler(pdev->txrx_pdev, + htt_t2h_msg, + peer_id, tid); + break; + } + + ol_rx_in_order_indication_handler(pdev->txrx_pdev, + htt_t2h_msg, peer_id, + tid, offload_ind); + break; + } + + default: + htt_t2h_lp_msg_handler(context, htt_t2h_msg, true); + return; + + }; + + /* Free the indication buffer */ + qdf_nbuf_free(htt_t2h_msg); +} + +#ifdef WLAN_FEATURE_FASTPATH +#define HTT_T2H_MSG_BUF_REINIT(_buf, dev) \ + do { \ + QDF_NBUF_CB_PADDR(_buf) -= (HTC_HEADER_LEN + \ + HTC_HDR_ALIGNMENT_PADDING); \ + qdf_nbuf_init_fast((_buf)); \ + qdf_mem_dma_sync_single_for_device(dev, \ + (QDF_NBUF_CB_PADDR(_buf)), \ + (skb_end_pointer(_buf) - \ + (_buf)->data), \ + PCI_DMA_FROMDEVICE); \ + } while (0) + +/** + * htt_t2h_msg_handler_fast() - Fastpath specific message handler + * @context: HTT context + * @cmpl_msdus: netbuf completions + * @num_cmpls: number of completions to be handled + * + * Return: None + */ +void htt_t2h_msg_handler_fast(void *context, qdf_nbuf_t *cmpl_msdus, + uint32_t num_cmpls) +{ + struct htt_pdev_t *pdev = (struct htt_pdev_t *)context; + qdf_nbuf_t htt_t2h_msg; + uint32_t *msg_word; + uint32_t i; + enum htt_t2h_msg_type msg_type; + uint32_t msg_len; + + for (i = 0; i < num_cmpls; i++) { + htt_t2h_msg = cmpl_msdus[i]; + msg_len = qdf_nbuf_len(htt_t2h_msg); + + /* + * Move the data pointer to point to HTT header + * past the HTC header + HTC header alignment padding + */ + qdf_nbuf_pull_head(htt_t2h_msg, HTC_HEADER_LEN + + HTC_HDR_ALIGNMENT_PADDING); + + /* confirm alignment */ + HTT_ASSERT3((((unsigned long) qdf_nbuf_data(htt_t2h_msg)) & 0x3) + == 0); + + msg_word = (u_int32_t *) qdf_nbuf_data(htt_t2h_msg); + msg_type = HTT_T2H_MSG_TYPE_GET(*msg_word); + + switch (msg_type) { + case HTT_T2H_MSG_TYPE_RX_IND: + { + unsigned int num_mpdu_ranges; + unsigned int num_msdu_bytes; + unsigned int calculated_msg_len; + unsigned int rx_mpdu_range_offset_bytes; + u_int16_t peer_id; + u_int8_t tid; + msg_len = qdf_nbuf_len(htt_t2h_msg); + + peer_id = HTT_RX_IND_PEER_ID_GET(*msg_word); + tid = HTT_RX_IND_EXT_TID_GET(*msg_word); + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, invalid tid %d\n", + tid); + WARN_ON(1); + break; + } + num_msdu_bytes = + HTT_RX_IND_FW_RX_DESC_BYTES_GET( + *(msg_word + 2 + + HTT_RX_PPDU_DESC_SIZE32)); + /* + * 1 word for the message header, + * HTT_RX_PPDU_DESC_SIZE32 words for the FW + * rx PPDU desc. + * 1 word to specify the number of MSDU bytes, + * 1 word for every 4 MSDU bytes (round up), + * 1 word for the MPDU range header + */ + rx_mpdu_range_offset_bytes = + (HTT_RX_IND_HDR_BYTES + num_msdu_bytes + 3); + if (qdf_unlikely(num_msdu_bytes > + rx_mpdu_range_offset_bytes)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, %s %u\n", + "invalid num_msdu_bytes", + num_msdu_bytes); + WARN_ON(1); + break; + } + pdev->rx_mpdu_range_offset_words = + rx_mpdu_range_offset_bytes >> 2; + num_mpdu_ranges = + HTT_RX_IND_NUM_MPDU_RANGES_GET(*(msg_word + + 1)); + pdev->rx_ind_msdu_byte_idx = 0; + if (qdf_unlikely(rx_mpdu_range_offset_bytes > + msg_len)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, %s %d\n", + "invalid rx_mpdu_range_offset_words", + pdev->rx_mpdu_range_offset_words); + WARN_ON(1); + break; + } + calculated_msg_len = rx_mpdu_range_offset_bytes + + (num_mpdu_ranges * + (int)sizeof(uint32_t)); + /* + * Check that the addition and multiplication + * do not cause integer overflow + */ + if (qdf_unlikely(calculated_msg_len < + rx_mpdu_range_offset_bytes)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, %s %u\n", + "invalid num_mpdu_ranges", + (num_mpdu_ranges * + (int)sizeof(uint32_t))); + WARN_ON(1); + break; + } + if (qdf_unlikely(calculated_msg_len > msg_len)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IND, %s %u\n", + "invalid offset_words + mpdu_ranges", + calculated_msg_len); + WARN_ON(1); + break; + } + ol_rx_indication_handler(pdev->txrx_pdev, htt_t2h_msg, + peer_id, tid, num_mpdu_ranges); + break; + } + case HTT_T2H_MSG_TYPE_TX_COMPL_IND: + { + int num_msdus; + enum htt_tx_status status; + + /* status - no enum translation needed */ + status = HTT_TX_COMPL_IND_STATUS_GET(*msg_word); + num_msdus = HTT_TX_COMPL_IND_NUM_GET(*msg_word); + + /* + * each desc id will occupy 2 bytes. + * the 4 is for htt msg header + */ + if ((num_msdus * HTT_TX_COMPL_BYTES_PER_MSDU_ID + + HTT_TX_COMPL_HEAD_SZ) > msg_len) { + qdf_print("%s: num_msdus(%d) is invalid," + "adf_nbuf_len = %d\n", + __FUNCTION__, + num_msdus, + msg_len); + break; + } + + if (num_msdus & 0x1) { + struct htt_tx_compl_ind_base *compl = + (void *)msg_word; + + /* + * Host CPU endianness can be different + * from FW CPU. This can result in even + * and odd MSDU IDs being switched. If + * this happens, copy the switched final + * odd MSDU ID from location + * payload[size], to location + * payload[size-1],where the message + * handler function expects to find it + */ + if (compl->payload[num_msdus] != + HTT_TX_COMPL_INV_MSDU_ID) { + compl->payload[num_msdus - 1] = + compl->payload[num_msdus]; + } + } + ol_tx_completion_handler(pdev->txrx_pdev, num_msdus, + status, msg_word); + + break; + } + case HTT_T2H_MSG_TYPE_RX_PN_IND: + { + u_int16_t peer_id; + u_int8_t tid, pn_ie_cnt, *pn_ie = NULL; + int seq_num_start, seq_num_end; + int msg_len = qdf_nbuf_len(htt_t2h_msg); + + if (msg_len < HTT_RX_PN_IND_BYTES) { + qdf_print("invalid nbuff len"); + WARN_ON(1); + break; + } + + /*First dword */ + peer_id = HTT_RX_PN_IND_PEER_ID_GET(*msg_word); + tid = HTT_RX_PN_IND_EXT_TID_GET(*msg_word); + + msg_word++; + /*Second dword */ + seq_num_start = + HTT_RX_PN_IND_SEQ_NUM_START_GET(*msg_word); + seq_num_end = + HTT_RX_PN_IND_SEQ_NUM_END_GET(*msg_word); + pn_ie_cnt = + HTT_RX_PN_IND_PN_IE_CNT_GET(*msg_word); + + if (msg_len - HTT_RX_PN_IND_BYTES < + pn_ie_cnt * sizeof(uint8_t)) { + qdf_print("invalid pn_ie len"); + WARN_ON(1); + break; + } + + msg_word++; + /*Third dword*/ + if (pn_ie_cnt) + pn_ie = (u_int8_t *)msg_word; + + ol_rx_pn_ind_handler(pdev->txrx_pdev, peer_id, tid, + seq_num_start, seq_num_end, pn_ie_cnt, pn_ie); + + break; + } + case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: + { + int num_msdus; + + num_msdus = HTT_TX_COMPL_IND_NUM_GET(*msg_word); + /* + * each desc id will occupy 2 bytes. + * the 4 is for htt msg header + */ + if ((num_msdus * HTT_TX_COMPL_BYTES_PER_MSDU_ID + + HTT_TX_COMPL_HEAD_SZ) > msg_len) { + qdf_print("%s: num_msdus(%d) is invalid," + "adf_nbuf_len = %d\n", + __FUNCTION__, + num_msdus, + msg_len); + break; + } + + if (num_msdus & 0x1) { + struct htt_tx_compl_ind_base *compl = + (void *)msg_word; + + /* + * Host CPU endianness can be different + * from FW CPU. This * can result in + * even and odd MSDU IDs being switched. + * If this happens, copy the switched + * final odd MSDU ID from location + * payload[size], to location + * payload[size-1], where the message + * handler function expects to find it + */ + if (compl->payload[num_msdus] != + HTT_TX_COMPL_INV_MSDU_ID) { + compl->payload[num_msdus - 1] = + compl->payload[num_msdus]; + } + } + ol_tx_inspect_handler(pdev->txrx_pdev, + num_msdus, msg_word + 1); + break; + } + case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: + { + u_int16_t peer_id; + u_int8_t tid; + u_int8_t offload_ind, frag_ind; + + if (qdf_unlikely( + !pdev->cfg.is_full_reorder_offload)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND not supported when full reorder offload is disabled\n"); + break; + } + + if (qdf_unlikely( + pdev->txrx_pdev->cfg.is_high_latency)) { + qdf_print("HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND not supported on high latency\n"); + break; + } + + peer_id = HTT_RX_IN_ORD_PADDR_IND_PEER_ID_GET( + *msg_word); + tid = HTT_RX_IN_ORD_PADDR_IND_EXT_TID_GET( + *msg_word); + offload_ind = + HTT_RX_IN_ORD_PADDR_IND_OFFLOAD_GET( + *msg_word); + frag_ind = HTT_RX_IN_ORD_PADDR_IND_FRAG_GET( + *msg_word); + + if (qdf_unlikely(frag_ind)) { + ol_rx_frag_indication_handler( + pdev->txrx_pdev, htt_t2h_msg, peer_id, + tid); + break; + } + + ol_rx_in_order_indication_handler( + pdev->txrx_pdev, htt_t2h_msg, + peer_id, tid, offload_ind); + break; + } + default: + htt_t2h_lp_msg_handler(context, htt_t2h_msg, false); + break; + }; + + /* Re-initialize the indication buffer */ + HTT_T2H_MSG_BUF_REINIT(htt_t2h_msg, pdev->osdev); + qdf_nbuf_set_pktlen(htt_t2h_msg, 0); + } +} +#endif /* WLAN_FEATURE_FASTPATH */ + +/*--- target->host HTT message Info Element access methods ------------------*/ + +/*--- tx completion message ---*/ + +uint16_t htt_tx_compl_desc_id(void *iterator, int num) +{ + /* + * The MSDU IDs are packed , 2 per 32-bit word. + * Iterate on them as an array of 16-bit elements. + * This will work fine if the host endianness matches + * the target endianness. + * If the host endianness is opposite of the target's, + * this iterator will produce descriptor IDs in a different + * order than the target inserted them into the message - + * if the target puts in [0, 1, 2, 3, ...] the host will + * put out [1, 0, 3, 2, ...]. + * This is fine, except for the last ID if there are an + * odd number of IDs. But the TX_COMPL_IND handling code + * in the htt_t2h_msg_handler already added a duplicate + * of the final ID, if there were an odd number of IDs, + * so this function can safely treat the IDs as an array + * of 16-bit elements. + */ + return *(((uint16_t *) iterator) + num); +} + +/*--- rx indication message ---*/ + +int htt_rx_ind_flush(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) qdf_nbuf_data(rx_ind_msg); + return HTT_RX_IND_FLUSH_VALID_GET(*msg_word); +} + +void +htt_rx_ind_flush_seq_num_range(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + unsigned int *seq_num_start, + unsigned int *seq_num_end) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) qdf_nbuf_data(rx_ind_msg); + msg_word++; + *seq_num_start = HTT_RX_IND_FLUSH_SEQ_NUM_START_GET(*msg_word); + *seq_num_end = HTT_RX_IND_FLUSH_SEQ_NUM_END_GET(*msg_word); +} + +int htt_rx_ind_release(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) qdf_nbuf_data(rx_ind_msg); + return HTT_RX_IND_REL_VALID_GET(*msg_word); +} + +void +htt_rx_ind_release_seq_num_range(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + unsigned int *seq_num_start, + unsigned int *seq_num_end) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) qdf_nbuf_data(rx_ind_msg); + msg_word++; + *seq_num_start = HTT_RX_IND_REL_SEQ_NUM_START_GET(*msg_word); + *seq_num_end = HTT_RX_IND_REL_SEQ_NUM_END_GET(*msg_word); +} + +void +htt_rx_ind_mpdu_range_info(struct htt_pdev_t *pdev, + qdf_nbuf_t rx_ind_msg, + int mpdu_range_num, + enum htt_rx_status *status, int *mpdu_count) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) qdf_nbuf_data(rx_ind_msg); + msg_word += pdev->rx_mpdu_range_offset_words + mpdu_range_num; + *status = HTT_RX_IND_MPDU_STATUS_GET(*msg_word); + *mpdu_count = HTT_RX_IND_MPDU_COUNT_GET(*msg_word); +} + +/** + * htt_rx_ind_rssi_dbm() - Return the RSSI provided in a rx indication message. + * + * @pdev: the HTT instance the rx data was received on + * @rx_ind_msg: the netbuf containing the rx indication message + * + * Return the RSSI from an rx indication message, in dBm units. + * + * Return: RSSI in dBm, or HTT_INVALID_RSSI + */ +int16_t htt_rx_ind_rssi_dbm(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg) +{ + int8_t rssi; + uint32_t *msg_word; + + msg_word = (uint32_t *) + (qdf_nbuf_data(rx_ind_msg) + + HTT_RX_IND_FW_RX_PPDU_DESC_BYTE_OFFSET); + + /* check if the RX_IND message contains valid rx PPDU start info */ + if (!HTT_RX_IND_START_VALID_GET(*msg_word)) + return HTT_RSSI_INVALID; + + rssi = HTT_RX_IND_RSSI_CMB_GET(*msg_word); + return (HTT_TGT_RSSI_INVALID == rssi) ? + HTT_RSSI_INVALID : rssi; +} + +/** + * htt_rx_ind_rssi_dbm_chain() - Return the RSSI for a chain provided in a rx + * indication message. + * @pdev: the HTT instance the rx data was received on + * @rx_ind_msg: the netbuf containing the rx indication message + * @chain: the index of the chain (0-4) + * + * Return the RSSI for a chain from an rx indication message, in dBm units. + * + * Return: RSSI, or HTT_INVALID_RSSI + */ +int16_t +htt_rx_ind_rssi_dbm_chain(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg, + int8_t chain) +{ + int8_t rssi; + uint32_t *msg_word; + + if (chain < 0 || chain > 3) + return HTT_RSSI_INVALID; + + msg_word = (uint32_t *) + (qdf_nbuf_data(rx_ind_msg) + + HTT_RX_IND_FW_RX_PPDU_DESC_BYTE_OFFSET); + + /* check if the RX_IND message contains valid rx PPDU start info */ + if (!HTT_RX_IND_START_VALID_GET(*msg_word)) + return HTT_RSSI_INVALID; + + msg_word += 1 + chain; + + rssi = HTT_RX_IND_RSSI_PRI20_GET(*msg_word); + return (HTT_TGT_RSSI_INVALID == rssi) ? + HTT_RSSI_INVALID : + rssi; +} + +/** + * htt_rx_ind_legacy_rate() - Return the data rate + * @pdev: the HTT instance the rx data was received on + * @rx_ind_msg: the netbuf containing the rx indication message + * @legacy_rate: (output) the data rate + * The legacy_rate parameter's value depends on the + * legacy_rate_sel value. + * If legacy_rate_sel is 0: + * 0x8: OFDM 48 Mbps + * 0x9: OFDM 24 Mbps + * 0xA: OFDM 12 Mbps + * 0xB: OFDM 6 Mbps + * 0xC: OFDM 54 Mbps + * 0xD: OFDM 36 Mbps + * 0xE: OFDM 18 Mbps + * 0xF: OFDM 9 Mbps + * If legacy_rate_sel is 1: + * 0x8: CCK 11 Mbps long preamble + * 0x9: CCK 5.5 Mbps long preamble + * 0xA: CCK 2 Mbps long preamble + * 0xB: CCK 1 Mbps long preamble + * 0xC: CCK 11 Mbps short preamble + * 0xD: CCK 5.5 Mbps short preamble + * 0xE: CCK 2 Mbps short preamble + * -1 on error. + * @legacy_rate_sel: (output) 0 to indicate OFDM, 1 to indicate CCK. + * -1 on error. + * + * Return the data rate provided in a rx indication message. + */ +void +htt_rx_ind_legacy_rate(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg, + uint8_t *legacy_rate, uint8_t *legacy_rate_sel) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) + (qdf_nbuf_data(rx_ind_msg) + + HTT_RX_IND_FW_RX_PPDU_DESC_BYTE_OFFSET); + + /* check if the RX_IND message contains valid rx PPDU start info */ + if (!HTT_RX_IND_START_VALID_GET(*msg_word)) { + *legacy_rate = -1; + *legacy_rate_sel = -1; + return; + } + + *legacy_rate = HTT_RX_IND_LEGACY_RATE_GET(*msg_word); + *legacy_rate_sel = HTT_RX_IND_LEGACY_RATE_SEL_GET(*msg_word); +} + +/** + * htt_rx_ind_timestamp() - Return the timestamp + * @pdev: the HTT instance the rx data was received on + * @rx_ind_msg: the netbuf containing the rx indication message + * @timestamp_microsec: (output) the timestamp to microsecond resolution. + * -1 on error. + * @timestamp_submicrosec: the submicrosecond portion of the + * timestamp. -1 on error. + * + * Return the timestamp provided in a rx indication message. + */ +void +htt_rx_ind_timestamp(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg, + uint32_t *timestamp_microsec, + uint8_t *timestamp_submicrosec) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) + (qdf_nbuf_data(rx_ind_msg) + + HTT_RX_IND_FW_RX_PPDU_DESC_BYTE_OFFSET); + + /* check if the RX_IND message contains valid rx PPDU start info */ + if (!HTT_RX_IND_END_VALID_GET(*msg_word)) { + *timestamp_microsec = -1; + *timestamp_submicrosec = -1; + return; + } + + *timestamp_microsec = *(msg_word + 6); + *timestamp_submicrosec = + HTT_RX_IND_TIMESTAMP_SUBMICROSEC_GET(*msg_word); +} + +#define INVALID_TSF -1 +/** + * htt_rx_ind_tsf32() - Return the TSF timestamp + * @pdev: the HTT instance the rx data was received on + * @rx_ind_msg: the netbuf containing the rx indication message + * + * Return the TSF timestamp provided in a rx indication message. + * + * Return: TSF timestamp + */ +uint32_t +htt_rx_ind_tsf32(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) + (qdf_nbuf_data(rx_ind_msg) + + HTT_RX_IND_FW_RX_PPDU_DESC_BYTE_OFFSET); + + /* check if the RX_IND message contains valid rx PPDU start info */ + if (!HTT_RX_IND_END_VALID_GET(*msg_word)) + return INVALID_TSF; + + return *(msg_word + 5); +} + +/** + * htt_rx_ind_ext_tid() - Return the extended traffic ID provided in a rx + * indication message. + * @pdev: the HTT instance the rx data was received on + * @rx_ind_msg: the netbuf containing the rx indication message + * + * Return the extended traffic ID in a rx indication message. + * + * Return: Extended TID + */ +uint8_t +htt_rx_ind_ext_tid(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) + (qdf_nbuf_data(rx_ind_msg)); + + return HTT_RX_IND_EXT_TID_GET(*msg_word); +} + +/*--- stats confirmation message ---*/ + +void +htt_t2h_dbg_stats_hdr_parse(uint8_t *stats_info_list, + enum htt_dbg_stats_type *type, + enum htt_dbg_stats_status *status, + int *length, uint8_t **stats_data) +{ + uint32_t *msg_word = (uint32_t *) stats_info_list; + *type = HTT_T2H_STATS_CONF_TLV_TYPE_GET(*msg_word); + *status = HTT_T2H_STATS_CONF_TLV_STATUS_GET(*msg_word); + *length = HTT_T2H_STATS_CONF_TLV_HDR_SIZE + /* header length */ + HTT_T2H_STATS_CONF_TLV_LENGTH_GET(*msg_word); /* data len */ + *stats_data = stats_info_list + HTT_T2H_STATS_CONF_TLV_HDR_SIZE; +} + +void +htt_rx_frag_ind_flush_seq_num_range(htt_pdev_handle pdev, + qdf_nbuf_t rx_frag_ind_msg, + uint16_t *seq_num_start, uint16_t *seq_num_end) +{ + uint32_t *msg_word; + + msg_word = (uint32_t *) qdf_nbuf_data(rx_frag_ind_msg); + msg_word++; + *seq_num_start = HTT_RX_FRAG_IND_FLUSH_SEQ_NUM_START_GET(*msg_word); + *seq_num_end = HTT_RX_FRAG_IND_FLUSH_SEQ_NUM_END_GET(*msg_word); +} diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_tx.c b/drivers/staging/qcacld-3.0/core/dp/htt/htt_tx.c new file mode 100644 index 0000000000000000000000000000000000000000..bd6667f0cec66aaab0de6fa8af38e717cabf4fe7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_tx.c @@ -0,0 +1,1986 @@ +/* + * Copyright (c) 2011, 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file htt_tx.c + * @brief Implement transmit aspects of HTT. + * @details + * This file contains three categories of HTT tx code: + * 1. An abstraction of the tx descriptor, to hide the + * differences between the HL vs. LL tx descriptor. + * 2. Functions for allocating and freeing HTT tx descriptors. + * 3. The function that accepts a tx frame from txrx and sends the + * tx frame to HTC. + */ +#include /* uint32_t, offsetof, etc. */ +#include /* qdf_dma_addr_t */ +#include /* qdf_mem_alloc_consistent et al */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_mdelay */ + +#include /* htt_tx_msdu_desc_t */ +#include /* HTC_HDR_LENGTH */ +#include /* htc_flush_surprise_remove */ +#include /* ol_cfg_netbuf_frags_max, etc. */ +#include /* HTT_TX_DESC_VADDR_OFFSET */ +#include /* ol_tx_msdu_id_storage */ +#include +#include + +#include + +/* IPA Micro controller TX data packet HTT Header Preset + * 31 | 30 29 | 28 | 27 | 26 22 | 21 16 | 15 13 | 12 8 | 7 0 + ***---------------------------------------------------------------------------- + * R | CS OL | R | PP | ext TID | vdev ID | pkt type | pkt subtyp | msg type + * 0 | 0 | 0 | | 0x1F | 0 | 2 | 0 | 0x01 + ***---------------------------------------------------------------------------- + * pkt ID | pkt length + ***---------------------------------------------------------------------------- + * frag_desc_ptr + ***---------------------------------------------------------------------------- + * peer_id + ***---------------------------------------------------------------------------- + */ +#define HTT_IPA_UC_OFFLOAD_TX_HEADER_DEFAULT 0x07C04001 + +#ifdef QCA_WIFI_3_0 +#define IPA_UC_TX_BUF_FRAG_DESC_OFFSET 20 +#define IPA_UC_TX_BUF_FRAG_HDR_OFFSET 64 +#define IPA_UC_TX_BUF_TSO_HDR_SIZE 6 +#define IPA_UC_TX_BUF_PADDR_HI_MASK 0x0000001F +#else +#define IPA_UC_TX_BUF_FRAG_DESC_OFFSET 16 +#define IPA_UC_TX_BUF_FRAG_HDR_OFFSET 32 +#endif /* QCA_WIFI_3_0 */ + +#if HTT_PADDR64 +#define HTT_TX_DESC_FRAG_FIELD_UPDATE(frag_filed_ptr, frag_desc_addr) \ +do { \ + *frag_filed_ptr = qdf_get_lower_32_bits(frag_desc_addr); \ + frag_filed_ptr++; \ + /* frags_desc_ptr.hi */ \ + *frag_filed_ptr = qdf_get_upper_32_bits(frag_desc_addr) & 0x1F; \ +} while (0) +#else +#define HTT_TX_DESC_FRAG_FIELD_UPDATE(frag_filed_ptr, frag_desc_addr) \ +do { \ + *frag_filed_ptr = qdf_get_lower_32_bits(frag_desc_addr); \ +} while (0) +#endif + +/*--- setup / tear-down functions -------------------------------------------*/ + +static qdf_dma_addr_t htt_tx_get_paddr(htt_pdev_handle pdev, + char *target_vaddr); + +#ifdef HELIUMPLUS +/** + * htt_tx_desc_get_size() - get tx descripotrs size + * @pdev: htt device instance pointer + * + * This function will get HTT TX descriptor size and fragment descriptor size + * + * Return: None + */ +static void htt_tx_desc_get_size(struct htt_pdev_t *pdev) +{ + pdev->tx_descs.size = sizeof(struct htt_host_tx_desc_t); + if (HTT_WIFI_IP_VERSION(pdev->wifi_ip_ver.major, 0x2)) { + /* + * sizeof MSDU_EXT/Fragmentation descriptor. + */ + pdev->frag_descs.size = sizeof(struct msdu_ext_desc_t); + } else { + /* + * Add the fragmentation descriptor elements. + * Add the most that the OS may deliver, plus one more + * in case the txrx code adds a prefix fragment (for + * TSO or audio interworking SNAP header) + */ + pdev->frag_descs.size = + (ol_cfg_netbuf_frags_max(pdev->ctrl_pdev)+1) * 8 + + 4; + } +} + +/** + * htt_tx_frag_desc_field_update() - Update fragment descriptor field + * @pdev: htt device instance pointer + * @fptr: Fragment descriptor field pointer + * @index: Descriptor index to find page and offset + * @desc_v_ptr: descriptor virtual pointot to find offset + * + * This function will update fragment descriptor field with actual fragment + * descriptor stating physical pointer + * + * Return: None + */ +static void htt_tx_frag_desc_field_update(struct htt_pdev_t *pdev, + uint32_t *fptr, unsigned int index, + struct htt_tx_msdu_desc_t *desc_v_ptr) +{ + unsigned int target_page; + unsigned int offset; + struct qdf_mem_dma_page_t *dma_page; + qdf_dma_addr_t frag_desc_addr; + + target_page = index / pdev->frag_descs.desc_pages.num_element_per_page; + offset = index % pdev->frag_descs.desc_pages.num_element_per_page; + dma_page = &pdev->frag_descs.desc_pages.dma_pages[target_page]; + frag_desc_addr = (dma_page->page_p_addr + + offset * pdev->frag_descs.size); + HTT_TX_DESC_FRAG_FIELD_UPDATE(fptr, frag_desc_addr); +} + +/** + * htt_tx_frag_desc_attach() - Attach fragment descriptor + * @pdev: htt device instance pointer + * @desc_pool_elems: Number of fragment descriptor + * + * This function will allocate fragment descriptor + * + * Return: 0 success + */ +static int htt_tx_frag_desc_attach(struct htt_pdev_t *pdev, + uint16_t desc_pool_elems) +{ + pdev->frag_descs.pool_elems = desc_pool_elems; + qdf_mem_multi_pages_alloc(pdev->osdev, &pdev->frag_descs.desc_pages, + pdev->frag_descs.size, desc_pool_elems, + qdf_get_dma_mem_context((&pdev->frag_descs), memctx), false); + if ((0 == pdev->frag_descs.desc_pages.num_pages) || + (!pdev->frag_descs.desc_pages.dma_pages)) { + ol_txrx_err("FRAG descriptor alloc fail"); + return -ENOBUFS; + } + return 0; +} + +/** + * htt_tx_frag_desc_detach() - Detach fragment descriptor + * @pdev: htt device instance pointer + * + * This function will free fragment descriptor + * + * Return: None + */ +static void htt_tx_frag_desc_detach(struct htt_pdev_t *pdev) +{ + qdf_mem_multi_pages_free(pdev->osdev, &pdev->frag_descs.desc_pages, + qdf_get_dma_mem_context((&pdev->frag_descs), memctx), false); +} + +/** + * htt_tx_frag_alloc() - Allocate single fragment descriptor from the pool + * @pdev: htt device instance pointer + * @index: Descriptor index + * @frag_paddr: Fragment descriptor physical address + * @frag_ptr: Fragment descriptor virtual address + * + * This function will free fragment descriptor + * + * Return: None + */ +int htt_tx_frag_alloc(htt_pdev_handle pdev, + u_int16_t index, qdf_dma_addr_t *frag_paddr, void **frag_ptr) +{ + uint16_t frag_page_index; + uint16_t frag_elem_index; + struct qdf_mem_dma_page_t *dma_page; + + /* + * Index should never be 0, since its used by the hardware + * to terminate the link. + */ + if (index >= pdev->tx_descs.pool_elems) { + *frag_ptr = NULL; + return 1; + } + + frag_page_index = index / + pdev->frag_descs.desc_pages.num_element_per_page; + frag_elem_index = index % + pdev->frag_descs.desc_pages.num_element_per_page; + dma_page = &pdev->frag_descs.desc_pages.dma_pages[frag_page_index]; + + *frag_ptr = dma_page->page_v_addr_start + + frag_elem_index * pdev->frag_descs.size; + if (((char *)(*frag_ptr) < dma_page->page_v_addr_start) || + ((char *)(*frag_ptr) > dma_page->page_v_addr_end)) { + *frag_ptr = NULL; + return 1; + } + + *frag_paddr = dma_page->page_p_addr + + frag_elem_index * pdev->frag_descs.size; + return 0; +} +#else + +/** + * htt_tx_desc_get_size() - get tx descripotrs size + * @pdev: htt device instance pointer + * + * This function will get HTT TX descriptor size and fragment descriptor size + * + * Return: None + */ +static inline void htt_tx_desc_get_size(struct htt_pdev_t *pdev) +{ + if (pdev->cfg.is_high_latency) { + pdev->tx_descs.size = sizeof(struct htt_host_tx_desc_t); + } else { + /* + * Start with the size of the base struct + * that actually gets downloaded. + * + * Add the fragmentation descriptor elements. + * Add the most that the OS may deliver, plus one more + * in case the txrx code adds a prefix fragment (for + * TSO or audio interworking SNAP header) + */ + pdev->tx_descs.size = + sizeof(struct htt_host_tx_desc_t) + + (ol_cfg_netbuf_frags_max(pdev->ctrl_pdev) + 1) * 8 + /* 2x uint32_t */ + + 4; /* uint32_t fragmentation list terminator */ + } +} + +#ifndef CONFIG_HL_SUPPORT + +/** + * htt_tx_frag_desc_field_update() - Update fragment descriptor field + * @pdev: htt device instance pointer + * @fptr: Fragment descriptor field pointer + * @index: Descriptor index to find page and offset + * @desc_v_ptr: descriptor virtual pointot to find offset + * + * This function will update fragment descriptor field with actual fragment + * descriptor stating physical pointer + * + * Return: None + */ +static void htt_tx_frag_desc_field_update(struct htt_pdev_t *pdev, + uint32_t *fptr, unsigned int index, + struct htt_tx_msdu_desc_t *desc_v_ptr) +{ + *fptr = (uint32_t)htt_tx_get_paddr(pdev, (char *)desc_v_ptr) + + HTT_TX_DESC_LEN; +} +#endif + +/** + * htt_tx_frag_desc_attach() - Attach fragment descriptor + * @pdev: htt device instance pointer + * @desc_pool_elems: Number of fragment descriptor + * + * This function will allocate fragment descriptor + * + * Return: 0 success + */ +static inline int htt_tx_frag_desc_attach(struct htt_pdev_t *pdev, + int desc_pool_elems) +{ + return 0; +} + +/** + * htt_tx_frag_desc_detach() - Detach fragment descriptor + * @pdev: htt device instance pointer + * + * This function will free fragment descriptor + * + * Return: None + */ +static void htt_tx_frag_desc_detach(struct htt_pdev_t *pdev) {} +#endif /* HELIUMPLUS */ + +#ifdef CONFIG_HL_SUPPORT + +/** + * htt_tx_attach() - Attach HTT device instance + * @pdev: htt device instance pointer + * @desc_pool_elems: Number of TX descriptors + * + * This function will allocate HTT TX resources + * + * Return: 0 Success + */ +int htt_tx_attach(struct htt_pdev_t *pdev, int desc_pool_elems) +{ + int i, i_int, pool_size; + uint32_t **p; + uint32_t num_link = 0; + uint16_t num_page, num_desc_per_page; + void **cacheable_pages = NULL; + + htt_tx_desc_get_size(pdev); + + /* + * Make sure tx_descs.size is a multiple of 4-bytes. + * It should be, but round up just to be sure. + */ + pdev->tx_descs.size = (pdev->tx_descs.size + 3) & (~0x3); + + pdev->tx_descs.pool_elems = desc_pool_elems; + pdev->tx_descs.alloc_cnt = 0; + pool_size = pdev->tx_descs.pool_elems * pdev->tx_descs.size; + qdf_mem_multi_pages_alloc(pdev->osdev, &pdev->tx_descs.desc_pages, + pdev->tx_descs.size, + pdev->tx_descs.pool_elems, + qdf_get_dma_mem_context((&pdev->tx_descs), + memctx), true); + if ((0 == pdev->tx_descs.desc_pages.num_pages) || + (!pdev->tx_descs.desc_pages.cacheable_pages)) { + ol_txrx_err("HTT desc alloc fail"); + goto out_fail; + } + num_page = pdev->tx_descs.desc_pages.num_pages; + num_desc_per_page = pdev->tx_descs.desc_pages.num_element_per_page; + + /* link tx descriptors into a freelist */ + cacheable_pages = pdev->tx_descs.desc_pages.cacheable_pages; + + pdev->tx_descs.freelist = (uint32_t *)cacheable_pages[0]; + p = (uint32_t **)pdev->tx_descs.freelist; + for (i = 0; i < num_page; i++) { + for (i_int = 0; i_int < num_desc_per_page; i_int++) { + if (i_int == (num_desc_per_page - 1)) { + /* + * Last element on this page, + * should point next page + */ + if (!cacheable_pages[i + 1]) { + ol_txrx_err("over flow num link %d\n", + num_link); + goto free_htt_desc; + } + *p = (uint32_t *)cacheable_pages[i + 1]; + } else { + *p = (uint32_t *) + (((char *)p) + pdev->tx_descs.size); + } + num_link++; + p = (uint32_t **) *p; + /* Last link established exit */ + if (num_link == (pdev->tx_descs.pool_elems - 1)) + break; + } + } + *p = NULL; + + if (htt_tx_frag_desc_attach(pdev, desc_pool_elems)) { + ol_txrx_err("HTT Frag descriptor alloc fail"); + goto free_htt_desc; + } + + /* success */ + return 0; + +free_htt_desc: + qdf_mem_multi_pages_free(pdev->osdev, &pdev->tx_descs.desc_pages, + qdf_get_dma_mem_context((&pdev->tx_descs), + memctx), true); +out_fail: + return -ENOBUFS; +} + +void htt_tx_detach(struct htt_pdev_t *pdev) +{ + if (!pdev) { + qdf_print("htt tx detach invalid instance"); + return; + } + + htt_tx_frag_desc_detach(pdev); + qdf_mem_multi_pages_free(pdev->osdev, &pdev->tx_descs.desc_pages, + qdf_get_dma_mem_context((&pdev->tx_descs), + memctx), true); +} + +/** + * htt_tx_set_frag_desc_addr() - set up the fragmentation descriptor address + * @pdev: pointer to the HTT instance making the allocation + * @htt_tx_desc: Host tx decriptor that does not include HTC hdr + * @index: index to alloc htt tx desc + * + * + * Return: None + */ +static inline void +htt_tx_set_frag_desc_addr(struct htt_pdev_t *pdev, + struct htt_tx_msdu_desc_t *htt_tx_desc, + uint16_t index) +{ +} + +/** + * htt_tx_desc_frags_table_set() - set up the descriptor and payload + * to correspondinf fragments + * @pdev: pointer to the HTT instance making the allocation + * @htt_tx_desc: Host tx decriptor that does not include HTC hdr + * @paddr: fragment physical address + * @frag_desc_paddr_lo: frag descriptor address + * @reset: reset + * + * Return: None + */ +void htt_tx_desc_frags_table_set(htt_pdev_handle pdev, + void *desc, + qdf_dma_addr_t paddr, + qdf_dma_addr_t frag_desc_paddr, + int reset) +{ + /* fragments table only applies to LL systems */ +} + +/** + * htt_tx_credit_update() - get the number of credits by which the amount of + * target credits needs to be updated + * @pdev: htt context + * + * Return: number of credits + */ +int htt_tx_credit_update(struct htt_pdev_t *pdev) +{ + int credit_delta; + + credit_delta = QDF_MIN(qdf_atomic_read( + &pdev->htt_tx_credit.target_delta), + qdf_atomic_read(&pdev->htt_tx_credit.bus_delta)); + if (credit_delta) { + qdf_atomic_add(-credit_delta, + &pdev->htt_tx_credit.target_delta); + qdf_atomic_add(-credit_delta, + &pdev->htt_tx_credit.bus_delta); + } + return credit_delta; +} + +/** + * htt_tx_get_paddr() - get physical address for htt desc + * + * Get HTT descriptor physical address from virtual address + * Find page first and find offset + * Not required for HL systems + * + * Return: Physical address of descriptor + */ +static inline +qdf_dma_addr_t htt_tx_get_paddr(htt_pdev_handle pdev, + char *target_vaddr) +{ + return 0; +} + + +#else + +int htt_tx_attach(struct htt_pdev_t *pdev, int desc_pool_elems) +{ + int i, i_int, pool_size; + uint32_t **p; + struct qdf_mem_dma_page_t *page_info; + uint32_t num_link = 0; + uint16_t num_page, num_desc_per_page; + + htt_tx_desc_get_size(pdev); + + /* + * Make sure tx_descs.size is a multiple of 4-bytes. + * It should be, but round up just to be sure. + */ + pdev->tx_descs.size = (pdev->tx_descs.size + 3) & (~0x3); + + pdev->tx_descs.pool_elems = desc_pool_elems; + pdev->tx_descs.alloc_cnt = 0; + pool_size = pdev->tx_descs.pool_elems * pdev->tx_descs.size; + qdf_mem_multi_pages_alloc(pdev->osdev, &pdev->tx_descs.desc_pages, + pdev->tx_descs.size, pdev->tx_descs.pool_elems, + qdf_get_dma_mem_context((&pdev->tx_descs), memctx), false); + if ((0 == pdev->tx_descs.desc_pages.num_pages) || + (!pdev->tx_descs.desc_pages.dma_pages)) { + ol_txrx_err("HTT desc alloc fail"); + goto out_fail; + } + num_page = pdev->tx_descs.desc_pages.num_pages; + num_desc_per_page = pdev->tx_descs.desc_pages.num_element_per_page; + + /* link tx descriptors into a freelist */ + page_info = pdev->tx_descs.desc_pages.dma_pages; + pdev->tx_descs.freelist = (uint32_t *)page_info->page_v_addr_start; + p = (uint32_t **) pdev->tx_descs.freelist; + for (i = 0; i < num_page; i++) { + for (i_int = 0; i_int < num_desc_per_page; i_int++) { + if (i_int == (num_desc_per_page - 1)) { + /* + * Last element on this page, + * should pint next page + */ + if (!page_info->page_v_addr_start) { + ol_txrx_err("over flow num link %d\n", + num_link); + goto free_htt_desc; + } + page_info++; + *p = (uint32_t *)page_info->page_v_addr_start; + } else { + *p = (uint32_t *) + (((char *) p) + pdev->tx_descs.size); + } + num_link++; + p = (uint32_t **) *p; + /* Last link established exit */ + if (num_link == (pdev->tx_descs.pool_elems - 1)) + break; + } + } + *p = NULL; + + if (htt_tx_frag_desc_attach(pdev, desc_pool_elems)) { + ol_txrx_err("HTT Frag descriptor alloc fail"); + goto free_htt_desc; + } + + /* success */ + return 0; + +free_htt_desc: + qdf_mem_multi_pages_free(pdev->osdev, &pdev->tx_descs.desc_pages, + qdf_get_dma_mem_context((&pdev->tx_descs), memctx), false); +out_fail: + return -ENOBUFS; +} + +void htt_tx_detach(struct htt_pdev_t *pdev) +{ + if (!pdev) { + qdf_print("htt tx detach invalid instance"); + return; + } + + htt_tx_frag_desc_detach(pdev); + qdf_mem_multi_pages_free(pdev->osdev, &pdev->tx_descs.desc_pages, + qdf_get_dma_mem_context((&pdev->tx_descs), memctx), false); +} + +static void +htt_tx_set_frag_desc_addr(struct htt_pdev_t *pdev, + struct htt_tx_msdu_desc_t *htt_tx_desc, + uint16_t index) +{ + uint32_t *fragmentation_descr_field_ptr; + + fragmentation_descr_field_ptr = (uint32_t *) + ((uint32_t *)htt_tx_desc) + + HTT_TX_DESC_FRAGS_DESC_PADDR_OFFSET_DWORD; + /* + * The fragmentation descriptor is allocated from consistent + * memory. Therefore, we can use the address directly rather + * than having to map it from a virtual/CPU address to a + * physical/bus address. + */ + htt_tx_frag_desc_field_update(pdev, fragmentation_descr_field_ptr, + index, htt_tx_desc); + + return; +} + +void htt_tx_desc_frags_table_set(htt_pdev_handle pdev, + void *htt_tx_desc, + qdf_dma_addr_t paddr, + qdf_dma_addr_t frag_desc_paddr, + int reset) +{ + uint32_t *fragmentation_descr_field_ptr; + + fragmentation_descr_field_ptr = (uint32_t *) + ((uint32_t *) htt_tx_desc) + + HTT_TX_DESC_FRAGS_DESC_PADDR_OFFSET_DWORD; + if (reset) { +#if defined(HELIUMPLUS) + *fragmentation_descr_field_ptr = frag_desc_paddr; +#else + *fragmentation_descr_field_ptr = + htt_tx_get_paddr(pdev, htt_tx_desc) + HTT_TX_DESC_LEN; +#endif + } else { + *fragmentation_descr_field_ptr = paddr; + } +} + +void htt_tx_pending_discard(htt_pdev_handle pdev) +{ + htc_flush_surprise_remove(pdev->htc_pdev); +} + +static qdf_dma_addr_t htt_tx_get_paddr(htt_pdev_handle pdev, + char *target_vaddr) +{ + uint16_t i; + struct qdf_mem_dma_page_t *page_info = NULL; + uint64_t offset; + + for (i = 0; i < pdev->tx_descs.desc_pages.num_pages; i++) { + page_info = pdev->tx_descs.desc_pages.dma_pages + i; + if (!page_info->page_v_addr_start) { + qdf_assert(0); + return 0; + } + if ((target_vaddr >= page_info->page_v_addr_start) && + (target_vaddr <= page_info->page_v_addr_end)) + break; + } + + if (!page_info) { + ol_txrx_err("invalid page_info"); + return 0; + } + + offset = (uint64_t)(target_vaddr - page_info->page_v_addr_start); + return page_info->page_p_addr + offset; +} + +#endif + +/*--- descriptor allocation functions ---------------------------------------*/ + +void *htt_tx_desc_alloc(htt_pdev_handle pdev, qdf_dma_addr_t *paddr, + uint16_t index) +{ + struct htt_host_tx_desc_t *htt_host_tx_desc; /* includes HTC hdr */ + struct htt_tx_msdu_desc_t *htt_tx_desc; /* doesn't include HTC hdr */ + + htt_host_tx_desc = (struct htt_host_tx_desc_t *)pdev->tx_descs.freelist; + if (!htt_host_tx_desc) + return NULL; /* pool is exhausted */ + + htt_tx_desc = &htt_host_tx_desc->align32.tx_desc; + + if (pdev->tx_descs.freelist) { + pdev->tx_descs.freelist = + *((uint32_t **) pdev->tx_descs.freelist); + pdev->tx_descs.alloc_cnt++; + } + /* + * For LL, set up the fragmentation descriptor address. + * Currently, this HTT tx desc allocation is performed once up front. + * If this is changed to have the allocation done during tx, then it + * would be helpful to have separate htt_tx_desc_alloc functions for + * HL vs. LL, to remove the below conditional branch. + */ + htt_tx_set_frag_desc_addr(pdev, htt_tx_desc, index); + + /* + * Include the headroom for the HTC frame header when specifying the + * physical address for the HTT tx descriptor. + */ + *paddr = (qdf_dma_addr_t)htt_tx_get_paddr(pdev, + (char *)htt_host_tx_desc); + /* + * The allocated tx descriptor space includes headroom for a + * HTC frame header. Hide this headroom, so that we don't have + * to jump past the headroom each time we program a field within + * the tx desc, but only once when we download the tx desc (and + * the headroom) to the target via HTC. + * Skip past the headroom and return the address of the HTT tx desc. + */ + return (void *)htt_tx_desc; +} + +void htt_tx_desc_free(htt_pdev_handle pdev, void *tx_desc) +{ + char *htt_host_tx_desc = tx_desc; + /* rewind over the HTC frame header space */ + htt_host_tx_desc -= + offsetof(struct htt_host_tx_desc_t, align32.tx_desc); + *((uint32_t **) htt_host_tx_desc) = pdev->tx_descs.freelist; + pdev->tx_descs.freelist = (uint32_t *) htt_host_tx_desc; + pdev->tx_descs.alloc_cnt--; +} + +/*--- descriptor field access methods ---------------------------------------*/ + +/* PUT THESE AS inline IN ol_htt_tx_api.h */ + +void htt_tx_desc_flag_postponed(htt_pdev_handle pdev, void *desc) +{ +} + +void htt_tx_desc_flag_batch_more(htt_pdev_handle pdev, void *desc) +{ +} + +/*--- tx send function ------------------------------------------------------*/ + +#ifdef ATH_11AC_TXCOMPACT + +/* + * Scheduling the Queued packets in HTT which could not be sent out + * because of No CE desc + */ +void htt_tx_sched(htt_pdev_handle pdev) +{ + qdf_nbuf_t msdu; + int download_len = pdev->download_len; + int packet_len; + + HTT_TX_NBUF_QUEUE_REMOVE(pdev, msdu); + while (msdu) { + int not_accepted; + /* packet length includes HTT tx desc frag added above */ + packet_len = qdf_nbuf_len(msdu); + if (packet_len < download_len) { + /* + * This case of packet length being less than the + * nominal download length can happen for a couple + * of reasons: + * In HL, the nominal download length is a large + * artificial value. + * In LL, the frame may not have the optional header + * fields accounted for in the nominal download size + * (LLC/SNAP header, IPv4 or IPv6 header). + */ + download_len = packet_len; + } + + not_accepted = + htc_send_data_pkt(pdev->htc_pdev, msdu, + pdev->htc_tx_endpoint, + download_len); + if (not_accepted) { + HTT_TX_NBUF_QUEUE_INSERT_HEAD(pdev, msdu); + return; + } + HTT_TX_NBUF_QUEUE_REMOVE(pdev, msdu); + } +} + +int htt_tx_send_std(htt_pdev_handle pdev, qdf_nbuf_t msdu, uint16_t msdu_id) +{ + + int download_len = pdev->download_len; + + int packet_len; + + /* packet length includes HTT tx desc frag added above */ + packet_len = qdf_nbuf_len(msdu); + if (packet_len < download_len) { + /* + * This case of packet length being less than the nominal + * download length can happen for a couple of reasons: + * In HL, the nominal download length is a large artificial + * value. + * In LL, the frame may not have the optional header fields + * accounted for in the nominal download size (LLC/SNAP header, + * IPv4 or IPv6 header). + */ + download_len = packet_len; + } + + if (QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_EXT_HEADER(msdu)) + download_len += sizeof(struct htt_tx_msdu_desc_ext_t); + + + QDF_NBUF_UPDATE_TX_PKT_COUNT(msdu, QDF_NBUF_TX_PKT_HTT); + DPTRACE(qdf_dp_trace(msdu, QDF_DP_TRACE_HTT_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(msdu), + sizeof(qdf_nbuf_data(msdu)), QDF_TX)); + if (qdf_nbuf_queue_len(&pdev->txnbufq) > 0) { + HTT_TX_NBUF_QUEUE_ADD(pdev, msdu); + htt_tx_sched(pdev); + return 0; + } + + if (htc_send_data_pkt(pdev->htc_pdev, msdu, + pdev->htc_tx_endpoint, download_len)) { + HTT_TX_NBUF_QUEUE_ADD(pdev, msdu); + } + + return 0; /* success */ + +} + +#ifndef CONFIG_HL_SUPPORT +#ifdef FEATURE_RUNTIME_PM +/** + * htt_tx_resume_handler() - resume callback for the htt endpoint + * @context: a pointer to the htt context + * + * runs htt_tx_sched. + */ +void htt_tx_resume_handler(void *context) +{ + struct htt_pdev_t *pdev = (struct htt_pdev_t *) context; + + htt_tx_sched(pdev); +} +#else +void +htt_tx_resume_handler(void *context) { } +#endif +#endif + +qdf_nbuf_t +htt_tx_send_batch(htt_pdev_handle pdev, qdf_nbuf_t head_msdu, int num_msdus) +{ + qdf_print("Not apply to LL"); + qdf_assert(0); + return head_msdu; + +} + +int +htt_tx_send_nonstd(htt_pdev_handle pdev, + qdf_nbuf_t msdu, + uint16_t msdu_id, enum htt_pkt_type pkt_type) +{ + int download_len; + + /* + * The pkt_type could be checked to see what L2 header type is present, + * and then the L2 header could be examined to determine its length. + * But for simplicity, just use the maximum possible header size, + * rather than computing the actual header size. + */ + download_len = sizeof(struct htt_host_tx_desc_t) + + HTT_TX_HDR_SIZE_OUTER_HDR_MAX /* worst case */ + + HTT_TX_HDR_SIZE_802_1Q + + HTT_TX_HDR_SIZE_LLC_SNAP + + ol_cfg_tx_download_size(pdev->ctrl_pdev); + qdf_assert(download_len <= pdev->download_len); + return htt_tx_send_std(pdev, msdu, msdu_id); +} + +#ifndef QCA_TX_PADDING_CREDIT_SUPPORT +int htt_tx_padding_credit_update_handler(void *context, int pad_credit) +{ + return 1; +} +#endif + +#else /*ATH_11AC_TXCOMPACT */ + +#ifdef QCA_TX_PADDING_CREDIT_SUPPORT +static int htt_tx_padding_credit_update(htt_pdev_handle htt_pdev, + int pad_credit) +{ + int ret = 0; + + if (pad_credit) + qdf_atomic_add(pad_credit, + &htt_pdev->txrx_pdev->pad_reserve_tx_credit); + + ret = qdf_atomic_read(&htt_pdev->txrx_pdev->pad_reserve_tx_credit); + + return ret; +} + +int htt_tx_padding_credit_update_handler(void *context, int pad_credit) +{ + struct htt_pdev_t *htt_pdev = (struct htt_pdev_t *)context; + + return htt_tx_padding_credit_update(htt_pdev, pad_credit); +} +#else +int htt_tx_padding_credit_update_handler(void *context, int pad_credit) +{ + return 1; +} +#endif + +#ifdef QCA_TX_HTT2_SUPPORT +static inline HTC_ENDPOINT_ID +htt_tx_htt2_get_ep_id(htt_pdev_handle pdev, qdf_nbuf_t msdu) +{ + /* + * TX HTT2 service mainly for small sized frame and check if + * this candidate frame allow or not. + */ + if ((pdev->htc_tx_htt2_endpoint != ENDPOINT_UNUSED) && + qdf_nbuf_get_tx_parallel_dnload_frm(msdu) && + (qdf_nbuf_len(msdu) < pdev->htc_tx_htt2_max_size)) + return pdev->htc_tx_htt2_endpoint; + else + return pdev->htc_tx_endpoint; +} +#else +#define htt_tx_htt2_get_ep_id(pdev, msdu) (pdev->htc_tx_endpoint) +#endif /* QCA_TX_HTT2_SUPPORT */ + +static inline int +htt_tx_send_base(htt_pdev_handle pdev, + qdf_nbuf_t msdu, + uint16_t msdu_id, int download_len, uint8_t more_data) +{ + struct htt_host_tx_desc_t *htt_host_tx_desc; + struct htt_htc_pkt *pkt; + int packet_len; + HTC_ENDPOINT_ID ep_id; + + /* + * The HTT tx descriptor was attached as the prefix fragment to the + * msdu netbuf during the call to htt_tx_desc_init. + * Retrieve it so we can provide its HTC header space to HTC. + */ + htt_host_tx_desc = (struct htt_host_tx_desc_t *) + qdf_nbuf_get_frag_vaddr(msdu, 0); + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -ENOBUFS; /* failure */ + + pkt->msdu_id = msdu_id; + pkt->pdev_ctxt = pdev->txrx_pdev; + + /* packet length includes HTT tx desc frag added above */ + packet_len = qdf_nbuf_len(msdu); + if (packet_len < download_len) { + /* + * This case of packet length being less than the nominal + * download length can happen for a couple reasons: + * In HL, the nominal download length is a large artificial + * value. + * In LL, the frame may not have the optional header fields + * accounted for in the nominal download size (LLC/SNAP header, + * IPv4 or IPv6 header). + */ + download_len = packet_len; + } + + ep_id = htt_tx_htt2_get_ep_id(pdev, msdu); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + pdev->tx_send_complete_part2, + (unsigned char *)htt_host_tx_desc, + download_len - HTC_HDR_LENGTH, + ep_id, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msdu); + + QDF_NBUF_UPDATE_TX_PKT_COUNT(msdu, QDF_NBUF_TX_PKT_HTT); + DPTRACE(qdf_dp_trace(msdu, QDF_DP_TRACE_HTT_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(msdu), + sizeof(qdf_nbuf_data(msdu)), QDF_TX)); + htc_send_data_pkt(pdev->htc_pdev, &pkt->htc_pkt, more_data); + + return 0; /* success */ +} + +qdf_nbuf_t +htt_tx_send_batch(htt_pdev_handle pdev, qdf_nbuf_t head_msdu, int num_msdus) +{ + qdf_nbuf_t rejected = NULL; + uint16_t *msdu_id_storage; + uint16_t msdu_id; + qdf_nbuf_t msdu; + + /* + * FOR NOW, iterate through the batch, sending the frames singly. + * Eventually HTC and HIF should be able to accept a batch of + * data frames rather than singles. + */ + msdu = head_msdu; + while (num_msdus--) { + qdf_nbuf_t next_msdu = qdf_nbuf_next(msdu); + + msdu_id_storage = ol_tx_msdu_id_storage(msdu); + msdu_id = *msdu_id_storage; + + /* htt_tx_send_base returns 0 as success and 1 as failure */ + if (htt_tx_send_base(pdev, msdu, msdu_id, pdev->download_len, + num_msdus)) { + qdf_nbuf_set_next(msdu, rejected); + rejected = msdu; + } + msdu = next_msdu; + } + return rejected; +} + +int +htt_tx_send_nonstd(htt_pdev_handle pdev, + qdf_nbuf_t msdu, + uint16_t msdu_id, enum htt_pkt_type pkt_type) +{ + int download_len; + + /* + * The pkt_type could be checked to see what L2 header type is present, + * and then the L2 header could be examined to determine its length. + * But for simplicity, just use the maximum possible header size, + * rather than computing the actual header size. + */ + download_len = sizeof(struct htt_host_tx_desc_t) + + HTT_TX_HDR_SIZE_OUTER_HDR_MAX /* worst case */ + + HTT_TX_HDR_SIZE_802_1Q + + HTT_TX_HDR_SIZE_LLC_SNAP + + ol_cfg_tx_download_size(pdev->ctrl_pdev); + return htt_tx_send_base(pdev, msdu, msdu_id, download_len, 0); +} + +int htt_tx_send_std(htt_pdev_handle pdev, qdf_nbuf_t msdu, uint16_t msdu_id) +{ + return htt_tx_send_base(pdev, msdu, msdu_id, pdev->download_len, 0); +} + +#endif /*ATH_11AC_TXCOMPACT */ + +#if defined(HTT_DBG) +void htt_tx_desc_display(void *tx_desc) +{ + struct htt_tx_msdu_desc_t *htt_tx_desc; + + htt_tx_desc = (struct htt_tx_msdu_desc_t *)tx_desc; + + /* only works for little-endian */ + qdf_debug("HTT tx desc (@ %pK):", htt_tx_desc); + qdf_debug(" msg type = %d", htt_tx_desc->msg_type); + qdf_debug(" pkt subtype = %d", htt_tx_desc->pkt_subtype); + qdf_debug(" pkt type = %d", htt_tx_desc->pkt_type); + qdf_debug(" vdev ID = %d", htt_tx_desc->vdev_id); + qdf_debug(" ext TID = %d", htt_tx_desc->ext_tid); + qdf_debug(" postponed = %d", htt_tx_desc->postponed); + qdf_debug(" extension = %d", htt_tx_desc->extension); + qdf_debug(" cksum_offload = %d", htt_tx_desc->cksum_offload); + qdf_debug(" tx_compl_req= %d", htt_tx_desc->tx_compl_req); + qdf_debug(" length = %d", htt_tx_desc->len); + qdf_debug(" id = %d", htt_tx_desc->id); +#if HTT_PADDR64 + qdf_debug(" frag desc addr.lo = %#x", + htt_tx_desc->frags_desc_ptr.lo); + qdf_debug(" frag desc addr.hi = %#x", + htt_tx_desc->frags_desc_ptr.hi); +#else /* ! HTT_PADDR64 */ + qdf_debug(" frag desc addr = %#x", htt_tx_desc->frags_desc_ptr); +#endif /* HTT_PADDR64 */ + qdf_debug(" peerid = %d", htt_tx_desc->peerid); + qdf_debug(" chanfreq = %d", htt_tx_desc->chanfreq); +} +#endif + +#ifdef IPA_OFFLOAD +#ifdef QCA_WIFI_3_0 +/** + * htt_tx_ipa_uc_wdi_tx_buf_alloc() - Alloc WDI TX buffers + * @pdev: htt context + * @uc_tx_buf_sz: TX buffer size + * @uc_tx_buf_cnt: TX Buffer count + * @uc_tx_partition_base: IPA UC TX partition base value + * + * Allocate WDI TX buffers. Also note Rome supports only WDI 1.0. + * + * Return: 0 success + */ + +static int htt_tx_ipa_uc_wdi_tx_buf_alloc(struct htt_pdev_t *pdev, + unsigned int uc_tx_buf_sz, + unsigned int uc_tx_buf_cnt, + unsigned int uc_tx_partition_base) +{ + unsigned int tx_buffer_count; + unsigned int tx_buffer_count_pwr2; + qdf_dma_addr_t buffer_paddr; + uint32_t *header_ptr; + target_paddr_t *ring_vaddr; + uint16_t idx; + qdf_mem_info_t *mem_map_table = NULL, *mem_info = NULL; + qdf_shared_mem_t *shared_tx_buffer; + + ring_vaddr = (target_paddr_t *)pdev->ipa_uc_tx_rsc.tx_comp_ring->vaddr; + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + mem_map_table = qdf_mem_map_table_alloc(uc_tx_buf_cnt); + if (!mem_map_table) { + qdf_print("Failed to allocate memory"); + return 0; + } + mem_info = mem_map_table; + } + + /* Allocate TX buffers as many as possible */ + for (tx_buffer_count = 0; + tx_buffer_count < (uc_tx_buf_cnt - 1); tx_buffer_count++) { + + shared_tx_buffer = qdf_mem_shared_mem_alloc(pdev->osdev, + uc_tx_buf_sz); + if (!shared_tx_buffer || !shared_tx_buffer->vaddr) { + qdf_print("IPA WDI TX buffer alloc fail %d allocated\n", + tx_buffer_count); + goto pwr2; + } + + header_ptr = shared_tx_buffer->vaddr; + buffer_paddr = qdf_mem_get_dma_addr(pdev->osdev, + &shared_tx_buffer->mem_info); + + /* HTT control header */ + *header_ptr = HTT_IPA_UC_OFFLOAD_TX_HEADER_DEFAULT; + header_ptr++; + + /* PKT ID */ + *header_ptr |= ((uint16_t) uc_tx_partition_base + + tx_buffer_count) << 16; + + header_ptr++; + + /* Frag Desc Pointer */ + /* 64bits descriptor, Low 32bits */ + *header_ptr = qdf_get_lower_32_bits(buffer_paddr + + IPA_UC_TX_BUF_FRAG_DESC_OFFSET); + header_ptr++; + + /* 64bits descriptor, high 32bits */ + *header_ptr = qdf_get_upper_32_bits(buffer_paddr) & + IPA_UC_TX_BUF_PADDR_HI_MASK; + header_ptr++; + + /* chanreq, peerid */ + *header_ptr = 0xFFFFFFFF; + header_ptr++; + + /* FRAG Header */ + /* 6 words TSO header */ + header_ptr += IPA_UC_TX_BUF_TSO_HDR_SIZE; + *header_ptr = buffer_paddr + IPA_UC_TX_BUF_FRAG_HDR_OFFSET; + + *ring_vaddr = buffer_paddr; + pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[tx_buffer_count] = + shared_tx_buffer; + + /* Memory barrier to ensure actual value updated */ + + ring_vaddr++; + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + *mem_info = pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[ + tx_buffer_count]->mem_info; + mem_info++; + } + } + +pwr2: + /* + * Tx complete ring buffer count should be power of 2. + * So, allocated Tx buffer count should be one less than ring buffer + * size. + */ + tx_buffer_count_pwr2 = qdf_rounddown_pow_of_two(tx_buffer_count + 1) + - 1; + if (tx_buffer_count > tx_buffer_count_pwr2) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "%s: Allocated Tx buffer count %d is rounded down to %d", + __func__, tx_buffer_count, tx_buffer_count_pwr2); + + /* Free over allocated buffers below power of 2 */ + for (idx = tx_buffer_count_pwr2; idx < tx_buffer_count; idx++) { + if (pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[idx]) { + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[ + idx]); + pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[idx] = + NULL; + } + } + } + + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + cds_smmu_map_unmap(true, tx_buffer_count_pwr2, + mem_map_table); + qdf_mem_free(mem_map_table); + } + + return tx_buffer_count_pwr2; +} + +/** + * htt_tx_buf_pool_free() - Free tx buffer pool + * @pdev: htt context + * + * Free memory in tx buffer pool + * + * Return: 0 success + */ +static void htt_tx_buf_pool_free(struct htt_pdev_t *pdev) +{ + uint16_t idx; + qdf_mem_info_t *mem_map_table = NULL, *mem_info = NULL; + uint32_t num_unmapped = 0; + + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + mem_map_table = qdf_mem_map_table_alloc( + pdev->ipa_uc_tx_rsc.alloc_tx_buf_cnt); + if (!mem_map_table) { + qdf_print("Failed to allocate memory"); + return; + } + mem_info = mem_map_table; + } + + for (idx = 0; idx < pdev->ipa_uc_tx_rsc.alloc_tx_buf_cnt; idx++) { + if (pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[idx]) { + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + *mem_info = pdev->ipa_uc_tx_rsc. + tx_buf_pool_strg[idx]->mem_info; + mem_info++; + num_unmapped++; + } + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_tx_rsc. + tx_buf_pool_strg[idx]); + pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[idx] = NULL; + } + } + + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + if (num_unmapped) + cds_smmu_map_unmap(false, num_unmapped, mem_map_table); + qdf_mem_free(mem_map_table); + } +} +#else +static int htt_tx_ipa_uc_wdi_tx_buf_alloc(struct htt_pdev_t *pdev, + unsigned int uc_tx_buf_sz, + unsigned int uc_tx_buf_cnt, + unsigned int uc_tx_partition_base) +{ + unsigned int tx_buffer_count; + unsigned int tx_buffer_count_pwr2; + qdf_dma_addr_t buffer_paddr; + uint32_t *header_ptr; + uint32_t *ring_vaddr; + uint16_t idx; + qdf_mem_info_t *mem_map_table = NULL, *mem_info = NULL; + qdf_shared_mem_t *shared_tx_buffer; + + ring_vaddr = pdev->ipa_uc_tx_rsc.tx_comp_ring->vaddr; + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + mem_map_table = qdf_mem_map_table_alloc(uc_tx_buf_cnt); + if (!mem_map_table) { + qdf_print("Failed to allocate memory"); + return 0; + } + mem_info = mem_map_table; + } + + /* Allocate TX buffers as many as possible */ + for (tx_buffer_count = 0; + tx_buffer_count < (uc_tx_buf_cnt - 1); tx_buffer_count++) { + shared_tx_buffer = qdf_mem_shared_mem_alloc(pdev->osdev, + uc_tx_buf_sz); + if (!shared_tx_buffer || !shared_tx_buffer->vaddr) { + qdf_print("TX BUF alloc fail, loop index: %d", + tx_buffer_count); + goto pwr2; + } + + /* Init buffer */ + qdf_mem_zero(shared_tx_buffer->vaddr, uc_tx_buf_sz); + header_ptr = (uint32_t *)shared_tx_buffer->vaddr; + buffer_paddr = qdf_mem_get_dma_addr(pdev->osdev, + &shared_tx_buffer->mem_info); + + /* HTT control header */ + *header_ptr = HTT_IPA_UC_OFFLOAD_TX_HEADER_DEFAULT; + header_ptr++; + + /* PKT ID */ + *header_ptr |= ((uint16_t) uc_tx_partition_base + + tx_buffer_count) << 16; + header_ptr++; + + /*FRAG Desc Pointer */ + *header_ptr = (uint32_t) (buffer_paddr + + IPA_UC_TX_BUF_FRAG_DESC_OFFSET); + header_ptr++; + *header_ptr = 0xFFFFFFFF; + + /* FRAG Header */ + header_ptr++; + *header_ptr = buffer_paddr + IPA_UC_TX_BUF_FRAG_HDR_OFFSET; + + *ring_vaddr = buffer_paddr; + pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[tx_buffer_count] = + shared_tx_buffer; + /* Memory barrier to ensure actual value updated */ + + ring_vaddr++; + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + *mem_info = pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[ + tx_buffer_count]->mem_info; + mem_info++; + } + } + +pwr2: + /* + * Tx complete ring buffer count should be power of 2. + * So, allocated Tx buffer count should be one less than ring buffer + * size. + */ + tx_buffer_count_pwr2 = qdf_rounddown_pow_of_two(tx_buffer_count + 1) + - 1; + if (tx_buffer_count > tx_buffer_count_pwr2) { + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "%s: Allocated Tx buffer count %d is rounded down to %d", + __func__, tx_buffer_count, tx_buffer_count_pwr2); + + /* Free over allocated buffers below power of 2 */ + for (idx = tx_buffer_count_pwr2; idx < tx_buffer_count; idx++) { + if (pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[idx]) { + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_tx_rsc. + tx_buf_pool_strg[idx]); + pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[idx] = + NULL; + } + } + } + + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + cds_smmu_map_unmap(true, tx_buffer_count_pwr2, + mem_map_table); + qdf_mem_free(mem_map_table); + } + + return tx_buffer_count_pwr2; +} + +static void htt_tx_buf_pool_free(struct htt_pdev_t *pdev) +{ + uint16_t idx; + qdf_mem_info_t *mem_map_table = NULL, *mem_info = NULL; + uint32_t num_unmapped = 0; + + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + mem_map_table = qdf_mem_map_table_alloc( + pdev->ipa_uc_tx_rsc.alloc_tx_buf_cnt); + if (!mem_map_table) { + qdf_print("Failed to allocate memory"); + return; + } + mem_info = mem_map_table; + } + + for (idx = 0; idx < pdev->ipa_uc_tx_rsc.alloc_tx_buf_cnt; idx++) { + if (pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[idx]) { + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + *mem_info = pdev->ipa_uc_tx_rsc. + tx_buf_pool_strg[idx]->mem_info; + mem_info++; + num_unmapped++; + } + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_tx_rsc. + tx_buf_pool_strg[idx]); + pdev->ipa_uc_tx_rsc.tx_buf_pool_strg[idx] = NULL; + } + } + + if (qdf_mem_smmu_s1_enabled(pdev->osdev)) { + if (num_unmapped) + cds_smmu_map_unmap(false, num_unmapped, mem_map_table); + qdf_mem_free(mem_map_table); + } +} +#endif + +/** + * htt_tx_ipa_uc_attach() - attach htt ipa uc tx resource + * @pdev: htt context + * @uc_tx_buf_sz: single tx buffer size + * @uc_tx_buf_cnt: total tx buffer count + * @uc_tx_partition_base: tx buffer partition start + * + * Return: 0 success + * ENOBUFS No memory fail + */ +int htt_tx_ipa_uc_attach(struct htt_pdev_t *pdev, + unsigned int uc_tx_buf_sz, + unsigned int uc_tx_buf_cnt, + unsigned int uc_tx_partition_base) +{ + int return_code = 0; + unsigned int tx_comp_ring_size; + + /* Allocate CE Write Index WORD */ + pdev->ipa_uc_tx_rsc.tx_ce_idx = + qdf_mem_shared_mem_alloc(pdev->osdev, 4); + if (!pdev->ipa_uc_tx_rsc.tx_ce_idx) { + qdf_print("Unable to allocate memory for IPA tx ce idx"); + return -ENOBUFS; + } + + /* Allocate TX COMP Ring */ + tx_comp_ring_size = uc_tx_buf_cnt * sizeof(target_paddr_t); + pdev->ipa_uc_tx_rsc.tx_comp_ring = + qdf_mem_shared_mem_alloc(pdev->osdev, + tx_comp_ring_size); + if (!pdev->ipa_uc_tx_rsc.tx_comp_ring || + !pdev->ipa_uc_tx_rsc.tx_comp_ring->vaddr) { + qdf_print("TX COMP ring alloc fail"); + return_code = -ENOBUFS; + goto free_tx_ce_idx; + } + + /* Allocate TX BUF vAddress Storage */ + pdev->ipa_uc_tx_rsc.tx_buf_pool_strg = + qdf_mem_malloc(uc_tx_buf_cnt * + sizeof(*pdev->ipa_uc_tx_rsc.tx_buf_pool_strg)); + if (!pdev->ipa_uc_tx_rsc.tx_buf_pool_strg) { + return_code = -ENOBUFS; + goto free_tx_comp_base; + } + + qdf_mem_zero(pdev->ipa_uc_tx_rsc.tx_buf_pool_strg, + uc_tx_buf_cnt * + sizeof(*pdev->ipa_uc_tx_rsc.tx_buf_pool_strg)); + + pdev->ipa_uc_tx_rsc.alloc_tx_buf_cnt = htt_tx_ipa_uc_wdi_tx_buf_alloc( + pdev, uc_tx_buf_sz, uc_tx_buf_cnt, uc_tx_partition_base); + + + return 0; + +free_tx_comp_base: + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_tx_rsc.tx_comp_ring); +free_tx_ce_idx: + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_tx_rsc.tx_ce_idx); + + return return_code; +} + +/** + * htt_tx_ipa_uc_detach() - Free WDI TX resources + * @pdev: htt context + * + * Remove IPA WDI TX resources during device detach + * Free all of allocated resources + * + * Return: 0 success + */ +int htt_tx_ipa_uc_detach(struct htt_pdev_t *pdev) +{ + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_tx_rsc.tx_ce_idx); + qdf_mem_shared_mem_free(pdev->osdev, + pdev->ipa_uc_tx_rsc.tx_comp_ring); + + /* Free each single buffer */ + htt_tx_buf_pool_free(pdev); + + /* Free storage */ + qdf_mem_free(pdev->ipa_uc_tx_rsc.tx_buf_pool_strg); + + return 0; +} +#endif /* IPA_OFFLOAD */ + +#if defined(FEATURE_TSO) && defined(HELIUMPLUS) +void +htt_tx_desc_fill_tso_info(htt_pdev_handle pdev, void *desc, + struct qdf_tso_info_t *tso_info) +{ + u_int32_t *word; + int i; + struct qdf_tso_seg_elem_t *tso_seg = tso_info->curr_seg; + struct msdu_ext_desc_t *msdu_ext_desc = (struct msdu_ext_desc_t *)desc; + + word = (u_int32_t *)(desc); + + /* Initialize the TSO flags per MSDU */ + msdu_ext_desc->tso_flags = + tso_seg->seg.tso_flags; + + /* First 24 bytes (6*4) contain the TSO flags */ + TSO_DEBUG("%s seq# %u l2 len %d, ip len %d\n", + __func__, + tso_seg->seg.tso_flags.tcp_seq_num, + tso_seg->seg.tso_flags.l2_len, + tso_seg->seg.tso_flags.ip_len); + TSO_DEBUG("%s flags 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + __func__, + *word, + *(word + 1), + *(word + 2), + *(word + 3), + *(word + 4), + *(word + 5)); + + word += 6; + + for (i = 0; i < tso_seg->seg.num_frags; i++) { + uint32_t lo = 0; + uint32_t hi = 0; + + qdf_dmaaddr_to_32s(tso_seg->seg.tso_frags[i].paddr, + &lo, &hi); + /* [31:0] first 32 bits of the buffer pointer */ + *word = lo; + word++; + /* [15:0] the upper 16 bits of the first buffer pointer */ + /* [31:16] length of the first buffer */ + *word = (tso_seg->seg.tso_frags[i].length << 16) | hi; + word++; + TSO_DEBUG("%s frag[%d] ptr_low 0x%x ptr_hi 0x%x len %u\n", + __func__, i, + msdu_ext_desc->frags[i].u.frag32.ptr_low, + msdu_ext_desc->frags[i].u.frag32.ptr_hi, + msdu_ext_desc->frags[i].u.frag32.len); + } + + if (tso_seg->seg.num_frags < FRAG_NUM_MAX) + *word = 0; + qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_FILLHTTSEG); +} +#endif /* FEATURE_TSO */ + +/** + * htt_get_ext_tid() - get ext_tid value + * @type: extension header type + * @ext_header_data: header data + * @msdu_info: msdu info + * + * Return: ext_tid value + */ +static inline +int htt_get_ext_tid(enum extension_header_type type, + void *ext_header_data, struct htt_msdu_info_t *msdu_info) +{ + if (type == OCB_MODE_EXT_HEADER && ext_header_data) + return ((struct ocb_tx_ctrl_hdr_t *)ext_header_data)->ext_tid; + else + return msdu_info->info.ext_tid; +} + +/** + * htt_get_channel_freq() - get channel frequency + * @type: extension header type + * @ext_header_data: header data + * + * Return: channel frequency number + */ +static inline +int htt_get_channel_freq(enum extension_header_type type, + void *ext_header_data) +{ + if (type == OCB_MODE_EXT_HEADER && ext_header_data) + return ((struct ocb_tx_ctrl_hdr_t *)ext_header_data) + ->channel_freq; + else + return HTT_INVALID_CHANNEL; +} + +/** + * htt_fill_ocb_ext_header() - fill OCB extension header + * @msdu: network buffer + * @local_desc_ext: extension descriptor + * @type: extension header type + * @ext_header_data: header data + * @is_dsrc: is dsrc is eenabled or not + * + * Return: none + */ +#ifdef WLAN_FEATURE_DSRC +static +void htt_fill_ocb_ext_header(qdf_nbuf_t msdu, + struct htt_tx_msdu_desc_ext_t *local_desc_ext, + enum extension_header_type type, + void *ext_header_data) +{ + struct ocb_tx_ctrl_hdr_t *tx_ctrl = + (struct ocb_tx_ctrl_hdr_t *)ext_header_data; + + if (tx_ctrl->all_flags == 0) + return; + /* + * Copy the info that was read from TX control header from the + * user application to the extended HTT header. + * First copy everything + * to a local temp structure, and then copy everything to the + * actual uncached structure in one go to save memory writes. + */ + local_desc_ext->valid_pwr = tx_ctrl->valid_pwr; + local_desc_ext->valid_mcs_mask = tx_ctrl->valid_datarate; + local_desc_ext->valid_retries = tx_ctrl->valid_retries; + local_desc_ext->valid_expire_tsf = tx_ctrl->valid_expire_tsf; + local_desc_ext->valid_chainmask = tx_ctrl->valid_chain_mask; + + local_desc_ext->pwr = tx_ctrl->pwr; + if (tx_ctrl->valid_datarate && + tx_ctrl->datarate <= htt_ofdm_datarate_max) + local_desc_ext->mcs_mask = + (1 << (tx_ctrl->datarate + 4)); + local_desc_ext->retry_limit = tx_ctrl->retry_limit; + local_desc_ext->expire_tsf_lo = tx_ctrl->expire_tsf_lo; + local_desc_ext->expire_tsf_hi = tx_ctrl->expire_tsf_hi; + local_desc_ext->chain_mask = tx_ctrl->chain_mask; + local_desc_ext->is_dsrc = 1; + qdf_nbuf_push_head(msdu, sizeof(struct htt_tx_msdu_desc_ext_t)); + qdf_mem_copy(qdf_nbuf_data(msdu), local_desc_ext, + sizeof(struct htt_tx_msdu_desc_ext_t)); + QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_EXT_HEADER(msdu) = 1; +} +#else +static +void htt_fill_ocb_ext_header(qdf_nbuf_t msdu, + struct htt_tx_msdu_desc_ext_t *local_desc_ext, + enum extension_header_type type, + void *ext_header_data) +{ +} +#endif + +/** + * htt_fill_wisa_ext_header() - fill WiSA extension header + * @msdu: network buffer + * @local_desc_ext: extension descriptor + * @type: extension header type + * @ext_header_data: header data + * + * Return: none + */ +static +void htt_fill_wisa_ext_header(qdf_nbuf_t msdu, + struct htt_tx_msdu_desc_ext_t *local_desc_ext, + enum extension_header_type type, void *ext_header_data) +{ + void *qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + QDF_STATUS status; + + if (!qdf_ctx) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: qdf_ctx is NULL", __func__); + return; + } + + local_desc_ext->valid_mcs_mask = 1; + if (WISA_MODE_EXT_HEADER_6MBPS == type) + local_desc_ext->mcs_mask = htt_ofdm_datarate_6_mbps; + else + local_desc_ext->mcs_mask = htt_ofdm_datarate_24_mbps; + local_desc_ext->valid_nss_mask = 1; + local_desc_ext->nss_mask = 1; + local_desc_ext->valid_bandwidth = 1; + local_desc_ext->bandwidth_mask = htt_tx_bandwidth_20MHz; + local_desc_ext->valid_guard_interval = 1; + local_desc_ext->guard_interval = htt_tx_guard_interval_regular; + + /* + * Do dma_unmap and dma_map again if already mapped + * as adding extra bytes in skb + */ + if (QDF_NBUF_CB_PADDR(msdu) != 0) + qdf_nbuf_unmap_single(qdf_ctx, msdu, QDF_DMA_TO_DEVICE); + + qdf_nbuf_push_head(msdu, sizeof(struct htt_tx_msdu_desc_ext_t)); + qdf_mem_copy(qdf_nbuf_data(msdu), local_desc_ext, + sizeof(struct htt_tx_msdu_desc_ext_t)); + + if (QDF_NBUF_CB_PADDR(msdu) != 0) { + status = qdf_nbuf_map_single(qdf_ctx, msdu, QDF_DMA_TO_DEVICE); + if (qdf_unlikely(status != QDF_STATUS_SUCCESS)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_WARN, + "%s: nbuf map failed", __func__); + return; + } + } + QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_EXT_HEADER(msdu) = 1; +} + +/** + * htt_push_ext_header() - fill extension header + * @msdu: network buffer + * @local_desc_ext: extension descriptor + * @type: extension header type + * @ext_header_data: header data + * @is_dsrc: is dsrc is eenabled or not + * + * Return: none + */ +static +void htt_push_ext_header(qdf_nbuf_t msdu, + struct htt_tx_msdu_desc_ext_t *local_desc_ext, + enum extension_header_type type, void *ext_header_data) +{ + switch (type) { + case OCB_MODE_EXT_HEADER: + htt_fill_ocb_ext_header(msdu, local_desc_ext, + type, ext_header_data); + break; + case WISA_MODE_EXT_HEADER_6MBPS: + case WISA_MODE_EXT_HEADER_24MBPS: + htt_fill_wisa_ext_header(msdu, local_desc_ext, + type, ext_header_data); + break; + default: + QDF_TRACE(QDF_MODULE_ID_HTT, QDF_TRACE_LEVEL_INFO, + "Invalid EXT header type %d\n", type); + break; + } +} + +QDF_STATUS +htt_tx_desc_init(htt_pdev_handle pdev, + void *htt_tx_desc, + qdf_dma_addr_t htt_tx_desc_paddr, + uint16_t msdu_id, + qdf_nbuf_t msdu, struct htt_msdu_info_t *msdu_info, + struct qdf_tso_info_t *tso_info, + void *ext_header_data, + enum extension_header_type type) +{ + uint8_t pkt_type, pkt_subtype = 0, ce_pkt_type = 0; + uint32_t hw_classify = 0, data_attr = 0; + uint32_t *word0, *word1, local_word3; +#if HTT_PADDR64 + uint32_t *word4; +#else /* ! HTT_PADDR64 */ + uint32_t *word3; +#endif /* HTT_PADDR64 */ + uint32_t local_word0, local_word1; + struct htt_host_tx_desc_t *htt_host_tx_desc = + (struct htt_host_tx_desc_t *) + (((char *)htt_tx_desc) - HTT_TX_DESC_VADDR_OFFSET); + bool desc_ext_required = (type != EXT_HEADER_NOT_PRESENT); + int channel_freq; + void *qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + qdf_dma_dir_t dir; + QDF_STATUS status; + + if (qdf_unlikely(!qdf_ctx)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: qdf_ctx is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + if (qdf_unlikely(!msdu_info)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: bad arg: msdu_info is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + if (qdf_unlikely(!tso_info)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: bad arg: tso_info is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + word0 = (uint32_t *) htt_tx_desc; + word1 = word0 + 1; + /* + * word2 is frag desc pointer + * word3 or 4 is peer_id + */ +#if HTT_PADDR64 + word4 = word0 + 4; /* Dword 3 */ +#else /* ! HTT_PADDR64 */ + word3 = word0 + 3; /* Dword 3 */ +#endif /* HTT_PADDR64 */ + + pkt_type = msdu_info->info.l2_hdr_type; + + if (qdf_likely(pdev->cfg.ce_classify_enabled)) { + if (qdf_likely(pkt_type == htt_pkt_type_eth2 || + pkt_type == htt_pkt_type_ethernet)) + qdf_nbuf_tx_info_get(msdu, pkt_type, pkt_subtype, + hw_classify); + + ce_pkt_type = htt_to_ce_pkt_type[pkt_type]; + if (0xffffffff == ce_pkt_type) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH, + "Invalid HTT pkt type %d\n", pkt_type); + return QDF_STATUS_E_INVAL; + } + } + + /* + * HTT Tx Desc is in uncached memory. Used cached writes per word, to + * reduce unnecessary memory access. + */ + + local_word0 = 0; + + HTT_H2T_MSG_TYPE_SET(local_word0, HTT_H2T_MSG_TYPE_TX_FRM); + HTT_TX_DESC_PKT_TYPE_SET(local_word0, pkt_type); + HTT_TX_DESC_PKT_SUBTYPE_SET(local_word0, pkt_subtype); + HTT_TX_DESC_VDEV_ID_SET(local_word0, msdu_info->info.vdev_id); + HTT_TX_DESC_EXT_TID_SET(local_word0, htt_get_ext_tid(type, + ext_header_data, msdu_info)); + HTT_TX_DESC_EXTENSION_SET(local_word0, desc_ext_required); + HTT_TX_DESC_EXT_TID_SET(local_word0, msdu_info->info.ext_tid); + HTT_TX_DESC_CKSUM_OFFLOAD_SET(local_word0, + msdu_info->action.cksum_offload); + if (pdev->cfg.is_high_latency) + HTT_TX_DESC_TX_COMP_SET(local_word0, msdu_info->action. + tx_comp_req); + HTT_TX_DESC_NO_ENCRYPT_SET(local_word0, + msdu_info->action.do_encrypt ? + 0 : 1); + + *word0 = local_word0; + + local_word1 = 0; + + if (tso_info->is_tso) { + uint32_t total_len = tso_info->curr_seg->seg.total_len; + + HTT_TX_DESC_FRM_LEN_SET(local_word1, total_len); + TSO_DEBUG("%s setting HTT TX DESC Len = %d\n", + __func__, total_len); + } else { + HTT_TX_DESC_FRM_LEN_SET(local_word1, qdf_nbuf_len(msdu)); + } + + QDF_BUG(HTT_TX_DESC_FRM_LEN_GET(local_word1) != 0); + + HTT_TX_DESC_FRM_ID_SET(local_word1, msdu_id); + *word1 = local_word1; + + /* + * Initialize peer_id to INVALID_PEER because + * this is NOT Reinjection path + */ + local_word3 = HTT_INVALID_PEER; + channel_freq = htt_get_channel_freq(type, ext_header_data); + if (channel_freq != HTT_INVALID_CHANNEL && channel_freq > 0) + HTT_TX_DESC_CHAN_FREQ_SET(local_word3, channel_freq); +#if HTT_PADDR64 + *word4 = local_word3; +#else /* ! HTT_PADDR64 */ + *word3 = local_word3; +#endif /* HTT_PADDR64 */ + + /* + * If any of the tx control flags are set, then we need the extended + * HTT header. + */ + if (desc_ext_required) { + struct htt_tx_msdu_desc_ext_t local_desc_ext = {0}; + + htt_push_ext_header(msdu, &local_desc_ext, + type, ext_header_data); + } + + /* + * Specify that the data provided by the OS is a bytestream, + * and thus should not be byte-swapped during the HIF download + * even if the host is big-endian. + * There could be extra fragments added before the OS's fragments, + * e.g. for TSO, so it's incorrect to clear the frag 0 wordstream flag. + * Instead, clear the wordstream flag for the final fragment, which + * is certain to be (one of the) fragment(s) provided by the OS. + * Setting the flag for this final fragment suffices for specifying + * all fragments provided by the OS rather than added by the driver. + */ + qdf_nbuf_set_frag_is_wordstream(msdu, qdf_nbuf_get_num_frags(msdu) - 1, + 0); + + if (QDF_NBUF_CB_PADDR(msdu) == 0) { + dir = QDF_NBUF_CB_TX_DMA_BI_MAP(msdu) ? + QDF_DMA_BIDIRECTIONAL : QDF_DMA_TO_DEVICE; + status = qdf_nbuf_map_single(qdf_ctx, msdu, dir); + if (qdf_unlikely(status != QDF_STATUS_SUCCESS)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: nbuf map failed", __func__); + return QDF_STATUS_E_NOMEM; + } + } + + /* store a link to the HTT tx descriptor within the netbuf */ + qdf_nbuf_frag_push_head(msdu, sizeof(struct htt_host_tx_desc_t), + (char *)htt_host_tx_desc, /* virtual addr */ + htt_tx_desc_paddr); + + /* + * Indicate that the HTT header (and HTC header) is a meta-data + * "wordstream", i.e. series of uint32_t, rather than a data + * bytestream. + * This allows the HIF download to byteswap the HTT + HTC headers if + * the host is big-endian, to convert to the target's little-endian + * format. + */ + qdf_nbuf_set_frag_is_wordstream(msdu, 0, 1); + + if (qdf_likely(pdev->cfg.ce_classify_enabled && + (msdu_info->info.l2_hdr_type != htt_pkt_type_mgmt))) { + uint32_t pkt_offset = qdf_nbuf_get_frag_len(msdu, 0); + + data_attr = hw_classify << QDF_CE_TX_CLASSIFY_BIT_S; + data_attr |= ce_pkt_type << QDF_CE_TX_PKT_TYPE_BIT_S; + data_attr |= pkt_offset << QDF_CE_TX_PKT_OFFSET_BIT_S; + } + + qdf_nbuf_data_attr_set(msdu, data_attr); + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + +/** + * htt_tx_group_credit_process() - process group data for + * credit update indication + * @pdev: pointer to htt device. + * @msg_word: htt msg + * + * Return: None + */ +void htt_tx_group_credit_process(struct htt_pdev_t *pdev, u_int32_t *msg_word) +{ + int group_credit_sign; + int32_t group_credit; + u_int32_t group_credit_abs, vdev_id_mask, ac_mask; + u_int8_t group_abs, group_id; + u_int8_t group_offset = 0, more_group_present = 0; + + more_group_present = HTT_TX_CREDIT_TXQ_GRP_GET(*msg_word); + + while (more_group_present) { + /* Parse the Group Data */ + group_id = HTT_TXQ_GROUP_ID_GET(*(msg_word+1 + +group_offset)); + group_credit_abs = + HTT_TXQ_GROUP_CREDIT_COUNT_GET(*(msg_word+1 + +group_offset)); + group_credit_sign = + HTT_TXQ_GROUP_SIGN_GET(*(msg_word+1 + +group_offset)) ? -1 : 1; + group_credit = group_credit_sign * group_credit_abs; + group_abs = HTT_TXQ_GROUP_ABS_GET(*(msg_word+1 + +group_offset)); + + vdev_id_mask = + HTT_TXQ_GROUP_VDEV_ID_MASK_GET(*(msg_word+2 + +group_offset)); + ac_mask = HTT_TXQ_GROUP_AC_MASK_GET(*(msg_word+2 + +group_offset)); + + ol_txrx_update_tx_queue_groups(pdev->txrx_pdev, group_id, + group_credit, group_abs, + vdev_id_mask, ac_mask); + more_group_present = HTT_TXQ_GROUP_EXT_GET(*(msg_word+1 + +group_offset)); + group_offset += HTT_TX_GROUP_INDEX_OFFSET; + } + ol_tx_update_group_credit_stats(pdev->txrx_pdev); +} +#endif + diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/htt_types.h b/drivers/staging/qcacld-3.0/core/dp/htt/htt_types.h new file mode 100644 index 0000000000000000000000000000000000000000..0ad01dec88b77dcb460de01baa1951f15720de82 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/htt_types.h @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2011, 2014-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HTT_TYPES__H_ +#define _HTT_TYPES__H_ + +#include /* uint16_t, dma_addr_t */ +#include /* qdf_device_t */ +#include /* qdf_spinlock_t */ +#include /* qdf_timer_t */ +#include /* qdf_atomic_inc */ +#include /* qdf_nbuf_t */ +#include /* HTC_PACKET */ +#include +#include +#define DEBUG_DMA_DONE + +#define HTT_TX_MUTEX_TYPE qdf_spinlock_t + +#ifdef QCA_TX_HTT2_SUPPORT +#ifndef HTC_TX_HTT2_MAX_SIZE +/* Should sync to the target's implementation. */ +#define HTC_TX_HTT2_MAX_SIZE (120) +#endif +#endif /* QCA_TX_HTT2_SUPPORT */ + +/* + * Set the base misclist size to the size of the htt tx copy engine + * to guarantee that a packet on the misclist wont be freed while it + * is sitting in the copy engine. + */ +#define HTT_HTC_PKT_MISCLIST_SIZE 2048 + +struct htt_htc_pkt { + void *pdev_ctxt; + target_paddr_t nbuf_paddr; + HTC_PACKET htc_pkt; + uint16_t msdu_id; +}; + +struct htt_htc_pkt_union { + union { + struct htt_htc_pkt pkt; + struct htt_htc_pkt_union *next; + } u; +}; + +/* + * HTT host descriptor: + * Include the htt_tx_msdu_desc that gets downloaded to the target, + * but also include the HTC_FRAME_HDR and alignment padding that + * precede the htt_tx_msdu_desc. + * htc_send_data_pkt expects this header space at the front of the + * initial fragment (i.e. tx descriptor) that is downloaded. + */ +struct htt_host_tx_desc_t { + uint8_t htc_header[HTC_HEADER_LEN]; + /* force the tx_desc field to begin on a 4-byte boundary */ + union { + uint32_t dummy_force_align; + struct htt_tx_msdu_desc_t tx_desc; + } align32; +}; + +struct htt_tx_mgmt_desc_buf { + qdf_nbuf_t msg_buf; + A_BOOL is_inuse; + qdf_nbuf_t mgmt_frm; +}; + +struct htt_tx_mgmt_desc_ctxt { + struct htt_tx_mgmt_desc_buf *pool; + A_UINT32 pending_cnt; +}; + +struct htt_list_node { + struct htt_list_node *prev; + struct htt_list_node *next; +}; + +struct htt_rx_hash_entry { + qdf_dma_addr_t paddr; + qdf_nbuf_t netbuf; + A_UINT8 fromlist; + struct htt_list_node listnode; +#ifdef RX_HASH_DEBUG + A_UINT32 cookie; +#endif +}; + +struct htt_rx_hash_bucket { + struct htt_list_node listhead; + struct htt_rx_hash_entry *entries; + struct htt_list_node freepool; +#ifdef RX_HASH_DEBUG + A_UINT32 count; +#endif +}; + +/* + * Micro controller datapath offload + * WLAN TX resources + */ +struct htt_ipa_uc_tx_resource_t { + qdf_shared_mem_t *tx_ce_idx; + qdf_shared_mem_t *tx_comp_ring; + + uint32_t tx_comp_idx_paddr; + qdf_shared_mem_t **tx_buf_pool_strg; + uint32_t alloc_tx_buf_cnt; +}; + +/** + * struct htt_ipa_uc_rx_resource_t + * @rx_rdy_idx_paddr: rx ready index physical address + * @rx_ind_ring: rx indication ring memory info + * @rx_ipa_prc_done_idx: rx process done index memory info + * @rx2_ind_ring: rx2 indication ring memory info + * @rx2_ipa_prc_done_idx: rx2 process done index memory info + */ +struct htt_ipa_uc_rx_resource_t { + qdf_dma_addr_t rx_rdy_idx_paddr; + qdf_shared_mem_t *rx_ind_ring; + qdf_shared_mem_t *rx_ipa_prc_done_idx; + + /* 2nd RX ring */ + qdf_shared_mem_t *rx2_ind_ring; + qdf_shared_mem_t *rx2_ipa_prc_done_idx; +}; + +/** + * struct ipa_uc_rx_ring_elem_t + * @rx_packet_paddr: rx packet physical address + * @vdev_id: virtual interface id + * @rx_packet_leng: packet length + */ +#if HTT_PADDR64 +struct ipa_uc_rx_ring_elem_t { + target_paddr_t rx_packet_paddr; + uint32_t vdev_id; + uint32_t rx_packet_leng; +}; +#else +struct ipa_uc_rx_ring_elem_t { + target_paddr_t rx_packet_paddr; + uint16_t vdev_id; + uint16_t rx_packet_leng; +}; +#endif + +struct htt_tx_credit_t { + qdf_atomic_t bus_delta; + qdf_atomic_t target_delta; +}; + +#if defined(HELIUMPLUS) +/** + * msdu_ext_frag_desc: + * semantically, this is an array of 6 of 2-tuples of + * a 48-bit physical address and a 16 bit len field + * with the following layout: + * 31 16 8 0 + * | p t r - l o w 3 2 | + * | len | ptr-7/16 | + */ +struct msdu_ext_frag_desc { + union { + uint64_t desc64; + struct { + uint32_t ptr_low; + uint32_t ptr_hi:16, + len:16; + } frag32; + } u; +}; + +struct msdu_ext_desc_t { + struct qdf_tso_flags_t tso_flags; + struct msdu_ext_frag_desc frags[6]; +/* + * u_int32_t frag_ptr0; + * u_int32_t frag_len0; + * u_int32_t frag_ptr1; + * u_int32_t frag_len1; + * u_int32_t frag_ptr2; + * u_int32_t frag_len2; + * u_int32_t frag_ptr3; + * u_int32_t frag_len3; + * u_int32_t frag_ptr4; + * u_int32_t frag_len4; + * u_int32_t frag_ptr5; + * u_int32_t frag_len5; + */ +}; +#endif /* defined(HELIUMPLUS) */ + +/** + * struct mon_channel + * @ch_num: Monitor mode capture channel number + * @ch_freq: channel frequency. + */ +struct mon_channel { + uint32_t ch_num; + uint32_t ch_freq; +}; + +struct htt_pdev_t { + struct cdp_cfg *ctrl_pdev; + ol_txrx_pdev_handle txrx_pdev; + HTC_HANDLE htc_pdev; + qdf_device_t osdev; + + HTC_ENDPOINT_ID htc_tx_endpoint; + +#ifdef QCA_TX_HTT2_SUPPORT + HTC_ENDPOINT_ID htc_tx_htt2_endpoint; + uint16_t htc_tx_htt2_max_size; +#endif /* QCA_TX_HTT2_SUPPORT */ + +#ifdef ATH_11AC_TXCOMPACT + HTT_TX_MUTEX_TYPE txnbufq_mutex; + qdf_nbuf_queue_t txnbufq; + struct htt_htc_pkt_union *htt_htc_pkt_misclist; +#endif + + struct htt_htc_pkt_union *htt_htc_pkt_freelist; + struct { + int is_high_latency; + int is_full_reorder_offload; + int default_tx_comp_req; + int ce_classify_enabled; + uint8_t is_first_wakeup_packet; + /* + * To track if credit reporting through + * HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND is enabled/disabled. + * In Genoa(QCN7605) credits are reported through + * HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND only. + */ + u8 credit_update_enabled; + /* Explicitly request TX completions. */ + u8 request_tx_comp; + } cfg; + struct { + uint8_t major; + uint8_t minor; + } tgt_ver; +#if defined(HELIUMPLUS) + struct { + u_int8_t major; + u_int8_t minor; + } wifi_ip_ver; +#endif /* defined(HELIUMPLUS) */ + struct { + struct { + /* + * Ring of network buffer objects - + * This ring is used exclusively by the host SW. + * This ring mirrors the dev_addrs_ring that is shared + * between the host SW and the MAC HW. + * The host SW uses this netbufs ring to locate the nw + * buffer objects whose data buffers the HW has filled. + */ + qdf_nbuf_t *netbufs_ring; + /* + * Ring of buffer addresses - + * This ring holds the "physical" device address of the + * rx buffers the host SW provides for MAC HW to fill. + */ +#if HTT_PADDR64 + uint64_t *paddrs_ring; +#else /* ! HTT_PADDR64 */ + uint32_t *paddrs_ring; +#endif + qdf_dma_mem_context(memctx); + } buf; + /* + * Base address of ring, as a "physical" device address rather + * than a CPU address. + */ + qdf_dma_addr_t base_paddr; + int32_t size; /* how many elems in the ring (power of 2) */ + uint32_t size_mask; /* size - 1, at least 16 bits long */ + + int fill_level; /* how many rx buffers to keep in the ring */ + int fill_cnt; /* # of rx buffers (full+empty) in the ring */ + int pop_fail_cnt; /* # of nebuf pop failures */ + + /* + * target_idx - + * Without reorder offload: + * not used + * With reorder offload: + * points to the location in the rx ring from which rx buffers + * are available to copy into the MAC DMA ring + */ + struct { + uint32_t *vaddr; + qdf_dma_addr_t paddr; + qdf_dma_mem_context(memctx); + } target_idx; + + /* + * alloc_idx/host_idx - + * Without reorder offload: + * where HTT SW has deposited empty buffers + * This is allocated in consistent mem, so that the FW can read + * this variable, and program the HW's FW_IDX reg with the value + * of this shadow register + * With reorder offload: + * points to the end of the available free rx buffers + */ + struct { + uint32_t *vaddr; + qdf_dma_addr_t paddr; + qdf_dma_mem_context(memctx); + } alloc_idx; + + /* + * sw_rd_idx - + * where HTT SW has processed bufs filled by rx MAC DMA + */ + struct { + unsigned int msdu_desc; + unsigned int msdu_payld; + } sw_rd_idx; + + /* + * refill_retry_timer - timer triggered when the ring is not + * refilled to the level expected + */ + qdf_timer_t refill_retry_timer; + + /* + * refill_ref_cnt - ref cnt for Rx buffer replenishment - this + * variable is used to guarantee that only one thread tries + * to replenish Rx ring. + */ + qdf_atomic_t refill_ref_cnt; + qdf_spinlock_t refill_lock; + qdf_atomic_t refill_debt; +#ifdef DEBUG_DMA_DONE + uint32_t dbg_initial_msdu_payld; + uint32_t dbg_mpdu_range; + uint32_t dbg_mpdu_count; + uint32_t dbg_ring_idx; + uint32_t dbg_refill_cnt; + uint32_t dbg_sync_success; +#endif +#ifdef HTT_RX_RESTORE + int rx_reset; + uint8_t htt_rx_restore; +#endif + qdf_spinlock_t rx_hash_lock; + struct htt_rx_hash_bucket **hash_table; + uint32_t listnode_offset; + bool smmu_map; + } rx_ring; + +#ifndef CONFIG_HL_SUPPORT + struct { + qdf_atomic_t fill_cnt; /* # of buffers in pool */ + qdf_atomic_t refill_low_mem; /* if set refill the ring */ + qdf_nbuf_t *netbufs_ring; + qdf_spinlock_t rx_buff_pool_lock; + } rx_buff_pool; +#endif + +#ifdef CONFIG_HL_SUPPORT + int rx_desc_size_hl; +#endif + long rx_fw_desc_offset; + int rx_mpdu_range_offset_words; + int rx_ind_msdu_byte_idx; + + struct { + int size; /* of each HTT tx desc */ + uint16_t pool_elems; + uint16_t alloc_cnt; + struct qdf_mem_multi_page_t desc_pages; + uint32_t *freelist; + qdf_dma_mem_context(memctx); + } tx_descs; +#if defined(HELIUMPLUS) + struct { + int size; /* of each Fragment/MSDU-Ext descriptor */ + int pool_elems; + struct qdf_mem_multi_page_t desc_pages; + qdf_dma_mem_context(memctx); + } frag_descs; +#endif /* defined(HELIUMPLUS) */ + + int download_len; + void (*tx_send_complete_part2)(void *pdev, A_STATUS status, + qdf_nbuf_t msdu, uint16_t msdu_id); + + HTT_TX_MUTEX_TYPE htt_tx_mutex; + HTT_TX_MUTEX_TYPE credit_mutex; + + struct { + int htc_err_cnt; + } stats; +#ifdef CONFIG_HL_SUPPORT + int cur_seq_num_hl; +#endif + struct htt_tx_mgmt_desc_ctxt tx_mgmt_desc_ctxt; + struct targetdef_s *targetdef; + struct ce_reg_def *target_ce_def; + + struct htt_ipa_uc_tx_resource_t ipa_uc_tx_rsc; + struct htt_ipa_uc_rx_resource_t ipa_uc_rx_rsc; + int is_ipa_uc_enabled; + + struct htt_tx_credit_t htt_tx_credit; + +#ifdef DEBUG_RX_RING_BUFFER + struct rx_buf_debug *rx_buff_list; + qdf_spinlock_t rx_buff_list_lock; + int rx_buff_index; + int rx_buff_posted_cum; + int rx_buff_recvd_cum; + int rx_buff_recvd_err; +#endif + /* + * Counters below are being invoked from functions defined outside of + * DEBUG_RX_RING_BUFFER + */ + int rx_buff_debt_invoked; + int rx_buff_fill_n_invoked; + int refill_retry_timer_starts; + int refill_retry_timer_calls; + int refill_retry_timer_doubles; + + /* callback function for packetdump */ + tp_rx_pkt_dump_cb rx_pkt_dump_cb; + + struct mon_channel mon_ch_info; + + /* Flag to indicate whether new htt format is supported */ + bool new_htt_format_enabled; +}; + +#define HTT_EPID_GET(_htt_pdev_hdl) \ + (((struct htt_pdev_t *)(_htt_pdev_hdl))->htc_tx_endpoint) + +#if defined(HELIUMPLUS) +#define HTT_WIFI_IP(pdev, x, y) (((pdev)->wifi_ip_ver.major == (x)) && \ + ((pdev)->wifi_ip_ver.minor == (y))) + +#define HTT_SET_WIFI_IP(pdev, x, y) (((pdev)->wifi_ip_ver.major = (x)) && \ + ((pdev)->wifi_ip_ver.minor = (y))) +#endif /* defined(HELIUMPLUS) */ + +#endif /* _HTT_TYPES__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/htt/rx_desc.h b/drivers/staging/qcacld-3.0/core/dp/htt/rx_desc.h new file mode 100644 index 0000000000000000000000000000000000000000..cd31a86a86df829215f61a6c570e81cd97c9c1c9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/htt/rx_desc.h @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2011-2015, 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _RX_DESC_H_ +#define _RX_DESC_H_ + +/* + * REMIND: Copy one of rx_desc related structures here for export, + * hopes they are always the same between Peregrine and Rome in future + */ +struct rx_attention { + volatile + uint32_t first_mpdu:1, /* [0] */ + last_mpdu:1, /* [1] */ + mcast_bcast:1, /* [2] */ + peer_idx_invalid:1, /* [3] */ + peer_idx_timeout:1, /* [4] */ + power_mgmt:1, /* [5] */ + non_qos:1, /* [6] */ + null_data:1, /* [7] */ + mgmt_type:1, /* [8] */ + ctrl_type:1, /* [9] */ + more_data:1, /* [10] */ + eosp:1, /* [11] */ + u_apsd_trigger:1, /* [12] */ + fragment:1, /* [13] */ + order:1, /* [14] */ + classification:1, /* [15] */ + overflow_err:1, /* [16] */ + msdu_length_err:1, /* [17] */ + tcp_udp_chksum_fail:1, /* [18] */ + ip_chksum_fail:1, /* [19] */ + sa_idx_invalid:1, /* [20] */ + da_idx_invalid:1, /* [21] */ + sa_idx_timeout:1, /* [22] */ + da_idx_timeout:1, /* [23] */ + encrypt_required:1, /* [24] */ + directed:1, /* [25] */ + buffer_fragment:1, /* [26] */ + mpdu_length_err:1, /* [27] */ + tkip_mic_err:1, /* [28] */ + decrypt_err:1, /* [29] */ + fcs_err:1, /* [30] */ + msdu_done:1; /* [31] */ +}; + +struct rx_frag_info { + volatile + uint32_t ring0_more_count:8, /* [7:0] */ + ring1_more_count:8, /* [15:8] */ + ring2_more_count:8, /* [23:16] */ + ring3_more_count:8; /* [31:24] */ + volatile + uint32_t ring4_more_count:8, /* [7:0] */ + ring5_more_count:8, /* [15:8] */ + ring6_more_count:8, /* [23:16] */ + ring7_more_count:8; /* [31:24] */ +}; + +struct rx_msdu_start { + volatile + uint32_t msdu_length:14, /* [13:0] */ +#if defined(HELIUMPLUS) + l3_offset:7, /* [20:14] */ + ipsec_ah:1, /* [21] */ + reserved_0a:2, /* [23:22] */ + l4_offset:7, /* [30:24] */ + ipsec_esp:1; /* [31] */ +#else + ip_offset:6, /* [19:14] */ + ring_mask:4, /* [23:20] */ + tcp_udp_offset:7, /* [30:24] */ + reserved_0c:1; /* [31] */ +#endif /* defined(HELIUMPLUS) */ +#if defined(HELIUMPLUS) + volatile uint32_t flow_id_toeplitz:32; /* [31:0] */ +#else + volatile uint32_t flow_id_crc:32; /* [31:0] */ +#endif /* defined(HELIUMPLUS) */ + volatile + uint32_t msdu_number:8, /* [7:0] */ + decap_format:2, /* [9:8] */ + ipv4_proto:1, /* [10] */ + ipv6_proto:1, /* [11] */ + tcp_proto:1, /* [12] */ + udp_proto:1, /* [13] */ + ip_frag:1, /* [14] */ + tcp_only_ack:1, /* [15] */ + sa_idx:11, /* [26:16] */ + reserved_2b:5; /* [31:27] */ +#if defined(HELIUMPLUS) + volatile + uint32_t da_idx:11, /* [10:0] */ + da_is_bcast_mcast:1, /* [11] */ + reserved_3a:4, /* [15:12] */ + ip4_protocol_ip6_next_header:8, /* [23:16] */ + ring_mask:8; /* [31:24] */ + volatile uint32_t toeplitz_hash_2_or_4:32; /* [31:0] */ +#endif /* defined(HELIUMPLUS) */ +}; + +struct rx_msdu_end { + volatile + uint32_t ip_hdr_chksum:16, /* [15:0] */ + tcp_udp_chksum:16; /* [31:16] */ + volatile + uint32_t key_id_octet:8, /* [7:0] */ +#if defined(HELIUMPLUS) + classification_rule:6, /* [13:8] */ + classify_not_done_truncate:1, /* [14] */ + classify_not_done_cce_dis:1, /* [15] */ +#else + classification_filter:8, /* [15:8] */ +#endif /* defined(HELIUMPLUS) */ + ext_wapi_pn_63_48:16; /* [31:16] */ + volatile uint32_t ext_wapi_pn_95_64:32; /* [31:0] */ + volatile uint32_t ext_wapi_pn_127_96:32; /* [31:0] */ + volatile + uint32_t reported_mpdu_length:14, /* [13:0] */ + first_msdu:1, /* [14] */ + last_msdu:1, /* [15] */ +#if defined(HELIUMPLUS) + sa_idx_timeout:1, /* [16] */ + da_idx_timeout:1, /* [17] */ + msdu_limit_error:1, /* [18] */ + classify_ring_mask:8, /* [26:19] */ +#endif /* defined(HELIUMPLUS) */ + reserved_3a:3, /* [29:27] */ + pre_delim_err:1, /* [30] */ + reserved_3b:1; /* [31] */ +#if defined(HELIUMPLUS) + volatile uint32_t ipv6_options_crc:32; + volatile uint32_t tcp_seq_number:32; + volatile uint32_t tcp_ack_number:32; + volatile + uint32_t tcp_flag:9, /* [8:0] */ + lro_eligible:1, /* [9] */ + l3_header_padding:3, /* [12:10] */ + reserved_8a:3, /* [15:13] */ + window_size:16; /* [31:16] */ + volatile + uint32_t da_offset:6, /* [5:0] */ + sa_offset:6, /* [11:6] */ + da_offset_valid:1, /* [12] */ + sa_offset_valid:1, /* [13] */ + type_offset:7, /* [20:14] */ + reserved_9a:11; /* [31:21] */ + volatile uint32_t rule_indication_31_0:32; + volatile uint32_t rule_indication_63_32:32; + volatile uint32_t rule_indication_95_64:32; + volatile uint32_t rule_indication_127_96:32; +#endif /* defined(HELIUMPLUS) */ +}; + +struct rx_mpdu_end { + volatile + uint32_t reserved_0:13, /* [12:0] */ + overflow_err:1, /* [13] */ + last_mpdu:1, /* [14] */ + post_delim_err:1, /* [15] */ + post_delim_cnt:12, /* [27:16] */ + mpdu_length_err:1, /* [28] */ + tkip_mic_err:1, /* [29] */ + decrypt_err:1, /* [30] */ + fcs_err:1; /* [31] */ +}; + + +#if defined(HELIUMPLUS) + +struct rx_mpdu_start { + volatile + uint32_t peer_idx:11, /* [10:0] */ + fr_ds:1, /* [11] */ + to_ds:1, /* [12] */ + encrypted:1, /* [13] */ + retry:1, /* [14] */ + reserved:1, /* [15] */ + seq_num:12, /* [27:16] */ + encrypt_type:4; /* [31:28] */ + volatile uint32_t pn_31_0:32; /* [31:0] */ + volatile + uint32_t pn_47_32:16, /* [15:0] */ + toeplitz_hash:2, /* [17:16] */ + reserved_2:10, /* [27:18] */ + tid:4; /* [31:28] */ +}; + + +struct rx_ppdu_start { + volatile + uint32_t rssi_pri_chain0:8, /* [7:0] */ + rssi_sec20_chain0:8, /* [15:8] */ + rssi_sec40_chain0:8, /* [23:16] */ + rssi_sec80_chain0:8; /* [31:24] */ + volatile + uint32_t rssi_pri_chain1:8, /* [7:0] */ + rssi_sec20_chain1:8, /* [15:8] */ + rssi_sec40_chain1:8, /* [23:16] */ + rssi_sec80_chain1:8; /* [31:24] */ + volatile + uint32_t rssi_pri_chain2:8, /* [7:0] */ + rssi_sec20_chain2:8, /* [15:8] */ + rssi_sec40_chain2:8, /* [23:16] */ + rssi_sec80_chain2:8; /* [31:24] */ + volatile + uint32_t rssi_pri_chain3:8, /* [7:0] */ + rssi_sec20_chain3:8, /* [15:8] */ + rssi_sec40_chain3:8, /* [23:16] */ + rssi_sec80_chain3:8; /* [31:24] */ + volatile + uint32_t rssi_comb:8, /* [7:0] */ + bandwidth:3, /* [10:8] */ + reserved_4a:5, /* [15:11] */ + rssi_comb_ht:8, /* [23:16] */ + reserved_4b:8; /* [31:24] */ + volatile + uint32_t l_sig_rate:4, /*[3:0] */ + l_sig_rate_select:1, /* [4] */ + l_sig_length:12, /* [16:5] */ + l_sig_parity:1, /* [17] */ + l_sig_tail:6, /* [23:18] */ + preamble_type:8; /* [31:24] */ + volatile + uint32_t ht_sig_vht_sig_ah_sig_a_1:24, /* [23:0] */ + captured_implicit_sounding:1, /* [24] */ + reserved_6:7; /* [31:25] */ + volatile + uint32_t ht_sig_vht_sig_ah_sig_a_2:24, /* [23:0] */ + reserved_7:8; /* [31:24] */ + volatile uint32_t vht_sig_b:32; /* [31:0] */ + volatile + uint32_t service:16, /* [15:0] */ + reserved_9:16; /* [31:16] */ +}; + +#define VHT_SIG_A_1(rx_desc) ((rx_desc)->ppdu_start.ht_sig_vht_sig_ah_sig_a_1) +#define VHT_SIG_A_2(rx_desc) ((rx_desc)->ppdu_start.ht_sig_vht_sig_ah_sig_a_2) +#define TSF_TIMESTAMP(rx_desc) \ +((rx_desc)->ppdu_end.rx_pkt_end.phy_timestamp_1_lower_32) + +struct rx_location_info { + volatile + uint32_t rtt_fac_legacy:14, /* [13:0] */ + rtt_fac_legacy_status:1, /* [14] */ + rtt_fac_vht:14, /* [28:15] */ + rtt_fac_vht_status:1, /* [29] */ + rtt_cfr_status:1, /* [30] */ + rtt_cir_status:1; /* [31] */ + volatile + uint32_t rtt_fac_sifs:10, /* [9:0] */ + rtt_fac_sifs_status:2, /* [11:10] */ + rtt_channel_dump_size:11, /* [22:12] */ + rtt_mac_phy_phase:2, /* [24:23] */ + rtt_hw_ifft_mode:1, /* [25] */ + rtt_btcf_status:1, /* [26] */ + rtt_preamble_type:2, /* [28:27] */ + rtt_pkt_bw:2, /* [30:29] */ + rtt_gi_type:1; /* [31] */ + volatile + uint32_t rtt_mcs_rate:4, /* [3:0] */ + rtt_strongest_chain:2, /* [5:4] */ + rtt_phase_jump:7, /* [12:6] */ + rtt_rx_chain_mask:4, /* [16:13] */ + rtt_tx_data_start_x_phase:1, /* [17] */ + reserved_2:13, /* [30:18] */ + rx_location_info_valid:1; /* [31] */ +}; + +struct rx_pkt_end { + volatile + uint32_t rx_success:1, /* [0] */ + reserved_0a:2, /* [2:1] */ + error_tx_interrupt_rx:1, /* [3] */ + error_ofdm_power_drop:1, /* [4] */ + error_ofdm_restart:1, /* [5] */ + error_cck_power_drop:1, /* [6] */ + error_cck_restart:1, /* [7] */ + reserved_0b:24; /* [31:8] */ + volatile uint32_t phy_timestamp_1_lower_32:32; /* [31:0] */ + volatile uint32_t phy_timestamp_1_upper_32:32; /* [31:0] */ + volatile uint32_t phy_timestamp_2_lower_32:32; /* [31:0] */ + volatile uint32_t phy_timestamp_2_upper_32:32; /* [31:0] */ + struct rx_location_info rx_location_info; +}; + +struct rx_phy_ppdu_end { + volatile + uint32_t reserved_0a:2, /* [1:0] */ + error_radar:1, /* [2] */ + error_rx_abort:1, /* [3] */ + error_rx_nap:1, /* [4] */ + error_ofdm_timing:1, /* [5] */ + error_ofdm_signal_parity:1, /* [6] */ + error_ofdm_rate_illegal:1, /* [7] */ + error_ofdm_length_illegal:1, /* [8] */ + error_ppdu_ofdm_restart:1, /* [9] */ + error_ofdm_service:1, /* [10] */ + error_ppdu_ofdm_power_drop:1, /* [11] */ + error_cck_blocker:1, /* [12] */ + error_cck_timing:1, /* [13] */ + error_cck_header_crc:1, /* [14] */ + error_cck_rate_illegal:1, /* [15] */ + error_cck_length_illegal:1, /* [16] */ + error_ppdu_cck_restart:1, /* [17] */ + error_cck_service:1, /* [18] */ + error_ppdu_cck_power_drop:1, /* [19] */ + error_ht_crc_err:1, /* [20] */ + error_ht_length_illegal:1, /* [21] */ + error_ht_rate_illegal:1, /* [22] */ + error_ht_zlf:1, /* [23] */ + error_false_radar_ext:1, /* [24] */ + error_green_field:1, /* [25] */ + error_spectral_scan:1, /* [26] */ + error_rx_bw_gt_dyn_bw:1, /* [27] */ + error_leg_ht_mismatch:1, /* [28] */ + error_vht_crc_error:1, /* [29] */ + error_vht_siga_unsupported:1, /* [30] */ + error_vht_lsig_len_invalid:1; /* [31] */ + volatile + uint32_t error_vht_ndp_or_zlf:1, /* [0] */ + error_vht_nsym_lt_zero:1, /* [1] */ + error_vht_rx_extra_symbol_mismatch:1, /* [2] */ + error_vht_rx_skip_group_id0:1, /* [3] */ + error_vht_rx_skip_group_id1to62:1, /* [4] */ + error_vht_rx_skip_group_id63:1, /* [5] */ + error_ofdm_ldpc_decoder_disabled:1, /* [6] */ + error_defer_nap:1, /* [7] */ + error_fdomain_timeout:1, /* [8] */ + error_lsig_rel_check:1, /* [9] */ + error_bt_collision:1, /* [10] */ + error_unsupported_mu_feedback:1, /* [11] */ + error_ppdu_tx_interrupt_rx:1, /* [12] */ + error_rx_unsupported_cbf:1, /* [13] */ + reserved_1:18; /* [31:14] */ +}; + +struct rx_timing_offset { + volatile + uint32_t timing_offset:12, /* [11:0] */ + reserved:20; /* [31:12] */ +}; + +struct rx_ppdu_end { + volatile uint32_t evm_p0:32; + volatile uint32_t evm_p1:32; + volatile uint32_t evm_p2:32; + volatile uint32_t evm_p3:32; + volatile uint32_t evm_p4:32; + volatile uint32_t evm_p5:32; + volatile uint32_t evm_p6:32; + volatile uint32_t evm_p7:32; + volatile uint32_t evm_p8:32; + volatile uint32_t evm_p9:32; + volatile uint32_t evm_p10:32; + volatile uint32_t evm_p11:32; + volatile uint32_t evm_p12:32; + volatile uint32_t evm_p13:32; + volatile uint32_t evm_p14:32; + volatile uint32_t evm_p15:32; + volatile uint32_t reserved_16:32; + volatile uint32_t reserved_17:32; + volatile uint32_t wb_timestamp_lower_32:32; + volatile uint32_t wb_timestamp_upper_32:32; + struct rx_pkt_end rx_pkt_end; + struct rx_phy_ppdu_end rx_phy_ppdu_end; + struct rx_timing_offset rx_timing_offset; + volatile + uint32_t rx_antenna:24, /* [23:0] */ + tx_ht_vht_ack:1, /* [24] */ + rx_pkt_end_valid:1, /* [25] */ + rx_phy_ppdu_end_valid:1, /* [26] */ + rx_timing_offset_valid:1, /* [27] */ + bb_captured_channel:1, /* [28] */ + unsupported_mu_nc:1, /* [29] */ + otp_txbf_disable:1, /* [30] */ + reserved_31:1; /* [31] */ + volatile + uint32_t coex_bt_tx_from_start_of_rx:1, /* [0] */ + coex_bt_tx_after_start_of_rx:1, /* [1] */ + coex_wan_tx_from_start_of_rx:1, /* [2] */ + coex_wan_tx_after_start_of_rx:1, /* [3] */ + coex_wlan_tx_from_start_of_rx:1, /* [4] */ + coex_wlan_tx_after_start_of_rx:1, /* [5] */ + mpdu_delimiter_errors_seen:1, /* [6] */ + ftm:1, /* [7] */ + ftm_dialog_token:8, /* [15:8] */ + ftm_follow_up_dialog_token:8, /* [23:16] */ + reserved_32:8; /* [31:24] */ + volatile + uint32_t before_mpdu_cnt_passing_fcs:8, /* [7:0] */ + before_mpdu_cnt_failing_fcs:8, /* [15:8] */ + after_mpdu_cnt_passing_fcs:8, /* [23:16] */ + after_mpdu_cnt_failing_fcs:8; /* [31:24] */ + volatile uint32_t phy_timestamp_tx_lower_32:32; /* [31:0] */ + volatile uint32_t phy_timestamp_tx_upper_32:32; /* [31:0] */ + volatile + uint32_t bb_length:16, /* [15:0] */ + bb_data:1, /* [16] */ + peer_idx_valid:1, /* [17] */ + peer_idx:11, /* [28:18] */ + reserved_26:2, /* [30:29] */ + ppdu_done:1; /* [31] */ +}; +#else +struct rx_ppdu_start { + volatile + uint32_t rssi_chain0_pri20:8, /* [7:0] */ + rssi_chain0_sec20:8, /* [15:8] */ + rssi_chain0_sec40:8, /* [23:16] */ + rssi_chain0_sec80:8; /* [31:24] */ + volatile + uint32_t rssi_chain1_pri20:8, /* [7:0] */ + rssi_chain1_sec20:8, /* [15:8] */ + rssi_chain1_sec40:8, /* [23:16] */ + rssi_chain1_sec80:8; /* [31:24] */ + volatile + uint32_t rssi_chain2_pri20:8, /* [7:0] */ + rssi_chain2_sec20:8, /* [15:8] */ + rssi_chain2_sec40:8, /* [23:16] */ + rssi_chain2_sec80:8; /* [31:24] */ + volatile + uint32_t rssi_chain3_pri20:8, /* [7:0] */ + rssi_chain3_sec20:8, /* [15:8] */ + rssi_chain3_sec40:8, /* [23:16] */ + rssi_chain3_sec80:8; /* [31:24] */ + volatile + uint32_t rssi_comb:8, /* [7:0] */ + reserved_4a:16, /* [23:8] */ + is_greenfield:1, /* [24] */ + reserved_4b:7; /* [31:25] */ + volatile + uint32_t l_sig_rate:4, /* [3:0] */ + l_sig_rate_select:1, /* [4] */ + l_sig_length:12, /* [16:5] */ + l_sig_parity:1, /* [17] */ + l_sig_tail:6, /* [23:18] */ + preamble_type:8; /* [31:24] */ + volatile + uint32_t ht_sig_vht_sig_a_1:24, /* [23:0] */ + reserved_6:8; /* [31:24] */ + volatile + uint32_t ht_sig_vht_sig_a_2:24, /* [23:0] */ + txbf_h_info:1, /* [24] */ + reserved_7:7; /* [31:25] */ + volatile + uint32_t vht_sig_b:29, /* [28:0] */ + reserved_8:3; /* [31:29] */ + volatile + uint32_t service:16, /* [15:0] */ + reserved_9:16; /* [31:16] */ +}; + +#define VHT_SIG_A_1(rx_desc) ((rx_desc)->ppdu_start.ht_sig_vht_sig_a_1) +#define VHT_SIG_A_2(rx_desc) ((rx_desc)->ppdu_start.ht_sig_vht_sig_a_2) + +#define TSF_TIMESTAMP(rx_desc) ((rx_desc)->ppdu_end.tsf_timestamp) + +struct rx_mpdu_start { + volatile + uint32_t peer_idx:11, /* [10:0] */ + fr_ds:1, /* [11] */ + to_ds:1, /* [12] */ + encrypted:1, /* [13] */ + retry:1, /* [14] */ + txbf_h_info:1, /* [15] */ + seq_num:12, /* [27:16] */ + encrypt_type:4; /* [31:28] */ + volatile uint32_t pn_31_0:32; /* [31:0] */ + volatile + uint32_t pn_47_32:16, /* [15:0] */ + directed:1, /* [16] */ + reserved_2:11, /* [27:17] */ + tid:4; /* [31:28] */ +}; + +struct rx_ppdu_end { + volatile uint32_t evm_p0:32; /* [31:0] */ + volatile uint32_t evm_p1:32; /* [31:0] */ + volatile uint32_t evm_p2:32; /* [31:0] */ + volatile uint32_t evm_p3:32; /* [31:0] */ + volatile uint32_t evm_p4:32; /* [31:0] */ + volatile uint32_t evm_p5:32; /* [31:0] */ + volatile uint32_t evm_p6:32; /* [31:0] */ + volatile uint32_t evm_p7:32; /* [31:0] */ + volatile uint32_t evm_p8:32; /* [31:0] */ + volatile uint32_t evm_p9:32; /* [31:0] */ + volatile uint32_t evm_p10:32; /* [31:0] */ + volatile uint32_t evm_p11:32; /* [31:0] */ + volatile uint32_t evm_p12:32; /* [31:0] */ + volatile uint32_t evm_p13:32; /* [31:0] */ + volatile uint32_t evm_p14:32; /* [31:0] */ + volatile uint32_t evm_p15:32; /* [31:0] */ + volatile uint32_t tsf_timestamp:32; /* [31:0] */ + volatile uint32_t wb_timestamp:32; /* [31:0] */ + volatile + uint32_t locationing_timestamp:8, /* [7:0] */ + phy_err_code:8, /* [15:8] */ + phy_err:1, /* [16] */ + rx_location:1, /* [17] */ + txbf_h_info:1, /* [18] */ + reserved_18:13; /* [31:19] */ + volatile + uint32_t rx_antenna:24, /* [23:0] */ + tx_ht_vht_ack:1, /* [24] */ + bb_captured_channel:1, /* [25] */ + reserved_19:6; /* [31:26] */ + volatile + uint32_t rtt_correction_value:24, /* [23:0] */ + reserved_20:7, /* [30:24] */ + rtt_normal_mode:1; /* [31] */ + volatile + uint32_t bb_length:16, /* [15:0] */ + reserved_21:15, /* [30:16] */ + ppdu_done:1; /* [31] */ +}; +#endif /* defined(HELIUMPLUS) */ + +#endif /*_RX_DESC_H_*/ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/cfg_legacy_dp.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/cfg_legacy_dp.h new file mode 100644 index 0000000000000000000000000000000000000000..ccbfbb3fc613a8ac3554133c1ea0b4a36017668a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/cfg_legacy_dp.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CFG_LEGACY_DP +#define __CFG_LEGACY_DP + +#include "cfg_define.h" +#include "cfg_converged.h" +#include "qdf_types.h" + +/* + * + * gEnableFlowSteering - Enable rx traffic flow steering + * @Default: false + * + * Enable Rx traffic flow steering to enable Rx interrupts on multiple CEs based + * on the flows. Different CEs<==>different IRQs<==>probably different CPUs. + * Parallel Rx paths. + * 1 - enable 0 - disable + * + * Usage: Internal + * + * + */ + #define CFG_DP_FLOW_STEERING_ENABLED \ + CFG_INI_BOOL( \ + "gEnableFlowSteering", \ + false, \ + "") + +/* + * + * maxMSDUsPerRxInd - Max number of MSDUs per HTT RX IN ORDER INDICATION msg. + * Note that this has a direct impact on the size of source CE rings. + * It is possible to go below 8, but would require testing; so we are + * restricting the lower limit to 8 artificially + * + * It is recommended that this value is a POWER OF 2. + * + * Values lower than 8 are for experimental purposes only + * + * . + */ +#define CFG_DP_MAX_MSDUS_PER_RXIND \ + CFG_INI_UINT("maxMSDUsPerRxInd", \ + 4, 32, 32, CFG_VALUE_OR_DEFAULT, \ + "Max number of MSDUs per HTT RX INORDER IND msg") + +/* + * + * gEnableTxSchedWrrVO - Set TX sched parameters for VO + * @Default: + * + * This key is mapping to VO defined in data path module through + * OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC. The user can tune the + * WRR TX sched parameters such as skip, credit, limit, credit, disc for VO. + * e.g., gEnableTxSchedWrrVO = 10, 9, 8, 1, 8 + * + * + */ +#define CFG_DP_ENABLE_TX_SCHED_WRR_VO \ + CFG_INI_STRING("gEnableTxSchedWrrVO", \ + 0, 50, "", "et TX sched parameters for VO") + +/* + * + * gEnableTxSchedWrrVI - Set TX sched parameters for VI + * @Default: + * + * This key is mapping to VI defined in data path module through + * OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC. The user can tune the + * WRR TX sched parameters such as skip, credit, limit, credit, disc for VI. + * e.g., gEnableTxSchedWrrVI = 10, 9, 8, 1, 8 + * + * + */ +#define CFG_DP_ENABLE_TX_SCHED_WRR_VI \ + CFG_INI_STRING("gEnableTxSchedWrrVI", \ + 0, 50, "", "Set TX sched parameters for VI") + +/* + * + * gEnableTxSchedWrrBE - Set TX sched parameters for BE + * @Default: + * + * This key is mapping to BE defined in data path module through + * OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC. The user can tune the + * WRR TX sched parameters such as skip, credit, limit, credit, disc for BE. + * e.g., gEnableTxSchedWrrBE = 10, 9, 8, 1, 8 + * + * + */ +#define CFG_DP_ENABLE_TX_SCHED_WRR_BE \ + CFG_INI_STRING("gEnableTxSchedWrrBE", \ + 0, 50, "", "Set TX sched parameters for BE") + +/* + * + * gEnableTxSchedWrrBK - Set TX sched parameters for BK + * @Default: + * + * This key is mapping to BK defined in data path module through + * OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC. The user can tune the + * WRR TX sched parameters such as skip, credit, limit, credit, disc for BK. + * e.g., gEnableTxSchedWrrBK = 10, 9, 8, 1, 8 + * + * + */ +#define CFG_DP_ENABLE_TX_SCHED_WRR_BK \ + CFG_INI_STRING("gEnableTxSchedWrrBK", \ + 0, 50, "", "Set TX sched parameters for BK") + +#define CFG_DP_CE_CLASSIFY_ENABLE \ + CFG_INI_BOOL("gCEClassifyEnable", \ + true, "enable CE classify") + +/* + * + * gEnablePeerUnmapConfSupport - Set PEER UNMAP confirmation support + * Default: false + * 1 - enable 0 - disable + * + * Enable peer unmap confirmation support in the Host. Host will send + * this support to the FW only if FW support is enabled. + * + * + */ +#define CFG_DP_ENABLE_PEER_UMAP_CONF_SUPPORT \ + CFG_INI_BOOL("gEnablePeerUnmapConfSupport", \ + false, "enable PEER UNMAP CONF support") + +#define CFG_LEGACY_DP_ALL \ + CFG(CFG_DP_FLOW_STEERING_ENABLED) \ + CFG(CFG_DP_CE_CLASSIFY_ENABLE) \ + CFG(CFG_DP_MAX_MSDUS_PER_RXIND) \ + CFG(CFG_DP_ENABLE_TX_SCHED_WRR_VO) \ + CFG(CFG_DP_ENABLE_TX_SCHED_WRR_VI) \ + CFG(CFG_DP_ENABLE_TX_SCHED_WRR_BE) \ + CFG(CFG_DP_ENABLE_TX_SCHED_WRR_BK) \ + CFG(CFG_DP_ENABLE_PEER_UMAP_CONF_SUPPORT) + +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_cfg.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..751e5458902a78e8773d9ea3f764d5abab7c1a71 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_cfg.h @@ -0,0 +1,844 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_CFG__H_ +#define _OL_CFG__H_ + +#include /* uint32_t */ +#include /* ol_pdev_handle */ +#include /* ieee80211_qosframe_htc_addr4 */ +#include /* LLC_SNAP_HDR_LEN */ +#if defined(CONFIG_HL_SUPPORT) +#include "wlan_tgt_def_config_hl.h" +#else +#include "wlan_tgt_def_config.h" +#endif +#include "ol_txrx_ctrl_api.h" /* txrx_pdev_cfg_param_t */ +#include +#include "qca_vendor.h" + +/** + * @brief format of data frames delivered to/from the WLAN driver by/to the OS + */ +enum wlan_frm_fmt { + wlan_frm_fmt_unknown, + wlan_frm_fmt_raw, + wlan_frm_fmt_native_wifi, + wlan_frm_fmt_802_3, +}; + +/* Max throughput */ +#ifdef SLUB_MEM_OPTIMIZE +#define MAX_THROUGHPUT 400 +#else +#define MAX_THROUGHPUT 800 +#endif + +/* Throttle period Different level Duty Cycle values*/ +#define THROTTLE_DUTY_CYCLE_LEVEL0 (0) +#define THROTTLE_DUTY_CYCLE_LEVEL1 (50) +#define THROTTLE_DUTY_CYCLE_LEVEL2 (75) +#define THROTTLE_DUTY_CYCLE_LEVEL3 (94) + +struct wlan_ipa_uc_rsc_t { + u8 uc_offload_enabled; + u32 tx_max_buf_cnt; + u32 tx_buf_size; + u32 rx_ind_ring_size; + u32 tx_partition_base; +}; + +/* Config parameters for txrx_pdev */ +struct txrx_pdev_cfg_t { + u8 is_high_latency; + u8 defrag_timeout_check; + u8 rx_pn_check; + u8 pn_rx_fwd_check; + u8 host_addba; + u8 tx_free_at_download; + u8 rx_fwd_inter_bss; + u32 max_thruput_mbps; + u32 target_tx_credit; + u32 vow_config; + u32 tx_download_size; + u32 max_peer_id; + u32 max_vdev; + u32 max_nbuf_frags; + u32 throttle_period_ms; + u8 dutycycle_level[4]; + enum wlan_frm_fmt frame_type; + u8 rx_fwd_disabled; + u8 is_packet_log_enabled; + u8 is_full_reorder_offload; +#ifdef WLAN_FEATURE_TSF_PLUS + u8 is_ptp_rx_opt_enabled; +#endif + struct wlan_ipa_uc_rsc_t ipa_uc_rsc; + bool ip_tcp_udp_checksum_offload; + bool p2p_ip_tcp_udp_checksum_offload; + /* IP, TCP and UDP checksum offload for NAN Mode*/ + bool nan_tcp_udp_checksumoffload; + bool enable_rxthread; + bool ce_classify_enabled; +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) + uint32_t tx_flow_stop_queue_th; + uint32_t tx_flow_start_queue_offset; +#endif + bool flow_steering_enabled; + /* + * To track if credit reporting through + * HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND is enabled/disabled. + * In Genoa(QCN7605) credits are reported through + * HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND only. + */ + u8 credit_update_enabled; + struct ol_tx_sched_wrr_ac_specs_t ac_specs[QCA_WLAN_AC_ALL]; + bool gro_enable; + bool tc_based_dyn_gro; + uint32_t tc_ingress_prio; + bool tso_enable; + bool lro_enable; + bool sg_enable; + bool enable_data_stall_detection; + bool enable_flow_steering; + bool disable_intra_bss_fwd; + /* IPA Micro controller data path offload TX buffer size */ + uint32_t uc_tx_buffer_size; + /* IPA Micro controller data path offload RX indication ring count */ + uint32_t uc_rx_indication_ring_count; + /* IPA Micro controller data path offload TX partition base */ + uint32_t uc_tx_partition_base; + /* Flag to indicate whether new htt format is supported */ + bool new_htt_format_enabled; + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK + /* enable the tcp delay ack feature in the driver */ + bool del_ack_enable; + /* timeout if no more tcp ack frames, unit is ms */ + uint16_t del_ack_timer_value; + /* the maximum number of replaced tcp ack frames */ + uint16_t del_ack_pkt_count; +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE + uint16_t bundle_timer_value; + uint16_t bundle_size; +#endif + uint8_t pktlog_buffer_size; +}; + +/** + * ol_tx_set_flow_control_parameters() - set flow control parameters + * @cfg_ctx: cfg context + * @cfg_param: cfg parameters + * + * Return: none + */ +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +void ol_tx_set_flow_control_parameters(struct cdp_cfg *cfg_ctx, + struct txrx_pdev_cfg_param_t *cfg_param); +#else +static inline +void ol_tx_set_flow_control_parameters(struct cdp_cfg *cfg_ctx, + struct txrx_pdev_cfg_param_t *cfg_param) +{ +} +#endif + +/** + * ol_pdev_cfg_attach - setup configuration parameters + * @osdev: OS handle needed as an argument for some OS primitives + * @cfg_param: configuration parameters + * + * Allocation configuration context that will be used across data path + * + * Return: the control device object + */ +struct cdp_cfg *ol_pdev_cfg_attach(qdf_device_t osdev, void *pcfg_param); + +/** + * @brief Specify whether the system is high-latency or low-latency. + * @details + * Indicate whether the system is operating in high-latency (message + * based, e.g. USB) mode or low-latency (memory-mapped, e.g. PCIe) mode. + * Some chips support just one type of host / target interface. + * Other chips support both LL and HL interfaces (e.g. PCIe and USB), + * so the selection will be made based on which bus HW is present, or + * which is preferred if both are present. + * + * @param pdev - handle to the physical device + * @return 1 -> high-latency -OR- 0 -> low-latency + */ +int ol_cfg_is_high_latency(struct cdp_cfg *cfg_pdev); + +/** + * @brief Specify whether credit reporting through + * HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND is enabled by default. + * In Genoa credits are reported only through + * HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND + * @details + * @param pdev - handle to the physical device + * @return 1 -> enabled -OR- 0 -> disabled + */ +int ol_cfg_is_credit_update_enabled(struct cdp_cfg *cfg_pdev); + +/** + * @brief Specify the range of peer IDs. + * @details + * Specify the maximum peer ID. This is the maximum number of peers, + * minus one. + * This is used by the host to determine the size of arrays indexed by + * peer ID. + * + * @param pdev - handle to the physical device + * @return maximum peer ID + */ +int ol_cfg_max_peer_id(struct cdp_cfg *cfg_pdev); + +/** + * @brief Specify the max number of virtual devices within a physical device. + * @details + * Specify how many virtual devices may exist within a physical device. + * + * @param pdev - handle to the physical device + * @return maximum number of virtual devices + */ +int ol_cfg_max_vdevs(struct cdp_cfg *cfg_pdev); + +/** + * @brief Check whether host-side rx PN check is enabled or disabled. + * @details + * Choose whether to allocate rx PN state information and perform + * rx PN checks (if applicable, based on security type) on the host. + * If the rx PN check is specified to be done on the host, the host SW + * will determine which peers are using a security type (e.g. CCMP) that + * requires a PN check. + * + * @param pdev - handle to the physical device + * @return 1 -> host performs rx PN check -OR- 0 -> no host-side rx PN check + */ +int ol_cfg_rx_pn_check(struct cdp_cfg *cfg_pdev); + +/** + * @brief Check whether host-side rx forwarding is enabled or disabled. + * @details + * Choose whether to check whether to forward rx frames to tx on the host. + * For LL systems, this rx -> tx host-side forwarding check is typically + * enabled. + * For HL systems, the rx -> tx forwarding check is typically done on the + * target. However, even in HL systems, the host-side rx -> tx forwarding + * will typically be enabled, as a second-tier safety net in case the + * target doesn't have enough memory to store all rx -> tx forwarded frames. + * + * @param pdev - handle to the physical device + * @return 1 -> host does rx->tx forward -OR- 0 -> no host-side rx->tx forward + */ +int ol_cfg_rx_fwd_check(struct cdp_cfg *cfg_pdev); + +/** + * ol_set_cfg_rx_fwd_disabled - set rx fwd disable/enable + * + * @pdev - handle to the physical device + * @disable_rx_fwd 1 -> no rx->tx forward -> rx->tx forward + * + * Choose whether to forward rx frames to tx (where applicable) within the + * WLAN driver, or to leave all forwarding up to the operating system. + * Currently only intra-bss fwd is supported. + * + */ +void ol_set_cfg_rx_fwd_disabled(struct cdp_cfg *ppdev, uint8_t disable_rx_fwd); + +/** + * ol_set_cfg_packet_log_enabled - Set packet log config in HTT + * config based on CFG ini configuration + * + * @pdev - handle to the physical device + * @val - 0 - disable, 1 - enable + */ +void ol_set_cfg_packet_log_enabled(struct cdp_cfg *ppdev, uint8_t val); + +/** + * @brief Check whether rx forwarding is enabled or disabled. + * @details + * Choose whether to forward rx frames to tx (where applicable) within the + * WLAN driver, or to leave all forwarding up to the operating system. + * + * @param pdev - handle to the physical device + * @return 1 -> no rx->tx forward -OR- 0 -> rx->tx forward (in host or target) + */ +int ol_cfg_rx_fwd_disabled(struct cdp_cfg *cfg_pdev); + +/** + * @brief Check whether to perform inter-BSS or intra-BSS rx->tx forwarding. + * @details + * Check whether data received by an AP on one virtual device destined + * to a STA associated with a different virtual device within the same + * physical device should be forwarded within the driver, or whether + * forwarding should only be done within a virtual device. + * + * @param pdev - handle to the physical device + * @return + * 1 -> forward both within and between vdevs + * -OR- + * 0 -> forward only within a vdev + */ +int ol_cfg_rx_fwd_inter_bss(struct cdp_cfg *cfg_pdev); + +/** + * @brief Specify data frame format used by the OS. + * @details + * Specify what type of frame (802.3 or native WiFi) the host data SW + * should expect from and provide to the OS shim. + * + * @param pdev - handle to the physical device + * @return enumerated data frame format + */ +enum wlan_frm_fmt ol_cfg_frame_type(struct cdp_cfg *cfg_pdev); + +/** + * @brief Specify the peak throughput. + * @details + * Specify the peak throughput that a system is expected to support. + * The data SW uses this configuration to help choose the size for its + * tx descriptor pool and rx buffer ring. + * The data SW assumes that the peak throughput applies to either rx or tx, + * rather than having separate specs of the rx max throughput vs. the tx + * max throughput. + * + * @param pdev - handle to the physical device + * @return maximum supported throughput in Mbps (not MBps) + */ +int ol_cfg_max_thruput_mbps(struct cdp_cfg *cfg_pdev); + +/** + * @brief Specify the maximum number of fragments per tx network buffer. + * @details + * Specify the maximum number of fragments that a tx frame provided to + * the WLAN driver by the OS may contain. + * In LL systems, the host data SW uses this maximum fragment count to + * determine how many elements to allocate in the fragmentation descriptor + * it creates to specify to the tx MAC DMA where to locate the tx frame's + * data. + * This maximum fragments count is only for regular frames, not TSO frames, + * since TSO frames are sent in segments with a limited number of fragments + * per segment. + * + * @param pdev - handle to the physical device + * @return maximum number of fragments that can occur in a regular tx frame + */ +int ol_cfg_netbuf_frags_max(struct cdp_cfg *cfg_pdev); + +/** + * @brief For HL systems, specify when to free tx frames. + * @details + * In LL systems, the host's tx frame is referenced by the MAC DMA, and + * thus cannot be freed until the target indicates that it is finished + * transmitting the frame. + * In HL systems, the entire tx frame is downloaded to the target. + * Consequently, the target has its own copy of the tx frame, and the + * host can free the tx frame as soon as the download completes. + * Alternatively, the HL host can keep the frame allocated until the + * target explicitly tells the HL host it is done transmitting the frame. + * This gives the target the option of discarding its copy of the tx + * frame, and then later getting a new copy from the host. + * This function tells the host whether it should retain its copy of the + * transmit frames until the target explicitly indicates it is finished + * transmitting them, or if it should free its copy as soon as the + * tx frame is downloaded to the target. + * + * @param pdev - handle to the physical device + * @return + * 0 -> retain the tx frame until the target indicates it is done + * transmitting the frame + * -OR- + * 1 -> free the tx frame as soon as the download completes + */ +int ol_cfg_tx_free_at_download(struct cdp_cfg *cfg_pdev); +void ol_cfg_set_tx_free_at_download(struct cdp_cfg *cfg_pdev); + +/** + * @brief Low water mark for target tx credit. + * Tx completion handler is invoked to reap the buffers when the target tx + * credit goes below Low Water Mark. + */ +#define OL_CFG_NUM_MSDU_REAP 512 +#define ol_cfg_tx_credit_lwm(pdev) \ + ((CFG_TGT_NUM_MSDU_DESC > OL_CFG_NUM_MSDU_REAP) ? \ + (CFG_TGT_NUM_MSDU_DESC - OL_CFG_NUM_MSDU_REAP) : 0) + +/** + * @brief In a HL system, specify the target initial credit count. + * @details + * The HL host tx data SW includes a module for determining which tx frames + * to download to the target at a given time. + * To make this judgement, the HL tx download scheduler has to know + * how many buffers the HL target has available to hold tx frames. + * Due to the possibility that a single target buffer pool can be shared + * between rx and tx frames, the host may not be able to obtain a precise + * specification of the tx buffer space available in the target, but it + * uses the best estimate, as provided by this configuration function, + * to determine how best to schedule the tx frame downloads. + * + * @param pdev - handle to the physical device + * @return the number of tx buffers available in a HL target + */ +uint16_t ol_cfg_target_tx_credit(struct cdp_cfg *cfg_pdev); + +/** + * @brief Specify the LL tx MSDU header download size. + * @details + * In LL systems, determine how many bytes from a tx frame to download, + * in order to provide the target FW's Descriptor Engine with enough of + * the packet's payload to interpret what kind of traffic this is, + * and who it is for. + * This download size specification does not include the 802.3 / 802.11 + * frame encapsulation headers; it starts with the encapsulated IP packet + * (or whatever ethertype is carried within the ethernet-ish frame). + * The LL host data SW will determine how many bytes of the MSDU header to + * download by adding this download size specification to the size of the + * frame header format specified by the ol_cfg_frame_type configuration + * function. + * + * @param pdev - handle to the physical device + * @return the number of bytes beyond the 802.3 or native WiFi header to + * download to the target for tx classification + */ +int ol_cfg_tx_download_size(struct cdp_cfg *cfg_pdev); + +/** + * brief Specify where defrag timeout and duplicate detection is handled + * @details + * non-aggregate duplicate detection and timing out stale fragments + * requires additional target memory. To reach max client + * configurations (128+), non-aggregate duplicate detection and the + * logic to time out stale fragments is moved to the host. + * + * @param pdev - handle to the physical device + * @return + * 0 -> target is responsible non-aggregate duplicate detection and + * timing out stale fragments. + * + * 1 -> host is responsible non-aggregate duplicate detection and + * timing out stale fragments. + */ +int ol_cfg_rx_host_defrag_timeout_duplicate_check(struct cdp_cfg *cfg_pdev); + +/** + * brief Query for the period in ms used for throttling for + * thermal mitigation + * @details + * In LL systems, transmit data throttling is used for thermal + * mitigation where data is paused and resumed during the + * throttle period i.e. the throttle period consists of an + * "on" phase when transmit is allowed and an "off" phase when + * transmit is suspended. This function returns the total + * period used for throttling. + * + * @param pdev - handle to the physical device + * @return the total throttle period in ms + */ +int ol_cfg_throttle_period_ms(struct cdp_cfg *cfg_pdev); + +/** + * brief Query for the duty cycle in percentage used for throttling for + * thermal mitigation + * + * @param pdev - handle to the physical device + * @param level - duty cycle level + * @return the duty cycle level in percentage + */ +int ol_cfg_throttle_duty_cycle_level(struct cdp_cfg *cfg_pdev, int level); + +/** + * brief Check whether full reorder offload is + * enabled/disable by the host + * @details + * If the host does not support receive reorder (i.e. the + * target performs full receive re-ordering) this will return + * "enabled" + * + * @param pdev - handle to the physical device + * @return 1 - enable, 0 - disable + */ +int ol_cfg_is_full_reorder_offload(struct cdp_cfg *cfg_pdev); + +int ol_cfg_is_rx_thread_enabled(struct cdp_cfg *cfg_pdev); + +#ifdef WLAN_FEATURE_TSF_PLUS +void ol_set_cfg_ptp_rx_opt_enabled(struct cdp_cfg *cfg_pdev, u_int8_t val); +u_int8_t ol_cfg_is_ptp_rx_opt_enabled(struct cdp_cfg *cfg_pdev); +#else +static inline void +ol_set_cfg_ptp_rx_opt_enabled(struct cdp_cfg *cfg_pdev, u_int8_t val) +{ +} + +static inline u_int8_t +ol_cfg_is_ptp_rx_opt_enabled(struct cdp_cfg *cfg_pdev) +{ + return 0; +} +#endif + +/** + * ol_cfg_is_ip_tcp_udp_checksum_offload_enabled() - return + * ip_tcp_udp_checksum_offload is enable/disable + * @pdev : handle to the physical device + * + * Return: 1 - enable, 0 - disable + */ +static inline +int ol_cfg_is_ip_tcp_udp_checksum_offload_enabled(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->ip_tcp_udp_checksum_offload; +} + + +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +int ol_cfg_get_tx_flow_stop_queue_th(struct cdp_cfg *cfg_pdev); + +int ol_cfg_get_tx_flow_start_queue_offset(struct cdp_cfg *cfg_pdev); +#endif + +bool ol_cfg_is_ce_classify_enabled(struct cdp_cfg *cfg_pdev); + +enum wlan_target_fmt_translation_caps { + wlan_frm_tran_cap_raw = 0x01, + wlan_frm_tran_cap_native_wifi = 0x02, + wlan_frm_tran_cap_8023 = 0x04, +}; + +/** + * @brief Specify the maximum header size added by SW tx encapsulation + * @details + * This function returns the maximum size of the new L2 header, not the + * difference between the new and old L2 headers. + * Thus, this function returns the maximum 802.11 header size that the + * tx SW may need to add to tx data frames. + * + * @param pdev - handle to the physical device + */ +static inline int ol_cfg_sw_encap_hdr_max_size(struct cdp_cfg *cfg_pdev) +{ + /* + * 24 byte basic 802.11 header + * + 6 byte 4th addr + * + 2 byte QoS control + * + 4 byte HT control + * + 8 byte LLC/SNAP + */ + return sizeof(struct ieee80211_qosframe_htc_addr4) + LLC_SNAP_HDR_LEN; +} + +static inline uint8_t ol_cfg_tx_encap(struct cdp_cfg *cfg_pdev) +{ + /* tx encap done in HW */ + return 0; +} + +static inline int ol_cfg_host_addba(struct cdp_cfg *cfg_pdev) +{ + /* + * ADDBA negotiation is handled by the target FW for Peregrine + Rome. + */ + return 0; +} + +/** + * @brief If the host SW's ADDBA negotiation fails, should it be retried? + * + * @param pdev - handle to the physical device + */ +static inline int ol_cfg_addba_retry(struct cdp_cfg *cfg_pdev) +{ + return 0; /* disabled for now */ +} + +/** + * @brief How many frames to hold in a paused vdev's tx queue in LL systems + */ +static inline int ol_tx_cfg_max_tx_queue_depth_ll(struct cdp_cfg *cfg_pdev) +{ + /* + * Store up to 1500 frames for a paused vdev. + * For example, if the vdev is sending 300 Mbps of traffic, and the + * PHY is capable of 600 Mbps, then it will take 56 ms for the PHY to + * drain both the 700 frames that are queued initially, plus the next + * 700 frames that come in while the PHY is catching up. + * So in this example scenario, the PHY will remain fully utilized + * in a MCC system that has a channel-switching period of 56 ms or less. + * 700 frames calculation was correct when FW drain packet without + * any overhead. Actual situation drain overhead will slowdown drain + * speed. And channel period is less than 56 msec + * Worst scenario, 1500 frames should be stored in host. + */ + return 1500; +} + +/** + * @brief Get packet log config from HTT config + */ +uint8_t ol_cfg_is_packet_log_enabled(struct cdp_cfg *cfg_pdev); + +#ifdef IPA_OFFLOAD +/** + * @brief IPA micro controller data path offload enable or not + * @detail + * This function returns IPA micro controller data path offload + * feature enabled or not + * + * @param pdev - handle to the physical device + */ +unsigned int ol_cfg_ipa_uc_offload_enabled(struct cdp_cfg *cfg_pdev); +/** + * @brief IPA micro controller data path TX buffer size + * @detail + * This function returns IPA micro controller data path offload + * TX buffer size which should be pre-allocated by driver. + * Default buffer size is 2K + * + * @param pdev - handle to the physical device + */ +unsigned int ol_cfg_ipa_uc_tx_buf_size(struct cdp_cfg *cfg_pdev); +/** + * @brief IPA micro controller data path TX buffer size + * @detail + * This function returns IPA micro controller data path offload + * TX buffer count which should be pre-allocated by driver. + * + * @param pdev - handle to the physical device + */ +unsigned int ol_cfg_ipa_uc_tx_max_buf_cnt(struct cdp_cfg *cfg_pdev); +/** + * @brief IPA micro controller data path TX buffer size + * @detail + * This function returns IPA micro controller data path offload + * RX indication ring size which will notified by WLAN FW to IPA + * micro controller + * + * @param pdev - handle to the physical device + */ +unsigned int ol_cfg_ipa_uc_rx_ind_ring_size(struct cdp_cfg *cfg_pdev); +/** + * @brief IPA micro controller data path TX buffer size + * @param pdev - handle to the physical device + */ +unsigned int ol_cfg_ipa_uc_tx_partition_base(struct cdp_cfg *cfg_pdev); +void ol_cfg_set_ipa_uc_tx_partition_base(struct cdp_cfg *cfg_pdev, + uint32_t value); +#else +static inline unsigned int ol_cfg_ipa_uc_offload_enabled( + struct cdp_cfg *cfg_pdev) +{ + return 0; +} + +static inline unsigned int ol_cfg_ipa_uc_tx_buf_size( + struct cdp_cfg *cfg_pdev) +{ + return 0; +} + +static inline unsigned int ol_cfg_ipa_uc_tx_max_buf_cnt( + struct cdp_cfg *cfg_pdev) +{ + return 0; +} + +static inline unsigned int ol_cfg_ipa_uc_rx_ind_ring_size( + struct cdp_cfg *cfg_pdev) +{ + return 0; +} + +static inline unsigned int ol_cfg_ipa_uc_tx_partition_base( + struct cdp_cfg *cfg_pdev) +{ + return 0; +} + +static inline void ol_cfg_set_ipa_uc_tx_partition_base( + void *cfg_pdev, uint32_t value) +{ +} +#endif /* IPA_OFFLOAD */ + +/** + * ol_set_cfg_flow_steering - Set Rx flow steering config based on CFG ini + * config. + * + * @pdev - handle to the physical device + * @val - 0 - disable, 1 - enable + * + * Return: None + */ +static inline void ol_set_cfg_flow_steering(struct cdp_cfg *cfg_pdev, + uint8_t val) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + cfg->flow_steering_enabled = val; +} + +/** + * ol_cfg_is_flow_steering_enabled - Return Rx flow steering config. + * + * @pdev - handle to the physical device + * + * Return: value of configured flow steering value. + */ +static inline uint8_t ol_cfg_is_flow_steering_enabled(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->flow_steering_enabled; +} + +/** + * ol_set_cfg_new_htt_format - Set whether FW supports new htt format + * + * @pdev - handle to the physical device + * @val - true - supported, false - not supported + * + * Return: None + */ +static inline void +ol_set_cfg_new_htt_format(struct cdp_cfg *cfg_pdev, bool val) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + cfg->new_htt_format_enabled = val; +} + +/** + * ol_cfg_is_htt_new_format_enabled - Return whether FW supports new htt format + * + * @pdev - handle to the physical device + * + * Return: value of configured htt_new_format + */ +static inline bool +ol_cfg_is_htt_new_format_enabled(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->new_htt_format_enabled; +} + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK +/** + * ol_cfg_get_del_ack_timer_value() - get delayed ack timer value + * @cfg_pdev: pdev handle + * + * Return: timer value + */ +int ol_cfg_get_del_ack_timer_value(struct cdp_cfg *cfg_pdev); + +/** + * ol_cfg_get_del_ack_enable_value() - get delayed ack enable value + * @cfg_pdev: pdev handle + * + * Return: enable/disable + */ +bool ol_cfg_get_del_ack_enable_value(struct cdp_cfg *cfg_pdev); + +/** + * ol_cfg_get_del_ack_count_value() - get delayed ack count value + * @cfg_pdev: pdev handle + * + * Return: count value + */ +int ol_cfg_get_del_ack_count_value(struct cdp_cfg *cfg_pdev); + +/** + * ol_cfg_update_del_ack_params() - update delayed ack params + * @cfg_ctx: cfg context + * @cfg_param: parameters + * + * Return: none + */ +void ol_cfg_update_del_ack_params(struct txrx_pdev_cfg_t *cfg_ctx, + struct txrx_pdev_cfg_param_t *cfg_param); +#else +/** + * ol_cfg_update_del_ack_params() - update delayed ack params + * @cfg_ctx: cfg context + * @cfg_param: parameters + * + * Return: none + */ +static inline +void ol_cfg_update_del_ack_params(struct txrx_pdev_cfg_t *cfg_ctx, + struct txrx_pdev_cfg_param_t *cfg_param) +{ +} +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE +int ol_cfg_get_bundle_timer_value(struct cdp_cfg *cfg_pdev); +int ol_cfg_get_bundle_size(struct cdp_cfg *cfg_pdev); +#else +#endif +/** + * ol_cfg_get_wrr_skip_weight() - brief Query for the param of wrr_skip_weight + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: wrr_skip_weight for specified ac. + */ +int ol_cfg_get_wrr_skip_weight(struct cdp_cfg *pdev, int ac); + +/** + * ol_cfg_get_credit_threshold() - Query for the param of credit_threshold + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: credit_threshold for specified ac. + */ +uint32_t ol_cfg_get_credit_threshold(struct cdp_cfg *pdev, int ac); + +/** + * ol_cfg_get_send_limit() - Query for the param of send_limit + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: send_limit for specified ac. + */ +uint16_t ol_cfg_get_send_limit(struct cdp_cfg *pdev, int ac); + +/** + * ol_cfg_get_credit_reserve() - Query for the param of credit_reserve + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: credit_reserve for specified ac. + */ +int ol_cfg_get_credit_reserve(struct cdp_cfg *pdev, int ac); + +/** + * ol_cfg_get_discard_weight() - Query for the param of discard_weight + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: discard_weight for specified ac. + */ +int ol_cfg_get_discard_weight(struct cdp_cfg *pdev, int ac); +#endif /* _OL_CFG__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_defines.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_defines.h new file mode 100644 index 0000000000000000000000000000000000000000..c89a6e2d0b88097efb2731a75550020d2b9ac120 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_defines.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013-2014, 2016, 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Offload specific Opaque Data types. + */ +#ifndef _DEV_OL_DEFINES_H +#define _DEV_OL_DEFINES_H + +#define OL_TXRX_PDEV_ID 0 + +#define NORMALIZED_TO_NOISE_FLOOR (-96) + + /** + * ol_txrx_pdev_handle - opaque handle for txrx physical device + * object + */ +struct ol_txrx_pdev_t; +typedef struct ol_txrx_pdev_t *ol_txrx_pdev_handle; + +/** + * ol_txrx_vdev_handle - opaque handle for txrx virtual device + * object + */ +struct ol_txrx_vdev_t; +typedef struct ol_txrx_vdev_t *ol_txrx_vdev_handle; + +/** + * ol_pdev_handle - opaque handle for the configuration + * associated with the physical device + */ +struct ol_pdev_t; +typedef struct ol_pdev_t *ol_pdev_handle; + +/** + * ol_txrx_peer_handle - opaque handle for txrx peer object + */ +struct ol_txrx_peer_t; +typedef struct ol_txrx_peer_t *ol_txrx_peer_handle; + +#endif /* _DEV_OL_DEFINES_H */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_api.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..73038c116331cb408ddd8464c7cffa979302abb6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_api.h @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2011, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_htt_api.h + * @brief Specify the general HTT API functions called by the host data SW. + * @details + * This file declares the HTT API functions that are not specific to + * either tx nor rx. + */ +#ifndef _OL_HTT_API__H_ +#define _OL_HTT_API__H_ + +#include /* qdf_device_t */ +#include /* qdf_nbuf_t */ +#include /* A_STATUS */ +#include /* HTC_HANDLE */ +#include "htt.h" /* htt_dbg_stats_type, etc. */ +#include /* ol_pdev_handle */ +#include +#include + +struct htt_pdev_t; +typedef struct htt_pdev_t *htt_pdev_handle; + +htt_pdev_handle +htt_pdev_alloc(ol_txrx_pdev_handle txrx_pdev, + struct cdp_cfg *ctrl_pdev, + HTC_HANDLE htc_pdev, qdf_device_t osdev); + +/** + * @brief Allocate and initialize a HTT instance. + * @details + * This function allocates and initializes an HTT instance. + * This involves allocating a pool of HTT tx descriptors in + * consistent memory, allocating and filling a rx ring (LL only), + * and connecting the HTC's HTT_DATA_MSG service. + * The HTC service connect call will block, so this function + * needs to be called in passive context. + * Because HTC setup has not been completed at the time this function + * is called, this function cannot send any HTC messages to the target. + * Messages to configure the target are instead sent in the + * htc_attach_target function. + * + * @param pdev - data SW's physical device handle + * (used as context pointer during HTT -> txrx calls) + * @param desc_pool_size - number of HTT descriptors to (pre)allocate + * @return success -> HTT pdev handle; failure -> NULL + */ +int +htt_attach(struct htt_pdev_t *pdev, int desc_pool_size); + +/** + * @brief Send HTT configuration messages to the target. + * @details + * For LL only, this function sends a rx ring configuration message to the + * target. For HL, this function is a no-op. + * + * @param htt_pdev - handle to the HTT instance being initialized + */ +QDF_STATUS htt_attach_target(htt_pdev_handle htt_pdev); + +/** + * enum htt_op_mode - Virtual device operation mode + * + * @htt_op_mode_unknown: Unknown mode + * @htt_op_mode_ap: AP mode + * @htt_op_mode_ibss: IBSS mode + * @htt_op_mode_sta: STA (client) mode + * @htt_op_mode_monitor: Monitor mode + * @htt_op_mode_ocb: OCB mode + */ +enum htt_op_mode { + htt_op_mode_unknown, + htt_op_mode_ap, + htt_op_mode_ibss, + htt_op_mode_sta, + htt_op_mode_monitor, + htt_op_mode_ocb, +}; + +/* no-ops */ +#define htt_vdev_attach(htt_pdev, vdev_id, op_mode) +#define htt_vdev_detach(htt_pdev, vdev_id) +#define htt_peer_qos_update(htt_pdev, peer_id, qos_capable) +#define htt_peer_uapsdmask_update(htt_pdev, peer_id, uapsd_mask) + +void htt_pdev_free(htt_pdev_handle pdev); + +/** + * @brief Deallocate a HTT instance. + * + * @param htt_pdev - handle to the HTT instance being torn down + */ +void htt_detach(htt_pdev_handle htt_pdev); + +/** + * @brief Stop the communication between HTT and target + * @details + * For ISOC solution, this function stop the communication between HTT and + * target. + * For Peregrine/Rome, it's already stopped by ol_ath_disconnect_htc + * before ol_txrx_pdev_detach called in ol_ath_detach. So this function is + * a no-op. + * Peregrine/Rome HTT layer is on top of HTC while ISOC solution HTT layer is + * on top of DXE layer. + * + * @param htt_pdev - handle to the HTT instance being initialized + */ +void htt_detach_target(htt_pdev_handle htt_pdev); + +/* + * @brief Tell the target side of HTT to suspend H2T processing until synced + * @param htt_pdev - the host HTT object + * @param sync_cnt - what sync count value the target HTT FW should wait for + * before resuming H2T processing + */ +A_STATUS htt_h2t_sync_msg(htt_pdev_handle htt_pdev, uint8_t sync_cnt); + +int +htt_h2t_aggr_cfg_msg(htt_pdev_handle htt_pdev, + int max_subfrms_ampdu, int max_subfrms_amsdu); + +/** + * @brief Get the FW status + * @details + * Trigger FW HTT to retrieve FW status. + * A separate HTT message will come back with the statistics we want. + * + * @param pdev - handle to the HTT instance + * @param stats_type_upload_mask - bitmask identifying which stats to upload + * @param stats_type_reset_mask - bitmask identifying which stats to reset + * @param cookie - unique value to distinguish and identify stats requests + * @return 0 - succeed to send the request to FW; otherwise, failed to do so. + */ +int +htt_h2t_dbg_stats_get(struct htt_pdev_t *pdev, + uint32_t stats_type_upload_mask, + uint32_t stats_type_reset_mask, + uint8_t cfg_stats_type, + uint32_t cfg_val, uint8_t cookie); + +/** + * @brief Get the fields from HTT T2H stats upload message's stats info header + * @details + * Parse the a HTT T2H message's stats info tag-length-value header, + * to obtain the stats type, status, data length, and data address. + * + * @param stats_info_list - address of stats record's header + * @param[out] type - which type of FW stats are contained in the record + * @param[out] status - whether the stats are (fully) present in the record + * @param[out] length - how large the data portion of the stats record is + * @param[out] stats_data - where the data portion of the stats record is + */ +void +htt_t2h_dbg_stats_hdr_parse(uint8_t *stats_info_list, + enum htt_dbg_stats_type *type, + enum htt_dbg_stats_status *status, + int *length, uint8_t **stats_data); + +/** + * @brief Display a stats record from the HTT T2H STATS_CONF message. + * @details + * Parse the stats type and status, and invoke a type-specified printout + * to display the stats values. + * + * @param stats_data - buffer holding the stats record from the STATS_CONF msg + * @param concise - whether to do a verbose or concise printout + */ +void htt_t2h_stats_print(uint8_t *stats_data, int concise); + +/** + * htt_log_rx_ring_info() - log htt rx ring info during FW_RX_REFILL failure + * @pdev: handle to the HTT instance + * + * Return: None + */ +void htt_log_rx_ring_info(htt_pdev_handle pdev); + +#ifndef HTT_DEBUG_LEVEL +#if defined(DEBUG) +#define HTT_DEBUG_LEVEL 10 +#else +#define HTT_DEBUG_LEVEL 0 +#endif +#endif + +#if HTT_DEBUG_LEVEL > 5 +void htt_display(htt_pdev_handle pdev, int indent); +#else +#define htt_display(pdev, indent) +#endif + +#define HTT_DXE_RX_LOG 0 +#define htt_rx_reorder_log_print(pdev) + +#ifdef IPA_OFFLOAD +int htt_h2t_ipa_uc_rsc_cfg_msg(struct htt_pdev_t *pdev); + +/** + * htt_ipa_uc_get_resource() - Get uc resource from htt and lower layer + * @pdev - handle to the HTT instance + * @ce_sr - CE source ring DMA mapping info + * @tx_comp_ring - tx completion ring DMA mapping info + * @rx_rdy_ring - rx Ready ring DMA mapping info + * @rx2_rdy_ring - rx2 Ready ring DMA mapping info + * @rx_proc_done_idx - rx process done index + * @rx2_proc_done_idx - rx2 process done index + * @ce_sr_ring_size: copyengine source ring size + * @ce_reg_paddr - CE Register address + * @tx_num_alloc_buffer - Number of TX allocated buffers + * + * Return: 0 success + */ +int +htt_ipa_uc_get_resource(htt_pdev_handle pdev, + qdf_shared_mem_t **ce_sr, + qdf_shared_mem_t **tx_comp_ring, + qdf_shared_mem_t **rx_rdy_ring, + qdf_shared_mem_t **rx2_rdy_ring, + qdf_shared_mem_t **rx_proc_done_idx, + qdf_shared_mem_t **rx2_proc_done_idx, + uint32_t *ce_sr_ring_size, + qdf_dma_addr_t *ce_reg_paddr, + uint32_t *tx_num_alloc_buffer); + +int +htt_ipa_uc_set_doorbell_paddr(htt_pdev_handle pdev, + qdf_dma_addr_t ipa_uc_tx_doorbell_paddr, + qdf_dma_addr_t ipa_uc_rx_doorbell_paddr); + +int +htt_h2t_ipa_uc_set_active(struct htt_pdev_t *pdev, bool uc_active, bool is_tx); + +int htt_h2t_ipa_uc_get_stats(struct htt_pdev_t *pdev); + +int htt_h2t_ipa_uc_get_share_stats(struct htt_pdev_t *pdev, + uint8_t reset_stats); + +int htt_h2t_ipa_uc_set_quota(struct htt_pdev_t *pdev, uint64_t quota_bytes); + +int htt_ipa_uc_attach(struct htt_pdev_t *pdev); + +void htt_ipa_uc_detach(struct htt_pdev_t *pdev); +#else +/** + * htt_h2t_ipa_uc_rsc_cfg_msg() - Send WDI IPA config message to firmware + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline int htt_h2t_ipa_uc_rsc_cfg_msg(struct htt_pdev_t *pdev) +{ + return 0; +} + +/** + * htt_ipa_uc_set_doorbell_paddr() - Propagate IPA doorbell address + * @pdev: handle to the HTT instance + * @ipa_uc_tx_doorbell_paddr: TX doorbell base physical address + * @ipa_uc_rx_doorbell_paddr: RX doorbell base physical address + * + * Return: 0 success + */ +static inline int +htt_ipa_uc_set_doorbell_paddr(htt_pdev_handle pdev, + uint32_t ipa_uc_tx_doorbell_paddr, + uint32_t ipa_uc_rx_doorbell_paddr) +{ + return 0; +} + +/** + * htt_h2t_ipa_uc_set_active() - Propagate WDI path enable/disable to firmware + * @pdev: handle to the HTT instance + * @uc_active: WDI UC path enable or not + * @is_tx: TX path or RX path + * + * Return: 0 success + */ +static inline int +htt_h2t_ipa_uc_set_active(struct htt_pdev_t *pdev, bool uc_active, + bool is_tx) +{ + return 0; +} + +/** + * htt_h2t_ipa_uc_get_stats() - WDI UC state query request to firmware + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline int htt_h2t_ipa_uc_get_stats(struct htt_pdev_t *pdev) +{ + return 0; +} + +/** + * htt_h2t_ipa_uc_get_share_stats() - WDI UC wifi sharing state request to FW + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline int htt_h2t_ipa_uc_get_share_stats(struct htt_pdev_t *pdev, + uint8_t reset_stats) +{ + return 0; +} + +/** + * htt_h2t_ipa_uc_set_quota() - WDI UC set quota request to firmware + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline int htt_h2t_ipa_uc_set_quota(struct htt_pdev_t *pdev, + uint64_t quota_bytes) +{ + return 0; +} + +/** + * htt_ipa_uc_attach() - Allocate UC data path resources + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline int htt_ipa_uc_attach(struct htt_pdev_t *pdev) +{ + return 0; +} + +/** + * htt_ipa_uc_attach() - Remove UC data path resources + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline void htt_ipa_uc_detach(struct htt_pdev_t *pdev) +{ +} +#endif /* IPA_OFFLOAD */ + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +void htt_rx_mon_note_capture_channel(htt_pdev_handle pdev, int mon_ch); + +void ol_htt_mon_note_chan(struct cdp_pdev *ppdev, int mon_ch); +#else +static inline +void htt_rx_mon_note_capture_channel(htt_pdev_handle pdev, int mon_ch) {} + +static inline +void ol_htt_mon_note_chan(struct cdp_pdev *ppdev, int mon_ch) {} +#endif + +#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT) + +void htt_dump_bundle_stats(struct htt_pdev_t *pdev); +void htt_clear_bundle_stats(struct htt_pdev_t *pdev); +#else + +static inline void htt_dump_bundle_stats(struct htt_pdev_t *pdev) +{ +} + +static inline void htt_clear_bundle_stats(struct htt_pdev_t *pdev) +{ +} +#endif + +void htt_mark_first_wakeup_packet(htt_pdev_handle pdev, uint8_t value); + +typedef void (*tp_rx_pkt_dump_cb)(qdf_nbuf_t msdu, uint8_t peer_id, + uint8_t status); +#ifdef REMOVE_PKT_LOG +static inline +void htt_register_rx_pkt_dump_callback(struct htt_pdev_t *pdev, + tp_rx_pkt_dump_cb ol_rx_pkt_dump_call) +{ +} + +static inline +void htt_deregister_rx_pkt_dump_callback(struct htt_pdev_t *pdev) +{ +} + +static inline +void ol_rx_pkt_dump_call(qdf_nbuf_t msdu, uint8_t peer_id, uint8_t status) +{ +} +#else +void htt_register_rx_pkt_dump_callback(struct htt_pdev_t *pdev, + tp_rx_pkt_dump_cb ol_rx_pkt_dump_call); +void htt_deregister_rx_pkt_dump_callback(struct htt_pdev_t *pdev); +void ol_rx_pkt_dump_call(qdf_nbuf_t msdu, uint8_t peer_id, uint8_t status); +#endif + +#endif /* _OL_HTT_API__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_rx_api.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_rx_api.h new file mode 100644 index 0000000000000000000000000000000000000000..4f39903ae354baf26f1043aee47b06dafc954209 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_rx_api.h @@ -0,0 +1,1025 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_htt_rx_api.h + * @brief Specify the rx HTT API functions called by the host data SW. + * @details + * This file declares the HTT API functions that are specifically + * related to receive processing. + * In particular, this file specifies methods of the abstract HTT rx + * descriptor, and functions to iterate though a series of rx descriptors + * and rx MSDU buffers. + */ +#ifndef _OL_HTT_RX_API__H_ +#define _OL_HTT_RX_API__H_ + +#include /* uint16_t, etc. */ +#include /* qdf_nbuf_t */ +#include /* bool */ + +#include /* HTT_RX_IND_MPDU_STATUS */ +#include /* htt_pdev_handle */ + +#include +#include + +/*================ constants and types used in the rx API ===================*/ + +#define HTT_RSSI_INVALID 0x7fff + +#ifndef EXTERNAL_USE_ONLY + +#define IEEE80211_LSIG_LEN 3 +#define IEEE80211_HTSIG_LEN 6 +#define IEEE80211_SB_LEN 2 + +/** + * struct ieee80211_rx_status - RX status + * @rs_numchains: Number of chains + * @rs_flags: Flags + * @rs_rssi: RSSI (noise floor adjusted) + * @rs_abs_rssi: Absolute RSSI + * @rs_datarate: Data rate received + * @rs_rateieee: ieee rate + * @rs_ratephy: Phy rate + * @rs_rssictl: RSSI (noise floor adjusted) + * @rs_rssiextn: RSSI (noise floor adjusted) + * @rs_isvalidrssi: rs_rssi is valid or not + * @rs_phymode: Phy mode + * @rs_freq: Received frequency + * @rs_tstamp: Received timestamp + * @rs_full_chan: Detail channel structure of recv frame. + * It could be NULL if not available + * @rs_isaggr: Is Aggreggated? + * @rs_isapsd: Is APSD? + * @rs_noisefloor: Noise floor + * @rs_channel: Channel + * @rs_rpttstamp: txbf report time stamp + * @rs_cryptodecapcount: Crypto bytes decapped/demic'ed + * @rs_padspace: No. of padding bytes present after header + * in wbuf + * @rs_qosdecapcount: QoS/HTC bytes decapped + * @rs_lsig: lsig + * @rs_htsig: HT sig + * @rs_servicebytes: Received service bytes + */ +struct ieee80211_rx_status { + int rs_numchains; + int rs_flags; + int rs_rssi; + int rs_abs_rssi; + int rs_datarate; + int rs_rateieee; + int rs_ratephy; + + uint8_t rs_rssictl[IEEE80211_MAX_ANTENNA]; + uint8_t rs_rssiextn[IEEE80211_MAX_ANTENNA]; + uint8_t rs_isvalidrssi; + + enum ieee80211_phymode rs_phymode; + int rs_freq; + + union { + uint8_t data[8]; + uint64_t tsf; + } rs_tstamp; + + struct ieee80211_channel *rs_full_chan; + + uint8_t rs_isaggr; + uint8_t rs_isapsd; + int16_t rs_noisefloor; + uint16_t rs_channel; +#ifdef ATH_SUPPORT_TxBF + uint32_t rs_rpttstamp; +#endif + + /* + * The following counts are meant to assist in stats calculation. + * These variables are incremented only in specific situations, and + * should not be relied upon for any purpose other than the original + * stats related purpose they have been introduced for. + */ + + uint16_t rs_cryptodecapcount; + uint8_t rs_padspace; + uint8_t rs_qosdecapcount; + + /* End of stats calculation related counts. */ + + uint8_t rs_lsig[IEEE80211_LSIG_LEN]; + uint8_t rs_htsig[IEEE80211_HTSIG_LEN]; + uint8_t rs_servicebytes[IEEE80211_SB_LEN]; + +}; +#endif /* EXTERNAL_USE_ONLY */ + +/** + * struct ocb_rx_stats_hdr_t - RX stats header + * @version: The version must be 1. + * @length: The length of this structure + * @channel_freq: The center frequency for the packet + * @rssi_cmb: combined RSSI from all chains + * @rssi[4]: rssi for chains 0 through 3 (for 20 MHz bandwidth) + * @tsf32: timestamp in TSF units + * @timestamp_microsec: timestamp in microseconds + * @datarate: MCS index + * @timestamp_submicrosec: submicrosecond portion of the timestamp + * @ext_tid: Extended TID + * @reserved: Ensure the size of the structure is a multiple of 4. + * Must be 0. + * + * When receiving an OCB packet, the RX stats is sent to the user application + * so that the user application can do processing based on the RX stats. + * This structure will be preceded by an ethernet header with + * the proto field set to 0x8152. This struct includes various RX + * paramaters including RSSI, data rate, and center frequency. + */ +PREPACK struct ocb_rx_stats_hdr_t { + uint16_t version; + uint16_t length; + uint16_t channel_freq; + int16_t rssi_cmb; + int16_t rssi[4]; + uint32_t tsf32; + uint32_t timestamp_microsec; + uint8_t datarate; + uint8_t timestamp_submicrosec; + uint8_t ext_tid; + uint8_t reserved; +}; + +/*================ rx indication message field access methods ===============*/ + +/** + * @brief Check if a rx indication message has a rx reorder flush command. + * @details + * Space is reserved in each rx indication message for a rx reorder flush + * command, to release specified MPDUs from the rx reorder holding array + * before processing the new MPDUs referenced by the rx indication message. + * This rx reorder flush command contains a flag to show whether the command + * is valid within a given rx indication message. + * This function checks the validity flag from the rx indication + * flush command IE within the rx indication message. + * + * @param pdev - the HTT instance the rx data was received on + * @param rx_ind_msg - the netbuf containing the rx indication message + * @return + * 1 - the message's rx flush command is valid and should be processed + * before processing new rx MPDUs, + * -OR- + * 0 - the message's rx flush command is invalid and should be ignored + */ +int htt_rx_ind_flush(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg); + +/** + * @brief Return the sequence number starting the range of MPDUs to flush. + * @details + * Read the fields of the rx indication message that identify the start + * and end of the range of MPDUs to flush from the rx reorder holding array + * and send on to subsequent stages of rx processing. + * These sequence numbers are the 6 LSBs of the 12-bit 802.11 sequence + * number. These sequence numbers are masked with the block ack window size, + * rounded up to a power of two (minus one, to create a bitmask) to obtain + * the corresponding index into the rx reorder holding array. + * The series of MPDUs to flush includes the one specified by the start + * sequence number. + * The series of MPDUs to flush excludes the one specified by the end + * sequence number; the MPDUs up to but not including the end sequence number + * are to be flushed. + * These start and end seq num fields are only valid if the "flush valid" + * flag is set. + * + * @param pdev - the HTT instance the rx data was received on + * @param rx_ind_msg - the netbuf containing the rx indication message + * @param seq_num_start - (call-by-reference output) sequence number + * for the start of the range of MPDUs to flush + * @param seq_num_end - (call-by-reference output) sequence number + * for the end of the range of MPDUs to flush + */ +void +htt_rx_ind_flush_seq_num_range(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + unsigned *seq_num_start, unsigned *seq_num_end); + +/** + * @brief Check if a rx indication message has a rx reorder release command. + * @details + * Space is reserved in each rx indication message for a rx reorder release + * command, to release specified MPDUs from the rx reorder holding array + * after processing the new MPDUs referenced by the rx indication message. + * This rx reorder release command contains a flag to show whether the command + * is valid within a given rx indication message. + * This function checks the validity flag from the rx indication + * release command IE within the rx indication message. + * + * @param pdev - the HTT instance the rx data was received on + * @param rx_ind_msg - the netbuf containing the rx indication message + * @return + * 1 - the message's rx release command is valid and should be processed + * after processing new rx MPDUs, + * -OR- + * 0 - the message's rx release command is invalid and should be ignored + */ +int htt_rx_ind_release(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg); + +/** + * @brief Return the sequence number starting the range of MPDUs to release. + * @details + * Read the fields of the rx indication message that identify the start + * and end of the range of MPDUs to release from the rx reorder holding + * array and send on to subsequent stages of rx processing. + * These sequence numbers are the 6 LSBs of the 12-bit 802.11 sequence + * number. These sequence numbers are masked with the block ack window size, + * rounded up to a power of two (minus one, to create a bitmask) to obtain + * the corresponding index into the rx reorder holding array. + * The series of MPDUs to release includes the one specified by the start + * sequence number. + * The series of MPDUs to release excludes the one specified by the end + * sequence number; the MPDUs up to but not including the end sequence number + * are to be released. + * These start and end seq num fields are only valid if the "release valid" + * flag is set. + * + * @param pdev - the HTT instance the rx data was received on + * @param rx_ind_msg - the netbuf containing the rx indication message + * @param seq_num_start - (call-by-reference output) sequence number + * for the start of the range of MPDUs to release + * @param seq_num_end - (call-by-reference output) sequence number + * for the end of the range of MPDUs to release + */ +void +htt_rx_ind_release_seq_num_range(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + unsigned int *seq_num_start, + unsigned int *seq_num_end); + +/* + * For now, the host HTT -> host data rx status enum + * exactly matches the target HTT -> host HTT rx status enum; + * no translation is required. + * However, the host data SW should only use the htt_rx_status, + * so that in the future a translation from target HTT rx status + * to host HTT rx status can be added, if the need ever arises. + */ +enum htt_rx_status { + htt_rx_status_unknown = HTT_RX_IND_MPDU_STATUS_UNKNOWN, + htt_rx_status_ok = HTT_RX_IND_MPDU_STATUS_OK, + htt_rx_status_err_fcs = HTT_RX_IND_MPDU_STATUS_ERR_FCS, + htt_rx_status_err_dup = HTT_RX_IND_MPDU_STATUS_ERR_DUP, + htt_rx_status_err_replay = HTT_RX_IND_MPDU_STATUS_ERR_REPLAY, + htt_rx_status_err_inv_peer = HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER, + htt_rx_status_ctrl_mgmt_null = HTT_RX_IND_MPDU_STATUS_MGMT_CTRL, + htt_rx_status_tkip_mic_err = HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR, + + htt_rx_status_err_misc = HTT_RX_IND_MPDU_STATUS_ERR_MISC +}; + +/** + * @brief Check the status MPDU range referenced by a rx indication message. + * @details + * Check the status of a range of MPDUs referenced by a rx indication message. + * This status determines whether the MPDUs should be processed or discarded. + * If the status is OK, then the MPDUs within the range should be processed + * as usual. + * Otherwise (FCS error, duplicate error, replay error, unknown sender error, + * etc.) the MPDUs within the range should be discarded. + * + * @param pdev - the HTT instance the rx data was received on + * @param rx_ind_msg - the netbuf containing the rx indication message + * @param mpdu_range_num - which MPDU range within the rx ind msg to check, + * starting from 0 + * @param status - (call-by-reference output) MPDU status + * @param mpdu_count - (call-by-reference output) count of MPDUs comprising + * the specified MPDU range + */ +void +htt_rx_ind_mpdu_range_info(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + int mpdu_range_num, + enum htt_rx_status *status, int *mpdu_count); + +/** + * @brief Return the RSSI provided in a rx indication message. + * @details + * Return the RSSI from an rx indication message, converted to dBm units. + * + * @param pdev - the HTT instance the rx data was received on + * @param rx_ind_msg - the netbuf containing the rx indication message + * @return RSSI in dBm, or HTT_INVALID_RSSI + */ +int16_t +htt_rx_ind_rssi_dbm(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg); + +int16_t +htt_rx_ind_rssi_dbm_chain(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg, + int8_t chain); + +void +htt_rx_ind_legacy_rate(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg, + uint8_t *legacy_rate, uint8_t *legacy_rate_sel); + + +void +htt_rx_ind_timestamp(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg, + uint32_t *timestamp_microsec, + uint8_t *timestamp_submicrosec); + +uint32_t +htt_rx_ind_tsf32(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg); + +uint8_t +htt_rx_ind_ext_tid(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg); + + +/*==================== rx MPDU descriptor access methods ====================*/ + +/** + * @brief Check if the retry bit is set in Rx-descriptor + * @details + * This function returns the retry bit of the 802.11 header for the + * provided rx MPDU descriptor. + * + * @param pdev - the handle of the physical device the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @return boolean -- true if retry is set, false otherwise + */ +extern +bool (*htt_rx_mpdu_desc_retry)( + htt_pdev_handle pdev, void *mpdu_desc); + +/** + * @brief Return a rx MPDU's sequence number. + * @details + * This function returns the LSBs of the 802.11 sequence number for the + * provided rx MPDU descriptor. + * Depending on the system, 6-12 LSBs from the 802.11 sequence number are + * returned. (Typically, either the 8 or 12 LSBs are returned.) + * This sequence number is masked with the block ack window size, + * rounded up to a power of two (minus one, to create a bitmask) to obtain + * the corresponding index into the rx reorder holding array. + * + * @param pdev - the HTT instance the rx data was received on + * @param mpdu_desc - the abstract descriptor for the MPDU in question + * @return the LSBs of the sequence number for the MPDU + */ +extern uint16_t +(*htt_rx_mpdu_desc_seq_num)(htt_pdev_handle pdev, void *mpdu_desc); + +/** + * @brief Return a rx MPDU's rx reorder array index, based on sequence number. + * @details + * This function returns a sequence-number based index into the rx + * reorder array for the specified MPDU. + * In some systems, this rx reorder array is simply the LSBs of the + * sequence number, or possibly even the full sequence number. + * To support such systems, the returned index has to be masked with + * the power-of-two array size before using the value to index the + * rx reorder array. + * In other systems, this rx reorder array index is + * (sequence number) % (block ack window size) + * + * @param pdev - the HTT instance the rx data was received on + * @param mpdu_desc - the abstract descriptor for the MPDU in question + * @return the rx reorder array index the MPDU goes into + */ +/* use sequence number (or LSBs thereof) as rx reorder array index */ +#define htt_rx_mpdu_desc_reorder_idx htt_rx_mpdu_desc_seq_num + +union htt_rx_pn_t { + /* WEP: 24-bit PN */ + uint32_t pn24; + + /* TKIP or CCMP: 48-bit PN */ + uint64_t pn48; + + /* WAPI: 128-bit PN */ + uint64_t pn128[2]; +}; + +/** + * @brief Find the packet number (PN) for a MPDU. + * @details + * This function only applies when the rx PN check is configured to be + * performed in the host rather than the target, and on peers using a + * security type for which a PN check applies. + * The pn_len_bits argument is used to determine which element of the + * htt_rx_pn_t union to deposit the PN value read from the MPDU descriptor + * into. + * A 24-bit PN is deposited into pn->pn24. + * A 48-bit PN is deposited into pn->pn48. + * A 128-bit PN is deposited in little-endian order into pn->pn128. + * Specifically, bits 63:0 of the PN are copied into pn->pn128[0], while + * bits 127:64 of the PN are copied into pn->pn128[1]. + * + * @param pdev - the HTT instance the rx data was received on + * @param mpdu_desc - the abstract descriptor for the MPDU in question + * @param pn - the location to copy the packet number into + * @param pn_len_bits - the PN size, in bits + */ +extern void (*htt_rx_mpdu_desc_pn)(htt_pdev_handle pdev, + void *mpdu_desc, + union htt_rx_pn_t *pn, int pn_len_bits); + +/** + * @brief This function Returns the TID value from the Rx descriptor + * for Low Latency driver + * @details + * This function returns the TID set in the 802.11 QoS Control for the MPDU + * in the packet header, by looking at the mpdu_start of the Rx descriptor. + * Rx descriptor gets a copy of the TID from the MAC. + * @pdev: Handle (pointer) to HTT pdev. + * @param mpdu_desc - the abstract descriptor for the MPDU in question + * @return: Actual TID set in the packet header. + */ +extern +uint8_t (*htt_rx_mpdu_desc_tid)( + htt_pdev_handle pdev, void *mpdu_desc); + +/** + * @brief Return the TSF timestamp indicating when a MPDU was received. + * @details + * This function provides the timestamp indicating when the PPDU that + * the specified MPDU belongs to was received. + * + * @param pdev - the HTT instance the rx data was received on + * @param mpdu_desc - the abstract descriptor for the MPDU in question + * @return 32 LSBs of TSF time at which the MPDU's PPDU was received + */ +uint32_t htt_rx_mpdu_desc_tsf32(htt_pdev_handle pdev, void *mpdu_desc); + +/** + * @brief Return the 802.11 header of the MPDU + * @details + * This function provides a pointer to the start of the 802.11 header + * of the Rx MPDU + * + * @param pdev - the HTT instance the rx data was received on + * @param mpdu_desc - the abstract descriptor for the MPDU in question + * @return pointer to 802.11 header of the received MPDU + */ +char *htt_rx_mpdu_wifi_hdr_retrieve(htt_pdev_handle pdev, void *mpdu_desc); + +/** + * @brief Return the RSSI provided in a rx descriptor. + * @details + * Return the RSSI from a rx descriptor, converted to dBm units. + * + * @param pdev - the HTT instance the rx data was received on + * @param mpdu_desc - the abstract descriptor for the MPDU in question + * @return RSSI in dBm, or HTT_INVALID_RSSI + */ +int16_t htt_rx_mpdu_desc_rssi_dbm(htt_pdev_handle pdev, void *mpdu_desc); + +/*==================== rx MSDU descriptor access methods ====================*/ + +/** + * @brief Check if a MSDU completes a MPDU. + * @details + * When A-MSDU aggregation is used, a single MPDU will consist of + * multiple MSDUs. This function checks a MSDU's rx descriptor to + * see whether the MSDU is the final MSDU within a MPDU. + * + * @param pdev - the handle of the physical device the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @return + * 0 - there are subsequent MSDUs within the A-MSDU / MPDU + * -OR- + * 1 - this is the last MSDU within its MPDU + */ +extern bool (*htt_rx_msdu_desc_completes_mpdu)(htt_pdev_handle pdev, + void *msdu_desc); + +/** + * @brief Check if a MSDU is first msdu of MPDU. + * @details + * When A-MSDU aggregation is used, a single MPDU will consist of + * multiple MSDUs. This function checks a MSDU's rx descriptor to + * see whether the MSDU is the first MSDU within a MPDU. + * + * @param pdev - the handle of the physical device the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @return + * 0 - this is interior MSDU in the A-MSDU / MPDU + * -OR- + * 1 - this is the first MSDU within its MPDU + */ +extern bool (*htt_rx_msdu_first_msdu_flag)(htt_pdev_handle pdev, + void *msdu_desc); + +/** + * @brief Retrieve encrypt bit from a mpdu desc. + * @details + * Fw will pass all the frame to the host whether encrypted or not, and will + * indicate the encrypt flag in the desc, this function is to get the info + * and used to make a judge whether should make pn check, because + * non-encrypted frames always get the same pn number 0. + * + * @param pdev - the HTT instance the rx data was received on + * @param mpdu_desc - the abstract descriptor for the MPDU in question + * @return 0 - the frame was not encrypted + * 1 - the frame was encrypted + */ +extern bool (*htt_rx_mpdu_is_encrypted)(htt_pdev_handle pdev, void *mpdu_desc); + +/** + * @brief Indicate whether a rx desc has a WLAN unicast vs. mcast/bcast flag. + * @details + * A flag indicating whether a MPDU was delivered over WLAN as unicast or + * multicast/broadcast may be only valid once per MPDU (LL), or within each + * rx descriptor for the MSDUs within the MPDU (HL). (In practice, it is + * unlikely that A-MSDU aggregation will be used in HL, so typically HL will + * only have one MSDU per MPDU anyway.) + * This function indicates whether the specified rx descriptor contains + * a WLAN ucast vs. mcast/bcast flag. + * + * @param pdev - the HTT instance the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @return + * 0 - The rx descriptor does not contain a WLAN ucast vs. mcast flag. + * -OR- + * 1 - The rx descriptor has a valid WLAN ucast vs. mcast flag. + */ +extern int (*htt_rx_msdu_has_wlan_mcast_flag)(htt_pdev_handle pdev, + void *msdu_desc); + +/** + * @brief Indicate whether a MSDU was received as unicast or mcast/bcast + * @details + * Indicate whether the MPDU that the specified MSDU belonged to was + * delivered over the WLAN as unicast, or as multicast/broadcast. + * This query can only be performed on rx descriptors for which + * htt_rx_msdu_has_wlan_mcast_flag is true. + * + * @param pdev - the HTT instance the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @return + * 0 - The MSDU was delivered over the WLAN as unicast. + * -OR- + * 1 - The MSDU was delivered over the WLAN as broadcast or multicast. + */ +extern bool (*htt_rx_msdu_is_wlan_mcast)(htt_pdev_handle pdev, void *msdu_desc); + +/** + * @brief Indicate whether a MSDU was received as a fragmented frame + * @details + * This query can only be performed on LL system. + * + * @param pdev - the HTT instance the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @return + * 0 - The MSDU was a non-fragmented frame. + * -OR- + * 1 - The MSDU was fragmented frame. + */ +extern int (*htt_rx_msdu_is_frag)(htt_pdev_handle pdev, void *msdu_desc); + +/** + * @brief Indicate if a MSDU should be delivered to the OS shim or discarded. + * @details + * Indicate whether a MSDU should be discarded or delivered to the OS shim. + * + * @param pdev - the HTT instance the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @return + * 0 - The MSDU should be delivered to the OS + * -OR- + * non-zero - The MSDU should not be delivered to the OS. + * If the "forward" flag is set, it should be forwarded to tx. + * Else, it should be discarded. + */ +int htt_rx_msdu_discard(htt_pdev_handle pdev, void *msdu_desc); + +/** + * @brief Indicate whether a MSDU should be forwarded to tx. + * @details + * Indicate whether a MSDU should be forwarded to tx, e.g. for intra-BSS + * STA-to-STA forwarding in an AP, or for multicast echo in an AP. + * + * @param pdev - the HTT instance the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @return + * 0 - The MSDU should not be forwarded + * -OR- + * non-zero - The MSDU should be forwarded. + * If the "discard" flag is set, then the original MSDU can be + * directly forwarded into the tx path. + * Else, a copy (clone?) of the rx MSDU needs to be created to + * send to the tx path. + */ +int htt_rx_msdu_forward(htt_pdev_handle pdev, void *msdu_desc); + +/** + * @brief Indicate whether a MSDU's contents need to be inspected. + * @details + * Indicate whether the host data SW needs to examine the contents of the + * received MSDU, and based on the packet type infer what special handling + * to provide for the MSDU. + * + * @param pdev - the HTT instance the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @return + * 0 - No inspection + special handling is required. + * -OR- + * non-zero - Inspect the MSDU contents to infer what special handling + * to apply to the MSDU. + */ +int htt_rx_msdu_inspect(htt_pdev_handle pdev, void *msdu_desc); + +/** + * @brief Provide all action specifications for a rx MSDU + * @details + * Provide all action specifications together. This provides the same + * information in a single function call as would be provided by calling + * the functions htt_rx_msdu_discard, htt_rx_msdu_forward, and + * htt_rx_msdu_inspect. + * + * @param pdev - the HTT instance the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @param[out] discard - 1: discard the MSDU, 0: deliver the MSDU to the OS + * @param[out] forward - 1: forward the rx MSDU to tx, 0: no rx->tx forward + * @param[out] inspect - 1: process according to MSDU contents, 0: no inspect + */ +void +htt_rx_msdu_actions(htt_pdev_handle pdev, + void *msdu_desc, int *discard, int *forward, int *inspect); + +/** + * @brief Get the key id sent in IV of the frame + * @details + * Provide the key index octet which is taken from IV. + * This is valid only for the first MSDU. + * + * @param pdev - the HTT instance the rx data was received on + * @param msdu_desc - the abstract descriptor for the MSDU in question + * @key_id - Key id octet + * @return indication of whether key id access is successful + * true - Success + * false - if this is not first msdu + */ +extern bool +(*htt_rx_msdu_desc_key_id)(htt_pdev_handle pdev, + void *mpdu_desc, uint8_t *key_id); + +extern bool +(*htt_rx_msdu_chan_info_present)( + htt_pdev_handle pdev, + void *mpdu_desc); + +extern bool +(*htt_rx_msdu_center_freq)( + htt_pdev_handle pdev, + struct ol_txrx_peer_t *peer, + void *mpdu_desc, + uint16_t *primary_chan_center_freq_mhz, + uint16_t *contig_chan1_center_freq_mhz, + uint16_t *contig_chan2_center_freq_mhz, + uint8_t *phy_mode); + +/*====================== rx MSDU + descriptor delivery ======================*/ + +/** + * @brief Return a linked-list of network buffer holding the next rx A-MSDU. + * @details + * In some systems, the rx MSDUs are uploaded along with the rx + * indication message, while in other systems the rx MSDUs are uploaded + * out of band, via MAC DMA. + * This function provides an abstract way to obtain a linked-list of the + * next MSDUs, regardless of whether the MSDU was delivered in-band with + * the rx indication message, or out of band through MAC DMA. + * In a LL system, this function returns a linked list of the one or more + * MSDUs that together comprise an A-MSDU. + * In a HL system, this function returns a degenerate linked list consisting + * of a single MSDU (head_msdu == tail_msdu). + * This function also makes sure each MSDU's rx descriptor can be found + * through the MSDU's network buffer. + * In most systems, this is trivial - a single network buffer stores both + * the MSDU rx descriptor and the MSDU payload. + * In systems where the rx descriptor is in a separate buffer from the + * network buffer holding the MSDU payload, a pointer to the rx descriptor + * has to be stored in the network buffer. + * After this function call, the descriptor for a given MSDU can be + * obtained via the htt_rx_msdu_desc_retrieve function. + * + * @param pdev - the HTT instance the rx data was received on + * @param rx_ind_msg - the netbuf containing the rx indication message + * @param head_msdu - call-by-reference network buffer handle, which gets set + * in this function to point to the head MSDU of the A-MSDU + * @param tail_msdu - call-by-reference network buffer handle, which gets set + * in this function to point to the tail MSDU of the A-MSDU, or the + * same MSDU that the head_msdu points to if only a single MSDU is + * delivered at a time. + * @return indication of whether any MSDUs in the AMSDU use chaining: + * 0 - no buffer chaining + * 1 - buffers are chained + */ +extern int +(*htt_rx_amsdu_pop)(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, qdf_nbuf_t *tail_msdu, + uint32_t *msdu_count); + +extern int +(*htt_rx_frag_pop)(htt_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + qdf_nbuf_t *head_msdu, qdf_nbuf_t *tail_msdu, + uint32_t *msdu_count); + +/** + * @brief Return the maximum number of available msdus currently + * + * @param pdev - the HTT instance the rx data was received on + */ +extern int +(*htt_rx_offload_msdu_cnt)( + htt_pdev_handle pdev); + +/** + * @brief Return a linked list of buffers holding one MSDU + * In some systems the buffers are delivered along with offload delivery + * indication message itself, while in other systems the buffers are uploaded + * out of band, via MAC DMA. + * @details + * This function provides an abstract way to obtain a linked-list of the + * buffers corresponding to an msdu, regardless of whether the MSDU was + * delivered in-band with the rx indication message, or out of band through + * MAC DMA. + * In a LL system, this function returns a linked list of one or more + * buffers corresponding to an MSDU + * In a HL system , TODO + * + * @param pdev - the HTT instance the rx data was received on + * @param offload_deliver_msg - the nebuf containing the offload deliver message + * @param head_msdu - call-by-reference network buffer handle, which gets set in + * this function to the head buffer of this MSDU + * @param tail_msdu - call-by-reference network buffer handle, which gets set in + * this function to the tail buffer of this MSDU + */ +extern int +(*htt_rx_offload_msdu_pop)(htt_pdev_handle pdev, + qdf_nbuf_t offload_deliver_msg, + int *vdev_id, + int *peer_id, + int *tid, + uint8_t *fw_desc, + qdf_nbuf_t *head_buf, qdf_nbuf_t *tail_buf); + +/** + * @brief Return the rx descriptor for the next rx MPDU. + * @details + * The rx MSDU descriptors may be uploaded as part of the rx indication + * message, or delivered separately out of band. + * This function provides an abstract way to obtain the next MPDU descriptor, + * regardless of whether the MPDU descriptors are delivered in-band with + * the rx indication message, or out of band. + * This is used to iterate through the series of MPDU descriptors referenced + * by a rx indication message. + * The htt_rx_amsdu_pop function should be called before this function + * (or at least before using the returned rx descriptor handle), so that + * the cache location for the rx descriptor will be flushed before the + * rx descriptor gets used. + * + * @param pdev - the HTT instance the rx data was received on + * @param rx_ind_msg - the netbuf containing the rx indication message + * @return next abstract rx descriptor from the series of MPDUs referenced + * by an rx ind msg + */ +extern void * +(*htt_rx_mpdu_desc_list_next)(htt_pdev_handle pdev, qdf_nbuf_t rx_ind_msg); + +/** + * @brief Retrieve a previously-stored rx descriptor from a MSDU buffer. + * @details + * The data SW will call the htt_rx_msdu_desc_link macro/function to + * link a MSDU's rx descriptor with the buffer holding the MSDU payload. + * This function retrieves the rx MSDU descriptor. + * + * @param pdev - the HTT instance the rx data was received on + * @param msdu - the buffer containing the MSDU payload + * @return the corresponding abstract rx MSDU descriptor + */ +extern void * +(*htt_rx_msdu_desc_retrieve)(htt_pdev_handle pdev, qdf_nbuf_t msdu); + +/** + * @brief Free both an rx MSDU descriptor and the associated MSDU buffer. + * @details + * Usually the WLAN driver does not free rx MSDU buffers, but needs to + * do so when an invalid frame (e.g. FCS error) was deposited into the + * queue of rx buffers. + * This function frees both the rx descriptor and the rx frame. + * On some systems, the rx descriptor and rx frame are stored in the + * same buffer, and thus one free suffices for both objects. + * On other systems, the rx descriptor and rx frame are stored + * separately, so distinct frees are internally needed. + * However, in either case, the rx descriptor has been associated with + * the MSDU buffer, and can be retrieved by htt_rx_msdu_desc_retrieve. + * Hence, it is only necessary to provide the MSDU buffer; the HTT SW + * internally finds the corresponding MSDU rx descriptor. + * + * @param htt_pdev - the HTT instance the rx data was received on + * @param rx_msdu_desc - rx descriptor for the MSDU being freed + * @param msdu - rx frame buffer for the MSDU being freed + */ +void htt_rx_desc_frame_free(htt_pdev_handle htt_pdev, qdf_nbuf_t msdu); + +/** + * @brief Look up and free the rx descriptor for a MSDU. + * @details + * When the driver delivers rx frames to the OS, it first needs + * to free the associated rx descriptors. + * In some systems the rx descriptors are allocated in the same + * buffer as the rx frames, so this operation is a no-op. + * In other systems, the rx descriptors are stored separately + * from the rx frames, so the rx descriptor has to be freed. + * The descriptor is located from the MSDU buffer with the + * htt_rx_desc_frame_free macro/function. + * + * @param htt_pdev - the HTT instance the rx data was received on + * @param msdu - rx frame buffer for the rx MSDU descriptor being freed + */ +void htt_rx_msdu_desc_free(htt_pdev_handle htt_pdev, qdf_nbuf_t msdu); + +/** + * @brief Add new MSDU buffers for the target to fill. + * @details + * In some systems, the underlying upload mechanism (HIF) allocates new rx + * buffers itself. In other systems, the underlying upload mechanism + * (MAC DMA) needs to be provided with new rx buffers. + * This function is used as an abstract method to indicate to the underlying + * data upload mechanism when it is an appropriate time to allocate new rx + * buffers. + * If the allocation is automatically handled, a la HIF, then this function + * call is ignored. + * If the allocation has to be done explicitly, a la MAC DMA, then this + * function provides the context and timing for such replenishment + * allocations. + * + * @param pdev - the HTT instance the rx data will be received on + */ +void htt_rx_msdu_buff_replenish(htt_pdev_handle pdev); + +/** + * @brief Add new MSDU buffers for the target to fill. + * @details + * This is full_reorder_offload version of the replenish function. + * In full_reorder, FW sends HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND + * msg to host. It includes the number of MSDUs. Thgis will be fed + * into htt_rx_msdu_buff_in_order_replenish function. + * The reason for creating yet another function is to avoid checks + * in real-time. + * + * @param pdev - the HTT instance the rx data will be received on + * @num - number of buffers to replenish + * + * Return: number of buffers actually replenished + */ +#ifndef CONFIG_HL_SUPPORT +int htt_rx_msdu_buff_in_order_replenish(htt_pdev_handle pdev, uint32_t num); +#else +static inline +int htt_rx_msdu_buff_in_order_replenish(htt_pdev_handle pdev, uint32_t num) +{ + return 0; +} +#endif + +/** + * @brief Links list of MSDUs into an single MPDU. Updates RX stats + * @details + * When HW MSDU splitting is turned on each MSDU in an AMSDU MPDU occupies + * a separate wbuf for delivery to the network stack. For delivery to the + * monitor mode interface they need to be restitched into an MPDU. This + * function does this. Also updates the RX status if the MPDU starts + * a new PPDU + * + * @param pdev - the HTT instance the rx data was received on + * @param head_msdu - network buffer handle, which points to the first MSDU + * in the list. This is a NULL terminated list + * @param rx_staus - pointer to the status associated with this MPDU. + * Updated only if there is a new PPDU and new status associated with it + * @param clone_not_reqd - If set the MPDU linking destroys the passed in + * list, else operates on a cloned nbuf + * @return network buffer handle to the MPDU + */ +#if defined(FEATURE_MONITOR_MODE_SUPPORT) +#if !defined(QCA6290_HEADERS_DEF) && !defined(QCA6390_HEADERS_DEF) && \ + !defined(QCA6490_HEADERS_DEF) && !defined(QCA6750_HEADERS_DEF) +qdf_nbuf_t +htt_rx_restitch_mpdu_from_msdus(htt_pdev_handle pdev, + qdf_nbuf_t head_msdu, + struct ieee80211_rx_status *rx_status, + unsigned clone_not_reqd); +#else +static inline qdf_nbuf_t +htt_rx_restitch_mpdu_from_msdus(htt_pdev_handle pdev, + qdf_nbuf_t head_msdu, + struct ieee80211_rx_status *rx_status, + unsigned clone_not_reqd) +{ + return NULL; +} +#endif +#endif +/** + * @brief Return the sequence number of MPDUs to flush. + * @param pdev - the HTT instance the rx data was received on + * @param rx_frag_ind_msg - the netbuf containing the rx fragment indication + * message + * @param seq_num_start - (call-by-reference output) sequence number + * for the start of the range of MPDUs to flush + * @param seq_num_end - (call-by-reference output) sequence number + * for the end of the range of MPDUs to flush + */ +void +htt_rx_frag_ind_flush_seq_num_range(htt_pdev_handle pdev, + qdf_nbuf_t rx_frag_ind_msg, + uint16_t *seq_num_start, uint16_t *seq_num_end); + +#ifdef CONFIG_HL_SUPPORT +/** + * htt_rx_msdu_rx_desc_size_hl() - Return the HL rx desc size + * @pdev: the HTT instance the rx data was received on. + * @msdu_desc: the hl rx desc pointer + * + * Return: HL rx desc size + */ +uint16_t htt_rx_msdu_rx_desc_size_hl(htt_pdev_handle pdev, void *msdu_desc); +#else +static inline +uint16_t htt_rx_msdu_rx_desc_size_hl(htt_pdev_handle pdev, void *msdu_desc) +{ + return 0; +} +#endif + +/** + * @brief populates vowext stats by processing RX desc. + * @param msdu - network buffer handle + * @param vowstats - handle to vow ext stats. + */ +void htt_rx_get_vowext_stats(qdf_nbuf_t msdu, struct vow_extstats *vowstats); + +/** + * @brief parses the offload message passed by the target. + * @param pdev - pdev handle + * @param paddr - physical address of the rx buffer + * @param vdev_id - reference to vdev id to be filled + * @param peer_id - reference to the peer id to be filled + * @param tid - reference to the tid to be filled + * @param fw_desc - reference to the fw descriptor to be filled + * @param peer_id - reference to the peer id to be filled + * @param head_buf - reference to the head buffer + * @param tail_buf - reference to the tail buffer + */ +int +htt_rx_offload_paddr_msdu_pop_ll(htt_pdev_handle pdev, + uint32_t *msg_word, + int msdu_iter, + int *vdev_id, + int *peer_id, + int *tid, + uint8_t *fw_desc, + qdf_nbuf_t *head_buf, qdf_nbuf_t *tail_buf); + +uint32_t htt_rx_amsdu_rx_in_order_get_pktlog(qdf_nbuf_t rx_ind_msg); + +/** + * htt_rx_update_smmu_map() - set smmu map/unmap for rx buffers + * @pdev: htt pdev handle + * @map: value to set smmu map/unmap for rx buffers + * + * Return: QDF_STATUS + */ +QDF_STATUS htt_rx_update_smmu_map(struct htt_pdev_t *pdev, bool map); + +/** htt_tx_enable_ppdu_end + * @enable_ppdu_end - set it to 1 if WLAN_FEATURE_TSF_PLUS is defined, + * else do nothing + */ +#ifdef WLAN_FEATURE_TSF_PLUS +void htt_rx_enable_ppdu_end(int *enable_ppdu_end); +#else +static inline +void htt_rx_enable_ppdu_end(int *enable_ppdu_end) +{ +} +#endif + +#endif /* _OL_HTT_RX_API__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_tx_api.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_tx_api.h new file mode 100644 index 0000000000000000000000000000000000000000..0f091427dad7a277ddd7a3036cdb20a2c46bc229 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_tx_api.h @@ -0,0 +1,843 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_htt_tx_api.h + * @brief Specify the tx HTT API functions called by the host data SW. + * @details + * This file declares the HTT API functions that are specifically + * related to transmit processing. + * In particular, the methods of the abstract HTT tx descriptor are + * specified. + */ +#ifndef _OL_HTT_TX_API__H_ +#define _OL_HTT_TX_API__H_ + +/* #include / * uint16_t, etc. * / */ +#include /* uint16_t, etc. */ +#include /* qdf_nbuf_t */ +#include /* wlan_frm_fmt */ + +#include /* needed by inline functions */ +#include +#include /* htt_pdev_handle */ +#include +#include +#include + +#define HTT_INVALID_CHANNEL -1 + +/* Remove these macros when they get added to htt.h. */ +#ifndef HTT_TX_DESC_EXTENSION_GET +#define HTT_TX_DESC_EXTENSION_OFFSET_DWORD 0 +#define HTT_TX_DESC_EXTENSION_M 0x10000000 +#define HTT_TX_DESC_EXTENSION_S 28 + +#define HTT_TX_DESC_EXTENSION_GET(_var) \ + (((_var) & HTT_TX_DESC_EXTENSION_M) >> HTT_TX_DESC_EXTENSION_S) +#define HTT_TX_DESC_EXTENSION_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_TX_DESC_EXTENSION, _val); \ + ((_var) |= ((_val) << HTT_TX_DESC_EXTENSION_S)); \ + } while (0) +#endif + +/*================ meta-info about tx MSDUs =================================*/ + +/* + * For simplicity, use the IEEE 802.11 frame type values. + */ +enum htt_frm_type { + htt_frm_type_mgmt = 0, + htt_frm_type_ctrl = 1, + htt_frm_type_data = 2 +}; + +/* + * For simplicity, use the IEEE 802.11 frame sub-type values. + */ +enum htt_frm_subtype { + htt_frm_subtype_mgmt_assoc_req = 0, + htt_frm_subtype_mgmt_assoc_resp = 1, + htt_frm_subtype_mgmt_reassoc_req = 2, + htt_frm_subtype_mgmt_reassoc_resp = 3, + htt_frm_subtype_mgmt_probe_req = 4, + htt_frm_subtype_mgmt_probe_resp = 5, + htt_frm_subtype_mgmt_timing_adv = 6, + htt_frm_subtype_mgmt_beacon = 8, + htt_frm_subtype_mgmt_atim = 9, + htt_frm_subtype_mgmt_disassoc = 10, + htt_frm_subtype_mgmt_auth = 11, + htt_frm_subtype_mgmt_deauth = 12, + htt_frm_subtype_mgmt_action = 13, + htt_frm_subtype_mgmt_action_no_ack = 14, + + htt_frm_subtype_data_data = 0, + htt_frm_subtype_data_data_cf_ack = 1, + htt_frm_subtype_data_data_cf_poll = 2, + htt_frm_subtype_data_data_cf_ack_cf_poll = 3, + htt_frm_subtype_data_null = 4, + htt_frm_subtype_data_cf_ack = 5, + htt_frm_subtype_data_cf_poll = 6, + htt_frm_subtype_data_cf_ack_cf_poll = 7, + htt_frm_subtype_data_QoS_data = 8, + htt_frm_subtype_data_QoS_data_cf_ack = 9, + htt_frm_subtype_data_QoS_data_cf_poll = 10, + htt_frm_subtype_data_QoS_data_cf_ack_cf_poll = 11, + htt_frm_subtype_data_QoS_null = 12, + htt_frm_subtype_data_QoS_cf_poll = 14, + htt_frm_subtype_data_QoS_cf_ack_cf_poll = 15, +}; + +enum htt_ofdm_datarate { /* Value MBPS Modulation Coding*/ + htt_ofdm_datarate_6_mbps = 0, /* 0 6 BPSK 1/2 */ + htt_ofdm_datarate_9_mbps = 1, /* 1 9 BPSK 3/4 */ + htt_ofdm_datarate_12_mbps = 2, /* 2 12 QPSK 1/2 */ + htt_ofdm_datarate_18_mbps = 3, /* 3 18 QPSK 3/4 */ + htt_ofdm_datarate_24_mbps = 4, /* 4 24 16-QAM 1/2 */ + htt_ofdm_datarate_36_mbps = 5, /* 5 36 16-QAM 3/4 */ + htt_ofdm_datarate_48_mbps = 6, /* 6 48 64-QAM 1/2 */ + htt_ofdm_datarate_54_mbps = 7, /* 7 54 64-QAM 3/4 */ + htt_ofdm_datarate_max = 7, +}; + +/** + * struct ocb_tx_ctrl_hdr_t - TX control header + * @version: must be 1 + * @length: length of this structure + * @channel_freq: channel on which to transmit the packet + * @valid_pwr: bit 0: if set, tx pwr spec is valid + * @valid_datarate: bit 1: if set, tx MCS mask spec is valid + * @valid_retries: bit 2: if set, tx retries spec is valid + * @valid_chain_mask: bit 3: if set, chain mask is valid + * @valid_expire_tsf: bit 4: if set, tx expire TSF spec is valid + * @valid_tid: bit 5: if set, TID is valid + * @reserved0_15_6: bits 15:6 - unused, set to 0x0 + * @all_flags: union of all the flags + * @expire_tsf_lo: TX expiry time (TSF) LSBs + * @expire_tsf_hi: TX expiry time (TSF) MSBs + * @pwr: Specify what power the tx frame needs to be transmitted + * at. The power a signed (two's complement) value is in + * units of 0.5 dBm. The value needs to be appropriately + * sign-extended when extracting the value from the message + * and storing it in a variable that is larger than A_INT8. + * If the transmission uses multiple tx chains, this power + * spec is the total transmit power, assuming incoherent + * combination of per-chain power to produce the total + * power. + * @datarate: The desired modulation and coding scheme. + * VALUE DATA RATE MODULATION CODING RATE + * @ 20 MHz + * (MBPS) + * 0 6 BPSK 1/2 + * 1 9 BPSK 3/4 + * 2 12 QPSK 1/2 + * 3 18 QPSK 3/4 + * 4 24 16-QAM 1/2 + * 5 36 16-QAM 3/4 + * 6 48 64-QAM 1/2 + * 7 54 64-QAM 3/4 + * @retry_limit: Specify the maximum number of transmissions, including + * the initial transmission, to attempt before giving up if + * no ack is received. + * If the tx rate is specified, then all retries shall use + * the same rate as the initial transmission. + * If no tx rate is specified, the target can choose + * whether to retain the original rate during the + * retransmissions, or to fall back to a more robust rate. + * @chain_mask: specify which chains to transmit from + * @ext_tid: Extended Traffic ID (0-15) + * @reserved: Ensure that the size of the structure is a multiple of + * 4. Must be 0. + * + * When sending an OCB packet, the user application has + * the option of including the following struct following an ethernet header + * with the proto field set to 0x8151. This struct includes various TX + * paramaters including the TX power and MCS. + */ +PREPACK struct ocb_tx_ctrl_hdr_t { + uint16_t version; + uint16_t length; + uint16_t channel_freq; + + union { + struct { + uint16_t + valid_pwr:1, + valid_datarate:1, + valid_retries:1, + valid_chain_mask:1, + valid_expire_tsf:1, + valid_tid:1, + reserved0_15_6:10; + }; + uint16_t all_flags; + }; + + uint32_t expire_tsf_lo; + uint32_t expire_tsf_hi; + int8_t pwr; + uint8_t datarate; + uint8_t retry_limit; + uint8_t chain_mask; + uint8_t ext_tid; + uint8_t reserved[3]; +} POSTPACK; + +/** + * @brief tx MSDU meta-data that HTT may use to program the FW/HW tx descriptor + */ +struct htt_msdu_info_t { + /* the info sub-struct specifies the characteristics of the MSDU */ + struct { + uint16_t ethertype; +#define HTT_INVALID_PEER_ID 0xffff + uint16_t peer_id; + uint8_t vdev_id; + uint8_t ext_tid; + /* + * l2_hdr_type - L2 format (802.3, native WiFi 802.11, + * or raw 802.11) + * Based on attach-time configuration, the tx frames provided + * by the OS to the tx data SW are expected to be either + * 802.3 format or the "native WiFi" variant of 802.11 format. + * Internally, the driver may also inject tx frames into the tx + * datapath, and these frames may be either 802.3 format or + * 802.11 "raw" format, with no further 802.11 encapsulation + * needed. + * The tx frames are tagged with their frame format, so target + * FW/HW will know how to interpret the packet's encapsulation + * headers when doing tx classification, and what form of 802.11 + * header encapsulation is needed, if any. + */ + uint8_t l2_hdr_type; /* enum htt_pkt_type */ + /* + * frame_type - is the tx frame management or data? + * Just to avoid confusion, the enum values for this frame type + * field use the 802.11 frame type values, although it is + * unexpected for control frames to be sent through the host + * data path. + */ + uint8_t frame_type; /* enum htt_frm_type */ + /* + * frame subtype - this field specifies the sub-type of + * management frames + * Just to avoid confusion, the enum values for this frame + * subtype field use the 802.11 management frame subtype values. + */ + uint8_t frame_subtype; /* enum htt_frm_subtype */ + uint8_t is_unicast; + + /* dest_addr is not currently used. + * It could be used as an input to a Tx BD (Riva tx descriptor) + * signature computation. + uint8_t *dest_addr; + */ + + uint8_t l3_hdr_offset; /* wrt qdf_nbuf_data(msdu), in bytes */ + + /* l4_hdr_offset is not currently used. + * It could be used to specify to a TCP/UDP checksum computation + * engine where the TCP/UDP header starts. + */ + /* uint8_t l4_hdr_offset; - wrt qdf_nbuf_data(msdu), in bytes */ + } info; + /* the action sub-struct specifies how to process the MSDU */ + struct { + /* mgmt frames: option to force 6 Mbps rate */ + uint8_t use_6mbps; + uint8_t do_encrypt; + uint8_t do_tx_complete; + uint8_t tx_comp_req; + + /* + * cksum_offload - Specify whether checksum offload is + * enabled or not + * Target FW uses this flag to turn on HW checksumming + * 0x0 - No checksum offload + * 0x1 - L3 header checksum only + * 0x2 - L4 checksum only + * 0x3 - L3 header checksum + L4 checksum + */ + qdf_nbuf_tx_cksum_t cksum_offload; + } action; +}; + +static inline void htt_msdu_info_dump(struct htt_msdu_info_t *msdu_info) +{ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "HTT MSDU info object (%pK)\n", msdu_info); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " ethertype: %#x\n", msdu_info->info.ethertype); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " peer_id: %d\n", msdu_info->info.peer_id); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " vdev_id: %d\n", msdu_info->info.vdev_id); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " ext_tid: %d\n", msdu_info->info.ext_tid); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " l2_hdr_type: %d\n", msdu_info->info.l2_hdr_type); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " frame_type: %d\n", msdu_info->info.frame_type); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " frame_subtype: %d\n", msdu_info->info.frame_subtype); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " is_unicast: %u\n", msdu_info->info.is_unicast); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " l3_hdr_offset: %u\n", msdu_info->info.l3_hdr_offset); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " use 6 Mbps: %d\n", msdu_info->action.use_6mbps); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " do_encrypt: %d\n", msdu_info->action.do_encrypt); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " do_tx_complete: %d\n", msdu_info->action.do_tx_complete); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " is_unicast: %u\n", msdu_info->info.is_unicast); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + " is_unicast: %u\n", msdu_info->info.is_unicast); +} + +/*================ tx completion message field access methods ===============*/ + +/** + * @brief Look up the descriptor ID of the nth MSDU from a tx completion msg. + * @details + * A tx completion message tells the host that the target is done + * transmitting a series of MSDUs. The message uses a descriptor ID + * to identify each such MSDU. This function/macro is used to + * find the ID of one such MSDU referenced by the tx completion message. + * + * @param iterator - tx completion message context provided by HTT to the + * tx completion message handler. This abstract reference to the + * HTT tx completion message's payload allows the data SW's tx + * completion handler to not care about the format of the HTT + * tx completion message. + * @param num - (zero-based) index to specify a single MSDU within the + * series of MSDUs referenced by the tx completion message + * @return descriptor ID for the specified MSDU + */ +uint16_t htt_tx_compl_desc_id(void *iterator, int num); + +/*========================= tx descriptor operations ========================*/ + +/** + * @brief Allocate a HTT abstract tx descriptor. + * @details + * Allocate a HTT abstract tx descriptor from a pool within "consistent" + * memory, which is accessible by HIF and/or MAC DMA as well as by the + * host CPU. + * It is expected that the tx datapath will allocate HTT tx descriptors + * and link them with datapath SW tx descriptors up front as the driver + * is loaded. Thereafter, the link from datapath SW tx descriptor to + * HTT tx descriptor will be maintained until the driver is unloaded. + * + * @param htt_pdev - handle to the HTT instance making the allocation + * @param[OUT] paddr_lo - physical address of the HTT descriptor + * @return success -> descriptor handle, -OR- failure -> NULL + */ +void *htt_tx_desc_alloc(htt_pdev_handle pdev, qdf_dma_addr_t *paddr, + uint16_t index); + +/** + * @brief Free a HTT abstract tx descriptor. + * + * @param htt_pdev - handle to the HTT instance that made the allocation + * @param htt_tx_desc - the descriptor to free + */ +void htt_tx_desc_free(htt_pdev_handle htt_pdev, void *htt_tx_desc); + +#if defined(HELIUMPLUS) +/** + * @brief Allocate TX frag descriptor + * @details + * Allocate TX frag descriptor + * + * @param pdev - handle to the HTT instance that made the allocation + * @param index - tx descriptor index + * @param frag_paddr_lo - fragment descriptor physical address lower 32bits + * @param frag_ptr - fragment descriptor hlos pointe + * @return success 0 + */ +int htt_tx_frag_alloc(htt_pdev_handle pdev, + u_int16_t index, qdf_dma_addr_t *frag_paddr, void **frag_ptr); +#else +static inline int htt_tx_frag_alloc(htt_pdev_handle pdev, + u_int16_t index, qdf_dma_addr_t *frag_paddr, void **frag_ptr) +{ + *frag_ptr = NULL; + return 0; +} +#endif /* defined(HELIUMPLUS) */ + +#if defined(CONFIG_HL_SUPPORT) + +/** + * @brief Discard all tx frames in the process of being downloaded. + * @details + * This function dicards any tx frames queued in HTT or the layers + * under HTT. + * The download completion callback is invoked on these frames. + * + * @param htt_pdev - handle to the HTT instance + * @param[OUT] frag_paddr_lo - physical address of the fragment descriptor + * (MSDU Link Extension Descriptor) + */ +static inline void htt_tx_pending_discard(htt_pdev_handle pdev) +{ +} +#else + +void htt_tx_pending_discard(htt_pdev_handle pdev); +#endif + +/** + * @brief Download a MSDU descriptor and (a portion of) the MSDU payload. + * @details + * This function is used within LL systems to download a tx descriptor and + * the initial portion of the tx MSDU payload, and within HL systems to + * download the tx descriptor and the entire tx MSDU payload. + * The HTT layer determines internally how much of the tx descriptor + * actually needs to be downloaded. In particular, the HTT layer does not + * download the fragmentation descriptor, and only for the LL case downloads + * the physical address of the fragmentation descriptor. + * In HL systems, the tx descriptor and the entire frame are downloaded. + * In LL systems, only the tx descriptor and the header of the frame are + * downloaded. To determine how much of the tx frame to download, this + * function assumes the tx frame is the default frame type, as specified + * by ol_cfg_frame_type. "Raw" frames need to be transmitted through the + * alternate htt_tx_send_nonstd function. + * The tx descriptor has already been attached to the qdf_nbuf object during + * a preceding call to htt_tx_desc_init. + * + * @param htt_pdev - the handle of the physical device sending the tx data + * @param msdu - the frame being transmitted + * @param msdu_id - unique ID for the frame being transmitted + * @return 0 -> success, -OR- 1 -> failure + */ +int +htt_tx_send_std(htt_pdev_handle htt_pdev, qdf_nbuf_t msdu, uint16_t msdu_id); + +/** + * @brief Download a Batch Of Tx MSDUs + * @details + * Each MSDU already has the MSDU ID stored in the headroom of the + * netbuf data buffer, and has the HTT tx descriptor already attached + * as a prefix fragment to the netbuf. + * + * @param htt_pdev - the handle of the physical device sending the tx data + * @param head_msdu - the MSDU Head for Tx batch being transmitted + * @param num_msdus - The total Number of MSDU's provided for batch tx + * @return null-terminated linked-list of unaccepted frames + */ +qdf_nbuf_t +htt_tx_send_batch(htt_pdev_handle htt_pdev, + qdf_nbuf_t head_msdu, int num_msdus); + +/* The htt scheduler for queued packets in htt + * htt when unable to send to HTC because of lack of resource + * forms a nbuf queue which is flushed when tx completion event from + * target is received + */ + +void htt_tx_sched(htt_pdev_handle pdev); + +/** + * @brief Same as htt_tx_send_std, but can handle raw frames. + */ +int +htt_tx_send_nonstd(htt_pdev_handle htt_pdev, + qdf_nbuf_t msdu, + uint16_t msdu_id, enum htt_pkt_type pkt_type); + +/** + * htt_pkt_dl_len_get() Gets the HTT PKT download length. + * @pdev: pointer to struct htt_pdev_t + * + * Return: size of HTT packet download length. + */ +int +htt_pkt_dl_len_get(struct htt_pdev_t *pdev); + +/* Used to set classify bit in HTT desc.*/ +#define HTT_TX_CLASSIFY_BIT_S 4 + +/** + * enum htt_ce_tx_pkt_type - enum of packet types to be set in CE + * descriptor + * @tx_pkt_type_raw: Value set for RAW frames + * @tx_pkt_type_native_wifi: Value set for NATIVE WIFI frames + * @tx_pkt_type_eth2: Value set for Ethernet II frames (mostly default) + * @tx_pkt_type_802_3: Value set for 802.3 / original ethernet frames + * @tx_pkt_type_mgmt: Value set for MGMT frames over HTT + * + */ +enum htt_ce_tx_pkt_type { + tx_pkt_type_raw = 0, + tx_pkt_type_native_wifi = 1, + tx_pkt_type_eth2 = 2, + tx_pkt_type_802_3 = 3, + tx_pkt_type_mgmt = 4 +}; + +/** + * enum extension_header_type - extension header type + * @EXT_HEADER_NOT_PRESENT: extension header not present + * @OCB_MODE_EXT_HEADER: Extension header for OCB mode + * @WISA_MODE_EXT_HEADER_6MBPS: WISA mode 6Mbps header + * @WISA_MODE_EXT_HEADER_24MBPS: WISA mode 24Mbps header + */ +enum extension_header_type { + EXT_HEADER_NOT_PRESENT, + OCB_MODE_EXT_HEADER, + WISA_MODE_EXT_HEADER_6MBPS, + WISA_MODE_EXT_HEADER_24MBPS, +}; + +extern const uint32_t htt_to_ce_pkt_type[]; + +/** + * Provide a constant to specify the offset of the HTT portion of the + * HTT tx descriptor, to avoid having to export the descriptor definition. + * The htt module checks internally that this exported offset is consistent + * with the private tx descriptor definition. + * + * Similarly, export a definition of the HTT tx descriptor size, and then + * check internally that this exported constant matches the private tx + * descriptor definition. + */ +#define HTT_TX_DESC_VADDR_OFFSET 8 + +/** + * htt_tx_desc_init() - Initialize the per packet HTT Tx descriptor + * @pdev: The handle of the physical device sending the + * tx data + * @htt_tx_desc: Abstract handle to the tx descriptor + * @htt_tx_desc_paddr_lo: Physical address of the HTT tx descriptor + * @msdu_id: ID to tag the descriptor with. + * The FW sends this ID back to host as a cookie + * during Tx completion, which the host uses to + * identify the MSDU. + * This ID is an index into the OL Tx desc. array. + * @msdu: The MSDU that is being prepared for transmission + * @msdu_info: Tx MSDU meta-data + * @tso_info: Storage for TSO meta-data + * @ext_header_data: extension header data + * @type: extension header type + * + * This function initializes the HTT tx descriptor. + * HTT Tx descriptor is a host-f/w interface structure, and meta-data + * accompanying every packet downloaded to f/w via the HTT interface. + * + * Return QDF_STATUS_SUCCESS for success, otherwise error. + */ +QDF_STATUS +htt_tx_desc_init(htt_pdev_handle pdev, + void *htt_tx_desc, + qdf_dma_addr_t htt_tx_desc_paddr, + uint16_t msdu_id, + qdf_nbuf_t msdu, struct htt_msdu_info_t *msdu_info, + struct qdf_tso_info_t *tso_info, + void *ext_header_data, + enum extension_header_type type); + +/** + * @brief Set a flag to indicate that the MSDU in question was postponed. + * @details + * In systems in which the host retains its tx frame until the target sends + * a tx completion, the target has the option of discarding it's copy of + * the tx descriptor (and frame, for HL) and sending a "postpone" message + * to the host, to inform the host that it must eventually download the + * tx descriptor (and frame, for HL). + * Before the host downloads the postponed tx desc/frame again, it will use + * this function to set a flag in the HTT tx descriptor indicating that this + * is a re-send of a postponed frame, rather than a new frame. The target + * uses this flag to keep the correct order between re-sent and new tx frames. + * This function is relevant for LL systems. + * + * @param pdev - the handle of the physical device sending the tx data + * @param desc - abstract handle to the tx descriptor + */ +void htt_tx_desc_flag_postponed(htt_pdev_handle pdev, void *desc); + +/** + * @brief Set a flag to tell the target that more tx downloads are en route. + * @details + * At times, particularly in response to a U-APSD trigger in a HL system, the + * host will download multiple tx descriptors (+ frames, in HL) in a batch. + * The host will use this function to set a "more" flag in the initial + * and interior frames of the batch, to tell the target that more tx frame + * downloads within the batch are imminent. + * + * @param pdev - the handle of the physical device sending the tx data + * @param desc - abstract handle to the tx descriptor + */ +void htt_tx_desc_flag_batch_more(htt_pdev_handle pdev, void *desc); + +/** + * @brief Specify the number of fragments in the fragmentation descriptor. + * @details + * Specify the number of fragments within the MSDU, i.e. the number of + * elements within the fragmentation descriptor. + * For LL, this is used to terminate the list of fragments used by the + * HW's tx MAC DMA. + * For HL, this is used to terminate the list of fragments provided to + * HTC for download. + * + * @param pdev - the handle of the physical device sending the tx data + * @param desc - abstract handle to the tx descriptor + * @param num_frags - the number of fragments comprising the MSDU + */ +static inline +void +htt_tx_desc_num_frags(htt_pdev_handle pdev, void *desc, uint32_t num_frags) +{ + /* + * Set the element after the valid frag elems to 0x0, + * to terminate the list of fragments. + */ +#if defined(HELIUMPLUS) + if (HTT_WIFI_IP(pdev, 2, 0)) { + struct msdu_ext_frag_desc *fdesc; + + /** Skip TSO related 4 dwords WIFI2.0*/ + fdesc = (struct msdu_ext_frag_desc *) + &(((struct msdu_ext_desc_t *)desc)->frags[0]); + fdesc[num_frags].u.desc64 = 0; + } else { + /* This piece of code should never be executed on HELIUMPLUS */ + *((u_int32_t *) + (((char *) desc) + HTT_TX_DESC_LEN + num_frags * 8)) = 0; + } +#else /* ! HELIUMPLUS */ + *((uint32_t *) + (((char *)desc) + HTT_TX_DESC_LEN + num_frags * 8)) = 0; +#endif /* HELIUMPLUS */ +} + +/* checksum offload flags for hw */ +#define IPV4_CSUM_EN 0x00010000 +#define UDP_IPV4_CSUM_EN 0x00020000 +#define UDP_IPV6_CSUM_EN 0x00040000 +#define TCP_IPV4_CSUM_EN 0x00080000 +#define TCP_IPV6_CSUM_EN 0x00100000 +#define PARTIAL_CSUM_EN 0x00200000 + +/** + * @brief Specify the location and size of a fragment of a tx MSDU. + * @details + * In LL systems, the tx MAC DMA needs to know how the MSDU is constructed + * from fragments. + * In LL and HL systems, the HIF's download DMA to the target (LL: tx desc + * + header of tx payload; HL: tx desc + entire tx payload) needs to know + * where to find the fragments to download. + * The tx data SW uses this function to specify the location and size of + * each of the MSDU's fragments. + * + * @param pdev - the handle of the physical device sending the tx data + * @param desc - abstract handle to the HTT tx descriptor + * @param frag_num - which fragment is being specified (zero-based indexing) + * @param frag_phys_addr - DMA/physical address of the fragment + * @param frag_len - number of bytes within the fragment + */ +static inline +void +htt_tx_desc_frag(htt_pdev_handle pdev, + void *desc, + int frag_num, qdf_dma_addr_t frag_phys_addr, uint16_t frag_len) +{ + uint32_t *word32; +#if defined(HELIUMPLUS) + uint64_t *word64; + + if (HTT_WIFI_IP(pdev, 2, 0)) { + word32 = (u_int32_t *)(desc); + /* Initialize top 6 words of TSO flags per packet */ + *word32++ = 0; + *word32++ = 0; + *word32++ = 0; + if (((struct txrx_pdev_cfg_t *)(pdev->ctrl_pdev)) + ->ip_tcp_udp_checksum_offload) + *word32 |= (IPV4_CSUM_EN | TCP_IPV4_CSUM_EN | + TCP_IPV6_CSUM_EN | UDP_IPV4_CSUM_EN | + UDP_IPV6_CSUM_EN); + else + *word32 = 0; + word32++; + *word32++ = 0; + *word32++ = 0; + + qdf_assert_always(word32 == (uint32_t *) + &(((struct msdu_ext_desc_t *)desc)->frags[0])); + + /* Each fragment consumes 2 DWORDS */ + word32 += (frag_num << 1); + word64 = (uint64_t *)word32; + *word64 = frag_phys_addr; + /* + * The frag_phys address is 37 bits. So, the higher 16 bits will + * be for len + */ + word32++; + *word32 &= 0x0000ffff; + *word32 |= (frag_len << 16); + } else { + /* For Helium+, this block cannot exist */ + QDF_ASSERT(0); + } +#else /* !defined(HELIUMPLUS) */ + { + uint64_t u64 = (uint64_t)frag_phys_addr; + uint32_t u32l = (u64 & 0xffffffff); + uint32_t u32h = (uint32_t)((u64 >> 32) & 0x1f); + uint64_t *word64; + + word32 = (uint32_t *) (((char *)desc) + + HTT_TX_DESC_LEN + frag_num * 8); + word64 = (uint64_t *)word32; + *word32 = u32l; + word32++; + *word32 = (u32h << 16) | frag_len; + } +#endif /* defined(HELIUMPLUS) */ +} + +void htt_tx_desc_frags_table_set(htt_pdev_handle pdev, + void *desc, + qdf_dma_addr_t paddr, + qdf_dma_addr_t frag_desc_paddr, + int reset); + +/** + * @brief Specify the type and subtype of a tx frame. + * + * @param pdev - the handle of the physical device sending the tx data + * @param type - format of the MSDU (802.3, native WiFi, raw, or mgmt) + * @param sub_type - sub_type (relevant for raw frames) + */ +static inline +void +htt_tx_desc_type(htt_pdev_handle pdev, + void *htt_tx_desc, enum htt_pkt_type type, uint8_t sub_type) +{ + uint32_t *word0; + + word0 = (uint32_t *) htt_tx_desc; + /* clear old values */ + *word0 &= ~(HTT_TX_DESC_PKT_TYPE_M | HTT_TX_DESC_PKT_SUBTYPE_M); + /* write new values */ + HTT_TX_DESC_PKT_TYPE_SET(*word0, type); + HTT_TX_DESC_PKT_SUBTYPE_SET(*word0, sub_type); +} + +/***** TX MGMT DESC management APIs ****/ + +/* Number of mgmt descriptors in the pool */ +#define HTT_MAX_NUM_MGMT_DESCS 32 + +/** htt_tx_mgmt_desc_pool_alloc + * @description - allocates the memory for mgmt frame descriptors + * @param - htt pdev object + * @param - num of descriptors to be allocated in the pool + */ +void htt_tx_mgmt_desc_pool_alloc(struct htt_pdev_t *pdev, A_UINT32 num_elems); + +/** htt_tx_mgmt_desc_alloc + * @description - reserves a mgmt descriptor from the pool + * @param - htt pdev object + * @param - pointer to variable to hold the allocated desc id + * @param - pointer to the mamangement from UMAC + * @return - pointer the allocated mgmt descriptor + */ +qdf_nbuf_t +htt_tx_mgmt_desc_alloc(struct htt_pdev_t *pdev, A_UINT32 *desc_id, + qdf_nbuf_t mgmt_frm); + +/** htt_tx_mgmt_desc_free + * @description - releases the management descriptor back to the pool + * @param - htt pdev object + * @param - descriptor ID + */ +void +htt_tx_mgmt_desc_free(struct htt_pdev_t *pdev, A_UINT8 desc_id, + A_UINT32 status); + +/** htt_tx_mgmt_desc_pool_free + * @description - releases all the resources allocated for mgmt desc pool + * @param - htt pdev object + */ +void htt_tx_mgmt_desc_pool_free(struct htt_pdev_t *pdev); + +/** + * @brief Provide a buffer to store a 802.11 header added by SW tx encap + * + * @param htt_tx_desc - which frame the 802.11 header is being added to + * @param new_l2_hdr_size - how large the buffer needs to be + */ +#define htt_tx_desc_mpdu_header(htt_tx_desc, new_l2_hdr_size) /*NULL*/ +/** + * @brief How many tx credits would be consumed by the specified tx frame. + * + * @param msdu - the tx frame in question + * @return number of credits used for this tx frame + */ +#define htt_tx_msdu_credit(msdu) 1 /* 1 credit per buffer */ +#ifdef HTT_DBG +void htt_tx_desc_display(void *tx_desc); +#else +#define htt_tx_desc_display(tx_desc) +#endif + +static inline void htt_tx_desc_set_peer_id(void *htt_tx_desc, uint16_t peer_id) +{ + uint16_t *peer_id_field_ptr; + + peer_id_field_ptr = (uint16_t *) + (htt_tx_desc + + HTT_TX_DESC_PEERID_DESC_PADDR_OFFSET_BYTES); + + *peer_id_field_ptr = peer_id; +} + +static inline +void htt_tx_desc_set_chanfreq(void *htt_tx_desc, uint16_t chanfreq) +{ + uint16_t *chanfreq_field_ptr; + + /* + * The reason we dont use CHAN_FREQ_OFFSET_BYTES is because + * it uses DWORD as unit + * + * The reason we dont use the SET macro in htt.h is because + * htt_tx_desc is incomplete type + */ + chanfreq_field_ptr = (uint16_t *) + (htt_tx_desc + + HTT_TX_DESC_PEERID_DESC_PADDR_OFFSET_BYTES + + sizeof(A_UINT16)); + + *chanfreq_field_ptr = chanfreq; +} + +#if defined(FEATURE_TSO) && defined(HELIUMPLUS) +void +htt_tx_desc_fill_tso_info(htt_pdev_handle pdev, void *desc, + struct qdf_tso_info_t *tso_info); +#else +#define htt_tx_desc_fill_tso_info(pdev, desc, tso_info) +#endif +#endif /* _OL_HTT_TX_API__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_api.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_api.h new file mode 100644 index 0000000000000000000000000000000000000000..1d88259272c4215d3172a120aa12b9eff9e05aab --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_api.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2011-2014,2016-2017,2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_api.h + * @brief Definitions used in multiple external interfaces to the txrx SW. + */ +#ifndef _OL_TXRX_API__H_ +#define _OL_TXRX_API__H_ + +/** + * @brief ADDBA negotiation status, used both during requests and confirmations + */ +enum ol_addba_status { + /* status: negotiation started or completed successfully */ + ol_addba_success, + + /* reject: aggregation is not applicable - don't try again */ + ol_addba_reject, + + /* busy: ADDBA negotiation couldn't be performed - try again later */ + ol_addba_busy, +}; + +enum ol_sec_type { + ol_sec_type_none, + ol_sec_type_wep128, + ol_sec_type_wep104, + ol_sec_type_wep40, + ol_sec_type_tkip, + ol_sec_type_tkip_nomic, + ol_sec_type_aes_ccmp, + ol_sec_type_wapi, + + /* keep this last! */ + ol_sec_type_types +}; + +#ifdef WLAN_FEATURE_TSF_PLUS +typedef int (*tp_ol_timestamp_cb)(qdf_nbuf_t netbuf, uint64_t target_time); + +/** + * ol_register_timestamp_callback() - set callbacks for timestamp tx msdu. + * @ol_tx_timestamp_cb: callback function for time stamp tx msdu + * + * This function register timestamp callback, the callback will + * be called when tx a msdu + * + * Return: nothing + */ +void ol_register_timestamp_callback(tp_ol_timestamp_cb ol_tx_timestamp_cb); + +/** + * ol_deregister_timestamp_callback() - reset callbacks for timestamp + * tx msdu to NULL. + * + * This function reset the timestamp callbacks for tx + * + * Return: nothing + */ +void ol_deregister_timestamp_callback(void); +#endif +#endif /* _OL_TXRX_API__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_ctrl_api.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_ctrl_api.h new file mode 100644 index 0000000000000000000000000000000000000000..e829076b6f4bbb7be01267224815b81265d31bb7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_ctrl_api.h @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_ctrl_api.h + * @brief Define the host data API functions called by the host control SW. + */ +#ifndef _OL_TXRX_CTRL_API__H_ +#define _OL_TXRX_CTRL_API__H_ + +#include /* A_STATUS */ +#include /* qdf_nbuf_t */ +#include /* qdf_device_t */ +#include /* HTC_HANDLE */ + +#include /* ol_sec_type */ +#include /* MAX_SPATIAL_STREAM */ +#include /* ol_pdev_handle, ol_vdev_handle, etc */ +#include +#include +#include +#define OL_ATH_TX_DRAIN_WAIT_DELAY 50 + +/** + * @brief Set up the data SW subsystem. + * @details + * As part of the WLAN device attach, the data SW subsystem has + * to be attached as a component within the WLAN device. + * This attach allocates and initializes the physical device object + * used by the data SW. + * The data SW subsystem attach needs to happen after the target has + * be started, and host / target parameter negotiation has completed, + * since the host data SW uses some of these host/target negotiated + * parameters (e.g. peer ID range) during the initializations within + * its attach function. + * However, the host data SW is not allowed to send HTC messages to the + * target within this pdev_attach function call, since the HTC setup + * has not complete at this stage of initializations. Any messaging + * to the target has to be done in the separate pdev_attach_target call + * that is invoked after HTC setup is complete. + * + * @param soc - datapath soc handle + * @param pdev_id - physical device instance id + * @return 0 for success or error code + */ +int +ol_txrx_pdev_post_attach(struct cdp_soc_t *soc, uint8_t pdev_id); + +/** + * @brief Parameter type to be input to ol_txrx_peer_update + * @details + * This struct is union,to be used to specify various information to update + * txrx peer object. + */ +union ol_txrx_peer_update_param_t { + uint8_t qos_capable; + uint8_t uapsd_mask; + enum ol_sec_type sec_type; +}; + +/** + * @brief Parameter type to be input to ol_txrx_peer_update + * @details + * This enum is used to specify what exact information in + * ol_txrx_peer_update_param_t + * is used to update the txrx peer object. + */ +enum ol_txrx_peer_update_select_t { + ol_txrx_peer_update_qos_capable = 1, + ol_txrx_peer_update_uapsdMask, + ol_txrx_peer_update_peer_security, +}; + +/** + * @brief Update the data peer object as some informaiton changed in node. + * @details + * Only a single prarameter can be changed for each call to this func. + * + * @param peer - pointer to the node's object + * @param param - new param to be upated in peer object. + * @param select - specify what's parameter needed to be update + */ +void +ol_txrx_peer_update(ol_txrx_vdev_handle data_vdev, uint8_t *peer_mac, + union ol_txrx_peer_update_param_t *param, + enum ol_txrx_peer_update_select_t select); + +#if defined(CONFIG_HL_SUPPORT) +/** + * @brief notify tx data SW that a peer-TID is ready to transmit to. + * @details + * This function applies only to HL systems - in LL systems, tx flow control + * is handled entirely within the target FW. + * If a peer-TID has tx paused, then the tx datapath will end up queuing + * any tx frames that arrive from the OS shim for that peer-TID. + * In a HL system, the host tx data SW itself will classify the tx frame, + * and determine that it needs to be queued rather than downloaded to the + * target for transmission. + * Once the peer-TID is ready to accept data, the host control SW will call + * this function to notify the host data SW that the queued frames can be + * enabled for transmission, or specifically to download the tx frames + * to the target to transmit. + * The TID parameter is an extended version of the QoS TID. Values 0-15 + * indicate a regular QoS TID, and the value 16 indicates either non-QoS + * data, multicast data, or broadcast data. + * + * @param data_peer - which peer is being unpaused + * @param tid - which TID within the peer is being unpaused, or -1 as a + * wildcard to unpause all TIDs within the peer + */ +void +ol_txrx_peer_tid_unpause(ol_txrx_peer_handle data_peer, int tid); + + +/** + * @brief Tell a paused peer to release a specified number of tx frames. + * @details + * This function applies only to HL systems - in LL systems, tx flow control + * is handled entirely within the target FW. + * Download up to a specified maximum number of tx frames from the tx + * queues of the specified TIDs within the specified paused peer, usually + * in response to a U-APSD trigger from the peer. + * It is up to the host data SW to determine how to choose frames from the + * tx queues of the specified TIDs. However, the host data SW does need to + * provide long-term fairness across the U-APSD enabled TIDs. + * The host data SW will notify the target data FW when it is done downloading + * the batch of U-APSD triggered tx frames, so the target data FW can + * differentiate between an in-progress download versus a case when there are + * fewer tx frames available than the specified limit. + * This function is relevant primarily to HL U-APSD, where the frames are + * held in the host. + * + * @param peer - which peer sent the U-APSD trigger + * @param tid_mask - bitmask of U-APSD enabled TIDs from whose tx queues + * tx frames can be released + * @param max_frms - limit on the number of tx frames to release from the + * specified TID's queues within the specified peer + */ +void ol_txrx_tx_release(ol_txrx_peer_handle peer, + u_int32_t tid_mask, + int max_frms); + +#else +static inline void +ol_txrx_peer_tid_unpause(ol_txrx_peer_handle data_peer, int tid) +{ +} + +static inline void +ol_txrx_tx_release(ol_txrx_peer_handle peer, + u_int32_t tid_mask, + int max_frms) +{ +} + +#endif /* CONFIG_HL_SUPPORT */ + +#ifdef QCA_SUPPORT_TX_THROTTLE +/** + * @brief Suspend all tx data per thermal event/timer for the + * specified physical device + * @details + * This function applies only to HL systerms, and it makes pause and + * unpause operations happen in pairs. + */ +void +ol_txrx_throttle_pause(ol_txrx_pdev_handle data_pdev); + + +/** + * @brief Resume all tx data per thermal event/timer for the + * specified physical device + * @details + * This function applies only to HL systerms, and it makes pause and + * unpause operations happen in pairs. + */ +void +ol_txrx_throttle_unpause(ol_txrx_pdev_handle data_pdev); +#else + +static inline void +ol_txrx_throttle_pause(ol_txrx_pdev_handle data_pdev) +{ +} + +static inline void +ol_txrx_throttle_unpause(ol_txrx_pdev_handle data_pdev) +{ +} +#endif + +/** + * @brief notify tx data SW that a peer's transmissions are suspended. + * @details + * This function applies only to HL systems - in LL systems, tx flow control + * is handled entirely within the target FW. + * The HL host tx data SW is doing tx classification and tx download + * scheduling, and therefore also needs to actively participate in tx + * flow control. Specifically, the HL tx data SW needs to check whether a + * given peer is available to transmit to, or is paused. + * This function is used to tell the HL tx data SW when a peer is paused, + * so the host tx data SW can hold the tx frames for that SW. + * + * @param data_peer - which peer is being paused + */ +static inline void ol_txrx_peer_pause(struct ol_txrx_peer_t *data_peer) +{ +} + +/** + * @brief Suspend all tx data for the specified physical device. + * @details + * This function applies only to HL systems - in LL systems, tx flow control + * is handled entirely within the target FW. + * In some systems it is necessary to be able to temporarily + * suspend all WLAN traffic, e.g. to allow another device such as bluetooth + * to temporarily have exclusive access to shared RF chain resources. + * This function suspends tx traffic within the specified physical device. + * + * @param data_pdev - the physical device being paused + */ +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(CONFIG_HL_SUPPORT) + +void ol_txrx_pdev_pause(struct ol_txrx_pdev_t *data_pdev, uint32_t reason); +#else +static inline +void ol_txrx_pdev_pause(struct ol_txrx_pdev_t *data_pdev, uint32_t reason) +{ +} +#endif + +/** + * @brief Resume tx for the specified physical device. + * @details + * This function applies only to HL systems - in LL systems, tx flow control + * is handled entirely within the target FW. + * + * @param data_pdev - the physical device being unpaused + */ +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(CONFIG_HL_SUPPORT) + +void ol_txrx_pdev_unpause(struct ol_txrx_pdev_t *pdev, uint32_t reason); +#else +static inline +void ol_txrx_pdev_unpause(struct ol_txrx_pdev_t *pdev, uint32_t reason) +{ +} +#endif + +/** + * @brief Synchronize the data-path tx with a control-path target download + * @dtails + * @param data_pdev - the data-path physical device object + * @param sync_cnt - after the host data-path SW downloads this sync request + * to the target data-path FW, the target tx data-path will hold itself + * in suspension until it is given an out-of-band sync counter value that + * is equal to or greater than this counter value + */ +void ol_txrx_tx_sync(ol_txrx_pdev_handle data_pdev, uint8_t sync_cnt); + +/** + * @brief Store a delivery notification callback for specific data frames. + * @details + * Through a non-std tx function, the txrx SW can be given tx data frames + * that are specially marked to not be unmapped and freed by the tx SW + * when transmission completes. Rather, these specially-marked frames + * are provided to the callback registered with this function. + * + * @param soc - datapath soc handle + * @param vdev_id - id of which vdev the callback is being registered with + * (Currently the callback is stored in the pdev rather than the vdev.) + * @param callback - the function to call when tx frames marked as "no free" + * are done being transmitted + * @param ctxt - the context argument provided to the callback function + */ +void +ol_txrx_data_tx_cb_set(struct cdp_soc_t *soc, uint8_t vdev_id, + ol_txrx_data_tx_cb callback, void *ctxt); + +/** + * @brief Discard all tx frames that are pending in txrx. + * @details + * Mainly used in clean up path to make sure all pending tx packets + * held by txrx are returned back to OS shim immediately. + * + * @param pdev - the data physical device object + * @return - void + */ +void ol_txrx_discard_tx_pending(ol_txrx_pdev_handle pdev); + +void +ol_txrx_peer_keyinstalled_state_update(ol_txrx_peer_handle data_peer, + uint8_t val); + +#define ol_tx_addba_conf(data_peer, tid, status) /* no-op */ + +/** + * @brief Find a txrx peer handle from the peer's MAC address + * @details + * The control SW typically uses the txrx peer handle to refer to the peer. + * In unusual circumstances, if it is infeasible for the control SW maintain + * the txrx peer handle but it can maintain the peer's MAC address, + * this function allows the peer handled to be retrieved, based on the peer's + * MAC address. + * In cases where there are multiple peer objects with the same MAC address, + * it is undefined which such object is returned. + * This function does not increment the peer's reference count. Thus, it is + * only suitable for use as long as the control SW has assurance that it has + * not deleted the peer object, by calling ol_txrx_peer_detach. + * + * @param pdev - the data physical device object + * @param peer_mac_addr - MAC address of the peer in question + * @return handle to the txrx peer object + */ +ol_txrx_peer_handle +ol_txrx_peer_find_by_addr(ol_txrx_pdev_handle pdev, uint8_t *peer_mac_addr); + +struct ol_txrx_peer_stats_t { + struct { + struct { + uint32_t ucast; + uint32_t mcast; + uint32_t bcast; + } frms; + struct { + uint32_t ucast; + uint32_t mcast; + uint32_t bcast; + } bytes; + } tx; + struct { + struct { + uint32_t ucast; + uint32_t mcast; + uint32_t bcast; + } frms; + struct { + uint32_t ucast; + uint32_t mcast; + uint32_t bcast; + } bytes; + } rx; +}; + +/** + * @brief Provide a snapshot of the txrx counters for the specified peer + * @details + * The txrx layer optionally maintains per-peer stats counters. + * This function provides the caller with a consistent snapshot of the + * txrx stats counters for the specified peer. + * + * @param pdev - the data physical device object + * @param peer - which peer's stats counters are requested + * @param stats - buffer for holding the stats counters snapshot + * @return success / failure status + */ +#ifdef QCA_ENABLE_OL_TXRX_PEER_STATS +A_STATUS +ol_txrx_peer_stats_copy(ol_txrx_pdev_handle pdev, + ol_txrx_peer_handle peer, ol_txrx_peer_stats_t *stats); +#else +#define ol_txrx_peer_stats_copy(pdev, peer, stats) A_ERROR /* failure */ +#endif /* QCA_ENABLE_OL_TXRX_PEER_STATS */ + + +#define OL_TXRX_RSSI_INVALID 0xffff +/** + * @brief Provide the current RSSI average from data frames sent by a peer. + * @details + * If a peer has sent data frames, the data SW will optionally keep + * a running average of the RSSI observed for those data frames. + * This function returns that time-average RSSI if is it available, + * or OL_TXRX_RSSI_INVALID if either RSSI tracking is disabled or if + * no data frame indications with valid RSSI meta-data have been received. + * The RSSI is in approximate dBm units, and is normalized with respect + * to a 20 MHz channel. For example, if a data frame is received on a + * 40 MHz channel, wherein both the primary 20 MHz channel and the + * secondary 20 MHz channel have an RSSI of -77 dBm, the reported RSSI + * will be -77 dBm, rather than the actual -74 dBm RSSI from the + * combination of the primary + extension 20 MHz channels. + * Alternatively, the RSSI may be evaluated only on the primary 20 MHz + * channel. + * + * @param peer - which peer's RSSI is desired + * @return RSSI evaluted from frames sent by the specified peer + */ +#ifdef QCA_SUPPORT_PEER_DATA_RX_RSSI +int16_t ol_txrx_peer_rssi(ol_txrx_peer_handle peer); +#else +#define ol_txrx_peer_rssi(peer) OL_TXRX_RSSI_INVALID +#endif /* QCA_SUPPORT_PEER_DATA_RX_RSSI */ + +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) + +/** + * ol_txrx_bad_peer_txctl_set_setting() - Configure the bad peer tx + * limit setting. + * @soc_hdl: soc handle + * @pdev_id: datapath pdev identifier + * @enable: enable/disable setting + * @period: balance period in ms + * @txq_limit: balance txq limit + * + * @param pdev - the physics device + */ +void +ol_txrx_bad_peer_txctl_set_setting( + struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + int enable, + int period, + int txq_limit); + +/** + * ol_txrx_bad_peer_txctl_update_threshold() - Configure the bad peer tx + * threshold limit + * @soc_hdl: soc handle + * @pdev_id: datapath pdev identifier + * @level: txctl level + * @tput_thresh throughput threshold + * @tx_limit: balance tx limit + * + * @param pdev - the physics device + */ +void +ol_txrx_bad_peer_txctl_update_threshold( + struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + int level, + int tput_thresh, + int tx_limit); + +#else + +static inline void +ol_txrx_bad_peer_txctl_set_setting( + struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + int enable, + int period, + int txq_limit) +{ +} + +static inline void +ol_txrx_bad_peer_txctl_update_threshold( + struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + int level, + int tput_thresh, + int tx_limit) +{ +} +#endif /* defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) */ + + +void ol_txrx_set_ocb_peer(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer); + +bool ol_txrx_get_ocb_peer(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t **peer); + +void ol_tx_set_is_mgmt_over_wmi_enabled(uint8_t value); +uint8_t ol_tx_get_is_mgmt_over_wmi_enabled(void); + +#ifdef QCA_LL_TX_FLOW_CONTROL_RESIZE +void ol_tx_flow_pool_resize_handler(uint8_t flow_pool_id, + uint16_t flow_pool_size); +#else +static inline void ol_tx_flow_pool_resize_handler(uint8_t flow_pool_id, + uint16_t flow_pool_size) +{ +} +#endif + +/* TX FLOW Control related functions */ +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 +#define TX_FLOW_MGMT_POOL_ID 0xEF + +#ifdef QCA_LL_TX_FLOW_GLOBAL_MGMT_POOL +#define TX_FLOW_MGMT_POOL_SIZE 32 +#else +#define TX_FLOW_MGMT_POOL_SIZE 0 +#endif + +void ol_tx_register_flow_control(struct ol_txrx_pdev_t *pdev); +void ol_tx_deregister_flow_control(struct ol_txrx_pdev_t *pdev); +void ol_tx_dump_flow_pool_info(struct cdp_soc_t *soc_hdl); +void ol_tx_dump_flow_pool_info_compact(struct ol_txrx_pdev_t *pdev); +void ol_tx_clear_flow_pool_stats(void); +void ol_tx_flow_pool_map_handler(uint8_t flow_id, uint8_t flow_type, + uint8_t flow_pool_id, uint16_t flow_pool_size); +void ol_tx_flow_pool_unmap_handler(uint8_t flow_id, uint8_t flow_type, + uint8_t flow_pool_id); +struct ol_tx_flow_pool_t *ol_tx_create_flow_pool(uint8_t flow_pool_id, + uint16_t flow_pool_size); + +/** + * ol_tx_inc_pool_ref() - increment pool ref count + * @pool: flow pool pointer + * + * Increments pool's ref count, used to make sure that no one is using + * pool when it is being deleted. + * As this function is taking pool->flow_pool_lock inside it, it should + * always be called outside this spinlock. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ol_tx_inc_pool_ref(struct ol_tx_flow_pool_t *pool); + +/** + * ol_tx_dec_pool_ref() - decrement pool ref count + * @pool: flow pool pointer + * @force: free pool forcefully + * + * Decrements pool's ref count and deletes the pool if ref count gets 0. + * As this function is taking pdev->tx_desc.flow_pool_list_lock and + * pool->flow_pool_lock inside it, it should always be called outside + * these two spinlocks. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ol_tx_dec_pool_ref(struct ol_tx_flow_pool_t *pool, bool force); + +#else + +static inline void ol_tx_register_flow_control(struct ol_txrx_pdev_t *pdev) +{ +} +static inline void ol_tx_deregister_flow_control(struct ol_txrx_pdev_t *pdev) +{ +} + +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_HL_NETDEV_FLOW_CONTROL) +void ol_tx_dump_flow_pool_info(struct cdp_soc_t *soc_hdl); +void ol_tx_dump_flow_pool_info_compact(struct ol_txrx_pdev_t *pdev); +#else +static inline void ol_tx_dump_flow_pool_info(struct cdp_soc_t *soc_hdl) +{ +} + +static inline +void ol_tx_dump_flow_pool_info_compact(struct ol_txrx_pdev_t *pdev) +{ +} +#endif + +static inline void ol_tx_clear_flow_pool_stats(void) +{ +} +static inline void ol_tx_flow_pool_map_handler(uint8_t flow_id, + uint8_t flow_type, uint8_t flow_pool_id, uint16_t flow_pool_size) +{ +} +static inline void ol_tx_flow_pool_unmap_handler(uint8_t flow_id, + uint8_t flow_type, uint8_t flow_pool_id) +{ +} +static inline struct ol_tx_flow_pool_t *ol_tx_create_flow_pool( + uint8_t flow_pool_id, uint16_t flow_pool_size) +{ + return NULL; +} +static inline QDF_STATUS +ol_tx_inc_pool_ref(struct ol_tx_flow_pool_t *pool) +{ + return QDF_STATUS_SUCCESS; +} +static inline QDF_STATUS +ol_tx_dec_pool_ref(struct ol_tx_flow_pool_t *pool, bool force) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +#endif /* _OL_TXRX_CTRL_API__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_dbg.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_dbg.h new file mode 100644 index 0000000000000000000000000000000000000000..438b4060715e127871abd2eaeff44460cca9ab9a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_dbg.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2011, 2014-2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_dbg.h + * @brief Functions provided for visibility and debugging. + */ +#ifndef _OL_TXRX_DBG__H_ +#define _OL_TXRX_DBG__H_ + +#include /* A_STATUS, uint64_t */ +#include /* qdf_semaphore_t */ +#include /* htt_dbg_stats_type */ +#include /* ol_txrx_stats */ + +#ifndef TXRX_DEBUG_LEVEL +#define TXRX_DEBUG_LEVEL 0 /* no debug info */ +#endif + +enum { + TXRX_DBG_MASK_OBJS = 0x01, + TXRX_DBG_MASK_STATS = 0x02, + TXRX_DBG_MASK_PROT_ANALYZE = 0x04, + TXRX_DBG_MASK_RX_REORDER_TRACE = 0x08, + TXRX_DBG_MASK_RX_PN_TRACE = 0x10 +}; + +/*--- txrx printouts ---*/ + +/* + * Uncomment this to enable txrx printouts with dynamically adjustable + * verbosity. These printouts should not impact performance. + */ +#define TXRX_PRINT_ENABLE 1 +/* uncomment this for verbose txrx printouts (may impact performance) */ +/* #define TXRX_PRINT_VERBOSE_ENABLE 1 */ + +/*--- txrx object (pdev, vdev, peer) display debug functions ---*/ + +#if TXRX_DEBUG_LEVEL > 5 +void ol_txrx_pdev_display(ol_txrx_pdev_handle pdev, int indent); +void ol_txrx_vdev_display(ol_txrx_vdev_handle vdev, int indent); +void ol_txrx_peer_display(ol_txrx_peer_handle peer, int indent); +#else +#define ol_txrx_pdev_display(pdev, indent) +#define ol_txrx_vdev_display(vdev, indent) +#define ol_txrx_peer_display(peer, indent) +#endif + +/*--- txrx stats display debug functions ---*/ + +/** + * ol_txrx_stats_display() - display tx rx stats + * @pdev: pdev handle + * @level: verbosity level for logs + * + * Return: none + */ +void ol_txrx_stats_display(ol_txrx_pdev_handle pdev, + enum qdf_stats_verbosity_level level); + +void ol_txrx_stats_clear(ol_txrx_pdev_handle pdev); + + +/*--- txrx protocol analyzer debug feature ---*/ + +/* uncomment this to enable the protocol analzyer feature */ +/* #define ENABLE_TXRX_PROT_ANALYZE 1 */ + +#if defined(ENABLE_TXRX_PROT_ANALYZE) + +void ol_txrx_prot_ans_display(ol_txrx_pdev_handle pdev); + +#else + +#define ol_txrx_prot_ans_display(pdev) + +#endif /* ENABLE_TXRX_PROT_ANALYZE */ + +/*--- txrx sequence number trace debug feature ---*/ + +/* uncomment this to enable the rx reorder trace feature */ +/* #define ENABLE_RX_REORDER_TRACE 1 */ + +#define ol_txrx_seq_num_trace_display(pdev) \ + ol_rx_reorder_trace_display(pdev, 0, 0) + +#if defined(ENABLE_RX_REORDER_TRACE) + +void +ol_rx_reorder_trace_display(ol_txrx_pdev_handle pdev, int just_once, int limit); + +#else + +#define ol_rx_reorder_trace_display(pdev, just_once, limit) + +#endif /* ENABLE_RX_REORDER_TRACE */ + +/*--- txrx packet number trace debug feature ---*/ + +/* uncomment this to enable the rx PN trace feature */ +/* #define ENABLE_RX_PN_TRACE 1 */ + +#define ol_txrx_pn_trace_display(pdev) ol_rx_pn_trace_display(pdev, 0) + +#if defined(ENABLE_RX_PN_TRACE) + +void ol_rx_pn_trace_display(ol_txrx_pdev_handle pdev, int just_once); + +#else + +#define ol_rx_pn_trace_display(pdev, just_once) + +#endif /* ENABLE_RX_PN_TRACE */ + +/*--- tx queue log debug feature ---*/ +/* uncomment this to enable the tx queue log feature */ +/* #define ENABLE_TX_QUEUE_LOG 1 */ + +#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT) + +void +ol_tx_queue_log_display(ol_txrx_pdev_handle pdev); +void ol_tx_queue_log_clear(ol_txrx_pdev_handle pdev); +#else + +static inline +void ol_tx_queue_log_display(ol_txrx_pdev_handle pdev) +{ +} + +static inline +void ol_tx_queue_log_clear(ol_txrx_pdev_handle pdev) +{ +} +#endif /* defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT) */ + + +/*----------------------------------------*/ + +#endif /* _OL_TXRX_DBG__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_htt_api.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_htt_api.h new file mode 100644 index 0000000000000000000000000000000000000000..c37651fee28cab54827998e6d720de429ee179b2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_htt_api.h @@ -0,0 +1,788 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_htt_api.h + * @brief Define the host data API functions called by the host HTT SW. + */ +#ifndef _OL_TXRX_HTT_API__H_ +#define _OL_TXRX_HTT_API__H_ + +#include /* HTT_TX_COMPL_IND_STAT */ +#include /* A_STATUS */ +#include /* qdf_nbuf_t */ + +#include /* ol_txrx_pdev_handle */ +#include + +#ifdef CONFIG_HL_SUPPORT +static inline uint16_t *ol_tx_msdu_id_storage(qdf_nbuf_t msdu) +{ + return (uint16_t *) (&QDF_NBUF_CB_TX_DESC_ID(msdu)); + +} + +/** + * @brief Deduct one credit from target_tx and one from any of the groups + * @details + * Deduct one credit from target_tx credit and one credit from any of the + * groups, whichever has more number of credits. + * + * @param pdev - the data physical device + */ +int ol_tx_deduct_one_credit(struct ol_txrx_pdev_t *pdev); +#else +static inline uint16_t *ol_tx_msdu_id_storage(qdf_nbuf_t msdu) +{ + qdf_assert(qdf_nbuf_headroom(msdu) >= (sizeof(uint16_t) * 2 - 1)); + return (uint16_t *) (((qdf_size_t) (qdf_nbuf_head(msdu) + 1)) & ~0x1); +} + +static inline int ol_tx_deduct_one_credit(struct ol_txrx_pdev_t *pdev) +{ + return 0; +} +#endif +/** + * @brief Tx MSDU download completion for a LL system + * @details + * Release the reference to the downloaded tx descriptor. + * In the unlikely event that the reference count is zero, free + * the tx descriptor and tx frame. + * + * @param pdev - (abstract) pointer to the txrx physical device + * @param status - indication of whether the download succeeded + * @param msdu - the downloaded tx frame + * @param msdu_id - the txrx ID of the tx frame - this is used for + * locating the frame's tx descriptor + */ +void +ol_tx_download_done_ll(void *pdev, + A_STATUS status, qdf_nbuf_t msdu, uint16_t msdu_id); + +/** + * @brief Tx MSDU download completion for HL system without tx completion msgs + * @details + * Free the tx descriptor and tx frame. + * Invoke the HL tx download scheduler. + * + * @param pdev - (abstract) pointer to the txrx physical device + * @param status - indication of whether the download succeeded + * @param msdu - the downloaded tx frame + * @param msdu_id - the txrx ID of the tx frame - this is used for + * locating the frame's tx descriptor + */ +void +ol_tx_download_done_hl_free(void *pdev, + A_STATUS status, qdf_nbuf_t msdu, uint16_t msdu_id); + +/** + * @brief Tx MSDU download completion for HL system with tx completion msgs + * @details + * Release the reference to the downloaded tx descriptor. + * In the unlikely event that the reference count is zero, free + * the tx descriptor and tx frame. + * Optionally, invoke the HL tx download scheduler. (It is probable that + * the HL tx download scheduler would operate in response to tx completion + * messages rather than download completion events.) + * + * @param pdev - (abstract) pointer to the txrx physical device + * @param status - indication of whether the download succeeded + * @param msdu - the downloaded tx frame + * @param msdu_id - the txrx ID of the tx frame - this is used for + * locating the frame's tx descriptor + */ +void +ol_tx_download_done_hl_retain(void *pdev, + A_STATUS status, + qdf_nbuf_t msdu, uint16_t msdu_id); + +/* + * For now, make the host HTT -> host txrx tx completion status + * match the target HTT -> host HTT tx completion status, so no + * translation is needed. + */ +/* + * host-only statuses use a different part of the number space + * than host-target statuses + */ +#define HTT_HOST_ONLY_STATUS_CODE_START 128 +enum htt_tx_status { + /* ok - successfully sent + acked */ + htt_tx_status_ok = HTT_TX_COMPL_IND_STAT_OK, + + /* discard - not sent (congestion control) */ + htt_tx_status_discard = HTT_TX_COMPL_IND_STAT_DISCARD, + + /* no_ack - sent, but no ack */ + htt_tx_status_no_ack = HTT_TX_COMPL_IND_STAT_NO_ACK, + + /* drop may due to tx descriptor not enough*/ + htt_tx_status_drop = HTT_TX_COMPL_IND_STAT_DROP, + + /* download_fail - host could not deliver the tx frame to target */ + htt_tx_status_download_fail = HTT_HOST_ONLY_STATUS_CODE_START, +}; + +/** + * @brief Process a tx completion message sent by the target. + * @details + * When the target is done transmitting a tx frame (either because + * the frame was sent + acknowledged, or because the target gave up) + * it sends a tx completion message to the host. + * This notification function is used regardless of whether the + * transmission succeeded or not; the status argument indicates whether + * the transmission succeeded. + * This tx completion message indicates via the descriptor ID which + * tx frames were completed, and indicates via the status whether the + * frames were transmitted successfully. + * The host frees the completed descriptors / frames (updating stats + * in the process). + * + * @param pdev - the data physical device that sent the tx frames + * (registered with HTT as a context pointer during attach time) + * @param num_msdus - how many MSDUs are referenced by the tx completion + * message + * @param status - whether transmission was successful + * @param msg_word - the tx completion message + */ +void +ol_tx_completion_handler(ol_txrx_pdev_handle pdev, + int num_msdus, + enum htt_tx_status status, void *msg_word); + +void ol_tx_credit_completion_handler(ol_txrx_pdev_handle pdev, int credits); + +struct rate_report_t { + u_int16_t id; + u_int16_t phy:4; + u_int32_t rate; +}; + +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) + +/** + * @brief Process a link status report for all peers. + * @details + * The ol_txrx_peer_link_status_handler function performs basic peer link + * status analysis + * + * According to the design, there are 3 kinds of peers which will be + * treated differently: + * 1) normal: not do any flow control for the peer + * 2) limited: will apply flow control for the peer, but frames are allowed + * to send + * 3) paused: will apply flow control for the peer, no frame is allowed + * to send + * + * @param pdev - the data physical device that sent the tx frames + * @param status - the number of peers need to be handled + * @param peer_link_report - the link status dedail message + */ +void +ol_txrx_peer_link_status_handler( + ol_txrx_pdev_handle pdev, + u_int16_t peer_num, + struct rate_report_t *peer_link_status); + + +#else +static inline void ol_txrx_peer_link_status_handler( + ol_txrx_pdev_handle pdev, + u_int16_t peer_num, + struct rate_report_t *peer_link_status) +{ +} +#endif + + +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + +/** + * ol_txrx_update_tx_queue_groups() - update vdev tx queue group if + * vdev id mask and ac mask is not matching + * @pdev: the data physical device + * @group_id: TXQ group id + * @credit: TXQ group credit count + * @absolute: TXQ group absolute + * @vdev_id_mask: TXQ vdev group id mask + * @ac_mask: TQX access category mask + * + * Return: None + */ +void +ol_txrx_update_tx_queue_groups( + ol_txrx_pdev_handle pdev, + u_int8_t group_id, + int32_t credit, + u_int8_t absolute, + u_int32_t vdev_id_mask, + u_int32_t ac_mask +); + +/** + * ol_tx_desc_update_group_credit() - update group credits for txq group + * @pdev: the data physical device + * @tx_desc_id: desc id of tx completion message + * @credit: number of credits to update + * @absolute: absolute value + * @status: tx completion message status + * + * Return: None + */ +void +ol_tx_desc_update_group_credit( + ol_txrx_pdev_handle pdev, + u_int16_t tx_desc_id, + int credit, u_int8_t absolute, enum htt_tx_status status); + +void ol_tx_deduct_one_any_group_credit(ol_txrx_pdev_handle pdev); + +#ifdef DEBUG_HL_LOGGING + +/** + * ol_tx_update_group_credit_stats() - update group credits stats for txq groups + * @pdev: the data physical device + * + * Return: None + */ +void +ol_tx_update_group_credit_stats(ol_txrx_pdev_handle pdev); + +/** + * ol_tx_dump_group_credit_stats() - dump group credits stats for txq groups + * @pdev: the data physical device + * + * Return: None + */ +void +ol_tx_dump_group_credit_stats(ol_txrx_pdev_handle pdev); + +/** + * ol_tx_clear_group_credit_stats() - clear group credits stats for txq groups + * @pdev: the data physical device + * + * Return: None + */ +void +ol_tx_clear_group_credit_stats(ol_txrx_pdev_handle pdev); +#else + +static inline void ol_tx_update_group_credit_stats(ol_txrx_pdev_handle pdev) +{ +} + +static inline void ol_tx_dump_group_credit_stats(ol_txrx_pdev_handle pdev) +{ +} + +static inline void ol_tx_clear_group_credit_stats(ol_txrx_pdev_handle pdev) +{ +} +#endif + +#else +static inline void +ol_tx_desc_update_group_credit( + ol_txrx_pdev_handle pdev, + u_int16_t tx_desc_id, + int credit, u_int8_t absolute, enum htt_tx_status status) +{ +} + +static inline void ol_tx_deduct_one_any_group_credit(ol_txrx_pdev_handle pdev) +{} +#endif + +/** + * @brief Init the total amount of target credit. + * @details + * + * @param pdev - the data physical device that sent the tx frames + * @param credit_delta - how much to increment the target's tx credit by + */ +void ol_tx_target_credit_init(struct ol_txrx_pdev_t *pdev, int credit_delta); + +/** + * @brief Process a tx completion message for a single MSDU. + * @details + * The ol_tx_single_completion_handler function performs the same tx + * completion processing as the ol_tx_completion_handler, but for a + * single frame. + * ol_tx_completion_handler is optimized to handle batch completions + * as efficiently as possible; in contrast ol_tx_single_completion_handler + * handles single frames as simply and generally as possible. + * Thus, this ol_tx_single_completion_handler function is suitable for + * intermittent usage, such as for tx mgmt frames. + * + * @param pdev - the data physical device that sent the tx frames + * @param status - whether transmission was successful + * @param tx_msdu_id - ID of the frame which completed transmission + */ +void +ol_tx_single_completion_handler(ol_txrx_pdev_handle pdev, + enum htt_tx_status status, uint16_t tx_desc_id); + +/** + * @brief Update the amount of target credit. + * @details + * When the target finishes with an old transmit frame, it can use the + * space that was occupied by the old tx frame to store a new tx frame. + * This function is used to inform the txrx layer, where the HL tx download + * scheduler resides, about such updates to the target's tx credit. + * This credit update is done explicitly, rather than having the txrx layer + * update the credit count itself inside the ol_tx_completion handler + * function. This provides HTT with the flexibility to limit the rate of + * downloads from the TXRX layer's download scheduler, by controlling how + * much credit the download scheduler gets, and also provides the flexibility + * to account for a change in the tx memory pool size within the target. + * This function is only used for HL systems; in LL systems, each tx frame + * is assumed to use exactly one credit (for its target-side tx descriptor), + * and any rate limiting is managed within the target. + * + * @param pdev - the data physical device that sent the tx frames + * @param credit_delta - how much to increment the target's tx credit by + */ +void ol_tx_target_credit_update(struct ol_txrx_pdev_t *pdev, int credit_delta); + +/** + * @brief Process an rx indication message sent by the target. + * @details + * The target sends a rx indication message to the host as a + * notification that there are new rx frames available for the + * host to process. + * The HTT host layer locates the rx descriptors and rx frames + * associated with the indication, and calls this function to + * invoke the rx data processing on the new frames. + * (For LL, the rx descriptors and frames are delivered directly + * to the host via MAC DMA, while for HL the rx descriptor and + * frame for individual frames are combined with the rx indication + * message.) + * All MPDUs referenced by a rx indication message belong to the + * same peer-TID. + * + * @param pdev - the data physical device that received the frames + * (registered with HTT as a context pointer during attach time) + * @param rx_ind_msg - the network buffer holding the rx indication message + * (For HL, this netbuf also holds the rx desc and rx payload, but + * the data SW is agnostic to whether the desc and payload are + * piggybacked with the rx indication message.) + * @param peer_id - which peer sent this rx data + * @param tid - what (extended) traffic type the rx data is + * @param num_mpdu_ranges - how many ranges of MPDUs does the message describe. + * Each MPDU within the range has the same rx status. + */ +#ifdef WLAN_PARTIAL_REORDER_OFFLOAD +void +ol_rx_indication_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + uint16_t peer_id, uint8_t tid, int num_mpdu_ranges); +#else +static inline void +ol_rx_indication_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + uint16_t peer_id, uint8_t tid, int num_mpdu_ranges) +{ +} +#endif + +/** + * @brief Process an rx fragment indication message sent by the target. + * @details + * The target sends a rx fragment indication message to the host as a + * notification that there are new rx fragment available for the + * host to process. + * The HTT host layer locates the rx descriptors and rx fragment + * associated with the indication, and calls this function to + * invoke the rx fragment data processing on the new fragment. + * + * @param pdev - the data physical device that received the frames + * (registered with HTT as a context pointer during attach time) + * @param rx_frag_ind_msg - the network buffer holding the rx fragment + * indication message + * @param peer_id - which peer sent this rx data + * @param tid - what (extended) traffic type the rx data is + */ +void ol_rx_frag_indication_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t rx_frag_ind_msg, + uint16_t peer_id, uint8_t tid); + +/** + * @brief Process rx offload deliver indication message sent by the target. + * @details + * When the target exits offload mode, target delivers packets that it has + * held in its memory to the host using this message. + * Low latency case: + * The message contains the number of MSDUs that are being delivered by the + * target to the host. The packet itself resides in host ring along with some + * metadata describing the peer id, vdev id, tid, FW desc and length of + * the packet being delivered. + * Hight letency case: + * The message itself contains the payload of the MSDU being delivered by + * the target to the host. The message also contains meta data describing + * the packet such as peer id, vdev id, tid, FW desc and length of the packet + * being delivered. Refer to htt.h for the exact structure of the message. + * @param pdev - the data physical device that received the frame. + * @param msg - offload deliver indication message + * @param msdu_cnt - number of MSDUs being delivred. + */ +void +ol_rx_offload_deliver_ind_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t msg, uint16_t msdu_cnt); + +/** + * @brief Process a peer map message sent by the target. + * @details + * Each time the target allocates a new peer ID, it will inform the + * host via the "peer map" message. This function processes that + * message. The host data SW looks for a peer object whose MAC address + * matches the MAC address specified in the peer map message, and then + * sets up a mapping between the peer ID specified in the message and + * the peer object that was found. + * + * @param pdev - data physical device handle + * (registered with HTT as a context pointer during attach time) + * @param peer_id - ID generated by the target to refer to the peer in question + * The target may create multiple IDs for a single peer. + * @param vdev_id - Reference to the virtual device the peer is associated with + * @param peer_mac_addr - MAC address of the peer in question + * @param tx_ready - whether transmits to this peer can be done already, or + * need to wait for a call to peer_tx_ready (only applies to HL systems) + */ +void +ol_rx_peer_map_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + uint8_t vdev_id, uint8_t *peer_mac_addr, int tx_ready); + +/** + * @brief notify the host that the target is ready to transmit to a new peer. + * @details + * Some targets can immediately accept tx frames for a new peer, as soon as + * the peer's association completes. Other target need a short setup time + * before they are ready to accept tx frames for the new peer. + * If the target needs time for setup, it will provide a peer_tx_ready + * message when it is done with the setup. This function forwards this + * notification from the target to the host's tx queue manager. + * This function only applies for HL systems, in which the host determines + * which peer a given tx frame is for, and stores the tx frames in queues. + * + * @param pdev - data physical device handle + * (registered with HTT as a context pointer during attach time) + * @param peer_id - ID for the new peer which can now accept tx frames + */ +void ol_txrx_peer_tx_ready_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id); + +/** + * @brief Process a peer unmap message sent by the target. + * @details + * Each time the target frees a peer ID, it will inform the host via the + * "peer unmap" message. This function processes that message. + * The host data SW uses the peer ID from the message to find the peer + * object from peer_map[peer_id], then invalidates peer_map[peer_id] + * (by setting it to NULL), and checks whether there are any remaining + * references to the peer object. If not, the function deletes the + * peer object. + * + * @param pdev - data physical device handle + * (registered with HTT as a context pointer during attach time) + * @param peer_id - ID that is being freed. + * The target may create multiple IDs for a single peer. + */ +void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id); + +/** + * @brief Process a security indication message sent by the target. + * @details + * When a key is assigned to a peer, the target will inform the host + * with a security indication message. + * The host remembers the security type, and infers whether a rx PN + * check is needed. + * + * @param pdev - data physical device handle + * @param peer_id - which peer the security info is for + * @param sec_type - which type of security / key the peer is using + * @param is_unicast - whether security spec is for a unicast or multicast key + * @param michael_key - key used for TKIP MIC (if sec_type == TKIP) + * @param rx_pn - RSC used for WAPI PN replay check (if sec_type == WAPI) + */ +void +ol_rx_sec_ind_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + enum htt_sec_type sec_type, + int is_unicast, uint32_t *michael_key, uint32_t *rx_pn); + +/** + * @brief Process an ADDBA message sent by the target. + * @details + * When the target notifies the host of an ADDBA event for a specified + * peer-TID, the host will set up the rx reordering state for the peer-TID. + * Specifically, the host will create a rx reordering array whose length + * is based on the window size specified in the ADDBA. + * + * @param pdev - data physical device handle + * (registered with HTT as a context pointer during attach time) + * @param peer_id - which peer the ADDBA event is for + * @param tid - which traffic ID within the peer the ADDBA event is for + * @param win_sz - how many sequence numbers are in the ARQ block ack window + * set up by the ADDBA event + * @param start_seq_num - the initial value of the sequence number during the + * block ack agreement, as specified by the ADDBA request. + * @param failed - indicate whether the target's ADDBA setup succeeded: + * 0 -> success, 1 -> fail + */ +void +ol_rx_addba_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + uint8_t tid, + uint8_t win_sz, uint16_t start_seq_num, uint8_t failed); + +/** + * @brief Process a DELBA message sent by the target. + * @details + * When the target notifies the host of a DELBA event for a specified + * peer-TID, the host will clean up the rx reordering state for the peer-TID. + * Specifically, the host will remove the rx reordering array, and will + * set the reorder window size to be 1 (stop and go ARQ). + * + * @param pdev - data physical device handle + * (registered with HTT as a context pointer during attach time) + * @param peer_id - which peer the ADDBA event is for + * @param tid - which traffic ID within the peer the ADDBA event is for + */ +void +ol_rx_delba_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id, uint8_t tid); + +enum htt_rx_flush_action { + htt_rx_flush_release, + htt_rx_flush_discard, +}; + +/** + * @brief Process a rx reorder flush message sent by the target. + * @details + * The target's rx reorder logic can send a flush indication to the + * host's rx reorder buffering either as a flush IE within a rx + * indication message, or as a standalone rx reorder flush message. + * This ol_rx_flush_handler function processes the standalone rx + * reorder flush message from the target. + * The flush message specifies a range of sequence numbers whose + * rx frames are flushed. + * Some sequence numbers within the specified range may not have + * rx frames; the host needs to check for each sequence number in + * the specified range whether there are rx frames held for that + * sequence number. + * + * @param pdev - data physical device handle + * (registered with HTT as a context pointer during attach time) + * @param peer_id - which peer's rx data is being flushed + * @param tid - which traffic ID within the peer has the rx data being flushed + * @param seq_num_start - Which sequence number within the rx reordering + * buffer the flushing should start with. + * This is the LSBs of the 802.11 sequence number. + * This sequence number is masked with the rounded-to-power-of-two + * window size to generate a reorder buffer index. + * The flush includes this initial sequence number. + * @param seq_num_end - Which sequence number within the rx reordering + * buffer the flushing should stop at. + * This is the LSBs of the 802.11 sequence number. + * This sequence number is masked with the rounded-to-power-of-two + * window size to generate a reorder buffer index. + * The flush excludes this final sequence number. + * @param action - whether to release or discard the rx frames + */ +void +ol_rx_flush_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + uint8_t tid, + uint16_t seq_num_start, + uint16_t seq_num_end, enum htt_rx_flush_action action); + +/** + * @brief Process a rx pn indication message + * @details + * When the peer is configured to get PN checking done in target, + * the target instead of sending reorder flush/release messages + * sends PN indication messages which contain the start and end + * sequence numbers to be flushed/released along with the sequence + * numbers of MPDUs that failed the PN check in target. + * + * @param pdev - data physical device handle + * (registered with HTT as a context pointer during attach time) + * @param peer_id - which peer's rx data is being flushed + * @param tid - which traffic ID within the peer + * @param seq_num_start - Which sequence number within the rx reordering + * buffer to start with. + * This is the LSBs of the 802.11 sequence number. + * This sequence number is masked with the rounded-to-power-of-two + * window size to generate a reorder buffer index. + * This is the initial sequence number. + * @param seq_num_end - Which sequence number within the rx reordering + * buffer to stop at. + * This is the LSBs of the 802.11 sequence number. + * This sequence number is masked with the rounded-to-power-of-two + * window size to generate a reorder buffer index. + * The processing stops right before this sequence number + * @param pn_ie_cnt - Indicates the number of PN information elements. + * @param pn_ie - Pointer to the array of PN information elements. Each + * PN information element contains the LSBs of the 802.11 sequence number + * of the MPDU that failed the PN checking in target. + */ +void +ol_rx_pn_ind_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + uint8_t tid, + uint16_t seq_num_start, + uint16_t seq_num_end, uint8_t pn_ie_cnt, uint8_t *pn_ie); + +/** + * @brief Process a stats message sent by the target. + * @details + * The host can request target for stats. + * The target sends the stats to the host via a confirmation message. + * This ol_txrx_fw_stats_handler function processes the confirmation message. + * Currently, this processing consists of copying the stats from the message + * buffer into the txrx pdev object, and waking the sleeping host context + * that requested the stats. + * + * @param pdev - data physical device handle + * (registered with HTT as a context pointer during attach time) + * @param cookie - Value echoed from the cookie in the stats request + * message. This allows the host SW to find the stats request object. + * (Currently, this cookie is unused.) + * @param stats_info_list - stats confirmation message contents, containing + * a list of the stats requested from the target + */ +void +ol_txrx_fw_stats_handler(ol_txrx_pdev_handle pdev, + uint8_t cookie, uint8_t *stats_info_list); + +/** + * @brief Process a tx inspect message sent by the target. + * @details: + * TODO: update + * This tx inspect message indicates via the descriptor ID + * which tx frames are to be inspected by host. The host + * re-injects the packet back to the host for a number of + * cases. + * + * @param pdev - the data physical device that sent the tx frames + * (registered with HTT as a context pointer during attach time) + * @param num_msdus - how many MSDUs are referenced by the tx completion + * message + * @param tx_msdu_id_iterator - abstract method of finding the IDs for the + * individual MSDUs referenced by the tx completion message, via the + * htt_tx_compl_desc_id API function + */ +void +ol_tx_inspect_handler(ol_txrx_pdev_handle pdev, + int num_msdus, void *tx_desc_id_iterator); + +/** + * @brief Get the UAPSD mask. + * @details + * This function will return the UAPSD TID mask. + * + * @param txrx_pdev - pointer to the txrx pdev object + * @param peer_id - PeerID. + * @return uapsd mask value + */ +uint8_t +ol_txrx_peer_uapsdmask_get(struct ol_txrx_pdev_t *txrx_pdev, uint16_t peer_id); + +/** + * @brief Get the Qos Capable. + * @details + * This function will return the txrx_peer qos_capable. + * + * @param txrx_pdev - pointer to the txrx pdev object + * @param peer_id - PeerID. + * @return qos_capable value + */ +uint8_t +ol_txrx_peer_qoscapable_get(struct ol_txrx_pdev_t *txrx_pdev, uint16_t peer_id); + +/** + * @brief Process an rx indication message sent by the target. + * @details + * The target sends a rx indication message to the host as a + * notification that there are new rx frames available for the + * host to process. + * The HTT host layer locates the rx descriptors and rx frames + * associated with the indication, and calls this function to + * invoke the rx data processing on the new frames. + * All MPDUs referenced by a rx indication message belong to the + * same peer-TID. The frames indicated have been re-ordered by + * the target. + * + * @param pdev - the data physical device that received the frames + * (registered with HTT as a context pointer during attach time) + * @param rx_ind_msg - the network buffer holding the rx indication message + * @param peer_id - which peer sent this rx data + * @param tid - what (extended) traffic type the rx data is + * @param is_offload - is this an offload indication? + */ +#ifdef WLAN_FULL_REORDER_OFFLOAD +void +ol_rx_in_order_indication_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + uint16_t peer_id, + uint8_t tid, uint8_t is_offload); +#else +static inline void +ol_rx_in_order_indication_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + uint16_t peer_id, + uint8_t tid, uint8_t is_offload) +{ +} +#endif + +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + +/** + * ol_tx_get_max_tx_groups_supported() - get max TCQ groups supported + * @pdev: the data physical device that received the frames + * + * Return: number of max groups supported + */ +u_int32_t ol_tx_get_max_tx_groups_supported(struct ol_txrx_pdev_t *pdev); +#else + +static inline u_int32_t +ol_tx_get_max_tx_groups_supported(struct ol_txrx_pdev_t *pdev) +{ + return 0; +} +#endif + +#if defined(FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL) && \ + defined(FEATURE_HL_DBS_GROUP_CREDIT_SHARING) +int ol_txrx_distribute_group_credits(struct ol_txrx_pdev_t *pdev, u8 group_id, + u32 membership_new); +#else +static inline int ol_txrx_distribute_group_credits(struct ol_txrx_pdev_t *pdev, + u8 group_id, + u32 membership_new) +{ + return 0; +} +#endif /* + * FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL && + * FEATURE_HL_DBS_GROUP_CREDIT_SHARING + */ +#endif /* _OL_TXRX_HTT_API__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_osif_api.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_osif_api.h new file mode 100644 index 0000000000000000000000000000000000000000..a880d46a29bf6461883a0c15155cf9f7459058db --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_osif_api.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2012, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_osif_api.h + * @brief Define the host data API functions called by the host OS shim SW. + */ +#ifndef _OL_TXRX_OSIF_API__H_ +#define _OL_TXRX_OSIF_API__H_ + +#include /* qdf_nbuf_t */ +#include "cds_sched.h" +#include "ol_txrx_ctrl_api.h" +#include + +/** + * struct ol_rx_cached_buf - rx cached buffer + * @list: linked list + * @buf: skb buffer + */ +struct ol_rx_cached_buf { + struct list_head list; + qdf_nbuf_t buf; +}; + +struct txrx_rx_metainfo; + +/** + * @brief Divide a jumbo TCP frame into smaller segments. + * @details + * For efficiency, the protocol stack above the WLAN driver may operate + * on jumbo tx frames, which are larger than the 802.11 MTU. + * The OSIF SW uses this txrx API function to divide the jumbo tx TCP frame + * into a series of segment frames. + * The segments are created as clones of the input jumbo frame. + * The txrx SW generates a new encapsulation header (ethernet + IP + TCP) + * for each of the output segment frames. The exact format of this header, + * e.g. 802.3 vs. Ethernet II, and IPv4 vs. IPv6, is chosen to match the + * header format of the input jumbo frame. + * The input jumbo frame is not modified. + * After the ol_txrx_osif_tso_segment returns, the OSIF SW needs to perform + * DMA mapping on each of the segment network buffers, and also needs to + * + * @param txrx_vdev - which virtual device will transmit the TSO segments + * @param max_seg_payload_bytes - the maximum size for the TCP payload of + * each segment frame. + * This does not include the ethernet + IP + TCP header sizes. + * @param jumbo_tcp_frame - jumbo frame which needs to be cloned+segmented + * @return + * NULL if the segmentation fails, - OR - + * a NULL-terminated list of segment network buffers + */ +qdf_nbuf_t ol_txrx_osif_tso_segment(ol_txrx_vdev_handle txrx_vdev, + int max_seg_payload_bytes, + qdf_nbuf_t jumbo_tcp_frame); + +qdf_nbuf_t ol_tx_data(struct cdp_soc_t *soc, uint8_t vdev_id, qdf_nbuf_t skb); + +void ol_rx_data_process(struct ol_txrx_peer_t *peer, + qdf_nbuf_t rx_buf_list); + +void ol_txrx_flush_rx_frames(struct ol_txrx_peer_t *peer, + bool drop); +#endif /* _OL_TXRX_OSIF_API__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_stats.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..8d966a51a90de5dd85fccef0aff0f3edf141d5d8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_txrx_stats.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2012, 2014-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_status.h + * @brief Functions provided for visibility and debugging. + * NOTE: This file is used by both kernel driver SW and userspace SW. + * Thus, do not reference use any kernel header files or defs in this file! + */ +#ifndef _OL_TXRX_STATS__H_ +#define _OL_TXRX_STATS__H_ + +#include /* uint64_t */ + + + +struct ol_txrx_stats_elem { + uint64_t pkts; + uint64_t bytes; +}; + +#define NUM_MAX_TSO_SEGS 4 +#define NUM_MAX_TSO_SEGS_MASK (NUM_MAX_TSO_SEGS - 1) + +#define NUM_MAX_TSO_MSDUS 32 +#define NUM_MAX_TSO_MSDUS_MASK (NUM_MAX_TSO_MSDUS - 1) + +struct ol_txrx_stats_tso_msdu { + struct qdf_tso_seg_t tso_segs[NUM_MAX_TSO_SEGS]; + uint8_t num_seg; + uint8_t tso_seg_idx; + uint32_t total_len; + uint32_t gso_size; + uint8_t nr_frags; +}; + +struct ol_txrx_stats_tso_info { + struct ol_txrx_stats_tso_msdu tso_msdu_info[NUM_MAX_TSO_MSDUS]; + uint32_t tso_msdu_idx; +}; + +/** + * @brief data stats published by the host txrx layer + * + * ------------------------- + * + * TX + * + */ +struct ol_txrx_stats_tx_dropped { + /* MSDUs that the host did not accept */ + struct ol_txrx_stats_elem host_reject; + /* MSDUs which could not be downloaded to the target */ + struct ol_txrx_stats_elem download_fail; + /* + * MSDUs which the target discarded + * (lack of memory or old age) + */ + struct ol_txrx_stats_elem target_discard; + /* + * MSDUs which the target sent but + * couldn't get an ack for + */ + struct ol_txrx_stats_elem no_ack; + + /* + * MSDUs which the target drop + * (lack of tx descriptor) + */ + struct ol_txrx_stats_elem target_drop; + + /* MSDU which were dropped for other reasons */ + struct ol_txrx_stats_elem others; +}; + +struct ol_txrx_tso_histogram { + uint32_t pkts_1; + uint32_t pkts_2_5; + uint32_t pkts_6_10; + uint32_t pkts_11_15; + uint32_t pkts_16_20; + uint32_t pkts_20_plus; +}; + +struct ol_txrx_stats_tx_histogram { + uint32_t pkts_1; + uint32_t pkts_2_10; + uint32_t pkts_11_20; + uint32_t pkts_21_30; + uint32_t pkts_31_40; + uint32_t pkts_41_50; + uint32_t pkts_51_60; + uint32_t pkts_61_plus; +}; +struct ol_txrx_stats_tx_tso { + struct ol_txrx_stats_elem tso_pkts; +#if defined(FEATURE_TSO) + struct ol_txrx_stats_tso_info tso_info; + struct ol_txrx_tso_histogram tso_hist; + qdf_spinlock_t tso_stats_lock; +#endif +}; + +struct ol_txrx_stats_tx { + /* MSDUs given to the txrx layer by the management stack */ + struct ol_txrx_stats_elem mgmt; + /* MSDUs received from the stack */ + struct ol_txrx_stats_elem from_stack; + /* MSDUs successfully sent across the WLAN */ + struct ol_txrx_stats_elem delivered; + struct ol_txrx_stats_tx_dropped dropped; + /* contains information of packets recevied per tx completion*/ + struct ol_txrx_stats_tx_histogram comp_histogram; + /* TSO (TCP segmentation offload) information */ + struct ol_txrx_stats_tx_tso tso; +}; + +/* + * RX + */ +struct ol_txrx_stats_rx_histogram { + uint32_t pkts_1; + uint32_t pkts_2_10; + uint32_t pkts_11_20; + uint32_t pkts_21_30; + uint32_t pkts_31_40; + uint32_t pkts_41_50; + uint32_t pkts_51_60; + uint32_t pkts_61_plus; +}; +struct ol_txrx_stats_rx_ibss_fwd { + /* MSDUs forwarded to network stack */ + u_int32_t packets_stack; + /* MSDUs forwarded from the rx path to the tx path */ + u_int32_t packets_fwd; + /* MSDUs forwarded to stack and tx path */ + u_int32_t packets_stack_n_fwd; +}; +struct ol_txrx_stats_rx { + /* MSDUs given to the OS shim */ + struct ol_txrx_stats_elem delivered; + struct ol_txrx_stats_elem dropped_err; + struct ol_txrx_stats_elem dropped_mic_err; + struct ol_txrx_stats_elem dropped_peer_invalid; + struct ol_txrx_stats_rx_ibss_fwd intra_bss_fwd; + struct ol_txrx_stats_rx_histogram rx_ind_histogram; + uint32_t msdus_with_frag_ind; + uint32_t msdus_with_offload_ind; +}; +struct ol_txrx_stats { + struct ol_txrx_stats_tx tx; + struct ol_txrx_stats_rx rx; +}; + +/* + * Structure to consolidate host stats + */ +struct ieee80211req_ol_ath_host_stats { + struct ol_txrx_stats txrx_stats; + struct { + int pkt_q_fail_count; + int pkt_q_empty_count; + int send_q_empty_count; + } htc; + struct { + int pipe_no_resrc_count; + int ce_ring_delta_fail_count; + } hif; +}; + +#endif /* _OL_TXRX_STATS__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_vowext_dbg_defs.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_vowext_dbg_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..ab82d5e00dc272b53215e928fe4bdc43340f2f93 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_vowext_dbg_defs.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012, 2014-2016, 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _VOW_DEFINES__H_ +#define _VOW_DEFINES__H_ + +#define UDP_CKSUM_OFFSET 40 /* UDP check sum offset in network buffer */ +#define RTP_HDR_OFFSET 42 /* RTP header offset in network buffer */ +#define EXT_HDR_OFFSET 54 /* Extension header offset in network buffer */ +#define UDP_PDU_RTP_EXT 0x90 /* ((2 << 6) | (1 << 4)) RTP V2 + X bit */ +#define IP_VER4_N_NO_EXTRA_HEADERS 0x45 +#define IPERF3_DATA_OFFSET 12 /* iperf3 data offset from EXT_HDR_OFFSET */ +#define HAL_RX_40 0x08 /* 40 Mhz */ +#define HAL_RX_GI 0x04 /* full gi */ + +struct vow_extstats { + uint8_t rx_rssi_ctl0; /* control channel chain0 rssi */ + uint8_t rx_rssi_ctl1; /* control channel chain1 rssi */ + uint8_t rx_rssi_ctl2; /* control channel chain2 rssi */ + uint8_t rx_rssi_ext0; /* extension channel chain0 rssi */ + uint8_t rx_rssi_ext1; /* extension channel chain1 rssi */ + uint8_t rx_rssi_ext2; /* extension channel chain2 rssi */ + uint8_t rx_rssi_comb; /* combined RSSI value */ + uint8_t rx_bw; /* Band width 0-20, 1-40, 2-80 */ + uint8_t rx_sgi; /* Guard interval, 0-Long GI, 1-Short GI */ + uint8_t rx_nss; /* Number of spatial streams */ + uint8_t rx_mcs; /* Rate MCS value */ + uint8_t rx_ratecode; /* Hardware rate code */ + uint8_t rx_rs_flags; /* Receive misc flags */ + uint8_t rx_moreaggr; /* 0 - non aggr frame */ + uint32_t rx_macTs; /* Time stamp */ + uint16_t rx_seqno; /* rx sequence number */ +}; + +/** + * @brief populates vow ext stats in given network buffer. + * @param msdu - network buffer handle + * @param pdev - handle to htt dev. + */ +void ol_ath_add_vow_extstats(htt_pdev_handle pdev, qdf_nbuf_t msdu); + +#endif /* _VOW_DEFINES__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ipv6_defs.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ipv6_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..ffdd6568afa2138404ca6bc7c2e5514300fbe00d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ipv6_defs.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2012-2014, 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _IPV6__H_ +#define _IPV6__H_ + +#if defined(ATH_TARGET) +#include /* A_UINT8 */ +#else +#include /* A_UINT8 */ +#endif + +/* utilities for converting between network byte order and native endianness */ +#ifndef BYTESWAP32 +#define BYTESWAP32(x) \ + ((((x) & 0x000000ff) << 24) /* byte 0 -> byte 3 */ | \ + (((x) & 0x0000ff00) << 8) /* byte 1 -> byte 2 */ | \ + (((x) & 0x00ff0000) >> 8) /* byte 2 -> byte 1 */ | \ + (((x) & 0xff000000) >> 24) /* byte 3 -> byte 0 */) +#endif /* BYTESWAP32 */ + +#ifndef BE_TO_CPU32 +#if defined(ATH_TARGET) +/* assume target is little-endian */ +#define BE_TO_CPU32(x) BYTESWAP32(x) +#else +#ifdef BIG_ENDIAN_HOST +#define BE_TO_CPU32(x) (x) +#else +#define BE_TO_CPU32(x) BYTESWAP32(x) +#endif +#endif +#endif /* BE_TO_CPU32 */ + +/* IPv6 header definition */ + +#define IPV6_ADDR_LEN 4 /* bytes */ +struct ipv6_hdr_t { + /* version, traffic class, and flow label */ + A_UINT32 ver_tclass_flowlabel; + A_UINT8 pyld_len[2]; /* payload length */ + A_UINT8 next_hdr; + A_UINT8 hop_limit; + A_UINT8 src_addr[IPV6_ADDR_LEN]; + A_UINT8 dst_addr[IPV6_ADDR_LEN]; +}; + +#define IPV6_HDR_LEN (sizeof(struct ipv6_hdr_t)) +#define IPV6_HDR_OFFSET_NEXT_HDR (offsetof(struct ipv6_hdr_t, next_hdr)) +#define IPV6_HDR_OFFSET_DST_ADDR (offsetof(struct ipv6_hdr_t, dst_addr[0])) + +/* IPv6 header field access macros */ + +#define IPV6_HDR_VERSION_M 0xF0000000 +#define IPV6_HDR_VERSION_S 28 + +#define IPV6_HDR_TRAFFIC_CLASS_M 0x0FF00000 +#define IPV6_HDR_TRAFFIC_CLASS_S 20 + +#define IPV6_HDR_FLOW_LABEL_M 0x000FFFFF +#define IPV6_HDR_FLOW_LABEL_S 0 + +static inline A_UINT8 ipv6_version(struct ipv6_hdr_t *ipv6_hdr) +{ + return (BE_TO_CPU32(ipv6_hdr->ver_tclass_flowlabel) & + IPV6_HDR_VERSION_M) >> IPV6_HDR_VERSION_S; +} + +static inline A_UINT8 ipv6_traffic_class(struct ipv6_hdr_t *ipv6_hdr) +{ + return (A_UINT8)((BE_TO_CPU32(ipv6_hdr->ver_tclass_flowlabel) & + IPV6_HDR_TRAFFIC_CLASS_M) >> IPV6_HDR_TRAFFIC_CLASS_S); +} + +static inline A_UINT32 ipv6_flow_label(struct ipv6_hdr_t *ipv6_hdr) +{ + return (BE_TO_CPU32(ipv6_hdr->ver_tclass_flowlabel) & + IPV6_HDR_FLOW_LABEL_M) >> IPV6_HDR_FLOW_LABEL_S; +} + +#endif /* _IPV6__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_cfg.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_cfg.c new file mode 100644 index 0000000000000000000000000000000000000000..111105298955eef212f3fbbf6d21005c0867934f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_cfg.c @@ -0,0 +1,685 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +unsigned int vow_config; + +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +/** + * ol_tx_set_flow_control_parameters() - set flow control parameters + * @cfg_ctx: cfg context + * @cfg_param: cfg parameters + * + * Return: none + */ +void ol_tx_set_flow_control_parameters(struct cdp_cfg *cfg_pdev, + struct txrx_pdev_cfg_param_t *cfg_param) +{ + struct txrx_pdev_cfg_t *cfg_ctx = (struct txrx_pdev_cfg_t *)cfg_pdev; + + cfg_ctx->tx_flow_start_queue_offset = + cfg_param->tx_flow_start_queue_offset; + cfg_ctx->tx_flow_stop_queue_th = + cfg_param->tx_flow_stop_queue_th; +} +#endif + +#ifdef CONFIG_HL_SUPPORT + +#ifdef CONFIG_CREDIT_REP_THROUGH_CREDIT_UPDATE +static inline +void ol_pdev_cfg_credit_update(struct txrx_pdev_cfg_t *cfg_ctx) +{ + cfg_ctx->tx_free_at_download = 1; + cfg_ctx->credit_update_enabled = 1; +} +#else +static inline +void ol_pdev_cfg_credit_update(struct txrx_pdev_cfg_t *cfg_ctx) +{ + cfg_ctx->tx_free_at_download = 0; + cfg_ctx->credit_update_enabled = 0; +} +#endif /* CONFIG_CREDIT_REP_THROUGH_CREDIT_UPDATE */ + +/** + * ol_pdev_cfg_param_update() - assign download size of tx frame for txrx + * pdev that will be used across datapath + * @cfg_ctx: ptr to config parameter for txrx pdev + * + * Return: None + */ +static inline +void ol_pdev_cfg_param_update(struct txrx_pdev_cfg_t *cfg_ctx) +{ + cfg_ctx->is_high_latency = 1; + /* 802.1Q and SNAP / LLC headers are accounted for elsewhere */ + cfg_ctx->tx_download_size = 1500; + ol_pdev_cfg_credit_update(cfg_ctx); +} + +#else /* CONFIG_HL_SUPPORT */ +static inline +void ol_pdev_cfg_param_update(struct txrx_pdev_cfg_t *cfg_ctx) +{ + /* + * Need to change HTT_LL_TX_HDR_SIZE_IP accordingly. + * Include payload, up to the end of UDP header for IPv4 case + */ + cfg_ctx->tx_download_size = 16; +} +#endif + +#ifdef CONFIG_RX_PN_CHECK_OFFLOAD +static inline +void ol_pdev_cfg_rx_pn_check(struct txrx_pdev_cfg_t *cfg_ctx) +{ + /* Do not do pn check on host */ + cfg_ctx->rx_pn_check = 0; +} +#else +static inline +void ol_pdev_cfg_rx_pn_check(struct txrx_pdev_cfg_t *cfg_ctx) +{ + /* Do pn check on host */ + cfg_ctx->rx_pn_check = 1; +} +#endif /* CONFIG_RX_PN_CHECK_OFFLOAD */ + +#if CFG_TGT_DEFAULT_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK +static inline +uint8_t ol_defrag_timeout_check(void) +{ + return 1; +} +#else +static inline +uint8_t ol_defrag_timeout_check(void) +{ + return 0; +} +#endif + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK +/** + * ol_cfg_update_del_ack_params() - update delayed ack params + * @cfg_ctx: cfg context + * @cfg_param: parameters + * + * Return: none + */ +void ol_cfg_update_del_ack_params(struct txrx_pdev_cfg_t *cfg_ctx, + struct txrx_pdev_cfg_param_t *cfg_param) +{ + cfg_ctx->del_ack_enable = cfg_param->del_ack_enable; + cfg_ctx->del_ack_timer_value = cfg_param->del_ack_timer_value; + cfg_ctx->del_ack_pkt_count = cfg_param->del_ack_pkt_count; +} +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE +static inline +void ol_cfg_update_bundle_params(struct txrx_pdev_cfg_t *cfg_ctx, + struct txrx_pdev_cfg_param_t *cfg_param) +{ + cfg_ctx->bundle_timer_value = cfg_param->bundle_timer_value; + cfg_ctx->bundle_size = cfg_param->bundle_size; +} +#else +static inline +void ol_cfg_update_bundle_params(struct txrx_pdev_cfg_t *cfg_ctx, + struct txrx_pdev_cfg_param_t *cfg_param) +{ +} +#endif + +/* FIX THIS - + * For now, all these configuration parameters are hardcoded. + * Many of these should actually be determined dynamically instead. + */ + +struct cdp_cfg *ol_pdev_cfg_attach(qdf_device_t osdev, void *pcfg_param) +{ + struct txrx_pdev_cfg_param_t *cfg_param = pcfg_param; + struct txrx_pdev_cfg_t *cfg_ctx; + int i; + + cfg_ctx = qdf_mem_malloc(sizeof(*cfg_ctx)); + if (!cfg_ctx) + return NULL; + + ol_pdev_cfg_param_update(cfg_ctx); + ol_pdev_cfg_rx_pn_check(cfg_ctx); + + cfg_ctx->defrag_timeout_check = ol_defrag_timeout_check(); + cfg_ctx->max_peer_id = 511; + cfg_ctx->max_vdev = CFG_TGT_NUM_VDEV; + cfg_ctx->pn_rx_fwd_check = 1; + cfg_ctx->frame_type = wlan_frm_fmt_802_3; + cfg_ctx->max_thruput_mbps = MAX_THROUGHPUT; + cfg_ctx->max_nbuf_frags = 1; + cfg_ctx->vow_config = vow_config; + cfg_ctx->target_tx_credit = CFG_TGT_NUM_MSDU_DESC; + cfg_ctx->throttle_period_ms = 40; + cfg_ctx->dutycycle_level[0] = THROTTLE_DUTY_CYCLE_LEVEL0; + cfg_ctx->dutycycle_level[1] = THROTTLE_DUTY_CYCLE_LEVEL1; + cfg_ctx->dutycycle_level[2] = THROTTLE_DUTY_CYCLE_LEVEL2; + cfg_ctx->dutycycle_level[3] = THROTTLE_DUTY_CYCLE_LEVEL3; + cfg_ctx->rx_fwd_disabled = 0; + cfg_ctx->is_packet_log_enabled = 0; + cfg_ctx->is_full_reorder_offload = cfg_param->is_full_reorder_offload; +#ifdef WLAN_FEATURE_TSF_PLUS + cfg_ctx->is_ptp_rx_opt_enabled = 0; +#endif + cfg_ctx->ipa_uc_rsc.uc_offload_enabled = + cfg_param->is_uc_offload_enabled; + cfg_ctx->ipa_uc_rsc.tx_max_buf_cnt = cfg_param->uc_tx_buffer_count; + cfg_ctx->ipa_uc_rsc.tx_buf_size = cfg_param->uc_tx_buffer_size; + cfg_ctx->ipa_uc_rsc.rx_ind_ring_size = + cfg_param->uc_rx_indication_ring_count; + cfg_ctx->ipa_uc_rsc.tx_partition_base = cfg_param->uc_tx_partition_base; + cfg_ctx->enable_rxthread = cfg_param->enable_rxthread; + cfg_ctx->ip_tcp_udp_checksum_offload = + cfg_param->ip_tcp_udp_checksum_offload; + cfg_ctx->p2p_ip_tcp_udp_checksum_offload = + cfg_param->p2p_ip_tcp_udp_checksum_offload; + cfg_ctx->nan_tcp_udp_checksumoffload = + cfg_param->nan_ip_tcp_udp_checksum_offload; + cfg_ctx->ce_classify_enabled = cfg_param->ce_classify_enabled; + cfg_ctx->gro_enable = cfg_param->gro_enable; + cfg_ctx->tc_based_dyn_gro = cfg_param->tc_based_dyn_gro; + cfg_ctx->tc_ingress_prio = cfg_param->tc_ingress_prio; + cfg_ctx->tso_enable = cfg_param->tso_enable; + cfg_ctx->lro_enable = cfg_param->lro_enable; + cfg_ctx->sg_enable = cfg_param->sg_enable; + cfg_ctx->enable_data_stall_detection = + cfg_param->enable_data_stall_detection; + cfg_ctx->enable_flow_steering = cfg_param->enable_flow_steering; + cfg_ctx->disable_intra_bss_fwd = cfg_param->disable_intra_bss_fwd; + cfg_ctx->pktlog_buffer_size = cfg_param->pktlog_buffer_size; + + ol_cfg_update_del_ack_params(cfg_ctx, cfg_param); + + ol_cfg_update_bundle_params(cfg_ctx, cfg_param); + + ol_tx_set_flow_control_parameters((struct cdp_cfg *)cfg_ctx, cfg_param); + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + cfg_ctx->ac_specs[i].wrr_skip_weight = + cfg_param->ac_specs[i].wrr_skip_weight; + cfg_ctx->ac_specs[i].credit_threshold = + cfg_param->ac_specs[i].credit_threshold; + cfg_ctx->ac_specs[i].send_limit = + cfg_param->ac_specs[i].send_limit; + cfg_ctx->ac_specs[i].credit_reserve = + cfg_param->ac_specs[i].credit_reserve; + cfg_ctx->ac_specs[i].discard_weight = + cfg_param->ac_specs[i].discard_weight; + } + + return (struct cdp_cfg *)cfg_ctx; +} + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE + +int ol_cfg_get_bundle_timer_value(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->bundle_timer_value; +} + +int ol_cfg_get_bundle_size(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->bundle_size; +} +#endif + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK +/** + * ol_cfg_get_del_ack_timer_value() - get delayed ack timer value + * @cfg_pdev: pdev handle + * + * Return: timer value + */ +int ol_cfg_get_del_ack_timer_value(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->del_ack_timer_value; +} + +/** + * ol_cfg_get_del_ack_enable_value() - get delayed ack enable value + * @cfg_pdev: pdev handle + * + * Return: enable/disable + */ +bool ol_cfg_get_del_ack_enable_value(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->del_ack_enable; +} + +/** + * ol_cfg_get_del_ack_count_value() - get delayed ack count value + * @pdev: cfg_pdev handle + * + * Return: count value + */ +int ol_cfg_get_del_ack_count_value(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->del_ack_pkt_count; +} +#endif + +int ol_cfg_is_high_latency(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->is_high_latency; +} + +int ol_cfg_is_credit_update_enabled(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->credit_update_enabled; +} + +int ol_cfg_max_peer_id(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + /* + * TBDXXX - this value must match the peer table + * size allocated in FW + */ + return cfg->max_peer_id; +} + +int ol_cfg_max_vdevs(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->max_vdev; +} + +int ol_cfg_rx_pn_check(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->rx_pn_check; +} + +int ol_cfg_rx_fwd_check(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->pn_rx_fwd_check; +} + +void ol_set_cfg_rx_fwd_disabled(struct cdp_cfg *cfg_pdev, + uint8_t disable_rx_fwd) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + cfg->rx_fwd_disabled = disable_rx_fwd; +} + +void ol_set_cfg_packet_log_enabled(struct cdp_cfg *cfg_pdev, uint8_t val) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + cfg->is_packet_log_enabled = val; +} + +uint8_t ol_cfg_is_packet_log_enabled(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->is_packet_log_enabled; +} + +int ol_cfg_rx_fwd_disabled(struct cdp_cfg *cfg_pdev) +{ +#if defined(ATHR_WIN_NWF) + /* for Windows, let the OS handle the forwarding */ + return 1; +#else + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->rx_fwd_disabled; +#endif +} + +int ol_cfg_rx_fwd_inter_bss(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->rx_fwd_inter_bss; +} + +enum wlan_frm_fmt ol_cfg_frame_type(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->frame_type; +} + +int ol_cfg_max_thruput_mbps(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->max_thruput_mbps; +} + +int ol_cfg_netbuf_frags_max(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->max_nbuf_frags; +} + +int ol_cfg_tx_free_at_download(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->tx_free_at_download; +} + +void ol_cfg_set_tx_free_at_download(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + cfg->tx_free_at_download = 1; +} + + +#ifdef CONFIG_HL_SUPPORT +uint16_t ol_cfg_target_tx_credit(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->target_tx_credit; +} +#else + +uint16_t ol_cfg_target_tx_credit(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + uint16_t rc; + uint16_t vow_max_sta = (cfg->vow_config & 0xffff0000) >> 16; + uint16_t vow_max_desc_persta = cfg->vow_config & 0x0000ffff; + + rc = (cfg->target_tx_credit + (vow_max_sta * vow_max_desc_persta)); + + return rc; +} +#endif + +int ol_cfg_tx_download_size(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->tx_download_size; +} + +int ol_cfg_rx_host_defrag_timeout_duplicate_check(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->defrag_timeout_check; +} + +int ol_cfg_throttle_period_ms(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->throttle_period_ms; +} + +int ol_cfg_throttle_duty_cycle_level(struct cdp_cfg *cfg_pdev, int level) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->dutycycle_level[level]; +} + +#ifdef CONFIG_HL_SUPPORT +int ol_cfg_is_full_reorder_offload(struct cdp_cfg *cfg_pdev) +{ + return 0; +} +#else +int ol_cfg_is_full_reorder_offload(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->is_full_reorder_offload; +} +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS +void ol_set_cfg_ptp_rx_opt_enabled(struct cdp_cfg *cfg_pdev, u_int8_t val) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + cfg->is_ptp_rx_opt_enabled = val; +} + +u_int8_t ol_cfg_is_ptp_rx_opt_enabled(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->is_ptp_rx_opt_enabled; +} +#endif + +/** + * ol_cfg_is_rx_thread_enabled() - return rx_thread is enable/disable + * @pdev : handle to the physical device + * + * Return: 1 - enable, 0 - disable + */ +int ol_cfg_is_rx_thread_enabled(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->enable_rxthread; +} + +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +/** + * ol_cfg_get_tx_flow_stop_queue_th() - return stop queue threshold + * @pdev : handle to the physical device + * + * Return: stop queue threshold + */ +int ol_cfg_get_tx_flow_stop_queue_th(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->tx_flow_stop_queue_th; +} + +/** + * ol_cfg_get_tx_flow_start_queue_offset() - return start queue offset + * @pdev : handle to the physical device + * + * Return: start queue offset + */ +int ol_cfg_get_tx_flow_start_queue_offset(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->tx_flow_start_queue_offset; +} +#endif + +#ifdef IPA_OFFLOAD +unsigned int ol_cfg_ipa_uc_offload_enabled(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return (unsigned int)cfg->ipa_uc_rsc.uc_offload_enabled; +} + +unsigned int ol_cfg_ipa_uc_tx_buf_size(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->ipa_uc_rsc.tx_buf_size; +} + +unsigned int ol_cfg_ipa_uc_tx_max_buf_cnt(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->ipa_uc_rsc.tx_max_buf_cnt; +} + +unsigned int ol_cfg_ipa_uc_rx_ind_ring_size(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->ipa_uc_rsc.rx_ind_ring_size; +} + +unsigned int ol_cfg_ipa_uc_tx_partition_base(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->ipa_uc_rsc.tx_partition_base; +} + +void ol_cfg_set_ipa_uc_tx_partition_base(struct cdp_cfg *cfg_pdev, uint32_t val) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + cfg->ipa_uc_rsc.tx_partition_base = val; +} +#endif /* IPA_OFFLOAD */ + +/** + * ol_cfg_is_ce_classify_enabled() - Return if CE classification is enabled + * or disabled + * @pdev : handle to the physical device + * + * Return: 1 - enabled, 0 - disabled + */ +bool ol_cfg_is_ce_classify_enabled(struct cdp_cfg *cfg_pdev) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)cfg_pdev; + + return cfg->ce_classify_enabled; +} + +/** + * ol_cfg_get_wrr_skip_weight() - brief Query for the param of wrr_skip_weight + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: wrr_skip_weight for specified ac. + */ +int ol_cfg_get_wrr_skip_weight(struct cdp_cfg *pdev, int ac) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)pdev; + + if (ac >= QCA_WLAN_AC_BE && ac <= QCA_WLAN_AC_VO) + return cfg->ac_specs[ac].wrr_skip_weight; + + return 0; +} + +/** + * ol_cfg_get_credit_threshold() - Query for the param of credit_threshold + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: credit_threshold for specified ac. + */ +uint32_t ol_cfg_get_credit_threshold(struct cdp_cfg *pdev, int ac) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)pdev; + + if (ac >= QCA_WLAN_AC_BE && ac <= QCA_WLAN_AC_VO) + return cfg->ac_specs[ac].credit_threshold; + + return 0; +} + +/** + * ol_cfg_get_send_limit() - Query for the param of send_limit + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: send_limit for specified ac. + */ +uint16_t ol_cfg_get_send_limit(struct cdp_cfg *pdev, int ac) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)pdev; + + if (ac >= QCA_WLAN_AC_BE && ac <= QCA_WLAN_AC_VO) + return cfg->ac_specs[ac].send_limit; + + return 0; +} + +/** + * ol_cfg_get_credit_reserve() - Query for the param of credit_reserve + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: credit_reserve for specified ac. + */ +int ol_cfg_get_credit_reserve(struct cdp_cfg *pdev, int ac) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)pdev; + + if (ac >= QCA_WLAN_AC_BE && ac <= QCA_WLAN_AC_VO) + return cfg->ac_specs[ac].credit_reserve; + + return 0; +} + +/** + * ol_cfg_get_discard_weight() - Query for the param of discard_weight + * @pdev: handle to the physical device. + * @ac: access control, it will be BE, BK, VI, VO + * + * Return: discard_weight for specified ac. + */ +int ol_cfg_get_discard_weight(struct cdp_cfg *pdev, int ac) +{ + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *)pdev; + + if (ac >= QCA_WLAN_AC_BE && ac <= QCA_WLAN_AC_VO) + return cfg->ac_specs[ac].discard_weight; + + return 0; +} diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_ctrl_txrx_api.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_ctrl_txrx_api.h new file mode 100644 index 0000000000000000000000000000000000000000..a2e29005fa89d8983e67eb720d8e9b0194d4101c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_ctrl_txrx_api.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2011-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_ctrl_txrx_api.h + * @brief Define the host control API functions called by the host data SW. + */ +#ifndef _OL_CTRL_TXRX_API__H_ +#define _OL_CTRL_TXRX_API__H_ + +#include /* uint8_t */ +#include /* qdf_nbuf_t */ + +#include /* ol_txrx_pdev_handle */ +#include +#include /* ieee80211_frame */ +#include +#ifdef SUPPORT_HOST_STATISTICS +/** * @brief Update tx statistics + * @details + * Update tx statistics after tx complete. + * + * @param pdev - ol_pdev_handle instance + * @param vdev_id - ID of the virtual device that tx frame + * @param had_error - whether there is error when tx + */ +void ol_tx_statistics(struct cdp_cfg *cfg_pdev, + uint16_t vdev_id, int had_error); +#else +#define ol_tx_statistics(pdev, vdev_id, had_error) +#endif + +/** * @brief Count on received packets for invalid peer case + * + * @param pdev - txrx pdev handle + * @param wh - received frame + * @param err_type - what kind of error occurred + */ +void ol_rx_err_inv_peer_statistics(struct cdp_cfg *cfg_pdev, + struct ieee80211_frame *wh, + enum ol_rx_err_type err_type); + +/** + * @brief Count on received packets, both success and failed + * + * @param pdev - ol_pdev_handle handle + * @param vdev_id - ID of the virtual device received the erroneous rx frame + * @param err_type - what kind of error occurred + * @param sec_type - The cipher type the peer is using + * @param is_mcast - whether this is one multi cast frame + */ +void ol_rx_err_statistics(struct cdp_cfg *cfg_pdev, + uint8_t vdev_id, + enum ol_rx_err_type err_type, + enum ol_sec_type sec_type, int is_mcast); + +/** + * @brief Provide notification of failure during host rx processing + * @details + * Indicate an error during host rx data processing, including what + * kind of error happened, when it happened, which peer and TID the + * erroneous rx frame is from, and what the erroneous rx frame itself + * is. + * + * @param pdev - handle to the ctrl SW's physical device object + * @param vdev_id - ID of the virtual device received the erroneous rx frame + * @param peer_mac_addr - MAC address of the peer that sent the erroneous + * rx frame + * @param tid - which TID within the peer sent the erroneous rx frame + * @param tsf32 - the timstamp in TSF units of the erroneous rx frame, or + * one of the fragments that when reassembled, constitute the rx frame + * @param err_type - what kind of error occurred + * @param rx_frame - the rx frame that had an error + * @pn - Packet sequence number + * @key_id - Key index octet received in IV of the frame + */ +void +ol_rx_err(struct cdp_cfg *cfg_pdev, + uint8_t vdev_id, + uint8_t *peer_mac_addr, + int tid, + uint32_t tsf32, + enum ol_rx_err_type err_type, + qdf_nbuf_t rx_frame, uint64_t *pn, uint8_t key_id); + +#ifdef HL_RX_AGGREGATION_HOLE_DETECTION +/** + * ol_rx_aggregation_hole - ol rx aggregation hole report + * @hole_info: hole_info + * + * Return: void + */ +void ol_rx_aggregation_hole(uint32_t hole_info); +#endif + +enum ol_rx_notify_type { + OL_RX_NOTIFY_IPV4_IGMP, +}; + +/** + * @brief Provide notification of reception of data of special interest. + * @details + * Indicate when "special" data has been received. The nature of the + * data that results in it being considered special is specified in the + * notify_type argument. + * This function is currently used by the data-path SW to notify the + * control path SW when the following types of rx data are received: + * + IPv4 IGMP frames + * The control SW can use these to learn about multicast group + * membership, if it so chooses. + * + * @param pdev - handle to the ctrl SW's physical device object + * @param vdev_id - ID of the virtual device received the special data + * @param peer_mac_addr - MAC address of the peer that sent the special data + * @param tid - which TID within the peer sent the special data + * @param tsf32 - the timstamp in TSF units of the special data + * @param notify_type - what kind of special data was received + * @param rx_frame - the rx frame containing the special data + */ +void +ol_rx_notify(struct cdp_cfg *cfg_pdev, + uint8_t vdev_id, + uint8_t *peer_mac_addr, + int tid, + uint32_t tsf32, + enum ol_rx_notify_type notify_type, qdf_nbuf_t rx_frame); + +#define ol_ctrl_addba_req(pdev, peer_mac_addr, tid) ol_addba_req_reject +#define ol_ctrl_rx_addba_complete(pdev, peer_mac_addr, tid, failed) /* no-op */ + +#endif /* _OL_CTRL_TXRX_API__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_osif_txrx_api.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_osif_txrx_api.h new file mode 100644 index 0000000000000000000000000000000000000000..ca40d9ca1c74d6874de2a8786a49806a9cc4e906 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_osif_txrx_api.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011, 2014-2016 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_osif_txrx_api.h + * @brief Define the OS specific API functions called by txrx SW. + */ +#ifndef _OL_OSIF_TXRX_API_H_ +#define _OL_OSIF_TXRX_API_H_ + +#include /* qdf_nbuf_t */ + +/** + * @brief Call tx completion handler to release the buffers + * @details + * + * Invoke tx completion handler when the tx credit goes below low water mark. + * This eliminate the packet drop in the host driver due to send routine not + * yielding the cpu when the amount of traffic pumped from the network layer + * is very high. + * + * @param osdev + */ + +void ol_osif_ath_tasklet(qdf_device_t osdev); + +#endif /* _OL_OSIF_TXRX_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx.c new file mode 100644 index 0000000000000000000000000000000000000000..c31121ed74745c8542c704c6308399593a7f4022 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx.c @@ -0,0 +1,1952 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_cpu_to_le64 */ +#include /* bool */ +#include /* ieee80211_frame */ + +/* external API header files */ +#include /* ol_rx_notify */ +#include /* ol_txrx_pdev_handle */ +#include /* ol_rx_indication_handler */ +#include /* htt_rx_peer_id, etc. */ + +/* internal API header files */ +#include /* ol_txrx_peer_find_by_id */ +#include /* ol_rx_reorder_store, etc. */ +#include /* OL_RX_REORDER_TIMEOUT_UPDATE */ +#include /* ol_rx_defrag_waitlist_flush */ +#include +#include +#include +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP +#include /* ol_rx_decap_info_t, etc */ +#endif +#include + +/* FIX THIS: txrx should not include private header files of other modules */ +#include +#include +#include /* ethernet + SNAP/LLC header defs and + * ethertype values + */ +#include /* IP protocol values */ +#include /* IPv4 header defs */ +#include /* IPv6 header defs */ +#include +#include +#include +#include "pktlog_ac_fmt.h" +#include +#include +#include + +#ifndef OL_RX_INDICATION_MAX_RECORDS +#define OL_RX_INDICATION_MAX_RECORDS 2048 +#endif + +/** + * enum ol_rx_ind_record_type - OL rx indication events + * @OL_RX_INDICATION_POP_START: event recorded before netbuf pop + * @OL_RX_INDICATION_POP_END: event recorded after netbuf pop + * @OL_RX_INDICATION_BUF_REPLENISH: event recorded after buffer replenishment + */ +enum ol_rx_ind_record_type { + OL_RX_INDICATION_POP_START, + OL_RX_INDICATION_POP_END, + OL_RX_INDICATION_BUF_REPLENISH, +}; + +/** + * struct ol_rx_ind_record - structure for detailing ol txrx rx ind. event + * @value: info corresponding to rx indication event + * @type: what the event was + * @time: when it happened + */ +struct ol_rx_ind_record { + uint16_t value; + enum ol_rx_ind_record_type type; + uint64_t time; +}; + +#ifdef OL_RX_INDICATION_RECORD +static uint32_t ol_rx_ind_record_index; +struct ol_rx_ind_record + ol_rx_indication_record_history[OL_RX_INDICATION_MAX_RECORDS]; + +/** + * ol_rx_ind_record_event() - record ol rx indication events + * @value: contains rx ind. event related info + * @type: ol rx indication message type + * + * This API record the ol rx indiation event in a rx indication + * record buffer. + * + * Return: None + */ +static void ol_rx_ind_record_event(uint32_t value, + enum ol_rx_ind_record_type type) +{ + ol_rx_indication_record_history[ol_rx_ind_record_index].value = value; + ol_rx_indication_record_history[ol_rx_ind_record_index].type = type; + ol_rx_indication_record_history[ol_rx_ind_record_index].time = + qdf_get_log_timestamp(); + + ol_rx_ind_record_index++; + if (ol_rx_ind_record_index >= OL_RX_INDICATION_MAX_RECORDS) + ol_rx_ind_record_index = 0; +} +#else +static inline +void ol_rx_ind_record_event(uint32_t value, enum ol_rx_ind_record_type type) +{ +} + +#endif /* OL_RX_INDICATION_RECORD */ + +void ol_rx_data_process(struct ol_txrx_peer_t *peer, + qdf_nbuf_t rx_buf_list); + +#ifdef WDI_EVENT_ENABLE +/** + * ol_rx_send_pktlog_event() - send rx packetlog event + * @pdev: pdev handle + * @peer: peer handle + * @msdu: skb list + * @pktlog_bit: packetlog bit from firmware + * + * Return: none + */ +#ifdef HELIUMPLUS +void ol_rx_send_pktlog_event(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, uint8_t pktlog_bit) +{ + struct ol_rx_remote_data data; + + /** + * pktlog is meant to log rx_desc information which is + * already overwritten by radio header when monitor mode is ON. + * Therefore, Do not log pktlog event when monitor mode is ON. + */ + if (!pktlog_bit || (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE)) + return; + + data.msdu = msdu; + if (peer) + data.mac_id = peer->vdev->mac_id; + else + data.mac_id = 0; + + wdi_event_handler(WDI_EVENT_RX_DESC_REMOTE, pdev->id, + &data); +} +#else +void ol_rx_send_pktlog_event(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, uint8_t pktlog_bit) +{ + struct ol_rx_remote_data data; + + /** + * pktlog is meant to log rx_desc information which is + * already overwritten by radio header when monitor mode is ON. + * Therefore, Do not log pktlog event when monitor mode is ON. + */ + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + return; + + data.msdu = msdu; + if (peer) + data.mac_id = peer->vdev->mac_id; + else + data.mac_id = 0; + + wdi_event_handler(WDI_EVENT_RX_DESC_REMOTE, pdev->id, + &data); +} +#endif +#endif /* WDI_EVENT_ENABLE */ + +#ifdef HTT_RX_RESTORE + +static void ol_rx_restore_handler(struct work_struct *htt_rx) +{ + qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "Enter: %s", __func__); + pld_device_self_recovery(qdf_ctx->dev); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "Exit: %s", __func__); +} + +static DECLARE_WORK(ol_rx_restore_work, ol_rx_restore_handler); + +void ol_rx_trigger_restore(htt_pdev_handle htt_pdev, qdf_nbuf_t head_msdu, + qdf_nbuf_t tail_msdu) +{ + qdf_nbuf_t next; + + while (head_msdu) { + next = qdf_nbuf_next(head_msdu); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "freeing %pK\n", head_msdu); + qdf_nbuf_free(head_msdu); + head_msdu = next; + } + + if (!htt_pdev->rx_ring.htt_rx_restore) { + cds_set_recovery_in_progress(true); + htt_pdev->rx_ring.htt_rx_restore = 1; + schedule_work(&ol_rx_restore_work); + } +} +#endif + +/** + * ol_rx_update_histogram_stats() - update rx histogram statistics + * @msdu_count: msdu count + * @frag_ind: fragment indication set + * @offload_ind: offload indication set + * + * Return: none + */ +void ol_rx_update_histogram_stats(uint32_t msdu_count, uint8_t frag_ind, + uint8_t offload_ind) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + if (msdu_count > 60) { + TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_61_plus, 1); + } else if (msdu_count > 50) { + TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_51_60, 1); + } else if (msdu_count > 40) { + TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_41_50, 1); + } else if (msdu_count > 30) { + TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_31_40, 1); + } else if (msdu_count > 20) { + TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_21_30, 1); + } else if (msdu_count > 10) { + TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_11_20, 1); + } else if (msdu_count > 1) { + TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_2_10, 1); + } else if (msdu_count == 1) { + TXRX_STATS_ADD(pdev, pub.rx.rx_ind_histogram.pkts_1, 1); + } + + if (frag_ind) + TXRX_STATS_ADD(pdev, pub.rx.msdus_with_frag_ind, msdu_count); + + if (offload_ind) + TXRX_STATS_ADD(pdev, pub.rx.msdus_with_offload_ind, msdu_count); + +} + +#ifdef WLAN_PARTIAL_REORDER_OFFLOAD + +#ifdef WDI_EVENT_ENABLE +static void ol_rx_process_inv_peer(ol_txrx_pdev_handle pdev, + void *rx_mpdu_desc, qdf_nbuf_t msdu) +{ + uint8_t a1[QDF_MAC_ADDR_SIZE]; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + struct ol_txrx_vdev_t *vdev = NULL; + struct ieee80211_frame *wh; + struct wdi_event_rx_peer_invalid_msg msg; + + wh = (struct ieee80211_frame *) + htt_rx_mpdu_wifi_hdr_retrieve(htt_pdev, rx_mpdu_desc); + /* + * Klocwork issue #6152 + * All targets that send a "INVALID_PEER" rx status provide a + * 802.11 header for each rx MPDU, so it is certain that + * htt_rx_mpdu_wifi_hdr_retrieve will succeed. + * However, both for robustness, e.g. if this function is given a + * MSDU descriptor rather than a MPDU descriptor, and to make it + * clear to static analysis that this code is safe, add an explicit + * check that htt_rx_mpdu_wifi_hdr_retrieve provides a non-NULL value. + */ + if (!wh || !IEEE80211_IS_DATA(wh)) + return; + + /* ignore frames for non-existent bssids */ + qdf_mem_copy(a1, wh->i_addr1, QDF_MAC_ADDR_SIZE); + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + if (qdf_mem_cmp(a1, vdev->mac_addr.raw, QDF_MAC_ADDR_SIZE)) + break; + } + if (!vdev) + return; + + msg.wh = wh; + msg.msdu = msdu; + msg.vdev_id = vdev->vdev_id; + wdi_event_handler(WDI_EVENT_RX_PEER_INVALID, pdev->id, + &msg); +} +#else +static inline +void ol_rx_process_inv_peer(ol_txrx_pdev_handle pdev, + void *rx_mpdu_desc, qdf_nbuf_t msdu) +{ +} +#endif + +#ifdef QCA_SUPPORT_PEER_DATA_RX_RSSI +static inline int16_t +ol_rx_rssi_avg(struct ol_txrx_pdev_t *pdev, int16_t rssi_old, int16_t rssi_new) +{ + int rssi_old_weight; + + if (rssi_new == HTT_RSSI_INVALID) + return rssi_old; + if (rssi_old == HTT_RSSI_INVALID) + return rssi_new; + + rssi_old_weight = + (1 << pdev->rssi_update_shift) - pdev->rssi_new_weight; + return (rssi_new * pdev->rssi_new_weight + + rssi_old * rssi_old_weight) >> pdev->rssi_update_shift; +} + +static void +ol_rx_ind_rssi_update(struct ol_txrx_peer_t *peer, qdf_nbuf_t rx_ind_msg) +{ + struct ol_txrx_pdev_t *pdev = peer->vdev->pdev; + + peer->rssi_dbm = ol_rx_rssi_avg(pdev, peer->rssi_dbm, + htt_rx_ind_rssi_dbm(pdev->htt_pdev, + rx_ind_msg)); +} + +static void +ol_rx_mpdu_rssi_update(struct ol_txrx_peer_t *peer, void *rx_mpdu_desc) +{ + struct ol_txrx_pdev_t *pdev = peer->vdev->pdev; + + if (!peer) + return; + peer->rssi_dbm = ol_rx_rssi_avg(pdev, peer->rssi_dbm, + htt_rx_mpdu_desc_rssi_dbm( + pdev->htt_pdev, + rx_mpdu_desc)); +} + +#else +#define ol_rx_ind_rssi_update(peer, rx_ind_msg) /* no-op */ +#define ol_rx_mpdu_rssi_update(peer, rx_mpdu_desc) /* no-op */ +#endif /* QCA_SUPPORT_PEER_DATA_RX_RSSI */ + +static void discard_msdus(htt_pdev_handle htt_pdev, + qdf_nbuf_t head_msdu, + qdf_nbuf_t tail_msdu) +{ + while (1) { + qdf_nbuf_t next; + + next = qdf_nbuf_next( + head_msdu); + htt_rx_desc_frame_free + (htt_pdev, + head_msdu); + if (head_msdu == + tail_msdu) { + break; + } + head_msdu = next; + } +} + +static void chain_msdus(htt_pdev_handle htt_pdev, + qdf_nbuf_t head_msdu, + qdf_nbuf_t tail_msdu) +{ + while (1) { + qdf_nbuf_t next; + + next = qdf_nbuf_next(head_msdu); + htt_rx_desc_frame_free( + htt_pdev, + head_msdu); + if (head_msdu == tail_msdu) + break; + head_msdu = next; + } +} + +static void process_reorder(ol_txrx_pdev_handle pdev, + void *rx_mpdu_desc, + uint8_t tid, + struct ol_txrx_peer_t *peer, + qdf_nbuf_t head_msdu, + qdf_nbuf_t tail_msdu, + int num_mpdu_ranges, + int num_mpdus, + bool rx_ind_release) +{ + htt_pdev_handle htt_pdev = pdev->htt_pdev; + enum htt_rx_status mpdu_status; + int reorder_idx; + + reorder_idx = htt_rx_mpdu_desc_reorder_idx(htt_pdev, rx_mpdu_desc); + OL_RX_REORDER_TRACE_ADD(pdev, tid, + reorder_idx, + htt_rx_mpdu_desc_seq_num(htt_pdev, + rx_mpdu_desc), + 1); + ol_rx_mpdu_rssi_update(peer, rx_mpdu_desc); + /* + * In most cases, out-of-bounds and duplicate sequence number detection + * is performed by the target, but in some cases it is done by the host. + * Specifically, the host does rx out-of-bounds sequence number + * detection for: + * 1. Peregrine or Rome target + * for peer-TIDs that do not have aggregation enabled, if the + * RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK flag + * is set during the driver build. + * 2. Riva-family targets, which have rx reorder timeouts handled by + * the host rather than the target. + * (The target already does duplicate detection, but the host + * may have given up waiting for a particular sequence number before + * it arrives. In this case, the out-of-bounds sequence number + * of the late frame allows the host to discard it, rather than + * sending it out of order. + */ + mpdu_status = OL_RX_SEQ_NUM_CHECK(pdev, + peer, + tid, + rx_mpdu_desc); + if (mpdu_status != htt_rx_status_ok) { + /* + * If the sequence number was out of bounds, the MPDU needs + * to be discarded. + */ + discard_msdus(htt_pdev, head_msdu, tail_msdu); + /* + * For Peregrine and Rome, + * OL_RX_REORDER_SEQ_NUM_CHECK should only fail for the case + * of (duplicate) non-aggregates. + * + * For Riva, Pronto and Northstar, + * there should be only one MPDU delivered at a time. + * Thus, there are no further MPDUs that need to be + * processed here. + * Just to be sure this is true, check the assumption + * that this was the only MPDU referenced by the rx + * indication. + */ + TXRX_ASSERT2((num_mpdu_ranges == 1) && num_mpdus == 1); + + /* + * The MPDU was not stored in the rx reorder array, so + * there's nothing to release. + */ + rx_ind_release = false; + } else { + ol_rx_reorder_store(pdev, peer, tid, + reorder_idx, head_msdu, tail_msdu); + if (peer->tids_rx_reorder[tid].win_sz_mask == 0) { + peer->tids_last_seq[tid] = htt_rx_mpdu_desc_seq_num( + htt_pdev, + rx_mpdu_desc); + } + } +} /* process_reorder */ + +#ifdef WLAN_FEATURE_DSRC +static void +ol_rx_ocb_update_peer(ol_txrx_pdev_handle pdev, qdf_nbuf_t rx_ind_msg, + struct ol_txrx_peer_t *peer) +{ + int i; + + htt_rx_ind_legacy_rate(pdev->htt_pdev, rx_ind_msg, + &peer->last_pkt_legacy_rate, + &peer->last_pkt_legacy_rate_sel); + peer->last_pkt_rssi_cmb = htt_rx_ind_rssi_dbm( + pdev->htt_pdev, rx_ind_msg); + for (i = 0; i < 4; i++) + peer->last_pkt_rssi[i] = + htt_rx_ind_rssi_dbm_chain(pdev->htt_pdev, rx_ind_msg, i); + + htt_rx_ind_timestamp(pdev->htt_pdev, rx_ind_msg, + &peer->last_pkt_timestamp_microsec, + &peer->last_pkt_timestamp_submicrosec); + peer->last_pkt_tsf = htt_rx_ind_tsf32(pdev->htt_pdev, rx_ind_msg); + peer->last_pkt_tid = htt_rx_ind_ext_tid(pdev->htt_pdev, rx_ind_msg); +} +#else +static void +ol_rx_ocb_update_peer(ol_txrx_pdev_handle pdev, qdf_nbuf_t rx_ind_msg, + struct ol_txrx_peer_t *peer) +{ +} +#endif + +void +ol_rx_indication_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + uint16_t peer_id, uint8_t tid, int num_mpdu_ranges) +{ + int mpdu_range; + unsigned int seq_num_start = 0, seq_num_end = 0; + bool rx_ind_release = false; + struct ol_txrx_vdev_t *vdev = NULL; + struct ol_txrx_peer_t *peer; + htt_pdev_handle htt_pdev; + uint16_t center_freq; + uint16_t chan1; + uint16_t chan2; + uint8_t phymode; + bool ret; + uint32_t msdu_count = 0; + + htt_pdev = pdev->htt_pdev; + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (!peer) { + /* + * If we can't find a peer send this packet to OCB interface + * using OCB self peer + */ + if (!ol_txrx_get_ocb_peer(pdev, &peer)) + peer = NULL; + } + + if (peer) { + vdev = peer->vdev; + ol_rx_ind_rssi_update(peer, rx_ind_msg); + + if (vdev->opmode == wlan_op_mode_ocb) + ol_rx_ocb_update_peer(pdev, rx_ind_msg, peer); + } + + TXRX_STATS_INCR(pdev, priv.rx.normal.ppdus); + + OL_RX_REORDER_TIMEOUT_MUTEX_LOCK(pdev); + + if (htt_rx_ind_flush(pdev->htt_pdev, rx_ind_msg) && peer) { + htt_rx_ind_flush_seq_num_range(pdev->htt_pdev, rx_ind_msg, + &seq_num_start, &seq_num_end); + if (tid == HTT_INVALID_TID) { + /* + * host/FW reorder state went out-of sync + * for a while because FW ran out of Rx indication + * buffer. We have to discard all the buffers in + * reorder queue. + */ + ol_rx_reorder_peer_cleanup(vdev, peer); + } else { + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + ol_txrx_err("invalid tid, %u", tid); + WARN_ON(1); + return; + } + ol_rx_reorder_flush(vdev, peer, tid, seq_num_start, + seq_num_end, htt_rx_flush_release); + } + } + + if (htt_rx_ind_release(pdev->htt_pdev, rx_ind_msg)) { + /* + * The ind info of release is saved here and do release at the + * end. This is for the reason of in HL case, the qdf_nbuf_t + * for msg and payload are the same buf. And the buf will be + * changed during processing + */ + rx_ind_release = true; + htt_rx_ind_release_seq_num_range(pdev->htt_pdev, rx_ind_msg, + &seq_num_start, &seq_num_end); + } +#ifdef DEBUG_DMA_DONE + pdev->htt_pdev->rx_ring.dbg_initial_msdu_payld = + pdev->htt_pdev->rx_ring.sw_rd_idx.msdu_payld; +#endif + + for (mpdu_range = 0; mpdu_range < num_mpdu_ranges; mpdu_range++) { + enum htt_rx_status status; + int i, num_mpdus; + qdf_nbuf_t head_msdu, tail_msdu, msdu; + void *rx_mpdu_desc; + +#ifdef DEBUG_DMA_DONE + pdev->htt_pdev->rx_ring.dbg_mpdu_range = mpdu_range; +#endif + + htt_rx_ind_mpdu_range_info(pdev->htt_pdev, rx_ind_msg, + mpdu_range, &status, &num_mpdus); + if ((status == htt_rx_status_ok) && peer) { + TXRX_STATS_ADD(pdev, priv.rx.normal.mpdus, num_mpdus); + /* valid frame - deposit it into rx reordering buffer */ + for (i = 0; i < num_mpdus; i++) { + int msdu_chaining; + /* + * Get a linked list of the MSDUs that comprise + * this MPDU. + * This also attaches each rx MSDU descriptor to + * the corresponding rx MSDU network buffer. + * (In some systems, the rx MSDU desc is already + * in the same buffer as the MSDU payload; in + * other systems they are separate, so a pointer + * needs to be set in the netbuf to locate the + * corresponding rx descriptor.) + * + * It is necessary to call htt_rx_amsdu_pop + * before htt_rx_mpdu_desc_list_next, because + * the (MPDU) rx descriptor has DMA unmapping + * done during the htt_rx_amsdu_pop call. + * The rx desc should not be accessed until this + * DMA unmapping has been done, since the DMA + * unmapping involves making sure the cache area + * for the mapped buffer is flushed, so the data + * written by the MAC DMA into memory will be + * fetched, rather than garbage from the cache. + */ + +#ifdef DEBUG_DMA_DONE + pdev->htt_pdev->rx_ring.dbg_mpdu_count = i; +#endif + + msdu_chaining = + htt_rx_amsdu_pop(htt_pdev, + rx_ind_msg, + &head_msdu, + &tail_msdu, + &msdu_count); +#ifdef HTT_RX_RESTORE + if (htt_pdev->rx_ring.rx_reset) { + ol_rx_trigger_restore(htt_pdev, + head_msdu, + tail_msdu); + OL_RX_REORDER_TIMEOUT_MUTEX_UNLOCK( + pdev); + return; + } +#endif + rx_mpdu_desc = + htt_rx_mpdu_desc_list_next(htt_pdev, + rx_ind_msg); + ret = htt_rx_msdu_center_freq(htt_pdev, peer, + rx_mpdu_desc, ¢er_freq, &chan1, + &chan2, &phymode); + if (ret == true) { + peer->last_pkt_center_freq = + center_freq; + } else { + peer->last_pkt_center_freq = 0; + } + + /* Pktlog */ + ol_rx_send_pktlog_event(pdev, peer, + head_msdu, 1); + + if (msdu_chaining) { + /* + * TBDXXX - to deliver SDU with + * chaining, we need to stitch those + * scattered buffers into one single + * buffer. + * Just discard it now. + */ + chain_msdus(htt_pdev, + head_msdu, + tail_msdu); + } else { + process_reorder(pdev, rx_mpdu_desc, + tid, peer, + head_msdu, tail_msdu, + num_mpdu_ranges, + num_mpdus, + rx_ind_release); + } + + } + } else { + /* invalid frames - discard them */ + OL_RX_REORDER_TRACE_ADD(pdev, tid, + TXRX_SEQ_NUM_ERR(status), + TXRX_SEQ_NUM_ERR(status), + num_mpdus); + TXRX_STATS_ADD(pdev, priv.rx.err.mpdu_bad, num_mpdus); + for (i = 0; i < num_mpdus; i++) { + /* pull the MPDU's MSDUs off the buffer queue */ + htt_rx_amsdu_pop(htt_pdev, rx_ind_msg, &msdu, + &tail_msdu, &msdu_count); +#ifdef HTT_RX_RESTORE + if (htt_pdev->rx_ring.rx_reset) { + ol_rx_trigger_restore(htt_pdev, msdu, + tail_msdu); + OL_RX_REORDER_TIMEOUT_MUTEX_UNLOCK( + pdev); + return; + } +#endif + /* pull the MPDU desc off the desc queue */ + rx_mpdu_desc = + htt_rx_mpdu_desc_list_next(htt_pdev, + rx_ind_msg); + OL_RX_ERR_STATISTICS_2(pdev, vdev, peer, + rx_mpdu_desc, msdu, + status); + + if (status == htt_rx_status_tkip_mic_err && + vdev && peer) { + union htt_rx_pn_t pn; + uint8_t key_id; + + htt_rx_mpdu_desc_pn( + pdev->htt_pdev, + htt_rx_msdu_desc_retrieve( + pdev->htt_pdev, + msdu), &pn, 48); + if (htt_rx_msdu_desc_key_id( + pdev->htt_pdev, + htt_rx_msdu_desc_retrieve( + pdev->htt_pdev, + msdu), + &key_id) == true) { + ol_rx_send_mic_err_ind( + vdev->pdev, + vdev->vdev_id, + peer->mac_addr.raw, + tid, 0, + OL_RX_ERR_TKIP_MIC, + msdu, &pn.pn48, + key_id); + } + } + + if (status != htt_rx_status_ctrl_mgmt_null) { + /* Pktlog */ + ol_rx_send_pktlog_event(pdev, + peer, msdu, 1); + } + + if (status == htt_rx_status_err_inv_peer) { + /* once per mpdu */ + ol_rx_process_inv_peer(pdev, + rx_mpdu_desc, + msdu); + } + + while (1) { + /* Free the nbuf */ + qdf_nbuf_t next; + + next = qdf_nbuf_next(msdu); + htt_rx_desc_frame_free(htt_pdev, msdu); + if (msdu == tail_msdu) + break; + msdu = next; + } + } + } + } + /* + * Now that a whole batch of MSDUs have been pulled out of HTT + * and put into the rx reorder array, it is an appropriate time + * to request HTT to provide new rx MSDU buffers for the target + * to fill. + * This could be done after the end of this function, but it's + * better to do it now, rather than waiting until after the driver + * and OS finish processing the batch of rx MSDUs. + */ + htt_rx_msdu_buff_replenish(htt_pdev); + + if ((true == rx_ind_release) && peer && vdev) { + ol_rx_reorder_release(vdev, peer, tid, seq_num_start, + seq_num_end); + } + OL_RX_REORDER_TIMEOUT_UPDATE(peer, tid); + OL_RX_REORDER_TIMEOUT_MUTEX_UNLOCK(pdev); + + if (pdev->rx.flags.defrag_timeout_check) + ol_rx_defrag_waitlist_flush(pdev); +} +#endif + +void +ol_rx_sec_ind_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + enum htt_sec_type sec_type, + int is_unicast, uint32_t *michael_key, uint32_t *rx_pn) +{ + struct ol_txrx_peer_t *peer; + int sec_index, i; + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (!peer) { + ol_txrx_err( + "Couldn't find peer from ID %d - skipping security inits\n", + peer_id); + return; + } + ol_txrx_dbg( + "sec spec for peer %pK ("QDF_MAC_ADDR_FMT"): %s key of type %d\n", + peer, + QDF_MAC_ADDR_REF(peer->mac_addr.raw), + is_unicast ? "ucast" : "mcast", sec_type); + sec_index = is_unicast ? txrx_sec_ucast : txrx_sec_mcast; + peer->security[sec_index].sec_type = sec_type; + /* + * michael key only valid for TKIP + * but for simplicity, copy it anyway + */ + qdf_mem_copy(&peer->security[sec_index].michael_key[0], + michael_key, + sizeof(peer->security[sec_index].michael_key)); + + if (sec_type != htt_sec_type_wapi) { + qdf_mem_zero(peer->tids_last_pn_valid, + OL_TXRX_NUM_EXT_TIDS); + } else if (sec_index == txrx_sec_mcast || peer->tids_last_pn_valid[0]) { + for (i = 0; i < OL_TXRX_NUM_EXT_TIDS; i++) { + /* + * Setting PN valid bit for WAPI sec_type, + * since WAPI PN has to be started with predefined value + */ + peer->tids_last_pn_valid[i] = 1; + qdf_mem_copy((uint8_t *) &peer->tids_last_pn[i], + (uint8_t *) rx_pn, + sizeof(union htt_rx_pn_t)); + peer->tids_last_pn[i].pn128[1] = + qdf_cpu_to_le64( + peer->tids_last_pn[i].pn128[1]); + peer->tids_last_pn[i].pn128[0] = + qdf_cpu_to_le64( + peer->tids_last_pn[i].pn128[0]); + if (sec_index == txrx_sec_ucast) + peer->tids_rekey_flag[i] = 1; + } + } +} + +void ol_rx_notify(struct cdp_cfg *cfg_pdev, + uint8_t vdev_id, + uint8_t *peer_mac_addr, + int tid, + uint32_t tsf32, + enum ol_rx_notify_type notify_type, qdf_nbuf_t rx_frame) +{ + /* + * NOTE: This is used in qca_main for AP mode to handle IGMP + * packets specially. Umac has a corresponding handler for this + * not sure if we need to have this for CLD as well. + */ +} + +#ifdef WLAN_PARTIAL_REORDER_OFFLOAD +/** + * @brief Look into a rx MSDU to see what kind of special handling it requires + * @details + * This function is called when the host rx SW sees that the target + * rx FW has marked a rx MSDU as needing inspection. + * Based on the results of the inspection, the host rx SW will infer + * what special handling to perform on the rx frame. + * Currently, the only type of frames that require special handling + * are IGMP frames. The rx data-path SW checks if the frame is IGMP + * (it should be, since the target would not have set the inspect flag + * otherwise), and then calls the ol_rx_notify function so the + * control-path SW can perform multicast group membership learning + * by sniffing the IGMP frame. + */ +#define SIZEOF_80211_HDR (sizeof(struct ieee80211_frame)) +static void +ol_rx_inspect(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu, void *rx_desc) +{ + ol_txrx_pdev_handle pdev = vdev->pdev; + uint8_t *data, *l3_hdr; + uint16_t ethertype; + int offset; + + data = qdf_nbuf_data(msdu); + if (pdev->frame_format == wlan_frm_fmt_native_wifi) { + offset = SIZEOF_80211_HDR + LLC_SNAP_HDR_OFFSET_ETHERTYPE; + l3_hdr = data + SIZEOF_80211_HDR + LLC_SNAP_HDR_LEN; + } else { + offset = QDF_MAC_ADDR_SIZE * 2; + l3_hdr = data + ETHERNET_HDR_LEN; + } + ethertype = (data[offset] << 8) | data[offset + 1]; + if (ethertype == ETHERTYPE_IPV4) { + offset = IPV4_HDR_OFFSET_PROTOCOL; + if (l3_hdr[offset] == IP_PROTOCOL_IGMP) { + ol_rx_notify(pdev->ctrl_pdev, + vdev->vdev_id, + peer->mac_addr.raw, + tid, + htt_rx_mpdu_desc_tsf32(pdev->htt_pdev, + rx_desc), + OL_RX_NOTIFY_IPV4_IGMP, msdu); + } + } +} +#endif + +void +ol_rx_offload_deliver_ind_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t msg, uint16_t msdu_cnt) +{ + int vdev_id, peer_id, tid; + qdf_nbuf_t head_buf, tail_buf, buf; + struct ol_txrx_peer_t *peer; + uint8_t fw_desc; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + + if (msdu_cnt > htt_rx_offload_msdu_cnt(htt_pdev)) { + ol_txrx_err("invalid msdu_cnt=%u", msdu_cnt); + + if (pdev->cfg.is_high_latency) + htt_rx_desc_frame_free(htt_pdev, msg); + + return; + } + + while (msdu_cnt) { + if (!htt_rx_offload_msdu_pop(htt_pdev, msg, &vdev_id, &peer_id, + &tid, &fw_desc, &head_buf, &tail_buf)) { + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (peer) { + ol_rx_data_process(peer, head_buf); + } else { + buf = head_buf; + while (1) { + qdf_nbuf_t next; + + next = qdf_nbuf_next(buf); + htt_rx_desc_frame_free(htt_pdev, buf); + if (buf == tail_buf) + break; + buf = next; + } + } + } + msdu_cnt--; + } + htt_rx_msdu_buff_replenish(htt_pdev); +} + +void +ol_rx_send_mic_err_ind(struct ol_txrx_pdev_t *pdev, uint8_t vdev_id, + uint8_t *peer_mac_addr, int tid, uint32_t tsf32, + enum ol_rx_err_type err_type, qdf_nbuf_t rx_frame, + uint64_t *pn, uint8_t key_id) +{ + struct cdp_rx_mic_err_info mic_failure_info; + qdf_ether_header_t *eth_hdr; + struct ol_if_ops *tops = NULL; + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_soc_handle ol_txrx_soc = &soc->cdp_soc; + + if (err_type != OL_RX_ERR_TKIP_MIC) + return; + + if (qdf_nbuf_len(rx_frame) < sizeof(*eth_hdr)) + return; + + eth_hdr = (qdf_ether_header_t *)qdf_nbuf_data(rx_frame); + + qdf_copy_macaddr((struct qdf_mac_addr *)&mic_failure_info.ta_mac_addr, + (struct qdf_mac_addr *)peer_mac_addr); + qdf_copy_macaddr((struct qdf_mac_addr *)&mic_failure_info.da_mac_addr, + (struct qdf_mac_addr *)eth_hdr->ether_dhost); + mic_failure_info.key_id = key_id; + mic_failure_info.multicast = + IEEE80211_IS_MULTICAST(eth_hdr->ether_dhost); + qdf_mem_copy(mic_failure_info.tsc, pn, SIR_CIPHER_SEQ_CTR_SIZE); + mic_failure_info.frame_type = cdp_rx_frame_type_802_3; + mic_failure_info.data = NULL; + mic_failure_info.vdev_id = vdev_id; + + tops = ol_txrx_soc->ol_ops; + if (tops->rx_mic_error) + tops->rx_mic_error(soc->psoc, pdev->id, &mic_failure_info); +} + +void +ol_rx_mic_error_handler( + ol_txrx_pdev_handle pdev, + u_int8_t tid, + u_int16_t peer_id, + void *msdu_desc, + qdf_nbuf_t msdu) +{ + union htt_rx_pn_t pn = {0}; + u_int8_t key_id = 0; + + struct ol_txrx_peer_t *peer = NULL; + struct ol_txrx_vdev_t *vdev = NULL; + + if (pdev) { + TXRX_STATS_MSDU_INCR(pdev, rx.dropped_mic_err, msdu); + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (peer) { + vdev = peer->vdev; + if (vdev) { + htt_rx_mpdu_desc_pn(vdev->pdev->htt_pdev, + msdu_desc, &pn, 48); + + if (htt_rx_msdu_desc_key_id( + vdev->pdev->htt_pdev, msdu_desc, + &key_id) == true) { + ol_rx_send_mic_err_ind(vdev->pdev, + vdev->vdev_id, + peer->mac_addr.raw, tid, 0, + OL_RX_ERR_TKIP_MIC, msdu, + &pn.pn48, key_id); + } + } + } + /* Pktlog */ + ol_rx_send_pktlog_event(pdev, peer, msdu, 1); + } +} + +#ifdef WLAN_PARTIAL_REORDER_OFFLOAD +/** + * @brief Check the first msdu to decide whether the a-msdu should be accepted. + */ +static bool +ol_rx_filter(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, void *rx_desc) +{ +#define FILTER_STATUS_REJECT 1 +#define FILTER_STATUS_ACCEPT 0 + uint8_t *wh; + uint32_t offset = 0; + uint16_t ether_type = 0; + bool is_encrypted = false, is_mcast = false; + uint8_t i; + enum privacy_filter_packet_type packet_type = + PRIVACY_FILTER_PACKET_UNICAST; + ol_txrx_pdev_handle pdev = vdev->pdev; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + int sec_idx; + + /* + * Safemode must avoid the PrivacyExemptionList and + * ExcludeUnencrypted checking + */ + if (vdev->safemode) + return FILTER_STATUS_ACCEPT; + + is_mcast = htt_rx_msdu_is_wlan_mcast(htt_pdev, rx_desc); + if (vdev->num_filters > 0) { + if (pdev->frame_format == wlan_frm_fmt_native_wifi) { + offset = SIZEOF_80211_HDR + + LLC_SNAP_HDR_OFFSET_ETHERTYPE; + } else { + offset = QDF_MAC_ADDR_SIZE * 2; + } + /* get header info from msdu */ + wh = qdf_nbuf_data(msdu); + + /* get ether type */ + ether_type = (wh[offset] << 8) | wh[offset + 1]; + /* get packet type */ + if (true == is_mcast) + packet_type = PRIVACY_FILTER_PACKET_MULTICAST; + else + packet_type = PRIVACY_FILTER_PACKET_UNICAST; + } + /* get encrypt info */ + is_encrypted = htt_rx_mpdu_is_encrypted(htt_pdev, rx_desc); +#ifdef ATH_SUPPORT_WAPI + if ((true == is_encrypted) && (ETHERTYPE_WAI == ether_type)) { + /* + * We expect the WAI frames to be always unencrypted when + * the UMAC gets it + */ + return FILTER_STATUS_REJECT; + } +#endif /* ATH_SUPPORT_WAPI */ + + for (i = 0; i < vdev->num_filters; i++) { + enum privacy_filter filter_type; + enum privacy_filter_packet_type filter_packet_type; + + /* skip if the ether type does not match */ + if (vdev->privacy_filters[i].ether_type != ether_type) + continue; + + /* skip if the packet type does not match */ + filter_packet_type = vdev->privacy_filters[i].packet_type; + if (filter_packet_type != packet_type && + filter_packet_type != PRIVACY_FILTER_PACKET_BOTH) { + continue; + } + + filter_type = vdev->privacy_filters[i].filter_type; + if (filter_type == PRIVACY_FILTER_ALWAYS) { + /* + * In this case, we accept the frame if and only if + * it was originally NOT encrypted. + */ + if (true == is_encrypted) + return FILTER_STATUS_REJECT; + else + return FILTER_STATUS_ACCEPT; + + } else if (filter_type == PRIVACY_FILTER_KEY_UNAVAILABLE) { + /* + * In this case, we reject the frame if it was + * originally NOT encrypted but we have the key mapping + * key for this frame. + */ + if (!is_encrypted && + !is_mcast && + (peer->security[txrx_sec_ucast].sec_type != + htt_sec_type_none) && + (peer->keyinstalled || !ETHERTYPE_IS_EAPOL_WAPI( + ether_type))) { + return FILTER_STATUS_REJECT; + } else { + return FILTER_STATUS_ACCEPT; + } + } else { + /* + * The privacy exemption does not apply to this frame. + */ + break; + } + } + + /* + * If the privacy exemption list does not apply to the frame, + * check ExcludeUnencrypted. + * If ExcludeUnencrypted is not set, or if this was oringially + * an encrypted frame, it will be accepted. + */ + if (!vdev->drop_unenc || (true == is_encrypted)) + return FILTER_STATUS_ACCEPT; + + /* + * If this is a open connection, it will be accepted. + */ + sec_idx = (true == is_mcast) ? txrx_sec_mcast : txrx_sec_ucast; + if (peer->security[sec_idx].sec_type == htt_sec_type_none) + return FILTER_STATUS_ACCEPT; + + if ((false == is_encrypted) && vdev->drop_unenc) { + OL_RX_ERR_STATISTICS(pdev, vdev, OL_RX_ERR_PRIVACY, + pdev->sec_types[htt_sec_type_none], + is_mcast); + } + return FILTER_STATUS_REJECT; +} +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS +#ifdef CONFIG_HL_SUPPORT +void ol_rx_timestamp(struct cdp_cfg *cfg_pdev, + void *rx_desc, qdf_nbuf_t msdu) +{ + struct htt_rx_ppdu_desc_t *rx_ppdu_desc; + + if (!ol_cfg_is_ptp_rx_opt_enabled(cfg_pdev)) + return; + + if (!rx_desc || !msdu) + return; + + rx_ppdu_desc = (struct htt_rx_ppdu_desc_t *)((uint8_t *)(rx_desc) - + HTT_RX_IND_HL_BYTES + HTT_RX_IND_HDR_PREFIX_BYTES); + msdu->tstamp = ns_to_ktime((u_int64_t)rx_ppdu_desc->tsf32 * + NSEC_PER_USEC); +} + +static inline void ol_rx_timestamp_update(ol_txrx_pdev_handle pdev, + qdf_nbuf_t head_msdu, + qdf_nbuf_t tail_msdu) +{ + qdf_nbuf_t loop_msdu; + struct htt_host_rx_desc_base *rx_desc; + + loop_msdu = head_msdu; + while (loop_msdu) { + rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, loop_msdu); + ol_rx_timestamp(pdev->ctrl_pdev, rx_desc, loop_msdu); + loop_msdu = qdf_nbuf_next(loop_msdu); + } +} +#else +void ol_rx_timestamp(struct cdp_cfg *cfg_pdev, + void *rx_desc, qdf_nbuf_t msdu) +{ + struct htt_host_rx_desc_base *rx_mpdu_desc = rx_desc; + uint32_t tsf64_low32, tsf64_high32; + uint64_t tsf64, tsf64_ns; + + if (!ol_cfg_is_ptp_rx_opt_enabled(cfg_pdev)) + return; + + if (!rx_mpdu_desc || !msdu) + return; + + tsf64_low32 = rx_mpdu_desc->ppdu_end.wb_timestamp_lower_32; + tsf64_high32 = rx_mpdu_desc->ppdu_end.wb_timestamp_upper_32; + + tsf64 = (uint64_t)tsf64_high32 << 32 | tsf64_low32; + if (tsf64 * NSEC_PER_USEC < tsf64) + tsf64_ns = 0; + else + tsf64_ns = tsf64 * NSEC_PER_USEC; + + msdu->tstamp = ns_to_ktime(tsf64_ns); +} + +/** + * ol_rx_timestamp_update() - update msdu tsf64 timestamp + * @pdev: pointer to txrx handle + * @head_msdu: pointer to head msdu + * @tail_msdu: pointer to tail msdu + * + * Return: none + */ +static inline void ol_rx_timestamp_update(ol_txrx_pdev_handle pdev, + qdf_nbuf_t head_msdu, + qdf_nbuf_t tail_msdu) +{ + qdf_nbuf_t loop_msdu; + uint64_t hostime, detlahostime, tsf64_time; + struct htt_host_rx_desc_base *rx_desc; + + if (!ol_cfg_is_ptp_rx_opt_enabled(pdev->ctrl_pdev)) + return; + + if (!tail_msdu) + return; + + hostime = ktime_get_ns(); + rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, tail_msdu); + if (rx_desc->ppdu_end.wb_timestamp_lower_32 == 0 && + rx_desc->ppdu_end.wb_timestamp_upper_32 == 0) { + detlahostime = hostime - pdev->last_host_time; + do_div(detlahostime, NSEC_PER_USEC); + tsf64_time = pdev->last_tsf64_time + detlahostime; + + rx_desc->ppdu_end.wb_timestamp_lower_32 = + tsf64_time & 0xFFFFFFFF; + rx_desc->ppdu_end.wb_timestamp_upper_32 = tsf64_time >> 32; + } else { + pdev->last_host_time = hostime; + pdev->last_tsf64_time = + (uint64_t)rx_desc->ppdu_end.wb_timestamp_upper_32 << 32 | + rx_desc->ppdu_end.wb_timestamp_lower_32; + } + + loop_msdu = head_msdu; + while (loop_msdu) { + ol_rx_timestamp(pdev->ctrl_pdev, rx_desc, loop_msdu); + loop_msdu = qdf_nbuf_next(loop_msdu); + } +} +#endif +#else +void ol_rx_timestamp(struct cdp_cfg *cfg_pdev, + void *rx_desc, qdf_nbuf_t msdu) +{ +} + +static inline void ol_rx_timestamp_update(ol_txrx_pdev_handle pdev, + qdf_nbuf_t head_msdu, + qdf_nbuf_t tail_msdu) +{ +} +#endif + +#ifdef WLAN_FEATURE_DSRC +static inline +void ol_rx_ocb_prepare_rx_stats_header(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + qdf_nbuf_t msdu) +{ + int i; + struct ol_txrx_ocb_chan_info *chan_info = 0; + int packet_freq = peer->last_pkt_center_freq; + + for (i = 0; i < vdev->ocb_channel_count; i++) { + if (vdev->ocb_channel_info[i].chan_freq == packet_freq) { + chan_info = &vdev->ocb_channel_info[i]; + break; + } + } + + if (!chan_info || !chan_info->disable_rx_stats_hdr) { + qdf_ether_header_t eth_header = { {0} }; + struct ocb_rx_stats_hdr_t rx_header = {0}; + + /* + * Construct the RX stats header and + * push that to the frontof the packet. + */ + rx_header.version = 1; + rx_header.length = sizeof(rx_header); + rx_header.channel_freq = peer->last_pkt_center_freq; + rx_header.rssi_cmb = peer->last_pkt_rssi_cmb; + qdf_mem_copy(rx_header.rssi, peer->last_pkt_rssi, + sizeof(rx_header.rssi)); + + if (peer->last_pkt_legacy_rate_sel) + rx_header.datarate = 0xFF; + else if (peer->last_pkt_legacy_rate == 0x8) + rx_header.datarate = 6; + else if (peer->last_pkt_legacy_rate == 0x9) + rx_header.datarate = 4; + else if (peer->last_pkt_legacy_rate == 0xA) + rx_header.datarate = 2; + else if (peer->last_pkt_legacy_rate == 0xB) + rx_header.datarate = 0; + else if (peer->last_pkt_legacy_rate == 0xC) + rx_header.datarate = 7; + else if (peer->last_pkt_legacy_rate == 0xD) + rx_header.datarate = 5; + else if (peer->last_pkt_legacy_rate == 0xE) + rx_header.datarate = 3; + else if (peer->last_pkt_legacy_rate == 0xF) + rx_header.datarate = 1; + else + rx_header.datarate = 0xFF; + + rx_header.timestamp_microsec = + peer->last_pkt_timestamp_microsec; + rx_header.timestamp_submicrosec = + peer->last_pkt_timestamp_submicrosec; + rx_header.tsf32 = peer->last_pkt_tsf; + rx_header.ext_tid = peer->last_pkt_tid; + + qdf_nbuf_push_head(msdu, sizeof(rx_header)); + qdf_mem_copy(qdf_nbuf_data(msdu), + &rx_header, sizeof(rx_header)); + + /* + * Construct the ethernet header with + * type 0x8152 and push that to the + * front of the packet to indicate the + * RX stats header. + */ + eth_header.ether_type = QDF_SWAP_U16(ETHERTYPE_OCB_RX); + qdf_nbuf_push_head(msdu, sizeof(eth_header)); + qdf_mem_copy(qdf_nbuf_data(msdu), ð_header, + sizeof(eth_header)); + } +} +#else +static inline +void ol_rx_ocb_prepare_rx_stats_header(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + qdf_nbuf_t msdu) +{ +} +#endif + +#ifdef WLAN_PARTIAL_REORDER_OFFLOAD +void +ol_rx_deliver(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, unsigned int tid, + qdf_nbuf_t msdu_list) +{ + ol_txrx_pdev_handle pdev = vdev->pdev; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + qdf_nbuf_t deliver_list_head = NULL; + qdf_nbuf_t deliver_list_tail = NULL; + qdf_nbuf_t msdu; + bool filter = false; +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + struct ol_rx_decap_info_t info; + + qdf_mem_zero(&info, sizeof(info)); +#endif + + msdu = msdu_list; + /* + * Check each MSDU to see whether it requires special handling, + * and free each MSDU's rx descriptor + */ + while (msdu) { + void *rx_desc; + int discard, inspect, dummy_fwd; + qdf_nbuf_t next = qdf_nbuf_next(msdu); + + rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu); + /* for HL, point to payload right now*/ + if (pdev->cfg.is_high_latency) { + qdf_nbuf_pull_head(msdu, + htt_rx_msdu_rx_desc_size_hl(htt_pdev, rx_desc)); + } + +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + info.is_msdu_cmpl_mpdu = + htt_rx_msdu_desc_completes_mpdu(htt_pdev, rx_desc); + info.is_first_subfrm = + htt_rx_msdu_first_msdu_flag(htt_pdev, rx_desc); + if (OL_RX_DECAP(vdev, peer, msdu, &info) != A_OK) { + discard = 1; + ol_txrx_dbg( + "decap error %pK from peer %pK ("QDF_MAC_ADDR_FMT") len %d\n", + msdu, peer, + QDF_MAC_ADDR_REF(peer->mac_addr.raw), + qdf_nbuf_len(msdu)); + goto DONE; + } +#endif + htt_rx_msdu_actions(pdev->htt_pdev, rx_desc, &discard, + &dummy_fwd, &inspect); + if (inspect) + ol_rx_inspect(vdev, peer, tid, msdu, rx_desc); + + /* + * Check the first msdu in the mpdu, if it will be filtered out, + * then discard the entire mpdu. + */ + if (htt_rx_msdu_first_msdu_flag(htt_pdev, rx_desc)) + filter = ol_rx_filter(vdev, peer, msdu, rx_desc); + +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP +DONE: +#endif + htt_rx_msdu_desc_free(htt_pdev, msdu); + if (discard || (true == filter)) { + ol_txrx_frms_dump("rx discarding:", + pdev, deliver_list_head, + ol_txrx_frm_dump_tcp_seq | + ol_txrx_frm_dump_contents, + 0 /* don't print contents */); + qdf_nbuf_free(msdu); + /* + * If discarding packet is last packet of the delivery + * list, NULL terminator should be added + * for delivery list. + */ + if (!next && deliver_list_head) { + /* add NULL terminator */ + qdf_nbuf_set_next(deliver_list_tail, NULL); + } + } else { + /* + * If this is for OCB, + * then prepend the RX stats header. + */ + if (vdev->opmode == wlan_op_mode_ocb) + ol_rx_ocb_prepare_rx_stats_header(vdev, peer, + msdu); + + OL_RX_PEER_STATS_UPDATE(peer, msdu); + OL_RX_ERR_STATISTICS_1(pdev, vdev, peer, rx_desc, + OL_RX_ERR_NONE); + TXRX_STATS_MSDU_INCR(vdev->pdev, rx.delivered, msdu); + + ol_rx_timestamp(pdev->ctrl_pdev, rx_desc, msdu); + OL_TXRX_LIST_APPEND(deliver_list_head, + deliver_list_tail, msdu); + QDF_NBUF_CB_DP_TRACE_PRINT(msdu) = false; + qdf_dp_trace_set_track(msdu, QDF_RX); + } + msdu = next; + } + /* sanity check - are there any frames left to give to the OS shim? */ + if (!deliver_list_head) + return; + + ol_txrx_frms_dump("rx delivering:", + pdev, deliver_list_head, + ol_txrx_frm_dump_tcp_seq | ol_txrx_frm_dump_contents, + 0 /* don't print contents */); + + ol_rx_data_process(peer, deliver_list_head); +} +#endif + +void +ol_rx_discard(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, unsigned int tid, + qdf_nbuf_t msdu_list) +{ + while (msdu_list) { + qdf_nbuf_t msdu = msdu_list; + + msdu_list = qdf_nbuf_next(msdu_list); + ol_txrx_dbg("discard rx %pK", msdu); + qdf_nbuf_free(msdu); + } +} + +void ol_rx_peer_init(struct ol_txrx_pdev_t *pdev, struct ol_txrx_peer_t *peer) +{ + uint8_t tid; + + for (tid = 0; tid < OL_TXRX_NUM_EXT_TIDS; tid++) { + ol_rx_reorder_init(&peer->tids_rx_reorder[tid], tid); + + /* invalid sequence number */ + peer->tids_last_seq[tid] = IEEE80211_SEQ_MAX; + /* invalid reorder index number */ + peer->tids_next_rel_idx[tid] = INVALID_REORDER_INDEX; + + } + /* + * Set security defaults: no PN check, no security. + * The target may send a HTT SEC_IND message to overwrite + * these defaults. + */ + peer->security[txrx_sec_ucast].sec_type = + peer->security[txrx_sec_mcast].sec_type = htt_sec_type_none; + peer->keyinstalled = 0; + + peer->last_assoc_rcvd = 0; + peer->last_disassoc_rcvd = 0; + peer->last_deauth_rcvd = 0; + + qdf_atomic_init(&peer->fw_pn_check); +} + +void +ol_rx_peer_cleanup(struct ol_txrx_vdev_t *vdev, struct ol_txrx_peer_t *peer) +{ + peer->keyinstalled = 0; + peer->last_assoc_rcvd = 0; + peer->last_disassoc_rcvd = 0; + peer->last_deauth_rcvd = 0; + ol_rx_reorder_peer_cleanup(vdev, peer); +} + +/* + * Free frames including both rx descriptors and buffers + */ +void ol_rx_frames_free(htt_pdev_handle htt_pdev, qdf_nbuf_t frames) +{ + qdf_nbuf_t next, frag = frames; + + while (frag) { + next = qdf_nbuf_next(frag); + htt_rx_desc_frame_free(htt_pdev, frag); + frag = next; + } +} + +#ifdef WLAN_FULL_REORDER_OFFLOAD +void +ol_rx_in_order_indication_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t rx_ind_msg, + uint16_t peer_id, + uint8_t tid, uint8_t is_offload) +{ + struct ol_txrx_vdev_t *vdev = NULL; + struct ol_txrx_peer_t *peer = NULL; + htt_pdev_handle htt_pdev = NULL; + int status; + qdf_nbuf_t head_msdu = NULL, tail_msdu = NULL; + uint8_t *rx_ind_data; + uint32_t *msg_word; + uint32_t msdu_count; + uint8_t pktlog_bit; + uint32_t filled = 0; + + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + ol_txrx_err("invalid tid, %u", tid); + WARN_ON(1); + return; + } + + if (pdev) { + if (qdf_unlikely(QDF_GLOBAL_MONITOR_MODE == cds_get_conparam())) + peer = pdev->self_peer; + else + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + htt_pdev = pdev->htt_pdev; + } else { + ol_txrx_err("Invalid pdev passed!"); + qdf_assert_always(pdev); + return; + } + +#if defined(HELIUMPLUS_DEBUG) + qdf_print("rx_ind_msg 0x%pK peer_id %d tid %d is_offload %d", + rx_ind_msg, peer_id, tid, is_offload); +#endif + + pktlog_bit = (htt_rx_amsdu_rx_in_order_get_pktlog(rx_ind_msg) == 0x01); + rx_ind_data = qdf_nbuf_data(rx_ind_msg); + msg_word = (uint32_t *)rx_ind_data; + /* Get the total number of MSDUs */ + msdu_count = HTT_RX_IN_ORD_PADDR_IND_MSDU_CNT_GET(*(msg_word + 1)); + + ol_rx_ind_record_event(msdu_count, OL_RX_INDICATION_POP_START); + + /* + * Get a linked list of the MSDUs in the rx in order indication. + * This also attaches each rx MSDU descriptor to the + * corresponding rx MSDU network buffer. + */ + status = htt_rx_amsdu_pop(htt_pdev, rx_ind_msg, &head_msdu, + &tail_msdu, &msdu_count); + ol_rx_ind_record_event(status, OL_RX_INDICATION_POP_END); + + if (qdf_unlikely(0 == status)) { + ol_txrx_warn("pop failed"); + return; + } + + /* + * Replenish the rx buffer ring first to provide buffers to the target + * rather than waiting for the indeterminate time taken by the OS + * to consume the rx frames + */ + filled = htt_rx_msdu_buff_in_order_replenish(htt_pdev, msdu_count); + ol_rx_ind_record_event(filled, OL_RX_INDICATION_BUF_REPLENISH); + + if (!head_msdu) { + ol_txrx_dbg("No packet to send to HDD"); + return; + } + + /* Send the chain of MSDUs to the OS */ + /* rx_opt_proc takes a NULL-terminated list of msdu netbufs */ + qdf_nbuf_set_next(tail_msdu, NULL); + + /* Pktlog */ + ol_rx_send_pktlog_event(pdev, peer, head_msdu, pktlog_bit); + + /* + * if this is an offload indication, peer id is carried in the + * rx buffer + */ + if (peer) { + vdev = peer->vdev; + } else { + ol_txrx_dbg("Couldn't find peer from ID 0x%x", peer_id); + while (head_msdu) { + qdf_nbuf_t msdu = head_msdu; + + head_msdu = qdf_nbuf_next(head_msdu); + TXRX_STATS_MSDU_INCR(pdev, + rx.dropped_peer_invalid, msdu); + htt_rx_desc_frame_free(htt_pdev, msdu); + } + return; + } + + /*Loop msdu to fill tstamp with tsf64 time in ol_rx_timestamp*/ + ol_rx_timestamp_update(pdev, head_msdu, tail_msdu); + + peer->rx_opt_proc(vdev, peer, tid, head_msdu); +} +#endif + +#ifndef REMOVE_PKT_LOG +/** + * ol_rx_pkt_dump_call() - updates status and + * calls packetdump callback to log rx packet + * + * @msdu: rx packet + * @peer_id: peer id + * @status: status of rx packet + * + * This function is used to update the status of rx packet + * and then calls packetdump callback to log that packet. + * + * Return: None + * + */ +void ol_rx_pkt_dump_call( + qdf_nbuf_t msdu, + uint8_t peer_id, + uint8_t status) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_soc_handle soc_hdl = ol_txrx_soc_t_to_cdp_soc_t(soc); + struct ol_txrx_peer_t *peer = NULL; + ol_txrx_pktdump_cb packetdump_cb; + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (!peer) { + ol_txrx_dbg("peer with peer id %d is NULL", peer_id); + return; + } + + packetdump_cb = pdev->ol_rx_packetdump_cb; + if (packetdump_cb && + wlan_op_mode_sta == peer->vdev->opmode) + packetdump_cb(soc_hdl, OL_TXRX_PDEV_ID, peer->vdev->vdev_id, + msdu, status, RX_DATA_PKT); +} +#endif + +#ifdef WLAN_FULL_REORDER_OFFLOAD +/* the msdu_list passed here must be NULL terminated */ +void +ol_rx_in_order_deliver(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu_list) +{ + qdf_nbuf_t msdu; + + msdu = msdu_list; + /* + * Currently, this does not check each MSDU to see whether it requires + * special handling. MSDUs that need special handling (example: IGMP + * frames) should be sent via a separate HTT message. Also, this does + * not do rx->tx forwarding or filtering. + */ + + while (msdu) { + qdf_nbuf_t next = qdf_nbuf_next(msdu); + + DPTRACE(qdf_dp_trace(msdu, + QDF_DP_TRACE_RX_TXRX_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(msdu), + sizeof(qdf_nbuf_data(msdu)), QDF_RX)); + + OL_RX_PEER_STATS_UPDATE(peer, msdu); + OL_RX_ERR_STATISTICS_1(vdev->pdev, vdev, peer, rx_desc, + OL_RX_ERR_NONE); + TXRX_STATS_MSDU_INCR(vdev->pdev, rx.delivered, msdu); + + msdu = next; + } + + ol_txrx_frms_dump("rx delivering:", + pdev, deliver_list_head, + ol_txrx_frm_dump_tcp_seq | ol_txrx_frm_dump_contents, + 0 /* don't print contents */); + + ol_rx_data_process(peer, msdu_list); +} +#endif + +#ifndef CONFIG_HL_SUPPORT +void +ol_rx_offload_paddr_deliver_ind_handler(htt_pdev_handle htt_pdev, + uint32_t msdu_count, + uint32_t *msg_word) +{ + int vdev_id, peer_id, tid; + qdf_nbuf_t head_buf, tail_buf, buf; + struct ol_txrx_peer_t *peer; + uint8_t fw_desc; + int msdu_iter = 0; + + while (msdu_count) { + if (htt_rx_offload_paddr_msdu_pop_ll( + htt_pdev, msg_word, msdu_iter, + &vdev_id, &peer_id, &tid, + &fw_desc, &head_buf, + &tail_buf)) { + msdu_iter++; + msdu_count--; + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "skip msg_word %pK, msdu #%d, continue next", + msg_word, msdu_iter); + continue; + } + + peer = ol_txrx_peer_find_by_id(htt_pdev->txrx_pdev, peer_id); + if (peer) { + QDF_NBUF_CB_DP_TRACE_PRINT(head_buf) = false; + qdf_dp_trace_set_track(head_buf, QDF_RX); + QDF_NBUF_CB_TX_PACKET_TRACK(head_buf) = + QDF_NBUF_TX_PKT_DATA_TRACK; + qdf_dp_trace_log_pkt(peer->vdev->vdev_id, + head_buf, QDF_RX, + QDF_TRACE_DEFAULT_PDEV_ID); + DPTRACE(qdf_dp_trace(head_buf, + QDF_DP_TRACE_RX_OFFLOAD_HTT_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(head_buf), + sizeof(qdf_nbuf_data(head_buf)), QDF_RX)); + ol_rx_data_process(peer, head_buf); + } else { + buf = head_buf; + while (1) { + qdf_nbuf_t next; + + next = qdf_nbuf_next(buf); + htt_rx_desc_frame_free(htt_pdev, buf); + if (buf == tail_buf) + break; + buf = next; + } + } + msdu_iter++; + msdu_count--; + } + htt_rx_msdu_buff_replenish(htt_pdev); +} +#endif + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +/** + * ol_htt_mon_note_chan() - Update monitor channel information + * @pdev: handle to the physical device + * @mon_ch: Monitor channel + * + * Return: None + */ +void ol_htt_mon_note_chan(struct cdp_pdev *ppdev, int mon_ch) +{ + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev; + + htt_rx_mon_note_capture_channel(pdev->htt_pdev, mon_ch); +} +#endif + +#ifdef NEVERDEFINED +/** + * @brief populates vow ext stats in given network buffer. + * @param msdu - network buffer handle + * @param pdev - handle to htt dev. + */ +void ol_ath_add_vow_extstats(htt_pdev_handle pdev, qdf_nbuf_t msdu) +{ + /* FIX THIS: + * txrx should not be directly using data types (scn) + * that are internal to other modules. + */ + struct ol_ath_softc_net80211 *scn = + (struct ol_ath_softc_net80211 *)pdev->ctrl_pdev; + uint8_t *data, *l3_hdr, *bp; + uint16_t ethertype; + int offset; + struct vow_extstats vowstats; + + if (scn->vow_extstats == 0) + return; + + data = qdf_nbuf_data(msdu); + + offset = QDF_MAC_ADDR_SIZE * 2; + l3_hdr = data + ETHERNET_HDR_LEN; + ethertype = (data[offset] << 8) | data[offset + 1]; + if (ethertype == ETHERTYPE_IPV4) { + offset = IPV4_HDR_OFFSET_PROTOCOL; + if ((l3_hdr[offset] == IP_PROTOCOL_UDP) && + (l3_hdr[0] == IP_VER4_N_NO_EXTRA_HEADERS)) { + bp = data + EXT_HDR_OFFSET; + + if ((data[RTP_HDR_OFFSET] == UDP_PDU_RTP_EXT) && + (bp[0] == 0x12) && + (bp[1] == 0x34) && + (bp[2] == 0x00) && (bp[3] == 0x08)) { + /* + * Clear UDP checksum so we do not have + * to recalculate it + * after filling in status fields. + */ + data[UDP_CKSUM_OFFSET] = 0; + data[(UDP_CKSUM_OFFSET + 1)] = 0; + + bp += IPERF3_DATA_OFFSET; + + htt_rx_get_vowext_stats(msdu, + &vowstats); + + /* control channel RSSI */ + *bp++ = vowstats.rx_rssi_ctl0; + *bp++ = vowstats.rx_rssi_ctl1; + *bp++ = vowstats.rx_rssi_ctl2; + + /* rx rate info */ + *bp++ = vowstats.rx_bw; + *bp++ = vowstats.rx_sgi; + *bp++ = vowstats.rx_nss; + + *bp++ = vowstats.rx_rssi_comb; + /* rsflags */ + *bp++ = vowstats.rx_rs_flags; + + /* Time stamp Lo */ + *bp++ = (uint8_t) + ((vowstats. + rx_macTs & 0x0000ff00) >> 8); + *bp++ = (uint8_t) + (vowstats.rx_macTs & 0x0000ff); + /* rx phy errors */ + *bp++ = (uint8_t) + ((scn->chan_stats. + phy_err_cnt >> 8) & 0xff); + *bp++ = + (uint8_t) (scn->chan_stats. + phy_err_cnt & 0xff); + /* rx clear count */ + *bp++ = (uint8_t) + ((scn->mib_cycle_cnts. + rx_clear_count >> 24) & 0xff); + *bp++ = (uint8_t) + ((scn->mib_cycle_cnts. + rx_clear_count >> 16) & 0xff); + *bp++ = (uint8_t) + ((scn->mib_cycle_cnts. + rx_clear_count >> 8) & 0xff); + *bp++ = (uint8_t) + (scn->mib_cycle_cnts. + rx_clear_count & 0xff); + /* rx cycle count */ + *bp++ = (uint8_t) + ((scn->mib_cycle_cnts. + cycle_count >> 24) & 0xff); + *bp++ = (uint8_t) + ((scn->mib_cycle_cnts. + cycle_count >> 16) & 0xff); + *bp++ = (uint8_t) + ((scn->mib_cycle_cnts. + cycle_count >> 8) & 0xff); + *bp++ = (uint8_t) + (scn->mib_cycle_cnts. + cycle_count & 0xff); + + *bp++ = vowstats.rx_ratecode; + *bp++ = vowstats.rx_moreaggr; + + /* sequence number */ + *bp++ = (uint8_t) + ((vowstats.rx_seqno >> 8) & + 0xff); + *bp++ = (uint8_t) + (vowstats.rx_seqno & 0xff); + } + } + } +} + +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx.h new file mode 100644 index 0000000000000000000000000000000000000000..17c1101138fd90ee6fd3d6b328866802a3dadcfb --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2011, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_RX__H_ +#define _OL_RX__H_ + +#include /* qdf_nbuf_t */ +#include /* htt_pdev_handle */ +#include /* ol_txrx_vdev_t */ + +#ifdef WLAN_PARTIAL_REORDER_OFFLOAD +void +ol_rx_deliver(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, unsigned int tid, + qdf_nbuf_t head_msdu); +#else +static inline void +ol_rx_deliver(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, unsigned int tid, + qdf_nbuf_t head_msdu) +{ +} +#endif + +void +ol_rx_discard(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, unsigned int tid, + qdf_nbuf_t head_msdu); + +/** + * ol_rx_send_mic_err_ind() - ol rx mic err handler + * @pdev: ol pdev + * @vdev_id: vdev id + * @peer_mac_addr: peer mac address + * @tid: TID + * @tsf32: TSF + * @err_type: error type + * @rx_frame: rx frame + * @pn: PN Number + * @key_id: key id + * + * This function handles rx error and send MIC error failure to HDD + * + * Return: none + */ +void +ol_rx_send_mic_err_ind(struct ol_txrx_pdev_t *pdev, uint8_t vdev_id, + uint8_t *peer_mac_addr, int tid, uint32_t tsf32, + enum ol_rx_err_type err_type, qdf_nbuf_t rx_frame, + uint64_t *pn, uint8_t key_id); + +void ol_rx_frames_free(htt_pdev_handle htt_pdev, qdf_nbuf_t frames); + +void ol_rx_peer_init(struct ol_txrx_pdev_t *pdev, struct ol_txrx_peer_t *peer); + +void +ol_rx_peer_cleanup(struct ol_txrx_vdev_t *vdev, struct ol_txrx_peer_t *peer); + +#ifdef WDI_EVENT_ENABLE +void ol_rx_send_pktlog_event(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, + uint8_t pktlog_bit); +#else +static inline +void ol_rx_send_pktlog_event(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, qdf_nbuf_t msdu, + uint8_t pktlog_bit) +{ +} +#endif + +#ifdef WLAN_FULL_REORDER_OFFLOAD +void +ol_rx_in_order_deliver(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t head_msdu); +#else +static inline void +ol_rx_in_order_deliver(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t head_msdu) +{ +} +#endif + +void ol_rx_log_packet(htt_pdev_handle htt_pdev, + uint8_t peer_id, qdf_nbuf_t msdu); +void +ol_rx_offload_paddr_deliver_ind_handler(htt_pdev_handle htt_pdev, + uint32_t msdu_count, + uint32_t *msg_word); +void ol_rx_update_histogram_stats(uint32_t msdu_count, + uint8_t frag_ind, uint8_t offload_ind); + +void +ol_rx_mic_error_handler( + ol_txrx_pdev_handle pdev, + u_int8_t tid, + u_int16_t peer_id, + void *msdu_desc, + qdf_nbuf_t msdu); + +void htt_rx_fill_ring_count(htt_pdev_handle pdev); + +void ol_rx_timestamp(struct cdp_cfg *cfg_pdev, void *rx_desc, qdf_nbuf_t msdu); + +#endif /* _OL_RX__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.c new file mode 100644 index 0000000000000000000000000000000000000000..bfaef16fccad7e2546fb07a0cf0f5c9305c75fb8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.c @@ -0,0 +1,1311 @@ +/* + * Copyright (c) 2011-2019,2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* qdf_system_time */ + +#define DEFRAG_IEEE80211_ADDR_EQ(a1, a2) \ + (!qdf_mem_cmp(a1, a2, QDF_MAC_ADDR_SIZE)) + +#define DEFRAG_IEEE80211_ADDR_COPY(dst, src) \ + qdf_mem_copy(dst, src, QDF_MAC_ADDR_SIZE) + +#define DEFRAG_IEEE80211_QOS_HAS_SEQ(wh) \ + (((wh)->i_fc[0] & \ + (IEEE80211_FC0_TYPE_MASK | QDF_IEEE80211_FC0_SUBTYPE_QOS)) == \ + (IEEE80211_FC0_TYPE_DATA | QDF_IEEE80211_FC0_SUBTYPE_QOS)) + +#define DEFRAG_IEEE80211_QOS_GET_TID(_x) \ + ((_x)->i_qos[0] & IEEE80211_QOS_TID) + +const struct ol_rx_defrag_cipher f_ccmp = { + "AES-CCM", + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN, + IEEE80211_WEP_MICLEN, + 0, +}; + +const struct ol_rx_defrag_cipher f_tkip = { + "TKIP", + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN, + IEEE80211_WEP_CRCLEN, + IEEE80211_WEP_MICLEN, +}; + +const struct ol_rx_defrag_cipher f_wep = { + "WEP", + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN, + IEEE80211_WEP_CRCLEN, + 0, +}; + +#if defined(CONFIG_HL_SUPPORT) + +/** + * ol_rx_frag_get_mac_hdr() - retrieve mac header + * @htt_pdev: pointer to htt pdev handle + * @frag: rx fragment + * + * Return: pointer to ieee mac header of frag + */ +static struct ieee80211_frame *ol_rx_frag_get_mac_hdr( + htt_pdev_handle htt_pdev, qdf_nbuf_t frag) +{ + void *rx_desc; + int rx_desc_len; + + rx_desc = htt_rx_msdu_desc_retrieve(htt_pdev, frag); + rx_desc_len = htt_rx_msdu_rx_desc_size_hl(htt_pdev, rx_desc); + return (struct ieee80211_frame *)(qdf_nbuf_data(frag) + rx_desc_len); +} + +/** + * ol_rx_frag_pull_hdr() - point to payload of rx frag + * @htt_pdev: pointer to htt pdev handle + * @frag: rx fragment + * @hdrsize: header size + * + * Return: None + */ +static void ol_rx_frag_pull_hdr(htt_pdev_handle htt_pdev, + qdf_nbuf_t frag, int hdrsize) +{ + void *rx_desc; + int rx_desc_len; + + rx_desc = htt_rx_msdu_desc_retrieve(htt_pdev, frag); + rx_desc_len = htt_rx_msdu_rx_desc_size_hl(htt_pdev, rx_desc); + qdf_nbuf_pull_head(frag, rx_desc_len + hdrsize); +} + +/** + * ol_rx_frag_desc_adjust() - adjust rx frag descriptor position + * @pdev: pointer to txrx handle + * @msdu: msdu + * @rx_desc_old_position: rx descriptor old position + * @ind_old_position:index of old position + * @rx_desc_len: rx desciptor length + * + * Return: None + */ +static void +ol_rx_frag_desc_adjust(ol_txrx_pdev_handle pdev, + qdf_nbuf_t msdu, + void **rx_desc_old_position, + void **ind_old_position, int *rx_desc_len) +{ + *rx_desc_old_position = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, + msdu); + *ind_old_position = *rx_desc_old_position - HTT_RX_IND_HL_BYTES; + *rx_desc_len = htt_rx_msdu_rx_desc_size_hl(pdev->htt_pdev, + *rx_desc_old_position); +} + +/** + * ol_rx_frag_restructure() - point to payload for HL + * @pdev: physical device object + * @msdu: the buffer containing the MSDU payload + * @rx_desc_old_position: rx MSDU descriptor + * @ind_old_position: rx msdu indication + * @f_type: pointing to rx defrag cipher + * @rx_desc_len: length by which rx descriptor to move + * + * Return: None + */ +static void +ol_rx_frag_restructure( + ol_txrx_pdev_handle pdev, + qdf_nbuf_t msdu, + void *rx_desc_old_position, + void *ind_old_position, + const struct ol_rx_defrag_cipher *f_type, + int rx_desc_len) +{ + if ((!ind_old_position) || (!rx_desc_old_position)) { + ol_txrx_err("ind_old_position,rx_desc_old_position is NULL\n"); + ASSERT(0); + return; + } + /* move rx description*/ + qdf_mem_move(rx_desc_old_position + f_type->ic_header, + rx_desc_old_position, rx_desc_len); + /* move rx indication*/ + qdf_mem_move(ind_old_position + f_type->ic_header, ind_old_position, + HTT_RX_IND_HL_BYTES); +} + +/** + * ol_rx_get_desc_len() - point to payload for HL + * @htt_pdev: the HTT instance the rx data was received on + * @wbuf: buffer containing the MSDU payload + * @rx_desc_old_position: rx MSDU descriptor + * + * Return: Return the HL rx desc size + */ +static +int ol_rx_get_desc_len(htt_pdev_handle htt_pdev, + qdf_nbuf_t wbuf, + void **rx_desc_old_position) +{ + int rx_desc_len = 0; + *rx_desc_old_position = htt_rx_msdu_desc_retrieve(htt_pdev, wbuf); + rx_desc_len = htt_rx_msdu_rx_desc_size_hl(htt_pdev, + *rx_desc_old_position); + + return rx_desc_len; +} + +/** + * ol_rx_defrag_push_rx_desc() - point to payload for HL + * @nbuf: buffer containing the MSDU payload + * @rx_desc_old_position: rx MSDU descriptor + * @ind_old_position: rx msdu indication + * @rx_desc_len: HL rx desc size + * + * Return: Return the HL rx desc size + */ +static +void ol_rx_defrag_push_rx_desc(qdf_nbuf_t nbuf, + void *rx_desc_old_position, + void *ind_old_position, + int rx_desc_len) +{ + qdf_nbuf_push_head(nbuf, rx_desc_len); + qdf_mem_move( + qdf_nbuf_data(nbuf), rx_desc_old_position, rx_desc_len); + qdf_mem_move( + qdf_nbuf_data(nbuf) - HTT_RX_IND_HL_BYTES, ind_old_position, + HTT_RX_IND_HL_BYTES); +} +#else + +static inline struct ieee80211_frame *ol_rx_frag_get_mac_hdr( + htt_pdev_handle htt_pdev, + qdf_nbuf_t frag) +{ + return + (struct ieee80211_frame *) qdf_nbuf_data(frag); +} + +static inline void ol_rx_frag_pull_hdr(htt_pdev_handle htt_pdev, + qdf_nbuf_t frag, int hdrsize) +{ + qdf_nbuf_pull_head(frag, hdrsize); +} + +static inline void +ol_rx_frag_desc_adjust(ol_txrx_pdev_handle pdev, + qdf_nbuf_t msdu, + void **rx_desc_old_position, + void **ind_old_position, int *rx_desc_len) +{ + *rx_desc_old_position = NULL; + *ind_old_position = NULL; + *rx_desc_len = 0; +} + +static inline void +ol_rx_frag_restructure( + ol_txrx_pdev_handle pdev, + qdf_nbuf_t msdu, + void *rx_desc_old_position, + void *ind_old_position, + const struct ol_rx_defrag_cipher *f_type, + int rx_desc_len) +{ + /* no op */ +} + +static inline +int ol_rx_get_desc_len(htt_pdev_handle htt_pdev, + qdf_nbuf_t wbuf, + void **rx_desc_old_position) +{ + return 0; +} + +static inline +void ol_rx_defrag_push_rx_desc(qdf_nbuf_t nbuf, + void *rx_desc_old_position, + void *ind_old_position, + int rx_desc_len) +{ + return; +} +#endif /* CONFIG_HL_SUPPORT */ + +/* + * Process incoming fragments + */ +void +ol_rx_frag_indication_handler(ol_txrx_pdev_handle pdev, + qdf_nbuf_t rx_frag_ind_msg, + uint16_t peer_id, uint8_t tid) +{ + uint16_t seq_num; + uint16_t seq_num_start, seq_num_end; + struct ol_txrx_peer_t *peer; + htt_pdev_handle htt_pdev; + qdf_nbuf_t head_msdu, tail_msdu; + void *rx_mpdu_desc; + uint8_t pktlog_bit; + uint32_t msdu_count = 0; + int ret; + void *rx_desc; + + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + ol_txrx_err("%s: invalid tid, %u\n", __FUNCTION__, tid); + return; + } + + htt_pdev = pdev->htt_pdev; + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + + if (!ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev) && + htt_rx_ind_flush(pdev->htt_pdev, rx_frag_ind_msg) && peer) { + htt_rx_frag_ind_flush_seq_num_range(pdev->htt_pdev, + rx_frag_ind_msg, + &seq_num_start, + &seq_num_end); + /* + * Assuming flush indication for frags sent from target is + * separate from normal frames + */ + ol_rx_reorder_flush_frag(htt_pdev, peer, tid, seq_num_start); + } else { + uint32_t *msg_word; + uint8_t *rx_ind_data; + + rx_ind_data = qdf_nbuf_data(rx_frag_ind_msg); + msg_word = (uint32_t *)rx_ind_data; + msdu_count = HTT_RX_IN_ORD_PADDR_IND_MSDU_CNT_GET(*(msg_word + + 1)); + } + + pktlog_bit = + (htt_rx_amsdu_rx_in_order_get_pktlog(rx_frag_ind_msg) == 0x01); + ret = htt_rx_frag_pop(htt_pdev, rx_frag_ind_msg, &head_msdu, + &tail_msdu, &msdu_count); + /* Return if msdu pop fails from rx hash table, as recovery + * is triggered and we exit gracefully. + */ + if (!ret) + return; + if (peer) { + qdf_assert(head_msdu == tail_msdu); + if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev)) { + rx_mpdu_desc = + htt_rx_mpdu_desc_list_next(htt_pdev, head_msdu); + } else { + rx_mpdu_desc = + htt_rx_mpdu_desc_list_next(htt_pdev, + rx_frag_ind_msg); + } + seq_num = htt_rx_mpdu_desc_seq_num(htt_pdev, rx_mpdu_desc); + OL_RX_ERR_STATISTICS_1(pdev, peer->vdev, peer, rx_mpdu_desc, + OL_RX_ERR_NONE_FRAG); + ol_rx_send_pktlog_event(pdev, peer, head_msdu, pktlog_bit); + rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, head_msdu); + ol_rx_timestamp(pdev->ctrl_pdev, rx_desc, head_msdu); + ol_rx_reorder_store_frag(pdev, peer, tid, seq_num, head_msdu); + } else { + /* invalid frame - discard it */ + if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev)) + htt_rx_msdu_desc_retrieve(htt_pdev, head_msdu); + else + htt_rx_mpdu_desc_list_next(htt_pdev, rx_frag_ind_msg); + + ol_rx_send_pktlog_event(pdev, peer, head_msdu, pktlog_bit); + htt_rx_desc_frame_free(htt_pdev, head_msdu); + } + /* request HTT to provide new rx MSDU buffers for the target to fill. */ + if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev) && + !pdev->cfg.is_high_latency) + htt_rx_msdu_buff_in_order_replenish(htt_pdev, msdu_count); + else + htt_rx_msdu_buff_replenish(htt_pdev); +} + +/* + * Flushing fragments + */ +void +ol_rx_reorder_flush_frag(htt_pdev_handle htt_pdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, uint16_t seq_num) +{ + struct ol_rx_reorder_array_elem_t *rx_reorder_array_elem; + int seq; + + seq = seq_num & peer->tids_rx_reorder[tid].win_sz_mask; + rx_reorder_array_elem = &peer->tids_rx_reorder[tid].array[seq]; + if (rx_reorder_array_elem->head) { + ol_rx_frames_free(htt_pdev, rx_reorder_array_elem->head); + rx_reorder_array_elem->head = NULL; + rx_reorder_array_elem->tail = NULL; + } +} + +/* + * Reorder and store fragments + */ +void +ol_rx_reorder_store_frag(ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, uint16_t seq_num, qdf_nbuf_t frag) +{ + struct ieee80211_frame *fmac_hdr, *mac_hdr; + uint8_t fragno, more_frag, all_frag_present = 0; + struct ol_rx_reorder_array_elem_t *rx_reorder_array_elem; + uint16_t frxseq, rxseq, seq; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + void *rx_desc; + uint8_t index; + + seq = seq_num & peer->tids_rx_reorder[tid].win_sz_mask; + qdf_assert(seq == 0); + rx_reorder_array_elem = &peer->tids_rx_reorder[tid].array[seq]; + + mac_hdr = (struct ieee80211_frame *) + ol_rx_frag_get_mac_hdr(htt_pdev, frag); + rxseq = qdf_le16_to_cpu(*(uint16_t *) mac_hdr->i_seq) >> + IEEE80211_SEQ_SEQ_SHIFT; + fragno = qdf_le16_to_cpu(*(uint16_t *) mac_hdr->i_seq) & + IEEE80211_SEQ_FRAG_MASK; + more_frag = mac_hdr->i_fc[1] & IEEE80211_FC1_MORE_FRAG; + + rx_desc = htt_rx_msdu_desc_retrieve(htt_pdev, frag); + qdf_assert(htt_rx_msdu_has_wlan_mcast_flag(htt_pdev, rx_desc)); + index = htt_rx_msdu_is_wlan_mcast(htt_pdev, rx_desc) ? + txrx_sec_mcast : txrx_sec_ucast; + + /* + * Multicast/Broadcast frames should not be fragmented so drop + * such frames. + */ + if (index != txrx_sec_ucast) { + ol_rx_frames_free(htt_pdev, frag); + return; + } + + if (peer->security[index].sec_type != htt_sec_type_none && + !htt_rx_mpdu_is_encrypted(htt_pdev, rx_desc)) { + ol_txrx_err("Unencrypted fragment received in security mode %d", + peer->security[index].sec_type); + ol_rx_frames_free(htt_pdev, frag); + return; + } + + if ((!more_frag) && (!fragno) && (!rx_reorder_array_elem->head)) { + rx_reorder_array_elem->head = frag; + rx_reorder_array_elem->tail = frag; + qdf_nbuf_set_next(frag, NULL); + ol_rx_defrag(pdev, peer, tid, rx_reorder_array_elem->head); + rx_reorder_array_elem->head = NULL; + rx_reorder_array_elem->tail = NULL; + return; + } + if (rx_reorder_array_elem->head) { + fmac_hdr = (struct ieee80211_frame *) + ol_rx_frag_get_mac_hdr(htt_pdev, + rx_reorder_array_elem->head); + frxseq = qdf_le16_to_cpu(*(uint16_t *) fmac_hdr->i_seq) >> + IEEE80211_SEQ_SEQ_SHIFT; + if (rxseq != frxseq + || !DEFRAG_IEEE80211_ADDR_EQ(mac_hdr->i_addr1, + fmac_hdr->i_addr1) + || !DEFRAG_IEEE80211_ADDR_EQ(mac_hdr->i_addr2, + fmac_hdr->i_addr2)) { + ol_rx_frames_free(htt_pdev, + rx_reorder_array_elem->head); + rx_reorder_array_elem->head = NULL; + rx_reorder_array_elem->tail = NULL; + ol_txrx_err("\n ol_rx_reorder_store:%s mismatch\n", + (rxseq == frxseq) + ? "address" + : "seq number"); + } + } + + ol_rx_fraglist_insert(htt_pdev, &rx_reorder_array_elem->head, + &rx_reorder_array_elem->tail, frag, + &all_frag_present); + + if (pdev->rx.flags.defrag_timeout_check) + ol_rx_defrag_waitlist_remove(peer, tid); + + if (all_frag_present) { + ol_rx_defrag(pdev, peer, tid, rx_reorder_array_elem->head); + rx_reorder_array_elem->head = NULL; + rx_reorder_array_elem->tail = NULL; + peer->tids_rx_reorder[tid].defrag_timeout_ms = 0; + peer->tids_last_seq[tid] = seq_num; + } else if (pdev->rx.flags.defrag_timeout_check) { + uint32_t now_ms = qdf_system_ticks_to_msecs(qdf_system_ticks()); + + peer->tids_rx_reorder[tid].defrag_timeout_ms = + now_ms + pdev->rx.defrag.timeout_ms; + ol_rx_defrag_waitlist_add(peer, tid); + } +} + +/* + * Insert and store fragments + */ +void +ol_rx_fraglist_insert(htt_pdev_handle htt_pdev, + qdf_nbuf_t *head_addr, + qdf_nbuf_t *tail_addr, + qdf_nbuf_t frag, uint8_t *all_frag_present) +{ + qdf_nbuf_t next, prev = NULL, cur = *head_addr; + struct ieee80211_frame *mac_hdr, *cmac_hdr, *next_hdr, *lmac_hdr; + uint8_t fragno, cur_fragno, lfragno, next_fragno; + uint8_t last_morefrag = 1, count = 0; + + qdf_assert(frag); + + mac_hdr = (struct ieee80211_frame *) + ol_rx_frag_get_mac_hdr(htt_pdev, frag); + fragno = qdf_le16_to_cpu(*(uint16_t *) mac_hdr->i_seq) & + IEEE80211_SEQ_FRAG_MASK; + + if (!(*head_addr)) { + *head_addr = frag; + *tail_addr = frag; + qdf_nbuf_set_next(*tail_addr, NULL); + return; + } + /* For efficiency, compare with tail first */ + lmac_hdr = (struct ieee80211_frame *) + ol_rx_frag_get_mac_hdr(htt_pdev, *tail_addr); + lfragno = qdf_le16_to_cpu(*(uint16_t *) lmac_hdr->i_seq) & + IEEE80211_SEQ_FRAG_MASK; + if (fragno > lfragno) { + qdf_nbuf_set_next(*tail_addr, frag); + *tail_addr = frag; + qdf_nbuf_set_next(*tail_addr, NULL); + } else { + do { + cmac_hdr = (struct ieee80211_frame *) + ol_rx_frag_get_mac_hdr(htt_pdev, cur); + cur_fragno = + qdf_le16_to_cpu(*(uint16_t *) cmac_hdr->i_seq) & + IEEE80211_SEQ_FRAG_MASK; + prev = cur; + cur = qdf_nbuf_next(cur); + } while (fragno > cur_fragno); + + if (fragno == cur_fragno) { + htt_rx_desc_frame_free(htt_pdev, frag); + *all_frag_present = 0; + return; + } + + qdf_nbuf_set_next(prev, frag); + qdf_nbuf_set_next(frag, cur); + } + next = qdf_nbuf_next(*head_addr); + lmac_hdr = (struct ieee80211_frame *)ol_rx_frag_get_mac_hdr(htt_pdev, + *tail_addr); + last_morefrag = lmac_hdr->i_fc[1] & IEEE80211_FC1_MORE_FRAG; + if (!last_morefrag) { + do { + next_hdr = + (struct ieee80211_frame *) + ol_rx_frag_get_mac_hdr(htt_pdev, next); + next_fragno = + qdf_le16_to_cpu(*(uint16_t *) next_hdr->i_seq) & + IEEE80211_SEQ_FRAG_MASK; + count++; + if (next_fragno != count) + break; + + next = qdf_nbuf_next(next); + } while (next); + + if (!next) { + *all_frag_present = 1; + return; + } + } + *all_frag_present = 0; +} + +/* + * add tid to pending fragment wait list + */ +void ol_rx_defrag_waitlist_add(struct ol_txrx_peer_t *peer, unsigned int tid) +{ + struct ol_txrx_pdev_t *pdev = peer->vdev->pdev; + struct ol_rx_reorder_t *rx_reorder = &peer->tids_rx_reorder[tid]; + + TAILQ_INSERT_TAIL(&pdev->rx.defrag.waitlist, rx_reorder, + defrag_waitlist_elem); +} + +/* + * remove tid from pending fragment wait list + */ +void ol_rx_defrag_waitlist_remove(struct ol_txrx_peer_t *peer, unsigned int tid) +{ + struct ol_txrx_pdev_t *pdev = peer->vdev->pdev; + struct ol_rx_reorder_t *rx_reorder = &peer->tids_rx_reorder[tid]; + + if (rx_reorder->defrag_waitlist_elem.tqe_next) { + + TAILQ_REMOVE(&pdev->rx.defrag.waitlist, rx_reorder, + defrag_waitlist_elem); + + rx_reorder->defrag_waitlist_elem.tqe_next = NULL; + rx_reorder->defrag_waitlist_elem.tqe_prev = NULL; + } else if (rx_reorder->defrag_waitlist_elem.tqe_next) { + ol_txrx_alert("waitlist->tqe_prv = NULL\n"); + QDF_ASSERT(0); + rx_reorder->defrag_waitlist_elem.tqe_next = NULL; + } +} + +#ifndef container_of +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr) - (char *)(&((type *)0)->member))) +#endif + +/* + * flush stale fragments from the waitlist + */ +void ol_rx_defrag_waitlist_flush(struct ol_txrx_pdev_t *pdev) +{ + struct ol_rx_reorder_t *rx_reorder, *tmp; + uint32_t now_ms = qdf_system_ticks_to_msecs(qdf_system_ticks()); + + TAILQ_FOREACH_SAFE(rx_reorder, &pdev->rx.defrag.waitlist, + defrag_waitlist_elem, tmp) { + struct ol_txrx_peer_t *peer; + struct ol_rx_reorder_t *rx_reorder_base; + unsigned int tid; + + if (rx_reorder->defrag_timeout_ms > now_ms) + break; + + tid = rx_reorder->tid; + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + ol_txrx_err("%s: invalid tid, %u\n", __FUNCTION__, tid); + WARN_ON(1); + continue; + } + /* get index 0 of the rx_reorder array */ + rx_reorder_base = rx_reorder - tid; + peer = + container_of(rx_reorder_base, struct ol_txrx_peer_t, + tids_rx_reorder[0]); + + ol_rx_defrag_waitlist_remove(peer, tid); + ol_rx_reorder_flush_frag(pdev->htt_pdev, peer, tid, + 0 /* frags always stored at seq 0 */); + } +} + +/* + * Handling security checking and processing fragments + */ +void +ol_rx_defrag(ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer, unsigned int tid, + qdf_nbuf_t frag_list) +{ + struct ol_txrx_vdev_t *vdev = NULL; + qdf_nbuf_t tmp_next, msdu, prev = NULL, cur = frag_list; + uint8_t index, tkip_demic = 0; + uint16_t hdr_space; + void *rx_desc; + struct ieee80211_frame *wh; + uint8_t key[DEFRAG_IEEE80211_KEY_LEN]; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + + vdev = peer->vdev; + + /* bypass defrag for safe mode */ + if (vdev->safemode) { + if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev)) + ol_rx_in_order_deliver(vdev, peer, tid, frag_list); + else + ol_rx_deliver(vdev, peer, tid, frag_list); + return; + } + + while (cur) { + tmp_next = qdf_nbuf_next(cur); + qdf_nbuf_set_next(cur, NULL); + /* + * Strict PN check between the first fragment of the current + * frame and the last fragment of the previous frame is not + * necessary. + */ + if (!ol_rx_pn_check_base(vdev, peer, tid, cur, + (cur == frag_list) ? false : true)) { + /* PN check failed,discard frags */ + if (prev) { + qdf_nbuf_set_next(prev, NULL); + ol_rx_frames_free(htt_pdev, frag_list); + } + ol_rx_frames_free(htt_pdev, tmp_next); + ol_txrx_err("PN Check failed"); + return; + } + /* remove FCS from each fragment */ + qdf_nbuf_trim_tail(cur, DEFRAG_IEEE80211_FCS_LEN); + prev = cur; + qdf_nbuf_set_next(cur, tmp_next); + cur = tmp_next; + } + cur = frag_list; + wh = (struct ieee80211_frame *)ol_rx_frag_get_mac_hdr(htt_pdev, cur); + hdr_space = ol_rx_frag_hdrsize(wh); + rx_desc = htt_rx_msdu_desc_retrieve(htt_pdev, frag_list); + qdf_assert(htt_rx_msdu_has_wlan_mcast_flag(htt_pdev, rx_desc)); + index = htt_rx_msdu_is_wlan_mcast(htt_pdev, rx_desc) ? + txrx_sec_mcast : txrx_sec_ucast; + + switch (peer->security[index].sec_type) { + case htt_sec_type_tkip: + tkip_demic = 1; + /* fall-through to rest of tkip ops */ + case htt_sec_type_tkip_nomic: + while (cur) { + tmp_next = qdf_nbuf_next(cur); + if (!ol_rx_frag_tkip_decap(pdev, cur, hdr_space)) { + /* TKIP decap failed, discard frags */ + ol_rx_frames_free(htt_pdev, frag_list); + ol_txrx_err("TKIP decap failed"); + return; + } + cur = tmp_next; + } + break; + + case htt_sec_type_aes_ccmp: + while (cur) { + tmp_next = qdf_nbuf_next(cur); + if (!ol_rx_frag_ccmp_demic(pdev, cur, hdr_space)) { + /* CCMP demic failed, discard frags */ + ol_rx_frames_free(htt_pdev, frag_list); + ol_txrx_err("CCMP demic failed"); + return; + } + if (!ol_rx_frag_ccmp_decap(pdev, cur, hdr_space)) { + /* CCMP decap failed, discard frags */ + ol_rx_frames_free(htt_pdev, frag_list); + ol_txrx_err("CCMP decap failed"); + return; + } + cur = tmp_next; + } + break; + + case htt_sec_type_wep40: + case htt_sec_type_wep104: + case htt_sec_type_wep128: + while (cur) { + tmp_next = qdf_nbuf_next(cur); + if (!ol_rx_frag_wep_decap(pdev, cur, hdr_space)) { + /* wep decap failed, discard frags */ + ol_rx_frames_free(htt_pdev, frag_list); + ol_txrx_err("wep decap failed"); + return; + } + cur = tmp_next; + } + break; + + default: + break; + } + + msdu = ol_rx_defrag_decap_recombine(htt_pdev, frag_list, hdr_space); + if (!msdu) + return; + + if (tkip_demic) { + qdf_mem_copy(key, + peer->security[index].michael_key, + sizeof(peer->security[index].michael_key)); + if (!ol_rx_frag_tkip_demic(pdev, key, msdu, hdr_space)) { + htt_rx_desc_frame_free(htt_pdev, msdu); + ol_rx_err(pdev->ctrl_pdev, + vdev->vdev_id, peer->mac_addr.raw, tid, 0, + OL_RX_DEFRAG_ERR, msdu, NULL, 0); + ol_txrx_err("TKIP demic failed"); + return; + } + } + wh = (struct ieee80211_frame *)ol_rx_frag_get_mac_hdr(htt_pdev, msdu); + if (DEFRAG_IEEE80211_QOS_HAS_SEQ(wh)) + ol_rx_defrag_qos_decap(pdev, msdu, hdr_space); + if (ol_cfg_frame_type(pdev->ctrl_pdev) == wlan_frm_fmt_802_3) + ol_rx_defrag_nwifi_to_8023(pdev, msdu); + + ol_rx_fwd_check(vdev, peer, tid, msdu); +} + +/* + * Handling TKIP processing for defragmentation + */ +int +ol_rx_frag_tkip_decap(ol_txrx_pdev_handle pdev, + qdf_nbuf_t msdu, uint16_t hdrlen) +{ + uint8_t *ivp, *origHdr; + + void *rx_desc_old_position = NULL; + void *ind_old_position = NULL; + int rx_desc_len = 0; + + ol_rx_frag_desc_adjust(pdev, + msdu, + &rx_desc_old_position, + &ind_old_position, &rx_desc_len); + /* Header should have extended IV */ + origHdr = (uint8_t *) (qdf_nbuf_data(msdu) + rx_desc_len); + + ivp = origHdr + hdrlen; + if (!(ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV)) + return OL_RX_DEFRAG_ERR; + + qdf_mem_move(origHdr + f_tkip.ic_header, origHdr, hdrlen); + ol_rx_frag_restructure( + pdev, + msdu, + rx_desc_old_position, + ind_old_position, + &f_tkip, + rx_desc_len); + qdf_nbuf_pull_head(msdu, f_tkip.ic_header); + qdf_nbuf_trim_tail(msdu, f_tkip.ic_trailer); + return OL_RX_DEFRAG_OK; +} + +/* + * Handling WEP processing for defragmentation + */ +int +ol_rx_frag_wep_decap(ol_txrx_pdev_handle pdev, qdf_nbuf_t msdu, uint16_t hdrlen) +{ + uint8_t *origHdr; + void *rx_desc_old_position = NULL; + void *ind_old_position = NULL; + int rx_desc_len = 0; + + ol_rx_frag_desc_adjust(pdev, + msdu, + &rx_desc_old_position, + &ind_old_position, &rx_desc_len); + origHdr = (uint8_t *) (qdf_nbuf_data(msdu) + rx_desc_len); + qdf_mem_move(origHdr + f_wep.ic_header, origHdr, hdrlen); + ol_rx_frag_restructure( + pdev, + msdu, + rx_desc_old_position, + ind_old_position, + &f_wep, + rx_desc_len); + qdf_nbuf_pull_head(msdu, f_wep.ic_header); + qdf_nbuf_trim_tail(msdu, f_wep.ic_trailer); + return OL_RX_DEFRAG_OK; +} + +/* + * Verify and strip MIC from the frame. + */ +int +ol_rx_frag_tkip_demic(ol_txrx_pdev_handle pdev, const uint8_t *key, + qdf_nbuf_t msdu, uint16_t hdrlen) +{ + int status; + uint32_t pktlen; + uint8_t mic[IEEE80211_WEP_MICLEN]; + uint8_t mic0[IEEE80211_WEP_MICLEN]; + void *rx_desc_old_position = NULL; + void *ind_old_position = NULL; + int rx_desc_len = 0; + + ol_rx_frag_desc_adjust(pdev, + msdu, + &rx_desc_old_position, + &ind_old_position, &rx_desc_len); + + pktlen = ol_rx_defrag_len(msdu) - rx_desc_len; + + status = ol_rx_defrag_mic(pdev, key, msdu, hdrlen, + pktlen - (hdrlen + f_tkip.ic_miclen), mic); + if (status != OL_RX_DEFRAG_OK) + return OL_RX_DEFRAG_ERR; + + ol_rx_defrag_copydata(msdu, pktlen - f_tkip.ic_miclen + rx_desc_len, + f_tkip.ic_miclen, (caddr_t) mic0); + if (qdf_mem_cmp(mic, mic0, f_tkip.ic_miclen)) + return OL_RX_DEFRAG_ERR; + + qdf_nbuf_trim_tail(msdu, f_tkip.ic_miclen); + return OL_RX_DEFRAG_OK; +} + +/* + * Handling CCMP processing for defragmentation + */ +int +ol_rx_frag_ccmp_decap(ol_txrx_pdev_handle pdev, + qdf_nbuf_t nbuf, uint16_t hdrlen) +{ + uint8_t *ivp, *origHdr; + void *rx_desc_old_position = NULL; + void *ind_old_position = NULL; + int rx_desc_len = 0; + + ol_rx_frag_desc_adjust(pdev, + nbuf, + &rx_desc_old_position, + &ind_old_position, &rx_desc_len); + + origHdr = (uint8_t *) (qdf_nbuf_data(nbuf) + rx_desc_len); + ivp = origHdr + hdrlen; + if (!(ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV)) + return OL_RX_DEFRAG_ERR; + + qdf_mem_move(origHdr + f_ccmp.ic_header, origHdr, hdrlen); + ol_rx_frag_restructure( + pdev, + nbuf, + rx_desc_old_position, + ind_old_position, + &f_ccmp, + rx_desc_len); + qdf_nbuf_pull_head(nbuf, f_ccmp.ic_header); + + return OL_RX_DEFRAG_OK; +} + +/* + * Verify and strip MIC from the frame. + */ +int +ol_rx_frag_ccmp_demic(ol_txrx_pdev_handle pdev, + qdf_nbuf_t wbuf, uint16_t hdrlen) +{ + uint8_t *ivp, *origHdr; + void *rx_desc_old_position = NULL; + void *ind_old_position = NULL; + int rx_desc_len = 0; + + ol_rx_frag_desc_adjust(pdev, + wbuf, + &rx_desc_old_position, + &ind_old_position, &rx_desc_len); + + origHdr = (uint8_t *) (qdf_nbuf_data(wbuf) + rx_desc_len); + + ivp = origHdr + hdrlen; + if (!(ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV)) + return OL_RX_DEFRAG_ERR; + + qdf_nbuf_trim_tail(wbuf, f_ccmp.ic_trailer); + + return OL_RX_DEFRAG_OK; +} + +/* + * Craft pseudo header used to calculate the MIC. + */ +void ol_rx_defrag_michdr(const struct ieee80211_frame *wh0, uint8_t hdr[]) +{ + const struct ieee80211_frame_addr4 *wh = + (const struct ieee80211_frame_addr4 *)wh0; + + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + DEFRAG_IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */ + DEFRAG_IEEE80211_ADDR_COPY(hdr + QDF_MAC_ADDR_SIZE, + wh->i_addr2); + break; + case IEEE80211_FC1_DIR_TODS: + DEFRAG_IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */ + DEFRAG_IEEE80211_ADDR_COPY(hdr + QDF_MAC_ADDR_SIZE, + wh->i_addr2); + break; + case IEEE80211_FC1_DIR_FROMDS: + DEFRAG_IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */ + DEFRAG_IEEE80211_ADDR_COPY(hdr + QDF_MAC_ADDR_SIZE, + wh->i_addr3); + break; + case IEEE80211_FC1_DIR_DSTODS: + DEFRAG_IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */ + DEFRAG_IEEE80211_ADDR_COPY(hdr + QDF_MAC_ADDR_SIZE, + wh->i_addr4); + break; + } + /* + * Bit 7 is QDF_IEEE80211_FC0_SUBTYPE_QOS for data frame, but + * it could also be set for deauth, disassoc, action, etc. for + * a mgt type frame. It comes into picture for MFP. + */ + if (wh->i_fc[0] & QDF_IEEE80211_FC0_SUBTYPE_QOS) { + const struct ieee80211_qosframe *qwh = + (const struct ieee80211_qosframe *)wh; + hdr[12] = qwh->i_qos[0] & IEEE80211_QOS_TID; + } else { + hdr[12] = 0; + } + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ +} + +/* + * Michael_mic for defragmentation + */ +int +ol_rx_defrag_mic(ol_txrx_pdev_handle pdev, + const uint8_t *key, + qdf_nbuf_t wbuf, + uint16_t off, uint16_t data_len, uint8_t mic[]) +{ + uint8_t hdr[16] = { 0, }; + uint32_t l, r; + const uint8_t *data; + uint32_t space; + void *rx_desc_old_position = NULL; + void *ind_old_position = NULL; + int rx_desc_len = 0; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + + ol_rx_frag_desc_adjust(pdev, + wbuf, + &rx_desc_old_position, + &ind_old_position, &rx_desc_len); + + ol_rx_defrag_michdr((struct ieee80211_frame *)(qdf_nbuf_data(wbuf) + + rx_desc_len), hdr); + l = get_le32(key); + r = get_le32(key + 4); + + /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */ + l ^= get_le32(hdr); + michael_block(l, r); + l ^= get_le32(&hdr[4]); + michael_block(l, r); + l ^= get_le32(&hdr[8]); + michael_block(l, r); + l ^= get_le32(&hdr[12]); + michael_block(l, r); + + /* first buffer has special handling */ + data = (uint8_t *) qdf_nbuf_data(wbuf) + rx_desc_len + off; + space = ol_rx_defrag_len(wbuf) - rx_desc_len - off; + for (;; ) { + if (space > data_len) + space = data_len; + + /* collect 32-bit blocks from current buffer */ + while (space >= sizeof(uint32_t)) { + l ^= get_le32(data); + michael_block(l, r); + data += sizeof(uint32_t); + space -= sizeof(uint32_t); + data_len -= sizeof(uint32_t); + } + if (data_len < sizeof(uint32_t)) + break; + + wbuf = qdf_nbuf_next(wbuf); + if (!wbuf) + return OL_RX_DEFRAG_ERR; + + rx_desc_len = ol_rx_get_desc_len(htt_pdev, wbuf, + &rx_desc_old_position); + + if (space != 0) { + const uint8_t *data_next; + /* + * Block straddles buffers, split references. + */ + data_next = + (uint8_t *) qdf_nbuf_data(wbuf) + rx_desc_len; + if ((ol_rx_defrag_len(wbuf) - rx_desc_len) < + sizeof(uint32_t) - space) { + return OL_RX_DEFRAG_ERR; + } + switch (space) { + case 1: + l ^= get_le32_split(data[0], data_next[0], + data_next[1], data_next[2]); + data = data_next + 3; + space = (ol_rx_defrag_len(wbuf) - rx_desc_len) + - 3; + break; + case 2: + l ^= get_le32_split(data[0], data[1], + data_next[0], data_next[1]); + data = data_next + 2; + space = (ol_rx_defrag_len(wbuf) - rx_desc_len) + - 2; + break; + case 3: + l ^= get_le32_split(data[0], data[1], data[2], + data_next[0]); + data = data_next + 1; + space = (ol_rx_defrag_len(wbuf) - rx_desc_len) + - 1; + break; + } + michael_block(l, r); + data_len -= sizeof(uint32_t); + } else { + /* + * Setup for next buffer. + */ + data = (uint8_t *) qdf_nbuf_data(wbuf) + rx_desc_len; + space = ol_rx_defrag_len(wbuf) - rx_desc_len; + } + } + /* Last block and padding (0x5a, 4..7 x 0) */ + switch (data_len) { + case 0: + l ^= get_le32_split(0x5a, 0, 0, 0); + break; + case 1: + l ^= get_le32_split(data[0], 0x5a, 0, 0); + break; + case 2: + l ^= get_le32_split(data[0], data[1], 0x5a, 0); + break; + case 3: + l ^= get_le32_split(data[0], data[1], data[2], 0x5a); + break; + } + michael_block(l, r); + michael_block(l, r); + put_le32(mic, l); + put_le32(mic + 4, r); + + return OL_RX_DEFRAG_OK; +} + +/* + * Calculate headersize + */ +uint16_t ol_rx_frag_hdrsize(const void *data) +{ + const struct ieee80211_frame *wh = (const struct ieee80211_frame *)data; + uint16_t size = sizeof(struct ieee80211_frame); + + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) + size += QDF_MAC_ADDR_SIZE; + + if (DEFRAG_IEEE80211_QOS_HAS_SEQ(wh)) { + size += sizeof(uint16_t); + if (wh->i_fc[1] & IEEE80211_FC1_ORDER) + size += sizeof(struct ieee80211_htc); + } + return size; +} + +/* + * Recombine and decap fragments + */ +qdf_nbuf_t +ol_rx_defrag_decap_recombine(htt_pdev_handle htt_pdev, + qdf_nbuf_t frag_list, uint16_t hdrsize) +{ + qdf_nbuf_t tmp; + qdf_nbuf_t msdu = frag_list; + qdf_nbuf_t rx_nbuf = frag_list; + struct ieee80211_frame *wh; + + msdu = qdf_nbuf_next(msdu); + qdf_nbuf_set_next(rx_nbuf, NULL); + while (msdu) { + htt_rx_msdu_desc_free(htt_pdev, msdu); + tmp = qdf_nbuf_next(msdu); + qdf_nbuf_set_next(msdu, NULL); + ol_rx_frag_pull_hdr(htt_pdev, msdu, hdrsize); + if (!ol_rx_defrag_concat(rx_nbuf, msdu)) { + ol_rx_frames_free(htt_pdev, tmp); + htt_rx_desc_frame_free(htt_pdev, rx_nbuf); + qdf_nbuf_free(msdu); + /* msdu rx desc already freed above */ + return NULL; + } + msdu = tmp; + } + wh = (struct ieee80211_frame *)ol_rx_frag_get_mac_hdr(htt_pdev, + rx_nbuf); + wh->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG; + *(uint16_t *) wh->i_seq &= ~IEEE80211_SEQ_FRAG_MASK; + + return rx_nbuf; +} + +void ol_rx_defrag_nwifi_to_8023(ol_txrx_pdev_handle pdev, qdf_nbuf_t msdu) +{ + struct ieee80211_frame wh; + uint32_t hdrsize; + struct llc_snap_hdr_t llchdr; + struct ethernet_hdr_t *eth_hdr; + void *rx_desc_old_position = NULL; + void *ind_old_position = NULL; + int rx_desc_len = 0; + struct ieee80211_frame *wh_ptr; + + ol_rx_frag_desc_adjust(pdev, + msdu, + &rx_desc_old_position, + &ind_old_position, &rx_desc_len); + + wh_ptr = (struct ieee80211_frame *)(qdf_nbuf_data(msdu) + rx_desc_len); + qdf_mem_copy(&wh, wh_ptr, sizeof(wh)); + hdrsize = sizeof(struct ieee80211_frame); + qdf_mem_copy(&llchdr, ((uint8_t *) (qdf_nbuf_data(msdu) + + rx_desc_len)) + hdrsize, + sizeof(struct llc_snap_hdr_t)); + + /* + * Now move the data pointer to the beginning of the mac header : + * new-header = old-hdr + (wifhdrsize + llchdrsize - ethhdrsize) + */ + qdf_nbuf_pull_head(msdu, (rx_desc_len + hdrsize + + sizeof(struct llc_snap_hdr_t) - + sizeof(struct ethernet_hdr_t))); + eth_hdr = (struct ethernet_hdr_t *)(qdf_nbuf_data(msdu)); + switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + qdf_mem_copy(eth_hdr->dest_addr, wh.i_addr1, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(eth_hdr->src_addr, wh.i_addr2, QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_TODS: + qdf_mem_copy(eth_hdr->dest_addr, wh.i_addr3, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(eth_hdr->src_addr, wh.i_addr2, QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_FROMDS: + qdf_mem_copy(eth_hdr->dest_addr, wh.i_addr1, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(eth_hdr->src_addr, wh.i_addr3, QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_DSTODS: + break; + } + + qdf_mem_copy(eth_hdr->ethertype, llchdr.ethertype, + sizeof(llchdr.ethertype)); + + ol_rx_defrag_push_rx_desc(msdu, rx_desc_old_position, + ind_old_position, rx_desc_len); +} + +/* + * Handling QOS for defragmentation + */ +void +ol_rx_defrag_qos_decap(ol_txrx_pdev_handle pdev, + qdf_nbuf_t nbuf, uint16_t hdrlen) +{ + struct ieee80211_frame *wh; + uint16_t qoslen; + void *rx_desc_old_position = NULL; + void *ind_old_position = NULL; + int rx_desc_len = 0; + + ol_rx_frag_desc_adjust(pdev, + nbuf, + &rx_desc_old_position, + &ind_old_position, &rx_desc_len); + + wh = (struct ieee80211_frame *)(qdf_nbuf_data(nbuf) + rx_desc_len); + if (DEFRAG_IEEE80211_QOS_HAS_SEQ(wh)) { + qoslen = sizeof(struct ieee80211_qoscntl); + /* Qos frame with Order bit set indicates a HTC frame */ + if (wh->i_fc[1] & IEEE80211_FC1_ORDER) + qoslen += sizeof(struct ieee80211_htc); + + /* remove QoS filed from header */ + hdrlen -= qoslen; + qdf_mem_move((uint8_t *) wh + qoslen, wh, hdrlen); + wh = (struct ieee80211_frame *)qdf_nbuf_pull_head(nbuf, + rx_desc_len + + qoslen); + /* clear QoS bit */ + /* + * KW# 6154 'qdf_nbuf_pull_head' in turn calls + * __qdf_nbuf_pull_head, + * which returns NULL if there is not sufficient data to pull. + * It's guaranteed that qdf_nbuf_pull_head will succeed rather + * than returning NULL, since the entire rx frame is already + * present in the rx buffer. + * However, to make it obvious to static analyzers that this + * code is safe, add an explicit check that qdf_nbuf_pull_head + * returns a non-NULL value. + * Since this part of the code is not performance-critical, + * adding this explicit check is okay. + */ + if (wh) + wh->i_fc[0] &= ~QDF_IEEE80211_FC0_SUBTYPE_QOS; + + ol_rx_defrag_push_rx_desc(nbuf, rx_desc_old_position, + ind_old_position, rx_desc_len); + + } +} diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.h new file mode 100644 index 0000000000000000000000000000000000000000..96f0c33248a0e30f5e63862fae3393e8ab80fa66 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2011-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_RX_DEFRAG_H_ +#define _OL_RX_DEFRAG_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define DEFRAG_IEEE80211_KEY_LEN 8 +#define DEFRAG_IEEE80211_FCS_LEN 4 + +struct ol_rx_defrag_cipher { + const char *ic_name; + uint16_t ic_header; + uint8_t ic_trailer; + uint8_t ic_miclen; +}; + +enum { + OL_RX_DEFRAG_ERR, + OL_RX_DEFRAG_OK, + OL_RX_DEFRAG_PN_ERR +}; + +#define ol_rx_defrag_copydata(buf, offset, len, _to) \ + qdf_nbuf_copy_bits(buf, offset, len, _to) + +#define ol_rx_defrag_len(buf) \ + qdf_nbuf_len(buf) + +void +ol_rx_fraglist_insert(htt_pdev_handle htt_pdev, + qdf_nbuf_t *head_addr, + qdf_nbuf_t *tail_addr, + qdf_nbuf_t frag, uint8_t *all_frag_present); + +void ol_rx_defrag_waitlist_add(struct ol_txrx_peer_t *peer, unsigned int tid); + +void ol_rx_defrag_waitlist_remove(struct ol_txrx_peer_t *peer, + unsigned int tid); + +void ol_rx_defrag_waitlist_flush(struct ol_txrx_pdev_t *pdev); + +void +ol_rx_defrag(ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer, unsigned int tid, + qdf_nbuf_t frag_list); + +int +ol_rx_frag_tkip_decap(ol_txrx_pdev_handle pdev, + qdf_nbuf_t msdu, uint16_t hdrlen); + +int +ol_rx_frag_wep_decap(ol_txrx_pdev_handle pdev, + qdf_nbuf_t nbuf, uint16_t hdrlen); + +void ol_rx_defrag_nwifi_to_8023(ol_txrx_pdev_handle pdev, qdf_nbuf_t msdu); + +void +ol_rx_defrag_qos_decap(ol_txrx_pdev_handle pdev, + qdf_nbuf_t nbuf, uint16_t hdrlen); + +int +ol_rx_frag_tkip_demic(ol_txrx_pdev_handle pdev, + const uint8_t *key, qdf_nbuf_t msdu, uint16_t hdrlen); + +int +ol_rx_frag_ccmp_decap(ol_txrx_pdev_handle pdev, + qdf_nbuf_t nbuf, uint16_t hdrlen); + +int +ol_rx_frag_ccmp_demic(ol_txrx_pdev_handle pdev, + qdf_nbuf_t wbuf, uint16_t hdrlen); + +uint16_t ol_rx_frag_hdrsize(const void *data); + +void ol_rx_defrag_michdr(const struct ieee80211_frame *wh0, uint8_t hdr[]); + +void +ol_rx_reorder_store_frag(ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, uint16_t seq_num, qdf_nbuf_t frag); + +qdf_nbuf_t +ol_rx_defrag_decap_recombine(htt_pdev_handle htt_pdev, + qdf_nbuf_t frag_list, uint16_t hdrsize); + +int +ol_rx_defrag_mic(ol_txrx_pdev_handle pdev, + const uint8_t *key, + qdf_nbuf_t wbuf, + uint16_t off, uint16_t data_len, uint8_t mic[]); + +void +ol_rx_reorder_flush_frag(htt_pdev_handle htt_pdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, uint16_t seq_num); + +static inline void xor_block(uint8_t *b, const uint8_t *a, qdf_size_t len) +{ + qdf_size_t i; + + for (i = 0; i < len; i++) + b[i] ^= a[i]; +} + +static inline uint32_t rotl(uint32_t val, int bits) +{ + return (val << bits) | (val >> (32 - bits)); +} + +static inline uint32_t rotr(uint32_t val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + +static inline uint32_t xswap(uint32_t val) +{ + return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8); +} + +static inline uint32_t +get_le32_split(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) +{ + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); +} + +static inline uint32_t get_le32(const uint8_t *p) +{ + return get_le32_split(p[0], p[1], p[2], p[3]); +} + +static inline void put_le32(uint8_t *p, uint32_t v) +{ + p[0] = (v) & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + +static inline uint8_t ol_rx_defrag_concat(qdf_nbuf_t dst, qdf_nbuf_t src) +{ + /* + * Inside qdf_nbuf_cat, if it is necessary to reallocate dst + * to provide space for src, the headroom portion is copied from + * the original dst buffer to the larger new dst buffer. + * (This is needed, because the headroom of the dst buffer + * contains the rx desc.) + */ + if (qdf_nbuf_cat(dst, src)) + return OL_RX_DEFRAG_ERR; + + /* Free source buffer */ + qdf_nbuf_free(src); + + return OL_RX_DEFRAG_OK; +} + +#define michael_block(l, r) \ + do { \ + r ^= rotl(l, 17); \ + l += r; \ + r ^= xswap(l); \ + l += r; \ + r ^= rotl(l, 3); \ + l += r; \ + r ^= rotr(l, 2); \ + l += r; \ + } while (0) + +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_fwd.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_fwd.c new file mode 100644 index 0000000000000000000000000000000000000000..96925f14596fb6e418681fda965ac41d6eb11b96 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_fwd.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2011, 2014-2019, 2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* standard header files */ +#include /* qdf_nbuf_map */ +#include /* qdf_mem_cmp */ + +/* external header files */ +#include /* wlan_op_mode_ap, etc. */ +#include /* htt_rx_msdu_desc_retrieve */ +#include /* ieee80211_frame, etc. */ + +/* internal header files */ +#include /* our own defs */ +#include /* ol_rx_deliver */ +#include /* TXRX_ASSERT1 */ +#include +#include + +/* + * Porting from Ap11PrepareForwardedPacket. + * This routine is called when a RX data frame from an associated station is + * to be forwarded to another associated station. We will prepare the + * received packet so that it is suitable for transmission again. + * Check that this Packet is suitable for forwarding. If yes, then + * prepare the new 802.11 header. + */ +static inline void ol_ap_fwd_check(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu) +{ + struct ieee80211_frame *mac_header; + unsigned char tmp_addr[QDF_MAC_ADDR_SIZE]; + unsigned char type; + unsigned char subtype; + unsigned char fromds; + unsigned char tods; + + mac_header = (struct ieee80211_frame *)(qdf_nbuf_data(msdu)); + TXRX_ASSERT1(mac_header); + + type = mac_header->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = mac_header->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + tods = mac_header->i_fc[1] & IEEE80211_FC1_DIR_TODS; + fromds = mac_header->i_fc[1] & IEEE80211_FC1_DIR_FROMDS; + + /* + * Make sure no QOS or any other non-data subtype + * Should be a ToDs data frame. + * Make sure that this frame is unicast and not for us. + * These packets should come up through the normal rx path and + * not forwarded. + */ + if (type != IEEE80211_FC0_TYPE_DATA || + subtype != 0x0 || + ((tods != 1) || (fromds != 0)) || + qdf_mem_cmp + (mac_header->i_addr3, vdev->mac_addr.raw, + QDF_MAC_ADDR_SIZE)) { + ol_txrx_dbg("Exit | Unnecessary to adjust mac header"); + } else { + /* Flip the ToDs bit to FromDs */ + mac_header->i_fc[1] &= 0xfe; + mac_header->i_fc[1] |= 0x2; + + /* + * Flip the addresses + * (ToDs, addr1, RA=BSSID) move to (FrDs, addr2, TA=BSSID) + * (ToDs, addr2, SA) move to (FrDs, addr3, SA) + * (ToDs, addr3, DA) move to (FrDs, addr1, DA) + */ + + memcpy(tmp_addr, mac_header->i_addr2, sizeof(tmp_addr)); + + memcpy(mac_header->i_addr2, + mac_header->i_addr1, sizeof(tmp_addr)); + + memcpy(mac_header->i_addr1, + mac_header->i_addr3, sizeof(tmp_addr)); + + memcpy(mac_header->i_addr3, tmp_addr, sizeof(tmp_addr)); + } +} + +static inline void ol_rx_fwd_to_tx(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + + if (pdev->frame_format == wlan_frm_fmt_native_wifi) + ol_ap_fwd_check(vdev, msdu); + + /* + * Map the netbuf, so it's accessible to the DMA that + * sends it to the target. + */ + qdf_nbuf_set_next(msdu, NULL); /* add NULL terminator */ + + /* for HL, point to payload before send to tx again.*/ + if (pdev->cfg.is_high_latency) { + void *rx_desc; + + rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, + msdu); + qdf_nbuf_pull_head(msdu, + htt_rx_msdu_rx_desc_size_hl(pdev->htt_pdev, + rx_desc)); + } + + /* Clear the msdu control block as it will be re-interpreted */ + qdf_mem_zero(msdu->cb, sizeof(msdu->cb)); + /* update any cb field expected by OL_TX_SEND */ + + msdu = OL_TX_SEND(vdev, msdu); + + if (msdu) { + /* + * The frame was not accepted by the tx. + * We could store the frame and try again later, + * but the simplest solution is to discard the frames. + */ + qdf_nbuf_tx_free(msdu, QDF_NBUF_PKT_ERROR); + } +} + +void +ol_rx_fwd_check(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu_list) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + qdf_nbuf_t deliver_list_head = NULL; + qdf_nbuf_t deliver_list_tail = NULL; + qdf_nbuf_t msdu; + + msdu = msdu_list; + while (msdu) { + struct ol_txrx_vdev_t *tx_vdev; + void *rx_desc; + uint16_t off = 0; + /* + * Remember the next list elem, because our processing + * may cause the MSDU to get linked into a different list. + */ + msdu_list = qdf_nbuf_next(msdu); + + rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu); + + if (!vdev->disable_intrabss_fwd && + htt_rx_msdu_forward(pdev->htt_pdev, rx_desc)) { + /* + * Use the same vdev that received the frame to + * transmit the frame. + * This is exactly what we want for intra-BSS + * forwarding, like STA-to-STA forwarding and + * multicast echo. + * If this is a intra-BSS forwarding case (which is not + * currently supported), then the tx vdev is different + * from the rx vdev. + * On the LL host the vdevs are not actually used + * for tx, so it would still work to use the rx vdev + * rather than the tx vdev. + * For HL, the tx classification searches for the DA + * within the given vdev, so we would want to get the DA + * peer ID from the target, so we can locate + * the tx vdev. + */ + tx_vdev = vdev; + /* + * Copying TID value of RX packet to forwarded + * packet if the tid is other than non qos tid. + * But for non qos tid fill invalid tid so that + * Fw will take care of filling proper tid. + */ + if (tid != HTT_NON_QOS_TID) { + qdf_nbuf_set_tid(msdu, tid); + } else { + qdf_nbuf_set_tid(msdu, + QDF_NBUF_TX_EXT_TID_INVALID); + } + + if (!ol_txrx_fwd_desc_thresh_check(vdev)) { + /* Drop the packet*/ + htt_rx_msdu_desc_free(pdev->htt_pdev, msdu); + TXRX_STATS_MSDU_LIST_INCR( + pdev, tx.dropped.host_reject, msdu); + /* add NULL terminator */ + qdf_nbuf_set_next(msdu, NULL); + qdf_nbuf_tx_free(msdu, + QDF_NBUF_PKT_ERROR); + msdu = msdu_list; + continue; + } + if (pdev->cfg.is_high_latency) + off = htt_rx_msdu_rx_desc_size_hl( + pdev->htt_pdev, + rx_desc); + + if (pdev->cfg.is_high_latency) + off = htt_rx_msdu_rx_desc_size_hl( + pdev->htt_pdev, + rx_desc); + + if (vdev->opmode == wlan_op_mode_ap && + __qdf_nbuf_data_is_ipv4_eapol_pkt( + qdf_nbuf_data(msdu) + off) && + qdf_mem_cmp(qdf_nbuf_data(msdu) + + QDF_NBUF_DEST_MAC_OFFSET, + vdev->mac_addr.raw, + QDF_MAC_ADDR_SIZE)) { + TXRX_STATS_MSDU_LIST_INCR( + pdev, tx.dropped.host_reject, msdu); + qdf_nbuf_set_next(msdu, NULL); + qdf_nbuf_tx_free(msdu, QDF_NBUF_PKT_ERROR); + msdu = msdu_list; + continue; + } + + /* + * This MSDU needs to be forwarded to the tx path. + * Check whether it also needs to be sent to the OS + * shim, in which case we need to make a copy + * (or clone?). + */ + if (htt_rx_msdu_discard(pdev->htt_pdev, rx_desc)) { + htt_rx_msdu_desc_free(pdev->htt_pdev, msdu); + ol_rx_fwd_to_tx(tx_vdev, msdu); + msdu = NULL; /* already handled this MSDU */ + tx_vdev->fwd_tx_packets++; + vdev->fwd_rx_packets++; + TXRX_STATS_ADD(pdev, + pub.rx.intra_bss_fwd.packets_fwd, 1); + } else { + qdf_nbuf_t copy; + + copy = qdf_nbuf_copy(msdu); + if (copy) { + ol_rx_fwd_to_tx(tx_vdev, copy); + tx_vdev->fwd_tx_packets++; + } + TXRX_STATS_ADD(pdev, + pub.rx.intra_bss_fwd.packets_stack_n_fwd, 1); + } + } else { + TXRX_STATS_ADD(pdev, + pub.rx.intra_bss_fwd.packets_stack, 1); + } + if (msdu) { + /* send this frame to the OS */ + OL_TXRX_LIST_APPEND(deliver_list_head, + deliver_list_tail, msdu); + } + msdu = msdu_list; + } + if (deliver_list_head) { + /* add NULL terminator */ + qdf_nbuf_set_next(deliver_list_tail, NULL); + if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev)) { + ol_rx_in_order_deliver(vdev, peer, tid, + deliver_list_head); + } else { + ol_rx_deliver(vdev, peer, tid, deliver_list_head); + } + } +} + +/* + * ol_get_intra_bss_fwd_pkts_count() - to get the total tx and rx packets + * that has been forwarded from txrx layer without going to upper layers. + * @soc_hdl: Datapath soc handle + * @vdev_id: vdev id + * @fwd_tx_packets: pointer to forwarded tx packets count parameter + * @fwd_rx_packets: pointer to forwarded rx packets count parameter + * + * Return: status -> A_OK - success, A_ERROR - failure + */ +A_STATUS ol_get_intra_bss_fwd_pkts_count(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + uint64_t *fwd_tx_packets, + uint64_t *fwd_rx_packets) +{ + struct ol_txrx_vdev_t *vdev = NULL; + + vdev = (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + if (!vdev) + return A_ERROR; + + *fwd_tx_packets = vdev->fwd_tx_packets; + *fwd_rx_packets = vdev->fwd_rx_packets; + return A_OK; +} + diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_fwd.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_fwd.h new file mode 100644 index 0000000000000000000000000000000000000000..c2c5947f9504e7b75ac39517aea9056e6af48be3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_fwd.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011, 2014-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_RX_FWD_H_ +#define _OL_RX_FWD_H_ + +#include /* qdf_nbuf_t, etc. */ + +#include /* ol_txrx_peer_t, etc. */ + +qdf_nbuf_t +ol_rx_fwd_mcast_check_sta(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + qdf_nbuf_t msdu, void *rx_desc, int is_wlan_mcast); + +qdf_nbuf_t +ol_rx_fwd_mcast_check_ap(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + qdf_nbuf_t msdu, void *rx_desc, int is_wlan_mcast); + +/** + * @brief Check if rx frames should be transmitted over WLAN. + * @details + * Check if rx frames should be transmitted back over WLAN, instead of + * or in addition to delivering the rx frames to the OS. + * Rx frames will be forwarded to the transmit path under the following + * conditions: + * 1. If the destination is a STA associated to the same virtual device + * within this physical device, the rx frame will be forwarded to the + * tx path rather than being sent to the OS. If the destination is a + * STA associated to a different virtual device within this physical + * device, then the rx frame will optionally be forwarded to the tx path. + * 2. If the frame is received by an AP, but the destination is for another + * AP that the current AP is associated with for WDS forwarding, the + * intermediate AP will forward the rx frame to the tx path to transmit + * to send to the destination AP, rather than sending it to the OS. + * 3. If the AP receives a multicast frame, it will retransmit the frame + * within the BSS, in addition to sending the frame to the OS. + * + * @param vdev - which virtual device the frames were addressed to + * @param peer - which peer the rx frames belong to + * @param tid - which TID within the peer the rx frames belong to + * @param msdu_list - NULL-terminated list of MSDUs to perform the rx->tx + * forwarding check on + */ +void +ol_rx_fwd_check(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu_list); + +A_STATUS +ol_get_intra_bss_fwd_pkts_count(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + uint64_t *fwd_tx_packets, + uint64_t *fwd_rx_packets); + +#endif /* _OL_RX_FWD_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_pn.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_pn.c new file mode 100644 index 0000000000000000000000000000000000000000..2cb97488d1ed5ae18607d5a478e37c048d339456 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_pn.c @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2011, 2013-2017, 2019, 2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_nbuf_t */ + +#include /* htt_rx_pn_t, etc. */ +#include /* ol_rx_err */ + +#include /* ol_rx_mpdu_list_next */ +#include /* our own defs */ +#include /* ol_rx_fwd_check */ +#include /* ol_rx_deliver */ + +/* add the MSDUs from this MPDU to the list of good frames */ +#define ADD_MPDU_TO_LIST(head, tail, mpdu, mpdu_tail) do { \ + if (!head) { \ + head = mpdu; \ + } else { \ + qdf_nbuf_set_next(tail, mpdu); \ + } \ + tail = mpdu_tail; \ + } while (0) + +int ol_rx_pn_cmp24(union htt_rx_pn_t *new_pn, + union htt_rx_pn_t *old_pn, int is_unicast, int opmode, + bool strict_chk) +{ + if (strict_chk) + return ((new_pn->pn24 & 0xffffff) - (old_pn->pn24 & 0xffffff) + != 1); + else + return ((new_pn->pn24 & 0xffffff) <= (old_pn->pn24 & 0xffffff)); +} + +int ol_rx_pn_cmp48(union htt_rx_pn_t *new_pn, + union htt_rx_pn_t *old_pn, int is_unicast, int opmode, + bool strict_chk) +{ + if (strict_chk) + return ((new_pn->pn48 & 0xffffffffffffULL) - + (old_pn->pn48 & 0xffffffffffffULL) != 1); + else + return ((new_pn->pn48 & 0xffffffffffffULL) <= + (old_pn->pn48 & 0xffffffffffffULL)); +} + +int ol_rx_pn_wapi_cmp(union htt_rx_pn_t *new_pn, + union htt_rx_pn_t *old_pn, int is_unicast, int opmode, + bool strict_chk) +{ + int pn_is_replay = 0; + + /* TODO Strick check for WAPI is not implemented*/ + + if (new_pn->pn128[1] == old_pn->pn128[1]) + pn_is_replay = (new_pn->pn128[0] <= old_pn->pn128[0]); + else + pn_is_replay = (new_pn->pn128[1] < old_pn->pn128[1]); + + if (is_unicast) { + if (opmode == wlan_op_mode_ap) + pn_is_replay |= ((new_pn->pn128[0] & 0x1ULL) != 0); + else + pn_is_replay |= ((new_pn->pn128[0] & 0x1ULL) != 1); + } + return pn_is_replay; +} + +qdf_nbuf_t +ol_rx_pn_check_base(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu_list, bool strict_chk) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + union htt_rx_pn_t *last_pn; + qdf_nbuf_t out_list_head = NULL; + qdf_nbuf_t out_list_tail = NULL; + qdf_nbuf_t mpdu; + int index; /* unicast vs. multicast */ + int pn_len; + void *rx_desc; + int last_pn_valid; + + /* Make sure host pn check is not redundant */ + if ((qdf_atomic_read(&peer->fw_pn_check)) || + (vdev->opmode == wlan_op_mode_ibss)) { + return msdu_list; + } + + /* First, check whether the PN check applies */ + rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu_list); + qdf_assert(htt_rx_msdu_has_wlan_mcast_flag(pdev->htt_pdev, rx_desc)); + index = htt_rx_msdu_is_wlan_mcast(pdev->htt_pdev, rx_desc) ? + txrx_sec_mcast : txrx_sec_ucast; + pn_len = pdev->rx_pn[peer->security[index].sec_type].len; + if (pn_len == 0) + return msdu_list; + + last_pn_valid = peer->tids_last_pn_valid[tid]; + last_pn = &peer->tids_last_pn[tid]; + mpdu = msdu_list; + while (mpdu) { + qdf_nbuf_t mpdu_tail, next_mpdu; + union htt_rx_pn_t new_pn; + int pn_is_replay = 0; + + rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, mpdu); + + /* + * Find the last MSDU within this MPDU, and + * the find the first MSDU within the next MPDU. + */ + ol_rx_mpdu_list_next(pdev, mpdu, &mpdu_tail, &next_mpdu); + + /* Don't check the PN replay for non-encrypted frames */ + if (!htt_rx_mpdu_is_encrypted(pdev->htt_pdev, rx_desc)) { + ADD_MPDU_TO_LIST(out_list_head, out_list_tail, + mpdu, mpdu_tail); + mpdu = next_mpdu; + continue; + } + + /* retrieve PN from rx descriptor */ + htt_rx_mpdu_desc_pn(pdev->htt_pdev, rx_desc, &new_pn, pn_len); + + /* if there was no prior PN, there's nothing to check */ + if (last_pn_valid) { + pn_is_replay = + pdev->rx_pn[peer->security[index].sec_type]. + cmp(&new_pn, last_pn, index == txrx_sec_ucast, + vdev->opmode, strict_chk); + } else { + last_pn_valid = peer->tids_last_pn_valid[tid] = 1; + } + + if (pn_is_replay) { + qdf_nbuf_t msdu; + static uint32_t last_pncheck_print_time /* = 0 */; + uint32_t current_time_ms; + + /* + * This MPDU failed the PN check: + * 1. notify the control SW of the PN failure + * (so countermeasures can be taken, if necessary) + * 2. Discard all the MSDUs from this MPDU. + */ + msdu = mpdu; + current_time_ms = + qdf_system_ticks_to_msecs(qdf_system_ticks()); + if (TXRX_PN_CHECK_FAILURE_PRINT_PERIOD_MS < + (current_time_ms - last_pncheck_print_time)) { + last_pncheck_print_time = current_time_ms; + ol_txrx_warn( + "PN check failed - TID %d, peer %pK " + "("QDF_MAC_ADDR_FMT") %s\n" + " old PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n" + " new PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n" + " new seq num = %d\n", + tid, peer, + QDF_MAC_ADDR_REF(peer->mac_addr.raw), + (index == + txrx_sec_ucast) ? "ucast" : "mcast", + last_pn->pn128[1], last_pn->pn128[0], + last_pn->pn128[0] & 0xffffffffffffULL, + new_pn.pn128[1], new_pn.pn128[0], + new_pn.pn128[0] & 0xffffffffffffULL, + htt_rx_mpdu_desc_seq_num(pdev->htt_pdev, + rx_desc)); + } else { + ol_txrx_dbg( + "PN check failed - TID %d, peer %pK " + "("QDF_MAC_ADDR_FMT") %s\n" + " old PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n" + " new PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n" + " new seq num = %d\n", + tid, peer, + QDF_MAC_ADDR_REF(peer->mac_addr.raw), + (index == + txrx_sec_ucast) ? "ucast" : "mcast", + last_pn->pn128[1], last_pn->pn128[0], + last_pn->pn128[0] & 0xffffffffffffULL, + new_pn.pn128[1], new_pn.pn128[0], + new_pn.pn128[0] & 0xffffffffffffULL, + htt_rx_mpdu_desc_seq_num(pdev->htt_pdev, + rx_desc)); + } +#if defined(ENABLE_RX_PN_TRACE) + ol_rx_pn_trace_display(pdev, 1); +#endif /* ENABLE_RX_PN_TRACE */ + ol_rx_err(pdev->ctrl_pdev, + vdev->vdev_id, peer->mac_addr.raw, tid, + htt_rx_mpdu_desc_tsf32(pdev->htt_pdev, + rx_desc), OL_RX_ERR_PN, + mpdu, NULL, 0); + /* free all MSDUs within this MPDU */ + do { + qdf_nbuf_t next_msdu; + + OL_RX_ERR_STATISTICS_1(pdev, vdev, peer, + rx_desc, OL_RX_ERR_PN); + next_msdu = qdf_nbuf_next(msdu); + htt_rx_desc_frame_free(pdev->htt_pdev, msdu); + if (msdu == mpdu_tail) + break; + msdu = next_msdu; + } while (1); + } else { + ADD_MPDU_TO_LIST(out_list_head, out_list_tail, + mpdu, mpdu_tail); + /* + * Remember the new PN. + * For simplicity, just do 2 64-bit word copies to + * cover the worst case (WAPI), regardless of the length + * of the PN. + * This is more efficient than doing a conditional + * branch to copy only the relevant portion. + + * IWNCOM AP will send 1 packet with old PN after USK + * rekey, don't update last_pn when recv the packet, or + * PN check failed for later packets + */ + if ((peer->security[index].sec_type + == htt_sec_type_wapi) && + (peer->tids_rekey_flag[tid] == 1) && + (index == txrx_sec_ucast)) { + peer->tids_rekey_flag[tid] = 0; + } else { + last_pn->pn128[0] = new_pn.pn128[0]; + last_pn->pn128[1] = new_pn.pn128[1]; + OL_RX_PN_TRACE_ADD(pdev, peer, tid, rx_desc); + } + } + + mpdu = next_mpdu; + } + /* make sure the list is null-terminated */ + if (out_list_tail) + qdf_nbuf_set_next(out_list_tail, NULL); + + return out_list_head; +} + +void +ol_rx_pn_check(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, unsigned int tid, + qdf_nbuf_t msdu_list) +{ + msdu_list = ol_rx_pn_check_base(vdev, peer, tid, msdu_list, false); + ol_rx_fwd_check(vdev, peer, tid, msdu_list); +} + +void +ol_rx_pn_check_only(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu_list) +{ + msdu_list = ol_rx_pn_check_base(vdev, peer, tid, msdu_list, false); + ol_rx_deliver(vdev, peer, tid, msdu_list); +} + +#if defined(ENABLE_RX_PN_TRACE) + +A_STATUS ol_rx_pn_trace_attach(ol_txrx_pdev_handle pdev) +{ + int num_elems; + + num_elems = 1 << TXRX_RX_PN_TRACE_SIZE_LOG2; + pdev->rx_pn_trace.idx = 0; + pdev->rx_pn_trace.cnt = 0; + pdev->rx_pn_trace.mask = num_elems - 1; + pdev->rx_pn_trace.data = + qdf_mem_malloc(sizeof(*pdev->rx_pn_trace.data) * num_elems); + if (!pdev->rx_pn_trace.data) + return A_NO_MEMORY; + return A_OK; +} + +void ol_rx_pn_trace_detach(ol_txrx_pdev_handle pdev) +{ + qdf_mem_free(pdev->rx_pn_trace.data); +} + +void +ol_rx_pn_trace_add(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, uint16_t tid, void *rx_desc) +{ + uint32_t idx = pdev->rx_pn_trace.idx; + union htt_rx_pn_t pn; + uint32_t pn32; + uint16_t seq_num; + uint8_t unicast; + + htt_rx_mpdu_desc_pn(pdev->htt_pdev, rx_desc, &pn, 48); + pn32 = pn.pn48 & 0xffffffff; + seq_num = htt_rx_mpdu_desc_seq_num(pdev->htt_pdev, rx_desc); + unicast = !htt_rx_msdu_is_wlan_mcast(pdev->htt_pdev, rx_desc); + + pdev->rx_pn_trace.data[idx].peer = peer; + pdev->rx_pn_trace.data[idx].tid = tid; + pdev->rx_pn_trace.data[idx].seq_num = seq_num; + pdev->rx_pn_trace.data[idx].unicast = unicast; + pdev->rx_pn_trace.data[idx].pn32 = pn32; + pdev->rx_pn_trace.cnt++; + idx++; + pdev->rx_pn_trace.idx = idx & pdev->rx_pn_trace.mask; +} + +void ol_rx_pn_trace_display(ol_txrx_pdev_handle pdev, int just_once) +{ + static int print_count /* = 0 */; + uint32_t i, start, end; + uint64_t cnt; + int elems; + int limit = 0; /* move this to the arg list? */ + + if (print_count != 0 && just_once) + return; + + print_count++; + + end = pdev->rx_pn_trace.idx; + if (pdev->rx_pn_trace.cnt <= pdev->rx_pn_trace.mask) { + /* trace log has not yet wrapped around - start at the top */ + start = 0; + cnt = 0; + } else { + start = end; + cnt = pdev->rx_pn_trace.cnt - (pdev->rx_pn_trace.mask + 1); + } + elems = (end - 1 - start) & pdev->rx_pn_trace.mask; + if (limit > 0 && elems > limit) { + int delta; + + delta = elems - limit; + start += delta; + start &= pdev->rx_pn_trace.mask; + cnt += delta; + } + + i = start; + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " seq PN"); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " count idx peer tid uni num LSBs"); + do { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " %6lld %4d %pK %2d %d %4d %8d", + cnt, i, + pdev->rx_pn_trace.data[i].peer, + pdev->rx_pn_trace.data[i].tid, + pdev->rx_pn_trace.data[i].unicast, + pdev->rx_pn_trace.data[i].seq_num, + pdev->rx_pn_trace.data[i].pn32); + cnt++; + i++; + i &= pdev->rx_pn_trace.mask; + } while (i != end); +} +#endif /* ENABLE_RX_PN_TRACE */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_pn.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_pn.h new file mode 100644 index 0000000000000000000000000000000000000000..fa64c3e5133c3a961e6a4a7598d945965d7ab189 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_pn.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2011, 2014-2017, 2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_RX_PN_H_ +#define _OL_RX_PN_H_ + +#include /* qdf_nbuf_t, etc. */ + +#include /* ol_txrx_peer_t, etc. */ + +int ol_rx_pn_cmp24(union htt_rx_pn_t *new_pn, + union htt_rx_pn_t *old_pn, int is_unicast, int opmode, + bool strict_chk); + +int ol_rx_pn_cmp48(union htt_rx_pn_t *new_pn, + union htt_rx_pn_t *old_pn, int is_unicast, int opmode, + bool strict_chk); + +int ol_rx_pn_wapi_cmp(union htt_rx_pn_t *new_pn, + union htt_rx_pn_t *old_pn, int is_unicast, int opmode, + bool strict_chk); + +/** + * @brief If applicable, check the Packet Number to detect replays. + * @details + * Determine whether a PN check is needed, and if so, what the PN size is. + * (A PN size of 0 is used to indirectly bypass the PN check for security + * methods that don't involve a PN check.) + * This function produces event notifications for any PN failures, via the + * ol_rx_err function. + * After the PN check, call the next stage of rx processing (rx --> tx + * forwarding check). + * + * @param vdev - which virtual device the frames were addressed to + * @param peer - which peer the rx frames belong to + * @param tid - which TID within the peer the rx frames belong to + * @param msdu_list - NULL-terminated list of MSDUs to perform PN check on + * (if PN check is applicable, i.e. PN length > 0) + */ +void +ol_rx_pn_check(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, unsigned int tid, + qdf_nbuf_t msdu_list); + +/** + * @brief If applicable, check the Packet Number to detect replays. + * @details + * Determine whether a PN check is needed, and if so, what the PN size is. + * (A PN size of 0 is used to indirectly bypass the PN check for security + * methods that don't involve a PN check.) + * This function produces event notifications for any PN failures, via the + * ol_rx_err function. + * After the PN check, deliver the valid rx frames to the OS shim. + * (Don't perform a rx --> tx forwarding check.) + * + * @param vdev - which virtual device the frames were addressed to + * @param peer - which peer the rx frames belong to + * @param tid - which TID within the peer the rx frames belong to + * @param msdu_list - NULL-terminated list of MSDUs to perform PN check on + * (if PN check is applicable, i.e. PN length > 0) + */ +void +ol_rx_pn_check_only(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu_list); + +/** + * @brief If applicable, check the Packet Number to detect replays. + * @details + * Same as ol_rx_pn_check but return valid rx netbufs + * rather than invoking the rx --> tx forwarding check. + * + * @param vdev - which virtual device the frames were addressed to + * @param peer - which peer the rx frames belong to + * @param tid - which TID within the peer the rx frames belong to + * @param msdu_list - NULL-terminated list of MSDUs to perform PN check on + * (if PN check is applicable, i.e. PN length > 0) + * @param strick_chk - if PN consecutive stric check is needed or not + * @return list of netbufs that didn't fail the PN check + */ +qdf_nbuf_t +ol_rx_pn_check_base(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu_list, bool strict_chk); + +#endif /* _OL_RX_PN_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder.c new file mode 100644 index 0000000000000000000000000000000000000000..2fae0cfd02e7bd18929fb14f53f5713349d2fd71 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder.c @@ -0,0 +1,931 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*=== header file includes ===*/ +/* generic utilities */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_mem_malloc */ + +/* external interfaces */ +#include /* ol_txrx_pdev_handle */ +#include /* ol_rx_addba_handler, etc. */ +#include /* ol_ctrl_rx_addba_complete */ +#include /* htt_rx_desc_frame_free */ +#include /* ol_rx_err */ + +/* datapath internal interfaces */ +#include /* ol_txrx_peer_find_by_id */ +#include /* TXRX_ASSERT */ +#include /* OL_RX_REORDER_TIMEOUT_REMOVE, etc. */ +#include +#include + +/*=== data types and defines ===*/ +#define OL_RX_REORDER_ROUND_PWR2(value) g_log2ceil[value] + +/*=== global variables ===*/ + +static char g_log2ceil[] = { + 1, /* 0 -> 1 */ + 1, /* 1 -> 1 */ + 2, /* 2 -> 2 */ + 4, 4, /* 3-4 -> 4 */ + 8, 8, 8, 8, /* 5-8 -> 8 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 9-16 -> 16 */ + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, /* 17-32 -> 32 */ + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, /* 33-64 -> 64 */ +}; + +/*=== function definitions ===*/ + +/*---*/ + +#define QCA_SUPPORT_RX_REORDER_RELEASE_CHECK 0 +#define OL_RX_REORDER_IDX_START_SELF_SELECT(peer, tid, idx_start) /* no-op */ +#define OL_RX_REORDER_IDX_WRAP(idx, win_sz, win_sz_mask) { idx &= win_sz_mask; } +#define OL_RX_REORDER_IDX_MAX(win_sz, win_sz_mask) win_sz_mask +#define OL_RX_REORDER_IDX_INIT(seq_num, win_sz, win_sz_mask) 0 /* n/a */ +#define OL_RX_REORDER_NO_HOLES(rx_reorder) 0 +#define OL_RX_REORDER_MPDU_CNT_INCR(rx_reorder, incr) /* n/a */ +#define OL_RX_REORDER_MPDU_CNT_DECR(rx_reorder, decr) /* n/a */ + +/*---*/ + +/* reorder array elements are known to be non-NULL */ +#define OL_RX_REORDER_LIST_APPEND(head_msdu, tail_msdu, rx_reorder_array_elem) \ + do { \ + if (tail_msdu) { \ + qdf_nbuf_set_next(tail_msdu, \ + rx_reorder_array_elem->head); \ + } \ + } while (0) + +/* functions called by txrx components */ + +void ol_rx_reorder_init(struct ol_rx_reorder_t *rx_reorder, uint8_t tid) +{ + rx_reorder->win_sz = 1; + rx_reorder->win_sz_mask = 0; + rx_reorder->array = &rx_reorder->base; + rx_reorder->base.head = rx_reorder->base.tail = NULL; + rx_reorder->tid = tid; + rx_reorder->defrag_timeout_ms = 0; + + rx_reorder->defrag_waitlist_elem.tqe_next = NULL; + rx_reorder->defrag_waitlist_elem.tqe_prev = NULL; +} + +static enum htt_rx_status +ol_rx_reorder_seq_num_check( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, unsigned int seq_num) +{ + unsigned int seq_num_delta; + + /* don't check the new seq_num against last_seq + if last_seq is not valid */ + if (peer->tids_last_seq[tid] == IEEE80211_SEQ_MAX) + return htt_rx_status_ok; + + /* + * For duplicate detection, it might be helpful to also check + * whether the retry bit is set or not - a strict duplicate packet + * should be the one with retry bit set. + * However, since many implementations do not set the retry bit, + * and since this same function is also used for filtering out + * late-arriving frames (frames that arive after their rx reorder + * timeout has expired) which are not retries, don't bother checking + * the retry bit for now. + */ + /* note: if new seq_num == old seq_num, seq_num_delta = 4095 */ + seq_num_delta = (seq_num - 1 - peer->tids_last_seq[tid]) & + (IEEE80211_SEQ_MAX - 1); /* account for wraparound */ + + if (seq_num_delta > (IEEE80211_SEQ_MAX >> 1)) { + return htt_rx_status_err_replay; + /* or maybe htt_rx_status_err_dup */ + } + return htt_rx_status_ok; +} + +/** + * ol_rx_seq_num_check() - Does duplicate detection for mcast packets and + * duplicate detection & check for out-of-order + * packets for unicast packets. + * @pdev: Pointer to pdev maintained by OL + * @peer: Pointer to peer structure maintained by OL + * @tid: TID value passed as part of HTT msg by f/w + * @rx_mpdu_desc: Pointer to Rx Descriptor for the given MPDU + * + * This function + * 1) For Multicast Frames -- does duplicate detection + * A frame is considered duplicate & dropped if it has a seq.number + * which is received twice in succession and with the retry bit set + * in the second case. + * A frame which is older than the last sequence number received + * is not considered duplicate but out-of-order. This function does + * perform out-of-order check for multicast frames, which is in + * keeping with the 802.11 2012 spec section 9.3.2.10 + * 2) For Unicast Frames -- does duplicate detection & out-of-order check + * only for non-aggregation tids. + * + * Return: Returns htt_rx_status_err_replay, if packet needs to be + * dropped, htt_rx_status_ok otherwise. + */ +enum htt_rx_status +ol_rx_seq_num_check(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + uint8_t tid, + void *rx_mpdu_desc) +{ + uint16_t pkt_tid = 0xffff; + uint16_t seq_num = IEEE80211_SEQ_MAX; + bool retry = 0; + + seq_num = htt_rx_mpdu_desc_seq_num(pdev->htt_pdev, rx_mpdu_desc); + + /* For mcast packets, we only the dup-detection, not re-order check */ + + if (qdf_unlikely(OL_RX_MCAST_TID == tid)) { + + pkt_tid = htt_rx_mpdu_desc_tid(pdev->htt_pdev, rx_mpdu_desc); + + /* Invalid packet TID, expected only for HL */ + /* Pass the packet on */ + if (qdf_unlikely(pkt_tid >= OL_TXRX_NUM_EXT_TIDS)) + return htt_rx_status_ok; + + retry = htt_rx_mpdu_desc_retry(pdev->htt_pdev, rx_mpdu_desc); + + /* + * At this point, we define frames to be duplicate if they + * arrive "ONLY" in succession with the same sequence number + * and the last one has the retry bit set. For an older frame, + * we consider that as an out of order frame, and hence do not + * perform the dup-detection or out-of-order check for multicast + * frames as per discussions & spec. + * Hence "seq_num <= last_seq_num" check is not necessary. + */ + if (qdf_unlikely(retry && + (seq_num == peer->tids_mcast_last_seq[pkt_tid]))) { + /* drop mcast */ + TXRX_STATS_INCR(pdev, priv.rx.err.msdu_mc_dup_drop); + return htt_rx_status_err_replay; + } + + /* + * This is a multicast packet likely to be passed on... + * Set the mcast last seq number here + * This is fairly accurate since: + * a) f/w sends multicast as separate PPDU/HTT messages + * b) Mcast packets are not aggregated & hence single + * c) Result of b) is that, flush / release bit is set + * always on the mcast packets, so likely to be + * immediatedly released. + */ + peer->tids_mcast_last_seq[pkt_tid] = seq_num; + return htt_rx_status_ok; + } else + return ol_rx_reorder_seq_num_check(pdev, peer, tid, seq_num); +} + + +void +ol_rx_reorder_store(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, + unsigned int idx, qdf_nbuf_t head_msdu, + qdf_nbuf_t tail_msdu) +{ + struct ol_rx_reorder_array_elem_t *rx_reorder_array_elem; + + idx &= peer->tids_rx_reorder[tid].win_sz_mask; + rx_reorder_array_elem = &peer->tids_rx_reorder[tid].array[idx]; + if (rx_reorder_array_elem->head) { + qdf_nbuf_set_next(rx_reorder_array_elem->tail, head_msdu); + } else { + rx_reorder_array_elem->head = head_msdu; + OL_RX_REORDER_MPDU_CNT_INCR(&peer->tids_rx_reorder[tid], 1); + } + rx_reorder_array_elem->tail = tail_msdu; +} + +void +ol_rx_reorder_release(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, unsigned int idx_start, + unsigned int idx_end) +{ + unsigned int idx; + unsigned int win_sz, win_sz_mask; + struct ol_rx_reorder_array_elem_t *rx_reorder_array_elem; + qdf_nbuf_t head_msdu; + qdf_nbuf_t tail_msdu; + + OL_RX_REORDER_IDX_START_SELF_SELECT(peer, tid, &idx_start); + /* may get reset below */ + peer->tids_next_rel_idx[tid] = (uint16_t) idx_end; + + win_sz = peer->tids_rx_reorder[tid].win_sz; + win_sz_mask = peer->tids_rx_reorder[tid].win_sz_mask; + idx_start &= win_sz_mask; + idx_end &= win_sz_mask; + rx_reorder_array_elem = &peer->tids_rx_reorder[tid].array[idx_start]; + + head_msdu = rx_reorder_array_elem->head; + tail_msdu = rx_reorder_array_elem->tail; + rx_reorder_array_elem->head = rx_reorder_array_elem->tail = NULL; + if (head_msdu) + OL_RX_REORDER_MPDU_CNT_DECR(&peer->tids_rx_reorder[tid], 1); + + idx = (idx_start + 1); + OL_RX_REORDER_IDX_WRAP(idx, win_sz, win_sz_mask); + while (idx != idx_end) { + rx_reorder_array_elem = &peer->tids_rx_reorder[tid].array[idx]; + if (rx_reorder_array_elem->head) { + OL_RX_REORDER_MPDU_CNT_DECR(&peer->tids_rx_reorder[tid], + 1); + OL_RX_REORDER_LIST_APPEND(head_msdu, tail_msdu, + rx_reorder_array_elem); + tail_msdu = rx_reorder_array_elem->tail; + } + rx_reorder_array_elem->head = rx_reorder_array_elem->tail = + NULL; + idx++; + OL_RX_REORDER_IDX_WRAP(idx, win_sz, win_sz_mask); + } + if (head_msdu) { + uint16_t seq_num; + htt_pdev_handle htt_pdev = vdev->pdev->htt_pdev; + + /* + * This logic is not quite correct - the last_seq value should + * be the sequence number of the final MPDU released rather than + * the initial MPDU released. + * However, tracking the sequence number of the first MPDU in + * the released batch works well enough: + * For Peregrine and Rome, the last_seq is checked only for + * non-aggregate cases, where only one MPDU at a time is + * released. + * For Riva, Pronto, and Northstar, the last_seq is checked to + * filter out late-arriving rx frames, whose sequence number + * will be less than the first MPDU in this release batch. + */ + seq_num = htt_rx_mpdu_desc_seq_num( + htt_pdev, + htt_rx_msdu_desc_retrieve(htt_pdev, + head_msdu)); + peer->tids_last_seq[tid] = seq_num; + /* rx_opt_proc takes a NULL-terminated list of msdu netbufs */ + qdf_nbuf_set_next(tail_msdu, NULL); + peer->rx_opt_proc(vdev, peer, tid, head_msdu); + } + /* + * If the rx reorder timeout is handled by host SW rather than the + * target's rx reorder logic, then stop the timer here. + * (If there are remaining rx holes, then the timer will be restarted.) + */ + OL_RX_REORDER_TIMEOUT_REMOVE(peer, tid); +} + +void +ol_rx_reorder_flush(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, + unsigned int idx_start, + unsigned int idx_end, enum htt_rx_flush_action action) +{ + struct ol_txrx_pdev_t *pdev; + unsigned int win_sz; + uint8_t win_sz_mask; + struct ol_rx_reorder_array_elem_t *rx_reorder_array_elem; + qdf_nbuf_t head_msdu = NULL; + qdf_nbuf_t tail_msdu = NULL; + + pdev = vdev->pdev; + win_sz = peer->tids_rx_reorder[tid].win_sz; + win_sz_mask = peer->tids_rx_reorder[tid].win_sz_mask; + + OL_RX_REORDER_IDX_START_SELF_SELECT(peer, tid, &idx_start); + /* a idx_end value of 0xffff means to flush the entire array */ + if (idx_end == 0xffff) { + idx_end = idx_start; + /* + * The array is being flushed in entirety because the block + * ack window has been shifted to a new position that does not + * overlap with the old position. (Or due to reception of a + * DELBA.) + * Thus, since the block ack window is essentially being reset, + * reset the "next release index". + */ + peer->tids_next_rel_idx[tid] = + OL_RX_REORDER_IDX_INIT(0 /*n/a */, win_sz, win_sz_mask); + } else { + peer->tids_next_rel_idx[tid] = (uint16_t) idx_end; + } + + idx_start &= win_sz_mask; + idx_end &= win_sz_mask; + + do { + rx_reorder_array_elem = + &peer->tids_rx_reorder[tid].array[idx_start]; + idx_start = (idx_start + 1); + OL_RX_REORDER_IDX_WRAP(idx_start, win_sz, win_sz_mask); + + if (rx_reorder_array_elem->head) { + OL_RX_REORDER_MPDU_CNT_DECR(&peer->tids_rx_reorder[tid], + 1); + if (!head_msdu) { + head_msdu = rx_reorder_array_elem->head; + tail_msdu = rx_reorder_array_elem->tail; + rx_reorder_array_elem->head = NULL; + rx_reorder_array_elem->tail = NULL; + continue; + } + qdf_nbuf_set_next(tail_msdu, + rx_reorder_array_elem->head); + tail_msdu = rx_reorder_array_elem->tail; + rx_reorder_array_elem->head = + rx_reorder_array_elem->tail = NULL; + } + } while (idx_start != idx_end); + + ol_rx_defrag_waitlist_remove(peer, tid); + + if (head_msdu) { + uint16_t seq_num; + htt_pdev_handle htt_pdev = vdev->pdev->htt_pdev; + + seq_num = htt_rx_mpdu_desc_seq_num( + htt_pdev, + htt_rx_msdu_desc_retrieve(htt_pdev, head_msdu)); + peer->tids_last_seq[tid] = seq_num; + /* rx_opt_proc takes a NULL-terminated list of msdu netbufs */ + qdf_nbuf_set_next(tail_msdu, NULL); + if (action == htt_rx_flush_release) { + peer->rx_opt_proc(vdev, peer, tid, head_msdu); + } else { + do { + qdf_nbuf_t next; + + next = qdf_nbuf_next(head_msdu); + htt_rx_desc_frame_free(pdev->htt_pdev, + head_msdu); + head_msdu = next; + } while (head_msdu); + } + } + /* + * If the rx reorder array is empty, then reset the last_seq value - + * it is likely that a BAR or a sequence number shift caused the + * sequence number to jump, so the old last_seq value is not relevant. + */ + if (OL_RX_REORDER_NO_HOLES(&peer->tids_rx_reorder[tid])) + peer->tids_last_seq[tid] = IEEE80211_SEQ_MAX; /* invalid */ + + OL_RX_REORDER_TIMEOUT_REMOVE(peer, tid); +} + +void +ol_rx_reorder_first_hole(struct ol_txrx_peer_t *peer, + unsigned int tid, unsigned int *idx_end) +{ + unsigned int win_sz, win_sz_mask; + unsigned int idx_start = 0, tmp_idx = 0; + + win_sz = peer->tids_rx_reorder[tid].win_sz; + win_sz_mask = peer->tids_rx_reorder[tid].win_sz_mask; + + OL_RX_REORDER_IDX_START_SELF_SELECT(peer, tid, &idx_start); + tmp_idx++; + OL_RX_REORDER_IDX_WRAP(tmp_idx, win_sz, win_sz_mask); + /* bypass the initial hole */ + while (tmp_idx != idx_start && + !peer->tids_rx_reorder[tid].array[tmp_idx].head) { + tmp_idx++; + OL_RX_REORDER_IDX_WRAP(tmp_idx, win_sz, win_sz_mask); + } + /* bypass the present frames following the initial hole */ + while (tmp_idx != idx_start && + peer->tids_rx_reorder[tid].array[tmp_idx].head) { + tmp_idx++; + OL_RX_REORDER_IDX_WRAP(tmp_idx, win_sz, win_sz_mask); + } + /* + * idx_end is exclusive rather than inclusive. + * In other words, it is the index of the first slot of the second + * hole, rather than the index of the final present frame following + * the first hole. + */ + *idx_end = tmp_idx; +} + +#ifdef HL_RX_AGGREGATION_HOLE_DETECTION + +/** + * ol_rx_reorder_detect_hole - ol rx reorder detect hole + * @peer: ol_txrx_peer_t + * @tid: tid + * @idx_start: idx_start + * + * Return: void + */ +static void ol_rx_reorder_detect_hole(struct ol_txrx_peer_t *peer, + uint32_t tid, + uint32_t idx_start) +{ + uint32_t win_sz_mask, next_rel_idx, hole_size; + + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + ol_txrx_err("%s: invalid tid, %u\n", __FUNCTION__, tid); + return; + } + + if (peer->tids_next_rel_idx[tid] == INVALID_REORDER_INDEX) + return; + + win_sz_mask = peer->tids_rx_reorder[tid].win_sz_mask; + /* Return directly if block-ack not enable */ + if (win_sz_mask == 0) + return; + + idx_start &= win_sz_mask; + next_rel_idx = peer->tids_next_rel_idx[tid] & win_sz_mask; + + if (idx_start != next_rel_idx) { + hole_size = ((int)idx_start - (int)next_rel_idx) & win_sz_mask; + + ol_rx_aggregation_hole(hole_size); + } + + return; +} + +#else + +/** + * ol_rx_reorder_detect_hole - ol rx reorder detect hole + * @peer: ol_txrx_peer_t + * @tid: tid + * @idx_start: idx_start + * + * Return: void + */ +static void ol_rx_reorder_detect_hole(struct ol_txrx_peer_t *peer, + uint32_t tid, + uint32_t idx_start) +{ + /* no-op */ +} + +#endif + +void +ol_rx_reorder_peer_cleanup(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer) +{ + int tid; + + for (tid = 0; tid < OL_TXRX_NUM_EXT_TIDS; tid++) { + ol_rx_reorder_flush(vdev, peer, tid, 0, 0, + htt_rx_flush_discard); + } + OL_RX_REORDER_TIMEOUT_PEER_CLEANUP(peer); +} + +/* functions called by HTT */ + +void +ol_rx_addba_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + uint8_t tid, + uint8_t win_sz, uint16_t start_seq_num, uint8_t failed) +{ + uint8_t round_pwr2_win_sz; + unsigned int array_size; + struct ol_txrx_peer_t *peer; + struct ol_rx_reorder_t *rx_reorder; + void *array_mem = NULL; + + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + ol_txrx_err("invalid tid, %u", tid); + WARN_ON(1); + return; + } + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (!peer) { + ol_txrx_err("not able to find peer, %u", peer_id); + return; + } + + if (pdev->cfg.host_addba) { + ol_ctrl_rx_addba_complete(pdev->ctrl_pdev, + &peer->mac_addr.raw[0], tid, failed); + } + if (failed) + return; + + peer->tids_last_seq[tid] = IEEE80211_SEQ_MAX; /* invalid */ + rx_reorder = &peer->tids_rx_reorder[tid]; + + TXRX_ASSERT2(win_sz <= 64); + round_pwr2_win_sz = OL_RX_REORDER_ROUND_PWR2(win_sz); + array_size = + round_pwr2_win_sz * sizeof(struct ol_rx_reorder_array_elem_t); + + array_mem = qdf_mem_malloc(array_size); + if (!array_mem) { + ol_txrx_err("memory allocation failed"); + return; + } + + if (rx_reorder->array != &rx_reorder->base) { + ol_txrx_info("delete array for tid %d", tid); + qdf_mem_free(rx_reorder->array); + } + + rx_reorder->array = array_mem; + rx_reorder->win_sz = win_sz; + TXRX_ASSERT1(rx_reorder->array); + + rx_reorder->win_sz_mask = round_pwr2_win_sz - 1; + rx_reorder->num_mpdus = 0; + + peer->tids_next_rel_idx[tid] = + OL_RX_REORDER_IDX_INIT(start_seq_num, rx_reorder->win_sz, + rx_reorder->win_sz_mask); +} + +void +ol_rx_delba_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id, uint8_t tid) +{ + struct ol_txrx_peer_t *peer; + struct ol_rx_reorder_t *rx_reorder; + + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + ol_txrx_err("invalid tid, %u", tid); + WARN_ON(1); + return; + } + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (!peer) { + ol_txrx_err("not able to find peer, %u", peer_id); + return; + } + + peer->tids_next_rel_idx[tid] = INVALID_REORDER_INDEX; + rx_reorder = &peer->tids_rx_reorder[tid]; + + /* check that there really was a block ack agreement */ + TXRX_ASSERT1(rx_reorder->win_sz_mask != 0); + /* + * Deallocate the old rx reorder array. + * The call to ol_rx_reorder_init below + * will reset rx_reorder->array to point to + * the single-element statically-allocated reorder array + * used for non block-ack cases. + */ + if (rx_reorder->array != &rx_reorder->base) { + ol_txrx_dbg("delete reorder array, tid:%d", + tid); + qdf_mem_free(rx_reorder->array); + } + + /* set up the TID with default parameters (ARQ window size = 1) */ + ol_rx_reorder_init(rx_reorder, tid); +} + +void +ol_rx_flush_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + uint8_t tid, + uint16_t idx_start, + uint16_t idx_end, enum htt_rx_flush_action action) +{ + struct ol_txrx_vdev_t *vdev = NULL; + void *rx_desc; + struct ol_txrx_peer_t *peer; + int idx; + struct ol_rx_reorder_array_elem_t *rx_reorder_array_elem; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + ol_txrx_err("%s: invalid tid, %u\n", __FUNCTION__, tid); + return; + } + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (peer) + vdev = peer->vdev; + else + return; + + OL_RX_REORDER_TIMEOUT_MUTEX_LOCK(pdev); + + idx = idx_start & peer->tids_rx_reorder[tid].win_sz_mask; + rx_reorder_array_elem = &peer->tids_rx_reorder[tid].array[idx]; + if (rx_reorder_array_elem->head) { + rx_desc = + htt_rx_msdu_desc_retrieve(htt_pdev, + rx_reorder_array_elem->head); + if (htt_rx_msdu_is_frag(htt_pdev, rx_desc)) { + ol_rx_reorder_flush_frag(htt_pdev, peer, tid, + idx_start); + /* + * Assuming flush message sent separately for frags + * and for normal frames + */ + OL_RX_REORDER_TIMEOUT_MUTEX_UNLOCK(pdev); + return; + } + } + + if (action == htt_rx_flush_release) + ol_rx_reorder_detect_hole(peer, tid, idx_start); + + ol_rx_reorder_flush(vdev, peer, tid, idx_start, idx_end, action); + /* + * If the rx reorder timeout is handled by host SW, see if there are + * remaining rx holes that require the timer to be restarted. + */ + OL_RX_REORDER_TIMEOUT_UPDATE(peer, tid); + OL_RX_REORDER_TIMEOUT_MUTEX_UNLOCK(pdev); +} + +void +ol_rx_pn_ind_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + uint8_t tid, + uint16_t seq_num_start, + uint16_t seq_num_end, uint8_t pn_ie_cnt, uint8_t *pn_ie) +{ + struct ol_txrx_vdev_t *vdev = NULL; + void *rx_desc; + struct ol_txrx_peer_t *peer; + struct ol_rx_reorder_array_elem_t *rx_reorder_array_elem; + unsigned int win_sz_mask; + qdf_nbuf_t head_msdu = NULL; + qdf_nbuf_t tail_msdu = NULL; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + uint16_t seq_num; + int i = 0; + + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + ol_txrx_err("%s: invalid tid, %u\n", __FUNCTION__, tid); + WARN_ON(1); + return; + } + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + + if (!peer) { + /* + * If we can't find a peer send this packet to OCB interface + * using OCB self peer + */ + if (!ol_txrx_get_ocb_peer(pdev, &peer)) + peer = NULL; + } + + if (peer) + vdev = peer->vdev; + else + return; + + qdf_atomic_set(&peer->fw_pn_check, 1); + /*TODO: Fragmentation case */ + win_sz_mask = peer->tids_rx_reorder[tid].win_sz_mask; + seq_num_start &= win_sz_mask; + seq_num_end &= win_sz_mask; + seq_num = seq_num_start; + + do { + rx_reorder_array_elem = + &peer->tids_rx_reorder[tid].array[seq_num]; + + if (rx_reorder_array_elem->head) { + if (pn_ie_cnt && seq_num == (int)(pn_ie[i])) { + qdf_nbuf_t msdu, next_msdu, mpdu_head, + mpdu_tail; + static uint32_t last_pncheck_print_time; + /* Do not need to initialize as C does it */ + + uint32_t current_time_ms; + union htt_rx_pn_t pn = { 0 }; + int index, pn_len; + + mpdu_head = msdu = rx_reorder_array_elem->head; + mpdu_tail = rx_reorder_array_elem->tail; + + pn_ie_cnt--; + i++; + rx_desc = htt_rx_msdu_desc_retrieve(htt_pdev, + msdu); + index = htt_rx_msdu_is_wlan_mcast( + pdev->htt_pdev, rx_desc) + ? txrx_sec_mcast + : txrx_sec_ucast; + pn_len = pdev->rx_pn[peer->security[index]. + sec_type].len; + htt_rx_mpdu_desc_pn(htt_pdev, rx_desc, &pn, + pn_len); + + current_time_ms = qdf_system_ticks_to_msecs( + qdf_system_ticks()); + if (TXRX_PN_CHECK_FAILURE_PRINT_PERIOD_MS < + (current_time_ms - + last_pncheck_print_time)) { + last_pncheck_print_time = + current_time_ms; + ol_txrx_warn( + "Tgt PN check failed - TID %d, peer %pK " + "("QDF_MAC_ADDR_FMT")\n" + " PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n" + " new seq num = %d\n", + tid, peer, + QDF_MAC_ADDR_REF(peer->mac_addr.raw), + pn.pn128[1], + pn.pn128[0], + pn.pn128[0] & 0xffffffffffffULL, + htt_rx_mpdu_desc_seq_num(htt_pdev, + rx_desc)); + } else { + ol_txrx_dbg( + "Tgt PN check failed - TID %d, peer %pK " + "("QDF_MAC_ADDR_FMT")\n" + " PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n" + " new seq num = %d\n", + tid, peer, + QDF_MAC_ADDR_REF(peer->mac_addr.raw), + pn.pn128[1], + pn.pn128[0], + pn.pn128[0] & 0xffffffffffffULL, + htt_rx_mpdu_desc_seq_num(htt_pdev, + rx_desc)); + } + ol_rx_err(pdev->ctrl_pdev, vdev->vdev_id, + peer->mac_addr.raw, tid, + htt_rx_mpdu_desc_tsf32(htt_pdev, + rx_desc), + OL_RX_ERR_PN, mpdu_head, NULL, 0); + + /* free all MSDUs within this MPDU */ + do { + next_msdu = qdf_nbuf_next(msdu); + htt_rx_desc_frame_free(htt_pdev, msdu); + if (msdu == mpdu_tail) + break; + msdu = next_msdu; + } while (1); + + } else { + if (!head_msdu) { + head_msdu = rx_reorder_array_elem->head; + tail_msdu = rx_reorder_array_elem->tail; + } else { + qdf_nbuf_set_next( + tail_msdu, + rx_reorder_array_elem->head); + tail_msdu = rx_reorder_array_elem->tail; + } + } + rx_reorder_array_elem->head = NULL; + rx_reorder_array_elem->tail = NULL; + } + seq_num = (seq_num + 1) & win_sz_mask; + } while (seq_num != seq_num_end); + + if (head_msdu) { + /* rx_opt_proc takes a NULL-terminated list of msdu netbufs */ + qdf_nbuf_set_next(tail_msdu, NULL); + peer->rx_opt_proc(vdev, peer, tid, head_msdu); + } +} + +#if defined(ENABLE_RX_REORDER_TRACE) + +A_STATUS ol_rx_reorder_trace_attach(ol_txrx_pdev_handle pdev) +{ + int num_elems; + + num_elems = 1 << TXRX_RX_REORDER_TRACE_SIZE_LOG2; + pdev->rx_reorder_trace.idx = 0; + pdev->rx_reorder_trace.cnt = 0; + pdev->rx_reorder_trace.mask = num_elems - 1; + pdev->rx_reorder_trace.data = qdf_mem_malloc( + sizeof(*pdev->rx_reorder_trace.data) * num_elems); + if (!pdev->rx_reorder_trace.data) + return A_NO_MEMORY; + + while (--num_elems >= 0) + pdev->rx_reorder_trace.data[num_elems].seq_num = 0xffff; + + return A_OK; +} + +void ol_rx_reorder_trace_detach(ol_txrx_pdev_handle pdev) +{ + qdf_mem_free(pdev->rx_reorder_trace.data); +} + +void +ol_rx_reorder_trace_add(ol_txrx_pdev_handle pdev, + uint8_t tid, + uint16_t reorder_idx, uint16_t seq_num, int num_mpdus) +{ + uint32_t idx = pdev->rx_reorder_trace.idx; + + pdev->rx_reorder_trace.data[idx].tid = tid; + pdev->rx_reorder_trace.data[idx].reorder_idx = reorder_idx; + pdev->rx_reorder_trace.data[idx].seq_num = seq_num; + pdev->rx_reorder_trace.data[idx].num_mpdus = num_mpdus; + pdev->rx_reorder_trace.cnt++; + idx++; + pdev->rx_reorder_trace.idx = idx & pdev->rx_reorder_trace.mask; +} + +void +ol_rx_reorder_trace_display(ol_txrx_pdev_handle pdev, int just_once, int limit) +{ + static int print_count; + uint32_t i, start, end; + uint64_t cnt; + int elems; + + if (print_count != 0 && just_once) + return; + + print_count++; + + end = pdev->rx_reorder_trace.idx; + if (pdev->rx_reorder_trace.data[end].seq_num == 0xffff) { + /* trace log has not yet wrapped around - start at the top */ + start = 0; + cnt = 0; + } else { + start = end; + cnt = pdev->rx_reorder_trace.cnt - + (pdev->rx_reorder_trace.mask + 1); + } + elems = (end - 1 - start) & pdev->rx_reorder_trace.mask; + if (limit > 0 && elems > limit) { + int delta; + + delta = elems - limit; + start += delta; + start &= pdev->rx_reorder_trace.mask; + cnt += delta; + } + + i = start; + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " log array seq"); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " count idx tid idx num (LSBs)"); + do { + uint16_t seq_num, reorder_idx; + + seq_num = pdev->rx_reorder_trace.data[i].seq_num; + reorder_idx = pdev->rx_reorder_trace.data[i].reorder_idx; + if (seq_num < (1 << 14)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " %6lld %4d %3d %4d %4d (%d)", + cnt, i, pdev->rx_reorder_trace.data[i].tid, + reorder_idx, seq_num, seq_num & 63); + } else { + int err = TXRX_SEQ_NUM_ERR(seq_num); + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " %6lld %4d err %d (%d MPDUs)", + cnt, i, err, + pdev->rx_reorder_trace.data[i].num_mpdus); + } + cnt++; + i++; + i &= pdev->rx_reorder_trace.mask; + } while (i != end); +} + +#endif /* ENABLE_RX_REORDER_TRACE */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder.h new file mode 100644 index 0000000000000000000000000000000000000000..07fa12fca823d598a8a817edba7b0be5d4323dcd --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011, 2014-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_RX_REORDER__H_ +#define _OL_RX_REORDER__H_ + +#include /* qdf_nbuf_t, etc. */ + +#include /* ol_txrx_peer_t, etc. */ + +#include /* ol_rx_reorder_t */ + +void +ol_rx_reorder_store(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, + unsigned int reorder_array_index, + qdf_nbuf_t head_msdu, qdf_nbuf_t tail_msdu); + +void +ol_rx_reorder_release(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, + unsigned int seq_num_start, unsigned int seq_num_end); + +void +ol_rx_reorder_flush(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, + unsigned int seq_num_start, + unsigned int seq_num_end, enum htt_rx_flush_action action); + +/** + * @brief - find end of first range of present MPDUs after the initial rx hole + * @param[in] peer - which sender's data is being checked + * @param[in] tid - which type of data is being checked + * @param[out] idx_end - the reorder array index holding the last MPDU in the + * range of in-order MPDUs that following the initial hole. + * Note that this is the index of the last in-order MPDU following the + * first hole, rather than the starting index of the second hole. + */ +void +ol_rx_reorder_first_hole(struct ol_txrx_peer_t *peer, + unsigned int tid, unsigned int *idx_end); + +void +ol_rx_reorder_peer_cleanup(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer); + +void ol_rx_reorder_init(struct ol_rx_reorder_t *rx_reorder, uint8_t tid); + +enum htt_rx_status +ol_rx_seq_num_check(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + uint8_t tid, void *rx_mpdu_desc); + +/* + * Peregrine and Rome: do sequence number checking in the host + * for peer-TIDs without aggregation enabled + */ + +#define OL_RX_SEQ_NUM_CHECK(pdev, peer, tid, rx_mpdu_desc) \ + (pdev->rx.flags.dup_check && peer->tids_rx_reorder[tid].win_sz_mask == \ + 0) ? ol_rx_seq_num_check(pdev, peer, tid, rx_mpdu_desc) : \ + htt_rx_status_ok + +#endif /* _OL_RX_REORDER__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder_timeout.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder_timeout.c new file mode 100644 index 0000000000000000000000000000000000000000..487b140b25553eebbf66e5a86d105f77c0396304 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder_timeout.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*=== header file includes ===*/ +/* generic utilities */ +#include /* qdf_nbuf_t, etc. */ +#include +#include + +/* datapath internal interfaces */ +#include /* TXRX_ASSERT, etc. */ +#include /* ol_rx_reorder_flush, etc. */ +#include + +#ifdef QCA_SUPPORT_OL_RX_REORDER_TIMEOUT + +void ol_rx_reorder_timeout_remove(struct ol_txrx_peer_t *peer, unsigned int tid) +{ + struct ol_txrx_pdev_t *pdev; + struct ol_tx_reorder_cat_timeout_t *rx_reorder_timeout_ac; + struct ol_rx_reorder_timeout_list_elem_t *list_elem; + int ac; + + pdev = peer->vdev->pdev; + ac = TXRX_TID_TO_WMM_AC(tid); + rx_reorder_timeout_ac = &pdev->rx.reorder_timeout.access_cats[ac]; + list_elem = &peer->tids_rx_reorder[tid].timeout; + if (!list_elem->active) { + /* this element has already been removed */ + return; + } + list_elem->active = 0; + TAILQ_REMOVE(&rx_reorder_timeout_ac->virtual_timer_list, list_elem, + reorder_timeout_list_elem); +} + +static void +ol_rx_reorder_timeout_start(struct ol_tx_reorder_cat_timeout_t + *rx_reorder_timeout_ac, uint32_t time_now_ms) +{ + uint32_t duration_ms; + struct ol_rx_reorder_timeout_list_elem_t *list_elem; + + list_elem = TAILQ_FIRST(&rx_reorder_timeout_ac->virtual_timer_list); + + duration_ms = list_elem->timestamp_ms - time_now_ms; + qdf_timer_start(&rx_reorder_timeout_ac->timer, duration_ms); +} + +static inline void +ol_rx_reorder_timeout_add(struct ol_txrx_peer_t *peer, uint8_t tid) +{ + uint32_t time_now_ms; + struct ol_txrx_pdev_t *pdev; + struct ol_tx_reorder_cat_timeout_t *rx_reorder_timeout_ac; + struct ol_rx_reorder_timeout_list_elem_t *list_elem; + int ac; + int start; + + pdev = peer->vdev->pdev; + ac = TXRX_TID_TO_WMM_AC(tid); + rx_reorder_timeout_ac = &pdev->rx.reorder_timeout.access_cats[ac]; + list_elem = &peer->tids_rx_reorder[tid].timeout; + + list_elem->active = 1; + list_elem->peer = peer; + list_elem->tid = tid; + + /* set the expiration timestamp */ + time_now_ms = qdf_system_ticks_to_msecs(qdf_system_ticks()); + list_elem->timestamp_ms = + time_now_ms + rx_reorder_timeout_ac->duration_ms; + + /* add to the queue */ + start = TAILQ_EMPTY(&rx_reorder_timeout_ac->virtual_timer_list); + TAILQ_INSERT_TAIL(&rx_reorder_timeout_ac->virtual_timer_list, + list_elem, reorder_timeout_list_elem); + if (start) + ol_rx_reorder_timeout_start(rx_reorder_timeout_ac, time_now_ms); +} + +void ol_rx_reorder_timeout_update(struct ol_txrx_peer_t *peer, uint8_t tid) +{ + if (!peer) + return; + + /* + * If there are no holes, i.e. no queued frames, + * then timeout doesn't apply. + */ + if (peer->tids_rx_reorder[tid].num_mpdus == 0) + return; + + /* + * If the virtual timer for this peer-TID is already running, + * then leave it. + */ + if (peer->tids_rx_reorder[tid].timeout.active) + return; + + ol_rx_reorder_timeout_add(peer, tid); +} + +static void ol_rx_reorder_timeout(void *arg) +{ + struct ol_txrx_pdev_t *pdev; + struct ol_rx_reorder_timeout_list_elem_t *list_elem, *tmp; + uint32_t time_now_ms; + struct ol_tx_reorder_cat_timeout_t *rx_reorder_timeout_ac; + + rx_reorder_timeout_ac = (struct ol_tx_reorder_cat_timeout_t *)arg; + time_now_ms = qdf_system_ticks_to_msecs(qdf_system_ticks()); + + pdev = rx_reorder_timeout_ac->pdev; + qdf_spin_lock(&pdev->rx.mutex); +/* TODO: conditionally take mutex lock during regular rx */ + TAILQ_FOREACH_SAFE(list_elem, + &rx_reorder_timeout_ac->virtual_timer_list, + reorder_timeout_list_elem, tmp) { + unsigned int idx_start, idx_end; + struct ol_txrx_peer_t *peer; + + if (list_elem->timestamp_ms > time_now_ms) + break; /* time has not expired yet for this element */ + + list_elem->active = 0; + /* remove the expired element from the list */ + TAILQ_REMOVE(&rx_reorder_timeout_ac->virtual_timer_list, + list_elem, reorder_timeout_list_elem); + + peer = list_elem->peer; + + idx_start = 0xffff; /* start from next_rel_idx */ + ol_rx_reorder_first_hole(peer, list_elem->tid, &idx_end); + ol_rx_reorder_flush(peer->vdev, + peer, + list_elem->tid, + idx_start, idx_end, htt_rx_flush_release); + } + /* restart the timer if unexpired elements are left in the list */ + if (!TAILQ_EMPTY(&rx_reorder_timeout_ac->virtual_timer_list)) + ol_rx_reorder_timeout_start(rx_reorder_timeout_ac, time_now_ms); + + qdf_spin_unlock(&pdev->rx.mutex); +} + +void ol_rx_reorder_timeout_init(struct ol_txrx_pdev_t *pdev) +{ + int i; + + for (i = 0; i < QDF_ARRAY_SIZE(pdev->rx.reorder_timeout.access_cats); + i++) { + struct ol_tx_reorder_cat_timeout_t *rx_reorder_timeout_ac; + + rx_reorder_timeout_ac = + &pdev->rx.reorder_timeout.access_cats[i]; + /* init the per-AC timers */ + qdf_timer_init(pdev->osdev, + &rx_reorder_timeout_ac->timer, + ol_rx_reorder_timeout, + rx_reorder_timeout_ac, + QDF_TIMER_TYPE_SW); + /* init the virtual timer list */ + TAILQ_INIT(&rx_reorder_timeout_ac->virtual_timer_list); + rx_reorder_timeout_ac->pdev = pdev; + } + pdev->rx.reorder_timeout.access_cats[TXRX_WMM_AC_VO].duration_ms = 40; + pdev->rx.reorder_timeout.access_cats[TXRX_WMM_AC_VI].duration_ms = 100; + pdev->rx.reorder_timeout.access_cats[TXRX_WMM_AC_BE].duration_ms = 100; + pdev->rx.reorder_timeout.access_cats[TXRX_WMM_AC_BK].duration_ms = 100; +} + +void ol_rx_reorder_timeout_peer_cleanup(struct ol_txrx_peer_t *peer) +{ + int tid; + + for (tid = 0; tid < OL_TXRX_NUM_EXT_TIDS; tid++) { + if (peer->tids_rx_reorder[tid].timeout.active) + ol_rx_reorder_timeout_remove(peer, tid); + } +} + +void ol_rx_reorder_timeout_cleanup(struct ol_txrx_pdev_t *pdev) +{ + int i; + + for (i = 0; i < QDF_ARRAY_SIZE(pdev->rx.reorder_timeout.access_cats); + i++) { + struct ol_tx_reorder_cat_timeout_t *rx_reorder_timeout_ac; + + rx_reorder_timeout_ac = + &pdev->rx.reorder_timeout.access_cats[i]; + qdf_timer_stop(&rx_reorder_timeout_ac->timer); + qdf_timer_free(&rx_reorder_timeout_ac->timer); + } +} + +#endif /* QCA_SUPPORT_OL_RX_REORDER_TIMEOUT */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder_timeout.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder_timeout.h new file mode 100644 index 0000000000000000000000000000000000000000..9f095015eb22d4b79b22f4e517fef218f1036611 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_reorder_timeout.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012, 2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_RX_REORDER_TIMEOUT__H_ +#define _OL_RX_REORDER_TIMEOUT__H_ + +#include /* ol_txrx_vdev_t, etc. */ +#ifdef QCA_SUPPORT_OL_RX_REORDER_TIMEOUT + +void ol_rx_reorder_timeout_init(struct ol_txrx_pdev_t *pdev); +void ol_rx_reorder_timeout_cleanup(struct ol_txrx_pdev_t *pdev); +void ol_rx_reorder_timeout_remove(struct ol_txrx_peer_t *peer, + unsigned int tid); +void ol_rx_reorder_timeout_update(struct ol_txrx_peer_t *peer, uint8_t tid); +void ol_rx_reorder_timeout_peer_cleanup(struct ol_txrx_peer_t *peer); + +#define OL_RX_REORDER_TIMEOUT_INIT ol_rx_reorder_timeout_init +#define OL_RX_REORDER_TIMEOUT_PEER_CLEANUP ol_rx_reorder_timeout_peer_cleanup +#define OL_RX_REORDER_TIMEOUT_CLEANUP ol_rx_reorder_timeout_cleanup +#define OL_RX_REORDER_TIMEOUT_REMOVE ol_rx_reorder_timeout_remove +#define OL_RX_REORDER_TIMEOUT_UPDATE ol_rx_reorder_timeout_update +#define OL_RX_REORDER_TIMEOUT_PEER_TID_INIT(peer, tid) \ + (peer)->tids_rx_reorder[(tid)].timeout.active = 0 +#define OL_RX_REORDER_TIMEOUT_MUTEX_LOCK(pdev) \ + qdf_spin_lock(&(pdev)->rx.mutex) +#define OL_RX_REORDER_TIMEOUT_MUTEX_UNLOCK(pdev) \ + qdf_spin_unlock(&(pdev)->rx.mutex) + +#else + +#define OL_RX_REORDER_TIMEOUT_INIT(pdev) /* no-op */ +#define OL_RX_REORDER_TIMEOUT_PEER_CLEANUP(peer) /* no-op */ +#define OL_RX_REORDER_TIMEOUT_CLEANUP(pdev) /* no-op */ +#define OL_RX_REORDER_TIMEOUT_REMOVE(peer, tid) /* no-op */ +#define OL_RX_REORDER_TIMEOUT_UPDATE(peer, tid) /* no-op */ +#define OL_RX_REORDER_TIMEOUT_PEER_TID_INIT(peer, tid) /* no-op */ +#define OL_RX_REORDER_TIMEOUT_MUTEX_LOCK(pdev) /* no-op */ +#define OL_RX_REORDER_TIMEOUT_MUTEX_UNLOCK(pdev) /* no-op */ + +#endif /* QCA_SUPPORT_OL_RX_REORDER_TIMEOUT */ + +#endif /* _OL_RX_REORDER_TIMEOUT__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx.c new file mode 100644 index 0000000000000000000000000000000000000000..d6c7d24981cb395b558d6fdc17e4567ff1cdf0f0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* OS abstraction libraries */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_atomic_read, etc. */ +#include /* qdf_unlikely */ + +/* APIs for other modules */ +#include /* HTT_TX_EXT_TID_MGMT */ +#include /* htt_tx_desc_tid */ + +/* internal header files relevant for all systems */ +#include /* TXRX_ASSERT1 */ +#include /* ol_tx_desc */ +#include /* ol_tx_send */ +#include + +/* internal header files relevant only for HL systems */ +#include /* ol_tx_classify, ol_tx_classify_mgmt */ +#include /* ol_tx_enqueue */ +#include /* ol_tx_sched */ + +/* internal header files relevant only for specific systems (Pronto) */ +#include /* OL_TX_ENCAP, etc */ +#include +#include + +/** + * ol_tx_data() - send data frame + * @soc_hdl: datapath soc handle + * @vdev_id: virtual interface id + * @skb: skb + * + * Return: skb/NULL for success + */ +qdf_nbuf_t ol_tx_data(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + qdf_nbuf_t skb) +{ + struct ol_txrx_pdev_t *pdev; + qdf_nbuf_t ret; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (qdf_unlikely(!vdev)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s:vdev is null", __func__); + return skb; + } + + pdev = vdev->pdev; + + if (qdf_unlikely(!pdev)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s:pdev is null", __func__); + return skb; + } + + if ((ol_cfg_is_ip_tcp_udp_checksum_offload_enabled(pdev->ctrl_pdev)) + && (qdf_nbuf_get_protocol(skb) == htons(ETH_P_IP)) + && (qdf_nbuf_get_ip_summed(skb) == CHECKSUM_PARTIAL)) + qdf_nbuf_set_ip_summed(skb, CHECKSUM_COMPLETE); + + /* Terminate the (single-element) list of tx frames */ + qdf_nbuf_set_next(skb, NULL); + ret = OL_TX_SEND(vdev, skb); + if (ret) { + ol_txrx_dbg("Failed to tx"); + return ret; + } + + return NULL; +} + +#ifdef IPA_OFFLOAD +qdf_nbuf_t ol_tx_send_ipa_data_frame(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + qdf_nbuf_t skb) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id( + soc, OL_TXRX_PDEV_ID); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + qdf_nbuf_t ret; + + if (qdf_unlikely(!pdev)) { + ol_txrx_err("%s: invalid pdev", __func__); + return skb; + } + if (qdf_unlikely(!vdev)) { + ol_txrx_err("%s: invalid vdev, vdev_id:%d", __func__, vdev_id); + return skb; + } + + if ((ol_cfg_is_ip_tcp_udp_checksum_offload_enabled(pdev->ctrl_pdev)) + && (qdf_nbuf_get_protocol(skb) == htons(ETH_P_IP)) + && (qdf_nbuf_get_ip_summed(skb) == CHECKSUM_PARTIAL)) + qdf_nbuf_set_ip_summed(skb, CHECKSUM_COMPLETE); + + /* Terminate the (single-element) list of tx frames */ + qdf_nbuf_set_next(skb, NULL); + + /* + * Add SKB to internal tracking table before further processing + * in WLAN driver. + */ + qdf_net_buf_debug_acquire_skb(skb, __FILE__, __LINE__); + + ret = OL_TX_SEND((struct ol_txrx_vdev_t *)vdev, skb); + if (ret) { + ol_txrx_dbg("Failed to tx"); + return ret; + } + + return NULL; +} +#endif + +void +ol_txrx_data_tx_cb_set(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + ol_txrx_data_tx_cb callback, void *ctxt) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + struct ol_txrx_pdev_t *pdev; + + if (!vdev || !vdev->pdev) + return; + + pdev = vdev->pdev; + pdev->tx_data_callback.func = callback; + pdev->tx_data_callback.ctxt = ctxt; +} + +QDF_STATUS +ol_txrx_mgmt_tx_cb_set(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, uint8_t type, + ol_txrx_mgmt_tx_cb download_cb, + ol_txrx_mgmt_tx_cb ota_ack_cb, void *ctxt) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + + if (!pdev) + return QDF_STATUS_E_FAILURE; + + TXRX_ASSERT1(type < OL_TXRX_MGMT_NUM_TYPES); + pdev->tx_mgmt_cb.download_cb = download_cb; + pdev->tx_mgmt_cb.ota_ack_cb = ota_ack_cb; + pdev->tx_mgmt_cb.ctxt = ctxt; + + return QDF_STATUS_SUCCESS; +} + +int +ol_txrx_mgmt_send_ext(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + qdf_nbuf_t tx_mgmt_frm, uint8_t type, + uint8_t use_6mbps, uint16_t chanfreq) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + struct ol_txrx_pdev_t *pdev; + struct ol_tx_desc_t *tx_desc; + struct ol_txrx_msdu_info_t tx_msdu_info; + int result = 0; + + if (!vdev || !vdev->pdev) + return QDF_STATUS_E_FAULT; + + pdev = vdev->pdev; + tx_msdu_info.tso_info.is_tso = 0; + + tx_msdu_info.htt.action.use_6mbps = use_6mbps; + tx_msdu_info.htt.info.ext_tid = HTT_TX_EXT_TID_MGMT; + tx_msdu_info.htt.info.vdev_id = vdev->vdev_id; + tx_msdu_info.htt.action.do_tx_complete = + pdev->tx_mgmt_cb.ota_ack_cb ? 1 : 0; + + /* + * FIX THIS: l2_hdr_type should only specify L2 header type + * The Peregrine/Rome HTT layer provides the FW with a "pkt type" + * that is a combination of L2 header type and 802.11 frame type. + * If the 802.11 frame type is "mgmt", then the HTT pkt type is "mgmt". + * But if the 802.11 frame type is "data", then the HTT pkt type is + * the L2 header type (more or less): 802.3 vs. Native WiFi + * (basic 802.11). + * (Or the header type can be "raw", which is any version of the 802.11 + * header, and also implies that some of the offloaded tx data + * processing steps may not apply.) + * For efficiency, the Peregrine/Rome HTT uses the msdu_info's + * l2_hdr_type field to program the HTT pkt type. Thus, this txrx SW + * needs to overload the l2_hdr_type to indicate whether the frame is + * data vs. mgmt, as well as 802.3 L2 header vs. 802.11 L2 header. + * To fix this, the msdu_info's l2_hdr_type should be left specifying + * just the L2 header type. For mgmt frames, there should be a + * separate function to patch the HTT pkt type to store a "mgmt" value + * rather than the L2 header type. Then the HTT pkt type can be + * programmed efficiently for data frames, and the msdu_info's + * l2_hdr_type field won't be confusingly overloaded to hold the 802.11 + * frame type rather than the L2 header type. + */ + /* + * FIX THIS: remove duplication of htt_frm_type_mgmt and + * htt_pkt_type_mgmt + * The htt module expects a "enum htt_pkt_type" value. + * The htt_dxe module expects a "enum htt_frm_type" value. + * This needs to be cleaned up, so both versions of htt use a + * consistent method of specifying the frame type. + */ +#ifdef QCA_SUPPORT_INTEGRATED_SOC + /* tx mgmt frames always come with a 802.11 header */ + tx_msdu_info.htt.info.l2_hdr_type = htt_pkt_type_native_wifi; + tx_msdu_info.htt.info.frame_type = htt_frm_type_mgmt; +#else + tx_msdu_info.htt.info.l2_hdr_type = htt_pkt_type_mgmt; + tx_msdu_info.htt.info.frame_type = htt_pkt_type_mgmt; +#endif + + tx_msdu_info.peer = NULL; + + tx_desc = ol_txrx_mgmt_tx_desc_alloc(pdev, vdev, tx_mgmt_frm, + &tx_msdu_info); + if (!tx_desc) + return -EINVAL; /* can't accept the tx mgmt frame */ + + TXRX_STATS_MSDU_INCR(pdev, tx.mgmt, tx_mgmt_frm); + TXRX_ASSERT1(type < OL_TXRX_MGMT_NUM_TYPES); + tx_desc->pkt_type = type + OL_TXRX_MGMT_TYPE_BASE; + + result = ol_txrx_mgmt_send_frame(vdev, tx_desc, tx_mgmt_frm, + &tx_msdu_info, chanfreq); + + return 0; /* accepted the tx mgmt frame */ +} diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx.h new file mode 100644 index 0000000000000000000000000000000000000000..b3c69dee358e412280b74f772fb2eaba366b25ce --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx.h @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_tx.h + * @brief Internal definitions for the high-level tx module. + */ +#ifndef _OL_TX__H_ +#define _OL_TX__H_ + +#include /* qdf_nbuf_t */ +#include +#include /* ol_txrx_vdev_t, etc. */ +#include /* ol_tx_spec */ +#include +#include /* ol_tx_desc_t, ol_txrx_msdu_info_t */ +#include +#include + +#ifdef IPA_OFFLOAD +/** + * ol_tx_send_ipa_data_frame() - send IPA data frame + * @soc_hdl: datapath soc handle + * @vdev: virtual interface id + * @skb: skb + * + * Return: skb/ NULL is for success + */ +qdf_nbuf_t ol_tx_send_ipa_data_frame(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + qdf_nbuf_t skb); +#endif + +#ifdef CONFIG_LL_DP_SUPPORT +struct ol_tx_desc_t * +ol_tx_prepare_ll(ol_txrx_vdev_handle vdev, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info); +#endif + +qdf_nbuf_t ol_tx_ll_wrapper(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list); +#ifdef WLAN_FEATURE_FASTPATH +qdf_nbuf_t ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list); + +void ol_tx_setup_fastpath_ce_handles(struct hif_opaque_softc *osc, + struct ol_txrx_pdev_t *pdev); +#else +static inline +void ol_tx_setup_fastpath_ce_handles(struct hif_opaque_softc *osc, + struct ol_txrx_pdev_t *pdev) +{ } + +qdf_nbuf_t ol_tx_ll(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list); +#endif + +qdf_nbuf_t ol_tx_ll_queue(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list); + +#ifdef CONFIG_HL_SUPPORT +#define OL_TX_SEND ol_tx_hl +#else +#define OL_TX_SEND OL_TX_LL +#endif + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +#define OL_TX_LL ol_tx_ll_queue +#else +#define OL_TX_LL ol_tx_ll_wrapper +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE +void ol_tx_hl_vdev_bundle_timer(void *context); + +void ol_tx_hl_queue_flush_all(struct ol_txrx_vdev_t *vdev); +qdf_nbuf_t +ol_tx_hl_pdev_queue_send_all(struct ol_txrx_pdev_t *pdev); +#else +static inline +void ol_tx_hl_vdev_bundle_timer(void *context) +{ +} + +static inline +void ol_tx_hl_queue_flush_all(struct ol_txrx_vdev_t *vdev) +{ +} + +static inline +qdf_nbuf_t +ol_tx_hl_pdev_queue_send_all(struct ol_txrx_pdev_t *pdev) +{ + return NULL; +} +#endif + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +void ol_tx_vdev_ll_pause_queue_send(void *context); +void ol_tx_pdev_ll_pause_queue_send_all(struct ol_txrx_pdev_t *pdev); +#else +static inline void ol_tx_vdev_ll_pause_queue_send(void *context) +{ +} +static inline +void ol_tx_pdev_ll_pause_queue_send_all(struct ol_txrx_pdev_t *pdev) +{ +} +#endif + +static inline +int ol_txrx_tx_is_raw(enum ol_tx_spec tx_spec) +{ + return tx_spec & + (OL_TX_SPEC_RAW | OL_TX_SPEC_NO_AGGR | OL_TX_SPEC_NO_ENCRYPT); +} + +static inline +uint8_t ol_txrx_tx_raw_subtype(enum ol_tx_spec tx_spec) +{ + uint8_t sub_type = 0x1; /* 802.11 MAC header present */ + + if (tx_spec & OL_TX_SPEC_NO_AGGR) + sub_type |= 0x1 << HTT_TX_MSDU_DESC_RAW_SUBTYPE_NO_AGGR_S; + if (tx_spec & OL_TX_SPEC_NO_ENCRYPT) + sub_type |= 0x1 << HTT_TX_MSDU_DESC_RAW_SUBTYPE_NO_ENCRYPT_S; + if (tx_spec & OL_TX_SPEC_NWIFI_NO_ENCRYPT) + sub_type |= 0x1 << HTT_TX_MSDU_DESC_RAW_SUBTYPE_NO_ENCRYPT_S; + return sub_type; +} + +/** + * ol_tx_hl() - transmit tx frames for a HL system. + * @vdev: the virtual device transmit the data + * @msdu_list: the tx frames to send + * + * Return: NULL if all MSDUs are accepted + */ +qdf_nbuf_t +ol_tx_hl(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list); + +/** + * ol_tx_non_std() - Allow the control-path SW to send data frames + * @soc_hdl: Datapath soc handle + * @vdev_id: id of vdev + * @tx_spec: what non-standard handling to apply to the tx data frames + * @msdu_list: NULL-terminated list of tx MSDUs + * + * Generally, all tx data frames come from the OS shim into the txrx layer. + * However, there are rare cases such as TDLS messaging where the UMAC + * control-path SW creates tx data frames. + * This UMAC SW can call this function to provide the tx data frames to + * the txrx layer. + * The UMAC SW can request a callback for these data frames after their + * transmission completes, by using the ol_txrx_data_tx_cb_set function + * to register a tx completion callback, and by specifying + * ol_tx_spec_no_free as the tx_spec arg when giving the frames to + * ol_tx_non_std. + * The MSDUs need to have the appropriate L2 header type (802.3 vs. 802.11), + * as specified by ol_cfg_frame_type(). + * + * Return: null - success, skb - failure + */ +#ifdef CONFIG_HL_SUPPORT +qdf_nbuf_t ol_tx_non_std_hl(struct ol_txrx_vdev_t *vdev, + enum ol_tx_spec tx_spec, + qdf_nbuf_t msdu_list); + +static inline qdf_nbuf_t +ol_tx_non_std(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + enum ol_tx_spec tx_spec, qdf_nbuf_t msdu_list) +{ + struct ol_txrx_vdev_t *vdev; + + vdev = (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) + return msdu_list; + else + return ol_tx_non_std_hl(vdev, tx_spec, msdu_list); +} +#else +qdf_nbuf_t ol_tx_non_std_ll(struct ol_txrx_vdev_t *vdev, + enum ol_tx_spec tx_spec, qdf_nbuf_t msdu_list); + +static inline qdf_nbuf_t +ol_tx_non_std(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + enum ol_tx_spec tx_spec, qdf_nbuf_t msdu_list) +{ + struct ol_txrx_vdev_t *vdev; + + vdev = (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) + return msdu_list; + else + return ol_tx_non_std_ll(vdev, tx_spec, msdu_list); +} +#endif + +void ol_txrx_mgmt_tx_complete(void *ctxt, qdf_nbuf_t netbuf, int err); + +/** + * ol_txrx_mgmt_tx_cb_set() - Store a callback for delivery + * notifications for management frames. + * @soc: Datapath soc handle + * @pdev_id: Physical device instance id + * @type: the type of mgmt frame the callback is used for + * @download_cb: the callback for notification of delivery to the target + * @ota_ack_cb: the callback for notification of delivery to the peer + * @ctxt: context to use with the callback + * + * When the txrx SW receives notifications from the target that a tx frame + * has been delivered to its recipient, it will check if the tx frame + * is a management frame. If so, the txrx SW will check the management + * frame type specified when the frame was submitted for transmission. + * If there is a callback function registered for the type of management + * frame in question, the txrx code will invoke the callback to inform + * the management + control SW that the mgmt frame was delivered. + * This function is used by the control SW to store a callback pointer + * for a given type of management frame. + */ +QDF_STATUS +ol_txrx_mgmt_tx_cb_set(struct cdp_soc_t *soc, uint8_t pdev_id, uint8_t type, + ol_txrx_mgmt_tx_cb download_cb, + ol_txrx_mgmt_tx_cb ota_ack_cb, void *ctxt); + +/** + * ol_txrx_mgmt_send_ext() - Transmit a management frame + * @soc: Datapath soc handle + * @vdev_id: virtual interface id + * @tx_mgmt_frm: management frame to transmit + * @type: the type of management frame (determines what callback to use) + * @use_6mbps: specify whether management frame to transmit should + * use 6 Mbps rather than 1 Mbps min rate(for 5GHz band or P2P) + * @chanfreq: channel to transmit the frame on + * + * Send the specified management frame from the specified virtual device. + * The type is used for determining whether to invoke a callback to inform + * the sender that the tx mgmt frame was delivered, and if so, which + * callback to use. + * + * Return: 0 - the frame is accepted for transmission + * 1 - the frame was not accepted + */ +int +ol_txrx_mgmt_send_ext(struct cdp_soc_t *soc, uint8_t vdev_id, + qdf_nbuf_t tx_mgmt_frm, + uint8_t type, uint8_t use_6mbps, uint16_t chanfreq); + +qdf_nbuf_t +ol_tx_reinject(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu, uint16_t peer_id); + +#if defined(FEATURE_TSO) +void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg); +void ol_tso_seg_list_deinit(struct ol_txrx_pdev_t *pdev); +void ol_tso_num_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg); +void ol_tso_num_seg_list_deinit(struct ol_txrx_pdev_t *pdev); +uint32_t ol_tx_tso_get_stats_idx(struct ol_txrx_pdev_t *pdev); +uint8_t ol_tx_prepare_tso(ol_txrx_vdev_handle vdev, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info); +void ol_tx_tso_update_stats(struct ol_txrx_pdev_t *pdev, + struct qdf_tso_info_t *tso_info, qdf_nbuf_t msdu, + uint32_t tso_msdu_idx); +#else +static inline uint32_t ol_tx_tso_get_stats_idx(struct ol_txrx_pdev_t *pdev) +{ + return 0; +} + +static inline void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, + uint32_t num_seg) +{ +} + +static inline void ol_tso_seg_list_deinit(struct ol_txrx_pdev_t *pdev) +{ +} + +static inline void ol_tso_num_seg_list_init(struct ol_txrx_pdev_t *pdev, + uint32_t num_seg) +{ +} + +static inline void ol_tso_num_seg_list_deinit(struct ol_txrx_pdev_t *pdev) +{ +} + +static inline uint8_t ol_tx_prepare_tso(ol_txrx_vdev_handle vdev, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info) +{ + return 0; +} + +static inline void ol_tx_tso_update_stats(struct ol_txrx_pdev_t *pdev, + struct qdf_tso_info_t *tso_info, + qdf_nbuf_t msdu, + uint32_t tso_msdu_idx) +{ +} +#endif + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +bool ol_tx_desc_is_high_prio(qdf_nbuf_t msdu); +#endif + +#if defined(HELIUMPLUS) +void ol_txrx_dump_frag_desc(char *msg, struct ol_tx_desc_t *tx_desc); +#else +static inline +void ol_txrx_dump_frag_desc(char *msg, struct ol_tx_desc_t *tx_desc) +{ +} +#endif + +#endif /* _OL_TX__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_classify.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_classify.c new file mode 100644 index 0000000000000000000000000000000000000000..3d80f993b23f96df2ada90083cd32e30172c1e08 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_classify.c @@ -0,0 +1,917 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_nbuf_t, etc. */ +#include /* HTT_TX_EXT_TID_MGMT */ +#include /* htt_tx_desc_tid */ +#include /* ol_txrx_vdev_handle */ +#include /* ol_txrx_sync */ +#include +#include /* TXRX_ASSERT1 */ +#include /* pdev stats */ +#include /* ol_tx_desc */ +#include /* ol_tx_send */ +#include +#include +#include +#include +#include +#include +#include /* ETHERTYPE_VLAN, etc. */ +#include /* ieee80211_frame */ +#include +/* + * In theory, this tx classify code could be used on the host or in the target. + * Thus, this code uses generic OS primitives, that can be aliased to either + * the host's OS primitives or the target's OS primitives. + * For now, the following #defines set up these host-specific or + * target-specific aliases. + */ + +#define OL_TX_CLASSIFY_EXTENSION(vdev, tx_desc, netbuf, msdu_info, txq) +#define OL_TX_CLASSIFY_MGMT_EXTENSION(vdev, tx_desc, netbuf, msdu_info, txq) + +#ifdef QCA_TX_HTT2_SUPPORT +static void +ol_tx_classify_htt2_frm( + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t tx_nbuf, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + struct htt_msdu_info_t *htt = &tx_msdu_info->htt; + A_UINT8 candi_frm = 0; + + /* + * Offload the frame re-order to L3 protocol and ONLY support + * TCP protocol now. + */ + if ((htt->info.l2_hdr_type == htt_pkt_type_ethernet) && + (htt->info.frame_type == htt_frm_type_data) && + htt->info.is_unicast && + (htt->info.ethertype == ETHERTYPE_IPV4)) { + struct ipv4_hdr_t *ipHdr; + + ipHdr = (struct ipv4_hdr_t *)(qdf_nbuf_data(tx_nbuf) + + htt->info.l3_hdr_offset); + if (ipHdr->protocol == IP_PROTOCOL_TCP) + candi_frm = 1; + } + + qdf_nbuf_set_tx_parallel_dnload_frm(tx_nbuf, candi_frm); +} + +#define OL_TX_CLASSIFY_HTT2_EXTENSION(vdev, netbuf, msdu_info) \ + ol_tx_classify_htt2_frm(vdev, netbuf, msdu_info) +#else +#define OL_TX_CLASSIFY_HTT2_EXTENSION(vdev, netbuf, msdu_info) /* no-op */ +#endif /* QCA_TX_HTT2_SUPPORT */ +/* DHCP go with voice priority; WMM_AC_VO_TID1();*/ +#define TX_DHCP_TID 6 + +#if defined(QCA_BAD_PEER_TX_FLOW_CL) +static inline A_BOOL +ol_if_tx_bad_peer_txq_overflow( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + struct ol_tx_frms_queue_t *txq) +{ + if (peer && pdev && txq && (peer->tx_limit_flag) && + (txq->frms >= pdev->tx_peer_bal.peer_bal_txq_limit)) + return true; + else + return false; +} +#else +static inline A_BOOL ol_if_tx_bad_peer_txq_overflow( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + struct ol_tx_frms_queue_t *txq) +{ + return false; +} +#endif + +/* EAPOL go with voice priority: WMM_AC_TO_TID1(WMM_AC_VO);*/ +#define TX_EAPOL_TID 6 + +/* ARP go with voice priority: WMM_AC_TO_TID1(pdev->arp_ac_override)*/ +#define TX_ARP_TID 6 + +/* For non-IP case, use default TID */ +#define TX_DEFAULT_TID 0 + +/* + * Determine IP TOS priority + * IP Tos format : + * (Refer Pg 57 WMM-test-plan-v1.2) + * IP-TOS - 8bits + * : DSCP(6-bits) ECN(2-bits) + * : DSCP - P2 P1 P0 X X X + * where (P2 P1 P0) form 802.1D + */ +static inline A_UINT8 +ol_tx_tid_by_ipv4(A_UINT8 *pkt) +{ + A_UINT8 ipPri, tid; + struct ipv4_hdr_t *ipHdr = (struct ipv4_hdr_t *)pkt; + + ipPri = ipHdr->tos >> 5; + tid = ipPri & 0x7; + + return tid; +} + +static inline A_UINT8 +ol_tx_tid_by_ipv6(A_UINT8 *pkt) +{ + return (ipv6_traffic_class((struct ipv6_hdr_t *)pkt) >> 5) & 0x7; +} + +static inline void +ol_tx_set_ether_type( + A_UINT8 *datap, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + A_UINT16 typeorlength; + A_UINT8 *ptr; + A_UINT8 *l3_data_ptr; + + if (tx_msdu_info->htt.info.l2_hdr_type == htt_pkt_type_raw) { + /* adjust hdr_ptr to RA */ + struct ieee80211_frame *wh = (struct ieee80211_frame *)datap; + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_DATA) { + struct llc_snap_hdr_t *llc; + /* dot11 encapsulated frame */ + struct ieee80211_qosframe *whqos = + (struct ieee80211_qosframe *)datap; + if (whqos->i_fc[0] & QDF_IEEE80211_FC0_SUBTYPE_QOS) { + tx_msdu_info->htt.info.l3_hdr_offset = + sizeof(struct ieee80211_qosframe); + } else { + tx_msdu_info->htt.info.l3_hdr_offset = + sizeof(struct ieee80211_frame); + } + llc = (struct llc_snap_hdr_t *) + (datap + tx_msdu_info->htt.info.l3_hdr_offset); + tx_msdu_info->htt.info.ethertype = + (llc->ethertype[0] << 8) | llc->ethertype[1]; + /* + * l3_hdr_offset refers to the end of the 802.3 or + * 802.11 header, which may be a LLC/SNAP header rather + * than the IP header. + * Thus, don't increment l3_hdr_offset += sizeof(*llc); + * rather,leave it as is. + */ + } else { + /* + * This function should only be applied to data frames. + * For management frames, we already know to use + * HTT_TX_EXT_TID_MGMT. + */ + TXRX_ASSERT2(0); + } + } else if (tx_msdu_info->htt.info.l2_hdr_type == + htt_pkt_type_ethernet) { + ptr = (datap + QDF_MAC_ADDR_SIZE * 2); + typeorlength = (ptr[0] << 8) | ptr[1]; + /*ETHERNET_HDR_LEN;*/ + l3_data_ptr = datap + sizeof(struct ethernet_hdr_t); + + if (typeorlength == ETHERTYPE_VLAN) { + ptr = (datap + QDF_MAC_ADDR_SIZE * 2 + + ETHERTYPE_VLAN_LEN); + typeorlength = (ptr[0] << 8) | ptr[1]; + l3_data_ptr += ETHERTYPE_VLAN_LEN; + } + + if (!IS_ETHERTYPE(typeorlength)) { + /* 802.3 header*/ + struct llc_snap_hdr_t *llc_hdr = + (struct llc_snap_hdr_t *)l3_data_ptr; + typeorlength = (llc_hdr->ethertype[0] << 8) | + llc_hdr->ethertype[1]; + l3_data_ptr += sizeof(struct llc_snap_hdr_t); + } + + tx_msdu_info->htt.info.l3_hdr_offset = (A_UINT8)(l3_data_ptr - + datap); + tx_msdu_info->htt.info.ethertype = typeorlength; + } +} + +static inline A_UINT8 +ol_tx_tid_by_ether_type( + A_UINT8 *datap, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + A_UINT8 tid; + A_UINT8 *l3_data_ptr; + A_UINT16 typeorlength; + + l3_data_ptr = datap + tx_msdu_info->htt.info.l3_hdr_offset; + typeorlength = tx_msdu_info->htt.info.ethertype; + + /* IP packet, do packet inspection for TID */ + if (typeorlength == ETHERTYPE_IPV4) { + tid = ol_tx_tid_by_ipv4(l3_data_ptr); + } else if (typeorlength == ETHERTYPE_IPV6) { + tid = ol_tx_tid_by_ipv6(l3_data_ptr); + } else if (ETHERTYPE_IS_EAPOL_WAPI(typeorlength)) { + /* EAPOL go with voice priority*/ + tid = TX_EAPOL_TID; + } else if (typeorlength == ETHERTYPE_ARP) { + tid = TX_ARP_TID; + } else { + /* For non-IP case, use default TID */ + tid = TX_DEFAULT_TID; + } + return tid; +} + +static inline A_UINT8 +ol_tx_tid_by_raw_type( + A_UINT8 *datap, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + A_UINT8 tid = HTT_TX_EXT_TID_NON_QOS_MCAST_BCAST; + + /* adjust hdr_ptr to RA */ + struct ieee80211_frame *wh = (struct ieee80211_frame *)datap; + + /* FIXME: This code does not handle 4 address formats. The QOS field + * is not at usual location. + */ + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_DATA) { + /* dot11 encapsulated frame */ + struct ieee80211_qosframe *whqos = + (struct ieee80211_qosframe *)datap; + if (whqos->i_fc[0] & QDF_IEEE80211_FC0_SUBTYPE_QOS) + tid = whqos->i_qos[0] & IEEE80211_QOS_TID; + else + tid = HTT_NON_QOS_TID; + } else { + /* + * This function should only be applied to data frames. + * For management frames, we already know to use + * HTT_TX_EXT_TID_MGMT. + */ + qdf_assert(0); + } + return tid; +} + +static A_UINT8 +ol_tx_tid( + struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t tx_nbuf, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + A_UINT8 *datap = qdf_nbuf_data(tx_nbuf); + A_UINT8 tid; + + if (pdev->frame_format == wlan_frm_fmt_raw) { + tx_msdu_info->htt.info.l2_hdr_type = htt_pkt_type_raw; + + ol_tx_set_ether_type(datap, tx_msdu_info); + tid = tx_msdu_info->htt.info.ext_tid == + QDF_NBUF_TX_EXT_TID_INVALID ? + ol_tx_tid_by_raw_type(datap, tx_msdu_info) : + tx_msdu_info->htt.info.ext_tid; + } else if (pdev->frame_format == wlan_frm_fmt_802_3) { + tx_msdu_info->htt.info.l2_hdr_type = htt_pkt_type_ethernet; + + ol_tx_set_ether_type(datap, tx_msdu_info); + tid = + tx_msdu_info->htt.info.ext_tid == + QDF_NBUF_TX_EXT_TID_INVALID ? + ol_tx_tid_by_ether_type(datap, tx_msdu_info) : + tx_msdu_info->htt.info.ext_tid; + } else if (pdev->frame_format == wlan_frm_fmt_native_wifi) { + struct llc_snap_hdr_t *llc; + + tx_msdu_info->htt.info.l2_hdr_type = htt_pkt_type_native_wifi; + tx_msdu_info->htt.info.l3_hdr_offset = + sizeof(struct ieee80211_frame); + llc = (struct llc_snap_hdr_t *) + (datap + tx_msdu_info->htt.info.l3_hdr_offset); + tx_msdu_info->htt.info.ethertype = + (llc->ethertype[0] << 8) | llc->ethertype[1]; + /* + * Native WiFi is a special case of "raw" 802.11 header format. + * However, we expect that for all cases that use native WiFi, + * the TID will be directly specified out of band. + */ + tid = tx_msdu_info->htt.info.ext_tid; + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_FATAL, + "Invalid standard frame type: %d\n", + pdev->frame_format); + qdf_assert(0); + tid = HTT_TX_EXT_TID_INVALID; + } + return tid; +} + +#if defined(FEATURE_WLAN_TDLS) +static inline +struct ol_txrx_peer_t *ol_tx_tdls_peer_find(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + uint8_t *dest_addr, + uint8_t *peer_id) +{ + struct ol_txrx_peer_t *peer = NULL; + uint8_t zero_mac_addr[QDF_MAC_ADDR_SIZE] = { 0, 0, 0, 0, 0, 0 }; + enum peer_debug_id_type id_type = PEER_DEBUG_ID_OL_INTERNAL; + + struct ol_txrx_peer_t *(*find_peer)(struct ol_txrx_pdev_t *pdev, + uint8_t *peer_mac_addr, + int mac_addr_is_aligned, + u8 check_valid, + enum peer_debug_id_type dbg_id) + = ol_txrx_peer_find_hash_find_get_ref; + + if (vdev->hlTdlsFlag) { + peer = find_peer(pdev, vdev->hl_tdls_ap_mac_addr.raw, + 0, 1, id_type); + + if (peer && (peer->peer_ids[0] == HTT_INVALID_PEER_ID)) { + ol_txrx_peer_release_ref(peer, id_type); + peer = NULL; + } else { + if (peer) { + *peer_id = peer->local_id; + return peer; + } + } + } + + /* Packets destined to TDLS Peer or AP with 'No TDLS Link'. + * Optimized to directly get the peer based on 'dest_addr' + */ + if (vdev->last_real_peer && + !qdf_mem_cmp(vdev->last_real_peer->mac_addr.raw, + dest_addr, QDF_MAC_ADDR_SIZE)) { + ol_txrx_peer_get_ref(vdev->last_real_peer, id_type); + *peer_id = vdev->last_real_peer->local_id; + peer = vdev->last_real_peer; + } else { + /* packets destined for other peers or AP with TDLS Link */ + if (vdev->last_real_peer && + !qdf_mem_cmp(vdev->hl_tdls_ap_mac_addr.raw, + zero_mac_addr, + QDF_MAC_ADDR_SIZE)) { + /* With No TDLS Link return last_real_peer for both AP + * and other bss peer + */ + ol_txrx_peer_get_ref(vdev->last_real_peer, id_type); + *peer_id = vdev->last_real_peer->local_id; + peer = vdev->last_real_peer; + } else { /* packet destined for other peers and AP when + * STA has TDLS link + */ + peer = find_peer(pdev, vdev->hl_tdls_ap_mac_addr.raw, + 0, 1, id_type); + + if (peer && + (peer->peer_ids[0] == HTT_INVALID_PEER_ID)) { + ol_txrx_peer_release_ref(peer, id_type); + peer = NULL; + } else { + if (peer) + *peer_id = peer->local_id; + } + } + } + return peer; +} + +#else +static struct ol_txrx_peer_t *ol_tx_tdls_peer_find(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + uint8_t *dest_addr, + uint8_t *peer_id) +{ + struct ol_txrx_peer_t *peer = NULL; + + peer = ol_txrx_assoc_peer_find(vdev); + + return peer; +} +#endif + +struct ol_tx_frms_queue_t * +ol_tx_classify( + struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t tx_nbuf, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + struct ol_txrx_peer_t *peer = NULL; + struct ol_tx_frms_queue_t *txq = NULL; + A_UINT8 *dest_addr; + A_UINT8 tid; + u_int8_t peer_id; + + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + dest_addr = ol_tx_dest_addr_find(pdev, tx_nbuf); + if (unlikely(!dest_addr)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "Error: dest_addr is NULL.\n"); + return NULL; /*error*/ + } + if ((IEEE80211_IS_MULTICAST(dest_addr)) || + (vdev->opmode == wlan_op_mode_ocb)) { + txq = &vdev->txqs[OL_TX_VDEV_MCAST_BCAST]; + tx_msdu_info->htt.info.ext_tid = + HTT_TX_EXT_TID_NON_QOS_MCAST_BCAST; + if (vdev->opmode == wlan_op_mode_sta) { + /* + * The STA sends a frame with a broadcast + * dest addr (DA) as a + * unicast frame to the AP's receive addr (RA). + * Find the peer object that represents the AP + * that the STA is associated with. + */ + peer = ol_txrx_assoc_peer_find(vdev); + if (!peer) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "Error: STA %pK ("QDF_MAC_ADDR_FMT") trying to send bcast DA tx data frame w/o association\n", + vdev, + QDF_MAC_ADDR_REF(vdev->mac_addr.raw)); + return NULL; /* error */ + } else if ((peer->security[ + OL_TXRX_PEER_SECURITY_MULTICAST].sec_type + != htt_sec_type_wapi) && + (qdf_nbuf_is_ipv4_pkt(tx_nbuf) == true)) { + if (QDF_NBUF_CB_PACKET_TYPE_DHCP == + QDF_NBUF_CB_GET_PACKET_TYPE( + tx_nbuf)) { + /* DHCP frame to go with + * voice priority + */ + txq = &peer->txqs[TX_DHCP_TID]; + tx_msdu_info->htt.info.ext_tid = + TX_DHCP_TID; + } + } + /* + * The following line assumes each peer object has a + * single ID. This is currently true, and is expected + * to remain true. + */ + tx_msdu_info->htt.info.peer_id = peer->peer_ids[0]; + } else if (vdev->opmode == wlan_op_mode_ocb) { + tx_msdu_info->htt.info.peer_id = HTT_INVALID_PEER_ID; + /* + * In OCB mode, don't worry about the peer. + * We don't need it. + */ + peer = NULL; + } else { + tx_msdu_info->htt.info.peer_id = HTT_INVALID_PEER_ID; + /* + * Look up the vdev's BSS peer, so that the + * classify_extension function can check whether to + * encrypt multicast / broadcast frames. + */ + peer = ol_txrx_peer_find_hash_find_get_ref + (pdev, + vdev->mac_addr.raw, + 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + if (!peer) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "Error: vdev %pK ("QDF_MAC_ADDR_FMT") trying to send bcast/mcast, but no self-peer found\n", + vdev, + QDF_MAC_ADDR_REF(vdev->mac_addr.raw)); + return NULL; /* error */ + } + } + tx_msdu_info->htt.info.is_unicast = false; + } else { + /* tid would be overwritten for non QoS case*/ + tid = ol_tx_tid(pdev, tx_nbuf, tx_msdu_info); + if ((HTT_TX_EXT_TID_INVALID == tid) || + (tid >= OL_TX_NUM_TIDS)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "%s Error: could not classify packet into valid TID(%d).\n", + __func__, tid); + return NULL; + } +#ifdef ATH_SUPPORT_WAPI + /* Check to see if a frame is a WAI frame */ + if (tx_msdu_info->htt.info.ethertype == ETHERTYPE_WAI) { + /* WAI frames should not be encrypted */ + tx_msdu_info->htt.action.do_encrypt = 0; + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "Tx Frame is a WAI frame\n"); + } +#endif /* ATH_SUPPORT_WAPI */ + + /* + * Find the peer and increment its reference count. + * If this vdev is an AP, use the dest addr (DA) to determine + * which peer STA this unicast data frame is for. + * If this vdev is a STA, the unicast data frame is for the + * AP the STA is associated with. + */ + if (vdev->opmode == wlan_op_mode_sta) { + /* + * TO DO: + * To support TDLS, first check if there is a TDLS + * peer STA, + * and if so, check if the DA matches the TDLS peer + * STA's MAC address. If there is no peer TDLS STA, + * or if the DA is not the TDLS STA's address, + * then the frame is either for the AP itself, or is + * supposed to be sent to the AP for forwarding. + */ + peer = ol_tx_tdls_peer_find(pdev, vdev, + dest_addr, + &peer_id); + } else { + peer = ol_txrx_peer_find_hash_find_get_ref(pdev, + dest_addr, + 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + } + tx_msdu_info->htt.info.is_unicast = true; + if (!peer) { + /* + * Unicast data xfer can only happen to an + * associated peer. It is illegitimate to send unicast + * data if there is no peer to send it to. + */ + ol_txrx_err_rl("Error: vdev %pK (" QDF_MAC_ADDR_FMT ") trying to send unicast tx data frame to an unknown peer", + vdev, + QDF_MAC_ADDR_REF(vdev->mac_addr.raw)); + return NULL; /* error */ + } + TX_SCHED_DEBUG_PRINT("Peer found\n"); + if (!peer->qos_capable) { + tid = OL_TX_NON_QOS_TID; + } else if ((peer->security[ + OL_TXRX_PEER_SECURITY_UNICAST].sec_type + != htt_sec_type_wapi) && + (qdf_nbuf_is_ipv4_pkt(tx_nbuf) == true)) { + if (QDF_NBUF_CB_PACKET_TYPE_DHCP == + QDF_NBUF_CB_GET_PACKET_TYPE(tx_nbuf)) + /* DHCP frame to go with voice priority */ + tid = TX_DHCP_TID; + } + + /* Only allow encryption when in authenticated state */ + if (OL_TXRX_PEER_STATE_AUTH != peer->state) + tx_msdu_info->htt.action.do_encrypt = 0; + + txq = &peer->txqs[tid]; + tx_msdu_info->htt.info.ext_tid = tid; + /* + * The following line assumes each peer object has a single ID. + * This is currently true, and is expected to remain true. + */ + tx_msdu_info->htt.info.peer_id = peer->peer_ids[0]; + /* + * WORKAROUND - check that the peer ID is valid. + * If tx data is provided before ol_rx_peer_map_handler is + * called to record the peer ID specified by the target, + * then we could end up here with an invalid peer ID. + * TO DO: rather than dropping the tx frame, pause the txq it + * goes into, then fill in the peer ID for the entries in the + * txq when the peer_map event provides the peer ID, and then + * unpause the txq. + */ + if (tx_msdu_info->htt.info.peer_id == HTT_INVALID_PEER_ID) { + if (peer) { + ol_txrx_info("remove the peer for invalid peer_id %pK", + peer); + /* remove the peer reference added above */ + ol_txrx_peer_release_ref + (peer, + PEER_DEBUG_ID_OL_INTERNAL); + tx_msdu_info->peer = NULL; + } + return NULL; + } + } + tx_msdu_info->peer = peer; + if (ol_if_tx_bad_peer_txq_overflow(pdev, peer, txq)) + return NULL; + /* + * If relevant, do a deeper inspection to determine additional + * characteristics of the tx frame. + * If the frame is invalid, then the txq will be set to NULL to + * indicate an error. + */ + OL_TX_CLASSIFY_EXTENSION(vdev, tx_desc, tx_nbuf, tx_msdu_info, txq); + if (IEEE80211_IS_MULTICAST(dest_addr) && vdev->opmode != + wlan_op_mode_sta && tx_msdu_info->peer != + NULL) { + ol_txrx_dbg("remove the peer reference %pK", peer); + /* remove the peer reference added above */ + ol_txrx_peer_release_ref(tx_msdu_info->peer, + PEER_DEBUG_ID_OL_INTERNAL); + /* Making peer NULL in case if multicast non STA mode */ + tx_msdu_info->peer = NULL; + } + + /* Whether this frame can download though HTT2 data pipe or not. */ + OL_TX_CLASSIFY_HTT2_EXTENSION(vdev, tx_nbuf, tx_msdu_info); + + /* Update Tx Queue info */ + tx_desc->txq = txq; + + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); + return txq; +} + +struct ol_tx_frms_queue_t * +ol_tx_classify_mgmt( + struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t tx_nbuf, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + struct ol_txrx_peer_t *peer = NULL; + struct ol_tx_frms_queue_t *txq = NULL; + A_UINT8 *dest_addr; + union ol_txrx_align_mac_addr_t local_mac_addr_aligned, *mac_addr; + + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + dest_addr = ol_tx_dest_addr_find(pdev, tx_nbuf); + if (unlikely(!dest_addr)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "Error: dest_addr is NULL.\n"); + return NULL; /*error*/ + } + if (IEEE80211_IS_MULTICAST(dest_addr)) { + /* + * AP: beacons are broadcast, + * public action frames (e.g. extended channel + * switch announce) may be broadcast + * STA: probe requests can be either broadcast or unicast + */ + txq = &vdev->txqs[OL_TX_VDEV_DEFAULT_MGMT]; + tx_msdu_info->htt.info.peer_id = HTT_INVALID_PEER_ID; + tx_msdu_info->peer = NULL; + tx_msdu_info->htt.info.is_unicast = 0; + } else { + /* + * Find the peer and increment its reference count. + * If this vdev is an AP, use the receiver addr (RA) to + * determine which peer STA this unicast mgmt frame is for. + * If this vdev is a STA, the unicast mgmt frame is for the + * AP the STA is associated with. + * Probe request / response and Assoc request / response are + * sent before the peer exists - in this case, use the + * vdev's default tx queue. + */ + if (vdev->opmode == wlan_op_mode_sta) { + /* + * TO DO: + * To support TDLS, first check if there is a TDLS + * peer STA, and if so, check if the DA matches + * the TDLS peer STA's MAC address. + */ + peer = ol_txrx_assoc_peer_find(vdev); + /* + * Some special case(preauth for example) needs to send + * unicast mgmt frame to unassociated AP. In such case, + * we need to check if dest addr match the associated + * peer addr. If not, we set peer as NULL to queue this + * frame to vdev queue. + */ + if (peer) { + + qdf_mem_copy( + &local_mac_addr_aligned.raw[0], + dest_addr, QDF_MAC_ADDR_SIZE); + mac_addr = &local_mac_addr_aligned; + if (ol_txrx_peer_find_mac_addr_cmp + (mac_addr, + &peer->mac_addr) != 0) { + ol_txrx_peer_release_ref + (peer, + PEER_DEBUG_ID_OL_INTERNAL); + peer = NULL; + } + } + } else { + /* find the peer and increment its reference count */ + peer = ol_txrx_peer_find_hash_find_get_ref(pdev, + dest_addr, + 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + } + tx_msdu_info->peer = peer; + if (!peer) { + txq = &vdev->txqs[OL_TX_VDEV_DEFAULT_MGMT]; + tx_msdu_info->htt.info.peer_id = HTT_INVALID_PEER_ID; + } else { + txq = &peer->txqs[HTT_TX_EXT_TID_MGMT]; + tx_msdu_info->htt.info.ext_tid = HTT_TX_EXT_TID_MGMT; + /* + * The following line assumes each peer object has a + * single ID. This is currently true, and is expected + * to remain true. + */ + tx_msdu_info->htt.info.peer_id = peer->peer_ids[0]; + } + tx_msdu_info->htt.info.is_unicast = 1; + } + /* + * If relevant, do a deeper inspection to determine additional + * characteristics of the tx frame. + * If the frame is invalid, then the txq will be set to NULL to + * indicate an error. + */ + OL_TX_CLASSIFY_MGMT_EXTENSION(vdev, tx_desc, tx_nbuf, + tx_msdu_info, txq); + + /* Whether this frame can download though HTT2 data pipe or not. */ + OL_TX_CLASSIFY_HTT2_EXTENSION(vdev, tx_nbuf, tx_msdu_info); + + /* Update Tx Queue info */ + tx_desc->txq = txq; + + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); + return txq; +} + +#ifdef currently_unused +QDF_STATUS +ol_tx_classify_extension( + struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t tx_msdu, + struct ol_txrx_msdu_info_t *msdu_info) +{ + u8 *datap = qdf_nbuf_data(tx_msdu); + struct ol_txrx_peer_t *peer; + int which_key; + + /* + * The following msdu_info fields were already filled in by the + * ol_tx entry function or the regular ol_tx_classify function: + * htt.info.vdev_id (ol_tx_hl or ol_tx_non_std_hl) + * htt.info.ext_tid (ol_tx_non_std_hl or ol_tx_classify) + * htt.info.frame_type (ol_tx_hl or ol_tx_non_std_hl) + * htt.info.l2_hdr_type (ol_tx_hl or ol_tx_non_std_hl) + * htt.info.is_unicast (ol_tx_classify) + * htt.info.peer_id (ol_tx_classify) + * peer (ol_tx_classify) + * if (is_unicast) { + * htt.info.ethertype (ol_tx_classify) + * htt.info.l3_hdr_offset (ol_tx_classify) + * } + * The following fields need to be filled in by this function: + * if (!is_unicast) { + * htt.info.ethertype + * htt.info.l3_hdr_offset + * } + * htt.action.band (NOT CURRENTLY USED) + * htt.action.do_encrypt + * htt.action.do_tx_complete + * The following fields are not needed for data frames, and can + * be left uninitialized: + * htt.info.frame_subtype + */ + + if (!msdu_info->htt.info.is_unicast) { + int l2_hdr_size; + u16 ethertype; + + if (msdu_info->htt.info.l2_hdr_type == htt_pkt_type_ethernet) { + struct ethernet_hdr_t *eh; + + eh = (struct ethernet_hdr_t *)datap; + l2_hdr_size = sizeof(*eh); + ethertype = (eh->ethertype[0] << 8) | eh->ethertype[1]; + + if (ethertype == ETHERTYPE_VLAN) { + struct ethernet_vlan_hdr_t *evh; + + evh = (struct ethernet_vlan_hdr_t *)datap; + l2_hdr_size = sizeof(*evh); + ethertype = (evh->ethertype[0] << 8) | + evh->ethertype[1]; + } + + if (!IS_ETHERTYPE(ethertype)) { + /* 802.3 header*/ + struct llc_snap_hdr_t *llc = + (struct llc_snap_hdr_t *)(datap + + l2_hdr_size); + ethertype = (llc->ethertype[0] << 8) | + llc->ethertype[1]; + l2_hdr_size += sizeof(*llc); + } + msdu_info->htt.info.l3_hdr_offset = l2_hdr_size; + msdu_info->htt.info.ethertype = ethertype; + } else { /* 802.11 */ + struct llc_snap_hdr_t *llc; + + l2_hdr_size = ol_txrx_ieee80211_hdrsize(datap); + llc = (struct llc_snap_hdr_t *)(datap + l2_hdr_size); + ethertype = (llc->ethertype[0] << 8) | + llc->ethertype[1]; + /* + * Don't include the LLC/SNAP header in l2_hdr_size, + * because l3_hdr_offset is actually supposed to refer + * to the header after the 802.3 or 802.11 header, + * which could be a LLC/SNAP header rather + * than the L3 header. + */ + } + msdu_info->htt.info.l3_hdr_offset = l2_hdr_size; + msdu_info->htt.info.ethertype = ethertype; + which_key = txrx_sec_mcast; + } else { + which_key = txrx_sec_ucast; + } + peer = msdu_info->peer; + /* + * msdu_info->htt.action.do_encrypt is initially set in ol_tx_desc_hl. + * Add more check here. + */ + msdu_info->htt.action.do_encrypt = (!peer) ? 0 : + (peer->security[which_key].sec_type == htt_sec_type_none) ? 0 : + msdu_info->htt.action.do_encrypt; + /* + * For systems that have a frame by frame spec for whether to receive + * a tx completion notification, use the tx completion notification + * only for certain management frames, not for data frames. + * (In the future, this may be changed slightly, e.g. to request a + * tx completion notification for the final EAPOL message sent by a + * STA during the key delivery handshake.) + */ + msdu_info->htt.action.do_tx_complete = 0; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ol_tx_classify_mgmt_extension( + struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t tx_msdu, + struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ieee80211_frame *wh; + + /* + * The following msdu_info fields were already filled in by the + * ol_tx entry function or the regular ol_tx_classify_mgmt function: + * htt.info.vdev_id (ol_txrx_mgmt_send) + * htt.info.frame_type (ol_txrx_mgmt_send) + * htt.info.l2_hdr_type (ol_txrx_mgmt_send) + * htt.action.do_tx_complete (ol_txrx_mgmt_send) + * htt.info.peer_id (ol_tx_classify_mgmt) + * htt.info.ext_tid (ol_tx_classify_mgmt) + * htt.info.is_unicast (ol_tx_classify_mgmt) + * peer (ol_tx_classify_mgmt) + * The following fields need to be filled in by this function: + * htt.info.frame_subtype + * htt.info.l3_hdr_offset + * htt.action.band (NOT CURRENTLY USED) + * The following fields are not needed for mgmt frames, and can + * be left uninitialized: + * htt.info.ethertype + * htt.action.do_encrypt + * (This will be filled in by other SW, which knows whether + * the peer has robust-management-frames enabled.) + */ + wh = (struct ieee80211_frame *)qdf_nbuf_data(tx_msdu); + msdu_info->htt.info.frame_subtype = + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >> + IEEE80211_FC0_SUBTYPE_SHIFT; + msdu_info->htt.info.l3_hdr_offset = sizeof(struct ieee80211_frame); + + return QDF_STATUS_SUCCESS; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_classify.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_classify.h new file mode 100644 index 0000000000000000000000000000000000000000..b88f329476cd6425df37fbd32899b64f0f82378b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_classify.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2012, 2014, 2016 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_tx_classify.h + * @brief API definitions for the tx classify module within the data SW. + */ +#ifndef _OL_TX_CLASSIFY__H_ +#define _OL_TX_CLASSIFY__H_ + +#include /* qdf_nbuf_t */ +#include /* ol_txrx_vdev_t, etc. */ + +static inline u_int8_t * +ol_tx_dest_addr_find( + struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t tx_nbuf) +{ + u_int8_t *hdr_ptr; + void *datap = qdf_nbuf_data(tx_nbuf); + + if (pdev->frame_format == wlan_frm_fmt_raw) { + /* adjust hdr_ptr to RA */ + struct ieee80211_frame *wh = + (struct ieee80211_frame *)datap; + hdr_ptr = wh->i_addr1; + } else if (pdev->frame_format == + wlan_frm_fmt_native_wifi) { + /* adjust hdr_ptr to RA */ + struct ieee80211_frame *wh = ( + struct ieee80211_frame *)datap; + hdr_ptr = wh->i_addr1; + } else if (pdev->frame_format == wlan_frm_fmt_802_3) { + hdr_ptr = datap; + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "Invalid standard frame type: %d\n", + pdev->frame_format); + qdf_assert(0); + hdr_ptr = NULL; + } + return hdr_ptr; +} + +#if defined(CONFIG_HL_SUPPORT) + +/** + * @brief Classify a tx frame to which tid queue. + * + * @param vdev - the virtual device sending the data + * (for specifying the transmitter address for multicast / broadcast data) + * @param tx_desc - descriptor object with meta-data about the tx frame + * @param netbuf - the tx frame + * @param tx_msdu_info - characteristics of the tx frame + */ +struct ol_tx_frms_queue_t * +ol_tx_classify( + struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t netbuf, + struct ol_txrx_msdu_info_t *tx_msdu_info); + +struct ol_tx_frms_queue_t * +ol_tx_classify_mgmt( + struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t netbuf, + struct ol_txrx_msdu_info_t *tx_msdu_info); + +#else + +#define ol_tx_classify(vdev, tx_desc, netbuf, tx_msdu_info) NULL +#define ol_tx_classify_mgmt(vdev, tx_desc, netbuf, tx_msdu_info) NULL + +#endif /* defined(CONFIG_HL_SUPPORT) */ + + +#endif /* _OL_TX_CLASSIFY__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_desc.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_desc.c new file mode 100644 index 0000000000000000000000000000000000000000..f56d917c152a69504cd979b38a787e321abfde2b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_desc.c @@ -0,0 +1,1148 @@ +/* + * Copyright (c) 2011, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* QDF_NBUF_EXEMPT_NO_EXEMPTION, etc. */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_assert */ +#include /* qdf_spinlock */ +#include /* qdf_tso_seg_dbg stuff */ +#ifdef QCA_COMPUTE_TX_DELAY +#include /* qdf_system_ticks */ +#endif + +#include /* htt_tx_desc_id */ + +#include +#include +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP +#include /* OL_TX_RESTORE_HDR, etc */ +#endif +#include + +#ifdef QCA_SUPPORT_TXDESC_SANITY_CHECKS +static inline void ol_tx_desc_sanity_checks(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + if (tx_desc->pkt_type != ol_tx_frm_freed) { + ol_txrx_err("Potential tx_desc corruption pkt_type:0x%x pdev:0x%pK", + tx_desc->pkt_type, pdev); + qdf_assert(0); + } +} +static inline void ol_tx_desc_reset_pkt_type(struct ol_tx_desc_t *tx_desc) +{ + tx_desc->pkt_type = ol_tx_frm_freed; +} +#ifdef QCA_COMPUTE_TX_DELAY +static inline void ol_tx_desc_compute_delay(struct ol_tx_desc_t *tx_desc) +{ + if (tx_desc->entry_timestamp_ticks != 0xffffffff) { + ol_txrx_err("Timestamp:0x%x", tx_desc->entry_timestamp_ticks); + qdf_assert(0); + } + tx_desc->entry_timestamp_ticks = qdf_system_ticks(); +} +static inline void ol_tx_desc_reset_timestamp(struct ol_tx_desc_t *tx_desc) +{ + tx_desc->entry_timestamp_ticks = 0xffffffff; +} +#endif +#else +static inline void ol_tx_desc_sanity_checks(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ +} +static inline void ol_tx_desc_reset_pkt_type(struct ol_tx_desc_t *tx_desc) +{ +} +static inline void ol_tx_desc_compute_delay(struct ol_tx_desc_t *tx_desc) +{ +} +static inline void ol_tx_desc_reset_timestamp(struct ol_tx_desc_t *tx_desc) +{ +} +#endif + +#ifdef DESC_TIMESTAMP_DEBUG_INFO +static inline void ol_tx_desc_update_tx_ts(struct ol_tx_desc_t *tx_desc) +{ + tx_desc->desc_debug_info.prev_tx_ts = tx_desc + ->desc_debug_info.curr_tx_ts; + tx_desc->desc_debug_info.curr_tx_ts = qdf_get_log_timestamp(); +} +#else +static inline void ol_tx_desc_update_tx_ts(struct ol_tx_desc_t *tx_desc) +{ +} +#endif + +/** + * ol_tx_desc_vdev_update() - vedv assign. + * @tx_desc: tx descriptor pointer + * @vdev: vdev handle + * + * Return: None + */ +static inline void +ol_tx_desc_vdev_update(struct ol_tx_desc_t *tx_desc, + struct ol_txrx_vdev_t *vdev) +{ + tx_desc->vdev = vdev; + tx_desc->vdev_id = vdev->vdev_id; +} + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL + +/** + * ol_tx_desc_count_inc() - tx desc count increment for desc allocation. + * @vdev: vdev handle + * + * Return: None + */ +static inline void +ol_tx_desc_count_inc(struct ol_txrx_vdev_t *vdev) +{ + qdf_atomic_inc(&vdev->tx_desc_count); +} +#else + +static inline void +ol_tx_desc_count_inc(struct ol_txrx_vdev_t *vdev) +{ +} + +#endif + +#ifndef QCA_LL_TX_FLOW_CONTROL_V2 +#ifdef QCA_LL_PDEV_TX_FLOW_CONTROL +/** + * ol_tx_do_pdev_flow_control_pause - pause queues when stop_th reached. + * @pdev: pdev handle + * + * Return: void + */ +static void ol_tx_do_pdev_flow_control_pause(struct ol_txrx_pdev_t *pdev) +{ + struct ol_txrx_vdev_t *vdev; + + if (qdf_unlikely(pdev->tx_desc.num_free < + pdev->tx_desc.stop_th && + pdev->tx_desc.num_free >= + pdev->tx_desc.stop_priority_th && + pdev->tx_desc.status == + FLOW_POOL_ACTIVE_UNPAUSED)) { + pdev->tx_desc.status = FLOW_POOL_NON_PRIO_PAUSED; + /* pause network NON PRIORITY queues */ + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + pdev->pause_cb(vdev->vdev_id, + WLAN_STOP_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); + } + } else if (qdf_unlikely((pdev->tx_desc.num_free < + pdev->tx_desc.stop_priority_th) && + pdev->tx_desc.status == + FLOW_POOL_NON_PRIO_PAUSED)) { + pdev->tx_desc.status = FLOW_POOL_ACTIVE_PAUSED; + /* pause priority queue */ + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + pdev->pause_cb(vdev->vdev_id, + WLAN_NETIF_PRIORITY_QUEUE_OFF, + WLAN_DATA_FLOW_CONTROL_PRIORITY); + } + } +} + +/** + * ol_tx_do_pdev_flow_control_unpause - unpause queues when start_th restored. + * @pdev: pdev handle + * + * Return: void + */ +static void ol_tx_do_pdev_flow_control_unpause(struct ol_txrx_pdev_t *pdev) +{ + struct ol_txrx_vdev_t *vdev; + + switch (pdev->tx_desc.status) { + case FLOW_POOL_ACTIVE_PAUSED: + if (pdev->tx_desc.num_free > + pdev->tx_desc.start_priority_th) { + /* unpause priority queue */ + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + pdev->pause_cb(vdev->vdev_id, + WLAN_NETIF_PRIORITY_QUEUE_ON, + WLAN_DATA_FLOW_CONTROL_PRIORITY); + } + pdev->tx_desc.status = FLOW_POOL_NON_PRIO_PAUSED; + } + break; + case FLOW_POOL_NON_PRIO_PAUSED: + if (pdev->tx_desc.num_free > pdev->tx_desc.start_th) { + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + pdev->pause_cb(vdev->vdev_id, + WLAN_WAKE_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); + } + pdev->tx_desc.status = FLOW_POOL_ACTIVE_UNPAUSED; + } + break; + case FLOW_POOL_INVALID: + if (pdev->tx_desc.num_free == pdev->tx_desc.pool_size) + ol_txrx_err("pool is INVALID State!!"); + break; + case FLOW_POOL_ACTIVE_UNPAUSED: + break; + default: + ol_txrx_err("pool is INACTIVE State!!\n"); + break; + }; +} +#else +static inline void +ol_tx_do_pdev_flow_control_pause(struct ol_txrx_pdev_t *pdev) +{ +} + +static inline void +ol_tx_do_pdev_flow_control_unpause(struct ol_txrx_pdev_t *pdev) +{ +} +#endif +/** + * ol_tx_desc_alloc() - allocate descriptor from freelist + * @pdev: pdev handle + * @vdev: vdev handle + * + * Return: tx descriptor pointer/ NULL in case of error + */ +static +struct ol_tx_desc_t *ol_tx_desc_alloc(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev) +{ + struct ol_tx_desc_t *tx_desc = NULL; + + qdf_spin_lock_bh(&pdev->tx_mutex); + if (pdev->tx_desc.freelist) { + tx_desc = ol_tx_get_desc_global_pool(pdev); + if (!tx_desc) { + qdf_spin_unlock_bh(&pdev->tx_mutex); + return NULL; + } + ol_tx_desc_dup_detect_set(pdev, tx_desc); + ol_tx_do_pdev_flow_control_pause(pdev); + ol_tx_desc_sanity_checks(pdev, tx_desc); + ol_tx_desc_compute_delay(tx_desc); + ol_tx_desc_vdev_update(tx_desc, vdev); + ol_tx_desc_count_inc(vdev); + ol_tx_desc_update_tx_ts(tx_desc); + qdf_atomic_inc(&tx_desc->ref_cnt); + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + return tx_desc; +} + +/** + * ol_tx_desc_alloc_wrapper() -allocate tx descriptor + * @pdev: pdev handler + * @vdev: vdev handler + * @msdu_info: msdu handler + * + * Return: tx descriptor or NULL + */ +struct ol_tx_desc_t * +ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + struct ol_txrx_msdu_info_t *msdu_info) +{ + return ol_tx_desc_alloc(pdev, vdev); +} + +#else +/** + * ol_tx_desc_alloc() -allocate tx descriptor + * @pdev: pdev handler + * @vdev: vdev handler + * @pool: flow pool + * + * Return: tx descriptor or NULL + */ +static +struct ol_tx_desc_t *ol_tx_desc_alloc(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + struct ol_tx_flow_pool_t *pool) +{ + struct ol_tx_desc_t *tx_desc = NULL; + + if (!pool) { + pdev->pool_stats.pkt_drop_no_pool++; + goto end; + } + + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->avail_desc) { + tx_desc = ol_tx_get_desc_flow_pool(pool); + ol_tx_desc_dup_detect_set(pdev, tx_desc); + if (qdf_unlikely(pool->avail_desc < pool->stop_th && + (pool->avail_desc >= pool->stop_priority_th) && + (pool->status == FLOW_POOL_ACTIVE_UNPAUSED))) { + pool->status = FLOW_POOL_NON_PRIO_PAUSED; + /* pause network NON PRIORITY queues */ + pdev->pause_cb(vdev->vdev_id, + WLAN_STOP_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); + } else if (qdf_unlikely((pool->avail_desc < + pool->stop_priority_th) && + pool->status == FLOW_POOL_NON_PRIO_PAUSED)) { + pool->status = FLOW_POOL_ACTIVE_PAUSED; + /* pause priority queue */ + pdev->pause_cb(vdev->vdev_id, + WLAN_NETIF_PRIORITY_QUEUE_OFF, + WLAN_DATA_FLOW_CONTROL_PRIORITY); + } + + qdf_spin_unlock_bh(&pool->flow_pool_lock); + + ol_tx_desc_sanity_checks(pdev, tx_desc); + ol_tx_desc_compute_delay(tx_desc); + ol_tx_desc_update_tx_ts(tx_desc); + ol_tx_desc_vdev_update(tx_desc, vdev); + qdf_atomic_inc(&tx_desc->ref_cnt); + } else { + pool->pkt_drop_no_desc++; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } + +end: + return tx_desc; +} + +/** + * ol_tx_desc_alloc_wrapper() -allocate tx descriptor + * @pdev: pdev handler + * @vdev: vdev handler + * @msdu_info: msdu handler + * + * Return: tx descriptor or NULL + */ +#ifdef QCA_LL_TX_FLOW_GLOBAL_MGMT_POOL +struct ol_tx_desc_t * +ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + struct ol_txrx_msdu_info_t *msdu_info) +{ + if (qdf_unlikely(msdu_info->htt.info.frame_type == htt_pkt_type_mgmt)) + return ol_tx_desc_alloc(pdev, vdev, pdev->mgmt_pool); + else + return ol_tx_desc_alloc(pdev, vdev, vdev->pool); +} +#else +struct ol_tx_desc_t * +ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + struct ol_txrx_msdu_info_t *msdu_info) +{ + return ol_tx_desc_alloc(pdev, vdev, vdev->pool); +} +#endif +#endif + +/** + * ol_tx_desc_alloc_hl() - allocate tx descriptor + * @pdev: pdev handle + * @vdev: vdev handle + * @msdu_info: tx msdu info + * + * Return: tx descriptor pointer/ NULL in case of error + */ +static struct ol_tx_desc_t * +ol_tx_desc_alloc_hl(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ol_tx_desc_t *tx_desc; + + tx_desc = ol_tx_desc_alloc_wrapper(pdev, vdev, msdu_info); + if (!tx_desc) + return NULL; + + qdf_atomic_dec(&pdev->tx_queue.rsrc_cnt); + + return tx_desc; +} + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL + +/** + * ol_tx_desc_vdev_rm() - decrement the tx desc count for vdev. + * @tx_desc: tx desc + * + * Return: None + */ +static inline void +ol_tx_desc_vdev_rm(struct ol_tx_desc_t *tx_desc) +{ + /* + * In module exit context, vdev handle could be destroyed but still + * we need to free pending completion tx_desc. + */ + if (!tx_desc || !tx_desc->vdev) + return; + + qdf_atomic_dec(&tx_desc->vdev->tx_desc_count); + tx_desc->vdev = NULL; +} +#else + +static inline void +ol_tx_desc_vdev_rm(struct ol_tx_desc_t *tx_desc) +{ +} +#endif + +#ifdef FEATURE_TSO +/** + * ol_tso_unmap_tso_segment() - Unmap TSO segment + * @pdev: pointer to ol_txrx_pdev_t structure + * @tx_desc: pointer to ol_tx_desc_t containing the TSO segment + * + * Unmap TSO segment (frag[1]). If it is the last TSO segment corresponding the + * nbuf, also unmap the EIT header(frag[0]). + * + * Return: None + */ +static void ol_tso_unmap_tso_segment(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + bool is_last_seg = false; + struct qdf_tso_num_seg_elem_t *tso_num_desc = NULL; + + if (qdf_unlikely(!tx_desc->tso_desc)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s %d TSO desc is NULL!", + __func__, __LINE__); + qdf_assert(0); + return; + } else if (qdf_unlikely(!tx_desc->tso_num_desc)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s %d TSO common info is NULL!", + __func__, __LINE__); + qdf_assert(0); + return; + } + + tso_num_desc = tx_desc->tso_num_desc; + + qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex); + + tso_num_desc->num_seg.tso_cmn_num_seg--; + is_last_seg = (tso_num_desc->num_seg.tso_cmn_num_seg == 0) ? + true : false; + qdf_nbuf_unmap_tso_segment(pdev->osdev, tx_desc->tso_desc, is_last_seg); + + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + +} + +/** + * ol_tx_tso_desc_free() - Add TSO TX descs back to the freelist + * @pdev: pointer to ol_txrx_pdev_t structure + * @tx_desc: pointer to ol_tx_desc_t containing the TSO segment + * + * Add qdf_tso_seg_elem_t corresponding to the TSO seg back to freelist. + * If it is the last segment of the jumbo skb, also add the + * qdf_tso_num_seg_elem_t to the free list. + * + * Return: None + */ +static void ol_tx_tso_desc_free(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + bool is_last_seg; + struct qdf_tso_num_seg_elem_t *tso_num_desc = tx_desc->tso_num_desc; + + is_last_seg = (tso_num_desc->num_seg.tso_cmn_num_seg == 0) ? + true : false; + if (is_last_seg) { + ol_tso_num_seg_free(pdev, tx_desc->tso_num_desc); + tx_desc->tso_num_desc = NULL; + } + + ol_tso_free_segment(pdev, tx_desc->tso_desc); + tx_desc->tso_desc = NULL; +} + +#else +static inline void ol_tx_tso_desc_free(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ +} + +static inline void ol_tso_unmap_tso_segment( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ +} +#endif + +/** + * ol_tx_desc_free_common() - common funcs to free tx_desc for all flow ctl vers + * @pdev: pdev handle + * @tx_desc: tx descriptor + * + * Set of common functions needed for QCA_LL_TX_FLOW_CONTROL_V2 and older + * versions of flow control. Needs to be called from within a spinlock. + * + * Return: None + */ +static void ol_tx_desc_free_common(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + ol_tx_desc_dup_detect_reset(pdev, tx_desc); + + if (tx_desc->pkt_type == OL_TX_FRM_TSO) + ol_tx_tso_desc_free(pdev, tx_desc); + + ol_tx_desc_reset_pkt_type(tx_desc); + ol_tx_desc_reset_timestamp(tx_desc); + /* clear the ref cnt */ + qdf_atomic_init(&tx_desc->ref_cnt); + tx_desc->vdev_id = OL_TXRX_INVALID_VDEV_ID; +} + +#ifndef QCA_LL_TX_FLOW_CONTROL_V2 +/** + * ol_tx_desc_free() - put descriptor to freelist + * @pdev: pdev handle + * @tx_desc: tx descriptor + * + * Return: None + */ +void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc) +{ + qdf_spin_lock_bh(&pdev->tx_mutex); + + ol_tx_desc_free_common(pdev, tx_desc); + + ol_tx_put_desc_global_pool(pdev, tx_desc); + ol_tx_desc_vdev_rm(tx_desc); + ol_tx_do_pdev_flow_control_unpause(pdev); + + qdf_spin_unlock_bh(&pdev->tx_mutex); +} + +#else + +/** + * ol_tx_update_free_desc_to_pool() - update free desc to pool + * @pdev: pdev handle + * @tx_desc: descriptor + * + * Return : 1 desc distribution required / 0 don't need distribution + */ +#ifdef QCA_LL_TX_FLOW_CONTROL_RESIZE +static inline bool ol_tx_update_free_desc_to_pool(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + struct ol_tx_flow_pool_t *pool = tx_desc->pool; + bool distribute_desc = false; + + if (unlikely(pool->overflow_desc)) { + ol_tx_put_desc_global_pool(pdev, tx_desc); + --pool->overflow_desc; + distribute_desc = true; + } else { + ol_tx_put_desc_flow_pool(pool, tx_desc); + } + + return distribute_desc; +} +#else +static inline bool ol_tx_update_free_desc_to_pool(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + ol_tx_put_desc_flow_pool(tx_desc->pool, tx_desc); + return false; +} +#endif + +/** + * ol_tx_desc_free() - put descriptor to pool freelist + * @pdev: pdev handle + * @tx_desc: tx descriptor + * + * Return: None + */ +void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc) +{ + bool distribute_desc = false; + struct ol_tx_flow_pool_t *pool = tx_desc->pool; + + qdf_spin_lock_bh(&pool->flow_pool_lock); + + ol_tx_desc_free_common(pdev, tx_desc); + distribute_desc = ol_tx_update_free_desc_to_pool(pdev, tx_desc); + + switch (pool->status) { + case FLOW_POOL_ACTIVE_PAUSED: + if (pool->avail_desc > pool->start_priority_th) { + /* unpause priority queue */ + pdev->pause_cb(pool->member_flow_id, + WLAN_NETIF_PRIORITY_QUEUE_ON, + WLAN_DATA_FLOW_CONTROL_PRIORITY); + pool->status = FLOW_POOL_NON_PRIO_PAUSED; + } + break; + case FLOW_POOL_NON_PRIO_PAUSED: + if (pool->avail_desc > pool->start_th) { + pdev->pause_cb(pool->member_flow_id, + WLAN_WAKE_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); + pool->status = FLOW_POOL_ACTIVE_UNPAUSED; + } + break; + case FLOW_POOL_INVALID: + if (pool->avail_desc == pool->flow_pool_size) { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + ol_tx_free_invalid_flow_pool(pool); + qdf_print("pool is INVALID State!!"); + return; + } + break; + case FLOW_POOL_ACTIVE_UNPAUSED: + break; + default: + qdf_print("pool is INACTIVE State!!"); + break; + }; + + qdf_spin_unlock_bh(&pool->flow_pool_lock); + + if (unlikely(distribute_desc)) + ol_tx_distribute_descs_to_deficient_pools_from_global_pool(); + +} +#endif + +const uint32_t htt_to_ce_pkt_type[] = { + [htt_pkt_type_raw] = tx_pkt_type_raw, + [htt_pkt_type_native_wifi] = tx_pkt_type_native_wifi, + [htt_pkt_type_ethernet] = tx_pkt_type_802_3, + [htt_pkt_type_mgmt] = tx_pkt_type_mgmt, + [htt_pkt_type_eth2] = tx_pkt_type_eth2, + [htt_pkt_num_types] = 0xffffffff +}; + +#define WISA_DEST_PORT_6MBPS 50000 +#define WISA_DEST_PORT_24MBPS 50001 + +/** + * ol_tx_get_wisa_ext_hdr_type() - get header type for WiSA mode + * @netbuf: network buffer + * + * Return: extension header type + */ +static enum extension_header_type +ol_tx_get_wisa_ext_hdr_type(qdf_nbuf_t netbuf) +{ + uint8_t *buf = qdf_nbuf_data(netbuf); + uint16_t dport; + + if (qdf_is_macaddr_group( + (struct qdf_mac_addr *)(buf + QDF_NBUF_DEST_MAC_OFFSET))) { + + dport = (uint16_t)(*(uint16_t *)(buf + + QDF_NBUF_TRAC_IPV4_OFFSET + + QDF_NBUF_TRAC_IPV4_HEADER_SIZE + sizeof(uint16_t))); + + if (dport == QDF_SWAP_U16(WISA_DEST_PORT_6MBPS)) + return WISA_MODE_EXT_HEADER_6MBPS; + else if (dport == QDF_SWAP_U16(WISA_DEST_PORT_24MBPS)) + return WISA_MODE_EXT_HEADER_24MBPS; + else + return EXT_HEADER_NOT_PRESENT; + } else { + return EXT_HEADER_NOT_PRESENT; + } +} + +/** + * ol_tx_get_ext_header_type() - extension header is required or not + * @vdev: vdev pointer + * @netbuf: network buffer + * + * This function returns header type and if extension header is + * not required than returns EXT_HEADER_NOT_PRESENT. + * + * Return: extension header type + */ +enum extension_header_type +ol_tx_get_ext_header_type(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t netbuf) +{ + if (vdev->is_wisa_mode_enable == true) + return ol_tx_get_wisa_ext_hdr_type(netbuf); + else + return EXT_HEADER_NOT_PRESENT; +} + +struct ol_tx_desc_t *ol_tx_desc_ll(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t netbuf, + struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ol_tx_desc_t *tx_desc; + unsigned int i; + uint32_t num_frags; + enum extension_header_type type; + + msdu_info->htt.info.vdev_id = vdev->vdev_id; + msdu_info->htt.action.cksum_offload = qdf_nbuf_get_tx_cksum(netbuf); + switch (qdf_nbuf_get_exemption_type(netbuf)) { + case QDF_NBUF_EXEMPT_NO_EXEMPTION: + case QDF_NBUF_EXEMPT_ON_KEY_MAPPING_KEY_UNAVAILABLE: + /* We want to encrypt this frame */ + msdu_info->htt.action.do_encrypt = 1; + break; + case QDF_NBUF_EXEMPT_ALWAYS: + /* We don't want to encrypt this frame */ + msdu_info->htt.action.do_encrypt = 0; + break; + default: + qdf_assert(0); + break; + } + + /* allocate the descriptor */ + tx_desc = ol_tx_desc_alloc_wrapper(pdev, vdev, msdu_info); + if (!tx_desc) + return NULL; + + /* initialize the SW tx descriptor */ + tx_desc->netbuf = netbuf; + + if (msdu_info->tso_info.is_tso) { + tx_desc->tso_desc = msdu_info->tso_info.curr_seg; + tx_desc->tso_num_desc = msdu_info->tso_info.tso_num_seg_list; + tx_desc->pkt_type = OL_TX_FRM_TSO; + TXRX_STATS_MSDU_INCR(pdev, tx.tso.tso_pkts, netbuf); + } else { + tx_desc->pkt_type = OL_TX_FRM_STD; + } + + type = ol_tx_get_ext_header_type(vdev, netbuf); + + /* initialize the HW tx descriptor */ + if (qdf_unlikely(htt_tx_desc_init(pdev->htt_pdev, tx_desc->htt_tx_desc, + tx_desc->htt_tx_desc_paddr, + ol_tx_desc_id(pdev, tx_desc), netbuf, &msdu_info->htt, + &msdu_info->tso_info, NULL, type))) { + /* + * HTT Tx descriptor initialization failed. + * therefore, free the tx desc + */ + ol_tx_desc_free(pdev, tx_desc); + return NULL; + } + + /* + * Initialize the fragmentation descriptor. + * Skip the prefix fragment (HTT tx descriptor) that was added + * during the call to htt_tx_desc_init above. + */ + num_frags = qdf_nbuf_get_num_frags(netbuf); + /* num_frags are expected to be 2 max */ + num_frags = (num_frags > QDF_NBUF_CB_TX_MAX_EXTRA_FRAGS) + ? QDF_NBUF_CB_TX_MAX_EXTRA_FRAGS + : num_frags; +#if defined(HELIUMPLUS) + /* + * Use num_frags - 1, since 1 frag is used to store + * the HTT/HTC descriptor + * Refer to htt_tx_desc_init() + */ + htt_tx_desc_num_frags(pdev->htt_pdev, tx_desc->htt_frag_desc, + num_frags - 1); +#else /* ! defined(HELIUMPLUS) */ + htt_tx_desc_num_frags(pdev->htt_pdev, tx_desc->htt_tx_desc, + num_frags - 1); +#endif /* defined(HELIUMPLUS) */ + + if (msdu_info->tso_info.is_tso) { + htt_tx_desc_fill_tso_info(pdev->htt_pdev, + tx_desc->htt_frag_desc, &msdu_info->tso_info); + TXRX_STATS_TSO_SEG_UPDATE(pdev, + msdu_info->tso_info.msdu_stats_idx, + msdu_info->tso_info.curr_seg->seg); + } else { + for (i = 1; i < num_frags; i++) { + qdf_size_t frag_len; + qdf_dma_addr_t frag_paddr; +#ifdef HELIUMPLUS_DEBUG + void *frag_vaddr; + + frag_vaddr = qdf_nbuf_get_frag_vaddr(netbuf, i); +#endif + frag_len = qdf_nbuf_get_frag_len(netbuf, i); + frag_paddr = qdf_nbuf_get_frag_paddr(netbuf, i); +#if defined(HELIUMPLUS) + htt_tx_desc_frag(pdev->htt_pdev, tx_desc->htt_frag_desc, + i - 1, frag_paddr, frag_len); +#if defined(HELIUMPLUS_DEBUG) + qdf_debug("htt_fdesc=%pK frag=%d frag_vaddr=0x%pK frag_paddr=0x%llx len=%zu\n", + tx_desc->htt_frag_desc, + i-1, frag_vaddr, frag_paddr, frag_len); + ol_txrx_dump_pkt(netbuf, frag_paddr, 64); +#endif /* HELIUMPLUS_DEBUG */ +#else /* ! defined(HELIUMPLUS) */ + htt_tx_desc_frag(pdev->htt_pdev, tx_desc->htt_tx_desc, + i - 1, frag_paddr, frag_len); +#endif /* defined(HELIUMPLUS) */ + } + } + +#if defined(HELIUMPLUS_DEBUG) + ol_txrx_dump_frag_desc("ol_tx_desc_ll()", tx_desc); +#endif + return tx_desc; +} + +struct ol_tx_desc_t * +ol_tx_desc_hl( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t netbuf, + struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ol_tx_desc_t *tx_desc; + + /* FIX THIS: these inits should probably be done by tx classify */ + msdu_info->htt.info.vdev_id = vdev->vdev_id; + msdu_info->htt.info.frame_type = pdev->htt_pkt_type; + msdu_info->htt.action.cksum_offload = qdf_nbuf_get_tx_cksum(netbuf); + switch (qdf_nbuf_get_exemption_type(netbuf)) { + case QDF_NBUF_EXEMPT_NO_EXEMPTION: + case QDF_NBUF_EXEMPT_ON_KEY_MAPPING_KEY_UNAVAILABLE: + /* We want to encrypt this frame */ + msdu_info->htt.action.do_encrypt = 1; + break; + case QDF_NBUF_EXEMPT_ALWAYS: + /* We don't want to encrypt this frame */ + msdu_info->htt.action.do_encrypt = 0; + break; + default: + qdf_assert(0); + break; + } + + /* allocate the descriptor */ + tx_desc = ol_tx_desc_alloc_hl(pdev, vdev, msdu_info); + if (!tx_desc) + return NULL; + + /* initialize the SW tx descriptor */ + tx_desc->netbuf = netbuf; + /* fix this - get pkt_type from msdu_info */ + tx_desc->pkt_type = OL_TX_FRM_STD; + +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + tx_desc->orig_l2_hdr_bytes = 0; +#endif + /* the HW tx descriptor will be initialized later by the caller */ + + return tx_desc; +} + +void ol_tx_desc_frame_list_free(struct ol_txrx_pdev_t *pdev, + ol_tx_desc_list *tx_descs, int had_error) +{ + struct ol_tx_desc_t *tx_desc, *tmp; + qdf_nbuf_t msdus = NULL; + + TAILQ_FOREACH_SAFE(tx_desc, tx_descs, tx_desc_list_elem, tmp) { + qdf_nbuf_t msdu = tx_desc->netbuf; + + qdf_atomic_init(&tx_desc->ref_cnt); /* clear the ref cnt */ +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + /* restore original hdr offset */ + OL_TX_RESTORE_HDR(tx_desc, msdu); +#endif + + /* + * In MCC IPA tx context, IPA driver provides skb with directly + * DMA mapped address. In such case, there's no need for WLAN + * driver to DMA unmap the skb. + */ + if (qdf_nbuf_get_users(msdu) <= 1) { + if (!qdf_nbuf_ipa_owned_get(msdu)) + qdf_nbuf_unmap(pdev->osdev, msdu, + QDF_DMA_TO_DEVICE); + } + + /* free the tx desc */ + ol_tx_desc_free(pdev, tx_desc); + /* link the netbuf into a list to free as a batch */ + qdf_nbuf_set_next(msdu, msdus); + msdus = msdu; + } + /* free the netbufs as a batch */ + qdf_nbuf_tx_free(msdus, had_error); +} + +void ol_tx_desc_frame_free_nonstd(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc, int had_error) +{ + int mgmt_type; + ol_txrx_mgmt_tx_cb ota_ack_cb; + + qdf_atomic_init(&tx_desc->ref_cnt); /* clear the ref cnt */ +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + /* restore original hdr offset */ + OL_TX_RESTORE_HDR(tx_desc, (tx_desc->netbuf)); +#endif + if (tx_desc->pkt_type == OL_TX_FRM_NO_FREE) { + + /* free the tx desc but don't unmap or free the frame */ + if (pdev->tx_data_callback.func) { + qdf_nbuf_set_next(tx_desc->netbuf, NULL); + pdev->tx_data_callback.func(pdev->tx_data_callback.ctxt, + tx_desc->netbuf, had_error); + goto free_tx_desc; + } + /* let the code below unmap and free the frame */ + } + if (tx_desc->pkt_type == OL_TX_FRM_TSO) + ol_tso_unmap_tso_segment(pdev, tx_desc); + else + qdf_nbuf_unmap(pdev->osdev, tx_desc->netbuf, QDF_DMA_TO_DEVICE); + /* check the frame type to see what kind of special steps are needed */ + if ((tx_desc->pkt_type >= OL_TXRX_MGMT_TYPE_BASE) && + (tx_desc->pkt_type != ol_tx_frm_freed)) { + qdf_dma_addr_t frag_desc_paddr = 0; + +#if defined(HELIUMPLUS) + frag_desc_paddr = tx_desc->htt_frag_desc_paddr; + /* FIX THIS - + * The FW currently has trouble using the host's fragments + * table for management frames. Until this is fixed, + * rather than specifying the fragment table to the FW, + * the host SW will specify just the address of the initial + * fragment. + * Now that the mgmt frame is done, the HTT tx desc's frags + * table pointer needs to be reset. + */ +#if defined(HELIUMPLUS_DEBUG) + qdf_print("Frag Descriptor Reset [%d] to 0x%x\n", + tx_desc->id, + frag_desc_paddr); +#endif /* HELIUMPLUS_DEBUG */ +#endif /* HELIUMPLUS */ + htt_tx_desc_frags_table_set(pdev->htt_pdev, + tx_desc->htt_tx_desc, 0, + frag_desc_paddr, 1); + + mgmt_type = tx_desc->pkt_type - OL_TXRX_MGMT_TYPE_BASE; + /* + * we already checked the value when the mgmt frame was + * provided to the txrx layer. + * no need to check it a 2nd time. + */ + ota_ack_cb = pdev->tx_mgmt_cb.ota_ack_cb; + if (ota_ack_cb) { + void *ctxt; + ctxt = pdev->tx_mgmt_cb.ctxt; + ota_ack_cb(ctxt, tx_desc->netbuf, had_error); + } + } else if (had_error == htt_tx_status_download_fail) { + /* Failed to send to target */ + goto free_tx_desc; + } else { + /* single regular frame, called from completion path */ + qdf_nbuf_set_next(tx_desc->netbuf, NULL); + qdf_nbuf_tx_free(tx_desc->netbuf, had_error); + } +free_tx_desc: + /* free the tx desc */ + ol_tx_desc_free(pdev, tx_desc); +} + +#if defined(FEATURE_TSO) +#ifdef TSOSEG_DEBUG +static int +ol_tso_seg_dbg_sanitize(struct qdf_tso_seg_elem_t *tsoseg) +{ + int rc = -1; + struct ol_tx_desc_t *txdesc; + + if (tsoseg) { + txdesc = tsoseg->dbg.txdesc; + /* Don't validate if TX desc is NULL*/ + if (!txdesc) + return 0; + if (txdesc->tso_desc != tsoseg) + qdf_tso_seg_dbg_bug("Owner sanity failed"); + else + rc = 0; + } + return rc; + +}; +#else +static int +ol_tso_seg_dbg_sanitize(struct qdf_tso_seg_elem_t *tsoseg) +{ + return 0; +} +#endif /* TSOSEG_DEBUG */ + +/** + * ol_tso_alloc_segment() - function to allocate a TSO segment + * element + * @pdev: the data physical device sending the data + * + * Allocates a TSO segment element from the free list held in + * the pdev + * + * Return: tso_seg + */ +struct qdf_tso_seg_elem_t *ol_tso_alloc_segment(struct ol_txrx_pdev_t *pdev) +{ + struct qdf_tso_seg_elem_t *tso_seg = NULL; + + qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex); + if (pdev->tso_seg_pool.freelist) { + pdev->tso_seg_pool.num_free--; + tso_seg = pdev->tso_seg_pool.freelist; + if (tso_seg->on_freelist != 1) { + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_print("tso seg alloc failed: not in freelist"); + QDF_BUG(0); + return NULL; + } else if (tso_seg->cookie != TSO_SEG_MAGIC_COOKIE) { + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_print("tso seg alloc failed: bad cookie"); + QDF_BUG(0); + return NULL; + } + /*this tso seg is not a part of freelist now.*/ + tso_seg->on_freelist = 0; + tso_seg->sent_to_target = 0; + tso_seg->force_free = 0; + pdev->tso_seg_pool.freelist = pdev->tso_seg_pool.freelist->next; + qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_ALLOC); + } + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + + return tso_seg; +} + +/** + * ol_tso_free_segment() - function to free a TSO segment + * element + * @pdev: the data physical device sending the data + * @tso_seg: The TSO segment element to be freed + * + * Returns a TSO segment element to the free list held in the + * pdev + * + * Return: none + */ +void ol_tso_free_segment(struct ol_txrx_pdev_t *pdev, + struct qdf_tso_seg_elem_t *tso_seg) +{ + qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex); + if (tso_seg->on_freelist != 0) { + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_print("Do not free tso seg, already freed"); + QDF_BUG(0); + return; + } else if (tso_seg->cookie != TSO_SEG_MAGIC_COOKIE) { + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_print("Do not free tso seg: cookie is not good."); + QDF_BUG(0); + return; + } else if ((tso_seg->sent_to_target != 1) && + (tso_seg->force_free != 1)) { + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_print("Do not free tso seg: yet to be sent to target"); + QDF_BUG(0); + return; + } + /* sanitize before free */ + ol_tso_seg_dbg_sanitize(tso_seg); + qdf_tso_seg_dbg_setowner(tso_seg, NULL); + /*this tso seg is now a part of freelist*/ + /* retain segment history, if debug is enabled */ + qdf_tso_seg_dbg_zero(tso_seg); + tso_seg->next = pdev->tso_seg_pool.freelist; + tso_seg->on_freelist = 1; + tso_seg->sent_to_target = 0; + tso_seg->cookie = TSO_SEG_MAGIC_COOKIE; + pdev->tso_seg_pool.freelist = tso_seg; + pdev->tso_seg_pool.num_free++; + qdf_tso_seg_dbg_record(tso_seg, tso_seg->force_free + ? TSOSEG_LOC_FORCE_FREE + : TSOSEG_LOC_FREE); + tso_seg->force_free = 0; + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); +} + +/** + * ol_tso_num_seg_alloc() - function to allocate a element to count TSO segments + * in a jumbo skb packet. + * @pdev: the data physical device sending the data + * + * Allocates a element to count TSO segments from the free list held in + * the pdev + * + * Return: tso_num_seg + */ +struct qdf_tso_num_seg_elem_t *ol_tso_num_seg_alloc(struct ol_txrx_pdev_t *pdev) +{ + struct qdf_tso_num_seg_elem_t *tso_num_seg = NULL; + + qdf_spin_lock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex); + if (pdev->tso_num_seg_pool.freelist) { + pdev->tso_num_seg_pool.num_free--; + tso_num_seg = pdev->tso_num_seg_pool.freelist; + pdev->tso_num_seg_pool.freelist = + pdev->tso_num_seg_pool.freelist->next; + } + qdf_spin_unlock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex); + + return tso_num_seg; +} + +/** + * ol_tso_num_seg_free() - function to free a TSO segment + * element + * @pdev: the data physical device sending the data + * @tso_seg: The TSO segment element to be freed + * + * Returns a element to the free list held in the pdev + * + * Return: none + */ +void ol_tso_num_seg_free(struct ol_txrx_pdev_t *pdev, + struct qdf_tso_num_seg_elem_t *tso_num_seg) +{ + qdf_spin_lock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex); + tso_num_seg->next = pdev->tso_num_seg_pool.freelist; + pdev->tso_num_seg_pool.freelist = tso_num_seg; + pdev->tso_num_seg_pool.num_free++; + qdf_spin_unlock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex); +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_desc.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_desc.h new file mode 100644 index 0000000000000000000000000000000000000000..96342bca7ce9a2288781278cb59f349d337588b7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_desc.h @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2011, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_tx_desc.h + * @brief API definitions for the tx descriptor module within the data SW. + */ +#ifndef _OL_TX_DESC__H_ +#define _OL_TX_DESC__H_ + +#include "queue.h" /* TAILQ_HEAD */ +#include /* qdf_nbuf_t */ +#include /* ol_txrx_vdev_t, etc. */ +#include /*TXRX_ASSERT2 */ +#include + +#define DIV_BY_8 3 +#define DIV_BY_32 5 +#define MOD_BY_8 0x7 +#define MOD_BY_32 0x1F + +struct ol_tx_desc_t * +ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + struct ol_txrx_msdu_info_t *msdu_info); + + +/** + * @brief Allocate and initialize a tx descriptor for a LL system. + * @details + * Allocate a tx descriptor pair for a new tx frame - a SW tx descriptor + * for private use within the host data SW, and a HTT tx descriptor for + * downloading tx meta-data to the target FW/HW. + * Fill in the fields of this pair of tx descriptors based on the + * information in the netbuf. + * For LL, this includes filling in a fragmentation descriptor to + * specify to the MAC HW where to find the tx frame's fragments. + * + * @param pdev - the data physical device sending the data + * (for accessing the tx desc pool) + * @param vdev - the virtual device sending the data + * (for specifying the transmitter address for multicast / broadcast data) + * @param netbuf - the tx frame + * @param msdu_info - tx meta-data + */ +struct ol_tx_desc_t *ol_tx_desc_ll(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t netbuf, + struct ol_txrx_msdu_info_t *msdu_info); + + +/** + * @brief Allocate and initialize a tx descriptor for a HL system. + * @details + * Allocate a tx descriptor pair for a new tx frame - a SW tx descriptor + * for private use within the host data SW, and a HTT tx descriptor for + * downloading tx meta-data to the target FW/HW. + * Fill in the fields of this pair of tx descriptors based on the + * information in the netbuf. + * + * @param pdev - the data physical device sending the data + * (for accessing the tx desc pool) + * @param vdev - the virtual device sending the data + * (for specifying the transmitter address for multicast / broadcast data) + * @param netbuf - the tx frame + * @param msdu_info - tx meta-data + */ +struct ol_tx_desc_t * +ol_tx_desc_hl( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t netbuf, + struct ol_txrx_msdu_info_t *msdu_info); + + +/** + * @brief Use a tx descriptor ID to find the corresponding descriptor object. + * + * @param pdev - the data physical device sending the data + * @param tx_desc_id - the ID of the descriptor in question + * @return the descriptor object that has the specified ID + */ +static inline struct ol_tx_desc_t *ol_tx_desc_find( + struct ol_txrx_pdev_t *pdev, uint16_t tx_desc_id) +{ + void **td_base = (void **)pdev->tx_desc.desc_pages.cacheable_pages; + + return &((union ol_tx_desc_list_elem_t *) + (td_base[tx_desc_id >> pdev->tx_desc.page_divider] + + (pdev->tx_desc.desc_reserved_size * + (tx_desc_id & pdev->tx_desc.offset_filter))))->tx_desc; +} + +/** + * @brief Use a tx descriptor ID to find the corresponding descriptor object + * and add sanity check. + * + * @param pdev - the data physical device sending the data + * @param tx_desc_id - the ID of the descriptor in question + * @return the descriptor object that has the specified ID, + * if failure, will return NULL. + */ + +#ifdef QCA_SUPPORT_TXDESC_SANITY_CHECKS +static inline struct ol_tx_desc_t * +ol_tx_desc_find_check(struct ol_txrx_pdev_t *pdev, u_int16_t tx_desc_id) +{ + struct ol_tx_desc_t *tx_desc; + + if (tx_desc_id >= pdev->tx_desc.pool_size) + return NULL; + + tx_desc = ol_tx_desc_find(pdev, tx_desc_id); + + if (tx_desc->pkt_type == ol_tx_frm_freed) + return NULL; + + return tx_desc; +} + +#else + +static inline struct ol_tx_desc_t * +ol_tx_desc_find_check(struct ol_txrx_pdev_t *pdev, u_int16_t tx_desc_id) +{ + struct ol_tx_desc_t *tx_desc; + + if (tx_desc_id >= pdev->tx_desc.pool_size) + return NULL; + + tx_desc = ol_tx_desc_find(pdev, tx_desc_id); + + /* check against invalid tx_desc_id */ + if (ol_cfg_is_high_latency(pdev->ctrl_pdev) && !tx_desc->vdev) + return NULL; + + return tx_desc; +} +#endif + +/** + * @brief Free a list of tx descriptors and the tx frames they refer to. + * @details + * Free a batch of "standard" tx descriptors and their tx frames. + * Free each tx descriptor, by returning it to the freelist. + * Unmap each netbuf, and free the netbufs as a batch. + * Irregular tx frames like TSO or management frames that require + * special handling are processed by the ol_tx_desc_frame_free_nonstd + * function rather than this function. + * + * @param pdev - the data physical device that sent the data + * @param tx_descs - a list of SW tx descriptors for the tx frames + * @param had_error - bool indication of whether the transmission failed. + * This is provided to callback functions that get notified of + * the tx frame completion. + */ +void ol_tx_desc_frame_list_free(struct ol_txrx_pdev_t *pdev, + ol_tx_desc_list *tx_descs, int had_error); + +/** + * @brief Free a non-standard tx frame and its tx descriptor. + * @details + * Check the tx frame type (e.g. TSO vs. management) to determine what + * special steps, if any, need to be performed prior to freeing the + * tx frame and its tx descriptor. + * This function can also be used to free single standard tx frames. + * After performing any special steps based on tx frame type, free the + * tx descriptor, i.e. return it to the freelist, and unmap and + * free the netbuf referenced by the tx descriptor. + * + * @param pdev - the data physical device that sent the data + * @param tx_desc - the SW tx descriptor for the tx frame that was sent + * @param had_error - bool indication of whether the transmission failed. + * This is provided to callback functions that get notified of + * the tx frame completion. + */ +void ol_tx_desc_frame_free_nonstd(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc, int had_error); + +/* + * @brief Determine the ID of a tx descriptor. + * + * @param pdev - the physical device that is sending the data + * @param tx_desc - the descriptor whose ID is being determined + * @return numeric ID that uniquely identifies the tx descriptor + */ +static inline uint16_t +ol_tx_desc_id(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc) +{ + TXRX_ASSERT2(tx_desc->id < pdev->tx_desc.pool_size); + return tx_desc->id; +} + +/* + * @brief Retrieves the beacon headr for the vdev + * @param pdev - opaque pointe to scn + * @param vdevid - vdev id + * @return void pointer to the beacon header for the given vdev + */ + +void *ol_ath_get_bcn_header(struct cdp_cfg *cfg_pdev, A_UINT32 vdev_id); + +/* + * @brief Free a tx descriptor, without freeing the matching frame. + * @details + * This function is using during the function call that submits tx frames + * into the txrx layer, for cases where a tx descriptor is successfully + * allocated, but for other reasons the frame could not be accepted. + * + * @param pdev - the data physical device that is sending the data + * @param tx_desc - the descriptor being freed + */ +void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc); + +#if defined(FEATURE_TSO) +struct qdf_tso_seg_elem_t *ol_tso_alloc_segment(struct ol_txrx_pdev_t *pdev); + +void ol_tso_free_segment(struct ol_txrx_pdev_t *pdev, + struct qdf_tso_seg_elem_t *tso_seg); +struct qdf_tso_num_seg_elem_t *ol_tso_num_seg_alloc( + struct ol_txrx_pdev_t *pdev); +void ol_tso_num_seg_free(struct ol_txrx_pdev_t *pdev, + struct qdf_tso_num_seg_elem_t *tso_num_seg); +void ol_free_remaining_tso_segs(ol_txrx_vdev_handle vdev, + struct ol_txrx_msdu_info_t *msdu_info, + bool is_tso_seg_mapping_done); + +#else +#define ol_tso_alloc_segment(pdev) /*no-op*/ +#define ol_tso_free_segment(pdev, tso_seg) /*no-op*/ +#define ol_tso_num_seg_alloc(pdev) /*no-op*/ +#define ol_tso_num_seg_free(pdev, tso_num_seg) /*no-op*/ +/*no-op*/ +#define ol_free_remaining_tso_segs(vdev, msdu_info, is_tso_seg_mapping_done) +#endif + +/** + * ol_tx_get_desc_global_pool() - get descriptor from global pool + * @pdev: pdev handler + * + * Caller needs to take lock and do sanity checks. + * + * Return: tx descriptor + */ +static inline +struct ol_tx_desc_t *ol_tx_get_desc_global_pool(struct ol_txrx_pdev_t *pdev) +{ + struct ol_tx_desc_t *tx_desc = &pdev->tx_desc.freelist->tx_desc; + + pdev->tx_desc.freelist = pdev->tx_desc.freelist->next; + pdev->tx_desc.num_free--; + return tx_desc; +} + +/** + * ol_tx_put_desc_global_pool() - put descriptor to global pool freelist + * @pdev: pdev handle + * @tx_desc: tx descriptor + * + * Caller needs to take lock and do sanity checks. + * + * Return: none + */ +static inline +void ol_tx_put_desc_global_pool(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + ((union ol_tx_desc_list_elem_t *)tx_desc)->next = + pdev->tx_desc.freelist; + pdev->tx_desc.freelist = + (union ol_tx_desc_list_elem_t *)tx_desc; + pdev->tx_desc.num_free++; +} + + +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 + +#ifdef QCA_LL_TX_FLOW_CONTROL_RESIZE +int ol_tx_distribute_descs_to_deficient_pools_from_global_pool(void); +#else +static inline +int ol_tx_distribute_descs_to_deficient_pools_from_global_pool(void) +{ + return 0; +} +#endif + +int ol_tx_free_invalid_flow_pool(struct ol_tx_flow_pool_t *pool); +/** + * ol_tx_get_desc_flow_pool() - get descriptor from flow pool + * @pool: flow pool + * + * Caller needs to take lock and do sanity checks. + * + * Return: tx descriptor + */ +static inline +struct ol_tx_desc_t *ol_tx_get_desc_flow_pool(struct ol_tx_flow_pool_t *pool) +{ + struct ol_tx_desc_t *tx_desc = &pool->freelist->tx_desc; + + pool->freelist = pool->freelist->next; + pool->avail_desc--; + return tx_desc; +} + +/** + * ol_tx_put_desc_flow_pool() - put descriptor to flow pool freelist + * @pool: flow pool + * @tx_desc: tx descriptor + * + * Caller needs to take lock and do sanity checks. + * + * Return: none + */ +static inline +void ol_tx_put_desc_flow_pool(struct ol_tx_flow_pool_t *pool, + struct ol_tx_desc_t *tx_desc) +{ + tx_desc->pool = pool; + ((union ol_tx_desc_list_elem_t *)tx_desc)->next = pool->freelist; + pool->freelist = (union ol_tx_desc_list_elem_t *)tx_desc; + pool->avail_desc++; +} + +#else +static inline int ol_tx_free_invalid_flow_pool(void *pool) +{ + return 0; +} +#endif + +#ifdef DESC_DUP_DETECT_DEBUG +/** + * ol_tx_desc_dup_detect_init() - initialize descriptor duplication logic + * @pdev: pdev handle + * @pool_size: global pool size + * + * Return: none + */ +static inline +void ol_tx_desc_dup_detect_init(struct ol_txrx_pdev_t *pdev, uint16_t pool_size) +{ + uint16_t size = (pool_size >> DIV_BY_8) + + sizeof(*pdev->tx_desc.free_list_bitmap); + pdev->tx_desc.free_list_bitmap = qdf_mem_malloc(size); +} + +/** + * ol_tx_desc_dup_detect_deinit() - deinit descriptor duplication logic + * @pdev: pdev handle + * + * Return: none + */ +static inline +void ol_tx_desc_dup_detect_deinit(struct ol_txrx_pdev_t *pdev) +{ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: pool_size %d num_free %d\n", __func__, + pdev->tx_desc.pool_size, pdev->tx_desc.num_free); + if (pdev->tx_desc.free_list_bitmap) + qdf_mem_free(pdev->tx_desc.free_list_bitmap); +} + +/** + * ol_tx_desc_dup_detect_set() - set bit for msdu_id + * @pdev: pdev handle + * @tx_desc: tx descriptor + * + * Return: none + */ +static inline +void ol_tx_desc_dup_detect_set(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + uint16_t msdu_id = ol_tx_desc_id(pdev, tx_desc); + bool test; + + if (!pdev->tx_desc.free_list_bitmap) + return; + + if (qdf_unlikely(msdu_id > pdev->tx_desc.pool_size)) { + qdf_print("msdu_id %d > pool_size %d", + msdu_id, pdev->tx_desc.pool_size); + QDF_BUG(0); + } + + test = test_and_set_bit(msdu_id, pdev->tx_desc.free_list_bitmap); + if (qdf_unlikely(test)) { + uint16_t size = (pdev->tx_desc.pool_size >> DIV_BY_8) + + ((pdev->tx_desc.pool_size & MOD_BY_8) ? 1 : 0); + qdf_print("duplicate msdu_id %d detected!!", msdu_id); + qdf_trace_hex_dump(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + (void *)pdev->tx_desc.free_list_bitmap, size); + QDF_BUG(0); + } +} + +/** + * ol_tx_desc_dup_detect_reset() - reset bit for msdu_id + * @pdev: pdev handle + * @tx_desc: tx descriptor + * + * Return: none + */ +static inline +void ol_tx_desc_dup_detect_reset(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + uint16_t msdu_id = ol_tx_desc_id(pdev, tx_desc); + bool test; + + if (!pdev->tx_desc.free_list_bitmap) + return; + + if (qdf_unlikely(msdu_id > pdev->tx_desc.pool_size)) { + qdf_print("msdu_id %d > pool_size %d", + msdu_id, pdev->tx_desc.pool_size); + QDF_BUG(0); + } + + test = !test_and_clear_bit(msdu_id, pdev->tx_desc.free_list_bitmap); + if (qdf_unlikely(test)) { + uint16_t size = (pdev->tx_desc.pool_size >> DIV_BY_8) + + ((pdev->tx_desc.pool_size & MOD_BY_8) ? 1 : 0); + qdf_print("duplicate free msg received for msdu_id %d!!\n", + msdu_id); + qdf_trace_hex_dump(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + (void *)pdev->tx_desc.free_list_bitmap, size); + QDF_BUG(0); + } +} +#else +static inline +void ol_tx_desc_dup_detect_init(struct ol_txrx_pdev_t *pdev, uint16_t size) +{ +} + +static inline +void ol_tx_desc_dup_detect_deinit(struct ol_txrx_pdev_t *pdev) +{ +} + +static inline +void ol_tx_desc_dup_detect_set(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ +} + +static inline +void ol_tx_desc_dup_detect_reset(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ +} +#endif + +enum extension_header_type +ol_tx_get_ext_header_type(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t netbuf); +enum extension_header_type +ol_tx_get_wisa_ext_type(qdf_nbuf_t netbuf); + + +#endif /* _OL_TX_DESC__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_hl.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_hl.c new file mode 100644 index 0000000000000000000000000000000000000000..2a222cb3507b7e870a793fd6c427adbb9b964afa --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_hl.c @@ -0,0 +1,2349 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_atomic_inc, etc. */ +#include /* qdf_os_spinlock */ +#include /* qdf_system_ticks, etc. */ +#include /* qdf_nbuf_t */ +#include /* QDF_NBUF_TX_EXT_TID_INVALID */ + +#include "queue.h" /* TAILQ */ +#ifdef QCA_COMPUTE_TX_DELAY +#include /* ethernet_hdr_t, etc. */ +#include /* ipv6_traffic_class */ +#endif + +#include /* ol_txrx_vdev_handle, etc. */ +#include /* htt_tx_compl_desc_id */ +#include /* htt_tx_status */ + +#include +#include +#include /* ol_txrx_vdev_t, etc */ +#include /* ol_tx_desc_find, ol_tx_desc_frame_free */ +#ifdef QCA_COMPUTE_TX_DELAY +#include /* ol_tx_dest_addr_find */ +#endif +#include /* OL_TX_DESC_NO_REFS, etc. */ +#include +#include /* ol_tx_reinject */ +#include + +#include /* ol_cfg_is_high_latency */ +#include +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP +#include /* OL_TX_RESTORE_HDR, etc */ +#endif +#include +#include +#include +#include +#include +#include "qdf_hrtimer.h" + +/* High/Low tx resource count in percentage */ +/* Set default high threashold to 15% */ +#ifndef TX_RESOURCE_HIGH_TH_IN_PER +#define TX_RESOURCE_HIGH_TH_IN_PER 15 +#endif + +/* Set default low threshold to 5% */ +#ifndef TX_RESOURCE_LOW_TH_IN_PER +#define TX_RESOURCE_LOW_TH_IN_PER 5 +#endif + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +static u16 ol_txrx_tx_desc_alloc_table[TXRX_FC_MAX] = { + [TXRX_FC_5GH_80M_2x2] = 2000, + [TXRX_FC_2GH_40M_2x2] = 800, +}; +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + +/* tx filtering is handled within the target FW */ +#define TX_FILTER_CHECK(tx_msdu_info) 0 /* don't filter */ + +u_int16_t +ol_tx_desc_pool_size_hl(struct cdp_cfg *ctrl_pdev) +{ + uint16_t desc_pool_size; + uint16_t steady_state_tx_lifetime_ms; + uint16_t safety_factor; + + /* + * Steady-state tx latency: + * roughly 1-2 ms flight time + * + roughly 1-2 ms prep time, + * + roughly 1-2 ms target->host notification time. + * = roughly 6 ms total + * Thus, steady state number of frames = + * steady state max throughput / frame size * tx latency, e.g. + * 1 Gbps / 1500 bytes * 6 ms = 500 + * + */ + steady_state_tx_lifetime_ms = 6; + + safety_factor = 8; + + desc_pool_size = + ol_cfg_max_thruput_mbps(ctrl_pdev) * + 1000 /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */ / + (8 * OL_TX_AVG_FRM_BYTES) * + steady_state_tx_lifetime_ms * + safety_factor; + + /* minimum */ + if (desc_pool_size < OL_TX_DESC_POOL_SIZE_MIN_HL) + desc_pool_size = OL_TX_DESC_POOL_SIZE_MIN_HL; + + /* maximum */ + if (desc_pool_size > OL_TX_DESC_POOL_SIZE_MAX_HL) + desc_pool_size = OL_TX_DESC_POOL_SIZE_MAX_HL; + + return desc_pool_size; +} + +#ifdef CONFIG_TX_DESC_HI_PRIO_RESERVE + +/** + * ol_tx_hl_desc_alloc() - Allocate and initialize a tx descriptor + * for a HL system. + * @pdev: the data physical device sending the data + * @vdev: the virtual device sending the data + * @msdu: the tx frame + * @msdu_info: the tx meta data + * + * Return: the tx decriptor + */ +static inline +struct ol_tx_desc_t *ol_tx_hl_desc_alloc(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ol_tx_desc_t *tx_desc = NULL; + + if (qdf_atomic_read(&pdev->tx_queue.rsrc_cnt) > + TXRX_HL_TX_DESC_HI_PRIO_RESERVED) { + tx_desc = ol_tx_desc_hl(pdev, vdev, msdu, msdu_info); + } else if (qdf_nbuf_is_ipv4_pkt(msdu) == true) { + if ((QDF_NBUF_CB_GET_PACKET_TYPE(msdu) == + QDF_NBUF_CB_PACKET_TYPE_DHCP) || + (QDF_NBUF_CB_GET_PACKET_TYPE(msdu) == + QDF_NBUF_CB_PACKET_TYPE_EAPOL)) { + tx_desc = ol_tx_desc_hl(pdev, vdev, msdu, msdu_info); + ol_txrx_info("Got tx desc from resv pool\n"); + } + } + return tx_desc; +} + +#elif defined(QCA_HL_NETDEV_FLOW_CONTROL) +bool ol_tx_desc_is_high_prio(qdf_nbuf_t msdu) +{ + enum qdf_proto_subtype proto_subtype; + bool high_prio = false; + + if (qdf_nbuf_is_ipv4_pkt(msdu) == true) { + if ((QDF_NBUF_CB_GET_PACKET_TYPE(msdu) == + QDF_NBUF_CB_PACKET_TYPE_DHCP) || + (QDF_NBUF_CB_GET_PACKET_TYPE(msdu) == + QDF_NBUF_CB_PACKET_TYPE_EAPOL)) + high_prio = true; + } else if (QDF_NBUF_CB_GET_PACKET_TYPE(msdu) == + QDF_NBUF_CB_PACKET_TYPE_ARP) { + high_prio = true; + } else if ((QDF_NBUF_CB_GET_PACKET_TYPE(msdu) == + QDF_NBUF_CB_PACKET_TYPE_ICMPv6)) { + proto_subtype = qdf_nbuf_get_icmpv6_subtype(msdu); + switch (proto_subtype) { + case QDF_PROTO_ICMPV6_NA: + case QDF_PROTO_ICMPV6_NS: + high_prio = true; + default: + high_prio = false; + } + } + return high_prio; +} + +static inline +struct ol_tx_desc_t *ol_tx_hl_desc_alloc(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ol_tx_desc_t *tx_desc = + ol_tx_desc_hl(pdev, vdev, msdu, msdu_info); + + if (!tx_desc) + return NULL; + + qdf_spin_lock_bh(&pdev->tx_mutex); + /* return if TX flow control disabled */ + if (vdev->tx_desc_limit == 0) { + qdf_spin_unlock_bh(&pdev->tx_mutex); + return tx_desc; + } + + if (!qdf_atomic_read(&vdev->os_q_paused) && + (qdf_atomic_read(&vdev->tx_desc_count) >= vdev->queue_stop_th)) { + /* + * Pause normal priority + * netdev queues if tx desc limit crosses + */ + pdev->pause_cb(vdev->vdev_id, + WLAN_STOP_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); + qdf_atomic_set(&vdev->os_q_paused, 1); + } else if (ol_tx_desc_is_high_prio(msdu) && !vdev->prio_q_paused && + (qdf_atomic_read(&vdev->tx_desc_count) + == vdev->tx_desc_limit)) { + /* Pause high priority queue */ + pdev->pause_cb(vdev->vdev_id, + WLAN_NETIF_PRIORITY_QUEUE_OFF, + WLAN_DATA_FLOW_CONTROL_PRIORITY); + vdev->prio_q_paused = 1; + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + + return tx_desc; +} + +#else + +static inline +struct ol_tx_desc_t *ol_tx_hl_desc_alloc(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ol_tx_desc_t *tx_desc = NULL; + + tx_desc = ol_tx_desc_hl(pdev, vdev, msdu, msdu_info); + return tx_desc; +} +#endif + +static inline uint16_t +ol_txrx_rsrc_threshold_lo(int desc_pool_size) +{ + int threshold_low; + + /* always maintain a 5% margin of unallocated descriptors */ + threshold_low = ((TX_RESOURCE_LOW_TH_IN_PER) * + desc_pool_size) / 100; + + return threshold_low; +} + +static inline uint16_t +ol_txrx_rsrc_threshold_hi(int desc_pool_size) +{ + int threshold_high; + /* when freeing up descriptors, keep going until + * there's a 15% margin + */ + threshold_high = ((TX_RESOURCE_HIGH_TH_IN_PER) * + desc_pool_size) / 100; + + return threshold_high; +} + +void ol_tx_init_pdev(ol_txrx_pdev_handle pdev) +{ + uint16_t desc_pool_size, i; + + desc_pool_size = ol_tx_desc_pool_size_hl(pdev->ctrl_pdev); + + qdf_atomic_init(&pdev->tx_queue.rsrc_cnt); + qdf_atomic_add(desc_pool_size, &pdev->tx_queue.rsrc_cnt); + + pdev->tx_queue.rsrc_threshold_lo = + ol_txrx_rsrc_threshold_lo(desc_pool_size); + pdev->tx_queue.rsrc_threshold_hi = + ol_txrx_rsrc_threshold_hi(desc_pool_size); + + for (i = 0 ; i < OL_TX_MAX_TXQ_GROUPS; i++) + qdf_atomic_init(&pdev->txq_grps[i].credit); + + ol_tx_target_credit_init(pdev, desc_pool_size); +} + +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP +static inline int ol_tx_encap_wrapper(struct ol_txrx_pdev_t *pdev, + ol_txrx_vdev_handle vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + if (OL_TX_ENCAP(vdev, tx_desc, msdu, tx_msdu_info) != A_OK) { + qdf_atomic_inc(&pdev->tx_queue.rsrc_cnt); + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, 1); + if (tx_msdu_info->peer) { + /* remove the peer reference added above */ + ol_txrx_peer_release_ref(tx_msdu_info->peer, + PEER_DEBUG_ID_OL_INTERNAL); + } + return -EINVAL; + } + + return 0; +} +#else +static inline int ol_tx_encap_wrapper(struct ol_txrx_pdev_t *pdev, + ol_txrx_vdev_handle vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + /* no-op */ + return 0; +} +#endif + +/** + * parse_ocb_tx_header() - Function to check for OCB + * @msdu: Pointer to OS packet (qdf_nbuf_t) + * @tx_ctrl: TX control header on a packet and extract it if present + * + * Return: true if ocb parsing is successful + */ +#ifdef WLAN_FEATURE_DSRC +#define OCB_HEADER_VERSION 1 +static bool parse_ocb_tx_header(qdf_nbuf_t msdu, + struct ocb_tx_ctrl_hdr_t *tx_ctrl) +{ + qdf_ether_header_t *eth_hdr_p; + struct ocb_tx_ctrl_hdr_t *tx_ctrl_hdr; + + /* Check if TX control header is present */ + eth_hdr_p = (qdf_ether_header_t *)qdf_nbuf_data(msdu); + if (eth_hdr_p->ether_type != QDF_SWAP_U16(ETHERTYPE_OCB_TX)) + /* TX control header is not present. Nothing to do.. */ + return true; + + /* Remove the ethernet header */ + qdf_nbuf_pull_head(msdu, sizeof(qdf_ether_header_t)); + + /* Parse the TX control header */ + tx_ctrl_hdr = (struct ocb_tx_ctrl_hdr_t *)qdf_nbuf_data(msdu); + + if (tx_ctrl_hdr->version == OCB_HEADER_VERSION) { + if (tx_ctrl) + qdf_mem_copy(tx_ctrl, tx_ctrl_hdr, + sizeof(*tx_ctrl_hdr)); + } else { + /* The TX control header is invalid. */ + return false; + } + + /* Remove the TX control header */ + qdf_nbuf_pull_head(msdu, tx_ctrl_hdr->length); + return true; +} +#else +static bool parse_ocb_tx_header(qdf_nbuf_t msdu, + struct ocb_tx_ctrl_hdr_t *tx_ctrl) +{ + return true; +} +#endif + +/** + * ol_txrx_mgmt_tx_desc_alloc() - Allocate and initialize a tx descriptor + * for management frame + * @pdev: the data physical device sending the data + * @vdev: the virtual device sending the data + * @tx_mgmt_frm: the tx management frame + * @tx_msdu_info: the tx meta data + * + * Return: the tx decriptor + */ +struct ol_tx_desc_t * +ol_txrx_mgmt_tx_desc_alloc( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t tx_mgmt_frm, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + struct ol_tx_desc_t *tx_desc; + + tx_msdu_info->htt.action.tx_comp_req = 1; + tx_desc = ol_tx_desc_hl(pdev, vdev, tx_mgmt_frm, tx_msdu_info); + return tx_desc; +} + +/** + * ol_txrx_mgmt_send_frame() - send a management frame + * @vdev: virtual device sending the frame + * @tx_desc: tx desc + * @tx_mgmt_frm: management frame to send + * @tx_msdu_info: the tx meta data + * @chanfreq: download change frequency + * + * Return: + * 0 -> the frame is accepted for transmission, -OR- + * 1 -> the frame was not accepted + */ +int ol_txrx_mgmt_send_frame( + struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t tx_mgmt_frm, + struct ol_txrx_msdu_info_t *tx_msdu_info, + uint16_t chanfreq) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + struct ol_tx_frms_queue_t *txq; + int status = 1; + + /* + * 1. Look up the peer and queue the frame in the peer's mgmt queue. + * 2. Invoke the download scheduler. + */ + txq = ol_tx_classify_mgmt(vdev, tx_desc, tx_mgmt_frm, tx_msdu_info); + if (!txq) { + /* TXRX_STATS_MSDU_LIST_INCR(vdev->pdev, tx.dropped.no_txq, + * msdu); + */ + qdf_atomic_inc(&pdev->tx_queue.rsrc_cnt); + ol_tx_desc_frame_free_nonstd(vdev->pdev, tx_desc, + 1 /* error */); + goto out; /* can't accept the tx mgmt frame */ + } + /* Initialize the HTT tx desc l2 header offset field. + * Even though tx encap does not apply to mgmt frames, + * htt_tx_desc_mpdu_header still needs to be called, + * to specifiy that there was no L2 header added by tx encap, + * so the frame's length does not need to be adjusted to account for + * an added L2 header. + */ + htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc, 0); + if (qdf_unlikely(htt_tx_desc_init( + pdev->htt_pdev, tx_desc->htt_tx_desc, + tx_desc->htt_tx_desc_paddr, + ol_tx_desc_id(pdev, tx_desc), + tx_mgmt_frm, + &tx_msdu_info->htt, &tx_msdu_info->tso_info, NULL, 0))) + goto out; + htt_tx_desc_display(tx_desc->htt_tx_desc); + htt_tx_desc_set_chanfreq(tx_desc->htt_tx_desc, chanfreq); + + ol_tx_enqueue(vdev->pdev, txq, tx_desc, tx_msdu_info); + ol_tx_sched(vdev->pdev); + status = 0; +out: + if (tx_msdu_info->peer) { + /* remove the peer reference added above */ + ol_txrx_peer_release_ref(tx_msdu_info->peer, + PEER_DEBUG_ID_OL_INTERNAL); + } + + return status; +} + +/** + * ol_tx_hl_base() - send tx frames for a HL system. + * @vdev: the virtual device sending the data + * @tx_spec: indicate what non-standard transmission actions to apply + * @msdu_list: the tx frames to send + * @tx_comp_req: tx completion req + * @call_sched: will schedule the tx if true + * + * Return: NULL if all MSDUs are accepted + */ +static inline qdf_nbuf_t +ol_tx_hl_base( + ol_txrx_vdev_handle vdev, + enum ol_tx_spec tx_spec, + qdf_nbuf_t msdu_list, + int tx_comp_req, + bool call_sched) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + qdf_nbuf_t msdu = msdu_list; + struct ol_txrx_msdu_info_t tx_msdu_info; + struct ocb_tx_ctrl_hdr_t tx_ctrl; + htt_pdev_handle htt_pdev = pdev->htt_pdev; + + tx_msdu_info.tso_info.is_tso = 0; + + /* + * The msdu_list variable could be used instead of the msdu var, + * but just to clarify which operations are done on a single MSDU + * vs. a list of MSDUs, use a distinct variable for single MSDUs + * within the list. + */ + while (msdu) { + qdf_nbuf_t next; + struct ol_tx_frms_queue_t *txq; + struct ol_tx_desc_t *tx_desc = NULL; + + qdf_mem_zero(&tx_ctrl, sizeof(tx_ctrl)); + tx_msdu_info.peer = NULL; + /* + * The netbuf will get stored into a (peer-TID) tx queue list + * inside the ol_tx_classify_store function or else dropped, + * so store the next pointer immediately. + */ + next = qdf_nbuf_next(msdu); + + tx_desc = ol_tx_hl_desc_alloc(pdev, vdev, msdu, &tx_msdu_info); + + if (!tx_desc) { + /* + * If we're out of tx descs, there's no need to try + * to allocate tx descs for the remaining MSDUs. + */ + TXRX_STATS_MSDU_LIST_INCR(pdev, tx.dropped.host_reject, + msdu); + return msdu; /* the list of unaccepted MSDUs */ + } + + /* OL_TXRX_PROT_AN_LOG(pdev->prot_an_tx_sent, msdu);*/ + + qdf_dp_trace_log_pkt(vdev->vdev_id, msdu, QDF_TX, + QDF_TRACE_DEFAULT_PDEV_ID); + DPTRACE(qdf_dp_trace_data_pkt(msdu, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_DP_TRACE_TX_PACKET_RECORD, + tx_desc->id, QDF_TX)); + + if (tx_spec != OL_TX_SPEC_STD) { +#if defined(FEATURE_WLAN_TDLS) + if (tx_spec & OL_TX_SPEC_NO_FREE) { + tx_desc->pkt_type = OL_TX_FRM_NO_FREE; + } else if (tx_spec & OL_TX_SPEC_TSO) { +#else + if (tx_spec & OL_TX_SPEC_TSO) { +#endif + tx_desc->pkt_type = OL_TX_FRM_TSO; + } + if (ol_txrx_tx_is_raw(tx_spec)) { + /* CHECK THIS: does this need + * to happen after htt_tx_desc_init? + */ + /* different types of raw frames */ + u_int8_t sub_type = + ol_txrx_tx_raw_subtype( + tx_spec); + htt_tx_desc_type(htt_pdev, + tx_desc->htt_tx_desc, + htt_pkt_type_raw, + sub_type); + } + } + + tx_msdu_info.htt.info.ext_tid = qdf_nbuf_get_tid(msdu); + tx_msdu_info.htt.info.vdev_id = vdev->vdev_id; + tx_msdu_info.htt.info.frame_type = htt_frm_type_data; + tx_msdu_info.htt.info.l2_hdr_type = pdev->htt_pkt_type; + + if (QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_NOTIFY_COMP(msdu) + == 1) { + tx_msdu_info.htt.action.tx_comp_req = 1; + tx_desc->pkt_type = OL_TX_FRM_NO_FREE; + } else { + tx_msdu_info.htt.action.tx_comp_req = + tx_comp_req; + } + + /* If the vdev is in OCB mode, + * parse the tx control header. + */ + if (vdev->opmode == wlan_op_mode_ocb) { + if (!parse_ocb_tx_header(msdu, &tx_ctrl)) { + /* There was an error parsing + * the header.Skip this packet. + */ + goto MSDU_LOOP_BOTTOM; + } + } + + txq = ol_tx_classify(vdev, tx_desc, msdu, + &tx_msdu_info); + + /* initialize the HW tx descriptor */ + htt_tx_desc_init( + pdev->htt_pdev, tx_desc->htt_tx_desc, + tx_desc->htt_tx_desc_paddr, + ol_tx_desc_id(pdev, tx_desc), + msdu, + &tx_msdu_info.htt, + &tx_msdu_info.tso_info, + &tx_ctrl, + vdev->opmode == wlan_op_mode_ocb); + + if ((!txq) || TX_FILTER_CHECK(&tx_msdu_info)) { + /* drop this frame, + * but try sending subsequent frames + */ + /* TXRX_STATS_MSDU_LIST_INCR(pdev, + * tx.dropped.no_txq, msdu); + */ + qdf_atomic_inc(&pdev->tx_queue.rsrc_cnt); + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, 1); + if (tx_msdu_info.peer) { + /* remove the peer reference + * added above + */ + ol_txrx_peer_release_ref( + tx_msdu_info.peer, + PEER_DEBUG_ID_OL_INTERNAL); + } + goto MSDU_LOOP_BOTTOM; + } + + if (tx_msdu_info.peer) { + /* + * If the state is not associated then drop all + * the data packets received for that peer + */ + if (tx_msdu_info.peer->state == + OL_TXRX_PEER_STATE_DISC) { + qdf_atomic_inc( + &pdev->tx_queue.rsrc_cnt); + ol_tx_desc_frame_free_nonstd(pdev, + tx_desc, + 1); + ol_txrx_peer_release_ref( + tx_msdu_info.peer, + PEER_DEBUG_ID_OL_INTERNAL); + msdu = next; + continue; + } else if (tx_msdu_info.peer->state != + OL_TXRX_PEER_STATE_AUTH) { + if (tx_msdu_info.htt.info.ethertype != + ETHERTYPE_PAE && + tx_msdu_info.htt.info.ethertype + != ETHERTYPE_WAI) { + qdf_atomic_inc( + &pdev->tx_queue. + rsrc_cnt); + ol_tx_desc_frame_free_nonstd( + pdev, + tx_desc, 1); + ol_txrx_peer_release_ref( + tx_msdu_info.peer, + PEER_DEBUG_ID_OL_INTERNAL); + msdu = next; + continue; + } + } + } + /* + * Initialize the HTT tx desc l2 header offset field. + * htt_tx_desc_mpdu_header needs to be called to + * make sure, the l2 header size is initialized + * correctly to handle cases where TX ENCAP is disabled + * or Tx Encap fails to perform Encap + */ + htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc, 0); + + /* + * Note: when the driver is built without support for + * SW tx encap,the following macro is a no-op. + * When the driver is built with support for SW tx + * encap, it performs encap, and if an error is + * encountered, jumps to the MSDU_LOOP_BOTTOM label. + */ + if (ol_tx_encap_wrapper(pdev, vdev, tx_desc, msdu, + &tx_msdu_info)) + goto MSDU_LOOP_BOTTOM; + + /* + * If debug display is enabled, show the meta-data + * being downloaded to the target via the + * HTT tx descriptor. + */ + htt_tx_desc_display(tx_desc->htt_tx_desc); + + ol_tx_enqueue(pdev, txq, tx_desc, &tx_msdu_info); + if (tx_msdu_info.peer) { + OL_TX_PEER_STATS_UPDATE(tx_msdu_info.peer, + msdu); + /* remove the peer reference added above */ + ol_txrx_peer_release_ref + (tx_msdu_info.peer, + PEER_DEBUG_ID_OL_INTERNAL); + } +MSDU_LOOP_BOTTOM: + msdu = next; + } + + if (call_sched) + ol_tx_sched(pdev); + return NULL; /* all MSDUs were accepted */ +} + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK + +/** + * ol_tx_pdev_reset_driver_del_ack() - reset driver delayed ack enabled flag + * @soc_hdl: soc handle + * @pdev_id: datapath pdev identifier + * + * Return: none + */ +void +ol_tx_pdev_reset_driver_del_ack(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_txrx_vdev_t *vdev; + + if (!pdev) + return; + + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + vdev->driver_del_ack_enabled = false; + + dp_debug("vdev_id %d driver_del_ack_enabled %d", + vdev->vdev_id, vdev->driver_del_ack_enabled); + } +} + +/** + * ol_tx_vdev_set_driver_del_ack_enable() - set driver delayed ack enabled flag + * @soc_hdl: datapath soc handle + * @vdev_id: vdev id + * @rx_packets: number of rx packets + * @time_in_ms: time in ms + * @high_th: high threshold + * @low_th: low threshold + * + * Return: none + */ +void +ol_tx_vdev_set_driver_del_ack_enable(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + unsigned long rx_packets, + uint32_t time_in_ms, + uint32_t high_th, + uint32_t low_th) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *) + ol_txrx_get_vdev_from_vdev_id(vdev_id); + bool old_driver_del_ack_enabled; + + if ((!vdev) || (low_th > high_th)) + return; + + old_driver_del_ack_enabled = vdev->driver_del_ack_enabled; + if (rx_packets > high_th) + vdev->driver_del_ack_enabled = true; + else if (rx_packets < low_th) + vdev->driver_del_ack_enabled = false; + + if (old_driver_del_ack_enabled != vdev->driver_del_ack_enabled) { + dp_debug("vdev_id %d driver_del_ack_enabled %d rx_packets %ld time_in_ms %d high_th %d low_th %d", + vdev->vdev_id, vdev->driver_del_ack_enabled, + rx_packets, time_in_ms, high_th, low_th); + } +} + +/** + * ol_tx_hl_send_all_tcp_ack() - send all queued tcp ack packets + * @vdev: vdev handle + * + * Return: none + */ +void ol_tx_hl_send_all_tcp_ack(struct ol_txrx_vdev_t *vdev) +{ + int i; + struct tcp_stream_node *tcp_node_list; + struct tcp_stream_node *temp; + struct ol_txrx_pdev_t *pdev = vdev->pdev; + + for (i = 0; i < OL_TX_HL_DEL_ACK_HASH_SIZE; i++) { + tcp_node_list = NULL; + qdf_spin_lock_bh(&vdev->tcp_ack_hash.node[i].hash_node_lock); + if (vdev->tcp_ack_hash.node[i].no_of_entries) + tcp_node_list = vdev->tcp_ack_hash.node[i].head; + + vdev->tcp_ack_hash.node[i].no_of_entries = 0; + vdev->tcp_ack_hash.node[i].head = NULL; + qdf_spin_unlock_bh(&vdev->tcp_ack_hash.node[i].hash_node_lock); + + /* Send all packets */ + while (tcp_node_list) { + int tx_comp_req = pdev->cfg.default_tx_comp_req || + pdev->cfg.request_tx_comp; + qdf_nbuf_t msdu_list; + + temp = tcp_node_list; + tcp_node_list = temp->next; + + msdu_list = ol_tx_hl_base(vdev, OL_TX_SPEC_STD, + temp->head, + tx_comp_req, false); + if (msdu_list) + qdf_nbuf_tx_free(msdu_list, 1/*error*/); + ol_txrx_vdev_free_tcp_node(vdev, temp); + } + } + ol_tx_sched(vdev->pdev); +} + +/** + * tcp_del_ack_tasklet() - tasklet function to send ack packets + * @data: vdev handle + * + * Return: none + */ +void tcp_del_ack_tasklet(void *data) +{ + struct ol_txrx_vdev_t *vdev = data; + + ol_tx_hl_send_all_tcp_ack(vdev); +} + +/** + * ol_tx_get_stream_id() - get stream_id from packet info + * @info: packet info + * + * Return: stream_id + */ +uint16_t ol_tx_get_stream_id(struct packet_info *info) +{ + return ((info->dst_port + info->dst_ip + info->src_port + info->src_ip) + & (OL_TX_HL_DEL_ACK_HASH_SIZE - 1)); +} + +/** + * ol_tx_is_tcp_ack() - check whether the packet is tcp ack frame + * @msdu: packet + * + * Return: true if the packet is tcp ack frame + */ +static bool +ol_tx_is_tcp_ack(qdf_nbuf_t msdu) +{ + uint16_t ether_type; + uint8_t protocol; + uint8_t flag, ip_header_len, tcp_header_len; + uint32_t seg_len; + uint8_t *skb_data; + uint32_t skb_len; + bool tcp_acked = false; + uint32_t tcp_header_off; + + qdf_nbuf_peek_header(msdu, &skb_data, &skb_len); + if (skb_len < (QDF_NBUF_TRAC_IPV4_OFFSET + + QDF_NBUF_TRAC_IPV4_HEADER_SIZE + + QDF_NBUF_TRAC_TCP_FLAGS_OFFSET)) + goto exit; + + ether_type = (uint16_t)(*(uint16_t *) + (skb_data + QDF_NBUF_TRAC_ETH_TYPE_OFFSET)); + protocol = (uint16_t)(*(uint16_t *) + (skb_data + QDF_NBUF_TRAC_IPV4_PROTO_TYPE_OFFSET)); + + if ((QDF_SWAP_U16(QDF_NBUF_TRAC_IPV4_ETH_TYPE) == ether_type) && + (protocol == QDF_NBUF_TRAC_TCP_TYPE)) { + ip_header_len = ((uint8_t)(*(uint8_t *) + (skb_data + QDF_NBUF_TRAC_IPV4_OFFSET)) & + QDF_NBUF_TRAC_IPV4_HEADER_MASK) << 2; + tcp_header_off = QDF_NBUF_TRAC_IPV4_OFFSET + ip_header_len; + + tcp_header_len = ((uint8_t)(*(uint8_t *) + (skb_data + tcp_header_off + + QDF_NBUF_TRAC_TCP_HEADER_LEN_OFFSET))) >> 2; + seg_len = skb_len - tcp_header_len - tcp_header_off; + flag = (uint8_t)(*(uint8_t *) + (skb_data + tcp_header_off + + QDF_NBUF_TRAC_TCP_FLAGS_OFFSET)); + + if ((flag == QDF_NBUF_TRAC_TCP_ACK_MASK) && (seg_len == 0)) + tcp_acked = true; + } + +exit: + + return tcp_acked; +} + +/** + * ol_tx_get_packet_info() - update packet info for passed msdu + * @msdu: packet + * @info: packet info + * + * Return: none + */ +void ol_tx_get_packet_info(qdf_nbuf_t msdu, struct packet_info *info) +{ + uint16_t ether_type; + uint8_t protocol; + uint8_t flag, ip_header_len, tcp_header_len; + uint32_t seg_len; + uint8_t *skb_data; + uint32_t skb_len; + uint32_t tcp_header_off; + + info->type = NO_TCP_PKT; + + qdf_nbuf_peek_header(msdu, &skb_data, &skb_len); + if (skb_len < (QDF_NBUF_TRAC_IPV4_OFFSET + + QDF_NBUF_TRAC_IPV4_HEADER_SIZE + + QDF_NBUF_TRAC_TCP_FLAGS_OFFSET)) + return; + + ether_type = (uint16_t)(*(uint16_t *) + (skb_data + QDF_NBUF_TRAC_ETH_TYPE_OFFSET)); + protocol = (uint16_t)(*(uint16_t *) + (skb_data + QDF_NBUF_TRAC_IPV4_PROTO_TYPE_OFFSET)); + + if ((QDF_SWAP_U16(QDF_NBUF_TRAC_IPV4_ETH_TYPE) == ether_type) && + (protocol == QDF_NBUF_TRAC_TCP_TYPE)) { + ip_header_len = ((uint8_t)(*(uint8_t *) + (skb_data + QDF_NBUF_TRAC_IPV4_OFFSET)) & + QDF_NBUF_TRAC_IPV4_HEADER_MASK) << 2; + tcp_header_off = QDF_NBUF_TRAC_IPV4_OFFSET + ip_header_len; + + tcp_header_len = ((uint8_t)(*(uint8_t *) + (skb_data + tcp_header_off + + QDF_NBUF_TRAC_TCP_HEADER_LEN_OFFSET))) >> 2; + seg_len = skb_len - tcp_header_len - tcp_header_off; + flag = (uint8_t)(*(uint8_t *) + (skb_data + tcp_header_off + + QDF_NBUF_TRAC_TCP_FLAGS_OFFSET)); + + info->src_ip = QDF_SWAP_U32((uint32_t)(*(uint32_t *) + (skb_data + QDF_NBUF_TRAC_IPV4_SRC_ADDR_OFFSET))); + info->dst_ip = QDF_SWAP_U32((uint32_t)(*(uint32_t *) + (skb_data + QDF_NBUF_TRAC_IPV4_DEST_ADDR_OFFSET))); + info->src_port = QDF_SWAP_U16((uint16_t)(*(uint16_t *) + (skb_data + tcp_header_off + + QDF_NBUF_TRAC_TCP_SPORT_OFFSET))); + info->dst_port = QDF_SWAP_U16((uint16_t)(*(uint16_t *) + (skb_data + tcp_header_off + + QDF_NBUF_TRAC_TCP_DPORT_OFFSET))); + info->stream_id = ol_tx_get_stream_id(info); + + if ((flag == QDF_NBUF_TRAC_TCP_ACK_MASK) && (seg_len == 0)) { + info->type = TCP_PKT_ACK; + info->ack_number = (uint32_t)(*(uint32_t *) + (skb_data + tcp_header_off + + QDF_NBUF_TRAC_TCP_ACK_OFFSET)); + info->ack_number = QDF_SWAP_U32(info->ack_number); + } else { + info->type = TCP_PKT_NO_ACK; + } + } +} + +/** + * ol_tx_hl_find_and_send_tcp_stream() - find and send tcp stream for passed + * stream info + * @vdev: vdev handle + * @info: packet info + * + * Return: none + */ +void ol_tx_hl_find_and_send_tcp_stream(struct ol_txrx_vdev_t *vdev, + struct packet_info *info) +{ + uint8_t no_of_entries; + struct tcp_stream_node *node_to_be_remove = NULL; + struct ol_txrx_pdev_t *pdev = vdev->pdev; + + /* remove tcp node from hash */ + qdf_spin_lock_bh(&vdev->tcp_ack_hash.node[info->stream_id]. + hash_node_lock); + + no_of_entries = vdev->tcp_ack_hash.node[info->stream_id]. + no_of_entries; + if (no_of_entries > 1) { + /* collision case */ + struct tcp_stream_node *head = + vdev->tcp_ack_hash.node[info->stream_id].head; + struct tcp_stream_node *temp; + + if ((head->dst_ip == info->dst_ip) && + (head->src_ip == info->src_ip) && + (head->src_port == info->src_port) && + (head->dst_port == info->dst_port)) { + node_to_be_remove = head; + vdev->tcp_ack_hash.node[info->stream_id].head = + head->next; + vdev->tcp_ack_hash.node[info->stream_id]. + no_of_entries--; + } else { + temp = head; + while (temp->next) { + if ((temp->next->dst_ip == info->dst_ip) && + (temp->next->src_ip == info->src_ip) && + (temp->next->src_port == info->src_port) && + (temp->next->dst_port == info->dst_port)) { + node_to_be_remove = temp->next; + temp->next = temp->next->next; + vdev->tcp_ack_hash. + node[info->stream_id]. + no_of_entries--; + break; + } + temp = temp->next; + } + } + } else if (no_of_entries == 1) { + /* Only one tcp_node */ + node_to_be_remove = + vdev->tcp_ack_hash.node[info->stream_id].head; + vdev->tcp_ack_hash.node[info->stream_id].head = NULL; + vdev->tcp_ack_hash.node[info->stream_id].no_of_entries = 0; + } + qdf_spin_unlock_bh(&vdev->tcp_ack_hash. + node[info->stream_id].hash_node_lock); + + /* send packets */ + if (node_to_be_remove) { + int tx_comp_req = pdev->cfg.default_tx_comp_req || + pdev->cfg.request_tx_comp; + qdf_nbuf_t msdu_list; + + msdu_list = ol_tx_hl_base(vdev, OL_TX_SPEC_STD, + node_to_be_remove->head, + tx_comp_req, true); + if (msdu_list) + qdf_nbuf_tx_free(msdu_list, 1/*error*/); + ol_txrx_vdev_free_tcp_node(vdev, node_to_be_remove); + } +} + +static struct tcp_stream_node * +ol_tx_hl_rep_tcp_ack(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu, + struct packet_info *info, bool *is_found, + bool *start_timer) +{ + struct tcp_stream_node *node_to_be_remove = NULL; + struct tcp_stream_node *head = + vdev->tcp_ack_hash.node[info->stream_id].head; + struct tcp_stream_node *temp; + + if ((head->dst_ip == info->dst_ip) && + (head->src_ip == info->src_ip) && + (head->src_port == info->src_port) && + (head->dst_port == info->dst_port)) { + *is_found = true; + if ((head->ack_number < info->ack_number) && + (head->no_of_ack_replaced < + ol_cfg_get_del_ack_count_value(vdev->pdev->ctrl_pdev))) { + /* replace ack packet */ + qdf_nbuf_tx_free(head->head, 1); + head->head = msdu; + head->ack_number = info->ack_number; + head->no_of_ack_replaced++; + *start_timer = true; + + vdev->no_of_tcpack_replaced++; + + if (head->no_of_ack_replaced == + ol_cfg_get_del_ack_count_value( + vdev->pdev->ctrl_pdev)) { + node_to_be_remove = head; + vdev->tcp_ack_hash.node[info->stream_id].head = + head->next; + vdev->tcp_ack_hash.node[info->stream_id]. + no_of_entries--; + } + } else { + /* append and send packets */ + head->head->next = msdu; + node_to_be_remove = head; + vdev->tcp_ack_hash.node[info->stream_id].head = + head->next; + vdev->tcp_ack_hash.node[info->stream_id]. + no_of_entries--; + } + } else { + temp = head; + while (temp->next) { + if ((temp->next->dst_ip == info->dst_ip) && + (temp->next->src_ip == info->src_ip) && + (temp->next->src_port == info->src_port) && + (temp->next->dst_port == info->dst_port)) { + *is_found = true; + if ((temp->next->ack_number < + info->ack_number) && + (temp->next->no_of_ack_replaced < + ol_cfg_get_del_ack_count_value( + vdev->pdev->ctrl_pdev))) { + /* replace ack packet */ + qdf_nbuf_tx_free(temp->next->head, 1); + temp->next->head = msdu; + temp->next->ack_number = + info->ack_number; + temp->next->no_of_ack_replaced++; + *start_timer = true; + + vdev->no_of_tcpack_replaced++; + + if (temp->next->no_of_ack_replaced == + ol_cfg_get_del_ack_count_value( + vdev->pdev->ctrl_pdev)) { + node_to_be_remove = temp->next; + temp->next = temp->next->next; + vdev->tcp_ack_hash. + node[info->stream_id]. + no_of_entries--; + } + } else { + /* append and send packets */ + temp->next->head->next = msdu; + node_to_be_remove = temp->next; + temp->next = temp->next->next; + vdev->tcp_ack_hash. + node[info->stream_id]. + no_of_entries--; + } + break; + } + temp = temp->next; + } + } + return node_to_be_remove; +} + +/** + * ol_tx_hl_find_and_replace_tcp_ack() - find and replace tcp ack packet for + * passed packet info + * @vdev: vdev handle + * @msdu: packet + * @info: packet info + * + * Return: none + */ +void ol_tx_hl_find_and_replace_tcp_ack(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, + struct packet_info *info) +{ + uint8_t no_of_entries; + struct tcp_stream_node *node_to_be_remove = NULL; + bool is_found = false, start_timer = false; + struct ol_txrx_pdev_t *pdev = vdev->pdev; + + /* replace ack if required or send packets */ + qdf_spin_lock_bh(&vdev->tcp_ack_hash.node[info->stream_id]. + hash_node_lock); + + no_of_entries = vdev->tcp_ack_hash.node[info->stream_id].no_of_entries; + if (no_of_entries > 0) { + node_to_be_remove = ol_tx_hl_rep_tcp_ack(vdev, msdu, info, + &is_found, + &start_timer); + } + + if (no_of_entries == 0 || !is_found) { + /* Alloc new tcp node */ + struct tcp_stream_node *new_node; + + new_node = ol_txrx_vdev_alloc_tcp_node(vdev); + if (!new_node) { + qdf_spin_unlock_bh(&vdev->tcp_ack_hash. + node[info->stream_id].hash_node_lock); + dp_alert("Malloc failed"); + return; + } + new_node->stream_id = info->stream_id; + new_node->dst_ip = info->dst_ip; + new_node->src_ip = info->src_ip; + new_node->dst_port = info->dst_port; + new_node->src_port = info->src_port; + new_node->ack_number = info->ack_number; + new_node->head = msdu; + new_node->next = NULL; + new_node->no_of_ack_replaced = 0; + + start_timer = true; + /* insert new_node */ + if (!vdev->tcp_ack_hash.node[info->stream_id].head) { + vdev->tcp_ack_hash.node[info->stream_id].head = + new_node; + vdev->tcp_ack_hash.node[info->stream_id]. + no_of_entries = 1; + } else { + struct tcp_stream_node *temp = + vdev->tcp_ack_hash.node[info->stream_id].head; + while (temp->next) + temp = temp->next; + + temp->next = new_node; + vdev->tcp_ack_hash.node[info->stream_id]. + no_of_entries++; + } + } + qdf_spin_unlock_bh(&vdev->tcp_ack_hash.node[info->stream_id]. + hash_node_lock); + + /* start timer */ + if (start_timer && + (!qdf_atomic_read(&vdev->tcp_ack_hash.is_timer_running))) { + qdf_hrtimer_start(&vdev->tcp_ack_hash.timer, + qdf_ns_to_ktime(( + ol_cfg_get_del_ack_timer_value( + vdev->pdev->ctrl_pdev) * + 1000000)), + __QDF_HRTIMER_MODE_REL); + qdf_atomic_set(&vdev->tcp_ack_hash.is_timer_running, 1); + } + + /* send packets */ + if (node_to_be_remove) { + int tx_comp_req = pdev->cfg.default_tx_comp_req || + pdev->cfg.request_tx_comp; + qdf_nbuf_t msdu_list = NULL; + + msdu_list = ol_tx_hl_base(vdev, OL_TX_SPEC_STD, + node_to_be_remove->head, + tx_comp_req, true); + if (msdu_list) + qdf_nbuf_tx_free(msdu_list, 1/*error*/); + ol_txrx_vdev_free_tcp_node(vdev, node_to_be_remove); + } +} + +/** + * ol_tx_hl_vdev_tcp_del_ack_timer() - delayed ack timer function + * @timer: timer handle + * + * Return: enum + */ +enum qdf_hrtimer_restart_status +ol_tx_hl_vdev_tcp_del_ack_timer(qdf_hrtimer_data_t *timer) +{ + struct ol_txrx_vdev_t *vdev = qdf_container_of(timer, + struct ol_txrx_vdev_t, + tcp_ack_hash.timer); + enum qdf_hrtimer_restart_status ret = __QDF_HRTIMER_NORESTART; + + qdf_sched_bh(&vdev->tcp_ack_hash.tcp_del_ack_tq); + qdf_atomic_set(&vdev->tcp_ack_hash.is_timer_running, 0); + return ret; +} + +/** + * ol_tx_hl_del_ack_queue_flush_all() - drop all queued packets + * @vdev: vdev handle + * + * Return: none + */ +void ol_tx_hl_del_ack_queue_flush_all(struct ol_txrx_vdev_t *vdev) +{ + int i; + struct tcp_stream_node *tcp_node_list; + struct tcp_stream_node *temp; + + qdf_hrtimer_cancel(&vdev->tcp_ack_hash.timer); + for (i = 0; i < OL_TX_HL_DEL_ACK_HASH_SIZE; i++) { + tcp_node_list = NULL; + qdf_spin_lock_bh(&vdev->tcp_ack_hash.node[i].hash_node_lock); + + if (vdev->tcp_ack_hash.node[i].no_of_entries) + tcp_node_list = vdev->tcp_ack_hash.node[i].head; + + vdev->tcp_ack_hash.node[i].no_of_entries = 0; + vdev->tcp_ack_hash.node[i].head = NULL; + qdf_spin_unlock_bh(&vdev->tcp_ack_hash.node[i].hash_node_lock); + + /* free all packets */ + while (tcp_node_list) { + temp = tcp_node_list; + tcp_node_list = temp->next; + + qdf_nbuf_tx_free(temp->head, 1/*error*/); + ol_txrx_vdev_free_tcp_node(vdev, temp); + } + } + ol_txrx_vdev_deinit_tcp_del_ack(vdev); +} + +/** + * ol_txrx_vdev_init_tcp_del_ack() - initialize tcp delayed ack structure + * @vdev: vdev handle + * + * Return: none + */ +void ol_txrx_vdev_init_tcp_del_ack(struct ol_txrx_vdev_t *vdev) +{ + int i; + + vdev->driver_del_ack_enabled = false; + + dp_debug("vdev-id=%u, driver_del_ack_enabled=%d", + vdev->vdev_id, + vdev->driver_del_ack_enabled); + + vdev->no_of_tcpack = 0; + vdev->no_of_tcpack_replaced = 0; + + qdf_hrtimer_init(&vdev->tcp_ack_hash.timer, + ol_tx_hl_vdev_tcp_del_ack_timer, + __QDF_CLOCK_MONOTONIC, + __QDF_HRTIMER_MODE_REL, + QDF_CONTEXT_HARDWARE + ); + qdf_create_bh(&vdev->tcp_ack_hash.tcp_del_ack_tq, + tcp_del_ack_tasklet, + vdev); + qdf_atomic_init(&vdev->tcp_ack_hash.is_timer_running); + qdf_atomic_init(&vdev->tcp_ack_hash.tcp_node_in_use_count); + qdf_spinlock_create(&vdev->tcp_ack_hash.tcp_free_list_lock); + vdev->tcp_ack_hash.tcp_free_list = NULL; + for (i = 0; i < OL_TX_HL_DEL_ACK_HASH_SIZE; i++) { + qdf_spinlock_create(&vdev->tcp_ack_hash.node[i].hash_node_lock); + vdev->tcp_ack_hash.node[i].no_of_entries = 0; + vdev->tcp_ack_hash.node[i].head = NULL; + } +} + +/** + * ol_txrx_vdev_deinit_tcp_del_ack() - deinitialize tcp delayed ack structure + * @vdev: vdev handle + * + * Return: none + */ +void ol_txrx_vdev_deinit_tcp_del_ack(struct ol_txrx_vdev_t *vdev) +{ + struct tcp_stream_node *temp; + + qdf_destroy_bh(&vdev->tcp_ack_hash.tcp_del_ack_tq); + + qdf_spin_lock_bh(&vdev->tcp_ack_hash.tcp_free_list_lock); + while (vdev->tcp_ack_hash.tcp_free_list) { + temp = vdev->tcp_ack_hash.tcp_free_list; + vdev->tcp_ack_hash.tcp_free_list = temp->next; + qdf_mem_free(temp); + } + qdf_spin_unlock_bh(&vdev->tcp_ack_hash.tcp_free_list_lock); +} + +/** + * ol_txrx_vdev_free_tcp_node() - add tcp node in free list + * @vdev: vdev handle + * @node: tcp stream node + * + * Return: none + */ +void ol_txrx_vdev_free_tcp_node(struct ol_txrx_vdev_t *vdev, + struct tcp_stream_node *node) +{ + qdf_atomic_dec(&vdev->tcp_ack_hash.tcp_node_in_use_count); + + qdf_spin_lock_bh(&vdev->tcp_ack_hash.tcp_free_list_lock); + if (vdev->tcp_ack_hash.tcp_free_list) { + node->next = vdev->tcp_ack_hash.tcp_free_list; + vdev->tcp_ack_hash.tcp_free_list = node; + } else { + vdev->tcp_ack_hash.tcp_free_list = node; + node->next = NULL; + } + qdf_spin_unlock_bh(&vdev->tcp_ack_hash.tcp_free_list_lock); +} + +/** + * ol_txrx_vdev_alloc_tcp_node() - allocate tcp node + * @vdev: vdev handle + * + * Return: tcp stream node + */ +struct tcp_stream_node *ol_txrx_vdev_alloc_tcp_node(struct ol_txrx_vdev_t *vdev) +{ + struct tcp_stream_node *node = NULL; + + qdf_spin_lock_bh(&vdev->tcp_ack_hash.tcp_free_list_lock); + if (vdev->tcp_ack_hash.tcp_free_list) { + node = vdev->tcp_ack_hash.tcp_free_list; + vdev->tcp_ack_hash.tcp_free_list = node->next; + } + qdf_spin_unlock_bh(&vdev->tcp_ack_hash.tcp_free_list_lock); + + if (!node) { + node = qdf_mem_malloc(sizeof(struct ol_txrx_vdev_t)); + if (!node) + return NULL; + } + qdf_atomic_inc(&vdev->tcp_ack_hash.tcp_node_in_use_count); + return node; +} + +qdf_nbuf_t +ol_tx_hl(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + int tx_comp_req = pdev->cfg.default_tx_comp_req || + pdev->cfg.request_tx_comp; + struct packet_info pkt_info; + qdf_nbuf_t temp; + + if (ol_tx_is_tcp_ack(msdu_list)) + vdev->no_of_tcpack++; + + /* check Enable through ini */ + if (!ol_cfg_get_del_ack_enable_value(vdev->pdev->ctrl_pdev) || + (!vdev->driver_del_ack_enabled)) { + if (qdf_atomic_read(&vdev->tcp_ack_hash.tcp_node_in_use_count)) + ol_tx_hl_send_all_tcp_ack(vdev); + + return ol_tx_hl_base(vdev, OL_TX_SPEC_STD, msdu_list, + tx_comp_req, true); + } + + ol_tx_get_packet_info(msdu_list, &pkt_info); + + if (pkt_info.type == TCP_PKT_NO_ACK) { + ol_tx_hl_find_and_send_tcp_stream(vdev, &pkt_info); + temp = ol_tx_hl_base(vdev, OL_TX_SPEC_STD, msdu_list, + tx_comp_req, true); + return temp; + } + + if (pkt_info.type == TCP_PKT_ACK) { + ol_tx_hl_find_and_replace_tcp_ack(vdev, msdu_list, &pkt_info); + return NULL; + } + + temp = ol_tx_hl_base(vdev, OL_TX_SPEC_STD, msdu_list, + tx_comp_req, true); + return temp; +} +#else + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE +void +ol_tx_pdev_reset_bundle_require(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + struct ol_txrx_vdev_t *vdev; + + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + vdev->bundling_required = false; + ol_txrx_info("vdev_id %d bundle_require %d\n", + vdev->vdev_id, vdev->bundling_required); + } +} + +void +ol_tx_vdev_set_bundle_require(uint8_t vdev_id, unsigned long tx_bytes, + uint32_t time_in_ms, uint32_t high_th, + uint32_t low_th) +{ + struct ol_txrx_vdev_t *vdev = (struct ol_txrx_vdev_t *) + ol_txrx_get_vdev_from_vdev_id(vdev_id); + bool old_bundle_required; + + if ((!vdev) || (low_th > high_th)) + return; + + old_bundle_required = vdev->bundling_required; + if (tx_bytes > ((high_th * time_in_ms * 1500) / 1000)) + vdev->bundling_required = true; + else if (tx_bytes < ((low_th * time_in_ms * 1500) / 1000)) + vdev->bundling_required = false; + + if (old_bundle_required != vdev->bundling_required) + ol_txrx_info("vdev_id %d bundle_require %d tx_bytes %ld time_in_ms %d high_th %d low_th %d\n", + vdev->vdev_id, vdev->bundling_required, tx_bytes, + time_in_ms, high_th, low_th); +} + +/** + * ol_tx_hl_queue_flush_all() - drop all packets in vdev bundle queue + * @vdev: vdev handle + * + * Return: none + */ +void +ol_tx_hl_queue_flush_all(struct ol_txrx_vdev_t *vdev) +{ + qdf_spin_lock_bh(&vdev->bundle_queue.mutex); + if (vdev->bundle_queue.txq.depth != 0) { + qdf_timer_stop(&vdev->bundle_queue.timer); + vdev->pdev->total_bundle_queue_length -= + vdev->bundle_queue.txq.depth; + qdf_nbuf_tx_free(vdev->bundle_queue.txq.head, 1/*error*/); + vdev->bundle_queue.txq.depth = 0; + vdev->bundle_queue.txq.head = NULL; + vdev->bundle_queue.txq.tail = NULL; + } + qdf_spin_unlock_bh(&vdev->bundle_queue.mutex); +} + +/** + * ol_tx_hl_vdev_queue_append() - append pkt in tx queue + * @vdev: vdev handle + * @msdu_list: msdu list + * + * Return: none + */ +static void +ol_tx_hl_vdev_queue_append(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu_list) +{ + qdf_spin_lock_bh(&vdev->bundle_queue.mutex); + + if (!vdev->bundle_queue.txq.head) { + qdf_timer_start( + &vdev->bundle_queue.timer, + ol_cfg_get_bundle_timer_value(vdev->pdev->ctrl_pdev)); + vdev->bundle_queue.txq.head = msdu_list; + vdev->bundle_queue.txq.tail = msdu_list; + } else { + qdf_nbuf_set_next(vdev->bundle_queue.txq.tail, msdu_list); + } + + while (qdf_nbuf_next(msdu_list)) { + vdev->bundle_queue.txq.depth++; + vdev->pdev->total_bundle_queue_length++; + msdu_list = qdf_nbuf_next(msdu_list); + } + + vdev->bundle_queue.txq.depth++; + vdev->pdev->total_bundle_queue_length++; + vdev->bundle_queue.txq.tail = msdu_list; + qdf_spin_unlock_bh(&vdev->bundle_queue.mutex); +} + +/** + * ol_tx_hl_vdev_queue_send_all() - send all packets in vdev bundle queue + * @vdev: vdev handle + * @call_sched: invoke scheduler + * + * Return: NULL for success + */ +static qdf_nbuf_t +ol_tx_hl_vdev_queue_send_all(struct ol_txrx_vdev_t *vdev, bool call_sched, + bool in_timer_context) +{ + qdf_nbuf_t msdu_list = NULL; + qdf_nbuf_t skb_list_head, skb_list_tail; + struct ol_txrx_pdev_t *pdev = vdev->pdev; + int tx_comp_req = pdev->cfg.default_tx_comp_req || + pdev->cfg.request_tx_comp; + int pkt_to_sent; + + qdf_spin_lock_bh(&vdev->bundle_queue.mutex); + + if (!vdev->bundle_queue.txq.depth) { + qdf_spin_unlock_bh(&vdev->bundle_queue.mutex); + return msdu_list; + } + + if (likely((qdf_atomic_read(&vdev->tx_desc_count) + + vdev->bundle_queue.txq.depth) < + vdev->queue_stop_th)) { + qdf_timer_stop(&vdev->bundle_queue.timer); + vdev->pdev->total_bundle_queue_length -= + vdev->bundle_queue.txq.depth; + msdu_list = ol_tx_hl_base(vdev, OL_TX_SPEC_STD, + vdev->bundle_queue.txq.head, + tx_comp_req, call_sched); + vdev->bundle_queue.txq.depth = 0; + vdev->bundle_queue.txq.head = NULL; + vdev->bundle_queue.txq.tail = NULL; + } else { + pkt_to_sent = vdev->queue_stop_th - + qdf_atomic_read(&vdev->tx_desc_count); + + if (pkt_to_sent) { + skb_list_head = vdev->bundle_queue.txq.head; + + while (pkt_to_sent) { + skb_list_tail = + vdev->bundle_queue.txq.head; + vdev->bundle_queue.txq.head = + qdf_nbuf_next(vdev->bundle_queue.txq.head); + vdev->pdev->total_bundle_queue_length--; + vdev->bundle_queue.txq.depth--; + pkt_to_sent--; + if (!vdev->bundle_queue.txq.head) { + qdf_timer_stop( + &vdev->bundle_queue.timer); + break; + } + } + + qdf_nbuf_set_next(skb_list_tail, NULL); + msdu_list = ol_tx_hl_base(vdev, OL_TX_SPEC_STD, + skb_list_head, tx_comp_req, + call_sched); + } + + if (in_timer_context && vdev->bundle_queue.txq.head) { + qdf_timer_start( + &vdev->bundle_queue.timer, + ol_cfg_get_bundle_timer_value( + vdev->pdev->ctrl_pdev)); + } + } + qdf_spin_unlock_bh(&vdev->bundle_queue.mutex); + + return msdu_list; +} + +/** + * ol_tx_hl_pdev_queue_send_all() - send all packets from all vdev bundle queue + * @pdev: pdev handle + * + * Return: NULL for success + */ +qdf_nbuf_t +ol_tx_hl_pdev_queue_send_all(struct ol_txrx_pdev_t *pdev) +{ + struct ol_txrx_vdev_t *vdev; + qdf_nbuf_t msdu_list; + + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + msdu_list = ol_tx_hl_vdev_queue_send_all(vdev, false, false); + if (msdu_list) + qdf_nbuf_tx_free(msdu_list, 1/*error*/); + } + ol_tx_sched(pdev); + return NULL; /* all msdus were accepted */ +} + +/** + * ol_tx_hl_vdev_bundle_timer() - bundle timer function + * @vdev: vdev handle + * + * Return: none + */ +void +ol_tx_hl_vdev_bundle_timer(void *ctx) +{ + qdf_nbuf_t msdu_list; + struct ol_txrx_vdev_t *vdev = (struct ol_txrx_vdev_t *)ctx; + + vdev->no_of_bundle_sent_in_timer++; + msdu_list = ol_tx_hl_vdev_queue_send_all(vdev, true, true); + if (msdu_list) + qdf_nbuf_tx_free(msdu_list, 1/*error*/); +} + +qdf_nbuf_t +ol_tx_hl(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu_list) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + int tx_comp_req = pdev->cfg.default_tx_comp_req || + pdev->cfg.request_tx_comp; + + /* No queuing for high priority packets */ + if (ol_tx_desc_is_high_prio(msdu_list)) { + vdev->no_of_pkt_not_added_in_queue++; + return ol_tx_hl_base(vdev, OL_TX_SPEC_STD, msdu_list, + tx_comp_req, true); + } else if (vdev->bundling_required && + (ol_cfg_get_bundle_size(vdev->pdev->ctrl_pdev) > 1)) { + ol_tx_hl_vdev_queue_append(vdev, msdu_list); + + if (pdev->total_bundle_queue_length >= + ol_cfg_get_bundle_size(vdev->pdev->ctrl_pdev)) { + vdev->no_of_bundle_sent_after_threshold++; + return ol_tx_hl_pdev_queue_send_all(pdev); + } + } else { + if (vdev->bundle_queue.txq.depth != 0) { + ol_tx_hl_vdev_queue_append(vdev, msdu_list); + return ol_tx_hl_vdev_queue_send_all(vdev, true, false); + } else { + vdev->no_of_pkt_not_added_in_queue++; + return ol_tx_hl_base(vdev, OL_TX_SPEC_STD, msdu_list, + tx_comp_req, true); + } + } + + return NULL; /* all msdus were accepted */ +} + +#else + +qdf_nbuf_t +ol_tx_hl(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + int tx_comp_req = pdev->cfg.default_tx_comp_req || + pdev->cfg.request_tx_comp; + + return ol_tx_hl_base(vdev, OL_TX_SPEC_STD, + msdu_list, tx_comp_req, true); +} +#endif +#endif + +qdf_nbuf_t ol_tx_non_std_hl(struct ol_txrx_vdev_t *vdev, + enum ol_tx_spec tx_spec, + qdf_nbuf_t msdu_list) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + int tx_comp_req = pdev->cfg.default_tx_comp_req || + pdev->cfg.request_tx_comp; + + if (!tx_comp_req) { + if ((tx_spec == OL_TX_SPEC_NO_FREE) && + (pdev->tx_data_callback.func)) + tx_comp_req = 1; + } + return ol_tx_hl_base(vdev, tx_spec, msdu_list, tx_comp_req, true); +} + +#ifdef FEATURE_WLAN_TDLS +void ol_txrx_copy_mac_addr_raw(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t *bss_addr) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev) + return; + + qdf_spin_lock_bh(&vdev->pdev->last_real_peer_mutex); + if (bss_addr && vdev->last_real_peer && + !qdf_mem_cmp((u8 *)bss_addr, + vdev->last_real_peer->mac_addr.raw, + QDF_MAC_ADDR_SIZE)) + qdf_mem_copy(vdev->hl_tdls_ap_mac_addr.raw, + vdev->last_real_peer->mac_addr.raw, + QDF_MAC_ADDR_SIZE); + qdf_spin_unlock_bh(&vdev->pdev->last_real_peer_mutex); +} + +void +ol_txrx_add_last_real_peer(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint8_t vdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + ol_txrx_peer_handle peer; + + if (!pdev || !vdev) + return; + + peer = ol_txrx_find_peer_by_addr( + (struct cdp_pdev *)pdev, + vdev->hl_tdls_ap_mac_addr.raw); + + qdf_spin_lock_bh(&pdev->last_real_peer_mutex); + if (!vdev->last_real_peer && peer && + (peer->peer_ids[0] != HTT_INVALID_PEER_ID)) { + vdev->last_real_peer = peer; + qdf_mem_zero(vdev->hl_tdls_ap_mac_addr.raw, + QDF_MAC_ADDR_SIZE); + } + qdf_spin_unlock_bh(&pdev->last_real_peer_mutex); +} + +bool is_vdev_restore_last_peer(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t *peer_mac) +{ + struct ol_txrx_peer_t *peer; + struct ol_txrx_pdev_t *pdev; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev) + return false; + + pdev = vdev->pdev; + peer = ol_txrx_find_peer_by_addr((struct cdp_pdev *)pdev, peer_mac); + + return vdev->last_real_peer && (vdev->last_real_peer == peer); +} + +void ol_txrx_update_last_real_peer(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint8_t vdev_id, bool restore_last_peer) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + struct ol_txrx_peer_t *peer; + + if (!restore_last_peer || !pdev || !vdev) + return; + + peer = ol_txrx_find_peer_by_addr((struct cdp_pdev *)pdev, + vdev->hl_tdls_ap_mac_addr.raw); + + qdf_spin_lock_bh(&pdev->last_real_peer_mutex); + if (!vdev->last_real_peer && peer && + (peer->peer_ids[0] != HTT_INVALID_PEER_ID)) { + vdev->last_real_peer = peer; + qdf_mem_zero(vdev->hl_tdls_ap_mac_addr.raw, + QDF_MAC_ADDR_SIZE); + } + qdf_spin_unlock_bh(&pdev->last_real_peer_mutex); +} + +void ol_txrx_set_peer_as_tdls_peer(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t *peer_mac, bool val) +{ + ol_txrx_peer_handle peer; + struct ol_txrx_pdev_t *pdev; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev) + return; + + pdev = vdev->pdev; + peer = ol_txrx_find_peer_by_addr((struct cdp_pdev *)pdev, peer_mac); + + ol_txrx_info_high("peer %pK, peer->ref_cnt %d", + peer, qdf_atomic_read(&peer->ref_cnt)); + + /* Mark peer as tdls */ + if (peer) + peer->is_tdls_peer = val; +} + +void ol_txrx_set_tdls_offchan_enabled(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t *peer_mac, bool val) +{ + ol_txrx_peer_handle peer; + struct ol_txrx_pdev_t *pdev; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev) + return; + + pdev = vdev->pdev; + peer = ol_txrx_find_peer_by_addr((struct cdp_pdev *)pdev, peer_mac); + + ol_txrx_info_high("peer %pK, peer->ref_cnt %d", + peer, qdf_atomic_read(&peer->ref_cnt)); + + /* Set TDLS Offchan operation enable/disable */ + if (peer && peer->is_tdls_peer) + peer->tdls_offchan_enabled = val; +} +#endif + +#if defined(CONFIG_HL_SUPPORT) && defined(DEBUG_HL_LOGGING) +/** + * ol_txrx_pdev_txq_log_init() - initialise pdev txq logs + * @pdev: the physical device object + * + * Return: None + */ +void ol_txrx_pdev_txq_log_init(struct ol_txrx_pdev_t *pdev) +{ + qdf_spinlock_create(&pdev->txq_log_spinlock); + pdev->txq_log.size = OL_TXQ_LOG_SIZE; + pdev->txq_log.oldest_record_offset = 0; + pdev->txq_log.offset = 0; + pdev->txq_log.allow_wrap = 1; + pdev->txq_log.wrapped = 0; +} + +/** + * ol_txrx_pdev_txq_log_destroy() - remove txq log spinlock for pdev + * @pdev: the physical device object + * + * Return: None + */ +void ol_txrx_pdev_txq_log_destroy(struct ol_txrx_pdev_t *pdev) +{ + qdf_spinlock_destroy(&pdev->txq_log_spinlock); +} +#endif + +#if defined(DEBUG_HL_LOGGING) + +/** + * ol_txrx_pdev_grp_stats_init() - initialise group stat spinlock for pdev + * @pdev: the physical device object + * + * Return: None + */ +void ol_txrx_pdev_grp_stats_init(struct ol_txrx_pdev_t *pdev) +{ + qdf_spinlock_create(&pdev->grp_stat_spinlock); + pdev->grp_stats.last_valid_index = -1; + pdev->grp_stats.wrap_around = 0; +} + +/** + * ol_txrx_pdev_grp_stat_destroy() - destroy group stat spinlock for pdev + * @pdev: the physical device object + * + * Return: None + */ +void ol_txrx_pdev_grp_stat_destroy(struct ol_txrx_pdev_t *pdev) +{ + qdf_spinlock_destroy(&pdev->grp_stat_spinlock); +} +#endif + +#if defined(CONFIG_HL_SUPPORT) && defined(FEATURE_WLAN_TDLS) + +/** + * ol_txrx_hl_tdls_flag_reset() - reset tdls flag for vdev + * @soc_hdl: Datapath soc handle + * @vdev_id: id of vdev + * @flag: flag + * + * Return: None + */ +void +ol_txrx_hl_tdls_flag_reset(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + bool flag) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid vdev_id %d", __func__, vdev_id); + return; + } + + vdev->hlTdlsFlag = flag; +} +#endif + +/** + * ol_txrx_vdev_txqs_init() - initialise vdev tx queues + * @vdev: the virtual device object + * + * Return: None + */ +void ol_txrx_vdev_txqs_init(struct ol_txrx_vdev_t *vdev) +{ + uint8_t i; + + for (i = 0; i < OL_TX_VDEV_NUM_QUEUES; i++) { + TAILQ_INIT(&vdev->txqs[i].head); + vdev->txqs[i].paused_count.total = 0; + vdev->txqs[i].frms = 0; + vdev->txqs[i].bytes = 0; + vdev->txqs[i].ext_tid = OL_TX_NUM_TIDS + i; + vdev->txqs[i].flag = ol_tx_queue_empty; + /* aggregation is not applicable for vdev tx queues */ + vdev->txqs[i].aggr_state = ol_tx_aggr_disabled; + ol_tx_txq_set_group_ptr(&vdev->txqs[i], NULL); + ol_txrx_set_txq_peer(&vdev->txqs[i], NULL); + } +} + +/** + * ol_txrx_vdev_tx_queue_free() - free vdev tx queues + * @vdev: the virtual device object + * + * Return: None + */ +void ol_txrx_vdev_tx_queue_free(struct ol_txrx_vdev_t *vdev) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + struct ol_tx_frms_queue_t *txq; + int i; + + for (i = 0; i < OL_TX_VDEV_NUM_QUEUES; i++) { + txq = &vdev->txqs[i]; + ol_tx_queue_free(pdev, txq, (i + OL_TX_NUM_TIDS), false); + } +} + +/** + * ol_txrx_peer_txqs_init() - initialise peer tx queues + * @pdev: the physical device object + * @peer: peer object + * + * Return: None + */ +void ol_txrx_peer_txqs_init(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + uint8_t i; + struct ol_txrx_vdev_t *vdev = peer->vdev; + + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + for (i = 0; i < OL_TX_NUM_TIDS; i++) { + TAILQ_INIT(&peer->txqs[i].head); + peer->txqs[i].paused_count.total = 0; + peer->txqs[i].frms = 0; + peer->txqs[i].bytes = 0; + peer->txqs[i].ext_tid = i; + peer->txqs[i].flag = ol_tx_queue_empty; + peer->txqs[i].aggr_state = ol_tx_aggr_untried; + ol_tx_set_peer_group_ptr(pdev, peer, vdev->vdev_id, i); + ol_txrx_set_txq_peer(&peer->txqs[i], peer); + } + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + + /* aggregation is not applicable for mgmt and non-QoS tx queues */ + for (i = OL_TX_NUM_QOS_TIDS; i < OL_TX_NUM_TIDS; i++) + peer->txqs[i].aggr_state = ol_tx_aggr_disabled; + + ol_txrx_peer_pause(peer); +} + +/** + * ol_txrx_peer_tx_queue_free() - free peer tx queues + * @pdev: the physical device object + * @peer: peer object + * + * Return: None + */ +void ol_txrx_peer_tx_queue_free(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + struct ol_tx_frms_queue_t *txq; + uint8_t i; + + for (i = 0; i < OL_TX_NUM_TIDS; i++) { + txq = &peer->txqs[i]; + ol_tx_queue_free(pdev, txq, i, true); + } +} + +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + +/** + * ol_txrx_update_group_credit() - update group credit for tx queue + * @group: for which credit needs to be updated + * @credit: credits + * @absolute: TXQ group absolute + * + * Return: allocated pool size + */ +void ol_txrx_update_group_credit( + struct ol_tx_queue_group_t *group, + int32_t credit, + u_int8_t absolute) +{ + if (absolute) + qdf_atomic_set(&group->credit, credit); + else + qdf_atomic_add(credit, &group->credit); +} + +/** + * ol_txrx_update_tx_queue_groups() - update vdev tx queue group if + * vdev id mask and ac mask is not matching + * @pdev: the data physical device + * @group_id: TXQ group id + * @credit: TXQ group credit count + * @absolute: TXQ group absolute + * @vdev_id_mask: TXQ vdev group id mask + * @ac_mask: TQX access category mask + * + * Return: None + */ +void ol_txrx_update_tx_queue_groups( + ol_txrx_pdev_handle pdev, + u_int8_t group_id, + int32_t credit, + u_int8_t absolute, + u_int32_t vdev_id_mask, + u_int32_t ac_mask + ) +{ + struct ol_tx_queue_group_t *group; + u_int32_t group_vdev_bit_mask, vdev_bit_mask, group_vdev_id_mask; + u_int32_t membership; + struct ol_txrx_vdev_t *vdev; + + if (group_id >= OL_TX_MAX_TXQ_GROUPS) { + ol_txrx_warn("invalid group_id=%u, ignore update", group_id); + return; + } + + group = &pdev->txq_grps[group_id]; + + membership = OL_TXQ_GROUP_MEMBERSHIP_GET(vdev_id_mask, ac_mask); + + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + /* + * if the membership (vdev id mask and ac mask) + * matches then no need to update tx qeue groups. + */ + if (group->membership == membership) + /* Update Credit Only */ + goto credit_update; + + credit += ol_txrx_distribute_group_credits(pdev, group_id, + vdev_id_mask); + /* + * membership (vdev id mask and ac mask) is not matching + * TODO: ignoring ac mask for now + */ + qdf_assert(ac_mask == 0xffff); + group_vdev_id_mask = + OL_TXQ_GROUP_VDEV_ID_MASK_GET(group->membership); + + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + group_vdev_bit_mask = + OL_TXQ_GROUP_VDEV_ID_BIT_MASK_GET( + group_vdev_id_mask, vdev->vdev_id); + vdev_bit_mask = + OL_TXQ_GROUP_VDEV_ID_BIT_MASK_GET( + vdev_id_mask, vdev->vdev_id); + + if (group_vdev_bit_mask != vdev_bit_mask) { + /* + * Change in vdev tx queue group + */ + if (!vdev_bit_mask) { + /* Set Group Pointer (vdev and peer) to NULL */ + ol_txrx_info("Group membership removed for vdev_id %d from group_id %d", + vdev->vdev_id, group_id); + ol_tx_set_vdev_group_ptr( + pdev, vdev->vdev_id, NULL); + } else { + /* Set Group Pointer (vdev and peer) */ + ol_txrx_info("Group membership updated for vdev_id %d to group_id %d", + vdev->vdev_id, group_id); + ol_tx_set_vdev_group_ptr( + pdev, vdev->vdev_id, group); + } + } + } + /* Update membership */ + group->membership = membership; + ol_txrx_info("Group membership updated for group_id %d membership 0x%x", + group_id, group->membership); +credit_update: + /* Update Credit */ + ol_txrx_update_group_credit(group, credit, absolute); + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); +} +#endif + +#if defined(FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL) && \ + defined(FEATURE_HL_DBS_GROUP_CREDIT_SHARING) +#define MIN_INIT_GROUP_CREDITS 10 +int ol_txrx_distribute_group_credits(struct ol_txrx_pdev_t *pdev, + u8 group_id, + u32 vdevid_mask_new) +{ + struct ol_tx_queue_group_t *grp = &pdev->txq_grps[group_id]; + struct ol_tx_queue_group_t *grp_nxt = &pdev->txq_grps[!group_id]; + int creds_nxt = qdf_atomic_read(&grp_nxt->credit); + int vdevid_mask = OL_TXQ_GROUP_VDEV_ID_MASK_GET(grp->membership); + int vdevid_mask_othgrp = + OL_TXQ_GROUP_VDEV_ID_MASK_GET(grp_nxt->membership); + int creds_distribute = 0; + + /* if vdev added to the group is the first vdev */ + if ((vdevid_mask == 0) && (vdevid_mask_new != 0)) { + /* if other group has members */ + if (vdevid_mask_othgrp) { + if (creds_nxt < MIN_INIT_GROUP_CREDITS) + creds_distribute = creds_nxt / 2; + else + creds_distribute = MIN_INIT_GROUP_CREDITS; + + ol_txrx_update_group_credit(grp_nxt, -creds_distribute, + 0); + } else { + /* + * Other grp has no members, give all credits to this + * grp. + */ + creds_distribute = + qdf_atomic_read(&pdev->target_tx_credit); + } + /* if all vdevs are removed from this grp */ + } else if ((vdevid_mask != 0) && (vdevid_mask_new == 0)) { + if (vdevid_mask_othgrp) + /* Transfer credits to other grp */ + ol_txrx_update_group_credit(grp_nxt, + qdf_atomic_read(&grp-> + credit), + 0); + /* Set current grp credits to zero */ + ol_txrx_update_group_credit(grp, 0, 1); + } + + return creds_distribute; +} +#endif /* + * FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL && + * FEATURE_HL_DBS_GROUP_CREDIT_SHARING + */ + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +int ol_txrx_register_hl_flow_control(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, + tx_pause_callback flowcontrol) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + u32 desc_pool_size; + + if (!pdev || !flowcontrol) { + ol_txrx_err("pdev or pause_cb is NULL"); + return QDF_STATUS_E_INVAL; + } + + desc_pool_size = ol_tx_desc_pool_size_hl(pdev->ctrl_pdev); + /* + * Assert if the tx descriptor pool size meets the requirements + * Maximum 2 sessions are allowed on a band. + */ + QDF_ASSERT((2 * ol_txrx_tx_desc_alloc_table[TXRX_FC_5GH_80M_2x2] + + ol_txrx_tx_desc_alloc_table[TXRX_FC_2GH_40M_2x2]) + <= desc_pool_size); + + pdev->pause_cb = flowcontrol; + return 0; +} + +int ol_txrx_set_vdev_os_queue_status(struct cdp_soc_t *soc_hdl, u8 vdev_id, + enum netif_action_type action) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid vdev_id %d", __func__, vdev_id); + return -EINVAL; + } + + switch (action) { + case WLAN_NETIF_PRIORITY_QUEUE_ON: + qdf_spin_lock_bh(&vdev->pdev->tx_mutex); + vdev->prio_q_paused = 0; + qdf_spin_unlock_bh(&vdev->pdev->tx_mutex); + break; + case WLAN_WAKE_NON_PRIORITY_QUEUE: + qdf_atomic_set(&vdev->os_q_paused, 0); + break; + default: + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid action %d", __func__, action); + return -EINVAL; + } + return 0; +} + +int ol_txrx_set_vdev_tx_desc_limit(struct cdp_soc_t *soc_hdl, u8 vdev_id, + u32 chan_freq) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + enum ol_txrx_fc_limit_id fc_limit_id; + u32 td_limit; + + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid vdev_id %d", __func__, vdev_id); + return -EINVAL; + } + + /* TODO: Handle no of spatial streams and channel BW */ + if (WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq)) + fc_limit_id = TXRX_FC_5GH_80M_2x2; + else + fc_limit_id = TXRX_FC_2GH_40M_2x2; + + qdf_spin_lock_bh(&vdev->pdev->tx_mutex); + td_limit = ol_txrx_tx_desc_alloc_table[fc_limit_id]; + vdev->tx_desc_limit = td_limit; + vdev->queue_stop_th = td_limit - TXRX_HL_TX_DESC_HI_PRIO_RESERVED; + vdev->queue_restart_th = td_limit - TXRX_HL_TX_DESC_QUEUE_RESTART_TH; + qdf_spin_unlock_bh(&vdev->pdev->tx_mutex); + + return 0; +} + +void ol_tx_dump_flow_pool_info_compact(struct ol_txrx_pdev_t *pdev) +{ + char *comb_log_str; + int bytes_written = 0; + uint32_t free_size; + struct ol_txrx_vdev_t *vdev; + int i = 0; + + free_size = WLAN_MAX_VDEVS * 100; + comb_log_str = qdf_mem_malloc(free_size); + if (!comb_log_str) + return; + + qdf_spin_lock_bh(&pdev->tx_mutex); + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + bytes_written += snprintf(&comb_log_str[bytes_written], + free_size, "%d (%d,%d)(%d,%d)(%d,%d) |", + vdev->vdev_id, vdev->tx_desc_limit, + qdf_atomic_read(&vdev->tx_desc_count), + qdf_atomic_read(&vdev->os_q_paused), + vdev->prio_q_paused, vdev->queue_stop_th, + vdev->queue_restart_th); + free_size -= bytes_written; + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + qdf_nofl_debug("STATS | FC: %s", comb_log_str); + + free_size = WLAN_MAX_VDEVS * 100; + bytes_written = 0; + qdf_mem_zero(comb_log_str, free_size); + + bytes_written = snprintf(&comb_log_str[bytes_written], free_size, + "%d ", + qdf_atomic_read(&pdev->target_tx_credit)); + for (i = 0; i < OL_TX_MAX_TXQ_GROUPS; i++) { + bytes_written += snprintf(&comb_log_str[bytes_written], + free_size, "|%d, (0x%x, %d)", i, + OL_TXQ_GROUP_VDEV_ID_MASK_GET( + pdev->txq_grps[i].membership), + qdf_atomic_read( + &pdev->txq_grps[i].credit)); + free_size -= bytes_written; + } + qdf_nofl_debug("STATS | CREDIT: %s", comb_log_str); + qdf_mem_free(comb_log_str); +} + +void ol_tx_dump_flow_pool_info(struct cdp_soc_t *soc_hdl) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev; + struct ol_txrx_vdev_t *vdev; + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + qdf_spin_lock_bh(&pdev->tx_mutex); + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + txrx_nofl_info("vdev_id %d", vdev->vdev_id); + txrx_nofl_info("limit %d available %d stop_threshold %d restart_threshold %d", + vdev->tx_desc_limit, + qdf_atomic_read(&vdev->tx_desc_count), + vdev->queue_stop_th, vdev->queue_restart_th); + txrx_nofl_info("q_paused %d prio_q_paused %d", + qdf_atomic_read(&vdev->os_q_paused), + vdev->prio_q_paused); + txrx_nofl_info("no_of_bundle_sent_after_threshold %lld", + vdev->no_of_bundle_sent_after_threshold); + txrx_nofl_info("no_of_bundle_sent_in_timer %lld", + vdev->no_of_bundle_sent_in_timer); + txrx_nofl_info("no_of_pkt_not_added_in_queue %lld", + vdev->no_of_pkt_not_added_in_queue); + } + qdf_spin_unlock_bh(&pdev->tx_mutex); +} +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_ll.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_ll.c new file mode 100644 index 0000000000000000000000000000000000000000..6d697ce331f1e5213c4208a1657b816e27c220f5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_ll.c @@ -0,0 +1,780 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_atomic_inc, etc. */ +#include /* qdf_os_spinlock */ +#include /* qdf_system_ticks, etc. */ +#include /* qdf_nbuf_t */ +#include /* QDF_NBUF_TX_EXT_TID_INVALID */ + +#include "queue.h" /* TAILQ */ +#ifdef QCA_COMPUTE_TX_DELAY +#include /* ethernet_hdr_t, etc. */ +#include /* ipv6_traffic_class */ +#endif + +#include /* ol_txrx_vdev_handle, etc. */ +#include /* htt_tx_compl_desc_id */ +#include /* htt_tx_status */ + +#include +#include +#include /* ol_txrx_vdev_t, etc */ +#include /* ol_tx_desc_find, ol_tx_desc_frame_free */ +#ifdef QCA_COMPUTE_TX_DELAY +#include /* ol_tx_dest_addr_find */ +#endif +#include /* OL_TX_DESC_NO_REFS, etc. */ +#include +#include /* ol_tx_reinject */ +#include + +#include /* ol_cfg_is_high_latency */ +#include +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP +#include /* OL_TX_RESTORE_HDR, etc */ +#endif +#include +#include +#include +#include + +void ol_tx_init_pdev(ol_txrx_pdev_handle pdev) +{ + qdf_atomic_add(ol_cfg_target_tx_credit(pdev->ctrl_pdev), + &pdev->target_tx_credit); +} + +qdf_nbuf_t ol_tx_reinject(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, uint16_t peer_id) +{ + struct ol_tx_desc_t *tx_desc = NULL; + struct ol_txrx_msdu_info_t msdu_info; + + msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type; + msdu_info.htt.info.ext_tid = HTT_TX_EXT_TID_INVALID; + msdu_info.peer = NULL; + msdu_info.htt.action.tx_comp_req = 0; + msdu_info.tso_info.is_tso = 0; + + tx_desc = ol_tx_prepare_ll(vdev, msdu, &msdu_info); + if (!tx_desc) + return msdu; + + HTT_TX_DESC_POSTPONED_SET(*((uint32_t *)(tx_desc->htt_tx_desc)), true); + + htt_tx_desc_set_peer_id(tx_desc->htt_tx_desc, peer_id); + + ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id); + + return NULL; +} + +/* + * The TXRX module doesn't accept tx frames unless the target has + * enough descriptors for them. + * For LL, the TXRX descriptor pool is sized to match the target's + * descriptor pool. Hence, if the descriptor allocation in TXRX + * succeeds, that guarantees that the target has room to accept + * the new tx frame. + */ +struct ol_tx_desc_t * +ol_tx_prepare_ll(ol_txrx_vdev_handle vdev, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ol_tx_desc_t *tx_desc; + struct ol_txrx_pdev_t *pdev = vdev->pdev; + + (msdu_info)->htt.info.frame_type = pdev->htt_pkt_type; + tx_desc = ol_tx_desc_ll(pdev, vdev, msdu, msdu_info); + if (qdf_unlikely(!tx_desc)) { + /* + * If TSO packet, free associated + * remaining TSO segment descriptors + */ + if (qdf_nbuf_is_tso(msdu)) + ol_free_remaining_tso_segs( + vdev, msdu_info, true); + TXRX_STATS_MSDU_LIST_INCR( + pdev, tx.dropped.host_reject, msdu); + return NULL; + } + + return tx_desc; +} + +qdf_nbuf_t +ol_tx_non_std_ll(struct ol_txrx_vdev_t *vdev, + enum ol_tx_spec tx_spec, + qdf_nbuf_t msdu_list) +{ + qdf_nbuf_t msdu = msdu_list; + htt_pdev_handle htt_pdev = vdev->pdev->htt_pdev; + struct ol_txrx_msdu_info_t msdu_info; + + msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type; + msdu_info.htt.action.tx_comp_req = 0; + + /* + * The msdu_list variable could be used instead of the msdu var, + * but just to clarify which operations are done on a single MSDU + * vs. a list of MSDUs, use a distinct variable for single MSDUs + * within the list. + */ + while (msdu) { + qdf_nbuf_t next; + struct ol_tx_desc_t *tx_desc = NULL; + + msdu_info.htt.info.ext_tid = qdf_nbuf_get_tid(msdu); + msdu_info.peer = NULL; + msdu_info.tso_info.is_tso = 0; + + tx_desc = ol_tx_prepare_ll(vdev, msdu, &msdu_info); + if (!tx_desc) + return msdu; + + /* + * The netbuf may get linked into a different list inside the + * ol_tx_send function, so store the next pointer before the + * tx_send call. + */ + next = qdf_nbuf_next(msdu); + + if (tx_spec != OL_TX_SPEC_STD) { + if (tx_spec & OL_TX_SPEC_NO_FREE) { + tx_desc->pkt_type = OL_TX_FRM_NO_FREE; + } else if (tx_spec & OL_TX_SPEC_TSO) { + tx_desc->pkt_type = OL_TX_FRM_TSO; + } else if (tx_spec & OL_TX_SPEC_NWIFI_NO_ENCRYPT) { + uint8_t sub_type = + ol_txrx_tx_raw_subtype(tx_spec); + htt_tx_desc_type(htt_pdev, tx_desc->htt_tx_desc, + htt_pkt_type_native_wifi, + sub_type); + } else if (ol_txrx_tx_is_raw(tx_spec)) { + /* different types of raw frames */ + uint8_t sub_type = + ol_txrx_tx_raw_subtype(tx_spec); + htt_tx_desc_type(htt_pdev, tx_desc->htt_tx_desc, + htt_pkt_type_raw, sub_type); + } + } + /* + * If debug display is enabled, show the meta-data being + * downloaded to the target via the HTT tx descriptor. + */ + htt_tx_desc_display(tx_desc->htt_tx_desc); + ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id); + msdu = next; + } + return NULL; /* all MSDUs were accepted */ +} + +#if defined(HELIUMPLUS) +void ol_txrx_dump_frag_desc(char *msg, struct ol_tx_desc_t *tx_desc) +{ + uint32_t *frag_ptr_i_p; + int i; + + ol_txrx_err("OL TX Descriptor 0x%pK msdu_id %d\n", + tx_desc, tx_desc->id); + ol_txrx_err("HTT TX Descriptor vaddr: 0x%pK paddr: %pad", + tx_desc->htt_tx_desc, &tx_desc->htt_tx_desc_paddr); + ol_txrx_err("Fragment Descriptor 0x%pK (paddr=%pad)", + tx_desc->htt_frag_desc, &tx_desc->htt_frag_desc_paddr); + + /* + * it looks from htt_tx_desc_frag() that tx_desc->htt_frag_desc + * is already de-referrable (=> in virtual address space) + */ + frag_ptr_i_p = tx_desc->htt_frag_desc; + + /* Dump 6 words of TSO flags */ + print_hex_dump(KERN_DEBUG, "MLE Desc:TSO Flags: ", + DUMP_PREFIX_NONE, 8, 4, + frag_ptr_i_p, 24, true); + + frag_ptr_i_p += 6; /* Skip 6 words of TSO flags */ + + i = 0; + while (*frag_ptr_i_p) { + print_hex_dump(KERN_DEBUG, "MLE Desc:Frag Ptr: ", + DUMP_PREFIX_NONE, 8, 4, + frag_ptr_i_p, 8, true); + i++; + if (i > 5) /* max 6 times: frag_ptr0 to frag_ptr5 */ + break; + /* jump to next pointer - skip length */ + frag_ptr_i_p += 2; + } +} +#endif /* HELIUMPLUS */ + +struct ol_tx_desc_t * +ol_txrx_mgmt_tx_desc_alloc( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t tx_mgmt_frm, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + struct ol_tx_desc_t *tx_desc; + + /* For LL tx_comp_req is not used so initialized to 0 */ + tx_msdu_info->htt.action.tx_comp_req = 0; + tx_desc = ol_tx_desc_ll(pdev, vdev, tx_mgmt_frm, tx_msdu_info); + /* FIX THIS - + * The FW currently has trouble using the host's fragments table + * for management frames. Until this is fixed, rather than + * specifying the fragment table to the FW, specify just the + * address of the initial fragment. + */ +#if defined(HELIUMPLUS) + /* ol_txrx_dump_frag_desc("ol_txrx_mgmt_send(): after ol_tx_desc_ll", + * tx_desc); + */ +#endif /* defined(HELIUMPLUS) */ + if (tx_desc) { + /* + * Following the call to ol_tx_desc_ll, frag 0 is the + * HTT tx HW descriptor, and the frame payload is in + * frag 1. + */ + htt_tx_desc_frags_table_set( + pdev->htt_pdev, + tx_desc->htt_tx_desc, + qdf_nbuf_get_frag_paddr(tx_mgmt_frm, 1), + 0, 0); +#if defined(HELIUMPLUS) && defined(HELIUMPLUS_DEBUG) + ol_txrx_dump_frag_desc( + "after htt_tx_desc_frags_table_set", + tx_desc); +#endif /* defined(HELIUMPLUS) */ + } + + return tx_desc; +} + +int ol_txrx_mgmt_send_frame( + struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t tx_mgmt_frm, + struct ol_txrx_msdu_info_t *tx_msdu_info, + uint16_t chanfreq) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + + htt_tx_desc_set_chanfreq(tx_desc->htt_tx_desc, chanfreq); + QDF_NBUF_CB_TX_PACKET_TRACK(tx_desc->netbuf) = + QDF_NBUF_TX_PKT_MGMT_TRACK; + ol_tx_send_nonstd(pdev, tx_desc, tx_mgmt_frm, + htt_pkt_type_mgmt); + + return 0; +} + +#if defined(FEATURE_TSO) +void ol_free_remaining_tso_segs(ol_txrx_vdev_handle vdev, + struct ol_txrx_msdu_info_t *msdu_info, + bool is_tso_seg_mapping_done) +{ + struct qdf_tso_seg_elem_t *next_seg; + struct qdf_tso_seg_elem_t *free_seg = msdu_info->tso_info.curr_seg; + struct ol_txrx_pdev_t *pdev; + bool is_last_seg = false; + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is null"); + return; + } + + pdev = vdev->pdev; + if (qdf_unlikely(!pdev)) { + ol_txrx_err("pdev is null"); + return; + } + + /* + * TSO segment are mapped already, therefore, + * 1. unmap the tso segments, + * 2. free tso num segment if it is a last segment, and + * 3. free the tso segments. + */ + + if (is_tso_seg_mapping_done) { + struct qdf_tso_num_seg_elem_t *tso_num_desc = + msdu_info->tso_info.tso_num_seg_list; + + if (qdf_unlikely(!tso_num_desc)) { + ol_txrx_err("TSO common info is NULL!"); + return; + } + + while (free_seg) { + qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex); + tso_num_desc->num_seg.tso_cmn_num_seg--; + + is_last_seg = (tso_num_desc->num_seg.tso_cmn_num_seg == + 0) ? true : false; + qdf_nbuf_unmap_tso_segment(pdev->osdev, free_seg, + is_last_seg); + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + + if (is_last_seg) { + ol_tso_num_seg_free(pdev, + msdu_info->tso_info. + tso_num_seg_list); + msdu_info->tso_info.tso_num_seg_list = NULL; + } + + next_seg = free_seg->next; + free_seg->force_free = 1; + ol_tso_free_segment(pdev, free_seg); + free_seg = next_seg; + } + } else { + /* + * TSO segment are not mapped therefore, + * free the tso segments only. + */ + while (free_seg) { + next_seg = free_seg->next; + free_seg->force_free = 1; + ol_tso_free_segment(pdev, free_seg); + free_seg = next_seg; + } + } +} + +/** + * ol_tx_prepare_tso() - Given a jumbo msdu, prepare the TSO + * related information in the msdu_info meta data + * @vdev: virtual device handle + * @msdu: network buffer + * @msdu_info: meta data associated with the msdu + * + * Return: 0 - success, >0 - error + */ +uint8_t ol_tx_prepare_tso(ol_txrx_vdev_handle vdev, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info) +{ + msdu_info->tso_info.curr_seg = NULL; + if (qdf_nbuf_is_tso(msdu)) { + int num_seg = qdf_nbuf_get_tso_num_seg(msdu); + struct qdf_tso_num_seg_elem_t *tso_num_seg; + + msdu_info->tso_info.tso_num_seg_list = NULL; + msdu_info->tso_info.tso_seg_list = NULL; + msdu_info->tso_info.num_segs = num_seg; + while (num_seg) { + struct qdf_tso_seg_elem_t *tso_seg = + ol_tso_alloc_segment(vdev->pdev); + if (tso_seg) { + qdf_tso_seg_dbg_record(tso_seg, + TSOSEG_LOC_PREPARETSO); + tso_seg->next = + msdu_info->tso_info.tso_seg_list; + msdu_info->tso_info.tso_seg_list + = tso_seg; + num_seg--; + } else { + /* Free above alocated TSO segements till now */ + msdu_info->tso_info.curr_seg = + msdu_info->tso_info.tso_seg_list; + ol_free_remaining_tso_segs(vdev, msdu_info, + false); + return 1; + } + } + tso_num_seg = ol_tso_num_seg_alloc(vdev->pdev); + if (tso_num_seg) { + tso_num_seg->next = msdu_info->tso_info. + tso_num_seg_list; + msdu_info->tso_info.tso_num_seg_list = tso_num_seg; + } else { + /* Free the already allocated num of segments */ + msdu_info->tso_info.curr_seg = + msdu_info->tso_info.tso_seg_list; + ol_free_remaining_tso_segs(vdev, msdu_info, false); + return 1; + } + + if (qdf_unlikely(!qdf_nbuf_get_tso_info(vdev->pdev->osdev, + msdu, &msdu_info->tso_info))) { + /* Free the already allocated num of segments */ + msdu_info->tso_info.curr_seg = + msdu_info->tso_info.tso_seg_list; + ol_free_remaining_tso_segs(vdev, msdu_info, false); + return 1; + } + + msdu_info->tso_info.curr_seg = + msdu_info->tso_info.tso_seg_list; + num_seg = msdu_info->tso_info.num_segs; + } else { + msdu_info->tso_info.is_tso = 0; + msdu_info->tso_info.num_segs = 1; + } + return 0; +} + +/** + * ol_tx_tso_update_stats() - update TSO stats + * @pdev: pointer to ol_txrx_pdev_t structure + * @msdu_info: tso msdu_info for the msdu + * @msdu: tso mdsu for which stats are updated + * @tso_msdu_idx: stats index in the global TSO stats array where stats will be + * updated + * + * Return: None + */ +void ol_tx_tso_update_stats(struct ol_txrx_pdev_t *pdev, + struct qdf_tso_info_t *tso_info, qdf_nbuf_t msdu, + uint32_t tso_msdu_idx) +{ + TXRX_STATS_TSO_HISTOGRAM(pdev, tso_info->num_segs); + TXRX_STATS_TSO_GSO_SIZE_UPDATE(pdev, tso_msdu_idx, + qdf_nbuf_tcp_tso_size(msdu)); + TXRX_STATS_TSO_TOTAL_LEN_UPDATE(pdev, + tso_msdu_idx, qdf_nbuf_len(msdu)); + TXRX_STATS_TSO_NUM_FRAGS_UPDATE(pdev, tso_msdu_idx, + qdf_nbuf_get_nr_frags(msdu)); +} + +/** + * ol_tx_tso_get_stats_idx() - retrieve global TSO stats index and increment it + * @pdev: pointer to ol_txrx_pdev_t structure + * + * Retrieve the current value of the global variable and increment it. This is + * done in a spinlock as the global TSO stats may be accessed in parallel by + * multiple TX streams. + * + * Return: The current value of TSO stats index. + */ +uint32_t ol_tx_tso_get_stats_idx(struct ol_txrx_pdev_t *pdev) +{ + uint32_t msdu_stats_idx = 0; + + qdf_spin_lock_bh(&pdev->stats.pub.tx.tso.tso_stats_lock); + msdu_stats_idx = pdev->stats.pub.tx.tso.tso_info.tso_msdu_idx; + pdev->stats.pub.tx.tso.tso_info.tso_msdu_idx++; + pdev->stats.pub.tx.tso.tso_info.tso_msdu_idx &= + NUM_MAX_TSO_MSDUS_MASK; + qdf_spin_unlock_bh(&pdev->stats.pub.tx.tso.tso_stats_lock); + + TXRX_STATS_TSO_RESET_MSDU(pdev, msdu_stats_idx); + + return msdu_stats_idx; +} + +/** + * ol_tso_seg_list_init() - function to initialise the tso seg freelist + * @pdev: the data physical device sending the data + * @num_seg: number of segments needs to be intialised + * + * Return: none + */ +void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg) +{ + int i = 0; + struct qdf_tso_seg_elem_t *c_element; + + /* Host should not allocate any c_element. */ + if (num_seg <= 0) { + ol_txrx_err("Pool size passed is 0"); + QDF_BUG(0); + pdev->tso_seg_pool.pool_size = i; + qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex); + return; + } + + c_element = qdf_mem_malloc(sizeof(struct qdf_tso_seg_elem_t)); + pdev->tso_seg_pool.freelist = c_element; + for (i = 0; i < (num_seg - 1); i++) { + if (qdf_unlikely(!c_element)) { + ol_txrx_err("c_element NULL for seg %d", i); + QDF_BUG(0); + pdev->tso_seg_pool.pool_size = i; + pdev->tso_seg_pool.num_free = i; + qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex); + return; + } + /* set the freelist bit and magic cookie*/ + c_element->on_freelist = 1; + c_element->cookie = TSO_SEG_MAGIC_COOKIE; +#ifdef TSOSEG_DEBUG + c_element->dbg.txdesc = NULL; + qdf_atomic_init(&c_element->dbg.cur); /* history empty */ + qdf_tso_seg_dbg_record(c_element, TSOSEG_LOC_INIT1); +#endif /* TSOSEG_DEBUG */ + c_element->next = + qdf_mem_malloc(sizeof(struct qdf_tso_seg_elem_t)); + c_element = c_element->next; + } + /* + * NULL check for the last c_element of the list or + * first c_element if num_seg is equal to 1. + */ + if (qdf_unlikely(!c_element)) { + ol_txrx_err("c_element NULL for seg %d", i); + QDF_BUG(0); + pdev->tso_seg_pool.pool_size = i; + pdev->tso_seg_pool.num_free = i; + qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex); + return; + } + c_element->on_freelist = 1; + c_element->cookie = TSO_SEG_MAGIC_COOKIE; +#ifdef TSOSEG_DEBUG + qdf_tso_seg_dbg_init(c_element); + qdf_tso_seg_dbg_record(c_element, TSOSEG_LOC_INIT2); +#endif /* TSOSEG_DEBUG */ + c_element->next = NULL; + pdev->tso_seg_pool.pool_size = num_seg; + pdev->tso_seg_pool.num_free = num_seg; + qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex); +} + +/** + * ol_tso_seg_list_deinit() - function to de-initialise the tso seg freelist + * @pdev: the data physical device sending the data + * + * Return: none + */ +void ol_tso_seg_list_deinit(struct ol_txrx_pdev_t *pdev) +{ + int i; + struct qdf_tso_seg_elem_t *c_element; + struct qdf_tso_seg_elem_t *temp; + + /* pool size 0 implies that tso seg list is not initialised*/ + if (!pdev->tso_seg_pool.freelist && + pdev->tso_seg_pool.pool_size == 0) + return; + + qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex); + c_element = pdev->tso_seg_pool.freelist; + i = pdev->tso_seg_pool.pool_size; + + pdev->tso_seg_pool.freelist = NULL; + pdev->tso_seg_pool.num_free = 0; + pdev->tso_seg_pool.pool_size = 0; + + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_spinlock_destroy(&pdev->tso_seg_pool.tso_mutex); + + while (i-- > 0 && c_element) { + temp = c_element->next; + if (c_element->on_freelist != 1) { + qdf_tso_seg_dbg_bug("seg already freed (double?)"); + return; + } else if (c_element->cookie != TSO_SEG_MAGIC_COOKIE) { + qdf_tso_seg_dbg_bug("seg cookie is bad (corruption?)"); + return; + } + /* free this seg, so reset the cookie value*/ + c_element->cookie = 0; + qdf_mem_free(c_element); + c_element = temp; + } +} + +/** + * ol_tso_num_seg_list_init() - function to initialise the freelist of elements + * use to count the num of tso segments in jumbo + * skb packet freelist + * @pdev: the data physical device sending the data + * @num_seg: number of elements needs to be intialised + * + * Return: none + */ +void ol_tso_num_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg) +{ + int i = 0; + struct qdf_tso_num_seg_elem_t *c_element; + + /* Host should not allocate any c_element. */ + if (num_seg <= 0) { + ol_txrx_err("Pool size passed is 0"); + QDF_BUG(0); + pdev->tso_num_seg_pool.num_seg_pool_size = i; + qdf_spinlock_create(&pdev->tso_num_seg_pool.tso_num_seg_mutex); + return; + } + + c_element = qdf_mem_malloc(sizeof(struct qdf_tso_num_seg_elem_t)); + pdev->tso_num_seg_pool.freelist = c_element; + for (i = 0; i < (num_seg - 1); i++) { + if (qdf_unlikely(!c_element)) { + ol_txrx_err("c_element NULL for num of seg %d", i); + QDF_BUG(0); + pdev->tso_num_seg_pool.num_seg_pool_size = i; + pdev->tso_num_seg_pool.num_free = i; + qdf_spinlock_create(&pdev->tso_num_seg_pool. + tso_num_seg_mutex); + return; + } + c_element->next = + qdf_mem_malloc(sizeof(struct qdf_tso_num_seg_elem_t)); + c_element = c_element->next; + } + /* + * NULL check for the last c_element of the list or + * first c_element if num_seg is equal to 1. + */ + if (qdf_unlikely(!c_element)) { + ol_txrx_err("c_element NULL for num of seg %d", i); + QDF_BUG(0); + pdev->tso_num_seg_pool.num_seg_pool_size = i; + pdev->tso_num_seg_pool.num_free = i; + qdf_spinlock_create(&pdev->tso_num_seg_pool.tso_num_seg_mutex); + return; + } + c_element->next = NULL; + pdev->tso_num_seg_pool.num_seg_pool_size = num_seg; + pdev->tso_num_seg_pool.num_free = num_seg; + qdf_spinlock_create(&pdev->tso_num_seg_pool.tso_num_seg_mutex); +} + +/** + * ol_tso_num_seg_list_deinit() - function to de-initialise the freelist of + * elements use to count the num of tso segment + * in a jumbo skb packet freelist + * @pdev: the data physical device sending the data + * + * Return: none + */ +void ol_tso_num_seg_list_deinit(struct ol_txrx_pdev_t *pdev) +{ + int i; + struct qdf_tso_num_seg_elem_t *c_element; + struct qdf_tso_num_seg_elem_t *temp; + + /* pool size 0 implies that tso num seg list is not initialised*/ + if (!pdev->tso_num_seg_pool.freelist && + pdev->tso_num_seg_pool.num_seg_pool_size == 0) + return; + + qdf_spin_lock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex); + c_element = pdev->tso_num_seg_pool.freelist; + i = pdev->tso_num_seg_pool.num_seg_pool_size; + + pdev->tso_num_seg_pool.freelist = NULL; + pdev->tso_num_seg_pool.num_free = 0; + pdev->tso_num_seg_pool.num_seg_pool_size = 0; + + qdf_spin_unlock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex); + qdf_spinlock_destroy(&pdev->tso_num_seg_pool.tso_num_seg_mutex); + + while (i-- > 0 && c_element) { + temp = c_element->next; + qdf_mem_free(c_element); + c_element = temp; + } +} +#endif /* FEATURE_TSO */ + +#if defined(FEATURE_TSO) && defined(FEATURE_TSO_DEBUG) +void ol_txrx_tso_stats_init(ol_txrx_pdev_handle pdev) +{ + qdf_spinlock_create(&pdev->stats.pub.tx.tso.tso_stats_lock); +} + +void ol_txrx_tso_stats_deinit(ol_txrx_pdev_handle pdev) +{ + qdf_spinlock_destroy(&pdev->stats.pub.tx.tso.tso_stats_lock); +} + +void ol_txrx_stats_display_tso(ol_txrx_pdev_handle pdev) +{ + int msdu_idx; + int seg_idx; + + txrx_nofl_info("TSO Statistics:"); + txrx_nofl_info("TSO pkts %lld, bytes %lld\n", + pdev->stats.pub.tx.tso.tso_pkts.pkts, + pdev->stats.pub.tx.tso.tso_pkts.bytes); + + txrx_nofl_info("TSO Histogram for numbers of segments:\n" + "Single segment %d\n" + " 2-5 segments %d\n" + " 6-10 segments %d\n" + "11-15 segments %d\n" + "16-20 segments %d\n" + " 20+ segments %d\n", + pdev->stats.pub.tx.tso.tso_hist.pkts_1, + pdev->stats.pub.tx.tso.tso_hist.pkts_2_5, + pdev->stats.pub.tx.tso.tso_hist.pkts_6_10, + pdev->stats.pub.tx.tso.tso_hist.pkts_11_15, + pdev->stats.pub.tx.tso.tso_hist.pkts_16_20, + pdev->stats.pub.tx.tso.tso_hist.pkts_20_plus); + + txrx_nofl_info("TSO History Buffer: Total size %d, current_index %d", + NUM_MAX_TSO_MSDUS, + TXRX_STATS_TSO_MSDU_IDX(pdev)); + + for (msdu_idx = 0; msdu_idx < NUM_MAX_TSO_MSDUS; msdu_idx++) { + if (TXRX_STATS_TSO_MSDU_TOTAL_LEN(pdev, msdu_idx) == 0) + continue; + txrx_nofl_info("jumbo pkt idx: %d num segs %d gso_len %d total_len %d nr_frags %d", + msdu_idx, + TXRX_STATS_TSO_MSDU_NUM_SEG(pdev, msdu_idx), + TXRX_STATS_TSO_MSDU_GSO_SIZE(pdev, msdu_idx), + TXRX_STATS_TSO_MSDU_TOTAL_LEN(pdev, msdu_idx), + TXRX_STATS_TSO_MSDU_NR_FRAGS(pdev, msdu_idx)); + + for (seg_idx = 0; + ((seg_idx < TXRX_STATS_TSO_MSDU_NUM_SEG(pdev, + msdu_idx)) && (seg_idx < NUM_MAX_TSO_SEGS)); + seg_idx++) { + struct qdf_tso_seg_t tso_seg = + TXRX_STATS_TSO_SEG(pdev, msdu_idx, seg_idx); + + txrx_nofl_info("seg idx: %d", seg_idx); + txrx_nofl_info("tso_enable: %d", + tso_seg.tso_flags.tso_enable); + txrx_nofl_info("fin %d syn %d rst %d psh %d ack %d urg %d ece %d cwr %d ns %d", + tso_seg.tso_flags.fin, + tso_seg.tso_flags.syn, + tso_seg.tso_flags.rst, + tso_seg.tso_flags.psh, + tso_seg.tso_flags.ack, + tso_seg.tso_flags.urg, + tso_seg.tso_flags.ece, + tso_seg.tso_flags.cwr, + tso_seg.tso_flags.ns); + txrx_nofl_info("tcp_seq_num: 0x%x ip_id: %d", + tso_seg.tso_flags.tcp_seq_num, + tso_seg.tso_flags.ip_id); + } + } +} + +void ol_txrx_tso_stats_clear(ol_txrx_pdev_handle pdev) +{ + qdf_mem_zero(&pdev->stats.pub.tx.tso.tso_pkts, + sizeof(struct ol_txrx_stats_elem)); +#if defined(FEATURE_TSO) + qdf_mem_zero(&pdev->stats.pub.tx.tso.tso_info, + sizeof(struct ol_txrx_stats_tso_info)); + qdf_mem_zero(&pdev->stats.pub.tx.tso.tso_hist, + sizeof(struct ol_txrx_tso_histogram)); +#endif +} +#endif /* defined(FEATURE_TSO) && defined(FEATURE_TSO_DEBUG) */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_ll_fastpath.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_ll_fastpath.c new file mode 100644 index 0000000000000000000000000000000000000000..3c13c1702dfb21260dfcc09ae61a0f8ca6d8ac6b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_ll_fastpath.c @@ -0,0 +1,628 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* OS abstraction libraries */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_atomic_read, etc. */ +#include /* qdf_unlikely */ + +/* APIs for other modules */ +#include /* HTT_TX_EXT_TID_MGMT */ +#include /* htt_tx_desc_tid */ + +/* internal header files relevant for all systems */ +#include /* TXRX_ASSERT1 */ +#include /* ol_tx_desc */ +#include /* ol_tx_send */ +#include + +/* internal header files relevant only for HL systems */ +#include /* ol_tx_classify, ol_tx_classify_mgmt */ +#include /* ol_tx_enqueue */ +#include /* ol_tx_sched */ + +/* internal header files relevant only for specific systems (Pronto) */ +#include /* OL_TX_ENCAP, etc */ +#include +#include + +#include /* HIF_DEVICE */ +#include /* Layering violation, but required for fast path */ +#include +#include /* htc_endpoint */ +#include +#include + +#if defined(HIF_PCI) || defined(HIF_SNOC) || defined(HIF_AHB) || \ + defined(HIF_IPCI) +#include +#endif + +/** + * ol_tx_setup_fastpath_ce_handles() Update ce_handle for fastpath use. + * + * @osc: pointer to HIF context + * @pdev: pointer to ol pdev + * + * Return: void + */ +void ol_tx_setup_fastpath_ce_handles(struct hif_opaque_softc *osc, + struct ol_txrx_pdev_t *pdev) +{ + /* + * Before the HTT attach, set up the CE handles + * CE handles are (struct CE_state *) + * This is only required in the fast path + */ + pdev->ce_tx_hdl = hif_get_ce_handle(osc, CE_HTT_H2T_MSG); +} + +qdf_nbuf_t +ol_tx_ll_wrapper(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) +{ + struct hif_opaque_softc *hif_device = + (struct hif_opaque_softc *)cds_get_context(QDF_MODULE_ID_HIF); + + if (qdf_likely(hif_device && + hif_is_fastpath_mode_enabled(hif_device))) { + msdu_list = ol_tx_ll_fast(vdev, msdu_list); + } else { + qdf_print("Fast path is disabled\n"); + QDF_BUG(0); + } + return msdu_list; +} + +/** + * ol_tx_trace_pkt() - Trace TX packet at OL layer + * + * @skb: skb to be traced + * @msdu_id: msdu_id of the packet + * @vdev_id: vdev_id of the packet + * + * Return: None + */ +static inline void ol_tx_trace_pkt(qdf_nbuf_t skb, uint16_t msdu_id, + uint8_t vdev_id) +{ + DPTRACE(qdf_dp_trace_ptr(skb, + QDF_DP_TRACE_TXRX_FAST_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(skb), + sizeof(qdf_nbuf_data(skb)), + msdu_id, vdev_id)); + + qdf_dp_trace_log_pkt(vdev_id, skb, QDF_TX, QDF_TRACE_DEFAULT_PDEV_ID); + + DPTRACE(qdf_dp_trace_data_pkt(skb, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_DP_TRACE_TX_PACKET_RECORD, + msdu_id, QDF_TX)); +} + +/** + * ol_tx_tso_adjust_pkt_dnld_len() Update download len for TSO pkt + * + * @msdu: tso mdsu for which download length is updated + * @msdu_info: tso msdu_info for the msdu + * @download_len: packet download length + * + * Return: Updated download length + */ +#if defined(FEATURE_TSO) +static uint32_t +ol_tx_tso_adjust_pkt_dnld_len(qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info, + uint32_t download_len) +{ + uint32_t frag0_len = 0, delta = 0, eit_hdr_len = 0; + uint32_t loc_download_len = download_len; + + frag0_len = qdf_nbuf_get_frag_len(msdu, 0); + loc_download_len -= frag0_len; + eit_hdr_len = msdu_info->tso_info.curr_seg->seg.tso_frags[0].length; + + if (eit_hdr_len < loc_download_len) { + delta = loc_download_len - eit_hdr_len; + download_len -= delta; + } + + return download_len; +} +#else +static uint32_t +ol_tx_tso_adjust_pkt_dnld_len(qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *msdu_info, + uint32_t download_len) +{ + return download_len; +} +#endif + +/** + * ol_tx_prepare_ll_fast() Alloc and prepare Tx descriptor + * + * Allocate and prepare Tx descriptor with msdu and fragment descritor + * inforamtion. + * + * @pdev: pointer to ol pdev handle + * @vdev: pointer to ol vdev handle + * @msdu: linked list of msdu packets + * @pkt_download_len: packet download length + * @ep_id: endpoint ID + * @msdu_info: Handle to msdu_info + * + * Return: Pointer to Tx descriptor + */ +static inline struct ol_tx_desc_t * +ol_tx_prepare_ll_fast(struct ol_txrx_pdev_t *pdev, + ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu, + uint32_t *pkt_download_len, uint32_t ep_id, + struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ol_tx_desc_t *tx_desc = NULL; + uint32_t *htt_tx_desc; + void *htc_hdr_vaddr; + u_int32_t num_frags, i; + enum extension_header_type type; + + tx_desc = ol_tx_desc_alloc_wrapper(pdev, vdev, msdu_info); + if (qdf_unlikely(!tx_desc)) + return NULL; + + tx_desc->netbuf = msdu; + if (msdu_info->tso_info.is_tso) { + tx_desc->tso_desc = msdu_info->tso_info.curr_seg; + qdf_tso_seg_dbg_setowner(tx_desc->tso_desc, tx_desc); + qdf_tso_seg_dbg_record(tx_desc->tso_desc, + TSOSEG_LOC_TXPREPLLFAST); + tx_desc->tso_num_desc = msdu_info->tso_info.tso_num_seg_list; + tx_desc->pkt_type = OL_TX_FRM_TSO; + TXRX_STATS_MSDU_INCR(pdev, tx.tso.tso_pkts, msdu); + } else { + tx_desc->pkt_type = OL_TX_FRM_STD; + } + + htt_tx_desc = tx_desc->htt_tx_desc; + +#if defined(HELIUMPLUS) + qdf_mem_zero(tx_desc->htt_frag_desc, sizeof(struct msdu_ext_desc_t)); +#endif + + /* Make sure frags num is set to 0 */ + /* + * Do this here rather than in hardstart, so + * that we can hopefully take only one cache-miss while + * accessing skb->cb. + */ + + /* HTT Header */ + /* TODO : Take care of multiple fragments */ + + type = ol_tx_get_ext_header_type(vdev, msdu); + + /* TODO: Precompute and store paddr in ol_tx_desc_t */ + /* Virtual address of the HTT/HTC header, added by driver */ + htc_hdr_vaddr = (char *)htt_tx_desc - HTC_HEADER_LEN; + if (qdf_unlikely(htt_tx_desc_init(pdev->htt_pdev, htt_tx_desc, + tx_desc->htt_tx_desc_paddr, + tx_desc->id, msdu, + &msdu_info->htt, + &msdu_info->tso_info, + NULL, type))) { + /* + * HTT Tx descriptor initialization failed. + * therefore, free the tx desc + */ + ol_tx_desc_free(pdev, tx_desc); + return NULL; + } + + num_frags = qdf_nbuf_get_num_frags(msdu); + /* num_frags are expected to be 2 max */ + num_frags = (num_frags > QDF_NBUF_CB_TX_MAX_EXTRA_FRAGS) + ? QDF_NBUF_CB_TX_MAX_EXTRA_FRAGS + : num_frags; +#if defined(HELIUMPLUS) + /* + * Use num_frags - 1, since 1 frag is used to store + * the HTT/HTC descriptor + * Refer to htt_tx_desc_init() + */ + htt_tx_desc_num_frags(pdev->htt_pdev, tx_desc->htt_frag_desc, + num_frags - 1); +#else /* ! defined(HELIUMPLUS) */ + htt_tx_desc_num_frags(pdev->htt_pdev, tx_desc->htt_tx_desc, + num_frags - 1); +#endif /* defined(HELIUMPLUS) */ + if (msdu_info->tso_info.is_tso) { + htt_tx_desc_fill_tso_info(pdev->htt_pdev, + tx_desc->htt_frag_desc, + &msdu_info->tso_info); + TXRX_STATS_TSO_SEG_UPDATE(pdev, + msdu_info->tso_info.msdu_stats_idx, + msdu_info->tso_info.curr_seg->seg); + } else { + for (i = 1; i < num_frags; i++) { + qdf_size_t frag_len; + qdf_dma_addr_t frag_paddr; + + frag_len = qdf_nbuf_get_frag_len(msdu, i); + frag_paddr = qdf_nbuf_get_frag_paddr(msdu, i); + if (type != EXT_HEADER_NOT_PRESENT) { + frag_paddr += + sizeof(struct htt_tx_msdu_desc_ext_t); + frag_len -= + sizeof(struct htt_tx_msdu_desc_ext_t); + } +#if defined(HELIUMPLUS) + htt_tx_desc_frag(pdev->htt_pdev, tx_desc->htt_frag_desc, + i - 1, frag_paddr, frag_len); +#if defined(HELIUMPLUS_DEBUG) + qdf_debug("htt_fdesc=%pK frag=%d frag_paddr=0x%0llx len=%zu", + tx_desc->htt_frag_desc, + i - 1, frag_paddr, frag_len); + ol_txrx_dump_pkt(netbuf, frag_paddr, 64); +#endif /* HELIUMPLUS_DEBUG */ +#else /* ! defined(HELIUMPLUS) */ + htt_tx_desc_frag(pdev->htt_pdev, tx_desc->htt_tx_desc, + i - 1, frag_paddr, frag_len); +#endif /* defined(HELIUMPLUS) */ + } + } + + /* + * Do we want to turn on word_stream bit-map here ? For linux, non-TSO + * this is not required. We still have to mark the swap bit correctly, + * when posting to the ring + */ + /* Check to make sure, data download length is correct */ + + /* + * TODO : Can we remove this check and always download a fixed length ? + */ + + if (QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_EXT_HEADER(msdu)) + *pkt_download_len += sizeof(struct htt_tx_msdu_desc_ext_t); + + if (qdf_unlikely(qdf_nbuf_len(msdu) < *pkt_download_len)) + *pkt_download_len = qdf_nbuf_len(msdu); + + if (msdu_info->tso_info.curr_seg) + *pkt_download_len = ol_tx_tso_adjust_pkt_dnld_len( + msdu, msdu_info, + *pkt_download_len); + + /* Fill the HTC header information */ + /* + * Passing 0 as the seq_no field, we can probably get away + * with it for the time being, since this is not checked in f/w + */ + /* TODO : Prefill this, look at multi-fragment case */ + if (ol_txrx_get_new_htt_msg_format(pdev)) + HTC_TX_DESC_FILL(htc_hdr_vaddr, + *pkt_download_len - HTC_HEADER_LEN, ep_id, 0); + else + HTC_TX_DESC_FILL(htc_hdr_vaddr, *pkt_download_len, ep_id, 0); + + return tx_desc; +} + +#if defined(FEATURE_TSO) +/** + * ol_tx_ll_fast() Update metadata information and send msdu to HIF/CE + * + * @vdev: handle to ol_txrx_vdev_t + * @msdu_list: msdu list to be sent out. + * + * Return: on success return NULL, pointer to nbuf when it fails to send. + */ +qdf_nbuf_t +ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) +{ + qdf_nbuf_t msdu = msdu_list; + struct ol_txrx_pdev_t *pdev = vdev->pdev; + uint32_t pkt_download_len; + uint32_t ep_id = HTT_EPID_GET(pdev->htt_pdev); + struct ol_txrx_msdu_info_t msdu_info; + uint32_t tso_msdu_stats_idx = 0; + + qdf_mem_zero(&msdu_info, sizeof(msdu_info)); + msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type; + msdu_info.htt.action.tx_comp_req = 0; + /* + * The msdu_list variable could be used instead of the msdu var, + * but just to clarify which operations are done on a single MSDU + * vs. a list of MSDUs, use a distinct variable for single MSDUs + * within the list. + */ + while (msdu) { + qdf_nbuf_t next; + struct ol_tx_desc_t *tx_desc; + int segments = 1; + + msdu_info.htt.info.ext_tid = qdf_nbuf_get_tid(msdu); + msdu_info.peer = NULL; + + if (qdf_unlikely(ol_tx_prepare_tso(vdev, msdu, &msdu_info))) { + ol_txrx_err("ol_tx_prepare_tso failed\n"); + TXRX_STATS_MSDU_LIST_INCR(vdev->pdev, + tx.dropped.host_reject, + msdu); + return msdu; + } + + segments = msdu_info.tso_info.num_segs; + + if (msdu_info.tso_info.is_tso) { + tso_msdu_stats_idx = + ol_tx_tso_get_stats_idx(vdev->pdev); + msdu_info.tso_info.msdu_stats_idx = tso_msdu_stats_idx; + ol_tx_tso_update_stats(vdev->pdev, + &(msdu_info.tso_info), + msdu, tso_msdu_stats_idx); + } + + /* + * The netbuf may get linked into a different list + * inside the ce_send_fast function, so store the next + * pointer before the ce_send call. + */ + next = qdf_nbuf_next(msdu); + /* + * Increment the skb->users count here, for this SKB, to make + * sure it will be freed only after receiving Tx completion + * of the last segment. + * Decrement skb->users count before sending last segment + */ + if (qdf_nbuf_is_tso(msdu) && segments) + qdf_nbuf_inc_users(msdu); + + /* init the current segment to the 1st segment in the list */ + while (segments) { + if (msdu_info.tso_info.curr_seg) + QDF_NBUF_CB_PADDR(msdu) = msdu_info.tso_info. + curr_seg->seg.tso_frags[0].paddr; + + segments--; + + msdu_info.htt.info.frame_type = pdev->htt_pkt_type; + msdu_info.htt.info.vdev_id = vdev->vdev_id; + msdu_info.htt.action.cksum_offload = + qdf_nbuf_get_tx_cksum(msdu); + switch (qdf_nbuf_get_exemption_type(msdu)) { + case QDF_NBUF_EXEMPT_NO_EXEMPTION: + case QDF_NBUF_EXEMPT_ON_KEY_MAPPING_KEY_UNAVAILABLE: + /* We want to encrypt this frame */ + msdu_info.htt.action.do_encrypt = 1; + break; + case QDF_NBUF_EXEMPT_ALWAYS: + /* We don't want to encrypt this frame */ + msdu_info.htt.action.do_encrypt = 0; + break; + default: + msdu_info.htt.action.do_encrypt = 1; + qdf_assert(0); + break; + } + + pkt_download_len = ((struct htt_pdev_t *) + (pdev->htt_pdev))->download_len; + tx_desc = ol_tx_prepare_ll_fast(pdev, vdev, msdu, + &pkt_download_len, + ep_id, &msdu_info); + + TXRX_STATS_MSDU_INCR(pdev, tx.from_stack, msdu); + + if (qdf_likely(tx_desc)) { + struct qdf_tso_seg_elem_t *next_seg; + + /* + * if this is a jumbo nbuf, then increment the + * number of nbuf users for each additional + * segment of the msdu. This will ensure that + * the skb is freed only after receiving tx + * completion for all segments of an nbuf. + */ + if (segments != + (msdu_info.tso_info.num_segs - 1)) + qdf_nbuf_inc_users(msdu); + + ol_tx_trace_pkt(msdu, tx_desc->id, + vdev->vdev_id); + /* + * If debug display is enabled, show the meta + * data being downloaded to the target via the + * HTT tx descriptor. + */ + htt_tx_desc_display(tx_desc->htt_tx_desc); + + /* mark the relevant tso_seg free-able */ + if (msdu_info.tso_info.curr_seg) { + msdu_info.tso_info.curr_seg-> + sent_to_target = 1; + next_seg = msdu_info.tso_info. + curr_seg->next; + } else { + next_seg = NULL; + } + + /* Decrement the skb-users count if segment + * is the last segment or the only segment + */ + if (tx_desc->pkt_type == OL_TX_FRM_TSO && + segments == 0) + qdf_nbuf_tx_free(msdu, 0); + + if ((ce_send_fast(pdev->ce_tx_hdl, msdu, + ep_id, + pkt_download_len) == 0)) { + struct qdf_tso_info_t *tso_info = + &msdu_info.tso_info; + /* + * If TSO packet, free associated + * remaining TSO segment descriptors + */ + if (tx_desc->pkt_type == + OL_TX_FRM_TSO) { + tso_info->curr_seg = next_seg; + ol_free_remaining_tso_segs(vdev, + &msdu_info, true); + if (segments == + (msdu_info.tso_info.num_segs + - 1)) + qdf_nbuf_tx_free( + msdu, + QDF_NBUF_PKT_ERROR); + } + + /* + * The packet could not be sent. + * Free the descriptor, return the + * packet to the caller. + */ + ol_tx_desc_frame_free_nonstd(pdev, + tx_desc, + htt_tx_status_download_fail); + return msdu; + } + if (msdu_info.tso_info.curr_seg) + msdu_info.tso_info.curr_seg = next_seg; + + if (msdu_info.tso_info.is_tso) { + TXRX_STATS_TSO_INC_SEG(vdev->pdev, + tso_msdu_stats_idx); + TXRX_STATS_TSO_INC_SEG_IDX(vdev->pdev, + tso_msdu_stats_idx); + } + } else { + /* + * If TSO packet, free associated + * remaining TSO segment descriptors + */ + if (qdf_nbuf_is_tso(msdu)) { + ol_free_remaining_tso_segs(vdev, + &msdu_info, true); + if (segments == + (msdu_info.tso_info.num_segs - 1)) + qdf_nbuf_tx_free(msdu, + QDF_NBUF_PKT_ERROR); + } + TXRX_STATS_MSDU_LIST_INCR( + pdev, tx.dropped.host_reject, msdu); + /* the list of unaccepted MSDUs */ + return msdu; + } + } /* while segments */ + + msdu = next; + } /* while msdus */ + return NULL; /* all MSDUs were accepted */ +} +#else +qdf_nbuf_t +ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) +{ + qdf_nbuf_t msdu = msdu_list; + struct ol_txrx_pdev_t *pdev = vdev->pdev; + uint32_t pkt_download_len; + uint32_t ep_id = HTT_EPID_GET(pdev->htt_pdev); + struct ol_txrx_msdu_info_t msdu_info; + + msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type; + msdu_info.htt.action.tx_comp_req = 0; + msdu_info.tso_info.is_tso = 0; + /* + * The msdu_list variable could be used instead of the msdu var, + * but just to clarify which operations are done on a single MSDU + * vs. a list of MSDUs, use a distinct variable for single MSDUs + * within the list. + */ + while (msdu) { + qdf_nbuf_t next; + struct ol_tx_desc_t *tx_desc; + + msdu_info.htt.info.ext_tid = qdf_nbuf_get_tid(msdu); + msdu_info.peer = NULL; + + msdu_info.htt.info.frame_type = pdev->htt_pkt_type; + msdu_info.htt.info.vdev_id = vdev->vdev_id; + msdu_info.htt.action.cksum_offload = + qdf_nbuf_get_tx_cksum(msdu); + switch (qdf_nbuf_get_exemption_type(msdu)) { + case QDF_NBUF_EXEMPT_NO_EXEMPTION: + case QDF_NBUF_EXEMPT_ON_KEY_MAPPING_KEY_UNAVAILABLE: + /* We want to encrypt this frame */ + msdu_info.htt.action.do_encrypt = 1; + break; + case QDF_NBUF_EXEMPT_ALWAYS: + /* We don't want to encrypt this frame */ + msdu_info.htt.action.do_encrypt = 0; + break; + default: + msdu_info.htt.action.do_encrypt = 1; + qdf_assert(0); + break; + } + + pkt_download_len = ((struct htt_pdev_t *) + (pdev->htt_pdev))->download_len; + tx_desc = ol_tx_prepare_ll_fast(pdev, vdev, msdu, + &pkt_download_len, ep_id, + &msdu_info); + + TXRX_STATS_MSDU_INCR(pdev, tx.from_stack, msdu); + + if (qdf_likely(tx_desc)) { + DPTRACE(qdf_dp_trace_ptr(msdu, + QDF_DP_TRACE_TXRX_FAST_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(msdu), + sizeof(qdf_nbuf_data(msdu)), tx_desc->id, + vdev->vdev_id)); + /* + * If debug display is enabled, show the meta-data being + * downloaded to the target via the HTT tx descriptor. + */ + htt_tx_desc_display(tx_desc->htt_tx_desc); + /* + * The netbuf may get linked into a different list + * inside the ce_send_fast function, so store the next + * pointer before the ce_send call. + */ + next = qdf_nbuf_next(msdu); + if ((ce_send_fast(pdev->ce_tx_hdl, msdu, + ep_id, pkt_download_len) == 0)) { + /* + * The packet could not be sent + * Free the descriptor, return the packet to the + * caller + */ + ol_tx_desc_free(pdev, tx_desc); + return msdu; + } + msdu = next; + } else { + TXRX_STATS_MSDU_LIST_INCR( + pdev, tx.dropped.host_reject, msdu); + return msdu; /* the list of unaccepted MSDUs */ + } + } + + return NULL; /* all MSDUs were accepted */ +} +#endif /* FEATURE_TSO */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_ll_legacy.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_ll_legacy.c new file mode 100644 index 0000000000000000000000000000000000000000..5f905a04f19e1d46f0cbb664eade6bc171af4d0b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_ll_legacy.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* OS abstraction libraries */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_atomic_read, etc. */ +#include /* qdf_unlikely */ + +/* APIs for other modules */ +#include /* HTT_TX_EXT_TID_MGMT */ +#include /* htt_tx_desc_tid */ + +/* internal header files relevant for all systems */ +#include /* TXRX_ASSERT1 */ +#include /* ol_tx_desc */ +#include /* ol_tx_send */ +#include + +/* internal header files relevant only for HL systems */ +#include /* ol_tx_classify, ol_tx_classify_mgmt */ +#include /* ol_tx_enqueue */ +#include /* ol_tx_sched */ + +/* internal header files relevant only for specific systems (Pronto) */ +#include /* OL_TX_ENCAP, etc */ +#include +#include + +/** + * ol_tx_ll_wrapper() wrapper to ol_tx_ll + * + */ +qdf_nbuf_t +ol_tx_ll_wrapper(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) +{ + return ol_tx_ll(vdev, msdu_list); +} + +#if defined(FEATURE_TSO) +qdf_nbuf_t ol_tx_ll(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) +{ + qdf_nbuf_t msdu = msdu_list; + struct ol_txrx_msdu_info_t msdu_info; + uint32_t tso_msdu_stats_idx = 0; + + msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type; + msdu_info.htt.action.tx_comp_req = 0; + /* + * The msdu_list variable could be used instead of the msdu var, + * but just to clarify which operations are done on a single MSDU + * vs. a list of MSDUs, use a distinct variable for single MSDUs + * within the list. + */ + while (msdu) { + qdf_nbuf_t next; + struct ol_tx_desc_t *tx_desc = NULL; + int segments = 1; + + msdu_info.htt.info.ext_tid = qdf_nbuf_get_tid(msdu); + msdu_info.peer = NULL; + + if (qdf_unlikely(ol_tx_prepare_tso(vdev, msdu, &msdu_info))) { + qdf_print("ol_tx_prepare_tso failed\n"); + TXRX_STATS_MSDU_LIST_INCR(vdev->pdev, + tx.dropped.host_reject, + msdu); + return msdu; + } + + segments = msdu_info.tso_info.num_segs; + + if (msdu_info.tso_info.is_tso) { + tso_msdu_stats_idx = + ol_tx_tso_get_stats_idx(vdev->pdev); + msdu_info.tso_info.msdu_stats_idx = tso_msdu_stats_idx; + ol_tx_tso_update_stats(vdev->pdev, + &(msdu_info.tso_info), + msdu, tso_msdu_stats_idx); + } + + /* + * The netbuf may get linked into a different list inside the + * ol_tx_send function, so store the next pointer before the + * tx_send call. + */ + next = qdf_nbuf_next(msdu); + /* init the current segment to the 1st segment in the list */ + while (segments) { + if (msdu_info.tso_info.curr_seg) + QDF_NBUF_CB_PADDR(msdu) = + msdu_info.tso_info.curr_seg-> + seg.tso_frags[0].paddr; + + segments--; + + tx_desc = ol_tx_prepare_ll(vdev, msdu, &msdu_info); + if (!tx_desc) + return msdu; + + /* + * If this is a jumbo nbuf, then increment the number + * of nbuf users for each additional segment of the msdu + * This will ensure that the skb is freed only after + * receiving tx completion for all segments of an nbuf. + */ + if (segments) + qdf_nbuf_inc_users(msdu); + + TXRX_STATS_MSDU_INCR(vdev->pdev, tx.from_stack, msdu); + + /* + * If debug display is enabled, show the meta-data being + * downloaded to the target via the HTT tx descriptor. + */ + htt_tx_desc_display(tx_desc->htt_tx_desc); + + ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id); + + if (msdu_info.tso_info.curr_seg) { + msdu_info.tso_info.curr_seg = + msdu_info.tso_info.curr_seg->next; + } + + if (msdu_info.tso_info.is_tso) { + TXRX_STATS_TSO_INC_SEG(vdev->pdev, + tso_msdu_stats_idx); + TXRX_STATS_TSO_INC_SEG_IDX(vdev->pdev, + tso_msdu_stats_idx); + } + } /* while segments */ + + msdu = next; + } /* while msdus */ + return NULL; /* all MSDUs were accepted */ +} +#else /* TSO */ + +qdf_nbuf_t ol_tx_ll(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) +{ + qdf_nbuf_t msdu = msdu_list; + struct ol_txrx_msdu_info_t msdu_info; + + msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type; + msdu_info.htt.action.tx_comp_req = 0; + msdu_info.tso_info.is_tso = 0; + /* + * The msdu_list variable could be used instead of the msdu var, + * but just to clarify which operations are done on a single MSDU + * vs. a list of MSDUs, use a distinct variable for single MSDUs + * within the list. + */ + while (msdu) { + qdf_nbuf_t next; + struct ol_tx_desc_t *tx_desc = NULL; + + msdu_info.htt.info.ext_tid = qdf_nbuf_get_tid(msdu); + msdu_info.peer = NULL; + tx_desc = ol_tx_prepare_ll(vdev, msdu, &msdu_info); + if (!tx_desc) + return msdu; + + TXRX_STATS_MSDU_INCR(vdev->pdev, tx.from_stack, msdu); + + /* + * If debug display is enabled, show the meta-data being + * downloaded to the target via the HTT tx descriptor. + */ + htt_tx_desc_display(tx_desc->htt_tx_desc); + /* + * The netbuf may get linked into a different list inside the + * ol_tx_send function, so store the next pointer before the + * tx_send call. + */ + next = qdf_nbuf_next(msdu); + ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id); + msdu = next; + } + return NULL; /* all MSDUs were accepted */ +} +#endif /* TSO */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_queue.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_queue.c new file mode 100644 index 0000000000000000000000000000000000000000..55ca3c3eac1536dae65d23eded2895bfde402c90 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_queue.c @@ -0,0 +1,2051 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_atomic_read, etc. */ +#include /* ol_cfg_addba_retry */ +#include /* HTT_TX_EXT_TID_MGMT */ +#include /* htt_tx_desc_tid */ +#include /* ol_txrx_vdev_handle */ +#include /* ol_txrx_sync, ol_tx_addba_conf */ +#include +#include /* ol_ctrl_addba_req */ +#include /* TXRX_ASSERT1, etc. */ +#include /* ol_tx_desc, ol_tx_desc_frame_list_free */ +#include /* ol_tx_vdev_ll_pause_queue_send */ +#include /* ol_tx_sched_notify, etc. */ +#include +#include /* ol_tx_desc_pool_size_hl */ +#include /* ENABLE_TX_QUEUE_LOG */ +#include /* bool */ +#include "cdp_txrx_flow_ctrl_legacy.h" +#include +#include +#if defined(CONFIG_HL_SUPPORT) + +#ifndef offsetof +#define offsetof(type, field) ((qdf_size_t)(&((type *)0)->field)) +#endif + +/*--- function prototypes for optional host ADDBA negotiation ---------------*/ + +#define OL_TX_QUEUE_ADDBA_CHECK(pdev, txq, tx_msdu_info) /* no-op */ + +#ifndef container_of +#define container_of(ptr, type, member) ((type *)( \ + (char *)(ptr) - (char *)(&((type *)0)->member))) +#endif +/*--- function definitions --------------------------------------------------*/ + +/** + * ol_tx_queue_vdev_flush() - try to flush pending frames in the tx queues + * no matter it's queued in the TX scheduler or not + * @pdev: the physical device object + * @vdev: the virtual device object + * + * Return: None + */ +static void +ol_tx_queue_vdev_flush(struct ol_txrx_pdev_t *pdev, struct ol_txrx_vdev_t *vdev) +{ +#define PEER_ARRAY_COUNT 10 + struct ol_tx_frms_queue_t *txq; + struct ol_txrx_peer_t *peer, *peers[PEER_ARRAY_COUNT]; + int i, j, peer_count; + + ol_tx_hl_queue_flush_all(vdev); + + /* flush VDEV TX queues */ + for (i = 0; i < OL_TX_VDEV_NUM_QUEUES; i++) { + txq = &vdev->txqs[i]; + /* + * currently txqs of MCAST_BCAST/DEFAULT_MGMT packet are using + * tid HTT_TX_EXT_TID_NON_QOS_MCAST_BCAST/HTT_TX_EXT_TID_MGMT + * when inserted into scheduler, so use same tid when we flush + * them + */ + if (i == OL_TX_VDEV_MCAST_BCAST) + ol_tx_queue_free(pdev, + txq, + HTT_TX_EXT_TID_NON_QOS_MCAST_BCAST, + false); + else if (i == OL_TX_VDEV_DEFAULT_MGMT) + ol_tx_queue_free(pdev, + txq, + HTT_TX_EXT_TID_MGMT, + false); + else + ol_tx_queue_free(pdev, + txq, + (i + OL_TX_NUM_TIDS), + false); + } + /* flush PEER TX queues */ + do { + peer_count = 0; + /* select candidate peers */ + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { + for (i = 0; i < OL_TX_NUM_TIDS; i++) { + txq = &peer->txqs[i]; + if (txq->frms) { + ol_txrx_peer_get_ref + (peer, + PEER_DEBUG_ID_OL_TXQ_VDEV_FL); + peers[peer_count++] = peer; + break; + } + } + if (peer_count >= PEER_ARRAY_COUNT) + break; + } + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + /* flush TX queues of candidate peers */ + for (i = 0; i < peer_count; i++) { + for (j = 0; j < OL_TX_NUM_TIDS; j++) { + txq = &peers[i]->txqs[j]; + if (txq->frms) + ol_tx_queue_free(pdev, txq, j, true); + } + ol_txrx_info("Delete Peer %pK", peer); + ol_txrx_peer_release_ref(peers[i], + PEER_DEBUG_ID_OL_TXQ_VDEV_FL); + } + } while (peer_count >= PEER_ARRAY_COUNT); +} + +/** + * ol_tx_queue_flush() - try to flush pending frames in the tx queues + * no matter it's queued in the TX scheduler or not + * @pdev: the physical device object + * + * Return: None + */ +static inline void +ol_tx_queue_flush(struct ol_txrx_pdev_t *pdev) +{ + struct ol_txrx_vdev_t *vdev; + + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + ol_tx_queue_vdev_flush(pdev, vdev); + } +} + +void +ol_tx_queue_discard( + struct ol_txrx_pdev_t *pdev, + bool flush_all, + ol_tx_desc_list *tx_descs) +{ + u_int16_t num; + u_int16_t discarded, actual_discarded = 0; + + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + + if (flush_all == true) + /* flush all the pending tx queues in the scheduler */ + num = ol_tx_desc_pool_size_hl(pdev->ctrl_pdev) - + qdf_atomic_read(&pdev->tx_queue.rsrc_cnt); + else + /*TODO: Discard frames for a particular vdev only */ + num = pdev->tx_queue.rsrc_threshold_hi - + pdev->tx_queue.rsrc_threshold_lo; + + TX_SCHED_DEBUG_PRINT("+%s : %u\n,", __func__, + qdf_atomic_read(&pdev->tx_queue.rsrc_cnt)); + while (num > 0) { + discarded = ol_tx_sched_discard_select( + pdev, (u_int16_t)num, tx_descs, flush_all); + if (discarded == 0) + /* + * No more packets could be discarded. + * Probably tx queues are empty. + */ + break; + + num -= discarded; + actual_discarded += discarded; + } + qdf_atomic_add(actual_discarded, &pdev->tx_queue.rsrc_cnt); + TX_SCHED_DEBUG_PRINT("-%s\n", __func__); + + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + + if (flush_all == true && num > 0) + /* + * try to flush pending frames in the tx queues + * which are not queued in the TX scheduler. + */ + ol_tx_queue_flush(pdev); +} + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL + +/** + * is_ol_tx_discard_frames_success() - check whether currently queued tx frames + * can be discarded or not + * @pdev: the physical device object + * @tx_desc: tx desciptor ptr + * + * Return: Success if available tx descriptors are too few + */ +static inline bool +is_ol_tx_discard_frames_success(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + ol_txrx_vdev_handle vdev; + bool discard_frames; + + vdev = tx_desc->vdev; + + qdf_spin_lock_bh(&vdev->pdev->tx_mutex); + if (vdev->tx_desc_limit == 0) { + /* Flow control not enabled */ + discard_frames = qdf_atomic_read(&pdev->tx_queue.rsrc_cnt) <= + pdev->tx_queue.rsrc_threshold_lo; + } else { + /* + * Discard + * if netbuf is normal priority and tx_desc_count greater than + * queue stop threshold + * AND + * if netbuf is high priority and tx_desc_count greater than + * tx desc limit. + */ + discard_frames = (!ol_tx_desc_is_high_prio(tx_desc->netbuf) && + qdf_atomic_read(&vdev->tx_desc_count) > + vdev->queue_stop_th) || + (ol_tx_desc_is_high_prio(tx_desc->netbuf) && + qdf_atomic_read(&vdev->tx_desc_count) > + vdev->tx_desc_limit); + } + qdf_spin_unlock_bh(&vdev->pdev->tx_mutex); + + return discard_frames; +} +#else + +static inline bool +is_ol_tx_discard_frames_success(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + return qdf_atomic_read(&pdev->tx_queue.rsrc_cnt) <= + pdev->tx_queue.rsrc_threshold_lo; +} +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + +void +ol_tx_enqueue( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + struct ol_tx_desc_t *tx_desc, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + int bytes; + struct ol_tx_sched_notify_ctx_t notify_ctx; + + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + + /* + * If too few tx descriptors are available, drop some currently-queued + * tx frames, to provide enough tx descriptors for new frames, which + * may be higher priority than the current frames. + */ + if (is_ol_tx_discard_frames_success(pdev, tx_desc)) { + ol_tx_desc_list tx_descs; + + TAILQ_INIT(&tx_descs); + ol_tx_queue_discard(pdev, false, &tx_descs); + /*Discard Frames in Discard List*/ + ol_tx_desc_frame_list_free(pdev, &tx_descs, 1 /* error */); + } + + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + TAILQ_INSERT_TAIL(&txq->head, tx_desc, tx_desc_list_elem); + + bytes = qdf_nbuf_len(tx_desc->netbuf); + txq->frms++; + txq->bytes += bytes; + ol_tx_update_grp_frm_count(txq, 1); + ol_tx_queue_log_enqueue(pdev, tx_msdu_info, 1, bytes); + + if (txq->flag != ol_tx_queue_paused) { + notify_ctx.event = OL_TX_ENQUEUE_FRAME; + notify_ctx.frames = 1; + notify_ctx.bytes = qdf_nbuf_len(tx_desc->netbuf); + notify_ctx.txq = txq; + notify_ctx.info.tx_msdu_info = tx_msdu_info; + ol_tx_sched_notify(pdev, ¬ify_ctx); + txq->flag = ol_tx_queue_active; + } + + if (!ETHERTYPE_IS_EAPOL_WAPI(tx_msdu_info->htt.info.ethertype)) + OL_TX_QUEUE_ADDBA_CHECK(pdev, txq, tx_msdu_info); + + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +u_int16_t +ol_tx_dequeue( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + ol_tx_desc_list *head, + u_int16_t max_frames, + u_int32_t *credit, + int *bytes) +{ + u_int16_t num_frames; + int bytes_sum; + unsigned int credit_sum; + + TXRX_ASSERT2(txq->flag != ol_tx_queue_paused); + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + + if (txq->frms < max_frames) + max_frames = txq->frms; + + bytes_sum = 0; + credit_sum = 0; + for (num_frames = 0; num_frames < max_frames; num_frames++) { + unsigned int frame_credit; + struct ol_tx_desc_t *tx_desc; + + tx_desc = TAILQ_FIRST(&txq->head); + + frame_credit = htt_tx_msdu_credit(tx_desc->netbuf); + if (credit_sum + frame_credit > *credit) + break; + + credit_sum += frame_credit; + bytes_sum += qdf_nbuf_len(tx_desc->netbuf); + TAILQ_REMOVE(&txq->head, tx_desc, tx_desc_list_elem); + TAILQ_INSERT_TAIL(head, tx_desc, tx_desc_list_elem); + } + txq->frms -= num_frames; + txq->bytes -= bytes_sum; + ol_tx_update_grp_frm_count(txq, -credit_sum); + + /* a paused queue remains paused, regardless of whether it has frames */ + if (txq->frms == 0 && txq->flag == ol_tx_queue_active) + txq->flag = ol_tx_queue_empty; + + ol_tx_queue_log_dequeue(pdev, txq, num_frames, bytes_sum); + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); + + *bytes = bytes_sum; + *credit = credit_sum; + return num_frames; +} + +void +ol_tx_queue_free( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid, bool is_peer_txq) +{ + int frms = 0, bytes = 0; + struct ol_tx_desc_t *tx_desc; + struct ol_tx_sched_notify_ctx_t notify_ctx; + ol_tx_desc_list tx_tmp_list; + + TAILQ_INIT(&tx_tmp_list); + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + + notify_ctx.event = OL_TX_DELETE_QUEUE; + notify_ctx.txq = txq; + notify_ctx.info.ext_tid = tid; + ol_tx_sched_notify(pdev, ¬ify_ctx); + + frms = txq->frms; + tx_desc = TAILQ_FIRST(&txq->head); + while (txq->frms) { + bytes += qdf_nbuf_len(tx_desc->netbuf); + txq->frms--; + tx_desc = TAILQ_NEXT(tx_desc, tx_desc_list_elem); + } + ol_tx_queue_log_free(pdev, txq, tid, frms, bytes, is_peer_txq); + txq->bytes -= bytes; + ol_tx_queue_log_free(pdev, txq, tid, frms, bytes, is_peer_txq); + txq->flag = ol_tx_queue_empty; + /* txq->head gets reset during the TAILQ_CONCAT call */ + TAILQ_CONCAT(&tx_tmp_list, &txq->head, tx_desc_list_elem); + ol_tx_update_grp_frm_count(txq, -frms); + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + /* free tx frames without holding tx_queue_spinlock */ + qdf_atomic_add(frms, &pdev->tx_queue.rsrc_cnt); + while (frms) { + tx_desc = TAILQ_FIRST(&tx_tmp_list); + TAILQ_REMOVE(&tx_tmp_list, tx_desc, tx_desc_list_elem); + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, 0); + frms--; + } + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + + +/*--- queue pause / unpause functions ---------------------------------------*/ + +/** + * ol_txrx_peer_tid_pause_base() - suspend/pause txq for a given tid given peer + * @pdev: the physical device object + * @peer: peer device object + * @tid: tid for which queue needs to be paused + * + * Return: None + */ +static void +ol_txrx_peer_tid_pause_base( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + int tid) +{ + struct ol_tx_frms_queue_t *txq = &peer->txqs[tid]; + + if (txq->paused_count.total++ == 0) { + struct ol_tx_sched_notify_ctx_t notify_ctx; + + notify_ctx.event = OL_TX_PAUSE_QUEUE; + notify_ctx.txq = txq; + notify_ctx.info.ext_tid = tid; + ol_tx_sched_notify(pdev, ¬ify_ctx); + txq->flag = ol_tx_queue_paused; + } +} +#ifdef QCA_BAD_PEER_TX_FLOW_CL + +/** + * ol_txrx_peer_pause_but_no_mgmt_q_base() - suspend/pause all txqs except + * management queue for a given peer + * @pdev: the physical device object + * @peer: peer device object + * + * Return: None + */ +static void +ol_txrx_peer_pause_but_no_mgmt_q_base( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + int i; + + for (i = 0; i < OL_TX_MGMT_TID; i++) + ol_txrx_peer_tid_pause_base(pdev, peer, i); +} +#endif + + +/** + * ol_txrx_peer_pause_base() - suspend/pause all txqs for a given peer + * @pdev: the physical device object + * @peer: peer device object + * + * Return: None + */ +static void +ol_txrx_peer_pause_base( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + int i; + + for (i = 0; i < QDF_ARRAY_SIZE(peer->txqs); i++) + ol_txrx_peer_tid_pause_base(pdev, peer, i); +} + +/** + * ol_txrx_peer_tid_unpause_base() - unpause txq for a given tid given peer + * @pdev: the physical device object + * @peer: peer device object + * @tid: tid for which queue needs to be unpaused + * + * Return: None + */ +static void +ol_txrx_peer_tid_unpause_base( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + int tid) +{ + struct ol_tx_frms_queue_t *txq = &peer->txqs[tid]; + /* + * Don't actually unpause the tx queue until all pause requests + * have been removed. + */ + TXRX_ASSERT2(txq->paused_count.total > 0); + /* return, if not already paused */ + if (txq->paused_count.total == 0) + return; + + if (--txq->paused_count.total == 0) { + struct ol_tx_sched_notify_ctx_t notify_ctx; + + notify_ctx.event = OL_TX_UNPAUSE_QUEUE; + notify_ctx.txq = txq; + notify_ctx.info.ext_tid = tid; + ol_tx_sched_notify(pdev, ¬ify_ctx); + + if (txq->frms == 0) { + txq->flag = ol_tx_queue_empty; + } else { + txq->flag = ol_tx_queue_active; + /* + * Now that the are new tx frames available to download, + * invoke the scheduling function, to see if it wants to + * download the new frames. + * Since the queue lock is currently held, and since + * the scheduler function takes the lock, temporarily + * release the lock. + */ + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + ol_tx_sched(pdev); + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + } + } +} + +/** + * ol_txrx_peer_unpause_base() - unpause all txqs for a given peer + * @pdev: the physical device object + * @peer: peer device object + * + * Return: None + */ +static void +ol_txrx_peer_unpause_base( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + int i; + + for (i = 0; i < QDF_ARRAY_SIZE(peer->txqs); i++) + ol_txrx_peer_tid_unpause_base(pdev, peer, i); +} + +#ifdef QCA_BAD_PEER_TX_FLOW_CL +/** + * ol_txrx_peer_unpause_but_no_mgmt_q_base() - unpause all txqs except + * management queue for a given peer + * @pdev: the physical device object + * @peer: peer device object + * + * Return: None + */ +static void +ol_txrx_peer_unpause_but_no_mgmt_q_base( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + int i; + + for (i = 0; i < OL_TX_MGMT_TID; i++) + ol_txrx_peer_tid_unpause_base(pdev, peer, i); +} +#endif + +void +ol_txrx_peer_tid_unpause(ol_txrx_peer_handle peer, int tid) +{ + struct ol_txrx_pdev_t *pdev = peer->vdev->pdev; + + /* TO DO: log the queue unpause */ + + /* acquire the mutex lock, since we'll be modifying the queues */ + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + + if (tid == -1) { + int i; + + for (i = 0; i < QDF_ARRAY_SIZE(peer->txqs); i++) + ol_txrx_peer_tid_unpause_base(pdev, peer, i); + + } else { + ol_txrx_peer_tid_unpause_base(pdev, peer, tid); + } + + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +void +ol_txrx_vdev_pause(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint32_t reason, uint32_t pause_type) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + struct ol_txrx_pdev_t *pdev; + struct ol_txrx_peer_t *peer; + /* TO DO: log the queue pause */ + /* acquire the mutex lock, since we'll be modifying the queues */ + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is NULL"); + return; + } + + pdev = vdev->pdev; + + /* use peer_ref_mutex before accessing peer_list */ + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { + if (pause_type == PAUSE_TYPE_CHOP) { + if (!(peer->is_tdls_peer && peer->tdls_offchan_enabled)) + ol_txrx_peer_pause_base(pdev, peer); + } else if (pause_type == PAUSE_TYPE_CHOP_TDLS_OFFCHAN) { + if (peer->is_tdls_peer && peer->tdls_offchan_enabled) + ol_txrx_peer_pause_base(pdev, peer); + } else { + ol_txrx_peer_pause_base(pdev, peer); + } + } + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +void ol_txrx_vdev_unpause(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint32_t reason, uint32_t pause_type) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + struct ol_txrx_pdev_t *pdev; + struct ol_txrx_peer_t *peer; + + /* TO DO: log the queue unpause */ + /* acquire the mutex lock, since we'll be modifying the queues */ + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is NULL"); + return; + } + + pdev = vdev->pdev; + + /* take peer_ref_mutex before accessing peer_list */ + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + + TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { + if (pause_type == PAUSE_TYPE_CHOP) { + if (!(peer->is_tdls_peer && peer->tdls_offchan_enabled)) + ol_txrx_peer_unpause_base(pdev, peer); + } else if (pause_type == PAUSE_TYPE_CHOP_TDLS_OFFCHAN) { + if (peer->is_tdls_peer && peer->tdls_offchan_enabled) + ol_txrx_peer_unpause_base(pdev, peer); + } else { + ol_txrx_peer_unpause_base(pdev, peer); + } + } + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +void ol_txrx_vdev_flush(struct cdp_soc_t *soc_hdl, uint8_t vdev_id) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is NULL"); + return; + } + + if (!vdev) + return; + + ol_tx_queue_vdev_flush(vdev->pdev, vdev); +} + +#ifdef QCA_BAD_PEER_TX_FLOW_CL + +/** + * ol_txrx_peer_bal_add_limit_peer() - add one peer into limit list + * @pdev: Pointer to PDEV structure. + * @peer_id: Peer Identifier. + * @peer_limit Peer limit threshold + * + * Add one peer into the limit list of pdev + * Note that the peer limit info will be also updated + * If it is the first time, start the timer + * + * Return: None + */ +void +ol_txrx_peer_bal_add_limit_peer(struct ol_txrx_pdev_t *pdev, + u_int16_t peer_id, u_int16_t peer_limit) +{ + u_int16_t i, existed = 0; + struct ol_txrx_peer_t *peer = NULL; + + for (i = 0; i < pdev->tx_peer_bal.peer_num; i++) { + if (pdev->tx_peer_bal.limit_list[i].peer_id == peer_id) { + existed = 1; + break; + } + } + + if (!existed) { + u_int32_t peer_num = pdev->tx_peer_bal.peer_num; + /* Check if peer_num has reached the capabilit */ + if (peer_num >= MAX_NO_PEERS_IN_LIMIT) { + TX_SCHED_DEBUG_PRINT_ALWAYS( + "reach the maxinum peer num %d\n", + peer_num); + return; + } + pdev->tx_peer_bal.limit_list[peer_num].peer_id = peer_id; + pdev->tx_peer_bal.limit_list[peer_num].limit_flag = true; + pdev->tx_peer_bal.limit_list[peer_num].limit = peer_limit; + pdev->tx_peer_bal.peer_num++; + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (peer) { + peer->tx_limit_flag = true; + peer->tx_limit = peer_limit; + } + + TX_SCHED_DEBUG_PRINT_ALWAYS( + "Add one peer into limit queue, peer_id %d, cur peer num %d\n", + peer_id, + pdev->tx_peer_bal.peer_num); + } + + /* Only start the timer once */ + if (pdev->tx_peer_bal.peer_bal_timer_state == + ol_tx_peer_bal_timer_inactive) { + qdf_timer_start(&pdev->tx_peer_bal.peer_bal_timer, + pdev->tx_peer_bal.peer_bal_period_ms); + pdev->tx_peer_bal.peer_bal_timer_state = + ol_tx_peer_bal_timer_active; + } +} + +/** + * ol_txrx_peer_bal_remove_limit_peer() - remove one peer from limit list + * @pdev: Pointer to PDEV structure. + * @peer_id: Peer Identifier. + * + * Remove one peer from the limit list of pdev + * Note that Only stop the timer if no peer in limit state + * + * Return: NULL + */ +void +ol_txrx_peer_bal_remove_limit_peer(struct ol_txrx_pdev_t *pdev, + u_int16_t peer_id) +{ + u_int16_t i; + struct ol_txrx_peer_t *peer = NULL; + + for (i = 0; i < pdev->tx_peer_bal.peer_num; i++) { + if (pdev->tx_peer_bal.limit_list[i].peer_id == peer_id) { + pdev->tx_peer_bal.limit_list[i] = + pdev->tx_peer_bal.limit_list[ + pdev->tx_peer_bal.peer_num - 1]; + pdev->tx_peer_bal.peer_num--; + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (peer) + peer->tx_limit_flag = false; + + + TX_SCHED_DEBUG_PRINT( + "Remove one peer from limitq, peer_id %d, cur peer num %d\n", + peer_id, + pdev->tx_peer_bal.peer_num); + break; + } + } + + /* Only stop the timer if no peer in limit state */ + if (pdev->tx_peer_bal.peer_num == 0) { + qdf_timer_stop(&pdev->tx_peer_bal.peer_bal_timer); + pdev->tx_peer_bal.peer_bal_timer_state = + ol_tx_peer_bal_timer_inactive; + } +} + +void +ol_txrx_peer_pause_but_no_mgmt_q(ol_txrx_peer_handle peer) +{ + struct ol_txrx_pdev_t *pdev = peer->vdev->pdev; + + /* TO DO: log the queue pause */ + + /* acquire the mutex lock, since we'll be modifying the queues */ + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + + ol_txrx_peer_pause_but_no_mgmt_q_base(pdev, peer); + + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +void +ol_txrx_peer_unpause_but_no_mgmt_q(ol_txrx_peer_handle peer) +{ + struct ol_txrx_pdev_t *pdev = peer->vdev->pdev; + + /* TO DO: log the queue pause */ + + /* acquire the mutex lock, since we'll be modifying the queues */ + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + + ol_txrx_peer_unpause_but_no_mgmt_q_base(pdev, peer); + + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +u_int16_t +ol_tx_bad_peer_dequeue_check(struct ol_tx_frms_queue_t *txq, + u_int16_t max_frames, + u_int16_t *tx_limit_flag) +{ + if (txq && (txq->peer) && (txq->peer->tx_limit_flag) && + (txq->peer->tx_limit < max_frames)) { + TX_SCHED_DEBUG_PRINT( + "Peer ID %d goes to limit, threshold is %d\n", + txq->peer->peer_ids[0], txq->peer->tx_limit); + *tx_limit_flag = 1; + return txq->peer->tx_limit; + } else { + return max_frames; + } +} + +void +ol_tx_bad_peer_update_tx_limit(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u_int16_t frames, + u_int16_t tx_limit_flag) +{ + if (unlikely(!pdev)) { + TX_SCHED_DEBUG_PRINT_ALWAYS("Error: NULL pdev handler\n"); + return; + } + + if (unlikely(!txq)) { + TX_SCHED_DEBUG_PRINT_ALWAYS("Error: NULL txq\n"); + return; + } + + qdf_spin_lock_bh(&pdev->tx_peer_bal.mutex); + if (tx_limit_flag && (txq->peer) && + (txq->peer->tx_limit_flag)) { + if (txq->peer->tx_limit < frames) + txq->peer->tx_limit = 0; + else + txq->peer->tx_limit -= frames; + + TX_SCHED_DEBUG_PRINT_ALWAYS( + "Peer ID %d in limit, deque %d frms\n", + txq->peer->peer_ids[0], frames); + } else if (txq->peer) { + TX_SCHED_DEBUG_PRINT("Download peer_id %d, num_frames %d\n", + txq->peer->peer_ids[0], frames); + } + qdf_spin_unlock_bh(&pdev->tx_peer_bal.mutex); +} + +void +ol_txrx_bad_peer_txctl_set_setting(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + int enable, int period, int txq_limit) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (enable) + pdev->tx_peer_bal.enabled = ol_tx_peer_bal_enable; + else + pdev->tx_peer_bal.enabled = ol_tx_peer_bal_disable; + + /* Set the current settingl */ + pdev->tx_peer_bal.peer_bal_period_ms = period; + pdev->tx_peer_bal.peer_bal_txq_limit = txq_limit; +} + +void +ol_txrx_bad_peer_txctl_update_threshold(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, int level, + int tput_thresh, int tx_limit) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + /* Set the current settingl */ + pdev->tx_peer_bal.ctl_thresh[level].tput_thresh = + tput_thresh; + pdev->tx_peer_bal.ctl_thresh[level].tx_limit = + tx_limit; +} + +/** + * ol_tx_pdev_peer_bal_timer() - timer function + * @context: context of timer function + * + * Return: None + */ +static void +ol_tx_pdev_peer_bal_timer(void *context) +{ + int i; + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)context; + + qdf_spin_lock_bh(&pdev->tx_peer_bal.mutex); + + for (i = 0; i < pdev->tx_peer_bal.peer_num; i++) { + if (pdev->tx_peer_bal.limit_list[i].limit_flag) { + u_int16_t peer_id = + pdev->tx_peer_bal.limit_list[i].peer_id; + u_int16_t tx_limit = + pdev->tx_peer_bal.limit_list[i].limit; + + struct ol_txrx_peer_t *peer = NULL; + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + TX_SCHED_DEBUG_PRINT( + "%s peer_id %d peer = 0x%x tx limit %d\n", + __func__, peer_id, + (int)peer, tx_limit); + + /* + * It is possible the peer limit is still not 0, + * but it is the scenario should not be cared + */ + if (peer) { + peer->tx_limit = tx_limit; + } else { + ol_txrx_peer_bal_remove_limit_peer(pdev, + peer_id); + TX_SCHED_DEBUG_PRINT_ALWAYS( + "No such a peer, peer id = %d\n", + peer_id); + } + } + } + + qdf_spin_unlock_bh(&pdev->tx_peer_bal.mutex); + + if (pdev->tx_peer_bal.peer_num) { + ol_tx_sched(pdev); + qdf_timer_start(&pdev->tx_peer_bal.peer_bal_timer, + pdev->tx_peer_bal.peer_bal_period_ms); + } +} + +void +ol_txrx_set_txq_peer( + struct ol_tx_frms_queue_t *txq, + struct ol_txrx_peer_t *peer) +{ + if (txq) + txq->peer = peer; +} + +void ol_tx_badpeer_flow_cl_init(struct ol_txrx_pdev_t *pdev) +{ + u_int32_t timer_period; + + qdf_spinlock_create(&pdev->tx_peer_bal.mutex); + pdev->tx_peer_bal.peer_num = 0; + pdev->tx_peer_bal.peer_bal_timer_state + = ol_tx_peer_bal_timer_inactive; + + timer_period = 2000; + pdev->tx_peer_bal.peer_bal_period_ms = timer_period; + + qdf_timer_init( + pdev->osdev, + &pdev->tx_peer_bal.peer_bal_timer, + ol_tx_pdev_peer_bal_timer, + pdev, QDF_TIMER_TYPE_SW); +} + +void ol_tx_badpeer_flow_cl_deinit(struct ol_txrx_pdev_t *pdev) +{ + qdf_timer_stop(&pdev->tx_peer_bal.peer_bal_timer); + pdev->tx_peer_bal.peer_bal_timer_state = + ol_tx_peer_bal_timer_inactive; + qdf_timer_free(&pdev->tx_peer_bal.peer_bal_timer); + qdf_spinlock_destroy(&pdev->tx_peer_bal.mutex); +} + +void +ol_txrx_peer_link_status_handler( + ol_txrx_pdev_handle pdev, + u_int16_t peer_num, + struct rate_report_t *peer_link_status) +{ + u_int16_t i = 0; + struct ol_txrx_peer_t *peer = NULL; + + if (!pdev) { + TX_SCHED_DEBUG_PRINT_ALWAYS("Error: NULL pdev handler\n"); + return; + } + + if (!peer_link_status) { + TX_SCHED_DEBUG_PRINT_ALWAYS( + "Error:NULL link report message. peer num %d\n", + peer_num); + return; + } + + /* Check if bad peer tx flow CL is enabled */ + if (pdev->tx_peer_bal.enabled != ol_tx_peer_bal_enable) { + TX_SCHED_DEBUG_PRINT_ALWAYS( + "Bad peer tx flow CL is not enabled, ignore it\n"); + return; + } + + /* Check peer_num is reasonable */ + if (peer_num > MAX_NO_PEERS_IN_LIMIT) { + TX_SCHED_DEBUG_PRINT_ALWAYS( + "%s: Bad peer_num %d\n", __func__, peer_num); + return; + } + + TX_SCHED_DEBUG_PRINT_ALWAYS("%s: peer_num %d\n", __func__, peer_num); + + for (i = 0; i < peer_num; i++) { + u_int16_t peer_limit, peer_id; + u_int16_t pause_flag, unpause_flag; + u_int32_t peer_phy, peer_tput; + + peer_id = peer_link_status->id; + peer_phy = peer_link_status->phy; + peer_tput = peer_link_status->rate; + + TX_SCHED_DEBUG_PRINT("%s: peer id %d tput %d phy %d\n", + __func__, peer_id, peer_tput, peer_phy); + + /* Sanity check for the PHY mode value */ + if (peer_phy > TXRX_IEEE11_AC) { + TX_SCHED_DEBUG_PRINT_ALWAYS( + "%s: PHY value is illegal: %d, and the peer_id %d\n", + __func__, peer_link_status->phy, peer_id); + continue; + } + pause_flag = false; + unpause_flag = false; + peer_limit = 0; + + /* From now on, PHY, PER info should be all fine */ + qdf_spin_lock_bh(&pdev->tx_peer_bal.mutex); + + /* Update link status analysis for each peer */ + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (peer) { + u_int32_t thresh, limit, phy; + + phy = peer_link_status->phy; + thresh = pdev->tx_peer_bal.ctl_thresh[phy].tput_thresh; + limit = pdev->tx_peer_bal.ctl_thresh[phy].tx_limit; + + if (((peer->tx_pause_flag) || (peer->tx_limit_flag)) && + (peer_tput) && (peer_tput < thresh)) + peer_limit = limit; + + if (peer_limit) { + ol_txrx_peer_bal_add_limit_peer(pdev, peer_id, + peer_limit); + } else if (pdev->tx_peer_bal.peer_num) { + TX_SCHED_DEBUG_PRINT( + "%s: Check if peer_id %d exit limit\n", + __func__, peer_id); + ol_txrx_peer_bal_remove_limit_peer(pdev, + peer_id); + } + if ((peer_tput == 0) && + (peer->tx_pause_flag == false)) { + peer->tx_pause_flag = true; + pause_flag = true; + } else if (peer->tx_pause_flag) { + unpause_flag = true; + peer->tx_pause_flag = false; + } + } else { + TX_SCHED_DEBUG_PRINT( + "%s: Remove peer_id %d from limit list\n", + __func__, peer_id); + ol_txrx_peer_bal_remove_limit_peer(pdev, peer_id); + } + + peer_link_status++; + qdf_spin_unlock_bh(&pdev->tx_peer_bal.mutex); + if (pause_flag) + ol_txrx_peer_pause_but_no_mgmt_q(peer); + else if (unpause_flag) + ol_txrx_peer_unpause_but_no_mgmt_q(peer); + } +} +#endif /* QCA_BAD_PEER_TX_FLOW_CL */ + +/*--- ADDBA triggering functions --------------------------------------------*/ + + +/*=== debug functions =======================================================*/ + +/*--- queue event log -------------------------------------------------------*/ + +#if defined(DEBUG_HL_LOGGING) + +#define negative_sign -1 + +/** + * ol_tx_queue_log_entry_type_info() - log queues entry info + * @type: log entry type + * @size: size + * @align: alignment + * @var_size: variable size record + * + * Return: None + */ +static void +ol_tx_queue_log_entry_type_info( + u_int8_t *type, int *size, int *align, int var_size) +{ + switch (*type) { + case ol_tx_log_entry_type_enqueue: + case ol_tx_log_entry_type_dequeue: + case ol_tx_log_entry_type_queue_free: + *size = sizeof(struct ol_tx_log_queue_add_t); + *align = 2; + break; + + case ol_tx_log_entry_type_queue_state: + *size = offsetof(struct ol_tx_log_queue_state_var_sz_t, data); + *align = 4; + if (var_size) { + /* read the variable-sized record, + * to see how large it is + */ + int align_pad; + struct ol_tx_log_queue_state_var_sz_t *record; + + align_pad = + (*align - (uint32_t)(((unsigned long) type) + 1)) + & (*align - 1); + record = (struct ol_tx_log_queue_state_var_sz_t *) + (type + 1 + align_pad); + *size += record->num_cats_active * + (sizeof(u_int32_t) /* bytes */ + + sizeof(u_int16_t) /* frms */); + } + break; + + /*case ol_tx_log_entry_type_drop:*/ + default: + *size = 0; + *align = 0; + }; +} + +/** + * ol_tx_queue_log_oldest_update() - log oldest record + * @pdev: pointer to txrx handle + * @offset: offset value + * + * Return: None + */ +static void +ol_tx_queue_log_oldest_update(struct ol_txrx_pdev_t *pdev, int offset) +{ + int oldest_record_offset; + + /* + * If the offset of the oldest record is between the current and + * new values of the offset of the newest record, then the oldest + * record has to be dropped from the log to provide room for the + * newest record. + * Advance the offset of the oldest record until it points to a + * record that is beyond the new value of the offset of the newest + * record. + */ + if (!pdev->txq_log.wrapped) + /* + * The log has not even filled up yet - no need to remove + * the oldest record to make room for a new record. + */ + return; + + + if (offset > pdev->txq_log.offset) { + /* + * not wraparound - + * The oldest record offset may have already wrapped around, + * even if the newest record has not. In this case, then + * the oldest record offset is fine where it is. + */ + if (pdev->txq_log.oldest_record_offset == 0) + return; + + oldest_record_offset = pdev->txq_log.oldest_record_offset; + } else + /* wraparound */ + oldest_record_offset = 0; + + + while (oldest_record_offset < offset) { + int size, align, align_pad; + u_int8_t type; + + type = pdev->txq_log.data[oldest_record_offset]; + if (type == ol_tx_log_entry_type_wrap) { + oldest_record_offset = 0; + break; + } + ol_tx_queue_log_entry_type_info( + &pdev->txq_log.data[oldest_record_offset], + &size, &align, 1); + align_pad = + (align - ((oldest_record_offset + 1/*type*/))) + & (align - 1); + /* + * QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + * "TXQ LOG old alloc: offset %d, type %d, size %d (%d)\n", + * oldest_record_offset, type, size, size + 1 + align_pad); + */ + oldest_record_offset += size + 1 + align_pad; + } + if (oldest_record_offset >= pdev->txq_log.size) + oldest_record_offset = 0; + + pdev->txq_log.oldest_record_offset = oldest_record_offset; +} + +/** + * ol_tx_queue_log_alloc() - log data allocation + * @pdev: physical device object + * @type: ol_tx_log_entry_type + * @extra_bytes: extra bytes + * + * + * Return: log element + */ +static void * +ol_tx_queue_log_alloc( + struct ol_txrx_pdev_t *pdev, + u_int8_t type /* ol_tx_log_entry_type */, + int extra_bytes) +{ + int size, align, align_pad; + int offset; + + ol_tx_queue_log_entry_type_info(&type, &size, &align, 0); + size += extra_bytes; + + offset = pdev->txq_log.offset; + align_pad = (align - ((offset + 1/*type*/))) & (align - 1); + + if (pdev->txq_log.size - offset >= size + 1 + align_pad) + /* no need to wrap around */ + goto alloc_found; + + if (!pdev->txq_log.allow_wrap) + return NULL; /* log is full and can't wrap */ + + /* handle wrap-around */ + pdev->txq_log.wrapped = 1; + offset = 0; + align_pad = (align - ((offset + 1/*type*/))) & (align - 1); + /* sanity check that the log is large enough to hold this entry */ + if (pdev->txq_log.size <= size + 1 + align_pad) + return NULL; + + +alloc_found: + ol_tx_queue_log_oldest_update(pdev, offset + size + 1 + align_pad); + if (offset == 0) + pdev->txq_log.data[pdev->txq_log.offset] = + ol_tx_log_entry_type_wrap; + + /* + * QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + * "TXQ LOG new alloc: offset %d, type %d, size %d (%d)\n", + * offset, type, size, size + 1 + align_pad); + */ + pdev->txq_log.data[offset] = type; + pdev->txq_log.offset = offset + size + 1 + align_pad; + if (pdev->txq_log.offset >= pdev->txq_log.size) { + pdev->txq_log.offset = 0; + pdev->txq_log.wrapped = 1; + } + return &pdev->txq_log.data[offset + 1 + align_pad]; +} + +/** + * ol_tx_queue_log_record_display() - show log record of tx queue + * @pdev: pointer to txrx handle + * @offset: offset value + * + * Return: size of record + */ +static int +ol_tx_queue_log_record_display(struct ol_txrx_pdev_t *pdev, int offset) +{ + int size, align, align_pad; + u_int8_t type; + struct ol_txrx_peer_t *peer; + + qdf_spin_lock_bh(&pdev->txq_log_spinlock); + type = pdev->txq_log.data[offset]; + ol_tx_queue_log_entry_type_info( + &pdev->txq_log.data[offset], &size, &align, 1); + align_pad = (align - ((offset + 1/*type*/))) & (align - 1); + + switch (type) { + case ol_tx_log_entry_type_enqueue: + { + struct ol_tx_log_queue_add_t record; + + qdf_mem_copy(&record, + &pdev->txq_log.data[offset + 1 + align_pad], + sizeof(struct ol_tx_log_queue_add_t)); + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + + if (record.peer_id != 0xffff) { + peer = ol_txrx_peer_find_by_id(pdev, + record.peer_id); + if (peer) + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "Q: %6d %5d %3d %4d ("QDF_MAC_ADDR_FMT")", + record.num_frms, record.num_bytes, + record.tid, + record.peer_id, + QDF_MAC_ADDR_REF(peer->mac_addr.raw)); + else + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "Q: %6d %5d %3d %4d", + record.num_frms, record.num_bytes, + record.tid, record.peer_id); + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO, + "Q: %6d %5d %3d from vdev", + record.num_frms, record.num_bytes, + record.tid); + } + break; + } + case ol_tx_log_entry_type_dequeue: + { + struct ol_tx_log_queue_add_t record; + + qdf_mem_copy(&record, + &pdev->txq_log.data[offset + 1 + align_pad], + sizeof(struct ol_tx_log_queue_add_t)); + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + + if (record.peer_id != 0xffff) { + peer = ol_txrx_peer_find_by_id(pdev, record.peer_id); + if (peer) + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "DQ: %6d %5d %3d %4d ("QDF_MAC_ADDR_FMT")", + record.num_frms, record.num_bytes, + record.tid, + record.peer_id, + QDF_MAC_ADDR_REF(peer->mac_addr.raw)); + else + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "DQ: %6d %5d %3d %4d", + record.num_frms, record.num_bytes, + record.tid, record.peer_id); + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO, + "DQ: %6d %5d %3d from vdev", + record.num_frms, record.num_bytes, + record.tid); + } + break; + } + case ol_tx_log_entry_type_queue_free: + { + struct ol_tx_log_queue_add_t record; + + qdf_mem_copy(&record, + &pdev->txq_log.data[offset + 1 + align_pad], + sizeof(struct ol_tx_log_queue_add_t)); + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + + if (record.peer_id != 0xffff) { + peer = ol_txrx_peer_find_by_id(pdev, record.peer_id); + if (peer) + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "F: %6d %5d %3d %4d ("QDF_MAC_ADDR_FMT")", + record.num_frms, record.num_bytes, + record.tid, + record.peer_id, + QDF_MAC_ADDR_REF(peer->mac_addr.raw)); + else + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "F: %6d %5d %3d %4d", + record.num_frms, record.num_bytes, + record.tid, record.peer_id); + } else { + /* shouldn't happen */ + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO, + "Unexpected vdev queue removal\n"); + } + break; + } + + case ol_tx_log_entry_type_queue_state: + { + int i, j; + u_int32_t active_bitmap; + struct ol_tx_log_queue_state_var_sz_t record; + u_int8_t *data; + + qdf_mem_copy(&record, + &pdev->txq_log.data[offset + 1 + align_pad], + sizeof(struct ol_tx_log_queue_state_var_sz_t)); + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "S: bitmap = %#x", + record.active_bitmap); + data = &record.data[0]; + j = 0; + i = 0; + active_bitmap = record.active_bitmap; + while (active_bitmap) { + if (active_bitmap & 0x1) { + u_int16_t frms; + u_int32_t bytes; + + frms = data[0] | (data[1] << 8); + bytes = (data[2] << 0) | (data[3] << 8) | + (data[4] << 16) | (data[5] << 24); + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "cat %2d: %6d %5d", + i, frms, bytes); + data += 6; + j++; + } + i++; + active_bitmap >>= 1; + } + break; + } + + /*case ol_tx_log_entry_type_drop:*/ + + case ol_tx_log_entry_type_wrap: + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + return negative_sign * offset; /* go back to the top */ + + default: + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "*** invalid tx log entry type (%d)\n", type); + return 0; /* error */ + }; + + return size + 1 + align_pad; +} + +/** + * ol_tx_queue_log_display() - show tx queue log + * @pdev: pointer to txrx handle + * + * Return: None + */ +void +ol_tx_queue_log_display(struct ol_txrx_pdev_t *pdev) +{ + int offset; + int unwrap; + + qdf_spin_lock_bh(&pdev->txq_log_spinlock); + offset = pdev->txq_log.oldest_record_offset; + unwrap = pdev->txq_log.wrapped; + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + /* + * In theory, this should use mutex to guard against the offset + * being changed while in use, but since this is just for debugging, + * don't bother. + */ + txrx_nofl_info("Current target credit: %d", + qdf_atomic_read(&pdev->target_tx_credit)); + txrx_nofl_info("Tx queue log:"); + txrx_nofl_info(": Frames Bytes TID PEER"); + + while (unwrap || offset != pdev->txq_log.offset) { + int delta = ol_tx_queue_log_record_display(pdev, offset); + + if (delta == 0) + return; /* error */ + + if (delta < 0) + unwrap = 0; + + offset += delta; + } +} + +void +ol_tx_queue_log_enqueue( + struct ol_txrx_pdev_t *pdev, + struct ol_txrx_msdu_info_t *msdu_info, + int frms, int bytes) +{ + int tid; + u_int16_t peer_id = msdu_info->htt.info.peer_id; + struct ol_tx_log_queue_add_t *log_elem; + + tid = msdu_info->htt.info.ext_tid; + + qdf_spin_lock_bh(&pdev->txq_log_spinlock); + log_elem = ol_tx_queue_log_alloc(pdev, ol_tx_log_entry_type_enqueue, 0); + if (!log_elem) { + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + return; + } + + log_elem->num_frms = frms; + log_elem->num_bytes = bytes; + log_elem->peer_id = peer_id; + log_elem->tid = tid; + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); +} + +void +ol_tx_queue_log_dequeue( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int frms, int bytes) +{ + int ext_tid; + u_int16_t peer_id; + struct ol_tx_log_queue_add_t *log_elem; + + ext_tid = txq->ext_tid; + qdf_spin_lock_bh(&pdev->txq_log_spinlock); + log_elem = ol_tx_queue_log_alloc(pdev, ol_tx_log_entry_type_dequeue, 0); + if (!log_elem) { + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + return; + } + + if (ext_tid < OL_TX_NUM_TIDS) { + struct ol_txrx_peer_t *peer; + struct ol_tx_frms_queue_t *txq_base; + + txq_base = txq - ext_tid; + peer = container_of(txq_base, struct ol_txrx_peer_t, txqs[0]); + peer_id = peer->peer_ids[0]; + } else { + peer_id = ~0; + } + + log_elem->num_frms = frms; + log_elem->num_bytes = bytes; + log_elem->peer_id = peer_id; + log_elem->tid = ext_tid; + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); +} + +void +ol_tx_queue_log_free( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid, int frms, int bytes, bool is_peer_txq) +{ + u_int16_t peer_id; + struct ol_tx_log_queue_add_t *log_elem; + + qdf_spin_lock_bh(&pdev->txq_log_spinlock); + log_elem = ol_tx_queue_log_alloc(pdev, ol_tx_log_entry_type_queue_free, + 0); + if (!log_elem) { + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + return; + } + + if ((tid < OL_TX_NUM_TIDS) && is_peer_txq) { + struct ol_txrx_peer_t *peer; + struct ol_tx_frms_queue_t *txq_base; + + txq_base = txq - tid; + peer = container_of(txq_base, struct ol_txrx_peer_t, txqs[0]); + peer_id = peer->peer_ids[0]; + } else { + peer_id = ~0; + } + + log_elem->num_frms = frms; + log_elem->num_bytes = bytes; + log_elem->peer_id = peer_id; + log_elem->tid = tid; + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); +} + +void +ol_tx_queue_log_sched( + struct ol_txrx_pdev_t *pdev, + int credit, + int *num_cats, + u_int32_t **active_bitmap, + u_int8_t **data) +{ + int data_size; + struct ol_tx_log_queue_state_var_sz_t *log_elem; + + data_size = sizeof(u_int32_t) /* bytes */ + + sizeof(u_int16_t) /* frms */; + data_size *= *num_cats; + + qdf_spin_lock_bh(&pdev->txq_log_spinlock); + log_elem = ol_tx_queue_log_alloc( + pdev, ol_tx_log_entry_type_queue_state, data_size); + if (!log_elem) { + *num_cats = 0; + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); + return; + } + log_elem->num_cats_active = *num_cats; + log_elem->active_bitmap = 0; + log_elem->credit = credit; + + *active_bitmap = &log_elem->active_bitmap; + *data = &log_elem->data[0]; + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); +} + +/** + * ol_tx_queue_log_clear() - clear tx queue log + * @pdev: pointer to txrx handle + * + * Return: None + */ +void +ol_tx_queue_log_clear(struct ol_txrx_pdev_t *pdev) +{ + qdf_spin_lock_bh(&pdev->txq_log_spinlock); + qdf_mem_zero(&pdev->txq_log, sizeof(pdev->txq_log)); + pdev->txq_log.size = OL_TXQ_LOG_SIZE; + pdev->txq_log.oldest_record_offset = 0; + pdev->txq_log.offset = 0; + pdev->txq_log.allow_wrap = 1; + pdev->txq_log.wrapped = 0; + qdf_spin_unlock_bh(&pdev->txq_log_spinlock); +} +#endif /* defined(DEBUG_HL_LOGGING) */ + +/*--- queue state printouts -------------------------------------------------*/ + +#if TXRX_DEBUG_LEVEL > 5 + +/** + * ol_tx_queue_display() - show tx queue info + * @txq: pointer to txq frames + * @indent: indent + * + * Return: None + */ +static void +ol_tx_queue_display(struct ol_tx_frms_queue_t *txq, int indent) +{ + char *state; + + state = (txq->flag == ol_tx_queue_active) ? "active" : "paused"; + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*stxq %pK (%s): %d frms, %d bytes\n", + indent, " ", txq, state, txq->frms, txq->bytes); +} + +void +ol_tx_queues_display(struct ol_txrx_pdev_t *pdev) +{ + struct ol_txrx_vdev_t *vdev; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "pdev %pK tx queues:\n", pdev); + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + struct ol_txrx_peer_t *peer; + int i; + + for (i = 0; i < QDF_ARRAY_SIZE(vdev->txqs); i++) { + if (vdev->txqs[i].frms == 0) + continue; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "vdev %d (%pK), txq %d\n", vdev->vdev_id, + vdev, i); + ol_tx_queue_display(&vdev->txqs[i], 4); + } + TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { + for (i = 0; i < QDF_ARRAY_SIZE(peer->txqs); i++) { + if (peer->txqs[i].frms == 0) + continue; + + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO_LOW, + "peer %d (%pK), txq %d\n", + peer->peer_ids[0], vdev, i); + ol_tx_queue_display(&peer->txqs[i], 6); + } + } + } +} +#endif + +#endif /* defined(CONFIG_HL_SUPPORT) */ + +#if defined(CONFIG_HL_SUPPORT) + +/** + * ol_txrx_pdev_pause() - pause network queues for each vdev + * @pdev: pdev handle + * @reason: reason + * + * Return: none + */ +void ol_txrx_pdev_pause(struct ol_txrx_pdev_t *pdev, uint32_t reason) +{ + struct ol_txrx_vdev_t *vdev = NULL, *tmp; + + TAILQ_FOREACH_SAFE(vdev, &pdev->vdev_list, vdev_list_elem, tmp) { + cdp_fc_vdev_pause(cds_get_context(QDF_MODULE_ID_SOC), + vdev->vdev_id, reason, 0); + } + +} + +/** + * ol_txrx_pdev_unpause() - unpause network queues for each vdev + * @pdev: pdev handle + * @reason: reason + * + * Return: none + */ +void ol_txrx_pdev_unpause(struct ol_txrx_pdev_t *pdev, uint32_t reason) +{ + struct ol_txrx_vdev_t *vdev = NULL, *tmp; + + TAILQ_FOREACH_SAFE(vdev, &pdev->vdev_list, vdev_list_elem, tmp) { + cdp_fc_vdev_unpause(cds_get_context(QDF_MODULE_ID_SOC), + vdev->vdev_id, reason, 0); + } + +} +#endif + +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + +/** + * ol_tx_vdev_has_tx_queue_group() - check for vdev having txq groups + * @group: pointer to tx queue grpup + * @vdev_id: vdev id + * + * Return: true if vedv has txq groups + */ +static bool +ol_tx_vdev_has_tx_queue_group( + struct ol_tx_queue_group_t *group, + u_int8_t vdev_id) +{ + u_int16_t vdev_bitmap; + + vdev_bitmap = OL_TXQ_GROUP_VDEV_ID_MASK_GET(group->membership); + if (OL_TXQ_GROUP_VDEV_ID_BIT_MASK_GET(vdev_bitmap, vdev_id)) + return true; + + return false; +} + +/** + * ol_tx_ac_has_tx_queue_group() - check for ac having txq groups + * @group: pointer to tx queue grpup + * @ac: access category + * + * Return: true if vedv has txq groups + */ +static bool +ol_tx_ac_has_tx_queue_group( + struct ol_tx_queue_group_t *group, + u_int8_t ac) +{ + u_int16_t ac_bitmap; + + ac_bitmap = OL_TXQ_GROUP_AC_MASK_GET(group->membership); + if (OL_TXQ_GROUP_AC_BIT_MASK_GET(ac_bitmap, ac)) + return true; + + return false; +} + +#ifdef FEATURE_HL_DBS_GROUP_CREDIT_SHARING +static inline struct ol_tx_queue_group_t * +ol_tx_txq_find_other_group(struct ol_txrx_pdev_t *pdev, + struct ol_tx_queue_group_t *txq_grp) +{ + int i; + struct ol_tx_queue_group_t *other_grp = NULL; + + for (i = 0; i < OL_TX_MAX_TXQ_GROUPS; i++) { + if (&pdev->txq_grps[i] != txq_grp) { + other_grp = &pdev->txq_grps[i]; + break; + } + } + return other_grp; +} + +u32 ol_tx_txq_group_credit_limit( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u32 credit) +{ + struct ol_tx_queue_group_t *txq_grp = txq->group_ptrs[0]; + struct ol_tx_queue_group_t *other_grp; + u32 ask; + u32 updated_credit; + u32 credit_oth_grp; + + if (qdf_unlikely(!txq_grp)) + return credit; + + updated_credit = qdf_atomic_read(&txq_grp->credit); + + if (credit <= updated_credit) + /* We have enough credits */ + return credit; + + ask = credit - updated_credit; + other_grp = ol_tx_txq_find_other_group(pdev, txq_grp); + if (qdf_unlikely(!other_grp)) + return credit; + + credit_oth_grp = qdf_atomic_read(&other_grp->credit); + if (other_grp->frm_count < credit_oth_grp) { + u32 spare = credit_oth_grp - other_grp->frm_count; + + if (pdev->limit_lend) { + if (spare > pdev->min_reserve) + spare -= pdev->min_reserve; + else + spare = 0; + } + updated_credit += min(spare, ask); + } + return updated_credit; +} + +u32 ol_tx_txq_update_borrowed_group_credits(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u32 credits_used) +{ + struct ol_tx_queue_group_t *txq_grp = txq->group_ptrs[0]; + u32 credits_cur_grp; + u32 credits_brwd; + + if (qdf_unlikely(!txq_grp)) + return credits_used; + + credits_cur_grp = qdf_atomic_read(&txq_grp->credit); + if (credits_used > credits_cur_grp) { + struct ol_tx_queue_group_t *other_grp = + ol_tx_txq_find_other_group(pdev, txq_grp); + + if (qdf_likely(other_grp)) { + credits_brwd = credits_used - credits_cur_grp; + /* + * All the credits were used from the active txq group. + */ + credits_used = credits_cur_grp; + /* Deduct credits borrowed from other group */ + ol_txrx_update_group_credit(other_grp, -credits_brwd, + 0); + } + } + return credits_used; +} +#else /* FEATURE_HL_DBS_GROUP_CREDIT_SHARING */ +u_int32_t ol_tx_txq_group_credit_limit( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u_int32_t credit) +{ + u_int8_t i; + int updated_credit = credit; + + /* + * If this tx queue belongs to a group, check whether the group's + * credit limit is more stringent than the global credit limit. + */ + for (i = 0; i < OL_TX_MAX_GROUPS_PER_QUEUE; i++) { + if (txq->group_ptrs[i]) { + int group_credit; + + group_credit = qdf_atomic_read( + &txq->group_ptrs[i]->credit); + updated_credit = QDF_MIN(updated_credit, group_credit); + } + } + + credit = (updated_credit < 0) ? 0 : updated_credit; + + return credit; +} +#endif /* FEATURE_HL_DBS_GROUP_CREDIT_SHARING */ + +void ol_tx_txq_group_credit_update( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int32_t credit, + u_int8_t absolute) +{ + u_int8_t i; + /* + * If this tx queue belongs to a group then + * update group credit + */ + for (i = 0; i < OL_TX_MAX_GROUPS_PER_QUEUE; i++) { + if (txq->group_ptrs[i]) + ol_txrx_update_group_credit(txq->group_ptrs[i], + credit, absolute); + } + ol_tx_update_group_credit_stats(pdev); +} + +void +ol_tx_set_vdev_group_ptr( + ol_txrx_pdev_handle pdev, + u_int8_t vdev_id, + struct ol_tx_queue_group_t *grp_ptr) +{ + struct ol_txrx_vdev_t *vdev = NULL; + struct ol_txrx_peer_t *peer = NULL; + + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + if (vdev->vdev_id == vdev_id) { + u_int8_t i, j; + + /* update vdev queues group pointers */ + for (i = 0; i < OL_TX_VDEV_NUM_QUEUES; i++) { + for (j = 0; j < OL_TX_MAX_GROUPS_PER_QUEUE; j++) + vdev->txqs[i].group_ptrs[j] = grp_ptr; + } + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + /* Update peer queue group pointers */ + TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { + for (i = 0; i < OL_TX_NUM_TIDS; i++) { + for (j = 0; + j < OL_TX_MAX_GROUPS_PER_QUEUE; + j++) + peer->txqs[i].group_ptrs[j] = + grp_ptr; + } + } + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + break; + } + } +} + +void ol_tx_txq_set_group_ptr( + struct ol_tx_frms_queue_t *txq, + struct ol_tx_queue_group_t *grp_ptr) +{ + u_int8_t i; + + for (i = 0; i < OL_TX_MAX_GROUPS_PER_QUEUE; i++) + txq->group_ptrs[i] = grp_ptr; +} + +void ol_tx_set_peer_group_ptr( + ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer, + u_int8_t vdev_id, + u_int8_t tid) +{ + u_int8_t i, j = 0; + struct ol_tx_queue_group_t *group = NULL; + + for (i = 0; i < OL_TX_MAX_GROUPS_PER_QUEUE; i++) + peer->txqs[tid].group_ptrs[i] = NULL; + + for (i = 0; i < OL_TX_MAX_TXQ_GROUPS; i++) { + group = &pdev->txq_grps[i]; + if (ol_tx_vdev_has_tx_queue_group(group, vdev_id)) { + if (tid < OL_TX_NUM_QOS_TIDS) { + if (ol_tx_ac_has_tx_queue_group( + group, + TXRX_TID_TO_WMM_AC(tid))) { + peer->txqs[tid].group_ptrs[j] = group; + j++; + } + } else { + peer->txqs[tid].group_ptrs[j] = group; + j++; + } + } + if (j >= OL_TX_MAX_GROUPS_PER_QUEUE) + break; + } +} + +u_int32_t ol_tx_get_max_tx_groups_supported(struct ol_txrx_pdev_t *pdev) +{ + return OL_TX_MAX_TXQ_GROUPS; +} +#endif /* FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL */ + +#if defined(FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL) && \ + defined(FEATURE_HL_DBS_GROUP_CREDIT_SHARING) +void ol_tx_update_grp_frm_count(struct ol_tx_frms_queue_t *txq, int num_frms) +{ + int i; + + if (!num_frms || !txq) { + ol_txrx_dbg("Invalid params\n"); + return; + } + + for (i = 0; i < OL_TX_MAX_GROUPS_PER_QUEUE; i++) { + if (txq->group_ptrs[i]) { + txq->group_ptrs[i]->frm_count += num_frms; + qdf_assert(txq->group_ptrs[i]->frm_count >= 0); + } + } +} +#endif + +/*--- End of LL tx throttle queue code ---------------------------------------*/ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_queue.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..0688cb4e9bdb49ac7f06ae3347dbed29c4a221b7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_queue.h @@ -0,0 +1,666 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_tx_queue.h + * @brief API definitions for the tx frame queue module within the data SW. + */ +#ifndef _OL_TX_QUEUE__H_ +#define _OL_TX_QUEUE__H_ + +#include /* qdf_nbuf_t */ +#include /* ol_txrx_vdev_t, etc. */ +#include /* bool */ + +/*--- function prototypes for optional queue log feature --------------------*/ +#if defined(ENABLE_TX_QUEUE_LOG) || \ + (defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT)) + +/** + * ol_tx_queue_log_enqueue() - enqueue tx queue logs + * @pdev: physical device object + * @msdu_info: tx msdu meta data + * @frms: number of frames for which logs need to be enqueued + * @bytes: number of bytes + * + * + * Return: None + */ +void +ol_tx_queue_log_enqueue(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_msdu_info_t *msdu_info, + int frms, int bytes); + +/** + * ol_tx_queue_log_dequeue() - dequeue tx queue logs + * @pdev: physical device object + * @txq: tx queue + * @frms: number of frames for which logs need to be dequeued + * @bytes: number of bytes + * + * + * Return: None + */ +void +ol_tx_queue_log_dequeue(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, int frms, int bytes); + +/** + * ol_tx_queue_log_free() - free tx queue logs + * @pdev: physical device object + * @txq: tx queue + * @tid: tid value + * @frms: number of frames for which logs need to be freed + * @bytes: number of bytes + * @is_peer_txq - peer queue or not + * + * + * Return: None + */ +void +ol_tx_queue_log_free(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid, int frms, int bytes, bool is_peer_txq); + +#else + +static inline void +ol_tx_queue_log_enqueue(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_msdu_info_t *msdu_info, + int frms, int bytes) +{ +} + +static inline void +ol_tx_queue_log_dequeue(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, int frms, int bytes) +{ +} + +static inline void +ol_tx_queue_log_free(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid, int frms, int bytes, bool is_peer_txq) +{ +} + +#endif + +#if defined(CONFIG_HL_SUPPORT) + +/** + * @brief Queue a tx frame to the tid queue. + * + * @param pdev - the data virtual device sending the data + * (for storing the tx desc in the virtual dev's tx_target_list, + * and for accessing the phy dev) + * @param txq - which queue the tx frame gets stored in + * @param tx_desc - tx meta-data, including prev and next ptrs + * @param tx_msdu_info - characteristics of the tx frame + */ +void +ol_tx_enqueue( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + struct ol_tx_desc_t *tx_desc, + struct ol_txrx_msdu_info_t *tx_msdu_info); + +/** + * @brief - remove the specified number of frames from the head of a tx queue + * @details + * This function removes frames from the head of a tx queue, + * and returns them as a NULL-terminated linked list. + * The function will remove frames until one of the following happens: + * 1. The tx queue is empty + * 2. The specified number of frames have been removed + * 3. Removal of more frames would exceed the specified credit limit + * + * @param pdev - the physical device object + * @param txq - which tx queue to remove frames from + * @param head - which contains return linked-list of tx frames (descriptors) + * @param num_frames - maximum number of frames to remove + * @param[in/out] credit - + * input: max credit the dequeued frames can consume + * output: how much credit the dequeued frames consume + * @param[out] bytes - the sum of the sizes of the dequeued frames + * @return number of frames dequeued + */ +u_int16_t +ol_tx_dequeue( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + ol_tx_desc_list *head, + u_int16_t num_frames, + u_int32_t *credit, + int *bytes); + +/** + * @brief - free all of frames from the tx queue while deletion + * @details + * This function frees all of frames from the tx queue. + * This function is called during peer or vdev deletion. + * This function notifies the scheduler, so the scheduler can update + * its state to account for the absence of the queue. + * + * @param pdev - the physical device object, which stores the txqs + * @param txq - which tx queue to free frames from + * @param tid - the extended TID that the queue belongs to + * @param is_peer_txq - peer queue or not + */ +void +ol_tx_queue_free( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid, bool is_peer_txq); + +/** + * @brief - discard pending tx frames from the tx queue + * @details + * This function is called if there are too many queues in tx scheduler. + * This function is called if we wants to flush all pending tx + * queues in tx scheduler. + * + * @param pdev - the physical device object, which stores the txqs + * @param flush_all - flush all pending tx queues if set to true + * @param tx_descs - List Of tx_descs to be discarded will be returned by this + * function + */ + +void +ol_tx_queue_discard( + struct ol_txrx_pdev_t *pdev, + bool flush_all, + ol_tx_desc_list *tx_descs); + +#else + +static inline void +ol_tx_enqueue( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + struct ol_tx_desc_t *tx_desc, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ +} + +static inline u_int16_t +ol_tx_dequeue( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + ol_tx_desc_list *head, + u_int16_t num_frames, + u_int32_t *credit, + int *bytes) +{ + return 0; +} + +static inline void +ol_tx_queue_free( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid, bool is_peer_txq) +{ +} + +static inline void +ol_tx_queue_discard( + struct ol_txrx_pdev_t *pdev, + bool flush_all, + ol_tx_desc_list *tx_descs) +{ +} +#endif /* defined(CONFIG_HL_SUPPORT) */ + +#if (!defined(QCA_LL_LEGACY_TX_FLOW_CONTROL)) && (!defined(CONFIG_HL_SUPPORT)) +static inline +void ol_txrx_vdev_flush(struct cdp_soc_t *soc_hdl, uint8_t vdev_id) +{ +} +#else +/** + * ol_txrx_vdev_flush() - Drop all tx data for the specified virtual device + * @soc_hdl: soc handle + * @vdev_id: vdev id + * + * Returns: none + * + * This function applies primarily to HL systems, but also applies to + * LL systems that use per-vdev tx queues for MCC or thermal throttling. + * This function would typically be used by the ctrl SW after it parks + * a STA vdev and then resumes it, but to a new AP. In this case, though + * the same vdev can be used, any old tx frames queued inside it would be + * stale, and would need to be discarded. + */ +void ol_txrx_vdev_flush(struct cdp_soc_t *soc_hdl, uint8_t vdev_id); +#endif + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || \ + (defined(QCA_LL_TX_FLOW_CONTROL_V2)) || \ + defined(CONFIG_HL_SUPPORT) +/** + * ol_txrx_vdev_pause- Suspend all tx data for the specified virtual device + * soc_hdl: Datapath soc handle + * @vdev_id: id of vdev + * @reason: the reason for which vdev queue is getting paused + * @pause_type: type of pause + * + * Return: none + * + * This function applies primarily to HL systems, but also + * applies to LL systems that use per-vdev tx queues for MCC or + * thermal throttling. As an example, this function could be + * used when a single-channel physical device supports multiple + * channels by jumping back and forth between the channels in a + * time-shared manner. As the device is switched from channel A + * to channel B, the virtual devices that operate on channel A + * will be paused. + */ +void ol_txrx_vdev_pause(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint32_t reason, uint32_t pause_type); + +/** + * ol_txrx_vdev_unpause - Resume tx for the specified virtual device + * soc_hdl: Datapath soc handle + * @vdev_id: id of vdev being unpaused + * @reason: the reason for which vdev queue is getting unpaused + * @pause_type: type of pause + * + * Return: none + * + * This function applies primarily to HL systems, but also applies to + * LL systems that use per-vdev tx queues for MCC or thermal throttling. + */ +void ol_txrx_vdev_unpause(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint32_t reason, uint32_t pause_type); +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) + +void +ol_txrx_peer_bal_add_limit_peer( + struct ol_txrx_pdev_t *pdev, + u_int16_t peer_id, + u_int16_t peer_limit); + +void +ol_txrx_peer_bal_remove_limit_peer( + struct ol_txrx_pdev_t *pdev, + u_int16_t peer_id); + +/** + * ol_txrx_peer_pause_but_no_mgmt_q() - suspend/pause all txqs except + * management queue for a given peer + * @peer: peer device object + * + * Return: None + */ +void +ol_txrx_peer_pause_but_no_mgmt_q(ol_txrx_peer_handle peer); + +/** + * ol_txrx_peer_unpause_but_no_mgmt_q() - unpause all txqs except management + * queue for a given peer + * @peer: peer device object + * + * Return: None + */ +void +ol_txrx_peer_unpause_but_no_mgmt_q(ol_txrx_peer_handle peer); + +/** + * ol_tx_bad_peer_dequeue_check() - retrieve the send limit + * of the tx queue category + * @txq: tx queue of the head of the category list + * @max_frames: send limit of the txq category + * @tx_limit_flag: set true is tx limit is reached + * + * Return: send limit + */ +u_int16_t +ol_tx_bad_peer_dequeue_check(struct ol_tx_frms_queue_t *txq, + u_int16_t max_frames, + u_int16_t *tx_limit_flag); + +/** + * ol_tx_bad_peer_update_tx_limit() - update the send limit of the + * tx queue category + * @pdev: the physical device object + * @txq: tx queue of the head of the category list + * @frames: frames that has been dequeued + * @tx_limit_flag: tx limit reached flag + * + * Return: None + */ +void +ol_tx_bad_peer_update_tx_limit(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u_int16_t frames, + u_int16_t tx_limit_flag); + +/** + * ol_txrx_set_txq_peer() - set peer to the tx queue's peer + * @txq: tx queue for a given tid + * @peer: the peer device object + * + * Return: None + */ +void +ol_txrx_set_txq_peer( + struct ol_tx_frms_queue_t *txq, + struct ol_txrx_peer_t *peer); + +/** + * @brief - initialize the peer balance context + * @param pdev - the physical device object, which stores the txqs + */ +void ol_tx_badpeer_flow_cl_init(struct ol_txrx_pdev_t *pdev); + +/** + * @brief - deinitialize the peer balance context + * @param pdev - the physical device object, which stores the txqs + */ +void ol_tx_badpeer_flow_cl_deinit(struct ol_txrx_pdev_t *pdev); + +#else + +static inline void ol_txrx_peer_bal_add_limit_peer( + struct ol_txrx_pdev_t *pdev, + u_int16_t peer_id, + u_int16_t peer_limit) +{ +} + +static inline void ol_txrx_peer_bal_remove_limit_peer( + struct ol_txrx_pdev_t *pdev, + u_int16_t peer_id) +{ +} + +static inline void ol_txrx_peer_pause_but_no_mgmt_q(ol_txrx_peer_handle peer) +{ +} + +static inline void ol_txrx_peer_unpause_but_no_mgmt_q(ol_txrx_peer_handle peer) +{ +} + +static inline u_int16_t +ol_tx_bad_peer_dequeue_check(struct ol_tx_frms_queue_t *txq, + u_int16_t max_frames, + u_int16_t *tx_limit_flag) +{ + /* just return max_frames */ + return max_frames; +} + +static inline void +ol_tx_bad_peer_update_tx_limit(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u_int16_t frames, + u_int16_t tx_limit_flag) +{ +} + +static inline void +ol_txrx_set_txq_peer( + struct ol_tx_frms_queue_t *txq, + struct ol_txrx_peer_t *peer) +{ +} + +static inline void ol_tx_badpeer_flow_cl_init(struct ol_txrx_pdev_t *pdev) +{ +} + +static inline void ol_tx_badpeer_flow_cl_deinit(struct ol_txrx_pdev_t *pdev) +{ +} + +#endif /* defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) */ + +#if defined(CONFIG_HL_SUPPORT) && defined(DEBUG_HL_LOGGING) + +/** + * ol_tx_queue_log_sched() - start logging of tx queues for HL + * @pdev: physical device object + * @credit: number of credits + * @num_active_tids: number of active tids for which logging needs to be done + * @active_bitmap:bitmap + * @data: buffer + * + * Return: None + */ +void +ol_tx_queue_log_sched(struct ol_txrx_pdev_t *pdev, + int credit, + int *num_active_tids, + uint32_t **active_bitmap, uint8_t **data); +#else + +static inline void +ol_tx_queue_log_sched(struct ol_txrx_pdev_t *pdev, + int credit, + int *num_active_tids, + uint32_t **active_bitmap, uint8_t **data) +{ +} +#endif /* defined(CONFIG_HL_SUPPORT) && defined(DEBUG_HL_LOGGING) */ + +#if defined(CONFIG_HL_SUPPORT) && TXRX_DEBUG_LEVEL > 5 +/** + * @brief - show current state of all tx queues + * @param pdev - the physical device object, which stores the txqs + */ +void +ol_tx_queues_display(struct ol_txrx_pdev_t *pdev); + +#else + +static inline void +ol_tx_queues_display(struct ol_txrx_pdev_t *pdev) +{ +} +#endif + +#define ol_tx_queue_decs_reinit(peer, peer_id) /* no-op */ + +#ifdef QCA_SUPPORT_TX_THROTTLE +void ol_tx_throttle_set_level(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, int level); +void ol_tx_throttle_init_period(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, int period, + uint8_t *dutycycle_level); + +/** + * @brief - initialize the throttle context + * @param pdev - the physical device object, which stores the txqs + */ +void ol_tx_throttle_init(struct ol_txrx_pdev_t *pdev); +#else +static inline void ol_tx_throttle_init(struct ol_txrx_pdev_t *pdev) {} + +static inline void ol_tx_throttle_set_level(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, int level) +{} + +static inline void +ol_tx_throttle_init_period(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + int period, uint8_t *dutycycle_level) +{} +#endif + +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + +static inline bool +ol_tx_if_iterate_next_txq(struct ol_tx_frms_queue_t *first, + struct ol_tx_frms_queue_t *txq) +{ + return (first != txq); +} + +/** + * ol_tx_txq_group_credit_limit() - check for credit limit of a given tx queue + * @pdev: physical device object + * @txq: tx queue for which credit limit needs be to checked + * @credit: number of credits of the selected category + * + * Return: updated credits + */ +u_int32_t ol_tx_txq_group_credit_limit( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u_int32_t credit); + +/** + * ol_tx_txq_group_credit_update() - update group credits of the + * selected catoegory + * @pdev: physical device object + * @txq: tx queue for which credit needs to be updated + * @credit: number of credits by which selected category needs to be updated + * @absolute: TXQ group absolute value + * + * Return: None + */ +void ol_tx_txq_group_credit_update( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int32_t credit, + u_int8_t absolute); + +/** + * ol_tx_set_vdev_group_ptr() - update vdev queues group pointer + * @pdev: physical device object + * @vdev_id: vdev id for which group pointer needs to update + * @grp_ptr: pointer to ol tx queue group which needs to be set for vdev queues + * + * Return: None + */ +void +ol_tx_set_vdev_group_ptr( + ol_txrx_pdev_handle pdev, + u_int8_t vdev_id, + struct ol_tx_queue_group_t *grp_ptr); + +/** + * ol_tx_txq_set_group_ptr() - update tx queue group pointer + * @txq: tx queue of which group pointer needs to update + * @grp_ptr: pointer to ol tx queue group which needs to be + * set for given tx queue + * + * + * Return: None + */ +void +ol_tx_txq_set_group_ptr( + struct ol_tx_frms_queue_t *txq, + struct ol_tx_queue_group_t *grp_ptr); + +/** + * ol_tx_set_peer_group_ptr() - update peer tx queues group pointer + * for a given tid + * @pdev: physical device object + * @peer: peer device object + * @vdev_id: vdev id + * @tid: tid for which group pointer needs to update + * + * + * Return: None + */ +void +ol_tx_set_peer_group_ptr( + ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer, + u_int8_t vdev_id, + u_int8_t tid); +#else + +static inline bool +ol_tx_if_iterate_next_txq(struct ol_tx_frms_queue_t *first, + struct ol_tx_frms_queue_t *txq) +{ + return 0; +} + +static inline +u_int32_t ol_tx_txq_group_credit_limit( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u_int32_t credit) +{ + return credit; +} + +static inline void ol_tx_txq_group_credit_update( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int32_t credit, + u_int8_t absolute) +{ +} + +static inline void +ol_tx_txq_set_group_ptr( + struct ol_tx_frms_queue_t *txq, + struct ol_tx_queue_group_t *grp_ptr) +{ +} + +static inline void +ol_tx_set_peer_group_ptr( + ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer, + u_int8_t vdev_id, + u_int8_t tid) +{ +} +#endif + +#if defined(FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL) && \ + defined(FEATURE_HL_DBS_GROUP_CREDIT_SHARING) +/** + * @brief: Update group frame count + * @details: This function is used to maintain the count of frames + * enqueued in a particular group. + * + * @param: txq - The txq to which the frame is getting enqueued. + * @param: num_frms - Number of frames to be added/removed from the group. + */ +void ol_tx_update_grp_frm_count(struct ol_tx_frms_queue_t *txq, int num_frms); + +u32 ol_tx_txq_update_borrowed_group_credits(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u32 credits_used); +#else +static inline void ol_tx_update_grp_frm_count(struct ol_tx_frms_queue_t *txq, + int num_frms) +{} + +static inline u32 +ol_tx_txq_update_borrowed_group_credits(struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + u32 credits_used) +{ + return credits_used; +} +#endif /* + * FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL && + * FEATURE_HL_DBS_GROUP_CREDIT_SHARING + */ + +#endif /* _OL_TX_QUEUE__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_sched.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_sched.c new file mode 100644 index 0000000000000000000000000000000000000000..ec2742c79c359681209daf7560414015b3a05311 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_sched.c @@ -0,0 +1,1642 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_nbuf_t, etc. */ +#include /* HTT_TX_EXT_TID_MGMT */ +#include /* htt_tx_desc_tid */ +#include /* ol_txrx_vdev_handle */ +#include /* ol_txrx_sync */ +#include /* TXRX_ASSERT1 */ +#include /* pdev stats, etc. */ +#include /* ol_tx_desc */ +#include /* ol_tx_send */ +#include /* OL_TX_SCHED, etc. */ +#include +#include +#include +#include /* qdf_os_mem_alloc_consistent et al */ +#include +#if defined(CONFIG_HL_SUPPORT) + +#if defined(DEBUG_HL_LOGGING) +static void +ol_tx_sched_log(struct ol_txrx_pdev_t *pdev); + +#else +static void +ol_tx_sched_log(struct ol_txrx_pdev_t *pdev) +{ +} +#endif /* defined(DEBUG_HL_LOGGING) */ + +#if DEBUG_HTT_CREDIT +#define OL_TX_DISPATCH_LOG_CREDIT() \ + do { \ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, \ + "TX %d bytes\n", qdf_nbuf_len(msdu)); \ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, \ + " Decrease credit %d - 1 = %d, len:%d.\n", \ + qdf_atomic_read(&pdev->target_tx_credit), \ + qdf_atomic_read(&pdev->target_tx_credit) - 1, \ + qdf_nbuf_len(msdu)); \ + } while (0) +#else +#define OL_TX_DISPATCH_LOG_CREDIT() +#endif + +/*--- generic definitions used by the scheduler framework for all algs ---*/ + +struct ol_tx_sched_ctx { + ol_tx_desc_list head; + int frms; +}; + +typedef TAILQ_HEAD(ol_tx_frms_queue_list_s, ol_tx_frms_queue_t) + ol_tx_frms_queue_list; + + /*--- scheduler algorithm selection ---*/ + + /*--- scheduler options ----------------------------------------------- + * 1. Round-robin scheduler: + * Select the TID that is at the head of the list of active TIDs. + * Select the head tx queue for this TID. + * Move the tx queue to the back of the list of tx queues for + * this TID. + * Move the TID to the back of the list of active TIDs. + * Send as many frames from the tx queue as credit allows. + * 2. Weighted-round-robin advanced scheduler: + * Keep an ordered list of which TID gets selected next. + * Use a weighted-round-robin scheme to determine when to promote + * a TID within this list. + * If a TID at the head of the list is inactive, leave it at the + * head, but check the next TIDs. + * If the credit available is less than the credit threshold for the + * next active TID, don't send anything, and leave the TID at the + * head of the list. + * After a TID is selected, move it to the back of the list. + * Select the head tx queue for this TID. + * Move the tx queue to the back of the list of tx queues for this + * TID. + * Send no more frames than the limit specified for the TID. + */ +#define OL_TX_SCHED_RR 1 +#define OL_TX_SCHED_WRR_ADV 2 + +#ifndef OL_TX_SCHED + /*#define OL_TX_SCHED OL_TX_SCHED_RR*/ +#define OL_TX_SCHED OL_TX_SCHED_WRR_ADV /* default */ +#endif + + +#if OL_TX_SCHED == OL_TX_SCHED_RR + +#define ol_tx_sched_rr_t ol_tx_sched_t + +#define OL_TX_SCHED_NUM_CATEGORIES (OL_TX_NUM_TIDS + OL_TX_VDEV_NUM_QUEUES) + +#define ol_tx_sched_init ol_tx_sched_init_rr +#define ol_tx_sched_select_init(pdev) /* no-op */ +#define ol_tx_sched_select_batch ol_tx_sched_select_batch_rr +#define ol_tx_sched_txq_enqueue ol_tx_sched_txq_enqueue_rr +#define ol_tx_sched_txq_deactivate ol_tx_sched_txq_deactivate_rr +#define ol_tx_sched_category_tx_queues ol_tx_sched_category_tx_queues_rr +#define ol_tx_sched_txq_discard ol_tx_sched_txq_discard_rr +#define ol_tx_sched_category_info ol_tx_sched_category_info_rr +#define ol_tx_sched_discard_select_category \ + ol_tx_sched_discard_select_category_rr + +#elif OL_TX_SCHED == OL_TX_SCHED_WRR_ADV + +#define ol_tx_sched_wrr_adv_t ol_tx_sched_t + +#define OL_TX_SCHED_NUM_CATEGORIES OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES + +#define ol_tx_sched_init ol_tx_sched_init_wrr_adv +#define ol_tx_sched_select_init(pdev) \ + do { \ + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); \ + ol_tx_sched_select_init_wrr_adv(pdev); \ + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); \ + } while (0) +#define ol_tx_sched_select_batch ol_tx_sched_select_batch_wrr_adv +#define ol_tx_sched_txq_enqueue ol_tx_sched_txq_enqueue_wrr_adv +#define ol_tx_sched_txq_deactivate ol_tx_sched_txq_deactivate_wrr_adv +#define ol_tx_sched_category_tx_queues ol_tx_sched_category_tx_queues_wrr_adv +#define ol_tx_sched_txq_discard ol_tx_sched_txq_discard_wrr_adv +#define ol_tx_sched_category_info ol_tx_sched_category_info_wrr_adv +#define ol_tx_sched_discard_select_category \ + ol_tx_sched_discard_select_category_wrr_adv + +#else + +#error Unknown OL TX SCHED specification + +#endif /* OL_TX_SCHED */ + + /*--- round-robin scheduler ----------------------------------------*/ +#if OL_TX_SCHED == OL_TX_SCHED_RR + + /*--- definitions ---*/ + + struct ol_tx_active_queues_in_tid_t { + /* list_elem is used to queue up into up level queues*/ + TAILQ_ENTRY(ol_tx_active_queues_in_tid_t) list_elem; + u_int32_t frms; + u_int32_t bytes; + ol_tx_frms_queue_list head; + bool active; + int tid; + }; + + struct ol_tx_sched_rr_t { + struct ol_tx_active_queues_in_tid_t + tx_active_queues_in_tid_array[OL_TX_NUM_TIDS + + OL_TX_VDEV_NUM_QUEUES]; + TAILQ_HEAD(ol_tx_active_tids_s, ol_tx_active_queues_in_tid_t) + tx_active_tids_list; + u_int8_t discard_weights[OL_TX_NUM_TIDS + + OL_TX_VDEV_NUM_QUEUES]; + }; + +#define TX_SCH_MAX_CREDIT_FOR_THIS_TID(tidq) 16 + +/*--- functions ---*/ + +/* + * The scheduler sync spinlock has been acquired outside this function, + * so there is no need to worry about mutex within this function. + */ +static int +ol_tx_sched_select_batch_rr( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_sched_ctx *sctx, + u_int32_t credit) +{ + struct ol_tx_sched_rr_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_active_queues_in_tid_t *txq_queue; + struct ol_tx_frms_queue_t *next_tq; + u_int16_t frames, used_credits = 0, tx_limit, tx_limit_flag = 0; + int bytes; + + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + + if (TAILQ_EMPTY(&scheduler->tx_active_tids_list)) + return used_credits; + + txq_queue = TAILQ_FIRST(&scheduler->tx_active_tids_list); + + TAILQ_REMOVE(&scheduler->tx_active_tids_list, txq_queue, list_elem); + txq_queue->active = false; + + next_tq = TAILQ_FIRST(&txq_queue->head); + TAILQ_REMOVE(&txq_queue->head, next_tq, list_elem); + + credit = QDF_MIN(credit, TX_SCH_MAX_CREDIT_FOR_THIS_TID(next_tq)); + frames = next_tq->frms; /* download as many frames as credit allows */ + tx_limit = ol_tx_bad_peer_dequeue_check(next_tq, + frames, + &tx_limit_flag); + frames = ol_tx_dequeue( + pdev, next_tq, &sctx->head, tx_limit, &credit, &bytes); + ol_tx_bad_peer_update_tx_limit(pdev, next_tq, frames, tx_limit_flag); + + used_credits = credit; + txq_queue->frms -= frames; + txq_queue->bytes -= bytes; + + if (next_tq->frms > 0) { + TAILQ_INSERT_TAIL(&txq_queue->head, next_tq, list_elem); + TAILQ_INSERT_TAIL( + &scheduler->tx_active_tids_list, + txq_queue, list_elem); + txq_queue->active = true; + } else if (!TAILQ_EMPTY(&txq_queue->head)) { + /* + * This tx queue is empty, but there's another tx queue for the + * same TID that is not empty. + *Thus, the TID as a whole is active. + */ + TAILQ_INSERT_TAIL( + &scheduler->tx_active_tids_list, + txq_queue, list_elem); + txq_queue->active = true; + } + sctx->frms += frames; + + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); + return used_credits; +} + +static inline void +ol_tx_sched_txq_enqueue_rr( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid, + int frms, + int bytes) +{ + struct ol_tx_sched_rr_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_active_queues_in_tid_t *txq_queue; + + txq_queue = &scheduler->tx_active_queues_in_tid_array[tid]; + if (txq->flag != ol_tx_queue_active) + TAILQ_INSERT_TAIL(&txq_queue->head, txq, list_elem); + + txq_queue->frms += frms; + txq_queue->bytes += bytes; + + if (!txq_queue->active) { + TAILQ_INSERT_TAIL( + &scheduler->tx_active_tids_list, + txq_queue, list_elem); + txq_queue->active = true; + } +} + +static inline void +ol_tx_sched_txq_deactivate_rr( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid) +{ + struct ol_tx_sched_rr_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_active_queues_in_tid_t *txq_queue; + + txq_queue = &scheduler->tx_active_queues_in_tid_array[tid]; + txq_queue->frms -= txq->frms; + txq_queue->bytes -= txq->bytes; + + TAILQ_REMOVE(&txq_queue->head, txq, list_elem); + /*if (txq_queue->frms == 0 && txq_queue->active) {*/ + if (TAILQ_EMPTY(&txq_queue->head) && txq_queue->active) { + TAILQ_REMOVE(&scheduler->tx_active_tids_list, txq_queue, + list_elem); + txq_queue->active = false; + } +} + +ol_tx_frms_queue_list * +ol_tx_sched_category_tx_queues_rr(struct ol_txrx_pdev_t *pdev, int tid) +{ + struct ol_tx_sched_rr_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_active_queues_in_tid_t *txq_queue; + + txq_queue = &scheduler->tx_active_queues_in_tid_array[tid]; + return &txq_queue->head; +} + +int +ol_tx_sched_discard_select_category_rr(struct ol_txrx_pdev_t *pdev) +{ + struct ol_tx_sched_rr_t *scheduler; + u_int8_t i, tid = 0; + int max_score = 0; + + scheduler = pdev->tx_sched.scheduler; + /* + * Choose which TID's tx frames to drop next based on two factors: + * 1. Which TID has the most tx frames present + * 2. The TID's priority (high-priority TIDs have a low discard_weight) + */ + for (i = 0; i < (OL_TX_NUM_TIDS + OL_TX_VDEV_NUM_QUEUES); i++) { + int score; + + score = + scheduler->tx_active_queues_in_tid_array[i].frms * + scheduler->discard_weights[i]; + if (max_score == 0 || score > max_score) { + max_score = score; + tid = i; + } + } + return tid; +} + +void +ol_tx_sched_txq_discard_rr( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid, int frames, int bytes) +{ + struct ol_tx_sched_rr_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_active_queues_in_tid_t *txq_queue; + + txq_queue = &scheduler->tx_active_queues_in_tid_array[tid]; + + if (0 == txq->frms) + TAILQ_REMOVE(&txq_queue->head, txq, list_elem); + + txq_queue->frms -= frames; + txq_queue->bytes -= bytes; + if (txq_queue->active == true && txq_queue->frms == 0) { + TAILQ_REMOVE(&scheduler->tx_active_tids_list, txq_queue, + list_elem); + txq_queue->active = false; + } +} + +void +ol_tx_sched_category_info_rr( + struct ol_txrx_pdev_t *pdev, + int cat, int *active, + int *frms, int *bytes) +{ + struct ol_tx_sched_rr_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_active_queues_in_tid_t *txq_queue; + + txq_queue = &scheduler->tx_active_queues_in_tid_array[cat]; + + *active = txq_queue->active; + *frms = txq_queue->frms; + *bytes = txq_queue->bytes; +} + +enum { + ol_tx_sched_discard_weight_voice = 1, + ol_tx_sched_discard_weight_video = 4, + ol_tx_sched_discard_weight_ucast_default = 8, + ol_tx_sched_discard_weight_mgmt_non_qos = 1, /* 0? */ + ol_tx_sched_discard_weight_mcast = 1, /* 0? also for probe & assoc */ +}; + +void * +ol_tx_sched_init_rr( + struct ol_txrx_pdev_t *pdev) +{ + struct ol_tx_sched_rr_t *scheduler; + int i; + + scheduler = qdf_mem_malloc(sizeof(struct ol_tx_sched_rr_t)); + if (!scheduler) + return scheduler; + + for (i = 0; i < (OL_TX_NUM_TIDS + OL_TX_VDEV_NUM_QUEUES); i++) { + scheduler->tx_active_queues_in_tid_array[i].tid = i; + TAILQ_INIT(&scheduler->tx_active_queues_in_tid_array[i].head); + scheduler->tx_active_queues_in_tid_array[i].active = 0; + scheduler->tx_active_queues_in_tid_array[i].frms = 0; + scheduler->tx_active_queues_in_tid_array[i].bytes = 0; + } + for (i = 0; i < OL_TX_NUM_TIDS; i++) { + scheduler->tx_active_queues_in_tid_array[i].tid = i; + if (i < OL_TX_NON_QOS_TID) { + int ac = TXRX_TID_TO_WMM_AC(i); + + switch (ac) { + case TXRX_WMM_AC_VO: + scheduler->discard_weights[i] = + ol_tx_sched_discard_weight_voice; + case TXRX_WMM_AC_VI: + scheduler->discard_weights[i] = + ol_tx_sched_discard_weight_video; + default: + scheduler->discard_weights[i] = + ol_tx_sched_discard_weight_ucast_default; + }; + } else { + scheduler->discard_weights[i] = + ol_tx_sched_discard_weight_mgmt_non_qos; + } + } + for (i = 0; i < OL_TX_VDEV_NUM_QUEUES; i++) { + int j = i + OL_TX_NUM_TIDS; + + scheduler->tx_active_queues_in_tid_array[j].tid = + OL_TX_NUM_TIDS - 1; + scheduler->discard_weights[j] = + ol_tx_sched_discard_weight_mcast; + } + TAILQ_INIT(&scheduler->tx_active_tids_list); + + return scheduler; +} + +void +ol_txrx_set_wmm_param(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + struct ol_tx_wmm_param_t wmm_param) +{ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "Dummy function when OL_TX_SCHED_RR is enabled\n"); +} + +/** + * ol_tx_sched_stats_display() - tx sched stats display + * @pdev: Pointer to the PDEV structure. + * + * Return: none. + */ +void ol_tx_sched_stats_display(struct ol_txrx_pdev_t *pdev) +{ +} + +/** + * ol_tx_sched_cur_state_display() - tx sched cur stat display + * @pdev: Pointer to the PDEV structure. + * + * Return: none. + */ +void ol_tx_sched_cur_state_display(struct ol_txrx_pdev_t *pdev) +{ +} + +/** + * ol_tx_sched_cur_state_display() - reset tx sched stats + * @pdev: Pointer to the PDEV structure. + * + * Return: none. + */ +void ol_tx_sched_stats_clear(struct ol_txrx_pdev_t *pdev) +{ +} + +#endif /* OL_TX_SCHED == OL_TX_SCHED_RR */ + +/*--- advanced scheduler ----------------------------------------------------*/ +#if OL_TX_SCHED == OL_TX_SCHED_WRR_ADV + +/*--- definitions ---*/ + +struct ol_tx_sched_wrr_adv_category_info_t { + struct { + int wrr_skip_weight; + u_int32_t credit_threshold; + u_int16_t send_limit; + int credit_reserve; + int discard_weight; + } specs; + struct { + int wrr_count; + int frms; + int bytes; + ol_tx_frms_queue_list head; + bool active; + } state; +#ifdef DEBUG_HL_LOGGING + struct { + char *cat_name; + unsigned int queued; + unsigned int dispatched; + unsigned int discard; + } stat; +#endif +}; + +#define OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(cat, \ + wrr_skip_weight, \ + credit_threshold, \ + send_limit, \ + credit_reserve, \ + discard_weights) \ + enum { OL_TX_SCHED_WRR_ADV_ ## cat ## _WRR_SKIP_WEIGHT = \ + (wrr_skip_weight) }; \ + enum { OL_TX_SCHED_WRR_ADV_ ## cat ## _CREDIT_THRESHOLD = \ + (credit_threshold) }; \ + enum { OL_TX_SCHED_WRR_ADV_ ## cat ## _SEND_LIMIT = \ + (send_limit) }; \ + enum { OL_TX_SCHED_WRR_ADV_ ## cat ## _CREDIT_RESERVE = \ + (credit_reserve) }; \ + enum { OL_TX_SCHED_WRR_ADV_ ## cat ## _DISCARD_WEIGHT = \ + (discard_weights) }; +/* Rome: + * For high-volume traffic flows (VI, BE, BK), use a credit threshold + * roughly equal to a large A-MPDU (occupying half the target memory + * available for holding tx frames) to download AMPDU-sized batches + * of traffic. + * For high-priority, low-volume traffic flows (VO and mgmt), use no + * credit threshold, to minimize download latency. + */ +/* WRR send + * skip credit limit credit disc + * wts thresh (frms) reserv wts + */ +#ifdef HIF_SDIO +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(VO, 1, 17, 24, 0, 1); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(VI, 3, 17, 16, 1, 4); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(BE, 10, 17, 16, 1, 8); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(BK, 12, 6, 6, 1, 8); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(NON_QOS_DATA,10, 17, 16, 1, 8); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(UCAST_MGMT, 1, 1, 4, 0, 1); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(MCAST_DATA, 10, 17, 4, 1, 4); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(MCAST_MGMT, 1, 1, 4, 0, 1); +#else +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(VO, 1, 16, 24, 0, 1); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(VI, 3, 16, 16, 1, 4); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(BE, 10, 12, 12, 1, 8); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(BK, 12, 6, 6, 1, 8); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(NON_QOS_DATA, 12, 6, 4, 1, 8); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(UCAST_MGMT, 1, 1, 4, 0, 1); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(MCAST_DATA, 10, 16, 4, 1, 4); +OL_TX_SCHED_WRR_ADV_CAT_CFG_SPEC(MCAST_MGMT, 1, 1, 4, 0, 1); +#endif + +#ifdef DEBUG_HL_LOGGING + +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_INIT(category, scheduler) \ + do { \ + scheduler->categories[OL_TX_SCHED_WRR_ADV_CAT_ ## category] \ + .stat.queued = 0; \ + scheduler->categories[OL_TX_SCHED_WRR_ADV_CAT_ ## category] \ + .stat.discard = 0; \ + scheduler->categories[OL_TX_SCHED_WRR_ADV_CAT_ ## category] \ + .stat.dispatched = 0; \ + scheduler->categories[OL_TX_SCHED_WRR_ADV_CAT_ ## category] \ + .stat.cat_name = #category; \ + } while (0) +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_INC_QUEUED(category, frms) \ + category->stat.queued += frms; +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_INC_DISCARD(category, frms) \ + category->stat.discard += frms; +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_INC_DISPATCHED(category, frms) \ + category->stat.dispatched += frms; +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_DUMP(scheduler) \ + ol_tx_sched_wrr_adv_cat_stat_dump(scheduler) +#define OL_TX_SCHED_WRR_ADV_CAT_CUR_STATE_DUMP(scheduler) \ + ol_tx_sched_wrr_adv_cat_cur_state_dump(scheduler) +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_CLEAR(scheduler) \ + ol_tx_sched_wrr_adv_cat_stat_clear(scheduler) + +#else /* DEBUG_HL_LOGGING */ + +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_INIT(category, scheduler) +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_INC_QUEUED(category, frms) +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_INC_DISCARD(category, frms) +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_INC_DISPATCHED(category, frms) +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_DUMP(scheduler) +#define OL_TX_SCHED_WRR_ADV_CAT_CUR_STATE_DUMP(scheduler) +#define OL_TX_SCHED_WRR_ADV_CAT_STAT_CLEAR(scheduler) + +#endif /* DEBUG_HL_LOGGING */ + +#define OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(category, scheduler) \ + do { \ + scheduler->categories[OL_TX_SCHED_WRR_ADV_CAT_ ## category] \ + .specs.wrr_skip_weight = \ + OL_TX_SCHED_WRR_ADV_ ## category ## _WRR_SKIP_WEIGHT; \ + scheduler->categories[OL_TX_SCHED_WRR_ADV_CAT_ ## category] \ + .specs.credit_threshold = \ + OL_TX_SCHED_WRR_ADV_ ## category ## _CREDIT_THRESHOLD; \ + scheduler->categories[OL_TX_SCHED_WRR_ADV_CAT_ ## category] \ + .specs.send_limit = \ + OL_TX_SCHED_WRR_ADV_ ## category ## _SEND_LIMIT; \ + scheduler->categories[OL_TX_SCHED_WRR_ADV_CAT_ ## category] \ + .specs.credit_reserve = \ + OL_TX_SCHED_WRR_ADV_ ## category ## _CREDIT_RESERVE; \ + scheduler->categories[OL_TX_SCHED_WRR_ADV_CAT_ ## category] \ + .specs.discard_weight = \ + OL_TX_SCHED_WRR_ADV_ ## category ## _DISCARD_WEIGHT; \ + OL_TX_SCHED_WRR_ADV_CAT_STAT_INIT(category, scheduler); \ + } while (0) + +struct ol_tx_sched_wrr_adv_t { + int order[OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES]; + int index; + struct ol_tx_sched_wrr_adv_category_info_t + categories[OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES]; +}; + +#define OL_TX_AIFS_DEFAULT_VO 2 +#define OL_TX_AIFS_DEFAULT_VI 2 +#define OL_TX_AIFS_DEFAULT_BE 3 +#define OL_TX_AIFS_DEFAULT_BK 7 +#define OL_TX_CW_MIN_DEFAULT_VO 3 +#define OL_TX_CW_MIN_DEFAULT_VI 7 +#define OL_TX_CW_MIN_DEFAULT_BE 15 +#define OL_TX_CW_MIN_DEFAULT_BK 15 + +/*--- functions ---*/ + +#ifdef DEBUG_HL_LOGGING +static void ol_tx_sched_wrr_adv_cat_stat_dump( + struct ol_tx_sched_wrr_adv_t *scheduler) +{ + int i; + + txrx_nofl_info("Scheduler Stats:"); + txrx_nofl_info("====category(CRR,CRT,WSW): Queued Discard Dequeued frms wrr==="); + for (i = 0; i < OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES; ++i) { + txrx_nofl_info("%12s(%2d, %2d, %2d): %6d %7d %8d %4d %3d", + scheduler->categories[i].stat.cat_name, + scheduler->categories[i].specs.credit_reserve, + scheduler->categories[i].specs. + credit_threshold, + scheduler->categories[i]. + specs.wrr_skip_weight, + scheduler->categories[i].stat.queued, + scheduler->categories[i].stat.discard, + scheduler->categories[i].stat.dispatched, + scheduler->categories[i].state.frms, + scheduler->categories[i].state.wrr_count); + } +} + +static void ol_tx_sched_wrr_adv_cat_cur_state_dump( + struct ol_tx_sched_wrr_adv_t *scheduler) +{ + int i; + + txrx_nofl_info("Scheduler State Snapshot:"); + txrx_nofl_info("====category(CRR,CRT,WSW): IS_Active Pend_Frames Pend_bytes wrr==="); + for (i = 0; i < OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES; ++i) { + txrx_nofl_info("%12s(%2d, %2d, %2d): %9d %11d %10d %3d", + scheduler->categories[i].stat.cat_name, + scheduler->categories[i].specs.credit_reserve, + scheduler->categories[i].specs. + credit_threshold, + scheduler->categories[i].specs. + wrr_skip_weight, + scheduler->categories[i].state.active, + scheduler->categories[i].state.frms, + scheduler->categories[i].state.bytes, + scheduler->categories[i].state.wrr_count); + } +} + +static void ol_tx_sched_wrr_adv_cat_stat_clear( + struct ol_tx_sched_wrr_adv_t *scheduler) +{ + int i; + + for (i = 0; i < OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES; ++i) { + scheduler->categories[i].stat.queued = 0; + scheduler->categories[i].stat.discard = 0; + scheduler->categories[i].stat.dispatched = 0; + } +} + +#endif + +static void +ol_tx_sched_select_init_wrr_adv(struct ol_txrx_pdev_t *pdev) +{ + struct ol_tx_sched_wrr_adv_t *scheduler = pdev->tx_sched.scheduler; + /* start selection from the front of the ordered list */ + scheduler->index = 0; +} + +static void +ol_tx_sched_wrr_adv_rotate_order_list_tail( + struct ol_tx_sched_wrr_adv_t *scheduler, int idx) +{ + int value; + /* remember the value of the specified element */ + value = scheduler->order[idx]; + /* shift all further elements up one space */ + for (; idx < OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES-1; idx++) + scheduler->order[idx] = scheduler->order[idx + 1]; + + /* put the specified element at the end */ + scheduler->order[idx] = value; +} + +static void +ol_tx_sched_wrr_adv_credit_sanity_check(struct ol_txrx_pdev_t *pdev, + u_int32_t credit) +{ + struct ol_tx_sched_wrr_adv_t *scheduler = pdev->tx_sched.scheduler; + int i; + int okay = 1; + + for (i = 0; i < OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES; i++) { + if (scheduler->categories[i].specs.credit_threshold > credit) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "*** Config error: credit (%d) not enough to support category %d threshold (%d)\n", + credit, i, + scheduler->categories[i].specs. + credit_threshold); + okay = 0; + } + } + qdf_assert(okay); +} + +/* + * The scheduler sync spinlock has been acquired outside this function, + * so there is no need to worry about mutex within this function. + */ +static int +ol_tx_sched_select_batch_wrr_adv( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_sched_ctx *sctx, + u_int32_t credit) +{ + static int first = 1; + int category_index = 0; + struct ol_tx_sched_wrr_adv_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_frms_queue_t *txq, *first_txq = NULL; + int index; + struct ol_tx_sched_wrr_adv_category_info_t *category = NULL; + int frames, bytes, used_credits = 0, tx_limit; + u_int16_t tx_limit_flag; + u32 credit_rem = credit; + + /* + * Just for good measure, do a sanity check that the initial credit + * is enough to cover every category's credit threshold. + */ + if (first) { + first = 0; + ol_tx_sched_wrr_adv_credit_sanity_check(pdev, credit); + } + + /* choose the traffic category from the ordered list */ + index = scheduler->index; + while (index < OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES) { + category_index = scheduler->order[index]; + category = &scheduler->categories[category_index]; + if (!category->state.active) { + /* move on to the next category */ + index++; + continue; + } + if (++category->state.wrr_count < + category->specs.wrr_skip_weight) { + /* skip this cateogry (move it to the back) */ + ol_tx_sched_wrr_adv_rotate_order_list_tail(scheduler, + index); + /* + * try again (iterate) on the new element + * that was moved up + */ + continue; + } + /* found the first active category whose WRR turn is present */ + break; + } + if (index >= OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES) { + /* no categories are active */ + return 0; + } + + /* is there enough credit for the selected category? */ + if (credit < category->specs.credit_threshold) { + /* + * Can't send yet - wait until more credit becomes available. + * In the meantime, restore the WRR counter (since we didn't + * service this category after all). + */ + category->state.wrr_count = category->state.wrr_count - 1; + return 0; + } + /* enough credit is available - go ahead and send some frames */ + /* + * This category was serviced - reset the WRR counter, and move this + * category to the back of the order list. + */ + category->state.wrr_count = 0; + ol_tx_sched_wrr_adv_rotate_order_list_tail(scheduler, index); + /* + * With this category moved to the back, if there's still any credit + * left, set up the next invocation of this function to start from + * where this one left off, by looking at the category that just got + * shifted forward into the position the service category was + * occupying. + */ + scheduler->index = index; + + /* + * Take the tx queue from the head of the category list. + */ + txq = TAILQ_FIRST(&category->state.head); + + while (txq) { + TAILQ_REMOVE(&category->state.head, txq, list_elem); + credit = ol_tx_txq_group_credit_limit(pdev, txq, credit); + if (credit > category->specs.credit_reserve) { + credit -= category->specs.credit_reserve; + tx_limit = ol_tx_bad_peer_dequeue_check(txq, + category->specs.send_limit, + &tx_limit_flag); + frames = ol_tx_dequeue( + pdev, txq, &sctx->head, + tx_limit, &credit, &bytes); + ol_tx_bad_peer_update_tx_limit(pdev, txq, + frames, + tx_limit_flag); + + OL_TX_SCHED_WRR_ADV_CAT_STAT_INC_DISPATCHED(category, + frames); + /* Update used global credits */ + used_credits = credit; + credit = + ol_tx_txq_update_borrowed_group_credits(pdev, txq, + credit); + category->state.frms -= frames; + category->state.bytes -= bytes; + if (txq->frms > 0) { + TAILQ_INSERT_TAIL(&category->state.head, + txq, list_elem); + } else { + if (category->state.frms == 0) + category->state.active = 0; + } + sctx->frms += frames; + ol_tx_txq_group_credit_update(pdev, txq, -credit, 0); + break; + } else { + /* + * Current txq belongs to a group which does not have + * enough credits, + * Iterate over to next txq and see if we can download + * packets from that queue. + */ + if (ol_tx_if_iterate_next_txq(first_txq, txq)) { + credit = credit_rem; + if (!first_txq) + first_txq = txq; + + TAILQ_INSERT_TAIL(&category->state.head, + txq, list_elem); + + txq = TAILQ_FIRST(&category->state.head); + } else { + TAILQ_INSERT_HEAD(&category->state.head, txq, + list_elem); + break; + } + } + } /* while(txq) */ + + return used_credits; +} + +static inline void +ol_tx_sched_txq_enqueue_wrr_adv( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid, + int frms, + int bytes) +{ + struct ol_tx_sched_wrr_adv_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_sched_wrr_adv_category_info_t *category; + + category = &scheduler->categories[pdev->tid_to_ac[tid]]; + category->state.frms += frms; + category->state.bytes += bytes; + OL_TX_SCHED_WRR_ADV_CAT_STAT_INC_QUEUED(category, frms); + if (txq->flag != ol_tx_queue_active) { + TAILQ_INSERT_TAIL(&category->state.head, txq, list_elem); + category->state.active = 1; /* may have already been active */ + } +} + +static inline void +ol_tx_sched_txq_deactivate_wrr_adv( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int tid) +{ + struct ol_tx_sched_wrr_adv_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_sched_wrr_adv_category_info_t *category; + + category = &scheduler->categories[pdev->tid_to_ac[tid]]; + category->state.frms -= txq->frms; + category->state.bytes -= txq->bytes; + + TAILQ_REMOVE(&category->state.head, txq, list_elem); + + if (category->state.frms == 0 && category->state.active) + category->state.active = 0; +} + +static ol_tx_frms_queue_list * +ol_tx_sched_category_tx_queues_wrr_adv(struct ol_txrx_pdev_t *pdev, int cat) +{ + struct ol_tx_sched_wrr_adv_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_sched_wrr_adv_category_info_t *category; + + category = &scheduler->categories[cat]; + return &category->state.head; +} + +static int +ol_tx_sched_discard_select_category_wrr_adv(struct ol_txrx_pdev_t *pdev) +{ + struct ol_tx_sched_wrr_adv_t *scheduler; + u_int8_t i, cat = 0; + int max_score = 0; + + scheduler = pdev->tx_sched.scheduler; + /* + * Choose which category's tx frames to drop next based on two factors: + * 1. Which category has the most tx frames present + * 2. The category's priority (high-priority categories have a low + * discard_weight) + */ + for (i = 0; i < OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES; i++) { + int score; + + score = + scheduler->categories[i].state.frms * + scheduler->categories[i].specs.discard_weight; + if (max_score == 0 || score > max_score) { + max_score = score; + cat = i; + } + } + return cat; +} + +static void +ol_tx_sched_txq_discard_wrr_adv( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_frms_queue_t *txq, + int cat, int frames, int bytes) +{ + struct ol_tx_sched_wrr_adv_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_sched_wrr_adv_category_info_t *category; + + category = &scheduler->categories[cat]; + + if (0 == txq->frms) + TAILQ_REMOVE(&category->state.head, txq, list_elem); + + + category->state.frms -= frames; + category->state.bytes -= bytes; + OL_TX_SCHED_WRR_ADV_CAT_STAT_INC_DISCARD(category, frames); + if (category->state.frms == 0) + category->state.active = 0; +} + +static void +ol_tx_sched_category_info_wrr_adv( + struct ol_txrx_pdev_t *pdev, + int cat, int *active, + int *frms, int *bytes) +{ + struct ol_tx_sched_wrr_adv_t *scheduler = pdev->tx_sched.scheduler; + struct ol_tx_sched_wrr_adv_category_info_t *category; + + category = &scheduler->categories[cat]; + *active = category->state.active; + *frms = category->state.frms; + *bytes = category->state.bytes; +} + +/** + * ol_tx_sched_wrr_param_update() - update the WRR TX sched params + * @pdev: Pointer to PDEV structure. + * @scheduler: Pointer to tx scheduler. + * + * Update the WRR TX schedule parameters for each category if it is + * specified in the ini file by user. + * + * Return: none + */ +static void ol_tx_sched_wrr_param_update(struct ol_txrx_pdev_t *pdev, + struct ol_tx_sched_wrr_adv_t * + scheduler) +{ + int i; + static const char * const tx_sched_wrr_name[4] = { + "BE", + "BK", + "VI", + "VO" + }; + + if (!scheduler) + return; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "%s: Tuning the TX scheduler wrr parameters by ini file:", + __func__); + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " skip credit limit credit disc"); + + for (i = OL_TX_SCHED_WRR_ADV_CAT_BE; + i <= OL_TX_SCHED_WRR_ADV_CAT_VO; i++) { + if (ol_cfg_get_wrr_skip_weight(pdev->ctrl_pdev, i)) { + scheduler->categories[i].specs.wrr_skip_weight = + ol_cfg_get_wrr_skip_weight(pdev->ctrl_pdev, i); + scheduler->categories[i].specs.credit_threshold = + ol_cfg_get_credit_threshold(pdev->ctrl_pdev, i); + scheduler->categories[i].specs.send_limit = + ol_cfg_get_send_limit(pdev->ctrl_pdev, i); + scheduler->categories[i].specs.credit_reserve = + ol_cfg_get_credit_reserve(pdev->ctrl_pdev, i); + scheduler->categories[i].specs.discard_weight = + ol_cfg_get_discard_weight(pdev->ctrl_pdev, i); + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "%s-update: %d, %d, %d, %d, %d", + tx_sched_wrr_name[i], + scheduler->categories[i].specs.wrr_skip_weight, + scheduler->categories[i].specs.credit_threshold, + scheduler->categories[i].specs.send_limit, + scheduler->categories[i].specs.credit_reserve, + scheduler->categories[i].specs.discard_weight); + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "%s-orig: %d, %d, %d, %d, %d", + tx_sched_wrr_name[i], + scheduler->categories[i].specs.wrr_skip_weight, + scheduler->categories[i].specs.credit_threshold, + scheduler->categories[i].specs.send_limit, + scheduler->categories[i].specs.credit_reserve, + scheduler->categories[i].specs.discard_weight); + } + } +} + +static void * +ol_tx_sched_init_wrr_adv( + struct ol_txrx_pdev_t *pdev) +{ + struct ol_tx_sched_wrr_adv_t *scheduler; + int i; + + scheduler = qdf_mem_malloc( + sizeof(struct ol_tx_sched_wrr_adv_t)); + if (!scheduler) + return scheduler; + + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(VO, scheduler); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(VI, scheduler); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(BE, scheduler); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(BK, scheduler); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(NON_QOS_DATA, scheduler); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(UCAST_MGMT, scheduler); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(MCAST_DATA, scheduler); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(MCAST_MGMT, scheduler); + + ol_tx_sched_wrr_param_update(pdev, scheduler); + + for (i = 0; i < OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES; i++) { + scheduler->categories[i].state.active = 0; + scheduler->categories[i].state.frms = 0; + /*scheduler->categories[i].state.bytes = 0;*/ + TAILQ_INIT(&scheduler->categories[i].state.head); + /* + * init categories to not be skipped before + * their initial selection + */ + scheduler->categories[i].state.wrr_count = + scheduler->categories[i].specs.wrr_skip_weight - 1; + } + + /* + * Init the order array - the initial ordering doesn't matter, as the + * order array will get reshuffled as data arrives. + */ + for (i = 0; i < OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES; i++) + scheduler->order[i] = i; + + return scheduler; +} + + +/* WMM parameters are suppposed to be passed when associate with AP. + * According to AIFS+CWMin, the function maps each queue to one of four default + * settings of the scheduler, ie. VO, VI, BE, or BK. + */ +void +ol_txrx_set_wmm_param(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + struct ol_tx_wmm_param_t wmm_param) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle data_pdev = + ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_tx_sched_wrr_adv_t def_cfg; + struct ol_tx_sched_wrr_adv_t *scheduler = + data_pdev->tx_sched.scheduler; + u_int32_t i, ac_selected; + u_int32_t weight[QCA_WLAN_AC_ALL], default_edca[QCA_WLAN_AC_ALL]; + + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(VO, (&def_cfg)); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(VI, (&def_cfg)); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(BE, (&def_cfg)); + OL_TX_SCHED_WRR_ADV_CAT_CFG_STORE(BK, (&def_cfg)); + + /* default_eca = AIFS + CWMin */ + default_edca[OL_TX_SCHED_WRR_ADV_CAT_VO] = + OL_TX_AIFS_DEFAULT_VO + OL_TX_CW_MIN_DEFAULT_VO; + default_edca[OL_TX_SCHED_WRR_ADV_CAT_VI] = + OL_TX_AIFS_DEFAULT_VI + OL_TX_CW_MIN_DEFAULT_VI; + default_edca[OL_TX_SCHED_WRR_ADV_CAT_BE] = + OL_TX_AIFS_DEFAULT_BE + OL_TX_CW_MIN_DEFAULT_BE; + default_edca[OL_TX_SCHED_WRR_ADV_CAT_BK] = + OL_TX_AIFS_DEFAULT_BK + OL_TX_CW_MIN_DEFAULT_BK; + + weight[OL_TX_SCHED_WRR_ADV_CAT_VO] = + wmm_param.ac[QCA_WLAN_AC_VO].aifs + + wmm_param.ac[QCA_WLAN_AC_VO].cwmin; + weight[OL_TX_SCHED_WRR_ADV_CAT_VI] = + wmm_param.ac[QCA_WLAN_AC_VI].aifs + + wmm_param.ac[QCA_WLAN_AC_VI].cwmin; + weight[OL_TX_SCHED_WRR_ADV_CAT_BK] = + wmm_param.ac[QCA_WLAN_AC_BK].aifs + + wmm_param.ac[QCA_WLAN_AC_BK].cwmin; + weight[OL_TX_SCHED_WRR_ADV_CAT_BE] = + wmm_param.ac[QCA_WLAN_AC_BE].aifs + + wmm_param.ac[QCA_WLAN_AC_BE].cwmin; + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + if (default_edca[OL_TX_SCHED_WRR_ADV_CAT_VO] >= weight[i]) + ac_selected = OL_TX_SCHED_WRR_ADV_CAT_VO; + else if (default_edca[OL_TX_SCHED_WRR_ADV_CAT_VI] >= weight[i]) + ac_selected = OL_TX_SCHED_WRR_ADV_CAT_VI; + else if (default_edca[OL_TX_SCHED_WRR_ADV_CAT_BE] >= weight[i]) + ac_selected = OL_TX_SCHED_WRR_ADV_CAT_BE; + else + ac_selected = OL_TX_SCHED_WRR_ADV_CAT_BK; + + + scheduler->categories[i].specs.wrr_skip_weight = + def_cfg.categories[ac_selected].specs.wrr_skip_weight; + scheduler->categories[i].specs.credit_threshold = + def_cfg.categories[ac_selected].specs.credit_threshold; + scheduler->categories[i].specs.send_limit = + def_cfg.categories[ac_selected].specs.send_limit; + scheduler->categories[i].specs.credit_reserve = + def_cfg.categories[ac_selected].specs.credit_reserve; + scheduler->categories[i].specs.discard_weight = + def_cfg.categories[ac_selected].specs.discard_weight; + } +} + +/** + * ol_tx_sched_stats_display() - tx sched stats display + * @pdev: Pointer to the PDEV structure. + * + * Return: none. + */ +void ol_tx_sched_stats_display(struct ol_txrx_pdev_t *pdev) +{ + OL_TX_SCHED_WRR_ADV_CAT_STAT_DUMP(pdev->tx_sched.scheduler); +} + +/** + * ol_tx_sched_cur_state_display() - tx sched cur stat display + * @pdev: Pointer to the PDEV structure. + * + * Return: none. + */ +void ol_tx_sched_cur_state_display(struct ol_txrx_pdev_t *pdev) +{ + OL_TX_SCHED_WRR_ADV_CAT_CUR_STATE_DUMP(pdev->tx_sched.scheduler); +} + +/** + * ol_tx_sched_cur_state_display() - reset tx sched stats + * @pdev: Pointer to the PDEV structure. + * + * Return: none. + */ +void ol_tx_sched_stats_clear(struct ol_txrx_pdev_t *pdev) +{ + OL_TX_SCHED_WRR_ADV_CAT_STAT_CLEAR(pdev->tx_sched.scheduler); +} + +#endif /* OL_TX_SCHED == OL_TX_SCHED_WRR_ADV */ + +/*--- congestion control discard --------------------------------------------*/ + +static struct ol_tx_frms_queue_t * +ol_tx_sched_discard_select_txq( + struct ol_txrx_pdev_t *pdev, + ol_tx_frms_queue_list *tx_queues) +{ + struct ol_tx_frms_queue_t *txq; + struct ol_tx_frms_queue_t *selected_txq = NULL; + int max_frms = 0; + + /* return the tx queue with the most frames */ + TAILQ_FOREACH(txq, tx_queues, list_elem) { + if (txq->frms > max_frms) { + max_frms = txq->frms; + selected_txq = txq; + } + } + return selected_txq; +} + +u_int16_t +ol_tx_sched_discard_select( + struct ol_txrx_pdev_t *pdev, + u_int16_t frms, + ol_tx_desc_list *tx_descs, + bool force) +{ + int cat; + struct ol_tx_frms_queue_t *txq; + int bytes; + u_int32_t credit; + struct ol_tx_sched_notify_ctx_t notify_ctx; + + /* + * first decide what category of traffic (e.g. TID or AC) + * to discard next + */ + cat = ol_tx_sched_discard_select_category(pdev); + + /* then decide which peer within this category to discard from next */ + txq = ol_tx_sched_discard_select_txq( + pdev, ol_tx_sched_category_tx_queues(pdev, cat)); + if (!txq) + /* No More pending Tx Packets in Tx Queue. Exit Discard loop */ + return 0; + + + if (force == false) { + /* + * Now decide how many frames to discard from this peer-TID. + * Don't discard more frames than the caller has specified. + * Don't discard more than a fixed quantum of frames at a time. + * Don't discard more than 50% of the queue's frames at a time, + * but if there's only 1 frame left, go ahead and discard it. + */ +#define OL_TX_DISCARD_QUANTUM 10 + if (OL_TX_DISCARD_QUANTUM < frms) + frms = OL_TX_DISCARD_QUANTUM; + + + if (txq->frms > 1 && frms >= (txq->frms >> 1)) + frms = txq->frms >> 1; + } + + /* + * Discard from the head of the queue, because: + * 1. Front-dropping gives applications like TCP that include ARQ + * an early notification of congestion. + * 2. For time-sensitive applications like RTP, the newest frames are + * most relevant. + */ + credit = 10000; /* no credit limit */ + frms = ol_tx_dequeue(pdev, txq, tx_descs, frms, &credit, &bytes); + + notify_ctx.event = OL_TX_DISCARD_FRAMES; + notify_ctx.frames = frms; + notify_ctx.bytes = bytes; + notify_ctx.txq = txq; + notify_ctx.info.ext_tid = cat; + ol_tx_sched_notify(pdev, ¬ify_ctx); + + TX_SCHED_DEBUG_PRINT("%s Tx Drop : %d\n", __func__, frms); + return frms; +} + +/*--- scheduler framework ---------------------------------------------------*/ + +/* + * The scheduler mutex spinlock has been acquired outside this function, + * so there is need to take locks inside this function. + */ +void +ol_tx_sched_notify( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_sched_notify_ctx_t *ctx) +{ + struct ol_tx_frms_queue_t *txq = ctx->txq; + int tid; + + if (!pdev->tx_sched.scheduler) + return; + + switch (ctx->event) { + case OL_TX_ENQUEUE_FRAME: + tid = ctx->info.tx_msdu_info->htt.info.ext_tid; + ol_tx_sched_txq_enqueue(pdev, txq, tid, 1, ctx->bytes); + break; + case OL_TX_DELETE_QUEUE: + tid = ctx->info.ext_tid; + if (txq->flag == ol_tx_queue_active) + ol_tx_sched_txq_deactivate(pdev, txq, tid); + + break; + case OL_TX_PAUSE_QUEUE: + tid = ctx->info.ext_tid; + if (txq->flag == ol_tx_queue_active) + ol_tx_sched_txq_deactivate(pdev, txq, tid); + + break; + case OL_TX_UNPAUSE_QUEUE: + tid = ctx->info.ext_tid; + if (txq->frms != 0) + ol_tx_sched_txq_enqueue(pdev, txq, tid, + txq->frms, txq->bytes); + + break; + case OL_TX_DISCARD_FRAMES: + /* not necessarily TID, could be category */ + tid = ctx->info.ext_tid; + ol_tx_sched_txq_discard(pdev, txq, tid, + ctx->frames, ctx->bytes); + break; + default: + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Error: unknown sched notification (%d)\n", + ctx->event); + qdf_assert(0); + break; + } +} + +#define OL_TX_MSDU_ID_STORAGE_ERR(ptr) (!ptr) + +static void +ol_tx_sched_dispatch( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_sched_ctx *sctx) +{ + qdf_nbuf_t msdu, prev = NULL, head_msdu = NULL; + struct ol_tx_desc_t *tx_desc; + u_int16_t *msdu_id_storage; + u_int16_t msdu_id; + int num_msdus = 0; + + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + while (sctx->frms) { + tx_desc = TAILQ_FIRST(&sctx->head); + if (!tx_desc) { + /* TODO: find its reason */ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: err, no enough tx_desc from stx->head.\n", + __func__); + break; + } + msdu = tx_desc->netbuf; + TAILQ_REMOVE(&sctx->head, tx_desc, tx_desc_list_elem); + if (!head_msdu) + head_msdu = msdu; + + if (prev) + qdf_nbuf_set_next(prev, msdu); + + prev = msdu; + +#ifndef ATH_11AC_TXCOMPACT + /* + * When the tx frame is downloaded to the target, there are two + * outstanding references: + * 1. The host download SW (HTT, HTC, HIF) + * This reference is cleared by the ol_tx_send_done callback + * functions. + * 2. The target FW + * This reference is cleared by the ol_tx_completion_handler + * function. + * It is extremely probable that the download completion is + * processed before the tx completion message. However, under + * exceptional conditions the tx completion may be processed + *first. Thus, rather that assuming that reference (1) is + *done before reference (2), + * explicit reference tracking is needed. + * Double-increment the ref count to account for both references + * described above. + */ + qdf_atomic_init(&tx_desc->ref_cnt); + qdf_atomic_inc(&tx_desc->ref_cnt); + qdf_atomic_inc(&tx_desc->ref_cnt); +#endif + + /*Store the MSDU Id for each MSDU*/ + /* store MSDU ID */ + msdu_id = ol_tx_desc_id(pdev, tx_desc); + msdu_id_storage = ol_tx_msdu_id_storage(msdu); + if (OL_TX_MSDU_ID_STORAGE_ERR(msdu_id_storage)) { + /* + * Send the prior frames as a batch, + *then send this as a single, + * then resume handling the remaining frames. + */ + if (head_msdu) + ol_tx_send_batch(pdev, head_msdu, num_msdus); + + prev = NULL; + head_msdu = prev; + num_msdus = 0; + + if (htt_tx_send_std(pdev->htt_pdev, msdu, msdu_id)) { + ol_tx_target_credit_incr(pdev, msdu); + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, + 1 /* error */); + } + } else { + *msdu_id_storage = msdu_id; + num_msdus++; + } + sctx->frms--; + } + + /*Send Batch Of Frames*/ + if (head_msdu) + ol_tx_send_batch(pdev, head_msdu, num_msdus); + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +#ifdef QCA_TX_PADDING_CREDIT_SUPPORT +static void replenish_tx_pad_credit(struct ol_txrx_pdev_t *pdev) +{ + int replenish_credit = 0, avail_targ_tx_credit = 0; + int cur_tx_pad_credit = 0, grp_credit = 0, i = 0; + qdf_atomic_t *tx_grp_credit = NULL; + + cur_tx_pad_credit = qdf_atomic_read(&pdev->pad_reserve_tx_credit); + if (cur_tx_pad_credit < MIN_TX_PAD_CREDIT_THRESH) { + replenish_credit = MAX_TX_PAD_CREDIT_THRESH - cur_tx_pad_credit; + avail_targ_tx_credit = qdf_atomic_read(&pdev->target_tx_credit); + replenish_credit = (replenish_credit < avail_targ_tx_credit) ? + replenish_credit : avail_targ_tx_credit; + if (replenish_credit < 0) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_FATAL, + "Tx Pad Credits = %d Target Tx Credits = %d", + cur_tx_pad_credit, + avail_targ_tx_credit); + qdf_assert(0); + } + qdf_atomic_add(replenish_credit, &pdev->pad_reserve_tx_credit); + qdf_atomic_add(-replenish_credit, &pdev->target_tx_credit); + + while (replenish_credit > 0) { + for (i = 0; i < OL_TX_MAX_TXQ_GROUPS; i++) { + tx_grp_credit = &pdev->txq_grps[i].credit; + grp_credit = qdf_atomic_read(tx_grp_credit); + if (grp_credit) { + qdf_atomic_add(-1, tx_grp_credit); + replenish_credit--; + } + if (!replenish_credit) + break; + } + } + } +} +#else +static void replenish_tx_pad_credit(struct ol_txrx_pdev_t *pdev) +{ +} +#endif + +void +ol_tx_sched(struct ol_txrx_pdev_t *pdev) +{ + struct ol_tx_sched_ctx sctx; + u_int32_t credit; + + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + if (pdev->tx_sched.tx_sched_status != ol_tx_scheduler_idle) { + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + return; + } + pdev->tx_sched.tx_sched_status = ol_tx_scheduler_running; + + ol_tx_sched_log(pdev); + /* + *adf_os_print("BEFORE tx sched:\n"); + *ol_tx_queues_display(pdev); + */ + replenish_tx_pad_credit(pdev); + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + + TAILQ_INIT(&sctx.head); + sctx.frms = 0; + + ol_tx_sched_select_init(pdev); + while (qdf_atomic_read(&pdev->target_tx_credit) > 0) { + int num_credits; + + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + replenish_tx_pad_credit(pdev); + credit = qdf_atomic_read(&pdev->target_tx_credit); + num_credits = ol_tx_sched_select_batch(pdev, &sctx, credit); + if (num_credits > 0) { +#if DEBUG_HTT_CREDIT + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " Decrease credit %d - %d = %d.\n", + qdf_atomic_read(&pdev->target_tx_credit), + num_credits, + qdf_atomic_read(&pdev->target_tx_credit) - + num_credits); +#endif + DPTRACE(qdf_dp_trace_credit_record(QDF_TX_SCHED, + QDF_CREDIT_DEC, num_credits, + qdf_atomic_read(&pdev->target_tx_credit) - + num_credits, + qdf_atomic_read(&pdev->txq_grps[0].credit), + qdf_atomic_read(&pdev->txq_grps[1].credit))); + + qdf_atomic_add(-num_credits, &pdev->target_tx_credit); + } + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + + if (num_credits == 0) + break; + } + ol_tx_sched_dispatch(pdev, &sctx); + + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + /* + *adf_os_print("AFTER tx sched:\n"); + *ol_tx_queues_display(pdev); + */ + + pdev->tx_sched.tx_sched_status = ol_tx_scheduler_idle; + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +void * +ol_tx_sched_attach( + struct ol_txrx_pdev_t *pdev) +{ + pdev->tx_sched.tx_sched_status = ol_tx_scheduler_idle; + return ol_tx_sched_init(pdev); +} + +void +ol_tx_sched_detach( + struct ol_txrx_pdev_t *pdev) +{ + if (pdev->tx_sched.scheduler) { + qdf_mem_free(pdev->tx_sched.scheduler); + pdev->tx_sched.scheduler = NULL; + } +} + +/*--- debug functions -------------------------------------------------------*/ + +#if defined(DEBUG_HL_LOGGING) + +static void +ol_tx_sched_log(struct ol_txrx_pdev_t *pdev) +{ + u_int8_t *buf; + u_int32_t *active_bitmap; + int i, j, num_cats_active; + int active, frms, bytes; + int credit; + + /* don't bother recording state if credit is zero */ + credit = qdf_atomic_read(&pdev->target_tx_credit); + if (credit == 0) + return; + + + /* + * See how many TIDs are active, so queue state can be stored only + * for those TIDs. + * Do an initial iteration through all categories to see if any + * are active. Doing an extra iteration is inefficient, but + * efficiency is not a dominant concern when logging is enabled. + */ + num_cats_active = 0; + for (i = 0; i < OL_TX_SCHED_NUM_CATEGORIES; i++) { + ol_tx_sched_category_info(pdev, i, &active, &frms, &bytes); + if (active) + num_cats_active++; + } + /* don't bother recording state if there are no active queues */ + if (num_cats_active == 0) + return; + + + ol_tx_queue_log_sched(pdev, credit, &num_cats_active, + &active_bitmap, &buf); + + if (num_cats_active == 0) + return; + + *active_bitmap = 0; + for (i = 0, j = 0; + i < OL_TX_SCHED_NUM_CATEGORIES && j < num_cats_active; + i++) { + u_int8_t *p; + + ol_tx_sched_category_info(pdev, i, &active, &frms, &bytes); + if (!active) + continue; + + p = &buf[j*6]; + p[0] = (frms >> 0) & 0xff; + p[1] = (frms >> 8) & 0xff; + + p[2] = (bytes >> 0) & 0xff; + p[3] = (bytes >> 8) & 0xff; + p[4] = (bytes >> 16) & 0xff; + p[5] = (bytes >> 24) & 0xff; + j++; + *active_bitmap |= 1 << i; + } +} + +#endif /* defined(DEBUG_HL_LOGGING) */ + +#endif /* defined(CONFIG_HL_SUPPORT) */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_sched.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_sched.h new file mode 100644 index 0000000000000000000000000000000000000000..51987d701b95240124db15b7c7b6db9041c5f252 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_sched.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2012-2013, 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_tx_sched.h + * @brief API definitions for the tx scheduler module within the data SW. + */ +#ifndef _OL_TX_SCHED__H_ +#define _OL_TX_SCHED__H_ + +#include + +enum ol_tx_queue_action { + OL_TX_ENQUEUE_FRAME, + OL_TX_DELETE_QUEUE, + OL_TX_PAUSE_QUEUE, + OL_TX_UNPAUSE_QUEUE, + OL_TX_DISCARD_FRAMES, +}; + +struct ol_tx_sched_notify_ctx_t { + int event; + struct ol_tx_frms_queue_t *txq; + union { + int ext_tid; + struct ol_txrx_msdu_info_t *tx_msdu_info; + } info; + int frames; + int bytes; +}; + +#if defined(CONFIG_HL_SUPPORT) + +void +ol_tx_sched_notify( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_sched_notify_ctx_t *ctx); + +void +ol_tx_sched(struct ol_txrx_pdev_t *pdev); + +u_int16_t +ol_tx_sched_discard_select( + struct ol_txrx_pdev_t *pdev, + u_int16_t frms, + ol_tx_desc_list *tx_descs, + bool force); + +void * +ol_tx_sched_attach(struct ol_txrx_pdev_t *pdev); + +void +ol_tx_sched_detach(struct ol_txrx_pdev_t *pdev); + +void ol_tx_sched_stats_display(struct ol_txrx_pdev_t *pdev); + +void ol_tx_sched_cur_state_display(struct ol_txrx_pdev_t *pdev); + +void ol_tx_sched_stats_clear(struct ol_txrx_pdev_t *pdev); + +void +ol_txrx_set_wmm_param(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + struct ol_tx_wmm_param_t wmm_param); + +#else + +static inline void +ol_tx_sched_notify( + struct ol_txrx_pdev_t *pdev, + struct ol_tx_sched_notify_ctx_t *ctx) +{ +} + +static inline void +ol_tx_sched(struct ol_txrx_pdev_t *pdev) +{ +} + +static inline u_int16_t +ol_tx_sched_discard_select( + struct ol_txrx_pdev_t *pdev, + u_int16_t frms, + ol_tx_desc_list *tx_descs, + bool force) +{ + return 0; +} + +static inline void * +ol_tx_sched_attach(struct ol_txrx_pdev_t *pdev) +{ + return NULL; +} + +static inline void +ol_tx_sched_detach(struct ol_txrx_pdev_t *pdev) +{ +} + +static inline void ol_tx_sched_stats_display(struct ol_txrx_pdev_t *pdev) +{ +} + +static inline void ol_tx_sched_cur_state_display(struct ol_txrx_pdev_t *pdev) +{ +} + +static inline void ol_tx_sched_stats_clear(struct ol_txrx_pdev_t *pdev) +{ +} + +#endif /* defined(CONFIG_HL_SUPPORT) */ + +#if defined(CONFIG_HL_SUPPORT) || defined(TX_CREDIT_RECLAIM_SUPPORT) +/* + * HL needs to keep track of the amount of credit available to download + * tx frames to the target - the download scheduler decides when to + * download frames, and which frames to download, based on the credit + * availability. + * LL systems that use TX_CREDIT_RECLAIM_SUPPORT also need to keep track + * of the target_tx_credit, to determine when to poll for tx completion + * messages. + */ + +static inline void +ol_tx_target_credit_adjust(int factor, + struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t msdu) +{ + qdf_atomic_add(factor * htt_tx_msdu_credit(msdu), + &pdev->target_tx_credit); +} + +static inline void ol_tx_target_credit_decr(struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t msdu) +{ + ol_tx_target_credit_adjust(-1, pdev, msdu); +} + +static inline void ol_tx_target_credit_incr(struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t msdu) +{ + ol_tx_target_credit_adjust(1, pdev, msdu); +} + +#ifdef QCA_TX_PADDING_CREDIT_SUPPORT + +#define MIN_TX_PAD_CREDIT_THRESH 4 +#define MAX_TX_PAD_CREDIT_THRESH 5 + +#endif /* QCA_TX_PADDING_CREDIT_SUPPORT */ + +#else +/* + * LL does not need to keep track of target credit. + * Since the host tx descriptor pool size matches the target's, + * we know the target has space for the new tx frame if the host's + * tx descriptor allocation succeeded. + */ +static inline void +ol_tx_target_credit_adjust(int factor, + struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t msdu) +{ +} + +static inline void ol_tx_target_credit_decr(struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t msdu) +{ +} + +static inline void ol_tx_target_credit_incr(struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t msdu) +{ +} + +#endif +#endif /* _OL_TX_SCHED__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_send.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_send.c new file mode 100644 index 0000000000000000000000000000000000000000..3ae328107c3e500b466d11a9a380d1f2b4cc55ec --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_send.c @@ -0,0 +1,1691 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* qdf_atomic_inc, etc. */ +#include /* qdf_os_spinlock */ +#include /* qdf_system_ticks, etc. */ +#include /* qdf_nbuf_t */ +#include /* QDF_NBUF_TX_EXT_TID_INVALID */ + +#include "queue.h" /* TAILQ */ +#ifdef QCA_COMPUTE_TX_DELAY +#include /* ethernet_hdr_t, etc. */ +#include /* ipv6_traffic_class */ +#endif + +#include /* ol_txrx_vdev_handle, etc. */ +#include /* htt_tx_compl_desc_id */ +#include /* htt_tx_status */ + +#include +#include +#include /* ol_txrx_vdev_t, etc */ +#include /* ol_tx_desc_find, ol_tx_desc_frame_free */ +#ifdef QCA_COMPUTE_TX_DELAY +#include /* ol_tx_dest_addr_find */ +#endif +#include /* OL_TX_DESC_NO_REFS, etc. */ +#include +#include /* ol_tx_reinject */ +#include + +#include /* ol_cfg_is_high_latency */ +#include +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP +#include /* OL_TX_RESTORE_HDR, etc */ +#endif +#include +#include +#include +#include + +#ifdef TX_CREDIT_RECLAIM_SUPPORT + +#define OL_TX_CREDIT_RECLAIM(pdev) \ + do { \ + if (qdf_atomic_read(&pdev->target_tx_credit) < \ + ol_cfg_tx_credit_lwm(pdev->ctrl_pdev)) { \ + ol_osif_ath_tasklet(pdev->osdev); \ + } \ + } while (0) + +#else + +#define OL_TX_CREDIT_RECLAIM(pdev) + +#endif /* TX_CREDIT_RECLAIM_SUPPORT */ + +#if defined(CONFIG_HL_SUPPORT) || defined(TX_CREDIT_RECLAIM_SUPPORT) + +/* + * HL needs to keep track of the amount of credit available to download + * tx frames to the target - the download scheduler decides when to + * download frames, and which frames to download, based on the credit + * availability. + * LL systems that use TX_CREDIT_RECLAIM_SUPPORT also need to keep track + * of the target_tx_credit, to determine when to poll for tx completion + * messages. + */ +static inline void +ol_tx_target_credit_decr_int(struct ol_txrx_pdev_t *pdev, int delta) +{ + qdf_atomic_add(-1 * delta, &pdev->target_tx_credit); +} + +static inline void +ol_tx_target_credit_incr_int(struct ol_txrx_pdev_t *pdev, int delta) +{ + qdf_atomic_add(delta, &pdev->target_tx_credit); +} +#else + +static inline void +ol_tx_target_credit_decr_int(struct ol_txrx_pdev_t *pdev, int delta) +{ +} + +static inline void +ol_tx_target_credit_incr_int(struct ol_txrx_pdev_t *pdev, int delta) +{ +} +#endif + +#ifdef DESC_TIMESTAMP_DEBUG_INFO +static inline void ol_tx_desc_update_comp_ts(struct ol_tx_desc_t *tx_desc) +{ + tx_desc->desc_debug_info.last_comp_ts = qdf_get_log_timestamp(); +} +#else +static inline void ol_tx_desc_update_comp_ts(struct ol_tx_desc_t *tx_desc) +{ +} +#endif + +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_HL_NETDEV_FLOW_CONTROL) +void ol_tx_flow_ct_unpause_os_q(ol_txrx_pdev_handle pdev) +{ + struct ol_txrx_vdev_t *vdev; + bool trigger_unpause = false; + + qdf_spin_lock_bh(&pdev->tx_mutex); + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + if (vdev->tx_desc_limit == 0) + continue; + + /* un-pause high priority queue */ + if (vdev->prio_q_paused && + (qdf_atomic_read(&vdev->tx_desc_count) + < vdev->tx_desc_limit)) { + pdev->pause_cb(vdev->vdev_id, + WLAN_NETIF_PRIORITY_QUEUE_ON, + WLAN_DATA_FLOW_CONTROL_PRIORITY); + vdev->prio_q_paused = 0; + } + /* un-pause non priority queues */ + if (qdf_atomic_read(&vdev->os_q_paused) && + (qdf_atomic_read(&vdev->tx_desc_count) + <= vdev->queue_restart_th)) { + pdev->pause_cb(vdev->vdev_id, + WLAN_WAKE_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); + qdf_atomic_set(&vdev->os_q_paused, 0); + trigger_unpause = true; + } + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + if (trigger_unpause) + ol_tx_hl_pdev_queue_send_all(pdev); +} +#endif + +static inline uint16_t +ol_tx_send_base(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc, qdf_nbuf_t msdu) +{ + int msdu_credit_consumed; + + TX_CREDIT_DEBUG_PRINT("TX %d bytes\n", qdf_nbuf_len(msdu)); + TX_CREDIT_DEBUG_PRINT(" Decrease credit %d - 1 = %d, len:%d.\n", + qdf_atomic_read(&pdev->target_tx_credit), + qdf_atomic_read(&pdev->target_tx_credit) - 1, + qdf_nbuf_len(msdu)); + + msdu_credit_consumed = htt_tx_msdu_credit(msdu); + ol_tx_target_credit_decr_int(pdev, msdu_credit_consumed); + OL_TX_CREDIT_RECLAIM(pdev); + + /* + * When the tx frame is downloaded to the target, there are two + * outstanding references: + * 1. The host download SW (HTT, HTC, HIF) + * This reference is cleared by the ol_tx_send_done callback + * functions. + * 2. The target FW + * This reference is cleared by the ol_tx_completion_handler + * function. + * It is extremely probable that the download completion is processed + * before the tx completion message. However, under exceptional + * conditions the tx completion may be processed first. Thus, rather + * that assuming that reference (1) is done before reference (2), + * explicit reference tracking is needed. + * Double-increment the ref count to account for both references + * described above. + */ + + OL_TX_DESC_REF_INIT(tx_desc); + OL_TX_DESC_REF_INC(tx_desc); + OL_TX_DESC_REF_INC(tx_desc); + + return msdu_credit_consumed; +} + +void +ol_tx_send(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc, qdf_nbuf_t msdu, uint8_t vdev_id) +{ + int msdu_credit_consumed; + uint16_t id; + int failed; + + msdu_credit_consumed = ol_tx_send_base(pdev, tx_desc, msdu); + id = ol_tx_desc_id(pdev, tx_desc); + QDF_NBUF_UPDATE_TX_PKT_COUNT(msdu, QDF_NBUF_TX_PKT_TXRX); + DPTRACE(qdf_dp_trace_ptr(msdu, QDF_DP_TRACE_TXRX_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(msdu), + sizeof(qdf_nbuf_data(msdu)), tx_desc->id, + vdev_id)); + failed = htt_tx_send_std(pdev->htt_pdev, msdu, id); + if (qdf_unlikely(failed)) { + ol_tx_target_credit_incr_int(pdev, msdu_credit_consumed); + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, 1 /* had error */); + } +} + +void +ol_tx_send_batch(struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t head_msdu, int num_msdus) +{ + qdf_nbuf_t rejected; + + OL_TX_CREDIT_RECLAIM(pdev); + + rejected = htt_tx_send_batch(pdev->htt_pdev, head_msdu, num_msdus); + while (qdf_unlikely(rejected)) { + struct ol_tx_desc_t *tx_desc; + uint16_t *msdu_id_storage; + qdf_nbuf_t next; + + next = qdf_nbuf_next(rejected); + msdu_id_storage = ol_tx_msdu_id_storage(rejected); + tx_desc = ol_tx_desc_find(pdev, *msdu_id_storage); + + ol_tx_target_credit_incr(pdev, rejected); + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, 1 /* had error */); + + rejected = next; + } +} + +void +ol_tx_send_nonstd(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t msdu, enum htt_pkt_type pkt_type) +{ + int msdu_credit_consumed; + uint16_t id; + int failed; + + msdu_credit_consumed = ol_tx_send_base(pdev, tx_desc, msdu); + id = ol_tx_desc_id(pdev, tx_desc); + QDF_NBUF_UPDATE_TX_PKT_COUNT(msdu, QDF_NBUF_TX_PKT_TXRX); + failed = htt_tx_send_nonstd(pdev->htt_pdev, msdu, id, pkt_type); + if (failed) { + ol_txrx_err( + "Error: freeing tx frame after htt_tx failed"); + ol_tx_target_credit_incr_int(pdev, msdu_credit_consumed); + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, 1 /* had error */); + } +} + +static inline bool +ol_tx_download_done_base(struct ol_txrx_pdev_t *pdev, + A_STATUS status, qdf_nbuf_t msdu, uint16_t msdu_id) +{ + struct ol_tx_desc_t *tx_desc; + bool is_frame_freed = false; + + tx_desc = ol_tx_desc_find(pdev, msdu_id); + qdf_assert(tx_desc); + + /* + * If the download is done for + * the Management frame then + * call the download callback if registered + */ + if (tx_desc->pkt_type >= OL_TXRX_MGMT_TYPE_BASE) { + ol_txrx_mgmt_tx_cb download_cb = + pdev->tx_mgmt_cb.download_cb; + if (download_cb) { + download_cb(pdev->tx_mgmt_cb.ctxt, + tx_desc->netbuf, status != A_OK); + } + } + + if (status != A_OK) { + ol_tx_target_credit_incr(pdev, msdu); + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, + 1 /* download err */); + is_frame_freed = true; + } else { + if (OL_TX_DESC_NO_REFS(tx_desc)) { + /* + * The decremented value was zero - free the frame. + * Use the tx status recorded previously during + * tx completion handling. + */ + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, + tx_desc->status != + htt_tx_status_ok); + is_frame_freed = true; + } + } + return is_frame_freed; +} + +void +ol_tx_download_done_ll(void *pdev, + A_STATUS status, qdf_nbuf_t msdu, uint16_t msdu_id) +{ + ol_tx_download_done_base((struct ol_txrx_pdev_t *)pdev, status, msdu, + msdu_id); +} + +void +ol_tx_download_done_hl_retain(void *txrx_pdev, + A_STATUS status, + qdf_nbuf_t msdu, uint16_t msdu_id) +{ + struct ol_txrx_pdev_t *pdev = txrx_pdev; + + ol_tx_download_done_base(pdev, status, msdu, msdu_id); +} + +void +ol_tx_download_done_hl_free(void *txrx_pdev, + A_STATUS status, qdf_nbuf_t msdu, uint16_t msdu_id) +{ + struct ol_txrx_pdev_t *pdev = txrx_pdev; + struct ol_tx_desc_t *tx_desc; + bool is_frame_freed; + + tx_desc = ol_tx_desc_find(pdev, msdu_id); + qdf_assert(tx_desc); + + DPTRACE(qdf_dp_trace_ptr(msdu, + QDF_DP_TRACE_FREE_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(msdu), + sizeof(qdf_nbuf_data(msdu)), tx_desc->id, + status)); + + is_frame_freed = ol_tx_download_done_base(pdev, status, msdu, msdu_id); + + /* + * if frame is freed in ol_tx_download_done_base then return. + */ + if (is_frame_freed) { + qdf_atomic_add(1, &pdev->tx_queue.rsrc_cnt); + return; + } + + if ((tx_desc->pkt_type != OL_TX_FRM_NO_FREE) && + (tx_desc->pkt_type < OL_TXRX_MGMT_TYPE_BASE)) { + qdf_atomic_add(1, &pdev->tx_queue.rsrc_cnt); + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, status != A_OK); + } +} + +void ol_tx_target_credit_init(struct ol_txrx_pdev_t *pdev, int credit_delta) +{ + qdf_atomic_add(credit_delta, &pdev->orig_target_tx_credit); +} + +void ol_tx_target_credit_update(struct ol_txrx_pdev_t *pdev, int credit_delta) +{ + TX_CREDIT_DEBUG_PRINT(" Increase credit %d + %d = %d\n", + qdf_atomic_read(&pdev->target_tx_credit), + credit_delta, + qdf_atomic_read(&pdev->target_tx_credit) + + credit_delta); + qdf_atomic_add(credit_delta, &pdev->target_tx_credit); +} + +#ifdef QCA_COMPUTE_TX_DELAY + +static void +ol_tx_delay_compute(struct ol_txrx_pdev_t *pdev, + enum htt_tx_status status, + uint16_t *desc_ids, int num_msdus); + +#else +static inline void +ol_tx_delay_compute(struct ol_txrx_pdev_t *pdev, + enum htt_tx_status status, + uint16_t *desc_ids, int num_msdus) +{ +} +#endif /* QCA_COMPUTE_TX_DELAY */ + +#if defined(CONFIG_HL_SUPPORT) +int ol_tx_deduct_one_credit(struct ol_txrx_pdev_t *pdev) +{ + /* TODO: Check if enough credits */ + + if (!pdev->cfg.default_tx_comp_req) { + ol_tx_target_credit_update(pdev, -1); + ol_tx_deduct_one_any_group_credit(pdev); + + DPTRACE(qdf_dp_trace_credit_record(QDF_TX_HTT_MSG, + QDF_CREDIT_DEC, 1, + qdf_atomic_read(&pdev->target_tx_credit), + qdf_atomic_read(&pdev->txq_grps[0].credit), + qdf_atomic_read(&pdev->txq_grps[1].credit))); + } + + return 0; +} +#endif /* CONFIG_HL_SUPPORT */ + +#ifndef OL_TX_RESTORE_HDR +#define OL_TX_RESTORE_HDR(__tx_desc, __msdu) +#endif +/* + * The following macros could have been inline functions too. + * The only rationale for choosing macros, is to force the compiler to inline + * the implementation, which cannot be controlled for actual "inline" functions, + * since "inline" is only a hint to the compiler. + * In the performance path, we choose to force the inlining, in preference to + * type-checking offered by the actual inlined functions. + */ +#define ol_tx_msdu_complete_batch(_pdev, _tx_desc, _tx_descs, _status) \ + TAILQ_INSERT_TAIL(&(_tx_descs), (_tx_desc), tx_desc_list_elem) +#ifndef ATH_11AC_TXCOMPACT +#define ol_tx_msdu_complete_single(_pdev, _tx_desc, _netbuf,\ + _lcl_freelist, _tx_desc_last) \ + do { \ + qdf_atomic_init(&(_tx_desc)->ref_cnt); \ + /* restore orginal hdr offset */ \ + OL_TX_RESTORE_HDR((_tx_desc), (_netbuf)); \ + qdf_nbuf_unmap((_pdev)->osdev, (_netbuf), QDF_DMA_TO_DEVICE); \ + qdf_nbuf_free((_netbuf)); \ + ((union ol_tx_desc_list_elem_t *)(_tx_desc))->next = \ + (_lcl_freelist); \ + if (qdf_unlikely(!lcl_freelist)) { \ + (_tx_desc_last) = (union ol_tx_desc_list_elem_t *)\ + (_tx_desc); \ + } \ + (_lcl_freelist) = (union ol_tx_desc_list_elem_t *)(_tx_desc); \ + } while (0) +#else /*!ATH_11AC_TXCOMPACT */ +#define ol_tx_msdu_complete_single(_pdev, _tx_desc, _netbuf,\ + _lcl_freelist, _tx_desc_last) \ + do { \ + /* restore orginal hdr offset */ \ + OL_TX_RESTORE_HDR((_tx_desc), (_netbuf)); \ + qdf_nbuf_unmap((_pdev)->osdev, (_netbuf), QDF_DMA_TO_DEVICE); \ + qdf_nbuf_free((_netbuf)); \ + ((union ol_tx_desc_list_elem_t *)(_tx_desc))->next = \ + (_lcl_freelist); \ + if (qdf_unlikely(!lcl_freelist)) { \ + (_tx_desc_last) = (union ol_tx_desc_list_elem_t *)\ + (_tx_desc); \ + } \ + (_lcl_freelist) = (union ol_tx_desc_list_elem_t *)(_tx_desc); \ + } while (0) + +#endif /*!ATH_11AC_TXCOMPACT */ + +#ifdef QCA_TX_SINGLE_COMPLETIONS +#ifdef QCA_TX_STD_PATH_ONLY +#define ol_tx_msdu_complete(_pdev, _tx_desc, _tx_descs, \ + _netbuf, _lcl_freelist, \ + _tx_desc_last, _status, is_tx_desc_freed) \ + { \ + is_tx_desc_freed = 0; \ + ol_tx_msdu_complete_single((_pdev), (_tx_desc), \ + (_netbuf), (_lcl_freelist), \ + _tx_desc_last) \ + } +#else /* !QCA_TX_STD_PATH_ONLY */ +#define ol_tx_msdu_complete(_pdev, _tx_desc, _tx_descs, \ + _netbuf, _lcl_freelist, \ + _tx_desc_last, _status, is_tx_desc_freed) \ + do { \ + if (qdf_likely((_tx_desc)->pkt_type == OL_TX_FRM_STD)) { \ + is_tx_desc_freed = 0; \ + ol_tx_msdu_complete_single((_pdev), (_tx_desc),\ + (_netbuf), (_lcl_freelist), \ + (_tx_desc_last)); \ + } else { \ + is_tx_desc_freed = 1; \ + ol_tx_desc_frame_free_nonstd( \ + (_pdev), (_tx_desc), \ + (_status) != htt_tx_status_ok); \ + } \ + } while (0) +#endif /* !QCA_TX_STD_PATH_ONLY */ +#else /* !QCA_TX_SINGLE_COMPLETIONS */ +#ifdef QCA_TX_STD_PATH_ONLY +#define ol_tx_msdu_complete(_pdev, _tx_desc, _tx_descs, \ + _netbuf, _lcl_freelist, \ + _tx_desc_last, _status, is_tx_desc_freed) \ + { \ + is_tx_desc_freed = 0; \ + ol_tx_msdu_complete_batch((_pdev), (_tx_desc), \ + (_tx_descs), (_status)) \ + } +#else /* !QCA_TX_STD_PATH_ONLY */ +#define ol_tx_msdu_complete(_pdev, _tx_desc, _tx_descs, \ + _netbuf, _lcl_freelist, \ + _tx_desc_last, _status, is_tx_desc_freed) \ + do { \ + if (qdf_likely((_tx_desc)->pkt_type == OL_TX_FRM_STD)) { \ + is_tx_desc_freed = 0; \ + ol_tx_msdu_complete_batch((_pdev), (_tx_desc), \ + (_tx_descs), (_status)); \ + } else { \ + is_tx_desc_freed = 1; \ + ol_tx_desc_frame_free_nonstd((_pdev), (_tx_desc), \ + (_status) != \ + htt_tx_status_ok); \ + } \ + } while (0) +#endif /* !QCA_TX_STD_PATH_ONLY */ +#endif /* QCA_TX_SINGLE_COMPLETIONS */ + +#if !defined(CONFIG_HL_SUPPORT) +void ol_tx_discard_target_frms(ol_txrx_pdev_handle pdev) +{ + int i = 0; + struct ol_tx_desc_t *tx_desc; + int num_disarded = 0; + + for (i = 0; i < pdev->tx_desc.pool_size; i++) { + tx_desc = ol_tx_desc_find(pdev, i); + /* + * Confirm that each tx descriptor is "empty", i.e. it has + * no tx frame attached. + * In particular, check that there are no frames that have + * been given to the target to transmit, for which the + * target has never provided a response. + */ + if (qdf_atomic_read(&tx_desc->ref_cnt)) { + ol_txrx_dbg( + "Warning: freeing tx desc %d", tx_desc->id); + ol_tx_desc_frame_free_nonstd(pdev, + tx_desc, 1); + num_disarded++; + } + } + + if (num_disarded) + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "Warning: freed %d tx descs for which no tx completion rcvd from the target", + num_disarded); +} +#endif + +void ol_tx_credit_completion_handler(ol_txrx_pdev_handle pdev, int credits) +{ + ol_tx_target_credit_update(pdev, credits); + + if (pdev->cfg.is_high_latency) + ol_tx_sched(pdev); + + /* UNPAUSE OS Q */ + ol_tx_flow_ct_unpause_os_q(pdev); +} + +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 +/** + * ol_tx_flow_pool_lock() - take flow pool lock + * @tx_desc: tx desc + * + * Return: None + */ +static inline +void ol_tx_flow_pool_lock(struct ol_tx_desc_t *tx_desc) +{ + struct ol_tx_flow_pool_t *pool; + + pool = tx_desc->pool; + qdf_spin_lock_bh(&pool->flow_pool_lock); +} + +/** + * ol_tx_flow_pool_unlock() - release flow pool lock + * @tx_desc: tx desc + * + * Return: None + */ +static inline +void ol_tx_flow_pool_unlock(struct ol_tx_desc_t *tx_desc) +{ + struct ol_tx_flow_pool_t *pool; + + pool = tx_desc->pool; + qdf_spin_unlock_bh(&pool->flow_pool_lock); +} +#else +static inline +void ol_tx_flow_pool_lock(struct ol_tx_desc_t *tx_desc) +{ +} + +static inline +void ol_tx_flow_pool_unlock(struct ol_tx_desc_t *tx_desc) +{ +} +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS +static inline struct htt_tx_compl_ind_append_tx_tstamp *ol_tx_get_txtstamps( + u_int32_t *msg_word_header, u_int32_t **msg_word_payload, + int num_msdus) +{ + u_int32_t has_tx_tsf; + u_int32_t has_retry; + + struct htt_tx_compl_ind_append_tx_tstamp *txtstamp_list = NULL; + struct htt_tx_compl_ind_append_retries *retry_list = NULL; + int offset_dwords; + + if (num_msdus <= 0) + return NULL; + + has_tx_tsf = HTT_TX_COMPL_IND_APPEND1_GET(*msg_word_header); + + /* skip header and MSDUx ID part*/ + offset_dwords = ((num_msdus + 1) >> 1); + *msg_word_payload += offset_dwords; + + if (!has_tx_tsf) + return NULL; + + has_retry = HTT_TX_COMPL_IND_APPEND_GET(*msg_word_header); + if (has_retry) { + int retry_index = 0; + int width_for_each_retry = + (sizeof(struct htt_tx_compl_ind_append_retries) + + 3) >> 2; + + retry_list = (struct htt_tx_compl_ind_append_retries *) + (*msg_word_payload + offset_dwords); + while (retry_list) { + if (retry_list[retry_index++].flag == 0) + break; + } + offset_dwords = retry_index * width_for_each_retry; + } + + *msg_word_payload += offset_dwords; + txtstamp_list = (struct htt_tx_compl_ind_append_tx_tstamp *) + (*msg_word_payload); + return txtstamp_list; +} + +static inline +struct htt_tx_compl_ind_append_tx_tsf64 *ol_tx_get_txtstamp64s( + u_int32_t *msg_word_header, u_int32_t **msg_word_payload, + int num_msdus) +{ + u_int32_t has_tx_tstamp64; + u_int32_t has_rssi; + struct htt_tx_compl_ind_append_tx_tsf64 *txtstamp64_list = NULL; + + int offset_dwords = 0; + + if (num_msdus <= 0) + return NULL; + + has_tx_tstamp64 = HTT_TX_COMPL_IND_APPEND3_GET(*msg_word_header); + if (!has_tx_tstamp64) + return NULL; + + /*skip MSDUx ACK RSSI part*/ + has_rssi = HTT_TX_COMPL_IND_APPEND2_GET(*msg_word_header); + if (has_rssi) + offset_dwords = ((num_msdus + 1) >> 1); + + *msg_word_payload = *msg_word_payload + offset_dwords; + txtstamp64_list = + (struct htt_tx_compl_ind_append_tx_tsf64 *) + (*msg_word_payload); + + return txtstamp64_list; +} + +static inline void ol_tx_timestamp(ol_txrx_pdev_handle pdev, + qdf_nbuf_t netbuf, u_int64_t ts) +{ + if (!netbuf) + return; + + if (pdev->ol_tx_timestamp_cb) + pdev->ol_tx_timestamp_cb(netbuf, ts); +} +#else +static inline struct htt_tx_compl_ind_append_tx_tstamp *ol_tx_get_txtstamps( + u_int32_t *msg_word_header, u_int32_t **msg_word_payload, + int num_msdus) +{ + return NULL; +} + +static inline +struct htt_tx_compl_ind_append_tx_tsf64 *ol_tx_get_txtstamp64s( + u_int32_t *msg_word_header, u_int32_t **msg_word_payload, + int num_msdus) +{ + return NULL; +} + +static inline void ol_tx_timestamp(ol_txrx_pdev_handle pdev, + qdf_nbuf_t netbuf, u_int64_t ts) +{ +} +#endif + +static void ol_tx_update_ack_count(struct ol_tx_desc_t *tx_desc, + enum htt_tx_status status) +{ + if (!tx_desc->vdev) + return; + + if (status == htt_tx_status_ok) + ++tx_desc->vdev->txrx_stats.txack_success; + else + ++tx_desc->vdev->txrx_stats.txack_failed; +} + +/** + * ol_tx_notify_completion() - Notify tx completion for this desc + * @tx_desc: tx desc + * @netbuf: buffer + * + * Return: none + */ +static void ol_tx_notify_completion(struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t netbuf) +{ + void *osif_dev; + ol_txrx_completion_fp tx_compl_cbk = NULL; + + qdf_assert(tx_desc); + + ol_tx_flow_pool_lock(tx_desc); + + if (!tx_desc->vdev || + !tx_desc->vdev->osif_dev) { + ol_tx_flow_pool_unlock(tx_desc); + return; + } + osif_dev = tx_desc->vdev->osif_dev; + tx_compl_cbk = tx_desc->vdev->tx_comp; + ol_tx_flow_pool_unlock(tx_desc); + + if (tx_compl_cbk) + tx_compl_cbk(netbuf, osif_dev); +} + +/** + * ol_tx_update_connectivity_stats() - update connectivity stats + * @tx_desc: tx desc + * @netbuf: buffer + * @status: htt status + * + * + * Return: none + */ +static void ol_tx_update_connectivity_stats(struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t netbuf, + enum htt_tx_status status) +{ + void *osif_dev; + uint32_t pkt_type_bitmap; + ol_txrx_stats_rx_fp stats_rx = NULL; + uint8_t pkt_type = 0; + + qdf_assert(tx_desc); + + ol_tx_flow_pool_lock(tx_desc); + /* + * In cases when vdev has gone down and tx completion + * are received, leads to NULL vdev access. + * So, check for NULL before dereferencing it. + */ + if (!tx_desc->vdev || + !tx_desc->vdev->osif_dev || + !tx_desc->vdev->stats_rx) { + ol_tx_flow_pool_unlock(tx_desc); + return; + } + osif_dev = tx_desc->vdev->osif_dev; + stats_rx = tx_desc->vdev->stats_rx; + ol_tx_flow_pool_unlock(tx_desc); + + pkt_type_bitmap = cds_get_connectivity_stats_pkt_bitmap(osif_dev); + + if (pkt_type_bitmap) { + if (status != htt_tx_status_download_fail) + stats_rx(netbuf, osif_dev, + PKT_TYPE_TX_HOST_FW_SENT, &pkt_type); + if (status == htt_tx_status_ok) + stats_rx(netbuf, osif_dev, + PKT_TYPE_TX_ACK_CNT, &pkt_type); + } +} + +/** + * ol_tx_update_arp_stats() - update ARP packet TX stats + * @tx_desc: tx desc + * @netbuf: buffer + * @status: htt status + * + * + * Return: none + */ +static void ol_tx_update_arp_stats(struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t netbuf, + enum htt_tx_status status) +{ + uint32_t tgt_ip; + + qdf_assert(tx_desc); + + ol_tx_flow_pool_lock(tx_desc); + if (!tx_desc->vdev) { + ol_tx_flow_pool_unlock(tx_desc); + return; + } + + tgt_ip = cds_get_arp_stats_gw_ip(tx_desc->vdev->osif_dev); + ol_tx_flow_pool_unlock(tx_desc); + + if (tgt_ip == qdf_nbuf_get_arp_tgt_ip(netbuf)) { + if (status != htt_tx_status_download_fail) + cds_incr_arp_stats_tx_tgt_delivered(); + if (status == htt_tx_status_ok) + cds_incr_arp_stats_tx_tgt_acked(); + } +} + +/** + * WARNING: ol_tx_inspect_handler()'s behavior is similar to that of + * ol_tx_completion_handler(). + * any change in ol_tx_completion_handler() must be mirrored in + * ol_tx_inspect_handler(). + */ +void +ol_tx_completion_handler(ol_txrx_pdev_handle pdev, + int num_msdus, + enum htt_tx_status status, void *msg) +{ + int i; + uint16_t tx_desc_id; + struct ol_tx_desc_t *tx_desc; + uint32_t byte_cnt = 0; + qdf_nbuf_t netbuf; +#if !defined(REMOVE_PKT_LOG) + ol_txrx_pktdump_cb packetdump_cb; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); +#endif + uint32_t is_tx_desc_freed = 0; + struct htt_tx_compl_ind_append_tx_tstamp *txtstamp_list = NULL; + struct htt_tx_compl_ind_append_tx_tsf64 *txtstamp64_list = NULL; + u_int32_t *msg_word_header = (u_int32_t *)msg; + /*msg_word skip header*/ + u_int32_t *msg_word_payload = msg_word_header + 1; + u_int32_t *msg_word = (u_int32_t *)msg; + u_int16_t *desc_ids = (u_int16_t *)(msg_word + 1); + union ol_tx_desc_list_elem_t *lcl_freelist = NULL; + union ol_tx_desc_list_elem_t *tx_desc_last = NULL; + ol_tx_desc_list tx_descs; + uint64_t tx_tsf64; + + TAILQ_INIT(&tx_descs); + + ol_tx_delay_compute(pdev, status, desc_ids, num_msdus); + if (status == htt_tx_status_ok) { + txtstamp_list = ol_tx_get_txtstamps( + msg_word_header, &msg_word_payload, num_msdus); + if (pdev->enable_tx_compl_tsf64) + txtstamp64_list = ol_tx_get_txtstamp64s( + msg_word_header, &msg_word_payload, num_msdus); + } + + for (i = 0; i < num_msdus; i++) { + tx_desc_id = desc_ids[i]; + if (tx_desc_id >= pdev->tx_desc.pool_size) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: drop due to invalid msdu id = %x\n", + __func__, tx_desc_id); + continue; + } + tx_desc = ol_tx_desc_find(pdev, tx_desc_id); + qdf_assert(tx_desc); + ol_tx_desc_update_comp_ts(tx_desc); + tx_desc->status = status; + netbuf = tx_desc->netbuf; + + if (txtstamp64_list) { + tx_tsf64 = + (u_int64_t)txtstamp64_list[i].tx_tsf64_high << 32 | + txtstamp64_list[i].tx_tsf64_low; + + ol_tx_timestamp(pdev, netbuf, tx_tsf64); + } else if (txtstamp_list) + ol_tx_timestamp(pdev, netbuf, + (u_int64_t)txtstamp_list->timestamp[i] + ); + + QDF_NBUF_UPDATE_TX_PKT_COUNT(netbuf, QDF_NBUF_TX_PKT_FREE); + + if (QDF_NBUF_CB_GET_PACKET_TYPE(netbuf) == + QDF_NBUF_CB_PACKET_TYPE_ARP) { + if (qdf_nbuf_data_is_arp_req(netbuf)) + ol_tx_update_arp_stats(tx_desc, netbuf, + status); + } + + /* check tx completion notification */ + if (QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_NOTIFY_COMP(netbuf)) + ol_tx_notify_completion(tx_desc, netbuf); + + /* track connectivity stats */ + ol_tx_update_connectivity_stats(tx_desc, netbuf, + status); + ol_tx_update_ack_count(tx_desc, status); + +#if !defined(REMOVE_PKT_LOG) + if (tx_desc->pkt_type != OL_TX_FRM_TSO) { + packetdump_cb = pdev->ol_tx_packetdump_cb; + if (packetdump_cb) + packetdump_cb(soc, pdev->id, + tx_desc->vdev_id, + netbuf, status, TX_DATA_PKT); + } +#endif + + DPTRACE(qdf_dp_trace_ptr(netbuf, + QDF_DP_TRACE_FREE_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(netbuf), + sizeof(qdf_nbuf_data(netbuf)), tx_desc->id, status)); + htc_pm_runtime_put(pdev->htt_pdev->htc_pdev); + /* + * If credits are reported through credit_update_ind then do not + * update group credits on tx_complete_ind. + */ + if (!pdev->cfg.credit_update_enabled) + ol_tx_desc_update_group_credit(pdev, + tx_desc_id, + 1, 0, status); + /* Per SDU update of byte count */ + byte_cnt += qdf_nbuf_len(netbuf); + if (OL_TX_DESC_NO_REFS(tx_desc)) { + ol_tx_statistics( + pdev->ctrl_pdev, + HTT_TX_DESC_VDEV_ID_GET(*((uint32_t *) + (tx_desc-> + htt_tx_desc))), + status != htt_tx_status_ok); + ol_tx_msdu_complete(pdev, tx_desc, tx_descs, netbuf, + lcl_freelist, tx_desc_last, status, + is_tx_desc_freed); + +#ifdef QCA_SUPPORT_TXDESC_SANITY_CHECKS + if (!is_tx_desc_freed) { + tx_desc->pkt_type = ol_tx_frm_freed; +#ifdef QCA_COMPUTE_TX_DELAY + tx_desc->entry_timestamp_ticks = 0xffffffff; +#endif + } +#endif + } + } + + /* One shot protected access to pdev freelist, when setup */ + if (lcl_freelist) { + qdf_spin_lock(&pdev->tx_mutex); + tx_desc_last->next = pdev->tx_desc.freelist; + pdev->tx_desc.freelist = lcl_freelist; + pdev->tx_desc.num_free += (uint16_t) num_msdus; + qdf_spin_unlock(&pdev->tx_mutex); + } else { + ol_tx_desc_frame_list_free(pdev, &tx_descs, + status != htt_tx_status_ok); + } + + if (pdev->cfg.is_high_latency) { + /* + * Credit was already explicitly updated by HTT, + * but update the number of available tx descriptors, + * then invoke the scheduler, since new credit is probably + * available now. + */ + qdf_atomic_add(num_msdus, &pdev->tx_queue.rsrc_cnt); + ol_tx_sched(pdev); + } else { + ol_tx_target_credit_adjust(num_msdus, pdev, NULL); + } + + /* UNPAUSE OS Q */ + ol_tx_flow_ct_unpause_os_q(pdev); + /* Do one shot statistics */ + TXRX_STATS_UPDATE_TX_STATS(pdev, status, num_msdus, byte_cnt); +} + +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + +void ol_tx_desc_update_group_credit(ol_txrx_pdev_handle pdev, + u_int16_t tx_desc_id, int credit, u_int8_t absolute, + enum htt_tx_status status) +{ + uint8_t i, is_member; + uint16_t vdev_id_mask; + struct ol_tx_desc_t *tx_desc; + + if (tx_desc_id >= pdev->tx_desc.pool_size) { + qdf_print("Invalid desc id"); + return; + } + + tx_desc = ol_tx_desc_find(pdev, tx_desc_id); + for (i = 0; i < OL_TX_MAX_TXQ_GROUPS; i++) { + vdev_id_mask = + OL_TXQ_GROUP_VDEV_ID_MASK_GET( + pdev->txq_grps[i].membership); + is_member = OL_TXQ_GROUP_VDEV_ID_BIT_MASK_GET(vdev_id_mask, + tx_desc->vdev_id); + if (is_member) { + ol_txrx_update_group_credit(&pdev->txq_grps[i], + credit, absolute); + break; + } + } + ol_tx_update_group_credit_stats(pdev); +} + +void ol_tx_deduct_one_any_group_credit(ol_txrx_pdev_handle pdev) +{ + int credits_group_0, credits_group_1; + + qdf_spin_lock_bh(&pdev->tx_queue_spinlock); + credits_group_0 = qdf_atomic_read(&pdev->txq_grps[0].credit); + credits_group_1 = qdf_atomic_read(&pdev->txq_grps[1].credit); + + if (credits_group_0 > credits_group_1) + ol_txrx_update_group_credit(&pdev->txq_grps[0], -1, 0); + else if (credits_group_1 != 0) + ol_txrx_update_group_credit(&pdev->txq_grps[1], -1, 0); + + qdf_spin_unlock_bh(&pdev->tx_queue_spinlock); +} + +#ifdef DEBUG_HL_LOGGING + +void ol_tx_update_group_credit_stats(ol_txrx_pdev_handle pdev) +{ + uint16_t curr_index; + uint8_t i; + + qdf_spin_lock_bh(&pdev->grp_stat_spinlock); + pdev->grp_stats.last_valid_index++; + if (pdev->grp_stats.last_valid_index > (OL_TX_GROUP_STATS_LOG_SIZE + - 1)) { + pdev->grp_stats.last_valid_index -= OL_TX_GROUP_STATS_LOG_SIZE; + pdev->grp_stats.wrap_around = 1; + } + curr_index = pdev->grp_stats.last_valid_index; + + for (i = 0; i < OL_TX_MAX_TXQ_GROUPS; i++) { + pdev->grp_stats.stats[curr_index].grp[i].member_vdevs = + OL_TXQ_GROUP_VDEV_ID_MASK_GET( + pdev->txq_grps[i].membership); + pdev->grp_stats.stats[curr_index].grp[i].credit = + qdf_atomic_read(&pdev->txq_grps[i].credit); + } + + qdf_spin_unlock_bh(&pdev->grp_stat_spinlock); +} + +void ol_tx_dump_group_credit_stats(ol_txrx_pdev_handle pdev) +{ + uint16_t i, j, is_break = 0; + int16_t curr_index, old_index, wrap_around; + uint16_t curr_credit, mem_vdevs; + uint16_t old_credit = 0; + + txrx_nofl_info("Group credit stats:"); + txrx_nofl_info(" No: GrpID: Credit: Change: vdev_map"); + + qdf_spin_lock_bh(&pdev->grp_stat_spinlock); + curr_index = pdev->grp_stats.last_valid_index; + wrap_around = pdev->grp_stats.wrap_around; + qdf_spin_unlock_bh(&pdev->grp_stat_spinlock); + + if (curr_index < 0) { + txrx_nofl_info("Not initialized"); + return; + } + + for (i = 0; i < OL_TX_GROUP_STATS_LOG_SIZE; i++) { + old_index = curr_index - 1; + if (old_index < 0) { + if (wrap_around == 0) + is_break = 1; + else + old_index = OL_TX_GROUP_STATS_LOG_SIZE - 1; + } + + for (j = 0; j < OL_TX_MAX_TXQ_GROUPS; j++) { + qdf_spin_lock_bh(&pdev->grp_stat_spinlock); + curr_credit = + pdev->grp_stats.stats[curr_index]. + grp[j].credit; + if (!is_break) + old_credit = + pdev->grp_stats.stats[old_index]. + grp[j].credit; + + mem_vdevs = + pdev->grp_stats.stats[curr_index].grp[j]. + member_vdevs; + qdf_spin_unlock_bh(&pdev->grp_stat_spinlock); + + if (!is_break) + txrx_nofl_info("%4d: %5d: %6d %6d %8x", + curr_index, j, + curr_credit, + (curr_credit - old_credit), + mem_vdevs); + else + txrx_nofl_info("%4d: %5d: %6d %6s %8x", + curr_index, j, + curr_credit, "NA", mem_vdevs); + } + + if (is_break) + break; + + curr_index = old_index; + } +} + +void ol_tx_clear_group_credit_stats(ol_txrx_pdev_handle pdev) +{ + qdf_spin_lock_bh(&pdev->grp_stat_spinlock); + qdf_mem_zero(&pdev->grp_stats, sizeof(pdev->grp_stats)); + pdev->grp_stats.last_valid_index = -1; + pdev->grp_stats.wrap_around = 0; + qdf_spin_unlock_bh(&pdev->grp_stat_spinlock); +} +#endif +#endif + +/* + * ol_tx_single_completion_handler performs the same tx completion + * processing as ol_tx_completion_handler, but for a single frame. + * ol_tx_completion_handler is optimized to handle batch completions + * as efficiently as possible; in contrast ol_tx_single_completion_handler + * handles single frames as simply and generally as possible. + * Thus, this ol_tx_single_completion_handler function is suitable for + * intermittent usage, such as for tx mgmt frames. + */ +void +ol_tx_single_completion_handler(ol_txrx_pdev_handle pdev, + enum htt_tx_status status, uint16_t tx_desc_id) +{ + struct ol_tx_desc_t *tx_desc; + qdf_nbuf_t netbuf; +#if !defined(REMOVE_PKT_LOG) + ol_txrx_pktdump_cb packetdump_cb; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); +#endif + + tx_desc = ol_tx_desc_find_check(pdev, tx_desc_id); + if (!tx_desc) { + ol_txrx_err("invalid desc_id(%u), ignore it", tx_desc_id); + return; + } + + tx_desc->status = status; + netbuf = tx_desc->netbuf; + + QDF_NBUF_UPDATE_TX_PKT_COUNT(netbuf, QDF_NBUF_TX_PKT_FREE); + /* Do one shot statistics */ + TXRX_STATS_UPDATE_TX_STATS(pdev, status, 1, qdf_nbuf_len(netbuf)); + +#if !defined(REMOVE_PKT_LOG) + packetdump_cb = pdev->ol_tx_packetdump_cb; + if (packetdump_cb) + packetdump_cb(soc, pdev->id, tx_desc->vdev_id, + netbuf, status, TX_MGMT_PKT); +#endif + + if (OL_TX_DESC_NO_REFS(tx_desc)) { + ol_tx_desc_frame_free_nonstd(pdev, tx_desc, + status != htt_tx_status_ok); + } + + TX_CREDIT_DEBUG_PRINT(" Increase credit %d + %d = %d\n", + qdf_atomic_read(&pdev->target_tx_credit), + 1, qdf_atomic_read(&pdev->target_tx_credit) + 1); + + if (pdev->cfg.is_high_latency) { + /* + * Credit was already explicitly updated by HTT, + * but update the number of available tx descriptors, + * then invoke the scheduler, since new credit is probably + * available now. + */ + qdf_atomic_add(1, &pdev->tx_queue.rsrc_cnt); + ol_tx_sched(pdev); + } else { + qdf_atomic_add(1, &pdev->target_tx_credit); + } +} + +/** + * WARNING: ol_tx_inspect_handler()'s behavior is similar to that of + * ol_tx_completion_handler(). + * any change in ol_tx_completion_handler() must be mirrored here. + */ +void +ol_tx_inspect_handler(ol_txrx_pdev_handle pdev, + int num_msdus, void *tx_desc_id_iterator) +{ + uint16_t vdev_id, i; + struct ol_txrx_vdev_t *vdev; + uint16_t *desc_ids = (uint16_t *) tx_desc_id_iterator; + uint16_t tx_desc_id; + struct ol_tx_desc_t *tx_desc; + union ol_tx_desc_list_elem_t *lcl_freelist = NULL; + union ol_tx_desc_list_elem_t *tx_desc_last = NULL; + qdf_nbuf_t netbuf; + ol_tx_desc_list tx_descs; + uint32_t is_tx_desc_freed = 0; + + TAILQ_INIT(&tx_descs); + + for (i = 0; i < num_msdus; i++) { + tx_desc_id = desc_ids[i]; + if (tx_desc_id >= pdev->tx_desc.pool_size) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: drop due to invalid msdu id = %x\n", + __func__, tx_desc_id); + continue; + } + tx_desc = ol_tx_desc_find(pdev, tx_desc_id); + qdf_assert(tx_desc); + ol_tx_desc_update_comp_ts(tx_desc); + netbuf = tx_desc->netbuf; + + /* find the "vdev" this tx_desc belongs to */ + vdev_id = HTT_TX_DESC_VDEV_ID_GET(*((uint32_t *) + (tx_desc->htt_tx_desc))); + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + if (vdev->vdev_id == vdev_id) + break; + } + + /* vdev now points to the vdev for this descriptor. */ + +#ifndef ATH_11AC_TXCOMPACT + /* save this multicast packet to local free list */ + if (qdf_atomic_dec_and_test(&tx_desc->ref_cnt)) +#endif + { + /* + * For this function only, force htt status to be + * "htt_tx_status_ok" + * for graceful freeing of this multicast frame + */ + ol_tx_msdu_complete(pdev, tx_desc, tx_descs, netbuf, + lcl_freelist, tx_desc_last, + htt_tx_status_ok, + is_tx_desc_freed); +#ifdef QCA_SUPPORT_TXDESC_SANITY_CHECKS + if (!is_tx_desc_freed) { + tx_desc->pkt_type = ol_tx_frm_freed; +#ifdef QCA_COMPUTE_TX_DELAY + tx_desc->entry_timestamp_ticks = 0xffffffff; +#endif + } +#endif + } + } + + if (lcl_freelist) { + qdf_spin_lock(&pdev->tx_mutex); + tx_desc_last->next = pdev->tx_desc.freelist; + pdev->tx_desc.freelist = lcl_freelist; + qdf_spin_unlock(&pdev->tx_mutex); + } else { + ol_tx_desc_frame_list_free(pdev, &tx_descs, + htt_tx_status_discard); + } + TX_CREDIT_DEBUG_PRINT(" Increase HTT credit %d + %d = %d..\n", + qdf_atomic_read(&pdev->target_tx_credit), + num_msdus, + qdf_atomic_read(&pdev->target_tx_credit) + + num_msdus); + + if (pdev->cfg.is_high_latency) { + /* credit was already explicitly updated by HTT */ + ol_tx_sched(pdev); + } else { + ol_tx_target_credit_adjust(num_msdus, pdev, NULL); + } +} + +#ifdef QCA_COMPUTE_TX_DELAY +/** + * ol_tx_set_compute_interval - updates the compute interval + * period for TSM stats. + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @param interval: interval for stats computation + * + * Return: None + */ +void ol_tx_set_compute_interval(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, uint32_t interval) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + pdev->tx_delay.avg_period_ticks = qdf_system_msecs_to_ticks(interval); +} + +/** + * ol_tx_packet_count() - Return the uplink (transmitted) packet count + and loss count. + * @soc_hdl: soc handle + * @pdev_id: pdev identifier + * @out_packet_count - number of packets transmitted + * @out_packet_loss_count - number of packets lost + * @category - access category of interest + * + * This function will be called for getting uplink packet count and + * loss count for given stream (access category) a regular interval. + * This also resets the counters hence, the value returned is packets + * counted in last 5(default) second interval. These counter are + * incremented per access category in ol_tx_completion_handler() + */ +void +ol_tx_packet_count(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint16_t *out_packet_count, + uint16_t *out_packet_loss_count, int category) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + *out_packet_count = pdev->packet_count[category]; + *out_packet_loss_count = pdev->packet_loss_count[category]; + pdev->packet_count[category] = 0; + pdev->packet_loss_count[category] = 0; +} + +static uint32_t ol_tx_delay_avg(uint64_t sum, uint32_t num) +{ + uint32_t sum32; + int shift = 0; + /* + * To avoid doing a 64-bit divide, shift the sum down until it is + * no more than 32 bits (and shift the denominator to match). + */ + while ((sum >> 32) != 0) { + sum >>= 1; + shift++; + } + sum32 = (uint32_t) sum; + num >>= shift; + return (sum32 + (num >> 1)) / num; /* round to nearest */ +} + +void +ol_tx_delay(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint32_t *queue_delay_microsec, + uint32_t *tx_delay_microsec, int category) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + int index; + uint32_t avg_delay_ticks; + struct ol_tx_delay_data *data; + + qdf_assert(category >= 0 && category < QCA_TX_DELAY_NUM_CATEGORIES); + + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + qdf_spin_lock_bh(&pdev->tx_delay.mutex); + index = 1 - pdev->tx_delay.cats[category].in_progress_idx; + + data = &pdev->tx_delay.cats[category].copies[index]; + + if (data->avgs.transmit_num > 0) { + avg_delay_ticks = + ol_tx_delay_avg(data->avgs.transmit_sum_ticks, + data->avgs.transmit_num); + *tx_delay_microsec = + qdf_system_ticks_to_msecs(avg_delay_ticks * 1000); + } else { + /* + * This case should only happen if there's a query + * within 5 sec after the first tx data frame. + */ + *tx_delay_microsec = 0; + } + if (data->avgs.queue_num > 0) { + avg_delay_ticks = + ol_tx_delay_avg(data->avgs.queue_sum_ticks, + data->avgs.queue_num); + *queue_delay_microsec = + qdf_system_ticks_to_msecs(avg_delay_ticks * 1000); + } else { + /* + * This case should only happen if there's a query + * within 5 sec after the first tx data frame. + */ + *queue_delay_microsec = 0; + } + + qdf_spin_unlock_bh(&pdev->tx_delay.mutex); +} + +void +ol_tx_delay_hist(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint16_t *report_bin_values, int category) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + int index, i, j; + struct ol_tx_delay_data *data; + + qdf_assert(category >= 0 && category < QCA_TX_DELAY_NUM_CATEGORIES); + + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + qdf_spin_lock_bh(&pdev->tx_delay.mutex); + index = 1 - pdev->tx_delay.cats[category].in_progress_idx; + + data = &pdev->tx_delay.cats[category].copies[index]; + + for (i = 0, j = 0; i < QCA_TX_DELAY_HIST_REPORT_BINS - 1; i++) { + uint16_t internal_bin_sum = 0; + + while (j < (1 << i)) + internal_bin_sum += data->hist_bins_queue[j++]; + + report_bin_values[i] = internal_bin_sum; + } + report_bin_values[i] = data->hist_bins_queue[j]; /* overflow */ + + qdf_spin_unlock_bh(&pdev->tx_delay.mutex); +} + +#ifdef QCA_COMPUTE_TX_DELAY_PER_TID +static uint8_t +ol_tx_delay_tid_from_l3_hdr(struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t msdu, struct ol_tx_desc_t *tx_desc) +{ + uint16_t ethertype; + uint8_t *dest_addr, *l3_hdr; + int is_mgmt, is_mcast; + int l2_hdr_size; + + dest_addr = ol_tx_dest_addr_find(pdev, msdu); + if (!dest_addr) + return QDF_NBUF_TX_EXT_TID_INVALID; + + is_mcast = IEEE80211_IS_MULTICAST(dest_addr); + is_mgmt = tx_desc->pkt_type >= OL_TXRX_MGMT_TYPE_BASE; + if (is_mgmt) { + return (is_mcast) ? + OL_TX_NUM_TIDS + OL_TX_VDEV_DEFAULT_MGMT : + HTT_TX_EXT_TID_MGMT; + } + if (is_mcast) + return OL_TX_NUM_TIDS + OL_TX_VDEV_MCAST_BCAST; + + if (pdev->frame_format == wlan_frm_fmt_802_3) { + struct ethernet_hdr_t *enet_hdr; + + enet_hdr = (struct ethernet_hdr_t *)qdf_nbuf_data(msdu); + l2_hdr_size = sizeof(struct ethernet_hdr_t); + ethertype = + (enet_hdr->ethertype[0] << 8) | enet_hdr->ethertype[1]; + if (!IS_ETHERTYPE(ethertype)) { + struct llc_snap_hdr_t *llc_hdr; + + llc_hdr = (struct llc_snap_hdr_t *) + (qdf_nbuf_data(msdu) + l2_hdr_size); + l2_hdr_size += sizeof(struct llc_snap_hdr_t); + ethertype = + (llc_hdr->ethertype[0] << 8) | llc_hdr-> + ethertype[1]; + } + } else { + struct llc_snap_hdr_t *llc_hdr; + + l2_hdr_size = sizeof(struct ieee80211_frame); + llc_hdr = (struct llc_snap_hdr_t *)(qdf_nbuf_data(msdu) + + l2_hdr_size); + l2_hdr_size += sizeof(struct llc_snap_hdr_t); + ethertype = + (llc_hdr->ethertype[0] << 8) | llc_hdr->ethertype[1]; + } + l3_hdr = qdf_nbuf_data(msdu) + l2_hdr_size; + if (ETHERTYPE_IPV4 == ethertype) { + return (((struct ipv4_hdr_t *)l3_hdr)->tos >> 5) & 0x7; + } else if (ETHERTYPE_IPV6 == ethertype) { + return (ipv6_traffic_class((struct ipv6_hdr_t *)l3_hdr) >> 5) & + 0x7; + } else { + return QDF_NBUF_TX_EXT_TID_INVALID; + } +} + +static int ol_tx_delay_category(struct ol_txrx_pdev_t *pdev, uint16_t msdu_id) +{ + struct ol_tx_desc_t *tx_desc = ol_tx_desc_find(pdev, msdu_id); + uint8_t tid; + qdf_nbuf_t msdu = tx_desc->netbuf; + + tid = qdf_nbuf_get_tid(msdu); + if (tid == QDF_NBUF_TX_EXT_TID_INVALID) { + tid = ol_tx_delay_tid_from_l3_hdr(pdev, msdu, tx_desc); + if (tid == QDF_NBUF_TX_EXT_TID_INVALID) { + /* + * TID could not be determined + * (this is not an IP frame?) + */ + return -EINVAL; + } + } + return tid; +} +#else +static int ol_tx_delay_category(struct ol_txrx_pdev_t *pdev, uint16_t msdu_id) +{ + return 0; +} +#endif + +static inline int +ol_tx_delay_hist_bin(struct ol_txrx_pdev_t *pdev, uint32_t delay_ticks) +{ + int bin; + /* + * For speed, multiply and shift to approximate a divide. This causes + * a small error, but the approximation error should be much less + * than the other uncertainties in the tx delay computation. + */ + bin = (delay_ticks * pdev->tx_delay.hist_internal_bin_width_mult) >> + pdev->tx_delay.hist_internal_bin_width_shift; + if (bin >= QCA_TX_DELAY_HIST_INTERNAL_BINS) + bin = QCA_TX_DELAY_HIST_INTERNAL_BINS - 1; + + return bin; +} + +static void +ol_tx_delay_compute(struct ol_txrx_pdev_t *pdev, + enum htt_tx_status status, + uint16_t *desc_ids, int num_msdus) +{ + int i, index, cat; + uint32_t now_ticks = qdf_system_ticks(); + uint32_t tx_delay_transmit_ticks, tx_delay_queue_ticks; + uint32_t avg_time_ticks; + struct ol_tx_delay_data *data; + + qdf_assert(num_msdus > 0); + + /* + * keep static counters for total packet and lost packets + * reset them in ol_tx_delay(), function used to fetch the stats + */ + + cat = ol_tx_delay_category(pdev, desc_ids[0]); + if (cat < 0 || cat >= QCA_TX_DELAY_NUM_CATEGORIES) + return; + + pdev->packet_count[cat] = pdev->packet_count[cat] + num_msdus; + if (status != htt_tx_status_ok) { + for (i = 0; i < num_msdus; i++) { + cat = ol_tx_delay_category(pdev, desc_ids[i]); + if (cat < 0 || cat >= QCA_TX_DELAY_NUM_CATEGORIES) + return; + pdev->packet_loss_count[cat]++; + } + return; + } + + /* since we may switch the ping-pong index, provide mutex w. readers */ + qdf_spin_lock_bh(&pdev->tx_delay.mutex); + index = pdev->tx_delay.cats[cat].in_progress_idx; + + data = &pdev->tx_delay.cats[cat].copies[index]; + + if (pdev->tx_delay.tx_compl_timestamp_ticks != 0) { + tx_delay_transmit_ticks = + now_ticks - pdev->tx_delay.tx_compl_timestamp_ticks; + /* + * We'd like to account for the number of MSDUs that were + * transmitted together, but we don't know this. All we know + * is the number of MSDUs that were acked together. + * Since the frame error rate is small, this is nearly the same + * as the number of frames transmitted together. + */ + data->avgs.transmit_sum_ticks += tx_delay_transmit_ticks; + data->avgs.transmit_num += num_msdus; + } + pdev->tx_delay.tx_compl_timestamp_ticks = now_ticks; + + for (i = 0; i < num_msdus; i++) { + int bin; + uint16_t id = desc_ids[i]; + struct ol_tx_desc_t *tx_desc = ol_tx_desc_find(pdev, id); + + tx_delay_queue_ticks = + now_ticks - tx_desc->entry_timestamp_ticks; + + data->avgs.queue_sum_ticks += tx_delay_queue_ticks; + data->avgs.queue_num++; + bin = ol_tx_delay_hist_bin(pdev, tx_delay_queue_ticks); + data->hist_bins_queue[bin]++; + } + + /* check if it's time to start a new average */ + avg_time_ticks = + now_ticks - pdev->tx_delay.cats[cat].avg_start_time_ticks; + if (avg_time_ticks > pdev->tx_delay.avg_period_ticks) { + pdev->tx_delay.cats[cat].avg_start_time_ticks = now_ticks; + index = 1 - index; + pdev->tx_delay.cats[cat].in_progress_idx = index; + qdf_mem_zero(&pdev->tx_delay.cats[cat].copies[index], + sizeof(pdev->tx_delay.cats[cat].copies[index])); + } + + qdf_spin_unlock_bh(&pdev->tx_delay.mutex); +} + +#endif /* QCA_COMPUTE_TX_DELAY */ + +#ifdef WLAN_FEATURE_TSF_PLUS +void ol_register_timestamp_callback(tp_ol_timestamp_cb ol_tx_timestamp_cb) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + pdev->ol_tx_timestamp_cb = ol_tx_timestamp_cb; +} + +void ol_deregister_timestamp_callback(void) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + pdev->ol_tx_timestamp_cb = NULL; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_send.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_send.h new file mode 100644 index 0000000000000000000000000000000000000000..a3e84ab61c0b955d7c9f9845cd26985155ebfdf3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_send.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2011, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_tx_send.h + * @brief API definitions for the tx sendriptor module within the data SW. + */ +#ifndef _OL_TX_SEND__H_ +#define _OL_TX_SEND__H_ + +#include /* qdf_nbuf_t */ +#include /* ol_txrx_vdev_t, etc. */ + +#if defined(CONFIG_HL_SUPPORT) + +static inline void ol_tx_discard_target_frms(ol_txrx_pdev_handle pdev) +{ +} +#else + +/** + * @flush the ol tx when surprise remove. + * + */ +void ol_tx_discard_target_frms(ol_txrx_pdev_handle pdev); +#endif + +/** + * @brief Send a tx frame to the target. + * @details + * + * @param pdev - the phy dev + * @param vdev - the virtual device sending the data + * (for specifying the transmitter address for multicast / broadcast data) + * @param netbuf - the tx frame + */ +void +ol_tx_send(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc, qdf_nbuf_t msdu, uint8_t vdev_id); + +/** + * @brief Send a tx batch download to the target. + * @details + * This function is different from above in that + * it accepts a list of msdu's to be downloaded as a batch + * + * @param pdev - the phy dev + * @param msdu_list - the Head pointer to the Tx Batch + * @param num_msdus - Total msdus chained in msdu_list + */ + +void +ol_tx_send_batch(struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t msdu_list, int num_msdus); + +/** + * @brief Send a tx frame with a non-std header or payload type to the target. + * @details + * + * @param pdev - the phy dev + * @param vdev - the virtual device sending the data + * (for specifying the transmitter address for multicast / broadcast data) + * @param netbuf - the tx frame + * @param pkt_type - what kind of non-std frame is being sent + */ +void +ol_tx_send_nonstd(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t msdu, enum htt_pkt_type pkt_type); + +#ifdef QCA_COMPUTE_TX_DELAY +/** + * ol_tx_set_compute_interval() - update compute interval period for TSM stats + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @interval: interval for stats computation + * + * Return: NONE + */ +void ol_tx_set_compute_interval(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, uint32_t interval); + +/** + * ol_tx_packet_count() - Return the uplink (transmitted) packet counts + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @out_packet_count: number of packets transmitted + * @out_packet_loss_count: number of packets lost + * @category: access category of interest + * + * This function will be called for getting uplink packet count and + * loss count for given stream (access category) a regular interval. + * This also resets the counters hence, the value returned is packets + * counted in last 5(default) second interval. These counter are + * incremented per access category in ol_tx_completion_handler() + * + * Return: NONE + */ +void +ol_tx_packet_count(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint16_t *out_packet_count, + uint16_t *out_packet_loss_count, int category); + +/** + * ol_tx_delay() - get tx packet delay + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @queue_delay_microsec: tx packet delay within queue, usec + * @tx_delay_microsec: tx packet delay, usec + * @category: packet category + * + * Return: NONE + */ +void +ol_tx_delay(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint32_t *queue_delay_microsec, + uint32_t *tx_delay_microsec, int category); + +/** + * ol_tx_delay_hist() - get tx packet delay histogram + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @report_bin_values: bin + * @category: packet category + * + * Return: NONE + */ +void +ol_tx_delay_hist(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint16_t *report_bin_values, int category); +#endif /* QCA_COMPUTE_TX_DELAY */ + +/** + * ol_txrx_flow_control_cb() - call osif flow control callback + * @soc_hdl: Datapath soc handle + * @vdev_id: id of vdev + * @tx_resume: tx resume flag + * + * Return: none + */ +void ol_txrx_flow_control_cb(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + bool tx_resume); + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || (defined(CONFIG_HL_SUPPORT) && \ + defined(QCA_HL_NETDEV_FLOW_CONTROL)) +void ol_tx_flow_ct_unpause_os_q(ol_txrx_pdev_handle pdev); +#else +static inline void ol_tx_flow_ct_unpause_os_q(ol_txrx_pdev_handle pdev) +{ +} +#endif +#endif /* _OL_TX_SEND__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_throttle.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_throttle.c new file mode 100644 index 0000000000000000000000000000000000000000..42ca4acb46555688ea6e16e09d5c21ca436ae0fb --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_tx_throttle.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_atomic_read, etc. */ +#include /* ol_cfg_addba_retry */ +#include /* HTT_TX_EXT_TID_MGMT */ +#include /* htt_tx_desc_tid */ +#include /* ol_txrx_vdev_handle */ +#include /* ol_txrx_sync, ol_tx_addba_conf */ +#include +#include /* ol_ctrl_addba_req */ +#include /* TXRX_ASSERT1, etc. */ +#include /* ol_tx_desc, ol_tx_desc_frame_list_free */ +#include /* ol_tx_vdev_ll_pause_queue_send */ +#include /* ol_tx_sched_notify, etc. */ +#include +#include /* ol_tx_desc_pool_size_hl */ +#include /* ENABLE_TX_QUEUE_LOG */ +#include /* bool */ +#include "cdp_txrx_flow_ctrl_legacy.h" +#include +#include + +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 +/** + * ol_txrx_thermal_pause() - pause due to thermal mitigation + * @pdev: pdev handle + * + * Return: none + */ +static inline +void ol_txrx_thermal_pause(struct ol_txrx_pdev_t *pdev) +{ + ol_txrx_pdev_pause(pdev, OL_TXQ_PAUSE_REASON_THERMAL_MITIGATION); +} + +/** + * ol_txrx_thermal_unpause() - unpause due to thermal mitigation + * @pdev: pdev handle + * + * Return: none + */ +static inline +void ol_txrx_thermal_unpause(struct ol_txrx_pdev_t *pdev) +{ + ol_txrx_pdev_unpause(pdev, OL_TXQ_PAUSE_REASON_THERMAL_MITIGATION); +} +#else +/** + * ol_txrx_thermal_pause() - pause due to thermal mitigation + * @pdev: pdev handle + * + * Return: none + */ +static inline +void ol_txrx_thermal_pause(struct ol_txrx_pdev_t *pdev) +{ +} + +/** + * ol_txrx_thermal_unpause() - unpause due to thermal mitigation + * @pdev: pdev handle + * + * Return: none + */ +static inline +void ol_txrx_thermal_unpause(struct ol_txrx_pdev_t *pdev) +{ + ol_tx_pdev_ll_pause_queue_send_all(pdev); +} +#endif + +static void ol_tx_pdev_throttle_phase_timer(void *context) +{ + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)context; + int ms; + enum throttle_level cur_level; + enum throttle_phase cur_phase; + + /* update the phase */ + pdev->tx_throttle.current_throttle_phase++; + + if (pdev->tx_throttle.current_throttle_phase == THROTTLE_PHASE_MAX) + pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF; + + if (pdev->tx_throttle.current_throttle_phase == THROTTLE_PHASE_OFF) { + /* Traffic is stopped */ + ol_txrx_dbg( + "throttle phase --> OFF\n"); + ol_txrx_throttle_pause(pdev); + ol_txrx_thermal_pause(pdev); + cur_level = pdev->tx_throttle.current_throttle_level; + cur_phase = pdev->tx_throttle.current_throttle_phase; + ms = pdev->tx_throttle.throttle_time_ms[cur_level][cur_phase]; + if (pdev->tx_throttle.current_throttle_level != + THROTTLE_LEVEL_0) { + ol_txrx_dbg( + "start timer %d ms\n", ms); + qdf_timer_start(&pdev->tx_throttle. + phase_timer, ms); + } + } else { + /* Traffic can go */ + ol_txrx_dbg( + "throttle phase --> ON\n"); + ol_txrx_throttle_unpause(pdev); + ol_txrx_thermal_unpause(pdev); + cur_level = pdev->tx_throttle.current_throttle_level; + cur_phase = pdev->tx_throttle.current_throttle_phase; + ms = pdev->tx_throttle.throttle_time_ms[cur_level][cur_phase]; + if (pdev->tx_throttle.current_throttle_level != + THROTTLE_LEVEL_0) { + ol_txrx_dbg("start timer %d ms\n", ms); + qdf_timer_start(&pdev->tx_throttle.phase_timer, ms); + } + } +} + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +static void ol_tx_pdev_throttle_tx_timer(void *context) +{ + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)context; + + ol_tx_pdev_ll_pause_queue_send_all(pdev); +} +#endif + +#ifdef CONFIG_HL_SUPPORT + +/** + * ol_tx_set_throttle_phase_time() - Set the thermal mitgation throttle phase + * and time + * @pdev: the peer device object + * @level: throttle phase level + * @ms: throttle time + * + * Return: None + */ +static void +ol_tx_set_throttle_phase_time(struct ol_txrx_pdev_t *pdev, int level, int *ms) +{ + qdf_timer_stop(&pdev->tx_throttle.phase_timer); + + /* Set the phase */ + if (level != THROTTLE_LEVEL_0) { + pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF; + *ms = pdev->tx_throttle.throttle_time_ms[level] + [THROTTLE_PHASE_OFF]; + + /* pause all */ + ol_txrx_throttle_pause(pdev); + } else { + pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_ON; + *ms = pdev->tx_throttle.throttle_time_ms[level] + [THROTTLE_PHASE_ON]; + + /* unpause all */ + ol_txrx_throttle_unpause(pdev); + } +} +#else + +static void +ol_tx_set_throttle_phase_time(struct ol_txrx_pdev_t *pdev, int level, int *ms) +{ + /* Reset the phase */ + pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF; + + /* Start with the new time */ + *ms = pdev->tx_throttle. + throttle_time_ms[level][THROTTLE_PHASE_OFF]; + + qdf_timer_stop(&pdev->tx_throttle.phase_timer); +} +#endif + +void ol_tx_throttle_set_level(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, int level) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + int ms = 0; + + if (level >= THROTTLE_LEVEL_MAX) { + ol_txrx_dbg("invalid throttle level set %d, ignoring", level); + return; + } + + if (qdf_unlikely(!pdev)) { + ol_txrx_err("pdev is NULL"); + return; + } + + ol_txrx_info("Setting throttle level %d\n", level); + + /* Set the current throttle level */ + pdev->tx_throttle.current_throttle_level = (enum throttle_level)level; + + ol_tx_set_throttle_phase_time(pdev, level, &ms); + + if (level != THROTTLE_LEVEL_0) + qdf_timer_start(&pdev->tx_throttle.phase_timer, ms); +} + +void ol_tx_throttle_init_period(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, int period, + uint8_t *dutycycle_level) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev; + int i; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + if (qdf_unlikely(!pdev)) { + ol_txrx_err("pdev is NULL"); + return; + } + + /* Set the current throttle level */ + pdev->tx_throttle.throttle_period_ms = period; + + ol_txrx_dbg("level OFF ON\n"); + for (i = 0; i < THROTTLE_LEVEL_MAX; i++) { + pdev->tx_throttle.throttle_time_ms[i][THROTTLE_PHASE_ON] = + pdev->tx_throttle.throttle_period_ms - + ((dutycycle_level[i] * + pdev->tx_throttle.throttle_period_ms) / 100); + pdev->tx_throttle.throttle_time_ms[i][THROTTLE_PHASE_OFF] = + pdev->tx_throttle.throttle_period_ms - + pdev->tx_throttle.throttle_time_ms[ + i][THROTTLE_PHASE_ON]; + ol_txrx_dbg("%d %d %d\n", i, + pdev->tx_throttle. + throttle_time_ms[i][THROTTLE_PHASE_OFF], + pdev->tx_throttle. + throttle_time_ms[i][THROTTLE_PHASE_ON]); + } +} + +void ol_tx_throttle_init(struct ol_txrx_pdev_t *pdev) +{ + uint32_t throttle_period; + uint8_t dutycycle_level[THROTTLE_LEVEL_MAX]; + int i; + + pdev->tx_throttle.current_throttle_level = THROTTLE_LEVEL_0; + pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF; + qdf_spinlock_create(&pdev->tx_throttle.mutex); + + throttle_period = ol_cfg_throttle_period_ms(pdev->ctrl_pdev); + + for (i = 0; i < THROTTLE_LEVEL_MAX; i++) + dutycycle_level[i] = + ol_cfg_throttle_duty_cycle_level(pdev->ctrl_pdev, i); + + ol_tx_throttle_init_period(cds_get_context(QDF_MODULE_ID_SOC), pdev->id, + throttle_period, &dutycycle_level[0]); + + qdf_timer_init(pdev->osdev, &pdev->tx_throttle.phase_timer, + ol_tx_pdev_throttle_phase_timer, pdev, + QDF_TIMER_TYPE_SW); + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + qdf_timer_init(pdev->osdev, &pdev->tx_throttle.tx_timer, + ol_tx_pdev_throttle_tx_timer, pdev, QDF_TIMER_TYPE_SW); +#endif + + pdev->tx_throttle.tx_threshold = THROTTLE_TX_THRESHOLD; +} + +void +ol_txrx_throttle_pause(ol_txrx_pdev_handle pdev) +{ + qdf_spin_lock_bh(&pdev->tx_throttle.mutex); + + if (pdev->tx_throttle.is_paused) { + qdf_spin_unlock_bh(&pdev->tx_throttle.mutex); + return; + } + + pdev->tx_throttle.is_paused = true; + qdf_spin_unlock_bh(&pdev->tx_throttle.mutex); + ol_txrx_pdev_pause(pdev, 0); +} + +void +ol_txrx_throttle_unpause(ol_txrx_pdev_handle pdev) +{ + qdf_spin_lock_bh(&pdev->tx_throttle.mutex); + + if (!pdev->tx_throttle.is_paused) { + qdf_spin_unlock_bh(&pdev->tx_throttle.mutex); + return; + } + + pdev->tx_throttle.is_paused = false; + qdf_spin_unlock_bh(&pdev->tx_throttle.mutex); + ol_txrx_pdev_unpause(pdev, 0); +} diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx.c new file mode 100644 index 0000000000000000000000000000000000000000..8466148dcaa82bd7b0d4d5a2cab6dc396f11beff --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx.c @@ -0,0 +1,6605 @@ +/* + * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*=== includes ===*/ +/* header files for OS primitives */ +#include /* uint32_t, etc. */ +#include /* qdf_mem_malloc,free */ +#include /* qdf_device_t, qdf_print */ +#include /* qdf_spinlock */ +#include /* qdf_atomic_read */ +#include + +/* header files for utilities */ +#include "queue.h" /* TAILQ */ + +/* header files for configuration API */ +#include /* ol_cfg_is_high_latency */ +#include + +/* header files for HTT API */ +#include +#include + +/* header files for our own APIs */ +#include +#include +#include +#include +#include +#include +/* header files for our internal definitions */ +#include /* TXRX_ASSERT, etc. */ +#include /* WDI events */ +#include /* ol_tx_ll */ +#include /* ol_rx_deliver */ +#include /* ol_txrx_peer_find_attach, etc. */ +#include /* ol_rx_pn_check, etc. */ +#include /* ol_rx_fwd_check, etc. */ +#include /* OL_RX_REORDER_TIMEOUT_INIT, etc. */ +#include +#include /* ol_tx_discard_target_frms */ +#include /* ol_tx_desc_frame_free */ +#include +#include /* ol_tx_sched_attach, etc. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "wma.h" +#include "hif.h" +#include "hif_main.h" +#include +#ifndef REMOVE_PKT_LOG +#include "pktlog_ac.h" +#endif +#include +#include "epping_main.h" +#include +#include +#include +#include "wlan_qct_sys.h" + +#include +#include +#include "wlan_roam_debug.h" +#include "cfg_ucfg_api.h" +#ifdef DP_SUPPORT_RECOVERY_NOTIFY +#include +#include +#endif + +#define DPT_DEBUGFS_PERMS (QDF_FILE_USR_READ | \ + QDF_FILE_USR_WRITE | \ + QDF_FILE_GRP_READ | \ + QDF_FILE_OTH_READ) + +#define DPT_DEBUGFS_NUMBER_BASE 10 +/** + * enum dpt_set_param_debugfs - dpt set params + * @DPT_SET_PARAM_PROTO_BITMAP : set proto bitmap + * @DPT_SET_PARAM_NR_RECORDS: set num of records + * @DPT_SET_PARAM_VERBOSITY: set verbosity + */ +enum dpt_set_param_debugfs { + DPT_SET_PARAM_PROTO_BITMAP = 1, + DPT_SET_PARAM_NR_RECORDS = 2, + DPT_SET_PARAM_VERBOSITY = 3, + DPT_SET_PARAM_NUM_RECORDS_TO_DUMP = 4, + DPT_SET_PARAM_MAX, +}; + +static void ol_vdev_rx_set_intrabss_fwd(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, bool val); +uint32_t ol_txrx_get_tx_pending(struct cdp_pdev *pdev_handle); +extern void +ol_txrx_set_wmm_param(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + struct ol_tx_wmm_param_t wmm_param); + +/* thresh for peer's cached buf queue beyond which the elements are dropped */ +#define OL_TXRX_CACHED_BUFQ_THRESH 128 + +#ifdef DP_SUPPORT_RECOVERY_NOTIFY +static +int ol_peer_recovery_notifier_cb(struct notifier_block *block, + unsigned long state, void *data) +{ + struct qdf_notifer_data *notif_data = data; + qdf_notif_block *notif_block; + struct ol_txrx_peer_t *peer; + struct peer_hang_data hang_data = {0}; + enum peer_debug_id_type dbg_id; + + if (!data || !block) + return -EINVAL; + + notif_block = qdf_container_of(block, qdf_notif_block, notif_block); + + peer = notif_block->priv_data; + if (!peer) + return -EINVAL; + + if (notif_data->offset + sizeof(struct peer_hang_data) > + QDF_WLAN_HANG_FW_OFFSET) + return NOTIFY_STOP_MASK; + + QDF_HANG_EVT_SET_HDR(&hang_data.tlv_header, + HANG_EVT_TAG_DP_PEER_INFO, + QDF_HANG_GET_STRUCT_TLVLEN(struct peer_hang_data)); + + qdf_mem_copy(&hang_data.peer_mac_addr, &peer->mac_addr.raw, + QDF_MAC_ADDR_SIZE); + + for (dbg_id = 0; dbg_id < PEER_DEBUG_ID_MAX; dbg_id++) + if (qdf_atomic_read(&peer->access_list[dbg_id])) + hang_data.peer_timeout_bitmask |= (1 << dbg_id); + + qdf_mem_copy(notif_data->hang_data + notif_data->offset, + &hang_data, sizeof(struct peer_hang_data)); + notif_data->offset += sizeof(struct peer_hang_data); + + return 0; +} + +static qdf_notif_block ol_peer_recovery_notifier = { + .notif_block.notifier_call = ol_peer_recovery_notifier_cb, +}; + +static +QDF_STATUS ol_register_peer_recovery_notifier(struct ol_txrx_peer_t *peer) +{ + ol_peer_recovery_notifier.priv_data = peer; + + return qdf_hang_event_register_notifier(&ol_peer_recovery_notifier); +} + +static +QDF_STATUS ol_unregister_peer_recovery_notifier(void) +{ + return qdf_hang_event_unregister_notifier(&ol_peer_recovery_notifier); +} +#else +static inline +QDF_STATUS ol_register_peer_recovery_notifier(struct ol_txrx_peer_t *peer) +{ + return QDF_STATUS_SUCCESS; +} + +static +QDF_STATUS ol_unregister_peer_recovery_notifier(void) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * ol_tx_mark_first_wakeup_packet() - set flag to indicate that + * fw is compatible for marking first packet after wow wakeup + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @value: 1 for enabled/ 0 for disabled + * + * Return: None + */ +static void ol_tx_mark_first_wakeup_packet(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, uint8_t value) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + htt_mark_first_wakeup_packet(pdev->htt_pdev, value); +} + +/** + * ol_tx_set_is_mgmt_over_wmi_enabled() - set flag to indicate that mgmt over + * wmi is enabled or not. + * @value: 1 for enabled/ 0 for disable + * + * Return: None + */ +void ol_tx_set_is_mgmt_over_wmi_enabled(uint8_t value) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + pdev->is_mgmt_over_wmi_enabled = value; +} + +/** + * ol_tx_get_is_mgmt_over_wmi_enabled() - get value of is_mgmt_over_wmi_enabled + * + * Return: is_mgmt_over_wmi_enabled + */ +uint8_t ol_tx_get_is_mgmt_over_wmi_enabled(void) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return 0; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return 0; + } + + return pdev->is_mgmt_over_wmi_enabled; +} + + +#ifdef QCA_SUPPORT_TXRX_LOCAL_PEER_ID +static void * +ol_txrx_find_peer_by_addr_and_vdev(struct cdp_pdev *ppdev, + struct cdp_vdev *pvdev, uint8_t *peer_addr) +{ + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev; + struct ol_txrx_vdev_t *vdev = (struct ol_txrx_vdev_t *)pvdev; + struct ol_txrx_peer_t *peer; + + peer = ol_txrx_peer_vdev_find_hash(pdev, vdev, peer_addr, 0, 1); + if (!peer) + return NULL; + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); + return peer; +} + +/** + * ol_txrx_get_vdevid() - Get virtual interface id which peer registered + * @soc_hdl - data path soc handle + * @peer_mac - peer mac address + * @vdev_id - virtual interface id which peer registered + * + * Get virtual interface id which peer registered + * + * Return: QDF_STATUS_SUCCESS registration success + * QDF_STATUS_E_NOSUPPORT not support this feature + */ +static QDF_STATUS ol_txrx_get_vdevid(struct cdp_soc_t *soc_hdl, + uint8_t *peer_mac, uint8_t *vdev_id) +{ + uint8_t pdev_id = OL_TXRX_PDEV_ID; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_txrx_peer_t *peer = + ol_txrx_peer_find_hash_find_get_ref(pdev, peer_mac, 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + + if (!peer) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "peer argument is null!!"); + return QDF_STATUS_E_FAILURE; + } + + *vdev_id = peer->vdev->vdev_id; + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); + + return QDF_STATUS_SUCCESS; +} + +ol_txrx_vdev_handle +ol_txrx_get_vdev_by_peer_addr(struct cdp_pdev *ppdev, + struct qdf_mac_addr peer_addr) +{ + struct ol_txrx_pdev_t *pdev = cdp_pdev_to_ol_txrx_pdev_t(ppdev); + struct ol_txrx_peer_t *peer = NULL; + ol_txrx_vdev_handle vdev; + + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH, + "PDEV not found for peer_addr: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_addr.bytes)); + return NULL; + } + + peer = ol_txrx_peer_get_ref_by_addr(pdev, peer_addr.bytes, + PEER_DEBUG_ID_OL_INTERNAL); + + if (!peer) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH, + "PDEV not found for peer_addr:" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_addr.bytes)); + return NULL; + } + + vdev = peer->vdev; + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); + + return vdev; +} + +/** + * ol_txrx_wrapper_get_vdev_by_peer_addr() - Get vdev handle by peer mac address + * @ppdev - data path device instance + * @peer_addr - peer mac address + * + * Get virtual interface handle by local peer mac address + * + * Return: Virtual interface instance handle + * NULL in case cannot find + */ +static struct cdp_vdev * +ol_txrx_wrapper_get_vdev_by_peer_addr(struct cdp_pdev *ppdev, + struct qdf_mac_addr peer_addr) +{ + return (struct cdp_vdev *)ol_txrx_get_vdev_by_peer_addr(ppdev, + peer_addr); +} + +/* + * ol_txrx_find_peer_exist - find peer if already exists + * @soc_hdl: datapath soc handle + * @pdev_id: physical device instance id + * @peer_mac_addr: peer mac address + * + * Return: true or false + */ +static bool ol_txrx_find_peer_exist(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint8_t *peer_addr) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) + return false; + + return !!ol_txrx_find_peer_by_addr(ol_txrx_pdev_t_to_cdp_pdev(pdev), + peer_addr); +} + +/* + * ol_txrx_find_peer_exist_on_vdev - find if duplicate peer exists + * on the given vdev + * @soc_hdl: datapath soc handle + * @vdev_id: vdev instance id + * @peer_mac_addr: peer mac address + * + * Return: true or false + */ +static bool ol_txrx_find_peer_exist_on_vdev(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + uint8_t *peer_addr) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev) + return false; + + return !!ol_txrx_find_peer_by_addr_and_vdev( + ol_txrx_pdev_t_to_cdp_pdev(vdev->pdev), + ol_txrx_vdev_t_to_cdp_vdev(vdev), + peer_addr); +} + +/* + * ol_txrx_find_peer_exist_on_other_vdev - find if duplicate peer exists + * on other than the given vdev + * @soc_hdl: datapath soc handle + * @vdev_id: vdev instance id + * @peer_mac_addr: peer mac address + * @max_bssid: max number of bssids + * + * Return: true or false + */ +static bool ol_txrx_find_peer_exist_on_other_vdev(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + uint8_t *peer_addr, + uint16_t max_bssid) +{ + int i; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_vdev_t *vdev; + + for (i = 0; i < max_bssid; i++) { + vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, i); + /* Need to check vdevs other than the vdev_id */ + if (vdev_id == i || !vdev) + continue; + if (ol_txrx_find_peer_by_addr_and_vdev( + ol_txrx_pdev_t_to_cdp_pdev(vdev->pdev), + ol_txrx_vdev_t_to_cdp_vdev(vdev), + peer_addr)) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO, + "%s: Duplicate peer "QDF_MAC_ADDR_FMT" already exist on vdev %d", + __func__, QDF_MAC_ADDR_REF(peer_addr), i); + return true; + } + } + + return false; +} + +/** + * ol_txrx_find_peer_by_addr() - find peer via peer mac addr and peer_id + * @ppdev: pointer of type cdp_pdev + * @peer_addr: peer mac addr + * + * This function finds a peer with given mac address and returns its peer_id. + * Note that this function does not increment the peer->ref_cnt. + * This means that the peer may be deleted in some other parallel context after + * its been found. + * + * Return: peer handle if peer is found, NULL if peer is not found. + */ +void *ol_txrx_find_peer_by_addr(struct cdp_pdev *ppdev, + uint8_t *peer_addr) +{ + struct ol_txrx_peer_t *peer; + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev; + + peer = ol_txrx_peer_find_hash_find_get_ref(pdev, peer_addr, 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + if (!peer) + return NULL; + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); + return peer; +} + +/** + * ol_txrx_peer_get_ref_by_addr() - get peer ref via peer mac addr and peer_id + * @pdev: pointer of type ol_txrx_pdev_handle + * @peer_addr: peer mac addr + * + * This function finds the peer with given mac address and returns its peer_id. + * Note that this function increments the peer->ref_cnt. + * This makes sure that peer will be valid. This also means the caller needs to + * call the corresponding API - ol_txrx_peer_release_ref to delete the peer + * reference. + * Sample usage: + * { + * //the API call below increments the peer->ref_cnt + * peer = ol_txrx_peer_get_ref_by_addr(pdev, peer_addr, peer_id, dbg_id); + * + * // Once peer usage is done + * + * //the API call below decrements the peer->ref_cnt + * ol_txrx_peer_release_ref(peer, dbg_id); + * } + * + * Return: peer handle if the peer is found, NULL if peer is not found. + */ +ol_txrx_peer_handle ol_txrx_peer_get_ref_by_addr(ol_txrx_pdev_handle pdev, + u8 *peer_addr, + enum peer_debug_id_type dbg_id) +{ + struct ol_txrx_peer_t *peer; + + peer = ol_txrx_peer_find_hash_find_get_ref(pdev, peer_addr, 0, 1, + dbg_id); + if (!peer) + return NULL; + + return peer; +} + +/** + * @brief Find a txrx peer handle from a peer's local ID + * @param pdev - the data physical device object + * @param local_peer_id - the ID txrx assigned locally to the peer in question + * @dbg_id - debug_id to track caller + * @return handle to the txrx peer object + * @details + * The control SW typically uses the txrx peer handle to refer to the peer. + * In unusual circumstances, if it is infeasible for the control SW maintain + * the txrx peer handle but it can maintain a small integer local peer ID, + * this function allows the peer handled to be retrieved, based on the local + * peer ID. + * + * Note that this function increments the peer->ref_cnt. + * This makes sure that peer will be valid. This also means the caller needs to + * call the corresponding API - + * ol_txrx_peer_release_ref + * + * reference. + * Sample usage: + * { + * //the API call below increments the peer->ref_cnt + * peer = ol_txrx_peer_get_ref_by_local_id(pdev,local_peer_id, dbg_id); + * + * // Once peer usage is done + * + * //the API call below decrements the peer->ref_cnt + * ol_txrx_peer_release_ref(peer, dbg_id); + * } + * + * Return: peer handle if the peer is found, NULL if peer is not found. + */ +ol_txrx_peer_handle +ol_txrx_peer_get_ref_by_local_id(struct cdp_pdev *ppdev, + uint8_t local_peer_id, + enum peer_debug_id_type dbg_id) +{ + struct ol_txrx_peer_t *peer = NULL; + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev; + + if ((local_peer_id == OL_TXRX_INVALID_LOCAL_PEER_ID) || + (local_peer_id >= OL_TXRX_NUM_LOCAL_PEER_IDS)) { + return NULL; + } + + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + qdf_spin_lock_bh(&pdev->local_peer_ids.lock); + peer = pdev->local_peer_ids.map[local_peer_id]; + qdf_spin_unlock_bh(&pdev->local_peer_ids.lock); + if (peer && peer->valid) + ol_txrx_peer_get_ref(peer, dbg_id); + else + peer = NULL; + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + + return peer; +} + +static void ol_txrx_local_peer_id_pool_init(struct ol_txrx_pdev_t *pdev) +{ + int i; + + /* point the freelist to the first ID */ + pdev->local_peer_ids.freelist = 0; + + /* link each ID to the next one */ + for (i = 0; i < OL_TXRX_NUM_LOCAL_PEER_IDS; i++) { + pdev->local_peer_ids.pool[i] = i + 1; + pdev->local_peer_ids.map[i] = NULL; + } + + /* link the last ID to itself, to mark the end of the list */ + i = OL_TXRX_NUM_LOCAL_PEER_IDS; + pdev->local_peer_ids.pool[i] = i; + + qdf_spinlock_create(&pdev->local_peer_ids.lock); +} + +static void +ol_txrx_local_peer_id_alloc(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + int i; + + qdf_spin_lock_bh(&pdev->local_peer_ids.lock); + i = pdev->local_peer_ids.freelist; + if (pdev->local_peer_ids.pool[i] == i) { + /* the list is empty, except for the list-end marker */ + peer->local_id = OL_TXRX_INVALID_LOCAL_PEER_ID; + } else { + /* take the head ID and advance the freelist */ + peer->local_id = i; + pdev->local_peer_ids.freelist = pdev->local_peer_ids.pool[i]; + pdev->local_peer_ids.map[i] = peer; + } + qdf_spin_unlock_bh(&pdev->local_peer_ids.lock); +} + +static void +ol_txrx_local_peer_id_free(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + int i = peer->local_id; + + if ((i == OL_TXRX_INVALID_LOCAL_PEER_ID) || + (i >= OL_TXRX_NUM_LOCAL_PEER_IDS)) { + return; + } + /* put this ID on the head of the freelist */ + qdf_spin_lock_bh(&pdev->local_peer_ids.lock); + pdev->local_peer_ids.pool[i] = pdev->local_peer_ids.freelist; + pdev->local_peer_ids.freelist = i; + pdev->local_peer_ids.map[i] = NULL; + qdf_spin_unlock_bh(&pdev->local_peer_ids.lock); +} + +static void ol_txrx_local_peer_id_cleanup(struct ol_txrx_pdev_t *pdev) +{ + qdf_spinlock_destroy(&pdev->local_peer_ids.lock); +} + +#else +#define ol_txrx_local_peer_id_pool_init(pdev) /* no-op */ +#define ol_txrx_local_peer_id_alloc(pdev, peer) /* no-op */ +#define ol_txrx_local_peer_id_free(pdev, peer) /* no-op */ +#define ol_txrx_local_peer_id_cleanup(pdev) /* no-op */ +#endif + +#if defined(CONFIG_DP_TRACE) && defined(WLAN_DEBUGFS) +/** + * ol_txrx_read_dpt_buff_debugfs() - read dp trace buffer + * @file: file to read + * @arg: pdev object + * + * Return: QDF_STATUS + */ +static QDF_STATUS ol_txrx_read_dpt_buff_debugfs(qdf_debugfs_file_t file, + void *arg) +{ + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)arg; + uint32_t i = 0; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (pdev->state == QDF_DPT_DEBUGFS_STATE_SHOW_STATE_INVALID) + return QDF_STATUS_E_INVAL; + else if (pdev->state == QDF_DPT_DEBUGFS_STATE_SHOW_COMPLETE) { + pdev->state = QDF_DPT_DEBUGFS_STATE_SHOW_STATE_INIT; + return QDF_STATUS_SUCCESS; + } + + i = qdf_dpt_get_curr_pos_debugfs(file, pdev->state); + status = qdf_dpt_dump_stats_debugfs(file, i); + if (status == QDF_STATUS_E_FAILURE) + pdev->state = QDF_DPT_DEBUGFS_STATE_SHOW_IN_PROGRESS; + else if (status == QDF_STATUS_SUCCESS) + pdev->state = QDF_DPT_DEBUGFS_STATE_SHOW_COMPLETE; + + return status; +} + +/** + * ol_txrx_conv_str_to_int_debugfs() - convert string to int + * @buf: buffer containing string + * @len: buffer len + * @proto_bitmap: defines the protocol to be tracked + * @nr_records: defines the nth packet which is traced + * @verbosity: defines the verbosity level + * + * This function expects char buffer to be null terminated. + * Otherwise results could be unexpected values. + * + * Return: 0 on success + */ +static int ol_txrx_conv_str_to_int_debugfs(char *buf, qdf_size_t len, + int *proto_bitmap, + int *nr_records, + int *verbosity, + int *num_records_to_dump) +{ + int num_value = DPT_SET_PARAM_PROTO_BITMAP; + int ret, param_value = 0; + char *buf_param = buf; + int i; + + for (i = 1; i < DPT_SET_PARAM_MAX; i++) { + /* Loop till you reach space as kstrtoint operates till + * null character. Replace space with null character + * to read each value. + * terminate the loop either at null terminated char or + * len is 0. + */ + while (*buf && len) { + if (*buf == ' ') { + *buf = '\0'; + buf++; + len--; + break; + } + buf++; + len--; + } + /* get the parameter */ + ret = qdf_kstrtoint(buf_param, + DPT_DEBUGFS_NUMBER_BASE, + ¶m_value); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "%s: Error while parsing buffer. ret %d", + __func__, ret); + return ret; + } + switch (num_value) { + case DPT_SET_PARAM_PROTO_BITMAP: + *proto_bitmap = param_value; + break; + case DPT_SET_PARAM_NR_RECORDS: + *nr_records = param_value; + break; + case DPT_SET_PARAM_VERBOSITY: + *verbosity = param_value; + break; + case DPT_SET_PARAM_NUM_RECORDS_TO_DUMP: + if (param_value > MAX_QDF_DP_TRACE_RECORDS) + param_value = MAX_QDF_DP_TRACE_RECORDS; + *num_records_to_dump = param_value; + break; + default: + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s %d: :Set command needs exactly 4 arguments in format .", + __func__, __LINE__); + break; + } + num_value++; + /*buf_param should now point to the next param value. */ + buf_param = buf; + } + + /* buf is not yet NULL implies more than 4 params are passed. */ + if (*buf) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s %d: :Set command needs exactly 4 arguments in format .", + __func__, __LINE__); + return -EINVAL; + } + return 0; +} + +/** + * ol_txrx_write_dpt_buff_debugfs() - set dp trace parameters + * @priv: pdev object + * @buf: buff to get value for dpt parameters + * @len: buf length + * + * Return: QDF_STATUS + */ +static QDF_STATUS ol_txrx_write_dpt_buff_debugfs(void *priv, + const char *buf, + qdf_size_t len) +{ + int ret; + int proto_bitmap = 0; + int nr_records = 0; + int verbosity = 0; + int num_records_to_dump = 0; + char *buf1 = NULL; + + if (!buf || !len) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: null buffer or len. len %u", + __func__, (uint8_t)len); + return QDF_STATUS_E_FAULT; + } + + buf1 = (char *)qdf_mem_malloc(len); + if (!buf1) + return QDF_STATUS_E_FAULT; + + qdf_mem_copy(buf1, buf, len); + ret = ol_txrx_conv_str_to_int_debugfs(buf1, len, &proto_bitmap, + &nr_records, &verbosity, + &num_records_to_dump); + if (ret) { + qdf_mem_free(buf1); + return QDF_STATUS_E_INVAL; + } + + qdf_dpt_set_value_debugfs(proto_bitmap, nr_records, verbosity, + num_records_to_dump); + qdf_mem_free(buf1); + return QDF_STATUS_SUCCESS; +} + +static int ol_txrx_debugfs_init(struct ol_txrx_pdev_t *pdev) +{ + pdev->dpt_debugfs_fops.show = ol_txrx_read_dpt_buff_debugfs; + pdev->dpt_debugfs_fops.write = ol_txrx_write_dpt_buff_debugfs; + pdev->dpt_debugfs_fops.priv = pdev; + + pdev->dpt_stats_log_dir = qdf_debugfs_create_dir("dpt_stats", NULL); + + if (!pdev->dpt_stats_log_dir) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: error while creating debugfs dir for %s", + __func__, "dpt_stats"); + pdev->state = QDF_DPT_DEBUGFS_STATE_SHOW_STATE_INVALID; + return -EBUSY; + } + + if (!qdf_debugfs_create_file("dump_set_dpt_logs", DPT_DEBUGFS_PERMS, + pdev->dpt_stats_log_dir, + &pdev->dpt_debugfs_fops)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: debug Entry creation failed!", + __func__); + pdev->state = QDF_DPT_DEBUGFS_STATE_SHOW_STATE_INVALID; + return -EBUSY; + } + + pdev->state = QDF_DPT_DEBUGFS_STATE_SHOW_STATE_INIT; + return 0; +} + +static void ol_txrx_debugfs_exit(ol_txrx_pdev_handle pdev) +{ + qdf_debugfs_remove_dir_recursive(pdev->dpt_stats_log_dir); +} +#else +static inline int ol_txrx_debugfs_init(struct ol_txrx_pdev_t *pdev) +{ + return 0; +} + +static inline void ol_txrx_debugfs_exit(ol_txrx_pdev_handle pdev) +{ +} +#endif + +/** + * ol_txrx_pdev_attach() - allocate txrx pdev + * @soc_hdl: datapath soc handle + * @htc_pdev: HTC pdev + * @osdev: os dev + * @pdev_id: pdev identifier for pdev attach + * + * Return: QDF_STATUS_SUCCESS on success + * QDF error code for failure + */ +static QDF_STATUS +ol_txrx_pdev_attach(ol_txrx_soc_handle soc, + HTC_HANDLE htc_pdev, qdf_device_t osdev, uint8_t pdev_id) +{ + struct ol_txrx_soc_t *ol_soc = cdp_soc_t_to_ol_txrx_soc_t(soc); + struct ol_txrx_pdev_t *pdev; + struct cdp_cfg *cfg_pdev = cds_get_context(QDF_MODULE_ID_CFG); + QDF_STATUS status; + int i, tid; + + if (pdev_id == OL_TXRX_INVALID_PDEV_ID) + return QDF_STATUS_E_INVAL; + + pdev = qdf_mem_malloc(sizeof(*pdev)); + if (!pdev) { + status = QDF_STATUS_E_NOMEM; + goto fail0; + } + + /* init LL/HL cfg here */ + pdev->cfg.is_high_latency = ol_cfg_is_high_latency(cfg_pdev); + /* + * Credit reporting through HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND + * enabled or not. + */ + pdev->cfg.credit_update_enabled = + ol_cfg_is_credit_update_enabled(cfg_pdev); + + /* Explicitly request TX Completions from FW */ + pdev->cfg.request_tx_comp = cds_is_ptp_rx_opt_enabled() || + cds_is_packet_log_enabled(); + + pdev->cfg.default_tx_comp_req = !ol_cfg_tx_free_at_download(cfg_pdev); + + /* store provided params */ + pdev->ctrl_pdev = cfg_pdev; + pdev->osdev = osdev; + pdev->id = pdev_id; + pdev->soc = ol_soc; + ol_soc->pdev_list[pdev_id] = pdev; + + for (i = 0; i < htt_num_sec_types; i++) + pdev->sec_types[i] = (enum ol_sec_type)i; + + TXRX_STATS_INIT(pdev); + ol_txrx_tso_stats_init(pdev); + ol_txrx_fw_stats_desc_pool_init(pdev, FW_STATS_DESC_POOL_SIZE); + + TAILQ_INIT(&pdev->vdev_list); + + TAILQ_INIT(&pdev->req_list); + pdev->req_list_depth = 0; + qdf_spinlock_create(&pdev->req_list_spinlock); + qdf_spinlock_create(&pdev->tx_mutex); + + /* do initial set up of the peer ID -> peer object lookup map */ + if (ol_txrx_peer_find_attach(pdev)) { + status = QDF_STATUS_E_FAILURE; + goto fail1; + } + + /* initialize the counter of the target's tx buffer availability */ + qdf_atomic_init(&pdev->target_tx_credit); + qdf_atomic_init(&pdev->orig_target_tx_credit); + qdf_atomic_init(&pdev->pad_reserve_tx_credit); + qdf_atomic_add(1, &pdev->pad_reserve_tx_credit); + + if (ol_cfg_is_high_latency(cfg_pdev)) { + qdf_spinlock_create(&pdev->tx_queue_spinlock); + pdev->tx_sched.scheduler = ol_tx_sched_attach(pdev); + if (!pdev->tx_sched.scheduler) { + status = QDF_STATUS_E_FAILURE; + goto fail2; + } + } + ol_txrx_pdev_txq_log_init(pdev); + ol_txrx_pdev_grp_stats_init(pdev); + + pdev->htt_pdev = + htt_pdev_alloc(pdev, cfg_pdev, htc_pdev, osdev); + if (!pdev->htt_pdev) { + status = QDF_STATUS_E_FAILURE; + goto fail3; + } + + htt_register_rx_pkt_dump_callback(pdev->htt_pdev, + ol_rx_pkt_dump_call); + + /* + * Init the tid --> category table. + * Regular tids (0-15) map to their AC. + * Extension tids get their own categories. + */ + for (tid = 0; tid < OL_TX_NUM_QOS_TIDS; tid++) { + int ac = TXRX_TID_TO_WMM_AC(tid); + + pdev->tid_to_ac[tid] = ac; + } + pdev->tid_to_ac[OL_TX_NON_QOS_TID] = + OL_TX_SCHED_WRR_ADV_CAT_NON_QOS_DATA; + pdev->tid_to_ac[OL_TX_MGMT_TID] = + OL_TX_SCHED_WRR_ADV_CAT_UCAST_MGMT; + pdev->tid_to_ac[OL_TX_NUM_TIDS + OL_TX_VDEV_MCAST_BCAST] = + OL_TX_SCHED_WRR_ADV_CAT_MCAST_DATA; + pdev->tid_to_ac[OL_TX_NUM_TIDS + OL_TX_VDEV_DEFAULT_MGMT] = + OL_TX_SCHED_WRR_ADV_CAT_MCAST_MGMT; + + if (ol_cfg_is_flow_steering_enabled(pdev->ctrl_pdev)) + pdev->peer_id_unmap_ref_cnt = + TXRX_RFS_ENABLE_PEER_ID_UNMAP_COUNT; + else + pdev->peer_id_unmap_ref_cnt = + TXRX_RFS_DISABLE_PEER_ID_UNMAP_COUNT; + + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + pdev->chan_noise_floor = NORMALIZED_TO_NOISE_FLOOR; + + ol_txrx_debugfs_init(pdev); + + return QDF_STATUS_SUCCESS; + +fail3: + ol_txrx_peer_find_detach(pdev); + +fail2: + if (ol_cfg_is_high_latency(cfg_pdev)) + qdf_spinlock_destroy(&pdev->tx_queue_spinlock); + +fail1: + qdf_spinlock_destroy(&pdev->req_list_spinlock); + qdf_spinlock_destroy(&pdev->tx_mutex); + ol_txrx_tso_stats_deinit(pdev); + ol_txrx_fw_stats_desc_pool_deinit(pdev); + qdf_mem_free(pdev); + +fail0: + return status; +} + +#if !defined(REMOVE_PKT_LOG) && !defined(QVIT) +/** + * htt_pkt_log_init() - API to initialize packet log + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @scn: HIF context + * + * Return: void + */ +void htt_pkt_log_init(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, void *scn) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle handle = + ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (handle->pkt_log_init) + return; + + if (cds_get_conparam() != QDF_GLOBAL_FTM_MODE && + !QDF_IS_EPPING_ENABLED(cds_get_conparam())) { + pktlog_sethandle(&handle->pl_dev, scn); + pktlog_set_pdev_id(handle->pl_dev, pdev_id); + pktlog_set_callback_regtype(PKTLOG_DEFAULT_CALLBACK_REGISTRATION); + if (pktlogmod_init(scn)) + qdf_print(" pktlogmod_init failed"); + else + handle->pkt_log_init = true; + } +} + +/** + * htt_pktlogmod_exit() - API to cleanup pktlog info + * @handle: Pdev handle + * @scn: HIF Context + * + * Return: void + */ +static void htt_pktlogmod_exit(struct ol_txrx_pdev_t *handle) +{ + if (cds_get_conparam() != QDF_GLOBAL_FTM_MODE && + !QDF_IS_EPPING_ENABLED(cds_get_conparam()) && + handle->pkt_log_init) { + pktlogmod_exit(handle); + handle->pkt_log_init = false; + } +} + +#else +void htt_pkt_log_init(struct cdp_soc_t *soc_hdl, uint8_t pdev, void *scn) { } +static void htt_pktlogmod_exit(ol_txrx_pdev_handle handle) { } +#endif + +#ifdef QCA_LL_PDEV_TX_FLOW_CONTROL +/** + * ol_txrx_pdev_set_threshold() - set pdev pool stop/start threshold + * @pdev: txrx pdev + * + * Return: void + */ +static void ol_txrx_pdev_set_threshold(struct ol_txrx_pdev_t *pdev) +{ + uint32_t stop_threshold; + uint32_t start_threshold; + uint16_t desc_pool_size = pdev->tx_desc.pool_size; + + stop_threshold = ol_cfg_get_tx_flow_stop_queue_th(pdev->ctrl_pdev); + start_threshold = stop_threshold + + ol_cfg_get_tx_flow_start_queue_offset(pdev->ctrl_pdev); + pdev->tx_desc.start_th = (start_threshold * desc_pool_size) / 100; + pdev->tx_desc.stop_th = (stop_threshold * desc_pool_size) / 100; + pdev->tx_desc.stop_priority_th = + (TX_PRIORITY_TH * pdev->tx_desc.stop_th) / 100; + if (pdev->tx_desc.stop_priority_th >= MAX_TSO_SEGMENT_DESC) + pdev->tx_desc.stop_priority_th -= MAX_TSO_SEGMENT_DESC; + + pdev->tx_desc.start_priority_th = + (TX_PRIORITY_TH * pdev->tx_desc.start_th) / 100; + if (pdev->tx_desc.start_priority_th >= MAX_TSO_SEGMENT_DESC) + pdev->tx_desc.start_priority_th -= MAX_TSO_SEGMENT_DESC; + pdev->tx_desc.status = FLOW_POOL_ACTIVE_UNPAUSED; +} +#else +static inline void ol_txrx_pdev_set_threshold(struct ol_txrx_pdev_t *pdev) +{ +} +#endif + +/** + * ol_txrx_pdev_post_attach() - attach txrx pdev + * @soc_hdl: datapath soc handle + * @pdev_id: physical device instance id + * + * Return: 0 for success + */ +int +ol_txrx_pdev_post_attach(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + uint16_t i; + uint16_t fail_idx = 0; + int ret = 0; + uint16_t desc_pool_size; + struct hif_opaque_softc *osc = cds_get_context(QDF_MODULE_ID_HIF); + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + uint16_t desc_element_size = sizeof(union ol_tx_desc_list_elem_t); + union ol_tx_desc_list_elem_t *c_element; + unsigned int sig_bit; + uint16_t desc_per_page; + + if (!osc || !pdev) { + ret = -EINVAL; + goto ol_attach_fail; + } + + /* + * For LL, limit the number of host's tx descriptors to match + * the number of target FW tx descriptors. + * This simplifies the FW, by ensuring the host will never + * download more tx descriptors than the target has space for. + * The FW will drop/free low-priority tx descriptors when it + * starts to run low, so that in theory the host should never + * run out of tx descriptors. + */ + + /* + * LL - initialize the target credit outselves. + * HL - wait for a HTT target credit initialization + * during htt_attach. + */ + desc_pool_size = ol_tx_get_desc_global_pool_size(pdev); + ol_tx_init_pdev(pdev); + + ol_tx_desc_dup_detect_init(pdev, desc_pool_size); + + ol_tx_setup_fastpath_ce_handles(osc, pdev); + + if ((ol_txrx_get_new_htt_msg_format(pdev))) + ol_set_cfg_new_htt_format(pdev->ctrl_pdev, true); + else + ol_set_cfg_new_htt_format(pdev->ctrl_pdev, false); + + ret = htt_attach(pdev->htt_pdev, desc_pool_size); + if (ret) + goto htt_attach_fail; + + /* Attach micro controller data path offload resource */ + if (ol_cfg_ipa_uc_offload_enabled(pdev->ctrl_pdev)) { + ret = htt_ipa_uc_attach(pdev->htt_pdev); + if (ret) + goto uc_attach_fail; + } + + /* Calculate single element reserved size power of 2 */ + pdev->tx_desc.desc_reserved_size = qdf_get_pwr2(desc_element_size); + qdf_mem_multi_pages_alloc(pdev->osdev, &pdev->tx_desc.desc_pages, + pdev->tx_desc.desc_reserved_size, desc_pool_size, 0, true); + if ((0 == pdev->tx_desc.desc_pages.num_pages) || + (!pdev->tx_desc.desc_pages.cacheable_pages)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Page alloc fail"); + ret = -ENOMEM; + goto page_alloc_fail; + } + desc_per_page = pdev->tx_desc.desc_pages.num_element_per_page; + pdev->tx_desc.offset_filter = desc_per_page - 1; + /* Calculate page divider to find page number */ + sig_bit = 0; + while (desc_per_page) { + sig_bit++; + desc_per_page = desc_per_page >> 1; + } + pdev->tx_desc.page_divider = (sig_bit - 1); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "page_divider 0x%x, offset_filter 0x%x num elem %d, ol desc num page %d, ol desc per page %d", + pdev->tx_desc.page_divider, pdev->tx_desc.offset_filter, + desc_pool_size, pdev->tx_desc.desc_pages.num_pages, + pdev->tx_desc.desc_pages.num_element_per_page); + + /* + * Each SW tx desc (used only within the tx datapath SW) has a + * matching HTT tx desc (used for downloading tx meta-data to FW/HW). + * Go ahead and allocate the HTT tx desc and link it with the SW tx + * desc now, to avoid doing it during time-critical transmit. + */ + pdev->tx_desc.pool_size = desc_pool_size; + pdev->tx_desc.freelist = + (union ol_tx_desc_list_elem_t *) + (*pdev->tx_desc.desc_pages.cacheable_pages); + c_element = pdev->tx_desc.freelist; + for (i = 0; i < desc_pool_size; i++) { + void *htt_tx_desc; + void *htt_frag_desc = NULL; + qdf_dma_addr_t frag_paddr = 0; + qdf_dma_addr_t paddr; + + if (i == (desc_pool_size - 1)) + c_element->next = NULL; + else + c_element->next = (union ol_tx_desc_list_elem_t *) + ol_tx_desc_find(pdev, i + 1); + + htt_tx_desc = htt_tx_desc_alloc(pdev->htt_pdev, &paddr, i); + if (!htt_tx_desc) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_FATAL, + "%s: failed to alloc HTT tx desc (%d of %d)", + __func__, i, desc_pool_size); + fail_idx = i; + ret = -ENOMEM; + goto desc_alloc_fail; + } + + c_element->tx_desc.htt_tx_desc = htt_tx_desc; + c_element->tx_desc.htt_tx_desc_paddr = paddr; + ret = htt_tx_frag_alloc(pdev->htt_pdev, + i, &frag_paddr, &htt_frag_desc); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: failed to alloc HTT frag dsc (%d/%d)", + __func__, i, desc_pool_size); + /* Is there a leak here, is this handling correct? */ + fail_idx = i; + goto desc_alloc_fail; + } + if (!ret && htt_frag_desc) { + /* + * Initialize the first 6 words (TSO flags) + * of the frag descriptor + */ + memset(htt_frag_desc, 0, 6 * sizeof(uint32_t)); + c_element->tx_desc.htt_frag_desc = htt_frag_desc; + c_element->tx_desc.htt_frag_desc_paddr = frag_paddr; + } +#ifdef QCA_SUPPORT_TXDESC_SANITY_CHECKS + c_element->tx_desc.pkt_type = 0xff; +#ifdef QCA_COMPUTE_TX_DELAY + c_element->tx_desc.entry_timestamp_ticks = + 0xffffffff; +#endif +#endif + c_element->tx_desc.id = i; + qdf_atomic_init(&c_element->tx_desc.ref_cnt); + c_element = c_element->next; + fail_idx = i; + } + + /* link SW tx descs into a freelist */ + pdev->tx_desc.num_free = desc_pool_size; + ol_txrx_dbg("first tx_desc:0x%pK Last tx desc:0x%pK", + (uint32_t *)pdev->tx_desc.freelist, + (uint32_t *)(pdev->tx_desc.freelist + desc_pool_size)); + + ol_txrx_pdev_set_threshold(pdev); + + /* check what format of frames are expected to be delivered by the OS */ + pdev->frame_format = ol_cfg_frame_type(pdev->ctrl_pdev); + if (pdev->frame_format == wlan_frm_fmt_native_wifi) + pdev->htt_pkt_type = htt_pkt_type_native_wifi; + else if (pdev->frame_format == wlan_frm_fmt_802_3) { + if (ol_cfg_is_ce_classify_enabled(pdev->ctrl_pdev)) + pdev->htt_pkt_type = htt_pkt_type_eth2; + else + pdev->htt_pkt_type = htt_pkt_type_ethernet; + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s Invalid standard frame type: %d", + __func__, pdev->frame_format); + ret = -EINVAL; + goto control_init_fail; + } + + /* setup the global rx defrag waitlist */ + TAILQ_INIT(&pdev->rx.defrag.waitlist); + + /* configure where defrag timeout and duplicate detection is handled */ + pdev->rx.flags.defrag_timeout_check = + pdev->rx.flags.dup_check = + ol_cfg_rx_host_defrag_timeout_duplicate_check(pdev->ctrl_pdev); + +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + /* Need to revisit this part. Currently,hardcode to riva's caps */ + pdev->target_tx_tran_caps = wlan_frm_tran_cap_raw; + pdev->target_rx_tran_caps = wlan_frm_tran_cap_raw; + /* + * The Riva HW de-aggregate doesn't have capability to generate 802.11 + * header for non-first subframe of A-MSDU. + */ + pdev->sw_subfrm_hdr_recovery_enable = 1; + /* + * The Riva HW doesn't have the capability to set Protected Frame bit + * in the MAC header for encrypted data frame. + */ + pdev->sw_pf_proc_enable = 1; + + if (pdev->frame_format == wlan_frm_fmt_802_3) { + /* + * sw llc process is only needed in + * 802.3 to 802.11 transform case + */ + pdev->sw_tx_llc_proc_enable = 1; + pdev->sw_rx_llc_proc_enable = 1; + } else { + pdev->sw_tx_llc_proc_enable = 0; + pdev->sw_rx_llc_proc_enable = 0; + } + + switch (pdev->frame_format) { + case wlan_frm_fmt_raw: + pdev->sw_tx_encap = + pdev->target_tx_tran_caps & wlan_frm_tran_cap_raw + ? 0 : 1; + pdev->sw_rx_decap = + pdev->target_rx_tran_caps & wlan_frm_tran_cap_raw + ? 0 : 1; + break; + case wlan_frm_fmt_native_wifi: + pdev->sw_tx_encap = + pdev-> + target_tx_tran_caps & wlan_frm_tran_cap_native_wifi + ? 0 : 1; + pdev->sw_rx_decap = + pdev-> + target_rx_tran_caps & wlan_frm_tran_cap_native_wifi + ? 0 : 1; + break; + case wlan_frm_fmt_802_3: + pdev->sw_tx_encap = + pdev->target_tx_tran_caps & wlan_frm_tran_cap_8023 + ? 0 : 1; + pdev->sw_rx_decap = + pdev->target_rx_tran_caps & wlan_frm_tran_cap_8023 + ? 0 : 1; + break; + default: + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Invalid std frame type; [en/de]cap: f:%x t:%x r:%x", + pdev->frame_format, + pdev->target_tx_tran_caps, pdev->target_rx_tran_caps); + ret = -EINVAL; + goto control_init_fail; + } +#endif + + /* + * Determine what rx processing steps are done within the host. + * Possibilities: + * 1. Nothing - rx->tx forwarding and rx PN entirely within target. + * (This is unlikely; even if the target is doing rx->tx forwarding, + * the host should be doing rx->tx forwarding too, as a back up for + * the target's rx->tx forwarding, in case the target runs short on + * memory, and can't store rx->tx frames that are waiting for + * missing prior rx frames to arrive.) + * 2. Just rx -> tx forwarding. + * This is the typical configuration for HL, and a likely + * configuration for LL STA or small APs (e.g. retail APs). + * 3. Both PN check and rx -> tx forwarding. + * This is the typical configuration for large LL APs. + * Host-side PN check without rx->tx forwarding is not a valid + * configuration, since the PN check needs to be done prior to + * the rx->tx forwarding. + */ + if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev)) { + /* + * PN check, rx-tx forwarding and rx reorder is done by + * the target + */ + if (ol_cfg_rx_fwd_disabled(pdev->ctrl_pdev)) + pdev->rx_opt_proc = ol_rx_in_order_deliver; + else + pdev->rx_opt_proc = ol_rx_fwd_check; + } else { + if (ol_cfg_rx_pn_check(pdev->ctrl_pdev)) { + if (ol_cfg_rx_fwd_disabled(pdev->ctrl_pdev)) { + /* + * PN check done on host, + * rx->tx forwarding not done at all. + */ + pdev->rx_opt_proc = ol_rx_pn_check_only; + } else if (ol_cfg_rx_fwd_check(pdev->ctrl_pdev)) { + /* + * Both PN check and rx->tx forwarding done + * on host. + */ + pdev->rx_opt_proc = ol_rx_pn_check; + } else { +#define TRACESTR01 "invalid config: if rx PN check is on the host,"\ +"rx->tx forwarding check needs to also be on the host" + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + "%s: %s", __func__, TRACESTR01); +#undef TRACESTR01 + ret = -EINVAL; + goto control_init_fail; + } + } else { + /* PN check done on target */ + if ((!ol_cfg_rx_fwd_disabled(pdev->ctrl_pdev)) && + ol_cfg_rx_fwd_check(pdev->ctrl_pdev)) { + /* + * rx->tx forwarding done on host (possibly as + * back-up for target-side primary rx->tx + * forwarding) + */ + pdev->rx_opt_proc = ol_rx_fwd_check; + } else { + /* + * rx->tx forwarding either done in target, + * or not done at all + */ + pdev->rx_opt_proc = ol_rx_deliver; + } + } + } + + /* initialize mutexes for tx desc alloc and peer lookup */ + qdf_spinlock_create(&pdev->peer_ref_mutex); + qdf_spinlock_create(&pdev->rx.mutex); + qdf_spinlock_create(&pdev->last_real_peer_mutex); + qdf_spinlock_create(&pdev->peer_map_unmap_lock); + OL_TXRX_PEER_STATS_MUTEX_INIT(pdev); + + if (OL_RX_REORDER_TRACE_ATTACH(pdev) != A_OK) { + ret = -ENOMEM; + goto reorder_trace_attach_fail; + } + + if (OL_RX_PN_TRACE_ATTACH(pdev) != A_OK) { + ret = -ENOMEM; + goto pn_trace_attach_fail; + } + + /* + * WDI event attach + */ + wdi_event_attach(pdev); + + /* + * Initialize rx PN check characteristics for different security types. + */ + qdf_mem_zero(&pdev->rx_pn[0], sizeof(pdev->rx_pn)); + + /* TKIP: 48-bit TSC, CCMP: 48-bit PN */ + pdev->rx_pn[htt_sec_type_tkip].len = + pdev->rx_pn[htt_sec_type_tkip_nomic].len = + pdev->rx_pn[htt_sec_type_aes_ccmp].len = 48; + + pdev->rx_pn[htt_sec_type_aes_ccmp_256].len = + pdev->rx_pn[htt_sec_type_aes_gcmp].len = + pdev->rx_pn[htt_sec_type_aes_gcmp_256].len = 48; + + pdev->rx_pn[htt_sec_type_tkip].cmp = + pdev->rx_pn[htt_sec_type_tkip_nomic].cmp = + pdev->rx_pn[htt_sec_type_aes_ccmp].cmp = ol_rx_pn_cmp48; + + pdev->rx_pn[htt_sec_type_aes_ccmp_256].cmp = + pdev->rx_pn[htt_sec_type_aes_gcmp].cmp = + pdev->rx_pn[htt_sec_type_aes_gcmp_256].cmp = ol_rx_pn_cmp48; + + /* WAPI: 128-bit PN */ + pdev->rx_pn[htt_sec_type_wapi].len = 128; + pdev->rx_pn[htt_sec_type_wapi].cmp = ol_rx_pn_wapi_cmp; + + OL_RX_REORDER_TIMEOUT_INIT(pdev); + + ol_txrx_dbg("Created pdev %pK\n", pdev); + + pdev->cfg.host_addba = ol_cfg_host_addba(pdev->ctrl_pdev); + +#ifdef QCA_SUPPORT_PEER_DATA_RX_RSSI +#define OL_TXRX_RSSI_UPDATE_SHIFT_DEFAULT 3 + +/* #if 1 -- TODO: clean this up */ +#define OL_TXRX_RSSI_NEW_WEIGHT_DEFAULT \ + /* avg = 100% * new + 0% * old */ \ + (1 << OL_TXRX_RSSI_UPDATE_SHIFT_DEFAULT) +/* + * #else + * #define OL_TXRX_RSSI_NEW_WEIGHT_DEFAULT + * //avg = 25% * new + 25% * old + * (1 << (OL_TXRX_RSSI_UPDATE_SHIFT_DEFAULT-2)) + * #endif + */ + pdev->rssi_update_shift = OL_TXRX_RSSI_UPDATE_SHIFT_DEFAULT; + pdev->rssi_new_weight = OL_TXRX_RSSI_NEW_WEIGHT_DEFAULT; +#endif + + ol_txrx_local_peer_id_pool_init(pdev); + + pdev->cfg.ll_pause_txq_limit = + ol_tx_cfg_max_tx_queue_depth_ll(pdev->ctrl_pdev); + + /* TX flow control for peer who is in very bad link status */ + ol_tx_badpeer_flow_cl_init(pdev); + +#ifdef QCA_COMPUTE_TX_DELAY + qdf_mem_zero(&pdev->tx_delay, sizeof(pdev->tx_delay)); + qdf_spinlock_create(&pdev->tx_delay.mutex); + + /* initialize compute interval with 5 seconds (ESE default) */ + pdev->tx_delay.avg_period_ticks = qdf_system_msecs_to_ticks(5000); + { + uint32_t bin_width_1000ticks; + + bin_width_1000ticks = + qdf_system_msecs_to_ticks + (QCA_TX_DELAY_HIST_INTERNAL_BIN_WIDTH_MS + * 1000); + /* + * Compute a factor and shift that together are equal to the + * inverse of the bin_width time, so that rather than dividing + * by the bin width time, approximately the same result can be + * obtained much more efficiently by a multiply + shift. + * multiply_factor >> shift = 1 / bin_width_time, so + * multiply_factor = (1 << shift) / bin_width_time. + * + * Pick the shift semi-arbitrarily. + * If we knew statically what the bin_width would be, we could + * choose a shift that minimizes the error. + * Since the bin_width is determined dynamically, simply use a + * shift that is about half of the uint32_t size. This should + * result in a relatively large multiplier value, which + * minimizes error from rounding the multiplier to an integer. + * The rounding error only becomes significant if the tick units + * are on the order of 1 microsecond. In most systems, it is + * expected that the tick units will be relatively low-res, + * on the order of 1 millisecond. In such systems the rounding + * error is negligible. + * It would be more accurate to dynamically try out different + * shifts and choose the one that results in the smallest + * rounding error, but that extra level of fidelity is + * not needed. + */ + pdev->tx_delay.hist_internal_bin_width_shift = 16; + pdev->tx_delay.hist_internal_bin_width_mult = + ((1 << pdev->tx_delay.hist_internal_bin_width_shift) * + 1000 + (bin_width_1000ticks >> 1)) / + bin_width_1000ticks; + } +#endif /* QCA_COMPUTE_TX_DELAY */ + + /* Thermal Mitigation */ + ol_tx_throttle_init(pdev); + + ol_tso_seg_list_init(pdev, desc_pool_size); + + ol_tso_num_seg_list_init(pdev, desc_pool_size); + + ol_tx_register_flow_control(pdev); + + return 0; /* success */ + +pn_trace_attach_fail: + OL_RX_REORDER_TRACE_DETACH(pdev); + +reorder_trace_attach_fail: + qdf_spinlock_destroy(&pdev->peer_ref_mutex); + qdf_spinlock_destroy(&pdev->rx.mutex); + qdf_spinlock_destroy(&pdev->last_real_peer_mutex); + qdf_spinlock_destroy(&pdev->peer_map_unmap_lock); + OL_TXRX_PEER_STATS_MUTEX_DESTROY(pdev); + +control_init_fail: +desc_alloc_fail: + for (i = 0; i < fail_idx; i++) + htt_tx_desc_free(pdev->htt_pdev, + (ol_tx_desc_find(pdev, i))->htt_tx_desc); + + qdf_mem_multi_pages_free(pdev->osdev, + &pdev->tx_desc.desc_pages, 0, true); + +page_alloc_fail: + if (ol_cfg_ipa_uc_offload_enabled(pdev->ctrl_pdev)) + htt_ipa_uc_detach(pdev->htt_pdev); +uc_attach_fail: + htt_detach(pdev->htt_pdev); +htt_attach_fail: + ol_tx_desc_dup_detect_deinit(pdev); +ol_attach_fail: + return ret; /* fail */ +} + +/** + * ol_txrx_pdev_attach_target() - send target configuration + * + * @soc_hdl - data path soc handle + * @pdev_id - device instance id + * + * The majority of the data SW setup are done by the pdev_attach + * functions, but this function completes the data SW setup by + * sending datapath configuration messages to the target. + * + * Return: 0 - success 1 - failure + */ +static int ol_txrx_pdev_attach_target(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) + return QDF_STATUS_E_FAULT; + + return htt_attach_target(pdev->htt_pdev) == QDF_STATUS_SUCCESS ? 0:1; +} + +/** + * ol_tx_free_descs_inuse - free tx descriptors which are in use + * @pdev - the physical device for which tx descs need to be freed + * + * Cycle through the list of TX descriptors (for a pdev) which are in use, + * for which TX completion has not been received and free them. Should be + * called only when the interrupts are off and all lower layer RX is stopped. + * Otherwise there may be a race condition with TX completions. + * + * Return: None + */ +static void ol_tx_free_descs_inuse(ol_txrx_pdev_handle pdev) +{ + int i; + void *htt_tx_desc; + struct ol_tx_desc_t *tx_desc; + int num_freed_tx_desc = 0; + + for (i = 0; i < pdev->tx_desc.pool_size; i++) { + tx_desc = ol_tx_desc_find(pdev, i); + /* + * Confirm that each tx descriptor is "empty", i.e. it has + * no tx frame attached. + * In particular, check that there are no frames that have + * been given to the target to transmit, for which the + * target has never provided a response. + */ + if (qdf_atomic_read(&tx_desc->ref_cnt)) { + ol_txrx_dbg("Warning: freeing tx frame (no compltn)"); + ol_tx_desc_frame_free_nonstd(pdev, + tx_desc, 1); + num_freed_tx_desc++; + } + htt_tx_desc = tx_desc->htt_tx_desc; + htt_tx_desc_free(pdev->htt_pdev, htt_tx_desc); + } + + if (num_freed_tx_desc) + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "freed %d tx frames for which no resp from target", + num_freed_tx_desc); + +} + +/** + * ol_txrx_pdev_pre_detach() - detach the data SW state + * @soc_hdl - datapath soc handle + * @pdev_id - the data physical device id being removed + * @force - delete the pdev (and its vdevs and peers) even if + * there are outstanding references by the target to the vdevs + * and peers within the pdev + * + * This function is used when the WLAN driver is being removed to + * detach the host data component within the driver. + * + * Return: none + */ +static void ol_txrx_pdev_pre_detach(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + int force) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + + /* preconditions */ + TXRX_ASSERT2(pdev); + + /* check that the pdev has no vdevs allocated */ + TXRX_ASSERT1(TAILQ_EMPTY(&pdev->vdev_list)); + +#ifdef QCA_SUPPORT_TX_THROTTLE + /* Thermal Mitigation */ + qdf_timer_stop(&pdev->tx_throttle.phase_timer); + qdf_timer_free(&pdev->tx_throttle.phase_timer); +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + qdf_timer_stop(&pdev->tx_throttle.tx_timer); + qdf_timer_free(&pdev->tx_throttle.tx_timer); +#endif +#endif + + if (force) { + /* + * The assertion above confirms that all vdevs within this pdev + * were detached. However, they may not have actually been + * deleted. + * If the vdev had peers which never received a PEER_UNMAP msg + * from the target, then there are still zombie peer objects, + * and the vdev parents of the zombie peers are also zombies, + * hanging around until their final peer gets deleted. + * Go through the peer hash table and delete any peers left. + * As a side effect, this will complete the deletion of any + * vdevs that are waiting for their peers to finish deletion. + */ + ol_txrx_dbg("Force delete for pdev %pK\n", + pdev); + ol_txrx_peer_find_hash_erase(pdev); + } + + /* to get flow pool status before freeing descs */ + ol_tx_dump_flow_pool_info(cds_get_context(QDF_MODULE_ID_SOC)); + ol_tx_free_descs_inuse(pdev); + ol_tx_deregister_flow_control(pdev); + + /* + * ol_tso_seg_list_deinit should happen after + * ol_tx_deinit_tx_desc_inuse as it tries to access the tso seg freelist + * which is being de-initilized in ol_tso_seg_list_deinit + */ + ol_tso_seg_list_deinit(pdev); + ol_tso_num_seg_list_deinit(pdev); + + /* Stop the communication between HTT and target at first */ + htt_detach_target(pdev->htt_pdev); + + qdf_mem_multi_pages_free(pdev->osdev, + &pdev->tx_desc.desc_pages, 0, true); + pdev->tx_desc.freelist = NULL; + + /* Detach micro controller data path offload resource */ + if (ol_cfg_ipa_uc_offload_enabled(pdev->ctrl_pdev)) + htt_ipa_uc_detach(pdev->htt_pdev); + + htt_detach(pdev->htt_pdev); + ol_tx_desc_dup_detect_deinit(pdev); + + qdf_spinlock_destroy(&pdev->peer_ref_mutex); + qdf_spinlock_destroy(&pdev->last_real_peer_mutex); + qdf_spinlock_destroy(&pdev->rx.mutex); + qdf_spinlock_destroy(&pdev->peer_map_unmap_lock); +#ifdef QCA_SUPPORT_TX_THROTTLE + /* Thermal Mitigation */ + qdf_spinlock_destroy(&pdev->tx_throttle.mutex); +#endif + + /* TX flow control for peer who is in very bad link status */ + ol_tx_badpeer_flow_cl_deinit(pdev); + + OL_TXRX_PEER_STATS_MUTEX_DESTROY(pdev); + + OL_RX_REORDER_TRACE_DETACH(pdev); + OL_RX_PN_TRACE_DETACH(pdev); + + htt_pktlogmod_exit(pdev); + + /* + * WDI event detach + */ + wdi_event_detach(pdev); + + ol_txrx_local_peer_id_cleanup(pdev); + +#ifdef QCA_COMPUTE_TX_DELAY + qdf_spinlock_destroy(&pdev->tx_delay.mutex); +#endif + + return; +} + +/** + * ol_txrx_pdev_detach() - delete the data SW state + * @soc_hdl - data path soc handle + * @pdev_id - device instance id + * @force - delete the pdev (and its vdevs and peers) even if + * there are outstanding references by the target to the vdevs + * and peers within the pdev + * + * This function is used when the WLAN driver is being removed to + * remove the host data component within the driver. + * All virtual devices within the physical device need to be deleted + * (ol_txrx_vdev_detach) before the physical device itself is deleted. + * + * Return: Success or Failure + */ +static QDF_STATUS ol_txrx_pdev_detach(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + int force) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + struct ol_txrx_stats_req_internal *req, *temp_req; + int i = 0; + + if (!soc) { + ol_txrx_err("soc is NULL"); + return QDF_STATUS_E_FAILURE; + } + + /*checking to ensure txrx pdev structure is not NULL */ + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return QDF_STATUS_E_FAILURE; + } + + qdf_spin_lock_bh(&pdev->req_list_spinlock); + if (pdev->req_list_depth > 0) + ol_txrx_err( + "Warning: the txrx req list is not empty, depth=%d\n", + pdev->req_list_depth + ); + TAILQ_FOREACH_SAFE(req, &pdev->req_list, req_list_elem, temp_req) { + TAILQ_REMOVE(&pdev->req_list, req, req_list_elem); + pdev->req_list_depth--; + ol_txrx_err( + "%d: %pK,verbose(%d), concise(%d), up_m(0x%x), reset_m(0x%x)\n", + i++, + req, + req->base.print.verbose, + req->base.print.concise, + req->base.stats_type_upload_mask, + req->base.stats_type_reset_mask + ); + qdf_mem_free(req); + } + qdf_spin_unlock_bh(&pdev->req_list_spinlock); + + qdf_spinlock_destroy(&pdev->req_list_spinlock); + qdf_spinlock_destroy(&pdev->tx_mutex); + + OL_RX_REORDER_TIMEOUT_CLEANUP(pdev); + + if (pdev->cfg.is_high_latency) + ol_tx_sched_detach(pdev); + + htt_deregister_rx_pkt_dump_callback(pdev->htt_pdev); + + htt_pdev_free(pdev->htt_pdev); + ol_txrx_peer_find_detach(pdev); + ol_txrx_tso_stats_deinit(pdev); + ol_txrx_fw_stats_desc_pool_deinit(pdev); + + ol_txrx_pdev_txq_log_destroy(pdev); + ol_txrx_pdev_grp_stat_destroy(pdev); + + ol_txrx_debugfs_exit(pdev); + ol_unregister_peer_recovery_notifier(); + + soc->pdev_list[pdev->id] = NULL; + qdf_mem_free(pdev); + + return QDF_STATUS_SUCCESS; +} + +#if defined(QCA_HL_NETDEV_FLOW_CONTROL) + +/** + * ol_txrx_vdev_per_vdev_tx_desc_init() - initialise per vdev tx desc count + * related variables. + * @vdev: the virtual device object + * + * Return: None + */ +static inline void +ol_txrx_vdev_per_vdev_tx_desc_init(struct ol_txrx_vdev_t *vdev) +{ + qdf_atomic_init(&vdev->tx_desc_count); + vdev->tx_desc_limit = 0; + vdev->queue_restart_th = 0; + vdev->prio_q_paused = 0; + vdev->queue_stop_th = 0; +} +#else + +static inline void +ol_txrx_vdev_per_vdev_tx_desc_init(struct ol_txrx_vdev_t *vdev) +{ +} +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + +/** + * ol_txrx_vdev_attach - Allocate and initialize the data object + * for a new virtual device. + * + * @@soc_hdl - data path soc handle + * @pdev_id - physical device instance id + * @vdev_mac_addr - the MAC address of the virtual device + * @vdev_id - the ID used to identify the virtual device to the target + * @op_mode - whether this virtual device is operating as an AP, + * an IBSS, or a STA + * @subtype: Subtype of the operating vdev + * + * Return: QDF_STATUS_SUCCESS on success, + QDF error code on failure + */ +static QDF_STATUS +ol_txrx_vdev_attach(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint8_t *vdev_mac_addr, + uint8_t vdev_id, enum wlan_op_mode op_mode, + enum wlan_op_subtype subtype) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + + struct ol_txrx_vdev_t *vdev; + QDF_STATUS qdf_status; + + /* preconditions */ + TXRX_ASSERT2(pdev); + TXRX_ASSERT2(vdev_mac_addr); + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return QDF_STATUS_E_INVAL; + } + + vdev = qdf_mem_malloc(sizeof(*vdev)); + if (!vdev) + return QDF_STATUS_E_NOMEM; /* failure */ + + /* store provided params */ + vdev->pdev = pdev; + vdev->vdev_id = vdev_id; + vdev->opmode = op_mode; + vdev->subtype = subtype; + + vdev->delete.pending = 0; + vdev->safemode = 0; + vdev->drop_unenc = 1; + vdev->num_filters = 0; + vdev->fwd_tx_packets = 0; + vdev->fwd_rx_packets = 0; + + ol_txrx_vdev_per_vdev_tx_desc_init(vdev); + + qdf_mem_copy(&vdev->mac_addr.raw[0], vdev_mac_addr, + QDF_MAC_ADDR_SIZE); + + TAILQ_INIT(&vdev->peer_list); + vdev->last_real_peer = NULL; + +#ifdef QCA_IBSS_SUPPORT + vdev->ibss_peer_num = 0; + vdev->ibss_peer_heart_beat_timer = 0; +#endif + + ol_txrx_vdev_txqs_init(vdev); + + qdf_spinlock_create(&vdev->ll_pause.mutex); + vdev->ll_pause.paused_reason = 0; + vdev->ll_pause.txq.head = vdev->ll_pause.txq.tail = NULL; + vdev->ll_pause.txq.depth = 0; + qdf_atomic_init(&vdev->delete.detaching); + qdf_timer_init(pdev->osdev, + &vdev->ll_pause.timer, + ol_tx_vdev_ll_pause_queue_send, vdev, + QDF_TIMER_TYPE_SW); + qdf_atomic_init(&vdev->os_q_paused); + qdf_atomic_set(&vdev->os_q_paused, 0); + vdev->tx_fl_lwm = 0; + vdev->tx_fl_hwm = 0; + vdev->rx = NULL; + vdev->wait_on_peer_id = OL_TXRX_INVALID_LOCAL_PEER_ID; + qdf_mem_zero(&vdev->last_peer_mac_addr, + sizeof(union ol_txrx_align_mac_addr_t)); + qdf_spinlock_create(&vdev->flow_control_lock); + vdev->osif_flow_control_cb = NULL; + vdev->osif_flow_control_is_pause = NULL; + vdev->osif_fc_ctx = NULL; + + vdev->txrx_stats.txack_success = 0; + vdev->txrx_stats.txack_failed = 0; + + vdev->bundling_required = false; + qdf_spinlock_create(&vdev->bundle_queue.mutex); + vdev->bundle_queue.txq.head = NULL; + vdev->bundle_queue.txq.tail = NULL; + vdev->bundle_queue.txq.depth = 0; + qdf_timer_init( + pdev->osdev, + &vdev->bundle_queue.timer, + ol_tx_hl_vdev_bundle_timer, + vdev, QDF_TIMER_TYPE_SW); + + /* Default MAX Q depth for every VDEV */ + vdev->ll_pause.max_q_depth = + ol_tx_cfg_max_tx_queue_depth_ll(vdev->pdev->ctrl_pdev); + qdf_status = qdf_event_create(&vdev->wait_delete_comp); + + ol_txrx_vdev_init_tcp_del_ack(vdev); + + /* add this vdev into the pdev's list */ + TAILQ_INSERT_TAIL(&pdev->vdev_list, vdev, vdev_list_elem); + if (QDF_GLOBAL_MONITOR_MODE == cds_get_conparam()) + pdev->monitor_vdev = vdev; + + ol_txrx_hl_tdls_flag_reset(soc_hdl, vdev_id, false); + + ol_txrx_dbg( + "Created vdev %pK ("QDF_MAC_ADDR_FMT")\n", + vdev, + QDF_MAC_ADDR_REF(vdev->mac_addr.raw)); + + /* + * We've verified that htt_op_mode == wlan_op_mode, + * so no translation is needed. + */ + htt_vdev_attach(pdev->htt_pdev, vdev_id, op_mode); + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_vdev_register - Link a vdev's data object with the + * matching OS shim vdev object. + * + * @soc_hdl: datapath soc handle + * @vdev_id: the virtual device's id + * @osif_vdev: the virtual device's OS shim object + * @txrx_ops: (pointers to)functions used for tx and rx data xfer + * + * The data object for a virtual device is created by the + * function ol_txrx_vdev_attach. However, rather than fully + * linking the data vdev object with the vdev objects from the + * other subsystems that the data vdev object interacts with, + * the txrx_vdev_attach function focuses primarily on creating + * the data vdev object. After the creation of both the data + * vdev object and the OS shim vdev object, this + * txrx_osif_vdev_attach function is used to connect the two + * vdev objects, so the data SW can use the OS shim vdev handle + * when passing rx data received by a vdev up to the OS shim. + */ +static QDF_STATUS ol_txrx_vdev_register(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + ol_osif_vdev_handle osif_vdev, + struct ol_txrx_ops *txrx_ops) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (qdf_unlikely(!vdev) || qdf_unlikely(!txrx_ops)) { + qdf_print("vdev/txrx_ops is NULL!"); + qdf_assert(0); + return QDF_STATUS_E_FAILURE; + } + + vdev->osif_dev = osif_vdev; + vdev->rx = txrx_ops->rx.rx; + vdev->stats_rx = txrx_ops->rx.stats_rx; + vdev->tx_comp = txrx_ops->tx.tx_comp; + txrx_ops->tx.tx = ol_tx_data; + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_set_privacy_filters - set the privacy filter + * @vdev - the data virtual device object + * @filter - filters to be set + * @num - the number of filters + * + * Rx related. Set the privacy filters. When rx packets, check + * the ether type, filter type and packet type to decide whether + * discard these packets. + */ +static void +ol_txrx_set_privacy_filters(ol_txrx_vdev_handle vdev, + void *filters, uint32_t num) +{ + qdf_mem_copy(vdev->privacy_filters, filters, + num * sizeof(struct privacy_exemption)); + vdev->num_filters = num; +} + +#if defined(CONFIG_HL_SUPPORT) || defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) + +static void +ol_txrx_tx_desc_reset_vdev(ol_txrx_vdev_handle vdev) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + int i; + struct ol_tx_desc_t *tx_desc; + + qdf_spin_lock_bh(&pdev->tx_mutex); + for (i = 0; i < pdev->tx_desc.pool_size; i++) { + tx_desc = ol_tx_desc_find(pdev, i); + if (tx_desc->vdev == vdev) + tx_desc->vdev = NULL; + } + qdf_spin_unlock_bh(&pdev->tx_mutex); +} + +#else +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 +static void ol_txrx_tx_desc_reset_vdev(ol_txrx_vdev_handle vdev) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + struct ol_tx_flow_pool_t *pool; + int i; + struct ol_tx_desc_t *tx_desc; + + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + for (i = 0; i < pdev->tx_desc.pool_size; i++) { + tx_desc = ol_tx_desc_find(pdev, i); + if (!qdf_atomic_read(&tx_desc->ref_cnt)) + /* not in use */ + continue; + + pool = tx_desc->pool; + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (tx_desc->vdev == vdev) + tx_desc->vdev = NULL; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); +} + +#else +static void +ol_txrx_tx_desc_reset_vdev(ol_txrx_vdev_handle vdev) +{ +} +#endif /* QCA_LL_TX_FLOW_CONTROL_V2 */ +#endif /* CONFIG_HL_SUPPORT */ + +/** + * ol_txrx_vdev_detach - Deallocate the specified data virtual + * device object. + * @soc_hdl - data path soc handle + * @vdev_id: vdev id + * @callback: function to call (if non-NULL) once the vdev has + * been wholly deleted + * @callback_context: context to provide in the callback + * + * All peers associated with the virtual device need to be deleted + * (ol_txrx_peer_detach) before the virtual device itself is deleted. + * However, for the peers to be fully deleted, the peer deletion has to + * percolate through the target data FW and back up to the host data SW. + * Thus, even though the host control SW may have issued a peer_detach + * call for each of the vdev's peers, the peer objects may still be + * allocated, pending removal of all references to them by the target FW. + * In this case, though the vdev_detach function call will still return + * immediately, the vdev itself won't actually be deleted, until the + * deletions of all its peers complete. + * The caller can provide a callback function pointer to be notified when + * the vdev deletion actually happens - whether it's directly within the + * vdev_detach call, or if it's deferred until all in-progress peer + * deletions have completed. + */ +static QDF_STATUS +ol_txrx_vdev_detach(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + ol_txrx_vdev_delete_cb callback, void *context) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + struct ol_txrx_pdev_t *pdev; + + if (qdf_unlikely(!vdev)) + return QDF_STATUS_E_FAILURE; + + /* preconditions */ + TXRX_ASSERT2(vdev); + pdev = vdev->pdev; + + /* prevent anyone from restarting the ll_pause timer again */ + qdf_atomic_set(&vdev->delete.detaching, 1); + + ol_txrx_vdev_tx_queue_free(vdev); + + qdf_spin_lock_bh(&vdev->ll_pause.mutex); + qdf_timer_stop(&vdev->ll_pause.timer); + vdev->ll_pause.is_q_timer_on = false; + while (vdev->ll_pause.txq.head) { + qdf_nbuf_t next = qdf_nbuf_next(vdev->ll_pause.txq.head); + + qdf_nbuf_set_next(vdev->ll_pause.txq.head, NULL); + qdf_nbuf_tx_free(vdev->ll_pause.txq.head, QDF_NBUF_PKT_ERROR); + vdev->ll_pause.txq.head = next; + } + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + + /* ll_pause timer should be deleted without any locks held, and + * no timer function should be executed after this point because + * qdf_timer_free is deleting the timer synchronously. + */ + qdf_timer_free(&vdev->ll_pause.timer); + qdf_spinlock_destroy(&vdev->ll_pause.mutex); + + qdf_timer_free(&vdev->bundle_queue.timer); + qdf_spinlock_destroy(&vdev->bundle_queue.mutex); + + qdf_spin_lock_bh(&vdev->flow_control_lock); + vdev->osif_flow_control_cb = NULL; + vdev->osif_flow_control_is_pause = NULL; + vdev->osif_fc_ctx = NULL; + qdf_spin_unlock_bh(&vdev->flow_control_lock); + qdf_spinlock_destroy(&vdev->flow_control_lock); + + /* remove the vdev from its parent pdev's list */ + TAILQ_REMOVE(&pdev->vdev_list, vdev, vdev_list_elem); + + /* + * Use peer_ref_mutex while accessing peer_list, in case + * a peer is in the process of being removed from the list. + */ + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + /* check that the vdev has no peers allocated */ + if (!TAILQ_EMPTY(&vdev->peer_list)) { + /* debug print - will be removed later */ + ol_txrx_dbg( + "not deleting vdev object %pK ("QDF_MAC_ADDR_FMT") until deletion finishes for all its peers\n", + vdev, + QDF_MAC_ADDR_REF(vdev->mac_addr.raw)); + /* indicate that the vdev needs to be deleted */ + vdev->delete.pending = 1; + vdev->delete.callback = callback; + vdev->delete.context = context; + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + return QDF_STATUS_E_FAILURE; + } + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + qdf_event_destroy(&vdev->wait_delete_comp); + + ol_txrx_dbg( + "deleting vdev obj %pK ("QDF_MAC_ADDR_FMT")\n", + vdev, + QDF_MAC_ADDR_REF(vdev->mac_addr.raw)); + + htt_vdev_detach(pdev->htt_pdev, vdev->vdev_id); + + /* + * The ol_tx_desc_free might access the invalid content of vdev referred + * by tx desc, since this vdev might be detached in another thread + * asynchronous. + * + * Go through tx desc pool to set corresponding tx desc's vdev to NULL + * when detach this vdev, and add vdev checking in the ol_tx_desc_free + * to avoid crash. + * + */ + ol_txrx_tx_desc_reset_vdev(vdev); + + /* + * Doesn't matter if there are outstanding tx frames - + * they will be freed once the target sends a tx completion + * message for them. + */ + qdf_mem_free(vdev); + if (callback) + callback(context); + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_flush_rx_frames() - flush cached rx frames + * @peer: peer + * @drop: set flag to drop frames + * + * Return: None + */ +void ol_txrx_flush_rx_frames(struct ol_txrx_peer_t *peer, + bool drop) +{ + struct ol_txrx_cached_bufq_t *bufqi; + struct ol_rx_cached_buf *cache_buf; + QDF_STATUS ret; + ol_txrx_rx_fp data_rx = NULL; + + if (qdf_atomic_inc_return(&peer->flush_in_progress) > 1) { + qdf_atomic_dec(&peer->flush_in_progress); + return; + } + + qdf_assert(peer->vdev); + qdf_spin_lock_bh(&peer->peer_info_lock); + bufqi = &peer->bufq_info; + + if (peer->state >= OL_TXRX_PEER_STATE_CONN && peer->vdev->rx) + data_rx = peer->vdev->rx; + else + drop = true; + qdf_spin_unlock_bh(&peer->peer_info_lock); + + qdf_spin_lock_bh(&bufqi->bufq_lock); + cache_buf = list_entry((&bufqi->cached_bufq)->next, + typeof(*cache_buf), list); + while (!list_empty(&bufqi->cached_bufq)) { + list_del(&cache_buf->list); + bufqi->curr--; + qdf_assert(bufqi->curr >= 0); + qdf_spin_unlock_bh(&bufqi->bufq_lock); + if (drop) { + qdf_nbuf_free(cache_buf->buf); + } else { + /* Flush the cached frames to HDD */ + ret = data_rx(peer->vdev->osif_dev, cache_buf->buf); + if (ret != QDF_STATUS_SUCCESS) + qdf_nbuf_free(cache_buf->buf); + } + qdf_mem_free(cache_buf); + qdf_spin_lock_bh(&bufqi->bufq_lock); + cache_buf = list_entry((&bufqi->cached_bufq)->next, + typeof(*cache_buf), list); + } + bufqi->qdepth_no_thresh = bufqi->curr; + qdf_spin_unlock_bh(&bufqi->bufq_lock); + qdf_atomic_dec(&peer->flush_in_progress); +} + +static void ol_txrx_flush_cache_rx_queue(void) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct ol_txrx_peer_t *peer; + struct ol_txrx_vdev_t *vdev; + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) + return; + + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { + ol_txrx_flush_rx_frames(peer, 1); + } + } +} + +/* Define short name to use in cds_trigger_recovery */ +#define PEER_DEL_TIMEOUT QDF_PEER_DELETION_TIMEDOUT + +/** + * ol_txrx_dump_peer_access_list() - dump peer access list + * @peer: peer handle + * + * This function will dump if any peer debug ids are still accessing peer + * + * Return: None + */ +static void ol_txrx_dump_peer_access_list(ol_txrx_peer_handle peer) +{ + u32 i; + u32 pending_ref; + + for (i = 0; i < PEER_DEBUG_ID_MAX; i++) { + pending_ref = qdf_atomic_read(&peer->access_list[i]); + if (pending_ref) + ol_txrx_info_high("id %d pending refs %d", + i, pending_ref); + } +} + +/** + * ol_txrx_peer_attach - Allocate and set up references for a + * data peer object. + * @soc_hdl - data path soc handle + * @vdev_id - virtual device instance id + * @peer_mac_addr - MAC address of the new peer + * + * When an association with a peer starts, the host's control SW + * uses this function to inform the host data SW. + * The host data SW allocates its own peer object, and stores a + * reference to the control peer object within the data peer object. + * The host data SW also stores a reference to the virtual device + * that the peer is associated with. This virtual device handle is + * used when the data SW delivers rx data frames to the OS shim layer. + * The host data SW returns a handle to the new peer data object, + * so a reference within the control peer object can be set to the + * data peer object. + * + * Return: QDF status code + */ +static QDF_STATUS +ol_txrx_peer_attach(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t *peer_mac_addr) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + struct ol_txrx_peer_t *peer; + struct ol_txrx_peer_t *temp_peer; + uint8_t i; + bool wait_on_deletion = false; + unsigned long rc; + struct ol_txrx_pdev_t *pdev; + bool cmp_wait_mac = false; + uint8_t zero_mac_addr[QDF_MAC_ADDR_SIZE] = { 0, 0, 0, 0, 0, 0 }; + u8 check_valid = 0; + + /* preconditions */ + TXRX_ASSERT2(vdev); + TXRX_ASSERT2(peer_mac_addr); + + pdev = vdev->pdev; + TXRX_ASSERT2(pdev); + + if (pdev->enable_peer_unmap_conf_support) + check_valid = 1; + + if (qdf_mem_cmp(&zero_mac_addr, &vdev->last_peer_mac_addr, + QDF_MAC_ADDR_SIZE)) + cmp_wait_mac = true; + + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + /* check for duplicate existing peer */ + TAILQ_FOREACH(temp_peer, &vdev->peer_list, peer_list_elem) { + if (!ol_txrx_peer_find_mac_addr_cmp(&temp_peer->mac_addr, + (union ol_txrx_align_mac_addr_t *)peer_mac_addr) && + (check_valid == 0 || temp_peer->valid)) { + ol_txrx_info_high( + "vdev_id %d ("QDF_MAC_ADDR_FMT") already exists.\n", + vdev->vdev_id, + QDF_MAC_ADDR_REF(peer_mac_addr)); + if (qdf_atomic_read(&temp_peer->delete_in_progress)) { + vdev->wait_on_peer_id = temp_peer->local_id; + qdf_event_reset(&vdev->wait_delete_comp); + wait_on_deletion = true; + break; + } else { + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + return QDF_STATUS_E_FAILURE; + } + } + if (cmp_wait_mac && !ol_txrx_peer_find_mac_addr_cmp( + &temp_peer->mac_addr, + &vdev->last_peer_mac_addr) && + (check_valid == 0 || + temp_peer->valid)) { + ol_txrx_info_high( + "vdev_id %d ("QDF_MAC_ADDR_FMT") old peer exists.\n", + vdev->vdev_id, + QDF_MAC_ADDR_REF(vdev->last_peer_mac_addr.raw)); + if (qdf_atomic_read(&temp_peer->delete_in_progress)) { + vdev->wait_on_peer_id = temp_peer->local_id; + qdf_event_reset(&vdev->wait_delete_comp); + wait_on_deletion = true; + break; + } else { + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + ol_txrx_err("peer not found"); + return QDF_STATUS_E_FAILURE; + } + } + } + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + + qdf_mem_zero(&vdev->last_peer_mac_addr, + sizeof(union ol_txrx_align_mac_addr_t)); + if (wait_on_deletion) { + /* wait for peer deletion */ + rc = qdf_wait_for_event_completion(&vdev->wait_delete_comp, + PEER_DELETION_TIMEOUT); + if (QDF_STATUS_SUCCESS != rc) { + ol_txrx_err("error waiting for peer_id(%d) deletion, status %d\n", + vdev->wait_on_peer_id, (int) rc); + /* Added for debugging only */ + ol_txrx_dump_peer_access_list(temp_peer); + wlan_roam_debug_dump_table(); + vdev->wait_on_peer_id = OL_TXRX_INVALID_LOCAL_PEER_ID; + + return QDF_STATUS_E_FAILURE; + } + } + + peer = qdf_mem_malloc(sizeof(*peer)); + if (!peer) + return QDF_STATUS_E_NOMEM; + + /* store provided params */ + peer->vdev = vdev; + qdf_mem_copy(&peer->mac_addr.raw[0], peer_mac_addr, + QDF_MAC_ADDR_SIZE); + + ol_txrx_peer_txqs_init(pdev, peer); + + INIT_LIST_HEAD(&peer->bufq_info.cached_bufq); + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + /* add this peer into the vdev's list */ + TAILQ_INSERT_TAIL(&vdev->peer_list, peer, peer_list_elem); + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + /* check whether this is a real peer (peer mac addr != vdev mac addr) */ + if (ol_txrx_peer_find_mac_addr_cmp(&vdev->mac_addr, &peer->mac_addr)) { + qdf_spin_lock_bh(&pdev->last_real_peer_mutex); + vdev->last_real_peer = peer; + qdf_spin_unlock_bh(&pdev->last_real_peer_mutex); + } + + peer->rx_opt_proc = pdev->rx_opt_proc; + + ol_rx_peer_init(pdev, peer); + + /* initialize the peer_id */ + for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) + peer->peer_ids[i] = HTT_INVALID_PEER; + + qdf_spinlock_create(&peer->peer_info_lock); + qdf_spinlock_create(&peer->bufq_info.bufq_lock); + + peer->bufq_info.thresh = OL_TXRX_CACHED_BUFQ_THRESH; + + qdf_atomic_init(&peer->delete_in_progress); + qdf_atomic_init(&peer->flush_in_progress); + qdf_atomic_init(&peer->ref_cnt); + + for (i = 0; i < PEER_DEBUG_ID_MAX; i++) + qdf_atomic_init(&peer->access_list[i]); + + /* keep one reference for attach */ + ol_txrx_peer_get_ref(peer, PEER_DEBUG_ID_OL_PEER_ATTACH); + + /* Set a flag to indicate peer create is pending in firmware */ + qdf_atomic_init(&peer->fw_create_pending); + qdf_atomic_set(&peer->fw_create_pending, 1); + + peer->valid = 1; + qdf_timer_init(pdev->osdev, &peer->peer_unmap_timer, + peer_unmap_timer_handler, peer, QDF_TIMER_TYPE_SW); + + ol_txrx_peer_find_hash_add(pdev, peer); + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH, + "vdev %pK created peer %pK ref_cnt %d ("QDF_MAC_ADDR_FMT")\n", + vdev, peer, qdf_atomic_read(&peer->ref_cnt), + QDF_MAC_ADDR_REF(peer->mac_addr.raw)); + /* + * For every peer MAp message search and set if bss_peer + */ + if (qdf_mem_cmp(peer->mac_addr.raw, vdev->mac_addr.raw, + QDF_MAC_ADDR_SIZE)) + peer->bss_peer = 1; + + /* + * The peer starts in the "disc" state while association is in progress. + * Once association completes, the peer will get updated to "auth" state + * by a call to ol_txrx_peer_state_update if the peer is in open mode, + * or else to the "conn" state. For non-open mode, the peer will + * progress to "auth" state once the authentication completes. + */ + peer->state = OL_TXRX_PEER_STATE_INVALID; + ol_txrx_peer_state_update(soc_hdl, peer->mac_addr.raw, + OL_TXRX_PEER_STATE_DISC); + +#ifdef QCA_SUPPORT_PEER_DATA_RX_RSSI + peer->rssi_dbm = HTT_RSSI_INVALID; +#endif + if ((QDF_GLOBAL_MONITOR_MODE == cds_get_conparam()) && + !pdev->self_peer) { + pdev->self_peer = peer; + /* + * No Tx in monitor mode, otherwise results in target assert. + * Setting disable_intrabss_fwd to true + */ + ol_vdev_rx_set_intrabss_fwd(soc_hdl, vdev_id, true); + } + + ol_txrx_local_peer_id_alloc(pdev, peer); + + return QDF_STATUS_SUCCESS; +} + +#undef PEER_DEL_TIMEOUT + +/* + * Discarding tx filter - removes all data frames (disconnected state) + */ +static A_STATUS ol_tx_filter_discard(struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + return A_ERROR; +} + +/* + * Non-autentication tx filter - filters out data frames that are not + * related to authentication, but allows EAPOL (PAE) or WAPI (WAI) + * data frames (connected state) + */ +static A_STATUS ol_tx_filter_non_auth(struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + return + (tx_msdu_info->htt.info.ethertype == ETHERTYPE_PAE || + tx_msdu_info->htt.info.ethertype == + ETHERTYPE_WAI) ? A_OK : A_ERROR; +} + +/* + * Pass-through tx filter - lets all data frames through (authenticated state) + */ +static A_STATUS ol_tx_filter_pass_thru(struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + return A_OK; +} + +/** + * ol_txrx_peer_get_peer_mac_addr() - return mac_addr from peer handle. + * @peer: handle to peer + * + * returns mac addrs for module which do not know peer type + * + * Return: the mac_addr from peer + */ +static uint8_t * +ol_txrx_peer_get_peer_mac_addr(void *ppeer) +{ + ol_txrx_peer_handle peer = ppeer; + + if (!peer) + return NULL; + + return peer->mac_addr.raw; +} + +#ifdef WLAN_FEATURE_11W +/** + * ol_txrx_get_pn_info() - Returns pn info from peer + * @soc_hdl: soc handle + * @peer_mac: mac address of the peer + * @vdev_id: vdev identifier + * @last_pn_valid: return last_rmf_pn_valid value from peer. + * @last_pn: return last_rmf_pn value from peer. + * @rmf_pn_replays: return rmf_pn_replays value from peer. + * + * Return: NONE + */ +static void +ol_txrx_get_pn_info(struct cdp_soc_t *soc_hdl, uint8_t *peer_mac, + uint8_t vdev_id, uint8_t **last_pn_valid, + uint64_t **last_pn, uint32_t **rmf_pn_replays) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev; + ol_txrx_peer_handle peer; + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + peer = ol_txrx_peer_find_hash_find_get_ref(pdev, peer_mac, 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + if (!peer) + return; + + *last_pn_valid = &peer->last_rmf_pn_valid; + *last_pn = &peer->last_rmf_pn; + *rmf_pn_replays = &peer->rmf_pn_replays; + + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); +} +#else +static void +ol_txrx_get_pn_info(struct cdp_soc_t *soc_hdl, uint8_t *peer_mac, + uint8_t vdev_id, uint8_t **last_pn_valid, + uint64_t **last_pn, uint32_t **rmf_pn_replays) +{ +} +#endif + +/** + * ol_txrx_get_opmode() - Return operation mode of vdev + * @soc_hdl: Datapath soc handle + * @vdev_id: id of vdev + * + * Return: interface opmode if SUCCESS, + * 0 if interface does not exist. + */ +static int ol_txrx_get_opmode(struct cdp_soc_t *soc_hdl, uint8_t vdev_id) +{ + struct ol_txrx_vdev_t *vdev; + + vdev = (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + if (!vdev) { + ol_txrx_err("vdev for id %d is NULL", vdev_id); + return 0; + } + + return vdev->opmode; +} + +/** + * ol_txrx_get_peer_state() - Return peer state of peer + * @soc_hdl: datapath soc handle + * @vdev_id: virtual interface id + * @peer_mac: peer mac addr + * + * Return: return peer state + */ +static int ol_txrx_get_peer_state(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t *peer_mac) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = + ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + ol_txrx_peer_handle peer; + enum ol_txrx_peer_state peer_state; + + if (!pdev) + return QDF_STATUS_E_FAILURE; + + peer = ol_txrx_peer_find_hash_find_get_ref(pdev, peer_mac, 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + if (!peer) + return QDF_STATUS_E_FAILURE; + + peer_state = peer->state; + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); + + return peer_state; +} + +/** + * ol_txrx_get_vdev_mac_addr() - Return mac addr of vdev + * @soc_hdl: datapath soc handle + x @vdev_id: virtual interface id + * + * Return: vdev mac address + */ +static uint8_t * +ol_txrx_get_vdev_mac_addr(struct cdp_soc_t *soc_hdl, uint8_t vdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev) + return NULL; + + return vdev->mac_addr.raw; +} + +#ifdef currently_unused +/** + * ol_txrx_get_vdev_struct_mac_addr() - Return handle to struct qdf_mac_addr of + * vdev + * @vdev: vdev handle + * + * Return: Handle to struct qdf_mac_addr + */ +struct qdf_mac_addr * +ol_txrx_get_vdev_struct_mac_addr(ol_txrx_vdev_handle vdev) +{ + return (struct qdf_mac_addr *)&(vdev->mac_addr); +} +#endif + +#ifdef currently_unused +/** + * ol_txrx_get_pdev_from_vdev() - Return handle to pdev of vdev + * @vdev: vdev handle + * + * Return: Handle to pdev + */ +ol_txrx_pdev_handle ol_txrx_get_pdev_from_vdev(ol_txrx_vdev_handle vdev) +{ + return vdev->pdev; +} +#endif + +/** + * ol_txrx_get_ctrl_pdev_from_vdev() - Return control pdev of vdev + * @soc_hdl: datapath soc handle + * @vdev_id: virtual interface id + * + * Return: Handle to control pdev + */ +static struct cdp_cfg * +ol_txrx_get_ctrl_pdev_from_vdev(struct cdp_soc_t *soc_hdl, uint8_t vdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev) + return NULL; + + return vdev->pdev->ctrl_pdev; +} + +/** + * ol_txrx_is_rx_fwd_disabled() - returns the rx_fwd_disabled status on vdev + * @vdev: vdev handle + * + * Return: Rx Fwd disabled status + */ +static uint8_t +ol_txrx_is_rx_fwd_disabled(struct cdp_vdev *pvdev) +{ + struct ol_txrx_vdev_t *vdev = (struct ol_txrx_vdev_t *)pvdev; + struct txrx_pdev_cfg_t *cfg = (struct txrx_pdev_cfg_t *) + vdev->pdev->ctrl_pdev; + return cfg->rx_fwd_disabled; +} + +#ifdef QCA_IBSS_SUPPORT +/** + * ol_txrx_update_ibss_add_peer_num_of_vdev() - update and return peer num + * @soc_hdl: datapath soc handle + * @vdev_id: virtual interface id + * @peer_num_delta: peer nums to be adjusted + * + * Return: -1 for failure or total peer nums after adjustment. + */ +static int16_t +ol_txrx_update_ibss_add_peer_num_of_vdev(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + int16_t peer_num_delta) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + int16_t new_peer_num; + + if (!vdev) + return QDF_STATUS_E_FAILURE; + + new_peer_num = vdev->ibss_peer_num + peer_num_delta; + if (new_peer_num > MAX_PEERS || new_peer_num < 0) + return OL_TXRX_INVALID_NUM_PEERS; + + vdev->ibss_peer_num = new_peer_num; + + return new_peer_num; +} + +/** + * ol_txrx_set_ibss_vdev_heart_beat_timer() - Update ibss vdev heart + * beat timer + * @soc_hdl: Datapath soc handle + * @vdev_id: id of vdev + * @timer_value_sec: new heart beat timer value + * + * Return: Old timer value set in vdev. + */ +static uint16_t +ol_txrx_set_ibss_vdev_heart_beat_timer(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + uint16_t timer_value_sec) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + uint16_t old_timer_value = vdev->ibss_peer_heart_beat_timer; + + vdev->ibss_peer_heart_beat_timer = timer_value_sec; + + return old_timer_value; +} +#else /* !QCA_IBSS_SUPPORT */ +static inline int16_t +ol_txrx_update_ibss_add_peer_num_of_vdev(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + int16_t peer_num_delta) +{ + return 0; +} + +static uint16_t ol_txrx_set_ibss_vdev_heart_beat_timer( + struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint16_t timer_value_sec) +{ + return 0; +} +#endif /* QCA_IBSS_SUPPORT */ + +#ifdef WLAN_FEATURE_DSRC +/** + * ol_txrx_set_ocb_chan_info() - set OCB channel info to vdev. + * @soc_hdl: Datapath soc handle + * @vdev_id: id of vdev + * @ocb_set_chan: OCB channel information to be set in vdev. + * + * Return: NONE + */ +static void +ol_txrx_set_ocb_chan_info(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + struct ol_txrx_ocb_set_chan ocb_set_chan) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("pdev is NULL"); + return; + } + + vdev->ocb_channel_info = ocb_set_chan.ocb_channel_info; + vdev->ocb_channel_count = ocb_set_chan.ocb_channel_count; +} + +/** + * ol_txrx_get_ocb_chan_info() - return handle to vdev ocb_channel_info + * @soc_hdl: Datapath soc handle + * @vdev_id: id of vdev + * + * Return: handle to struct ol_txrx_ocb_chan_info + */ +static struct ol_txrx_ocb_chan_info * +ol_txrx_get_ocb_chan_info(struct cdp_soc_t *soc_hdl, uint8_t vdev_id) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("pdev is NULL"); + return NULL; + } + + return vdev->ocb_channel_info; +} +#endif + +QDF_STATUS ol_txrx_peer_state_update(struct cdp_soc_t *soc_hdl, + uint8_t *peer_mac, + enum ol_txrx_peer_state state) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = + ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + struct ol_txrx_peer_t *peer; + int peer_ref_cnt; + + if (qdf_unlikely(!pdev)) { + ol_txrx_err("Pdev is NULL"); + qdf_assert(0); + return QDF_STATUS_E_INVAL; + } + + peer = ol_txrx_peer_find_hash_find_get_ref(pdev, peer_mac, 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + if (!peer) { + ol_txrx_err( + "peer is null for peer_mac 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + peer_mac[0], peer_mac[1], peer_mac[2], peer_mac[3], + peer_mac[4], peer_mac[5]); + return QDF_STATUS_E_INVAL; + } + + /* TODO: Should we send WMI command of the connection state? */ + /* avoid multiple auth state change. */ + if (peer->state == state) { +#ifdef TXRX_PRINT_VERBOSE_ENABLE + ol_txrx_dbg("no state change, returns directly"); +#endif + peer_ref_cnt = ol_txrx_peer_release_ref + (peer, + PEER_DEBUG_ID_OL_INTERNAL); + return QDF_STATUS_SUCCESS; + } + + ol_txrx_dbg("change from %d to %d", + peer->state, state); + + peer->tx_filter = (state == OL_TXRX_PEER_STATE_AUTH) + ? ol_tx_filter_pass_thru + : ((state == OL_TXRX_PEER_STATE_CONN) + ? ol_tx_filter_non_auth + : ol_tx_filter_discard); + + if (peer->vdev->pdev->cfg.host_addba) { + if (state == OL_TXRX_PEER_STATE_AUTH) { + int tid; + /* + * Pause all regular (non-extended) TID tx queues until + * data arrives and ADDBA negotiation has completed. + */ + ol_txrx_dbg("pause peer and unpause mgmt/non-qos"); + ol_txrx_peer_pause(peer); /* pause all tx queues */ + /* unpause mgmt and non-QoS tx queues */ + for (tid = OL_TX_NUM_QOS_TIDS; + tid < OL_TX_NUM_TIDS; tid++) + ol_txrx_peer_tid_unpause(peer, tid); + } + } + peer_ref_cnt = ol_txrx_peer_release_ref(peer, + PEER_DEBUG_ID_OL_INTERNAL); + /* + * after ol_txrx_peer_release_ref, peer object cannot be accessed + * if the return code was 0 + */ + if (peer_ref_cnt > 0) + /* + * Set the state after the Pause to avoid the race condiction + * with ADDBA check in tx path + */ + peer->state = state; + return QDF_STATUS_SUCCESS; +} + +void +ol_txrx_peer_keyinstalled_state_update(struct ol_txrx_peer_t *peer, uint8_t val) +{ + peer->keyinstalled = val; +} + +void +ol_txrx_peer_update(ol_txrx_vdev_handle vdev, + uint8_t *peer_mac, + union ol_txrx_peer_update_param_t *param, + enum ol_txrx_peer_update_select_t select) +{ + struct ol_txrx_peer_t *peer; + + peer = ol_txrx_peer_find_hash_find_get_ref(vdev->pdev, peer_mac, 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + if (!peer) { + ol_txrx_dbg("peer is null"); + return; + } + + switch (select) { + case ol_txrx_peer_update_qos_capable: + { + /* save qos_capable here txrx peer, + * when HTT_ISOC_T2H_MSG_TYPE_PEER_INFO comes then save. + */ + peer->qos_capable = param->qos_capable; + /* + * The following function call assumes that the peer has a + * single ID. This is currently true, and + * is expected to remain true. + */ + htt_peer_qos_update(peer->vdev->pdev->htt_pdev, + peer->peer_ids[0], + peer->qos_capable); + break; + } + case ol_txrx_peer_update_uapsdMask: + { + peer->uapsd_mask = param->uapsd_mask; + htt_peer_uapsdmask_update(peer->vdev->pdev->htt_pdev, + peer->peer_ids[0], + peer->uapsd_mask); + break; + } + case ol_txrx_peer_update_peer_security: + { + enum ol_sec_type sec_type = param->sec_type; + enum htt_sec_type peer_sec_type = htt_sec_type_none; + + switch (sec_type) { + case ol_sec_type_none: + peer_sec_type = htt_sec_type_none; + break; + case ol_sec_type_wep128: + peer_sec_type = htt_sec_type_wep128; + break; + case ol_sec_type_wep104: + peer_sec_type = htt_sec_type_wep104; + break; + case ol_sec_type_wep40: + peer_sec_type = htt_sec_type_wep40; + break; + case ol_sec_type_tkip: + peer_sec_type = htt_sec_type_tkip; + break; + case ol_sec_type_tkip_nomic: + peer_sec_type = htt_sec_type_tkip_nomic; + break; + case ol_sec_type_aes_ccmp: + peer_sec_type = htt_sec_type_aes_ccmp; + break; + case ol_sec_type_wapi: + peer_sec_type = htt_sec_type_wapi; + break; + default: + peer_sec_type = htt_sec_type_none; + break; + } + + peer->security[txrx_sec_ucast].sec_type = + peer->security[txrx_sec_mcast].sec_type = + peer_sec_type; + + break; + } + default: + { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "ERROR: unknown param %d in %s", select, + __func__); + break; + } + } /* switch */ + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); +} + +uint8_t +ol_txrx_peer_uapsdmask_get(struct ol_txrx_pdev_t *txrx_pdev, uint16_t peer_id) +{ + + struct ol_txrx_peer_t *peer; + + peer = ol_txrx_peer_find_by_id(txrx_pdev, peer_id); + if (peer) + return peer->uapsd_mask; + return 0; +} + +uint8_t +ol_txrx_peer_qoscapable_get(struct ol_txrx_pdev_t *txrx_pdev, uint16_t peer_id) +{ + + struct ol_txrx_peer_t *peer_t = + ol_txrx_peer_find_by_id(txrx_pdev, peer_id); + if (peer_t) + return peer_t->qos_capable; + return 0; +} + +/** + * ol_txrx_peer_free_tids() - free tids for the peer + * @peer: peer handle + * + * Return: None + */ +static inline void ol_txrx_peer_free_tids(ol_txrx_peer_handle peer) +{ + int i = 0; + /* + * 'array' is allocated in addba handler and is supposed to be + * freed in delba handler. There is the case (for example, in + * SSR) where delba handler is not called. Because array points + * to address of 'base' by default and is reallocated in addba + * handler later, only free the memory when the array does not + * point to base. + */ + for (i = 0; i < OL_TXRX_NUM_EXT_TIDS; i++) { + if (peer->tids_rx_reorder[i].array != + &peer->tids_rx_reorder[i].base) { + ol_txrx_dbg("delete reorder arr, tid:%d", i); + qdf_mem_free(peer->tids_rx_reorder[i].array); + ol_rx_reorder_init(&peer->tids_rx_reorder[i], + (uint8_t)i); + } + } +} + +/** + * ol_txrx_peer_drop_pending_frames() - drop pending frames in the RX queue + * @peer: peer handle + * + * Drop pending packets pertaining to the peer from the RX thread queue. + * + * Return: None + */ +static void ol_txrx_peer_drop_pending_frames(struct ol_txrx_peer_t *peer) +{ + p_cds_sched_context sched_ctx = get_cds_sched_ctxt(); + + if (sched_ctx) + cds_drop_rxpkt_by_staid(sched_ctx, peer->local_id); +} + +/** + * ol_txrx_peer_release_ref() - release peer reference + * @peer: peer handle + * + * Release peer reference and delete peer if refcount is 0 + * + * Return: Resulting peer ref_cnt after this function is invoked + */ +int ol_txrx_peer_release_ref(ol_txrx_peer_handle peer, + enum peer_debug_id_type debug_id) +{ + int rc; + struct ol_txrx_vdev_t *vdev; + struct ol_txrx_pdev_t *pdev; + bool ref_silent = true; + int access_list = 0; + uint32_t err_code = 0; + + /* preconditions */ + TXRX_ASSERT2(peer); + + vdev = peer->vdev; + if (!vdev) { + ol_txrx_err("The vdev is not present anymore\n"); + return -EINVAL; + } + + pdev = vdev->pdev; + if (!pdev) { + ol_txrx_err("The pdev is not present anymore\n"); + err_code = 0xbad2; + goto ERR_STATE; + } + + if (debug_id >= PEER_DEBUG_ID_MAX || debug_id < 0) { + ol_txrx_err("incorrect debug_id %d ", debug_id); + err_code = 0xbad3; + goto ERR_STATE; + } + + if (debug_id == PEER_DEBUG_ID_OL_RX_THREAD) + ref_silent = true; + + if (!ref_silent) + wlan_roam_debug_log(vdev->vdev_id, DEBUG_PEER_UNREF_DELETE, + DEBUG_INVALID_PEER_ID, &peer->mac_addr.raw, + peer, 0xdead, + qdf_atomic_read(&peer->ref_cnt)); + + + /* + * Hold the lock all the way from checking if the peer ref count + * is zero until the peer references are removed from the hash + * table and vdev list (if the peer ref count is zero). + * This protects against a new HL tx operation starting to use the + * peer object just after this function concludes it's done being used. + * Furthermore, the lock needs to be held while checking whether the + * vdev's list of peers is empty, to make sure that list is not modified + * concurrently with the empty check. + */ + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + + /* + * Check for the reference count before deleting the peer + * as we noticed that sometimes we are re-entering this + * function again which is leading to dead-lock. + * (A double-free should never happen, so assert if it does.) + */ + rc = qdf_atomic_read(&(peer->ref_cnt)); + + if (rc == 0) { + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + ol_txrx_err("The Peer is not present anymore\n"); + qdf_assert(0); + return -EACCES; + } + /* + * now decrement rc; this will be the return code. + * 0 : peer deleted + * >0: peer ref removed, but still has other references + * <0: sanity failed - no changes to the state of the peer + */ + rc--; + + if (!qdf_atomic_read(&peer->access_list[debug_id])) { + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + ol_txrx_err("peer %pK ref was not taken by %d", + peer, debug_id); + ol_txrx_dump_peer_access_list(peer); + QDF_BUG(0); + return -EACCES; + } + qdf_atomic_dec(&peer->access_list[debug_id]); + + if (qdf_atomic_dec_and_test(&peer->ref_cnt)) { + u16 peer_id; + wlan_roam_debug_log(vdev->vdev_id, + DEBUG_DELETING_PEER_OBJ, + DEBUG_INVALID_PEER_ID, + &peer->mac_addr.raw, peer, 0, + qdf_atomic_read(&peer->ref_cnt)); + peer_id = peer->local_id; + + /* Drop all pending frames in the rx thread queue */ + ol_txrx_peer_drop_pending_frames(peer); + + /* remove the reference to the peer from the hash table */ + ol_txrx_peer_find_hash_remove(pdev, peer); + + /* remove the peer from its parent vdev's list */ + TAILQ_REMOVE(&peer->vdev->peer_list, peer, peer_list_elem); + + /* cleanup the Rx reorder queues for this peer */ + ol_rx_peer_cleanup(vdev, peer); + + qdf_spinlock_destroy(&peer->peer_info_lock); + qdf_spinlock_destroy(&peer->bufq_info.bufq_lock); + + /* peer is removed from peer_list */ + qdf_atomic_set(&peer->delete_in_progress, 0); + + /* + * Set wait_delete_comp event if the current peer id matches + * with registered peer id. + */ + if (peer_id == vdev->wait_on_peer_id) { + qdf_event_set(&vdev->wait_delete_comp); + vdev->wait_on_peer_id = OL_TXRX_INVALID_LOCAL_PEER_ID; + } + + qdf_timer_sync_cancel(&peer->peer_unmap_timer); + qdf_timer_free(&peer->peer_unmap_timer); + + /* check whether the parent vdev has no peers left */ + if (TAILQ_EMPTY(&vdev->peer_list)) { + /* + * Check if the parent vdev was waiting for its peers + * to be deleted, in order for it to be deleted too. + */ + if (vdev->delete.pending) { + ol_txrx_vdev_delete_cb vdev_delete_cb = + vdev->delete.callback; + void *vdev_delete_context = + vdev->delete.context; + /* + * Now that there are no references to the peer, + * we can release the peer reference lock. + */ + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + + /* + * The ol_tx_desc_free might access the invalid + * content of vdev referred by tx desc, since + * this vdev might be detached in another thread + * asynchronous. + * + * Go through tx desc pool to set corresponding + * tx desc's vdev to NULL when detach this vdev, + * and add vdev checking in the ol_tx_desc_free + * to avoid crash. + */ + ol_txrx_tx_desc_reset_vdev(vdev); + ol_txrx_dbg( + "deleting vdev object %pK ("QDF_MAC_ADDR_FMT") - its last peer is done", + vdev, + QDF_MAC_ADDR_REF(vdev->mac_addr.raw)); + /* all peers are gone, go ahead and delete it */ + qdf_mem_free(vdev); + if (vdev_delete_cb) + vdev_delete_cb(vdev_delete_context); + } else { + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + } + } else { + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + } + + ol_txrx_info_high("[%d][%d]: Deleting peer %pK ref_cnt -> %d %s", + debug_id, + qdf_atomic_read(&peer->access_list[debug_id]), + peer, rc, + qdf_atomic_read(&peer->fw_create_pending) == + 1 ? "(No Maps received)" : ""); + + ol_txrx_peer_tx_queue_free(pdev, peer); + + /* Remove mappings from peer_id to peer object */ + ol_txrx_peer_clear_map_peer(pdev, peer); + + /* Remove peer pointer from local peer ID map */ + ol_txrx_local_peer_id_free(pdev, peer); + + ol_txrx_peer_free_tids(peer); + + ol_txrx_dump_peer_access_list(peer); + + if (QDF_GLOBAL_MONITOR_MODE == cds_get_conparam() && + pdev->self_peer == peer) + pdev->self_peer = NULL; + + qdf_mem_free(peer); + } else { + access_list = qdf_atomic_read(&peer->access_list[debug_id]); + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + if (!ref_silent) + ol_txrx_info_high("[%d][%d]: ref delete peer %pK ref_cnt -> %d", + debug_id, access_list, peer, rc); + } + return rc; +ERR_STATE: + wlan_roam_debug_log(vdev->vdev_id, DEBUG_PEER_UNREF_DELETE, + DEBUG_INVALID_PEER_ID, &peer->mac_addr.raw, + peer, err_code, qdf_atomic_read(&peer->ref_cnt)); + return -EINVAL; +} + +/** + * ol_txrx_clear_peer_internal() - ol internal function to clear peer + * @peer: pointer to ol txrx peer structure + * + * Return: QDF Status + */ +static QDF_STATUS +ol_txrx_clear_peer_internal(struct ol_txrx_peer_t *peer) +{ + p_cds_sched_context sched_ctx = get_cds_sched_ctxt(); + /* Drop pending Rx frames in CDS */ + if (sched_ctx) + cds_drop_rxpkt_by_staid(sched_ctx, peer->local_id); + + /* Purge the cached rx frame queue */ + ol_txrx_flush_rx_frames(peer, 1); + + qdf_spin_lock_bh(&peer->peer_info_lock); + peer->state = OL_TXRX_PEER_STATE_DISC; + qdf_spin_unlock_bh(&peer->peer_info_lock); + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_clear_peer() - clear peer + * peer_addr: peer mac address + * + * Return: QDF Status + */ +static QDF_STATUS +ol_txrx_clear_peer(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + struct qdf_mac_addr peer_addr) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = + ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_txrx_peer_t *peer; + QDF_STATUS status; + + if (!pdev) { + ol_txrx_err("Unable to find pdev!"); + return QDF_STATUS_E_FAILURE; + } + + peer = ol_txrx_peer_get_ref_by_addr(pdev, peer_addr.bytes, + PEER_DEBUG_ID_OL_INTERNAL); + + /* Return success, if the peer is already cleared by + * data path via peer detach function. + */ + if (!peer) + return QDF_STATUS_SUCCESS; + + ol_txrx_dbg("Clear peer rx frames: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer->mac_addr.raw)); + ol_txrx_clear_peer_internal(peer); + status = ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); + + return status; +} + +/** + * peer_unmap_timer_handler() - peer unmap timer function + * @data: peer object pointer + * + * Return: none + */ +void peer_unmap_timer_handler(void *data) +{ + ol_txrx_peer_handle peer = (ol_txrx_peer_handle)data; + + if (!peer) + return; + + ol_txrx_err("all unmap events not received for peer %pK, ref_cnt %d", + peer, qdf_atomic_read(&peer->ref_cnt)); + ol_txrx_err("peer %pK ("QDF_MAC_ADDR_FMT")", + peer, + QDF_MAC_ADDR_REF(peer->mac_addr.raw)); + ol_register_peer_recovery_notifier(peer); + + cds_trigger_recovery(QDF_PEER_UNMAP_TIMEDOUT); +} + + +/** + * ol_txrx_peer_detach() - Delete a peer's data object. + + * @soc_hdl: datapath soc handle + * @vdev_id: virtual interface id + * @peer_mac: peer MAC address + * @bitmap - bitmap indicating special handling of request. + * When the host's control SW disassociates a peer, it calls + * this function to detach and delete the peer. The reference + * stored in the control peer object to the data peer + * object (set up by a call to ol_peer_store()) is provided. + * + * Return: SUCCESS or Failure + */ +static QDF_STATUS ol_txrx_peer_detach(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t *peer_mac, uint32_t bitmap) +{ + ol_txrx_peer_handle peer; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev) + return QDF_STATUS_E_FAILURE; + + peer = ol_txrx_find_peer_by_addr((struct cdp_pdev *)vdev->pdev, + peer_mac); + if (!peer) + return QDF_STATUS_E_FAILURE; + + ol_txrx_info_high("%s peer %pK, peer->ref_cnt %d", __func__, + peer, qdf_atomic_read(&peer->ref_cnt)); + + /* redirect peer's rx delivery function to point to a discard func */ + peer->rx_opt_proc = ol_rx_discard; + + peer->valid = 0; + + /* flush all rx packets before clearing up the peer local_id */ + ol_txrx_clear_peer_internal(peer); + + /* debug print to dump rx reorder state */ + /* htt_rx_reorder_log_print(vdev->pdev->htt_pdev); */ + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s:peer %pK ("QDF_MAC_ADDR_FMT")", + __func__, peer, + QDF_MAC_ADDR_REF(peer->mac_addr.raw)); + + qdf_spin_lock_bh(&vdev->pdev->last_real_peer_mutex); + if (vdev->last_real_peer == peer) + vdev->last_real_peer = NULL; + qdf_spin_unlock_bh(&vdev->pdev->last_real_peer_mutex); + htt_rx_reorder_log_print(peer->vdev->pdev->htt_pdev); + + /* + * set delete_in_progress to identify that wma + * is waiting for unmap massage for this peer + */ + qdf_atomic_set(&peer->delete_in_progress, 1); + + if (!(bitmap & (1 << CDP_PEER_DO_NOT_START_UNMAP_TIMER))) { + if (vdev->opmode == wlan_op_mode_sta) { + qdf_mem_copy(&peer->vdev->last_peer_mac_addr, + &peer->mac_addr, + sizeof(union ol_txrx_align_mac_addr_t)); + + /* + * Create a timer to track unmap events when the + * sta peer gets deleted. + */ + qdf_timer_start(&peer->peer_unmap_timer, + OL_TXRX_PEER_UNMAP_TIMEOUT); + ol_txrx_info_high + ("started peer_unmap_timer for peer %pK", + peer); + } + } + + /* + * Remove the reference added during peer_attach. + * The peer will still be left allocated until the + * PEER_UNMAP message arrives to remove the other + * reference, added by the PEER_MAP message. + */ + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_PEER_ATTACH); + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_peer_detach_force_delete() - Detach and delete a peer's data object + * @soc_hdl - datapath soc handle + * @vdev_id - virtual interface id + * @peer_mac - peer mac address + * + * Detach a peer and force peer object to be removed. It is called during + * roaming scenario when the firmware has already deleted a peer. + * Remove it from the peer_id_to_object map. Peer object is actually freed + * when last reference is deleted. + * + * Return: None + */ +static void ol_txrx_peer_detach_force_delete(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, uint8_t *peer_mac) +{ + struct ol_txrx_peer_t *peer; + struct ol_txrx_pdev_t *pdev; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev || !vdev->pdev) + return; + + pdev = vdev->pdev; + peer = ol_txrx_find_peer_by_addr(ol_txrx_pdev_t_to_cdp_pdev(pdev), + peer_mac); + if (!peer) + return; + + /* Clear the peer_id_to_obj map entries */ + ol_txrx_peer_remove_obj_map_entries(pdev, peer); + ol_txrx_peer_detach(soc_hdl, vdev_id, peer_mac, + 1 << CDP_PEER_DELETE_NO_SPECIAL); +} + +/** + * ol_txrx_peer_detach_sync() - peer detach sync callback + * @soc_hdl - datapath soc handle + * @vdev_id - virtual interface id + * @peer_mac - peer mac address + * @peer_unmap_sync - peer unmap sync cb. + * @bitmap - bitmap indicating special handling of request. + * + * Return: None + */ +static void ol_txrx_peer_detach_sync(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t *peer_mac, + ol_txrx_peer_unmap_sync_cb peer_unmap_sync, + uint32_t bitmap) +{ + struct ol_txrx_pdev_t *pdev; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev || !vdev->pdev) + return; + + pdev = vdev->pdev; + if (!pdev->peer_unmap_sync_cb) + pdev->peer_unmap_sync_cb = peer_unmap_sync; + + ol_txrx_peer_detach(soc_hdl, vdev_id, peer_mac, bitmap); +} + +/** + * ol_txrx_peer_unmap_sync_cb_set() - set peer unmap sync callback + * @soc_hdl - datapath soc handle + * pdev_id - physical device instance id + * @peer_unmap_sync - peer unmap sync callback + * + * Return: None + */ +static void ol_txrx_peer_unmap_sync_cb_set( + struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, + ol_txrx_peer_unmap_sync_cb peer_unmap_sync) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + + if (!pdev) + return; + + if (!pdev->peer_unmap_sync_cb) + pdev->peer_unmap_sync_cb = peer_unmap_sync; +} + +/** + * ol_txrx_peer_flush_frags() - Flush fragments for a particular peer + * @soc_hdl - datapath soc handle + * @vdev_id - virtual device id + * @peer_mac - peer mac address + * + * Return: None + */ +static void +ol_txrx_peer_flush_frags(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t *peer_mac) +{ + struct ol_txrx_peer_t *peer; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = + ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + + if (!pdev) + return; + + peer = ol_txrx_peer_find_hash_find_get_ref(pdev, peer_mac, 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + if (!peer) + return; + + ol_rx_reorder_peer_cleanup(peer->vdev, peer); + + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); +} + +/** + * ol_txrx_dump_tx_desc() - dump tx desc total and free count + * @txrx_pdev: Pointer to txrx pdev + * + * Return: none + */ +static void ol_txrx_dump_tx_desc(ol_txrx_pdev_handle pdev_handle) +{ + struct ol_txrx_pdev_t *pdev = (ol_txrx_pdev_handle) pdev_handle; + uint32_t total, num_free; + + if (ol_cfg_is_high_latency(pdev->ctrl_pdev)) + total = qdf_atomic_read(&pdev->orig_target_tx_credit); + else + total = ol_tx_get_desc_global_pool_size(pdev); + + num_free = ol_tx_get_total_free_desc(pdev); + + ol_txrx_info_high( + "total tx credit %d num_free %d", + total, num_free); + +} + +/** + * ol_txrx_wait_for_pending_tx() - wait for tx queue to be empty + * @timeout: timeout in ms + * + * Wait for tx queue to be empty, return timeout error if + * queue doesn't empty before timeout occurs. + * + * Return: + * QDF_STATUS_SUCCESS if the queue empties, + * QDF_STATUS_E_TIMEOUT in case of timeout, + * QDF_STATUS_E_FAULT in case of missing handle + */ +static QDF_STATUS ol_txrx_wait_for_pending_tx(int timeout) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct ol_txrx_pdev_t *txrx_pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return QDF_STATUS_E_FAULT; + } + + txrx_pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!txrx_pdev) { + ol_txrx_err("txrx context is null"); + return QDF_STATUS_E_FAULT; + } + + while (ol_txrx_get_tx_pending((struct cdp_pdev *)txrx_pdev)) { + qdf_sleep(OL_ATH_TX_DRAIN_WAIT_DELAY); + if (timeout <= 0) { + ol_txrx_err("tx frames are pending"); + ol_txrx_dump_tx_desc(txrx_pdev); + return QDF_STATUS_E_TIMEOUT; + } + timeout = timeout - OL_ATH_TX_DRAIN_WAIT_DELAY; + } + return QDF_STATUS_SUCCESS; +} + +#ifndef QCA_WIFI_3_0_EMU +#define SUSPEND_DRAIN_WAIT 500 +#else +#define SUSPEND_DRAIN_WAIT 3000 +#endif + +#ifdef FEATURE_RUNTIME_PM +/** + * ol_txrx_runtime_suspend() - ensure TXRX is ready to runtime suspend + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * + * TXRX is ready to runtime suspend if there are no pending packets + * in the tx queue. + * + * Return: QDF_STATUS + */ +static QDF_STATUS ol_txrx_runtime_suspend(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct cdp_pdev *txrx_pdev = (struct cdp_pdev *) + ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (ol_txrx_get_tx_pending(txrx_pdev)) + return QDF_STATUS_E_BUSY; + else + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_runtime_resume() - ensure TXRX is ready to runtime resume + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * + * This is a dummy function for symmetry. + * + * Return: QDF_STATUS_SUCCESS + */ +static QDF_STATUS ol_txrx_runtime_resume(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * ol_txrx_bus_suspend() - bus suspend + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * + * Ensure that ol_txrx is ready for bus suspend + * + * Return: QDF_STATUS + */ +static QDF_STATUS ol_txrx_bus_suspend(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + return ol_txrx_wait_for_pending_tx(SUSPEND_DRAIN_WAIT); +} + +/** + * ol_txrx_bus_resume() - bus resume + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * + * Dummy function for symetry + * + * Return: QDF_STATUS_SUCCESS + */ +static QDF_STATUS ol_txrx_bus_resume(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_get_tx_pending - Get the number of pending transmit + * frames that are awaiting completion. + * + * @pdev - the data physical device object + * Mainly used in clean up path to make sure all buffers have been freed + * + * Return: count of pending frames + */ +uint32_t ol_txrx_get_tx_pending(struct cdp_pdev *ppdev) +{ + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev; + uint32_t total; + + if (ol_cfg_is_high_latency(pdev->ctrl_pdev)) + total = qdf_atomic_read(&pdev->orig_target_tx_credit); + else + total = ol_tx_get_desc_global_pool_size(pdev); + + return total - ol_tx_get_total_free_desc(pdev); +} + +void ol_txrx_discard_tx_pending(ol_txrx_pdev_handle pdev_handle) +{ + ol_tx_desc_list tx_descs; + /* + * First let hif do the qdf_atomic_dec_and_test(&tx_desc->ref_cnt) + * then let htt do the qdf_atomic_dec_and_test(&tx_desc->ref_cnt) + * which is tha same with normal data send complete path + */ + htt_tx_pending_discard(pdev_handle->htt_pdev); + + TAILQ_INIT(&tx_descs); + ol_tx_queue_discard(pdev_handle, true, &tx_descs); + /* Discard Frames in Discard List */ + ol_tx_desc_frame_list_free(pdev_handle, &tx_descs, 1 /* error */); + + ol_tx_discard_target_frms(pdev_handle); +} + +static inline +uint64_t ol_txrx_stats_ptr_to_u64(struct ol_txrx_stats_req_internal *req) +{ + return (uint64_t) ((size_t) req); +} + +static inline +struct ol_txrx_stats_req_internal *ol_txrx_u64_to_stats_ptr(uint64_t cookie) +{ + return (struct ol_txrx_stats_req_internal *)((size_t) cookie); +} + +#ifdef currently_unused +void +ol_txrx_fw_stats_cfg(ol_txrx_vdev_handle vdev, + uint8_t cfg_stats_type, uint32_t cfg_val) +{ + uint8_t dummy_cookie = 0; + + htt_h2t_dbg_stats_get(vdev->pdev->htt_pdev, 0 /* upload mask */, + 0 /* reset mask */, + cfg_stats_type, cfg_val, dummy_cookie); +} +#endif + +/** + * ol_txrx_fw_stats_desc_pool_init() - Initialize the fw stats descriptor pool + * @pdev: handle to ol txrx pdev + * @pool_size: Size of fw stats descriptor pool + * + * Return: 0 for success, error code on failure. + */ +int ol_txrx_fw_stats_desc_pool_init(struct ol_txrx_pdev_t *pdev, + uint8_t pool_size) +{ + int i; + + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return -EINVAL; + } + pdev->ol_txrx_fw_stats_desc_pool.pool = qdf_mem_malloc(pool_size * + sizeof(struct ol_txrx_fw_stats_desc_elem_t)); + if (!pdev->ol_txrx_fw_stats_desc_pool.pool) + return -ENOMEM; + + pdev->ol_txrx_fw_stats_desc_pool.freelist = + &pdev->ol_txrx_fw_stats_desc_pool.pool[0]; + pdev->ol_txrx_fw_stats_desc_pool.pool_size = pool_size; + + for (i = 0; i < (pool_size - 1); i++) { + pdev->ol_txrx_fw_stats_desc_pool.pool[i].desc.desc_id = i; + pdev->ol_txrx_fw_stats_desc_pool.pool[i].desc.req = NULL; + pdev->ol_txrx_fw_stats_desc_pool.pool[i].next = + &pdev->ol_txrx_fw_stats_desc_pool.pool[i + 1]; + } + pdev->ol_txrx_fw_stats_desc_pool.pool[i].desc.desc_id = i; + pdev->ol_txrx_fw_stats_desc_pool.pool[i].desc.req = NULL; + pdev->ol_txrx_fw_stats_desc_pool.pool[i].next = NULL; + qdf_spinlock_create(&pdev->ol_txrx_fw_stats_desc_pool.pool_lock); + qdf_atomic_init(&pdev->ol_txrx_fw_stats_desc_pool.initialized); + qdf_atomic_set(&pdev->ol_txrx_fw_stats_desc_pool.initialized, 1); + return 0; +} + +/** + * ol_txrx_fw_stats_desc_pool_deinit() - Deinitialize the + * fw stats descriptor pool + * @pdev: handle to ol txrx pdev + * + * Return: None + */ +void ol_txrx_fw_stats_desc_pool_deinit(struct ol_txrx_pdev_t *pdev) +{ + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + if (!qdf_atomic_read(&pdev->ol_txrx_fw_stats_desc_pool.initialized)) { + ol_txrx_err("Pool is not initialized"); + return; + } + if (!pdev->ol_txrx_fw_stats_desc_pool.pool) { + ol_txrx_err("Pool is not allocated"); + return; + } + qdf_spin_lock_bh(&pdev->ol_txrx_fw_stats_desc_pool.pool_lock); + qdf_atomic_set(&pdev->ol_txrx_fw_stats_desc_pool.initialized, 0); + qdf_mem_free(pdev->ol_txrx_fw_stats_desc_pool.pool); + pdev->ol_txrx_fw_stats_desc_pool.pool = NULL; + + pdev->ol_txrx_fw_stats_desc_pool.freelist = NULL; + pdev->ol_txrx_fw_stats_desc_pool.pool_size = 0; + qdf_spin_unlock_bh(&pdev->ol_txrx_fw_stats_desc_pool.pool_lock); +} + +/** + * ol_txrx_fw_stats_desc_alloc() - Get fw stats descriptor from fw stats + * free descriptor pool + * @pdev: handle to ol txrx pdev + * + * Return: pointer to fw stats descriptor, NULL on failure + */ +struct ol_txrx_fw_stats_desc_t + *ol_txrx_fw_stats_desc_alloc(struct ol_txrx_pdev_t *pdev) +{ + struct ol_txrx_fw_stats_desc_t *desc = NULL; + + qdf_spin_lock_bh(&pdev->ol_txrx_fw_stats_desc_pool.pool_lock); + if (!qdf_atomic_read(&pdev->ol_txrx_fw_stats_desc_pool.initialized)) { + qdf_spin_unlock_bh(&pdev-> + ol_txrx_fw_stats_desc_pool.pool_lock); + ol_txrx_err("Pool deinitialized"); + return NULL; + } + if (pdev->ol_txrx_fw_stats_desc_pool.freelist) { + desc = &pdev->ol_txrx_fw_stats_desc_pool.freelist->desc; + pdev->ol_txrx_fw_stats_desc_pool.freelist = + pdev->ol_txrx_fw_stats_desc_pool.freelist->next; + } + qdf_spin_unlock_bh(&pdev->ol_txrx_fw_stats_desc_pool.pool_lock); + + if (desc) + ol_txrx_dbg("desc_id %d allocated", desc->desc_id); + else + ol_txrx_err("fw stats descriptors are exhausted"); + + return desc; +} + +/** + * ol_txrx_fw_stats_desc_get_req() - Put fw stats descriptor + * back into free pool + * @pdev: handle to ol txrx pdev + * @fw_stats_desc: fw_stats_desc_get descriptor + * + * Return: pointer to request + */ +struct ol_txrx_stats_req_internal + *ol_txrx_fw_stats_desc_get_req(struct ol_txrx_pdev_t *pdev, + unsigned char desc_id) +{ + struct ol_txrx_fw_stats_desc_elem_t *desc_elem; + struct ol_txrx_stats_req_internal *req; + + qdf_spin_lock_bh(&pdev->ol_txrx_fw_stats_desc_pool.pool_lock); + if (!qdf_atomic_read(&pdev->ol_txrx_fw_stats_desc_pool.initialized)) { + qdf_spin_unlock_bh(&pdev-> + ol_txrx_fw_stats_desc_pool.pool_lock); + ol_txrx_err("Desc ID %u Pool deinitialized", desc_id); + return NULL; + } + desc_elem = &pdev->ol_txrx_fw_stats_desc_pool.pool[desc_id]; + req = desc_elem->desc.req; + desc_elem->desc.req = NULL; + desc_elem->next = + pdev->ol_txrx_fw_stats_desc_pool.freelist; + pdev->ol_txrx_fw_stats_desc_pool.freelist = desc_elem; + qdf_spin_unlock_bh(&pdev->ol_txrx_fw_stats_desc_pool.pool_lock); + return req; +} + +/** + * ol_txrx_fw_stats_get() - Get fw stats + * + * @soc_hdl: datapath soc handle + * @vdev_id: virtual interface id + * @req: specifications of stats request + * @per_vdev: bool input whether stats requested per vdev or not + * @response_expected: bool input whether expecting response or not + * + * Return: success or failure + */ +static A_STATUS +ol_txrx_fw_stats_get(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + struct ol_txrx_stats_req *req, bool per_vdev, + bool response_expected) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + struct ol_txrx_pdev_t *pdev; + uint8_t cookie = FW_STATS_DESC_POOL_SIZE; + struct ol_txrx_stats_req_internal *non_volatile_req; + struct ol_txrx_fw_stats_desc_t *desc = NULL; + struct ol_txrx_fw_stats_desc_elem_t *elem = NULL; + + if (!vdev) + return A_EINVAL; + + pdev = vdev->pdev; + if (!pdev || + req->stats_type_upload_mask >= 1 << HTT_DBG_NUM_STATS || + req->stats_type_reset_mask >= 1 << HTT_DBG_NUM_STATS) { + return A_EINVAL; + } + + /* + * Allocate a non-transient stats request object. + * (The one provided as an argument is likely allocated on the stack.) + */ + non_volatile_req = qdf_mem_malloc(sizeof(*non_volatile_req)); + if (!non_volatile_req) + return A_NO_MEMORY; + + /* copy the caller's specifications */ + non_volatile_req->base = *req; + non_volatile_req->serviced = 0; + non_volatile_req->offset = 0; + if (response_expected) { + desc = ol_txrx_fw_stats_desc_alloc(pdev); + if (!desc) { + qdf_mem_free(non_volatile_req); + return A_NO_MEMORY; + } + + /* use the desc id as the cookie */ + cookie = desc->desc_id; + desc->req = non_volatile_req; + qdf_spin_lock_bh(&pdev->req_list_spinlock); + TAILQ_INSERT_TAIL(&pdev->req_list, non_volatile_req, req_list_elem); + pdev->req_list_depth++; + qdf_spin_unlock_bh(&pdev->req_list_spinlock); + } + + if (htt_h2t_dbg_stats_get(pdev->htt_pdev, + req->stats_type_upload_mask, + req->stats_type_reset_mask, + HTT_H2T_STATS_REQ_CFG_STAT_TYPE_INVALID, 0, + cookie)) { + if (response_expected) { + qdf_spin_lock_bh(&pdev->req_list_spinlock); + TAILQ_REMOVE(&pdev->req_list, non_volatile_req, + req_list_elem); + pdev->req_list_depth--; + qdf_spin_unlock_bh(&pdev->req_list_spinlock); + if (desc) { + qdf_spin_lock_bh(&pdev->ol_txrx_fw_stats_desc_pool. + pool_lock); + desc->req = NULL; + elem = container_of(desc, + struct ol_txrx_fw_stats_desc_elem_t, + desc); + elem->next = + pdev->ol_txrx_fw_stats_desc_pool.freelist; + pdev->ol_txrx_fw_stats_desc_pool.freelist = elem; + qdf_spin_unlock_bh(&pdev-> + ol_txrx_fw_stats_desc_pool. + pool_lock); + } + } + + qdf_mem_free(non_volatile_req); + return A_ERROR; + } + + if (response_expected == false) + qdf_mem_free(non_volatile_req); + + return A_OK; +} + +void +ol_txrx_fw_stats_handler(ol_txrx_pdev_handle pdev, + uint8_t cookie, uint8_t *stats_info_list) +{ + enum htt_dbg_stats_type type; + enum htt_cmn_dbg_stats_type cmn_type = HTT_DBG_CMN_NUM_STATS_INVALID; + enum htt_dbg_stats_status status; + int length; + uint8_t *stats_data; + struct ol_txrx_stats_req_internal *req, *tmp; + int more = 0; + int found = 0; + + if (cookie >= FW_STATS_DESC_POOL_SIZE) { + ol_txrx_err("Cookie is not valid"); + return; + } + req = ol_txrx_fw_stats_desc_get_req(pdev, (uint8_t)cookie); + if (!req) { + ol_txrx_err("%s: Request not retrieved for cookie %u", __func__, + (uint8_t)cookie); + return; + } + qdf_spin_lock_bh(&pdev->req_list_spinlock); + TAILQ_FOREACH(tmp, &pdev->req_list, req_list_elem) { + if (req == tmp) { + found = 1; + break; + } + } + qdf_spin_unlock_bh(&pdev->req_list_spinlock); + + if (!found) { + ol_txrx_err( + "req(%pK) from firmware can't be found in the list\n", req); + return; + } + + do { + htt_t2h_dbg_stats_hdr_parse(stats_info_list, &type, &status, + &length, &stats_data); + if (status == HTT_DBG_STATS_STATUS_SERIES_DONE) + break; + if (status == HTT_DBG_STATS_STATUS_PRESENT || + status == HTT_DBG_STATS_STATUS_PARTIAL) { + uint8_t *buf; + int bytes = 0; + + if (status == HTT_DBG_STATS_STATUS_PARTIAL) + more = 1; + if (req->base.print.verbose || req->base.print.concise) + /* provide the header along with the data */ + htt_t2h_stats_print(stats_info_list, + req->base.print.concise); + + switch (type) { + case HTT_DBG_STATS_WAL_PDEV_TXRX: + bytes = sizeof(struct wlan_dbg_stats); + if (req->base.copy.buf) { + int lmt; + + lmt = sizeof(struct wlan_dbg_stats); + if (req->base.copy.byte_limit < lmt) + lmt = req->base.copy.byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, lmt); + } + break; + case HTT_DBG_STATS_RX_REORDER: + bytes = sizeof(struct rx_reorder_stats); + if (req->base.copy.buf) { + int lmt; + + lmt = sizeof(struct rx_reorder_stats); + if (req->base.copy.byte_limit < lmt) + lmt = req->base.copy.byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, lmt); + } + break; + case HTT_DBG_STATS_RX_RATE_INFO: + bytes = sizeof(wlan_dbg_rx_rate_info_t); + if (req->base.copy.buf) { + int lmt; + + lmt = sizeof(wlan_dbg_rx_rate_info_t); + if (req->base.copy.byte_limit < lmt) + lmt = req->base.copy.byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, lmt); + } + break; + + case HTT_DBG_STATS_TX_RATE_INFO: + bytes = sizeof(wlan_dbg_tx_rate_info_t); + if (req->base.copy.buf) { + int lmt; + + lmt = sizeof(wlan_dbg_tx_rate_info_t); + if (req->base.copy.byte_limit < lmt) + lmt = req->base.copy.byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, lmt); + } + break; + + case HTT_DBG_STATS_TX_PPDU_LOG: + bytes = 0; + /* TO DO: specify how many bytes are present */ + /* TO DO: add copying to the requestor's buf */ + + case HTT_DBG_STATS_RX_REMOTE_RING_BUFFER_INFO: + bytes = sizeof(struct + rx_remote_buffer_mgmt_stats); + if (req->base.copy.buf) { + int limit; + + limit = sizeof(struct + rx_remote_buffer_mgmt_stats); + if (req->base.copy.byte_limit < limit) + limit = req->base.copy. + byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, limit); + } + break; + + case HTT_DBG_STATS_TXBF_INFO: + bytes = sizeof(struct wlan_dbg_txbf_data_stats); + if (req->base.copy.buf) { + int limit; + + limit = sizeof(struct + wlan_dbg_txbf_data_stats); + if (req->base.copy.byte_limit < limit) + limit = req->base.copy. + byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, limit); + } + break; + + case HTT_DBG_STATS_SND_INFO: + bytes = sizeof(struct wlan_dbg_txbf_snd_stats); + if (req->base.copy.buf) { + int limit; + + limit = sizeof(struct + wlan_dbg_txbf_snd_stats); + if (req->base.copy.byte_limit < limit) + limit = req->base.copy. + byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, limit); + } + break; + + case HTT_DBG_STATS_TX_SELFGEN_INFO: + bytes = sizeof(struct + wlan_dbg_tx_selfgen_stats); + if (req->base.copy.buf) { + int limit; + + limit = sizeof(struct + wlan_dbg_tx_selfgen_stats); + if (req->base.copy.byte_limit < limit) + limit = req->base.copy. + byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, limit); + } + break; + + case HTT_DBG_STATS_ERROR_INFO: + bytes = + sizeof(struct wlan_dbg_wifi2_error_stats); + if (req->base.copy.buf) { + int limit; + + limit = sizeof(struct + wlan_dbg_wifi2_error_stats); + if (req->base.copy.byte_limit < limit) + limit = req->base.copy. + byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, limit); + } + break; + + case HTT_DBG_STATS_TXBF_MUSU_NDPA_PKT: + bytes = + sizeof(struct rx_txbf_musu_ndpa_pkts_stats); + if (req->base.copy.buf) { + int limit; + + limit = sizeof(struct + rx_txbf_musu_ndpa_pkts_stats); + if (req->base.copy.byte_limit < limit) + limit = + req->base.copy.byte_limit; + buf = req->base.copy.buf + req->offset; + qdf_mem_copy(buf, stats_data, limit); + } + break; + + default: + break; + } + buf = req->base.copy.buf ? + req->base.copy.buf : stats_data; + + /* Not implemented for MCL */ + if (req->base.callback.fp) + req->base.callback.fp(req->base.callback.ctxt, + cmn_type, buf, bytes); + } + stats_info_list += length; + } while (1); + + if (!more) { + qdf_spin_lock_bh(&pdev->req_list_spinlock); + TAILQ_FOREACH(tmp, &pdev->req_list, req_list_elem) { + if (req == tmp) { + TAILQ_REMOVE(&pdev->req_list, req, req_list_elem); + pdev->req_list_depth--; + qdf_mem_free(req); + break; + } + } + qdf_spin_unlock_bh(&pdev->req_list_spinlock); + } +} + +#ifndef ATH_PERF_PWR_OFFLOAD /*---------------------------------------------*/ +int ol_txrx_debug(ol_txrx_vdev_handle vdev, int debug_specs) +{ + if (debug_specs & TXRX_DBG_MASK_OBJS) { +#if defined(TXRX_DEBUG_LEVEL) && TXRX_DEBUG_LEVEL > 5 + ol_txrx_pdev_display(vdev->pdev, 0); +#else + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_FATAL, + "The pdev,vdev,peer display functions are disabled.\n To enable them, recompile with TXRX_DEBUG_LEVEL > 5"); +#endif + } + if (debug_specs & TXRX_DBG_MASK_STATS) + ol_txrx_stats_display(vdev->pdev, + QDF_STATS_VERBOSITY_LEVEL_HIGH); + if (debug_specs & TXRX_DBG_MASK_PROT_ANALYZE) { +#if defined(ENABLE_TXRX_PROT_ANALYZE) + ol_txrx_prot_ans_display(vdev->pdev); +#else + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_FATAL, + "txrx protocol analysis is disabled.\n To enable it, recompile with ENABLE_TXRX_PROT_ANALYZE defined"); +#endif + } + if (debug_specs & TXRX_DBG_MASK_RX_REORDER_TRACE) { +#if defined(ENABLE_RX_REORDER_TRACE) + ol_rx_reorder_trace_display(vdev->pdev, 0, 0); +#else + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_FATAL, + "rx reorder seq num trace is disabled.\n To enable it, recompile with ENABLE_RX_REORDER_TRACE defined"); +#endif + + } + return 0; +} +#endif + +#ifdef currently_unused +int ol_txrx_aggr_cfg(ol_txrx_vdev_handle vdev, + int max_subfrms_ampdu, int max_subfrms_amsdu) +{ + return htt_h2t_aggr_cfg_msg(vdev->pdev->htt_pdev, + max_subfrms_ampdu, max_subfrms_amsdu); +} +#endif + +#if defined(TXRX_DEBUG_LEVEL) && TXRX_DEBUG_LEVEL > 5 +void ol_txrx_pdev_display(ol_txrx_pdev_handle pdev, int indent) +{ + struct ol_txrx_vdev_t *vdev; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*s%s:\n", indent, " ", "txrx pdev"); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*spdev object: %pK", indent + 4, " ", pdev); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*svdev list:", indent + 4, " "); + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + ol_txrx_vdev_display(vdev, indent + 8); + } + ol_txrx_peer_find_display(pdev, indent + 4); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*stx desc pool: %d elems @ %pK", indent + 4, " ", + pdev->tx_desc.pool_size, pdev->tx_desc.array); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, " "); + htt_display(pdev->htt_pdev, indent); +} + +void ol_txrx_vdev_display(ol_txrx_vdev_handle vdev, int indent) +{ + struct ol_txrx_peer_t *peer; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*stxrx vdev: %pK\n", indent, " ", vdev); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*sID: %d\n", indent + 4, " ", vdev->vdev_id); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*sMAC addr: %d:%d:%d:%d:%d:%d", + indent + 4, " ", + vdev->mac_addr.raw[0], vdev->mac_addr.raw[1], + vdev->mac_addr.raw[2], vdev->mac_addr.raw[3], + vdev->mac_addr.raw[4], vdev->mac_addr.raw[5]); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*speer list:", indent + 4, " "); + TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { + ol_txrx_peer_display(peer, indent + 8); + } +} + +void ol_txrx_peer_display(ol_txrx_peer_handle peer, int indent) +{ + int i; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*stxrx peer: %pK", indent, " ", peer); + for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) { + if (peer->peer_ids[i] != HTT_INVALID_PEER) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*sID: %d", indent + 4, " ", + peer->peer_ids[i]); + } + } +} +#endif /* TXRX_DEBUG_LEVEL */ + +/** + * ol_txrx_stats() - update ol layer stats + * @vdev_id: vdev_id + * @buffer: pointer to buffer + * @buf_len: length of the buffer + * + * Return: length of string + */ +static int +ol_txrx_stats(uint8_t vdev_id, char *buffer, unsigned int buf_len) +{ + uint32_t len = 0; + + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *) + ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: vdev is NULL", __func__); + snprintf(buffer, buf_len, "vdev not found"); + return len; + } + + len = scnprintf(buffer, buf_len, + "\n\nTXRX stats:\nllQueue State : %s\npause %u unpause %u\noverflow %u\nllQueue timer state : %s", + ((vdev->ll_pause.is_q_paused == false) ? + "UNPAUSED" : "PAUSED"), + vdev->ll_pause.q_pause_cnt, + vdev->ll_pause.q_unpause_cnt, + vdev->ll_pause.q_overflow_cnt, + ((vdev->ll_pause.is_q_timer_on == false) + ? "NOT-RUNNING" : "RUNNING")); + return len; +} + +#ifdef QCA_SUPPORT_TXRX_LOCAL_PEER_ID +/** + * ol_txrx_disp_peer_cached_bufq_stats() - display peer cached_bufq stats + * @peer: peer pointer + * + * Return: None + */ +static void ol_txrx_disp_peer_cached_bufq_stats(struct ol_txrx_peer_t *peer) +{ + txrx_nofl_info("cached_bufq: curr %d drops %d hwm %d whatifs %d thresh %d", + peer->bufq_info.curr, + peer->bufq_info.dropped, + peer->bufq_info.high_water_mark, + peer->bufq_info.qdepth_no_thresh, + peer->bufq_info.thresh); +} + +/** + * ol_txrx_disp_peer_stats() - display peer stats + * @pdev: pdev pointer + * + * Return: None + */ +static void ol_txrx_disp_peer_stats(ol_txrx_pdev_handle pdev) +{ int i; + struct ol_txrx_peer_t *peer; + struct hif_opaque_softc *osc = cds_get_context(QDF_MODULE_ID_HIF); + + if (osc && hif_is_load_or_unload_in_progress(HIF_GET_SOFTC(osc))) + return; + + for (i = 0; i < OL_TXRX_NUM_LOCAL_PEER_IDS; i++) { + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + qdf_spin_lock_bh(&pdev->local_peer_ids.lock); + peer = pdev->local_peer_ids.map[i]; + if (peer) { + ol_txrx_peer_get_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); + } + qdf_spin_unlock_bh(&pdev->local_peer_ids.lock); + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + + if (peer) { + txrx_nofl_info("stats: peer 0x%pK local peer id %d", + peer, i); + ol_txrx_disp_peer_cached_bufq_stats(peer); + ol_txrx_peer_release_ref(peer, + PEER_DEBUG_ID_OL_INTERNAL); + } + } +} +#else +static void ol_txrx_disp_peer_stats(ol_txrx_pdev_handle pdev) +{ + txrx_nofl_info("peer stats not supported w/o QCA_SUPPORT_TXRX_LOCAL_PEER_ID"); +} +#endif + +void ol_txrx_stats_display(ol_txrx_pdev_handle pdev, + enum qdf_stats_verbosity_level level) +{ + u64 tx_dropped = + pdev->stats.pub.tx.dropped.download_fail.pkts + + pdev->stats.pub.tx.dropped.target_discard.pkts + + pdev->stats.pub.tx.dropped.no_ack.pkts + + pdev->stats.pub.tx.dropped.target_drop.pkts + + pdev->stats.pub.tx.dropped.others.pkts; + + if (level == QDF_STATS_VERBOSITY_LEVEL_LOW) { + txrx_nofl_dbg("STATS |%u %u|TX: %lld tso %lld ok %lld drops(%u-%lld %u-%lld %u-%lld %u-%lld ?-%lld hR-%lld)|RX: %lld drops(E %lld PI %lld ME %lld) fwd(S %d F %d SF %d)|", + pdev->tx_desc.num_free, + pdev->tx_desc.pool_size, + pdev->stats.pub.tx.from_stack.pkts, + pdev->stats.pub.tx.tso.tso_pkts.pkts, + pdev->stats.pub.tx.delivered.pkts, + htt_tx_status_download_fail, + pdev->stats.pub.tx.dropped.download_fail.pkts, + htt_tx_status_discard, + pdev->stats.pub.tx.dropped. + target_discard.pkts, + htt_tx_status_no_ack, + pdev->stats.pub.tx.dropped.no_ack.pkts, + htt_tx_status_drop, + pdev->stats.pub.tx.dropped.target_drop.pkts, + pdev->stats.pub.tx.dropped.others.pkts, + pdev->stats.pub.tx.dropped.host_reject.pkts, + pdev->stats.pub.rx.delivered.pkts, + pdev->stats.pub.rx.dropped_err.pkts, + pdev->stats.pub.rx.dropped_peer_invalid.pkts, + pdev->stats.pub.rx.dropped_mic_err.pkts, + pdev->stats.pub.rx.intra_bss_fwd. + packets_stack, + pdev->stats.pub.rx.intra_bss_fwd. + packets_fwd, + pdev->stats.pub.rx.intra_bss_fwd. + packets_stack_n_fwd); + return; + } + + txrx_nofl_info("TX PATH Statistics:"); + txrx_nofl_info("sent %lld msdus (%lld B), host rejected %lld (%lld B), dropped %lld (%lld B)", + pdev->stats.pub.tx.from_stack.pkts, + pdev->stats.pub.tx.from_stack.bytes, + pdev->stats.pub.tx.dropped.host_reject.pkts, + pdev->stats.pub.tx.dropped.host_reject.bytes, + tx_dropped, + pdev->stats.pub.tx.dropped.download_fail.bytes + + pdev->stats.pub.tx.dropped.target_discard.bytes + + pdev->stats.pub.tx.dropped.target_drop.bytes + + pdev->stats.pub.tx.dropped.no_ack.bytes); + txrx_nofl_info("successfully delivered: %lld (%lld B), download fail: %lld (%lld B), target discard: %lld (%lld B), no ack: %lld (%lld B),target drop: %lld (%lld B), others: %lld (%lld B)", + pdev->stats.pub.tx.delivered.pkts, + pdev->stats.pub.tx.delivered.bytes, + pdev->stats.pub.tx.dropped.download_fail.pkts, + pdev->stats.pub.tx.dropped.download_fail.bytes, + pdev->stats.pub.tx.dropped.target_discard.pkts, + pdev->stats.pub.tx.dropped.target_discard.bytes, + pdev->stats.pub.tx.dropped.no_ack.pkts, + pdev->stats.pub.tx.dropped.no_ack.bytes, + pdev->stats.pub.tx.dropped.target_drop.pkts, + pdev->stats.pub.tx.dropped.target_drop.bytes, + pdev->stats.pub.tx.dropped.others.pkts, + pdev->stats.pub.tx.dropped.others.bytes); + txrx_nofl_info("Tx completions per HTT message:\n" + "Single Packet %d\n" + " 2-10 Packets %d\n" + "11-20 Packets %d\n" + "21-30 Packets %d\n" + "31-40 Packets %d\n" + "41-50 Packets %d\n" + "51-60 Packets %d\n" + " 60+ Packets %d\n", + pdev->stats.pub.tx.comp_histogram.pkts_1, + pdev->stats.pub.tx.comp_histogram.pkts_2_10, + pdev->stats.pub.tx.comp_histogram.pkts_11_20, + pdev->stats.pub.tx.comp_histogram.pkts_21_30, + pdev->stats.pub.tx.comp_histogram.pkts_31_40, + pdev->stats.pub.tx.comp_histogram.pkts_41_50, + pdev->stats.pub.tx.comp_histogram.pkts_51_60, + pdev->stats.pub.tx.comp_histogram.pkts_61_plus); + + txrx_nofl_info("RX PATH Statistics:"); + txrx_nofl_info("%lld ppdus, %lld mpdus, %lld msdus, %lld bytes\n" + "dropped: err %lld (%lld B), peer_invalid %lld (%lld B), mic_err %lld (%lld B)\n" + "msdus with frag_ind: %d msdus with offload_ind: %d", + pdev->stats.priv.rx.normal.ppdus, + pdev->stats.priv.rx.normal.mpdus, + pdev->stats.pub.rx.delivered.pkts, + pdev->stats.pub.rx.delivered.bytes, + pdev->stats.pub.rx.dropped_err.pkts, + pdev->stats.pub.rx.dropped_err.bytes, + pdev->stats.pub.rx.dropped_peer_invalid.pkts, + pdev->stats.pub.rx.dropped_peer_invalid.bytes, + pdev->stats.pub.rx.dropped_mic_err.pkts, + pdev->stats.pub.rx.dropped_mic_err.bytes, + pdev->stats.pub.rx.msdus_with_frag_ind, + pdev->stats.pub.rx.msdus_with_offload_ind); + + txrx_nofl_info(" fwd to stack %d, fwd to fw %d, fwd to stack & fw %d\n", + pdev->stats.pub.rx.intra_bss_fwd.packets_stack, + pdev->stats.pub.rx.intra_bss_fwd.packets_fwd, + pdev->stats.pub.rx.intra_bss_fwd.packets_stack_n_fwd); + + txrx_nofl_info("packets per HTT message:\n" + "Single Packet %d\n" + " 2-10 Packets %d\n" + "11-20 Packets %d\n" + "21-30 Packets %d\n" + "31-40 Packets %d\n" + "41-50 Packets %d\n" + "51-60 Packets %d\n" + " 60+ Packets %d\n", + pdev->stats.pub.rx.rx_ind_histogram.pkts_1, + pdev->stats.pub.rx.rx_ind_histogram.pkts_2_10, + pdev->stats.pub.rx.rx_ind_histogram.pkts_11_20, + pdev->stats.pub.rx.rx_ind_histogram.pkts_21_30, + pdev->stats.pub.rx.rx_ind_histogram.pkts_31_40, + pdev->stats.pub.rx.rx_ind_histogram.pkts_41_50, + pdev->stats.pub.rx.rx_ind_histogram.pkts_51_60, + pdev->stats.pub.rx.rx_ind_histogram.pkts_61_plus); + + ol_txrx_disp_peer_stats(pdev); +} + +void ol_txrx_stats_clear(ol_txrx_pdev_handle pdev) +{ + qdf_mem_zero(&pdev->stats, sizeof(pdev->stats)); +} + +#if defined(ENABLE_TXRX_PROT_ANALYZE) + +void ol_txrx_prot_ans_display(ol_txrx_pdev_handle pdev) +{ + ol_txrx_prot_an_display(pdev->prot_an_tx_sent); + ol_txrx_prot_an_display(pdev->prot_an_rx_sent); +} + +#endif /* ENABLE_TXRX_PROT_ANALYZE */ + +#ifdef QCA_SUPPORT_PEER_DATA_RX_RSSI +int16_t ol_txrx_peer_rssi(ol_txrx_peer_handle peer) +{ + return (peer->rssi_dbm == HTT_RSSI_INVALID) ? + OL_TXRX_RSSI_INVALID : peer->rssi_dbm; +} +#endif /* #ifdef QCA_SUPPORT_PEER_DATA_RX_RSSI */ + +#ifdef QCA_ENABLE_OL_TXRX_PEER_STATS +A_STATUS +ol_txrx_peer_stats_copy(ol_txrx_pdev_handle pdev, + ol_txrx_peer_handle peer, ol_txrx_peer_stats_t *stats) +{ + qdf_assert(pdev && peer && stats); + qdf_spin_lock_bh(&pdev->peer_stat_mutex); + qdf_mem_copy(stats, &peer->stats, sizeof(*stats)); + qdf_spin_unlock_bh(&pdev->peer_stat_mutex); + return A_OK; +} +#endif /* QCA_ENABLE_OL_TXRX_PEER_STATS */ + +/** + * ol_vdev_rx_set_intrabss_fwd() - Get fw stats + * + * @soc_hdl: datapath soc handle + * @vdev_id: virtual interface id + * @val: enable or disable + * + * Return: void + */ +static void ol_vdev_rx_set_intrabss_fwd(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, bool val) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_vdev_handle vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, + vdev_id); + + if (!vdev) + return; + + vdev->disable_intrabss_fwd = val; +} + +/** + * ol_txrx_update_mac_id() - update mac_id for vdev + * @soc_hdl: Datapath soc handle + * @vdev_id: vdev id + * @mac_id: mac id + * + * Return: none + */ +static void ol_txrx_update_mac_id(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint8_t mac_id) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *) + ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid vdev_id %d", __func__, vdev_id); + return; + } + vdev->mac_id = mac_id; +} + +/** + * ol_txrx_get_tx_ack_count() - get tx ack count + * @soc_hdl: Datapath soc handle + * @vdev_id: vdev_id + * + * Return: tx ack count + */ +static uint32_t ol_txrx_get_tx_ack_stats(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid vdev_id %d", __func__, vdev_id); + return 0; + } + + return vdev->txrx_stats.txack_success; +} + +/** + * ol_txrx_display_stats() - Display OL TXRX display stats + * @soc_hdl: Datapath soc handle + * @value: Module id for which stats needs to be displayed + * @verb_level: verbose level of stats to be displayed + * + * Return: status + */ +static QDF_STATUS +ol_txrx_display_stats(struct cdp_soc_t *soc_hdl, uint16_t value, + enum qdf_stats_verbosity_level verb_level) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id( + soc, + OL_TXRX_PDEV_ID); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: pdev is NULL", __func__); + return QDF_STATUS_E_NULL_VALUE; + } + + switch (value) { + case CDP_TXRX_PATH_STATS: + ol_txrx_stats_display(pdev, verb_level); + break; + case CDP_TXRX_TSO_STATS: + ol_txrx_stats_display_tso(pdev); + break; + case CDP_DUMP_TX_FLOW_POOL_INFO: + if (verb_level == QDF_STATS_VERBOSITY_LEVEL_LOW) + ol_tx_dump_flow_pool_info_compact(pdev); + else + ol_tx_dump_flow_pool_info(soc_hdl); + break; + case CDP_TXRX_DESC_STATS: + qdf_nbuf_tx_desc_count_display(); + break; + case CDP_WLAN_RX_BUF_DEBUG_STATS: + htt_display_rx_buf_debug(pdev->htt_pdev); + break; +#ifdef CONFIG_HL_SUPPORT + case CDP_SCHEDULER_STATS: + ol_tx_sched_cur_state_display(pdev); + ol_tx_sched_stats_display(pdev); + break; + case CDP_TX_QUEUE_STATS: + ol_tx_queue_log_display(pdev); + break; +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + case CDP_CREDIT_STATS: + ol_tx_dump_group_credit_stats(pdev); + break; +#endif + +#ifdef DEBUG_HL_LOGGING + case CDP_BUNDLE_STATS: + htt_dump_bundle_stats(pdev->htt_pdev); + break; +#endif +#endif + default: + status = QDF_STATUS_E_INVAL; + break; + } + return status; +} + +/** + * ol_txrx_clear_stats() - Clear OL TXRX stats + * @soc - ol soc handle + * @pdev_id: pdev identifier + * @value - Module id for which stats needs to be cleared + * + * Return: 0 - success/ non-zero failure + */ +static QDF_STATUS ol_txrx_clear_stats(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, uint8_t value) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: pdev is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + switch (value) { + case CDP_TXRX_PATH_STATS: + ol_txrx_stats_clear(pdev); + break; + case CDP_TXRX_TSO_STATS: + ol_txrx_tso_stats_clear(pdev); + break; + case CDP_DUMP_TX_FLOW_POOL_INFO: + ol_tx_clear_flow_pool_stats(); + break; + case CDP_TXRX_DESC_STATS: + qdf_nbuf_tx_desc_count_clear(); + break; +#ifdef CONFIG_HL_SUPPORT + case CDP_SCHEDULER_STATS: + ol_tx_sched_stats_clear(pdev); + break; + case CDP_TX_QUEUE_STATS: + ol_tx_queue_log_clear(pdev); + break; +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + case CDP_CREDIT_STATS: + ol_tx_clear_group_credit_stats(pdev); + break; +#endif + case CDP_BUNDLE_STATS: + htt_clear_bundle_stats(pdev->htt_pdev); + break; +#endif + default: + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +/** + * ol_txrx_drop_nbuf_list() - drop an nbuf list + * @buf_list: buffer list to be dropepd + * + * Return: int (number of bufs dropped) + */ +static inline int ol_txrx_drop_nbuf_list(qdf_nbuf_t buf_list) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + int num_dropped = 0; + qdf_nbuf_t buf, next_buf; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return 0; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return 0; + } + + buf = buf_list; + while (buf) { + QDF_NBUF_CB_RX_PEER_CACHED_FRM(buf) = 1; + next_buf = qdf_nbuf_queue_next(buf); + if (pdev) + TXRX_STATS_MSDU_INCR(pdev, + rx.dropped_peer_invalid, buf); + qdf_nbuf_free(buf); + buf = next_buf; + num_dropped++; + } + return num_dropped; +} + +/** + * ol_rx_data_handler() - data rx handler + * @pdev: dev handle + * @buf_list: buffer list + * @staid: Station id + * + * Return: None + */ +static void ol_rx_data_handler(struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t buf_list, uint16_t staid) +{ + void *osif_dev; + uint8_t drop_count = 0; + qdf_nbuf_t buf, next_buf; + QDF_STATUS ret; + ol_txrx_rx_fp data_rx = NULL; + struct ol_txrx_peer_t *peer; + + if (qdf_unlikely(!pdev)) + goto free_buf; + + /* Do not use peer directly. Derive peer from staid to + * make sure that peer is valid. + */ + peer = ol_txrx_peer_get_ref_by_local_id((struct cdp_pdev *)pdev, + staid, PEER_DEBUG_ID_OL_RX_THREAD); + if (!peer) + goto free_buf; + + qdf_spin_lock_bh(&peer->peer_info_lock); + if (qdf_unlikely(!(peer->state >= OL_TXRX_PEER_STATE_CONN) || + !peer->vdev->rx)) { + qdf_spin_unlock_bh(&peer->peer_info_lock); + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_RX_THREAD); + goto free_buf; + } + + data_rx = peer->vdev->rx; + osif_dev = peer->vdev->osif_dev; + qdf_spin_unlock_bh(&peer->peer_info_lock); + + qdf_spin_lock_bh(&peer->bufq_info.bufq_lock); + if (!list_empty(&peer->bufq_info.cached_bufq)) { + qdf_spin_unlock_bh(&peer->bufq_info.bufq_lock); + /* Flush the cached frames to HDD before passing new rx frame */ + ol_txrx_flush_rx_frames(peer, 0); + } else + qdf_spin_unlock_bh(&peer->bufq_info.bufq_lock); + + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_RX_THREAD); + + buf = buf_list; + while (buf) { + next_buf = qdf_nbuf_queue_next(buf); + qdf_nbuf_set_next(buf, NULL); /* Add NULL terminator */ + ret = data_rx(osif_dev, buf); + if (ret != QDF_STATUS_SUCCESS) { + ol_txrx_err("Frame Rx to HDD failed"); + if (pdev) + TXRX_STATS_MSDU_INCR(pdev, rx.dropped_err, buf); + qdf_nbuf_free(buf); + } + buf = next_buf; + } + return; + +free_buf: + drop_count = ol_txrx_drop_nbuf_list(buf_list); + ol_txrx_warn("Dropped frames %u", drop_count); +} + +/** + * ol_rx_data_cb() - data rx callback + * @context: dev handle + * @buf_list: buffer list + * @staid: Station id + * + * Return: None + */ +static inline void +ol_rx_data_cb(void *context, qdf_nbuf_t buf_list, uint16_t staid) +{ + struct ol_txrx_pdev_t *pdev = context; + + ol_rx_data_handler(pdev, buf_list, staid); +} + +/* print for every 16th packet */ +#define OL_TXRX_PRINT_RATE_LIMIT_THRESH 0x0f +struct ol_rx_cached_buf *cache_buf; + +/** helper function to drop packets + * Note: caller must hold the cached buq lock before invoking + * this function. Also, it assumes that the pointers passed in + * are valid (non-NULL) + */ +static inline void ol_txrx_drop_frames( + struct ol_txrx_cached_bufq_t *bufqi, + qdf_nbuf_t rx_buf_list) +{ + uint32_t dropped = ol_txrx_drop_nbuf_list(rx_buf_list); + + bufqi->dropped += dropped; + bufqi->qdepth_no_thresh += dropped; + + if (bufqi->qdepth_no_thresh > bufqi->high_water_mark) + bufqi->high_water_mark = bufqi->qdepth_no_thresh; +} + +static QDF_STATUS ol_txrx_enqueue_rx_frames( + struct ol_txrx_peer_t *peer, + struct ol_txrx_cached_bufq_t *bufqi, + qdf_nbuf_t rx_buf_list) +{ + struct ol_rx_cached_buf *cache_buf; + qdf_nbuf_t buf, next_buf; + static uint32_t count; + + if ((count++ & OL_TXRX_PRINT_RATE_LIMIT_THRESH) == 0) + ol_txrx_info_high( + "Data on the peer before it is registered bufq->curr %d bufq->drops %d", + bufqi->curr, bufqi->dropped); + + qdf_spin_lock_bh(&bufqi->bufq_lock); + if (bufqi->curr >= bufqi->thresh) { + ol_txrx_drop_frames(bufqi, rx_buf_list); + qdf_spin_unlock_bh(&bufqi->bufq_lock); + return QDF_STATUS_E_FAULT; + } + qdf_spin_unlock_bh(&bufqi->bufq_lock); + + buf = rx_buf_list; + while (buf) { + QDF_NBUF_CB_RX_PEER_CACHED_FRM(buf) = 1; + next_buf = qdf_nbuf_queue_next(buf); + cache_buf = qdf_mem_malloc(sizeof(*cache_buf)); + if (!cache_buf) { + qdf_nbuf_free(buf); + } else { + /* Add NULL terminator */ + qdf_nbuf_set_next(buf, NULL); + cache_buf->buf = buf; + if (peer && peer->valid) { + qdf_spin_lock_bh(&bufqi->bufq_lock); + list_add_tail(&cache_buf->list, + &bufqi->cached_bufq); + bufqi->curr++; + qdf_spin_unlock_bh(&bufqi->bufq_lock); + } else { + qdf_mem_free(cache_buf); + rx_buf_list = buf; + qdf_nbuf_set_next(rx_buf_list, next_buf); + qdf_spin_lock_bh(&bufqi->bufq_lock); + ol_txrx_drop_frames(bufqi, rx_buf_list); + qdf_spin_unlock_bh(&bufqi->bufq_lock); + return QDF_STATUS_E_FAULT; + } + } + buf = next_buf; + } + return QDF_STATUS_SUCCESS; +} +/** + * ol_rx_data_process() - process rx frame + * @peer: peer + * @rx_buf_list: rx buffer list + * + * Return: None + */ +void ol_rx_data_process(struct ol_txrx_peer_t *peer, + qdf_nbuf_t rx_buf_list) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + /* + * Firmware data path active response will use shim RX thread + * T2H MSG running on SIRQ context, + * IPA kernel module API should not be called on SIRQ CTXT + */ + ol_txrx_rx_fp data_rx = NULL; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + goto drop_rx_buf; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if ((!peer) || (!pdev)) { + ol_txrx_err("peer/pdev is NULL"); + goto drop_rx_buf; + } + + qdf_assert(peer->vdev); + + qdf_spin_lock_bh(&peer->peer_info_lock); + if (peer->state >= OL_TXRX_PEER_STATE_CONN) + data_rx = peer->vdev->rx; + qdf_spin_unlock_bh(&peer->peer_info_lock); + + /* + * If there is a data frame from peer before the peer is + * registered for data service, enqueue them on to pending queue + * which will be flushed to HDD once that station is registered. + */ + if (!data_rx) { + if (ol_txrx_enqueue_rx_frames(peer, &peer->bufq_info, + rx_buf_list) + != QDF_STATUS_SUCCESS) + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: failed to enqueue rx frm to cached_bufq", + __func__); + } else { +#ifdef QCA_CONFIG_SMP + /* + * If the kernel is SMP, schedule rx thread to + * better use multicores. + */ + if (!ol_cfg_is_rx_thread_enabled(pdev->ctrl_pdev)) { + ol_rx_data_handler(pdev, rx_buf_list, peer->local_id); + } else { + p_cds_sched_context sched_ctx = + get_cds_sched_ctxt(); + struct cds_ol_rx_pkt *pkt; + + if (unlikely(!sched_ctx)) + goto drop_rx_buf; + + pkt = cds_alloc_ol_rx_pkt(sched_ctx); + if (!pkt) + goto drop_rx_buf; + + pkt->callback = ol_rx_data_cb; + pkt->context = pdev; + pkt->Rxpkt = rx_buf_list; + pkt->staId = peer->local_id; + cds_indicate_rxpkt(sched_ctx, pkt); + } +#else /* QCA_CONFIG_SMP */ + ol_rx_data_handler(pdev, rx_buf_list, peer->local_id); +#endif /* QCA_CONFIG_SMP */ + } + + return; + +drop_rx_buf: + ol_txrx_drop_nbuf_list(rx_buf_list); +} + +/** + * ol_txrx_register_peer() - register peer + * @sta_desc: sta descriptor + * + * Return: QDF Status + */ +static QDF_STATUS ol_txrx_register_peer(struct ol_txrx_desc_type *sta_desc) +{ + struct ol_txrx_peer_t *peer; + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev = + ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + union ol_txrx_peer_update_param_t param; + struct privacy_exemption privacy_filter; + + if (!pdev) { + ol_txrx_err("Pdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + peer = ol_txrx_find_peer_by_addr((struct cdp_pdev *)pdev, + sta_desc->peer_addr.bytes); + + if (!peer) + return QDF_STATUS_E_FAULT; + + qdf_spin_lock_bh(&peer->peer_info_lock); + peer->state = OL_TXRX_PEER_STATE_CONN; + qdf_spin_unlock_bh(&peer->peer_info_lock); + + param.qos_capable = sta_desc->is_qos_enabled; + ol_txrx_peer_update(peer->vdev, peer->mac_addr.raw, ¶m, + ol_txrx_peer_update_qos_capable); + + if (sta_desc->is_wapi_supported) { + /*Privacy filter to accept unencrypted WAI frames */ + privacy_filter.ether_type = ETHERTYPE_WAI; + privacy_filter.filter_type = PRIVACY_FILTER_ALWAYS; + privacy_filter.packet_type = PRIVACY_FILTER_PACKET_BOTH; + ol_txrx_set_privacy_filters(peer->vdev, &privacy_filter, 1); + } + + ol_txrx_flush_rx_frames(peer, 0); + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_register_ocb_peer - Function to register the OCB peer + * @mac_addr: MAC address of the self peer + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE on failure + */ +static QDF_STATUS ol_txrx_register_ocb_peer(uint8_t *mac_addr) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev = + ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + ol_txrx_peer_handle peer; + + if (!pdev || !soc) { + ol_txrx_err("Unable to find pdev or soc!"); + return QDF_STATUS_E_FAILURE; + } + + peer = ol_txrx_find_peer_by_addr((struct cdp_pdev *)pdev, + mac_addr); + if (!peer) { + ol_txrx_err("Unable to find OCB peer!"); + return QDF_STATUS_E_FAILURE; + } + + ol_txrx_set_ocb_peer(pdev, peer); + + /* Set peer state to connected */ + ol_txrx_peer_state_update((struct cdp_soc_t *)soc, peer->mac_addr.raw, + OL_TXRX_PEER_STATE_AUTH); + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_set_ocb_peer - Function to store the OCB peer + * @pdev: Handle to the HTT instance + * @peer: Pointer to the peer + */ +void ol_txrx_set_ocb_peer(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + if (!pdev) + return; + + pdev->ocb_peer = peer; + pdev->ocb_peer_valid = (NULL != peer); +} + +/** + * ol_txrx_get_ocb_peer - Function to retrieve the OCB peer + * @pdev: Handle to the HTT instance + * @peer: Pointer to the returned peer + * + * Return: true if the peer is valid, false if not + */ +bool ol_txrx_get_ocb_peer(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t **peer) +{ + int rc; + + if ((!pdev) || (!peer)) { + rc = false; + goto exit; + } + + if (pdev->ocb_peer_valid) { + *peer = pdev->ocb_peer; + rc = true; + } else { + rc = false; + } + +exit: + return rc; +} + +/** + * ol_txrx_register_pause_cb() - register pause callback + * @soc_hdl: Datapath soc handle + * @pause_cb: pause callback + * + * Return: QDF status + */ +static QDF_STATUS ol_txrx_register_pause_cb(struct cdp_soc_t *soc_hdl, + tx_pause_callback pause_cb) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev; + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev || !pause_cb) { + ol_txrx_err("pdev or pause_cb is NULL"); + return QDF_STATUS_E_INVAL; + } + pdev->pause_cb = pause_cb; + return QDF_STATUS_SUCCESS; +} + +#ifdef RECEIVE_OFFLOAD +/** + * ol_txrx_offld_flush_handler() - offld flush handler + * @context: dev handle + * @rxpkt: rx data + * @staid: station id + * + * This function handles an offld flush indication. + * If the rx thread is enabled, it will be invoked by the rx + * thread else it will be called in the tasklet context + * + * Return: none + */ +static void ol_txrx_offld_flush_handler(void *context, + qdf_nbuf_t rxpkt, + uint16_t staid) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("Invalid soc context"); + qdf_assert(0); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (qdf_unlikely(!pdev)) { + ol_txrx_err("Invalid pdev context"); + qdf_assert(0); + return; + } + + if (pdev->offld_flush_cb) + pdev->offld_flush_cb(context); + else + ol_txrx_err("offld_flush_cb NULL"); +} + +/** + * ol_txrx_offld_flush() - offld flush callback + * @data: opaque data pointer + * + * This is the callback registered with CE to trigger + * an offld flush + * + * Return: none + */ +static void ol_txrx_offld_flush(void *data) +{ + p_cds_sched_context sched_ctx = get_cds_sched_ctxt(); + struct cds_ol_rx_pkt *pkt; + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!sched_ctx)) + return; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (qdf_unlikely(!pdev)) { + ol_txrx_err("TXRX module context is NULL"); + return; + } + + if (!ol_cfg_is_rx_thread_enabled(pdev->ctrl_pdev)) { + ol_txrx_offld_flush_handler(data, NULL, 0); + } else { + pkt = cds_alloc_ol_rx_pkt(sched_ctx); + if (qdf_unlikely(!pkt)) + return; + + pkt->callback = ol_txrx_offld_flush_handler; + pkt->context = data; + pkt->Rxpkt = NULL; + pkt->staId = 0; + cds_indicate_rxpkt(sched_ctx, pkt); + } +} + +/** + * ol_register_offld_flush_cb() - register the offld flush callback + * @offld_flush_cb: flush callback function + * @offld_init_cb: Allocate and initialize offld data structure. + * + * Store the offld flush callback provided and in turn + * register OL's offld flush handler with CE + * + * Return: none + */ +static void ol_register_offld_flush_cb(void (offld_flush_cb)(void *)) +{ + struct hif_opaque_softc *hif_device; + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc NULL!"); + TXRX_ASSERT2(0); + goto out; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev NULL!"); + TXRX_ASSERT2(0); + goto out; + } + if (pdev->offld_flush_cb) { + ol_txrx_info("offld already initialised"); + if (pdev->offld_flush_cb != offld_flush_cb) { + ol_txrx_err( + "offld_flush_cb is differ to previously registered callback") + TXRX_ASSERT2(0); + goto out; + } + goto out; + } + pdev->offld_flush_cb = offld_flush_cb; + hif_device = cds_get_context(QDF_MODULE_ID_HIF); + + if (qdf_unlikely(!hif_device)) { + ol_txrx_err("hif_device NULL!"); + qdf_assert(0); + goto out; + } + + hif_offld_flush_cb_register(hif_device, ol_txrx_offld_flush); + +out: + return; +} + +/** + * ol_deregister_offld_flush_cb() - deregister the offld flush callback + * + * Remove the offld flush callback provided and in turn + * deregister OL's offld flush handler with CE + * + * Return: none + */ +static void ol_deregister_offld_flush_cb(void) +{ + struct hif_opaque_softc *hif_device; + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev NULL!"); + return; + } + hif_device = cds_get_context(QDF_MODULE_ID_HIF); + + if (qdf_unlikely(!hif_device)) { + ol_txrx_err("hif_device NULL!"); + qdf_assert(0); + return; + } + + hif_offld_flush_cb_deregister(hif_device); + + pdev->offld_flush_cb = NULL; +} +#endif /* RECEIVE_OFFLOAD */ + +/** + * ol_register_data_stall_detect_cb() - register data stall callback + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @data_stall_detect_callback: data stall callback function + * + * + * Return: QDF_STATUS Enumeration + */ +static QDF_STATUS ol_register_data_stall_detect_cb( + struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + data_stall_detect_cb data_stall_detect_callback) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) { + ol_txrx_err("pdev NULL!"); + return QDF_STATUS_E_INVAL; + } + pdev->data_stall_detect_callback = data_stall_detect_callback; + return QDF_STATUS_SUCCESS; +} + +/** + * ol_deregister_data_stall_detect_cb() - de-register data stall callback + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @data_stall_detect_callback: data stall callback function + * + * + * Return: QDF_STATUS Enumeration + */ +static QDF_STATUS ol_deregister_data_stall_detect_cb( + struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + data_stall_detect_cb data_stall_detect_callback) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) { + ol_txrx_err("pdev NULL!"); + return QDF_STATUS_E_INVAL; + } + pdev->data_stall_detect_callback = NULL; + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_post_data_stall_event() - post data stall event + * @indicator: Module triggering data stall + * @data_stall_type: data stall event type + * @pdev_id: pdev id + * @vdev_id_bitmap: vdev id bitmap + * @recovery_type: data stall recovery type + * + * Return: None + */ +static void ol_txrx_post_data_stall_event( + struct cdp_soc_t *soc_hdl, + enum data_stall_log_event_indicator indicator, + enum data_stall_log_event_type data_stall_type, + uint32_t pdev_id, uint32_t vdev_id_bitmap, + enum data_stall_log_recovery_type recovery_type) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct data_stall_event_info data_stall_info; + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: pdev is NULL.", __func__); + return; + } + + if (!pdev->data_stall_detect_callback) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: data stall cb not registered", __func__); + return; + } + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: data_stall_type: %x pdev_id: %d", + __func__, data_stall_type, pdev_id); + + data_stall_info.indicator = indicator; + data_stall_info.data_stall_type = data_stall_type; + data_stall_info.vdev_id_bitmap = vdev_id_bitmap; + data_stall_info.pdev_id = pdev_id; + data_stall_info.recovery_type = recovery_type; + + if (data_stall_info.data_stall_type == + DATA_STALL_LOG_FW_RX_REFILL_FAILED) + htt_log_rx_ring_info(pdev->htt_pdev); + + pdev->data_stall_detect_callback(&data_stall_info); +} + +void +ol_txrx_dump_pkt(qdf_nbuf_t nbuf, uint32_t nbuf_paddr, int len) +{ + qdf_print(" Pkt: VA 0x%pK PA 0x%llx len %d\n", + qdf_nbuf_data(nbuf), (unsigned long long int)nbuf_paddr, len); + print_hex_dump(KERN_DEBUG, "Pkt: ", DUMP_PREFIX_ADDRESS, 16, 4, + qdf_nbuf_data(nbuf), len, true); +} + +struct cdp_vdev *ol_txrx_get_vdev_from_vdev_id(uint8_t vdev_id) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_vdev_handle vdev = NULL; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return NULL; + } + + vdev = ol_txrx_get_vdev_from_soc_vdev_id(soc, vdev_id); + + return ol_txrx_vdev_t_to_cdp_vdev(vdev); +} + +struct ol_txrx_vdev_t *ol_txrx_get_vdev_from_soc_vdev_id( + struct ol_txrx_soc_t *soc, uint8_t vdev_id) +{ + ol_txrx_pdev_handle pdev; + ol_txrx_vdev_handle vdev = NULL; + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (qdf_unlikely(!pdev)) + return NULL; + + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + if (vdev->vdev_id == vdev_id) + break; + } + + return vdev; +} + +/** + * ol_txrx_get_mon_vdev_from_pdev() - get monitor mode vdev from pdev + * @soc_hdl: datapath soc handle + * @pdev_id: the physical device id the virtual device belongs to + * + * Return: vdev id + * error if not found. + */ +uint8_t ol_txrx_get_mon_vdev_from_pdev(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = (struct ol_txrx_soc_t *)soc_hdl; + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (qdf_unlikely(!pdev)) + return -EINVAL; + + return pdev->monitor_vdev->vdev_id; +} + +/** + * ol_txrx_set_wisa_mode() - set wisa mode + * @soc_hdl: Datapath soc handle + * @vdev_id: vdev_id + * @enable: enable flag + * + * Return: QDF STATUS + */ +static QDF_STATUS ol_txrx_set_wisa_mode(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, bool enable) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) + return QDF_STATUS_E_INVAL; + + vdev->is_wisa_mode_enable = enable; + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_get_vdev_id() - get interface id from interface context + * @pvdev: vdev handle + * + * Return: virtual interface id + */ +static uint16_t ol_txrx_get_vdev_id(struct cdp_vdev *pvdev) +{ + struct ol_txrx_vdev_t *vdev = (struct ol_txrx_vdev_t *)pvdev; + + return vdev->vdev_id; +} + +/** + * ol_txrx_soc_attach_target() - attach soc target + * @soc: soc handle + * + * MCL legacy OL do nothing here + * + * Return: 0 + */ +static QDF_STATUS ol_txrx_soc_attach_target(ol_txrx_soc_handle soc) +{ + /* MCL legacy OL do nothing here */ + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_soc_detach() - detach soc target + * @soc: soc handle + * + * MCL legacy OL do nothing here + * + * Return: none + */ +static void ol_txrx_soc_detach(struct cdp_soc_t *soc) +{ + qdf_mem_free(soc); +} + +/** + * ol_txrx_pkt_log_con_service() - connect packet log service + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @scn: device context + * + * Return: noe + */ +#ifdef REMOVE_PKT_LOG +static void ol_txrx_pkt_log_con_service(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, void *scn) +{ +} +#else +static void ol_txrx_pkt_log_con_service(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, void *scn) +{ + htt_pkt_log_init(soc_hdl, pdev_id, scn); + pktlog_htc_attach(); +} +#endif + +/* OL wrapper functions for CDP abstraction */ +/** + * ol_txrx_wrapper_flush_rx_frames() - flush rx frames on the queue + * @soc: data path soc handle + * @pdev_id: datapath pdev identifier + * @peer_mac: peer mac address + * @drop: rx packets drop or deliver + * + * Return: none + */ +static void ol_txrx_wrapper_flush_rx_frames(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, void *peer_mac, + bool drop) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_txrx_peer_t *peer; + + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + peer = ol_txrx_peer_find_hash_find_get_ref(pdev, peer_mac, 0, 1, + PEER_DEBUG_ID_OL_INTERNAL); + if (!peer) { + ol_txrx_err("peer "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(peer_mac)); + return; + } + + ol_txrx_flush_rx_frames(peer, drop); +} + +/** + * ol_txrx_wrapper_register_peer() - register peer + * @pdev: pdev handle + * @sta_desc: peer description + * + * Return: QDF STATUS + */ +static QDF_STATUS ol_txrx_wrapper_register_peer( + struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, + struct ol_txrx_desc_type *sta_desc) +{ + return ol_txrx_register_peer(sta_desc); +} + +/** + * ol_txrx_wrapper_cfg_is_high_latency() - device is high or low latency device + * @pdev: pdev handle + * + * Return: 1 high latency bus + * 0 low latency bus + */ +static int ol_txrx_wrapper_cfg_is_high_latency(struct cdp_cfg *cfg_pdev) +{ + return ol_cfg_is_high_latency(cfg_pdev); +} + +/** + * ol_txrx_wrapper_peer_state_update() - specify the peer's authentication state + * @soc_hdl - datapath soc handle + * @peer_mac - mac address of which peer has changed its state + * @state - the new state of the peer + * + * Specify the peer's authentication state (none, connected, authenticated) + * to allow the data SW to determine whether to filter out invalid data frames. + * (In the "connected" state, where security is enabled, but authentication + * has not completed, tx and rx data frames other than EAPOL or WAPI should + * be discarded.) + * This function is only relevant for systems in which the tx and rx filtering + * are done in the host rather than in the target. + * + * Return: QDF Status + */ +static QDF_STATUS ol_txrx_wrapper_peer_state_update( + struct cdp_soc_t *soc_hdl, + uint8_t *peer_mac, + enum ol_txrx_peer_state state) +{ + return ol_txrx_peer_state_update(soc_hdl, peer_mac, state); +} + +/** + * ol_txrx_wrapper_set_flow_control_parameters() - set flow control parameters + * @cfg_ctx: cfg context + * @cfg_param: cfg parameters + * + * Return: none + */ +static void +ol_txrx_wrapper_set_flow_control_parameters(struct cdp_cfg *cfg_pdev, + void *cfg_param) +{ + return ol_tx_set_flow_control_parameters( + cfg_pdev, + (struct txrx_pdev_cfg_param_t *)cfg_param); +} + +/** + * ol_txrx_get_cfg() - get ini/cgf values in legacy dp + * @soc_hdl: soc context + * @cfg_param: cfg parameters + * + * Return: none + */ +static uint32_t ol_txrx_get_cfg(struct cdp_soc_t *soc_hdl, enum cdp_dp_cfg cfg) +{ + struct txrx_pdev_cfg_t *cfg_ctx; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id( + soc, + OL_TXRX_PDEV_ID); + uint32_t value = 0; + + if (!pdev) { + qdf_print("pdev is NULL"); + return 0; + } + + cfg_ctx = (struct txrx_pdev_cfg_t *)(pdev->ctrl_pdev); + switch (cfg) { + case cfg_dp_enable_data_stall: + value = cfg_ctx->enable_data_stall_detection; + break; + case cfg_dp_enable_ip_tcp_udp_checksum_offload: + value = cfg_ctx->ip_tcp_udp_checksum_offload; + break; + case cfg_dp_enable_p2p_ip_tcp_udp_checksum_offload: + value = cfg_ctx->p2p_ip_tcp_udp_checksum_offload; + break; + case cfg_dp_enable_nan_ip_tcp_udp_checksum_offload: + value = cfg_ctx->nan_tcp_udp_checksumoffload; + break; + case cfg_dp_tso_enable: + value = cfg_ctx->tso_enable; + break; + case cfg_dp_lro_enable: + value = cfg_ctx->lro_enable; + break; + case cfg_dp_sg_enable: + value = cfg_ctx->sg_enable; + break; + case cfg_dp_gro_enable: + value = cfg_ctx->gro_enable; + break; + case cfg_dp_tc_based_dyn_gro_enable: + value = cfg_ctx->tc_based_dyn_gro; + break; + case cfg_dp_tc_ingress_prio: + value = cfg_ctx->tc_ingress_prio; + break; +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 + case cfg_dp_tx_flow_start_queue_offset: + value = cfg_ctx->tx_flow_start_queue_offset; + break; + case cfg_dp_tx_flow_stop_queue_threshold: + value = cfg_ctx->tx_flow_stop_queue_th; + break; +#endif + case cfg_dp_ipa_uc_tx_buf_size: + value = cfg_ctx->uc_tx_buffer_size; + break; + case cfg_dp_ipa_uc_tx_partition_base: + value = cfg_ctx->uc_tx_partition_base; + break; + case cfg_dp_ipa_uc_rx_ind_ring_count: + value = cfg_ctx->uc_rx_indication_ring_count; + break; + case cfg_dp_enable_flow_steering: + value = cfg_ctx->enable_flow_steering; + break; + case cfg_dp_reorder_offload_supported: + value = cfg_ctx->is_full_reorder_offload; + break; + case cfg_dp_ce_classify_enable: + value = cfg_ctx->ce_classify_enabled; + break; + case cfg_dp_disable_intra_bss_fwd: + value = cfg_ctx->disable_intra_bss_fwd; + break; + case cfg_dp_pktlog_buffer_size: + value = cfg_ctx->pktlog_buffer_size; + break; + default: + value = 0; + break; + } + + return value; +} + +/* + * ol_get_pdev_param: function to get parameters from pdev + * @cdp_soc: txrx soc handle + * @pdev_id: id of pdev handle + * @param: parameter type to be get + * @val: parameter type to be get + * + * Return: SUCCESS or FAILURE + */ +static QDF_STATUS ol_get_pdev_param(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + enum cdp_pdev_param_type param, + cdp_config_param_type *val) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *olpdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + struct cdp_pdev *pdev = ol_txrx_pdev_t_to_cdp_pdev(olpdev); + + if (!pdev) + return QDF_STATUS_E_FAILURE; + + switch (param) { + case CDP_TX_PENDING: + val->cdp_pdev_param_tx_pending = ol_txrx_get_tx_pending(pdev); + break; + default: + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +/* + * ol_set_pdev_param: function to get parameters from pdev + * @cdp_soc: txrx soc handle + * @pdev_id: id of pdev handle + * @param: parameter type to be get + * @val: parameter type to be get + * + * Return: SUCCESS or FAILURE + */ +static QDF_STATUS ol_set_pdev_param(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + enum cdp_pdev_param_type param, + cdp_config_param_type val) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *olpdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + struct cdp_pdev *pdev = ol_txrx_pdev_t_to_cdp_pdev(olpdev); + + if (!pdev) + return QDF_STATUS_E_FAILURE; + + switch (param) { + case CDP_MONITOR_CHANNEL: + { + ol_htt_mon_note_chan(pdev, val.cdp_pdev_param_monitor_chan); + break; + } + default: + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef WDI_EVENT_ENABLE +void *ol_get_pldev(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + + if (pdev) + return pdev->pl_dev; + + return NULL; +} +#endif + +/** + * ol_register_packetdump_callback() - registers + * tx data packet, tx mgmt. packet and rx data packet + * dump callback handler. + * + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * @ol_tx_packetdump_cb: tx packetdump cb + * @ol_rx_packetdump_cb: rx packetdump cb + * + * This function is used to register tx data pkt, tx mgmt. + * pkt and rx data pkt dump callback + * + * Return: None + * + */ +static inline +void ol_register_packetdump_callback(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + ol_txrx_pktdump_cb ol_tx_packetdump_cb, + ol_txrx_pktdump_cb ol_rx_packetdump_cb) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + pdev->ol_tx_packetdump_cb = ol_tx_packetdump_cb; + pdev->ol_rx_packetdump_cb = ol_rx_packetdump_cb; +} + +/** + * ol_deregister_packetdump_callback() - deregidters + * tx data packet, tx mgmt. packet and rx data packet + * dump callback handler + * @soc_hdl: Datapath soc handle + * @pdev_id: id of data path pdev handle + * + * This function is used to deregidter tx data pkt., + * tx mgmt. pkt and rx data pkt. dump callback + * + * Return: None + * + */ +static inline +void ol_deregister_packetdump_callback(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + pdev->ol_tx_packetdump_cb = NULL; + pdev->ol_rx_packetdump_cb = NULL; +} + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * ol_txrx_register_pktcapture_cb() - Register pkt capture mode callback + * @soc: soc handle + * @pdev_id: pdev id + * @context: virtual device's osif_dev + * @cb: callback to register + * + * Return: QDF_STATUS Enumeration + */ +static QDF_STATUS ol_txrx_register_pktcapture_cb( + struct cdp_soc_t *soc, + uint8_t pdev_id, + void *context, + QDF_STATUS(cb)(void *, qdf_nbuf_t)) +{ + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id( + cdp_soc_t_to_ol_txrx_soc_t(soc), + pdev_id); + + if (!pdev) { + ol_txrx_err("pdev NULL!"); + return QDF_STATUS_E_INVAL; + } + + pdev->mon_osif_dev = context; + pdev->mon_cb = cb; + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_deregister_pktcapture_cb() - Register pkt capture mode callback + * @soc: soc handle + * @pdev_id: pdev id + * + * Return: QDF_STATUS Enumeration + */ +static QDF_STATUS ol_txrx_deregister_pktcapture_cb(struct cdp_soc_t *soc, + uint8_t pdev_id) +{ + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id( + cdp_soc_t_to_ol_txrx_soc_t(soc), + pdev_id); + + if (qdf_unlikely(!pdev)) { + qdf_print("%s: pdev is NULL!\n", __func__); + qdf_assert(0); + return QDF_STATUS_E_INVAL; + } + + pdev->mon_osif_dev = NULL; + pdev->mon_cb = NULL; + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_get_pktcapture_mode() - return pktcapture mode + * @soc: soc handle + * @pdev_id: pdev id + * + * Return: 0 - disable + * 1 - Mgmt packets + * 2 - Data packets + * 3 - Both Mgmt and Data packets + */ +static uint8_t ol_txrx_get_pktcapture_mode(struct cdp_soc_t *soc, + uint8_t pdev_id) +{ + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id( + cdp_soc_t_to_ol_txrx_soc_t(soc), + pdev_id); + + if (!pdev) { + qdf_print("%s: pdev is NULL\n", __func__); + return 0; + } + + if (!pdev->mon_cb || !pdev->mon_osif_dev) + return 0; + + return pdev->pktcapture_mode_value; +} + +/** + * ol_txrx_set_pktcapture_mode() - set pktcapture mode + * @soc: soc handle + * @pdev_id: pdev id + * @val : 0 - disable + * 1 - Mgmt packets + * 2 - Data packets + * 3 - Both Mgmt and Data packets + * + * Return: none + */ +static void ol_txrx_set_pktcapture_mode(struct cdp_soc_t *soc, + uint8_t pdev_id, uint8_t val) +{ + struct ol_txrx_pdev_t *pdev = ol_txrx_get_pdev_from_pdev_id( + cdp_soc_t_to_ol_txrx_soc_t(soc), + pdev_id); + + if (!pdev) { + qdf_print("%s: pdev is NULL\n", __func__); + return; + } + + pdev->pktcapture_mode_value = val; +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +static struct cdp_cmn_ops ol_ops_cmn = { + .txrx_soc_attach_target = ol_txrx_soc_attach_target, + .txrx_vdev_attach = ol_txrx_vdev_attach, + .txrx_vdev_detach = ol_txrx_vdev_detach, + .txrx_pdev_attach = ol_txrx_pdev_attach, + .txrx_pdev_attach_target = ol_txrx_pdev_attach_target, + .txrx_pdev_post_attach = ol_txrx_pdev_post_attach, + .txrx_pdev_pre_detach = ol_txrx_pdev_pre_detach, + .txrx_pdev_detach = ol_txrx_pdev_detach, + .txrx_peer_create = ol_txrx_peer_attach, + .txrx_peer_setup = NULL, + .txrx_peer_teardown = NULL, + .txrx_peer_delete = ol_txrx_peer_detach, + .txrx_peer_delete_sync = ol_txrx_peer_detach_sync, + .txrx_vdev_register = ol_txrx_vdev_register, + .txrx_soc_detach = ol_txrx_soc_detach, + .txrx_get_vdev_mac_addr = ol_txrx_get_vdev_mac_addr, + .txrx_get_ctrl_pdev_from_vdev = ol_txrx_get_ctrl_pdev_from_vdev, + .txrx_get_mon_vdev_from_pdev = ol_txrx_get_mon_vdev_from_pdev, + .txrx_mgmt_send_ext = ol_txrx_mgmt_send_ext, + .txrx_mgmt_tx_cb_set = ol_txrx_mgmt_tx_cb_set, + .txrx_data_tx_cb_set = ol_txrx_data_tx_cb_set, + .txrx_peer_unmap_sync_cb_set = ol_txrx_peer_unmap_sync_cb_set, + .flush_cache_rx_queue = ol_txrx_flush_cache_rx_queue, + .txrx_fw_stats_get = ol_txrx_fw_stats_get, + .display_stats = ol_txrx_display_stats, + .txrx_get_cfg = ol_txrx_get_cfg, + /* TODO: Add other functions */ +}; + +static struct cdp_misc_ops ol_ops_misc = { + .set_ibss_vdev_heart_beat_timer = + ol_txrx_set_ibss_vdev_heart_beat_timer, +#ifdef CONFIG_HL_SUPPORT + .set_wmm_param = ol_txrx_set_wmm_param, +#endif /* CONFIG_HL_SUPPORT */ + .bad_peer_txctl_set_setting = ol_txrx_bad_peer_txctl_set_setting, + .bad_peer_txctl_update_threshold = + ol_txrx_bad_peer_txctl_update_threshold, + .hl_tdls_flag_reset = ol_txrx_hl_tdls_flag_reset, + .tx_non_std = ol_tx_non_std, + .get_vdev_id = ol_txrx_get_vdev_id, + .get_tx_ack_stats = ol_txrx_get_tx_ack_stats, + .set_wisa_mode = ol_txrx_set_wisa_mode, + .txrx_data_stall_cb_register = ol_register_data_stall_detect_cb, + .txrx_data_stall_cb_deregister = ol_deregister_data_stall_detect_cb, + .txrx_post_data_stall_event = ol_txrx_post_data_stall_event, +#ifdef FEATURE_RUNTIME_PM + .runtime_suspend = ol_txrx_runtime_suspend, + .runtime_resume = ol_txrx_runtime_resume, +#endif /* FEATURE_RUNTIME_PM */ + .get_opmode = ol_txrx_get_opmode, + .mark_first_wakeup_packet = ol_tx_mark_first_wakeup_packet, + .update_mac_id = ol_txrx_update_mac_id, + .flush_rx_frames = ol_txrx_wrapper_flush_rx_frames, + .get_intra_bss_fwd_pkts_count = ol_get_intra_bss_fwd_pkts_count, + .pkt_log_init = htt_pkt_log_init, + .pkt_log_con_service = ol_txrx_pkt_log_con_service, + .register_pktdump_cb = ol_register_packetdump_callback, + .unregister_pktdump_cb = ol_deregister_packetdump_callback, +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK + .pdev_reset_driver_del_ack = ol_tx_pdev_reset_driver_del_ack, + .vdev_set_driver_del_ack_enable = ol_tx_vdev_set_driver_del_ack_enable, +#endif +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE + .vdev_set_bundle_require_flag = ol_tx_vdev_set_bundle_require, + .pdev_reset_bundle_require_flag = ol_tx_pdev_reset_bundle_require, +#endif +}; + +static struct cdp_flowctl_ops ol_ops_flowctl = { + .register_pause_cb = ol_txrx_register_pause_cb, +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 + .set_desc_global_pool_size = ol_tx_set_desc_global_pool_size, + .dump_flow_pool_info = ol_tx_dump_flow_pool_info, + .tx_desc_thresh_reached = ol_tx_desc_thresh_reached, +#endif /* QCA_LL_TX_FLOW_CONTROL_V2 */ +}; + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) +static struct cdp_lflowctl_ops ol_ops_l_flowctl = { + .register_tx_flow_control = ol_txrx_register_tx_flow_control, + .deregister_tx_flow_control_cb = ol_txrx_deregister_tx_flow_control_cb, + .flow_control_cb = ol_txrx_flow_control_cb, + .get_tx_resource = ol_txrx_get_tx_resource, + .ll_set_tx_pause_q_depth = ol_txrx_ll_set_tx_pause_q_depth, + .vdev_flush = ol_txrx_vdev_flush, + .vdev_pause = ol_txrx_vdev_pause, + .vdev_unpause = ol_txrx_vdev_unpause +}; /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ +#elif defined(QCA_HL_NETDEV_FLOW_CONTROL) +static struct cdp_lflowctl_ops ol_ops_l_flowctl = { + .register_tx_flow_control = ol_txrx_register_hl_flow_control, + .vdev_flush = ol_txrx_vdev_flush, + .vdev_pause = ol_txrx_vdev_pause, + .vdev_unpause = ol_txrx_vdev_unpause, + .set_vdev_os_queue_status = ol_txrx_set_vdev_os_queue_status, + .set_vdev_tx_desc_limit = ol_txrx_set_vdev_tx_desc_limit +}; +#else /* QCA_HL_NETDEV_FLOW_CONTROL */ +static struct cdp_lflowctl_ops ol_ops_l_flowctl = { }; +#endif + +#ifdef IPA_OFFLOAD +static struct cdp_ipa_ops ol_ops_ipa = { + .ipa_get_resource = ol_txrx_ipa_uc_get_resource, + .ipa_set_doorbell_paddr = ol_txrx_ipa_uc_set_doorbell_paddr, + .ipa_set_active = ol_txrx_ipa_uc_set_active, + .ipa_op_response = ol_txrx_ipa_uc_op_response, + .ipa_register_op_cb = ol_txrx_ipa_uc_register_op_cb, + .ipa_get_stat = ol_txrx_ipa_uc_get_stat, + .ipa_tx_data_frame = ol_tx_send_ipa_data_frame, + .ipa_set_uc_tx_partition_base = ol_cfg_set_ipa_uc_tx_partition_base, + .ipa_enable_autonomy = ol_txrx_ipa_enable_autonomy, + .ipa_disable_autonomy = ol_txrx_ipa_disable_autonomy, + .ipa_setup = ol_txrx_ipa_setup, + .ipa_cleanup = ol_txrx_ipa_cleanup, + .ipa_setup_iface = ol_txrx_ipa_setup_iface, + .ipa_cleanup_iface = ol_txrx_ipa_cleanup_iface, + .ipa_enable_pipes = ol_txrx_ipa_enable_pipes, + .ipa_disable_pipes = ol_txrx_ipa_disable_pipes, + .ipa_set_perf_level = ol_txrx_ipa_set_perf_level, +#ifdef FEATURE_METERING + .ipa_uc_get_share_stats = ol_txrx_ipa_uc_get_share_stats, + .ipa_uc_set_quota = ol_txrx_ipa_uc_set_quota +#endif +}; +#endif + +#ifdef RECEIVE_OFFLOAD +static struct cdp_rx_offld_ops ol_rx_offld_ops = { + .register_rx_offld_flush_cb = ol_register_offld_flush_cb, + .deregister_rx_offld_flush_cb = ol_deregister_offld_flush_cb +}; +#endif + +static struct cdp_bus_ops ol_ops_bus = { + .bus_suspend = ol_txrx_bus_suspend, + .bus_resume = ol_txrx_bus_resume +}; + +#ifdef WLAN_FEATURE_DSRC +static struct cdp_ocb_ops ol_ops_ocb = { + .set_ocb_chan_info = ol_txrx_set_ocb_chan_info, + .get_ocb_chan_info = ol_txrx_get_ocb_chan_info +}; +#endif + +static struct cdp_throttle_ops ol_ops_throttle = { +#ifdef QCA_SUPPORT_TX_THROTTLE + .throttle_init_period = ol_tx_throttle_init_period, + .throttle_set_level = ol_tx_throttle_set_level +#endif /* QCA_SUPPORT_TX_THROTTLE */ +}; + +static struct cdp_mob_stats_ops ol_ops_mob_stats = { + .clear_stats = ol_txrx_clear_stats, + .stats = ol_txrx_stats +}; + +static struct cdp_cfg_ops ol_ops_cfg = { + .set_cfg_rx_fwd_disabled = ol_set_cfg_rx_fwd_disabled, + .set_cfg_packet_log_enabled = ol_set_cfg_packet_log_enabled, + .cfg_attach = ol_pdev_cfg_attach, + .vdev_rx_set_intrabss_fwd = ol_vdev_rx_set_intrabss_fwd, + .is_rx_fwd_disabled = ol_txrx_is_rx_fwd_disabled, + .tx_set_is_mgmt_over_wmi_enabled = ol_tx_set_is_mgmt_over_wmi_enabled, + .is_high_latency = ol_txrx_wrapper_cfg_is_high_latency, + .set_flow_control_parameters = + ol_txrx_wrapper_set_flow_control_parameters, + .set_flow_steering = ol_set_cfg_flow_steering, + .set_ptp_rx_opt_enabled = ol_set_cfg_ptp_rx_opt_enabled, + .set_new_htt_msg_format = + ol_txrx_set_new_htt_msg_format, + .set_peer_unmap_conf_support = ol_txrx_set_peer_unmap_conf_support, + .get_peer_unmap_conf_support = ol_txrx_get_peer_unmap_conf_support, + .set_tx_compl_tsf64 = ol_txrx_set_tx_compl_tsf64, + .get_tx_compl_tsf64 = ol_txrx_get_tx_compl_tsf64, +}; + +static struct cdp_peer_ops ol_ops_peer = { + .register_peer = ol_txrx_wrapper_register_peer, + .clear_peer = ol_txrx_clear_peer, + .find_peer_exist = ol_txrx_find_peer_exist, + .find_peer_exist_on_vdev = ol_txrx_find_peer_exist_on_vdev, + .find_peer_exist_on_other_vdev = ol_txrx_find_peer_exist_on_other_vdev, + .peer_state_update = ol_txrx_wrapper_peer_state_update, + .get_vdevid = ol_txrx_get_vdevid, + .get_vdev_by_peer_addr = ol_txrx_wrapper_get_vdev_by_peer_addr, + .register_ocb_peer = ol_txrx_register_ocb_peer, + .peer_get_peer_mac_addr = ol_txrx_peer_get_peer_mac_addr, + .get_peer_state = ol_txrx_get_peer_state, + .update_ibss_add_peer_num_of_vdev = + ol_txrx_update_ibss_add_peer_num_of_vdev, +#if defined(CONFIG_HL_SUPPORT) && defined(FEATURE_WLAN_TDLS) + .copy_mac_addr_raw = ol_txrx_copy_mac_addr_raw, + .add_last_real_peer = ol_txrx_add_last_real_peer, + .is_vdev_restore_last_peer = is_vdev_restore_last_peer, + .update_last_real_peer = ol_txrx_update_last_real_peer, + .set_tdls_offchan_enabled = ol_txrx_set_tdls_offchan_enabled, + .set_peer_as_tdls_peer = ol_txrx_set_peer_as_tdls_peer, +#endif /* CONFIG_HL_SUPPORT */ + .peer_detach_force_delete = ol_txrx_peer_detach_force_delete, + .peer_flush_frags = ol_txrx_peer_flush_frags, +}; + +static struct cdp_tx_delay_ops ol_ops_delay = { +#ifdef QCA_COMPUTE_TX_DELAY + .tx_delay = ol_tx_delay, + .tx_delay_hist = ol_tx_delay_hist, + .tx_packet_count = ol_tx_packet_count, + .tx_set_compute_interval = ol_tx_set_compute_interval +#endif /* QCA_COMPUTE_TX_DELAY */ +}; + +static struct cdp_pmf_ops ol_ops_pmf = { + .get_pn_info = ol_txrx_get_pn_info +}; + +static struct cdp_ctrl_ops ol_ops_ctrl = { + .txrx_get_pldev = ol_get_pldev, + .txrx_wdi_event_sub = wdi_event_sub, + .txrx_wdi_event_unsub = wdi_event_unsub, + .txrx_get_pdev_param = ol_get_pdev_param, + .txrx_set_pdev_param = ol_set_pdev_param +}; + +/* WINplatform specific structures */ +static struct cdp_me_ops ol_ops_me = { + /* EMPTY FOR MCL */ +}; + +static struct cdp_mon_ops ol_ops_mon = { + /* EMPTY FOR MCL */ +}; + +static struct cdp_host_stats_ops ol_ops_host_stats = { + /* EMPTY FOR MCL */ +}; + +static struct cdp_wds_ops ol_ops_wds = { + /* EMPTY FOR MCL */ +}; + +static struct cdp_raw_ops ol_ops_raw = { + /* EMPTY FOR MCL */ +}; + +#ifdef WLAN_FEATURE_PKT_CAPTURE +static struct cdp_pktcapture_ops ol_ops_pkt_capture = { + .txrx_pktcapture_cb_register = ol_txrx_register_pktcapture_cb, + .txrx_pktcapture_cb_deregister = ol_txrx_deregister_pktcapture_cb, + .txrx_pktcapture_set_mode = ol_txrx_set_pktcapture_mode, + .txrx_pktcapture_get_mode = ol_txrx_get_pktcapture_mode, +}; +#endif /* #ifdef WLAN_FEATURE_PKT_CAPTURE */ + +static struct cdp_ops ol_txrx_ops = { + .cmn_drv_ops = &ol_ops_cmn, + .ctrl_ops = &ol_ops_ctrl, + .me_ops = &ol_ops_me, + .mon_ops = &ol_ops_mon, + .host_stats_ops = &ol_ops_host_stats, + .wds_ops = &ol_ops_wds, + .raw_ops = &ol_ops_raw, + .misc_ops = &ol_ops_misc, + .cfg_ops = &ol_ops_cfg, + .flowctl_ops = &ol_ops_flowctl, + .l_flowctl_ops = &ol_ops_l_flowctl, +#ifdef IPA_OFFLOAD + .ipa_ops = &ol_ops_ipa, +#endif +#ifdef RECEIVE_OFFLOAD + .rx_offld_ops = &ol_rx_offld_ops, +#endif + .bus_ops = &ol_ops_bus, +#ifdef WLAN_FEATURE_DSRC + .ocb_ops = &ol_ops_ocb, +#endif + .peer_ops = &ol_ops_peer, + .throttle_ops = &ol_ops_throttle, + .mob_stats_ops = &ol_ops_mob_stats, + .delay_ops = &ol_ops_delay, + .pmf_ops = &ol_ops_pmf, +#ifdef WLAN_FEATURE_PKT_CAPTURE + .pktcapture_ops = &ol_ops_pkt_capture, +#endif +}; + +ol_txrx_soc_handle ol_txrx_soc_attach(void *scn_handle, + struct ol_if_ops *dp_ol_if_ops) +{ + struct ol_txrx_soc_t *soc; + + soc = qdf_mem_malloc(sizeof(*soc)); + if (!soc) + return NULL; + + soc->psoc = scn_handle; + soc->cdp_soc.ops = &ol_txrx_ops; + soc->cdp_soc.ol_ops = dp_ol_if_ops; + + return ol_txrx_soc_t_to_cdp_soc_t(soc); +} + +bool ol_txrx_get_new_htt_msg_format(struct ol_txrx_pdev_t *pdev) +{ + if (!pdev) { + qdf_print("%s: pdev is NULL\n", __func__); + return false; + } + return pdev->new_htt_msg_format; +} + +void ol_txrx_set_new_htt_msg_format(uint8_t val) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + qdf_print("%s: pdev is NULL\n", __func__); + return; + } + pdev->new_htt_msg_format = val; +} + +bool ol_txrx_get_peer_unmap_conf_support(void) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return false; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + qdf_print("%s: pdev is NULL\n", __func__); + return false; + } + return pdev->enable_peer_unmap_conf_support; +} + +void ol_txrx_set_peer_unmap_conf_support(bool val) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + qdf_print("%s: pdev is NULL\n", __func__); + return; + } + pdev->enable_peer_unmap_conf_support = val; +} + +#ifdef WLAN_FEATURE_TSF_PLUS +bool ol_txrx_get_tx_compl_tsf64(void) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return false; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + qdf_print("%s: pdev is NULL\n", __func__); + return false; + } + return pdev->enable_tx_compl_tsf64; +} + +void ol_txrx_set_tx_compl_tsf64(bool val) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + pdev->enable_tx_compl_tsf64 = val; +} +#else +bool ol_txrx_get_tx_compl_tsf64(void) +{ + return false; +} + +void ol_txrx_set_tx_compl_tsf64(bool val) +{ +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx.h new file mode 100644 index 0000000000000000000000000000000000000000..d677f1b7f36050a03cfd9ddedf310795c2a75987 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx.h @@ -0,0 +1,919 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_TXRX__H_ +#define _OL_TXRX__H_ + +#include /* qdf_nbuf_t */ +#include /* ol_txrx_vdev_t, etc. */ +#include "cds_sched.h" +#include +#include +#include +#include + +/* + * Pool of tx descriptors reserved for + * high-priority traffic, such as ARP/EAPOL etc + * only for forwarding path. + */ +#define OL_TX_NON_FWD_RESERVE 100 + +/** + * enum ol_txrx_fc_limit_id - Flow control identifier for + * vdev limits based on band, channel bw and number of spatial streams + * @TXRX_FC_5GH_80M_2x2: Limit for 5GHz, 80MHz BW, 2x2 NSS + * @TXRX_FC_5GH_40M_2x2: + * @TXRX_FC_5GH_20M_2x2: + * @TXRX_FC_5GH_80M_1x1: + * @TXRX_FC_5GH_40M_1x1: + * @TXRX_FC_5GH_20M_1x1: + * @TXRX_FC_2GH_40M_2x2: + * @TXRX_FC_2GH_20M_2x2: + * @TXRX_FC_2GH_40M_1x1: + * @TXRX_FC_2GH_20M_1x1: + */ +enum ol_txrx_fc_limit_id { + TXRX_FC_5GH_80M_2x2, + TXRX_FC_5GH_40M_2x2, + TXRX_FC_5GH_20M_2x2, + TXRX_FC_5GH_80M_1x1, + TXRX_FC_5GH_40M_1x1, + TXRX_FC_5GH_20M_1x1, + TXRX_FC_2GH_40M_2x2, + TXRX_FC_2GH_20M_2x2, + TXRX_FC_2GH_40M_1x1, + TXRX_FC_2GH_20M_1x1, + TXRX_FC_MAX +}; + +#define TXRX_RFS_ENABLE_PEER_ID_UNMAP_COUNT 3 +#define TXRX_RFS_DISABLE_PEER_ID_UNMAP_COUNT 1 + +ol_txrx_peer_handle ol_txrx_peer_get_ref_by_addr(ol_txrx_pdev_handle pdev, + u8 *peer_addr, + enum peer_debug_id_type + dbg_id); + +int ol_txrx_peer_release_ref(ol_txrx_peer_handle peer, + enum peer_debug_id_type dbg_id); + +/** + * ol_txrx_soc_attach() - initialize the soc + * @scn_handle: Opaque SOC handle from control plane + * @dp_ol_if_ops: Offload Operations + * + * Return: SOC handle on success, NULL on failure + */ +ol_txrx_soc_handle ol_txrx_soc_attach(void *scn_handle, + struct ol_if_ops *dp_ol_if_ops); + +/** + * ol_tx_desc_pool_size_hl() - allocate tx descriptor pool size for HL systems + * @ctrl_pdev: the control pdev handle + * + * Return: allocated pool size + */ +u_int16_t +ol_tx_desc_pool_size_hl(struct cdp_cfg *ctrl_pdev); + +#ifndef OL_TX_AVG_FRM_BYTES +#define OL_TX_AVG_FRM_BYTES 1000 +#endif + +#ifndef OL_TX_DESC_POOL_SIZE_MIN_HL +#define OL_TX_DESC_POOL_SIZE_MIN_HL 500 +#endif + +#ifndef OL_TX_DESC_POOL_SIZE_MAX_HL +#define OL_TX_DESC_POOL_SIZE_MAX_HL 5000 +#endif + +#ifndef FW_STATS_DESC_POOL_SIZE +#define FW_STATS_DESC_POOL_SIZE 10 +#endif + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +#define TXRX_HL_TX_FLOW_CTRL_VDEV_LOW_WATER_MARK 400 +#define TXRX_HL_TX_FLOW_CTRL_MGMT_RESERVED 100 +#endif + +#define TXRX_HL_TX_DESC_HI_PRIO_RESERVED 20 +#define TXRX_HL_TX_DESC_QUEUE_RESTART_TH \ + (TXRX_HL_TX_DESC_HI_PRIO_RESERVED + 100) + +struct peer_hang_data { + uint16_t tlv_header; + uint8_t peer_mac_addr[QDF_MAC_ADDR_SIZE]; + uint16_t peer_timeout_bitmask; +} qdf_packed; + +#if defined(CONFIG_HL_SUPPORT) && defined(FEATURE_WLAN_TDLS) + +void +ol_txrx_hl_tdls_flag_reset(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, bool flag); +#else + +static inline void +ol_txrx_hl_tdls_flag_reset(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, bool flag) +{ +} +#endif + +#ifdef WDI_EVENT_ENABLE +void *ol_get_pldev(struct cdp_soc_t *soc, uint8_t pdev_id); +#else +static inline +void *ol_get_pldev(struct cdp_soc_t *soc, uint8_t pdev_id) +{ + return NULL; +} +#endif + +#ifdef QCA_SUPPORT_TXRX_LOCAL_PEER_ID +ol_txrx_peer_handle +ol_txrx_peer_get_ref_by_local_id(struct cdp_pdev *ppdev, + uint8_t local_peer_id, + enum peer_debug_id_type dbg_id); +#endif /* QCA_SUPPORT_TXRX_LOCAL_PEER_ID */ + +/** + * ol_txrx_get_pdev_from_pdev_id() - Returns pdev object given the pdev id + * @soc: core DP soc context + * @pdev_id: pdev id from pdev object can be retrieved + * + * Return: Pointer to DP pdev object + */ + +static inline struct ol_txrx_pdev_t * +ol_txrx_get_pdev_from_pdev_id(struct ol_txrx_soc_t *soc, + uint8_t pdev_id) +{ + return soc->pdev_list[pdev_id]; +} + +/* + * @nbuf: buffer which contains data to be displayed + * @nbuf_paddr: physical address of the buffer + * @len: defines the size of the data to be displayed + * + * Return: None + */ +void +ol_txrx_dump_pkt(qdf_nbuf_t nbuf, uint32_t nbuf_paddr, int len); + +/** + * ol_txrx_get_vdev_from_vdev_id() - get vdev from vdev_id + * @vdev_id: vdev_id + * + * Return: vdev handle + * NULL if not found. + */ +struct cdp_vdev *ol_txrx_get_vdev_from_vdev_id(uint8_t vdev_id); + +/** + * ol_txrx_get_vdev_from_soc_vdev_id() - get vdev from soc and vdev_id + * @soc: datapath soc handle + * @vdev_id: vdev_id + * + * Return: vdev handle + * NULL if not found. + */ +struct ol_txrx_vdev_t *ol_txrx_get_vdev_from_soc_vdev_id( + struct ol_txrx_soc_t *soc, uint8_t vdev_id); + +/** + * ol_txrx_get_mon_vdev_from_pdev() - get monitor mode vdev from pdev + * @soc: datapath soc handle + * @pdev_id: the physical device id the virtual device belongs to + * + * Return: vdev id + * error if not found. + */ +uint8_t ol_txrx_get_mon_vdev_from_pdev(struct cdp_soc_t *soc, + uint8_t pdev_id); + +/** + * ol_txrx_get_vdev_by_peer_addr() - Get vdev handle by peer mac address + * @ppdev - data path device instance + * @peer_addr - peer mac address + * + * Get virtual interface handle by local peer mac address + * + * Return: Virtual interface instance handle + * NULL in case cannot find + */ +ol_txrx_vdev_handle +ol_txrx_get_vdev_by_peer_addr(struct cdp_pdev *ppdev, + struct qdf_mac_addr peer_addr); + +void *ol_txrx_find_peer_by_addr(struct cdp_pdev *pdev, + uint8_t *peer_addr); + +/** + * @brief specify the peer's authentication state + * @details + * Specify the peer's authentication state (none, connected, authenticated) + * to allow the data SW to determine whether to filter out invalid data frames. + * (In the "connected" state, where security is enabled, but authentication + * has not completed, tx and rx data frames other than EAPOL or WAPI should + * be discarded.) + * This function is only relevant for systems in which the tx and rx filtering + * are done in the host rather than in the target. + * + * @param soc - datapath soc handle + * @param peer_mac - mac address of which peer has changed its state + * @param state - the new state of the peer + * + * Return: QDF Status + */ +QDF_STATUS ol_txrx_peer_state_update(struct cdp_soc_t *soc_hdl, + uint8_t *peer_mac, + enum ol_txrx_peer_state state); + +void htt_pkt_log_init(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, void *scn); +void peer_unmap_timer_handler(void *data); + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +/** + * ol_txrx_register_tx_flow_control() - register tx flow control callback + * @soc_hdl: soc handle + * @vdev_id: vdev_id + * @flowControl: flow control callback + * @osif_fc_ctx: callback context + * @flow_control_is_pause: is vdev paused by flow control + * + * Return: 0 for success or error code + */ +int ol_txrx_register_tx_flow_control(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + ol_txrx_tx_flow_control_fp flow_control, + void *osif_fc_ctx, + ol_txrx_tx_flow_control_is_pause_fp + flow_control_is_pause); + +/** + * ol_txrx_de_register_tx_flow_control_cb() - deregister tx flow control + * callback + * @soc_hdl: soc handle + * @vdev_id: vdev_id + * + * Return: 0 for success or error code + */ +int ol_txrx_deregister_tx_flow_control_cb(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id); + +bool ol_txrx_get_tx_resource(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + struct qdf_mac_addr peer_addr, + unsigned int low_watermark, + unsigned int high_watermark_offset); + +/** + * ol_txrx_ll_set_tx_pause_q_depth() - set pause queue depth + * @soc_hdl: soc handle + * @vdev_id: vdev id + * @pause_q_depth: pause queue depth + * + * Return: 0 for success or error code + */ +int ol_txrx_ll_set_tx_pause_q_depth(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, int pause_q_depth); +#endif + +void ol_tx_init_pdev(ol_txrx_pdev_handle pdev); + +#ifdef CONFIG_HL_SUPPORT +void ol_txrx_vdev_txqs_init(struct ol_txrx_vdev_t *vdev); +void ol_txrx_vdev_tx_queue_free(struct ol_txrx_vdev_t *vdev); +void ol_txrx_peer_txqs_init(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer); +void ol_txrx_peer_tx_queue_free(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer); +#else +static inline void +ol_txrx_vdev_txqs_init(struct ol_txrx_vdev_t *vdev) {} + +static inline void +ol_txrx_vdev_tx_queue_free(struct ol_txrx_vdev_t *vdev) {} + +static inline void +ol_txrx_peer_txqs_init(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) {} + +static inline void +ol_txrx_peer_tx_queue_free(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) {} +#endif + +#if defined(CONFIG_HL_SUPPORT) && defined(DEBUG_HL_LOGGING) +void ol_txrx_pdev_txq_log_init(struct ol_txrx_pdev_t *pdev); +void ol_txrx_pdev_txq_log_destroy(struct ol_txrx_pdev_t *pdev); +void ol_txrx_pdev_grp_stats_init(struct ol_txrx_pdev_t *pdev); +void ol_txrx_pdev_grp_stat_destroy(struct ol_txrx_pdev_t *pdev); +#else +static inline void +ol_txrx_pdev_txq_log_init(struct ol_txrx_pdev_t *pdev) {} + +static inline void +ol_txrx_pdev_txq_log_destroy(struct ol_txrx_pdev_t *pdev) {} + +static inline void +ol_txrx_pdev_grp_stats_init(struct ol_txrx_pdev_t *pdev) {} + +static inline void +ol_txrx_pdev_grp_stat_destroy(struct ol_txrx_pdev_t *pdev) {} +#endif + +#if defined(CONFIG_HL_SUPPORT) && defined(FEATURE_WLAN_TDLS) +/** + * ol_txrx_copy_mac_addr_raw() - copy raw mac addr + * @soc_hdl: datapath soc handle + * @vdev_id: the data virtual device id + * @bss_addr: bss address + * + * Return: None + */ +void ol_txrx_copy_mac_addr_raw(struct cdp_soc_t *soc, uint8_t vdev_id, + uint8_t *bss_addr); + +/** + * ol_txrx_add_last_real_peer() - add last peer + * @soc_hdl: datapath soc handle + * @pdev_id: the data physical device id + * @vdev_id: virtual device id + * + * Return: None + */ +void ol_txrx_add_last_real_peer(struct cdp_soc_t *soc, uint8_t pdev_id, + uint8_t vdev_id); + +/** + * is_vdev_restore_last_peer() - check for vdev last peer + * @soc: datapath soc handle + * vdev_id: vdev id + * @peer_mac: peer mac address + * + * Return: true if last peer is not null + */ +bool is_vdev_restore_last_peer(struct cdp_soc_t *soc, uint8_t vdev_id, + uint8_t *peer_mac); + +/** + * ol_txrx_update_last_real_peer() - check for vdev last peer + * @soc: datapath soc handle + * @pdev_id: the data physical device id + * @vdev_id: vdev_id + * @restore_last_peer: restore last peer flag + * + * Return: None + */ +void ol_txrx_update_last_real_peer(struct cdp_soc_t *soc, uint8_t pdev_id, + uint8_t vdev_id, + bool restore_last_peer); + +/** + * ol_txrx_set_peer_as_tdls_peer() - mark peer as tdls peer + * @soc: pointer to SOC handle + * @vdev_id: virtual interface id + * @peer_mac: peer mac address + * @value: false/true + * + * Return: None + */ +void ol_txrx_set_peer_as_tdls_peer(struct cdp_soc_t *soc, uint8_t vdev_id, + uint8_t *peer_mac, bool val); + +/** + * ol_txrx_set_tdls_offchan_enabled() - set tdls offchan enabled + * @soc: pointer to SOC handle + * @vdev_id: virtual interface id + * @peer_mac: peer mac address + * @value: false/true + * + * Return: None + */ +void ol_txrx_set_tdls_offchan_enabled(struct cdp_soc_t *soc, uint8_t vdev_id, + uint8_t *peer_mac, bool val); +#endif + +#if defined(FEATURE_TSO) && defined(FEATURE_TSO_DEBUG) +void ol_txrx_stats_display_tso(ol_txrx_pdev_handle pdev); +void ol_txrx_tso_stats_init(ol_txrx_pdev_handle pdev); +void ol_txrx_tso_stats_deinit(ol_txrx_pdev_handle pdev); +void ol_txrx_tso_stats_clear(ol_txrx_pdev_handle pdev); +#else +static inline +void ol_txrx_stats_display_tso(ol_txrx_pdev_handle pdev) +{ + ol_txrx_err("TSO is not supported\n"); +} + +static inline +void ol_txrx_tso_stats_init(ol_txrx_pdev_handle pdev) {} + +static inline +void ol_txrx_tso_stats_deinit(ol_txrx_pdev_handle pdev) {} + +static inline +void ol_txrx_tso_stats_clear(ol_txrx_pdev_handle pdev) {} +#endif + +struct ol_tx_desc_t * +ol_txrx_mgmt_tx_desc_alloc(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t tx_mgmt_frm, + struct ol_txrx_msdu_info_t *tx_msdu_info); + +int ol_txrx_mgmt_send_frame(struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t tx_mgmt_frm, + struct ol_txrx_msdu_info_t *tx_msdu_info, + uint16_t chanfreq); + +#ifdef CONFIG_HL_SUPPORT +static inline +uint32_t ol_tx_get_desc_global_pool_size(struct ol_txrx_pdev_t *pdev) +{ + return ol_tx_desc_pool_size_hl(pdev->ctrl_pdev); +} +#else +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 +static inline +uint32_t ol_tx_get_desc_global_pool_size(struct ol_txrx_pdev_t *pdev) +{ + return pdev->num_msdu_desc; +} +#else +static inline +uint32_t ol_tx_get_desc_global_pool_size(struct ol_txrx_pdev_t *pdev) +{ + return ol_cfg_target_tx_credit(pdev->ctrl_pdev); +} +#endif +#endif + +/** + * cdp_soc_t_to_ol_txrx_soc_t() - typecast cdp_soc_t to ol_txrx_soc_t + * @soc: OL soc handle + * + * Return: struct ol_txrx_soc_t pointer + */ +static inline +struct ol_txrx_soc_t *cdp_soc_t_to_ol_txrx_soc_t(ol_txrx_soc_handle soc) +{ + return (struct ol_txrx_soc_t *)soc; +} + +/** + * ol_txrx_soc_t_to_cdp_soc_t() - typecast ol_txrx_soc_t to cdp_soc + * @soc: Opaque soc handle + * + * Return: struct cdp_soc_t pointer + */ +static inline +ol_txrx_soc_handle ol_txrx_soc_t_to_cdp_soc_t(struct ol_txrx_soc_t *soc) +{ + return (struct cdp_soc_t *)soc; +} + +/** + * cdp_pdev_to_ol_txrx_pdev_t() - typecast cdp_pdev to ol_txrx_pdev_t + * @pdev: OL pdev handle + * + * Return: struct ol_txrx_pdev_t pointer + */ +static inline +struct ol_txrx_pdev_t *cdp_pdev_to_ol_txrx_pdev_t(struct cdp_pdev *pdev) +{ + return (struct ol_txrx_pdev_t *)pdev; +} + +/** + * ol_txrx_pdev_t_to_cdp_pdev() - typecast ol_txrx_pdev_t to cdp_pdev + * @pdev: Opaque pdev handle + * + * Return: struct cdp_pdev pointer + */ +static inline +struct cdp_pdev *ol_txrx_pdev_t_to_cdp_pdev(struct ol_txrx_pdev_t *pdev) +{ + return (struct cdp_pdev *)pdev; +} + +/** + * cdp_vdev_to_ol_txrx_vdev_t() - typecast cdp_vdev to ol_txrx_vdev_t + * @vdev: OL vdev handle + * + * Return: struct ol_txrx_vdev_t pointer + */ +static inline +struct ol_txrx_vdev_t *cdp_vdev_to_ol_txrx_vdev_t(struct cdp_vdev *vdev) +{ + return (struct ol_txrx_vdev_t *)vdev; +} + +/** + * ol_txrx_vdev_t_to_cdp_vdev() - typecast ol_txrx_vdev_t to cdp_vdev + * @vdev: Opaque vdev handle + * + * Return: struct cdp_vdev pointer + */ +static inline +struct cdp_vdev *ol_txrx_vdev_t_to_cdp_vdev(struct ol_txrx_vdev_t *vdev) +{ + return (struct cdp_vdev *)vdev; +} + +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 +void ol_tx_set_desc_global_pool_size(uint32_t num_msdu_desc); +uint32_t ol_tx_get_total_free_desc(struct ol_txrx_pdev_t *pdev); +/** + * ol_txrx_fwd_desc_thresh_check() - check to forward packet to tx path + * @vdev: which virtual device the frames were addressed to + * + * This API is to check whether enough descriptors are available or not + * to forward packet to tx path. If not enough descriptors left, + * start dropping tx-path packets. + * Do not pause netif queues as still a pool of descriptors is reserved + * for high-priority traffic such as EAPOL/ARP etc. + * In case of intra-bss forwarding, it could be possible that tx-path can + * consume all the tx descriptors and pause netif queues. Due to this, + * there would be some left for stack triggered packets such as ARP packets + * which could lead to disconnection of device. To avoid this, reserved + * a pool of descriptors for high-priority packets, i.e., reduce the + * threshold of drop in the intra-bss forwarding path. + * + * Return: true ; forward the packet, i.e., below threshold + * false; not enough descriptors, drop the packet + */ +bool ol_txrx_fwd_desc_thresh_check(struct ol_txrx_vdev_t *txrx_vdev); + +/** + * ol_tx_desc_thresh_reached() - is tx desc threshold reached + * @soc_hdl: Datapath soc handle + * @vdev_id: id of vdev + * + * Return: true if tx desc available reached threshold or false otherwise + */ +static inline bool ol_tx_desc_thresh_reached(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id) +{ + struct ol_txrx_vdev_t *vdev; + + vdev = (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + if (!vdev) { + dp_err("vdev is NULL"); + return false; + } + + return !(ol_txrx_fwd_desc_thresh_check(vdev)); +} + +#else +/** + * ol_tx_get_total_free_desc() - get total free descriptors + * @pdev: pdev handle + * + * Return: total free descriptors + */ +static inline +uint32_t ol_tx_get_total_free_desc(struct ol_txrx_pdev_t *pdev) +{ + return pdev->tx_desc.num_free; +} + +static inline +bool ol_txrx_fwd_desc_thresh_check(struct ol_txrx_vdev_t *txrx_vdev) +{ + return true; +} + +#endif + +#if defined(FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL) && \ + defined(FEATURE_HL_DBS_GROUP_CREDIT_SHARING) +static inline void +ol_txrx_init_txq_group_limit_lend(struct ol_txrx_pdev_t *pdev) +{ + BUILD_BUG_ON(OL_TX_MAX_GROUPS_PER_QUEUE > 1); + BUILD_BUG_ON(OL_TX_MAX_TXQ_GROUPS > 2); + pdev->limit_lend = 0; + pdev->min_reserve = 0; +} +#else +static inline void +ol_txrx_init_txq_group_limit_lend(struct ol_txrx_pdev_t *pdev) +{} +#endif + +int ol_txrx_fw_stats_desc_pool_init(struct ol_txrx_pdev_t *pdev, + uint8_t pool_size); +void ol_txrx_fw_stats_desc_pool_deinit(struct ol_txrx_pdev_t *pdev); +struct ol_txrx_fw_stats_desc_t + *ol_txrx_fw_stats_desc_alloc(struct ol_txrx_pdev_t *pdev); +struct ol_txrx_stats_req_internal + *ol_txrx_fw_stats_desc_get_req(struct ol_txrx_pdev_t *pdev, + uint8_t desc_id); + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +/** + * ol_txrx_register_hl_flow_control() -register hl netdev flow control callback + * @soc_hdl: soc handle + * @pdev_id: datapath pdev identifier + * @flowControl: flow control callback + * + * Return: 0 for success or error code + */ +int ol_txrx_register_hl_flow_control(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, + tx_pause_callback flowcontrol); + +/** + * ol_txrx_set_vdev_os_queue_status() - Set OS queue status for a vdev + * @soc_hdl: soc handle + * @vdev_id: vdev id for the vdev under consideration. + * @action: action to be done on queue for vdev + * + * Return: 0 on success, -EINVAL on failure + */ +int ol_txrx_set_vdev_os_queue_status(struct cdp_soc_t *soc_hdl, u8 vdev_id, + enum netif_action_type action); + +/** + * ol_txrx_set_vdev_tx_desc_limit() - Set TX descriptor limits for a vdev + * @soc_hdl: soc handle + * @vdev_id: vdev id for the vdev under consideration. + * @chan_freq: channel frequency on which the vdev has been started. + * + * Return: 0 on success, -EINVAL on failure + */ +int ol_txrx_set_vdev_tx_desc_limit(struct cdp_soc_t *soc_hdl, u8 vdev_id, + u32 chan_freq); +#endif + +/** + * ol_txrx_get_new_htt_msg_format() - check htt h2t msg feature + * @pdev - datapath device instance + * + * Check if h2t message length includes htc header length + * + * return if new htt h2t msg feature enabled + */ +bool ol_txrx_get_new_htt_msg_format(struct ol_txrx_pdev_t *pdev); + +/** + * ol_txrx_set_new_htt_msg_format() - set htt h2t msg feature + * @val - enable or disable new htt h2t msg feature + * + * Set if h2t message length includes htc header length + * + * return NONE + */ +void ol_txrx_set_new_htt_msg_format(uint8_t val); + +/** + * ol_txrx_set_peer_unmap_conf_support() - set peer unmap conf feature + * @val - enable or disable peer unmap conf feature + * + * Set if peer unamp conf feature is supported by both FW and in INI + * + * return NONE + */ +void ol_txrx_set_peer_unmap_conf_support(bool val); + +/** + * ol_txrx_get_peer_unmap_conf_support() - check peer unmap conf feature + * + * Check if peer unmap conf feature is enabled + * + * return true is peer unmap conf feature is enabled else false + */ +bool ol_txrx_get_peer_unmap_conf_support(void); + +/** + * ol_txrx_get_tx_compl_tsf64() - check tx compl tsf64 feature + * + * Check if tx compl tsf64 feature is enabled + * + * return true is tx compl tsf64 feature is enabled else false + */ +bool ol_txrx_get_tx_compl_tsf64(void); + +/** + * ol_txrx_set_tx_compl_tsf64() - set tx compl tsf64 feature + * @val - enable or disable tx compl tsf64 feature + * + * Set if tx compl tsf64 feature is supported FW + * + * return NONE + */ +void ol_txrx_set_tx_compl_tsf64(bool val); + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK + +/** + * ol_txrx_vdev_init_tcp_del_ack() - initialize tcp delayed ack structure + * @vdev: vdev handle + * + * Return: none + */ +void ol_txrx_vdev_init_tcp_del_ack(struct ol_txrx_vdev_t *vdev); + +/** + * ol_txrx_vdev_deinit_tcp_del_ack() - deinitialize tcp delayed ack structure + * @vdev: vdev handle + * + * Return: none + */ +void ol_txrx_vdev_deinit_tcp_del_ack(struct ol_txrx_vdev_t *vdev); + +/** + * ol_txrx_vdev_free_tcp_node() - add tcp node in free list + * @vdev: vdev handle + * @node: tcp stream node + * + * Return: none + */ +void ol_txrx_vdev_free_tcp_node(struct ol_txrx_vdev_t *vdev, + struct tcp_stream_node *node); + +/** + * ol_txrx_vdev_alloc_tcp_node() - allocate tcp node + * @vdev: vdev handle + * + * Return: tcp stream node + */ +struct tcp_stream_node * +ol_txrx_vdev_alloc_tcp_node(struct ol_txrx_vdev_t *vdev); + +/** + * ol_tx_pdev_reset_driver_del_ack() - reset driver delayed ack enabled flag + * @ppdev: the data physical device + * + * Return: none + */ +void +ol_tx_pdev_reset_driver_del_ack(struct cdp_soc_t *soc_hdl, uint8_t pdev_id); + +/** + * ol_tx_vdev_set_driver_del_ack_enable() - set driver delayed ack enabled flag + * @soc_hdl: datapath soc handle + * @vdev_id: vdev id + * @rx_packets: number of rx packets + * @time_in_ms: time in ms + * @high_th: high threshold + * @low_th: low threshold + * + * Return: none + */ +void +ol_tx_vdev_set_driver_del_ack_enable(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + unsigned long rx_packets, + uint32_t time_in_ms, + uint32_t high_th, + uint32_t low_th); + +/** + * ol_tx_hl_send_all_tcp_ack() - send all queued tcp ack packets + * @vdev: vdev handle + * + * Return: none + */ +void ol_tx_hl_send_all_tcp_ack(struct ol_txrx_vdev_t *vdev); + +/** + * tcp_del_ack_tasklet() - tasklet function to send ack packets + * @data: vdev handle + * + * Return: none + */ +void tcp_del_ack_tasklet(void *data); + +/** + * ol_tx_get_stream_id() - get stream_id from packet info + * @info: packet info + * + * Return: stream_id + */ +uint16_t ol_tx_get_stream_id(struct packet_info *info); + +/** + * ol_tx_get_packet_info() - update packet info for passed msdu + * @msdu: packet + * @info: packet info + * + * Return: none + */ +void ol_tx_get_packet_info(qdf_nbuf_t msdu, struct packet_info *info); + +/** + * ol_tx_hl_find_and_send_tcp_stream() - find and send tcp stream for passed + * stream info + * @vdev: vdev handle + * @info: packet info + * + * Return: none + */ +void ol_tx_hl_find_and_send_tcp_stream(struct ol_txrx_vdev_t *vdev, + struct packet_info *info); + +/** + * ol_tx_hl_find_and_replace_tcp_ack() - find and replace tcp ack packet for + * passed packet info + * @vdev: vdev handle + * @msdu: packet + * @info: packet info + * + * Return: none + */ +void ol_tx_hl_find_and_replace_tcp_ack(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, + struct packet_info *info); + +/** + * ol_tx_hl_vdev_tcp_del_ack_timer() - delayed ack timer function + * @timer: timer handle + * + * Return: enum + */ +enum qdf_hrtimer_restart_status +ol_tx_hl_vdev_tcp_del_ack_timer(qdf_hrtimer_data_t *timer); + +/** + * ol_tx_hl_del_ack_queue_flush_all() - drop all queued packets + * @vdev: vdev handle + * + * Return: none + */ +void ol_tx_hl_del_ack_queue_flush_all(struct ol_txrx_vdev_t *vdev); + +#else + +static inline +void ol_txrx_vdev_init_tcp_del_ack(struct ol_txrx_vdev_t *vdev) +{ +} + +static inline +void ol_txrx_vdev_deinit_tcp_del_ack(struct ol_txrx_vdev_t *vdev) +{ +} + +static inline +void ol_tx_pdev_reset_driver_del_ack(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ +} + +static inline +void ol_tx_vdev_set_driver_del_ack_enable(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + unsigned long rx_packets, + uint32_t time_in_ms, + uint32_t high_th, + uint32_t low_th) +{ +} + +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE +void ol_tx_vdev_set_bundle_require(uint8_t vdev_id, unsigned long tx_bytes, + uint32_t time_in_ms, uint32_t high_th, + uint32_t low_th); + +void ol_tx_pdev_reset_bundle_require(struct cdp_soc_t *soc_hdl, uint8_t pdev_id); + +#else + +static inline +void ol_tx_vdev_set_bundle_require(uint8_t vdev_id, unsigned long tx_bytes, + uint32_t time_in_ms, uint32_t high_th, + uint32_t low_th) +{ +} + +static inline +void ol_tx_pdev_reset_bundle_require(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ +} +#endif + +#endif /* _OL_TXRX__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_encap.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_encap.c new file mode 100644 index 0000000000000000000000000000000000000000..2fdd09e63c8a2cf694ed481745dfac7a39dcf5cf --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_encap.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2012-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_encap.c + * @brief Provide functions to encap/decap on txrx frames. + * @details + * This file contains functions for data frame encap/decap: + * ol_tx_encap: encap outgoing data frames. + * ol_rx_decap: decap incoming data frames. + */ +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + +#include /* qdf_nbuf_t, etc. */ +#include /* ieee80211_frame */ +#include /* TXRX_ASSERT1 */ +#include /* struct ol_rx_decap_info_t */ + +static inline A_STATUS +ol_tx_copy_native_wifi_header(qdf_nbuf_t msdu, + uint8_t *hdsize, uint8_t *localbuf) +{ + struct ieee80211_frame *wh = + (struct ieee80211_frame *)qdf_nbuf_data(msdu); + if ((wh->i_fc[1] & + IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) { + *hdsize = sizeof(struct ieee80211_frame_addr4); + } else { + *hdsize = sizeof(struct ieee80211_frame); + } + if (qdf_nbuf_len(msdu) < *hdsize) + return A_ERROR; + + qdf_mem_copy(localbuf, wh, *hdsize); + return A_OK; +} + +static inline A_STATUS +ol_tx_encap_from_native_wifi(struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t msdu, + struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + uint8_t localbuf[sizeof(struct ieee80211_qosframe_htc_addr4)]; + struct ieee80211_frame *wh; + uint8_t hdsize, new_hdsize; + struct ieee80211_qoscntl *qos_cntl; + struct ol_txrx_peer_t *peer; + + if (tx_msdu_info->htt.info.frame_type != htt_frm_type_data) + return A_OK; + + peer = tx_msdu_info->peer; + /* + * for unicast,the peer should not be NULL. + * for multicast, the peer is AP. + */ + if (tx_msdu_info->htt.info.is_unicast && peer->qos_capable) { + if (A_OK != + ol_tx_copy_native_wifi_header(msdu, &hdsize, localbuf)) + return A_ERROR; + wh = (struct ieee80211_frame *)localbuf; + + /*add qos cntl */ + qos_cntl = (struct ieee80211_qoscntl *)(localbuf + hdsize); + qos_cntl->i_qos[0] = + tx_msdu_info->htt.info.ext_tid & IEEE80211_QOS_TID; + +#ifdef NEVERDEFINED + if (wmmParam[ac].wmep_noackPolicy) + qos_cntl->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S; +#endif + + qos_cntl->i_qos[1] = 0; + wh->i_fc[0] |= QDF_IEEE80211_FC0_SUBTYPE_QOS; + /* count for qos field */ + new_hdsize = + hdsize + sizeof(struct ieee80211_qosframe) - + sizeof(struct ieee80211_frame); + + /*add ht control field if needed */ + + /* copy new hd to bd */ + qdf_mem_copy((void *) + htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc, + new_hdsize), localbuf, + new_hdsize); + qdf_nbuf_pull_head(msdu, hdsize); + tx_msdu_info->htt.info.l3_hdr_offset = new_hdsize; + tx_desc->orig_l2_hdr_bytes = hdsize; + } + /* Set Protected Frame bit in MAC header */ + if (vdev->pdev->sw_pf_proc_enable + && tx_msdu_info->htt.action.do_encrypt) { + if (tx_desc->orig_l2_hdr_bytes) { + wh = (struct ieee80211_frame *) + htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc, + tx_msdu_info->htt.info. + l3_hdr_offset); + } else { + if (A_OK != + ol_tx_copy_native_wifi_header(msdu, &hdsize, + localbuf)) + return A_ERROR; + wh = (struct ieee80211_frame *) + htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc, + hdsize); + qdf_mem_copy((void *)wh, localbuf, hdsize); + qdf_nbuf_pull_head(msdu, hdsize); + tx_msdu_info->htt.info.l3_hdr_offset = hdsize; + tx_desc->orig_l2_hdr_bytes = hdsize; + } + wh->i_fc[1] |= IEEE80211_FC1_WEP; + } + return A_OK; +} + +static inline A_STATUS +ol_tx_encap_from_8023(struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t msdu, struct ol_txrx_msdu_info_t *tx_msdu_info) +{ + uint8_t localbuf[sizeof(struct ieee80211_qosframe_htc_addr4) + + sizeof(struct llc_snap_hdr_t)]; + struct llc_snap_hdr_t *llc_hdr; + struct ethernet_hdr_t *eth_hdr; + struct ieee80211_frame *wh; + uint8_t hdsize, new_l2_hdsize, new_hdsize; + struct ieee80211_qoscntl *qos_cntl; + const uint8_t ethernet_II_llc_snap_header_prefix[] = { + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + struct ol_txrx_peer_t *peer; + uint16_t ether_type; + + if (tx_msdu_info->htt.info.frame_type != htt_frm_type_data) + return A_OK; + + /* + * for unicast,the peer should not be NULL. + * for multicast, the peer is AP. + */ + peer = tx_msdu_info->peer; + + eth_hdr = (struct ethernet_hdr_t *)qdf_nbuf_data(msdu); + hdsize = sizeof(struct ethernet_hdr_t); + wh = (struct ieee80211_frame *)localbuf; + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; + *(uint16_t *) wh->i_dur = 0; + new_hdsize = 0; + + switch (vdev->opmode) { + case wlan_op_mode_ap: + /* DA , BSSID , SA */ + qdf_mem_copy(wh->i_addr1, eth_hdr->dest_addr, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(wh->i_addr2, &vdev->mac_addr.raw, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(wh->i_addr3, eth_hdr->src_addr, + QDF_MAC_ADDR_SIZE); + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + new_hdsize = sizeof(struct ieee80211_frame); + break; + case wlan_op_mode_ibss: + /* DA, SA, BSSID */ + qdf_mem_copy(wh->i_addr1, eth_hdr->dest_addr, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(wh->i_addr2, eth_hdr->src_addr, + QDF_MAC_ADDR_SIZE); + /* need to check the bssid behaviour for IBSS vdev */ + qdf_mem_copy(wh->i_addr3, &vdev->mac_addr.raw, + QDF_MAC_ADDR_SIZE); + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + new_hdsize = sizeof(struct ieee80211_frame); + break; + case wlan_op_mode_sta: + /* BSSID, SA , DA */ + qdf_mem_copy(wh->i_addr1, &peer->mac_addr.raw, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(wh->i_addr2, eth_hdr->src_addr, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(wh->i_addr3, eth_hdr->dest_addr, + QDF_MAC_ADDR_SIZE); + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + new_hdsize = sizeof(struct ieee80211_frame); + break; + case wlan_op_mode_monitor: + default: + return A_ERROR; + } + /*add qos cntl */ + if (tx_msdu_info->htt.info.is_unicast && peer->qos_capable) { + qos_cntl = (struct ieee80211_qoscntl *)(localbuf + new_hdsize); + qos_cntl->i_qos[0] = + tx_msdu_info->htt.info.ext_tid & IEEE80211_QOS_TID; + wh->i_fc[0] |= QDF_IEEE80211_FC0_SUBTYPE_QOS; +#ifdef NEVERDEFINED + if (wmmParam[ac].wmep_noackPolicy) + qos_cntl->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S; +#endif + qos_cntl->i_qos[1] = 0; + new_hdsize += sizeof(struct ieee80211_qoscntl); + + /*add ht control field if needed */ + } + /* Set Protected Frame bit in MAC header */ + if (vdev->pdev->sw_pf_proc_enable + && tx_msdu_info->htt.action.do_encrypt) { + wh->i_fc[1] |= IEEE80211_FC1_WEP; + } + new_l2_hdsize = new_hdsize; + /* add llc snap if needed */ + if (vdev->pdev->sw_tx_llc_proc_enable) { + llc_hdr = (struct llc_snap_hdr_t *)(localbuf + new_hdsize); + ether_type = + (eth_hdr->ethertype[0] << 8) | (eth_hdr->ethertype[1]); + if (ether_type >= ETH_P_802_3_MIN) { + qdf_mem_copy(llc_hdr, + ethernet_II_llc_snap_header_prefix, + sizeof + (ethernet_II_llc_snap_header_prefix)); + if (ether_type == ETHERTYPE_AARP + || ether_type == ETHERTYPE_IPX) { + llc_hdr->org_code[2] = BTEP_SNAP_ORGCODE_2; + /* 0xf8; bridge tunnel header */ + } + llc_hdr->ethertype[0] = eth_hdr->ethertype[0]; + llc_hdr->ethertype[1] = eth_hdr->ethertype[1]; + new_hdsize += sizeof(struct llc_snap_hdr_t); + } else { + /* + * llc ready, and it's in payload pdu, + * do we need to move to BD pdu? + */ + } + } + qdf_mem_copy((void *) + htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc, + new_l2_hdsize), localbuf, + new_hdsize); + qdf_nbuf_pull_head(msdu, hdsize); + tx_msdu_info->htt.info.l3_hdr_offset = new_l2_hdsize; + tx_desc->orig_l2_hdr_bytes = hdsize; + return A_OK; +} + +A_STATUS +ol_tx_encap(struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t msdu, struct ol_txrx_msdu_info_t *msdu_info) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + + if (pdev->frame_format == wlan_frm_fmt_native_wifi) { + return ol_tx_encap_from_native_wifi(vdev, tx_desc, msdu, + msdu_info); + } else if (pdev->frame_format == wlan_frm_fmt_802_3) { + return ol_tx_encap_from_8023(vdev, tx_desc, msdu, msdu_info); + } + + /* todo for other types */ + return A_ERROR; +} + +static inline void +ol_rx_decap_to_native_wifi(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, + struct ol_rx_decap_info_t *info, + struct ethernet_hdr_t *ethr_hdr) +{ + struct ieee80211_frame_addr4 *wh; + uint16_t hdsize; + + /* + * we need to remove Qos control field and HT control. + * MSFT: http://msdn.microsoft.com/en-us/library/windows/ + * hardware/ff552608(v=vs.85).aspx + */ + wh = (struct ieee80211_frame_addr4 *)info->hdr; + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == + IEEE80211_FC1_DIR_DSTODS) + hdsize = sizeof(struct ieee80211_frame_addr4); + else + hdsize = sizeof(struct ieee80211_frame); + + wh = (struct ieee80211_frame_addr4 *)qdf_nbuf_push_head(msdu, hdsize); + TXRX_ASSERT2(wh); + TXRX_ASSERT2(hdsize <= info->hdr_len); + qdf_mem_copy((uint8_t *) wh, info->hdr, hdsize); + + /* amsdu subfrm handling if ethr_hdr is not NULL */ + if (ethr_hdr) { + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + qdf_mem_copy(wh->i_addr1, ethr_hdr->dest_addr, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(wh->i_addr2, ethr_hdr->src_addr, + QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_TODS: + qdf_mem_copy(wh->i_addr2, ethr_hdr->src_addr, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(wh->i_addr3, ethr_hdr->dest_addr, + QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_FROMDS: + qdf_mem_copy(wh->i_addr1, ethr_hdr->dest_addr, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(wh->i_addr3, ethr_hdr->src_addr, + QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_DSTODS: + qdf_mem_copy(wh->i_addr3, ethr_hdr->dest_addr, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(wh->i_addr4, ethr_hdr->src_addr, + QDF_MAC_ADDR_SIZE); + break; + } + } + if (IEEE80211_QOS_HAS_SEQ(wh)) { + if (wh->i_fc[1] & IEEE80211_FC1_ORDER) + wh->i_fc[1] &= ~IEEE80211_FC1_ORDER; + wh->i_fc[0] &= ~QDF_IEEE80211_FC0_SUBTYPE_QOS; + } +} + +static inline void +ol_rx_decap_to_8023(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, + struct ol_rx_decap_info_t *info, + struct ethernet_hdr_t *ethr_hdr) +{ + struct llc_snap_hdr_t *llc_hdr; + uint16_t ether_type; + uint16_t l2_hdr_space; + struct ieee80211_frame_addr4 *wh; + uint8_t local_buf[ETHERNET_HDR_LEN]; + uint8_t *buf; + + /* + * populate Ethernet header, + * if ethr_hdr is null, rx frame is 802.11 format(HW ft disabled) + * if ethr_hdr is not null, rx frame is "subfrm of amsdu". + */ + buf = (uint8_t *) qdf_nbuf_data(msdu); + llc_hdr = (struct llc_snap_hdr_t *)buf; + ether_type = (llc_hdr->ethertype[0] << 8) | llc_hdr->ethertype[1]; + /* do llc remove if needed */ + l2_hdr_space = 0; + if (IS_SNAP(llc_hdr)) { + if (IS_BTEP(llc_hdr)) { + /* remove llc */ + l2_hdr_space += sizeof(struct llc_snap_hdr_t); + llc_hdr = NULL; + } else if (IS_RFC1042(llc_hdr)) { + if (!(ether_type == ETHERTYPE_AARP || + ether_type == ETHERTYPE_IPX)) { + /* remove llc */ + l2_hdr_space += sizeof(struct llc_snap_hdr_t); + llc_hdr = NULL; + } + } + } + if (l2_hdr_space > ETHERNET_HDR_LEN) + buf = qdf_nbuf_pull_head(msdu, l2_hdr_space - ETHERNET_HDR_LEN); + else if (l2_hdr_space < ETHERNET_HDR_LEN) + buf = qdf_nbuf_push_head(msdu, ETHERNET_HDR_LEN - l2_hdr_space); + + /* normal msdu(non-subfrm of A-MSDU) if ethr_hdr is null */ + if (!ethr_hdr) { + /* + * mpdu hdr should be present in info, + * re-create ethr_hdr based on mpdu hdr + */ + TXRX_ASSERT2(info->hdr_len != 0); + wh = (struct ieee80211_frame_addr4 *)info->hdr; + ethr_hdr = (struct ethernet_hdr_t *)local_buf; + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr1, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr2, + QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_TODS: + qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr3, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr2, + QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_FROMDS: + qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr1, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr3, + QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_DSTODS: + qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr3, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr4, + QDF_MAC_ADDR_SIZE); + break; + } + } + if (!llc_hdr) { + ethr_hdr->ethertype[0] = (ether_type >> 8) & 0xff; + ethr_hdr->ethertype[1] = (ether_type) & 0xff; + } else { + uint32_t pktlen = + qdf_nbuf_len(msdu) - sizeof(ethr_hdr->ethertype); + TXRX_ASSERT2(pktlen <= ETHERNET_MTU); + ether_type = (uint16_t) pktlen; + ether_type = qdf_nbuf_len(msdu) - sizeof(struct ethernet_hdr_t); + ethr_hdr->ethertype[0] = (ether_type >> 8) & 0xff; + ethr_hdr->ethertype[1] = (ether_type) & 0xff; + } + qdf_mem_copy(buf, ethr_hdr, ETHERNET_HDR_LEN); +} + +static inline A_STATUS +ol_rx_decap_subfrm_amsdu(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, struct ol_rx_decap_info_t *info) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + uint8_t *subfrm_hdr; + uint8_t localbuf[ETHERNET_HDR_LEN]; + struct ethernet_hdr_t *ether_hdr = (struct ethernet_hdr_t *)localbuf; + + subfrm_hdr = (uint8_t *) qdf_nbuf_data(msdu); + if (pdev->frame_format == wlan_frm_fmt_native_wifi) { + /* decap to native wifi */ + qdf_mem_copy(ether_hdr, subfrm_hdr, ETHERNET_HDR_LEN); + qdf_nbuf_pull_head(msdu, ETHERNET_HDR_LEN); + ol_rx_decap_to_native_wifi(vdev, msdu, info, ether_hdr); + } else if (pdev->frame_format == wlan_frm_fmt_802_3) { + if (pdev->sw_rx_llc_proc_enable) { + /* remove llc snap hdr if it's necessary according to + * 802.11 table P-3 + */ + qdf_mem_copy(ether_hdr, subfrm_hdr, ETHERNET_HDR_LEN); + qdf_nbuf_pull_head(msdu, ETHERNET_HDR_LEN); + ol_rx_decap_to_8023(vdev, msdu, info, ether_hdr); + } else { + /* subfrm of A-MSDU is already in 802.3 format. + * if target HW or FW has done LLC rmv process, + * we do nothing here. + */ + } + } else { + /* todo for othertype */ + } + return A_OK; + +} + +static inline A_STATUS +ol_rx_decap_msdu(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu, struct ol_rx_decap_info_t *info) +{ + struct ol_txrx_pdev_t *pdev = vdev->pdev; + struct ieee80211_frame *wh; + + wh = (struct ieee80211_frame *)qdf_nbuf_data(msdu); + + if (pdev->frame_format == wlan_frm_fmt_native_wifi) { + /* Decap to native wifi because according to MSFT( + * MSFT: http://msdn.microsoft.com/en-us/library/windows/ + * hardware/ff552608(v=vs.85).aspx), + * we need to remove Qos and HTC field before indicate to OS. + */ + if (IEEE80211_QOS_HAS_SEQ(wh)) { + info->hdr_len = ol_txrx_ieee80211_hdrsize(wh); + TXRX_ASSERT2(info->hdr_len <= sizeof(info->hdr)); + qdf_mem_copy(info->hdr, /* use info->hdr as temp buf. */ + wh, info->hdr_len); + qdf_nbuf_pull_head(msdu, info->hdr_len); + ol_rx_decap_to_native_wifi(vdev, msdu, info, NULL); + /* 802.11 hdr^ eth_hdr^ */ + } + } else if (pdev->frame_format == wlan_frm_fmt_802_3) { + if (pdev->sw_rx_llc_proc_enable) { + info->hdr_len = ol_txrx_ieee80211_hdrsize(wh); + TXRX_ASSERT2(info->hdr_len <= sizeof(info->hdr)); + qdf_mem_copy(info->hdr, /* use info->hdr as temp buf. */ + wh, info->hdr_len); + qdf_nbuf_pull_head(msdu, info->hdr_len); + /* remove llc snap hdr if it's necessary according to + * 802.11 table P-3 + */ + ol_rx_decap_to_8023(vdev, msdu, info, /* 802.11 hdr */ + NULL); /* ethernet hdr */ + } else { + /* Subfrm of A-MSDU is already in 802.3 format. + * And if target HW or FW has done LLC rmv process ( + * sw_rx_lc_proc_enable == 0), we do nothing here. + */ + } + } else { + /* todo for othertype */ + } + return A_OK; + +} + +A_STATUS +ol_rx_decap(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + qdf_nbuf_t msdu, struct ol_rx_decap_info_t *info) +{ + A_STATUS status; + uint8_t *mpdu_hdr; + + if (!info->is_subfrm) { + if (info->is_msdu_cmpl_mpdu && !info->is_first_subfrm) { + /* It's normal MSDU. */ + } else { + /* + * It's a first subfrm of A-MSDU and + * may also be the last subfrm of A-MSDU + */ + info->is_subfrm = 1; + info->hdr_len = 0; + if (vdev->pdev->sw_subfrm_hdr_recovery_enable) { + /* we save the first subfrm mpdu hdr for + * subsequent subfrm 802.11 header recovery + * in certain chip(such as Riva). + */ + mpdu_hdr = qdf_nbuf_data(msdu); + info->hdr_len = + ol_txrx_ieee80211_hdrsize(mpdu_hdr); + TXRX_ASSERT2(info->hdr_len <= + sizeof(info->hdr)); + qdf_mem_copy(info->hdr, mpdu_hdr, + info->hdr_len); + qdf_nbuf_pull_head(msdu, info->hdr_len); + } + } + } + + if (info->is_subfrm && vdev->pdev->sw_subfrm_hdr_recovery_enable) { + /* + * This case is enabled for some HWs (such as Riva). The HW + * de-aggregate doesn't have capability to generate 802.11 + * header for non-first subframe of A-MSDU. That means sw needs + * to cache the first subfrm mpdu header to generate the + * subsequent subfrm's 802.11 header. + */ + TXRX_ASSERT2(info->hdr_len != 0); + status = ol_rx_decap_subfrm_amsdu(vdev, msdu, info); + } else { + status = ol_rx_decap_msdu(vdev, msdu, info); + } + + if (info->is_msdu_cmpl_mpdu) + info->is_subfrm = info->is_first_subfrm = info->hdr_len = 0; + + return status; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_encap.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_encap.h new file mode 100644 index 0000000000000000000000000000000000000000..360717e7a1dd9e95edfca6f78cb4b284d2eed14e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_encap.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012, 2014-2016, 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_encap.h + * @brief definitions for txrx encap/decap function and struct + */ +#ifndef _OL_TXRX_ENCAP__H_ +#define _OL_TXRX_ENCAP__H_ + +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + +#include /* qdf_nbuf_t */ +#include /* ieee80211_qosframe_htc_addr4 */ +#include /* ol_txrx_vdev_t, etc. */ + +/** + * @brief Encap outgoing frm from OS dependent format to Target + * acceptable frm format + * @details + * For native wifi format, the function will add Qos control field + * based on peer's QOS capbabilities . + * For 802.3 format, the function will transform to 802.11 format + * with or without QOS control field based on peer's QOS capabilities. + * @param vdev - handle to vdev object + * @param tx_desc - tx desc struct,some fields will be updated. + * @param msdu - qdf_nbuf_t + * @param msdu_info - information from tx classification. + * @return + * A_OK: encap operation successful + * other: operation failed,the msdu need be dropped. + */ +A_STATUS +ol_tx_encap(struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t msdu, struct ol_txrx_msdu_info_t *msdu_info); + +struct ol_rx_decap_info_t { + uint8_t hdr[sizeof(struct ieee80211_qosframe_htc_addr4)]; + int hdr_len; + uint8_t is_subfrm:1, is_first_subfrm:1, is_msdu_cmpl_mpdu:1; +}; + +/** + * @brief decap incoming frm from Target to Host OS + * acceptable frm format + * @details + * For native wifi format, the function will remove Qos control field + * and HT control field if any. + * For 802.3 format, the function will will do llc snap header process + * if Target haven't done that. + * @param vdev - handle to vdev object + * @param peer - the peer object. + * @param msdu - qdf_nbuf_t + * @param info - ol_rx_decap_info_t: context info for decap + * @return + * A_OK: decap operation successful + * other: operation failed,the msdu need be dropped. + */ +A_STATUS +ol_rx_decap(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + qdf_nbuf_t msdu, struct ol_rx_decap_info_t *info); + +static inline A_STATUS +OL_TX_ENCAP(struct ol_txrx_vdev_t *vdev, + struct ol_tx_desc_t *tx_desc, + qdf_nbuf_t msdu, struct ol_txrx_msdu_info_t *msdu_info) +{ + if (vdev->pdev->sw_tx_encap) + return ol_tx_encap(vdev, tx_desc, msdu, msdu_info); + return A_OK; +} + +static inline A_STATUS +OL_RX_DECAP(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + qdf_nbuf_t msdu, struct ol_rx_decap_info_t *info) +{ + if (vdev->pdev->sw_rx_decap) + return ol_rx_decap(vdev, peer, msdu, info); + return A_OK; +} + +#define OL_TX_RESTORE_HDR(__tx_desc, __msdu) \ + do { \ + if (__tx_desc->orig_l2_hdr_bytes != 0) \ + qdf_nbuf_push_head(__msdu, \ + __tx_desc->orig_l2_hdr_bytes); \ + } while (0) +#else +#define OL_TX_ENCAP(vdev, tx_desc, msdu, msdu_info) A_OK +#define OL_RX_DECAP(vdev, peer, msdu, info) A_OK +#define OL_TX_RESTORE_HDR(__tx_desc, __msdu) +#endif +#endif /* _OL_TXRX_ENCAP__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_event.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_event.c new file mode 100644 index 0000000000000000000000000000000000000000..d9ff0c30fe818bef9f4dcdc58cfc0f8cfbbc583a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_event.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ol_txrx_types.h" +#include "ol_txrx.h" + +static inline wdi_event_subscribe *wdi_event_next_sub(wdi_event_subscribe * + wdi_sub) +{ + if (!wdi_sub) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Invalid subscriber in %s\n", __func__); + return NULL; + } + return wdi_sub->priv.next; +} + +static inline void +wdi_event_del_subs(wdi_event_subscribe *wdi_sub, int event_index) +{ + wdi_event_notify deallocate_sub; + + while (wdi_sub) { + wdi_event_subscribe *next = wdi_event_next_sub(wdi_sub); + /* + * Context is NULL for static allocation of subs + * In dynamic allocation case notify the user + */ + if (wdi_sub->context) { + deallocate_sub = wdi_sub->context; + deallocate_sub(WDI_EVENT_SUB_DEALLOCATE, + WDI_EVENT_BASE + event_index); + } + wdi_sub = next; + } + /* qdf_mem_free(wdi_sub); */ +} + +static inline void +wdi_event_iter_sub(struct ol_txrx_pdev_t *pdev, + uint32_t event_index, + wdi_event_subscribe *wdi_sub, void *data) +{ + enum WDI_EVENT event = event_index + WDI_EVENT_BASE; + + if (wdi_sub) { + do { + wdi_sub->callback(pdev, event, data, 0, 0); + } while ((wdi_sub = wdi_event_next_sub(wdi_sub))); + } +} + +void +wdi_event_handler(enum WDI_EVENT event, + uint8_t pdev_id, void *data) +{ + uint32_t event_index; + wdi_event_subscribe *wdi_sub; + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle txrx_pdev; + + /* + * Input validation + */ + if (!event) { + ol_txrx_err("Invalid WDI event"); + return; + } + if (!soc) { + ol_txrx_err("Invalid soc"); + return; + } + + txrx_pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + if (!txrx_pdev) { + ol_txrx_err("Invalid pdev"); + return; + } + /* + * There can be NULL data, so no validation for the data + * Subscribers must do the sanity based on the requirements + */ + event_index = event - WDI_EVENT_BASE; + + wdi_sub = txrx_pdev->wdi_event_list[event_index]; + + /* Find the subscriber */ + wdi_event_iter_sub(txrx_pdev, event_index, wdi_sub, data); +} + +int +wdi_event_sub(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + wdi_event_subscribe *pevent_cb_sub, uint32_t event) +{ + uint32_t event_index; + wdi_event_subscribe *wdi_sub; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle txrx_pdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + wdi_event_subscribe *event_cb_sub = pevent_cb_sub; + + /* Input validation */ + if (!txrx_pdev || !txrx_pdev->wdi_event_list) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Invalid txrx_pdev or wdi_event_list in %s", + __func__); + return -EINVAL; + } + if (!event_cb_sub) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Invalid callback in %s", __func__); + return -EINVAL; + } + if ((!event) || (event >= WDI_EVENT_LAST) || (event < WDI_EVENT_BASE)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Invalid event in %s", __func__); + return -EINVAL; + } + /* Input validation */ + event_index = event - WDI_EVENT_BASE; + + wdi_sub = txrx_pdev->wdi_event_list[event_index]; + /* + * Check if it is the first subscriber of the event + */ + if (!wdi_sub) { + wdi_sub = event_cb_sub; + wdi_sub->priv.next = NULL; + wdi_sub->priv.prev = NULL; + txrx_pdev->wdi_event_list[event_index] = wdi_sub; + return 0; + } + event_cb_sub->priv.next = wdi_sub; + event_cb_sub->priv.prev = NULL; + wdi_sub->priv.prev = event_cb_sub; + txrx_pdev->wdi_event_list[event_index] = event_cb_sub; + + return 0; +} + +int +wdi_event_unsub(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + wdi_event_subscribe *pevent_cb_sub, uint32_t event) +{ + uint32_t event_index = event - WDI_EVENT_BASE; + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle txrx_pdev = ol_txrx_get_pdev_from_pdev_id(soc, + pdev_id); + + wdi_event_subscribe *event_cb_sub = pevent_cb_sub; + + /* Input validation */ + if (!txrx_pdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Invalid txrx_pdev in %s", __func__); + return -EINVAL; + } + + /* Input validation */ + if (!event_cb_sub) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Invalid callback in %s", __func__); + return -EINVAL; + } + if (!event_cb_sub->priv.prev) { + txrx_pdev->wdi_event_list[event_index] = + event_cb_sub->priv.next; + } else { + event_cb_sub->priv.prev->priv.next = event_cb_sub->priv.next; + } + if (event_cb_sub->priv.next) + event_cb_sub->priv.next->priv.prev = event_cb_sub->priv.prev; + + /* qdf_mem_free(event_cb_sub); */ + + return 0; +} + +A_STATUS wdi_event_attach(struct ol_txrx_pdev_t *txrx_pdev) +{ + /* Input validation */ + if (!txrx_pdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Invalid device in %s\nWDI event attach failed", + __func__); + return A_ERROR; + } + /* Separate subscriber list for each event */ + txrx_pdev->wdi_event_list = (wdi_event_subscribe **) + qdf_mem_malloc( + sizeof(wdi_event_subscribe *) * + WDI_NUM_EVENTS); + if (!txrx_pdev->wdi_event_list) + return A_NO_MEMORY; + + return A_OK; +} + +A_STATUS wdi_event_detach(struct ol_txrx_pdev_t *txrx_pdev) +{ + int i; + wdi_event_subscribe *wdi_sub; + + if (!txrx_pdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Invalid device in %s\nWDI detach failed", + __func__); + return A_ERROR; + } + if (!txrx_pdev->wdi_event_list) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: wdi_event_list is NULL", __func__); + return A_ERROR; + } + + for (i = 0; i < WDI_NUM_EVENTS; i++) { + wdi_sub = txrx_pdev->wdi_event_list[i]; + if (wdi_sub) { + /* Delete all the subscribers */ + wdi_event_del_subs(wdi_sub, i); + } + } + /* txrx_pdev->wdi_event_list would be non-null */ + qdf_mem_free(txrx_pdev->wdi_event_list); + txrx_pdev->wdi_event_list = NULL; + return A_OK; +} diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_flow_control.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_flow_control.c new file mode 100644 index 0000000000000000000000000000000000000000..bec7b1fd6b0e4377a1818d96460e43f60797f052 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_flow_control.c @@ -0,0 +1,1468 @@ +/* + * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* OS abstraction libraries */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_atomic_read, etc. */ +#include /* qdf_unlikely */ + +/* APIs for other modules */ +#include /* HTT_TX_EXT_TID_MGMT */ +#include /* htt_tx_desc_tid */ + +/* internal header files relevant for all systems */ +#include /* TXRX_ASSERT1 */ +#include /* ol_tx_desc */ +#include /* ol_tx_send */ +#include /* ol_txrx_get_vdev_from_vdev_id */ + +/* internal header files relevant only for HL systems */ +#include /* ol_tx_enqueue */ + +/* internal header files relevant only for specific systems (Pronto) */ +#include /* OL_TX_ENCAP, etc */ +#include +#include +#include +#define INVALID_FLOW_ID 0xFF +#define MAX_INVALID_BIN 3 + +#ifdef QCA_LL_TX_FLOW_GLOBAL_MGMT_POOL +#define TX_FLOW_MGMT_POOL_ID 0xEF +#define TX_FLOW_MGMT_POOL_SIZE 32 + +/** + * ol_tx_register_global_mgmt_pool() - register global pool for mgmt packets + * @pdev: pdev handler + * + * Return: none + */ +static void +ol_tx_register_global_mgmt_pool(struct ol_txrx_pdev_t *pdev) +{ + pdev->mgmt_pool = ol_tx_create_flow_pool(TX_FLOW_MGMT_POOL_ID, + TX_FLOW_MGMT_POOL_SIZE); + if (!pdev->mgmt_pool) + ol_txrx_err("Management pool creation failed\n"); +} + +/** + * ol_tx_deregister_global_mgmt_pool() - Deregister global pool for mgmt packets + * @pdev: pdev handler + * + * Return: none + */ +static void +ol_tx_deregister_global_mgmt_pool(struct ol_txrx_pdev_t *pdev) +{ + ol_tx_dec_pool_ref(pdev->mgmt_pool, false); +} +#else +static inline void +ol_tx_register_global_mgmt_pool(struct ol_txrx_pdev_t *pdev) +{ +} +static inline void +ol_tx_deregister_global_mgmt_pool(struct ol_txrx_pdev_t *pdev) +{ +} +#endif + +bool ol_txrx_fwd_desc_thresh_check(struct ol_txrx_vdev_t *txrx_vdev) +{ + struct ol_tx_flow_pool_t *pool; + bool enough_desc_flag; + + if (!txrx_vdev) + return false; + + pool = txrx_vdev->pool; + + if (!pool) + return false; + + qdf_spin_lock_bh(&pool->flow_pool_lock); + enough_desc_flag = (pool->avail_desc < (pool->stop_th + + OL_TX_NON_FWD_RESERVE)) + ? false : true; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + return enough_desc_flag; +} + +/** + * ol_tx_set_desc_global_pool_size() - set global pool size + * @num_msdu_desc: total number of descriptors + * + * Return: none + */ +void ol_tx_set_desc_global_pool_size(uint32_t num_msdu_desc) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + qdf_print("pdev is NULL"); + return; + } + pdev->num_msdu_desc = num_msdu_desc; + if (!ol_tx_get_is_mgmt_over_wmi_enabled()) + pdev->num_msdu_desc += TX_FLOW_MGMT_POOL_SIZE; + ol_txrx_info_high("Global pool size: %d\n", pdev->num_msdu_desc); +} + +/** + * ol_tx_get_total_free_desc() - get total free descriptors + * @pdev: pdev handle + * + * Return: total free descriptors + */ +uint32_t ol_tx_get_total_free_desc(struct ol_txrx_pdev_t *pdev) +{ + struct ol_tx_flow_pool_t *pool = NULL; + uint32_t free_desc; + + free_desc = pdev->tx_desc.num_free; + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_FOREACH(pool, &pdev->tx_desc.flow_pool_list, + flow_pool_list_elem) { + qdf_spin_lock_bh(&pool->flow_pool_lock); + free_desc += pool->avail_desc; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + return free_desc; +} + +/** + * ol_tx_register_flow_control() - Register fw based tx flow control + * @pdev: pdev handle + * + * Return: none + */ +void ol_tx_register_flow_control(struct ol_txrx_pdev_t *pdev) +{ + qdf_spinlock_create(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_INIT(&pdev->tx_desc.flow_pool_list); + + if (!ol_tx_get_is_mgmt_over_wmi_enabled()) + ol_tx_register_global_mgmt_pool(pdev); +} + +/** + * ol_tx_deregister_flow_control() - Deregister fw based tx flow control + * @pdev: pdev handle + * + * Return: none + */ +void ol_tx_deregister_flow_control(struct ol_txrx_pdev_t *pdev) +{ + int i = 0; + struct ol_tx_flow_pool_t *pool = NULL; + struct cdp_soc_t *soc; + + if (!ol_tx_get_is_mgmt_over_wmi_enabled()) + ol_tx_deregister_global_mgmt_pool(pdev); + + soc = cds_get_context(QDF_MODULE_ID_SOC); + + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + while (!TAILQ_EMPTY(&pdev->tx_desc.flow_pool_list)) { + pool = TAILQ_FIRST(&pdev->tx_desc.flow_pool_list); + if (!pool) + break; + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + ol_txrx_info("flow pool list is not empty %d!!!\n", i++); + + if (i == 1) + ol_tx_dump_flow_pool_info(soc); + + ol_tx_dec_pool_ref(pool, true); + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + qdf_spinlock_destroy(&pdev->tx_desc.flow_pool_list_lock); +} + +/** + * ol_tx_delete_flow_pool() - delete flow pool + * @pool: flow pool pointer + * @force: free pool forcefully + * + * Delete flow_pool if all tx descriptors are available. + * Otherwise put it in FLOW_POOL_INVALID state. + * If force is set then pull all available descriptors to + * global pool. + * + * Return: 0 for success or error + */ +static int ol_tx_delete_flow_pool(struct ol_tx_flow_pool_t *pool, bool force) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + uint16_t i, size; + union ol_tx_desc_list_elem_t *temp_list = NULL; + struct ol_tx_desc_t *tx_desc = NULL; + + if (!pool) { + ol_txrx_err("pool is NULL"); + QDF_ASSERT(0); + return -ENOMEM; + } + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + QDF_ASSERT(0); + return -ENOMEM; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + QDF_ASSERT(0); + return -ENOMEM; + } + + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->avail_desc == pool->flow_pool_size || force == true) + pool->status = FLOW_POOL_INACTIVE; + else + pool->status = FLOW_POOL_INVALID; + + /* Take all free descriptors and put it in temp_list */ + temp_list = pool->freelist; + size = pool->avail_desc; + pool->freelist = NULL; + pool->avail_desc = 0; + + if (pool->status == FLOW_POOL_INACTIVE) { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + /* Free flow_pool */ + qdf_spinlock_destroy(&pool->flow_pool_lock); + qdf_mem_free(pool); + } else { /* FLOW_POOL_INVALID case*/ + pool->flow_pool_size -= size; + pool->flow_pool_id = INVALID_FLOW_ID; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + ol_tx_inc_pool_ref(pool); + + pdev->tx_desc.num_invalid_bin++; + ol_txrx_info("invalid pool created %d", + pdev->tx_desc.num_invalid_bin); + if (pdev->tx_desc.num_invalid_bin > MAX_INVALID_BIN) + ASSERT(0); + + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_INSERT_TAIL(&pdev->tx_desc.flow_pool_list, pool, + flow_pool_list_elem); + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + } + + /* put free descriptors to global pool */ + qdf_spin_lock_bh(&pdev->tx_mutex); + for (i = 0; i < size; i++) { + tx_desc = &temp_list->tx_desc; + temp_list = temp_list->next; + + ol_tx_put_desc_global_pool(pdev, tx_desc); + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + + ol_tx_distribute_descs_to_deficient_pools_from_global_pool(); + + return 0; +} + +QDF_STATUS ol_tx_inc_pool_ref(struct ol_tx_flow_pool_t *pool) +{ + if (!pool) { + ol_txrx_err("flow pool is NULL"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock_bh(&pool->flow_pool_lock); + qdf_atomic_inc(&pool->ref_cnt); + qdf_spin_unlock_bh(&pool->flow_pool_lock); + ol_txrx_dbg("pool %pK, ref_cnt %x", + pool, qdf_atomic_read(&pool->ref_cnt)); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_tx_dec_pool_ref(struct ol_tx_flow_pool_t *pool, bool force) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (!pool) { + ol_txrx_err("flow pool is NULL"); + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (qdf_atomic_dec_and_test(&pool->ref_cnt)) { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + TAILQ_REMOVE(&pdev->tx_desc.flow_pool_list, pool, + flow_pool_list_elem); + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + ol_txrx_dbg("Deleting pool %pK", pool); + ol_tx_delete_flow_pool(pool, force); + } else { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + ol_txrx_dbg("pool %pK, ref_cnt %x", + pool, qdf_atomic_read(&pool->ref_cnt)); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_tx_flow_pool_status_to_str() - convert flow pool status to string + * @status - flow pool status + * + * Returns: String corresponding to flow pool status + */ +static const char *ol_tx_flow_pool_status_to_str + (enum flow_pool_status status) +{ + switch (status) { + CASE_RETURN_STRING(FLOW_POOL_ACTIVE_UNPAUSED); + CASE_RETURN_STRING(FLOW_POOL_ACTIVE_PAUSED); + CASE_RETURN_STRING(FLOW_POOL_NON_PRIO_PAUSED); + CASE_RETURN_STRING(FLOW_POOL_INVALID); + CASE_RETURN_STRING(FLOW_POOL_INACTIVE); + default: + return "unknown"; + } +} + +void ol_tx_dump_flow_pool_info_compact(struct ol_txrx_pdev_t *pdev) +{ + char *comb_log_str; + int bytes_written = 0; + uint32_t free_size; + struct ol_tx_flow_pool_t *pool = NULL; + + free_size = WLAN_MAX_VDEVS * 100 + 100; + comb_log_str = qdf_mem_malloc(free_size); + if (!comb_log_str) + return; + + bytes_written = snprintf(&comb_log_str[bytes_written], free_size, + "G:(%d,%d) ", + pdev->tx_desc.pool_size, + pdev->tx_desc.num_free); + + free_size -= bytes_written; + + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_FOREACH(pool, &pdev->tx_desc.flow_pool_list, + flow_pool_list_elem) { + qdf_spin_lock_bh(&pool->flow_pool_lock); + bytes_written += snprintf(&comb_log_str[bytes_written], + free_size, "| %d (%d,%d)", + pool->flow_pool_id, + pool->flow_pool_size, + pool->avail_desc); + free_size -= bytes_written; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + qdf_nofl_debug("STATS | FC: %s", comb_log_str); + qdf_mem_free(comb_log_str); +} + +/** + * ol_tx_dump_flow_pool_info() - dump global_pool and flow_pool info + * @soc_hdl: cdp_soc context, required only in lithium_dp flow control. + * + * Return: none + */ +void ol_tx_dump_flow_pool_info(struct cdp_soc_t *soc_hdl) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev; + struct ol_tx_flow_pool_t *pool = NULL, *pool_prev = NULL; + struct ol_tx_flow_pool_t tmp_pool; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + QDF_ASSERT(0); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("ERROR: pdev NULL"); + QDF_ASSERT(0); /* traceback */ + return; + } + + txrx_nofl_info("Global total %d :: avail %d invalid flow_pool %d ", + pdev->tx_desc.pool_size, + pdev->tx_desc.num_free, + pdev->tx_desc.num_invalid_bin); + + txrx_nofl_info("maps %d pool unmaps %d pool resize %d pkt drops %d", + pdev->pool_stats.pool_map_count, + pdev->pool_stats.pool_unmap_count, + pdev->pool_stats.pool_resize_count, + pdev->pool_stats.pkt_drop_no_pool); + /* + * Nested spin lock. + * Always take in below order. + * flow_pool_list_lock -> flow_pool_lock + */ + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_FOREACH(pool, &pdev->tx_desc.flow_pool_list, + flow_pool_list_elem) { + ol_tx_inc_pool_ref(pool); + qdf_spin_lock_bh(&pool->flow_pool_lock); + qdf_mem_copy(&tmp_pool, pool, sizeof(tmp_pool)); + qdf_spin_unlock_bh(&pool->flow_pool_lock); + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + if (pool_prev) + ol_tx_dec_pool_ref(pool_prev, false); + + txrx_nofl_info("flow_pool_id %d ::", tmp_pool.flow_pool_id); + txrx_nofl_info("status %s flow_id %d flow_type %d", + ol_tx_flow_pool_status_to_str + (tmp_pool.status), + tmp_pool.member_flow_id, tmp_pool.flow_type); + txrx_nofl_info("total %d :: available %d :: deficient %d :: overflow %d :: pkt dropped (no desc) %d", + tmp_pool.flow_pool_size, tmp_pool.avail_desc, + tmp_pool.deficient_desc, + tmp_pool.overflow_desc, + tmp_pool.pkt_drop_no_desc); + txrx_nofl_info("thresh: start %d stop %d prio start %d prio stop %d", + tmp_pool.start_th, tmp_pool.stop_th, + tmp_pool.start_priority_th, + tmp_pool.stop_priority_th); + pool_prev = pool; + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + /* decrement ref count for last pool in list */ + if (pool_prev) + ol_tx_dec_pool_ref(pool_prev, false); + +} + +/** + * ol_tx_clear_flow_pool_stats() - clear flow pool statistics + * + * Return: none + */ +void ol_tx_clear_flow_pool_stats(void) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is null"); + return; + } + qdf_mem_zero(&pdev->pool_stats, sizeof(pdev->pool_stats)); +} + +/** + * ol_tx_move_desc_n() - Move n descriptors from src_pool to dst_pool. + * @src_pool: source pool + * @dst_pool: destination pool + * @desc_move_count: descriptor move count + * + * Return: actual descriptors moved + */ +static int ol_tx_move_desc_n(struct ol_tx_flow_pool_t *src_pool, + struct ol_tx_flow_pool_t *dst_pool, + int desc_move_count) +{ + uint16_t count = 0, i; + struct ol_tx_desc_t *tx_desc; + union ol_tx_desc_list_elem_t *temp_list = NULL; + + /* Take descriptors from source pool and put it in temp_list */ + qdf_spin_lock_bh(&src_pool->flow_pool_lock); + for (i = 0; i < desc_move_count; i++) { + tx_desc = ol_tx_get_desc_flow_pool(src_pool); + ((union ol_tx_desc_list_elem_t *)tx_desc)->next = temp_list; + temp_list = (union ol_tx_desc_list_elem_t *)tx_desc; + + } + qdf_spin_unlock_bh(&src_pool->flow_pool_lock); + + /* Take descriptors from temp_list and put it in destination pool */ + qdf_spin_lock_bh(&dst_pool->flow_pool_lock); + for (i = 0; i < desc_move_count; i++) { + if (dst_pool->deficient_desc) + dst_pool->deficient_desc--; + else + break; + tx_desc = &temp_list->tx_desc; + temp_list = temp_list->next; + ol_tx_put_desc_flow_pool(dst_pool, tx_desc); + count++; + } + qdf_spin_unlock_bh(&dst_pool->flow_pool_lock); + + /* If anything is there in temp_list put it back to source pool */ + qdf_spin_lock_bh(&src_pool->flow_pool_lock); + while (temp_list) { + tx_desc = &temp_list->tx_desc; + temp_list = temp_list->next; + ol_tx_put_desc_flow_pool(src_pool, tx_desc); + } + qdf_spin_unlock_bh(&src_pool->flow_pool_lock); + + return count; +} + + +/** + * ol_tx_distribute_descs_to_deficient_pools() - Distribute descriptors + * @src_pool: source pool + * + * Distribute all descriptors of source pool to all + * deficient pools as per flow_pool_list. + * + * Return: 0 for success + */ +static int +ol_tx_distribute_descs_to_deficient_pools(struct ol_tx_flow_pool_t *src_pool) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + struct ol_tx_flow_pool_t *dst_pool = NULL; + uint16_t desc_count = src_pool->avail_desc; + uint16_t desc_move_count = 0; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return -EINVAL; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return -EINVAL; + } + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_FOREACH(dst_pool, &pdev->tx_desc.flow_pool_list, + flow_pool_list_elem) { + qdf_spin_lock_bh(&dst_pool->flow_pool_lock); + if (dst_pool->deficient_desc) { + desc_move_count = + (dst_pool->deficient_desc > desc_count) ? + desc_count : dst_pool->deficient_desc; + qdf_spin_unlock_bh(&dst_pool->flow_pool_lock); + desc_move_count = ol_tx_move_desc_n(src_pool, + dst_pool, desc_move_count); + desc_count -= desc_move_count; + + qdf_spin_lock_bh(&dst_pool->flow_pool_lock); + if (dst_pool->status == FLOW_POOL_ACTIVE_PAUSED) { + if (dst_pool->avail_desc > dst_pool->start_th) { + pdev->pause_cb(dst_pool->member_flow_id, + WLAN_NETIF_PRIORITY_QUEUE_ON, + WLAN_DATA_FLOW_CONTROL_PRIORITY); + + pdev->pause_cb(dst_pool->member_flow_id, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + + dst_pool->status = + FLOW_POOL_ACTIVE_UNPAUSED; + } + } + } + qdf_spin_unlock_bh(&dst_pool->flow_pool_lock); + if (desc_count == 0) + break; + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + return 0; +} + +/** + * ol_tx_create_flow_pool() - create flow pool + * @flow_pool_id: flow pool id + * @flow_pool_size: flow pool size + * + * Return: flow_pool pointer / NULL for error + */ +struct ol_tx_flow_pool_t *ol_tx_create_flow_pool(uint8_t flow_pool_id, + uint16_t flow_pool_size) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + struct ol_tx_flow_pool_t *pool; + uint16_t size = 0, i; + struct ol_tx_desc_t *tx_desc; + union ol_tx_desc_list_elem_t *temp_list = NULL; + uint32_t stop_threshold; + uint32_t start_threshold; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return NULL; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return NULL; + } + stop_threshold = ol_cfg_get_tx_flow_stop_queue_th(pdev->ctrl_pdev); + start_threshold = stop_threshold + + ol_cfg_get_tx_flow_start_queue_offset(pdev->ctrl_pdev); + pool = qdf_mem_malloc(sizeof(*pool)); + if (!pool) + return NULL; + + pool->flow_pool_id = flow_pool_id; + pool->flow_pool_size = flow_pool_size; + pool->status = FLOW_POOL_ACTIVE_UNPAUSED; + pool->start_th = (start_threshold * flow_pool_size)/100; + pool->stop_th = (stop_threshold * flow_pool_size)/100; + pool->stop_priority_th = (TX_PRIORITY_TH * pool->stop_th)/100; + if (pool->stop_priority_th >= MAX_TSO_SEGMENT_DESC) + pool->stop_priority_th -= MAX_TSO_SEGMENT_DESC; + + pool->start_priority_th = (TX_PRIORITY_TH * pool->start_th)/100; + if (pool->start_priority_th >= MAX_TSO_SEGMENT_DESC) + pool->start_priority_th -= MAX_TSO_SEGMENT_DESC; + + qdf_spinlock_create(&pool->flow_pool_lock); + qdf_atomic_init(&pool->ref_cnt); + ol_tx_inc_pool_ref(pool); + + /* Take TX descriptor from global_pool and put it in temp_list*/ + qdf_spin_lock_bh(&pdev->tx_mutex); + if (pdev->tx_desc.num_free >= pool->flow_pool_size) + size = pool->flow_pool_size; + else + size = pdev->tx_desc.num_free; + + for (i = 0; i < size; i++) { + tx_desc = ol_tx_get_desc_global_pool(pdev); + tx_desc->pool = pool; + ((union ol_tx_desc_list_elem_t *)tx_desc)->next = temp_list; + temp_list = (union ol_tx_desc_list_elem_t *)tx_desc; + + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + + /* put temp_list to flow_pool */ + pool->freelist = temp_list; + pool->avail_desc = size; + pool->deficient_desc = pool->flow_pool_size - pool->avail_desc; + /* used for resize pool*/ + pool->overflow_desc = 0; + + /* Add flow_pool to flow_pool_list */ + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_INSERT_TAIL(&pdev->tx_desc.flow_pool_list, pool, + flow_pool_list_elem); + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + return pool; +} + +/** + * ol_tx_free_invalid_flow_pool() - free invalid pool + * @pool: pool + * + * Return: 0 for success or failure + */ +int ol_tx_free_invalid_flow_pool(struct ol_tx_flow_pool_t *pool) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return -EINVAL; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if ((!pdev) || (!pool) || (pool->status != FLOW_POOL_INVALID)) { + ol_txrx_err("Invalid pool/pdev"); + return -EINVAL; + } + + /* direclty distribute to other deficient pools */ + ol_tx_distribute_descs_to_deficient_pools(pool); + + qdf_spin_lock_bh(&pool->flow_pool_lock); + pool->flow_pool_size = pool->avail_desc; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + + pdev->tx_desc.num_invalid_bin--; + ol_txrx_info("invalid pool deleted %d", + pdev->tx_desc.num_invalid_bin); + + return ol_tx_dec_pool_ref(pool, false); +} + +/** + * ol_tx_get_flow_pool() - get flow_pool from flow_pool_id + * @flow_pool_id: flow pool id + * + * Return: flow_pool ptr / NULL if not found + */ +static struct ol_tx_flow_pool_t *ol_tx_get_flow_pool(uint8_t flow_pool_id) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + struct ol_tx_flow_pool_t *pool = NULL; + bool is_found = false; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + QDF_ASSERT(0); + return NULL; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("ERROR: pdev NULL"); + QDF_ASSERT(0); /* traceback */ + return NULL; + } + + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_FOREACH(pool, &pdev->tx_desc.flow_pool_list, + flow_pool_list_elem) { + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->flow_pool_id == flow_pool_id) { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + is_found = true; + break; + } + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + if (is_found == false) + pool = NULL; + + return pool; +} + +/** + * ol_tx_flow_pool_vdev_map() - Map flow_pool with vdev + * @pool: flow_pool + * @vdev_id: flow_id /vdev_id + * + * Return: none + */ +static void ol_tx_flow_pool_vdev_map(struct ol_tx_flow_pool_t *pool, + uint8_t vdev_id) +{ + struct ol_txrx_vdev_t *vdev; + + vdev = (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + if (!vdev) { + ol_txrx_err("invalid vdev_id %d", vdev_id); + return; + } + + vdev->pool = pool; + qdf_spin_lock_bh(&pool->flow_pool_lock); + pool->member_flow_id = vdev_id; + qdf_spin_unlock_bh(&pool->flow_pool_lock); +} + +/** + * ol_tx_flow_pool_vdev_unmap() - Unmap flow_pool from vdev + * @pool: flow_pool + * @vdev_id: flow_id /vdev_id + * + * Return: none + */ +static void ol_tx_flow_pool_vdev_unmap(struct ol_tx_flow_pool_t *pool, + uint8_t vdev_id) +{ + struct ol_txrx_vdev_t *vdev; + + vdev = (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + if (!vdev) { + ol_txrx_dbg("invalid vdev_id %d", vdev_id); + return; + } + + vdev->pool = NULL; + qdf_spin_lock_bh(&pool->flow_pool_lock); + pool->member_flow_id = INVALID_FLOW_ID; + qdf_spin_unlock_bh(&pool->flow_pool_lock); +} + +/** + * ol_tx_flow_pool_map_handler() - Map flow_id with pool of descriptors + * @flow_id: flow id + * @flow_type: flow type + * @flow_pool_id: pool id + * @flow_pool_size: pool size + * + * Process below target to host message + * HTT_T2H_MSG_TYPE_FLOW_POOL_MAP + * + * Return: none + */ +void ol_tx_flow_pool_map_handler(uint8_t flow_id, uint8_t flow_type, + uint8_t flow_pool_id, uint16_t flow_pool_size) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + struct ol_tx_flow_pool_t *pool; + uint8_t pool_create = 0; + enum htt_flow_type type = flow_type; + + ol_txrx_dbg("flow_id %d flow_type %d flow_pool_id %d flow_pool_size %d", + flow_id, flow_type, flow_pool_id, flow_pool_size); + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (qdf_unlikely(!pdev)) { + ol_txrx_err("pdev is NULL"); + return; + } + pdev->pool_stats.pool_map_count++; + + pool = ol_tx_get_flow_pool(flow_pool_id); + if (!pool) { + pool = ol_tx_create_flow_pool(flow_pool_id, flow_pool_size); + if (!pool) { + ol_txrx_err("creation of flow_pool %d size %d failed", + flow_pool_id, flow_pool_size); + return; + } + pool_create = 1; + } + + switch (type) { + + case FLOW_TYPE_VDEV: + ol_tx_flow_pool_vdev_map(pool, flow_id); + pdev->pause_cb(flow_id, + WLAN_NETIF_PRIORITY_QUEUE_ON, + WLAN_DATA_FLOW_CONTROL_PRIORITY); + pdev->pause_cb(flow_id, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + break; + default: + if (pool_create) + ol_tx_dec_pool_ref(pool, false); + ol_txrx_err("flow type %d not supported", type); + break; + } +} + +/** + * ol_tx_flow_pool_unmap_handler() - Unmap flow_id from pool of descriptors + * @flow_id: flow id + * @flow_type: flow type + * @flow_pool_id: pool id + * + * Process below target to host message + * HTT_T2H_MSG_TYPE_FLOW_POOL_UNMAP + * + * Return: none + */ +void ol_tx_flow_pool_unmap_handler(uint8_t flow_id, uint8_t flow_type, + uint8_t flow_pool_id) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + struct ol_tx_flow_pool_t *pool; + enum htt_flow_type type = flow_type; + + ol_txrx_dbg("flow_id %d flow_type %d flow_pool_id %d", + flow_id, flow_type, flow_pool_id); + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (qdf_unlikely(!pdev)) { + ol_txrx_err("pdev is NULL"); + return; + } + pdev->pool_stats.pool_unmap_count++; + + pool = ol_tx_get_flow_pool(flow_pool_id); + if (!pool) { + ol_txrx_info("flow_pool not available flow_pool_id %d", type); + return; + } + + switch (type) { + + case FLOW_TYPE_VDEV: + ol_tx_flow_pool_vdev_unmap(pool, flow_id); + break; + default: + ol_txrx_info("flow type %d not supported", type); + return; + } + + /* + * only delete if all descriptors are available + * and pool ref count becomes 0 + */ + ol_tx_dec_pool_ref(pool, false); +} + +#ifdef QCA_LL_TX_FLOW_CONTROL_RESIZE +/** + * ol_tx_distribute_descs_to_deficient_pools_from_global_pool() + * + * Distribute descriptors of global pool to all + * deficient pools as per need. + * + * Return: 0 for success + */ +int ol_tx_distribute_descs_to_deficient_pools_from_global_pool(void) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + struct ol_tx_flow_pool_t *dst_pool = NULL; + struct ol_tx_flow_pool_t *tmp_pool = NULL; + uint16_t total_desc_req = 0; + uint16_t desc_move_count = 0; + uint16_t temp_count = 0, i; + union ol_tx_desc_list_elem_t *temp_list = NULL; + struct ol_tx_desc_t *tx_desc; + uint8_t free_invalid_pool = 0; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return -EINVAL; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return -EINVAL; + } + + /* Nested locks: maintain flow_pool_list_lock->flow_pool_lock */ + /* find out total deficient desc required */ + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_FOREACH(dst_pool, &pdev->tx_desc.flow_pool_list, + flow_pool_list_elem) { + qdf_spin_lock_bh(&dst_pool->flow_pool_lock); + total_desc_req += dst_pool->deficient_desc; + qdf_spin_unlock_bh(&dst_pool->flow_pool_lock); + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + qdf_spin_lock_bh(&pdev->tx_mutex); + desc_move_count = (pdev->tx_desc.num_free >= total_desc_req) ? + total_desc_req : pdev->tx_desc.num_free; + + for (i = 0; i < desc_move_count; i++) { + tx_desc = ol_tx_get_desc_global_pool(pdev); + ((union ol_tx_desc_list_elem_t *)tx_desc)->next = temp_list; + temp_list = (union ol_tx_desc_list_elem_t *)tx_desc; + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + + if (!desc_move_count) + return 0; + + /* destribute desc to deficient pool */ + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_FOREACH(dst_pool, &pdev->tx_desc.flow_pool_list, + flow_pool_list_elem) { + qdf_spin_lock_bh(&dst_pool->flow_pool_lock); + if (dst_pool->deficient_desc) { + temp_count = + (dst_pool->deficient_desc > desc_move_count) ? + desc_move_count : dst_pool->deficient_desc; + + desc_move_count -= temp_count; + for (i = 0; i < temp_count; i++) { + tx_desc = &temp_list->tx_desc; + temp_list = temp_list->next; + ol_tx_put_desc_flow_pool(dst_pool, tx_desc); + } + + if (dst_pool->status == FLOW_POOL_ACTIVE_PAUSED) { + if (dst_pool->avail_desc > dst_pool->start_th) { + pdev->pause_cb(dst_pool->member_flow_id, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + dst_pool->status = + FLOW_POOL_ACTIVE_UNPAUSED; + } + } else if ((dst_pool->status == FLOW_POOL_INVALID) && + (dst_pool->avail_desc == + dst_pool->flow_pool_size)) { + free_invalid_pool = 1; + tmp_pool = dst_pool; + } + } + qdf_spin_unlock_bh(&dst_pool->flow_pool_lock); + if (desc_move_count == 0) + break; + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + if (free_invalid_pool && tmp_pool) + ol_tx_free_invalid_flow_pool(tmp_pool); + + return 0; +} + +/** + * ol_tx_flow_pool_update_queue_state() - update network queue for pool based on + * new available count. + * @pool : pool handle + * + * Return : none + */ +static void ol_tx_flow_pool_update_queue_state(struct ol_txrx_pdev_t *pdev, + struct ol_tx_flow_pool_t *pool) +{ + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->avail_desc > pool->start_th) { + pool->status = FLOW_POOL_ACTIVE_UNPAUSED; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + pdev->pause_cb(pool->member_flow_id, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + } else if (pool->avail_desc < pool->stop_th && + pool->avail_desc >= pool->stop_priority_th) { + pool->status = FLOW_POOL_NON_PRIO_PAUSED; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + pdev->pause_cb(pool->member_flow_id, + WLAN_STOP_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); + pdev->pause_cb(pool->member_flow_id, + WLAN_NETIF_PRIORITY_QUEUE_ON, + WLAN_DATA_FLOW_CONTROL); + } else if (pool->avail_desc < pool->stop_priority_th) { + pool->status = FLOW_POOL_ACTIVE_PAUSED; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + pdev->pause_cb(pool->member_flow_id, + WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + } else { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } +} + +/** + * ol_tx_flow_pool_update() - update pool parameters with new size + * @pool : pool handle + * @new_pool_size : new pool size + * @deficient_count : deficient count + * @overflow_count : overflow count + * + * Return : none + */ +static void ol_tx_flow_pool_update(struct ol_tx_flow_pool_t *pool, + uint16_t new_pool_size, + uint16_t deficient_count, + uint16_t overflow_count) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + uint32_t stop_threshold; + uint32_t start_threshold; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + stop_threshold = ol_cfg_get_tx_flow_stop_queue_th(pdev->ctrl_pdev); + start_threshold = stop_threshold + + ol_cfg_get_tx_flow_start_queue_offset(pdev->ctrl_pdev); + pool->flow_pool_size = new_pool_size; + pool->start_th = (start_threshold * new_pool_size) / 100; + pool->stop_th = (stop_threshold * new_pool_size) / 100; + pool->stop_priority_th = (TX_PRIORITY_TH * pool->stop_th) / 100; + if (pool->stop_priority_th >= MAX_TSO_SEGMENT_DESC) + pool->stop_priority_th -= MAX_TSO_SEGMENT_DESC; + + pool->start_priority_th = (TX_PRIORITY_TH * pool->start_th) / 100; + if (pool->start_priority_th >= MAX_TSO_SEGMENT_DESC) + pool->start_priority_th -= MAX_TSO_SEGMENT_DESC; + + if (deficient_count) + pool->deficient_desc = deficient_count; + + if (overflow_count) + pool->overflow_desc = overflow_count; +} + +/** + * ol_tx_flow_pool_resize() - resize pool with new size + * @pool: pool pointer + * @new_pool_size: new pool size + * + * Return: none + */ +static void ol_tx_flow_pool_resize(struct ol_tx_flow_pool_t *pool, + uint16_t new_pool_size) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + uint16_t diff = 0, overflow_count = 0, deficient_count = 0; + uint16_t move_desc_to_global = 0, move_desc_from_global = 0; + union ol_tx_desc_list_elem_t *temp_list = NULL; + int i = 0, update_done = 0; + struct ol_tx_desc_t *tx_desc = NULL; + uint16_t temp = 0; + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("pdev is NULL"); + return; + } + + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->flow_pool_size == new_pool_size) { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + ol_txrx_info("pool resize received with same size"); + return; + } + qdf_spin_unlock_bh(&pool->flow_pool_lock); + + /* Reduce pool size */ + /* start_priority_th desc should available after reduction */ + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->flow_pool_size > new_pool_size) { + diff = pool->flow_pool_size - new_pool_size; + diff += pool->overflow_desc; + pool->overflow_desc = 0; + temp = QDF_MIN(pool->deficient_desc, diff); + pool->deficient_desc -= temp; + diff -= temp; + + if (diff) { + /* Have enough descriptors */ + if (pool->avail_desc >= + (diff + pool->start_priority_th)) { + move_desc_to_global = diff; + } + /* Do not have enough descriptors */ + else if (pool->avail_desc > pool->start_priority_th) { + move_desc_to_global = pool->avail_desc - + pool->start_priority_th; + overflow_count = diff - move_desc_to_global; + } + + /* Move desc to temp_list */ + for (i = 0; i < move_desc_to_global; i++) { + tx_desc = ol_tx_get_desc_flow_pool(pool); + ((union ol_tx_desc_list_elem_t *)tx_desc)->next + = temp_list; + temp_list = + (union ol_tx_desc_list_elem_t *)tx_desc; + } + } + + /* update pool size and threshold */ + ol_tx_flow_pool_update(pool, new_pool_size, 0, overflow_count); + update_done = 1; + } + qdf_spin_unlock_bh(&pool->flow_pool_lock); + + if (move_desc_to_global && temp_list) { + /* put free descriptors to global pool */ + qdf_spin_lock_bh(&pdev->tx_mutex); + for (i = 0; i < move_desc_to_global; i++) { + tx_desc = &temp_list->tx_desc; + temp_list = temp_list->next; + ol_tx_put_desc_global_pool(pdev, tx_desc); + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + } + + if (update_done) + goto update_done; + + /* Increase pool size */ + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->flow_pool_size < new_pool_size) { + diff = new_pool_size - pool->flow_pool_size; + diff += pool->deficient_desc; + pool->deficient_desc = 0; + temp = QDF_MIN(pool->overflow_desc, diff); + pool->overflow_desc -= temp; + diff -= temp; + } + qdf_spin_unlock_bh(&pool->flow_pool_lock); + + if (diff) { + /* take descriptors from global pool */ + qdf_spin_lock_bh(&pdev->tx_mutex); + + if (pdev->tx_desc.num_free >= diff) { + move_desc_from_global = diff; + } else { + move_desc_from_global = pdev->tx_desc.num_free; + deficient_count = diff - move_desc_from_global; + } + + for (i = 0; i < move_desc_from_global; i++) { + tx_desc = ol_tx_get_desc_global_pool(pdev); + ((union ol_tx_desc_list_elem_t *)tx_desc)->next = + temp_list; + temp_list = (union ol_tx_desc_list_elem_t *)tx_desc; + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + } + /* update desc to pool */ + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (move_desc_from_global && temp_list) { + for (i = 0; i < move_desc_from_global; i++) { + tx_desc = &temp_list->tx_desc; + temp_list = temp_list->next; + ol_tx_put_desc_flow_pool(pool, tx_desc); + } + } + /* update pool size and threshold */ + ol_tx_flow_pool_update(pool, new_pool_size, deficient_count, 0); + qdf_spin_unlock_bh(&pool->flow_pool_lock); + +update_done: + + ol_tx_flow_pool_update_queue_state(pdev, pool); +} + +/** + * ol_tx_flow_pool_resize_handler() - Resize pool with new size + * @flow_pool_id: pool id + * @flow_pool_size: pool size + * + * Process below target to host message + * HTT_T2H_MSG_TYPE_FLOW_POOL_RESIZE + * + * Return: none + */ +void ol_tx_flow_pool_resize_handler(uint8_t flow_pool_id, + uint16_t flow_pool_size) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + ol_txrx_pdev_handle pdev; + struct ol_tx_flow_pool_t *pool; + + ol_txrx_dbg("flow_pool_id %d flow_pool_size %d", + flow_pool_id, flow_pool_size); + + if (qdf_unlikely(!soc)) { + ol_txrx_err("soc is NULL"); + return; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (qdf_unlikely(!pdev)) { + ol_txrx_err("pdev is NULL"); + return; + } + pdev->pool_stats.pool_resize_count++; + + pool = ol_tx_get_flow_pool(flow_pool_id); + if (!pool) { + ol_txrx_err("resize for flow_pool %d size %d failed", + flow_pool_id, flow_pool_size); + return; + } + + ol_tx_inc_pool_ref(pool); + ol_tx_flow_pool_resize(pool, flow_pool_size); + ol_tx_dec_pool_ref(pool, false); +} +#endif + +/** + * ol_txrx_map_to_netif_reason_type() - map to netif_reason_type + * @reason: network queue pause reason + * + * Return: netif_reason_type + */ +static enum netif_reason_type +ol_txrx_map_to_netif_reason_type(uint32_t reason) +{ + switch (reason) { + case OL_TXQ_PAUSE_REASON_FW: + return WLAN_FW_PAUSE; + case OL_TXQ_PAUSE_REASON_PEER_UNAUTHORIZED: + return WLAN_PEER_UNAUTHORISED; + case OL_TXQ_PAUSE_REASON_TX_ABORT: + return WLAN_TX_ABORT; + case OL_TXQ_PAUSE_REASON_VDEV_STOP: + return WLAN_VDEV_STOP; + case OL_TXQ_PAUSE_REASON_THERMAL_MITIGATION: + return WLAN_THERMAL_MITIGATION; + default: + ol_txrx_err("reason not supported %d", reason); + return WLAN_REASON_TYPE_MAX; + } +} + +void ol_txrx_vdev_pause(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint32_t reason, uint32_t pause_type) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + struct ol_txrx_pdev_t *pdev; + enum netif_reason_type netif_reason; + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is NULL"); + return; + } + + pdev = vdev->pdev; + if (qdf_unlikely((!pdev) || (!pdev->pause_cb))) { + ol_txrx_err("invalid pdev"); + return; + } + + netif_reason = ol_txrx_map_to_netif_reason_type(reason); + if (netif_reason == WLAN_REASON_TYPE_MAX) + return; + + pdev->pause_cb(vdev->vdev_id, WLAN_STOP_ALL_NETIF_QUEUE, netif_reason); +} + +/** + * ol_txrx_vdev_unpause() - unpause vdev network queues + * @soc_hdl: datapath soc handle + * @vdev: vdev handle + * @reason: network queue pause reason + * @pause_type: type of pause + * + * Return: none + */ +void ol_txrx_vdev_unpause(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint32_t reason, uint32_t pause_type) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + struct ol_txrx_pdev_t *pdev; + enum netif_reason_type netif_reason; + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is NULL"); + return; + } + + pdev = vdev->pdev; + if (qdf_unlikely((!pdev) || (!pdev->pause_cb))) { + ol_txrx_err("invalid pdev"); + return; + } + + netif_reason = ol_txrx_map_to_netif_reason_type(reason); + if (netif_reason == WLAN_REASON_TYPE_MAX) + return; + + pdev->pause_cb(vdev->vdev_id, WLAN_WAKE_ALL_NETIF_QUEUE, + netif_reason); +} + +/** + * ol_txrx_pdev_pause() - pause network queues for each vdev + * @pdev: pdev handle + * @reason: network queue pause reason + * + * Return: none + */ +void ol_txrx_pdev_pause(struct ol_txrx_pdev_t *pdev, uint32_t reason) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct ol_txrx_vdev_t *vdev = NULL, *tmp; + + TAILQ_FOREACH_SAFE(vdev, &pdev->vdev_list, vdev_list_elem, tmp) { + ol_txrx_vdev_pause(ol_txrx_soc_t_to_cdp_soc_t(soc), + vdev->vdev_id, reason, 0); + } +} + +/** + * ol_txrx_pdev_unpause() - unpause network queues for each vdev + * @pdev: pdev handle + * @reason: network queue pause reason + * + * Return: none + */ +void ol_txrx_pdev_unpause(struct ol_txrx_pdev_t *pdev, uint32_t reason) +{ + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct ol_txrx_vdev_t *vdev = NULL, *tmp; + + TAILQ_FOREACH_SAFE(vdev, &pdev->vdev_list, vdev_list_elem, tmp) { + ol_txrx_vdev_unpause(ol_txrx_soc_t_to_cdp_soc_t(soc), + vdev->vdev_id, reason, 0); + } +} diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_internal.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..dcd3e42ac56cc4a9bcf5741c5dbf6529819a3553 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_internal.h @@ -0,0 +1,826 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_TXRX_INTERNAL__H_ +#define _OL_TXRX_INTERNAL__H_ + +#include /* qdf_assert */ +#include /* qdf_nbuf_t */ +#include /* qdf_mem_set */ +#include /* ieee80211_frame */ +#include /* htt_rx_msdu_desc_completes_mpdu, etc. */ + +#include + +#include +#include /* ETHERNET_HDR_LEN, etc. */ +#include /* IPV4_HDR_LEN, etc. */ +#include /* IP_PROTOCOL_TCP, etc. */ + +#ifdef ATH_11AC_TXCOMPACT +#define OL_TX_DESC_NO_REFS(tx_desc) 1 +#define OL_TX_DESC_REF_INIT(tx_desc) /* no-op */ +#define OL_TX_DESC_REF_INC(tx_desc) /* no-op */ +#else +#define OL_TX_DESC_NO_REFS(tx_desc) \ + qdf_atomic_dec_and_test(&tx_desc->ref_cnt) +#define OL_TX_DESC_REF_INIT(tx_desc) qdf_atomic_init(&tx_desc->ref_cnt) +#define OL_TX_DESC_REF_INC(tx_desc) qdf_atomic_inc(&tx_desc->ref_cnt) +#endif + +#ifndef TXRX_ASSERT_LEVEL +#define TXRX_ASSERT_LEVEL 3 +#endif + +#ifdef __KLOCWORK__ +#define TXRX_ASSERT1(x) do { if (!(x)) abort(); } while (0) +#define TXRX_ASSERT2(x) do { if (!(x)) abort(); } while (0) +#else /* #ifdef __KLOCWORK__ */ + +#if TXRX_ASSERT_LEVEL > 0 +#define TXRX_ASSERT1(condition) qdf_assert((condition)) +#else +#define TXRX_ASSERT1(condition) +#endif + +#if TXRX_ASSERT_LEVEL > 1 +#define TXRX_ASSERT2(condition) qdf_assert((condition)) +#else +#define TXRX_ASSERT2(condition) +#endif +#endif /* #ifdef __KLOCWORK__ */ + +#ifdef TXRX_PRINT_ENABLE + +#include /* va_list */ +#include /* qdf_vprint */ + +#define ol_txrx_alert(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_TXRX, params) +#define ol_txrx_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_TXRX, params) +#define ol_txrx_warn(params...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_TXRX, params) +#define ol_txrx_info(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_TXRX, params) +#define ol_txrx_info_high(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_TXRX, params) +#define ol_txrx_dbg(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_TXRX, params) + +#define txrx_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_TXRX, params) +#define txrx_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_TXRX, params) +#define txrx_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_TXRX, params) +#define txrx_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_TXRX, params) +#define txrx_nofl_dbg(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_TXRX, params) + +#define ol_txrx_err_rl(params...) \ + QDF_TRACE_ERROR_RL(QDF_MODULE_ID_TXRX, params) + +/* + * define PN check failure message print rate + * as 1 second + */ +#define TXRX_PN_CHECK_FAILURE_PRINT_PERIOD_MS 1000 + +#else + +#define ol_txrx_alert(format, args...) +#define ol_txrx_err(format, args...) +#define ol_txrx_warn(format, args...) +#define ol_txrx_info(format, args...) +#define ol_txrx_info_high(format, args...) +#define ol_txrx_dbg(format, args...) + +#define txrx_nofl_alert(params...) +#define txrx_nofl_err(params...) +#define txrx_nofl_warn(params...) +#define txrx_nofl_info(params...) +#define txrx_nofl_dbg(params...) + +#define ol_txrx_err_rl(params...) + +#endif /* TXRX_PRINT_ENABLE */ + +/*--- tx credit debug printouts ---*/ + +#ifndef DEBUG_CREDIT +#define DEBUG_CREDIT 0 +#endif + +#if DEBUG_CREDIT +#define TX_CREDIT_DEBUG_PRINT(fmt, ...) qdf_print(fmt, ## __VA_ARGS__) +#else +#define TX_CREDIT_DEBUG_PRINT(fmt, ...) +#endif + +/*--- tx scheduler debug printouts ---*/ + +#ifdef HOST_TX_SCHED_DEBUG +#define TX_SCHED_DEBUG_PRINT(fmt, ...) qdf_print(fmt, ## __VA_ARGS__) +#else +#define TX_SCHED_DEBUG_PRINT(fmt, ...) +#endif +#define TX_SCHED_DEBUG_PRINT_ALWAYS(fmt, ...) qdf_print(fmt, ## __VA_ARGS__) + +#define OL_TXRX_LIST_APPEND(head, tail, elem) \ + do { \ + if (!(head)) { \ + (head) = (elem); \ + } else { \ + qdf_nbuf_set_next((tail), (elem)); \ + } \ + (tail) = (elem); \ + } while (0) + +static inline void +ol_rx_mpdu_list_next(struct ol_txrx_pdev_t *pdev, + void *mpdu_list, + qdf_nbuf_t *mpdu_tail, qdf_nbuf_t *next_mpdu) +{ + htt_pdev_handle htt_pdev = pdev->htt_pdev; + qdf_nbuf_t msdu; + + /* + * For now, we use a simply flat list of MSDUs. + * So, traverse the list until we reach the last MSDU within the MPDU. + */ + TXRX_ASSERT2(mpdu_list); + msdu = mpdu_list; + while (!htt_rx_msdu_desc_completes_mpdu + (htt_pdev, htt_rx_msdu_desc_retrieve(htt_pdev, msdu))) { + msdu = qdf_nbuf_next(msdu); + TXRX_ASSERT2(msdu); + } + /* msdu now points to the last MSDU within the first MPDU */ + *mpdu_tail = msdu; + *next_mpdu = qdf_nbuf_next(msdu); +} + +/*--- txrx stats macros ---*/ + +/* unconditional defs */ +#define TXRX_STATS_INCR(pdev, field) TXRX_STATS_ADD(pdev, field, 1) + +/* default conditional defs (may be undefed below) */ + +#define TXRX_STATS_INIT(_pdev) \ + qdf_mem_zero(&((_pdev)->stats), sizeof((_pdev)->stats)) +#define TXRX_STATS_ADD(_pdev, _field, _delta) { \ + _pdev->stats._field += _delta; } +#define TXRX_STATS_MSDU_INCR(pdev, field, netbuf) \ + do { \ + TXRX_STATS_INCR((pdev), pub.field.pkts); \ + TXRX_STATS_ADD((pdev), pub.field.bytes, qdf_nbuf_len(netbuf)); \ + } while (0) + +/* conditional defs based on verbosity level */ + + +#define TXRX_STATS_MSDU_LIST_INCR(pdev, field, netbuf_list) \ + do { \ + qdf_nbuf_t tmp_list = netbuf_list; \ + while (tmp_list) { \ + TXRX_STATS_MSDU_INCR(pdev, field, tmp_list); \ + tmp_list = qdf_nbuf_next(tmp_list); \ + } \ + } while (0) + +#define TXRX_STATS_MSDU_INCR_TX_STATUS(status, pdev, netbuf) do { \ + if (status == htt_tx_status_ok) \ + TXRX_STATS_MSDU_INCR(pdev, tx.delivered, netbuf); \ + else if (status == htt_tx_status_discard) \ + TXRX_STATS_MSDU_INCR(pdev, tx.dropped.target_discard, \ + netbuf); \ + else if (status == htt_tx_status_no_ack) \ + TXRX_STATS_MSDU_INCR(pdev, tx.dropped.no_ack, netbuf); \ + else if (status == htt_tx_status_drop) \ + TXRX_STATS_MSDU_INCR(pdev, tx.dropped.target_drop, \ + netbuf); \ + else if (status == htt_tx_status_download_fail) \ + TXRX_STATS_MSDU_INCR(pdev, tx.dropped.download_fail, \ + netbuf); \ + else \ + /* NO-OP */; \ + } while (0) + +#define TXRX_STATS_UPDATE_TX_COMP_HISTOGRAM(_pdev, _p_cntrs) \ + do { \ + if (_p_cntrs == 1) { \ + TXRX_STATS_ADD(_pdev, pub.tx.comp_histogram.pkts_1, 1);\ + } else if (_p_cntrs > 2 && _p_cntrs <= 10) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.comp_histogram.pkts_2_10, 1); \ + } else if (_p_cntrs > 10 && _p_cntrs <= 20) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.comp_histogram.pkts_11_20, 1); \ + } else if (_p_cntrs > 20 && _p_cntrs <= 30) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.comp_histogram.pkts_21_30, 1); \ + } else if (_p_cntrs > 30 && _p_cntrs <= 40) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.comp_histogram.pkts_31_40, 1); \ + } else if (_p_cntrs > 40 && _p_cntrs <= 50) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.comp_histogram.pkts_41_50, 1); \ + } else if (_p_cntrs > 50 && _p_cntrs <= 60) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.comp_histogram.pkts_51_60, 1); \ + } else { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.comp_histogram.pkts_61_plus, 1); \ + } \ + } while (0) + +#define TXRX_STATS_UPDATE_TX_STATS(_pdev, _status, _p_cntrs, _b_cntrs) \ + do { \ + switch (status) { \ + case htt_tx_status_ok: \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.delivered.pkts, _p_cntrs); \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.delivered.bytes, _b_cntrs); \ + break; \ + case htt_tx_status_discard: \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.dropped.target_discard.pkts, _p_cntrs);\ + TXRX_STATS_ADD(_pdev, \ + pub.tx.dropped.target_discard.bytes, _b_cntrs);\ + break; \ + case htt_tx_status_no_ack: \ + TXRX_STATS_ADD(_pdev, pub.tx.dropped.no_ack.pkts, \ + _p_cntrs); \ + TXRX_STATS_ADD(_pdev, pub.tx.dropped.no_ack.bytes, \ + _b_cntrs); \ + break; \ + case htt_tx_status_drop: \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.dropped.target_drop.pkts, _p_cntrs);\ + TXRX_STATS_ADD(_pdev, \ + pub.tx.dropped.target_drop.bytes, _b_cntrs);\ + break; \ + case htt_tx_status_download_fail: \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.dropped.download_fail.pkts, _p_cntrs); \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.dropped.download_fail.bytes, _b_cntrs);\ + break; \ + default: \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.dropped.others.pkts, _p_cntrs); \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.dropped.others.bytes, _b_cntrs); \ + break; \ + } \ + TXRX_STATS_UPDATE_TX_COMP_HISTOGRAM(_pdev, _p_cntrs); \ + } while (0) + + +/*--- txrx sequence number trace macros ---*/ + +#define TXRX_SEQ_NUM_ERR(_status) (0xffff - _status) + +#if defined(ENABLE_RX_REORDER_TRACE) + +A_STATUS ol_rx_reorder_trace_attach(ol_txrx_pdev_handle pdev); +void ol_rx_reorder_trace_detach(ol_txrx_pdev_handle pdev); +void ol_rx_reorder_trace_add(ol_txrx_pdev_handle pdev, + uint8_t tid, + uint16_t reorder_idx, + uint16_t seq_num, int num_mpdus); + +#define OL_RX_REORDER_TRACE_ATTACH ol_rx_reorder_trace_attach +#define OL_RX_REORDER_TRACE_DETACH ol_rx_reorder_trace_detach +#define OL_RX_REORDER_TRACE_ADD ol_rx_reorder_trace_add + +#else + +#define OL_RX_REORDER_TRACE_ATTACH(_pdev) A_OK +#define OL_RX_REORDER_TRACE_DETACH(_pdev) +#define OL_RX_REORDER_TRACE_ADD(pdev, tid, reorder_idx, seq_num, num_mpdus) + +#endif /* ENABLE_RX_REORDER_TRACE */ + +/*--- txrx packet number trace macros ---*/ + +#if defined(ENABLE_RX_PN_TRACE) + +A_STATUS ol_rx_pn_trace_attach(ol_txrx_pdev_handle pdev); +void ol_rx_pn_trace_detach(ol_txrx_pdev_handle pdev); +void ol_rx_pn_trace_add(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer, + uint16_t tid, void *rx_desc); + +#define OL_RX_PN_TRACE_ATTACH ol_rx_pn_trace_attach +#define OL_RX_PN_TRACE_DETACH ol_rx_pn_trace_detach +#define OL_RX_PN_TRACE_ADD ol_rx_pn_trace_add + +#else + +#define OL_RX_PN_TRACE_ATTACH(_pdev) A_OK +#define OL_RX_PN_TRACE_DETACH(_pdev) +#define OL_RX_PN_TRACE_ADD(pdev, peer, tid, rx_desc) + +#endif /* ENABLE_RX_PN_TRACE */ + +static inline int ol_txrx_ieee80211_hdrsize(const void *data) +{ + const struct ieee80211_frame *wh = (const struct ieee80211_frame *)data; + int size = sizeof(struct ieee80211_frame); + + /* NB: we don't handle control frames */ + TXRX_ASSERT1((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != + IEEE80211_FC0_TYPE_CTL); + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == + IEEE80211_FC1_DIR_DSTODS) + size += QDF_MAC_ADDR_SIZE; + if (IEEE80211_QOS_HAS_SEQ(wh)) { + size += sizeof(uint16_t); + /* Qos frame with Order bit set indicates an HTC frame */ + if (wh->i_fc[1] & IEEE80211_FC1_ORDER) + size += sizeof(struct ieee80211_htc); + } + return size; +} + +/*--- frame display utility ---*/ + +enum ol_txrx_frm_dump_options { + ol_txrx_frm_dump_contents = 0x1, + ol_txrx_frm_dump_tcp_seq = 0x2, +}; + +#ifdef TXRX_DEBUG_DATA +static inline void +ol_txrx_frms_dump(const char *name, + struct ol_txrx_pdev_t *pdev, + qdf_nbuf_t frm, + enum ol_txrx_frm_dump_options display_options, int max_len) +{ +#define TXRX_FRM_DUMP_MAX_LEN 128 + uint8_t local_buf[TXRX_FRM_DUMP_MAX_LEN] = { 0 }; + uint8_t *p; + + if (name) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, "%s\n", + name); + } + while (frm) { + p = qdf_nbuf_data(frm); + if (display_options & ol_txrx_frm_dump_tcp_seq) { + int tcp_offset; + int l2_hdr_size; + uint16_t ethtype; + uint8_t ip_prot; + + if (pdev->frame_format == wlan_frm_fmt_802_3) { + struct ethernet_hdr_t *enet_hdr = + (struct ethernet_hdr_t *)p; + l2_hdr_size = ETHERNET_HDR_LEN; + + /* + * LLC/SNAP present? + */ + ethtype = (enet_hdr->ethertype[0] << 8) | + enet_hdr->ethertype[1]; + if (!IS_ETHERTYPE(ethertype)) { + /* 802.3 format */ + struct llc_snap_hdr_t *llc_hdr; + + llc_hdr = (struct llc_snap_hdr_t *) + (p + l2_hdr_size); + l2_hdr_size += LLC_SNAP_HDR_LEN; + ethtype = (llc_hdr->ethertype[0] << 8) | + llc_hdr->ethertype[1]; + } + } else { + struct llc_snap_hdr_t *llc_hdr; + + /* (generic?) 802.11 */ + l2_hdr_size = sizeof(struct ieee80211_frame); + llc_hdr = (struct llc_snap_hdr_t *) + (p + l2_hdr_size); + l2_hdr_size += LLC_SNAP_HDR_LEN; + ethtype = (llc_hdr->ethertype[0] << 8) | + llc_hdr->ethertype[1]; + } + if (ethtype == ETHERTYPE_IPV4) { + struct ipv4_hdr_t *ipv4_hdr; + + ipv4_hdr = + (struct ipv4_hdr_t *)(p + l2_hdr_size); + ip_prot = ipv4_hdr->protocol; + tcp_offset = l2_hdr_size + IPV4_HDR_LEN; + } else if (ethtype == ETHERTYPE_IPV6) { + struct ipv6_hdr_t *ipv6_hdr; + + ipv6_hdr = + (struct ipv6_hdr_t *)(p + l2_hdr_size); + ip_prot = ipv6_hdr->next_hdr; + tcp_offset = l2_hdr_size + IPV6_HDR_LEN; + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO, + "frame %pK non-IP ethertype (%x)\n", + frm, ethtype); + goto NOT_IP_TCP; + } + if (ip_prot == IP_PROTOCOL_TCP) { +#if NEVERDEFINED + struct tcp_hdr_t *tcp_hdr; + uint32_t tcp_seq_num; + + tcp_hdr = (struct tcp_hdr_t *)(p + tcp_offset); + tcp_seq_num = + (tcp_hdr->seq_num[0] << 24) | + (tcp_hdr->seq_num[1] << 16) | + (tcp_hdr->seq_num[1] << 8) | + (tcp_hdr->seq_num[1] << 0); + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO, + "frame %pK: TCP seq num = %d\n", frm, + tcp_seq_num); +#else + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO, + "frame %pK: TCP seq num = %d\n", frm, + ((*(p + tcp_offset + 4)) << 24) | + ((*(p + tcp_offset + 5)) << 16) | + ((*(p + tcp_offset + 6)) << 8) | + (*(p + tcp_offset + 7))); +#endif + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO, + "frame %pK non-TCP IP protocol (%x)\n", + frm, ip_prot); + } + } +NOT_IP_TCP: + if (display_options & ol_txrx_frm_dump_contents) { + int i, frag_num, len_lim; + + len_lim = max_len; + if (len_lim > qdf_nbuf_len(frm)) + len_lim = qdf_nbuf_len(frm); + if (len_lim > TXRX_FRM_DUMP_MAX_LEN) + len_lim = TXRX_FRM_DUMP_MAX_LEN; + + /* + * Gather frame contents from netbuf fragments + * into a contiguous buffer. + */ + frag_num = 0; + i = 0; + while (i < len_lim) { + int frag_bytes; + + frag_bytes = + qdf_nbuf_get_frag_len(frm, frag_num); + if (frag_bytes > len_lim - i) + frag_bytes = len_lim - i; + if (frag_bytes > 0) { + p = qdf_nbuf_get_frag_vaddr(frm, + frag_num); + qdf_mem_copy(&local_buf[i], p, + frag_bytes); + } + frag_num++; + i += frag_bytes; + } + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "frame %pK data (%pK), hex dump of bytes 0-%d of %d:\n", + frm, p, len_lim - 1, (int)qdf_nbuf_len(frm)); + p = local_buf; + while (len_lim > 16) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO, + " " /* indent */ + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + *(p + 0), *(p + 1), *(p + 2), + *(p + 3), *(p + 4), *(p + 5), + *(p + 6), *(p + 7), *(p + 8), + *(p + 9), *(p + 10), *(p + 11), + *(p + 12), *(p + 13), *(p + 14), + *(p + 15)); + p += 16; + len_lim -= 16; + } + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + " " /* indent */); + while (len_lim > 0) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO, "%02x ", *p); + p++; + len_lim--; + } + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO, + "\n"); + } + frm = qdf_nbuf_next(frm); + } +} +#else +#define ol_txrx_frms_dump(name, pdev, frms, display_options, max_len) +#endif /* TXRX_DEBUG_DATA */ + +#ifdef SUPPORT_HOST_STATISTICS + +#define OL_RX_ERR_STATISTICS(pdev, vdev, err_type, sec_type, is_mcast) \ + ol_rx_err_statistics(pdev->ctrl_pdev, vdev->vdev_id, err_type, \ + sec_type, is_mcast) + +#define OL_RX_ERR_STATISTICS_1(pdev, vdev, peer, rx_desc, err_type) \ + do { \ + int is_mcast; \ + enum htt_sec_type sec_type; \ + is_mcast = htt_rx_msdu_is_wlan_mcast( \ + pdev->htt_pdev, rx_desc); \ + sec_type = peer->security[is_mcast \ + ? txrx_sec_mcast \ + : txrx_sec_ucast].sec_type; \ + OL_RX_ERR_STATISTICS(pdev, vdev, err_type, \ + pdev->sec_types[sec_type], \ + is_mcast); \ + } while (false) + +#ifdef CONFIG_HL_SUPPORT + + /** + * ol_rx_err_inv_get_wifi_header() - retrieve wifi header + * @pdev: handle to the physical device + * @rx_msdu: msdu of which header needs to be retrieved + * + * Return: wifi header + */ + static inline + struct ieee80211_frame *ol_rx_err_inv_get_wifi_header( + struct ol_pdev_t *pdev, qdf_nbuf_t rx_msdu) + { + return NULL; + } +#else + + static inline + struct ieee80211_frame *ol_rx_err_inv_get_wifi_header( + struct ol_pdev_t *pdev, qdf_nbuf_t rx_msdu) + { + struct ieee80211_frame *wh = NULL; + + if (ol_cfg_frame_type(pdev) == wlan_frm_fmt_native_wifi) + /* For windows, it is always native wifi header .*/ + wh = (struct ieee80211_frame *)qdf_nbuf_data(rx_msdu); + + return wh; + } +#endif + +#define OL_RX_ERR_INV_PEER_STATISTICS(pdev, rx_msdu) \ + do { \ + struct ieee80211_frame *wh = NULL; \ + /*FIX THIS : */ \ + /* Here htt_rx_mpdu_wifi_hdr_retrieve should be used. */ \ + /*But at present it seems it does not work.*/ \ + /*wh = (struct ieee80211_frame *) */ \ + /*htt_rx_mpdu_wifi_hdr_retrieve(pdev->htt_pdev, rx_desc);*/ \ + /* this only apply to LL device.*/ \ + wh = ol_rx_err_inv_get_wifi_header(pdev->ctrl_pdev, rx_msdu); \ + ol_rx_err_inv_peer_statistics(pdev->ctrl_pdev, \ + wh, OL_RX_ERR_UNKNOWN_PEER); \ + } while (false) + +#define OL_RX_ERR_STATISTICS_2(pdev, vdev, peer, rx_desc, rx_msdu, rx_status) \ + do { \ + enum ol_rx_err_type err_type = OL_RX_ERR_NONE; \ + if (rx_status == htt_rx_status_decrypt_err) \ + err_type = OL_RX_ERR_DECRYPT; \ + else if (rx_status == htt_rx_status_tkip_mic_err) \ + err_type = OL_RX_ERR_TKIP_MIC; \ + else if (rx_status == htt_rx_status_mpdu_length_err) \ + err_type = OL_RX_ERR_MPDU_LENGTH; \ + else if (rx_status == htt_rx_status_mpdu_encrypt_required_err) \ + err_type = OL_RX_ERR_ENCRYPT_REQUIRED; \ + else if (rx_status == htt_rx_status_err_dup) \ + err_type = OL_RX_ERR_DUP; \ + else if (rx_status == htt_rx_status_err_fcs) \ + err_type = OL_RX_ERR_FCS; \ + else \ + err_type = OL_RX_ERR_UNKNOWN; \ + \ + if (vdev && peer) { \ + OL_RX_ERR_STATISTICS_1(pdev, vdev, peer, \ + rx_mpdu_desc, err_type); \ + } else { \ + OL_RX_ERR_INV_PEER_STATISTICS(pdev, rx_msdu); \ + } \ + } while (false) +#else +#define OL_RX_ERR_STATISTICS(pdev, vdev, err_type, sec_type, is_mcast) +#define OL_RX_ERR_STATISTICS_1(pdev, vdev, peer, rx_desc, err_type) +#define OL_RX_ERR_STATISTICS_2(pdev, vdev, peer, rx_desc, rx_msdu, rx_status) +#endif /* SUPPORT_HOST_STATISTICS */ + +#ifdef QCA_ENABLE_OL_TXRX_PEER_STATS +#define OL_TXRX_PEER_STATS_UPDATE_BASE(peer, tx_or_rx, type, msdu) \ + do { \ + qdf_spin_lock_bh(&peer->vdev->pdev->peer_stat_mutex); \ + peer->stats.tx_or_rx.frms.type += 1; \ + peer->stats.tx_or_rx.bytes.type += qdf_nbuf_len(msdu); \ + qdf_spin_unlock_bh(&peer->vdev->pdev->peer_stat_mutex); \ + } while (0) +#define OL_TXRX_PEER_STATS_UPDATE(peer, tx_or_rx, msdu) \ + do { \ + struct ol_txrx_vdev_t *vdev = peer->vdev; \ + struct ol_txrx_pdev_t *pdev = vdev->pdev; \ + uint8_t *dest_addr; \ + if (pdev->frame_format == wlan_frm_fmt_802_3) { \ + dest_addr = qdf_nbuf_data(msdu); \ + } else { /* 802.11 format */ \ + struct ieee80211_frame *frm; \ + frm = (struct ieee80211_frame *) qdf_nbuf_data(msdu); \ + if (vdev->opmode == wlan_op_mode_ap) { \ + dest_addr = (uint8_t *) &(frm->i_addr1[0]); \ + } else { \ + dest_addr = (uint8_t *) &(frm->i_addr3[0]); \ + } \ + } \ + if (qdf_unlikely(QDF_IS_ADDR_BROADCAST(dest_addr))) { \ + OL_TXRX_PEER_STATS_UPDATE_BASE(peer, tx_or_rx, \ + bcast, msdu); \ + } else if (qdf_unlikely(IEEE80211_IS_MULTICAST(dest_addr))) { \ + OL_TXRX_PEER_STATS_UPDATE_BASE(peer, tx_or_rx, \ + mcast, msdu); \ + } else { \ + OL_TXRX_PEER_STATS_UPDATE_BASE(peer, tx_or_rx, \ + ucast, msdu); \ + } \ + } while (0) +#define OL_TX_PEER_STATS_UPDATE(peer, msdu) \ + OL_TXRX_PEER_STATS_UPDATE(peer, tx, msdu) +#define OL_RX_PEER_STATS_UPDATE(peer, msdu) \ + OL_TXRX_PEER_STATS_UPDATE(peer, rx, msdu) +#define OL_TXRX_PEER_STATS_MUTEX_INIT(pdev) \ + qdf_spinlock_create(&pdev->peer_stat_mutex) +#define OL_TXRX_PEER_STATS_MUTEX_DESTROY(pdev) \ + qdf_spinlock_destroy(&pdev->peer_stat_mutex) +#else +#define OL_TX_PEER_STATS_UPDATE(peer, msdu) /* no-op */ +#define OL_RX_PEER_STATS_UPDATE(peer, msdu) /* no-op */ +#define OL_TXRX_PEER_STATS_MUTEX_INIT(peer) /* no-op */ +#define OL_TXRX_PEER_STATS_MUTEX_DESTROY(peer) /* no-op */ +#endif + +#ifndef DEBUG_HTT_CREDIT +#define DEBUG_HTT_CREDIT 0 +#endif + +#if defined(FEATURE_TSO_DEBUG) +#define TXRX_STATS_TSO_HISTOGRAM(_pdev, _p_cntrs) \ + do { \ + if (_p_cntrs == 1) { \ + TXRX_STATS_ADD(_pdev, pub.tx.tso.tso_hist.pkts_1, 1); \ + } else if (_p_cntrs >= 2 && _p_cntrs <= 5) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.tso.tso_hist.pkts_2_5, 1); \ + } else if (_p_cntrs > 5 && _p_cntrs <= 10) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.tso.tso_hist.pkts_6_10, 1); \ + } else if (_p_cntrs > 10 && _p_cntrs <= 15) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.tso.tso_hist.pkts_11_15, 1); \ + } else if (_p_cntrs > 15 && _p_cntrs <= 20) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.tso.tso_hist.pkts_16_20, 1); \ + } else if (_p_cntrs > 20) { \ + TXRX_STATS_ADD(_pdev, \ + pub.tx.tso.tso_hist.pkts_20_plus, 1); \ + } \ + } while (0) + +#define TXRX_STATS_TSO_RESET_MSDU(pdev, idx) \ + do { \ + pdev->stats.pub.tx.tso.tso_info.tso_msdu_info[idx].num_seg \ + = 0; \ + pdev->stats.pub.tx.tso.tso_info.tso_msdu_info[idx].tso_seg_idx \ + = 0; \ + } while (0) + +#define TXRX_STATS_TSO_MSDU_IDX(pdev) \ + pdev->stats.pub.tx.tso.tso_info.tso_msdu_idx + +#define TXRX_STATS_TSO_MSDU(pdev, idx) \ + pdev->stats.pub.tx.tso.tso_info.tso_msdu_info[idx] + +#define TXRX_STATS_TSO_MSDU_NUM_SEG(pdev, idx) \ + pdev->stats.pub.tx.tso.tso_info.tso_msdu_info[idx].num_seg + +#define TXRX_STATS_TSO_MSDU_GSO_SIZE(pdev, idx) \ + pdev->stats.pub.tx.tso.tso_info.tso_msdu_info[idx].gso_size + +#define TXRX_STATS_TSO_MSDU_TOTAL_LEN(pdev, idx) \ + pdev->stats.pub.tx.tso.tso_info.tso_msdu_info[idx].total_len + +#define TXRX_STATS_TSO_MSDU_NR_FRAGS(pdev, idx) \ + pdev->stats.pub.tx.tso.tso_info.tso_msdu_info[idx].nr_frags + +#define TXRX_STATS_TSO_CURR_MSDU(pdev, idx) \ + TXRX_STATS_TSO_MSDU(pdev, idx) + +#define TXRX_STATS_TSO_SEG_IDX(pdev, idx) \ + TXRX_STATS_TSO_CURR_MSDU(pdev, idx).tso_seg_idx + +#define TXRX_STATS_TSO_INC_SEG(pdev, idx) \ + do { \ + TXRX_STATS_TSO_CURR_MSDU(pdev, idx).num_seg++; \ + TXRX_STATS_TSO_CURR_MSDU(pdev, idx).num_seg &= \ + NUM_MAX_TSO_SEGS_MASK; \ + } while (0) + +#define TXRX_STATS_TSO_RST_SEG(pdev, idx) \ + TXRX_STATS_TSO_CURR_MSDU(pdev, idx).num_seg = 0 + +#define TXRX_STATS_TSO_RST_SEG_IDX(pdev, idx) \ + TXRX_STATS_TSO_CURR_MSDU(pdev, idx).tso_seg_idx = 0 + +#define TXRX_STATS_TSO_SEG(pdev, msdu_idx, seg_idx) \ + TXRX_STATS_TSO_MSDU(pdev, msdu_idx).tso_segs[seg_idx] + +#define TXRX_STATS_TSO_CURR_SEG(pdev, idx) \ + TXRX_STATS_TSO_SEG(pdev, idx, \ + TXRX_STATS_TSO_SEG_IDX(pdev, idx)) \ + +#define TXRX_STATS_TSO_INC_SEG_IDX(pdev, idx) \ + do { \ + TXRX_STATS_TSO_SEG_IDX(pdev, idx)++; \ + TXRX_STATS_TSO_SEG_IDX(pdev, idx) &= NUM_MAX_TSO_SEGS_MASK; \ + } while (0) + +#define TXRX_STATS_TSO_SEG_UPDATE(pdev, idx, tso_seg) \ + (TXRX_STATS_TSO_CURR_SEG(pdev, idx) = tso_seg) + +#define TXRX_STATS_TSO_GSO_SIZE_UPDATE(pdev, idx, size) \ + (TXRX_STATS_TSO_CURR_MSDU(pdev, idx).gso_size = size) + +#define TXRX_STATS_TSO_TOTAL_LEN_UPDATE(pdev, idx, len) \ + (TXRX_STATS_TSO_CURR_MSDU(pdev, idx).total_len = len) + +#define TXRX_STATS_TSO_NUM_FRAGS_UPDATE(pdev, idx, frags) \ + (TXRX_STATS_TSO_CURR_MSDU(pdev, idx).nr_frags = frags) + +#else +#define TXRX_STATS_TSO_HISTOGRAM(_pdev, _p_cntrs) /* no-op */ +#define TXRX_STATS_TSO_RESET_MSDU(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_MSDU_IDX(pdev) /* no-op */ +#define TXRX_STATS_TSO_MSDU(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_MSDU_NUM_SEG(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_CURR_MSDU(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_INC_MSDU_IDX(pdev) /* no-op */ +#define TXRX_STATS_TSO_SEG_IDX(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_SEG(pdev, msdu_idx, seg_idx) /* no-op */ +#define TXRX_STATS_TSO_CURR_SEG(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_INC_SEG_IDX(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_SEG_UPDATE(pdev, idx, tso_seg) /* no-op */ +#define TXRX_STATS_TSO_INC_SEG(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_RST_SEG(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_RST_SEG_IDX(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_GSO_SIZE_UPDATE(pdev, idx, size) /* no-op */ +#define TXRX_STATS_TSO_TOTAL_LEN_UPDATE(pdev, idx, len) /* no-op */ +#define TXRX_STATS_TSO_NUM_FRAGS_UPDATE(pdev, idx, frags) /* no-op */ +#define TXRX_STATS_TSO_MSDU_GSO_SIZE(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_MSDU_TOTAL_LEN(pdev, idx) /* no-op */ +#define TXRX_STATS_TSO_MSDU_NR_FRAGS(pdev, idx) /* no-op */ + +#endif /* FEATURE_TSO_DEBUG */ + +#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL + +void +ol_txrx_update_group_credit( + struct ol_tx_queue_group_t *group, + int32_t credit, + u_int8_t absolute); +#endif + +#endif /* _OL_TXRX_INTERNAL__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_ipa.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_ipa.c new file mode 100644 index 0000000000000000000000000000000000000000..a5febdcf048884988040d8f4bbad428f44d05183 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_ipa.c @@ -0,0 +1,1730 @@ +/* + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*=== includes ===*/ +/* header files for OS primitives */ +#include /* uint32_t, etc. */ +#include /* qdf_mem_malloc,free */ +#include /* qdf_device_t, qdf_print */ +#include /* qdf_spinlock */ +#include /* qdf_atomic_read */ + +/* header files for utilities */ +#include "queue.h" /* TAILQ */ + +/* header files for configuration API */ +#include /* ol_cfg_is_high_latency */ +#include + +/* header files for HTT API */ +#include +#include + +/* header files for our own APIs */ +#include +#include +#include +#include +#include +#include +/* header files for our internal definitions */ +#include /* TXRX_ASSERT, etc. */ +#include /* WDI events */ +#include /* ol_tx_ll */ +#include /* ol_rx_deliver */ +#include /* ol_txrx_peer_find_attach, etc. */ +#include /* ol_rx_pn_check, etc. */ +#include /* ol_rx_fwd_check, etc. */ +#include /* OL_RX_REORDER_TIMEOUT_INIT, etc. */ +#include +#include /* ol_tx_discard_target_frms */ +#include /* ol_tx_desc_frame_free */ +#include +#include /* ol_tx_sched_attach, etc. */ +#include +#include +#include +#include +#include +#include +#include "wma.h" +#include "hif.h" +#include +#ifndef REMOVE_PKT_LOG +#include "pktlog_ac.h" +#endif +#include "epping_main.h" +#include + +#ifdef IPA_OFFLOAD +#include + +/* For Tx pipes, use Ethernet-II Header format */ +#ifdef QCA_WIFI_3_0 +struct ol_txrx_ipa_uc_tx_hdr ipa_uc_tx_hdr = { + { + 0x0000, + 0x00000000, + 0x00000000 + }, + { + 0x00000000 + }, + { + {0x00, 0x03, 0x7f, 0xaa, 0xbb, 0xcc}, + {0x00, 0x03, 0x7f, 0xdd, 0xee, 0xff}, + 0x0008 + } +}; +#else +struct ol_txrx_ipa_uc_tx_hdr ipa_uc_tx_hdr = { + { + 0x00000000, + 0x00000000 + }, + { + 0x00000000 + }, + { + {0x00, 0x03, 0x7f, 0xaa, 0xbb, 0xcc}, + {0x00, 0x03, 0x7f, 0xdd, 0xee, 0xff}, + 0x0008 + } +}; +#endif + +QDF_STATUS ol_txrx_ipa_uc_get_resource(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_txrx_ipa_resources *ipa_res; + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!osdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: qdf device is null!", __func__); + return QDF_STATUS_E_NOENT; + } + + ipa_res = &pdev->ipa_resource; + htt_ipa_uc_get_resource(pdev->htt_pdev, + &ipa_res->ce_sr, + &ipa_res->tx_comp_ring, + &ipa_res->rx_rdy_ring, + &ipa_res->rx2_rdy_ring, + &ipa_res->rx_proc_done_idx, + &ipa_res->rx2_proc_done_idx, + &ipa_res->ce_sr_ring_size, + &ipa_res->ce_reg_paddr, + &ipa_res->tx_num_alloc_buffer); + + if ((0 == qdf_mem_get_dma_addr(osdev, + &ipa_res->ce_sr->mem_info)) || + (0 == qdf_mem_get_dma_addr(osdev, + &ipa_res->tx_comp_ring->mem_info)) || + (0 == qdf_mem_get_dma_addr(osdev, + &ipa_res->rx_rdy_ring->mem_info)) +#if defined(QCA_WIFI_3_0) && defined(CONFIG_IPA3) + || (0 == qdf_mem_get_dma_addr(osdev, + &ipa_res->rx2_rdy_ring->mem_info)) +#endif + ) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_uc_set_doorbell_paddr(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_txrx_ipa_resources *ipa_res; + int ret; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + ipa_res = &pdev->ipa_resource; + ret = htt_ipa_uc_set_doorbell_paddr(pdev->htt_pdev, + ipa_res->tx_comp_doorbell_dmaaddr, + ipa_res->rx_ready_doorbell_dmaaddr); + + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "htt_ipa_uc_set_doorbell_dmaaddr fail: %d", ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_uc_set_active(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + bool uc_active, bool is_tx) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + int ret; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + ret = htt_h2t_ipa_uc_set_active(pdev->htt_pdev, uc_active, is_tx); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "htt_h2t_ipa_uc_set_active fail: %d", ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_uc_op_response(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, uint8_t *op_msg) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (pdev->ipa_uc_op_cb) { + pdev->ipa_uc_op_cb(op_msg, pdev->usr_ctxt); + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: IPA callback function is not registered", __func__); + qdf_mem_free(op_msg); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_uc_register_op_cb(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, + ipa_uc_op_cb_type op_cb, + void *usr_ctxt) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + pdev->ipa_uc_op_cb = op_cb; + pdev->usr_ctxt = usr_ctxt; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_uc_get_stat(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + int ret; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + ret = htt_h2t_ipa_uc_get_stats(pdev->htt_pdev); + + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "htt_h2t_ipa_uc_get_stats fail: %d", ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_enable_autonomy(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + /* TBD */ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_disable_autonomy(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id) +{ + /* TBD */ + return QDF_STATUS_SUCCESS; +} + +#ifdef CONFIG_IPA_WDI_UNIFIED_API + +#ifndef QCA_LL_TX_FLOW_CONTROL_V2 +static inline void ol_txrx_setup_mcc_sys_pipes( + qdf_ipa_sys_connect_params_t *sys_in, + qdf_ipa_wdi_conn_in_params_t *pipe_in) +{ + int i = 0; + + /* Setup MCC sys pipe */ + QDF_IPA_WDI_CONN_IN_PARAMS_NUM_SYS_PIPE_NEEDED(pipe_in) = + OL_TXRX_IPA_MAX_IFACE; + for (i = 0; i < OL_TXRX_IPA_MAX_IFACE; i++) + memcpy(&QDF_IPA_WDI_CONN_IN_PARAMS_SYS_IN(pipe_in)[i], + &sys_in[i], sizeof(qdf_ipa_sys_connect_params_t)); +} +#else +static inline void ol_txrx_setup_mcc_sys_pipes( + qdf_ipa_sys_connect_params_t *sys_in, + qdf_ipa_wdi_conn_in_params_t *pipe_in) +{ + QDF_IPA_WDI_CONN_IN_PARAMS_NUM_SYS_PIPE_NEEDED(pipe_in) = 0; +} +#endif + +#ifdef ENABLE_SMMU_S1_TRANSLATION +/** + * ol_txrx_ipa_wdi_tx_smmu_params() - Config IPA TX params + * @ipa_res: IPA resources + * @tx_smmu: IPA WDI pipe setup info + * + * Return: None + */ +static inline void ol_txrx_ipa_wdi_tx_smmu_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_pipe_setup_info_smmu_t *tx_smmu) +{ + QDF_IPA_WDI_SETUP_INFO_SMMU_CLIENT(tx_smmu) = + IPA_CLIENT_WLAN1_CONS; + qdf_mem_copy(&QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_BASE( + tx_smmu), + &ipa_res->tx_comp_ring->sgtable, + sizeof(sgtable_t)); + QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_SIZE(tx_smmu) = + ipa_res->tx_comp_ring->mem_info.size; + qdf_mem_copy(&QDF_IPA_WDI_SETUP_INFO_SMMU_EVENT_RING_BASE( + tx_smmu), + &ipa_res->ce_sr->sgtable, + sizeof(sgtable_t)); + QDF_IPA_WDI_SETUP_INFO_SMMU_EVENT_RING_SIZE(tx_smmu) = + ipa_res->ce_sr_ring_size; + QDF_IPA_WDI_SETUP_INFO_SMMU_EVENT_RING_DOORBELL_PA(tx_smmu) = + ipa_res->ce_reg_paddr; + QDF_IPA_WDI_SETUP_INFO_SMMU_NUM_PKT_BUFFERS(tx_smmu) = + ipa_res->tx_num_alloc_buffer; + QDF_IPA_WDI_SETUP_INFO_SMMU_PKT_OFFSET(tx_smmu) = 0; +} + +#ifdef QCA_WIFI_3_0 +/** + * ol_txrx_ipa_wdi_rx_smmu_params() - Config IPA RX params + * @ipa_res: IPA resources + * @rx_smmu: IPA WDI pipe setup info + * + * Return: None + */ +static inline void ol_txrx_ipa_wdi_rx_smmu_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_pipe_setup_info_smmu_t *rx_smmu) +{ + QDF_IPA_WDI_SETUP_INFO_SMMU_CLIENT(rx_smmu) = + IPA_CLIENT_WLAN1_PROD; + qdf_mem_copy(&QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_BASE( + rx_smmu), + &ipa_res->rx_rdy_ring->sgtable, + sizeof(sgtable_t)); + QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_SIZE(rx_smmu) = + ipa_res->rx_rdy_ring->mem_info.size; + QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_DOORBELL_PA(rx_smmu) = + ipa_res->rx_proc_done_idx->mem_info.pa; + qdf_mem_copy(&QDF_IPA_WDI_SETUP_INFO_SMMU_EVENT_RING_BASE( + rx_smmu), + &ipa_res->rx2_rdy_ring->sgtable, + sizeof(sgtable_t)); + QDF_IPA_WDI_SETUP_INFO_SMMU_EVENT_RING_SIZE(rx_smmu) = + ipa_res->rx2_rdy_ring->mem_info.size; + QDF_IPA_WDI_SETUP_INFO_SMMU_EVENT_RING_DOORBELL_PA(rx_smmu) = + ipa_res->rx2_proc_done_idx->mem_info.pa; + QDF_IPA_WDI_SETUP_INFO_SMMU_PKT_OFFSET(rx_smmu) = 0; + +} +#else +static inline void ol_txrx_ipa_wdi_rx_smmu_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_pipe_setup_info_smmu_t *rx_smmu) +{ + QDF_IPA_WDI_SETUP_INFO_SMMU_CLIENT(rx_smmu) = + IPA_CLIENT_WLAN1_PROD; + qdf_mem_copy(&QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_BASE( + rx_smmu), + &ipa_res->rx_rdy_ring->sgtable, + sizeof(sgtable_t)); + QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_SIZE(rx_smmu) = + ipa_res->rx_rdy_ring->mem_info.size; + QDF_IPA_WDI_SETUP_INFO_SMMU_TRANSFER_RING_DOORBELL_PA(rx_smmu) = + ipa_res->rx_proc_done_idx->mem_info.pa; + QDF_IPA_WDI_SETUP_INFO_SMMU_PKT_OFFSET(rx_smmu) = 0; +} + +#endif + +#else + +static inline void ol_txrx_ipa_wdi_tx_smmu_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_pipe_setup_info_smmu_t *tx_smmu) +{ +} + +static inline void ol_txrx_ipa_wdi_rx_smmu_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_pipe_setup_info_smmu_t *rx_smmu) +{ +} +#endif + +/** + * ol_txrx_ipa_wdi_tx_params() - Config IPA TX params + * @ipa_res: IPA resources + * @tx: IPA WDI pipe setup info + * + * Return: None + */ +static inline void ol_txrx_ipa_wdi_tx_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_pipe_setup_info_t *tx) +{ + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!osdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: qdf device is null!", __func__); + return; + } + + QDF_IPA_WDI_SETUP_INFO_CLIENT(tx) = IPA_CLIENT_WLAN1_CONS; + QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(tx) = + qdf_mem_get_dma_addr(osdev, + &ipa_res->tx_comp_ring->mem_info); + QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(tx) = + ipa_res->tx_comp_ring->mem_info.size; + QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(tx) = + qdf_mem_get_dma_addr(osdev, + &ipa_res->ce_sr->mem_info); + QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(tx) = + ipa_res->ce_sr_ring_size; + QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(tx) = + ipa_res->ce_reg_paddr; + QDF_IPA_WDI_SETUP_INFO_NUM_PKT_BUFFERS(tx) = + ipa_res->tx_num_alloc_buffer; + QDF_IPA_WDI_SETUP_INFO_PKT_OFFSET(tx) = 0; +} + +#ifdef QCA_WIFI_3_0 +/** + * ol_txrx_ipa_wdi_rx_params() - Config IPA RX params + * @ipa_res: IPA resources + * @rx: IPA WDI pipe setup info + * + * Return: None + */ +static inline void ol_txrx_ipa_wdi_rx_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_pipe_setup_info_t *rx) +{ + QDF_IPA_WDI_SETUP_INFO_CLIENT(rx) = IPA_CLIENT_WLAN1_PROD; + QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(rx) = + ipa_res->rx_rdy_ring->mem_info.pa; + QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(rx) = + ipa_res->rx_rdy_ring->mem_info.size; + QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(rx) = + ipa_res->rx_proc_done_idx->mem_info.pa; + QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(rx) = + ipa_res->rx2_rdy_ring->mem_info.pa; + QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(rx) = + ipa_res->rx2_rdy_ring->mem_info.size; + QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(rx) = + ipa_res->rx2_proc_done_idx->mem_info.pa; + QDF_IPA_WDI_SETUP_INFO_PKT_OFFSET(rx) = 0; +} + +#else + +static inline void ol_txrx_ipa_wdi_rx_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_pipe_setup_info_t *rx) +{ + QDF_IPA_WDI_SETUP_INFO_CLIENT(rx) = IPA_CLIENT_WLAN1_PROD; + QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(rx) = + ipa_res->rx_rdy_ring->mem_info.pa; + QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(rx) = + ipa_res->rx_rdy_ring->mem_info.size; + QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(rx) = + ipa_res->rx_proc_done_idx->mem_info.pa; + QDF_IPA_WDI_SETUP_INFO_PKT_OFFSET(rx) = 0; +} + +#endif + +QDF_STATUS ol_txrx_ipa_setup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + void *ipa_i2w_cb, void *ipa_w2i_cb, + void *ipa_wdi_meter_notifier_cb, + uint32_t ipa_desc_size, void *ipa_priv, + bool is_rm_enabled, uint32_t *p_tx_pipe_handle, + uint32_t *p_rx_pipe_handle, bool is_smmu_enabled, + qdf_ipa_sys_connect_params_t *sys_in, + bool over_gsi) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_txrx_ipa_resources *ipa_res; + qdf_ipa_ep_cfg_t *tx_cfg; + qdf_ipa_ep_cfg_t *rx_cfg; + qdf_ipa_wdi_pipe_setup_info_t *tx; + qdf_ipa_wdi_pipe_setup_info_t *rx; + qdf_ipa_wdi_pipe_setup_info_smmu_t *tx_smmu; + qdf_ipa_wdi_pipe_setup_info_smmu_t *rx_smmu; + qdf_ipa_wdi_conn_in_params_t *pipe_in = NULL; + qdf_ipa_wdi_conn_out_params_t pipe_out; + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + uint32_t tx_comp_db_dmaaddr = 0, rx_rdy_db_dmaaddr = 0; + int ret; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!osdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: qdf device is null!", __func__); + return QDF_STATUS_E_NOENT; + } + + pipe_in = qdf_mem_malloc(sizeof(*pipe_in)); + if (!pipe_in) { + ol_txrx_err("pipe_in allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + ipa_res = &pdev->ipa_resource; + qdf_mem_zero(pipe_in, sizeof(*pipe_in)); + qdf_mem_zero(&pipe_out, sizeof(pipe_out)); + + ol_txrx_setup_mcc_sys_pipes(sys_in, pipe_in); + + /* TX PIPE */ + if (is_smmu_enabled) { + QDF_IPA_WDI_CONN_IN_PARAMS_SMMU_ENABLED(pipe_in) = true; + tx_smmu = &QDF_IPA_WDI_CONN_IN_PARAMS_TX_SMMU(pipe_in); + tx_cfg = &QDF_IPA_WDI_SETUP_INFO_SMMU_EP_CFG(tx_smmu); + } else { + QDF_IPA_WDI_CONN_IN_PARAMS_SMMU_ENABLED(pipe_in) = false; + tx = &QDF_IPA_WDI_CONN_IN_PARAMS_TX(pipe_in); + tx_cfg = &QDF_IPA_WDI_SETUP_INFO_EP_CFG(tx); + } + + QDF_IPA_EP_CFG_NAT_EN(tx_cfg) = IPA_BYPASS_NAT; + QDF_IPA_EP_CFG_HDR_LEN(tx_cfg) = OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN; + QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE_VALID(tx_cfg) = 1; + QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE(tx_cfg) = 0; + QDF_IPA_EP_CFG_HDR_ADDITIONAL_CONST_LEN(tx_cfg) = + OL_TXRX_IPA_UC_WLAN_8023_HDR_SIZE; + QDF_IPA_EP_CFG_MODE(tx_cfg) = IPA_BASIC; + QDF_IPA_EP_CFG_HDR_LITTLE_ENDIAN(tx_cfg) = true; + + if (is_smmu_enabled) + ol_txrx_ipa_wdi_tx_smmu_params(ipa_res, tx_smmu); + else + ol_txrx_ipa_wdi_tx_params(ipa_res, tx); + + + /* RX PIPE */ + if (is_smmu_enabled) { + rx_smmu = &QDF_IPA_WDI_CONN_IN_PARAMS_RX_SMMU(pipe_in); + rx_cfg = &QDF_IPA_WDI_SETUP_INFO_EP_CFG(rx_smmu); + } else { + rx = &QDF_IPA_WDI_CONN_IN_PARAMS_RX(pipe_in); + rx_cfg = &QDF_IPA_WDI_SETUP_INFO_EP_CFG(rx); + } + + QDF_IPA_EP_CFG_NAT_EN(rx_cfg) = IPA_BYPASS_NAT; + QDF_IPA_EP_CFG_HDR_LEN(rx_cfg) = OL_TXRX_IPA_UC_WLAN_RX_HDR_LEN; + QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE_VALID(rx_cfg) = 1; + QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE(rx_cfg) = 0; + QDF_IPA_EP_CFG_HDR_ADDITIONAL_CONST_LEN(rx_cfg) = + OL_TXRX_IPA_UC_WLAN_8023_HDR_SIZE; + QDF_IPA_EP_CFG_HDR_OFST_METADATA_VALID(rx_cfg) = 0; + QDF_IPA_EP_CFG_HDR_METADATA_REG_VALID(rx_cfg) = 1; + QDF_IPA_EP_CFG_MODE(rx_cfg) = IPA_BASIC; + QDF_IPA_EP_CFG_HDR_LITTLE_ENDIAN(rx_cfg) = true; + + if (is_smmu_enabled) + ol_txrx_ipa_wdi_rx_smmu_params(ipa_res, rx_smmu); + else + ol_txrx_ipa_wdi_rx_params(ipa_res, rx); + + QDF_IPA_WDI_CONN_IN_PARAMS_NOTIFY(pipe_in) = ipa_w2i_cb; + QDF_IPA_WDI_CONN_IN_PARAMS_PRIV(pipe_in) = ipa_priv; + + /* Connect WDI IPA PIPE */ + ret = qdf_ipa_wdi_conn_pipes(pipe_in, &pipe_out); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: ipa_wdi_conn_pipes: IPA pipe setup failed: ret=%d", + __func__, ret); + qdf_mem_free(pipe_in); + return QDF_STATUS_E_FAILURE; + } + + /* IPA uC Doorbell registers */ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Tx DB PA=0x%x, Rx DB PA=0x%x", __func__, + (unsigned int) + QDF_IPA_WDI_CONN_OUT_PARAMS_TX_UC_DB_PA(&pipe_out), + (unsigned int) + QDF_IPA_WDI_CONN_OUT_PARAMS_RX_UC_DB_PA(&pipe_out)); + + ipa_res->tx_comp_doorbell_dmaaddr = + QDF_IPA_WDI_CONN_OUT_PARAMS_TX_UC_DB_PA(&pipe_out); + ipa_res->rx_ready_doorbell_dmaaddr = + QDF_IPA_WDI_CONN_OUT_PARAMS_RX_UC_DB_PA(&pipe_out); + + if (is_smmu_enabled) { + pld_smmu_map(osdev->dev, ipa_res->tx_comp_doorbell_dmaaddr, + &tx_comp_db_dmaaddr, sizeof(uint32_t)); + ipa_res->tx_comp_doorbell_dmaaddr = tx_comp_db_dmaaddr; + + pld_smmu_map(osdev->dev, ipa_res->rx_ready_doorbell_dmaaddr, + &rx_rdy_db_dmaaddr, sizeof(uint32_t)); + ipa_res->rx_ready_doorbell_dmaaddr = rx_rdy_db_dmaaddr; + } + + qdf_mem_free(pipe_in); + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_ipa_cleanup() - Disconnect IPA pipes + * @soc_hdl: soc handle + * @pdev_id: pdev id + * @tx_pipe_handle: Tx pipe handle + * @rx_pipe_handle: Rx pipe handle + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_cleanup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint32_t tx_pipe_handle, + uint32_t rx_pipe_handle) +{ + int ret; + struct ol_txrx_ipa_resources *ipa_res; + struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC); + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + ol_txrx_pdev_handle pdev; + + if (!soc || !osdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + pdev = ol_txrx_get_pdev_from_pdev_id(soc, OL_TXRX_PDEV_ID); + if (!pdev) { + ol_txrx_err("%s NULL pdev invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + ipa_res = &pdev->ipa_resource; + if (osdev->smmu_s1_enabled) { + ret = pld_smmu_unmap(osdev->dev, + ipa_res->rx_ready_doorbell_dmaaddr, + sizeof(uint32_t)); + if (ret) + ol_txrx_err("%s rx_ready, smmu unmap failed", __func__); + + ret = pld_smmu_unmap(osdev->dev, + ipa_res->tx_comp_doorbell_dmaaddr, + sizeof(uint32_t)); + if (ret) + ol_txrx_err("%s tx_comp, smmu unmap failed", __func__); + } + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Disconnect IPA pipe", __func__); + ret = qdf_ipa_wdi_disconn_pipes(); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "ipa_wdi_disconn_pipes failed: ret=%d", ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_ipa_setup_iface() - Setup IPA header and register interface + * @ifname: Interface name + * @mac_addr: Interface MAC address + * @prod_client: IPA prod client type + * @cons_client: IPA cons client type + * @session_id: Session ID + * @is_ipv6_enabled: Is IPV6 enabled or not + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_setup_iface(char *ifname, uint8_t *mac_addr, + qdf_ipa_client_type_t prod_client, + qdf_ipa_client_type_t cons_client, + uint8_t session_id, bool is_ipv6_enabled) +{ + qdf_ipa_wdi_reg_intf_in_params_t in; + qdf_ipa_wdi_hdr_info_t hdr_info; + struct ol_txrx_ipa_uc_tx_hdr uc_tx_hdr; + struct ol_txrx_ipa_uc_tx_hdr uc_tx_hdr_v6; + int ret = -EINVAL; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Add Partial hdr: %s, "QDF_MAC_ADDR_FMT, + __func__, ifname, QDF_MAC_ADDR_REF(mac_addr)); + + qdf_mem_zero(&in, sizeof(qdf_ipa_wdi_reg_intf_in_params_t)); + qdf_mem_zero(&hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t)); + memcpy(&uc_tx_hdr, &ipa_uc_tx_hdr, OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN); + qdf_ether_addr_copy(uc_tx_hdr.eth.h_source, mac_addr); + uc_tx_hdr.ipa_hd.vdev_id = session_id; + + /* IPV4 header */ + uc_tx_hdr.eth.h_proto = qdf_htons(ETH_P_IP); + + QDF_IPA_WDI_HDR_INFO_HDR(&hdr_info) = (uint8_t *)&uc_tx_hdr; + QDF_IPA_WDI_HDR_INFO_HDR_LEN(&hdr_info) = + OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN; + QDF_IPA_WDI_HDR_INFO_HDR_TYPE(&hdr_info) = IPA_HDR_L2_ETHERNET_II; + QDF_IPA_WDI_HDR_INFO_DST_MAC_ADDR_OFFSET(&hdr_info) = + OL_TXRX_IPA_UC_WLAN_HDR_DES_MAC_OFFSET; + + QDF_IPA_WDI_REG_INTF_IN_PARAMS_NETDEV_NAME(&in) = ifname; + memcpy(&(QDF_IPA_WDI_REG_INTF_IN_PARAMS_HDR_INFO(&in)[IPA_IP_v4]), + &hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t)); + QDF_IPA_WDI_REG_INTF_IN_PARAMS_ALT_DST_PIPE(&in) = cons_client; + QDF_IPA_WDI_REG_INTF_IN_PARAMS_IS_META_DATA_VALID(&in) = 1; + QDF_IPA_WDI_REG_INTF_IN_PARAMS_META_DATA(&in) = + htonl(session_id << 16); + QDF_IPA_WDI_REG_INTF_IN_PARAMS_META_DATA_MASK(&in) = htonl(0x00FF0000); + + /* IPV6 header */ + if (is_ipv6_enabled) { + memcpy(&uc_tx_hdr_v6, &uc_tx_hdr, + OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN); + uc_tx_hdr_v6.eth.h_proto = qdf_htons(ETH_P_IPV6); + QDF_IPA_WDI_HDR_INFO_HDR(&hdr_info) = (uint8_t *)&uc_tx_hdr_v6; + memcpy(&(QDF_IPA_WDI_REG_INTF_IN_PARAMS_HDR_INFO(&in)[IPA_IP_v6]), + &hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t)); + } + + ret = qdf_ipa_wdi_reg_intf(&in); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: ipa_wdi_reg_intf falied: ret=%d", __func__, ret); + } + + return ret; +} + +/** + * ol_txrx_ipa_cleanup_iface() - Cleanup IPA header and deregister interface + * @ifname: Interface name + * @is_ipv6_enabled: Is IPV6 enabled or not + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_cleanup_iface(char *ifname, bool is_ipv6_enabled) +{ + int ret; + + /* unregister the interface with IPA */ + ret = qdf_ipa_wdi_dereg_intf(ifname); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: ipa_wdi_dereg_intf failed: devname=%s, ret=%d", + __func__, ifname, ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_enable_pipes(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + QDF_STATUS status; + int ret; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + status = htt_rx_update_smmu_map(pdev->htt_pdev, true); + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "IPA SMMU map failed status:%d", status); + return status; + } + + /* ACTIVATE TX PIPE */ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Enable IPA pipes", __func__); + ret = qdf_ipa_wdi_enable_pipes(); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: ipa_wdi_enable_pipes failed: ret=%d", + __func__, ret); + status = htt_rx_update_smmu_map(pdev->htt_pdev, false); + if (status != QDF_STATUS_SUCCESS) + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "IPA SMMU unmap failed"); + return QDF_STATUS_E_FAILURE; + } + + ol_txrx_ipa_uc_set_active(soc_hdl, pdev_id, true, true); + ol_txrx_ipa_uc_set_active(soc_hdl, pdev_id, true, false); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_disable_pipes(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + int ret; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Disable IPA pipes", __func__); + ret = qdf_ipa_wdi_disable_pipes(); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: ipa_wdi_disable_pipes failed: ret=%d", + __func__, ret); + } + + if (htt_rx_update_smmu_map(pdev->htt_pdev, false) != + QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "IPA SMMU unmap failed"); + } + + return ret ? QDF_STATUS_E_FAILURE : QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_ipa_set_perf_level() - Set IPA clock bandwidth based on data rates + * @client: Client type + * @max_supported_bw_mbps: Maximum bandwidth needed (in Mbps) + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_set_perf_level(int client, + uint32_t max_supported_bw_mbps) +{ + qdf_ipa_wdi_perf_profile_t profile; + int result; + + QDF_IPA_WDI_PERF_PROFILE_CLIENT(&profile) = client; + QDF_IPA_WDI_PERF_PROFILE_MAX_SUPPORTED_BW_MBPS(&profile) = + max_supported_bw_mbps; + result = qdf_ipa_wdi_set_perf_profile(&profile); + + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Set perf profile failed, code %d", result); + + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#else /* CONFIG_IPA_WDI_UNIFIED_API */ + +#ifdef ENABLE_SMMU_S1_TRANSLATION +/** + * ol_txrx_ipa_tx_smmu_params() - Config IPA TX params + * @ipa_res: IPA resources + * @pipe_in: IPA WDI TX pipe params + * + * Return: None + */ +static inline void ol_txrx_ipa_tx_smmu_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_in_params_t *pipe_in) +{ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: SMMU Enabled", __func__); + + QDF_IPA_PIPE_IN_SMMU_ENABLED(pipe_in) = true; + qdf_mem_copy(&QDF_IPA_PIPE_IN_DL_SMMU_COMP_RING(pipe_in), + &ipa_res->tx_comp_ring->sgtable, + sizeof(sgtable_t)); + QDF_IPA_PIPE_IN_DL_SMMU_COMP_RING_SIZE(pipe_in) = + ipa_res->tx_comp_ring->mem_info.size; + qdf_mem_copy(&QDF_IPA_PIPE_IN_DL_SMMU_CE_RING(pipe_in), + &ipa_res->ce_sr->sgtable, + sizeof(sgtable_t)); + QDF_IPA_PIPE_IN_DL_SMMU_CE_DOOR_BELL_PA(pipe_in) = + ipa_res->ce_reg_paddr; + QDF_IPA_PIPE_IN_DL_SMMU_CE_RING_SIZE(pipe_in) = + ipa_res->ce_sr_ring_size; + QDF_IPA_PIPE_IN_DL_SMMU_NUM_TX_BUFFERS(pipe_in) = + ipa_res->tx_num_alloc_buffer; +} + +/** + * ol_txrx_ipa_rx_smmu_params() - Config IPA TX params + * @ipa_res: IPA resources + * @pipe_in: IPA WDI TX pipe params + * + * Return: None + */ +static inline void ol_txrx_ipa_rx_smmu_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_in_params_t *pipe_in) +{ + qdf_mem_copy(&QDF_IPA_PIPE_IN_UL_SMMU_RDY_RING(pipe_in), + &ipa_res->rx_rdy_ring->sgtable, + sizeof(sgtable_t)); + QDF_IPA_PIPE_IN_UL_SMMU_RDY_RING_SIZE(pipe_in) = + ipa_res->rx_rdy_ring->mem_info.size; + QDF_IPA_PIPE_IN_UL_SMMU_RDY_RING_RP_PA(pipe_in) = + ipa_res->rx_proc_done_idx->mem_info.pa; + + QDF_IPA_PIPE_IN_UL_SMMU_RDY_RING_RP_VA(pipe_in) = + ipa_res->rx_proc_done_idx->vaddr; + qdf_mem_copy(&QDF_IPA_PIPE_IN_UL_SMMU_RDY_COMP_RING(pipe_in), + &ipa_res->rx2_rdy_ring->sgtable, + sizeof(sgtable_t)); + QDF_IPA_PIPE_IN_UL_SMMU_RDY_COMP_RING_SIZE(pipe_in) = + ipa_res->rx2_rdy_ring->mem_info.size; + QDF_IPA_PIPE_IN_UL_SMMU_RDY_COMP_RING_WP_PA(pipe_in) = + ipa_res->rx2_proc_done_idx->mem_info.pa; + QDF_IPA_PIPE_IN_UL_SMMU_RDY_COMP_RING_WP_VA(pipe_in) = + ipa_res->rx2_proc_done_idx->vaddr; +} +#else +static inline void ol_txrx_ipa_tx_smmu_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_in_params_t *pipe_in) +{ +} + +static inline void ol_txrx_ipa_rx_smmu_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_in_params_t *pipe_in) +{ +} +#endif + +/** + * ol_txrx_ipa_tx_params() - Config IPA TX params + * @ipa_res: IPA resources + * @pipe_in: IPA WDI TX pipe params + * + * Return: None + */ +static inline void ol_txrx_ipa_tx_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_in_params_t *pipe_in) +{ + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!osdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: qdf device is null!", __func__); + return; + } + + QDF_IPA_PIPE_IN_DL_COMP_RING_BASE_PA(pipe_in) = + qdf_mem_get_dma_addr(osdev, + &ipa_res->tx_comp_ring->mem_info); + QDF_IPA_PIPE_IN_DL_COMP_RING_SIZE(pipe_in) = + ipa_res->tx_comp_ring->mem_info.size; + QDF_IPA_PIPE_IN_DL_CE_RING_BASE_PA(pipe_in) = + qdf_mem_get_dma_addr(osdev, + &ipa_res->ce_sr->mem_info); + QDF_IPA_PIPE_IN_DL_CE_DOOR_BELL_PA(pipe_in) = + ipa_res->ce_reg_paddr; + QDF_IPA_PIPE_IN_DL_CE_RING_SIZE(pipe_in) = + ipa_res->ce_sr_ring_size; + QDF_IPA_PIPE_IN_DL_NUM_TX_BUFFERS(pipe_in) = + ipa_res->tx_num_alloc_buffer; +} + +/** + * ol_txrx_ipa_rx_params() - Config IPA RX params + * @ipa_res: IPA resources + * @pipe_in: IPA WDI RX pipe params + * + * Return: None + */ +static inline void ol_txrx_ipa_rx_params( + struct ol_txrx_ipa_resources *ipa_res, + qdf_ipa_wdi_in_params_t *pipe_in) +{ + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!osdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: qdf device is null!", __func__); + return; + } + + QDF_IPA_PIPE_IN_UL_RDY_RING_BASE_PA(pipe_in) = + ipa_res->rx_rdy_ring->mem_info.pa; + QDF_IPA_PIPE_IN_UL_RDY_RING_SIZE(pipe_in) = + ipa_res->rx_rdy_ring->mem_info.size; + QDF_IPA_PIPE_IN_UL_RDY_RING_RP_PA(pipe_in) = + ipa_res->rx_proc_done_idx->mem_info.pa; + OL_TXRX_IPA_WDI2_SET(pipe_in, ipa_res, + osdev); +} + +QDF_STATUS ol_txrx_ipa_setup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + void *ipa_i2w_cb, void *ipa_w2i_cb, + void *ipa_wdi_meter_notifier_cb, + uint32_t ipa_desc_size, void *ipa_priv, + bool is_rm_enabled, uint32_t *p_tx_pipe_handle, + uint32_t *p_rx_pipe_handle) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + qdf_device_t osdev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + struct ol_txrx_ipa_resources *ipa_res; + qdf_ipa_wdi_in_params_t pipe_in; + qdf_ipa_wdi_out_params_t pipe_out; + uint32_t tx_comp_db_dmaaddr = 0, rx_rdy_db_dmaaddr = 0; + int ret; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!osdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: qdf device is null!", __func__); + return QDF_STATUS_E_NOENT; + } + + ipa_res = &pdev->ipa_resource; + qdf_mem_zero(&pipe_in, sizeof(pipe_in)); + qdf_mem_zero(&pipe_out, sizeof(pipe_out)); + + /* TX PIPE */ + QDF_IPA_PIPE_IN_NAT_EN(&pipe_in) = IPA_BYPASS_NAT; + QDF_IPA_PIPE_IN_HDR_LEN(&pipe_in) = OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN; + QDF_IPA_PIPE_IN_HDR_OFST_PKT_SIZE_VALID(&pipe_in) = 1; + QDF_IPA_PIPE_IN_HDR_OFST_PKT_SIZE(&pipe_in) = 0; + QDF_IPA_PIPE_IN_HDR_ADDITIONAL_CONST_LEN(&pipe_in) = + OL_TXRX_IPA_UC_WLAN_8023_HDR_SIZE; + QDF_IPA_PIPE_IN_MODE(&pipe_in) = IPA_BASIC; + QDF_IPA_PIPE_IN_CLIENT(&pipe_in) = IPA_CLIENT_WLAN1_CONS; + QDF_IPA_PIPE_IN_DESC_FIFO_SZ(&pipe_in) = ipa_desc_size; + QDF_IPA_PIPE_IN_PRIV(&pipe_in) = ipa_priv; + QDF_IPA_PIPE_IN_HDR_LITTLE_ENDIAN(&pipe_in) = true; + QDF_IPA_PIPE_IN_NOTIFY(&pipe_in) = ipa_i2w_cb; + + if (!is_rm_enabled) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: IPA RM DISABLED, IPA AWAKE", __func__); + QDF_IPA_PIPE_IN_KEEP_IPA_AWAKE(&pipe_in) = true; + } + + if (qdf_mem_smmu_s1_enabled(osdev)) + ol_txrx_ipa_tx_smmu_params(ipa_res, &pipe_in); + else + ol_txrx_ipa_tx_params(ipa_res, &pipe_in); + + /* Connect WDI IPA PIPE */ + ret = qdf_ipa_connect_wdi_pipe(&pipe_in, &pipe_out); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "ipa_connect_wdi_pipe: Tx pipe setup failed: ret=%d", ret); + return QDF_STATUS_E_FAILURE; + } + + /* Micro Controller Doorbell register */ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s CONS DB pipe out 0x%x TX PIPE Handle 0x%x", __func__, + (unsigned int)QDF_IPA_PIPE_OUT_UC_DOOR_BELL_PA(&pipe_out), + pipe_out.clnt_hdl); + ipa_res->tx_comp_doorbell_dmaaddr = + QDF_IPA_PIPE_OUT_UC_DOOR_BELL_PA(&pipe_out); + /* WLAN TX PIPE Handle */ + ipa_res->tx_pipe_handle = QDF_IPA_PIPE_OUT_CLNT_HDL(&pipe_out); + *p_tx_pipe_handle = QDF_IPA_PIPE_OUT_CLNT_HDL(&pipe_out); + + /* RX PIPE */ + QDF_IPA_PIPE_IN_NAT_EN(&pipe_in) = IPA_BYPASS_NAT; + QDF_IPA_PIPE_IN_HDR_LEN(&pipe_in) = OL_TXRX_IPA_UC_WLAN_RX_HDR_LEN; + QDF_IPA_PIPE_IN_HDR_OFST_METADATA_VALID(&pipe_in) = 0; + QDF_IPA_PIPE_IN_HDR_METADATA_REG_VALID(&pipe_in) = 1; + QDF_IPA_PIPE_IN_MODE(&pipe_in) = IPA_BASIC; + QDF_IPA_PIPE_IN_CLIENT(&pipe_in) = IPA_CLIENT_WLAN1_PROD; + QDF_IPA_PIPE_IN_DESC_FIFO_SZ(&pipe_in) = + ipa_desc_size + SPS_DESC_SIZE; + QDF_IPA_PIPE_IN_NOTIFY(&pipe_in) = ipa_w2i_cb; + if (!is_rm_enabled) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: IPA RM DISABLED, IPA AWAKE", __func__); + QDF_IPA_PIPE_IN_KEEP_IPA_AWAKE(&pipe_in) = true; + } + + if (qdf_mem_smmu_s1_enabled(osdev)) + ol_txrx_ipa_rx_smmu_params(ipa_res, &pipe_in); + else + ol_txrx_ipa_rx_params(ipa_res, &pipe_in); + +#ifdef FEATURE_METERING + QDF_IPA_PIPE_IN_WDI_NOTIFY(&pipe_in) = ipa_wdi_meter_notifier_cb; +#endif + + ret = qdf_ipa_connect_wdi_pipe(&pipe_in, &pipe_out); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "ipa_connect_wdi_pipe: Rx pipe setup failed: ret=%d", ret); + return QDF_STATUS_E_FAILURE; + } + ipa_res->rx_ready_doorbell_dmaaddr = + QDF_IPA_PIPE_OUT_UC_DOOR_BELL_PA(&pipe_out); + ipa_res->rx_pipe_handle = QDF_IPA_PIPE_OUT_CLNT_HDL(&pipe_out); + *p_rx_pipe_handle = QDF_IPA_PIPE_OUT_CLNT_HDL(&pipe_out); + + if (qdf_mem_smmu_s1_enabled(osdev)) { + pld_smmu_map(osdev->dev, ipa_res->tx_comp_doorbell_dmaaddr, + &tx_comp_db_dmaaddr, sizeof(uint32_t)); + ipa_res->tx_comp_doorbell_dmaaddr = tx_comp_db_dmaaddr; + + pld_smmu_map(osdev->dev, ipa_res->rx_ready_doorbell_dmaaddr, + &rx_rdy_db_dmaaddr, sizeof(uint32_t)); + ipa_res->rx_ready_doorbell_dmaaddr = rx_rdy_db_dmaaddr; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_ipa_cleanup() - Disconnect IPA pipes + * @soc_hdl: soc handle + * @pdev_id: pdev id + * @tx_pipe_handle: Tx pipe handle + * @rx_pipe_handle: Rx pipe handle + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_cleanup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint32_t tx_pipe_handle, + uint32_t rx_pipe_handle) +{ + int ret; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Disconnect TX PIPE tx_pipe_handle=0x%x", + __func__, tx_pipe_handle); + ret = qdf_ipa_disconnect_wdi_pipe(tx_pipe_handle); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "ipa_disconnect_wdi_pipe: Tx pipe cleanup failed: ret=%d", + ret); + return QDF_STATUS_E_FAILURE; + } + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Disconnect RX PIPE rx_pipe_handle=0x%x", + __func__, rx_pipe_handle); + ret = qdf_ipa_disconnect_wdi_pipe(rx_pipe_handle); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "ipa_disconnect_wdi_pipe: Rx pipe cleanup failed: ret=%d", + ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_remove_ipa_header() - Remove a specific header from IPA + * @name: Name of the header to be removed + * + * Return: QDF_STATUS + */ +static QDF_STATUS ol_txrx_ipa_remove_header(char *name) +{ + qdf_ipa_ioc_get_hdr_t hdrlookup; + int ret = 0, len; + qdf_ipa_ioc_del_hdr_t *ipa_hdr; + + qdf_mem_zero(&hdrlookup, sizeof(hdrlookup)); + strlcpy(hdrlookup.name, name, sizeof(hdrlookup.name)); + ret = qdf_ipa_get_hdr(&hdrlookup); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "Hdr deleted already %s, %d", name, ret); + return QDF_STATUS_E_FAILURE; + } + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, "hdl: 0x%x", + hdrlookup.hdl); + len = sizeof(qdf_ipa_ioc_del_hdr_t) + sizeof(qdf_ipa_hdr_del_t) * 1; + ipa_hdr = (qdf_ipa_ioc_del_hdr_t *)qdf_mem_malloc(len); + if (!ipa_hdr) + return QDF_STATUS_E_FAILURE; + + QDF_IPA_IOC_DEL_HDR_NUM_HDRS(ipa_hdr) = 1; + QDF_IPA_IOC_DEL_HDR_COMMIT(ipa_hdr) = 0; + QDF_IPA_IOC_DEL_HDR_HDL(ipa_hdr) = QDF_IPA_IOC_GET_HDR_HDL(&hdrlookup); + QDF_IPA_IOC_DEL_HDR_STATUS(ipa_hdr) = -1; + ret = qdf_ipa_del_hdr(ipa_hdr); + if (ret != 0) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Delete header failed: %d", ret); + qdf_mem_free(ipa_hdr); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_free(ipa_hdr); + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_ipa_add_header_info() - Add IPA header for a given interface + * @ifname: Interface name + * @mac_addr: Interface MAC address + * @is_ipv6_enabled: Is IPV6 enabled or not + * + * Return: 0 on success, negativer errno value on error + */ +static int ol_txrx_ipa_add_header_info(char *ifname, uint8_t *mac_addr, + uint8_t session_id, bool is_ipv6_enabled) +{ + qdf_ipa_ioc_add_hdr_t *ipa_hdr = NULL; + int ret = -EINVAL; + struct ol_txrx_ipa_uc_tx_hdr *uc_tx_hdr = NULL; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "Add Partial hdr: %s, "QDF_MAC_ADDR_FMT, ifname, + QDF_MAC_ADDR_REF(mac_addr)); + + /* dynamically allocate the memory to add the hdrs */ + ipa_hdr = qdf_mem_malloc(sizeof(qdf_ipa_ioc_add_hdr_t) + + sizeof(qdf_ipa_hdr_add_t)); + if (!ipa_hdr) { + ret = -ENOMEM; + goto end; + } + + QDF_IPA_IOC_ADD_HDR_COMMIT(ipa_hdr) = 0; + QDF_IPA_IOC_ADD_HDR_NUM_HDRS(ipa_hdr) = 1; + + uc_tx_hdr = (struct ol_txrx_ipa_uc_tx_hdr *) + QDF_IPA_IOC_ADD_HDR_HDR(ipa_hdr); + memcpy(uc_tx_hdr, &ipa_uc_tx_hdr, OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN); + memcpy(uc_tx_hdr->eth.h_source, mac_addr, ETH_ALEN); + uc_tx_hdr->ipa_hd.vdev_id = session_id; + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "ifname=%s, vdev_id=%d", + ifname, uc_tx_hdr->ipa_hd.vdev_id); + snprintf(QDF_IPA_IOC_ADD_HDR_NAME(ipa_hdr), IPA_RESOURCE_NAME_MAX, + "%s%s", ifname, OL_TXRX_IPA_IPV4_NAME_EXT); + QDF_IPA_IOC_ADD_HDR_HDR_LEN(ipa_hdr) = OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN; + QDF_IPA_IOC_ADD_HDR_TYPE(ipa_hdr) = IPA_HDR_L2_ETHERNET_II; + QDF_IPA_IOC_ADD_HDR_IS_PARTIAL(ipa_hdr) = 1; + QDF_IPA_IOC_ADD_HDR_HDR_HDL(ipa_hdr) = 0; + QDF_IPA_IOC_ADD_HDR_IS_ETH2_OFST_VALID(ipa_hdr) = 1; + QDF_IPA_IOC_ADD_HDR_ETH2_OFST(ipa_hdr) = + OL_TXRX_IPA_UC_WLAN_HDR_DES_MAC_OFFSET; + + ret = qdf_ipa_add_hdr(ipa_hdr); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s IPv4 add hdr failed: %d", ifname, ret); + goto end; + } + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: IPv4 hdr_hdl: 0x%x", + QDF_IPA_IOC_ADD_HDR_NAME(ipa_hdr), + QDF_IPA_IOC_ADD_HDR_HDR_HDL(ipa_hdr)); + + if (is_ipv6_enabled) { + snprintf(QDF_IPA_IOC_ADD_HDR_NAME(ipa_hdr), + IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, OL_TXRX_IPA_IPV6_NAME_EXT); + + uc_tx_hdr = (struct ol_txrx_ipa_uc_tx_hdr *) + QDF_IPA_IOC_ADD_HDR_HDR(ipa_hdr); + uc_tx_hdr->eth.h_proto = cpu_to_be16(ETH_P_IPV6); + + ret = qdf_ipa_add_hdr(ipa_hdr); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: IPv6 add hdr failed: %d", ifname, ret); + goto clean_ipv4_hdr; + } + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: IPv6 hdr_hdl: 0x%x", + QDF_IPA_IOC_ADD_HDR_NAME(ipa_hdr), + QDF_IPA_IOC_ADD_HDR_HDR_HDL(ipa_hdr)); + } + + qdf_mem_free(ipa_hdr); + + return ret; + +clean_ipv4_hdr: + snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, OL_TXRX_IPA_IPV4_NAME_EXT); + ol_txrx_ipa_remove_header(ipa_hdr->hdr[0].name); +end: + if (ipa_hdr) + qdf_mem_free(ipa_hdr); + + return ret; +} + +/** + * ol_txrx_ipa_register_interface() - register IPA interface + * @ifname: Interface name + * @prod_client: IPA prod client type + * @cons_client: IPA cons client type + * @session_id: Session ID + * @is_ipv6_enabled: Is IPV6 enabled or not + * + * Return: 0 on success, negative errno on error + */ +static int ol_txrx_ipa_register_interface(char *ifname, + qdf_ipa_client_type_t prod_client, + qdf_ipa_client_type_t cons_client, + uint8_t session_id, + bool is_ipv6_enabled) +{ + qdf_ipa_tx_intf_t tx_intf; + qdf_ipa_rx_intf_t rx_intf; + qdf_ipa_ioc_tx_intf_prop_t *tx_prop = NULL; + qdf_ipa_ioc_rx_intf_prop_t *rx_prop = NULL; + + char ipv4_hdr_name[IPA_RESOURCE_NAME_MAX]; + char ipv6_hdr_name[IPA_RESOURCE_NAME_MAX]; + + int num_prop = 1; + int ret = 0; + + if (is_ipv6_enabled) + num_prop++; + + /* Allocate TX properties for TOS categories, 1 each for IPv4 & IPv6 */ + tx_prop = + qdf_mem_malloc(sizeof(qdf_ipa_ioc_tx_intf_prop_t) * num_prop); + if (!tx_prop) + goto register_interface_fail; + + /* Allocate RX properties, 1 each for IPv4 & IPv6 */ + rx_prop = + qdf_mem_malloc(sizeof(qdf_ipa_ioc_rx_intf_prop_t) * num_prop); + if (!rx_prop) + goto register_interface_fail; + + qdf_mem_zero(&tx_intf, sizeof(tx_intf)); + qdf_mem_zero(&rx_intf, sizeof(rx_intf)); + + snprintf(ipv4_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, OL_TXRX_IPA_IPV4_NAME_EXT); + snprintf(ipv6_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, OL_TXRX_IPA_IPV6_NAME_EXT); + + QDF_IPA_IOC_RX_INTF_PROP_IP(&rx_prop[IPA_IP_v4]) = IPA_IP_v4; + QDF_IPA_IOC_RX_INTF_PROP_SRC_PIPE(&rx_prop[IPA_IP_v4]) = prod_client; + QDF_IPA_IOC_RX_INTF_PROP_HDR_L2_TYPE(&rx_prop[IPA_IP_v4]) = + IPA_HDR_L2_ETHERNET_II; + QDF_IPA_IOC_RX_INTF_PROP_ATTRIB_MASK(&rx_prop[IPA_IP_v4]) = + IPA_FLT_META_DATA; + + /* + * Interface ID is 3rd byte in the CLD header. Add the meta data and + * mask to identify the interface in IPA hardware + */ + QDF_IPA_IOC_RX_INTF_PROP_META_DATA(&rx_prop[IPA_IP_v4]) = + htonl(session_id << 16); + QDF_IPA_IOC_RX_INTF_PROP_META_DATA_MASK(&rx_prop[IPA_IP_v4]) = + htonl(0x00FF0000); + + rx_intf.num_props++; + if (is_ipv6_enabled) { + QDF_IPA_IOC_RX_INTF_PROP_IP(&rx_prop[IPA_IP_v6]) = IPA_IP_v6; + QDF_IPA_IOC_RX_INTF_PROP_SRC_PIPE(&rx_prop[IPA_IP_v6]) = + prod_client; + QDF_IPA_IOC_RX_INTF_PROP_HDR_L2_TYPE(&rx_prop[IPA_IP_v6]) = + IPA_HDR_L2_ETHERNET_II; + QDF_IPA_IOC_RX_INTF_PROP_ATTRIB_MASK(&rx_prop[IPA_IP_v6]) = + IPA_FLT_META_DATA; + QDF_IPA_IOC_RX_INTF_PROP_META_DATA(&rx_prop[IPA_IP_v6]) = + htonl(session_id << 16); + QDF_IPA_IOC_RX_INTF_PROP_META_DATA_MASK(&rx_prop[IPA_IP_v6]) = + htonl(0x00FF0000); + + rx_intf.num_props++; + } + + QDF_IPA_IOC_TX_INTF_PROP_IP(&tx_prop[IPA_IP_v4]) = IPA_IP_v4; + QDF_IPA_IOC_TX_INTF_PROP_HDR_L2_TYPE(&tx_prop[IPA_IP_v4]) = + IPA_HDR_L2_ETHERNET_II; + QDF_IPA_IOC_TX_INTF_PROP_DST_PIPE(&tx_prop[IPA_IP_v4]) = + IPA_CLIENT_WLAN1_CONS; + QDF_IPA_IOC_TX_INTF_PROP_ALT_DST_PIPE(&tx_prop[IPA_IP_v4]) = + cons_client; + strlcpy(QDF_IPA_IOC_TX_INTF_PROP_HDR_NAME(&tx_prop[IPA_IP_v4]), + ipv4_hdr_name, IPA_RESOURCE_NAME_MAX); + tx_intf.num_props++; + + if (is_ipv6_enabled) { + QDF_IPA_IOC_TX_INTF_PROP_IP(&tx_prop[IPA_IP_v6]) = IPA_IP_v6; + QDF_IPA_IOC_TX_INTF_PROP_HDR_L2_TYPE(&tx_prop[IPA_IP_v6]) = + IPA_HDR_L2_ETHERNET_II; + QDF_IPA_IOC_TX_INTF_PROP_DST_PIPE(&tx_prop[IPA_IP_v6]) = + IPA_CLIENT_WLAN1_CONS; + QDF_IPA_IOC_TX_INTF_PROP_ALT_DST_PIPE(&tx_prop[IPA_IP_v6]) = + cons_client; + strlcpy(QDF_IPA_IOC_TX_INTF_PROP_HDR_NAME(&tx_prop[IPA_IP_v6]), + ipv6_hdr_name, IPA_RESOURCE_NAME_MAX); + tx_intf.num_props++; + } + + QDF_IPA_TX_INTF_PROP(&tx_intf) = tx_prop; + QDF_IPA_RX_INTF_PROP(&rx_intf) = rx_prop; + + /* Call the ipa api to register interface */ + ret = qdf_ipa_register_intf(ifname, &tx_intf, &rx_intf); + +register_interface_fail: + qdf_mem_free(tx_prop); + qdf_mem_free(rx_prop); + return ret; +} + +/** + * ol_txrx_ipa_setup_iface() - Setup IPA header and register interface + * @ifname: Interface name + * @mac_addr: Interface MAC address + * @prod_client: IPA prod client type + * @cons_client: IPA cons client type + * @session_id: Session ID + * @is_ipv6_enabled: Is IPV6 enabled or not + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_setup_iface(char *ifname, uint8_t *mac_addr, + qdf_ipa_client_type_t prod_client, + qdf_ipa_client_type_t cons_client, + uint8_t session_id, bool is_ipv6_enabled) +{ + int ret; + + ret = ol_txrx_ipa_add_header_info(ifname, mac_addr, session_id, + is_ipv6_enabled); + if (ret) + return QDF_STATUS_E_FAILURE; + + /* Configure the TX and RX pipes filter rules */ + ret = ol_txrx_ipa_register_interface(ifname, + prod_client, + cons_client, + session_id, is_ipv6_enabled); + if (ret) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_ipa_cleanup_iface() - Cleanup IPA header and deregister interface + * @ifname: Interface name + * @is_ipv6_enabled: Is IPV6 enabled or not + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_cleanup_iface(char *ifname, bool is_ipv6_enabled) +{ + char name_ipa[IPA_RESOURCE_NAME_MAX]; + int ret; + + /* Remove the headers */ + snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, OL_TXRX_IPA_IPV4_NAME_EXT); + ol_txrx_ipa_remove_header(name_ipa); + + if (is_ipv6_enabled) { + snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s", + ifname, OL_TXRX_IPA_IPV6_NAME_EXT); + ol_txrx_ipa_remove_header(name_ipa); + } + /* unregister the interface with IPA */ + ret = qdf_ipa_deregister_intf(ifname); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: ipa_deregister_intf fail: %d", + ifname, ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_enable_pipes(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_txrx_ipa_resources *ipa_res; + int result; + QDF_STATUS status; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + ipa_res = &pdev->ipa_resource; + status = htt_rx_update_smmu_map(pdev->htt_pdev, true); + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "IPA SMMU map failed status:%d", status); + return status; + } + + /* ACTIVATE TX PIPE */ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Enable TX PIPE(tx_pipe_handle=%d)", + __func__, ipa_res->tx_pipe_handle); + result = qdf_ipa_enable_wdi_pipe(ipa_res->tx_pipe_handle); + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Enable TX PIPE fail, code %d", + __func__, result); + goto smmu_unmap; + } + result = qdf_ipa_resume_wdi_pipe(ipa_res->tx_pipe_handle); + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Resume TX PIPE fail, code %d", + __func__, result); + goto smmu_unmap; + } + ol_txrx_ipa_uc_set_active(soc_hdl, pdev_id, true, true); + + /* ACTIVATE RX PIPE */ + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Enable RX PIPE(rx_pipe_handle=%d)", + __func__, ipa_res->rx_pipe_handle); + result = qdf_ipa_enable_wdi_pipe(ipa_res->rx_pipe_handle); + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Enable RX PIPE fail, code %d", + __func__, result); + goto smmu_unmap; + } + result = qdf_ipa_resume_wdi_pipe(ipa_res->rx_pipe_handle); + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Resume RX PIPE fail, code %d", + __func__, result); + goto smmu_unmap; + } + ol_txrx_ipa_uc_set_active(soc_hdl, pdev_id, true, false); + + return QDF_STATUS_SUCCESS; + +smmu_unmap: + if (htt_rx_update_smmu_map(pdev->htt_pdev, false) != + QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "IPA SMMU unmap failed"); + } + + return QDF_STATUS_E_FAILURE; +} + +QDF_STATUS ol_txrx_ipa_disable_pipes(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + struct ol_txrx_ipa_resources *ipa_res; + int result; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + ipa_res = &pdev->ipa_resource; + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Disable RX PIPE", __func__); + result = qdf_ipa_suspend_wdi_pipe(ipa_res->rx_pipe_handle); + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Suspend RX PIPE fail, code %d", + __func__, result); + goto smmu_unmap; + } + + result = qdf_ipa_disable_wdi_pipe(ipa_res->rx_pipe_handle); + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Disable RX PIPE fail, code %d", + __func__, result); + goto smmu_unmap; + } + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: Disable TX PIPE", __func__); + result = qdf_ipa_suspend_wdi_pipe(ipa_res->tx_pipe_handle); + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Suspend TX PIPE fail, code %d", + __func__, result); + goto smmu_unmap; + } + result = qdf_ipa_disable_wdi_pipe(ipa_res->tx_pipe_handle); + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Disable TX PIPE fail, code %d", + __func__, result); + goto smmu_unmap; + } + +smmu_unmap: + if (htt_rx_update_smmu_map(pdev->htt_pdev, false) != + QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "IPA SMMU unmap failed"); + } + + return result ? QDF_STATUS_E_FAILURE : QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_ipa_set_perf_level() - Set IPA clock bandwidth based on data rates + * @client: Client type + * @max_supported_bw_mbps: Maximum bandwidth needed (in Mbps) + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_set_perf_level(int client, + uint32_t max_supported_bw_mbps) +{ + qdf_ipa_rm_resource_name_t resource_name; + qdf_ipa_rm_perf_profile_t profile; + int result; + + if (client == QDF_IPA_CLIENT_WLAN1_PROD) { + resource_name = QDF_IPA_RM_RESOURCE_WLAN_PROD; + } else if (client == QDF_IPA_CLIENT_WLAN1_CONS) { + resource_name = QDF_IPA_RM_RESOURCE_WLAN_CONS; + } else { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "not supported client %d", client); + return QDF_STATUS_E_FAILURE; + } + + QDF_IPA_RM_PERF_PROFILE_MAX_SUPPORTED_BANDWIDTH_MBPS(&profile) = + max_supported_bw_mbps; + result = qdf_ipa_rm_set_perf_profile(resource_name, &profile); + + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Set perf profile failed, code %d", result); + + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#endif /* CONFIG_IPA_WDI_UNIFIED_API */ + +#ifdef FEATURE_METERING +QDF_STATUS ol_txrx_ipa_uc_get_share_stats(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, uint8_t reset_stats) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + int result; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + result = htt_h2t_ipa_uc_get_share_stats(pdev->htt_pdev, reset_stats); + + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Get IPA sharing stats failed, code %d", result); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ol_txrx_ipa_uc_set_quota(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint64_t quota_bytes) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + int result; + + if (!pdev) { + ol_txrx_err("%s invalid instance", __func__); + return QDF_STATUS_E_FAILURE; + } + + result = htt_h2t_ipa_uc_set_quota(pdev->htt_pdev, quota_bytes); + + if (result) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "Set IPA quota failed, code %d", result); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} +#endif +#endif /* IPA_UC_OFFLOAD */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_ipa.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_ipa.h new file mode 100644 index 0000000000000000000000000000000000000000..53872551af18fba654c775e6132ef05d0b59b719 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_ipa.h @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OL_TXRX_IPA_H_ +#define _OL_TXRX_IPA_H_ + +#ifdef IPA_OFFLOAD + +#include /* ol_txrx_vdev_t, etc. */ +#include + +/** + * struct frag_header - fragment header type registered to IPA hardware + * @length: fragment length + * @reserved1: Reserved not used + * @reserved2: Reserved not used + * + */ +#ifdef QCA_WIFI_3_0 +struct frag_header { + uint16_t length; + uint32_t reserved1; + uint32_t reserved2; +} __packed; +#else +struct frag_header { + uint32_t + length:16, + reserved16:16; + uint32_t reserved2; +} __packed; +#endif + +/** + * struct ipa_header - ipa header type registered to IPA hardware + * @vdev_id: vdev id + * @reserved: Reserved not used + * + */ +struct ipa_header { + uint32_t + vdev_id:8, /* vdev_id field is LSB of IPA DESC */ + reserved:24; +} __packed; + +/** + * struct ol_txrx_ipa_uc_tx_hdr - full tx header registered to IPA hardware + * @frag_hd: fragment header + * @ipa_hd: ipa header + * @eth: ether II header + * + */ +struct ol_txrx_ipa_uc_tx_hdr { + struct frag_header frag_hd; + struct ipa_header ipa_hd; + struct ethhdr eth; +} __packed; + +/** + * struct ol_txrx_ipa_uc_rx_hdr - full rx header registered to IPA hardware + * @eth: ether II header + * + */ +struct ol_txrx_ipa_uc_rx_hdr { + struct ethhdr eth; +} __packed; + +#define OL_TXRX_IPA_UC_WLAN_8023_HDR_SIZE 14 + +#define OL_TXRX_IPA_IPV4_NAME_EXT "_ipv4" +#define OL_TXRX_IPA_IPV6_NAME_EXT "_ipv6" + +#define OL_TXRX_IPA_MAX_IFACE MAX_IPA_IFACE + +#define OL_TXRX_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header) +#define OL_TXRX_IPA_WLAN_IPA_HEADER sizeof(struct ipa_header) +#define OL_TXRX_IPA_UC_WLAN_TX_HDR_LEN sizeof(struct ol_txrx_ipa_uc_tx_hdr) +#define OL_TXRX_IPA_UC_WLAN_RX_HDR_LEN sizeof(struct ol_txrx_ipa_uc_rx_hdr) +#define OL_TXRX_IPA_UC_WLAN_HDR_DES_MAC_OFFSET \ + (OL_TXRX_IPA_WLAN_FRAG_HEADER + OL_TXRX_IPA_WLAN_IPA_HEADER) + +#if defined(QCA_WIFI_3_0) && defined(CONFIG_IPA3) +#define OL_TXRX_IPA_WDI2_SET(pipe_in, ipa_res, osdev) \ + do { \ + QDF_IPA_PIPE_IN_UL_RDY_RING_RP_VA(pipe_in) = \ + ipa_res->rx_proc_done_idx->vaddr; \ + QDF_IPA_PIPE_IN_UL_RDY_COMP_RING(pipe_in) = \ + qdf_mem_get_dma_addr(osdev, \ + &ipa_res->rx2_rdy_ring->mem_info);\ + QDF_IPA_PIPE_IN_UL_RDY_COMP_RING_SIZE(pipe_in) = \ + ipa_res->rx2_rdy_ring->mem_info.size; \ + QDF_IPA_PIPE_IN_UL_RDY_COMP_RING_WP_PA(pipe_in) = \ + qdf_mem_get_dma_addr(osdev, \ + &ipa_res->rx2_proc_done_idx->mem_info); \ + QDF_IPA_PIPE_IN_UL_RDY_COMP_RING_WP_VA(pipe_in) = \ + ipa_res->rx2_proc_done_idx->vaddr; \ + } while (0) +#else +/* Do nothing */ +#define OL_TXRX_IPA_WDI2_SET(pipe_in, ipa_res, osdev) +#endif /* IPA3 */ + +/** + * ol_txrx_ipa_uc_get_resource() - Client request resource information + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * + * OL client will request IPA UC related resource information + * Resource information will be distributted to IPA module + * All of the required resources should be pre-allocated + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_uc_get_resource(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id); + +/** + * ol_txrx_ipa_uc_set_doorbell_paddr() - Client set IPA UC doorbell register + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * + * IPA UC let know doorbell register physical address + * WLAN firmware will use this physical address to notify IPA UC + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_uc_set_doorbell_paddr(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id); + +/** + * ol_txrx_ipa_uc_set_active() - Client notify IPA UC data path active or not + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * @uc_active: WDI UC path enable or not + * @is_tx: TX path or RX path + * + * IPA UC let know doorbell register physical address + * WLAN firmware will use this physical address to notify IPA UC + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_uc_set_active(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + bool uc_active, bool is_tx); + +/** + * ol_txrx_ipa_uc_op_response() - Handle OP command response from firmware + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * @op_msg: op response message from firmware + * + * Return: none + */ +QDF_STATUS ol_txrx_ipa_uc_op_response(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, uint8_t *op_msg); + +/** + * ol_txrx_ipa_uc_register_op_cb() - Register OP handler function + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * @op_cb: handler function pointer + * + * Return: none + */ +QDF_STATUS ol_txrx_ipa_uc_register_op_cb(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, + ipa_uc_op_cb_type op_cb, + void *usr_ctxt); + +/** + * ol_txrx_ipa_uc_get_stat() - Get firmware wdi status + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * + * Return: none + */ +QDF_STATUS ol_txrx_ipa_uc_get_stat(struct cdp_soc_t *soc_hdl, uint8_t pdev_id); + +/** + * ol_txrx_ipa_enable_autonomy() - Enable autonomy RX path + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * + * Set all RX packet route to IPA + * Return: none + */ +QDF_STATUS ol_txrx_ipa_enable_autonomy(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id); + +/** + * ol_txrx_ipa_disable_autonomy() - Disable autonomy RX path + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * + * Disable RX packet route to host + * Return: none + */ +QDF_STATUS ol_txrx_ipa_disable_autonomy(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id); + +#ifdef CONFIG_IPA_WDI_UNIFIED_API +/** + * ol_txrx_ipa_setup() - Setup and connect IPA pipes + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * @ipa_i2w_cb: IPA to WLAN callback + * @ipa_w2i_cb: WLAN to IPA callback + * @ipa_wdi_meter_notifier_cb: IPA WDI metering callback + * @ipa_desc_size: IPA descriptor size + * @ipa_priv: handle to the HTT instance + * @is_rm_enabled: Is IPA RM enabled or not + * @p_tx_pipe_handle: pointer to Tx pipe handle + * @p_rx_pipe_handle: pointer to Rx pipe handle + * @is_smmu_enabled: Is SMMU enabled or not + * @sys_in: parameters to setup sys pipe in mcc mode + * @over_gsi: is ipa ver gsi fw + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_setup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + void *ipa_i2w_cb, void *ipa_w2i_cb, + void *ipa_wdi_meter_notifier_cb, + uint32_t ipa_desc_size, + void *ipa_priv, bool is_rm_enabled, + uint32_t *tx_pipe_handle, uint32_t *rx_pipe_handle, + bool is_smmu_enabled, + qdf_ipa_sys_connect_params_t *sys_in, + bool over_gsi); +#else /* CONFIG_IPA_WDI_UNIFIED_API */ +/** + * ol_txrx_ipa_setup() - Setup and connect IPA pipes + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * @ipa_i2w_cb: IPA to WLAN callback + * @ipa_w2i_cb: WLAN to IPA callback + * @ipa_wdi_meter_notifier_cb: IPA WDI metering callback + * @ipa_desc_size: IPA descriptor size + * @ipa_priv: handle to the HTT instance + * @is_rm_enabled: Is IPA RM enabled or not + * @p_tx_pipe_handle: pointer to Tx pipe handle + * @p_rx_pipe_handle: pointer to Rx pipe handle + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_setup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + void *ipa_i2w_cb, void *ipa_w2i_cb, + void *ipa_wdi_meter_notifier_cb, + uint32_t ipa_desc_size, void *ipa_priv, + bool is_rm_enabled, uint32_t *tx_pipe_handle, + uint32_t *rx_pipe_handle); +#endif /* CONFIG_IPA_WDI_UNIFIED_API */ +QDF_STATUS ol_txrx_ipa_cleanup(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint32_t tx_pipe_handle, + uint32_t rx_pipe_handle); +QDF_STATUS ol_txrx_ipa_setup_iface(char *ifname, uint8_t *mac_addr, + qdf_ipa_client_type_t prod_client, + qdf_ipa_client_type_t cons_client, + uint8_t session_id, bool is_ipv6_enabled); +QDF_STATUS ol_txrx_ipa_cleanup_iface(char *ifname, bool is_ipv6_enabled); + +/** + * ol_txrx_ipa_enable_pipes() - Enable and resume traffic on Tx/Rx pipes + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_enable_pipes(struct cdp_soc_t *soc_hdl, uint8_t pdev_id); + +/** + * ol_txrx_ipa_disable_pipes() – Suspend traffic and disable Tx/Rx pipes + * @soc_hdl: data path soc handle + * @pdev_id: device instance id + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_disable_pipes(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id); +QDF_STATUS ol_txrx_ipa_set_perf_level(int client, + uint32_t max_supported_bw_mbps); +#ifdef FEATURE_METERING +/** + * ol_txrx_ipa_uc_get_share_stats() - get Tx/Rx byte stats from FW + * @soc_hdl: data path soc handle + * @pdev_id: physical device instance id + * @value: reset stats + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_uc_get_share_stats(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, uint8_t reset_stats); +/** + * ol_txrx_ipa_uc_set_quota() - set quota limit to FW + * @soc_hdl: data path soc handle + * @pdev_id: physical device instance number + * @value: quota limit bytes + * + * Return: QDF_STATUS + */ +QDF_STATUS ol_txrx_ipa_uc_set_quota(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + uint64_t quota_bytes); +#endif +#endif +#endif /* _OL_TXRX_IPA_H_*/ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_legacy_flow_control.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_legacy_flow_control.c new file mode 100644 index 0000000000000000000000000000000000000000..cad4dee01da4b9fc0af7b005ecea64ff44324562 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_legacy_flow_control.c @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OS abstraction libraries */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_atomic_read, etc. */ +#include /* qdf_unlikely */ + +/* APIs for other modules */ +#include /* HTT_TX_EXT_TID_MGMT */ +#include /* htt_tx_desc_tid */ + +/* internal header files relevant for all systems */ +#include /* TXRX_ASSERT1 */ +#include /* ol_tx_desc */ +#include /* ol_tx_send */ +#include /* ol_txrx_get_vdev_from_vdev_id */ + +/* internal header files relevant only for HL systems */ +#include /* ol_tx_enqueue */ + +/* internal header files relevant only for specific systems (Pronto) */ +#include /* OL_TX_ENCAP, etc */ +#include +#include +#include + +void ol_txrx_vdev_pause(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint32_t reason, uint32_t pause_type) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is NULL"); + return; + } + + /* TO DO: log the queue pause */ + /* acquire the mutex lock, since we'll be modifying the queues */ + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + + qdf_spin_lock_bh(&vdev->ll_pause.mutex); + vdev->ll_pause.paused_reason |= reason; + vdev->ll_pause.q_pause_cnt++; + vdev->ll_pause.is_q_paused = true; + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +void ol_txrx_vdev_unpause(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + uint32_t reason, uint32_t pause_type) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is NULL"); + return; + } + + /* TO DO: log the queue unpause */ + /* acquire the mutex lock, since we'll be modifying the queues */ + TX_SCHED_DEBUG_PRINT("Enter %s\n", __func__); + + qdf_spin_lock_bh(&vdev->ll_pause.mutex); + if (vdev->ll_pause.paused_reason & reason) { + vdev->ll_pause.paused_reason &= ~reason; + if (!vdev->ll_pause.paused_reason) { + vdev->ll_pause.is_q_paused = false; + vdev->ll_pause.q_unpause_cnt++; + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + ol_tx_vdev_ll_pause_queue_send((void *)vdev); + } else { + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + } + } else { + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + } + TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__); +} + +void ol_txrx_vdev_flush(struct cdp_soc_t *soc_hdl, uint8_t vdev_id) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is NULL"); + return; + } + + qdf_spin_lock_bh(&vdev->ll_pause.mutex); + qdf_timer_stop(&vdev->ll_pause.timer); + vdev->ll_pause.is_q_timer_on = false; + while (vdev->ll_pause.txq.head) { + qdf_nbuf_t next = + qdf_nbuf_next(vdev->ll_pause.txq.head); + qdf_nbuf_set_next(vdev->ll_pause.txq.head, NULL); + if (QDF_NBUF_CB_PADDR(vdev->ll_pause.txq.head)) { + if (!qdf_nbuf_ipa_owned_get(vdev->ll_pause.txq.head)) + qdf_nbuf_unmap(vdev->pdev->osdev, + vdev->ll_pause.txq.head, + QDF_DMA_TO_DEVICE); + } + qdf_nbuf_tx_free(vdev->ll_pause.txq.head, + QDF_NBUF_PKT_ERROR); + vdev->ll_pause.txq.head = next; + } + vdev->ll_pause.txq.tail = NULL; + vdev->ll_pause.txq.depth = 0; + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); +} + +#define OL_TX_VDEV_PAUSE_QUEUE_SEND_MARGIN 400 +#define OL_TX_VDEV_PAUSE_QUEUE_SEND_PERIOD_MS 5 + +static void ol_tx_vdev_ll_pause_queue_send_base(struct ol_txrx_vdev_t *vdev) +{ + int max_to_accept; + + if (!vdev) + return; + + qdf_spin_lock_bh(&vdev->ll_pause.mutex); + if (vdev->ll_pause.paused_reason) { + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + return; + } + + /* + * Send as much of the backlog as possible, but leave some margin + * of unallocated tx descriptors that can be used for new frames + * being transmitted by other vdevs. + * Ideally there would be a scheduler, which would not only leave + * some margin for new frames for other vdevs, but also would + * fairly apportion the tx descriptors between multiple vdevs that + * have backlogs in their pause queues. + * However, the fairness benefit of having a scheduler for frames + * from multiple vdev's pause queues is not sufficient to outweigh + * the extra complexity. + */ + max_to_accept = vdev->pdev->tx_desc.num_free - + OL_TX_VDEV_PAUSE_QUEUE_SEND_MARGIN; + while (max_to_accept > 0 && vdev->ll_pause.txq.depth) { + qdf_nbuf_t tx_msdu; + + max_to_accept--; + vdev->ll_pause.txq.depth--; + tx_msdu = vdev->ll_pause.txq.head; + if (tx_msdu) { + vdev->ll_pause.txq.head = qdf_nbuf_next(tx_msdu); + if (!vdev->ll_pause.txq.head) + vdev->ll_pause.txq.tail = NULL; + qdf_nbuf_set_next(tx_msdu, NULL); + QDF_NBUF_UPDATE_TX_PKT_COUNT(tx_msdu, + QDF_NBUF_TX_PKT_TXRX_DEQUEUE); + tx_msdu = ol_tx_ll_wrapper(vdev, tx_msdu); + /* + * It is unexpected that ol_tx_ll would reject the frame + * since we checked that there's room for it, though + * there's an infinitesimal possibility that between the + * time we checked the room available and now, a + * concurrent batch of tx frames used up all the room. + * For simplicity, just drop the frame. + */ + if (tx_msdu) { + qdf_nbuf_unmap(vdev->pdev->osdev, tx_msdu, + QDF_DMA_TO_DEVICE); + qdf_nbuf_tx_free(tx_msdu, QDF_NBUF_PKT_ERROR); + } + } + } + if (vdev->ll_pause.txq.depth) { + qdf_timer_stop(&vdev->ll_pause.timer); + if (!qdf_atomic_read(&vdev->delete.detaching)) { + qdf_timer_start(&vdev->ll_pause.timer, + OL_TX_VDEV_PAUSE_QUEUE_SEND_PERIOD_MS); + vdev->ll_pause.is_q_timer_on = true; + } + if (vdev->ll_pause.txq.depth >= vdev->ll_pause.max_q_depth) + vdev->ll_pause.q_overflow_cnt++; + } + + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); +} + +static qdf_nbuf_t +ol_tx_vdev_pause_queue_append(struct ol_txrx_vdev_t *vdev, + qdf_nbuf_t msdu_list, uint8_t start_timer) +{ + qdf_spin_lock_bh(&vdev->ll_pause.mutex); + while (msdu_list && + vdev->ll_pause.txq.depth < vdev->ll_pause.max_q_depth) { + qdf_nbuf_t next = qdf_nbuf_next(msdu_list); + + QDF_NBUF_UPDATE_TX_PKT_COUNT(msdu_list, + QDF_NBUF_TX_PKT_TXRX_ENQUEUE); + DPTRACE(qdf_dp_trace(msdu_list, + QDF_DP_TRACE_TXRX_QUEUE_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(msdu_list), + sizeof(qdf_nbuf_data(msdu_list)), QDF_TX)); + + vdev->ll_pause.txq.depth++; + if (!vdev->ll_pause.txq.head) { + vdev->ll_pause.txq.head = msdu_list; + vdev->ll_pause.txq.tail = msdu_list; + } else { + qdf_nbuf_set_next(vdev->ll_pause.txq.tail, msdu_list); + } + vdev->ll_pause.txq.tail = msdu_list; + + msdu_list = next; + } + if (vdev->ll_pause.txq.tail) + qdf_nbuf_set_next(vdev->ll_pause.txq.tail, NULL); + + if (start_timer) { + qdf_timer_stop(&vdev->ll_pause.timer); + if (!qdf_atomic_read(&vdev->delete.detaching)) { + qdf_timer_start(&vdev->ll_pause.timer, + OL_TX_VDEV_PAUSE_QUEUE_SEND_PERIOD_MS); + vdev->ll_pause.is_q_timer_on = true; + } + } + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + + return msdu_list; +} + +/* + * Store up the tx frame in the vdev's tx queue if the vdev is paused. + * If there are too many frames in the tx queue, reject it. + */ +qdf_nbuf_t ol_tx_ll_queue(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) +{ + uint16_t eth_type; + uint32_t paused_reason; + + if (!msdu_list) + return NULL; + + paused_reason = vdev->ll_pause.paused_reason; + if (paused_reason) { + if (qdf_unlikely((paused_reason & + OL_TXQ_PAUSE_REASON_PEER_UNAUTHORIZED) == + paused_reason)) { + eth_type = (((struct ethernet_hdr_t *) + qdf_nbuf_data(msdu_list))-> + ethertype[0] << 8) | + (((struct ethernet_hdr_t *) + qdf_nbuf_data(msdu_list))->ethertype[1]); + if (ETHERTYPE_IS_EAPOL_WAPI(eth_type)) { + msdu_list = ol_tx_ll_wrapper(vdev, msdu_list); + return msdu_list; + } + } + msdu_list = ol_tx_vdev_pause_queue_append(vdev, msdu_list, 1); + } else { + if (vdev->ll_pause.txq.depth > 0 || + vdev->pdev->tx_throttle.current_throttle_level != + THROTTLE_LEVEL_0) { + /* + * not paused, but there is a backlog of frms + * from a prior pause or throttle off phase + */ + msdu_list = ol_tx_vdev_pause_queue_append( + vdev, msdu_list, 0); + /* + * if throttle is disabled or phase is "on", + * send the frame + */ + if (vdev->pdev->tx_throttle.current_throttle_level == + THROTTLE_LEVEL_0 || + vdev->pdev->tx_throttle.current_throttle_phase == + THROTTLE_PHASE_ON) { + /* + * send as many frames as possible + * from the vdevs backlog + */ + ol_tx_vdev_ll_pause_queue_send_base(vdev); + } + } else { + /* + * not paused, no throttle and no backlog - + * send the new frames + */ + msdu_list = ol_tx_ll_wrapper(vdev, msdu_list); + } + } + return msdu_list; +} + +/* + * Run through the transmit queues for all the vdevs and + * send the pending frames + */ +void ol_tx_pdev_ll_pause_queue_send_all(struct ol_txrx_pdev_t *pdev) +{ + int max_to_send; /* tracks how many frames have been sent */ + qdf_nbuf_t tx_msdu; + struct ol_txrx_vdev_t *vdev = NULL; + uint8_t more; + + if (!pdev) + return; + + if (pdev->tx_throttle.current_throttle_phase == THROTTLE_PHASE_OFF) + return; + + /* ensure that we send no more than tx_threshold frames at once */ + max_to_send = pdev->tx_throttle.tx_threshold; + + /* round robin through the vdev queues for the given pdev */ + + /* + * Potential improvement: download several frames from the same vdev + * at a time, since it is more likely that those frames could be + * aggregated together, remember which vdev was serviced last, + * so the next call this function can resume the round-robin + * traversing where the current invocation left off + */ + do { + more = 0; + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + qdf_spin_lock_bh(&vdev->ll_pause.mutex); + if (vdev->ll_pause.txq.depth) { + if (vdev->ll_pause.paused_reason) { + qdf_spin_unlock_bh(&vdev->ll_pause. + mutex); + continue; + } + + tx_msdu = vdev->ll_pause.txq.head; + if (!tx_msdu) { + qdf_spin_unlock_bh(&vdev->ll_pause. + mutex); + continue; + } + + max_to_send--; + vdev->ll_pause.txq.depth--; + + vdev->ll_pause.txq.head = + qdf_nbuf_next(tx_msdu); + + if (!vdev->ll_pause.txq.head) + vdev->ll_pause.txq.tail = NULL; + + qdf_nbuf_set_next(tx_msdu, NULL); + tx_msdu = ol_tx_ll_wrapper(vdev, tx_msdu); + /* + * It is unexpected that ol_tx_ll would reject + * the frame, since we checked that there's + * room for it, though there's an infinitesimal + * possibility that between the time we checked + * the room available and now, a concurrent + * batch of tx frames used up all the room. + * For simplicity, just drop the frame. + */ + if (tx_msdu) { + qdf_nbuf_unmap(pdev->osdev, tx_msdu, + QDF_DMA_TO_DEVICE); + qdf_nbuf_tx_free(tx_msdu, + QDF_NBUF_PKT_ERROR); + } + } + /*check if there are more msdus to transmit */ + if (vdev->ll_pause.txq.depth) + more = 1; + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + } + } while (more && max_to_send); + + vdev = NULL; + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + qdf_spin_lock_bh(&vdev->ll_pause.mutex); + if (vdev->ll_pause.txq.depth) { + qdf_timer_stop(&pdev->tx_throttle.tx_timer); + qdf_timer_start( + &pdev->tx_throttle.tx_timer, + OL_TX_VDEV_PAUSE_QUEUE_SEND_PERIOD_MS); + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + return; + } + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + } +} + +void ol_tx_vdev_ll_pause_queue_send(void *context) +{ + struct ol_txrx_vdev_t *vdev = (struct ol_txrx_vdev_t *)context; + struct ol_txrx_pdev_t *pdev = vdev->pdev; + + if (pdev && + pdev->tx_throttle.current_throttle_level != THROTTLE_LEVEL_0 && + pdev->tx_throttle.current_throttle_phase == THROTTLE_PHASE_OFF) + return; + ol_tx_vdev_ll_pause_queue_send_base(vdev); +} + +int ol_txrx_register_tx_flow_control(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id, + ol_txrx_tx_flow_control_fp flowControl, + void *osif_fc_ctx, + ol_txrx_tx_flow_control_is_pause_fp + flow_control_is_pause) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid vdev_id %d", __func__, vdev_id); + return -EINVAL; + } + + qdf_spin_lock_bh(&vdev->flow_control_lock); + vdev->osif_flow_control_cb = flowControl; + vdev->osif_flow_control_is_pause = flow_control_is_pause; + vdev->osif_fc_ctx = osif_fc_ctx; + qdf_spin_unlock_bh(&vdev->flow_control_lock); + return 0; +} + +int ol_txrx_deregister_tx_flow_control_cb(struct cdp_soc_t *soc_hdl, + uint8_t vdev_id) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid vdev_id", __func__); + return -EINVAL; + } + + qdf_spin_lock_bh(&vdev->flow_control_lock); + vdev->osif_flow_control_cb = NULL; + vdev->osif_flow_control_is_pause = NULL; + vdev->osif_fc_ctx = NULL; + qdf_spin_unlock_bh(&vdev->flow_control_lock); + return 0; +} + +/** + * ol_txrx_get_tx_resource() - if tx resource less than low_watermark + * soc_hdl: soc handle + * @pdev_id: datapath pdev identifier + * @peer_addr: peer mac address + * @low_watermark: low watermark + * @high_watermark_offset: high watermark offset value + * + * Return: true/false + */ +bool +ol_txrx_get_tx_resource(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + struct qdf_mac_addr peer_addr, + unsigned int low_watermark, + unsigned int high_watermark_offset) +{ + struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl); + ol_txrx_pdev_handle pdev = + ol_txrx_get_pdev_from_pdev_id(soc, pdev_id); + ol_txrx_vdev_handle vdev; + + if (qdf_unlikely(!pdev)) { + ol_txrx_err("pdev is NULL"); + return true; + } + + vdev = ol_txrx_get_vdev_by_peer_addr(ol_txrx_pdev_t_to_cdp_pdev(pdev), + peer_addr); + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Invalid peer address: " QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(peer_addr.bytes)); + /* Return true so caller do not understand that resource + * is less than low_watermark. + * sta_id validation will be done in ol_tx_send_data_frame + * and if sta_id is not registered then host will drop + * packet. + */ + return true; + } + + qdf_spin_lock_bh(&vdev->pdev->tx_mutex); + + if (vdev->pdev->tx_desc.num_free < (uint16_t)low_watermark) { + vdev->tx_fl_lwm = (uint16_t)low_watermark; + vdev->tx_fl_hwm = + (uint16_t)(low_watermark + high_watermark_offset); + /* Not enough free resource, stop TX OS Q */ + qdf_atomic_set(&vdev->os_q_paused, 1); + qdf_spin_unlock_bh(&vdev->pdev->tx_mutex); + return false; + } + qdf_spin_unlock_bh(&vdev->pdev->tx_mutex); + return true; +} + +int ol_txrx_ll_set_tx_pause_q_depth(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + int pause_q_depth) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid vdev_id %d", __func__, vdev_id); + return -EINVAL; + } + + qdf_spin_lock_bh(&vdev->ll_pause.mutex); + vdev->ll_pause.max_q_depth = pause_q_depth; + qdf_spin_unlock_bh(&vdev->ll_pause.mutex); + + return 0; +} + +void ol_txrx_flow_control_cb(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, + bool tx_resume) +{ + struct ol_txrx_vdev_t *vdev = + (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); + + if (qdf_unlikely(!vdev)) { + ol_txrx_err("vdev is NULL"); + return; + } + + qdf_spin_lock_bh(&vdev->flow_control_lock); + if ((vdev->osif_flow_control_cb) && (vdev->osif_fc_ctx)) + vdev->osif_flow_control_cb(vdev->osif_fc_ctx, tx_resume); + qdf_spin_unlock_bh(&vdev->flow_control_lock); +} + +/** + * ol_txrx_flow_control_is_pause() - is osif paused by flow control + * @vdev: vdev handle + * + * Return: true if osif is paused by flow control + */ +static bool ol_txrx_flow_control_is_pause(ol_txrx_vdev_handle vdev) +{ + bool is_pause = false; + + if ((vdev->osif_flow_control_is_pause) && (vdev->osif_fc_ctx)) + is_pause = vdev->osif_flow_control_is_pause(vdev->osif_fc_ctx); + + return is_pause; +} + +/** + * ol_tx_flow_ct_unpause_os_q() - Unpause OS Q + * @pdev: physical device object + * + * + * Return: None + */ +void ol_tx_flow_ct_unpause_os_q(ol_txrx_pdev_handle pdev) +{ + struct ol_txrx_vdev_t *vdev; + struct cdp_soc_t *soc_hdl = ol_txrx_soc_t_to_cdp_soc_t(pdev->soc); + + TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { + if ((qdf_atomic_read(&vdev->os_q_paused) && + (vdev->tx_fl_hwm != 0)) || + ol_txrx_flow_control_is_pause(vdev)) { + qdf_spin_lock(&pdev->tx_mutex); + if (pdev->tx_desc.num_free > vdev->tx_fl_hwm) { + qdf_atomic_set(&vdev->os_q_paused, 0); + qdf_spin_unlock(&pdev->tx_mutex); + ol_txrx_flow_control_cb(soc_hdl, + vdev->vdev_id, true); + } else { + qdf_spin_unlock(&pdev->tx_mutex); + } + } + } +} + diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_peer_find.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_peer_find.c new file mode 100644 index 0000000000000000000000000000000000000000..1f10c5b32b0d8cca6530c34a8538b1ccf0c46828 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_peer_find.c @@ -0,0 +1,843 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*=== includes ===*/ +/* header files for OS primitives */ +#include /* uint32_t, etc. */ +#include /* qdf_mem_malloc, etc. */ +#include /* qdf_device_t, qdf_print */ +/* header files for utilities */ +#include "queue.h" /* TAILQ */ + +/* header files for configuration API */ +#include /* ol_cfg_max_peer_id */ + +/* header files for our internal definitions */ +#include /* ol_txrx_pdev_t, etc. */ +#include /* TXRX_DEBUG_LEVEL */ +#include /* ol_txrx_pdev_t, etc. */ +#include /* ol_txrx_peer_release_ref */ +#include /* ol_txrx_peer_find_attach, etc. */ +#include +#include "wlan_roam_debug.h" + +/*=== misc. / utility function definitions ==================================*/ + +static int ol_txrx_log2_ceil(unsigned int value) +{ + /* need to switch to unsigned math so that negative values + * will right-shift towards 0 instead of -1 + */ + unsigned int tmp = value; + int log2 = -1; + + if (value == 0) { + TXRX_ASSERT2(0); + return 0; + } + + while (tmp) { + log2++; + tmp >>= 1; + } + if (1U << log2 != value) + log2++; + + return log2; +} + +int ol_txrx_peer_get_ref(struct ol_txrx_peer_t *peer, + enum peer_debug_id_type dbg_id) +{ + int refs_dbg_id; + + if (!peer) { + ol_txrx_err("peer is null for ID %d", dbg_id); + return -EINVAL; + } + + if (dbg_id >= PEER_DEBUG_ID_MAX || dbg_id < 0) { + ol_txrx_err("incorrect debug_id %d ", dbg_id); + return -EINVAL; + } + + qdf_atomic_inc(&peer->ref_cnt); + qdf_atomic_inc(&peer->access_list[dbg_id]); + refs_dbg_id = qdf_atomic_read(&peer->access_list[dbg_id]); + + return refs_dbg_id; +} + +/*=== function definitions for peer MAC addr --> peer object hash table =====*/ + +/* + * TXRX_PEER_HASH_LOAD_FACTOR: + * Multiply by 2 and divide by 2^0 (shift by 0), then round up to a + * power of two. + * This provides at least twice as many bins in the peer hash table + * as there will be entries. + * Having substantially more bins than spaces minimizes the probability of + * having to compare MAC addresses. + * Because the MAC address comparison is fairly efficient, it is okay if the + * hash table is sparsely loaded, but it's generally better to use extra mem + * to keep the table sparse, to keep the lookups as fast as possible. + * An optimization would be to apply a more conservative loading factor for + * high latency, where the lookup happens during the tx classification of + * every tx frame, than for low-latency, where the lookup only happens + * during association, when the PEER_MAP message is received. + */ +#define TXRX_PEER_HASH_LOAD_MULT 2 +#define TXRX_PEER_HASH_LOAD_SHIFT 0 + +static int ol_txrx_peer_find_hash_attach(struct ol_txrx_pdev_t *pdev) +{ + int i, hash_elems, log2; + + /* allocate the peer MAC address -> peer object hash table */ + hash_elems = ol_cfg_max_peer_id(pdev->ctrl_pdev) + 1; + hash_elems *= TXRX_PEER_HASH_LOAD_MULT; + hash_elems >>= TXRX_PEER_HASH_LOAD_SHIFT; + log2 = ol_txrx_log2_ceil(hash_elems); + hash_elems = 1 << log2; + + pdev->peer_hash.mask = hash_elems - 1; + pdev->peer_hash.idx_bits = log2; + /* allocate an array of TAILQ peer object lists */ + pdev->peer_hash.bins = + qdf_mem_malloc(hash_elems * + sizeof(TAILQ_HEAD(anonymous_tail_q, + ol_txrx_peer_t))); + if (!pdev->peer_hash.bins) + return 1; /* failure */ + + for (i = 0; i < hash_elems; i++) + TAILQ_INIT(&pdev->peer_hash.bins[i]); + + return 0; /* success */ +} + +static void ol_txrx_peer_find_hash_detach(struct ol_txrx_pdev_t *pdev) +{ + qdf_mem_free(pdev->peer_hash.bins); +} + +static inline unsigned int +ol_txrx_peer_find_hash_index(struct ol_txrx_pdev_t *pdev, + union ol_txrx_align_mac_addr_t *mac_addr) +{ + unsigned int index; + + index = + mac_addr->align2.bytes_ab ^ + mac_addr->align2.bytes_cd ^ mac_addr->align2.bytes_ef; + index ^= index >> pdev->peer_hash.idx_bits; + index &= pdev->peer_hash.mask; + return index; +} + +void +ol_txrx_peer_find_hash_add(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + unsigned int index; + + index = ol_txrx_peer_find_hash_index(pdev, &peer->mac_addr); + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + /* + * It is important to add the new peer at the tail of the peer list + * with the bin index. Together with having the hash_find function + * search from head to tail, this ensures that if two entries with + * the same MAC address are stored, the one added first will be + * found first. + */ + TAILQ_INSERT_TAIL(&pdev->peer_hash.bins[index], peer, hash_list_elem); + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); +} + +struct ol_txrx_peer_t *ol_txrx_peer_vdev_find_hash(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + uint8_t *peer_mac_addr, + int mac_addr_is_aligned, + uint8_t check_valid) +{ + union ol_txrx_align_mac_addr_t local_mac_addr_aligned, *mac_addr; + unsigned int index; + struct ol_txrx_peer_t *peer; + + if (mac_addr_is_aligned) { + mac_addr = (union ol_txrx_align_mac_addr_t *)peer_mac_addr; + } else { + qdf_mem_copy(&local_mac_addr_aligned.raw[0], + peer_mac_addr, QDF_MAC_ADDR_SIZE); + mac_addr = &local_mac_addr_aligned; + } + index = ol_txrx_peer_find_hash_index(pdev, mac_addr); + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + TAILQ_FOREACH(peer, &pdev->peer_hash.bins[index], hash_list_elem) { + if (ol_txrx_peer_find_mac_addr_cmp(mac_addr, &peer->mac_addr) == + 0 && (check_valid == 0 || peer->valid) + && peer->vdev == vdev) { + /* found it */ + ol_txrx_peer_get_ref(peer, PEER_DEBUG_ID_OL_INTERNAL); + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + return peer; + } + } + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + return NULL; /* failure */ +} + +struct ol_txrx_peer_t * + ol_txrx_peer_find_hash_find_get_ref + (struct ol_txrx_pdev_t *pdev, + uint8_t *peer_mac_addr, + int mac_addr_is_aligned, + u8 check_valid, + enum peer_debug_id_type dbg_id) +{ + union ol_txrx_align_mac_addr_t local_mac_addr_aligned, *mac_addr; + unsigned int index; + struct ol_txrx_peer_t *peer; + + if (mac_addr_is_aligned) { + mac_addr = (union ol_txrx_align_mac_addr_t *)peer_mac_addr; + } else { + qdf_mem_copy(&local_mac_addr_aligned.raw[0], + peer_mac_addr, QDF_MAC_ADDR_SIZE); + mac_addr = &local_mac_addr_aligned; + } + index = ol_txrx_peer_find_hash_index(pdev, mac_addr); + qdf_spin_lock_bh(&pdev->peer_ref_mutex); + TAILQ_FOREACH(peer, &pdev->peer_hash.bins[index], hash_list_elem) { + if (ol_txrx_peer_find_mac_addr_cmp(mac_addr, &peer->mac_addr) == + 0 && (check_valid == 0 || peer->valid)) { + /* found it */ + ol_txrx_peer_get_ref(peer, dbg_id); + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + return peer; + } + } + qdf_spin_unlock_bh(&pdev->peer_ref_mutex); + return NULL; /* failure */ +} + +void +ol_txrx_peer_find_hash_remove(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer) +{ + unsigned int index; + + index = ol_txrx_peer_find_hash_index(pdev, &peer->mac_addr); + /* + * DO NOT take the peer_ref_mutex lock here - it needs to be taken + * by the caller. + * The caller needs to hold the lock from the time the peer object's + * reference count is decremented and tested up through the time the + * reference to the peer object is removed from the hash table, by + * this function. + * Holding the lock only while removing the peer object reference + * from the hash table keeps the hash table consistent, but does not + * protect against a new HL tx context starting to use the peer object + * if it looks up the peer object from its MAC address just after the + * peer ref count is decremented to zero, but just before the peer + * object reference is removed from the hash table. + */ + /* qdf_spin_lock_bh(&pdev->peer_ref_mutex); */ + TAILQ_REMOVE(&pdev->peer_hash.bins[index], peer, hash_list_elem); + /* qdf_spin_unlock_bh(&pdev->peer_ref_mutex); */ +} + +void ol_txrx_peer_find_hash_erase(struct ol_txrx_pdev_t *pdev) +{ + unsigned int i; + /* + * Not really necessary to take peer_ref_mutex lock - by this point, + * it's known that the pdev is no longer in use. + */ + + for (i = 0; i <= pdev->peer_hash.mask; i++) { + if (!TAILQ_EMPTY(&pdev->peer_hash.bins[i])) { + struct ol_txrx_peer_t *peer, *peer_next; + + /* + * TAILQ_FOREACH_SAFE must be used here to avoid any + * memory access violation after peer is freed + */ + TAILQ_FOREACH_SAFE(peer, &pdev->peer_hash.bins[i], + hash_list_elem, peer_next) { + /* + * Don't remove the peer from the hash table - + * that would modify the list we are currently + * traversing, + * and it's not necessary anyway. + */ + /* + * Artificially adjust the peer's ref count to + * 1, so it will get deleted by + * ol_txrx_peer_release_ref. + */ + qdf_atomic_init(&peer->ref_cnt); /* set to 0 */ + ol_txrx_peer_get_ref(peer, + PEER_DEBUG_ID_OL_HASH_ERS); + ol_txrx_peer_release_ref(peer, + PEER_DEBUG_ID_OL_HASH_ERS); + } + } + } +} + +/*=== function definitions for peer id --> peer object map ==================*/ + +static int ol_txrx_peer_find_map_attach(struct ol_txrx_pdev_t *pdev) +{ + int max_peers, peer_map_size; + + /* allocate the peer ID -> peer object map */ + max_peers = ol_cfg_max_peer_id(pdev->ctrl_pdev) + 1; + peer_map_size = max_peers * sizeof(pdev->peer_id_to_obj_map[0]); + pdev->peer_id_to_obj_map = qdf_mem_malloc(peer_map_size); + if (!pdev->peer_id_to_obj_map) + return 1; /* failure */ + + return 0; /* success */ +} + +static void ol_txrx_peer_find_map_detach(struct ol_txrx_pdev_t *pdev) +{ + qdf_mem_free(pdev->peer_id_to_obj_map); +} + +/** + * ol_txrx_peer_clear_map_peer() - Remove map entries that refer to a peer. + * @pdev: pdev handle + * @peer: peer for removing obj map entries + * + * Run through the entire peer_id_to_obj map and nullify all the entries + * that map to a particular peer. Called before deleting the peer object. + * + * Return: None + */ +void ol_txrx_peer_clear_map_peer(ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer) +{ + int max_peers; + int i; + + max_peers = ol_cfg_max_peer_id(pdev->ctrl_pdev) + 1; + + qdf_spin_lock_bh(&pdev->peer_map_unmap_lock); + for (i = 0; i < max_peers; i++) { + if (pdev->peer_id_to_obj_map[i].peer == peer) { + /* Found a map entry for this peer, clear it. */ + pdev->peer_id_to_obj_map[i].peer = NULL; + } + } + qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock); +} + +/* + * ol_txrx_peer_find_add_id() - Add peer_id entry to peer + * + * @pdev: Handle to pdev object + * @peer_mac_addr: MAC address of peer provided by firmware + * @peer_id: peer_id provided by firmware + * + * Search for peer object for the MAC address, add the peer_id to + * its array of peer_id's and update the peer_id_to_obj map entry + * for that peer_id. Increment corresponding reference counts. + * + * Riva/Pronto has one peer id for each peer. + * Peregrine/Rome has two peer id for each peer. + * iHelium has upto three peer id for each peer. + * + * Return: None + */ +static inline void ol_txrx_peer_find_add_id(struct ol_txrx_pdev_t *pdev, + uint8_t *peer_mac_addr, uint16_t peer_id) +{ + struct ol_txrx_peer_t *peer; + int status; + int i; + uint32_t peer_id_ref_cnt; + uint32_t peer_ref_cnt; + u8 check_valid = 0; + + if (pdev->enable_peer_unmap_conf_support) + check_valid = 1; + + /* check if there's already a peer object with this MAC address */ + peer = + ol_txrx_peer_find_hash_find_get_ref(pdev, peer_mac_addr, + 1 /* is aligned */, + check_valid, + PEER_DEBUG_ID_OL_PEER_MAP); + + if (!peer || peer_id == HTT_INVALID_PEER) { + /* + * Currently peer IDs are assigned for vdevs as well as peers. + * If the peer ID is for a vdev, then we will fail to find a + * peer with a matching MAC address. + */ + ol_txrx_err("peer not found or peer ID is %d invalid", + peer_id); + wlan_roam_debug_log(DEBUG_INVALID_VDEV_ID, + DEBUG_PEER_MAP_EVENT, + peer_id, peer_mac_addr, + peer, 0, 0); + + return; + } + + qdf_spin_lock(&pdev->peer_map_unmap_lock); + + /* peer's ref count was already incremented by + * peer_find_hash_find + */ + if (!pdev->peer_id_to_obj_map[peer_id].peer) { + pdev->peer_id_to_obj_map[peer_id].peer = peer; + qdf_atomic_init + (&pdev->peer_id_to_obj_map[peer_id].peer_id_ref_cnt); + } + qdf_atomic_inc + (&pdev->peer_id_to_obj_map[peer_id].peer_id_ref_cnt); + + status = 1; + + /* find a place in peer_id array and insert peer_id */ + for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) { + if (peer->peer_ids[i] == HTT_INVALID_PEER) { + peer->peer_ids[i] = peer_id; + status = 0; + break; + } + } + + if (qdf_atomic_read(&peer->fw_create_pending) == 1) { + qdf_atomic_set(&peer->fw_create_pending, 0); + } + + qdf_spin_unlock(&pdev->peer_map_unmap_lock); + + peer_id_ref_cnt = qdf_atomic_read(&pdev-> + peer_id_to_obj_map[peer_id].peer_id_ref_cnt); + peer_ref_cnt = qdf_atomic_read(&peer->ref_cnt); + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: peer %pK ID %d peer_id[%d] peer_id_ref_cnt %d peer->ref_cnt %d", + __func__, peer, peer_id, i, peer_id_ref_cnt, peer_ref_cnt); + wlan_roam_debug_log(DEBUG_INVALID_VDEV_ID, + DEBUG_PEER_MAP_EVENT, + peer_id, &peer->mac_addr.raw, peer, + peer_id_ref_cnt, + peer_ref_cnt); + + + if (status) { + /* TBDXXX: assert for now */ + qdf_assert(0); + } +} + +/*=== allocation / deallocation function definitions ========================*/ + +int ol_txrx_peer_find_attach(struct ol_txrx_pdev_t *pdev) +{ + if (ol_txrx_peer_find_map_attach(pdev)) + return 1; + if (ol_txrx_peer_find_hash_attach(pdev)) { + ol_txrx_peer_find_map_detach(pdev); + return 1; + } + return 0; /* success */ +} + +void ol_txrx_peer_find_detach(struct ol_txrx_pdev_t *pdev) +{ + ol_txrx_peer_find_map_detach(pdev); + ol_txrx_peer_find_hash_detach(pdev); +} + +/** + * ol_txrx_peer_unmap_conf_handler() - send peer unmap conf cmd to FW + * @pdev: pdev_handle + * @peer_id: peer_id + * + * Return: None + */ +static inline void +ol_txrx_peer_unmap_conf_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (peer_id == HTT_INVALID_PEER) { + ol_txrx_err( + "invalid peer ID %d\n", peer_id); + return; + } + + qdf_atomic_inc(&pdev->peer_id_to_obj_map[peer_id].peer_id_unmap_cnt); + + if (qdf_atomic_read( + &pdev->peer_id_to_obj_map[peer_id].peer_id_unmap_cnt) == + pdev->peer_id_unmap_ref_cnt) { + ol_txrx_dbg("send unmap conf cmd: peer_id[%d] unmap_cnt[%d]", + peer_id, pdev->peer_id_unmap_ref_cnt); + status = pdev->peer_unmap_sync_cb( + DEBUG_INVALID_VDEV_ID, + 1, &peer_id); + + if (status == QDF_STATUS_SUCCESS || + status == QDF_STATUS_E_BUSY) { + qdf_atomic_init( + &pdev->peer_id_to_obj_map[peer_id].peer_id_unmap_cnt); + } else { + qdf_atomic_set( + &pdev->peer_id_to_obj_map[peer_id].peer_id_unmap_cnt, + OL_TXRX_INVALID_PEER_UNMAP_COUNT); + ol_txrx_err("unable to send unmap conf cmd [%d]", + peer_id); + } + + } +} + +/*=== function definitions for message handling =============================*/ + +#if defined(CONFIG_HL_SUPPORT) + +void +ol_rx_peer_map_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + uint8_t vdev_id, uint8_t *peer_mac_addr, int tx_ready) +{ + ol_txrx_peer_find_add_id(pdev, peer_mac_addr, peer_id); + if (!tx_ready) { + struct ol_txrx_peer_t *peer; + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (!peer) { + /* ol_txrx_peer_detach called before peer map arrived*/ + return; + } else { + if (tx_ready) { + int i; + + /* unpause all tx queues now, since the + * target is ready + */ + for (i = 0; i < QDF_ARRAY_SIZE(peer->txqs); + i++) + ol_txrx_peer_tid_unpause(peer, i); + + } else { + /* walk through paused mgmt queue, + * update tx descriptors + */ + ol_tx_queue_decs_reinit(peer, peer_id); + + /* keep non-mgmt tx queues paused until assoc + * is finished tx queues were paused in + * ol_txrx_peer_attach + */ + /* unpause tx mgmt queue */ + ol_txrx_peer_tid_unpause(peer, + HTT_TX_EXT_TID_MGMT); + } + } + } +} + +void ol_txrx_peer_tx_ready_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id) +{ + struct ol_txrx_peer_t *peer; + + peer = ol_txrx_peer_find_by_id(pdev, peer_id); + if (peer) { + int i; + /* + * Unpause all data tx queues now that the target is ready. + * The mgmt tx queue was not paused, so skip it. + */ + for (i = 0; i < QDF_ARRAY_SIZE(peer->txqs); i++) { + if (i == HTT_TX_EXT_TID_MGMT) + continue; /* mgmt tx queue was not paused */ + + ol_txrx_peer_tid_unpause(peer, i); + } + } +} +#else + +void +ol_rx_peer_map_handler(ol_txrx_pdev_handle pdev, + uint16_t peer_id, + uint8_t vdev_id, + uint8_t *peer_mac_addr, + int tx_ready) +{ + ol_txrx_peer_find_add_id(pdev, peer_mac_addr, peer_id); +} + +void ol_txrx_peer_tx_ready_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id) +{ +} + +#endif + +/* + * ol_rx_peer_unmap_handler() - Handle peer unmap event from firmware + * + * @pdev: Handle to pdev pbject + * @peer_id: peer_id unmapped by firmware + * + * Decrement reference count for the peer_id in peer_id_to_obj_map, + * decrement reference count in corresponding peer object and clear the entry + * in peer's peer_ids array. + * In case of unmap events for a peer that is already deleted, just decrement + * del_peer_id_ref_cnt. + * + * Return: None + */ +void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id) +{ + struct ol_txrx_peer_t *peer; + int i = 0; + int32_t ref_cnt; + + if (peer_id == HTT_INVALID_PEER) { + ol_txrx_err( + "invalid peer ID %d\n", peer_id); + wlan_roam_debug_log(DEBUG_INVALID_VDEV_ID, + DEBUG_PEER_UNMAP_EVENT, + peer_id, NULL, NULL, 0, 0x100); + return; + } + + qdf_spin_lock_bh(&pdev->peer_map_unmap_lock); + + /* send peer unmap conf cmd to fw for unmapped peer_ids */ + if (pdev->enable_peer_unmap_conf_support && + pdev->peer_unmap_sync_cb) + ol_txrx_peer_unmap_conf_handler(pdev, peer_id); + + if (qdf_atomic_read( + &pdev->peer_id_to_obj_map[peer_id].del_peer_id_ref_cnt)) { + /* This peer_id belongs to a peer already deleted */ + qdf_atomic_dec(&pdev->peer_id_to_obj_map[peer_id]. + del_peer_id_ref_cnt); + ref_cnt = qdf_atomic_read(&pdev->peer_id_to_obj_map[peer_id]. + del_peer_id_ref_cnt); + qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock); + wlan_roam_debug_log(DEBUG_INVALID_VDEV_ID, + DEBUG_PEER_UNMAP_EVENT, + peer_id, NULL, NULL, ref_cnt, 0x101); + ol_txrx_dbg("peer already deleted, peer_id %d del_peer_id_ref_cnt %d", + peer_id, ref_cnt); + return; + } + peer = pdev->peer_id_to_obj_map[peer_id].peer; + + if (!peer) { + /* + * Currently peer IDs are assigned for vdevs as well as peers. + * If the peer ID is for a vdev, then the peer pointer stored + * in peer_id_to_obj_map will be NULL. + */ + qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock); + ol_txrx_info("peer not found for peer_id %d", peer_id); + wlan_roam_debug_log(DEBUG_INVALID_VDEV_ID, + DEBUG_PEER_UNMAP_EVENT, + peer_id, NULL, NULL, 0, 0x102); + return; + } + + if (qdf_atomic_dec_and_test + (&pdev->peer_id_to_obj_map[peer_id].peer_id_ref_cnt)) { + pdev->peer_id_to_obj_map[peer_id].peer = NULL; + for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) { + if (peer->peer_ids[i] == peer_id) { + peer->peer_ids[i] = HTT_INVALID_PEER; + break; + } + } + } + + ref_cnt = qdf_atomic_read + (&pdev->peer_id_to_obj_map[peer_id].peer_id_ref_cnt); + + qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock); + + wlan_roam_debug_log(DEBUG_INVALID_VDEV_ID, + DEBUG_PEER_UNMAP_EVENT, + peer_id, &peer->mac_addr.raw, peer, ref_cnt, + qdf_atomic_read(&peer->ref_cnt)); + + /* + * Remove a reference to the peer. + * If there are no more references, delete the peer object. + */ + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_PEER_MAP); + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "%s: peer_id %d peer %pK peer_id_ref_cnt %d", + __func__, peer_id, peer, ref_cnt); +} + +/** + * ol_txrx_peer_remove_obj_map_entries() - Remove matching pdev peer map entries + * @pdev: pdev handle + * @peer: peer for removing obj map entries + * + * Saves peer_id_ref_cnt to a different field and removes the link + * to peer object. It also decrements the peer reference count by + * the number of references removed. + * + * Return: None + */ +void ol_txrx_peer_remove_obj_map_entries(ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer) +{ + int i; + uint16_t peer_id; + int32_t peer_id_ref_cnt; + int32_t num_deleted_maps = 0; + uint16_t save_peer_ids[MAX_NUM_PEER_ID_PER_PEER]; + uint16_t save_peer_id_ref_cnt[MAX_NUM_PEER_ID_PER_PEER] = {0}; + + qdf_spin_lock_bh(&pdev->peer_map_unmap_lock); + for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) { + peer_id = peer->peer_ids[i]; + save_peer_ids[i] = HTT_INVALID_PEER; + if (peer_id == HTT_INVALID_PEER || + !pdev->peer_id_to_obj_map[peer_id].peer) { + /* unused peer_id, or object is already dereferenced */ + continue; + } + if (pdev->peer_id_to_obj_map[peer_id].peer != peer) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_ERROR, + FL("peer pointer mismatch in peer_id_to_obj")); + continue; + } + peer_id_ref_cnt = qdf_atomic_read( + &pdev->peer_id_to_obj_map[peer_id]. + peer_id_ref_cnt); + save_peer_ids[i] = peer_id; + save_peer_id_ref_cnt[i] = peer_id_ref_cnt; + + /* + * Transfer peer_id_ref_cnt into del_peer_id_ref_cnt so that + * ol_txrx_peer_release_ref will decrement del_peer_id_ref_cnt + * and any map events will increment peer_id_ref_cnt. Otherwise + * accounting will be messed up. + * + * Add operation will ensure that back to back roaming in the + * middle of unmap/map event sequence will be accounted for. + */ + qdf_atomic_add(peer_id_ref_cnt, + &pdev->peer_id_to_obj_map[peer_id].del_peer_id_ref_cnt); + qdf_atomic_init(&pdev->peer_id_to_obj_map[peer_id]. + peer_id_ref_cnt); + num_deleted_maps += peer_id_ref_cnt; + pdev->peer_id_to_obj_map[peer_id].peer = NULL; + peer->peer_ids[i] = HTT_INVALID_PEER; + } + qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock); + + /* Debug print the information after releasing bh spinlock */ + for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) { + if (save_peer_ids[i] == HTT_INVALID_PEER) + continue; + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + FL("peer_id = %d, peer_id_ref_cnt = %d, index = %d"), + save_peer_ids[i], save_peer_id_ref_cnt[i], i); + } + + if (num_deleted_maps > qdf_atomic_read(&peer->ref_cnt)) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + FL("num_deleted_maps %d ref_cnt %d"), + num_deleted_maps, qdf_atomic_read(&peer->ref_cnt)); + QDF_BUG(0); + return; + } + + while (num_deleted_maps-- > 0) + ol_txrx_peer_release_ref(peer, PEER_DEBUG_ID_OL_PEER_MAP); +} + +struct ol_txrx_peer_t *ol_txrx_assoc_peer_find(struct ol_txrx_vdev_t *vdev) +{ + struct ol_txrx_peer_t *peer; + + qdf_spin_lock_bh(&vdev->pdev->last_real_peer_mutex); + /* + * Check the TXRX Peer is itself valid And also + * if HTT Peer ID has been setup for this peer + */ + if (vdev->last_real_peer + && vdev->last_real_peer->peer_ids[0] != HTT_INVALID_PEER_ID) { + qdf_spin_lock_bh(&vdev->pdev->peer_ref_mutex); + ol_txrx_peer_get_ref(vdev->last_real_peer, + PEER_DEBUG_ID_OL_INTERNAL); + qdf_spin_unlock_bh(&vdev->pdev->peer_ref_mutex); + peer = vdev->last_real_peer; + } else { + peer = NULL; + } + qdf_spin_unlock_bh(&vdev->pdev->last_real_peer_mutex); + return peer; +} + + +/*=== function definitions for debug ========================================*/ + +#if defined(TXRX_DEBUG_LEVEL) && TXRX_DEBUG_LEVEL > 5 +void ol_txrx_peer_find_display(ol_txrx_pdev_handle pdev, int indent) +{ + int i, max_peers; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*speer map:\n", indent, " "); + max_peers = ol_cfg_max_peer_id(pdev->ctrl_pdev) + 1; + for (i = 0; i < max_peers; i++) { + if (pdev->peer_id_to_obj_map[i].peer) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*sid %d -> %pK\n", + indent + 4, " ", i, + pdev->peer_id_to_obj_map[i].peer); + } + } + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW, + "%*speer hash table:\n", indent, " "); + for (i = 0; i <= pdev->peer_hash.mask; i++) { + if (!TAILQ_EMPTY(&pdev->peer_hash.bins[i])) { + struct ol_txrx_peer_t *peer; + + TAILQ_FOREACH(peer, &pdev->peer_hash.bins[i], + hash_list_elem) { + QDF_TRACE(QDF_MODULE_ID_TXRX, + QDF_TRACE_LEVEL_INFO_LOW, + "%*shash idx %d -> %pK ("QDF_MAC_ADDR_FMT")\n", + indent + 4, " ", i, peer, + QDF_MAC_ADDR_REF(peer->mac_addr.raw)); + } + } + } +} + +#endif /* if TXRX_DEBUG_LEVEL */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_peer_find.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_peer_find.h new file mode 100644 index 0000000000000000000000000000000000000000..1992eb1c9ec8d538ce0858a5376f2630c7c5d7b4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_peer_find.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2011, 2015-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_peer_find.h + * @brief Define the API for the rx peer lookup datapath module. + */ +#ifndef _OL_TXRX_PEER_FIND__H_ +#define _OL_TXRX_PEER_FIND__H_ + +#include /* HTT_INVALID_PEER */ +#include /* ol_txrx_pdev_t, etc. */ +#include /* TXRX_ASSERT */ + +/** + * ol_txrx_peer_get_ref() - get peer reference + * @peer: peer for removing obj map entries + * @dbg_id: debug id to keep track of peer references + * + * The function increments the peer ref count. The ref count can be reduced by + * caling ol_txrx_peer_release_ref function. Callers are responsible for + * acquiring the peer_ref_mutex lock when needed. + * + * Return: peer debug id ref count or error + */ +int +ol_txrx_peer_get_ref(struct ol_txrx_peer_t *peer, + enum peer_debug_id_type dbg_id); + +int ol_txrx_peer_find_attach(struct ol_txrx_pdev_t *pdev); + +void ol_txrx_peer_find_detach(struct ol_txrx_pdev_t *pdev); + +static inline +int +ol_txrx_peer_find_mac_addr_cmp(union ol_txrx_align_mac_addr_t *mac_addr1, + union ol_txrx_align_mac_addr_t *mac_addr2) +{ + return !((mac_addr1->align4.bytes_abcd == mac_addr2->align4.bytes_abcd) + /* + * Intentionally use & rather than &&. + * because the operands are binary rather than generic bool, + * the functionality is equivalent. + * Using && has the advantage of short-circuited evaluation, + * but using & has the advantage of no conditional branching, + * which is a more significant benefit. + */ + & (mac_addr1->align4.bytes_ef == mac_addr2->align4.bytes_ef)); +} + +static inline +struct ol_txrx_peer_t *ol_txrx_peer_find_by_id(struct ol_txrx_pdev_t *pdev, + uint16_t peer_id) +{ + struct ol_txrx_peer_t *peer; + + peer = (peer_id > ol_cfg_max_peer_id(pdev->ctrl_pdev)) ? NULL : + pdev->peer_id_to_obj_map[peer_id].peer; + /* + * Currently, peer IDs are assigned to vdevs as well as peers. + * If the peer ID is for a vdev, the peer_id_to_obj_map entry + * will hold NULL rather than a valid peer pointer. + */ + /* TXRX_ASSERT2(peer); */ + /* + * Only return the peer object if it is valid, + * i.e. it has not already been detached. + * If it has already been detached, then returning the + * peer object could result in unpausing the peer's tx queues + * in HL systems, which is an invalid operation following peer_detach. + */ + if (peer && peer->valid) + return peer; + + return NULL; +} + +void +ol_txrx_peer_find_hash_add(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer); + +struct ol_txrx_peer_t * + ol_txrx_peer_find_hash_find_get_ref + (struct ol_txrx_pdev_t *pdev, + uint8_t *peer_mac_addr, + int mac_addr_is_aligned, + u8 check_valid, + enum peer_debug_id_type dbg_id); + +struct +ol_txrx_peer_t *ol_txrx_peer_vdev_find_hash(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_vdev_t *vdev, + uint8_t *peer_mac_addr, + int mac_addr_is_aligned, + uint8_t check_valid); + +void +ol_txrx_peer_find_hash_remove(struct ol_txrx_pdev_t *pdev, + struct ol_txrx_peer_t *peer); + +void ol_txrx_peer_find_hash_erase(struct ol_txrx_pdev_t *pdev); + +struct ol_txrx_peer_t *ol_txrx_assoc_peer_find(struct ol_txrx_vdev_t *vdev); +void ol_txrx_peer_remove_obj_map_entries(ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer); +void ol_txrx_peer_clear_map_peer(ol_txrx_pdev_handle pdev, + struct ol_txrx_peer_t *peer); +#if defined(TXRX_DEBUG_LEVEL) && TXRX_DEBUG_LEVEL > 5 +void ol_txrx_peer_find_display(ol_txrx_pdev_handle pdev, int indent); +#else +#define ol_txrx_peer_find_display(pdev, indent) +#endif /* TXRX_DEBUG_LEVEL */ + +#endif /* _OL_TXRX_PEER_FIND__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_types.h b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_types.h new file mode 100644 index 0000000000000000000000000000000000000000..f9fedd30279d34f6f7f5c504eabec41153747431 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_txrx_types.h @@ -0,0 +1,1567 @@ +/* + * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file ol_txrx_types.h + * @brief Define the major data types used internally by the host datapath SW. + */ +#ifndef _OL_TXRX_TYPES__H_ +#define _OL_TXRX_TYPES__H_ + +#include /* qdf_nbuf_t */ +#include +#include "queue.h" /* TAILQ */ +#include /* A_UINT8 */ +#include /* htt_sec_type, htt_pkt_type, etc. */ +#include /* qdf_atomic_t */ +#include /* wdi_event_subscribe */ +#include /* qdf_timer_t */ +#include /* qdf_spinlock */ +#include /* ol_pktlog_dev_handle */ +#include +#include "ol_txrx_htt_api.h" +#include "ol_htt_tx_api.h" +#include "ol_htt_rx_api.h" +#include "ol_txrx_ctrl_api.h" /* WLAN_MAX_STA_COUNT */ +#include "ol_txrx_osif_api.h" /* ol_rx_callback */ +#include "cdp_txrx_flow_ctrl_v2.h" +#include "cdp_txrx_peer_ops.h" +#include +#include "qdf_hrtimer.h" + +/* + * The target may allocate multiple IDs for a peer. + * In particular, the target may allocate one ID to represent the + * multicast key the peer uses, and another ID to represent the + * unicast key the peer uses. + */ +#define MAX_NUM_PEER_ID_PER_PEER 16 + +/* OL_TXRX_NUM_EXT_TIDS - + * 16 "real" TIDs + 3 pseudo-TIDs for mgmt, mcast/bcast & non-QoS data + */ +#define OL_TXRX_NUM_EXT_TIDS 19 + +#define OL_TX_NUM_QOS_TIDS 16 /* 16 regular TIDs */ +#define OL_TX_NON_QOS_TID 16 +#define OL_TX_MGMT_TID 17 +#define OL_TX_NUM_TIDS 18 +#define OL_RX_MCAST_TID 18 /* Mcast TID only between f/w & host */ + +#define OL_TX_VDEV_MCAST_BCAST 0 /* HTT_TX_EXT_TID_MCAST_BCAST */ +#define OL_TX_VDEV_DEFAULT_MGMT 1 /* HTT_TX_EXT_TID_DEFALT_MGMT */ +#define OL_TX_VDEV_NUM_QUEUES 2 + +#define OL_TXRX_MGMT_TYPE_BASE htt_pkt_num_types +#define OL_TXRX_MGMT_NUM_TYPES 8 + +#define OL_TX_MUTEX_TYPE qdf_spinlock_t +#define OL_RX_MUTEX_TYPE qdf_spinlock_t + +/* TXRX Histogram defines */ +#define TXRX_DATA_HISTROGRAM_GRANULARITY 1000 +#define TXRX_DATA_HISTROGRAM_NUM_INTERVALS 100 + +#define OL_TXRX_INVALID_VDEV_ID (-1) +#define ETHERTYPE_OCB_TX 0x8151 +#define ETHERTYPE_OCB_RX 0x8152 + +#define OL_TXRX_MAX_PDEV_CNT 1 + +struct ol_txrx_pdev_t; +struct ol_txrx_vdev_t; +struct ol_txrx_peer_t; + +/* rx filter related */ +#define MAX_PRIVACY_FILTERS 4 /* max privacy filters */ + +enum privacy_filter { + PRIVACY_FILTER_ALWAYS, + PRIVACY_FILTER_KEY_UNAVAILABLE, +}; + +enum privacy_filter_packet_type { + PRIVACY_FILTER_PACKET_UNICAST, + PRIVACY_FILTER_PACKET_MULTICAST, + PRIVACY_FILTER_PACKET_BOTH +}; + +struct privacy_exemption { + /* ethertype - + * type of ethernet frames this filter applies to, in host byte order + */ + uint16_t ether_type; + enum privacy_filter filter_type; + enum privacy_filter_packet_type packet_type; +}; + +enum ol_tx_frm_type { + OL_TX_FRM_STD = 0, /* regular frame - no added header fragments */ + OL_TX_FRM_TSO, /* TSO segment, with a modified IP header added */ + OL_TX_FRM_AUDIO, /* audio frames, with a custom LLC/SNAP hdr added */ + OL_TX_FRM_NO_FREE, /* frame requires special tx completion callback */ + ol_tx_frm_freed = 0xff, /* the tx desc is in free list */ +}; + +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) + +#define MAX_NO_PEERS_IN_LIMIT (2*10 + 2) + +enum ol_tx_peer_bal_state { + ol_tx_peer_bal_enable = 0, + ol_tx_peer_bal_disable, +}; + +enum ol_tx_peer_bal_timer_state { + ol_tx_peer_bal_timer_disable = 0, + ol_tx_peer_bal_timer_active, + ol_tx_peer_bal_timer_inactive, +}; + +struct ol_tx_limit_peer_t { + u_int16_t limit_flag; + u_int16_t peer_id; + u_int16_t limit; +}; + +enum tx_peer_level { + TXRX_IEEE11_B = 0, + TXRX_IEEE11_A_G, + TXRX_IEEE11_N, + TXRX_IEEE11_AC, + TXRX_IEEE11_AX, + TXRX_IEEE11_MAX, +}; + +struct tx_peer_threshold { + u_int32_t tput_thresh; + u_int32_t tx_limit; +}; +#endif + + +struct ol_tx_desc_t { + qdf_nbuf_t netbuf; + void *htt_tx_desc; + uint16_t id; + qdf_dma_addr_t htt_tx_desc_paddr; + void *htt_frag_desc; /* struct msdu_ext_desc_t * */ + qdf_dma_addr_t htt_frag_desc_paddr; + qdf_atomic_t ref_cnt; + enum htt_tx_status status; + +#ifdef QCA_COMPUTE_TX_DELAY + uint32_t entry_timestamp_ticks; +#endif + +#ifdef DESC_TIMESTAMP_DEBUG_INFO + struct { + uint64_t prev_tx_ts; + uint64_t curr_tx_ts; + uint64_t last_comp_ts; + } desc_debug_info; +#endif + + /* + * Allow tx descriptors to be stored in (doubly-linked) lists. + * This is mainly used for HL tx queuing and scheduling, but is + * also used by LL+HL for batch processing of tx frames. + */ + TAILQ_ENTRY(ol_tx_desc_t) tx_desc_list_elem; + + /* + * Remember whether the tx frame is a regular packet, or whether + * the driver added extra header fragments (e.g. a modified IP header + * for TSO fragments, or an added LLC/SNAP header for audio interworking + * data) that need to be handled in a special manner. + * This field is filled in with the ol_tx_frm_type enum. + */ + uint8_t pkt_type; + + u_int8_t vdev_id; + + struct ol_txrx_vdev_t *vdev; + + void *txq; + +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + /* + * used by tx encap, to restore the os buf start offset + * after tx complete + */ + uint8_t orig_l2_hdr_bytes; +#endif + +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 + struct ol_tx_flow_pool_t *pool; +#endif + void *tso_desc; + void *tso_num_desc; +}; + +typedef TAILQ_HEAD(some_struct_name, ol_tx_desc_t) ol_tx_desc_list; + +union ol_tx_desc_list_elem_t { + union ol_tx_desc_list_elem_t *next; + struct ol_tx_desc_t tx_desc; +}; + +union ol_txrx_align_mac_addr_t { + uint8_t raw[QDF_MAC_ADDR_SIZE]; + struct { + uint16_t bytes_ab; + uint16_t bytes_cd; + uint16_t bytes_ef; + } align2; + struct { + uint32_t bytes_abcd; + uint16_t bytes_ef; + } align4; +}; + +struct ol_rx_reorder_timeout_list_elem_t { + TAILQ_ENTRY(ol_rx_reorder_timeout_list_elem_t) + reorder_timeout_list_elem; + uint32_t timestamp_ms; + struct ol_txrx_peer_t *peer; + uint8_t tid; + uint8_t active; +}; + +/* wait on peer deletion timeout value in milliseconds */ +#define PEER_DELETION_TIMEOUT 500 + +enum txrx_wmm_ac { + TXRX_WMM_AC_BE, + TXRX_WMM_AC_BK, + TXRX_WMM_AC_VI, + TXRX_WMM_AC_VO, + + TXRX_NUM_WMM_AC +}; + +#define TXRX_TID_TO_WMM_AC(_tid) ( \ + (((_tid) >> 1) == 3) ? TXRX_WMM_AC_VO : \ + (((_tid) >> 1) == 2) ? TXRX_WMM_AC_VI : \ + (((_tid) ^ ((_tid) >> 1)) & 0x1) ? TXRX_WMM_AC_BK : \ + TXRX_WMM_AC_BE) + +enum { + OL_TX_SCHED_WRR_ADV_CAT_BE, + OL_TX_SCHED_WRR_ADV_CAT_BK, + OL_TX_SCHED_WRR_ADV_CAT_VI, + OL_TX_SCHED_WRR_ADV_CAT_VO, + OL_TX_SCHED_WRR_ADV_CAT_NON_QOS_DATA, + OL_TX_SCHED_WRR_ADV_CAT_UCAST_MGMT, + OL_TX_SCHED_WRR_ADV_CAT_MCAST_DATA, + OL_TX_SCHED_WRR_ADV_CAT_MCAST_MGMT, + + OL_TX_SCHED_WRR_ADV_NUM_CATEGORIES /* must be last */ +}; + +A_COMPILE_TIME_ASSERT(ol_tx_sched_htt_ac_values, + /* check that regular WMM AC enum values match */ + ((int)OL_TX_SCHED_WRR_ADV_CAT_VO == (int)HTT_AC_WMM_VO) && + ((int)OL_TX_SCHED_WRR_ADV_CAT_VI == (int)HTT_AC_WMM_VI) && + ((int)OL_TX_SCHED_WRR_ADV_CAT_BK == (int)HTT_AC_WMM_BK) && + ((int)OL_TX_SCHED_WRR_ADV_CAT_BE == (int)HTT_AC_WMM_BE) && + + /* check that extension AC enum values match */ + ((int)OL_TX_SCHED_WRR_ADV_CAT_NON_QOS_DATA + == (int)HTT_AC_EXT_NON_QOS) && + ((int)OL_TX_SCHED_WRR_ADV_CAT_UCAST_MGMT + == (int)HTT_AC_EXT_UCAST_MGMT) && + ((int)OL_TX_SCHED_WRR_ADV_CAT_MCAST_DATA + == (int)HTT_AC_EXT_MCAST_DATA) && + ((int)OL_TX_SCHED_WRR_ADV_CAT_MCAST_MGMT + == (int)HTT_AC_EXT_MCAST_MGMT)); + +struct ol_tx_reorder_cat_timeout_t { + TAILQ_HEAD(, ol_rx_reorder_timeout_list_elem_t) virtual_timer_list; + qdf_timer_t timer; + uint32_t duration_ms; + struct ol_txrx_pdev_t *pdev; +}; + +enum ol_tx_scheduler_status { + ol_tx_scheduler_idle = 0, + ol_tx_scheduler_running, +}; + +enum ol_tx_queue_status { + ol_tx_queue_empty = 0, + ol_tx_queue_active, + ol_tx_queue_paused, +}; + +struct ol_txrx_msdu_info_t { + struct htt_msdu_info_t htt; + struct ol_txrx_peer_t *peer; + struct qdf_tso_info_t tso_info; +}; + +enum { + ol_tx_aggr_untried = 0, + ol_tx_aggr_enabled, + ol_tx_aggr_disabled, + ol_tx_aggr_retry, + ol_tx_aggr_in_progress, +}; + +#define OL_TX_MAX_GROUPS_PER_QUEUE 1 +#define OL_TX_MAX_VDEV_ID 16 +#define OL_TXQ_GROUP_VDEV_ID_MASK_GET(_membership) \ + (((_membership) & 0xffff0000) >> 16) +#define OL_TXQ_GROUP_VDEV_ID_BIT_MASK_GET(_mask, _vdev_id) \ + ((_mask >> _vdev_id) & 0x01) +#define OL_TXQ_GROUP_AC_MASK_GET(_membership) \ + ((_membership) & 0x0000ffff) +#define OL_TXQ_GROUP_AC_BIT_MASK_GET(_mask, _ac_mask) \ + ((_mask >> _ac_mask) & 0x01) +#define OL_TXQ_GROUP_MEMBERSHIP_GET(_vdev_mask, _ac_mask) \ + ((_vdev_mask << 16) | _ac_mask) + +struct ol_tx_frms_queue_t { + /* list_elem - + * Allow individual tx frame queues to be linked together into + * scheduler queues of tx frame queues + */ + TAILQ_ENTRY(ol_tx_frms_queue_t) list_elem; + uint8_t aggr_state; + struct { + uint8_t total; + /* pause requested by ctrl SW rather than txrx SW */ + uint8_t by_ctrl; + } paused_count; + uint8_t ext_tid; + uint16_t frms; + uint32_t bytes; + ol_tx_desc_list head; + enum ol_tx_queue_status flag; + struct ol_tx_queue_group_t *group_ptrs[OL_TX_MAX_GROUPS_PER_QUEUE]; +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) + struct ol_txrx_peer_t *peer; +#endif +}; + +enum { + ol_tx_log_entry_type_invalid, + ol_tx_log_entry_type_queue_state, + ol_tx_log_entry_type_enqueue, + ol_tx_log_entry_type_dequeue, + ol_tx_log_entry_type_drop, + ol_tx_log_entry_type_queue_free, + + ol_tx_log_entry_type_wrap, +}; + +struct ol_tx_log_queue_state_var_sz_t { + uint32_t active_bitmap; + uint16_t credit; + uint8_t num_cats_active; + uint8_t data[1]; +}; + +struct ol_tx_log_queue_add_t { + uint8_t num_frms; + uint8_t tid; + uint16_t peer_id; + uint16_t num_bytes; +}; + +struct ol_mac_addr { + uint8_t mac_addr[QDF_MAC_ADDR_SIZE]; +}; + +struct ol_tx_sched_t; + +#ifndef ol_txrx_local_peer_id_t +#define ol_txrx_local_peer_id_t uint8_t /* default */ +#endif + +#ifdef QCA_COMPUTE_TX_DELAY +/* + * Delay histogram bins: 16 bins of 10 ms each to count delays + * from 0-160 ms, plus one overflow bin for delays > 160 ms. + */ +#define QCA_TX_DELAY_HIST_INTERNAL_BINS 17 +#define QCA_TX_DELAY_HIST_INTERNAL_BIN_WIDTH_MS 10 + +struct ol_tx_delay_data { + struct { + uint64_t transmit_sum_ticks; + uint64_t queue_sum_ticks; + uint32_t transmit_num; + uint32_t queue_num; + } avgs; + uint16_t hist_bins_queue[QCA_TX_DELAY_HIST_INTERNAL_BINS]; +}; + +#endif /* QCA_COMPUTE_TX_DELAY */ + +/* Thermal Mitigation */ +enum throttle_phase { + THROTTLE_PHASE_OFF, + THROTTLE_PHASE_ON, + /* Invalid */ + THROTTLE_PHASE_MAX, +}; + +#define THROTTLE_TX_THRESHOLD (100) + +/* + * Threshold to stop/start priority queue in term of % the actual flow start + * and stop thresholds. When num of available descriptors falls below + * stop_priority_th, priority queue will be paused. When num of available + * descriptors are greater than start_priority_th, priority queue will be + * un-paused. + */ +#define TX_PRIORITY_TH (80) + +/* + * No of maximum descriptor used by TSO jumbo packet with + * 64K aggregation. + */ +#define MAX_TSO_SEGMENT_DESC (44) + +struct ol_tx_queue_group_t { + qdf_atomic_t credit; + u_int32_t membership; + int frm_count; +}; +#define OL_TX_MAX_TXQ_GROUPS 2 + +#define OL_TX_GROUP_STATS_LOG_SIZE 128 +struct ol_tx_group_credit_stats_t { + struct { + struct { + u_int16_t member_vdevs; + u_int16_t credit; + } grp[OL_TX_MAX_TXQ_GROUPS]; + } stats[OL_TX_GROUP_STATS_LOG_SIZE]; + u_int16_t last_valid_index; + u_int16_t wrap_around; +}; + + +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +/** + * enum flow_pool_status - flow pool status + * @FLOW_POOL_ACTIVE_UNPAUSED : pool is active (can take/put descriptors) + * and network queues are unpaused + * @FLOW_POOL_ACTIVE_PAUSED: pool is active (can take/put descriptors) + * and network queues are paused + * @FLOW_POOL_INVALID: pool is invalid (put descriptor) + * @FLOW_POOL_INACTIVE: pool is inactive (pool is free) + * @FLOW_POOL_NON_PRIO_PAUSED: non-priority queues are paused + */ +enum flow_pool_status { + FLOW_POOL_ACTIVE_UNPAUSED = 0, + FLOW_POOL_ACTIVE_PAUSED = 1, + FLOW_POOL_NON_PRIO_PAUSED = 2, + FLOW_POOL_INVALID = 3, + FLOW_POOL_INACTIVE = 4 +}; + +/** + * struct ol_txrx_pool_stats - flow pool related statistics + * @pool_map_count: flow pool map received + * @pool_unmap_count: flow pool unmap received + * @pool_resize_count: flow pool resize command received + * @pkt_drop_no_pool: packets dropped due to unavailablity of pool + */ +struct ol_txrx_pool_stats { + uint16_t pool_map_count; + uint16_t pool_unmap_count; + uint16_t pool_resize_count; + uint16_t pkt_drop_no_pool; +}; + +/** + * struct ol_tx_flow_pool_t - flow_pool info + * @flow_pool_list_elem: flow_pool_list element + * @flow_pool_lock: flow_pool lock + * @flow_pool_id: flow_pool id + * @flow_pool_size: flow_pool size + * @avail_desc: available descriptors + * @deficient_desc: deficient descriptors + * @overflow_desc: overflow descriptors + * @status: flow pool status + * @flow_type: flow pool type + * @member_flow_id: member flow id + * @stop_th: stop threshold + * @start_th: start threshold + * @freelist: tx descriptor freelist + * @pkt_drop_no_desc: drop due to no descriptors + * @ref_cnt: pool's ref count + * @stop_priority_th: Threshold to stop priority queue + * @start_priority_th: Threshold to start priority queue + */ +struct ol_tx_flow_pool_t { + TAILQ_ENTRY(ol_tx_flow_pool_t) flow_pool_list_elem; + qdf_spinlock_t flow_pool_lock; + uint8_t flow_pool_id; + uint16_t flow_pool_size; + uint16_t avail_desc; + uint16_t deficient_desc; + uint16_t overflow_desc; + enum flow_pool_status status; + enum htt_flow_type flow_type; + uint8_t member_flow_id; + uint16_t stop_th; + uint16_t start_th; + union ol_tx_desc_list_elem_t *freelist; + uint16_t pkt_drop_no_desc; + qdf_atomic_t ref_cnt; + uint16_t stop_priority_th; + uint16_t start_priority_th; +}; +#endif + +#define OL_TXRX_INVALID_PEER_UNMAP_COUNT 0xF +/* + * struct ol_txrx_peer_id_map - Map of firmware peer_ids to peers on host + * @peer: Pointer to peer object + * @peer_id_ref_cnt: No. of firmware references to the peer_id + * @del_peer_id_ref_cnt: No. of outstanding unmap events for peer_id + * after the peer object is deleted on the host. + * + * peer_id is used as an index into the array of ol_txrx_peer_id_map. + */ +struct ol_txrx_peer_id_map { + struct ol_txrx_peer_t *peer; + qdf_atomic_t peer_id_ref_cnt; + qdf_atomic_t del_peer_id_ref_cnt; + qdf_atomic_t peer_id_unmap_cnt; +}; + +/** + * ol_txrx_stats_req_internal - specifications of the requested + * statistics internally + */ +struct ol_txrx_stats_req_internal { + struct ol_txrx_stats_req base; + TAILQ_ENTRY(ol_txrx_stats_req_internal) req_list_elem; + int serviced; /* state of this request */ + int offset; +}; + +struct ol_txrx_fw_stats_desc_t { + struct ol_txrx_stats_req_internal *req; + unsigned char desc_id; +}; + +struct ol_txrx_fw_stats_desc_elem_t { + struct ol_txrx_fw_stats_desc_elem_t *next; + struct ol_txrx_fw_stats_desc_t desc; +}; + +/** + * struct ol_txrx_soc_t - soc reference structure + * @cdp_soc: common base structure + * @cdp_ctrl_objmgr_psoc: opaque handle for UMAC psoc object + * @pdev_list: list of all the pdev on a soc + * + * This is the reference to the soc and all the data + * which is soc specific. + */ +struct ol_txrx_soc_t { + /* Common base structure - Should be the first member */ + struct cdp_soc_t cdp_soc; + + struct cdp_ctrl_objmgr_psoc *psoc; + struct ol_txrx_pdev_t *pdev_list[OL_TXRX_MAX_PDEV_CNT]; +}; + +/* + * As depicted in the diagram below, the pdev contains an array of + * NUM_EXT_TID ol_tx_active_queues_in_tid_t elements. + * Each element identifies all the tx queues that are active for + * the TID, from the different peers. + * + * Each peer contains an array of NUM_EXT_TID ol_tx_frms_queue_t elements. + * Each element identifies the tx frames for the TID that need to be sent + * to the peer. + * + * + * pdev: ol_tx_active_queues_in_tid_t active_in_tids[NUM_EXT_TIDS] + * TID + * 0 1 2 17 + * +============+============+============+== ==+============+ + * | active (y) | active (n) | active (n) | | active (y) | + * |------------+------------+------------+-- --+------------| + * | queues | queues | queues | | queues | + * +============+============+============+== ==+============+ + * | | + * .--+-----------------------------------------------' + * | | + * | | peer X: peer Y: + * | | ol_tx_frms_queue_t ol_tx_frms_queue_t + * | | tx_queues[NUM_EXT_TIDS] tx_queues[NUM_EXT_TIDS] + * | | TID +======+ TID +======+ + * | `---->| next |-------------------------->| next |--X + * | 0 | prev | .------. .------. 0 | prev | .------. + * | | txq |-->|txdesc|-->|txdesc| | txq |-->|txdesc| + * | +======+ `------' `------' +======+ `------' + * | | next | | | 1 | next | | + * | 1 | prev | v v | prev | v + * | | txq | .------. .------. | txq | .------. + * | +======+ |netbuf| |netbuf| +======+ |netbuf| + * | | next | `------' `------' | next | `------' + * | 2 | prev | 2 | prev | + * | | txq | | txq | + * | +======+ +======+ + * | | | | | + * | + * | + * | | | | | + * | +======+ +======+ + * `------->| next |--X | next | + * 17 | prev | .------. 17 | prev | + * | txq |-->|txdesc| | txq | + * +======+ `------' +======+ + * | + * v + * .------. + * |netbuf| + * `------' + */ +struct ol_txrx_pdev_t { + /* soc - reference to soc structure */ + struct ol_txrx_soc_t *soc; + + /* ctrl_pdev - handle for querying config info */ + struct cdp_cfg *ctrl_pdev; + + /* osdev - handle for mem alloc / free, map / unmap */ + qdf_device_t osdev; + + htt_pdev_handle htt_pdev; + +#ifdef WLAN_FEATURE_PKT_CAPTURE + void *mon_osif_dev; + QDF_STATUS (*mon_cb)(void *osif_dev, + qdf_nbuf_t msdu_list); + uint8_t pktcapture_mode_value; +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +#ifdef WLAN_FEATURE_FASTPATH + struct CE_handle *ce_tx_hdl; /* Handle to Tx packet posting CE */ + struct CE_handle *ce_htt_msg_hdl; /* Handle to TxRx completion CE */ +#endif /* WLAN_FEATURE_FASTPATH */ + + struct { + int is_high_latency; + int host_addba; + int ll_pause_txq_limit; + int default_tx_comp_req; + u8 credit_update_enabled; + u8 request_tx_comp; + } cfg; + + /* WDI subscriber's event list */ + wdi_event_subscribe **wdi_event_list; + +#if !defined(REMOVE_PKT_LOG) && !defined(QVIT) + bool pkt_log_init; + /* Pktlog pdev */ + struct pktlog_dev_t *pl_dev; +#endif /* #ifndef REMOVE_PKT_LOG */ + + /* Monitor mode interface*/ + struct ol_txrx_vdev_t *monitor_vdev; + + enum ol_sec_type sec_types[htt_num_sec_types]; + /* standard frame type */ + enum wlan_frm_fmt frame_format; + enum htt_pkt_type htt_pkt_type; + +#ifdef QCA_SUPPORT_SW_TXRX_ENCAP + /* txrx encap/decap */ + uint8_t sw_tx_encap; + uint8_t sw_rx_decap; + uint8_t target_tx_tran_caps; + uint8_t target_rx_tran_caps; + /* llc process */ + uint8_t sw_tx_llc_proc_enable; + uint8_t sw_rx_llc_proc_enable; + /* A-MSDU */ + uint8_t sw_subfrm_hdr_recovery_enable; + /* Protected Frame bit handling */ + uint8_t sw_pf_proc_enable; +#endif + /* + * target tx credit - + * not needed for LL, but used for HL download scheduler to keep + * track of roughly how much space is available in the target for + * tx frames + */ + qdf_atomic_t target_tx_credit; + qdf_atomic_t orig_target_tx_credit; + + /* + * needed for SDIO HL, Genoa Adma + */ + qdf_atomic_t pad_reserve_tx_credit; + + struct { + uint16_t pool_size; + struct ol_txrx_fw_stats_desc_elem_t *pool; + struct ol_txrx_fw_stats_desc_elem_t *freelist; + qdf_spinlock_t pool_lock; + qdf_atomic_t initialized; + } ol_txrx_fw_stats_desc_pool; + + /* Peer mac address to staid mapping */ + struct ol_mac_addr mac_to_staid[WLAN_MAX_STA_COUNT + 3]; + + /* ol_txrx_vdev list */ + TAILQ_HEAD(, ol_txrx_vdev_t) vdev_list; + + TAILQ_HEAD(, ol_txrx_stats_req_internal) req_list; + int req_list_depth; + qdf_spinlock_t req_list_spinlock; + + /* peer ID to peer object map (array of pointers to peer objects) */ + struct ol_txrx_peer_id_map *peer_id_to_obj_map; + + struct { + unsigned int mask; + unsigned int idx_bits; + + TAILQ_HEAD(, ol_txrx_peer_t) * bins; + } peer_hash; + + /* rx specific processing */ + struct { + struct { + TAILQ_HEAD(, ol_rx_reorder_t) waitlist; + uint32_t timeout_ms; + } defrag; + struct { + int defrag_timeout_check; + int dup_check; + } flags; + + struct { + struct ol_tx_reorder_cat_timeout_t + access_cats[TXRX_NUM_WMM_AC]; + } reorder_timeout; + qdf_spinlock_t mutex; + } rx; + + /* rx proc function */ + void (*rx_opt_proc)(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu_list); + + /* tx data delivery notification callback function */ + struct { + ol_txrx_data_tx_cb func; + void *ctxt; + } tx_data_callback; + + /* tx management delivery notification callback functions */ + struct { + ol_txrx_mgmt_tx_cb download_cb; + ol_txrx_mgmt_tx_cb ota_ack_cb; + void *ctxt; + } tx_mgmt_cb; + + data_stall_detect_cb data_stall_detect_callback; + /* packetdump callback functions */ + ol_txrx_pktdump_cb ol_tx_packetdump_cb; + ol_txrx_pktdump_cb ol_rx_packetdump_cb; + +#ifdef WLAN_FEATURE_TSF_PLUS + tp_ol_timestamp_cb ol_tx_timestamp_cb; +#endif + + struct { + uint16_t pool_size; + uint16_t num_free; + union ol_tx_desc_list_elem_t *array; + union ol_tx_desc_list_elem_t *freelist; +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 + uint8_t num_invalid_bin; + qdf_spinlock_t flow_pool_list_lock; + TAILQ_HEAD(flow_pool_list_t, ol_tx_flow_pool_t) flow_pool_list; +#endif + uint32_t page_size; + uint16_t desc_reserved_size; + uint8_t page_divider; + uint32_t offset_filter; + struct qdf_mem_multi_page_t desc_pages; +#ifdef DESC_DUP_DETECT_DEBUG + unsigned long *free_list_bitmap; +#endif +#ifdef QCA_LL_PDEV_TX_FLOW_CONTROL + uint16_t stop_th; + uint16_t start_th; + uint16_t stop_priority_th; + uint16_t start_priority_th; + enum flow_pool_status status; +#endif + } tx_desc; + + /* The pdev_id for this pdev */ + uint8_t id; + + uint8_t is_mgmt_over_wmi_enabled; +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) + struct ol_txrx_pool_stats pool_stats; + uint32_t num_msdu_desc; +#ifdef QCA_LL_TX_FLOW_GLOBAL_MGMT_POOL + struct ol_tx_flow_pool_t *mgmt_pool; +#endif +#endif + + struct { + int (*cmp)(union htt_rx_pn_t *new, + union htt_rx_pn_t *old, + int is_unicast, int opmode, bool strict_chk); + int len; + } rx_pn[htt_num_sec_types]; + + /* tx mutex */ + OL_TX_MUTEX_TYPE tx_mutex; + + /* + * peer ref mutex: + * 1. Protect peer object lookups until the returned peer object's + * reference count is incremented. + * 2. Provide mutex when accessing peer object lookup structures. + */ + OL_RX_MUTEX_TYPE peer_ref_mutex; + + /* + * last_real_peer_mutex: + * Protect lookups of any vdev's last_real_peer pointer until the + * reference count for the pointed-to peer object is incremented. + * This mutex could be in the vdev struct, but it's slightly simpler + * to have a single lock in the pdev struct. Since the lock is only + * held for an extremely short time, and since it's very unlikely for + * two vdev's to concurrently access the lock, there's no real + * benefit to having a per-vdev lock. + */ + OL_RX_MUTEX_TYPE last_real_peer_mutex; + + qdf_spinlock_t peer_map_unmap_lock; + + ol_txrx_peer_unmap_sync_cb peer_unmap_sync_cb; + + struct { + struct { + struct { + struct { + uint64_t ppdus; + uint64_t mpdus; + } normal; + struct { + /* + * mpdu_bad is general - + * replace it with the specific counters + * below + */ + uint64_t mpdu_bad; + /* uint64_t mpdu_fcs; */ + /* uint64_t mpdu_duplicate; */ + /* uint64_t mpdu_pn_replay; */ + /* uint64_t mpdu_bad_sender; */ + /* ^ comment: peer not found */ + /* uint64_t mpdu_flushed; */ + /* uint64_t msdu_defrag_mic_err; */ + uint64_t msdu_mc_dup_drop; + } err; + } rx; + } priv; + struct ol_txrx_stats pub; + } stats; + +#if defined(ENABLE_RX_REORDER_TRACE) + struct { + uint32_t mask; + uint32_t idx; + uint64_t cnt; +#define TXRX_RX_REORDER_TRACE_SIZE_LOG2 8 /* 256 entries */ + struct { + uint16_t reorder_idx; + uint16_t seq_num; + uint8_t num_mpdus; + uint8_t tid; + } *data; + } rx_reorder_trace; +#endif /* ENABLE_RX_REORDER_TRACE */ + +#if defined(ENABLE_RX_PN_TRACE) + struct { + uint32_t mask; + uint32_t idx; + uint64_t cnt; +#define TXRX_RX_PN_TRACE_SIZE_LOG2 5 /* 32 entries */ + struct { + struct ol_txrx_peer_t *peer; + uint32_t pn32; + uint16_t seq_num; + uint8_t unicast; + uint8_t tid; + } *data; + } rx_pn_trace; +#endif /* ENABLE_RX_PN_TRACE */ + + /* + * tx_sched only applies for HL, but is defined unconditionally + * rather than only if defined(CONFIG_HL_SUPPORT). + * This is because the struct only + * occupies a few bytes, and to avoid the complexity of + * wrapping references + * to the struct members in "defined(CONFIG_HL_SUPPORT)" conditional + * compilation. + * If this struct gets expanded to a non-trivial size, + * then it should be + * conditionally compiled to only apply if defined(CONFIG_HL_SUPPORT). + */ + qdf_spinlock_t tx_queue_spinlock; + struct { + enum ol_tx_scheduler_status tx_sched_status; + struct ol_tx_sched_t *scheduler; + } tx_sched; + /* + * tx_queue only applies for HL, but is defined unconditionally to avoid + * wrapping references to tx_queue in "defined(CONFIG_HL_SUPPORT)" + * conditional compilation. + */ + struct { + qdf_atomic_t rsrc_cnt; + /* threshold_lo - when to start tx desc margin replenishment */ + uint16_t rsrc_threshold_lo; + /* + * threshold_hi - where to stop during tx desc margin + * replenishment + */ + uint16_t rsrc_threshold_hi; + } tx_queue; + +#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT) +#define OL_TXQ_LOG_SIZE 512 + qdf_spinlock_t txq_log_spinlock; + struct { + int size; + int oldest_record_offset; + int offset; + int allow_wrap; + u_int32_t wrapped; + /* aligned to u_int32_t boundary */ + u_int8_t data[OL_TXQ_LOG_SIZE]; + } txq_log; +#endif + +#ifdef QCA_ENABLE_OL_TXRX_PEER_STATS + qdf_spinlock_t peer_stat_mutex; +#endif + + int rssi_update_shift; + int rssi_new_weight; +#ifdef QCA_SUPPORT_TXRX_LOCAL_PEER_ID + struct { + ol_txrx_local_peer_id_t pool[OL_TXRX_NUM_LOCAL_PEER_IDS + 1]; + ol_txrx_local_peer_id_t freelist; + qdf_spinlock_t lock; + ol_txrx_peer_handle map[OL_TXRX_NUM_LOCAL_PEER_IDS]; + } local_peer_ids; +#endif + +#ifdef QCA_COMPUTE_TX_DELAY +#ifdef QCA_COMPUTE_TX_DELAY_PER_TID +#define QCA_TX_DELAY_NUM_CATEGORIES \ + (OL_TX_NUM_TIDS + OL_TX_VDEV_NUM_QUEUES) +#else +#define QCA_TX_DELAY_NUM_CATEGORIES 1 +#endif + struct { + qdf_spinlock_t mutex; + struct { + struct ol_tx_delay_data copies[2]; /* ping-pong */ + int in_progress_idx; + uint32_t avg_start_time_ticks; + } cats[QCA_TX_DELAY_NUM_CATEGORIES]; + uint32_t tx_compl_timestamp_ticks; + uint32_t avg_period_ticks; + uint32_t hist_internal_bin_width_mult; + uint32_t hist_internal_bin_width_shift; + } tx_delay; + + uint16_t packet_count[QCA_TX_DELAY_NUM_CATEGORIES]; + uint16_t packet_loss_count[QCA_TX_DELAY_NUM_CATEGORIES]; + +#endif /* QCA_COMPUTE_TX_DELAY */ + + struct { + qdf_spinlock_t mutex; + /* timer used to monitor the throttle "on" phase and + * "off" phase + */ + qdf_timer_t phase_timer; + /* timer used to send tx frames */ + qdf_timer_t tx_timer; + /* This is the time in ms of the throttling window, it will + * include an "on" phase and an "off" phase + */ + uint32_t throttle_period_ms; + /* Current throttle level set by the client ex. level 0, + * level 1, etc + */ + enum throttle_level current_throttle_level; + /* Index that points to the phase within the throttle period */ + enum throttle_phase current_throttle_phase; + /* Maximum number of frames to send to the target at one time */ + uint32_t tx_threshold; + /* stores time in ms of on/off phase for each throttle level */ + int throttle_time_ms[THROTTLE_LEVEL_MAX][THROTTLE_PHASE_MAX]; + /* mark true if traffic is paused due to thermal throttling */ + bool is_paused; + } tx_throttle; + +#if defined(FEATURE_TSO) + struct { + uint16_t pool_size; + uint16_t num_free; + struct qdf_tso_seg_elem_t *freelist; + /* tso mutex */ + OL_TX_MUTEX_TYPE tso_mutex; + } tso_seg_pool; + struct { + uint16_t num_seg_pool_size; + uint16_t num_free; + struct qdf_tso_num_seg_elem_t *freelist; + /* tso mutex */ + OL_TX_MUTEX_TYPE tso_num_seg_mutex; + } tso_num_seg_pool; +#endif + +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) + struct { + enum ol_tx_peer_bal_state enabled; + qdf_spinlock_t mutex; + /* timer used to trigger more frames for bad peers */ + qdf_timer_t peer_bal_timer; + /*This is the time in ms of the peer balance timer period */ + u_int32_t peer_bal_period_ms; + /*This is the txq limit */ + u_int32_t peer_bal_txq_limit; + /*This is the state of the peer balance timer */ + enum ol_tx_peer_bal_timer_state peer_bal_timer_state; + /*This is the counter about active peers which are under + *tx flow control + */ + u_int32_t peer_num; + /*This is peer list which are under tx flow control */ + struct ol_tx_limit_peer_t limit_list[MAX_NO_PEERS_IN_LIMIT]; + /*This is threshold configurationl */ + struct tx_peer_threshold ctl_thresh[TXRX_IEEE11_MAX]; + } tx_peer_bal; +#endif /* CONFIG_Hl_SUPPORT && QCA_BAD_PEER_TX_FLOW_CL */ + + struct ol_tx_queue_group_t txq_grps[OL_TX_MAX_TXQ_GROUPS]; +#if defined(FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL) && \ + defined(FEATURE_HL_DBS_GROUP_CREDIT_SHARING) + bool limit_lend; + u16 min_reserve; +#endif +#ifdef DEBUG_HL_LOGGING + qdf_spinlock_t grp_stat_spinlock; + struct ol_tx_group_credit_stats_t grp_stats; +#endif + int tid_to_ac[OL_TX_NUM_TIDS + OL_TX_VDEV_NUM_QUEUES]; + uint8_t ocb_peer_valid; + struct ol_txrx_peer_t *ocb_peer; + tx_pause_callback pause_cb; + + void (*offld_flush_cb)(void *); + struct ol_txrx_peer_t *self_peer; + + /* dp debug fs */ + struct dentry *dpt_stats_log_dir; + enum qdf_dpt_debugfs_state state; + struct qdf_debugfs_fops dpt_debugfs_fops; + +#ifdef IPA_OFFLOAD + ipa_uc_op_cb_type ipa_uc_op_cb; + void *usr_ctxt; + struct ol_txrx_ipa_resources ipa_resource; +#endif /* IPA_UC_OFFLOAD */ + bool new_htt_msg_format; + uint8_t peer_id_unmap_ref_cnt; + bool enable_peer_unmap_conf_support; + bool enable_tx_compl_tsf64; + uint64_t last_host_time; + uint64_t last_tsf64_time; + + /* Current noise-floor reading for the pdev channel */ + int16_t chan_noise_floor; + uint32_t total_bundle_queue_length; +}; + +#define OL_TX_HL_DEL_ACK_HASH_SIZE 256 + +/** + * enum ol_tx_hl_packet_type - type for tcp packet + * @TCP_PKT_ACK: TCP ACK frame + * @TCP_PKT_NO_ACK: TCP frame, but not the ack + * @NO_TCP_PKT: Not the TCP frame + */ +enum ol_tx_hl_packet_type { + TCP_PKT_ACK, + TCP_PKT_NO_ACK, + NO_TCP_PKT +}; + +/** + * struct packet_info - tcp packet information + */ +struct packet_info { + /** @type: flag the packet type */ + enum ol_tx_hl_packet_type type; + /** @stream_id: stream identifier */ + uint16_t stream_id; + /** @ack_number: tcp ack number */ + uint32_t ack_number; + /** @dst_ip: destination ip address */ + uint32_t dst_ip; + /** @src_ip: source ip address */ + uint32_t src_ip; + /** @dst_port: destination port */ + uint16_t dst_port; + /** @src_port: source port */ + uint16_t src_port; +}; + +/** + * struct tcp_stream_node - tcp stream node + */ +struct tcp_stream_node { + /** @next: next tcp stream node */ + struct tcp_stream_node *next; + /** @no_of_ack_replaced: count for ack replaced frames */ + uint8_t no_of_ack_replaced; + /** @stream_id: stream identifier */ + uint16_t stream_id; + /** @dst_ip: destination ip address */ + uint32_t dst_ip; + /** @src_ip: source ip address */ + uint32_t src_ip; + /** @dst_port: destination port */ + uint16_t dst_port; + /** @src_port: source port */ + uint16_t src_port; + /** @ack_number: tcp ack number */ + uint32_t ack_number; + /** @head: point to the tcp ack frame */ + qdf_nbuf_t head; +}; + +/** + * struct tcp_del_ack_hash_node - hash node for tcp delayed ack + */ +struct tcp_del_ack_hash_node { + /** @hash_node_lock: spin lock */ + qdf_spinlock_t hash_node_lock; + /** @no_of_entries: number of entries */ + uint8_t no_of_entries; + /** @head: the head of the steam node list */ + struct tcp_stream_node *head; +}; + +struct ol_txrx_vdev_t { + struct ol_txrx_pdev_t *pdev; /* pdev - the physical device that is + * the parent of this virtual device + */ + uint8_t vdev_id; /* ID used to specify a particular vdev + * to the target + */ + void *osif_dev; + + void *ctrl_vdev; /* vdev objmgr handle */ + + union ol_txrx_align_mac_addr_t mac_addr; /* MAC address */ + /* tx paused - NO LONGER NEEDED? */ + TAILQ_ENTRY(ol_txrx_vdev_t) vdev_list_elem; /* node in the pdev's list + * of vdevs + */ + TAILQ_HEAD(peer_list_t, ol_txrx_peer_t) peer_list; + struct ol_txrx_peer_t *last_real_peer; /* last real peer created for + * this vdev (not "self" + * pseudo-peer) + */ + ol_txrx_rx_fp rx; /* receive function used by this vdev */ + ol_txrx_stats_rx_fp stats_rx; /* receive function used by this vdev */ + + struct { + uint32_t txack_success; + uint32_t txack_failed; + } txrx_stats; + + /* completion function used by this vdev*/ + ol_txrx_completion_fp tx_comp; + + struct { + /* + * If the vdev object couldn't be deleted immediately because + * it still had some peer objects left, remember that a delete + * was requested, so it can be deleted once all its peers have + * been deleted. + */ + int pending; + /* + * Store a function pointer and a context argument to provide a + * notification for when the vdev is deleted. + */ + ol_txrx_vdev_delete_cb callback; + void *context; + atomic_t detaching; + } delete; + + /* safe mode control to bypass the encrypt and decipher process */ + uint32_t safemode; + + /* rx filter related */ + uint32_t drop_unenc; + struct privacy_exemption privacy_filters[MAX_PRIVACY_FILTERS]; + uint32_t num_filters; + + enum wlan_op_mode opmode; + enum wlan_op_subtype subtype; + +#ifdef QCA_IBSS_SUPPORT + /* ibss mode related */ + int16_t ibss_peer_num; /* the number of active peers */ + int16_t ibss_peer_heart_beat_timer; /* for detecting peer departure */ +#endif + +#if defined(CONFIG_HL_SUPPORT) + struct ol_tx_frms_queue_t txqs[OL_TX_VDEV_NUM_QUEUES]; +#endif + + struct { + struct { + qdf_nbuf_t head; + qdf_nbuf_t tail; + int depth; + } txq; + uint32_t paused_reason; + qdf_spinlock_t mutex; + qdf_timer_t timer; + int max_q_depth; + bool is_q_paused; + bool is_q_timer_on; + uint32_t q_pause_cnt; + uint32_t q_unpause_cnt; + uint32_t q_overflow_cnt; + } ll_pause; + bool disable_intrabss_fwd; + qdf_atomic_t os_q_paused; + uint16_t tx_fl_lwm; + uint16_t tx_fl_hwm; + qdf_spinlock_t flow_control_lock; + ol_txrx_tx_flow_control_fp osif_flow_control_cb; + ol_txrx_tx_flow_control_is_pause_fp osif_flow_control_is_pause; + void *osif_fc_ctx; + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK + /** @driver_del_ack_enabled: true if tcp delayed ack enabled*/ + bool driver_del_ack_enabled; + /** @no_of_tcpack_replaced: number of tcp ack replaced */ + uint32_t no_of_tcpack_replaced; + /** @no_of_tcpack: number of tcp ack frames */ + uint32_t no_of_tcpack; + + /** @tcp_ack_hash: hash table for tcp delay ack running information */ + struct { + /** @node: tcp ack frame will be stored in this hash table */ + struct tcp_del_ack_hash_node node[OL_TX_HL_DEL_ACK_HASH_SIZE]; + /** @timer: timeout if no more tcp ack feeding */ + __qdf_hrtimer_data_t timer; + /** @is_timer_running: is timer running? */ + qdf_atomic_t is_timer_running; + /** @tcp_node_in_use_count: number of nodes in use */ + qdf_atomic_t tcp_node_in_use_count; + /** @tcp_del_ack_tq: bh to handle the tcp delayed ack */ + qdf_bh_t tcp_del_ack_tq; + /** @tcp_free_list: free list */ + struct tcp_stream_node *tcp_free_list; + /** @tcp_free_list_lock: spin lock */ + qdf_spinlock_t tcp_free_list_lock; + } tcp_ack_hash; +#endif + +#if defined(CONFIG_HL_SUPPORT) && defined(FEATURE_WLAN_TDLS) + union ol_txrx_align_mac_addr_t hl_tdls_ap_mac_addr; + bool hlTdlsFlag; +#endif + +#if defined(QCA_HL_NETDEV_FLOW_CONTROL) + qdf_atomic_t tx_desc_count; + int tx_desc_limit; + int queue_restart_th; + int queue_stop_th; + int prio_q_paused; +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + + uint16_t wait_on_peer_id; + union ol_txrx_align_mac_addr_t last_peer_mac_addr; + qdf_event_t wait_delete_comp; +#if defined(FEATURE_TSO) + struct { + int pool_elems; /* total number of elements in the pool */ + int alloc_cnt; /* number of allocated elements */ + uint32_t *freelist; /* free list of qdf_tso_seg_elem_t */ + } tso_pool_t; +#endif + + /* last channel change event received */ + struct { + bool is_valid; /* whether the rest of the members are valid */ + uint16_t mhz; + uint16_t band_center_freq1; + uint16_t band_center_freq2; + WLAN_PHY_MODE phy_mode; + } ocb_channel_event; + + /* Information about the schedules in the schedule */ + struct ol_txrx_ocb_chan_info *ocb_channel_info; + uint32_t ocb_channel_count; + +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 + struct ol_tx_flow_pool_t *pool; +#endif + /* intra bss forwarded tx and rx packets count */ + uint64_t fwd_tx_packets; + uint64_t fwd_rx_packets; + bool is_wisa_mode_enable; + uint8_t mac_id; + + uint64_t no_of_bundle_sent_after_threshold; + uint64_t no_of_bundle_sent_in_timer; + uint64_t no_of_pkt_not_added_in_queue; + bool bundling_required; + struct { + struct { + qdf_nbuf_t head; + qdf_nbuf_t tail; + int depth; + } txq; + qdf_spinlock_t mutex; + qdf_timer_t timer; + } bundle_queue; +}; + +struct ol_rx_reorder_array_elem_t { + qdf_nbuf_t head; + qdf_nbuf_t tail; +}; + +struct ol_rx_reorder_t { + uint8_t win_sz; + uint8_t win_sz_mask; + uint8_t num_mpdus; + struct ol_rx_reorder_array_elem_t *array; + /* base - single rx reorder element used for non-aggr cases */ + struct ol_rx_reorder_array_elem_t base; +#if defined(QCA_SUPPORT_OL_RX_REORDER_TIMEOUT) + struct ol_rx_reorder_timeout_list_elem_t timeout; +#endif + /* only used for defrag right now */ + TAILQ_ENTRY(ol_rx_reorder_t) defrag_waitlist_elem; + uint32_t defrag_timeout_ms; + /* get back to parent ol_txrx_peer_t when ol_rx_reorder_t is in a + * waitlist + */ + uint16_t tid; +}; + +enum { + txrx_sec_mcast = 0, + txrx_sec_ucast +}; + +typedef A_STATUS (*ol_tx_filter_func)(struct ol_txrx_msdu_info_t * + tx_msdu_info); + +#define OL_TXRX_PEER_SECURITY_MULTICAST 0 +#define OL_TXRX_PEER_SECURITY_UNICAST 1 +#define OL_TXRX_PEER_SECURITY_MAX 2 + + +/* Allow 6000 ms to receive peer unmap events after peer is deleted */ +#define OL_TXRX_PEER_UNMAP_TIMEOUT (6000) + +struct ol_txrx_cached_bufq_t { + /* cached_bufq is used to enqueue the pending RX frames from a peer + * before the peer is registered for data service. The list will be + * flushed to HDD once that station is registered. + */ + struct list_head cached_bufq; + /* mutual exclusion lock to access the cached_bufq queue */ + qdf_spinlock_t bufq_lock; + /* # entries in queue after which subsequent adds will be dropped */ + uint32_t thresh; + /* # entries in present in cached_bufq */ + uint32_t curr; + /* # max num of entries in the queue if bufq thresh was not in place */ + uint32_t high_water_mark; + /* # max num of entries in the queue if we did not drop packets */ + uint32_t qdepth_no_thresh; + /* # of packes (beyond threshold) dropped from cached_bufq */ + uint32_t dropped; +}; + +struct ol_txrx_peer_t { + struct ol_txrx_vdev_t *vdev; + + /* UMAC peer objmgr handle */ + struct cdp_ctrl_objmgr_peer *ctrl_peer; + + qdf_atomic_t ref_cnt; + qdf_atomic_t access_list[PEER_DEBUG_ID_MAX]; + qdf_atomic_t delete_in_progress; + qdf_atomic_t flush_in_progress; + + /* The peer state tracking is used for HL systems + * that don't support tx and rx filtering within the target. + * In such systems, the peer's state determines what kind of + * tx and rx filtering, if any, is done. + * This variable doesn't apply to LL systems, or to HL systems for + * which the target handles tx and rx filtering. However, it is + * simplest to declare and update this variable unconditionally, + * for all systems. + */ + enum ol_txrx_peer_state state; + qdf_spinlock_t peer_info_lock; + + /* Wrapper around the cached_bufq list */ + struct ol_txrx_cached_bufq_t bufq_info; + + ol_tx_filter_func tx_filter; + + /* peer ID(s) for this peer */ + uint16_t peer_ids[MAX_NUM_PEER_ID_PER_PEER]; +#ifdef QCA_SUPPORT_TXRX_LOCAL_PEER_ID + uint16_t local_id; +#endif + + union ol_txrx_align_mac_addr_t mac_addr; + + /* node in the vdev's list of peers */ + TAILQ_ENTRY(ol_txrx_peer_t) peer_list_elem; + /* node in the hash table bin's list of peers */ + TAILQ_ENTRY(ol_txrx_peer_t) hash_list_elem; + + /* + * per TID info - + * stored in separate arrays to avoid alignment padding mem overhead + */ + struct ol_rx_reorder_t tids_rx_reorder[OL_TXRX_NUM_EXT_TIDS]; + union htt_rx_pn_t tids_last_pn[OL_TXRX_NUM_EXT_TIDS]; + uint8_t tids_last_pn_valid[OL_TXRX_NUM_EXT_TIDS]; + uint8_t tids_rekey_flag[OL_TXRX_NUM_EXT_TIDS]; + uint16_t tids_next_rel_idx[OL_TXRX_NUM_EXT_TIDS]; + uint16_t tids_last_seq[OL_TXRX_NUM_EXT_TIDS]; + uint16_t tids_mcast_last_seq[OL_TXRX_NUM_EXT_TIDS]; + + struct { + enum htt_sec_type sec_type; + uint32_t michael_key[2]; /* relevant for TKIP */ + } security[2]; /* 0 -> multicast, 1 -> unicast */ + + /* + * rx proc function: this either is a copy of pdev's rx_opt_proc for + * regular rx processing, or has been redirected to a /dev/null discard + * function when peer deletion is in progress. + */ + void (*rx_opt_proc)(struct ol_txrx_vdev_t *vdev, + struct ol_txrx_peer_t *peer, + unsigned int tid, qdf_nbuf_t msdu_list); + +#if defined(CONFIG_HL_SUPPORT) + struct ol_tx_frms_queue_t txqs[OL_TX_NUM_TIDS]; +#endif + +#ifdef QCA_ENABLE_OL_TXRX_PEER_STATS + ol_txrx_peer_stats_t stats; +#endif + int16_t rssi_dbm; + + /* NAWDS Flag and Bss Peer bit */ + uint16_t nawds_enabled:1, bss_peer:1, valid:1; + + /* QoS info */ + uint8_t qos_capable; + /* U-APSD tid mask */ + uint8_t uapsd_mask; + /*flag indicating key installed */ + uint8_t keyinstalled; + + /* Bit to indicate if PN check is done in fw */ + qdf_atomic_t fw_pn_check; + +#ifdef WLAN_FEATURE_11W + /* PN counter for Robust Management Frames */ + uint64_t last_rmf_pn; + uint32_t rmf_pn_replays; + uint8_t last_rmf_pn_valid; +#endif + + /* Properties of the last received PPDU */ + int16_t last_pkt_rssi_cmb; + int16_t last_pkt_rssi[4]; + uint8_t last_pkt_legacy_rate; + uint8_t last_pkt_legacy_rate_sel; + uint32_t last_pkt_timestamp_microsec; + uint8_t last_pkt_timestamp_submicrosec; + uint32_t last_pkt_tsf; + uint8_t last_pkt_tid; + uint16_t last_pkt_center_freq; +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) + u_int16_t tx_limit; + u_int16_t tx_limit_flag; + u_int16_t tx_pause_flag; +#endif + qdf_time_t last_assoc_rcvd; + qdf_time_t last_disassoc_rcvd; + qdf_time_t last_deauth_rcvd; + qdf_atomic_t fw_create_pending; + qdf_timer_t peer_unmap_timer; + bool is_tdls_peer; /* Mark peer as tdls peer */ + bool tdls_offchan_enabled; /* TDLS OffChan operation in use */ +}; + +struct ol_rx_remote_data { + qdf_nbuf_t msdu; + uint8_t mac_id; +}; + +struct ol_fw_data { + void *data; + uint32_t len; +}; + +#define INVALID_REORDER_INDEX 0xFFFF + +#define SPS_DESC_SIZE 8 + +#endif /* _OL_TXRX_TYPES__H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/wdi_event.h b/drivers/staging/qcacld-3.0/core/dp/txrx/wdi_event.h new file mode 100644 index 0000000000000000000000000000000000000000..fcf771bbce562b49813148a97924d4e0ec0aea17 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/wdi_event.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WDI_EVENT_H_ +#define _WDI_EVENT_H_ + +#include "athdefs.h" +#include "qdf_nbuf.h" +#include + +#define WDI_NO_VAL (-1) + +struct wdi_event_rx_peer_invalid_msg { + qdf_nbuf_t msdu; + struct ieee80211_frame *wh; + uint8_t vdev_id; +}; + +#define WDI_EVENT_NOTIFY_BASE 0x200 +enum WDI_EVENT_NOTIFY { + WDI_EVENT_SUB_DEALLOCATE = WDI_EVENT_NOTIFY_BASE, + /* End of new notification types */ + + WDI_EVENT_NOTIFY_LAST +}; + +/* Opaque event callback */ +typedef void (*wdi_event_cb)(void *pdev, enum WDI_EVENT event, void *data, + u_int16_t peer_id, uint32_t status); + +/* Opaque event notify */ +typedef void (*wdi_event_notify)(enum WDI_EVENT_NOTIFY notify, + enum WDI_EVENT event); + +/** + * @typedef wdi_event_subscribe + * @brief Used by consumers to subscribe to WDI event notifications. + * @details + * The event_subscribe struct includes pointers to other event_subscribe + * objects. These pointers are simply to simplify the management of + * lists of event subscribers. These pointers are set during the + * event_sub() function, and shall not be modified except by the + * WDI event management SW, until after the object's event subscription + * is canceled by calling event_unsub(). + */ + +typedef struct wdi_event_subscribe_t { + /* subscriber event callback structure head */ + wdi_event_cb callback; + /* subscriber object that processes the event callback */ + void *context; + struct { + /* + * private - the event subscriber SW shall not use this struct + */ + struct wdi_event_subscribe_t *next; + struct wdi_event_subscribe_t *prev; + } priv; +} wdi_event_subscribe; + +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/wdi_event_api.h b/drivers/staging/qcacld-3.0/core/dp/txrx/wdi_event_api.h new file mode 100644 index 0000000000000000000000000000000000000000..11358b25e0520a016923e9841e8f4f7d32104f9b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx/wdi_event_api.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2012-2014, 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WDI_EVENT_API_H_ +#define _WDI_EVENT_API_H_ + +#include "wdi_event.h" +#include +#include +struct ol_txrx_pdev_t; + +#ifdef WDI_EVENT_ENABLE +/** + * @brief Subscribe to a specified WDI event. + * @details + * This function adds the provided wdi_event_subscribe object to a list of + * subscribers for the specified WDI event. + * When the event in question happens, each subscriber for the event will + * have their callback function invoked. + * The order in which callback functions from multiple subscribers are + * invoked is unspecified. + * + * @param soc_hdl - datapath soc handle + * @param pdev_id - physical device instance id + * @param event_cb_sub - the callback and context for the event subscriber + * @param event - which event's notifications are being subscribed to + * @return error code, or 0 for success + */ +int wdi_event_sub(struct cdp_soc_t *soc, uint8_t pdev_id, + wdi_event_subscribe *event_cb_sub, uint32_t event); + +/** + * @brief Unsubscribe from a specified WDI event. + * @details + * This function removes the provided event subscription object from the + * list of subscribers for its event. + * This function shall only be called if there was a successful prior call + * to event_sub() on the same wdi_event_subscribe object. + * + * @param soc_hdl - datapath soc handle + * @param pdev_id - physical device instance id + * @param event_cb_sub - the event subscription object + * @param event - which event is being unsubscribed + * @return error code, or 0 for success + */ +int wdi_event_unsub(struct cdp_soc_t *soc, uint8_t pdev_id, + wdi_event_subscribe *event_cb_sub, uint32_t event); + + +void wdi_event_handler(enum WDI_EVENT event, + uint8_t pdev_id, void *data); +A_STATUS wdi_event_attach(struct ol_txrx_pdev_t *txrx_pdev); +A_STATUS wdi_event_detach(struct ol_txrx_pdev_t *txrx_pdev); + +#else + +static inline void wdi_event_handler(enum WDI_EVENT event, + uint8_t pdev_id, void *data) +{ +} + +static inline A_STATUS wdi_event_attach(struct ol_txrx_pdev_t *txrx_pdev) +{ + return A_OK; +} + +static inline A_STATUS wdi_event_detach(struct ol_txrx_pdev_t *txrx_pdev) +{ + return A_OK; +} + +static inline int wdi_event_sub(struct cdp_soc_t *soc, uint8_t pdev_id, + wdi_event_subscribe *event_cb_sub, + uint32_t event) +{ + return 0; +} + +static inline int wdi_event_unsub(struct cdp_soc_t *soc, uint8_t pdev_id, + wdi_event_subscribe *event_cb_sub, + uint32_t event) +{ + return 0; +} +#endif /* WDI_EVENT_ENABLE */ + +#endif /* _WDI_EVENT_API_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_fisa_rx.c b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_fisa_rx.c new file mode 100644 index 0000000000000000000000000000000000000000..a939f0ab37323c989ef8c5917332d67f6fab5aac --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_fisa_rx.c @@ -0,0 +1,1385 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "hal_rx_flow.h" +#include "dp_htt.h" +#include "dp_internal.h" +#include +#include + +static void dp_rx_fisa_flush_flow_wrap(struct dp_fisa_rx_sw_ft *sw_ft); + +/** REO will push frame into REO2FW RING */ +#define REO_DESTINATION_FW 6 + +#if defined(FISA_DEBUG_ENABLE) +/** + * hex_dump_skb_data() - Helper function to dump skb while debugging + * @nbuf: Nbuf to be dumped + * @dump: dump enable/disable dumping + * + * Return: NONE + */ +static void hex_dump_skb_data(qdf_nbuf_t nbuf, bool dump) +{ + qdf_nbuf_t next_nbuf; + int i = 0; + + if (!dump) + return; + + if (!nbuf) + return; + + dp_fisa_debug("%ps: skb: %pK skb->next:%pK frag_list %pK skb->data:%pK len %d data_len %d", + (void *)_RET_IP_, nbuf, qdf_nbuf_next(nbuf), + skb_shinfo(nbuf)->frag_list, qdf_nbuf_data(nbuf), + nbuf->len, nbuf->data_len); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, nbuf->data, + 64); + + next_nbuf = skb_shinfo(nbuf)->frag_list; + while (next_nbuf) { + dp_fisa_debug("%d nbuf:%pK nbuf->next:%pK nbuf->data:%pK len %d", + i, next_nbuf, qdf_nbuf_next(next_nbuf), + qdf_nbuf_data(next_nbuf), + qdf_nbuf_len(next_nbuf)); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + qdf_nbuf_data(next_nbuf), 64); + next_nbuf = qdf_nbuf_next(next_nbuf); + i++; + } +} + +/** + * dump_tlvs() - Helper function to dump TLVs of msdu + * @hal_soc_hdl: Handle to TLV functions + * @buf: Pointer to TLV header + * @dbg_level: level control output of TLV dump + * + * Return: NONE + */ +static void dump_tlvs(hal_soc_handle_t hal_soc_hdl, uint8_t *buf, + uint8_t dbg_level) +{ + uint32_t fisa_aggr_count, fisa_timeout, cumulat_l4_csum, cumulat_ip_len; + int flow_aggr_cont; + + hal_rx_dump_pkt_tlvs(hal_soc_hdl, buf, dbg_level); + + flow_aggr_cont = hal_rx_get_fisa_flow_agg_continuation(hal_soc_hdl, + buf); + fisa_aggr_count = hal_rx_get_fisa_flow_agg_count(hal_soc_hdl, buf); + fisa_timeout = hal_rx_get_fisa_timeout(hal_soc_hdl, buf); + cumulat_l4_csum = hal_rx_get_fisa_cumulative_l4_checksum(hal_soc_hdl, + buf); + cumulat_ip_len = hal_rx_get_fisa_cumulative_ip_length(hal_soc_hdl, buf); + + dp_fisa_debug("flow_aggr_cont %d, fisa_timeout %d, fisa_aggr_count %d, cumulat_l4_csum %d, cumulat_ip_len %d", + flow_aggr_cont, fisa_timeout, fisa_aggr_count, cumulat_l4_csum, + cumulat_ip_len); +} +#else +static void hex_dump_skb_data(qdf_nbuf_t nbuf, bool dump) +{ +} + +static void dump_tlvs(hal_soc_handle_t hal_soc_hdl, uint8_t *buf, + uint8_t dbg_level) +{ +} +#endif + +/** + * nbuf_skip_rx_pkt_tlv() - Function to skip the TLVs and mac header from msdu + * @hal_soc_hdl: Handle to hal_soc to get the TLV info + * @nbuf: msdu for which TLVs has to be skipped + * + * Return: None + */ +static void nbuf_skip_rx_pkt_tlv(hal_soc_handle_t hal_soc_hdl, qdf_nbuf_t nbuf) +{ + uint8_t *rx_tlv_hdr; + uint32_t l2_hdr_offset; + + rx_tlv_hdr = qdf_nbuf_data(nbuf); + l2_hdr_offset = hal_rx_msdu_end_l3_hdr_padding_get(hal_soc_hdl, + rx_tlv_hdr); + qdf_nbuf_pull_head(nbuf, RX_PKT_TLVS_LEN + l2_hdr_offset); +} + +/** + * print_flow_tuple() - Debug function to dump flow tuple + * @flow_tuple: flow tuple containing tuple info + * + * Return: NONE + */ +static void print_flow_tuple(struct cdp_rx_flow_tuple_info *flow_tuple) +{ + dp_info("dest_ip_127_96 0x%x", flow_tuple->dest_ip_127_96); + dp_info("dest_ip_95_64 0x%x", flow_tuple->dest_ip_95_64); + dp_info("dest_ip_63_32 0x%x", flow_tuple->dest_ip_63_32); + dp_info("dest_ip_31_0 0x%x", flow_tuple->dest_ip_31_0); + dp_info("src_ip_127_96 0x%x", flow_tuple->src_ip_127_96); + dp_info("src_ip_95_64 0x%x", flow_tuple->src_ip_95_64); + dp_info("src_ip_63_32 0x%x", flow_tuple->src_ip_63_32); + dp_info("src_ip_31_0 0x%x", flow_tuple->src_ip_31_0); + dp_info("dest_port 0x%x", flow_tuple->dest_port); + dp_info("src_port 0x%x", flow_tuple->src_port); + dp_info("l4_protocol 0x%x", flow_tuple->l4_protocol); +} + +/** + * get_flow_tuple_from_nbuf() - Get the flow tuple from msdu + * @hal_soc_hdl: Handle to hal soc + * @flow_tuple_info: return argument where the flow is populated + * @nbuf: msdu from which flow tuple is extracted. + * @rx_tlv_hdr: Pointer to msdu TLVs + * + * Return: None + */ +static void +get_flow_tuple_from_nbuf(hal_soc_handle_t hal_soc_hdl, + struct cdp_rx_flow_tuple_info *flow_tuple_info, + qdf_nbuf_t nbuf, uint8_t *rx_tlv_hdr) +{ + struct iphdr *iph; + struct tcphdr *tcph; + uint32_t ip_hdr_offset = HAL_RX_TLV_GET_IP_OFFSET(rx_tlv_hdr); + uint32_t tcp_hdr_offset = HAL_RX_TLV_GET_TCP_OFFSET(rx_tlv_hdr); + uint32_t l2_hdr_offset = hal_rx_msdu_end_l3_hdr_padding_get(hal_soc_hdl, + rx_tlv_hdr); + + flow_tuple_info->tuple_populated = true; + + qdf_nbuf_pull_head(nbuf, RX_PKT_TLVS_LEN + l2_hdr_offset); + + iph = (struct iphdr *)(qdf_nbuf_data(nbuf) + ip_hdr_offset); + tcph = (struct tcphdr *)(qdf_nbuf_data(nbuf) + ip_hdr_offset + + tcp_hdr_offset); + + flow_tuple_info->dest_ip_31_0 = qdf_ntohl(iph->daddr); + flow_tuple_info->dest_ip_63_32 = 0; + flow_tuple_info->dest_ip_95_64 = 0; + flow_tuple_info->dest_ip_127_96 = + HAL_IP_DA_SA_PREFIX_IPV4_COMPATIBLE_IPV6; + + flow_tuple_info->src_ip_31_0 = qdf_ntohl(iph->saddr); + flow_tuple_info->src_ip_63_32 = 0; + flow_tuple_info->src_ip_95_64 = 0; + flow_tuple_info->src_ip_127_96 = + HAL_IP_DA_SA_PREFIX_IPV4_COMPATIBLE_IPV6; + + flow_tuple_info->dest_port = qdf_ntohs(tcph->dest); + flow_tuple_info->src_port = qdf_ntohs(tcph->source); + flow_tuple_info->l4_protocol = iph->protocol; + dp_fisa_debug("l4_protocol %d", flow_tuple_info->l4_protocol); + + qdf_nbuf_push_head(nbuf, RX_PKT_TLVS_LEN + l2_hdr_offset); + + dp_fisa_debug("head_skb: %pK head_skb->next:%pK head_skb->data:%pK len %d data_len %d", + nbuf, qdf_nbuf_next(nbuf), qdf_nbuf_data(nbuf), nbuf->len, + nbuf->data_len); +} + +/** + * dp_rx_fisa_setup_hw_fse() - Populate flow so as to update DDR flow table + * @fisa_hdl: Handle fisa context + * @hashed_flow_idx: Index to flow table + * @rx_flow_info: tuple to be populated in flow table + * @flow_steer_info: REO index to which flow to be steered + * + * Return: Pointer to DDR flow table entry + */ +static void * +dp_rx_fisa_setup_hw_fse(struct dp_rx_fst *fisa_hdl, + uint32_t hashed_flow_idx, + struct cdp_rx_flow_tuple_info *rx_flow_info, + uint32_t flow_steer_info) +{ + struct hal_rx_flow flow; + void *hw_fse; + + flow.reo_destination_indication = flow_steer_info; + flow.fse_metadata = 0xDEADBEEF; + flow.tuple_info.dest_ip_127_96 = rx_flow_info->dest_ip_127_96; + flow.tuple_info.dest_ip_95_64 = rx_flow_info->dest_ip_95_64; + flow.tuple_info.dest_ip_63_32 = rx_flow_info->dest_ip_63_32; + flow.tuple_info.dest_ip_31_0 = rx_flow_info->dest_ip_31_0; + flow.tuple_info.src_ip_127_96 = rx_flow_info->src_ip_127_96; + flow.tuple_info.src_ip_95_64 = rx_flow_info->src_ip_95_64; + flow.tuple_info.src_ip_63_32 = rx_flow_info->src_ip_63_32; + flow.tuple_info.src_ip_31_0 = rx_flow_info->src_ip_31_0; + flow.tuple_info.dest_port = rx_flow_info->dest_port; + flow.tuple_info.src_port = rx_flow_info->src_port; + flow.tuple_info.l4_protocol = rx_flow_info->l4_protocol; + flow.reo_destination_handler = HAL_RX_FSE_REO_DEST_FT; + hw_fse = hal_rx_flow_setup_fse(fisa_hdl->hal_rx_fst, hashed_flow_idx, + &flow); + + return hw_fse; +} + +/** + * dp_rx_fisa_update_sw_ft_entry() - Helper function to update few SW FT entry + * @sw_ft_entry: Pointer to softerware flow tabel entry + * @flow_hash: flow_hash for the flow + * @vdev: Saving dp_vdev in FT later used in the flushing the flow + * @flow_id: Flow ID of the flow + * + * Return: NONE + */ +static void dp_rx_fisa_update_sw_ft_entry(struct dp_fisa_rx_sw_ft *sw_ft_entry, + uint32_t flow_hash, + struct dp_vdev *vdev, + struct dp_soc *soc_hdl, + uint32_t flow_id) +{ + sw_ft_entry->flow_hash = flow_hash; + sw_ft_entry->flow_id = flow_id; + sw_ft_entry->vdev = vdev; + sw_ft_entry->soc_hdl = soc_hdl; +} + +/** + * is_same_flow() - Function to compare flow tuple to decide if they match + * @tuple1: flow tuple 1 + * @tuple2: flow tuple 2 + * + * Return: true if they match, false if they differ + */ +static bool is_same_flow(struct cdp_rx_flow_tuple_info *tuple1, + struct cdp_rx_flow_tuple_info *tuple2) +{ + if ((tuple1->src_port ^ tuple2->src_port) | + (tuple1->dest_port ^ tuple2->dest_port) | + (tuple1->src_ip_31_0 ^ tuple2->src_ip_31_0) | + (tuple1->src_ip_63_32 ^ tuple2->src_ip_63_32) | + (tuple1->src_ip_95_64 ^ tuple2->src_ip_95_64) | + (tuple1->src_ip_127_96 ^ tuple2->src_ip_127_96) | + (tuple1->dest_ip_31_0 ^ tuple2->dest_ip_31_0) | + /* DST IP check not required? */ + (tuple1->dest_ip_63_32 ^ tuple2->dest_ip_63_32) | + (tuple1->dest_ip_95_64 ^ tuple2->dest_ip_95_64) | + (tuple1->dest_ip_127_96 ^ tuple2->dest_ip_127_96) | + (tuple1->l4_protocol ^ tuple2->l4_protocol)) + return false; + else + return true; +} + +/** + * dp_rx_fisa_add_ft_entry() - Add new flow to HW and SW FT if it is not added + * @fisa_hdl: handle to FISA context + * @flow_idx_hash: Hashed flow index + * @nbuf: nbuf belonging to new flow + * @vdev: Handle DP vdev to save in SW flow table + * @rx_tlv_hdr: Pointer to TLV header + * + * Return: pointer to sw FT entry on success, NULL otherwise + */ +static struct dp_fisa_rx_sw_ft * +dp_rx_fisa_add_ft_entry(struct dp_rx_fst *fisa_hdl, + uint32_t flow_idx_hash, + qdf_nbuf_t nbuf, struct dp_vdev *vdev, + uint8_t *rx_tlv_hdr, + uint32_t reo_dest_indication) +{ + struct dp_fisa_rx_sw_ft *sw_ft_entry; + uint32_t flow_hash; + uint32_t hashed_flow_idx; + uint32_t skid_count = 0, max_skid_length; + struct cdp_rx_flow_tuple_info rx_flow_tuple_info; + bool is_fst_updated = false; + bool is_flow_tcp, is_flow_udp, is_flow_ipv6; + hal_soc_handle_t hal_soc_hdl = fisa_hdl->soc_hdl->hal_soc; + uint32_t reo_id = QDF_NBUF_CB_RX_CTX_ID(nbuf); + + is_flow_tcp = HAL_RX_TLV_GET_TCP_PROTO(rx_tlv_hdr); + is_flow_udp = HAL_RX_TLV_GET_UDP_PROTO(rx_tlv_hdr); + is_flow_ipv6 = HAL_RX_TLV_GET_IPV6(rx_tlv_hdr); + + if (is_flow_ipv6 || !(is_flow_tcp || is_flow_udp)) { + dp_fisa_debug("Not UDP or TCP IPV4 flow"); + return NULL; + } + + /* Get the hash from TLV + * FSE FT Toeplitz hash is same Common parser hash available in TLV + * common parser toeplitz hash is same as FSE toeplitz hash as + * toeplitz key is same. + */ + rx_flow_tuple_info.tuple_populated = false; + flow_hash = flow_idx_hash; + hashed_flow_idx = flow_hash & fisa_hdl->hash_mask; + max_skid_length = fisa_hdl->max_skid_length; + + dp_fisa_debug("flow_hash 0x%x hashed_flow_idx 0x%x", flow_hash, + hashed_flow_idx); + dp_fisa_debug("max_skid_length 0x%x", max_skid_length); + qdf_spin_lock_bh(&fisa_hdl->dp_rx_fst_lock); + do { + sw_ft_entry = &(((struct dp_fisa_rx_sw_ft *) + fisa_hdl->base)[hashed_flow_idx]); + if (!sw_ft_entry->is_populated) { + /* Add SW FT entry */ + dp_rx_fisa_update_sw_ft_entry(sw_ft_entry, + flow_hash, vdev, + fisa_hdl->soc_hdl, + hashed_flow_idx); + if (!rx_flow_tuple_info.tuple_populated) + get_flow_tuple_from_nbuf(hal_soc_hdl, + &rx_flow_tuple_info, + nbuf, rx_tlv_hdr); + + /* Add HW FT entry */ + sw_ft_entry->hw_fse = + dp_rx_fisa_setup_hw_fse(fisa_hdl, + hashed_flow_idx, + &rx_flow_tuple_info, + reo_dest_indication); + sw_ft_entry->is_populated = true; + sw_ft_entry->napi_id = reo_id; + sw_ft_entry->reo_dest_indication = reo_dest_indication; + qdf_mem_copy(&sw_ft_entry->rx_flow_tuple_info, + &rx_flow_tuple_info, + sizeof(struct cdp_rx_flow_tuple_info)); + + sw_ft_entry->is_flow_tcp = is_flow_tcp; + sw_ft_entry->is_flow_udp = is_flow_udp; + + is_fst_updated = true; + fisa_hdl->add_flow_count++; + break; + } + /* else */ + if (!rx_flow_tuple_info.tuple_populated) + get_flow_tuple_from_nbuf(hal_soc_hdl, + &rx_flow_tuple_info, + nbuf, rx_tlv_hdr); + + if (is_same_flow(&sw_ft_entry->rx_flow_tuple_info, + &rx_flow_tuple_info)) { + sw_ft_entry->vdev = vdev; + dp_fisa_debug("It is same flow fse entry idx %d", + hashed_flow_idx); + /* Incoming flow tuple matches with existing + * entry. This is subsequent skbs of the same + * flow. Earlier entry made is not reflected + * yet in FSE cache + */ + break; + } + /* else */ + /* hash collision move to the next FT entry */ + dp_fisa_debug("Hash collision %d", fisa_hdl->hash_collision_cnt); + fisa_hdl->hash_collision_cnt++; +#ifdef NOT_YET /* assist Flow eviction algorithm */ + /* uint32_t lru_ft_entry_time = 0xffffffff, lru_ft_entry_idx = 0; */ + if (fisa_hdl->hw_ft_entry->timestamp < lru_ft_entry_time) { + lru_ft_entry_time = fisa_hdl->hw_ft_entry->timestamp; + lru_ft_entry_idx = hashed_flow_idx; + } +#endif + skid_count++; + hashed_flow_idx++; + hashed_flow_idx &= fisa_hdl->hash_mask; + } while (skid_count <= max_skid_length); + + /* + * fisa_hdl->flow_eviction_cnt++; + * if (skid_count > max_skid_length) + * Remove LRU flow from HW FT + * Remove LRU flow from SW FT + */ + qdf_spin_unlock_bh(&fisa_hdl->dp_rx_fst_lock); + + if (skid_count > max_skid_length) { + dp_fisa_debug("Max skid length reached flow cannot be added, evict exiting flow"); + return NULL; + } + + /** + * Send HTT cache invalidation command to firmware to + * reflect the flow update + */ + if (is_fst_updated && + (qdf_atomic_inc_return(&fisa_hdl->fse_cache_flush_posted) == 1)) { + /* return 1 after increment implies FSE cache flush message + * already posted. so start restart the timer + */ + qdf_timer_start(&fisa_hdl->fse_cache_flush_timer, + FSE_CACHE_FLUSH_TIME_OUT); + + } + dp_fisa_debug("sw_ft_entry %pK", sw_ft_entry); + return sw_ft_entry; +} + +/** + * is_flow_idx_valid() - Function to decide if flow_idx TLV is valid + * @flow_invalid: flow invalid TLV value + * @flow_timeout: flow timeout TLV value, set when FSE timedout flow search + * + * Return: True if flow_idx value is valid + */ +static bool is_flow_idx_valid(bool flow_invalid, bool flow_timeout) +{ + if (!flow_invalid && !flow_timeout) + return true; + else + return false; +} + +/** + * dp_rx_get_fisa_flow() - Get FT entry corresponding to incoming nbuf + * @fisa_hdl: handle to FISA context + * @vdev: handle to DP vdev + * @nbuf: incoming msdu + * + * Return: handle SW FT entry for nbuf flow + */ +static struct dp_fisa_rx_sw_ft * +dp_rx_get_fisa_flow(struct dp_rx_fst *fisa_hdl, struct dp_vdev *vdev, + qdf_nbuf_t nbuf) +{ + uint8_t *rx_tlv_hdr; + uint32_t flow_idx; + uint32_t reo_destination_indication; + bool flow_invalid, flow_timeout, flow_idx_valid; + struct dp_fisa_rx_sw_ft *sw_ft_entry = NULL; + struct dp_fisa_rx_sw_ft *sw_ft_base = (struct dp_fisa_rx_sw_ft *) + fisa_hdl->base; + hal_soc_handle_t hal_soc_hdl = fisa_hdl->soc_hdl->hal_soc; + + if (QDF_NBUF_CB_RX_TCP_PROTO(nbuf)) + return sw_ft_entry; + + rx_tlv_hdr = qdf_nbuf_data(nbuf); + hal_rx_msdu_get_reo_destination_indication(hal_soc_hdl, rx_tlv_hdr, + &reo_destination_indication); + /* + * Compare reo_destination_indication between reo ring descriptor + * and rx_pkt_tlvs, if they are different, then likely these kind + * of frames re-injected by FW or touched by other module already, + * skip FISA to avoid REO2SW ring mismatch issue for same flow. + */ + if (reo_destination_indication != qdf_nbuf_get_rx_reo_dest_ind(nbuf)) + return sw_ft_entry; + + hal_rx_msdu_get_flow_params(hal_soc_hdl, rx_tlv_hdr, &flow_invalid, + &flow_timeout, &flow_idx); + + dp_fisa_debug("nbuf %pK fl_idx %d fl_inv %d fl_timeout %d", + nbuf, flow_idx, flow_invalid, flow_timeout); + + flow_idx_valid = is_flow_idx_valid(flow_invalid, flow_timeout); + if (flow_idx_valid) { + /* If flow index is invalid, fail to get flow */ + if (qdf_unlikely(flow_idx >= fisa_hdl->max_entries)) { + dp_info("flow_idx is invalid 0x%x", flow_idx); + hal_rx_dump_pkt_tlvs(hal_soc_hdl, rx_tlv_hdr, + QDF_TRACE_LEVEL_INFO_HIGH); + DP_STATS_INC(fisa_hdl, invalid_flow_index, 1); + return NULL; + } + sw_ft_entry = &sw_ft_base[flow_idx]; + sw_ft_entry->vdev = vdev; + + return sw_ft_entry; + } + + /* else new flow, add entry to FT */ + sw_ft_entry = dp_rx_fisa_add_ft_entry(fisa_hdl, flow_idx, nbuf, vdev, + rx_tlv_hdr, + reo_destination_indication); + + return sw_ft_entry; +} + +#ifdef NOT_YET +/** + * dp_rx_fisa_aggr_tcp() - Aggregate incoming to TCP nbuf + * @fisa_flow: Handle to SW flow entry, which holds the aggregated nbuf + * @nbuf: Incoming nbuf + * + * Return: FISA_AGGR_DONE on successful aggregation + */ +static enum fisa_aggr_ret +dp_rx_fisa_aggr_tcp(struct dp_fisa_rx_sw_ft *fisa_flow, qdf_nbuf_t nbuf) +{ + qdf_nbuf_t head_skb = fisa_flow->head_skb; + struct iphdr *iph; + uint32_t tcp_data_len; + + fisa_flow->bytes_aggregated += qdf_nbuf_len(nbuf); + if (!head_skb) { + /* First nbuf for the flow */ + dp_fisa_debug("first head skb"); + fisa_flow->head_skb = nbuf; + return FISA_AGGR_DONE; + } + + tcp_data_len = (ntohs(iph->tot_len) - sizeof(struct iphdr) - + sizeof(struct tcphdr)); + qdf_nbuf_pull_head(nbuf, (nbuf->len - tcp_data_len)); + + if (qdf_nbuf_get_ext_list(head_skb)) { + /* this is 3rd skb after head skb, 2nd skb */ + fisa_flow->last_skb->next = nbuf; + } else { + /* 1st skb after head skb */ + qdf_nbuf_append_ext_list(head_skb, nbuf, + fisa_flow->cumulative_ip_length); + qdf_nbuf_set_is_frag(head, 1); + } + + fisa_flow->last_skb = nbuf; + fisa_flow->aggr_count++; + + /* move it to while flushing the flow, that is update before flushing */ + return FISA_AGGR_DONE; +} +#else +static enum fisa_aggr_ret +dp_rx_fisa_aggr_tcp(struct dp_rx_fst *fisa_hdl, + struct dp_fisa_rx_sw_ft *fisa_flow, qdf_nbuf_t nbuf) +{ + return FISA_AGGR_DONE; +} +#endif + +/** + * get_transport_payload_offset() - Get offset to payload + * @fisa_hdl: Handle to FISA context + * @rx_tlv_hdr: TLV hdr pointer + * + * Return: Offset value to transport payload + */ +static int get_transport_payload_offset(struct dp_rx_fst *fisa_hdl, + uint8_t *rx_tlv_hdr) +{ + uint32_t eth_hdr_len = HAL_RX_TLV_GET_IP_OFFSET(rx_tlv_hdr); + uint32_t ip_hdr_len = HAL_RX_TLV_GET_TCP_OFFSET(rx_tlv_hdr); + + /* ETHERNET_HDR_LEN + ip_hdr_len + UDP/TCP; */ + return (eth_hdr_len + ip_hdr_len + sizeof(struct udphdr)); +} + +/** + * get_transport_header_offset() - Get transport header offset + * @fisa_flow: Handle to FISA sw flow entry + * @rx_tlv_hdr: TLV hdr pointer + * + * Return: Offset value to transport header + */ +static int get_transport_header_offset(struct dp_fisa_rx_sw_ft *fisa_flow, + uint8_t *rx_tlv_hdr) + +{ + uint32_t eth_hdr_len = HAL_RX_TLV_GET_IP_OFFSET(rx_tlv_hdr); + uint32_t ip_hdr_len = HAL_RX_TLV_GET_TCP_OFFSET(rx_tlv_hdr); + + /* ETHERNET_HDR_LEN + ip_hdr_len */ + return (eth_hdr_len + ip_hdr_len); +} + +/** + * dp_rx_fisa_aggr_udp() - Aggregate incoming to UDP nbuf + * @fisa_flow: Handle to SW flow entry, which holds the aggregated nbuf + * @nbuf: Incoming nbuf + * + * Return: FISA_AGGR_DONE on successful aggregation + */ +static enum fisa_aggr_ret +dp_rx_fisa_aggr_udp(struct dp_rx_fst *fisa_hdl, + struct dp_fisa_rx_sw_ft *fisa_flow, qdf_nbuf_t nbuf) +{ + qdf_nbuf_t head_skb = fisa_flow->head_skb; + uint8_t *rx_tlv_hdr = qdf_nbuf_data(nbuf); + uint32_t l2_hdr_offset = + hal_rx_msdu_end_l3_hdr_padding_get(fisa_hdl->soc_hdl->hal_soc, + rx_tlv_hdr); + struct udphdr *udp_hdr; + uint32_t transport_payload_offset; + + qdf_nbuf_pull_head(nbuf, RX_PKT_TLVS_LEN + l2_hdr_offset); + + udp_hdr = (struct udphdr *)(qdf_nbuf_data(nbuf) + + get_transport_header_offset(fisa_flow, rx_tlv_hdr)); + + /** + * Incoming nbuf is of size greater than ongoing aggregation + * then flush the aggregate and start new aggregation for nbuf + */ + if (head_skb && + (qdf_ntohs(udp_hdr->len) > + qdf_ntohs(fisa_flow->head_skb_udp_hdr->len))) { + /* current msdu should not take into account for flushing */ + fisa_flow->adjusted_cumulative_ip_length -= + (qdf_ntohs(udp_hdr->len) - sizeof(struct udphdr)); + fisa_flow->cur_aggr--; + dp_rx_fisa_flush_flow_wrap(fisa_flow); + /* napi_flush_cumulative_ip_length not include current msdu */ + fisa_flow->napi_flush_cumulative_ip_length -= + qdf_ntohs(udp_hdr->len); + head_skb = NULL; + } + + if (!head_skb) { + dp_fisa_debug("first head skb nbuf %pK", nbuf); + /* First nbuf for the flow */ + fisa_flow->head_skb = nbuf; + fisa_flow->head_skb_udp_hdr = udp_hdr; + fisa_flow->cur_aggr_gso_size = qdf_ntohs(udp_hdr->len) - + sizeof(struct udphdr); + fisa_flow->adjusted_cumulative_ip_length = + qdf_ntohs(udp_hdr->len); + fisa_flow->head_skb_ip_hdr_offset = + HAL_RX_TLV_GET_IP_OFFSET(rx_tlv_hdr); + fisa_flow->head_skb_l4_hdr_offset = + HAL_RX_TLV_GET_TCP_OFFSET(rx_tlv_hdr); + + return FISA_AGGR_DONE; + } + + transport_payload_offset = + get_transport_payload_offset(fisa_hdl, rx_tlv_hdr); + + hex_dump_skb_data(nbuf, false); + qdf_nbuf_pull_head(nbuf, transport_payload_offset); + hex_dump_skb_data(nbuf, false); + + fisa_flow->bytes_aggregated += qdf_nbuf_len(nbuf); + + if (qdf_nbuf_get_ext_list(head_skb)) { + /* + * This is 3rd skb for flow. + * After head skb, 2nd skb in fraglist + */ + qdf_nbuf_set_next(fisa_flow->last_skb, nbuf); + } else { + /* 1st skb after head skb + * implement qdf wrapper set_ext_list + */ + skb_shinfo(head_skb)->frag_list = nbuf; + qdf_nbuf_set_is_frag(nbuf, 1); + } + + fisa_flow->last_skb = nbuf; + fisa_flow->aggr_count++; + + dp_fisa_debug("Stiched head skb fisa_flow %pK", fisa_flow); + hex_dump_skb_data(fisa_flow->head_skb, false); + + /** + * Incoming nbuf is of size less than ongoing aggregation + * then flush the aggregate + */ + if (qdf_ntohs(udp_hdr->len) < + qdf_ntohs(fisa_flow->head_skb_udp_hdr->len)) + dp_rx_fisa_flush_flow_wrap(fisa_flow); + + return FISA_AGGR_DONE; +} +/** + * dp_fisa_rx_linear_skb() - Linearize fraglist skb to linear skb + * @vdev: handle to DP vdev + * @head_skb: non linear skb + * @size: Total length of non linear stiched skb + * + * Return: Linearized skb pointer + */ +static qdf_nbuf_t dp_fisa_rx_linear_skb(struct dp_vdev *vdev, + qdf_nbuf_t head_skb, uint32_t size) +{ +#if 0 + qdf_nbuf_t linear_skb, ext_skb, next_skb; + + linear_skb = qdf_nbuf_alloc(vdev->osdev, size, 0, 0, FALSE); + if (!linear_skb) + return NULL; + + dp_fisa_debug("head %pK data %pK tail %pK\n", head_skb->head, + head_skb->data, head_skb->tail); + ext_skb = skb_shinfo(head_skb)->frag_list; + if (ext_skb) { + skb_put_data(linear_skb, head_skb->data, 1512); + dp_fisa_debug("%d: len %d\n", __LINE__, ext_skb->len); + skb_put_data(linear_skb, ext_skb->data, ext_skb->len); + } else { + dp_fisa_debug("%d: len %d\n", __LINE__, head_skb->len); + skb_put_data(linear_skb, head_skb->data, head_skb->len); + goto done; + } + + next_skb = ext_skb->next; + while (next_skb) { + dp_fisa_debug("%d: len %d\n", __LINE__, next_skb->len); + skb_put_data(linear_skb, next_skb->data, next_skb->len); + next_skb = next_skb->next; + } + +done: + skb_copy_header(linear_skb, head_skb); + skb_reset_transport_header(linear_skb); + skb_reset_network_header(linear_skb); + linear_skb->pkt_type = PACKET_HOST; + skb_set_mac_header(linear_skb, 0); + linear_skb->ip_summed = CHECKSUM_PARTIAL; + dp_fisa_debug("linear skb %pK len %d gso_size %d mac_len %d net_header %d mac_header %d", + linear_skb, linear_skb->len, + skb_shinfo(linear_skb)->gso_size, linear_skb->mac_len, + linear_skb->network_header, linear_skb->mac_header); + + return linear_skb; +#endif + return NULL; +} + +/** + * dp_rx_fisa_flush_udp_flow() - Flush all aggregated nbuf of the udp flow + * @vdev: handle to dp_vdev + * @fisa_flow: Flow for which aggregates to be flushed + * + * Return: None + */ +static void +dp_rx_fisa_flush_udp_flow(struct dp_vdev *vdev, + struct dp_fisa_rx_sw_ft *fisa_flow) +{ + qdf_nbuf_t head_skb = fisa_flow->head_skb; + struct iphdr *head_skb_iph; + struct udphdr *head_skb_udp_hdr; + struct skb_shared_info *shinfo; + qdf_nbuf_t linear_skb; + struct dp_vdev *fisa_flow_vdev; + + dp_fisa_debug("head_skb %pK", head_skb); + dp_fisa_debug("cumulative ip length %d", + fisa_flow->adjusted_cumulative_ip_length); + if (!head_skb) { + dp_fisa_debug("Already flushed"); + return; + } + + head_skb->hash = QDF_NBUF_CB_RX_FLOW_ID(head_skb); + head_skb->sw_hash = 1; + if (qdf_nbuf_get_ext_list(head_skb)) { + __sum16 pseudo; + + shinfo = skb_shinfo(head_skb); + /* Update the head_skb before flush */ + dp_fisa_debug("cumu ip length host order 0x%x", + fisa_flow->adjusted_cumulative_ip_length); + head_skb_iph = (struct iphdr *)(qdf_nbuf_data(head_skb) + + fisa_flow->head_skb_ip_hdr_offset); + dp_fisa_debug("iph ptr %pK", head_skb_iph); + + head_skb_udp_hdr = fisa_flow->head_skb_udp_hdr; + + dp_fisa_debug("udph ptr %pK", head_skb_udp_hdr); + + dp_fisa_debug("tot_len 0x%x", qdf_ntohs(head_skb_iph->tot_len)); + + /* data_len is total length of non head_skb, + * cumulative ip length is including head_skb ip length also + */ + head_skb->data_len = + (fisa_flow->adjusted_cumulative_ip_length) - + qdf_ntohs(head_skb_udp_hdr->len); + + head_skb->len += head_skb->data_len; + + head_skb_iph->tot_len = + qdf_htons((fisa_flow->adjusted_cumulative_ip_length) + + /* IP hdr len */ + fisa_flow->head_skb_l4_hdr_offset); + pseudo = ~csum_tcpudp_magic(head_skb_iph->saddr, + head_skb_iph->daddr, + fisa_flow->adjusted_cumulative_ip_length, + head_skb_iph->protocol, 0); + + head_skb_iph->check = 0; + head_skb_iph->check = ip_fast_csum((u8 *)head_skb_iph, + head_skb_iph->ihl); + + head_skb_udp_hdr->len = + qdf_htons(qdf_ntohs(head_skb_iph->tot_len) - + fisa_flow->head_skb_l4_hdr_offset); + head_skb_udp_hdr->check = pseudo; + head_skb->csum_start = (u8 *)head_skb_udp_hdr - head_skb->head; + head_skb->csum_offset = offsetof(struct udphdr, check); + + shinfo->gso_size = fisa_flow->cur_aggr_gso_size; + dp_fisa_debug("gso_size %d, udp_len %d\n", shinfo->gso_size, + qdf_ntohs(head_skb_udp_hdr->len)); + shinfo->gso_segs = fisa_flow->cur_aggr; + shinfo->gso_type = SKB_GSO_UDP_L4; + head_skb->ip_summed = CHECKSUM_PARTIAL; + } + + qdf_nbuf_set_next(fisa_flow->head_skb, NULL); + QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(fisa_flow->head_skb) = 1; + if (fisa_flow->last_skb) + qdf_nbuf_set_next(fisa_flow->last_skb, NULL); + + hex_dump_skb_data(fisa_flow->head_skb, false); + + fisa_flow_vdev = dp_get_vdev_from_soc_vdev_id_wifi3(fisa_flow->soc_hdl, + QDF_NBUF_CB_RX_VDEV_ID(fisa_flow->head_skb)); + if (qdf_unlikely(!fisa_flow_vdev || + (fisa_flow_vdev != fisa_flow->vdev))) { + qdf_nbuf_free(fisa_flow->head_skb); + goto out; + } + dp_fisa_debug("fisa_flow->curr_aggr %d", fisa_flow->cur_aggr); + linear_skb = dp_fisa_rx_linear_skb(vdev, fisa_flow->head_skb, 24000); + if (linear_skb) { + if (!vdev->osif_rx || QDF_STATUS_SUCCESS != + vdev->osif_rx(vdev->osif_vdev, linear_skb)) + qdf_nbuf_free(linear_skb); + /* Free non linear skb */ + qdf_nbuf_free(fisa_flow->head_skb); + } else { + if (!vdev->osif_rx || QDF_STATUS_SUCCESS != + vdev->osif_rx(vdev->osif_vdev, fisa_flow->head_skb)) + qdf_nbuf_free(fisa_flow->head_skb); + } + +out: + fisa_flow->head_skb = NULL; + fisa_flow->last_skb = NULL; + + fisa_flow->flush_count++; +} + +/** + * dp_rx_fisa_flush_tcp_flow() - Flush all aggregated nbuf of the TCP flow + * @vdev: handle to dp_vdev + * @fisa_flow: Flow for which aggregates to be flushed + * + * Return: None + */ +static void +dp_rx_fisa_flush_tcp_flow(struct dp_vdev *vdev, + struct dp_fisa_rx_sw_ft *fisa_flow) +{ + qdf_nbuf_t head_skb = fisa_flow->head_skb; + struct iphdr *head_skb_iph; + struct skb_shared_info *shinfo; + + if (!head_skb) { + dp_fisa_debug("Already flushed"); + return; + } + + shinfo = skb_shinfo(head_skb); + + /* Update the head_skb before flush */ + head_skb->hash = fisa_flow->flow_hash; + head_skb->sw_hash = 1; + shinfo->gso_type = SKB_GSO_UDP_L4; + + head_skb_iph = (struct iphdr *)(qdf_nbuf_data(head_skb) + + fisa_flow->head_skb_ip_hdr_offset); + + head_skb_iph->tot_len = fisa_flow->adjusted_cumulative_ip_length; + head_skb_iph->check = ip_fast_csum((u8 *)head_skb_iph, + head_skb_iph->ihl); + + qdf_nbuf_set_next(fisa_flow->head_skb, NULL); + if (fisa_flow->last_skb) + qdf_nbuf_set_next(fisa_flow->last_skb, NULL); + vdev->osif_rx(vdev->osif_vdev, fisa_flow->head_skb); + + fisa_flow->head_skb = NULL; + + fisa_flow->flush_count++; +} + +/** + * dp_rx_fisa_flush_flow() - Flush all aggregated nbuf of the flow + * @vdev: handle to dp_vdev + * @fisa_flow: Flow for which aggregates to be flushed + * + * Return: None + */ +static void dp_rx_fisa_flush_flow(struct dp_vdev *vdev, + struct dp_fisa_rx_sw_ft *flow) +{ + dp_fisa_debug("dp_rx_fisa_flush_flow"); + + if (flow->is_flow_udp) + dp_rx_fisa_flush_udp_flow(vdev, flow); + else + dp_rx_fisa_flush_tcp_flow(vdev, flow); +} + +/** + * dp_fisa_aggregation_should_stop - check if fisa aggregate should stop + * @fisa_flow: Handle SW flow entry + * @hal_aggr_count: current aggregate count from RX PKT TLV + * @hal_cumulative_ip_len: current cumulative ip length from RX PKT TLV + * @rx_tlv_hdr: current msdu RX PKT TLV + * + * Return: true - current flow aggregation should stop, + false - continue to aggregate. + */ +static bool dp_fisa_aggregation_should_stop( + struct dp_fisa_rx_sw_ft *fisa_flow, + uint32_t hal_aggr_count, + uint16_t hal_cumulative_ip_len, + uint8_t *rx_tlv_hdr) +{ + uint32_t msdu_len = hal_rx_msdu_start_msdu_len_get(rx_tlv_hdr); + uint32_t l4_hdr_offset = HAL_RX_TLV_GET_IP_OFFSET(rx_tlv_hdr) + + HAL_RX_TLV_GET_TCP_OFFSET(rx_tlv_hdr); + uint32_t cumulative_ip_len_delta = hal_cumulative_ip_len - + fisa_flow->hal_cumultive_ip_len; + /** + * current cumulative ip length should > last cumulative_ip_len + * and <= last cumulative_ip_len + 1478, also current aggregate + * count should be equal to last aggregate count + 1, + * cumulative_ip_len delta should be equal to current msdu length + * - l4 header offset, + * otherwise, current fisa flow aggregation should be stopped. + */ + if (fisa_flow->do_not_aggregate || + hal_cumulative_ip_len <= fisa_flow->hal_cumultive_ip_len || + cumulative_ip_len_delta > FISA_MAX_SINGLE_CUMULATIVE_IP_LEN || + (fisa_flow->last_hal_aggr_count + 1) != hal_aggr_count || + cumulative_ip_len_delta != (msdu_len - l4_hdr_offset)) + return true; + + return false; +} + +/** + * dp_add_nbuf_to_fisa_flow() - Aggregate incoming nbuf + * @fisa_hdl: handle to fisa context + * @vdev: handle DP vdev + * @nbuf: Incoming nbuf + * @fisa_flow: Handle SW flow entry + * + * Return: Success on aggregation + */ +static int dp_add_nbuf_to_fisa_flow(struct dp_rx_fst *fisa_hdl, + struct dp_vdev *vdev, qdf_nbuf_t nbuf, + struct dp_fisa_rx_sw_ft *fisa_flow) +{ + bool flow_aggr_cont; + uint8_t *rx_tlv_hdr = qdf_nbuf_data(nbuf); + bool flow_invalid, flow_timeout; + uint32_t flow_idx; + uint16_t hal_cumulative_ip_len; + hal_soc_handle_t hal_soc_hdl = fisa_hdl->soc_hdl->hal_soc; + uint32_t hal_aggr_count; + uint8_t napi_id = QDF_NBUF_CB_RX_CTX_ID(nbuf); + + dump_tlvs(hal_soc_hdl, rx_tlv_hdr, QDF_TRACE_LEVEL_ERROR); + dp_fisa_debug("nbuf: %pK nbuf->next:%pK nbuf->data:%pK len %d data_len %d", + nbuf, qdf_nbuf_next(nbuf), qdf_nbuf_data(nbuf), nbuf->len, + nbuf->data_len); + + /* Packets of the same flow are arriving on a different REO than + * the one configured. + */ + if (qdf_unlikely(fisa_flow->napi_id != napi_id)) { + QDF_BUG(0); + return FISA_AGGR_NOT_ELIGIBLE; + } + + hal_cumulative_ip_len = hal_rx_get_fisa_cumulative_ip_length( + hal_soc_hdl, + rx_tlv_hdr); + flow_aggr_cont = hal_rx_get_fisa_flow_agg_continuation(hal_soc_hdl, + rx_tlv_hdr); + hal_aggr_count = hal_rx_get_fisa_flow_agg_count(hal_soc_hdl, + rx_tlv_hdr); + + if (!flow_aggr_cont) { + /* Start of new aggregation for the flow + * Flush previous aggregates for this flow + */ + dp_fisa_debug("no fgc nbuf %pK, flush %pK napi %d", nbuf, + fisa_flow, QDF_NBUF_CB_RX_CTX_ID(nbuf)); + dp_rx_fisa_flush_flow(vdev, fisa_flow); + /* Clear of previoud context values */ + fisa_flow->napi_flush_cumulative_l4_checksum = 0; + fisa_flow->napi_flush_cumulative_ip_length = 0; + fisa_flow->cur_aggr = 0; + fisa_flow->do_not_aggregate = false; + if (hal_cumulative_ip_len > FISA_MAX_SINGLE_CUMULATIVE_IP_LEN) + qdf_assert(0); + } else if (qdf_unlikely(dp_fisa_aggregation_should_stop( + fisa_flow, + hal_aggr_count, + hal_cumulative_ip_len, + rx_tlv_hdr))) { + /* Either HW cumulative ip length is wrong, or packet is missed + * Flush the flow and do not aggregate until next start new + * aggreagtion + */ + dp_rx_fisa_flush_flow(vdev, fisa_flow); + fisa_flow->do_not_aggregate = true; + fisa_flow->cur_aggr = 0; + fisa_flow->napi_flush_cumulative_ip_length = 0; + goto invalid_fisa_assist; + } else { + /* takecare to skip the udp hdr len for sub sequent cumulative + * length + */ + fisa_flow->cur_aggr++; + } + hal_rx_msdu_get_flow_params(hal_soc_hdl, rx_tlv_hdr, &flow_invalid, + &flow_timeout, &flow_idx); + dp_fisa_debug("nbuf %pK cumulat_ip_length %d flow %pK fl aggr cont %d", + nbuf, hal_cumulative_ip_len, fisa_flow, flow_aggr_cont); + + fisa_flow->aggr_count++; + fisa_flow->last_hal_aggr_count = hal_aggr_count; + fisa_flow->hal_cumultive_ip_len = hal_cumulative_ip_len; + + if (!fisa_flow->head_skb) { + /* This is start of aggregation for the flow, save the offsets*/ + fisa_flow->napi_flush_cumulative_l4_checksum = 0; + fisa_flow->cur_aggr = 0; + } + + fisa_flow->adjusted_cumulative_ip_length = + /* cumulative ip len has all the aggr msdu udp header len + * Aggr UDP msdu has one UDP header len + */ + (hal_cumulative_ip_len - + (fisa_flow->cur_aggr * sizeof(struct udphdr))) - + fisa_flow->napi_flush_cumulative_ip_length; + + /** + * cur_aggr does not include the head_skb, so compare with + * FISA_FLOW_MAX_AGGR_COUNT - 1. + */ + if (fisa_flow->cur_aggr > (FISA_FLOW_MAX_AGGR_COUNT - 1)) + dp_err("HAL cumulative_ip_length %d", hal_cumulative_ip_len); + + dp_fisa_debug("hal cum_len 0x%x - napI_cumu_len 0x%x = flow_cum_len 0x%x cur_aggr %d", + hal_cumulative_ip_len, + fisa_flow->napi_flush_cumulative_ip_length, + fisa_flow->adjusted_cumulative_ip_length, + fisa_flow->cur_aggr); + + if (fisa_flow->adjusted_cumulative_ip_length > + FISA_FLOW_MAX_CUMULATIVE_IP_LEN) { + dp_err("fisa_flow %pK nbuf %pK", fisa_flow, nbuf); + dp_err("fisa_flow->adjusted_cumulative_ip_length %d", + fisa_flow->adjusted_cumulative_ip_length); + dp_err("HAL cumulative_ip_length %d", hal_cumulative_ip_len); + dp_err("napi_flush_cumulative_ip_length %d", + fisa_flow->napi_flush_cumulative_ip_length); + qdf_assert(0); + } + + if (fisa_flow->is_flow_udp) { + dp_rx_fisa_aggr_udp(fisa_hdl, fisa_flow, nbuf); + } else if (fisa_flow->is_flow_tcp) { + qdf_assert(0); + dp_rx_fisa_aggr_tcp(fisa_hdl, fisa_flow, nbuf); + } + + return FISA_AGGR_DONE; + +invalid_fisa_assist: + /* Not eligible aggregation deliver frame without FISA */ + return FISA_AGGR_NOT_ELIGIBLE; +} + +/** + * dp_is_nbuf_bypass_fisa() - FISA bypass check for RX frame + * @nbuf: RX nbuf pointer + * + * Return: true if FISA should be bypassed else false + */ +static bool dp_is_nbuf_bypass_fisa(qdf_nbuf_t nbuf) +{ + /* RX frame from non-regular path or DHCP packet */ + if (qdf_nbuf_is_exc_frame(nbuf) || + qdf_nbuf_is_ipv4_dhcp_pkt(nbuf) || + qdf_nbuf_is_da_mcbc(nbuf)) + return true; + + return false; +} + +/** + * dp_rx_fisa_flush_by_vdev_ctx_id() - Flush fisa aggregates per vdev and rx + * context id + * @soc: core txrx main context + * @vdev: Handle DP vdev + * @rx_ctx_id: Rx context id + * + * Return: Success on flushing the flows for the vdev and rx ctx id + */ +static +QDF_STATUS dp_rx_fisa_flush_by_vdev_ctx_id(struct dp_soc *soc, + struct dp_vdev *vdev, + uint8_t rx_ctx_id) +{ + struct dp_rx_fst *fisa_hdl = soc->rx_fst; + struct dp_fisa_rx_sw_ft *sw_ft_entry = + (struct dp_fisa_rx_sw_ft *)fisa_hdl->base; + int ft_size = fisa_hdl->max_entries; + int i; + + for (i = 0; i < ft_size; i++) { + if (sw_ft_entry[i].is_populated && + vdev == sw_ft_entry[i].vdev && + sw_ft_entry[i].napi_id == rx_ctx_id) { + dp_fisa_debug("flushing %d %pk vdev %pK napi id:%d", i, + &sw_ft_entry[i], vdev, rx_ctx_id); + dp_rx_fisa_flush_flow_wrap(&sw_ft_entry[i]); + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * dp_fisa_disallowed_for_vdev() - Check if fisa is allowed on vdev + * @soc: core txrx main context + * @vdev: Handle DP vdev + * @rx_ctx_id: Rx context id + * + * Return: true if fisa is disallowed for vdev else false + */ +static bool dp_fisa_disallowed_for_vdev(struct dp_soc *soc, + struct dp_vdev *vdev, + uint8_t rx_ctx_id) +{ + if (!vdev->fisa_disallowed[rx_ctx_id]) { + if (vdev->fisa_force_flushed[rx_ctx_id]) + vdev->fisa_force_flushed[rx_ctx_id] = 0; + return false; + } + + if (!vdev->fisa_force_flushed[rx_ctx_id]) { + dp_rx_fisa_flush_by_vdev_ctx_id(soc, vdev, rx_ctx_id); + vdev->fisa_force_flushed[rx_ctx_id] = 1; + } + + return true; +} + +/** + * dp_fisa_rx() - Entry function to FISA to handle aggregation + * @soc: core txrx main context + * @vdev: Handle DP vdev + * @nbuf_list: List nbufs to be aggregated + * + * Return: Success on aggregation + */ +QDF_STATUS dp_fisa_rx(struct dp_soc *soc, struct dp_vdev *vdev, + qdf_nbuf_t nbuf_list) +{ + struct dp_rx_fst *dp_fisa_rx_hdl = soc->rx_fst; + qdf_nbuf_t head_nbuf; + qdf_nbuf_t next_nbuf; + struct dp_fisa_rx_sw_ft *fisa_flow; + int fisa_ret; + uint8_t rx_ctx_id = QDF_NBUF_CB_RX_CTX_ID(nbuf_list); + + head_nbuf = nbuf_list; + + while (head_nbuf) { + next_nbuf = head_nbuf->next; + qdf_nbuf_set_next(head_nbuf, NULL); + + /* bypass FISA check */ + if (dp_is_nbuf_bypass_fisa(head_nbuf)) + goto deliver_nbuf; + + if (dp_fisa_disallowed_for_vdev(soc, vdev, rx_ctx_id)) + goto deliver_nbuf; + + if (qdf_atomic_read(&soc->skip_fisa_param.skip_fisa)) { + if (!soc->skip_fisa_param.fisa_force_flush[rx_ctx_id]) { + dp_rx_fisa_flush_by_ctx_id(soc, rx_ctx_id); + soc->skip_fisa_param. + fisa_force_flush[rx_ctx_id] = 1; + } + goto deliver_nbuf; + } else if (soc->skip_fisa_param.fisa_force_flush[rx_ctx_id]) { + soc->skip_fisa_param.fisa_force_flush[rx_ctx_id] = 0; + } + + qdf_nbuf_push_head(head_nbuf, RX_PKT_TLVS_LEN + + QDF_NBUF_CB_RX_PACKET_L3_HDR_PAD(head_nbuf)); + + /* Add new flow if the there is no ongoing flow */ + fisa_flow = dp_rx_get_fisa_flow(dp_fisa_rx_hdl, vdev, + head_nbuf); + + /* Fragmented skb do not handle via fisa + * get that flow and deliver that flow to rx_thread + */ + if (qdf_unlikely(qdf_nbuf_get_ext_list(head_nbuf))) { + dp_fisa_debug("Fragmented skb, will not be FISAed"); + if (fisa_flow) + dp_rx_fisa_flush_flow(vdev, fisa_flow); + goto pull_nbuf; + } + + if (!fisa_flow) + goto pull_nbuf; + + fisa_ret = dp_add_nbuf_to_fisa_flow(dp_fisa_rx_hdl, vdev, + head_nbuf, fisa_flow); + if (fisa_ret == FISA_AGGR_DONE) + goto next_msdu; + +pull_nbuf: + nbuf_skip_rx_pkt_tlv(dp_fisa_rx_hdl->soc_hdl->hal_soc, + head_nbuf); + +deliver_nbuf: /* Deliver without FISA */ + QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(head_nbuf) = 1; + qdf_nbuf_set_next(head_nbuf, NULL); + hex_dump_skb_data(head_nbuf, false); + if (!vdev->osif_rx || QDF_STATUS_SUCCESS != + vdev->osif_rx(vdev->osif_vdev, head_nbuf)) + qdf_nbuf_free(head_nbuf); +next_msdu: + head_nbuf = next_nbuf; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dp_rx_dump_fisa_stats(struct dp_soc *soc) +{ + struct dp_rx_fst *rx_fst = soc->rx_fst; + struct dp_fisa_rx_sw_ft *sw_ft_entry = + &((struct dp_fisa_rx_sw_ft *)rx_fst->base)[0]; + int ft_size = rx_fst->max_entries; + int i; + uint64_t avg_aggregated; + + dp_info("Num of flows programmed %d", rx_fst->add_flow_count); + dp_info("Num of flows evicted %d", rx_fst->del_flow_count); + dp_info("Hash collision count %d", rx_fst->hash_collision_cnt); + + for (i = 0; i < ft_size; i++, sw_ft_entry++) { + if (!sw_ft_entry->is_populated) + continue; + + dp_info("FLOw ID %d is %s on napi/ring %d", + sw_ft_entry->flow_id, + sw_ft_entry->is_flow_udp ? "udp" : "tcp", + sw_ft_entry->napi_id); + dp_info("num msdu aggr %d", sw_ft_entry->aggr_count); + dp_info("flush count %d", sw_ft_entry->flush_count); + dp_info("bytes_aggregated %llu", sw_ft_entry->bytes_aggregated); + avg_aggregated = sw_ft_entry->bytes_aggregated; + qdf_do_div(avg_aggregated, sw_ft_entry->flush_count); + dp_info("avg aggregation %llu", avg_aggregated); + print_flow_tuple(&sw_ft_entry->rx_flow_tuple_info); + } + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_fisa_flush_flow_wrap() - flush fisa flow by invoking + * dp_rx_fisa_flush_flow() + * @sw_ft: fisa flow for which aggregates to be flushed + * + * Return: None. + */ +static void dp_rx_fisa_flush_flow_wrap(struct dp_fisa_rx_sw_ft *sw_ft) +{ + /* Save the ip_len and checksum as hardware assist is + * always based on his start of aggregation + */ + sw_ft->napi_flush_cumulative_l4_checksum = + sw_ft->cumulative_l4_checksum; + sw_ft->napi_flush_cumulative_ip_length = + sw_ft->hal_cumultive_ip_len; + dp_fisa_debug("napi_flush_cumulative_ip_length 0x%x", + sw_ft->napi_flush_cumulative_ip_length); + + dp_rx_fisa_flush_flow(sw_ft->vdev, + sw_ft); + sw_ft->cur_aggr = 0; +} + +QDF_STATUS dp_rx_fisa_flush_by_ctx_id(struct dp_soc *soc, int napi_id) +{ + struct dp_rx_fst *fisa_hdl = soc->rx_fst; + struct dp_fisa_rx_sw_ft *sw_ft_entry = + (struct dp_fisa_rx_sw_ft *)fisa_hdl->base; + int ft_size = fisa_hdl->max_entries; + int i; + + for (i = 0; i < ft_size; i++) { + if (sw_ft_entry[i].napi_id == napi_id && + sw_ft_entry[i].is_populated) { + dp_fisa_debug("flushing %d %pK napi_id %d", i, + &sw_ft_entry[i], napi_id); + dp_rx_fisa_flush_flow_wrap(&sw_ft_entry[i]); + } + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dp_rx_fisa_flush_by_vdev_id(struct dp_soc *soc, uint8_t vdev_id) +{ + struct dp_rx_fst *fisa_hdl = soc->rx_fst; + struct dp_fisa_rx_sw_ft *sw_ft_entry = + (struct dp_fisa_rx_sw_ft *)fisa_hdl->base; + int ft_size = fisa_hdl->max_entries; + int i; + struct dp_vdev *vdev; + + vdev = dp_get_vdev_from_soc_vdev_id_wifi3(soc, vdev_id); + if (qdf_unlikely(!vdev)) { + dp_err("null vdev by vdev_id %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + for (i = 0; i < ft_size; i++) { + if (vdev == sw_ft_entry[i].vdev) { + dp_fisa_debug("flushing %d %pk vdev %pK", i, + &sw_ft_entry[i], vdev); + dp_rx_fisa_flush_flow_wrap(&sw_ft_entry[i]); + } + } + + return QDF_STATUS_SUCCESS; +} + +void dp_set_fisa_disallowed_for_vdev(struct cdp_soc_t *cdp_soc, uint8_t vdev_id, + uint8_t rx_ctx_id, uint8_t val) +{ + struct dp_soc *soc = (struct dp_soc *)cdp_soc; + struct dp_vdev *vdev; + + vdev = dp_get_vdev_from_soc_vdev_id_wifi3(soc, vdev_id); + if (qdf_unlikely(!vdev)) + return; + + vdev->fisa_disallowed[rx_ctx_id] = val; +} diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_fisa_rx.h b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_fisa_rx.h new file mode 100644 index 0000000000000000000000000000000000000000..2642c584ae4ab55079e6970a0bca5ad9a4b24871 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_fisa_rx.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +//#define FISA_DEBUG_ENABLE + +#ifdef FISA_DEBUG_ENABLE +#define dp_fisa_debug dp_info +#else +#define dp_fisa_debug dp_debug +#endif + +#if defined(WLAN_SUPPORT_RX_FISA) + +#define FSE_CACHE_FLUSH_TIME_OUT 5 /* milliSeconds */ +#define FISA_UDP_MAX_DATA_LEN 1470 /* udp max data length */ +#define FISA_UDP_HDR_LEN 8 /* udp header length */ +#define FISA_FLOW_MAX_AGGR_COUNT 16 /* max flow aggregate count */ +/* single packet max cumulative ip length */ +#define FISA_MAX_SINGLE_CUMULATIVE_IP_LEN \ + (FISA_UDP_MAX_DATA_LEN + FISA_UDP_HDR_LEN) +/* max flow cumulative ip length */ +#define FISA_FLOW_MAX_CUMULATIVE_IP_LEN \ + (FISA_MAX_SINGLE_CUMULATIVE_IP_LEN * FISA_FLOW_MAX_AGGR_COUNT) + +/** + * dp_rx_dump_fisa_stats() - Dump fisa stats + * @soc: core txrx main context + * + * Return: QDF_STATUS + */ +QDF_STATUS dp_rx_dump_fisa_stats(struct dp_soc *soc); + +/** + * dp_fisa_rx() - FISA Rx packet delivery entry function + * @soc: core txrx main context + * @vdev: core txrx vdev + * @nbuf_list: Delivery list of nbufs + * + * Return: QDF_STATUS + */ +QDF_STATUS dp_fisa_rx(struct dp_soc *dp_fisa_rx_hdl, struct dp_vdev *vdev, + qdf_nbuf_t nbuf_list); + +/** + * dp_rx_fisa_flush_by_ctx_id() - FISA Rx flush function to flush + * aggregation at end of NAPI + * @soc: core txrx main context + * @napi_id: Flows which are rxed on the NAPI ID to be flushed + * + * Return: QDF_STATUS + */ +QDF_STATUS dp_rx_fisa_flush_by_ctx_id(struct dp_soc *soc, int napi_id); + +/** + * dp_rx_fisa_flush_by_vdev_id() - Flush fisa aggregates per vdev id + * @soc: core txrx main context + * @vdev_id: vdev ID + * + * Return: Success on flushing the flows for the vdev + */ +QDF_STATUS dp_rx_fisa_flush_by_vdev_id(struct dp_soc *soc, uint8_t vdev_id); + +/** + * dp_rx_skip_fisa() - Set flags to skip fisa aggregation + * @cdp_soc: core txrx main context + * @value: allow or skip fisa + * + * Return: None + */ +static inline +void dp_rx_skip_fisa(struct cdp_soc_t *cdp_soc, uint32_t value) +{ + struct dp_soc *soc = (struct dp_soc *)cdp_soc; + + qdf_atomic_set(&soc->skip_fisa_param.skip_fisa, !value); +} + +/** + * dp_set_fisa_disallowed_for_vdev() - Set fisa disallowed flag for vdev + * @cdp_soc: core txrx main context + * @vdev_id: Vdev id + * @rx_ctx_id: rx context id + * @val: value to be set + * + * Return: None + */ +void dp_set_fisa_disallowed_for_vdev(struct cdp_soc_t *cdp_soc, uint8_t vdev_id, + uint8_t rx_ctx_id, uint8_t val); +#else +static QDF_STATUS dp_rx_dump_fisa_stats(struct dp_soc *soc) +{ + return QDF_STATUS_SUCCESS; +} + +void dp_rx_dump_fisa_table(struct dp_soc *soc) +{ +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_rx_fst.c b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_rx_fst.c new file mode 100644 index 0000000000000000000000000000000000000000..f1929a249fc046e028c02a7241f471bf2ff3c95d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_rx_fst.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "dp_types.h" +#include "qdf_mem.h" +#include "qdf_nbuf.h" +#include "cfg_dp.h" +#include "wlan_cfg.h" +#include "dp_types.h" +#include "hal_rx_flow.h" +#include "dp_htt.h" +#include "dp_internal.h" + +#ifdef WLAN_SUPPORT_RX_FISA + +void dp_rx_dump_fisa_table(struct dp_soc *soc) +{ + hal_rx_dump_fse_table(soc->rx_fst->hal_rx_fst); +} + +/** + * dp_rx_flow_send_htt_operation_cmd() - Invalidate FSE cache on FT change + * @pdev: handle to DP pdev + * @fse_op: Cache operation code + * @rx_flow_tuple: flow tuple whose entry has to be invalidated + * + * Return: Success if we successfully send FW HTT command + */ +static QDF_STATUS +dp_rx_flow_send_htt_operation_cmd(struct dp_pdev *pdev, + enum dp_htt_flow_fst_operation fse_op, + struct cdp_rx_flow_tuple_info *rx_flow_tuple) +{ + struct dp_htt_rx_flow_fst_operation fse_op_cmd; + struct cdp_rx_flow_info rx_flow_info; + + rx_flow_info.is_addr_ipv4 = true; + rx_flow_info.op_code = CDP_FLOW_FST_ENTRY_ADD; + qdf_mem_copy(&rx_flow_info.flow_tuple_info, rx_flow_tuple, + sizeof(struct cdp_rx_flow_tuple_info)); + rx_flow_info.fse_metadata = 0xDADA; + fse_op_cmd.pdev_id = pdev->pdev_id; + fse_op_cmd.op_code = fse_op; + fse_op_cmd.rx_flow = &rx_flow_info; + + return dp_htt_rx_flow_fse_operation(pdev, &fse_op_cmd); +} + +/** + * dp_fisa_fse_cache_flush_timer() - FSE cache flush timeout handler + * @arg: SoC handle + * + * Return: None + */ +static void dp_fisa_fse_cache_flush_timer(void *arg) +{ + struct dp_soc *soc = (struct dp_soc *)arg; + struct dp_rx_fst *fisa_hdl = soc->rx_fst; + struct cdp_rx_flow_tuple_info rx_flow_tuple_info = { 0 }; + static uint32_t fse_cache_flush_rec_idx; + struct fse_cache_flush_history *fse_cache_flush_rec; + QDF_STATUS status; + + fse_cache_flush_rec = &fisa_hdl->cache_fl_rec[fse_cache_flush_rec_idx % + MAX_FSE_CACHE_FL_HST]; + fse_cache_flush_rec->timestamp = qdf_get_log_timestamp(); + fse_cache_flush_rec->flows_added = + qdf_atomic_read(&fisa_hdl->fse_cache_flush_posted); + fse_cache_flush_rec_idx++; + dp_info("FSE cache flush for %d flows", + fse_cache_flush_rec->flows_added); + + qdf_atomic_set(&fisa_hdl->fse_cache_flush_posted, 0); + status = + dp_rx_flow_send_htt_operation_cmd(soc->pdev_list[0], + DP_HTT_FST_CACHE_INVALIDATE_FULL, + &rx_flow_tuple_info); + if (QDF_IS_STATUS_ERROR(status)) { + dp_err("Failed to send the cache invalidation\n"); + /* + * Not big impact cache entry gets updated later + */ + } +} + +/** + * dp_rx_fst_attach() - Initialize Rx FST and setup necessary parameters + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: Handle to flow search table entry + */ +QDF_STATUS dp_rx_fst_attach(struct dp_soc *soc, struct dp_pdev *pdev) +{ + struct dp_rx_fst *fst; + uint8_t *hash_key; + struct wlan_cfg_dp_soc_ctxt *cfg = soc->wlan_cfg_ctx; + QDF_STATUS status; + + /* Check if it is enabled in the INI */ + if (!wlan_cfg_is_rx_fisa_enabled(cfg)) { + dp_err("RX FISA feature is disabled"); + return QDF_STATUS_E_NOSUPPORT; + } + +#ifdef NOT_YET /* Not required for now */ + /* Check if FW supports */ + if (!wlan_psoc_nif_fw_ext_cap_get((void *)pdev->ctrl_pdev, + WLAN_SOC_CEXT_RX_FSE_SUPPORT)) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "rx fse disabled in FW\n"); + wlan_cfg_set_rx_flow_tag_enabled(cfg, false); + return QDF_STATUS_E_NOSUPPORT; + } +#endif + if (soc->rx_fst) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "RX FST already allocated\n"); + return QDF_STATUS_SUCCESS; + } + + fst = qdf_mem_malloc(sizeof(struct dp_rx_fst)); + if (!fst) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "RX FST allocation failed\n"); + return QDF_STATUS_E_NOMEM; + } + + fst->max_skid_length = wlan_cfg_rx_fst_get_max_search(cfg); + fst->max_entries = wlan_cfg_get_rx_flow_search_table_size(cfg); + hash_key = wlan_cfg_rx_fst_get_hash_key(cfg); + + fst->hash_mask = fst->max_entries - 1; + fst->num_entries = 0; + dp_err("FST setup params FT size %d, hash_mask 0x%x, skid_length %d", + fst->max_entries, fst->hash_mask, fst->max_skid_length); + + fst->base = (uint8_t *)qdf_mem_malloc(DP_RX_GET_SW_FT_ENTRY_SIZE * + fst->max_entries); + + if (!fst->base) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "Rx fst->base allocation failed, #entries:%d\n", + fst->max_entries); + + goto out2; + } + + fst->hal_rx_fst = hal_rx_fst_attach(soc->osdev, + &fst->hal_rx_fst_base_paddr, + fst->max_entries, + fst->max_skid_length, hash_key); + + if (qdf_unlikely(!fst->hal_rx_fst)) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "Rx Hal fst allocation failed, #entries:%d\n", + fst->max_entries); + goto out1; + } + + qdf_spinlock_create(&fst->dp_rx_fst_lock); + + status = qdf_timer_init(soc->osdev, &fst->fse_cache_flush_timer, + dp_fisa_fse_cache_flush_timer, (void *)soc, + QDF_TIMER_TYPE_WAKE_APPS); + if (QDF_IS_STATUS_ERROR(status)) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "Failed to init cache_flush_timer\n"); + goto timer_init_fail; + } + + qdf_atomic_init(&fst->fse_cache_flush_posted); + + fst->soc_hdl = soc; + soc->rx_fst = fst; + soc->fisa_enable = true; + qdf_atomic_init(&soc->skip_fisa_param.skip_fisa); + + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "Rx FST attach successful, #entries:%d\n", + fst->max_entries); + + return QDF_STATUS_SUCCESS; + +timer_init_fail: + qdf_spinlock_destroy(&fst->dp_rx_fst_lock); + hal_rx_fst_detach(fst->hal_rx_fst, soc->osdev); +out1: + qdf_mem_free(fst->base); +out2: + qdf_mem_free(fst); + return QDF_STATUS_E_NOMEM; +} + +/** + * dp_rx_flow_send_fst_fw_setup() - Program FST parameters in FW/HW post-attach + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: Success when fst parameters are programmed in FW, error otherwise + */ +QDF_STATUS dp_rx_flow_send_fst_fw_setup(struct dp_soc *soc, + struct dp_pdev *pdev) +{ + struct dp_htt_rx_flow_fst_setup fisa_hw_fst_setup_cmd = {0}; + struct dp_rx_fst *fst = soc->rx_fst; + struct wlan_cfg_dp_soc_ctxt *cfg = soc->wlan_cfg_ctx; + QDF_STATUS status; + + /* mac_id = 0 is used to configure both macs with same FT */ + fisa_hw_fst_setup_cmd.pdev_id = 0; + fisa_hw_fst_setup_cmd.max_entries = fst->max_entries; + fisa_hw_fst_setup_cmd.max_search = fst->max_skid_length; + fisa_hw_fst_setup_cmd.base_addr_lo = fst->hal_rx_fst_base_paddr & + 0xffffffff; + fisa_hw_fst_setup_cmd.base_addr_hi = (fst->hal_rx_fst_base_paddr >> 32); + fisa_hw_fst_setup_cmd.ip_da_sa_prefix = HTT_RX_IPV4_COMPATIBLE_IPV6; + fisa_hw_fst_setup_cmd.hash_key_len = HAL_FST_HASH_KEY_SIZE_BYTES; + fisa_hw_fst_setup_cmd.hash_key = wlan_cfg_rx_fst_get_hash_key(cfg); + + status = dp_htt_rx_flow_fst_setup(pdev, &fisa_hw_fst_setup_cmd); + + return status; +} + +/** + * dp_rx_fst_detach() - De-initialize Rx FST + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: None + */ +void dp_rx_fst_detach(struct dp_soc *soc, struct dp_pdev *pdev) +{ + struct dp_rx_fst *dp_fst; + + dp_fst = soc->rx_fst; + if (qdf_likely(dp_fst)) { + qdf_timer_sync_cancel(&dp_fst->fse_cache_flush_timer); + hal_rx_fst_detach(dp_fst->hal_rx_fst, soc->osdev); + qdf_mem_free(dp_fst->base); + qdf_spinlock_destroy(&dp_fst->dp_rx_fst_lock); + qdf_mem_free(dp_fst); + } + soc->rx_fst = NULL; + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, + "Rx FST detached\n"); +} +#else /* WLAN_SUPPORT_RX_FISA */ + +#endif /* !WLAN_SUPPORT_RX_FISA */ + diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_rx_thread.c b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_rx_thread.c new file mode 100644 index 0000000000000000000000000000000000000000..6bffa2f9a7a786637c2dfd60a056a514c49d2195 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_rx_thread.c @@ -0,0 +1,1047 @@ +/* + * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "dp_peer.h" +#include "dp_internal.h" +#include +#include +#include + +/* Timeout in ms to wait for a DP rx thread */ +#define DP_RX_THREAD_WAIT_TIMEOUT 1000 + +#define DP_RX_TM_DEBUG 0 +#if DP_RX_TM_DEBUG +/** + * dp_rx_tm_walk_skb_list() - Walk skb list and print members + * @nbuf_list - nbuf list to print + * + * Returns: None + */ +static inline void dp_rx_tm_walk_skb_list(qdf_nbuf_t nbuf_list) +{ + qdf_nbuf_t nbuf; + int i = 0; + + nbuf = nbuf_list; + while (nbuf) { + dp_debug("%d nbuf:%pK nbuf->next:%pK nbuf->data:%pK", i, + nbuf, qdf_nbuf_next(nbuf), qdf_nbuf_data(nbuf)); + nbuf = qdf_nbuf_next(nbuf); + i++; + } +} +#else +static inline void dp_rx_tm_walk_skb_list(qdf_nbuf_t nbuf_list) +{ } +#endif /* DP_RX_TM_DEBUG */ + +/** + * dp_rx_tm_get_soc_handle() - get soc handle from struct dp_rx_tm_handle_cmn + * @rx_tm_handle_cmn - rx thread manager cmn handle + * + * Returns: ol_txrx_soc_handle on success, NULL on failure. + */ +static inline +ol_txrx_soc_handle dp_rx_tm_get_soc_handle(struct dp_rx_tm_handle_cmn *rx_tm_handle_cmn) +{ + struct dp_txrx_handle_cmn *txrx_handle_cmn; + ol_txrx_soc_handle soc; + + txrx_handle_cmn = + dp_rx_thread_get_txrx_handle(rx_tm_handle_cmn); + + soc = dp_txrx_get_soc_from_ext_handle(txrx_handle_cmn); + return soc; +} + +/** + * dp_rx_tm_thread_dump_stats() - display stats for a rx_thread + * @rx_thread - rx_thread pointer for which the stats need to be + * displayed + * + * Returns: None + */ +static void dp_rx_tm_thread_dump_stats(struct dp_rx_thread *rx_thread) +{ + uint8_t reo_ring_num; + uint32_t off = 0; + char nbuf_queued_string[100]; + uint32_t total_queued = 0; + uint32_t temp = 0; + + qdf_mem_zero(nbuf_queued_string, sizeof(nbuf_queued_string)); + + for (reo_ring_num = 0; reo_ring_num < DP_RX_TM_MAX_REO_RINGS; + reo_ring_num++) { + temp = rx_thread->stats.nbuf_queued[reo_ring_num]; + if (!temp) + continue; + total_queued += temp; + if (off >= sizeof(nbuf_queued_string)) + continue; + off += qdf_scnprintf(&nbuf_queued_string[off], + sizeof(nbuf_queued_string) - off, + "reo[%u]:%u ", reo_ring_num, temp); + } + + if (!total_queued) + return; + + dp_info("thread:%u - qlen:%u queued:(total:%u %s) dequeued:%u stack:%u gro_flushes: %u gro_flushes_by_vdev_del: %u rx_flushes: %u max_len:%u invalid(peer:%u vdev:%u rx-handle:%u others:%u enq fail:%u)", + rx_thread->id, + qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue), + total_queued, + nbuf_queued_string, + rx_thread->stats.nbuf_dequeued, + rx_thread->stats.nbuf_sent_to_stack, + rx_thread->stats.gro_flushes, + rx_thread->stats.gro_flushes_by_vdev_del, + rx_thread->stats.rx_flushed, + rx_thread->stats.nbufq_max_len, + rx_thread->stats.dropped_invalid_peer, + rx_thread->stats.dropped_invalid_vdev, + rx_thread->stats.dropped_invalid_os_rx_handles, + rx_thread->stats.dropped_others, + rx_thread->stats.dropped_enq_fail); +} + +QDF_STATUS dp_rx_tm_dump_stats(struct dp_rx_tm_handle *rx_tm_hdl) +{ + int i; + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + if (!rx_tm_hdl->rx_thread[i]) + continue; + dp_rx_tm_thread_dump_stats(rx_tm_hdl->rx_thread[i]); + } + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_ALLOW_PKT_DROPPING +/* + * dp_check_and_update_pending() - Check and Set RX Pending flag + * @tm_handle_cmn - DP thread pointer + * + * Returns: QDF_STATUS_SUCCESS on success or qdf error code on + * failure + */ +static inline +QDF_STATUS dp_check_and_update_pending(struct dp_rx_tm_handle_cmn + *tm_handle_cmn) +{ + struct dp_txrx_handle_cmn *txrx_handle_cmn; + struct dp_rx_tm_handle *rx_tm_hdl = + (struct dp_rx_tm_handle *)tm_handle_cmn; + struct dp_soc *dp_soc; + uint32_t rx_pending_hl_threshold; + uint32_t rx_pending_lo_threshold; + uint32_t nbuf_queued_total = 0; + uint32_t nbuf_dequeued_total = 0; + uint32_t rx_flushed_total = 0; + uint32_t pending = 0; + int i; + + txrx_handle_cmn = + dp_rx_thread_get_txrx_handle(tm_handle_cmn); + if (!txrx_handle_cmn) { + dp_err("invalid txrx_handle_cmn!"); + QDF_BUG(0); + return QDF_STATUS_E_FAILURE; + } + + dp_soc = (struct dp_soc *)dp_txrx_get_soc_from_ext_handle( + txrx_handle_cmn); + if (!dp_soc) { + dp_err("invalid soc!"); + QDF_BUG(0); + return QDF_STATUS_E_FAILURE; + } + + rx_pending_hl_threshold = wlan_cfg_rx_pending_hl_threshold( + dp_soc->wlan_cfg_ctx); + rx_pending_lo_threshold = wlan_cfg_rx_pending_lo_threshold( + dp_soc->wlan_cfg_ctx); + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + if (likely(rx_tm_hdl->rx_thread[i])) { + nbuf_queued_total += + rx_tm_hdl->rx_thread[i]->stats.nbuf_queued_total; + nbuf_dequeued_total += + rx_tm_hdl->rx_thread[i]->stats.nbuf_dequeued; + rx_flushed_total += + rx_tm_hdl->rx_thread[i]->stats.rx_flushed; + } + } + + if (nbuf_queued_total > (nbuf_dequeued_total + rx_flushed_total)) + pending = nbuf_queued_total - (nbuf_dequeued_total + + rx_flushed_total); + + if (unlikely(pending > rx_pending_hl_threshold)) + qdf_atomic_set(&rx_tm_hdl->allow_dropping, 1); + else if (pending < rx_pending_lo_threshold) + qdf_atomic_set(&rx_tm_hdl->allow_dropping, 0); + + return QDF_STATUS_SUCCESS; +} + +#else +static inline +QDF_STATUS dp_check_and_update_pending(struct dp_rx_tm_handle_cmn + *tm_handle_cmn) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * dp_rx_tm_thread_enqueue() - enqueue nbuf list into rx_thread + * @rx_thread - rx_thread in which the nbuf needs to be queued + * @nbuf_list - list of packets to be queued into the thread + * + * Enqueue packet into rx_thread and wake it up. The function + * moves the next pointer of the nbuf_list into the ext list of + * the first nbuf for storage into the thread. Only the first + * nbuf is queued into the thread nbuf queue. The reverse is + * done at the time of dequeue. + * + * Returns: QDF_STATUS_SUCCESS on success or qdf error code on + * failure + */ +static QDF_STATUS dp_rx_tm_thread_enqueue(struct dp_rx_thread *rx_thread, + qdf_nbuf_t nbuf_list) +{ + qdf_nbuf_t head_ptr, next_ptr_list; + uint32_t temp_qlen; + uint32_t num_elements_in_nbuf; + uint32_t nbuf_queued; + struct dp_rx_tm_handle_cmn *tm_handle_cmn; + uint8_t reo_ring_num = QDF_NBUF_CB_RX_CTX_ID(nbuf_list); + qdf_wait_queue_head_t *wait_q_ptr; + uint8_t allow_dropping; + + tm_handle_cmn = rx_thread->rtm_handle_cmn; + + if (!tm_handle_cmn) { + dp_alert("tm_handle_cmn is null!"); + QDF_BUG(0); + return QDF_STATUS_E_FAILURE; + } + + wait_q_ptr = &rx_thread->wait_q; + + if (reo_ring_num >= DP_RX_TM_MAX_REO_RINGS) { + dp_alert("incorrect ring %u", reo_ring_num); + QDF_BUG(0); + return QDF_STATUS_E_FAILURE; + } + + num_elements_in_nbuf = QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(nbuf_list); + nbuf_queued = num_elements_in_nbuf; + + allow_dropping = qdf_atomic_read( + &((struct dp_rx_tm_handle *)tm_handle_cmn)->allow_dropping); + if (unlikely(allow_dropping)) { + qdf_nbuf_list_free(nbuf_list); + rx_thread->stats.dropped_enq_fail += num_elements_in_nbuf; + nbuf_queued = 0; + goto enq_done; + } + + dp_rx_tm_walk_skb_list(nbuf_list); + + head_ptr = nbuf_list; + + /* Ensure head doesn't have an ext list */ + while (qdf_unlikely(head_ptr && qdf_nbuf_get_ext_list(head_ptr))) { + QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(head_ptr) = 1; + num_elements_in_nbuf--; + next_ptr_list = head_ptr->next; + qdf_nbuf_set_next(head_ptr, NULL); + qdf_nbuf_queue_head_enqueue_tail(&rx_thread->nbuf_queue, + head_ptr); + head_ptr = next_ptr_list; + } + + if (!head_ptr) + goto enq_done; + + QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(head_ptr) = num_elements_in_nbuf; + + next_ptr_list = head_ptr->next; + + if (next_ptr_list) { + /* move ->next pointer to ext list */ + qdf_nbuf_append_ext_list(head_ptr, next_ptr_list, 0); + dp_debug("appended next_ptr_list %pK to nbuf %pK ext list %pK", + qdf_nbuf_next(nbuf_list), nbuf_list, + qdf_nbuf_get_ext_list(nbuf_list)); + } + qdf_nbuf_set_next(head_ptr, NULL); + + qdf_nbuf_queue_head_enqueue_tail(&rx_thread->nbuf_queue, head_ptr); + +enq_done: + temp_qlen = qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue); + + rx_thread->stats.nbuf_queued[reo_ring_num] += nbuf_queued; + rx_thread->stats.nbuf_queued_total += nbuf_queued; + + dp_check_and_update_pending(tm_handle_cmn); + + if (temp_qlen > rx_thread->stats.nbufq_max_len) + rx_thread->stats.nbufq_max_len = temp_qlen; + + dp_debug("enqueue packet thread %pK wait queue %pK qlen %u", + rx_thread, wait_q_ptr, + qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue)); + + qdf_set_bit(RX_POST_EVENT, &rx_thread->event_flag); + qdf_wake_up_interruptible(wait_q_ptr); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS dp_rx_tm_thread_gro_flush_ind(struct dp_rx_thread *rx_thread) +{ + struct dp_rx_tm_handle_cmn *tm_handle_cmn; + qdf_wait_queue_head_t *wait_q_ptr; + + tm_handle_cmn = rx_thread->rtm_handle_cmn; + wait_q_ptr = &rx_thread->wait_q; + + qdf_atomic_set(&rx_thread->gro_flush_ind, 1); + + dp_debug("Flush indication received"); + + qdf_set_bit(RX_POST_EVENT, &rx_thread->event_flag); + qdf_wake_up_interruptible(wait_q_ptr); + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_thread_adjust_nbuf_list() - create an nbuf list from the frag list + * @head - nbuf list to be created + * + * Returns: void + */ +static void dp_rx_thread_adjust_nbuf_list(qdf_nbuf_t head) +{ + qdf_nbuf_t next_ptr_list, nbuf_list; + + nbuf_list = head; + if (head && QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(head) > 1) { + /* move ext list to ->next pointer */ + next_ptr_list = qdf_nbuf_get_ext_list(head); + qdf_nbuf_append_ext_list(head, NULL, 0); + qdf_nbuf_set_next(nbuf_list, next_ptr_list); + dp_rx_tm_walk_skb_list(nbuf_list); + } +} + +/** + * dp_rx_tm_thread_dequeue() - dequeue nbuf list from rx_thread + * @rx_thread - rx_thread from which the nbuf needs to be dequeued + * + * Returns: nbuf or nbuf_list dequeued from rx_thread + */ +static qdf_nbuf_t dp_rx_tm_thread_dequeue(struct dp_rx_thread *rx_thread) +{ + qdf_nbuf_t head; + + head = qdf_nbuf_queue_head_dequeue(&rx_thread->nbuf_queue); + dp_rx_thread_adjust_nbuf_list(head); + + dp_debug("Dequeued %pK nbuf_list", head); + return head; +} + +/** + * dp_rx_thread_process_nbufq() - process nbuf queue of a thread + * @rx_thread - rx_thread whose nbuf queue needs to be processed + * + * Returns: 0 on success, error code on failure + */ +static int dp_rx_thread_process_nbufq(struct dp_rx_thread *rx_thread) +{ + qdf_nbuf_t nbuf_list; + uint8_t vdev_id; + ol_txrx_rx_fp stack_fn; + ol_osif_vdev_handle osif_vdev; + ol_txrx_soc_handle soc; + uint32_t num_list_elements = 0; + + struct dp_txrx_handle_cmn *txrx_handle_cmn; + + txrx_handle_cmn = + dp_rx_thread_get_txrx_handle(rx_thread->rtm_handle_cmn); + + soc = dp_txrx_get_soc_from_ext_handle(txrx_handle_cmn); + if (!soc) { + dp_err("invalid soc!"); + QDF_BUG(0); + return -EFAULT; + } + + dp_debug("enter: qlen %u", + qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue)); + + nbuf_list = dp_rx_tm_thread_dequeue(rx_thread); + while (nbuf_list) { + num_list_elements = + QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(nbuf_list); + rx_thread->stats.nbuf_dequeued += num_list_elements; + + vdev_id = QDF_NBUF_CB_RX_VDEV_ID(nbuf_list); + cdp_get_os_rx_handles_from_vdev(soc, vdev_id, &stack_fn, + &osif_vdev); + dp_debug("rx_thread %pK sending packet %pK to stack", + rx_thread, nbuf_list); + if (!stack_fn || !osif_vdev || + QDF_STATUS_SUCCESS != stack_fn(osif_vdev, nbuf_list)) { + rx_thread->stats.dropped_invalid_os_rx_handles += + num_list_elements; + qdf_nbuf_list_free(nbuf_list); + } else { + rx_thread->stats.nbuf_sent_to_stack += + num_list_elements; + } + nbuf_list = dp_rx_tm_thread_dequeue(rx_thread); + } + + dp_debug("exit: qlen %u", + qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue)); + + return 0; +} + +/** + * dp_rx_thread_gro_flush() - flush GRO packets for the RX thread + * @rx_thread - rx_thread to be processed + * + * Returns: void + */ +static void dp_rx_thread_gro_flush(struct dp_rx_thread *rx_thread) +{ + dp_debug("flushing packets for thread %u", rx_thread->id); + + local_bh_disable(); + napi_gro_flush(&rx_thread->napi, false); + local_bh_enable(); + + rx_thread->stats.gro_flushes++; +} + +/** + * dp_rx_thread_sub_loop() - rx thread subloop + * @rx_thread - rx_thread to be processed + * @shutdown - pointer to shutdown variable + * + * The function handles shutdown and suspend events from other + * threads and processes nbuf queue of a rx thread. In case a + * shutdown event is received from some other wlan thread, the + * function sets the shutdown pointer to true and returns + * + * Returns: 0 on success, error code on failure + */ +static int dp_rx_thread_sub_loop(struct dp_rx_thread *rx_thread, bool *shutdown) +{ + while (true) { + if (qdf_atomic_test_and_clear_bit(RX_SHUTDOWN_EVENT, + &rx_thread->event_flag)) { + if (qdf_atomic_test_and_clear_bit(RX_SUSPEND_EVENT, + &rx_thread->event_flag)) { + qdf_event_set(&rx_thread->suspend_event); + } + dp_debug("shutting down (%s) id %d pid %d", + qdf_get_current_comm(), rx_thread->id, + qdf_get_current_pid()); + *shutdown = true; + break; + } + + dp_rx_thread_process_nbufq(rx_thread); + + if (qdf_atomic_read(&rx_thread->gro_flush_ind) | + qdf_atomic_test_bit(RX_VDEV_DEL_EVENT, + &rx_thread->event_flag)) { + dp_rx_thread_gro_flush(rx_thread); + qdf_atomic_set(&rx_thread->gro_flush_ind, 0); + } + + if (qdf_atomic_test_and_clear_bit(RX_VDEV_DEL_EVENT, + &rx_thread->event_flag)) { + rx_thread->stats.gro_flushes_by_vdev_del++; + qdf_event_set(&rx_thread->vdev_del_event); + } + + if (qdf_atomic_test_and_clear_bit(RX_SUSPEND_EVENT, + &rx_thread->event_flag)) { + dp_debug("received suspend ind (%s) id %d pid %d", + qdf_get_current_comm(), rx_thread->id, + qdf_get_current_pid()); + qdf_event_set(&rx_thread->suspend_event); + dp_debug("waiting for resume (%s) id %d pid %d", + qdf_get_current_comm(), rx_thread->id, + qdf_get_current_pid()); + qdf_wait_single_event(&rx_thread->resume_event, 0); + } + break; + } + return 0; +} + +/** + * dp_rx_thread_loop() - main dp rx thread loop + * @arg: pointer to dp_rx_thread structure for the rx thread + * + * Return: thread exit code + */ +static int dp_rx_thread_loop(void *arg) +{ + struct dp_rx_thread *rx_thread = arg; + bool shutdown = false; + int status; + struct dp_rx_tm_handle_cmn *tm_handle_cmn; + + if (!arg) { + dp_err("bad Args passed"); + return 0; + } + + tm_handle_cmn = rx_thread->rtm_handle_cmn; + + qdf_set_user_nice(qdf_get_current_task(), -1); + qdf_set_wake_up_idle(true); + + qdf_event_set(&rx_thread->start_event); + dp_info("starting rx_thread (%s) id %d pid %d", qdf_get_current_comm(), + rx_thread->id, qdf_get_current_pid()); + while (!shutdown) { + /* This implements the execution model algorithm */ + dp_debug("sleeping"); + status = + qdf_wait_queue_interruptible + (rx_thread->wait_q, + qdf_atomic_test_bit(RX_POST_EVENT, + &rx_thread->event_flag) || + qdf_atomic_test_bit(RX_SUSPEND_EVENT, + &rx_thread->event_flag) || + qdf_atomic_test_bit(RX_VDEV_DEL_EVENT, + &rx_thread->event_flag)); + dp_debug("woken up"); + + if (status == -ERESTARTSYS) { + QDF_DEBUG_PANIC("wait_event_interruptible returned -ERESTARTSYS"); + break; + } + qdf_atomic_clear_bit(RX_POST_EVENT, &rx_thread->event_flag); + dp_rx_thread_sub_loop(rx_thread, &shutdown); + } + + /* If we get here the scheduler thread must exit */ + dp_info("exiting (%s) id %d pid %d", qdf_get_current_comm(), + rx_thread->id, qdf_get_current_pid()); + qdf_event_set(&rx_thread->shutdown_event); + qdf_exit_thread(QDF_STATUS_SUCCESS); + + return 0; +} + +/** + * dp_rx_tm_thread_napi_poll() - dummy napi poll for rx_thread NAPI + * @napi: pointer to DP rx_thread NAPI + * @budget: NAPI BUDGET + * + * Return: 0 as it is not supposed to be polled at all as it is not scheduled. + */ +static int dp_rx_tm_thread_napi_poll(struct napi_struct *napi, int budget) +{ + QDF_DEBUG_PANIC("this napi_poll should not be polled as we don't schedule it"); + + return 0; +} + +/** + * dp_rx_tm_thread_napi_init() - Initialize dummy rx_thread NAPI + * @rx_thread: dp_rx_thread structure containing dummy napi and netdev + * + * Return: None + */ +static void dp_rx_tm_thread_napi_init(struct dp_rx_thread *rx_thread) +{ + /* Todo - optimize to use only one dummy netdev for all thread napis */ + init_dummy_netdev(&rx_thread->netdev); + netif_napi_add(&rx_thread->netdev, &rx_thread->napi, + dp_rx_tm_thread_napi_poll, 64); + napi_enable(&rx_thread->napi); +} + +/** + * dp_rx_tm_thread_napi_deinit() - De-initialize dummy rx_thread NAPI + * @rx_thread: dp_rx_thread handle containing dummy napi and netdev + * + * Return: None + */ +static void dp_rx_tm_thread_napi_deinit(struct dp_rx_thread *rx_thread) +{ + netif_napi_del(&rx_thread->napi); +} + +/* + * dp_rx_tm_thread_init() - Initialize dp_rx_thread structure and thread + * + * @rx_thread: dp_rx_thread structure to be initialized + * @id: id of the thread to be initialized + * + * Return: QDF_STATUS on success, QDF error code on failure + */ +static QDF_STATUS dp_rx_tm_thread_init(struct dp_rx_thread *rx_thread, + uint8_t id) +{ + char thread_name[15]; + QDF_STATUS qdf_status; + + qdf_mem_zero(thread_name, sizeof(thread_name)); + + if (!rx_thread) { + dp_err("rx_thread is null!"); + return QDF_STATUS_E_FAULT; + } + rx_thread->id = id; + rx_thread->event_flag = 0; + qdf_nbuf_queue_head_init(&rx_thread->nbuf_queue); + qdf_event_create(&rx_thread->start_event); + qdf_event_create(&rx_thread->suspend_event); + qdf_event_create(&rx_thread->resume_event); + qdf_event_create(&rx_thread->shutdown_event); + qdf_event_create(&rx_thread->vdev_del_event); + qdf_atomic_init(&rx_thread->gro_flush_ind); + qdf_init_waitqueue_head(&rx_thread->wait_q); + qdf_scnprintf(thread_name, sizeof(thread_name), "dp_rx_thread_%u", id); + dp_info("%s %u", thread_name, id); + + if (cdp_cfg_get(dp_rx_tm_get_soc_handle(rx_thread->rtm_handle_cmn), + cfg_dp_gro_enable)) + dp_rx_tm_thread_napi_init(rx_thread); + + rx_thread->task = qdf_create_thread(dp_rx_thread_loop, + rx_thread, thread_name); + if (!rx_thread->task) { + dp_err("could not create dp_rx_thread %d", id); + return QDF_STATUS_E_FAILURE; + } + + qdf_wake_up_process(rx_thread->task); + qdf_status = qdf_wait_single_event(&rx_thread->start_event, 0); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + dp_err("failed waiting for thread creation id %d", id); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/* + * dp_rx_tm_thread_deinit() - De-Initialize dp_rx_thread structure and thread + * @rx_thread: dp_rx_thread structure to be de-initialized + * @id: id of the thread to be initialized + * + * Return: QDF_STATUS_SUCCESS + */ +static QDF_STATUS dp_rx_tm_thread_deinit(struct dp_rx_thread *rx_thread) +{ + qdf_event_destroy(&rx_thread->start_event); + qdf_event_destroy(&rx_thread->suspend_event); + qdf_event_destroy(&rx_thread->resume_event); + qdf_event_destroy(&rx_thread->shutdown_event); + qdf_event_destroy(&rx_thread->vdev_del_event); + + if (cdp_cfg_get(dp_rx_tm_get_soc_handle(rx_thread->rtm_handle_cmn), + cfg_dp_gro_enable)) + dp_rx_tm_thread_napi_deinit(rx_thread); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dp_rx_tm_init(struct dp_rx_tm_handle *rx_tm_hdl, + uint8_t num_dp_rx_threads) +{ + int i; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + if (num_dp_rx_threads > DP_MAX_RX_THREADS) { + dp_err("unable to initialize %u number of threads. MAX %u", + num_dp_rx_threads, DP_MAX_RX_THREADS); + return QDF_STATUS_E_INVAL; + } + + rx_tm_hdl->num_dp_rx_threads = num_dp_rx_threads; + rx_tm_hdl->state = DP_RX_THREADS_INVALID; + + dp_info("initializing %u threads", num_dp_rx_threads); + + /* allocate an array to contain the DP RX thread pointers */ + rx_tm_hdl->rx_thread = qdf_mem_malloc(num_dp_rx_threads * + sizeof(struct dp_rx_thread *)); + + if (qdf_unlikely(!rx_tm_hdl->rx_thread)) { + qdf_status = QDF_STATUS_E_NOMEM; + goto ret; + } + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + rx_tm_hdl->rx_thread[i] = + (struct dp_rx_thread *) + qdf_mem_malloc(sizeof(struct dp_rx_thread)); + if (qdf_unlikely(!rx_tm_hdl->rx_thread[i])) { + QDF_ASSERT(0); + qdf_status = QDF_STATUS_E_NOMEM; + goto ret; + } + rx_tm_hdl->rx_thread[i]->rtm_handle_cmn = + (struct dp_rx_tm_handle_cmn *)rx_tm_hdl; + qdf_status = + dp_rx_tm_thread_init(rx_tm_hdl->rx_thread[i], i); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + break; + } +ret: + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + dp_rx_tm_deinit(rx_tm_hdl); + else + rx_tm_hdl->state = DP_RX_THREADS_RUNNING; + + return qdf_status; +} + +/** + * dp_rx_tm_resume() - suspend DP RX threads + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread + * infrastructure + * + * Return: Success/Failure + */ +QDF_STATUS dp_rx_tm_suspend(struct dp_rx_tm_handle *rx_tm_hdl) +{ + int i; + QDF_STATUS qdf_status; + struct dp_rx_thread *rx_thread; + + if (rx_tm_hdl->state == DP_RX_THREADS_SUSPENDED) { + dp_info("already in suspend state! Ignoring."); + return QDF_STATUS_E_INVAL; + } + + rx_tm_hdl->state = DP_RX_THREADS_SUSPENDING; + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + if (!rx_tm_hdl->rx_thread[i]) + continue; + qdf_event_reset(&rx_tm_hdl->rx_thread[i]->resume_event); + qdf_event_reset(&rx_tm_hdl->rx_thread[i]->suspend_event); + qdf_set_bit(RX_SUSPEND_EVENT, + &rx_tm_hdl->rx_thread[i]->event_flag); + qdf_wake_up_interruptible(&rx_tm_hdl->rx_thread[i]->wait_q); + } + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + rx_thread = rx_tm_hdl->rx_thread[i]; + if (!rx_thread) + continue; + dp_debug("thread %d", i); + qdf_status = qdf_wait_single_event(&rx_thread->suspend_event, + DP_RX_THREAD_WAIT_TIMEOUT); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + dp_debug("thread:%d suspended", rx_thread->id); + else + goto suspend_fail; + } + rx_tm_hdl->state = DP_RX_THREADS_SUSPENDED; + + return QDF_STATUS_SUCCESS; + +suspend_fail: + dp_err("thread:%d %s(%d) while waiting for suspend", + rx_thread->id, + qdf_status == QDF_STATUS_E_TIMEOUT ? "timeout out" : "failed", + qdf_status); + + dp_rx_tm_resume(rx_tm_hdl); + + return qdf_status; +} + +/** + * dp_rx_thread_flush_by_vdev_id() - flush rx packets by vdev_id in + a particular rx thread queue + * @rx_thread - rx_thread pointer of the queue from which packets are + * to be flushed out + * @vdev_id: vdev id for which packets are to be flushed + * + * The function will flush the RX packets by vdev_id in a particular + * RX thead queue. And will notify and wait the TX thread to flush the + * packets in the NAPI RX GRO hash list + * + * Return: void + */ +static inline +void dp_rx_thread_flush_by_vdev_id(struct dp_rx_thread *rx_thread, + uint8_t vdev_id) +{ + qdf_nbuf_t nbuf_list, tmp_nbuf_list; + uint32_t num_list_elements = 0; + QDF_STATUS qdf_status; + + qdf_nbuf_queue_head_lock(&rx_thread->nbuf_queue); + QDF_NBUF_QUEUE_WALK_SAFE(&rx_thread->nbuf_queue, nbuf_list, + tmp_nbuf_list) { + if (QDF_NBUF_CB_RX_VDEV_ID(nbuf_list) == vdev_id) { + qdf_nbuf_unlink_no_lock(nbuf_list, + &rx_thread->nbuf_queue); + dp_rx_thread_adjust_nbuf_list(nbuf_list); + num_list_elements = + QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(nbuf_list); + rx_thread->stats.rx_flushed += num_list_elements; + qdf_nbuf_list_free(nbuf_list); + } + } + qdf_nbuf_queue_head_unlock(&rx_thread->nbuf_queue); + + qdf_event_reset(&rx_thread->vdev_del_event); + qdf_set_bit(RX_VDEV_DEL_EVENT, &rx_thread->event_flag); + qdf_wake_up_interruptible(&rx_thread->wait_q); + + qdf_status = qdf_wait_single_event(&rx_thread->vdev_del_event, + DP_RX_THREAD_WAIT_TIMEOUT); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + dp_debug("thread:%d napi gro flush successfully", + rx_thread->id); + else if (qdf_status == QDF_STATUS_E_TIMEOUT) + dp_err("thread:%d timed out waiting for napi gro flush", + rx_thread->id); + else + dp_err("thread:%d failed while waiting for napi gro flush", + rx_thread->id); +} + +/** + * dp_rx_tm_flush_by_vdev_id() - flush rx packets by vdev_id in all + rx thread queues + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread + * infrastructure + * @vdev_id: vdev id for which packets are to be flushed + * + * Return: QDF_STATUS_SUCCESS + */ +QDF_STATUS dp_rx_tm_flush_by_vdev_id(struct dp_rx_tm_handle *rx_tm_hdl, + uint8_t vdev_id) +{ + struct dp_rx_thread *rx_thread; + int i; + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + rx_thread = rx_tm_hdl->rx_thread[i]; + if (!rx_thread) + continue; + + dp_debug("thread %d", i); + dp_rx_thread_flush_by_vdev_id(rx_thread, vdev_id); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_tm_resume() - resume DP RX threads + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread + * infrastructure + * + * Return: QDF_STATUS_SUCCESS on resume success. QDF error otherwise. + */ +QDF_STATUS dp_rx_tm_resume(struct dp_rx_tm_handle *rx_tm_hdl) +{ + int i; + + if (rx_tm_hdl->state != DP_RX_THREADS_SUSPENDED && + rx_tm_hdl->state != DP_RX_THREADS_SUSPENDING) { + dp_info("resume callback received w/o suspend! Ignoring."); + return QDF_STATUS_E_INVAL; + } + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + if (!rx_tm_hdl->rx_thread[i]) + continue; + dp_debug("calling thread %d to resume", i); + + /* postively reset event_flag for DP_RX_THREADS_SUSPENDING + * state + */ + qdf_clear_bit(RX_SUSPEND_EVENT, + &rx_tm_hdl->rx_thread[i]->event_flag); + qdf_event_set(&rx_tm_hdl->rx_thread[i]->resume_event); + } + + rx_tm_hdl->state = DP_RX_THREADS_RUNNING; + + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_tm_shutdown() - shutdown all DP RX threads + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure + * + * Return: QDF_STATUS_SUCCESS + */ +static QDF_STATUS dp_rx_tm_shutdown(struct dp_rx_tm_handle *rx_tm_hdl) +{ + int i; + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + if (!rx_tm_hdl->rx_thread[i]) + continue; + qdf_set_bit(RX_SHUTDOWN_EVENT, + &rx_tm_hdl->rx_thread[i]->event_flag); + qdf_set_bit(RX_POST_EVENT, + &rx_tm_hdl->rx_thread[i]->event_flag); + qdf_wake_up_interruptible(&rx_tm_hdl->rx_thread[i]->wait_q); + } + + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + if (!rx_tm_hdl->rx_thread[i]) + continue; + dp_debug("waiting for shutdown of thread %d", i); + qdf_wait_single_event(&rx_tm_hdl->rx_thread[i]->shutdown_event, + 0); + } + rx_tm_hdl->state = DP_RX_THREADS_INVALID; + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_tm_deinit() - de-initialize RX thread infrastructure + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread + * infrastructure + * + * Return: QDF_STATUS_SUCCESS + */ +QDF_STATUS dp_rx_tm_deinit(struct dp_rx_tm_handle *rx_tm_hdl) +{ + int i = 0; + if (!rx_tm_hdl->rx_thread) { + dp_err("rx_tm_hdl->rx_thread not initialized!"); + return QDF_STATUS_SUCCESS; + } + + dp_rx_tm_shutdown(rx_tm_hdl); + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + if (!rx_tm_hdl->rx_thread[i]) + continue; + dp_rx_tm_thread_deinit(rx_tm_hdl->rx_thread[i]); + qdf_mem_free(rx_tm_hdl->rx_thread[i]); + } + + /* free the array of RX thread pointers*/ + qdf_mem_free(rx_tm_hdl->rx_thread); + rx_tm_hdl->rx_thread = NULL; + + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_tm_select_thread() - select a DP RX thread for a nbuf + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread + * infrastructure + * @reo_ring_num: REO ring number corresponding to the thread + * + * The function relies on the presence of QDF_NBUF_CB_RX_CTX_ID passed to it + * from the nbuf list. Depending on the RX_CTX (copy engine or reo + * ring) on which the packet was received, the function selects + * a corresponding rx_thread. + * + * Return: rx thread ID selected for the nbuf + */ +static uint8_t dp_rx_tm_select_thread(struct dp_rx_tm_handle *rx_tm_hdl, + uint8_t reo_ring_num) +{ + uint8_t selected_rx_thread; + + if (reo_ring_num >= rx_tm_hdl->num_dp_rx_threads) { + dp_err_rl("unexpected ring number"); + QDF_BUG(0); + return 0; + } + + selected_rx_thread = reo_ring_num; + dp_debug("selected thread %u", selected_rx_thread); + return selected_rx_thread; +} + +QDF_STATUS dp_rx_tm_enqueue_pkt(struct dp_rx_tm_handle *rx_tm_hdl, + qdf_nbuf_t nbuf_list) +{ + uint8_t selected_thread_id; + + selected_thread_id = + dp_rx_tm_select_thread(rx_tm_hdl, + QDF_NBUF_CB_RX_CTX_ID(nbuf_list)); + dp_rx_tm_thread_enqueue(rx_tm_hdl->rx_thread[selected_thread_id], + nbuf_list); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +dp_rx_tm_gro_flush_ind(struct dp_rx_tm_handle *rx_tm_hdl, int rx_ctx_id) +{ + uint8_t selected_thread_id; + + selected_thread_id = dp_rx_tm_select_thread(rx_tm_hdl, rx_ctx_id); + dp_rx_tm_thread_gro_flush_ind(rx_tm_hdl->rx_thread[selected_thread_id]); + + return QDF_STATUS_SUCCESS; +} + +struct napi_struct *dp_rx_tm_get_napi_context(struct dp_rx_tm_handle *rx_tm_hdl, + uint8_t rx_ctx_id) +{ + if (rx_ctx_id >= rx_tm_hdl->num_dp_rx_threads) { + dp_err_rl("unexpected rx_ctx_id %u", rx_ctx_id); + QDF_BUG(0); + return NULL; + } + + return &rx_tm_hdl->rx_thread[rx_ctx_id]->napi; +} + +QDF_STATUS dp_rx_tm_set_cpu_mask(struct dp_rx_tm_handle *rx_tm_hdl, + qdf_cpu_mask *new_mask) +{ + int i = 0; + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + if (!rx_tm_hdl->rx_thread[i]) + continue; + qdf_thread_set_cpus_allowed_mask(rx_tm_hdl->rx_thread[i]->task, + new_mask); + } + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_rx_thread.h b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_rx_thread.h new file mode 100644 index 0000000000000000000000000000000000000000..f7f8864f74560ba1c12c75fd6c33dcc1124264c4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_rx_thread.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__DP_RX_THREAD_H) +#define __DP_RX_THREAD_H + +#include +#include +#include +#include +/* Maximum number of REO rings supported (for stats tracking) */ +#define DP_RX_TM_MAX_REO_RINGS 4 + +/* Number of DP RX threads supported */ +#define DP_MAX_RX_THREADS DP_RX_TM_MAX_REO_RINGS + +/* + * struct dp_rx_tm_handle_cmn - Opaque handle for rx_threads to store + * rx_tm_handle. This handle will be common for all the threads. + * Individual threads should not be accessing + * elements from dp_rx_tm_handle. It should be via an API. + */ +struct dp_rx_tm_handle_cmn; + +/** + * struct dp_rx_thread_stats - structure holding stats for DP RX thread + * @nbuf_queued: packets queued into the thread per reo ring + * @nbuf_queued_total: packets queued into the thread for all reo rings + * @nbuf_dequeued: packets de-queued from the thread + * @nbuf_sent_to_stack: packets sent to the stack. some dequeued packets may be + * dropped due to no peer or vdev, hence this stat. + * @gro_flushes: number of GRO flushes + * @gro_flushes_by_vdev_del: number of GRO flushes triggered by vdev del. + * @nbufq_max_len: maximum number of nbuf_lists queued for the thread + * @dropped_invalid_vdev: packets(nbuf_list) dropped due to no vdev + * @rx_flushed: packets flushed after vdev delete + * @dropped_invalid_peer: packets(nbuf_list) dropped due to no peer + * @dropped_others: packets dropped due to other reasons + * @dropped_enq_fail: packets dropped due to pending queue full + */ +struct dp_rx_thread_stats { + unsigned int nbuf_queued[DP_RX_TM_MAX_REO_RINGS]; + unsigned int nbuf_queued_total; + unsigned int nbuf_dequeued; + unsigned int nbuf_sent_to_stack; + unsigned int gro_flushes; + unsigned int gro_flushes_by_vdev_del; + unsigned int nbufq_max_len; + unsigned int dropped_invalid_vdev; + unsigned int rx_flushed; + unsigned int dropped_invalid_peer; + unsigned int dropped_invalid_os_rx_handles; + unsigned int dropped_others; + unsigned int dropped_enq_fail; +}; + +/** + * struct dp_rx_thread - structure holding variables for a single DP RX thread + * @id: id of the dp_rx_thread (0 or 1 or 2..DP_MAX_RX_THREADS - 1) + * @task: task structure corresponding to the thread + * @start_event: handle of Event for DP Rx thread to signal startup + * @suspend_event: handle of Event for DP Rx thread to signal suspend + * @resume_event: handle of Event for DP Rx thread to signal resume + * @shutdown_event: handle of Event for DP Rx thread to signal shutdown + * @vdev_del_event: handle of Event for vdev del thread to signal completion + * for gro flush + * @event_flag: event flag to post events to DP Rx thread + * @nbuf_queue:nbuf queue used to store RX packets + * @nbufq_len: length of the nbuf queue + * @aff_mask: cuurent affinity mask of the DP Rx thread + * @stats: per thread stats + * @rtm_handle_cmn: abstract RX TM handle. This allows access to the dp_rx_tm + * structures via APIs. + * @napi: napi to deliver packet to stack via GRO + * @netdev: dummy netdev to initialize the napi structure with + */ +struct dp_rx_thread { + uint8_t id; + qdf_thread_t *task; + qdf_event_t start_event; + qdf_event_t suspend_event; + qdf_event_t resume_event; + qdf_event_t shutdown_event; + qdf_event_t vdev_del_event; + qdf_atomic_t gro_flush_ind; + unsigned long event_flag; + qdf_nbuf_queue_head_t nbuf_queue; + unsigned long aff_mask; + struct dp_rx_thread_stats stats; + struct dp_rx_tm_handle_cmn *rtm_handle_cmn; + struct napi_struct napi; + qdf_wait_queue_head_t wait_q; + struct net_device netdev; +}; + +/** + * enum dp_rx_thread_state - enum to keep track of the state of the rx threads + * @DP_RX_THREADS_INVALID: initial invalid state + * @DP_RX_THREADS_RUNNING: rx threads functional(NOT suspended, processing + * packets or waiting on a wait_queue) + * @DP_RX_THREADS_SUSPENDING: rx thread is suspending + * @DP_RX_THREADS_SUSPENDED: rx_threads suspended from cfg8011 suspend + */ +enum dp_rx_thread_state { + DP_RX_THREADS_INVALID, + DP_RX_THREADS_RUNNING, + DP_RX_THREADS_SUSPENDING, + DP_RX_THREADS_SUSPENDED +}; + +/** + * struct dp_rx_tm_handle - DP RX thread infrastructure handle + * @num_dp_rx_threads: number of DP RX threads initialized + * @txrx_handle_cmn: opaque txrx handle to get to pdev and soc + * @state: state of the rx_threads. All of them should be in the same state. + * @rx_thread: array of pointers of type struct dp_rx_thread + * @allow_dropping: flag to indicate frame dropping is enabled + */ +struct dp_rx_tm_handle { + uint8_t num_dp_rx_threads; + struct dp_txrx_handle_cmn *txrx_handle_cmn; + enum dp_rx_thread_state state; + struct dp_rx_thread **rx_thread; + qdf_atomic_t allow_dropping; +}; + +/** + * dp_rx_tm_init() - initialize DP Rx thread infrastructure + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure + * @num_dp_rx_threads: number of DP Rx threads to be initialized + * + * Return: QDF_STATUS_SUCCESS + */ +QDF_STATUS dp_rx_tm_init(struct dp_rx_tm_handle *rx_tm_hdl, + uint8_t num_dp_rx_threads); + +/** + * dp_rx_tm_deinit() - de-initialize DP Rx thread infrastructure + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS dp_rx_tm_deinit(struct dp_rx_tm_handle *rx_tm_hdl); + +/** + * dp_rx_tm_enqueue_pkt() - enqueue RX packet into RXTI + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure + * @nbuf_list: single or a list of nbufs to be enqueued into RXTI + * + * Return: QDF_STATUS_SUCCESS + */ +QDF_STATUS dp_rx_tm_enqueue_pkt(struct dp_rx_tm_handle *rx_tm_hdl, + qdf_nbuf_t nbuf_list); + +/** + * dp_rx_tm_gro_flush_ind() - flush GRO packets for a RX Context Id + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure + * @rx_ctx_id: RX Thread Contex Id for which GRO flush needs to be done + * + * Return: QDF_STATUS_SUCCESS + */ +QDF_STATUS dp_rx_tm_gro_flush_ind(struct dp_rx_tm_handle *rx_tm_handle, + int rx_ctx_id); + +/** + * dp_rx_tm_suspend() - suspend all threads in RXTI + * @rx_tm_handle: pointer to dp_rx_tm_handle object + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS dp_rx_tm_suspend(struct dp_rx_tm_handle *rx_tm_handle); + +/** + * dp_rx_tm_flush_by_vdev_id() - flush rx packets by vdev_id in all + rx thread queues + * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread + * infrastructure + * @vdev_id: vdev id for which packets are to be flushed + * + * Return: QDF_STATUS_SUCCESS + */ +QDF_STATUS dp_rx_tm_flush_by_vdev_id(struct dp_rx_tm_handle *rx_tm_hdl, + uint8_t vdev_id); + +/** + * dp_rx_tm_resume() - resume all threads in RXTI + * @rx_tm_handle: pointer to dp_rx_tm_handle object + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS dp_rx_tm_resume(struct dp_rx_tm_handle *rx_tm_handle); + +/** + * dp_rx_tm_dump_stats() - dump stats for all threads in RXTI + * @rx_tm_handle: pointer to dp_rx_tm_handle object + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS dp_rx_tm_dump_stats(struct dp_rx_tm_handle *rx_tm_handle); + +/** + * dp_rx_thread_get_txrx_handle() - get txrx handle from rx_tm_handle_cmn + * @rx_tm_handle_cmn: opaque pointer to dp_rx_tm_handle_cmn struct + * + * Return: pointer to dp_txrx_handle_cmn handle + */ +static inline struct dp_txrx_handle_cmn* +dp_rx_thread_get_txrx_handle(struct dp_rx_tm_handle_cmn *rx_tm_handle_cmn) +{ + return (((struct dp_rx_tm_handle *)rx_tm_handle_cmn)->txrx_handle_cmn); +} + +/** + * dp_rx_tm_get_napi_context() - get NAPI context for a RX CTX ID + * @soc: ol_txrx_soc_handle object + * @rx_ctx_id: RX context ID (RX thread ID) corresponding to which NAPI is + * needed + * + * Return: NULL on failure, else pointer to NAPI corresponding to rx_ctx_id + */ +struct napi_struct *dp_rx_tm_get_napi_context(struct dp_rx_tm_handle *rx_tm_hdl, + uint8_t rx_ctx_id); + +/** + * dp_rx_tm_set_cpu_mask() - set CPU mask for RX threads + * @soc: ol_txrx_soc_handle object + * @new_mask: New CPU mask pointer + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS dp_rx_tm_set_cpu_mask(struct dp_rx_tm_handle *rx_tm_hdl, + qdf_cpu_mask *new_mask); + +#endif /* __DP_RX_THREAD_H */ diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_txrx.c b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_txrx.c new file mode 100644 index 0000000000000000000000000000000000000000..a830ed3796353335c71346b61df413431255c5db --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_txrx.c @@ -0,0 +1,753 @@ +/* + * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "dp_types.h" +#include +#include +#include +#include "dp_tx_desc.h" +#include "dp_rx.h" +#include +#include + +QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, uint8_t pdev_id, + struct dp_txrx_config *config) +{ + struct dp_txrx_handle *dp_ext_hdl; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + uint8_t num_dp_rx_threads; + struct dp_pdev *pdev; + + if (qdf_unlikely(!soc)) { + dp_err("soc is NULL"); + return 0; + } + + pdev = dp_get_pdev_from_soc_pdev_id_wifi3(cdp_soc_t_to_dp_soc(soc), + pdev_id); + if (!pdev) { + dp_err("pdev is NULL"); + return 0; + } + + dp_ext_hdl = qdf_mem_malloc(sizeof(*dp_ext_hdl)); + if (!dp_ext_hdl) { + QDF_ASSERT(0); + return QDF_STATUS_E_NOMEM; + } + + dp_info("dp_txrx_handle allocated"); + dp_ext_hdl->soc = soc; + dp_ext_hdl->pdev = dp_pdev_to_cdp_pdev(pdev); + cdp_soc_set_dp_txrx_handle(soc, dp_ext_hdl); + qdf_mem_copy(&dp_ext_hdl->config, config, sizeof(*config)); + dp_ext_hdl->rx_tm_hdl.txrx_handle_cmn = + dp_txrx_get_cmn_hdl_frm_ext_hdl(dp_ext_hdl); + + num_dp_rx_threads = cdp_get_num_rx_contexts(soc); + + if (dp_ext_hdl->config.enable_rx_threads) { + qdf_status = dp_rx_tm_init(&dp_ext_hdl->rx_tm_hdl, + num_dp_rx_threads); + } + + return qdf_status; +} + +QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc) +{ + struct dp_txrx_handle *dp_ext_hdl; + + if (!soc) + return QDF_STATUS_E_INVAL; + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) + return QDF_STATUS_E_FAULT; + + if (dp_ext_hdl->config.enable_rx_threads) + dp_rx_tm_deinit(&dp_ext_hdl->rx_tm_hdl); + + qdf_mem_free(dp_ext_hdl); + dp_info("dp_txrx_handle_t de-allocated"); + + cdp_soc_set_dp_txrx_handle(soc, NULL); + + return QDF_STATUS_SUCCESS; +} + +#ifdef DP_MEM_PRE_ALLOC + +/* Num elements in REO ring */ +#define REO_DST_RING_SIZE 1024 + +/* Num elements in TCL Data ring */ +#define TCL_DATA_RING_SIZE 3072 + +/* Num elements in WBM2SW ring */ +#define WBM2SW_RELEASE_RING_SIZE 4096 + +/* Num elements in WBM Idle Link */ +#define WBM_IDLE_LINK_RING_SIZE (32 * 1024) + +/* Num TX desc in TX desc pool */ +#define DP_TX_DESC_POOL_SIZE 4096 + +/** + * struct dp_consistent_prealloc - element representing DP pre-alloc memory + * @ring_type: HAL ring type + * @size: size of pre-alloc memory + * @in_use: whether this element is in use (occupied) + * @va_unaligned: Unaligned virtual address + * @va_aligned: aligned virtual address. + * @pa_unaligned: Unaligned physical address. + * @pa_aligned: Aligned physical address. + */ + +struct dp_consistent_prealloc { + enum hal_ring_type ring_type; + uint32_t size; + uint8_t in_use; + void *va_unaligned; + void *va_aligned; + qdf_dma_addr_t pa_unaligned; + qdf_dma_addr_t pa_aligned; +}; + +/** + * struct dp_multi_page_prealloc - element representing DP pre-alloc multiple + pages memory + * @desc_type: source descriptor type for memory allocation + * @element_size: single element size + * @element_num: total number of elements should be allocated + * @in_use: whether this element is in use (occupied) + * @cacheable: coherent memory or cacheable memory + * @pages: multi page information storage + */ +struct dp_multi_page_prealloc { + enum dp_desc_type desc_type; + size_t element_size; + uint16_t element_num; + bool in_use; + bool cacheable; + struct qdf_mem_multi_page_t pages; +}; + +/** + * struct dp_consistent_prealloc_unaligned - element representing DP pre-alloc + unaligned memory + * @ring_type: HAL ring type + * @size: size of pre-alloc memory + * @in_use: whether this element is in use (occupied) + * @va_unaligned: unaligned virtual address + * @pa_unaligned: unaligned physical address + */ +struct dp_consistent_prealloc_unaligned { + enum hal_ring_type ring_type; + uint32_t size; + bool in_use; + void *va_unaligned; + qdf_dma_addr_t pa_unaligned; +}; + +/** + * struct dp_prealloc_context - element representing DP prealloc context memory + * @ctxt_type: DP context type + * @size: size of pre-alloc memory + * @in_use: check if element is being used + * @addr: address of memory allocated + */ +struct dp_prealloc_context { + enum dp_ctxt_type ctxt_type; + uint32_t size; + bool in_use; + void *addr; +}; + +static struct dp_prealloc_context g_dp_context_allocs[] = { + {DP_PDEV_TYPE, (sizeof(struct dp_pdev)), false, NULL}, +#ifdef WLAN_FEATURE_DP_RX_RING_HISTORY + /* 4 Rx ring history */ + {DP_RX_RING_HIST_TYPE, sizeof(struct dp_rx_history), false, NULL}, + {DP_RX_RING_HIST_TYPE, sizeof(struct dp_rx_history), false, NULL}, + {DP_RX_RING_HIST_TYPE, sizeof(struct dp_rx_history), false, NULL}, + {DP_RX_RING_HIST_TYPE, sizeof(struct dp_rx_history), false, NULL}, + /* 1 Rx error ring history */ + {DP_RX_ERR_RING_HIST_TYPE, sizeof(struct dp_rx_err_history), + false, NULL}, +#ifndef RX_DEFRAG_DO_NOT_REINJECT + /* 1 Rx reinject ring history */ + {DP_RX_REINJECT_RING_HIST_TYPE, sizeof(struct dp_rx_reinject_history), + false, NULL}, +#endif /* RX_DEFRAG_DO_NOT_REINJECT */ +#endif /* WLAN_FEATURE_DP_RX_RING_HISTORY */ +}; + +static struct dp_consistent_prealloc g_dp_consistent_allocs[] = { + /* 5 REO DST rings */ + {REO_DST, (sizeof(struct reo_destination_ring)) * REO_DST_RING_SIZE, 0, NULL, NULL, 0, 0}, + {REO_DST, (sizeof(struct reo_destination_ring)) * REO_DST_RING_SIZE, 0, NULL, NULL, 0, 0}, + {REO_DST, (sizeof(struct reo_destination_ring)) * REO_DST_RING_SIZE, 0, NULL, NULL, 0, 0}, + {REO_DST, (sizeof(struct reo_destination_ring)) * REO_DST_RING_SIZE, 0, NULL, NULL, 0, 0}, + {REO_DST, (sizeof(struct reo_destination_ring)) * REO_DST_RING_SIZE, 0, NULL, NULL, 0, 0}, + /* 3 TCL data rings */ + {TCL_DATA, (sizeof(struct tlv_32_hdr) + sizeof(struct tcl_data_cmd)) * TCL_DATA_RING_SIZE, 0, NULL, NULL, 0, 0}, + {TCL_DATA, (sizeof(struct tlv_32_hdr) + sizeof(struct tcl_data_cmd)) * TCL_DATA_RING_SIZE, 0, NULL, NULL, 0, 0}, + {TCL_DATA, (sizeof(struct tlv_32_hdr) + sizeof(struct tcl_data_cmd)) * TCL_DATA_RING_SIZE, 0, NULL, NULL, 0, 0}, + /* 4 WBM2SW rings */ + {WBM2SW_RELEASE, (sizeof(struct wbm_release_ring)) * WBM2SW_RELEASE_RING_SIZE, 0, NULL, NULL, 0, 0}, + {WBM2SW_RELEASE, (sizeof(struct wbm_release_ring)) * WBM2SW_RELEASE_RING_SIZE, 0, NULL, NULL, 0, 0}, + {WBM2SW_RELEASE, (sizeof(struct wbm_release_ring)) * WBM2SW_RELEASE_RING_SIZE, 0, NULL, NULL, 0, 0}, + {WBM2SW_RELEASE, (sizeof(struct wbm_release_ring)) * WBM2SW_RELEASE_RING_SIZE, 0, NULL, 0, 0}, + /* SW2WBM link descriptor return ring */ + {SW2WBM_RELEASE, (sizeof(struct wbm_release_ring)) * WLAN_CFG_WBM_RELEASE_RING_SIZE, 0, NULL, 0, 0}, + /* 1 WBM idle link desc ring */ + {WBM_IDLE_LINK, (sizeof(struct wbm_link_descriptor_ring)) * WBM_IDLE_LINK_RING_SIZE, 0, NULL, NULL, 0, 0}, + /* 2 RXDMA DST ERR rings */ + {RXDMA_DST, (sizeof(struct reo_entrance_ring)) * WLAN_CFG_RXDMA_ERR_DST_RING_SIZE, 0, NULL, NULL, 0, 0}, + {RXDMA_DST, (sizeof(struct reo_entrance_ring)) * WLAN_CFG_RXDMA_ERR_DST_RING_SIZE, 0, NULL, NULL, 0, 0}, + /* REFILL ring 0 */ + {RXDMA_BUF, (sizeof(struct wbm_buffer_ring)) * WLAN_CFG_RXDMA_REFILL_RING_SIZE, 0, NULL, NULL, 0, 0}, + +}; + +/* Number of HW link descriptors needed (rounded to power of 2) */ +#define NUM_HW_LINK_DESCS (32 * 1024) + +/* Size in bytes of HW LINK DESC */ +#define HW_LINK_DESC_SIZE 128 + +/* Size in bytes of TX Desc (rounded to power of 2) */ +#define TX_DESC_SIZE 128 + +/* Size in bytes of TX TSO Desc (rounded to power of 2) */ +#define TX_TSO_DESC_SIZE 256 + +/* Size in bytes of TX TSO Num Seg Desc (rounded to power of 2) */ +#define TX_TSO_NUM_SEG_DESC_SIZE 16 + +#define NON_CACHEABLE 0 +#define CACHEABLE 1 + +static struct dp_multi_page_prealloc g_dp_multi_page_allocs[] = { + /* 4 TX DESC pools */ + {DP_TX_DESC_TYPE, TX_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_DESC_TYPE, TX_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_DESC_TYPE, TX_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_DESC_TYPE, TX_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + + /* 4 Tx EXT DESC NON Cacheable pools */ + {DP_TX_EXT_DESC_TYPE, HAL_TX_EXT_DESC_WITH_META_DATA, DP_TX_DESC_POOL_SIZE, 0, NON_CACHEABLE, { 0 } }, + {DP_TX_EXT_DESC_TYPE, HAL_TX_EXT_DESC_WITH_META_DATA, DP_TX_DESC_POOL_SIZE, 0, NON_CACHEABLE, { 0 } }, + {DP_TX_EXT_DESC_TYPE, HAL_TX_EXT_DESC_WITH_META_DATA, DP_TX_DESC_POOL_SIZE, 0, NON_CACHEABLE, { 0 } }, + {DP_TX_EXT_DESC_TYPE, HAL_TX_EXT_DESC_WITH_META_DATA, DP_TX_DESC_POOL_SIZE, 0, NON_CACHEABLE, { 0 } }, + + /* 4 Tx EXT DESC Link Cacheable pools */ + {DP_TX_EXT_DESC_LINK_TYPE, sizeof(struct dp_tx_ext_desc_elem_s), DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_EXT_DESC_LINK_TYPE, sizeof(struct dp_tx_ext_desc_elem_s), DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_EXT_DESC_LINK_TYPE, sizeof(struct dp_tx_ext_desc_elem_s), DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_EXT_DESC_LINK_TYPE, sizeof(struct dp_tx_ext_desc_elem_s), DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + + /* 4 TX TSO DESC pools */ + {DP_TX_TSO_DESC_TYPE, TX_TSO_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_TSO_DESC_TYPE, TX_TSO_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_TSO_DESC_TYPE, TX_TSO_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_TSO_DESC_TYPE, TX_TSO_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + + /* 4 TX TSO NUM SEG DESC pools */ + {DP_TX_TSO_NUM_SEG_TYPE, TX_TSO_NUM_SEG_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_TSO_NUM_SEG_TYPE, TX_TSO_NUM_SEG_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_TSO_NUM_SEG_TYPE, TX_TSO_NUM_SEG_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + {DP_TX_TSO_NUM_SEG_TYPE, TX_TSO_NUM_SEG_DESC_SIZE, DP_TX_DESC_POOL_SIZE, 0, CACHEABLE, { 0 } }, + + /* DP RX DESCs BUF pools */ + {DP_RX_DESC_BUF_TYPE, sizeof(union dp_rx_desc_list_elem_t), + WLAN_CFG_RX_SW_DESC_WEIGHT_SIZE * WLAN_CFG_RXDMA_REFILL_RING_SIZE, 0, CACHEABLE, { 0 } }, + +#ifndef DISABLE_MON_CONFIG + /* 2 DP RX DESCs Status pools */ + {DP_RX_DESC_STATUS_TYPE, sizeof(union dp_rx_desc_list_elem_t), + WLAN_CFG_RXDMA_MONITOR_STATUS_RING_SIZE + 1, 0, CACHEABLE, { 0 } }, + {DP_RX_DESC_STATUS_TYPE, sizeof(union dp_rx_desc_list_elem_t), + WLAN_CFG_RXDMA_MONITOR_STATUS_RING_SIZE + 1, 0, CACHEABLE, { 0 } }, +#endif + + /* DP HW Link DESCs pools */ + {DP_HW_LINK_DESC_TYPE, HW_LINK_DESC_SIZE, NUM_HW_LINK_DESCS, 0, NON_CACHEABLE, { 0 } }, + +}; + +static struct dp_consistent_prealloc_unaligned + g_dp_consistent_unaligned_allocs[] = { + /* CE-0 */ + {CE_SRC, (sizeof(struct ce_srng_src_desc) * 16 + CE_DESC_RING_ALIGN), + false, NULL, 0}, + /* CE-1 */ + {CE_DST, (sizeof(struct ce_srng_dest_desc) * 512 + CE_DESC_RING_ALIGN), + false, NULL, 0}, + {CE_DST_STATUS, (sizeof(struct ce_srng_dest_status_desc) * 512 + + CE_DESC_RING_ALIGN), false, NULL, 0}, + /* CE-2 */ + {CE_DST, (sizeof(struct ce_srng_dest_desc) * 32 + CE_DESC_RING_ALIGN), + false, NULL, 0}, + {CE_DST_STATUS, (sizeof(struct ce_srng_dest_status_desc) * 32 + + CE_DESC_RING_ALIGN), false, NULL, 0}, + /* CE-3 */ + {CE_SRC, (sizeof(struct ce_srng_src_desc) * 32 + CE_DESC_RING_ALIGN), + false, NULL, 0}, + /* CE-4 */ + {CE_SRC, (sizeof(struct ce_srng_src_desc) * 256 + CE_DESC_RING_ALIGN), + false, NULL, 0}, + /* CE-5 */ + {CE_DST, (sizeof(struct ce_srng_dest_desc) * 512 + CE_DESC_RING_ALIGN), + false, NULL, 0}, + {CE_DST_STATUS, (sizeof(struct ce_srng_dest_status_desc) * 512 + + CE_DESC_RING_ALIGN), false, NULL, 0}, +}; + +void dp_prealloc_deinit(void) +{ + int i; + struct dp_prealloc_context *cp; + struct dp_consistent_prealloc *p; + struct dp_multi_page_prealloc *mp; + struct dp_consistent_prealloc_unaligned *up; + qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_consistent_allocs); i++) { + p = &g_dp_consistent_allocs[i]; + + if (qdf_unlikely(p->in_use)) + dp_info("i %d: consistent_mem in use while free", i); + + if (p->va_aligned) { + dp_info("i %d: va aligned %pK pa aligned %pK size %d", + i, p->va_aligned, (void *)p->pa_aligned, + p->size); + qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev, + p->size, + p->va_unaligned, + p->pa_unaligned, 0); + qdf_mem_zero(p, sizeof(*p)); + } + } + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_multi_page_allocs); i++) { + mp = &g_dp_multi_page_allocs[i]; + + if (qdf_unlikely(mp->in_use)) + dp_info("i %d: multi-page mem in use while free", i); + + if (mp->pages.num_pages) { + dp_info("i %d: type %d cacheable_pages %pK " + "dma_pages %pK num_pages %d", + i, mp->desc_type, + mp->pages.cacheable_pages, + mp->pages.dma_pages, + mp->pages.num_pages); + qdf_mem_multi_pages_free(qdf_ctx, &mp->pages, + 0, mp->cacheable); + qdf_mem_zero(mp, sizeof(*mp)); + } + } + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_consistent_unaligned_allocs); i++) { + up = &g_dp_consistent_unaligned_allocs[i]; + + if (qdf_unlikely(up->in_use)) + dp_info("i %d: unaligned mem in use while free", i); + + if (up->va_unaligned) { + dp_info("i %d: va unalign %pK pa unalign %pK size %d", + i, up->va_unaligned, + (void *)up->pa_unaligned, up->size); + qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev, + up->size, + up->va_unaligned, + up->pa_unaligned, 0); + qdf_mem_zero(up, sizeof(*up)); + } + } + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_context_allocs); i++) { + cp = &g_dp_context_allocs[i]; + if (qdf_unlikely(up->in_use)) + dp_warn("i %d: context in use while free", i); + + if (cp->addr) { + qdf_mem_free(cp->addr); + cp->addr = NULL; + } + } +} + +QDF_STATUS dp_prealloc_init(void) +{ + int i; + struct dp_prealloc_context *cp; + struct dp_consistent_prealloc *p; + struct dp_multi_page_prealloc *mp; + struct dp_consistent_prealloc_unaligned *up; + qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!qdf_ctx) { + dp_err("qdf_ctx is NULL"); + QDF_BUG(0); + return QDF_STATUS_E_FAILURE; + } + + /*Context pre-alloc*/ + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_context_allocs); i++) { + cp = &g_dp_context_allocs[i]; + cp->addr = qdf_mem_malloc(cp->size); + + if (qdf_unlikely(!cp->addr)) { + dp_warn("i %d: unable to preallocate %d bytes memory!", + i, cp->size); + break; + } + } + + if (i != QDF_ARRAY_SIZE(g_dp_context_allocs)) { + dp_err("unable to allocate context memory!"); + goto deinit; + } + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_consistent_allocs); i++) { + p = &g_dp_consistent_allocs[i]; + p->in_use = 0; + p->va_aligned = + qdf_aligned_mem_alloc_consistent(qdf_ctx, + &p->size, + &p->va_unaligned, + &p->pa_unaligned, + &p->pa_aligned, + DP_RING_BASE_ALIGN); + if (qdf_unlikely(!p->va_unaligned)) { + dp_warn("i %d: unable to preallocate %d bytes memory!", + i, p->size); + break; + } else { + dp_info("i %d: va aligned %pK pa aligned %pK size %d", i, + p->va_aligned, (void *)p->pa_aligned, p->size); + } + } + + if (i != QDF_ARRAY_SIZE(g_dp_consistent_allocs)) { + dp_info("unable to allocate consistent memory!"); + goto deinit; + } + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_multi_page_allocs); i++) { + mp = &g_dp_multi_page_allocs[i]; + mp->in_use = false; + qdf_mem_multi_pages_alloc(qdf_ctx, &mp->pages, + mp->element_size, + mp->element_num, + 0, mp->cacheable); + if (qdf_unlikely(!mp->pages.num_pages)) { + dp_warn("i %d: preallocate %d bytes multi-pages failed!", + i, (int)(mp->element_size * mp->element_num)); + break; + } else { + mp->pages.is_mem_prealloc = true; + dp_info("i %d: cacheable_pages %pK dma_pages %pK num_pages %d", + i, mp->pages.cacheable_pages, + mp->pages.dma_pages, + mp->pages.num_pages); + } + } + + if (i != QDF_ARRAY_SIZE(g_dp_multi_page_allocs)) { + dp_info("unable to allocate multi-pages memory!"); + goto deinit; + } + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_consistent_unaligned_allocs); i++) { + up = &g_dp_consistent_unaligned_allocs[i]; + up->in_use = 0; + up->va_unaligned = qdf_mem_alloc_consistent(qdf_ctx, + qdf_ctx->dev, + up->size, + &up->pa_unaligned); + if (qdf_unlikely(!up->va_unaligned)) { + dp_warn("i %d: fail to prealloc unaligned %d bytes!", + i, up->size); + break; + } + dp_info("i %d: va unalign %pK pa unalign %pK size %d", + i, up->va_unaligned, + (void *)up->pa_unaligned, up->size); + } + + if (i != QDF_ARRAY_SIZE(g_dp_consistent_unaligned_allocs)) { + dp_info("unable to allocate unaligned memory!"); + /** + * Only if unaligned memory prealloc fail, is deinit + * necessary for all other DP srng/multi-pages memory? + */ + goto deinit; + } + + return QDF_STATUS_SUCCESS; +deinit: + dp_prealloc_deinit(); + return QDF_STATUS_E_FAILURE; +} + +void *dp_prealloc_get_context_memory(uint32_t ctxt_type) +{ + int i; + struct dp_prealloc_context *cp; + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_context_allocs); i++) { + cp = &g_dp_context_allocs[i]; + + if ((ctxt_type == cp->ctxt_type) && !cp->in_use) { + cp->in_use = true; + return cp->addr; + } + } + + return NULL; +} + +QDF_STATUS dp_prealloc_put_context_memory(uint32_t ctxt_type, void *vaddr) +{ + int i; + struct dp_prealloc_context *cp; + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_context_allocs); i++) { + cp = &g_dp_context_allocs[i]; + + if ((ctxt_type == cp->ctxt_type) && vaddr == cp->addr) { + qdf_mem_zero(cp->addr, cp->size); + cp->in_use = false; + return QDF_STATUS_SUCCESS; + } + } + + return QDF_STATUS_E_FAILURE; +} + +void *dp_prealloc_get_coherent(uint32_t *size, void **base_vaddr_unaligned, + qdf_dma_addr_t *paddr_unaligned, + qdf_dma_addr_t *paddr_aligned, + uint32_t align, + uint32_t ring_type) +{ + int i; + struct dp_consistent_prealloc *p; + void *va_aligned = NULL; + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_consistent_allocs); i++) { + p = &g_dp_consistent_allocs[i]; + if (p->ring_type == ring_type && !p->in_use && + p->va_unaligned && *size <= p->size) { + p->in_use = 1; + *base_vaddr_unaligned = p->va_unaligned; + *paddr_unaligned = p->pa_unaligned; + *paddr_aligned = p->pa_aligned; + va_aligned = p->va_aligned; + *size = p->size; + dp_info("index %i -> ring type %s va-aligned %pK", i, + dp_srng_get_str_from_hal_ring_type(ring_type), + va_aligned); + break; + } + } + + return va_aligned; +} + +void dp_prealloc_put_coherent(qdf_size_t size, void *vaddr_unligned, + qdf_dma_addr_t paddr) +{ + int i; + struct dp_consistent_prealloc *p; + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_consistent_allocs); i++) { + p = &g_dp_consistent_allocs[i]; + if (p->va_unaligned == vaddr_unligned) { + dp_info("index %d, returned", i); + p->in_use = 0; + qdf_mem_zero(p->va_unaligned, p->size); + break; + } + } + + if (i == QDF_ARRAY_SIZE(g_dp_consistent_allocs)) + dp_err("unable to find vaddr %pK", vaddr_unligned); +} + +void dp_prealloc_get_multi_pages(uint32_t desc_type, + size_t element_size, + uint16_t element_num, + struct qdf_mem_multi_page_t *pages, + bool cacheable) +{ + int i; + struct dp_multi_page_prealloc *mp; + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_multi_page_allocs); i++) { + mp = &g_dp_multi_page_allocs[i]; + + if (desc_type == mp->desc_type && !mp->in_use && + mp->pages.num_pages && element_size == mp->element_size && + element_num <= mp->element_num) { + mp->in_use = true; + *pages = mp->pages; + + dp_info("i %d: desc_type %d cacheable_pages %pK" + "dma_pages %pK num_pages %d", + i, desc_type, + mp->pages.cacheable_pages, + mp->pages.dma_pages, + mp->pages.num_pages); + break; + } + } +} + +void dp_prealloc_put_multi_pages(uint32_t desc_type, + struct qdf_mem_multi_page_t *pages) +{ + int i; + struct dp_multi_page_prealloc *mp; + bool mp_found = false; + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_multi_page_allocs); i++) { + mp = &g_dp_multi_page_allocs[i]; + + if (desc_type == mp->desc_type) { + /* compare different address by cacheable flag */ + mp_found = mp->cacheable ? + (mp->pages.cacheable_pages == + pages->cacheable_pages) : + (mp->pages.dma_pages == pages->dma_pages); + /* find it, put back to prealloc pool */ + if (mp_found) { + dp_info("i %d: desc_type %d returned", + i, desc_type); + mp->in_use = false; + qdf_mem_multi_pages_zero(&mp->pages, + mp->cacheable); + break; + } + } + } + + if (qdf_unlikely(!mp_found)) + dp_warn("Not prealloc pages %pK desc_type %d" + "cacheable_pages %pK dma_pages %pK", + pages, + desc_type, + pages->cacheable_pages, + pages->dma_pages); +} + +void *dp_prealloc_get_consistent_mem_unaligned(size_t size, + qdf_dma_addr_t *base_addr, + uint32_t ring_type) +{ + int i; + struct dp_consistent_prealloc_unaligned *up; + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_consistent_unaligned_allocs); i++) { + up = &g_dp_consistent_unaligned_allocs[i]; + + if (ring_type == up->ring_type && size == up->size && + up->va_unaligned && !up->in_use) { + up->in_use = true; + *base_addr = up->pa_unaligned; + dp_info("i %d: va unalign %pK pa unalign %pK size %d", + i, up->va_unaligned, + (void *)up->pa_unaligned, up->size); + return up->va_unaligned; + } + } + + return NULL; +} + +void dp_prealloc_put_consistent_mem_unaligned(void *va_unaligned) +{ + int i; + struct dp_consistent_prealloc_unaligned *up; + + for (i = 0; i < QDF_ARRAY_SIZE(g_dp_consistent_unaligned_allocs); i++) { + up = &g_dp_consistent_unaligned_allocs[i]; + + if (va_unaligned == up->va_unaligned) { + dp_info("index %d, returned", i); + up->in_use = false; + qdf_mem_zero(up->va_unaligned, up->size); + break; + } + } + + if (i == QDF_ARRAY_SIZE(g_dp_consistent_unaligned_allocs)) + dp_err("unable to find vaddr %pK", va_unaligned); +} +#endif + +/** + * dp_rx_tm_get_pending() - get number of frame in thread + * nbuf queue pending + * @soc: ol_txrx_soc_handle object + * + * Return: number of frames + */ +#ifdef FEATURE_WLAN_DP_RX_THREADS +int dp_rx_tm_get_pending(ol_txrx_soc_handle soc) +{ + int i; + int num_pending = 0; + struct dp_rx_thread *rx_thread; + struct dp_txrx_handle *dp_ext_hdl; + struct dp_rx_tm_handle *rx_tm_hdl; + + if (!soc) + return 0; + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) + return 0; + + rx_tm_hdl = &dp_ext_hdl->rx_tm_hdl; + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + rx_thread = rx_tm_hdl->rx_thread[i]; + if (!rx_thread) + continue; + num_pending += qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue); + } + + if (num_pending) + dp_debug("pending frames in thread queue %d", num_pending); + + return num_pending; +} +#else +int dp_rx_tm_get_pending(ol_txrx_soc_handle soc) +{ + return 0; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_txrx.h b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_txrx.h new file mode 100644 index 0000000000000000000000000000000000000000..fdd3c2f7519d8fa20299f883d1fe41681aeb3e96 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/dp/txrx3.0/dp_txrx.h @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DP_TXRX_H +#define _DP_TXRX_H + +#include +#include +#include +#include +#include + +/** + * struct dp_txrx_config - dp txrx configuration passed to dp txrx modules + * @enable_dp_rx_threads: enable DP rx threads or not + */ +struct dp_txrx_config { + bool enable_rx_threads; +}; + +struct dp_txrx_handle_cmn; +/** + * struct dp_txrx_handle - main dp txrx container handle + * @soc: ol_txrx_soc_handle soc handle + * @rx_tm_hdl: rx thread infrastructure handle + */ +struct dp_txrx_handle { + ol_txrx_soc_handle soc; + struct cdp_pdev *pdev; + struct dp_rx_tm_handle rx_tm_hdl; + struct dp_txrx_config config; +}; + +#ifdef FEATURE_WLAN_DP_RX_THREADS +/** + * dp_txrx_get_cmn_hdl_frm_ext_hdl() - conversion func ext_hdl->txrx_handle_cmn + * @dp_ext_hdl: pointer to dp_txrx_handle structure + * + * Return: typecasted pointer of type - struct dp_txrx_handle_cmn + */ +static inline struct dp_txrx_handle_cmn * +dp_txrx_get_cmn_hdl_frm_ext_hdl(struct dp_txrx_handle *dp_ext_hdl) +{ + return (struct dp_txrx_handle_cmn *)dp_ext_hdl; +} + +/** + * dp_txrx_get_ext_hdl_frm_cmn_hdl() - conversion func txrx_handle_cmn->ext_hdl + * @txrx_cmn_hdl: pointer to dp_txrx_handle_cmn structure + * + * Return: typecasted pointer of type - struct dp_txrx_handle + */ +static inline struct dp_txrx_handle * +dp_txrx_get_ext_hdl_frm_cmn_hdl(struct dp_txrx_handle_cmn *txrx_cmn_hdl) +{ + return (struct dp_txrx_handle *)txrx_cmn_hdl; +} + +static inline ol_txrx_soc_handle +dp_txrx_get_soc_from_ext_handle(struct dp_txrx_handle_cmn *txrx_cmn_hdl) +{ + struct dp_txrx_handle *dp_ext_hdl; + + dp_ext_hdl = dp_txrx_get_ext_hdl_frm_cmn_hdl(txrx_cmn_hdl); + + return dp_ext_hdl->soc; +} + +static inline struct cdp_pdev* +dp_txrx_get_pdev_from_ext_handle(struct dp_txrx_handle_cmn *txrx_cmn_hdl) +{ + struct dp_txrx_handle *dp_ext_hdl; + + dp_ext_hdl = dp_txrx_get_ext_hdl_frm_cmn_hdl(txrx_cmn_hdl); + + return dp_ext_hdl->pdev; +} + +/** + * dp_txrx_init() - initialize DP TXRX module + * @soc: ol_txrx_soc_handle + * @pdev_id: id of dp pdev handle + * @config: configuration for DP TXRX modules + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, uint8_t pdev_id, + struct dp_txrx_config *config); + +/** + * dp_txrx_deinit() - de-initialize DP TXRX module + * @soc: ol_txrx_soc_handle + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc); + +/** + * dp_txrx_flush_pkts_by_vdev_id() - flush rx packets for a vdev_id + * @soc: ol_txrx_soc_handle object + * @vdev_id: vdev_id for which rx packets are to be flushed + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +static inline QDF_STATUS dp_txrx_flush_pkts_by_vdev_id(ol_txrx_soc_handle soc, + uint8_t vdev_id) +{ + struct dp_txrx_handle *dp_ext_hdl; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + if (!soc) { + qdf_status = QDF_STATUS_E_INVAL; + goto ret; + } + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) { + qdf_status = QDF_STATUS_E_FAULT; + goto ret; + } + + qdf_status = dp_rx_tm_flush_by_vdev_id(&dp_ext_hdl->rx_tm_hdl, vdev_id); +ret: + return qdf_status; +} + +/** + * dp_txrx_resume() - resume all threads + * @soc: ol_txrx_soc_handle object + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +static inline QDF_STATUS dp_txrx_resume(ol_txrx_soc_handle soc) +{ + struct dp_txrx_handle *dp_ext_hdl; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + if (!soc) { + qdf_status = QDF_STATUS_E_INVAL; + goto ret; + } + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) { + qdf_status = QDF_STATUS_E_FAULT; + goto ret; + } + + qdf_status = dp_rx_tm_resume(&dp_ext_hdl->rx_tm_hdl); +ret: + return qdf_status; +} + +/** + * dp_txrx_suspend() - suspend all threads + * @soc: ol_txrx_soc_handle object + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +static inline QDF_STATUS dp_txrx_suspend(ol_txrx_soc_handle soc) +{ + struct dp_txrx_handle *dp_ext_hdl; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + if (!soc) { + qdf_status = QDF_STATUS_E_INVAL; + goto ret; + } + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) { + qdf_status = QDF_STATUS_E_FAULT; + goto ret; + } + + qdf_status = dp_rx_tm_suspend(&dp_ext_hdl->rx_tm_hdl); + +ret: + return qdf_status; +} + +/** + * dp_rx_enqueue_pkt() - enqueue packet(s) into the thread + * @soc: ol_txrx_soc_handle object + * @nbuf_list: list of packets to be queued into the rx_thread + * + * The function accepts a list of skbs connected by the skb->next pointer and + * queues them into a RX thread to be sent to the stack. + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +static inline +QDF_STATUS dp_rx_enqueue_pkt(ol_txrx_soc_handle soc, qdf_nbuf_t nbuf_list) +{ + struct dp_txrx_handle *dp_ext_hdl; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + if (!soc || !nbuf_list) { + qdf_status = QDF_STATUS_E_INVAL; + dp_err("invalid input params soc %pK nbuf %pK" + , soc, nbuf_list); + goto ret; + } + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) { + qdf_status = QDF_STATUS_E_FAULT; + goto ret; + } + + qdf_status = dp_rx_tm_enqueue_pkt(&dp_ext_hdl->rx_tm_hdl, nbuf_list); +ret: + return qdf_status; +} + +/** + * dp_rx_gro_flush_ind() - Flush GRO packets for a given RX CTX Id + * @soc: ol_txrx_soc_handle object + * @rx_ctx_id: Context Id (Thread for which GRO packets need to be flushed) + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +static inline +QDF_STATUS dp_rx_gro_flush_ind(ol_txrx_soc_handle soc, int rx_ctx_id) +{ + struct dp_txrx_handle *dp_ext_hdl; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + if (!soc) { + qdf_status = QDF_STATUS_E_INVAL; + dp_err("invalid input param soc %pK", soc); + goto ret; + } + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) { + qdf_status = QDF_STATUS_E_FAULT; + goto ret; + } + + qdf_status = dp_rx_tm_gro_flush_ind(&dp_ext_hdl->rx_tm_hdl, rx_ctx_id); +ret: + return qdf_status; +} + +/** + * dp_txrx_ext_dump_stats() - dump txrx external module stats + * @soc: ol_txrx_soc_handle object + * @stats_id: id for the module whose stats are needed + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +static inline QDF_STATUS dp_txrx_ext_dump_stats(ol_txrx_soc_handle soc, + uint8_t stats_id) +{ + struct dp_txrx_handle *dp_ext_hdl; + QDF_STATUS qdf_status; + + if (!soc) { + dp_err("invalid input params soc %pK", soc); + return QDF_STATUS_E_INVAL; + } + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) { + return QDF_STATUS_E_FAULT; + } + + if (stats_id == CDP_DP_RX_THREAD_STATS) + qdf_status = dp_rx_tm_dump_stats(&dp_ext_hdl->rx_tm_hdl); + else + qdf_status = QDF_STATUS_E_INVAL; + + return qdf_status; +} + +/** + * dp_rx_get_napi_context() - get NAPI context for a RX CTX ID + * @soc: ol_txrx_soc_handle object + * @rx_ctx_id: RX context ID (RX thread ID) corresponding to which NAPI is + * needed + * + * Return: NULL on failure, else pointer to NAPI corresponding to rx_ctx_id + */ +static inline +struct napi_struct *dp_rx_get_napi_context(ol_txrx_soc_handle soc, + uint8_t rx_ctx_id) +{ + struct dp_txrx_handle *dp_ext_hdl; + + if (!soc) { + dp_err("soc in NULL!"); + return NULL; + } + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) { + dp_err("dp_ext_hdl in NULL!"); + return NULL; + } + + return dp_rx_tm_get_napi_context(&dp_ext_hdl->rx_tm_hdl, rx_ctx_id); +} + +/** + * dp_txrx_set_cpu_mask() - set CPU mask for RX threads + * @soc: ol_txrx_soc_handle object + * @new_mask: New CPU mask pointer + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +static inline +QDF_STATUS dp_txrx_set_cpu_mask(ol_txrx_soc_handle soc, qdf_cpu_mask *new_mask) +{ + struct dp_txrx_handle *dp_ext_hdl; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + if (!soc) { + qdf_status = QDF_STATUS_E_INVAL; + goto ret; + } + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) { + qdf_status = QDF_STATUS_E_FAULT; + goto ret; + } + + qdf_status = dp_rx_tm_set_cpu_mask(&dp_ext_hdl->rx_tm_hdl, new_mask); + +ret: + return qdf_status; +} + +#else + +static inline +QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, uint8_t pdev_id, + struct dp_txrx_config *config) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS dp_txrx_flush_pkts_by_vdev_id(ol_txrx_soc_handle soc, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS dp_txrx_resume(ol_txrx_soc_handle soc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS dp_txrx_suspend(ol_txrx_soc_handle soc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS dp_rx_enqueue_pkt(ol_txrx_soc_handle soc, qdf_nbuf_t nbuf_list) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS dp_rx_gro_flush_ind(ol_txrx_soc_handle soc, int rx_ctx_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS dp_txrx_ext_dump_stats(ol_txrx_soc_handle soc, + uint8_t stats_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +struct napi_struct *dp_rx_get_napi_context(ol_txrx_soc_handle soc, + uint8_t rx_ctx_id) +{ + return NULL; +} + +static inline +QDF_STATUS dp_txrx_set_cpu_mask(ol_txrx_soc_handle soc, qdf_cpu_mask *new_mask) +{ + return QDF_STATUS_SUCCESS; +} + +#endif /* FEATURE_WLAN_DP_RX_THREADS */ + +#ifdef DP_MEM_PRE_ALLOC +/** + * dp_prealloc_init() - Pre-allocate DP memory + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS dp_prealloc_init(void); + +/** + * dp_prealloc_deinit() - Free pre-alloced DP memory + * + * Return: None + */ +void dp_prealloc_deinit(void); + +/** + * dp_prealloc_get_context_memory() - gets pre-alloc DP context memory from + * global pool + * @ctxt_type: type of DP context + * + * This is done only as part of init happening in a single context. Hence + * no lock is used for protection + * + * Return: Address of context + */ +void *dp_prealloc_get_context_memory(uint32_t ctxt_type); + +/** + * dp_prealloc_put_context_memory() - puts back pre-alloc DP context memory to + * global pool + * @ctxt_type: type of DP context + * @vaddr: address of DP context + * + * This is done only as part of de-init happening in a single context. Hence + * no lock is used for protection + * + * Return: Failure if address not found + */ +QDF_STATUS dp_prealloc_put_context_memory(uint32_t ctxt_type, void *vaddr); + +/** + * dp_prealloc_get_coherent() - gets pre-alloc DP memory + * @size: size of memory needed + * @base_vaddr_unaligned: Unaligned virtual address. + * @paddr_unaligned: Unaligned physical address. + * @paddr_aligned: Aligned physical address. + * @align: Base address alignment. + * @align: alignment needed + * @ring_type: HAL ring type + * + * Return: unaligned virtual address if success or null if memory alloc fails. + */ +void *dp_prealloc_get_coherent(uint32_t *size, void **base_vaddr_unaligned, + qdf_dma_addr_t *paddr_unaligned, + qdf_dma_addr_t *paddr_aligned, + uint32_t align, + uint32_t ring_type); +/** + * dp_prealloc_put_coherent() - puts back pre-alloc DP memory + * @size: size of memory to be returned + * @base_vaddr_unaligned: Unaligned virtual address. + * @paddr_unaligned: Unaligned physical address. + * + * Return: None + */ +void dp_prealloc_put_coherent(qdf_size_t size, void *vaddr_unligned, + qdf_dma_addr_t paddr); + +/** + * dp_prealloc_get_multi_page() - gets pre-alloc DP multi-pages memory + * @src_type: the source that do memory allocation + * @element_size: single element size + * @element_num: total number of elements should be allocated + * @pages: multi page information storage + * @cacheable: coherent memory or cacheable memory + * + * Return: None. + */ +void dp_prealloc_get_multi_pages(uint32_t src_type, + size_t element_size, + uint16_t element_num, + struct qdf_mem_multi_page_t *pages, + bool cacheable); + +/** + * dp_prealloc_put_multi_pages() - puts back pre-alloc DP multi-pages memory + * @src_type: the source that do memory freement + * @pages: multi page information storage + * + * Return: None + */ +void dp_prealloc_put_multi_pages(uint32_t src_type, + struct qdf_mem_multi_page_t *pages); +/** + * dp_prealloc_get_consistent_mem_unaligned() - gets pre-alloc unaligned + consistent memory + * @size: total memory size + * @base_addr: pointer to dma address. + * @ring_type: HAL ring type that requires memory + * + * Return: memory virtual address pointer, NULL if fail + */ +void *dp_prealloc_get_consistent_mem_unaligned(size_t size, + qdf_dma_addr_t *base_addr, + uint32_t ring_type); +/** + * dp_prealloc_put_consistent_mem_unaligned() - puts back pre-alloc unaligned + consistent memory + * @va_unaligned: memory virtual address pointer. + * + * Return: None + */ +void dp_prealloc_put_consistent_mem_unaligned(void *va_unaligned); + +#else +static inline QDF_STATUS dp_prealloc_init(void) { return QDF_STATUS_SUCCESS; } + +static inline void dp_prealloc_deinit(void) { } + +#endif + +/** + * dp_rx_tm_get_pending() - get number of frame in thread + * nbuf queue pending + * @soc: ol_txrx_soc_handle object + * + * Return: number of frames + */ +int dp_rx_tm_get_pending(ol_txrx_soc_handle soc); +#endif /* _DP_TXRX_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/hdd_config.h b/drivers/staging/qcacld-3.0/core/hdd/inc/hdd_config.h new file mode 100644 index 0000000000000000000000000000000000000000..d31766b6d6f18b2c3bba01b500da2328e9e2d482 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/hdd_config.h @@ -0,0 +1,1715 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __HDD_CONFIG_H +#define __HDD_CONFIG_H + +#include "hdd_sar_safety_config.h" + +#if defined(CONFIG_HL_SUPPORT) +#include "wlan_tgt_def_config_hl.h" +#else +#include "wlan_tgt_def_config.h" +#endif + +#define CFG_QDF_TRACE_ENABLE_DEFAULT (0xffff) +#include + +/** + * enum hdd_wext_control - knob for wireless extensions + * @hdd_wext_disabled - interface is completely disabled. An access + * control error log will be generated for each attempted use. + * @hdd_wext_deprecated - interface is available but should not be + * used. An access control warning log will be generated for each + * use. + * @hdd_wext_enabled - interface is available without restriction. No + * access control logs will be generated. + * + * enum hdd_wext_control is used to enable coarse grained control on + * wireless extensions ioctls. This control is used by configuration + * item private_wext_control. + * + */ +enum hdd_wext_control { + hdd_wext_disabled = 0, + hdd_wext_deprecated = 1, + hdd_wext_enabled = 2, +}; + +/* + * + * private_wext_control - Private wireless extensions control + * @Min: 0 + * @Max: 2 + * @Default: 1 + * + * Values are per enum hdd_wext_control. + * This ini is used to control access to private wireless extensions + * ioctls SIOCIWFIRSTPRIV (0x8BE0) thru SIOCIWLASTPRIV (0x8BFF). The + * functionality provided by some of these ioctls has been superceeded + * by cfg80211 (either standard commands or vendor commands), but many + * of the private ioctls do not have a cfg80211-based equivalent, so + * by default support for these ioctls is deprecated. + * + * Related: None + * + * Supported Feature: All + * + * Usage: Internal/External + * + * + */ +#define CFG_PRIVATE_WEXT_CONTROL CFG_INI_UINT( \ + "private_wext_control", \ + hdd_wext_disabled, \ + hdd_wext_enabled, \ + hdd_wext_deprecated, \ + CFG_VALUE_OR_DEFAULT, \ + "Private WEXT Control") + +enum hdd_dot11_mode { + eHDD_DOT11_MODE_AUTO = 0, /* covers all things we support */ + eHDD_DOT11_MODE_abg, /* 11a/b/g only, no HT, no proprietary */ + eHDD_DOT11_MODE_11b, + eHDD_DOT11_MODE_11g, + eHDD_DOT11_MODE_11n, + eHDD_DOT11_MODE_11g_ONLY, + eHDD_DOT11_MODE_11n_ONLY, + eHDD_DOT11_MODE_11b_ONLY, + eHDD_DOT11_MODE_11ac_ONLY, + eHDD_DOT11_MODE_11ac, + eHDD_DOT11_MODE_11a, + eHDD_DOT11_MODE_11ax_ONLY, + eHDD_DOT11_MODE_11ax, +}; + +/* + * + * gDot11Mode - Phymode of vdev + * @Min: 0 (auto) + * @Max: 12 (11ax) + * @Default: 12 (11ax) + * + * This ini is used to set Phy Mode (auto, b, g, n, etc/) Valid values are + * 0-12, with 0 = Auto, 12 = 11ax. + * + * Related: None. + * + * Supported Feature: SAP + * + * Usage: Internal/External + * + * + */ + #define CFG_HDD_DOT11_MODE CFG_INI_UINT( \ + "gDot11Mode", \ + eHDD_DOT11_MODE_AUTO, \ + eHDD_DOT11_MODE_11ax, \ + eHDD_DOT11_MODE_11ax, \ + CFG_VALUE_OR_DEFAULT, \ + "dot11 mode") + +/* + * + * gInterfaceChangeWait - Interface change wait + * @Min: 0, + * @Max: 500000 + * @Default: 10000 + * + * Timer waiting for interface up from the upper layer. If + * this timer expires all the cds modules shall be closed. + * Time Units: ms + * + * Value 0 can be used to disable idle module stop. + * + * Related: None + * + * Supported Feature: All + * + * + */ +#define CFG_INTERFACE_CHANGE_WAIT CFG_INI_UINT( \ + "gInterfaceChangeWait", \ + 0, \ + 500000, \ + 10000, \ + CFG_VALUE_OR_DEFAULT, \ + "Interface change wait") + +#ifdef QCA_WIFI_NAPIER_EMULATION +#define CFG_TIMER_MULTIPLIER_DEFAULT 100 +#else +#define CFG_TIMER_MULTIPLIER_DEFAULT 1 +#endif + +/* + * + * gTimerMultiplier - Scale QDF timers by this value + * @Min: 1 + * @Max: 0xFFFFFFFF + * @Default: 1 (100 for emulation) + * + * To assist in debugging emulation setups, scale QDF timers by this factor. + * + * @E.g. + * # QDF timers expire in real time + * gTimerMultiplier=1 + * # QDF timers expire after 100 times real time + * gTimerMultiplier=100 + * + * Related: None + * + * Usage: Internal + * + * + */ +#define CFG_TIMER_MULTIPLIER CFG_INI_UINT( \ + "gTimerMultiplier", \ + 1, \ + 0xFFFFFFFF, \ + CFG_TIMER_MULTIPLIER_DEFAULT, \ + CFG_VALUE_OR_DEFAULT, \ + "Timer Multiplier") + +#define CFG_BUG_ON_REINIT_FAILURE_DEFAULT 0 +/* + * + * g_bug_on_reinit_failure - Enable/Disable bug on reinit + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to debug ssr reinit failure issues by raising vos bug so + * dumps can be collected. + * g_bug_on_reinit_failure = 0 wlan driver will only recover after driver + * unload and load + * g_bug_on_reinit_failure = 1 raise vos bug to collect dumps + * + * Related: gEnableSSR + * + * Supported Feature: SSR + * + * Usage: External + * + * + */ +#define CFG_BUG_ON_REINIT_FAILURE CFG_INI_BOOL( \ + "g_bug_on_reinit_failure", \ + CFG_BUG_ON_REINIT_FAILURE_DEFAULT, \ + "BUG on reinit failure") + +/* + * + * gEnableDumpCollect - It will use for collect the dumps + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to set collect default dump + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_RAMDUMP_COLLECTION CFG_INI_BOOL( \ + "gEnableDumpCollect", \ + 1, \ + "Enable dump collect") + +#if defined(MDM_PLATFORM) && !defined(FEATURE_MULTICAST_HOST_FW_MSGS) +#define CFG_MULTICAST_HOST_FW_MSGS_DEFAULT 0 +#else +#define CFG_MULTICAST_HOST_FW_MSGS_DEFAULT 1 +#endif + +/* + * + * gMulticastHostFwMsgs - Multicast host FW messages + * @Min: 0 + * @Max: 1 + * @Default: 0 for MDM platform and 1 for other + * + * + */ +#define CFG_MULTICAST_HOST_FW_MSGS CFG_INI_UINT( \ + "gMulticastHostFwMsgs", \ + 0, \ + 1, \ + CFG_MULTICAST_HOST_FW_MSGS_DEFAULT, \ + CFG_VALUE_OR_DEFAULT, \ + "Multicast host FW msgs") + +#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE +/* + * + * wlanLoggingEnable - Wlan logging enable + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * + */ +#define CFG_WLAN_LOGGING_SUPPORT CFG_INI_BOOL( \ + "wlanLoggingEnable", \ + 1, \ + "Wlan logging enable") + +/* + * + * host_log_custom_nl_proto - Host log netlink protocol + * @Min: 0 + * @Max: 32 + * @Default: 2 + * + * This ini is used to set host log netlink protocol. The default + * value is 2 (NETLINK_USERSOCK), customer should avoid selecting the + * netlink protocol that already used on their platform by other + * applications or services. By choosing the non-default value(2), + * Customer need to change the netlink protocol of application receive + * tool(cnss_diag) accordingly. Available values could be: + * + * host_log_custom_nl_proto = 0 - NETLINK_ROUTE, Routing/device hook + * host_log_custom_nl_proto = 1 - NETLINK_UNUSED, Unused number + * host_log_custom_nl_proto = 2 - NETLINK_USERSOCK, Reserved for user + * mode socket protocols + * host_log_custom_nl_proto = 3 - NETLINK_FIREWALL, Unused number, + * formerly ip_queue + * host_log_custom_nl_proto = 4 - NETLINK_SOCK_DIAG, socket monitoring + * host_log_custom_nl_proto = 5 - NETLINK_NFLOG, netfilter/iptables ULOG + * host_log_custom_nl_proto = 6 - NETLINK_XFRM, ipsec + * host_log_custom_nl_proto = 7 - NETLINK_SELINUX, SELinux event + * notifications + * host_log_custom_nl_proto = 8 - NETLINK_ISCSI, Open-iSCSI + * host_log_custom_nl_proto = 9 - NETLINK_AUDIT, auditing + * host_log_custom_nl_proto = 10 - NETLINK_FIB_LOOKUP + * host_log_custom_nl_proto = 11 - NETLINK_CONNECTOR + * host_log_custom_nl_proto = 12 - NETLINK_NETFILTER, netfilter subsystem + * host_log_custom_nl_proto = 13 - NETLINK_IP6_FW + * host_log_custom_nl_proto = 14 - NETLINK_DNRTMSG, DECnet routing messages + * host_log_custom_nl_proto = 15 - NETLINK_KOBJECT_UEVENT, Kernel + * messages to userspace + * host_log_custom_nl_proto = 16 - NETLINK_GENERIC, leave room for + * NETLINK_DM (DM Events) + * host_log_custom_nl_proto = 18 - NETLINK_SCSITRANSPORT, SCSI Transports + * host_log_custom_nl_proto = 19 - NETLINK_ECRYPTFS + * host_log_custom_nl_proto = 20 - NETLINK_RDMA + * host_log_custom_nl_proto = 21 - NETLINK_CRYPTO, Crypto layer + * host_log_custom_nl_proto = 22 - NETLINK_SMC, SMC monitoring + * + * The max value is: MAX_LINKS which is 32 + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_HOST_LOG_CUSTOM_NETLINK_PROTO CFG_INI_UINT( \ + "host_log_custom_nl_proto", \ + 0, \ + 32, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "host log custom netlink protocol") + +/* + * + * wlanLoggingToConsole - Wlan logging to console + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * + */ +#define CFG_WLAN_LOGGING_CONSOLE_SUPPORT CFG_INI_BOOL( \ + "wlanLoggingToConsole", \ + 1, \ + "Wlan logging to console") + +#define CFG_WLAN_LOGGING_SUPPORT_ALL \ + CFG(CFG_WLAN_LOGGING_SUPPORT) \ + CFG(CFG_WLAN_LOGGING_CONSOLE_SUPPORT) \ + CFG(CFG_HOST_LOG_CUSTOM_NETLINK_PROTO) +#else +#define CFG_WLAN_LOGGING_SUPPORT_ALL +#endif + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +/* + * + * gWlanAutoShutdown - Wlan auto shutdown timer value + * @Min: 0 + * @Max: 86400 + * @Default: 0 + * + * This ini specifies the seconds of WLAN inactivity firmware has to wait + * before indicating WLAN idle event to driver. Upon receiving firmware's + * WLAN idle indication, driver may indicate similar event to upper layer + * daemons(SCM, or any other components working to achieve the same purpose), + * who may choose what to do next, e.g. whether to unload driver module or not. + * 0 indicates no auto shutdown will take place. + * + * + */ +#define CFG_WLAN_AUTO_SHUTDOWN CFG_INI_UINT( \ + "gWlanAutoShutdown", \ + 0, \ + 86400, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Wlan auto shutdown") +#define CFG_WLAN_AUTO_SHUTDOWN_ALL \ + CFG(CFG_WLAN_AUTO_SHUTDOWN) +#else +#define CFG_WLAN_AUTO_SHUTDOWN_ALL +#endif + +/* + * + * gEnablefwprint - Enable FW uart print + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * + */ +#define CFG_ENABLE_FW_UART_PRINT CFG_INI_BOOL( \ + "gEnablefwprint", \ + 0, \ + "Enable FW uart print") + +/* + * + * gEnablefwlog - Enable FW log + * @Min: 0 + * @Max: 2 + * @Default: 1 + * + * + */ +#define CFG_ENABLE_FW_LOG CFG_INI_UINT( \ + "gEnablefwlog", \ + 0, \ + 2, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable FW log") + +#ifndef REMOVE_PKT_LOG + +#ifdef FEATURE_PKTLOG +#define CFG_ENABLE_PACKET_LOG_DEFAULT 1 +#else +#define CFG_ENABLE_PACKET_LOG_DEFAULT 0 +#endif + +/* + * + * gEnablePacketLog - Enale packet log + * @Min: 0 + * @Max: 1 + * @Default: 1 if packet log code is enabled, 0 otherwise + * + * This option enables/disables packet log collecting. + * + * + */ +#define CFG_ENABLE_PACKET_LOG CFG_INI_BOOL( \ + "gEnablePacketLog", \ + CFG_ENABLE_PACKET_LOG_DEFAULT, \ + "Enable packet log") + +#define CFG_ENABLE_PACKET_LOG_ALL \ + CFG(CFG_ENABLE_PACKET_LOG) +#else +#define CFG_ENABLE_PACKET_LOG_ALL +#endif + +#ifdef FEATURE_RUNTIME_PM + +/** + * enum hdd_runtime_pm_cfg - Runtime PM (RTPM) configuration options + * @hdd_runtime_pm_disabled: RTPM and CxPC aware RTPM disabled + * @hdd_runtime_pm_static: RTPM enabled, but CxPC aware RTPM disabled + * @hdd_runtime_pm_dynamic: RTPM and CxPC aware RTPM enabled + */ +enum hdd_runtime_pm_cfg { + hdd_runtime_pm_disabled = 0, + hdd_runtime_pm_static = 1, + hdd_runtime_pm_dynamic = 2, +}; + +/* + * + * gRuntimePM - enable runtime suspend + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to enable runtime PM + * + * 0: RTPM disabled, so CxPC aware RTPM will be disabled as well + * 1: RTPM enabled, but CxPC aware RTPM disabled + * 2: RTPM enabled and CxPC aware RTPM enabled as well + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_ENABLE_RUNTIME_PM CFG_INI_UINT( \ + "gRuntimePM", \ + 0, \ + 2, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "This ini is used to enable runtime_suspend") +#define CFG_ENABLE_RUNTIME_PM_ALL \ + CFG(CFG_ENABLE_RUNTIME_PM) +#else +#define CFG_ENABLE_RUNTIME_PM_ALL +#endif + +#ifdef WLAN_FEATURE_WMI_SEND_RECV_QMI +/* + * + * enable_qmi_stats - enable periodic stats over qmi + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable periodic stats over qmi if DUT is + * in RTPM suspended state to avoid WoW enter/exit for every stats + * request. + * + * 0: Periodic stats over QMI is disabled + * 1: Periodic stats over QMI is enabled + * Related: None + * + * Supported Feature: Power Save + * + * Usage: External + * + * + */ +#define CFG_ENABLE_QMI_STATS CFG_INI_UINT( \ + "enable_qmi_stats", \ + 0, \ + 1, \ + 1, \ + CFG_VALUE_OR_DEFAULT, \ + "This ini is used to enable periodic stats over qmi") +#define CFG_ENABLE_QMI_STATS_ALL \ + CFG(CFG_ENABLE_QMI_STATS) +#else +#define CFG_ENABLE_QMI_STATS_ALL +#endif + +/* + * + * gInformBssRssiRaw - Report rssi in cfg80211_inform_bss_frame + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * Option to report rssi in cfg80211_inform_bss_frame() + * + * Related: None + * + * Supported Feature: N/A + * + * Usage: External + * + * + */ +#define CFG_INFORM_BSS_RSSI_RAW CFG_INI_BOOL( \ + "gInformBssRssiRaw", \ + 1, \ + "Option to report rssi in cfg80211_inform_bss_frame") + +#ifdef FEATURE_WLAN_DYNAMIC_CVM +/* + * + * gConfigVCmodeBitmap - Bitmap for operating voltage corner mode + * @Min: 0x00000000 + * @Max: 0x0fffffff + * @Default: 0x0000000a + * This ini is used to set operating voltage corner mode for differenet + * phymode and bw configurations. Every 2 bits till BIT27 are dedicated + * for a specific configuration. Bit values decide the type of voltage + * corner mode. All the details below - + * + * Configure operating voltage corner mode based on phymode and bw. + * bit 0-1 - operating voltage corner mode for 11a/b. + * bit 2-3 - operating voltage corner mode for 11g. + * bit 4-5 - operating voltage corner mode for 11n, 20MHz, 1x1. + * bit 6-7 - operating voltage corner mode for 11n, 20MHz, 2x2. + * bit 8-9 - operating voltage corner mode for 11n, 40MHz, 1x1. + * bit 10-11 - operating voltage corner mode for 11n, 40MHz, 2x2. + * bit 12-13 - operating voltage corner mode for 11ac, 20MHz, 1x1. + * bit 14-15 - operating voltage corner mode for 11ac, 20MHz, 2x2. + * bit 16-17 - operating voltage corner mode for 11ac, 40MHz, 1x1. + * bit 18-19 - operating voltage corner mode for 11ac, 40MHz, 2x2. + * bit 20-21 - operating voltage corner mode for 11ac, 80MHz, 1x1. + * bit 22-23 - operating voltage corner mode for 11ac, 80MHz, 2x2. + * bit 24-25 - operating voltage corner mode for 11ac, 160MHz, 1x1. + * bit 26-27 - operating voltage corner mode for 11ac, 160MHz, 2x2. + * --------------------------------------------- + * 00 - Static voltage corner SVS + * 01 - static voltage corner LOW SVS + * 10 - Dynamic voltage corner selection based on TPUT + * 11 - Dynamic voltage corner selection based on TPUT and Tx Flush counters + + * Related: None + * + * Supported Feature: None + * + * Usage: External + * + * + */ +#define CFG_VC_MODE_BITMAP CFG_INI_INT( \ + "gConfigVCmode", \ + 0x00000000, \ + 0x0fffffff, \ + 0x00000005, \ + CFG_VALUE_OR_DEFAULT, \ + "Bitmap for operating voltage corner mode") + +#define CFG_VC_MODE_BITMAP_ALL CFG(CFG_VC_MODE_BITMAP) +#else +#define CFG_VC_MODE_BITMAP_ALL +#endif + +/* + * + * def_sta_operating_freq - Default STA operating Freq + * @Min: 0 + * @Max: 2484 + * @Default: 2412 + * + * This ini is used to specify the default operating frequency of a STA during + * initialization. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_OPERATING_FREQUENCY CFG_INI_UINT( \ + "def_sta_operating_freq", \ + 0, \ + 2484, \ + 2412, \ + CFG_VALUE_OR_DEFAULT, \ + "Default STA Operating Frequency") +#ifdef DHCP_SERVER_OFFLOAD +#define IPADDR_NUM_ENTRIES (4) +#define IPADDR_STRING_LENGTH (16) +#define CFG_DHCP_SERVER_IP_DEFAULT "" + +/* + * struct wlan_mlme_chainmask - All chainmask related cfg items + * @dhcpServerIP: Dhcp server IP address + * @is_dhcp_server_ip_valid: is dhcp server valid + */ +struct dhcp_server { + uint8_t dhcp_server_ip[IPADDR_NUM_ENTRIES]; + bool is_dhcp_server_ip_valid; +} + +/* + * + * gDHCPServerIP - Dhcp server Ip name + * @Default: + * + * This ini is used to give the DHCP IP server name + */ +#define CFG_DHCP_SERVER_IP_NAME \ + CFG_INI_STRING("gDHCPServerIP", \ + 0, IPADDR_STRING_LENGTH, CFG_DHCP_SERVER_IP_DEFAULT, "DHCP Server IP") +#endif /* DHCP_SERVER_OFFLOAD */ + +/* + * + * gNumVdevs - max number of VDEVs supported + * + * @Min: 0x1 + * @Max: 0x4 + * @Default: CFG_TGT_NUM_VDEV + * + * Usage: External + * + * + */ +#define CFG_NUM_VDEV_ENABLE CFG_INI_UINT( \ + "gNumVdevs", \ + 1, \ + 4, \ + CFG_TGT_NUM_VDEV, \ + CFG_VALUE_OR_DEFAULT, \ + "Number of VDEVs") + +#define CFG_CONCURRENT_IFACE_MAX_LEN 16 +/* + * + * gEnableConcurrentSTA - This will control the creation of concurrent STA + * interface + * @Default: NULL + * + * This ini is used for providing control to create a concurrent STA session + * along with the creation of wlan0 and p2p0. The name of the interface is + * specified as the parameter + * + * Usage: Internal + * + * + */ + +#define CFG_ENABLE_CONCURRENT_STA CFG_INI_STRING( \ + "gEnableConcurrentSTA", \ + 0, \ + CFG_CONCURRENT_IFACE_MAX_LEN, \ + "", \ + "Enable Concurrent STA") + +#define CFG_DBS_SCAN_PARAM_LENGTH 42 +/* + * + * gdbs_scan_selection - DBS Scan Selection. + * @Default: "" + * + * This ini is used to enable DBS scan selection. + * Example + * @Value: "5,2,2,16,2,2" + * 1st argument is module_id, 2nd argument is number of DBS scan, + * 3rd argument is number of non-DBS scan, + * and other arguments follows. + * 5,2,2,16,2,2 means: + * 5 is module id, 2 is num of DBS scan, 2 is num of non-DBS scan. + * 16 is module id, 2 is num of DBS scan, 2 is num of non-DBS scan. + * + * Related: None. + * + * Supported Feature: DBS Scan + * + * Usage: Internal/External + * + * + */ +#define CFG_DBS_SCAN_SELECTION CFG_INI_STRING( \ + "gdbs_scan_selection", \ + 0, \ + CFG_DBS_SCAN_PARAM_LENGTH, \ + "", \ + "DBS Scan Selection") + +/* + * + * enable_mac_provision - Enable/disable MAC address provisioning feature + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable MAC address provisioning feature + * + * Supported Feature: STA/SAP/P2P + * + * Usage: External + * + * + */ +#define CFG_ENABLE_MAC_PROVISION CFG_INI_BOOL( \ + "enable_mac_provision", \ + 0, \ + "enable/disable MAC address provisioning feature") + +/* + * + * provisioned_intf_pool - It is bit mask value of Interfaces + * @Min: 0 + * @Max: 0xffffffff + * @Default: 0xffffffff + * + * This ini will contain the bitmask of all the interfaces + * which can use addresses from provisioned list. Using enum QDF_OPMODE + * for deciding the bit positions corresponding to each interface. + * Bit 0 : QDF_STA_MODE + * Bit 1 : QDF_SAP_MODE + * Bit 2 : QDF_P2P_CLIENT_MODE + * Bit 3 : QDF_P2P_GO_MODE + * Bit 4 : QDF_FTM_MODE + * Bit 5 : QDF_IBSS_MODE + * Bit 6 : QDF_MONITOR_MODE + * Bit 7 : QDF_P2P_DEVICE_MODE + * Bit 8 : QDF_OCB_MODE + * Bit 9 : QDF_EPPING_MODE + * Bit 10 : QDF_QVIT_MODE + * Bit 11 : QDF_NDI_MODE + * Bit 12 : QDF_MAX_NO_OF_MODE + * For example : + * If Bit 0 represents STA + * Bit 1 represents SAP + * Bit 2 represents P2PGO + * If only STA and SAP can use addresses from provisioned list then the value + * of ini should be 3 (00000011) as first and second bit should be set. + * If only STA and P2PGO can use addresses from provisioned list then the value + * of ini should be 5 (00000101) as first and third bit should be set. + * Similarly, for only SAP and P2PGO ini should be 6 (00000110) + * + * Supported Feature: STA/SAP/P2P + * + * Usage: External + * + * + */ +#define CFG_PROVISION_INTERFACE_POOL CFG_INI_UINT( \ + "provisioned_intf_pool", \ + 0, \ + 0xffffffff, \ + 0xffffffff, \ + CFG_VALUE_OR_DEFAULT, \ + "It is bit mask value of Interfaces") + +/* + * + * deriveded_intf_pool - It is bit mask value of Interfaces + * @Min: 0 + * @Max: 0xffffffff + * @Default: 0xffffffff + * + * This ini will contain the bitmask of all the interfaces + * which can use addresses from derived list + * + * + * Supported Feature: STA/SAP/P2P + * + * Usage: External + * + * + */ +#define CFG_DERIVED_INTERFACE_POOL CFG_INI_UINT( \ + "derived_intf_pool", \ + 0, \ + 0xffffffff, \ + 0xffffffff, \ + CFG_VALUE_OR_DEFAULT, \ + "It is bit mask value of Interfaces") + +/* + * Start of action oui inis + * + * To enable action oui feature, set gEnableActionOUI + * + * Each action oui is expected in the following format: + * ..... (maximum 10) + * + * whereas, each Extension is separated by space and have the following format: + * + * where each Token is a string of hexa-decimal digits and + * following are the details about each token + * + * Token1 = OUI + * Token2 = Data_Length + * Token3 = Data + * Token4 = Data_Mask + * Token5 = Info_Presence_Bit + * Token6 = MAC_Address + * Token7 = Mac_Address Mask + * Token8 = Capability + * + * is mandatory and it can be either 3 or 5 bytes means 6 or 10 + * hexa-decimal characters + * If the OUI and Data checks needs to be ignored, the oui FFFFFF + * needs to be provided as OUI and bit 0 of Info_Presence_Bit should + * be set to 0. + * + * is mandatory field and should give length of + * the if present else zero + * + * Presence of is controlled by , if is 0, + * then is not expected else Data of the size Data Length bytes are + * expected which means the length of Data string is 2 * Data Length, + * since every byte constitutes two hexa-decimal characters. + * + * is mandatory if is present and length of the + * Data mask string depends on the + * If is 06, then length of Data Mask string is + * 2 characters (represents 1 byte) + * data_mask_length = ((Data_Length - (Data_Length % 8)) / 8) + + * ((Data_Length % 8) ? 1 : 0) + * and has to be constructed from left to right. + * + * Presence of and is + * controlled by which is mandatory + * will give the information for + * OUI – bit 0 Should be set to 1 + * Setting to 0 will ignore OUI and data check + * Mac Address present – bit 1 + * NSS – bit 2 + * HT check – bit 3 + * VHT check – bit 4 + * Band info – bit 5 + * reserved – bit 6 (should always be zero) + * reserved – bit 7 (should always be zero) + * and should be constructed from right to left (b7b6b5b4b3b2b1b0) + * + * for should be constructed from left to right + * + * is 1 byte long and it contains the below info + * NSS – 4 bits starting from LSB (b0 – b3) + * HT enabled – bit 4 + * VHT enabled – bit 5 + * 2G band – bit 6 + * 5G band – bit 7 + * and should be constructed from right to left (b7b6b5b4b3b2b1b0) + * is present if atleast one of the bit is set + * from b2 - b6 in + * + * Example 1: + * + * OUI is 00-10-18, data length is 05 (hex form), data is 02-11-04-5C-DE and + * need to consider first 3 bytes and last byte of data for comparision + * mac-addr EE-1A-59-FE-FD-AF is present and first 3 bytes and last byte of + * mac address should be considered for comparision + * capability is not present + * then action OUI for gActionOUIITOExtension is as follows: + * + * gActionOUIITOExtension=001018 05 0211045CDE E8 03 EE1A59FEFDAF E4 + * + * data mask calculation in above example: + * Data[0] = 02 ---- d0 = 1 + * Data[1] = 11 ---- d1 = 1 + * Data[2] = 04 ---- d2 = 1 + * Data[3] = 5C ---- d3 = 0 + * Data[4] = DE ---- d4 = 1 + * data_mask = d0d1d2d3d4 + append with zeros to complete 8-bit = 11101000 = E8 + * + * mac mask calculation in above example: + * mac_addr[0] = EE ---- m0 = 1 + * mac_addr[1] = 1A ---- m1 = 1 + * mac_addr[2] = 59 ---- m2 = 1 + * mac_addr[3] = FE ---- m3 = 0 + * mac_addr[4] = FD ---- m4 = 0 + * mac_addr[5] = AF ---- m5 = 1 + * mac_mask = m0m1m2m3m4m5 + append with zeros to complete 8-bit = 11100100 = E4 + * + * Example 2: + * + * OUI is 00-10-18, data length is 00 and no Mac Address and capability + * + * gActionOUIITOExtension=001018 00 01 + * + */ + +/* + * + * gEnableActionOUI - Enable/Disable action oui feature + * @Min: 0 (disable) + * @Max: 1 (enable) + * @Default: 1 (enable) + * + * This ini is used to enable the action oui feature to control + * mode of connection, connected AP's in-activity time, Tx rate etc., + * + * Related: If gEnableActionOUI is set, then at least one of the following inis + * must be set with the proper action oui extensions: + * gActionOUIConnect1x1, gActionOUIITOExtension, gActionOUICCKM1X1 + * + * Supported Feature: action ouis + * + * Usage: External + * + * + */ +#define CFG_ENABLE_ACTION_OUI CFG_INI_BOOL( \ + "gEnableActionOUI", \ + 1, \ + "Enable/Disable action oui feature") + +/* + * + * gActionOUIConnect1x1 - Used to specify action OUIs for 1x1 connection + * @Default: 000C43 00 25 C2 001018 06 02FFF02C0000 BC 25 42 001018 06 02FF040C0000 BC 25 42 00037F 00 35 6C 001018 06 02FF009C0000 BC 25 48 + * Note: User should strictly add new action OUIs at the end of this + * default value. + * + * Default OUIs: (All values in Hex) + * OUI 1 : 000C43 + * OUI data Len : 00 + * Info Mask : 25 - Check for NSS and Band + * Capabilities: C2 - NSS == 2 && Band == 2G || Band == 5G + * OUI 2 : 001018 + * OUI data Len : 06 + * OUI Data : 02FFF02C0000 + * OUI data Mask: BC - 10111100 + * Info Mask : 25 - Check for NSS and Band + * Capabilities: 42 - NSS == 2 && Band == 2G + * OUI 3 : 001018 + * OUI data Len : 06 + * OUI Data : 02FF040C0000 + * OUI data Mask: BC - 10111100 + * Info Mask : 25 - Check for NSS and Band + * Capabilities: 42 - NSS == 2 && Band == 2G + * OUI 4 : 00037F + * OUI data Len : 00 + * Info Mask : 35 - Check for NSS, VHT Caps and Band + * Capabilities: 6C - (NSS == 3 or 4) && VHT Caps Preset && Band == 2G + * OUI 5 : 001018 + * OUI data Len : 06 + * OUI Data : 02FF009C0000 + * OUI data Mask: BC - 10111100 + * Info Mask : 25 - Check for NSS and Band + * Capabilities: 48 - NSS == 4 && Band == 2G + * + * This ini is used to specify the AP OUIs with which only 1x1 connection + * is allowed. + * + * Related: None + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_CONNECT_1X1 CFG_INI_STRING( \ + "gActionOUIConnect1x1", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "000C43 00 25 C2 001018 06 02FFF02C0000 BC 25 42 001018 06 02FF040C0000 BC 25 42 00037F 00 35 6C 001018 06 02FF009C0000 BC 25 48", \ + "Used to specify action OUIs for 1x1 connection") + +/* + * + * gActionOUIITOExtension - Used to extend in-activity time for specified APs + * @Default: 00037F 06 01010000FF7F FC 01 000AEB 02 0100 C0 01 000B86 03 010408 E0 01 + * Note: User should strictly add new action OUIs at the end of this + * default value. + * + * Default OUIs: (All values in Hex) + * OUI 1: 00037F + * OUI data Len: 06 + * OUI Data: 01010000FF7F + * OUI data Mask: FC - 11111100 + * Info Mask : 01 - only OUI present in Info mask + * + * OUI 2: 000AEB + * OUI data Len: 02 + * OUI Data: 0100 + * OUI data Mask: C0 - 11000000 + * Info Mask : 01 - only OUI present in Info mask + * + * OUI 3: 000B86 + * OUI data Len: 03 + * OUI Data: 010408 + * OUI data Mask: E0 - 11100000 + * Info Mask : 01 - only OUI present in Info mask + * + * This ini is used to specify AP OUIs using which station's in-activity time + * can be extended with the respective APs + * + * Related: None + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_ITO_EXTENSION CFG_INI_STRING( \ + "gActionOUIITOExtension", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "00037F 06 01010000FF7F FC 01 000AEB 02 0100 C0 01 000B86 03 010408 E0 01", \ + "Used to extend in-activity time for specified APs") + +/* + * + * gActionOUICCKM1X1 - Used to specify action OUIs to control station's TX rates + * + * This ini is used to specify AP OUIs for which station's CCKM TX rates + * should be 1x1 only. + * + * Related: None + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_CCKM_1X1 CFG_INI_STRING( \ + "gActionOUICCKM1X1", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "", \ + "Used to specify action OUIs to control station's TX rates") + +/* + * + * gActionOUIITOAlternate - Used to specify action OUIs to have alternate ITO in + * weak RSSI state + * + * This ini is used to specify AP OUIs for which the stations will have + * alternate ITOs for the case when the RSSI is weak. + * + * Related: None + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_ITO_ALTERNATE CFG_INI_STRING( \ + "gActionOUIITOAlternate", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "001018 06 0202001c0000 FC 01", \ + "Used to specify action OUIs to have alternate ITO") + +/* + * + * gActionOUISwitchTo11nMode - Used to specify action OUIs for switching to 11n + * + * This ini is used to specify which AP for which the connection has to be + * made in 2x2 mode with HT capabilities only and not VHT. + * + * Default OUIs: (All values in Hex) + * OUI 1 : 00904C + * OUI data Len : 03 + * OUI Data : 0418BF + * OUI data Mask: E0 - 11100000 + * Info Mask : 21 - Check for Band + * Capabilities: 40 - Band == 2G + * + * Related: None + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_SWITCH_TO_11N_MODE CFG_INI_STRING( \ + "gActionOUISwitchTo11nMode", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "00904C 05 0418BF0CB2 F8 21 40", \ + "Used to specify action OUIs for switching to 11n") + +/* + * + * gActionOUIConnect1x1with1TxRxChain - Used to specify action OUIs for + * 1x1 connection with one Tx/Rx Chain + * @Default: + * Note: User should strictly add new action OUIs at the end of this + * default value. + * + * Default OUIs: (All values in Hex) + * OUI 1 : 001018 + * OUI data Len : 06 + * OUI Data : 02FFF0040000 + * OUI data Mask: BC - 10111100 + * Info Mask : 21 - Check for Band + * Capabilities: 40 - Band == 2G + * + * OUI 2 : 001018 + * OUI data Len : 06 + * OUI Data : 02FFF0050000 + * OUI data Mask: BC - 10111100 + * Info Mask : 21 - Check for Band + * Capabilities: 40 - Band == 2G + * + * OUI 3 : 001018 + * OUI data Len : 06 + * OUI Data : 02FFF4050000 + * OUI data Mask: BC - 10111100 + * Info Mask : 21 - Check for Band + * Capabilities: 40 - Band == 2G + * + * This ini is used to specify the AP OUIs with which only 1x1 connection + * with one Tx/Rx Chain is allowed. + * + * Related: gEnableActionOUI + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_CONNECT_1X1_WITH_1_CHAIN CFG_INI_STRING( \ + "gActionOUIConnect1x1with1TxRxChain", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "001018 06 02FFF0040000 BC 21 40 001018 06 02FFF0050000 BC 21 40 001018 06 02FFF4050000 BC 21 40", \ + "Used to specify action OUIs for 1x1 connection with one Tx/Rx Chain") + +/* + * + * gActionOUIDisableAggressiveTX - Used to specify action OUIs to disable + * Aggressive TX feature when operating in softap. + * + * @Default: + * Note: User should strictly add new action OUIs at the end of this + * default value. + * + * Default OUIs: + * + * OUI 1 : FFFFFF + * OUI data Len : 00 + * OUI Data: No data + * OUI data Mask: No data mask + * Info Mask: 2A - Check for mac-addr, HT capability and Band + * Mac-addr: F8:59:71:00:00:00 - first 3 bytes + * Mac-mask: E0 - Match only first 3 bytes of peer mac-addr + * Capabilities: 50 – HT should be enabled, and band should be 2.4GHz + * + * OUI 2 : FFFFFF + * OUI data Len : 00 + * OUI Data: No data + * OUI data Mask: No data mask + * Info Mask: 2A - Check for mac-addr, HT capability and Band + * Mac-addr: 14:AB:C5:00:00:00 - first 3 bytes + * Mac-mask: E0 - Match only first 3 bytes of peer mac-addr + * Capabilities: 50 – HT should be enabled, and band should be 2.4GHz + * + * When operating in Softap mode, this ini is used to specify + * STA (peer) OUIs/mac-addr for which aggressive tx is disabled after + * association is successful. + * + * Related: gEnableActionOUI + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_DISABLE_AGGRESSIVE_TX CFG_INI_STRING( \ + "gActionOUIDisableAggressiveTX", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "FFFFFF 00 2A F85971000000 E0 50 FFFFFF 00 2A 14ABC5000000 E0 50", \ + "Used to specify action OUIs to disable aggressive TX") + +/* + * + * gActionOUIDisableAggressiveEDCA - Used to specify action OUIs to control + * EDCA configuration when join the candidate AP + * + * @Default: NULL + * Note: User should strictly add new action OUIs at the end of this + * default value. + * + * This ini is used to specify AP OUIs. The station's EDCA should follow the + * APs' when connecting to those AP, even if the gEnableEdcaParams is set. + * For example, it follows the AP's EDCA whose OUI is 0050F2 with the + * following setting: + * gActionOUIDisableAggressiveEDCA=0050F2 00 01 + * Explain: 0050F2: OUI + * 00: data length is 0 + * 01: info mask, only OUI present in Info mask + * Refer to gEnableActionOUI for more detail about the format. + * + * Related: gEnableEdcaParams, gEnableActionOUI + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_DISABLE_AGGRESSIVE_EDCA CFG_INI_STRING( \ + "gActionOUIDisableAggressiveEDCA", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "", \ + "Used to specify action OUIs to control edca configuration") + +/* + * + * gActionOUIReconnAssocTimeout - Used to specify action OUIs to + * reconnect to same BSSID when wait for association response timeout + * + * This ini is used to specify AP OUIs. Some of AP doesn't response our + * first association request, but it would response our second association + * request. Add such OUI configuration INI to apply reconnect logic when + * association timeout happends with such AP. + * For default: + * gActionOUIReconnAssocTimeout=00E04C 00 01 + * Explain: 00E04C: OUI + * 00: data length is 0 + * 01: info mask, only OUI present in Info mask + * Note: User should strictly add new action OUIs at the end of this + * default value. + * Refer to gEnableActionOUI for more detail about the format. + * + * Related: gEnableActionOUI + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_RECONN_ASSOCTIMEOUT CFG_INI_STRING( \ + "gActionOUIReconnAssocTimeout", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "00E04C 00 01", \ + "Used to specify action OUIs to reconnect when assoc timeout") + +/* + * + * gActionOUIDisableTWT - Used to specify action OUIs to control TWT param + * while joining the candidate AP + * + * This ini is used to specify AP OUIs. Some APs advertise TWT but do not + * follow through when the STA reaches out to them. Thus, TWT will be + * disabled when we receive OUIs of those APs. + * Note: User should strictly add new action OUIs at the end of this + * default value. + * + * Default OUIs: (All values in Hex) + * OUI 1: 001018 + * OUI data Len: 00 + * Info Mask : 01 - only OUI present in Info mask + * + * OUI 2: 000986 + * OUI data Len: 00 + * Info Mask : 01 - only OUI present in Info mask + * + * Refer to gEnableActionOUI for more detail about the format. + * + * Related: gEnableActionOUI + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_DISABLE_TWT CFG_INI_STRING( \ + "gActionOUIDisableTWT", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "001018 00 01 000986 00 01", \ + "Used to specify action OUIs to control TWT configuration") + +/* End of action oui inis */ + +#ifdef ENABLE_MTRACE_LOG +/* + * + * enable_mtrace - Enable Mtrace. + * @Default: 0 + * + * This ini is used to enable MTRACE logging + * + * Related: None. + * + * Supported Feature: MTRACE + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_MTRACE CFG_INI_BOOL( \ + "enable_mtrace", \ + false, \ + "Enable MTRACE") +#define CFG_ENABLE_MTRACE_ALL CFG(CFG_ENABLE_MTRACE) +#else +#define CFG_ENABLE_MTRACE_ALL +#endif + +/* + * + * gAdvertiseConcurrentOperation - Iface combination advertising + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to control whether driver should indicate to kernel + * wiphy layer the combination of all its interfaces' supportability. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define CFG_ADVERTISE_CONCURRENT_OPERATION CFG_INI_BOOL( \ + "gAdvertiseConcurrentOperation", \ + 1, \ + "Iface combination advertising") + +/* + * + * gEnableUnitTestFramework - Enable/Disable unit test framework + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * Related: None + * + * Supported Feature: unit test framework + * + * Usage: Internal (only for dev and test team) + * + * + */ +#define CFG_ENABLE_UNIT_TEST_FRAMEWORK CFG_INI_BOOL( \ + "gEnableUnitTestFramework", \ + 0, \ + "Enable/Disable unit test framework") + +/* + * + * gDisableChannel - Used to disable channels specified + * + * @Min: 0 + * @Max: 1 + * Default: 0 + * + * This ini is used to disable the channels given in the command + * SET_DISABLE_CHANNEL_LIST and to restore the channels when the + * command is given with channel list as 0 + * Usage: External + * + * + */ +#define CFG_ENABLE_DISABLE_CHANNEL CFG_INI_BOOL( \ + "gDisableChannel", \ + 0, \ + "Enable/Disable to disable channels specified") + +/* + * + * gEnableSARV1toSARV2 - Used to Enable/Disable SAR version conversion + * + * @Min: 0 + * @Max: 1 + * Default: 0 + * + * If user space is using SARV1 and FW is using SARV2 in BDF in that case + * this ini is used to enable conversion from user specified SARV1 command + * to FW expected SARV2 command. + * If value of this ini is set to 0, SAR version 1 will + * not be converted to SARV2 and command will be rejected. + * If value of this ini is set to 1 SAR version 1 will be converted to + * SARV2 based on FW capability + * Usage: External + * + * + */ +#define CFG_SAR_CONVERSION CFG_INI_BOOL( \ + "gEnableSARV1toSARV2", \ + 0, \ + "Enable/Disable conversion from SARV1 to SARV2") + +/* + * + * gDisableWow - Used to disable wow feature + * + * @Min: 0 + * @Max: 1 + * Default: 0 + * + * This ini is used to disable wow feature for all modes + * that means hlos platform suspend(cfg80211 suspend) will + * be rejected by wifi kernel driver. + * + * Usage: External + * + * + */ +#define CFG_WOW_DISABLE CFG_INI_UINT( \ + "gDisableWow", \ + 0, \ + 1, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Disable wow feature") + +/* + * + * nb_commands_interval - Used to rate limit nb commands from userspace + * + * @Min: 0 + * @Max: 10 + * Default: 3 + * + * This ini is used to specify the duration in which any supp. nb command from + * userspace will not be processed completely in driver. For ex, the default + * value of 3 seconds signifies that consecutive commands within that + * time will not be processed fully. + * + * Usage: Internal + * + * + */ +#define CFG_NB_COMMANDS_RATE_LIMIT CFG_INI_UINT( \ + "nb_commands_interval", \ + 0, \ + 10, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Rate limiting for nb commands") + +#ifdef WLAN_FEATURE_PERIODIC_STA_STATS +/* + * + * periodic_stats_timer_interval - Print selective stats on this specified + * interval + * + * @Min: 0 + * @Max: 10000 + * Default: 3000 + * + * This ini is used to specify interval in milliseconds for periodic stats + * timer. This timer will print selective stats after expiration of each + * interval. STA starts this periodic timer after initial connection or after + * roaming is successful. This will be restarted for every + * periodic_stats_timer_interval till the periodic_stats_timer_duration expires. + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_PERIODIC_STATS_TIMER_INTERVAL CFG_INI_UINT( \ + "periodic_stats_timer_interval", \ + 0, \ + 10000, \ + 3000, \ + CFG_VALUE_OR_DEFAULT, \ + "Periodic stats timer interval") + +/* + * + * periodic_stats_timer_duration - Used as duration for which periodic timer + * should run + * + * @Min: 0 + * @Max: 60000 + * Default: 30000 + * + * This ini is used as duration in milliseconds for which periodic stats timer + * should run. This periodic timer will print selective stats for every + * periodic_stats_timer_interval until this duration is reached. + * + * Supported Feature: STA + * + * Usage: Internal + * + * + */ +#define CFG_PERIODIC_STATS_TIMER_DURATION CFG_INI_UINT( \ + "periodic_stats_timer_duration", \ + 0, \ + 60000, \ + 30000, \ + CFG_VALUE_OR_DEFAULT, \ + "Periodic stats timer duration") + +#define CFG_WLAN_STA_PERIODIC_STATS \ + CFG(CFG_PERIODIC_STATS_TIMER_DURATION) \ + CFG(CFG_PERIODIC_STATS_TIMER_INTERVAL) +#else +#define CFG_WLAN_STA_PERIODIC_STATS +#endif /* WLAN_FEATURE_PERIODIC_STA_STATS */ + +/** + * enum host_log_level - Debug verbose level imposed by user + * @HOST_LOG_LEVEL_NONE: no trace will be logged. + * @HOST_LOG_LEVEL_FATAL: fatal error will be logged + * @HOST_LOG_LEVEL_ERROR: error(include level less than error) will be logged + * @HOST_LOG_LEVEL_WARN: warning(include level less than warning) will be logged + * @HOST_LOG_LEVEL_INFO: inform(include level less than inform) will be logged + * @HOST_LOG_LEVEL_DEBUG: debug(include level less than debug) will be logged + * @HOST_LOG_LEVEL_TRACE: trace(include level less than trace) will be logged + * @HOST_LOG_LEVEL_MAX: Max host log level + */ +enum host_log_level { + HOST_LOG_LEVEL_NONE = 0, + HOST_LOG_LEVEL_FATAL, + HOST_LOG_LEVEL_ERROR, + HOST_LOG_LEVEL_WARN, + HOST_LOG_LEVEL_INFO, + HOST_LOG_LEVEL_DEBUG, + HOST_LOG_LEVEL_TRACE, + HOST_LOG_LEVEL_MAX, +}; + +/* + * + * gHostModuleLoglevel - modulized host debug log level + * @Min: N/A + * @Max: N/A + * @Default: N/A + * + * This ini is used to set modulized host debug log level. + * WLAN host module log level input string format looks like below: + * gHostModuleLoglevel=",,..." + * For example: + * gHostModuleLoglevel=51,1,52,2,53,3,54,4,55,5,56,6 + * The above input string means: + * For WLAN host module ID 51 enable log level HOST_LOG_LEVEL_FATAL + * For WLAN host module ID 52 enable log level HOST_LOG_LEVEL_ERROR + * For WLAN host module ID 53 enable log level HOST_LOG_LEVEL_WARN + * For WLAN host module ID 54 enable log level HOST_LOG_LEVEL_INFO + * For WLAN host module ID 55 enable log level HOST_LOG_LEVEL_DEBUG + * For WLAN host module ID 55 enable log level HOST_LOG_LEVEL_TRACE + * For valid values of module ids check enum QDF_MODULE_ID and + * for valid values of log levels check below. + * HOST_LOG_LEVEL_NONE = 0, No trace will be logged + * HOST_LOG_LEVEL_FATAL = 1, fatal error log + * HOST_LOG_LEVEL_ERROR = 2, error(include level less than error) log + * HOST_LOG_LEVEL_WARN = 3, warning(include level less than warning) log + * HOST_LOG_LEVEL_INFO = 4, inform(include level less than inform) log + * HOST_LOG_LEVEL_DEBUG = 5, debug(include level less than debug) log + * HOST_LOG_LEVEL_TRACE = 6, trace(include level less than trace) log + * + * Related: None + * + * Supported Feature: Debugging + * + * Usage: Internal + * + * + */ + +#define HOST_MODULE_LOG_LEVEL_STRING_MAX_LENGTH (QDF_MODULE_ID_MAX * 6) +#define CFG_ENABLE_HOST_MODULE_LOG_LEVEL CFG_INI_STRING( \ + "gHostModuleLoglevel", \ + 0, \ + HOST_MODULE_LOG_LEVEL_STRING_MAX_LENGTH, \ + "", \ + "Set modulized host debug log level") + +/* + * + * gActionOUIForceMaxNss - Used to specify action OUIs for Max NSS connection + * @Default: + * Note: User should strictly add new action OUIs at the end of this + * default value. + * + * Default OUIs: (All values in Hex) + * OUI 1 :001018 + * OUI data Len : 06 + * OUI Data : 0201009c0000 + * OUI data Mask: FC + * Info Mask : 01 - only OUI present in Info mask + * OUI 2 :001018 + * OUI data Len : 06 + * OUI Data : 0201001c0000 + * OUI data Mask: FC + * Info Mask : 01 - only OUI present in Info mask + * OUI 3 :001018 + * OUI data Len : 06 + * OUI Data : 0200009c0000 + * OUI data Mask: FC + * Info Mask : 01 - only OUI present in Info mask + * + * This ini is used to specify the AP OUIs with which max capability is + * sent in association request even though AP advertises 1x1 capability. + * + * Related: None + * + * Supported Feature: Action OUIs + * + * Usage: External + * + * + */ +#define CFG_ACTION_OUI_FORCE_MAX_NSS CFG_INI_STRING( \ + "gActionOUIForceMaxNss", \ + 0, \ + ACTION_OUI_MAX_STR_LEN, \ + "001018 06 0201009c0000 FC 01 001018 06 0201001c0000 FC 01 001018 06 0200009c0000 FC 01", \ + "Used to specify action OUIs for forcing max NSS connection") + +#define CFG_HDD_ALL \ + CFG_ENABLE_PACKET_LOG_ALL \ + CFG_ENABLE_RUNTIME_PM_ALL \ + CFG_ENABLE_QMI_STATS_ALL \ + CFG_VC_MODE_BITMAP_ALL \ + CFG_WLAN_AUTO_SHUTDOWN_ALL \ + CFG_WLAN_LOGGING_SUPPORT_ALL \ + CFG_WLAN_STA_PERIODIC_STATS \ + CFG(CFG_ACTION_OUI_CCKM_1X1) \ + CFG(CFG_ACTION_OUI_CONNECT_1X1) \ + CFG(CFG_ACTION_OUI_CONNECT_1X1_WITH_1_CHAIN) \ + CFG(CFG_ACTION_OUI_ITO_ALTERNATE) \ + CFG(CFG_ACTION_OUI_ITO_EXTENSION) \ + CFG(CFG_ACTION_OUI_DISABLE_AGGRESSIVE_TX) \ + CFG(CFG_ACTION_OUI_FORCE_MAX_NSS) \ + CFG(CFG_ACTION_OUI_DISABLE_AGGRESSIVE_EDCA) \ + CFG(CFG_ACTION_OUI_SWITCH_TO_11N_MODE) \ + CFG(CFG_ACTION_OUI_RECONN_ASSOCTIMEOUT) \ + CFG(CFG_ACTION_OUI_DISABLE_TWT) \ + CFG(CFG_ADVERTISE_CONCURRENT_OPERATION) \ + CFG(CFG_BUG_ON_REINIT_FAILURE) \ + CFG(CFG_DBS_SCAN_SELECTION) \ + CFG(CFG_DERIVED_INTERFACE_POOL) \ + CFG(CFG_ENABLE_CONCURRENT_STA) \ + CFG(CFG_ENABLE_ACTION_OUI) \ + CFG(CFG_ENABLE_FW_LOG) \ + CFG(CFG_ENABLE_FW_UART_PRINT) \ + CFG(CFG_ENABLE_MAC_PROVISION) \ + CFG_ENABLE_MTRACE_ALL \ + CFG(CFG_ENABLE_RAMDUMP_COLLECTION) \ + CFG(CFG_ENABLE_UNIT_TEST_FRAMEWORK) \ + CFG(CFG_INTERFACE_CHANGE_WAIT) \ + CFG(CFG_INFORM_BSS_RSSI_RAW) \ + CFG(CFG_MULTICAST_HOST_FW_MSGS) \ + CFG(CFG_NUM_VDEV_ENABLE) \ + CFG(CFG_OPERATING_FREQUENCY) \ + CFG(CFG_PRIVATE_WEXT_CONTROL) \ + CFG(CFG_PROVISION_INTERFACE_POOL) \ + CFG(CFG_TIMER_MULTIPLIER) \ + CFG(CFG_NB_COMMANDS_RATE_LIMIT) \ + CFG(CFG_HDD_DOT11_MODE) \ + CFG(CFG_ENABLE_DISABLE_CHANNEL) \ + CFG(CFG_SAR_CONVERSION) \ + CFG(CFG_WOW_DISABLE) \ + CFG(CFG_ENABLE_HOST_MODULE_LOG_LEVEL) \ + SAR_SAFETY_FEATURE_ALL +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/hdd_dp_cfg.h b/drivers/staging/qcacld-3.0/core/hdd/inc/hdd_dp_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..f65f35fce467b499cbb81a97c901849fd856165c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/hdd_dp_cfg.h @@ -0,0 +1,1455 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __HDD_DP_CONFIG_H +#define __HDD_DP_CONFIG_H + +#define CFG_ENABLE_RX_THREAD BIT(0) +#define CFG_ENABLE_RPS BIT(1) +#define CFG_ENABLE_NAPI BIT(2) +#define CFG_ENABLE_DYNAMIC_RPS BIT(3) +#define CFG_ENABLE_DP_RX_THREADS BIT(4) +#define CFG_RX_MODE_MAX (CFG_ENABLE_RX_THREAD | \ + CFG_ENABLE_RPS | \ + CFG_ENABLE_NAPI | \ + CFG_ENABLE_DYNAMIC_RPS | \ + CFG_ENABLE_DP_RX_THREADS) +#ifdef MDM_PLATFORM +#define CFG_RX_MODE_DEFAULT 0 +#elif defined(HELIUMPLUS) +#define CFG_RX_MODE_DEFAULT CFG_ENABLE_NAPI +#endif + +#ifndef CFG_RX_MODE_DEFAULT +#if defined(FEATURE_WLAN_DP_RX_THREADS) +#define CFG_RX_MODE_DEFAULT (CFG_ENABLE_DP_RX_THREADS | CFG_ENABLE_NAPI) +#else +#define CFG_RX_MODE_DEFAULT (CFG_ENABLE_RX_THREAD | CFG_ENABLE_NAPI) +#endif +#endif + +/* Max # of packets to be processed in 1 tx comp loop */ +#define CFG_DP_TX_COMP_LOOP_PKT_LIMIT_DEFAULT 64 +#define CFG_DP_TX_COMP_LOOP_PKT_LIMIT_MAX (1024 * 1024) + +/*Max # of packets to be processed in 1 rx reap loop */ +#define CFG_DP_RX_REAP_LOOP_PKT_LIMIT_DEFAULT 64 +#define CFG_DP_RX_REAP_LOOP_PKT_LIMIT_MAX (1024 * 1024) + +/* Max # of HP OOS (out of sync) updates */ +#define CFG_DP_RX_HP_OOS_UPDATE_LIMIT_DEFAULT 0 +#define CFG_DP_RX_HP_OOS_UPDATE_LIMIT_MAX 1024 + +/* Max Yield time duration for RX Softirq */ +#define CFG_DP_RX_SOFTIRQ_MAX_YIELD_TIME_NS_DEFAULT (500 * 1000) +#define CFG_DP_RX_SOFTIRQ_MAX_YIELD_TIME_NS_MAX (10 * 1000 * 1000) + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + +/* + * + * TxFlowLowWaterMark - Low watermark for pausing network queues + * + * @Min: 0 + * @Max: 1000 + * @Default: 300 + * + * This ini specifies the low watermark of data packets transmitted + * before pausing netif queues in tx flow path. It is only applicable + * where legacy flow control is used i.e.for Rome. + * + * Related: TxFlowHighWaterMarkOffset, TxFlowMaxQueueDepth, + * TxLbwFlowLowWaterMark, TxLbwFlowHighWaterMarkOffset, + * TxLbwFlowMaxQueueDepth, TxHbwFlowLowWaterMark, + * TxHbwFlowHighWaterMarkOffset, TxHbwFlowMaxQueueDepth + * + * Supported Feature: Dynamic Flow Control + * + * Usage: Internal + * + * + */ +#define CFG_DP_LL_TX_FLOW_LWM \ + CFG_INI_UINT( \ + "TxFlowLowWaterMark", \ + 0, \ + 1000, \ + 300, \ + CFG_VALUE_OR_DEFAULT, \ + "Low watermark for pausing network queues") + +/* + * + * TxFlowHighWaterMarkOffset - High Watermark offset to unpause Netif queues + * @Min: 0 + * @Max: 300 + * @Default: 94 + * + * This ini specifies the offset to upause the netif queues + * when they are paused due to insufficient descriptors as guided by + * ini TxFlowLowWaterMark. It is only applicable where legacy flow control + * is used i.e.for Rome. + * + * Related: TxFlowLowWaterMark, TxFlowMaxQueueDepth, + * TxLbwFlowLowWaterMark, TxLbwFlowHighWaterMarkOffset, + * TxLbwFlowMaxQueueDepth, TxHbwFlowLowWaterMark, + * TxHbwFlowHighWaterMarkOffset, TxHbwFlowMaxQueueDepth + * + * Supported Feature: Dynamic Flow Control + * + * Usage: Internal + * + * + */ +#define CFG_DP_LL_TX_FLOW_HWM_OFFSET \ + CFG_INI_UINT( \ + "TxFlowHighWaterMarkOffset", \ + 0, \ + 300, \ + 94, \ + CFG_VALUE_OR_DEFAULT, \ + "High Watermark offset to unpause Netif queues") + +/* + * + * TxFlowMaxQueueDepth - Max pause queue depth. + * + * @Min: 400 + * @Max: 3500 + * @Default: 1500 + * + * This ini specifies the max queue pause depth.It is only applicable + * where legacy flow control is used i.e.for Rome. + * + * Related: TxFlowLowWaterMark, TxFlowHighWaterMarkOffset, + * TxLbwFlowLowWaterMark, TxLbwFlowHighWaterMarkOffset, + * TxLbwFlowMaxQueueDepth, TxHbwFlowLowWaterMark, + * TxHbwFlowHighWaterMarkOffset, TxHbwFlowMaxQueueDepth + * + * Supported Feature: Dynamic Flow Control + * + * Usage: Internal + * + * + */ +#define CFG_DP_LL_TX_FLOW_MAX_Q_DEPTH \ + CFG_INI_UINT( \ + "TxFlowMaxQueueDepth", \ + 400, \ + 3500, \ + 1500, \ + CFG_VALUE_OR_DEFAULT, \ + "Max pause queue depth") + +/* + * + * TxLbwFlowLowWaterMark - Low watermark for pausing network queues + * in low bandwidth band + * @Min: 0 + * @Max: 1000 + * @Default: 450 + * + * This ini specifies the low watermark of data packets transmitted + * before pausing netif queues in tx flow path in low bandwidth band. + * It is only applicable where legacy flow control is used i.e.for Rome. + * + * Related: TxFlowLowWaterMark, TxFlowHighWaterMarkOffset, + * TxFlowMaxQueueDepth, TxLbwFlowHighWaterMarkOffset, + * TxLbwFlowMaxQueueDepth, TxHbwFlowLowWaterMark, + * TxHbwFlowHighWaterMarkOffset, TxHbwFlowMaxQueueDepth + * + * Supported Feature: Dynamic Flow Control + * + * Usage: Internal + * + * + */ +#define CFG_DP_LL_TX_LBW_FLOW_LWM \ + CFG_INI_UINT( \ + "TxLbwFlowLowWaterMark", \ + 0, \ + 1000, \ + 450, \ + CFG_VALUE_OR_DEFAULT, \ + "Low watermark for pausing network queues") + +/* + * + * TxLbwFlowHighWaterMarkOffset - High Watermark offset to unpause Netif queues + * in low bandwidth band. + * @Min: 0 + * @Max: 300 + * @Default: 50 + * + * This ini specifies the offset to upause the netif queues + * when they are paused due to insufficient descriptors as guided by + * ini TxLbwFlowLowWaterMark in low bandwidth band. It is only applicable + * where legacy flow control is used i.e.for Rome. + * + * Related: TxFlowLowWaterMark, TxFlowHighWaterMarkOffset, + * TxFlowMaxQueueDepth, TxLbwFlowLowWaterMark, + * TxLbwFlowMaxQueueDepth, TxHbwFlowLowWaterMark, + * TxHbwFlowHighWaterMarkOffset, TxHbwFlowMaxQueueDepth + * + * Supported Feature: Dynamic Flow Control + * + * Usage: Internal + * + * + */ +#define CFG_DP_LL_TX_LBW_FLOW_HWM_OFFSET \ + CFG_INI_UINT( \ + "TxLbwFlowHighWaterMarkOffset", \ + 0, \ + 300, \ + 50, \ + CFG_VALUE_OR_DEFAULT, \ + "High Watermark offset to unpause Netif queues") + +/* + * + * TxLbwFlowMaxQueueDepth - Max pause queue depth in low bandwidth band + * + * @Min: 400 + * @Max: 3500 + * @Default: 750 + * + * This ini specifies the max queue pause depth in low bandwidth band. + * It is only applicable where legacy flow control is used i.e.for Rome. + * + * Related: TxFlowLowWaterMark, TxFlowHighWaterMarkOffset, + * TxFlowMaxQueueDepth, TxLbwFlowLowWaterMark, + * TxLbwFlowHighWaterMarkOffset, TxHbwFlowLowWaterMark, + * TxHbwFlowHighWaterMarkOffset, TxHbwFlowMaxQueueDepth + * + * Supported Feature: Dynamic Flow Control + * + * Usage: Internal + * + * + */ +#define CFG_DP_LL_TX_LBW_FLOW_MAX_Q_DEPTH \ + CFG_INI_UINT( \ + "TxLbwFlowMaxQueueDepth", \ + 400, \ + 3500, \ + 750, \ + CFG_VALUE_OR_DEFAULT, \ + "Max pause queue depth in low bandwidth band") + +/* + * + * TxHbwFlowLowWaterMark - Low watermark for pausing network queues + * in high bandwidth band + * @Min: 0 + * @Max: 1000 + * @Default: 406 + * + * This ini specifies the threshold of data packets transmitted + * before pausing netif queues.It is only applicable where + * legacy flow control is used i.e.for Rome. + * + * Related: TxFlowLowWaterMark, TxFlowHighWaterMarkOffset, + * TxFlowMaxQueueDepth, TxLbwFlowLowWaterMark, + * TxLbwFlowHighWaterMarkOffset, TxLbwFlowMaxQueueDepth, + * TxHbwFlowHighWaterMarkOffset, TxHbwFlowMaxQueueDepth + * + * Supported Feature: Dynamic Flow Control + * + * Usage: Internal + * + * + */ +#define CFG_DP_LL_TX_HBW_FLOW_LWM \ + CFG_INI_UINT( \ + "TxHbwFlowLowWaterMark", \ + 0, \ + 1000, \ + 406, \ + CFG_VALUE_OR_DEFAULT, \ + "Low watermark for pausing network queues") + +/* + * + * TxHbwFlowHighWaterMarkOffset - High Watermark offset to unpause Netif queues + * in high bandwidth band. + * @Min: 0 + * @Max: 300 + * @Default: 94 + * + * This ini specifies the offset to upause the netif queues + * when they are paused due to insufficient descriptors as guided by + * ini TxHbwFlowLowWaterMark in high bandwidth band. It is only applicable + * where legacy flow control is used i.e.for Rome. + * + * Related: TxFlowLowWaterMark, TxFlowHighWaterMarkOffset, + * TxFlowMaxQueueDepth, TxLbwFlowLowWaterMark, + * TxLbwFlowHighWaterMarkOffset, TxLbwFlowMaxQueueDepth, + * TxHbwFlowLowWaterMark, TxHbwFlowMaxQueueDepth + * + * Supported Feature: Dynamic Flow Control + * + * Usage: Internal + * + * + */ +#define CFG_DP_LL_TX_HBW_FLOW_HWM_OFFSET \ + CFG_INI_UINT( \ + "TxHbwFlowHighWaterMarkOffset", \ + 0, \ + 300, \ + 94, \ + CFG_VALUE_OR_DEFAULT, \ + "High Watermark offset to unpause Netif queues") + +/* + * + * TxHbwFlowMaxQueueDepth - Max pause queue depth in high bandwidth band + * @Min: 4000 + * @Max: 3500 + * @Default: 1500 + * + * This ini specifies the max queue pause depth in high bandwidth band. + * It is only applicable where legacy flow control is used i.e.for Rome. + * + * Related: TxFlowLowWaterMark, TxFlowHighWaterMarkOffset, + * TxFlowMaxQueueDepth, TxLbwFlowLowWaterMark, + * TxLbwFlowHighWaterMarkOffset, TxLbwFlowMaxQueueDepth, + * TxHbwFlowLowWaterMark, TxHbwFlowHighWaterMarkOffset + * + * Supported Feature: Dynamic Flow Control + * + * Usage: Internal + * + * + */ +#define CFG_DP_LL_TX_HBW_FLOW_MAX_Q_DEPTH \ + CFG_INI_UINT( \ + "TxHbwFlowMaxQueueDepth", \ + 400, \ + 3500, \ + 1500, \ + CFG_VALUE_OR_DEFAULT, \ + "Max pause queue depth in high bandwidth band") + +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +/* + * + * gBusBandwidthVeryHighThreshold - bus bandwidth very high threshold + * + * @Min: 0 + * @Max: 4294967295UL + * @Default: 9000 + * + * This ini specifies the bus bandwidth very high threshold + * + * Usage: Internal + * + * + */ +#define CFG_DP_BUS_BANDWIDTH_VERY_HIGH_THRESHOLD \ + CFG_INI_UINT( \ + "gBusBandwidthVeryHighThreshold", \ + 0, \ + 4294967295UL, \ + 9000, \ + CFG_VALUE_OR_DEFAULT, \ + "Bus bandwidth very high threshold") + +/* + * + * gBusBandwidthHighThreshold - bus bandwidth high threshold + * + * @Min: 0 + * @Max: 4294967295UL + * @Default: 2000 + * + * This ini specifies the bus bandwidth high threshold + * + * Usage: Internal + * + * + */ +#define CFG_DP_BUS_BANDWIDTH_HIGH_THRESHOLD \ + CFG_INI_UINT( \ + "gBusBandwidthHighThreshold", \ + 0, \ + 4294967295UL, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "Bus bandwidth high threshold") + +/* + * + * gBusBandwidthMediumThreshold - bus bandwidth medium threshold + * + * @Min: 0 + * @Max: 4294967295UL + * @Default: 500 + * + * This ini specifies the bus bandwidth medium threshold + * + * Usage: Internal + * + * + */ +#define CFG_DP_BUS_BANDWIDTH_MEDIUM_THRESHOLD \ + CFG_INI_UINT( \ + "gBusBandwidthMediumThreshold", \ + 0, \ + 4294967295UL, \ + 500, \ + CFG_VALUE_OR_DEFAULT, \ + "Bus bandwidth medium threshold") + +/* + * + * gBusBandwidthLowThreshold - bus bandwidth low threshold + * + * @Min: 0 + * @Max: 4294967295UL + * @Default: 150 + * + * This ini specifies the bus bandwidth low threshold + * + * Usage: Internal + * + * + */ +#define CFG_DP_BUS_BANDWIDTH_LOW_THRESHOLD \ + CFG_INI_UINT( \ + "gBusBandwidthLowThreshold", \ + 0, \ + 4294967295UL, \ + 150, \ + CFG_VALUE_OR_DEFAULT, \ + "Bus bandwidth low threshold") + +/* + * + * gBusBandwidthComputeInterval - bus bandwidth compute interval + * + * @Min: 0 + * @Max: 10000 + * @Default: 100 + * + * This ini specifies thebus bandwidth compute interval + * + * Usage: Internal + * + * + */ +#define CFG_DP_BUS_BANDWIDTH_COMPUTE_INTERVAL \ + CFG_INI_UINT( \ + "gBusBandwidthComputeInterval", \ + 0, \ + 10000, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "Bus bandwidth compute interval") + +/* + * + * gTcpLimitOutputEnable - Control to enable TCP limit output byte + * @Default: true + * + * This ini is used to enable dynamic configuration of TCP limit output bytes + * tcp_limit_output_bytes param. Enabling this will let driver post message to + * cnss-daemon, accordingly cnss-daemon will modify the tcp_limit_output_bytes. + * + * Supported Feature: Tcp limit output bytes + * + * Usage: Internal + * + * + */ +#define CFG_DP_ENABLE_TCP_LIMIT_OUTPUT \ + CFG_INI_BOOL( \ + "gTcpLimitOutputEnable", \ + true, \ + "Control to enable TCP limit output byte") + +/* + * + * gTcpAdvWinScaleEnable - Control to enable TCP adv window scaling + * @Default: true + * + * This ini is used to enable dynamic configuration of TCP adv window scaling + * system parameter. + * + * Supported Feature: Tcp Advance Window Scaling + * + * Usage: Internal + * + * + */ +#define CFG_DP_ENABLE_TCP_ADV_WIN_SCALE \ + CFG_INI_BOOL( \ + "gTcpAdvWinScaleEnable", \ + true, \ + "Control to enable TCP adv window scaling") + +/* + * + * gTcpDelAckEnable - Control to enable Dynamic Configuration of Tcp Delayed Ack + * @Default: true + * + * This ini is used to enable Dynamic Configuration of Tcp Delayed Ack + * + * Related: gTcpDelAckThresholdHigh, gTcpDelAckThresholdLow, + * gTcpDelAckTimerCount + * + * Supported Feature: Tcp Delayed Ack + * + * Usage: Internal + * + * + */ +#define CFG_DP_ENABLE_TCP_DELACK \ + CFG_INI_BOOL( \ + "gTcpDelAckEnable", \ + true, \ + "Control to enable Dynamic Config of Tcp Delayed Ack") + +/* + * + * gTcpDelAckThresholdHigh - High Threshold inorder to trigger TCP Del Ack + * indication + * @Min: 0 + * @Max: 16000 + * @Default: 500 + * + * This ini is used to mention the High Threshold inorder to trigger TCP Del Ack + * indication i.e the threshold of packets received over a period of 100 ms. + * i.e to have a low RX throughput requirement + * Related: gTcpDelAckEnable, gTcpDelAckThresholdLow, gTcpDelAckTimerCount + * + * Supported Feature: Tcp Delayed Ack + * + * Usage: Internal + * + * + */ +#define CFG_DP_TCP_DELACK_THRESHOLD_HIGH \ + CFG_INI_UINT( \ + "gTcpDelAckThresholdHigh", \ + 0, \ + 16000, \ + 500, \ + CFG_VALUE_OR_DEFAULT, \ + "High Threshold inorder to trigger TCP Del Ack") + +/* + * + * gTcpDelAckThresholdLow - Low Threshold inorder to trigger TCP Del Ack + * indication + * @Min: 0 + * @Max: 10000 + * @Default: 1000 + * + * This ini is used to mention the Low Threshold inorder to trigger TCP Del Ack + * indication i.e the threshold of packets received over a period of 100 ms. + * i.e to have a low RX throughput requirement + * + * Related: gTcpDelAckEnable, gTcpDelAckThresholdHigh, gTcpDelAckTimerCount + * + * Supported Feature: Tcp Delayed Ack + * + * Usage: Internal + * + * + */ +#define CFG_DP_TCP_DELACK_THRESHOLD_LOW \ + CFG_INI_UINT( \ + "gTcpDelAckThresholdLow", \ + 0, \ + 10000, \ + 1000, \ + CFG_VALUE_OR_DEFAULT, \ + "Low Threshold inorder to trigger TCP Del Ack") + +/* + * + * gTcpDelAckTimerCount - Del Ack Timer Count inorder to trigger TCP Del Ack + * indication + * @Min: 1 + * @Max: 1000 + * @Default: 30 + * + * This ini is used to mention the Del Ack Timer Count inorder to + * trigger TCP Del Ack indication i.e number of 100 ms periods + * + * Related: gTcpDelAckEnable, gTcpDelAckThresholdHigh, gTcpDelAckThresholdLow + * + * Supported Feature: Tcp Delayed Ack + * + * Usage: Internal + * + * + */ +#define CFG_DP_TCP_DELACK_TIMER_COUNT \ + CFG_INI_UINT( \ + "gTcpDelAckTimerCount", \ + 1, \ + 1000, \ + 30, \ + CFG_VALUE_OR_DEFAULT, \ + "Del Ack Timer Count inorder to trigger TCP Del Ack") + +/* + * + * gTcpTxHighTputThreshold - High Threshold inorder to trigger High + * Tx Throughput requirement. + * @Min: 0 + * @Max: 16000 + * @Default: 500 + * + * This ini specifies the threshold of packets transmitted + * over a period of 100 ms beyond which TCP can be considered to have a high + * TX throughput requirement. The driver uses this condition to tweak TCP TX + * specific parameters (via cnss-daemon) + * + * Supported Feature: To tweak TCP TX n/w parameters + * + * Usage: Internal + * + * + */ +#define CFG_DP_TCP_TX_HIGH_TPUT_THRESHOLD \ + CFG_INI_UINT( \ + "gTcpTxHighTputThreshold", \ + 0, \ + 16000, \ + 500, \ + CFG_VALUE_OR_DEFAULT, \ + "High Threshold inorder to trigger High Tx Tp") + +/* + * + * gBusLowTputCntThreshold - Threshold count to trigger low Tput + * GRO flush skip + * @Min: 0 + * @Max: 200 + * @Default: 10 + * + * This ini is a threshold that if count of times for bus Tput level + * PLD_BUS_WIDTH_LOW in bus_bw_timer() >= this threshold, will enable skipping + * GRO flush, current default threshold is 10, then will delay GRO flush-skip + * 1 second for low Tput level. + * + * Supported Feature: GRO flush skip when low T-put + * + * Usage: Internal + * + * + */ +#define CFG_DP_BUS_LOW_BW_CNT_THRESHOLD \ + CFG_INI_UINT( \ + "gBusLowTputCntThreshold", \ + 0, \ + 200, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Threshold to trigger GRO flush skip for low T-put") + +/* + * + * gHandleLatencyCriticalClients - Enable the handling of latency critical + * clients in bus bandwidth timer. + * @Default: false + * + * This ini enables the handling of latency critical clients, eg: 11g/a + * clients, when they are running their corresponding peak throughput. + * + * Supported Feature: Latency critical clients in host + * + * Usage: External + * + * + */ +#define CFG_DP_BUS_HANDLE_LATENCY_CRITICAL_CLIENTS \ + CFG_INI_BOOL( \ + "gHandleLatencyCriticalClients", \ + false, \ + "Control to enable latency critical clients") + +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK +/* + * + * gDriverDelAckHighThreshold - High Threshold inorder to trigger TCP + * delay ack feature in the host. + * @Min: 0 + * @Max: 70000 + * @Default: 300 + * + * This ini specifies the threshold of RX packets transmitted + * over a period of 100 ms beyond which TCP delay ack can be enabled + * to improve TCP RX throughput requirement. + * + * Supported Feature: Tcp Delayed Ack in the host + * + * Usage: Internal + * + * + */ +#define CFG_DP_DRIVER_TCP_DELACK_HIGH_THRESHOLD \ + CFG_INI_UINT( \ + "gDriverDelAckHighThreshold", \ + 0, \ + 70000, \ + 300, \ + CFG_VALUE_OR_DEFAULT, \ + "TCP delack high threshold") + +/* + * + * gDriverDelAckLowThreshold - Low Threshold inorder to disable TCP + * delay ack feature in the host. + * @Min: 0 + * @Max: 70000 + * @Default: 100 + * + * This ini is used to mention the Low Threshold inorder to disable TCP Del + * Ack feature in the host. + * + * Supported Feature: Tcp Delayed Ack in the host + * + * Usage: Internal + * + * + */ +#define CFG_DP_DRIVER_TCP_DELACK_LOW_THRESHOLD \ + CFG_INI_UINT( \ + "gDriverDelAckLowThreshold", \ + 0, \ + 70000, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "TCP delack low threshold") + +/* + * + * gDriverDelAckTimerValue - Timeout value (ms) to send out all TCP del + * ack frames + * @Min: 1 + * @Max: 15 + * @Default: 3 + * + * This ini specifies the time out value to send out all pending TCP delay + * ACK frames. + * + * Supported Feature: Tcp Delayed Ack in the host + * + * Usage: Internal + * + * + */ +#define CFG_DP_DRIVER_TCP_DELACK_TIMER_VALUE \ + CFG_INI_UINT( \ + "gDriverDelAckTimerValue", \ + 1, \ + 15, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Send out all TCP Del Acks if time out") + +/* + * + * gDriverDelAckPktCount - The maximum number of TCP delay ack frames + * @Min: 0 + * @Max: 50 + * @Default: 20 + * + * This ini specifies the maximum number of TCP delayed ack frames. + * + * Supported Feature: Tcp Delayed Ack in the host + * + * Usage: Internal + * + * + */ +#define CFG_DP_DRIVER_TCP_DELACK_PKT_CNT \ + CFG_INI_UINT( \ + "gDriverDelAckPktCount", \ + 0, \ + 50, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "No of TCP Del ACK count") + +/* + * + * gDriverDelAckEnable - Control to enable Dynamic Configuration of Tcp + * Delayed Ack in the host. + * @Default: true + * + * This ini is used to enable Dynamic Configuration of Tcp Delayed Ack + * in the host. + * + * Related: gDriverDelAckHighThreshold, gDriverDelAckLowThreshold, + * gDriverDelAckPktCount, gDriverDelAckTimerValue + * + * Supported Feature: Tcp Delayed Ack in the host + * + * Usage: Internal + * + * + */ +#define CFG_DP_DRIVER_TCP_DELACK_ENABLE \ + CFG_INI_BOOL( \ + "gDriverDelAckEnable", \ + true, \ + "Enable tcp del ack in the driver") +#endif + +/* + * + * NAPI_CPU_AFFINITY_MASK - CPU mask to affine NAPIs + * + * @Min: 0 + * @Max: 0xFF + * @Default: 0 + * + * This ini is used to set NAPI IRQ CPU affinity + * + * Supported Feature: NAPI + * + * Usage: Internal + * + * + */ +#define CFG_DP_NAPI_CE_CPU_MASK \ + CFG_INI_UINT( \ + "NAPI_CPU_AFFINITY_MASK", \ + 0, \ + 0xFF, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "CPU mask to affine NAPIs") + +/* + * + * RX_THREAD_CPU_AFFINITY_MASK - CPU mask to affine Rx_thread + * + * @Min: 0 + * @Max: 0xFF + * @Default: 0x02 + * + * This ini is used to set Rx_thread CPU affinity + * + * Supported Feature: Rx_thread + * + * Usage: Internal + * + * + */ +#ifdef RX_PERFORMANCE +#define CFG_DP_RX_THREAD_CPU_MASK \ + CFG_INI_UINT( \ + "RX_THREAD_CPU_AFFINITY_MASK", \ + 0, \ + 0xFF, \ + 0xFE, \ + CFG_VALUE_OR_DEFAULT, \ + "CPU mask to affine Rx_thread") +#else +#define CFG_DP_RX_THREAD_CPU_MASK \ + CFG_INI_UINT( \ + "RX_THREAD_CPU_AFFINITY_MASK", \ + 0, \ + 0xFF, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "CPU mask to affine Rx_thread") +#endif + +/* + * + * RX_THREAD_UL_CPU_AFFINITY_MASK - CPU mask to affine Rx_thread + * + * @Min: 0 + * @Max: 0xFF + * @Default: 0x0 + * + * This ini is used to set Rx_thread CPU affinity for uplink traffic + * + * Supported Feature: Rx_thread + * + * Usage: Internal + * + * + */ +#define CFG_DP_RX_THREAD_UL_CPU_MASK \ + CFG_INI_UINT( \ + "RX_THREAD_UL_CPU_AFFINITY_MASK", \ + 0, \ + 0xFF, \ + 0x0, \ + CFG_VALUE_OR_DEFAULT, \ + "CPU mask to affine Rx_thread for uplink traffic") + +/* + * + * rpsRxQueueCpuMapList - RPS map for different RX queues + * + * @Default: e + * + * This ini is used to set RPS map for different RX queues. + * + * List of RPS CPU maps for different rx queues registered by WLAN driver + * Ref - Kernel/Documentation/networking/scaling.txt + * RPS CPU map for a particular RX queue, selects CPU(s) for bottom half + * processing of RX packets. For example, for a system with 4 CPUs, + * 0xe: Use CPU1 - CPU3 and donot use CPU0. + * 0x0: RPS is disabled, packets are processed on the interrupting CPU. +.* + * WLAN driver registers NUM_TX_QUEUES queues for tx and rx each during + * alloc_netdev_mq. Hence, we need to have a cpu mask for each of the rx queues. + * + * For example, if the NUM_TX_QUEUES is 4, a sample WLAN ini entry may look like + * rpsRxQueueCpuMapList=a b c d + * For a 4 CPU system (CPU0 - CPU3), this implies: + * 0xa - (1010) use CPU1, CPU3 for rx queue 0 + * 0xb - (1011) use CPU0, CPU1 and CPU3 for rx queue 1 + * 0xc - (1100) use CPU2, CPU3 for rx queue 2 + * 0xd - (1101) use CPU0, CPU2 and CPU3 for rx queue 3 + + * In practice, we may want to avoid the cores which are heavily loaded. + * + * Default value of rpsRxQueueCpuMapList. Different platforms may have + * different configurations for NUM_TX_QUEUES and # of cpus, and will need to + * configure an appropriate value via ini file. Setting default value to 'e' to + * avoid use of CPU0 (since its heavily used by other system processes) by rx + * queue 0, which is currently being used for rx packet processing. + * + * Maximum length of string used to hold a list of cpu maps for various rx + * queues. Considering a 16 core system with 5 rx queues, a RPS CPU map + * list may look like - + * rpsRxQueueCpuMapList = ffff ffff ffff ffff ffff + * (all 5 rx queues can be processed on all 16 cores) + * max string len = 24 + 1(for '\0'). Considering 30 to be on safe side. + * + * Supported Feature: Rx_thread + * + * Usage: Internal + * + */ +#define CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST \ + CFG_INI_STRING( \ + "rpsRxQueueCpuMapList", \ + 1, \ + 30, \ + "e", \ + "specify RPS map for different RX queus") + +/* + * + * gEnableTxOrphan- Enable/Disable orphaning of Tx packets + * @Default: false + * + * This ini is used to enable/disable orphaning of Tx packets. + * + * Related: None + * + * Usage: External + * + * + */ +#define CFG_DP_TX_ORPHAN_ENABLE \ + CFG_INI_BOOL( \ + "gEnableTxOrphan", \ + false, \ + "orphaning of Tx packets") + +/* + * + * rx_mode - Control to decide rx mode for packet procesing + * + * @Min: 0 + * @Max: (CFG_ENABLE_RX_THREAD | CFG_ENABLE_RPS | CFG_ENABLE_NAPI | \ + * CFG_ENABLE_DYNAMIC_RPS) + * + * Some possible configurations: + * rx_mode=0 - Uses tasklets for bottom half + * CFG_ENABLE_NAPI (rx_mode=4) - Uses NAPI for bottom half + * CFG_ENABLE_RX_THREAD | CFG_ENABLE_NAPI (rx_mode=5) - NAPI for bottom half, + * rx_thread for stack. Single threaded. + * CFG_ENABLE_DP_RX_THREAD | CFG_ENABLE_NAPI (rx_mode=10) - NAPI for bottom + * half, dp_rx_thread for stack processing. Supports multiple rx threads. + * + * Usage: Internal + * + * + */ +#define CFG_DP_RX_MODE \ + CFG_INI_UINT("rx_mode", \ + 0, CFG_RX_MODE_MAX, CFG_RX_MODE_DEFAULT, \ + CFG_VALUE_OR_DEFAULT, \ + "Control to decide rx mode for packet procesing") + +/* + * + * tx_comp_loop_pkt_limit - Control to decide max # of packets to be processed + * in 1 tx comp loop + * + * @Min: 8 + * @Max: CFG_DP_TX_COMP_LOOP_PKT_LIMIT_MAX + * + * Usage: Internal + * + * + */ +#define CFG_DP_TX_COMP_LOOP_PKT_LIMIT \ + CFG_INI_UINT("tx_comp_loop_pkt_limit", \ + 1, CFG_DP_TX_COMP_LOOP_PKT_LIMIT_MAX, \ + CFG_DP_TX_COMP_LOOP_PKT_LIMIT_DEFAULT, \ + CFG_VALUE_OR_DEFAULT, \ + "Control to decide tx comp loop pkt limit") +/* + * + * rx_reap_loop_pkt_limit - Control to decide max # of packets to be reaped + * in 1 dp_rx_process reap loop + * + * @Min: 8 + * @Max: CFG_DP_RX_REAP_LOOP_PKT_LIMIT_MAX + * + * Usage: Internal + * + * + */ +#define CFG_DP_RX_REAP_LOOP_PKT_LIMIT \ + CFG_INI_UINT("rx_reap_loop_pkt_limit", \ + 0, CFG_DP_RX_REAP_LOOP_PKT_LIMIT_MAX, \ + CFG_DP_RX_REAP_LOOP_PKT_LIMIT_DEFAULT, \ + CFG_VALUE_OR_DEFAULT, \ + "Control to decide rx reap loop packet limit") + +/* + * + * rx_hp_oos_update_limit - Control to decide max # of HP OOS (out of sync) + * updates + * + * @Min: 0 + * @Max: CFG_DP_RX_HP_OOS_UPDATE_LIMIT_MAX + * + * Usage: Internal + * + * + */ +#define CFG_DP_RX_HP_OOS_UPDATE_LIMIT \ + CFG_INI_UINT("rx_hp_oos_update_limit", \ + 0, CFG_DP_RX_HP_OOS_UPDATE_LIMIT_MAX, \ + CFG_DP_RX_HP_OOS_UPDATE_LIMIT_DEFAULT, \ + CFG_VALUE_OR_DEFAULT, \ + "Control to decide HP OOS update limit") + +/* + * + * rx_softirq_max_yield_duration_ns - Control to decide max duration for RX + * softirq + * + * @Min: 100 * 1000 , 100us + * @Max: CFG_DP_RX_SOFTIRQ_MAX_YIELD_TIME_NS_MAX + * + * Usage: Internal + * + * + */ +#define CFG_DP_RX_SOFTIRQ_MAX_YIELD_TIME_NS \ + CFG_INI_UINT("rx_softirq_max_yield_duration_ns", \ + 100 * 1000, CFG_DP_RX_SOFTIRQ_MAX_YIELD_TIME_NS_MAX, \ + CFG_DP_RX_SOFTIRQ_MAX_YIELD_TIME_NS_DEFAULT, \ + CFG_VALUE_OR_DEFAULT, \ + "max yield time duration for RX Softirq") + +/* + * + * enable_multicast_replay_filter - Enable filtering of replayed multicast + * packets + * + * In a typical infrastructure setup, it is quite normal to receive + * replayed multicast packets. These packets may cause more harm than + * help if not handled properly. Providing a configuration option + * to enable filtering of such packets + * + * + */ +#define CFG_DP_FILTER_MULTICAST_REPLAY \ + CFG_INI_BOOL("enable_multicast_replay_filter", \ + true, "Enable filtering of replayed multicast packets") + +/* + * + * rx_wakelock_timeout - Amount of time to hold wakelock for RX unicast packets + * @Min: 0 + * @Max: 100 + * @Default: 50 + * + * This ini item configures the amount of time, in milliseconds, that the driver + * should prevent system power collapse after receiving an RX unicast packet. + * A conigured value of 0 disables the RX Wakelock feature completely. + * + * Related: None. + * + * Supported Feature: RX Wakelock + * + * Usage: Internal/External + * + * + */ +#define CFG_DP_RX_WAKELOCK_TIMEOUT \ + CFG_INI_UINT("rx_wakelock_timeout", \ + 0, 100, 50, CFG_VALUE_OR_DEFAULT, \ + "Amount of time to hold wakelock for RX unicast packets") + +/* + * + * num_dp_rx_threads - Control to set the number of dp rx threads + * + * @Min: 1 + * @Max: 4 + * @Default: 1 + * + * Usage: Internal + * + * + */ +#define CFG_DP_NUM_DP_RX_THREADS \ + CFG_INI_UINT("num_dp_rx_threads", \ + 1, 4, 1, CFG_VALUE_OR_DEFAULT, \ + "Control to set the number of dp rx threads") + +/* + * + * ce_service_max_rx_ind_flush - Maximum number of HTT messages + * to be processed per NAPI poll + * + * @Min: 1 + * @Max: 32 + * @Default: 1 + * + * Usage: Internal + * + * + */ +#define CFG_DP_CE_SERVICE_MAX_RX_IND_FLUSH \ + CFG_INI_UINT("ce_service_max_rx_ind_flush", \ + 1, 32, 1, \ + CFG_VALUE_OR_DEFAULT, "Ctrl to set ce service max rx ind flsh") + +/* + * + * ce_service_max_yield_time - Time in microseconds after which + * a NAPI poll must yield + * + * @Min: 500 + * @Max: 10000 + * @Default: 500 + * + * Usage: Internal + * + * + */ +#define CFG_DP_CE_SERVICE_MAX_YIELD_TIME \ + CFG_INI_UINT("ce_service_max_yield_time", \ + 500, 10000, 500, \ + CFG_VALUE_OR_DEFAULT, "Ctrl to set ce service max yield time") + +#ifdef WLAN_FEATURE_FASTPATH +#define CFG_DP_ENABLE_FASTPATH \ + CFG_INI_BOOL("gEnableFastPath", \ + false, "Ctrl to enable fastpath feature") + +#define CFG_DP_ENABLE_FASTPATH_ALL \ + CFG(CFG_DP_ENABLE_FASTPATH) +#else +#define CFG_DP_ENABLE_FASTPATH_ALL +#endif + +#define CFG_DP_ENABLE_TCP_PARAM_UPDATE \ + CFG_INI_BOOL("enable_tcp_param_update", \ + false, "configure TCP param through Wi-Fi HAL") +/* + * + * + * Enable/disable DPTRACE + * Enabling this might have performace impact. + * + * Config DPTRACE + * The sequence of params is important. If some param is missing, defaults are + * considered. + * Param 1: Enable/Disable DP Trace live mode (uint8_t) + * Param 2: DP Trace live mode high bandwidth thresh.(uint8_t) + * (packets/second) beyond which DP Trace is disabled. Decimal Val. + * MGMT, DHCP, EAPOL, ARP pkts are not counted. ICMP and Data are. + * Param 3: Default Verbosity (0-4) + * Param 4: Proto Bitmap (uint8_t). Decimal Value. + * (decimal 62 = 0x3e) + * e.g., to disable live mode, use the following param in the ini file. + * gDptraceConfig = 0 + * e.g., to enable dptrace live mode and set the thresh as 6, + * use the following param in the ini file. + * gDptraceConfig = 1, 6 + * + * + */ +#ifdef CONFIG_DP_TRACE +#define CFG_DP_ENABLE_DP_TRACE \ + CFG_INI_BOOL("enable_dp_trace", \ + true, "Ctrl to enable dp trace feature") + +#define CFG_DP_DP_TRACE_CONFIG \ + CFG_INI_STRING( \ + "gDptraceConfig", \ + 1, \ + 20, \ + "1, 6, 2, 126", \ + "dp trace configuration string") + +/* + * + * dp_proto_event_bitmap - Control for which protocol packet diag event should + * be sent to user space. + * @Min: 0 + * @Max: 0x17 + * @Default: 0x6 + * + * This ini is used to control for which protocol packet diag event should be + * sent to user space. + * + * QDF_NBUF_PKT_TRAC_TYPE_DNS 0x01 + * QDF_NBUF_PKT_TRAC_TYPE_EAPOL 0x02 + * QDF_NBUF_PKT_TRAC_TYPE_DHCP 0x04 + * QDF_NBUF_PKT_TRAC_TYPE_ARP 0x10 + * + * Related: None + * + * Supported Feature: STA, SAP + * + * Usage: Internal + * + * + */ +#define CFG_DP_PROTO_EVENT_BITMAP \ + CFG_INI_UINT("dp_proto_event_bitmap", \ + 0, 0x17, 0x17, \ + CFG_VALUE_OR_DEFAULT, \ + "Control for which protocol type diag log should be sent") + +#define CFG_DP_CONFIG_DP_TRACE_ALL \ + CFG(CFG_DP_ENABLE_DP_TRACE) \ + CFG(CFG_DP_DP_TRACE_CONFIG) \ + CFG(CFG_DP_PROTO_EVENT_BITMAP) +#else +#define CFG_DP_CONFIG_DP_TRACE_ALL +#endif + +#ifdef WLAN_NUD_TRACKING +/* + * + * gEnableNUDTracking - Will enable or disable NUD tracking within driver + * @Min: 0 + * @Max: 3 + * @Default: 2 + * + * This ini is used to specify the behaviour of the driver for NUD tracking. + * If the ini value is:- + * 0: Driver will not track the NUD failures, and ignore the same. + * 1: Driver will track the NUD failures and if honoured will disconnect from + * the connected BSSID. + * 2: Driver will track the NUD failures and if honoured will roam away from + * the connected BSSID to a new BSSID to retain the data connectivity. + * 3: Driver will try to roam to a new AP but if roam fails, disconnect. + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_DP_ENABLE_NUD_TRACKING \ + CFG_INI_UINT("gEnableNUDTracking", \ + 0, \ + 3, \ + 2, \ + CFG_VALUE_OR_DEFAULT, "Driver NUD tracking behaviour") + +#define CFG_DP_ENABLE_NUD_TRACKING_ALL \ + CFG(CFG_DP_ENABLE_NUD_TRACKING) +#else +#define CFG_DP_ENABLE_NUD_TRACKING_ALL +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE + +#define CFG_DP_HL_BUNDLE_HIGH_TH \ + CFG_INI_UINT( \ + "tx_bundle_high_threashold", \ + 0, \ + 70000, \ + 4330, \ + CFG_VALUE_OR_DEFAULT, \ + "tx bundle high threashold") + +#define CFG_DP_HL_BUNDLE_LOW_TH \ + CFG_INI_UINT( \ + "tx_bundle_low_threashold", \ + 0, \ + 70000, \ + 4000, \ + CFG_VALUE_OR_DEFAULT, \ + "tx bundle low threashold") + +#define CFG_DP_HL_BUNDLE_TIMER_VALUE \ + CFG_INI_UINT( \ + "tx_bundle_timer_in_ms", \ + 10, \ + 10000, \ + 100, \ + CFG_VALUE_OR_DEFAULT, \ + "tx bundle timer value in ms") + +#define CFG_DP_HL_BUNDLE_SIZE \ + CFG_INI_UINT( \ + "tx_bundle_size", \ + 0, \ + 64, \ + 16, \ + CFG_VALUE_OR_DEFAULT, \ + "tx bundle size") + +#endif + +/* + * + * gWmiCreditCount - Credit count for WMI exchange + * @0: Not allowed + * @1: Serialize WMI commands, 1 command at a time + * @Default: 2: As advertized by FW + * + * This ini is used to serialize the WMI commandsif required. + * + * Related: None + * + * Usage: External + * + * + */ +#define WLAN_CFG_WMI_CREDIT_DEFAULT 0 +#define WLAN_CFG_WMI_CREDIT_MIN 1 +#define WLAN_CFG_WMI_CREDIT_MAX 2 + +#define CFG_DP_HTC_WMI_CREDIT_CNT \ + CFG_INI_UINT("gWmiCreditCount", \ + WLAN_CFG_WMI_CREDIT_MIN, \ + WLAN_CFG_WMI_CREDIT_MAX, \ + WLAN_CFG_WMI_CREDIT_DEFAULT, \ + CFG_VALUE_OR_DEFAULT, "WMI HTC CREDIT COUNT") + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +#define CFG_HDD_DP_LEGACY_TX_FLOW \ + CFG(CFG_DP_LL_TX_FLOW_LWM) \ + CFG(CFG_DP_LL_TX_FLOW_HWM_OFFSET) \ + CFG(CFG_DP_LL_TX_FLOW_MAX_Q_DEPTH) \ + CFG(CFG_DP_LL_TX_LBW_FLOW_LWM) \ + CFG(CFG_DP_LL_TX_LBW_FLOW_HWM_OFFSET) \ + CFG(CFG_DP_LL_TX_LBW_FLOW_MAX_Q_DEPTH) \ + CFG(CFG_DP_LL_TX_HBW_FLOW_LWM) \ + CFG(CFG_DP_LL_TX_HBW_FLOW_HWM_OFFSET) \ + CFG(CFG_DP_LL_TX_HBW_FLOW_MAX_Q_DEPTH) +#else +#define CFG_HDD_DP_LEGACY_TX_FLOW +#endif + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +#define CFG_HDD_DP_BUS_BANDWIDTH \ + CFG(CFG_DP_BUS_BANDWIDTH_VERY_HIGH_THRESHOLD) \ + CFG(CFG_DP_BUS_BANDWIDTH_HIGH_THRESHOLD) \ + CFG(CFG_DP_BUS_BANDWIDTH_MEDIUM_THRESHOLD) \ + CFG(CFG_DP_BUS_BANDWIDTH_LOW_THRESHOLD) \ + CFG(CFG_DP_BUS_BANDWIDTH_COMPUTE_INTERVAL) \ + CFG(CFG_DP_ENABLE_TCP_LIMIT_OUTPUT) \ + CFG(CFG_DP_ENABLE_TCP_ADV_WIN_SCALE) \ + CFG(CFG_DP_ENABLE_TCP_DELACK) \ + CFG(CFG_DP_TCP_DELACK_THRESHOLD_HIGH) \ + CFG(CFG_DP_TCP_DELACK_THRESHOLD_LOW) \ + CFG(CFG_DP_TCP_DELACK_TIMER_COUNT) \ + CFG(CFG_DP_TCP_TX_HIGH_TPUT_THRESHOLD) \ + CFG(CFG_DP_BUS_LOW_BW_CNT_THRESHOLD) \ + CFG(CFG_DP_BUS_HANDLE_LATENCY_CRITICAL_CLIENTS) + +#else +#define CFG_HDD_DP_BUS_BANDWIDTH +#endif + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK +#define CFG_DP_DRIVER_TCP_DELACK \ + CFG(CFG_DP_DRIVER_TCP_DELACK_HIGH_THRESHOLD) \ + CFG(CFG_DP_DRIVER_TCP_DELACK_LOW_THRESHOLD) \ + CFG(CFG_DP_DRIVER_TCP_DELACK_TIMER_VALUE) \ + CFG(CFG_DP_DRIVER_TCP_DELACK_PKT_CNT) \ + CFG(CFG_DP_DRIVER_TCP_DELACK_ENABLE) +#else +#define CFG_DP_DRIVER_TCP_DELACK +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE +#define CFG_DP_HL_BUNDLE \ + CFG(CFG_DP_HL_BUNDLE_HIGH_TH) \ + CFG(CFG_DP_HL_BUNDLE_LOW_TH) \ + CFG(CFG_DP_HL_BUNDLE_TIMER_VALUE) \ + CFG(CFG_DP_HL_BUNDLE_SIZE) +#else +#define CFG_DP_HL_BUNDLE +#endif + +#define CFG_HDD_DP_ALL \ + CFG(CFG_DP_NAPI_CE_CPU_MASK) \ + CFG(CFG_DP_RX_THREAD_CPU_MASK) \ + CFG(CFG_DP_RX_THREAD_UL_CPU_MASK) \ + CFG(CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST) \ + CFG(CFG_DP_TX_ORPHAN_ENABLE) \ + CFG(CFG_DP_RX_MODE) \ + CFG(CFG_DP_TX_COMP_LOOP_PKT_LIMIT)\ + CFG(CFG_DP_RX_REAP_LOOP_PKT_LIMIT)\ + CFG(CFG_DP_RX_HP_OOS_UPDATE_LIMIT)\ + CFG(CFG_DP_RX_SOFTIRQ_MAX_YIELD_TIME_NS)\ + CFG(CFG_DP_CE_SERVICE_MAX_RX_IND_FLUSH) \ + CFG(CFG_DP_CE_SERVICE_MAX_YIELD_TIME) \ + CFG(CFG_DP_ENABLE_TCP_PARAM_UPDATE) \ + CFG(CFG_DP_FILTER_MULTICAST_REPLAY) \ + CFG(CFG_DP_RX_WAKELOCK_TIMEOUT) \ + CFG(CFG_DP_NUM_DP_RX_THREADS) \ + CFG(CFG_DP_HTC_WMI_CREDIT_CNT) \ + CFG_DP_ENABLE_FASTPATH_ALL \ + CFG_HDD_DP_BUS_BANDWIDTH \ + CFG_DP_DRIVER_TCP_DELACK \ + CFG_HDD_DP_LEGACY_TX_FLOW \ + CFG_DP_ENABLE_NUD_TRACKING_ALL \ + CFG_DP_CONFIG_DP_TRACE_ALL \ + CFG_DP_HL_BUNDLE +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/hdd_sar_safety_config.h b/drivers/staging/qcacld-3.0/core/hdd/inc/hdd_sar_safety_config.h new file mode 100644 index 0000000000000000000000000000000000000000..2ee3511846aec07b99afc7bdb9034d0170593b83 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/hdd_sar_safety_config.h @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __HDD_SAR_SAFETY_CONFIG_H +#define __HDD_SAR_SAFETY_CONFIG_H + +#ifdef SAR_SAFETY_FEATURE + +/* + * + * gSarSafetyTimeout - Specify SAR safety timeout value in milliseconds + * + * @Min: 120000 + * @Max: 600000 + * Default: 300000 + * + * This ini is used to define SAR safety timeout value in milliseconds. + * This timer is started when the QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS + * is received first time. + * SAR safety timer will wait for the gSarSafetyTimeout for + * QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command and if + * SAR safety timer timeouts host will configure the gSarSafetyIndex + * to the FW. + * + * Usage: External + * + * + */ + +#define CFG_SAR_SAFETY_TIMEOUT CFG_INI_UINT( \ + "gSarSafetyTimeout", \ + 120000, \ + 600000, \ + 300000, \ + CFG_VALUE_OR_DEFAULT, \ + "Timeout value for SAR safety timer") + +/* + * + * gSarSafetyUnsolicitedTimeout - Specify SAR safety unsolicited timeout value + * in milliseconds + * + * @Min: 5000 + * @Max: 30000 + * Default: 15000 + * + * This ini is used to define SAR safety unsolicited timeout value in + * milliseconds. This timer is started on first data tx. + * SAR unsolicited timer will wait for the + * gSarSafetyUnsolicitedTimeout for QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS + * vendor command and if SAR unsolicited timer timeouts host will indicate + * user space with QCA_NL80211_VENDOR_SUBCMD_REQUEST_SAR_LIMITS_EVENT to send + * QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command. + * + * Usage: External + * + * + */ + +#define CFG_SAR_SAFETY_UNSOLICITED_TIMEOUT CFG_INI_UINT( \ + "gSarSafetyUnsolicitedTimeout", \ + 5000, \ + 30000, \ + 15000, \ + CFG_VALUE_OR_DEFAULT, \ + "Timeout value for SAR Unsolicited timer") + +/* + * + * gSarSafetyReqRespTimeout - Specify SAR safety request response timeout value + * in milliseconds + * + * @Min: 500 + * @Max: 3000 + * Default: 1000 + * + * This ini is used to define SAR request-response timeout value + * in milliseconds. SAR request-response timer will wait for the + * gSarSafetyReqRespTimeout for QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS + * vendor command and if SAR request-response timer timeouts host will + * indicate user space with QCA_NL80211_VENDOR_SUBCMD_REQUEST_SAR_LIMITS_EVENT + * for gSarSafetyReqRespRetry number of times to send + * QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command and still if host + * does not get QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command, host + * will configure the gSarSafetyIndex to the FW. + * + * Usage: External + * + * + */ + +#define CFG_SAR_SAFETY_REQ_RESP_TIMEOUT CFG_INI_UINT( \ + "gSarSafetyReqRespTimeout", \ + 500, \ + 1000, \ + 1000, \ + CFG_VALUE_OR_DEFAULT, \ + "Timeout value for SAR safety request response timer") + +/* + * + * gSarSafetyReqRespRetry - Specify SAR request response retries value + * + * @Min: 1 + * @Max: 10 + * Default: 5 + * + * This ini is used to define SAR request-response retries value. + * SAR request-response timer will wait for the gSarReqRespTimeout for + * QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command and if + * SAR request-response timer timeouts host will indicate user space + * for gSarSafetyReqRespRetry number of times to send + * QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command and still if + * host does not get QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor + * command, host will configure the gSarSafetyIndex to the FW. + * + * Usage: External + * + * + */ + +#define CFG_SAR_SAFETY_REQ_RESP_RETRIES CFG_INI_UINT( \ + "gSarSafetyReqRespRetry", \ + 1, \ + 5, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Max Number of SAR Request Response Retries") + +/* + * + * gSarSafetyIndex - Specify SAR safety index + * + * @Min: 0 + * @Max: 11 + * Default: 11 + * + * This ini is used to define SAR safety index, when sar safety timer + * timeouts or sar request response timer timeouts for gSarSafetyReqRespRetry + * number of times, host will configure gSarSafetyIndex value to the FW. + * + * Usage: External + * + * + */ + +#define CFG_SAR_SAFETY_INDEX CFG_INI_UINT( \ + "gSarSafetyIndex", \ + 0, \ + 11, \ + 11, \ + CFG_VALUE_OR_DEFAULT, \ + "SAR safety index value") +/* + * + * gSarSafetySleepIndex - Specify SAR Safety sleep index + * + * @Min: 0 + * @Max: 11 + * Default: 11 + * + * This ini is used to define SAR sleep index, when device goes into the + * sleep mode, before going into the sleep mode host configures + * gSarSafetySleepIndex value to the FW. + * + * Usage: External + * + * + */ + +#define CFG_SAR_SAFETY_SLEEP_INDEX CFG_INI_UINT( \ + "gSarSafetySleepIndex", \ + 0, \ + 11, \ + 11, \ + CFG_VALUE_OR_DEFAULT, \ + "SAR safety sleep index value") + +/* + * + * gEnableSarSafety - Enable/Disable SAR safety feature + * + * @Min: 0 + * @Max: 1 + * Default: 0 + * + * This ini is used to enable/disable SAR safety feature + * Value 1 of this ini enables SAR safety feature and + * value 0 of this ini disables SAR safety feature + * + * Usage: External + * + * + */ + +#define CFG_ENABLE_SAR_SAFETY_FEATURE CFG_INI_BOOL( \ + "gEnableSarSafety", \ + 0, \ + "Enable/Disable SAR safety feature") + +/* + * + * gConfigSarSafetySleepIndex - Enable/Disable SAR Safety sleep index + * + * @Min: 0 + * @Max: 1 + * Default: 0 + * + * This Configuration is to decide that before going to + * sleep mode whether to maintain high RF power + * (SAR disable) or to configure SAR sleep mode index + * + * Value 0 for this ini indicates to maintain high + * RF power (SAR disable) + * Value 1 for this ini indicates to configure SAR + * sleep mode index. + * + * Usage: External + * + * + */ + +#define CFG_CONFIG_SAR_SAFETY_SLEEP_MODE_INDEX CFG_INI_BOOL( \ + "gConfigSarSafetySleepIndex", \ + 0, \ + "Config SAR sleep Index") + +#define SAR_SAFETY_FEATURE_ALL \ + CFG(CFG_SAR_SAFETY_TIMEOUT) \ + CFG(CFG_SAR_SAFETY_UNSOLICITED_TIMEOUT) \ + CFG(CFG_SAR_SAFETY_REQ_RESP_TIMEOUT) \ + CFG(CFG_SAR_SAFETY_REQ_RESP_RETRIES) \ + CFG(CFG_SAR_SAFETY_INDEX) \ + CFG(CFG_SAR_SAFETY_SLEEP_INDEX) \ + CFG(CFG_ENABLE_SAR_SAFETY_FEATURE) \ + CFG(CFG_CONFIG_SAR_SAFETY_SLEEP_MODE_INDEX) \ + +#else +#define SAR_SAFETY_FEATURE_ALL +#endif + +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/qc_sap_ioctl.h b/drivers/staging/qcacld-3.0/core/hdd/inc/qc_sap_ioctl.h new file mode 100644 index 0000000000000000000000000000000000000000..7226536b5a837260b7ee4823ef2dec5f95619028 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/qc_sap_ioctl.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _QC_SAP_IOCTL_H_ +#define _QC_SAP_IOCTL_H_ + +/* + * QCSAP ioctls. + */ + +/* + * Channel List Info + */ + +struct channel_list_info { + uint8_t num_channels; + uint8_t channels[CFG_VALID_CHANNEL_LIST_LEN]; +}; + +#ifdef __linux__ +/* + * Wireless Extensions API, private ioctl interfaces. + * + * NB: Even-numbered ioctl numbers have set semantics and are privileged! + * (regardless of the incorrect comment in wireless.h!) + */ + +#define QCSAP_IOCTL_SETPARAM (SIOCIWFIRSTPRIV + 0) +#define QCSAP_IOCTL_GETPARAM (SIOCIWFIRSTPRIV + 1) +/* (SIOCIWFIRSTPRIV+2) is unused */ +#define QCSAP_IOCTL_SET_NONE_GET_THREE (SIOCIWFIRSTPRIV + 3) +#define WE_GET_TSF 1 +#define QCSAP_IOCTL_GET_STAWPAIE (SIOCIWFIRSTPRIV + 4) +#define QCSAP_IOCTL_STOPBSS (SIOCIWFIRSTPRIV + 6) +#define QCSAP_IOCTL_VERSION (SIOCIWFIRSTPRIV + 7) +/* (SIOCIWFIRSTPRIV + 8) is unused */ +#define QCSAP_IOCTL_GET_CHANNEL (SIOCIWFIRSTPRIV + 9) +#define QCSAP_IOCTL_ASSOC_STA_MACADDR (SIOCIWFIRSTPRIV + 10) +#define QCSAP_IOCTL_DISASSOC_STA (SIOCIWFIRSTPRIV + 11) +#define QCSAP_IOCTL_SET_PKTLOG (SIOCIWFIRSTPRIV + 12) + +/* Private ioctls and their sub-ioctls */ +#define QCSAP_PRIV_GET_CHAR_SET_NONE (SIOCIWFIRSTPRIV + 13) +#define QCSAP_GET_STATS 1 +#define QCSAP_LIST_FW_PROFILE 2 + +/* (SIOCIWFIRSTPRIV + 14) is unused */ + +#define QCSAP_IOCTL_PRIV_SET_THREE_INT_GET_NONE (SIOCIWFIRSTPRIV + 15) +#define WE_SET_WLAN_DBG 1 +#define WE_SET_DP_TRACE 2 +#define QCSAP_IOCTL_PRIV_SET_VAR_INT_GET_NONE (SIOCIWFIRSTPRIV + 16) +#define WE_UNIT_TEST_CMD 7 +/* + * + * ch_avoid - unit test SAP channel avoidance + * + * @INPUT: chan avoid ranges + * + * @OUTPUT: none + * + * This IOCTL is used to fake a channel avoidance event. + * To test SAP/GO chan switch during chan avoid event process. + * + * @E.g: iwpriv wlan0 ch_avoid 2452 2462 + * + * Supported Feature: SAP chan avoidance. + * + * Usage: Internal + * + * + */ +#define WE_SET_CHAN_AVOID 21 + +#define WE_SET_THERMAL_THROTTLE_CFG 27 + +#define WE_P2P_NOA_CMD 2 + +#define QCSAP_IOCTL_MODIFY_ACL (SIOCIWFIRSTPRIV + 18) +#define QCSAP_IOCTL_GET_CHANNEL_LIST (SIOCIWFIRSTPRIV + 19) +#define QCSAP_IOCTL_SET_TX_POWER (SIOCIWFIRSTPRIV + 20) +#define QCSAP_IOCTL_GET_STA_INFO (SIOCIWFIRSTPRIV + 21) +#define QCSAP_IOCTL_SET_MAX_TX_POWER (SIOCIWFIRSTPRIV + 22) +#define QCSAP_IOCTL_GET_INI_CFG (SIOCIWFIRSTPRIV + 25) + +#define QCSAP_IOCTL_SET_TWO_INT_GET_NONE (SIOCIWFIRSTPRIV + 28) +/* QCSAP_IOCTL_SET_TWO_INT_GET_NONE sub commands */ +#define QCSAP_IOCTL_SET_FW_CRASH_INJECT 1 +#define QCSAP_IOCTL_DUMP_DP_TRACE_LEVEL 2 +#define QCSAP_ENABLE_FW_PROFILE 3 +#define QCSAP_SET_FW_PROFILE_HIST_INTVL 4 +/* Private sub-ioctl for initiating WoW suspend without Apps suspend */ +#define QCSAP_SET_WLAN_SUSPEND 5 +#define QCSAP_SET_WLAN_RESUME 6 +#define QCSAP_SET_BA_AGEING_TIMEOUT 7 + +#define QCSAP_IOCTL_PRIV_GET_RSSI (SIOCIWFIRSTPRIV + 29) +#define QCSAP_IOCTL_PRIV_GET_SOFTAP_LINK_SPEED (SIOCIWFIRSTPRIV + 31) +#define QCSAP_IOCTL_GET_BA_AGEING_TIMEOUT (SIOCIWFIRSTPRIV + 32) + +#define MAX_VAR_ARGS 7 + +#define QCSAP_IOCTL_MAX_STR_LEN 1024 + +#define RC_2_RATE_IDX(_rc) ((_rc) & 0x7) +#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) + +#define RC_2_RATE_IDX_11AC(_rc) ((_rc) & 0xf) +#define HT_RC_2_STREAMS_11AC(_rc) ((((_rc) & 0x30) >> 4) + 1) + +#define RC_2_RATE_IDX_11AX(_rc) ((_rc) & 0x1f) +#define HT_RC_2_STREAMS_11AX(_rc) (((_rc) >> 5) & 0x7) + +/* + * + * setRadar - simulate a radar event + * + * @INPUT: None + * + * @OUTPUT: None + * + * This IOCTL is used to simulate a radar event, state machines for + * SAP will behave as same way in which a radar event is reported by WMA + * + * @E.g: iwpriv wlan0 setRadar + * + * Supported Feature: DFS + * + * Usage: Internal + * + * + */ + +/* + * + * setRadarDbg - enable/disable radar specific logs + * + * @INPUT: 1/0 + * + * @OUTPUT: None + * + * This IOCTL is enable radar phyerror info in wma + * + * @E.g: iwpriv wlan0 setRadarDbg + * iwpriv wlan0 setRadarDbg 1 + * + * Supported Feature: DFS + * + * Usage: Internal + * + * + */ +enum { + QCSAP_PARAM_MAX_ASSOC = 1, + QCSAP_PARAM_GET_WLAN_DBG, + QCSAP_PARAM_CLR_ACL = 4, + QCSAP_PARAM_ACL_MODE, + QCSAP_PARAM_HIDE_SSID, + QCSAP_PARAM_SET_MC_RATE, + QCSAP_PARAM_SET_TXRX_FW_STATS, + QCSAP_PARAM_SET_MCC_CHANNEL_LATENCY, + QCSAP_PARAM_SET_MCC_CHANNEL_QUOTA, + QCSAP_DBGLOG_LOG_LEVEL, + QCSAP_DBGLOG_VAP_ENABLE, + QCSAP_DBGLOG_VAP_DISABLE, + QCSAP_DBGLOG_MODULE_ENABLE, + QCSAP_DBGLOG_MODULE_DISABLE, + QCSAP_DBGLOG_MOD_LOG_LEVEL, + QCSAP_DBGLOG_TYPE, + QCSAP_DBGLOG_REPORT_ENABLE, + QCASAP_TXRX_FWSTATS_RESET, + QCSAP_PARAM_RTSCTS, + QCASAP_SET_11N_RATE, + QCASAP_SET_VHT_RATE, + QCASAP_SHORT_GI, + QCSAP_SET_AMPDU, + QCSAP_SET_AMSDU, + QCSAP_GTX_HT_MCS, + QCSAP_GTX_VHT_MCS, + QCSAP_GTX_USRCFG, + QCSAP_GTX_THRE, + QCSAP_GTX_MARGIN, + QCSAP_GTX_STEP, + QCSAP_GTX_MINTPC, + QCSAP_GTX_BWMASK, + QCASAP_SET_TM_LEVEL, + QCASAP_SET_DFS_IGNORE_CAC, + QCASAP_GET_DFS_NOL, + QCASAP_SET_DFS_NOL, + QCSAP_PARAM_SET_CHANNEL_CHANGE, + QCASAP_SET_DFS_TARGET_CHNL, + QCASAP_SET_RADAR_CMD, + QCSAP_GET_ACL, + QCASAP_TX_CHAINMASK_CMD, + QCASAP_RX_CHAINMASK_CMD, + QCASAP_NSS_CMD, + QCSAP_IPA_UC_STAT, + QCASAP_SET_PHYMODE, + QCASAP_GET_TEMP_CMD, + QCASAP_DUMP_STATS, + QCASAP_CLEAR_STATS, + QCASAP_SET_RADAR_DBG, + QCSAP_GET_FW_PROFILE_DATA, + QCSAP_START_FW_PROFILING, + QCSAP_CAP_TSF, + QCSAP_GET_TSF, + QCSAP_PARAM_CONC_SYSTEM_PREF, + QCASAP_PARAM_LDPC, + QCASAP_PARAM_TX_STBC, + QCASAP_PARAM_RX_STBC, + QCSAP_PARAM_CHAN_WIDTH, + QCSAP_PARAM_SET_TXRX_STATS, + QCASAP_SET_11AX_RATE, + QCASAP_SET_PEER_RATE, /* Not Supported */ + QCASAP_PARAM_DCM, + QCASAP_PARAM_RANGE_EXT, + QCSAP_SET_DEFAULT_AMPDU, + QCSAP_ENABLE_RTS_BURSTING, + QCASAP_SET_HE_BSS_COLOR, + QCSAP_SET_BTCOEX_MODE, + QCSAP_SET_BTCOEX_LOW_RSSI_THRESHOLD, +}; + +int iw_get_channel_list_with_cc(struct net_device *dev, + mac_handle_t mac_handle, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra); + +#endif /* __linux__ */ + +#endif /*_QC_SAP_IOCTL_H_*/ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_apf.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_apf.h new file mode 100644 index 0000000000000000000000000000000000000000..377552106e95da17ae1f71a897d096cef6e2c339 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_apf.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +/** + * DOC: wlan_hdd_apf.h + * + * Android Packet Filter related API's and definitions + */ + +#ifndef __WLAN_HDD_APF_H +#define __WLAN_HDD_APF_H + +#ifdef FEATURE_WLAN_APF + +#include +#include "sir_api.h" +#include "wlan_hdd_main.h" +#include "wmi_unified_param.h" + +#define APF_CONTEXT_MAGIC 0x4575354 + +#define MAX_APF_MEMORY_LEN 4096 + +/* APF commands wait times in msec */ +#define WLAN_WAIT_TIME_APF_READ_MEM 10000 + +/** + * wlan_hdd_cfg80211_apf_offload() - SSR Wrapper to APF Offload + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ + +int wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +/** + * hdd_apf_context_init - APF Context initialization operations + * @adapter: hdd adapter + * + * Return: None + */ +void hdd_apf_context_init(struct hdd_adapter *adapter); + +/** + * hdd_apf_context_destroy - APF Context de-init operations + * @adapter: hdd adapter + * + * Return: None + */ +void hdd_apf_context_destroy(struct hdd_adapter *adapter); + +/** + * hdd_get_apf_capabilities_cb() - Callback function to get APF capabilities + * @hdd_context: pointer to the hdd context + * @apf_get_offload: struct for get offload + * + * This function receives the response/data from the lower layer and + * checks to see if the thread is still waiting then post the results to + * upper layer, if the request has timed out then ignore. + * + * Return: None + */ +void hdd_get_apf_capabilities_cb(void *hdd_context, + struct sir_apf_get_offload *data); +#else /* FEATURE_WLAN_APF */ + +static inline void hdd_apf_context_init(struct hdd_adapter *adapter) +{ +} + +static inline void hdd_apf_context_destroy(struct hdd_adapter *adapter) +{ +} + +#endif /* FEATURE_WLAN_APF */ + +#endif /* WLAN_HDD_APF_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_assoc.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_assoc.h new file mode 100644 index 0000000000000000000000000000000000000000..e3e2c72cec123acf19ab3673084bfb0267cd881b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_assoc.h @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_ASSOC_H__) +#define WLAN_HDD_ASSOC_H__ + +/** + * DOC: wlan_hdd_assoc.h + * + */ + +/* Include files */ +#include +#include +#include "cdp_txrx_peer_ops.h" +#include +#include + +#define HDD_TIME_STRING_LEN 24 + +/* Timeout (in ms) for Link to Up before Registering Station */ +#define ASSOC_LINKUP_TIMEOUT 60 + +/* Timeout in ms for peer info request commpletion */ +#define IBSS_PEER_INFO_REQ_TIMOEUT 1000 + +#define INVALID_PEER_IDX -1 + +/** + * enum eConnectionState - connection state values at HDD + * @eConnectionState_NotConnected: Not associated in Infra or participating in + * in an IBSS / Ad-hoc network + * @eConnectionState_Connecting: While connection in progress + * @eConnectionState_Associated: Associated in an Infrastructure network + * @eConnectionState_IbssDisconnected: Participating in an IBSS network though + * disconnected (no partner stations in the IBSS) + * @eConnectionState_IbssConnected: Participating in an IBSS network with + * partner stations also present + * @eConnectionState_Disconnecting: Disconnecting in an Infrastructure network. + * @eConnectionState_NdiDisconnected: NDI in disconnected state - no peers + * @eConnectionState_NdiConnected: NDI in connected state - at least one peer + */ +typedef enum { + eConnectionState_NotConnected, + eConnectionState_Connecting, + eConnectionState_Associated, + eConnectionState_IbssDisconnected, + eConnectionState_IbssConnected, + eConnectionState_Disconnecting, + eConnectionState_NdiDisconnected, + eConnectionState_NdiConnected, +} eConnectionState; + +/** + * enum peer_status - Peer status + * @ePeerConnected: peer connected + * @ePeerDisconnected: peer disconnected + */ +enum peer_status { + ePeerConnected = 1, + ePeerDisconnected +}; + +/** + * struct hdd_conn_flag - connection flags + * @ht_present: ht element present or not + * @vht_present: vht element present or not + * @hs20_present: hs20 element present or not + * @ht_op_present: ht operation present or not + * @vht_op_present: vht operation present or not + */ +struct hdd_conn_flag { + uint8_t ht_present:1; + uint8_t vht_present:1; + uint8_t hs20_present:1; + uint8_t ht_op_present:1; + uint8_t vht_op_present:1; + uint8_t reserved:3; +}; + +/*defines for tx_BF_cap_info */ +#define TX_BF_CAP_INFO_TX_BF 0x00000001 +#define TX_BF_CAP_INFO_RX_STAG_RED_SOUNDING 0x00000002 +#define TX_BF_CAP_INFO_TX_STAG_RED_SOUNDING 0x00000004 +#define TX_BF_CAP_INFO_RX_ZFL 0x00000008 +#define TX_BF_CAP_INFO_TX_ZFL 0x00000010 +#define TX_BF_CAP_INFO_IMP_TX_BF 0x00000020 +#define TX_BF_CAP_INFO_CALIBRATION 0x000000c0 +#define TX_BF_CAP_INFO_CALIBRATION_SHIFT 6 +#define TX_BF_CAP_INFO_EXP_CSIT_BF 0x00000100 +#define TX_BF_CAP_INFO_EXP_UNCOMP_STEER_MAT 0x00000200 +#define TX_BF_CAP_INFO_EXP_BF_CSI_FB 0x00001c00 +#define TX_BF_CAP_INFO_EXP_BF_CSI_FB_SHIFT 10 +#define TX_BF_CAP_INFO_EXP_UNCMP_STEER_MAT 0x0000e000 +#define TX_BF_CAP_INFO_EXP_UNCMP_STEER_MAT_SHIFT 13 +#define TX_BF_CAP_INFO_EXP_CMP_STEER_MAT_FB 0x00070000 +#define TX_BF_CAP_INFO_EXP_CMP_STEER_MAT_FB_SHIFT 16 +#define TX_BF_CAP_INFO_CSI_NUM_BF_ANT 0x00180000 +#define TX_BF_CAP_INFO_CSI_NUM_BF_ANT_SHIFT 18 +#define TX_BF_CAP_INFO_UNCOMP_STEER_MAT_BF_ANT 0x00600000 +#define TX_BF_CAP_INFO_UNCOMP_STEER_MAT_BF_ANT_SHIFT 20 +#define TX_BF_CAP_INFO_COMP_STEER_MAT_BF_ANT 0x01800000 +#define TX_BF_CAP_INFO_COMP_STEER_MAT_BF_ANT_SHIFT 22 +#define TX_BF_CAP_INFO_RSVD 0xfe000000 + +/* defines for antenna selection info */ +#define ANTENNA_SEL_INFO 0x01 +#define ANTENNA_SEL_INFO_EXP_CSI_FB_TX 0x02 +#define ANTENNA_SEL_INFO_ANT_ID_FB_TX 0x04 +#define ANTENNA_SEL_INFO_EXP_CSI_FB 0x08 +#define ANTENNA_SEL_INFO_ANT_ID_FB 0x10 +#define ANTENNA_SEL_INFO_RX_AS 0x20 +#define ANTENNA_SEL_INFO_TX_SOUNDING_PPDU 0x40 +#define ANTENNA_SEL_INFO_RSVD 0x80 + +/** + * struct hdd_connection_info - structure to store connection information + * @conn_state: connection state of the NIC + * @bssid: BSSID + * @SSID: SSID Info + * @peer_macaddr:Peer Mac Address of the IBSS Stations + * @auth_type: Auth Type + * @uc_encrypt_type: Unicast Encryption Type + * @mc_encrypt_type: Multicast Encryption Type + * @is_authenticated: Remembers authenticated state + * @dot11mode: dot11mode + * @proxy_arp_service: proxy arp service + * @ptk_installed: ptk installed state + * @gtk_installed: gtk installed state + * @nss: number of spatial streams negotiated + * @rate_flags: rate flags for current connection + * @chan_freq: channel frequency + * @txrate: txrate structure holds nss & datarate info + * @rxrate: rx rate info + * @noise: holds noise information + * @ht_caps: holds ht capabilities info + * @vht_caps: holds vht capabilities info + * @hs20vendor_ie: holds passpoint/hs20 info + * @conn_flag: flag conn info params is present or not + * @roam_count: roaming counter + * @signal: holds rssi info + * @assoc_status_code: holds assoc fail reason + * @congestion: holds congestion percentage + * @last_ssid: holds last ssid + * @last_auth_type: holds last auth type + * @auth_time: last authentication established time + * @connect_time: last association established time + * @ch_width: channel width of operating channel + * @max_tx_bitrate: Max tx bitrate supported by the AP + * to which currently sta is connected. + */ +struct hdd_connection_info { + eConnectionState conn_state; + struct qdf_mac_addr bssid; + tCsrSSIDInfo ssid; + struct qdf_mac_addr peer_macaddr[MAX_PEERS]; + enum csr_akm_type auth_type; + eCsrEncryptionType uc_encrypt_type; + eCsrEncryptionType mc_encrypt_type; + uint8_t is_authenticated; + uint32_t dot11mode; + uint8_t proxy_arp_service; + bool ptk_installed; + bool gtk_installed; + uint8_t nss; + uint32_t rate_flags; + uint32_t chan_freq; + struct rate_info txrate; + struct rate_info rxrate; + int8_t noise; + struct ieee80211_ht_cap ht_caps; + struct ieee80211_vht_cap vht_caps; + struct hdd_conn_flag conn_flag; + tDot11fIEhs20vendor_ie hs20vendor_ie; + struct ieee80211_ht_operation ht_operation; + struct ieee80211_vht_operation vht_operation; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) \ + && defined(WLAN_FEATURE_11AX) + struct ieee80211_he_operation *he_operation; + uint32_t he_oper_len; +#endif + uint32_t roam_count; + int8_t signal; + int32_t assoc_status_code; + tCsrSSIDInfo last_ssid; + enum csr_akm_type last_auth_type; + char auth_time[HDD_TIME_STRING_LEN]; + char connect_time[HDD_TIME_STRING_LEN]; + enum phy_ch_width ch_width; + struct rate_info max_tx_bitrate; +}; + +/* Forward declarations */ +struct hdd_adapter; +struct hdd_station_ctx; +struct hdd_context; + +/** + * hdd_is_connecting() - Function to check connection progress + * @hdd_sta_ctx: pointer to global HDD Station context + * + * Return: true if connecting, false otherwise + */ +bool hdd_is_connecting(struct hdd_station_ctx *hdd_sta_ctx); + +/* + * hdd_is_fils_connection: API to determine if connection is FILS + * @adapter: hdd adapter + * + * Return: true if fils connection else false + */ +bool hdd_is_fils_connection(struct hdd_adapter *adapter); + +/** + * hdd_conn_set_connection_state() - set connection state + * @adapter: pointer to the adapter + * @conn_state: connection state + * + * This function updates the global HDD station context connection state. + * + * Return: none + */ +void hdd_conn_set_connection_state(struct hdd_adapter *adapter, + eConnectionState conn_state); + +/** + * hdd_conn_is_connected() - Function to check connection status + * @sta_ctx: pointer to global HDD Station context + * + * Return: false if any errors encountered, true otherwise + */ +bool hdd_conn_is_connected(struct hdd_station_ctx *sta_ctx); + +/** + * hdd_adapter_is_connected_sta() - check if @adapter is a connected station + * @adapter: the adapter to check + * + * Return: true if @adapter is a connected station + */ +bool hdd_adapter_is_connected_sta(struct hdd_adapter *adapter); + +/** + * hdd_conn_get_connected_band() - get current connection radio band + * @sta_ctx: pointer to global HDD Station context + * + * Return: BAND_2G or BAND_5G based on current AP connection + * BAND_ALL if not connected + */ +enum band_info hdd_conn_get_connected_band(struct hdd_station_ctx *sta_ctx); + +/** + * hdd_get_sta_connection_in_progress() - get STA for which connection + * is in progress + * @hdd_ctx: hdd context + * + * Return: hdd adpater for which connection is in progress + */ +struct hdd_adapter *hdd_get_sta_connection_in_progress( + struct hdd_context *hdd_ctx); + +/** + * hdd_abort_ongoing_sta_connection() - Disconnect the sta for which the + * connection is in progress. + * + * @hdd_ctx: hdd context + * + * Return: none + */ +void hdd_abort_ongoing_sta_connection(struct hdd_context *hdd_ctx); + +/** + * hdd_sme_roam_callback() - hdd sme roam callback + * @context: pointer to adapter context + * @roam_info: pointer to roam info + * @roam_id: roam id + * @roam_status: roam status + * @roam_result: roam result + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_sme_roam_callback(void *context, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result); + +/** + * hdd_set_genie_to_csr() - set genie to csr + * @adapter: pointer to adapter + * @rsn_auth_type: pointer to auth type + * + * Return: 0 on success, error number otherwise + */ +int hdd_set_genie_to_csr(struct hdd_adapter *adapter, + enum csr_akm_type *rsn_auth_type); + +/** + * hdd_set_csr_auth_type() - set csr auth type + * @adapter: pointer to adapter + * @rsn_auth_type: auth type + * + * Return: 0 on success, error number otherwise + */ +int hdd_set_csr_auth_type(struct hdd_adapter *adapter, + enum csr_akm_type rsn_auth_type); + +#ifdef FEATURE_WLAN_TDLS +/** + * hdd_roam_register_tdlssta() - register new TDLS station + * @adapter: pointer to adapter + * @peerMac: pointer to peer MAC address + * @qos: Quality of service + * + * Construct the txrx_desc and register the new STA with the Data Plane. + * This is called as part of ADD_STA in the TDLS setup. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_roam_register_tdlssta(struct hdd_adapter *adapter, + const uint8_t *peerMac, uint8_t qos); +#endif + +#ifdef FEATURE_WLAN_ESE +/** + * hdd_indicate_ese_bcn_report_no_results() - beacon report no scan results + * @adapter: pointer to adapter + * @measurementToken: measurement token + * @flag: flag + * @numBss: number of bss + * + * If the measurement is none and no scan results found, + * indicate the supplicant about measurement done. + * + * Return: none + */ +void +hdd_indicate_ese_bcn_report_no_results(const struct hdd_adapter *adapter, + const uint16_t measurementToken, + const bool flag, + const uint8_t numBss); +#endif /* FEATURE_WLAN_ESE */ + +/** + * hdd_change_peer_state() - change peer state + * @adapter: HDD adapter + * @peer_mac_addr: Peer MAC address + * @sta_state: peer state + * @roam_synch_in_progress: roam synch in progress + * + * Return: QDF status + */ +QDF_STATUS hdd_change_peer_state(struct hdd_adapter *adapter, + uint8_t *peer_mac_addr, + enum ol_txrx_peer_state sta_state, + bool roam_synch_in_progress); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +bool hdd_is_roam_sync_in_progress(struct csr_roam_info *roaminfo); +#else +static inline bool hdd_is_roam_sync_in_progress(struct csr_roam_info *roaminfo) +{ + return false; +} +#endif + +/** + * hdd_update_dp_vdev_flags() - update datapath vdev flags + * @cbk_data: callback data + * @vdev_id: virtual interface id + * @vdev_param: vdev parameter + * @is_link_up: link state up or down + * + * Return: QDF status + */ +QDF_STATUS hdd_update_dp_vdev_flags(void *cbk_data, + uint8_t vdev_id, + uint32_t vdev_param, + bool is_link_up); + +QDF_STATUS hdd_roam_register_sta(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + struct bss_description *bss_desc); + +/** + * hdd_save_peer() - Save peer MAC address in adapter peer table. + * @sta_ctx: pointer to hdd station context + * @peer_mac_addr: mac address of new peer + * + * This information is passed to iwconfig later. The peer that joined + * last is passed as information to iwconfig. + + * Return: true if success, false otherwise + */ +bool hdd_save_peer(struct hdd_station_ctx *sta_ctx, + struct qdf_mac_addr *peer_mac_addr); + +/** + * hdd_delete_peer() - removes peer from hdd station context peer table + * @sta_ctx: pointer to hdd station context + * @peer_mac_addr: mac address of peer to be deleted + * + * Return: None + */ +void hdd_delete_peer(struct hdd_station_ctx *sta_ctx, + struct qdf_mac_addr *peer_mac_addr); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS +hdd_wma_send_fastreassoc_cmd(struct hdd_adapter *adapter, + const tSirMacAddr bssid, uint32_t ch_freq); +/** + * hdd_save_gtk_params() - Save GTK offload params + * @adapter: HDD adapter + * @csr_roam_info: CSR roam info + * @is_reassoc: boolean to indicate roaming + * + * Return: None + */ +void hdd_save_gtk_params(struct hdd_adapter *adapter, + struct csr_roam_info *csr_roam_info, bool is_reassoc); +#else +static inline QDF_STATUS +hdd_wma_send_fastreassoc_cmd(struct hdd_adapter *adapter, + const tSirMacAddr bssid, uint32_t ch_freq) +{ + return QDF_STATUS_SUCCESS; +} +static inline void hdd_save_gtk_params(struct hdd_adapter *adapter, + struct csr_roam_info *csr_roam_info, + bool is_reassoc) +{ +} +#endif + +/** + * hdd_copy_ht_caps()- copy ht caps info from roam ht caps + * info to source ht_cap info of type ieee80211_ht_cap. + * @hdd_ht_cap: pointer to Source ht_cap info of type ieee80211_ht_cap + * @roam_ht_cap: pointer to roam ht_caps info + * + * Return: None + */ + +void hdd_copy_ht_caps(struct ieee80211_ht_cap *hdd_ht_cap, + tDot11fIEHTCaps *roam_ht_cap); + +/** + * hdd_add_beacon_filter() - add beacon filter + * @adapter: Pointer to the hdd adapter + * + * Return: 0 on success and errno on failure + */ +int hdd_add_beacon_filter(struct hdd_adapter *adapter); + +/** + * hdd_copy_vht_caps()- copy vht caps info from roam vht caps + * info to source vht_cap info of type ieee80211_vht_cap. + * @hdd_vht_cap: pointer to Source vht_cap info of type ieee80211_vht_cap + * @roam_vht_cap: pointer to roam vht_caps info + * + * Return: None + */ +void hdd_copy_vht_caps(struct ieee80211_vht_cap *hdd_vht_cap, + tDot11fIEVHTCaps *roam_vht_cap); + +/** + * hdd_roam_profile_init() - initialize adapter roam profile + * @adapter: The HDD adapter being initialized + * + * This function initializes the roam profile that is embedded within + * the adapter. + * + * Return: void + */ +void hdd_roam_profile_init(struct hdd_adapter *adapter); + +/** + * hdd_any_valid_peer_present() - Check if any valid peer is present + * @adapter: The HDD adapter + * + * Check if there is any peer present with non-zero mac address other than + * broadcast address. + * + * Return: True if there is any valid peer present + */ +bool hdd_any_valid_peer_present(struct hdd_adapter *adapter); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_bcn_recv.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_bcn_recv.h new file mode 100644 index 0000000000000000000000000000000000000000..b603ee1f04317128f5d4be863258faa2521e801d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_bcn_recv.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: feature_bcn_recv + * Feature for receiving beacons of connected AP and sending select + * params to upper layer via vendor event + */ + +#ifdef WLAN_BCN_RECV_FEATURE + +struct wireless_dev; +struct wiphy; + +/** + * wlan_hdd_cfg80211_bcn_rcv_op() - Process beacon report operations + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Wrapper function of __wlan_hdd_cfg80211_bcn_rcv_op() + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_bcn_rcv_op(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +/** + * hdd_beacon_recv_pause_indication()- Send vendor event to user space + * to inform SCAN started indication + * @hdd_handle: hdd handler + * @vdev_id: vdev id + * @type: scan event type + * @is_disconnected: Connection state of driver + * + * Return: None + */ +void hdd_beacon_recv_pause_indication(hdd_handle_t hdd_handle, + uint8_t vdev_id, + enum scan_event_type type, + bool is_disconnected); + +#define BCN_RECV_FEATURE_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_bcn_rcv_op\ +}, + +#define BCN_RECV_FEATURE_VENDOR_EVENTS \ +[QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING_INDEX] = { \ + .vendor_id = QCA_NL80211_VENDOR_ID, \ + .subcmd = QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING \ +}, +#else +#define BCN_RECV_FEATURE_VENDOR_COMMANDS +#define BCN_RECV_FEATURE_VENDOR_EVENTS + +static inline +void hdd_beacon_recv_pause_indication(hdd_handle_t hdd_handle, + uint8_t vdev_id, + enum scan_event_type type, + bool is_disconnected) +{ +} +#endif + diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_cfg.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..79edc72848ffa536cf6bd03c32d0289889cb0ab1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_cfg.h @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(HDD_CONFIG_H__) +#define HDD_CONFIG_H__ + +/** + * + * DOC: wlan_hdd_config.h + * + * WLAN Adapter Configuration functions + */ + +/* $HEADER$ */ + +/* Include files */ +#include +#include +#include +#include +#include +#include "osapi_linux.h" +#include +#include "wlan_pmo_hw_filter_public_struct.h" +#include "wlan_action_oui_public_struct.h" +#include "hdd_config.h" + +struct hdd_context; + +#define CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST_LEN 30 + +#define FW_MODULE_LOG_LEVEL_STRING_LENGTH (512) +#define TX_SCHED_WRR_PARAMS_NUM (5) + +/* Defines for all of the things we read from the configuration (registry). */ + +#ifdef CONFIG_DP_TRACE +/* Max length of gDptraceConfig string. e.g.- "1, 6, 1, 62" */ +#define DP_TRACE_CONFIG_STRING_LENGTH (20) + +/* At max 4 DP Trace config parameters are allowed. Refer - gDptraceConfig */ +#define DP_TRACE_CONFIG_NUM_PARAMS (4) + +/* + * Default value of live mode in case it cannot be determined from cfg string + * gDptraceConfig + */ +#define DP_TRACE_CONFIG_DEFAULT_LIVE_MODE (1) + +/* + * Default value of thresh (packets/second) beyond which DP Trace is disabled. + * Use this default in case the value cannot be determined from cfg string + * gDptraceConfig + */ +#define DP_TRACE_CONFIG_DEFAULT_THRESH (6) + +/* + * Number of intervals of BW timer to wait before enabling/disabling DP Trace. + * Since throughput threshold to disable live logging for DP Trace is very low, + * we calculate throughput based on # packets received in a second. + * For example assuming bandwidth timer interval is 100ms, and if more than 6 + * prints are received in 10 * 100 ms interval, we want to disable DP Trace + * live logging. DP_TRACE_CONFIG_DEFAULT_THRESH_TIME_LIMIT is the default + * value, to be used in case the real value cannot be derived from + * bw timer interval + */ +#define DP_TRACE_CONFIG_DEFAULT_THRESH_TIME_LIMIT (10) + +/* Default proto bitmap in case its missing in gDptraceConfig string */ +#define DP_TRACE_CONFIG_DEFAULT_BITMAP \ + (QDF_NBUF_PKT_TRAC_TYPE_EAPOL |\ + QDF_NBUF_PKT_TRAC_TYPE_DHCP |\ + QDF_NBUF_PKT_TRAC_TYPE_MGMT_ACTION |\ + QDF_NBUF_PKT_TRAC_TYPE_ARP |\ + QDF_NBUF_PKT_TRAC_TYPE_ICMP |\ + QDF_NBUF_PKT_TRAC_TYPE_ICMPv6)\ + +/* Default verbosity, in case its missing in gDptraceConfig string*/ +#define DP_TRACE_CONFIG_DEFAULT_VERBOSTY QDF_DP_TRACE_VERBOSITY_LOW + +#endif + +/* + * Type declarations + */ + +struct hdd_config { + /* Config parameters */ + enum hdd_dot11_mode dot11Mode; + +#ifdef FEATURE_WLAN_DYNAMIC_CVM + /* Bitmap for operating voltage corner mode */ + uint32_t vc_mode_cfg_bitmap; +#endif +#ifdef ENABLE_MTRACE_LOG + bool enable_mtrace; +#endif + bool advertise_concurrent_operation; +#ifdef DHCP_SERVER_OFFLOAD + struct dhcp_server dhcp_server_ip; +#endif /* DHCP_SERVER_OFFLOAD */ + bool apf_enabled; + uint16_t sap_tx_leakage_threshold; + bool sap_internal_restart; + bool tx_orphan_enable; + bool is_11k_offload_supported; + bool action_oui_enable; + uint8_t action_oui_str[ACTION_OUI_MAXIMUM_ID][ACTION_OUI_MAX_STR_LEN]; + bool is_unit_test_framework_enabled; + bool disable_channel; + + /* HDD converged ini items are listed below this*/ + bool bug_on_reinit_failure; + bool is_ramdump_enabled; + uint32_t iface_change_wait_time; + uint8_t multicast_host_fw_msgs; + enum hdd_wext_control private_wext_control; + bool enablefwprint; + uint8_t enable_fw_log; + +#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE + /* WLAN Logging */ + bool wlan_logging_enable; + bool wlan_logging_to_console; + uint8_t host_log_custom_nl_proto; +#endif /* WLAN_LOGGING_SOCK_SVC_ENABLE */ + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + uint32_t wlan_auto_shutdown; +#endif + +#ifndef REMOVE_PKT_LOG + bool enable_packet_log; +#endif + uint32_t rx_mode; + uint32_t tx_comp_loop_pkt_limit; + uint32_t rx_reap_loop_pkt_limit; + uint32_t rx_hp_oos_update_limit; + uint64_t rx_softirq_max_yield_duration_ns; +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH + /* bandwidth threshold for very high bandwidth */ + uint32_t bus_bw_very_high_threshold; + /* bandwidth threshold for high bandwidth */ + uint32_t bus_bw_high_threshold; + /* bandwidth threshold for medium bandwidth */ + uint32_t bus_bw_medium_threshold; + /* bandwidth threshold for low bandwidth */ + uint32_t bus_bw_low_threshold; + uint32_t bus_bw_compute_interval; + uint32_t enable_tcp_delack; + bool enable_tcp_limit_output; + uint32_t enable_tcp_adv_win_scale; + uint32_t tcp_delack_thres_high; + uint32_t tcp_delack_thres_low; + uint32_t tcp_tx_high_tput_thres; + uint32_t tcp_delack_timer_count; + bool enable_tcp_param_update; + uint32_t bus_low_cnt_threshold; + bool enable_latency_crit_clients; +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK + bool del_ack_enable; + uint32_t del_ack_threshold_high; + uint32_t del_ack_threshold_low; + uint16_t del_ack_timer_value; + uint16_t del_ack_pkt_count; +#endif + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + uint32_t tx_flow_low_watermark; + uint32_t tx_flow_hi_watermark_offset; + uint32_t tx_flow_max_queue_depth; + uint32_t tx_lbw_flow_low_watermark; + uint32_t tx_lbw_flow_hi_watermark_offset; + uint32_t tx_lbw_flow_max_queue_depth; + uint32_t tx_hbw_flow_low_watermark; + uint32_t tx_hbw_flow_hi_watermark_offset; + uint32_t tx_hbw_flow_max_queue_depth; +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + uint32_t napi_cpu_affinity_mask; + /* CPU affinity mask for rx_thread */ + uint32_t rx_thread_ul_affinity_mask; + uint32_t rx_thread_affinity_mask; + uint8_t cpu_map_list[CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST_LEN]; + bool multicast_replay_filter; + uint32_t rx_wakelock_timeout; + uint8_t num_dp_rx_threads; +#ifdef CONFIG_DP_TRACE + bool enable_dp_trace; + uint8_t dp_trace_config[DP_TRACE_CONFIG_STRING_LENGTH]; +#endif + uint8_t enable_nud_tracking; + uint32_t operating_chan_freq; + uint8_t num_vdevs; + uint8_t enable_concurrent_sta[CFG_CONCURRENT_IFACE_MAX_LEN]; + uint8_t dbs_scan_selection[CFG_DBS_SCAN_PARAM_LENGTH]; +#ifdef FEATURE_RUNTIME_PM + uint8_t runtime_pm; +#endif +#ifdef WLAN_FEATURE_WMI_SEND_RECV_QMI + bool is_qmi_stats_enabled; +#endif + uint8_t inform_bss_rssi_raw; + + bool mac_provision; + uint32_t provisioned_intf_pool; + uint32_t derived_intf_pool; + uint32_t cfg_wmi_credit_cnt; + uint32_t enable_sar_conversion; + bool is_wow_disabled; +#ifdef WLAN_FEATURE_TSF_PLUS + uint8_t tsf_ptp_options; +#endif /* WLAN_FEATURE_TSF_PLUS */ + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE + uint32_t pkt_bundle_threshold_high; + uint32_t pkt_bundle_threshold_low; + uint16_t pkt_bundle_timer_value; + uint16_t pkt_bundle_size; +#endif + uint32_t dp_proto_event_bitmap; + +#ifdef SAR_SAFETY_FEATURE + uint32_t sar_safety_timeout; + uint32_t sar_safety_unsolicited_timeout; + uint32_t sar_safety_req_resp_timeout; + uint32_t sar_safety_req_resp_retry; + uint32_t sar_safety_index; + uint32_t sar_safety_sleep_index; + bool enable_sar_safety; + bool config_sar_safety_sleep_index; +#endif + bool get_roam_chan_from_fw; + uint32_t fisa_enable; + +#ifdef WLAN_FEATURE_PERIODIC_STA_STATS + /* Periodicity of logging */ + uint32_t periodic_stats_timer_interval; + /* Duration for which periodic logging should be done */ + uint32_t periodic_stats_timer_duration; +#endif /* WLAN_FEATURE_PERIODIC_STA_STATS */ + uint8_t nb_commands_interval; +}; + +/** + * hdd_to_csr_wmm_mode() - Utility function to convert HDD to CSR WMM mode + * + * @uint8_t mode - hdd WMM user mode + * + * Return: CSR WMM mode + */ +eCsrRoamWmmUserModeType hdd_to_csr_wmm_mode(uint8_t mode); + +QDF_STATUS hdd_update_mac_config(struct hdd_context *hdd_ctx); +QDF_STATUS hdd_set_sme_config(struct hdd_context *hdd_ctx); +QDF_STATUS hdd_set_policy_mgr_user_cfg(struct hdd_context *hdd_ctx); +QDF_STATUS hdd_set_sme_chan_list(struct hdd_context *hdd_ctx); +bool hdd_update_config_cfg(struct hdd_context *hdd_ctx); +void hdd_cfg_get_global_config(struct hdd_context *hdd_ctx, char *buf, + int buflen); + +eCsrPhyMode hdd_cfg_xlate_to_csr_phy_mode(enum hdd_dot11_mode dot11Mode); + +QDF_STATUS hdd_set_idle_ps_config(struct hdd_context *hdd_ctx, bool val); +void hdd_get_pmkid_modes(struct hdd_context *hdd_ctx, + struct pmkid_mode_bits *pmkid_modes); + +int hdd_update_tgt_cfg(hdd_handle_t hdd_handle, struct wma_tgt_cfg *cfg); + +/** + * hdd_string_to_u8_array() - used to convert decimal string into u8 array + * @str: Decimal string + * @array: Array where converted value is stored + * @len: Length of the populated array + * @array_max_len: Maximum length of the array + * + * This API is called to convert decimal string (each byte separated by + * a comma) into an u8 array + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_string_to_u8_array(char *str, uint8_t *array, + uint8_t *len, uint16_t array_max_len); + +QDF_STATUS hdd_hex_string_to_u16_array(char *str, uint16_t *int_array, + uint8_t *len, uint8_t int_array_max_len); + +void hdd_cfg_print_global_config(struct hdd_context *hdd_ctx); + +QDF_STATUS hdd_update_nss(struct hdd_adapter *adapter, uint8_t tx_nss, + uint8_t rx_nss); + +/** + * hdd_get_tx_nss() - Get the number of spatial streams supported by the + * adapter + * + * @adapter: the pointer to adapter + * @tx_nss: the number Tx of spatial streams supported by the adapter + * + * This function is used to get the number of Tx spatial streams supported by + * the adapter. + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_get_tx_nss(struct hdd_adapter *adapter, uint8_t *tx_nss); + +/** + * hdd_get_rx_nss() - Get the number of spatial streams supported by the + * adapter + * + * @adapter: the pointer to adapter + * @rx_nss: the number Rx of spatial streams supported by the adapter + * + * This function is used to get the number of Rx spatial streams supported by + * the adapter. + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_get_rx_nss(struct hdd_adapter *adapter, uint8_t *rx_nss); + +/** + * hdd_dfs_indicate_radar() - Block tx as radar found on the channel + * @hdd_ctxt: HDD context pointer + * + * This function is invoked in atomic context when a radar + * is found on the SAP current operating channel and Data Tx + * from netif has to be stopped to honor the DFS regulations. + * Actions: Stop the netif Tx queues,Indicate Radar present + * in HDD context for future usage. + * + * Return: true on success, else false + */ +bool hdd_dfs_indicate_radar(struct hdd_context *hdd_ctx); + +/** + * hdd_override_all_ps() - overrides to disables all the powersave features. + * @hdd_ctx: Pointer to HDD context. + * Overrides below powersave ini configurations. + * gEnableImps=0 + * gEnableBmps=0 + * gRuntimePM=0 + * gWlanAutoShutdown = 0 + * gEnableSuspend=0 + * gEnableWoW=0 + * + * Return: None + */ +void hdd_override_all_ps(struct hdd_context *hdd_ctx); +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_cfr.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_cfr.h new file mode 100644 index 0000000000000000000000000000000000000000..223b7f11799840ece706a4277cd2fb32107d41a0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_cfr.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_cfr.h + * + * WLAN Host Device Driver cfr capture implementation + * + */ + +#if !defined(_WLAN_HDD_CFR_H) +#define _WLAN_HDD_CFR_H + +#ifdef WLAN_CFR_ENABLE + +#define HDD_INVALID_GROUP_ID 16 +#define ENHANCED_CFR_VERSION 2 + +/** + * wlan_hdd_cfg80211_peer_cfr_capture_cfg() - configure peer cfr capture + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function starts CFR capture + * + * Return: 0 on success and errno on failure + */ +int +wlan_hdd_cfg80211_peer_cfr_capture_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_CFR_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_peer_cfr_capture_cfg \ +}, +#else +#define FEATURE_CFR_VENDOR_COMMANDS +#endif /* WLAN_CFR_ENABLE */ +#endif /* _WLAN_HDD_CFR_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_conc_ut.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_conc_ut.h new file mode 100644 index 0000000000000000000000000000000000000000..c1cc042da67fb2ef6b7eced595caaaaf1902fe22 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_conc_ut.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_CONC_UT_H +#define __WLAN_HDD_CONC_UT_H + +/* Include files */ + +#include "wlan_hdd_main.h" +#include "wlan_policy_mgr_api.h" +#ifdef MPC_UT_FRAMEWORK +void clean_report(struct hdd_context *hdd_ctx); +void fill_report(struct hdd_context *hdd_ctx, char *title, + uint32_t first_persona, uint32_t second_persona, uint32_t third_persona, + uint32_t chnl_1st_conn, uint32_t chnl_2nd_conn, uint32_t chnl_3rd_conn, + bool status, enum policy_mgr_pcl_type pcl_type, char *reason, + uint8_t *pcl); +void print_report(struct hdd_context *hdd_ctx); +void wlan_hdd_one_connection_scenario(struct hdd_context *hdd_ctx); +void wlan_hdd_two_connections_scenario(struct hdd_context *hdd_ctx, + uint8_t first_chnl, enum policy_mgr_chain_mode first_chain_mask); +void wlan_hdd_three_connections_scenario(struct hdd_context *hdd_ctx, + uint8_t first_chnl, uint8_t second_chnl, + enum policy_mgr_chain_mode chain_mask, uint8_t use_same_mac); +#else +static inline +void clean_report(struct hdd_context *hdd_ctx) +{ +} + +static inline +void fill_report(struct hdd_context *hdd_ctx, char *title, + uint32_t first_persona, uint32_t second_persona, uint32_t third_persona, + uint32_t chnl_1st_conn, uint32_t chnl_2nd_conn, uint32_t chnl_3rd_conn, + bool status, enum policy_mgr_pcl_type pcl_type, char *reason, + uint8_t *pcl) +{ +} + +static inline +void print_report(struct hdd_context *hdd_ctx) +{ +} + +static inline +void wlan_hdd_one_connection_scenario(struct hdd_context *hdd_ctx) +{ +} + +static inline +void wlan_hdd_two_connections_scenario(struct hdd_context *hdd_ctx, + uint8_t first_chnl, enum policy_mgr_chain_mode first_chain_mask) +{ +} + +static inline +void wlan_hdd_three_connections_scenario(struct hdd_context *hdd_ctx, + uint8_t first_chnl, uint8_t second_chnl, + enum policy_mgr_chain_mode chain_mask, uint8_t use_same_mac) +{ +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_data_stall_detection.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_data_stall_detection.h new file mode 100644 index 0000000000000000000000000000000000000000..4104ba58cc735710144e4dc2d7d3c014d821bf2b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_data_stall_detection.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_DATA_STALL_DETECTION_H +#define __WLAN_HDD_DATA_STALL_DETECTION_H + +/** + * DOC: wlan_hdd_data_stall_detection.h + * + * WLAN Host Device Driver data stall detection API specification + */ + +/** + * hdd_register_data_stall_detect_cb() - register data stall callback + * + * Return: 0 for success or Error code for failure + */ +int hdd_register_data_stall_detect_cb(void); + +/** + * hdd_deregister_data_stall_detect_cb() - de-register data stall callback + * + * Return: 0 for success or Error code for failure + */ +int hdd_deregister_data_stall_detect_cb(void); +#endif /* __WLAN_HDD_DATA_STALL_DETECTION_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs.h new file mode 100644 index 0000000000000000000000000000000000000000..daa4795e581fa9c875795f95d97b32f42cc47172 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_HDD_DEBUGFS_H +#define _WLAN_HDD_DEBUGFS_H + +#ifdef WLAN_DEBUGFS + +#define HDD_DEBUGFS_FILE_NAME_MAX 24 + +/** + * enum hdd_debugfs_file_id - Debugfs file Identifier + * @HDD_DEBUFS_FILE_ID_CONNECT_INFO: connect_info file id + * @HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO: roam_scan_stats file id + * @HDD_DEBUFS_FILE_ID_OFFLOAD_INFO: offload_info file id + * @HDD_DEBUGFS_FILE_ID_MAX: maximum id of csr debugfs file + */ +enum hdd_debugfs_file_id { + HDD_DEBUFS_FILE_ID_CONNECT_INFO = 0, + HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO = 1, + HDD_DEBUFS_FILE_ID_OFFLOAD_INFO = 2, + + HDD_DEBUGFS_FILE_ID_MAX, +}; + +/** + * struct hdd_debugfs_file_info - Debugfs file info + * @name: name of debugfs file + * @id: id from enum hdd_debugfs_file_id used to identify file + * @buf_max_size: max size of buffer from which debugfs file is updated + * @entry: dentry pointer to debugfs file + */ +struct hdd_debugfs_file_info { + uint8_t name[HDD_DEBUGFS_FILE_NAME_MAX]; + enum hdd_debugfs_file_id id; + ssize_t buf_max_size; + struct dentry *entry; +}; + +QDF_STATUS hdd_debugfs_init(struct hdd_adapter *adapter); +void hdd_debugfs_exit(struct hdd_adapter *adapter); + +/** + * hdd_wait_for_debugfs_threads_completion() - Wait for debugfs threads + * completion before proceeding further to stop modules + * + * Return: true if there is no debugfs open + * false if there is at least one debugfs open + */ +bool hdd_wait_for_debugfs_threads_completion(void); + +/** + * hdd_return_debugfs_threads_count() - Return active debugfs threads + * + * Return: total number of active debugfs threads in driver + */ +int hdd_return_debugfs_threads_count(void); + +/** + * hdd_debugfs_thread_increment() - Increment debugfs thread count + * + * This function is used to increment and keep track of debugfs thread count. + * This is invoked for every file open operation. + * + * Return: None + */ +void hdd_debugfs_thread_increment(void); + +/** + * hdd_debugfs_thread_decrement() - Decrement debugfs thread count + * + * This function is used to decrement and keep track of debugfs thread count. + * This is invoked for every file release operation. + * + * Return: None + */ +void hdd_debugfs_thread_decrement(void); + +#else +static inline QDF_STATUS hdd_debugfs_init(struct hdd_adapter *adapter) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void hdd_debugfs_exit(struct hdd_adapter *adapter) +{ +} + +/** + * hdd_wait_for_debugfs_threads_completion() - Wait for debugfs threads + * completion before proceeding further to stop modules + * + * Return: true if there is no debugfs open + * false if there is at least one debugfs open + */ +static inline +bool hdd_wait_for_debugfs_threads_completion(void) +{ + return true; +} + +/** + * hdd_return_debugfs_threads_count() - Return active debugfs threads + * + * Return: total number of active debugfs threads in driver + */ +static inline +int hdd_return_debugfs_threads_count(void) +{ + return 0; +} + +/** + * hdd_debugfs_thread_increment() - Increment debugfs thread count + * + * This function is used to increment and keep track of debugfs thread count. + * This is invoked for every file open operation. + * + * Return: None + */ +static inline +void hdd_debugfs_thread_increment(void) +{ +} + +/** + * hdd_debugfs_thread_decrement() - Decrement debugfs thread count + * + * This function is used to decrement and keep track of debugfs thread count. + * This is invoked for every file release operation. + * + * Return: None + */ +static inline +void hdd_debugfs_thread_decrement(void) +{ +} + +#endif /* #ifdef WLAN_DEBUGFS */ +#endif /* #ifndef _WLAN_HDD_DEBUGFS_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_coex.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_coex.h new file mode 100644 index 0000000000000000000000000000000000000000..5a6bfed8829dde75ba9138a7de0128e8dcff7320 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_coex.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_HDD_DEBUGFS_COEX_H +#define _WLAN_HDD_DEBUGFS_COEX_H + +#ifdef WLAN_MWS_INFO_DEBUGFS +/** + * hdd_debugfs_mws_coex_info_init() - MWS coex initialization + * @hdd_ctx: Pointer to the hdd_ctx + * + * This function is called to initialize the coex debugfs. + * Return: None + */ +void hdd_debugfs_mws_coex_info_init(struct hdd_context *hdd_ctx); + +/** + * hdd_debugfs_mws_coex_info_deinit() - MWS coex deintialization + * @hdd_ctx: Pointer to the hdd_ctx + * + * This function is called to deinitialize the coex debugfs. + * Return: None + */ +void hdd_debugfs_mws_coex_info_deinit(struct hdd_context *hdd_ctx); +#else +static inline void hdd_debugfs_mws_coex_info_init(struct hdd_context *hdd_ctx) +{ +} + +static inline void hdd_debugfs_mws_coex_info_deinit(struct hdd_context *hdd_ctx) +{ +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_config.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_config.h new file mode 100644 index 0000000000000000000000000000000000000000..d0fa1ab4d1a2ad3a7e507968723f900bea92866d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_config.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_config.h + * + * WLAN Host Device Driver implementation to update + * debugfs with ini configs + */ + +#ifndef _WLAN_HDD_DEBUGFS_CONFIG_H +#define _WLAN_HDD_DEBUGFS_CONFIG_H + +#ifdef WLAN_DEBUGFS +/** + * hdd_debugfs_ini_config_init() - API to initialize ini config file + * @hdd_ctx: hdd context + * + * Return: 0 on success and errno on failure + */ +int hdd_debugfs_ini_config_init(struct hdd_context *hdd_ctx); + +/** + * hdd_debugfs_ini_config_deinit() - API to deinit ini config file + * @hdd_ctx: hdd context + * + * Return: None + */ +void hdd_debugfs_ini_config_deinit(struct hdd_context *hdd_ctx); +#else +static inline int hdd_debugfs_ini_config_init(struct hdd_context *hdd_ctx) +{ + return 0; +} + +static inline void hdd_debugfs_ini_config_deinit(struct hdd_context *hdd_ctx) +{ +} +#endif /* WLAN_DEBUGFS */ +#endif /* _WLAN_HDD_DEBUGFS_CONFIG_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_csr.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_csr.h new file mode 100644 index 0000000000000000000000000000000000000000..8d4b36b0e6008932c380baed6bd42fcbda059e30 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_csr.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_csr.h + * + * WLAN Host Device Driver implementation to update + * debugfs with connect, scan and roam information + */ + +#ifndef _WLAN_HDD_DEBUGFS_CSR_H +#define _WLAN_HDD_DEBUGFS_CSR_H + +#include + +#ifdef WLAN_DEBUGFS + +#define DEBUGFS_CONNECT_INFO_BUF_SIZE (4 * 1024) +#define DEBUGFS_OFFLOAD_INFO_BUF_SIZE (4 * 1024) +#define DEBUGFS_ROAM_SCAN_STATS_INFO_BUF_SIZE (4 * 1024) + +/** + * struct wlan_hdd_debugfs_buffer_info - Debugfs buffer info + * @length: current length of the debugfs buffer + * @max_buf_len: maximum buffer length of the debugfs buffer + * @id: id from enum hdd_debugfs_file_id used to identify file + * @data: start of debugfs buffer from which file read starts + * @adapter: pointer to adapter + * + * This structure is used to hold the debugfs buffer details and is stored in + * private data of file argument in file open operation. + */ +struct wlan_hdd_debugfs_buffer_info { + ssize_t length; + ssize_t max_buf_len; + enum hdd_debugfs_file_id id; + uint8_t *data; + struct hdd_adapter *adapter; +}; + +/** + * struct hdd_roam_scan_stats_debugfs_priv - private data for request mgr + * @res: pointer to roam scan stats response + */ +struct hdd_roam_scan_stats_debugfs_priv { + struct wmi_roam_scan_stats_res *roam_scan_stats_res; +}; + +/** + * wlan_hdd_debugfs_csr_init() - Create wifi diagnostic debugfs files + * @adapter: pointer to adapter for which debugfs files are to be created + * + * Return: None + */ +void wlan_hdd_debugfs_csr_init(struct hdd_adapter *adapter); + +/** + * wlan_hdd_debugfs_csr_deinit() - Remove wifi diagnostic debugfs files + * @adapter: pointer to adapter for which debugfs files are to be removed + * + * Return: None + */ +void wlan_hdd_debugfs_csr_deinit(struct hdd_adapter *adapter); + +/** + * wlan_hdd_current_time_info_debugfs() - API to get time into user buffer + * @buf: output buffer to hold current time when queried + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +ssize_t +wlan_hdd_current_time_info_debugfs(uint8_t *buf, ssize_t buf_avail_len); + +/** + * wlan_hdd_debugfs_update_connect_info() - API to get connect info + * into user buffer + * @buf: output buffer to hold connect info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +ssize_t +wlan_hdd_debugfs_update_connect_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len); + +/** + * wlan_hdd_debugfs_update_filters_info() - API to get offload info + * into user buffer + * @buf: output buffer to hold offload info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +ssize_t +wlan_hdd_debugfs_update_filters_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len); + +/** + * wlan_hdd_debugfs_update_roam_stats() - API to get roam scan stats info + * into user buffer + * @buf: output buffer to hold roam scan stats info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +ssize_t +wlan_hdd_debugfs_update_roam_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len); + +#else +/** + * wlan_hdd_debugfs_csr_init() - Create wifi diagnostic debugfs files + * @adapter: pointer to adapter for which debugfs files are to be created + * + * Return: None + */ +static inline void wlan_hdd_debugfs_csr_init(struct hdd_adapter *adapter) +{ +} + +/** + * wlan_hdd_debugfs_csr_deinit() - Remove wifi diagnostic debugfs files + * @adapter: pointer to adapter for which debugfs files are to be removed + * + * Return: None + */ +static inline void wlan_hdd_debugfs_csr_deinit(struct hdd_adapter *adapter) +{ +} + +/** + * wlan_hdd_current_time_info_debugfs() - API to get time into user buffer + * @buf: output buffer to hold current time when queried + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +static inline ssize_t +wlan_hdd_current_time_info_debugfs(uint8_t *buf, ssize_t buf_avail_len) +{ + return 0; +} + +/** + * wlan_hdd_debugfs_update_connect_info() - API to get connect info + * into user buffer + * @buf: output buffer to hold connect info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +static inline ssize_t +wlan_hdd_debugfs_update_connect_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + return 0; +} + +/** + * wlan_hdd_debugfs_update_filters_info() - API to get offload info + * into user buffer + * @buf: output buffer to hold offload info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +static inline ssize_t +wlan_hdd_debugfs_update_filters_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + return 0; +} + +/** + * wlan_hdd_debugfs_update_roam_stats() - API to get roam scan stats info + * into user buffer + * @buf: output buffer to hold roam scan stats info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes copied + */ +static inline ssize_t +wlan_hdd_debugfs_update_roam_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + return 0; +} + +#endif + +#endif /* _WLAN_HDD_DEBUGFS_CSR_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_llstat.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_llstat.h new file mode 100644 index 0000000000000000000000000000000000000000..8b7b04eb2eace5f12e6a1c18de2257e2e6afb599 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_llstat.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_llstat.h + * + * WLAN Host Device Driver implementation to update + * debugfs with Link Layer statistics + */ + +#ifndef _WLAN_HDD_DEBUGFS_LLSTAT_H +#define _WLAN_HDD_DEBUGFS_LLSTAT_H + +#define DEBUGFS_LLSTATS_BUF_SIZE 12288 +#define DEBUGFS_LLSTATS_REQID 4294967295UL +#define DEBUGFS_LLSTATS_REQMASK 0x7 + +#include + +#if defined(WLAN_FEATURE_LINK_LAYER_STATS) && defined(WLAN_DEBUGFS) +/** + * hdd_debugfs_process_peer_stats() - Parse Peer stats and add it to buffer + * @adapter: Pointer to device adapter + * @data: Pointer to stats data + * + * Receiving Link Layer peer statistics from FW. This function stores the + * firmware data in a buffer to be written into debugfs. + * + * Return: None + */ +void hdd_debugfs_process_peer_stats(struct hdd_adapter *adapter, void *data); + +/** + * hdd_debugfs_process_radio_stats() - Parse Radio stats and add it to buffer + * @adapter: Pointer to device adapter + * @more_data: More data + * @data: Pointer to stats data + * @num_radio: Number of radios + * + * Receiving Link Layer Radio statistics from FW. This function stores the + * firmware data in a buffer to be written into debugfs. + * + * Return: None + */ +void hdd_debugfs_process_radio_stats(struct hdd_adapter *adapter, + uint32_t more_data, void *data, uint32_t num_radio); + +/** + * hdd_link_layer_process_iface_stats() - This function is called after + * @adapter: Pointer to device adapter + * @data: Pointer to stats data + * @num_peers: Number of peers + * + * Receiving Link Layer Interface statistics from FW.This function converts + * the firmware data to the NL data and sends the same to the kernel/upper + * layers. + * + * Return: None + */ +void hdd_debugfs_process_iface_stats(struct hdd_adapter *adapter, + void *data, uint32_t num_peers); + +/** + * wlan_hdd_create_ll_stats_file() - API to create Link Layer stats file + * @adapter: interface adapter pointer + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_create_ll_stats_file(struct hdd_adapter *adapter); +#else +static inline void hdd_debugfs_process_peer_stats(struct hdd_adapter *adapter, + void *data) +{ +} + +static inline void hdd_debugfs_process_radio_stats( + struct hdd_adapter *adapter, + uint32_t more_data, void *data, uint32_t num_radio) +{ +} + +static inline void hdd_debugfs_process_iface_stats( + struct hdd_adapter *adapter, + void *data, uint32_t num_peers) +{ +} +static inline int wlan_hdd_create_ll_stats_file(struct hdd_adapter *adapter) +{ + return 0; +} +#endif +#endif /* #ifndef _WLAN_HDD_DEBUGFS_LLSTAT_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_mibstat.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_mibstat.h new file mode 100644 index 0000000000000000000000000000000000000000..e42236cd3b75f9a555304a5b819d7e67e10af9df --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_debugfs_mibstat.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_mibstat.h + * + * WLAN Host Device Driver implementation to update + * debugfs with MIB statistics + */ + +#ifndef _WLAN_HDD_DEBUGFS_MIBSTAT_H +#define _WLAN_HDD_DEBUGFS_MIBSTAT_H + +#define DEBUGFS_MIBSTATS_BUF_SIZE 4096 + +#include + +#if defined(WLAN_FEATURE_MIB_STATS) && defined(WLAN_DEBUGFS) +/** + * hdd_debugfs_process_mib_stats() - Process mib stats from fw + * + * This function is used to store mib stats to global variable mib_stats. + * + * Return: None + */ +void hdd_debugfs_process_mib_stats(struct hdd_adapter *adapter, + struct stats_event *stats); + +/** + * wlan_hdd_create_mib_stats_file() - API to create MIB stats file + * @adapter: interface adapter pointer + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_create_mib_stats_file(struct hdd_adapter *adapter); + +/** + * wlan_hdd_destroy_mib_stats_lock() - API to destroy MIB stats lock + * + * Return: No return + */ +void wlan_hdd_destroy_mib_stats_lock(void); +#else +static inline int wlan_hdd_create_mib_stats_file(struct hdd_adapter *adapter) +{ + return 0; +} + +static inline void wlan_hdd_destroy_mib_stats_lock(void) +{ +} +#endif +#endif /* #ifndef _WLAN_HDD_DEBUGFS_MIBSTAT_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_driver_ops.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_driver_ops.h new file mode 100644 index 0000000000000000000000000000000000000000..976f67c55d4a40769a4c966cde350f851e45ec43 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_driver_ops.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2015-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_DRIVER_OPS_H__ +#define __WLAN_HDD_DRIVER_OPS_H__ + +#include "hif.h" + +struct hdd_context; + +/** + * DOC: wlan_hdd_driver_ops.h + * + * Functions to register the wlan driver. + */ + +/** + * wlan_hdd_register_driver() - Register with platform layer + * + * This function is used to register HDD callbacks with the platform + * layer. + * + * Return: 0 if registration is successful, negative errno if + * registration fails + */ +int wlan_hdd_register_driver(void); + +/** + * wlan_hdd_unregister_driver() - Unregister from platform layer + * + * This function is used to unregister HDD callbacks from the platform + * layer. + * + * Return: void + */ + +void wlan_hdd_unregister_driver(void); + +/** + * wlan_hdd_bus_suspend() - suspend the wlan bus + * + * This function is called by the platform driver to suspend the + * wlan bus + * + * Return: 0 on success, negative errno on error + */ +int wlan_hdd_bus_suspend(void); + +/** + * wlan_hdd_bus_suspend_noirq() - handle .suspend_noirq callback + * + * This function is called by the platform driver to complete the + * bus suspend callback when device interrupts are disabled by kernel. + * Call HIF and WMA suspend_noirq callbacks to make sure there is no + * wake up pending from FW before allowing suspend. + * + * Return: 0 for success and -EBUSY if FW is requesting wake up + */ +int wlan_hdd_bus_suspend_noirq(void); + +/** + * wlan_hdd_bus_resume() - wake up the bus + * + * This function is called by the platform driver to resume wlan + * bus + * + * Return: 0 for success and negative errno if failure + */ +int wlan_hdd_bus_resume(void); + +/** + * wlan_hdd_bus_resume_noirq() - handle bus resume no irq + * + * This function is called by the platform driver to do bus + * resume no IRQ before calling resume callback. Call WMA and HIF + * layers to complete the resume_noirq. + * + * Return: 0 for success and negative error code for failure + */ +int wlan_hdd_bus_resume_noirq(void); + +/** + * hdd_hif_close() - HIF close helper + * @hdd_ctx: HDD context + * @hif_ctx: HIF context + * + * Helper function to close HIF + */ +void hdd_hif_close(struct hdd_context *hdd_ctx, void *hif_ctx); + +/** + * hdd_hif_open() - HIF open helper + * @dev: wlan device structure + * @bdev: bus device structure + * @bid: bus identifier for shared busses + * @bus_type: underlying bus type + * @reinit: true if we are reinitializing the driver during recovery phase + * + * This function brings-up HIF layer during load/recovery phase. + * + * Return: 0 on success and errno on failure. + */ +int hdd_hif_open(struct device *dev, void *bdev, const struct hif_bus_id *bid, + enum qdf_bus_type bus_type, bool reinit); + +/** + * hdd_soc_idle_restart_lock() - Takes wakelock for idle restart + * @dev: wlan device structure + * + * This function takes wakelock to prevent suspend during idle restart + * + * Return: 0 for success and non zero for error + */ +int hdd_soc_idle_restart_lock(struct device *dev); + +/** + * hdd_soc_idle_restart_unlock() - Releases wakelock for idle restart + * + * This function releases wakelock to allow suspend after idle restart + * + * Return: none + */ +void hdd_soc_idle_restart_unlock(void); + +#ifdef FORCE_WAKE +/** + * hdd_set_hif_init_phase() - Enable/disable the + * init_phase flag + * @hif_ctx: hif opaque handle + * @hal_init_phase: init phase flag + * + * Return: None + */ +void hdd_set_hif_init_phase(struct hif_opaque_softc *hif_ctx, + bool init_phase); +#else +static inline +void hdd_set_hif_init_phase(struct hif_opaque_softc *hif_ctx, + bool init_phase) +{ +} +#endif /* FORCE_WAKE */ +#endif /* __WLAN_HDD_DRIVER_OPS_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_ether.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_ether.h new file mode 100644 index 0000000000000000000000000000000000000000..1c71e01d075f1f16ac0f3ca13980bad1c7abc488 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_ether.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_HDD_ETHER_H +#define _WLAN_HDD_ETHER_H +/** + * DOC: wlan_hdd_ether.h + * + * This module describes Ethernet packet formats for processing by HDD. + */ + +/* + * Include Files + */ +#include +#include +#include +#include + +/* + * Preprocessor Definitions and Constants + */ +#define WLAN_SNAP_OUI_LEN 3 +#define WLAN_SNAP_DSAP 0xAAU +#define WLAN_SNAP_SSAP 0xAAU +#define WLAN_SNAP_CTRL 0x03 +#define WLAN_MIN_PROTO 0x0600 + +/* + * Type Declarations + */ +struct wlan_snap_hdr { + unsigned char dsap; + unsigned char ssap; + unsigned char ctrl; + unsigned char oui[WLAN_SNAP_OUI_LEN]; +} __packed; + +struct wlan_8023 { + unsigned char h_dest[ETH_ALEN]; + unsigned char h_source[ETH_ALEN]; + __be16 h_len; + struct wlan_snap_hdr h_snap; + __be16 h_proto; +} __packed; + +struct wlan_8023_vlan { + unsigned char h_dest[ETH_ALEN]; + unsigned char h_source[ETH_ALEN]; + __be16 h_vlan_proto; + __be16 h_vlan_TCI; + __be16 h_len; + struct wlan_snap_hdr h_snap; + __be16 h_proto; +} __packed; + +union generic_ethhdr { + struct ethhdr eth_II; + struct vlan_ethhdr eth_IIv; + struct wlan_8023 eth_8023; + struct wlan_8023_vlan eth_8023v; +}; + +#endif /* #ifndef _WLAN_HDD_ETHER_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_ftm.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_ftm.h new file mode 100644 index 0000000000000000000000000000000000000000..d72dd88170c308788f27ed73d421828df3aee6a9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_ftm.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_HDD_FTM_H +#define WLAN_HDD_FTM_H + +/** + * DOC: wlan_hdd_ftm.h + * + * WLAN Host Device Driver Factory Test Mode header file + */ + +#include "qdf_status.h" +#include "scheduler_api.h" +#include "cds_api.h" +#include "qdf_types.h" +#include + +struct hdd_context; + +#if defined(QCA_WIFI_FTM) +int wlan_hdd_qcmbr_unified_ioctl(struct hdd_adapter *adapter, + struct ifreq *ifr); +int hdd_update_cds_config_ftm(struct hdd_context *hdd_ctx); +#else +static inline int hdd_update_cds_config_ftm(struct hdd_context *hdd_ctx) +{ + return 0; +} +#endif /* QCA_WIFI_FTM */ +#endif /* WLAN_HDD_FTM_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_fw_state.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_fw_state.h new file mode 100644 index 0000000000000000000000000000000000000000..b28de4b2d8f40860f84cdc6d89df13e5bbc190fd --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_fw_state.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_fw_state.h + * + * Get firmware state related API's and definitions + */ + +#ifndef __WLAN_HDD_FW_STATE_H +#define __WLAN_HDD_FW_STATE_H + +#ifdef FEATURE_FW_STATE +#include +/** + * wlan_hdd_cfg80211_get_fw_status() - get fw state + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * Return: 0 on success; error number otherwise. + */ +int wlan_hdd_cfg80211_get_fw_state(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_FW_STATE_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_get_fw_state \ +}, +#else /* FEATURE_FW_STATE */ +#define FEATURE_FW_STATE_COMMANDS +#endif /* FEATURE_FW_STATE */ +#endif /* __WLAN_HDD_FW_STATE_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_gpio.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_gpio.h new file mode 100644 index 0000000000000000000000000000000000000000..b0c15d81e2a024adbdece797c8758f9d2fc7175f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_gpio.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_gpio.h + * + * This Header file provide declaration for cfg80211 command handler API + */ + +#ifndef __WLAN_HDD_GPIO_H__ +#define __WLAN_HDD_GPIO_H__ + +#include +#include +#include + +#ifdef WLAN_FEATURE_GPIO_CFG +#include + +#define FEATURE_GPIO_CFG_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_set_gpio_config, \ + vendor_command_policy(wlan_cfg80211_gpio_config_policy, \ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX) \ +}, + +/** + * wlan_hdd_cfg80211_set_gpio_config() - set GPIO config + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data to be passed via vendor interface + * @data_len: Length of the data to be passed + * + * Return: Return the Success or Failure code + */ +int wlan_hdd_cfg80211_set_gpio_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); +#else +#define FEATURE_GPIO_CFG_VENDOR_COMMANDS +#endif /* WLAN_FEATURE_GPIO_CFG */ +#endif /* __WLAN_CFG80211_GPIO_CFG_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_hang_event.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_hang_event.h new file mode 100644 index 0000000000000000000000000000000000000000..3376b2ce4b69f592a7989792dacd356d0574bc1d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_hang_event.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef WLAN_HDD_HANG_EVENT_H +#define WLAN_HDD_HANG_EVENT_H +#include +#include + +#ifdef WLAN_HANG_EVENT +/** + * wlan_hdd_hang_event_notifier_register() - HDD hang event notifier register + * @hdd_ctx: HDD context + * + * This function registers hdd layer notifier for the hang event notifier chain. + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_hdd_hang_event_notifier_register(struct hdd_context *hdd_ctx); +/** + * wlan_hdd_hang_event_notifier_unregister() - HDD hang event notifier + * unregister + * @hdd_ctx: HDD context + * + * This function unregisters hdd layer notifier for the hang event notifier + * chain. + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_hdd_hang_event_notifier_unregister(void); +#else +static inline +QDF_STATUS wlan_hdd_hang_event_notifier_register(struct hdd_context *hdd_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS wlan_hdd_hang_event_notifier_unregister(void) +{ + return QDF_STATUS_SUCCESS; +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_he.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_he.h new file mode 100644 index 0000000000000000000000000000000000000000..e19db708e51aad9e20abd4b7cb2018e439479baf --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_he.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_he.h + * + * WLAN Host Device Driver file for 802.11ax (High Efficiency) support. + * + */ + +#if !defined(WLAN_HDD_HE_H) +#define WLAN_HDD_HE_H + +struct hdd_context; +struct wma_tgt_cfg; +struct hdd_beacon_data; +struct sap_config; + +#ifdef WLAN_FEATURE_11AX +/** + * enum qca_wlan_vendor_attr_get_he_capabilities - attributes for HE caps. + * vendor command. + * @QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID - invalid + * @QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED - to check if HE capabilities is supported + * @QCA_WLAN_VENDOR_ATTR_PHY_CAPAB - to get HE PHY capabilities + * @QCA_WLAN_VENDOR_ATTR_MAC_CAPAB - to get HE MAC capabilities + * @QCA_WLAN_VENDOR_ATTR_HE_MCS - to get HE MCS + * @QCA_WLAN_VENDOR_ATTR_NUM_SS - to get NUM SS + * @QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK - to get RU index mask + * @QCA_WLAN_VENDOR_ATTR_RU_COUNT - to get RU count, + * @QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD - to get PPE Threshold, + * @QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - next to last valid enum + * @QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX - max value supported + * + * enum values are used for NL attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES sub command. + */ +enum qca_wlan_vendor_attr_get_he_capabilities { + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED, + QCA_WLAN_VENDOR_ATTR_PHY_CAPAB, + QCA_WLAN_VENDOR_ATTR_MAC_CAPAB, + QCA_WLAN_VENDOR_ATTR_HE_MCS, + QCA_WLAN_VENDOR_ATTR_NUM_SS = 5, + QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK, + QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX = + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1, +}; + +/** + * hdd_update_tgt_he_cap() - Update HE related capabilities + * @hdd_ctx: HDD context + * @he_cap: Target HE capabilities + * + * This function updaates WNI CFG with Target capabilities received as part of + * Default values present in WNI CFG are the values supported by FW/HW. + * INI should be introduced if user control is required to control the value. + * + * Return: None + */ +void hdd_update_tgt_he_cap(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *cfg); + +/** + * wlan_hdd_check_11ax_support() - check if beacon IE and update hw mode + * @beacon: beacon IE buffer + * @config: pointer to sap config + * + * Check if HE cap IE is present in beacon IE, if present update hw mode + * to 11ax. + * + * Return: None + */ +void wlan_hdd_check_11ax_support(struct hdd_beacon_data *beacon, + struct sap_config *config); + +/** + * hdd_update_he_cap_in_cfg() - update HE cap in global CFG + * @hdd_ctx: pointer to hdd context + * + * This API will update the HE config in CFG after taking intersection + * of INI and firmware capabilities provided reading CFG + * + * Return: 0 on success and errno on failure + */ +int hdd_update_he_cap_in_cfg(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_cfg80211_get_he_cap() - get HE Capabilities + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 if success, non-zero for failure + */ +int wlan_hdd_cfg80211_get_he_cap(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, + int data_len); +#define FEATURE_11AX_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_get_he_cap \ +}, + +#else +static inline void hdd_update_tgt_he_cap(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *cfg) +{ +} + +static inline void wlan_hdd_check_11ax_support(struct hdd_beacon_data *beacon, + struct sap_config *config) +{ +} + +static inline int hdd_update_he_cap_in_cfg(struct hdd_context *hdd_ctx) +{ + return 0; +} + +/* dummy definition */ +#define FEATURE_11AX_VENDOR_COMMANDS + +#endif +#endif /* if !defined(WLAN_HDD_HE_H)*/ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_host_offload.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_host_offload.h new file mode 100644 index 0000000000000000000000000000000000000000..e9b984e5d270399cb13dfbecdcca7a7ccefb853a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_host_offload.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014-2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_HOST_OFFLOAD_H__ +#define __WLAN_HDD_HOST_OFFLOAD_H__ + +/** + * DOC: wlan_hdd_host_offload.h + * + * Android WLAN HDD Host Offload API + */ + +/* Offload types. */ +#define WLAN_IPV4_ARP_REPLY_OFFLOAD 0 +#define WLAN_IPV6_NEIGHBOR_DISCOVERY_OFFLOAD 1 + +/* Enable or disable offload. */ +#define WLAN_OFFLOAD_DISABLE 0 +#define WLAN_OFFLOAD_ENABLE 0x1 +#define WLAN_OFFLOAD_BC_FILTER_ENABLE 0x2 +#define WLAN_OFFLOAD_ARP_AND_BC_FILTER_ENABLE \ + (WLAN_OFFLOAD_ENABLE | WLAN_OFFLOAD_BC_FILTER_ENABLE) + +/* Offload request. */ +struct host_offload_req { + uint8_t offloadType; + uint8_t enableOrDisable; + union { + uint8_t hostIpv4Addr[QDF_IPV4_ADDR_SIZE]; + uint8_t hostIpv6Addr[QDF_IPV6_ADDR_SIZE]; + } params; + struct qdf_mac_addr bssId; +}; + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +void hdd_wlan_offload_event(uint8_t type, uint8_t state); +#else +static inline +void hdd_wlan_offload_event(uint8_t type, uint8_t state) +{ +} +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +#endif /* __WLAN_HDD_HOST_OFFLOAD_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_includes.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_includes.h new file mode 100644 index 0000000000000000000000000000000000000000..1c1c90a0e853a318ca3e45ac06cb07af7ade6fec --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_includes.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(HDD_INCLUDES_H__) +#define HDD_INCLUDES_H__ + +/** + * DOC: wlan_hdd_includes.h + * + * Internal includes for the Linux HDD + */ + +/* + * Include files + * + * throw all the includes in here to get the .c files in the HDD to compile. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_hdd_assoc.h" +#include "wlan_hdd_wext.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_tx_rx.h" +#include + +#ifdef FEATURE_OEM_DATA_SUPPORT +#include "wlan_hdd_oemdata.h" +#endif + +#endif /* end #if !defined(HDD_INCLUDES_H__) */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_ipa.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_ipa.h new file mode 100644 index 0000000000000000000000000000000000000000..459449bcae5cc711f0dbfdd571749336fb3b1649 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_ipa.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef HDD_IPA_H__ +#define HDD_IPA_H__ + +/** + * DOC: wlan_hdd_ipa.h + * + * WLAN IPA interface module headers + */ + +#include + +#ifdef IPA_OFFLOAD + +/** + * hdd_ipa_send_nbuf_to_network() - Send network buffer to kernel + * @nbuf: network buffer + * @dev: network adapter + * + * Called when a network buffer is received which should not be routed + * to the IPA module. + * + * Return: None + */ +void hdd_ipa_send_nbuf_to_network(qdf_nbuf_t nbuf, qdf_netdev_t dev); + +/** + * hdd_ipa_set_tx_flow_info() - To set TX flow info if IPA is + * enabled + * + * This routine is called to set TX flow info if IPA is enabled + * + * Return: None + */ +void hdd_ipa_set_tx_flow_info(void); + +/** + * hdd_ipa_set_mcc_mode() - To set mcc mode if IPA is enabled + * @mcc_mode: mcc mode + * + * This routine is called to set mcc mode if IPA is enabled + * + * Return: None + */ +void hdd_ipa_set_mcc_mode(bool mcc_mode); + +#else +static inline +void hdd_ipa_send_nbuf_to_network(qdf_nbuf_t skb, qdf_netdev_t dev) +{ +} + +static inline void hdd_ipa_set_tx_flow_info(void) +{ +} + +static inline void hdd_ipa_set_mcc_mode(bool mcc_mode) +{ +} + +#endif +#endif /* HDD_IPA_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_lro.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_lro.h new file mode 100644 index 0000000000000000000000000000000000000000..afa7fdb6e9f8292b9aca767be80ee082615f39c9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_lro.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_LRO_H__ +#define __WLAN_HDD_LRO_H__ +/** + * DOC: wlan_hdd_lro.h + * + * WLAN LRO interface module headers + */ + +struct hdd_context; + +#if defined(FEATURE_LRO) +/** + * hdd_lro_rx() - Handle Rx procesing via LRO + * @adapter: pointer to adapter context + * @skb: pointer to sk_buff + * + * Return: QDF_STATUS_SUCCESS if processed via LRO or non zero return code + */ +QDF_STATUS hdd_lro_rx(struct hdd_adapter *adapter, struct sk_buff *skb); + +void hdd_lro_display_stats(struct hdd_context *hdd_ctx); + +/** + * hdd_lro_set_reset() - vendor command for Disable/Enable LRO + * @hdd_ctx: hdd context + * @hdd_adapter_t: adapter + * @enable_flag: enable or disable LRO. + * + * Return: none + */ +QDF_STATUS hdd_lro_set_reset(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t enable_flag); + +/** + * hdd_is_lro_enabled() - Is LRO enabled + * @hdd_ctx: HDD context + * + * This function checks if LRO is enabled in HDD context. + * + * Return: 0 - success, < 0 - failure + */ +int hdd_is_lro_enabled(struct hdd_context *hdd_ctx); + +#else +static inline QDF_STATUS hdd_lro_rx(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline void hdd_lro_display_stats(struct hdd_context *hdd_ctx) +{ +} + +static inline QDF_STATUS hdd_lro_set_reset(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t enable_flag) +{ + return 0; +} + +static inline int hdd_is_lro_enabled(struct hdd_context *hdd_ctx) +{ + return -EOPNOTSUPP; +} +#endif /* FEATURE_LRO */ +#endif /* __WLAN_HDD_LRO_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_main.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_main.h new file mode 100644 index 0000000000000000000000000000000000000000..5f247c185095f89f39d052fb3d05effd3ebc6786 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_main.h @@ -0,0 +1,4769 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_MAIN_H) +#define WLAN_HDD_MAIN_H +/** + * DOC: wlan_hdd_main.h + * + * Linux HDD Adapter Type + */ + +/* + * The following terms were in use in prior versions of the driver but + * have now been replaced with terms that are aligned with the Linux + * Coding style. Macros are defined to hopefully prevent new instances + * from being introduced, primarily by code propagation. + */ +#define pHddCtx +#define pAdapter +#define pHostapdAdapter +#define pHddApCtx +#define pHddStaCtx +#define pHostapdState +#define pRoamInfo +#define pScanInfo +#define pBeaconIes + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include "sir_mac_prot_def.h" +#include "csr_api.h" +#include "wlan_dsc.h" +#include +#include +#include +#include +#include +#if defined(WLAN_OPEN_SOURCE) && defined(CONFIG_HAS_WAKELOCK) +#include +#endif +#ifdef WLAN_FEATURE_TSF_PTP +#include +#include +#endif +#include +#include "wlan_hdd_tdls.h" +#include "wlan_hdd_tsf.h" +#include "wlan_hdd_cfg80211.h" +#include "wlan_hdd_debugfs.h" +#include +#include "sap_api.h" +#include +#include "cdp_txrx_flow_ctrl_legacy.h" +#include +#include "wlan_hdd_nan_datapath.h" +#if defined(CONFIG_HL_SUPPORT) +#include "wlan_tgt_def_config_hl.h" +#else +#include "wlan_tgt_def_config.h" +#endif +#include +#include +#include +#include +#include +#include +#include "wlan_pmo_ucfg_api.h" +#ifdef WIFI_POS_CONVERGED +#include "os_if_wifi_pos.h" +#include "wifi_pos_api.h" +#else +#include "wlan_hdd_oemdata.h" +#endif +#include "wlan_hdd_he.h" + +#include +#include +#include "wlan_hdd_nud_tracking.h" +#include "wlan_hdd_twt.h" +#include "wma_sar_public_structs.h" +#include "wlan_mlme_ucfg_api.h" +#include "pld_common.h" +#include + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +#include "qdf_periodic_work.h" +#endif + +#if defined(CLD_PM_QOS) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) +#include +#endif + +#include "wlan_hdd_sta_info.h" + +/* + * Preprocessor definitions and constants + */ + +#ifdef FEATURE_WLAN_APF +/** + * struct hdd_apf_context - hdd Context for apf + * @magic: magic number + * @qdf_apf_event: Completion variable for APF get operations + * @capability_response: capabilities response received from fw + * @apf_enabled: True: APF Interpreter enabled, False: Disabled + * @cmd_in_progress: Flag that indicates an APF command is in progress + * @buf: Buffer to accumulate read memory chunks + * @buf_len: Length of the read memory requested + * @offset: APF work memory offset to fetch from + * @lock: APF Context lock + */ +struct hdd_apf_context { + unsigned int magic; + qdf_event_t qdf_apf_event; + bool apf_enabled; + bool cmd_in_progress; + uint8_t *buf; + uint32_t buf_len; + uint32_t offset; + qdf_spinlock_t lock; +}; +#endif /* FEATURE_WLAN_APF */ + +/** Number of Tx Queues */ +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || \ + defined(QCA_HL_NETDEV_FLOW_CONTROL) || \ + defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +#define NUM_TX_QUEUES 5 +#else +#define NUM_TX_QUEUES 4 +#endif + +/* HDD_IS_RATE_LIMIT_REQ: Macro helper to implement rate limiting + * @flag: The flag to determine if limiting is required or not + * @rate: The number of seconds within which if multiple commands come, the + * flag will be set to true + * + * If the function in which this macro is used is called multiple times within + * "rate" number of seconds, the "flag" will be set to true which can be used + * to reject/take appropriate action. + */ +#define HDD_IS_RATE_LIMIT_REQ(flag, rate)\ + do {\ + static ulong __last_ticks;\ + ulong __ticks = jiffies;\ + flag = false; \ + if (!time_after(__ticks,\ + __last_ticks + rate * HZ)) {\ + flag = true; \ + } \ + else { \ + __last_ticks = __ticks;\ + } \ + } while (0) + +/* + * API in_compat_syscall() is introduced in 4.6 kernel to check whether we're + * in a compat syscall or not. It is a new way to query the syscall type, which + * works properly on all architectures. + * + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)) +static inline bool in_compat_syscall(void) { return is_compat_task(); } +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) || \ + defined(CFG80211_REMOVE_IEEE80211_BACKPORT) +#define HDD_NL80211_BAND_2GHZ NL80211_BAND_2GHZ +#define HDD_NL80211_BAND_5GHZ NL80211_BAND_5GHZ +#define HDD_NUM_NL80211_BANDS NUM_NL80211_BANDS +#else +#define HDD_NL80211_BAND_2GHZ IEEE80211_BAND_2GHZ +#define HDD_NL80211_BAND_5GHZ IEEE80211_BAND_5GHZ +#define HDD_NUM_NL80211_BANDS ((enum nl80211_band)IEEE80211_NUM_BANDS) +#endif + +#if defined(CONFIG_BAND_6GHZ) && (defined(CFG80211_6GHZ_BAND_SUPPORTED) || \ + (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)) +#define HDD_NL80211_BAND_6GHZ NL80211_BAND_6GHZ +#endif + +#define TSF_GPIO_PIN_INVALID 255 + +/** Length of the TX queue for the netdev */ +#define HDD_NETDEV_TX_QUEUE_LEN (3000) + +/** Hdd Tx Time out value */ +#define HDD_TX_TIMEOUT msecs_to_jiffies(5000) + +#define HDD_TX_STALL_THRESHOLD 4 + +/** Hdd Default MTU */ +#define HDD_DEFAULT_MTU (1500) + +#ifdef QCA_CONFIG_SMP +#define NUM_CPUS NR_CPUS +#else +#define NUM_CPUS 1 +#endif + +#define ACS_COMPLETE_TIMEOUT 3000 + +#define HDD_PSOC_IDLE_SHUTDOWN_SUSPEND_DELAY (1000) +/** + * enum hdd_adapter_flags - event bitmap flags registered net device + * @NET_DEVICE_REGISTERED: Adapter is registered with the kernel + * @SME_SESSION_OPENED: Firmware vdev has been created + * @INIT_TX_RX_SUCCESS: Adapter datapath is initialized + * @WMM_INIT_DONE: Adapter is initialized + * @SOFTAP_BSS_STARTED: Software Access Point (SAP) is running + * @DEVICE_IFACE_OPENED: Adapter has been "opened" via the kernel + * @SOFTAP_INIT_DONE: Software Access Point (SAP) is initialized + * @VENDOR_ACS_RESPONSE_PENDING: Waiting for event for vendor acs + * @DOWN_DURING_SSR: Mark interface is down during SSR + */ +enum hdd_adapter_flags { + NET_DEVICE_REGISTERED, + SME_SESSION_OPENED, + INIT_TX_RX_SUCCESS, + WMM_INIT_DONE, + SOFTAP_BSS_STARTED, + DEVICE_IFACE_OPENED, + SOFTAP_INIT_DONE, + VENDOR_ACS_RESPONSE_PENDING, + DOWN_DURING_SSR, +}; + +#define WLAN_WAIT_DISCONNECT_ALREADY_IN_PROGRESS 1000 +#define WLAN_WAIT_TIME_STOP_ROAM 4000 +#define WLAN_WAIT_TIME_STATS 800 +#define WLAN_WAIT_TIME_LINK_STATUS 800 + +/** Maximum time(ms) to wait for mc thread suspend **/ +#define WLAN_WAIT_TIME_MCTHREAD_SUSPEND 1200 + +/** Maximum time(ms) to wait for target to be ready for suspend **/ +#define WLAN_WAIT_TIME_READY_TO_SUSPEND 2000 + +/* Scan Req Timeout */ +#define WLAN_WAIT_TIME_SCAN_REQ 100 + +#define WLAN_WAIT_TIME_APF 1000 + +#define WLAN_WAIT_TIME_FW_ROAM_STATS 1000 + +#define WLAN_WAIT_TIME_ANTENNA_ISOLATION 8000 + +/* Maximum time(ms) to wait for RSO CMD status event */ +#define WAIT_TIME_RSO_CMD_STATUS 2000 + +/* rcpi request timeout in milli seconds */ +#define WLAN_WAIT_TIME_RCPI 500 + +#define WLAN_WAIT_PEER_CLEANUP 5000 + +#define MAX_CFG_STRING_LEN 255 + +/* Maximum time(ms) to wait for external acs response */ +#define WLAN_VENDOR_ACS_WAIT_TIME 1000 + +/* Maximum time(ms) to wait for monitor mode vdev up event completion*/ +#define WLAN_MONITOR_MODE_VDEV_UP_EVT SME_CMD_VDEV_START_BSS_TIMEOUT + +/* Mac Address string length */ +#define MAC_ADDRESS_STR_LEN 18 /* Including null terminator */ +/* Max and min IEs length in bytes */ +#define MAX_GENIE_LEN (512) +#define MIN_GENIE_LEN (2) + +#define WPS_OUI_TYPE "\x00\x50\xf2\x04" +#define WPS_OUI_TYPE_SIZE 4 + +#define P2P_OUI_TYPE "\x50\x6f\x9a\x09" +#define P2P_OUI_TYPE_SIZE 4 + +#define HS20_OUI_TYPE "\x50\x6f\x9a\x10" +#define HS20_OUI_TYPE_SIZE 4 + +#define OSEN_OUI_TYPE "\x50\x6f\x9a\x12" +#define OSEN_OUI_TYPE_SIZE 4 + +#ifdef WLAN_FEATURE_WFD +#define WFD_OUI_TYPE "\x50\x6f\x9a\x0a" +#define WFD_OUI_TYPE_SIZE 4 +#endif + +#define MBO_OUI_TYPE "\x50\x6f\x9a\x16" +#define MBO_OUI_TYPE_SIZE 4 + +#define QCN_OUI_TYPE "\x8c\xfd\xf0\x01" +#define QCN_OUI_TYPE_SIZE 4 + +#define wlan_hdd_get_wps_ie_ptr(ie, ie_len) \ + wlan_get_vendor_ie_ptr_from_oui(WPS_OUI_TYPE, WPS_OUI_TYPE_SIZE, \ + ie, ie_len) + +#define hdd_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_HDD, params) +#define hdd_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_HDD, params) +#define hdd_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_HDD, params) +#define hdd_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_HDD, params) +#define hdd_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_HDD, params) + +#define hdd_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_HDD, params) +#define hdd_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_HDD, params) +#define hdd_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_HDD, params) +#define hdd_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_HDD, params) +#define hdd_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_HDD, params) + +#define hdd_alert_rl(params...) QDF_TRACE_FATAL_RL(QDF_MODULE_ID_HDD, params) +#define hdd_err_rl(params...) QDF_TRACE_ERROR_RL(QDF_MODULE_ID_HDD, params) +#define hdd_warn_rl(params...) QDF_TRACE_WARN_RL(QDF_MODULE_ID_HDD, params) +#define hdd_info_rl(params...) QDF_TRACE_INFO_RL(QDF_MODULE_ID_HDD, params) +#define hdd_debug_rl(params...) QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_HDD, params) + +#define hdd_enter() QDF_TRACE_ENTER(QDF_MODULE_ID_HDD, "enter") +#define hdd_enter_dev(dev) \ + QDF_TRACE_ENTER(QDF_MODULE_ID_HDD, "enter(%s)", (dev)->name) +#define hdd_exit() QDF_TRACE_EXIT(QDF_MODULE_ID_HDD, "exit") + +#define WLAN_HDD_GET_PRIV_PTR(__dev__) \ + (struct hdd_adapter *)(netdev_priv((__dev__))) + +#define MAX_NO_OF_2_4_CHANNELS 14 + +#define WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET 24 + +#define WLAN_HDD_IS_SOCIAL_CHANNEL(center_freq) \ + (((center_freq) == 2412) || ((center_freq) == 2437) || \ + ((center_freq) == 2462)) + +#define WLAN_HDD_QOS_ACTION_FRAME 1 +#define WLAN_HDD_QOS_MAP_CONFIGURE 4 +#define HDD_SAP_WAKE_LOCK_DURATION WAKELOCK_DURATION_RECOMMENDED + +/* SAP client disconnect wake lock duration in milli seconds */ +#define HDD_SAP_CLIENT_DISCONNECT_WAKE_LOCK_DURATION \ + WAKELOCK_DURATION_RECOMMENDED + +#define HDD_CFG_REQUEST_FIRMWARE_RETRIES (3) +#define HDD_CFG_REQUEST_FIRMWARE_DELAY (20) + +#define MAX_USER_COMMAND_SIZE 4096 +#define DNS_DOMAIN_NAME_MAX_LEN 255 +#define ICMPv6_ADDR_LEN 16 + + +#define HDD_MIN_TX_POWER (-100) /* minimum tx power */ +#define HDD_MAX_TX_POWER (+100) /* maximum tx power */ + +/* If IPA UC data path is enabled, target should reserve extra tx descriptors + * for IPA data path. + * Then host data path should allow less TX packet pumping in case + * IPA data path enabled + */ +#define WLAN_TFC_IPAUC_TX_DESC_RESERVE 100 + +/* + * NET_NAME_UNKNOWN is only introduced after Kernel 3.17, to have a macro + * here if the Kernel version is less than 3.17 to avoid the interleave + * conditional compilation. + */ +#if !((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) ||\ + defined(WITH_BACKPORTS)) +#define NET_NAME_UNKNOWN 0 +#endif + +#define PRE_CAC_SSID "pre_cac_ssid" + +#define SCAN_REJECT_THRESHOLD_TIME 300000 /* Time is in msec, equal to 5 mins */ +#define SCAN_REJECT_THRESHOLD 15 + +/* Default Psoc id */ +#define DEFAULT_PSOC_ID 1 + +/* wait time for nud stats in milliseconds */ +#define WLAN_WAIT_TIME_NUD_STATS 800 +/* nud stats skb max length */ +#define WLAN_NUD_STATS_LEN 800 +/* ARP packet type for NUD debug stats */ +#define WLAN_NUD_STATS_ARP_PKT_TYPE 1 +/* Assigned size of driver memory dump is 4096 bytes */ +#define DRIVER_MEM_DUMP_SIZE 4096 + +/* MAX OS Q block time value in msec + * Prevent from permanent stall, resume OS Q if timer expired + */ +#define WLAN_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME 1000 +#define WLAN_SAP_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME 100 +#define WLAN_HDD_TX_FLOW_CONTROL_MAX_24BAND_CH 14 + +#ifndef NUM_TX_RX_HISTOGRAM +#define NUM_TX_RX_HISTOGRAM 128 +#endif + +#define NUM_TX_RX_HISTOGRAM_MASK (NUM_TX_RX_HISTOGRAM - 1) + +#define HDD_NOISE_FLOOR_DBM (-96) + +/** + * enum hdd_auth_key_mgmt - auth key mgmt protocols + * @HDD_AUTH_KEY_MGMT_802_1X: 802.1x + * @HDD_AUTH_KEY_MGMT_PSK: PSK + * @HDD_AUTH_KEY_MGMT_CCKM: CCKM + */ +enum hdd_auth_key_mgmt { + HDD_AUTH_KEY_MGMT_802_1X = BIT(0), + HDD_AUTH_KEY_MGMT_PSK = BIT(1), + HDD_AUTH_KEY_MGMT_CCKM = BIT(2) +}; + +/** + * wlan_net_dev_ref_dbgid - Debug IDs to detect net device reference leaks + */ +/* + * New value added to the enum must also be reflected in function + * net_dev_ref_debug_string_from_id() + */ +typedef enum { + NET_DEV_HOLD_ID_RESERVED = 0, + NET_DEV_HOLD_GET_STA_CONNECTION_IN_PROGRESS = 1, + NET_DEV_HOLD_CHECK_DFS_CHANNEL_FOR_ADAPTER = 2, + NET_DEV_HOLD_GET_SAP_OPERATING_BAND = 3, + NET_DEV_HOLD_RECOVERY_NOTIFIER_CALL = 4, + NET_DEV_HOLD_IS_ANY_STA_CONNECTING = 5, + NET_DEV_HOLD_SAP_DESTROY_CTX_ALL = 6, + NET_DEV_HOLD_DRV_CMD_MAX_TX_POWER = 7, + NET_DEV_HOLD_IPA_SET_TX_FLOW_INFO = 8, + NET_DEV_HOLD_SET_RPS_CPU_MASK = 9, + NET_DEV_HOLD_DFS_INDICATE_RADAR = 10, + NET_DEV_HOLD_MAX_STA_INTERFACE_UP_COUNT_REACHED = 11, + NET_DEV_HOLD_IS_CHAN_SWITCH_IN_PROGRESS = 12, + NET_DEV_HOLD_STA_DESTROY_CTX_ALL = 13, + NET_DEV_HOLD_CHECK_FOR_EXISTING_MACADDR = 14, + NET_DEV_HOLD_DEINIT_ALL_ADAPTERS = 15, + NET_DEV_HOLD_STOP_ALL_ADAPTERS = 16, + NET_DEV_HOLD_RESET_ALL_ADAPTERS = 17, + NET_DEV_HOLD_IS_ANY_INTERFACE_OPEN = 18, + NET_DEV_HOLD_START_ALL_ADAPTERS = 19, + NET_DEV_HOLD_GET_ADAPTER_BY_RAND_MACADDR = 20, + NET_DEV_HOLD_GET_ADAPTER_BY_MACADDR = 21, + NET_DEV_HOLD_GET_ADAPTER_BY_VDEV = 22, + NET_DEV_HOLD_ADAPTER_GET_BY_REFERENCE = 23, + NET_DEV_HOLD_GET_ADAPTER_BY_IFACE_NAME = 24, + NET_DEV_HOLD_GET_ADAPTER = 25, + NET_DEV_HOLD_GET_OPERATING_CHAN_FREQ = 26, + NET_DEV_HOLD_UNREGISTER_WEXT_ALL_ADAPTERS = 27, + NET_DEV_HOLD_ABORT_MAC_SCAN_ALL_ADAPTERS = 28, + NET_DEV_HOLD_ABORT_SCHED_SCAN_ALL_ADAPTERS = 29, + NET_DEV_HOLD_GET_FIRST_VALID_ADAPTER = 30, + NET_DEV_HOLD_CLEAR_RPS_CPU_MASK = 31, + NET_DEV_HOLD_BUS_BW_WORK_HANDLER = 32, + NET_DEV_HOLD_DISPLAY_NETIF_QUEUE_HISTORY_COMPACT = 33, + NET_DEV_HOLD_DISPLAY_NETIF_QUEUE_HISTORY = 34, + NET_DEV_HOLD_CLEAR_NETIF_QUEUE_HISTORY = 35, + NET_DEV_HOLD_UNSAFE_CHANNEL_RESTART_SAP = 36, + NET_DEV_HOLD_INDICATE_MGMT_FRAME = 37, + NET_DEV_HOLD_STATE_INFO_DUMP = 38, + NET_DEV_HOLD_DISABLE_ROAMING = 39, + NET_DEV_HOLD_ENABLE_ROAMING = 40, + NET_DEV_HOLD_AUTO_SHUTDOWN_ENABLE = 41, + NET_DEV_HOLD_GET_CON_SAP_ADAPTER = 42, + NET_DEV_HOLD_IS_ANY_ADAPTER_CONNECTED = 43, + NET_DEV_HOLD_IS_ROAMING_IN_PROGRESS = 44, + NET_DEV_HOLD_DEL_P2P_INTERFACE = 45, + NET_DEV_HOLD_IS_NDP_ALLOWED = 46, + NET_DEV_HOLD_NDI_OPEN = 47, + NET_DEV_HOLD_SEND_OEM_REG_RSP_NLINK_MSG = 48, + NET_DEV_HOLD_PERIODIC_STA_STATS_DISPLAY = 49, + NET_DEV_HOLD_SUSPEND_WLAN = 50, + NET_DEV_HOLD_RESUME_WLAN = 51, + NET_DEV_HOLD_SSR_RESTART_SAP = 52, + NET_DEV_HOLD_SEND_DEFAULT_SCAN_IES = 53, + NET_DEV_HOLD_CFG80211_SUSPEND_WLAN = 54, + NET_DEV_HOLD_COUNTRY_CHANGE_UPDATE_STA = 55, + NET_DEV_HOLD_COUNTRY_CHANGE_UPDATE_SAP = 56, + NET_DEV_HOLD_CACHE_STATION_STATS_CB = 57, + NET_DEV_HOLD_DISPLAY_TXRX_STATS = 58, + NET_DEV_HOLD_CDS_INCR_ARP_STATS_TX_TGT_DELIVERED = 59, + NET_DEV_HOLD_CDS_INCR_ARP_STATS_TX_TGT_ACKED = 60, + NET_DEV_HOLD_GET_MODE_SPECIFIC_INTERFACE_COUNT = 61, + + /* Keep it at the end */ + NET_DEV_HOLD_ID_MAX +} wlan_net_dev_ref_dbgid; + +/** + * struct hdd_tx_rx_histogram - structure to keep track of tx and rx packets + * received over 100ms intervals + * @interval_rx: # of rx packets received in the last 100ms interval + * @interval_tx: # of tx packets received in the last 100ms interval + * @next_vote_level: pld_bus_width_type voting level (high or low) + * determined on the basis of total tx and rx packets + * received in the last 100ms interval + * @next_rx_level: pld_bus_width_type voting level (high or low) + * determined on the basis of rx packets received in the + * last 100ms interval + * @next_tx_level: pld_bus_width_type voting level (high or low) + * determined on the basis of tx packets received in the + * last 100ms interval + * @is_rx_pm_qos_high Capture rx_pm_qos voting + * @is_tx_pm_qos_high Capture tx_pm_qos voting + * @qtime timestamp when the record is added + * + * The structure keeps track of throughput requirements of wlan driver. + * An entry is added if either of next_vote_level, next_rx_level or + * next_tx_level changes. An entry is not added for every 100ms interval. + */ +struct hdd_tx_rx_histogram { + uint64_t interval_rx; + uint64_t interval_tx; + uint32_t next_vote_level; + uint32_t next_rx_level; + uint32_t next_tx_level; + bool is_rx_pm_qos_high; + bool is_tx_pm_qos_high; + uint64_t qtime; +}; + +struct hdd_tx_rx_stats { + /* start_xmit stats */ + __u32 tx_called; + __u32 tx_dropped; + __u32 tx_orphaned; + __u32 tx_classified_ac[NUM_TX_QUEUES]; + __u32 tx_dropped_ac[NUM_TX_QUEUES]; + + /* rx stats */ + __u32 rx_packets[NUM_CPUS]; + __u32 rx_dropped[NUM_CPUS]; + __u32 rx_delivered[NUM_CPUS]; + __u32 rx_refused[NUM_CPUS]; + qdf_atomic_t rx_usolict_arp_n_mcast_drp; + + /* rx gro */ + __u32 rx_aggregated; + __u32 rx_gro_dropped; + __u32 rx_non_aggregated; + __u32 rx_gro_flush_skip; + __u32 rx_gro_low_tput_flush; + + /* txflow stats */ + bool is_txflow_paused; + __u32 txflow_pause_cnt; + __u32 txflow_unpause_cnt; + __u32 txflow_timer_cnt; + + /*tx timeout stats*/ + __u32 tx_timeout_cnt; + __u32 cont_txtimeout_cnt; + u64 jiffies_last_txtimeout; +}; + +#ifdef WLAN_FEATURE_11W +/** + * struct hdd_pmf_stats - Protected Management Frame statistics + * @num_unprot_deauth_rx: Number of unprotected deauth frames received + * @num_unprot_disassoc_rx: Number of unprotected disassoc frames received + */ +struct hdd_pmf_stats { + uint8_t num_unprot_deauth_rx; + uint8_t num_unprot_disassoc_rx; +}; +#endif + +/** + * struct hdd_arp_stats_s - arp debug stats count + * @tx_arp_req_count: no. of arp req received from network stack + * @rx_arp_rsp_count: no. of arp res received from FW + * @tx_dropped: no. of arp req dropped at hdd layer + * @rx_dropped: no. of arp res dropped + * @rx_delivered: no. of arp res delivered to network stack + * @rx_refused: no of arp rsp refused (not delivered) to network stack + * @tx_host_fw_sent: no of arp req sent by FW OTA + * @rx_host_drop_reorder: no of arp res dropped by host + * @rx_fw_cnt: no of arp res received by FW + * @tx_ack_cnt: no of arp req acked by FW + */ +struct hdd_arp_stats_s { + uint16_t tx_arp_req_count; + uint16_t rx_arp_rsp_count; + uint16_t tx_dropped; + uint16_t rx_dropped; + uint16_t rx_delivered; + uint16_t rx_refused; + uint16_t tx_host_fw_sent; + uint16_t rx_host_drop_reorder; + uint16_t rx_fw_cnt; + uint16_t tx_ack_cnt; +}; + +/** + * struct hdd_dns_stats_s - dns debug stats count + * @tx_dns_req_count: no. of dns query received from network stack + * @rx_dns_rsp_count: no. of dns res received from FW + * @tx_dropped: no. of dns query dropped at hdd layer + * @rx_delivered: no. of dns res delivered to network stack + * @rx_refused: no of dns res refused (not delivered) to network stack + * @tx_host_fw_sent: no of dns query sent by FW OTA + * @rx_host_drop: no of dns res dropped by host + * @tx_ack_cnt: no of dns req acked by FW + */ +struct hdd_dns_stats_s { + uint16_t tx_dns_req_count; + uint16_t rx_dns_rsp_count; + uint16_t tx_dropped; + uint16_t rx_delivered; + uint16_t rx_refused; + uint16_t tx_host_fw_sent; + uint16_t rx_host_drop; + uint16_t tx_ack_cnt; +}; + +/** + * struct hdd_tcp_stats_s - tcp debug stats count + * @tx_tcp_syn_count: no. of tcp syn received from network stack + * @@tx_tcp_ack_count: no. of tcp ack received from network stack + * @rx_tcp_syn_ack_count: no. of tcp syn ack received from FW + * @tx_tcp_syn_dropped: no. of tcp syn dropped at hdd layer + * @tx_tcp_ack_dropped: no. of tcp ack dropped at hdd layer + * @rx_delivered: no. of tcp syn ack delivered to network stack + * @rx_refused: no of tcp syn ack refused (not delivered) to network stack + * @tx_tcp_syn_host_fw_sent: no of tcp syn sent by FW OTA + * @@tx_tcp_ack_host_fw_sent: no of tcp ack sent by FW OTA + * @rx_host_drop: no of tcp syn ack dropped by host + * @tx_tcp_syn_ack_cnt: no of tcp syn acked by FW + * @tx_tcp_syn_ack_cnt: no of tcp ack acked by FW + * @is_tcp_syn_ack_rcv: flag to check tcp syn ack received or not + * @is_tcp_ack_sent: flag to check tcp ack sent or not + */ +struct hdd_tcp_stats_s { + uint16_t tx_tcp_syn_count; + uint16_t tx_tcp_ack_count; + uint16_t rx_tcp_syn_ack_count; + uint16_t tx_tcp_syn_dropped; + uint16_t tx_tcp_ack_dropped; + uint16_t rx_delivered; + uint16_t rx_refused; + uint16_t tx_tcp_syn_host_fw_sent; + uint16_t tx_tcp_ack_host_fw_sent; + uint16_t rx_host_drop; + uint16_t rx_fw_cnt; + uint16_t tx_tcp_syn_ack_cnt; + uint16_t tx_tcp_ack_ack_cnt; + bool is_tcp_syn_ack_rcv; + bool is_tcp_ack_sent; + +}; + +/** + * struct hdd_icmpv4_stats_s - icmpv4 debug stats count + * @tx_icmpv4_req_count: no. of icmpv4 req received from network stack + * @rx_icmpv4_rsp_count: no. of icmpv4 res received from FW + * @tx_dropped: no. of icmpv4 req dropped at hdd layer + * @rx_delivered: no. of icmpv4 res delivered to network stack + * @rx_refused: no of icmpv4 res refused (not delivered) to network stack + * @tx_host_fw_sent: no of icmpv4 req sent by FW OTA + * @rx_host_drop: no of icmpv4 res dropped by host + * @rx_fw_cnt: no of icmpv4 res received by FW + * @tx_ack_cnt: no of icmpv4 req acked by FW + */ +struct hdd_icmpv4_stats_s { + uint16_t tx_icmpv4_req_count; + uint16_t rx_icmpv4_rsp_count; + uint16_t tx_dropped; + uint16_t rx_delivered; + uint16_t rx_refused; + uint16_t tx_host_fw_sent; + uint16_t rx_host_drop; + uint16_t rx_fw_cnt; + uint16_t tx_ack_cnt; +}; + +/** + * struct hdd_peer_stats - Peer stats at HDD level + * @rx_count: RX count + * @rx_bytes: RX bytes + * @fcs_count: FCS err count + */ +struct hdd_peer_stats { + uint32_t rx_count; + uint64_t rx_bytes; + uint32_t fcs_count; +}; + +struct hdd_stats { + tCsrSummaryStatsInfo summary_stat; + tCsrGlobalClassAStatsInfo class_a_stat; + tCsrGlobalClassDStatsInfo class_d_stat; + struct csr_per_chain_rssi_stats_info per_chain_rssi_stats; + struct hdd_tx_rx_stats tx_rx_stats; + struct hdd_arp_stats_s hdd_arp_stats; + struct hdd_dns_stats_s hdd_dns_stats; + struct hdd_tcp_stats_s hdd_tcp_stats; + struct hdd_icmpv4_stats_s hdd_icmpv4_stats; + struct hdd_peer_stats peer_stats; +#ifdef WLAN_FEATURE_11W + struct hdd_pmf_stats hdd_pmf_stats; +#endif +}; + +/** + * struct hdd_roaming_info - HDD Internal Roaming Information + * @bssid: BSSID to which we are connected + * @peer_mac: Peer MAC address for IBSS connection + * @roam_id: Unique identifier for a roaming instance + * @roam_status: Current roam command status + * @defer_key_complete: Should key complete be deferred? + * + */ +struct hdd_roaming_info { + tSirMacAddr bssid; + tSirMacAddr peer_mac; + uint32_t roam_id; + eRoamCmdStatus roam_status; + bool defer_key_complete; + +}; + +#ifdef FEATURE_WLAN_WAPI +/* Define WAPI macros for Length, BKID count etc*/ +#define MAX_NUM_AKM_SUITES 16 + +/** WAPI AUTH mode definition */ +enum wapi_auth_mode { + WAPI_AUTH_MODE_OPEN = 0, + WAPI_AUTH_MODE_PSK = 1, + WAPI_AUTH_MODE_CERT +} __packed; + +#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) +#define WPA_GET_BE24(a) ((u32) ((a[0] << 16) | (a[1] << 8) | a[2])) +#define WAPI_PSK_AKM_SUITE 0x02721400 +#define WAPI_CERT_AKM_SUITE 0x01721400 + +/** + * struct hdd_wapi_info - WAPI Information structure definition + * @wapi_mode: Is WAPI enabled on this adapter? + * @is_wapi_sta: Is the STA associated with WAPI? + * @wapi_auth_mode: WAPI authentication mode used by this adapter + */ +struct hdd_wapi_info { + bool wapi_mode; + bool is_wapi_sta; + enum wapi_auth_mode wapi_auth_mode; +}; +#endif /* FEATURE_WLAN_WAPI */ + +struct hdd_beacon_data { + u8 *head; + u8 *tail; + u8 *proberesp_ies; + u8 *assocresp_ies; + int head_len; + int tail_len; + int proberesp_ies_len; + int assocresp_ies_len; + int dtim_period; +}; + +/** + * struct hdd_mon_set_ch_info - Holds monitor mode channel switch params + * @freq: Channel frequency. + * @cb_mode: Channel bonding + * @channel_width: Channel width 0/1/2 for 20/40/80MHz respectively. + * @phy_mode: PHY mode + */ +struct hdd_mon_set_ch_info { + uint32_t freq; + uint8_t cb_mode; + uint32_t channel_width; + eCsrPhyMode phy_mode; +}; + +/** + * struct hdd_station_ctx -- STA-specific information + * @roam_profile: current roaming profile + * @security_ie: WPA or RSN IE used by the @roam_profile + * @assoc_additional_ie: association additional IE used by the @roam_profile + * @wpa_versions: bitmap of supported WPA versions + * @auth_key_mgmt: bitmap of supported auth key mgmt protocols + * @requested_bssid: Specific BSSID to which to connect + * @conn_info: current connection information + * @roam_info: current roaming information + * @ft_carrier_on: is carrier on + * @ibss_sta_generation: current ibss generation. Incremented whenever + * ibss New peer joins and departs the network + * @ibss_enc_key_installed: is the ibss wep/wpa-none encryptions key + * installed? + * @ibss_enc_key: current ibss wep/wpa-none encryption key (if + * @ibss_enc_key_installed is %true) + * @ibss_peer_info: information about the ibss peer + * @hdd_reassoc_scenario: is station in the middle of reassociation? + * @sta_debug_state: STA context debug variable + * @broadcast_sta_id: STA ID assigned for broadcast frames + * @ch_info: monitor mode channel information + * @ap_supports_immediate_power_save: Does the current AP allow our STA + * to immediately go into power save? + */ +struct hdd_station_ctx { + struct csr_roam_profile roam_profile; + uint8_t security_ie[WLAN_MAX_IE_LEN]; + tSirAddie assoc_additional_ie; + enum nl80211_wpa_versions wpa_versions; + enum hdd_auth_key_mgmt auth_key_mgmt; + struct qdf_mac_addr requested_bssid; + struct hdd_connection_info conn_info; + struct hdd_connection_info cache_conn_info; + int ft_carrier_on; + int ibss_sta_generation; + bool ibss_enc_key_installed; + tCsrRoamSetKey ibss_enc_key; + tSirPeerInfoRspParams ibss_peer_info; + bool hdd_reassoc_scenario; + int sta_debug_state; + struct hdd_mon_set_ch_info ch_info; + bool ap_supports_immediate_power_save; +}; + +/** + * enum bss_state - current state of the BSS + * @BSS_STOP: BSS is stopped + * @BSS_START: BSS is started + */ +enum bss_state { + BSS_STOP, + BSS_START, +}; + +/** + * struct hdd_hostapd_state - hostapd-related state information + * @bss_state: Current state of the BSS + * @qdf_event: Event to synchronize actions between hostapd thread and + * internal callback threads + * @qdf_stop_bss_event: Event to synchronize Stop BSS. When Stop BSS + * is issued userspace thread can wait on this event. The event will + * be set when the Stop BSS processing in UMAC has completed. + * @qdf_sta_disassoc_event: Event to synchronize STA Disassociation. + * When a STA is disassociated userspace thread can wait on this + * event. The event will be set when the STA Disassociation + * processing in UMAC has completed. + * @qdf_sta_eap_frm_done_event: Event to synchronize P2P GO disassoc + * frame and EAP frame. + * @qdf_status: Used to communicate state from other threads to the + * userspace thread. + */ +struct hdd_hostapd_state { + enum bss_state bss_state; + qdf_event_t qdf_event; + qdf_event_t qdf_stop_bss_event; + qdf_event_t qdf_sta_disassoc_event; + qdf_event_t qdf_sta_eap_frm_done_event; + QDF_STATUS qdf_status; +}; + +/** + * enum bss_stop_reason - reasons why a BSS is stopped. + * @BSS_STOP_REASON_INVALID: no reason specified explicitly. + * @BSS_STOP_DUE_TO_MCC_SCC_SWITCH: BSS stopped due to host + * driver is trying to switch AP role to a different channel + * to maintain SCC mode with the STA role on the same card. + * this usually happens when STA is connected to an external + * AP that runs on a different channel + * @BSS_STOP_DUE_TO_VENDOR_CONFIG_CHAN: BSS stopped due to + * vendor subcmd set sap config channel + */ +enum bss_stop_reason { + BSS_STOP_REASON_INVALID = 0, + BSS_STOP_DUE_TO_MCC_SCC_SWITCH = 1, + BSS_STOP_DUE_TO_VENDOR_CONFIG_CHAN = 2, +}; + +/** + * struct hdd_rate_info - rate_info in HDD + * @rate: tx/rx rate (kbps) + * @mode: 0->11abg legacy, 1->HT, 2->VHT (refer to sir_sme_phy_mode) + * @nss: number of streams + * @mcs: mcs index for HT/VHT mode + * @rate_flags: rate flags for last tx/rx + * + * rate info in HDD + */ +struct hdd_rate_info { + uint32_t rate; + uint8_t mode; + uint8_t nss; + uint8_t mcs; + enum tx_rate_info rate_flags; +}; + +/** + * struct hdd_mic_info - mic error info in HDD + * @ta_mac_addr: transmitter mac address + * @multicast: Flag for multicast + * @key_id: Key ID + * @tsc: Sequence number + * @vdev_id: vdev id + * + */ +struct hdd_mic_error_info { + struct qdf_mac_addr ta_mac_addr; + bool multicast; + uint8_t key_id; + uint8_t tsc[SIR_CIPHER_SEQ_CTR_SIZE]; + uint16_t vdev_id; +}; + +enum hdd_mic_work_status { + MIC_UNINITIALIZED, + MIC_INITIALIZED, + MIC_SCHEDULED, + MIC_DISABLED +}; + +/** + * struct hdd_mic_work - mic work info in HDD + * @mic_error_work: mic error work + * @status: sattus of mic error work + * @info: Pointer to mic error information + * @lock: lock to synchronixe mic error work + * + */ +struct hdd_mic_work { + qdf_work_t work; + enum hdd_mic_work_status status; + struct hdd_mic_error_info *info; + qdf_spinlock_t lock; +}; + +/** + * struct hdd_fw_txrx_stats - fw txrx status in HDD + * (refer to station_info struct in Kernel) + * @tx_packets: packets transmitted to this station + * @tx_bytes: bytes transmitted to this station + * @rx_packets: packets received from this station + * @rx_bytes: bytes received from this station + * @tx_retries: cumulative retry counts + * @tx_failed: the number of failed frames + * @tx_succeed: the number of succeed frames + * @rssi: The signal strength (dbm) + * @peer_rssi_per_chain: the average value of RSSI (dbm) per chain + * @tx_rate: last used tx rate info + * @rx_rate: last used rx rate info + * + * fw txrx status in HDD + */ +struct hdd_fw_txrx_stats { + uint32_t tx_packets; + uint64_t tx_bytes; + uint32_t rx_packets; + uint64_t rx_bytes; + uint32_t tx_retries; + uint32_t tx_failed; + uint32_t tx_succeed; + int8_t rssi; + int32_t peer_rssi_per_chain[WMI_MAX_CHAINS]; + struct hdd_rate_info tx_rate; + struct hdd_rate_info rx_rate; +}; + +/** + * struct hdd_ap_ctx - SAP/P2PGO specific information + * @hostapd_state: state control information + * @dfs_cac_block_tx: Is data tramsmission blocked due to DFS CAC? + * @ap_active: Are any stations active? + * @disable_intrabss_fwd: Prevent forwarding between stations + * @broadcast_sta_id: Station ID assigned after BSS starts + * @privacy: The privacy bits of configuration + * @encryption_type: The encryption being used + * @group_key: Group Encryption Key + * @wep_key: WEP key array + * @wep_def_key_idx: WEP default key index + * @sap_context: Pointer to context maintained by SAP (opaque to HDD) + * @sap_config: SAP configuration + * @operating_chan_freq: channel upon which the SAP is operating + * @beacon: Beacon information + * @vendor_acs_timer: Timer for ACS + * @vendor_acs_timer_initialized: Is @vendor_acs_timer initialized? + * @bss_stop_reason: Reason why the BSS was stopped + * @acs_in_progress: In progress acs flag for an adapter + */ +struct hdd_ap_ctx { + struct hdd_hostapd_state hostapd_state; + bool dfs_cac_block_tx; + bool ap_active; + bool disable_intrabss_fwd; + uint8_t broadcast_sta_id; + uint8_t privacy; + eCsrEncryptionType encryption_type; + tCsrRoamSetKey group_key; + tCsrRoamSetKey wep_key[CSR_MAX_NUM_KEY]; + uint8_t wep_def_key_idx; + struct sap_context *sap_context; + struct sap_config sap_config; + uint32_t operating_chan_freq; + struct hdd_beacon_data *beacon; + qdf_mc_timer_t vendor_acs_timer; + bool vendor_acs_timer_initialized; + enum bss_stop_reason bss_stop_reason; + qdf_atomic_t acs_in_progress; +}; + +/** + * struct hdd_scan_info - Per-adapter scan information + * @scan_add_ie: Additional IE for scan + * @default_scan_ies: Default scan IEs + * @default_scan_ies_len: Length of @default_scan_ies + * @scan_mode: Scan mode + */ +struct hdd_scan_info { + tSirAddie scan_add_ie; + uint8_t *default_scan_ies; + uint16_t default_scan_ies_len; + tSirScanType scan_mode; +}; + +#define WLAN_HDD_MAX_MC_ADDR_LIST CFG_TGT_MAX_MULTICAST_FILTER_ENTRIES + +struct hdd_multicast_addr_list { + uint8_t mc_cnt; + uint8_t addr[WLAN_HDD_MAX_MC_ADDR_LIST][ETH_ALEN]; +}; + +#define WLAN_HDD_MAX_HISTORY_ENTRY 25 + +/** + * struct hdd_netif_queue_stats - netif queue operation statistics + * @pause_count - pause counter + * @unpause_count - unpause counter + */ +struct hdd_netif_queue_stats { + u32 pause_count; + u32 unpause_count; + qdf_time_t total_pause_time; +}; + +/** + * struct hdd_netif_queue_history - netif queue operation history + * @time: timestamp + * @netif_action: action type + * @netif_reason: reason type + * @pause_map: pause map + * @tx_q_state: state of the netdev TX queues + */ +struct hdd_netif_queue_history { + qdf_time_t time; + uint16_t netif_action; + uint16_t netif_reason; + uint32_t pause_map; + unsigned long tx_q_state[NUM_TX_QUEUES]; +}; + +/** + * struct hdd_chan_change_params - channel related information + * @chan_freq: operating channel frequency + * @chan_params: channel parameters + */ +struct hdd_chan_change_params { + uint32_t chan_freq; + struct ch_params chan_params; +}; + +/** + * struct hdd_runtime_pm_context - context to prevent/allow runtime pm + * @dfs: dfs context to prevent/allow runtime pm + * @connect: connect context to prevent/allow runtime pm + * @user: user context to prevent/allow runtime pm + * + * Runtime PM control for underlying activities + */ +struct hdd_runtime_pm_context { + qdf_runtime_lock_t dfs; + qdf_runtime_lock_t connect; + qdf_runtime_lock_t user; + bool is_user_wakelock_acquired; +}; + +/* + * WLAN_HDD_ADAPTER_MAGIC is a magic number used to identify net devices + * belonging to this driver from net devices belonging to other devices. + * Therefore, the magic number must be unique relative to the numbers for + * other drivers in the system. If WLAN_HDD_ADAPTER_MAGIC is already defined + * (e.g. by compiler argument), then use that. If it's not already defined, + * then use the first 4 characters of MULTI_IF_NAME to construct the magic + * number. If MULTI_IF_NAME is not defined, then use a default magic number. + */ +#ifndef WLAN_HDD_ADAPTER_MAGIC +#ifdef MULTI_IF_NAME +#define WLAN_HDD_ADAPTER_MAGIC \ + (MULTI_IF_NAME[0] == 0 ? 0x574c414e : \ + (MULTI_IF_NAME[1] == 0 ? (MULTI_IF_NAME[0] << 24) : \ + (MULTI_IF_NAME[2] == 0 ? (MULTI_IF_NAME[0] << 24) | \ + (MULTI_IF_NAME[1] << 16) : \ + (MULTI_IF_NAME[0] << 24) | (MULTI_IF_NAME[1] << 16) | \ + (MULTI_IF_NAME[2] << 8) | MULTI_IF_NAME[3]))) +#else +#define WLAN_HDD_ADAPTER_MAGIC 0x574c414e /* ASCII "WLAN" */ +#endif +#endif + +/** + * struct rcpi_info - rcpi info + * @rcpi: computed value in dB + * @mac_addr: peer mac addr for which rcpi is computed + */ +struct rcpi_info { + int32_t rcpi; + struct qdf_mac_addr mac_addr; +}; + +struct hdd_context; + +#ifdef WLAN_FEATURE_DYNAMIC_RX_AGGREGATION +/** + * enum qdisc_filter_status - QDISC filter status + * @QDISC_FILTER_RTNL_LOCK_FAIL: rtnl lock acquire failed + * @QDISC_FILTER_PRIO_MATCH: qdisc filter with priority match + * @QDISC_FILTER_PRIO_MISMATCH: no filter match with configured priority + */ +enum qdisc_filter_status { + QDISC_FILTER_RTNL_LOCK_FAIL, + QDISC_FILTER_PRIO_MATCH, + QDISC_FILTER_PRIO_MISMATCH, +}; +#endif + +/** + * struct hdd_adapter - hdd vdev/net_device context + * @vdev: object manager vdev context + * @vdev_lock: lock to protect vdev context access + * @vdev_id: Unique identifier assigned to the vdev + * @event_flags: a bitmap of hdd_adapter_flags + * @mic_work: mic work information + * @gpio_tsf_sync_work: work to sync send TSF CAP WMI command + * @cache_sta_count: number of currently cached stations + * @acs_complete_event: acs complete event + * @latency_level: 0 - normal, 1 - moderate, 2 - low, 3 - ultralow + * @last_disconnect_reason: Last disconnected internal reason code + * as per enum qca_disconnect_reason_codes + * @connect_req_status: Last disconnected internal status code + * as per enum qca_sta_connect_fail_reason_codes + * @upgrade_udp_qos_threshold: The threshold for user priority upgrade for + any UDP packet. + * @handle_feature_update: Handle feature update only if it is triggered + * by hdd_netdev_feature_update + * @netdev_features_update_work: work for handling the netdev features update + for the adapter. + * @gro_disallowed: Flag to check if GRO is enabled or disable for adapter + * @gro_flushed: Flag to indicate if GRO explicit flush is done or not + * @delete_in_progress: Flag to indicate that the adapter delete is in + * progress, and any operation using rtnl lock inside + * the driver can be avoided/skipped. + */ +struct hdd_adapter { + /* Magic cookie for adapter sanity verification. Note that this + * needs to be at the beginning of the private data structure so + * that it will exist at the beginning of dev->priv and hence + * will always be in mapped memory + */ + uint32_t magic; + + /* list node for membership in the adapter list */ + qdf_list_node_t node; + + struct hdd_context *hdd_ctx; + struct wlan_objmgr_vdev *vdev; + qdf_spinlock_t vdev_lock; + uint8_t vdev_id; + + /** Handle to the network device */ + struct net_device *dev; + + enum QDF_OPMODE device_mode; + + /** IPv4 notifier callback for handling ARP offload on change in IP */ + struct work_struct ipv4_notifier_work; +#ifdef WLAN_NS_OFFLOAD + /** IPv6 notifier callback for handling NS offload on change in IP */ + struct work_struct ipv6_notifier_work; +#endif + + /* TODO Move this to sta Ctx */ + struct wireless_dev wdev; + + /** ops checks if Opportunistic Power Save is Enable or Not + * ctw stores CT Window value once we receive Opps command from + * wpa_supplicant then using CT Window value we need to Enable + * Opportunistic Power Save + */ + uint8_t ops; + uint32_t ctw; + + /** Current MAC Address for the adapter */ + struct qdf_mac_addr mac_addr; + +#ifdef WLAN_NUD_TRACKING + struct hdd_nud_tracking_info nud_tracking; +#endif + + struct hdd_mic_work mic_work; + bool disconnection_in_progress; + qdf_mutex_t disconnection_status_lock; + unsigned long event_flags; + + /**Device TX/RX statistics*/ + struct net_device_stats stats; + /** HDD statistics*/ + struct hdd_stats hdd_stats; + + /* estimated link speed */ + uint32_t estimated_linkspeed; + + /** + * vdev_destroy_event is moved from the qdf_event to linux event + * consciously, Lets take example when sap interface is waiting on the + * session_close event and then there is a SSR the wait event is + * completed the interface down is returned and the next command to the + * driver will hdd_hostapd_uinit-->vhdd_deinit_ap_mode--> + * hdd_hostapd_deinit_sap_session where in the sap_ctx would be freed. + * During the SSR if the same sap context is used it would result + * in null pointer de-reference. + */ + struct completion vdev_destroy_event; + + /* QDF event for session open */ + qdf_event_t qdf_session_open_event; + +#ifdef FEATURE_MONITOR_MODE_SUPPORT + /* QDF event for monitor mode vdev up */ + qdf_event_t qdf_monitor_mode_vdev_up_event; +#endif + + /* TODO: move these to sta ctx. These may not be used in AP */ + /** completion variable for disconnect callback */ + struct completion disconnect_comp_var; + + struct completion roaming_comp_var; + + /* completion variable for Linkup Event */ + struct completion linkup_event_var; + + /* completion variable for off channel remain on channel Event */ + struct completion offchannel_tx_event; + /* Completion variable for action frame */ + struct completion tx_action_cnf_event; + + struct completion sta_authorized_event; + + struct completion ibss_peer_info_comp; + + /* Track whether the linkup handling is needed */ + bool is_link_up_service_needed; + + /* WMM Status */ + struct hdd_wmm_status hdd_wmm_status; + + /** Multiple station supports */ + /** Per-station structure */ + + /* TODO: Will be removed as a part of next phase of clean up */ + struct hdd_station_info sta_info[WLAN_MAX_STA_COUNT]; + struct hdd_station_info cache_sta_info[WLAN_MAX_STA_COUNT]; + + /* TODO: _list from name will be removed after clean up */ + struct hdd_sta_info_obj sta_info_list; + struct hdd_sta_info_obj cache_sta_info_list; + qdf_atomic_t cache_sta_count; + +#ifdef FEATURE_WLAN_WAPI + struct hdd_wapi_info wapi_info; +#endif + + int8_t rssi; + int32_t rssi_on_disconnect; +#ifdef WLAN_FEATURE_LPSS + bool rssi_send; +#endif + + uint8_t snr; + + struct work_struct sap_stop_bss_work; + + union { + struct hdd_station_ctx station; + struct hdd_ap_ctx ap; + } session; + + qdf_atomic_t ch_switch_in_progress; + qdf_event_t acs_complete_event; + +#ifdef WLAN_FEATURE_TSF + /* tsf value received from firmware */ + uint64_t cur_target_time; + uint64_t cur_tsf_sync_soc_time; + uint64_t last_tsf_sync_soc_time; + uint64_t cur_target_global_tsf_time; + uint64_t last_target_global_tsf_time; + qdf_mc_timer_t host_capture_req_timer; +#ifdef WLAN_FEATURE_TSF_PLUS + /* spin lock for read/write timestamps */ + qdf_spinlock_t host_target_sync_lock; + qdf_mc_timer_t host_target_sync_timer; + uint64_t cur_host_time; + uint64_t last_host_time; + uint64_t last_target_time; + /* to store the count of continuous invalid tstamp-pair */ + int continuous_error_count; + /* to store the count of continuous capture retry */ + int continuous_cap_retry_count; + /* to indicate whether tsf_sync has been initialized */ + qdf_atomic_t tsf_sync_ready_flag; +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC + qdf_work_t gpio_tsf_sync_work; +#endif +#endif /* WLAN_FEATURE_TSF_PLUS */ +#endif + + struct hdd_multicast_addr_list mc_addr_list; + uint8_t addr_filter_pattern; + + struct hdd_scan_info scan_info; + + /* Flag to ensure PSB is configured through framework */ + uint8_t psb_changed; + /* UAPSD psb value configured through framework */ + uint8_t configured_psb; + /* Use delayed work for Sec AP ACS as Pri AP Startup need to complete + * since CSR (PMAC Struct) Config is same for both AP + */ + struct delayed_work acs_pending_work; + + struct work_struct scan_block_work; + qdf_list_t blocked_scan_request_q; + qdf_mutex_t blocked_scan_request_q_lock; + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH + unsigned long prev_rx_packets; + unsigned long prev_tx_packets; + unsigned long prev_tx_bytes; + uint64_t prev_fwd_tx_packets; + uint64_t prev_fwd_rx_packets; +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || \ + defined(QCA_HL_NETDEV_FLOW_CONTROL) + qdf_mc_timer_t tx_flow_control_timer; + bool tx_flow_timer_initialized; +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL || QCA_HL_NETDEV_FLOW_CONTROL */ +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + unsigned int tx_flow_low_watermark; + unsigned int tx_flow_hi_watermark_offset; +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + + bool offloads_configured; + + /* DSCP to UP QoS Mapping */ + enum sme_qos_wmmuptype dscp_to_up_map[WLAN_MAX_DSCP + 1]; + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + bool is_link_layer_stats_set; +#endif + uint8_t link_status; + uint8_t upgrade_udp_qos_threshold; + + /* variable for temperature in Celsius */ + int temperature; + +#ifdef WLAN_FEATURE_DSRC + /* MAC addresses used for OCB interfaces */ + struct qdf_mac_addr ocb_mac_address[QDF_MAX_CONCURRENCY_PERSONA]; + int ocb_mac_addr_count; +#endif + + /* BITMAP indicating pause reason */ + uint32_t pause_map; + spinlock_t pause_map_lock; + qdf_time_t start_time; + qdf_time_t last_time; + qdf_time_t total_pause_time; + qdf_time_t total_unpause_time; + uint8_t history_index; + struct hdd_netif_queue_history + queue_oper_history[WLAN_HDD_MAX_HISTORY_ENTRY]; + struct hdd_netif_queue_stats queue_oper_stats[WLAN_REASON_TYPE_MAX]; + ol_txrx_tx_fp tx_fn; + /* debugfs entry */ + struct dentry *debugfs_phy; + /* + * The pre cac channel frequency is saved here and will be used when + * the SAP's channel needs to be moved from the existing 2.4GHz channel. + */ + uint32_t pre_cac_freq; + + /* + * Indicate if HO fails during disconnect so that + * disconnect is not initiated by HDD as its already + * initiated by CSR + */ + bool roam_ho_fail; + struct lfr_firmware_status lfr_fw_status; + bool con_status; + bool dad; + uint8_t active_ac; + uint32_t pkt_type_bitmap; + uint32_t track_arp_ip; + uint8_t dns_payload[256]; + uint32_t track_dns_domain_len; + uint32_t track_src_port; + uint32_t track_dest_port; + uint32_t track_dest_ipv4; + uint32_t mon_chan_freq; + uint32_t mon_bandwidth; + uint16_t latency_level; +#ifdef FEATURE_MONITOR_MODE_SUPPORT + bool monitor_mode_vdev_up_in_progress; +#endif + /* rcpi information */ + struct rcpi_info rcpi; + bool send_mode_change; +#ifdef FEATURE_WLAN_APF + struct hdd_apf_context apf_context; +#endif /* FEATURE_WLAN_APF */ + +#ifdef WLAN_DEBUGFS + struct hdd_debugfs_file_info csr_file[HDD_DEBUGFS_FILE_ID_MAX]; +#endif /* WLAN_DEBUGFS */ + +#ifdef WLAN_FEATURE_MOTION_DETECTION + bool motion_detection_mode; + bool motion_det_cfg; + bool motion_det_in_progress; + uint32_t motion_det_baseline_value; +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + enum qca_disconnect_reason_codes last_disconnect_reason; + enum wlan_status_code connect_req_status; + +#ifdef WLAN_FEATURE_PERIODIC_STA_STATS + /* Indicate whether to display sta periodic stats */ + bool is_sta_periodic_stats_enabled; + uint16_t periodic_stats_timer_count; + uint32_t periodic_stats_timer_counter; + qdf_mutex_t sta_periodic_stats_lock; +#endif /* WLAN_FEATURE_PERIODIC_STA_STATS */ + qdf_event_t peer_cleanup_done; +#ifdef FEATURE_OEM_DATA + bool oem_data_in_progress; + void *cookie; + bool response_expected; +#endif + bool handle_feature_update; + + qdf_work_t netdev_features_update_work; + qdf_atomic_t gro_disallowed; + uint8_t gro_flushed[DP_MAX_RX_THREADS]; + bool delete_in_progress; + qdf_atomic_t net_dev_hold_ref_count[NET_DEV_HOLD_ID_MAX]; +}; + +#define WLAN_HDD_GET_STATION_CTX_PTR(adapter) (&(adapter)->session.station) +#define WLAN_HDD_GET_AP_CTX_PTR(adapter) (&(adapter)->session.ap) +#define WLAN_HDD_GET_CTX(adapter) ((adapter)->hdd_ctx) +#define WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter) \ + (&(adapter)->session.ap.hostapd_state) +#define WLAN_HDD_GET_SAP_CTX_PTR(adapter) ((adapter)->session.ap.sap_context) + +#ifdef WLAN_FEATURE_NAN +#define WLAN_HDD_IS_NDP_ENABLED(hdd_ctx) ((hdd_ctx)->nan_datapath_enabled) +#else +/* WLAN_HDD_GET_NDP_CTX_PTR and WLAN_HDD_GET_NDP_WEXT_STATE_PTR are not defined + * intentionally so that all references to these must be within NDP code. + * non-NDP code can call WLAN_HDD_IS_NDP_ENABLED(), and when it is enabled, + * invoke NDP code to do all work. + */ +#define WLAN_HDD_IS_NDP_ENABLED(hdd_ctx) (false) +#endif + +/* Set mac address locally administered bit */ +#define WLAN_HDD_RESET_LOCALLY_ADMINISTERED_BIT(macaddr) (macaddr[0] &= 0xFD) + +#define HDD_DEFAULT_MCC_P2P_QUOTA 70 +#define HDD_RESET_MCC_P2P_QUOTA 50 + +/* + * struct hdd_priv_data - driver ioctl private data payload + * @buf: pointer to command buffer (may be in userspace) + * @used_len: length of the command/data currently in @buf + * @total_len: total length of the @buf memory allocation + */ +struct hdd_priv_data { + uint8_t *buf; + int used_len; + int total_len; +}; + +#define MAX_MOD_LOGLEVEL 10 +struct fw_log_info { + uint8_t enable; + uint8_t dl_type; + uint8_t dl_report; + uint8_t dl_loglevel; + uint8_t index; + uint32_t dl_mod_loglevel[MAX_MOD_LOGLEVEL]; + +}; + +/** + * enum antenna_mode - number of TX/RX chains + * @HDD_ANTENNA_MODE_INVALID: Invalid mode place holder + * @HDD_ANTENNA_MODE_1X1: Number of TX/RX chains equals 1 + * @HDD_ANTENNA_MODE_2X2: Number of TX/RX chains equals 2 + * @HDD_ANTENNA_MODE_MAX: Place holder for max mode + */ +enum antenna_mode { + HDD_ANTENNA_MODE_INVALID, + HDD_ANTENNA_MODE_1X1, + HDD_ANTENNA_MODE_2X2, + HDD_ANTENNA_MODE_MAX +}; + +/** + * enum smps_mode - SM power save mode + * @HDD_SMPS_MODE_STATIC: Static power save + * @HDD_SMPS_MODE_DYNAMIC: Dynamic power save + * @HDD_SMPS_MODE_RESERVED: Reserved + * @HDD_SMPS_MODE_DISABLED: Disable power save + * @HDD_SMPS_MODE_MAX: Place holder for max mode + */ +enum smps_mode { + HDD_SMPS_MODE_STATIC, + HDD_SMPS_MODE_DYNAMIC, + HDD_SMPS_MODE_RESERVED, + HDD_SMPS_MODE_DISABLED, + HDD_SMPS_MODE_MAX +}; + +#ifdef WLAN_FEATURE_OFFLOAD_PACKETS +/** + * struct hdd_offloaded_packets - request id to pattern id mapping + * @request_id: request id + * @pattern_id: pattern id + * + */ +struct hdd_offloaded_packets { + uint32_t request_id; + uint8_t pattern_id; +}; + +/** + * struct hdd_offloaded_packets_ctx - offloaded packets context + * @op_table: request id to pattern id table + * @op_lock: mutex lock + */ +struct hdd_offloaded_packets_ctx { + struct hdd_offloaded_packets op_table[MAXNUM_PERIODIC_TX_PTRNS]; + struct mutex op_lock; +}; +#endif + +/** + * enum driver_status: Driver Modules status + * @DRIVER_MODULES_UNINITIALIZED: Driver CDS modules uninitialized + * @DRIVER_MODULES_ENABLED: Driver CDS modules opened + * @DRIVER_MODULES_CLOSED: Driver CDS modules closed + */ +enum driver_modules_status { + DRIVER_MODULES_UNINITIALIZED, + DRIVER_MODULES_ENABLED, + DRIVER_MODULES_CLOSED +}; + +/** + * struct acs_dfs_policy - Define ACS policies + * @acs_dfs_mode: Dfs mode enabled/disabled. + * @acs_chan_freq: pre defined channel frequency to avoid ACS. + */ +struct acs_dfs_policy { + enum dfs_mode acs_dfs_mode; + uint32_t acs_chan_freq; +}; + +/** + * enum suspend_fail_reason: Reasons a WLAN suspend might fail + * SUSPEND_FAIL_IPA: IPA in progress + * SUSPEND_FAIL_RADAR: radar scan in progress + * SUSPEND_FAIL_ROAM: roaming in progress + * SUSPEND_FAIL_SCAN: scan in progress + * SUSPEND_FAIL_INITIAL_WAKEUP: received initial wakeup from firmware + * SUSPEND_FAIL_MAX_COUNT: the number of wakeup reasons, always at the end + */ +enum suspend_fail_reason { + SUSPEND_FAIL_IPA, + SUSPEND_FAIL_RADAR, + SUSPEND_FAIL_ROAM, + SUSPEND_FAIL_SCAN, + SUSPEND_FAIL_INITIAL_WAKEUP, + SUSPEND_FAIL_MAX_COUNT +}; + +/** + * suspend_resume_stats - Collection of counters for suspend/resume events + * @suspends: number of suspends completed + * @resumes: number of resumes completed + * @suspend_fail: counters for failed suspend reasons + */ +struct suspend_resume_stats { + uint32_t suspends; + uint32_t resumes; + uint32_t suspend_fail[SUSPEND_FAIL_MAX_COUNT]; +}; + +/** + * hdd_sta_smps_param - SMPS parameters to configure from hdd + * HDD_STA_SMPS_PARAM_UPPER_RSSI_THRESH: RSSI threshold to enter Dynamic SMPS + * mode from inactive mode + * HDD_STA_SMPS_PARAM_STALL_RSSI_THRESH: RSSI threshold to enter + * Stalled-D-SMPS mode from D-SMPS mode or to enter D-SMPS mode from + * Stalled-D-SMPS mode + * HDD_STA_SMPS_PARAM_LOWER_RSSI_THRESH: RSSI threshold to disable SMPS modes + * HDD_STA_SMPS_PARAM_UPPER_BRSSI_THRESH: Upper threshold for beacon-RSSI. + * Used to reduce RX chainmask. + * HDD_STA_SMPS_PARAM_LOWER_BRSSI_THRESH: Lower threshold for beacon-RSSI. + * Used to increase RX chainmask. + * HDD_STA_SMPS_PARAM_DTIM_1CHRX_ENABLE: Enable/Disable DTIM 1chRx feature + */ +enum hdd_sta_smps_param { + HDD_STA_SMPS_PARAM_UPPER_RSSI_THRESH = 0, + HDD_STA_SMPS_PARAM_STALL_RSSI_THRESH = 1, + HDD_STA_SMPS_PARAM_LOWER_RSSI_THRESH = 2, + HDD_STA_SMPS_PARAM_UPPER_BRSSI_THRESH = 3, + HDD_STA_SMPS_PARAM_LOWER_BRSSI_THRESH = 4, + HDD_STA_SMPS_PARAM_DTIM_1CHRX_ENABLE = 5 +}; + +/** + * enum RX_OFFLOAD - Receive offload modes + * @CFG_LRO_ENABLED: Large Rx offload + * @CFG_GRO_ENABLED: Generic Rx Offload + * @CFG_DYNAMIC_GRO_ENABLED: Dynamic GRO enabled + */ +enum RX_OFFLOAD { + CFG_LRO_ENABLED = 1, + CFG_GRO_ENABLED, + CFG_DYNAMIC_GRO_ENABLED, +}; + +/* One per STA: 1 for BCMC_STA_ID, 1 for each SAP_SELF_STA_ID, + * 1 for WDS_STAID + */ +#define HDD_MAX_ADAPTERS (WLAN_MAX_STA_COUNT + QDF_MAX_NO_OF_SAP_MODE + 2) + +#ifdef DISABLE_CHANNEL_LIST + +/** + * struct hdd_cache_channel_info - Structure of the channel info + * which needs to be cached + * @channel_num: channel number + * @reg_status: Current regulatory status of the channel + * Enable + * Disable + * DFS + * Invalid + * @wiphy_status: Current wiphy status + */ +struct hdd_cache_channel_info { + uint32_t channel_num; + enum channel_state reg_status; + uint32_t wiphy_status; +}; + +/** + * struct hdd_cache_channels - Structure of the channels to be cached + * @num_channels: Number of channels to be cached + * @channel_info: Structure of the channel info + */ +struct hdd_cache_channels { + uint32_t num_channels; + struct hdd_cache_channel_info *channel_info; +}; +#endif + +/** + * struct hdd_dynamic_mac - hdd structure to handle dynamic mac address changes + * @dynamic_mac: Dynamicaly configured mac, this contains the mac on which + * current interface is up + * @is_provisioned_mac: is this mac from provisioned list + * @bit_position: holds the bit mask position from where this mac is assigned, + * if mac is assigned from provisioned this field contains the position from + * provisioned_intf_addr_mask else contains the position from + * derived_intf_addr_mask + */ +struct hdd_dynamic_mac { + struct qdf_mac_addr dynamic_mac; + bool is_provisioned_mac; + uint8_t bit_position; +}; + +/** + * hdd_fw_ver_info - FW version info structure + * @major_spid: FW version - major spid. + * @minor_spid: FW version - minor spid + * @siid: FW version - siid + * @sub_id: FW version - sub id + * @rel_id: FW version - release id + * @crmid: FW version - crmid + */ + +struct hdd_fw_ver_info { + uint32_t major_spid; + uint32_t minor_spid; + uint32_t siid; + uint32_t sub_id; + uint32_t rel_id; + uint32_t crmid; +}; + +/** + * The logic for get current index of history is dependent on this + * value being power of 2. + */ +#define WLAN_HDD_ADAPTER_OPS_HISTORY_MAX 4 +QDF_COMPILE_TIME_ASSERT(adapter_ops_history_size, + (WLAN_HDD_ADAPTER_OPS_HISTORY_MAX & + (WLAN_HDD_ADAPTER_OPS_HISTORY_MAX - 1)) == 0); + +/** + * enum hdd_adapter_ops_event - events for adapter ops history + * @WLAN_HDD_ADAPTER_OPS_WORK_POST: adapter ops work posted + * @WLAN_HDD_ADAPTER_OPS_WORK_SCHED: adapter ops work scheduled + */ +enum hdd_adapter_ops_event { + WLAN_HDD_ADAPTER_OPS_WORK_POST, + WLAN_HDD_ADAPTER_OPS_WORK_SCHED, +}; + +/** + * struct hdd_adapter_ops_record - record of adapter ops history + * @timestamp: time of the occurence of event + * @event: event + * @vdev_id: vdev id corresponding to the event + */ +struct hdd_adapter_ops_record { + uint64_t timestamp; + enum hdd_adapter_ops_event event; + int vdev_id; +}; + +/** + * struct hdd_adapter_ops_history - history of adapter ops + * @index: index to store the next event + * @entry: array of events + */ +struct hdd_adapter_ops_history { + qdf_atomic_t index; + struct hdd_adapter_ops_record entry[WLAN_HDD_ADAPTER_OPS_HISTORY_MAX]; +}; + +/** + * struct hdd_context - hdd shared driver and psoc/device context + * @psoc: object manager psoc context + * @pdev: object manager pdev context + * @iftype_data_2g: Interface data for 2g band + * @iftype_data_5g: Interface data for 5g band + * @num_latency_critical_clients: Number of latency critical clients connected + * @bus_bw_work: work for periodically computing DDR bus bandwidth requirements + * @g_event_flags: a bitmap of hdd_driver_flags + * @psoc_idle_timeout_work: delayed work for psoc idle shutdown + * @dynamic_nss_chains_support: Per vdev dynamic nss chains update capability + * @sar_cmd_params: SAR command params to be configured to the FW + * @rx_aggregation: rx aggregation enable or disable state + * @gro_force_flush: gro force flushed indication flag + * @tc_based_dyn_gro: TC based dynamic GRO enable/disable flag + * @tc_ingress_prio: TC ingress priority + * @adapter_ops_wq: High priority workqueue for handling adapter operations + * @is_dual_mac_cfg_updated: indicate whether dual mac cfg has been updated + */ +struct hdd_context { + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + mac_handle_t mac_handle; + struct wiphy *wiphy; + qdf_spinlock_t hdd_adapter_lock; + qdf_list_t hdd_adapters; /* List of adapters */ + + /** Pointer for firmware image data */ + const struct firmware *fw; + + /** Pointer for configuration data */ + const struct firmware *cfg; + + /** Pointer to the parent device */ + struct device *parent_dev; + + /** Config values read from qcom_cfg.ini file */ + struct hdd_config *config; + + /* Pointer for wiphy 2G/5G band channels */ + struct ieee80211_channel *channels_2ghz; + struct ieee80211_channel *channels_5ghz; + +#if (defined(CONFIG_BAND_6GHZ) && defined(CFG80211_6GHZ_BAND_SUPPORTED)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + struct ieee80211_sband_iftype_data *iftype_data_2g; + struct ieee80211_sband_iftype_data *iftype_data_5g; +#endif + +#if defined(CONFIG_BAND_6GHZ) && (defined(CFG80211_6GHZ_BAND_SUPPORTED) || \ + (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)) + + struct ieee80211_sband_iftype_data *iftype_data_6g; +#endif + /* Completion variable to indicate Mc Thread Suspended */ + struct completion mc_sus_event_var; + + bool is_scheduler_suspended; + +#ifdef WLAN_FEATURE_PKT_CAPTURE + bool is_ol_mon_thread_suspended; +#endif + +#ifdef QCA_CONFIG_SMP + bool is_ol_rx_thread_suspended; +#endif + + bool hdd_wlan_suspended; + bool suspended; + /* flag to start pktlog after SSR/PDR if previously enabled */ + bool is_pktlog_enabled; + + /* Lock to avoid race condition during start/stop bss */ + struct mutex sap_lock; + +#ifdef FEATURE_OEM_DATA_SUPPORT + /* OEM App registered or not */ + bool oem_app_registered; + + /* OEM App Process ID */ + int32_t oem_pid; +#endif + + qdf_atomic_t num_latency_critical_clients; + /** Concurrency Parameters*/ + uint32_t concurrency_mode; + + uint8_t no_of_open_sessions[QDF_MAX_NO_OF_MODE]; + uint8_t no_of_active_sessions[QDF_MAX_NO_OF_MODE]; + + /** P2P Device MAC Address for the adapter */ + struct qdf_mac_addr p2p_device_address; + + qdf_wake_lock_t rx_wake_lock; + qdf_wake_lock_t sap_wake_lock; + + /* Flag keeps track of wiphy suspend/resume */ + bool is_wiphy_suspended; + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH + struct qdf_periodic_work bus_bw_work; + int cur_vote_level; + qdf_spinlock_t bus_bw_lock; + int cur_rx_level; + uint64_t prev_no_rx_offload_pkts; + uint64_t prev_rx_offload_pkts; + /* Count of non TSO packets in previous bus bw delta time */ + uint64_t prev_no_tx_offload_pkts; + /* Count of TSO packets in previous bus bw delta time */ + uint64_t prev_tx_offload_pkts; + int cur_tx_level; + uint64_t prev_tx; + qdf_atomic_t low_tput_gro_enable; + uint32_t bus_low_vote_cnt; +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + + struct completion ready_to_suspend; + /* defining the solution type */ + uint32_t target_type; + + /* defining the firmware version */ + uint32_t target_fw_version; + uint32_t target_fw_vers_ext; + struct hdd_fw_ver_info fw_version_info; + + /* defining the chip/rom version */ + uint32_t target_hw_version; + /* defining the chip/rom revision */ + uint32_t target_hw_revision; + /* chip/rom name */ + char *target_hw_name; + struct regulatory reg; +#ifdef FEATURE_WLAN_CH_AVOID + uint16_t unsafe_channel_count; + uint16_t unsafe_channel_list[NUM_CHANNELS]; +#endif /* FEATURE_WLAN_CH_AVOID */ + + uint8_t max_intf_count; + uint8_t current_intf_count; +#ifdef WLAN_FEATURE_LPSS + uint8_t lpss_support; +#endif + uint8_t ap_arpns_support; + tSirScanType ioctl_scan_mode; + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + qdf_work_t sta_ap_intf_check_work; +#endif + + uint8_t dev_dfs_cac_status; + + bool bt_coex_mode_set; +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE + qdf_mc_timer_t skip_acs_scan_timer; + uint8_t skip_acs_scan_status; + uint32_t *last_acs_freq_list; + uint8_t num_of_channels; + qdf_spinlock_t acs_skip_lock; +#endif + + qdf_wake_lock_t sap_dfs_wakelock; + atomic_t sap_dfs_ref_cnt; + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + bool is_extwow_app_type1_param_set; + bool is_extwow_app_type2_param_set; +#endif + + /* Time since boot up to extscan start (in micro seconds) */ + uint64_t ext_scan_start_since_boot; + unsigned long g_event_flags; + uint8_t miracast_value; + +#ifdef WLAN_NS_OFFLOAD + /* IPv6 notifier callback for handling NS offload on change in IP */ + struct notifier_block ipv6_notifier; +#endif + bool ns_offload_enable; + /* IPv4 notifier callback for handling ARP offload on change in IP */ + struct notifier_block ipv4_notifier; + +#ifdef FEATURE_RUNTIME_PM + struct notifier_block pm_qos_notifier; + bool runtime_pm_prevented; + qdf_spinlock_t pm_qos_lock; +#endif + /* number of rf chains supported by target */ + uint32_t num_rf_chains; + /* Is htTxSTBC supported by target */ + uint8_t ht_tx_stbc_supported; +#ifdef WLAN_FEATURE_OFFLOAD_PACKETS + struct hdd_offloaded_packets_ctx op_ctx; +#endif + bool mcc_mode; + struct mutex memdump_lock; + uint16_t driver_dump_size; + uint8_t *driver_dump_mem; + + bool connection_in_progress; + qdf_spinlock_t connection_status_lock; + + uint16_t hdd_txrx_hist_idx; + struct hdd_tx_rx_histogram *hdd_txrx_hist; + + /* + * place to store FTM capab of target. This allows changing of FTM capab + * at runtime and intersecting it with target capab before updating. + */ + uint32_t fine_time_meas_cap_target; + uint32_t rx_high_ind_cnt; + /* For Rx thread non GRO/LRO packet accounting */ + uint64_t no_rx_offload_pkt_cnt; + uint64_t no_tx_offload_pkt_cnt; + /* Current number of TX X RX chains being used */ + enum antenna_mode current_antenna_mode; + + /* the radio index assigned by cnss_logger */ + int radio_index; + qdf_work_t sap_pre_cac_work; + bool hbw_requested; + enum RX_OFFLOAD ol_enable; +#ifdef WLAN_FEATURE_NAN + bool nan_datapath_enabled; +#endif + /* Present state of driver cds modules */ + enum driver_modules_status driver_status; + struct qdf_delayed_work psoc_idle_timeout_work; + bool rps; + bool dynamic_rps; + bool enable_rxthread; + /* support for DP RX threads */ + bool enable_dp_rx_threads; + bool napi_enable; + struct acs_dfs_policy acs_policy; + uint16_t wmi_max_len; + struct suspend_resume_stats suspend_resume_stats; + struct hdd_runtime_pm_context runtime_context; + bool roaming_in_progress; + struct scan_chan_info *chan_info; + struct mutex chan_info_lock; + /* bit map to set/reset TDLS by different sources */ + unsigned long tdls_source_bitmap; + bool tdls_umac_comp_active; + bool tdls_nap_active; + uint8_t beacon_probe_rsp_cnt_per_scan; + uint8_t last_scan_reject_vdev_id; + enum scan_reject_states last_scan_reject_reason; + unsigned long last_scan_reject_timestamp; + uint8_t scan_reject_cnt; + bool dfs_cac_offload; + bool reg_offload; + bool rcpi_enabled; +#ifdef FEATURE_WLAN_CH_AVOID + struct ch_avoid_ind_type coex_avoid_freq_list; + struct ch_avoid_ind_type dnbs_avoid_freq_list; + /* Lock to control access to dnbs and coex avoid freq list */ + struct mutex avoid_freq_lock; +#endif +#ifdef WLAN_FEATURE_TSF + /* indicate whether tsf has been initialized */ + qdf_atomic_t tsf_ready_flag; + /* indicate whether it's now capturing tsf(updating tstamp-pair) */ + qdf_atomic_t cap_tsf_flag; + /* the context that is capturing tsf */ + struct hdd_adapter *cap_tsf_context; +#endif +#ifdef WLAN_FEATURE_TSF_PTP + struct ptp_clock_info ptp_cinfo; + struct ptp_clock *ptp_clock; +#endif + uint8_t bt_a2dp_active:1; + uint8_t bt_vo_active:1; + enum band_info curr_band; + bool imps_enabled; +#ifdef WLAN_FEATURE_PACKET_FILTERING + int user_configured_pkt_filter_rules; +#endif + bool is_fils_roaming_supported; + QDF_STATUS (*receive_offload_cb)(struct hdd_adapter *, + struct sk_buff *); + qdf_atomic_t vendor_disable_lro_flag; + + /* disable RX offload (GRO/LRO) in concurrency scenarios */ + qdf_atomic_t disable_rx_ol_in_concurrency; + /* disable RX offload (GRO/LRO) in low throughput scenarios */ + qdf_atomic_t disable_rx_ol_in_low_tput; + bool en_tcp_delack_no_lro; + bool force_rsne_override; + qdf_wake_lock_t monitor_mode_wakelock; + bool lte_coex_ant_share; + bool obss_scan_offload; + int sscan_pid; + uint32_t track_arp_ip; + + /* defining the board related information */ + uint32_t hw_bd_id; + struct board_info hw_bd_info; +#ifdef WLAN_SUPPORT_TWT + enum twt_status twt_state; + qdf_event_t twt_disable_comp_evt; +#endif +#ifdef FEATURE_WLAN_APF + uint32_t apf_version; + bool apf_enabled_v2; +#endif + +#ifdef DISABLE_CHANNEL_LIST + struct hdd_cache_channels *original_channels; + qdf_mutex_t cache_channel_lock; +#endif + enum sar_version sar_version; + struct hdd_dynamic_mac dynamic_mac_list[QDF_MAX_CONCURRENCY_PERSONA]; + bool dynamic_nss_chains_support; + struct qdf_mac_addr hw_macaddr; + struct qdf_mac_addr provisioned_mac_addr[QDF_MAX_CONCURRENCY_PERSONA]; + struct qdf_mac_addr derived_mac_addr[QDF_MAX_CONCURRENCY_PERSONA]; + uint32_t num_provisioned_addr; + uint32_t num_derived_addr; + unsigned long provisioned_intf_addr_mask; + unsigned long derived_intf_addr_mask; + + struct sar_limit_cmd_params *sar_cmd_params; +#ifdef SAR_SAFETY_FEATURE + qdf_mc_timer_t sar_safety_timer; + qdf_mc_timer_t sar_safety_unsolicited_timer; + qdf_event_t sar_safety_req_resp_event; + qdf_atomic_t sar_safety_req_resp_event_in_progress; +#endif + + qdf_time_t runtime_resume_start_time_stamp; + qdf_time_t runtime_suspend_done_time_stamp; +#if defined(CLD_PM_QOS) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + struct pm_qos_request pm_qos_req; +#endif +#ifdef WLAN_FEATURE_PKT_CAPTURE + /* enable packet capture support */ + bool enable_pkt_capture_support; + /* value for packet capturte mode */ + uint8_t val_pkt_capture_mode; +#endif + bool roam_ch_from_fw_supported; + struct { + qdf_atomic_t rx_aggregation; + uint8_t gro_force_flush[DP_MAX_RX_THREADS]; + bool tc_based_dyn_gro; + uint32_t tc_ingress_prio; + } dp_agg_param; +#ifdef FW_THERMAL_THROTTLE_SUPPORT + uint8_t dutycycle_off_percent; +#endif + + qdf_workqueue_t *adapter_ops_wq; + struct hdd_adapter_ops_history adapter_ops_history; + bool is_dual_mac_cfg_updated; +}; + +/** + * struct hdd_vendor_acs_chan_params - vendor acs channel parameters + * @pcl_count: pcl list count + * @vendor_pcl_list: pointer to pcl frequency (MHz) list + * @vendor_weight_list: pointer to pcl weight list + */ +struct hdd_vendor_acs_chan_params { + uint32_t pcl_count; + uint32_t *vendor_pcl_list; + uint8_t *vendor_weight_list; +}; + +/** + * struct hdd_external_acs_timer_context - acs timer context + * @reason: reason for acs trigger + * @adapter: hdd adapter for acs + */ +struct hdd_external_acs_timer_context { + int8_t reason; + struct hdd_adapter *adapter; +}; + +/** + * struct hdd_vendor_chan_info - vendor channel info + * @band: channel operating band + * @pri_chan_freq: primary channel freq in MHz + * @ht_sec_chan_freq: secondary channel freq in MHz + * @vht_seg0_center_chan_freq: segment0 for vht in MHz + * @vht_seg1_center_chan_freq: vht segment 1 in MHz + * @chan_width: channel width + */ +struct hdd_vendor_chan_info { + uint8_t band; + uint32_t pri_chan_freq; + uint32_t ht_sec_chan_freq; + uint32_t vht_seg0_center_chan_freq; + uint32_t vht_seg1_center_chan_freq; + uint8_t chan_width; +}; + +/** + * struct hdd_channel_info - standard channel info + * @freq: Freq in Mhz + * @flags: channel info flags + * @flagext: extended channel info flags + * @ieee_chan_number: channel number + * @max_reg_power: max tx power according to regulatory + * @max_radio_power: max radio power + * @min_radio_power: min radio power + * @reg_class_id: regulatory class + * @max_antenna_gain: max antenna gain allowed on channel + * @vht_center_freq_seg0: vht center freq segment 0 + * @vht_center_freq_seg1: vht center freq segment 1 + */ +struct hdd_channel_info { + u_int16_t freq; + u_int32_t flags; + u_int16_t flagext; + u_int8_t ieee_chan_number; + int8_t max_reg_power; + int8_t max_radio_power; + int8_t min_radio_power; + u_int8_t reg_class_id; + u_int8_t max_antenna_gain; + u_int8_t vht_center_freq_seg0; + u_int8_t vht_center_freq_seg1; +}; + +/* + * Function declarations and documentation + */ + +/** + * wlan_hdd_history_get_next_index() - get next index to store the history + entry + * @curr_idx: current index + * @max_entries: max entries in the history + * + * Returns: The index at which record is to be stored in history + */ +static inline uint32_t wlan_hdd_history_get_next_index(qdf_atomic_t *curr_idx, + uint32_t max_entries) +{ + uint32_t idx = qdf_atomic_inc_return(curr_idx); + + return idx & (max_entries - 1); +} + +/** + * hdd_adapter_ops_record_event() - record an entry in the adapter ops history + * @hdd_ctx: pointer to hdd context + * @event: event + * @vdev_id: vdev id corresponding to event + * + * Returns: None + */ +static inline void +hdd_adapter_ops_record_event(struct hdd_context *hdd_ctx, + enum hdd_adapter_ops_event event, + int vdev_id) +{ + struct hdd_adapter_ops_history *adapter_hist; + struct hdd_adapter_ops_record *record; + uint32_t idx; + + adapter_hist = &hdd_ctx->adapter_ops_history; + + idx = wlan_hdd_history_get_next_index(&adapter_hist->index, + WLAN_HDD_ADAPTER_OPS_HISTORY_MAX); + + record = &adapter_hist->entry[idx]; + record->event = event; + record->vdev_id = vdev_id; + record->timestamp = qdf_get_log_timestamp(); +} + +/** + * hdd_validate_channel_and_bandwidth() - Validate the channel-bandwidth combo + * @adapter: HDD adapter + * @chan_freq: Channel frequency + * @chan_bw: Bandwidth + * + * Checks if the given bandwidth is valid for the given channel number. + * + * Return: 0 for success, non-zero for failure + */ +int hdd_validate_channel_and_bandwidth(struct hdd_adapter *adapter, + qdf_freq_t chan_freq, + enum phy_ch_width chan_bw); + +/** + * hdd_get_front_adapter() - Get the first adapter from the adapter list + * @hdd_ctx: pointer to the HDD context + * @current_adapter: pointer to the current adapter + * @out_adapter: double pointer to pass the next adapter + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_get_front_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter **out_adapter); + +/** + * hdd_get_next_adapter() - Get the next adapter from the adapter list + * @hdd_ctx: pointer to the HDD context + * @current_adapter: pointer to the current adapter + * @out_adapter: double pointer to pass the next adapter + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_get_next_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *current_adapter, + struct hdd_adapter **out_adapter); + +/** + * hdd_get_front_adapter_no_lock() - Get the first adapter from the adapter list + * This API doesnot use any lock in it's implementation. It is the caller's + * directive to ensure concurrency safety. + * @hdd_ctx: pointer to the HDD context + * @out_adapter: double pointer to pass the next adapter + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_get_front_adapter_no_lock(struct hdd_context *hdd_ctx, + struct hdd_adapter **out_adapter); + +/** + * hdd_get_next_adapter_no_lock() - Get the next adapter from the adapter list + * This API doesnot use any lock in it's implementation. It is the caller's + * directive to ensure concurrency safety. + * @hdd_ctx: pointer to the HDD context + * @current_adapter: pointer to the current adapter + * @out_adapter: double pointer to pass the next adapter + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_get_next_adapter_no_lock(struct hdd_context *hdd_ctx, + struct hdd_adapter *current_adapter, + struct hdd_adapter **out_adapter); + +/** + * hdd_remove_adapter() - Remove the adapter from the adapter list + * @hdd_ctx: pointer to the HDD context + * @adapter: pointer to the adapter to be removed + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_remove_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter); + +/** + * hdd_remove_front_adapter() - Remove the first adapter from the adapter list + * @hdd_ctx: pointer to the HDD context + * @out_adapter: pointer to the adapter to be removed + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_remove_front_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter **out_adapter); + +/** + * hdd_add_adapter_back() - Add an adapter to the adapter list + * @hdd_ctx: pointer to the HDD context + * @adapter: pointer to the adapter to be added + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_add_adapter_back(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter); + +/** + * hdd_add_adapter_front() - Add an adapter to the head of the adapter list + * @hdd_ctx: pointer to the HDD context + * @adapter: pointer to the adapter to be added + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_add_adapter_front(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter); + +/** + * typedef hdd_adapter_iterate_cb() – Iteration callback function + * @adapter: current adapter of interest + * @context: user context supplied to the iterator + * + * This specifies the type of a callback function to supply to + * hdd_adapter_iterate(). + * + * Return: + * * QDF_STATUS_SUCCESS if further iteration should continue + * * QDF_STATUS_E_ABORTED if further iteration should be aborted + */ +typedef QDF_STATUS (*hdd_adapter_iterate_cb)(struct hdd_adapter *adapter, + void *context); + +/** + * hdd_adapter_iterate() – Safely iterate over all adapters + * @cb: callback function to invoke for each adapter + * @context: user-supplied context to pass to @cb + * + * This function will iterate over all of the adapters known to the system in + * a safe manner, invoking the callback function for each adapter. + * The callback function will be invoked in the same context/thread as the + * caller without any additional locks in force. + * Iteration continues until either the callback has been invoked for all + * adapters or a callback returns a value of QDF_STATUS_E_ABORTED to indicate + * that further iteration should cease. + * + * Return: + * * QDF_STATUS_E_ABORTED if any callback function returns that value + * * QDF_STATUS_E_FAILURE if the callback was not invoked for all adapters due + * to concurrency (i.e. adapter was deleted while iterating) + * * QDF_STATUS_SUCCESS if @cb was invoked for each adapter and none returned + * an error + */ +QDF_STATUS hdd_adapter_iterate(hdd_adapter_iterate_cb cb, + void *context); + +/** + * hdd_adapter_dev_hold_debug - Debug API to call dev_hold + * @adapter: hdd_adapter pointer + * @dbgid: Debug ID corresponding to API that is requesting the dev_hold + * + * Return: none + */ +void hdd_adapter_dev_hold_debug(struct hdd_adapter *adapter, + wlan_net_dev_ref_dbgid dbgid); + +/** + * hdd_adapter_dev_put_debug - Debug API to call dev_put + * @adapter: hdd_adapter pointer + * @dbgid: Debug ID corresponding to API that is requesting the dev_put + * + * Return: none + */ +void hdd_adapter_dev_put_debug(struct hdd_adapter *adapter, + wlan_net_dev_ref_dbgid dbgid); + +/** + * __hdd_take_ref_and_fetch_front_adapter_safe - Helper macro to lock, fetch + * front and next adapters, take ref and unlock. + * @hdd_ctx: the global HDD context + * @adapter: an hdd_adapter pointer to use as a cursor + * @next_adapter: hdd_adapter pointer to next adapter + * @dbgid: debug ID to detect reference leaks + */ +#define __hdd_take_ref_and_fetch_front_adapter_safe(hdd_ctx, adapter, \ + next_adapter, dbgid) \ + qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock), \ + hdd_get_front_adapter_no_lock(hdd_ctx, &adapter), \ + (adapter) ? hdd_adapter_dev_hold_debug(adapter, dbgid) : (false), \ + hdd_get_next_adapter_no_lock(hdd_ctx, adapter, &next_adapter), \ + (next_adapter) ? hdd_adapter_dev_hold_debug(next_adapter, dbgid) : \ + (false), \ + qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock) + +/** + * __hdd_take_ref_and_fetch_next_adapter_safe - Helper macro to lock, fetch next + * adapter, take ref and unlock. + * @hdd_ctx: the global HDD context + * @adapter: hdd_adapter pointer to use as a cursor + * @next_adapter: hdd_adapter pointer to next adapter + * @dbgid: debug ID to detect reference leaks + */ +#define __hdd_take_ref_and_fetch_next_adapter_safe(hdd_ctx, adapter, \ + next_adapter, dbgid) \ + qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock), \ + adapter = next_adapter, \ + hdd_get_next_adapter_no_lock(hdd_ctx, adapter, &next_adapter), \ + (next_adapter) ? hdd_adapter_dev_hold_debug(next_adapter, dbgid) : \ + (false), \ + qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock) + +/** + * __hdd_is_adapter_valid - Helper macro to return true/false for valid adapter. + * @adapter: an hdd_adapter pointer to use as a cursor + */ +#define __hdd_is_adapter_valid(_adapter) !!_adapter + +/** + * hdd_for_each_adapter_dev_held_safe - Adapter iterator with dev_hold called + * in a delete safe manner + * @hdd_ctx: the global HDD context + * @adapter: an hdd_adapter pointer to use as a cursor + * @next_adapter: hdd_adapter pointer to the next adapter + * + * This iterator will take the reference of the netdev associated with the + * given adapter so as to prevent it from being removed in other context. It + * also takes the reference of the next adapter if exist. This avoids infinite + * loop due to deletion of the adapter list entry inside the loop. Deletion of + * list entry will make the list entry to point to self. If the control goes + * inside the loop body then the dev_hold has been invoked. + * + * ***** NOTE ***** + * Before the end of each iteration, hdd_adapter_dev_put_debug(adapter, dbgid) + * must be called. Not calling this will keep hold of a reference, thus + * preventing unregister of the netdevice. If the loop is terminated in + * between with return/goto/break statements, + * hdd_adapter_dev_put_debug(next_adapter, dbgid) must be done along with + * hdd_adapter_dev_put_debug(adapter, dbgid) before termination of the loop. + * + * Usage example: + * hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, dbgid) { + * + * + * hdd_adapter_dev_put_debug(adapter, dbgid) + * } + */ +#define hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, \ + dbgid) \ + for (__hdd_take_ref_and_fetch_front_adapter_safe(hdd_ctx, adapter, \ + next_adapter, dbgid); \ + __hdd_is_adapter_valid(adapter); \ + __hdd_take_ref_and_fetch_next_adapter_safe(hdd_ctx, adapter, \ + next_adapter, dbgid)) + +/** + * wlan_hdd_get_adapter_by_vdev_id_from_objmgr() - Fetch adapter from objmgr + * using vdev_id. + * @hdd_ctx: the global HDD context + * @adapter: an hdd_adapter double pointer to store the address of the adapter + * @vdev: the vdev whose corresponding adapter has to be fetched + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_hdd_get_adapter_by_vdev_id_from_objmgr(struct hdd_context *hdd_ctx, + struct hdd_adapter **adapter, + struct wlan_objmgr_vdev *vdev); + +struct hdd_adapter *hdd_open_adapter(struct hdd_context *hdd_ctx, + uint8_t session_type, + const char *name, tSirMacAddr mac_addr, + unsigned char name_assign_type, + bool rtnl_held); + +/** + * hdd_close_adapter() - remove and free @adapter from the adapter list + * @hdd_ctx: The Hdd context containing the adapter list + * @adapter: the adapter to remove and free + * @rtnl_held: if the caller is already holding the RTNL lock + * + * Return: None + */ +void hdd_close_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + bool rtnl_held); + +/** + * hdd_close_all_adapters() - remove and free all adapters from the adapter list + * @hdd_ctx: The Hdd context containing the adapter list + * @rtnl_held: if the caller is already holding the RTNL lock + * + * Return: None + */ +void hdd_close_all_adapters(struct hdd_context *hdd_ctx, bool rtnl_held); + +QDF_STATUS hdd_stop_all_adapters(struct hdd_context *hdd_ctx); +void hdd_deinit_all_adapters(struct hdd_context *hdd_ctx, bool rtnl_held); +QDF_STATUS hdd_reset_all_adapters(struct hdd_context *hdd_ctx); +QDF_STATUS hdd_start_all_adapters(struct hdd_context *hdd_ctx); + +/** + * hdd_get_adapter_by_vdev() - Return adapter with the given vdev id + * @hdd_ctx: hdd context. + * @vdev_id: vdev id for the adapter to get. + * + * This function is used to get the adapter with provided vdev id + * + * Return: adapter pointer if found + * + */ +struct hdd_adapter *hdd_get_adapter_by_vdev(struct hdd_context *hdd_ctx, + uint32_t vdev_id); + +/** + * hdd_adapter_get_by_reference() - Return adapter with the given reference + * @hdd_ctx: hdd context + * @reference: reference for the adapter to get + * + * This function is used to get the adapter with provided reference. + * The adapter reference will be held until being released by calling + * hdd_adapter_put(). + * + * Return: adapter pointer if found + * + */ +struct hdd_adapter *hdd_adapter_get_by_reference(struct hdd_context *hdd_ctx, + struct hdd_adapter *reference); + +/** + * hdd_adapter_put() - Release reference to adapter + * @adapter: adapter reference + * + * Release reference to adapter previously acquired via + * hdd_adapter_get_*() function + */ +void hdd_adapter_put(struct hdd_adapter *adapter); + +struct hdd_adapter *hdd_get_adapter_by_macaddr(struct hdd_context *hdd_ctx, + tSirMacAddr mac_addr); + +/** + * hdd_get_adapter_home_channel() - return home channel of adapter + * @adapter: hdd adapter of vdev + * + * This function returns operation channel of station/p2p-cli if + * connected, returns opration channel of sap/p2p-go if started. + * + * Return: home channel if connected/started or invalid channel 0 + */ +uint32_t hdd_get_adapter_home_channel(struct hdd_adapter *adapter); + +/* + * hdd_get_adapter_by_rand_macaddr() - find Random mac adapter + * @hdd_ctx: hdd context + * @mac_addr: random mac addr + * + * Find the Adapter based on random mac addr. Adapter's vdev + * have active random mac list. + * + * Return: adapter ptr or null + */ +struct hdd_adapter * +hdd_get_adapter_by_rand_macaddr(struct hdd_context *hdd_ctx, + tSirMacAddr mac_addr); + +/** + * hdd_is_vdev_in_conn_state() - Check whether the vdev is in + * connected/started state. + * @adapter: hdd adapter of the vdev + * + * This function will give whether the vdev in the adapter is in + * connected/started state. + * + * Return: True/false + */ +bool hdd_is_vdev_in_conn_state(struct hdd_adapter *adapter); + +/** + * hdd_vdev_create() - Create the vdev in the firmware + * @adapter: hdd adapter + * + * This function will create the vdev in the firmware + * + * Return: 0 when the vdev create is sent to firmware or -EINVAL when + * there is a failure to send the command. + */ +int hdd_vdev_create(struct hdd_adapter *adapter); +int hdd_vdev_destroy(struct hdd_adapter *adapter); +int hdd_vdev_ready(struct hdd_adapter *adapter); + +QDF_STATUS hdd_init_station_mode(struct hdd_adapter *adapter); +struct hdd_adapter *hdd_get_adapter(struct hdd_context *hdd_ctx, + enum QDF_OPMODE mode); + +/** + * hdd_get_device_mode() - Get device mode + * @vdev_id: vdev id + * + * Return: Device mode + */ +enum QDF_OPMODE hdd_get_device_mode(uint32_t vdev_id); + +void hdd_deinit_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + bool rtnl_held); +QDF_STATUS hdd_stop_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter); + +void hdd_set_station_ops(struct net_device *dev); + +/** + * wlan_hdd_get_intf_addr() - Get address for the interface + * @hdd_ctx: Pointer to hdd context + * @interface_type: type of the interface for which address is queried + * + * This function is used to get mac address for every new interface + * + * Return: If addr is present then return pointer to MAC address + * else NULL + */ + +uint8_t *wlan_hdd_get_intf_addr(struct hdd_context *hdd_ctx, + enum QDF_OPMODE interface_type); +void wlan_hdd_release_intf_addr(struct hdd_context *hdd_ctx, + uint8_t *releaseAddr); + +/** + * hdd_get_operating_chan_freq() - return operating channel of the device mode + * @hdd_ctx: Pointer to the HDD context. + * @mode: Device mode for which operating channel is required. + * Supported modes: + * QDF_STA_MODE, + * QDF_P2P_CLIENT_MODE, + * QDF_SAP_MODE, + * QDF_P2P_GO_MODE. + * + * This API returns the operating channel of the requested device mode + * + * Return: channel frequency, or + * 0 if the requested device mode is not found. + */ +uint32_t hdd_get_operating_chan_freq(struct hdd_context *hdd_ctx, + enum QDF_OPMODE mode); + +void hdd_set_conparam(int32_t con_param); +enum QDF_GLOBAL_MODE hdd_get_conparam(void); +void wlan_hdd_reset_prob_rspies(struct hdd_adapter *adapter); +void hdd_prevent_suspend(uint32_t reason); + +/* + * hdd_get_first_valid_adapter() - Get the first valid adapter from adapter list + * + * This function is used to fetch the first valid adapter from the adapter + * list. If there is no valid adapter then it returns NULL + * + * @hdd_ctx: HDD context handler + * + * Return: NULL if no valid adapter found in the adapter list + * + */ +struct hdd_adapter *hdd_get_first_valid_adapter(struct hdd_context *hdd_ctx); + +void hdd_allow_suspend(uint32_t reason); +void hdd_prevent_suspend_timeout(uint32_t timeout, uint32_t reason); + +#ifdef QCA_IBSS_SUPPORT +/** + * hdd_set_ibss_power_save_params() - update IBSS Power Save params to WMA. + * @struct hdd_adapter Hdd adapter. + * + * This function sets the IBSS power save config parameters to WMA + * which will send it to firmware if FW supports IBSS power save + * before vdev start. + * + * Return: QDF_STATUS QDF_STATUS_SUCCESS on Success and QDF_STATUS_E_FAILURE + * on failure. + */ +QDF_STATUS hdd_set_ibss_power_save_params(struct hdd_adapter *adapter); +#else +static inline QDF_STATUS +hdd_set_ibss_power_save_params(struct hdd_adapter *adapter) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * wlan_hdd_validate_context() - check the HDD context + * @hdd_ctx: Global HDD context pointer + * + * Return: 0 if the context is valid. Error code otherwise + */ +#define wlan_hdd_validate_context(hdd_ctx) \ + __wlan_hdd_validate_context(hdd_ctx, __func__) + +int __wlan_hdd_validate_context(struct hdd_context *hdd_ctx, const char *func); + +/** + * hdd_validate_adapter() - Validate the given adapter + * @adapter: the adapter to validate + * + * This function validates the given adapter, and ensures that it is open. + * + * Return: Errno + */ +#define hdd_validate_adapter(adapter) \ + __hdd_validate_adapter(adapter, __func__) + +int __hdd_validate_adapter(struct hdd_adapter *adapter, const char *func); + +/** + * wlan_hdd_validate_vdev_id() - ensure the given vdev Id is valid + * @vdev_id: the vdev Id to validate + * + * Return: Errno + */ +#define wlan_hdd_validate_vdev_id(vdev_id) \ + __wlan_hdd_validate_vdev_id(vdev_id, __func__) + +int __wlan_hdd_validate_vdev_id(uint8_t vdev_id, const char *func); + +/** + * hdd_is_valid_mac_address() - validate MAC address + * @mac_addr: Pointer to the input MAC address + * + * This function validates whether the given MAC address is valid or not + * Expected MAC address is of the format XX:XX:XX:XX:XX:XX + * where X is the hexa decimal digit character and separated by ':' + * This algorithm works even if MAC address is not separated by ':' + * + * This code checks given input string mac contains exactly 12 hexadecimal + * digits and a separator colon : appears in the input string only after + * an even number of hex digits. + * + * Return: true for valid and false for invalid + */ +bool hdd_is_valid_mac_address(const uint8_t *mac_addr); + +bool wlan_hdd_validate_modules_state(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_validate_mac_address() - Function to validate mac address + * @mac_addr: input mac address + * + * Return QDF_STATUS + */ +#define wlan_hdd_validate_mac_address(mac_addr) \ + __wlan_hdd_validate_mac_address(mac_addr, __func__) + +QDF_STATUS __wlan_hdd_validate_mac_address(struct qdf_mac_addr *mac_addr, + const char *func); + +/** + * hdd_is_any_adapter_connected() - Check if any adapter is in connected state + * @hdd_ctx: the global hdd context + * + * Returns: true, if any of the adapters is in connected state, + * false, if none of the adapters is in connected state. + */ +bool hdd_is_any_adapter_connected(struct hdd_context *hdd_ctx); + +/** + * hdd_add_latency_critical_client() - Add latency critical client + * @hdd_ctx: Global HDD context + * @phymode: the phymode of the connected adapter + * + * This function adds to the latency critical count if the present + * connection is also a latency critical one. + * + * Returns: None + */ +static inline void +hdd_add_latency_critical_client(struct hdd_context *hdd_ctx, + enum qca_wlan_802_11_mode phymode) +{ + switch (phymode) { + case QCA_WLAN_802_11_MODE_11A: + case QCA_WLAN_802_11_MODE_11G: + qdf_atomic_inc(&hdd_ctx->num_latency_critical_clients); + break; + default: + break; + } +} + +/** + * hdd_del_latency_critical_client() - Add tlatency critical client + * @hdd_ctx: Global HDD context + * @phymode: the phymode of the connected adapter + * + * This function removes from the latency critical count if the present + * connection is also a latency critical one. + * + * Returns: None + */ +static inline void +hdd_del_latency_critical_client(struct hdd_context *hdd_ctx, + enum qca_wlan_802_11_mode phymode) +{ + switch (phymode) { + case QCA_WLAN_802_11_MODE_11A: + case QCA_WLAN_802_11_MODE_11G: + qdf_atomic_dec(&hdd_ctx->num_latency_critical_clients); + break; + default: + break; + } +} + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +/** + * hdd_bus_bw_compute_prev_txrx_stats() - get tx and rx stats + * @adapter: hdd adapter reference + * + * This function get the collected tx and rx stats before starting + * the bus bandwidth timer. + * + * Return: None + */ +void hdd_bus_bw_compute_prev_txrx_stats(struct hdd_adapter *adapter); + +/** + * hdd_bus_bw_compute_reset_prev_txrx_stats() - reset previous tx and rx stats + * @adapter: hdd adapter reference + * + * This function resets the adapter previous tx rx stats. + * + * Return: None + */ +void hdd_bus_bw_compute_reset_prev_txrx_stats(struct hdd_adapter *adapter); + +/** + * hdd_bus_bw_compute_timer_start() - start the bandwidth timer + * @hdd_ctx: the global hdd context + * + * Return: None + */ +void hdd_bus_bw_compute_timer_start(struct hdd_context *hdd_ctx); + +/** + * hdd_bus_bw_compute_timer_try_start() - try to start the bandwidth timer + * @hdd_ctx: the global hdd context + * + * This function ensures there is at least one adapter in the associated state + * before starting the bandwidth timer. + * + * Return: None + */ +void hdd_bus_bw_compute_timer_try_start(struct hdd_context *hdd_ctx); + +/** + * hdd_bus_bw_compute_timer_stop() - stop the bandwidth timer + * @hdd_ctx: the global hdd context + * + * Return: None + */ +void hdd_bus_bw_compute_timer_stop(struct hdd_context *hdd_ctx); + +/** + * hdd_bus_bw_compute_timer_try_stop() - try to stop the bandwidth timer + * @hdd_ctx: the global hdd context + * + * This function ensures there are no adapters in the associated state before + * stopping the bandwidth timer. + * + * Return: None + */ +void hdd_bus_bw_compute_timer_try_stop(struct hdd_context *hdd_ctx); + +/** + * hdd_bus_bandwidth_init() - Initialize bus bandwidth data structures. + * @hdd_ctx: HDD context + * + * Initialize bus bandwidth related data structures like spinlock and timer. + * + * Return: None. + */ +int hdd_bus_bandwidth_init(struct hdd_context *hdd_ctx); + +/** + * hdd_bus_bandwidth_deinit() - De-initialize bus bandwidth data structures. + * @hdd_ctx: HDD context + * + * De-initialize bus bandwidth related data structures like timer. + * + * Return: None. + */ +void hdd_bus_bandwidth_deinit(struct hdd_context *hdd_ctx); + +static inline enum pld_bus_width_type +hdd_get_current_throughput_level(struct hdd_context *hdd_ctx) +{ + return hdd_ctx->cur_vote_level; +} + +#ifdef DP_MEM_PRE_ALLOC +static inline +void *hdd_get_prealloc_dma_mem_unaligned(size_t size, + qdf_dma_addr_t *paddr, + uint32_t ring_type) +{ + return dp_prealloc_get_consistent_mem_unaligned(size, paddr, + ring_type); +} + +static inline +void hdd_put_prealloc_dma_mem_unaligned(void *vaddr) +{ + dp_prealloc_put_consistent_mem_unaligned(vaddr); +} +#endif + +/** + * hdd_set_current_throughput_level() - update the current vote + * level + * @hdd_ctx: the global hdd context + * @next_vote_level: pld_bus_width_type voting level + * + * This function updates the current vote level to the new level + * provided + * + * Return: None + */ +static inline void +hdd_set_current_throughput_level(struct hdd_context *hdd_ctx, + enum pld_bus_width_type next_vote_level) +{ + hdd_ctx->cur_vote_level = next_vote_level; +} + +static inline bool +hdd_is_low_tput_gro_enable(struct hdd_context *hdd_ctx) +{ + return (qdf_atomic_read(&hdd_ctx->low_tput_gro_enable)) ? true : false; +} + +#define GET_CUR_RX_LVL(config) ((config)->cur_rx_level) +#define GET_BW_COMPUTE_INTV(config) ((config)->bus_bw_compute_interval) +#else + +static inline +void hdd_bus_bw_compute_prev_txrx_stats(struct hdd_adapter *adapter) +{ +} + +static inline +void hdd_bus_bw_compute_reset_prev_txrx_stats(struct hdd_adapter *adapter) +{ +} + +static inline +void hdd_bus_bw_compute_timer_start(struct hdd_context *hdd_ctx) +{ +} + +static inline +void hdd_bus_bw_compute_timer_try_start(struct hdd_context *hdd_ctx) +{ +} + +static inline +void hdd_bus_bw_compute_timer_stop(struct hdd_context *hdd_ctx) +{ +} + +static inline +void hdd_bus_bw_compute_timer_try_stop(struct hdd_context *hdd_ctx) +{ +} + +static inline +int hdd_bus_bandwidth_init(struct hdd_context *hdd_ctx) +{ + return 0; +} + +static inline +void hdd_bus_bandwidth_deinit(struct hdd_context *hdd_ctx) +{ +} + +static inline enum pld_bus_width_type +hdd_get_current_throughput_level(struct hdd_context *hdd_ctx) +{ + return PLD_BUS_WIDTH_NONE; +} + +static inline void +hdd_set_current_throughput_level(struct hdd_context *hdd_ctx, + enum pld_bus_width_type next_vote_level) +{ +} + +static inline bool +hdd_is_low_tput_gro_enable(struct hdd_context *hdd_ctx) +{ + return false; +} + +#define GET_CUR_RX_LVL(config) 0 +#define GET_BW_COMPUTE_INTV(config) 0 + +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + +/** + * hdd_init_adapter_ops_wq() - Init global workqueue for adapter operations. + * @hdd_ctx: pointer to HDD context + * + * Return: QDF_STATUS_SUCCESS if workqueue is allocated, + * QDF_STATUS_E_NOMEM if workqueue aloocation fails. + */ +QDF_STATUS hdd_init_adapter_ops_wq(struct hdd_context *hdd_ctx); + +/** + * hdd_deinit_adapter_ops_wq() - Deinit global workqueue for adapter operations. + * @hdd_ctx: pointer to HDD context + * + * Return: None + */ +void hdd_deinit_adapter_ops_wq(struct hdd_context *hdd_ctx); + +/** + * hdd_adapter_feature_update_work_init() - Init per adapter work for netdev + * feature update + * @adapter: pointer to adapter structure + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_adapter_feature_update_work_init(struct hdd_adapter *adapter); + +/** + * hdd_adapter_feature_update_work_deinit() - Deinit per adapter work for + * netdev feature update + * @adapter: pointer to adapter structure + * + * Return: QDF_STATUS + */ +void hdd_adapter_feature_update_work_deinit(struct hdd_adapter *adapter); + +int hdd_qdf_trace_enable(QDF_MODULE_ID module_id, uint32_t bitmask); + +int hdd_init(void); +void hdd_deinit(void); + +/** + * hdd_wlan_startup() - HDD init function + * hdd_ctx: the HDD context corresponding to the psoc to startup + * + * Return: Errno + */ +int hdd_wlan_startup(struct hdd_context *hdd_ctx); + +/** + * hdd_wlan_exit() - HDD WLAN exit function + * @hdd_ctx: pointer to the HDD Context + * + * Return: None + */ +void hdd_wlan_exit(struct hdd_context *hdd_ctx); + +/** + * hdd_psoc_create_vdevs() - create the default vdevs for a psoc + * @hdd_ctx: the HDD context for the psoc to operate against + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_psoc_create_vdevs(struct hdd_context *hdd_ctx); + +/* + * hdd_context_create() - Allocate and inialize HDD context. + * @dev: Device Pointer to the underlying device + * + * Allocate and initialize HDD context. HDD context is allocated as part of + * wiphy allocation and then context is initialized. + * + * Return: HDD context on success and ERR_PTR on failure + */ +struct hdd_context *hdd_context_create(struct device *dev); + +/** + * hdd_context_destroy() - Destroy HDD context + * @hdd_ctx: HDD context to be destroyed. + * + * Free config and HDD context as well as destroy all the resources. + * + * Return: None + */ +void hdd_context_destroy(struct hdd_context *hdd_ctx); + +int hdd_wlan_notify_modem_power_state(int state); + +void wlan_hdd_send_svc_nlink_msg(int radio, int type, void *data, int len); +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +void wlan_hdd_auto_shutdown_enable(struct hdd_context *hdd_ctx, bool enable); +#endif + +struct hdd_adapter * +hdd_get_con_sap_adapter(struct hdd_adapter *this_sap_adapter, + bool check_start_bss); + +bool hdd_is_5g_supported(struct hdd_context *hdd_ctx); + +/** + * hdd_is_2g_supported() - check if 2GHz channels are supported + * @hdd_ctx: Pointer to the hdd context + * + * HDD function to know if 2GHz channels are supported + * + * Return: true if 2GHz channels are supported + */ +bool hdd_is_2g_supported(struct hdd_context *hdd_ctx); + +int wlan_hdd_scan_abort(struct hdd_adapter *adapter); + +/** + * hdd_indicate_active_ndp_cnt() - Callback to indicate active ndp count to hdd + * if ndp connection is on NDI established + * @psoc: pointer to psoc object + * @vdev_id: vdev id + * @cnt: number of active ndp sessions + * + * This HDD callback registerd with policy manager to indicates number of active + * ndp sessions to hdd. + * + * Return: none + */ +void hdd_indicate_active_ndp_cnt(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t cnt); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static inline bool roaming_offload_enabled(struct hdd_context *hdd_ctx) +{ + bool is_roam_offload; + + ucfg_mlme_get_roaming_offload(hdd_ctx->psoc, &is_roam_offload); + + return is_roam_offload; +} +#else +static inline bool roaming_offload_enabled(struct hdd_context *hdd_ctx) +{ + return false; +} +#endif + +#ifdef WLAN_FEATURE_HOST_ROAM +static inline bool hdd_driver_roaming_supported(struct hdd_context *hdd_ctx) +{ + bool lfr_enabled; + + ucfg_mlme_is_lfr_enabled(hdd_ctx->psoc, &lfr_enabled); + + return lfr_enabled; +} +#else +static inline bool hdd_driver_roaming_supported(struct hdd_context *hdd_ctx) +{ + return false; +} +#endif + +static inline bool hdd_roaming_supported(struct hdd_context *hdd_ctx) +{ + bool val; + + val = hdd_driver_roaming_supported(hdd_ctx) || + roaming_offload_enabled(hdd_ctx); + + return val; +} + +#ifdef CFG80211_SCAN_RANDOM_MAC_ADDR +static inline bool hdd_scan_random_mac_addr_supported(void) +{ + return true; +} +#else +static inline bool hdd_scan_random_mac_addr_supported(void) +{ + return false; +} +#endif + +/** + * hdd_start_vendor_acs(): Start vendor ACS procedure + * @adapter: pointer to SAP adapter struct + * + * This function sends the ACS config to the ACS daemon and + * starts the vendor ACS timer to wait for the next command. + * + * Return: Status of vendor ACS procedure + */ +int hdd_start_vendor_acs(struct hdd_adapter *adapter); + +/** + * hdd_acs_response_timeout_handler() - timeout handler for acs_timer + * @context: timeout handler context + * + * Return: None + */ +void hdd_acs_response_timeout_handler(void *context); + +/** + * wlan_hdd_cfg80211_start_acs(): Start ACS Procedure for SAP + * @adapter: pointer to SAP adapter struct + * + * This function starts the ACS procedure if there are no + * constraints like MBSSID DFS restrictions. + * + * Return: Status of ACS Start procedure + */ +int wlan_hdd_cfg80211_start_acs(struct hdd_adapter *adapter); + +/** + * hdd_cfg80211_update_acs_config() - update acs config to application + * @adapter: hdd adapter + * @reason: channel change reason + * + * Return: 0 for success else error code + */ +int hdd_cfg80211_update_acs_config(struct hdd_adapter *adapter, + uint8_t reason); + +/** + * hdd_update_acs_timer_reason() - update acs timer start reason + * @adapter: hdd adapter + * @reason: channel change reason + * + * Return: 0 for success + */ +int hdd_update_acs_timer_reason(struct hdd_adapter *adapter, uint8_t reason); + +/** + * hdd_switch_sap_channel() - Move SAP to the given channel + * @adapter: AP adapter + * @channel: Channel + * @forced: Force to switch channel, ignore SCC/MCC check + * + * Moves the SAP interface by invoking the function which + * executes the callback to perform channel switch using (E)CSA. + * + * Return: None + */ +void hdd_switch_sap_channel(struct hdd_adapter *adapter, uint8_t channel, + bool forced); + +#if defined(FEATURE_WLAN_CH_AVOID) +void hdd_unsafe_channel_restart_sap(struct hdd_context *hdd_ctx); + +void hdd_ch_avoid_ind(struct hdd_context *hdd_ctxt, + struct unsafe_ch_list *unsafe_chan_list, + struct ch_avoid_ind_type *avoid_freq_list); +#else +static inline +void hdd_unsafe_channel_restart_sap(struct hdd_context *hdd_ctx) +{ +} + +static inline +void hdd_ch_avoid_ind(struct hdd_context *hdd_ctxt, + struct unsafe_ch_list *unsafe_chan_list, + struct ch_avoid_ind_type *avoid_freq_list) +{ +} +#endif + +/** + * hdd_free_mac_address_lists() - Free both the MAC address lists + * @hdd_ctx: HDD context + * + * This API clears/memset provisioned address list and + * derived address list + * + */ +void hdd_free_mac_address_lists(struct hdd_context *hdd_ctx); + +/** + * hdd_update_macaddr() - update mac address + * @hdd_ctx: hdd contxt + * @hw_macaddr: mac address + * @generate_mac_auto: Indicates whether the first address is + * provisioned address or derived address. + * + * Mac address for multiple virtual interface is found as following + * i) The mac address of the first interface is just the actual hw mac address. + * ii) MSM 3 or 4 bits of byte5 of the actual mac address are used to + * define the mac address for the remaining interfaces and locally + * admistered bit is set. INTF_MACADDR_MASK is based on the number of + * supported virtual interfaces, right now this is 0x07 (meaning 8 + * interface). + * Byte[3] of second interface will be hw_macaddr[3](bit5..7) + 1, + * for third interface it will be hw_macaddr[3](bit5..7) + 2, etc. + * + * Return: None + */ +void hdd_update_macaddr(struct hdd_context *hdd_ctx, + struct qdf_mac_addr hw_macaddr, bool generate_mac_auto); + +/** + * hdd_store_nss_chains_cfg_in_vdev() - Store the per vdev ini cfg in vdev_obj + * @adapter: Current HDD adapter passed from caller + * + * This function will store the per vdev nss params to the particular mlme + * vdev obj. + * + * Return: None + */ +void +hdd_store_nss_chains_cfg_in_vdev(struct hdd_adapter *adapter); + +/** + * wlan_hdd_disable_roaming() - disable roaming on all STAs except the input one + * @cur_adapter: Current HDD adapter passed from caller + * @mlme_operation_requestor: roam disable requestor + * + * This function loops through all adapters and disables roaming on each STA + * mode adapter except the current adapter passed from the caller + * + * Return: None + */ +void wlan_hdd_disable_roaming(struct hdd_adapter *cur_adapter, + uint32_t mlme_operation_requestor); + +/** + * wlan_hdd_enable_roaming() - enable roaming on all STAs except the input one + * @cur_adapter: Current HDD adapter passed from caller + * @mlme_operation_requestor: roam disable requestor + * + * This function loops through all adapters and enables roaming on each STA + * mode adapter except the current adapter passed from the caller + * + * Return: None + */ +void wlan_hdd_enable_roaming(struct hdd_adapter *cur_adapter, + uint32_t mlme_operation_requestor); + +QDF_STATUS hdd_post_cds_enable_config(struct hdd_context *hdd_ctx); + +QDF_STATUS hdd_abort_mac_scan_all_adapters(struct hdd_context *hdd_ctx); + +void wlan_hdd_stop_sap(struct hdd_adapter *ap_adapter); +void wlan_hdd_start_sap(struct hdd_adapter *ap_adapter, bool reinit); + +#ifdef QCA_CONFIG_SMP +int wlan_hdd_get_cpu(void); +#else +static inline int wlan_hdd_get_cpu(void) +{ + return 0; +} +#endif + +void wlan_hdd_sap_pre_cac_failure(void *data); +void hdd_clean_up_pre_cac_interface(struct hdd_context *hdd_ctx); + +void wlan_hdd_txrx_pause_cb(uint8_t vdev_id, + enum netif_action_type action, enum netif_reason_type reason); + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +void wlan_hdd_mod_fc_timer(struct hdd_adapter *adapter, + enum netif_action_type action); +#else +static inline void wlan_hdd_mod_fc_timer(struct hdd_adapter *adapter, + enum netif_action_type action) +{ +} +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + +int hdd_wlan_dump_stats(struct hdd_adapter *adapter, int value); +void wlan_hdd_display_tx_rx_histogram(struct hdd_context *hdd_ctx); +void wlan_hdd_clear_tx_rx_histogram(struct hdd_context *hdd_ctx); + +void +wlan_hdd_display_netif_queue_history(struct hdd_context *hdd_ctx, + enum qdf_stats_verbosity_level verb_lvl); + +/** + * wlan_hdd_display_adapter_netif_queue_history() - display adapter based netif + * queue history + * @adapter: hdd adapter + * + * Return: none + */ +void +wlan_hdd_display_adapter_netif_queue_history(struct hdd_adapter *adapter); + +void wlan_hdd_clear_netif_queue_history(struct hdd_context *hdd_ctx); +const char *hdd_get_fwpath(void); +void hdd_indicate_mgmt_frame(tSirSmeMgmtFrameInd *frame_ind); + +/** + * hdd_get_adapter_by_iface_name() - Return adapter with given interface name + * @hdd_ctx: hdd context. + * @iface_name: interface name + * + * This function is used to get the adapter with given interface name + * + * Return: adapter pointer if found, NULL otherwise + * + */ +struct hdd_adapter *hdd_get_adapter_by_iface_name(struct hdd_context *hdd_ctx, + const char *iface_name); +enum phy_ch_width hdd_map_nl_chan_width(enum nl80211_chan_width ch_width); + +/** + * hdd_nl_to_qdf_iface_type() - map nl80211_iftype to QDF_OPMODE + * @nl_type: the input NL80211 interface type to map + * @out_qdf_type: the output, equivalent QDF operating mode + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_nl_to_qdf_iface_type(enum nl80211_iftype nl_type, + enum QDF_OPMODE *out_qdf_type); + +/** + * wlan_hdd_find_opclass() - Find operating class for a channel + * @mac_handle: global MAC handle + * @channel: channel id + * @bw_offset: bandwidth offset + * + * Function invokes sme api to find the operating class + * + * Return: operating class + */ +uint8_t wlan_hdd_find_opclass(mac_handle_t mac_handle, uint8_t channel, + uint8_t bw_offset); + +int hdd_update_config(struct hdd_context *hdd_ctx); + +/** + * hdd_update_components_config() - Initialize driver per module ini parameters + * @hdd_ctx: HDD Context + * + * API is used to initialize components configuration parameters + * Return: 0 for success, errno for failure + */ +int hdd_update_components_config(struct hdd_context *hdd_ctx); + +QDF_STATUS hdd_chan_change_notify(struct hdd_adapter *adapter, + struct net_device *dev, + struct hdd_chan_change_params chan_change, + bool legacy_phymode); +int wlan_hdd_set_channel(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + enum nl80211_channel_type channel_type); +int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter, + struct cfg80211_beacon_data *params, + const u8 *ssid, size_t ssid_len, + enum nl80211_hidden_ssid hidden_ssid, + bool check_for_concurrency); + +#if !defined(REMOVE_PKT_LOG) +int hdd_process_pktlog_command(struct hdd_context *hdd_ctx, uint32_t set_value, + int set_value2); +int hdd_pktlog_enable_disable(struct hdd_context *hdd_ctx, bool enable, + uint8_t user_triggered, int size); + +#else +static inline +int hdd_pktlog_enable_disable(struct hdd_context *hdd_ctx, bool enable, + uint8_t user_triggered, int size) +{ + return 0; +} + +static inline +int hdd_process_pktlog_command(struct hdd_context *hdd_ctx, + uint32_t set_value, int set_value2) +{ + return 0; +} +#endif /* REMOVE_PKT_LOG */ + +#if defined(FEATURE_SG) && !defined(CONFIG_HL_SUPPORT) +/** + * hdd_set_sg_flags() - enable SG flag in the network device + * @hdd_ctx: HDD context + * @wlan_dev: network device structure + * + * This function enables the SG feature flag in the + * given network device. + * + * Return: none + */ +static inline void hdd_set_sg_flags(struct hdd_context *hdd_ctx, + struct net_device *wlan_dev) +{ + hdd_debug("SG Enabled"); + wlan_dev->features |= NETIF_F_SG; +} +#else +static inline void hdd_set_sg_flags(struct hdd_context *hdd_ctx, + struct net_device *wlan_dev){} +#endif + +/** + * hdd_set_netdev_flags() - set netdev flags for adapter as per ini config + * @adapter: hdd adapter context + * + * This function sets netdev feature flags for the adapter. + * + * Return: none + */ +void hdd_set_netdev_flags(struct hdd_adapter *adapter); + +#ifdef FEATURE_TSO +/** + * hdd_get_tso_csum_feature_flags() - Return TSO and csum flags if enabled + * + * Return: Enabled feature flags set, 0 on failure + */ +static inline netdev_features_t hdd_get_tso_csum_feature_flags(void) +{ + netdev_features_t netdev_features = 0; + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!soc) { + hdd_err("soc handle is NULL"); + return 0; + } + + if (cdp_cfg_get(soc, cfg_dp_enable_ip_tcp_udp_checksum_offload)) { + netdev_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + + if (cdp_cfg_get(soc, cfg_dp_tso_enable)) { + /* + * Enable TSO only if IP/UDP/TCP TX checksum flag is + * enabled. + */ + netdev_features |= NETIF_F_TSO | NETIF_F_TSO6 | + NETIF_F_SG; + } + } + return netdev_features; +} + +/** + * hdd_set_tso_flags() - enable TSO flags in the network device + * @hdd_ctx: HDD context + * @wlan_dev: network device structure + * + * This function enables the TSO related feature flags in the + * given network device. + * + * Return: none + */ +static inline void hdd_set_tso_flags(struct hdd_context *hdd_ctx, + struct net_device *wlan_dev) +{ + hdd_debug("TSO Enabled"); + + wlan_dev->features |= hdd_get_tso_csum_feature_flags(); +} +#else +static inline void hdd_set_tso_flags(struct hdd_context *hdd_ctx, + struct net_device *wlan_dev) +{ + hdd_set_sg_flags(hdd_ctx, wlan_dev); +} + +static inline netdev_features_t hdd_get_tso_csum_feature_flags(void) +{ + return 0; +} +#endif /* FEATURE_TSO */ + +/** + * wlan_hdd_get_host_log_nl_proto() - Get host log netlink protocol + * @hdd_ctx: HDD context + * + * This function returns with host log netlink protocol settings + * + * Return: none + */ +#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE +static inline int wlan_hdd_get_host_log_nl_proto(struct hdd_context *hdd_ctx) +{ + return hdd_ctx->config->host_log_custom_nl_proto; +} +#else +static inline int wlan_hdd_get_host_log_nl_proto(struct hdd_context *hdd_ctx) +{ + return NETLINK_USERSOCK; +} +#endif + +#ifdef CONFIG_CNSS_LOGGER +/** + * wlan_hdd_nl_init() - wrapper function to CNSS_LOGGER case + * @hdd_ctx: the hdd context pointer + * + * The nl_srv_init() will call to cnss_logger_device_register() and + * expect to get a radio_index from cnss_logger module and assign to + * hdd_ctx->radio_index, then to maintain the consistency to original + * design, adding the radio_index check here, then return the error + * code if radio_index is not assigned correctly, which means the nl_init + * from cnss_logger is failed. + * + * Return: 0 if successfully, otherwise error code + */ +static inline int wlan_hdd_nl_init(struct hdd_context *hdd_ctx) +{ + int proto; + + proto = wlan_hdd_get_host_log_nl_proto(hdd_ctx); + hdd_ctx->radio_index = nl_srv_init(hdd_ctx->wiphy, proto); + + /* radio_index is assigned from 0, so only >=0 will be valid index */ + if (hdd_ctx->radio_index >= 0) + return 0; + else + return -EINVAL; +} +#else +/** + * wlan_hdd_nl_init() - wrapper function to non CNSS_LOGGER case + * @hdd_ctx: the hdd context pointer + * + * In case of non CNSS_LOGGER case, the nl_srv_init() will initialize + * the netlink socket and return the success or not. + * + * Return: the return value from nl_srv_init() + */ +static inline int wlan_hdd_nl_init(struct hdd_context *hdd_ctx) +{ + int proto; + + proto = wlan_hdd_get_host_log_nl_proto(hdd_ctx); + return nl_srv_init(hdd_ctx->wiphy, proto); +} +#endif +QDF_STATUS hdd_sme_open_session_callback(uint8_t vdev_id, + QDF_STATUS qdf_status); +QDF_STATUS hdd_sme_close_session_callback(uint8_t vdev_id); + +/** + * hdd_reassoc() - perform a userspace-directed reassoc + * @adapter: Adapter upon which the command was received + * @bssid: BSSID with which to reassociate + * @ch_freq: channel upon which to reassociate + * @src: The source for the trigger of this action + * + * This function performs a userspace-directed reassoc operation + * + * Return: 0 for success non-zero for failure + */ +int hdd_reassoc(struct hdd_adapter *adapter, const uint8_t *bssid, + uint32_t ch_freq, const handoff_src src); + +int hdd_register_cb(struct hdd_context *hdd_ctx); +void hdd_deregister_cb(struct hdd_context *hdd_ctx); +int hdd_start_station_adapter(struct hdd_adapter *adapter); +int hdd_start_ap_adapter(struct hdd_adapter *adapter); +int hdd_configure_cds(struct hdd_context *hdd_ctx); +int hdd_set_fw_params(struct hdd_adapter *adapter); + +/** + * hdd_wlan_start_modules() - Single driver state machine for starting modules + * @hdd_ctx: HDD context + * @reinit: flag to indicate from SSR or normal path + * + * This function maintains the driver state machine it will be invoked from + * startup, reinit and change interface. Depending on the driver state shall + * perform the opening of the modules. + * + * Return: Errno + */ +int hdd_wlan_start_modules(struct hdd_context *hdd_ctx, bool reinit); + +/** + * hdd_wlan_stop_modules - Single driver state machine for stoping modules + * @hdd_ctx: HDD context + * @ftm_mode: ftm mode + * + * This function maintains the driver state machine it will be invoked from + * exit, shutdown and con_mode change handler. Depending on the driver state + * shall perform the stopping/closing of the modules. + * + * Return: Errno + */ +int hdd_wlan_stop_modules(struct hdd_context *hdd_ctx, bool ftm_mode); + +/** + * hdd_psoc_idle_timer_start() - start the idle psoc detection timer + * @hdd_ctx: the hdd context for which the timer should be started + * + * Return: None + */ +void hdd_psoc_idle_timer_start(struct hdd_context *hdd_ctx); + +/** + * hdd_psoc_idle_timer_stop() - stop the idle psoc detection timer + * @hdd_ctx: the hdd context for which the timer should be stopped + * + * Return: None + */ +void hdd_psoc_idle_timer_stop(struct hdd_context *hdd_ctx); + +/** + * hdd_trigger_psoc_idle_restart() - trigger restart of a previously shutdown + * idle psoc, if needed + * @hdd_ctx: the hdd context which should be restarted + * + * This API does nothing if the given psoc is already active. + * + * Return: Errno + */ +int hdd_trigger_psoc_idle_restart(struct hdd_context *hdd_ctx); + +int hdd_start_adapter(struct hdd_adapter *adapter); +void hdd_populate_random_mac_addr(struct hdd_context *hdd_ctx, uint32_t num); +/** + * hdd_is_interface_up()- Checkfor interface up before ssr + * @hdd_ctx: HDD context + * + * check if there are any wlan interfaces before SSR accordingly start + * the interface. + * + * Return: 0 if interface was opened else false + */ +bool hdd_is_interface_up(struct hdd_adapter *adapter); + +void hdd_connect_result(struct net_device *dev, const u8 *bssid, + struct csr_roam_info *roam_info, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, u16 status, gfp_t gfp, + bool connect_timeout, + tSirResultCodes timeout_reason); + +#ifdef WLAN_FEATURE_FASTPATH +void hdd_enable_fastpath(struct hdd_context *hdd_ctx, + void *context); +#else +static inline void hdd_enable_fastpath(struct hdd_context *hdd_ctx, + void *context) +{ +} +#endif +void hdd_wlan_update_target_info(struct hdd_context *hdd_ctx, void *context); + +enum sap_acs_dfs_mode wlan_hdd_get_dfs_mode(enum dfs_mode mode); +void hdd_unsafe_channel_restart_sap(struct hdd_context *hdd_ctx); +/** + * hdd_clone_local_unsafe_chan() - clone hdd ctx unsafe chan list + * @hdd_ctx: hdd context pointer + * @local_unsafe_list: copied unsafe chan list array + * @local_unsafe_list_count: channel number in returned local_unsafe_list + * + * The function will allocate memory and make a copy the current unsafe + * channels from hdd ctx. The caller need to free the local_unsafe_list + * memory after use. + * + * Return: 0 if successfully clone unsafe chan list. + */ +int hdd_clone_local_unsafe_chan(struct hdd_context *hdd_ctx, + uint16_t **local_unsafe_list, uint16_t *local_unsafe_list_count); + +/** + * hdd_local_unsafe_channel_updated() - check unsafe chan list same or not + * @hdd_ctx: hdd context pointer + * @local_unsafe_list: unsafe chan list to be compared with hdd_ctx's list + * @local_unsafe_list_count: channel number in local_unsafe_list + * + * The function checked the input channel is same as current unsafe chan + * list in hdd_ctx. + * + * Return: true if input channel list is same as the list in hdd_ctx + */ +bool hdd_local_unsafe_channel_updated(struct hdd_context *hdd_ctx, + uint16_t *local_unsafe_list, uint16_t local_unsafe_list_count); + +int hdd_enable_disable_ca_event(struct hdd_context *hddctx, + uint8_t set_value); +void wlan_hdd_undo_acs(struct hdd_adapter *adapter); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)) +static inline int +hdd_wlan_nla_put_u64(struct sk_buff *skb, int attrtype, u64 value) +{ + return nla_put_u64(skb, attrtype, value); +} +#else +static inline int +hdd_wlan_nla_put_u64(struct sk_buff *skb, int attrtype, u64 value) +{ + return nla_put_u64_64bit(skb, attrtype, value, NL80211_ATTR_PAD); +} +#endif + +/** + * hdd_roam_profile() - Get adapter's roam profile + * @adapter: The adapter being queried + * + * Given an adapter this function returns a pointer to its roam profile. + * + * NOTE WELL: Caller is responsible for ensuring this interface is only + * invoked for STA-type interfaces + * + * Return: pointer to the adapter's roam profile + */ +static inline +struct csr_roam_profile *hdd_roam_profile(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + return &sta_ctx->roam_profile; +} + +/** + * hdd_security_ie() - Get adapter's security IE + * @adapter: The adapter being queried + * + * Given an adapter this function returns a pointer to its security IE + * buffer. Note that this buffer is maintained outside the roam + * profile but, when in use, is referenced by a pointer within the + * roam profile. + * + * NOTE WELL: Caller is responsible for ensuring this interface is only + * invoked for STA-type interfaces + * + * Return: pointer to the adapter's roam profile security IE buffer + */ +static inline +uint8_t *hdd_security_ie(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + return sta_ctx->security_ie; +} + +/** + * hdd_assoc_additional_ie() - Get adapter's assoc additional IE + * @adapter: The adapter being queried + * + * Given an adapter this function returns a pointer to its assoc + * additional IE buffer. Note that this buffer is maintained outside + * the roam profile but, when in use, is referenced by a pointer + * within the roam profile. + * + * NOTE WELL: Caller is responsible for ensuring this interface is only + * invoked for STA-type interfaces + * + * Return: pointer to the adapter's assoc additional IE buffer + */ +static inline +tSirAddie *hdd_assoc_additional_ie(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + return &sta_ctx->assoc_additional_ie; +} + +/** + * hdd_is_roaming_in_progress() - check if roaming is in progress + * @hdd_ctx - Global HDD context + * + * Checks if roaming is in progress on any of the adapters + * + * Return: true if roaming is in progress else false + */ +bool hdd_is_roaming_in_progress(struct hdd_context *hdd_ctx); +void hdd_set_roaming_in_progress(bool value); + +/** + * hdd_is_connection_in_progress() - check if connection is in progress + * @out_vdev_id: id of vdev where connection is occurring + * @out_reason: scan reject reason + * + * Go through each adapter and check if connection is in progress. + * Output parameters @out_vdev_id and @out_reason will only be written + * when a connection is in progress. + * + * Return: true if connection is in progress else false + */ +bool hdd_is_connection_in_progress(uint8_t *out_vdev_id, + enum scan_reject_states *out_reason); + +void hdd_restart_sap(struct hdd_adapter *ap_adapter); +void hdd_check_and_restart_sap_with_non_dfs_acs(void); +bool hdd_set_connection_in_progress(bool value); + +/** + * wlan_hdd_init_chan_info() - initialize channel info variables + * @hdd_ctx: hdd ctx + * + * This API initialize channel info variables + * + * Return: None + */ +void wlan_hdd_init_chan_info(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_deinit_chan_info() - deinitialize channel info variables + * @hdd_ctx: hdd ctx + * + * This API deinitialize channel info variables + * + * Return: None + */ +void wlan_hdd_deinit_chan_info(struct hdd_context *hdd_ctx); +void wlan_hdd_start_sap(struct hdd_adapter *ap_adapter, bool reinit); + +/** + * hdd_is_any_interface_open() - Check for interface up + * @hdd_ctx: HDD context + * + * Return: true if any interface is open + */ +bool hdd_is_any_interface_open(struct hdd_context *hdd_ctx); + +#ifdef WIFI_POS_CONVERGED +/** + * hdd_send_peer_status_ind_to_app() - wrapper to call legacy or new wifi_pos + * function to send peer status to a registered application + * @peer_mac: MAC address of peer + * @peer_status: ePeerConnected or ePeerDisconnected + * @peer_timing_meas_cap: 0: RTT/RTT2, 1: RTT3. Default is 0 + * @vdev_id: ID of the underlying vdev + * @chan_info: operating channel information + * @dev_mode: dev mode for which indication is sent + * + * Return: none + */ +static inline void hdd_send_peer_status_ind_to_app( + struct qdf_mac_addr *peer_mac, + uint8_t peer_status, + uint8_t peer_timing_meas_cap, + uint8_t vdev_id, + struct oem_channel_info *chan_info, + enum QDF_OPMODE dev_mode) +{ + struct wifi_pos_ch_info ch_info; + + if (!chan_info) { + os_if_wifi_pos_send_peer_status(peer_mac, peer_status, + peer_timing_meas_cap, vdev_id, + NULL, dev_mode); + return; + } + + /* chan_id is obsoleted by mhz */ + ch_info.chan_id = 0; + ch_info.mhz = chan_info->mhz; + ch_info.band_center_freq1 = chan_info->band_center_freq1; + ch_info.band_center_freq2 = chan_info->band_center_freq2; + ch_info.info = chan_info->info; + ch_info.reg_info_1 = chan_info->reg_info_1; + ch_info.reg_info_2 = chan_info->reg_info_2; + ch_info.nss = chan_info->nss; + ch_info.rate_flags = chan_info->rate_flags; + ch_info.sec_ch_offset = chan_info->sec_ch_offset; + ch_info.ch_width = chan_info->ch_width; + os_if_wifi_pos_send_peer_status(peer_mac, peer_status, + peer_timing_meas_cap, vdev_id, + &ch_info, dev_mode); +} +#else +static inline void hdd_send_peer_status_ind_to_app( + struct qdf_mac_addr *peer_mac, + uint8_t peer_status, + uint8_t peer_timing_meas_cap, + uint8_t vdev_id, + struct oem_channel_info *chan_info, + enum QDF_OPMODE dev_mode) +{ + hdd_send_peer_status_ind_to_oem_app(peer_mac, peer_status, + peer_timing_meas_cap, vdev_id, chan_info, dev_mode); +} +#endif /* WIFI_POS_CONVERGENCE */ + +/** + * wlan_hdd_send_p2p_quota()- Send P2P Quota value to FW + * @adapter: Adapter data + * @sval: P2P quota value + * + * Send P2P quota value to FW + * + * Return: 0 success else failure + */ +int wlan_hdd_send_p2p_quota(struct hdd_adapter *adapter, int sval); + +/** + * wlan_hdd_send_p2p_quota()- Send MCC latency to FW + * @adapter: Adapter data + * @sval: MCC latency value + * + * Send MCC latency value to FW + * + * Return: 0 success else failure + */ +int wlan_hdd_send_mcc_latency(struct hdd_adapter *adapter, int sval); + +/** + * wlan_hdd_get_adapter_from_vdev()- Get adapter from vdev id + * and PSOC object data + * @psoc: Psoc object data + * @vdev_id: vdev id + * + * Get adapter from vdev id and PSOC object data + * + * Return: adapter pointer + */ +struct hdd_adapter *wlan_hdd_get_adapter_from_vdev(struct wlan_objmgr_psoc + *psoc, uint8_t vdev_id); +/** + * hdd_unregister_notifiers()- unregister kernel notifiers + * @hdd_ctx: Hdd Context + * + * Unregister netdev notifiers like Netdevice,IPv4 and IPv6. + * + */ +void hdd_unregister_notifiers(struct hdd_context *hdd_ctx); + +/** + * hdd_dbs_scan_selection_init() - initialization for DBS scan selection config + * @hdd_ctx: HDD context + * + * This function sends the DBS scan selection config configuration to the + * firmware via WMA + * + * Return: 0 - success, < 0 - failure + */ +int hdd_dbs_scan_selection_init(struct hdd_context *hdd_ctx); + +/** + * hdd_start_complete()- complete the start event + * @ret: return value for complete event. + * + * complete the startup event and set the return in + * global variable + * + * Return: void + */ + +void hdd_start_complete(int ret); + +/** + * hdd_chip_pwr_save_fail_detected_cb() - chip power save failure detected + * callback + * @hdd_handle: HDD handle + * @data: chip power save failure detected data + * + * This function reads the chip power save failure detected data and fill in + * the skb with NL attributes and send up the NL event. + * This callback execute in atomic context and must not invoke any + * blocking calls. + * + * Return: none + */ + +void hdd_chip_pwr_save_fail_detected_cb(hdd_handle_t hdd_handle, + struct chip_pwr_save_fail_detected_params + *data); + +/** + * hdd_update_ie_whitelist_attr() - Copy probe req ie whitelist attrs from cfg + * @ie_whitelist: output parameter + * @hdd_ctx: pointer to hdd context + * + * Return: None + */ +void hdd_update_ie_whitelist_attr(struct probe_req_whitelist_attr *ie_whitelist, + struct hdd_context *hdd_ctx); + +/** + * hdd_get_rssi_snr_by_bssid() - gets the rssi and snr by bssid from scan cache + * @adapter: adapter handle + * @bssid: bssid to look for in scan cache + * @rssi: rssi value found + * @snr: snr value found + * + * Return: QDF_STATUS + */ +int hdd_get_rssi_snr_by_bssid(struct hdd_adapter *adapter, const uint8_t *bssid, + int8_t *rssi, int8_t *snr); + +/** + * hdd_reset_limit_off_chan() - reset limit off-channel command parameters + * @adapter - HDD adapter + * + * Return: 0 on success and non zero value on failure + */ +int hdd_reset_limit_off_chan(struct hdd_adapter *adapter); + +#if defined(WLAN_FEATURE_FILS_SK) && \ + (defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) +/** + * hdd_clear_fils_connection_info: API to clear fils info from roam profile and + * free allocated memory + * @adapter: pointer to hdd adapter + * + * Return: None + */ +void hdd_clear_fils_connection_info(struct hdd_adapter *adapter); + +/** + * hdd_update_hlp_info() - Update HLP packet received in FILS (re)assoc rsp + * @dev: net device + * @roam_fils_params: Fils join rsp params + * + * This API is used to send the received HLP packet in Assoc rsp(FILS AKM) + * to the network layer. + * + * Return: None + */ +void hdd_update_hlp_info(struct net_device *dev, + struct csr_roam_info *roam_info); +#else +static inline void hdd_clear_fils_connection_info(struct hdd_adapter *adapter) +{ } +static inline void hdd_update_hlp_info(struct net_device *dev, + struct csr_roam_info *roam_info) +{} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) +static inline void hdd_dev_setup_destructor(struct net_device *dev) +{ + dev->destructor = free_netdev; +} +#else +static inline void hdd_dev_setup_destructor(struct net_device *dev) +{ + dev->needs_free_netdev = true; +} +#endif /* KERNEL_VERSION(4, 12, 0) */ + +/** + * hdd_dp_trace_init() - initialize DP Trace by calling the QDF API + * @config: hdd config + * + * Return: NONE + */ +#ifdef CONFIG_DP_TRACE +void hdd_dp_trace_init(struct hdd_config *config); +#else +static inline +void hdd_dp_trace_init(struct hdd_config *config) {} +#endif + +/** + * hdd_set_rx_mode_rps() - Enable/disable RPS in SAP mode + * @enable: Set true to enable RPS in SAP mode + * + * Callback function registered with datapath + * + * Return: none + */ +void hdd_set_rx_mode_rps(bool enable); + +/** + * hdd_limit_max_per_index_score() -check if per index score doesn't exceed 100% + * (0x64). If it exceed make it 100% + * + * @per_index_score: per_index_score as input + * + * Return: per_index_score within the max limit + */ +uint32_t hdd_limit_max_per_index_score(uint32_t per_index_score); + +/** + * hdd_update_score_config - API to update candidate scoring related params + * configuration parameters + * @score_config: score config to update + * @cfg: config params + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_update_score_config( + struct scoring_config *score_config, struct hdd_context *hdd_ctx); + +/** + * hdd_get_stainfo() - get stainfo for the specified peer + * @astainfo: array of the station info in which the sta info + * corresponding to mac_addr needs to be searched + * @mac_addr: mac address of requested peer + * + * This function find the stainfo for the peer with mac_addr + * + * Return: stainfo if found, NULL if not found + */ +struct hdd_station_info *hdd_get_stainfo(struct hdd_station_info *astainfo, + struct qdf_mac_addr mac_addr); + +/** + * hdd_component_psoc_open() - Open the legacy components + * @psoc: Pointer to psoc object + * + * This function opens the legacy components and initializes the + * component's private objects. + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_component_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * hdd_component_psoc_close() - Close the legacy components + * @psoc: Pointer to psoc object + * + * This function closes the legacy components and resets the + * component's private objects. + * + * Return: None + */ +void hdd_component_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * hdd_component_psoc_enable() - Trigger psoc enable for CLD Components + * + * Return: None + */ +void hdd_component_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * hdd_component_psoc_disable() - Trigger psoc disable for CLD Components + * + * Return: None + */ +void hdd_component_psoc_disable(struct wlan_objmgr_psoc *psoc); + +/** + * hdd_component_pdev_open() - Trigger pdev open for CLD Components + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_component_pdev_open(struct wlan_objmgr_pdev *pdev); + +/** + * hdd_component_pdev_close() - Trigger pdev close for CLD Components + * + * Return: None + */ +void hdd_component_pdev_close(struct wlan_objmgr_pdev *pdev); + +#ifdef WLAN_FEATURE_MEMDUMP_ENABLE +int hdd_driver_memdump_init(void); +void hdd_driver_memdump_deinit(void); + +/** + * hdd_driver_mem_cleanup() - Frees memory allocated for + * driver dump + * + * This function frees driver dump memory. + * + * Return: None + */ +void hdd_driver_mem_cleanup(void); + +#else /* WLAN_FEATURE_MEMDUMP_ENABLE */ +static inline int hdd_driver_memdump_init(void) +{ + return 0; +} +static inline void hdd_driver_memdump_deinit(void) +{ +} + +static inline void hdd_driver_mem_cleanup(void) +{ +} +#endif /* WLAN_FEATURE_MEMDUMP_ENABLE */ +/** + * hdd_set_disconnect_status() - set adapter disconnection status + * @hdd_adapter: Pointer to hdd adapter + * @disconnecting: Disconnect status to set + * + * Return: None + */ +void hdd_set_disconnect_status(struct hdd_adapter *adapter, bool disconnecting); + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +/** + * wlan_hdd_set_mon_chan() - Set capture channel on the monitor mode interface. + * @adapter: Handle to adapter + * @freq: Monitor mode frequency (MHz) + * @bandwidth: Capture channel bandwidth + * + * Return: 0 on success else error code. + */ +int wlan_hdd_set_mon_chan(struct hdd_adapter *adapter, qdf_freq_t freq, + uint32_t bandwidth); +#else +static inline +int wlan_hdd_set_mon_chan(struct hdd_adapter *adapter, qdf_freq_t freq, + uint32_t bandwidth) +{ + return 0; +} +#endif + +/** + * hdd_wlan_get_version() - Get version information + * @hdd_ctx: Global HDD context + * @version_len: length of the version buffer size + * @version: the buffer to the version string + * + * This function is used to get Wlan Driver, Firmware, Hardware Version + * & the Board related information. + * + * Return: the length of the version string + */ +uint32_t hdd_wlan_get_version(struct hdd_context *hdd_ctx, + const size_t version_len, uint8_t *version); +/** + * hdd_assemble_rate_code() - assemble rate code to be sent to FW + * @preamble: rate preamble + * @nss: number of streams + * @rate: rate index + * + * Rate code assembling is different for targets which are 11ax capable. + * Check for the target support and assemble the rate code accordingly. + * + * Return: assembled rate code + */ +int hdd_assemble_rate_code(uint8_t preamble, uint8_t nss, uint8_t rate); + +/** + * hdd_set_11ax_rate() - set 11ax rate + * @adapter: adapter being modified + * @value: new 11ax rate code + * @sap_config: pointer to SAP config to check HW mode + * this will be NULL for call from STA persona + * + * Return: 0 on success, negative errno on failure + */ +int hdd_set_11ax_rate(struct hdd_adapter *adapter, int value, + struct sap_config *sap_config); + +/** + * hdd_update_hw_sw_info() - API to update the HW/SW information + * @hdd_ctx: Global HDD context + * + * API to update the HW and SW information in the driver + * + * Note: + * All the version/revision information would only be retrieved after + * firmware download + * + * Return: None + */ +void hdd_update_hw_sw_info(struct hdd_context *hdd_ctx); + +/** + * hdd_get_nud_stats_cb() - callback api to update the stats received from FW + * @data: pointer to hdd context. + * @rsp: pointer to data received from FW. + * @context: callback context + * + * This is called when wlan driver received response event for + * get arp stats to firmware. + * + * Return: None + */ +void hdd_get_nud_stats_cb(void *data, struct rsp_stats *rsp, void *context); + +/** + * hdd_context_get_mac_handle() - get mac handle from hdd context + * @hdd_ctx: Global HDD context pointer + * + * Retrieves the global MAC handle from the HDD context + * + * Return: The global MAC handle (which may be NULL) + */ +static inline +mac_handle_t hdd_context_get_mac_handle(struct hdd_context *hdd_ctx) +{ + return hdd_ctx ? hdd_ctx->mac_handle : NULL; +} + +/** + * hdd_adapter_get_mac_handle() - get mac handle from hdd adapter + * @adapter: HDD adapter pointer + * + * Retrieves the global MAC handle given an HDD adapter + * + * Return: The global MAC handle (which may be NULL) + */ +static inline +mac_handle_t hdd_adapter_get_mac_handle(struct hdd_adapter *adapter) +{ + return adapter ? + hdd_context_get_mac_handle(adapter->hdd_ctx) : NULL; +} + +/** + * hdd_handle_to_context() - turn an HDD handle into an HDD context + * @hdd_handle: HDD handle to be converted + * + * Return: HDD context referenced by @hdd_handle + */ +static inline +struct hdd_context *hdd_handle_to_context(hdd_handle_t hdd_handle) +{ + return (struct hdd_context *)hdd_handle; +} + +/** + * wlan_hdd_free_cache_channels() - Free the cache channels list + * @hdd_ctx: Pointer to HDD context + * + * Return: None + */ +void wlan_hdd_free_cache_channels(struct hdd_context *hdd_ctx); + +/** + * hdd_update_dynamic_mac() - Updates the dynamic MAC list + * @hdd_ctx: Pointer to HDD context + * @curr_mac_addr: Current interface mac address + * @new_mac_addr: New mac address which needs to be updated + * + * This function updates newly configured MAC address to the + * dynamic MAC address list corresponding to the current + * adapter MAC address + * + * Return: None + */ +void hdd_update_dynamic_mac(struct hdd_context *hdd_ctx, + struct qdf_mac_addr *curr_mac_addr, + struct qdf_mac_addr *new_mac_addr); + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +/** + * wlan_hdd_send_tcp_param_update_event() - Send vendor event to update + * TCP parameter through Wi-Fi HAL + * @hdd_ctx: Pointer to HDD context + * @data: Parameters to update + * @dir: Direction(tx/rx) to update + * + * Return: None + */ +void wlan_hdd_send_tcp_param_update_event(struct hdd_context *hdd_ctx, + void *data, + uint8_t dir); + +/** + * wlan_hdd_update_tcp_rx_param() - update TCP param in RX dir + * @hdd_ctx: Pointer to HDD context + * @data: Parameters to update + * + * Return: None + */ +void wlan_hdd_update_tcp_rx_param(struct hdd_context *hdd_ctx, void *data); + +/** + * wlan_hdd_update_tcp_tx_param() - update TCP param in TX dir + * @hdd_ctx: Pointer to HDD context + * @data: Parameters to update + * + * Return: None + */ +void wlan_hdd_update_tcp_tx_param(struct hdd_context *hdd_ctx, void *data); +#else +static inline +void wlan_hdd_update_tcp_rx_param(struct hdd_context *hdd_ctx, void *data) +{ +} + +static inline +void wlan_hdd_update_tcp_tx_param(struct hdd_context *hdd_ctx, void *data) +{ +} + +static inline +void wlan_hdd_send_tcp_param_update_event(struct hdd_context *hdd_ctx, + void *data, + uint8_t dir) +{ +} +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + +#ifdef WLAN_FEATURE_MOTION_DETECTION +/** + * hdd_md_host_evt_cb - Callback for Motion Detection Event + * @ctx: HDD context + * @sir_md_evt: motion detect event + * + * Callback for Motion Detection Event. Re-enables Motion + * Detection again upon event + * + * Return: QDF_STATUS QDF_STATUS_SUCCESS on Success and + * QDF_STATUS_E_FAILURE on failure + */ +QDF_STATUS hdd_md_host_evt_cb(void *ctx, struct sir_md_evt *event); + +/** + * hdd_md_bl_evt_cb - Callback for Motion Detection Baseline Event + * @ctx: HDD context + * @sir_md_bl_evt: motion detect baseline event + * + * Callback for Motion Detection Baseline Event + * + * Return: QDF_STATUS QDF_STATUS_SUCCESS on Success and + * QDF_STATUS_E_FAILURE on failure + */ +QDF_STATUS hdd_md_bl_evt_cb(void *ctx, struct sir_md_bl_evt *event); +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +/** + * hdd_hidden_ssid_enable_roaming() - enable roaming after hidden ssid rsp + * @hdd_handle: Hdd handler + * @vdev_id: Vdev Id + * + * This is a wrapper function to enable roaming after getting hidden + * ssid rsp + */ +void hdd_hidden_ssid_enable_roaming(hdd_handle_t hdd_handle, uint8_t vdev_id); + +/** + * hdd_send_update_owe_info_event - Send update OWE info event + * @adapter: Pointer to adapter + * @sta_addr: MAC address of peer STA + * @owe_ie: OWE IE + * @owe_ie_len: Length of OWE IE + * + * Send update OWE info event to hostapd + * + * Return: none + */ +#ifdef CFG80211_EXTERNAL_DH_UPDATE_SUPPORT +void hdd_send_update_owe_info_event(struct hdd_adapter *adapter, + uint8_t sta_addr[], + uint8_t *owe_ie, + uint32_t owe_ie_len); +#else +static inline void hdd_send_update_owe_info_event(struct hdd_adapter *adapter, + uint8_t sta_addr[], + uint8_t *owe_ie, + uint32_t owe_ie_len) +{ +} +#endif + +/** + * hdd_psoc_idle_shutdown - perform idle shutdown after interface inactivity + * timeout + * @device: pointer to struct device + * + * Return: 0 for success non-zero error code for failure + */ +int hdd_psoc_idle_shutdown(struct device *dev); + +/** + * hdd_psoc_idle_restart - perform idle restart after idle shutdown + * @device: pointer to struct device + * + * Return: 0 for success non-zero error code for failure + */ +int hdd_psoc_idle_restart(struct device *dev); + +/** + * hdd_common_roam_callback() - common sme roam callback + * @psoc: Object Manager Psoc + * @session_id: session id for which callback is called + * @roam_info: pointer to roam info + * @roam_status: roam status + * @roam_result: roam result + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_common_roam_callback(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result); + +#ifdef WLAN_FEATURE_PKT_CAPTURE + +/** + * wlan_hdd_is_session_type_monitor() - check if session type is MONITOR + * @session_type: session type + * + * Return: True - if session type for adapter is monitor, else False + * + */ +bool wlan_hdd_is_session_type_monitor(uint8_t session_type); + +/** + * wlan_hdd_check_mon_concurrency() - check if MONITOR and STA concurrency + * is UP when packet capture mode is enabled. + * + * Return: True - if STA and monitor concurrency is there, else False + * + */ +bool wlan_hdd_check_mon_concurrency(void); + +/** + * wlan_hdd_add_monitor_check() - check for monitor intf and add if needed + * @hdd_ctx: pointer to hdd context + * @adapter: output pointer to hold created monitor adapter + * @type: type of the interface + * @name: name of the interface + * @rtnl_held: True if RTNL lock is held + * @name_assign_type: the name of assign type of the netdev + * + * Return: 0 - on success + * err code - on failure + */ +int wlan_hdd_add_monitor_check(struct hdd_context *hdd_ctx, + struct hdd_adapter **adapter, + enum nl80211_iftype type, const char *name, + bool rtnl_held, unsigned char name_assign_type); + +/** + * wlan_hdd_del_monitor() - delete monitor interface + * @hdd_ctx: pointer to hdd context + * @adapter: adapter to be deleted + * @rtnl_held: rtnl lock held + * + * This function is invoked to delete monitor interface. + * + * Return: None + */ +void wlan_hdd_del_monitor(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, bool rtnl_held); +#else +static inline +bool wlan_hdd_is_session_type_monitor(uint8_t session_type) +{ + return false; +} + +static inline +bool wlan_hdd_check_mon_concurrency(void) +{ + return false; +} + +static inline +int wlan_hdd_add_monitor_check(struct hdd_context *hdd_ctx, + struct hdd_adapter **adapter, + enum nl80211_iftype type, const char *name, + bool rtnl_held, unsigned char name_assign_type) +{ + return 0; +} + +static inline +void wlan_hdd_del_monitor(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, bool rtnl_held) +{ +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) && \ + defined(WLAN_FEATURE_11AX) +/** + * hdd_cleanup_conn_info() - Cleanup connectin info + * @adapter: Adapter upon which the command was received + * + * This function frees the memory allocated for the connection + * info structure + * + * Return: none + */ +void hdd_cleanup_conn_info(struct hdd_adapter *adapter); +/** + * hdd_sta_destroy_ctx_all() - cleanup all station contexts + * @hdd_ctx: Global HDD context + * + * This function destroys all the station contexts + * + * Return: none + */ +void hdd_sta_destroy_ctx_all(struct hdd_context *hdd_ctx); +#else +static inline void hdd_cleanup_conn_info(struct hdd_adapter *adapter) +{ +} +static inline void hdd_sta_destroy_ctx_all(struct hdd_context *hdd_ctx) +{ +} +#endif + +#ifdef FEATURE_MONITOR_MODE_SUPPORT + +void hdd_sme_monitor_mode_callback(uint8_t vdev_id); + +QDF_STATUS hdd_monitor_mode_vdev_status(struct hdd_adapter *adapter); + +QDF_STATUS hdd_monitor_mode_qdf_create_event(struct hdd_adapter *adapter, + uint8_t session_type); +#else +static inline void hdd_sme_monitor_mode_callback(uint8_t vdev_id) {} + +static inline QDF_STATUS +hdd_monitor_mode_vdev_status(struct hdd_adapter *adapter) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +hdd_monitor_mode_qdf_create_event(struct hdd_adapter *adapter, + uint8_t session_type) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * hdd_init_start_completion() - Init the completion variable to wait on ON/OFF + * + * Return: None + */ +void hdd_init_start_completion(void); + +/** + * hdd_netdev_feature_update - Update the netdev features + * @net_dev: Handle to net_device + * + * This func holds the rtnl_lock. Do not call with rtnl_lock held. + * + * Return: None + */ +void hdd_netdev_update_features(struct hdd_adapter *adapter); + +#endif /* end #if !defined(WLAN_HDD_MAIN_H) */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_misc.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_misc.h new file mode 100644 index 0000000000000000000000000000000000000000..b105df1b3a98372ebf50d6192d8eb7bf49a9aa9c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_misc.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2012-2014,2016-2017,2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_HDD_MISC_H +#define WLAN_HDD_MISC_H +/* + * To prevent name conflicts when loading different instances of the driver: + * + * If DYNAMIC_SINGLE_CHIP is defined, which means there are multiple possible + * drivers, but only one instance of driver at a time(WLAN dynamic detect), + * prepend DYNAMIC_SINGLE_CHIP to the filenames. + * + * Otherwise, if MULTI_IF_NAME is defined, which means there are multiple + * instances of the driver with different module names, prepend MULTI_IF_NAME + * to the filenames. + */ +#ifdef DYNAMIC_SINGLE_CHIP +#define PREFIX DYNAMIC_SINGLE_CHIP "/" +#else + +#ifdef MULTI_IF_NAME +#define PREFIX MULTI_IF_NAME "/" +#else +#define PREFIX "" +#endif + +#endif + +#ifdef MSM_PLATFORM +#define WLAN_INI_FILE "wlan/qca_cld/" PREFIX "WCNSS_qcom_cfg.ini" +#define WLAN_MAC_FILE "wlan/qca_cld/" PREFIX "wlan_mac.bin" +#else +#define WLAN_INI_FILE "wlan/" PREFIX "qcom_cfg.ini" +#define WLAN_MAC_FILE "wlan/" PREFIX "wlan_mac.bin" +#endif /* MSM_PLATFORM */ + +#endif /* WLAN_HDD_MISC_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_mpta_helper.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_mpta_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..4839558a61eea1610f893e9edb2bb6950adcc8dd --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_mpta_helper.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_mpta_helper.h + * + * Add Vendor subcommand QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG + */ + +#ifndef __WLAN_HDD_MPTA_HELPER_H +#define __WLAN_HDD_MPTA_HELPER_H + +#include "wlan_fw_offload_main.h" + +#ifdef FEATURE_MPTA_HELPER +#include + +/** + * wlan_hdd_mpta_helper_enable() - enable/disable mpta helper + * according to cfg from INI + * @coex_cfg_params: pointer of coex config command params + * @config: pointer of BTC config items + * + * Return: 0 on success; error number otherwise. + * + */ +int +wlan_hdd_mpta_helper_enable(struct coex_config_params *coex_cfg_params, + struct wlan_fwol_coex_config *config); + +/** + * wlan_hdd_cfg80211_mpta_helper_config() - update + * tri-radio coex status by mpta helper + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Return: 0 on success; error number otherwise. + * + */ +int wlan_hdd_cfg80211_mpta_helper_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_MPTA_HELPER_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG,\ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_mpta_helper_config \ +}, + +#else /* FEATURE_MPTA_HELPER */ +#define FEATURE_MPTA_HELPER_COMMANDS + +static inline int +wlan_hdd_mpta_helper_enable(struct coex_config_params *coex_cfg_params, + struct wlan_fwol_coex_config *config) +{ + return 0; +} + +#endif /* FEATURE_MPTA_HELPER */ + +#endif /* __WLAN_HDD_MPTA_HELPER_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_nan.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_nan.h new file mode 100644 index 0000000000000000000000000000000000000000..bd75c95aa32a52e82276da5a631bbbf7083413b8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_nan.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_NAN_H +#define __WLAN_HDD_NAN_H + +/** + * DOC: wlan_hdd_nan.h + * + * WLAN Host Device Driver NAN API specification + */ + +struct hdd_context; + +#ifdef WLAN_FEATURE_NAN +struct wiphy; +struct wireless_dev; + +bool wlan_hdd_nan_is_supported(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_cfg80211_nan_ext_request() - handle NAN Extended request + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This function is called by userspace to send a NAN request to + * firmware. This is an SSR-protected wrapper function. + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_nan_ext_request(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_NAN_VENDOR_COMMANDS \ + { \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NAN_EXT, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_nan_ext_request \ + }, \ + { \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NDP, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_process_ndp_cmd \ + }, +#else /* WLAN_FEATURE_NAN */ +#define FEATURE_NAN_VENDOR_COMMANDS + +static inline bool wlan_hdd_nan_is_supported(struct hdd_context *hdd_ctx) +{ + return false; +} +#endif /* WLAN_FEATURE_NAN */ +#endif /* __WLAN_HDD_NAN_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_napi.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_napi.h new file mode 100644 index 0000000000000000000000000000000000000000..9e008218a3dcb96160a4ed286ad545f6c283abc0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_napi.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __HDD_NAPI_H__ +#define __HDD_NAPI_H__ + +#ifdef FEATURE_NAPI +/** + * DOC: wlan_hdd_napi.h + * + * WLAN NAPI interface module headers + */ + +/* CLD headers */ +#include "hif_napi.h" + +/* Linux headers */ +#include /* net_device */ + +struct hdd_context; + +#define HDD_NAPI_ANY (-1) + +int hdd_napi_enabled(int id); +int hdd_napi_create(void); +int hdd_napi_destroy(int force); +int hdd_display_napi_stats(void); +int hdd_clear_napi_stats(void); + +/* the following triggers napi_enable/disable as required */ +int hdd_napi_event(enum qca_napi_event event, void *data); + +int hdd_napi_poll(struct napi_struct *napi, int budget); + +struct qca_napi_data *hdd_napi_get_all(void); + +#if defined HELIUMPLUS && defined MSM_PLATFORM +int hdd_napi_apply_throughput_policy(struct hdd_context *hddctx, + uint64_t tx_packets, + uint64_t rx_packets); +int hdd_napi_serialize(int is_on); +#else +static inline int hdd_napi_apply_throughput_policy(struct hdd_context *hddctx, + uint64_t tx_packets, + uint64_t rx_packets) +{ + return 0; +} +static inline int hdd_napi_serialize(int is_on) +{ + return -EINVAL; +} +#endif /* HELIUMPLUS && MSM_PLATFORM */ + +#else /* ! defined(FEATURE_NAPI) */ +#include "hif_napi.h" +/** + * Stub API + * + */ + +#define HDD_NAPI_ANY (-1) + +static inline int hdd_napi_enabled(int id) { return 0; } +static inline int hdd_napi_create(void) { return 0; } +static inline int hdd_napi_destroy(int force) { return 0; } +static inline int hdd_display_napi_stats(void) { return 0; } +static inline int hdd_clear_napi_stats(void) { return 0; } +static inline int hdd_napi_event(enum qca_napi_event event, void *data) +{ + return 0; +} +static inline struct qca_napi_data *hdd_napi_get_all(void) { return NULL; } +static inline int hdd_napi_apply_throughput_policy(void *hdd_ctx, + uint64_t tx_packets, uint64_t rx_packets) +{ + return 0; +} + +static inline int hdd_napi_serialize(int is_on) +{ + return -EINVAL; +} +#endif /* FEATURE_NAPI */ + +#endif /* HDD_NAPI_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_oemdata.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_oemdata.h new file mode 100644 index 0000000000000000000000000000000000000000..49157f150db68f071cc953d31837a9e10d02e789 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_oemdata.h @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_oemdata.h + * + * Internal includes for the oem data + */ + +#ifndef __WLAN_HDD_OEM_DATA_H__ +#define __WLAN_HDD_OEM_DATA_H__ + +#include "wmi_unified_param.h" + +struct hdd_context; + +#ifdef FEATURE_OEM_DATA +#define WLAN_WAIT_TIME_GET_OEM_DATA 1000 +#endif +#ifdef FEATURE_OEM_DATA_SUPPORT + +#ifndef OEM_DATA_REQ_SIZE +#define OEM_DATA_REQ_SIZE 500 +#endif + +#ifndef OEM_DATA_RSP_SIZE +#define OEM_DATA_RSP_SIZE 1724 +#endif + +#define OEM_APP_SIGNATURE_LEN 16 +#define OEM_APP_SIGNATURE_STR "QUALCOMM-OEM-APP" + +#define OEM_TARGET_SIGNATURE_LEN 8 +#define OEM_TARGET_SIGNATURE "QUALCOMM" + +#define OEM_CAP_MAX_NUM_CHANNELS 128 + +/** + * enum oem_err_code - OEM error codes + * @OEM_ERR_NULL_CONTEXT: %NULL context + * @OEM_ERR_APP_NOT_REGISTERED: OEM App is not registered + * @OEM_ERR_INVALID_SIGNATURE: Invalid signature + * @OEM_ERR_NULL_MESSAGE_HEADER: Invalid message header + * @OEM_ERR_INVALID_MESSAGE_TYPE: Invalid message type + * @OEM_ERR_INVALID_MESSAGE_LENGTH: Invalid length in message body + */ +enum oem_err_code { + OEM_ERR_NULL_CONTEXT = 1, + OEM_ERR_APP_NOT_REGISTERED, + OEM_ERR_INVALID_SIGNATURE, + OEM_ERR_NULL_MESSAGE_HEADER, + OEM_ERR_INVALID_MESSAGE_TYPE, + OEM_ERR_INVALID_MESSAGE_LENGTH +}; + +/** + * struct driver_version - Driver version identifier (w.x.y.z) + * @major: Version ID major number + * @minor: Version ID minor number + * @patch: Version ID patch number + * @build: Version ID build number + */ +struct driver_version { + uint8_t major; + uint8_t minor; + uint8_t patch; + uint8_t build; +}; + +/** + * struct oem_data_cap - OEM Data Capabilities + * @oem_target_signature: Signature of chipset vendor, e.g. QUALCOMM + * @oem_target_type: Chip type + * @oem_fw_version: Firmware version + * @driver_version: Host software version + * @allowed_dwell_time_min: Channel dwell time - allowed minimum + * @allowed_dwell_time_max: Channel dwell time - allowed maximum + * @curr_dwell_time_min: Channel dwell time - current minimim + * @curr_dwell_time_max: Channel dwell time - current maximum + * @supported_bands: Supported bands, 2.4G or 5G Hz + * @num_channels: Num of channels IDs to follow + * @channel_list: List of channel IDs + */ +struct oem_data_cap { + uint8_t oem_target_signature[OEM_TARGET_SIGNATURE_LEN]; + uint32_t oem_target_type; + uint32_t oem_fw_version; + struct driver_version driver_version; + uint16_t allowed_dwell_time_min; + uint16_t allowed_dwell_time_max; + uint16_t curr_dwell_time_min; + uint16_t curr_dwell_time_max; + uint16_t supported_bands; + uint16_t num_channels; + uint8_t channel_list[OEM_CAP_MAX_NUM_CHANNELS]; +}; + +/** + * struct hdd_channel_info - Channel information + * @reserved0: reserved for padding and future use + * @mhz: primary 20 MHz channel frequency in mhz + * @band_center_freq1: Center frequency 1 in MHz + * @band_center_freq2: Center frequency 2 in MHz, valid only for 11ac + * VHT 80+80 mode + * @info: channel info + * @reg_info_1: regulatory information field 1 which contains min power, + * max power, reg power and reg class id + * @reg_info_2: regulatory information field 2 which contains antennamax + */ +struct hdd_channel_info { + uint32_t reserved0; + uint32_t mhz; + uint32_t band_center_freq1; + uint32_t band_center_freq2; + uint32_t info; + uint32_t reg_info_1; + uint32_t reg_info_2; +}; + +/** + * struct peer_status_info - Status information for a given peer + * @peer_mac_addr: peer mac address + * @peer_status: peer status: 1: CONNECTED, 2: DISCONNECTED + * @vdev_id: vdev_id for the peer mac + * @peer_capability: peer capability: 0: RTT/RTT2, 1: RTT3. Default is 0 + * @reserved0: reserved0 + * @peer_chan_info: channel info on which peer is connected + */ +struct peer_status_info { + uint8_t peer_mac_addr[ETH_ALEN]; + uint8_t peer_status; + uint8_t vdev_id; + uint32_t peer_capability; + uint32_t reserved0; + struct hdd_channel_info peer_chan_info; +}; + +/** + * enum oem_capability_mask - mask field for userspace client capabilities + * @OEM_CAP_RM_FTMRR: FTM range report mask bit + * @OEM_CAP_RM_LCI: LCI capability mask bit + */ +enum oem_capability_mask { + OEM_CAP_RM_FTMRR = (1 << (0)), + OEM_CAP_RM_LCI = (1 << (1)), +}; + +/** + * struct oem_get_capability_rsp - capabilities set by userspace and target. + * @target_cap: target capabilities + * @client_capabilities: capabilities set by userspace via set request + */ +struct oem_get_capability_rsp { + struct oem_data_cap target_cap; + struct sme_oem_capability cap; +}; + +/** + * hdd_send_peer_status_ind_to_oem_app() - + * Function to send peer status to a registered application + * @peer_mac: MAC address of peer + * @peer_status: ePeerConnected or ePeerDisconnected + * @peer_capability: 0: RTT/RTT2, 1: RTT3. Default is 0 + * @vdev_id: vdev_id + * @chan_info: operating channel information + * @dev_mode: dev mode for which indication is sent + * + * Return: none + */ +void hdd_send_peer_status_ind_to_oem_app(struct qdf_mac_addr *peer_mac, + uint8_t peer_status, + uint8_t peer_capability, + uint8_t vdev_id, + struct oem_channel_info *chan_info, + enum QDF_OPMODE dev_mode); + +int iw_get_oem_data_cap(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +/** + * oem_activate_service() - API to register the oem command handler + * @hdd_ctx: Pointer to HDD Context + * + * This API is used to register the handler to receive netlink message + * from an OEM application process + * + * Return: 0 on success and errno on failure + */ +int oem_activate_service(struct hdd_context *hdd_ctx); + +/** + * oem_deactivate_service() - API to unregister the oem command handler + * + * This API is used to deregister the handler to receive netlink message + * from an OEM application process + * + * Return: 0 on success and errno on failure + */ +int oem_deactivate_service(void); + +void hdd_send_oem_data_rsp_msg(struct oem_data_rsp *oem_rsp); + +/** + * update_channel_bw_info() - set bandwidth info for the chan + * @hdd_ctx: hdd context + * @chan_freq: channel freq for which info are required + * @chan_info: struct where the bandwidth info is filled + * + * This function finds the maximum bandwidth allowed, secondary + * channel offset and center freq for the channel as per regulatory + * domain and uses these info calculate the phy mode for the + * channel. + * + * Return: void + */ +void hdd_update_channel_bw_info(struct hdd_context *hdd_ctx, + uint32_t chan_freq, + void *hdd_chan_info); +#else +static inline int oem_activate_service(struct hdd_context *hdd_ctx) +{ + return 0; +} + +static inline int oem_deactivate_service(void) +{ + return 0; +} + +static inline void hdd_send_oem_data_rsp_msg(void *oem_rsp) {} + +static inline void hdd_update_channel_bw_info(struct hdd_context *hdd_ctx, + uint32_t chan_freq, + void *hdd_chan_info) {} +#endif /* FEATURE_OEM_DATA_SUPPORT */ + +#ifdef FEATURE_OEM_DATA +#define OEM_DATA_MAX_SIZE 1024 +/** + * wlan_hdd_cfg80211_oem_data_handler() - the handler for oem data + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +int wlan_hdd_cfg80211_oem_data_handler(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +#define FEATURE_OEM_DATA_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OEM_DATA, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_oem_data_handler \ +}, +#else +#define FEATURE_OEM_DATA_VENDOR_COMMANDS +#endif + +#ifdef FEATURE_OEM_DATA +/** + * hdd_oem_event_handler_cb() - callback for oem data event + * @oem_event_data: oem data received in the event from the FW + * @vdev_id: vdev id + * + * Return: None + */ +void hdd_oem_event_handler_cb(const struct oem_data *oem_event_data, + uint8_t vdev_id); +#else +static inline void hdd_oem_event_handler_cb(void *oem_event_data, + uint8_t vdev_id) +{ +} +#endif + +#endif /* __WLAN_HDD_OEM_DATA_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_p2p.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_p2p.h new file mode 100644 index 0000000000000000000000000000000000000000..39d80f10f8a5c2b8f2e9a7c2dda8c9344fe494b3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_p2p.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __P2P_H +#define __P2P_H + +/** + * DOC: wlan_hdd_p2p.h + * + * Linux HDD P2P include file + */ + +#define WLAN_HDD_GET_TYPE_FRM_FC(__fc__) (((__fc__) & 0x0F) >> 2) +#define WLAN_HDD_GET_SUBTYPE_FRM_FC(__fc__) (((__fc__) & 0xF0) >> 4) +#define WLAN_HDD_80211_FRM_DA_OFFSET 4 + +#define P2P_POWER_SAVE_TYPE_OPPORTUNISTIC (1 << 0) +#define P2P_POWER_SAVE_TYPE_PERIODIC_NOA (1 << 1) +#define P2P_POWER_SAVE_TYPE_SINGLE_NOA (1 << 2) + +struct p2p_app_set_ps { + uint8_t opp_ps; + uint32_t ct_window; + uint8_t count; + uint32_t duration; + uint32_t interval; + uint32_t single_noa_duration; + uint8_t ps_selection; +}; + +int wlan_hdd_cfg80211_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, u64 *cookie); + +int wlan_hdd_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie); + +int wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie); + +int hdd_set_p2p_ps(struct net_device *dev, void *msgData); +int hdd_set_p2p_opps(struct net_device *dev, uint8_t *command); +int hdd_set_p2p_noa(struct net_device *dev, uint8_t *command); + +/** + * hdd_indicate_mgmt_frame_to_user- send mgmt frame to user + * @adapter: adapter pointer + * @frm_len: frame length + * @pb_frames: frame bytes + * @frame_type: frame type + * @rx_freq: frequency on which frame was received + * @rx_rssi: rssi + * @rx_flags: rx flags of the frame + */ +void hdd_indicate_mgmt_frame_to_user(struct hdd_adapter *adapter, + uint32_t frm_len, uint8_t *pb_frames, + uint8_t frame_type, uint32_t rx_freq, + int8_t rx_rssi, + enum rxmgmt_flags rx_flags); + +int wlan_hdd_check_remain_on_channel(struct hdd_adapter *adapter); +void wlan_hdd_cancel_existing_remain_on_channel(struct hdd_adapter *adapter); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) +int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie); +#else +int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *chan, bool offchan, + unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + struct vif_params *params); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) || defined(WITH_BACKPORTS) +struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); +#else +struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy, + const char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); + +#endif + +int wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); +int __wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); + + +void wlan_hdd_cleanup_remain_on_channel_ctx(struct hdd_adapter *adapter); + +/** + * wlan_hdd_set_power_save() - hdd set power save + * @adapter: adapter context + * @ps_config: pointer to power save configure + * + * This function sets power save parameters. + * + * Return: 0 - success + * others - failure + */ +int wlan_hdd_set_power_save(struct hdd_adapter *adapter, + struct p2p_ps_config *ps_config); + +/** + * wlan_hdd_set_mas() - Function to set MAS value to FW + * @adapter: Pointer to HDD adapter + * @mas_value: 0-Disable, 1-Enable MAS + * + * This function passes down the value of MAS to FW + * + * Return: Configuration message posting status, SUCCESS or Fail + * + */ +int32_t wlan_hdd_set_mas(struct hdd_adapter *adapter, uint8_t mas_value); + +/** + * wlan_hdd_set_mcc_p2p_quota() - Function to set quota for P2P + * to FW + * @adapter: Pointer to HDD adapter + * @set_value: Quota value for the interface + * + * This function is used to set the quota for P2P cases + * + * Return: Configuration message posting status, SUCCESS or Fail + * + */ +int wlan_hdd_set_mcc_p2p_quota(struct hdd_adapter *adapter, + uint32_t set_value); + +/** + * wlan_hdd_go_set_mcc_p2p_quota() - Function to set quota for + * P2P GO to FW + * @hostapd_adapter: Pointer to HDD adapter + * @set_value: Quota value for the interface + * + * This function is used to set the quota for P2P GO cases + * + * Return: Configuration message posting status, SUCCESS or Fail + * + */ +int wlan_hdd_go_set_mcc_p2p_quota(struct hdd_adapter *hostapd_adapter, + uint32_t set_value); +/** + * wlan_hdd_set_mcc_latency() - Set MCC latency to FW + * @adapter: Pointer to HDD adapter + * @set_value: Latency value + * + * Sets the MCC latency value during STA-P2P concurrency + * + * Return: None + */ +void wlan_hdd_set_mcc_latency(struct hdd_adapter *adapter, int set_value); + +/** + * wlan_hdd_cleanup_actionframe() - Cleanup action frame + * @adapter: Pointer to HDD adapter + * + * This function cleans up action frame. + * + * Return: None + */ +void wlan_hdd_cleanup_actionframe(struct hdd_adapter *adapter); +#endif /* __P2P_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_packet_filter_api.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_packet_filter_api.h new file mode 100644 index 0000000000000000000000000000000000000000..dee4eb36d46a32b71e7690029030d32da0bb98d4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_packet_filter_api.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_PACKET_FILTER_API_H__) +#define WLAN_HDD_PACKET_FILTER_API_H__ + +/** + * DOC: wlan_hdd_packet_filter_rules.h + * + */ + +/* Include files */ +#include "wlan_hdd_assoc.h" +#include "wlan_hdd_power.h" + +#ifdef WLAN_FEATURE_PACKET_FILTERING + +/** + * hdd_enable_default_pkt_filters() - Enable default packet filters based + * on, filters bit map provided in INI, when target goes to suspend mode + * @adapter: Adapter context for which default filters to be configure + * + * Return: zero if success, non-zero otherwise + */ +int hdd_enable_default_pkt_filters(struct hdd_adapter *adapter); + +/** + * hdd_disable_default_pkt_filters() - Disable default packet filters based + * on, filters bit map provided in INI, when target resumes + * @adapter: Adapter context for which default filters to be cleared + * + * Return: zero if success, non-zero otherwise + */ +int hdd_disable_default_pkt_filters(struct hdd_adapter *adapter); + +/** + * wlan_hdd_set_filter() - Set packet filter + * @hdd_ctx: Global HDD context + * @request: Packet filter request struct + * @vdev_id: Target vdev for the request + * + * Return: 0 on success, non-zero on error + */ +int wlan_hdd_set_filter(struct hdd_context *hdd_ctx, + struct pkt_filter_cfg *request, + uint8_t vdev_id); + +#else /* WLAN_FEATURE_PACKET_FILTERING */ + +static inline int +hdd_enable_default_pkt_filters(struct hdd_adapter *adapter) +{ + return 0; +} + +static inline int +hdd_disable_default_pkt_filters(struct hdd_adapter *adapter) +{ + return 0; +} + +#endif /* WLAN_FEATURE_PACKET_FILTERING */ +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_packet_filter_rules.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_packet_filter_rules.h new file mode 100644 index 0000000000000000000000000000000000000000..8d2bbfc92ebb0953462c8ed66f689ebb17932d9c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_packet_filter_rules.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_PACKET_FILTER_RULES_H__) +#define WLAN_HDD_PACKET_FILTER_RULES_H__ + +/** + * DOC: wlan_hdd_packet_filter_rules.h + * + */ + +/* Include files */ + +#define MAX_NUM_PACKET_FILTERS 6 + +/** + * @filter_action: Filter action, set/clear the filter + * Ex: filter_action = 1 set the filter + * filter_action = 2 clear the filter + * @filter_id: Filter id Ex: 1/2/3/4/5 .... MAX_NUM_FILTERS + * @num_params: Number of parameters Ex: 1/2/3/4/5 + * @params_data: Packet filter parameters details + * + * @protocol_layer: the type of protocol layer header to which the data + * being configured correspond + * Ex: protocol_layer = 1 - MAC Header + * protocol_layer = 2 - ARP Header + * protocol_layer = 3 - IP Header + * @compare_flag: comparison type + * EX: compare_flag = 0 - comparison is invalid + * compare_flag = 1 - compare for equality of the data present in received + * packet to the corresponding configured data + * compare_flag = 2 - compare for equality of the data present in received + * packet to the corresponding configured data after + * applying the mask + * compare_flag = 3 - compare for non-equality of the data present in + * received packet to the corresponding configured data + * compare_flag = 4 - compare for non-equality of the data present in + * received packet to the corresponding configured data + * after applying the mask + * @data_fffset: Offset of the data to compare from the respective protocol + * layer header start (as per the respective protocol + * specification) in terms of bytes + * @data_length: length of data to compare + * @compare_data: Array of 8 bytes + * @data_mask: Mask to be applied on the received packet data (Array of 8 bytes) + */ +static struct pkt_filter_cfg + packet_filter_default_rules[MAX_NUM_PACKET_FILTERS] = { + { .filter_action = 1, + .filter_id = 0, + .num_params = 3, + .params_data = { + { .protocol_layer = 1, + .compare_flag = 5, + .data_offset = 0, + .data_length = 1, + .compare_data = {1, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} }, + { .protocol_layer = 2, + .compare_flag = 3, + .data_offset = 6, + .data_length = 2, + .compare_data = {134, 221, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} }, + { .protocol_layer = 3, + .compare_flag = 4, + .data_offset = 24, + .data_length = 2, + .compare_data = {255, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {255, 0, 0, 0, 0, 0, 0, 0} } } }, + + { .filter_action = 1, + .filter_id = 0, + .num_params = 3, + .params_data = { + { .protocol_layer = 1, + .compare_flag = 5, + .data_offset = 0, + .data_length = 1, + .compare_data = {1, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} }, + { .protocol_layer = 2, + .compare_flag = 3, + .data_offset = 6, + .data_length = 2, + .compare_data = {8, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} }, + { .protocol_layer = 3, + .compare_flag = 4, + .data_offset = 16, + .data_length = 1, + .compare_data = {224, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {240, 0, 0, 0, 0, 0, 0, 0} } } }, + { .filter_action = 1, + .filter_id = 0, + .num_params = 3, + .params_data = { + { .protocol_layer = 1, + .compare_flag = 5, + .data_offset = 0, + .data_length = 1, + .compare_data = {1, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} }, + { .protocol_layer = 2, + .compare_flag = 3, + .data_offset = 6, + .data_length = 2, + .compare_data = {8, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} }, + { .protocol_layer = 3, + .compare_flag = 4, + .data_offset = 18, + .data_length = 2, + .compare_data = {0, 255, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 255, 0, 0, 0, 0, 0, 0} } } }, + { .filter_action = 1, + .filter_id = 0, + .num_params = 2, + .params_data = { + { .protocol_layer = 1, + .compare_flag = 5, + .data_offset = 0, + .data_length = 1, + .compare_data = {1, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} }, + { .protocol_layer = 2, + .compare_flag = 3, + .data_offset = 8, + .data_length = 4, + .compare_data = {0, 1, 175, 129, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} } } }, + { .filter_action = 1, + .filter_id = 0, + .num_params = 2, + .params_data = { + { .protocol_layer = 1, + .compare_flag = 5, + .data_offset = 0, + .data_length = 1, + .compare_data = {1, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} }, + { .protocol_layer = 2, + .compare_flag = 3, + .data_offset = 6, + .data_length = 2, + .compare_data = {0, 39, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} } } }, + { .filter_action = 1, + .filter_id = 0, + .num_params = 2, + .params_data = { + { .protocol_layer = 1, + .compare_flag = 5, + .data_offset = 0, + .data_length = 1, + .compare_data = {1, 0, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} }, + { .protocol_layer = 2, + .compare_flag = 3, + .data_offset = 4, + .data_length = 2, + .compare_data = {0, 12, 0, 0, 0, 0, 0, 0}, + .data_mask = {0, 0, 0, 0, 0, 0, 0, 0} } } } }; +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_periodic_sta_stats.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_periodic_sta_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..61725404d760f2dee002d1c67e66fd84512e5121 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_periodic_sta_stats.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_periodic_sta_stats.h + * + * WLAN Host Device Driver periodic STA statistics related implementation + * + */ + +#if !defined(WLAN_HDD_PERIODIC_STA_STATS_H) +#define WLAN_HDD_PERIODIC_STA_STATS_H + +#ifdef WLAN_FEATURE_PERIODIC_STA_STATS +/* + * Used to get device name from the adapter + */ +#define WLAN_HDD_GET_DEV_NAME(adapter) ((adapter)->dev->name) + +/** + * hdd_periodic_sta_stats_config() - Initialize periodic stats configuration + * @config: Pointer to hdd configuration + * @psoc: Pointer to psoc + * + * Return: none + */ +void hdd_periodic_sta_stats_config(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc); + +/** + * hdd_periodic_sta_stats_init() - Initialize periodic stats display flag + * @adapter: Pointer to the station adapter + * + * Return: none + */ +void hdd_periodic_sta_stats_init(struct hdd_adapter *adapter); + +/** + * hdd_periodic_sta_stats_display() - Display periodic stats at STA + * @hdd_ctx: hdd context + * + * Return: none + */ +void hdd_periodic_sta_stats_display(struct hdd_context *hdd_ctx); + +/** + * hdd_periodic_sta_stats_start() - Start displaying periodic stats for STA + * @adapter: Pointer to the station adapter + * + * Return: none + */ +void hdd_periodic_sta_stats_start(struct hdd_adapter *adapter); + +/** + * hdd_periodic_sta_stats_stop() - Stop displaying periodic stats for STA + * @adapter: Pointer to the station adapter + * + * Return: none + */ +void hdd_periodic_sta_stats_stop(struct hdd_adapter *adapter); + +/** + * hdd_periodic_sta_stats_mutex_create() - Create mutex for STA periodic stats + * @adapter: Pointer to the station adapter + * + * Return: none + */ +void hdd_periodic_sta_stats_mutex_create(struct hdd_adapter *adapter); + +/** + * hdd_periodic_sta_stats_mutex_destroy() - Destroy STA periodic stats mutex + * @adapter: Pointer to the station adapter + * + * Return: none + */ +void hdd_periodic_sta_stats_mutex_destroy(struct hdd_adapter *adapter); + +#else +static inline void +hdd_periodic_sta_stats_display(struct hdd_context *hdd_ctx) {} + +static inline void +hdd_periodic_sta_stats_config(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) {} + +static inline void hdd_periodic_sta_stats_start(struct hdd_adapter *adapter) {} + +static inline void hdd_periodic_sta_stats_stop(struct hdd_adapter *adapter) {} + +static inline void +hdd_periodic_sta_stats_init(struct hdd_adapter *adapter) {} + +static inline void +hdd_periodic_sta_stats_mutex_create(struct hdd_adapter *adapter) {} + +static inline void +hdd_periodic_sta_stats_mutex_destroy(struct hdd_adapter *adapter) {} + +#endif /* end #ifdef WLAN_FEATURE_PERIODIC_STA_STATS */ + +#endif /* end #if !defined(WLAN_HDD_PERIODIC_STA_STATS_H) */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_power.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_power.h new file mode 100644 index 0000000000000000000000000000000000000000..7c9aa63092a035bec8a2e9db60d2960a3d9529ab --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_power.h @@ -0,0 +1,627 @@ +/* + * Copyright (c) 2012, 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_POWER_H +#define __WLAN_HDD_POWER_H + +/** + * DOC: wlan_hdd_power.h + * + * HDD Power Management API + */ + +#include "wlan_hdd_main.h" +#include +#include + +#define HDD_WAKELOCK_TIMEOUT_CONNECT 1000 +#define HDD_WAKELOCK_TIMEOUT_RESUME 1000 + +/* + * HDD_WAKELOCK_CONNECT_COMPLETE = CSR_JOIN_FAILURE_TIMEOUT_DEFAULT (3000) + + * WNI_CFG_AUTHENTICATE_FAILURE_TIMEOUT_STADEF (1000) + + * WNI_CFG_ASSOCIATION_FAILURE_TIMEOUT_STADEF (2000) + */ +#define HDD_WAKELOCK_CONNECT_COMPLETE 6000 + +#ifdef WLAN_FEATURE_PACKET_FILTERING + +#define HDD_MAX_CMP_PER_PACKET_FILTER 5 + +/** + * enum pkt_filter_protocol_layer - packet filter protocol layer + * @HDD_FILTER_PROTO_TYPE_INVALID: Invalid initial value + * @HDD_FILTER_PROTO_TYPE_MAC: MAC protocol + * @HDD_FILTER_PROTO_TYPE_ARP: ARP protocol + * @HDD_FILTER_PROTO_TYPE_IPV4: IP V4 protocol + * @HDD_FILTER_PROTO_TYPE_IPV6: IP V6 protocol + * @HDD_FILTER_PROTO_TYPE_UDP: UDP protocol + * @HDD_FILTER_PROTO_TYPE_INVALID: Max place holder value + */ +enum pkt_filter_protocol_layer { + HDD_FILTER_PROTO_TYPE_INVALID = 0, + HDD_FILTER_PROTO_TYPE_MAC = 1, + HDD_FILTER_PROTO_TYPE_ARP = 2, + HDD_FILTER_PROTO_TYPE_IPV4 = 3, + HDD_FILTER_PROTO_TYPE_IPV6 = 4, + HDD_FILTER_PROTO_TYPE_UDP = 5, + HDD_FILTER_PROTO_TYPE_MAX +}; + +/** + * enum pkt_filter_action - packet filter action + * @HDD_RCV_FILTER_INVALID: Invalid initial value + * @HDD_RCV_FILTER_SET: Packet filter set + * @HDD_RCV_FILTER_CLEAR: Packet filter clear + * @HDD_RCV_FILTER_MAX: Max place holder value + */ +enum pkt_filter_action { + HDD_RCV_FILTER_INVALID = 0, + HDD_RCV_FILTER_SET = 1, + HDD_RCV_FILTER_CLEAR = 2, + HDD_RCV_FILTER_MAX +}; + +/** + * enum pkt_filter_compare_flag - packet filter compare flag + * @HDD_FILTER_CMP_TYPE_INVALID: Invalid initial value + * @HDD_FILTER_CMP_TYPE_EQUAL: Compare if filter is equal + * @HDD_FILTER_CMP_TYPE_MASK_EQUAL: Compare if filter mask is equal + * @HDD_FILTER_CMP_TYPE_NOT_EQUAL: Compare if filter is not equal + * @HDD_FILTER_CMP_TYPE_MASK_NOT_EQUAL: Compare if filter mask is not equal + * @HDD_FILTER_CMP_TYPE_MAX: Max place holder value + */ +enum pkt_filter_compare_flag { + HDD_FILTER_CMP_TYPE_INVALID = 0, + HDD_FILTER_CMP_TYPE_EQUAL = 1, + HDD_FILTER_CMP_TYPE_MASK_EQUAL = 2, + HDD_FILTER_CMP_TYPE_NOT_EQUAL = 3, + HDD_FILTER_CMP_TYPE_MASK_NOT_EQUAL = 4, + HDD_FILTER_CMP_TYPE_MAX +}; + +/** + * struct pkt_filter_param_cfg - packet filter parameter config + * @protocol_layer: Protocol layer + * @compare_flag: Compare flag + * @data_fffset: Data offset + * @data_length: Data length + * @compare_data: Compare data + * @data_mask: Data mask + */ +struct pkt_filter_param_cfg { + uint8_t protocol_layer; + uint8_t compare_flag; + uint8_t data_offset; + uint8_t data_length; + uint8_t compare_data[SIR_MAX_FILTER_TEST_DATA_LEN]; + uint8_t data_mask[SIR_MAX_FILTER_TEST_DATA_LEN]; +}; + +/** + * struct pkt_filter_cfg - packet filter config received from user space + * @filter_action: Filter action + * @filter_id: Filter id + * @num_params: Number of parameters + * @params_data: Packet filter parameters detail + */ +struct pkt_filter_cfg { + uint8_t filter_action; + uint8_t filter_id; + uint8_t num_params; + struct pkt_filter_param_cfg params_data[HDD_MAX_CMP_PER_PACKET_FILTER]; +}; + +#endif + +#ifdef FEATURE_ANI_LEVEL_REQUEST +/** + * ani_priv - structure to store the priv data for get ani request + * @num_freq: number of freq received from the FW + * @ani: data received from the FW + */ +struct ani_priv { + uint32_t num_freq; + struct wmi_host_ani_level_event *ani; +}; +#endif + +/** + * enum suspend_resume_state - Suspend resume state + * @HDD_WLAN_EARLY_SUSPEND: Early suspend state. + * @HDD_WLAN_SUSPEND: Suspend state. + * @HDD_WLAN_EARLY_RESUME: Early resume state. + * @HDD_WLAN_RESUME: Resume state. + * + * Suspend state to indicate in diag event of suspend resume. + */ +enum suspend_resume_state { + HDD_WLAN_EARLY_SUSPEND, + HDD_WLAN_SUSPEND, + HDD_WLAN_EARLY_RESUME, + HDD_WLAN_RESUME +}; + +/** + * hdd_svc_fw_shutdown_ind() - API to send FW SHUTDOWN IND to Userspace + * @dev: Device Pointer + * + * Return: None + */ +void hdd_svc_fw_shutdown_ind(struct device *dev); + +/** + * hdd_wlan_shutdown() - HDD SSR shutdown function + * + * This function is called by the HIF to shutdown the driver during SSR. + * + * Return: QDF_STATUS_SUCCESS if the driver was shut down, + * or an error status otherwise + */ +QDF_STATUS hdd_wlan_shutdown(void); + +/** + * hdd_wlan_re_init() - HDD SSR re-init function + * + * This function is called by the HIF to re-initialize the driver after SSR. + * + * Return: QDF_STATUS_SUCCESS if the driver was re-initialized, + * or an error status otherwise + */ +QDF_STATUS hdd_wlan_re_init(void); + +/** + * hdd_enable_arp_offload() - API to enable ARP offload + * @adapter: Adapter context for which ARP offload is to be configured + * @trigger: trigger reason for request + * + * Return: None + */ +void hdd_enable_arp_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger); + +/** + * hdd_disable_arp_offload() - API to disable ARP offload + * @adapter: Adapter context for which ARP offload is to be configured + * @trigger: trigger reason for request + * + * Return: None + */ +void hdd_disable_arp_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger); + +/** + * hdd_enable_host_offloads() - Central API to enable the supported offloads + * @adapter: pointer to the adapter + * @trigger: trigger reason for request + * + * Central function to enable the supported offloads + * + * Return: nothing + */ +void hdd_enable_host_offloads(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger); + +/** + * hdd_disable_host_offloads() - Central API to disable the supported offloads + * @adapter: pointer to the adapter + * @trigger: trigger reason for request + * + * Central function to disable the supported offloads + * + * Return: nothing + */ +void hdd_disable_host_offloads(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger); + +/** + * hdd_enable_mc_addr_filtering() - enable MC address list in FW + * @adapter: adapter whose MC list is being set + * @trigger: trigger reason for request + * + * Return: nothing + */ +void hdd_enable_mc_addr_filtering(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger); + +/** + * hdd_disable_mc_addr_filtering() - disable MC address list in FW + * @adapter: adapter whose MC list is being set + * @trigger: trigger reason for request + * + * Return: nothing + */ +void hdd_disable_mc_addr_filtering(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger); + +/** + * hdd_cache_mc_addr_list() - API to cache MC address list + * @mc_list_config: set of mc address list configurations + * + * Return: 0 on success else error code + */ +int hdd_cache_mc_addr_list(struct pmo_mc_addr_list_params *mc_list_config); + +/** + * hdd_disable_and_flush_mc_addr_list() - API to Disable & Flush cached MC list + * @adapter: adapter whose MC list is being set + * @trigger: trigger reason for request + * + * Return: nothing + */ +void hdd_disable_and_flush_mc_addr_list(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger); + +/** + * wlan_hdd_cfg80211_update_replay_counter_cb() - replay counter callback + * @cb_ctx: Callback context as void* as PMO do not about HDD adapter type + * @gtk_rsp_param: Pointer to gtk offload response parameter + * + * Callback routine called upon receiving of gtk offload rsp from fwr + * + * Return: none + */ +void wlan_hdd_cfg80211_update_replay_counter_cb( + void *cb_ctx, + struct pmo_gtk_rsp_params *gtk_rsp_param); + +/** + * wlan_hdd_cfg80211_suspend_wlan() - cfg80211 suspend callback + * @wiphy: Pointer to wiphy + * @wow: Pointer to wow + * + * This API is called when cfg80211 driver suspends + * + * Return: integer status + */ +int wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy, + struct cfg80211_wowlan *wow); + +/** + * wlan_hdd_cfg80211_resume_wlan() - cfg80211 resume callback + * @wiphy: Pointer to wiphy + * + * This API is called when cfg80211 driver resumes driver updates + * latest sched_scan scan result(if any) to cfg80211 database + * + * Return: integer status + */ +int wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy); + +/** + * hdd_ipv4_notifier_work_queue() - IP V4 change notifier work handler + * @work: Pointer to work context + * + * Return: none + */ +void hdd_ipv4_notifier_work_queue(struct work_struct *work); + +#ifdef WLAN_NS_OFFLOAD +/** + * hdd_enable_ns_offload() - enable NS offload + * @adapter: pointer to the adapter + * + * Return: nothing + */ +void hdd_enable_ns_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger); + +/** + * hdd_disable_ns_offload() - disable NS offload + * @adapter: pointer to the adapter + * + * Return: nothing + */ +void hdd_disable_ns_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger); +#else /* WLAN_NS_OFFLOAD */ +static inline +void hdd_enable_ns_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ +} + +static inline +void hdd_disable_ns_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ +} +#endif /* WLAN_NS_OFFLOAD */ + +/** + * hdd_ipv6_notifier_work_queue() - IP V6 change notifier work handler + * @work: Pointer to work context + * + * Return: none + */ +void hdd_ipv6_notifier_work_queue(struct work_struct *work); + +/** + * wlan_hdd_cfg80211_get_txpower() - cfg80211 get power handler function + * @wiphy: Pointer to wiphy structure. + * @wdev: Pointer to wireless_dev structure. + * @dbm: dbm + * + * This is the cfg80211 get txpower handler function which invokes + * the internal function @__wlan_hdd_cfg80211_get_txpower with + * SSR protection. + * + * Return: 0 for success, error number on failure. + */ +int wlan_hdd_cfg80211_get_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm); + +/** + * wlan_hdd_cfg80211_set_txpower() - set TX power + * @wiphy: Pointer to wiphy + * @wdev: Pointer to network device + * @type: TX power setting type + * @dbm: TX power in dbm + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_cfg80211_set_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, + int dbm); + +/** + * wlan_hdd_cfg80211_set_power_mgmt() - set cfg80211 power management config + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @allow_power_save: is wlan allowed to go into power save mode + * @timeout: Timeout value + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool allow_power_save, + int timeout); + +/** + * wlan_hdd_ipv4_changed() - IPv4 change notifier callback + * @nb: pointer to notifier block + * @data: data + * @arg: arg + * + * This is the IPv4 notifier callback function gets invoked + * if any change in IP and then invoke the function @__wlan_hdd_ipv4_changed + * to reconfigure the offload parameters. + * + * Return: 0 on success, error number otherwise. + */ +int wlan_hdd_ipv4_changed(struct notifier_block *nb, + unsigned long data, void *arg); + +#ifdef FEATURE_RUNTIME_PM +/** + * wlan_hdd_pm_qos_notify() - PM QOS notifier call back function + * @nb: Pointer to notifier block kernel structure + * @curr_val: PM QOS current value + * @context: call back context + * + * This callback function for PM QOS change notification is used to setup + * dynamic runtime PM. + * + * Return: NOTIFY_DONE for success + */ +int wlan_hdd_pm_qos_notify(struct notifier_block *nb, unsigned long curr_val, + void *context); +#endif +/** + * wlan_hdd_ipv6_changed() - IPv6 change notifier callback + * @nb: pointer to notifier block + * @data: data + * @arg: arg + * + * This is the IPv6 notifier callback function gets invoked + * if any change in IP and then invoke the function @__wlan_hdd_ipv6_changed + * to reconfigure the offload parameters. + * + * Return: 0 on success, error number otherwise. + */ +int wlan_hdd_ipv6_changed(struct notifier_block *nb, + unsigned long data, void *arg); + +/** + * hdd_set_power_config() - set power config to firmware + * @hddctx: HDD context + * @adapter: HDD adapter + * @power: new power config value + * + * Return: 0 on success; Errno on failure + */ +int hdd_set_power_config(struct hdd_context *hddctx, + struct hdd_adapter *adapter, uint8_t power); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +/** + * hdd_wlan_suspend_resume_event()- send suspend/resume state + * @state: suspend/resume state + * + * This Function sends suspend resume state diag event + * + * Return: void. + */ +void hdd_wlan_suspend_resume_event(uint8_t state); + +#else +static inline +void hdd_wlan_suspend_resume_event(uint8_t state) {} +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +/** + * wlan_hdd_set_powersave() - Set powersave mode + * @adapter: adapter upon which the request was received + * @allow_power_save: is wlan allowed to go into power save mode + * @timeout: timeout period in ms + * + * Return: 0 on success, non-zero on any error + */ +int wlan_hdd_set_powersave(struct hdd_adapter *adapter, + bool allow_power_save, uint32_t timeout); + +/** + * wlan_hdd_inc_suspend_stats() - Prints, then increments, then prints suspend + * failed statistics. + * @hdd_ctx: The HDD context to operate on + * @reason: The suspend failed reason to increment + * + * This function prints all of the suspend failed statistics, increments the + * specified suspend fail reason statistic, and prints the them all again. This + * is for easily keeping track of the most common reasons suspend fails. + * + * Return: none + */ +void wlan_hdd_inc_suspend_stats(struct hdd_context *hdd_ctx, + enum suspend_fail_reason reason); + +/* + * Unit-test suspend/resume is a testing feature that allows putting firmware + * into WoW suspend irrespective of Apps suspend status. It emulates the chain + * of events that occur durring normal system-level suspend/resume, such as + * initiating all of the suspend/resume stages in the correct order, and + * enabling/disabling appropriate copy engine irqs. + */ +#ifdef WLAN_SUSPEND_RESUME_TEST +/** + * wlan_hdd_unit_test_bus_suspend() - suspend the wlan bus + * @wow_params: collection of wow enable override parameters + * + * This function does the same as wlan_hdd_bus_suspend, but additionally passes + * the appropriate flags to FW, indicating this is a unit-test suspend and it + * should use an HTC wakeup method to resume. + * + * Return: 0 for success or error code + */ +int wlan_hdd_unit_test_bus_suspend(struct wow_enable_params wow_params); + +/** + * hdd_wlan_fake_apps_resume() - Resume from unit-test triggered suspend + * @wiphy: the kernel wiphy struct for the device being resumed + * @dev: the kernel net_device struct for the device being resumed + * + * Return: Zero on success, calls QDF_BUG() on failure + */ +int hdd_wlan_fake_apps_resume(struct wiphy *wiphy, struct net_device *dev); + +/** + * hdd_wlan_fake_apps_suspend() - Initiate a unit-test triggered suspend + * @wiphy: the kernel wiphy struct for the device being suspended + * @dev: the kernel net_device struct for the device being suspended + * @pause_setting: interface pause override setting + * @resume_setting: resume trigger override setting + * + * Return: Zero on success, suspend related non-zero error code on failure + */ +int hdd_wlan_fake_apps_suspend(struct wiphy *wiphy, struct net_device *dev, + enum wow_interface_pause pause_setting, + enum wow_resume_trigger resume_setting); +#else +static inline int +hdd_wlan_fake_apps_resume(struct wiphy *wiphy, struct net_device *dev) +{ + return 0; +} + +static inline int +hdd_wlan_fake_apps_suspend(struct wiphy *wiphy, struct net_device *dev, + enum wow_interface_pause pause_setting, + enum wow_resume_trigger resume_setting) +{ + return 0; +} +#endif /* WLAN_SUSPEND_RESUME_TEST */ + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * wlan_hdd_mon_thread_resume() - Resume MON thread + * @hdd_ctx: HDD context + * + * Check if MON thread is suspended, and resume if yes. + * + * Return: None + */ +void wlan_hdd_mon_thread_resume(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_mon_thread_suspend() - Suspend MON thread + * @hdd_ctx: HDD context + * + * To suspend MON thread + * + * Return: 0 for success + */ +int wlan_hdd_mon_thread_suspend(struct hdd_context *hdd_ctx); + +#else +static inline void wlan_hdd_mon_thread_resume(struct hdd_context *hdd_ctx) {} +static inline int wlan_hdd_mon_thread_suspend(struct hdd_context *hdd_ctx) +{ + return 0; +} + +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +#ifdef QCA_CONFIG_SMP +/** + * wlan_hdd_rx_thread_resume() - Resume RX thread + * @hdd_ctx: HDD context + * + * Check if RX thread suspended, and resume if yes. + * + * Return: None + */ +void wlan_hdd_rx_thread_resume(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_rx_thread_suspend() - Suspend RX thread + * @hdd_ctx: HDD context + * + * To suspend RX thread + * + * Return: 0 for success + */ +int wlan_hdd_rx_thread_suspend(struct hdd_context *hdd_ctx); + +#else +static inline void wlan_hdd_rx_thread_resume(struct hdd_context *hdd_ctx) {} +static inline int wlan_hdd_rx_thread_suspend(struct hdd_context *hdd_ctx) +{ + return 0; +} +#endif + +#ifdef FEATURE_ANI_LEVEL_REQUEST +/** + * wlan_hdd_get_ani_level() - Wrapper to call API to fetch ani level + * @adapter: pointer to HDD adapter + * @ani: pointer to structure storing ani level for channels + * @parsed_freqs: parsed freqs from the get ani command + * @num_freqs: number of parsed channels + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_hdd_get_ani_level(struct hdd_adapter *adapter, + struct wmi_host_ani_level_event *ani, + uint32_t *parsed_freqs, + uint8_t num_freqs); +#endif /* FEATURE_ANI_LEVEL_REQUEST */ +#endif /* __WLAN_HDD_POWER_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_regulatory.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_regulatory.h new file mode 100644 index 0000000000000000000000000000000000000000..4ab1b717eb59790cfc84787b8bcbaf8578dc774c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_regulatory.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined __HDD_REGULATORY_H +#define __HDD_REGULATORY_H + +/** + * DOC: wlan_hdd_regulatory.h + * + * HDD Regulatory prototype implementation + */ + +struct hdd_context; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS) +#define IEEE80211_CHAN_PASSIVE_SCAN IEEE80211_CHAN_NO_IR +#define IEEE80211_CHAN_NO_IBSS IEEE80211_CHAN_NO_IR +#endif + +/** + * hdd_update_regulatory_config() - API to update regulatory config parameters + * @hdd_ctx: HDD context + * + * Return: 0 on success, err on failure + */ +int hdd_update_regulatory_config(struct hdd_context *hdd_ctx); + +int hdd_regulatory_init(struct hdd_context *hdd_ctx, struct wiphy *wiphy); +void hdd_program_country_code(struct hdd_context *hdd_ctx); +void hdd_reset_global_reg_params(void); + +/** + * hdd_send_wiphy_regd_sync_event() - sends the regulatory sync event + * @hdd_ctx: HDD context + * + * Return: None + */ +void hdd_send_wiphy_regd_sync_event(struct hdd_context *hdd_ctx); + +/** + * hdd_reg_set_country() - helper function for setting the regulatory country + * @hdd_ctx: the HDD context to set the country for + * @country_code: the two character country code to configure + * + * Return: zero for success, non-zero error code for failure + */ +int hdd_reg_set_country(struct hdd_context *hdd_ctx, char *country_code); + +/** + * hdd_reg_legacy_setband_to_reg_wifi_band_bitmap() - Convert the user space + * band input to a bitmap of band capabilities, with reg_wifi_band as the + * bit value + * @qca_setband: user space/setband value band input, can be 0, 1, or 2 + * + * Return: bitmap on top of reg_wifi_band of bands enabled + */ +uint32_t hdd_reg_legacy_setband_to_reg_wifi_band_bitmap(uint8_t qca_setband); + +/** + * hdd_reg_set_band() - helper function for setting the regulatory band + * @hdd_ctx: the HDD context to set the band for + * @band_bitmap: the band bitmap to configure + * + * Return: zero for success, non-zero error code for failure + */ +int hdd_reg_set_band(struct net_device *dev, uint32_t band_bitmap); + +/** + * hdd_update_indoor_channel() - enable/disable indoor channel + * @hdd_ctx: hdd context + * @disable: whether to enable / disable indoor channel + * + * enable/disable indoor channel in wiphy/cds + * + * Return: void + */ +void hdd_update_indoor_channel(struct hdd_context *hdd_ctx, + bool disable); +/** + * hdd_modify_indoor_channel_state_flags() - modify wiphy flags and cds state + * @wiphy_chan: wiphy channel number + * @cds_chan: cds channel structure + * @chan_enum: channel enum maintain in reg db + * @chan_num: channel index + * @disable: Disable/enable the flags + * + * Modify wiphy flags and cds state if channel is indoor. + * + * Return: void + */ +void hdd_modify_indoor_channel_state_flags( + struct hdd_context *hdd_ctx, + struct ieee80211_channel *wiphy_chan, + struct regulatory_channel *cds_chan, + enum channel_enum chan_enum, int chan_num, bool disable); + +/** + * hdd_update_regdb_offload_config() - Update regdb offload disable ini + * for regulatory component. + * + * @psoc: psoc ptr + * + * Return: None + */ +void hdd_update_regdb_offload_config(struct hdd_context *hdd_ctx); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_softap_tx_rx.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_softap_tx_rx.h new file mode 100644 index 0000000000000000000000000000000000000000..61a61e75e0fd4fc98612793f883cc538214de8f8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_softap_tx_rx.h @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_SOFTAP_TX_RX_H) +#define WLAN_HDD_SOFTAP_TX_RX_H + +/** + * DOC: wlan_hdd_softap_tx_rx.h + * + * Linux HDD SOFTAP Tx/Rx APIs + */ + +#include +#include + +/** + * hdd_softap_hard_start_xmit() - Transmit a frame + * @skb: pointer to OS packet + * @dev: pointer to net_device structure + * + * Function registered as a net_device .ndo_start_xmit() method for + * master mode interfaces (SoftAP/P2P GO), called by the OS if any + * packet needs to be transmitted. + * + * Return: Status of the transmission + */ +netdev_tx_t hdd_softap_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev); + +/** + * hdd_softap_ipa_start_xmit() - Transmit a frame, request from IPA + * @nbuf: pointer to buffer/packet + * @dev: pointer to net_device structure + * + * Function registered as a xmit callback in SAP mode, + * called by IPA if any packet needs to be transmitted. + * + * Return: Status of the transmission + */ +QDF_STATUS hdd_softap_ipa_start_xmit(qdf_nbuf_t nbuf, qdf_netdev_t dev); + +/** + * hdd_softap_tx_timeout() - TX timeout handler + * @dev: pointer to network device + * + * Function registered as a net_device .ndo_tx_timeout() method for + * master mode interfaces (SoftAP/P2P GO), called by the OS if the + * driver takes too long to transmit a frame. + * + * Return: None + */ +void hdd_softap_tx_timeout(struct net_device *dev); + +/** + * hdd_softap_init_tx_rx() - Initialize Tx/Rx module + * @adapter: pointer to adapter context + * + * Return: None + */ +void hdd_softap_init_tx_rx(struct hdd_adapter *adapter); + +/** + * hdd_softap_deinit_tx_rx() - Deinitialize Tx/Rx module + * @adapter: pointer to adapter context + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_softap_deinit_tx_rx(struct hdd_adapter *adapter); + +/** + * hdd_softap_init_tx_rx_sta() - Initialize Tx/Rx for a softap station + * @adapter: pointer to adapter context + * @sta_mac: pointer to the MAC address of the station + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_softap_init_tx_rx_sta(struct hdd_adapter *adapter, + struct qdf_mac_addr *sta_mac); + +/** + * hdd_softap_rx_packet_cbk() - Receive packet handler + * @adapter_context: pointer to HDD adapter + * @rx_buf: pointer to rx qdf_nbuf chain + * + * Receive callback registered with the Data Path. The Data Path will + * call this to notify the HDD when one or more packets were received + * for a registered STA. + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_softap_rx_packet_cbk(void *adapter_context, qdf_nbuf_t rx_buf); + +/** + * hdd_softap_deregister_sta() - Deregister a STA with the Data Path + * @adapter: pointer to adapter context + * @sta_info: double pointer to HDD station info structure + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS hdd_softap_deregister_sta(struct hdd_adapter *adapter, + struct hdd_station_info **sta_info); + +/** + * hdd_softap_register_sta() - Register a SoftAP STA + * @adapter: pointer to adapter context + * @auth_required: is additional authentication required? + * @privacy_required: should 802.11 privacy bit be set? + * @sta_mac: station MAC address + * @wmm_enabled: is WMM enabled for this STA? + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS hdd_softap_register_sta(struct hdd_adapter *adapter, + bool auth_required, + bool privacy_required, + struct qdf_mac_addr *sta_mac, + bool wmm_enabled); + +/** + * hdd_softap_register_bc_sta() - Register the SoftAP broadcast STA + * @adapter: pointer to adapter context + * @privacy_required: should 802.11 privacy bit be set? + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS hdd_softap_register_bc_sta(struct hdd_adapter *adapter, + bool privacy_required); + +/** + * hdd_softap_stop_bss() - Stop the BSS + * @adapter: pointer to adapter context + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS hdd_softap_stop_bss(struct hdd_adapter *adapter); + +/** + * hdd_softap_change_sta_state() - Change the state of a SoftAP station + * @adapter: pointer to adapter context + * @sta_mac: MAC address of the station + * @state: new state of the station + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS hdd_softap_change_sta_state(struct hdd_adapter *adapter, + struct qdf_mac_addr *sta_mac, + enum ol_txrx_peer_state state); + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +/** + * hdd_softap_tx_resume_timer_expired_handler() - TX Q resume timer handler + * @adapter_context: pointer to vdev adapter + * + * TX Q resume timer handler for SAP and P2P GO interface. If Blocked + * OS Q is not resumed during timeout period, to prevent permanent + * stall, resume OS Q forcefully for SAP and P2P GO interface. + * + * Return: None + */ +void hdd_softap_tx_resume_timer_expired_handler(void *adapter_context); + +/** + * hdd_softap_tx_resume_cb() - Resume OS TX Q. + * @adapter_context: pointer to vdev apdapter + * @tx_resume: TX Q resume trigger + * + * Q was stopped due to WLAN TX path low resource condition + * + * Return: None + */ +void hdd_softap_tx_resume_cb(void *adapter_context, bool tx_resume); +#else +static inline +void hdd_softap_tx_resume_timer_expired_handler(void *adapter_context) +{ +} + +static inline +void hdd_softap_tx_resume_cb(void *adapter_context, bool tx_resume) +{ +} +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + +#ifdef SAP_DHCP_FW_IND +/** + * hdd_post_dhcp_ind() - Send DHCP START/STOP indication to FW + * @adapter: pointer to hdd adapter + * @mac_addr: mac address + * @type: WMA message type + * + * Return: error number + */ +int hdd_post_dhcp_ind(struct hdd_adapter *adapter, + uint8_t *mac_addr, uint16_t type); + +/** + * hdd_softap_inspect_dhcp_packet() - Inspect DHCP packet + * @adapter: pointer to hdd adapter + * @skb: pointer to OS packet (sk_buff) + * @dir: direction + * + * Inspect the Tx/Rx frame, and send DHCP START/STOP notification to the FW + * through WMI message, during DHCP based IP address acquisition phase. + * + * - Send DHCP_START notification to FW when SAP gets DHCP Discovery + * - Send DHCP_STOP notification to FW when SAP sends DHCP ACK/NAK + * + * DHCP subtypes are determined by a status octet in the DHCP Message type + * option (option code 53 (0x35)). + * + * Each peer will be in one of 4 DHCP phases, starts from QDF_DHCP_PHASE_ACK, + * and transitioned per DHCP message type as it arrives. + * + * - QDF_DHCP_PHASE_DISCOVER: upon receiving DHCP_DISCOVER message in ACK phase + * - QDF_DHCP_PHASE_OFFER: upon receiving DHCP_OFFER message in DISCOVER phase + * - QDF_DHCP_PHASE_REQUEST: upon receiving DHCP_REQUEST message in OFFER phase + * or ACK phase (Renewal process) + * - QDF_DHCP_PHASE_ACK : upon receiving DHCP_ACK/NAK message in REQUEST phase + * or DHCP_DELINE message in OFFER phase + * + * Return: error number + */ +int hdd_softap_inspect_dhcp_packet(struct hdd_adapter *adapter, + struct sk_buff *skb, + enum qdf_proto_dir dir); +#else +static inline +int hdd_post_dhcp_ind(struct hdd_adapter *adapter, + uint8_t *mac_addr, uint16_t type) +{ + return 0; +} + +static inline +int hdd_softap_inspect_dhcp_packet(struct hdd_adapter *adapter, + struct sk_buff *skb, + enum qdf_proto_dir dir) +{ + return 0; +} +#endif + +/** + * hdd_softap_check_wait_for_tx_eap_pkt() - Check and wait for eap failure + * pkt completion event + * @adapter: pointer to hdd adapter + * @mac_addr: mac address of peer + * + * Check and wait for eap failure pkt tx completion. + * + * Return: void + */ +void hdd_softap_check_wait_for_tx_eap_pkt(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr); + +#ifndef QCA_LL_LEGACY_TX_FLOW_CONTROL +#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 19, 0)) +/** + * hdd_skb_orphan() - skb_unshare a cloned packed else skb_orphan + * @adapter: pointer to HDD adapter + * @skb: pointer to skb data packet + * + * Return: pointer to skb structure + */ +static inline struct sk_buff *hdd_skb_orphan(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_skb_fill_gso_size(adapter->dev, skb); + + if (skb_cloned(skb)) { + ++adapter->hdd_stats.tx_rx_stats.tx_orphaned; + skb_orphan(skb); + return skb; + } + + if (unlikely(hdd_ctx->config->tx_orphan_enable)) { + /* + * For UDP packets we want to orphan the packet to allow the app + * to send more packets. The flow would ultimately be controlled + * by the limited number of tx descriptors for the vdev. + */ + ++adapter->hdd_stats.tx_rx_stats.tx_orphaned; + skb_orphan(skb); + } + + return skb; +} +#else +static inline struct sk_buff *hdd_skb_orphan(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + struct sk_buff *nskb; + + hdd_skb_fill_gso_size(adapter->dev, skb); + nskb = skb_unshare(skb, GFP_ATOMIC); + + return nskb; +} +#endif +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ +#endif /* end #if !defined(WLAN_HDD_SOFTAP_TX_RX_H) */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_spectralscan.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_spectralscan.h new file mode 100644 index 0000000000000000000000000000000000000000..c1754e5b56cf96f52bb185ebb5b756cea3586f35 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_spectralscan.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_spectralscan.h + * + * WLAN Host Device Driver spectral scan implementation + * + */ + +#if !defined(WLAN_HDD_SPECTRALSCAN_H) +#define WLAN_HDD_SPECTRALSCAN_H + +#ifdef WLAN_CONV_SPECTRAL_ENABLE + +#define SPECTRAL_VERSION_1 1 +#define SPECTRAL_VERSION_2 2 +#define SPECTRAL_VERSION_3 3 +#define SPECTRAL_SUB_VERSION_0 0 +#define SPECTRAL_SUB_VERSION_1 1 + +/* + * enum spectral_scan_msg_type - spectral scan registration + * @SPECTRAL_SCAN_REGISTER_REQ: spectral scan app register request + * @SPECTRAL_SCAN_REGISTER_RSP: spectral scan app register response + */ +enum spectral_scan_msg_type { + SPECTRAL_SCAN_REGISTER_REQ, + SPECTRAL_SCAN_REGISTER_RSP, +}; + +/* + * struct spectral_scan_msg - spectral scan request message + * @msg_type: message type + * @pid: process id + */ +struct spectral_scan_msg { + uint32_t msg_type; + uint32_t pid; +}; + +/** + * struct spectral_scan_msg_v - spectral scan request message included version + * @msg_type: message type + * @pid: process id + * @version: version information + * @sub_version: sub version information + */ +struct spectral_scan_msg_v { + uint32_t msg_type; + uint32_t pid; + uint32_t version; + uint32_t sub_version; +}; + +#define FEATURE_SPECTRAL_SCAN_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_spectral_scan_start \ +}, \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_spectral_scan_stop \ +}, \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_spectral_scam_get_config \ +}, \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_spectral_scan_get_diag_stats \ +}, \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_spectral_scan_get_cap_info \ +}, \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_spectral_scan_get_status \ +}, + +/** + * wlan_hdd_cfg80211_spectral_scan_start() - start spectral scan + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function starts spectral scan + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_cfg80211_spectral_scan_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * wlan_hdd_cfg80211_spectral_scan_stop() - stop spectral scan + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function stops spectral scan + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_cfg80211_spectral_scan_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * wlan_hdd_cfg80211_spectral_scan_start() - start spectral scan + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function starts spectral scan + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_cfg80211_spectral_scam_get_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * wlan_hdd_cfg80211_spectral_scan_start() - start spectral scan + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function starts spectral scan + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_cfg80211_spectral_scan_get_diag_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * wlan_hdd_cfg80211_spectral_scan_start() - start spectral scan + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function starts spectral scan + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_cfg80211_spectral_scan_get_cap_info(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * wlan_hdd_cfg80211_spectral_scan_start() - start spectral scan + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function starts spectral scan + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_cfg80211_spectral_scan_get_status(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * hdd_spectral_register_to_dbr() - Register spectral to DBR + * @hdd_ctx: pointer to hdd context + * + * This function is that register spectral to Direct Buffer RX component. + * + * Return: None + */ +void hdd_spectral_register_to_dbr(struct hdd_context *hdd_ctx); +#else +#define FEATURE_SPECTRAL_SCAN_VENDOR_COMMANDS +static inline void +hdd_spectral_register_to_dbr(struct hdd_context *hdd_ctx) +{ +} +#endif + +#if defined(CNSS_GENL) && defined(WLAN_CONV_SPECTRAL_ENABLE) +/** + * spectral_scan_activate_service() - Activate spectral scan message handler + * @hdd_ctx: callback context to use + * + * This function registers a handler to receive netlink message from + * the spectral scan application process. + * + * Return: None + */ +void spectral_scan_activate_service(struct hdd_context *hdd_ctx); + +/** + * spectral_scan_deactivate_service() - Deactivate spectral scan message handler + * + * This function deregisters a handler to receive netlink message from + * the spectral scan application process. + * + * Return: None + */ +void spectral_scan_deactivate_service(void); + +/** + * wlan_spectral_update_rx_chainmask() - API to update rx chainmask before + * start spectral gen3 + * @adapter: pointer to adapter + * + * API to update rx chainmask before start spectral gen3. + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS wlan_spectral_update_rx_chainmask(struct hdd_adapter *adapter); +#else +static inline void spectral_scan_activate_service(struct hdd_context *hdd_ctx) +{ +} + +static inline void spectral_scan_deactivate_service(void) +{ +} + +#ifdef WLAN_CONV_SPECTRAL_ENABLE +static inline QDF_STATUS +wlan_spectral_update_rx_chainmask(struct hdd_adapter *adapter) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_CONV_SPECTRAL_ENABLE */ +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_sysfs.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_sysfs.h new file mode 100644 index 0000000000000000000000000000000000000000..253344f6c8f04ab703c6eeec1eac8045e0dffccd --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_sysfs.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_HDD_SYSFS_H_ +#define _WLAN_HDD_SYSFS_H_ + +#ifdef WLAN_SYSFS + +#define MAX_SYSFS_USER_COMMAND_SIZE_LENGTH (32) + +/** + * hdd_sysfs_create_driver_root_obj() - create driver root kobject + * + * Return: none + */ +void hdd_sysfs_create_driver_root_obj(void); + +/** + * hdd_sysfs_destroy_driver_root_obj() - destroy driver root kobject + * + * Return: none + */ +void hdd_sysfs_destroy_driver_root_obj(void); + +/** + * hdd_sysfs_create_version_interface() - create version interface + * @psoc: PSOC ptr + * + * Return: none + */ +void hdd_sysfs_create_version_interface(struct wlan_objmgr_psoc *psoc); + +/** + * hdd_sysfs_destroy_version_interface() - destroy version interface + * + * Return: none + */ +void hdd_sysfs_destroy_version_interface(void); + +/** + * hdd_sysfs_dp_aggregation_create() - API to create dp aggregation + * related sysfs entry + * + * file path: /sys/kernel/wifi/dp_aggregation + * + * usage: + * echo [0/1] > dp_aggregation + * + * Return: 0 on success and errno on failure + */ +int +hdd_sysfs_dp_aggregation_create(void); + +/** + * hdd_sysfs_dp_aggregation_destroy() - API to destroy dp aggregation + * related sysfs entry + * + * Return: None + */ +void +hdd_sysfs_dp_aggregation_destroy(void); + +/** + * hdd_sys_validate_and_copy_buf() - validate sysfs input buf and copy into + * destination buffer + * @dest_buf - pointer to destination buffer where data should be copied + * @dest_buf_size - size of destination buffer + * @src_buf - pointer to constant sysfs source buffer + * @src_buf_size - size of source buffer + * + * Return: 0 for success and error code for failure + */ +int +hdd_sysfs_validate_and_copy_buf(char *dest_buf, size_t dest_buf_size, + char const *src_buf, size_t src_buf_size); + +#ifdef WLAN_POWER_DEBUG +/** + * hdd_sysfs_create_powerstats_interface() - create power_stats interface + * + * Return: none + */ +void hdd_sysfs_create_powerstats_interface(void); +/** + * hdd_sysfs_destroy_powerstats_interface() - destroy power_stats interface + * + * Return: none + */ +void hdd_sysfs_destroy_powerstats_interface(void); +#else +static inline +void hdd_sysfs_create_powerstats_interface(void) +{ +} + +static inline +void hdd_sysfs_destroy_powerstats_interface(void) +{ +} +#endif /*End of WLAN_POWER_DEBUG */ +#else +static inline +void hdd_sysfs_create_driver_root_obj(void) +{ +} + +static inline +void hdd_sysfs_destroy_driver_root_obj(void) +{ +} + +static inline +void hdd_sysfs_create_powerstats_interface(void) +{ +} + +static inline +void hdd_sysfs_destroy_powerstats_interface(void) +{ +} + +static inline +void hdd_sysfs_create_version_interface(struct wlan_objmgr_psoc *psoc) +{ +} + +static inline +void hdd_sysfs_destroy_version_interface(void) +{ +} + +static inline int +hdd_sysfs_dp_aggregation_create(void) +{ + return 0; +} + +static inline void +hdd_sysfs_dp_aggregation_destroy(void) +{ +} + +static inline int +hdd_sysfs_validate_and_copy_buf(char *dest_buf, size_t dest_buf_size, + char const *src_buf, size_t src_buf_size) +{ + return -EPERM; +} +#endif + +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS +/** + * hdd_sysfs_create_adapter_root_obj() - create adapter sysfs entries + * @adapter: HDD adapter + * + * Return: none + */ +void hdd_sysfs_create_adapter_root_obj(struct hdd_adapter *adapter); +/** + * hdd_sysfs_destroy_adapter_root_obj() - Destroy adapter sysfs entries + * @adapter: HDD adapter + * + * Return: none + */ +void hdd_sysfs_destroy_adapter_root_obj(struct hdd_adapter *adapter); +#else +static inline +void hdd_sysfs_create_adapter_root_obj(struct hdd_adapter *adapter) +{ +} + +static inline +void hdd_sysfs_destroy_adapter_root_obj(struct hdd_adapter *adapter) +{ +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_tdls.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_tdls.h new file mode 100644 index 0000000000000000000000000000000000000000..73a510125946f63873c85aedd4067e42278e0a09 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_tdls.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __HDD_TDLS_H +#define __HDD_TDLS_H +/** + * DOC: wlan_hdd_tdls.h + * WLAN Host Device Driver TDLS include file + */ + +struct hdd_context; + +#ifdef FEATURE_WLAN_TDLS + +/* Bit mask flag for tdls_option to FW */ +#define ENA_TDLS_OFFCHAN (1 << 0) /* TDLS Off Channel support */ +#define ENA_TDLS_BUFFER_STA (1 << 1) /* TDLS Buffer STA support */ +#define ENA_TDLS_SLEEP_STA (1 << 2) /* TDLS Sleep STA support */ + +int wlan_hdd_tdls_get_all_peers(struct hdd_adapter *adapter, char *buf, + int buflen); + +int wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +int wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *peer, + enum nl80211_tdls_operation oper); +#else +int wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy, + struct net_device *dev, + uint8_t *peer, + enum nl80211_tdls_operation oper); +#endif + +#ifdef TDLS_MGMT_VERSION2 +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + const u8 *buf, size_t len); +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, const uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + bool initiator, const uint8_t *buf, + size_t len); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, const uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + const uint8_t *buf, size_t len); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + const uint8_t *buf, size_t len); +#else +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, const uint8_t *buf, + size_t len); +#endif +#endif + +/** + * hdd_set_tdls_offchannel() - set tdls off-channel number + * @hdd_ctx: Pointer to the HDD context + * @adapter: Pointer to the HDD adapter + * @offchannel: tdls off-channel number + * + * This function sets tdls off-channel number + * + * Return: 0 on success; negative errno otherwise + */ +int hdd_set_tdls_offchannel(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + int offchannel); + +/** + * hdd_set_tdls_secoffchanneloffset() - set secondary tdls off-channel offset + * @hdd_ctx: Pointer to the HDD context + * @adapter: Pointer to the HDD adapter + * @offchanoffset: tdls off-channel offset + * + * This function sets secondary tdls off-channel offset + * + * Return: 0 on success; negative errno otherwise + */ +int hdd_set_tdls_secoffchanneloffset(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + int offchanoffset); + +/** + * hdd_set_tdls_offchannelmode() - set tdls off-channel mode + * @hdd_ctx: Pointer to the HDD context + * @adapter: Pointer to the HDD adapter + * @offchanmode: tdls off-channel mode + * 1-Enable Channel Switch + * 2-Disable Channel Switch + * + * This function sets tdls off-channel mode + * + * Return: 0 on success; negative errno otherwise + */ +int hdd_set_tdls_offchannelmode(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + int offchanmode); +int hdd_set_tdls_scan_type(struct hdd_context *hdd_ctx, int val); +int wlan_hdd_tdls_antenna_switch(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint32_t mode); + +/** + * wlan_hdd_cfg80211_configure_tdls_mode() - configure tdls mode + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Return: Return the Success or Failure code. + */ +int wlan_hdd_cfg80211_configure_tdls_mode(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +QDF_STATUS hdd_tdls_register_peer(void *userdata, uint32_t vdev_id, + const uint8_t *mac, uint8_t qos); + +/** + * hdd_init_tdls_config() - initialize tdls config + * @tdls_cfg: pointer to tdls_start_params structure + * + * Return: none + */ +void hdd_init_tdls_config(struct tdls_start_params *tdls_cfg); + +/** + * hdd_config_tdls_with_band_switch() - configure tdls when band changes + * Disable tdls offchmode if only one of + * bands is supported + * Enable tdls offchmode if all band enable + * @hdd_ctx: Pointer to the HDD context + * + * Return: none + */ +void hdd_config_tdls_with_band_switch(struct hdd_context *hdd_ctx); +#else + +static inline int wlan_hdd_tdls_antenna_switch(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint32_t mode) +{ + return 0; +} + +static inline int wlan_hdd_cfg80211_configure_tdls_mode(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return 0; +} + +static inline +QDF_STATUS hdd_tdls_register_peer(void *userdata, uint32_t vdev_id, + const uint8_t *mac, uint8_t qos) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void hdd_init_tdls_config(struct tdls_start_params *tdls_cfg) +{ +} + +static inline void hdd_config_tdls_with_band_switch(struct hdd_context *hdd_ctx) +{ +} + +#endif /* End of FEATURE_WLAN_TDLS */ +#endif /* __HDD_TDLS_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_trace.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..41d12a3216054386772351d22cd53ec627bffd1d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_trace.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_TRACE_H__ +#define __WLAN_HDD_TRACE_H__ + +#include "mac_trace.h" + +#define NO_SESSION 0xFF + +#undef ENUMS +#define ENUMS \ + ENUM(TRACE_CODE_HDD_OPEN_REQUEST) \ + ENUM(TRACE_CODE_HDD_STOP_REQUEST) \ + ENUM(TRACE_CODE_HDD_TX_TIMEOUT) \ + ENUM(TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL) \ + ENUM(TRACE_CODE_HDD_SETSUSPENDMODE_IOCTL) \ + ENUM(TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL) \ + ENUM(TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL) \ + ENUM(TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL) \ + ENUM(TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL) \ + ENUM(TRACE_CODE_HDD_SETROAMDELTA_IOCTL) \ + ENUM(TRACE_CODE_HDD_GETROAMDELTA_IOCTL) \ + ENUM(TRACE_CODE_HDD_GETBAND_IOCTL) \ + ENUM(TRACE_CODE_HDD_GETCOUNTRYREV_IOCTL) \ + ENUM(TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL) \ + ENUM(TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL) \ + ENUM(TRACE_CODE_HDD_HOSTAPD_OPEN_REQUEST) \ + ENUM(TRACE_CODE_HDD_HOSTAPD_STOP_REQUEST) \ + ENUM(TRACE_CODE_HDD_HOSTAPD_UNINIT_REQUEST) \ + ENUM(TRACE_CODE_HDD_SOFTAP_TX_TIMEOUT) \ + ENUM(TRACE_CODE_HDD_HOSTAPD_SET_MAC_ADDR) \ + ENUM(TRACE_CODE_HDD_HOSTAPD_P2P_SET_NOA_IOCTL) \ + ENUM(TRACE_CODE_HDD_HOSTAPD_P2P_SET_PS_IOCTL) \ + ENUM(TRACE_CODE_HDD_HOSTAPD_SET_SAP_CHANNEL_LIST_IOCTL) \ + ENUM(TRACE_CODE_HDD_ADD_VIRTUAL_INTF) \ + ENUM(TRACE_CODE_HDD_DEL_VIRTUAL_INTF) \ + ENUM(TRACE_CODE_HDD_CHANGE_VIRTUAL_INTF) \ + ENUM(TRACE_CODE_HDD_CFG80211_START_AP) \ + ENUM(TRACE_CODE_HDD_CFG80211_CHANGE_BEACON) \ + ENUM(TRACE_CODE_HDD_CFG80211_STOP_AP) \ + ENUM(TRACE_CODE_HDD_CFG80211_CHANGE_BSS) \ + ENUM(TRACE_CODE_HDD_CFG80211_ADD_KEY) \ + ENUM(TRACE_CODE_HDD_CFG80211_GET_KEY) \ + ENUM(TRACE_CODE_HDD_CFG80211_SET_DEFAULT_KEY) \ + ENUM(TRACE_CODE_HDD_CFG80211_CONNECT) \ + ENUM(TRACE_CODE_HDD_CFG80211_DISCONNECT) \ + ENUM(TRACE_CODE_HDD_CFG80211_JOIN_IBSS) \ + ENUM(TRACE_CODE_HDD_CFG80211_LEAVE_IBSS) \ + ENUM(TRACE_CODE_HDD_CFG80211_SET_WIPHY_PARAMS) \ + ENUM(TRACE_CODE_HDD_CFG80211_SET_TXPOWER) \ + ENUM(TRACE_CODE_HDD_CFG80211_GET_TXPOWER) \ + ENUM(TRACE_CODE_HDD_CFG80211_SET_CHANNEL) \ + ENUM(TRACE_CODE_HDD_CFG80211_ADD_BEACON) \ + ENUM(TRACE_CODE_HDD_CFG80211_SET_BEACON) \ + ENUM(TRACE_CODE_HDD_CFG80211_CHANGE_IFACE) \ + ENUM(TRACE_CODE_HDD_CHANGE_STATION) \ + ENUM(TRACE_CODE_HDD_CFG80211_UPDATE_BSS) \ + ENUM(TRACE_CODE_HDD_CFG80211_SCAN) \ + ENUM(TRACE_CODE_HDD_REMAIN_ON_CHANNEL) \ + ENUM(TRACE_CODE_HDD_REMAINCHANREADYHANDLER) \ + ENUM(TRACE_CODE_HDD_CFG80211_CANCEL_REMAIN_ON_CHANNEL) \ + ENUM(TRACE_CODE_HDD_ACTION) \ + ENUM(TRACE_CODE_HDD_MGMT_TX_CANCEL_WAIT) \ + ENUM(TRACE_CODE_HDD_CFG80211_GET_STA) \ + ENUM(TRACE_CODE_HDD_CFG80211_SET_POWER_MGMT) \ + ENUM(TRACE_CODE_HDD_CFG80211_DEL_STA) \ + ENUM(TRACE_CODE_HDD_CFG80211_ADD_STA) \ + ENUM(TRACE_CODE_HDD_CFG80211_SET_PMKSA) \ + ENUM(TRACE_CODE_HDD_CFG80211_UPDATE_FT_IES) \ + ENUM(TRACE_CODE_HDD_CFG80211_TDLS_MGMT) \ + ENUM(TRACE_CODE_HDD_CFG80211_TDLS_OPER) \ + ENUM(TRACE_CODE_HDD_CFG80211_SET_REKEY_DATA) \ + ENUM(TRACE_CODE_HDD_UNSUPPORTED_IOCTL) \ + ENUM(TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL) \ + ENUM(TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL) \ + ENUM(TRACE_CODE_HDD_STORE_JOIN_REQ) \ + ENUM(TRACE_CODE_HDD_CLEAR_JOIN_REQ) \ + ENUM(TRACE_CODE_HDD_ISSUE_JOIN_REQ) \ + ENUM(TRACE_CODE_HDD_CFG80211_RESUME_WLAN) \ + ENUM(TRACE_CODE_HDD_CFG80211_SUSPEND_WLAN) \ + ENUM(TRACE_CODE_HDD_CFG80211_SET_MAC_ACL) \ + ENUM(TRACE_CODE_HDD_CFG80211_TESTMODE) \ + ENUM(TRACE_CODE_HDD_CFG80211_DUMP_SURVEY) \ + ENUM(TRACE_CODE_HDD_CFG80211_SCHED_SCAN_START) \ + ENUM(TRACE_CODE_HDD_CFG80211_SCHED_SCAN_STOP) \ + ENUM(TRACE_CODE_HDD_CFG80211_DEL_PMKSA) \ + ENUM(TRACE_CODE_HDD_SEND_MGMT_TX) \ + /* + * New CFG80211 enums to be added before this comment. + * TRACE_CODE_HDD_RX_SME_MSG is used as code for MTRACE commands. + */ \ + ENUM(TRACE_CODE_HDD_RX_SME_MSG) + +enum { +#undef ENUM +#define ENUM(enum) enum, + ENUMS +}; + +/** + * hdd_trace_event_string() - Convert trace event to string + * @code: trace event enumeration to convert + * + * Return: string representation of the input enumeration + */ +static inline const char *hdd_trace_event_string(uint32_t code) +{ + switch (code) { + default: + return "UNKNOWN"; +#undef ENUM +#define ENUM(enum) CASE_RETURN_STRING(enum) + ENUMS + } +} + +#undef ENUMS +#undef ENUM + +#ifdef HDD_TRACE_RECORD +void hdd_trace_init(void); +#else +static inline void hdd_trace_init(void) {} +#endif + +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_tsf.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_tsf.h new file mode 100644 index 0000000000000000000000000000000000000000..f3dd3b310354952b8c3505a1adc468266013359b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_tsf.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined WLAN_HDD_TSF_H +#define WLAN_HDD_TSF_H +#include "wlan_hdd_cfg.h" +#include "wlan_hdd_main.h" + +/** + * enum hdd_tsf_get_state - status of get tsf action + * @TSF_RETURN: get tsf + * @TSF_STA_NOT_CONNECTED_NO_TSF: sta not connected to ap + * @TSF_NOT_RETURNED_BY_FW: fw not returned tsf + * @TSF_CURRENT_IN_CAP_STATE: driver in capture state + * @TSF_CAPTURE_FAIL: capture fail + * @TSF_GET_FAIL: get fail + * @TSF_RESET_GPIO_FAIL: GPIO reset fail + * @TSF_SAP_NOT_STARTED_NO_TSF SAP not started + * @TSF_NOT_READY: TSF module is not initialized or init failed + * @TSF_DISABLED_BY_TSFPLUS: cap_tsf/get_tsf are disabled due to TSF_PLUS + */ +enum hdd_tsf_get_state { + TSF_RETURN = 0, + TSF_STA_NOT_CONNECTED_NO_TSF, + TSF_NOT_RETURNED_BY_FW, + TSF_CURRENT_IN_CAP_STATE, + TSF_CAPTURE_FAIL, + TSF_GET_FAIL, + TSF_RESET_GPIO_FAIL, + TSF_SAP_NOT_STARTED_NO_TSF, + TSF_NOT_READY, + TSF_DISABLED_BY_TSFPLUS +}; + +/** + * enum hdd_tsf_capture_state - status of capture + * @TSF_IDLE: idle + * @TSF_CAP_STATE: current is in capture state + */ +enum hdd_tsf_capture_state { + TSF_IDLE = 0, + TSF_CAP_STATE +}; + +#ifdef WLAN_FEATURE_TSF +/** + * wlan_hdd_tsf_init() - set gpio and callbacks for + * capturing tsf and init tsf_plus + * @hdd_ctx: pointer to the struct hdd_context + * + * This function set the callback to sme module, the callback will be + * called when a tsf event is reported by firmware; set gpio number + * to FW, FW will toggle this gpio when received a CAP_TSF command; + * do tsf_plus init + * + * Return: nothing + */ +void wlan_hdd_tsf_init(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_tsf_deinit() - reset callbacks for capturing tsf, deinit tsf_plus + * @hdd_ctx: pointer to the struct hdd_context + * + * This function reset the callback to sme module, and deinit tsf_plus + * + * Return: nothing + */ +void wlan_hdd_tsf_deinit(struct hdd_context *hdd_ctx); + +/** + * hdd_capture_tsf() - capture tsf + * @adapter: pointer to adapter + * @buf: pointer to uplayer buf + * @len : the length of buf + * + * This function returns tsf value to uplayer. + * + * Return: 0 for success or non-zero negative failure code + */ +int hdd_capture_tsf(struct hdd_adapter *adapter, uint32_t *buf, int len); + +/** + * hdd_indicate_tsf() - return tsf to uplayer + * + * @adapter: pointer to adapter + * @buf: pointer to uplayer buf + * @len : the length of buf + * + * This function returns tsf value to uplayer. + * + * Return: Describe the execute result of this routine + */ +int hdd_indicate_tsf(struct hdd_adapter *adapter, uint32_t *buf, int len); + +/** + * wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Handle TSF SET / GET operation from userspace + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int hdd_get_tsf_cb(void *pcb_cxt, struct stsf *ptsf); + +#else +static inline void wlan_hdd_tsf_init(struct hdd_context *hdd_ctx) +{ +} + +static inline void wlan_hdd_tsf_deinit(struct hdd_context *hdd_ctx) +{ +} + +static inline int hdd_indicate_tsf(struct hdd_adapter *adapter, uint32_t *buf, + int len) +{ + return -ENOTSUPP; +} + +static inline int +hdd_capture_tsf(struct hdd_adapter *adapter, uint32_t *buf, int len) +{ + return -ENOTSUPP; +} + +static inline int wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return -ENOTSUPP; +} +static inline int hdd_get_tsf_cb(void *pcb_cxt, struct stsf *ptsf) +{ + return -ENOTSUPP; +} + +#endif + +#if defined(WLAN_FEATURE_TSF_PLUS) && defined(WLAN_FEATURE_TSF) +/** + * hdd_tsf_is_tx_set() - check ini configuration + * @hdd: pointer to hdd context + * + * This function checks tsf configuration for ptp on tx + * + * Return: true on enable, false on disable + */ + +bool hdd_tsf_is_tx_set(struct hdd_context *hdd); +/** + * hdd_tsf_is_rx_set() - check ini configuration + * @hdd: pointer to hdd context + * + * This function checks tsf configuration for ptp on rx + * + * Return: true on enable, false on disable + */ +bool hdd_tsf_is_rx_set(struct hdd_context *hdd); +/** + * hdd_tsf_is_raw_set() - check ini configuration + * @hdd: pointer to hdd context + * + * This function checks tsf configuration for ptp on raw + * + * Return: true on enable, false on disable + */ +bool hdd_tsf_is_raw_set(struct hdd_context *hdd); +/** + * hdd_tsf_is_dbg_fs_set() - check ini configuration + * @hdd: pointer to hdd context + * + * This function checks tsf configuration for ptp on dbg fs + * + * Return: true on enable, false on disable + */ +bool hdd_tsf_is_dbg_fs_set(struct hdd_context *hdd); + +/** + * hdd_start_tsf_sync() - start tsf sync + * @adapter: pointer to adapter + * + * This function initialize and start TSF synchronization + * + * Return: Describe the execute result of this routine + */ +int hdd_start_tsf_sync(struct hdd_adapter *adapter); + +/** + * hdd_stop_tsf_sync() - stop tsf sync + * @adapter: pointer to adapter + * + * This function stop and de-initialize TSF synchronization + * + * Return: Describe the execute result of this routine + */ +int hdd_stop_tsf_sync(struct hdd_adapter *adapter); + +/** + * hdd_tsf_notify_wlan_state_change() - + * notify tsf module of wlan connection state + * @old_state: old wlan state + * @new_state: new wlan state + * + * This function check the old and new connection state, determine whether + * to start or stop tsf sync + * + * Return: nothing + */ +void hdd_tsf_notify_wlan_state_change(struct hdd_adapter *adapter, + eConnectionState old_state, + eConnectionState new_state); + +/** + * hdd_tx_timestamp() - time stamp TX netbuf + * + * @netbuf: pointer to a TX netbuf + * @target_time: TX time for the netbuf + * + * This function get corresponding host time from target time, + * and time stamp the TX netbuf with this time + * + * Return: Describe the execute result of this routine + */ +int hdd_tx_timestamp(qdf_nbuf_t netbuf, uint64_t target_time); + +/** + * hdd_rx_timestamp() - time stamp RX netbuf + * + * @netbuf: pointer to a RX netbuf + * @target_time: RX time for the netbuf + * + * This function get corresponding host time from target time, + * and time stamp the RX netbuf with this time + * + * Return: Describe the execute result of this routine + */ +int hdd_rx_timestamp(qdf_nbuf_t netbuf, uint64_t target_time); +/** + * hdd_capture_req_timer_expired_handler() - capture req timer handler + * @arg: pointer to a adapter + * + * This function set a timeout handler for TSF capture timer. + * + * Return: none + */ + +void hdd_capture_req_timer_expired_handler(void *arg); + +/** + * hdd_tsf_is_tsf64_tx_set() - check ini configuration + * @hdd: pointer to hdd context + * + * This function checks tsf configuration for ptp on tsf64 tx + * + * Return: true on enable, false on disable + */ +bool hdd_tsf_is_tsf64_tx_set(struct hdd_context *hdd); +#else +static inline int hdd_start_tsf_sync(struct hdd_adapter *adapter) +{ + return -ENOTSUPP; +} + +static inline int hdd_stop_tsf_sync(struct hdd_adapter *adapter) +{ + return -ENOTSUPP; +} + +static inline +void hdd_tsf_notify_wlan_state_change(struct hdd_adapter *adapter, + eConnectionState old_state, + eConnectionState new_state) + +{ +} + +static inline +int hdd_tx_timestamp(qdf_nbuf_t netbuf, uint64_t target_time) +{ + return -ENOTSUPP; +} + +static inline +int hdd_rx_timestamp(qdf_nbuf_t netbuf, uint64_t target_time) +{ + return -ENOTSUPP; +} + +static inline +void hdd_capture_req_timer_expired_handler(void *arg) +{ +} + +static inline +bool hdd_tsf_is_tsf64_tx_set(struct hdd_context *hdd) +{ + return FALSE; +} +#endif + +#ifdef WLAN_FEATURE_TSF_PTP +/** + * wlan_get_ts_info() - return ts info to uplayer + * @dev: pointer to net_device + * @info: pointer to ethtool_ts_info + * + * Return: Describe the execute result of this routine + */ +int wlan_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info); + +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_twt.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_twt.h new file mode 100644 index 0000000000000000000000000000000000000000..909d3dc7df01a376a079189e39b69fa43408c5a8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_twt.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_twt.h + * + * WLAN Host Device Driver file for TWT (Target Wake Time) support. + * + */ + +#if !defined(WLAN_HDD_TWT_H) +#define WLAN_HDD_TWT_H + +#include "qdf_types.h" +#include "qdf_status.h" + +struct hdd_context; +struct wma_tgt_cfg; +struct wmi_twt_enable_complete_event_param; + +#ifdef WLAN_SUPPORT_TWT +/** + * enum twt_status - TWT target state + * @TWT_INIT: Init State + * @TWT_DISABLED: TWT is disabled + * @TWT_FW_TRIGGER_ENABLE_REQUESTED: FW triggered enable requested + * @TWT_FW_TRIGGER_ENABLED: FW triggered twt enabled + * @TWT_HOST_TRIGGER_ENABLE_REQUESTED: Host triggered TWT requested + * @TWT_HOST_TRIGGER_ENABLED: Host triggered TWT enabled + * @TWT_DISABLE_REQUESTED: TWT disable requested + * @TWT_SUSPEND_REQUESTED: TWT suspend requested + * @TWT_SUSPENDED: Successfully suspended TWT + * @TWT_RESUME_REQUESTED: TWT Resume requested + * @TWT_RESUMED: Successfully resumed TWT + * @TWT_CLOSED: Deinitialized TWT feature and closed + */ +enum twt_status { + TWT_INIT, + TWT_DISABLED, + TWT_FW_TRIGGER_ENABLE_REQUESTED, + TWT_FW_TRIGGER_ENABLED, + TWT_HOST_TRIGGER_ENABLE_REQUESTED, + TWT_HOST_TRIGGER_ENABLED, + TWT_DISABLE_REQUESTED, + TWT_SUSPEND_REQUESTED, + TWT_SUSPENDED, + TWT_RESUME_REQUESTED, + TWT_RESUMED, + TWT_CLOSED, +}; + +/** + * hdd_update_tgt_twt_cap() - Update TWT target capabilities + * @hdd_ctx: HDD Context + * @cfg: Pointer to target configuration + * + * Return: None + */ +void hdd_update_tgt_twt_cap(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *cfg); + +/** + * hdd_send_twt_enable_cmd() - Send TWT enable command to target + * @hdd_ctx: HDD Context + * + * Return: None + */ +void hdd_send_twt_enable_cmd(struct hdd_context *hdd_ctx); + +#define TWT_DISABLE_COMPLETE_TIMEOUT 1000 +/** + * hdd_send_twt_disable_cmd() - Send TWT disable command to target + * @hdd_ctx: HDD Context + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_send_twt_disable_cmd(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_twt_init() - Initialize TWT + * @hdd_ctx: pointer to global HDD Context + * + * Initialize the TWT feature by registering the callbacks + * with the lower layers. + * + * Return: None + */ +void wlan_hdd_twt_init(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_twt_deinit() - Deinitialize TWT + * @hdd_ctx: pointer to global HDD Context + * + * Deinitialize the TWT feature by deregistering the + * callbacks with the lower layers. + * + * Return: None + */ +void wlan_hdd_twt_deinit(struct hdd_context *hdd_ctx); + +#else +static inline void hdd_update_tgt_twt_cap(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *cfg) +{ +} + +static inline void hdd_send_twt_enable_cmd(struct hdd_context *hdd_ctx) +{ +} + +static inline QDF_STATUS hdd_send_twt_disable_cmd(struct hdd_context *hdd_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void wlan_hdd_twt_init(struct hdd_context *hdd_ctx) +{ +} + +static inline void wlan_hdd_twt_deinit(struct hdd_context *hdd_ctx) +{ +} + +#endif +#endif /* if !defined(WLAN_HDD_TWT_H)*/ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_tx_rx.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_tx_rx.h new file mode 100644 index 0000000000000000000000000000000000000000..a6677d2076e138c78f0f3791652ffd1bf858b20a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_tx_rx.h @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_TX_RX_H) +#define WLAN_HDD_TX_RX_H + +/** + * + * DOC: wlan_hdd_tx_rx.h + * + * Linux HDD Tx/RX APIs + */ + +#include +#include +#include +#include "cdp_txrx_flow_ctrl_legacy.h" + +struct hdd_netif_queue_history; +struct hdd_context; + +#define hdd_dp_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_HDD_DATA, params) + +#define hdd_dp_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_HDD_DATA, params) + +#define hdd_dp_alert_rl(params...) \ + QDF_TRACE_FATAL_RL(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_err_rl(params...) \ + QDF_TRACE_ERROR_RL(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_warn_rl(params...) \ + QDF_TRACE_WARN_RL(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_info_rl(params...) \ + QDF_TRACE_INFO_RL(QDF_MODULE_ID_HDD_DATA, params) +#define hdd_dp_debug_rl(params...) \ + QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_HDD_DATA, params) + +#define hdd_dp_enter() hdd_dp_debug("enter") +#define hdd_dp_enter_dev(dev) hdd_dp_debug("enter(%s)", (dev)->name) +#define hdd_dp_exit() hdd_dp_debug("exit") + +#define HDD_ETHERTYPE_802_1_X 0x888E +#ifdef FEATURE_WLAN_WAPI +#define HDD_ETHERTYPE_WAI 0x88b4 +#define IS_HDD_ETHERTYPE_WAI(_skb) (ntohs(_skb->protocol) == \ + HDD_ETHERTYPE_WAI) +#else +#define IS_HDD_ETHERTYPE_WAI(_skb) (false) +#endif + +#define HDD_PSB_CFG_INVALID 0xFF +#define HDD_PSB_CHANGED 0xFF +#define SME_QOS_UAPSD_CFG_BK_CHANGED_MASK 0xF1 +#define SME_QOS_UAPSD_CFG_BE_CHANGED_MASK 0xF2 +#define SME_QOS_UAPSD_CFG_VI_CHANGED_MASK 0xF4 +#define SME_QOS_UAPSD_CFG_VO_CHANGED_MASK 0xF8 + +netdev_tx_t hdd_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); +void hdd_tx_timeout(struct net_device *dev); + +QDF_STATUS hdd_init_tx_rx(struct hdd_adapter *adapter); +QDF_STATUS hdd_deinit_tx_rx(struct hdd_adapter *adapter); + +/** + * hdd_rx_flush_packet_cbk() - flush rx packet handler + * @adapter_context: pointer to HDD adapter context + * @vdev_id: vdev_id of the packets to be flushed + * + * Flush rx packet callback registered with data path. DP will call this to + * notify HDD when packets for a particular vdev is to be flushed out. + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_rx_flush_packet_cbk(void *adapter_context, uint8_t vdev_id); + +/** + * hdd_rx_packet_cbk() - Receive packet handler + * @adapter_context: pointer to HDD adapter context + * @rxBuf: pointer to rx qdf_nbuf + * + * Receive callback registered with data path. DP will call this to notify + * the HDD when one or more packets were received for a registered + * STA. + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_rx_packet_cbk(void *adapter_context, qdf_nbuf_t rxBuf); + +#if defined(WLAN_SUPPORT_RX_FISA) +/** + * hdd_rx_fisa_cbk() - Entry function to FISA to handle aggregation + * @soc: core txrx main context + * @vdev: Handle DP vdev + * @nbuf_list: List nbufs to be aggregated + * + * Return: Success on aggregation + */ +QDF_STATUS hdd_rx_fisa_cbk(void *dp_soc, void *dp_vdev, qdf_nbuf_t rxbuf_list); + +/** + * hdd_rx_fisa_flush_by_ctx_id() - Flush function to end of context + * flushing of aggregates + * @soc: core txrx main context + * @ring_num: REO number to flush the flow Rxed on the REO + * + * Return: Success on flushing the flows for the REO + */ +QDF_STATUS hdd_rx_fisa_flush_by_ctx_id(void *dp_soc, int ring_num); + +/** + * hdd_rx_fisa_flush_by_vdev_id() - Flush fisa aggregates per vdev id + * @soc: core txrx main context + * @vdev_id: vdev ID + * + * Return: Success on flushing the flows for the vdev + */ +QDF_STATUS hdd_rx_fisa_flush_by_vdev_id(void *dp_soc, uint8_t vdev_id); +#else +static inline QDF_STATUS hdd_rx_fisa_flush_by_vdev_id(void *dp_soc, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * hdd_rx_deliver_to_stack() - HDD helper function to deliver RX pkts to stack + * @adapter: pointer to HDD adapter context + * @skb: pointer to skb + * + * The function calls the appropriate stack function depending upon the packet + * type and whether GRO/LRO is enabled. + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_rx_deliver_to_stack(struct hdd_adapter *adapter, + struct sk_buff *skb); + +/** + * hdd_rx_thread_gro_flush_ind_cbk() - receive handler to flush GRO packets + * @adapter: pointer to HDD adapter + * @rx_ctx_id: RX CTX Id for which flush should happen + * + * Receive callback registered with DP layer which flushes GRO packets + * for a given RX CTX ID (RX Thread) + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_rx_thread_gro_flush_ind_cbk(void *adapter, int rx_ctx_id); + +/** + * hdd_rx_pkt_thread_enqueue_cbk() - receive pkt handler to enqueue into thread + * @adapter: pointer to HDD adapter + * @nbuf_list: pointer to qdf_nbuf list + * + * Receive callback registered with DP layer which enqueues packets into dp rx + * thread + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_rx_pkt_thread_enqueue_cbk(void *adapter_context, + qdf_nbuf_t nbuf_list); + +/** + * hdd_rx_ol_init() - Initialize Rx offload mode (LRO or GRO) + * @hdd_ctx: pointer to HDD Station Context + * + * Return: 0 on success and non zero on failure. + */ +int hdd_rx_ol_init(struct hdd_context *hdd_ctx); + +/** + * hdd_disable_rx_ol_in_concurrency() - Disable Rx offload due to concurrency + * @disable: true/false to disable/enable the Rx offload + * + * Return: none + */ +void hdd_disable_rx_ol_in_concurrency(bool disable); + +/** + * hdd_disable_rx_ol_for_low_tput() - Disable Rx offload in low TPUT scenario + * @hdd_ctx: hdd context + * @disable: true/false to disable/enable the Rx offload + * + * Return: none + */ +void hdd_disable_rx_ol_for_low_tput(struct hdd_context *hdd_ctx, bool disable); + +/** + * hdd_reset_all_adapters_connectivity_stats() - reset connectivity stats + * @hdd_ctx: pointer to HDD Station Context + * + * Return: None + */ +void hdd_reset_all_adapters_connectivity_stats(struct hdd_context *hdd_ctx); + +/** + * hdd_tx_rx_collect_connectivity_stats_info() - collect connectivity stats + * @skb: pointer to skb data + * @adapter: pointer to vdev apdapter + * @action: action done on pkt. + * @pkt_type: data pkt type + * + * Return: None + */ +void hdd_tx_rx_collect_connectivity_stats_info(struct sk_buff *skb, + void *adapter, enum connectivity_stats_pkt_status action, + uint8_t *pkt_type); + +/** + * hdd_tx_queue_cb() - Disable/Enable the Transmit Queues + * @hdd_handle: HDD handle + * @vdev_id: vdev id + * @action: Action to be taken on the Tx Queues + * @reason: Reason for the netif action + * + * Return: None + */ +void hdd_tx_queue_cb(hdd_handle_t hdd_handle, uint32_t vdev_id, + enum netif_action_type action, + enum netif_reason_type reason); + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +void hdd_tx_resume_cb(void *adapter_context, bool tx_resume); + +/** + * hdd_tx_flow_control_is_pause() - Is TX Q paused by flow control + * @adapter_context: pointer to vdev apdapter + * + * Return: true if TX Q is paused by flow control + */ +bool hdd_tx_flow_control_is_pause(void *adapter_context); + +/** + * hdd_register_tx_flow_control() - Register TX Flow control + * @adapter: adapter handle + * @timer_callback: timer callback + * @flow_control_fp: txrx flow control + * @flow_control_is_pause_fp: is txrx paused by flow control + * + * Return: none + */ +void hdd_register_tx_flow_control(struct hdd_adapter *adapter, + qdf_mc_timer_callback_t timer_callback, + ol_txrx_tx_flow_control_fp flow_control_fp, + ol_txrx_tx_flow_control_is_pause_fp flow_control_is_pause); +void hdd_deregister_tx_flow_control(struct hdd_adapter *adapter); + +/** + * hdd_get_tx_resource() - check tx resources and take action + * @adapter: adapter handle + * @mac_addr: mac address + * @timer_value: timer value + * + * Return: none + */ +void hdd_get_tx_resource(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr, uint16_t timer_value); + +#else +static inline void hdd_tx_resume_cb(void *adapter_context, bool tx_resume) +{ +} +static inline bool hdd_tx_flow_control_is_pause(void *adapter_context) +{ + return false; +} +static inline void hdd_register_tx_flow_control(struct hdd_adapter *adapter, + qdf_mc_timer_callback_t timer_callback, + ol_txrx_tx_flow_control_fp flow_control_fp, + ol_txrx_tx_flow_control_is_pause_fp flow_control_is_pause) +{ +} +static inline void hdd_deregister_tx_flow_control(struct hdd_adapter *adapter) +{ +} + + +/** + * hdd_get_tx_resource() - check tx resources and take action + * @adapter: adapter handle + * @mac_addr: mac address + * @timer_value: timer value + * + * Return: none + */ +static inline +void hdd_get_tx_resource(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr, uint16_t timer_value) +{ +} +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || \ + defined(QCA_HL_NETDEV_FLOW_CONTROL) +void hdd_tx_resume_timer_expired_handler(void *adapter_context); +#else +static inline void hdd_tx_resume_timer_expired_handler(void *adapter_context) +{ +} +#endif + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +void hdd_register_hl_netdev_fc_timer(struct hdd_adapter *adapter, + qdf_mc_timer_callback_t timer_callback); +void hdd_deregister_hl_netdev_fc_timer(struct hdd_adapter *adapter); +#else +static inline void hdd_register_hl_netdev_fc_timer(struct hdd_adapter *adapter, + qdf_mc_timer_callback_t + timer_callback) +{} + +static inline void + hdd_deregister_hl_netdev_fc_timer(struct hdd_adapter *adapter) +{} +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + +const char *hdd_reason_type_to_string(enum netif_reason_type reason); +const char *hdd_action_type_to_string(enum netif_action_type action); + +void wlan_hdd_netif_queue_control(struct hdd_adapter *adapter, + enum netif_action_type action, enum netif_reason_type reason); + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +int hdd_set_mon_rx_cb(struct net_device *dev); +#else +static inline +int hdd_set_mon_rx_cb(struct net_device *dev) +{ + return 0; +} +#endif + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * hdd_set_pktcapture_cb() - Set pkt capture mode callback + * @dev: Pointer to net_device structure + * @pdev_id: pdev id + * + * Return: 0 on success; non-zero for failure + */ +int hdd_set_pktcapture_cb(struct net_device *dev, uint8_t pdev_id); + +/** + * hdd_reset_pktcapture_cb() - Reset pkt capture mode callback + * @pdev_id: pdev id + * + * Return: None + */ +void hdd_reset_pktcapture_cb(uint8_t pdev_id); +#else +static inline +int hdd_set_pktcapture_cb(struct net_device *dev, uint8_t pdev_id) +{ + return -ENOTSUPP; +} + +static inline +void hdd_reset_pktcapture_cb(uint8_t pdev_id) +{ +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +void hdd_send_rps_ind(struct hdd_adapter *adapter); +void hdd_send_rps_disable_ind(struct hdd_adapter *adapter); + +/** + * hdd_adapter_set_rps() - Enable/disable RPS for mode specified + * @vdev_id: vdev id of adapter for which RPS needs to be enabled + * @enable: Set true to enable RPS in SAP mode + * + * Callback function registered with ipa + * + * Return: none + */ +#ifdef IPA_LAN_RX_NAPI_SUPPORT +void hdd_adapter_set_rps(uint8_t vdev_id, bool enable); +#else +static inline +void hdd_adapter_set_rps(uint8_t vdev_id, bool enable) +{ +} +#endif + +void wlan_hdd_classify_pkt(struct sk_buff *skb); + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +void hdd_reset_tcp_delack(struct hdd_context *hdd_ctx); + +/** + * hdd_reset_tcp_adv_win_scale() - Reset tcp adv window scale value to default + * @hdd_ctx: Handle to hdd context + * + * Function used to reset TCP advance window scale value to its default value + * + * Return: None + */ +void hdd_reset_tcp_adv_win_scale(struct hdd_context *hdd_ctx); +#ifdef RX_PERFORMANCE +bool hdd_is_current_high_throughput(struct hdd_context *hdd_ctx); +#else +static inline bool hdd_is_current_high_throughput(struct hdd_context *hdd_ctx) +{ + return false; +} +#endif +#define HDD_MSM_CFG(msm_cfg) msm_cfg +#else +static inline void hdd_reset_tcp_delack(struct hdd_context *hdd_ctx) {} +static inline void hdd_reset_tcp_adv_win_scale(struct hdd_context *hdd_ctx) {} +static inline bool hdd_is_current_high_throughput(struct hdd_context *hdd_ctx) +{ + return false; +} +#define HDD_MSM_CFG(msm_cfg) 0 +#endif + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +void hdd_event_eapol_log(struct sk_buff *skb, enum qdf_proto_dir dir); +#else +static inline +void hdd_event_eapol_log(struct sk_buff *skb, enum qdf_proto_dir dir) +{} +#endif + +/** + * hdd_set_udp_qos_upgrade_config() - Set the threshold for UDP packet + * QoS upgrade. + * @adapter: adapter for which this configuration is to be applied + * @priority: the threshold priority + * + * Returns: 0 on success, -EINVAL on failure + */ +int hdd_set_udp_qos_upgrade_config(struct hdd_adapter *adapter, + uint8_t priority); + +/* + * As of the 4.7 kernel, net_device->trans_start is removed. Create shims to + * support compiling against older versions of the kernel. + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)) +static inline void netif_trans_update(struct net_device *dev) +{ + dev->trans_start = jiffies; +} + +#define TX_TIMEOUT_TRACE(dev, module_id) QDF_TRACE( \ + module_id, QDF_TRACE_LEVEL_ERROR, \ + "%s: Transmission timeout occurred jiffies %lu trans_start %lu", \ + __func__, jiffies, dev->trans_start) +#else +#define TX_TIMEOUT_TRACE(dev, module_id) QDF_TRACE( \ + module_id, QDF_TRACE_LEVEL_ERROR, \ + "%s: Transmission timeout occurred jiffies %lu", \ + __func__, jiffies) +#endif + +static inline void +hdd_skb_fill_gso_size(struct net_device *dev, struct sk_buff *skb) +{ + if (skb_cloned(skb) && skb_is_nonlinear(skb) && + skb_shinfo(skb)->gso_size == 0 && + ip_hdr(skb)->protocol == IPPROTO_TCP) { + skb_shinfo(skb)->gso_size = dev->mtu - + ((skb_transport_header(skb) - skb_network_header(skb)) + + tcp_hdrlen(skb)); + } +} + +/** + * hdd_txrx_get_tx_ack_count() - get tx acked count + * @adapter: Pointer to adapter + * + * Return: tx acked count + */ +uint32_t hdd_txrx_get_tx_ack_count(struct hdd_adapter *adapter); + +#ifdef CONFIG_HL_SUPPORT +static inline QDF_STATUS +hdd_skb_nontso_linearize(struct sk_buff *skb) +{ + return QDF_STATUS_SUCCESS; +} +#else +static inline QDF_STATUS +hdd_skb_nontso_linearize(struct sk_buff *skb) +{ + if (qdf_nbuf_is_nonlinear(skb) && qdf_nbuf_is_tso(skb) == false) { + if (qdf_unlikely(skb_linearize(skb))) + return QDF_STATUS_E_NOMEM; + } + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * hdd_dp_cfg_update() - update hdd config for HDD DP INIs + * @psoc: Pointer to psoc obj + * @hdd_ctx: Pointer to hdd context + * + * Return: None + */ +void hdd_dp_cfg_update(struct wlan_objmgr_psoc *psoc, + struct hdd_context *hdd_ctx); + +/** + * hdd_print_netdev_txq_status() - print netdev tx queue status + * @dev: Pointer to network device + * + * This function is used to print netdev tx queue status + * + * Return: None + */ +void hdd_print_netdev_txq_status(struct net_device *dev); + +/** + * wlan_hdd_dump_queue_history_state() - Dump hdd queue history states + * @q_hist: pointer to hdd queue history structure + * @buf: buffer where the queue history string is dumped + * @size: size of the buffer + * + * Dump hdd queue history states into a buffer + * + * Return: number of bytes written to the buffer + */ +uint32_t +wlan_hdd_dump_queue_history_state(struct hdd_netif_queue_history *q_hist, + char *buf, uint32_t size); + +/** + * wlan_hdd_rx_rpm_mark_last_busy() - Check if dp rx marked last busy + * @hdd_ctx: Pointer to hdd context + * @hif_ctx: Pointer to hif context + * + * Return: dp mark last busy less than runtime delay value + */ +bool wlan_hdd_rx_rpm_mark_last_busy(struct hdd_context *hdd_ctx, + void *hif_ctx); + +#endif /* end #if !defined(WLAN_HDD_TX_RX_H) */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_wext.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_wext.h new file mode 100644 index 0000000000000000000000000000000000000000..708ac2424abc85034077742391bee56867bb1631 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_wext.h @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WEXT_IW_H__ +#define __WEXT_IW_H__ + +#include +#include +#include +#include +#include +#include +#include "qdf_event.h" + +struct hdd_context; +struct sap_config; + +/* + * order of parameters in addTs private ioctl + */ +#define HDD_WLAN_WMM_PARAM_HANDLE 0 +#define HDD_WLAN_WMM_PARAM_TID 1 +#define HDD_WLAN_WMM_PARAM_DIRECTION 2 +#define HDD_WLAN_WMM_PARAM_APSD 3 +#define HDD_WLAN_WMM_PARAM_USER_PRIORITY 4 +#define HDD_WLAN_WMM_PARAM_NOMINAL_MSDU_SIZE 5 +#define HDD_WLAN_WMM_PARAM_MAXIMUM_MSDU_SIZE 6 +#define HDD_WLAN_WMM_PARAM_MINIMUM_DATA_RATE 7 +#define HDD_WLAN_WMM_PARAM_MEAN_DATA_RATE 8 +#define HDD_WLAN_WMM_PARAM_PEAK_DATA_RATE 9 +#define HDD_WLAN_WMM_PARAM_MAX_BURST_SIZE 10 +#define HDD_WLAN_WMM_PARAM_MINIMUM_PHY_RATE 11 +#define HDD_WLAN_WMM_PARAM_SURPLUS_BANDWIDTH_ALLOWANCE 12 +#define HDD_WLAN_WMM_PARAM_SERVICE_INTERVAL 13 +#define HDD_WLAN_WMM_PARAM_SUSPENSION_INTERVAL 14 +#define HDD_WLAN_WMM_PARAM_BURST_SIZE_DEFN 15 +#define HDD_WLAN_WMM_PARAM_ACK_POLICY 16 +#define HDD_WLAN_WMM_PARAM_INACTIVITY_INTERVAL 17 +#define HDD_WLAN_WMM_PARAM_MAX_SERVICE_INTERVAL 18 +#define HDD_WLAN_WMM_PARAM_COUNT 19 + +#define MHZ 6 + +#define WE_MAX_STR_LEN IW_PRIV_SIZE_MASK +#define WLAN_HDD_UI_BAND_AUTO 0 +#define WLAN_HDD_UI_BAND_5_GHZ 1 +#define WLAN_HDD_UI_BAND_2_4_GHZ 2 + +enum hdd_wlan_wmm_direction { + HDD_WLAN_WMM_DIRECTION_UPSTREAM = 0, + HDD_WLAN_WMM_DIRECTION_DOWNSTREAM = 1, + HDD_WLAN_WMM_DIRECTION_BIDIRECTIONAL = 2, +}; + +enum hdd_wlan_wmm_power_save { + HDD_WLAN_WMM_POWER_SAVE_LEGACY = 0, + HDD_WLAN_WMM_POWER_SAVE_UAPSD = 1, +}; + +typedef enum { + /* TSPEC/re-assoc done, async */ + HDD_WLAN_WMM_STATUS_SETUP_SUCCESS = 0, + /* no need to setup TSPEC since ACM=0 and no UAPSD desired, + * sync + async + */ + HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_NO_UAPSD = 1, + /* no need to setup TSPEC since ACM=0 and UAPSD already exists, + * sync + async + */ + HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_UAPSD_EXISTING = 2, + /* TSPEC result pending, sync */ + HDD_WLAN_WMM_STATUS_SETUP_PENDING = 3, + /* TSPEC/re-assoc failed, sync + async */ + HDD_WLAN_WMM_STATUS_SETUP_FAILED = 4, + /* Request rejected due to invalid params, sync + async */ + HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM = 5, + /* TSPEC request rejected since AP!=QAP, sync */ + HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM = 6, + + /* TSPEC modification/re-assoc successful, async */ + HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS = 7, + /* TSPEC modification a no-op since ACM=0 and + * no change in UAPSD, sync + async + */ + HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_NO_UAPSD = 8, + /* TSPEC modification a no-op since ACM=0 and + * requested U-APSD already exists, sync + async + */ + HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_UAPSD_EXISTING = 9, + /* TSPEC result pending, sync */ + HDD_WLAN_WMM_STATUS_MODIFY_PENDING = 10, + /* TSPEC modification failed, prev TSPEC in effect, sync + async */ + HDD_WLAN_WMM_STATUS_MODIFY_FAILED = 11, + /* TSPEC modification request rejected due to invalid params, + * sync + async + */ + HDD_WLAN_WMM_STATUS_MODIFY_FAILED_BAD_PARAM = 12, + + /* TSPEC release successful, sync and also async */ + HDD_WLAN_WMM_STATUS_RELEASE_SUCCESS = 13, + /* TSPEC release pending, sync */ + HDD_WLAN_WMM_STATUS_RELEASE_PENDING = 14, + /* TSPEC release failed, sync + async */ + HDD_WLAN_WMM_STATUS_RELEASE_FAILED = 15, + /* TSPEC release rejected due to invalid params, sync */ + HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM = 16, + /* TSPEC modified due to the mux'ing of requests on ACs, async */ + + HDD_WLAN_WMM_STATUS_MODIFIED = 17, + /* TSPEC revoked by AP, async */ + HDD_WLAN_WMM_STATUS_LOST = 18, + /* some internal failure like memory allocation failure, etc, sync */ + HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE = 19, + + /* U-APSD failed during setup but OTA setup (whether TSPEC exchnage or + * re-assoc) was done so app should release this QoS, async + */ + HDD_WLAN_WMM_STATUS_SETUP_UAPSD_SET_FAILED = 20, + /* U-APSD failed during modify, but OTA setup (whether TSPEC exchnage or + * re-assoc) was done so app should release this QoS, async + */ + HDD_WLAN_WMM_STATUS_MODIFY_UAPSD_SET_FAILED = 21 +} hdd_wlan_wmm_status_e; + +/** Enable 11d */ +#define ENABLE_11D 1 + +/** Disable 11d */ +#define DISABLE_11D 0 + +#define HDD_RTSCTS_EN_MASK 0xF +#define HDD_RTSCTS_ENABLE 1 +#define HDD_CTS_ENABLE 2 + +#define HDD_AUTO_RATE_SGI 0x8 + +/* Packet Types. */ +#define WLAN_KEEP_ALIVE_UNSOLICIT_ARP_RSP 2 +#define WLAN_KEEP_ALIVE_NULL_PKT 1 + +/* + * Defines for fw_test command + */ +#define HDD_FWTEST_PARAMS 3 +#define HDD_FWTEST_SU_PARAM_ID 53 +#define HDD_FWTEST_MU_PARAM_ID 2 +#define HDD_FWTEST_SU_DEFAULT_VALUE 100 +#define HDD_FWTEST_MU_DEFAULT_VALUE 40 +#define HDD_FWTEST_MAX_VALUE 500 + +#ifdef WLAN_WEXT_SUPPORT_ENABLE +/** + * hdd_unregister_wext() - unregister wext context + * @dev: net device handle + * + * Unregisters wext interface context for a given net device + * + * Returns: None + */ +void hdd_unregister_wext(struct net_device *dev); + +/** + * hdd_register_wext() - register wext context + * @dev: net device handle + * + * Registers wext interface context for a given net device + * + * Returns: None + */ +void hdd_register_wext(struct net_device *dev); + +void hdd_wlan_get_stats(struct hdd_adapter *adapter, uint16_t *length, + char *buffer, uint16_t buf_len); +void hdd_wlan_list_fw_profile(uint16_t *length, + char *buffer, uint16_t buf_len); + +int iw_set_var_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int iw_set_three_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int hdd_priv_get_data(struct iw_point *p_priv_data, + union iwreq_data *wrqu); + +void *mem_alloc_copy_from_user_helper(const void *wrqu_data, size_t len); + +int hdd_get_ldpc(struct hdd_adapter *adapter, int *value); + +/** + * hdd_set_ldpc() - Set adapter LDPC + * @adapter: adapter being modified + * @value: new LDPC value + * + * Return: 0 on success, negative errno on failure + */ +int hdd_set_ldpc(struct hdd_adapter *adapter, int value); + +/** + * hdd_we_set_short_gi() - Set adapter Short GI + * @adapter: adapter being modified + * @sgi: new sgi value + * + * Return: 0 on success, negative errno on failure + */ +int hdd_we_set_short_gi(struct hdd_adapter *adapter, int sgi); + +int hdd_get_tx_stbc(struct hdd_adapter *adapter, int *value); + +/** + * hdd_set_tx_stbc() - Set adapter TX STBC + * @adapter: adapter being modified + * @value: new TX STBC value + * + * Return: 0 on success, negative errno on failure + */ +int hdd_set_tx_stbc(struct hdd_adapter *adapter, int value); + +int hdd_get_rx_stbc(struct hdd_adapter *adapter, int *value); + +/** + * hdd_set_rx_stbc() - Set adapter RX STBC + * @adapter: adapter being modified + * @value: new RX STBC value + * + * Return: 0 on success, negative errno on failure + */ +int hdd_set_rx_stbc(struct hdd_adapter *adapter, int value); + +/** + * hdd_assemble_rate_code() - assemble rate code to be sent to FW + * @preamble: rate preamble + * @nss: number of streams + * @rate: rate index + * + * Rate code assembling is different for targets which are 11ax capable. + * Check for the target support and assemble the rate code accordingly. + * + * Return: assembled rate code + */ +int hdd_assemble_rate_code(uint8_t preamble, uint8_t nss, uint8_t rate); + +/** + * hdd_set_11ax_rate() - set 11ax rate + * @adapter: adapter being modified + * @value: new 11ax rate code + * @sap_config: pointer to SAP config to check HW mode + * this will be NULL for call from STA persona + * + * Return: 0 on success, negative errno on failure + */ +int hdd_set_11ax_rate(struct hdd_adapter *adapter, int value, + struct sap_config *sap_config); + +/** + * wlan_hdd_update_phymode() - handle change in PHY mode + * @adapter: adapter being modified + * @new_phymode: new PHY mode for the device + * + * This function is called when the device is set to a new PHY mode. + * It takes a holistic look at the desired PHY mode along with the + * configured capabilities of the driver and the reported capabilities + * of the hardware in order to correctly configure all PHY-related + * parameters. + * + * Return: 0 on success, negative errno value on error + */ +int wlan_hdd_update_phymode(struct hdd_adapter *adapter, int new_phymode); +/** + * wlan_hdd_update_btcoex_mode() - set BTCoex Mode + * @adapter: adapter being modified + * @value: new BTCoex mode for the adapter + * + * This function is called to set a BTCoex Operation Mode + * + * Return: 0 on success, negative errno value on error + */ +int wlan_hdd_set_btcoex_mode(struct hdd_adapter *adapter, int value); + +/** + * wlan_hdd_set_btcoex_rssi_threshold() - set RSSI threshold + * @adapter: adapter being modified + * @value: new RSSI Threshold for the adapter + * + * This function is called to set a new RSSI threshold for + * change of Coex operating mode from TDD to FDD + * + * Return: 0 on success, negative errno value on error + */ +int wlan_hdd_set_btcoex_rssi_threshold(struct hdd_adapter *adapter, int value); + +struct iw_request_info; + +/** + * hdd_check_private_wext_control() - Check to see if private + * wireless extensions ioctls are allowed + * @hdd_ctx: Global HDD context + * @info: Wireless extensions ioctl information passed by the kernel + * + * This function will examine the "private_wext_control" configuration + * item to determine whether or not private wireless extensions ioctls + * are allowed. + * + * Return: 0 if the ioctl is allowed to be processed, -ENOTSUPP if the + * ioctls have been disabled. Note that in addition to returning + * status, this function will log a message if the ioctls are disabled + * or deprecated. + */ +int hdd_check_private_wext_control(struct hdd_context *hdd_ctx, + struct iw_request_info *info); + +/** + * hdd_crash_inject() - Inject a crash + * @adapter: Adapter upon which the command was received + * @v1: first value to inject + * @v2: second value to inject + * + * This function is the handler for the crash inject debug feature. + * This feature only exists for internal testing and must not be + * enabled on a production device. + * + * Return: result of the command + */ +#ifdef CONFIG_WLAN_DEBUG_CRASH_INJECT +int hdd_crash_inject(struct hdd_adapter *adapter, uint32_t v1, uint32_t v2); +#else +static inline +int hdd_crash_inject(struct hdd_adapter *adapter, uint32_t v1, uint32_t v2) +{ + return -ENOTSUPP; +} +#endif + +#ifdef CONFIG_DP_TRACE +void hdd_set_dump_dp_trace(uint16_t cmd_type, uint16_t count); +#else +static inline +void hdd_set_dump_dp_trace(uint16_t cmd_type, uint16_t count) {} +#endif +#else /* WLAN_WEXT_SUPPORT_ENABLE */ + +static inline void hdd_unregister_wext(struct net_device *dev) +{ +} + +static inline void hdd_register_wext(struct net_device *dev) +{ +} +#endif /* WLAN_WEXT_SUPPORT_ENABLE */ + +#if defined(WLAN_WEXT_SUPPORT_ENABLE) && defined(HASTINGS_BT_WAR) +int hdd_hastings_bt_war_enable_fw(struct hdd_context *hdd_ctx); +int hdd_hastings_bt_war_disable_fw(struct hdd_context *hdd_ctx); +#else +static inline +int hdd_hastings_bt_war_enable_fw(struct hdd_context *hdd_ctx) +{ + return -ENOTSUPP; +} + +static inline +int hdd_hastings_bt_war_disable_fw(struct hdd_context *hdd_ctx) +{ + return -ENOTSUPP; +} + +#endif + +#endif /* __WEXT_IW_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_wmm.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_wmm.h new file mode 100644 index 0000000000000000000000000000000000000000..a519f1f8e31dbaca5a341532eebe47d4b2c8c78a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_wmm.h @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2011-2012,2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_HDD_WMM_H +#define _WLAN_HDD_WMM_H + +/** + * DOC: HDD WMM + * + * This module (wlan_hdd_wmm.h interface + wlan_hdd_wmm.c implementation) + * houses all the logic for WMM in HDD. + * + * On the control path, it has the logic to setup QoS, modify QoS and delete + * QoS (QoS here refers to a TSPEC). The setup QoS comes in two flavors: an + * explicit application invoked and an internal HDD invoked. The implicit QoS + * is for applications that do NOT call the custom QCT WLAN OIDs for QoS but + * which DO mark their traffic for priortization. It also has logic to start, + * update and stop the U-APSD trigger frame generation. It also has logic to + * read WMM related config parameters from the registry. + * + * On the data path, it has the logic to figure out the WMM AC of an egress + * packet and when to signal TL to serve a particular AC queue. It also has the + * logic to retrieve a packet based on WMM priority in response to a fetch from + * TL. + * + * The remaining functions are utility functions for information hiding. + */ + +/* Include files */ +#include +#include +#include +#include +#include + +/*Maximum number of ACs */ +#define WLAN_MAX_AC 4 + + +/* Preprocessor Definitions and Constants */ + +/* #define HDD_WMM_DEBUG 1 */ + +#define HDD_WMM_CTX_MAGIC 0x574d4d58 /* "WMMX" */ + +#define HDD_WMM_HANDLE_IMPLICIT 0xFFFFFFFF + +#define HDD_WLAN_INVALID_STA_ID 0xFF + +/* Type Declarations */ + +/** + * enum hdd_wmm_user_mode - WMM modes of operation + * + * @HDD_WMM_USER_MODE_AUTO: STA can associate with any AP, & HDD looks at + * the SME notification after association to find out if associated + * with QAP and acts accordingly + * @HDD_WMM_USER_MODE_QBSS_ONLY - SME will add the extra logic to make sure + * STA associates with a QAP only + * @HDD_WMM_USER_MODE_NO_QOS - Join any AP, but uapsd is disabled + */ +enum hdd_wmm_user_mode { + HDD_WMM_USER_MODE_AUTO = 0, + HDD_WMM_USER_MODE_QBSS_ONLY = 1, + HDD_WMM_USER_MODE_NO_QOS = 2, +}; + +/* UAPSD Mask bits */ +/* (Bit0:VO; Bit1:VI; Bit2:BK; Bit3:BE all other bits are ignored) */ +#define HDD_AC_VO 0x1 +#define HDD_AC_VI 0x2 +#define HDD_AC_BK 0x4 +#define HDD_AC_BE 0x8 + +/** + * enum hdd_wmm_linuxac: AC/Queue Index values for Linux Qdisc to + * operate on different traffic. + */ +enum hdd_wmm_linuxac { + HDD_LINUX_AC_VO = 0, + HDD_LINUX_AC_VI = 1, + HDD_LINUX_AC_BE = 2, + HDD_LINUX_AC_BK = 3, + HDD_LINUX_AC_HI_PRIO = 4, +}; + +/** + * struct hdd_wmm_qos_context - HDD WMM QoS Context + * + * This structure holds the context for a single flow which has either + * been confgured explicitly from userspace or implicitly via the + * Implicit QoS feature. + * + * @node: list node which can be used to put the context into a list + * of contexts + * @handle: identifer which uniquely identifies this context to userspace + * @flow_id: identifier which uniquely identifies this flow to SME + * @adapter: adapter upon which this flow was configured + * @ac_type: access category for this flow + * @status: the status of the last operation performed on this flow by SME + * @implicit_qos_work: work structure used for deferring implicit QoS work + * from softirq context to thread context + * @magic: magic number used to verify that this is a valid context when + * referenced anonymously + */ +struct hdd_wmm_qos_context { + struct list_head node; + uint32_t handle; + uint32_t flow_id; + struct hdd_adapter *adapter; + sme_ac_enum_type ac_type; + hdd_wlan_wmm_status_e status; + struct work_struct implicit_qos_work; + uint32_t magic; + bool is_inactivity_timer_running; +}; + +/** + * struct hdd_wmm_ac_status - WMM related per-AC state & status info + * @is_access_required - does the AP require access to this AC? + * @is_access_needed - does the worker thread need to acquire access to + * this AC? + * @is_access_pending - is implicit QoS negotiation currently taking place? + * @has_access_failed - has implicit QoS negotiation already failed? + * @was_access_granted - has implicit QoS negotiation already succeeded? + * @is_access_allowed - is access to this AC allowed, either because we + * are not doing WMM, we are not doing implicit QoS, implict QoS has + * completed, or explicit QoS has completed? + * @is_tspec_valid - is the tspec valid? + * @is_uapsd_info_valid - are the UAPSD-related fields valid? + * @tspec - current (possibly aggregate) Tspec for this AC + * @is_uapsd_enabled - is UAPSD enabled on this AC? + * @uapsd_service_interval - service interval for this AC + * @uapsd_suspension_interval - suspension interval for this AC + * @uapsd_direction - direction for this AC + * @inactivity_time - inactivity time for this AC + * @last_traffic_count - TX counter used for inactivity detection + * @inactivity_timer - timer used for inactivity detection + */ +struct hdd_wmm_ac_status { + bool is_access_required; + bool is_access_needed; + bool is_access_pending; + bool has_access_failed; + bool was_access_granted; + bool is_access_allowed; + bool is_tspec_valid; + bool is_uapsd_info_valid; + struct sme_qos_wmmtspecinfo tspec; + bool is_uapsd_enabled; + uint32_t uapsd_service_interval; + uint32_t uapsd_suspension_interval; + enum sme_qos_wmm_dir_type uapsd_direction; + +#ifdef FEATURE_WLAN_ESE + uint32_t inactivity_time; + uint32_t last_traffic_count; + qdf_mc_timer_t inactivity_timer; +#endif +}; + +/** + * struct hdd_wmm_status - WMM status maintained per-adapter + * @context_list - list of WMM contexts active on the adapter + * @mutex - mutex used for exclusive access to this adapter's WMM status + * @ac_status - per-AC WMM status + * @qap - is this connected to a QoS-enabled AP? + * @qos_connection - is this a QoS connection? + */ +struct hdd_wmm_status { + struct list_head context_list; + struct mutex mutex; + struct hdd_wmm_ac_status ac_status[WLAN_MAX_AC]; + bool qap; + bool qos_connection; +}; + +extern const uint8_t hdd_qdisc_ac_to_tl_ac[]; +extern const uint8_t hdd_wmm_up_to_ac_map[]; +extern const uint8_t hdd_linux_up_to_ac_map[]; + +/** + * hdd_wmmps_helper() - Function to set uapsd psb dynamically + * + * @adapter: [in] pointer to adapter structure + * @ptr: [in] pointer to command buffer + * + * Return: Zero on success, appropriate error on failure. + */ +int hdd_wmmps_helper(struct hdd_adapter *adapter, uint8_t *ptr); + +/** + * hdd_send_dscp_up_map_to_fw() - send dscp to up map to FW + * @adapter : [in] pointer to Adapter context + * + * This function will send the WMM DSCP configuration of an + * adapter to FW. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_send_dscp_up_map_to_fw(struct hdd_adapter *adapter); + +/** + * hdd_wmm_init() - initialize the WMM DSCP configuation + * @adapter : [in] pointer to Adapter context + * + * This function will initialize the WMM DSCP configuation of an + * adapter to an initial state. The configuration can later be + * overwritten via application APIs or via QoS Map sent OTA. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_init(struct hdd_adapter *adapter); + +/** + * hdd_wmm_adapter_init() - initialize the WMM configuration of an adapter + * @adapter: [in] pointer to Adapter context + * + * This function will initialize the WMM configuation and status of an + * adapter to an initial state. The configuration can later be + * overwritten via application APIs + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_adapter_init(struct hdd_adapter *adapter); + +/** + * hdd_wmm_close() - WMM close function + * @adapter: [in] pointer to adapter context + * + * Function which will perform any necessary work to to clean up the + * WMM functionality prior to the kernel module unload. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_adapter_close(struct hdd_adapter *adapter); + +/** + * hdd_select_queue() - Return queue to be used. + * @dev: Pointer to the WLAN device. + * @skb: Pointer to OS packet (sk_buff). + * + * This function is registered with the Linux OS for network + * core to decide which queue to use for the skb. + * + * Return: Qdisc queue index. + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev, + select_queue_fallback_t fallback); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv); +#else +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb); +#endif + +/** + * hdd_wmm_acquire_access_required() - Function which will determine + * acquire admittance for a WMM AC is required or not based on psb configuration + * done in framework + * + * @adapter: [in] pointer to adapter structure + * @ac_type: [in] WMM AC type of OS packet + * + * Return: void + */ +void hdd_wmm_acquire_access_required(struct hdd_adapter *adapter, + sme_ac_enum_type ac_type); + +/** + * hdd_wmm_acquire_access() - Function which will attempt to acquire + * admittance for a WMM AC + * + * @adapter: [in] pointer to adapter context + * @ac_type: [in] WMM AC type of OS packet + * @granted: [out] pointer to bool flag when indicates if access + * has been granted or not + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_acquire_access(struct hdd_adapter *adapter, + sme_ac_enum_type ac_type, bool *granted); + +/** + * hdd_wmm_assoc() - Function which will handle the housekeeping + * required by WMM when association takes place + * + * @adapter: [in] pointer to adapter context + * @roam_info: [in] pointer to roam information + * @bss_type: [in] type of BSS + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_assoc(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + eCsrRoamBssType bss_type); + +/** + * hdd_wmm_connect() - Function which will handle the housekeeping + * required by WMM when a connection is established + * + * @adapter : [in] pointer to adapter context + * @roam_info: [in] pointer to roam information + * @bss_type : [in] type of BSS + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_connect(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + eCsrRoamBssType bss_type); + +/** + * hdd_wmm_is_active() - Function which will determine if WMM is + * active on the current connection + * + * @adapter: [in] pointer to adapter context + * + * Return: true if WMM is enabled, false if WMM is not enabled + */ +bool hdd_wmm_is_active(struct hdd_adapter *adapter); + +/** + * hdd_wmm_is_acm_allowed() - Function which will determine if WMM is + * active on the current connection + * + * @vdev_id: vdev id + * + * Return: true if WMM is enabled, false if WMM is not enabled + */ +bool hdd_wmm_is_acm_allowed(uint8_t vdev_id); + + +/** + * hdd_wmm_addts() - Function which will add a traffic spec at the + * request of an application + * + * @adapter : [in] pointer to adapter context + * @handle : [in] handle to uniquely identify a TS + * @tspec : [in] pointer to the traffic spec + * + * Return: HDD_WLAN_WMM_STATUS_* + */ +hdd_wlan_wmm_status_e hdd_wmm_addts(struct hdd_adapter *adapter, + uint32_t handle, + struct sme_qos_wmmtspecinfo *tspec); + +/** + * hdd_wmm_delts() - Function which will delete a traffic spec at the + * request of an application + * + * @adapter: [in] pointer to adapter context + * @handle: [in] handle to uniquely identify a TS + * + * Return: HDD_WLAN_WMM_STATUS_* + */ +hdd_wlan_wmm_status_e hdd_wmm_delts(struct hdd_adapter *adapter, + uint32_t handle); + +/** + * hdd_wmm_checkts() - Function which will return the status of a traffic + * spec at the request of an application + * + * @adapter: [in] pointer to adapter context + * @handle: [in] handle to uniquely identify a TS + * + * Return: HDD_WLAN_WMM_STATUS_* + */ +hdd_wlan_wmm_status_e hdd_wmm_checkts(struct hdd_adapter *adapter, + uint32_t handle); +/** + * hdd_wmm_adapter_clear() - Function which will clear the WMM status + * for all the ACs + * + * @adapter: [in] pointer to Adapter context + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_adapter_clear(struct hdd_adapter *adapter); + +void wlan_hdd_process_peer_unauthorised_pause(struct hdd_adapter *adapter); +#endif /* #ifndef _WLAN_HDD_WMM_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_wowl.h b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_wowl.h new file mode 100644 index 0000000000000000000000000000000000000000..1a6c35cf398cca2a6da59415e3e85e9303dc511b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/inc/wlan_hdd_wowl.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_HDD_WOWL_H +#define _WLAN_HDD_WOWL_H + +/** + * DOC: wlan_hdd_wowl + * + * This module houses all the logic for WOWL in HDD. + * + * It provides the following APIs + * + * - Ability to enable/disable following WoWL modes + * 1) Magic packet (MP) mode + * 2) Pattern Byte Matching (PBM) mode + * - Ability to add/remove patterns for PBM + * + * A Magic Packet is a packet that contains 6 0xFFs followed by 16 + * contiguous copies of the receiving NIC's Ethernet address. There is + * no API to configure Magic Packet Pattern. + * + * Wakeup pattern (used for PBM) is defined as following: + * typedef struct + * { + * U8 PatternSize; // Non-Zero pattern size + * U8 PatternMaskSize; // Non-zero pattern mask size + * U8 PatternMask[PatternMaskSize]; // Pattern mask + * U8 Pattern[PatternSize]; // Pattern + * } hdd_wowl_ptrn_t; + * + * PatternSize and PatternMaskSize indicate size of the variable + * length Pattern and PatternMask. PatternMask indicates which bytes + * of an incoming packet should be compared with corresponding bytes + * in the pattern. + * + * Maximum allowed pattern size is 128 bytes. Maximum allowed + * PatternMaskSize is 16 bytes. + * + * Maximum number of patterns that can be configured is 8 + * + * HDD will add following 2 commonly used patterns for PBM by default: + * 1) ARP Broadcast Pattern + * 2) Unicast Pattern + * + * However note that WoWL will not be enabled by default by HDD. WoWL + * needs to enabled explcitly by exercising the iwpriv command. + * + * HDD will expose an API that accepts patterns as Hex string in the + * following format: + * "PatternSize:PatternMaskSize:PatternMask:Pattern" + * + * Multiple patterns can be specified by deleimiting each pattern with + * the ';' token: + * "PatternSize1:PatternMaskSize1:PatternMask1:Pattern1;PatternSize2:..." + * + * Patterns can be configured dynamically via iwpriv cmd or statically + * via qcom_cfg.ini file + * + * PBM (when enabled) can perform filtering on unicast data or + * broadcast data or both. These configurations are part of factory + * defaults (cfg.dat) and the default behavior is to perform filtering + * on both unicast and data frames. + * + * MP filtering (when enabled) is performed ALWAYS on both unicast and + * broadcast data frames. + * + * Management frames are not subjected to WoWL filtering and are + * discarded when WoWL is enabled. + * + * Whenever a patern match succeeds, RX path is restored and packets + * (both management and data) will be pushed to the host from that + * point onwards. Therefore, exit from WoWL is implicit and happens + * automatically when the first packet match succeeds. + * + * WoWL works on top of BMPS. So when WoWL is requested, SME will + * attempt to put the device in BMPS mode (if not already in BMPS). If + * attempt to BMPS fails, request for WoWL will be rejected. + */ + +#include +#include "wlan_pmo_wow_public_struct.h" + +#define WOWL_PTRN_MAX_SIZE 146 +#define WOWL_PTRN_MASK_MAX_SIZE 19 +#define WOWL_MAX_PTRNS_ALLOWED PMO_WOW_FILTERS_MAX + +/** + * hdd_add_wowl_ptrn() - Function which will add the WoWL pattern to be + * used when PBM filtering is enabled + * @adapter: pointer to the adapter + * @ptrn: pointer to the pattern string to be added + * + * Return: false if any errors encountered, true otherwise + */ +bool hdd_add_wowl_ptrn(struct hdd_adapter *adapter, const char *ptrn); + +/** + * hdd_del_wowl_ptrn() - Function which will remove a WoWL pattern + * @adapter: pointer to the adapter + * @ptrn: pointer to the pattern string to be removed + * + * Return: false if any errors encountered, true otherwise + */ +bool hdd_del_wowl_ptrn(struct hdd_adapter *adapter, const char *ptrn); + +/** + * hdd_add_wowl_ptrn_debugfs() - Function which will add a WoW pattern + * sent from debugfs interface + * @adapter: pointer to the adapter + * @pattern_idx: index of the pattern to be added + * @pattern_offset: offset of the pattern in the frame payload + * @pattern_buf: pointer to the pattern hex string to be added + * @pattern_mask: pointer to the pattern mask hex string + * + * Return: false if any errors encountered, true otherwise + */ +bool hdd_add_wowl_ptrn_debugfs(struct hdd_adapter *adapter, uint8_t pattern_idx, + uint8_t pattern_offset, char *pattern_buf, + char *pattern_mask); + +/** + * hdd_del_wowl_ptrn_debugfs() - Function which will remove a WoW pattern + * sent from debugfs interface + * @adapter: pointer to the adapter + * @pattern_idx: index of the pattern to be removed + * + * Return: false if any errors encountered, true otherwise + */ +bool hdd_del_wowl_ptrn_debugfs(struct hdd_adapter *adapter, + uint8_t pattern_idx); + +/** + * hdd_free_user_wowl_ptrns() - Deinit function to cleanup WoWL allocated memory + * + * Return: None + */ +void hdd_free_user_wowl_ptrns(void); + +#endif /* #ifndef _WLAN_HDD_WOWL_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_active_tos.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_active_tos.c new file mode 100644 index 0000000000000000000000000000000000000000..3fec47eb87dfae1a587eb3ef38fc04ad12caa920 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_active_tos.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_active_tos.c + * + * WLAN active tos functions + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include "wlan_policy_mgr_ucfg.h" +#include "wlan_scan_ucfg_api.h" + +#define HDD_AC_BK_BIT 1 +#define HDD_AC_BE_BIT 2 +#define HDD_AC_VI_BIT 4 +#define HDD_AC_VO_BIT 8 + +#define HDD_MAX_OFF_CHAN_TIME_FOR_VO 20 +#define HDD_MAX_OFF_CHAN_TIME_FOR_VI 20 +#define HDD_MAX_OFF_CHAN_TIME_FOR_BE 40 +#define HDD_MAX_OFF_CHAN_TIME_FOR_BK 40 + +#define HDD_MAX_OFF_CHAN_ENTRIES 2 + +#define HDD_AC_BIT_INDX 0 +#define HDD_DWELL_TIME_INDX 1 + +static int limit_off_chan_tbl[QCA_WLAN_AC_ALL][HDD_MAX_OFF_CHAN_ENTRIES] = { + { HDD_AC_BK_BIT, HDD_MAX_OFF_CHAN_TIME_FOR_BK }, + { HDD_AC_BE_BIT, HDD_MAX_OFF_CHAN_TIME_FOR_BE }, + { HDD_AC_VI_BIT, HDD_MAX_OFF_CHAN_TIME_FOR_VI }, + { HDD_AC_VO_BIT, HDD_MAX_OFF_CHAN_TIME_FOR_VO }, +}; + +static const struct nla_policy +wlan_hdd_set_limit_off_channel_param_policy +[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START] = {.type = NLA_U8 }, +}; + +/** + * hdd_set_limit_off_chan_for_tos() - set limit off-channel command parameters + * @adapter: HDD adapter + * @tos: type of service + * @is_tos_active: status of the traffic + * + * Return: 0 on success and non zero value on failure + */ + +static int +hdd_set_limit_off_chan_for_tos(struct hdd_adapter *adapter, + enum qca_wlan_ac_type tos, + bool is_tos_active) +{ + int ac_bit; + struct hdd_context *hdd_ctx; + uint32_t max_off_chan_time = 0; + QDF_STATUS status; + int ret; + uint8_t def_sys_pref = 0; + uint32_t rest_conc_time; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + + if (ret < 0) + return ret; + ucfg_policy_mgr_get_sys_pref(hdd_ctx->psoc, + &def_sys_pref); + + ac_bit = limit_off_chan_tbl[tos][HDD_AC_BIT_INDX]; + + if (is_tos_active) + adapter->active_ac |= ac_bit; + else + adapter->active_ac &= ~ac_bit; + + if (adapter->active_ac) { + if (adapter->active_ac & HDD_AC_VO_BIT) { + max_off_chan_time = + limit_off_chan_tbl[QCA_WLAN_AC_VO][HDD_DWELL_TIME_INDX]; + policy_mgr_set_cur_conc_system_pref(hdd_ctx->psoc, + PM_LATENCY); + } else if (adapter->active_ac & HDD_AC_VI_BIT) { + max_off_chan_time = + limit_off_chan_tbl[QCA_WLAN_AC_VI][HDD_DWELL_TIME_INDX]; + policy_mgr_set_cur_conc_system_pref(hdd_ctx->psoc, + PM_LATENCY); + } else { + /*ignore this command if only BE/BK is active */ + is_tos_active = false; + policy_mgr_set_cur_conc_system_pref(hdd_ctx->psoc, + def_sys_pref); + } + } else { + /* No active tos */ + policy_mgr_set_cur_conc_system_pref(hdd_ctx->psoc, + def_sys_pref); + } + + ucfg_scan_cfg_get_conc_max_resttime(hdd_ctx->psoc, &rest_conc_time); + status = sme_send_limit_off_channel_params(hdd_ctx->mac_handle, + adapter->vdev_id, + is_tos_active, + max_off_chan_time, + rest_conc_time, + true); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("failed to set limit off chan params"); + ret = -EINVAL; + } + + return ret; +} + +/** + * __wlan_hdd_cfg80211_set_limit_offchan_param() - set limit off-channel cmd + * parameters + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: pointer to limit off-channel command parameters. + * @data_len: the length in byte of limit off-channel command parameters. + * + * This is called when application wants to limit the off channel time due to + * active voip traffic. + * + * Return: An error code or 0 on success. + */ +static int +__wlan_hdd_cfg80211_set_limit_offchan_param(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_MAX + 1]; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int ret = 0; + uint8_t tos; + uint8_t tos_status; + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret < 0) + return ret; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_MAX, + data, data_len, + wlan_hdd_set_limit_off_channel_param_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS]) { + hdd_err("attr tos failed"); + goto fail; + } + + tos = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS]); + if (tos >= QCA_WLAN_AC_ALL) { + hdd_err("tos value %d exceeded Max value %d", + tos, QCA_WLAN_AC_ALL); + goto fail; + } + hdd_debug("tos %d", tos); + + if (!tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START]) { + hdd_err("attr tos active failed"); + goto fail; + } + tos_status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START]); + + hdd_debug("tos status %d", tos_status); + ret = hdd_set_limit_off_chan_for_tos(adapter, tos, tos_status); + +fail: + return ret; +} + +int wlan_hdd_cfg80211_set_limit_offchan_param(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) + +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_limit_offchan_param(wiphy, wdev, data, + data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_active_tos.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_active_tos.h new file mode 100644 index 0000000000000000000000000000000000000000..80f39c4ac2e9f7e0cb498c44e6d62e7910ac5511 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_active_tos.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_ACTIVE_TOS_H +#define __WLAN_HDD_ACTIVE_TOS_H + +/** + * DOC: wlan_hdd_active_tos_h + * + * WLAN Host Device Driver ACTIVE TOS API specification + */ + +#ifdef FEATURE_ACTIVE_TOS +/** + * wlan_hdd_cfg80211_set_limit_offchan_param() - set limit off-channel cmd + * parameters + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: pointer to limit off-channel command parameters. + * @data_len: the length in byte of limit off-channel command parameters. + * + * This is called when application wants to limit the off channel time due to + * active voip traffic. + * + * Return: An error code or 0 on success. + */ +int wlan_hdd_cfg80211_set_limit_offchan_param(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +#define FEATURE_ACTIVE_TOS_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_set_limit_offchan_param \ +}, +#else /* FEATURE_ACTIVE_TOS */ +#define FEATURE_ACTIVE_TOS_VENDOR_COMMANDS +#endif /* FEATURE_ACTIVE_TOS */ + +#endif /* __WLAN_HDD_ACTIVE_TOS_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_apf.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_apf.c new file mode 100644 index 0000000000000000000000000000000000000000..c49b471e96c68c92b9eb477eec2c4f7af7e5d8b7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_apf.c @@ -0,0 +1,716 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_apf.c + * + * Android Packet Filter support and implementation + */ + +#include "wlan_hdd_apf.h" +#include "osif_sync.h" +#include "qca_vendor.h" +#include "wlan_osif_request_manager.h" + +/* + * define short names for the global vendor params + * used by __wlan_hdd_cfg80211_apf_offload() + */ +#define APF_INVALID \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID +#define APF_SUBCMD \ + QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER +#define APF_VERSION \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION +#define APF_FILTER_ID \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID +#define APF_PACKET_SIZE \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE +#define APF_CURRENT_OFFSET \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET +#define APF_PROGRAM \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM +#define APF_PROG_LEN \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH +#define APF_MAX \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX + +static const struct nla_policy +wlan_hdd_apf_offload_policy[APF_MAX + 1] = { + [APF_SUBCMD] = {.type = NLA_U32}, + [APF_VERSION] = {.type = NLA_U32}, + [APF_FILTER_ID] = {.type = NLA_U32}, + [APF_PACKET_SIZE] = {.type = NLA_U32}, + [APF_CURRENT_OFFSET] = {.type = NLA_U32}, + [APF_PROGRAM] = {.type = NLA_BINARY, + .len = MAX_APF_MEMORY_LEN}, + [APF_PROG_LEN] = {.type = NLA_U32}, +}; + +void hdd_apf_context_init(struct hdd_adapter *adapter) +{ + qdf_event_create(&adapter->apf_context.qdf_apf_event); + qdf_spinlock_create(&adapter->apf_context.lock); + adapter->apf_context.apf_enabled = true; +} + +void hdd_apf_context_destroy(struct hdd_adapter *adapter) +{ + qdf_event_destroy(&adapter->apf_context.qdf_apf_event); + qdf_spinlock_destroy(&adapter->apf_context.lock); + qdf_mem_zero(&adapter->apf_context, + sizeof(struct hdd_apf_context)); +} + +struct apf_offload_priv { + struct sir_apf_get_offload apf_get_offload; +}; + +void hdd_get_apf_capabilities_cb(void *context, + struct sir_apf_get_offload *data) +{ + struct osif_request *request; + struct apf_offload_priv *priv; + + hdd_enter(); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + priv->apf_get_offload = *data; + osif_request_complete(request); + osif_request_put(request); + + hdd_exit(); +} + +/** + * hdd_post_get_apf_capabilities_rsp() - Callback function to APF Offload + * @hdd_context: hdd_context + * @apf_get_offload: struct for get offload + * + * Return: 0 on success, error number otherwise. + */ +static int +hdd_post_get_apf_capabilities_rsp(struct hdd_context *hdd_ctx, + struct sir_apf_get_offload *apf_get_offload) +{ + struct sk_buff *skb; + uint32_t nl_buf_len; + + hdd_enter(); + + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += + (sizeof(apf_get_offload->max_bytes_for_apf_inst) + NLA_HDRLEN) + + (sizeof(apf_get_offload->apf_version) + NLA_HDRLEN); + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + hdd_ctx->apf_version = apf_get_offload->apf_version; + hdd_debug("APF Version: %u APF max bytes: %u", + apf_get_offload->apf_version, + apf_get_offload->max_bytes_for_apf_inst); + + if (nla_put_u32(skb, APF_PACKET_SIZE, + apf_get_offload->max_bytes_for_apf_inst) || + nla_put_u32(skb, APF_VERSION, apf_get_offload->apf_version)) { + hdd_err("nla put failure"); + goto nla_put_failure; + } + + cfg80211_vendor_cmd_reply(skb); + hdd_exit(); + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * hdd_get_apf_capabilities - Get APF offload Capabilities + * @hdd_ctx: Hdd context + * + * Return: 0 on success, errno on failure + */ +static int hdd_get_apf_capabilities(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + int ret; + void *cookie; + struct osif_request *request; + struct apf_offload_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_APF, + }; + + hdd_enter(); + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Unable to allocate request"); + return -EINVAL; + } + cookie = osif_request_cookie(request); + + status = sme_get_apf_capabilities(hdd_ctx->mac_handle, + hdd_get_apf_capabilities_cb, + cookie); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Unable to retrieve APF caps"); + ret = qdf_status_to_os_return(status); + goto cleanup; + } + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("Target response timed out"); + goto cleanup; + } + priv = osif_request_priv(request); + ret = hdd_post_get_apf_capabilities_rsp(hdd_ctx, + &priv->apf_get_offload); + if (ret) + hdd_err("Failed to post get apf capabilities"); + +cleanup: + /* + * either we never sent a request to SME, we sent a request to + * SME and timed out, or we sent a request to SME, received a + * response from SME, and posted the response to userspace. + * regardless we are done with the request. + */ + osif_request_put(request); + hdd_exit(); + + return ret; +} + +/** + * hdd_set_reset_apf_offload - Post set/reset apf to SME + * @hdd_ctx: Hdd context + * @tb: Length of @data + * @adapter: pointer to adapter struct + * + * Return: 0 on success; errno on failure + */ +static int hdd_set_reset_apf_offload(struct hdd_context *hdd_ctx, + struct nlattr **tb, + struct hdd_adapter *adapter) +{ + struct sir_apf_set_offload apf_set_offload = {0}; + QDF_STATUS status; + int prog_len; + int ret = 0; + + if (!hdd_conn_is_connected( + WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + hdd_err("Not in Connected state!"); + return -ENOTSUPP; + } + + /* Parse and fetch apf packet size */ + if (!tb[APF_PACKET_SIZE]) { + hdd_err("attr apf packet size failed"); + ret = -EINVAL; + goto fail; + } + + apf_set_offload.session_id = adapter->vdev_id; + apf_set_offload.total_length = nla_get_u32(tb[APF_PACKET_SIZE]); + + if (!apf_set_offload.total_length) { + hdd_debug("APF reset packet"); + goto post_sme; + } + + /* Parse and fetch apf program */ + if (!tb[APF_PROGRAM]) { + hdd_err("attr apf program failed"); + ret = -EINVAL; + goto fail; + } + + prog_len = nla_len(tb[APF_PROGRAM]); + apf_set_offload.program = qdf_mem_malloc(sizeof(uint8_t) * prog_len); + + if (!apf_set_offload.program) { + ret = -ENOMEM; + goto fail; + } + + apf_set_offload.current_length = prog_len; + nla_memcpy(apf_set_offload.program, tb[APF_PROGRAM], prog_len); + + /* Parse and fetch filter Id */ + if (!tb[APF_FILTER_ID]) { + hdd_err("attr filter id failed"); + ret = -EINVAL; + goto fail; + } + apf_set_offload.filter_id = nla_get_u32(tb[APF_FILTER_ID]); + + /* Parse and fetch current offset */ + if (!tb[APF_CURRENT_OFFSET]) { + hdd_err("attr current offset failed"); + ret = -EINVAL; + goto fail; + } + apf_set_offload.current_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]); + +post_sme: + hdd_debug("Posting, session_id: %d APF Version: %d filter ID: %d total_len: %d current_len: %d offset: %d", + apf_set_offload.session_id, apf_set_offload.version, + apf_set_offload.filter_id, apf_set_offload.total_length, + apf_set_offload.current_length, + apf_set_offload.current_offset); + + status = sme_set_apf_instructions(hdd_ctx->mac_handle, + &apf_set_offload); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_set_apf_instructions failed(err=%d)", status); + ret = -EINVAL; + goto fail; + } + hdd_exit(); + +fail: + if (apf_set_offload.current_length) + qdf_mem_free(apf_set_offload.program); + + if (!ret) + hdd_ctx->apf_enabled_v2 = true; + + return ret; +} + +/** + * hdd_enable_disable_apf - Enable or Disable the APF interpreter + * @adapter: HDD Adapter + * @apf_enable: true: Enable APF Int., false: disable APF Int. + * + * Return: 0 on success, errno on failure + */ +static int +hdd_enable_disable_apf(struct hdd_adapter *adapter, bool apf_enable) +{ + QDF_STATUS status; + + status = sme_set_apf_enable_disable(hdd_adapter_get_mac_handle(adapter), + adapter->vdev_id, apf_enable); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Unable to post sme apf enable/disable message (status-%d)", + status); + return -EINVAL; + } + + adapter->apf_context.apf_enabled = apf_enable; + + return 0; +} + +/** + * hdd_apf_write_memory - Write into the apf work memory + * @adapter: HDD Adapter + * @tb: list of attributes + * + * This function writes code/data into the APF work memory and + * provides program length that is passed on to the interpreter. + * + * Return: 0 on success, errno on failure + */ +static int +hdd_apf_write_memory(struct hdd_adapter *adapter, struct nlattr **tb) +{ + struct wmi_apf_write_memory_params write_mem_params = {0}; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + int ret = 0; + + write_mem_params.vdev_id = adapter->vdev_id; + if (adapter->apf_context.apf_enabled) { + hdd_err("Cannot get/set when APF interpreter is enabled"); + return -EINVAL; + } + + /* Read program length */ + if (!tb[APF_PROG_LEN]) { + hdd_err("attr program length failed"); + return -EINVAL; + } + write_mem_params.program_len = nla_get_u32(tb[APF_PROG_LEN]); + + /* Read APF work memory offset */ + if (!tb[APF_CURRENT_OFFSET]) { + hdd_err("attr apf packet size failed"); + return -EINVAL; + } + write_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]); + + /* Parse and fetch apf program */ + if (!tb[APF_PROGRAM]) { + hdd_err("attr apf program failed"); + return -EINVAL; + } + + write_mem_params.length = nla_len(tb[APF_PROGRAM]); + if (!write_mem_params.length) { + hdd_err("Program attr with empty data"); + return -EINVAL; + } + + write_mem_params.buf = qdf_mem_malloc(sizeof(uint8_t) + * write_mem_params.length); + if (!write_mem_params.buf) + return -EINVAL; + nla_memcpy(write_mem_params.buf, tb[APF_PROGRAM], + write_mem_params.length); + + write_mem_params.apf_version = hdd_ctx->apf_version; + + status = sme_apf_write_work_memory(hdd_adapter_get_mac_handle(adapter), + &write_mem_params); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Unable to retrieve APF caps"); + ret = -EINVAL; + } + + hdd_debug("Writing successful into APF work memory from offset 0x%X:", + write_mem_params.addr_offset); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + write_mem_params.buf, write_mem_params.length); + + if (write_mem_params.buf) + qdf_mem_free(write_mem_params.buf); + + return ret; +} + +/** + * hdd_apf_read_memory_callback - HDD Callback for the APF read memory + * operation + * @context: Hdd context + * @evt: APF read memory event response parameters + * + * Return: 0 on success, errno on failure + */ +static void +hdd_apf_read_memory_callback(void *hdd_context, + struct wmi_apf_read_memory_resp_event_params *evt) +{ + struct hdd_context *hdd_ctx = hdd_context; + struct hdd_adapter *adapter; + struct hdd_apf_context *context; + uint8_t *buf_ptr; + uint32_t pkt_offset; + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx) || !evt) { + hdd_err("HDD context is invalid or event buf(%pK) is null", + evt); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, evt->vdev_id); + if (hdd_validate_adapter(adapter)) + return; + context = &adapter->apf_context; + + if (context->magic != APF_CONTEXT_MAGIC) { + /* The caller presumably timed out, nothing to do */ + hdd_err("Caller timed out or corrupt magic, simply return"); + return; + } + + if (evt->offset < context->offset) { + hdd_err("Offset in read event(%d) smaller than offset in request(%d)!", + evt->offset, context->offset); + return; + } + + /* + * offset in the event is relative to the APF work memory. + * Calculate the packet offset, which gives us the relative + * location in the buffer to start copy into. + */ + pkt_offset = evt->offset - context->offset; + + if ((pkt_offset > context->buf_len) || + (context->buf_len - pkt_offset < evt->length)) { + hdd_err("Read chunk exceeding allocated space"); + return; + } + buf_ptr = context->buf + pkt_offset; + + qdf_mem_copy(buf_ptr, evt->data, evt->length); + + if (!evt->more_data) { + /* Release the caller after last event, clear magic */ + context->magic = 0; + qdf_event_set(&context->qdf_apf_event); + } + + hdd_exit(); +} + +/** + * hdd_apf_read_memory - Read part of the apf work memory + * @adapter: HDD Adapter + * @tb: list of attributes + * + * Return: 0 on success, errno on failure + */ +static int hdd_apf_read_memory(struct hdd_adapter *adapter, struct nlattr **tb) +{ + struct wmi_apf_read_memory_params read_mem_params = {0}; + struct hdd_apf_context *context = &adapter->apf_context; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + unsigned long nl_buf_len = NLMSG_HDRLEN; + int ret = 0; + struct sk_buff *skb = NULL; + uint8_t *bufptr; + + if (context->apf_enabled) { + hdd_err("Cannot get/set while interpreter is enabled"); + return -EINVAL; + } + + read_mem_params.vdev_id = adapter->vdev_id; + + /* Read APF work memory offset */ + if (!tb[APF_CURRENT_OFFSET]) { + hdd_err("attr apf memory offset failed"); + return -EINVAL; + } + read_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]); + if (read_mem_params.addr_offset > MAX_APF_MEMORY_LEN) { + hdd_err("attr apf memory offset should be less than %d", + MAX_APF_MEMORY_LEN); + return -EINVAL; + } + + /* Read length */ + if (!tb[APF_PACKET_SIZE]) { + hdd_err("attr apf packet size failed"); + return -EINVAL; + } + read_mem_params.length = nla_get_u32(tb[APF_PACKET_SIZE]); + if (!read_mem_params.length) { + hdd_err("apf read length cannot be zero!"); + return -EINVAL; + } + bufptr = qdf_mem_malloc(read_mem_params.length); + if (!bufptr) + return -ENOMEM; + + qdf_event_reset(&context->qdf_apf_event); + context->offset = read_mem_params.addr_offset; + + context->buf = bufptr; + context->buf_len = read_mem_params.length; + context->magic = APF_CONTEXT_MAGIC; + + status = sme_apf_read_work_memory(hdd_adapter_get_mac_handle(adapter), + &read_mem_params, + hdd_apf_read_memory_callback); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to post sme APF read memory message (status-%d)", + status); + ret = -EINVAL; + goto fail; + } + + /* request was sent -- wait for the response */ + status = qdf_wait_for_event_completion(&context->qdf_apf_event, + WLAN_WAIT_TIME_APF_READ_MEM); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Target response timed out"); + context->magic = 0; + ret = -ETIMEDOUT; + goto fail; + } + + nl_buf_len += sizeof(uint32_t) + NLA_HDRLEN; + nl_buf_len += context->buf_len + NLA_HDRLEN; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + ret = -ENOMEM; + goto fail; + } + + if (nla_put_u32(skb, APF_SUBCMD, QCA_WLAN_READ_PACKET_FILTER) || + nla_put(skb, APF_PROGRAM, read_mem_params.length, context->buf)) { + hdd_err("put fail"); + kfree_skb(skb); + ret = -EINVAL; + goto fail; + } + + cfg80211_vendor_cmd_reply(skb); + + hdd_debug("Reading APF work memory from offset 0x%X:", + read_mem_params.addr_offset); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + context->buf, read_mem_params.length); +fail: + if (context->buf) { + qdf_mem_free(context->buf); + context->buf = NULL; + } + + return ret; +} + +/** + * wlan_hdd_cfg80211_apf_offload() - Set/Reset to APF Offload + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +static int +__wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[APF_MAX + 1]; + int ret_val = 0, apf_subcmd; + struct hdd_apf_context *context; + + hdd_enter(); + + if (!adapter) { + hdd_err("Adapter is null"); + return -EINVAL; + } + + context = &adapter->apf_context; + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (!ucfg_pmo_is_apf_enabled(hdd_ctx->psoc)) { + hdd_err("APF is not supported or disabled through INI"); + return -ENOTSUPP; + } + + if (!(adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE)) { + hdd_err("APF only supported in STA or P2P CLI modes!"); + return -ENOTSUPP; + } + + if (wlan_cfg80211_nla_parse(tb, APF_MAX, data, data_len, + wlan_hdd_apf_offload_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[APF_SUBCMD]) { + hdd_err("attr apf sub-command failed"); + return -EINVAL; + } + apf_subcmd = nla_get_u32(tb[APF_SUBCMD]); + + /* Do not allow simultaneous new APF commands on the same adapter */ + qdf_spin_lock(&context->lock); + if (context->cmd_in_progress) { + qdf_spin_unlock(&context->lock); + hdd_err("Another cmd in progress for same session!"); + return -EAGAIN; + } + context->cmd_in_progress = true; + qdf_spin_unlock(&context->lock); + + switch (apf_subcmd) { + /* Legacy APF sub-commands */ + case QCA_WLAN_SET_PACKET_FILTER: + ret_val = hdd_set_reset_apf_offload(hdd_ctx, tb, + adapter); + break; + case QCA_WLAN_GET_PACKET_FILTER: + ret_val = hdd_get_apf_capabilities(hdd_ctx); + break; + + /* APF 3.0 sub-commands */ + case QCA_WLAN_WRITE_PACKET_FILTER: + ret_val = hdd_apf_write_memory(adapter, tb); + break; + case QCA_WLAN_READ_PACKET_FILTER: + ret_val = hdd_apf_read_memory(adapter, tb); + break; + case QCA_WLAN_ENABLE_PACKET_FILTER: + ret_val = hdd_enable_disable_apf(adapter, true); + break; + case QCA_WLAN_DISABLE_PACKET_FILTER: + ret_val = hdd_enable_disable_apf(adapter, false); + break; + default: + hdd_err("Unknown APF Sub-command: %d", apf_subcmd); + ret_val = -ENOTSUPP; + } + + qdf_spin_lock(&context->lock); + context->cmd_in_progress = false; + qdf_spin_unlock(&context->lock); + + return ret_val; +} + +int +wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_apf_offload(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_assoc.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_assoc.c new file mode 100644 index 0000000000000000000000000000000000000000..7cf4b2e7723d4806926375b511854cb2463394f5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_assoc.c @@ -0,0 +1,6103 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_assoc.c + * + * WLAN Host Device Driver implementation + * + */ + +#include "wlan_hdd_includes.h" +#include +#include "dot11f.h" +#include "wlan_hdd_power.h" +#include "wlan_hdd_trace.h" +#include +#include +#include +#include +#include "wlan_hdd_cfg80211.h" +#include "csr_inside_api.h" +#include "wlan_hdd_p2p.h" +#include "wlan_hdd_tdls.h" +#include "sme_api.h" +#include "wlan_hdd_hostapd.h" +#include +#include +#include "wlan_hdd_lpass.h" +#include +#include +#include "wlan_policy_mgr_api.h" +#include +#include "sme_power_save_api.h" +#include "wlan_hdd_napi.h" +#include +#include +#include +#include +#include +#include "ol_txrx.h" +#include +#include +#include +#include "wlan_pmo_ucfg_api.h" +#include "wlan_hdd_tsf.h" +#include "wlan_utility.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_ipa_ucfg_api.h" +#include "wlan_hdd_stats.h" +#include "wlan_hdd_scan.h" +#include "wlan_crypto_global_api.h" +#include "wlan_hdd_bcn_recv.h" + +#include "wlan_hdd_nud_tracking.h" +#include +#include +#include "wlan_blm_ucfg_api.h" +#include "wlan_hdd_sta_info.h" +#include "wlan_hdd_periodic_sta_stats.h" + +#include + +/* These are needed to recognize WPA and RSN suite types */ +#define HDD_WPA_OUI_SIZE 4 +#define HDD_RSN_OUI_SIZE 4 +uint8_t ccp_wpa_oui00[HDD_WPA_OUI_SIZE] = { 0x00, 0x50, 0xf2, 0x00 }; +uint8_t ccp_wpa_oui01[HDD_WPA_OUI_SIZE] = { 0x00, 0x50, 0xf2, 0x01 }; +uint8_t ccp_wpa_oui02[HDD_WPA_OUI_SIZE] = { 0x00, 0x50, 0xf2, 0x02 }; +uint8_t ccp_wpa_oui03[HDD_WPA_OUI_SIZE] = { 0x00, 0x50, 0xf2, 0x03 }; +uint8_t ccp_wpa_oui04[HDD_WPA_OUI_SIZE] = { 0x00, 0x50, 0xf2, 0x04 }; +uint8_t ccp_wpa_oui05[HDD_WPA_OUI_SIZE] = { 0x00, 0x50, 0xf2, 0x05 }; + +#ifdef FEATURE_WLAN_ESE +/* CCKM */ +uint8_t ccp_wpa_oui06[HDD_WPA_OUI_SIZE] = { 0x00, 0x40, 0x96, 0x00 }; +/* CCKM */ +uint8_t ccp_rsn_oui06[HDD_RSN_OUI_SIZE] = { 0x00, 0x40, 0x96, 0x00 }; +#endif /* FEATURE_WLAN_ESE */ + +/* group cipher */ +uint8_t ccp_rsn_oui00[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x00 }; + +/* WEP-40 or RSN */ +uint8_t ccp_rsn_oui01[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x01 }; + +/* TKIP or RSN-PSK */ +uint8_t ccp_rsn_oui02[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x02 }; + +/* Reserved */ +uint8_t ccp_rsn_oui03[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x03 }; + +/* AES-CCMP */ +uint8_t ccp_rsn_oui04[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x04 }; + +/* WEP-104 */ +uint8_t ccp_rsn_oui05[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x05 }; + +#ifdef WLAN_FEATURE_11W +/* RSN-PSK-SHA256 */ +uint8_t ccp_rsn_oui07[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x06 }; + +/* RSN-8021X-SHA256 */ +uint8_t ccp_rsn_oui08[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x05 }; +#endif + +/* AES-GCMP-128 */ +uint8_t ccp_rsn_oui09[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x08 }; + +/* AES-GCMP-256 */ +uint8_t ccp_rsn_oui0a[HDD_RSN_OUI_SIZE] = { 0x00, 0x0F, 0xAC, 0x09 }; +#ifdef WLAN_FEATURE_FILS_SK +uint8_t ccp_rsn_oui_0e[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x0E}; +uint8_t ccp_rsn_oui_0f[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x0F}; +uint8_t ccp_rsn_oui_10[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x10}; +uint8_t ccp_rsn_oui_11[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x11}; +#endif +uint8_t ccp_rsn_oui_12[HDD_RSN_OUI_SIZE] = {0x50, 0x6F, 0x9A, 0x02}; +uint8_t ccp_rsn_oui_0b[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x0B}; +uint8_t ccp_rsn_oui_0c[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x0C}; +/* FT-SUITE-B AKM */ +uint8_t ccp_rsn_oui_0d[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x0D}; + +/* OWE https://tools.ietf.org/html/rfc8110 */ +uint8_t ccp_rsn_oui_18[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x12}; + +#ifdef WLAN_FEATURE_SAE +/* SAE AKM */ +uint8_t ccp_rsn_oui_80[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x08}; +/* FT SAE AKM */ +uint8_t ccp_rsn_oui_90[HDD_RSN_OUI_SIZE] = {0x00, 0x0F, 0xAC, 0x09}; +#endif +static const +u8 ccp_rsn_oui_13[HDD_RSN_OUI_SIZE] = {0x50, 0x6F, 0x9A, 0x01}; + +/* Offset where the EID-Len-IE, start. */ +#define FT_ASSOC_RSP_IES_OFFSET 6 /* Capability(2) + AID(2) + Status Code(2) */ +#define FT_ASSOC_REQ_IES_OFFSET 4 /* Capability(2) + LI(2) */ + +#define HDD_PEER_AUTHORIZE_WAIT 10 + +/** + * beacon_filter_table - table of IEs used for beacon filtering + */ +static const int beacon_filter_table[] = { + WLAN_ELEMID_DSPARMS, + WLAN_ELEMID_ERP, + WLAN_ELEMID_EDCAPARMS, + WLAN_ELEMID_QOS_CAPABILITY, + WLAN_ELEMID_HTINFO_ANA, + WLAN_ELEMID_OP_MODE_NOTIFY, + WLAN_ELEMID_VHTOP, +#ifdef WLAN_FEATURE_11AX_BSS_COLOR + /* + * EID: 221 vendor IE is being used temporarily by 11AX + * bss-color-change IE till it gets any fixed number. This + * vendor EID needs to be replaced with bss-color-change IE + * number. + */ + WLAN_ELEMID_VENDOR, +#endif +}; + +/* HE operation BIT positins */ +#if defined(WLAN_FEATURE_11AX) +#define HE_OPERATION_DFLT_PE_DURATION_POS 0 +#define HE_OPERATION_TWT_REQUIRED_POS 3 +#define HE_OPERATION_RTS_THRESHOLD_POS 4 +#define HE_OPERATION_VHT_OPER_POS 14 +#define HE_OPERATION_CO_LOCATED_BSS_POS 15 +#define HE_OPERATION_ER_SU_DISABLE_POS 16 +#define HE_OPERATION_OPER_INFO_6G_POS 17 +#define HE_OPERATION_RESERVED_POS 18 +#define HE_OPERATION_BSS_COLOR_POS 24 +#define HE_OPERATION_PARTIAL_BSS_COLOR_POS 30 +#define HE_OPERATION_BSS_COL_DISABLED_POS 31 +#endif + +#if defined(WLAN_FEATURE_SAE) && \ + (defined(CFG80211_EXTERNAL_AUTH_SUPPORT) || \ + LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) +/** + * wlan_hdd_sae_callback() - Sends SAE info to supplicant + * @adapter: pointer adapter context + * @roam_info: pointer to roam info + * + * This API is used to send required SAE info to trigger SAE in supplicant. + * + * Return: None + */ +static void wlan_hdd_sae_callback(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + int flags; + struct sir_sae_info *sae_info = roam_info->sae_info; + struct cfg80211_external_auth_params params = {0}; + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + if (!sae_info) { + hdd_err("SAE info in NULL"); + return; + } + + flags = cds_get_gfp_flags(); + + params.key_mgmt_suite = 0x00; + params.key_mgmt_suite |= 0x0F << 8; + params.key_mgmt_suite |= 0xAC << 16; + params.key_mgmt_suite |= 0x8 << 24; + + params.action = NL80211_EXTERNAL_AUTH_START; + qdf_mem_copy(params.bssid, sae_info->peer_mac_addr.bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(params.ssid.ssid, sae_info->ssid.ssId, + sae_info->ssid.length); + params.ssid.ssid_len = sae_info->ssid.length; + + cfg80211_external_auth_request(adapter->dev, ¶ms, flags); + hdd_debug("SAE: sent cmd"); +} +#else +static inline void wlan_hdd_sae_callback(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ } +#endif + +/** + * hdd_conn_set_authenticated() - set authentication state + * @adapter: pointer to the adapter + * @auth_state: authentication state + * + * This function updates the global HDD station context + * authentication state. + * + * Return: none + */ +static void +hdd_conn_set_authenticated(struct hdd_adapter *adapter, uint8_t auth_state) +{ + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + char *auth_time; + uint32_t time_buffer_size; + + /* save the new connection state */ + hdd_debug("Authenticated state Changed from oldState:%d to State:%d", + sta_ctx->conn_info.is_authenticated, auth_state); + sta_ctx->conn_info.is_authenticated = auth_state; + + auth_time = sta_ctx->conn_info.auth_time; + time_buffer_size = sizeof(sta_ctx->conn_info.auth_time); + + if (auth_state) + qdf_get_time_of_the_day_in_hr_min_sec_usec(auth_time, + time_buffer_size); + else + qdf_mem_zero(auth_time, time_buffer_size); + +} + +void hdd_conn_set_connection_state(struct hdd_adapter *adapter, + eConnectionState conn_state) +{ + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + char *connect_time; + uint32_t time_buffer_size; + + /* save the new connection state */ + if (conn_state == hdd_sta_ctx->conn_info.conn_state) + return; + + hdd_nofl_debug("connection state changed %d --> %d for dev %s (vdev %d)", + hdd_sta_ctx->conn_info.conn_state, conn_state, + adapter->dev->name, adapter->vdev_id); + + hdd_tsf_notify_wlan_state_change(adapter, + hdd_sta_ctx->conn_info.conn_state, + conn_state); + hdd_sta_ctx->conn_info.conn_state = conn_state; + + connect_time = hdd_sta_ctx->conn_info.connect_time; + time_buffer_size = sizeof(hdd_sta_ctx->conn_info.connect_time); + if (conn_state == eConnectionState_Associated) + qdf_get_time_of_the_day_in_hr_min_sec_usec(connect_time, + time_buffer_size); + else + qdf_mem_zero(connect_time, time_buffer_size); + +} + +/** + * hdd_conn_get_connection_state() - get connection state + * @adapter: pointer to the adapter + * @pConnState: pointer to connection state + * + * This function updates the global HDD station context connection state. + * + * Return: true if (Infra Associated or IBSS Connected) + * and sets output parameter pConnState; + * false otherwise + */ +static inline bool +hdd_conn_get_connection_state(struct hdd_station_ctx *sta_ctx, + eConnectionState *out_state) +{ + eConnectionState state = sta_ctx->conn_info.conn_state; + + if (out_state) + *out_state = state; + + switch (state) { + case eConnectionState_Associated: + case eConnectionState_IbssConnected: + case eConnectionState_IbssDisconnected: + case eConnectionState_NdiConnected: + return true; + default: + return false; + } +} + +bool hdd_is_connecting(struct hdd_station_ctx *hdd_sta_ctx) +{ + return hdd_sta_ctx->conn_info.conn_state == + eConnectionState_Connecting; +} + +bool hdd_conn_is_connected(struct hdd_station_ctx *sta_ctx) +{ + return hdd_conn_get_connection_state(sta_ctx, NULL); +} + +bool hdd_adapter_is_connected_sta(struct hdd_adapter *adapter) +{ + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_NDI_MODE: + return hdd_conn_is_connected(&adapter->session.station); + default: + return false; + } +} + +enum band_info hdd_conn_get_connected_band(struct hdd_station_ctx *sta_ctx) +{ + uint32_t sta_freq = 0; + + if (eConnectionState_Associated == sta_ctx->conn_info.conn_state) + sta_freq = sta_ctx->conn_info.chan_freq; + + if (wlan_reg_is_24ghz_ch_freq(sta_freq)) + return BAND_2G; + else if (wlan_reg_is_5ghz_ch_freq(sta_freq) || + wlan_reg_is_6ghz_chan_freq(sta_freq)) + return BAND_5G; + else /* If station is not connected return as BAND_ALL */ + return BAND_ALL; +} + +/** + * hdd_conn_get_connected_cipher_algo() - get current connection cipher type + * @sta_ctx: pointer to global HDD Station context + * @pConnectedCipherAlgo: pointer to connected cipher algo + * + * Return: false if any errors encountered, true otherwise + */ +static inline bool +hdd_conn_get_connected_cipher_algo(struct hdd_station_ctx *sta_ctx, + eCsrEncryptionType *pConnectedCipherAlgo) +{ + bool connected = false; + + connected = hdd_conn_get_connection_state(sta_ctx, NULL); + + if (pConnectedCipherAlgo) + *pConnectedCipherAlgo = sta_ctx->conn_info.uc_encrypt_type; + + return connected; +} + +struct hdd_adapter *hdd_get_sta_connection_in_progress( + struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + struct hdd_station_ctx *hdd_sta_ctx; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_GET_STA_CONNECTION_IN_PROGRESS; + + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return NULL; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if ((QDF_STA_MODE == adapter->device_mode) || + (QDF_P2P_CLIENT_MODE == adapter->device_mode) || + (QDF_P2P_DEVICE_MODE == adapter->device_mode)) { + if (eConnectionState_Connecting == + hdd_sta_ctx->conn_info.conn_state) { + hdd_debug("vdev_id %d: Connection is in progress", + adapter->vdev_id); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return adapter; + } else if ((eConnectionState_Associated == + hdd_sta_ctx->conn_info.conn_state) && + sme_is_sta_key_exchange_in_progress( + hdd_ctx->mac_handle, + adapter->vdev_id)) { + hdd_debug("vdev_id %d: Key exchange is in progress", + adapter->vdev_id); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return adapter; + } + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + return NULL; +} + +void hdd_abort_ongoing_sta_connection(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *sta_adapter; + QDF_STATUS status; + + sta_adapter = hdd_get_sta_connection_in_progress(hdd_ctx); + if (sta_adapter) { + hdd_debug("Disconnecting STA on vdev: %d", + sta_adapter->vdev_id); + status = wlan_hdd_disconnect(sta_adapter, + eCSR_DISCONNECT_REASON_DEAUTH, + eSIR_MAC_UNSPEC_FAILURE_REASON); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("wlan_hdd_disconnect failed, status: %d", + status); + } + } +} + +/** + * hdd_remove_beacon_filter() - remove beacon filter + * @adapter: Pointer to the hdd adapter + * + * Return: 0 on success and errno on failure + */ +static int hdd_remove_beacon_filter(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + status = sme_remove_beacon_filter(hdd_ctx->mac_handle, + adapter->vdev_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_remove_beacon_filter() failed"); + return -EFAULT; + } + + return 0; +} + +int hdd_add_beacon_filter(struct hdd_adapter *adapter) +{ + int i; + uint32_t ie_map[SIR_BCN_FLT_MAX_ELEMS_IE_LIST] = {0}; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + for (i = 0; i < ARRAY_SIZE(beacon_filter_table); i++) + qdf_set_bit((beacon_filter_table[i]), + (unsigned long int *)ie_map); + + status = sme_add_beacon_filter(hdd_ctx->mac_handle, + adapter->vdev_id, ie_map); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_add_beacon_filter() failed"); + return -EFAULT; + } + return 0; +} + +void hdd_copy_ht_caps(struct ieee80211_ht_cap *hdd_ht_cap, + tDot11fIEHTCaps *roam_ht_cap) + +{ + uint32_t i, temp_ht_cap; + + qdf_mem_zero(hdd_ht_cap, sizeof(struct ieee80211_ht_cap)); + + if (roam_ht_cap->advCodingCap) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_LDPC_CODING; + if (roam_ht_cap->supportedChannelWidthSet) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + temp_ht_cap = roam_ht_cap->mimoPowerSave & + (IEEE80211_HT_CAP_SM_PS >> IEEE80211_HT_CAP_SM_PS_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->cap_info |= + temp_ht_cap << IEEE80211_HT_CAP_SM_PS_SHIFT; + if (roam_ht_cap->greenField) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_GRN_FLD; + if (roam_ht_cap->shortGI20MHz) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_SGI_20; + if (roam_ht_cap->shortGI40MHz) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_SGI_40; + if (roam_ht_cap->txSTBC) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_TX_STBC; + temp_ht_cap = roam_ht_cap->rxSTBC & (IEEE80211_HT_CAP_RX_STBC >> + IEEE80211_HT_CAP_RX_STBC_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->cap_info |= + temp_ht_cap << IEEE80211_HT_CAP_RX_STBC_SHIFT; + if (roam_ht_cap->delayedBA) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_DELAY_BA; + if (roam_ht_cap->maximalAMSDUsize) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_MAX_AMSDU; + if (roam_ht_cap->dsssCckMode40MHz) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_DSSSCCK40; + if (roam_ht_cap->psmp) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_RESERVED; + if (roam_ht_cap->stbcControlFrame) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; + if (roam_ht_cap->lsigTXOPProtection) + hdd_ht_cap->cap_info |= IEEE80211_HT_CAP_LSIG_TXOP_PROT; + + /* 802.11n HT capability AMPDU settings (for ampdu_params_info) */ + if (roam_ht_cap->maxRxAMPDUFactor) + hdd_ht_cap->ampdu_params_info |= + IEEE80211_HT_AMPDU_PARM_FACTOR; + temp_ht_cap = roam_ht_cap->mpduDensity & + (IEEE80211_HT_AMPDU_PARM_DENSITY >> + IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->ampdu_params_info |= + temp_ht_cap << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; + + /* 802.11n HT extended capabilities masks */ + if (roam_ht_cap->pco) + hdd_ht_cap->extended_ht_cap_info |= + IEEE80211_HT_EXT_CAP_PCO; + temp_ht_cap = roam_ht_cap->transitionTime & + (IEEE80211_HT_EXT_CAP_PCO_TIME >> + IEEE80211_HT_EXT_CAP_PCO_TIME_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->extended_ht_cap_info |= + temp_ht_cap << IEEE80211_HT_EXT_CAP_PCO_TIME_SHIFT; + temp_ht_cap = roam_ht_cap->mcsFeedback & + (IEEE80211_HT_EXT_CAP_MCS_FB >> IEEE80211_HT_EXT_CAP_MCS_FB_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->extended_ht_cap_info |= + temp_ht_cap << IEEE80211_HT_EXT_CAP_MCS_FB_SHIFT; + + /* tx_bf_cap_info capabilities */ + if (roam_ht_cap->txBF) + hdd_ht_cap->tx_BF_cap_info |= TX_BF_CAP_INFO_TX_BF; + if (roam_ht_cap->rxStaggeredSounding) + hdd_ht_cap->tx_BF_cap_info |= + TX_BF_CAP_INFO_RX_STAG_RED_SOUNDING; + if (roam_ht_cap->txStaggeredSounding) + hdd_ht_cap->tx_BF_cap_info |= + TX_BF_CAP_INFO_TX_STAG_RED_SOUNDING; + if (roam_ht_cap->rxZLF) + hdd_ht_cap->tx_BF_cap_info |= TX_BF_CAP_INFO_RX_ZFL; + if (roam_ht_cap->txZLF) + hdd_ht_cap->tx_BF_cap_info |= TX_BF_CAP_INFO_TX_ZFL; + if (roam_ht_cap->implicitTxBF) + hdd_ht_cap->tx_BF_cap_info |= TX_BF_CAP_INFO_IMP_TX_BF; + temp_ht_cap = roam_ht_cap->calibration & + (TX_BF_CAP_INFO_CALIBRATION >> TX_BF_CAP_INFO_CALIBRATION_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->tx_BF_cap_info |= + temp_ht_cap << TX_BF_CAP_INFO_CALIBRATION_SHIFT; + if (roam_ht_cap->explicitCSITxBF) + hdd_ht_cap->tx_BF_cap_info |= TX_BF_CAP_INFO_EXP_CSIT_BF; + if (roam_ht_cap->explicitUncompressedSteeringMatrix) + hdd_ht_cap->tx_BF_cap_info |= + TX_BF_CAP_INFO_EXP_UNCOMP_STEER_MAT; + temp_ht_cap = roam_ht_cap->explicitBFCSIFeedback & + (TX_BF_CAP_INFO_EXP_BF_CSI_FB >> + TX_BF_CAP_INFO_EXP_BF_CSI_FB_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->tx_BF_cap_info |= + temp_ht_cap << TX_BF_CAP_INFO_EXP_BF_CSI_FB_SHIFT; + temp_ht_cap = + roam_ht_cap->explicitUncompressedSteeringMatrixFeedback & + (TX_BF_CAP_INFO_EXP_UNCMP_STEER_MAT >> + TX_BF_CAP_INFO_EXP_UNCMP_STEER_MAT_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->tx_BF_cap_info |= + temp_ht_cap << + TX_BF_CAP_INFO_EXP_UNCMP_STEER_MAT_SHIFT; + temp_ht_cap = + roam_ht_cap->explicitCompressedSteeringMatrixFeedback & + (TX_BF_CAP_INFO_EXP_CMP_STEER_MAT_FB >> + TX_BF_CAP_INFO_EXP_CMP_STEER_MAT_FB_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->tx_BF_cap_info |= + temp_ht_cap << + TX_BF_CAP_INFO_EXP_CMP_STEER_MAT_FB_SHIFT; + temp_ht_cap = roam_ht_cap->csiNumBFAntennae & + (TX_BF_CAP_INFO_CSI_NUM_BF_ANT >> + TX_BF_CAP_INFO_CSI_NUM_BF_ANT_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->tx_BF_cap_info |= + temp_ht_cap << TX_BF_CAP_INFO_CSI_NUM_BF_ANT_SHIFT; + temp_ht_cap = roam_ht_cap->uncompressedSteeringMatrixBFAntennae & + (TX_BF_CAP_INFO_UNCOMP_STEER_MAT_BF_ANT >> + TX_BF_CAP_INFO_UNCOMP_STEER_MAT_BF_ANT_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->tx_BF_cap_info |= + temp_ht_cap << + TX_BF_CAP_INFO_UNCOMP_STEER_MAT_BF_ANT_SHIFT; + temp_ht_cap = roam_ht_cap->compressedSteeringMatrixBFAntennae & + (TX_BF_CAP_INFO_COMP_STEER_MAT_BF_ANT >> + TX_BF_CAP_INFO_COMP_STEER_MAT_BF_ANT_SHIFT); + if (temp_ht_cap) + hdd_ht_cap->tx_BF_cap_info |= + temp_ht_cap << + TX_BF_CAP_INFO_COMP_STEER_MAT_BF_ANT_SHIFT; + + /* antenna selection */ + if (roam_ht_cap->antennaSelection) + hdd_ht_cap->antenna_selection_info |= ANTENNA_SEL_INFO; + if (roam_ht_cap->explicitCSIFeedbackTx) + hdd_ht_cap->antenna_selection_info |= + ANTENNA_SEL_INFO_EXP_CSI_FB_TX; + if (roam_ht_cap->antennaIndicesFeedbackTx) + hdd_ht_cap->antenna_selection_info |= + ANTENNA_SEL_INFO_ANT_ID_FB_TX; + if (roam_ht_cap->explicitCSIFeedback) + hdd_ht_cap->antenna_selection_info |= + ANTENNA_SEL_INFO_EXP_CSI_FB; + if (roam_ht_cap->antennaIndicesFeedback) + hdd_ht_cap->antenna_selection_info |= + ANTENNA_SEL_INFO_ANT_ID_FB; + if (roam_ht_cap->rxAS) + hdd_ht_cap->antenna_selection_info |= + ANTENNA_SEL_INFO_RX_AS; + if (roam_ht_cap->txSoundingPPDUs) + hdd_ht_cap->antenna_selection_info |= + ANTENNA_SEL_INFO_TX_SOUNDING_PPDU; + + /* mcs data rate */ + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; ++i) + hdd_ht_cap->mcs.rx_mask[i] = + roam_ht_cap->supportedMCSSet[i]; + hdd_ht_cap->mcs.rx_highest = + ((short) (roam_ht_cap->supportedMCSSet[11]) << 8) | + ((short) (roam_ht_cap->supportedMCSSet[10])); + hdd_ht_cap->mcs.tx_params = + roam_ht_cap->supportedMCSSet[12]; +} + +#define VHT_CAP_MAX_MPDU_LENGTH_MASK 0x00000003 +#define VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT 2 +#define VHT_CAP_RXSTBC_MASK_SHIFT 8 +#define VHT_CAP_BEAMFORMEE_STS_SHIFT 13 +#define VHT_CAP_BEAMFORMEE_STS_MASK \ + (0x0000e000 >> VHT_CAP_BEAMFORMEE_STS_SHIFT) +#define VHT_CAP_SOUNDING_DIMENSIONS_SHIFT 16 +#define VHT_CAP_SOUNDING_DIMENSIONS_MASK \ + (0x00070000 >> VHT_CAP_SOUNDING_DIMENSIONS_SHIFT) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK_SHIFT 23 +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK \ + (0x03800000 >> VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK_SHIFT) +#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB_SHIFT 26 + +void hdd_copy_vht_caps(struct ieee80211_vht_cap *hdd_vht_cap, + tDot11fIEVHTCaps *roam_vht_cap) +{ + uint32_t temp_vht_cap; + + qdf_mem_zero(hdd_vht_cap, sizeof(struct ieee80211_vht_cap)); + + temp_vht_cap = roam_vht_cap->maxMPDULen & VHT_CAP_MAX_MPDU_LENGTH_MASK; + hdd_vht_cap->vht_cap_info |= temp_vht_cap; + temp_vht_cap = roam_vht_cap->supportedChannelWidthSet & + (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK >> + VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT); + if (temp_vht_cap) { + if (roam_vht_cap->supportedChannelWidthSet & + (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ >> + VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT)) + hdd_vht_cap->vht_cap_info |= + temp_vht_cap << + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + if (roam_vht_cap->supportedChannelWidthSet & + (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ >> + VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT)) + hdd_vht_cap->vht_cap_info |= + temp_vht_cap << + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + } + if (roam_vht_cap->ldpcCodingCap) + hdd_vht_cap->vht_cap_info |= IEEE80211_VHT_CAP_RXLDPC; + if (roam_vht_cap->shortGI80MHz) + hdd_vht_cap->vht_cap_info |= IEEE80211_VHT_CAP_SHORT_GI_80; + if (roam_vht_cap->shortGI160and80plus80MHz) + hdd_vht_cap->vht_cap_info |= IEEE80211_VHT_CAP_SHORT_GI_160; + if (roam_vht_cap->txSTBC) + hdd_vht_cap->vht_cap_info |= IEEE80211_VHT_CAP_TXSTBC; + temp_vht_cap = roam_vht_cap->rxSTBC & (IEEE80211_VHT_CAP_RXSTBC_MASK >> + VHT_CAP_RXSTBC_MASK_SHIFT); + if (temp_vht_cap) + hdd_vht_cap->vht_cap_info |= + temp_vht_cap << VHT_CAP_RXSTBC_MASK_SHIFT; + if (roam_vht_cap->suBeamFormerCap) + hdd_vht_cap->vht_cap_info |= + IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; + if (roam_vht_cap->suBeamformeeCap) + hdd_vht_cap->vht_cap_info |= + IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + temp_vht_cap = roam_vht_cap->csnofBeamformerAntSup & + (VHT_CAP_BEAMFORMEE_STS_MASK); + if (temp_vht_cap) + hdd_vht_cap->vht_cap_info |= + temp_vht_cap << VHT_CAP_BEAMFORMEE_STS_SHIFT; + temp_vht_cap = roam_vht_cap->numSoundingDim & + (VHT_CAP_SOUNDING_DIMENSIONS_MASK); + if (temp_vht_cap) + hdd_vht_cap->vht_cap_info |= + temp_vht_cap << VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + if (roam_vht_cap->muBeamformerCap) + hdd_vht_cap->vht_cap_info |= + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; + if (roam_vht_cap->muBeamformeeCap) + hdd_vht_cap->vht_cap_info |= + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + if (roam_vht_cap->vhtTXOPPS) + hdd_vht_cap->vht_cap_info |= + IEEE80211_VHT_CAP_VHT_TXOP_PS; + if (roam_vht_cap->htcVHTCap) + hdd_vht_cap->vht_cap_info |= + IEEE80211_VHT_CAP_HTC_VHT; + temp_vht_cap = roam_vht_cap->maxAMPDULenExp & + (VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK); + if (temp_vht_cap) + hdd_vht_cap->vht_cap_info |= + temp_vht_cap << + VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK_SHIFT; + temp_vht_cap = roam_vht_cap->vhtLinkAdaptCap & + (IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB >> + VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB_SHIFT); + if (temp_vht_cap) + hdd_vht_cap->vht_cap_info |= temp_vht_cap << + VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB_SHIFT; + if (roam_vht_cap->rxAntPattern) + hdd_vht_cap->vht_cap_info |= + IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; + if (roam_vht_cap->txAntPattern) + hdd_vht_cap->vht_cap_info |= + IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; + hdd_vht_cap->supp_mcs.rx_mcs_map = roam_vht_cap->rxMCSMap; + hdd_vht_cap->supp_mcs.rx_highest = + ((uint16_t)roam_vht_cap->rxHighSupDataRate); + hdd_vht_cap->supp_mcs.tx_mcs_map = roam_vht_cap->txMCSMap; + hdd_vht_cap->supp_mcs.tx_highest = + ((uint16_t)roam_vht_cap->txSupDataRate); +} + +/* ht param */ +#define HT_PARAM_CONTROLLED_ACCESS_ONLY 0x10 +#define HT_PARAM_SERVICE_INT_GRAN 0xe0 +#define HT_PARAM_SERVICE_INT_GRAN_SHIFT 5 + +/* operatinon mode */ +#define HT_OP_MODE_TX_BURST_LIMIT 0x0008 + +/* stbc_param */ +#define HT_STBC_PARAM_MCS 0x007f + +/** + * hdd_copy_ht_operation()- copy HT operation element from roam info to + * hdd station context. + * @hdd_sta_ctx: pointer to hdd station context + * @roam_info: pointer to roam info + * + * Return: None + */ +static void hdd_copy_ht_operation(struct hdd_station_ctx *hdd_sta_ctx, + struct csr_roam_info *roam_info) +{ + tDot11fIEHTInfo *roam_ht_ops = &roam_info->ht_operation; + struct ieee80211_ht_operation *hdd_ht_ops = + &hdd_sta_ctx->conn_info.ht_operation; + uint32_t i, temp_ht_ops; + + qdf_mem_zero(hdd_ht_ops, sizeof(struct ieee80211_ht_operation)); + + hdd_ht_ops->primary_chan = roam_ht_ops->primaryChannel; + + /* HT_PARAMS */ + temp_ht_ops = roam_ht_ops->secondaryChannelOffset & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET; + if (temp_ht_ops) + hdd_ht_ops->ht_param |= temp_ht_ops; + else + hdd_ht_ops->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; + if (roam_ht_ops->recommendedTxWidthSet) + hdd_ht_ops->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; + if (roam_ht_ops->rifsMode) + hdd_ht_ops->ht_param |= IEEE80211_HT_PARAM_RIFS_MODE; + if (roam_ht_ops->controlledAccessOnly) + hdd_ht_ops->ht_param |= HT_PARAM_CONTROLLED_ACCESS_ONLY; + temp_ht_ops = roam_ht_ops->serviceIntervalGranularity & + (HT_PARAM_SERVICE_INT_GRAN >> HT_PARAM_SERVICE_INT_GRAN_SHIFT); + if (temp_ht_ops) + hdd_ht_ops->ht_param |= temp_ht_ops << + HT_PARAM_SERVICE_INT_GRAN_SHIFT; + + /* operation mode */ + temp_ht_ops = roam_ht_ops->opMode & + IEEE80211_HT_OP_MODE_PROTECTION; + switch (temp_ht_ops) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + hdd_ht_ops->operation_mode |= + IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER; + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + hdd_ht_ops->operation_mode |= + IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + hdd_ht_ops->operation_mode |= + IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONE: + default: + hdd_ht_ops->operation_mode |= + IEEE80211_HT_OP_MODE_PROTECTION_NONE; + } + if (roam_ht_ops->nonGFDevicesPresent) + hdd_ht_ops->operation_mode |= + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT; + if (roam_ht_ops->transmitBurstLimit) + hdd_ht_ops->operation_mode |= + HT_OP_MODE_TX_BURST_LIMIT; + if (roam_ht_ops->obssNonHTStaPresent) + hdd_ht_ops->operation_mode |= + IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; + + /* stbc_param */ + temp_ht_ops = roam_ht_ops->basicSTBCMCS & + HT_STBC_PARAM_MCS; + if (temp_ht_ops) + hdd_ht_ops->stbc_param |= temp_ht_ops; + if (roam_ht_ops->dualCTSProtection) + hdd_ht_ops->stbc_param |= + IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT; + if (roam_ht_ops->secondaryBeacon) + hdd_ht_ops->stbc_param |= + IEEE80211_HT_STBC_PARAM_STBC_BEACON; + if (roam_ht_ops->lsigTXOPProtectionFullSupport) + hdd_ht_ops->stbc_param |= + IEEE80211_HT_STBC_PARAM_LSIG_TXOP_FULLPROT; + if (roam_ht_ops->pcoActive) + hdd_ht_ops->stbc_param |= + IEEE80211_HT_STBC_PARAM_PCO_ACTIVE; + if (roam_ht_ops->pcoPhase) + hdd_ht_ops->stbc_param |= + IEEE80211_HT_STBC_PARAM_PCO_PHASE; + + /* basic MCs set */ + for (i = 0; i < 16; ++i) + hdd_ht_ops->basic_set[i] = + roam_ht_ops->basicMCSSet[i]; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +static void hdd_copy_vht_center_freq(struct ieee80211_vht_operation *ieee_ops, + tDot11fIEVHTOperation *roam_ops) +{ + ieee_ops->center_freq_seg0_idx = roam_ops->chan_center_freq_seg0; + ieee_ops->center_freq_seg1_idx = roam_ops->chan_center_freq_seg1; +} +#else +static void hdd_copy_vht_center_freq(struct ieee80211_vht_operation *ieee_ops, + tDot11fIEVHTOperation *roam_ops) +{ + ieee_ops->center_freq_seg1_idx = roam_ops->chan_center_freq_seg0; + ieee_ops->center_freq_seg2_idx = roam_ops->chan_center_freq_seg1; +} +#endif /* KERNEL_VERSION(4, 12, 0) */ + +/** + * hdd_copy_vht_operation()- copy VHT operations element from roam info to + * hdd station context. + * @hdd_sta_ctx: pointer to hdd station context + * @roam_info: pointer to roam info + * + * Return: None + */ +static void hdd_copy_vht_operation(struct hdd_station_ctx *hdd_sta_ctx, + struct csr_roam_info *roam_info) +{ + tDot11fIEVHTOperation *roam_vht_ops = &roam_info->vht_operation; + struct ieee80211_vht_operation *hdd_vht_ops = + &hdd_sta_ctx->conn_info.vht_operation; + + qdf_mem_zero(hdd_vht_ops, sizeof(struct ieee80211_vht_operation)); + + hdd_vht_ops->chan_width = roam_vht_ops->chanWidth; + hdd_copy_vht_center_freq(hdd_vht_ops, roam_vht_ops); + hdd_vht_ops->basic_mcs_set = roam_vht_ops->basicMCSSet; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) && \ + defined(WLAN_FEATURE_11AX) +static void hdd_copy_he_operation(struct hdd_station_ctx *hdd_sta_ctx, + struct csr_roam_info *roam_info) +{ + tDot11fIEhe_op *roam_he_operation = &roam_info->he_operation; + struct ieee80211_he_operation *hdd_he_operation; + uint32_t he_oper_params = 0; + uint32_t len = 0, filled = 0; + uint8_t he_oper_6g_params = 0; + uint32_t he_oper_len; + + if (!roam_he_operation->present) + return; + if (roam_he_operation->vht_oper_present) + len += 3; + if (roam_he_operation->co_located_bss) + len += 1; + if (roam_he_operation->oper_info_6g_present) + len += 5; + + he_oper_len = sizeof(struct ieee80211_he_operation) + len; + + hdd_he_operation = qdf_mem_malloc(he_oper_len); + if (!hdd_he_operation) + return; + + /* Fill he_oper_params */ + he_oper_params |= roam_he_operation->default_pe << + HE_OPERATION_DFLT_PE_DURATION_POS; + he_oper_params |= roam_he_operation->twt_required << + HE_OPERATION_TWT_REQUIRED_POS; + he_oper_params |= roam_he_operation->txop_rts_threshold << + HE_OPERATION_RTS_THRESHOLD_POS; + he_oper_params |= roam_he_operation->vht_oper_present << + HE_OPERATION_VHT_OPER_POS; + he_oper_params |= roam_he_operation->co_located_bss << + HE_OPERATION_CO_LOCATED_BSS_POS; + he_oper_params |= roam_he_operation->er_su_disable << + HE_OPERATION_ER_SU_DISABLE_POS; + he_oper_params |= roam_he_operation->oper_info_6g_present << + HE_OPERATION_OPER_INFO_6G_POS; + he_oper_params |= roam_he_operation->reserved2 << + HE_OPERATION_RESERVED_POS; + he_oper_params |= roam_he_operation->bss_color << + HE_OPERATION_BSS_COLOR_POS; + he_oper_params |= roam_he_operation->partial_bss_col << + HE_OPERATION_PARTIAL_BSS_COLOR_POS; + he_oper_params |= roam_he_operation->bss_col_disabled << + HE_OPERATION_BSS_COL_DISABLED_POS; + + hdd_he_operation->he_oper_params = he_oper_params; + + /* Fill he_mcs_nss set */ + qdf_mem_copy(&hdd_he_operation->he_mcs_nss_set, + roam_he_operation->basic_mcs_nss, + sizeof(hdd_he_operation->he_mcs_nss_set)); + + /* Fill he_params_optional fields */ + + if (roam_he_operation->vht_oper_present) { + hdd_he_operation->optional[filled++] = + roam_he_operation->vht_oper.info.chan_width; + hdd_he_operation->optional[filled++] = + roam_he_operation->vht_oper.info.center_freq_seg0; + hdd_he_operation->optional[filled++] = + roam_he_operation->vht_oper.info.center_freq_seg1; + } + if (roam_he_operation->co_located_bss) + hdd_he_operation->optional[filled++] = + roam_he_operation->maxbssid_ind.info.data; + + if (roam_he_operation->oper_info_6g_present) { + hdd_he_operation->optional[filled++] = + roam_he_operation->oper_info_6g.info.primary_ch; + he_oper_6g_params |= + roam_he_operation->oper_info_6g.info.ch_width << 0; + he_oper_6g_params |= + roam_he_operation->oper_info_6g.info.dup_bcon << 2; + he_oper_6g_params |= + roam_he_operation->oper_info_6g.info.reserved << 3; + + hdd_he_operation->optional[filled++] = he_oper_6g_params; + hdd_he_operation->optional[filled++] = + roam_he_operation->oper_info_6g.info.center_freq_seg0; + hdd_he_operation->optional[filled++] = + roam_he_operation->oper_info_6g.info.center_freq_seg1; + hdd_he_operation->optional[filled] = + roam_he_operation->oper_info_6g.info.min_rate; + } + + if (hdd_sta_ctx->cache_conn_info.he_operation) { + qdf_mem_free(hdd_sta_ctx->cache_conn_info.he_operation); + hdd_sta_ctx->cache_conn_info.he_operation = NULL; + } + + hdd_sta_ctx->cache_conn_info.he_oper_len = he_oper_len; + + hdd_sta_ctx->cache_conn_info.he_operation = hdd_he_operation; +} +#else +static inline void hdd_copy_he_operation(struct hdd_station_ctx *hdd_sta_ctx, + struct csr_roam_info *roam_info) +{ +} +#endif + +/** + * hdd_save_bss_info() - save connection info in hdd sta ctx + * @adapter: Pointer to adapter + * @roam_info: pointer to roam info + * + * Return: None + */ +static void hdd_save_bss_info(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (roam_info->vht_caps.present) { + hdd_sta_ctx->conn_info.conn_flag.vht_present = true; + hdd_copy_vht_caps(&hdd_sta_ctx->conn_info.vht_caps, + &roam_info->vht_caps); + } else { + hdd_sta_ctx->conn_info.conn_flag.vht_present = false; + } + if (roam_info->ht_caps.present) { + hdd_sta_ctx->conn_info.conn_flag.ht_present = true; + hdd_copy_ht_caps(&hdd_sta_ctx->conn_info.ht_caps, + &roam_info->ht_caps); + } else { + hdd_sta_ctx->conn_info.conn_flag.ht_present = false; + } + if (roam_info->reassoc || + hdd_is_roam_sync_in_progress(roam_info)) + hdd_sta_ctx->conn_info.roam_count++; + if (roam_info->hs20vendor_ie.present) { + hdd_sta_ctx->conn_info.conn_flag.hs20_present = true; + qdf_mem_copy(&hdd_sta_ctx->conn_info.hs20vendor_ie, + &roam_info->hs20vendor_ie, + sizeof(roam_info->hs20vendor_ie)); + } else { + hdd_sta_ctx->conn_info.conn_flag.hs20_present = false; + } + if (roam_info->ht_operation.present) { + hdd_sta_ctx->conn_info.conn_flag.ht_op_present = true; + hdd_copy_ht_operation(hdd_sta_ctx, roam_info); + } else { + hdd_sta_ctx->conn_info.conn_flag.ht_op_present = false; + } + if (roam_info->vht_operation.present) { + hdd_sta_ctx->conn_info.conn_flag.vht_op_present = true; + hdd_copy_vht_operation(hdd_sta_ctx, roam_info); + } else { + hdd_sta_ctx->conn_info.conn_flag.vht_op_present = false; + } + + /* Cleanup already existing he info */ + hdd_cleanup_conn_info(adapter); + + /* Cache last connection info */ + qdf_mem_copy(&hdd_sta_ctx->cache_conn_info, &hdd_sta_ctx->conn_info, + sizeof(hdd_sta_ctx->cache_conn_info)); + + hdd_copy_he_operation(hdd_sta_ctx, roam_info); +} + +/** + * hdd_conn_save_connect_info() - save current connection information + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @bss_type: bss type + * + * Return: none + */ +static void +hdd_conn_save_connect_info(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + eCsrRoamBssType bss_type) +{ + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + eCsrEncryptionType encrypt_type = eCSR_ENCRYPT_TYPE_NONE; + + QDF_ASSERT(roam_info); + + if (roam_info) { + /* Save the BSSID for the connection */ + if (eCSR_BSS_TYPE_INFRASTRUCTURE == bss_type) { + QDF_ASSERT(roam_info->bss_desc); + qdf_copy_macaddr(&sta_ctx->conn_info.bssid, + &roam_info->bssid); + + } else if (eCSR_BSS_TYPE_IBSS == bss_type) { + qdf_copy_macaddr(&sta_ctx->conn_info.bssid, + &roam_info->bssid); + } else { + /* + * can't happen. We need a valid IBSS or Infra setting + * in the BSSDescription or we can't function. + */ + QDF_ASSERT(0); + } + + /* notify WMM */ + hdd_wmm_connect(adapter, roam_info, bss_type); + + if (!roam_info->u.pConnectedProfile) { + QDF_ASSERT(roam_info->u.pConnectedProfile); + } else { + /* Get Multicast Encryption Type */ + encrypt_type = + roam_info->u.pConnectedProfile->mcEncryptionType; + sta_ctx->conn_info.mc_encrypt_type = encrypt_type; + /* Get Unicast Encryption Type */ + encrypt_type = + roam_info->u.pConnectedProfile->EncryptionType; + sta_ctx->conn_info.uc_encrypt_type = encrypt_type; + + sta_ctx->conn_info.auth_type = + roam_info->u.pConnectedProfile->AuthType; + sta_ctx->conn_info.last_auth_type = + sta_ctx->conn_info.auth_type; + + sta_ctx->conn_info.chan_freq = + roam_info->u.pConnectedProfile->op_freq; + + /* Save the ssid for the connection */ + qdf_mem_copy(&sta_ctx->conn_info.ssid.SSID, + &roam_info->u.pConnectedProfile->SSID, + sizeof(tSirMacSSid)); + qdf_mem_copy(&sta_ctx->conn_info.last_ssid.SSID, + &roam_info->u.pConnectedProfile->SSID, + sizeof(tSirMacSSid)); + + /* Save dot11mode in which STA associated to AP */ + sta_ctx->conn_info.dot11mode = + roam_info->u.pConnectedProfile->dot11Mode; + + sta_ctx->conn_info.proxy_arp_service = + roam_info->u.pConnectedProfile->proxy_arp_service; + + sta_ctx->conn_info.nss = roam_info->chan_info.nss; + + sta_ctx->conn_info.rate_flags = + roam_info->chan_info.rate_flags; + + sta_ctx->conn_info.ch_width = + roam_info->chan_info.ch_width; + } + hdd_save_bss_info(adapter, roam_info); + } +} + +/** + * hdd_send_ft_assoc_response() - send fast transition assoc response + * @dev: pointer to net device + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * + * Send the 11R key information to the supplicant. Only then can the supplicant + * generate the PMK-R1. (BTW, the ESE supplicant also needs the Assoc Resp IEs + * for the same purpose.) + * + * Mainly the Assoc Rsp IEs are passed here. For the IMDA this contains the + * R1KHID, R0KHID and the MDID. For FT, this consists of the Reassoc Rsp FTIEs. + * This is the Assoc Response. + * + * Return: none + */ +static void +hdd_send_ft_assoc_response(struct net_device *dev, + struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + union iwreq_data wrqu; + char *buff; + unsigned int len = 0; + u8 *assoc_rsp = NULL; + + if (roam_info->nAssocRspLength < FT_ASSOC_RSP_IES_OFFSET) { + hdd_debug("Invalid assoc rsp length %d", + roam_info->nAssocRspLength); + return; + } + + assoc_rsp = + (u8 *) (roam_info->pbFrames + roam_info->nBeaconLength + + roam_info->nAssocReqLength); + if (!assoc_rsp) { + hdd_debug("AssocReq or AssocRsp is NULL"); + return; + } + /* assoc_rsp needs to point to the IEs */ + assoc_rsp += FT_ASSOC_RSP_IES_OFFSET; + + /* Send the Assoc Resp, the supplicant needs this for initial Auth. */ + len = roam_info->nAssocRspLength - FT_ASSOC_RSP_IES_OFFSET; + if (len > IW_GENERIC_IE_MAX) { + hdd_err("Invalid Assoc resp length %d", len); + return; + } + wrqu.data.length = len; + + /* We need to send the IEs to the supplicant. */ + buff = qdf_mem_malloc(IW_GENERIC_IE_MAX); + if (!buff) + return; + + memcpy(buff, assoc_rsp, len); + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, buff); + + qdf_mem_free(buff); +} + +/** + * hdd_send_ft_event() - send fast transition event + * @adapter: pointer to adapter + * + * Send the FTIEs, RIC IEs during FT. This is eventually used to send the + * FT events to the supplicant. At the reception of Auth2 we send the RIC + * followed by the auth response IEs to the supplicant. + * Once both are received in the supplicant, an FT event is generated + * to the supplicant. + * + * Return: none + */ +static void hdd_send_ft_event(struct hdd_adapter *adapter) +{ + uint16_t auth_resp_len = 0; + uint32_t ric_ies_length = 0; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle; + +#if defined(KERNEL_SUPPORT_11R_CFG80211) + struct cfg80211_ft_event_params ftEvent; + uint8_t ftIe[DOT11F_IE_FTINFO_MAX_LEN]; + uint8_t ricIe[DOT11F_IE_RICDESCRIPTOR_MAX_LEN]; + struct net_device *dev = adapter->dev; +#else + char *buff; + union iwreq_data wrqu; + uint16_t str_len; +#endif + + mac_handle = hdd_ctx->mac_handle; +#if defined(KERNEL_SUPPORT_11R_CFG80211) + qdf_mem_zero(ftIe, DOT11F_IE_FTINFO_MAX_LEN); + qdf_mem_zero(ricIe, DOT11F_IE_RICDESCRIPTOR_MAX_LEN); + + sme_get_rici_es(mac_handle, adapter->vdev_id, (u8 *) ricIe, + DOT11F_IE_RICDESCRIPTOR_MAX_LEN, &ric_ies_length); + if (ric_ies_length == 0) + hdd_warn("Do not send RIC IEs as length is 0"); + + ftEvent.ric_ies = ricIe; + ftEvent.ric_ies_len = ric_ies_length; + hdd_debug("RIC IEs is of length %d", (int)ric_ies_length); + + sme_get_ft_pre_auth_response(mac_handle, adapter->vdev_id, + (u8 *) ftIe, DOT11F_IE_FTINFO_MAX_LEN, + &auth_resp_len); + + if (auth_resp_len == 0) { + hdd_debug("AuthRsp FTIES is of length 0"); + return; + } + + sme_set_ft_pre_auth_state(mac_handle, adapter->vdev_id, true); + + ftEvent.target_ap = ftIe; + + ftEvent.ies = (u8 *) (ftIe + QDF_MAC_ADDR_SIZE); + ftEvent.ies_len = auth_resp_len - QDF_MAC_ADDR_SIZE; + + hdd_debug("ftEvent.ies_len %zu", ftEvent.ies_len); + hdd_debug("ftEvent.ric_ies_len %zu", ftEvent.ric_ies_len); + hdd_debug("ftEvent.target_ap %2x-%2x-%2x-%2x-%2x-%2x", + ftEvent.target_ap[0], ftEvent.target_ap[1], + ftEvent.target_ap[2], ftEvent.target_ap[3], ftEvent.target_ap[4], + ftEvent.target_ap[5]); + + (void)cfg80211_ft_event(dev, &ftEvent); + +#else + /* We need to send the IEs to the supplicant */ + buff = qdf_mem_malloc(IW_CUSTOM_MAX); + if (!buff) + return; + + /* Sme needs to send the RIC IEs first */ + str_len = strlcpy(buff, "RIC=", IW_CUSTOM_MAX); + sme_get_rici_es(mac_handle, adapter->vdev_id, + (u8 *) &(buff[str_len]), (IW_CUSTOM_MAX - str_len), + &ric_ies_length); + if (ric_ies_length == 0) { + hdd_warn("Do not send RIC IEs as length is 0"); + } else { + wrqu.data.length = str_len + ric_ies_length; + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, buff); + } + + /* Sme needs to provide the Auth Resp */ + qdf_mem_zero(buff, IW_CUSTOM_MAX); + str_len = strlcpy(buff, "AUTH=", IW_CUSTOM_MAX); + sme_get_ft_pre_auth_response(mac_handle, adapter->vdev_id, + (u8 *) &buff[str_len], + (IW_CUSTOM_MAX - str_len), &auth_resp_len); + + if (auth_resp_len == 0) { + qdf_mem_free(buff); + hdd_debug("AuthRsp FTIES is of length 0"); + return; + } + + wrqu.data.length = str_len + auth_resp_len; + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, buff); + + qdf_mem_free(buff); +#endif +} + +#ifdef FEATURE_WLAN_ESE +/** + * hdd_send_new_ap_channel_info() - send new ap channel info + * @dev: pointer to net device + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * + * Send the ESE required "new AP Channel info" to the supplicant. + * (This keeps the supplicant "up to date" on the current channel.) + * + * The current (new AP) channel information is passed in. + * + * Return: none + */ +static void +hdd_send_new_ap_channel_info(struct net_device *dev, + struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + union iwreq_data wrqu; + struct bss_description *descriptor = roam_info->bss_desc; + mac_handle_t mac_hdl; + struct wlan_objmgr_pdev *pdev; + + if (!descriptor) { + hdd_err("bss descriptor is null"); + return; + } + /* + * Send the Channel event, the supplicant needs this to generate + * the Adjacent AP report. + */ + hdd_debug("Sending up an SIOCGIWFREQ, channel freq: %d", + descriptor->chan_freq); + memset(&wrqu, '\0', sizeof(wrqu)); + mac_hdl = hdd_adapter_get_mac_handle(adapter); + if (!mac_hdl) { + hdd_err("MAC handle invalid, falling back!"); + return; + } + pdev = MAC_CONTEXT(mac_hdl)->pdev; + if (!pdev) { + hdd_err("pdev invalid in MAC context, falling back!"); + return; + } + wrqu.freq.m = wlan_reg_freq_to_chan(pdev, descriptor->chan_freq); + wrqu.freq.e = 0; + wrqu.freq.i = 0; + wireless_send_event(adapter->dev, SIOCGIWFREQ, &wrqu, NULL); +} + +#endif /* FEATURE_WLAN_ESE */ + +/** + * hdd_send_update_beacon_ies_event() - send update beacons ie event + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * + * Return: none + */ +static void +hdd_send_update_beacon_ies_event(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + union iwreq_data wrqu; + u8 *beacon_ies; + u8 currentLen = 0; + char *buff; + int totalIeLen = 0, currentOffset = 0, strLen; + + memset(&wrqu, '\0', sizeof(wrqu)); + + if (0 == roam_info->nBeaconLength) { + hdd_debug("beacon frame length is 0"); + return; + } + beacon_ies = (u8 *) (roam_info->pbFrames + BEACON_FRAME_IES_OFFSET); + if (!beacon_ies) { + hdd_warn("Beacon IEs is NULL"); + return; + } + /* beacon_ies needs to point to the IEs */ + hdd_debug("Beacon IEs is now at %02x%02x", + (unsigned int)beacon_ies[0], + (unsigned int)beacon_ies[1]); + hdd_debug("Beacon IEs length = %d", + roam_info->nBeaconLength - BEACON_FRAME_IES_OFFSET); + + /* We need to send the IEs to the supplicant. */ + buff = qdf_mem_malloc(IW_CUSTOM_MAX); + if (!buff) + return; + + strLen = strlcpy(buff, "BEACONIEs=", IW_CUSTOM_MAX); + currentLen = strLen + 1; + + totalIeLen = roam_info->nBeaconLength - BEACON_FRAME_IES_OFFSET; + do { + /* + * If the beacon size exceeds max CUSTOM event size, break it + * into chunks of CUSTOM event max size and send it to + * supplicant. Changes are done in supplicant to handle this. + */ + qdf_mem_zero(&buff[strLen + 1], IW_CUSTOM_MAX - (strLen + 1)); + currentLen = + QDF_MIN(totalIeLen, IW_CUSTOM_MAX - (strLen + 1) - 1); + qdf_mem_copy(&buff[strLen + 1], beacon_ies + currentOffset, + currentLen); + currentOffset += currentLen; + totalIeLen -= currentLen; + wrqu.data.length = strLen + 1 + currentLen; + if (totalIeLen) + buff[strLen] = 1; /* more chunks pending */ + else + buff[strLen] = 0; /* last chunk */ + + hdd_debug("Beacon IEs length to supplicant = %d", + currentLen); + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, buff); + } while (totalIeLen > 0); + + qdf_mem_free(buff); +} + +/** + * hdd_send_association_event() - send association event + * @dev: pointer to net device + * @roam_info: pointer to roam info + * + * Return: none + */ +static void hdd_send_association_event(struct net_device *dev, + struct csr_roam_info *roam_info) +{ + int ret; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + union iwreq_data wrqu; + int we_event; + char *msg; + struct qdf_mac_addr peer_macaddr; + struct csr_roam_profile *roam_profile; + + roam_profile = hdd_roam_profile(adapter); + memset(&wrqu, '\0', sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + we_event = SIOCGIWAP; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (roam_info) + if (roam_info->roamSynchInProgress) { + /* Update tdls module about the disconnection event */ + hdd_notify_sta_disconnect(adapter->vdev_id, + true, false, + adapter->vdev); + } +#endif + if (eConnectionState_Associated == sta_ctx->conn_info.conn_state) { + struct oem_channel_info chan_info = {0}; + + if (!roam_info || !roam_info->bss_desc) { + hdd_warn("STA in associated state but roam_info is null"); + return; + } + + if (!hdd_is_roam_sync_in_progress(roam_info)) { + policy_mgr_incr_active_session(hdd_ctx->psoc, + adapter->device_mode, adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, + adapter->device_mode, true); + } + memcpy(wrqu.ap_addr.sa_data, roam_info->bss_desc->bssId, + sizeof(roam_info->bss_desc->bssId)); + + ucfg_p2p_status_connect(adapter->vdev); + + hdd_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT + " connected to " + QDF_MAC_ADDR_FMT, dev->name, adapter->vdev_id, + QDF_MAC_ADDR_REF(adapter->mac_addr.bytes), + QDF_MAC_ADDR_REF(wrqu.ap_addr.sa_data)); + + hdd_send_update_beacon_ies_event(adapter, roam_info); + + /* + * Send IWEVASSOCRESPIE Event if WLAN_FEATURE_CIQ_METRICS + * is Enabled Or Send IWEVASSOCRESPIE Event if + * fFTEnable is true. + * Send FT Keys to the supplicant when FT is enabled + */ + if ((roam_profile->AuthType.authType[0] == + eCSR_AUTH_TYPE_FT_RSN_PSK) + || (roam_profile->AuthType.authType[0] == + eCSR_AUTH_TYPE_FT_RSN) + || (roam_profile->AuthType.authType[0] == + eCSR_AUTH_TYPE_FT_SAE) + || (roam_profile->AuthType.authType[0] == + eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384) +#ifdef FEATURE_WLAN_ESE + || (roam_profile->AuthType.authType[0] == + eCSR_AUTH_TYPE_CCKM_RSN) + || (roam_profile->AuthType.authType[0] == + eCSR_AUTH_TYPE_CCKM_WPA) +#endif + ) { + hdd_send_ft_assoc_response(dev, adapter, roam_info); + } + qdf_copy_macaddr(&peer_macaddr, + &sta_ctx->conn_info.bssid); + chan_info.mhz = roam_info->chan_info.mhz; + chan_info.info = roam_info->chan_info.info; + chan_info.band_center_freq1 = + roam_info->chan_info.band_center_freq1; + chan_info.band_center_freq2 = + roam_info->chan_info.band_center_freq2; + chan_info.reg_info_1 = + roam_info->chan_info.reg_info_1; + chan_info.reg_info_2 = + roam_info->chan_info.reg_info_2; + + ret = hdd_objmgr_set_peer_mlme_state(adapter->vdev, + WLAN_ASSOC_STATE); + if (ret) + hdd_err("Peer object "QDF_MAC_ADDR_FMT" fail to set associated state", + QDF_MAC_ADDR_REF(peer_macaddr.bytes)); + + /* send peer status indication to oem app */ + hdd_send_peer_status_ind_to_app(&peer_macaddr, + ePeerConnected, + roam_info->timingMeasCap, + adapter->vdev_id, &chan_info, + adapter->device_mode); + +#ifdef FEATURE_WLAN_TDLS + /* Update tdls module about connection event */ + hdd_notify_sta_connect(adapter->vdev_id, + roam_info->tdls_chan_swit_prohibited, + roam_info->tdls_prohibited, + adapter->vdev); +#endif + + hdd_add_latency_critical_client( + hdd_ctx, + hdd_convert_cfgdot11mode_to_80211mode( + sta_ctx->conn_info.dot11mode)); + /* start timer in sta/p2p_cli */ + hdd_bus_bw_compute_prev_txrx_stats(adapter); + hdd_bus_bw_compute_timer_start(hdd_ctx); + } else if (eConnectionState_IbssConnected == /* IBss Associated */ + sta_ctx->conn_info.conn_state) { + policy_mgr_update_connection_info(hdd_ctx->psoc, + adapter->vdev_id); + memcpy(wrqu.ap_addr.sa_data, sta_ctx->conn_info.bssid.bytes, + ETH_ALEN); + hdd_debug("%s(vdevid-%d): new IBSS peer connection to BSSID " QDF_MAC_ADDR_FMT, + dev->name, adapter->vdev_id, + QDF_MAC_ADDR_REF(sta_ctx->conn_info.bssid.bytes)); + } else { /* Not Associated */ + hdd_nofl_info("%s(vdevid-%d): disconnected", dev->name, + adapter->vdev_id); + memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN); + policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, + adapter->device_mode, adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, + false); + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + wlan_hdd_auto_shutdown_enable(hdd_ctx, true); +#endif + + if ((adapter->device_mode == QDF_STA_MODE) || + (adapter->device_mode == QDF_P2P_CLIENT_MODE)) { + qdf_copy_macaddr(&peer_macaddr, + &sta_ctx->conn_info.bssid); + + /* send peer status indication to oem app */ + hdd_send_peer_status_ind_to_app(&peer_macaddr, + ePeerDisconnected, 0, + adapter->vdev_id, + NULL, + adapter->device_mode); + } + + hdd_lpass_notify_disconnect(adapter); + /* Update tdls module about the disconnection event */ + hdd_notify_sta_disconnect(adapter->vdev_id, + false, + false, + adapter->vdev); + + hdd_del_latency_critical_client( + hdd_ctx, + hdd_convert_cfgdot11mode_to_80211mode( + sta_ctx->conn_info.dot11mode)); + /* stop timer in sta/p2p_cli */ + hdd_bus_bw_compute_reset_prev_txrx_stats(adapter); + hdd_bus_bw_compute_timer_try_stop(hdd_ctx); + } + hdd_ipa_set_tx_flow_info(); + + msg = NULL; + /* During the WLAN uninitialization,supplicant is stopped before the + * driver so not sending the status of the connection to supplicant + */ + if (cds_is_load_or_unload_in_progress()) { + wireless_send_event(dev, we_event, &wrqu, msg); +#ifdef FEATURE_WLAN_ESE + if (eConnectionState_Associated == + sta_ctx->conn_info.conn_state) { + if ((roam_profile->AuthType.authType[0] == + eCSR_AUTH_TYPE_CCKM_RSN) || + (roam_profile->AuthType.authType[0] == + eCSR_AUTH_TYPE_CCKM_WPA)) + hdd_send_new_ap_channel_info(dev, adapter, + roam_info); + } +#endif + } +} + +/** + * hdd_conn_remove_connect_info() - remove connection info + * @sta_ctx: pointer to global HDD station context + * @roam_info: pointer to roam info + * + * Return: none + */ +static void hdd_conn_remove_connect_info(struct hdd_station_ctx *sta_ctx) +{ + /* Remove bssid and peer_macaddr */ + qdf_mem_zero(&sta_ctx->conn_info.bssid, QDF_MAC_ADDR_SIZE); + qdf_mem_zero(&sta_ctx->conn_info.peer_macaddr[0], + QDF_MAC_ADDR_SIZE); + + /* Clear all security settings */ + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_OPEN_SYSTEM; + sta_ctx->conn_info.mc_encrypt_type = eCSR_ENCRYPT_TYPE_NONE; + sta_ctx->conn_info.uc_encrypt_type = eCSR_ENCRYPT_TYPE_NONE; + + qdf_mem_zero(&sta_ctx->ibss_enc_key, sizeof(tCsrRoamSetKey)); + + sta_ctx->conn_info.proxy_arp_service = 0; + + qdf_mem_zero(&sta_ctx->conn_info.ssid, sizeof(tCsrSSIDInfo)); + + /* + * Reset the ptk, gtk status flags to avoid using current connection + * status in further connections. + */ + sta_ctx->conn_info.gtk_installed = false; + sta_ctx->conn_info.ptk_installed = false; +} + +/** + * hdd_clear_roam_profile_ie() - Clear Roam Profile IEs + * @adapter: adapter who's IEs are to be cleared + * + * Return: None + */ +static void hdd_clear_roam_profile_ie(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx; + struct csr_roam_profile *roam_profile; + + hdd_enter(); + + /* clear WPA/RSN/WSC IE information in the profile */ + roam_profile = hdd_roam_profile(adapter); + + roam_profile->nWPAReqIELength = 0; + roam_profile->pWPAReqIE = NULL; + roam_profile->nRSNReqIELength = 0; + roam_profile->pRSNReqIE = NULL; + +#ifdef FEATURE_WLAN_WAPI + roam_profile->nWAPIReqIELength = 0; + roam_profile->pWAPIReqIE = NULL; +#endif + + roam_profile->bWPSAssociation = false; + roam_profile->bOSENAssociation = false; + roam_profile->pAddIEScan = NULL; + roam_profile->nAddIEScanLength = 0; + roam_profile->pAddIEAssoc = NULL; + roam_profile->nAddIEAssocLength = 0; + + roam_profile->EncryptionType.numEntries = 1; + roam_profile->EncryptionType.encryptionType[0] = + eCSR_ENCRYPT_TYPE_NONE; + + roam_profile->mcEncryptionType.numEntries = 1; + roam_profile->mcEncryptionType.encryptionType[0] = + eCSR_ENCRYPT_TYPE_NONE; + + roam_profile->AuthType.numEntries = 1; + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_OPEN_SYSTEM; + + qdf_mem_zero(roam_profile->bssid_hint.bytes, QDF_MAC_ADDR_SIZE); + +#ifdef WLAN_FEATURE_11W + roam_profile->MFPEnabled = false; + roam_profile->MFPRequired = 0; + roam_profile->MFPCapable = 0; +#endif + + qdf_mem_zero(roam_profile->Keys.KeyLength, CSR_MAX_NUM_KEY); + qdf_mem_zero(roam_profile->Keys.KeyMaterial, + sizeof(roam_profile->Keys.KeyMaterial)); +#ifdef FEATURE_WLAN_WAPI + adapter->wapi_info.wapi_auth_mode = WAPI_AUTH_MODE_OPEN; + adapter->wapi_info.wapi_mode = false; +#endif + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + sta_ctx->auth_key_mgmt = 0; + qdf_zero_macaddr(&sta_ctx->requested_bssid); + hdd_clear_fils_connection_info(adapter); + hdd_exit(); +} + +/** + * hdd_print_bss_info() - print bss info + * @hdd_sta_ctx: pointer to hdd station context + * + * Return: None + */ +static void hdd_print_bss_info(struct hdd_station_ctx *hdd_sta_ctx) +{ + uint32_t *ht_cap_info; + uint32_t *vht_cap_info; + struct hdd_connection_info *conn_info; + + conn_info = &hdd_sta_ctx->conn_info; + + hdd_nofl_debug("*********** WIFI DATA LOGGER **************"); + hdd_nofl_debug("freq: %d dot11mode %d AKM %d ssid: \"%.*s\" ,roam_count %d nss %d legacy %d mcs %d signal %d noise: %d", + conn_info->chan_freq, conn_info->dot11mode, + conn_info->last_auth_type, + conn_info->last_ssid.SSID.length, + conn_info->last_ssid.SSID.ssId, conn_info->roam_count, + conn_info->txrate.nss, conn_info->txrate.legacy, + conn_info->txrate.mcs, conn_info->signal, + conn_info->noise); + ht_cap_info = (uint32_t *)&conn_info->ht_caps; + vht_cap_info = (uint32_t *)&conn_info->vht_caps; + hdd_nofl_debug("HT 0x%x VHT 0x%x ht20 info 0x%x", + conn_info->conn_flag.ht_present ? *ht_cap_info : 0, + conn_info->conn_flag.vht_present ? *vht_cap_info : 0, + conn_info->conn_flag.hs20_present ? + conn_info->hs20vendor_ie.release_num : 0); +} + +/** + * hdd_cm_set_default_wlm_mode - reset the default wlm mode if + * wlm_latency_reset_on_disconnect is set. + *@adapter: adapter pointer + * + * return: None. + */ +static void hdd_cm_set_default_wlm_mode(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + bool reset; + uint8_t def_level; + mac_handle_t mac_handle; + uint16_t vdev_id; + + if (!hdd_ctx) { + hdd_err("hdd_ctx is NULL"); + return; + } + + status = ucfg_mlme_cfg_get_wlm_reset(hdd_ctx->psoc, &reset); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("could not get wlm reset flag"); + return; + } + if (!reset) + return; + + status = ucfg_mlme_cfg_get_wlm_level(hdd_ctx->psoc, &def_level); + if (QDF_IS_STATUS_ERROR(status)) + def_level = QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL; + + mac_handle = hdd_ctx->mac_handle; + vdev_id = adapter->vdev_id; + + status = sme_set_wlm_latency_level(mac_handle, vdev_id, def_level); + if (QDF_IS_STATUS_SUCCESS(status)) { + hdd_debug("reset wlm mode %x on disconnection", def_level); + adapter->latency_level = def_level; + } else { + hdd_err("reset wlm mode failed: %d", status); + } +} + +/** + * hdd_reset_udp_qos_upgrade_config() - Reset the threshold for UDP packet + * QoS upgrade. + * @adapter: adapter for which this configuration is to be applied + * + * Return: None + */ +static void hdd_reset_udp_qos_upgrade_config(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + bool reset; + QDF_STATUS status; + + if (!hdd_ctx) { + hdd_err("hdd_ctx is NULL"); + return; + } + + status = ucfg_mlme_cfg_get_wlm_reset(hdd_ctx->psoc, &reset); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("could not get the wlm reset flag"); + return; + } + + if (reset) { + adapter->upgrade_udp_qos_threshold = QCA_WLAN_AC_BK; + hdd_debug("UDP packets qos upgrade to: %d", + adapter->upgrade_udp_qos_threshold); + } +} + +/** + * hdd_dis_connect_handler() - disconnect event handler + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @roam_id: roam identifier + * @roam_status: roam status + * @roam_result: roam result + * + * This function handles disconnect event: + * 1. Disable transmit queues; + * 2. Clean up internal connection states and data structures; + * 3. Send disconnect indication to supplicant. + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS hdd_dis_connect_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct net_device *dev = adapter->dev; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + bool sendDisconInd = true; + mac_handle_t mac_handle; + struct wlan_ies disconnect_ies = {0}; + bool from_ap = false; + uint32_t reason_code = 0; + struct wlan_objmgr_vdev *vdev; + + if (!dev) { + hdd_err("net_dev is released return"); + return QDF_STATUS_E_FAILURE; + } + /* notify apps that we can't pass traffic anymore */ + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + if (ucfg_ipa_is_enabled() && + QDF_IS_STATUS_SUCCESS(wlan_hdd_validate_mac_address( + &sta_ctx->conn_info.bssid))) + ucfg_ipa_wlan_evt(hdd_ctx->pdev, adapter->dev, + adapter->device_mode, + adapter->vdev_id, + WLAN_IPA_STA_DISCONNECT, + sta_ctx->conn_info.bssid.bytes); + + hdd_cm_set_default_wlm_mode(adapter); + hdd_reset_udp_qos_upgrade_config(adapter); + hdd_periodic_sta_stats_stop(adapter); + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + wlan_hdd_auto_shutdown_enable(hdd_ctx, true); +#endif + + DPTRACE(qdf_dp_trace_mgmt_pkt(QDF_DP_TRACE_MGMT_PACKET_RECORD, + adapter->vdev_id, + QDF_TRACE_DEFAULT_PDEV_ID, + QDF_PROTO_TYPE_MGMT, QDF_PROTO_MGMT_DISASSOC)); + + /* HDD has initiated disconnect, do not send disconnect indication + * to kernel. Sending disconnected event to kernel for userspace + * initiated disconnect will be handled by disconnect handler call + * to cfg80211_disconnected. + */ + if ((eConnectionState_Disconnecting == + sta_ctx->conn_info.conn_state) || + (eConnectionState_NotConnected == + sta_ctx->conn_info.conn_state) || + (eConnectionState_Connecting == + sta_ctx->conn_info.conn_state)) { + hdd_debug("HDD has initiated a disconnect, no need to send disconnect indication to kernel"); + sendDisconInd = false; + } else { + INIT_COMPLETION(adapter->disconnect_comp_var); + hdd_conn_set_connection_state(adapter, + eConnectionState_Disconnecting); + } + + hdd_clear_roam_profile_ie(adapter); + hdd_wmm_init(adapter); + wlan_deregister_txrx_packetdump(OL_TXRX_PDEV_ID); + + /* indicate 'disconnect' status to wpa_supplicant... */ + hdd_send_association_event(dev, roam_info); + + /* + * Due to audio share glitch with P2P clients due + * to roam scan on concurrent interface, disable + * roaming if "p2p_disable_roam" ini is enabled. + * Re-enable roaming again once the p2p client + * gets disconnected. + */ + if (ucfg_p2p_is_roam_config_disabled(hdd_ctx->psoc) && + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + hdd_debug("P2P client disconnected, enable roam"); + wlan_hdd_enable_roaming(adapter, RSO_CONNECT_START); + } + + /* indicate disconnected event to nl80211 */ + if (roam_status != eCSR_ROAM_IBSS_LEAVE) { + /* + * Only send indication to kernel if not initiated + * by kernel + */ + if (sendDisconInd) { + int reason = WLAN_REASON_UNSPECIFIED; + + if (roam_info && roam_info->disconnect_ies) { + disconnect_ies.data = + roam_info->disconnect_ies->data; + disconnect_ies.len = + roam_info->disconnect_ies->len; + } + /* + * To avoid wpa_supplicant sending "HANGED" CMD + * to ICS UI. + */ + if (roam_info && eCSR_ROAM_LOSTLINK == roam_status) { + reason = roam_info->reasonCode; + if (reason == + eSIR_MAC_PEER_STA_REQ_LEAVING_BSS_REASON) + pr_info("wlan: disconnected due to poor signal, rssi is %d dB\n", + roam_info->rxRssi); + } + ucfg_mlme_get_discon_reason_n_from_ap(hdd_ctx->psoc, + adapter->vdev_id, + &from_ap, + &reason_code); + wlan_hdd_cfg80211_indicate_disconnect( + adapter, !from_ap, + reason_code, + disconnect_ies.data, + disconnect_ies.len); + } + + /* update P2P connection status */ + ucfg_p2p_status_disconnect(adapter->vdev); + } + + /* Inform BLM about the disconnection with the AP */ + if (adapter->device_mode == QDF_STA_MODE) + ucfg_blm_update_bssid_connect_params(hdd_ctx->pdev, + sta_ctx->conn_info.bssid, + BLM_AP_DISCONNECTED); + + hdd_wmm_adapter_clear(adapter); + mac_handle = hdd_ctx->mac_handle; + sme_ft_reset(mac_handle, adapter->vdev_id); + sme_reset_key(mac_handle, adapter->vdev_id); + + if (adapter->device_mode == QDF_STA_MODE) { + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + wlan_crypto_reset_vdev_params(vdev); + hdd_objmgr_put_vdev(vdev); + } + } + + hdd_remove_beacon_filter(adapter); + + if (sme_is_beacon_report_started(mac_handle, adapter->vdev_id)) { + hdd_debug("Sending beacon pause indication to userspace"); + hdd_beacon_recv_pause_indication((hdd_handle_t)hdd_ctx, + adapter->vdev_id, + SCAN_EVENT_TYPE_MAX, true); + } + /* clear scan cache for Link Lost */ + if (roam_status != eCSR_ROAM_IBSS_LEAVE) { + if (eCSR_ROAM_LOSTLINK == roam_status) { + wlan_hdd_cfg80211_unlink_bss(adapter, + sta_ctx->conn_info.bssid.bytes, + sta_ctx->conn_info.ssid.SSID.ssId, + sta_ctx->conn_info.ssid.SSID.length); + sme_remove_bssid_from_scan_list(mac_handle, + sta_ctx->conn_info.bssid.bytes); + } + } + + /* Clear saved connection information in HDD */ + hdd_conn_remove_connect_info(sta_ctx); + /* + * eConnectionState_Connecting state mean that connection is in + * progress so no need to set state to eConnectionState_NotConnected + */ + if ((eConnectionState_Connecting != sta_ctx->conn_info.conn_state)) + hdd_conn_set_connection_state(adapter, + eConnectionState_NotConnected); + + /* Clear roaming in progress flag */ + hdd_set_roaming_in_progress(false); + + ucfg_pmo_flush_gtk_offload_req(adapter->vdev); + + if ((QDF_STA_MODE == adapter->device_mode) || + (QDF_P2P_CLIENT_MODE == adapter->device_mode)) { + sme_ps_disable_auto_ps_timer(mac_handle, + adapter->vdev_id); + adapter->send_mode_change = true; + } + wlan_hdd_clear_link_layer_stats(adapter); + + policy_mgr_check_concurrent_intf_and_restart_sap(hdd_ctx->psoc); + adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt = 0; + + /* + * Reset hdd_reassoc_scenario to false here. After roaming in + * 802.1x or WPA3 security, EAPOL is handled at supplicant and + * the hdd_reassoc_scenario flag will not be reset if disconnection + * happens before EAP/EAPOL at supplicant is complete. + */ + sta_ctx->hdd_reassoc_scenario = false; + + /* Unblock anyone waiting for disconnect to complete */ + complete(&adapter->disconnect_comp_var); + + hdd_nud_reset_tracking(adapter); + + hdd_set_disconnect_status(adapter, false); + + hdd_reset_limit_off_chan(adapter); + + hdd_print_bss_info(sta_ctx); + + if (policy_mgr_is_sta_active_connection_exists(hdd_ctx->psoc)) + sme_enable_roaming_on_connected_sta(mac_handle, + adapter->vdev_id); + + return status; +} + +/** + * hdd_set_peer_authorized_event() - set peer_authorized_event + * @vdev_id: vdevid + * + * Return: None + */ +static void hdd_set_peer_authorized_event(uint32_t vdev_id) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct hdd_adapter *adapter = NULL; + + if (!hdd_ctx) { + hdd_err("Invalid hdd context"); + return; + } + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("Invalid vdev_id"); + return; + } + complete(&adapter->sta_authorized_event); +} + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2) +static inline +void hdd_set_unpause_queue(void *soc, struct hdd_adapter *adapter) +{ + unsigned long rc; + /* wait for event from firmware to set the event */ + rc = wait_for_completion_timeout( + &adapter->sta_authorized_event, + msecs_to_jiffies(HDD_PEER_AUTHORIZE_WAIT)); + if (!rc) + hdd_debug("timeout waiting for sta_authorized_event"); + + cdp_fc_vdev_unpause(soc, adapter->vdev_id, + OL_TXQ_PAUSE_REASON_PEER_UNAUTHORIZED, + 0); +} +#else +static inline +void hdd_set_unpause_queue(void *soc, struct hdd_adapter *adapter) +{ } +#endif + +QDF_STATUS hdd_change_peer_state(struct hdd_adapter *adapter, + uint8_t *peer_mac, + enum ol_txrx_peer_state sta_state, + bool roam_synch_in_progress) +{ + QDF_STATUS err; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + err = cdp_peer_state_update(soc, peer_mac, sta_state); + if (err != QDF_STATUS_SUCCESS) { + hdd_err("peer state update failed"); + return QDF_STATUS_E_FAULT; + } +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (roam_synch_in_progress) + return QDF_STATUS_SUCCESS; +#endif + + if (sta_state == OL_TXRX_PEER_STATE_AUTH) { + /* Reset scan reject params on successful set key */ + hdd_debug("Reset scan reject params"); + hdd_init_scan_reject_params(adapter->hdd_ctx); +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + /* make sure event is reset */ + INIT_COMPLETION(adapter->sta_authorized_event); +#endif + + err = sme_set_peer_authorized( + peer_mac, + hdd_set_peer_authorized_event, + adapter->vdev_id); + if (err != QDF_STATUS_SUCCESS) { + hdd_err("Failed to set the peer state to authorized"); + return QDF_STATUS_E_FAULT; + } + + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + hdd_set_unpause_queue(soc, adapter); + } + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_update_dp_vdev_flags(void *cbk_data, + uint8_t vdev_id, + uint32_t vdev_param, + bool is_link_up) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct hdd_context *hdd_ctx; + struct wlan_objmgr_psoc **psoc; + cdp_config_param_type val; + + if (!cbk_data) + return status; + + psoc = cbk_data; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("Invalid HDD Context"); + return QDF_STATUS_E_INVAL; + } + + if (!hdd_ctx->tdls_nap_active) + return status; + + if (vdev_id == WLAN_INVALID_VDEV_ID) { + status = QDF_STATUS_E_FAILURE; + return status; + } + + val.cdp_vdev_param_tdls_flags = is_link_up; + cdp_txrx_set_vdev_param(soc, vdev_id, vdev_param, val); + + return status; +} + +/** + * hdd_conn_change_peer_state() - Change the state of the peer + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @mac_addr: peer mac address + * @sta_state: peer state + * + * Return: QDF_STATUS enumeration + */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static QDF_STATUS hdd_conn_change_peer_state(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint8_t *mac_addr, + enum ol_txrx_peer_state sta_state) +{ + return hdd_change_peer_state(adapter, mac_addr, sta_state, + roam_info->roamSynchInProgress); +} +#else +static QDF_STATUS hdd_conn_change_peer_state(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint8_t *mac_addr, + enum ol_txrx_peer_state sta_state) +{ + return hdd_change_peer_state(adapter, mac_addr, sta_state, + false); +} +#endif + +#if defined(WLAN_SUPPORT_RX_FISA) +/** + * hdd_rx_register_fisa_ops() - FISA callback functions + * @txrx_ops: operations handle holding callback functions + * @hdd_rx_fisa_cbk: callback for fisa aggregation handle function + * @hdd_rx_fisa_flush: callback function to flush fisa aggregation + * + * Return: None + */ +static inline void +hdd_rx_register_fisa_ops(struct ol_txrx_ops *txrx_ops) +{ + txrx_ops->rx.osif_fisa_rx = hdd_rx_fisa_cbk; + txrx_ops->rx.osif_fisa_flush = hdd_rx_fisa_flush_by_ctx_id; +} +#else +static inline void +hdd_rx_register_fisa_ops(struct ol_txrx_ops *txrx_ops) +{ +} +#endif + +/** + * hdd_roam_register_sta() - register station + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @sta_id: station identifier + * @bss_desc: pointer to BSS description + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_roam_register_sta(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + struct bss_description *bss_desc) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct ol_txrx_desc_type txrx_desc = {0}; + struct ol_txrx_ops txrx_ops; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!bss_desc) + return QDF_STATUS_E_FAILURE; + + /* Get the Station ID from the one saved during the association */ + if (!QDF_IS_ADDR_BROADCAST(roam_info->bssid.bytes)) + WLAN_ADDR_COPY(txrx_desc.peer_addr.bytes, + roam_info->bssid.bytes); + else + WLAN_ADDR_COPY(txrx_desc.peer_addr.bytes, + adapter->mac_addr.bytes); + + /* set the QoS field appropriately */ + if (hdd_wmm_is_active(adapter)) + txrx_desc.is_qos_enabled = 1; + else + txrx_desc.is_qos_enabled = 0; + +#ifdef FEATURE_WLAN_WAPI + hdd_debug("WAPI STA Registered: %d", + adapter->wapi_info.is_wapi_sta); + if (adapter->wapi_info.is_wapi_sta) + txrx_desc.is_wapi_supported = 1; + else + txrx_desc.is_wapi_supported = 0; +#endif /* FEATURE_WLAN_WAPI */ + + /* Register the vdev transmit and receive functions */ + qdf_mem_zero(&txrx_ops, sizeof(txrx_ops)); + + if (adapter->hdd_ctx->enable_dp_rx_threads) { + txrx_ops.rx.rx = hdd_rx_pkt_thread_enqueue_cbk; + txrx_ops.rx.rx_stack = hdd_rx_packet_cbk; + txrx_ops.rx.rx_flush = hdd_rx_flush_packet_cbk; + txrx_ops.rx.rx_gro_flush = hdd_rx_thread_gro_flush_ind_cbk; + } else { + txrx_ops.rx.rx = hdd_rx_packet_cbk; + txrx_ops.rx.rx_stack = NULL; + txrx_ops.rx.rx_flush = NULL; + } + + if (adapter->hdd_ctx->config->fisa_enable && + (adapter->device_mode != QDF_MONITOR_MODE)) { + hdd_debug("FISA feature enabled"); + hdd_rx_register_fisa_ops(&txrx_ops); + } + + txrx_ops.rx.stats_rx = hdd_tx_rx_collect_connectivity_stats_info; + + txrx_ops.tx.tx = NULL; + cdp_vdev_register(soc, adapter->vdev_id, (ol_osif_vdev_handle)adapter, + &txrx_ops); + if (!txrx_ops.tx.tx) { + hdd_err("%s vdev register fail", __func__); + return QDF_STATUS_E_FAILURE; + } + + adapter->tx_fn = txrx_ops.tx.tx; + qdf_status = cdp_peer_register(soc, OL_TXRX_PDEV_ID, &txrx_desc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("cdp_peer_register() failed Status: %d [0x%08X]", + qdf_status, qdf_status); + return qdf_status; + } + + if (!roam_info->fAuthRequired) { + /* + * Connections that do not need Upper layer auth, transition + * TLSHIM directly to 'Authenticated' state + */ + qdf_status = hdd_conn_change_peer_state( + adapter, roam_info, + txrx_desc.peer_addr.bytes, + OL_TXRX_PEER_STATE_AUTH); + + hdd_conn_set_authenticated(adapter, true); + hdd_objmgr_set_peer_mlme_auth_state(adapter->vdev, true); + } else { + hdd_debug("ULA auth Sta: " QDF_MAC_ADDR_FMT + " Changing TL state to CONNECTED at Join time", + QDF_MAC_ADDR_REF(txrx_desc.peer_addr.bytes)); + + qdf_status = hdd_conn_change_peer_state( + adapter, roam_info, + txrx_desc.peer_addr.bytes, + OL_TXRX_PEER_STATE_CONN); + + hdd_conn_set_authenticated(adapter, false); + hdd_objmgr_set_peer_mlme_auth_state(adapter->vdev, false); + } + return qdf_status; +} + +/** + * hdd_send_roamed_ind() - send roamed indication to cfg80211 + * @dev: network device + * @bss: cfg80211 roamed bss pointer + * @req_ie: IEs used in reassociation request + * @req_ie_len: Length of the @req_ie + * @resp_ie: IEs received in successful reassociation response + * @resp_ie_len: Length of @resp_ie + * + * Return: none + */ +#if defined CFG80211_ROAMED_API_UNIFIED || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) +static void hdd_send_roamed_ind(struct net_device *dev, + struct cfg80211_bss *bss, const uint8_t *req_ie, + size_t req_ie_len, const uint8_t *resp_ie, + size_t resp_ie_len) +{ + struct cfg80211_roam_info info = {0}; + + info.bss = bss; + info.req_ie = req_ie; + info.req_ie_len = req_ie_len; + info.resp_ie = resp_ie; + info.resp_ie_len = resp_ie_len; + cfg80211_roamed(dev, &info, GFP_KERNEL); +} +#else +static inline void hdd_send_roamed_ind(struct net_device *dev, + struct cfg80211_bss *bss, + const uint8_t *req_ie, size_t req_ie_len, + const uint8_t *resp_ie, + size_t resp_ie_len) +{ + cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie, resp_ie_len, + GFP_KERNEL); +} +#endif + +#if defined(WLAN_FEATURE_ROAM_OFFLOAD) +#if defined(WLAN_FEATURE_FILS_SK) +void hdd_save_gtk_params(struct hdd_adapter *adapter, + struct csr_roam_info *csr_roam_info, bool is_reassoc) +{ + uint8_t *kek; + uint32_t kek_len; + + if (is_reassoc) { + kek = csr_roam_info->kek; + kek_len = csr_roam_info->kek_len; + } else { + /* + * This should come for FILS case only. + * Caller should make sure fils_join_rsp is + * not NULL, if there is need to use else where. + */ + kek = csr_roam_info->fils_join_rsp->kek; + kek_len = csr_roam_info->fils_join_rsp->kek_len; + } + + wlan_hdd_save_gtk_offload_params(adapter, NULL, 0, kek, kek_len, + csr_roam_info->replay_ctr, true); + + hdd_debug("Kek len %d", kek_len); +} +#else +void hdd_save_gtk_params(struct hdd_adapter *adapter, + struct csr_roam_info *csr_roam_info, bool is_reassoc) +{ + uint8_t *kek; + uint32_t kek_len; + + /* + * is_reassoc is set to true always for Legacy GTK offload + * case, It is false only for FILS case + */ + kek = csr_roam_info->kek; + kek_len = csr_roam_info->kek_len; + + wlan_hdd_save_gtk_offload_params(adapter, NULL, 0, kek, kek_len, + csr_roam_info->replay_ctr, true); + + hdd_debug("Kek len %d", kek_len); +} +#endif +#endif + +static void hdd_roam_decr_conn_count(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx) +{ + /* In case of LFR2.0, the number of connection count is + * already decrement in hdd_sme_roam_callback. Hence + * here we should not decrement again. + */ + if (roaming_offload_enabled(hdd_ctx)) + policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, + adapter->device_mode, + adapter->vdev_id); +} +/** + * hdd_send_re_assoc_event() - send reassoc event + * @dev: pointer to net device + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @reqRsnIe: pointer to RSN Information element + * @reqRsnLength: length of RSN IE + * + * Return: none + */ +static void hdd_send_re_assoc_event(struct net_device *dev, + struct hdd_adapter *adapter, struct csr_roam_info *roam_info, + uint8_t *reqRsnIe, uint32_t reqRsnLength) +{ + unsigned int len = 0; + u8 *assoc_rsp = NULL; + uint8_t *rsp_rsn_ie = qdf_mem_malloc(IW_GENERIC_IE_MAX); + uint8_t *assoc_req_ies = qdf_mem_malloc(IW_GENERIC_IE_MAX); + uint32_t rsp_rsn_lemgth = 0; + struct ieee80211_channel *chan; + uint8_t buf_ssid_ie[2 + WLAN_SSID_MAX_LEN]; /* 2 bytes-EID and len */ + uint8_t *buf_ptr, ssid_ie_len; + struct cfg80211_bss *bss = NULL; + uint8_t *final_req_ie = NULL; + tCsrRoamConnectedProfile roam_profile; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + qdf_mem_zero(&roam_profile, sizeof(roam_profile)); + + if (!rsp_rsn_ie) { + hdd_err("Unable to allocate RSN IE"); + goto done; + } + + if (!assoc_req_ies) { + hdd_err("Unable to allocate Assoc Req IE"); + goto done; + } + + if (!roam_info || !roam_info->bss_desc) { + hdd_err("Invalid CSR roam info"); + goto done; + } + + if (roam_info->nAssocRspLength < FT_ASSOC_RSP_IES_OFFSET) { + hdd_err("Invalid assoc rsp length %d", + roam_info->nAssocRspLength); + goto done; + } + + assoc_rsp = + (u8 *) (roam_info->pbFrames + roam_info->nBeaconLength + + roam_info->nAssocReqLength); + if (!assoc_rsp) + goto done; + + /* assoc_rsp needs to point to the IEs */ + assoc_rsp += FT_ASSOC_RSP_IES_OFFSET; + + /* + * Active session count is decremented upon disconnection, but during + * roaming, there is no disconnect indication and hence active session + * count is not decremented. + * After roaming is completed, active session count is incremented + * as a part of connect indication but effectively after roaming the + * active session count should still be the same and hence upon + * successful reassoc decrement the active session count here. + */ + if (!hdd_is_roam_sync_in_progress(roam_info)) { + hdd_roam_decr_conn_count(adapter, hdd_ctx); + hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, + false); + } + + /* Send the Assoc Resp, the supplicant needs this for initial Auth */ + len = roam_info->nAssocRspLength - FT_ASSOC_RSP_IES_OFFSET; + if (len > IW_GENERIC_IE_MAX) { + hdd_err("Invalid Assoc resp length %d", len); + goto done; + } + rsp_rsn_lemgth = len; + qdf_mem_copy(rsp_rsn_ie, assoc_rsp, len); + qdf_mem_zero(rsp_rsn_ie + len, IW_GENERIC_IE_MAX - len); + + chan = ieee80211_get_channel(adapter->wdev.wiphy, + roam_info->bss_desc->chan_freq); + + sme_roam_get_connect_profile(hdd_ctx->mac_handle, adapter->vdev_id, + &roam_profile); + + bss = wlan_cfg80211_get_bss(adapter->wdev.wiphy, + chan, roam_info->bssid.bytes, + &roam_profile.SSID.ssId[0], + roam_profile.SSID.length); + + if (!bss) + hdd_warn("Get BSS returned NULL"); + buf_ptr = buf_ssid_ie; + *buf_ptr = WLAN_ELEMID_SSID; + buf_ptr++; + *buf_ptr = roam_profile.SSID.length; /*len of ssid*/ + buf_ptr++; + qdf_mem_copy(buf_ptr, &roam_profile.SSID.ssId[0], + roam_profile.SSID.length); + ssid_ie_len = 2 + roam_profile.SSID.length; + final_req_ie = qdf_mem_malloc(IW_GENERIC_IE_MAX); + if (!final_req_ie) { + if (bss) + cfg80211_put_bss(adapter->wdev.wiphy, bss); + goto done; + } + buf_ptr = final_req_ie; + qdf_mem_copy(buf_ptr, buf_ssid_ie, ssid_ie_len); + buf_ptr += ssid_ie_len; + qdf_mem_copy(buf_ptr, reqRsnIe, reqRsnLength); + qdf_mem_copy(rsp_rsn_ie, assoc_rsp, len); + qdf_mem_zero(final_req_ie + (ssid_ie_len + reqRsnLength), + IW_GENERIC_IE_MAX - (ssid_ie_len + reqRsnLength)); + hdd_debug("Req RSN IE:"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + final_req_ie, (ssid_ie_len + reqRsnLength)); + hdd_send_roamed_ind(dev, bss, final_req_ie, + (ssid_ie_len + reqRsnLength), rsp_rsn_ie, + rsp_rsn_lemgth); + + qdf_mem_copy(assoc_req_ies, + (u8 *)roam_info->pbFrames + roam_info->nBeaconLength, + roam_info->nAssocReqLength); + + hdd_save_gtk_params(adapter, roam_info, true); + + hdd_debug("ReAssoc Req IE dump"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + assoc_req_ies, roam_info->nAssocReqLength); + + wlan_hdd_send_roam_auth_event(adapter, roam_info->bssid.bytes, + assoc_req_ies, roam_info->nAssocReqLength, + rsp_rsn_ie, rsp_rsn_lemgth, + roam_info); + + hdd_update_hlp_info(dev, roam_info); + +done: + sme_roam_free_connect_profile(&roam_profile); + if (final_req_ie) + qdf_mem_free(final_req_ie); + qdf_mem_free(rsp_rsn_ie); + qdf_mem_free(assoc_req_ies); +} + +/** + * hdd_is_roam_sync_in_progress()- Check if roam offloaded + * @roaminfo - Roaming Information + * + * Return: roam sync status if roaming offloaded else false + */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +bool hdd_is_roam_sync_in_progress(struct csr_roam_info *roaminfo) +{ + if (roaminfo) + return roaminfo->roamSynchInProgress; + else + return false; +} +#endif + +#ifdef QCA_IBSS_SUPPORT +/** + * hdd_roam_ibss_indication_handler() - update the status of the IBSS + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @roam_id: roam id + * @roam_status: roam status + * @roam_result: roam result + * + * Here we update the status of the Ibss when we receive information that we + * have started/joined an ibss session. + * + * Return: none + */ +static void hdd_roam_ibss_indication_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_debug("%s: id %d, status %d, result %d", + adapter->dev->name, roam_id, + roam_status, roam_result); + + switch (roam_result) { + /* both IBSS Started and IBSS Join should come in here. */ + case eCSR_ROAM_RESULT_IBSS_STARTED: + case eCSR_ROAM_RESULT_IBSS_JOIN_SUCCESS: + case eCSR_ROAM_RESULT_IBSS_COALESCED: + { + if (!roam_info) { + QDF_ASSERT(0); + return; + } + + /* When IBSS Started comes from CSR, we need to move + * connection state to IBSS Disconnected (meaning no peers + * are in the IBSS). + */ + hdd_conn_set_connection_state(adapter, + eConnectionState_IbssDisconnected); + /* notify wmm */ + hdd_wmm_connect(adapter, roam_info, + eCSR_BSS_TYPE_IBSS); + + hdd_roam_register_sta(adapter, roam_info, roam_info->bss_desc); + + if (roam_info->bss_desc) { + struct cfg80211_bss *bss; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) + struct ieee80211_channel *chan; +#endif + /* we created the IBSS, notify supplicant */ + hdd_debug("%s: created ibss " QDF_MAC_ADDR_FMT, + adapter->dev->name, + QDF_MAC_ADDR_REF( + roam_info->bss_desc->bssId)); + + /* we must first give cfg80211 the BSS information */ + bss = wlan_hdd_cfg80211_update_bss_db(adapter, + roam_info); + if (!bss) { + hdd_err("%s: unable to create IBSS entry", + adapter->dev->name); + return; + } + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) + chan = ieee80211_get_channel( + adapter->wdev.wiphy, + roam_info->bss_desc->chan_freq); + + if (chan) + cfg80211_ibss_joined(adapter->dev, + bss->bssid, chan, + GFP_KERNEL); + else + hdd_warn("%s: freq: %d, can't find channel", + adapter->dev->name, + roam_info->bss_desc->chan_freq); +#else + cfg80211_ibss_joined(adapter->dev, bss->bssid, + GFP_KERNEL); +#endif + cfg80211_put_bss( + hdd_ctx->wiphy, + bss); + } + if (eCSR_ROAM_RESULT_IBSS_STARTED == roam_result) { + policy_mgr_incr_active_session(hdd_ctx->psoc, + adapter->device_mode, adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, + adapter->device_mode, true); + } else if (eCSR_ROAM_RESULT_IBSS_JOIN_SUCCESS == roam_result || + eCSR_ROAM_RESULT_IBSS_COALESCED == roam_result) { + policy_mgr_update_connection_info(hdd_ctx->psoc, + adapter->vdev_id); + } + break; + } + + case eCSR_ROAM_RESULT_IBSS_START_FAILED: + { + hdd_err("%s: unable to create IBSS", adapter->dev->name); + break; + } + + default: + hdd_err("%s: unexpected result %d", + adapter->dev->name, (int)roam_result); + break; + } +} + +/** + * hdd_is_key_install_required_for_ibss() - check encryption type to identify + * if key installation is required + * @encr_type: encryption type + * + * Return: true if key installation is required and false otherwise. + */ +static inline bool hdd_is_key_install_required_for_ibss( + eCsrEncryptionType encr_type) +{ + if (eCSR_ENCRYPT_TYPE_WEP40_STATICKEY == encr_type || + eCSR_ENCRYPT_TYPE_WEP104_STATICKEY == encr_type || + eCSR_ENCRYPT_TYPE_TKIP == encr_type || + eCSR_ENCRYPT_TYPE_AES_GCMP == encr_type || + eCSR_ENCRYPT_TYPE_AES_GCMP_256 == encr_type || + eCSR_ENCRYPT_TYPE_AES == encr_type) + return true; + else + return false; +} +#else +/** + * hdd_roam_ibss_indication_handler() - update the status of the IBSS + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @roam_id: roam id + * @roam_status: roam status + * @roam_result: roam result + * + * This function is dummy + * + * Return: none + */ +static inline void +hdd_roam_ibss_indication_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ +} + +/** + * hdd_get_ibss_peer_sta_id() - get sta id for IBSS peer + * @hddstactx: pointer to HDD sta context + * @roaminfo: pointer to roaminfo structure + * + * This function is dummy + * + * Return: WLAN_MAX_STA_COUNT if peer not found. + */ +static inline uint8_t +hdd_get_ibss_peer_sta_id(struct hdd_station_ctx *hddstactx, + struct csr_roam_info *roaminfo) +{ + return WLAN_MAX_STA_COUNT; +} + +/** + * hdd_is_key_install_required_for_ibss() - check encryption type to identify + * if key installation is required + * @encr_type: encryption type + * + * This function is dummy + * + * Return: true. + */ +static inline bool +hdd_is_key_install_required_for_ibss(eCsrEncryptionType encr_type) +{ + return true; +} +#endif + +/** + * hdd_change_sta_state_authenticated()- + * This function changes STA state to authenticated + * @adapter: pointer to the adapter structure. + * @roaminfo: pointer to the RoamInfo structure. + * + * This is called from hdd_RoamSetKeyCompleteHandler + * in context to eCSR_ROAM_SET_KEY_COMPLETE event from fw. + * + * Return: 0 on success and errno on failure + */ +static int hdd_change_sta_state_authenticated(struct hdd_adapter *adapter, + struct csr_roam_info *roaminfo) +{ + QDF_STATUS status; + uint8_t *mac_addr; + uint32_t timeout, auto_bmps_timer_val; + struct hdd_station_ctx *hddstactx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + ucfg_mlme_get_auto_bmps_timer_value(hdd_ctx->psoc, + &auto_bmps_timer_val); + timeout = hddstactx->hdd_reassoc_scenario ? + AUTO_PS_ENTRY_TIMER_DEFAULT_VALUE : + (auto_bmps_timer_val * 1000); + + if (QDF_IBSS_MODE == adapter->device_mode) + mac_addr = roaminfo->peerMac.bytes; + else + mac_addr = hddstactx->conn_info.bssid.bytes; + + hdd_debug("Changing Peer state to AUTHENTICATED for Sta = " + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(mac_addr)); + + /* Connections that do not need Upper layer authentication, + * transition TL to 'Authenticated' state after the keys are set + */ + + status = hdd_change_peer_state(adapter, mac_addr, + OL_TXRX_PEER_STATE_AUTH, + hdd_is_roam_sync_in_progress(roaminfo)); + hdd_conn_set_authenticated(adapter, true); + hdd_objmgr_set_peer_mlme_auth_state(adapter->vdev, true); + + if ((QDF_STA_MODE == adapter->device_mode) || + (QDF_P2P_CLIENT_MODE == adapter->device_mode)) { + sme_ps_enable_auto_ps_timer(hdd_ctx->mac_handle, + adapter->vdev_id, + timeout); + } + + return qdf_status_to_os_return(status); +} + +/** + * hdd_change_peer_state_after_set_key() - change the peer state on set key + * complete + * @adapter: pointer to HDD adapter + * @roaminfo: pointer to roam info + * @roam_result: roam result + * + * Peer state will be OL_TXRX_PEER_STATE_CONN until set key is complete. + * This function checks for the successful set key completion and update + * the peer state to OL_TXRX_PEER_STATE_AUTH. + * + * Return: None + */ +static void hdd_change_peer_state_after_set_key(struct hdd_adapter *adapter, + struct csr_roam_info *roaminfo, + eCsrRoamResult roam_result) +{ + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + eCsrEncryptionType encr_type = hdd_sta_ctx->conn_info.uc_encrypt_type; + + /* + * If the security mode is one of the following, IBSS peer will be + * waiting in CONN state and we will move the peer state to AUTH + * here. For non-secure connection, no need to wait for set-key complete + * peer will be moved to AUTH in hdd_roam_register_sta. + */ + if (QDF_IBSS_MODE == adapter->device_mode) { + if (hdd_is_key_install_required_for_ibss(encr_type)) + hdd_change_sta_state_authenticated(adapter, roaminfo); + + return; + } + + if (eCSR_ROAM_RESULT_AUTHENTICATED == roam_result) { + hdd_sta_ctx->conn_info.gtk_installed = true; + /* + * PTK exchange happens in preauthentication itself if key_mgmt + * is FT-PSK, ptk_installed was false as there is no set PTK + * after roaming. STA TL state moves to authenticated only if + * ptk_installed is true. So, make ptk_installed to true in + * case of 11R roaming. + */ + if (sme_neighbor_roam_is11r_assoc(adapter->hdd_ctx->mac_handle, + adapter->vdev_id)) + hdd_sta_ctx->conn_info.ptk_installed = true; + } else { + hdd_sta_ctx->conn_info.ptk_installed = true; + } + + /* In WPA case move STA to authenticated when ptk is installed. Earlier + * in WEP case STA was moved to AUTHENTICATED prior to setting the + * unicast key and it was resulting in sending few un-encrypted packet. + * Now in WEP case STA state will be moved to AUTHENTICATED after we + * set the unicast and broadcast key. + */ + if ((encr_type == eCSR_ENCRYPT_TYPE_WEP40) || + (encr_type == eCSR_ENCRYPT_TYPE_WEP104) || + (encr_type == eCSR_ENCRYPT_TYPE_WEP40_STATICKEY) || + (encr_type == eCSR_ENCRYPT_TYPE_WEP104_STATICKEY)) { + if (hdd_sta_ctx->conn_info.gtk_installed && + hdd_sta_ctx->conn_info.ptk_installed) + hdd_change_sta_state_authenticated(adapter, roaminfo); + } else if (hdd_sta_ctx->conn_info.ptk_installed) { + hdd_change_sta_state_authenticated(adapter, roaminfo); + } + + if (hdd_sta_ctx->conn_info.gtk_installed && + hdd_sta_ctx->conn_info.ptk_installed) { + hdd_sta_ctx->conn_info.gtk_installed = false; + hdd_sta_ctx->conn_info.ptk_installed = false; + } +} + +/** + * hdd_roam_set_key_complete_handler() - Update the security parameters + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @roam_id: roam id + * @roam_status: roam status + * @roam_result: roam result + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS +hdd_roam_set_key_complete_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ + eCsrEncryptionType algorithm; + bool connected = false; + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + hdd_enter(); + + if (!roam_info) { + hdd_err("roam_info is NULL"); + return QDF_STATUS_E_FAILURE; + } + /* + * if (WPA), tell TL to go to 'authenticated' after the keys are set. + * then go to 'authenticated'. For all other authentication types + * (those that do not require upper layer authentication) we can put TL + * directly into 'authenticated' state. + */ + hdd_debug("Set Key completion roam_status =%d roam_result=%d " + QDF_MAC_ADDR_FMT, roam_status, roam_result, + QDF_MAC_ADDR_REF(roam_info->peerMac.bytes)); + + connected = hdd_conn_get_connected_cipher_algo(sta_ctx, + &algorithm); + if (connected) { + hdd_change_peer_state_after_set_key(adapter, roam_info, + roam_result); + } + + if (!adapter->hdd_ctx || !adapter->hdd_ctx->psoc) { + hdd_err("hdd_ctx or psoc is NULL"); + return QDF_STATUS_E_FAILURE; + } + policy_mgr_restart_opportunistic_timer(adapter->hdd_ctx->psoc, false); + + hdd_exit(); + return QDF_STATUS_SUCCESS; +} + +#if defined(WLAN_FEATURE_FILS_SK) && \ + (defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) +void hdd_clear_fils_connection_info(struct hdd_adapter *adapter) +{ + struct csr_roam_profile *roam_profile; + + if ((adapter->device_mode == QDF_SAP_MODE) || + (adapter->device_mode == QDF_P2P_GO_MODE)) + return; + + roam_profile = hdd_roam_profile(adapter); + if (roam_profile->fils_con_info) { + qdf_mem_free(roam_profile->fils_con_info); + roam_profile->fils_con_info = NULL; + } + + if (roam_profile->hlp_ie) { + qdf_mem_free(roam_profile->hlp_ie); + roam_profile->hlp_ie = NULL; + roam_profile->hlp_ie_len = 0; + } +} +#endif + +/** + * hdd_netif_queue_enable() - Enable the network queue for a + * particular adapter. + * @adapter: pointer to the adapter structure + * + * This function schedules a work to update the netdev features + * and enable the network queue if the feature "disable checksum/tso + * for legacy connections" is enabled via INI. If not, it will + * retain the existing behavior by just enabling the network queues. + * + * Returns: none + */ +static inline void hdd_netif_queue_enable(struct hdd_adapter *adapter) +{ + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (cdp_cfg_get(soc, cfg_dp_disable_legacy_mode_csum_offload)) { + hdd_adapter_ops_record_event(hdd_ctx, + WLAN_HDD_ADAPTER_OPS_WORK_POST, + adapter->vdev_id); + qdf_queue_work(0, hdd_ctx->adapter_ops_wq, + &adapter->netdev_features_update_work); + } else { + wlan_hdd_netif_queue_control(adapter, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + } +} + +static void hdd_save_connect_status(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + if (!roam_info) + return; + + adapter->connect_req_status = roam_info->reasonCode; +} + +/** + * hdd_association_completion_handler() - association completion handler + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @roam_id: roam id + * @roam_status: roam status + * @roam_result: roam result + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS +hdd_association_completion_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ + struct net_device *dev = adapter->dev; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + int8_t snr = 0; + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + uint8_t *reqRsnIe; + uint32_t reqRsnLength = DOT11F_IE_RSN_MAX_LEN, ie_len; + int ft_carrier_on = false; + bool hddDisconInProgress = false; + unsigned long rc; + tSirResultCodes timeout_reason = 0; + bool ok; + mac_handle_t mac_handle; + uint32_t conn_info_freq; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + /* validate config */ + if (!hdd_ctx->config) { + hdd_err("config is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + hdd_get_rssi_snr_by_bssid(adapter, sta_ctx->conn_info.bssid.bytes, + &adapter->rssi, &snr); + + /* If RSSi is reported as positive then it is invalid */ + if (adapter->rssi > 0) { + hdd_debug_rl("RSSI invalid %d", adapter->rssi); + adapter->rssi = 0; + } + + hdd_debug("snr: %d, rssi: %d", snr, adapter->rssi); + + sta_ctx->conn_info.signal = adapter->rssi; + sta_ctx->conn_info.noise = + sta_ctx->conn_info.signal - snr; + sta_ctx->cache_conn_info.signal = sta_ctx->conn_info.signal; + sta_ctx->cache_conn_info.noise = sta_ctx->conn_info.noise; + + hdd_save_connect_status(adapter, roam_info); + /* + * reset scan reject params if connection is success or we received + * final failure from CSR after trying with all APs. + */ + hdd_reset_scan_reject_params(hdd_ctx, roam_status, roam_result); + + /* HDD has initiated disconnect, do not send connect result indication + * to kernel as it will be handled by __cfg80211_disconnect. + */ + if (((eConnectionState_Disconnecting == + sta_ctx->conn_info.conn_state) || + (eConnectionState_NotConnected == + sta_ctx->conn_info.conn_state)) && + ((eCSR_ROAM_RESULT_ASSOCIATED == roam_result) || + (eCSR_ROAM_ASSOCIATION_FAILURE == roam_status))) { + hdd_info("hddDisconInProgress state=%d, result=%d, status=%d", + sta_ctx->conn_info.conn_state, + roam_result, roam_status); + hddDisconInProgress = true; + } + + mac_handle = hdd_ctx->mac_handle; + + if (eCSR_ROAM_RESULT_ASSOCIATED == roam_result) { + if (!roam_info) { + hdd_err("roam_info is NULL"); + return QDF_STATUS_E_FAILURE; + } + if (!hddDisconInProgress) { + hdd_conn_set_connection_state(adapter, + eConnectionState_Associated); + } + + /* + * Due to audio share glitch with P2P clients due + * to roam scan on concurrent interface, disable + * roaming if "p2p_disable_roam" ini is enabled. + * Donot re-enable roaming again on other STA interface + * if p2p client connection is active on any vdev. + */ + if (ucfg_p2p_is_roam_config_disabled(hdd_ctx->psoc) && + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + hdd_debug("p2p cli active keep roam disabled"); + } else { + /* + * Enable roaming on other STA iface except this one. + * Firmware dosent support connection on one STA iface + * while roaming on other STA iface + */ + wlan_hdd_enable_roaming(adapter, RSO_CONNECT_START); + } + + /* Save the connection info from CSR... */ + hdd_conn_save_connect_info(adapter, roam_info, + eCSR_BSS_TYPE_INFRASTRUCTURE); + + if (hdd_add_beacon_filter(adapter) != 0) + hdd_err("hdd_add_beacon_filter() failed"); +#ifdef FEATURE_WLAN_WAPI + if (roam_info->u.pConnectedProfile->AuthType == + eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE + || roam_info->u.pConnectedProfile->AuthType == + eCSR_AUTH_TYPE_WAPI_WAI_PSK) { + adapter->wapi_info.is_wapi_sta = true; + } else { + adapter->wapi_info.is_wapi_sta = false; + } +#endif /* FEATURE_WLAN_WAPI */ + if ((QDF_STA_MODE == adapter->device_mode) && + roam_info->bss_desc) { + ie_len = GET_IE_LEN_IN_BSS(roam_info->bss_desc->length); + sta_ctx->ap_supports_immediate_power_save = + wlan_hdd_is_ap_supports_immediate_power_save( + (uint8_t *)roam_info->bss_desc->ieFields, + ie_len); + hdd_debug("ap_supports_immediate_power_save flag [%d]", + sta_ctx->ap_supports_immediate_power_save); + } + + /* Indicate 'connect' status to user space */ + hdd_send_association_event(dev, roam_info); + + if (policy_mgr_is_mcc_in_24G(hdd_ctx->psoc)) { + if (hdd_ctx->miracast_value) + wlan_hdd_set_mas(adapter, + hdd_ctx->miracast_value); + } + + /* Initialize the Linkup event completion variable */ + INIT_COMPLETION(adapter->linkup_event_var); + + /* + * Sometimes Switching ON the Carrier is taking time to activate + * the device properly. Before allowing any packet to go up to + * the application, device activation has to be ensured for + * proper queue mapping by the kernel. we have registered net + * device notifier for device change notification. With this we + * will come to know that the device is getting + * activated properly. + */ + if (sta_ctx->ft_carrier_on == false) { + /* + * Enable Linkup Event Servicing which allows the net + * device notifier to set the linkup event variable. + */ + adapter->is_link_up_service_needed = true; + + /* Switch on the Carrier to activate the device */ + wlan_hdd_netif_queue_control(adapter, + WLAN_NETIF_CARRIER_ON, + WLAN_CONTROL_PATH); + + /* + * Wait for the Link to up to ensure all the queues + * are set properly by the kernel. + */ + rc = wait_for_completion_timeout( + &adapter->linkup_event_var, + msecs_to_jiffies(ASSOC_LINKUP_TIMEOUT) + ); + if (!rc) + hdd_warn("Warning:ASSOC_LINKUP_TIMEOUT"); + + /* + * Disable Linkup Event Servicing - no more service + * required from the net device notifier call. + */ + adapter->is_link_up_service_needed = false; + } else { + sta_ctx->ft_carrier_on = false; + ft_carrier_on = true; + } + + if (ucfg_ipa_is_enabled()) + ucfg_ipa_wlan_evt(hdd_ctx->pdev, adapter->dev, + adapter->device_mode, + adapter->vdev_id, + WLAN_IPA_STA_CONNECT, + roam_info->bssid.bytes); + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + wlan_hdd_auto_shutdown_enable(hdd_ctx, false); +#endif + + conn_info_freq = sta_ctx->conn_info.chan_freq; + + if (policy_mgr_is_chan_ok_for_dnbs(hdd_ctx->psoc, + conn_info_freq, &ok)) { + hdd_err("Unable to check DNBS eligibility for chan(freq):%u", + sta_ctx->conn_info.chan_freq); + return QDF_STATUS_E_FAILURE; + } + + if (!ok) { + hdd_err("Chan(freq):%u not suitable for DNBS", + sta_ctx->conn_info.chan_freq); + wlan_hdd_netif_queue_control(adapter, + WLAN_NETIF_CARRIER_OFF, + WLAN_CONTROL_PATH); + if (!hddDisconInProgress) { + hdd_err("Disconnecting..."); + sme_roam_disconnect( + mac_handle, + adapter->vdev_id, + eCSR_DISCONNECT_REASON_UNSPECIFIED, + eSIR_MAC_UNSPEC_FAILURE_REASON); + } + return QDF_STATUS_E_FAILURE; + } + + DPTRACE(qdf_dp_trace_mgmt_pkt(QDF_DP_TRACE_MGMT_PACKET_RECORD, + adapter->vdev_id, + QDF_TRACE_DEFAULT_PDEV_ID, + QDF_PROTO_TYPE_MGMT, QDF_PROTO_MGMT_ASSOC)); + + reqRsnIe = qdf_mem_malloc(sizeof(uint8_t) * + DOT11F_IE_RSN_MAX_LEN); + if (!reqRsnIe) + return QDF_STATUS_E_NOMEM; + + if (roam_info->fReassocReq || ft_carrier_on) + hdd_nud_indicate_roam(adapter); + + /* + * For reassoc, the station is already registered, all we need + * is to change the state of the STA in TL. + * If authentication is required (WPA/WPA2/DWEP), change TL to + * CONNECTED instead of AUTHENTICATED. + */ + if (!roam_info->fReassocReq) { + struct cfg80211_bss *bss; + u8 *assoc_rsp = NULL; + unsigned int assoc_rsp_len = 0; + u8 *assoc_req = NULL; + unsigned int assoc_req_len = 0; + struct ieee80211_channel *chan; + uint32_t rsp_rsn_lemgth = DOT11F_IE_RSN_MAX_LEN; + uint8_t *rsp_rsn_ie = + qdf_mem_malloc(sizeof(uint8_t) * + DOT11F_IE_RSN_MAX_LEN); + if (!rsp_rsn_ie) { + qdf_mem_free(reqRsnIe); + return QDF_STATUS_E_NOMEM; + } + + + /* add bss_id to cfg80211 data base */ + bss = + wlan_hdd_cfg80211_update_bss_db(adapter, + roam_info); + if (!bss) { + hdd_err("wlan: Not able to create BSS entry"); + wlan_hdd_netif_queue_control(adapter, + WLAN_NETIF_CARRIER_OFF, + WLAN_CONTROL_PATH); + if (!hddDisconInProgress) { + /* + * Here driver was not able to add bss + * in cfg80211 database this can happen + * if connected channel is not valid, + * i.e reg domain was changed during + * connection. Queue disconnect for the + * session if disconnect is not in + * progress. + */ + hdd_debug("Disconnecting..."); + sme_roam_disconnect( + mac_handle, + adapter->vdev_id, + eCSR_DISCONNECT_REASON_UNSPECIFIED, + eSIR_MAC_UNSPEC_FAILURE_REASON); + } + qdf_mem_free(reqRsnIe); + qdf_mem_free(rsp_rsn_ie); + return QDF_STATUS_E_FAILURE; + } + + cfg80211_put_bss(hdd_ctx->wiphy, bss); + + /* Association Response */ + assoc_rsp = + (u8 *) (roam_info->pbFrames + + roam_info->nBeaconLength + + roam_info->nAssocReqLength); + if (assoc_rsp) { + /* + * assoc_rsp needs to point to the IEs + */ + assoc_rsp += FT_ASSOC_RSP_IES_OFFSET; + assoc_rsp_len = + roam_info->nAssocRspLength - + FT_ASSOC_RSP_IES_OFFSET; + + hdd_debug("assoc_rsp_len %d", assoc_rsp_len); + } else { + hdd_debug("AssocRsp is NULL"); + assoc_rsp_len = 0; + } + + /* Association Request */ + assoc_req = (u8 *) (roam_info->pbFrames + + roam_info->nBeaconLength); + if (assoc_req) { + if (!ft_carrier_on) { + /* + * assoc_req needs to point to + * the IEs + */ + assoc_req += + FT_ASSOC_REQ_IES_OFFSET; + assoc_req_len = + roam_info->nAssocReqLength - + FT_ASSOC_REQ_IES_OFFSET; + } else { + /* + * This should contain only the + * FTIEs + */ + assoc_req_len = + roam_info->nAssocReqLength; + } + hdd_debug("assoc_req_len %d", assoc_req_len); + } else { + hdd_debug("AssocReq is NULL"); + assoc_req_len = 0; + } + + if ((roam_info->u.pConnectedProfile->AuthType == + eCSR_AUTH_TYPE_FT_RSN) || + (roam_info->u.pConnectedProfile->AuthType == + eCSR_AUTH_TYPE_FT_RSN_PSK) || + (roam_info->u.pConnectedProfile->AuthType == + eCSR_AUTH_TYPE_FT_SAE) || + (roam_info->u.pConnectedProfile->AuthType == + eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384)) { + if (ft_carrier_on) { + if (!hddDisconInProgress && + roam_info->bss_desc) { + struct cfg80211_bss *roam_bss; + + /* + * After roaming is completed, + * active session count is + * incremented as a part of + * connect indication but + * effectively the active + * session count should still + * be the same and hence upon + * successful reassoc + * decrement the active session + * count here. + */ + if (!hdd_is_roam_sync_in_progress + (roam_info)) { + hdd_roam_decr_conn_count( + adapter, hdd_ctx); + + hdd_green_ap_start_state_mc( + hdd_ctx, + adapter->device_mode, + false); + } + hdd_debug("ft_carrier_on is %d, sending roamed indication", + ft_carrier_on); + chan = ieee80211_get_channel( + adapter->wdev.wiphy, + roam_info->bss_desc->chan_freq); + + roam_bss = + wlan_cfg80211_get_bss( + adapter->wdev.wiphy, + chan, + roam_info->bssid.bytes, + roam_info->u. + pConnectedProfile->SSID.ssId, + roam_info->u. + pConnectedProfile->SSID.length); + + cdp_hl_fc_set_td_limit(soc, + adapter->vdev_id, + conn_info_freq); + + hdd_send_roamed_ind( + dev, + roam_bss, + assoc_req, + assoc_req_len, + assoc_rsp, + assoc_rsp_len); + wlan_hdd_send_roam_auth_event( + adapter, + roam_info->bssid.bytes, + assoc_req, + assoc_req_len, + assoc_rsp, + assoc_rsp_len, + roam_info); + } + } else if (!hddDisconInProgress) { + hdd_debug("ft_carrier_on is %d, sending connect indication", + ft_carrier_on); + cdp_hl_fc_set_td_limit(soc, + adapter->vdev_id, + conn_info_freq); + hdd_connect_result(dev, + roam_info-> + bssid.bytes, + roam_info, + assoc_req, + assoc_req_len, + assoc_rsp, + assoc_rsp_len, + WLAN_STATUS_SUCCESS, + GFP_KERNEL, false, + roam_info->status_code); + } + } else { + /* + * wpa supplicant expecting WPA/RSN IE in + * connect result. + */ + + sme_roam_get_wpa_rsn_req_ie(mac_handle, + adapter->vdev_id, + &reqRsnLength, + reqRsnIe); + + sme_roam_get_wpa_rsn_rsp_ie(mac_handle, + adapter->vdev_id, + &rsp_rsn_lemgth, + rsp_rsn_ie); + if (!hddDisconInProgress) { + if (ft_carrier_on) + hdd_send_re_assoc_event(dev, + adapter, + roam_info, + reqRsnIe, + reqRsnLength); + else { + hdd_debug("sending connect indication to nl80211:for bssid " + QDF_MAC_ADDR_FMT + " result:%d and Status:%d", + QDF_MAC_ADDR_REF + (roam_info->bssid.bytes), + roam_result, roam_status); + + /* inform connect result to nl80211 */ + hdd_connect_result(dev, + roam_info-> + bssid.bytes, + roam_info, + assoc_req, + assoc_req_len, + assoc_rsp, + assoc_rsp_len, + WLAN_STATUS_SUCCESS, + GFP_KERNEL, + false, + roam_info->status_code); + } + cdp_hl_fc_set_td_limit(soc, + adapter->vdev_id, + conn_info_freq); + } + } + if (!hddDisconInProgress) { + /* + * Perform any WMM-related association + * processing. + */ + hdd_wmm_assoc(adapter, roam_info, + eCSR_BSS_TYPE_INFRASTRUCTURE); + + /* + * Register the Station with DP after associated + */ + qdf_status = hdd_roam_register_sta(adapter, + roam_info, + roam_info->bss_desc); + hdd_debug("Enabling queues"); + hdd_netif_queue_enable(adapter); + } + qdf_mem_free(rsp_rsn_ie); + } else { + /* + * wpa supplicant expecting WPA/RSN IE in connect result + * in case of reassociation also need to indicate it to + * supplicant. + */ + sme_roam_get_wpa_rsn_req_ie( + mac_handle, + adapter->vdev_id, + &reqRsnLength, reqRsnIe); + + cdp_hl_fc_set_td_limit(soc, adapter->vdev_id, + conn_info_freq); + hdd_send_re_assoc_event(dev, adapter, roam_info, + reqRsnIe, reqRsnLength); + /* Reassoc successfully */ + if (roam_info->fAuthRequired) { + qdf_status = + hdd_change_peer_state(adapter, + roam_info->bssid.bytes, + OL_TXRX_PEER_STATE_CONN, +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + roam_info->roamSynchInProgress +#else + false +#endif + ); + hdd_conn_set_authenticated(adapter, false); + hdd_objmgr_set_peer_mlme_auth_state( + adapter->vdev, + false); + } else { + hdd_debug("sta: " QDF_MAC_ADDR_FMT + "Changing TL state to AUTHENTICATED", + QDF_MAC_ADDR_REF( + roam_info->bssid.bytes)); + qdf_status = + hdd_change_peer_state(adapter, + roam_info->bssid.bytes, + OL_TXRX_PEER_STATE_AUTH, +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + roam_info->roamSynchInProgress +#else + false +#endif + ); + hdd_conn_set_authenticated(adapter, true); + hdd_objmgr_set_peer_mlme_auth_state( + adapter->vdev, + true); + } + + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + /* + * Perform any WMM-related association + * processing + */ + hdd_wmm_assoc(adapter, roam_info, + eCSR_BSS_TYPE_INFRASTRUCTURE); + } + + /* Start the tx queues */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (roam_info->roamSynchInProgress) + hdd_debug("LFR3:netif_tx_wake_all_queues"); +#endif + hdd_debug("Enabling queues"); + hdd_netif_queue_enable(adapter); + } + qdf_mem_free(reqRsnIe); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("STA register with TL failed status: %d [%08X]", + qdf_status, qdf_status); + } +#ifdef WLAN_FEATURE_11W + qdf_mem_zero(&adapter->hdd_stats.hdd_pmf_stats, + sizeof(adapter->hdd_stats.hdd_pmf_stats)); +#endif + if (adapter->device_mode == QDF_STA_MODE) + ucfg_blm_update_bssid_connect_params(hdd_ctx->pdev, + roam_info->bssid, + BLM_AP_CONNECTED); + + policy_mgr_check_n_start_opportunistic_timer(hdd_ctx->psoc); + hdd_debug("check for SAP restart"); + policy_mgr_check_concurrent_intf_and_restart_sap( + hdd_ctx->psoc); + } else { + bool connect_timeout = false; + if (roam_info && roam_info->is_fils_connection && + eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE == roam_result) + qdf_copy_macaddr(&roam_info->bssid, + &sta_ctx->requested_bssid); + if (roam_info) + hdd_err("%s(vdevid-%d): connection failed with " QDF_MAC_ADDR_FMT + " result: %d and Status: %d", dev->name, + adapter->vdev_id, + QDF_MAC_ADDR_REF(roam_info->bssid.bytes), + roam_result, roam_status); + else + hdd_err("%s(vdevid-%d): connection failed with " QDF_MAC_ADDR_FMT + " result: %d and Status: %d", dev->name, + adapter->vdev_id, + QDF_MAC_ADDR_REF(sta_ctx->requested_bssid.bytes), + roam_result, roam_status); + + if ((eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE == roam_result) || + (roam_info && + ((eSIR_SME_JOIN_TIMEOUT_RESULT_CODE == + roam_info->status_code) || + (eSIR_SME_AUTH_TIMEOUT_RESULT_CODE == + roam_info->status_code) || + (eSIR_SME_ASSOC_TIMEOUT_RESULT_CODE == + roam_info->status_code)))) { + uint8_t *ssid; + uint8_t ssid_len; + + if (roam_info && roam_info->pProfile && + roam_info->pProfile->SSIDs.SSIDList) { + ssid_len = + roam_info->pProfile->SSIDs. + SSIDList[0].SSID.length; + ssid = roam_info->pProfile->SSIDs. + SSIDList[0].SSID.ssId; + } else { + ssid_len = sta_ctx->conn_info.ssid.SSID.length; + ssid = sta_ctx->conn_info.ssid.SSID.ssId; + } + wlan_hdd_cfg80211_unlink_bss(adapter, + roam_info ? + roam_info->bssid.bytes : + sta_ctx->requested_bssid.bytes, ssid, ssid_len); + sme_remove_bssid_from_scan_list(mac_handle, + roam_info ? + roam_info->bssid.bytes : + sta_ctx->requested_bssid.bytes); + if (roam_result != + eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE) + connect_timeout = true; + } + + /* + * CR465478: Only send up a connection failure result when CSR + * has completed operation - with a ASSOCIATION_FAILURE status. + */ + if (eCSR_ROAM_ASSOCIATION_FAILURE == roam_status + && !hddDisconInProgress) { + u8 *assoc_rsp = NULL; + u8 *assoc_req = NULL; + + if (roam_info) { + if (roam_info->pbFrames) { + /* Association Request */ + assoc_req = + (u8 *)(roam_info->pbFrames + + roam_info->nBeaconLength); + /* Association Response */ + assoc_rsp = + (u8 *)(roam_info->pbFrames + + roam_info->nBeaconLength + + roam_info->nAssocReqLength); + hdd_debug("assoc_req_len %d assoc resp len %d", + roam_info->nAssocReqLength, + roam_info->nAssocRspLength); + } + hdd_err("send connect failure to nl80211: for bssid " + QDF_MAC_ADDR_FMT + " result: %d and Status: %d reasoncode: %d", + QDF_MAC_ADDR_REF(roam_info->bssid.bytes), + roam_result, roam_status, + roam_info->reasonCode); + sta_ctx->conn_info.assoc_status_code = + roam_info->status_code; + } else { + hdd_err("connect failed: for bssid " + QDF_MAC_ADDR_FMT + " result: %d and status: %d ", + QDF_MAC_ADDR_REF(sta_ctx->requested_bssid.bytes), + roam_result, roam_status); + } + hdd_debug("Invoking packetdump deregistration API"); + wlan_deregister_txrx_packetdump(OL_TXRX_PDEV_ID); + + /* inform association failure event to nl80211 */ + if (eCSR_ROAM_RESULT_ASSOC_FAIL_CON_CHANNEL == + roam_result) { + if (roam_info) + hdd_connect_result(dev, + roam_info->bssid.bytes, + roam_info, assoc_req, + roam_info->nAssocReqLength, + assoc_rsp, + roam_info->nAssocRspLength, + WLAN_STATUS_ASSOC_DENIED_UNSPEC, + GFP_KERNEL, + connect_timeout, + roam_info->status_code); + else + hdd_connect_result(dev, + sta_ctx->requested_bssid.bytes, + NULL, NULL, 0, NULL, 0, + WLAN_STATUS_ASSOC_DENIED_UNSPEC, + GFP_KERNEL, + connect_timeout, + timeout_reason); + } else { + if (roam_info) + hdd_connect_result(dev, + roam_info->bssid.bytes, + roam_info, assoc_req, + roam_info->nAssocReqLength, + assoc_rsp, + roam_info->nAssocRspLength, + roam_info->reasonCode ? + roam_info->reasonCode : + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL, + connect_timeout, + roam_info->status_code); + else + hdd_connect_result(dev, + sta_ctx->requested_bssid.bytes, + NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL, + connect_timeout, + timeout_reason); + } + hdd_clear_roam_profile_ie(adapter); + sme_reset_key(hdd_ctx->mac_handle, + adapter->vdev_id); + } else if ((eCSR_ROAM_CANCELLED == roam_status + && !hddDisconInProgress)) { + hdd_connect_result(dev, + sta_ctx->requested_bssid.bytes, + NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL, + connect_timeout, + timeout_reason); + } + + if (roam_status == eCSR_ROAM_ASSOCIATION_FAILURE || + roam_status == eCSR_ROAM_CANCELLED) { + /* notify connect faiilure on final failure */ + ucfg_tdls_notify_connect_failure(hdd_ctx->psoc); + /* do we need to change the HW mode on final failure */ + policy_mgr_check_n_start_opportunistic_timer( + hdd_ctx->psoc); + + /* + * Enable roaming on other STA iface except this one. + * Firmware dosent support connection on one STA iface + * while roaming on other STA iface + */ + wlan_hdd_enable_roaming(adapter, RSO_CONNECT_START); + } + + /* + * Set connection state to eConnectionState_NotConnected only + * when CSR has completed operation - with a + * ASSOCIATION_FAILURE or eCSR_ROAM_CANCELLED status. + */ + if (((eCSR_ROAM_ASSOCIATION_FAILURE == roam_status) || + (eCSR_ROAM_CANCELLED == roam_status)) + && !hddDisconInProgress) { + hdd_conn_set_connection_state(adapter, + eConnectionState_NotConnected); + } + hdd_wmm_init(adapter); + + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + /* + * if hddDisconInProgress is set and roam_result is + * eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE that mean HDD is + * waiting on disconnect_comp_var so unblock anyone waiting for + * disconnect to complete. + */ + /* + * Also add eCSR_ROAM_ASSOCIATION_FAILURE and + * eCSR_ROAM_CANCELLED here to handle the following scenarios: + * + * 1. Connection is in progress, but completes with failure + * before we check the CSR state. + * 2. Connection is in progress. But the connect command is in + * pending queue and is removed from pending queue as part + * of csr_roam_disconnect. + */ + if ((roam_result == eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE || + roam_status == eCSR_ROAM_ASSOCIATION_FAILURE || + roam_status == eCSR_ROAM_CANCELLED) && + hddDisconInProgress) + complete(&adapter->disconnect_comp_var); + + policy_mgr_check_concurrent_intf_and_restart_sap(hdd_ctx->psoc); + } + + hdd_periodic_sta_stats_start(adapter); + + return QDF_STATUS_SUCCESS; +} + +bool hdd_save_peer(struct hdd_station_ctx *sta_ctx, + struct qdf_mac_addr *peer_mac_addr) +{ + int idx; + struct qdf_mac_addr *mac_addr; + + for (idx = 0; idx < MAX_PEERS; idx++) { + mac_addr = &sta_ctx->conn_info.peer_macaddr[idx]; + if (qdf_is_macaddr_zero(mac_addr)) { + hdd_debug("adding peer: "QDF_MAC_ADDR_FMT" at idx: %d", + QDF_MAC_ADDR_REF(peer_mac_addr->bytes), idx); + qdf_copy_macaddr(mac_addr, peer_mac_addr); + return true; + } + } + + return false; +} + +void hdd_delete_peer(struct hdd_station_ctx *sta_ctx, + struct qdf_mac_addr *peer_mac_addr) +{ + int i; + struct qdf_mac_addr *mac_addr; + + for (i = 0; i < MAX_PEERS; i++) { + mac_addr = &sta_ctx->conn_info.peer_macaddr[i]; + if (qdf_is_macaddr_equal(mac_addr, peer_mac_addr)) { + qdf_zero_macaddr(mac_addr); + return; + } + } +} + +bool hdd_any_valid_peer_present(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + int i; + struct qdf_mac_addr *mac_addr; + + for (i = 0; i < MAX_PEERS; i++) { + mac_addr = &sta_ctx->conn_info.peer_macaddr[i]; + if (!qdf_is_macaddr_zero(mac_addr) && + !qdf_is_macaddr_broadcast(mac_addr)) { + hdd_debug("peer: index: %u " QDF_MAC_ADDR_FMT, i, + QDF_MAC_ADDR_REF(mac_addr->bytes)); + return true; + } + } + + return false; +} + +/** + * roam_remove_ibss_station() - Remove the IBSS peer MAC address in the adapter + * @adapter: pointer to adapter + * @sta_id: station id + * + * Return: + * true if we remove MAX_PEERS or less STA + * false otherwise. + */ +static bool roam_remove_ibss_station(struct hdd_adapter *adapter, + uint8_t *mac_addr) +{ + bool successful = false; + int idx = 0; + uint8_t valid_idx = 0; + uint8_t del_idx = 0; + uint8_t empty_slots = 0; + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + for (idx = 0; idx < MAX_PEERS; idx++) { + if (WLAN_ADDR_EQ(mac_addr, + sta_ctx->conn_info.peer_macaddr[idx].bytes)) { + qdf_zero_macaddr(&sta_ctx->conn_info.peer_macaddr[idx]); + + successful = true; + + /* + * Note the deleted Index, if its 0 we need special + * handling. + */ + del_idx = idx; + + empty_slots++; + } else { + if (hdd_is_valid_mac_address( + sta_ctx->conn_info.peer_macaddr[idx].bytes)) + valid_idx = idx; + else + empty_slots++; + } + } + + if (MAX_PEERS == empty_slots) { + /* Last peer departed, set the IBSS state appropriately */ + hdd_conn_set_connection_state(adapter, + eConnectionState_IbssDisconnected); + hdd_debug("Last IBSS Peer Departed!!!"); + } + /* Find next active sta_id, to have a valid sta trigger for TL. */ + if (successful) { + if (del_idx == 0) { + if (hdd_is_valid_mac_address( + sta_ctx->conn_info.peer_macaddr[valid_idx].bytes)) { + qdf_copy_macaddr(&sta_ctx->conn_info. + peer_macaddr[0], + &sta_ctx->conn_info. + peer_macaddr[valid_idx]); + + qdf_zero_macaddr(&sta_ctx->conn_info. + peer_macaddr[valid_idx]); + } + } + } + return successful; +} + +/** + * roam_ibss_connect_handler() - IBSS connection handler + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * + * We update the status of the IBSS to connected in this function. + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS roam_ibss_connect_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + struct cfg80211_bss *bss; + /* + * Set the internal connection state to show 'IBSS Connected' (IBSS with + * a partner stations). + */ + hdd_conn_set_connection_state(adapter, eConnectionState_IbssConnected); + + /* Save the connection info from CSR... */ + hdd_conn_save_connect_info(adapter, roam_info, eCSR_BSS_TYPE_IBSS); + + /* Send the bssid address to the wext. */ + hdd_send_association_event(adapter->dev, roam_info); + /* add bss_id to cfg80211 data base */ + bss = wlan_hdd_cfg80211_update_bss_db(adapter, roam_info); + if (!bss) { + hdd_err("%s: unable to create IBSS entry", + adapter->dev->name); + return QDF_STATUS_E_FAILURE; + } + cfg80211_put_bss( + WLAN_HDD_GET_CTX(adapter)->wiphy, + bss); + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_roam_mic_error_indication_handler() - MIC error indication handler + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @roam_id: roam id + * @roam_status: roam status + * @roam_result: roam result + * + * This function indicates the Mic failure to the supplicant + * + * Return: None + */ +static void +hdd_roam_mic_error_indication_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + tSirMicFailureInfo *mic_failure_info; + + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) + return; + + mic_failure_info = roam_info->u.pMICFailureInfo; + cfg80211_michael_mic_failure(adapter->dev, + mic_failure_info->taMacAddr, + mic_failure_info->multicast ? + NL80211_KEYTYPE_GROUP : + NL80211_KEYTYPE_PAIRWISE, + mic_failure_info->keyId, + mic_failure_info->TSC, + GFP_KERNEL); +} + +static QDF_STATUS wlan_hdd_set_key_helper(struct hdd_adapter *adapter, + uint32_t *roam_id) +{ + int ret; + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return QDF_STATUS_E_FAILURE; + ret = wlan_cfg80211_crypto_add_key(vdev, + WLAN_CRYPTO_KEY_TYPE_UNICAST, 0, + false); + hdd_objmgr_put_vdev(vdev); + if (ret != 0) { + hdd_err("crypto add key fail, status: %d", ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * roam_roam_connect_status_update_handler() - IBSS connect status update + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * @roam_id: roam id + * @roam_status: roam status + * @roam_result: roam result + * + * The Ibss connection status is updated regularly here in this function. + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS +roam_roam_connect_status_update_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS qdf_status; + + switch (roam_result) { + case eCSR_ROAM_RESULT_IBSS_NEW_PEER: + { + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct station_info *stainfo; + eCsrEncryptionType encr_type = sta_ctx->ibss_enc_key.encType; + + hdd_debug("IBSS New Peer indication from SME " + "with peerMac " QDF_MAC_ADDR_FMT " BSSID: " + QDF_MAC_ADDR_FMT " and stationID= %d", + QDF_MAC_ADDR_REF(roam_info->peerMac.bytes), + QDF_MAC_ADDR_REF(sta_ctx->conn_info.bssid.bytes), + roam_info->staId); + + if (!hdd_save_peer + (WLAN_HDD_GET_STATION_CTX_PTR(adapter), + &roam_info->peerMac)) { + hdd_warn("Max reached: Can't register new IBSS peer"); + break; + } + + if (hdd_is_key_install_required_for_ibss(encr_type)) + roam_info->fAuthRequired = true; + + /* Register the Station with datapath for the new peer. */ + qdf_status = hdd_roam_register_sta(adapter, + roam_info, + roam_info->bss_desc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Cannot register STA for IBSS. qdf_status: %d [%08X]", + qdf_status, qdf_status); + } + sta_ctx->ibss_sta_generation++; + stainfo = qdf_mem_malloc(sizeof(*stainfo)); + if (!stainfo) + return QDF_STATUS_E_NOMEM; + + stainfo->filled = 0; + stainfo->generation = sta_ctx->ibss_sta_generation; + + cfg80211_new_sta(adapter->dev, + (const u8 *)roam_info->peerMac.bytes, + stainfo, GFP_KERNEL); + qdf_mem_free(stainfo); + + if (hdd_is_key_install_required_for_ibss(encr_type)) { + sta_ctx->ibss_enc_key.keyDirection = + eSIR_TX_RX; + qdf_copy_macaddr(&sta_ctx->ibss_enc_key.peerMac, + &roam_info->peerMac); + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return QDF_STATUS_E_FAILURE; + wlan_crypto_update_set_key_peer(vdev, true, 0, + &roam_info->peerMac); + hdd_objmgr_put_vdev(vdev); + + hdd_debug("New peer joined set PTK encType=%d", + encr_type); + qdf_status = wlan_hdd_set_key_helper(adapter, &roam_id); + if (QDF_STATUS_SUCCESS != qdf_status) { + hdd_err("sme set_key fail status: %d", + qdf_status); + return QDF_STATUS_E_FAILURE; + } + } + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + break; + } + + case eCSR_ROAM_RESULT_IBSS_CONNECT: + { + + roam_ibss_connect_handler(adapter, roam_info); + + break; + } + case eCSR_ROAM_RESULT_IBSS_PEER_DEPARTED: + { + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (!roam_remove_ibss_station(adapter, + roam_info->peerMac.bytes)) + hdd_warn("IBSS peer departed by cannot find peer in our registration table with TL"); + + hdd_debug("IBSS Peer Departed from SME " + "with peerMac " QDF_MAC_ADDR_FMT " BSSID: " + QDF_MAC_ADDR_FMT " and stationID= %d", + QDF_MAC_ADDR_REF(roam_info->peerMac.bytes), + QDF_MAC_ADDR_REF(sta_ctx->conn_info.bssid.bytes), + roam_info->staId); + + sta_ctx->ibss_sta_generation++; + + cfg80211_del_sta(adapter->dev, + (const u8 *)&roam_info->peerMac.bytes, + GFP_KERNEL); + break; + } + case eCSR_ROAM_RESULT_IBSS_INACTIVE: + { + hdd_debug("Received eCSR_ROAM_RESULT_IBSS_INACTIVE from SME"); + /* Stop only when we are inactive */ + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + hdd_conn_set_connection_state(adapter, + eConnectionState_NotConnected); + + /* Send the bssid address to the wext. */ + hdd_send_association_event(adapter->dev, roam_info); + break; + } + default: + break; + + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_TDLS +QDF_STATUS hdd_roam_register_tdlssta(struct hdd_adapter *adapter, + const uint8_t *peerMac, uint8_t qos) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct ol_txrx_desc_type txrx_desc = { 0 }; + struct ol_txrx_ops txrx_ops; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + /* + * TDLS sta in BSS should be set as STA type TDLS and STA MAC should + * be peer MAC, here we are working on direct Link + */ + WLAN_ADDR_COPY(txrx_desc.peer_addr.bytes, peerMac); + + /* set the QoS field appropriately .. */ + txrx_desc.is_qos_enabled = qos; + + /* Register the vdev transmit and receive functions */ + qdf_mem_zero(&txrx_ops, sizeof(txrx_ops)); + if (adapter->hdd_ctx->enable_dp_rx_threads) { + txrx_ops.rx.rx = hdd_rx_pkt_thread_enqueue_cbk; + txrx_ops.rx.rx_stack = hdd_rx_packet_cbk; + txrx_ops.rx.rx_flush = hdd_rx_flush_packet_cbk; + txrx_ops.rx.rx_gro_flush = hdd_rx_thread_gro_flush_ind_cbk; + } else { + txrx_ops.rx.rx = hdd_rx_packet_cbk; + txrx_ops.rx.rx_stack = NULL; + txrx_ops.rx.rx_flush = NULL; + } + cdp_vdev_register(soc, adapter->vdev_id, (ol_osif_vdev_handle)adapter, + &txrx_ops); + adapter->tx_fn = txrx_ops.tx.tx; + txrx_ops.rx.stats_rx = hdd_tx_rx_collect_connectivity_stats_info; + + /* Register the Station with TL... */ + qdf_status = cdp_peer_register(soc, OL_TXRX_PDEV_ID, &txrx_desc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("cdp_peer_register() failed Status: %d [0x%08X]", + qdf_status, qdf_status); + return qdf_status; + } + + return qdf_status; +} + +#endif + +#ifdef WLAN_FEATURE_11W + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) + +static void hdd_rx_unprot_disassoc(struct net_device *dev, + const u8 *buf, size_t len) +{ + cfg80211_rx_unprot_mlme_mgmt(dev, buf, len); +} + +static void hdd_rx_unprot_deauth(struct net_device *dev, + const u8 *buf, size_t len) +{ + cfg80211_rx_unprot_mlme_mgmt(dev, buf, len); +} + +#else + +static void hdd_rx_unprot_disassoc(struct net_device *dev, + const u8 *buf, size_t len) +{ + cfg80211_send_unprot_disassoc(dev, buf, len); +} + +static void hdd_rx_unprot_deauth(struct net_device *dev, + const u8 *buf, size_t len) +{ + cfg80211_send_unprot_deauth(dev, buf, len); +} + +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) */ + +/** + * hdd_indicate_unprot_mgmt_frame() - indicate unprotected management frame + * @adapter: pointer to the adapter + * @frame_length: Length of the unprotected frame being passed + * @frame: Pointer to the frame buffer + * @frame_type: 802.11 frame type + * + * This function forwards the unprotected management frame to the supplicant. + * + * Return: nothing + */ +static void +hdd_indicate_unprot_mgmt_frame(struct hdd_adapter *adapter, + uint32_t frame_length, + uint8_t *frame, uint8_t frame_type) +{ + uint8_t type, subtype; + + hdd_debug("Frame Type = %d Frame Length = %d", + frame_type, frame_length); + + if (hdd_validate_adapter(adapter)) + return; + + if (!frame_length) { + hdd_err("Frame Length is Invalid ZERO"); + return; + } + + if (!frame) { + hdd_err("frame is NULL"); + return; + } + + type = WLAN_HDD_GET_TYPE_FRM_FC(frame[0]); + if (type != SIR_MAC_MGMT_FRAME) { + hdd_warn("Unexpected frame type %d", type); + return; + } + + subtype = WLAN_HDD_GET_SUBTYPE_FRM_FC(frame[0]); + switch (subtype) { + case SIR_MAC_MGMT_DISASSOC: + hdd_rx_unprot_disassoc(adapter->dev, frame, frame_length); + adapter->hdd_stats.hdd_pmf_stats.num_unprot_disassoc_rx++; + break; + case SIR_MAC_MGMT_DEAUTH: + hdd_rx_unprot_deauth(adapter->dev, frame, frame_length); + adapter->hdd_stats.hdd_pmf_stats.num_unprot_deauth_rx++; + break; + default: + hdd_warn("Unexpected frame subtype %d", subtype); + break; + } +} +#endif /* WLAN_FEATURE_11W */ + +#ifdef FEATURE_WLAN_ESE +/** + * hdd_indicate_tsm_ie() - send traffic stream metrics ie + * @adapter: pointer to adapter + * @tid: traffic identifier + * @state: state + * @measInterval: measurement interval + * + * This function sends traffic stream metrics IE information to + * the supplicant via wireless event. + * + * Return: none + */ +static void +hdd_indicate_tsm_ie(struct hdd_adapter *adapter, uint8_t tid, + uint8_t state, uint16_t measInterval) +{ + union iwreq_data wrqu; + char buf[IW_CUSTOM_MAX + 1]; + int nBytes = 0; + + if (!adapter) + return; + + /* create the event */ + memset(&wrqu, '\0', sizeof(wrqu)); + memset(buf, '\0', sizeof(buf)); + + hdd_debug("TSM Ind tid(%d) state(%d) MeasInt(%d)", + tid, state, measInterval); + + nBytes = + snprintf(buf, IW_CUSTOM_MAX, "TSMIE=%d:%d:%d", tid, state, + measInterval); + + wrqu.data.pointer = buf; + wrqu.data.length = nBytes; + /* send the event */ + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, buf); +} + +/** + * hdd_indicate_cckm_pre_auth() - send cckm preauth indication + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * + * This function sends cckm preauth indication to the supplicant + * via wireless custom event. + * + * Return: none + */ +static void +hdd_indicate_cckm_pre_auth(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + union iwreq_data wrqu; + char buf[IW_CUSTOM_MAX + 1]; + char *pos = buf; + int nBytes = 0, freeBytes = IW_CUSTOM_MAX; + + if ((!adapter) || (!roam_info)) + return; + + /* create the event */ + memset(&wrqu, '\0', sizeof(wrqu)); + memset(buf, '\0', sizeof(buf)); + + /* Timestamp0 is lower 32 bits and Timestamp1 is upper 32 bits */ + hdd_debug("CCXPREAUTHNOTIFY=" QDF_MAC_ADDR_FMT " %d:%d", + QDF_MAC_ADDR_REF(roam_info->bssid.bytes), + roam_info->timestamp[0], roam_info->timestamp[1]); + + nBytes = snprintf(pos, freeBytes, "CCXPREAUTHNOTIFY="); + pos += nBytes; + freeBytes -= nBytes; + + qdf_mem_copy(pos, roam_info->bssid.bytes, QDF_MAC_ADDR_SIZE); + pos += QDF_MAC_ADDR_SIZE; + freeBytes -= QDF_MAC_ADDR_SIZE; + + nBytes = snprintf(pos, freeBytes, " %u:%u", + roam_info->timestamp[0], roam_info->timestamp[1]); + freeBytes -= nBytes; + + wrqu.data.pointer = buf; + wrqu.data.length = (IW_CUSTOM_MAX - freeBytes); + + /* send the event */ + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, buf); +} + +/** + * hdd_indicate_ese_adj_ap_rep_ind() - send adjacent AP report indication + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * + * Return: none + */ +static void +hdd_indicate_ese_adj_ap_rep_ind(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + union iwreq_data wrqu; + char buf[IW_CUSTOM_MAX + 1]; + int nBytes = 0; + + if ((!adapter) || (!roam_info)) + return; + + /* create the event */ + memset(&wrqu, '\0', sizeof(wrqu)); + memset(buf, '\0', sizeof(buf)); + + hdd_debug("CCXADJAPREP=%u", roam_info->tsmRoamDelay); + + nBytes = + snprintf(buf, IW_CUSTOM_MAX, "CCXADJAPREP=%u", + roam_info->tsmRoamDelay); + + wrqu.data.pointer = buf; + wrqu.data.length = nBytes; + + /* send the event */ + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, buf); +} + +/** + * hdd_indicate_ese_bcn_report_no_results() - beacon report no scan results + * @adapter: pointer to adapter + * @measurementToken: measurement token + * @flag: flag + * @numBss: number of bss + * + * If the measurement is none and no scan results found, + * indicate the supplicant about measurement done. + * + * Return: none + */ +void +hdd_indicate_ese_bcn_report_no_results(const struct hdd_adapter *adapter, + const uint16_t measurementToken, + const bool flag, const uint8_t numBss) +{ + union iwreq_data wrqu; + char buf[IW_CUSTOM_MAX]; + char *pos = buf; + int nBytes = 0, freeBytes = IW_CUSTOM_MAX; + + memset(&wrqu, '\0', sizeof(wrqu)); + memset(buf, '\0', sizeof(buf)); + + hdd_debug("CCXBCNREP=%d %d %d", measurementToken, + flag, numBss); + + nBytes = + snprintf(pos, freeBytes, "CCXBCNREP=%d %d %d", measurementToken, + flag, numBss); + + wrqu.data.pointer = buf; + wrqu.data.length = nBytes; + /* send the event */ + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, buf); +} + +/** + * hdd_indicate_ese_bcn_report_ind() - send beacon report indication + * @adapter: pointer to adapter + * @roam_info: pointer to roam info + * + * If the measurement is none and no scan results found, + * indicate the supplicant about measurement done. + * + * Return: none + */ +static void +hdd_indicate_ese_bcn_report_ind(const struct hdd_adapter *adapter, + const struct csr_roam_info *roam_info) +{ + union iwreq_data wrqu; + char buf[IW_CUSTOM_MAX]; + char *pos = buf; + int nBytes = 0, freeBytes = IW_CUSTOM_MAX; + uint8_t i = 0, len = 0; + uint8_t tot_bcn_ieLen = 0; /* total size of the beacon report data */ + uint8_t lastSent = 0, sendBss = 0; + int bcnRepFieldSize = + sizeof(roam_info->pEseBcnReportRsp->bcnRepBssInfo[0]. + bcnReportFields); + uint8_t ieLenByte = 1; + /* + * CCXBCNREP=meas_tokflagno_of_bsstot_bcn_ie_len = 18 bytes + */ +#define ESEBCNREPHEADER_LEN (18) + + if ((!adapter) || (!roam_info)) + return; + + /* + * Custom event can pass maximum of 256 bytes of data, + * based on the IE len we need to identify how many BSS info can + * be filled in to custom event data. + */ + /* + * meas_tokflagno_of_bsstot_bcn_ie_len bcn_rep_data + * bcn_rep_data will have bcn_rep_fields,ie_len,ie without any spaces + * CCXBCNREP=meas_tokflagno_of_bsstot_bcn_ie_len = 18 bytes + */ + + if ((roam_info->pEseBcnReportRsp->flag >> 1) + && (!roam_info->pEseBcnReportRsp->numBss)) { + hdd_debug("Measurement Done but no scan results"); + /* If the measurement is none and no scan results found, + * indicate the supplicant about measurement done + */ + hdd_indicate_ese_bcn_report_no_results( + adapter, + roam_info->pEseBcnReportRsp-> + measurementToken, + roam_info->pEseBcnReportRsp->flag, + roam_info->pEseBcnReportRsp->numBss); + } else { + while (lastSent < roam_info->pEseBcnReportRsp->numBss) { + memset(&wrqu, '\0', sizeof(wrqu)); + memset(buf, '\0', sizeof(buf)); + tot_bcn_ieLen = 0; + sendBss = 0; + pos = buf; + freeBytes = IW_CUSTOM_MAX; + + for (i = lastSent; + i < roam_info->pEseBcnReportRsp->numBss; i++) { + len = + bcnRepFieldSize + ieLenByte + + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i].ieLen; + if ((len + tot_bcn_ieLen) > + (IW_CUSTOM_MAX - ESEBCNREPHEADER_LEN)) { + break; + } + tot_bcn_ieLen += len; + sendBss++; + hdd_debug("i(%d) sizeof bcnReportFields(%d) IeLength(%d) Length of Ie(%d) totLen(%d)", + i, bcnRepFieldSize, 1, + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i].ieLen, tot_bcn_ieLen); + } + + hdd_debug("Sending %d BSS Info", sendBss); + hdd_debug("CCXBCNREP=%d %d %d %d", + roam_info->pEseBcnReportRsp->measurementToken, + roam_info->pEseBcnReportRsp->flag, sendBss, + tot_bcn_ieLen); + + nBytes = snprintf(pos, freeBytes, "CCXBCNREP=%d %d %d ", + roam_info->pEseBcnReportRsp-> + measurementToken, + roam_info->pEseBcnReportRsp->flag, + sendBss); + pos += nBytes; + freeBytes -= nBytes; + + /* Copy total Beacon report data length */ + qdf_mem_copy(pos, (char *)&tot_bcn_ieLen, + sizeof(tot_bcn_ieLen)); + pos += sizeof(tot_bcn_ieLen); + freeBytes -= sizeof(tot_bcn_ieLen); + + for (i = 0; i < sendBss; i++) { + hdd_debug("ChanNum(%d) Spare(%d) MeasDuration(%d)" + " PhyType(%d) RecvSigPower(%d) ParentTSF(%u)" + " TargetTSF[0](%u) TargetTSF[1](%u) BeaconInterval(%u)" + " CapabilityInfo(%d) BSSID(%02X:%02X:%02X:%02X:%02X:%02X)", + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + ChanNum, + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + Spare, + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + MeasDuration, + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + PhyType, + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + RecvSigPower, + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + ParentTsf, + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + TargetTsf[0], + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + TargetTsf[1], + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + BcnInterval, + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + CapabilityInfo, + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + Bssid[0], + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + Bssid[1], + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + Bssid[2], + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + Bssid[3], + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + Bssid[4], + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent].bcnReportFields. + Bssid[5]); + + /* bcn report fields are copied */ + len = + sizeof(roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + + lastSent]. + bcnReportFields); + qdf_mem_copy(pos, + (char *)&roam_info-> + pEseBcnReportRsp->bcnRepBssInfo[i + + lastSent]. + bcnReportFields, len); + pos += len; + freeBytes -= len; + + /* Add 1 byte of ie len */ + len = + roam_info->pEseBcnReportRsp-> + bcnRepBssInfo[i + lastSent].ieLen; + qdf_mem_copy(pos, (char *)&len, sizeof(len)); + pos += sizeof(len); + freeBytes -= sizeof(len); + + /* copy IE from scan results */ + qdf_mem_copy(pos, + (char *)roam_info-> + pEseBcnReportRsp->bcnRepBssInfo[i + + lastSent]. + pBuf, len); + pos += len; + freeBytes -= len; + } + + wrqu.data.pointer = buf; + wrqu.data.length = IW_CUSTOM_MAX - freeBytes; + + /* send the event */ + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, + buf); + lastSent += sendBss; + } + } +} + +#endif /* FEATURE_WLAN_ESE */ + +/** + * hdd_is_8021x_sha256_auth_type() - check authentication type to 8021x_sha256 + * @sta_ctx: Station Context + * + * API to check if the connection authentication type is 8021x_sha256. + * + * Return: bool + */ +#ifdef WLAN_FEATURE_11W +static inline bool +hdd_is_8021x_sha256_auth_type(struct hdd_station_ctx *sta_ctx) +{ + return eCSR_AUTH_TYPE_RSN_8021X_SHA256 == + sta_ctx->conn_info.auth_type; +} +#else +static inline bool +hdd_is_8021x_sha256_auth_type(struct hdd_station_ctx *sta_ctx) +{ + return false; +} + +#endif + +/* + * hdd_roam_channel_switch_handler() - hdd channel switch handler + * @adapter: Pointer to adapter context + * @roam_info: Pointer to roam info + * + * Return: None + */ +static void hdd_roam_channel_switch_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + struct hdd_chan_change_params chan_change; + struct cfg80211_bss *bss; + struct net_device *dev = adapter->dev; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); + + /* Enable Roaming on STA interface which was disabled before CSA */ + if (adapter->device_mode == QDF_STA_MODE) + sme_start_roaming(mac_handle, adapter->vdev_id, + REASON_DRIVER_ENABLED, + RSO_CHANNEL_SWITCH); + + chan_change.chan_freq = roam_info->chan_info.mhz; + chan_change.chan_params.ch_width = + roam_info->chan_info.ch_width; + chan_change.chan_params.sec_ch_offset = + roam_info->chan_info.sec_ch_offset; + chan_change.chan_params.mhz_freq_seg0 = + roam_info->chan_info.band_center_freq1; + chan_change.chan_params.mhz_freq_seg1 = + roam_info->chan_info.band_center_freq2; + + bss = wlan_hdd_cfg80211_update_bss_db(adapter, roam_info); + if (!bss) + hdd_err("%s: unable to create BSS entry", adapter->dev->name); + else + cfg80211_put_bss(wiphy, bss); + + status = hdd_chan_change_notify(adapter, adapter->dev, chan_change, + roam_info->mode == SIR_SME_PHY_MODE_LEGACY); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("channel change notification failed"); + + status = policy_mgr_set_hw_mode_on_channel_switch(hdd_ctx->psoc, + adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("set hw mode change not done"); + + policy_mgr_check_concurrent_intf_and_restart_sap(hdd_ctx->psoc); +} + +/** + * hdd_sme_roam_callback() - hdd sme roam callback + * @context: pointer to adapter context + * @roam_info: pointer to roam info + * @roam_id: roam id + * @roam_status: roam status + * @roam_result: roam result + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS +hdd_sme_roam_callback(void *context, struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, eCsrRoamResult roam_result) +{ + QDF_STATUS qdf_ret_status = QDF_STATUS_SUCCESS; + struct hdd_adapter *adapter = context; + struct hdd_station_ctx *sta_ctx = NULL; + struct cfg80211_bss *bss_status; + struct hdd_context *hdd_ctx; + + hdd_debug("CSR Callback: status=%d result=%d roamID=%d", + roam_status, roam_result, roam_id); + + /* Sanity check */ + if ((!adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return QDF_STATUS_E_FAILURE; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + MTRACE(qdf_trace(QDF_MODULE_ID_HDD, TRACE_CODE_HDD_RX_SME_MSG, + adapter->vdev_id, roam_status)); + + switch (roam_status) { + /* + * We did pre-auth,then we attempted a 11r or ese reassoc. + * reassoc failed due to failure, timeout, reject from ap + * in any case tell the OS, our carrier is off and mark + * interface down. + */ + case eCSR_ROAM_FT_REASSOC_FAILED: + hdd_err("Reassoc Failed with roam_status: %d roam_result: %d SessionID: %d", + roam_status, roam_result, adapter->vdev_id); + qdf_ret_status = + hdd_dis_connect_handler(adapter, roam_info, roam_id, + roam_status, roam_result); + sta_ctx->ft_carrier_on = false; + sta_ctx->hdd_reassoc_scenario = false; + hdd_debug("hdd_reassoc_scenario set to: %d, ReAssoc Failed, session: %d", + sta_ctx->hdd_reassoc_scenario, adapter->vdev_id); + break; + + case eCSR_ROAM_FT_START: + /* + * When we roam for ESE and 11r, we dont want the OS to be + * informed that the link is down. So mark the link ready for + * ft_start. After this the eCSR_ROAM_SHOULD_ROAM will + * be received. Where in we will not mark the link down + * Also we want to stop tx at this point when we will be + * doing disassoc at this time. This saves 30-60 msec + * after reassoc. + */ + hdd_debug("Disabling queues"); + hdd_debug("Roam Synch Ind: NAPI Serialize ON"); + hdd_napi_serialize(1); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + sta_ctx->ft_carrier_on = true; + sta_ctx->hdd_reassoc_scenario = true; + hdd_debug("hdd_reassoc_scenario set to: %d, due to eCSR_ROAM_FT_START, session: %d", + sta_ctx->hdd_reassoc_scenario, adapter->vdev_id); + break; + case eCSR_ROAM_NAPI_OFF: + hdd_debug("After Roam Synch Comp: NAPI Serialize OFF"); + hdd_napi_serialize(0); + if (roam_result == eCSR_ROAM_RESULT_FAILURE) { + adapter->roam_ho_fail = true; + hdd_set_roaming_in_progress(false); + } else { + adapter->roam_ho_fail = false; + } + complete(&adapter->roaming_comp_var); + break; + case eCSR_ROAM_SYNCH_COMPLETE: + hdd_debug("LFR3: Roam synch complete"); + hdd_set_roaming_in_progress(false); + break; + case eCSR_ROAM_SHOULD_ROAM: + /* notify apps that we can't pass traffic anymore */ + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + if (sta_ctx->ft_carrier_on == false) { + wlan_hdd_netif_queue_control(adapter, + WLAN_NETIF_CARRIER_OFF, + WLAN_CONTROL_PATH); + } + break; + case eCSR_ROAM_LOSTLINK: + if (roam_result == eCSR_ROAM_RESULT_LOSTLINK) { + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + break; + } + case eCSR_ROAM_DISASSOCIATED: + { + hdd_debug("****eCSR_ROAM_DISASSOCIATED****"); + hdd_napi_serialize(0); + hdd_set_connection_in_progress(false); + hdd_set_roaming_in_progress(false); + adapter->roam_ho_fail = false; + complete(&adapter->roaming_comp_var); + + /* Call to clear any MC Addr List filter applied after + * successful connection. + */ + hdd_disable_and_flush_mc_addr_list(adapter, + pmo_peer_disconnect); + qdf_ret_status = + hdd_dis_connect_handler(adapter, roam_info, roam_id, + roam_status, roam_result); + } + break; + case eCSR_ROAM_IBSS_LEAVE: + hdd_debug("****eCSR_ROAM_IBSS_LEAVE****"); + qdf_ret_status = + hdd_dis_connect_handler(adapter, roam_info, roam_id, + roam_status, roam_result); + break; + case eCSR_ROAM_ASSOCIATION_COMPLETION: + hdd_debug("****eCSR_ROAM_ASSOCIATION_COMPLETION****"); + /* + * To Do - address probable memory leak with WEP encryption upon + * successful association. + */ + if (eCSR_ROAM_RESULT_ASSOCIATED != roam_result) { + /* Clear saved connection information in HDD */ + hdd_conn_remove_connect_info( + WLAN_HDD_GET_STATION_CTX_PTR(adapter)); + } + qdf_ret_status = + hdd_association_completion_handler(adapter, roam_info, + roam_id, roam_status, + roam_result); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (roam_info) + roam_info->roamSynchInProgress = false; +#endif + break; + case eCSR_ROAM_CANCELLED: + hdd_debug("****eCSR_ROAM_CANCELLED****"); + /* fallthrough */ + case eCSR_ROAM_ASSOCIATION_FAILURE: + qdf_ret_status = hdd_association_completion_handler(adapter, + roam_info, + roam_id, + roam_status, + roam_result); + break; + case eCSR_ROAM_IBSS_IND: + hdd_roam_ibss_indication_handler(adapter, roam_info, roam_id, + roam_status, roam_result); + break; + + case eCSR_ROAM_CONNECT_STATUS_UPDATE: + qdf_ret_status = + roam_roam_connect_status_update_handler(adapter, + roam_info, + roam_id, + roam_status, + roam_result); + break; + + case eCSR_ROAM_MIC_ERROR_IND: + hdd_roam_mic_error_indication_handler(adapter, roam_info); + break; + + case eCSR_ROAM_SET_KEY_COMPLETE: + { + qdf_ret_status = + hdd_roam_set_key_complete_handler(adapter, roam_info, + roam_id, roam_status, + roam_result); + if (eCSR_ROAM_RESULT_AUTHENTICATED == roam_result) { + sta_ctx->hdd_reassoc_scenario = false; + hdd_debug("hdd_reassoc_scenario set to: %d, set key complete, session: %d", + sta_ctx->hdd_reassoc_scenario, + adapter->vdev_id); + } + } +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (roam_info) + roam_info->roamSynchInProgress = false; +#endif + break; + + case eCSR_ROAM_FT_RESPONSE: + hdd_send_ft_event(adapter); + break; + + case eCSR_ROAM_PMK_NOTIFY: + if (eCSR_AUTH_TYPE_RSN == sta_ctx->conn_info.auth_type + || hdd_is_8021x_sha256_auth_type(sta_ctx)) { + /* notify the supplicant of a new candidate */ + qdf_ret_status = + wlan_hdd_cfg80211_pmksa_candidate_notify( + adapter, roam_info, 1, false); + } + break; + +#ifdef FEATURE_WLAN_LFR_METRICS + case eCSR_ROAM_PREAUTH_INIT_NOTIFY: + /* This event is to notify pre-auth initiation */ + if (QDF_STATUS_SUCCESS != + wlan_hdd_cfg80211_roam_metrics_preauth(adapter, + roam_info)) { + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; + case eCSR_ROAM_PREAUTH_STATUS_SUCCESS: + /* + * This event will notify pre-auth completion in case of success + */ + if (QDF_STATUS_SUCCESS != + wlan_hdd_cfg80211_roam_metrics_preauth_status(adapter, + roam_info, 1)) { + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; + case eCSR_ROAM_PREAUTH_STATUS_FAILURE: + /* + * This event will notify pre-auth completion incase of failure. + */ + if (QDF_STATUS_SUCCESS != + wlan_hdd_cfg80211_roam_metrics_preauth_status(adapter, + roam_info, 0)) { + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; + case eCSR_ROAM_HANDOVER_SUCCESS: + /* This event is to notify handover success. + * It will be only invoked on success + */ + if (QDF_STATUS_SUCCESS != + wlan_hdd_cfg80211_roam_metrics_handover(adapter, + roam_info)) { + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; +#endif +#ifdef WLAN_FEATURE_11W + case eCSR_ROAM_UNPROT_MGMT_FRAME_IND: + if (roam_info) + hdd_indicate_unprot_mgmt_frame(adapter, + roam_info->nFrameLength, + roam_info->pbFrames, + roam_info->frameType); + break; +#endif +#ifdef FEATURE_WLAN_ESE + case eCSR_ROAM_TSM_IE_IND: + if (roam_info) + hdd_indicate_tsm_ie(adapter, roam_info->tsm_ie.tsid, + roam_info->tsm_ie.state, + roam_info->tsm_ie.msmt_interval); + break; + + case eCSR_ROAM_CCKM_PREAUTH_NOTIFY: + { + if (eCSR_AUTH_TYPE_CCKM_WPA == + sta_ctx->conn_info.auth_type + || eCSR_AUTH_TYPE_CCKM_RSN == + sta_ctx->conn_info.auth_type) { + hdd_indicate_cckm_pre_auth(adapter, roam_info); + } + break; + } + + case eCSR_ROAM_ESE_ADJ_AP_REPORT_IND: + { + hdd_indicate_ese_adj_ap_rep_ind(adapter, roam_info); + break; + } + + case eCSR_ROAM_ESE_BCN_REPORT_IND: + { + hdd_indicate_ese_bcn_report_ind(adapter, roam_info); + break; + } +#endif /* FEATURE_WLAN_ESE */ + case eCSR_ROAM_STA_CHANNEL_SWITCH: + hdd_roam_channel_switch_handler(adapter, roam_info); + break; + + case eCSR_ROAM_UPDATE_SCAN_RESULT: + if ((roam_info) && (roam_info->bss_desc)) { + bss_status = wlan_hdd_inform_bss_frame(adapter, + roam_info->bss_desc); + if (!bss_status) + hdd_debug("UPDATE_SCAN_RESULT returned NULL"); + else + cfg80211_put_bss( +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)) || defined(WITH_BACKPORTS) + (WLAN_HDD_GET_CTX(adapter))->wiphy, +#endif + bss_status); + } + break; + case eCSR_ROAM_NDP_STATUS_UPDATE: + hdd_ndp_event_handler(adapter, roam_info, roam_id, roam_status, + roam_result); + break; + case eCSR_ROAM_START: + hdd_debug("Process ROAM_START from firmware"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + hdd_set_connection_in_progress(true); + hdd_set_roaming_in_progress(true); + policy_mgr_restart_opportunistic_timer(hdd_ctx->psoc, true); + break; + case eCSR_ROAM_ABORT: + hdd_debug("Firmware aborted roaming operation, previous connection is still valid"); + hdd_napi_serialize(0); + wlan_hdd_netif_queue_control(adapter, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + hdd_set_connection_in_progress(false); + hdd_set_roaming_in_progress(false); + adapter->roam_ho_fail = false; + sta_ctx->ft_carrier_on = false; + complete(&adapter->roaming_comp_var); + break; + + case eCSR_ROAM_SAE_COMPUTE: + if (roam_info) + wlan_hdd_sae_callback(adapter, roam_info); + break; + + case eCSR_ROAM_ROAMING_START: + /* + * For LFR2, Handle roaming start to remove disassociated + * session + */ + if (roaming_offload_enabled(hdd_ctx)) + break; + if (roam_result == eCSR_ROAM_RESULT_NOT_ASSOCIATED) { + hdd_debug("Decrement session of disassociated AP device_mode %d sessionId %d", + adapter->device_mode, + adapter->vdev_id); + policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, + adapter->device_mode, + adapter->vdev_id); + } + break; + + case eCSR_ROAM_FIPS_PMK_REQUEST: + /* notify the supplicant of a new candidate */ + qdf_ret_status = wlan_hdd_cfg80211_pmksa_candidate_notify( + adapter, roam_info, 1, false); + break; + + default: + break; + } + return qdf_ret_status; +} + +#ifdef WLAN_FEATURE_FILS_SK +/** + * hdd_translate_fils_rsn_to_csr_auth() - Translate FILS RSN to CSR auth type + * @auth_suite: auth suite + * @auth_type: pointer to enum csr_akm_type + * + * Return: None + */ +static void hdd_translate_fils_rsn_to_csr_auth(int8_t auth_suite[4], + enum csr_akm_type *auth_type) +{ + if (!memcmp(auth_suite, ccp_rsn_oui_0e, 4)) + *auth_type = eCSR_AUTH_TYPE_FILS_SHA256; + else if (!memcmp(auth_suite, ccp_rsn_oui_0f, 4)) + *auth_type = eCSR_AUTH_TYPE_FILS_SHA384; + else if (!memcmp(auth_suite, ccp_rsn_oui_10, 4)) + *auth_type = eCSR_AUTH_TYPE_FT_FILS_SHA256; + else if (!memcmp(auth_suite, ccp_rsn_oui_11, 4)) + *auth_type = eCSR_AUTH_TYPE_FT_FILS_SHA384; +} +#else +static inline void hdd_translate_fils_rsn_to_csr_auth(int8_t auth_suite[4], + enum csr_akm_type *auth_type) +{ +} +#endif + +#ifdef WLAN_FEATURE_SAE +/** + * hdd_translate_sae_rsn_to_csr_auth() - Translate SAE RSN to CSR auth type + * @auth_suite: auth suite + * @auth_type: pointer to enum csr_akm_type + * + * Return: None + */ +static void hdd_translate_sae_rsn_to_csr_auth(int8_t auth_suite[4], + enum csr_akm_type *auth_type) +{ + if (qdf_mem_cmp(auth_suite, ccp_rsn_oui_80, 4) == 0) + *auth_type = eCSR_AUTH_TYPE_SAE; + else if (qdf_mem_cmp(auth_suite, ccp_rsn_oui_90, 4) == 0) + *auth_type = eCSR_AUTH_TYPE_FT_SAE; + +} +#else +static inline void hdd_translate_sae_rsn_to_csr_auth(int8_t auth_suite[4], + enum csr_akm_type *auth_type) +{ +} +#endif + +/** + * hdd_translate_rsn_to_csr_auth_type() - Translate RSN to CSR auth type + * @auth_suite: auth suite + * + * Return: enum csr_akm_type enumeration + */ +enum csr_akm_type hdd_translate_rsn_to_csr_auth_type(uint8_t auth_suite[4]) +{ + enum csr_akm_type auth_type = eCSR_AUTH_TYPE_UNKNOWN; + /* is the auth type supported? */ + if (memcmp(auth_suite, ccp_rsn_oui01, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_RSN; + } else if (memcmp(auth_suite, ccp_rsn_oui02, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_RSN_PSK; + } else if (memcmp(auth_suite, ccp_rsn_oui04, 4) == 0) { + /* Check for 11r FT Authentication with PSK */ + auth_type = eCSR_AUTH_TYPE_FT_RSN_PSK; + } else if (memcmp(auth_suite, ccp_rsn_oui03, 4) == 0) { + /* Check for 11R FT Authentication with 802.1X */ + auth_type = eCSR_AUTH_TYPE_FT_RSN; + } else +#ifdef FEATURE_WLAN_ESE + if (memcmp(auth_suite, ccp_rsn_oui06, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_CCKM_RSN; + } else +#endif /* FEATURE_WLAN_ESE */ +#ifdef WLAN_FEATURE_11W + if (memcmp(auth_suite, ccp_rsn_oui07, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_RSN_PSK_SHA256; + } else if (memcmp(auth_suite, ccp_rsn_oui08, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_RSN_8021X_SHA256; + } else if (memcmp(auth_suite, ccp_rsn_oui_18, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_OWE; + } else +#endif + if (memcmp(auth_suite, ccp_rsn_oui_12, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_DPP_RSN; + } else if (memcmp(auth_suite, ccp_rsn_oui_0b, 4) == 0) { + /* Check for Suite B EAP 256 */ + auth_type = eCSR_AUTH_TYPE_SUITEB_EAP_SHA256; + } else if (memcmp(auth_suite, ccp_rsn_oui_0c, 4) == 0) { + /* Check for Suite B EAP 384 */ + auth_type = eCSR_AUTH_TYPE_SUITEB_EAP_SHA384; + } else if (memcmp(auth_suite, ccp_rsn_oui_0d, 4) == 0) { + /* Check for FT Suite B EAP 384 */ + auth_type = eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384; + } else if (memcmp(auth_suite, ccp_rsn_oui_13, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_OSEN; + } else { + hdd_translate_fils_rsn_to_csr_auth(auth_suite, &auth_type); + hdd_translate_sae_rsn_to_csr_auth(auth_suite, &auth_type); + } + + return auth_type; +} + +/** + * hdd_translate_wpa_to_csr_auth_type() - Translate WPA to CSR auth type + * @auth_suite: auth suite + * + * Return: enum csr_akm_type enumeration + */ +enum csr_akm_type hdd_translate_wpa_to_csr_auth_type(uint8_t auth_suite[4]) +{ + enum csr_akm_type auth_type = eCSR_AUTH_TYPE_UNKNOWN; + /* is the auth type supported? */ + if (memcmp(auth_suite, ccp_wpa_oui01, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_WPA; + } else if (memcmp(auth_suite, ccp_wpa_oui02, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_WPA_PSK; + } else +#ifdef FEATURE_WLAN_ESE + if (memcmp(auth_suite, ccp_wpa_oui06, 4) == 0) { + auth_type = eCSR_AUTH_TYPE_CCKM_WPA; + } else +#endif /* FEATURE_WLAN_ESE */ + { + hdd_translate_fils_rsn_to_csr_auth(auth_suite, &auth_type); + } + + return auth_type; +} + +/** + * hdd_translate_rsn_to_csr_encryption_type() - + * Translate RSN to CSR encryption type + * @cipher_suite: cipher suite + * + * Return: eCsrEncryptionType enumeration + */ +eCsrEncryptionType +hdd_translate_rsn_to_csr_encryption_type(uint8_t cipher_suite[4]) +{ + eCsrEncryptionType cipher_type; + + if (memcmp(cipher_suite, ccp_rsn_oui04, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_AES; + else if (memcmp(cipher_suite, ccp_rsn_oui09, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_AES_GCMP; + else if (memcmp(cipher_suite, ccp_rsn_oui0a, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_AES_GCMP_256; + else if (memcmp(cipher_suite, ccp_rsn_oui02, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_TKIP; + else if (memcmp(cipher_suite, ccp_rsn_oui00, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_NONE; + else if (memcmp(cipher_suite, ccp_rsn_oui01, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_WEP40_STATICKEY; + else if (memcmp(cipher_suite, ccp_rsn_oui05, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_WEP104_STATICKEY; + else + cipher_type = eCSR_ENCRYPT_TYPE_FAILED; + + return cipher_type; +} + +/** + * hdd_translate_wpa_to_csr_encryption_type() - + * Translate WPA to CSR encryption type + * @cipher_suite: cipher suite + * + * Return: eCsrEncryptionType enumeration + */ +eCsrEncryptionType +hdd_translate_wpa_to_csr_encryption_type(uint8_t cipher_suite[4]) +{ + eCsrEncryptionType cipher_type; + + if (memcmp(cipher_suite, ccp_wpa_oui04, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_AES; + else if (memcmp(cipher_suite, ccp_wpa_oui02, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_TKIP; + else if (memcmp(cipher_suite, ccp_wpa_oui00, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_NONE; + else if (memcmp(cipher_suite, ccp_wpa_oui01, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_WEP40_STATICKEY; + else if (memcmp(cipher_suite, ccp_wpa_oui05, 4) == 0) + cipher_type = eCSR_ENCRYPT_TYPE_WEP104_STATICKEY; + else + cipher_type = eCSR_ENCRYPT_TYPE_FAILED; + + return cipher_type; +} + +#ifdef WLAN_FEATURE_FILS_SK +bool hdd_is_fils_connection(struct hdd_adapter *adapter) +{ + struct csr_roam_profile *roam_profile; + + roam_profile = hdd_roam_profile(adapter); + if (roam_profile->fils_con_info) + return roam_profile->fils_con_info->is_fils_connection; + + return false; +} +#else +bool hdd_is_fils_connection(struct hdd_adapter *adapter) +{ + return false; +} +#endif + +/** + * hdd_process_genie() - process gen ie + * @adapter: pointer to adapter + * @bssid: pointer to mac address + * @encrypt_type: pointer to encryption type + * @mc_encrypt_type: pointer to multicast encryption type + * @auth_type: pointer to auth type + * + * Return: 0 on success, error number otherwise + */ +#ifdef WLAN_FEATURE_11W +static int32_t hdd_process_genie(struct hdd_adapter *adapter, + u8 *bssid, + eCsrEncryptionType *encrypt_type, + eCsrEncryptionType *mc_encrypt_type, + enum csr_akm_type *auth_type, + uint8_t *mfp_required, uint8_t *mfp_capable, + uint16_t gen_ie_len, uint8_t *gen_ie) +#else +static int32_t hdd_process_genie(struct hdd_adapter *adapter, + u8 *bssid, + eCsrEncryptionType *encrypt_type, + eCsrEncryptionType *mc_encrypt_type, + enum csr_akm_type *auth_type, + uint16_t gen_ie_len, uint8_t *gen_ie) +#endif +{ + mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); + tDot11fIERSN dot11_rsn_ie = {0}; + tDot11fIEWPA dot11_wpa_ie = {0}; + uint8_t *rsn_ie; + uint16_t rsn_ie_len; + uint32_t parse_status; + uint16_t rsn_cap = 0; + + /* + * Clear struct of tDot11fIERSN and tDot11fIEWPA specifically + * setting present flag to 0. + */ + memset(&dot11_wpa_ie, 0, sizeof(tDot11fIEWPA)); + memset(&dot11_rsn_ie, 0, sizeof(tDot11fIERSN)); + + /* Type check */ + if (gen_ie[0] == DOT11F_EID_RSN) { + /* Validity checks */ + if ((gen_ie_len < DOT11F_IE_RSN_MIN_LEN) || + (gen_ie_len > DOT11F_IE_RSN_MAX_LEN)) { + hdd_err("Invalid DOT11F RSN IE length: %d", + gen_ie_len); + return -EINVAL; + } + /* Skip past the EID byte and length byte */ + rsn_ie = gen_ie + 2; + rsn_ie_len = gen_ie_len - 2; + /* Unpack the RSN IE */ + parse_status = sme_unpack_rsn_ie(mac_handle, rsn_ie, rsn_ie_len, + &dot11_rsn_ie, false); + if (!DOT11F_SUCCEEDED(parse_status)) { + hdd_err("Invalid RSN IE: parse status %d", + parse_status); + return -EINVAL; + } + + /* dot11_rsn_ie.akm_suite_cnt */ + /* Just translate the FIRST one */ + *auth_type = + hdd_translate_rsn_to_csr_auth_type( + dot11_rsn_ie.akm_suite[0]); + /* dot11_rsn_ie.pwise_cipher_suite_count */ + *encrypt_type = + hdd_translate_rsn_to_csr_encryption_type( + dot11_rsn_ie.pwise_cipher_suites[0]); + /* dot11_rsn_ie.gp_cipher_suite_count */ + *mc_encrypt_type = + hdd_translate_rsn_to_csr_encryption_type( + dot11_rsn_ie.gp_cipher_suite); +#ifdef WLAN_FEATURE_11W + *mfp_required = (dot11_rsn_ie.RSN_Cap[0] >> 6) & 0x1; + *mfp_capable = csr_is_mfpc_capable(&dot11_rsn_ie); +#endif + qdf_mem_copy(&rsn_cap, dot11_rsn_ie.RSN_Cap, sizeof(rsn_cap)); + wlan_crypto_set_vdev_param(adapter->vdev, + WLAN_CRYPTO_PARAM_RSN_CAP, rsn_cap); + } else if (gen_ie[0] == DOT11F_EID_WPA) { + /* Validity checks */ + if ((gen_ie_len < DOT11F_IE_WPA_MIN_LEN) || + (gen_ie_len > DOT11F_IE_WPA_MAX_LEN)) { + hdd_err("Invalid DOT11F WPA IE length: %d", + gen_ie_len); + return -EINVAL; + } + /* Skip past the EID and length byte - and four byte WiFi OUI */ + rsn_ie = gen_ie + 2 + 4; + rsn_ie_len = gen_ie_len - (2 + 4); + /* Unpack the WPA IE */ + parse_status = dot11f_unpack_ie_wpa(MAC_CONTEXT(mac_handle), + rsn_ie, rsn_ie_len, + &dot11_wpa_ie, false); + if (!DOT11F_SUCCEEDED(parse_status)) { + hdd_err("Invalid WPA IE: parse status %d", + parse_status); + return -EINVAL; + } + + /* dot11_wpa_ie.auth_suite_count */ + /* Just translate the FIRST one */ + *auth_type = + hdd_translate_wpa_to_csr_auth_type( + dot11_wpa_ie.auth_suites[0]); + /* dot11_wpa_ie.unicast_cipher_count */ + *encrypt_type = + hdd_translate_wpa_to_csr_encryption_type( + dot11_wpa_ie.unicast_ciphers[0]); + /* dot11_wpa_ie.unicast_cipher_count */ + *mc_encrypt_type = + hdd_translate_wpa_to_csr_encryption_type( + dot11_wpa_ie.multicast_cipher); + } else { + hdd_warn("gen_ie[0]: %d", gen_ie[0]); + return -EINVAL; + } + return 0; +} + +#ifdef WLAN_FEATURE_11W +/** + * hdd_set_def_mfp_cap: Set default value for MFPCapable + * @profile: Source profile + * + * Return: None + */ +static void hdd_set_def_mfp_cap( + struct csr_roam_profile *roam_profile) +{ + roam_profile->MFPCapable = roam_profile->MFPEnabled; +} +#else +static void hdd_set_def_mfp_cap( + struct csr_roam_profile *roam_profile) +{ +} +#endif + +/** + * hdd_set_def_rsne_override() - set default encryption type and auth type + * in profile. + * @roam_profile: pointer to adapter + * @auth_type: pointer to auth type + * + * Set default value of encryption type and auth type in profile to + * search the AP using filter, as in force_rsne_override the RSNIE can be + * currupt and we might not get the proper encryption type and auth type + * while parsing the RSNIE. + * + * Return: void + */ +static void hdd_set_def_rsne_override( + struct csr_roam_profile *roam_profile, enum csr_akm_type *auth_type) +{ + hdd_debug("Set def values in roam profile"); + hdd_set_def_mfp_cap(roam_profile); + roam_profile->EncryptionType.numEntries = 2; + roam_profile->mcEncryptionType.numEntries = 2; + /* Use the cipher type in the RSN IE */ + roam_profile->EncryptionType.encryptionType[0] = eCSR_ENCRYPT_TYPE_AES; + roam_profile->EncryptionType.encryptionType[1] = eCSR_ENCRYPT_TYPE_TKIP; + roam_profile->mcEncryptionType.encryptionType[0] = + eCSR_ENCRYPT_TYPE_AES; + roam_profile->mcEncryptionType.encryptionType[1] = + eCSR_ENCRYPT_TYPE_TKIP; + *auth_type = eCSR_AUTH_TYPE_RSN_PSK; +} + +#ifdef WLAN_FEATURE_11W +/** + * hdd_update_values_mfp_cap: Update values for MFPCapable,MFPEnabled + * @profile: Source profile + * + * Return: None + */ +static void hdd_update_values_mfp_cap( + struct csr_roam_profile *roam_profile, + uint8_t mfp_required, uint8_t mfp_capable ) +{ + hdd_debug("mfp_required = %d, mfp_capable = %d", + mfp_required, mfp_capable); + roam_profile->MFPRequired = mfp_required; + roam_profile->MFPCapable = mfp_capable; +} +#else +static void hdd_update_values_mfp_cap( + struct csr_roam_profile *roam_profile, + uint8_t mfp_required, uint8_t mfp_capable ) +{ +} +#endif + +#ifdef WLAN_FEATURE_11W +/** + * hdd_set_mfp_enable: Set value for MFPEnabled + * @profile: Source profile + * + * Return: None + */ +static void hdd_set_mfp_enable(struct csr_roam_profile *roam_profile) +{ + /* + * Reset MFPEnabled if testmode RSNE passed doesn't have MFPR + * or MFPC bit set + */ + if (roam_profile->MFPEnabled && + !(roam_profile->MFPRequired || + roam_profile->MFPCapable)) { + hdd_debug("Reset MFPEnabled"); + roam_profile->MFPEnabled = 0; + } +} +#else +static void hdd_set_mfp_enable(struct csr_roam_profile *roam_profile) +{ +} +#endif + +#ifdef WLAN_FEATURE_11W +static uint32_t wlan_hdd_process_genie(struct hdd_adapter *adapter, + u8 *bssid, + eCsrEncryptionType *encrypt_type, + eCsrEncryptionType *mc_encrypt_type, + enum csr_akm_type *auth_type, + uint8_t *mfp_required, + uint8_t *mfp_capable, + uint16_t gen_ie_len, uint8_t *gen_ie) +{ + uint32_t status; + + status = hdd_process_genie(adapter, bssid, + encrypt_type, mc_encrypt_type, + auth_type, mfp_required, mfp_capable, + gen_ie_len, gen_ie); + + return status; +} +#else +static uint32_t wlan_hdd_process_genie(struct hdd_adapter *adapter, + u8 *bssid, + eCsrEncryptionType *encrypt_type, + eCsrEncryptionType *mc_encrypt_type, + enum csr_akm_type *auth_type, + uint8_t *mfp_required, + uint8_t *mfp_capable, + uint16_t gen_ie_len, uint8_t *gen_ie) +{ + uint32_t status; + + status = hdd_process_genie(adapter, bssid, + encrypt_type, mc_encrypt_type, + auth_type, gen_ie_len, gen_ie); + + return status; +} +#endif + +/** + * hdd_set_genie_to_csr() - set genie to csr + * @adapter: pointer to adapter + * @rsn_auth_type: pointer to auth type + * + * Return: 0 on success, error number otherwise + */ +int hdd_set_genie_to_csr(struct hdd_adapter *adapter, + enum csr_akm_type *rsn_auth_type) +{ + struct csr_roam_profile *roam_profile; + uint8_t *security_ie; + uint32_t status = 0; + eCsrEncryptionType rsn_encrypt_type; + eCsrEncryptionType mc_rsn_encrypt_type; + struct hdd_context *hdd_ctx; + uint8_t mfp_required = 0; + uint8_t mfp_capable = 0; + u8 bssid[ETH_ALEN]; /* MAC address of assoc peer */ + + roam_profile = hdd_roam_profile(adapter); + security_ie = hdd_security_ie(adapter); + + /* MAC address of assoc peer */ + /* But, this routine is only called when we are NOT associated. */ + qdf_mem_copy(bssid, roam_profile->BSSIDs.bssid, sizeof(bssid)); + if (security_ie[0] == DOT11F_EID_RSN || + security_ie[0] == DOT11F_EID_WPA) { + /* continue */ + } else { + return 0; + } + + /* The actual processing may eventually be more extensive than this. */ + /* Right now, just consume any PMKIDs that are sent in by the app. */ + status = wlan_hdd_process_genie(adapter, bssid, + &rsn_encrypt_type, + &mc_rsn_encrypt_type, rsn_auth_type, + &mfp_required, &mfp_capable, + security_ie[1] + 2, + security_ie); + + if (status == 0) { + /* + * Now copy over all the security attributes + * you have parsed out. + */ + roam_profile->EncryptionType.numEntries = 1; + roam_profile->mcEncryptionType.numEntries = 1; + + /* Use the cipher type in the RSN IE */ + roam_profile->EncryptionType.encryptionType[0] = + rsn_encrypt_type; + roam_profile->mcEncryptionType.encryptionType[0] = + mc_rsn_encrypt_type; + + if ((QDF_IBSS_MODE == adapter->device_mode) && + ((eCSR_ENCRYPT_TYPE_AES == mc_rsn_encrypt_type) || + (eCSR_ENCRYPT_TYPE_AES_GCMP == mc_rsn_encrypt_type) || + (eCSR_ENCRYPT_TYPE_AES_GCMP_256 == mc_rsn_encrypt_type) || + (eCSR_ENCRYPT_TYPE_TKIP == mc_rsn_encrypt_type))) { + /* + * For wpa none supplicant sends the WPA IE with unicast + * cipher as eCSR_ENCRYPT_TYPE_NONE ,where as the + * multicast cipher as either AES/TKIP based on group + * cipher configuration mentioned in the + * wpa_supplicant.conf. + */ + + /* Set the unicast cipher same as multicast cipher */ + roam_profile->EncryptionType.encryptionType[0] + = mc_rsn_encrypt_type; + } + + hdd_update_values_mfp_cap(roam_profile, mfp_required, + mfp_capable); + } + + if (QDF_STATUS_SUCCESS != wlan_set_vdev_crypto_prarams_from_ie( + adapter->vdev, security_ie, + (security_ie[1] + 2))) + hdd_err("Failed to set the crypto params from IE"); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (hdd_ctx->force_rsne_override && + (security_ie[0] == DOT11F_EID_RSN)) { + hdd_warn("Test mode enabled set def Auth and enc type. RSN IE passed in connect req: "); + qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + roam_profile->pRSNReqIE, + roam_profile->nRSNReqIELength); + + roam_profile->force_rsne_override = true; + + hdd_set_mfp_enable(roam_profile); + + /* If parsing failed set the def value for the roam profile */ + if (status) + hdd_set_def_rsne_override(roam_profile, rsn_auth_type); + return 0; + } + return status; +} + +#ifdef WLAN_FEATURE_FILS_SK +/** + * hdd_check_fils_rsn_n_set_auth_type() - This API checks whether a give + * auth type is fils if yes, sets it in profile. + * @rsn_auth_type: auth type + * + * Return: true if FILS auth else false + */ +static +bool hdd_check_fils_rsn_n_set_auth_type(struct csr_roam_profile *roam_profile, + enum csr_akm_type rsn_auth_type) +{ + bool is_fils_rsn = false; + + if (!roam_profile->fils_con_info) + return false; + + if ((rsn_auth_type == eCSR_AUTH_TYPE_FILS_SHA256) || + (rsn_auth_type == eCSR_AUTH_TYPE_FILS_SHA384) || + (rsn_auth_type == eCSR_AUTH_TYPE_FT_FILS_SHA256) || + (rsn_auth_type == eCSR_AUTH_TYPE_FT_FILS_SHA384)) + is_fils_rsn = true; + if (is_fils_rsn) + roam_profile->fils_con_info->akm_type = rsn_auth_type; + + return is_fils_rsn; +} +#else +static +bool hdd_check_fils_rsn_n_set_auth_type(struct csr_roam_profile *roam_profile, + enum csr_akm_type rsn_auth_type) +{ + return false; +} +#endif + +/** + * hdd_set_csr_auth_type() - set csr auth type + * @adapter: pointer to adapter + * @rsn_auth_type: auth type + * + * Return: 0 on success, error number otherwise + */ +int hdd_set_csr_auth_type(struct hdd_adapter *adapter, + enum csr_akm_type rsn_auth_type) +{ + struct csr_roam_profile *roam_profile; + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + enum hdd_auth_key_mgmt key_mgmt = sta_ctx->auth_key_mgmt; + + roam_profile = hdd_roam_profile(adapter); + roam_profile->AuthType.numEntries = 1; + + switch (sta_ctx->conn_info.auth_type) { + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + case eCSR_AUTH_TYPE_AUTOSWITCH: +#ifdef FEATURE_WLAN_ESE + case eCSR_AUTH_TYPE_CCKM_WPA: + case eCSR_AUTH_TYPE_CCKM_RSN: +#endif + if (!sta_ctx->wpa_versions) { + + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_OPEN_SYSTEM; + } else if (sta_ctx->wpa_versions & NL80211_WPA_VERSION_1) { + +#ifdef FEATURE_WLAN_ESE + if ((rsn_auth_type == eCSR_AUTH_TYPE_CCKM_WPA) && + ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) + == HDD_AUTH_KEY_MGMT_802_1X)) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_CCKM_WPA; + } else if (rsn_auth_type == eCSR_AUTH_TYPE_CCKM_WPA) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_CCKM_WPA; + } else +#endif + if ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) + == HDD_AUTH_KEY_MGMT_802_1X) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_WPA; + } else + if ((key_mgmt & HDD_AUTH_KEY_MGMT_PSK) + == HDD_AUTH_KEY_MGMT_PSK) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_WPA_PSK; + } else { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_WPA_NONE; + } + } + if (sta_ctx->wpa_versions & NL80211_WPA_VERSION_2) { +#ifdef FEATURE_WLAN_ESE + if ((rsn_auth_type == eCSR_AUTH_TYPE_CCKM_RSN) && + ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) + == HDD_AUTH_KEY_MGMT_802_1X)) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_CCKM_RSN; + } else if (rsn_auth_type == eCSR_AUTH_TYPE_CCKM_RSN) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_CCKM_RSN; + } else +#endif + if (rsn_auth_type == eCSR_AUTH_TYPE_DPP_RSN) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_DPP_RSN; + } else if (rsn_auth_type == eCSR_AUTH_TYPE_OSEN) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_OSEN; + } else if ((rsn_auth_type == eCSR_AUTH_TYPE_FT_RSN) && + ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) + == HDD_AUTH_KEY_MGMT_802_1X)) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_FT_RSN; + } else if ((rsn_auth_type == eCSR_AUTH_TYPE_FT_RSN_PSK) + && + ((key_mgmt & HDD_AUTH_KEY_MGMT_PSK) + == HDD_AUTH_KEY_MGMT_PSK)) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_FT_RSN_PSK; + } else + +#ifdef WLAN_FEATURE_11W + if (rsn_auth_type == eCSR_AUTH_TYPE_RSN_PSK_SHA256) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_RSN_PSK_SHA256; + } else if (rsn_auth_type == + eCSR_AUTH_TYPE_RSN_8021X_SHA256) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_RSN_8021X_SHA256; + } else +#endif + if (hdd_check_fils_rsn_n_set_auth_type(roam_profile, + rsn_auth_type)) { + roam_profile->AuthType.authType[0] = + rsn_auth_type; + } else if ((rsn_auth_type == eCSR_AUTH_TYPE_OWE) && + ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) + == HDD_AUTH_KEY_MGMT_802_1X)) { + /* OWE case */ + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_OWE; + } else if (rsn_auth_type == eCSR_AUTH_TYPE_SAE) { + /* SAE with open authentication case */ + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_SAE; + } else if ((rsn_auth_type == + eCSR_AUTH_TYPE_SUITEB_EAP_SHA256) && + ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) + == HDD_AUTH_KEY_MGMT_802_1X)) { + /* Suite B EAP SHA 256 */ + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_SUITEB_EAP_SHA256; + } else if ((rsn_auth_type == + eCSR_AUTH_TYPE_SUITEB_EAP_SHA384) && + ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) + == HDD_AUTH_KEY_MGMT_802_1X)) { + /* Suite B EAP SHA 384 */ + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_SUITEB_EAP_SHA384; + } else if ((rsn_auth_type == eCSR_AUTH_TYPE_FT_SAE) && + ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) == + HDD_AUTH_KEY_MGMT_802_1X)) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_FT_SAE; + } else if ((rsn_auth_type == + eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384) && + ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) + == HDD_AUTH_KEY_MGMT_802_1X)) { + /* FT Suite-B EAP SHA 384 */ + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384; + + } else if ((key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) + == HDD_AUTH_KEY_MGMT_802_1X) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_RSN; + } else + if ((key_mgmt & HDD_AUTH_KEY_MGMT_PSK) + == HDD_AUTH_KEY_MGMT_PSK) { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_RSN_PSK; + } else { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_UNKNOWN; + } + } + break; + + case eCSR_AUTH_TYPE_SHARED_KEY: + + roam_profile->AuthType.authType[0] = eCSR_AUTH_TYPE_SHARED_KEY; + break; + + case eCSR_AUTH_TYPE_SAE: + + if (rsn_auth_type == eCSR_AUTH_TYPE_FT_SAE) + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_FT_SAE; + else + roam_profile->AuthType.authType[0] = eCSR_AUTH_TYPE_SAE; + break; + + default: + +#ifdef FEATURE_WLAN_ESE + hdd_debug("In default, unknown auth type."); +#endif /* FEATURE_WLAN_ESE */ + roam_profile->AuthType.authType[0] = eCSR_AUTH_TYPE_UNKNOWN; + break; + } + + return 0; +} + +#ifdef WLAN_FEATURE_FILS_SK +static void hdd_initialize_fils_info(struct hdd_adapter *adapter) +{ + struct csr_roam_profile *roam_profile; + + roam_profile = hdd_roam_profile(adapter); + roam_profile->fils_con_info = NULL; + roam_profile->hlp_ie = NULL; + roam_profile->hlp_ie_len = 0; +} +#else +static void hdd_initialize_fils_info(struct hdd_adapter *adapter) +{ } +#endif + +void hdd_roam_profile_init(struct hdd_adapter *adapter) +{ + struct csr_roam_profile *roam_profile; + uint8_t *security_ie; + tSirAddie *assoc_additional_ie; + struct hdd_station_ctx *sta_ctx; + + hdd_enter(); + + roam_profile = hdd_roam_profile(adapter); + qdf_mem_zero(roam_profile, sizeof(*roam_profile)); + + security_ie = hdd_security_ie(adapter); + qdf_mem_zero(security_ie, WLAN_MAX_IE_LEN); + + assoc_additional_ie = hdd_assoc_additional_ie(adapter); + qdf_mem_zero(assoc_additional_ie, sizeof(*assoc_additional_ie)); + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + /* Configure the roaming profile links to SSID and bssid. */ + roam_profile->SSIDs.numOfSSIDs = 0; + roam_profile->SSIDs.SSIDList = &sta_ctx->conn_info.ssid; + + roam_profile->BSSIDs.numOfBSSIDs = 0; + roam_profile->BSSIDs.bssid = &sta_ctx->conn_info.bssid; + + /* Set the numOfChannels to zero to scan all the channels */ + roam_profile->ChannelInfo.numOfChannels = 0; + roam_profile->ChannelInfo.freq_list = NULL; + + roam_profile->BSSType = eCSR_BSS_TYPE_INFRASTRUCTURE; + + roam_profile->phyMode = eCSR_DOT11_MODE_AUTO; + sta_ctx->wpa_versions = 0; + + /* Set the default scan mode */ + adapter->scan_info.scan_mode = eSIR_ACTIVE_SCAN; + + hdd_clear_roam_profile_ie(adapter); + + hdd_initialize_fils_info(adapter); + + hdd_exit(); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_bcn_recv.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_bcn_recv.c new file mode 100644 index 0000000000000000000000000000000000000000..c426a386dd59ce1a988c103a7cf028032945f546 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_bcn_recv.c @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_bcn_recv.c + * Feature for receiving beacons of connected AP and sending select + * params to upper layer via vendor event + */ + +#include +#include +#include "wlan_osif_priv.h" +#include "qdf_trace.h" +#include "wlan_hdd_main.h" +#include "osif_sync.h" +#include "wlan_hdd_bcn_recv.h" +#include +#include + +#define SET_BIT(value, mask) ((value) |= (1 << (mask))) + +#define BOOTTIME QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BOOTTIME_WHEN_RECEIVED + +#ifndef CHAR_BIT +#define CHAR_BIT 8 /* Normally in */ +#endif + +static const struct nla_policy +beacon_reporting_params[QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_ACTIVE_REPORTING] = {.type = + NLA_FLAG}, + [QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PERIOD] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME] = {.type = + NLA_FLAG}, +}; + +/** + * get_beacon_report_data_len() - Calculate length for beacon + * report to allocate skb buffer + * @report: beacon report structure + * + * Return: skb buffer length + */ +static +int get_beacon_report_data_len(struct wlan_beacon_report *report) +{ + uint32_t data_len = NLMSG_HDRLEN; + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE */ + data_len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_SSID */ + data_len += nla_total_size(report->ssid.length); + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BSSID */ + data_len += nla_total_size(ETH_ALEN); + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_FREQ */ + data_len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BI */ + data_len += nla_total_size(sizeof(u16)); + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_TSF */ + data_len += nla_total_size(sizeof(uint64_t)); + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BOOTTIME_WHEN_RECEIVED */ + data_len += nla_total_size(sizeof(uint64_t)); + + return data_len; +} + +/** + * get_pause_ind_data_len() - Calculate skb buffer length + * @is_disconnected: Connection state + * + * Calculate length for pause indication to allocate skb buffer + * + * Return: skb buffer length + */ +static int get_pause_ind_data_len(bool is_disconnected) +{ + uint32_t data_len = NLMSG_HDRLEN; + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE */ + data_len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON */ + data_len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES */ + if (!is_disconnected) + data_len += nla_total_size(sizeof(u8)); + + return data_len; +} + +/** + * hdd_send_bcn_recv_info() - Send beacon info to userspace for + * connected AP + * @hdd_handle: hdd_handle to get hdd_adapter + * @beacon_report: Required beacon report + * + * Send beacon info to userspace for connected AP through a vendor event: + * QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING. + */ +static QDF_STATUS hdd_send_bcn_recv_info(hdd_handle_t hdd_handle, + struct wlan_beacon_report + *beacon_report) +{ + struct sk_buff *vendor_event; + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + uint32_t data_len; + int flags = cds_get_gfp_flags(); + struct hdd_adapter *adapter; + + if (wlan_hdd_validate_context(hdd_ctx)) + return QDF_STATUS_E_FAILURE; + + data_len = get_beacon_report_data_len(beacon_report); + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, beacon_report->vdev_id); + if (hdd_validate_adapter(adapter)) + return QDF_STATUS_E_FAILURE; + + vendor_event = + cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, &(adapter->wdev), + data_len, + QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING_INDEX, + flags); + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return QDF_STATUS_E_FAILURE; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE, + QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO) || + nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_SSID, + beacon_report->ssid.length, beacon_report->ssid.ssid) || + nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BSSID, + ETH_ALEN, beacon_report->bssid.bytes) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_FREQ, + beacon_report->frequency) || + nla_put_u16(vendor_event, + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BI, + beacon_report->beacon_interval) || + wlan_cfg80211_nla_put_u64(vendor_event, + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_TSF, + beacon_report->time_stamp) || + wlan_cfg80211_nla_put_u64(vendor_event, BOOTTIME, + beacon_report->boot_time)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + kfree_skb(vendor_event); + return QDF_STATUS_E_FAILURE; + } + + cfg80211_vendor_event(vendor_event, flags); + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_handle_beacon_reporting_start_op() - Process bcn recv start op + * @hdd_ctx: Pointer to hdd context + * @adapter: Pointer to network adapter + * @active_report: Active reporting flag + * @nth_value: Beacon report period + * @do_not_resume: beacon reporting resume after a pause is completed + * + * This function process beacon reporting start operation. + */ +static int hdd_handle_beacon_reporting_start_op(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + bool active_report, + uint32_t nth_value, + bool do_not_resume) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + int errno; + uint32_t mask = 0; + + if (active_report) { + /* Register beacon report callback */ + qdf_status = + sme_register_bcn_report_pe_cb(hdd_ctx->mac_handle, + hdd_send_bcn_recv_info); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err("bcn recv info cb reg failed = %d", qdf_status); + errno = qdf_status_to_os_return(qdf_status); + return errno; + } + + /* Register pause indication callback */ + qdf_status = + sme_register_bcn_recv_pause_ind_cb(hdd_ctx->mac_handle, + hdd_beacon_recv_pause_indication); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err("pause_ind_cb reg failed = %d", qdf_status); + errno = qdf_status_to_os_return(qdf_status); + return errno; + } + /* Update Beacon report period in case of active reporting */ + nth_value = 1; + /* + * Set MSB which indicates fw to don't wakeup host in wow + * mode in case of active beacon report. + */ + mask = (sizeof(uint32_t) * CHAR_BIT) - 1; + SET_BIT(nth_value, mask); + } + /* Handle beacon receive start indication */ + qdf_status = sme_handle_bcn_recv_start(hdd_ctx->mac_handle, + adapter->vdev_id, nth_value, + do_not_resume); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err("bcn rcv start failed with status=%d", qdf_status); + if (sme_register_bcn_report_pe_cb(hdd_ctx->mac_handle, NULL)) + hdd_err("bcn report cb deregistration failed"); + if (sme_register_bcn_recv_pause_ind_cb(hdd_ctx->mac_handle, + NULL)) + hdd_err("bcn pause ind cb deregistration failed"); + errno = qdf_status_to_os_return(qdf_status); + return errno; + } + + errno = qdf_status_to_os_return(qdf_status); + + return errno; +} + +/** + * hdd_handle_beacon_reporting_stop_op() - Process bcn recv stop op + * @hdd_ctx: Pointer to hdd context + * @adapter: Pointer to network adapter + * + * This function process beacon reporting stop operation. + */ +static int hdd_handle_beacon_reporting_stop_op(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + int errno; + + /* Reset bcn recv start flag */ + sme_stop_beacon_report(hdd_ctx->mac_handle, adapter->vdev_id); + + /* Deregister beacon report callback */ + qdf_status = sme_register_bcn_report_pe_cb(hdd_ctx->mac_handle, NULL); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err("Callback de-registration failed = %d", qdf_status); + errno = qdf_status_to_os_return(qdf_status); + return errno; + } + + /* Deregister pause indication callback */ + qdf_status = sme_register_bcn_recv_pause_ind_cb(hdd_ctx->mac_handle, + NULL); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err("scan even deregister failed = %d", qdf_status); + errno = qdf_status_to_os_return(qdf_status); + return errno; + } + + if (hdd_adapter_is_connected_sta(adapter)) + /* Add beacon filter */ + if (hdd_add_beacon_filter(adapter)) { + hdd_err("Beacon filter addition failed"); + return -EINVAL; + } + + errno = qdf_status_to_os_return(qdf_status); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_bcn_rcv_op() - enable/disable beacon reporting + * indication + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * This function is used to enable/disable asynchronous beacon + * reporting feature using vendor commands. + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_bcn_rcv_op(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_MAX + 1]; + uint32_t bcn_report, nth_value = 1; + int errno; + bool active_report, do_not_resume; + struct wlan_objmgr_vdev *vdev; + enum scm_scan_status scan_req_status; + + hdd_enter_dev(dev); + + errno = hdd_validate_adapter(adapter); + if (errno) + return errno; + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_err("Command not allowed as device not in STA mode"); + return -EINVAL; + } + + if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + hdd_err("STA not in connected state"); + return -EINVAL; + } + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + scan_req_status = ucfg_scan_get_pdev_status(wlan_vdev_get_pdev(vdev)); + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); + + if (scan_req_status != SCAN_NOT_IN_PROGRESS) { + hdd_debug("Scan in progress: %d, bcn rpt start OP not allowed", + scan_req_status); + return -EBUSY; + } + + errno = + wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_MAX, + data, data_len, beacon_reporting_params); + if (errno) { + hdd_err("Failed to parse the beacon reporting params %d", + errno); + return errno; + } + + /* Parse and fetch OP Type */ + if (!tb[QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE]) { + hdd_err("attr beacon report OP type failed"); + return -EINVAL; + } + bcn_report = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE]); + hdd_debug("Bcn Report: OP type:%d", bcn_report); + + switch (bcn_report) { + case QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START: + active_report = + !!tb[QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_ACTIVE_REPORTING]; + hdd_debug("attr active_report %d", active_report); + + do_not_resume = + !!tb[QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME]; + hdd_debug("Attr beacon report do not resume %d", do_not_resume); + + if (tb[QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PERIOD]) + nth_value = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PERIOD]); + hdd_debug("Beacon Report: Period: %d", nth_value); + + if (sme_is_beacon_report_started(hdd_ctx->mac_handle, + adapter->vdev_id)) { + hdd_debug("Start cmd already in progress, issue the stop to FW, before new start"); + if (hdd_handle_beacon_reporting_stop_op(hdd_ctx, + adapter)) { + hdd_err("Failed to stop the beacon reporting before starting new start"); + return -EAGAIN; + } + } + errno = hdd_handle_beacon_reporting_start_op(hdd_ctx, + adapter, + active_report, + nth_value, + do_not_resume); + if (errno) { + hdd_err("Failed to start beacon reporting %d,", errno); + break; + } + break; + case QCA_WLAN_VENDOR_BEACON_REPORTING_OP_STOP: + if (sme_is_beacon_report_started(hdd_ctx->mac_handle, + adapter->vdev_id)) { + errno = hdd_handle_beacon_reporting_stop_op(hdd_ctx, + adapter); + if (errno) { + hdd_err("Failed to stop the beacon report, %d", + errno); + } + } else { + hdd_err_rl("BCN_RCV_STOP rej as no START CMD active"); + errno = -EINVAL; + } + break; + default: + hdd_debug("Invalid bcn report type %d", bcn_report); + } + + return errno; +} + +void hdd_beacon_recv_pause_indication(hdd_handle_t hdd_handle, + uint8_t vdev_id, + enum scan_event_type type, + bool is_disconnected) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + struct hdd_adapter *adapter; + struct sk_buff *vendor_event; + uint32_t data_len; + int flags; + uint32_t abort_reason; + bool do_not_resume; + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (hdd_validate_adapter(adapter)) + return; + + data_len = get_pause_ind_data_len(is_disconnected); + flags = cds_get_gfp_flags(); + + vendor_event = + cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, &(adapter->wdev), + data_len, + QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING_INDEX, + flags); + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + do_not_resume = + sme_is_beacon_reporting_do_not_resume(hdd_ctx->mac_handle, + adapter->vdev_id); + + if (is_disconnected) { + abort_reason = + QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED; + /* Deregister callbacks and Reset bcn recv start flag */ + if (sme_is_beacon_report_started(hdd_ctx->mac_handle, + adapter->vdev_id)) + hdd_handle_beacon_reporting_stop_op(hdd_ctx, + adapter); + } else { + /* + * In case of scan, Check that auto resume of beacon reporting + * is allowed or not. + * If not allowed: + * Deregister callbacks and Reset bcn recv start flag in order + * to make sure host should not send beacon report to userspace + * further. + * If Auto resume allowed: + * Send pause indication to userspace and continue sending + * connected AP's beacon to userspace. + */ + if (do_not_resume) + hdd_handle_beacon_reporting_stop_op(hdd_ctx, + adapter); + + switch (type) { + case SCAN_EVENT_TYPE_FOREIGN_CHANNEL: + abort_reason = + QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_SCAN_STARTED; + break; + default: + abort_reason = + QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_UNSPECIFIED; + } + } + /* Send vendor event to user space to inform ABORT */ + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE, + QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON, + abort_reason)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + kfree_skb(vendor_event); + return; + } + + /* + * Send auto resume flag to user space to specify the driver will + * automatically resume reporting beacon events only in case of + * pause indication due to scan started. + * If do_not_resume flag is set in the recent + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START command, then in the + * subsequent QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE event (if any) + * the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES shall not be + * set by the driver. + */ + if (!is_disconnected && !do_not_resume) + if (nla_put_flag(vendor_event, + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + kfree_skb(vendor_event); + return; + } + + cfg80211_vendor_event(vendor_event, flags); +} + +int wlan_hdd_cfg80211_bcn_rcv_op(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_bcn_rcv_op(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_bss_transition.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_bss_transition.c new file mode 100644 index 0000000000000000000000000000000000000000..da842121b528c9ba945a85e6e183b7908b6c3bc0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_bss_transition.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_bss_transition.c + * + * WLAN bss transition functions + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include + +/** + * wlan_hdd_is_bt_in_progress() - check if bt activity is in progress + * @hdd_ctx : HDD context + * + * Return: true if BT activity is in progress else false + */ +static bool wlan_hdd_is_bt_in_progress(struct hdd_context *hdd_ctx) +{ + if (hdd_ctx->bt_a2dp_active || hdd_ctx->bt_vo_active) + return true; + + return false; +} + +/** + * wlan_hdd_fill_btm_resp() - Fill bss candidate response buffer + * @reply_skb : pointer to reply_skb + * @info : bss candidate information + * @index : attribute type index for nla_next_start() + * + * Return : 0 on success and errno on failure + */ +static int wlan_hdd_fill_btm_resp(struct sk_buff *reply_skb, + struct bss_candidate_info *info, + int index) +{ + struct nlattr *attr; + + attr = nla_nest_start(reply_skb, index); + if (!attr) { + hdd_err("nla_nest_start failed"); + kfree_skb(reply_skb); + return -EINVAL; + } + + if (nla_put(reply_skb, + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID, + ETH_ALEN, info->bssid.bytes) || + nla_put_u32(reply_skb, + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS, + info->status)) { + hdd_err("nla_put failed"); + kfree_skb(reply_skb); + return -EINVAL; + } + + nla_nest_end(reply_skb, attr); + + return 0; +} + +/** + * __wlan_hdd_cfg80211_fetch_bss_transition_status() - fetch bss transition + * status + * @wiphy : WIPHY structure pointer + * @wdev : Wireless device structure pointer + * @data : Pointer to the data received + * @data_len : Length of the data received + * + * This function is used to fetch transition status for candidate bss. The + * transition status is either accept or reason for reject. + * + * Return : 0 on success and errno on failure + */ +static int +__wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + struct nlattr *tb_msg[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1]; + uint8_t transition_reason; + struct nlattr *attr; + struct sk_buff *reply_skb; + int rem, j; + int ret; + bool is_bt_in_progress; + struct bss_candidate_info candidate_info[MAX_CANDIDATE_INFO]; + uint16_t nof_candidates, i = 0; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + mac_handle_t mac_handle; + QDF_STATUS status; + + const struct nla_policy + btm_params_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO] = { + .type = NLA_NESTED}, + }; + const struct nla_policy + btm_cand_list_policy[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] + = {[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = { + .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = { + .type = NLA_U32}, + }; + + hdd_enter(); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (adapter->device_mode != QDF_STA_MODE || + hdd_sta_ctx->conn_info.conn_state != eConnectionState_Associated) { + hdd_err("Command is either not invoked for STA mode (device mode: %d) or STA is not associated (Connection state: %d)", + adapter->device_mode, hdd_sta_ctx->conn_info.conn_state); + return -EINVAL; + } + + ret = wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, + data_len, btm_params_policy); + if (ret) { + hdd_err("Attribute parse failed"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON] || + !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO]) { + hdd_err("Missing attributes"); + return -EINVAL; + } + + transition_reason = nla_get_u8( + tb[QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON]); + + nla_for_each_nested(attr, + tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO], + rem) { + ret = wlan_cfg80211_nla_parse_nested(tb_msg, + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX, + attr, btm_cand_list_policy); + if (ret) { + hdd_err("Attribute parse failed"); + return -EINVAL; + } + + if (!tb_msg[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]) { + hdd_err("Missing BSSID attribute"); + return -EINVAL; + } + + qdf_mem_copy((void *)candidate_info[i].bssid.bytes, + nla_data(tb_msg[ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]), + QDF_MAC_ADDR_SIZE); + i++; + if (i == MAX_CANDIDATE_INFO) + break; + } + + /* + * Determine status for each candidate and fill in the status field. + * Also arrange the candidates in the order of preference. + */ + nof_candidates = i; + + is_bt_in_progress = wlan_hdd_is_bt_in_progress(hdd_ctx); + + mac_handle = hdd_ctx->mac_handle; + status = sme_get_bss_transition_status(mac_handle, transition_reason, + &hdd_sta_ctx->conn_info.bssid, + candidate_info, + nof_candidates, + is_bt_in_progress); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + /* Prepare the reply and send it to userspace */ + reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, + ((QDF_MAC_ADDR_SIZE + sizeof(uint32_t)) * + nof_candidates) + NLMSG_HDRLEN); + if (!reply_skb) { + hdd_err("reply buffer alloc failed"); + return -ENOMEM; + } + + attr = nla_nest_start(reply_skb, + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO); + if (!attr) { + hdd_err("nla_nest_start failed"); + kfree_skb(reply_skb); + return -EINVAL; + } + + /* + * Order candidates as - accepted candidate list followed by rejected + * candidate list + */ + for (i = 0, j = 0; i < nof_candidates; i++) { + /* copy accepted candidate list */ + if (candidate_info[i].status == QCA_STATUS_ACCEPT) { + if (wlan_hdd_fill_btm_resp(reply_skb, + &candidate_info[i], j)) + return -EINVAL; + j++; + } + } + for (i = 0; i < nof_candidates; i++) { + /* copy rejected candidate list */ + if (candidate_info[i].status != QCA_STATUS_ACCEPT) { + if (wlan_hdd_fill_btm_resp(reply_skb, + &candidate_info[i], j)) + return -EINVAL; + j++; + } + } + nla_nest_end(reply_skb, attr); + + hdd_exit(); + + return cfg80211_vendor_cmd_reply(reply_skb); +} + +int wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_fetch_bss_transition_status(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_bss_transition.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_bss_transition.h new file mode 100644 index 0000000000000000000000000000000000000000..86ba2470ae645c96948edda5a6a9c5aad4d6022e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_bss_transition.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_BSS_TRANSITION_H +#define __WLAN_HDD_BSS_TRANSITION_H + +/** + * DOC: wlan_hdd_bss_transition_h + * + * WLAN Host Device Driver BSS transition API specification + */ + +#ifdef FEATURE_BSS_TRANSITION +/** + * wlan_hdd_cfg80211_fetch_bss_transition_status() - fetch bss transition status + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function is used to fetch transition status for candidate bss. The + * transition status is either accept or reason for reject. + * + * Return: 0 on success and errno on failure + */ +int +wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +#define FEATURE_BSS_TRANSITION_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = \ + QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_fetch_bss_transition_status \ +}, +#else /* FEATURE_BSS_TRANSITION */ +#define FEATURE_BSS_TRANSITION_VENDOR_COMMANDS +#endif /* FEATURE_BSS_TRANSITION */ + +#endif /* __WLAN_HDD_BSS_TRANSITION_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg.c new file mode 100644 index 0000000000000000000000000000000000000000..68f9492dff61958bf125e5b9c1fb1e59c3f99ca0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg.c @@ -0,0 +1,1354 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_cfg.c + * + * WLAN Host Device Driver configuration interface implementation + */ + +/* Include Files */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_hdd_he.h" +#include +#include "wifi_pos_api.h" +#include "wlan_hdd_green_ap.h" +#include "wlan_hdd_twt.h" +#include "wlan_policy_mgr_ucfg.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_mlme_public_struct.h" +#include "wlan_fwol_ucfg_api.h" +#include "cfg_ucfg_api.h" +#include "hdd_dp_cfg.h" +#include "wlan_hdd_object_manager.h" + +/** + * get_next_line() - find and locate the new line pointer + * @str: pointer to string + * + * This function returns a pointer to the character after the occurrence + * of a new line character. It also modifies the original string by replacing + * the '\n' character with the null character. + * + * Return: the pointer to the character at new line, + * or NULL if no new line character was found + */ +static char *get_next_line(char *str) +{ + char c; + + if (!str || *str == '\0') + return NULL; + + c = *str; + while (c != '\n' && c != '\0' && c != 0xd) { + str = str + 1; + c = *str; + } + + if (c == '\0') + return NULL; + + *str = '\0'; + return str + 1; +} + +/** look for space. Ascii values to look are + * 0x09 == horizontal tab + * 0x0a == Newline ("\n") + * 0x0b == vertical tab + * 0x0c == Newpage or feed form. + * 0x0d == carriage return (CR or "\r") + * Null ('\0') should not considered as space. + */ +#define i_isspace(ch) (((ch) >= 0x09 && (ch) <= 0x0d) || (ch) == ' ') + +/** + * i_trim() - trims any leading and trailing white spaces + * @str: pointer to string + * + * Return: the pointer of the string + */ +static char *i_trim(char *str) +{ + char *ptr; + + if (*str == '\0') + return str; + + /* Find the first non white-space */ + ptr = str; + while (i_isspace(*ptr)) + ptr++; + + if (*ptr == '\0') + return str; + + /* This is the new start of the string */ + str = ptr; + + /* Find the last non white-space */ + ptr += strlen(ptr) - 1; + + while (ptr != str && i_isspace(*ptr)) + ptr--; + + /* Null terminate the following character */ + ptr[1] = '\0'; + + return str; +} + +/** struct hdd_cfg_entry - ini configuration entry + * @name: name of the entry + * @value: value of the entry + */ +struct hdd_cfg_entry { + char *name; + char *value; +}; + +/** + * update_mac_from_string() - convert string to 6 bytes mac address + * @hdd_ctx: the pointer to hdd context + * @mac_table: the mac_table to carry the conversion + * @num: number of the interface + * + * 00AA00BB00CC -> 0x00 0xAA 0x00 0xBB 0x00 0xCC + * + * Return: QDF_STATUS + */ +static QDF_STATUS update_mac_from_string(struct hdd_context *hdd_ctx, + struct hdd_cfg_entry *mac_table, + int num) +{ + int i = 0, j = 0, res = 0; + char *candidate = NULL; + struct qdf_mac_addr macaddr[QDF_MAX_CONCURRENCY_PERSONA]; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + memset(macaddr, 0, sizeof(macaddr)); + + for (i = 0; i < num; i++) { + candidate = mac_table[i].value; + for (j = 0; j < QDF_MAC_ADDR_SIZE; j++) { + res = + hex2bin(&macaddr[i].bytes[j], &candidate[(j << 1)], + 1); + if (res < 0) + break; + } + if (res == 0 && !qdf_is_macaddr_zero(&macaddr[i])) { + qdf_mem_copy((uint8_t *)&hdd_ctx-> + provisioned_mac_addr[i].bytes[0], + (uint8_t *) &macaddr[i].bytes[0], + QDF_MAC_ADDR_SIZE); + } else { + status = QDF_STATUS_E_FAILURE; + break; + } + } + return status; +} + +/** + * hdd_set_power_save_offload_config() - set power save offload configuration + * @hdd_ctx: the pointer to hdd context + * + * Return: none + */ +static void hdd_set_power_save_offload_config(struct hdd_context *hdd_ctx) +{ + uint32_t listen_interval = 0; + char *power_usage = NULL; + + power_usage = ucfg_mlme_get_power_usage(hdd_ctx->psoc); + if (!power_usage) { + hdd_err("invalid power usage"); + return; + } + + if (strcmp(power_usage, "Min") == 0) + ucfg_mlme_get_bmps_min_listen_interval(hdd_ctx->psoc, + &listen_interval); + else if (strcmp(power_usage, "Max") == 0) + ucfg_mlme_get_bmps_max_listen_interval(hdd_ctx->psoc, + &listen_interval); + /* + * Based on Mode Set the LI + * Otherwise default LI value of 1 will + * be taken + */ + if (listen_interval) { + /* + * setcfg for listenInterval. + * Make sure CFG is updated because PE reads this + * from CFG at the time of assoc or reassoc + */ + ucfg_mlme_set_sap_listen_interval(hdd_ctx->psoc, + listen_interval); + } +} + +/** + * hdd_update_mac_config() - update MAC address from cfg file + * @hdd_ctx: the pointer to hdd context + * + * It overwrites the MAC address if config file exist. + * + * Return: QDF_STATUS_SUCCESS if the MAC address is found from cfg file + * and overwritten, otherwise QDF_STATUS_E_INVAL + */ +QDF_STATUS hdd_update_mac_config(struct hdd_context *hdd_ctx) +{ + int status, i = 0; + const struct firmware *fw = NULL; + char *line, *buffer = NULL; + char *temp = NULL; + char *name, *value; + int max_mac_addr = QDF_MAX_CONCURRENCY_PERSONA; + struct hdd_cfg_entry mac_table[QDF_MAX_CONCURRENCY_PERSONA]; + tSirMacAddr custom_mac_addr; + + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + memset(mac_table, 0, sizeof(mac_table)); + status = request_firmware(&fw, WLAN_MAC_FILE, hdd_ctx->parent_dev); + if (status) { + /* + * request_firmware "fails" if the file is not found, which is a + * valid setup for us, so log using debug instead of error + */ + hdd_debug("request_firmware failed; status:%d", status); + return QDF_STATUS_E_FAILURE; + } + + if (!fw || !fw->data || !fw->size) { + hdd_alert("invalid firmware"); + qdf_status = QDF_STATUS_E_INVAL; + goto config_exit; + } + + hdd_debug("wlan_mac.bin size %zu", fw->size); + + temp = qdf_mem_malloc(fw->size + 1); + + if (!temp) { + hdd_err("fail to alloc memory"); + qdf_status = QDF_STATUS_E_NOMEM; + goto config_exit; + } + buffer = temp; + qdf_mem_copy(buffer, fw->data, fw->size); + buffer[fw->size] = 0x0; + + /* data format: + * Intf0MacAddress=00AA00BB00CC + * Intf1MacAddress=00AA00BB00CD + * END + */ + while (buffer) { + line = get_next_line(buffer); + buffer = i_trim(buffer); + + if (strlen((char *)buffer) == 0 || *buffer == '#') { + buffer = line; + continue; + } + if (strncmp(buffer, "END", 3) == 0) + break; + + name = buffer; + buffer = strnchr(buffer, strlen(buffer), '='); + if (buffer) { + *buffer++ = '\0'; + i_trim(name); + if (strlen(name) != 0) { + buffer = i_trim(buffer); + if (strlen(buffer) == 12) { + value = buffer; + mac_table[i].name = name; + mac_table[i++].value = value; + if (i >= QDF_MAX_CONCURRENCY_PERSONA) + break; + } + } + } + buffer = line; + } + + if (i != 0 && i <= QDF_MAX_CONCURRENCY_PERSONA) { + hdd_debug("%d Mac addresses provided", i); + } else { + hdd_err("invalid number of Mac address provided, nMac = %d", i); + qdf_status = QDF_STATUS_E_INVAL; + goto config_exit; + } + + qdf_status = update_mac_from_string(hdd_ctx, &mac_table[0], i); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err("Invalid MAC addresses provided"); + goto config_exit; + } + hdd_ctx->num_provisioned_addr = i; + hdd_debug("Populating remaining %d Mac addresses", + max_mac_addr - i); + hdd_populate_random_mac_addr(hdd_ctx, max_mac_addr - i); + + if (hdd_ctx->num_provisioned_addr) + qdf_mem_copy(&custom_mac_addr, + &hdd_ctx->provisioned_mac_addr[0].bytes[0], + sizeof(tSirMacAddr)); + else + qdf_mem_copy(&custom_mac_addr, + &hdd_ctx->derived_mac_addr[0].bytes[0], + sizeof(tSirMacAddr)); + + sme_set_custom_mac_addr(custom_mac_addr); + +config_exit: + qdf_mem_free(temp); + release_firmware(fw); + return qdf_status; +} + +/** + * hdd_disable_runtime_pm() - Override to disable runtime_pm. + * @cfg_ini: Handle to struct hdd_config + * + * Return: None + */ +#ifdef FEATURE_RUNTIME_PM +static void hdd_disable_runtime_pm(struct hdd_config *cfg_ini) +{ + cfg_ini->runtime_pm = 0; +} +#else +static void hdd_disable_runtime_pm(struct hdd_config *cfg_ini) +{ +} +#endif + +/** + * hdd_disable_auto_shutdown() - Override to disable auto_shutdown. + * @cfg_ini: Handle to struct hdd_config + * + * Return: None + */ +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +static void hdd_disable_auto_shutdown(struct hdd_config *cfg_ini) +{ + cfg_ini->wlan_auto_shutdown = 0; +} +#else +static void hdd_disable_auto_shutdown(struct hdd_config *cfg_ini) +{ +} +#endif + +void hdd_override_all_ps(struct hdd_context *hdd_ctx) +{ + struct hdd_config *cfg_ini = hdd_ctx->config; + + ucfg_mlme_override_bmps_imps(hdd_ctx->psoc); + hdd_disable_runtime_pm(cfg_ini); + hdd_disable_auto_shutdown(cfg_ini); +} + +/** + * hdd_cfg_xlate_to_csr_phy_mode() - convert PHY mode + * @dot11Mode: the mode to convert + * + * Convert the configuration PHY mode to CSR PHY mode + * + * Return: the CSR phy mode value + */ +eCsrPhyMode hdd_cfg_xlate_to_csr_phy_mode(enum hdd_dot11_mode dot11Mode) +{ + if (cds_is_sub_20_mhz_enabled()) + return eCSR_DOT11_MODE_abg; + + switch (dot11Mode) { + case (eHDD_DOT11_MODE_abg): + return eCSR_DOT11_MODE_abg; + case (eHDD_DOT11_MODE_11b): + return eCSR_DOT11_MODE_11b; + case (eHDD_DOT11_MODE_11g): + return eCSR_DOT11_MODE_11g; + default: + case (eHDD_DOT11_MODE_11n): + return eCSR_DOT11_MODE_11n; + case (eHDD_DOT11_MODE_11g_ONLY): + return eCSR_DOT11_MODE_11g_ONLY; + case (eHDD_DOT11_MODE_11n_ONLY): + return eCSR_DOT11_MODE_11n_ONLY; + case (eHDD_DOT11_MODE_11b_ONLY): + return eCSR_DOT11_MODE_11b_ONLY; + case (eHDD_DOT11_MODE_11ac_ONLY): + return eCSR_DOT11_MODE_11ac_ONLY; + case (eHDD_DOT11_MODE_11ac): + return eCSR_DOT11_MODE_11ac; + case (eHDD_DOT11_MODE_AUTO): + return eCSR_DOT11_MODE_AUTO; + case (eHDD_DOT11_MODE_11a): + return eCSR_DOT11_MODE_11a; + case (eHDD_DOT11_MODE_11ax_ONLY): + return eCSR_DOT11_MODE_11ax_ONLY; + case (eHDD_DOT11_MODE_11ax): + return eCSR_DOT11_MODE_11ax; + } + +} + +/** + * hdd_set_idle_ps_config() - set idle power save configuration + * @hdd_ctx: the pointer to hdd context + * @val: the value to configure + * + * Return: QDF_STATUS_SUCCESS if command set correctly, + * otherwise the QDF_STATUS return from SME layer + */ +QDF_STATUS hdd_set_idle_ps_config(struct hdd_context *hdd_ctx, bool val) +{ + QDF_STATUS status; + + hdd_debug("Enter Val %d", val); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_debug("Skipping powersave in FTM"); + return QDF_STATUS_SUCCESS; + } + + if (hdd_ctx->imps_enabled == val) { + hdd_nofl_debug("Already in the requested power state:%d", val); + return QDF_STATUS_SUCCESS; + } + + status = sme_set_idle_powersave_config(val); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Fail to Set Idle PS Config val %d", val); + return status; + } + + hdd_ctx->imps_enabled = val; + + return status; +} + +/** + * hdd_set_fine_time_meas_cap() - set fine timing measurement capability + * @hdd_ctx: HDD context + * + * This function is used to pass fine timing measurement capability coming + * from INI to SME. This function make sure that configure INI is supported + * by the device. Use bit mask to mask out the unsupported capabilities. + * + * Return: None + */ +static void hdd_set_fine_time_meas_cap(struct hdd_context *hdd_ctx) +{ + uint32_t capability = 0; + + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, &capability); + ucfg_wifi_pos_set_ftm_cap(hdd_ctx->psoc, capability); + hdd_debug("fine time meas capability - Enabled: %04x", capability); +} + +/** + * hdd_set_oem_6g_supported() - set oem 6g support enabled/disable + * @hdd_ctx: HDD context + * + * This function is used to pass oem 6g support enabled/disable value + * coming from INI to SME. This function make sure that configure + * INI is supported by the device. + * + * Return: None + */ +static void hdd_set_oem_6g_supported(struct hdd_context *hdd_ctx) +{ + bool oem_6g_disable = true; + bool is_reg_6g_support, set_wifi_pos_6g_disabled; + + ucfg_mlme_get_oem_6g_supported(hdd_ctx->psoc, &oem_6g_disable); + is_reg_6g_support = wlan_reg_is_6ghz_supported(hdd_ctx->psoc); + set_wifi_pos_6g_disabled = (oem_6g_disable || !is_reg_6g_support); + + /** + * Host uses following truth table to set wifi pos 6Ghz disable in + * ucfg_wifi_pos_set_oem_6g_supported(). + * ----------------------------------------------------------------- + * oem_6g_disable INI value | reg domain 6G support | Disable 6Ghz | + * ----------------------------------------------------------------- + * 1 | 1 | 1 | + * 1 | 0 | 1 | + * 0 | 1 | 0 | + * 0 | 0 | 1 | + * ----------------------------------------------------------------- + */ + ucfg_wifi_pos_set_oem_6g_supported(hdd_ctx->psoc, + set_wifi_pos_6g_disabled); + hdd_debug("oem 6g support is - %s", + set_wifi_pos_6g_disabled ? "Disbaled" : "Enabled"); +} + +/** + * hdd_convert_string_to_u8_array() - used to convert string into u8 array + * @str: String to be converted + * @hex_array: Array where converted value is stored + * @len: Length of the populated array + * @array_max_len: Maximum length of the array + * @to_hex: true, if conversion required for hex string + * + * This API is called to convert string (each byte separated by + * a comma) into an u8 array + * + * Return: QDF_STATUS + */ + +static QDF_STATUS hdd_convert_string_to_array(char *str, uint8_t *array, + uint8_t *len, uint16_t array_max_len, bool to_hex) +{ + char *format, *s = str; + + if (!str || !array || !len) + return QDF_STATUS_E_INVAL; + + format = (to_hex) ? "%02x" : "%d"; + + *len = 0; + while ((s) && (*len < array_max_len)) { + int val; + /* Increment length only if sscanf successfully extracted + * one element. Any other return value means error. + * Ignore it. + */ + if (sscanf(s, format, &val) == 1) { + array[*len] = (uint8_t) val; + *len += 1; + } + + s = strpbrk(s, ","); + if (s) + s++; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_string_to_u8_array(char *str, uint8_t *array, + uint8_t *len, uint16_t array_max_len) +{ + return hdd_convert_string_to_array(str, array, len, + array_max_len, false); +} + +/** + * hdd_hex_string_to_u16_array() - convert a hex string to a uint16 array + * @str: input string + * @int_array: pointer to input array of type uint16 + * @len: pointer to number of elements which the function adds to the array + * @int_array_max_len: maximum number of elements in input uint16 array + * + * This function is used to convert a space separated hex string to an array of + * uint16_t. For example, an input string str = "a b c d" would be converted to + * a unint16 array, int_array = {0xa, 0xb, 0xc, 0xd}, *len = 4. + * This assumes that input value int_array_max_len >= 4. + * + * Return: QDF_STATUS_SUCCESS - if the conversion is successful + * non zero value - if the conversion is a failure + */ +QDF_STATUS hdd_hex_string_to_u16_array(char *str, + uint16_t *int_array, uint8_t *len, uint8_t int_array_max_len) +{ + char *s = str; + uint32_t val = 0; + + if (!str || !int_array || !len) + return QDF_STATUS_E_INVAL; + + hdd_debug("str %pK intArray %pK intArrayMaxLen %d", + s, int_array, int_array_max_len); + + *len = 0; + + while ((s) && (*len < int_array_max_len)) { + /* + * Increment length only if sscanf successfully extracted one + * element. Any other return value means error. Ignore it. + */ + if (sscanf(s, "%x", &val) == 1) { + int_array[*len] = (uint16_t) val; + hdd_debug("s %pK val %x intArray[%d]=0x%x", + s, val, *len, int_array[*len]); + *len += 1; + } + s = strpbrk(s, " "); + if (s) + s++; + } + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_update_config_cfg() - API to update INI setting based on hw/fw caps + * @hdd_ctx: pointer to hdd_ctx + * + * This API reads the cfg file which is updated with hardware/firmware + * capabilities and intersect it with INI setting provided by user. After + * taking intersection it adjust cfg it self. For example, if user has enabled + * RX LDPC through INI but hardware/firmware doesn't support it then disable + * it in CFG file here. + * + * Return: true or false based on outcome. + */ +bool hdd_update_config_cfg(struct hdd_context *hdd_ctx) +{ + bool status = true; + + /* + * During the initialization both 2G and 5G capabilities should be same. + * So read 5G HT capablity and update 2G and 5G capablities. + */ + + if (0 != hdd_update_he_cap_in_cfg(hdd_ctx)) { + status = false; + hdd_err("Couldn't set HE CAP in cfg"); + } + + return status; +} + +/** + * hdd_set_policy_mgr_user_cfg() -initializes the policy manager + * configuration parameters + * + * @hdd_ctx: the pointer to hdd context + * + * Return: QDF_STATUS_SUCCESS if configuration is correctly applied, + * otherwise the appropriate QDF_STATUS would be returned + */ +QDF_STATUS hdd_set_policy_mgr_user_cfg(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + struct policy_mgr_user_cfg *user_cfg; + + user_cfg = qdf_mem_malloc(sizeof(*user_cfg)); + if (!user_cfg) { + hdd_err("unable to allocate user_cfg"); + return QDF_STATUS_E_NOMEM; + } + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, + &user_cfg->enable2x2); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + user_cfg->sub_20_mhz_enabled = cds_is_sub_20_mhz_enabled(); + status = policy_mgr_set_user_cfg(hdd_ctx->psoc, user_cfg); + qdf_mem_free(user_cfg); + + return status; +} + +eCsrRoamWmmUserModeType hdd_to_csr_wmm_mode(uint8_t mode) +{ + switch (mode) { + case HDD_WMM_USER_MODE_QBSS_ONLY: + return eCsrRoamWmmQbssOnly; + case HDD_WMM_USER_MODE_NO_QOS: + return eCsrRoamWmmNoQos; + case HDD_WMM_USER_MODE_AUTO: + default: + return eCsrRoamWmmAuto; + } +} + +static QDF_STATUS +hdd_set_sme_cfgs_related_to_plcy_mgr(struct hdd_context *hdd_ctx, + struct sme_config_params *sme_cfg) +{ + uint8_t mcc_to_scc_switch = 0, is_force_1x1 = 0, allow_diff_bi = 0; + uint8_t conc_rule1 = 0, conc_rule2 = 0, sta_cxn_5g = 0; + + if (QDF_STATUS_SUCCESS != + ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc, + &mcc_to_scc_switch)) { + hdd_err("can't get mcc to scc switch"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.cc_switch_mode = mcc_to_scc_switch; + + if (QDF_STATUS_SUCCESS != + ucfg_policy_mgr_get_conc_rule1(hdd_ctx->psoc, + &conc_rule1)) { + hdd_err("can't get conc rule1"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.conc_custom_rule1 = conc_rule1; + + if (QDF_STATUS_SUCCESS != + ucfg_policy_mgr_get_conc_rule2(hdd_ctx->psoc, + &conc_rule2)) { + hdd_err("can't get conc rule2"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.conc_custom_rule2 = conc_rule2; + + if (QDF_STATUS_SUCCESS != + ucfg_policy_mgr_get_sta_cxn_5g_band(hdd_ctx->psoc, + &sta_cxn_5g)) { + hdd_err("can't get conc rule2"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.is_sta_connection_in_5gz_enabled = sta_cxn_5g; + + if (QDF_STATUS_SUCCESS != + ucfg_policy_mgr_get_force_1x1(hdd_ctx->psoc, + &is_force_1x1)) { + hdd_err("can't get force 1x1 flag"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.is_force_1x1 = is_force_1x1; + + if (QDF_STATUS_SUCCESS != + ucfg_policy_mgr_get_allow_mcc_go_diff_bi(hdd_ctx->psoc, + &allow_diff_bi)) { + hdd_err("can't get allow mcc go diff BI flag"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.fAllowMCCGODiffBI = allow_diff_bi; + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +static QDF_STATUS hdd_set_sap_mcc_chnl_avoid(struct sme_config_params *sme_cfg, + uint8_t val) +{ + sme_cfg->csr_config.sap_channel_avoidance = val; + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS hdd_set_sap_mcc_chnl_avoid(struct sme_config_params *sme_cfg, + uint8_t val) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +static +QDF_STATUS hdd_set_sme_cfgs_related_to_mlme(struct hdd_context *hdd_ctx, + struct sme_config_params *sme_cfg) +{ + QDF_STATUS status; + uint8_t wmm_mode = 0, enable_mcc = 0, sap_mcc_avoid = 0; + uint8_t mcc_rts_cts = 0, mcc_bcast_prob_rsp = 0; + uint32_t mcast_mcc_rest_time = 0; + bool b80211e_enabled = 0; + + status = ucfg_mlme_get_80211e_is_enabled(hdd_ctx->psoc, + &b80211e_enabled); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get b80211e_enabled failed"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.Is11eSupportEnabled = b80211e_enabled; + + status = ucfg_mlme_get_wmm_mode(hdd_ctx->psoc, &wmm_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get wmm_mode failed"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.WMMSupportMode = hdd_to_csr_wmm_mode(wmm_mode); + hdd_debug("wmm_mode=%d 802_11e_enabled=%d", wmm_mode, b80211e_enabled); + + status = ucfg_mlme_get_mcc_feature(hdd_ctx->psoc, &enable_mcc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("ucfg_mlme_get_mcc_feature fail, use def"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.fEnableMCCMode = enable_mcc; + + status = ucfg_mlme_get_mcc_rts_cts_prot(hdd_ctx->psoc, &mcc_rts_cts); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("ucfg_mlme_get_mcc_rts_cts_prot fail, use def"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.mcc_rts_cts_prot_enable = mcc_rts_cts; + + status = ucfg_mlme_get_mcc_bcast_prob_resp(hdd_ctx->psoc, + &mcc_bcast_prob_rsp); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("ucfg_mlme_get_mcc_bcast_prob_resp fail, use def"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.mcc_bcast_prob_resp_enable = mcc_bcast_prob_rsp; + + status = ucfg_mlme_get_sta_miracast_mcc_rest_time(hdd_ctx->psoc, + &mcast_mcc_rest_time); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("ucfg_mlme_get_sta_miracast_mcc_rest_time, use def"); + return QDF_STATUS_E_FAILURE; + } + sme_cfg->csr_config.f_sta_miracast_mcc_rest_time_val = + mcast_mcc_rest_time; + status = ucfg_mlme_get_sap_mcc_chnl_avoid(hdd_ctx->psoc, + &sap_mcc_avoid); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("ucfg_mlme_get_sap_mcc_chnl_avoid, use def"); + return QDF_STATUS_E_FAILURE; + } + status = hdd_set_sap_mcc_chnl_avoid(sme_cfg, sap_mcc_avoid); + + return status; +} + +/** + * hdd_set_sme_config() -initializes the sme configuration parameters + * + * @hdd_ctx: the pointer to hdd context + * + * Return: QDF_STATUS_SUCCESS if configuration is correctly applied, + * otherwise the appropriate QDF_STATUS would be returned + */ +QDF_STATUS hdd_set_sme_config(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct sme_config_params *sme_config; + mac_handle_t mac_handle = hdd_ctx->mac_handle; + bool roam_scan_enabled; + bool enable_dfs_scan = true; + uint32_t channel_bonding_mode; + +#ifdef FEATURE_WLAN_ESE + bool ese_enabled; +#endif + struct wlan_mlme_ibss_cfg ibss_cfg = {0}; + + struct hdd_config *config = hdd_ctx->config; + + if (QDF_IS_STATUS_ERROR(ucfg_mlme_get_ibss_cfg( + hdd_ctx->psoc, &ibss_cfg))) { + hdd_err("Unable to get IBSS config params"); + return QDF_STATUS_E_FAILURE; + } + + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + if (!sme_config) { + hdd_err("unable to allocate sme_config"); + return QDF_STATUS_E_NOMEM; + } + + /* Config params obtained from the registry + * To Do: set regulatory information here + */ + sme_config->csr_config.phyMode = + hdd_cfg_xlate_to_csr_phy_mode(config->dot11Mode); + sme_update_nud_config(mac_handle, config->enable_nud_tracking); + if (config->dot11Mode == eHDD_DOT11_MODE_abg || + config->dot11Mode == eHDD_DOT11_MODE_11b || + config->dot11Mode == eHDD_DOT11_MODE_11g || + config->dot11Mode == eHDD_DOT11_MODE_11b_ONLY || + config->dot11Mode == eHDD_DOT11_MODE_11g_ONLY) { + sme_config->csr_config.channelBondingMode24GHz = 0; + sme_config->csr_config.channelBondingMode5GHz = 0; + } else { + ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, + &channel_bonding_mode); + sme_config->csr_config.channelBondingMode24GHz = + channel_bonding_mode; + ucfg_mlme_get_channel_bonding_5ghz(hdd_ctx->psoc, + &channel_bonding_mode); + sme_config->csr_config.channelBondingMode5GHz = + channel_bonding_mode; + } + /* Remaining config params not obtained from registry + * On RF EVB beacon using channel 1. + */ + /* This param cannot be configured from INI */ + sme_config->csr_config.send_smps_action = true; + sme_config->csr_config.ad_hoc_ch_freq_5g = ibss_cfg.adhoc_ch_5g; + sme_config->csr_config.ad_hoc_ch_freq_2g = ibss_cfg.adhoc_ch_2g; + sme_config->csr_config.ProprietaryRatesEnabled = 0; + sme_config->csr_config.HeartbeatThresh50 = 40; + ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc, + &enable_dfs_scan); + sme_config->csr_config.fEnableDFSChnlScan = enable_dfs_scan; + sme_config->csr_config.Csr11dinfo.Channels.numChannels = 0; + hdd_set_power_save_offload_config(hdd_ctx); + +#ifdef FEATURE_WLAN_ESE + ucfg_mlme_is_ese_enabled(hdd_ctx->psoc, &ese_enabled); + if (ese_enabled) + ucfg_mlme_set_fast_transition_enabled(hdd_ctx->psoc, true); +#endif + + ucfg_mlme_is_roam_scan_offload_enabled(hdd_ctx->psoc, + &roam_scan_enabled); + + if (!roam_scan_enabled) { + /* Disable roaming in concurrency if roam scan + * offload is disabled + */ + ucfg_mlme_set_fast_roam_in_concurrency_enabled( + hdd_ctx->psoc, false); + } + + sme_config->csr_config.isCoalesingInIBSSAllowed = + ibss_cfg.coalesing_enable; + + /* Update maximum interfaces information */ + sme_config->csr_config.max_intf_count = hdd_ctx->max_intf_count; + + hdd_set_fine_time_meas_cap(hdd_ctx); + hdd_set_oem_6g_supported(hdd_ctx); + + cds_set_multicast_logging(hdd_ctx->config->multicast_host_fw_msgs); + + sme_config->csr_config.sta_roam_policy_params.dfs_mode = + CSR_STA_ROAM_POLICY_DFS_ENABLED; + sme_config->csr_config.sta_roam_policy_params.skip_unsafe_channels = 0; + + status = hdd_set_sme_cfgs_related_to_mlme(hdd_ctx, sme_config); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("hdd_set_sme_cfgs_related_to_mlme() fail: %d", status); + status = hdd_set_sme_cfgs_related_to_plcy_mgr(hdd_ctx, sme_config); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("hdd_set_sme_cfgs_related_to_plcy_mgr fail: %d", + status); + hdd_debug("dot11Mode=%d", config->dot11Mode); + status = sme_update_config(mac_handle, sme_config); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("sme_update_config() failure: %d", status); + + qdf_mem_free(sme_config); + return status; +} + +/** + * hdd_cfg_get_global_config() - get the configuration table + * @hdd_ctx: pointer to hdd context + * @buf: buffer to store the configuration + * @buflen: size of the buffer + * + * Return: none + */ +void hdd_cfg_get_global_config(struct hdd_context *hdd_ctx, char *buf, + int buflen) +{ + ucfg_cfg_store_print(hdd_ctx->psoc); + + snprintf(buf, buflen, + "WLAN configuration written to debug log"); +} + +/** + * hdd_cfg_print_global_config() - print the configuration table + * @hdd_ctx: pointer to hdd context + * + * Return: none + */ +void hdd_cfg_print_global_config(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + status = ucfg_cfg_store_print(hdd_ctx->psoc); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to log cfg ini"); +} + +/** + * hdd_get_pmkid_modes() - returns PMKID mode bits + * @hdd_ctx: the pointer to hdd context + * + * Return: value of pmkid_modes + */ +void hdd_get_pmkid_modes(struct hdd_context *hdd_ctx, + struct pmkid_mode_bits *pmkid_modes) +{ + uint32_t cur_pmkid_modes; + QDF_STATUS status; + + status = ucfg_mlme_get_pmkid_modes(hdd_ctx->psoc, &cur_pmkid_modes); + if (status != QDF_STATUS_SUCCESS) + hdd_err("get pmkid modes fail"); + + pmkid_modes->fw_okc = (cur_pmkid_modes & + CFG_PMKID_MODES_OKC) ? 1 : 0; + pmkid_modes->fw_pmksa_cache = (cur_pmkid_modes & + CFG_PMKID_MODES_PMKSA_CACHING) ? 1 : 0; +} + +static void +hdd_populate_vdev_nss(struct wlan_mlme_nss_chains *user_cfg, + uint8_t tx_nss, + uint8_t rx_nss, + enum nss_chains_band_info band) +{ + user_cfg->rx_nss[band] = rx_nss; + user_cfg->tx_nss[band] = tx_nss; +} + +static QDF_STATUS +hdd_set_nss_params(struct hdd_adapter *adapter, + uint8_t tx_nss, + uint8_t rx_nss) +{ + enum nss_chains_band_info band; + struct wlan_mlme_nss_chains user_cfg; + mac_handle_t mac_handle; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + qdf_mem_zero(&user_cfg, sizeof(user_cfg)); + + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) { + hdd_err("NULL MAC handle"); + return QDF_STATUS_E_INVAL; + } + + for (band = NSS_CHAINS_BAND_2GHZ; band < NSS_CHAINS_BAND_MAX; band++) + hdd_populate_vdev_nss(&user_cfg, tx_nss, + rx_nss, band); + if (QDF_IS_STATUS_ERROR( + sme_nss_chains_update(mac_handle, + &user_cfg, + adapter->vdev_id))) + return QDF_STATUS_E_FAILURE; + + /* Check TDLS status and update antenna mode */ + if ((adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) && + policy_mgr_is_sta_active_connection_exists(hdd_ctx->psoc)) + wlan_hdd_tdls_antenna_switch(hdd_ctx, adapter, rx_nss); + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_update_nss() - Update the number of spatial streams supported. + * Ensure that nss is either 1 or 2 before calling this. + * + * @adapter: the pointer to adapter + * @tx_nss: the Tx number of spatial streams to be updated + * @rx_nss: the Rx number of spatial streams to be updated + * + * This function is used to modify the number of spatial streams + * supported when not in connected state. + * + * Return: QDF_STATUS_SUCCESS if nss is correctly updated, + * otherwise QDF_STATUS_E_FAILURE would be returned + */ +QDF_STATUS hdd_update_nss(struct hdd_adapter *adapter, uint8_t tx_nss, + uint8_t rx_nss) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t rx_supp_data_rate, tx_supp_data_rate; + bool status = true; + QDF_STATUS qdf_status; + qdf_size_t val_len; + struct mlme_ht_capabilities_info ht_cap_info; + uint8_t mcs_set[SIZE_OF_SUPPORTED_MCS_SET] = {0}; + uint8_t mcs_set_temp[SIZE_OF_SUPPORTED_MCS_SET]; + uint8_t enable2x2; + mac_handle_t mac_handle; + bool bval = 0; + uint8_t band, max_supp_nss; + + if ((tx_nss == 2 || rx_nss == 2) && (hdd_ctx->num_rf_chains != 2)) { + hdd_err("No support for 2 spatial streams"); + return QDF_STATUS_E_INVAL; + } + + if (tx_nss > MAX_VDEV_NSS || rx_nss > MAX_VDEV_NSS) { + hdd_debug("Cannot support tx_nss: %d rx_nss: %d", tx_nss, + rx_nss); + return QDF_STATUS_E_INVAL; + } + + qdf_status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("unable to get vht_enable2x2"); + return QDF_STATUS_E_FAILURE; + } + + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) { + hdd_err("NULL MAC handle"); + return QDF_STATUS_E_INVAL; + } + max_supp_nss = MAX_VDEV_NSS; + + /* + * If FW is supporting the dynamic nss update, this command is meant to + * be per vdev, so update only the ini params of that particular vdev + * and not the global param enable2x2 + */ + if (hdd_ctx->dynamic_nss_chains_support) { + if (hdd_is_vdev_in_conn_state(adapter)) + return hdd_set_nss_params(adapter, tx_nss, rx_nss); + hdd_debug("Vdev %d in disconnect state, changing ini nss params", + adapter->vdev_id); + if (!bval) { + hdd_err("Nss in 1x1, no change required, 2x2 mode disabled"); + return QDF_STATUS_E_FAILURE; + } + + for (band = NSS_CHAINS_BAND_2GHZ; band < NSS_CHAINS_BAND_MAX; + band++) { + /* This API will change the global ini in mlme cfg */ + sme_update_nss_in_mlme_cfg(mac_handle, rx_nss, tx_nss, + adapter->device_mode, band); + /* + * This API will change the vdev nss params in mac + * context + */ + sme_update_vdev_type_nss(mac_handle, max_supp_nss, + band); + } + /* + * This API will change the ini and dynamic nss params in + * mlme vdev priv obj. + */ + hdd_store_nss_chains_cfg_in_vdev(adapter); + + return QDF_STATUS_SUCCESS; + } + + /* + * The code below is executed only when fw doesn't support dynamic + * update of nss and chains per vdev feature, for the upcoming + * connection + */ + enable2x2 = (rx_nss == 2) ? 1 : 0; + + if (bval == enable2x2) { + hdd_debug("NSS same as requested"); + return QDF_STATUS_SUCCESS; + } + + if (sme_is_any_session_in_connected_state(mac_handle)) { + hdd_err("Connected sessions present, Do not change NSS"); + return QDF_STATUS_E_INVAL; + } + + qdf_status = ucfg_mlme_set_vht_enable2x2(hdd_ctx->psoc, enable2x2); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to set vht_enable2x2"); + return QDF_STATUS_E_FAILURE; + } + + if (tx_nss == 1 && rx_nss == 2) { + /* 1x2 */ + rx_supp_data_rate = VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_2_2; + tx_supp_data_rate = VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + } else if (enable2x2) { + /* 2x2 */ + rx_supp_data_rate = VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_2_2; + tx_supp_data_rate = VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_2_2; + } else { + /* 1x1 */ + rx_supp_data_rate = VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + tx_supp_data_rate = VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + } + + /* Update Rx Highest Long GI data Rate */ + qdf_status = + ucfg_mlme_cfg_set_vht_rx_supp_data_rate(hdd_ctx->psoc, + rx_supp_data_rate); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to set rx_supp_data_rate"); + status = false; + } + /* Update Tx Highest Long GI data Rate */ + qdf_status = + ucfg_mlme_cfg_set_vht_tx_supp_data_rate(hdd_ctx->psoc, + tx_supp_data_rate); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to set tx_supp_data_rate"); + status = false; + } + + qdf_status = ucfg_mlme_get_ht_cap_info(hdd_ctx->psoc, &ht_cap_info); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to get HT Cap info"); + goto skip_ht_cap_update; + } + + if (!(hdd_ctx->ht_tx_stbc_supported && enable2x2)) { + ht_cap_info.tx_stbc = 0; + } else { + qdf_status = + ucfg_mlme_cfg_get_vht_tx_stbc(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to get vht_tx_stbc"); + ht_cap_info.tx_stbc = bval; + } + } + + qdf_status = ucfg_mlme_set_ht_cap_info(hdd_ctx->psoc, ht_cap_info); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Could not set the HT_CAP_INFO"); + } +skip_ht_cap_update: + qdf_status = ucfg_mlme_update_nss_vht_cap(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to set update_nss_vht_cap"); + status = false; + } + +#define WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES 0xff + val_len = SIZE_OF_SUPPORTED_MCS_SET; + qdf_status = ucfg_mlme_get_supported_mcs_set(hdd_ctx->psoc, + mcs_set_temp, + &val_len); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + mcs_set[0] = mcs_set_temp[0]; + if (enable2x2) + for (val_len = 0; val_len < rx_nss; val_len++) + mcs_set[val_len] = + WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES; + if (ucfg_mlme_set_supported_mcs_set( + hdd_ctx->psoc, mcs_set, + (qdf_size_t)SIZE_OF_SUPPORTED_MCS_SET) == + QDF_STATUS_E_FAILURE) { + status = false; + hdd_err("Could not pass on MCS SET to CFG"); + } + } else { + status = false; + hdd_err("Could not get MCS SET from CFG"); + } + sme_update_he_cap_nss(mac_handle, adapter->vdev_id, rx_nss); +#undef WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES + + if (QDF_STATUS_SUCCESS != sme_update_nss(mac_handle, rx_nss)) + status = false; + + hdd_set_policy_mgr_user_cfg(hdd_ctx); + + return (status == false) ? QDF_STATUS_E_FAILURE : QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_get_tx_nss(struct hdd_adapter *adapter, uint8_t *tx_nss) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_mlme_nss_chains *dynamic_cfg; + enum band_info operating_band; + uint8_t proto_generic_nss; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return QDF_STATUS_E_INVAL; + + proto_generic_nss = wlan_vdev_mlme_get_nss(vdev); + if (hdd_ctx->dynamic_nss_chains_support) { + dynamic_cfg = mlme_get_dynamic_vdev_config(vdev); + if (!dynamic_cfg) { + hdd_err("nss chain dynamic config NULL"); + hdd_objmgr_put_vdev(vdev); + return QDF_STATUS_E_INVAL; + } + if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + operating_band = hdd_get_sap_operating_band( + adapter->hdd_ctx); + } else { + operating_band = hdd_conn_get_connected_band( + &adapter->session.station); + } + switch (operating_band) { + case BAND_2G: + *tx_nss = dynamic_cfg->tx_nss[NSS_CHAINS_BAND_2GHZ]; + break; + case BAND_5G: + *tx_nss = dynamic_cfg->tx_nss[NSS_CHAINS_BAND_5GHZ]; + break; + default: + *tx_nss = proto_generic_nss; + } + if (*tx_nss > proto_generic_nss) + *tx_nss = proto_generic_nss; + } else + *tx_nss = proto_generic_nss; + hdd_objmgr_put_vdev(vdev); + + return status; +} + +QDF_STATUS hdd_get_rx_nss(struct hdd_adapter *adapter, uint8_t *rx_nss) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_mlme_nss_chains *dynamic_cfg; + enum band_info operating_band; + uint8_t proto_generic_nss; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return QDF_STATUS_E_INVAL; + + proto_generic_nss = wlan_vdev_mlme_get_nss(vdev); + if (hdd_ctx->dynamic_nss_chains_support) { + dynamic_cfg = mlme_get_dynamic_vdev_config(vdev); + if (!dynamic_cfg) { + hdd_err("nss chain dynamic config NULL"); + hdd_objmgr_put_vdev(vdev); + return QDF_STATUS_E_INVAL; + } + if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + operating_band = hdd_get_sap_operating_band( + adapter->hdd_ctx); + } else { + operating_band = hdd_conn_get_connected_band( + &adapter->session.station); + } + switch (operating_band) { + case BAND_2G: + *rx_nss = dynamic_cfg->rx_nss[NSS_CHAINS_BAND_2GHZ]; + break; + case BAND_5G: + *rx_nss = dynamic_cfg->rx_nss[NSS_CHAINS_BAND_5GHZ]; + break; + default: + *rx_nss = proto_generic_nss; + } + if (*rx_nss > proto_generic_nss) + *rx_nss = proto_generic_nss; + } else + *rx_nss = proto_generic_nss; + hdd_objmgr_put_vdev(vdev); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg80211.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg80211.c new file mode 100644 index 0000000000000000000000000000000000000000..2a8d403f4655a1b2b50886964e9e6ead72cf7bee --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg80211.c @@ -0,0 +1,24788 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_cfg80211.c + * + * WLAN Host Device Driver cfg80211 APIs implementation + * + */ + +#include +#include +#include +#include +#include +#include +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include "sir_params.h" +#include "dot11f.h" +#include "wlan_hdd_assoc.h" +#include "wlan_hdd_wext.h" +#include "sme_api.h" +#include "sme_power_save_api.h" +#include "wlan_hdd_p2p.h" +#include "wlan_hdd_cfg80211.h" +#include "wlan_hdd_hostapd.h" +#include "wlan_hdd_softap_tx_rx.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_power.h" +#include "wlan_hdd_trace.h" +#include "wlan_hdd_tx_rx.h" +#include "qdf_str.h" +#include "qdf_trace.h" +#include "qdf_types.h" +#include "cds_utils.h" +#include "cds_sched.h" +#include "wlan_hdd_scan.h" +#include +#include "wlan_hdd_tdls.h" +#include "wlan_hdd_wmm.h" +#include "wma_types.h" +#include "wma.h" +#include "wma_twt.h" +#include "wlan_hdd_misc.h" +#include "wlan_hdd_nan.h" +#include "wlan_logging_sock_svc.h" +#include "sap_api.h" +#include "csr_api.h" +#include "pld_common.h" +#include "wmi_unified_param.h" + +#include +#include +#include + +#include "wlan_hdd_ext_scan.h" + +#include "wlan_hdd_stats.h" +#include "cds_api.h" +#include "wlan_policy_mgr_api.h" +#include "qwlan_version.h" + +#include "wlan_hdd_ocb.h" +#include "wlan_hdd_tsf.h" + +#include "wlan_hdd_subnet_detect.h" +#include +#include "wlan_hdd_lpass.h" +#include "wlan_hdd_nan_datapath.h" +#include "wlan_hdd_disa.h" +#include "wlan_osif_request_manager.h" +#include "wlan_hdd_he.h" +#ifdef FEATURE_WLAN_APF +#include "wlan_hdd_apf.h" +#endif +#include "wlan_hdd_fw_state.h" +#include "wlan_hdd_mpta_helper.h" + +#include +#include +#include +#include "wlan_pmo_ucfg_api.h" +#include "os_if_wifi_pos.h" +#include "wlan_utility.h" +#include "wlan_reg_ucfg_api.h" +#include "wifi_pos_api.h" +#include "wlan_hdd_spectralscan.h" +#include "wlan_ipa_ucfg_api.h" +#include +#include +#include "wlan_tdls_cfg_api.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_policy_mgr_ucfg.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_mlme_public_struct.h" +#include "wlan_extscan_ucfg_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_pmo_cfg.h" +#include "cfg_ucfg_api.h" + +#include "wlan_crypto_def_i.h" +#include "wlan_crypto_global_api.h" +#include "wlan_nl_to_crypto_params.h" +#include "wlan_crypto_global_def.h" +#include "cdp_txrx_cfg.h" +#include "wlan_hdd_object_manager.h" +#include "nan_ucfg_api.h" +#include "wlan_fwol_ucfg_api.h" +#include "wlan_cfg80211_crypto.h" +#include "wlan_cfg80211_interop_issues_ap.h" +#include "wlan_scan_ucfg_api.h" +#include "wlan_hdd_coex_config.h" +#include "wlan_hdd_bcn_recv.h" +#include "wlan_blm_ucfg_api.h" +#include "wlan_hdd_hw_capability.h" +#include "wlan_hdd_oemdata.h" +#include "os_if_fwol.h" +#include "wlan_hdd_sta_info.h" +#include "sme_api.h" +#include "wlan_hdd_thermal.h" +#include +#include "wlan_hdd_cfr.h" +#include +#include "hif.h" +#include "wlan_hdd_ioctl.h" +#include "wlan_hdd_gpio.h" + +#define g_mode_rates_size (12) +#define a_mode_rates_size (8) + +/** + * rtt_is_initiator - Macro to check if the bitmap has any RTT roles set + * @bitmap: The bitmap to be checked + */ +#define rtt_is_enabled(bitmap) \ + ((bitmap) & (WMI_FW_STA_RTT_INITR | \ + WMI_FW_STA_RTT_RESPR | \ + WMI_FW_AP_RTT_INITR | \ + WMI_FW_AP_RTT_RESPR)) + +/* + * Android CTS verifier needs atleast this much wait time (in msec) + */ +#define MAX_REMAIN_ON_CHANNEL_DURATION (5000) + +/* + * Refer @tCfgProtection structure for definition of the bit map. + * below value is obtained by setting the following bit-fields. + * enable obss, fromllb, overlapOBSS and overlapFromllb protection. + */ +#define IBSS_CFG_PROTECTION_ENABLE_MASK 0x8282 + +#define HDD2GHZCHAN(freq, chan, flag) { \ + .band = HDD_NL80211_BAND_2GHZ, \ + .center_freq = (freq), \ + .hw_value = (chan), \ + .flags = (flag), \ + .max_antenna_gain = 0, \ + .max_power = 0, \ +} + +#define HDD5GHZCHAN(freq, chan, flag) { \ + .band = HDD_NL80211_BAND_5GHZ, \ + .center_freq = (freq), \ + .hw_value = (chan), \ + .flags = (flag), \ + .max_antenna_gain = 0, \ + .max_power = 0, \ +} + +#define HDD_G_MODE_RATETAB(rate, rate_id, flag) \ + { \ + .bitrate = rate, \ + .hw_value = rate_id, \ + .flags = flag, \ + } + +#define IS_DFS_MODE_VALID(mode) ((mode >= DFS_MODE_NONE && \ + mode <= DFS_MODE_DEPRIORITIZE)) +/* + * Number of DPTRACE records to dump when a cfg80211 disconnect with reason + * WLAN_REASON_DEAUTH_LEAVING DEAUTH is received from user-space. + */ +#define WLAN_DEAUTH_DPTRACE_DUMP_COUNT 100 +#ifndef WLAN_CIPHER_SUITE_GCMP +#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 +#endif +#ifndef WLAN_CIPHER_SUITE_GCMP_256 +#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09 +#endif + +static const u32 hdd_gcmp_cipher_suits[] = { + WLAN_CIPHER_SUITE_GCMP, + WLAN_CIPHER_SUITE_GCMP_256, +}; + +static const u32 hdd_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, +#ifdef FEATURE_WLAN_ESE +#define WLAN_CIPHER_SUITE_BTK 0x004096fe /* use for BTK */ +#define WLAN_CIPHER_SUITE_KRK 0x004096ff /* use for KRK */ + WLAN_CIPHER_SUITE_BTK, + WLAN_CIPHER_SUITE_KRK, + WLAN_CIPHER_SUITE_CCMP, +#else + WLAN_CIPHER_SUITE_CCMP, +#endif +#ifdef FEATURE_WLAN_WAPI + WLAN_CIPHER_SUITE_SMS4, +#endif +#ifdef WLAN_FEATURE_11W + WLAN_CIPHER_SUITE_AES_CMAC, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) + WLAN_CIPHER_SUITE_BIP_GMAC_128, + WLAN_CIPHER_SUITE_BIP_GMAC_256, +#endif +#endif +}; + +static const struct ieee80211_channel hdd_channels_2_4_ghz[] = { + HDD2GHZCHAN(2412, 1, 0), + HDD2GHZCHAN(2417, 2, 0), + HDD2GHZCHAN(2422, 3, 0), + HDD2GHZCHAN(2427, 4, 0), + HDD2GHZCHAN(2432, 5, 0), + HDD2GHZCHAN(2437, 6, 0), + HDD2GHZCHAN(2442, 7, 0), + HDD2GHZCHAN(2447, 8, 0), + HDD2GHZCHAN(2452, 9, 0), + HDD2GHZCHAN(2457, 10, 0), + HDD2GHZCHAN(2462, 11, 0), + HDD2GHZCHAN(2467, 12, 0), + HDD2GHZCHAN(2472, 13, 0), + HDD2GHZCHAN(2484, 14, 0), +}; + +static const struct ieee80211_channel hdd_channels_5_ghz[] = { + HDD5GHZCHAN(5180, 36, 0), + HDD5GHZCHAN(5200, 40, 0), + HDD5GHZCHAN(5220, 44, 0), + HDD5GHZCHAN(5240, 48, 0), + HDD5GHZCHAN(5260, 52, 0), + HDD5GHZCHAN(5280, 56, 0), + HDD5GHZCHAN(5300, 60, 0), + HDD5GHZCHAN(5320, 64, 0), + HDD5GHZCHAN(5500, 100, 0), + HDD5GHZCHAN(5520, 104, 0), + HDD5GHZCHAN(5540, 108, 0), + HDD5GHZCHAN(5560, 112, 0), + HDD5GHZCHAN(5580, 116, 0), + HDD5GHZCHAN(5600, 120, 0), + HDD5GHZCHAN(5620, 124, 0), + HDD5GHZCHAN(5640, 128, 0), + HDD5GHZCHAN(5660, 132, 0), + HDD5GHZCHAN(5680, 136, 0), + HDD5GHZCHAN(5700, 140, 0), + HDD5GHZCHAN(5720, 144, 0), + HDD5GHZCHAN(5745, 149, 0), + HDD5GHZCHAN(5765, 153, 0), + HDD5GHZCHAN(5785, 157, 0), + HDD5GHZCHAN(5805, 161, 0), + HDD5GHZCHAN(5825, 165, 0), +}; + +#ifdef WLAN_FEATURE_DSRC +static const struct ieee80211_channel hdd_channels_dot11p[] = { + HDD5GHZCHAN(5852, 170, 0), + HDD5GHZCHAN(5855, 171, 0), + HDD5GHZCHAN(5860, 172, 0), + HDD5GHZCHAN(5865, 173, 0), + HDD5GHZCHAN(5870, 174, 0), + HDD5GHZCHAN(5875, 175, 0), + HDD5GHZCHAN(5880, 176, 0), + HDD5GHZCHAN(5885, 177, 0), + HDD5GHZCHAN(5890, 178, 0), + HDD5GHZCHAN(5895, 179, 0), + HDD5GHZCHAN(5900, 180, 0), + HDD5GHZCHAN(5905, 181, 0), + HDD5GHZCHAN(5910, 182, 0), + HDD5GHZCHAN(5915, 183, 0), + HDD5GHZCHAN(5920, 184, 0), +}; +#else +static const struct ieee80211_channel hdd_etsi13_srd_ch[] = { + HDD5GHZCHAN(5845, 169, 0), + HDD5GHZCHAN(5865, 173, 0), +}; +#endif + +#define band_2_ghz_channels_size sizeof(hdd_channels_2_4_ghz) + +#ifdef WLAN_FEATURE_DSRC +#define band_5_ghz_chanenls_size (sizeof(hdd_channels_5_ghz) + \ + sizeof(hdd_channels_dot11p)) +#else +#define band_5_ghz_chanenls_size (sizeof(hdd_channels_5_ghz) + \ + sizeof(hdd_etsi13_srd_ch)) +#endif + +static struct ieee80211_rate g_mode_rates[] = { + HDD_G_MODE_RATETAB(10, 0x1, 0), + HDD_G_MODE_RATETAB(20, 0x2, 0), + HDD_G_MODE_RATETAB(55, 0x4, 0), + HDD_G_MODE_RATETAB(110, 0x8, 0), + HDD_G_MODE_RATETAB(60, 0x10, 0), + HDD_G_MODE_RATETAB(90, 0x20, 0), + HDD_G_MODE_RATETAB(120, 0x40, 0), + HDD_G_MODE_RATETAB(180, 0x80, 0), + HDD_G_MODE_RATETAB(240, 0x100, 0), + HDD_G_MODE_RATETAB(360, 0x200, 0), + HDD_G_MODE_RATETAB(480, 0x400, 0), + HDD_G_MODE_RATETAB(540, 0x800, 0), +}; + +static struct ieee80211_rate a_mode_rates[] = { + HDD_G_MODE_RATETAB(60, 0x10, 0), + HDD_G_MODE_RATETAB(90, 0x20, 0), + HDD_G_MODE_RATETAB(120, 0x40, 0), + HDD_G_MODE_RATETAB(180, 0x80, 0), + HDD_G_MODE_RATETAB(240, 0x100, 0), + HDD_G_MODE_RATETAB(360, 0x200, 0), + HDD_G_MODE_RATETAB(480, 0x400, 0), + HDD_G_MODE_RATETAB(540, 0x800, 0), +}; + +static struct ieee80211_supported_band wlan_hdd_band_2_4_ghz = { + .channels = NULL, + .n_channels = ARRAY_SIZE(hdd_channels_2_4_ghz), + .band = HDD_NL80211_BAND_2GHZ, + .bitrates = g_mode_rates, + .n_bitrates = g_mode_rates_size, + .ht_cap.ht_supported = 1, + .ht_cap.cap = IEEE80211_HT_CAP_SGI_20 + | IEEE80211_HT_CAP_GRN_FLD + | IEEE80211_HT_CAP_DSSSCCK40 + | IEEE80211_HT_CAP_LSIG_TXOP_PROT + | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40, + .ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .ht_cap.mcs.rx_mask = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ht_cap.mcs.rx_highest = cpu_to_le16(72), + .ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED, +}; + +static struct ieee80211_supported_band wlan_hdd_band_5_ghz = { + .channels = NULL, + .n_channels = ARRAY_SIZE(hdd_channels_5_ghz), + .band = HDD_NL80211_BAND_5GHZ, + .bitrates = a_mode_rates, + .n_bitrates = a_mode_rates_size, + .ht_cap.ht_supported = 1, + .ht_cap.cap = IEEE80211_HT_CAP_SGI_20 + | IEEE80211_HT_CAP_GRN_FLD + | IEEE80211_HT_CAP_DSSSCCK40 + | IEEE80211_HT_CAP_LSIG_TXOP_PROT + | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40, + .ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .ht_cap.mcs.rx_mask = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ht_cap.mcs.rx_highest = cpu_to_le16(72), + .ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED, + .vht_cap.vht_supported = 1, +}; + +#if defined(CONFIG_BAND_6GHZ) && (defined(CFG80211_6GHZ_BAND_SUPPORTED) || \ + (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)) + +static struct ieee80211_channel hdd_channels_6_ghz[NUM_6GHZ_CHANNELS]; + +static struct ieee80211_supported_band wlan_hdd_band_6_ghz = { + .channels = NULL, + .n_channels = 0, + .band = HDD_NL80211_BAND_6GHZ, + .bitrates = a_mode_rates, + .n_bitrates = a_mode_rates_size, +}; + +#define HDD_SET_6GHZCHAN(ch, freq, chan, flag) { \ + (ch).band = HDD_NL80211_BAND_6GHZ; \ + (ch).center_freq = (freq); \ + (ch).hw_value = (chan); \ + (ch).flags = (flag); \ + (ch).max_antenna_gain = 0; \ + (ch).max_power = 0; \ +} + +static void hdd_init_6ghz(struct hdd_context *hdd_ctx) +{ + uint32_t i; + struct wiphy *wiphy = hdd_ctx->wiphy; + struct ieee80211_channel *chlist = hdd_channels_6_ghz; + uint32_t num = ARRAY_SIZE(hdd_channels_6_ghz); + QDF_STATUS status; + uint32_t band_capability; + + hdd_enter(); + + status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_capability); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get MLME Band Capability"); + return; + } + + if (!(band_capability & (BIT(REG_BAND_6G)))) { + hdd_debug("6ghz band not enabled"); + return; + } + + qdf_mem_zero(chlist, sizeof(*chlist) * num); + for (i = 0; i < num; i++) + HDD_SET_6GHZCHAN(chlist[i], 5945 + i * 20, 1 + i * 4, \ + IEEE80211_CHAN_DISABLED); + wiphy->bands[HDD_NL80211_BAND_6GHZ] = &wlan_hdd_band_6_ghz; + wiphy->bands[HDD_NL80211_BAND_6GHZ]->channels = chlist; + wiphy->bands[HDD_NL80211_BAND_6GHZ]->n_channels = num; + + hdd_exit(); +} +#else +static void hdd_init_6ghz(struct hdd_context *hdd_ctx) +{ +} +#endif + +#if defined(CFG80211_IFTYPE_AKM_SUITES_SUPPORT) +/*akm suits supported by sta*/ +static const u32 hdd_sta_akm_suites[] = { + WLAN_AKM_SUITE_8021X, + WLAN_AKM_SUITE_PSK, + WLAN_AKM_SUITE_FT_8021X, + WLAN_AKM_SUITE_FT_PSK, + WLAN_AKM_SUITE_8021X_SHA256, + WLAN_AKM_SUITE_PSK_SHA256, + WLAN_AKM_SUITE_TDLS, + WLAN_AKM_SUITE_SAE, + WLAN_AKM_SUITE_FT_OVER_SAE, + WLAN_AKM_SUITE_EAP_SHA256, + WLAN_AKM_SUITE_EAP_SHA384, + WLAN_AKM_SUITE_FILS_SHA256, + WLAN_AKM_SUITE_FILS_SHA384, + WLAN_AKM_SUITE_FT_FILS_SHA256, + WLAN_AKM_SUITE_FT_FILS_SHA384, + WLAN_AKM_SUITE_OWE, + WLAN_AKM_SUITE_DPP_RSN, + WLAN_AKM_SUITE_FT_EAP_SHA_384, + RSN_AUTH_KEY_MGMT_CCKM, + RSN_AUTH_KEY_MGMT_OSEN, + WAPI_PSK_AKM_SUITE, + WAPI_CERT_AKM_SUITE, +}; + +/*akm suits supported by AP*/ +static const u32 hdd_ap_akm_suites[] = { + WLAN_AKM_SUITE_PSK, + WLAN_AKM_SUITE_SAE, + WLAN_AKM_SUITE_OWE, +}; + +/* This structure contain information what akm suits are + * supported for each mode + */ +static const struct wiphy_iftype_akm_suites + wlan_hdd_akm_suites[] = { + { + .iftypes_mask = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + .akm_suites = hdd_sta_akm_suites, + .n_akm_suites = (sizeof(hdd_sta_akm_suites) / sizeof(u32)), + }, + { + .iftypes_mask = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO), + .akm_suites = hdd_ap_akm_suites, + .n_akm_suites = (sizeof(hdd_ap_akm_suites) / sizeof(u32)), + }, +}; +#endif + +/* This structure contain information what kind of frame are expected in + * TX/RX direction for each kind of interface + */ +static const struct ieee80211_txrx_stypes + wlan_hdd_txrx_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = 0xffff, + .rx = BIT(SIR_MAC_MGMT_ACTION) | + BIT(SIR_MAC_MGMT_PROBE_REQ) | + BIT(SIR_MAC_MGMT_AUTH), + }, + [NL80211_IFTYPE_AP] = { + .tx = 0xffff, + .rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) | + BIT(SIR_MAC_MGMT_REASSOC_REQ) | + BIT(SIR_MAC_MGMT_PROBE_REQ) | + BIT(SIR_MAC_MGMT_DISASSOC) | + BIT(SIR_MAC_MGMT_AUTH) | + BIT(SIR_MAC_MGMT_DEAUTH) | + BIT(SIR_MAC_MGMT_ACTION), + }, + [NL80211_IFTYPE_ADHOC] = { + .tx = 0xffff, + .rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) | + BIT(SIR_MAC_MGMT_REASSOC_REQ) | + BIT(SIR_MAC_MGMT_PROBE_REQ) | + BIT(SIR_MAC_MGMT_DISASSOC) | + BIT(SIR_MAC_MGMT_AUTH) | + BIT(SIR_MAC_MGMT_DEAUTH) | + BIT(SIR_MAC_MGMT_ACTION), + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = 0xffff, + .rx = BIT(SIR_MAC_MGMT_ACTION) | + BIT(SIR_MAC_MGMT_PROBE_REQ), + }, + [NL80211_IFTYPE_P2P_GO] = { + /* This is also same as for SoftAP */ + .tx = 0xffff, + .rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) | + BIT(SIR_MAC_MGMT_REASSOC_REQ) | + BIT(SIR_MAC_MGMT_PROBE_REQ) | + BIT(SIR_MAC_MGMT_DISASSOC) | + BIT(SIR_MAC_MGMT_AUTH) | + BIT(SIR_MAC_MGMT_DEAUTH) | + BIT(SIR_MAC_MGMT_ACTION), + }, +}; + +/* Interface limits and combinations registered by the driver */ + +/* STA ( + STA ) combination */ +static const struct ieee80211_iface_limit + wlan_hdd_sta_iface_limit[] = { + { + .max = 3, /* p2p0 is a STA as well */ + .types = BIT(NL80211_IFTYPE_STATION), + }, +}; + +/* ADHOC (IBSS) limit */ +static const struct ieee80211_iface_limit + wlan_hdd_adhoc_iface_limit[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC), + }, +}; + +/* AP ( + AP ) combination */ +static const struct ieee80211_iface_limit + wlan_hdd_ap_iface_limit[] = { + { + .max = (QDF_MAX_NO_OF_SAP_MODE + SAP_MAX_OBSS_STA_CNT), + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +/* P2P limit */ +static const struct ieee80211_iface_limit + wlan_hdd_p2p_iface_limit[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_GO), + }, +}; + +static const struct ieee80211_iface_limit + wlan_hdd_sta_ap_iface_limit[] = { + { + /* We need 1 extra STA interface for OBSS scan when SAP starts + * with HT40 in STA+SAP concurrency mode + */ + .max = (1 + SAP_MAX_OBSS_STA_CNT), + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = QDF_MAX_NO_OF_SAP_MODE, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +/* STA + P2P combination */ +static const struct ieee80211_iface_limit + wlan_hdd_sta_p2p_iface_limit[] = { + { + /* One reserved for dedicated P2PDEV usage */ + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION) + }, + { + /* Support for two identical (GO + GO or CLI + CLI) + * or dissimilar (GO + CLI) P2P interfaces + */ + .max = 2, + .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT), + }, +}; + +/* STA + AP + P2PGO combination */ +static const struct ieee80211_iface_limit +wlan_hdd_sta_ap_p2pgo_iface_limit[] = { + /* Support for AP+P2PGO interfaces */ + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_GO) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) + } +}; + +/* SAP + P2P combination */ +static const struct ieee80211_iface_limit +wlan_hdd_sap_p2p_iface_limit[] = { + { + /* 1 dedicated for p2p0 which is a STA type */ + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION) + }, + { + /* The p2p interface in SAP+P2P can be GO/CLI. + * The p2p connection can be formed on p2p0 or p2p-p2p0-x. + */ + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) + }, + { + /* SAP+GO to support only one SAP interface */ + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) + } +}; + +/* P2P + P2P combination */ +static const struct ieee80211_iface_limit +wlan_hdd_p2p_p2p_iface_limit[] = { + { + /* 1 dedicated for p2p0 which is a STA type */ + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION) + }, + { + /* The p2p interface in P2P+P2P can be GO/CLI. + * For P2P+P2P, the new interfaces are formed on p2p-p2p0-x. + */ + .max = 2, + .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) + }, +}; + +static const struct ieee80211_iface_limit + wlan_hdd_mon_iface_limit[] = { + { + .max = 3, /* Monitor interface */ + .types = BIT(NL80211_IFTYPE_MONITOR), + }, +}; + +static struct ieee80211_iface_combination + wlan_hdd_iface_combination[] = { + /* STA */ + { + .limits = wlan_hdd_sta_iface_limit, + .num_different_channels = 2, + .max_interfaces = 3, + .n_limits = ARRAY_SIZE(wlan_hdd_sta_iface_limit), + }, + /* ADHOC */ + { + .limits = wlan_hdd_adhoc_iface_limit, + .num_different_channels = 2, + .max_interfaces = 2, + .n_limits = ARRAY_SIZE(wlan_hdd_adhoc_iface_limit), + }, + /* AP */ + { + .limits = wlan_hdd_ap_iface_limit, + .num_different_channels = 2, + .max_interfaces = (SAP_MAX_OBSS_STA_CNT + QDF_MAX_NO_OF_SAP_MODE), + .n_limits = ARRAY_SIZE(wlan_hdd_ap_iface_limit), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) || \ + defined(CFG80211_BEACON_INTERVAL_BACKPORT) + .beacon_int_min_gcd = 1, +#endif + }, + /* P2P */ + { + .limits = wlan_hdd_p2p_iface_limit, + .num_different_channels = 2, + .max_interfaces = 2, + .n_limits = ARRAY_SIZE(wlan_hdd_p2p_iface_limit), + }, + /* STA + AP */ + { + .limits = wlan_hdd_sta_ap_iface_limit, + .num_different_channels = 2, + .max_interfaces = (1 + SAP_MAX_OBSS_STA_CNT + QDF_MAX_NO_OF_SAP_MODE), + .n_limits = ARRAY_SIZE(wlan_hdd_sta_ap_iface_limit), + .beacon_int_infra_match = true, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) || \ + defined(CFG80211_BEACON_INTERVAL_BACKPORT) + .beacon_int_min_gcd = 1, +#endif + }, + /* STA + P2P */ + { + .limits = wlan_hdd_sta_p2p_iface_limit, + .num_different_channels = 2, + /* one interface reserved for P2PDEV dedicated usage */ + .max_interfaces = 4, + .n_limits = ARRAY_SIZE(wlan_hdd_sta_p2p_iface_limit), + .beacon_int_infra_match = true, + }, + /* STA + P2P GO + SAP */ + { + .limits = wlan_hdd_sta_ap_p2pgo_iface_limit, + /* we can allow 3 channels for three different persona + * but due to firmware limitation, allow max 2 concrnt channels. + */ + .num_different_channels = 2, + /* one interface reserved for P2PDEV dedicated usage */ + .max_interfaces = 4, + .n_limits = ARRAY_SIZE(wlan_hdd_sta_ap_p2pgo_iface_limit), + .beacon_int_infra_match = true, + }, + /* SAP + P2P */ + { + .limits = wlan_hdd_sap_p2p_iface_limit, + .num_different_channels = 2, + /* 1-p2p0 + 1-SAP + 1-P2P (on p2p0 or p2p-p2p0-x) */ + .max_interfaces = 3, + .n_limits = ARRAY_SIZE(wlan_hdd_sap_p2p_iface_limit), + .beacon_int_infra_match = true, + }, + /* P2P + P2P */ + { + .limits = wlan_hdd_p2p_p2p_iface_limit, + .num_different_channels = 2, + /* 1-p2p0 + 2-P2P (on p2p-p2p0-x) */ + .max_interfaces = 3, + .n_limits = ARRAY_SIZE(wlan_hdd_p2p_p2p_iface_limit), + .beacon_int_infra_match = true, + }, + /* Monitor */ + { + .limits = wlan_hdd_mon_iface_limit, + .max_interfaces = 3, + .num_different_channels = 2, + .n_limits = ARRAY_SIZE(wlan_hdd_mon_iface_limit), + }, +}; + +static struct cfg80211_ops wlan_hdd_cfg80211_ops; + +#ifdef WLAN_NL80211_TESTMODE +enum wlan_hdd_tm_attr { + WLAN_HDD_TM_ATTR_INVALID = 0, + WLAN_HDD_TM_ATTR_CMD = 1, + WLAN_HDD_TM_ATTR_DATA = 2, + WLAN_HDD_TM_ATTR_STREAM_ID = 3, + WLAN_HDD_TM_ATTR_TYPE = 4, + /* keep last */ + WLAN_HDD_TM_ATTR_AFTER_LAST, + WLAN_HDD_TM_ATTR_MAX = WLAN_HDD_TM_ATTR_AFTER_LAST - 1, +}; + +enum wlan_hdd_tm_cmd { + WLAN_HDD_TM_CMD_WLAN_FTM = 0, + WLAN_HDD_TM_CMD_WLAN_HB = 1, +}; + +#define WLAN_HDD_TM_DATA_MAX_LEN 5000 + +static const struct nla_policy wlan_hdd_tm_policy[WLAN_HDD_TM_ATTR_MAX + 1] = { + [WLAN_HDD_TM_ATTR_CMD] = {.type = NLA_U32}, + [WLAN_HDD_TM_ATTR_DATA] = {.type = NLA_BINARY, + .len = WLAN_HDD_TM_DATA_MAX_LEN}, +}; +#endif /* WLAN_NL80211_TESTMODE */ + +enum wlan_hdd_vendor_ie_access_policy { + WLAN_HDD_VENDOR_IE_ACCESS_NONE = 0, + WLAN_HDD_VENDOR_IE_ACCESS_ALLOW_IF_LISTED, +}; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) +static const struct wiphy_wowlan_support wowlan_support_cfg80211_init = { + .flags = WIPHY_WOWLAN_MAGIC_PKT, + .n_patterns = WOWL_MAX_PTRNS_ALLOWED, + .pattern_min_len = 1, + .pattern_max_len = WOWL_PTRN_MAX_SIZE, +}; +#endif + +/** + * hdd_add_channel_switch_support()- Adds Channel Switch flag if supported + * @flags: Pointer to the flags to Add channel switch flag. + * + * This Function adds Channel Switch support flag, if channel switch is + * supported by kernel. + * Return: void. + */ +#ifdef CHANNEL_SWITCH_SUPPORTED +static inline void hdd_add_channel_switch_support(uint32_t *flags) +{ + *flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; +} +#else +static inline void hdd_add_channel_switch_support(uint32_t *flags) +{ +} +#endif + +#ifdef FEATURE_WLAN_TDLS + +/* TDLS capabilities params */ +#define PARAM_MAX_TDLS_SESSION \ + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS +#define PARAM_TDLS_FEATURE_SUPPORT \ + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED + +/** + * __wlan_hdd_cfg80211_get_tdls_capabilities() - Provide TDLS Capabilities. + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function provides TDLS capabilities + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_get_tdls_capabilities(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int status; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct sk_buff *skb; + uint32_t set = 0; + uint32_t max_num_tdls_sta = 0; + bool tdls_support; + bool tdls_external_control; + bool tdls_sleep_sta_enable; + bool tdls_buffer_sta; + bool tdls_off_channel; + + hdd_enter_dev(wdev->netdev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, (2 * sizeof(u32)) + + NLMSG_HDRLEN); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + goto fail; + } + + if ((cfg_tdls_get_support_enable(hdd_ctx->psoc, &tdls_support) == + QDF_STATUS_SUCCESS) && !tdls_support) { + hdd_debug("TDLS feature not Enabled or Not supported in FW"); + if (nla_put_u32(skb, PARAM_MAX_TDLS_SESSION, 0) || + nla_put_u32(skb, PARAM_TDLS_FEATURE_SUPPORT, 0)) { + hdd_err("nla put fail"); + goto fail; + } + } else { + cfg_tdls_get_external_control(hdd_ctx->psoc, + &tdls_external_control); + cfg_tdls_get_sleep_sta_enable(hdd_ctx->psoc, + &tdls_sleep_sta_enable); + cfg_tdls_get_buffer_sta_enable(hdd_ctx->psoc, + &tdls_buffer_sta); + cfg_tdls_get_off_channel_enable(hdd_ctx->psoc, + &tdls_off_channel); + set = set | WIFI_TDLS_SUPPORT; + set = set | (tdls_external_control ? + WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT : 0); + set = set | (tdls_off_channel ? + WIIF_TDLS_OFFCHANNEL_SUPPORT : 0); + max_num_tdls_sta = cfg_tdls_get_max_peer_count(hdd_ctx->psoc); + + hdd_debug("TDLS Feature supported value %x", set); + if (nla_put_u32(skb, PARAM_MAX_TDLS_SESSION, + max_num_tdls_sta) || + nla_put_u32(skb, PARAM_TDLS_FEATURE_SUPPORT, set)) { + hdd_err("nla put fail"); + goto fail; + } + } + return cfg80211_vendor_cmd_reply(skb); +fail: + if (skb) + kfree_skb(skb); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_get_tdls_capabilities() - Provide TDLS Capabilities. + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function provides TDLS capabilities + * + * Return: 0 on success and errno on failure + */ +static int +wlan_hdd_cfg80211_get_tdls_capabilities(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_tdls_capabilities(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +static uint8_t hdd_get_bw_offset(uint32_t ch_width) +{ + uint8_t bw_offset = 0; + + if (ch_width == CH_WIDTH_40MHZ) + bw_offset = 1 << BW_40_OFFSET_BIT; + else if (ch_width == CH_WIDTH_20MHZ) + bw_offset = 1 << BW_20_OFFSET_BIT; + + return bw_offset; +} + +#else /* !FEATURE_WLAN_TDLS */ + +static inline uint8_t hdd_get_bw_offset(uint32_t ch_width) +{ + return 0; +} + +#endif /* FEATURE_WLAN_TDLS */ + +int wlan_hdd_merge_avoid_freqs(struct ch_avoid_ind_type *destFreqList, + struct ch_avoid_ind_type *srcFreqList) +{ + int i; + uint32_t room; + struct ch_avoid_freq_type *avoid_range = + &destFreqList->avoid_freq_range[destFreqList->ch_avoid_range_cnt]; + + room = CH_AVOID_MAX_RANGE - destFreqList->ch_avoid_range_cnt; + if (srcFreqList->ch_avoid_range_cnt > room) { + hdd_err("avoid freq overflow"); + return -EINVAL; + } + destFreqList->ch_avoid_range_cnt += srcFreqList->ch_avoid_range_cnt; + + for (i = 0; i < srcFreqList->ch_avoid_range_cnt; i++) { + avoid_range->start_freq = + srcFreqList->avoid_freq_range[i].start_freq; + avoid_range->end_freq = + srcFreqList->avoid_freq_range[i].end_freq; + avoid_range++; + } + return 0; +} +/* + * FUNCTION: wlan_hdd_send_avoid_freq_event + * This is called when wlan driver needs to send vendor specific + * avoid frequency range event to userspace + */ +int wlan_hdd_send_avoid_freq_event(struct hdd_context *hdd_ctx, + struct ch_avoid_ind_type *avoid_freq_list) +{ + struct sk_buff *vendor_event; + + hdd_enter(); + + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return -EINVAL; + } + + if (!avoid_freq_list) { + hdd_err("avoid_freq_list is null"); + return -EINVAL; + } + + vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, sizeof(struct ch_avoid_ind_type), + QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return -EINVAL; + } + + memcpy(skb_put(vendor_event, sizeof(struct ch_avoid_ind_type)), + (void *)avoid_freq_list, sizeof(struct ch_avoid_ind_type)); + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + + hdd_exit(); + return 0; +} + +/* + * define short names for the global vendor params + * used by QCA_NL80211_VENDOR_SUBCMD_HANG + */ +#define HANG_REASON_INDEX QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX + +/** + * hdd_convert_hang_reason() - Convert cds recovery reason to vendor specific + * hang reason + * @reason: cds recovery reason + * + * Return: Vendor specific reason code + */ +static enum qca_wlan_vendor_hang_reason +hdd_convert_hang_reason(enum qdf_hang_reason reason) +{ + u32 ret_val; + + switch (reason) { + case QDF_RX_HASH_NO_ENTRY_FOUND: + ret_val = QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND; + break; + case QDF_PEER_DELETION_TIMEDOUT: + ret_val = QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT; + break; + case QDF_PEER_UNMAP_TIMEDOUT: + ret_val = QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT; + break; + case QDF_SCAN_REQ_EXPIRED: + ret_val = QCA_WLAN_HANG_SCAN_REQ_EXPIRED; + break; + case QDF_SCAN_ATTEMPT_FAILURES: + ret_val = QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES; + break; + case QDF_GET_MSG_BUFF_FAILURE: + ret_val = QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE; + break; + case QDF_ACTIVE_LIST_TIMEOUT: + ret_val = QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT; + break; + case QDF_SUSPEND_TIMEOUT: + ret_val = QCA_WLAN_HANG_SUSPEND_TIMEOUT; + break; + case QDF_RESUME_TIMEOUT: + ret_val = QCA_WLAN_HANG_RESUME_TIMEOUT; + break; + case QDF_WMI_EXCEED_MAX_PENDING_CMDS: + ret_val = QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS; + break; + case QDF_AP_STA_CONNECT_REQ_TIMEOUT: + ret_val = QCA_WLAN_HANG_AP_STA_CONNECT_REQ_TIMEOUT; + break; + case QDF_STA_AP_CONNECT_REQ_TIMEOUT: + ret_val = QCA_WLAN_HANG_STA_AP_CONNECT_REQ_TIMEOUT; + break; + case QDF_MAC_HW_MODE_CHANGE_TIMEOUT: + ret_val = QCA_WLAN_HANG_MAC_HW_MODE_CHANGE_TIMEOUT; + break; + case QDF_MAC_HW_MODE_CONFIG_TIMEOUT: + ret_val = QCA_WLAN_HANG_MAC_HW_MODE_CONFIG_TIMEOUT; + break; + case QDF_VDEV_START_RESPONSE_TIMED_OUT: + ret_val = QCA_WLAN_HANG_VDEV_START_RESPONSE_TIMED_OUT; + break; + case QDF_VDEV_RESTART_RESPONSE_TIMED_OUT: + ret_val = QCA_WLAN_HANG_VDEV_RESTART_RESPONSE_TIMED_OUT; + break; + case QDF_VDEV_STOP_RESPONSE_TIMED_OUT: + ret_val = QCA_WLAN_HANG_VDEV_STOP_RESPONSE_TIMED_OUT; + break; + case QDF_VDEV_DELETE_RESPONSE_TIMED_OUT: + ret_val = QCA_WLAN_HANG_VDEV_DELETE_RESPONSE_TIMED_OUT; + break; + case QDF_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT: + ret_val = QCA_WLAN_HANG_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT; + break; + case QDF_WMI_BUF_SEQUENCE_MISMATCH: + ret_val = QCA_WLAN_HANG_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT; + break; + case QDF_HAL_REG_WRITE_FAILURE: + ret_val = QCA_WLAN_HANG_REG_WRITE_FAILURE; + break; + case QDF_SUSPEND_NO_CREDIT: + ret_val = QCA_WLAN_HANG_SUSPEND_NO_CREDIT; + break; + case QCA_HANG_BUS_FAILURE: + ret_val = QCA_WLAN_HANG_BUS_FAILURE; + break; + case QDF_REASON_UNSPECIFIED: + default: + ret_val = QCA_WLAN_HANG_REASON_UNSPECIFIED; + break; + } + return ret_val; +} + +/** + * wlan_hdd_send_hang_reason_event() - Send hang reason to the userspace + * @hdd_ctx: Pointer to hdd context + * @reason: cds recovery reason + * @data: Hang Data + * + * Return: 0 on success or failure reason + */ +int wlan_hdd_send_hang_reason_event(struct hdd_context *hdd_ctx, + enum qdf_hang_reason reason, uint8_t *data, + size_t data_len) +{ + struct sk_buff *vendor_event; + enum qca_wlan_vendor_hang_reason hang_reason; + struct hdd_adapter *sta_adapter; + struct wireless_dev *wdev = NULL; + + hdd_enter(); + + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return -EINVAL; + } + + sta_adapter = hdd_get_adapter(hdd_ctx, QDF_STA_MODE); + if (sta_adapter) + wdev = &(sta_adapter->wdev); + + + vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + wdev, + sizeof(uint32_t) + data_len, + HANG_REASON_INDEX, + GFP_KERNEL); + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return -ENOMEM; + } + + hang_reason = hdd_convert_hang_reason(reason); + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_HANG_REASON, + (uint32_t)hang_reason) || + nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_HANG_REASON_DATA, + data_len, data)) { + hdd_err("QCA_WLAN_VENDOR_ATTR_HANG_REASON put fail"); + kfree_skb(vendor_event); + return -EINVAL; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + + hdd_exit(); + return 0; +} + +#undef HANG_REASON_INDEX + +/** + * wlan_hdd_get_adjacent_chan(): Gets next/previous channel + * with respect to the channel passed. + * @chan: Channel + * @upper: If "true" then next channel is returned or else + * previous channel is returned. + * + * This function returns the next/previous adjacent-channel to + * the channel passed. If "upper = true" then next channel is + * returned else previous is returned. + */ +int wlan_hdd_get_adjacent_chan(uint8_t chan, bool upper) +{ + enum channel_enum ch_idx = wlan_reg_get_chan_enum(chan); + + if (ch_idx == INVALID_CHANNEL) + return -EINVAL; + + if (upper && (ch_idx < (NUM_CHANNELS - 1))) + ch_idx++; + else if (!upper && (ch_idx > CHAN_ENUM_2412)) + ch_idx--; + else + return -EINVAL; + + return WLAN_REG_CH_NUM(ch_idx); +} + +/** + * wlan_hdd_send_avoid_freq_for_dnbs(): Sends list of frequencies to be + * avoided when Do_Not_Break_Stream is active. + * @hdd_ctx: HDD Context + * @op_chan: AP/P2P-GO operating channel + * + * This function sends list of frequencies to be avoided when + * Do_Not_Break_Stream is active. + * To clear the avoid_frequency_list in the application, + * op_chan = 0 can be passed. + * + * Return: 0 on success and errno on failure + */ +int wlan_hdd_send_avoid_freq_for_dnbs(struct hdd_context *hdd_ctx, uint8_t op_chan) +{ + struct ch_avoid_ind_type p2p_avoid_freq_list; + uint8_t min_chan, max_chan; + int ret; + int chan; + + hdd_enter(); + + if (!hdd_ctx) { + hdd_err("invalid param"); + return -EINVAL; + } + + qdf_mem_zero(&p2p_avoid_freq_list, sizeof(struct ch_avoid_ind_type)); + /* + * If channel passed is zero, clear the avoid_freq list in application. + */ + if (!op_chan) { +#ifdef FEATURE_WLAN_CH_AVOID + mutex_lock(&hdd_ctx->avoid_freq_lock); + qdf_mem_zero(&hdd_ctx->dnbs_avoid_freq_list, + sizeof(struct ch_avoid_ind_type)); + if (hdd_ctx->coex_avoid_freq_list.ch_avoid_range_cnt) + memcpy(&p2p_avoid_freq_list, + &hdd_ctx->coex_avoid_freq_list, + sizeof(struct ch_avoid_ind_type)); + mutex_unlock(&hdd_ctx->avoid_freq_lock); +#endif + ret = wlan_hdd_send_avoid_freq_event(hdd_ctx, + &p2p_avoid_freq_list); + if (ret) + hdd_err("wlan_hdd_send_avoid_freq_event error:%d", + ret); + + return ret; + } + + if (WLAN_REG_IS_24GHZ_CH(op_chan)) { + min_chan = WLAN_REG_MIN_24GHZ_CH_NUM; + max_chan = WLAN_REG_MAX_24GHZ_CH_NUM; + } else if (WLAN_REG_IS_5GHZ_CH(op_chan)) { + min_chan = WLAN_REG_MIN_5GHZ_CH_NUM; + max_chan = WLAN_REG_MAX_5GHZ_CH_NUM; + } else { + hdd_err("invalid channel:%d", op_chan); + return -EINVAL; + } + + if ((op_chan > min_chan) && (op_chan < max_chan)) { + p2p_avoid_freq_list.ch_avoid_range_cnt = 2; + p2p_avoid_freq_list.avoid_freq_range[0].start_freq = + wlan_chan_to_freq(min_chan); + + /* Get channel before the op_chan */ + chan = wlan_hdd_get_adjacent_chan(op_chan, false); + if (chan < 0) + return -EINVAL; + p2p_avoid_freq_list.avoid_freq_range[0].end_freq = + wlan_chan_to_freq(chan); + + /* Get channel next to the op_chan */ + chan = wlan_hdd_get_adjacent_chan(op_chan, true); + if (chan < 0) + return -EINVAL; + p2p_avoid_freq_list.avoid_freq_range[1].start_freq = + wlan_chan_to_freq(chan); + + p2p_avoid_freq_list.avoid_freq_range[1].end_freq = + wlan_chan_to_freq(max_chan); + } else if (op_chan == min_chan) { + p2p_avoid_freq_list.ch_avoid_range_cnt = 1; + + chan = wlan_hdd_get_adjacent_chan(op_chan, true); + if (chan < 0) + return -EINVAL; + p2p_avoid_freq_list.avoid_freq_range[0].start_freq = + wlan_chan_to_freq(chan); + + p2p_avoid_freq_list.avoid_freq_range[0].end_freq = + wlan_chan_to_freq(max_chan); + } else { + p2p_avoid_freq_list.ch_avoid_range_cnt = 1; + p2p_avoid_freq_list.avoid_freq_range[0].start_freq = + wlan_chan_to_freq(min_chan); + + chan = wlan_hdd_get_adjacent_chan(op_chan, false); + if (chan < 0) + return -EINVAL; + p2p_avoid_freq_list.avoid_freq_range[0].end_freq = + wlan_chan_to_freq(chan); + } +#ifdef FEATURE_WLAN_CH_AVOID + mutex_lock(&hdd_ctx->avoid_freq_lock); + hdd_ctx->dnbs_avoid_freq_list = p2p_avoid_freq_list; + if (hdd_ctx->coex_avoid_freq_list.ch_avoid_range_cnt) { + ret = wlan_hdd_merge_avoid_freqs(&p2p_avoid_freq_list, + &hdd_ctx->coex_avoid_freq_list); + if (ret) { + mutex_unlock(&hdd_ctx->avoid_freq_lock); + hdd_err("avoid freq merge failed"); + return ret; + } + } + mutex_unlock(&hdd_ctx->avoid_freq_lock); +#endif + ret = wlan_hdd_send_avoid_freq_event(hdd_ctx, &p2p_avoid_freq_list); + if (ret) + hdd_err("wlan_hdd_send_avoid_freq_event error:%d", ret); + + return ret; +} + +/* vendor specific events */ +static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] = { + [QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY + }, + + [QCA_NL80211_VENDOR_SUBCMD_NAN_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_NAN + }, + +#ifdef WLAN_FEATURE_STATS_EXT + [QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_STATS_EXT + }, +#endif /* WLAN_FEATURE_STATS_EXT */ +#ifdef FEATURE_WLAN_EXTSCAN + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + . + subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + . + subcmd + = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + . + subcmd + = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + . + subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + . + subcmd + = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE_INDEX] = { + . + vendor_id + = + QCA_NL80211_VENDOR_ID, + . + subcmd + = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE + }, +#endif /* FEATURE_WLAN_EXTSCAN */ + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + [QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET + }, + [QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET + }, + [QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR + }, + [QCA_NL80211_VENDOR_SUBCMD_LL_RADIO_STATS_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS + }, + [QCA_NL80211_VENDOR_SUBCMD_LL_IFACE_STATS_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS + }, + [QCA_NL80211_VENDOR_SUBCMD_LL_PEER_INFO_STATS_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS + }, + [QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT + }, +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + [QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE_CHANGE_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE + }, + [QCA_NL80211_VENDOR_SUBCMD_DO_ACS_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_DO_ACS + }, +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + [QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH + }, +#endif + [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED + }, + [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED + }, + [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED + }, + [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED + }, + [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED_INDEX] = { + .vendor_id = + QCA_NL80211_VENDOR_ID, + .subcmd = + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED + }, +#ifdef FEATURE_WLAN_EXTSCAN + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_NETWORK_FOUND_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_NETWORK_FOUND + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_PASSPOINT_NETWORK_FOUND_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_PASSPOINT_NETWORK_FOUND + }, + [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST + }, +#endif /* FEATURE_WLAN_EXTSCAN */ + + FEATURE_RSSI_MONITOR_VENDOR_EVENTS + +#ifdef WLAN_FEATURE_TSF + [QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_TSF + }, +#endif + [QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE + }, + [QCA_NL80211_VENDOR_SUBCMD_SCAN_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN + }, + /* OCB events */ + [QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT + }, +#ifdef FEATURE_LFR_SUBNET_DETECTION + [QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG + }, +#endif /*FEATURE_LFR_SUBNET_DETECTION */ + + FEATURE_INTEROP_ISSUES_AP_VENDOR_COMMANDS_INDEX + + [QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_NDP + }, + + [QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP + }, + [QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH + }, + [QCA_NL80211_VENDOR_SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS + }, + [QCA_NL80211_VENDOR_SUBCMD_PWR_SAVE_FAIL_DETECTED_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE + }, + [QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET, + }, + [QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_HANG, + }, + [QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO, + }, + [QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT, + }, + [QCA_NL80211_VENDOR_SUBCMD_NAN_EXT_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_NAN_EXT + }, + [QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES, + }, + + BCN_RECV_FEATURE_VENDOR_EVENTS + [QCA_NL80211_VENDOR_SUBCMD_ROAM_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAM, + }, + [QCA_NL80211_VENDOR_SUBCMD_OEM_DATA_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_OEM_DATA, + }, + [QCA_NL80211_VENDOR_SUBCMD_REQUEST_SAR_LIMITS_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS_EVENT, + }, + [QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO, + } +}; + +/** + * __is_driver_dfs_capable() - get driver DFS capability + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This function is called by userspace to indicate whether or not + * the driver supports DFS offload. + * + * Return: 0 on success, negative errno on failure + */ +static int __is_driver_dfs_capable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + u32 dfs_capability = 0; + struct sk_buff *temp_skbuff; + int ret_val; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter_dev(wdev->netdev); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) || \ + defined(CFG80211_DFS_OFFLOAD_BACKPORT) + dfs_capability = + wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_DFS_OFFLOAD); +#else + dfs_capability = !!(wiphy->flags & WIPHY_FLAG_DFS_OFFLOAD); +#endif + + temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) + + NLMSG_HDRLEN); + + if (temp_skbuff) { + ret_val = nla_put_u32(temp_skbuff, QCA_WLAN_VENDOR_ATTR_DFS, + dfs_capability); + if (ret_val) { + hdd_err("QCA_WLAN_VENDOR_ATTR_DFS put fail"); + kfree_skb(temp_skbuff); + + return ret_val; + } + + return cfg80211_vendor_cmd_reply(temp_skbuff); + } + + hdd_err("dfs capability: buffer alloc fail"); + return -ENOMEM; +} + +/** + * is_driver_dfs_capable() - get driver DFS capability + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This function is called by userspace to indicate whether or not + * the driver supports DFS offload. This is an SSR-protected + * wrapper function. + * + * Return: 0 on success, negative errno on failure + */ +static int is_driver_dfs_capable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __is_driver_dfs_capable(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * wlan_hdd_sap_cfg_dfs_override() - DFS MCC restriction check + * + * @adapter: SAP adapter pointer + * + * DFS in MCC is not supported for Multi bssid SAP mode due to single physical + * radio. So in case of DFS MCC scenario override current SAP given config + * to follow concurrent SAP DFS config + * + * Return: 0 - No DFS issue, 1 - Override done and negative error codes + */ +int wlan_hdd_sap_cfg_dfs_override(struct hdd_adapter *adapter) +{ + struct hdd_adapter *con_sap_adapter; + struct sap_config *sap_config, *con_sap_config; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t con_ch_freq; + + if (!hdd_ctx) { + hdd_err("hdd context is NULL"); + return 0; + } + + /* + * Check if AP+AP case, once primary AP chooses a DFS + * channel secondary AP should always follow primary APs channel + */ + if (!policy_mgr_concurrent_beaconing_sessions_running( + hdd_ctx->psoc)) + return 0; + + con_sap_adapter = hdd_get_con_sap_adapter(adapter, true); + if (!con_sap_adapter) + return 0; + + sap_config = &adapter->session.ap.sap_config; + con_sap_config = &con_sap_adapter->session.ap.sap_config; + con_ch_freq = con_sap_adapter->session.ap.operating_chan_freq; + + if (!wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, con_ch_freq)) + return 0; + + hdd_debug("Only SCC AP-AP DFS Permitted (ch_freq=%d, con_ch_freq=%d)", + sap_config->chan_freq, con_ch_freq); + hdd_debug("Overriding guest AP's channel"); + sap_config->chan_freq = con_ch_freq; + + if (con_sap_config->acs_cfg.acs_mode == true) { + if (con_ch_freq != con_sap_config->acs_cfg.pri_ch_freq && + con_ch_freq != con_sap_config->acs_cfg.ht_sec_ch_freq) { + hdd_err("Primary AP channel config error"); + hdd_err("Operating ch: %d ACS ch freq: %d Sec Freq %d", + con_ch_freq, + con_sap_config->acs_cfg.pri_ch_freq, + con_sap_config->acs_cfg.ht_sec_ch_freq); + return -EINVAL; + } + /* Sec AP ACS info is overwritten with Pri AP due to DFS + * MCC restriction. So free ch list allocated in do_acs + * func for Sec AP and realloc for Pri AP ch list size + */ + if (sap_config->acs_cfg.freq_list) { + qdf_mem_free(sap_config->acs_cfg.freq_list); + sap_config->acs_cfg.freq_list = NULL; + } + if (sap_config->acs_cfg.master_freq_list) { + qdf_mem_free(sap_config->acs_cfg.master_freq_list); + sap_config->acs_cfg.master_freq_list = NULL; + } + + qdf_mem_copy(&sap_config->acs_cfg, + &con_sap_config->acs_cfg, + sizeof(struct sap_acs_cfg)); + + sap_config->acs_cfg.freq_list = + qdf_mem_malloc(sizeof(uint32_t) * + con_sap_config->acs_cfg.ch_list_count); + if (!sap_config->acs_cfg.freq_list) { + sap_config->acs_cfg.ch_list_count = 0; + return -ENOMEM; + } + qdf_mem_copy(sap_config->acs_cfg.freq_list, + con_sap_config->acs_cfg.freq_list, + con_sap_config->acs_cfg.ch_list_count * + sizeof(uint32_t)); + + sap_config->acs_cfg.master_freq_list = + qdf_mem_malloc(sizeof(uint32_t) * + con_sap_config->acs_cfg.master_ch_list_count); + if (!sap_config->acs_cfg.master_freq_list) { + sap_config->acs_cfg.master_ch_list_count = 0; + qdf_mem_free(sap_config->acs_cfg.freq_list); + sap_config->acs_cfg.freq_list = NULL; + return -ENOMEM; + } + qdf_mem_copy(sap_config->acs_cfg.master_freq_list, + con_sap_config->acs_cfg.master_freq_list, + con_sap_config->acs_cfg.master_ch_list_count * + sizeof(uint32_t)); + } else { + sap_config->acs_cfg.pri_ch_freq = con_ch_freq; + if (sap_config->acs_cfg.ch_width > eHT_CHANNEL_WIDTH_20MHZ) + sap_config->acs_cfg.ht_sec_ch_freq = + con_sap_config->sec_ch_freq; + } + + return con_ch_freq; +} + +/** + * wlan_hdd_set_acs_ch_range : Populate ACS hw mode and channel range values + * @sap_cfg: pointer to SAP config struct + * @hw_mode: hw mode retrieved from vendor command buffer + * @ht_enabled: whether HT phy mode is enabled + * @vht_enabled: whether VHT phy mode is enabled + * + * This function populates the ACS hw mode based on the configuration retrieved + * from the vendor command buffer; and sets ACS start and end channel for the + * given band. + * + * Return: 0 if success; -EINVAL if ACS channel list is NULL + */ +static int wlan_hdd_set_acs_ch_range( + struct sap_config *sap_cfg, enum qca_wlan_vendor_acs_hw_mode hw_mode, + bool ht_enabled, bool vht_enabled) +{ + int i; + + if (hw_mode == QCA_ACS_MODE_IEEE80211B) { + sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11b; + sap_cfg->acs_cfg.start_ch_freq = + wlan_reg_ch_to_freq(CHAN_ENUM_2412); + sap_cfg->acs_cfg.end_ch_freq = + wlan_reg_ch_to_freq(CHAN_ENUM_2484); + } else if (hw_mode == QCA_ACS_MODE_IEEE80211G) { + sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11g; + sap_cfg->acs_cfg.start_ch_freq = + wlan_reg_ch_to_freq(CHAN_ENUM_2412); + sap_cfg->acs_cfg.end_ch_freq = + wlan_reg_ch_to_freq(CHAN_ENUM_2472); + } else if (hw_mode == QCA_ACS_MODE_IEEE80211A) { + sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11a; + sap_cfg->acs_cfg.start_ch_freq = + wlan_reg_ch_to_freq(CHAN_ENUM_5180); + sap_cfg->acs_cfg.end_ch_freq = + wlan_reg_ch_to_freq(CHAN_ENUM_5865); + } else if (hw_mode == QCA_ACS_MODE_IEEE80211ANY) { + sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_abg; + sap_cfg->acs_cfg.start_ch_freq = + wlan_reg_ch_to_freq(CHAN_ENUM_2412); + sap_cfg->acs_cfg.end_ch_freq = + wlan_reg_ch_to_freq(CHAN_ENUM_5865); + } + + if (ht_enabled) + sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11n; + + if (vht_enabled) + sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11ac; + + /* Parse ACS Chan list from hostapd */ + if (!sap_cfg->acs_cfg.freq_list) + return -EINVAL; + + sap_cfg->acs_cfg.start_ch_freq = sap_cfg->acs_cfg.freq_list[0]; + sap_cfg->acs_cfg.end_ch_freq = + sap_cfg->acs_cfg.freq_list[sap_cfg->acs_cfg.ch_list_count - 1]; + for (i = 0; i < sap_cfg->acs_cfg.ch_list_count; i++) { + /* avoid channel as start channel */ + if (sap_cfg->acs_cfg.start_ch_freq > + sap_cfg->acs_cfg.freq_list[i] && + sap_cfg->acs_cfg.freq_list[i] != 0) + sap_cfg->acs_cfg.start_ch_freq = + sap_cfg->acs_cfg.freq_list[i]; + if (sap_cfg->acs_cfg.end_ch_freq < + sap_cfg->acs_cfg.freq_list[i]) + sap_cfg->acs_cfg.end_ch_freq = + sap_cfg->acs_cfg.freq_list[i]; + } + + return 0; +} + +static void hdd_update_acs_channel_list(struct sap_config *sap_config, + enum band_info band) +{ + int i, temp_count = 0; + int acs_list_count = sap_config->acs_cfg.ch_list_count; + + for (i = 0; i < acs_list_count; i++) { + if (BAND_2G == band) { + if (WLAN_REG_IS_24GHZ_CH_FREQ( + sap_config->acs_cfg.freq_list[i])) { + sap_config->acs_cfg.freq_list[temp_count] = + sap_config->acs_cfg.freq_list[i]; + temp_count++; + } + } else if (BAND_5G == band) { + if (WLAN_REG_IS_5GHZ_CH_FREQ( + sap_config->acs_cfg.freq_list[i]) || + WLAN_REG_IS_6GHZ_CHAN_FREQ( + sap_config->acs_cfg.freq_list[i])) { + sap_config->acs_cfg.freq_list[temp_count] = + sap_config->acs_cfg.freq_list[i]; + temp_count++; + } + } + } + sap_config->acs_cfg.ch_list_count = temp_count; +} + + +/** + * wlan_hdd_cfg80211_start_acs : Start ACS Procedure for SAP + * @adapter: pointer to SAP adapter struct + * + * This function starts the ACS procedure if there are no + * constraints like MBSSID DFS restrictions. + * + * Return: Status of ACS Start procedure + */ +int wlan_hdd_cfg80211_start_acs(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + struct sap_config *sap_config; + sap_event_cb acs_event_callback; + uint8_t mcc_to_scc_switch = 0; + int status; + QDF_STATUS qdf_status; + + if (!adapter) { + hdd_err("adapter is NULL"); + return -EINVAL; + } + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return status; + + sap_config = &adapter->session.ap.sap_config; + if (!sap_config) { + hdd_err("SAP config is NULL"); + return -EINVAL; + } + if (hdd_ctx->acs_policy.acs_chan_freq) + sap_config->chan_freq = hdd_ctx->acs_policy.acs_chan_freq; + else + sap_config->chan_freq = AUTO_CHANNEL_SELECT; + ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc, + &mcc_to_scc_switch); + /* + * No DFS SCC is allowed in Auto use case. Hence not + * calling DFS override + */ + if (QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION != + mcc_to_scc_switch && + !(policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) && + WLAN_REG_IS_24GHZ_CH_FREQ(sap_config->acs_cfg.end_ch_freq))) { + status = wlan_hdd_sap_cfg_dfs_override(adapter); + if (status < 0) + return status; + + if (status > 0) { + /*notify hostapd about channel override */ + wlan_hdd_cfg80211_acs_ch_select_evt(adapter); + return 0; + } + } + /* When first 2 connections are on the same frequency band, + * then PCL would include only channels from the other + * frequency band on which no connections are active + */ + if ((policy_mgr_get_connection_count(hdd_ctx->psoc) == 2) && + (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211ANY)) { + struct policy_mgr_conc_connection_info *conc_connection_info; + uint32_t i; + + conc_connection_info = policy_mgr_get_conn_info(&i); + if (conc_connection_info[0].mac == + conc_connection_info[1].mac) { + if (!WLAN_REG_IS_24GHZ_CH_FREQ( + sap_config->acs_cfg.pcl_chan_freq[0])) { + sap_config->acs_cfg.band = + QCA_ACS_MODE_IEEE80211A; + hdd_update_acs_channel_list(sap_config, + BAND_5G); + } else { + sap_config->acs_cfg.band = + QCA_ACS_MODE_IEEE80211G; + hdd_update_acs_channel_list(sap_config, + BAND_2G); + } + } + } + status = wlan_hdd_config_acs(hdd_ctx, adapter); + if (status) { + hdd_err("ACS config failed"); + return -EINVAL; + } + + acs_event_callback = hdd_hostapd_sap_event_cb; + + qdf_mem_copy(sap_config->self_macaddr.bytes, + adapter->mac_addr.bytes, sizeof(struct qdf_mac_addr)); + + qdf_status = wlansap_acs_chselect(WLAN_HDD_GET_SAP_CTX_PTR(adapter), + acs_event_callback, + sap_config, adapter->dev); + + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err("ACS channel select failed"); + return -EINVAL; + } + if (sap_is_auto_channel_select(WLAN_HDD_GET_SAP_CTX_PTR(adapter))) + sap_config->acs_cfg.acs_mode = true; + + return 0; +} + +/** + * hdd_update_vendor_pcl_list() - This API will return unsorted pcl list + * @hdd_ctx: hdd context + * @acs_chan_params: external acs channel params + * @sap_config: SAP config + * + * This API provides unsorted pcl list. + * this list is a subset of the valid channel list given by hostapd. + * if channel is not present in pcl, weightage will be given as zero + * + * Return: Zero on success, non-zero on failure + */ +static void hdd_update_vendor_pcl_list(struct hdd_context *hdd_ctx, + struct hdd_vendor_acs_chan_params *acs_chan_params, + struct sap_config *sap_config) +{ + int i, j; + /* + * PCL shall contain only the preferred channels from the + * application. If those channels are not present in the + * driver PCL, then set the weight to zero + */ + for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++) { + acs_chan_params->vendor_pcl_list[i] = + sap_config->acs_cfg.freq_list[i]; + acs_chan_params->vendor_weight_list[i] = 0; + for (j = 0; j < sap_config->acs_cfg.pcl_ch_count; j++) { + if (sap_config->acs_cfg.freq_list[i] == + sap_config->acs_cfg.pcl_chan_freq[j]) { + acs_chan_params->vendor_weight_list[i] = + sap_config->acs_cfg.pcl_channels_weight_list[j]; + break; + } + } + } + acs_chan_params->pcl_count = sap_config->acs_cfg.ch_list_count; +} + +/** + * hdd_update_reg_chan_info : This API contructs channel info + * for all the given channel + * @adapter: pointer to SAP adapter struct + * @channel_count: channel count + * @freq_list: channel frequency (MHz) list + * + * Return: Status of of channel information updation + */ +static int +hdd_update_reg_chan_info(struct hdd_adapter *adapter, + uint32_t channel_count, uint32_t *freq_list) +{ + int i; + struct hdd_channel_info *icv; + struct ch_params ch_params = {0}; + uint8_t bw_offset = 0, chan = 0; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct sap_config *sap_config = &adapter->session.ap.sap_config; + mac_handle_t mac_handle; + uint8_t sub_20_chan_width = 0; + QDF_STATUS status; + + mac_handle = hdd_ctx->mac_handle; + sap_config->channel_info_count = channel_count; + + status = ucfg_mlme_get_sub_20_chan_width(hdd_ctx->psoc, + &sub_20_chan_width); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to get sub_20_chan_width config"); + + for (i = 0; i < channel_count; i++) { + icv = &sap_config->channel_info[i]; + chan = wlan_reg_freq_to_chan(hdd_ctx->pdev, + freq_list[i]); + if (chan == 0) + continue; + + icv->freq = wlan_reg_get_channel_freq(hdd_ctx->pdev, chan); + icv->ieee_chan_number = chan; + icv->max_reg_power = wlan_reg_get_channel_reg_power( + hdd_ctx->pdev, chan); + + /* filling demo values */ + icv->max_radio_power = HDD_MAX_TX_POWER; + icv->min_radio_power = HDD_MIN_TX_POWER; + /* not supported in current driver */ + icv->max_antenna_gain = 0; + + bw_offset = hdd_get_bw_offset(sap_config->acs_cfg.ch_width); + icv->reg_class_id = + wlan_hdd_find_opclass(mac_handle, chan, bw_offset); + + if (WLAN_REG_IS_5GHZ_CH(chan)) { + ch_params.ch_width = sap_config->acs_cfg.ch_width; + wlan_reg_set_channel_params(hdd_ctx->pdev, chan, + 0, &ch_params); + icv->vht_center_freq_seg0 = ch_params.center_freq_seg0; + icv->vht_center_freq_seg1 = ch_params.center_freq_seg1; + } + + icv->flags = 0; + icv->flags = cds_get_vendor_reg_flags(hdd_ctx->pdev, + icv->ieee_chan_number, + sap_config->acs_cfg.ch_width, + sap_config->acs_cfg.is_ht_enabled, + sap_config->acs_cfg.is_vht_enabled, + sub_20_chan_width); + if (icv->flags & IEEE80211_CHAN_PASSIVE) + icv->flagext |= IEEE80211_CHAN_DFS; + + hdd_debug("freq %d flags %d flagext %d ieee %d maxreg %d maxpw %d minpw %d regClass %d antenna %d seg0 %d seg1 %d", + icv->freq, icv->flags, + icv->flagext, icv->ieee_chan_number, + icv->max_reg_power, icv->max_radio_power, + icv->min_radio_power, icv->reg_class_id, + icv->max_antenna_gain, icv->vht_center_freq_seg0, + icv->vht_center_freq_seg1); + } + return 0; +} + +/* Short name for QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO event */ +#define CHAN_INFO_ATTR_FLAGS \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS +#define CHAN_INFO_ATTR_FLAG_EXT \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT +#define CHAN_INFO_ATTR_FREQ \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ +#define CHAN_INFO_ATTR_MAX_REG_POWER \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER +#define CHAN_INFO_ATTR_MAX_POWER \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER +#define CHAN_INFO_ATTR_MIN_POWER \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER +#define CHAN_INFO_ATTR_REG_CLASS_ID \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID +#define CHAN_INFO_ATTR_ANTENNA_GAIN \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN +#define CHAN_INFO_ATTR_VHT_SEG_0 \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 +#define CHAN_INFO_ATTR_VHT_SEG_1 \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 + +#define CHAN_INFO_ATTR_FREQ_VHT_SEG_0 \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 +#define CHAN_INFO_ATTR_FREQ_VHT_SEG_1 \ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 + +/** + * hdd_cfg80211_update_channel_info() - add channel info attributes + * @hdd_ctx: pointer to hdd context + * @skb: pointer to sk buff + * @hdd_ctx: pointer to hdd station context + * @idx: attribute index + * + * Return: Success(0) or reason code for failure + */ +static int32_t +hdd_cfg80211_update_channel_info(struct hdd_context *hdd_ctx, + struct sk_buff *skb, + struct sap_config *sap_config, int idx) +{ + struct nlattr *nla_attr, *channel; + struct hdd_channel_info *icv; + int i; + uint32_t freq_seg_0, freq_seg_1; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + + for (i = 0; i < sap_config->channel_info_count; i++) { + channel = nla_nest_start(skb, i); + if (!channel) + goto fail; + + icv = &sap_config->channel_info[i]; + if (!icv) { + hdd_err("channel info not found"); + goto fail; + } + + freq_seg_0 = wlan_reg_legacy_chan_to_freq( + hdd_ctx->pdev, + icv->vht_center_freq_seg0); + freq_seg_1 = wlan_reg_legacy_chan_to_freq( + hdd_ctx->pdev, + icv->vht_center_freq_seg1); + + if (nla_put_u16(skb, CHAN_INFO_ATTR_FREQ, + icv->freq) || + nla_put_u32(skb, CHAN_INFO_ATTR_FLAGS, + icv->flags) || + nla_put_u32(skb, CHAN_INFO_ATTR_FLAG_EXT, + icv->flagext) || + nla_put_u8(skb, CHAN_INFO_ATTR_MAX_REG_POWER, + icv->max_reg_power) || + nla_put_u8(skb, CHAN_INFO_ATTR_MAX_POWER, + icv->max_radio_power) || + nla_put_u8(skb, CHAN_INFO_ATTR_MIN_POWER, + icv->min_radio_power) || + nla_put_u8(skb, CHAN_INFO_ATTR_REG_CLASS_ID, + icv->reg_class_id) || + nla_put_u8(skb, CHAN_INFO_ATTR_ANTENNA_GAIN, + icv->max_antenna_gain) || + nla_put_u8(skb, CHAN_INFO_ATTR_VHT_SEG_0, + icv->vht_center_freq_seg0) || + nla_put_u8(skb, CHAN_INFO_ATTR_VHT_SEG_1, + icv->vht_center_freq_seg1) || + nla_put_u32(skb, CHAN_INFO_ATTR_FREQ_VHT_SEG_0, + freq_seg_0) || + nla_put_u32(skb, CHAN_INFO_ATTR_FREQ_VHT_SEG_1, + freq_seg_1)) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, channel); + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + hdd_err("nl channel update failed"); + return -EINVAL; +} +#undef CHAN_INFO_ATTR_FLAGS +#undef CHAN_INFO_ATTR_FLAG_EXT +#undef CHAN_INFO_ATTR_FREQ +#undef CHAN_INFO_ATTR_MAX_REG_POWER +#undef CHAN_INFO_ATTR_MAX_POWER +#undef CHAN_INFO_ATTR_MIN_POWER +#undef CHAN_INFO_ATTR_REG_CLASS_ID +#undef CHAN_INFO_ATTR_ANTENNA_GAIN +#undef CHAN_INFO_ATTR_VHT_SEG_0 +#undef CHAN_INFO_ATTR_VHT_SEG_1 + +#undef CHAN_INFO_ATTR_FREQ_VHT_SEG_0 +#undef CHAN_INFO_ATTR_FREQ_VHT_SEG_1 + +/** + * hdd_cfg80211_update_pcl() - add pcl info attributes + * @hdd_ctx: pointer to hdd context + * @skb: pointer to sk buff + * @hdd_ctx: pointer to hdd station context + * @idx: attribute index + * @vendor_pcl_list: PCL list + * @vendor_weight_list: PCL weights + * + * Return: Success(0) or reason code for failure + */ +static int32_t +hdd_cfg80211_update_pcl(struct hdd_context *hdd_ctx, + struct sk_buff *skb, + uint8_t ch_list_count, int idx, + uint32_t *vendor_pcl_list, uint8_t *vendor_weight_list) +{ + struct nlattr *nla_attr, *channel; + int i; + uint8_t chan; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + + for (i = 0; i < ch_list_count; i++) { + channel = nla_nest_start(skb, i); + if (!channel) + goto fail; + + chan = (uint8_t)wlan_reg_freq_to_chan(hdd_ctx->pdev, + vendor_pcl_list[i]); + + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL, chan) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_PCL_FREQ, + vendor_pcl_list[i]) || + nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT, + vendor_weight_list[i])) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, channel); + } + nla_nest_end(skb, nla_attr); + + return 0; +fail: + hdd_err("updating pcl list failed"); + return -EINVAL; +} + +static void hdd_get_scan_band(struct hdd_context *hdd_ctx, + struct sap_config *sap_config, + enum band_info *band) +{ + /* Get scan band */ + if ((sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211B) || + (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211G)) { + *band = BAND_2G; + } else if (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211A) { + *band = BAND_5G; + } else if (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211ANY) { + *band = BAND_ALL; + } +} + +/** + * wlan_hdd_sap_get_valid_channellist() - Get SAPs valid channel list + * @ap_adapter: adapter + * @channel_count: valid channel count + * @freq_list: valid channel frequency (MHz) list + * @band: frequency band + * + * This API returns valid channel list for SAP after removing nol and + * channel which lies outside of configuration. + * + * Return: Zero on success, non-zero on failure + */ +static int wlan_hdd_sap_get_valid_channellist(struct hdd_adapter *adapter, + uint32_t *channel_count, + uint32_t *freq_list, + enum band_info band) +{ + struct sap_config *sap_config; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t pcl_freqs[NUM_CHANNELS] = {0}; + uint32_t chan_count; + uint32_t i; + QDF_STATUS status; + struct wlan_objmgr_pdev *pdev = hdd_ctx->pdev; + + sap_config = &adapter->session.ap.sap_config; + + status = policy_mgr_get_valid_chans(hdd_ctx->psoc, + pcl_freqs, + &chan_count); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get channel list"); + return -EINVAL; + } + + *channel_count = 0; + for (i = 0; i < chan_count; i++) { + if (*channel_count >= NUM_CHANNELS) + break; + + if (band == BAND_2G && + WLAN_REG_IS_24GHZ_CH_FREQ(pcl_freqs[i]) && + !wlan_reg_is_disable_for_freq(pdev, pcl_freqs[i])) { + freq_list[*channel_count] = pcl_freqs[i]; + *channel_count += 1; + } else if (band == BAND_5G && + (WLAN_REG_IS_5GHZ_CH_FREQ(pcl_freqs[i]) || + WLAN_REG_IS_6GHZ_CHAN_FREQ(pcl_freqs[i])) && + !wlan_reg_is_disable_for_freq(pdev, pcl_freqs[i])) { + freq_list[*channel_count] = pcl_freqs[i]; + *channel_count += 1; + } + } + + if (*channel_count == 0) { + hdd_err("no valid channel found"); + return -EINVAL; + } + + return 0; +} + +/** + * hdd_get_external_acs_event_len() - Get event buffer length for external ACS + * @channel_count: number of channels for ACS operation + * + * Return: External ACS event (SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG) buffer length. + */ +static int hdd_get_external_acs_event_len(uint32_t channel_count) +{ + uint32_t len = NLMSG_HDRLEN; + uint32_t i; + + /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON */ + len += nla_total_size(sizeof(u8)); + + /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED */ + len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT */ + len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH */ + len += nla_total_size(sizeof(u8)); + + /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND */ + len += nla_total_size(sizeof(u8)); + + /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE */ + len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST */ + len += nla_total_size(channel_count * sizeof(u32)); + + for (i = 0; i < channel_count; i++) { + /* QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL */ + len += nla_total_size(sizeof(u8)); + + /* QCA_WLAN_VENDOR_ATTR_PCL_FREQ */ + len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT */ + len += nla_total_size(sizeof(u8)); + } + + for (i = 0; i < channel_count; i++) { + /* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ */ + len += nla_total_size(sizeof(u16)); + + /* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS */ + len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT */ + len += nla_total_size(sizeof(u32)); + + /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER */ + len += nla_total_size(sizeof(u8)); + + /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER */ + len += nla_total_size(sizeof(u8)); + + /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER */ + len += nla_total_size(sizeof(u8)); + + /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID */ + len += nla_total_size(sizeof(u8)); + + /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN */ + len += nla_total_size(sizeof(u8)); + + /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 */ + len += nla_total_size(sizeof(u8)); + + /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 */ + len += nla_total_size(sizeof(u8)); + + /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 */ + len += nla_total_size(sizeof(u32)); + + /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 */ + len += nla_total_size(sizeof(u32)); + } + + /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY */ + len += nla_total_size(sizeof(u32)); + + return len; +} + +int hdd_cfg80211_update_acs_config(struct hdd_adapter *adapter, + uint8_t reason) +{ + struct sk_buff *skb = NULL; + struct sap_config *sap_config; + uint32_t channel_count = 0, status = -EINVAL; + uint32_t *freq_list; + uint32_t vendor_pcl_list[NUM_CHANNELS] = {0}; + uint8_t vendor_weight_list[NUM_CHANNELS] = {0}; + struct hdd_vendor_acs_chan_params acs_chan_params; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + enum band_info band = BAND_2G; + eCsrPhyMode phy_mode; + enum qca_wlan_vendor_attr_external_acs_policy acs_policy; + uint32_t i, id; + QDF_STATUS qdf_status; + bool is_external_acs_policy = cfg_default(CFG_EXTERNAL_ACS_POLICY); + uint32_t len; + + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return -EINVAL; + } + + hdd_enter(); + sap_config = &adapter->session.ap.sap_config; + /* When first 2 connections are on the same frequency band, + * then PCL would include only channels from the other + * frequency band on which no connections are active + */ + if ((policy_mgr_get_connection_count(hdd_ctx->psoc) == 2) && + (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211ANY)) { + struct policy_mgr_conc_connection_info *conc_connection_info; + + conc_connection_info = policy_mgr_get_conn_info(&i); + if (conc_connection_info[0].mac == + conc_connection_info[1].mac) { + + if (!WLAN_REG_IS_24GHZ_CH_FREQ( + sap_config->acs_cfg.pcl_chan_freq[0])) { + sap_config->acs_cfg.band = + QCA_ACS_MODE_IEEE80211A; + hdd_update_acs_channel_list(sap_config, + BAND_5G); + } else { + sap_config->acs_cfg.band = + QCA_ACS_MODE_IEEE80211G; + hdd_update_acs_channel_list(sap_config, + BAND_2G); + } + } + } + + hdd_get_scan_band(hdd_ctx, &adapter->session.ap.sap_config, &band); + + freq_list = qdf_mem_malloc(sizeof(uint32_t) * NUM_CHANNELS); + if (!freq_list) + return -ENOMEM; + + if (sap_config->acs_cfg.freq_list) { + /* Copy INI or hostapd provided ACS channel range*/ + for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++) + freq_list[i] = sap_config->acs_cfg.freq_list[i]; + channel_count = sap_config->acs_cfg.ch_list_count; + } else { + /* No channel list provided, copy all valid channels */ + wlan_hdd_sap_get_valid_channellist(adapter, + &channel_count, + freq_list, + band); + } + + sap_config->channel_info = qdf_mem_malloc( + sizeof(struct hdd_channel_info) * + channel_count); + if (!sap_config->channel_info) { + status = -ENOMEM; + goto fail; + } + + hdd_update_reg_chan_info(adapter, channel_count, freq_list); + + /* Get phymode */ + phy_mode = adapter->session.ap.sap_config.acs_cfg.hw_mode; + + len = hdd_get_external_acs_event_len(channel_count); + id = QCA_NL80211_VENDOR_SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG; + skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, &adapter->wdev, + len, id, GFP_KERNEL); + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + status = -ENOMEM; + goto fail; + } + /* + * Application expects pcl to be a subset of channel list + * Remove all channels which are not in channel list from pcl + * and add weight as zero + */ + acs_chan_params.vendor_pcl_list = vendor_pcl_list; + acs_chan_params.vendor_weight_list = vendor_weight_list; + + hdd_update_vendor_pcl_list(hdd_ctx, &acs_chan_params, + sap_config); + + if (acs_chan_params.pcl_count) { + hdd_debug("ACS PCL list: len: %d", + acs_chan_params.pcl_count); + for (i = 0; i < acs_chan_params.pcl_count; i++) + hdd_debug("channel_frequency: %u, weight: %u", + acs_chan_params. + vendor_pcl_list[i], + acs_chan_params. + vendor_weight_list[i]); + } + + qdf_status = ucfg_mlme_get_external_acs_policy(hdd_ctx->psoc, + &is_external_acs_policy); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("get_external_acs_policy failed, set default"); + + if (is_external_acs_policy) { + acs_policy = + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY; + } else { + acs_policy = + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED; + } + /* Update values in NL buffer */ + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON, + reason) || + nla_put_flag(skb, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED) || + nla_put_flag(skb, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT) + || + nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH, + sap_config->acs_cfg.ch_width) || + nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND, + band) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE, + phy_mode) || + nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST, + channel_count * sizeof(uint32_t), freq_list)) { + hdd_err("nla put fail"); + goto fail; + } + status = + hdd_cfg80211_update_pcl(hdd_ctx, skb, + acs_chan_params.pcl_count, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL, + vendor_pcl_list, + vendor_weight_list); + + if (status != 0) + goto fail; + + id = QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO; + + status = hdd_cfg80211_update_channel_info(hdd_ctx, skb, sap_config, id); + if (status != 0) + goto fail; + + status = nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY, + acs_policy); + + if (status != 0) + goto fail; + + cfg80211_vendor_event(skb, GFP_KERNEL); + qdf_mem_free(freq_list); + qdf_mem_free(sap_config->channel_info); + + return 0; +fail: + qdf_mem_free(freq_list); + if (sap_config->channel_info) + qdf_mem_free(sap_config->channel_info); + if (skb) + kfree_skb(skb); + return status; +} + +/** + * hdd_create_acs_timer(): Initialize vendor ACS timer + * @adapter: pointer to SAP adapter struct + * + * This function initializes the vendor ACS timer. + * + * Return: Status of create vendor ACS timer + */ +static int hdd_create_acs_timer(struct hdd_adapter *adapter) +{ + struct hdd_external_acs_timer_context *timer_context; + QDF_STATUS status; + + if (adapter->session.ap.vendor_acs_timer_initialized) + return 0; + + hdd_debug("Starting vendor app based ACS"); + timer_context = qdf_mem_malloc(sizeof(*timer_context)); + if (!timer_context) + return -ENOMEM; + + timer_context->adapter = adapter; + + set_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags); + status = qdf_mc_timer_init(&adapter->session.ap.vendor_acs_timer, + QDF_TIMER_TYPE_SW, + hdd_acs_response_timeout_handler, timer_context); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to initialize acs response timeout timer"); + return -EFAULT; + } + adapter->session.ap.vendor_acs_timer_initialized = true; + return 0; +} + +static const struct nla_policy +wlan_hdd_cfg80211_do_acs_policy[QCA_WLAN_VENDOR_ATTR_ACS_MAX+1] = { + [QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE] = { .type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED] = { .type = NLA_FLAG }, + [QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED] = { .type = NLA_FLAG }, + [QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED] = { .type = NLA_FLAG }, + [QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH] = { .type = NLA_U16 }, + [QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST] = { .type = NLA_UNSPEC }, + [QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST] = { .type = NLA_UNSPEC }, +}; + +int hdd_start_vendor_acs(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int status; + QDF_STATUS qdf_status; + bool is_acs_support_for_dfs_ltecoex = cfg_default(CFG_USER_ACS_DFS_LTE); + + status = hdd_create_acs_timer(adapter); + if (status != 0) { + hdd_err("failed to create acs timer"); + return status; + } + status = hdd_update_acs_timer_reason(adapter, + QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT); + if (status != 0) { + hdd_err("failed to update acs timer reason"); + return status; + } + qdf_status = ucfg_mlme_get_acs_support_for_dfs_ltecoex( + hdd_ctx->psoc, + &is_acs_support_for_dfs_ltecoex); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("get_acs_support_for_dfs_ltecoex failed, set def"); + + if (is_acs_support_for_dfs_ltecoex) + status = qdf_status_to_os_return(wlan_sap_set_vendor_acs( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + true)); + else + status = qdf_status_to_os_return(wlan_sap_set_vendor_acs( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + false)); + + return status; +} + +/** + * hdd_avoid_acs_channels() - Avoid acs channels + * @hdd_ctx: Pointer to the hdd context + * @sap_config: Sap config structure pointer + * + * This function avoids channels from the acs corresponding to + * the frequencies configured in the ini sap_avoid_acs_freq_list + * + * Return: None + */ + +#ifdef SAP_AVOID_ACS_FREQ_LIST +static void hdd_avoid_acs_channels(struct hdd_context *hdd_ctx, + struct sap_config *sap_config) +{ + int i, j, ch_cnt = 0; + uint16_t avoid_acs_freq_list[CFG_VALID_CHANNEL_LIST_LEN]; + uint8_t avoid_acs_freq_list_num; + + ucfg_mlme_get_acs_avoid_freq_list(hdd_ctx->psoc, + avoid_acs_freq_list, + &avoid_acs_freq_list_num); + + for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++) { + for (j = 0; j < avoid_acs_freq_list_num; j++) { + if (sap_config->acs_cfg.freq_list[i] == + avoid_acs_freq_list[j]) { + hdd_debug("skip freq %d", + sap_config->acs_cfg.freq_list[i]); + break; + } + } + if (j == avoid_acs_freq_list_num) + sap_config->acs_cfg.freq_list[ch_cnt++] = + sap_config->acs_cfg.freq_list[i]; + } + sap_config->acs_cfg.ch_list_count = ch_cnt; +} +#else +static void hdd_avoid_acs_channels(struct hdd_context *hdd_ctx, + struct sap_config *sap_config) +{ +} +#endif + +/** + * wlan_hdd_trim_acs_channel_list() - Trims ACS channel list with + * intersection of PCL + * @pcl: preferred channel list + * @pcl_count: Preferred channel list count + * @org_ch_list: ACS channel list from user space + * @org_ch_list_count: ACS channel count from user space + * + * Return: None + */ +static void wlan_hdd_trim_acs_channel_list(uint32_t *pcl, uint8_t pcl_count, + uint32_t *org_freq_list, + uint8_t *org_ch_list_count) +{ + uint16_t i, j, ch_list_count = 0; + + if (*org_ch_list_count >= NUM_CHANNELS) { + hdd_err("org_ch_list_count too big %d", + *org_ch_list_count); + return; + } + + if (pcl_count >= NUM_CHANNELS) { + hdd_err("pcl_count is too big %d", pcl_count); + return; + } + + hdd_debug("Update ACS chan freq with PCL"); + for (j = 0; j < *org_ch_list_count; j++) + for (i = 0; i < pcl_count; i++) + if (pcl[i] == org_freq_list[j]) { + org_freq_list[ch_list_count++] = pcl[i]; + break; + } + + *org_ch_list_count = ch_list_count; +} + +/** + * wlan_hdd_handle_zero_acs_list() - Handle worst case of acs channel + * trimmed to zero + * @hdd_ctx: struct hdd_context + * @org_ch_list: ACS channel list from user space + * @org_ch_list_count: ACS channel count from user space + * + * When all chan in ACS freq list is filtered out + * by wlan_hdd_trim_acs_channel_list, the hostapd start will fail. + * This happens when PCL is PM_24G_SCC_CH_SBS_CH, and SAP acs range includes + * 5G channel list. One example is STA active on 6Ghz chan. Hostapd + * start SAP on 5G ACS range. The intersection of PCL and ACS range is zero. + * Instead of ACS failure, this API selects one channel from ACS range + * and report to Hostapd. When hostapd do start_ap, the driver will + * force SCC to 6G or move SAP to 2G based on SAP's configuration. + * + * Return: None + */ +static void wlan_hdd_handle_zero_acs_list(struct hdd_context *hdd_ctx, + uint32_t *acs_freq_list, + uint8_t *acs_ch_list_count, + uint32_t *org_freq_list, + uint8_t org_ch_list_count) +{ + uint16_t i, sta_count; + uint32_t acs_chan_default = 0; + + if (!acs_ch_list_count || *acs_ch_list_count > 0 || + !acs_freq_list) { + return; + } + if (!org_ch_list_count || !org_freq_list) + return; + + if (!policy_mgr_is_force_scc(hdd_ctx->psoc)) + return; + sta_count = policy_mgr_mode_specific_connection_count + (hdd_ctx->psoc, PM_STA_MODE, NULL); + sta_count += policy_mgr_mode_specific_connection_count + (hdd_ctx->psoc, PM_P2P_CLIENT_MODE, NULL); + if (!sta_count) + return; + + for (i = 0; i < org_ch_list_count; i++) { + if (!wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, + org_freq_list[i])) { + acs_chan_default = org_freq_list[i]; + break; + } + } + if (!acs_chan_default) + acs_chan_default = org_freq_list[0]; + + acs_freq_list[0] = acs_chan_default; + *acs_ch_list_count = 1; + hdd_debug("retore acs chan list to single freq %d", acs_chan_default); +} + +/** + * __wlan_hdd_cfg80211_do_acs(): CFG80211 handler function for DO_ACS Vendor CMD + * @wiphy: Linux wiphy struct pointer + * @wdev: Linux wireless device struct pointer + * @data: ACS information from hostapd + * @data_len: ACS information length + * + * This function handle DO_ACS Vendor command from hostapd, parses ACS config + * and starts ACS procedure. + * + * Return: ACS procedure start status + */ +static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *ndev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct sap_config *sap_config; + struct sk_buff *temp_skbuff; + int ret, i; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1]; + bool ht_enabled, ht40_enabled, vht_enabled; + uint8_t ch_width; + enum qca_wlan_vendor_acs_hw_mode hw_mode; + enum policy_mgr_con_mode pm_mode; + QDF_STATUS qdf_status; + bool is_vendor_acs_support = false; + bool is_external_acs_policy = false; + bool sap_force_11n_for_11ac = 0; + bool go_force_11n_for_11ac = 0; + bool go_11ac_override = 0; + bool sap_11ac_override = 0; + + /* ***Note*** Donot set SME config related to ACS operation here because + * ACS operation is not synchronouse and ACS for Second AP may come when + * ACS operation for first AP is going on. So only do_acs is split to + * separate start_acs routine. Also SME-PMAC struct that is used to + * pass paremeters from HDD to SAP is global. Thus All ACS related SME + * config shall be set only from start_acs. + */ + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + ucfg_mlme_get_sap_force_11n_for_11ac(hdd_ctx->psoc, + &sap_force_11n_for_11ac); + ucfg_mlme_get_go_force_11n_for_11ac(hdd_ctx->psoc, + &go_force_11n_for_11ac); + + if (!((adapter->device_mode == QDF_SAP_MODE) || + (adapter->device_mode == QDF_P2P_GO_MODE))) { + hdd_err("Invalid device mode %d", adapter->device_mode); + return -EINVAL; + } + + if (cds_is_sub_20_mhz_enabled()) { + hdd_err("ACS not supported in sub 20 MHz ch wd."); + return -EINVAL; + } + + if (qdf_atomic_read(&adapter->session.ap.acs_in_progress) > 0) { + hdd_err("ACS rejected as previous req already in progress"); + return -EINVAL; + } else { + qdf_atomic_set(&adapter->session.ap.acs_in_progress, 1); + qdf_event_reset(&adapter->acs_complete_event); + } + + ret = wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX, data, + data_len, + wlan_hdd_cfg80211_do_acs_policy); + if (ret) { + hdd_err("Invalid ATTR"); + goto out; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) { + hdd_err("Attr hw_mode failed"); + ret = -EINVAL; + goto out; + } + hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]); + + hdd_nofl_info("ACS request vid %d hw mode %d", adapter->vdev_id, + hw_mode); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED]) + ht_enabled = + nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED]); + else + ht_enabled = 0; + + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED]) + ht40_enabled = + nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED]); + else + ht40_enabled = 0; + + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED]) + vht_enabled = + nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED]); + else + vht_enabled = 0; + + if (((adapter->device_mode == QDF_SAP_MODE) && + sap_force_11n_for_11ac) || + ((adapter->device_mode == QDF_P2P_GO_MODE) && + go_force_11n_for_11ac)) { + vht_enabled = 0; + hdd_info("VHT is Disabled in ACS"); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]) { + ch_width = nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]); + } else { + if (ht_enabled && ht40_enabled) + ch_width = 40; + else + ch_width = 20; + } + + /* this may be possible, when sap_force_11n_for_11ac or + * go_force_11n_for_11ac is set + */ + if ((ch_width == 80 || ch_width == 160) && !vht_enabled) { + if (ht_enabled && ht40_enabled) + ch_width = 40; + else + ch_width = 20; + } + + sap_config = &adapter->session.ap.sap_config; + + /* Check and free if memory is already allocated for acs channel list */ + wlan_hdd_undo_acs(adapter); + + qdf_mem_zero(&sap_config->acs_cfg, sizeof(struct sap_acs_cfg)); + + if (ch_width == 160) + sap_config->acs_cfg.ch_width = CH_WIDTH_160MHZ; + else if (ch_width == 80) + sap_config->acs_cfg.ch_width = CH_WIDTH_80MHZ; + else if (ch_width == 40) + sap_config->acs_cfg.ch_width = CH_WIDTH_40MHZ; + else + sap_config->acs_cfg.ch_width = CH_WIDTH_20MHZ; + + /* Firstly try to get channel frequencies */ + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST]) { + uint32_t *freq = + nla_data(tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST]); + sap_config->acs_cfg.ch_list_count = nla_len( + tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST]) / + sizeof(uint32_t); + if (sap_config->acs_cfg.ch_list_count) { + sap_config->acs_cfg.freq_list = qdf_mem_malloc( + sap_config->acs_cfg.ch_list_count * + sizeof(uint32_t)); + sap_config->acs_cfg.master_freq_list = qdf_mem_malloc( + sap_config->acs_cfg.ch_list_count * + sizeof(uint32_t)); + if (!sap_config->acs_cfg.freq_list || + !sap_config->acs_cfg.master_freq_list) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < sap_config->acs_cfg.ch_list_count; + i++) { + sap_config->acs_cfg.master_freq_list[i] = + freq[i]; + sap_config->acs_cfg.freq_list[i] = freq[i]; + } + sap_config->acs_cfg.master_ch_list_count = + sap_config->acs_cfg.ch_list_count; + } + } else if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]) { + uint8_t *tmp = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]); + + sap_config->acs_cfg.ch_list_count = nla_len( + tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]); + if (sap_config->acs_cfg.ch_list_count) { + sap_config->acs_cfg.freq_list = qdf_mem_malloc( + sap_config->acs_cfg.ch_list_count * + sizeof(uint32_t)); + sap_config->acs_cfg.master_freq_list = qdf_mem_malloc( + sap_config->acs_cfg.ch_list_count * + sizeof(uint32_t)); + if (!sap_config->acs_cfg.freq_list || + !sap_config->acs_cfg.master_freq_list) { + ret = -ENOMEM; + goto out; + } + + /* convert channel to frequency */ + for (i = 0; i < sap_config->acs_cfg.ch_list_count; + i++) { + sap_config->acs_cfg.freq_list[i] = + wlan_reg_legacy_chan_to_freq( + hdd_ctx->pdev, + tmp[i]); + sap_config->acs_cfg.master_freq_list[i] = + sap_config->acs_cfg.freq_list[i]; + } + sap_config->acs_cfg.master_ch_list_count = + sap_config->acs_cfg.ch_list_count; + } + } + + if (!sap_config->acs_cfg.ch_list_count) { + hdd_err("acs config chan count 0"); + ret = -EINVAL; + goto out; + } + + hdd_avoid_acs_channels(hdd_ctx, sap_config); + + pm_mode = + policy_mgr_convert_device_mode_to_qdf_type(adapter->device_mode); + /* consult policy manager to get PCL */ + qdf_status = policy_mgr_get_pcl(hdd_ctx->psoc, pm_mode, + sap_config->acs_cfg.pcl_chan_freq, + &sap_config->acs_cfg.pcl_ch_count, + sap_config->acs_cfg. + pcl_channels_weight_list, + NUM_CHANNELS); + + sap_config->acs_cfg.band = hw_mode; + + qdf_status = ucfg_mlme_get_external_acs_policy(hdd_ctx->psoc, + &is_external_acs_policy); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("get_external_acs_policy failed"); + + sap_config->acs_cfg.acs_mode = true; + + if (is_external_acs_policy && + policy_mgr_is_force_scc(hdd_ctx->psoc) && + policy_mgr_get_connection_count(hdd_ctx->psoc)) { + wlan_hdd_trim_acs_channel_list( + sap_config->acs_cfg.pcl_chan_freq, + sap_config->acs_cfg.pcl_ch_count, + sap_config->acs_cfg.freq_list, + &sap_config->acs_cfg.ch_list_count); + if (!sap_config->acs_cfg.ch_list_count && + sap_config->acs_cfg.master_ch_list_count) + wlan_hdd_handle_zero_acs_list( + hdd_ctx, + sap_config->acs_cfg.freq_list, + &sap_config->acs_cfg.ch_list_count, + sap_config->acs_cfg.master_freq_list, + sap_config->acs_cfg.master_ch_list_count); + /* if it is only one channel, send ACS event to upper layer */ + if (sap_config->acs_cfg.ch_list_count == 1) { + sap_config->acs_cfg.start_ch_freq = + sap_config->acs_cfg.freq_list[0]; + sap_config->acs_cfg.end_ch_freq = + sap_config->acs_cfg.freq_list[0]; + sap_config->acs_cfg.pri_ch_freq = + sap_config->acs_cfg.freq_list[0]; + wlan_sap_set_sap_ctx_acs_cfg( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), sap_config); + sap_config_acs_result(hdd_ctx->mac_handle, + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + sap_config->acs_cfg.ht_sec_ch_freq); + sap_config->ch_params.ch_width = + sap_config->acs_cfg.ch_width; + sap_config->ch_params.sec_ch_offset = + wlan_reg_freq_to_chan(hdd_ctx->pdev, + sap_config->acs_cfg.ht_sec_ch_freq); + sap_config->ch_params.center_freq_seg0 = + wlan_reg_freq_to_chan( + hdd_ctx->pdev, + sap_config->acs_cfg.vht_seg0_center_ch_freq); + sap_config->ch_params.center_freq_seg1 = + wlan_reg_freq_to_chan( + hdd_ctx->pdev, + sap_config->acs_cfg.vht_seg1_center_ch_freq); + sap_config->ch_params.mhz_freq_seg0 = + sap_config->acs_cfg.vht_seg0_center_ch_freq; + sap_config->ch_params.mhz_freq_seg1 = + sap_config->acs_cfg.vht_seg1_center_ch_freq; + /*notify hostapd about channel override */ + wlan_hdd_cfg80211_acs_ch_select_evt(adapter); + ret = 0; + goto out; + } + } + + ret = wlan_hdd_set_acs_ch_range(sap_config, hw_mode, + ht_enabled, vht_enabled); + if (ret) { + hdd_err("set acs channel range failed"); + goto out; + } + + ucfg_mlme_is_go_11ac_override(hdd_ctx->psoc, &go_11ac_override); + ucfg_mlme_is_sap_11ac_override(hdd_ctx->psoc, &sap_11ac_override); + /* ACS override for android */ + if (ht_enabled && + sap_config->acs_cfg.end_ch_freq >= + WLAN_REG_CH_TO_FREQ(CHAN_ENUM_5180) && + ((adapter->device_mode == QDF_SAP_MODE && + !sap_force_11n_for_11ac && + sap_11ac_override) || + (adapter->device_mode == QDF_P2P_GO_MODE && + !go_force_11n_for_11ac && + go_11ac_override))) { + vht_enabled = 1; + sap_config->acs_cfg.hw_mode = eCSR_DOT11_MODE_11ac; + qdf_status = + ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc, + &ch_width); + sap_config->acs_cfg.ch_width = ch_width; + } + + /* Check 2.4ghz cbmode and update BW if only 2.4 channels are present */ + if (sap_config->acs_cfg.end_ch_freq <= + WLAN_REG_CH_TO_FREQ(CHAN_ENUM_2484) && + sap_config->acs_cfg.ch_width >= eHT_CHANNEL_WIDTH_40MHZ) { + uint32_t channel_bonding_mode; + + ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, + &channel_bonding_mode); + sap_config->acs_cfg.ch_width = channel_bonding_mode ? + eHT_CHANNEL_WIDTH_40MHZ : eHT_CHANNEL_WIDTH_20MHZ; + + hdd_debug("Only 2.4ghz channels, resetting BW to %d 2.4 cbmode %d", + sap_config->acs_cfg.ch_width, channel_bonding_mode); + } + + hdd_nofl_debug("ACS Config country %s ch_width %d hw_mode %d ACS_BW: %d HT: %d VHT: %d START_CH: %d END_CH: %d band %d", + hdd_ctx->reg.alpha2, ch_width, + sap_config->acs_cfg.hw_mode, sap_config->acs_cfg.ch_width, + ht_enabled, vht_enabled, sap_config->acs_cfg.start_ch_freq, + sap_config->acs_cfg.end_ch_freq, + sap_config->acs_cfg.band); + host_log_acs_req_event(adapter->dev->name, + csr_phy_mode_str(sap_config->acs_cfg.hw_mode), + ch_width, ht_enabled, vht_enabled, + sap_config->acs_cfg.start_ch_freq, + sap_config->acs_cfg.end_ch_freq); + + sap_config->acs_cfg.is_ht_enabled = ht_enabled; + sap_config->acs_cfg.is_vht_enabled = vht_enabled; + + sap_dump_acs_channel(&sap_config->acs_cfg); + + qdf_status = ucfg_mlme_get_vendor_acs_support(hdd_ctx->psoc, + &is_vendor_acs_support); + if (QDF_IS_STATUS_ERROR(qdf_status)) + hdd_err("get_vendor_acs_support failed, set default"); + + /* Check if vendor specific acs is enabled */ + if (is_vendor_acs_support) + ret = hdd_start_vendor_acs(adapter); + else + ret = wlan_hdd_cfg80211_start_acs(adapter); + +out: + if (ret == 0) { + temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, + NLMSG_HDRLEN); + if (temp_skbuff) + return cfg80211_vendor_cmd_reply(temp_skbuff); + } + qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); + + return ret; +} + +/** + * wlan_hdd_cfg80211_do_acs : CFG80211 handler function for DO_ACS Vendor CMD + * @wiphy: Linux wiphy struct pointer + * @wdev: Linux wireless device struct pointer + * @data: ACS information from hostapd + * @data_len: ACS information len + * + * This function handle DO_ACS Vendor command from hostapd, parses ACS config + * and starts ACS procedure. + * + * Return: ACS procedure start status + */ + +static int wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_do_acs(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_undo_acs : Do cleanup of DO_ACS + * @adapter: Pointer to adapter struct + * + * This function handle cleanup of what was done in DO_ACS, including free + * memory. + * + * Return: void + */ +void wlan_hdd_undo_acs(struct hdd_adapter *adapter) +{ + sap_undo_acs(WLAN_HDD_GET_SAP_CTX_PTR(adapter), + &adapter->session.ap.sap_config); +} + +/** + * hdd_fill_acs_chan_freq() - Populate channel frequencies (MHz) selected in ACS + * @hdd_ctx: pointer to hdd context + * @sap_cfg: sap acs configuration + * @vendor_event: output pointer to populate channel frequencies (MHz) + * + * Return: If populated successfully return 0 else negative value. + */ +static int hdd_fill_acs_chan_freq(struct hdd_context *hdd_ctx, + struct sap_config *sap_cfg, + struct sk_buff *vendor_event) +{ + uint32_t id; + int errno; + + id = QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY; + errno = nla_put_u32(vendor_event, id, sap_cfg->acs_cfg.pri_ch_freq); + if (errno) { + hdd_err("VENDOR_ATTR_ACS_PRIMARY_FREQUENCY put fail"); + return errno; + } + + id = QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY; + errno = nla_put_u32(vendor_event, id, sap_cfg->acs_cfg.ht_sec_ch_freq); + if (errno) { + hdd_err("VENDOR_ATTR_ACS_SECONDARY_FREQUENCY put fail"); + return errno; + } + + id = QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY; + errno = nla_put_u32(vendor_event, id, + sap_cfg->acs_cfg.vht_seg0_center_ch_freq); + if (errno) { + hdd_err("VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY put fail"); + return errno; + } + + id = QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY; + errno = nla_put_u32(vendor_event, id, + sap_cfg->acs_cfg.vht_seg1_center_ch_freq); + if (errno) { + hdd_err("VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY put fail"); + return errno; + } + + return 0; +} + +static int hdd_get_acs_evt_data_len(void) +{ + uint32_t len = NLMSG_HDRLEN; + + /* QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL */ + len += nla_total_size(sizeof(u8)); + + /* QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL */ + len += nla_total_size(sizeof(u8)); + + /* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL */ + len += nla_total_size(sizeof(u8)); + + /* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL */ + len += nla_total_size(sizeof(u8)); + + /* QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY */ + len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY */ + len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY */ + len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY */ + len += nla_total_size(sizeof(u32)); + + /* QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH */ + len += nla_total_size(sizeof(u16)); + + /* QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE */ + len += nla_total_size(sizeof(u8)); + + return len; +} + +/** + * wlan_hdd_cfg80211_acs_ch_select_evt: Callback function for ACS evt + * @adapter: Pointer to SAP adapter struct + * @pri_channel: SAP ACS procedure selected Primary channel + * @sec_channel: SAP ACS procedure selected secondary channel + * + * This is a callback function from SAP module on ACS procedure is completed. + * This function send the ACS selected channel information to hostapd + * + * Return: None + */ +void wlan_hdd_cfg80211_acs_ch_select_evt(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct sap_config *sap_cfg = + &(WLAN_HDD_GET_AP_CTX_PTR(adapter))->sap_config; + struct sk_buff *vendor_event; + int ret_val; + uint16_t ch_width; + uint8_t pri_channel; + uint8_t ht_sec_channel; + uint8_t vht_seg0_center_ch, vht_seg1_center_ch; + uint32_t id = QCA_NL80211_VENDOR_SUBCMD_DO_ACS_INDEX; + uint32_t len = hdd_get_acs_evt_data_len(); + + qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); + qdf_event_set(&adapter->acs_complete_event); + + vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + &adapter->wdev, len, id, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + ret_val = hdd_fill_acs_chan_freq(hdd_ctx, sap_cfg, vendor_event); + if (ret_val) { + hdd_err("failed to put frequencies"); + kfree_skb(vendor_event); + return; + } + + pri_channel = wlan_reg_freq_to_chan(hdd_ctx->pdev, + sap_cfg->acs_cfg.pri_ch_freq); + + ht_sec_channel = wlan_reg_freq_to_chan(hdd_ctx->pdev, + sap_cfg->acs_cfg.ht_sec_ch_freq); + + ret_val = nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL, + pri_channel); + if (ret_val) { + hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL put fail"); + kfree_skb(vendor_event); + return; + } + + ret_val = nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL, + ht_sec_channel); + if (ret_val) { + hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL put fail"); + kfree_skb(vendor_event); + return; + } + vht_seg0_center_ch = wlan_reg_freq_to_chan( + hdd_ctx->pdev, + sap_cfg->acs_cfg.vht_seg0_center_ch_freq); + ret_val = nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL, + vht_seg0_center_ch); + if (ret_val) { + hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL put fail"); + kfree_skb(vendor_event); + return; + } + vht_seg1_center_ch = wlan_reg_freq_to_chan( + hdd_ctx->pdev, + sap_cfg->acs_cfg.vht_seg1_center_ch_freq); + ret_val = nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL, + vht_seg1_center_ch); + if (ret_val) { + hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL put fail"); + kfree_skb(vendor_event); + return; + } + + if (sap_cfg->acs_cfg.ch_width == CH_WIDTH_160MHZ) + ch_width = 160; + else if (sap_cfg->acs_cfg.ch_width == CH_WIDTH_80MHZ) + ch_width = 80; + else if (sap_cfg->acs_cfg.ch_width == CH_WIDTH_40MHZ) + ch_width = 40; + else + ch_width = 20; + + ret_val = nla_put_u16(vendor_event, + QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, + ch_width); + if (ret_val) { + hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH put fail"); + kfree_skb(vendor_event); + return; + } + if (WLAN_REG_IS_24GHZ_CH_FREQ(sap_cfg->acs_cfg.pri_ch_freq)) + ret_val = nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, + QCA_ACS_MODE_IEEE80211G); + else + ret_val = nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, + QCA_ACS_MODE_IEEE80211A); + + if (ret_val) { + hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE put fail"); + kfree_skb(vendor_event); + return; + } + + hdd_debug("ACS result for %s: PRI_CH_FREQ: %d SEC_CH_FREQ: %d VHT_SEG0: %d VHT_SEG1: %d ACS_BW: %d", + adapter->dev->name, sap_cfg->acs_cfg.pri_ch_freq, + sap_cfg->acs_cfg.ht_sec_ch_freq, + sap_cfg->acs_cfg.vht_seg0_center_ch_freq, + sap_cfg->acs_cfg.vht_seg1_center_ch_freq, ch_width); + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); +} + +/** + * hdd_is_wlm_latency_manager_supported - Checks if WLM Latency manager is + * supported + * @hdd_ctx: The HDD context + * + * Return: True if supported, false otherwise + */ +static inline +bool hdd_is_wlm_latency_manager_supported(struct hdd_context *hdd_ctx) +{ + bool latency_enable; + + if (QDF_IS_STATUS_ERROR(ucfg_mlme_get_latency_enable + (hdd_ctx->psoc, &latency_enable))) + return false; + + if (latency_enable && + sme_is_feature_supported_by_fw(VDEV_LATENCY_CONFIG)) + return true; + else + return false; +} + +static int +__wlan_hdd_cfg80211_get_supported_features(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct sk_buff *skb = NULL; + uint32_t fset = 0; + int ret; +#ifdef FEATURE_WLAN_TDLS + bool bvalue; +#endif + uint32_t fine_time_meas_cap; + + /* ENTER_DEV() intentionally not used in a frequently invoked API */ + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) { + hdd_debug("Infra Station mode is supported by driver"); + fset |= WIFI_FEATURE_INFRA; + } + if (true == hdd_is_5g_supported(hdd_ctx)) { + hdd_debug("INFRA_5G is supported by firmware"); + fset |= WIFI_FEATURE_INFRA_5G; + } +#ifdef WLAN_FEATURE_P2P + if ((wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) && + (wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_GO))) { + hdd_debug("WiFi-Direct is supported by driver"); + fset |= WIFI_FEATURE_P2P; + } +#endif + fset |= WIFI_FEATURE_SOFT_AP; + + /* HOTSPOT is a supplicant feature, enable it by default */ + fset |= WIFI_FEATURE_HOTSPOT; + + if (ucfg_extscan_get_enable(hdd_ctx->psoc) && + sme_is_feature_supported_by_fw(EXTENDED_SCAN)) { + hdd_debug("EXTScan is supported by firmware"); + fset |= WIFI_FEATURE_EXTSCAN | WIFI_FEATURE_HAL_EPNO; + } + if (wlan_hdd_nan_is_supported(hdd_ctx)) { + hdd_debug("NAN is supported by firmware"); + fset |= WIFI_FEATURE_NAN; + } + + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, &fine_time_meas_cap); + + if (sme_is_feature_supported_by_fw(RTT) && + rtt_is_enabled(fine_time_meas_cap)) { + hdd_debug("RTT is supported by firmware and driver: %x", + fine_time_meas_cap); + fset |= WIFI_FEATURE_D2D_RTT; + fset |= WIFI_FEATURE_D2AP_RTT; + } +#ifdef FEATURE_WLAN_SCAN_PNO + if (ucfg_scan_get_pno_scan_support(hdd_ctx->psoc) && + sme_is_feature_supported_by_fw(PNO)) { + hdd_debug("PNO is supported by firmware"); + fset |= WIFI_FEATURE_PNO; + } +#endif + fset |= WIFI_FEATURE_ADDITIONAL_STA; +#ifdef FEATURE_WLAN_TDLS + cfg_tdls_get_support_enable(hdd_ctx->psoc, &bvalue); + if ((bvalue) && sme_is_feature_supported_by_fw(TDLS)) { + hdd_debug("TDLS is supported by firmware"); + fset |= WIFI_FEATURE_TDLS; + } + + cfg_tdls_get_off_channel_enable(hdd_ctx->psoc, &bvalue); + if (sme_is_feature_supported_by_fw(TDLS) && + bvalue && sme_is_feature_supported_by_fw(TDLS_OFF_CHANNEL)) { + hdd_debug("TDLS off-channel is supported by firmware"); + fset |= WIFI_FEATURE_TDLS_OFFCHANNEL; + } +#endif + fset |= WIFI_FEATURE_AP_STA; + fset |= WIFI_FEATURE_RSSI_MONITOR; + fset |= WIFI_FEATURE_TX_TRANSMIT_POWER; + fset |= WIFI_FEATURE_SET_TX_POWER_LIMIT; + fset |= WIFI_FEATURE_CONFIG_NDO; + + if (hdd_link_layer_stats_supported()) + fset |= WIFI_FEATURE_LINK_LAYER_STATS; + + if (hdd_roaming_supported(hdd_ctx)) + fset |= WIFI_FEATURE_CONTROL_ROAMING; + + if (hdd_scan_random_mac_addr_supported()) + fset |= WIFI_FEATURE_SCAN_RAND; + + if (hdd_is_wlm_latency_manager_supported(hdd_ctx)) + fset |= WIFI_FEATURE_SET_LATENCY_MODE; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(fset) + + NLMSG_HDRLEN); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -EINVAL; + } + hdd_debug("Supported Features : 0x%x", fset); + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_FEATURE_SET, fset)) { + hdd_err("nla put fail"); + goto nla_put_failure; + } + ret = cfg80211_vendor_cmd_reply(skb); + return ret; +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_get_supported_features() - get supported features + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Return: Return the Success or Failure code. + */ +static int +wlan_hdd_cfg80211_get_supported_features(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_supported_features(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_set_scanning_mac_oui() - set scan MAC + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Set the MAC address that is to be used for scanning. + * + * Return: Return the Success or Failure code. + */ +static int +__wlan_hdd_cfg80211_set_scanning_mac_oui(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct scan_mac_oui scan_mac_oui = { {0} }; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX + 1]; + QDF_STATUS status; + int ret, len; + struct net_device *ndev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + mac_handle_t mac_handle; + bool mac_spoofing_enabled; + + hdd_enter_dev(wdev->netdev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + mac_spoofing_enabled = ucfg_scan_is_mac_spoofing_enabled(hdd_ctx->psoc); + if (!mac_spoofing_enabled) { + hdd_debug("MAC address spoofing is not enabled"); + return -ENOTSUPP; + } + + /* + * audit note: it is ok to pass a NULL policy here since only + * one attribute is parsed and it is explicitly validated + */ + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX, + data, data_len, NULL)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]) { + hdd_err("attr mac oui failed"); + return -EINVAL; + } + + len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]); + if (len != sizeof(scan_mac_oui.oui)) { + hdd_err("attr mac oui invalid size %d expected %zu", + len, sizeof(scan_mac_oui.oui)); + return -EINVAL; + } + + nla_memcpy(scan_mac_oui.oui, + tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI], + sizeof(scan_mac_oui.oui)); + + /* populate rest of scan_mac_oui for mac addr randomization */ + scan_mac_oui.vdev_id = adapter->vdev_id; + scan_mac_oui.enb_probe_req_sno_randomization = true; + + hdd_debug("Oui (%02x:%02x:%02x), vdev_id = %d", + scan_mac_oui.oui[0], scan_mac_oui.oui[1], + scan_mac_oui.oui[2], scan_mac_oui.vdev_id); + + hdd_update_ie_whitelist_attr(&scan_mac_oui.ie_whitelist, hdd_ctx); + + mac_handle = hdd_ctx->mac_handle; + status = sme_set_scanning_mac_oui(mac_handle, &scan_mac_oui); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("sme_set_scanning_mac_oui failed(err=%d)", status); + + return qdf_status_to_os_return(status); +} + +/** + * wlan_hdd_cfg80211_set_scanning_mac_oui() - set scan MAC + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Set the MAC address that is to be used for scanning. This is an + * SSR-protecting wrapper function. + * + * Return: Return the Success or Failure code. + */ +static int +wlan_hdd_cfg80211_set_scanning_mac_oui(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_scanning_mac_oui(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_cfg80211_set_feature() - Set the bitmask for supported features + * @feature_flags: pointer to the byte array of features. + * @feature: Feature to be turned ON in the byte array. + * + * Return: None + * + * This is called to turn ON or SET the feature flag for the requested feature. + **/ +#define NUM_BITS_IN_BYTE 8 +static void wlan_hdd_cfg80211_set_feature(uint8_t *feature_flags, + uint8_t feature) +{ + uint32_t index; + uint8_t bit_mask; + + index = feature / NUM_BITS_IN_BYTE; + bit_mask = 1 << (feature % NUM_BITS_IN_BYTE); + feature_flags[index] |= bit_mask; +} + +/** + * __wlan_hdd_cfg80211_get_features() - Get the Driver Supported features + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This is called when wlan driver needs to send supported feature set to + * supplicant upon a request/query from the supplicant. + * + * Return: Return the Success or Failure code. + **/ +#define MAX_CONCURRENT_CHAN_ON_24G 2 +#define MAX_CONCURRENT_CHAN_ON_5G 2 +static int +__wlan_hdd_cfg80211_get_features(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct sk_buff *skb = NULL; + uint32_t dbs_capability = 0; + bool one_by_one_dbs, two_by_two_dbs; + bool value, twt_req, twt_res; + QDF_STATUS ret = QDF_STATUS_E_FAILURE; + QDF_STATUS status; + int ret_val; + + uint8_t feature_flags[(NUM_QCA_WLAN_VENDOR_FEATURES + 7) / 8] = {0}; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter_dev(wdev->netdev); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (roaming_offload_enabled(hdd_ctx)) { + hdd_debug("Key Mgmt Offload is supported"); + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD); + } + + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY); + if (policy_mgr_is_scan_simultaneous_capable(hdd_ctx->psoc)) + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS); + + if (policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc)) + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_CONCURRENT_BAND_SESSIONS); + + if (wma_is_p2p_lo_capable()) + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD); + + value = 0; + status = ucfg_mlme_get_oce_sta_enabled_info(hdd_ctx->psoc, &value); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not get OCE STA enable info"); + if (value) + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_OCE_STA); + + value = 0; + status = ucfg_mlme_get_oce_sap_enabled_info(hdd_ctx->psoc, &value); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not get OCE SAP enable info"); + if (value) + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON); + + ucfg_mlme_get_twt_requestor(hdd_ctx->psoc, &twt_req); + ucfg_mlme_get_twt_responder(hdd_ctx->psoc, &twt_res); + + if (twt_req || twt_res) + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_TWT); + + /* Check the kernel version for upstream commit aced43ce780dc5 that + * has support for processing user cell_base hints when wiphy is + * self managed or check the backport flag for the same. + */ +#if defined CFG80211_USER_HINT_CELL_BASE_SELF_MANAGED || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY); +#endif + + if (wlan_hdd_thermal_config_support()) + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_THERMAL_CONFIG); + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(feature_flags) + + NLMSG_HDRLEN); + + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS, + sizeof(feature_flags), feature_flags)) + goto nla_put_failure; + + ret = policy_mgr_get_dbs_hw_modes(hdd_ctx->psoc, + &one_by_one_dbs, &two_by_two_dbs); + if (QDF_STATUS_SUCCESS == ret) { + if (one_by_one_dbs) + dbs_capability = DRV_DBS_CAPABILITY_1X1; + + if (two_by_two_dbs) + dbs_capability = DRV_DBS_CAPABILITY_2X2; + + if (!one_by_one_dbs && !two_by_two_dbs) + dbs_capability = DRV_DBS_CAPABILITY_DISABLED; + } else { + hdd_err("wma_get_dbs_hw_mode failed"); + dbs_capability = DRV_DBS_CAPABILITY_DISABLED; + } + + hdd_debug("dbs_capability is %d", dbs_capability); + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA, + dbs_capability)) + goto nla_put_failure; + + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND, + MAX_CONCURRENT_CHAN_ON_24G)) + goto nla_put_failure; + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND, + MAX_CONCURRENT_CHAN_ON_5G)) + goto nla_put_failure; + + return cfg80211_vendor_cmd_reply(skb); + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_get_features() - Get the Driver Supported features + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This is called when wlan driver needs to send supported feature set to + * supplicant upon a request/query from the supplicant. + * + * Return: Return the Success or Failure code. + */ +static int +wlan_hdd_cfg80211_get_features(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_features(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +#define PARAM_NUM_NW \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS +#define PARAM_SET_BSSID \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID +#define PARAM_SET_BSSID_HINT \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT +#define PARAM_SSID_LIST QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST +#define PARAM_LIST_SSID QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID +#define MAX_ROAMING_PARAM \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX +#define PARAM_NUM_BSSID \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID +#define PARAM_BSSID_PREFS \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS +#define PARAM_ROAM_BSSID \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID +#define PARAM_RSSI_MODIFIER \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER +#define PARAMS_NUM_BSSID \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID +#define PARAM_BSSID_PARAMS \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS +#define PARAM_A_BAND_BOOST_THLD \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD +#define PARAM_A_BAND_PELT_THLD \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD +#define PARAM_A_BAND_BOOST_FACTOR \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR +#define PARAM_A_BAND_PELT_FACTOR \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR +#define PARAM_A_BAND_MAX_BOOST \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST +#define PARAM_ROAM_HISTERESYS \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS +#define PARAM_RSSI_TRIGGER \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER +#define PARAM_ROAM_ENABLE \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE +#define PARAM_ROAM_CONTROL_CONFIG \ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL +#define PARAM_FREQ_LIST_SCHEME \ + QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME +#define PARAM_FREQ_LIST_SCHEME_MAX \ + QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_MAX +#define PARAM_SCAN_FREQ_LIST \ + QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST +#define PARAM_SCAN_FREQ_LIST_TYPE \ + QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_TYPE +#define PARAM_CAND_SEL_CRITERIA_MAX \ + QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_MAX +#define PARAM_CAND_SEL_SCORE_RSSI \ + QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_RSSI + + +static const struct nla_policy +wlan_hdd_set_roam_param_policy[MAX_ROAMING_PARAM + 1] = { + [QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID] = {.type = NLA_U32}, + [PARAM_NUM_NW] = {.type = NLA_U32}, + [PARAM_A_BAND_BOOST_FACTOR] = {.type = NLA_U32}, + [PARAM_A_BAND_PELT_FACTOR] = {.type = NLA_U32}, + [PARAM_A_BAND_MAX_BOOST] = {.type = NLA_U32}, + [PARAM_ROAM_HISTERESYS] = {.type = NLA_S32}, + [PARAM_A_BAND_BOOST_THLD] = {.type = NLA_S32}, + [PARAM_A_BAND_PELT_THLD] = {.type = NLA_S32}, + [PARAM_RSSI_TRIGGER] = {.type = NLA_U32}, + [PARAM_ROAM_ENABLE] = { .type = NLA_S32}, + [PARAM_NUM_BSSID] = {.type = NLA_U32}, + [PARAM_RSSI_MODIFIER] = {.type = NLA_U32}, + [PARAMS_NUM_BSSID] = {.type = NLA_U32}, + [PARAM_ROAM_BSSID] = {.type = NLA_UNSPEC, .len = QDF_MAC_ADDR_SIZE}, + [PARAM_SET_BSSID] = {.type = NLA_UNSPEC, .len = QDF_MAC_ADDR_SIZE}, + [PARAM_SET_BSSID_HINT] = {.type = NLA_FLAG}, + [PARAM_ROAM_CONTROL_CONFIG] = {.type = NLA_NESTED}, +}; + +/** + * hdd_set_white_list() - parse white list + * @hdd_ctx: HDD context + * @roam_params: roam params + * @tb: list of attributes + * @vdev_id: vdev id + * + * Return: 0 on success; error number on failure + */ +static int hdd_set_white_list(struct hdd_context *hdd_ctx, + struct roam_ext_params *roam_params, + struct nlattr **tb, uint8_t vdev_id) +{ + int rem, i; + uint32_t buf_len = 0, count; + struct nlattr *tb2[MAX_ROAMING_PARAM + 1]; + struct nlattr *curr_attr = NULL; + mac_handle_t mac_handle; + + i = 0; + if (tb[PARAM_NUM_NW]) { + count = nla_get_u32(tb[PARAM_NUM_NW]); + } else { + hdd_err("Number of networks is not provided"); + goto fail; + } + + if (count && tb[PARAM_SSID_LIST]) { + nla_for_each_nested(curr_attr, + tb[PARAM_SSID_LIST], rem) { + if (i == MAX_SSID_ALLOWED_LIST) { + hdd_err("Excess MAX_SSID_ALLOWED_LIST"); + goto fail; + } + if (wlan_cfg80211_nla_parse(tb2, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX, + nla_data(curr_attr), + nla_len(curr_attr), + wlan_hdd_set_roam_param_policy)) { + hdd_err("nla_parse failed"); + goto fail; + } + /* Parse and Fetch allowed SSID list*/ + if (!tb2[PARAM_LIST_SSID]) { + hdd_err("attr allowed ssid failed"); + goto fail; + } + buf_len = nla_len(tb2[PARAM_LIST_SSID]); + /* + * Upper Layers include a null termination + * character. Check for the actual permissible + * length of SSID and also ensure not to copy + * the NULL termination character to the driver + * buffer. + */ + if (buf_len > 1 && + ((buf_len - 1) <= WLAN_SSID_MAX_LEN)) { + nla_memcpy(roam_params->ssid_allowed_list[i].ssId, + tb2[PARAM_LIST_SSID], buf_len - 1); + roam_params->ssid_allowed_list[i].length = buf_len - 1; + hdd_debug("SSID[%d]: %.*s,length = %d", + i, + roam_params->ssid_allowed_list[i].length, + roam_params->ssid_allowed_list[i].ssId, + roam_params->ssid_allowed_list[i].length); + i++; + } else { + hdd_err("Invalid buffer length"); + } + } + } + + if (i != count) { + hdd_err("Invalid number of SSIDs i = %d, count = %d", i, count); + goto fail; + } + + roam_params->num_ssid_allowed_list = i; + hdd_debug("Num of Allowed SSID %d", roam_params->num_ssid_allowed_list); + mac_handle = hdd_ctx->mac_handle; + sme_update_roam_params(mac_handle, vdev_id, + roam_params, REASON_ROAM_SET_SSID_ALLOWED); + return 0; + +fail: + return -EINVAL; +} + +/** + * hdd_set_bssid_prefs() - parse set bssid prefs + * @hdd_ctx: HDD context + * @roam_params: roam params + * @tb: list of attributes + * @vdev_id: vdev id + * + * Return: 0 on success; error number on failure + */ +static int hdd_set_bssid_prefs(struct hdd_context *hdd_ctx, + struct roam_ext_params *roam_params, + struct nlattr **tb, uint8_t vdev_id) +{ + int rem, i; + uint32_t count; + struct nlattr *tb2[MAX_ROAMING_PARAM + 1]; + struct nlattr *curr_attr = NULL; + mac_handle_t mac_handle; + + /* Parse and fetch number of preferred BSSID */ + if (!tb[PARAM_NUM_BSSID]) { + hdd_err("attr num of preferred bssid failed"); + goto fail; + } + count = nla_get_u32(tb[PARAM_NUM_BSSID]); + if (count > MAX_BSSID_FAVORED) { + hdd_err("Preferred BSSID count %u exceeds max %u", + count, MAX_BSSID_FAVORED); + goto fail; + } + hdd_debug("Num of Preferred BSSID (%d)", count); + if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS]) { + hdd_err("attr Preferred BSSID failed"); + goto fail; + } + + i = 0; + nla_for_each_nested(curr_attr, + tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS], + rem) { + if (i == count) { + hdd_warn("Ignoring excess Preferred BSSID"); + break; + } + + if (wlan_cfg80211_nla_parse(tb2, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX, + nla_data(curr_attr), + nla_len(curr_attr), + wlan_hdd_set_roam_param_policy)) { + hdd_err("nla_parse failed"); + goto fail; + } + /* Parse and fetch MAC address */ + if (!tb2[PARAM_ROAM_BSSID]) { + hdd_err("attr mac address failed"); + goto fail; + } + nla_memcpy(roam_params->bssid_favored[i].bytes, + tb2[PARAM_ROAM_BSSID], + QDF_MAC_ADDR_SIZE); + hdd_debug(QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(roam_params->bssid_favored[i].bytes)); + /* Parse and fetch preference factor*/ + if (!tb2[PARAM_RSSI_MODIFIER]) { + hdd_err("BSSID Preference score failed"); + goto fail; + } + roam_params->bssid_favored_factor[i] = nla_get_u32( + tb2[PARAM_RSSI_MODIFIER]); + hdd_debug("BSSID Preference score (%d)", + roam_params->bssid_favored_factor[i]); + i++; + } + if (i < count) + hdd_warn("Num Preferred BSSID %u less than expected %u", + i, count); + + roam_params->num_bssid_favored = i; + mac_handle = hdd_ctx->mac_handle; + sme_update_roam_params(mac_handle, vdev_id, + roam_params, REASON_ROAM_SET_FAVORED_BSSID); + + return 0; + +fail: + return -EINVAL; +} + +/** + * hdd_set_blacklist_bssid() - parse set blacklist bssid + * @hdd_ctx: HDD context + * @roam_params: roam params + * @tb: list of attributes + * @vdev_id: vdev id + * + * Return: 0 on success; error number on failure + */ +static int hdd_set_blacklist_bssid(struct hdd_context *hdd_ctx, + struct roam_ext_params *roam_params, + struct nlattr **tb, + uint8_t vdev_id) +{ + int rem, i; + uint32_t count; + uint8_t j = 0; + struct nlattr *tb2[MAX_ROAMING_PARAM + 1]; + struct nlattr *curr_attr = NULL; + mac_handle_t mac_handle; + + /* Parse and fetch number of blacklist BSSID */ + if (!tb[PARAMS_NUM_BSSID]) { + hdd_err("attr num of blacklist bssid failed"); + goto fail; + } + count = nla_get_u32(tb[PARAMS_NUM_BSSID]); + if (count > MAX_BSSID_AVOID_LIST) { + hdd_err("Blacklist BSSID count %u exceeds max %u", + count, MAX_BSSID_AVOID_LIST); + goto fail; + } + hdd_debug("Num of blacklist BSSID (%d)", count); + + i = 0; + if (count && tb[PARAM_BSSID_PARAMS]) { + nla_for_each_nested(curr_attr, + tb[PARAM_BSSID_PARAMS], + rem) { + if (i == count) { + hdd_warn("Ignoring excess Blacklist BSSID"); + break; + } + + if (wlan_cfg80211_nla_parse(tb2, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX, + nla_data(curr_attr), + nla_len(curr_attr), + wlan_hdd_set_roam_param_policy)) { + hdd_err("nla_parse failed"); + goto fail; + } + /* Parse and fetch MAC address */ + if (!tb2[PARAM_SET_BSSID]) { + hdd_err("attr blacklist addr failed"); + goto fail; + } + if (tb2[PARAM_SET_BSSID_HINT]) { + struct reject_ap_info ap_info; + + nla_memcpy(ap_info.bssid.bytes, + tb2[PARAM_SET_BSSID], + QDF_MAC_ADDR_SIZE); + ap_info.reject_ap_type = USERSPACE_AVOID_TYPE; + ap_info.reject_reason = + REASON_USERSPACE_AVOID_LIST; + ap_info.source = ADDED_BY_DRIVER; + + /* This BSSID is avoided and not blacklisted */ + ucfg_blm_add_bssid_to_reject_list(hdd_ctx->pdev, + &ap_info); + i++; + continue; + } + nla_memcpy(roam_params->bssid_avoid_list[j].bytes, + tb2[PARAM_SET_BSSID], QDF_MAC_ADDR_SIZE); + hdd_debug(QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(roam_params-> + bssid_avoid_list[j].bytes)); + i++; + j++; + } + } + + if (i < count) + hdd_warn("Num Blacklist BSSID %u less than expected %u", + i, count); + + roam_params->num_bssid_avoid_list = j; + /* Send the blacklist to the blacklist mgr component */ + ucfg_blm_add_userspace_black_list(hdd_ctx->pdev, + roam_params->bssid_avoid_list, + roam_params->num_bssid_avoid_list); + + mac_handle = hdd_ctx->mac_handle; + sme_update_roam_params(mac_handle, vdev_id, + roam_params, REASON_ROAM_SET_BLACKLIST_BSSID); + + return 0; +fail: + return -EINVAL; +} + +static const struct nla_policy +roam_scan_freq_list_scheme_policy[PARAM_FREQ_LIST_SCHEME_MAX + 1] = { + [PARAM_SCAN_FREQ_LIST_TYPE] = {.type = NLA_U32}, + [PARAM_SCAN_FREQ_LIST] = {.type = NLA_NESTED}, +}; + +/** + * hdd_send_roam_scan_channel_freq_list_to_sme() - Send control roam scan freqs + * @hdd_ctx: HDD context + * @vdev_id: vdev id + * @tb: Nested attribute carrying frequency list scheme + * + * Extracts the frequency list and frequency list type from the frequency + * list scheme and send the frequencies to SME. + * + * Return: QDF_STATUS + */ +static QDF_STATUS +hdd_send_roam_scan_channel_freq_list_to_sme(struct hdd_context *hdd_ctx, + uint8_t vdev_id, struct nlattr *tb) +{ + QDF_STATUS status; + struct nlattr *tb2[PARAM_FREQ_LIST_SCHEME_MAX + 1], *curr_attr; + uint8_t num_chan = 0; + uint32_t freq_list[SIR_MAX_SUPPORTED_CHANNEL_LIST] = {0}; + uint32_t list_type; + mac_handle_t mac_handle = hdd_ctx->mac_handle; + int rem; + + if (wlan_cfg80211_nla_parse_nested(tb2, PARAM_FREQ_LIST_SCHEME_MAX, + tb, + roam_scan_freq_list_scheme_policy)) { + hdd_err("nla_parse failed"); + return QDF_STATUS_E_INVAL; + } + + if (!tb2[PARAM_SCAN_FREQ_LIST] || !tb2[PARAM_SCAN_FREQ_LIST_TYPE]) { + hdd_err("ROAM_CONTROL_SCAN_FREQ_LIST or type are not present"); + return QDF_STATUS_E_INVAL; + } + + list_type = nla_get_u32(tb2[PARAM_SCAN_FREQ_LIST_TYPE]); + if (list_type != QCA_PREFERRED_SCAN_FREQ_LIST && + list_type != QCA_SPECIFIC_SCAN_FREQ_LIST) { + hdd_err("Invalid freq list type received: %u", list_type); + return QDF_STATUS_E_INVAL; + } + + nla_for_each_nested(curr_attr, tb2[PARAM_SCAN_FREQ_LIST], rem) + num_chan++; + if (num_chan > SIR_MAX_SUPPORTED_CHANNEL_LIST) { + hdd_err("number of channels (%d) supported exceeded max (%d)", + num_chan, SIR_MAX_SUPPORTED_CHANNEL_LIST); + return QDF_STATUS_E_INVAL; + } + num_chan = 0; + + nla_for_each_nested(curr_attr, tb2[PARAM_SCAN_FREQ_LIST], rem) { + if (nla_len(curr_attr) != sizeof(uint32_t)) { + hdd_err("len is not correct for frequency %d", + num_chan); + return QDF_STATUS_E_INVAL; + } + freq_list[num_chan++] = nla_get_u32(curr_attr); + } + + status = sme_update_roam_scan_freq_list(mac_handle, vdev_id, freq_list, + num_chan, list_type); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to update channel list information"); + + return status; +} + +static const struct nla_policy +roam_control_policy[QCA_ATTR_ROAM_CONTROL_MAX + 1] = { + [QCA_ATTR_ROAM_CONTROL_ENABLE] = {.type = NLA_U8}, + [QCA_ATTR_ROAM_CONTROL_STATUS] = {.type = NLA_U8}, + [PARAM_FREQ_LIST_SCHEME] = {.type = NLA_NESTED}, + [QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD] = {.type = NLA_U32}, + [QCA_ATTR_ROAM_CONTROL_CLEAR_ALL] = {.type = NLA_FLAG}, + [QCA_ATTR_ROAM_CONTROL_TRIGGERS] = {.type = NLA_U32}, + [QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA] = {.type = NLA_NESTED}, + [QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD] = {.type = NLA_U32}, +}; + +/** + * hdd_send_roam_full_scan_period_to_sme() - Send full roam scan period to SME + * @hdd_ctx: HDD context + * @vdev_id: vdev id + * @full_roam_scan_period: Idle period in seconds between two successive + * full channel roam scans + * @check_and_update: If this is true/set, update the value only if the current + * configured value is not same as global value read from + * ini param. This is to give priority to the user configured + * values and retain the value, if updated already. + * If this is not set, update the value without any check. + * + * Validate the full roam scan period and send it to firmware + * + * Return: QDF_STATUS + */ +static QDF_STATUS +hdd_send_roam_full_scan_period_to_sme(struct hdd_context *hdd_ctx, + uint8_t vdev_id, + uint32_t full_roam_scan_period, + bool check_and_update) +{ + QDF_STATUS status; + uint32_t full_roam_scan_period_current, full_roam_scan_period_global; + + if (!ucfg_mlme_validate_full_roam_scan_period(full_roam_scan_period)) + return QDF_STATUS_E_INVAL; + + hdd_debug("Received Command to Set full roam scan period = %u", + full_roam_scan_period); + + status = sme_get_full_roam_scan_period(hdd_ctx->mac_handle, vdev_id, + &full_roam_scan_period_current); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + full_roam_scan_period_global = + sme_get_full_roam_scan_period_global(hdd_ctx->mac_handle); + if (check_and_update && + full_roam_scan_period_current != full_roam_scan_period_global) { + hdd_debug("Full roam scan period is already updated, value: %u", + full_roam_scan_period_current); + return QDF_STATUS_SUCCESS; + } + status = sme_update_full_roam_scan_period(hdd_ctx->mac_handle, vdev_id, + full_roam_scan_period); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set full scan period"); + + return status; +} + +/** + * wlan_hdd_convert_control_roam_trigger_reason_bitmap - Convert the + * vendor specific reason code to internal reason code. + * @trigger_reason_bitmap: Vendor specific roam trigger bitmap + * + * Return: Internal roam trigger bitmap + */ +static uint32_t +wlan_hdd_convert_control_roam_trigger_bitmap(uint32_t trigger_reason_bitmap) +{ + uint32_t drv_trigger_bitmap = 0, all_bitmap; + + /* Enable the complete trigger bitmap when all bits are set in + * the control config bitmap + */ + all_bitmap = (QCA_ROAM_TRIGGER_REASON_BSS_LOAD << 1) - 1; + if (trigger_reason_bitmap == all_bitmap) + return BIT(ROAM_TRIGGER_REASON_MAX) - 1; + + if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_PER) + drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_PER); + + if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BEACON_MISS) + drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_BMISS); + + if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_POOR_RSSI) + drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_LOW_RSSI); + + if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BETTER_RSSI) + drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_HIGH_RSSI); + + if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_PERIODIC) + drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_PERIODIC); + + if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_DENSE) + drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_DENSE); + + if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BTM) + drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_BTM); + + if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BSS_LOAD) + drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_BSS_LOAD); + + return drv_trigger_bitmap; +} + +/** + * hdd_send_roam_triggers_to_sme() - Send roam trigger bitmap to SME + * @hdd_ctx: HDD context + * @vdev_id: vdev id + * @roam_trigger_bitmap: Vendor configured roam trigger bitmap to be configured + * to firmware + * + * Send the roam trigger bitmap received to SME + * + * Return: QDF_STATUS + */ +static QDF_STATUS +hdd_send_roam_triggers_to_sme(struct hdd_context *hdd_ctx, + uint8_t vdev_id, + uint32_t roam_trigger_bitmap) +{ + QDF_STATUS status; + struct roam_triggers triggers; + struct hdd_adapter *adapter; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("adapter NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_err("Roam trigger bitmap supported only in STA mode"); + return QDF_STATUS_E_FAILURE; + } + + /* + * In standalone STA, if this vendor command is received between + * ROAM_START and roam synch indication, it is better to reject + * roam disable since driver would send vdev_params command to + * de-initialize roaming structures in fw. + * In STA+STA mode, if this vendor command to enable roaming is + * received for one STA vdev and ROAM_START was received for other + * STA vdev, then also driver would be send vdev_params command to + * de-initialize roaming structures in fw on the roaming enabled + * vdev. + */ + if (hdd_ctx->roaming_in_progress) { + hdd_err("Reject set roam trigger as roaming is in progress"); + return QDF_STATUS_E_FAILURE; + } + + triggers.vdev_id = vdev_id; + triggers.trigger_bitmap = + wlan_hdd_convert_control_roam_trigger_bitmap(roam_trigger_bitmap); + + status = sme_set_roam_triggers(hdd_ctx->mac_handle, &triggers); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set roam control trigger bitmap"); + + return status; +} + +/* + * Disable default scoring algorithm. This is intended to set all bits of the + * disable_bitmap in struct scoring_param. + */ +#define DISABLE_SCORING 0 + +/* + * Enable scoring algorithm. This is intended to clear all bits of the + * disable_bitmap in struct scoring_param. + */ +#define ENABLE_SCORING 1 + +/* + * Controlled roam candidate selection is enabled from userspace. + * Driver/firmware should honor the selection criteria + */ +#define CONTROL_ROAM_CAND_SEL_ENABLE 1 + +/* + * Controlled roam candidate selection is disabled from userspace. + * Driver/firmware can use its internal candidate selection criteria + */ +#define CONTROL_ROAM_CAND_SEL_DISABLE 0 + +static const struct nla_policy +roam_scan_cand_sel_policy[PARAM_CAND_SEL_CRITERIA_MAX + 1] = { + [PARAM_CAND_SEL_SCORE_RSSI] = {.type = NLA_U8}, +}; + +/** + * hdd_send_roam_cand_sel_criteria_to_sme() - Send candidate sel criteria to SME + * @hdd_ctx: HDD context + * @vdev_id: vdev id + * @attr: Nested attribute carrying candidate selection criteria + * + * Extract different candidate sel criteria mentioned and convert it to + * driver/firmware understable format. + * + * Return: QDF_STATUS + */ +static QDF_STATUS +hdd_send_roam_cand_sel_criteria_to_sme(struct hdd_context *hdd_ctx, + uint8_t vdev_id, + struct nlattr *attr) +{ + QDF_STATUS status; + struct nlattr *tb2[PARAM_CAND_SEL_CRITERIA_MAX + 1]; + struct nlattr *curr_attr; + uint8_t sel_criteria = 0, rssi_score = 0, scoring; + int rem; + + hdd_debug("Received Command to Set candidate selection criteria "); + nla_for_each_nested(curr_attr, attr, rem) { + sel_criteria++; + break; + } + + if (sel_criteria && + wlan_cfg80211_nla_parse_nested(tb2, PARAM_CAND_SEL_CRITERIA_MAX, + attr, roam_scan_cand_sel_policy)) { + hdd_err("nla_parse failed"); + return QDF_STATUS_E_INVAL; + } + + /* + * Firmware supports the below configurations currently, + * 1. Default selection criteria where all scoring params + * are enabled and different weightages/scores are given to + * different parameters. + * When userspace doesn't specify any candidate selection criteria, + * this will be enabled. + * 2. Legacy candidate selection criteria where scoring + * algorithm is disabled and only RSSI is considered for + * roam candidate selection. + * When userspace specify 100% weightage for RSSI, this will + * be enabled. + * Rest of the combinations are not supported for now. + */ + if (sel_criteria == CONTROL_ROAM_CAND_SEL_ENABLE) { + /* Legacy selection criteria: 100% weightage to RSSI */ + if (tb2[PARAM_CAND_SEL_SCORE_RSSI]) + rssi_score = nla_get_u8(tb2[PARAM_CAND_SEL_SCORE_RSSI]); + + if (rssi_score != 100) { + hdd_debug("Ignore the candidate selection criteria"); + return QDF_STATUS_E_INVAL; + } + scoring = DISABLE_SCORING; + } else { + /* Default selection criteria */ + scoring = ENABLE_SCORING; + } + + status = sme_modify_roam_cand_sel_criteria(hdd_ctx->mac_handle, vdev_id, + !!scoring); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to disable scoring"); + + return status; +} + +/** + * hdd_send_roam_scan_period_to_sme() - Send roam scan period to SME + * @hdd_ctx: HDD context + * @vdev_id: vdev id + * @roam_scan_period: Roam scan period in seconds + * @check_and_update: If this is true/set, update the value only if the current + * configured value is not same as global value read from + * ini param. This is to give priority to the user configured + * values and retain the value, if updated already. + * If this is not set, update the value without any check. + * + * Validate the roam scan period and send it to firmware if valid. + * + * Return: QDF_STATUS + */ +static QDF_STATUS +hdd_send_roam_scan_period_to_sme(struct hdd_context *hdd_ctx, + uint8_t vdev_id, + uint32_t roam_scan_period, + bool check_and_update) +{ + QDF_STATUS status; + uint16_t roam_scan_period_current, roam_scan_period_global; + + if (!ucfg_mlme_validate_scan_period(roam_scan_period * 1000)) + return QDF_STATUS_E_INVAL; + + hdd_debug("Received Command to Set roam scan period (Empty Scan refresh period) = %d", + roam_scan_period); + + status = sme_get_empty_scan_refresh_period(hdd_ctx->mac_handle, vdev_id, + &roam_scan_period_current); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + roam_scan_period_global = + sme_get_empty_scan_refresh_period_global(hdd_ctx->mac_handle); + if (check_and_update && + roam_scan_period_current != roam_scan_period_global) { + hdd_debug("roam scan period is already updated, value: %u", + roam_scan_period_current / 1000); + return QDF_STATUS_SUCCESS; + } + status = sme_update_empty_scan_refresh_period(hdd_ctx->mac_handle, + vdev_id, + roam_scan_period * 1000); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set scan period"); + + return status; +} + +/** + * hdd_set_roam_with_control_config() - Set roam control configuration + * @hdd_ctx: HDD context + * @tb: List of attributes carrying roam subcmd data + * @vdev_id: vdev id + * + * Extracts the attribute PARAM_ROAM_CONTROL_CONFIG from the attributes list tb + * and sends the corresponding roam control configuration to driver/firmware. + * + * Return: 0 on success; error number on failure + */ +static int +hdd_set_roam_with_control_config(struct hdd_context *hdd_ctx, + struct nlattr **tb, + uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct nlattr *tb2[QCA_ATTR_ROAM_CONTROL_MAX + 1], *attr; + uint32_t value; + + hdd_enter(); + /* The command must carry PARAM_ROAM_CONTROL_CONFIG */ + if (!tb[PARAM_ROAM_CONTROL_CONFIG]) { + hdd_err("Attribute CONTROL_CONFIG is not present"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse_nested(tb2, QCA_ATTR_ROAM_CONTROL_MAX, + tb[PARAM_ROAM_CONTROL_CONFIG], + roam_control_policy)) { + hdd_err("nla_parse failed"); + return -EINVAL; + } + + attr = tb2[PARAM_FREQ_LIST_SCHEME]; + if (attr) { + status = hdd_send_roam_scan_channel_freq_list_to_sme(hdd_ctx, + vdev_id, + attr); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("failed to config roam control"); + } + + if (tb2[QCA_ATTR_ROAM_CONTROL_TRIGGERS]) { + value = nla_get_u32(tb2[QCA_ATTR_ROAM_CONTROL_TRIGGERS]); + hdd_debug("Received roam trigger bitmap: 0x%x", value); + status = hdd_send_roam_triggers_to_sme(hdd_ctx, + vdev_id, + value); + if (status) + hdd_err("failed to config roam triggers"); + } + + attr = tb2[QCA_ATTR_ROAM_CONTROL_ENABLE]; + if (attr) { + status = sme_set_roam_config_enable(hdd_ctx->mac_handle, + vdev_id, + nla_get_u8(attr)); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("failed to enable/disable roam control config"); + + hdd_debug("Parse and send roam control %s:", + nla_get_u8(attr) ? "Enable" : "Disable"); + + attr = tb2[QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD]; + if (attr) { + /* Default value received as part of Roam control enable + * Set this only if user hasn't configured any value so + * far. + */ + value = nla_get_u32(attr); + status = hdd_send_roam_scan_period_to_sme(hdd_ctx, + vdev_id, + value, true); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("failed to send scan period to firmware"); + } + + attr = tb2[QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD]; + if (attr) { + value = nla_get_u32(attr); + /* Default value received as part of Roam control enable + * Set this only if user hasn't configured any value so + * far. + */ + status = hdd_send_roam_full_scan_period_to_sme(hdd_ctx, + vdev_id, + value, + true); + if (status) + hdd_err("failed to config full scan period"); + } + } else { + attr = tb2[QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD]; + if (attr) { + /* User configured value, cache the value directly */ + value = nla_get_u32(attr); + status = hdd_send_roam_scan_period_to_sme(hdd_ctx, + vdev_id, + value, false); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("failed to send scan period to firmware"); + } + + attr = tb2[QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD]; + if (attr) { + value = nla_get_u32(attr); + /* User configured value, cache the value directly */ + status = hdd_send_roam_full_scan_period_to_sme(hdd_ctx, + vdev_id, + value, + false); + if (status) + hdd_err("failed to config full scan period"); + } + } + + /* Scoring and roam candidate selection criteria */ + attr = tb2[QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA]; + if (attr) { + status = hdd_send_roam_cand_sel_criteria_to_sme(hdd_ctx, + vdev_id, attr); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("failed to set candidate selection criteria"); + } + + return qdf_status_to_os_return(status); +} + +#define ENABLE_ROAM_TRIGGERS_ALL (QCA_ROAM_TRIGGER_REASON_PER | \ + QCA_ROAM_TRIGGER_REASON_BEACON_MISS | \ + QCA_ROAM_TRIGGER_REASON_POOR_RSSI | \ + QCA_ROAM_TRIGGER_REASON_BETTER_RSSI | \ + QCA_ROAM_TRIGGER_REASON_PERIODIC | \ + QCA_ROAM_TRIGGER_REASON_DENSE | \ + QCA_ROAM_TRIGGER_REASON_BTM | \ + QCA_ROAM_TRIGGER_REASON_BSS_LOAD) + +static int +hdd_clear_roam_control_config(struct hdd_context *hdd_ctx, + struct nlattr **tb, + uint8_t vdev_id) +{ + QDF_STATUS status; + struct nlattr *tb2[QCA_ATTR_ROAM_CONTROL_MAX + 1]; + mac_handle_t mac_handle = hdd_ctx->mac_handle; + uint32_t value; + + /* The command must carry PARAM_ROAM_CONTROL_CONFIG */ + if (!tb[PARAM_ROAM_CONTROL_CONFIG]) { + hdd_err("Attribute CONTROL_CONFIG is not present"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse_nested(tb2, QCA_ATTR_ROAM_CONTROL_MAX, + tb[PARAM_ROAM_CONTROL_CONFIG], + roam_control_policy)) { + hdd_err("nla_parse failed"); + return -EINVAL; + } + + hdd_debug("Clear the control config done through SET"); + if (tb2[QCA_ATTR_ROAM_CONTROL_CLEAR_ALL]) { + hdd_debug("Disable roam control config done through SET"); + status = sme_set_roam_config_enable(hdd_ctx->mac_handle, + vdev_id, 0); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed to enable/disable roam control config"); + return qdf_status_to_os_return(status); + } + + value = ENABLE_ROAM_TRIGGERS_ALL; + hdd_debug("Reset roam trigger bitmap to 0x%x", value); + status = hdd_send_roam_triggers_to_sme(hdd_ctx, vdev_id, value); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed to restore roam trigger bitmap"); + return qdf_status_to_os_return(status); + } + + status = sme_roam_control_restore_default_config(mac_handle, + vdev_id); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed to config roam control"); + return qdf_status_to_os_return(status); + } + } + + return 0; +} + +/** + * hdd_roam_control_config_buf_size() - Calculate the skb size to be allocated + * @hdd_ctx: HDD context + * @tb: List of attributes to be populated + * + * Calculate the buffer size to be allocated based on the attributes + * mentioned in tb. + * + * Return: buffer size to be allocated + */ +static uint16_t +hdd_roam_control_config_buf_size(struct hdd_context *hdd_ctx, + struct nlattr **tb) +{ + uint16_t skb_len = 0; + + if (tb[QCA_ATTR_ROAM_CONTROL_STATUS]) + skb_len += NLA_HDRLEN + sizeof(uint8_t); + + if (tb[QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD]) + skb_len += NLA_HDRLEN + sizeof(uint32_t); + + if (tb[QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME]) + /* + * Response has 3 nests, 1 atrribure value and a + * attribute list of frequencies. + */ + skb_len += 3 * nla_total_size(0) + + nla_total_size(sizeof(uint32_t)) + + (nla_total_size(sizeof(uint32_t)) * + NUM_CHANNELS); + + return skb_len; +} + +/** + * hdd_roam_control_config_fill_data() - Fill the data requested by userspace + * @hdd_ctx: HDD context + * @vdev_id: vdev id + * @skb: SK buffer + * @tb: List of attributes + * + * Get the data corresponding to the attribute list specified in tb and + * update the same to skb by populating the same attributes. + * + * Return: 0 on success; error number on failure + */ +static int +hdd_roam_control_config_fill_data(struct hdd_context *hdd_ctx, uint8_t vdev_id, + struct sk_buff *skb, struct nlattr **tb) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t roam_control; + struct nlattr *config, *get_freq_scheme, *get_freq; + uint32_t full_roam_scan_period; + uint8_t num_channels = 0; + uint32_t i = 0, freq_list[NUM_CHANNELS] = { 0 }; + struct hdd_adapter *hdd_adapter = NULL; + + config = nla_nest_start(skb, PARAM_ROAM_CONTROL_CONFIG); + if (!config) { + hdd_err("nla nest start failure"); + return -EINVAL; + } + + if (tb[QCA_ATTR_ROAM_CONTROL_STATUS]) { + status = sme_get_roam_config_status(hdd_ctx->mac_handle, + vdev_id, + &roam_control); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + hdd_debug("Roam control: %s", + roam_control ? "Enabled" : "Disabled"); + if (nla_put_u8(skb, QCA_ATTR_ROAM_CONTROL_STATUS, + roam_control)) { + hdd_info("failed to put vendor_roam_control"); + return -ENOMEM; + } + } + + if (tb[QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD]) { + status = sme_get_full_roam_scan_period(hdd_ctx->mac_handle, + vdev_id, + &full_roam_scan_period); + if (QDF_IS_STATUS_ERROR(status)) + goto out; + hdd_debug("full_roam_scan_period: %u", full_roam_scan_period); + + if (nla_put_u32(skb, QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD, + full_roam_scan_period)) { + hdd_info("failed to put full_roam_scan_period"); + return -EINVAL; + } + } + + if (tb[QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME]) { + hdd_adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!hdd_adapter) { + hdd_info("HDD adapter is NULL"); + return -EINVAL; + } + + hdd_debug("Get roam scan frequencies req received"); + status = hdd_get_roam_scan_freq(hdd_adapter, + hdd_ctx->mac_handle, + freq_list, &num_channels); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_info("failed to get roam scan freq"); + goto out; + } + + hdd_debug("num_channels %d", num_channels); + get_freq_scheme = nla_nest_start( + skb, QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME); + if (!get_freq_scheme) { + hdd_info("failed to nest start for roam scan freq"); + return -EINVAL; + } + + if (nla_put_u32(skb, PARAM_SCAN_FREQ_LIST_TYPE, 0)) { + hdd_info("failed to put list type"); + return -EINVAL; + } + + get_freq = nla_nest_start( + skb, QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST); + if (!get_freq) { + hdd_info("failed to nest start for roam scan freq"); + return -EINVAL; + } + + for (i = 0; i < num_channels; i++) { + if (nla_put_u32(skb, PARAM_SCAN_FREQ_LIST, + freq_list[i])) { + hdd_info("failed to put freq at index %d", i); + return -EINVAL; + } + } + nla_nest_end(skb, get_freq); + nla_nest_end(skb, get_freq_scheme); + } + nla_nest_end(skb, config); + +out: + return qdf_status_to_os_return(status); +} + +/** + * hdd_send_roam_control_config() - Send the roam config as vendor cmd reply + * @mac_handle: Opaque handle to the MAC context + * @vdev_id: vdev id + * @tb: List of attributes + * + * Parse the attributes list tb and get the data corresponding to the + * attributes specified in tb. Send them as a vendor response. + * + * Return: 0 on success; error number on failure + */ +static int +hdd_send_roam_control_config(struct hdd_context *hdd_ctx, + uint8_t vdev_id, + struct nlattr **tb) +{ + struct sk_buff *skb; + uint16_t skb_len; + int status; + + skb_len = hdd_roam_control_config_buf_size(hdd_ctx, tb); + if (!skb_len) { + hdd_err("No data requested"); + return -EINVAL; + } + + skb_len += NLMSG_HDRLEN; + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, skb_len); + if (!skb) { + hdd_info("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + status = hdd_roam_control_config_fill_data(hdd_ctx, vdev_id, skb, tb); + if (status) + goto fail; + + return cfg80211_vendor_cmd_reply(skb); + +fail: + hdd_err("nla put fail"); + kfree_skb(skb); + return status; +} + +/** + * hdd_get_roam_control_config() - Send requested roam config to userspace + * @hdd_ctx: HDD context + * @tb: list of attributes + * @vdev_id: vdev id + * + * Return: 0 on success; error number on failure + */ +static int hdd_get_roam_control_config(struct hdd_context *hdd_ctx, + struct nlattr **tb, + uint8_t vdev_id) +{ + QDF_STATUS status; + struct nlattr *tb2[QCA_ATTR_ROAM_CONTROL_MAX + 1]; + + /* The command must carry PARAM_ROAM_CONTROL_CONFIG */ + if (!tb[PARAM_ROAM_CONTROL_CONFIG]) { + hdd_err("Attribute CONTROL_CONFIG is not present"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse_nested(tb2, QCA_ATTR_ROAM_CONTROL_MAX, + tb[PARAM_ROAM_CONTROL_CONFIG], + roam_control_policy)) { + hdd_err("nla_parse failed"); + return -EINVAL; + } + + status = hdd_send_roam_control_config(hdd_ctx, vdev_id, tb2); + if (status) { + hdd_err("failed to enable/disable roam control"); + return status; + } + + return qdf_status_to_os_return(status); +} + +#undef PARAM_ROAM_CONTROL_CONFIG +#undef PARAM_FREQ_LIST_SCHEME_MAX +#undef PARAM_FREQ_LIST_SCHEME +#undef PARAM_SCAN_FREQ_LIST +#undef PARAM_SCAN_FREQ_LIST_TYPE +#undef PARAM_CAND_SEL_CRITERIA_MAX +#undef PARAM_CAND_SEL_SCORE_RSSI + +/** + * hdd_set_ext_roam_params() - parse ext roam params + * @hdd_ctx: HDD context + * @roam_params: roam params + * @tb: list of attributes + * @vdev_id: vdev id + * + * Return: 0 on success; error number on failure + */ +static int hdd_set_ext_roam_params(struct hdd_context *hdd_ctx, + const void *data, int data_len, + uint8_t vdev_id, + struct roam_ext_params *roam_params) +{ + uint32_t cmd_type, req_id; + struct nlattr *tb[MAX_ROAMING_PARAM + 1]; + int ret; + mac_handle_t mac_handle; + + if (wlan_cfg80211_nla_parse(tb, MAX_ROAMING_PARAM, data, data_len, + wlan_hdd_set_roam_param_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + /* Parse and fetch Command Type */ + if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]) { + hdd_err("roam cmd type failed"); + goto fail; + } + + cmd_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]); + if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]) { + hdd_err("attr request id failed"); + goto fail; + } + mac_handle = hdd_ctx->mac_handle; + req_id = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]); + hdd_debug("Req Id: %u Cmd Type: %u", req_id, cmd_type); + switch (cmd_type) { + case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SSID_WHITE_LIST: + ret = hdd_set_white_list(hdd_ctx, roam_params, tb, vdev_id); + if (ret) + goto fail; + break; + + case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_EXTSCAN_ROAM_PARAMS: + /* Parse and fetch 5G Boost Threshold */ + if (!tb[PARAM_A_BAND_BOOST_THLD]) { + hdd_err("5G boost threshold failed"); + goto fail; + } + roam_params->raise_rssi_thresh_5g = nla_get_s32( + tb[PARAM_A_BAND_BOOST_THLD]); + hdd_debug("5G Boost Threshold (%d)", + roam_params->raise_rssi_thresh_5g); + /* Parse and fetch 5G Penalty Threshold */ + if (!tb[PARAM_A_BAND_PELT_THLD]) { + hdd_err("5G penalty threshold failed"); + goto fail; + } + roam_params->drop_rssi_thresh_5g = nla_get_s32( + tb[PARAM_A_BAND_PELT_THLD]); + hdd_debug("5G Penalty Threshold (%d)", + roam_params->drop_rssi_thresh_5g); + /* Parse and fetch 5G Boost Factor */ + if (!tb[PARAM_A_BAND_BOOST_FACTOR]) { + hdd_err("5G boost Factor failed"); + goto fail; + } + roam_params->raise_factor_5g = nla_get_u32( + tb[PARAM_A_BAND_BOOST_FACTOR]); + hdd_debug("5G Boost Factor (%d)", + roam_params->raise_factor_5g); + /* Parse and fetch 5G Penalty factor */ + if (!tb[PARAM_A_BAND_PELT_FACTOR]) { + hdd_err("5G Penalty Factor failed"); + goto fail; + } + roam_params->drop_factor_5g = nla_get_u32( + tb[PARAM_A_BAND_PELT_FACTOR]); + hdd_debug("5G Penalty factor (%d)", + roam_params->drop_factor_5g); + /* Parse and fetch 5G Max Boost */ + if (!tb[PARAM_A_BAND_MAX_BOOST]) { + hdd_err("5G Max Boost failed"); + goto fail; + } + roam_params->max_raise_rssi_5g = nla_get_u32( + tb[PARAM_A_BAND_MAX_BOOST]); + hdd_debug("5G Max Boost (%d)", + roam_params->max_raise_rssi_5g); + /* Parse and fetch Rssi Diff */ + if (!tb[PARAM_ROAM_HISTERESYS]) { + hdd_err("Rssi Diff failed"); + goto fail; + } + roam_params->rssi_diff = nla_get_s32( + tb[PARAM_ROAM_HISTERESYS]); + hdd_debug("RSSI Diff (%d)", + roam_params->rssi_diff); + /* Parse and fetch Alert Rssi Threshold */ + if (!tb[PARAM_RSSI_TRIGGER]) { + hdd_err("Alert Rssi Threshold failed"); + goto fail; + } + roam_params->alert_rssi_threshold = nla_get_u32( + tb[PARAM_RSSI_TRIGGER]); + hdd_debug("Alert RSSI Threshold (%d)", + roam_params->alert_rssi_threshold); + sme_update_roam_params(mac_handle, vdev_id, + roam_params, + REASON_ROAM_EXT_SCAN_PARAMS_CHANGED); + break; + case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_LAZY_ROAM: + /* Parse and fetch Activate Good Rssi Roam */ + if (!tb[PARAM_ROAM_ENABLE]) { + hdd_err("Activate Good Rssi Roam failed"); + goto fail; + } + roam_params->good_rssi_roam = nla_get_s32( + tb[PARAM_ROAM_ENABLE]); + hdd_debug("Activate Good Rssi Roam (%d)", + roam_params->good_rssi_roam); + sme_update_roam_params(mac_handle, vdev_id, + roam_params, + REASON_ROAM_GOOD_RSSI_CHANGED); + break; + case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PREFS: + ret = hdd_set_bssid_prefs(hdd_ctx, roam_params, tb, vdev_id); + if (ret) + goto fail; + break; + case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: + ret = hdd_set_blacklist_bssid(hdd_ctx, roam_params, + tb, vdev_id); + if (ret) + goto fail; + break; + case QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET: + ret = hdd_set_roam_with_control_config(hdd_ctx, tb, vdev_id); + if (ret) + goto fail; + break; + case QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR: + ret = hdd_clear_roam_control_config(hdd_ctx, tb, vdev_id); + if (ret) + goto fail; + break; + case QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET: + ret = hdd_get_roam_control_config(hdd_ctx, tb, vdev_id); + if (ret) + goto fail; + break; + } + + return 0; + +fail: + return -EINVAL; +} + +/** + * __wlan_hdd_cfg80211_set_ext_roam_params() - Settings for roaming parameters + * @wiphy: The wiphy structure + * @wdev: The wireless device + * @data: Data passed by framework + * @data_len: Parameters to be configured passed as data + * + * The roaming related parameters are configured by the framework + * using this interface. + * + * Return: Return either success or failure code. + */ +static int +__wlan_hdd_cfg80211_set_ext_roam_params(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct roam_ext_params *roam_params = NULL; + int ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_err("Driver Modules are closed"); + return -EINVAL; + } + + roam_params = qdf_mem_malloc(sizeof(*roam_params)); + if (!roam_params) + return -ENOMEM; + + ret = hdd_set_ext_roam_params(hdd_ctx, data, data_len, + adapter->vdev_id, roam_params); + if (ret) + goto fail; + + if (roam_params) + qdf_mem_free(roam_params); + return 0; +fail: + if (roam_params) + qdf_mem_free(roam_params); + + return ret; +} +#undef PARAM_NUM_NW +#undef PARAM_SET_BSSID +#undef PARAM_SET_BSSID_HINT +#undef PARAM_SSID_LIST +#undef PARAM_LIST_SSID +#undef MAX_ROAMING_PARAM +#undef PARAM_NUM_BSSID +#undef PARAM_BSSID_PREFS +#undef PARAM_ROAM_BSSID +#undef PARAM_RSSI_MODIFIER +#undef PARAMS_NUM_BSSID +#undef PARAM_BSSID_PARAMS +#undef PARAM_A_BAND_BOOST_THLD +#undef PARAM_A_BAND_PELT_THLD +#undef PARAM_A_BAND_BOOST_FACTOR +#undef PARAM_A_BAND_PELT_FACTOR +#undef PARAM_A_BAND_MAX_BOOST +#undef PARAM_ROAM_HISTERESYS +#undef PARAM_RSSI_TRIGGER +#undef PARAM_ROAM_ENABLE + + +/** + * wlan_hdd_cfg80211_set_ext_roam_params() - set ext scan roam params + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Return: Return the Success or Failure code. + */ +static int +wlan_hdd_cfg80211_set_ext_roam_params(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_ext_roam_params(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#define PWR_SAVE_FAIL_CMD_INDEX \ + QCA_NL80211_VENDOR_SUBCMD_PWR_SAVE_FAIL_DETECTED_INDEX + +void hdd_chip_pwr_save_fail_detected_cb(hdd_handle_t hdd_handle, + struct chip_pwr_save_fail_detected_params + *data) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + struct sk_buff *skb; + int flags = cds_get_gfp_flags(); + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + if (!data) { + hdd_debug("data is null"); + return; + } + + skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, NLMSG_HDRLEN + + sizeof(data->failure_reason_code) + + NLMSG_HDRLEN, PWR_SAVE_FAIL_CMD_INDEX, + flags); + + if (!skb) { + hdd_info("cfg80211_vendor_event_alloc failed"); + return; + } + + hdd_debug("failure reason code: %u", data->failure_reason_code); + + if (nla_put_u32(skb, + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON, + data->failure_reason_code)) + goto fail; + + cfg80211_vendor_event(skb, flags); + hdd_exit(); + return; + +fail: + kfree_skb(skb); +} +#undef PWR_SAVE_FAIL_CMD_INDEX + +static const struct nla_policy +wlan_hdd_set_no_dfs_flag_config_policy[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX + +1] = { + [QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG] = {.type = NLA_U32 }, +}; + +/** + * wlan_hdd_check_dfs_channel_for_adapter() - check dfs channel in adapter + * @hdd_ctx: HDD context + * @device_mode: device mode + * Return: bool + */ +static bool wlan_hdd_check_dfs_channel_for_adapter(struct hdd_context *hdd_ctx, + enum QDF_OPMODE device_mode) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + struct hdd_ap_ctx *ap_ctx; + struct hdd_station_ctx *sta_ctx; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_CHECK_DFS_CHANNEL_FOR_ADAPTER; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if ((device_mode == adapter->device_mode) && + (device_mode == QDF_SAP_MODE)) { + ap_ctx = + WLAN_HDD_GET_AP_CTX_PTR(adapter); + /* + * if there is SAP already running on DFS channel, + * do not disable scan on dfs channels. Note that + * with SAP on DFS, there cannot be conurrency on + * single radio. But then we can have multiple + * radios !! + */ + if (CHANNEL_STATE_DFS == + wlan_reg_get_channel_state_for_freq( + hdd_ctx->pdev, + ap_ctx->operating_chan_freq)) { + hdd_err("SAP running on DFS channel"); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return true; + } + } + + if ((device_mode == adapter->device_mode) && + (device_mode == QDF_STA_MODE)) { + sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + /* + * if STA is already connected on DFS channel, + * do not disable scan on dfs channels + */ + if (hdd_conn_is_connected(sta_ctx) && + (CHANNEL_STATE_DFS == + wlan_reg_get_channel_state_for_freq( + hdd_ctx->pdev, + sta_ctx->conn_info.chan_freq))) { + hdd_err("client connected on DFS channel"); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return true; + } + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return false; +} + +/** + * wlan_hdd_enable_dfs_chan_scan() - disable/enable DFS channels + * @hdd_ctx: HDD context within host driver + * @enable_dfs_channels: If true, DFS channels can be used for scanning + * + * Loops through devices to see who is operating on DFS channels + * and then disables/enables DFS channels. + * Fails the disable request if any device is active on a DFS channel. + * + * Return: 0 or other error codes. + */ + +int wlan_hdd_enable_dfs_chan_scan(struct hdd_context *hdd_ctx, + bool enable_dfs_channels) +{ + QDF_STATUS status; + bool err; + mac_handle_t mac_handle; + bool enable_dfs_scan = true; + + ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc, + &enable_dfs_scan); + + if (enable_dfs_channels == enable_dfs_scan) { + hdd_debug("DFS channels are already %s", + enable_dfs_channels ? "enabled" : "disabled"); + return 0; + } + + if (!enable_dfs_channels) { + err = wlan_hdd_check_dfs_channel_for_adapter(hdd_ctx, + QDF_STA_MODE); + if (err) + return -EOPNOTSUPP; + + err = wlan_hdd_check_dfs_channel_for_adapter(hdd_ctx, + QDF_SAP_MODE); + if (err) + return -EOPNOTSUPP; + } + + ucfg_scan_cfg_set_dfs_chan_scan_allowed(hdd_ctx->psoc, + enable_dfs_channels); + + mac_handle = hdd_ctx->mac_handle; + status = sme_enable_dfs_chan_scan(mac_handle, enable_dfs_channels); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set DFS channel scan flag to %d", + enable_dfs_channels); + return qdf_status_to_os_return(status); + } + + hdd_abort_mac_scan_all_adapters(hdd_ctx); + + /* pass dfs channel status to regulatory component */ + status = ucfg_reg_enable_dfs_channels(hdd_ctx->pdev, + enable_dfs_channels); + + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to %s DFS channels", + enable_dfs_channels ? "enable" : "disable"); + + return qdf_status_to_os_return(status); +} + +/** + * __wlan_hdd_cfg80211_disable_dfs_chan_scan() - DFS channel configuration + * @wiphy: corestack handler + * @wdev: wireless device + * @data: data + * @data_len: data length + * Return: success(0) or reason code for failure + */ +static int __wlan_hdd_cfg80211_disable_dfs_chan_scan(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX + 1]; + int ret_val; + uint32_t no_dfs_flag = 0; + bool enable_dfs_scan = true; + hdd_enter_dev(dev); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX, + data, data_len, + wlan_hdd_set_no_dfs_flag_config_policy)) { + hdd_err("invalid attr"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]) { + hdd_err("attr dfs flag failed"); + return -EINVAL; + } + + no_dfs_flag = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]); + + hdd_debug("DFS flag: %d", no_dfs_flag); + + if (no_dfs_flag > 1) { + hdd_err("invalid value of dfs flag"); + return -EINVAL; + } + ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc, + &enable_dfs_scan); + + if (enable_dfs_scan) { + ret_val = wlan_hdd_enable_dfs_chan_scan(hdd_ctx, !no_dfs_flag); + } else { + if ((!no_dfs_flag) != enable_dfs_scan) { + hdd_err("DFS chan ini configured %d, no dfs flag: %d", + enable_dfs_scan, + no_dfs_flag); + return -EINVAL; + } + } + + return ret_val; +} + +/** + * wlan_hdd_cfg80211_disable_dfs_chan_scan () - DFS scan vendor command + * + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Handles QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX. Validate it and + * call wlan_hdd_disable_dfs_chan_scan to send it to firmware. + * + * Return: EOK or other error codes. + */ + +static int wlan_hdd_cfg80211_disable_dfs_chan_scan(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_disable_dfs_chan_scan(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +static const struct nla_policy +wlan_hdd_wisa_cmd_policy[QCA_WLAN_VENDOR_ATTR_WISA_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_WISA_MODE] = {.type = NLA_U32 }, +}; + +/** + * __wlan_hdd_cfg80211_handle_wisa_cmd() - Handle WISA vendor cmd + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Handles QCA_WLAN_VENDOR_SUBCMD_WISA. Validate cmd attributes and + * setup WISA Mode features. + * + * Return: Success(0) or reason code for failure + */ +static int __wlan_hdd_cfg80211_handle_wisa_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WISA_MAX + 1]; + struct sir_wisa_params wisa; + int ret_val; + QDF_STATUS status; + bool wisa_mode; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + mac_handle_t mac_handle; + + hdd_enter_dev(dev); + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + goto err; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_WISA_MAX, data, + data_len, wlan_hdd_wisa_cmd_policy)) { + hdd_err("Invalid WISA cmd attributes"); + ret_val = -EINVAL; + goto err; + } + if (!tb[QCA_WLAN_VENDOR_ATTR_WISA_MODE]) { + hdd_err("Invalid WISA mode"); + ret_val = -EINVAL; + goto err; + } + + wisa_mode = !!nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_WISA_MODE]); + hdd_debug("WISA Mode: %d", wisa_mode); + wisa.mode = wisa_mode; + wisa.vdev_id = adapter->vdev_id; + mac_handle = hdd_ctx->mac_handle; + status = sme_set_wisa_params(mac_handle, &wisa); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Unable to set WISA mode: %d to FW", wisa_mode); + ret_val = -EINVAL; + } + if (QDF_IS_STATUS_SUCCESS(status) || !wisa_mode) + cdp_set_wisa_mode(soc, adapter->vdev_id, wisa_mode); +err: + hdd_exit(); + return ret_val; +} + +/** + * wlan_hdd_cfg80211_handle_wisa_cmd() - Handle WISA vendor cmd + * @wiphy: corestack handler + * @wdev: wireless device + * @data: data + * @data_len: data length + * + * Handles QCA_WLAN_VENDOR_SUBCMD_WISA. Validate cmd attributes and + * setup WISA mode features. + * + * Return: Success(0) or reason code for failure + */ +static int wlan_hdd_cfg80211_handle_wisa_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_handle_wisa_cmd(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +struct hdd_station_info *hdd_get_stainfo(struct hdd_station_info *astainfo, + struct qdf_mac_addr mac_addr) +{ + struct hdd_station_info *stainfo = NULL; + int i; + + for (i = 0; i < WLAN_MAX_STA_COUNT; i++) { + if (!qdf_mem_cmp(&astainfo[i].sta_mac, + &mac_addr, + QDF_MAC_ADDR_SIZE)) { + stainfo = &astainfo[i]; + break; + } + } + + return stainfo; +} + +/* + * undef short names defined for get station command + * used by __wlan_hdd_cfg80211_get_station_cmd() + */ +#undef STATION_INVALID +#undef STATION_INFO +#undef STATION_ASSOC_FAIL_REASON +#undef STATION_REMOTE +#undef STATION_MAX +#undef LINK_INFO_STANDARD_NL80211_ATTR +#undef AP_INFO_STANDARD_NL80211_ATTR +#undef INFO_ROAM_COUNT +#undef INFO_AKM +#undef WLAN802_11_MODE +#undef AP_INFO_HS20_INDICATION +#undef HT_OPERATION +#undef VHT_OPERATION +#undef INFO_ASSOC_FAIL_REASON +#undef REMOTE_MAX_PHY_RATE +#undef REMOTE_TX_PACKETS +#undef REMOTE_TX_BYTES +#undef REMOTE_RX_PACKETS +#undef REMOTE_RX_BYTES +#undef REMOTE_LAST_TX_RATE +#undef REMOTE_LAST_RX_RATE +#undef REMOTE_WMM +#undef REMOTE_SUPPORTED_MODE +#undef REMOTE_AMPDU +#undef REMOTE_TX_STBC +#undef REMOTE_RX_STBC +#undef REMOTE_CH_WIDTH +#undef REMOTE_SGI_ENABLE +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) +#undef REMOTE_PAD +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + +/** + * hdd_get_roam_reason() - convert wmi roam reason to + * enum qca_roam_reason + * @roam_scan_trigger: wmi roam scan trigger ID + * + * Return: Meaningful qca_roam_reason from enum WMI_ROAM_TRIGGER_REASON_ID + */ +static enum qca_roam_reason hdd_get_roam_reason(uint16_t roam_scan_trigger) +{ + switch (roam_scan_trigger) { + case WMI_ROAM_TRIGGER_REASON_PER: + return QCA_ROAM_REASON_PER; + case WMI_ROAM_TRIGGER_REASON_BMISS: + return QCA_ROAM_REASON_BEACON_MISS; + case WMI_ROAM_TRIGGER_REASON_LOW_RSSI: + case WMI_ROAM_TRIGGER_REASON_BACKGROUND: + return QCA_ROAM_REASON_POOR_RSSI; + case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI: + return QCA_ROAM_REASON_BETTER_RSSI; + case WMI_ROAM_TRIGGER_REASON_DENSE: + return QCA_ROAM_REASON_CONGESTION; + case WMI_ROAM_TRIGGER_REASON_FORCED: + return QCA_ROAM_REASON_USER_TRIGGER; + case WMI_ROAM_TRIGGER_REASON_BTM: + return QCA_ROAM_REASON_BTM; + case WMI_ROAM_TRIGGER_REASON_BSS_LOAD: + return QCA_ROAM_REASON_BSS_LOAD; + default: + return QCA_ROAM_REASON_UNKNOWN; + } + + return QCA_ROAM_REASON_UNKNOWN; +} + +/** + * __wlan_hdd_cfg80211_keymgmt_set_key() - Store the Keys in the driver session + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the Key data + * @data_len:Length of the data passed + * + * This is called when wlan driver needs to save the keys received via + * vendor specific command. + * + * Return: Return the Success or Failure code. + */ +static int __wlan_hdd_cfg80211_keymgmt_set_key(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + uint8_t local_pmk[SIR_ROAM_SCAN_PSK_SIZE]; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *hdd_adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + int status; + struct pmkid_mode_bits pmkid_modes; + mac_handle_t mac_handle; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if ((!data) || (data_len <= 0) || + (data_len > SIR_ROAM_SCAN_PSK_SIZE)) { + hdd_err("Invalid data"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(hdd_adapter); + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return -EINVAL; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes); + + mac_handle = hdd_ctx->mac_handle; + sme_update_roam_key_mgmt_offload_enabled(mac_handle, + hdd_adapter->vdev_id, + true, &pmkid_modes); + qdf_mem_zero(&local_pmk, SIR_ROAM_SCAN_PSK_SIZE); + qdf_mem_copy(local_pmk, data, data_len); + sme_roam_set_psk_pmk(mac_handle, hdd_adapter->vdev_id, + local_pmk, data_len, true); + qdf_mem_zero(&local_pmk, SIR_ROAM_SCAN_PSK_SIZE); + return 0; +} + +/** + * wlan_hdd_cfg80211_keymgmt_set_key() - Store the Keys in the driver session + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the Key data + * @data_len:Length of the data passed + * + * This is called when wlan driver needs to save the keys received via + * vendor specific command. + * + * Return: Return the Success or Failure code. + */ +static int wlan_hdd_cfg80211_keymgmt_set_key(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_keymgmt_set_key(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif + +static const struct nla_policy qca_wlan_vendor_get_wifi_info_policy[ + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX] = {.type = NLA_U32 }, +}; + +/** + * __wlan_hdd_cfg80211_get_wifi_info() - Get the wifi driver related info + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This is called when wlan driver needs to send wifi driver related info + * (driver/fw version) to the user space application upon request. + * + * Return: Return the Success or Failure code. + */ +static int +__wlan_hdd_cfg80211_get_wifi_info(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1]; + tSirVersionString driver_version; + tSirVersionString firmware_version; + int status; + struct sk_buff *reply_skb; + uint32_t skb_len = 0, count = 0; + + hdd_enter_dev(wdev->netdev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + if (wlan_cfg80211_nla_parse(tb_vendor, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX, + data, data_len, + qca_wlan_vendor_get_wifi_info_policy)) { + hdd_err("WIFI_INFO_GET NL CMD parsing failed"); + return -EINVAL; + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) { + hdd_debug("Rcvd req for Driver version"); + strlcpy(driver_version, QWLAN_VERSIONSTR, + sizeof(driver_version)); + skb_len += strlen(driver_version) + 1; + count++; + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) { + hdd_debug("Rcvd req for FW version"); + snprintf(firmware_version, sizeof(firmware_version), + "FW:%d.%d.%d.%d.%d.%d HW:%s", + hdd_ctx->fw_version_info.major_spid, + hdd_ctx->fw_version_info.minor_spid, + hdd_ctx->fw_version_info.siid, + hdd_ctx->fw_version_info.rel_id, + hdd_ctx->fw_version_info.crmid, + hdd_ctx->fw_version_info.sub_id, + hdd_ctx->target_hw_name); + skb_len += strlen(firmware_version) + 1; + count++; + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX]) { + hdd_debug("Rcvd req for Radio index"); + skb_len += sizeof(uint32_t); + count++; + } + + if (count == 0) { + hdd_err("unknown attribute in get_wifi_info request"); + return -EINVAL; + } + + skb_len += (NLA_HDRLEN * count) + NLMSG_HDRLEN; + reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len); + + if (!reply_skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) { + if (nla_put_string(reply_skb, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION, + driver_version)) + goto error_nla_fail; + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) { + if (nla_put_string(reply_skb, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION, + firmware_version)) + goto error_nla_fail; + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX]) { + if (nla_put_u32(reply_skb, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX, + hdd_ctx->radio_index)) + goto error_nla_fail; + } + + return cfg80211_vendor_cmd_reply(reply_skb); + +error_nla_fail: + hdd_err("nla put fail"); + kfree_skb(reply_skb); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_get_wifi_info() - Get the wifi driver related info + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This is called when wlan driver needs to send wifi driver related info + * (driver/fw version) to the user space application upon request. + * + * Return: Return the Success or Failure code. + */ +static int +wlan_hdd_cfg80211_get_wifi_info(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_wifi_info(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This is called by userspace to know the supported logger features + * + * Return: Return the Success or Failure code. + */ +static int +__wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int status; + uint32_t features; + struct sk_buff *reply_skb = NULL; + bool enable_ring_buffer; + + hdd_enter_dev(wdev->netdev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + features = 0; + wlan_mlme_get_status_ring_buffer(hdd_ctx->psoc, &enable_ring_buffer); + if (enable_ring_buffer) { + features |= WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_SUPPORTED; + features |= WIFI_LOGGER_CONNECT_EVENT_SUPPORTED; + features |= WIFI_LOGGER_WAKE_LOCK_SUPPORTED; + features |= WIFI_LOGGER_DRIVER_DUMP_SUPPORTED; + features |= WIFI_LOGGER_PACKET_FATE_SUPPORTED; + hdd_debug("Supported logger features: 0x%0x", features); + } else { + hdd_info("Ring buffer disable"); + } + + reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, + sizeof(uint32_t) + NLA_HDRLEN + NLMSG_HDRLEN); + if (!reply_skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED, + features)) { + hdd_err("nla put fail"); + kfree_skb(reply_skb); + return -EINVAL; + } + + return cfg80211_vendor_cmd_reply(reply_skb); +} + +/** + * wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This is called by userspace to know the supported logger features + * + * Return: Return the Success or Failure code. + */ +static int +wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_logger_supp_feature(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +#ifdef WLAN_FEATURE_GTK_OFFLOAD +void wlan_hdd_save_gtk_offload_params(struct hdd_adapter *adapter, + uint8_t *kck_ptr, uint8_t kck_len, + uint8_t *kek_ptr, uint32_t kek_len, + uint8_t *replay_ctr, bool big_endian) +{ + struct hdd_station_ctx *hdd_sta_ctx; + uint8_t *buf; + int i; + struct pmo_gtk_req *gtk_req = NULL; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + gtk_req = qdf_mem_malloc(sizeof(*gtk_req)); + if (!gtk_req) + return; + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (kck_ptr) { + if (kck_len > sizeof(gtk_req->kck)) { + kck_len = sizeof(gtk_req->kck); + QDF_ASSERT(0); + } + qdf_mem_copy(gtk_req->kck, kck_ptr, kck_len); + gtk_req->kck_len = kck_len; + } + + if (kek_ptr) { + /* paranoia */ + if (kek_len > sizeof(gtk_req->kek)) { + kek_len = sizeof(gtk_req->kek); + QDF_ASSERT(0); + } + qdf_mem_copy(gtk_req->kek, kek_ptr, kek_len); + } + + qdf_copy_macaddr(>k_req->bssid, &hdd_sta_ctx->conn_info.bssid); + + gtk_req->kek_len = kek_len; + gtk_req->is_fils_connection = hdd_is_fils_connection(adapter); + + /* convert big to little endian since driver work on little endian */ + buf = (uint8_t *)>k_req->replay_counter; + for (i = 0; i < 8; i++) + buf[7 - i] = replay_ctr[i]; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + goto end; + status = ucfg_pmo_cache_gtk_offload_req(vdev, gtk_req); + hdd_objmgr_put_vdev(vdev); + if (status != QDF_STATUS_SUCCESS) + hdd_err("Failed to cache GTK Offload"); + +end: + qdf_mem_free(gtk_req); +} +#endif + +#if defined(WLAN_FEATURE_FILS_SK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * wlan_hdd_add_fils_params_roam_auth_event() - Adds FILS params in roam auth + * @skb: SK buffer + * @roam_info: Roam info + * + * API adds fils params[pmk, pmkid, next sequence number] to roam auth event + * + * Return: zero on success, error code on failure + */ +static int +wlan_hdd_add_fils_params_roam_auth_event(struct sk_buff *skb, + struct csr_roam_info *roam_info) +{ + if (roam_info->pmk_len && + nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK, + roam_info->pmk_len, roam_info->pmk)) { + hdd_err("pmk send fail"); + return -EINVAL; + } + + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID, + PMKID_LEN, roam_info->pmkid)) { + hdd_err("pmkid send fail"); + return -EINVAL; + } + + hdd_debug("Update ERP Seq Num %d, Next ERP Seq Num %d", + roam_info->update_erp_next_seq_num, + roam_info->next_erp_seq_num); + if (roam_info->update_erp_next_seq_num && + nla_put_u16(skb, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM, + roam_info->next_erp_seq_num)) { + hdd_err("ERP seq num send fail"); + return -EINVAL; + } + + return 0; +} +#else +static inline int +wlan_hdd_add_fils_params_roam_auth_event(struct sk_buff *skb, + struct csr_roam_info *roam_info) +{ + return 0; +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +void hdd_send_roam_scan_ch_list_event(struct hdd_context *hdd_ctx, + uint8_t vdev_id, uint16_t buf_len, + uint8_t *buf) +{ + struct sk_buff *vendor_event; + struct hdd_adapter *adapter; + uint32_t len, ret; + + if (!hdd_ctx) { + hdd_err_rl("hdd context is null"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) + return; + + len = nla_total_size(buf_len) + NLMSG_HDRLEN; + vendor_event = + cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, &(adapter->wdev), len, + QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + ret = nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_CONNECT_CHANNELS, + buf_len, buf); + if (ret) { + hdd_err("OEM event put fails status %d", ret); + kfree_skb(vendor_event); + return; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); +} + +/** + * wlan_hdd_send_roam_auth_event() - Send the roamed and authorized event + * @adapter: Pointer to adapter struct + * @bssid: pointer to bssid of roamed AP. + * @req_rsn_ie: Pointer to request RSN IE + * @req_rsn_len: Length of the request RSN IE + * @rsp_rsn_ie: Pointer to response RSN IE + * @rsp_rsn_len: Length of the response RSN IE + * @roam_info_ptr: Pointer to the roaming related information + * + * This is called when wlan driver needs to send the roaming and + * authorization information after roaming. + * + * The information that would be sent is the request RSN IE, response + * RSN IE and BSSID of the newly roamed AP. + * + * If the Authorized status is authenticated, then additional parameters + * like PTK's KCK and KEK and Replay Counter would also be passed to the + * supplicant. + * + * The supplicant upon receiving this event would ignore the legacy + * cfg80211_roamed call and use the entire information from this event. + * The cfg80211_roamed should still co-exist since the kernel will + * make use of the parameters even if the supplicant ignores it. + * + * Return: Return the Success or Failure code. + */ +int wlan_hdd_send_roam_auth_event(struct hdd_adapter *adapter, uint8_t *bssid, + uint8_t *req_rsn_ie, uint32_t req_rsn_len, uint8_t *rsp_rsn_ie, + uint32_t rsp_rsn_len, struct csr_roam_info *roam_info_ptr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct sk_buff *skb = NULL; + enum csr_akm_type auth_type; + uint32_t fils_params_len; + int status; + enum qca_roam_reason hdd_roam_reason; + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (!roaming_offload_enabled(hdd_ctx) || + !roam_info_ptr->roamSynchInProgress) + return 0; + + /* + * PMK is sent from FW in Roam Synch Event for FILS Roaming. + * In that case, add three more NL attributes.ie. PMK, PMKID + * and ERP next sequence number. Add corresponding lengths + * with 3 extra NL message headers for each of the + * aforementioned params. + */ + fils_params_len = roam_info_ptr->pmk_len + PMKID_LEN + + sizeof(uint16_t) + (3 * NLMSG_HDRLEN); + + skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + &(adapter->wdev), + ETH_ALEN + req_rsn_len + rsp_rsn_len + + sizeof(uint8_t) + SIR_REPLAY_CTR_LEN + + SIR_KCK_KEY_LEN + roam_info_ptr->kek_len + + sizeof(uint16_t) + sizeof(uint8_t) + + (9 * NLMSG_HDRLEN) + fils_params_len, + QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH_INDEX, + GFP_KERNEL); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return -EINVAL; + } + + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, + ETH_ALEN, bssid) || + nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE, + req_rsn_len, req_rsn_ie) || + nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE, + rsp_rsn_len, rsp_rsn_ie)) { + hdd_err("nla put fail"); + goto nla_put_failure; + } + if (roam_info_ptr->synchAuthStatus == + CSR_ROAM_AUTH_STATUS_AUTHENTICATED) { + hdd_debug("Include Auth Params TLV's"); + if (nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, true)) { + hdd_err("nla put fail"); + goto nla_put_failure; + } + auth_type = roam_info_ptr->u.pConnectedProfile->AuthType; + /* if FT or CCKM connection: dont send replay counter */ + if (auth_type != eCSR_AUTH_TYPE_FT_RSN && + auth_type != eCSR_AUTH_TYPE_FT_RSN_PSK && + auth_type != eCSR_AUTH_TYPE_FT_SAE && + auth_type != eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384 && + auth_type != eCSR_AUTH_TYPE_CCKM_WPA && + auth_type != eCSR_AUTH_TYPE_CCKM_RSN && + nla_put(skb, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, + SIR_REPLAY_CTR_LEN, + roam_info_ptr->replay_ctr)) { + hdd_err("non FT/non CCKM connection"); + hdd_err("failed to send replay counter"); + goto nla_put_failure; + } + if (roam_info_ptr->kek_len > SIR_KEK_KEY_LEN_FILS || + nla_put(skb, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, + roam_info_ptr->kck_len, roam_info_ptr->kck) || + nla_put(skb, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, + roam_info_ptr->kek_len, roam_info_ptr->kek)) { + hdd_err("nla put fail, kek_len %d", + roam_info_ptr->kek_len); + goto nla_put_failure; + } + + hdd_roam_reason = + hdd_get_roam_reason(roam_info_ptr->roam_reason); + + if (nla_put_u16(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON, + hdd_roam_reason)) { + hdd_err("roam reason send failure"); + goto nla_put_failure; + } + + status = wlan_hdd_add_fils_params_roam_auth_event(skb, + roam_info_ptr); + if (status) + goto nla_put_failure; + + /* + * Save the gtk rekey parameters in HDD STA context. They will + * be used next time when host enables GTK offload and goes + * into power save state. + */ + wlan_hdd_save_gtk_offload_params(adapter, roam_info_ptr->kck, + roam_info_ptr->kck_len, + roam_info_ptr->kek, + roam_info_ptr->kek_len, + roam_info_ptr->replay_ctr, + true); + hdd_debug("roam_info_ptr->replay_ctr 0x%llx", + *((uint64_t *)roam_info_ptr->replay_ctr)); + + } else { + hdd_debug("No Auth Params TLV's"); + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, + false)) { + hdd_err("nla put fail"); + goto nla_put_failure; + } + } + + hdd_debug("Auth Status = %d Subnet Change Status = %d", + roam_info_ptr->synchAuthStatus, + roam_info_ptr->subnet_change_status); + + /* + * Add subnet change status if subnet has changed + * 0 = unchanged + * 1 = changed + * 2 = unknown + */ + if (roam_info_ptr->subnet_change_status) { + if (nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS, + roam_info_ptr->subnet_change_status)) { + hdd_err("nla put fail"); + goto nla_put_failure; + } + } + + cfg80211_vendor_event(skb, GFP_KERNEL); + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} +#endif + +#define ANT_DIV_SET_PERIOD(probe_period, stay_period) \ + ((1 << 26) | \ + (((probe_period) & 0x1fff) << 13) | \ + ((stay_period) & 0x1fff)) + +#define ANT_DIV_SET_SNR_DIFF(snr_diff) \ + ((1 << 27) | \ + ((snr_diff) & 0x1fff)) + +#define ANT_DIV_SET_PROBE_DWELL_TIME(probe_dwell_time) \ + ((1 << 28) | \ + ((probe_dwell_time) & 0x1fff)) + +#define ANT_DIV_SET_WEIGHT(mgmt_snr_weight, data_snr_weight, ack_snr_weight) \ + ((1 << 29) | \ + (((mgmt_snr_weight) & 0xff) << 16) | \ + (((data_snr_weight) & 0xff) << 8) | \ + ((ack_snr_weight) & 0xff)) + +#define RX_REORDER_TIMEOUT_VOICE \ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE +#define RX_REORDER_TIMEOUT_VIDEO \ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO +#define RX_REORDER_TIMEOUT_BESTEFFORT \ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT +#define RX_REORDER_TIMEOUT_BACKGROUND \ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND +#define RX_BLOCKSIZE_PEER_MAC \ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC +#define RX_BLOCKSIZE_WINLIMIT \ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT +static const struct nla_policy +wlan_hdd_wifi_config_policy[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1] = { + + [QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED] = { + .type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR] = {.type = NLA_U16 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY] = { + .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL] = {.type = NLA_U8}, + [RX_REORDER_TIMEOUT_VOICE] = {.type = NLA_U32}, + [RX_REORDER_TIMEOUT_VIDEO] = {.type = NLA_U32}, + [RX_REORDER_TIMEOUT_BESTEFFORT] = {.type = NLA_U32}, + [RX_REORDER_TIMEOUT_BACKGROUND] = {.type = NLA_U32}, + [RX_BLOCKSIZE_PEER_MAC] = { + .type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE}, + [RX_BLOCKSIZE_WINLIMIT] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_LRO] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL] = {.type = NLA_U16 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_GTX] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST] = { + .type = NLA_BINARY, + .len = WLAN_MAX_IE_LEN + 2}, + [QCA_WLAN_VENDOR_ATTR_DISCONNECT_IES] = { + .type = NLA_BINARY, + .len = SIR_MAC_MAX_ADD_IE_LENGTH + 2}, + [QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE] = { + .type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS] = {.type = NLA_U8 }, + +}; + +static const struct nla_policy qca_wlan_vendor_twt_add_dialog_policy[ + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST] = {.type = NLA_FLAG }, + [QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER] = {.type = NLA_FLAG }, + [QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION] = {.type = NLA_FLAG }, + [QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA] = { + .type = NLA_U32 }, +}; + +static const struct nla_policy +qca_wlan_vendor_attr_he_omi_tx_policy [QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE] = {.type = NLA_U8 }, +}; + +static const struct nla_policy +wlan_hdd_wifi_test_config_policy[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE] = { + .type = NLA_U16}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_BW] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_NSS] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG] = { + .type = NLA_FLAG}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX] = { + .type = NLA_NESTED}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS] + = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP] = { + .type = NLA_NESTED}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE] = { + .type = NLA_NESTED}, + [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT] = { + .type = NLA_U8}, +}; + +/** + * wlan_hdd_save_default_scan_ies() - API to store the default scan IEs + * @hdd_ctx: HDD context + * @adapter: Pointer to HDD adapter + * @ie_data: Pointer to Scan IEs buffer + * @ie_len: Length of Scan IEs + * + * This API is used to store the default scan ies received from + * supplicant. Also saves QCN IE if g_qcn_ie_support INI is enabled + * + * Return: 0 on success; error number otherwise + */ +static int wlan_hdd_save_default_scan_ies(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *ie_data, uint16_t ie_len) +{ + struct hdd_scan_info *scan_info = &adapter->scan_info; + bool add_qcn_ie; + + if (!scan_info) + return -EINVAL; + + if (scan_info->default_scan_ies) { + qdf_mem_free(scan_info->default_scan_ies); + scan_info->default_scan_ies = NULL; + } + + scan_info->default_scan_ies_len = ie_len; + ucfg_mlme_get_qcn_ie_support(hdd_ctx->psoc, &add_qcn_ie); + if (add_qcn_ie) + ie_len += (QCN_IE_HDR_LEN + QCN_IE_VERSION_SUBATTR_LEN); + + scan_info->default_scan_ies = qdf_mem_malloc(ie_len); + if (!scan_info->default_scan_ies) { + scan_info->default_scan_ies_len = 0; + return -ENOMEM; + } + + qdf_mem_copy(scan_info->default_scan_ies, ie_data, + scan_info->default_scan_ies_len); + + /* Add QCN IE if g_qcn_ie_support INI is enabled */ + if (add_qcn_ie) + sme_add_qcn_ie(hdd_ctx->mac_handle, + scan_info->default_scan_ies, + &scan_info->default_scan_ies_len); + + hdd_debug("Saved default scan IE:len %d", + scan_info->default_scan_ies_len); + qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + (uint8_t *) scan_info->default_scan_ies, + scan_info->default_scan_ies_len); + + return 0; +} + +/** + * wlan_hdd_handle_restrict_offchan_config() - + * Handle wifi configuration attribute : + * QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL + * @adapter: Pointer to HDD adapter + * @restrict_offchan: Restrict offchannel setting done by + * application + * + * Return: 0 on success; error number otherwise + */ +static int wlan_hdd_handle_restrict_offchan_config(struct hdd_adapter *adapter, + u8 restrict_offchan) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + enum QDF_OPMODE dev_mode = adapter->device_mode; + struct wlan_objmgr_vdev *vdev; + int ret_val = 0; + + if (!(dev_mode == QDF_SAP_MODE || dev_mode == QDF_P2P_GO_MODE)) { + hdd_err("Invalid interface type:%d", dev_mode); + return -EINVAL; + } + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + if (restrict_offchan == 1) { + enum policy_mgr_con_mode pmode = + policy_mgr_convert_device_mode_to_qdf_type(dev_mode); + int chan; + + u32 vdev_id = wlan_vdev_get_id(vdev); + + wlan_vdev_obj_lock(vdev); + wlan_vdev_mlme_cap_set(vdev, WLAN_VDEV_C_RESTRICT_OFFCHAN); + wlan_vdev_obj_unlock(vdev); + chan = wlan_freq_to_chan( + policy_mgr_get_channel(hdd_ctx->psoc, pmode, &vdev_id)); + if (!chan || + wlan_hdd_send_avoid_freq_for_dnbs(hdd_ctx, chan)) { + hdd_err("unable to send avoid_freq"); + ret_val = -EINVAL; + } + hdd_info("vdev %d mode %d dnbs enabled", vdev_id, dev_mode); + } else if (restrict_offchan == 0) { + wlan_vdev_obj_lock(vdev); + wlan_vdev_mlme_cap_clear(vdev, WLAN_VDEV_C_RESTRICT_OFFCHAN); + wlan_vdev_obj_unlock(vdev); + if (wlan_hdd_send_avoid_freq_for_dnbs(hdd_ctx, 0)) { + hdd_err("unable to clear avoid_freq"); + ret_val = -EINVAL; + } + hdd_info("vdev mode %d dnbs disabled", dev_mode); + } else { + ret_val = -EINVAL; + hdd_err("Invalid RESTRICT_OFFCHAN setting"); + } + hdd_objmgr_put_vdev(vdev); + return ret_val; +} + +/** + * wlan_hdd_cfg80211_wifi_set_reorder_timeout() - set reorder timeout + * @adapter: Pointer to HDD adapter + * @tb: array of pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +static +int wlan_hdd_cfg80211_wifi_set_reorder_timeout(struct hdd_adapter *adapter, + struct nlattr *tb[]) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret_val = 0; + QDF_STATUS qdf_status; + struct sir_set_rx_reorder_timeout_val reorder_timeout; + mac_handle_t mac_handle; + +#define RX_TIMEOUT_VAL_MIN 10 +#define RX_TIMEOUT_VAL_MAX 1000 + + if (tb[RX_REORDER_TIMEOUT_VOICE] || + tb[RX_REORDER_TIMEOUT_VIDEO] || + tb[RX_REORDER_TIMEOUT_BESTEFFORT] || + tb[RX_REORDER_TIMEOUT_BACKGROUND]) { + + /* if one is specified, all must be specified */ + if (!tb[RX_REORDER_TIMEOUT_VOICE] || + !tb[RX_REORDER_TIMEOUT_VIDEO] || + !tb[RX_REORDER_TIMEOUT_BESTEFFORT] || + !tb[RX_REORDER_TIMEOUT_BACKGROUND]) { + hdd_err("four AC timeout val are required MAC"); + return -EINVAL; + } + + reorder_timeout.rx_timeout_pri[0] = nla_get_u32( + tb[RX_REORDER_TIMEOUT_VOICE]); + reorder_timeout.rx_timeout_pri[1] = nla_get_u32( + tb[RX_REORDER_TIMEOUT_VIDEO]); + reorder_timeout.rx_timeout_pri[2] = nla_get_u32( + tb[RX_REORDER_TIMEOUT_BESTEFFORT]); + reorder_timeout.rx_timeout_pri[3] = nla_get_u32( + tb[RX_REORDER_TIMEOUT_BACKGROUND]); + /* timeout value is required to be in the rang 10 to 1000ms */ + if (reorder_timeout.rx_timeout_pri[0] >= RX_TIMEOUT_VAL_MIN && + reorder_timeout.rx_timeout_pri[0] <= RX_TIMEOUT_VAL_MAX && + reorder_timeout.rx_timeout_pri[1] >= RX_TIMEOUT_VAL_MIN && + reorder_timeout.rx_timeout_pri[1] <= RX_TIMEOUT_VAL_MAX && + reorder_timeout.rx_timeout_pri[2] >= RX_TIMEOUT_VAL_MIN && + reorder_timeout.rx_timeout_pri[2] <= RX_TIMEOUT_VAL_MAX && + reorder_timeout.rx_timeout_pri[3] >= RX_TIMEOUT_VAL_MIN && + reorder_timeout.rx_timeout_pri[3] <= RX_TIMEOUT_VAL_MAX) { + mac_handle = hdd_ctx->mac_handle; + qdf_status = sme_set_reorder_timeout(mac_handle, + &reorder_timeout); + if (qdf_status != QDF_STATUS_SUCCESS) { + hdd_err("failed to set reorder timeout err %d", + qdf_status); + ret_val = -EPERM; + } + } else { + hdd_err("one of the timeout value is not in range"); + ret_val = -EINVAL; + } + } + + return ret_val; +} + +/** + * wlan_hdd_cfg80211_wifi_set_rx_blocksize() - set rx blocksize + * @adapter: hdd adapter + * @tb: array of pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +static int wlan_hdd_cfg80211_wifi_set_rx_blocksize(struct hdd_adapter *adapter, + struct nlattr *tb[]) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret_val = 0; + uint32_t set_value; + QDF_STATUS qdf_status; + struct sir_peer_set_rx_blocksize rx_blocksize; + mac_handle_t mac_handle; + +#define WINDOW_SIZE_VAL_MIN 1 +#define WINDOW_SIZE_VAL_MAX 64 + + if (tb[RX_BLOCKSIZE_PEER_MAC] || + tb[RX_BLOCKSIZE_WINLIMIT]) { + + /* if one is specified, both must be specified */ + if (!tb[RX_BLOCKSIZE_PEER_MAC] || + !tb[RX_BLOCKSIZE_WINLIMIT]) { + hdd_err("Both Peer MAC and windows limit required"); + return -EINVAL; + } + + memcpy(&rx_blocksize.peer_macaddr, + nla_data(tb[RX_BLOCKSIZE_PEER_MAC]), + sizeof(rx_blocksize.peer_macaddr)), + + rx_blocksize.vdev_id = adapter->vdev_id; + set_value = nla_get_u32(tb[RX_BLOCKSIZE_WINLIMIT]); + /* maximum window size is 64 */ + if (set_value >= WINDOW_SIZE_VAL_MIN && + set_value <= WINDOW_SIZE_VAL_MAX) { + rx_blocksize.rx_block_ack_win_limit = set_value; + mac_handle = hdd_ctx->mac_handle; + qdf_status = sme_set_rx_set_blocksize(mac_handle, + &rx_blocksize); + if (qdf_status != QDF_STATUS_SUCCESS) { + hdd_err("failed to set aggr sizes err %d", + qdf_status); + ret_val = -EPERM; + } + } else { + hdd_err("window size val is not in range"); + ret_val = -EINVAL; + } + } + + return ret_val; +} + +/** + * hdd_set_roam_reason_vsie_status() - enable/disable inclusion of + * roam reason vsie in Reassoc + * + * @adapter: hdd adapter + * @attr: nla attr sent by supplicant + * + * Return: 0 on success, negative errno on failure + */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static int hdd_set_roam_reason_vsie_status(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint8_t roam_reason_vsie_enabled; + int errno; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct hdd_context *hdd_ctx = NULL; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("hdd_ctx failure"); + return -EINVAL; + } + + roam_reason_vsie_enabled = nla_get_u8(attr); + if (roam_reason_vsie_enabled > 1) + roam_reason_vsie_enabled = 1; + + status = + ucfg_mlme_set_roam_reason_vsie_status(hdd_ctx->psoc, + roam_reason_vsie_enabled); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("set roam reason vsie failed"); + return -EINVAL; + } + + errno = sme_cli_set_command + (adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_DISABLE_ROAM_REASON_VSIE, + roam_reason_vsie_enabled, VDEV_CMD); + if (errno) { + hdd_err("Failed to set beacon report error vsie"); + status = QDF_STATUS_E_FAILURE; + } + + return qdf_status_to_os_return(status); +} +#else +static int hdd_set_roam_reason_vsie_status(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + return -ENOTSUPP; +} +#endif + +static int hdd_config_access_policy(struct hdd_adapter *adapter, + struct nlattr *tb[]) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct nlattr *policy_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY]; + struct nlattr *ielist_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST]; + uint32_t access_policy; + uint8_t ie[WLAN_MAX_IE_LEN + 2]; + QDF_STATUS status; + + /* nothing to do if neither attribute is present */ + if (!ielist_attr && !policy_attr) + return 0; + + /* if one is present, both must be present */ + if (!ielist_attr || !policy_attr) { + hdd_err("Missing attribute for %s", + policy_attr ? + "ACCESS_POLICY_IE_LIST" : "ACCESS_POLICY"); + return -EINVAL; + } + + /* validate the access policy */ + access_policy = nla_get_u32(policy_attr); + switch (access_policy) { + case QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED: + case QCA_ACCESS_POLICY_DENY_UNLESS_LISTED: + /* valid */ + break; + default: + hdd_err("Invalid value. access_policy %u", access_policy); + return -EINVAL; + } + + /* + * ie length is validated by the nla_policy. need to make a + * copy since SME will always read WLAN_MAX_IE_LEN+2 bytes + */ + nla_memcpy(ie, ielist_attr, sizeof(ie)); + + hdd_debug("calling sme_update_access_policy_vendor_ie"); + status = sme_update_access_policy_vendor_ie(hdd_ctx->mac_handle, + adapter->vdev_id, + ie, access_policy); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set vendor ie and access policy, %d", + status); + + return qdf_status_to_os_return(status); +} + +static int hdd_config_mpdu_aggregation(struct hdd_adapter *adapter, + struct nlattr *tb[]) +{ + struct nlattr *tx_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION]; + struct nlattr *rx_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION]; + uint8_t tx_size, rx_size; + QDF_STATUS status; + + /* nothing to do if neither attribute is present */ + if (!tx_attr && !rx_attr) + return 0; + + /* if one is present, both must be present */ + if (!tx_attr || !rx_attr) { + hdd_err("Missing attribute for %s", + tx_attr ? "RX" : "TX"); + return -EINVAL; + } + + tx_size = nla_get_u8(tx_attr); + rx_size = nla_get_u8(rx_attr); + if (!cfg_in_range(CFG_TX_AGGREGATION_SIZE, tx_size) || + !cfg_in_range(CFG_RX_AGGREGATION_SIZE, rx_size)) { + hdd_err("TX %d RX %d MPDU aggr size not in range", + tx_size, rx_size); + + return -EINVAL; + } + + status = wma_set_tx_rx_aggr_size(adapter->vdev_id, + tx_size, + rx_size, + WMI_VDEV_CUSTOM_AGGR_TYPE_AMPDU); + + return qdf_status_to_os_return(status); +} + +static QDF_STATUS +hdd_populate_vdev_chains(struct wlan_mlme_nss_chains *nss_chains_cfg, + uint8_t tx_chains, + uint8_t rx_chains, + enum nss_chains_band_info band, + struct wlan_objmgr_vdev *vdev) +{ + struct wlan_mlme_nss_chains *dynamic_cfg; + + nss_chains_cfg->num_rx_chains[band] = rx_chains; + nss_chains_cfg->num_tx_chains[band] = tx_chains; + + dynamic_cfg = ucfg_mlme_get_dynamic_vdev_config(vdev); + if (!dynamic_cfg) { + hdd_err("nss chain dynamic config NULL"); + return QDF_STATUS_E_FAILURE; + } + /* + * If user gives any nss value, then chains will be adjusted based on + * nss (in SME func sme_validate_user_nss_chain_params). + * If Chains are not suitable as per current NSS then, we need to + * return, and the below logic is added for the same. + */ + + if ((dynamic_cfg->rx_nss[band] > rx_chains) || + (dynamic_cfg->tx_nss[band] > tx_chains)) { + hdd_err("Chains less than nss, configure correct nss first."); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +int +hdd_set_dynamic_antenna_mode(struct hdd_adapter *adapter, + uint8_t num_rx_chains, + uint8_t num_tx_chains) +{ + enum nss_chains_band_info band; + struct wlan_mlme_nss_chains user_cfg; + QDF_STATUS status; + mac_handle_t mac_handle; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct wlan_objmgr_vdev *vdev; + int ret; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) { + hdd_err("NULL MAC handle"); + return -EINVAL; + } + + if (!hdd_is_vdev_in_conn_state(adapter)) { + hdd_debug("Vdev (id %d) not in connected/started state, cannot accept command", + adapter->vdev_id); + return -EINVAL; + } + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) { + hdd_err("vdev is NULL"); + return -EINVAL; + } + + qdf_mem_zero(&user_cfg, sizeof(user_cfg)); + for (band = NSS_CHAINS_BAND_2GHZ; band < NSS_CHAINS_BAND_MAX; band++) { + status = hdd_populate_vdev_chains(&user_cfg, + num_rx_chains, + num_tx_chains, band, vdev); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_objmgr_put_vdev(vdev); + return -EINVAL; + } + } + hdd_objmgr_put_vdev(vdev); + + status = sme_nss_chains_update(mac_handle, + &user_cfg, + adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + return 0; +} + +static int hdd_config_vdev_chains(struct hdd_adapter *adapter, + struct nlattr *tb[]) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t tx_chains, rx_chains; + struct nlattr *tx_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS]; + struct nlattr *rx_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS]; + + if (!tx_attr && !rx_attr) + return 0; + + tx_chains = nla_get_u8(tx_attr); + rx_chains = nla_get_u8(rx_attr); + + if (hdd_ctx->dynamic_nss_chains_support) + return hdd_set_dynamic_antenna_mode(adapter, rx_chains, + tx_chains); + return 0; +} + +static int hdd_config_tx_rx_nss(struct hdd_adapter *adapter, + struct nlattr *tb[]) +{ + uint8_t tx_nss, rx_nss; + QDF_STATUS status; + + struct nlattr *tx_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS]; + struct nlattr *rx_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS]; + + if (!tx_attr && !rx_attr) + return 0; + + tx_nss = nla_get_u8(tx_attr); + rx_nss = nla_get_u8(rx_attr); + hdd_debug("tx_nss %d rx_nss %d", tx_nss, rx_nss); + /* Only allow NSS for tx_rx_nss for 1x1, 1x2, 2x2 */ + if (!((tx_nss == 1 && rx_nss == 2) || (tx_nss == 1 && rx_nss == 1) || + (tx_nss == 2 && rx_nss == 2))) { + hdd_err("Setting tx_nss %d rx_nss %d not allowed", tx_nss, + rx_nss); + return 0; + } + status = hdd_update_nss(adapter, tx_nss, rx_nss); + if (status != QDF_STATUS_SUCCESS) + hdd_debug("Can't set tx_nss %d rx_nss %d", tx_nss, rx_nss); + + return 0; +} + +static int hdd_config_ant_div_period(struct hdd_adapter *adapter, + struct nlattr *tb[]) +{ + struct nlattr *probe_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD]; + struct nlattr *stay_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD]; + uint32_t probe_period, stay_period, ant_div_usrcfg; + int errno; + + /* nothing to do if neither attribute is present */ + if (!probe_attr && !stay_attr) + return 0; + + /* if one is present, both must be present */ + if (!probe_attr || !stay_attr) { + hdd_err("Missing attribute for %s", + probe_attr ? "STAY" : "PROBE"); + return -EINVAL; + } + + probe_period = nla_get_u32(probe_attr); + stay_period = nla_get_u32(stay_attr); + ant_div_usrcfg = ANT_DIV_SET_PERIOD(probe_period, stay_period); + hdd_debug("ant div set period: %x", ant_div_usrcfg); + errno = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANT_DIV_USRCFG, + ant_div_usrcfg, PDEV_CMD); + if (errno) + hdd_err("Failed to set ant div period, %d", errno); + + return errno; +} + +static int hdd_config_ant_div_snr_weight(struct hdd_adapter *adapter, + struct nlattr *tb[]) +{ + struct nlattr *mgmt_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT]; + struct nlattr *data_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT]; + struct nlattr *ack_attr = + tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT]; + uint32_t mgmt_snr, data_snr, ack_snr, ant_div_usrcfg; + int errno; + + /* nothing to do if none of the attributes are present */ + if (!mgmt_attr && !data_attr && !ack_attr) + return 0; + + /* if one is present, all must be present */ + if (!mgmt_attr || !data_attr || !ack_attr) { + hdd_err("Missing attribute"); + return -EINVAL; + } + + mgmt_snr = nla_get_u32(mgmt_attr); + data_snr = nla_get_u32(data_attr); + ack_snr = nla_get_u32(ack_attr); + ant_div_usrcfg = ANT_DIV_SET_WEIGHT(mgmt_snr, data_snr, ack_snr); + hdd_debug("ant div set weight: %x", ant_div_usrcfg); + errno = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANT_DIV_USRCFG, + ant_div_usrcfg, PDEV_CMD); + if (errno) + hdd_err("Failed to set ant div weight, %d", errno); + + return errno; +} + +static int hdd_config_fine_time_measurement(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t user_capability; + uint32_t target_capability; + uint32_t final_capability; + QDF_STATUS status; + + user_capability = nla_get_u32(attr); + target_capability = hdd_ctx->fine_time_meas_cap_target; + final_capability = user_capability & target_capability; + + status = ucfg_mlme_set_fine_time_meas_cap(hdd_ctx->psoc, + final_capability); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to set value, status %d", status); + return -EINVAL; + } + + sme_update_fine_time_measurement_capab(hdd_ctx->mac_handle, + adapter->vdev_id, + final_capability); + ucfg_wifi_pos_set_ftm_cap(hdd_ctx->psoc, final_capability); + + hdd_debug("user: 0x%x, target: 0x%x, final: 0x%x", + user_capability, target_capability, final_capability); + + return 0; +} + +static int hdd_config_modulated_dtim(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct wlan_objmgr_vdev *vdev; + uint32_t modulated_dtim; + QDF_STATUS status; + + modulated_dtim = nla_get_u32(attr); + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + status = ucfg_pmo_config_modulated_dtim(vdev, modulated_dtim); + + hdd_objmgr_put_vdev(vdev); + + return qdf_status_to_os_return(status); +} + +static int hdd_config_listen_interval(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct wlan_objmgr_vdev *vdev; + uint32_t listen_interval; + QDF_STATUS status; + + listen_interval = nla_get_u32(attr); + if (listen_interval > cfg_max(CFG_PMO_ENABLE_DYNAMIC_DTIM)) { + hdd_err_rl("Invalid value for listen interval - %d", + listen_interval); + return -EINVAL; + } + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + status = ucfg_pmo_config_listen_interval(vdev, listen_interval); + + hdd_objmgr_put_vdev(vdev); + + return qdf_status_to_os_return(status); +} + +static int hdd_config_lro(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t enable_flag; + + enable_flag = nla_get_u8(attr); + + return hdd_lro_set_reset(hdd_ctx, adapter, enable_flag); +} + +static int hdd_config_scan_enable(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t enable_flag; + + enable_flag = nla_get_u8(attr); + if (enable_flag) + ucfg_scan_psoc_set_enable(hdd_ctx->psoc, + REASON_USER_SPACE); + else + ucfg_scan_psoc_set_disable(hdd_ctx->psoc, + REASON_USER_SPACE); + + return 0; +} + +/** + * hdd_config_udp_qos_upgrade_threshold() - NL attribute handler to parse + * priority upgrade threshold value. + * @adapter: adapter for which this configuration is to be applied + * @attr: NL attribute + * + * Returns: 0 on success, -EINVAL on failure + */ +static int hdd_config_udp_qos_upgrade_threshold(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint8_t priority = nla_get_u8(attr); + + return hdd_set_udp_qos_upgrade_config(adapter, priority); +} + +static int hdd_config_power(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t power; + + power = nla_get_u8(attr); + + return hdd_set_power_config(hdd_ctx, adapter, power); +} + +static int hdd_config_stats_avg_factor(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint16_t stats_avg_factor; + QDF_STATUS status; + + stats_avg_factor = nla_get_u16(attr); + status = sme_configure_stats_avg_factor(hdd_ctx->mac_handle, + adapter->vdev_id, + stats_avg_factor); + + return qdf_status_to_os_return(status); +} + +static int hdd_config_non_agg_retry(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint8_t retry; + int param_id; + + retry = nla_get_u8(attr); + retry = retry > CFG_NON_AGG_RETRY_MAX ? + CFG_NON_AGG_RETRY_MAX : retry; + param_id = WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH; + + return wma_cli_set_command(adapter->vdev_id, param_id, + retry, PDEV_CMD); +} + +static int hdd_config_agg_retry(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint8_t retry; + int param_id; + + retry = nla_get_u8(attr); + retry = retry > CFG_AGG_RETRY_MAX ? + CFG_AGG_RETRY_MAX : retry; + + /* Value less than CFG_AGG_RETRY_MIN has side effect to t-put */ + retry = ((retry > 0) && (retry < CFG_AGG_RETRY_MIN)) ? + CFG_AGG_RETRY_MIN : retry; + param_id = WMI_PDEV_PARAM_AGG_SW_RETRY_TH; + + return wma_cli_set_command(adapter->vdev_id, param_id, + retry, PDEV_CMD); +} + +static int hdd_config_mgmt_retry(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint8_t retry; + int param_id; + uint8_t max_mgmt_retry; + + retry = nla_get_u8(attr); + max_mgmt_retry = (cfg_max(CFG_MGMT_RETRY_MAX)); + retry = retry > max_mgmt_retry ? + max_mgmt_retry : retry; + param_id = WMI_PDEV_PARAM_MGMT_RETRY_LIMIT; + + return wma_cli_set_command(adapter->vdev_id, param_id, + retry, PDEV_CMD); +} + +static int hdd_config_ctrl_retry(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint8_t retry; + int param_id; + + retry = nla_get_u8(attr); + retry = retry > CFG_CTRL_RETRY_MAX ? + CFG_CTRL_RETRY_MAX : retry; + param_id = WMI_PDEV_PARAM_CTRL_RETRY_LIMIT; + + return wma_cli_set_command(adapter->vdev_id, param_id, + retry, PDEV_CMD); +} + +static int hdd_config_propagation_delay(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint8_t delay; + uint32_t abs_delay; + int param_id; + + delay = nla_get_u8(attr); + delay = delay > CFG_PROPAGATION_DELAY_MAX ? + CFG_PROPAGATION_DELAY_MAX : delay; + abs_delay = delay + CFG_PROPAGATION_DELAY_BASE; + param_id = WMI_PDEV_PARAM_PROPAGATION_DELAY; + + return wma_cli_set_command(adapter->vdev_id, param_id, + abs_delay, PDEV_CMD); +} + +static int hdd_config_propagation_abs_delay(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint32_t abs_delay; + int param_id; + + abs_delay = nla_get_u32(attr); + param_id = WMI_PDEV_PARAM_PROPAGATION_DELAY; + + return wma_cli_set_command(adapter->vdev_id, param_id, + abs_delay, PDEV_CMD); +} + +static int hdd_config_tx_fail_count(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t tx_fail_count; + QDF_STATUS status; + + tx_fail_count = nla_get_u32(attr); + if (!tx_fail_count) + return 0; + + status = sme_update_tx_fail_cnt_threshold(hdd_ctx->mac_handle, + adapter->vdev_id, + tx_fail_count); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("sme_update_tx_fail_cnt_threshold (err=%d)", + status); + + return qdf_status_to_os_return(status); +} + +static int hdd_config_channel_avoidance_ind(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t set_value; + + set_value = nla_get_u8(attr); + hdd_debug("set_value: %d", set_value); + + return hdd_enable_disable_ca_event(hdd_ctx, set_value); +} + +static int hdd_config_guard_time(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t guard_time; + QDF_STATUS status; + + guard_time = nla_get_u32(attr); + status = sme_configure_guard_time(hdd_ctx->mac_handle, + adapter->vdev_id, + guard_time); + + return qdf_status_to_os_return(status); +} + +static int hdd_config_scan_default_ies(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t *scan_ie; + uint16_t scan_ie_len; + QDF_STATUS status; + mac_handle_t mac_handle; + + scan_ie_len = nla_len(attr); + hdd_debug("IE len %d session %d device mode %d", + scan_ie_len, adapter->vdev_id, adapter->device_mode); + + if (!scan_ie_len) { + hdd_err("zero-length IE prohibited"); + return -EINVAL; + } + + if (scan_ie_len > MAX_DEFAULT_SCAN_IE_LEN) { + hdd_err("IE length %d exceeds max of %d", + scan_ie_len, MAX_DEFAULT_SCAN_IE_LEN); + return -EINVAL; + } + + scan_ie = nla_data(attr); + if (!wlan_is_ie_valid(scan_ie, scan_ie_len)) { + hdd_err("Invalid default scan IEs"); + return -EINVAL; + } + + if (wlan_hdd_save_default_scan_ies(hdd_ctx, adapter, + scan_ie, scan_ie_len)) + hdd_err("Failed to save default scan IEs"); + + if (adapter->device_mode == QDF_STA_MODE) { + mac_handle = hdd_ctx->mac_handle; + status = sme_set_default_scan_ie(mac_handle, + adapter->vdev_id, scan_ie, + scan_ie_len); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("failed to set default scan IEs in sme: %d", + status); + return -EPERM; + } + } + + return 0; +} + +static int hdd_config_ant_div_ena(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint32_t antdiv_enable; + int errno; + + antdiv_enable = nla_get_u32(attr); + hdd_debug("antdiv_enable: %d", antdiv_enable); + errno = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_ENA_ANT_DIV, + antdiv_enable, PDEV_CMD); + if (errno) + hdd_err("Failed to set antdiv_enable, %d", errno); + + return errno; +} + +static int hdd_config_ant_div_snr_diff(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint32_t ant_div_snr_diff; + uint32_t ant_div_usrcfg; + int errno; + + ant_div_snr_diff = nla_get_u32(attr); + hdd_debug("snr diff: %x", ant_div_snr_diff); + + ant_div_usrcfg = ANT_DIV_SET_SNR_DIFF(ant_div_snr_diff); + hdd_debug("usrcfg: %x", ant_div_usrcfg); + + errno = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANT_DIV_USRCFG, + ant_div_usrcfg, PDEV_CMD); + if (errno) + hdd_err("Failed to set snr diff, %d", errno); + + return errno; +} + +static int hdd_config_ant_div_probe_dwell_time(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint32_t dwell_time; + uint32_t ant_div_usrcfg; + int errno; + + dwell_time = nla_get_u32(attr); + hdd_debug("dwell time: %x", dwell_time); + + ant_div_usrcfg = ANT_DIV_SET_PROBE_DWELL_TIME(dwell_time); + hdd_debug("usrcfg: %x", ant_div_usrcfg); + + errno = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANT_DIV_USRCFG, + ant_div_usrcfg, PDEV_CMD); + if (errno) + hdd_err("Failed to set probe dwell time, %d", errno); + + return errno; +} + +static int hdd_config_ant_div_chain(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint32_t antdiv_chain; + int errno; + + antdiv_chain = nla_get_u32(attr); + hdd_debug("antdiv_chain: %d", antdiv_chain); + + errno = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_FORCE_CHAIN_ANT, + antdiv_chain, PDEV_CMD); + if (errno) + hdd_err("Failed to set chain, %d", errno); + + return errno; +} + +static int hdd_config_ant_div_selftest(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint32_t antdiv_selftest; + int errno; + + antdiv_selftest = nla_get_u32(attr); + hdd_debug("antdiv_selftest: %d", antdiv_selftest); + + errno = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANT_DIV_SELFTEST, + antdiv_selftest, PDEV_CMD); + if (errno) + hdd_err("Failed to set selftest, %d", errno); + + return errno; +} + +static int hdd_config_ant_div_selftest_intvl(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint32_t antdiv_selftest_intvl; + int errno; + + antdiv_selftest_intvl = nla_get_u32(attr); + hdd_debug("antdiv_selftest_intvl: %d", antdiv_selftest_intvl); + + errno = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL, + antdiv_selftest_intvl, PDEV_CMD); + if (errno) + hdd_err("Failed to set selftest interval, %d", errno); + + return errno; +} + +static int hdd_config_ignore_assoc_disallowed(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t ignore_assoc_disallowed; + + ignore_assoc_disallowed = nla_get_u8(attr); + hdd_debug("%u", ignore_assoc_disallowed); + if ((ignore_assoc_disallowed < QCA_IGNORE_ASSOC_DISALLOWED_DISABLE) || + (ignore_assoc_disallowed > QCA_IGNORE_ASSOC_DISALLOWED_ENABLE)) + return -EINVAL; + + sme_update_session_param(hdd_ctx->mac_handle, + adapter->vdev_id, + SIR_PARAM_IGNORE_ASSOC_DISALLOWED, + ignore_assoc_disallowed); + + return 0; +} + +static int hdd_config_restrict_offchannel(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint8_t restrict_offchan; + + restrict_offchan = nla_get_u8(attr); + hdd_debug("%u", restrict_offchan); + + if (restrict_offchan > 1) { + hdd_err("Invalid value %u", restrict_offchan); + return -EINVAL; + } + + return wlan_hdd_handle_restrict_offchan_config(adapter, + restrict_offchan); +} + +static int hdd_config_total_beacon_miss_count(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t first_miss_count; + uint8_t final_miss_count; + uint8_t total_miss_count; + QDF_STATUS status; + + total_miss_count = nla_get_u8(attr); + ucfg_mlme_get_roam_bmiss_first_bcnt(hdd_ctx->psoc, + &first_miss_count); + if (total_miss_count <= first_miss_count) { + hdd_err("Total %u needs to exceed first %u", + total_miss_count, first_miss_count); + return -EINVAL; + } + + final_miss_count = total_miss_count - first_miss_count; + + hdd_debug("First count %u, final count %u", + first_miss_count, final_miss_count); + + /***** + * TODO: research why is 0 being passed for vdev id??? + */ + status = sme_set_roam_bmiss_final_bcnt(hdd_ctx->mac_handle, + 0, + final_miss_count); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set final count, status %u", status); + return qdf_status_to_os_return(status); + } + + status = sme_set_bmiss_bcnt(adapter->vdev_id, + first_miss_count, + final_miss_count); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set count, status %u", status); + + return qdf_status_to_os_return(status); +} + +static int hdd_config_latency_level(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint16_t latency_level; + QDF_STATUS status; + + if (!hdd_is_wlm_latency_manager_supported(hdd_ctx)) + return -ENOTSUPP; + + latency_level = nla_get_u16(attr); + switch (latency_level) { + case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL: + case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE: + case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW: + case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW: + /* valid values */ + break; + default: + hdd_err("Invalid value %u", latency_level); + return -EINVAL; + } + + /* Map the latency value to the level which fw expected + * 0 - normal, 1 - moderate, 2 - low, 3 - ultralow + */ + adapter->latency_level = latency_level - 1; + status = sme_set_wlm_latency_level(hdd_ctx->mac_handle, + adapter->vdev_id, + adapter->latency_level); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("set latency level failed, %u", status); + + return qdf_status_to_os_return(status); +} + +static int hdd_config_disable_fils(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t disable_fils; + bool enabled; + QDF_STATUS status; + + /* ignore unless in STA mode */ + if (adapter->device_mode != QDF_STA_MODE) + return 0; + + disable_fils = nla_get_u8(attr); + hdd_debug("%u", disable_fils); + + enabled = !disable_fils; + status = ucfg_mlme_set_fils_enabled_info(hdd_ctx->psoc, enabled); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not set fils enabled info, %d", status); + + status = ucfg_mlme_set_enable_bcast_probe_rsp(hdd_ctx->psoc, enabled); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not set enable bcast probe resp info, %d", + status); + + status = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE, + !disable_fils, VDEV_CMD); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("failed to set enable bcast probe resp, %d", + status); + + return qdf_status_to_os_return(status); +} + +static int hdd_config_rsn_ie(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t force_rsne_override; + + force_rsne_override = nla_get_u8(attr); + if (force_rsne_override > 1) { + hdd_err("Invalid value %d", force_rsne_override); + return -EINVAL; + } + + hdd_ctx->force_rsne_override = force_rsne_override; + hdd_debug("force_rsne_override - %d", force_rsne_override); + + return 0; +} + +static int hdd_config_gtx(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + uint8_t config_gtx; + int errno; + + config_gtx = nla_get_u8(attr); + if (config_gtx > 1) { + hdd_err_rl("Invalid config_gtx value %d", config_gtx); + return -EINVAL; + } + + errno = sme_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_ENABLE, + config_gtx, VDEV_CMD); + if (errno) + hdd_err("Failed to set GTX, %d", errno); + + return errno; +} + +/** + * hdd_config_disconnect_ies() - Configure disconnect IEs + * @adapter: Pointer to HDD adapter + * @attr: array of pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +static int hdd_config_disconnect_ies(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + + hdd_debug("IE len %u session %u device mode %u", + nla_len(attr), adapter->vdev_id, adapter->device_mode); + if (!nla_len(attr) || + nla_len(attr) > SIR_MAC_MAX_ADD_IE_LENGTH + 2 || + !wlan_is_ie_valid(nla_data(attr), nla_len(attr))) { + hdd_err("Invalid disconnect IEs"); + return -EINVAL; + } + + status = sme_set_disconnect_ies(hdd_ctx->mac_handle, + adapter->vdev_id, + nla_data(attr), + nla_len(attr)); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set disconnect_ies"); + + return qdf_status_to_os_return(status); +} + +#ifdef WLAN_FEATURE_ELNA +/** + * hdd_set_elna_bypass() - Set eLNA bypass + * @adapter: Pointer to HDD adapter + * @attr: Pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +static int hdd_set_elna_bypass(struct hdd_adapter *adapter, + const struct nlattr *attr) +{ + int ret; + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + ret = os_if_fwol_set_elna_bypass(vdev, attr); + + hdd_objmgr_put_vdev(vdev); + + return ret; +} +#endif + +/** + * typedef independent_setter_fn - independent attribute handler + * @adapter: The adapter being configured + * @attr: The nl80211 attribute being applied + * + * Defines the signature of functions in the independent attribute vtable + * + * Return: 0 if the attribute was handled successfully, otherwise an errno + */ +typedef int (*independent_setter_fn)(struct hdd_adapter *adapter, + const struct nlattr *attr); + +/** + * struct independent_setters + * @id: vendor attribute which this entry handles + * @cb: callback function to invoke to process the attribute when present + */ +struct independent_setters { + uint32_t id; + independent_setter_fn cb; +}; + +/* vtable for independent setters */ +static const struct independent_setters independent_setters[] = { + {QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES, + hdd_config_scan_default_ies}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT, + hdd_config_fine_time_measurement}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM, + hdd_config_modulated_dtim}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL, + hdd_config_listen_interval}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_LRO, + hdd_config_lro}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE, + hdd_config_scan_enable}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER, + hdd_config_power}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR, + hdd_config_stats_avg_factor}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME, + hdd_config_guard_time}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY, + hdd_config_non_agg_retry}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY, + hdd_config_agg_retry}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY, + hdd_config_mgmt_retry}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY, + hdd_config_ctrl_retry}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY, + hdd_config_propagation_delay}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY, + hdd_config_propagation_abs_delay}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT, + hdd_config_tx_fail_count}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND, + hdd_config_channel_avoidance_ind}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA, + hdd_config_ant_div_ena}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF, + hdd_config_ant_div_snr_diff}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME, + hdd_config_ant_div_probe_dwell_time}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN, + hdd_config_ant_div_chain}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST, + hdd_config_ant_div_selftest}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL, + hdd_config_ant_div_selftest_intvl}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED, + hdd_config_ignore_assoc_disallowed}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL, + hdd_config_restrict_offchannel}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT, + hdd_config_total_beacon_miss_count}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL, + hdd_config_latency_level}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS, + hdd_config_disable_fils}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE, + hdd_config_rsn_ie}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_GTX, + hdd_config_gtx}, + {QCA_WLAN_VENDOR_ATTR_DISCONNECT_IES, + hdd_config_disconnect_ies}, +#ifdef WLAN_FEATURE_ELNA + {QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS, + hdd_set_elna_bypass}, +#endif + {QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON, + hdd_set_roam_reason_vsie_status}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT, + hdd_config_power}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE, + hdd_config_udp_qos_upgrade_threshold}, +}; + +#ifdef WLAN_FEATURE_ELNA +/** + * hdd_get_elna_bypass() - Get eLNA bypass + * @adapter: Pointer to HDD adapter + * @skb: sk buffer to hold nl80211 attributes + * @attr: Pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +static int hdd_get_elna_bypass(struct hdd_adapter *adapter, + struct sk_buff *skb, + const struct nlattr *attr) +{ + int ret; + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + ret = os_if_fwol_get_elna_bypass(vdev, skb, attr); + + hdd_objmgr_put_vdev(vdev); + + return ret; +} +#endif + +/** + * hdd_get_roam_reason_vsie_status() - Get roam_reason_vsie + * @adapter: Pointer to HDD adapter + * @skb: sk buffer to hold nl80211 attributes + * @attr: Pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static int hdd_get_roam_reason_vsie_status(struct hdd_adapter *adapter, + struct sk_buff *skb, + const struct nlattr *attr) +{ + uint8_t roam_reason_vsie_enabled; + struct hdd_context *hdd_ctx = NULL; + QDF_STATUS status; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + status = ucfg_mlme_get_roam_reason_vsie_status + (hdd_ctx->psoc, + &roam_reason_vsie_enabled); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("get roam reason vsie failed"); + return -EINVAL; + } + hdd_debug("is roam_reason_vsie_enabled %d", roam_reason_vsie_enabled); + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON, + roam_reason_vsie_enabled)) { + hdd_err("nla_put failure"); + return -EINVAL; + } + + return 0; +} +#else +static int hdd_get_roam_reason_vsie_status(struct hdd_adapter *adapter, + struct sk_buff *skb, + const struct nlattr *attr) +{ + return -EINVAL; +} +#endif + +/** + * typedef config_getter_fn - get configuration handler + * @adapter: The adapter being configured + * @skb: sk buffer to hold nl80211 attributes + * @attr: The nl80211 attribute being applied + * + * Defines the signature of functions in the attribute vtable + * + * Return: 0 if the attribute was handled successfully, otherwise an errno + */ +typedef int (*config_getter_fn)(struct hdd_adapter *adapter, + struct sk_buff *skb, + const struct nlattr *attr); + +/** + * struct config_getters + * @id: vendor attribute which this entry handles + * @cb: callback function to invoke to process the attribute when present + */ +struct config_getters { + uint32_t id; + size_t max_attr_len; + config_getter_fn cb; +}; + +/** + * hdd_get_optimized_power_config() - Get the number of spatial streams + * supported by the adapter + * @adapter: Pointer to HDD adapter + * @skb: sk buffer to hold nl80211 attributes + * @attr: Pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +static int hdd_get_optimized_power_config(struct hdd_adapter *adapter, + struct sk_buff *skb, + const struct nlattr *attr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t optimized_power_cfg; + int errno; + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + optimized_power_cfg = ucfg_pmo_get_power_save_mode(hdd_ctx->psoc); + + if (nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT, + optimized_power_cfg)) { + hdd_err("nla_put failure"); + return -EINVAL; + } + + return 0; +} + +/** + * hdd_get_tx_nss_config() - Get the number of tx spatial streams supported by + * the adapter + * @adapter: Pointer to HDD adapter + * @skb: sk buffer to hold nl80211 attributes + * @attr: Pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +static int hdd_get_tx_nss_config(struct hdd_adapter *adapter, + struct sk_buff *skb, + const struct nlattr *attr) +{ + uint8_t tx_nss; + QDF_STATUS status; + + if (!hdd_is_vdev_in_conn_state(adapter)) { + hdd_err("Not in connected state"); + return -EINVAL; + } + + status = hdd_get_tx_nss(adapter, &tx_nss); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to get nss"); + return -EINVAL; + } + + hdd_debug("tx_nss %d", tx_nss); + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS, tx_nss)) { + hdd_err("nla_put failure"); + return -EINVAL; + } + + return 0; +} + +/** + * hdd_get_rx_nss_config() - Get the number of rx spatial streams supported by + * the adapter + * @adapter: Pointer to HDD adapter + * @skb: sk buffer to hold nl80211 attributes + * @attr: Pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +static int hdd_get_rx_nss_config(struct hdd_adapter *adapter, + struct sk_buff *skb, + const struct nlattr *attr) +{ + uint8_t rx_nss; + QDF_STATUS status; + + if (!hdd_is_vdev_in_conn_state(adapter)) { + hdd_err("Not in connected state"); + return -EINVAL; + } + + status = hdd_get_rx_nss(adapter, &rx_nss); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to get nss"); + return -EINVAL; + } + + hdd_debug("rx_nss %d", rx_nss); + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS, rx_nss)) { + hdd_err("nla_put failure"); + return -EINVAL; + } + + return 0; +} + +/* vtable for config getters */ +static const struct config_getters config_getters[] = { +#ifdef WLAN_FEATURE_ELNA + {QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS, + sizeof(uint8_t), + hdd_get_elna_bypass}, +#endif + {QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON, + sizeof(uint8_t), + hdd_get_roam_reason_vsie_status}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT, + sizeof(uint8_t), + hdd_get_optimized_power_config}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS, + sizeof(uint8_t), + hdd_get_tx_nss_config}, + {QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS, + sizeof(uint8_t), + hdd_get_rx_nss_config}, +}; + +/** + * hdd_get_configuration() - Handle get configuration + * @adapter: adapter upon which the vendor command was received + * @tb: parsed attribute array + * + * This is a table-driven function which dispatches attributes + * in a QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION + * vendor command. + * + * Return: 0 if there were no issues, otherwise errno of the last issue + */ +static int hdd_get_configuration(struct hdd_adapter *adapter, + struct nlattr **tb) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t i, id; + unsigned long nl_buf_len = NLMSG_HDRLEN; + struct sk_buff *skb; + struct nlattr *attr; + config_getter_fn cb; + int errno = 0; + + for (i = 0; i < QDF_ARRAY_SIZE(config_getters); i++) { + id = config_getters[i].id; + attr = tb[id]; + if (!attr) + continue; + + nl_buf_len += NLA_HDRLEN + + NLA_ALIGN(config_getters[i].max_attr_len); + } + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + for (i = 0; i < QDF_ARRAY_SIZE(config_getters); i++) { + id = config_getters[i].id; + attr = tb[id]; + if (!attr) + continue; + + cb = config_getters[i].cb; + errno = cb(adapter, skb, attr); + if (errno) + break; + } + + if (errno) { + hdd_err("Failed to get wifi configuration, errno = %d", errno); + kfree_skb(skb); + return -EINVAL; + } + + cfg80211_vendor_cmd_reply(skb); + + return errno; +} + +/** + * hdd_set_independent_configuration() - Handle independent attributes + * @adapter: adapter upon which the vendor command was received + * @tb: parsed attribute array + * + * This is a table-driven function which dispatches independent + * attributes in a QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION + * vendor command. An attribute is considered independent if it + * doesn't depend upon any other attributes + * + * Return: 0 if there were no issues, otherwise errno of the last issue + */ +static int hdd_set_independent_configuration(struct hdd_adapter *adapter, + struct nlattr **tb) +{ + uint32_t i; + uint32_t id; + struct nlattr *attr; + independent_setter_fn cb; + int errno = 0; + int ret; + + for (i = 0; i < QDF_ARRAY_SIZE(independent_setters); i++) { + id = independent_setters[i].id; + attr = tb[id]; + if (!attr) + continue; + + cb = independent_setters[i].cb; + ret = cb(adapter, attr); + if (ret) + errno = ret; + } + + return errno; +} + +/** + * typedef interdependent_setter_fn - interdependent attribute handler + * @adapter: The adapter being configured + * @tb: The parsed nl80211 attributes being applied + * + * Defines the signature of functions in the interdependent attribute vtable + * + * Return: 0 if attributes were handled successfully, otherwise an errno + */ +typedef int (*interdependent_setter_fn)(struct hdd_adapter *adapter, + struct nlattr **tb); + +/* vtable for interdependent setters */ +static const interdependent_setter_fn interdependent_setters[] = { + hdd_config_access_policy, + hdd_config_mpdu_aggregation, + hdd_config_ant_div_period, + hdd_config_ant_div_snr_weight, + wlan_hdd_cfg80211_wifi_set_reorder_timeout, + wlan_hdd_cfg80211_wifi_set_rx_blocksize, + hdd_config_vdev_chains, + hdd_config_tx_rx_nss, +}; + +/** + * hdd_set_interdependent_configuration() - Handle interdependent attributes + * @adapter: adapter upon which the vendor command was received + * @tb: parsed attribute array + * + * This is a table-driven function which handles interdependent + * attributes in a QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION + * vendor command. A set of attributes is considered interdependent if + * they depend upon each other. In the typical case if one of the + * attributes is present in the the attribute array, then all of the + * attributes must be present. + * + * Return: 0 if there were no issues, otherwise errno of the last issue + */ +static int hdd_set_interdependent_configuration(struct hdd_adapter *adapter, + struct nlattr **tb) +{ + uint32_t i; + interdependent_setter_fn cb; + int errno = 0; + int ret; + + for (i = 0; i < QDF_ARRAY_SIZE(interdependent_setters); i++) { + cb = interdependent_setters[i]; + ret = cb(adapter, tb); + if (ret) + errno = ret; + } + + return errno; +} + +/** + * __wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration + * vendor command + * + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX. + * + * Return: Error code. + */ +static int +__wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1]; + int errno; + int ret; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, data, + data_len, wlan_hdd_wifi_config_policy)) { + hdd_err("invalid attr"); + return -EINVAL; + } + + ret = hdd_set_independent_configuration(adapter, tb); + if (ret) + errno = ret; + + ret = hdd_set_interdependent_configuration(adapter, tb); + if (ret) + errno = ret; + + return errno; +} + +/** + * wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration + * vendor command + * + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX. + * + * Return: EOK or other error codes. + */ +static int wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_wifi_configuration_set(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_wifi_configuration_get() - Wifi configuration + * vendor command + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX. + * + * Return: Error code. + */ +static int +__wlan_hdd_cfg80211_wifi_configuration_get(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1]; + int errno; + int ret; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, data, + data_len, wlan_hdd_wifi_config_policy)) { + hdd_err("invalid attr"); + return -EINVAL; + } + + ret = hdd_get_configuration(adapter, tb); + if (ret) + errno = ret; + + return errno; +} + +/** + * wlan_hdd_cfg80211_wifi_configuration_get() - Wifi configuration + * vendor command + * + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX. + * + * Return: EOK or other error codes. + */ +static int wlan_hdd_cfg80211_wifi_configuration_get(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_wifi_configuration_get(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static void hdd_disable_runtime_pm_for_user(struct hdd_context *hdd_ctx) +{ + struct hdd_runtime_pm_context *ctx = &hdd_ctx->runtime_context; + + if (!ctx) + return; + + if (ctx->is_user_wakelock_acquired) + return; + + ctx->is_user_wakelock_acquired = true; + qdf_runtime_pm_prevent_suspend(&ctx->user); +} + +/** + * hdd_twt_setup_req_type_to_cmd() - Converts twt setup request type to twt + * cmd + * @req_type: twt setup request type + * @twt_cmd: pointer to store twt command + * + * Return: QDF_STATUS_SUCCESS on success, else other qdf error values + */ +static QDF_STATUS +hdd_twt_setup_req_type_to_cmd(u8 req_type, enum WMI_HOST_TWT_COMMAND *twt_cmd) +{ + if (req_type == QCA_WLAN_VENDOR_TWT_SETUP_REQUEST) { + *twt_cmd = WMI_HOST_TWT_COMMAND_REQUEST_TWT; + } else if (req_type == QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST) { + *twt_cmd = WMI_HOST_TWT_COMMAND_SUGGEST_TWT; + } else if (req_type == QCA_WLAN_VENDOR_TWT_SETUP_DEMAND) { + *twt_cmd = WMI_HOST_TWT_COMMAND_DEMAND_TWT; + } else { + hdd_err_rl("Invalid TWT_SETUP_REQ_TYPE %d", req_type); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * __wlan_hdd_cfg80211_set_wifi_test_config() - Wifi test configuration + * vendor command + * + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Handles QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX + * + * Return: Error code. + */ +static int +__wlan_hdd_cfg80211_set_wifi_test_config(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX + 1]; + int ret_val = 0; + uint8_t cfg_val = 0; + uint8_t set_val = 0; + struct sme_config_params *sme_config; + bool update_sme_cfg = false; + uint8_t tid = 0, ac; + uint16_t buff_size = 0; + mac_handle_t mac_handle; + QDF_STATUS status; + bool bval = false; + uint8_t value = 0; + uint8_t wmm_mode = 0; + uint32_t cmd_id; + + hdd_enter_dev(dev); + + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + if (!sme_config) + return -ENOMEM; + + mac_handle = hdd_ctx->mac_handle; + sme_get_config_param(mac_handle, sme_config); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + ret_val = -EPERM; + goto send_err; + } + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + goto send_err; + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_err("Driver Modules are closed, can not start logger"); + ret_val = -EINVAL; + goto send_err; + } + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX, + data, data_len, wlan_hdd_wifi_test_config_policy)) { + hdd_err("invalid attr"); + ret_val = -EINVAL; + goto send_err; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ] + ); + hdd_debug("set addba accept req from peer value %d", cfg_val); + ret_val = sme_set_addba_accept(mac_handle, adapter->vdev_id, + cfg_val); + if (ret_val) + goto send_err; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS]); + hdd_debug("set HE MCS value 0x%0X", cfg_val); + ret_val = sme_update_he_mcs(mac_handle, adapter->vdev_id, + cfg_val); + if (ret_val) + goto send_err; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE]); + if (!cfg_val) { + sme_config->csr_config.WMMSupportMode = + hdd_to_csr_wmm_mode(HDD_WMM_USER_MODE_NO_QOS); + hdd_debug("wmm is disabled"); + } else { + status = ucfg_mlme_get_wmm_mode(hdd_ctx->psoc, + &wmm_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get wmm_mode failed"); + return QDF_STATUS_E_FAILURE; + } + sme_config->csr_config.WMMSupportMode = + hdd_to_csr_wmm_mode(wmm_mode); + hdd_debug("using wmm default value"); + } + update_sme_cfg = true; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ]); + if (cfg_val) { + /*Auto BA mode*/ + set_val = 0; + hdd_debug("BA operating mode is set to auto"); + } else { + /*Manual BA mode*/ + set_val = 1; + hdd_debug("BA operating mode is set to Manual"); + } + + ret_val = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_BA_MODE, set_val, VDEV_CMD); + if (ret_val) { + hdd_err("Set BA operating mode failed"); + goto send_err; + } + if (!cfg_val) { + ret_val = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_AMSDU_AGGREGATION_SIZE_OPTIMIZATION, + 0, VDEV_CMD); + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION] + ); + if (cfg_val > HE_FRAG_LEVEL1) + set_val = HE_FRAG_LEVEL1; + else + set_val = cfg_val; + + hdd_debug("set HE fragmention to %d", set_val); + ret_val = sme_update_he_frag_supp(mac_handle, + adapter->vdev_id, set_val); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE]); + sme_config->csr_config.wep_tkip_in_he = cfg_val; + hdd_debug("Set WEP/TKIP allow in HE %d", cfg_val); + + update_sme_cfg = true; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION]) { + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID]) { + tid = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID]); + } else { + hdd_err("TID is not set for ADD/DEL BA cfg"); + ret_val = -EINVAL; + goto send_err; + } + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION]); + if (cfg_val == QCA_WLAN_ADD_BA) { + if (tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]) + buff_size = nla_get_u16(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]); + ret_val = sme_send_addba_req(mac_handle, + adapter->vdev_id, + tid, buff_size); + } else if (cfg_val == QCA_WLAN_DELETE_BA) { + } else { + hdd_err("Invalid BA session cfg"); + ret_val = -EINVAL; + goto send_err; + } + } else if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]) { + buff_size = nla_get_u16(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]); + hdd_debug("set buff size to %d for all tids", buff_size); + ret_val = sme_set_ba_buff_size(mac_handle, + adapter->vdev_id, buff_size); + if (ret_val) + goto send_err; + if (buff_size > 64) + /* Configure ADDBA req buffer size to 256 */ + set_val = 3; + else + /* Configure ADDBA req buffer size to 64 */ + set_val = 2; + ret_val = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_BA_MODE, set_val, VDEV_CMD); + if (ret_val) + hdd_err("Failed to set BA operating mode %d", set_val); + ret_val = wma_cli_set_command(adapter->vdev_id, + GEN_VDEV_PARAM_AMPDU, + buff_size, GEN_CMD); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK]) { + int he_mcs_val; + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC]) { + ac = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC]); + } else { + hdd_err("AC is not set for NO ACK policy config"); + ret_val = -EINVAL; + goto send_err; + } + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK]); + hdd_debug("Set NO_ACK to %d for ac %d", cfg_val, ac); + ret_val = sme_set_no_ack_policy(mac_handle, + adapter->vdev_id, + cfg_val, ac); + if (cfg_val) { + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, + &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + if (bval) + /*2x2 MCS 5 value*/ + he_mcs_val = 0x45; + else + /*1x1 MCS 5 value*/ + he_mcs_val = 0x25; + + if (hdd_set_11ax_rate(adapter, he_mcs_val, NULL)) + hdd_err("HE MCS set failed, MCS val %0x", + he_mcs_val); + } else { + if (hdd_set_11ax_rate(adapter, 0xFF, NULL)) + hdd_err("disable fixed rate failed"); + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF]); + hdd_debug("Set HE LTF to %d", cfg_val); + ret_val = sme_set_auto_rate_he_ltf(mac_handle, + adapter->vdev_id, + cfg_val); + if (ret_val) + sme_err("Failed to set auto rate HE LTF"); + + ret_val = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_HE_LTF, + cfg_val, VDEV_CMD); + if (ret_val) + goto send_err; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE]); + hdd_debug("Set Tx beamformee to %d", cfg_val); + ret_val = sme_update_tx_bfee_supp(mac_handle, + adapter->vdev_id, + cfg_val); + if (ret_val) + sme_err("Failed to set Tx beamformee cap"); + + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS]); + status = ucfg_mlme_cfg_get_vht_tx_bfee_ant_supp(hdd_ctx->psoc, + &value); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get tx_bfee_ant_supp"); + + if (!cfg_in_range(CFG_VHT_BEAMFORMEE_ANT_SUPP, cfg_val)) { + hdd_err("NSTS %d not supported, supp_val %d", cfg_val, + value); + ret_val = -ENOTSUPP; + goto send_err; + } + hdd_debug("Set Tx beamformee NSTS to %d", cfg_val); + ret_val = sme_update_tx_bfee_nsts(hdd_ctx->mac_handle, + adapter->vdev_id, + cfg_val, + value); + if (ret_val) + sme_err("Failed to set Tx beamformee cap"); + + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR]); + if (cfg_val) { + hdd_debug("Set HE mac padding dur to %d", cfg_val); + ret_val = sme_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN, + 0, VDEV_CMD); + if (ret_val) + hdd_err("MU_EDCA update disable failed"); + sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, true); + sme_set_he_mu_edca_def_cfg(hdd_ctx->mac_handle); + if (sme_update_mu_edca_params(hdd_ctx->mac_handle, + adapter->vdev_id)) + hdd_err("Failed to send mu edca params"); + } else { + ret_val = sme_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN, + 1, VDEV_CMD); + sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, false); + } + ret_val = sme_update_he_trigger_frm_mac_pad(hdd_ctx->mac_handle, + adapter->vdev_id, + cfg_val); + if (ret_val) + hdd_err("Failed to set Trig frame mac padding cap"); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA]); + if (cfg_val) { + ret_val = sme_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN, + 0, VDEV_CMD); + if (ret_val) + hdd_err("MU_EDCA update disable failed"); + sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, true); + sme_set_he_mu_edca_def_cfg(hdd_ctx->mac_handle); + if (sme_update_mu_edca_params(hdd_ctx->mac_handle, + adapter->vdev_id)) + hdd_err("Failed to send mu edca params"); + } else { + ret_val = sme_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN, + 1, VDEV_CMD); + sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, false); + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP]) { + cfg_val = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP]); + ret_val = sme_update_he_om_ctrl_supp(hdd_ctx->mac_handle, + adapter->vdev_id, + cfg_val); + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX; + if (tb[cmd_id]) { + struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX + 1]; + struct nlattr *curr_attr; + int tmp, rc; + nla_for_each_nested(curr_attr, tb[cmd_id], tmp) { + rc = wlan_cfg80211_nla_parse( + tb2, QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX, + nla_data(curr_attr), + nla_len(curr_attr), + qca_wlan_vendor_attr_he_omi_tx_policy); + if (rc) { + hdd_err("Invalid ATTR"); + goto send_err; + } + cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW; + if (tb2[cmd_id]) { + cfg_val = nla_get_u8(tb2[cmd_id]); + ret_val = sme_set_he_om_ctrl_param( + hdd_ctx->mac_handle, + adapter->vdev_id, + cmd_id, cfg_val); + } + cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS; + if (tb2[cmd_id]) { + cfg_val = nla_get_u8(tb2[cmd_id]); + ret_val = sme_set_he_om_ctrl_param( + hdd_ctx->mac_handle, + adapter->vdev_id, + cmd_id, cfg_val); + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE; + if (tb2[cmd_id]) { + cfg_val = nla_get_u8(tb2[cmd_id]); + ret_val = sme_set_he_om_ctrl_param( + hdd_ctx->mac_handle, + adapter->vdev_id, + cmd_id, cfg_val); + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS; + if (tb2[cmd_id]) { + cfg_val = nla_get_u8(tb2[cmd_id]); + ret_val = sme_set_he_om_ctrl_param( + hdd_ctx->mac_handle, + adapter->vdev_id, + cmd_id, cfg_val); + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE; + if (tb2[cmd_id]) { + cfg_val = nla_get_u8(tb2[cmd_id]); + ret_val = sme_set_he_om_ctrl_param( + hdd_ctx->mac_handle, + adapter->vdev_id, + cmd_id, cfg_val); + } + + } + if (ret_val) { + sme_reset_he_om_ctrl(hdd_ctx->mac_handle); + goto send_err; + } + ret_val = sme_send_he_om_ctrl_update(hdd_ctx->mac_handle, + adapter->vdev_id); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG]) + sme_reset_he_om_ctrl(hdd_ctx->mac_handle); + + cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU; + if (tb[cmd_id]) { + cfg_val = nla_get_u8(tb[cmd_id]); + hdd_debug("Configure Tx SU PPDU enable %d", cfg_val); + if (cfg_val) + sme_config_su_ppdu_queue(adapter->vdev_id, true); + else + sme_config_su_ppdu_queue(adapter->vdev_id, false); + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT; + if (tb[cmd_id]) { + cfg_val = nla_get_u8(tb[cmd_id]); + hdd_debug("Configure 2G VHT support %d", cfg_val); + ucfg_mlme_set_vht_for_24ghz(hdd_ctx->psoc, + (cfg_val ? true : false)); + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP; + if (tb[cmd_id]) { + cfg_val = nla_get_u8(tb[cmd_id]); + hdd_debug("Configure +HTC_HE support %d", cfg_val); + sme_update_he_htc_he_supp(hdd_ctx->mac_handle, + adapter->vdev_id, + (cfg_val ? true : false)); + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS; + if (tb[cmd_id]) { + hdd_disable_runtime_pm_for_user(hdd_ctx); + cfg_val = nla_get_u8(tb[cmd_id]); + hdd_debug("Configure HE testbed defaults %d", cfg_val); + if (!cfg_val) + sme_reset_he_caps(hdd_ctx->mac_handle, + adapter->vdev_id); + else + sme_set_he_testbed_def(hdd_ctx->mac_handle, + adapter->vdev_id); + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU; + if (tb[cmd_id]) { + cfg_val = nla_get_u8(tb[cmd_id]); + hdd_debug("Configure Action frame Tx in TB PPDU %d", cfg_val); + sme_config_action_tx_in_tb_ppdu(hdd_ctx->mac_handle, + adapter->vdev_id, cfg_val); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP]) { + struct wmi_twt_add_dialog_param params = {0}; + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + uint32_t wake_intvl_exp = 0, result = 0; + struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX + 1]; + struct nlattr *twt_session; + int tmp, rc; + uint32_t congestion_timeout = 0; + + if ((adapter->device_mode != QDF_STA_MODE && + adapter->device_mode != QDF_P2P_CLIENT_MODE) || + hdd_sta_ctx->conn_info.conn_state != + eConnectionState_Associated) { + hdd_err_rl("Invalid state, vdev %d mode %d state %d", + adapter->vdev_id, adapter->device_mode, + hdd_sta_ctx->conn_info.conn_state); + goto send_err; + } + + qdf_mem_copy(params.peer_macaddr, + hdd_sta_ctx->conn_info.bssid.bytes, + QDF_MAC_ADDR_SIZE); + params.vdev_id = adapter->vdev_id; + params.dialog_id = 0; + + cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP; + nla_for_each_nested(twt_session, tb[cmd_id], tmp) { + rc = wlan_cfg80211_nla_parse( + tb2, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX, + nla_data(twt_session), + nla_len(twt_session), + qca_wlan_vendor_twt_add_dialog_policy); + if (rc) { + hdd_err_rl("Invalid twt ATTR"); + goto send_err; + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP; + if (!tb2[cmd_id]) { + hdd_err_rl("TWT_SETUP_WAKE_INTVL_EXP is must"); + goto send_err; + } + wake_intvl_exp = nla_get_u8(tb2[cmd_id]); + if (wake_intvl_exp > TWT_SETUP_WAKE_INTVL_EXP_MAX) { + hdd_err_rl("Invalid wake_intvl_exp %u > %u", + wake_intvl_exp, + TWT_SETUP_WAKE_INTVL_EXP_MAX); + goto send_err; + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST; + if (tb2[cmd_id]) + params.flag_bcast = nla_get_flag(tb2[cmd_id]); + + cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE; + if (!tb2[cmd_id]) { + hdd_err_rl("TWT_SETUP_REQ_TYPE is must"); + goto send_err; + } + + status = hdd_twt_setup_req_type_to_cmd( + nla_get_u8(tb2[cmd_id]), + ¶ms.twt_cmd); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err_rl("TWT cmd type is invalid"); + goto send_err; + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER; + if (tb2[cmd_id]) + params.flag_trigger = nla_get_flag(tb2[cmd_id]); + + cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE; + if (!tb2[cmd_id]) { + hdd_err_rl("TWT_SETUP_FLOW_TYPE is must"); + goto send_err; + } + params.flag_flow_type = nla_get_u8(tb2[cmd_id]); + + cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION; + if (tb2[cmd_id]) + params.flag_protection = + nla_get_flag(tb2[cmd_id]); + + cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME; + if (tb2[cmd_id]) + params.sp_offset_us = nla_get_u32(tb2[cmd_id]); + + cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION; + if (!tb2[cmd_id]) { + hdd_err_rl("TWT_SETUP_WAKE_DURATION is must"); + goto send_err; + } + params.wake_dura_us = 256 * nla_get_u32(tb2[cmd_id]); + if (params.wake_dura_us > TWT_SETUP_WAKE_DURATION_MAX) { + hdd_err_rl("Invalid wake_dura_us %u", + params.wake_dura_us); + goto send_err; + } + + cmd_id = + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA; + if (!tb2[cmd_id]) { + hdd_err_rl("SETUP_WAKE_INTVL_MANTISSA is must"); + goto send_err; + } + params.wake_intvl_mantis = nla_get_u32(tb2[cmd_id]); + if (params.wake_intvl_mantis > + TWT_SETUP_WAKE_INTVL_MANTISSA_MAX) { + hdd_err_rl("Invalid wake_intvl_mantis %u", + params.wake_dura_us); + goto send_err; + } + + if (wake_intvl_exp && params.wake_intvl_mantis) { + result = 2 << (wake_intvl_exp - 1); + if (result > + (UINT_MAX / params.wake_intvl_mantis)) { + hdd_err_rl("Invalid exp %d mantissa %d", + wake_intvl_exp, + params.wake_intvl_mantis); + goto send_err; + } + params.wake_intvl_us = + params.wake_intvl_mantis * result; + } else { + params.wake_intvl_us = params.wake_intvl_mantis; + } + + hdd_debug("twt: vdev %d, intvl_us %d, mantis %d", + params.vdev_id, params.wake_intvl_us, + params.wake_intvl_mantis); + hdd_debug("twt: dura %d, offset %d, cmd %d", + params.wake_dura_us, params.sp_offset_us, + params.twt_cmd); + hdd_debug("twt: bcast %d, trigger %d, type %d, prot %d", + params.flag_bcast, params.flag_trigger, + params.flag_flow_type, + params.flag_protection); + + ucfg_mlme_get_twt_congestion_timeout(hdd_ctx->psoc, + &congestion_timeout); + if (congestion_timeout) { + ret_val = qdf_status_to_os_return( + hdd_send_twt_disable_cmd(hdd_ctx)); + if (ret_val) { + hdd_err("Failed to disable TWT"); + goto send_err; + } + ucfg_mlme_set_twt_congestion_timeout( + hdd_ctx->psoc, 0); + hdd_send_twt_enable_cmd(hdd_ctx); + } + ret_val = qdf_status_to_os_return( + wma_twt_process_add_dialog(¶ms)); + if (ret_val) + goto send_err; + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE]) { + struct wmi_twt_del_dialog_param params = {0}; + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if ((adapter->device_mode != QDF_STA_MODE && + adapter->device_mode != QDF_P2P_CLIENT_MODE) || + hdd_sta_ctx->conn_info.conn_state != + eConnectionState_Associated) { + hdd_err("Invalid state, vdev %d mode %d state %d", + adapter->vdev_id, adapter->device_mode, + hdd_sta_ctx->conn_info.conn_state); + goto send_err; + } + qdf_mem_copy(params.peer_macaddr, + hdd_sta_ctx->conn_info.bssid.bytes, + QDF_MAC_ADDR_SIZE); + params.vdev_id = adapter->vdev_id; + params.dialog_id = 0; + hdd_debug("twt_terminate: vdev_id %d", params.vdev_id); + ret_val = qdf_status_to_os_return( + wma_twt_process_del_dialog(¶ms)); + if (ret_val) + goto send_err; + } + + cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT; + if (tb[cmd_id]) { + cfg_val = nla_get_u8(tb[cmd_id]); + hdd_debug("twt_request: val %d", cfg_val); + ret_val = sme_update_he_twt_req_support(hdd_ctx->mac_handle, + adapter->vdev_id, + cfg_val); + } + + if (update_sme_cfg) + sme_update_config(mac_handle, sme_config); + +send_err: + qdf_mem_free(sme_config); + + return ret_val; +} + +/** + * wlan_hdd_cfg80211_set_wifi_test_config() - Wifi test configuration + * vendor command + * + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Handles QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX + * + * Return: EOK or other error codes. + */ +static int wlan_hdd_cfg80211_set_wifi_test_config(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_wifi_test_config(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static const struct +nla_policy +qca_wlan_vendor_wifi_logger_start_policy +[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID] + = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL] + = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS] + = {.type = NLA_U32 }, +}; + +/** + * __wlan_hdd_cfg80211_wifi_logger_start() - This function is used to enable + * or disable the collection of packet statistics from the firmware + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function enables or disables the collection of packet statistics from + * the firmware + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + QDF_STATUS status; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1]; + struct sir_wifi_start_log start_log = { 0 }; + mac_handle_t mac_handle; + + hdd_enter_dev(wdev->netdev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_err("Driver Modules are closed, can not start logger"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX, + data, data_len, + qca_wlan_vendor_wifi_logger_start_policy)) { + hdd_err("Invalid attribute"); + return -EINVAL; + } + + /* Parse and fetch ring id */ + if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]) { + hdd_err("attr ATTR failed"); + return -EINVAL; + } + start_log.ring_id = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]); + hdd_debug("Ring ID=%d", start_log.ring_id); + + /* Parse and fetch verbose level */ + if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]) { + hdd_err("attr verbose_level failed"); + return -EINVAL; + } + start_log.verbose_level = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]); + hdd_debug("verbose_level=%d", start_log.verbose_level); + + /* Parse and fetch flag */ + if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]) { + hdd_err("attr flag failed"); + return -EINVAL; + } + start_log.is_iwpriv_command = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]); + + start_log.user_triggered = 1; + + /* size is buff size which can be set using iwpriv command*/ + start_log.size = 0; + start_log.is_pktlog_buff_clear = false; + + cds_set_ring_log_level(start_log.ring_id, start_log.verbose_level); + + if (start_log.ring_id == RING_ID_WAKELOCK) { + /* Start/stop wakelock events */ + if (start_log.verbose_level > WLAN_LOG_LEVEL_OFF) + cds_set_wakelock_logging(true); + else + cds_set_wakelock_logging(false); + return 0; + } + + if (start_log.ring_id == RING_ID_PER_PACKET_STATS) { + if (hdd_ctx->is_pktlog_enabled && + (start_log.verbose_level == WLAN_LOG_LEVEL_ACTIVE)) + return 0; + + if ((!hdd_ctx->is_pktlog_enabled) && + (start_log.verbose_level != WLAN_LOG_LEVEL_ACTIVE)) + return 0; + } + + mac_handle = hdd_ctx->mac_handle; + status = sme_wifi_start_logger(mac_handle, start_log); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_wifi_start_logger failed(err=%d)", + status); + return -EINVAL; + } + + if (start_log.ring_id == RING_ID_PER_PACKET_STATS) { + if (start_log.verbose_level == WLAN_LOG_LEVEL_ACTIVE) + hdd_ctx->is_pktlog_enabled = true; + else + hdd_ctx->is_pktlog_enabled = false; + } + + return 0; +} + +/** + * wlan_hdd_cfg80211_wifi_logger_start() - Wrapper function used to enable + * or disable the collection of packet statistics from the firmware + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function is used to enable or disable the collection of packet + * statistics from the firmware + * + * Return: 0 on success and errno on failure + */ +static int wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_wifi_logger_start(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +static const struct +nla_policy +qca_wlan_vendor_wifi_logger_get_ring_data_policy +[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID] + = {.type = NLA_U32 }, +}; + +/** + * __wlan_hdd_cfg80211_wifi_logger_get_ring_data() - Flush per packet stats + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function is used to flush or retrieve the per packet statistics from + * the driver + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_wifi_logger_get_ring_data(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + QDF_STATUS status; + uint32_t ring_id; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb + [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX + 1]; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX, + data, data_len, + qca_wlan_vendor_wifi_logger_get_ring_data_policy)) { + hdd_err("Invalid attribute"); + return -EINVAL; + } + + /* Parse and fetch ring id */ + if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]) { + hdd_err("attr ATTR failed"); + return -EINVAL; + } + + ring_id = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]); + + if (ring_id == RING_ID_PER_PACKET_STATS) { + wlan_logging_set_per_pkt_stats(); + hdd_debug("Flushing/Retrieving packet stats"); + } else if (ring_id == RING_ID_DRIVER_DEBUG) { + /* + * As part of DRIVER ring ID, flush both driver and fw logs. + * For other Ring ID's driver doesn't have any rings to flush + */ + hdd_debug("Bug report triggered by framework"); + + status = cds_flush_logs(WLAN_LOG_TYPE_NON_FATAL, + WLAN_LOG_INDICATOR_FRAMEWORK, + WLAN_LOG_REASON_CODE_UNUSED, + false, false); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed to trigger bug report"); + return -EINVAL; + } + } else { + wlan_report_log_completion(WLAN_LOG_TYPE_NON_FATAL, + WLAN_LOG_INDICATOR_FRAMEWORK, + WLAN_LOG_REASON_CODE_UNUSED, + ring_id); + } + return 0; +} + +/** + * wlan_hdd_cfg80211_wifi_logger_get_ring_data() - Wrapper to flush packet stats + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function is used to flush or retrieve the per packet statistics from + * the driver + * + * Return: 0 on success and errno on failure + */ +static int wlan_hdd_cfg80211_wifi_logger_get_ring_data(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_wifi_logger_get_ring_data(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +#ifdef WLAN_FEATURE_OFFLOAD_PACKETS +/** + * hdd_map_req_id_to_pattern_id() - map request id to pattern id + * @hdd_ctx: HDD context + * @request_id: [input] request id + * @pattern_id: [output] pattern id + * + * This function loops through request id to pattern id array + * if the slot is available, store the request id and return pattern id + * if entry exists, return the pattern id + * + * Return: 0 on success and errno on failure + */ +static int hdd_map_req_id_to_pattern_id(struct hdd_context *hdd_ctx, + uint32_t request_id, + uint8_t *pattern_id) +{ + uint32_t i; + + mutex_lock(&hdd_ctx->op_ctx.op_lock); + for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) { + if (hdd_ctx->op_ctx.op_table[i].request_id == MAX_REQUEST_ID) { + hdd_ctx->op_ctx.op_table[i].request_id = request_id; + *pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id; + mutex_unlock(&hdd_ctx->op_ctx.op_lock); + return 0; + } else if (hdd_ctx->op_ctx.op_table[i].request_id == + request_id) { + *pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id; + mutex_unlock(&hdd_ctx->op_ctx.op_lock); + return 0; + } + } + mutex_unlock(&hdd_ctx->op_ctx.op_lock); + return -ENOBUFS; +} + +/** + * hdd_unmap_req_id_to_pattern_id() - unmap request id to pattern id + * @hdd_ctx: HDD context + * @request_id: [input] request id + * @pattern_id: [output] pattern id + * + * This function loops through request id to pattern id array + * reset request id to 0 (slot available again) and + * return pattern id + * + * Return: 0 on success and errno on failure + */ +static int hdd_unmap_req_id_to_pattern_id(struct hdd_context *hdd_ctx, + uint32_t request_id, + uint8_t *pattern_id) +{ + uint32_t i; + + mutex_lock(&hdd_ctx->op_ctx.op_lock); + for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) { + if (hdd_ctx->op_ctx.op_table[i].request_id == request_id) { + hdd_ctx->op_ctx.op_table[i].request_id = MAX_REQUEST_ID; + *pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id; + mutex_unlock(&hdd_ctx->op_ctx.op_lock); + return 0; + } + } + mutex_unlock(&hdd_ctx->op_ctx.op_lock); + return -EINVAL; +} + + +/* + * define short names for the global vendor params + * used by __wlan_hdd_cfg80211_offloaded_packets() + */ +#define PARAM_MAX QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX +#define PARAM_REQUEST_ID \ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID +#define PARAM_CONTROL \ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL +#define PARAM_IP_PACKET \ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA +#define PARAM_SRC_MAC_ADDR \ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR +#define PARAM_DST_MAC_ADDR \ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR +#define PARAM_PERIOD QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD +#define PARAM_PROTO_TYPE \ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE + +/** + * wlan_hdd_add_tx_ptrn() - add tx pattern + * @adapter: adapter pointer + * @hdd_ctx: hdd context + * @tb: nl attributes + * + * This function reads the NL attributes and forms a AddTxPtrn message + * posts it to SME. + * + */ +static int +wlan_hdd_add_tx_ptrn(struct hdd_adapter *adapter, struct hdd_context *hdd_ctx, + struct nlattr **tb) +{ + struct sSirAddPeriodicTxPtrn *add_req; + QDF_STATUS status; + uint32_t request_id, len; + int32_t ret; + uint8_t pattern_id = 0; + struct qdf_mac_addr dst_addr; + uint16_t eth_type = htons(ETH_P_IP); + mac_handle_t mac_handle; + + if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + hdd_err("Not in Connected state!"); + return -ENOTSUPP; + } + + add_req = qdf_mem_malloc(sizeof(*add_req)); + if (!add_req) + return -ENOMEM; + + /* Parse and fetch request Id */ + if (!tb[PARAM_REQUEST_ID]) { + hdd_err("attr request id failed"); + ret = -EINVAL; + goto fail; + } + + request_id = nla_get_u32(tb[PARAM_REQUEST_ID]); + if (request_id == MAX_REQUEST_ID) { + hdd_err("request_id cannot be MAX"); + ret = -EINVAL; + goto fail; + } + hdd_debug("Request Id: %u", request_id); + + if (!tb[PARAM_PERIOD]) { + hdd_err("attr period failed"); + ret = -EINVAL; + goto fail; + } + + add_req->usPtrnIntervalMs = nla_get_u32(tb[PARAM_PERIOD]); + hdd_debug("Period: %u ms", add_req->usPtrnIntervalMs); + if (add_req->usPtrnIntervalMs == 0) { + hdd_err("Invalid interval zero, return failure"); + ret = -EINVAL; + goto fail; + } + + if (!tb[PARAM_SRC_MAC_ADDR]) { + hdd_err("attr source mac address failed"); + ret = -EINVAL; + goto fail; + } + nla_memcpy(add_req->mac_address.bytes, tb[PARAM_SRC_MAC_ADDR], + QDF_MAC_ADDR_SIZE); + hdd_debug("input src mac address: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(add_req->mac_address.bytes)); + + if (!qdf_is_macaddr_equal(&add_req->mac_address, + &adapter->mac_addr)) { + hdd_err("input src mac address and connected ap bssid are different"); + ret = -EINVAL; + goto fail; + } + + if (!tb[PARAM_DST_MAC_ADDR]) { + hdd_err("attr dst mac address failed"); + ret = -EINVAL; + goto fail; + } + nla_memcpy(dst_addr.bytes, tb[PARAM_DST_MAC_ADDR], QDF_MAC_ADDR_SIZE); + hdd_debug("input dst mac address: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(dst_addr.bytes)); + + if (!tb[PARAM_IP_PACKET]) { + hdd_err("attr ip packet failed"); + ret = -EINVAL; + goto fail; + } + add_req->ucPtrnSize = nla_len(tb[PARAM_IP_PACKET]); + hdd_debug("IP packet len: %u", add_req->ucPtrnSize); + + if (add_req->ucPtrnSize < 0 || + add_req->ucPtrnSize > (PERIODIC_TX_PTRN_MAX_SIZE - + ETH_HLEN)) { + hdd_err("Invalid IP packet len: %d", + add_req->ucPtrnSize); + ret = -EINVAL; + goto fail; + } + + if (!tb[PARAM_PROTO_TYPE]) + eth_type = htons(ETH_P_IP); + else + eth_type = htons(nla_get_u16(tb[PARAM_PROTO_TYPE])); + + hdd_debug("packet proto type: %u", eth_type); + + len = 0; + qdf_mem_copy(&add_req->ucPattern[0], dst_addr.bytes, QDF_MAC_ADDR_SIZE); + len += QDF_MAC_ADDR_SIZE; + qdf_mem_copy(&add_req->ucPattern[len], add_req->mac_address.bytes, + QDF_MAC_ADDR_SIZE); + len += QDF_MAC_ADDR_SIZE; + qdf_mem_copy(&add_req->ucPattern[len], ð_type, 2); + len += 2; + + /* + * This is the IP packet, add 14 bytes Ethernet (802.3) header + * ------------------------------------------------------------ + * | 14 bytes Ethernet (802.3) header | IP header and payload | + * ------------------------------------------------------------ + */ + qdf_mem_copy(&add_req->ucPattern[len], + nla_data(tb[PARAM_IP_PACKET]), + add_req->ucPtrnSize); + add_req->ucPtrnSize += len; + + ret = hdd_map_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id); + if (ret) { + hdd_err("req id to pattern id failed (ret=%d)", ret); + goto fail; + } + add_req->ucPtrnId = pattern_id; + hdd_debug("pattern id: %d", add_req->ucPtrnId); + + mac_handle = hdd_ctx->mac_handle; + status = sme_add_periodic_tx_ptrn(mac_handle, add_req); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_add_periodic_tx_ptrn failed (err=%d)", status); + ret = qdf_status_to_os_return(status); + goto fail; + } + + hdd_exit(); + +fail: + qdf_mem_free(add_req); + return ret; +} + +/** + * wlan_hdd_del_tx_ptrn() - delete tx pattern + * @adapter: adapter pointer + * @hdd_ctx: hdd context + * @tb: nl attributes + * + * This function reads the NL attributes and forms a DelTxPtrn message + * posts it to SME. + * + */ +static int +wlan_hdd_del_tx_ptrn(struct hdd_adapter *adapter, struct hdd_context *hdd_ctx, + struct nlattr **tb) +{ + struct sSirDelPeriodicTxPtrn *del_req; + QDF_STATUS status; + uint32_t request_id, ret; + uint8_t pattern_id = 0; + mac_handle_t mac_handle; + + /* Parse and fetch request Id */ + if (!tb[PARAM_REQUEST_ID]) { + hdd_err("attr request id failed"); + return -EINVAL; + } + request_id = nla_get_u32(tb[PARAM_REQUEST_ID]); + if (request_id == MAX_REQUEST_ID) { + hdd_err("request_id cannot be MAX"); + return -EINVAL; + } + + ret = hdd_unmap_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id); + if (ret) { + hdd_err("req id to pattern id failed (ret=%d)", ret); + return -EINVAL; + } + + del_req = qdf_mem_malloc(sizeof(*del_req)); + if (!del_req) + return -ENOMEM; + + qdf_copy_macaddr(&del_req->mac_address, &adapter->mac_addr); + hdd_debug(QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(del_req->mac_address.bytes)); + del_req->ucPtrnId = pattern_id; + hdd_debug("Request Id: %u Pattern id: %d", + request_id, del_req->ucPtrnId); + + mac_handle = hdd_ctx->mac_handle; + status = sme_del_periodic_tx_ptrn(mac_handle, del_req); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_del_periodic_tx_ptrn failed (err=%d)", status); + goto fail; + } + + hdd_exit(); + qdf_mem_free(del_req); + return 0; + +fail: + qdf_mem_free(del_req); + return -EINVAL; +} + + +/** + * __wlan_hdd_cfg80211_offloaded_packets() - send offloaded packets + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int +__wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[PARAM_MAX + 1]; + uint8_t control; + int ret; + static const struct nla_policy policy[PARAM_MAX + 1] = { + [PARAM_REQUEST_ID] = { .type = NLA_U32 }, + [PARAM_CONTROL] = { .type = NLA_U32 }, + [PARAM_SRC_MAC_ADDR] = { .type = NLA_BINARY, + .len = QDF_MAC_ADDR_SIZE }, + [PARAM_DST_MAC_ADDR] = { .type = NLA_BINARY, + .len = QDF_MAC_ADDR_SIZE }, + [PARAM_PERIOD] = { .type = NLA_U32 }, + [PARAM_PROTO_TYPE] = {.type = NLA_U16}, + }; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (!sme_is_feature_supported_by_fw(WLAN_PERIODIC_TX_PTRN)) { + hdd_err("Periodic Tx Pattern Offload feature is not supported in FW!"); + return -ENOTSUPP; + } + + if (wlan_cfg80211_nla_parse(tb, PARAM_MAX, data, data_len, policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[PARAM_CONTROL]) { + hdd_err("attr control failed"); + return -EINVAL; + } + control = nla_get_u32(tb[PARAM_CONTROL]); + hdd_debug("Control: %d", control); + + if (control == WLAN_START_OFFLOADED_PACKETS) + return wlan_hdd_add_tx_ptrn(adapter, hdd_ctx, tb); + if (control == WLAN_STOP_OFFLOADED_PACKETS) + return wlan_hdd_del_tx_ptrn(adapter, hdd_ctx, tb); + + hdd_err("Invalid control: %d", control); + return -EINVAL; +} + +/* + * done with short names for the global vendor params + * used by __wlan_hdd_cfg80211_offloaded_packets() + */ +#undef PARAM_MAX +#undef PARAM_REQUEST_ID +#undef PARAM_CONTROL +#undef PARAM_IP_PACKET +#undef PARAM_SRC_MAC_ADDR +#undef PARAM_DST_MAC_ADDR +#undef PARAM_PERIOD +#undef PARAM_PROTO_TYPE + +/** + * wlan_hdd_cfg80211_offloaded_packets() - Wrapper to offload packets + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +static int wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_offloaded_packets(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif + +#ifdef WLAN_NS_OFFLOAD +static const struct nla_policy +ns_offload_set_policy[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG] = {.type = NLA_U8}, +}; + +/** + * __wlan_hdd_cfg80211_set_ns_offload() - enable/disable NS offload + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Return: 0 on success, negative errno on failure + */ +static int +__wlan_hdd_cfg80211_set_ns_offload(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int status; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX + 1]; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + hdd_enter_dev(wdev->netdev); + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return status; + + if (!ucfg_pmo_is_ns_offloaded(hdd_ctx->psoc)) { + hdd_err("ND Offload not supported"); + return -EINVAL; + } + + if (!ucfg_pmo_is_active_mode_offloaded(hdd_ctx->psoc)) { + hdd_warn("Active mode offload is disabled"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX, + (struct nlattr *)data, data_len, + ns_offload_set_policy)) { + hdd_err("nla_parse failed"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]) { + hdd_err("ND Offload flag attribute not present"); + return -EINVAL; + } + + hdd_ctx->ns_offload_enable = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]); + + if (QDF_IBSS_MODE == adapter->device_mode) { + hdd_debug("NS Offload is not supported in IBSS mode"); + return -EINVAL; + } + + /* update ns offload in case it is already enabled/disabled */ + if (hdd_ctx->ns_offload_enable) + hdd_enable_ns_offload(adapter, pmo_ns_offload_dynamic_update); + else + hdd_disable_ns_offload(adapter, pmo_ns_offload_dynamic_update); + + return 0; +} + +/** + * wlan_hdd_cfg80211_set_ns_offload() - enable/disable NS offload + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Return: Return the Success or Failure code. + */ +static int wlan_hdd_cfg80211_set_ns_offload(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_ns_offload(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif /* WLAN_NS_OFFLOAD */ + +/** + * struct weighed_pcl: Preferred channel info + * @freq: Channel frequency + * @weight: Weightage of the channel + * @flag: Validity of the channel in p2p negotiation + */ +struct weighed_pcl { + u32 freq; + u32 weight; + u32 flag; +}; + +static const struct nla_policy get_preferred_freq_list_policy + [QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE] = { + .type = NLA_U32}, +}; + +static uint32_t wlan_hdd_populate_weigh_pcl( + struct policy_mgr_pcl_chan_weights * + chan_weights, + struct weighed_pcl *w_pcl, + enum policy_mgr_con_mode intf_mode) +{ + int i, j; + uint32_t chan_idx = 0; + uint32_t set = 0; + uint32_t pcl_len = chan_weights->pcl_len; + uint32_t valid_weight; + + /* convert channel number to frequency */ + for (i = 0; i < chan_weights->pcl_len; i++) { + w_pcl[i].freq = chan_weights->pcl_list[i]; + w_pcl[i].weight = chan_weights->weight_list[i]; + + if (intf_mode == PM_SAP_MODE || intf_mode == PM_P2P_GO_MODE) + w_pcl[i].flag = set | PCL_CHANNEL_SUPPORT_GO; + else + w_pcl[i].flag = set | PCL_CHANNEL_SUPPORT_CLI; + } + chan_idx = pcl_len; + if (pcl_len && chan_weights->weight_list[pcl_len - 1] > + PCL_GROUPS_WEIGHT_DIFFERENCE) + /* Set non-pcl channels weight 20 point less than the last PCL entry */ + valid_weight = chan_weights->weight_list[pcl_len - 1] - + PCL_GROUPS_WEIGHT_DIFFERENCE; + else + valid_weight = 1; + + /* Include rest of the valid channels */ + for (i = 0; i < chan_weights->saved_num_chan; i++) { + for (j = 0; j < chan_weights->pcl_len; j++) { + if (chan_weights->saved_chan_list[i] == + chan_weights->pcl_list[j]) + break; + } + if (j == chan_weights->pcl_len) { + w_pcl[chan_idx].freq = + chan_weights->saved_chan_list[i]; + + if (!chan_weights->weighed_valid_list[i]) { + w_pcl[chan_idx].flag = + set | PCL_CHANNEL_EXCLUDE_IN_GO_NEG; + w_pcl[chan_idx].weight = 0; + } else { + if (intf_mode == PM_SAP_MODE || + intf_mode == PM_P2P_GO_MODE) + w_pcl[chan_idx].flag = + set | PCL_CHANNEL_SUPPORT_GO; + else + w_pcl[chan_idx].flag = + set | PCL_CHANNEL_SUPPORT_CLI; + w_pcl[chan_idx].weight = valid_weight; + } + chan_idx++; + } + } + return chan_idx; +} + +/** __wlan_hdd_cfg80211_get_preferred_freq_list() - get preferred frequency list + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function return the preferred frequency list generated by the policy + * manager. + * + * Return: success or failure code + */ +static int __wlan_hdd_cfg80211_get_preferred_freq_list(struct wiphy *wiphy, + struct wireless_dev + *wdev, const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int i, ret = 0; + QDF_STATUS status; + uint32_t pcl_len = 0; + uint32_t pcl_len_legacy = 0; + uint32_t freq_list[NUM_CHANNELS]; + uint32_t freq_list_legacy[NUM_CHANNELS]; + enum policy_mgr_con_mode intf_mode; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX + 1]; + struct sk_buff *reply_skb; + struct weighed_pcl *w_pcl; + struct nlattr *nla_attr, *channel; + struct policy_mgr_pcl_chan_weights *chan_weights; + + hdd_enter_dev(wdev->netdev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return -EINVAL; + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX, + data, data_len, + get_preferred_freq_list_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE]) { + hdd_err("attr interface type failed"); + return -EINVAL; + } + + intf_mode = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE]); + + if (intf_mode < PM_STA_MODE || intf_mode >= PM_MAX_NUM_OF_MODE) { + hdd_err("Invalid interface type"); + return -EINVAL; + } + + hdd_debug("Userspace requested pref freq list"); + + chan_weights = + qdf_mem_malloc(sizeof(struct policy_mgr_pcl_chan_weights)); + if (!chan_weights) + return -ENOMEM; + + status = policy_mgr_get_pcl( + hdd_ctx->psoc, intf_mode, chan_weights->pcl_list, + &chan_weights->pcl_len, chan_weights->weight_list, + QDF_ARRAY_SIZE(chan_weights->weight_list)); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Get pcl failed"); + qdf_mem_free(chan_weights); + return -EINVAL; + } + /* + * save the pcl in freq_list_legacy to be sent up with + * QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST. + * freq_list will carry the extended pcl in + * QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL. + */ + pcl_len_legacy = chan_weights->pcl_len; + for (i = 0; i < pcl_len_legacy; i++) + freq_list_legacy[i] = chan_weights->pcl_list[i]; + chan_weights->saved_num_chan = NUM_CHANNELS; + sme_get_valid_channels(chan_weights->saved_chan_list, + &chan_weights->saved_num_chan); + policy_mgr_get_valid_chan_weights(hdd_ctx->psoc, chan_weights); + w_pcl = qdf_mem_malloc(sizeof(struct weighed_pcl) * NUM_CHANNELS); + if (!w_pcl) { + qdf_mem_free(chan_weights); + return -ENOMEM; + } + pcl_len = wlan_hdd_populate_weigh_pcl(chan_weights, w_pcl, intf_mode); + qdf_mem_free(chan_weights); + + for (i = 0; i < pcl_len; i++) + freq_list[i] = w_pcl[i].freq; + + /* send the freq_list back to supplicant */ + reply_skb = cfg80211_vendor_cmd_alloc_reply_skb( + wiphy, + (sizeof(u32) + NLA_HDRLEN) + + (sizeof(u32) * pcl_len_legacy + NLA_HDRLEN) + + NLA_HDRLEN + + (NLA_HDRLEN * 4 + sizeof(u32) * 3) * pcl_len + + NLMSG_HDRLEN); + + if (!reply_skb) { + hdd_err("Allocate reply_skb failed"); + qdf_mem_free(w_pcl); + return -EINVAL; + } + + if (nla_put_u32(reply_skb, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, + intf_mode) || + nla_put(reply_skb, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST, + sizeof(uint32_t) * pcl_len_legacy, + freq_list_legacy)) { + hdd_err("nla put fail"); + kfree_skb(reply_skb); + qdf_mem_free(w_pcl); + return -EINVAL; + } + + i = QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL; + nla_attr = nla_nest_start(reply_skb, i); + + if (!nla_attr) { + hdd_err("nla nest start fail"); + kfree_skb(reply_skb); + qdf_mem_free(w_pcl); + return -EINVAL; + } + + for (i = 0; i < pcl_len; i++) { + channel = nla_nest_start(reply_skb, i); + if (!channel) { + hdd_err("updating pcl list failed"); + kfree_skb(reply_skb); + qdf_mem_free(w_pcl); + return -EINVAL; + } + if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_PCL_FREQ, + w_pcl[i].freq) || + nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT, + w_pcl[i].weight) || + nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_PCL_FLAG, + w_pcl[i].flag)) { + hdd_err("nla put fail"); + kfree_skb(reply_skb); + qdf_mem_free(w_pcl); + return -EINVAL; + } + nla_nest_end(reply_skb, channel); + } + nla_nest_end(reply_skb, nla_attr); + qdf_mem_free(w_pcl); + + return cfg80211_vendor_cmd_reply(reply_skb); +} + +/** wlan_hdd_cfg80211_get_preferred_freq_list () - get preferred frequency list + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function return the preferred frequency list generated by the policy + * manager. + * + * Return: success or failure code + */ +static int wlan_hdd_cfg80211_get_preferred_freq_list(struct wiphy *wiphy, + struct wireless_dev + *wdev, const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_preferred_freq_list(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +static const struct nla_policy set_probable_oper_channel_policy + [QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ] = { + .type = NLA_U32}, +}; + +/** + * __wlan_hdd_cfg80211_set_probable_oper_channel () - set probable channel + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_set_probable_oper_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *ndev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int ret = 0; + enum policy_mgr_con_mode intf_mode; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX + 1]; + uint32_t ch_freq; + + hdd_enter_dev(ndev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX, + data, data_len, + set_probable_oper_channel_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE]) { + hdd_err("attr interface type failed"); + return -EINVAL; + } + + intf_mode = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE]); + + if (intf_mode < PM_STA_MODE || intf_mode >= PM_MAX_NUM_OF_MODE) { + hdd_err("Invalid interface type"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ]) { + hdd_err("attr probable freq failed"); + return -EINVAL; + } + + ch_freq = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ]); + /* check pcl table */ + if (!policy_mgr_allow_concurrency(hdd_ctx->psoc, intf_mode, + ch_freq, HW_MODE_20_MHZ)) { + hdd_err("Set channel hint failed due to concurrency check"); + return -EINVAL; + } + + if (0 != wlan_hdd_check_remain_on_channel(adapter)) + hdd_warn("Remain On Channel Pending"); + + if (wlan_hdd_change_hw_mode_for_given_chnl(adapter, ch_freq, + POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN)) { + hdd_err("Failed to change hw mode"); + return -EINVAL; + } + + return 0; +} + +/** + * wlan_hdd_cfg80211_set_probable_oper_channel () - set probable channel + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int wlan_hdd_cfg80211_set_probable_oper_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_probable_oper_channel(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static const struct +nla_policy +qca_wlan_vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_MAX+1] = { + [QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE }, +}; + +/** + * __wlan_hdd_cfg80211_get_link_properties() - Get link properties + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function is used to get link properties like nss, rate flags and + * operating frequency for the active connection with the given peer. + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_station_ctx *hdd_sta_ctx; + struct hdd_station_info *sta_info; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX+1]; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + struct sk_buff *reply_skb; + uint32_t rate_flags = 0; + uint8_t nss; + uint8_t final_rate_flags = 0; + uint32_t freq; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (0 != wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, + data_len, qca_wlan_vendor_attr_policy)) { + hdd_err("Invalid attribute"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) { + hdd_err("Attribute peerMac not provided for mode=%d", + adapter->device_mode); + return -EINVAL; + } + + if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) < QDF_MAC_ADDR_SIZE) { + hdd_err("Attribute peerMac is invalid for mode=%d", + adapter->device_mode); + return -EINVAL; + } + + qdf_mem_copy(peer_mac, nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]), + QDF_MAC_ADDR_SIZE); + hdd_debug("peerMac="QDF_MAC_ADDR_FMT" for device_mode:%d", + QDF_MAC_ADDR_REF(peer_mac), adapter->device_mode); + + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if ((hdd_sta_ctx->conn_info.conn_state != + eConnectionState_Associated) || + qdf_mem_cmp(hdd_sta_ctx->conn_info.bssid.bytes, + peer_mac, QDF_MAC_ADDR_SIZE)) { + hdd_err("Not Associated to mac "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_mac)); + return -EINVAL; + } + + nss = hdd_sta_ctx->conn_info.nss; + freq = hdd_sta_ctx->conn_info.chan_freq; + rate_flags = hdd_sta_ctx->conn_info.rate_flags; + } else if (adapter->device_mode == QDF_P2P_GO_MODE || + adapter->device_mode == QDF_SAP_MODE) { + + if (QDF_IS_ADDR_BROADCAST(peer_mac)) { + hdd_err("Ignore bcast/self sta"); + return -EINVAL; + } + + sta_info = hdd_get_sta_info_by_mac( + &adapter->sta_info_list, peer_mac, + STA_INFO_CFG80211_GET_LINK_PROPERTIES); + + if (!sta_info) { + hdd_err("No active peer with mac = " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_mac)); + return -EINVAL; + } + + nss = sta_info->nss; + freq = (WLAN_HDD_GET_AP_CTX_PTR(adapter))->operating_chan_freq; + rate_flags = sta_info->rate_flags; + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_CFG80211_GET_LINK_PROPERTIES); + } else { + hdd_err("Not Associated! with mac "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_mac)); + return -EINVAL; + } + + if (!(rate_flags & TX_RATE_LEGACY)) { + if (rate_flags & TX_RATE_VHT80) { + final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS) + final_rate_flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; +#endif + } else if (rate_flags & TX_RATE_VHT40) { + final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS) + final_rate_flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; +#endif + } else if (rate_flags & TX_RATE_VHT20) { + final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS; + } else if (rate_flags & + (TX_RATE_HT20 | TX_RATE_HT40)) { + final_rate_flags |= RATE_INFO_FLAGS_MCS; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS) + if (rate_flags & TX_RATE_HT40) + final_rate_flags |= + RATE_INFO_FLAGS_40_MHZ_WIDTH; +#endif + } + + if (rate_flags & TX_RATE_SGI) { + if (!(final_rate_flags & RATE_INFO_FLAGS_VHT_MCS)) + final_rate_flags |= RATE_INFO_FLAGS_MCS; + final_rate_flags |= RATE_INFO_FLAGS_SHORT_GI; + } + } + + reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, + sizeof(u8) + sizeof(u8) + sizeof(u32) + NLMSG_HDRLEN); + + if (!reply_skb) { + hdd_err("getLinkProperties: skb alloc failed"); + return -EINVAL; + } + + if (nla_put_u8(reply_skb, + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_NSS, + nss) || + nla_put_u8(reply_skb, + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_RATE_FLAGS, + final_rate_flags) || + nla_put_u32(reply_skb, + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_FREQ, + freq)) { + hdd_err("nla_put failed"); + kfree_skb(reply_skb); + return -EINVAL; + } + + return cfg80211_vendor_cmd_reply(reply_skb); +} + +/** + * wlan_hdd_cfg80211_get_link_properties() - Wrapper function to get link + * properties. + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function is used to get link properties like nss, rate flags and + * operating frequency for the active connection with the given peer. + * + * Return: 0 on success and errno on failure + */ +static int wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_link_properties(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static const struct nla_policy +wlan_hdd_sap_config_policy[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST] = { + .type = NLA_NESTED}, +}; + +static const struct nla_policy +wlan_hdd_set_acs_dfs_config_policy[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT] = {.type = NLA_U32}, +}; + +/** + * __wlan_hdd_cfg80211_acs_dfs_mode() - set ACS DFS mode and channel + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * This function parses the incoming NL vendor command data attributes and + * updates the SAP context about channel_hint and DFS mode. + * If channel_hint is set, SAP will choose that channel + * as operating channel. + * + * If DFS mode is enabled, driver will include DFS channels + * in ACS else driver will skip DFS channels. + * + * Return: 0 on success, negative errno on failure + */ +static int +__wlan_hdd_cfg80211_acs_dfs_mode(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX + 1]; + int ret; + struct acs_dfs_policy *acs_policy; + int mode = DFS_MODE_NONE; + uint32_t freq_hint = 0; + + hdd_enter_dev(wdev->netdev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX, + data, data_len, + wlan_hdd_set_acs_dfs_config_policy)) { + hdd_err("invalid attr"); + return -EINVAL; + } + + acs_policy = &hdd_ctx->acs_policy; + /* + * SCM sends this attribute to restrict SAP from choosing + * DFS channels from ACS. + */ + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE]) + mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE]); + + if (!IS_DFS_MODE_VALID(mode)) { + hdd_err("attr acs dfs mode is not valid"); + return -EINVAL; + } + acs_policy->acs_dfs_mode = mode; + + /* + * SCM sends this attribute to provide an active channel, + * to skip redundant ACS between drivers, and save driver start up time + */ + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT]) { + freq_hint = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT]); + } else if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT]) { + uint32_t channel_hint = nla_get_u8( + tb[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT]); + + freq_hint = wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, + channel_hint); + } + + if (freq_hint && !WLAN_REG_IS_24GHZ_CH_FREQ(freq_hint) && + !WLAN_REG_IS_5GHZ_CH_FREQ(freq_hint) && + !WLAN_REG_IS_6GHZ_CHAN_FREQ(freq_hint)) { + hdd_err("acs channel frequency is not valid"); + return -EINVAL; + } + + acs_policy->acs_chan_freq = freq_hint; + + return 0; +} + +/** + * wlan_hdd_cfg80211_acs_dfs_mode() - Wrapper to set ACS DFS mode + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * This function parses the incoming NL vendor command data attributes and + * updates the SAP context about channel_hint and DFS mode. + * + * Return: 0 on success; errno on failure + */ +static int wlan_hdd_cfg80211_acs_dfs_mode(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_acs_dfs_mode(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * wlan_hdd_get_sta_roam_dfs_mode() - get sta roam dfs mode policy + * @mode : cfg80211 dfs mode + * + * Return: return csr sta roam dfs mode else return NONE + */ +static enum sta_roam_policy_dfs_mode wlan_hdd_get_sta_roam_dfs_mode( + enum dfs_mode mode) +{ + switch (mode) { + case DFS_MODE_ENABLE: + return CSR_STA_ROAM_POLICY_DFS_ENABLED; + case DFS_MODE_DISABLE: + return CSR_STA_ROAM_POLICY_DFS_DISABLED; + case DFS_MODE_DEPRIORITIZE: + return CSR_STA_ROAM_POLICY_DFS_DEPRIORITIZE; + default: + hdd_err("STA Roam policy dfs mode is NONE"); + return CSR_STA_ROAM_POLICY_NONE; + } +} + +/* + * hdd_get_sap_operating_band: Get current operating channel + * for sap. + * @hdd_ctx: hdd context + * + * Return : Corresponding band for SAP operating channel + */ +uint8_t hdd_get_sap_operating_band(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + uint32_t operating_chan_freq; + uint8_t sap_operating_band = 0; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_SAP_OPERATING_BAND; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->device_mode != QDF_SAP_MODE) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + operating_chan_freq = adapter->session.ap.operating_chan_freq; + if (WLAN_REG_IS_24GHZ_CH_FREQ(operating_chan_freq)) + sap_operating_band = BAND_2G; + else if (WLAN_REG_IS_5GHZ_CH_FREQ(operating_chan_freq) || + WLAN_REG_IS_6GHZ_CHAN_FREQ(operating_chan_freq)) + sap_operating_band = BAND_5G; + else + sap_operating_band = BAND_ALL; + + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return sap_operating_band; +} + +static const struct nla_policy +wlan_hdd_set_sta_roam_config_policy[ +QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE] = {.type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL] = {.type = NLA_U8 }, +}; + +/** + * __wlan_hdd_cfg80211_sta_roam_policy() - Set params to restrict scan channels + * for station connection or roaming. + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * __wlan_hdd_cfg80211_sta_roam_policy will decide if DFS channels or unsafe + * channels needs to be skipped in scanning or not. + * If dfs_mode is disabled, driver will not scan DFS channels. + * If skip_unsafe_channels is set, driver will skip unsafe channels + * in Scanning. + * + * Return: 0 on success, negative errno on failure + */ +static int +__wlan_hdd_cfg80211_sta_roam_policy(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[ + QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX + 1]; + int ret; + enum sta_roam_policy_dfs_mode sta_roam_dfs_mode; + enum dfs_mode mode = DFS_MODE_NONE; + bool skip_unsafe_channels = false; + QDF_STATUS status; + uint8_t sap_operating_band; + mac_handle_t mac_handle; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX, + data, data_len, + wlan_hdd_set_sta_roam_config_policy)) { + hdd_err("invalid attr"); + return -EINVAL; + } + if (tb[QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE]) + mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE]); + if (!IS_DFS_MODE_VALID(mode)) { + hdd_err("attr sta roam dfs mode policy is not valid"); + return -EINVAL; + } + + sta_roam_dfs_mode = wlan_hdd_get_sta_roam_dfs_mode(mode); + + if (tb[QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL]) + skip_unsafe_channels = nla_get_u8( + tb[QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL]); + sap_operating_band = hdd_get_sap_operating_band(hdd_ctx); + mac_handle = hdd_ctx->mac_handle; + status = sme_update_sta_roam_policy(mac_handle, sta_roam_dfs_mode, + skip_unsafe_channels, + adapter->vdev_id, + sap_operating_band); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_update_sta_roam_policy (err=%d)", status); + return -EINVAL; + } + return 0; +} + +/** + * wlan_hdd_cfg80211_sta_roam_policy() - Wrapper to restrict scan channels, + * connection and roaming for station. + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * __wlan_hdd_cfg80211_sta_roam_policy will decide if DFS channels or unsafe + * channels needs to be skipped in scanning or not. + * If dfs_mode is disabled, driver will not scan DFS channels. + * If skip_unsafe_channels is set, driver will skip unsafe channels + * in Scanning. + * Return: 0 on success; errno on failure + */ +static int wlan_hdd_cfg80211_sta_roam_policy(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_sta_roam_policy(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef FEATURE_WLAN_CH_AVOID + +static int hdd_validate_avoid_freq_chanlist( + struct hdd_context *hdd_ctx, + struct ch_avoid_ind_type *channel_list) +{ + unsigned int range_idx, ch_idx; + unsigned int unsafe_channel_index, unsafe_channel_count = 0; + bool ch_found = false; + uint32_t ch_idx_freq; + + unsafe_channel_count = QDF_MIN((uint16_t)hdd_ctx->unsafe_channel_count, + (uint16_t)NUM_CHANNELS); + + for (range_idx = 0; range_idx < channel_list->ch_avoid_range_cnt; + range_idx++) { + if ((channel_list->avoid_freq_range[range_idx].start_freq < + CDS_24_GHZ_CHANNEL_1) || + (channel_list->avoid_freq_range[range_idx].end_freq > + CDS_5_GHZ_CHANNEL_165) || + (channel_list->avoid_freq_range[range_idx].start_freq > + channel_list->avoid_freq_range[range_idx].end_freq)) + continue; + + for (ch_idx = channel_list-> + avoid_freq_range[range_idx].start_freq; + ch_idx <= channel_list-> + avoid_freq_range[range_idx].end_freq; + ch_idx++) { + if (INVALID_CHANNEL == wlan_reg_get_chan_enum(ch_idx)) + continue; + ch_idx_freq = wlan_reg_chan_to_freq(hdd_ctx->pdev, + ch_idx); + for (unsafe_channel_index = 0; + unsafe_channel_index < unsafe_channel_count; + unsafe_channel_index++) { + if (ch_idx_freq == + hdd_ctx->unsafe_channel_list[ + unsafe_channel_index]) { + hdd_info("Duplicate channel freq %d", + ch_idx_freq); + ch_found = true; + break; + } + } + if (!ch_found) { + hdd_ctx->unsafe_channel_list[ + unsafe_channel_count++] = (uint16_t)ch_idx_freq; + } + ch_found = false; + } + } + return unsafe_channel_count; +} + +/** + * __wlan_hdd_cfg80211_avoid_freq() - ask driver to restart SAP if SAP + * is on unsafe channel. + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * wlan_hdd_cfg80211_avoid_freq do restart the sap if sap is already + * on any of unsafe channels. + * If sap is on any of unsafe channel, hdd_unsafe_channel_restart_sap + * will send WLAN_SVC_LTE_COEX_IND indication to userspace to restart. + * + * Return: 0 on success; errno on failure + */ +static int +__wlan_hdd_cfg80211_avoid_freq(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int ret; + qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + uint16_t *local_unsafe_list; + uint16_t unsafe_channel_count; + uint16_t unsafe_channel_index, local_unsafe_list_count; + struct ch_avoid_ind_type *channel_list; + enum QDF_GLOBAL_MODE curr_mode; + uint8_t num_args = 0; + bool user_set_avoid_channel = true; + + hdd_enter_dev(wdev->netdev); + + if (!qdf_ctx) { + hdd_err("qdf_ctx is NULL"); + return -EINVAL; + } + curr_mode = hdd_get_conparam(); + if (QDF_GLOBAL_FTM_MODE == curr_mode || + QDF_GLOBAL_MONITOR_MODE == curr_mode) { + hdd_err("Command not allowed in FTM/MONITOR mode"); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + if (!data && data_len == 0) { + hdd_debug("Userspace doesn't set avoid frequency channel list"); + user_set_avoid_channel = false; + goto process_unsafe_channel; + } + if (!data || data_len < (sizeof(channel_list->ch_avoid_range_cnt) + + sizeof(struct ch_avoid_freq_type))) { + hdd_err("Avoid frequency channel list empty"); + return -EINVAL; + } + num_args = (data_len - sizeof(channel_list->ch_avoid_range_cnt)) / + sizeof(channel_list->avoid_freq_range[0].start_freq); + + if (num_args < 2 || num_args > CH_AVOID_MAX_RANGE * 2 || + num_args % 2 != 0) { + hdd_err("Invalid avoid frequency channel list"); + return -EINVAL; + } + + channel_list = (struct ch_avoid_ind_type *)data; + if (channel_list->ch_avoid_range_cnt == 0 || + channel_list->ch_avoid_range_cnt > CH_AVOID_MAX_RANGE || + 2 * channel_list->ch_avoid_range_cnt != num_args) { + hdd_err("Invalid frequency range count %d", + channel_list->ch_avoid_range_cnt); + return -EINVAL; + } + +process_unsafe_channel: + ret = hdd_clone_local_unsafe_chan(hdd_ctx, + &local_unsafe_list, + &local_unsafe_list_count); + if (0 != ret) { + hdd_err("failed to clone the cur unsafe chan list"); + return ret; + } + + pld_get_wlan_unsafe_channel(qdf_ctx->dev, hdd_ctx->unsafe_channel_list, + &(hdd_ctx->unsafe_channel_count), + sizeof(hdd_ctx->unsafe_channel_list)); + if (user_set_avoid_channel) { + hdd_ctx->unsafe_channel_count = + hdd_validate_avoid_freq_chanlist( + hdd_ctx, + channel_list); + unsafe_channel_count = hdd_ctx->unsafe_channel_count; + + pld_set_wlan_unsafe_channel(qdf_ctx->dev, + hdd_ctx->unsafe_channel_list, + hdd_ctx->unsafe_channel_count); + } else { + unsafe_channel_count = QDF_MIN( + (uint16_t)hdd_ctx->unsafe_channel_count, + (uint16_t)NUM_CHANNELS); + } + + for (unsafe_channel_index = 0; + unsafe_channel_index < unsafe_channel_count; + unsafe_channel_index++) { + hdd_debug("Channel frequency %d is not safe", + hdd_ctx->unsafe_channel_list[unsafe_channel_index]); + } + if (hdd_local_unsafe_channel_updated(hdd_ctx, local_unsafe_list, + local_unsafe_list_count)) + hdd_unsafe_channel_restart_sap(hdd_ctx); + qdf_mem_free(local_unsafe_list); + + return 0; +} + +/** + * wlan_hdd_cfg80211_avoid_freq() - ask driver to restart SAP if SAP + * is on unsafe channel. + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * wlan_hdd_cfg80211_avoid_freq do restart the sap if sap is already + * on any of unsafe channels. + * If sap is on any of unsafe channel, hdd_unsafe_channel_restart_sap + * will send WLAN_SVC_LTE_COEX_IND indication to userspace to restart. + * + * Return: 0 on success; errno on failure + */ +static int wlan_hdd_cfg80211_avoid_freq(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_avoid_freq(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +#endif +/** + * __wlan_hdd_cfg80211_sap_configuration_set() - ask driver to restart SAP if + * SAP is on unsafe channel. + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * __wlan_hdd_cfg80211_sap_configuration_set function set SAP params to + * driver. + * QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHAN will set sap config channel and + * will initiate restart of sap. + * + * Return: 0 on success; errno on failure + */ +static int +__wlan_hdd_cfg80211_sap_configuration_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *ndev = wdev->netdev; + struct hdd_adapter *hostapd_adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX + 1]; + struct hdd_ap_ctx *ap_ctx; + int ret; + uint32_t chan_freq = 0; + bool chan_freq_present = false; + QDF_STATUS status; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return -EINVAL; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX, + data, data_len, + wlan_hdd_sap_config_policy)) { + hdd_err("invalid attr"); + return -EINVAL; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY]) { + chan_freq = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY]); + chan_freq_present = true; + } else if (tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL]) { + uint32_t config_channel = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL]); + + chan_freq = wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, + config_channel); + chan_freq_present = true; + } + + if (chan_freq_present) { + if (!test_bit(SOFTAP_BSS_STARTED, + &hostapd_adapter->event_flags)) { + hdd_err("SAP is not started yet. Restart sap will be invalid"); + return -EINVAL; + } + + if (!WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq) && + !WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq) && + !WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_freq)) { + hdd_err("Channel frequency %u is invalid to restart SAP", + chan_freq); + return -ENOTSUPP; + } + + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(hostapd_adapter); + ap_ctx->sap_config.chan_freq = chan_freq; + ap_ctx->sap_config.ch_params.ch_width = + ap_ctx->sap_config.ch_width_orig; + ap_ctx->bss_stop_reason = BSS_STOP_DUE_TO_VENDOR_CONFIG_CHAN; + + wlan_reg_set_channel_params_for_freq( + hdd_ctx->pdev, chan_freq, + ap_ctx->sap_config.sec_ch_freq, + &ap_ctx->sap_config.ch_params); + + hdd_restart_sap(hostapd_adapter); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST]) { + uint32_t freq_len, i; + uint32_t *freq; + + hdd_debug("setting mandatory freq/chan list"); + + freq_len = nla_len( + tb[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST])/ + sizeof(uint32_t); + + if (freq_len > NUM_CHANNELS) { + hdd_err("insufficient space to hold channels"); + return -ENOMEM; + } + + freq = nla_data( + tb[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST]); + + hdd_debug("freq_len=%d", freq_len); + + for (i = 0; i < freq_len; i++) { + hdd_debug("freq[%d]=%d", i, freq[i]); + } + + status = policy_mgr_set_sap_mandatory_channels( + hdd_ctx->psoc, freq, freq_len); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + } + + return 0; +} + +/** + * wlan_hdd_cfg80211_sap_configuration_set() - sap configuration vendor command + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * __wlan_hdd_cfg80211_sap_configuration_set function set SAP params to + * driver. + * QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHAN will set sap config channel and + * will initiate restart of sap. + * + * Return: 0 on success; errno on failure + */ +static int wlan_hdd_cfg80211_sap_configuration_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_sap_configuration_set(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_process_wake_lock_stats() - wrapper function to absract cp_stats + * or legacy get_wake_lock_stats API. + * @hdd_ctx: pointer to hdd_ctx + * + * Return: 0 on success; error number otherwise. + */ +static int wlan_hdd_process_wake_lock_stats(struct hdd_context *hdd_ctx) +{ + return wlan_cfg80211_mc_cp_stats_get_wakelock_stats(hdd_ctx->psoc, + hdd_ctx->wiphy); +} + +/** + * __wlan_hdd_cfg80211_get_wakelock_stats() - gets wake lock stats + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * This function parses the incoming NL vendor command data attributes and + * invokes the SME Api and blocks on a completion variable. + * WMA copies required data and invokes callback + * wlan_hdd_cfg80211_wakelock_stats_rsp_callback to send wake lock stats. + * + * Return: 0 on success; error number otherwise. + */ +static int __wlan_hdd_cfg80211_get_wakelock_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return -EINVAL; + + ret = wlan_hdd_process_wake_lock_stats(hdd_ctx); + hdd_exit(); + return ret; +} + +/** + * wlan_hdd_cfg80211_get_wakelock_stats() - gets wake lock stats + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * This function parses the incoming NL vendor command data attributes and + * invokes the SME Api and blocks on a completion variable. + * WMA copies required data and invokes callback + * wlan_hdd_cfg80211_wakelock_stats_rsp_callback to send wake lock stats. + * + * Return: 0 on success; error number otherwise. + */ +static int wlan_hdd_cfg80211_get_wakelock_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_wakelock_stats(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_get_bus_size() - Get WMI Bus size + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * This function reads wmi max bus size and fill in the skb with + * NL attributes and send up the NL event. + * Return: 0 on success; errno on failure + */ +static int +__wlan_hdd_cfg80211_get_bus_size(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int ret_val; + struct sk_buff *skb; + uint32_t nl_buf_len; + + hdd_enter(); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + hdd_debug("WMI Max Bus size: %d", hdd_ctx->wmi_max_len); + + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += (sizeof(hdd_ctx->wmi_max_len) + NLA_HDRLEN); + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + if (nla_put_u16(skb, QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE, + hdd_ctx->wmi_max_len)) { + hdd_err("nla put failure"); + goto nla_put_failure; + } + + cfg80211_vendor_cmd_reply(skb); + + hdd_exit(); + + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_get_bus_size() - SSR Wrapper to Get Bus size + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +static int wlan_hdd_cfg80211_get_bus_size(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_bus_size(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +const struct nla_policy setband_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SETBAND_MASK] = {.type = NLA_U32}, +}; + +static uint32_t +wlan_vendor_bitmap_to_reg_wifi_band_bitmap(uint32_t vendor_bitmap) +{ + uint32_t reg_bitmap = 0; + + if (vendor_bitmap & QCA_SETBAND_2G) + reg_bitmap |= BIT(REG_BAND_2G); + if (vendor_bitmap & QCA_SETBAND_5G) + reg_bitmap |= BIT(REG_BAND_5G); + if (vendor_bitmap & QCA_SETBAND_6G) + reg_bitmap |= BIT(REG_BAND_6G); + + return reg_bitmap; +} + +/** + *__wlan_hdd_cfg80211_setband() - set band + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_setband(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + int ret; + uint32_t reg_wifi_band_bitmap = 0, band_val, band_mask; + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, + data, data_len, setband_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SETBAND_MASK]) { + band_mask = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SETBAND_MASK]); + reg_wifi_band_bitmap = + wlan_vendor_bitmap_to_reg_wifi_band_bitmap(band_mask); + } else if (tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]) { + band_val = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]); + reg_wifi_band_bitmap = + hdd_reg_legacy_setband_to_reg_wifi_band_bitmap( + band_val); + } + + if (!reg_wifi_band_bitmap) { + hdd_err("attr SETBAND_VALUE failed"); + return -EINVAL; + } + + ret = hdd_reg_set_band(dev, reg_wifi_band_bitmap); + + hdd_exit(); + return ret; +} + +/** + *wlan_hdd_validate_acs_channel() - validate channel frequency provided by ACS + * @adapter: hdd adapter + * @chan_freq: channel frequency in MHz + * + * return: QDF status based on success or failure + */ +static QDF_STATUS wlan_hdd_validate_acs_channel(struct hdd_adapter *adapter, + uint32_t chan_freq, int chan_bw) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t channel; + + if (QDF_STATUS_SUCCESS != + wlan_hdd_validate_operation_channel(adapter, chan_freq)) + return QDF_STATUS_E_FAILURE; + + channel = (uint8_t)wlan_reg_freq_to_chan(hdd_ctx->pdev, chan_freq); + if ((wlansap_is_channel_in_nol_list(WLAN_HDD_GET_SAP_CTX_PTR(adapter), + channel, + PHY_SINGLE_CHANNEL_CENTERED))) { + hdd_info("channel %d is in nol", channel); + return -EINVAL; + } + + if ((wlansap_is_channel_leaking_in_nol( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + channel, chan_bw))) { + hdd_info("channel %d is leaking in nol", channel); + return -EINVAL; + } + + return 0; + +} + +static void hdd_update_acs_sap_config(struct hdd_context *hdd_ctx, + struct sap_config *sap_config, + struct hdd_vendor_chan_info *channel_list) +{ + uint8_t ch_width; + QDF_STATUS status; + uint32_t channel_bonding_mode; + + sap_config->chan_freq = channel_list->pri_chan_freq; + + sap_config->ch_params.center_freq_seg0 = + wlan_reg_freq_to_chan(hdd_ctx->pdev, + channel_list->vht_seg0_center_chan_freq); + sap_config->ch_params.center_freq_seg1 = + wlan_reg_freq_to_chan(hdd_ctx->pdev, + channel_list->vht_seg1_center_chan_freq); + + sap_config->ch_params.sec_ch_offset = + wlan_reg_freq_to_chan(hdd_ctx->pdev, + channel_list->ht_sec_chan_freq); + + sap_config->ch_params.ch_width = channel_list->chan_width; + if (!WLAN_REG_IS_24GHZ_CH_FREQ(sap_config->chan_freq)) { + status = + ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc, + &ch_width); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to set channel_width"); + sap_config->ch_width_orig = ch_width; + } else { + ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, + &channel_bonding_mode); + sap_config->ch_width_orig = channel_bonding_mode ? + eHT_CHANNEL_WIDTH_40MHZ : eHT_CHANNEL_WIDTH_20MHZ; + } + sap_config->acs_cfg.pri_ch_freq = channel_list->pri_chan_freq; + sap_config->acs_cfg.ch_width = channel_list->chan_width; + sap_config->acs_cfg.vht_seg0_center_ch_freq = + channel_list->vht_seg0_center_chan_freq; + sap_config->acs_cfg.vht_seg1_center_ch_freq = + channel_list->vht_seg1_center_chan_freq; + sap_config->acs_cfg.ht_sec_ch_freq = + channel_list->ht_sec_chan_freq; +} + +static int hdd_update_acs_channel(struct hdd_adapter *adapter, uint8_t reason, + uint8_t channel_cnt, + struct hdd_vendor_chan_info *channel_list) +{ + struct sap_config *sap_config; + struct hdd_ap_ctx *hdd_ap_ctx; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status = QDF_STATUS_SUCCESS; + mac_handle_t mac_handle; + uint32_t ch; + + if (!channel_list) { + hdd_err("channel_list is NULL"); + return -EINVAL; + } + + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + sap_config = &adapter->session.ap.sap_config; + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&adapter->session. + ap.vendor_acs_timer)) { + qdf_mc_timer_stop(&adapter->session.ap.vendor_acs_timer); + } + + if (channel_list->pri_chan_freq == 0) { + /* Check mode, set default channel */ + channel_list->pri_chan_freq = 2437; + /* + * sap_select_default_oper_chan(mac_handle, + * sap_config->acs_cfg.hw_mode); + */ + } + + mac_handle = hdd_ctx->mac_handle; + switch (reason) { + /* SAP init case */ + case QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT: + hdd_update_acs_sap_config(hdd_ctx, sap_config, channel_list); + /* Update Hostapd */ + wlan_hdd_cfg80211_acs_ch_select_evt(adapter); + break; + + /* DFS detected on current channel */ + case QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS: + ch = wlan_reg_freq_to_chan(hdd_ctx->pdev, + channel_list->pri_chan_freq); + + wlan_sap_update_next_channel( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), (uint8_t)ch, + channel_list->chan_width); + status = sme_update_new_channel_event( + mac_handle, + adapter->vdev_id); + break; + + /* LTE coex event on current channel */ + case QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX: + ch = wlan_reg_freq_to_chan(hdd_ctx->pdev, + channel_list->pri_chan_freq); + sap_config->acs_cfg.pri_ch_freq = channel_list->pri_chan_freq; + sap_config->acs_cfg.ch_width = channel_list->chan_width; + hdd_ap_ctx->sap_config.ch_width_orig = + channel_list->chan_width; + wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, adapter->vdev_id, + CSA_REASON_LTE_COEX); + hdd_switch_sap_channel(adapter, (uint8_t)ch, true); + break; + + default: + hdd_info("invalid reason for timer invoke"); + } + hdd_exit(); + return qdf_status_to_os_return(status); +} + +/** + * Define short name for vendor channel set config + */ +#define SET_CHAN_REASON QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON +#define SET_CHAN_CHAN_LIST QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST +#define SET_CHAN_PRIMARY_CHANNEL \ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY +#define SET_CHAN_SECONDARY_CHANNEL \ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY +#define SET_CHAN_SEG0_CENTER_CHANNEL \ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 +#define SET_CHAN_SEG1_CENTER_CHANNEL \ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 +#define SET_CHAN_CHANNEL_WIDTH \ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH + +#define SET_CHAN_FREQ_LIST QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST +#define SET_CHAN_FREQUENCY_PRIMARY \ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY +#define SET_CHAN_FREQUENCY_SECONDARY \ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY +#define SET_CHAN_SEG0_CENTER_FREQUENCY \ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 +#define SET_CHAN_SEG1_CENTER_FREQUENCY \ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 + +#define SET_CHAN_MAX QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX +#define SET_EXT_ACS_BAND QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND + +static const struct nla_policy acs_chan_config_policy[SET_CHAN_MAX + 1] = { + [SET_CHAN_REASON] = {.type = NLA_U8}, + [SET_CHAN_CHAN_LIST] = {.type = NLA_NESTED}, + [SET_CHAN_FREQ_LIST] = {.type = NLA_NESTED}, +}; + +static const struct nla_policy acs_chan_list_policy[SET_CHAN_MAX + 1] = { + [SET_CHAN_PRIMARY_CHANNEL] = {.type = NLA_U8}, + [SET_CHAN_SECONDARY_CHANNEL] = {.type = NLA_U8}, + [SET_CHAN_SEG0_CENTER_CHANNEL] = {.type = NLA_U8}, + [SET_CHAN_SEG1_CENTER_CHANNEL] = {.type = NLA_U8}, + [SET_CHAN_CHANNEL_WIDTH] = {.type = NLA_U8}, + [SET_EXT_ACS_BAND] = {.type = NLA_U8}, + + [SET_CHAN_FREQUENCY_PRIMARY] = {.type = NLA_U32}, + [SET_CHAN_FREQUENCY_SECONDARY] = {.type = NLA_U32}, + [SET_CHAN_SEG0_CENTER_FREQUENCY] = {.type = NLA_U32}, + [SET_CHAN_SEG1_CENTER_FREQUENCY] = {.type = NLA_U32}, +}; + +/** + * hdd_extract_external_acs_frequencies() - API to parse and extract vendor acs + * channel frequency (in MHz) configuration. + * @hdd_ctx: pointer to hdd context + * @list_ptr: pointer to hdd_vendor_chan_info + * @channel_cnt: channel count + * @data: data + * @data_len: data len + * + * Return: 0 on success, negative errno on failure + */ +static int +hdd_extract_external_acs_frequencies(struct hdd_context *hdd_ctx, + struct hdd_vendor_chan_info **list_ptr, + uint8_t *channel_cnt, + const void *data, int data_len) +{ + int rem; + uint32_t i = 0; + struct nlattr *tb[SET_CHAN_MAX + 1]; + struct nlattr *tb2[SET_CHAN_MAX + 1]; + struct nlattr *curr_attr; + struct hdd_vendor_chan_info *channel_list; + + if (wlan_cfg80211_nla_parse(tb, SET_CHAN_MAX, data, data_len, + acs_chan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + nla_for_each_nested(curr_attr, tb[SET_CHAN_FREQ_LIST], rem) + i++; + + if (!i) { + hdd_err_rl("Error: channel count is zero"); + return -EINVAL; + } + + if (i > NUM_CHANNELS) { + hdd_err_rl("Error: Exceeded max channels: %u", NUM_CHANNELS); + return -ENOMEM; + } + + channel_list = qdf_mem_malloc(sizeof(struct hdd_vendor_chan_info) * i); + if (!channel_list) + return -ENOMEM; + + *channel_cnt = (uint8_t)i; + i = 0; + nla_for_each_nested(curr_attr, tb[SET_CHAN_FREQ_LIST], rem) { + if (wlan_cfg80211_nla_parse_nested(tb2, SET_CHAN_MAX, + curr_attr, + acs_chan_list_policy)) { + hdd_err_rl("nla_parse failed"); + qdf_mem_free(channel_list); + *channel_cnt = 0; + return -EINVAL; + } + + if (tb2[SET_EXT_ACS_BAND]) + channel_list[i].band = + nla_get_u8(tb2[SET_EXT_ACS_BAND]); + + if (tb2[SET_CHAN_FREQUENCY_PRIMARY]) + channel_list[i].pri_chan_freq = + nla_get_u32(tb2[SET_CHAN_FREQUENCY_PRIMARY]); + + if (tb2[SET_CHAN_FREQUENCY_SECONDARY]) + channel_list[i].ht_sec_chan_freq = + nla_get_u32(tb2[SET_CHAN_FREQUENCY_SECONDARY]); + + if (tb2[SET_CHAN_SEG0_CENTER_FREQUENCY]) + channel_list[i].vht_seg0_center_chan_freq = + nla_get_u32(tb2[SET_CHAN_SEG0_CENTER_FREQUENCY]); + + if (tb2[SET_CHAN_SEG1_CENTER_FREQUENCY]) + channel_list[i].vht_seg1_center_chan_freq = + nla_get_u32(tb2[SET_CHAN_SEG1_CENTER_FREQUENCY]); + + if (tb2[SET_CHAN_CHANNEL_WIDTH]) + channel_list[i].chan_width = + nla_get_u8(tb2[SET_CHAN_CHANNEL_WIDTH]); + + hdd_debug("index %d, pri_chan_freq %u, ht_sec_chan_freq %u seg0_freq %u seg1_freq %u width %u", + i, channel_list[i].pri_chan_freq, + channel_list[i].ht_sec_chan_freq, + channel_list[i].vht_seg0_center_chan_freq, + channel_list[i].vht_seg1_center_chan_freq, + channel_list[i].chan_width); + i++; + } + *list_ptr = channel_list; + + return 0; +} + +/** + * hdd_extract_external_acs_channels() - API to parse and extract vendor acs + * channel configuration. + * @hdd_ctx: pointer to hdd context + * @list_ptr: pointer to hdd_vendor_chan_info + * @channel_cnt: channel count + * @data: data + * @data_len: data len + * + * Return: 0 on success, negative errno on failure + */ +static int +hdd_extract_external_acs_channels(struct hdd_context *hdd_ctx, + struct hdd_vendor_chan_info **list_ptr, + uint8_t *channel_cnt, + const void *data, int data_len) +{ + int rem; + uint32_t i = 0; + struct nlattr *tb[SET_CHAN_MAX + 1]; + struct nlattr *tb2[SET_CHAN_MAX + 1]; + struct nlattr *curr_attr; + struct hdd_vendor_chan_info *channel_list; + + if (wlan_cfg80211_nla_parse(tb, SET_CHAN_MAX, data, data_len, + acs_chan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + nla_for_each_nested(curr_attr, tb[SET_CHAN_CHAN_LIST], rem) + i++; + + if (!i) { + hdd_err_rl("Error: channel count is zero"); + return -EINVAL; + } + + if (i > NUM_CHANNELS) { + hdd_err_rl("Error: Exceeded max channels: %u", NUM_CHANNELS); + return -ENOMEM; + } + + channel_list = qdf_mem_malloc(sizeof(struct hdd_vendor_chan_info) * i); + if (!channel_list) + return -ENOMEM; + + *channel_cnt = (uint8_t)i; + i = 0; + nla_for_each_nested(curr_attr, tb[SET_CHAN_CHAN_LIST], rem) { + if (wlan_cfg80211_nla_parse_nested(tb2, SET_CHAN_MAX, + curr_attr, + acs_chan_list_policy)) { + hdd_err("nla_parse failed"); + qdf_mem_free(channel_list); + *channel_cnt = 0; + return -EINVAL; + } + + if (tb2[SET_EXT_ACS_BAND]) { + channel_list[i].band = + nla_get_u8(tb2[SET_EXT_ACS_BAND]); + } + + if (tb2[SET_CHAN_PRIMARY_CHANNEL]) { + uint32_t ch = + nla_get_u8(tb2[SET_CHAN_PRIMARY_CHANNEL]); + + channel_list[i].pri_chan_freq = + wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, ch); + } + + if (tb2[SET_CHAN_SECONDARY_CHANNEL]) { + uint32_t ch = + nla_get_u8(tb2[SET_CHAN_SECONDARY_CHANNEL]); + + channel_list[i].ht_sec_chan_freq = + wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, ch); + } + + if (tb2[SET_CHAN_SEG0_CENTER_CHANNEL]) { + uint32_t ch = + nla_get_u8(tb2[SET_CHAN_SEG0_CENTER_CHANNEL]); + + channel_list[i].vht_seg0_center_chan_freq = + wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, ch); + } + + if (tb2[SET_CHAN_SEG1_CENTER_CHANNEL]) { + uint32_t ch = + nla_get_u8(tb2[SET_CHAN_SEG1_CENTER_CHANNEL]); + + channel_list[i].vht_seg1_center_chan_freq = + wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, ch); + } + + if (tb2[SET_CHAN_CHANNEL_WIDTH]) { + channel_list[i].chan_width = + nla_get_u8(tb2[SET_CHAN_CHANNEL_WIDTH]); + } + hdd_debug("index %d, pri_chan_freq %u, ht_sec_chan_freq %u seg0_freq %u seg1_freq %u width %u", + i, channel_list[i].pri_chan_freq, + channel_list[i].ht_sec_chan_freq, + channel_list[i].vht_seg0_center_chan_freq, + channel_list[i].vht_seg1_center_chan_freq, + channel_list[i].chan_width); + i++; + } + *list_ptr = channel_list; + + return 0; +} + +/** + * hdd_parse_vendor_acs_chan_config() - API to parse vendor acs channel config + * @hdd_ctx: pointer to hdd context + * @channel_list: pointer to hdd_vendor_chan_info + * @reason: channel change reason + * @channel_cnt: channel count + * @data: data + * @data_len: data len + * + * Return: 0 on success, negative errno on failure + */ +static int +hdd_parse_vendor_acs_chan_config(struct hdd_context *hdd_ctx, + struct hdd_vendor_chan_info **chan_list_ptr, + uint8_t *reason, uint8_t *channel_cnt, + const void *data, int data_len) +{ + struct nlattr *tb[SET_CHAN_MAX + 1]; + int ret; + + if (wlan_cfg80211_nla_parse(tb, SET_CHAN_MAX, data, data_len, + acs_chan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (tb[SET_CHAN_REASON]) + *reason = nla_get_u8(tb[SET_CHAN_REASON]); + + if (!tb[SET_CHAN_FREQ_LIST] && !tb[SET_CHAN_CHAN_LIST]) { + hdd_err("Both channel list and frequency list are empty"); + return -EINVAL; + } + + if (tb[SET_CHAN_FREQ_LIST]) { + ret = hdd_extract_external_acs_frequencies(hdd_ctx, + chan_list_ptr, + channel_cnt, + data, data_len); + if (ret) { + hdd_err("Failed to extract frequencies"); + return ret; + } + + return 0; + } + + ret = hdd_extract_external_acs_channels(hdd_ctx, chan_list_ptr, + channel_cnt, data, data_len); + if (ret) + hdd_err("Failed to extract channels"); + + return ret; +} + +/** + * Undef short names for vendor set channel configuration + */ +#undef SET_CHAN_REASON +#undef SET_CHAN_CHAN_LIST +#undef SET_CHAN_PRIMARY_CHANNEL +#undef SET_CHAN_SECONDARY_CHANNEL +#undef SET_CHAN_SEG0_CENTER_CHANNEL +#undef SET_CHAN_SEG1_CENTER_CHANNEL + +#undef SET_CHAN_FREQ_LIST +#undef SET_CHAN_FREQUENCY_PRIMARY +#undef SET_CHAN_FREQUENCY_SECONDARY +#undef SET_CHAN_SEG0_CENTER_FREQUENCY +#undef SET_CHAN_SEG1_CENTER_FREQUENCY + +#undef SET_CHAN_CHANNEL_WIDTH +#undef SET_CHAN_MAX + +/** + * __wlan_hdd_cfg80211_update_vendor_channel() - update vendor channel + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_update_vendor_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int ret_val; + QDF_STATUS qdf_status; + uint8_t channel_cnt = 0, reason = -1; + struct hdd_vendor_chan_info *channel_list = NULL; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_vendor_chan_info *channel_list_ptr; + + hdd_enter(); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (test_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags)) + clear_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags); + else { + hdd_err("already timeout happened for acs"); + return -EINVAL; + } + + ret_val = hdd_parse_vendor_acs_chan_config(hdd_ctx, &channel_list, + &reason, &channel_cnt, data, + data_len); + channel_list_ptr = channel_list; + if (ret_val) + return ret_val; + + /* Validate channel to be set */ + while (channel_cnt && channel_list) { + qdf_status = wlan_hdd_validate_acs_channel(adapter, + channel_list->pri_chan_freq, + channel_list->chan_width); + if (qdf_status == QDF_STATUS_SUCCESS) + break; + else if (channel_cnt == 1) { + hdd_err("invalid channel frequ %u received from app", + channel_list->pri_chan_freq); + channel_list->pri_chan_freq = 0; + break; + } + + channel_cnt--; + channel_list++; + } + + if ((channel_cnt <= 0) || !channel_list) { + hdd_err("no available channel/chanlist %d/%pK", channel_cnt, + channel_list); + qdf_mem_free(channel_list_ptr); + return -EINVAL; + } + + hdd_debug("received primary channel freq as %d", + channel_list->pri_chan_freq); + + ret_val = hdd_update_acs_channel(adapter, reason, + channel_cnt, channel_list); + qdf_mem_free(channel_list_ptr); + return ret_val; +} + +/** + * wlan_hdd_cfg80211_update_vendor_channel() - update vendor channel + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Return: 0 on success, negative errno on failure + */ +static int wlan_hdd_cfg80211_update_vendor_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_update_vendor_channel(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_cfg80211_setband() - Wrapper to setband + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +static int wlan_hdd_cfg80211_setband(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_setband(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static uint32_t +wlan_reg_wifi_band_bitmap_to_vendor_bitmap(uint32_t reg_wifi_band_bitmap) +{ + uint32_t vendor_mask = 0; + + if (reg_wifi_band_bitmap & BIT(REG_BAND_2G)) + vendor_mask |= QCA_SETBAND_2G; + if (reg_wifi_band_bitmap & BIT(REG_BAND_5G)) + vendor_mask |= QCA_SETBAND_5G; + if (reg_wifi_band_bitmap & BIT(REG_BAND_6G)) + vendor_mask |= QCA_SETBAND_6G; + + return vendor_mask; +} + +/** + *__wlan_hdd_cfg80211_getband() - get band + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_getband(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct sk_buff *skb; + QDF_STATUS status = QDF_STATUS_SUCCESS; + int ret; + uint32_t reg_wifi_band_bitmap, vendor_band_mask; + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, + sizeof(uint32_t) + + NLA_HDRLEN); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return -ENOMEM; + } + + status = ucfg_reg_get_band(hdd_ctx->pdev, ®_wifi_band_bitmap); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("failed to get band"); + goto failure; + } + + vendor_band_mask = wlan_reg_wifi_band_bitmap_to_vendor_bitmap( + reg_wifi_band_bitmap); + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_SETBAND_MASK, + vendor_band_mask)) { + hdd_err("nla put failure"); + goto failure; + } + + cfg80211_vendor_cmd_reply(skb); + + hdd_exit(); + + return 0; + +failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_getband() - Wrapper to getband + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +static int wlan_hdd_cfg80211_getband(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_getband(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_cfg80211_sar_convert_limit_set() - Convert limit set value + * @nl80211_value: Vendor command attribute value + * @wmi_value: Pointer to return converted WMI return value + * + * Convert NL80211 vendor command value for SAR limit set to WMI value + * Return: 0 on success, -1 on invalid value + */ +static int wlan_hdd_cfg80211_sar_convert_limit_set(u32 nl80211_value, + u32 *wmi_value) + +{ + int ret = 0; + + switch (nl80211_value) { + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE: + *wmi_value = WMI_SAR_FEATURE_OFF; + break; + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0: + *wmi_value = WMI_SAR_FEATURE_ON_SET_0; + break; + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1: + *wmi_value = WMI_SAR_FEATURE_ON_SET_1; + break; + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2: + *wmi_value = WMI_SAR_FEATURE_ON_SET_2; + break; + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3: + *wmi_value = WMI_SAR_FEATURE_ON_SET_3; + break; + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4: + *wmi_value = WMI_SAR_FEATURE_ON_SET_4; + break; + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER: + *wmi_value = WMI_SAR_FEATURE_ON_USER_DEFINED; + break; + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: + *wmi_value = WMI_SAR_FEATURE_ON_SAR_V2_0; + break; + + default: + ret = -1; + } + return ret; +} + +#ifdef WLAN_FEATURE_SARV1_TO_SARV2 +/** + * hdd_convert_sarv1_to_sarv2() - convert SAR V1 BDF reference to SAR V2 + * @hdd_ctx: The HDD global context + * @tb: The parsed array of netlink attributes + * @sar_limit_cmd: The WMI command to be filled + * + * This feature/function is designed to solve the following problem: + * 1) Userspace application was written to use SARv1 BDF entries + * 2) Product is configured with SAR V2 BDF entries + * + * So if this feature is enabled, and if the firmware is configured + * with SAR V2 support, and if the incoming request is to enable a SAR + * V1 BDF entry, then the WMI command is generated to actually + * configure a SAR V2 BDF entry. + * + * Return: true if conversion was performed and @sar_limit_cmd is + * ready to be sent to firmware. Otherwise false in which case the + * normal parsing logic should be applied. + */ + +static bool +hdd_convert_sarv1_to_sarv2(struct hdd_context *hdd_ctx, + struct nlattr *tb[], + struct sar_limit_cmd_params *sar_limit_cmd) +{ + struct nlattr *attr; + uint32_t bdf_index, set; + struct sar_limit_cmd_row *row; + + hdd_enter(); + if (hdd_ctx->sar_version != SAR_VERSION_2) { + hdd_debug("SAR version: %d", hdd_ctx->sar_version); + return false; + } + + attr = tb[QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE]; + if (!attr) + return false; + + bdf_index = nla_get_u32(attr); + + if ((bdf_index >= QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0) && + (bdf_index <= QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4)) { + set = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0; + } else if (bdf_index == QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE) { + set = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE; + bdf_index = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0; + } else { + return false; + } + + /* Need two rows to hold the per-chain V2 power index + * To disable SARv2 limit, send chain, num_limits_row and + * power limit set to 0 (except power index 0xff) + */ + row = qdf_mem_malloc(2 * sizeof(*row)); + if (!row) + return false; + + if (wlan_hdd_cfg80211_sar_convert_limit_set( + set, &sar_limit_cmd->sar_enable)) { + hdd_err("Failed to convert SAR limit to WMI value"); + return false; + } + + sar_limit_cmd->commit_limits = 1; + sar_limit_cmd->num_limit_rows = 2; + sar_limit_cmd->sar_limit_row_list = row; + row[0].limit_value = bdf_index; + row[1].limit_value = row[0].limit_value; + row[0].chain_id = 0; + row[1].chain_id = 1; + row[0].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK; + row[1].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK; + + hdd_exit(); + return true; +} + +#else /* WLAN_FEATURE_SARV1_TO_SARV2 */ +static bool +hdd_convert_sarv1_to_sarv2(struct hdd_context *hdd_ctx, + struct nlattr *tb[], + struct sar_limit_cmd_params *sar_limit_cmd) +{ + return false; +} + +#endif /* WLAN_FEATURE_SARV1_TO_SARV2 */ + +/** + * wlan_hdd_cfg80211_sar_convert_band() - Convert WLAN band value + * @nl80211_value: Vendor command attribute value + * @wmi_value: Pointer to return converted WMI return value + * + * Convert NL80211 vendor command value for SAR BAND to WMI value + * Return: 0 on success, -1 on invalid value + */ +static int wlan_hdd_cfg80211_sar_convert_band(u32 nl80211_value, u32 *wmi_value) +{ + int ret = 0; + + switch (nl80211_value) { + case HDD_NL80211_BAND_2GHZ: + *wmi_value = WMI_SAR_2G_ID; + break; + case HDD_NL80211_BAND_5GHZ: + *wmi_value = WMI_SAR_5G_ID; + break; + default: + ret = -1; + } + return ret; +} + +/** + * wlan_hdd_cfg80211_sar_convert_modulation() - Convert WLAN modulation value + * @nl80211_value: Vendor command attribute value + * @wmi_value: Pointer to return converted WMI return value + * + * Convert NL80211 vendor command value for SAR Modulation to WMI value + * Return: 0 on success, -1 on invalid value + */ +static int wlan_hdd_cfg80211_sar_convert_modulation(u32 nl80211_value, + u32 *wmi_value) +{ + int ret = 0; + + switch (nl80211_value) { + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK: + *wmi_value = WMI_SAR_MOD_CCK; + break; + case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM: + *wmi_value = WMI_SAR_MOD_OFDM; + break; + default: + ret = -1; + } + return ret; +} + +void hdd_store_sar_config(struct hdd_context *hdd_ctx, + struct sar_limit_cmd_params *sar_limit_cmd) +{ + /* Free the previously stored sar_limit_cmd */ + wlan_hdd_free_sar_config(hdd_ctx); + + hdd_ctx->sar_cmd_params = sar_limit_cmd; +} + +void wlan_hdd_free_sar_config(struct hdd_context *hdd_ctx) +{ + struct sar_limit_cmd_params *sar_limit_cmd; + + if (!hdd_ctx->sar_cmd_params) + return; + + sar_limit_cmd = hdd_ctx->sar_cmd_params; + hdd_ctx->sar_cmd_params = NULL; + qdf_mem_free(sar_limit_cmd->sar_limit_row_list); + qdf_mem_free(sar_limit_cmd); +} + +#define SAR_LIMITS_SAR_ENABLE QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE +#define SAR_LIMITS_NUM_SPECS QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS +#define SAR_LIMITS_SPEC QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC +#define SAR_LIMITS_SPEC_BAND QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND +#define SAR_LIMITS_SPEC_CHAIN QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN +#define SAR_LIMITS_SPEC_MODULATION \ + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION +#define SAR_LIMITS_SPEC_POWER_LIMIT \ + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT +#define SAR_LIMITS_SPEC_POWER_LIMIT_INDEX \ + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX +#define SAR_LIMITS_MAX QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX + +static const struct nla_policy +sar_limits_policy[SAR_LIMITS_MAX + 1] = { + [SAR_LIMITS_SAR_ENABLE] = {.type = NLA_U32}, + [SAR_LIMITS_NUM_SPECS] = {.type = NLA_U32}, + [SAR_LIMITS_SPEC_BAND] = {.type = NLA_U32}, + [SAR_LIMITS_SPEC_CHAIN] = {.type = NLA_U32}, + [SAR_LIMITS_SPEC_MODULATION] = {.type = NLA_U32}, + [SAR_LIMITS_SPEC_POWER_LIMIT] = {.type = NLA_U32}, + [SAR_LIMITS_SPEC_POWER_LIMIT_INDEX] = {.type = NLA_U32}, +}; + +/** + * hdd_extract_sar_nested_attrs() - Extract nested SAR attribute + * @spec: nested nla attribue + * @row: output to hold extract nested attribute + * + * This function extracts nested SAR attribute one at a time which means + * for each nested attribute this has to be invoked from + * __wlan_hdd_set_sar_power_limits(). + * + * Return: On success - 0 + * On Failure - Negative value + */ +static int hdd_extract_sar_nested_attrs(struct nlattr *spec[], + struct sar_limit_cmd_row *row) +{ + uint32_t limit; + uint32_t band; + uint32_t modulation; + int ret; + + row->validity_bitmap = 0; + + if (spec[SAR_LIMITS_SPEC_POWER_LIMIT]) { + limit = nla_get_u32(spec[SAR_LIMITS_SPEC_POWER_LIMIT]); + row->limit_value = limit; + } else if (spec[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX]) { + limit = nla_get_u32(spec[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX]); + row->limit_value = limit; + } else { + hdd_err("SAR Spec does not have power limit or index value"); + return -EINVAL; + } + + if (spec[SAR_LIMITS_SPEC_BAND]) { + band = nla_get_u32(spec[SAR_LIMITS_SPEC_BAND]); + ret = wlan_hdd_cfg80211_sar_convert_band(band, &row->band_id); + if (ret) { + hdd_err("Invalid SAR Band attr"); + return ret; + } + + row->validity_bitmap |= WMI_SAR_BAND_ID_VALID_MASK; + } + + if (spec[SAR_LIMITS_SPEC_CHAIN]) { + row->chain_id = nla_get_u32(spec[SAR_LIMITS_SPEC_CHAIN]); + row->validity_bitmap |= WMI_SAR_CHAIN_ID_VALID_MASK; + } + + if (spec[SAR_LIMITS_SPEC_MODULATION]) { + modulation = nla_get_u32(spec[SAR_LIMITS_SPEC_MODULATION]); + ret = wlan_hdd_cfg80211_sar_convert_modulation(modulation, + &row->mod_id); + if (ret) { + hdd_err("Invalid SAR Modulation attr"); + return ret; + } + + row->validity_bitmap |= WMI_SAR_MOD_ID_VALID_MASK; + } + + return 0; +} + +/** + * __wlan_hdd_set_sar_power_limits() - Set SAR power limits + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * This function is used to setup Specific Absorption Rate limit specs. + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_set_sar_power_limits(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *spec[SAR_LIMITS_MAX + 1]; + struct nlattr *tb[SAR_LIMITS_MAX + 1]; + struct nlattr *spec_list; + struct sar_limit_cmd_params *sar_limit_cmd; + int ret = -EINVAL, i = 0, rem = 0; + QDF_STATUS status; + uint32_t num_limit_rows = 0; + struct sar_limit_cmd_row *row; + uint32_t sar_enable; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (wlan_cfg80211_nla_parse(tb, SAR_LIMITS_MAX, data, data_len, + sar_limits_policy)) { + hdd_err("Invalid SAR attributes"); + return -EINVAL; + } + + if (tb[SAR_LIMITS_SAR_ENABLE]) { + sar_enable = nla_get_u32(tb[SAR_LIMITS_SAR_ENABLE]); + + if ((sar_enable >= + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 && + sar_enable <= + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4) && + hdd_ctx->sar_version == SAR_VERSION_2 && + !hdd_ctx->config->enable_sar_conversion) { + hdd_err("SARV1 to SARV2 is disabled from ini"); + return -EINVAL; + } else if (sar_enable == + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 && + hdd_ctx->sar_version == SAR_VERSION_1) { + hdd_err("FW expects SARV1 given command is SARV2"); + return -EINVAL; + } + } + + sar_limit_cmd = qdf_mem_malloc(sizeof(struct sar_limit_cmd_params)); + if (!sar_limit_cmd) + return -ENOMEM; + + wlan_hdd_sar_timers_reset(hdd_ctx); + + /* is special SAR V1 => SAR V2 logic enabled and applicable? */ + if (hdd_ctx->config->enable_sar_conversion && + (hdd_convert_sarv1_to_sarv2(hdd_ctx, tb, sar_limit_cmd))) + goto send_sar_limits; + + /* Vendor command manadates all SAR Specs in single call */ + sar_limit_cmd->commit_limits = 1; + sar_limit_cmd->sar_enable = WMI_SAR_FEATURE_NO_CHANGE; + if (tb[SAR_LIMITS_SAR_ENABLE]) { + uint32_t *sar_ptr = &sar_limit_cmd->sar_enable; + + sar_enable = nla_get_u32(tb[SAR_LIMITS_SAR_ENABLE]); + ret = wlan_hdd_cfg80211_sar_convert_limit_set(sar_enable, + sar_ptr); + if (ret) { + hdd_err("Invalid SAR Enable attr"); + goto fail; + } + } + + hdd_debug("attr sar sar_enable %d", sar_limit_cmd->sar_enable); + + if (tb[SAR_LIMITS_NUM_SPECS]) { + num_limit_rows = nla_get_u32(tb[SAR_LIMITS_NUM_SPECS]); + hdd_debug("attr sar num_limit_rows %u", num_limit_rows); + } + + if (num_limit_rows > MAX_SAR_LIMIT_ROWS_SUPPORTED) { + hdd_err("SAR Spec list exceed supported size"); + goto fail; + } + + if (num_limit_rows == 0) + goto send_sar_limits; + + row = qdf_mem_malloc(sizeof(*row) * num_limit_rows); + if (!row) { + hdd_err("Failed to allocate memory for sar_limit_row_list"); + goto fail; + } + + sar_limit_cmd->num_limit_rows = num_limit_rows; + sar_limit_cmd->sar_limit_row_list = row; + + if (!tb[SAR_LIMITS_SPEC]) { + hdd_err("Invalid SAR specification list"); + goto fail; + } + + nla_for_each_nested(spec_list, tb[SAR_LIMITS_SPEC], rem) { + if (i == num_limit_rows) { + hdd_warn("SAR Cmd has excess SPECs in list"); + break; + } + + if (wlan_cfg80211_nla_parse(spec, + SAR_LIMITS_MAX, + nla_data(spec_list), + nla_len(spec_list), + sar_limits_policy)) { + hdd_err("nla_parse failed for SAR Spec list"); + goto fail; + } + + ret = hdd_extract_sar_nested_attrs(spec, row); + if (ret) { + hdd_err("Failed to extract SAR nested attrs"); + goto fail; + } + + hdd_debug("Spec_ID: %d, Band: %d Chain: %d Mod: %d POW_Limit: %d Validity_Bitmap: %d", + i, row->band_id, row->chain_id, row->mod_id, + row->limit_value, row->validity_bitmap); + + i++; + row++; + } + + if (i < sar_limit_cmd->num_limit_rows) { + hdd_warn("SAR Cmd has less SPECs in list"); + sar_limit_cmd->num_limit_rows = i; + } + +send_sar_limits: + status = sme_set_sar_power_limits(hdd_ctx->mac_handle, sar_limit_cmd); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set sar power limits"); + goto fail; + } + + /* After SSR, the SAR configuration is lost. As SSR is hidden from + * userland, this command will not come from userspace after a SSR. To + * restore this configuration, save this in hdd context and restore + * after re-init. + */ + hdd_store_sar_config(hdd_ctx, sar_limit_cmd); + return 0; + +fail: + if (sar_limit_cmd) { + qdf_mem_free(sar_limit_cmd->sar_limit_row_list); + qdf_mem_free(sar_limit_cmd); + } + + return ret; +} + +#undef SAR_LIMITS_SAR_ENABLE +#undef SAR_LIMITS_NUM_SPECS +#undef SAR_LIMITS_SPEC +#undef SAR_LIMITS_SPEC_BAND +#undef SAR_LIMITS_SPEC_CHAIN +#undef SAR_LIMITS_SPEC_MODULATION +#undef SAR_LIMITS_SPEC_POWER_LIMIT +#undef SAR_LIMITS_SPEC_POWER_LIMIT_INDEX +#undef SAR_LIMITS_MAX + +/** + * wlan_hdd_cfg80211_set_sar_power_limits() - Set SAR power limits + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Wrapper function of __wlan_hdd_cfg80211_set_sar_power_limits() + * + * Return: 0 on success, negative errno on failure + */ +static int wlan_hdd_cfg80211_set_sar_power_limits(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_set_sar_power_limits(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +static const struct +nla_policy qca_wlan_vendor_attr[QCA_WLAN_VENDOR_ATTR_MAX+1] = { + [QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = {.type = NLA_BINARY, + .len = QDF_MAC_ADDR_SIZE}, +}; + +void wlan_hdd_rso_cmd_status_cb(hdd_handle_t hdd_handle, + struct rso_cmd_status *rso_status) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + struct hdd_adapter *adapter; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, rso_status->vdev_id); + if (!adapter) { + hdd_err("adapter NULL"); + return; + } + + adapter->lfr_fw_status.is_disabled = rso_status->status; + complete(&adapter->lfr_fw_status.disable_lfr_event); +} + +/** + * __wlan_hdd_cfg80211_set_fast_roaming() - enable/disable roaming + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * This function is used to enable/disable roaming using vendor commands + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_set_fast_roaming(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + uint32_t is_fast_roam_enabled; + int ret; + QDF_STATUS qdf_status; + unsigned long rc; + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + mac_handle_t mac_handle; + bool roaming_enabled; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + ret = wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, + qca_wlan_vendor_attr); + if (ret) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch Enable flag */ + if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]) { + hdd_err("attr enable failed"); + return -EINVAL; + } + + is_fast_roam_enabled = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]); + hdd_debug("isFastRoamEnabled %d", is_fast_roam_enabled); + + /* + * Get current roaming state and decide whether to wait for RSO_STOP + * response or not. + */ + roaming_enabled = ucfg_is_roaming_enabled(hdd_ctx->pdev, + adapter->vdev_id); + + /* Update roaming */ + mac_handle = hdd_ctx->mac_handle; + qdf_status = sme_config_fast_roaming(mac_handle, adapter->vdev_id, + is_fast_roam_enabled); + if (qdf_status != QDF_STATUS_SUCCESS) + hdd_err("sme_config_fast_roaming failed with status=%d", + qdf_status); + ret = qdf_status_to_os_return(qdf_status); + + if (eConnectionState_Associated == hdd_sta_ctx->conn_info.conn_state && + roaming_enabled && + QDF_IS_STATUS_SUCCESS(qdf_status) && !is_fast_roam_enabled) { + + INIT_COMPLETION(adapter->lfr_fw_status.disable_lfr_event); + /* + * wait only for LFR disable in fw as LFR enable + * is always success + */ + rc = wait_for_completion_timeout( + &adapter->lfr_fw_status.disable_lfr_event, + msecs_to_jiffies(WAIT_TIME_RSO_CMD_STATUS)); + if (!rc) { + hdd_err("Timed out waiting for RSO CMD status"); + return -ETIMEDOUT; + } + + if (!adapter->lfr_fw_status.is_disabled) { + hdd_err("Roam disable attempt in FW fails"); + return -EBUSY; + } + } + + hdd_exit(); + return ret; +} + +/** + * wlan_hdd_cfg80211_set_fast_roaming() - enable/disable roaming + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Wrapper function of __wlan_hdd_cfg80211_set_fast_roaming() + * + * Return: 0 on success, negative errno on failure + */ +static int wlan_hdd_cfg80211_set_fast_roaming(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_fast_roaming(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/* + * define short names for the global vendor params + * used by wlan_hdd_cfg80211_setarp_stats_cmd() + */ +#define STATS_SET_INVALID \ + QCA_ATTR_NUD_STATS_SET_INVALID +#define STATS_SET_START \ + QCA_ATTR_NUD_STATS_SET_START +#define STATS_GW_IPV4 \ + QCA_ATTR_NUD_STATS_GW_IPV4 +#define STATS_SET_DATA_PKT_INFO \ + QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO +#define STATS_SET_MAX \ + QCA_ATTR_NUD_STATS_SET_MAX + +const struct nla_policy +qca_wlan_vendor_set_nud_stats[STATS_SET_MAX + 1] = { + [STATS_SET_START] = {.type = NLA_FLAG }, + [STATS_GW_IPV4] = {.type = NLA_U32 }, + [STATS_SET_DATA_PKT_INFO] = {.type = NLA_U32 }, +}; + +/* define short names for the global vendor params */ +#define CONNECTIVITY_STATS_SET_INVALID \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_SET_INVALID +#define STATS_PKT_INFO_TYPE \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_STATS_PKT_INFO_TYPE +#define STATS_DNS_DOMAIN_NAME \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_DNS_DOMAIN_NAME +#define STATS_SRC_PORT \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_SRC_PORT +#define STATS_DEST_PORT \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_PORT +#define STATS_DEST_IPV4 \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_IPV4 +#define STATS_DEST_IPV6 \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_IPV6 +#define CONNECTIVITY_STATS_SET_MAX \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_SET_MAX + +const struct nla_policy +qca_wlan_vendor_set_connectivity_check_stats[CONNECTIVITY_STATS_SET_MAX + 1] = { + [STATS_PKT_INFO_TYPE] = {.type = NLA_U32 }, + [STATS_DNS_DOMAIN_NAME] = {.type = NLA_NUL_STRING, + .len = DNS_DOMAIN_NAME_MAX_LEN }, + [STATS_SRC_PORT] = {.type = NLA_U32 }, + [STATS_DEST_PORT] = {.type = NLA_U32 }, + [STATS_DEST_IPV4] = {.type = NLA_U32 }, + [STATS_DEST_IPV6] = {.type = NLA_BINARY, + .len = ICMPv6_ADDR_LEN }, +}; + +/** + * hdd_dns_unmake_name_query() - Convert an uncompressed DNS name to a + * NUL-terminated string + * @name: DNS name + * + * Return: Produce a printable version of a DNS name. + */ +static inline uint8_t *hdd_dns_unmake_name_query(uint8_t *name) +{ + uint8_t *p; + unsigned int len; + + p = name; + while ((len = *p)) { + *(p++) = '.'; + p += len; + } + + return name + 1; +} + +/** + * hdd_dns_make_name_query() - Convert a standard NUL-terminated string + * to DNS name + * @string: Name as a NUL-terminated string + * @buf: Buffer in which to place DNS name + * + * DNS names consist of "element" pairs. + * + * Return: Byte following constructed DNS name + */ +static uint8_t *hdd_dns_make_name_query(const uint8_t *string, + uint8_t *buf, uint8_t len) +{ + uint8_t *length_byte = buf++; + uint8_t c; + + if (string[len - 1]) { + hdd_debug("DNS name is not null terminated"); + return NULL; + } + + while ((c = *(string++))) { + if (c == '.') { + *length_byte = buf - length_byte - 1; + length_byte = buf; + } + *(buf++) = c; + } + *length_byte = buf - length_byte - 1; + *(buf++) = '\0'; + return buf; +} + +/** + * hdd_set_clear_connectivity_check_stats_info() - set/clear stats info + * @adapter: Pointer to hdd adapter + * @arp_stats_params: arp stats structure to be sent to FW + * @tb: nl attribute + * @is_set_stats: set/clear stats + * + * + * Return: 0 on success, negative errno on failure + */ +static int hdd_set_clear_connectivity_check_stats_info( + struct hdd_adapter *adapter, + struct set_arp_stats_params *arp_stats_params, + struct nlattr **tb, bool is_set_stats) +{ + struct nlattr *tb2[CONNECTIVITY_STATS_SET_MAX + 1]; + struct nlattr *curr_attr = NULL; + int err = 0; + uint32_t pkt_bitmap; + int rem; + + /* Set NUD command for start tracking is received. */ + nla_for_each_nested(curr_attr, + tb[STATS_SET_DATA_PKT_INFO], + rem) { + + if (wlan_cfg80211_nla_parse(tb2, + CONNECTIVITY_STATS_SET_MAX, + nla_data(curr_attr), nla_len(curr_attr), + qca_wlan_vendor_set_connectivity_check_stats)) { + hdd_err("nla_parse failed"); + err = -EINVAL; + goto end; + } + + if (tb2[STATS_PKT_INFO_TYPE]) { + pkt_bitmap = nla_get_u32(tb2[STATS_PKT_INFO_TYPE]); + if (!pkt_bitmap) { + hdd_err("pkt tracking bitmap is empty"); + err = -EINVAL; + goto end; + } + + if (is_set_stats) { + arp_stats_params->pkt_type_bitmap = pkt_bitmap; + arp_stats_params->flag = true; + adapter->pkt_type_bitmap |= + arp_stats_params->pkt_type_bitmap; + + if (pkt_bitmap & CONNECTIVITY_CHECK_SET_ARP) { + if (!tb[STATS_GW_IPV4]) { + hdd_err("GW ipv4 address is not present"); + err = -EINVAL; + goto end; + } + arp_stats_params->ip_addr = + nla_get_u32(tb[STATS_GW_IPV4]); + arp_stats_params->pkt_type = + WLAN_NUD_STATS_ARP_PKT_TYPE; + adapter->track_arp_ip = + arp_stats_params->ip_addr; + } + + if (pkt_bitmap & CONNECTIVITY_CHECK_SET_DNS) { + uint8_t *domain_name; + + if (!tb2[STATS_DNS_DOMAIN_NAME]) { + hdd_err("DNS domain id is not present"); + err = -EINVAL; + goto end; + } + domain_name = nla_data( + tb2[STATS_DNS_DOMAIN_NAME]); + adapter->track_dns_domain_len = + nla_len(tb2[ + STATS_DNS_DOMAIN_NAME]); + if (!hdd_dns_make_name_query( + domain_name, + adapter->dns_payload, + adapter->track_dns_domain_len)) + adapter->track_dns_domain_len = + 0; + /* DNStracking isn't supported in FW. */ + arp_stats_params->pkt_type_bitmap &= + ~CONNECTIVITY_CHECK_SET_DNS; + } + + if (pkt_bitmap & + CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) { + if (!tb2[STATS_SRC_PORT] || + !tb2[STATS_DEST_PORT]) { + hdd_err("Source/Dest port is not present"); + err = -EINVAL; + goto end; + } + arp_stats_params->tcp_src_port = + nla_get_u32( + tb2[STATS_SRC_PORT]); + arp_stats_params->tcp_dst_port = + nla_get_u32( + tb2[STATS_DEST_PORT]); + adapter->track_src_port = + arp_stats_params->tcp_src_port; + adapter->track_dest_port = + arp_stats_params->tcp_dst_port; + } + + if (pkt_bitmap & + CONNECTIVITY_CHECK_SET_ICMPV4) { + if (!tb2[STATS_DEST_IPV4]) { + hdd_err("destination ipv4 address to track ping packets is not present"); + err = -EINVAL; + goto end; + } + arp_stats_params->icmp_ipv4 = + nla_get_u32( + tb2[STATS_DEST_IPV4]); + adapter->track_dest_ipv4 = + arp_stats_params->icmp_ipv4; + } + } else { + /* clear stats command received */ + arp_stats_params->pkt_type_bitmap = pkt_bitmap; + arp_stats_params->flag = false; + adapter->pkt_type_bitmap &= + (~arp_stats_params->pkt_type_bitmap); + + if (pkt_bitmap & CONNECTIVITY_CHECK_SET_ARP) { + arp_stats_params->pkt_type = + WLAN_NUD_STATS_ARP_PKT_TYPE; + qdf_mem_zero(&adapter->hdd_stats. + hdd_arp_stats, + sizeof(adapter->hdd_stats. + hdd_arp_stats)); + adapter->track_arp_ip = 0; + } + + if (pkt_bitmap & CONNECTIVITY_CHECK_SET_DNS) { + /* DNStracking isn't supported in FW. */ + arp_stats_params->pkt_type_bitmap &= + ~CONNECTIVITY_CHECK_SET_DNS; + qdf_mem_zero(&adapter->hdd_stats. + hdd_dns_stats, + sizeof(adapter->hdd_stats. + hdd_dns_stats)); + qdf_mem_zero(adapter->dns_payload, + adapter->track_dns_domain_len); + adapter->track_dns_domain_len = 0; + } + + if (pkt_bitmap & + CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) { + qdf_mem_zero(&adapter->hdd_stats. + hdd_tcp_stats, + sizeof(adapter->hdd_stats. + hdd_tcp_stats)); + adapter->track_src_port = 0; + adapter->track_dest_port = 0; + } + + if (pkt_bitmap & + CONNECTIVITY_CHECK_SET_ICMPV4) { + qdf_mem_zero(&adapter->hdd_stats. + hdd_icmpv4_stats, + sizeof(adapter->hdd_stats. + hdd_icmpv4_stats)); + adapter->track_dest_ipv4 = 0; + } + } + } else { + hdd_err("stats list empty"); + err = -EINVAL; + goto end; + } + } + +end: + return err; +} + +static const struct nla_policy qca_wlan_vendor_set_trace_level_policy[ + QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_PARAM] = {.type = NLA_NESTED }, + [QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MODULE_ID] = {.type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_TRACE_MASK] = {.type = NLA_U32 }, +}; + +/** + * __wlan_hdd_cfg80211_set_trace_level() - Set the trace level + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Return: 0 on success, negative errno on failure + */ +static int +__wlan_hdd_cfg80211_set_trace_level(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb1[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX + 1]; + struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX + 1]; + struct nlattr *apth; + int rem; + int ret = 1; + int print_idx = -1; + int module_id = -1; + int bit_mask = -1; + int status; + + hdd_enter(); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret != 0) + return -EINVAL; + + print_idx = qdf_get_pidx(); + if (print_idx < 0 || print_idx >= MAX_PRINT_CONFIG_SUPPORTED) { + hdd_err("Invalid print controle object index"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb1, + QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX, + data, data_len, + qca_wlan_vendor_set_trace_level_policy)) { + hdd_err("Invalid attr"); + return -EINVAL; + } + + if (!tb1[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_PARAM]) { + hdd_err("attr trace level param failed"); + return -EINVAL; + } + + nla_for_each_nested(apth, + tb1[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_PARAM], rem) { + if (wlan_cfg80211_nla_parse(tb2, + QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX, + nla_data(apth), nla_len(apth), + qca_wlan_vendor_set_trace_level_policy)) { + hdd_err("Invalid attr"); + return -EINVAL; + } + + if (!tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MODULE_ID]) { + hdd_err("attr Module ID failed"); + return -EINVAL; + } + module_id = nla_get_u32 + (tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MODULE_ID]); + + if (!tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_TRACE_MASK]) { + hdd_err("attr Verbose mask failed"); + return -EINVAL; + } + bit_mask = nla_get_u32 + (tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_TRACE_MASK]); + + status = hdd_qdf_trace_enable(module_id, bit_mask); + + if (status != 0) + hdd_err("can not set verbose mask %d for the category %d", + bit_mask, module_id); + } + + hdd_exit(); + return ret; +} + +/** + * wlan_hdd_cfg80211_set_trace_level() - Set the trace level + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Wrapper function of __wlan_hdd_cfg80211_set_trace_level() + * + * Return: 0 on success, negative errno on failure + */ + +static int wlan_hdd_cfg80211_set_trace_level(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_trace_level(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_set_nud_stats() - set arp stats command to firmware + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: pointer to apfind configuration data. + * @data_len: the length in byte of apfind data. + * + * This is called when wlan driver needs to send arp stats to + * firmware. + * + * Return: An error code or 0 on success. + */ +static int __wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct nlattr *tb[STATS_SET_MAX + 1]; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct set_arp_stats_params arp_stats_params = {0}; + int err = 0; + mac_handle_t mac_handle; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + err = wlan_hdd_validate_context(hdd_ctx); + if (0 != err) + return err; + + if (adapter->vdev_id == WLAN_UMAC_VDEV_ID_MAX) { + hdd_err("Invalid vdev id"); + return -EINVAL; + } + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_err("STATS supported in only STA mode!"); + return -EINVAL; + } + + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + hdd_debug("Not Associated"); + return 0; + } + + if (hdd_is_roaming_in_progress(hdd_ctx)) + return -EINVAL; + + err = wlan_cfg80211_nla_parse(tb, STATS_SET_MAX, data, data_len, + qca_wlan_vendor_set_nud_stats); + if (err) { + hdd_err("STATS_SET_START ATTR"); + return err; + } + + if (tb[STATS_SET_START]) { + /* tracking is enabled for stats other than arp. */ + if (tb[STATS_SET_DATA_PKT_INFO]) { + err = hdd_set_clear_connectivity_check_stats_info( + adapter, + &arp_stats_params, tb, true); + if (err) + return -EINVAL; + + /* + * if only tracking dns, then don't send + * wmi command to FW. + */ + if (!arp_stats_params.pkt_type_bitmap) + return err; + } else { + if (!tb[STATS_GW_IPV4]) { + hdd_err("STATS_SET_START CMD"); + return -EINVAL; + } + + arp_stats_params.pkt_type_bitmap = + CONNECTIVITY_CHECK_SET_ARP; + adapter->pkt_type_bitmap |= + arp_stats_params.pkt_type_bitmap; + arp_stats_params.flag = true; + arp_stats_params.ip_addr = + nla_get_u32(tb[STATS_GW_IPV4]); + adapter->track_arp_ip = arp_stats_params.ip_addr; + arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE; + } + } else { + /* clear stats command received. */ + if (tb[STATS_SET_DATA_PKT_INFO]) { + err = hdd_set_clear_connectivity_check_stats_info( + adapter, + &arp_stats_params, tb, false); + if (err) + return -EINVAL; + + /* + * if only tracking dns, then don't send + * wmi command to FW. + */ + if (!arp_stats_params.pkt_type_bitmap) + return err; + } else { + arp_stats_params.pkt_type_bitmap = + CONNECTIVITY_CHECK_SET_ARP; + adapter->pkt_type_bitmap &= + (~arp_stats_params.pkt_type_bitmap); + arp_stats_params.flag = false; + qdf_mem_zero(&adapter->hdd_stats.hdd_arp_stats, + sizeof(adapter->hdd_stats.hdd_arp_stats)); + arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE; + } + } + + hdd_debug("STATS_SET_START Received flag %d!", arp_stats_params.flag); + + arp_stats_params.vdev_id = adapter->vdev_id; + + mac_handle = hdd_ctx->mac_handle; + if (QDF_STATUS_SUCCESS != + sme_set_nud_debug_stats(mac_handle, &arp_stats_params)) { + hdd_err("STATS_SET_START CMD Failed!"); + return -EINVAL; + } + + hdd_exit(); + + return err; +} + +/** + * wlan_hdd_cfg80211_set_nud_stats() - set arp stats command to firmware + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: pointer to apfind configuration data. + * @data_len: the length in byte of apfind data. + * + * This is called when wlan driver needs to send arp stats to + * firmware. + * + * Return: An error code or 0 on success. + */ +static int wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_nud_stats(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#undef STATS_SET_INVALID +#undef STATS_SET_START +#undef STATS_GW_IPV4 +#undef STATS_SET_MAX + +/* + * define short names for the global vendor params + * used by wlan_hdd_cfg80211_setarp_stats_cmd() + */ +#define STATS_GET_INVALID \ + QCA_ATTR_NUD_STATS_SET_INVALID +#define COUNT_FROM_NETDEV \ + QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV +#define COUNT_TO_LOWER_MAC \ + QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC +#define RX_COUNT_BY_LOWER_MAC \ + QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC +#define COUNT_TX_SUCCESS \ + QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS +#define RSP_RX_COUNT_BY_LOWER_MAC \ + QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC +#define RSP_RX_COUNT_BY_UPPER_MAC \ + QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC +#define RSP_COUNT_TO_NETDEV \ + QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV +#define RSP_COUNT_OUT_OF_ORDER_DROP \ + QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP +#define AP_LINK_ACTIVE \ + QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE +#define AP_LINK_DAD \ + QCA_ATTR_NUD_STATS_IS_DAD +#define DATA_PKT_STATS \ + QCA_ATTR_NUD_STATS_DATA_PKT_STATS +#define STATS_GET_MAX \ + QCA_ATTR_NUD_STATS_GET_MAX + +#define CHECK_STATS_INVALID \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_INVALID +#define CHECK_STATS_PKT_TYPE \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_TYPE +#define CHECK_STATS_PKT_DNS_DOMAIN_NAME \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DNS_DOMAIN_NAME +#define CHECK_STATS_PKT_SRC_PORT \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_SRC_PORT +#define CHECK_STATS_PKT_DEST_PORT \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_PORT +#define CHECK_STATS_PKT_DEST_IPV4 \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_IPV4 +#define CHECK_STATS_PKT_DEST_IPV6 \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_IPV6 +#define CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV +#define CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC +#define CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC +#define CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS +#define CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC +#define CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC +#define CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV +#define CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP \ + QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP +#define CHECK_DATA_STATS_MAX \ + QCA_ATTR_CONNECTIVITY_CHECK_DATA_STATS_MAX + + +const struct nla_policy +qca_wlan_vendor_get_nud_stats[STATS_GET_MAX + 1] = { + [COUNT_FROM_NETDEV] = {.type = NLA_U16 }, + [COUNT_TO_LOWER_MAC] = {.type = NLA_U16 }, + [RX_COUNT_BY_LOWER_MAC] = {.type = NLA_U16 }, + [COUNT_TX_SUCCESS] = {.type = NLA_U16 }, + [RSP_RX_COUNT_BY_LOWER_MAC] = {.type = NLA_U16 }, + [RSP_RX_COUNT_BY_UPPER_MAC] = {.type = NLA_U16 }, + [RSP_COUNT_TO_NETDEV] = {.type = NLA_U16 }, + [RSP_COUNT_OUT_OF_ORDER_DROP] = {.type = NLA_U16 }, + [AP_LINK_ACTIVE] = {.type = NLA_FLAG }, + [AP_LINK_DAD] = {.type = NLA_FLAG }, + [DATA_PKT_STATS] = {.type = NLA_U16 }, +}; + +/** + * hdd_populate_dns_stats_info() - send dns stats info to network stack + * @adapter: pointer to adapter context + * @skb: pointer to skb + * + * + * Return: An error code or 0 on success. + */ +static int hdd_populate_dns_stats_info(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + uint8_t *dns_query; + + dns_query = qdf_mem_malloc(adapter->track_dns_domain_len + 1); + if (!dns_query) + return -EINVAL; + + qdf_mem_copy(dns_query, adapter->dns_payload, + adapter->track_dns_domain_len); + + if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, + CONNECTIVITY_CHECK_SET_DNS) || + nla_put(skb, CHECK_STATS_PKT_DNS_DOMAIN_NAME, + adapter->track_dns_domain_len, + hdd_dns_unmake_name_query(dns_query)) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV, + adapter->hdd_stats.hdd_dns_stats.tx_dns_req_count) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC, + adapter->hdd_stats.hdd_dns_stats.tx_host_fw_sent) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC, + adapter->hdd_stats.hdd_dns_stats.tx_host_fw_sent) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS, + adapter->hdd_stats.hdd_dns_stats.tx_ack_cnt) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC, + adapter->hdd_stats.hdd_dns_stats.rx_dns_rsp_count) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV, + adapter->hdd_stats.hdd_dns_stats.rx_delivered) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP, + adapter->hdd_stats.hdd_dns_stats.rx_host_drop)) { + hdd_err("nla put fail"); + qdf_mem_free(dns_query); + kfree_skb(skb); + return -EINVAL; + } + qdf_mem_free(dns_query); + return 0; +} + +/** + * hdd_populate_tcp_stats_info() - send tcp stats info to network stack + * @adapter: pointer to adapter context + * @skb: pointer to skb + * @pkt_type: tcp pkt type + * + * Return: An error code or 0 on success. + */ +static int hdd_populate_tcp_stats_info(struct hdd_adapter *adapter, + struct sk_buff *skb, + uint8_t pkt_type) +{ + switch (pkt_type) { + case CONNECTIVITY_CHECK_SET_TCP_SYN: + /* Fill info for tcp syn packets (tx packet) */ + if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, + CONNECTIVITY_CHECK_SET_TCP_SYN) || + nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT, + adapter->track_src_port) || + nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT, + adapter->track_dest_port) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV, + adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_count) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC, + adapter->hdd_stats.hdd_tcp_stats. + tx_tcp_syn_host_fw_sent) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC, + adapter->hdd_stats.hdd_tcp_stats. + tx_tcp_syn_host_fw_sent) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS, + adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_ack_cnt)) { + hdd_err("nla put fail"); + kfree_skb(skb); + return -EINVAL; + } + break; + case CONNECTIVITY_CHECK_SET_TCP_SYN_ACK: + /* Fill info for tcp syn-ack packets (rx packet) */ + if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, + CONNECTIVITY_CHECK_SET_TCP_SYN_ACK) || + nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT, + adapter->track_src_port) || + nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT, + adapter->track_dest_port) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC, + adapter->hdd_stats.hdd_tcp_stats.rx_fw_cnt) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC, + adapter->hdd_stats.hdd_tcp_stats. + rx_tcp_syn_ack_count) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV, + adapter->hdd_stats.hdd_tcp_stats.rx_delivered) || + nla_put_u16(skb, + CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP, + adapter->hdd_stats.hdd_tcp_stats.rx_host_drop)) { + hdd_err("nla put fail"); + kfree_skb(skb); + return -EINVAL; + } + break; + case CONNECTIVITY_CHECK_SET_TCP_ACK: + /* Fill info for tcp ack packets (tx packet) */ + if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, + CONNECTIVITY_CHECK_SET_TCP_ACK) || + nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT, + adapter->track_src_port) || + nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT, + adapter->track_dest_port) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV, + adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_count) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC, + adapter->hdd_stats.hdd_tcp_stats. + tx_tcp_ack_host_fw_sent) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC, + adapter->hdd_stats.hdd_tcp_stats. + tx_tcp_ack_host_fw_sent) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS, + adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_ack_cnt)) { + hdd_err("nla put fail"); + kfree_skb(skb); + return -EINVAL; + } + break; + default: + break; + } + return 0; +} + +/** + * hdd_populate_icmpv4_stats_info() - send icmpv4 stats info to network stack + * @adapter: pointer to adapter context + * @skb: pointer to skb + * + * + * Return: An error code or 0 on success. + */ +static int hdd_populate_icmpv4_stats_info(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, + CONNECTIVITY_CHECK_SET_ICMPV4) || + nla_put_u32(skb, CHECK_STATS_PKT_DEST_IPV4, + adapter->track_dest_ipv4) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV, + adapter->hdd_stats.hdd_icmpv4_stats.tx_icmpv4_req_count) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC, + adapter->hdd_stats.hdd_icmpv4_stats.tx_host_fw_sent) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC, + adapter->hdd_stats.hdd_icmpv4_stats.tx_host_fw_sent) || + nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS, + adapter->hdd_stats.hdd_icmpv4_stats.tx_ack_cnt) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC, + adapter->hdd_stats.hdd_icmpv4_stats.rx_fw_cnt) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC, + adapter->hdd_stats.hdd_icmpv4_stats.rx_icmpv4_rsp_count) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV, + adapter->hdd_stats.hdd_icmpv4_stats.rx_delivered) || + nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP, + adapter->hdd_stats.hdd_icmpv4_stats.rx_host_drop)) { + hdd_err("nla put fail"); + kfree_skb(skb); + return -EINVAL; + } + return 0; +} + +/** + * hdd_populate_connectivity_check_stats_info() - send connectivity stats info + * to network stack + * @adapter: pointer to adapter context + * @skb: pointer to skb + * + * + * Return: An error code or 0 on success. + */ + +static int hdd_populate_connectivity_check_stats_info( + struct hdd_adapter *adapter, struct sk_buff *skb) +{ + struct nlattr *connect_stats, *connect_info; + uint32_t count = 0; + + connect_stats = nla_nest_start(skb, DATA_PKT_STATS); + if (!connect_stats) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_DNS) { + connect_info = nla_nest_start(skb, count); + if (!connect_info) { + hdd_err("nla_nest_start failed count %u", count); + return -EINVAL; + } + + if (hdd_populate_dns_stats_info(adapter, skb)) + goto put_attr_fail; + nla_nest_end(skb, connect_info); + count++; + } + + if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) { + connect_info = nla_nest_start(skb, count); + if (!connect_info) { + hdd_err("nla_nest_start failed count %u", count); + return -EINVAL; + } + if (hdd_populate_tcp_stats_info(adapter, skb, + CONNECTIVITY_CHECK_SET_TCP_SYN)) + goto put_attr_fail; + nla_nest_end(skb, connect_info); + count++; + + connect_info = nla_nest_start(skb, count); + if (!connect_info) { + hdd_err("nla_nest_start failed count %u", count); + return -EINVAL; + } + if (hdd_populate_tcp_stats_info(adapter, skb, + CONNECTIVITY_CHECK_SET_TCP_SYN_ACK)) + goto put_attr_fail; + nla_nest_end(skb, connect_info); + count++; + + connect_info = nla_nest_start(skb, count); + if (!connect_info) { + hdd_err("nla_nest_start failed count %u", count); + return -EINVAL; + } + if (hdd_populate_tcp_stats_info(adapter, skb, + CONNECTIVITY_CHECK_SET_TCP_ACK)) + goto put_attr_fail; + nla_nest_end(skb, connect_info); + count++; + } + + if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_ICMPV4) { + connect_info = nla_nest_start(skb, count); + if (!connect_info) { + hdd_err("nla_nest_start failed count %u", count); + return -EINVAL; + } + + if (hdd_populate_icmpv4_stats_info(adapter, skb)) + goto put_attr_fail; + nla_nest_end(skb, connect_info); + count++; + } + + nla_nest_end(skb, connect_stats); + return 0; + +put_attr_fail: + hdd_err("QCA_WLAN_VENDOR_ATTR put fail. count %u", count); + return -EINVAL; +} + + +/** + * __wlan_hdd_cfg80211_get_nud_stats() - get arp stats command to firmware + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: pointer to apfind configuration data. + * @data_len: the length in byte of apfind data. + * + * This is called when wlan driver needs to get arp stats to + * firmware. + * + * Return: An error code or 0 on success. + */ +static int __wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int err = 0; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct get_arp_stats_params arp_stats_params; + mac_handle_t mac_handle; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint32_t pkt_type_bitmap; + struct sk_buff *skb; + struct osif_request *request = NULL; + static const struct osif_request_params params = { + .priv_size = 0, + .timeout_ms = WLAN_WAIT_TIME_NUD_STATS, + }; + void *cookie = NULL; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + err = wlan_hdd_validate_context(hdd_ctx); + if (0 != err) + return err; + + err = hdd_validate_adapter(adapter); + if (err) + return err; + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_err("STATS supported in only STA mode!"); + return -EINVAL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + + cookie = osif_request_cookie(request); + + arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE; + arp_stats_params.vdev_id = adapter->vdev_id; + + pkt_type_bitmap = adapter->pkt_type_bitmap; + + /* send NUD failure event only when ARP tracking is enabled. */ + if (cdp_cfg_get(soc, cfg_dp_enable_data_stall) && + (pkt_type_bitmap & CONNECTIVITY_CHECK_SET_ARP)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "Data stall due to NUD failure"); + cdp_post_data_stall_event(soc, + DATA_STALL_LOG_INDICATOR_FRAMEWORK, + DATA_STALL_LOG_NUD_FAILURE, + OL_TXRX_PDEV_ID, 0XFF, + DATA_STALL_LOG_RECOVERY_TRIGGER_PDR); + } + + mac_handle = hdd_ctx->mac_handle; + if (sme_set_nud_debug_stats_cb(mac_handle, hdd_get_nud_stats_cb, + cookie) != QDF_STATUS_SUCCESS) { + hdd_err("Setting NUD debug stats callback failure"); + err = -EINVAL; + goto exit; + } + + if (QDF_STATUS_SUCCESS != + sme_get_nud_debug_stats(mac_handle, &arp_stats_params)) { + hdd_err("STATS_SET_START CMD Failed!"); + err = -EINVAL; + goto exit; + } + + err = osif_request_wait_for_response(request); + if (err) { + hdd_err("SME timedout while retrieving NUD stats"); + err = -ETIMEDOUT; + goto exit; + } + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, + WLAN_NUD_STATS_LEN); + if (!skb) { + hdd_err("%s: cfg80211_vendor_cmd_alloc_reply_skb failed", + __func__); + err = -ENOMEM; + goto exit; + } + + hdd_update_sta_arp_stats(adapter); + + if (nla_put_u16(skb, COUNT_FROM_NETDEV, + adapter->hdd_stats.hdd_arp_stats.tx_arp_req_count) || + nla_put_u16(skb, COUNT_TO_LOWER_MAC, + adapter->hdd_stats.hdd_arp_stats.tx_host_fw_sent) || + nla_put_u16(skb, RX_COUNT_BY_LOWER_MAC, + adapter->hdd_stats.hdd_arp_stats.tx_host_fw_sent) || + nla_put_u16(skb, COUNT_TX_SUCCESS, + adapter->hdd_stats.hdd_arp_stats.tx_ack_cnt) || + nla_put_u16(skb, RSP_RX_COUNT_BY_LOWER_MAC, + adapter->hdd_stats.hdd_arp_stats.rx_fw_cnt) || + nla_put_u16(skb, RSP_RX_COUNT_BY_UPPER_MAC, + adapter->hdd_stats.hdd_arp_stats.rx_arp_rsp_count) || + nla_put_u16(skb, RSP_COUNT_TO_NETDEV, + adapter->hdd_stats.hdd_arp_stats.rx_delivered) || + nla_put_u16(skb, RSP_COUNT_OUT_OF_ORDER_DROP, + adapter->hdd_stats.hdd_arp_stats. + rx_host_drop_reorder)) { + hdd_err("nla put fail"); + kfree_skb(skb); + err = -EINVAL; + goto exit; + } + if (adapter->con_status) + nla_put_flag(skb, AP_LINK_ACTIVE); + if (adapter->dad) + nla_put_flag(skb, AP_LINK_DAD); + + /* ARP tracking is done above. */ + pkt_type_bitmap &= ~CONNECTIVITY_CHECK_SET_ARP; + + if (pkt_type_bitmap) { + if (hdd_populate_connectivity_check_stats_info(adapter, skb)) { + err = -EINVAL; + goto exit; + } + } + + cfg80211_vendor_cmd_reply(skb); +exit: + osif_request_put(request); + return err; +} + +/** + * wlan_hdd_cfg80211_get_nud_stats() - get arp stats command to firmware + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: pointer to apfind configuration data. + * @data_len: the length in byte of apfind data. + * + * This is called when wlan driver needs to get arp stats to + * firmware. + * + * Return: An error code or 0 on success. + */ +static int wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_nud_stats(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#undef QCA_ATTR_NUD_STATS_SET_INVALID +#undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV +#undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC +#undef QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC +#undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS +#undef QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC +#undef QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC +#undef QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV +#undef QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP +#undef QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE +#undef QCA_ATTR_NUD_STATS_GET_MAX + +void hdd_bt_activity_cb(hdd_handle_t hdd_handle, uint32_t bt_activity) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + int status; + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return; + + if (bt_activity == WLAN_COEX_EVENT_BT_A2DP_PROFILE_ADD) + hdd_ctx->bt_a2dp_active = 1; + else if (bt_activity == WLAN_COEX_EVENT_BT_A2DP_PROFILE_REMOVE) + hdd_ctx->bt_a2dp_active = 0; + else if (bt_activity == WLAN_COEX_EVENT_BT_VOICE_PROFILE_ADD) + hdd_ctx->bt_vo_active = 1; + else if (bt_activity == WLAN_COEX_EVENT_BT_VOICE_PROFILE_REMOVE) + hdd_ctx->bt_vo_active = 0; + else + return; + + ucfg_scan_set_bt_activity(hdd_ctx->psoc, hdd_ctx->bt_a2dp_active); + hdd_debug("a2dp_active: %d vo_active: %d", hdd_ctx->bt_a2dp_active, + hdd_ctx->bt_vo_active); +} + +struct chain_rssi_priv { + struct chain_rssi_result chain_rssi; +}; + +/** + * hdd_get_chain_rssi_cb() - Callback function to get chain rssi + * @context: opaque context originally passed to SME. HDD always passes + * a cookie for the request context + * @data: struct for get chain rssi + * + * This function receives the response/data from the lower layer and + * checks to see if the thread is still waiting then post the results to + * upper layer, if the request has timed out then ignore. + * + * Return: None + */ +static void hdd_get_chain_rssi_cb(void *context, + struct chain_rssi_result *data) +{ + struct osif_request *request; + struct chain_rssi_priv *priv; + + hdd_enter(); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + priv->chain_rssi = *data; + osif_request_complete(request); + osif_request_put(request); +} + +/** + * hdd_post_get_chain_rssi_rsp - send rsp to user space + * @hdd_ctx: pointer to hdd context + * @result: chain rssi result + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_post_get_chain_rssi_rsp(struct hdd_context *hdd_ctx, + struct chain_rssi_result *result) +{ + struct sk_buff *skb; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, + (sizeof(result->chain_rssi) + NLA_HDRLEN) + + (sizeof(result->chain_evm) + NLA_HDRLEN) + + (sizeof(result->ant_id) + NLA_HDRLEN) + + NLMSG_HDRLEN); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return -ENOMEM; + } + + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI, + sizeof(result->chain_rssi), + result->chain_rssi)) { + hdd_err("put fail"); + goto nla_put_failure; + } + + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_CHAIN_EVM, + sizeof(result->chain_evm), + result->chain_evm)) { + hdd_err("put fail"); + goto nla_put_failure; + } + + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO, + sizeof(result->ant_id), + result->ant_id)) { + hdd_err("put fail"); + goto nla_put_failure; + } + + cfg80211_vendor_cmd_reply(skb); + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * __wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * Return: 0 on success; error number otherwise. + */ +static int __wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + mac_handle_t mac_handle; + struct get_chain_rssi_req_params req_msg; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + QDF_STATUS status; + int retval; + void *cookie; + struct osif_request *request; + struct chain_rssi_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + + hdd_enter(); + + retval = wlan_hdd_validate_context(hdd_ctx); + if (0 != retval) + return retval; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, + data, data_len, NULL)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) { + hdd_err("attr mac addr failed"); + return -EINVAL; + } + if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) != + QDF_MAC_ADDR_SIZE) { + hdd_err("incorrect mac size"); + return -EINVAL; + } + memcpy(&req_msg.peer_macaddr, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]), + QDF_MAC_ADDR_SIZE); + req_msg.session_id = adapter->vdev_id; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + mac_handle = hdd_ctx->mac_handle; + status = sme_get_chain_rssi(mac_handle, + &req_msg, + hdd_get_chain_rssi_cb, + cookie); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Unable to get chain rssi"); + retval = qdf_status_to_os_return(status); + } else { + retval = osif_request_wait_for_response(request); + if (retval) { + hdd_err("Target response timed out"); + } else { + priv = osif_request_priv(request); + retval = hdd_post_get_chain_rssi_rsp(hdd_ctx, + &priv->chain_rssi); + if (retval) + hdd_err("Failed to post chain rssi"); + } + } + osif_request_put(request); + + hdd_exit(); + return retval; +} + +/** + * wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * Return: 0 on success; error number otherwise. + */ +static int wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_chain_rssi(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_fill_intf_info() - Fill skb buffer with interface info + * @skb: Pointer to skb + * @info: mac mode info + * @index: attribute type index for nla_nest_start() + * + * Return : 0 on success and errno on failure + */ +static int wlan_hdd_fill_intf_info(struct sk_buff *skb, + struct connection_info *info, int index) +{ + struct nlattr *attr; + uint32_t freq; + struct hdd_context *hdd_ctx; + struct hdd_adapter *hdd_adapter; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) + goto error; + + hdd_adapter = hdd_get_adapter_by_vdev(hdd_ctx, info->vdev_id); + if (!hdd_adapter) + goto error; + + attr = nla_nest_start(skb, index); + if (!attr) + goto error; + + freq = sme_chn_to_freq(info->channel); + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX, + hdd_adapter->dev->ifindex) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ, freq)) + goto error; + + nla_nest_end(skb, attr); + + return 0; +error: + hdd_err("Fill buffer with interface info failed"); + return -EINVAL; +} + +/** + * wlan_hdd_fill_mac_info() - Fill skb buffer with mac info + * @skb: Pointer to skb + * @info: mac mode info + * @mac_id: MAC id + * @conn_count: number of current connections + * + * Return : 0 on success and errno on failure + */ +static int wlan_hdd_fill_mac_info(struct sk_buff *skb, + struct connection_info *info, uint32_t mac_id, + uint32_t conn_count) +{ + struct nlattr *attr, *intf_attr; + uint32_t band = 0, i = 0, j = 0; + bool present = false; + + while (i < conn_count) { + if (info[i].mac_id == mac_id) { + present = true; + if (info[i].channel <= SIR_11B_CHANNEL_END) + band |= 1 << NL80211_BAND_2GHZ; + else if (info[i].channel <= SIR_11A_CHANNEL_END) + band |= 1 << NL80211_BAND_5GHZ; + } + i++; + } + + if (!present) + return 0; + + i = 0; + attr = nla_nest_start(skb, mac_id); + if (!attr) + goto error; + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID, mac_id) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND, band)) + goto error; + + intf_attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO); + if (!intf_attr) + goto error; + + while (i < conn_count) { + if (info[i].mac_id == mac_id) { + if (wlan_hdd_fill_intf_info(skb, &info[i], j)) + return -EINVAL; + j++; + } + i++; + } + + nla_nest_end(skb, intf_attr); + + nla_nest_end(skb, attr); + + return 0; +error: + hdd_err("Fill buffer with mac info failed"); + return -EINVAL; +} + + +int wlan_hdd_send_mode_change_event(void) +{ + int err; + struct hdd_context *hdd_ctx; + struct sk_buff *skb; + struct nlattr *attr; + struct connection_info info[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t conn_count, mac_id; + + hdd_enter(); + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return -EINVAL; + } + + err = wlan_hdd_validate_context(hdd_ctx); + if (0 != err) + return err; + + conn_count = policy_mgr_get_connection_info(hdd_ctx->psoc, info); + if (!conn_count) + return -EINVAL; + + skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL, + (sizeof(uint32_t) * 4) * + MAX_NUMBER_OF_CONC_CONNECTIONS + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO_INDEX, + GFP_KERNEL); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_MAC_INFO); + if (!attr) { + hdd_err("nla_nest_start failed"); + kfree_skb(skb); + return -EINVAL; + } + + for (mac_id = 0; mac_id < MAX_MAC; mac_id++) { + if (wlan_hdd_fill_mac_info(skb, info, mac_id, conn_count)) { + kfree_skb(skb); + return -EINVAL; + } + } + + nla_nest_end(skb, attr); + + cfg80211_vendor_event(skb, GFP_KERNEL); + hdd_exit(); + + return err; +} + + +/* Short name for QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_VALID_CHANNELS command */ + +#define EXTSCAN_CONFIG_MAX \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX +#define EXTSCAN_CONFIG_REQUEST_ID \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID +#define EXTSCAN_CONFIG_WIFI_BAND \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND +#define ETCAN_CONFIG_MAX_CHANNELS \ +QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS +#define EXTSCAN_RESULTS_NUM_CHANNELS \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_CHANNELS +#define EXTSCAN_RESULTS_CHANNELS \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CHANNELS + +/** + * hdd_remove_dsrc_channels () - remove dsrc chanels + * @hdd_ctx: hdd context + * @wiphy: Pointer to wireless phy + * @chan_list: channel list + * @num_channels: number of channels + * + * Return: none + */ +static void hdd_remove_dsrc_channels(struct hdd_context *hdd_ctx, + struct wiphy *wiphy, uint32_t *chan_list, + uint8_t *num_channels) +{ + uint8_t num_chan_temp = 0; + int i; + + for (i = 0; i < *num_channels; i++) { + if (!wlan_reg_is_dsrc_chan(hdd_ctx->pdev, + wlan_reg_freq_to_chan( + hdd_ctx->pdev, + chan_list[i]))) { + chan_list[num_chan_temp] = chan_list[i]; + num_chan_temp++; + } + } + *num_channels = num_chan_temp; +} + +/** + * hdd_remove_passive_channels () - remove passive channels + * @wiphy: Pointer to wireless phy + * @chan_list: channel list + * @num_channels: number of channels + * + * Return: none + */ +static void hdd_remove_passive_channels(struct wiphy *wiphy, + uint32_t *chan_list, + uint8_t *num_channels) +{ + uint8_t num_chan_temp = 0; + int i, j, k; + + for (i = 0; i < *num_channels; i++) + for (j = 0; j < HDD_NUM_NL80211_BANDS; j++) { + if (!wiphy->bands[j]) + continue; + for (k = 0; k < wiphy->bands[j]->n_channels; k++) { + if ((chan_list[i] == + wiphy->bands[j]->channels[k].center_freq) + && (!(wiphy->bands[j]->channels[k].flags & + IEEE80211_CHAN_PASSIVE_SCAN)) + ) { + chan_list[num_chan_temp] = chan_list[i]; + num_chan_temp++; + } + } + } + + *num_channels = num_chan_temp; +} + +/** + * __wlan_hdd_cfg80211_extscan_get_valid_channels () - get valid channels + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: none + */ +static int +__wlan_hdd_cfg80211_extscan_get_valid_channels(struct wiphy *wiphy, + struct wireless_dev + *wdev, const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + uint32_t chan_list[CFG_VALID_CHANNEL_LIST_LEN] = {0}; + uint8_t num_channels = 0, i, buf[256] = {0}; + struct nlattr *tb[EXTSCAN_CONFIG_MAX + + 1]; + uint32_t requestId, maxChannels; + tWifiBand wifi_band; + QDF_STATUS status; + struct sk_buff *reply_skb; + int ret, len = 0; + + /* ENTER_DEV() intentionally not used in a frequently invoked API */ + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return -EINVAL; + + if (wlan_cfg80211_nla_parse( + tb, + EXTSCAN_CONFIG_MAX, + data, data_len, wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch request Id */ + if (!tb[EXTSCAN_CONFIG_REQUEST_ID]) { + hdd_err("attr request id failed"); + return -EINVAL; + } + requestId = + nla_get_u32(tb + [EXTSCAN_CONFIG_REQUEST_ID]); + + /* Parse and fetch wifi band */ + if (!tb + [EXTSCAN_CONFIG_WIFI_BAND]) { + hdd_err("attr wifi band failed"); + return -EINVAL; + } + wifi_band = + nla_get_u32(tb + [EXTSCAN_CONFIG_WIFI_BAND]); + if (!tb + [ETCAN_CONFIG_MAX_CHANNELS]) { + hdd_err("attr max channels failed"); + return -EINVAL; + } + maxChannels = + nla_get_u32(tb + [ETCAN_CONFIG_MAX_CHANNELS]); + + if (maxChannels > CFG_VALID_CHANNEL_LIST_LEN) { + hdd_err("Max channels %d exceeded Valid channel list len %d", + maxChannels, CFG_VALID_CHANNEL_LIST_LEN); + return -EINVAL; + } + + hdd_err("Req Id: %u Wifi band: %d Max channels: %d", requestId, + wifi_band, maxChannels); + status = sme_get_valid_channels_by_band(hdd_ctx->mac_handle, + wifi_band, chan_list, + &num_channels); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("sme_get_valid_channels_by_band failed (err=%d)", + status); + return -EINVAL; + } + + num_channels = QDF_MIN(num_channels, maxChannels); + + hdd_remove_dsrc_channels(hdd_ctx, wiphy, chan_list, &num_channels); + if ((QDF_SAP_MODE == adapter->device_mode) || + !strncmp(hdd_get_fwpath(), "ap", 2)) + hdd_remove_passive_channels(wiphy, chan_list, + &num_channels); + + hdd_debug("Number of channels: %d", num_channels); + for (i = 0; i < num_channels; i++) + len += scnprintf(buf + len, sizeof(buf) - len, + "%u ", chan_list[i]); + + hdd_debug("Channels: %s", buf); + + reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) + + sizeof(u32) * + num_channels + + NLMSG_HDRLEN); + + if (reply_skb) { + if (nla_put_u32( + reply_skb, + EXTSCAN_RESULTS_NUM_CHANNELS, + num_channels) || + nla_put( + reply_skb, + EXTSCAN_RESULTS_CHANNELS, + sizeof(u32) * num_channels, chan_list)) { + hdd_err("nla put fail"); + kfree_skb(reply_skb); + return -EINVAL; + } + ret = cfg80211_vendor_cmd_reply(reply_skb); + return ret; + } + + hdd_err("valid channels: buffer alloc fail"); + return -EINVAL; +} + +#undef EXTSCAN_CONFIG_MAX +#undef EXTSCAN_CONFIG_REQUEST_ID +#undef EXTSCAN_CONFIG_WIFI_BAND +#undef ETCAN_CONFIG_MAX_CHANNELS +#undef EXTSCAN_RESULTS_NUM_CHANNELS +#undef EXTSCAN_RESULTS_CHANNELS + +/** + * wlan_hdd_cfg80211_extscan_get_valid_channels() - get ext scan valid channels + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int wlan_hdd_cfg80211_extscan_get_valid_channels( + struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_extscan_get_valid_channels(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = { + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = is_driver_dfs_capable + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_VALID_CHANNELS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_extscan_get_valid_channels + }, +#ifdef WLAN_FEATURE_STATS_EXT + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_STATS_EXT, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_stats_ext_request + }, +#endif +#ifdef FEATURE_WLAN_EXTSCAN + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_extscan_start + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_extscan_stop + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_extscan_get_capabilities + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_extscan_get_cached_results + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_extscan_set_bssid_hotlist + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_extscan_reset_bssid_hotlist + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE, + .flags = + WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_extscan_set_significant_change + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE, + .flags = + WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_extscan_reset_significant_change + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_LIST, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_epno_list + }, +#endif /* FEATURE_WLAN_EXTSCAN */ + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_ll_stats_clear + }, + + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_ll_stats_set + }, + + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_ll_stats_get + }, +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ +#ifdef FEATURE_WLAN_TDLS + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_exttdls_enable + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_exttdls_disable + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = wlan_hdd_cfg80211_exttdls_get_status + }, +#endif + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = wlan_hdd_cfg80211_get_supported_features + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_scanning_mac_oui + }, + + FEATURE_CONCURRENCY_MATRIX_VENDOR_COMMANDS + + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_disable_dfs_chan_scan + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WISA, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_handle_wisa_cmd + }, + + FEATURE_STATION_INFO_VENDOR_COMMANDS + + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DO_ACS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_do_acs + }, + + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = wlan_hdd_cfg80211_get_features + }, +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_keymgmt_set_key + }, +#endif +#ifdef FEATURE_WLAN_EXTSCAN + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_PASSPOINT_LIST, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_passpoint_list + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_RESET_PASSPOINT_LIST, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_reset_passpoint_list + }, +#endif /* FEATURE_WLAN_EXTSCAN */ + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = wlan_hdd_cfg80211_get_wifi_info + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_wifi_configuration_set + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_wifi_configuration_get + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_wifi_test_config + }, + + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAM, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_ext_roam_params + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_wifi_logger_start + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV, + .doit = wlan_hdd_cfg80211_wifi_logger_get_ring_data + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_get_preferred_freq_list + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_probable_oper_channel + }, +#ifdef WLAN_FEATURE_TSF + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TSF, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_handle_tsf_cmd + }, +#endif +#ifdef FEATURE_WLAN_TDLS + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_get_tdls_capabilities + }, +#endif +#ifdef WLAN_FEATURE_OFFLOAD_PACKETS + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_offloaded_packets + }, +#endif + FEATURE_RSSI_MONITOR_VENDOR_COMMANDS + FEATURE_OEM_DATA_VENDOR_COMMANDS + FEATURE_INTEROP_ISSUES_AP_VENDOR_COMMANDS + +#ifdef WLAN_NS_OFFLOAD + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_ns_offload + }, +#endif /* WLAN_NS_OFFLOAD */ + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = wlan_hdd_cfg80211_get_logger_supp_feature + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_vendor_scan + }, + + /* Vendor abort scan */ + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_vendor_abort_scan + }, + + /* OCB commands */ + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_ocb_set_config + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_ocb_set_utc_time + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_ocb_start_timing_advert + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_ocb_stop_timing_advert + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_ocb_get_tsf_timer + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_dcc_get_stats + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_dcc_clear_stats + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_dcc_update_ndl + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_get_link_properties + }, + + FEATURE_OTA_TEST_VENDOR_COMMANDS + +#ifdef FEATURE_LFR_SUBNET_DETECTION + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_gateway_params + }, +#endif /* FEATURE_LFR_SUBNET_DETECTION */ + + FEATURE_TX_POWER_VENDOR_COMMANDS + +#ifdef FEATURE_WLAN_APF + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_apf_offload + }, +#endif /* FEATURE_WLAN_APF */ + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_acs_dfs_mode + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_STA_CONNECT_ROAM_POLICY, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_sta_roam_policy + }, +#ifdef FEATURE_WLAN_CH_AVOID + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_avoid_freq + }, +#endif + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_sap_configuration_set + }, + + FEATURE_P2P_LISTEN_OFFLOAD_VENDOR_COMMANDS + + FEATURE_SAP_COND_CHAN_SWITCH_VENDOR_COMMANDS + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_get_wakelock_stats + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_get_bus_size + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_update_vendor_channel + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SETBAND, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_setband + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GETBAND, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_getband, + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAMING, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_fast_roaming + }, +#ifdef WLAN_FEATURE_DISA + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_encrypt_decrypt_msg + }, +#endif +#ifdef FEATURE_WLAN_TDLS + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_configure_tdls_mode + }, +#endif + FEATURE_SAR_LIMITS_VENDOR_COMMANDS + BCN_RECV_FEATURE_VENDOR_COMMANDS + + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_sar_power_limits + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_trace_level + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_ll_stats_ext_set_param + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_set_nud_stats + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_get_nud_stats + }, + + FEATURE_BSS_TRANSITION_VENDOR_COMMANDS + FEATURE_SPECTRAL_SCAN_VENDOR_COMMANDS + FEATURE_CFR_VENDOR_COMMANDS + FEATURE_11AX_VENDOR_COMMANDS + + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_get_chain_rssi + }, + + FEATURE_ACTIVE_TOS_VENDOR_COMMANDS + FEATURE_NAN_VENDOR_COMMANDS + FEATURE_FW_STATE_COMMANDS + FEATURE_COEX_CONFIG_COMMANDS + FEATURE_MPTA_HELPER_COMMANDS + FEATURE_HW_CAPABILITY_COMMANDS + FEATURE_THERMAL_VENDOR_COMMANDS + FEATURE_GPIO_CFG_VENDOR_COMMANDS +}; + +struct hdd_context *hdd_cfg80211_wiphy_alloc(void) +{ + struct wiphy *wiphy; + struct hdd_context *hdd_ctx; + + hdd_enter(); + + wiphy = wiphy_new(&wlan_hdd_cfg80211_ops, sizeof(*hdd_ctx)); + if (!wiphy) { + hdd_err("failed to allocate wiphy!"); + return NULL; + } + + hdd_ctx = wiphy_priv(wiphy); + hdd_ctx->wiphy = wiphy; + + return hdd_ctx; +} + +int wlan_hdd_cfg80211_update_band(struct hdd_context *hdd_ctx, + struct wiphy *wiphy, + enum band_info new_band) +{ + int i, j; + enum channel_state channel_state; + + hdd_enter(); + + for (i = 0; i < HDD_NUM_NL80211_BANDS; i++) { + + if (!wiphy->bands[i]) + continue; + + for (j = 0; j < wiphy->bands[i]->n_channels; j++) { + struct ieee80211_supported_band *band = wiphy->bands[i]; + + channel_state = wlan_reg_get_channel_state( + hdd_ctx->pdev, + band->channels[j].hw_value); + + if (HDD_NL80211_BAND_2GHZ == i && + BAND_5G == new_band) { + /* 5G only */ +#ifdef WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY + /* Enable Social channels for P2P */ + if (WLAN_HDD_IS_SOCIAL_CHANNEL + (band->channels[j].center_freq) + && CHANNEL_STATE_ENABLE == + channel_state) + band->channels[j].flags &= + ~IEEE80211_CHAN_DISABLED; + else +#endif + band->channels[j].flags |= + IEEE80211_CHAN_DISABLED; + continue; + } else if (HDD_NL80211_BAND_5GHZ == i && + BAND_2G == new_band) { + /* 2G only */ + band->channels[j].flags |= + IEEE80211_CHAN_DISABLED; + continue; + } + + if (CHANNEL_STATE_DISABLE != channel_state) + band->channels[j].flags &= + ~IEEE80211_CHAN_DISABLED; + } + } + return 0; +} + +#if defined(CFG80211_SCAN_RANDOM_MAC_ADDR) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy) +{ + wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; +} +#else +static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy) +{ +} +#endif + +#define WLAN_HDD_MAX_NUM_CSA_COUNTERS 2 + +#if defined(CFG80211_RAND_TA_FOR_PUBLIC_ACTION_FRAME) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) +/** + * wlan_hdd_cfg80211_action_frame_randomization_init() - Randomize SA of MA + * frames + * @wiphy: Pointer to wiphy + * + * This function is used to indicate the support of source mac address + * randomization of management action frames + * + * Return: None + */ +static void +wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy) +{ + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA); +} +#else +static void +wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy) +{ +} +#endif + +#if defined(WLAN_FEATURE_FILS_SK) && \ + (defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) +static void wlan_hdd_cfg80211_set_wiphy_fils_feature(struct wiphy *wiphy) +{ + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_SK_OFFLOAD); +} +#else +static void wlan_hdd_cfg80211_set_wiphy_fils_feature(struct wiphy *wiphy) +{ +} +#endif + +#if defined (CFG80211_SCAN_DBS_CONTROL_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)) +static void wlan_hdd_cfg80211_set_wiphy_scan_flags(struct wiphy *wiphy) +{ + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_LOW_SPAN_SCAN); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_LOW_POWER_SCAN); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN); +} +#else +static void wlan_hdd_cfg80211_set_wiphy_scan_flags(struct wiphy *wiphy) +{ +} +#endif + +#if defined(CFG80211_SCAN_OCE_CAPABILITY_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)) +static void wlan_hdd_cfg80211_set_wiphy_oce_scan_flags(struct wiphy *wiphy) +{ + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME); + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP); + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE); + wiphy_ext_feature_set( + wiphy, NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION); +} +#else +static void wlan_hdd_cfg80211_set_wiphy_oce_scan_flags(struct wiphy *wiphy) +{ +} +#endif + +#if defined(WLAN_FEATURE_SAE) && \ + (defined(CFG80211_EXTERNAL_AUTH_SUPPORT) || \ + LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) +/** + * wlan_hdd_cfg80211_set_wiphy_sae_feature() - Indicates support of SAE feature + * @wiphy: Pointer to wiphy + * + * This function is used to indicate the support of SAE + * + * Return: None + */ +static void wlan_hdd_cfg80211_set_wiphy_sae_feature(struct wiphy *wiphy) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + if (ucfg_fwol_get_sae_enable(hdd_ctx->psoc)) + wiphy->features |= NL80211_FEATURE_SAE; +} +#else +static void wlan_hdd_cfg80211_set_wiphy_sae_feature(struct wiphy *wiphy) +{ +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) || \ + defined(CFG80211_DFS_OFFLOAD_BACKPORT) +static void wlan_hdd_cfg80211_set_dfs_offload_feature(struct wiphy *wiphy) +{ + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD); +} +#else +static void wlan_hdd_cfg80211_set_dfs_offload_feature(struct wiphy *wiphy) +{ + wiphy->flags |= WIPHY_FLAG_DFS_OFFLOAD; +} +#endif + +#ifdef WLAN_FEATURE_DSRC +static void wlan_hdd_get_num_dsrc_ch_and_len(struct hdd_config *hdd_cfg, + int *num_ch, int *ch_len) +{ + *num_ch = QDF_ARRAY_SIZE(hdd_channels_dot11p); + *ch_len = sizeof(hdd_channels_dot11p); +} + +static void wlan_hdd_copy_dsrc_ch(char *ch_ptr, int ch_arr_len) +{ + if (!ch_arr_len) + return; + qdf_mem_copy(ch_ptr, &hdd_channels_dot11p[0], ch_arr_len); +} + + +static void wlan_hdd_get_num_srd_ch_and_len(struct hdd_config *hdd_cfg, + int *num_ch, int *ch_len) +{ + *num_ch = 0; + *ch_len = 0; +} + +static void wlan_hdd_copy_srd_ch(char *ch_ptr, int ch_arr_len) +{ +} + +/** + * wlan_hdd_populate_srd_chan_info() - Populate SRD chan info in hdd context + * @hdd_ctx: pointer to hdd context + * @index: SRD channel beginning index in chan_info of @hdd_ctx + * + * Return: Number of SRD channels populated + */ +static uint32_t +wlan_hdd_populate_srd_chan_info(struct hdd_context *hdd_ctx, uint32_t index) +{ + return 0; +} + +#else + +static void wlan_hdd_get_num_dsrc_ch_and_len(struct hdd_config *hdd_cfg, + int *num_ch, int *ch_len) +{ + *num_ch = 0; + *ch_len = 0; +} + +static void wlan_hdd_copy_dsrc_ch(char *ch_ptr, int ch_arr_len) +{ +} + +static void wlan_hdd_get_num_srd_ch_and_len(struct hdd_config *hdd_cfg, + int *num_ch, int *ch_len) +{ + *num_ch = QDF_ARRAY_SIZE(hdd_etsi13_srd_ch); + *ch_len = sizeof(hdd_etsi13_srd_ch); +} + +static void wlan_hdd_copy_srd_ch(char *ch_ptr, int ch_arr_len) +{ + if (!ch_arr_len) + return; + qdf_mem_copy(ch_ptr, &hdd_etsi13_srd_ch[0], ch_arr_len); +} + +/** + * wlan_hdd_populate_srd_chan_info() - Populate SRD chan info in hdd context + * @hdd_ctx: pointer to hdd context + * @index: SRD channel beginning index in chan_info of @hdd_ctx + * + * Return: Number of SRD channels populated + */ +static uint32_t +wlan_hdd_populate_srd_chan_info(struct hdd_context *hdd_ctx, uint32_t index) +{ + uint32_t num_srd_ch, i; + struct scan_chan_info *chan_info; + + num_srd_ch = QDF_ARRAY_SIZE(hdd_etsi13_srd_ch); + chan_info = hdd_ctx->chan_info; + + for (i = 0; i < num_srd_ch; i++) + chan_info[index + i].freq = hdd_etsi13_srd_ch[i].center_freq; + + return num_srd_ch; +} + +#endif + +#if (defined(CONFIG_BAND_6GHZ) && defined(CFG80211_6GHZ_BAND_SUPPORTED)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +#if defined(CONFIG_BAND_6GHZ) && (defined(CFG80211_6GHZ_BAND_SUPPORTED) || \ + (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)) +static QDF_STATUS +wlan_hdd_iftype_data_alloc_6ghz(struct hdd_context *hdd_ctx) +{ + hdd_ctx->iftype_data_6g = + qdf_mem_malloc(sizeof(*hdd_ctx->iftype_data_6g)); + + if (!hdd_ctx->iftype_data_6g) + return QDF_STATUS_E_NOMEM; + + return QDF_STATUS_SUCCESS; +} + +static void +wlan_hdd_iftype_data_mem_free_6ghz(struct hdd_context *hdd_ctx) +{ + qdf_mem_free(hdd_ctx->iftype_data_6g); + hdd_ctx->iftype_data_6g = NULL; +} +#else +static inline QDF_STATUS +wlan_hdd_iftype_data_alloc_6ghz(struct hdd_context *hdd_ctx) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void +wlan_hdd_iftype_data_mem_free_6ghz(struct hdd_context *hdd_ctx) +{ +} +#endif + +static QDF_STATUS +wlan_hdd_iftype_data_alloc(struct hdd_context *hdd_ctx) +{ + hdd_ctx->iftype_data_2g = + qdf_mem_malloc(sizeof(*hdd_ctx->iftype_data_2g)); + + if (!hdd_ctx->iftype_data_2g) { + hdd_err("mem alloc failed for 2g iftype data"); + return QDF_STATUS_E_NOMEM; + } + hdd_ctx->iftype_data_5g = + qdf_mem_malloc(sizeof(*hdd_ctx->iftype_data_5g)); + + if (!hdd_ctx->iftype_data_5g) { + hdd_err("mem alloc failed for 5g iftype data"); + qdf_mem_free(hdd_ctx->iftype_data_2g); + hdd_ctx->iftype_data_2g = NULL; + return QDF_STATUS_E_NOMEM; + } + + if (QDF_IS_STATUS_ERROR(wlan_hdd_iftype_data_alloc_6ghz(hdd_ctx))) { + hdd_err("mem alloc failed for 6g iftype data"); + qdf_mem_free(hdd_ctx->iftype_data_5g); + qdf_mem_free(hdd_ctx->iftype_data_2g); + hdd_ctx->iftype_data_2g = NULL; + hdd_ctx->iftype_data_5g = NULL; + return QDF_STATUS_E_NOMEM; + } + + return QDF_STATUS_SUCCESS; +} + +static void +wlan_hdd_iftype_data_mem_free(struct hdd_context *hdd_ctx) +{ + wlan_hdd_iftype_data_mem_free_6ghz(hdd_ctx); + qdf_mem_free(hdd_ctx->iftype_data_5g); + qdf_mem_free(hdd_ctx->iftype_data_2g); + hdd_ctx->iftype_data_5g = NULL; + hdd_ctx->iftype_data_2g = NULL; +} +#else +static QDF_STATUS +wlan_hdd_iftype_data_alloc(struct hdd_context *hdd_ctx) + +{ + return QDF_STATUS_SUCCESS; +} + +static inline void +wlan_hdd_iftype_data_mem_free(struct hdd_context *hdd_ctx) +{ +} +#endif + +#if defined(WLAN_FEATURE_NAN) && \ + (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) +static void wlan_hdd_set_nan_if_mode(struct wiphy *wiphy) +{ + wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN); +} + +static void wlan_hdd_set_nan_supported_bands(struct wiphy *wiphy) +{ + wiphy->nan_supported_bands = + BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ); +} +#else +static void wlan_hdd_set_nan_if_mode(struct wiphy *wiphy) +{ +} + +static void wlan_hdd_set_nan_supported_bands(struct wiphy *wiphy) +{ +} +#endif + +/** + * wlan_hdd_update_akm_suit_info() - Populate akm suits supported by driver + * @wiphy: wiphy + * + * Return: void + */ +#if defined(CFG80211_IFTYPE_AKM_SUITES_SUPPORT) +static void +wlan_hdd_update_akm_suit_info(struct wiphy *wiphy) +{ + wiphy->iftype_akm_suites = wlan_hdd_akm_suites; + wiphy->num_iftype_akm_suites = QDF_ARRAY_SIZE(wlan_hdd_akm_suites); +} +#else +static void +wlan_hdd_update_akm_suit_info(struct wiphy *wiphy) +{ +} +#endif + +/* + * FUNCTION: wlan_hdd_cfg80211_init + * This function is called by hdd_wlan_startup() + * during initialization. + * This function is used to initialize and register wiphy structure. + */ +int wlan_hdd_cfg80211_init(struct device *dev, + struct wiphy *wiphy, struct hdd_config *config) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + uint32_t *cipher_suites; + hdd_enter(); + + /* Now bind the underlying wlan device with wiphy */ + set_wiphy_dev(wiphy, dev); + + wiphy->mgmt_stypes = wlan_hdd_txrx_stypes; + wlan_hdd_update_akm_suit_info(wiphy); + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME + | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD + | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL +#ifdef FEATURE_WLAN_STA_4ADDR_SCHEME + | WIPHY_FLAG_4ADDR_STATION +#endif + | WIPHY_FLAG_OFFCHAN_TX; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) + wiphy->wowlan = &wowlan_support_cfg80211_init; +#else + wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT; + wiphy->wowlan.n_patterns = WOWL_MAX_PTRNS_ALLOWED; + wiphy->wowlan.pattern_min_len = 1; + wiphy->wowlan.pattern_max_len = WOWL_PTRN_MAX_SIZE; +#endif + +#ifdef FEATURE_WLAN_TDLS + wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS + | WIPHY_FLAG_TDLS_EXTERNAL_SETUP; +#endif + + wiphy->features |= NL80211_FEATURE_HT_IBSS; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); +#endif + + wlan_hdd_cfg80211_set_wiphy_scan_flags(wiphy); + + wlan_scan_cfg80211_add_connected_pno_support(wiphy); + + wiphy->max_scan_ssids = MAX_SCAN_SSID; + + wiphy->max_scan_ie_len = SIR_MAC_MAX_ADD_IE_LENGTH; + + wiphy->max_acl_mac_addrs = MAX_ACL_MAC_ADDRESS; + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_ADHOC) + | BIT(NL80211_IFTYPE_P2P_CLIENT) + | BIT(NL80211_IFTYPE_P2P_GO) + | BIT(NL80211_IFTYPE_AP) + | BIT(NL80211_IFTYPE_MONITOR); + + wlan_hdd_set_nan_if_mode(wiphy); + + /* + * In case of static linked driver at the time of driver unload, + * module exit doesn't happens. Module cleanup helps in cleaning + * of static memory. + * If driver load happens statically, at the time of driver unload, + * wiphy flags don't get reset because of static memory. + * It's better not to store channel in static memory. + * The memory is for channels of struct wiphy and shouldn't be + * released during stop modules. So if it's allocated in active + * domain, the memory leak detector would catch the leak during + * stop modules. To avoid this,alloc in init domain in advance. + */ + hdd_ctx->channels_2ghz = qdf_mem_malloc(band_2_ghz_channels_size); + if (!hdd_ctx->channels_2ghz) { + hdd_err("Not enough memory to allocate channels"); + return -ENOMEM; + } + hdd_ctx->channels_5ghz = qdf_mem_malloc(band_5_ghz_chanenls_size); + if (!hdd_ctx->channels_5ghz) + goto mem_fail_5g; + + if (QDF_IS_STATUS_ERROR(wlan_hdd_iftype_data_alloc(hdd_ctx))) + goto mem_fail_iftype_data; + + /*Initialise the supported cipher suite details */ + if (ucfg_fwol_get_gcmp_enable(hdd_ctx->psoc)) { + cipher_suites = qdf_mem_malloc(sizeof(hdd_cipher_suites) + + sizeof(hdd_gcmp_cipher_suits)); + if (!cipher_suites) + goto mem_fail_cipher_suites; + wiphy->n_cipher_suites = QDF_ARRAY_SIZE(hdd_cipher_suites) + + QDF_ARRAY_SIZE(hdd_gcmp_cipher_suits); + qdf_mem_copy(cipher_suites, &hdd_cipher_suites, + sizeof(hdd_cipher_suites)); + qdf_mem_copy(cipher_suites + QDF_ARRAY_SIZE(hdd_cipher_suites), + &hdd_gcmp_cipher_suits, + sizeof(hdd_gcmp_cipher_suits)); + } else { + cipher_suites = qdf_mem_malloc(sizeof(hdd_cipher_suites)); + if (!cipher_suites) + goto mem_fail_cipher_suites; + wiphy->n_cipher_suites = QDF_ARRAY_SIZE(hdd_cipher_suites); + qdf_mem_copy(cipher_suites, &hdd_cipher_suites, + sizeof(hdd_cipher_suites)); + } + wiphy->cipher_suites = cipher_suites; + cipher_suites = NULL; + /*signal strength in mBm (100*dBm) */ + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->max_remain_on_channel_duration = MAX_REMAIN_ON_CHANNEL_DURATION; + + if (cds_get_conparam() != QDF_GLOBAL_FTM_MODE) { + wiphy->n_vendor_commands = + ARRAY_SIZE(hdd_wiphy_vendor_commands); + wiphy->vendor_commands = hdd_wiphy_vendor_commands; + + wiphy->vendor_events = wlan_hdd_cfg80211_vendor_events; + wiphy->n_vendor_events = + ARRAY_SIZE(wlan_hdd_cfg80211_vendor_events); + } + +#ifdef QCA_HT_2040_COEX + wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE; +#endif + wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; + + wiphy->features |= NL80211_FEATURE_VIF_TXPOWER; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) || \ + defined(CFG80211_BEACON_TX_RATE_CUSTOM_BACKPORT) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT); +#endif + + hdd_add_channel_switch_support(&wiphy->flags); + wiphy->max_num_csa_counters = WLAN_HDD_MAX_NUM_CSA_COUNTERS; + wlan_hdd_cfg80211_action_frame_randomization_init(wiphy); + + wlan_hdd_set_nan_supported_bands(wiphy); + + hdd_exit(); + return 0; + +mem_fail_cipher_suites: + wlan_hdd_iftype_data_mem_free(hdd_ctx); +mem_fail_iftype_data: + qdf_mem_free(hdd_ctx->channels_5ghz); + hdd_ctx->channels_5ghz = NULL; +mem_fail_5g: + hdd_err("Not enough memory to allocate channels"); + qdf_mem_free(hdd_ctx->channels_2ghz); + hdd_ctx->channels_2ghz = NULL; + + return -ENOMEM; +} + +/** + * wlan_hdd_cfg80211_deinit() - Deinit cfg80211 + * @wiphy: the wiphy to validate against + * + * this function deinit cfg80211 and cleanup the + * memory allocated in wlan_hdd_cfg80211_init also + * reset the global reg params. + * + * Return: void + */ +void wlan_hdd_cfg80211_deinit(struct wiphy *wiphy) +{ + int i; + const uint32_t *cipher_suites; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + for (i = 0; i < HDD_NUM_NL80211_BANDS; i++) { + if (wiphy->bands[i] && + (wiphy->bands[i]->channels)) + wiphy->bands[i]->channels = NULL; + } + wlan_hdd_iftype_data_mem_free(hdd_ctx); + qdf_mem_free(hdd_ctx->channels_5ghz); + qdf_mem_free(hdd_ctx->channels_2ghz); + hdd_ctx->channels_2ghz = NULL; + hdd_ctx->channels_5ghz = NULL; + + cipher_suites = wiphy->cipher_suites; + wiphy->cipher_suites = NULL; + wiphy->n_cipher_suites = 0; + qdf_mem_free((uint32_t *)cipher_suites); + cipher_suites = NULL; + hdd_reset_global_reg_params(); +} + +/** + * wlan_hdd_update_band_cap() - update capabilities for supported bands + * @hdd_ctx: HDD context + * + * this function will update capabilities for supported bands + * + * Return: void + */ +static void wlan_hdd_update_ht_cap(struct hdd_context *hdd_ctx) +{ + struct mlme_ht_capabilities_info ht_cap_info = {0}; + QDF_STATUS status; + uint32_t channel_bonding_mode; + struct ieee80211_supported_band *band_2g; + struct ieee80211_supported_band *band_5g; + uint8_t i; + + status = ucfg_mlme_get_ht_cap_info(hdd_ctx->psoc, &ht_cap_info); + if (QDF_STATUS_SUCCESS != status) + hdd_err("could not get HT capability info"); + + band_2g = hdd_ctx->wiphy->bands[HDD_NL80211_BAND_2GHZ]; + band_5g = hdd_ctx->wiphy->bands[HDD_NL80211_BAND_5GHZ]; + + if (band_2g) { + if (ht_cap_info.tx_stbc) + band_2g->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + + if (!sme_is_feature_supported_by_fw(DOT11AC)) { + band_2g->vht_cap.vht_supported = 0; + band_2g->vht_cap.cap = 0; + } + + if (!ht_cap_info.short_gi_20_mhz) + band_2g->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20; + + for (i = 0; i < hdd_ctx->num_rf_chains; i++) + band_2g->ht_cap.mcs.rx_mask[i] = 0xff; + + /* + * According to mcs_nss HT MCS parameters highest data rate for + * Nss = 1 is 150 Mbps + */ + band_2g->ht_cap.mcs.rx_highest = + cpu_to_le16(150 * hdd_ctx->num_rf_chains); + } + + if (band_5g) { + if (ht_cap_info.tx_stbc) + band_5g->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + + if (!sme_is_feature_supported_by_fw(DOT11AC)) { + band_5g->vht_cap.vht_supported = 0; + band_5g->vht_cap.cap = 0; + } + + if (!ht_cap_info.short_gi_20_mhz) + band_5g->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20; + + if (!ht_cap_info.short_gi_40_mhz) + band_5g->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; + + ucfg_mlme_get_channel_bonding_5ghz(hdd_ctx->psoc, + &channel_bonding_mode); + if (!channel_bonding_mode) + band_5g->ht_cap.cap &= + ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + for (i = 0; i < hdd_ctx->num_rf_chains; i++) + band_5g->ht_cap.mcs.rx_mask[i] = 0xff; + /* + * According to mcs_nss HT MCS parameters highest data rate for + * Nss = 1 is 150 Mbps + */ + band_5g->ht_cap.mcs.rx_highest = + cpu_to_le16(150 * hdd_ctx->num_rf_chains); + } +} + +/** + * wlan_hdd_update_band_cap_in_wiphy() - update channel flags based on band cap + * @hdd_ctx: HDD context + * + * This function updates the channel flags based on the band capability set + * in the MLME CFG + * + * Return: void + */ +static void wlan_hdd_update_band_cap_in_wiphy(struct hdd_context *hdd_ctx) +{ + int i, j; + uint32_t band_capability; + QDF_STATUS status; + struct ieee80211_supported_band *band; + + status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_capability); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get MLME Band Capability"); + return; + } + + for (i = 0; i < HDD_NUM_NL80211_BANDS; i++) { + if (!hdd_ctx->wiphy->bands[i]) + continue; + + for (j = 0; j < hdd_ctx->wiphy->bands[i]->n_channels; j++) { + band = hdd_ctx->wiphy->bands[i]; + + if (HDD_NL80211_BAND_2GHZ == i && + BIT(REG_BAND_5G) == band_capability) { + /* 5G only */ +#ifdef WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY + /* Enable social channels for P2P */ + if (WLAN_HDD_IS_SOCIAL_CHANNEL + (band->channels[j].center_freq)) + band->channels[j].flags &= + ~IEEE80211_CHAN_DISABLED; + else +#endif + band->channels[j].flags |= + IEEE80211_CHAN_DISABLED; + continue; + } else if (HDD_NL80211_BAND_5GHZ == i && + BIT(REG_BAND_2G) == band_capability) { + /* 2G only */ + band->channels[j].flags |= + IEEE80211_CHAN_DISABLED; + continue; + } + } + } +} + +#ifdef FEATURE_WLAN_ESE +/** + * wlan_hdd_update_lfr_wiphy() - update LFR flag based on configures + * @hdd_ctx: HDD context + * + * This function updates the LFR flag based on LFR configures + * + * Return: void + */ +static void wlan_hdd_update_lfr_wiphy(struct hdd_context *hdd_ctx) +{ + bool fast_transition_enabled; + bool lfr_enabled; + bool ese_enabled; + + ucfg_mlme_is_fast_transition_enabled(hdd_ctx->psoc, + &fast_transition_enabled); + ucfg_mlme_is_lfr_enabled(hdd_ctx->psoc, &lfr_enabled); + ucfg_mlme_is_ese_enabled(hdd_ctx->psoc, &ese_enabled); + if (fast_transition_enabled || lfr_enabled || ese_enabled) + hdd_ctx->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; +} +#else +static void wlan_hdd_update_lfr_wiphy(struct hdd_context *hdd_ctx) +{ + bool fast_transition_enabled; + bool lfr_enabled; + + ucfg_mlme_is_fast_transition_enabled(hdd_ctx->psoc, + &fast_transition_enabled); + ucfg_mlme_is_lfr_enabled(hdd_ctx->psoc, &lfr_enabled); + if (fast_transition_enabled || lfr_enabled) + hdd_ctx->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; +} +#endif + +/* + * In this function, wiphy structure is updated after QDF + * initialization. In wlan_hdd_cfg80211_init, only the + * default values will be initialized. The final initialization + * of all required members can be done here. + */ +void wlan_hdd_update_wiphy(struct hdd_context *hdd_ctx) +{ + int value; + bool fils_enabled, mac_spoofing_enabled; + bool dfs_master_capable = true, is_oce_sta_enabled = false; + QDF_STATUS status; + struct wiphy *wiphy = hdd_ctx->wiphy; + uint8_t allow_mcc_go_diff_bi = 0, enable_mcc = 0; + + if (!wiphy) { + hdd_err("Invalid wiphy"); + return; + } + ucfg_mlme_get_sap_max_peers(hdd_ctx->psoc, &value); + hdd_ctx->wiphy->max_ap_assoc_sta = value; + wlan_hdd_update_ht_cap(hdd_ctx); + wlan_hdd_update_band_cap_in_wiphy(hdd_ctx); + wlan_hdd_update_lfr_wiphy(hdd_ctx); + + fils_enabled = 0; + status = ucfg_mlme_get_fils_enabled_info(hdd_ctx->psoc, + &fils_enabled); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not get fils enabled info"); + if (fils_enabled) + wlan_hdd_cfg80211_set_wiphy_fils_feature(wiphy); + + status = ucfg_mlme_get_dfs_master_capability(hdd_ctx->psoc, + &dfs_master_capable); + if (QDF_IS_STATUS_SUCCESS(status) && dfs_master_capable) + wlan_hdd_cfg80211_set_dfs_offload_feature(wiphy); + + status = ucfg_mlme_get_oce_sta_enabled_info(hdd_ctx->psoc, + &is_oce_sta_enabled); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not get OCE STA enable info"); + if (is_oce_sta_enabled) + wlan_hdd_cfg80211_set_wiphy_oce_scan_flags(wiphy); + + wlan_hdd_cfg80211_set_wiphy_sae_feature(wiphy); + + if (QDF_STATUS_SUCCESS != + ucfg_policy_mgr_get_allow_mcc_go_diff_bi(hdd_ctx->psoc, + &allow_mcc_go_diff_bi)) + hdd_err("can't get mcc_go_diff_bi value, use default"); + + if (QDF_STATUS_SUCCESS != + ucfg_mlme_get_mcc_feature(hdd_ctx->psoc, &enable_mcc)) + hdd_err("can't get enable_mcc value, use default"); + + if (hdd_ctx->config->advertise_concurrent_operation) { + if (enable_mcc) { + int i; + + for (i = 0; + i < ARRAY_SIZE(wlan_hdd_iface_combination); + i++) { + if (!allow_mcc_go_diff_bi) + wlan_hdd_iface_combination[i]. + beacon_int_infra_match = true; + } + } + wiphy->n_iface_combinations = + ARRAY_SIZE(wlan_hdd_iface_combination); + wiphy->iface_combinations = wlan_hdd_iface_combination; + } + + mac_spoofing_enabled = ucfg_scan_is_mac_spoofing_enabled(hdd_ctx->psoc); + if (mac_spoofing_enabled) + wlan_hdd_cfg80211_scan_randomization_init(wiphy); +} + +/** + * wlan_hdd_update_11n_mode - update 11n mode in hdd cfg + * @cfg: hdd cfg + * + * this function update 11n mode in hdd cfg + * + * Return: void + */ +void wlan_hdd_update_11n_mode(struct hdd_context *hdd_ctx) +{ + struct hdd_config *cfg = hdd_ctx->config; + + if (sme_is_feature_supported_by_fw(DOT11AC)) { + hdd_debug("support 11ac"); + } else { + hdd_debug("not support 11ac"); + if ((cfg->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY) || + (cfg->dot11Mode == eHDD_DOT11_MODE_11ac)) { + cfg->dot11Mode = eHDD_DOT11_MODE_11n; + ucfg_mlme_set_sap_11ac_override(hdd_ctx->psoc, 0); + ucfg_mlme_set_go_11ac_override(hdd_ctx->psoc, 0); + } + } +} + +QDF_STATUS wlan_hdd_update_wiphy_supported_band(struct hdd_context *hdd_ctx) +{ + int len_5g_ch, num_ch; + int num_dsrc_ch, len_dsrc_ch, num_srd_ch, len_srd_ch; + bool is_vht_for_24ghz = false; + QDF_STATUS status; + struct hdd_config *cfg = hdd_ctx->config; + struct wiphy *wiphy = hdd_ctx->wiphy; + + if (wiphy->registered) + return QDF_STATUS_SUCCESS; + + if (hdd_is_2g_supported(hdd_ctx)) { + if (!hdd_ctx->channels_2ghz) + return QDF_STATUS_E_NOMEM; + wiphy->bands[HDD_NL80211_BAND_2GHZ] = &wlan_hdd_band_2_4_ghz; + wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels = + hdd_ctx->channels_2ghz; + qdf_mem_copy(wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels, + &hdd_channels_2_4_ghz[0], + sizeof(hdd_channels_2_4_ghz)); + + status = ucfg_mlme_get_vht_for_24ghz(hdd_ctx->psoc, + &is_vht_for_24ghz); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not get VHT capability"); + + if (is_vht_for_24ghz && + sme_is_feature_supported_by_fw(DOT11AC) && + (cfg->dot11Mode == eHDD_DOT11_MODE_AUTO || + cfg->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY || + cfg->dot11Mode == eHDD_DOT11_MODE_11ac || + cfg->dot11Mode == eHDD_DOT11_MODE_11ax || + cfg->dot11Mode == eHDD_DOT11_MODE_11ax_ONLY)) + wlan_hdd_band_2_4_ghz.vht_cap.vht_supported = 1; + } + if (!hdd_is_5g_supported(hdd_ctx) || + (eHDD_DOT11_MODE_11b == cfg->dot11Mode) || + (eHDD_DOT11_MODE_11g == cfg->dot11Mode) || + (eHDD_DOT11_MODE_11b_ONLY == cfg->dot11Mode) || + (eHDD_DOT11_MODE_11g_ONLY == cfg->dot11Mode)) + return QDF_STATUS_SUCCESS; + + if (!hdd_ctx->channels_5ghz) + return QDF_STATUS_E_NOMEM; + wiphy->bands[HDD_NL80211_BAND_5GHZ] = &wlan_hdd_band_5_ghz; + wiphy->bands[HDD_NL80211_BAND_5GHZ]->channels = hdd_ctx->channels_5ghz; + wlan_hdd_get_num_dsrc_ch_and_len(cfg, &num_dsrc_ch, &len_dsrc_ch); + wlan_hdd_get_num_srd_ch_and_len(cfg, &num_srd_ch, &len_srd_ch); + num_ch = QDF_ARRAY_SIZE(hdd_channels_5_ghz) + num_dsrc_ch + num_srd_ch; + len_5g_ch = sizeof(hdd_channels_5_ghz); + + wiphy->bands[HDD_NL80211_BAND_5GHZ]->n_channels = num_ch; + qdf_mem_copy(wiphy->bands[HDD_NL80211_BAND_5GHZ]->channels, + &hdd_channels_5_ghz[0], len_5g_ch); + if (num_dsrc_ch) + wlan_hdd_copy_dsrc_ch((char *)wiphy->bands[ + HDD_NL80211_BAND_5GHZ]->channels + + len_5g_ch, len_dsrc_ch); + if (num_srd_ch) + wlan_hdd_copy_srd_ch((char *)wiphy->bands[ + HDD_NL80211_BAND_5GHZ]->channels + + len_5g_ch, len_srd_ch); + + if (cfg->dot11Mode != eHDD_DOT11_MODE_AUTO && + cfg->dot11Mode != eHDD_DOT11_MODE_11ac && + cfg->dot11Mode != eHDD_DOT11_MODE_11ac_ONLY && + cfg->dot11Mode != eHDD_DOT11_MODE_11ax && + cfg->dot11Mode != eHDD_DOT11_MODE_11ax_ONLY) + wlan_hdd_band_5_ghz.vht_cap.vht_supported = 0; + + if (cfg->dot11Mode == eHDD_DOT11_MODE_AUTO || + cfg->dot11Mode == eHDD_DOT11_MODE_11ax || + cfg->dot11Mode == eHDD_DOT11_MODE_11ax_ONLY) + hdd_init_6ghz(hdd_ctx); + + return QDF_STATUS_SUCCESS; +} + +/* In this function we are registering wiphy. */ +int wlan_hdd_cfg80211_register(struct wiphy *wiphy) +{ + hdd_enter(); + /* Register our wiphy dev with cfg80211 */ + if (0 > wiphy_register(wiphy)) { + hdd_err("wiphy register failed"); + return -EIO; + } + + hdd_exit(); + return 0; +} + +/* This function registers for all frame which supplicant is interested in */ +int wlan_hdd_cfg80211_register_frames(struct hdd_adapter *adapter) +{ + mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); + /* Register for all P2P action, public action etc frames */ + uint16_t type = (SIR_MAC_MGMT_FRAME << 2) | (SIR_MAC_MGMT_ACTION << 4); + QDF_STATUS status; + + hdd_enter(); + if (adapter->device_mode == QDF_FTM_MODE) { + hdd_info("No need to register frames in FTM mode"); + return 0; + } + + /* Register frame indication call back */ + status = sme_register_mgmt_frame_ind_callback(mac_handle, + hdd_indicate_mgmt_frame); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to register hdd_indicate_mgmt_frame"); + goto ret_status; + } + + /* Right now we are registering these frame when driver is getting + * initialized. Once we will move to 2.6.37 kernel, in which we have + * frame register ops, we will move this code as a part of that + */ + + /* GAS Initial Request */ + status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_INITIAL_REQ, + GAS_INITIAL_REQ_SIZE); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to register GAS_INITIAL_REQ"); + goto ret_status; + } + + /* GAS Initial Response */ + status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_INITIAL_RSP, + GAS_INITIAL_RSP_SIZE); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to register GAS_INITIAL_RSP"); + goto dereg_gas_initial_req; + } + + /* GAS Comeback Request */ + status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_COMEBACK_REQ, + GAS_COMEBACK_REQ_SIZE); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to register GAS_COMEBACK_REQ"); + goto dereg_gas_initial_rsp; + } + + /* GAS Comeback Response */ + status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_COMEBACK_RSP, + GAS_COMEBACK_RSP_SIZE); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to register GAS_COMEBACK_RSP"); + goto dereg_gas_comeback_req; + } + + /* WNM BSS Transition Request frame */ + status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) WNM_BSS_ACTION_FRAME, + WNM_BSS_ACTION_FRAME_SIZE); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to register WNM_BSS_ACTION_FRAME"); + goto dereg_gas_comeback_rsp; + } + + /* WNM-Notification */ + status = sme_register_mgmt_frame(mac_handle, adapter->vdev_id, type, + (uint8_t *) WNM_NOTIFICATION_FRAME, + WNM_NOTIFICATION_FRAME_SIZE); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to register WNM_NOTIFICATION_FRAME"); + goto dereg_wnm_bss_action_frm; + } + + return 0; + +dereg_wnm_bss_action_frm: + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) WNM_BSS_ACTION_FRAME, + WNM_BSS_ACTION_FRAME_SIZE); +dereg_gas_comeback_rsp: + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_COMEBACK_RSP, + GAS_COMEBACK_RSP_SIZE); +dereg_gas_comeback_req: + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_COMEBACK_REQ, + GAS_COMEBACK_REQ_SIZE); +dereg_gas_initial_rsp: + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_INITIAL_RSP, + GAS_INITIAL_RSP_SIZE); +dereg_gas_initial_req: + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_INITIAL_REQ, + GAS_INITIAL_REQ_SIZE); +ret_status: + return qdf_status_to_os_return(status); +} + +void wlan_hdd_cfg80211_deregister_frames(struct hdd_adapter *adapter) +{ + mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); + /* Deregister for all P2P action, public action etc frames */ + uint16_t type = (SIR_MAC_MGMT_FRAME << 2) | (SIR_MAC_MGMT_ACTION << 4); + + hdd_enter(); + + /* Right now we are registering these frame when driver is getting + * initialized. Once we will move to 2.6.37 kernel, in which we have + * frame register ops, we will move this code as a part of that + */ + + /* GAS Initial Request */ + + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_INITIAL_REQ, + GAS_INITIAL_REQ_SIZE); + + /* GAS Initial Response */ + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_INITIAL_RSP, + GAS_INITIAL_RSP_SIZE); + + /* GAS Comeback Request */ + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_COMEBACK_REQ, + GAS_COMEBACK_REQ_SIZE); + + /* GAS Comeback Response */ + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) GAS_COMEBACK_RSP, + GAS_COMEBACK_RSP_SIZE); + + /* P2P Public Action */ + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) P2P_PUBLIC_ACTION_FRAME, + P2P_PUBLIC_ACTION_FRAME_SIZE); + + /* P2P Action */ + sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, + (uint8_t *) P2P_ACTION_FRAME, + P2P_ACTION_FRAME_SIZE); + + /* WNM-Notification */ + sme_deregister_mgmt_frame(mac_handle, adapter->vdev_id, type, + (uint8_t *) WNM_NOTIFICATION_FRAME, + WNM_NOTIFICATION_FRAME_SIZE); +} + +bool wlan_hdd_is_ap_supports_immediate_power_save(uint8_t *ies, int length) +{ + const uint8_t *vendor_ie; + + if (length < 2) { + hdd_debug("bss size is less than expected"); + return true; + } + if (!ies) { + hdd_debug("invalid IE pointer"); + return true; + } + vendor_ie = wlan_get_vendor_ie_ptr_from_oui(VENDOR1_AP_OUI_TYPE, + VENDOR1_AP_OUI_TYPE_SIZE, ies, length); + if (vendor_ie) { + hdd_debug("AP can't support immediate powersave. defer it"); + return false; + } + return true; +} + +/* + * FUNCTION: wlan_hdd_validate_operation_channel + * called by wlan_hdd_cfg80211_start_bss() and + * wlan_hdd_set_channel() + * This function validates whether given channel is part of valid + * channel list. + */ +QDF_STATUS wlan_hdd_validate_operation_channel(struct hdd_adapter *adapter, + uint32_t ch_freq) +{ + bool value = 0; + uint32_t i; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct regulatory_channel *cur_chan_list; + QDF_STATUS status = QDF_STATUS_E_INVAL; + + status = ucfg_mlme_get_sap_allow_all_channels(hdd_ctx->psoc, &value); + if (status != QDF_STATUS_SUCCESS) + hdd_err("Unable to fetch sap allow all channels"); + + if (value) { + /* Validate the channel */ + for (i = CHAN_ENUM_2412; i < NUM_CHANNELS; i++) { + if (ch_freq == WLAN_REG_CH_TO_FREQ(i)) { + status = QDF_STATUS_SUCCESS; + break; + } + } + } else { + cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * + sizeof(struct regulatory_channel)); + if (!cur_chan_list) + return QDF_STATUS_E_NOMEM; + + if (wlan_reg_get_current_chan_list( + hdd_ctx->pdev, cur_chan_list) != QDF_STATUS_SUCCESS) { + qdf_mem_free(cur_chan_list); + return QDF_STATUS_E_INVAL; + } + + for (i = 0; i < NUM_CHANNELS; i++) { + if (ch_freq != cur_chan_list[i].center_freq) + continue; + if (cur_chan_list[i].state != CHANNEL_STATE_DISABLE && + cur_chan_list[i].state != CHANNEL_STATE_INVALID) + status = QDF_STATUS_SUCCESS; + break; + } + qdf_mem_free(cur_chan_list); + } + + return status; + +} + +static int __wlan_hdd_cfg80211_change_bss(struct wiphy *wiphy, + struct net_device *dev, + struct bss_parameters *params) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret = 0; + QDF_STATUS qdf_ret_status; + mac_handle_t mac_handle; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_CHANGE_BSS, + adapter->vdev_id, params->ap_isolate); + + hdd_debug("Device_mode %s(%d), ap_isolate = %d", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode, params->ap_isolate); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + if (!(adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE)) { + return -EOPNOTSUPP; + } + + /* ap_isolate == -1 means that in change bss, upper layer doesn't + * want to update this parameter + */ + if (-1 != params->ap_isolate) { + adapter->session.ap.disable_intrabss_fwd = + !!params->ap_isolate; + + mac_handle = hdd_ctx->mac_handle; + qdf_ret_status = sme_ap_disable_intra_bss_fwd(mac_handle, + adapter->vdev_id, + adapter->session. + ap. + disable_intrabss_fwd); + if (!QDF_IS_STATUS_SUCCESS(qdf_ret_status)) + ret = -EINVAL; + + ucfg_ipa_set_ap_ibss_fwd(hdd_ctx->pdev, + adapter->session.ap. + disable_intrabss_fwd); + } + + hdd_exit(); + return ret; +} + +/** + * hdd_change_adapter_mode() - change @adapter's operating mode to @new_mode + * @adapter: the adapter to change modes on + * @new_mode: the new operating mode to change to + * + * Return: Errno + */ +static int hdd_change_adapter_mode(struct hdd_adapter *adapter, + enum QDF_OPMODE new_mode) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct net_device *netdev = adapter->dev; + struct hdd_config *config = hdd_ctx->config; + struct csr_roam_profile *roam_profile; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + hdd_enter(); + + hdd_stop_adapter(hdd_ctx, adapter); + hdd_deinit_adapter(hdd_ctx, adapter, true); + adapter->device_mode = new_mode; + memset(&adapter->session, 0, sizeof(adapter->session)); + hdd_set_station_ops(netdev); + + roam_profile = hdd_roam_profile(adapter); + roam_profile->pAddIEScan = adapter->scan_info.scan_add_ie.addIEdata; + roam_profile->nAddIEScanLength = adapter->scan_info.scan_add_ie.length; + + if (new_mode == QDF_IBSS_MODE) { + status = hdd_start_station_adapter(adapter); + roam_profile->BSSType = eCSR_BSS_TYPE_START_IBSS; + roam_profile->phyMode = + hdd_cfg_xlate_to_csr_phy_mode(config->dot11Mode); + } + + hdd_exit(); + + return qdf_status_to_os_return(status); +} + +static int wlan_hdd_cfg80211_change_bss(struct wiphy *wiphy, + struct net_device *dev, + struct bss_parameters *params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_change_bss(wiphy, dev, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static bool hdd_is_client_mode(enum QDF_OPMODE mode) +{ + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_DEVICE_MODE: + case QDF_IBSS_MODE: + return true; + default: + return false; + } +} + +static bool hdd_is_ap_mode(enum QDF_OPMODE mode) +{ + switch (mode) { + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + return true; + default: + return false; + } +} + +/** + * __wlan_hdd_cfg80211_change_iface() - change interface cfg80211 op + * @wiphy: Pointer to the wiphy structure + * @ndev: Pointer to the net device + * @type: Interface type + * @flags: Flags for change interface + * @params: Pointer to change interface parameters + * + * Return: 0 for success, error number on failure. + */ +static int __wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + struct hdd_context *hdd_ctx; + bool iff_up = ndev->flags & IFF_UP; + enum QDF_OPMODE new_mode; + bool ap_random_bssid_enabled; + QDF_STATUS status; + int errno; + + hdd_enter(); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (wlan_hdd_check_mon_concurrency()) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_CHANGE_IFACE, + adapter->vdev_id, type); + + status = hdd_nl_to_qdf_iface_type(type, &new_mode); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + /* A userspace issue leads to it sending a 'change to station mode' + * request on a "p2p" device, expecting the driver do execute a 'change + * to p2p-device mode' request instead. The (unfortunate) work around + * here is implemented by overriding the new mode if the net_device name + * starts with "p2p" and the requested mode was station. + */ + if (strnstr(ndev->name, "p2p", 3) && new_mode == QDF_STA_MODE) + new_mode = QDF_P2P_DEVICE_MODE; + + hdd_debug("Changing mode for '%s' from %s to %s", + ndev->name, + qdf_opmode_str(adapter->device_mode), + qdf_opmode_str(new_mode)); + + errno = hdd_trigger_psoc_idle_restart(hdd_ctx); + if (errno) { + hdd_err("Failed to restart psoc; errno:%d", errno); + return -EINVAL; + } + + /* Reset the current device mode bit mask */ + policy_mgr_clear_concurrency_mode(hdd_ctx->psoc, adapter->device_mode); + + if (hdd_is_client_mode(adapter->device_mode)) { + if (hdd_is_client_mode(new_mode)) { + if (new_mode == QDF_IBSS_MODE) { + hdd_deregister_hl_netdev_fc_timer(adapter); + hdd_deregister_tx_flow_control(adapter); + } + + errno = hdd_change_adapter_mode(adapter, new_mode); + if (errno) { + hdd_err("change intf mode fail %d", errno); + goto err; + } + } else if (hdd_is_ap_mode(new_mode)) { + if (new_mode == QDF_P2P_GO_MODE) + wlan_hdd_cancel_existing_remain_on_channel + (adapter); + + hdd_stop_adapter(hdd_ctx, adapter); + hdd_deinit_adapter(hdd_ctx, adapter, true); + memset(&adapter->session, 0, sizeof(adapter->session)); + adapter->device_mode = new_mode; + + status = ucfg_mlme_get_ap_random_bssid_enable( + hdd_ctx->psoc, + &ap_random_bssid_enabled); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + if (adapter->device_mode == QDF_SAP_MODE && + ap_random_bssid_enabled) { + /* To meet Android requirements create + * a randomized MAC address of the + * form 02:1A:11:Fx:xx:xx + */ + get_random_bytes(&ndev->dev_addr[3], 3); + ndev->dev_addr[0] = 0x02; + ndev->dev_addr[1] = 0x1A; + ndev->dev_addr[2] = 0x11; + ndev->dev_addr[3] |= 0xF0; + memcpy(adapter->mac_addr.bytes, ndev->dev_addr, + QDF_MAC_ADDR_SIZE); + pr_info("wlan: Generated HotSpot BSSID " + QDF_MAC_ADDR_FMT "\n", + QDF_MAC_ADDR_REF(ndev->dev_addr)); + } + + hdd_set_ap_ops(adapter->dev); + } else { + hdd_err("Changing to device mode '%s' is not supported", + qdf_opmode_str(new_mode)); + errno = -EOPNOTSUPP; + goto err; + } + } else if (hdd_is_ap_mode(adapter->device_mode)) { + if (hdd_is_client_mode(new_mode)) { + errno = hdd_change_adapter_mode(adapter, new_mode); + if (errno) { + hdd_err("change mode fail %d", errno); + goto err; + } + } else if (hdd_is_ap_mode(new_mode)) { + adapter->device_mode = new_mode; + + /* avoid starting the adapter, since it never stopped */ + iff_up = false; + } else { + hdd_err("Changing to device mode '%s' is not supported", + qdf_opmode_str(new_mode)); + errno = -EOPNOTSUPP; + goto err; + } + } else { + hdd_err("Changing from device mode '%s' is not supported", + qdf_opmode_str(adapter->device_mode)); + errno = -EOPNOTSUPP; + goto err; + } + + /* restart the adapter if it was up before the change iface request */ + if (iff_up) { + errno = hdd_start_adapter(adapter); + if (errno) { + hdd_err("Failed to start adapter"); + errno = -EINVAL; + goto err; + } + } + + ndev->ieee80211_ptr->iftype = type; + hdd_lpass_notify_mode_change(adapter); +err: + /* Set bitmask based on updated value */ + policy_mgr_set_concurrency_mode(hdd_ctx->psoc, adapter->device_mode); + + hdd_exit(); + + return errno; +} + +static int _wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *net_dev, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_change_iface(wiphy, net_dev, type, + flags, params); + + osif_vdev_sync_trans_stop(vdev_sync); + + return errno; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) +/** + * wlan_hdd_cfg80211_change_iface() - change interface cfg80211 op + * @wiphy: Pointer to the wiphy structure + * @ndev: Pointer to the net device + * @type: Interface type + * @flags: Flags for change interface + * @params: Pointer to change interface parameters + * + * Return: 0 for success, error number on failure. + */ +static int wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + return _wlan_hdd_cfg80211_change_iface(wiphy, ndev, type, + flags, params); +} +#else +static int wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_iftype type, + struct vif_params *params) +{ + return _wlan_hdd_cfg80211_change_iface(wiphy, ndev, type, + ¶ms->flags, params); +} +#endif /* KERNEL_VERSION(4, 12, 0) */ + +QDF_STATUS wlan_hdd_send_sta_authorized_event( + struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + const struct qdf_mac_addr *mac_addr) +{ + struct sk_buff *vendor_event; + QDF_STATUS status; + struct nl80211_sta_flag_update sta_flags; + + hdd_enter(); + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return QDF_STATUS_E_INVAL; + } + + vendor_event = + cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, &adapter->wdev, sizeof(sta_flags) + + QDF_MAC_ADDR_SIZE + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES_INDEX, + GFP_KERNEL); + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_zero(&sta_flags, sizeof(sta_flags)); + + sta_flags.mask |= BIT(NL80211_STA_FLAG_AUTHORIZED); + sta_flags.set = true; + + status = nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS, + sizeof(struct nl80211_sta_flag_update), + &sta_flags); + if (status) { + hdd_err("STA flag put fails"); + kfree_skb(vendor_event); + return QDF_STATUS_E_FAILURE; + } + status = nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR, + QDF_MAC_ADDR_SIZE, mac_addr->bytes); + if (status) { + hdd_err("STA MAC put fails"); + kfree_skb(vendor_event); + return QDF_STATUS_E_FAILURE; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + + hdd_exit(); + return QDF_STATUS_SUCCESS; +} + +/** + * __wlan_hdd_change_station() - change station + * @wiphy: Pointer to the wiphy structure + * @dev: Pointer to the net device. + * @mac: bssid + * @params: Pointer to station parameters + * + * Return: 0 for success, error number on failure. + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +static int __wlan_hdd_change_station(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *mac, + struct station_parameters *params) +#else +static int __wlan_hdd_change_station(struct wiphy *wiphy, + struct net_device *dev, + uint8_t *mac, + struct station_parameters *params) +#endif +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + struct hdd_station_ctx *sta_ctx; + struct hdd_ap_ctx *ap_ctx; + struct qdf_mac_addr sta_macaddr; + int ret; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CHANGE_STATION, + adapter->vdev_id, params->listen_interval); + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + qdf_mem_copy(sta_macaddr.bytes, mac, QDF_MAC_ADDR_SIZE); + + if ((adapter->device_mode == QDF_SAP_MODE) || + (adapter->device_mode == QDF_P2P_GO_MODE)) { + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + /* + * For Encrypted SAP session, this will be done as + * part of eSAP_STA_SET_KEY_EVENT + */ + if (ap_ctx->encryption_type != + eCSR_ENCRYPT_TYPE_NONE) { + hdd_debug("Encrypt type %d, not setting peer authorized now", + ap_ctx->encryption_type); + return 0; + } + + status = + hdd_softap_change_sta_state(adapter, + &sta_macaddr, + OL_TXRX_PEER_STATE_AUTH); + + if (status != QDF_STATUS_SUCCESS) { + hdd_debug("Not able to change TL state to AUTHENTICATED"); + return -EINVAL; + } + status = wlan_hdd_send_sta_authorized_event( + adapter, + hdd_ctx, + &sta_macaddr); + if (status != QDF_STATUS_SUCCESS) { + return -EINVAL; + } + } + } else if ((adapter->device_mode == QDF_STA_MODE) || + (adapter->device_mode == QDF_P2P_CLIENT_MODE)) { + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) { +#if defined(FEATURE_WLAN_TDLS) + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + ret = wlan_cfg80211_tdls_update_peer(vdev, mac, params); + hdd_objmgr_put_vdev(vdev); +#endif + } + } + hdd_exit(); + return ret; +} + +/** + * wlan_hdd_change_station() - cfg80211 change station handler function + * @wiphy: Pointer to the wiphy structure + * @dev: Pointer to the net device. + * @mac: bssid + * @params: Pointer to station parameters + * + * This is the cfg80211 change station handler function which invokes + * the internal function @__wlan_hdd_change_station with + * SSR protection. + * + * Return: 0 for success, error number on failure. + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) || defined(WITH_BACKPORTS) +static int wlan_hdd_change_station(struct wiphy *wiphy, + struct net_device *dev, + const u8 *mac, + struct station_parameters *params) +#else +static int wlan_hdd_change_station(struct wiphy *wiphy, + struct net_device *dev, + u8 *mac, + struct station_parameters *params) +#endif +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_change_station(wiphy, dev, mac, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef FEATURE_WLAN_ESE +static bool hdd_is_krk_enc_type(uint32_t cipher_type) +{ + if (cipher_type == WLAN_CIPHER_SUITE_KRK) + return true; + + return false; +} +#else +static bool hdd_is_krk_enc_type(uint32_t cipher_type) +{ + return false; +} +#endif + +#if defined(FEATURE_WLAN_ESE) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +static bool hdd_is_btk_enc_type(uint32_t cipher_type) +{ + if (cipher_type == WLAN_CIPHER_SUITE_BTK) + return true; + + return false; +} +#else +static bool hdd_is_btk_enc_type(uint32_t cipher_type) +{ + return false; +} +#endif + +#ifdef QCA_IBSS_SUPPORT +/** + * wlan_hdd_add_key_ibss() - API to add IBSS key + * @adapter: Pointer to adapter + * @pairwise: need to add key pairwise + * @key_index: key index + * @mac_addr: Pointer to mac_addr + * @params: Pointer to key params + * @key_already_installed: pointer to key already installed state + * + * This API will add IBSS key for given mac address. + * + * Return: 0 for success, error number on failure. + */ +static int wlan_hdd_add_key_ibss(struct hdd_adapter *adapter, + bool pairwise, u8 key_index, + const u8 *mac_addr, struct key_params *params, + bool *key_already_installed) +{ + struct wlan_objmgr_vdev *vdev; + int errno; + + if (pairwise) + return 0; + /* if a key is already installed, block all subsequent ones */ + if (adapter->session.station.ibss_enc_key_installed) { + hdd_debug("IBSS key installed already"); + *key_already_installed = true; + return 0; + } + /*Set the group key */ + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + errno = wlan_cfg80211_crypto_add_key(vdev, WLAN_CRYPTO_KEY_TYPE_GROUP, + key_index, false); + if (errno) { + hdd_err("add_ibss_key failed, errno: %d", errno); + hdd_objmgr_put_vdev(vdev); + return errno; + } + /* Save the keys here and call set_key for setting + * the PTK after peer joins the IBSS network + */ + wlan_cfg80211_store_key(vdev, key_index, WLAN_CRYPTO_KEY_TYPE_UNICAST, + mac_addr, params); + hdd_objmgr_put_vdev(vdev); + adapter->session.station.ibss_enc_key_installed = 1; + + return 0; +} +#else +/** + * wlan_hdd_add_key_ibss() - API to add IBSS key + * @adapter: Pointer to adapter + * @pairwise: need to add key pairwise + * @key_index: key index + * @mac_addr: Pointer to mac_addr + * @params: Pointer to key params + * @key_already_installed: pointer to key already installed state + * + * This function is dummy + * + * Return: 0 + */ +static inline int +wlan_hdd_add_key_ibss(struct hdd_adapter *adapter, + bool pairwise, u8 key_index, + const u8 *mac_addr, struct key_params *params, + bool *key_already_installed) +{ + return 0; +} +#endif + +static int wlan_hdd_add_key_sap(struct hdd_adapter *adapter, + bool pairwise, u8 key_index, + enum wlan_crypto_cipher_type cipher) +{ + struct wlan_objmgr_vdev *vdev; + int errno = 0; + struct hdd_hostapd_state *hostapd_state = + WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + /* Do not send install key when sap restart is in progress. If there is + * critical channel request handling going on, fw will stop that request + * and will not send restart resposne + */ + if (wlan_vdev_is_restart_progress(vdev) == QDF_STATUS_SUCCESS) { + hdd_err("vdev: %d restart in progress", wlan_vdev_get_id(vdev)); + hdd_objmgr_put_vdev(vdev); + return -EINVAL; + } + + if (hostapd_state->bss_state == BSS_START) { + errno = + wlan_cfg80211_crypto_add_key(vdev, + (pairwise ? + WLAN_CRYPTO_KEY_TYPE_UNICAST : + WLAN_CRYPTO_KEY_TYPE_GROUP), + key_index, true); + if (!errno) + wma_update_set_key(adapter->vdev_id, pairwise, + key_index, cipher); + } + hdd_objmgr_put_vdev(vdev); + + return errno; +} + +static int wlan_hdd_add_key_sta(struct hdd_adapter *adapter, + bool pairwise, u8 key_index, + mac_handle_t mac_handle, bool *ft_mode) +{ + struct wlan_objmgr_vdev *vdev; + int errno; + QDF_STATUS status; + + /* The supplicant may attempt to set the PTK once + * pre-authentication is done. Save the key in the + * UMAC and include it in the ADD BSS request + */ + status = sme_check_ft_status(mac_handle, adapter->vdev_id); + if (status == QDF_STATUS_SUCCESS) { + *ft_mode = true; + return 0; + } + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + errno = wlan_cfg80211_crypto_add_key(vdev, (pairwise ? + WLAN_CRYPTO_KEY_TYPE_UNICAST : + WLAN_CRYPTO_KEY_TYPE_GROUP), + key_index, true); + hdd_objmgr_put_vdev(vdev); + if (!errno && adapter->send_mode_change) { + wlan_hdd_send_mode_change_event(); + adapter->send_mode_change = false; + } + + return errno; +} + +static int __wlan_hdd_cfg80211_add_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, + struct key_params *params) +{ + struct hdd_context *hdd_ctx; + mac_handle_t mac_handle; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + struct wlan_objmgr_vdev *vdev; + bool key_already_installed = false, ft_mode = false; + enum wlan_crypto_cipher_type cipher; + int errno; + struct qdf_mac_addr mac_address; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_ADD_KEY, + adapter->vdev_id, params->key_len); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + hdd_debug("converged Device_mode %s(%d) index %d, pairwise %d", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode, key_index, pairwise); + mac_handle = hdd_ctx->mac_handle; + + if (hdd_is_btk_enc_type(params->cipher)) + return sme_add_key_btk(mac_handle, adapter->vdev_id, + params->key, params->key_len); + if (hdd_is_krk_enc_type(params->cipher)) + return sme_add_key_krk(mac_handle, adapter->vdev_id, + params->key, params->key_len); + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + if (!pairwise && ((adapter->device_mode == QDF_STA_MODE) || + (adapter->device_mode == QDF_P2P_CLIENT_MODE))) { + qdf_mem_copy(mac_address.bytes, + adapter->session.station.conn_info.bssid.bytes, + QDF_MAC_ADDR_SIZE); + } else { + if (mac_addr) + qdf_mem_copy(mac_address.bytes, mac_addr, + QDF_MAC_ADDR_SIZE); + } + errno = wlan_cfg80211_store_key(vdev, key_index, + (pairwise ? + WLAN_CRYPTO_KEY_TYPE_UNICAST : + WLAN_CRYPTO_KEY_TYPE_GROUP), + mac_address.bytes, params); + hdd_objmgr_put_vdev(vdev); + if (errno) + return errno; + cipher = osif_nl_to_crypto_cipher_type(params->cipher); + if (pairwise) + wma_set_peer_ucast_cipher(mac_address.bytes, cipher); + + cdp_peer_flush_frags(cds_get_context(QDF_MODULE_ID_SOC), + wlan_vdev_get_id(vdev), mac_address.bytes); + + switch (adapter->device_mode) { + case QDF_IBSS_MODE: + errno = wlan_hdd_add_key_ibss(adapter, pairwise, key_index, + mac_addr, params, + &key_already_installed); + if (key_already_installed) + return 0; + break; + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + errno = wlan_hdd_add_key_sap(adapter, pairwise, + key_index, cipher); + break; + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + errno = wlan_hdd_add_key_sta(adapter, pairwise, key_index, + mac_handle, &ft_mode); + if (ft_mode) + return 0; + break; + default: + break; + } + if (!errno && (adapter->device_mode != QDF_SAP_MODE)) + wma_update_set_key(adapter->vdev_id, pairwise, key_index, + cipher); + hdd_exit(); + + return errno; +} + +static int wlan_hdd_cfg80211_add_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, + struct key_params *params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(ndev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_add_key(wiphy, ndev, key_index, pairwise, + mac_addr, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/* + * FUNCTION: __wlan_hdd_cfg80211_get_key + * This function is used to get the key information + */ +static int __wlan_hdd_cfg80211_get_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, + struct key_params *) + ) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + struct csr_roam_profile *roam_profile; + struct key_params params; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + hdd_debug("Device_mode %s(%d)", + qdf_opmode_str(adapter->device_mode), adapter->device_mode); + + memset(¶ms, 0, sizeof(params)); + + if (CSR_MAX_NUM_KEY <= key_index) { + hdd_err("Invalid key index: %d", key_index); + return -EINVAL; + } + + if ((adapter->device_mode == QDF_SAP_MODE) || + (adapter->device_mode == QDF_P2P_GO_MODE)) { + struct hdd_ap_ctx *ap_ctx = + WLAN_HDD_GET_AP_CTX_PTR(adapter); + + roam_profile = + wlan_sap_get_roam_profile(ap_ctx->sap_context); + } else { + roam_profile = hdd_roam_profile(adapter); + } + + if (!roam_profile) { + hdd_err("Get roam profile failed!"); + return -EINVAL; + } + + switch (roam_profile->EncryptionType.encryptionType[0]) { + case eCSR_ENCRYPT_TYPE_NONE: + params.cipher = IW_AUTH_CIPHER_NONE; + break; + + case eCSR_ENCRYPT_TYPE_WEP40_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP40: + params.cipher = WLAN_CIPHER_SUITE_WEP40; + break; + + case eCSR_ENCRYPT_TYPE_WEP104_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP104: + params.cipher = WLAN_CIPHER_SUITE_WEP104; + break; + + case eCSR_ENCRYPT_TYPE_TKIP: + params.cipher = WLAN_CIPHER_SUITE_TKIP; + break; + + case eCSR_ENCRYPT_TYPE_AES: + params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; + break; + case eCSR_ENCRYPT_TYPE_AES_GCMP: + params.cipher = WLAN_CIPHER_SUITE_GCMP; + break; + case eCSR_ENCRYPT_TYPE_AES_GCMP_256: + params.cipher = WLAN_CIPHER_SUITE_GCMP_256; + break; + default: + params.cipher = IW_AUTH_CIPHER_NONE; + break; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_GET_KEY, + adapter->vdev_id, params.cipher); + + params.key_len = roam_profile->Keys.KeyLength[key_index]; + params.seq_len = 0; + params.seq = NULL; + params.key = &roam_profile->Keys.KeyMaterial[key_index][0]; + callback(cookie, ¶ms); + + hdd_exit(); + return 0; +} + +static int wlan_hdd_cfg80211_get_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, + struct key_params *) + ) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(ndev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_key(wiphy, ndev, key_index, pairwise, + mac_addr, cookie, callback); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_del_key() - Delete the encryption key for station + * @wiphy: wiphy interface context + * @ndev: pointer to net device + * @key_index: Key index used in 802.11 frames + * @unicast: true if it is unicast key + * @multicast: true if it is multicast key + * + * This function is required for cfg80211_ops API. + * It is used to delete the key information + * Underlying hardware implementation does not have API to delete the + * encryption key. It is automatically deleted when the peer is + * removed. Hence this function currently does nothing. + * Future implementation may interprete delete key operation to + * replacing the key with a random junk value, effectively making it + * useless. + * + * Return: status code, always 0. + */ + +static int __wlan_hdd_cfg80211_del_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, + bool pairwise, const u8 *mac_addr) +{ + hdd_exit(); + return 0; +} + +/** + * wlan_hdd_cfg80211_del_key() - cfg80211 delete key handler function + * @wiphy: Pointer to wiphy structure. + * @dev: Pointer to net_device structure. + * @key_index: key index + * @pairwise: pairwise + * @mac_addr: mac address + * + * This is the cfg80211 delete key handler function which invokes + * the internal function @__wlan_hdd_cfg80211_del_key with + * SSR protection. + * + * Return: 0 for success, error number on failure. + */ +static int wlan_hdd_cfg80211_del_key(struct wiphy *wiphy, + struct net_device *dev, + u8 key_index, + bool pairwise, const u8 *mac_addr) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_del_key(wiphy, dev, key_index, + pairwise, mac_addr); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int __wlan_hdd_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, + bool unicast, bool multicast) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + struct hdd_context *hdd_ctx; + struct qdf_mac_addr bssid = QDF_MAC_ADDR_BCAST_INIT; + struct hdd_station_ctx *sta_ctx; + struct wlan_crypto_key *crypto_key; + int ret; + QDF_STATUS status; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SET_DEFAULT_KEY, + adapter->vdev_id, key_index); + + hdd_debug("Device_mode %s(%d) key_index = %d", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode, key_index); + + if (CSR_MAX_NUM_KEY <= key_index) { + hdd_err("Invalid key index: %d", key_index); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + + if (0 != ret) + return ret; + crypto_key = wlan_crypto_get_key(adapter->vdev, key_index); + if (!crypto_key) { + hdd_err("Invalid NULL key info"); + return -EINVAL; + } + hdd_debug("unicast %d, cipher %d", unicast, crypto_key->cipher_type); + if (!IS_WEP_CIPHER(crypto_key->cipher_type)) + return 0; + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (unicast) + status = + wlan_cfg80211_set_default_key(adapter->vdev, key_index, + &sta_ctx->conn_info.bssid); + else + status = wlan_cfg80211_set_default_key(adapter->vdev, key_index, + &bssid); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("ret fail status %d", ret); + return -EINVAL; + } + if ((adapter->device_mode == QDF_STA_MODE) || + (adapter->device_mode == QDF_P2P_CLIENT_MODE)) { + ret = + wlan_cfg80211_crypto_add_key(adapter->vdev, (unicast ? + WLAN_CRYPTO_KEY_TYPE_UNICAST : + WLAN_CRYPTO_KEY_TYPE_GROUP), + key_index, true); + wma_update_set_key(adapter->vdev_id, unicast, key_index, + crypto_key->cipher_type); + } + + return ret; +} + +static int wlan_hdd_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, + bool unicast, bool multicast) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(ndev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_default_key(wiphy, ndev, key_index, + unicast, multicast); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +void wlan_hdd_cfg80211_unlink_bss(struct hdd_adapter *adapter, + tSirMacAddr bssid, uint8_t *ssid, + uint8_t ssid_len) +{ + struct net_device *dev = adapter->dev; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + + __wlan_cfg80211_unlink_bss_list(wiphy, bssid, ssid, ssid_len); +} + +#ifdef WLAN_ENABLE_AGEIE_ON_SCAN_RESULTS +static inline int +wlan_hdd_get_frame_len(struct bss_description *bss_desc) +{ + return GET_IE_LEN_IN_BSS(bss_desc->length) + sizeof(qcom_ie_age); +} + +static inline void wlan_hdd_add_age_ie(struct ieee80211_mgmt *mgmt, + uint32_t *ie_length, struct bss_description *bss_desc) +{ + qcom_ie_age *qie_age = NULL; + + /* + * GPS Requirement: need age ie per entry. Using vendor specific. + * Assuming this is the last IE, copy at the end + */ + *ie_length -= sizeof(qcom_ie_age); + qie_age = (qcom_ie_age *)(mgmt->u.probe_resp.variable + *ie_length); + qie_age->element_id = QCOM_VENDOR_IE_ID; + qie_age->len = QCOM_VENDOR_IE_AGE_LEN; + qie_age->oui_1 = QCOM_OUI1; + qie_age->oui_2 = QCOM_OUI2; + qie_age->oui_3 = QCOM_OUI3; + qie_age->type = QCOM_VENDOR_IE_AGE_TYPE; + /* + * Lowi expects the timestamp of bss in units of 1/10 ms. In driver + * all bss related timestamp is in units of ms. Due to this when scan + * results are sent to lowi the scan age is high.To address this, + * send age in units of 1/10 ms. + */ + qie_age->age = (uint32_t)(qdf_mc_timer_get_system_time() - + bss_desc->received_time)/10; + qie_age->tsf_delta = bss_desc->tsf_delta; + memcpy(&qie_age->beacon_tsf, bss_desc->timeStamp, + sizeof(qie_age->beacon_tsf)); + memcpy(&qie_age->seq_ctrl, &bss_desc->seq_ctrl, + sizeof(qie_age->seq_ctrl)); +} +#else +static inline int +wlan_hdd_get_frame_len(struct bss_description *bss_desc) +{ + return GET_IE_LEN_IN_BSS(bss_desc->length); +} +static inline void wlan_hdd_add_age_ie(struct ieee80211_mgmt *mgmt, + uint32_t *ie_length, struct bss_description *bss_desc) +{ +} +#endif /* WLAN_ENABLE_AGEIE_ON_SCAN_RESULTS */ + + +struct cfg80211_bss * +wlan_hdd_inform_bss_frame(struct hdd_adapter *adapter, + struct bss_description *bss_desc) +{ + struct wireless_dev *wdev = adapter->dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + uint32_t ie_length = wlan_hdd_get_frame_len(bss_desc); + const char *ie = + ((ie_length != 0) ? (const char *)&bss_desc->ieFields : NULL); + uint32_t i; + struct cfg80211_bss *bss_status = NULL; + struct hdd_context *hdd_ctx; + struct hdd_config *cfg_param; + struct wlan_cfg80211_inform_bss bss_data = {0}; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + /* + * wlan_hdd_validate_context should not be used here, In validate ctx + * start_modules_in_progress or stop_modules_in_progress is validated, + * If the start_modules_in_progress is set to true means the interface + * is not UP yet if the stop_modules_in_progress means that interface + * is already down. So in both the two scenario's driver should not be + * informing bss to kernel. Hence removing the validate context. + */ + + if (!hdd_ctx || !hdd_ctx->config) { + hdd_debug("HDD context is Null"); + return NULL; + } + + if (cds_is_driver_recovering() || + cds_is_load_or_unload_in_progress()) { + hdd_debug("Recovery or load/unload in progress. State: 0x%x", + cds_get_driver_state()); + return NULL; + } + + cfg_param = hdd_ctx->config; + bss_data.frame_len = ie_length + offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + bss_data.mgmt = qdf_mem_malloc(bss_data.frame_len); + if (!bss_data.mgmt) + return NULL; + + memcpy(bss_data.mgmt->bssid, bss_desc->bssId, ETH_ALEN); + + /* Android does not want the timestamp from the frame. + * Instead it wants a monotonic increasing value + */ + bss_data.mgmt->u.probe_resp.timestamp = qdf_get_monotonic_boottime(); + + bss_data.mgmt->u.probe_resp.beacon_int = bss_desc->beaconInterval; + bss_data.mgmt->u.probe_resp.capab_info = bss_desc->capabilityInfo; + + wlan_hdd_add_age_ie(bss_data.mgmt, &ie_length, bss_desc); + + memcpy(bss_data.mgmt->u.probe_resp.variable, ie, ie_length); + if (bss_desc->fProbeRsp) { + bss_data.mgmt->frame_control |= + (u16) (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); + } else { + bss_data.mgmt->frame_control |= + (u16) (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); + } + + bss_data.chan = ieee80211_get_channel(wiphy, bss_desc->chan_freq); + if (!bss_data.chan) { + hdd_err("chan pointer is NULL, chan freq: %d", + bss_desc->chan_freq); + qdf_mem_free(bss_data.mgmt); + return NULL; + } + + /* + * Based on .ini configuration, raw rssi can be reported for bss. + * Raw rssi is typically used for estimating power. + */ + bss_data.rssi = (cfg_param->inform_bss_rssi_raw) ? bss_desc->rssi_raw : + bss_desc->rssi; + + /* Supplicant takes the signal strength in terms of mBm(100*dBm) */ + bss_data.rssi = QDF_MIN(bss_data.rssi, 0) * 100; + + bss_data.boottime_ns = bss_desc->scansystimensec; + + /* Set all per chain rssi as invalid */ + for (i = 0; i < WLAN_MGMT_TXRX_HOST_MAX_ANTENNA; i++) + bss_data.per_chain_rssi[i] = WLAN_INVALID_PER_CHAIN_RSSI; + + hdd_debug("BSSID: " QDF_MAC_ADDR_FMT " Channel:%d RSSI:%d TSF %u seq %d is_prob_resp %d", + QDF_MAC_ADDR_REF(bss_data.mgmt->bssid), + bss_data.chan->center_freq, (int)(bss_data.rssi / 100), + bss_desc->timeStamp[0], ((bss_desc->seq_ctrl.seqNumHi << + HIGH_SEQ_NUM_OFFSET) | bss_desc->seq_ctrl.seqNumLo), + bss_desc->fProbeRsp); + + bss_status = wlan_cfg80211_inform_bss_frame_data(wiphy, &bss_data); + hdd_ctx->beacon_probe_rsp_cnt_per_scan++; + qdf_mem_free(bss_data.mgmt); + return bss_status; +} + +/** + * wlan_hdd_cfg80211_update_bss_db() - update bss database of CF80211 + * @adapter: Pointer to adapter + * @roam_info: Pointer to roam info + * + * This function is used to update the BSS data base of CFG8011 + * + * Return: struct cfg80211_bss pointer + */ +struct cfg80211_bss * +wlan_hdd_cfg80211_update_bss_db(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + tCsrRoamConnectedProfile roamProfile; + mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); + struct cfg80211_bss *bss = NULL; + + memset(&roamProfile, 0, sizeof(tCsrRoamConnectedProfile)); + sme_roam_get_connect_profile(mac_handle, adapter->vdev_id, + &roamProfile); + + if (roamProfile.bss_desc) { + bss = wlan_hdd_inform_bss_frame(adapter, roamProfile.bss_desc); + + if (!bss) + hdd_debug("wlan_hdd_inform_bss_frame returned NULL"); + + sme_roam_free_connect_profile(&roamProfile); + } else { + hdd_err("roamProfile.bss_desc is NULL"); + } + return bss; +} + +/** + * wlan_hdd_cfg80211_pmksa_candidate_notify() - notify a new PMSKA candidate + * @adapter: Pointer to adapter + * @roam_info: Pointer to roam info + * @index: Index + * @preauth: Preauth flag + * + * This function is used to notify the supplicant of a new PMKSA candidate. + * PMK value is notified to supplicant whether PMK caching or OKC is enabled + * in firmware or not. Supplicant needs this value becaue it uses PMK caching + * by default. + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_cfg80211_pmksa_candidate_notify(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + int index, bool preauth) +{ + struct net_device *dev = adapter->dev; + + hdd_enter(); + hdd_debug("is going to notify supplicant of:"); + + if (!roam_info) { + hdd_err("roam_info is NULL"); + return -EINVAL; + } + + hdd_info(QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(roam_info->bssid.bytes)); + cfg80211_pmksa_candidate_notify(dev, index, + roam_info->bssid.bytes, + preauth, GFP_KERNEL); + return 0; +} + +#ifdef FEATURE_WLAN_LFR_METRICS +/** + * wlan_hdd_cfg80211_roam_metrics_preauth() - roam metrics preauth + * @adapter: Pointer to adapter + * @roam_info: Pointer to roam info + * + * 802.11r/LFR metrics reporting function to report preauth initiation + * + * Return: QDF status + */ +#define MAX_LFR_METRICS_EVENT_LENGTH 100 +QDF_STATUS +wlan_hdd_cfg80211_roam_metrics_preauth(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + unsigned char metrics_notification[MAX_LFR_METRICS_EVENT_LENGTH + 1]; + union iwreq_data wrqu; + + hdd_enter(); + + if (!adapter) { + hdd_err("adapter is NULL!"); + return QDF_STATUS_E_FAILURE; + } + + /* create the event */ + memset(&wrqu, 0, sizeof(wrqu)); + memset(metrics_notification, 0, sizeof(metrics_notification)); + + wrqu.data.pointer = metrics_notification; + wrqu.data.length = scnprintf(metrics_notification, + sizeof(metrics_notification), + "QCOM: LFR_PREAUTH_INIT " QDF_FULL_MAC_FMT, + QDF_FULL_MAC_REF(roam_info->bssid.bytes)); + + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, + metrics_notification); + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +/** + * wlan_hdd_cfg80211_roam_metrics_handover() - roam metrics hand over + * @adapter: Pointer to adapter + * @roam_info: Pointer to roam info + * @preauth_status: Preauth status + * + * 802.11r/LFR metrics reporting function to report handover initiation + * + * Return: QDF status + */ +QDF_STATUS +wlan_hdd_cfg80211_roam_metrics_preauth_status(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + bool preauth_status) +{ + unsigned char metrics_notification[MAX_LFR_METRICS_EVENT_LENGTH + 1]; + union iwreq_data wrqu; + + hdd_enter(); + + if (!adapter) { + hdd_err("adapter is NULL!"); + return QDF_STATUS_E_FAILURE; + } + + /* create the event */ + memset(&wrqu, 0, sizeof(wrqu)); + memset(metrics_notification, 0, sizeof(metrics_notification)); + + scnprintf(metrics_notification, sizeof(metrics_notification), + "QCOM: LFR_PREAUTH_STATUS " QDF_FULL_MAC_FMT, + QDF_FULL_MAC_REF(roam_info->bssid.bytes)); + + if (1 == preauth_status) + strlcat(metrics_notification, " true", + sizeof(metrics_notification)); + else + strlcat(metrics_notification, " false", + sizeof(metrics_notification)); + + wrqu.data.pointer = metrics_notification; + wrqu.data.length = strlen(metrics_notification); + + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, + metrics_notification); + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +/** + * wlan_hdd_cfg80211_roam_metrics_handover() - roam metrics hand over + * @adapter: Pointer to adapter + * @roam_info: Pointer to roam info + * + * 802.11r/LFR metrics reporting function to report handover initiation + * + * Return: QDF status + */ +QDF_STATUS +wlan_hdd_cfg80211_roam_metrics_handover(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info) +{ + unsigned char metrics_notification[MAX_LFR_METRICS_EVENT_LENGTH + 1]; + union iwreq_data wrqu; + + hdd_enter(); + + if (!adapter) { + hdd_err("adapter is NULL!"); + return QDF_STATUS_E_FAILURE; + } + + /* create the event */ + memset(&wrqu, 0, sizeof(wrqu)); + memset(metrics_notification, 0, sizeof(metrics_notification)); + + wrqu.data.pointer = metrics_notification; + wrqu.data.length = scnprintf(metrics_notification, + sizeof(metrics_notification), + "QCOM: LFR_PREAUTH_HANDOVER " + QDF_FULL_MAC_FMT, + QDF_FULL_MAC_REF(roam_info->bssid.bytes)); + + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, + metrics_notification); + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +static +void hdd_mon_select_cbmode(struct hdd_adapter *adapter, + uint32_t op_freq, + struct ch_params *ch_params) +{ + struct hdd_station_ctx *station_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct hdd_mon_set_ch_info *ch_info = &station_ctx->ch_info; + enum hdd_dot11_mode hdd_dot11_mode; + uint8_t ini_dot11_mode = + (WLAN_HDD_GET_CTX(adapter))->config->dot11Mode; + + hdd_debug("Dot11Mode is %u", ini_dot11_mode); + switch (ini_dot11_mode) { + case eHDD_DOT11_MODE_AUTO: + case eHDD_DOT11_MODE_11ax: + case eHDD_DOT11_MODE_11ax_ONLY: + if (sme_is_feature_supported_by_fw(DOT11AX)) + hdd_dot11_mode = eHDD_DOT11_MODE_11ax; + else if (sme_is_feature_supported_by_fw(DOT11AC)) + hdd_dot11_mode = eHDD_DOT11_MODE_11ac; + else + hdd_dot11_mode = eHDD_DOT11_MODE_11n; + break; + case eHDD_DOT11_MODE_11ac: + case eHDD_DOT11_MODE_11ac_ONLY: + if (sme_is_feature_supported_by_fw(DOT11AC)) + hdd_dot11_mode = eHDD_DOT11_MODE_11ac; + else + hdd_dot11_mode = eHDD_DOT11_MODE_11n; + break; + case eHDD_DOT11_MODE_11n: + case eHDD_DOT11_MODE_11n_ONLY: + hdd_dot11_mode = eHDD_DOT11_MODE_11n; + break; + default: + hdd_dot11_mode = ini_dot11_mode; + break; + } + ch_info->channel_width = ch_params->ch_width; + ch_info->phy_mode = + hdd_cfg_xlate_to_csr_phy_mode(hdd_dot11_mode); + ch_info->freq = op_freq; + ch_info->cb_mode = ch_params->ch_width; + hdd_debug("ch_info width %d, phymode %d channel freq %d", + ch_info->channel_width, ch_info->phy_mode, + ch_info->freq); +} +#else +static +void hdd_mon_select_cbmode(struct hdd_adapter *adapter, + uint32_t op_freq, + struct ch_params *ch_params) +{ +} +#endif + +/** + * hdd_select_cbmode() - select channel bonding mode + * @adapter: Pointer to adapter + * @oper_freq: Operating frequency (MHz) + * @ch_params: channel info struct to populate + * + * Return: none + */ +void hdd_select_cbmode(struct hdd_adapter *adapter, qdf_freq_t oper_freq, + struct ch_params *ch_params) +{ + uint32_t sec_ch_freq = 0; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + /* + * CDS api expects secondary channel for calculating + * the channel params + */ + if ((ch_params->ch_width == CH_WIDTH_40MHZ) && + (WLAN_REG_IS_24GHZ_CH_FREQ(oper_freq))) { + if (oper_freq >= 2412 && oper_freq <= 2432) + sec_ch_freq = oper_freq + 20; + else if (oper_freq >= 2437 && oper_freq <= 2472) + sec_ch_freq = oper_freq - 20; + } + + /* This call decides required channel bonding mode */ + wlan_reg_set_channel_params_for_freq(hdd_ctx->pdev, oper_freq, + sec_ch_freq, ch_params); + + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + hdd_mon_select_cbmode(adapter, oper_freq, ch_params); +} + +/** + * wlan_hdd_handle_sap_sta_dfs_conc() - to handle SAP STA DFS conc + * @adapter: STA adapter + * @roam_profile: STA roam profile + * + * This routine will move SAP from dfs to non-dfs, if sta is coming up. + * + * Return: false if sta-sap conc is not allowed, else return true + */ +static +bool wlan_hdd_handle_sap_sta_dfs_conc(struct hdd_adapter *adapter, + struct csr_roam_profile *roam_profile) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *ap_adapter; + struct hdd_ap_ctx *hdd_ap_ctx; + struct hdd_hostapd_state *hostapd_state; + QDF_STATUS status; + uint32_t ch_freq = 0; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return true; + } + + ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); + /* probably no sap running, no handling required */ + if (!ap_adapter) + return true; + + /* + * sap is not in started state, so it is fine to go ahead with sta. + * if sap is currently doing CAC then don't allow sta to go further. + */ + if (!test_bit(SOFTAP_BSS_STARTED, &(ap_adapter)->event_flags) && + (hdd_ctx->dev_dfs_cac_status != DFS_CAC_IN_PROGRESS)) + return true; + + if (hdd_ctx->dev_dfs_cac_status == DFS_CAC_IN_PROGRESS) { + hdd_err("Concurrent SAP is in CAC state, STA is not allowed"); + return false; + } + + /* + * log and return error, if we allow STA to go through, we don't + * know what is going to happen better stop sta connection + */ + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); + if (!hdd_ap_ctx) { + hdd_err("AP context not found"); + return false; + } + + /* sap is on non-dfs channel, nothing to handle */ + if (!wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, + hdd_ap_ctx->operating_chan_freq)) { + hdd_debug("sap is on non-dfs channel, sta is allowed"); + return true; + } + /* + * find out by looking in to scan cache where sta is going to + * connect by passing its roam_profile. + */ + status = policy_mgr_get_channel_from_scan_result( + hdd_ctx->psoc, roam_profile, &ch_freq); + + /* + * If the STA's channel is 2.4 GHz, then set pcl with only 2.4 GHz + * channels for roaming case. + */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) { + hdd_info("sap is on dfs, new sta conn on 2.4 is allowed"); + return true; + } + + /* + * If channel is 0 or DFS or LTE unsafe then better to call pcl and + * find out the best channel. If channel is non-dfs 5 GHz then + * better move SAP to STA's channel to make scc, so we have room + * for 3port MCC scenario. + */ + if (!ch_freq || wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, ch_freq) || + !policy_mgr_is_safe_channel(hdd_ctx->psoc, ch_freq)) + ch_freq = policy_mgr_get_nondfs_preferred_channel( + hdd_ctx->psoc, PM_SAP_MODE, true); + + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter); + qdf_event_reset(&hostapd_state->qdf_event); + wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, ap_adapter->vdev_id, + CSA_REASON_STA_CONNECT_DFS_TO_NON_DFS); + + status = wlansap_set_channel_change_with_csa( + WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), ch_freq, + hdd_ap_ctx->sap_config.ch_width_orig, false); + + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Set channel with CSA IE failed, can't allow STA"); + return false; + } + + /* + * wait here for SAP to finish the channel switch. When channel + * switch happens, SAP sends few beacons with CSA_IE. After + * successfully Transmission of those beacons, it will move its + * state from started to disconnected and move to new channel. + * once it moves to new channel, sap again moves its state + * machine from disconnected to started and set this event. + * wait for 10 secs to finish this. + */ + status = qdf_wait_for_event_completion(&hostapd_state->qdf_event, 10000); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("wait for qdf_event failed, STA not allowed!!"); + return false; + } + + return true; +} + +#ifdef WLAN_FEATURE_11W +/** + * wlan_hdd_cfg80211_check_pmf_valid() - check if pmf status is ok + * @roam_profile: pointer to roam profile + * + * if MFPEnabled is set but the peer AP is non-PMF i.e 80211w=2 + * or pmf=2 is an explicit configuration in the supplicant + * configuration, drop the connection request. + * + * Return: 0 if check result is valid, otherwise return error code + */ +static +int wlan_hdd_cfg80211_check_pmf_valid(struct csr_roam_profile *roam_profile) +{ + if (roam_profile->MFPEnabled && + !(roam_profile->MFPRequired || roam_profile->MFPCapable)) { + hdd_err("Drop connect req as supplicant has indicated PMF required for the non-PMF peer. MFPEnabled %d MFPRequired %d MFPCapable %d", + roam_profile->MFPEnabled, + roam_profile->MFPRequired, + roam_profile->MFPCapable); + return -EINVAL; + } + return 0; +} +#else +static inline +int wlan_hdd_cfg80211_check_pmf_valid(struct csr_roam_profile *roam_profile) +{ + return 0; +} +#endif + +/** + * wlan_hdd_cfg80211_connect_start() - to start the association process + * @adapter: Pointer to adapter + * @ssid: Pointer to ssid + * @ssid_len: Length of ssid + * @bssid: Pointer to bssid + * @bssid_hint: Pointer to bssid hint + * @operatingChannel: Operating channel + * @ch_width: channel width. this is needed only for IBSS + * + * This function is used to start the association process + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_connect_start(struct hdd_adapter *adapter, + const u8 *ssid, size_t ssid_len, + const u8 *bssid, const u8 *bssid_hint, + uint32_t oper_freq, + enum nl80211_chan_width ch_width) +{ + int status = 0; + QDF_STATUS qdf_status; + struct hdd_context *hdd_ctx; + struct hdd_station_ctx *hdd_sta_ctx; + uint32_t roam_id = INVALID_ROAM_ID; + struct csr_roam_profile *roam_profile; + enum csr_akm_type rsn_auth_type; + struct sme_config_params *sme_config; + mac_handle_t mac_handle; + uint8_t wmm_mode = 0; + uint8_t value = 0; + struct wlan_objmgr_vdev *vdev; + uint32_t channel_bonding_mode; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + goto ret_status; + + if (WLAN_SSID_MAX_LEN < ssid_len) { + hdd_err("wrong SSID len"); + status = -EINVAL; + goto ret_status; + } + + if (true == hdd_is_connection_in_progress(NULL, NULL)) { + hdd_err("Connection refused: conn in progress"); + status = -EINVAL; + goto ret_status; + } + + /* Disable roaming on all other adapters before connect start */ + wlan_hdd_disable_roaming(adapter, RSO_CONNECT_START); + + hdd_notify_teardown_tdls_links(hdd_ctx->psoc); + + qdf_mem_zero(&hdd_sta_ctx->conn_info.conn_flag, + sizeof(hdd_sta_ctx->conn_info.conn_flag)); + + /* + * Reset the ptk, gtk status flags to avoid using old/previous + * connection status. + */ + hdd_sta_ctx->conn_info.gtk_installed = false; + hdd_sta_ctx->conn_info.ptk_installed = false; + adapter->last_disconnect_reason = 0; + + roam_profile = hdd_roam_profile(adapter); + if (roam_profile) { + struct hdd_station_ctx *sta_ctx; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + /* Restart the opportunistic timer + * + * If hw_mode_change_in_progress is true, then wait + * till firmware sends the callback for hw_mode change. + * + * Else set connect_in_progress as true and proceed. + */ + policy_mgr_restart_opportunistic_timer( + hdd_ctx->psoc, false); + if (policy_mgr_is_hw_mode_change_in_progress( + hdd_ctx->psoc)) { + qdf_status = policy_mgr_wait_for_connection_update( + hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("qdf wait for event failed!!"); + status = -EINVAL; + goto ret_status; + } + } + hdd_set_connection_in_progress(true); + + status = ucfg_mlme_get_wmm_mode(hdd_ctx->psoc, &wmm_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get wmm_mode failed"); + status = -EINVAL; + goto ret_status; + } + if (HDD_WMM_USER_MODE_NO_QOS == wmm_mode) { + /*QoS not enabled in cfg file */ + roam_profile->uapsd_mask = 0; + } else { + /*QoS enabled, update uapsd mask from cfg file */ + status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc, + &value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_mask failed"); + status = -EINVAL; + goto ret_status; + } + roam_profile->uapsd_mask = value; + } + + roam_profile->SSIDs.numOfSSIDs = 1; + roam_profile->SSIDs.SSIDList->SSID.length = ssid_len; + qdf_mem_zero(roam_profile->SSIDs.SSIDList->SSID.ssId, + sizeof(roam_profile->SSIDs.SSIDList->SSID.ssId)); + qdf_mem_copy((void *)(roam_profile->SSIDs.SSIDList->SSID.ssId), + ssid, ssid_len); + + /* cleanup bssid hint */ + qdf_mem_zero(roam_profile->bssid_hint.bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_zero((void *)(roam_profile->BSSIDs.bssid), + QDF_MAC_ADDR_SIZE); + + if (bssid) { + roam_profile->BSSIDs.numOfBSSIDs = 1; + qdf_mem_copy((void *)(roam_profile->BSSIDs.bssid), + bssid, QDF_MAC_ADDR_SIZE); + /* + * Save BSSID in separate variable as + * roam_profile's BSSID is getting zeroed out in the + * association process. In case of join failure + * we should send valid BSSID to supplicant + */ + qdf_mem_copy(sta_ctx->requested_bssid.bytes, + bssid, QDF_MAC_ADDR_SIZE); + } else if (bssid_hint) { + qdf_mem_copy(roam_profile->bssid_hint.bytes, + bssid_hint, QDF_MAC_ADDR_SIZE); + /* + * Save BSSID in a separate variable as + * roam_profile's BSSID is getting zeroed out in the + * association process. In case of join failure + * we should send valid BSSID to supplicant + */ + qdf_mem_copy(sta_ctx->requested_bssid.bytes, + bssid_hint, QDF_MAC_ADDR_SIZE); + } + + if (hdd_sta_ctx->wpa_versions) { + hdd_set_genie_to_csr(adapter, &rsn_auth_type); + hdd_set_csr_auth_type(adapter, rsn_auth_type); + } +#ifdef FEATURE_WLAN_WAPI + if (adapter->wapi_info.wapi_mode) { + switch (adapter->wapi_info.wapi_auth_mode) { + case WAPI_AUTH_MODE_PSK: + { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_WAPI_WAI_PSK; + break; + } + case WAPI_AUTH_MODE_CERT: + { + roam_profile->AuthType.authType[0] = + eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE; + break; + } + default: + break; + } /* End of switch */ + if (adapter->wapi_info.wapi_auth_mode == + WAPI_AUTH_MODE_PSK + || adapter->wapi_info.wapi_auth_mode == + WAPI_AUTH_MODE_CERT) { + roam_profile->AuthType.numEntries = 1; + roam_profile->EncryptionType.numEntries = 1; + roam_profile->EncryptionType.encryptionType[0] = + eCSR_ENCRYPT_TYPE_WPI; + roam_profile->mcEncryptionType.numEntries = 1; + roam_profile->mcEncryptionType. + encryptionType[0] = eCSR_ENCRYPT_TYPE_WPI; + } + } +#endif + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) { + status = -EINVAL; + goto conn_failure; + } + ucfg_pmo_flush_gtk_offload_req(vdev); + hdd_objmgr_put_vdev(vdev); + roam_profile->csrPersona = adapter->device_mode; + + if (oper_freq) { + roam_profile->ChannelInfo.freq_list = &oper_freq; + roam_profile->ChannelInfo.numOfChannels = 1; + } else { + roam_profile->ChannelInfo.freq_list = NULL; + roam_profile->ChannelInfo.numOfChannels = 0; + } + if (QDF_IBSS_MODE == adapter->device_mode && oper_freq) { + /* + * Need to post the IBSS power save parameters + * to WMA. WMA will configure this parameters + * to firmware if power save is enabled by the + * firmware. + */ + qdf_status = hdd_set_ibss_power_save_params(adapter); + + if (QDF_STATUS_SUCCESS != qdf_status) { + hdd_err("Set IBSS Power Save Params Failed"); + status = -EINVAL; + goto conn_failure; + } + roam_profile->ch_params.ch_width = + hdd_map_nl_chan_width(ch_width); + /* + * In IBSS mode while operating in 2.4 GHz, + * the device supports only 20 MHz. + */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(oper_freq)) + roam_profile->ch_params.ch_width = + CH_WIDTH_20MHZ; + hdd_select_cbmode(adapter, oper_freq, + &roam_profile->ch_params); + } + + if (wlan_hdd_cfg80211_check_pmf_valid(roam_profile)) { + status = -EINVAL; + goto conn_failure; + } + + /* + * After 8-way handshake supplicant should give the scan command + * in that it update the additional IEs, But because of scan + * enhancements, the supplicant is not issuing the scan command + * now. So the unicast frames which are sent from the host are + * not having the additional IEs. If it is P2P CLIENT and there + * is no additional IE present in roamProfile, then use the + * addtional IE form scan_info + */ + + if ((adapter->device_mode == QDF_P2P_CLIENT_MODE) && + (!roam_profile->pAddIEScan)) { + roam_profile->pAddIEScan = + &adapter->scan_info.scan_add_ie.addIEdata[0]; + roam_profile->nAddIEScanLength = + adapter->scan_info.scan_add_ie.length; + } + + /* + * When connecting between two profiles there could be scenario + * when the vdev create and vdev destroy clears the additional + * scan IEs in roam profile and when the supplicant doesn't + * issue scan request. So the unicast frames that are sent from + * the STA doesn't have the additional MBO IE. + */ + if (adapter->device_mode == QDF_STA_MODE && + (adapter->scan_info.default_scan_ies || + adapter->scan_info.scan_add_ie.length) && + !roam_profile->nAddIEScanLength) { + if (adapter->scan_info.default_scan_ies) { + roam_profile->pAddIEScan = + adapter->scan_info.default_scan_ies; + roam_profile->nAddIEScanLength = + adapter->scan_info.default_scan_ies_len; + } else if (adapter->scan_info.scan_add_ie.length) { + roam_profile->pAddIEScan = + adapter->scan_info.scan_add_ie.addIEdata; + roam_profile->nAddIEScanLength = + adapter->scan_info.scan_add_ie.length; + } + } + + if ((policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) == true) + && (false == wlan_hdd_handle_sap_sta_dfs_conc(adapter, + roam_profile))) { + hdd_err("sap-sta conc will fail, can't allow sta"); + hdd_conn_set_connection_state(adapter, + eConnectionState_NotConnected); + status = -ENOMEM; + goto conn_failure; + } + + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + if (!sme_config) { + hdd_conn_set_connection_state(adapter, + eConnectionState_NotConnected); + status = -ENOMEM; + goto conn_failure; + } + + mac_handle = hdd_ctx->mac_handle; + sme_get_config_param(mac_handle, sme_config); + /* These values are not sessionized. So, any change in these SME + * configs on an older or parallel interface will affect the + * cb mode. So, restoring the default INI params before starting + * interfaces such as sta, cli etc., + */ + ucfg_mlme_get_channel_bonding_5ghz(hdd_ctx->psoc, + &channel_bonding_mode); + sme_config->csr_config.channelBondingMode5GHz = + channel_bonding_mode; + ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, + &channel_bonding_mode); + sme_config->csr_config.channelBondingMode24GHz = + channel_bonding_mode; + sme_update_config(mac_handle, sme_config); + qdf_mem_free(sme_config); + /* + * Change conn_state to connecting before sme_roam_connect(), + * because sme_roam_connect() has a direct path to call + * hdd_sme_roam_callback(), which will change the conn_state + * If direct path, conn_state will be accordingly changed to + * NotConnected or Associated by either + * hdd_association_completion_handler() or + * hdd_dis_connect_handler() in sme_RoamCallback()if + * sme_RomConnect is to be queued, + * Connecting state will remain until it is completed. + * + * If connection state is not changed, connection state will + * remain in eConnectionState_NotConnected state. + * In hdd_association_completion_handler, "hddDisconInProgress" + * is set to true if conn state is + * eConnectionState_NotConnected. + * If "hddDisconInProgress" is set to true then cfg80211 layer + * is not informed of connect result indication which + * is an issue. + */ + if (QDF_STA_MODE == adapter->device_mode || + QDF_P2P_CLIENT_MODE == adapter->device_mode) + hdd_conn_set_connection_state(adapter, + eConnectionState_Connecting); + + hdd_set_disconnect_status(adapter, false); + + qdf_runtime_pm_prevent_suspend( + &hdd_ctx->runtime_context.connect); + hdd_prevent_suspend_timeout(HDD_WAKELOCK_CONNECT_COMPLETE, + WIFI_POWER_EVENT_WAKELOCK_CONNECT); + qdf_status = sme_roam_connect(mac_handle, + adapter->vdev_id, roam_profile, + &roam_id); + if (QDF_IS_STATUS_ERROR(qdf_status)) + status = qdf_status_to_os_return(qdf_status); + + if ((QDF_STATUS_SUCCESS != qdf_status) && + (QDF_STA_MODE == adapter->device_mode || + QDF_P2P_CLIENT_MODE == adapter->device_mode)) { + hdd_err("Vdev %d connect failed with status %d", + adapter->vdev_id, qdf_status); + /* change back to NotAssociated */ + hdd_conn_set_connection_state(adapter, + eConnectionState_NotConnected); + qdf_runtime_pm_allow_suspend( + &hdd_ctx->runtime_context.connect); + hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_CONNECT); + } + + /* Reset connect_in_progress */ + hdd_set_connection_in_progress(false); + + roam_profile->ChannelInfo.freq_list = NULL; + roam_profile->ChannelInfo.numOfChannels = 0; + } else { + hdd_err("No valid Roam profile"); + status = -EINVAL; + } + goto ret_status; + +conn_failure: + /* Reset connect_in_progress */ + hdd_set_connection_in_progress(false); + +ret_status: + /* + * Enable roaming on other STA adapter for failure case. + * For success case, it is enabled in assoc completion handler + */ + if (status) + wlan_hdd_enable_roaming(adapter, RSO_CONNECT_START); + + return status; +} + +/** + * wlan_hdd_cfg80211_set_auth_type() - set auth type + * @adapter: Pointer to adapter + * @auth_type: Auth type + * + * This function is used to set the authentication type (OPEN/SHARED). + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_set_auth_type(struct hdd_adapter *adapter, + enum nl80211_auth_type auth_type) +{ + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct csr_roam_profile *roam_profile; + + /*set authentication type */ + switch (auth_type) { + case NL80211_AUTHTYPE_AUTOMATIC: + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_AUTOSWITCH; + break; + + case NL80211_AUTHTYPE_OPEN_SYSTEM: + case NL80211_AUTHTYPE_FT: + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_OPEN_SYSTEM; + break; + + case NL80211_AUTHTYPE_SHARED_KEY: + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_SHARED_KEY; + break; +#ifdef FEATURE_WLAN_ESE + case NL80211_AUTHTYPE_NETWORK_EAP: + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_CCKM_WPA; + break; +#endif +#if defined(WLAN_FEATURE_FILS_SK) && \ + (defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) + case NL80211_AUTHTYPE_FILS_SK: + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_OPEN_SYSTEM; + break; +#endif + case NL80211_AUTHTYPE_SAE: + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_SAE; + break; + default: + hdd_err("Unsupported authentication type: %d", auth_type); + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_UNKNOWN; + return -EINVAL; + } + + roam_profile = hdd_roam_profile(adapter); + roam_profile->AuthType.authType[0] = sta_ctx->conn_info.auth_type; + return 0; +} + +#if defined(WLAN_FEATURE_FILS_SK) && \ + (defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) +static bool hdd_validate_fils_info_ptr(struct csr_roam_profile *roam_profile) +{ + struct cds_fils_connection_info *fils_con_info; + + fils_con_info = roam_profile->fils_con_info; + if (!fils_con_info) { + hdd_err("No valid Roam profile"); + return false; + } + + return true; +} + +static enum eAniAuthType wlan_hdd_get_fils_auth_type( + enum nl80211_auth_type auth) +{ + switch (auth) { + case NL80211_AUTHTYPE_FILS_SK: + return SIR_FILS_SK_WITHOUT_PFS; + case NL80211_AUTHTYPE_FILS_SK_PFS: + return SIR_FILS_SK_WITH_PFS; + case NL80211_AUTHTYPE_FILS_PK: + return SIR_FILS_PK_AUTH; + default: + return eSIR_DONOT_USE_AUTH_TYPE; + } +} + +static bool wlan_hdd_fils_data_in_limits(struct cfg80211_connect_params *req) +{ + hdd_debug("seq=%d auth=%d lengths: user=%zu rrk=%zu realm=%zu", + req->fils_erp_next_seq_num, req->auth_type, + req->fils_erp_username_len, req->fils_erp_rrk_len, + req->fils_erp_realm_len); + + if (req->fils_erp_rrk_len > FILS_MAX_RRK_LENGTH || + req->fils_erp_realm_len > FILS_MAX_REALM_LEN || + req->fils_erp_username_len > FILS_MAX_KEYNAME_NAI_LENGTH) { + hdd_err("length incorrect, user=%zu rrk=%zu realm=%zu", + req->fils_erp_username_len, req->fils_erp_rrk_len, + req->fils_erp_realm_len); + return false; + } + + if (!req->fils_erp_rrk || !req->fils_erp_realm || + !req->fils_erp_username) + hdd_err("ERP info is NULL, user=%pK rrk=%pK realm=%pK", + req->fils_erp_username, req->fils_erp_rrk, + req->fils_erp_realm); + + return true; +} + +/** + * wlan_hdd_cfg80211_set_fils_config() - set fils config params during connect + * @adapter: Pointer to adapter + * @req: Pointer to fils parameters + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_set_fils_config(struct hdd_adapter *adapter, + struct cfg80211_connect_params *req) +{ + struct csr_roam_profile *roam_profile; + enum eAniAuthType auth_type; + uint8_t *buf; + bool value; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + roam_profile = hdd_roam_profile(adapter); + + value = 0; + status = ucfg_mlme_get_fils_enabled_info(hdd_ctx->psoc, &value); + if (QDF_IS_STATUS_ERROR(status) || !value) { + hdd_err("get_fils_enabled status: %d fils_enabled: %d", + status, value); + return -EINVAL; + } + + hdd_clear_fils_connection_info(adapter); + roam_profile->fils_con_info = + qdf_mem_malloc(sizeof(*roam_profile->fils_con_info)); + + if (!roam_profile->fils_con_info) + return -EINVAL; + /* + * The initial connection for FILS may happen with an OPEN + * auth type. Hence we need to allow the connection to go + * through in that case as well. Below is_fils_connection + * flag is propagated down to CSR and PE sessions through + * the JOIN request. As the flag is used, do not free the + * memory allocated to fils_con_info and return success. + */ + if (req->auth_type != NL80211_AUTHTYPE_FILS_SK) { + roam_profile->fils_con_info->is_fils_connection = false; + return 0; + } + + /* + * Once above check is done, then we can check for valid FILS + * auth types. Currently only NL80211_AUTHTYPE_FILS_SK is + * supported. Once all auth types are supported, then we can + * merge these 2 conditions into one. + */ + auth_type = wlan_hdd_get_fils_auth_type(req->auth_type); + if (auth_type == eSIR_DONOT_USE_AUTH_TYPE) { + hdd_err("invalid auth type for fils %d", req->auth_type); + goto fils_conn_fail; + } + if (!wlan_hdd_fils_data_in_limits(req)) + goto fils_conn_fail; + + roam_profile->fils_con_info->is_fils_connection = true; + roam_profile->fils_con_info->sequence_number = + (req->fils_erp_next_seq_num + 1); + roam_profile->fils_con_info->auth_type = auth_type; + + roam_profile->fils_con_info->r_rk_length = + req->fils_erp_rrk_len; + if (req->fils_erp_rrk_len) + qdf_mem_copy(roam_profile->fils_con_info->r_rk, + req->fils_erp_rrk, + roam_profile->fils_con_info->r_rk_length); + + roam_profile->fils_con_info->realm_len = req->fils_erp_realm_len; + if (req->fils_erp_realm_len) + qdf_mem_copy(roam_profile->fils_con_info->realm, + req->fils_erp_realm, + roam_profile->fils_con_info->realm_len); + + roam_profile->fils_con_info->key_nai_length = + req->fils_erp_username_len + sizeof(char) + + req->fils_erp_realm_len; + hdd_debug("key_nai_length = %d", + roam_profile->fils_con_info->key_nai_length); + if (roam_profile->fils_con_info->key_nai_length > + FILS_MAX_KEYNAME_NAI_LENGTH) { + hdd_err("Do not allow FILS conn due to excess NAI Length %d", + roam_profile->fils_con_info->key_nai_length); + goto fils_conn_fail; + } + + if (!req->fils_erp_username_len) { + hdd_err("FILS_PMKSA: No ERP username, return success"); + return 0; + } + + buf = roam_profile->fils_con_info->keyname_nai; + qdf_mem_copy(buf, req->fils_erp_username, req->fils_erp_username_len); + buf += req->fils_erp_username_len; + *buf++ = '@'; + qdf_mem_copy(buf, req->fils_erp_realm, req->fils_erp_realm_len); + + return 0; + +fils_conn_fail: + if (roam_profile->fils_con_info) { + qdf_mem_free(roam_profile->fils_con_info); + roam_profile->fils_con_info = NULL; + } + return -EINVAL; +} + +static bool wlan_hdd_is_akm_suite_fils(uint32_t key_mgmt) +{ + switch (key_mgmt) { + case WLAN_AKM_SUITE_FILS_SHA256: + case WLAN_AKM_SUITE_FILS_SHA384: + case WLAN_AKM_SUITE_FT_FILS_SHA256: + case WLAN_AKM_SUITE_FT_FILS_SHA384: + return true; + default: + return false; + } +} + +static bool wlan_hdd_is_conn_type_fils(struct cfg80211_connect_params *req) +{ + enum nl80211_auth_type auth_type = req->auth_type; + /* + * Below n_akm_suites is defined as int in the kernel, even though it + * is supposed to be unsigned. + */ + int num_akm_suites = req->crypto.n_akm_suites; + uint32_t key_mgmt = req->crypto.akm_suites[0]; + enum eAniAuthType fils_auth_type = + wlan_hdd_get_fils_auth_type(req->auth_type); + + if (num_akm_suites <= 0) + return false; + + /* + * Auth type will be either be OPEN or FILS type for a FILS connection + */ + if ((auth_type != NL80211_AUTHTYPE_OPEN_SYSTEM) && + (fils_auth_type == eSIR_DONOT_USE_AUTH_TYPE)) + return false; + + if (!wlan_hdd_is_akm_suite_fils(key_mgmt)) + return false; + + hdd_debug("Fils Auth %d AKM %d", fils_auth_type, key_mgmt); + + return true; +} + +#else +static bool hdd_validate_fils_info_ptr(struct csr_roam_profile *roam_profile) +{ + return true; +} + +static int wlan_hdd_cfg80211_set_fils_config(struct hdd_adapter *adapter, + struct cfg80211_connect_params *req) +{ + return 0; +} + +static bool wlan_hdd_is_akm_suite_fils(uint32_t key_mgmt) +{ + return false; +} + +static bool wlan_hdd_is_conn_type_fils(struct cfg80211_connect_params *req) +{ + return false; +} +#endif + +/** + * wlan_hdd_set_akm_suite() - set key management type + * @adapter: Pointer to adapter + * @key_mgmt: Key management type + * + * This function is used to set the key mgmt type(PSK/8021x). + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_set_akm_suite(struct hdd_adapter *adapter, u32 key_mgmt) +{ + struct hdd_station_ctx *sta_ctx; + struct csr_roam_profile *roam_profile; + + roam_profile = hdd_roam_profile(adapter); + + if (wlan_hdd_is_akm_suite_fils(key_mgmt) && + !hdd_validate_fils_info_ptr(roam_profile)) + return -EINVAL; +#ifndef WLAN_AKM_SUITE_8021X_SHA256 +#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05 +#endif +#ifndef WLAN_AKM_SUITE_PSK_SHA256 +#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06 +#endif + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + /*set key mgmt type */ + switch (key_mgmt) { + case WLAN_AKM_SUITE_PSK: + case WLAN_AKM_SUITE_PSK_SHA256: + case WLAN_AKM_SUITE_FT_PSK: + case WLAN_AKM_SUITE_DPP_RSN: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_PSK; + break; + + case WLAN_AKM_SUITE_8021X_SHA256: + case WLAN_AKM_SUITE_8021X: + case WLAN_AKM_SUITE_FT_8021X: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + break; +#ifdef FEATURE_WLAN_ESE +#define WLAN_AKM_SUITE_CCKM 0x00409600 /* Should be in ieee802_11_defs.h */ + case WLAN_AKM_SUITE_CCKM: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_CCKM; + break; +#endif +#ifndef WLAN_AKM_SUITE_OSEN +#define WLAN_AKM_SUITE_OSEN 0x506f9a01 /* Should be in ieee802_11_defs.h */ +#endif + case WLAN_AKM_SUITE_OSEN: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + break; +#if defined(WLAN_FEATURE_FILS_SK) && \ + (defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) + case WLAN_AKM_SUITE_FILS_SHA256: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + roam_profile->fils_con_info->akm_type = + eCSR_AUTH_TYPE_FILS_SHA256; + break; + + case WLAN_AKM_SUITE_FILS_SHA384: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + roam_profile->fils_con_info->akm_type = + eCSR_AUTH_TYPE_FILS_SHA384; + break; + + case WLAN_AKM_SUITE_FT_FILS_SHA256: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + roam_profile->fils_con_info->akm_type = + eCSR_AUTH_TYPE_FT_FILS_SHA256; + break; + + case WLAN_AKM_SUITE_FT_FILS_SHA384: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + roam_profile->fils_con_info->akm_type = + eCSR_AUTH_TYPE_FT_FILS_SHA384; + break; +#endif + + case WLAN_AKM_SUITE_OWE: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + break; + + case WLAN_AKM_SUITE_EAP_SHA256: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + break; + case WLAN_AKM_SUITE_EAP_SHA384: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + break; + + case WLAN_AKM_SUITE_SAE: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + break; + + case WLAN_AKM_SUITE_FT_SAE: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + break; + + case WLAN_AKM_SUITE_FT_EAP_SHA_384: + sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X; + break; + + default: + hdd_err("Unsupported key mgmt type: %d", key_mgmt); + return -EINVAL; + } + return 0; +} + +/** + * wlan_hdd_cfg80211_set_cipher() - set encryption type + * @adapter: Pointer to adapter + * @cipher: Cipher type + * @ucast: Unicast flag + * + * This function is used to set the encryption type + * (NONE/WEP40/WEP104/TKIP/CCMP). + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_set_cipher(struct hdd_adapter *adapter, + u32 cipher, bool ucast) +{ + eCsrEncryptionType encryptionType = eCSR_ENCRYPT_TYPE_NONE; + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct csr_roam_profile *roam_profile; + + if (!cipher) { + encryptionType = eCSR_ENCRYPT_TYPE_NONE; + } else { + + /*set encryption method */ + switch (cipher) { + case IW_AUTH_CIPHER_NONE: + encryptionType = eCSR_ENCRYPT_TYPE_NONE; + break; + + case WLAN_CIPHER_SUITE_WEP40: + encryptionType = eCSR_ENCRYPT_TYPE_WEP40; + break; + + case WLAN_CIPHER_SUITE_WEP104: + encryptionType = eCSR_ENCRYPT_TYPE_WEP104; + break; + + case WLAN_CIPHER_SUITE_TKIP: + encryptionType = eCSR_ENCRYPT_TYPE_TKIP; + break; + + case WLAN_CIPHER_SUITE_CCMP: + encryptionType = eCSR_ENCRYPT_TYPE_AES; + break; +#ifdef FEATURE_WLAN_WAPI + case WLAN_CIPHER_SUITE_SMS4: + encryptionType = eCSR_ENCRYPT_TYPE_WPI; + break; +#endif + +#ifdef FEATURE_WLAN_ESE + case WLAN_CIPHER_SUITE_KRK: + encryptionType = eCSR_ENCRYPT_TYPE_KRK; + break; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + case WLAN_CIPHER_SUITE_BTK: + encryptionType = eCSR_ENCRYPT_TYPE_BTK; + break; +#endif +#endif + case WLAN_CIPHER_SUITE_GCMP: + encryptionType = eCSR_ENCRYPT_TYPE_AES_GCMP; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + encryptionType = eCSR_ENCRYPT_TYPE_AES_GCMP_256; + break; + default: + hdd_err("Unsupported cipher type: %d", cipher); + return -EOPNOTSUPP; + } + } + + roam_profile = hdd_roam_profile(adapter); + if (ucast) { + sta_ctx->conn_info.uc_encrypt_type = encryptionType; + roam_profile->EncryptionType.numEntries = 1; + roam_profile->EncryptionType.encryptionType[0] = + encryptionType; + } else { + sta_ctx->conn_info.mc_encrypt_type = encryptionType; + roam_profile->mcEncryptionType.numEntries = 1; + roam_profile->mcEncryptionType.encryptionType[0] = + encryptionType; + } + + return 0; +} + +/** + * wlan_hdd_add_assoc_ie() - Add Assoc IE to roamProfile + * @adapter: Pointer to adapter + * @gen_ie: Pointer to IE data + * @len: length of IE data + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_add_assoc_ie(struct hdd_adapter *adapter, + const uint8_t *gen_ie, uint16_t len) +{ + struct csr_roam_profile *roam_profile; + tSirAddie *assoc_add_ie; + uint16_t cur_add_ie_len; + + assoc_add_ie = hdd_assoc_additional_ie(adapter); + cur_add_ie_len = assoc_add_ie->length; + if (SIR_MAC_MAX_ADD_IE_LENGTH < (cur_add_ie_len + len)) { + hdd_err("current len %u, new ie of len %u will overflow", + cur_add_ie_len, len); + return -ENOMEM; + } + memcpy(assoc_add_ie->addIEdata + cur_add_ie_len, gen_ie, len); + assoc_add_ie->length += len; + + roam_profile = hdd_roam_profile(adapter); + roam_profile->pAddIEAssoc = assoc_add_ie->addIEdata; + roam_profile->nAddIEAssocLength = assoc_add_ie->length; + + return 0; +} + +#ifdef WLAN_FEATURE_FILS_SK +/** + * wlan_hdd_save_hlp_ie - API to save HLP IE + * @roam_profile: Pointer to roam profile + * @gen_ie: IE buffer to store + * @len: length of the IE buffer @gen_ie + * @flush: Flush the older saved HLP if any + * + * Return: None + */ +static void wlan_hdd_save_hlp_ie(struct csr_roam_profile *roam_profile, + const uint8_t *gen_ie, uint16_t len, + bool flush) +{ + uint8_t *hlp_ie = roam_profile->hlp_ie; + + if (flush) { + roam_profile->hlp_ie_len = 0; + if (hlp_ie) { + qdf_mem_free(hlp_ie); + roam_profile->hlp_ie = NULL; + } + } + + if ((roam_profile->hlp_ie_len + + len) > FILS_MAX_HLP_DATA_LEN) { + hdd_err("HLP len exceeds: hlp_ie_len %d len %d", + roam_profile->hlp_ie_len, len); + return; + } + + if (!roam_profile->hlp_ie) { + roam_profile->hlp_ie = + qdf_mem_malloc(FILS_MAX_HLP_DATA_LEN); + hlp_ie = roam_profile->hlp_ie; + if (!hlp_ie) + return; + } + + qdf_mem_copy(hlp_ie + roam_profile->hlp_ie_len, gen_ie, len); + roam_profile->hlp_ie_len += len; +} +#else +static inline void wlan_hdd_save_hlp_ie(struct csr_roam_profile *roam_profile, + const uint8_t *gen_ie, uint16_t len, + bool flush) +{} +#endif + +/** + * hdd_populate_crypto_auth_type() - populate auth type for crypto + * @vdev: pointed to vdev obmgr + * @auth_type: legacy auth_type + * + * set the crypto auth type for corresponding auth type received + * from NL + * + * Return: None + */ +static void hdd_populate_crypto_auth_type(struct wlan_objmgr_vdev *vdev, + enum nl80211_auth_type auth_type) +{ + QDF_STATUS status; + uint32_t set_val = 0; + wlan_crypto_auth_mode crypto_auth_type = + osif_nl_to_crypto_auth_type(auth_type); + + HDD_SET_BIT(set_val, crypto_auth_type); + status = wlan_crypto_set_vdev_param(vdev, + WLAN_CRYPTO_PARAM_AUTH_MODE, + set_val); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set auth type %0X to crypto component", + set_val); +} + +/** + * hdd_populate_crypto_akm_type() - populate akm type for crypto + * @vdev: pointed to vdev obmgr + * @akm_type: legacy akm_type + * + * set the crypto akm type for corresponding akm type received + * from NL + * + * Return: None + */ +static void hdd_populate_crypto_akm_type(struct wlan_objmgr_vdev *vdev, + u32 key_mgmt) +{ + QDF_STATUS status; + uint32_t set_val = 0; + wlan_crypto_key_mgmt crypto_akm_type = + osif_nl_to_crypto_akm_type(key_mgmt); + + HDD_SET_BIT(set_val, crypto_akm_type); + + status = wlan_crypto_set_vdev_param(vdev, + WLAN_CRYPTO_PARAM_KEY_MGMT, + set_val); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set akm type %0x to crypto component", + set_val); +} + +/** + * hdd_populate_crypto_cipher_type() - populate cipher type for crypto + * @cipher: legacy cipher type + * @vdev: pointed to vdev obmgr + * @cipher_param_type: param type, UCST/MCAST + * + * set the crypto cipher type for corresponding cipher type received + * from NL + * + * Return: None + */ +static void hdd_populate_crypto_cipher_type(u32 cipher, + struct wlan_objmgr_vdev *vdev, + wlan_crypto_param_type + cipher_param_type) +{ + QDF_STATUS status; + uint32_t set_val = 0; + wlan_crypto_cipher_type crypto_cipher_type = + osif_nl_to_crypto_cipher_type(cipher); + + HDD_SET_BIT(set_val, crypto_cipher_type); + status = wlan_crypto_set_vdev_param(vdev, cipher_param_type, set_val); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("Failed to set cipher params %d type %0x to crypto", + cipher_param_type, set_val); +} + +/** + * hdd_populate_crypto_params() - set crypto params + * @vdev: Pointer to vdev obh mgr + * @req: Pointer to security parameters + * + * Set Auth, Akm and Cipher type for crypto + * + * Return: None + */ +static void hdd_populate_crypto_params(struct wlan_objmgr_vdev *vdev, + struct cfg80211_connect_params *req) +{ + hdd_populate_crypto_auth_type(vdev, req->auth_type); + + if (req->crypto.n_akm_suites) + hdd_populate_crypto_akm_type(vdev, req->crypto.akm_suites[0]); + + if (req->crypto.n_ciphers_pairwise) + hdd_populate_crypto_cipher_type(req->crypto.ciphers_pairwise[0], + vdev, + WLAN_CRYPTO_PARAM_UCAST_CIPHER); + + if (req->crypto.cipher_group) + hdd_populate_crypto_cipher_type(req->crypto.cipher_group, + vdev, + WLAN_CRYPTO_PARAM_MCAST_CIPHER); + + wlan_crypto_free_vdev_key(vdev); +} + +/** + * hdd_set_crypto_key_mgmt_param() - Set key mgmt param. + * @adapter: Pointer to adapter. + * + * Return: None + */ +static void hdd_set_crypto_key_mgmt_param(struct hdd_adapter *adapter) +{ + uint32_t key_mgmt = 0; + struct wlan_objmgr_vdev *vdev; + + if (!adapter) { + hdd_err("adapter is null"); + return; + } + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return; + + if (adapter->wapi_info.wapi_auth_mode == WAPI_AUTH_MODE_PSK) + HDD_SET_BIT(key_mgmt, WLAN_CRYPTO_KEY_MGMT_WAPI_PSK); + if (adapter->wapi_info.wapi_auth_mode == WAPI_AUTH_MODE_CERT) + HDD_SET_BIT(key_mgmt, WLAN_CRYPTO_KEY_MGMT_WAPI_CERT); + + wlan_crypto_set_vdev_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT, key_mgmt); + hdd_objmgr_put_vdev(vdev); +} + +/** + * wlan_hdd_cfg80211_set_ie() - set IEs + * @adapter: Pointer to adapter + * @ie: Pointer ot ie + * @ie: IE length + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_set_ie(struct hdd_adapter *adapter, + const uint8_t *ie, + size_t ie_len) +{ + struct csr_roam_profile *roam_profile; + tSirAddie *assoc_add_ie; + const uint8_t *genie = ie; + uint16_t remLen = ie_len; +#ifdef FEATURE_WLAN_WAPI + uint32_t akmsuite[MAX_NUM_AKM_SUITES]; + uint8_t *tmp; + uint16_t akmsuiteCount; + uint32_t *akmlist; +#endif + int status; + uint8_t *security_ie; + + roam_profile = hdd_roam_profile(adapter); + + /* clear previous assocAddIE */ + assoc_add_ie = hdd_assoc_additional_ie(adapter); + assoc_add_ie->length = 0; + roam_profile->bWPSAssociation = false; + roam_profile->bOSENAssociation = false; + security_ie = hdd_security_ie(adapter); + + while (remLen >= 2) { + uint16_t eLen = 0; + uint8_t elementId; + + elementId = *genie++; + eLen = *genie++; + remLen -= 2; + + /* Sanity check on eLen */ + if (eLen > remLen) { + hdd_err("%s: Invalid IE length[%d] for IE[0x%X]", + __func__, eLen, elementId); + QDF_ASSERT(0); + return -EINVAL; + } + + switch (elementId) { + case DOT11F_EID_WPA: + if (4 > eLen) { /* should have at least OUI which is 4 bytes so extra 2 bytes not needed */ + hdd_err("Invalid vendor ie"); + return -EINVAL; + } else if (0 == + memcmp(&genie[0], "\x00\x50\xf2\x04", 4)) { + uint16_t curAddIELen = assoc_add_ie->length; + + hdd_debug("WPS IE(len %d)", eLen + 2); + + if (SIR_MAC_MAX_ADD_IE_LENGTH < + (assoc_add_ie->length + eLen)) { + hdd_err("Cannot accommodate assocAddIE. Need bigger buffer space"); + QDF_ASSERT(0); + return -ENOMEM; + } + /* WSC IE is saved to Additional IE ; it should be accumulated to handle WPS IE + P2P IE */ + memcpy(assoc_add_ie->addIEdata + + curAddIELen, genie - 2, eLen + 2); + assoc_add_ie->length += eLen + 2; + + roam_profile->bWPSAssociation = true; + roam_profile->pAddIEAssoc = + assoc_add_ie->addIEdata; + roam_profile->nAddIEAssocLength = + assoc_add_ie->length; + } else if (0 == memcmp(&genie[0], "\x00\x50\xf2", 3)) { + if (eLen > (WLAN_MAX_IE_LEN - 2)) { + hdd_err("%s: Invalid WPA IE length[%d]", + __func__, eLen); + QDF_ASSERT(0); + return -EINVAL; + } + hdd_debug("WPA IE (len %d)", eLen + 2); + memset(security_ie, 0, WLAN_MAX_IE_LEN); + memcpy(security_ie, genie - 2, (eLen + 2)); + roam_profile->pWPAReqIE = security_ie; + roam_profile->nWPAReqIELength = eLen + 2; /* ie_len; */ + } else if ((0 == memcmp(&genie[0], P2P_OUI_TYPE, + P2P_OUI_TYPE_SIZE))) { + uint16_t curAddIELen = + assoc_add_ie->length; + hdd_debug("P2P IE(len %d)", eLen + 2); + + if (SIR_MAC_MAX_ADD_IE_LENGTH < + (assoc_add_ie->length + eLen)) { + hdd_err("Cannot accommodate assocAddIE Need bigger buffer space"); + QDF_ASSERT(0); + return -ENOMEM; + } + /* P2P IE is saved to Additional IE ; it should be accumulated to handle WPS IE + P2P IE */ + memcpy(assoc_add_ie->addIEdata + + curAddIELen, genie - 2, eLen + 2); + assoc_add_ie->length += eLen + 2; + + roam_profile->pAddIEAssoc = + assoc_add_ie->addIEdata; + roam_profile->nAddIEAssocLength = + assoc_add_ie->length; + } +#ifdef WLAN_FEATURE_WFD + else if ((0 == memcmp(&genie[0], WFD_OUI_TYPE, + WFD_OUI_TYPE_SIZE)) && + /* Consider WFD IE, only for P2P Client */ + (QDF_P2P_CLIENT_MODE == + adapter->device_mode)) { + uint16_t curAddIELen = + assoc_add_ie->length; + hdd_debug("WFD IE(len %d)", eLen + 2); + + if (SIR_MAC_MAX_ADD_IE_LENGTH < + (assoc_add_ie->length + eLen)) { + hdd_err("Cannot accommodate assocAddIE Need bigger buffer space"); + QDF_ASSERT(0); + return -ENOMEM; + } + /* WFD IE is saved to Additional IE ; it should + * be accumulated to handle WPS IE + P2P IE + + * WFD IE + */ + memcpy(assoc_add_ie->addIEdata + + curAddIELen, genie - 2, eLen + 2); + assoc_add_ie->length += eLen + 2; + + roam_profile->pAddIEAssoc = + assoc_add_ie->addIEdata; + roam_profile->nAddIEAssocLength = + assoc_add_ie->length; + } +#endif + /* Appending HS 2.0 Indication Element in Assiciation Request */ + else if ((0 == memcmp(&genie[0], HS20_OUI_TYPE, + HS20_OUI_TYPE_SIZE))) { + uint16_t curAddIELen = + assoc_add_ie->length; + hdd_debug("HS20 IE(len %d)", eLen + 2); + + if (SIR_MAC_MAX_ADD_IE_LENGTH < + (assoc_add_ie->length + eLen)) { + hdd_err("Cannot accommodate assocAddIE Need bigger buffer space"); + QDF_ASSERT(0); + return -ENOMEM; + } + memcpy(assoc_add_ie->addIEdata + + curAddIELen, genie - 2, eLen + 2); + assoc_add_ie->length += eLen + 2; + + roam_profile->pAddIEAssoc = + assoc_add_ie->addIEdata; + roam_profile->nAddIEAssocLength = + assoc_add_ie->length; + } + /* Appending OSEN Information Element in Assiciation Request */ + else if ((0 == memcmp(&genie[0], OSEN_OUI_TYPE, + OSEN_OUI_TYPE_SIZE))) { + uint16_t curAddIELen = + assoc_add_ie->length; + hdd_debug("OSEN IE(len %d)", eLen + 2); + + if (SIR_MAC_MAX_ADD_IE_LENGTH < + (assoc_add_ie->length + eLen)) { + hdd_err("Cannot accommodate assocAddIE Need bigger buffer space"); + QDF_ASSERT(0); + return -ENOMEM; + } + memcpy(assoc_add_ie->addIEdata + + curAddIELen, genie - 2, eLen + 2); + assoc_add_ie->length += eLen + 2; + + roam_profile->bOSENAssociation = true; + roam_profile->pAddIEAssoc = + assoc_add_ie->addIEdata; + roam_profile->nAddIEAssocLength = + assoc_add_ie->length; + } else if ((0 == memcmp(&genie[0], MBO_OUI_TYPE, + MBO_OUI_TYPE_SIZE))){ + hdd_debug("Set MBO IE(len %d)", eLen + 2); + status = wlan_hdd_add_assoc_ie(adapter, + genie - 2, eLen + 2); + if (status) + return status; + } else { + uint16_t add_ie_len = + assoc_add_ie->length; + + hdd_debug("Additional IE(len %d)", eLen + 2); + + if (SIR_MAC_MAX_ADD_IE_LENGTH < + (assoc_add_ie->length + eLen)) { + hdd_err("Cannot accommodate assocAddIE Need bigger buffer space"); + QDF_ASSERT(0); + return -ENOMEM; + } + + memcpy(assoc_add_ie->addIEdata + + add_ie_len, genie - 2, eLen + 2); + assoc_add_ie->length += eLen + 2; + + roam_profile->pAddIEAssoc = + assoc_add_ie->addIEdata; + roam_profile->nAddIEAssocLength = + assoc_add_ie->length; + } + break; + case DOT11F_EID_RSN: + if (eLen > DOT11F_IE_RSN_MAX_LEN) { + hdd_err("%s: Invalid WPA RSN IE length[%d]", + __func__, eLen); + return -EINVAL; + } + memset(security_ie, 0, WLAN_MAX_IE_LEN); + memcpy(security_ie, genie - 2, (eLen + 2)); + roam_profile->pRSNReqIE = security_ie; + roam_profile->nRSNReqIELength = eLen + 2; /* ie_len; */ + hdd_debug("RSN IE(len %d)", eLen + 2); + break; + /* + * Appending Extended Capabilities with Interworking bit set + * in Assoc Req. + * + * In assoc req this EXT Cap will only be taken into account if + * interworkingService bit is set to 1. Currently + * driver is only interested in interworkingService capability + * from supplicant. If in future any other EXT Cap info is + * required from supplicat, it needs to be handled while + * sending Assoc Req in LIM. + */ + case DOT11F_EID_EXTCAP: + { + uint16_t curAddIELen = + assoc_add_ie->length; + hdd_debug("Extended CAPS IE(len %d)", eLen + 2); + + if (SIR_MAC_MAX_ADD_IE_LENGTH < + (assoc_add_ie->length + eLen)) { + hdd_err("Cannot accommodate assocAddIE Need bigger buffer space"); + QDF_ASSERT(0); + return -ENOMEM; + } + memcpy(assoc_add_ie->addIEdata + curAddIELen, genie - 2, eLen + 2); + assoc_add_ie->length += eLen + 2; + + roam_profile->pAddIEAssoc = assoc_add_ie->addIEdata; + roam_profile->nAddIEAssocLength = assoc_add_ie->length; + break; + } +#ifdef FEATURE_WLAN_WAPI + case WLAN_ELEMID_WAPI: + /* Setting WAPI Mode to ON=1 */ + adapter->wapi_info.wapi_mode = 1; + hdd_debug("WAPI MODE IS %u", adapter->wapi_info.wapi_mode); + /* genie is pointing to data field of WAPI IE's buffer */ + tmp = (uint8_t *)genie; + /* Validate length for Version(2 bytes) and Number + * of AKM suite (2 bytes) in WAPI IE buffer, coming from + * supplicant*/ + if (eLen < 4) { + hdd_err("Invalid IE Len: %u", eLen); + return -EINVAL; + } + tmp = tmp + 2; /* Skip Version */ + /* Get the number of AKM suite */ + akmsuiteCount = WPA_GET_LE16(tmp); + /* Skip the number of AKM suite */ + tmp = tmp + 2; + /* Validate total length for WAPI IE's buffer */ + if (eLen < (4 + (akmsuiteCount * sizeof(uint32_t)))) { + hdd_err("Invalid IE Len: %u", eLen); + return -EINVAL; + } + /* AKM suite list, each OUI contains 4 bytes */ + akmlist = (uint32_t *)(tmp); + if (akmsuiteCount <= MAX_NUM_AKM_SUITES) { + qdf_mem_copy(akmsuite, akmlist, + sizeof(uint32_t) * akmsuiteCount); + } else { + hdd_err("Invalid akmSuite count: %u", + akmsuiteCount); + QDF_ASSERT(0); + return -EINVAL; + } + + if (WAPI_PSK_AKM_SUITE == akmsuite[0]) { + hdd_debug("WAPI AUTH MODE SET TO PSK"); + adapter->wapi_info.wapi_auth_mode = + WAPI_AUTH_MODE_PSK; + } + if (WAPI_CERT_AKM_SUITE == akmsuite[0]) { + hdd_debug("WAPI AUTH MODE SET TO CERTIFICATE"); + adapter->wapi_info.wapi_auth_mode = + WAPI_AUTH_MODE_CERT; + } + + hdd_set_crypto_key_mgmt_param(adapter); + break; +#endif + case DOT11F_EID_SUPPOPERATINGCLASSES: + { + hdd_debug("Supported Operating Classes IE(len %d)", + eLen + 2); + status = wlan_hdd_add_assoc_ie(adapter, + genie - 2, eLen + 2); + if (status) + return status; + break; + } + case WLAN_REQUEST_IE_MAX_LEN: + { + if (genie[0] == SIR_FILS_HLP_EXT_EID) { + hdd_debug("HLP EXT IE(len %d)", + eLen + 2); + wlan_hdd_save_hlp_ie(roam_profile, + genie - 2, eLen + 2, + true); + status = wlan_hdd_add_assoc_ie( + adapter, genie - 2, + eLen + 2); + if (status) + return status; + } else if (genie[0] == + SIR_DH_PARAMETER_ELEMENT_EXT_EID) { + hdd_debug("DH EXT IE(len %d)", + eLen + 2); + status = wlan_hdd_add_assoc_ie( + adapter, genie - 2, + eLen + 2); + if (status) + return status; + } else { + hdd_err("UNKNOWN EID: %X", genie[0]); + } + break; + } + case DOT11F_EID_FRAGMENT_IE: + { + hdd_debug("Fragment IE(len %d)", eLen + 2); + wlan_hdd_save_hlp_ie(roam_profile, + genie - 2, eLen + 2, + false); + status = wlan_hdd_add_assoc_ie(adapter, + genie - 2, eLen + 2); + if (status) + return status; + break; + } + case WLAN_ELEMID_RSNXE: + hdd_debug("RSNXE(len %d)", eLen + 2); + status = wlan_hdd_add_assoc_ie(adapter, genie - 2, + eLen + 2); + if (status) + return status; + break; + default: + hdd_err("UNKNOWN IE: %X", elementId); + /* when Unknown IE is received we break + * and continue to the next IE in the buffer + */ + break; + } + genie += eLen; + remLen -= eLen; + } + return 0; +} + +#ifdef QCA_IBSS_SUPPORT +/** + * hdd_is_wpaie_present() - check for WPA ie + * @ie: Pointer to ie + * @ie_len: Ie length + * + * Parse the received IE to find the WPA IE + * + * Return: true if wpa ie is found else false + */ +static bool hdd_is_wpaie_present(const uint8_t *ie, uint8_t ie_len) +{ + uint8_t eLen = 0; + uint16_t remLen = ie_len; + uint8_t elementId = 0; + + while (remLen >= 2) { + elementId = *ie++; + eLen = *ie++; + remLen -= 2; + if (eLen > remLen) { + hdd_err("Invalid IE length: %d", eLen); + return false; + } + if ((elementId == DOT11F_EID_WPA) && (remLen > 5)) { + /* OUI - 0x00 0X50 0XF2 + * WPA Information Element - 0x01 + * WPA version - 0x01 + */ + if (0 == memcmp(&ie[0], "\x00\x50\xf2\x01\x01", 5)) + return true; + } + ie += eLen; + remLen -= eLen; + } + return false; +} + +/** + * wlan_hdd_cfg80211_set_privacy_ibss() - set ibss privacy + * @adapter: Pointer to adapter + * @param: Pointer to IBSS parameters + * + * This function is used to initialize the security settings in IBSS mode + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_set_privacy_ibss(struct hdd_adapter *adapter, + struct cfg80211_ibss_params + *params) +{ + uint32_t ret; + int status = 0; + eCsrEncryptionType encryptionType = eCSR_ENCRYPT_TYPE_NONE; + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct csr_roam_profile *roam_profile; + + hdd_enter(); + + sta_ctx->wpa_versions = 0; + qdf_mem_zero(&sta_ctx->ibss_enc_key, sizeof(tCsrRoamSetKey)); + sta_ctx->ibss_enc_key_installed = 0; + + if (params->ie_len && (params->ie)) { + if (wlan_get_ie_ptr_from_eid(WLAN_EID_RSN, params->ie, + params->ie_len)) { + sta_ctx->wpa_versions = NL80211_WPA_VERSION_2; + encryptionType = eCSR_ENCRYPT_TYPE_AES; + } else if (hdd_is_wpaie_present(params->ie, params->ie_len)) { + tDot11fIEWPA dot11_wpa_ie; + mac_handle_t mac_handle = + hdd_adapter_get_mac_handle(adapter); + const u8 *ie; + + memset(&dot11_wpa_ie, 0, sizeof(dot11_wpa_ie)); + ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_WPA, + params->ie, params->ie_len); + if (ie) { + sta_ctx->wpa_versions = NL80211_WPA_VERSION_1; + /* Unpack the WPA IE + * Skip past the EID byte and length byte + * and four byte WiFi OUI + */ + if (ie[1] < DOT11F_IE_WPA_MIN_LEN || + ie[1] > DOT11F_IE_WPA_MAX_LEN) { + hdd_err("invalid ie len:%d", ie[1]); + return -EINVAL; + } + ret = dot11f_unpack_ie_wpa( + MAC_CONTEXT(mac_handle), + (uint8_t *)&ie[2 + 4], + ie[1] - 4, &dot11_wpa_ie, false); + if (DOT11F_FAILED(ret)) { + hdd_err("unpack failed ret: 0x%x", ret); + return -EINVAL; + } + /* + * Extract the multicast cipher, the + * encType for unicast cipher for + * wpa-none is none + */ + encryptionType = + hdd_translate_wpa_to_csr_encryption_type + (dot11_wpa_ie.multicast_cipher); + } + } + + status = + wlan_hdd_cfg80211_set_ie(adapter, params->ie, + params->ie_len); + + if (0 > status) { + hdd_err("Failed to parse WPA/RSN IE"); + return status; + } + } + + roam_profile = hdd_roam_profile(adapter); + roam_profile->AuthType.authType[0] = + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_OPEN_SYSTEM; + + if (params->privacy) { + /* Security enabled IBSS, At this time there is no information + * available about the security parameters, so initialise the + * encryption type to eCSR_ENCRYPT_TYPE_WEP40_STATICKEY. + * The correct security parameters will be updated later in + * wlan_hdd_cfg80211_add_key Hal expects encryption type to be + * set inorder enable privacy bit in beacons + */ + + encryptionType = eCSR_ENCRYPT_TYPE_WEP40_STATICKEY; + } + hdd_debug("encryptionType=%d", encryptionType); + sta_ctx->conn_info.uc_encrypt_type = encryptionType; + roam_profile->EncryptionType.numEntries = 1; + roam_profile->EncryptionType.encryptionType[0] = + encryptionType; + return status; +} + +/** + * __wlan_hdd_cfg80211_join_ibss() - join ibss + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @param: Pointer to IBSS join parameters + * + * This function is used to create/join an IBSS network + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_join_ibss(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct csr_roam_profile *roam_profile; + int status; + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct qdf_mac_addr bssid; + uint8_t channelNum = 0; + mac_handle_t mac_handle; + struct wlan_mlme_ibss_cfg ibss_cfg = {0}; + uint8_t conn_info_channel; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_JOIN_IBSS, + adapter->vdev_id, adapter->device_mode); + + hdd_debug("Device_mode %s(%d)", + qdf_opmode_str(adapter->device_mode), adapter->device_mode); + + status = wlan_hdd_validate_context(hdd_ctx); + + if (0 != status) + return status; + + if (QDF_IS_STATUS_ERROR(ucfg_mlme_get_ibss_cfg(hdd_ctx->psoc, + &ibss_cfg))) { + return -EINVAL; + } + + mac_handle = hdd_ctx->mac_handle; + if (NULL != + params->chandef.chan) { + uint32_t numChans = CFG_VALID_CHANNEL_LIST_LEN; + uint32_t validChan[CFG_VALID_CHANNEL_LIST_LEN]; + int indx; + + /* Get channel number */ + channelNum = ieee80211_frequency_to_channel( + params-> + chandef. + chan-> + center_freq); + ucfg_mlme_get_valid_channel_freq_list(hdd_ctx->psoc, validChan, + &numChans); + + for (indx = 0; indx < numChans; indx++) { + if (channelNum == + wlan_reg_freq_to_chan(hdd_ctx->pdev, validChan[indx])) + break; + } + if (indx >= numChans) { + hdd_err("Not valid Channel: %d", channelNum); + return -EINVAL; + } + } + + /* Disable NAN Discovery if enabled */ + ucfg_nan_disable_concurrency(hdd_ctx->psoc); + + if (!policy_mgr_allow_concurrency( + hdd_ctx->psoc, PM_IBSS_MODE, + wlan_reg_chan_to_freq(hdd_ctx->pdev, channelNum), + HW_MODE_20_MHZ)) { + hdd_err("This concurrency combination is not allowed"); + return -ECONNREFUSED; + } + + status = policy_mgr_reset_connection_update(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("qdf_reset_connection_update failed status: %d", status); + + status = policy_mgr_current_connections_update( + hdd_ctx->psoc, adapter->vdev_id, + wlan_reg_chan_to_freq(hdd_ctx->pdev, channelNum), + POLICY_MGR_UPDATE_REASON_JOIN_IBSS); + if (QDF_STATUS_E_FAILURE == status) { + hdd_err("connections update failed!!"); + return -EINVAL; + } + + if (QDF_STATUS_SUCCESS == status) { + status = policy_mgr_wait_for_connection_update( + hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("qdf wait for event failed!!"); + return -EINVAL; + } + } + + /*Try disconnecting if already in connected state */ + status = wlan_hdd_try_disconnect(adapter, + eSIR_MAC_UNSPEC_FAILURE_REASON); + if (0 > status) { + hdd_err("Failed to disconnect the existing IBSS connection"); + return -EALREADY; + } + + roam_profile = hdd_roam_profile(adapter); + + if (eCSR_BSS_TYPE_START_IBSS != roam_profile->BSSType) { + hdd_err("Interface type is not set to IBSS"); + return -EINVAL; + } + + /* enable selected protection checks in IBSS mode */ + roam_profile->cfg_protection = IBSS_CFG_PROTECTION_ENABLE_MASK; + + /* BSSID is provided by upper layers hence no need to AUTO generate */ + if (params->bssid) { + if (ucfg_mlme_set_ibss_auto_bssid(hdd_ctx->psoc, 0) + == QDF_STATUS_E_FAILURE) { + hdd_err("Unable to update MLME IBSS Auto BSSID config"); + return -EIO; + } + qdf_mem_copy(bssid.bytes, params->bssid, QDF_MAC_ADDR_SIZE); + } else if (ibss_cfg.coalesing_enable == 0) { + if (ucfg_mlme_set_ibss_auto_bssid(hdd_ctx->psoc, 0) + == QDF_STATUS_E_FAILURE) { + hdd_err("Unable to update MLME IBSS Auto BSSID config"); + return -EIO; + } + qdf_copy_macaddr(&bssid, &ibss_cfg.bssid); + } + + if (cfg_in_range(CFG_BEACON_INTERVAL, params->beacon_interval)) + roam_profile->beaconInterval = params->beacon_interval; + else + roam_profile->beaconInterval = cfg_get(hdd_ctx->psoc, + CFG_BEACON_INTERVAL); + + /* Set Channel */ + if (channelNum) { + /* Set the Operational Channel */ + hdd_debug("set channel %d", channelNum); + roam_profile->ChannelInfo.numOfChannels = 1; + sta_ctx->conn_info.chan_freq = + wlan_reg_chan_to_freq(hdd_ctx->pdev, + channelNum); + roam_profile->ChannelInfo.freq_list = + &sta_ctx->conn_info.chan_freq; + } + + /* Initialize security parameters */ + status = wlan_hdd_cfg80211_set_privacy_ibss(adapter, params); + if (status < 0) { + hdd_err("failed to set security parameters"); + return status; + } + + conn_info_channel = + wlan_reg_freq_to_chan( + hdd_ctx->pdev, + sta_ctx->conn_info.chan_freq); + /* Issue connect start */ + status = wlan_hdd_cfg80211_connect_start(adapter, params->ssid, + params->ssid_len, + bssid.bytes, NULL, + conn_info_channel, + params->chandef.width); + + if (0 > status) { + hdd_err("connect failed"); + return status; + } + hdd_exit(); + return 0; +} + +/** + * wlan_hdd_cfg80211_join_ibss() - join ibss + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @param: Pointer to IBSS join parameters + * + * This function is used to create/join an IBSS network + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_join_ibss(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_join_ibss(wiphy, dev, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_leave_ibss() - leave ibss + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * + * This function is used to leave an IBSS network + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_leave_ibss(struct wiphy *wiphy, + struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct csr_roam_profile *roam_profile; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int status; + mac_handle_t mac_handle; + unsigned long rc; + tSirUpdateIE update_ie; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_LEAVE_IBSS, + adapter->vdev_id, eCSR_DISCONNECT_REASON_IBSS_LEAVE); + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return status; + + hdd_debug("Device_mode %s(%d)", + qdf_opmode_str(adapter->device_mode), adapter->device_mode); + + roam_profile = hdd_roam_profile(adapter); + + /* Issue disconnect only if interface type is set to IBSS */ + if (eCSR_BSS_TYPE_START_IBSS != roam_profile->BSSType) { + hdd_err("BSS Type is not set to IBSS"); + return -EINVAL; + } + /* Clearing add IE of beacon */ + qdf_mem_copy(update_ie.bssid.bytes, adapter->mac_addr.bytes, + sizeof(tSirMacAddr)); + update_ie.vdev_id = adapter->vdev_id; + update_ie.ieBufferlength = 0; + update_ie.pAdditionIEBuffer = NULL; + update_ie.append = true; + update_ie.notify = true; + mac_handle = hdd_ctx->mac_handle; + if (sme_update_add_ie(mac_handle, + &update_ie, + eUPDATE_IE_PROBE_BCN) == QDF_STATUS_E_FAILURE) { + hdd_err("Could not pass on PROBE_RSP_BCN data to PE"); + } + + /* Reset WNI_CFG_PROBE_RSP Flags */ + wlan_hdd_reset_prob_rspies(adapter); + + /* Issue Disconnect request */ + INIT_COMPLETION(adapter->disconnect_comp_var); + status = sme_roam_disconnect(mac_handle, + adapter->vdev_id, + eCSR_DISCONNECT_REASON_IBSS_LEAVE, + eSIR_MAC_UNSPEC_FAILURE_REASON); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_roam_disconnect failed status: %d", + status); + return -EAGAIN; + } + + /* wait for mc thread to cleanup and then return to upper stack + * so by the time upper layer calls the change interface, we are + * all set to proceed further + */ + rc = wait_for_completion_timeout(&adapter->disconnect_comp_var, + msecs_to_jiffies(SME_DISCONNECT_TIMEOUT)); + if (!rc) { + hdd_err("Failed to disconnect, timed out"); + return -ETIMEDOUT; + } + + hdd_exit(); + return 0; +} + +/** + * wlan_hdd_cfg80211_leave_ibss() - leave ibss + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * + * This function is used to leave an IBSS network + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_leave_ibss(struct wiphy *wiphy, + struct net_device *dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_leave_ibss(wiphy, dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#else +/** + * wlan_hdd_cfg80211_join_ibss() - join ibss + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @param: Pointer to IBSS join parameters + * + * This function is dummy + * + * Return: 0 + */ +static inline int +wlan_hdd_cfg80211_join_ibss(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + return 0; +} + +/** + * wlan_hdd_cfg80211_leave_ibss() - leave ibss + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * + * This function is dummy + * + * Return: 0 + */ +static inline int +wlan_hdd_cfg80211_leave_ibss(struct wiphy *wiphy, + struct net_device *dev) +{ + return 0; +} +#endif + +static void wlan_hdd_cfg80211_store_wep_key(struct hdd_adapter *adapter, + struct wlan_objmgr_vdev *vdev, + struct cfg80211_connect_params *req) +{ + struct key_params params; + + qdf_mem_zero(¶ms, sizeof(params)); + params.cipher = req->crypto.ciphers_pairwise[0]; + params.key_len = req->key_len; + params.key = req->key; + wlan_cfg80211_store_key(vdev, req->key_idx, + WLAN_CRYPTO_KEY_TYPE_UNICAST, + NULL, ¶ms); +} + +/** + * wlan_hdd_cfg80211_set_privacy() - set security parameters during connection + * @adapter: Pointer to adapter + * @req: Pointer to security parameters + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_set_privacy(struct hdd_adapter *adapter, + struct cfg80211_connect_params *req) +{ + int status = 0; + struct hdd_station_ctx *sta_ctx; + struct csr_roam_profile *roam_profile; + struct wlan_objmgr_vdev *vdev; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + sta_ctx->wpa_versions = req->crypto.wpa_versions; + roam_profile = hdd_roam_profile(adapter); + + /* populate auth,akm and cipher params for crypto */ + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + hdd_populate_crypto_params(vdev, req); + + /*set authentication type */ + status = wlan_hdd_cfg80211_set_auth_type(adapter, req->auth_type); + + if (wlan_hdd_is_conn_type_fils(req)) { + status = wlan_hdd_cfg80211_set_fils_config(adapter, req); + + if (0 > status) { + hdd_err("Failed to set fils config"); + goto release_vdev_ref; + } + } + + /*set key mgmt type */ + if (req->crypto.n_akm_suites) { + status = + wlan_hdd_set_akm_suite(adapter, req->crypto.akm_suites[0]); + if (0 > status) { + hdd_err("Failed to set akm suite"); + goto release_vdev_ref; + } + } + + /*set pairwise cipher type */ + if (req->crypto.n_ciphers_pairwise) { + status = wlan_hdd_cfg80211_set_cipher(adapter, + req->crypto. + ciphers_pairwise[0], + true); + if (0 > status) { + hdd_err("Failed to set unicast cipher type"); + goto release_vdev_ref; + } + } else { + /*Reset previous cipher suite to none */ + status = wlan_hdd_cfg80211_set_cipher(adapter, 0, true); + if (0 > status) { + hdd_err("Failed to set unicast cipher type"); + goto release_vdev_ref; + } + } + + /*set group cipher type */ + status = + wlan_hdd_cfg80211_set_cipher(adapter, req->crypto.cipher_group, + false); + + if (0 > status) { + hdd_err("Failed to set mcast cipher type"); + goto release_vdev_ref; + } +#ifdef WLAN_FEATURE_11W + roam_profile->MFPEnabled = (req->mfp == NL80211_MFP_REQUIRED); +#endif + + /*parse WPA/RSN IE, and set the correspoing fileds in Roam profile */ + if (req->ie_len) { + status = + wlan_hdd_cfg80211_set_ie(adapter, req->ie, req->ie_len); + if (0 > status) { + hdd_err("Failed to parse the WPA/RSN IE"); + goto release_vdev_ref; + } + } + + /*incase of WEP set default key information */ + if (req->key && req->key_len) { + u32 cipher = req->crypto.ciphers_pairwise[0]; + + if ((WLAN_CIPHER_SUITE_WEP40 == cipher) || + (WLAN_CIPHER_SUITE_WEP104 == cipher)) { + enum hdd_auth_key_mgmt key_mgmt = + sta_ctx->auth_key_mgmt; + + if (key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) { + hdd_err("Dynamic WEP not supported"); + status = -EOPNOTSUPP; + goto release_vdev_ref; + } + + if ((eCSR_SECURITY_WEP_KEYSIZE_MAX_BYTES >= + req->key_len) && (CSR_MAX_NUM_KEY > req->key_idx)) + wlan_hdd_cfg80211_store_wep_key(adapter, + vdev, req); + } + } +release_vdev_ref: + hdd_objmgr_put_vdev(vdev); + + return status; +} + +/** + * wlan_hdd_clear_wapi_privacy() - reset WAPI settings in HDD layer + * @adapter: pointer to HDD adapter object + * + * This function resets all WAPI related parameters imposed before STA + * connection starts. It's invoked when privacy checking against concurrency + * fails, to make sure no improper WAPI settings are still populated before + * returning an error to the upper layer requester. + * + * Return: none + */ +#ifdef FEATURE_WLAN_WAPI +static inline void wlan_hdd_clear_wapi_privacy(struct hdd_adapter *adapter) +{ + adapter->wapi_info.wapi_mode = 0; + adapter->wapi_info.wapi_auth_mode = WAPI_AUTH_MODE_OPEN; +} +#else +static inline void wlan_hdd_clear_wapi_privacy(struct hdd_adapter *adapter) +{ +} +#endif + +/** + * wlan_hdd_cfg80211_clear_privacy() - reset STA security parameters + * @adapter: pointer to HDD adapter object + * + * This function resets all privacy related parameters imposed + * before STA connection starts. It's invoked when privacy checking + * against concurrency fails, to make sure no improper settings are + * still populated before returning an error to the upper layer requester. + * + * Return: none + */ +static void wlan_hdd_cfg80211_clear_privacy(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + hdd_debug("resetting all privacy configurations"); + + hdd_sta_ctx->wpa_versions = 0; + + hdd_sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_NONE; + hdd_sta_ctx->roam_profile.AuthType.authType[0] = eCSR_AUTH_TYPE_NONE; + + hdd_sta_ctx->conn_info.uc_encrypt_type = eCSR_ENCRYPT_TYPE_NONE; + hdd_sta_ctx->roam_profile.EncryptionType.numEntries = 0; + hdd_sta_ctx->conn_info.mc_encrypt_type = eCSR_ENCRYPT_TYPE_NONE; + hdd_sta_ctx->roam_profile.mcEncryptionType.numEntries = 0; + + wlan_hdd_clear_wapi_privacy(adapter); +} + +static int wlan_hdd_wait_for_disconnect(mac_handle_t mac_handle, + struct hdd_adapter *adapter, + uint16_t reason, + tSirMacReasonCodes mac_reason) +{ + eConnectionState prev_conn_state; + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + QDF_STATUS status = QDF_STATUS_SUCCESS; + int ret = 0; + unsigned long rc; + uint32_t wait_time = SME_DISCONNECT_TIMEOUT; + void *hif_ctx; + + /* Return if already disconnected */ + if (sta_ctx->conn_info.conn_state == eConnectionState_NotConnected || + sta_ctx->conn_info.conn_state == eConnectionState_IbssDisconnected) + return 0; + + /* If already in disconnecting state just wait for its completion */ + if (sta_ctx->conn_info.conn_state == eConnectionState_Disconnecting) + goto wait_for_disconnect; + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (hif_ctx) { + /* + * Trigger runtime sync resume before sending disconneciton + */ + hif_pm_runtime_sync_resume(hif_ctx); + } + + INIT_COMPLETION(adapter->disconnect_comp_var); + prev_conn_state = sta_ctx->conn_info.conn_state; + hdd_conn_set_connection_state(adapter, eConnectionState_Disconnecting); + + status = sme_roam_disconnect(mac_handle, adapter->vdev_id, reason, + mac_reason); + if (status == QDF_STATUS_CMD_NOT_QUEUED && + prev_conn_state == eConnectionState_Connecting) { + /* + * Wait here instead of returning directly, this will block the + * next connect command and allow processing of the scan for + * ssid and the previous connect command in CSR. + */ + hdd_debug("CSR not connected but scan for SSID is in progress, wait for scan to be aborted or completed."); + } else if (QDF_IS_STATUS_ERROR(status)) { + hdd_debug("SB Disconnect in progress/SME is disconencted/Connect removed from pending queue: status = %d", + status); + /* + * Wait here instead of returning directly. This will block the + * next connect command and allow processing of the disconnect + * in SME. As disconnect is already in progress, wait here for + * WLAN_WAIT_DISCONNECT_ALREADY_IN_PROGRESS instead of + * SME_DISCONNECT_TIMEOUT. + */ + wait_time = WLAN_WAIT_DISCONNECT_ALREADY_IN_PROGRESS; + } + +wait_for_disconnect: + rc = wait_for_completion_timeout(&adapter->disconnect_comp_var, + msecs_to_jiffies(wait_time)); + + if (!rc && QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Disconnect timed out!!!"); + ret = -ETIMEDOUT; + } + + hdd_conn_set_connection_state(adapter, eConnectionState_NotConnected); + + return ret; +} + +static void wlan_hdd_wait_for_roaming(mac_handle_t mac_handle, + struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + unsigned long rc; + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (adapter->device_mode != QDF_STA_MODE) + return; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + /* Return if not in connected state */ + if (sta_ctx->conn_info.conn_state != eConnectionState_Associated) + return; + + sme_stop_roaming(mac_handle, adapter->vdev_id, + REASON_DRIVER_DISABLED, + RSO_INVALID_REQUESTOR); + /* + * If firmware has already started roaming process, driver + * needs to wait for processing of this disconnect request. + * + */ + INIT_COMPLETION(adapter->roaming_comp_var); + if (hdd_is_roaming_in_progress(hdd_ctx) || + sme_neighbor_middle_of_roaming(mac_handle, + adapter->vdev_id)) { + rc = wait_for_completion_timeout(&adapter->roaming_comp_var, + msecs_to_jiffies(WLAN_WAIT_TIME_STOP_ROAM)); + if (!rc) { + hdd_err("roaming_comp_var time out vdev id: %d", + adapter->vdev_id); + /* Clear roaming in progress flag */ + hdd_set_roaming_in_progress(false); + } + if (adapter->roam_ho_fail) { + INIT_COMPLETION(adapter->disconnect_comp_var); + hdd_conn_set_connection_state(adapter, + eConnectionState_Disconnecting); + } + } +} + +int wlan_hdd_try_disconnect(struct hdd_adapter *adapter, + enum eSirMacReasonCodes reason) +{ + mac_handle_t mac_handle; + + mac_handle = hdd_adapter_get_mac_handle(adapter); + wlan_hdd_wait_for_roaming(mac_handle, adapter); + + return wlan_hdd_wait_for_disconnect(mac_handle, adapter, + eCSR_DISCONNECT_REASON_UNSPECIFIED, + reason); +} + +/** + * wlan_hdd_reassoc_bssid_hint() - Start reassociation if bssid is present + * @adapter: Pointer to the HDD adapter + * @req: Pointer to the structure cfg_connect_params receieved from user space + * + * This function will start reassociation if prev_bssid is set and bssid/ + * bssid_hint, channel/channel_hint parameters are present in connect request. + * + * Return: 0 if connect was for ReAssociation, non-zero error code otherwise + */ +#if defined(CFG80211_CONNECT_PREV_BSSID) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) +static int wlan_hdd_reassoc_bssid_hint(struct hdd_adapter *adapter, + struct cfg80211_connect_params *req) +{ + int status = -EINVAL; + const uint8_t *bssid = NULL; + uint32_t ch_freq = 0; + struct hdd_station_ctx *sta_ctx; + + if (req->bssid) + bssid = req->bssid; + else if (req->bssid_hint) + bssid = req->bssid_hint; + + if (req->channel) + ch_freq = req->channel->center_freq; + else if (req->channel_hint) + ch_freq = req->channel_hint->center_freq; + + if (bssid && ch_freq && req->prev_bssid) { + hdd_debug("REASSOC Attempt on ch freq %d to " QDF_MAC_ADDR_FMT, + ch_freq, QDF_MAC_ADDR_REF(bssid)); + /* + * Save BSSID in a separate variable as + * roam_profile's BSSID is getting zeroed out in the + * association process. In case of join failure + * we should send valid BSSID to supplicant + */ + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + qdf_mem_copy(sta_ctx->requested_bssid.bytes, bssid, + QDF_MAC_ADDR_SIZE); + + hdd_set_roaming_in_progress(true); + + status = hdd_reassoc(adapter, bssid, ch_freq, + CONNECT_CMD_USERSPACE); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_set_roaming_in_progress(false); + hdd_debug("Failed with status: %d", status); + } + } + + return status; +} +#else +static int wlan_hdd_reassoc_bssid_hint(struct hdd_adapter *adapter, + struct cfg80211_connect_params *req) +{ + return -ENOTSUPP; +} +#endif + + +/** + * wlan_hdd_check_ht20_ht40_ind() - check if Supplicant has indicated to + * connect in HT20 mode + * @hdd_ctx: hdd context + * @adapter: Pointer to the HDD adapter + * @req: Pointer to the structure cfg_connect_params receieved from user space + * + * This function will check if supplicant has indicated to to connect in HT20 + * mode. this is currently applicable only for 2.4Ghz mode only. + * if feature is enabled and supplicant indicate HT20 set + * force_24ghz_in_ht20 to true to force 2.4Ghz in HT20 else set it to false. + * + * Return: void + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) +static void +wlan_hdd_check_ht20_ht40_ind(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + struct cfg80211_connect_params *req) +{ + struct csr_roam_profile *roam_profile; + bool is_override_ht20_40_24g; + + roam_profile = hdd_roam_profile(adapter); + + roam_profile->force_24ghz_in_ht20 = false; + + ucfg_mlme_is_override_ht20_40_24g(hdd_ctx->psoc, + &is_override_ht20_40_24g); + if (is_override_ht20_40_24g && + !(req->ht_capa.cap_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + roam_profile->force_24ghz_in_ht20 = true; + + if (is_override_ht20_40_24g) + hdd_nofl_debug("HT cap %x", req->ht_capa.cap_info); +} +#else +static inline void +wlan_hdd_check_ht20_ht40_ind(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + struct cfg80211_connect_params *req) +{ + struct csr_roam_profile *roam_profile; + + roam_profile = hdd_roam_profile(adapter); + + roam_profile->force_24ghz_in_ht20 = false; +} +#endif + +static inline void hdd_dump_connect_req(struct hdd_adapter *adapter, + struct net_device *ndev, + struct cfg80211_connect_params *req) +{ + uint32_t i; + + hdd_nofl_debug("cfg80211_connect req for %s(vdevid-%d): mode %d freq %d SSID %.*s auth type %d WPA ver %d n_akm %d n_cipher %d grp_cipher %x mfp %d freq hint %d", + ndev->name, adapter->vdev_id, adapter->device_mode, + req->channel ? req->channel->center_freq : 0, + (int)req->ssid_len, req->ssid, req->auth_type, + req->crypto.wpa_versions, + req->crypto.n_akm_suites, req->crypto.n_ciphers_pairwise, + req->crypto.cipher_group, req->mfp, + req->channel_hint ? req->channel_hint->center_freq : 0); + if (req->bssid) + hdd_nofl_debug("BSSID "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(req->bssid)); + if (req->bssid_hint) + hdd_nofl_debug("BSSID hint "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(req->bssid_hint)); + if (req->prev_bssid) + hdd_nofl_debug("prev BSSID "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(req->prev_bssid)); + + for (i = 0; i < req->crypto.n_akm_suites; i++) + hdd_nofl_debug("akm[%d] = %x", i, req->crypto.akm_suites[i]); + + for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) + hdd_nofl_debug("cipher_pairwise[%d] = %x", i, + req->crypto.ciphers_pairwise[i]); +} + +/** + * __wlan_hdd_cfg80211_connect() - cfg80211 connect api + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @req: Pointer to cfg80211 connect request + * + * This function is used to start the association process + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_connect(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_connect_params *req) +{ + int status; + uint32_t ch_freq, sap_cnt, sta_cnt; + const u8 *bssid = NULL; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) + const u8 *bssid_hint = req->bssid_hint; +#else + const u8 *bssid_hint = NULL; +#endif + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + struct hdd_context *hdd_ctx; + uint8_t vdev_id_list[MAX_NUMBER_OF_CONC_CONNECTIONS], i; + bool disable_nan = true; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_CONNECT, + adapter->vdev_id, adapter->device_mode); + + if (adapter->device_mode != QDF_STA_MODE && + adapter->device_mode != QDF_P2P_CLIENT_MODE) { + hdd_err("Device_mode %s(%d) is not supported", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return -EINVAL; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return status; + + if (req->bssid) + bssid = req->bssid; + else if (bssid_hint) + bssid = bssid_hint; + + if (bssid && hdd_get_adapter_by_macaddr(hdd_ctx, (uint8_t *)bssid)) { + hdd_err("adapter exist with same mac address " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(bssid)); + return -EINVAL; + } + + hdd_dump_connect_req(adapter, ndev, req); + + /* + * Disable NAN Discovery if incoming connection is P2P or if a STA + * connection already exists and if this is a case of STA+STA + * or SAP+STA concurrency + */ + sta_cnt = policy_mgr_get_mode_specific_conn_info(hdd_ctx->psoc, NULL, + vdev_id_list, + PM_STA_MODE); + sap_cnt = policy_mgr_get_mode_specific_conn_info(hdd_ctx->psoc, NULL, + &vdev_id_list[sta_cnt], + PM_SAP_MODE); + + if (adapter->device_mode == QDF_P2P_CLIENT_MODE || sap_cnt || sta_cnt) { + for (i = 0; i < sta_cnt + sap_cnt; i++) + if (vdev_id_list[i] == adapter->vdev_id) + disable_nan = false; + if (disable_nan) { + hdd_debug("Invalid NAN concurrency. SAP: %d STA: %d P2P: %d", + sap_cnt, sta_cnt, + (adapter->device_mode == QDF_P2P_CLIENT_MODE)); + ucfg_nan_disable_concurrency(hdd_ctx->psoc); + } + } + /* + * STA+NDI concurrency gets preference over NDI+NDI. Disable + * first NDI in case an NDI+NDI concurrency exists if FW does + * not support 4 port concurrency of two NDI + NAN with STA. + */ + if (!ucfg_nan_is_sta_nan_ndi_4_port_allowed(hdd_ctx->psoc)) + ucfg_nan_check_and_disable_unsupported_ndi(hdd_ctx->psoc, + false); + + /* + * In STA + STA roaming scenario, connection to same ssid but different + * bssid is allowed on both vdevs. So there could be a race where the + * STA1 connectes to a bssid when roaming is in progress on STA2 for + * the same bssid. Here the firwmare would have already created peer for + * the roam candidate and host would have created peer on the other + * vdev. When roam synch indication is received, then peer create fails + * at host for the roaming vdev due to duplicate peer detection logic. + * Still roam synch confirm is sent to the firmware. + * When disconnection is received for STA1, then del bss is sent for + * this vdev and firmware asserts as the peer was not created for this + * vdev. + */ + if (hdd_is_roaming_in_progress(hdd_ctx) || + sme_is_any_session_in_middle_of_roaming(hdd_ctx->mac_handle)) { + hdd_err("Roaming in progress. Defer connect"); + return -EBUSY; + } + + /* + * Check if this is reassoc to same bssid, if reassoc is success, return + */ + status = wlan_hdd_reassoc_bssid_hint(adapter, req); + if (!status) + return status; + + /* Try disconnecting if already in connected state */ + status = wlan_hdd_try_disconnect(adapter, + eSIR_MAC_UNSPEC_FAILURE_REASON); + if (0 > status) { + hdd_err("Failed to disconnect the existing connection"); + return -EALREADY; + } + + /*initialise security parameters */ + status = wlan_hdd_cfg80211_set_privacy(adapter, req); + if (status < 0) { + hdd_err("Failed to set security params"); + return status; + } + + if (req->channel) + ch_freq = req->channel->center_freq; + else + ch_freq = 0; + + wlan_hdd_check_ht20_ht40_ind(hdd_ctx, adapter, req); + + status = wlan_hdd_cfg80211_connect_start(adapter, req->ssid, + req->ssid_len, req->bssid, + bssid_hint, ch_freq, 0); + if (status) { + wlan_hdd_cfg80211_clear_privacy(adapter); + hdd_err("connect failed"); + } + + hdd_exit(); + + return status; +} + +/** + * wlan_hdd_cfg80211_connect() - cfg80211 connect api + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @req: Pointer to cfg80211 connect request + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_connect(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_connect_params *req) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(ndev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_connect(wiphy, ndev, req); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_ieee80211_reason_code_to_str() - return string conversion of reason code + * @reason: ieee80211 reason code. + * + * This utility function helps log string conversion of reason code. + * + * Return: string conversion of reason code, if match found; + * "Unknown" otherwise. + */ +static const char *hdd_ieee80211_reason_code_to_str(uint16_t reason) +{ + switch (reason) { + CASE_RETURN_STRING(WLAN_REASON_UNSPECIFIED); + CASE_RETURN_STRING(WLAN_REASON_PREV_AUTH_NOT_VALID); + CASE_RETURN_STRING(WLAN_REASON_DEAUTH_LEAVING); + CASE_RETURN_STRING(WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + CASE_RETURN_STRING(WLAN_REASON_DISASSOC_AP_BUSY); + CASE_RETURN_STRING(WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); + CASE_RETURN_STRING(WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + CASE_RETURN_STRING(WLAN_REASON_DISASSOC_STA_HAS_LEFT); + CASE_RETURN_STRING(WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH); + CASE_RETURN_STRING(WLAN_REASON_DISASSOC_BAD_POWER); + CASE_RETURN_STRING(WLAN_REASON_DISASSOC_BAD_SUPP_CHAN); + CASE_RETURN_STRING(WLAN_REASON_INVALID_IE); + CASE_RETURN_STRING(WLAN_REASON_MIC_FAILURE); + CASE_RETURN_STRING(WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT); + CASE_RETURN_STRING(WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT); + CASE_RETURN_STRING(WLAN_REASON_IE_DIFFERENT); + CASE_RETURN_STRING(WLAN_REASON_INVALID_GROUP_CIPHER); + CASE_RETURN_STRING(WLAN_REASON_INVALID_PAIRWISE_CIPHER); + CASE_RETURN_STRING(WLAN_REASON_INVALID_AKMP); + CASE_RETURN_STRING(WLAN_REASON_UNSUPP_RSN_VERSION); + CASE_RETURN_STRING(WLAN_REASON_INVALID_RSN_IE_CAP); + CASE_RETURN_STRING(WLAN_REASON_IEEE8021X_FAILED); + CASE_RETURN_STRING(WLAN_REASON_CIPHER_SUITE_REJECTED); + CASE_RETURN_STRING(WLAN_REASON_DISASSOC_UNSPECIFIED_QOS); + CASE_RETURN_STRING(WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH); + CASE_RETURN_STRING(WLAN_REASON_DISASSOC_LOW_ACK); + CASE_RETURN_STRING(WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP); + CASE_RETURN_STRING(WLAN_REASON_QSTA_LEAVE_QBSS); + CASE_RETURN_STRING(WLAN_REASON_QSTA_NOT_USE); + CASE_RETURN_STRING(WLAN_REASON_QSTA_REQUIRE_SETUP); + CASE_RETURN_STRING(WLAN_REASON_QSTA_TIMEOUT); + CASE_RETURN_STRING(WLAN_REASON_QSTA_CIPHER_NOT_SUPP); + CASE_RETURN_STRING(WLAN_REASON_MESH_PEER_CANCELED); + CASE_RETURN_STRING(WLAN_REASON_MESH_MAX_PEERS); + CASE_RETURN_STRING(WLAN_REASON_MESH_CONFIG); + CASE_RETURN_STRING(WLAN_REASON_MESH_CLOSE); + CASE_RETURN_STRING(WLAN_REASON_MESH_MAX_RETRIES); + CASE_RETURN_STRING(WLAN_REASON_MESH_CONFIRM_TIMEOUT); + CASE_RETURN_STRING(WLAN_REASON_MESH_INVALID_GTK); + CASE_RETURN_STRING(WLAN_REASON_MESH_INCONSISTENT_PARAM); + CASE_RETURN_STRING(WLAN_REASON_MESH_INVALID_SECURITY); + CASE_RETURN_STRING(WLAN_REASON_MESH_PATH_ERROR); + CASE_RETURN_STRING(WLAN_REASON_MESH_PATH_NOFORWARD); + CASE_RETURN_STRING(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE); + CASE_RETURN_STRING(WLAN_REASON_MAC_EXISTS_IN_MBSS); + CASE_RETURN_STRING(WLAN_REASON_MESH_CHAN_REGULATORY); + CASE_RETURN_STRING(WLAN_REASON_MESH_CHAN); + default: + return "Unknown"; + } +} + +/** + * hdd_qca_reason_to_str() - return string conversion of qca reason code + * @reason: enum qca_disconnect_reason_codes + * + * This utility function helps log string conversion of qca reason code. + * + * Return: string conversion of reason code, if match found; + * "Unknown" otherwise. + */ +static const char * +hdd_qca_reason_to_str(enum qca_disconnect_reason_codes reason) +{ + switch (reason) { + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_INTERNAL_ROAM_FAILURE); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_EXTERNAL_ROAM_FAILURE); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_GATEWAY_REACHABILITY_FAILURE); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_UNSUPPORTED_CHANNEL_CSA); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_OPER_CHANNEL_DISABLED_INDOOR); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_OPER_CHANNEL_USER_DISABLED); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_DEVICE_RECOVERY); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_KEY_TIMEOUT); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_OPER_CHANNEL_BAND_CHANGE); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_IFACE_DOWN); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_PEER_XRETRY_FAIL); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_PEER_INACTIVITY); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_SA_QUERY_TIMEOUT); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE); + CASE_RETURN_STRING(QCA_DISCONNECT_REASON_USER_TRIGGERED); + case QCA_DISCONNECT_REASON_UNSPECIFIED: + return ""; + default: + return "Unknown"; + } +} + +/** + * wlan_hdd_sir_mac_to_qca_reason() - Convert to qca internal disconnect reason + * @internal_reason: Mac reason code of type @enum eSirMacReasonCodes + * + * Check if it is internal reason code and convert it to the + * enum qca_disconnect_reason_codes. + * + * Return: Reason code of type enum qca_disconnect_reason_codes + */ +static enum qca_disconnect_reason_codes +wlan_hdd_sir_mac_to_qca_reason(enum eSirMacReasonCodes internal_reason) +{ + enum qca_disconnect_reason_codes reason = + QCA_DISCONNECT_REASON_UNSPECIFIED; + switch (internal_reason) { + case eSIR_MAC_HOST_TRIGGERED_ROAM_FAILURE: + reason = QCA_DISCONNECT_REASON_INTERNAL_ROAM_FAILURE; + break; + case eSIR_MAC_FW_TRIGGERED_ROAM_FAILURE: + reason = QCA_DISCONNECT_REASON_EXTERNAL_ROAM_FAILURE; + break; + case eSIR_MAC_GATEWAY_REACHABILITY_FAILURE: + reason = + QCA_DISCONNECT_REASON_GATEWAY_REACHABILITY_FAILURE; + break; + case eSIR_MAC_UNSUPPORTED_CHANNEL_CSA: + reason = QCA_DISCONNECT_REASON_UNSUPPORTED_CHANNEL_CSA; + break; + case eSIR_MAC_OPER_CHANNEL_DISABLED_INDOOR: + reason = + QCA_DISCONNECT_REASON_OPER_CHANNEL_DISABLED_INDOOR; + break; + case eSIR_MAC_OPER_CHANNEL_USER_DISABLED: + reason = + QCA_DISCONNECT_REASON_OPER_CHANNEL_USER_DISABLED; + break; + case eSIR_MAC_DEVICE_RECOVERY: + reason = QCA_DISCONNECT_REASON_DEVICE_RECOVERY; + break; + case eSIR_MAC_KEY_TIMEOUT: + reason = QCA_DISCONNECT_REASON_KEY_TIMEOUT; + break; + case eSIR_MAC_OPER_CHANNEL_BAND_CHANGE: + reason = QCA_DISCONNECT_REASON_OPER_CHANNEL_BAND_CHANGE; + break; + case eSIR_MAC_IFACE_DOWN: + reason = QCA_DISCONNECT_REASON_IFACE_DOWN; + break; + case eSIR_MAC_PEER_XRETRY_FAIL: + reason = QCA_DISCONNECT_REASON_PEER_XRETRY_FAIL; + break; + case eSIR_MAC_PEER_INACTIVITY: + reason = QCA_DISCONNECT_REASON_PEER_INACTIVITY; + break; + case eSIR_MAC_SA_QUERY_TIMEOUT: + reason = QCA_DISCONNECT_REASON_SA_QUERY_TIMEOUT; + break; + case eSIR_MAC_CHANNEL_SWITCH_FAILED: + reason = QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE; + break; + case eSIR_MAC_BEACON_MISSED: + reason = QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE; + break; + case eSIR_MAC_USER_TRIGGERED_ROAM_FAILURE: + reason = QCA_DISCONNECT_REASON_USER_TRIGGERED; + break; + default: + hdd_debug("No QCA reason code for mac reason: %u", + internal_reason); + /* Unspecified reason by default */ + } + + return reason; +} + +/** + * wlan_hdd_get_ieee80211_disconnect_reason() - Get ieee80211 disconnect reason + * @adapter: pointer to adapter structure + * @reason: Mac Disconnect reason code as per @enum eSirMacReasonCodes + * + * Reason codes that are greater than eSIR_MAC_REASON_PROP_START are internal + * reason codes. Convert them to qca reason code format and cache in adapter + * and return UNSPECIFIED. + * Rest of the reason codes are valid ieee80211 reason codes. + * + * Return: Reason code of type ieee80211_reasoncode. + */ +static enum ieee80211_reasoncode +wlan_hdd_get_cfg80211_disconnect_reason(struct hdd_adapter *adapter, + enum eSirMacReasonCodes reason) +{ + enum ieee80211_reasoncode ieee80211_reason = WLAN_REASON_UNSPECIFIED; + + /* + * Convert and cache internal reason code in adapter. This can be + * sent to userspace with a vendor event. + */ + if (reason >= eSIR_MAC_REASON_PROP_START) { + adapter->last_disconnect_reason = + wlan_hdd_sir_mac_to_qca_reason(reason); + /* + * Applications expect reason code as 0 for beacon miss failure + * due to backward compatibility. So send ieee80211_reason as 0. + */ + if (reason == eSIR_MAC_BEACON_MISSED) + ieee80211_reason = 0; + } else { + ieee80211_reason = (enum ieee80211_reasoncode)reason; + adapter->last_disconnect_reason = + QCA_DISCONNECT_REASON_UNSPECIFIED; + } + + return ieee80211_reason; +} + +#if defined(CFG80211_DISCONNECTED_V2) || \ +(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)) +void +wlan_hdd_cfg80211_indicate_disconnect(struct hdd_adapter *adapter, + bool locally_generated, + enum eSirMacReasonCodes reason, + uint8_t *disconnect_ies, + uint16_t disconnect_ies_len) +{ + enum ieee80211_reasoncode ieee80211_reason; + + ieee80211_reason = wlan_hdd_get_cfg80211_disconnect_reason(adapter, + reason); + hdd_nofl_info("Disconnect reason: %u %s vendor: %u %s LG: %u", + ieee80211_reason, + hdd_ieee80211_reason_code_to_str(ieee80211_reason), + adapter->last_disconnect_reason, + hdd_qca_reason_to_str(adapter->last_disconnect_reason), + locally_generated); + cfg80211_disconnected(adapter->dev, ieee80211_reason, disconnect_ies, + disconnect_ies_len, locally_generated, + GFP_KERNEL); +} +#else +void +wlan_hdd_cfg80211_indicate_disconnect(struct hdd_adapter *adapter, + bool locally_generated, + enum eSirMacReasonCodes reason, + uint8_t *disconnect_ies, + uint16_t disconnect_ies_len) +{ + enum ieee80211_reasoncode ieee80211_reason; + + ieee80211_reason = wlan_hdd_get_cfg80211_disconnect_reason(adapter, + reason); + hdd_nofl_info("Disconnect reason: %u %s vendor: %u %s LG: %u", + ieee80211_reason, + hdd_ieee80211_reason_code_to_str(ieee80211_reason), + adapter->last_disconnect_reason, + hdd_qca_reason_to_str(adapter->last_disconnect_reason), + locally_generated); + cfg80211_disconnected(adapter->dev, ieee80211_reason, disconnect_ies, + disconnect_ies_len, GFP_KERNEL); +} +#endif + +int wlan_hdd_disconnect(struct hdd_adapter *adapter, u16 reason, + tSirMacReasonCodes mac_reason) +{ + int ret; + mac_handle_t mac_handle; + + mac_handle = hdd_adapter_get_mac_handle(adapter); + wlan_hdd_wait_for_roaming(mac_handle, adapter); + + /*stop tx queues */ + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, WLAN_CONTROL_PATH); + + /* Disable STA power-save mode */ + if ((adapter->device_mode == QDF_STA_MODE) && + wlan_hdd_set_powersave(adapter, false, 0)) + hdd_debug("Not disable PS for STA"); + + ret = wlan_hdd_wait_for_disconnect(mac_handle, adapter, reason, + mac_reason); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + /* Sending disconnect event to userspace for kernel version < 3.11 + * is handled by __cfg80211_disconnect call to __cfg80211_disconnected + */ + wlan_hdd_cfg80211_indicate_disconnect(adapter, true, + mac_reason, NULL, 0); +#endif + + return ret; +} + +/** + * __wlan_hdd_cfg80211_disconnect() - cfg80211 disconnect api + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @reason: Disconnect reason code + * + * This function is used to issue a disconnect request to SME + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *dev, u16 reason) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int status; + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct wlan_objmgr_vdev *vdev; + bool enable_deauth_to_disassoc_map; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_DISCONNECT, + adapter->vdev_id, reason); + + hdd_print_netdev_txq_status(dev); + status = wlan_hdd_validate_context(hdd_ctx); + + if (0 != status) + return status; + + qdf_mutex_acquire(&adapter->disconnection_status_lock); + if (adapter->disconnection_in_progress) { + qdf_mutex_release(&adapter->disconnection_status_lock); + hdd_debug("Disconnect is already in progress"); + return 0; + } + adapter->disconnection_in_progress = true; + qdf_mutex_release(&adapter->disconnection_status_lock); + + /* Issue disconnect request to SME, if station is in connected state */ + if ((sta_ctx->conn_info.conn_state == eConnectionState_Associated) || + (sta_ctx->conn_info.conn_state == eConnectionState_Connecting)) { + eCsrRoamDisconnectReason reasonCode = + eCSR_DISCONNECT_REASON_UNSPECIFIED; + + switch (reason) { + case WLAN_REASON_MIC_FAILURE: + reasonCode = eCSR_DISCONNECT_REASON_MIC_ERROR; + break; + + case WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY: + case WLAN_REASON_DISASSOC_AP_BUSY: + case WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA: + reasonCode = eCSR_DISCONNECT_REASON_DISASSOC; + break; + + case WLAN_REASON_PREV_AUTH_NOT_VALID: + case WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA: + reasonCode = eCSR_DISCONNECT_REASON_DEAUTH; + break; + + case WLAN_REASON_DEAUTH_LEAVING: + status = ucfg_mlme_get_enable_deauth_to_disassoc_map( + hdd_ctx->psoc, + &enable_deauth_to_disassoc_map); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + reasonCode = + enable_deauth_to_disassoc_map ? + eCSR_DISCONNECT_REASON_STA_HAS_LEFT : + eCSR_DISCONNECT_REASON_DEAUTH; + qdf_dp_trace_dump_all( + WLAN_DEAUTH_DPTRACE_DUMP_COUNT, + QDF_TRACE_DEFAULT_PDEV_ID); + break; + case WLAN_REASON_DISASSOC_STA_HAS_LEFT: + reasonCode = eCSR_DISCONNECT_REASON_STA_HAS_LEFT; + break; + default: + reasonCode = eCSR_DISCONNECT_REASON_UNSPECIFIED; + break; + } + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + if (ucfg_scan_get_vdev_status(vdev) != SCAN_NOT_IN_PROGRESS) + wlan_abort_scan(hdd_ctx->pdev, INVAL_PDEV_ID, + adapter->vdev_id, INVALID_SCAN_ID, + false); + + wlan_hdd_cleanup_remain_on_channel_ctx(adapter); + /* First clean up the tdls peers if any */ + hdd_notify_sta_disconnect(adapter->vdev_id, + false, true, vdev); + hdd_objmgr_put_vdev(vdev); + + hdd_nofl_info("%s(vdevid-%d): Received Disconnect reason:%d %s", + dev->name, adapter->vdev_id, reason, + hdd_ieee80211_reason_code_to_str(reason)); + status = wlan_hdd_disconnect(adapter, reasonCode, reason); + if (0 != status) { + hdd_err("wlan_hdd_disconnect failed, status: %d", status); + hdd_set_disconnect_status(adapter, false); + return -EINVAL; + } + } else { + hdd_err("Unexpected cfg disconnect called while in state: %d", + sta_ctx->conn_info.conn_state); + hdd_set_disconnect_status(adapter, false); + } + + return status; +} + +/** + * wlan_hdd_cfg80211_disconnect() - cfg80211 disconnect api + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @reason: Disconnect reason code + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *dev, u16 reason) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_disconnect(wiphy, dev, reason); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_set_wiphy_params() - set wiphy parameters + * @wiphy: Pointer to wiphy + * @changed: Parameters changed + * + * This function is used to set the phy parameters. RTS Threshold/FRAG + * Threshold/Retry Count etc. + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_set_wiphy_params(struct wiphy *wiphy, + u32 changed) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int status; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SET_WIPHY_PARAMS, + NO_SESSION, wiphy->rts_threshold); + + status = wlan_hdd_validate_context(hdd_ctx); + + if (0 != status) + return status; + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + u32 rts_threshold = (wiphy->rts_threshold == -1) ? + cfg_max(CFG_RTS_THRESHOLD) : + wiphy->rts_threshold; + + if ((cfg_min(CFG_RTS_THRESHOLD) > rts_threshold) || + (cfg_max(CFG_RTS_THRESHOLD) < rts_threshold)) { + hdd_err("Invalid RTS Threshold value: %u", + rts_threshold); + return -EINVAL; + } + + if (0 != ucfg_mlme_set_rts_threshold(hdd_ctx->psoc, + rts_threshold)) { + hdd_err("mlme_set_rts_threshold failed for val %u", + rts_threshold); + return -EIO; + } + + hdd_debug("set rts threshold %u", rts_threshold); + } + + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + u16 frag_threshold = (wiphy->frag_threshold == -1) ? + cfg_max(CFG_FRAG_THRESHOLD) : + wiphy->frag_threshold; + + if ((cfg_min(CFG_FRAG_THRESHOLD) > frag_threshold) || + (cfg_max(CFG_FRAG_THRESHOLD) < frag_threshold)) { + hdd_err("Invalid frag_threshold value %hu", + frag_threshold); + return -EINVAL; + } + + if (0 != ucfg_mlme_set_frag_threshold(hdd_ctx->psoc, + frag_threshold)) { + hdd_err("mlme_set_frag_threshold failed for val %hu", + frag_threshold); + return -EIO; + } + + hdd_debug("set frag threshold %hu", frag_threshold); + } + + hdd_exit(); + return 0; +} + +/** + * wlan_hdd_cfg80211_set_wiphy_params() - set wiphy parameters + * @wiphy: Pointer to wiphy + * @changed: Parameters changed + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_wiphy_params(wiphy, changed); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * __wlan_hdd_set_default_mgmt_key() - dummy implementation of set default mgmt + * key + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @key_index: Key index + * + * Return: 0 + */ +static int __wlan_hdd_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *netdev, + u8 key_index) +{ + hdd_enter(); + return 0; +} + +/** + * wlan_hdd_set_default_mgmt_key() - SSR wrapper for + * wlan_hdd_set_default_mgmt_key + * @wiphy: pointer to wiphy + * @netdev: pointer to net_device structure + * @key_index: key index + * + * Return: 0 on success, error number on failure + */ +static int wlan_hdd_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *netdev, + u8 key_index) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_set_default_mgmt_key(wiphy, netdev, key_index); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * Default val of cwmin, this value is used to overide the + * incorrect user set value + */ +#define DEFAULT_CWMIN 15 + +/** + * Default val of cwmax, this value is used to overide the + * incorrect user set value + */ +#define DEFAULT_CWMAX 1023 + +/** + * __wlan_hdd_set_txq_params() - implementation of set tx queue params + * to configure internal EDCA parameters + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @params: Pointer to tx queue parameters + * + * Return: 0 + */ +static int __wlan_hdd_set_txq_params(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_txq_params *params) +{ + QDF_STATUS status; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle; + tSirMacEdcaParamRecord txq_edca_params; + static const uint8_t ieee_ac_to_qca_ac[] = { + [IEEE80211_AC_VO] = QCA_WLAN_AC_VO, + [IEEE80211_AC_VI] = QCA_WLAN_AC_VI, + [IEEE80211_AC_BE] = QCA_WLAN_AC_BE, + [IEEE80211_AC_BK] = QCA_WLAN_AC_BK, + }; + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + mac_handle = hdd_ctx->mac_handle; + if (params->cwmin == 0 || params->cwmin > DEFAULT_CWMAX) + params->cwmin = DEFAULT_CWMIN; + + if (params->cwmax < params->cwmin || params->cwmax > DEFAULT_CWMAX) + params->cwmax = DEFAULT_CWMAX; + + txq_edca_params.cw.min = convert_cw(params->cwmin); + txq_edca_params.cw.max = convert_cw(params->cwmax); + txq_edca_params.aci.aifsn = params->aifs; + /* The txop is multiple of 32us units */ + txq_edca_params.txoplimit = params->txop; + txq_edca_params.aci.aci = + ieee_ac_to_qca_ac[params->ac]; + + status = sme_update_session_txq_edca_params(mac_handle, + adapter->vdev_id, + &txq_edca_params); + + hdd_exit(); + return qdf_status_to_os_return(status); +} + +/** + * wlan_hdd_set_txq_params() - SSR wrapper for wlan_hdd_set_txq_params + * @wiphy: pointer to wiphy + * @netdev: pointer to net_device structure + * @params: pointer to ieee80211_txq_params + * + * Return: 0 on success, error number on failure + */ +static int wlan_hdd_set_txq_params(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_txq_params *params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_set_txq_params(wiphy, dev, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_softap_deauth_current_sta() - Deauth current sta + * @sta_info: pointer to the current station info structure + * @adapter: pointer to adapter structure + * @hdd_ctx: pointer to hdd context + * @hapd_state: pointer to hostapd state structure + * @param: pointer to del sta params + * + * Return: QDF_STATUS on success, corresponding QDF failure status on failure + */ +static +QDF_STATUS hdd_softap_deauth_current_sta(struct hdd_adapter *adapter, + struct hdd_station_info *sta_info, + struct hdd_hostapd_state *hapd_state, + struct csr_del_sta_params *param) +{ + qdf_event_t *disassoc_event = &hapd_state->qdf_sta_disassoc_event; + struct hdd_context *hdd_ctx; + QDF_STATUS qdf_status; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("hdd_ctx is NULL"); + return QDF_STATUS_E_INVAL; + } + + qdf_event_reset(&hapd_state->qdf_sta_disassoc_event); + + if (!qdf_is_macaddr_broadcast(¶m->peerMacAddr)) + sme_send_disassoc_req_frame(hdd_ctx->mac_handle, + adapter->vdev_id, + (uint8_t *)¶m->peerMacAddr, + param->reason_code, 0); + + qdf_status = hdd_softap_sta_deauth(adapter, param); + + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + sta_info->is_deauth_in_progress = true; + qdf_status = qdf_wait_for_event_completion( + disassoc_event, + SME_PEER_DISCONNECT_TIMEOUT); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_warn("Deauth time expired"); + } else { + sta_info->is_deauth_in_progress = false; + hdd_debug("STA removal failed for ::" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); + return QDF_STATUS_E_NOENT; + } + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_softap_deauth_all_sta() - Deauth all sta in the sta list + * @hdd_ctx: pointer to hdd context + * @adapter: pointer to adapter structure + * @hapd_state: pointer to hostapd state structure + * @param: pointer to del sta params + * + * Return: QDF_STATUS on success, corresponding QDF failure status on failure + */ +static +QDF_STATUS hdd_softap_deauth_all_sta(struct hdd_adapter *adapter, + struct hdd_hostapd_state *hapd_state, + struct csr_del_sta_params *param) +{ + QDF_STATUS status; + bool is_sap_bcast_deauth_enabled = false; + struct hdd_context *hdd_ctx; + struct hdd_station_info *sta_info, *tmp = NULL; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("hdd_ctx is NULL"); + return QDF_STATUS_E_INVAL; + } + + ucfg_mlme_get_sap_bcast_deauth_enabled(hdd_ctx->psoc, + &is_sap_bcast_deauth_enabled); + + if (is_sap_bcast_deauth_enabled) + return QDF_STATUS_E_INVAL; + + hdd_for_each_sta_ref_safe(adapter->sta_info_list, sta_info, tmp, + STA_INFO_SOFTAP_DEAUTH_ALL_STA) { + if (!sta_info->is_deauth_in_progress) { + hdd_debug("Delete STA with MAC:" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); + qdf_mem_copy(param->peerMacAddr.bytes, + sta_info->sta_mac.bytes, + QDF_MAC_ADDR_SIZE); + status = + hdd_softap_deauth_current_sta(adapter, sta_info, + hapd_state, param); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_put_sta_info_ref( + &adapter->sta_info_list, + &sta_info, true, + STA_INFO_SOFTAP_DEAUTH_ALL_STA); + if (tmp) + hdd_put_sta_info_ref( + &adapter->sta_info_list, + &tmp, true, + STA_INFO_SOFTAP_DEAUTH_ALL_STA); + return status; + } + } + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_DEAUTH_ALL_STA); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * __wlan_hdd_cfg80211_del_station() - delete station v2 + * @wiphy: Pointer to wiphy + * @dev: Underlying net device + * @param: Pointer to delete station parameter + * + * Return: 0 for success, non-zero for failure + */ +static +int __wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *dev, + struct csr_del_sta_params *param) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + struct hdd_hostapd_state *hapd_state; + uint8_t *mac; + mac_handle_t mac_handle; + struct hdd_station_info *sta_info; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_DEL_STA, + adapter->vdev_id, adapter->device_mode); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("hdd_ctx is NULL"); + return -EINVAL; + } + + mac = (uint8_t *) param->peerMacAddr.bytes; + mac_handle = hdd_ctx->mac_handle; + + if (QDF_SAP_MODE != adapter->device_mode && + QDF_P2P_GO_MODE != adapter->device_mode) + goto fn_end; + + hapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + if (!hapd_state) { + hdd_err("Hostapd State is Null"); + return 0; + } + + if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac)) { + if (!QDF_IS_STATUS_SUCCESS(hdd_softap_deauth_all_sta(adapter, + hapd_state, + param))) + goto fn_end; + } else { + if (param->reason_code == eSIR_MAC_1X_AUTH_FAILURE_REASON) + hdd_softap_check_wait_for_tx_eap_pkt( + adapter, (struct qdf_mac_addr *)mac); + + sta_info = hdd_get_sta_info_by_mac( + &adapter->sta_info_list, + mac, + STA_INFO_CFG80211_DEL_STATION); + + if (!sta_info) { + hdd_debug("Skip DEL STA as this is not used::" + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac)); + return -ENOENT; + } + + if (sta_info->is_deauth_in_progress) { + hdd_debug("Skip DEL STA as deauth is in progress::" + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac)); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, + true, + STA_INFO_CFG80211_DEL_STATION); + return -ENOENT; + } + + hdd_debug("ucast, Delete STA with MAC:" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac)); + hdd_softap_deauth_current_sta(adapter, sta_info, hapd_state, + param); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_CFG80211_DEL_STATION); + } + +fn_end: + hdd_exit(); + return 0; +} + +#if defined(USE_CFG80211_DEL_STA_V2) +/** + * wlan_hdd_del_station() - delete station wrapper + * @adapter: pointer to the hdd adapter + * + * Return: Errno + */ +int wlan_hdd_del_station(struct hdd_adapter *adapter) +{ + struct station_del_parameters del_sta; + + del_sta.mac = NULL; + del_sta.subtype = IEEE80211_STYPE_DEAUTH >> 4; + del_sta.reason_code = WLAN_REASON_DEAUTH_LEAVING; + + return wlan_hdd_cfg80211_del_station(adapter->wdev.wiphy, + adapter->dev, &del_sta); +} +#else +int wlan_hdd_del_station(struct hdd_adapter *adapter) +{ + return wlan_hdd_cfg80211_del_station(adapter->wdev.wiphy, + adapter->dev, NULL); +} +#endif + +/** + * wlan_hdd_cfg80211_del_station() - delete station entry handler + * @wiphy: Pointer to wiphy + * @dev: net_device to operate against + * @mac: binary mac address + * @reason_code: reason for the deauthorization/disassociation + * @subtype: management frame subtype to indicate removal + * + * Return: Errno + */ +static int _wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *mac, + uint16_t reason_code, + uint8_t subtype) +{ + int errno; + struct csr_del_sta_params delStaParams; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + wlansap_populate_del_sta_params(mac, reason_code, subtype, + &delStaParams); + errno = __wlan_hdd_cfg80211_del_station(wiphy, dev, &delStaParams); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef USE_CFG80211_DEL_STA_V2 +int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *dev, + struct station_del_parameters *param) +{ + if (!param) + return -EINVAL; + + return _wlan_hdd_cfg80211_del_station(wiphy, dev, param->mac, + param->reason_code, + param->subtype); +} +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, + const uint8_t *mac) +{ + uint16_t reason = eSIR_MAC_DEAUTH_LEAVING_BSS_REASON; + uint8_t subtype = SIR_MAC_MGMT_DEAUTH >> 4; + + return _wlan_hdd_cfg80211_del_station(wiphy, dev, mac, reason, subtype); +} +#else +int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, + uint8_t *mac) +{ + uint16_t reason = eSIR_MAC_DEAUTH_LEAVING_BSS_REASON; + uint8_t subtype = SIR_MAC_MGMT_DEAUTH >> 4; + + return _wlan_hdd_cfg80211_del_station(wiphy, dev, mac, reason, subtype); +} +#endif + +/** + * __wlan_hdd_cfg80211_add_station() - add station + * @wiphy: Pointer to wiphy + * @mac: Pointer to station mac address + * @pmksa: Pointer to add station parameter + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_add_station(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *mac, + struct station_parameters *params) +{ + int status = -EPERM; +#ifdef FEATURE_WLAN_TDLS + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + u32 mask, set; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_ADD_STA, + adapter->vdev_id, params->listen_interval); + + if (0 != wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + mask = params->sta_flags_mask; + + set = params->sta_flags_set; + + hdd_debug("mask 0x%x set 0x%x " QDF_MAC_ADDR_FMT, mask, set, + QDF_MAC_ADDR_REF(mac)); + + if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { + if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) { + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + status = wlan_cfg80211_tdls_add_peer(vdev, + mac); + hdd_objmgr_put_vdev(vdev); + } + } + } +#endif + hdd_exit(); + return status; +} + +/** + * wlan_hdd_cfg80211_add_station() - add station + * @wiphy: Pointer to wiphy + * @mac: Pointer to station mac address + * @pmksa: Pointer to add station parameter + * + * Return: 0 for success, non-zero for failure + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +static int wlan_hdd_cfg80211_add_station(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *mac, + struct station_parameters *params) +#else +static int wlan_hdd_cfg80211_add_station(struct wiphy *wiphy, + struct net_device *dev, uint8_t *mac, + struct station_parameters *params) +#endif +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_add_station(wiphy, dev, mac, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static QDF_STATUS wlan_hdd_set_pmksa_cache(struct hdd_adapter *adapter, + tPmkidCacheInfo *pmk_cache) +{ + QDF_STATUS result; + struct wlan_crypto_pmksa *pmksa; + struct wlan_objmgr_vdev *vdev; + mac_handle_t mac_handle; + struct hdd_context *hdd_ctx; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return QDF_STATUS_E_INVAL; + } + + if (wlan_hdd_validate_context(hdd_ctx)) + return QDF_STATUS_E_INVAL; + mac_handle = hdd_ctx->mac_handle; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return QDF_STATUS_E_FAILURE; + + pmksa = qdf_mem_malloc(sizeof(*pmksa)); + if (!pmksa) { + hdd_objmgr_put_vdev(vdev); + return QDF_STATUS_E_NOMEM; + } + + if (!pmk_cache->ssid_len) { + qdf_copy_macaddr(&pmksa->bssid, &pmk_cache->BSSID); + } else { + qdf_mem_copy(pmksa->ssid, pmk_cache->ssid, pmk_cache->ssid_len); + qdf_mem_copy(pmksa->cache_id, pmk_cache->cache_id, + WLAN_CACHE_ID_LEN); + pmksa->ssid_len = pmk_cache->ssid_len; + } + qdf_mem_copy(pmksa->pmkid, pmk_cache->PMKID, PMKID_LEN); + qdf_mem_copy(pmksa->pmk, pmk_cache->pmk, pmk_cache->pmk_len); + pmksa->pmk_len = pmk_cache->pmk_len; + + result = wlan_crypto_set_del_pmksa(vdev, pmksa, true); + if (result != QDF_STATUS_SUCCESS) { + qdf_mem_zero(pmksa, sizeof(*pmksa)); + qdf_mem_free(pmksa); + } + + if (result == QDF_STATUS_SUCCESS && pmk_cache->pmk_len) + sme_roam_set_psk_pmk(mac_handle, adapter->vdev_id, + pmk_cache->pmk, pmk_cache->pmk_len, + false); + hdd_objmgr_put_vdev(vdev); + + return result; +} + +static QDF_STATUS wlan_hdd_del_pmksa_cache(struct hdd_adapter *adapter, + tPmkidCacheInfo *pmk_cache) +{ + QDF_STATUS result; + struct wlan_crypto_pmksa pmksa; + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return QDF_STATUS_E_FAILURE; + + qdf_copy_macaddr(&pmksa.bssid, &pmk_cache->BSSID); + result = wlan_crypto_set_del_pmksa(adapter->vdev, &pmksa, false); + hdd_objmgr_put_vdev(vdev); + + return result; +} + +QDF_STATUS wlan_hdd_flush_pmksa_cache(struct hdd_adapter *adapter) +{ + QDF_STATUS result; + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return QDF_STATUS_E_FAILURE; + + result = wlan_crypto_set_del_pmksa(adapter->vdev, NULL, false); + hdd_objmgr_put_vdev(vdev); + + return result; +} + +#if defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) +/* + * wlan_hdd_is_pmksa_valid: API to validate pmksa + * @pmksa: pointer to cfg80211_pmksa structure + * + * Return: True if valid else false + */ +static inline bool wlan_hdd_is_pmksa_valid(struct cfg80211_pmksa *pmksa) +{ + if (!pmksa->bssid) { + hdd_warn("bssid is NULL"); + if (!pmksa->ssid || !pmksa->cache_id) { + hdd_err("either ssid or cache_id are NULL"); + return false; + } + } + return true; +} + +/* + * hdd_fill_pmksa_info: API to update tPmkidCacheInfo from cfg80211_pmksa + * @adapter: Pointer to hdd adapter + * @pmk_cache: pmk that needs to be udated + * @pmksa: pmk from supplicant + * @is_delete: Bool to decide set or delete PMK + * Return: None + */ +static void hdd_fill_pmksa_info(struct hdd_adapter *adapter, + tPmkidCacheInfo *pmk_cache, + struct cfg80211_pmksa *pmksa, bool is_delete) +{ + if (pmksa->bssid) { + hdd_debug("%s PMKSA for " QDF_MAC_ADDR_FMT, + is_delete ? "Delete" : "Set", + QDF_MAC_ADDR_REF(pmksa->bssid)); + qdf_mem_copy(pmk_cache->BSSID.bytes, + pmksa->bssid, QDF_MAC_ADDR_SIZE); + } else { + qdf_mem_copy(pmk_cache->ssid, pmksa->ssid, pmksa->ssid_len); + qdf_mem_copy(pmk_cache->cache_id, pmksa->cache_id, + CACHE_ID_LEN); + pmk_cache->ssid_len = pmksa->ssid_len; + hdd_debug("%s PMKSA for ssid %*.*s cache_id %x %x", + is_delete ? "Delete" : "Set", + pmk_cache->ssid_len, pmk_cache->ssid_len, + pmk_cache->ssid, pmk_cache->cache_id[0], + pmk_cache->cache_id[1]); + } + + if (is_delete) + return; + + qdf_mem_copy(pmk_cache->PMKID, pmksa->pmkid, PMKID_LEN); + if (pmksa->pmk_len && (pmksa->pmk_len <= CSR_RSN_MAX_PMK_LEN)) { + qdf_mem_copy(pmk_cache->pmk, pmksa->pmk, pmksa->pmk_len); + pmk_cache->pmk_len = pmksa->pmk_len; + } else + hdd_debug("pmk len is %zu", pmksa->pmk_len); +} +#else +/* + * wlan_hdd_is_pmksa_valid: API to validate pmksa + * @pmksa: pointer to cfg80211_pmksa structure + * + * Return: True if valid else false + */ +static inline bool wlan_hdd_is_pmksa_valid(struct cfg80211_pmksa *pmksa) +{ + if (!pmksa->bssid) { + hdd_err("both bssid is NULL %pK", pmksa->bssid); + return false; + } + return true; +} + +/* + * hdd_fill_pmksa_info: API to update tPmkidCacheInfo from cfg80211_pmksa + * @adapter: Pointer to hdd adapter + * @pmk_cache: pmk which needs to be updated + * @pmksa: pmk from supplicant + * @is_delete: Bool to decide whether to set or delete PMK + * + * Return: None + */ +static void hdd_fill_pmksa_info(struct hdd_adapter *adapter, + tPmkidCacheInfo *pmk_cache, + struct cfg80211_pmksa *pmksa, bool is_delete) +{ + mac_handle_t mac_handle; + + hdd_debug("%s PMKSA for " QDF_MAC_ADDR_FMT, is_delete ? "Delete" : "Set", + QDF_MAC_ADDR_REF(pmksa->bssid)); + qdf_mem_copy(pmk_cache->BSSID.bytes, + pmksa->bssid, QDF_MAC_ADDR_SIZE); + + if (is_delete) + return; + mac_handle = hdd_adapter_get_mac_handle(adapter); + sme_get_pmk_info(mac_handle, adapter->vdev_id, pmk_cache); + qdf_mem_copy(pmk_cache->PMKID, pmksa->pmkid, PMKID_LEN); +} +#endif + +/** + * __wlan_hdd_cfg80211_set_pmksa() - set pmksa + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @pmksa: Pointer to set pmksa parameter + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_set_pmksa(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS result = QDF_STATUS_SUCCESS; + int status; + tPmkidCacheInfo *pmk_cache; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + if (!pmksa) { + hdd_err("pmksa is NULL"); + return -EINVAL; + } + + if (!pmksa->pmkid) { + hdd_err("pmksa->pmkid(%pK) is NULL", + pmksa->pmkid); + return -EINVAL; + } + + if (!wlan_hdd_is_pmksa_valid(pmksa)) + return -EINVAL; + + status = wlan_hdd_validate_context(hdd_ctx); + + if (0 != status) + return status; + + pmk_cache = qdf_mem_malloc(sizeof(*pmk_cache)); + if (!pmk_cache) + return -ENOMEM; + + hdd_fill_pmksa_info(adapter, pmk_cache, pmksa, false); + + /* + * Add to the PMKSA Cache in CSR + * PMKSA cache will be having following + * 1. pmkid id + * 2. pmk + * 3. bssid or cache identifier + */ + result = wlan_hdd_set_pmksa_cache(adapter, pmk_cache); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SET_PMKSA, + adapter->vdev_id, result); + + sme_set_del_pmkid_cache(hdd_ctx->psoc, adapter->vdev_id, + pmk_cache, true); + + qdf_mem_zero(pmk_cache, sizeof(pmk_cache)); + + qdf_mem_free(pmk_cache); + hdd_exit(); + + return QDF_IS_STATUS_SUCCESS(result) ? 0 : -EINVAL; +} + +/** + * wlan_hdd_cfg80211_set_pmksa() - set pmksa + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @pmksa: Pointer to set pmksa parameter + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_set_pmksa(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_pmksa(wiphy, dev, pmksa); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_del_pmksa() - delete pmksa + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @pmksa: Pointer to pmksa parameter + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_del_pmksa(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int status = 0; + tPmkidCacheInfo *pmk_cache; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + if (!pmksa) { + hdd_err("pmksa is NULL"); + return -EINVAL; + } + + if (!wlan_hdd_is_pmksa_valid(pmksa)) + return -EINVAL; + + status = wlan_hdd_validate_context(hdd_ctx); + + if (0 != status) + return status; + + pmk_cache = qdf_mem_malloc(sizeof(*pmk_cache)); + if (!pmk_cache) + return -ENOMEM; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_DEL_PMKSA, + adapter->vdev_id, 0); + + hdd_fill_pmksa_info(adapter, pmk_cache, pmksa, true); + + /* clear single_pmk_info information */ + sme_clear_sae_single_pmk_info(hdd_ctx->psoc, adapter->vdev_id, + pmk_cache); + + /* Delete the PMKID CSR cache */ + if (QDF_STATUS_SUCCESS != + wlan_hdd_del_pmksa_cache(adapter, pmk_cache)) { + if (!pmksa->bssid) + hdd_err("Failed to delete PMKSA for null bssid"); + else + hdd_err("Failed to delete PMKSA for " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pmksa->bssid)); + status = -EINVAL; + } + + sme_set_del_pmkid_cache(hdd_ctx->psoc, adapter->vdev_id, pmk_cache, + false); + + qdf_mem_zero(pmk_cache, sizeof(*pmk_cache)); + qdf_mem_free(pmk_cache); + + hdd_exit(); + + return status; +} + +/** + * wlan_hdd_cfg80211_del_pmksa() - delete pmksa + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @pmksa: Pointer to pmksa parameter + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_del_pmksa(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_del_pmksa(wiphy, dev, pmksa); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; + +} + +/** + * __wlan_hdd_cfg80211_flush_pmksa() - flush pmksa + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_flush_pmksa(struct wiphy *wiphy, + struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int errno; + QDF_STATUS status; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + hdd_debug("Flushing PMKSA"); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + status = wlan_hdd_flush_pmksa_cache(adapter); + if (status == QDF_STATUS_E_NOSUPPORT) { + errno = -EOPNOTSUPP; + } else if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Cannot flush PMKIDCache"); + errno = -EINVAL; + } + + sme_set_del_pmkid_cache(hdd_ctx->psoc, adapter->vdev_id, NULL, false); + hdd_exit(); + return errno; +} + +/** + * wlan_hdd_cfg80211_flush_pmksa() - flush pmksa + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_flush_pmksa(struct wiphy *wiphy, + struct net_device *dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_flush_pmksa(wiphy, dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#if defined(KERNEL_SUPPORT_11R_CFG80211) +/** + * __wlan_hdd_cfg80211_update_ft_ies() - update fast transition ies + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @ftie: Pointer to fast transition ie parameter + * + * Return: 0 for success, non-zero for failure + */ +static int +__wlan_hdd_cfg80211_update_ft_ies(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + int status; + mac_handle_t mac_handle; + + hdd_enter(); + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_UPDATE_FT_IES, + adapter->vdev_id, sta_ctx->conn_info.conn_state); + + /* Added for debug on reception of Re-assoc Req. */ + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + hdd_err("Called with Ie of length = %zu when not associated", + ftie->ie_len); + hdd_err("Should be Re-assoc Req IEs"); + } + hdd_debug("%s called with Ie of length = %zu", __func__, + ftie->ie_len); + + /* Pass the received FT IEs to SME */ + mac_handle = hdd_ctx->mac_handle; + sme_set_ft_ies(mac_handle, adapter->vdev_id, + (const u8 *)ftie->ie, ftie->ie_len); + hdd_exit(); + return 0; +} + +/** + * wlan_hdd_cfg80211_update_ft_ies() - update fast transition ies + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @ftie: Pointer to fast transition ie parameter + * + * Return: 0 for success, non-zero for failure + */ +static int +wlan_hdd_cfg80211_update_ft_ies(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_update_ft_ies(wiphy, dev, ftie); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif + +#ifdef CFG80211_EXTERNAL_DH_UPDATE_SUPPORT +/** + * __wlan_hdd_cfg80211_update_owe_info() - update OWE info + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @owe_info: Pointer to OWE info + * + * Return: 0 for success, non-zero for failure + */ +static int +__wlan_hdd_cfg80211_update_owe_info(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_update_owe_info *owe_info) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + QDF_STATUS status; + int errno; + + hdd_enter_dev(dev); + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) { + hdd_err("invalid vdev id: %d", adapter->vdev_id); + return -EINVAL; + } + + hdd_debug("owe_status %d", owe_info->status); + + status = wlansap_update_owe_info(WLAN_HDD_GET_SAP_CTX_PTR(adapter), + owe_info->peer, owe_info->ie, + owe_info->ie_len, owe_info->status); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to update OWE info"); + errno = qdf_status_to_os_return(status); + } + + hdd_exit(); + return errno; +} + +/** + * wlan_hdd_cfg80211_update_owe_info() - update OWE info + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @owe_info: Pointer to OWE info + * + * Return: 0 for success, non-zero for failure + */ +static int +wlan_hdd_cfg80211_update_owe_info(struct wiphy *wiphy, + struct net_device *net_dev, + struct cfg80211_update_owe_info *owe_info) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_update_owe_info(wiphy, net_dev, owe_info); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif + +void wlan_hdd_cfg80211_update_replay_counter_cb( + void *cb_ctx, struct pmo_gtk_rsp_params *gtk_rsp_param) + +{ + struct hdd_adapter *adapter = (struct hdd_adapter *)cb_ctx; + uint8_t temp_replay_counter[8]; + int i; + uint8_t *p; + + hdd_enter(); + + if (!adapter) { + hdd_err("HDD adapter is Null"); + goto out; + } + + if (!gtk_rsp_param) { + hdd_err("gtk_rsp_param is Null"); + goto out; + } + + if (gtk_rsp_param->status_flag != QDF_STATUS_SUCCESS) { + hdd_err("wlan Failed to get replay counter value"); + goto out; + } + + hdd_debug("updated replay counter: %llu from fwr", + gtk_rsp_param->replay_counter); + /* convert little to big endian since supplicant works on big endian */ + p = (uint8_t *)>k_rsp_param->replay_counter; + for (i = 0; i < 8; i++) + temp_replay_counter[7 - i] = (uint8_t) p[i]; + + hdd_debug("gtk_rsp_param bssid "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(gtk_rsp_param->bssid.bytes)); + /* Update replay counter to NL */ + cfg80211_gtk_rekey_notify(adapter->dev, + gtk_rsp_param->bssid.bytes, + temp_replay_counter, GFP_KERNEL); +out: + hdd_exit(); + +} + +#ifdef WLAN_FEATURE_GTK_OFFLOAD +/** + * wlan_hdd_copy_gtk_kek - Copy the KEK from GTK rekey data to GTK request + * @gtk_req: Pointer to GTK request + * @data: Pointer to rekey data + * + * Return: none + */ +#ifdef CFG80211_REKEY_DATA_KEK_LEN +static +void wlan_hdd_copy_gtk_kek(struct pmo_gtk_req *gtk_req, + struct cfg80211_gtk_rekey_data *data) +{ + qdf_mem_copy(gtk_req->kek, data->kek, data->kek_len); + gtk_req->kek_len = data->kek_len; +} +#else +static +void wlan_hdd_copy_gtk_kek(struct pmo_gtk_req *gtk_req, + struct cfg80211_gtk_rekey_data *data) +{ + qdf_mem_copy(gtk_req->kek, data->kek, NL80211_KEK_LEN); + gtk_req->kek_len = NL80211_KEK_LEN; +} +#endif + +/** + * __wlan_hdd_cfg80211_set_rekey_data() - set rekey data + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @data: Pointer to rekey data + * + * This function is used to offload GTK rekeying job to the firmware. + * + * Return: 0 for success, non-zero for failure + */ +static +int __wlan_hdd_cfg80211_set_rekey_data(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_gtk_rekey_data *data) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int result, i; + struct pmo_gtk_req *gtk_req = NULL; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t *buf; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + result = -EINVAL; + goto out; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) { + result = -EINVAL; + goto out; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SET_REKEY_DATA, + adapter->vdev_id, adapter->device_mode); + + result = wlan_hdd_validate_context(hdd_ctx); + if (0 != result) + goto out; + + gtk_req = qdf_mem_malloc(sizeof(*gtk_req)); + if (!gtk_req) { + result = -ENOMEM; + goto out; + } + + /* convert big to little endian since driver work on little endian */ + buf = (uint8_t *)>k_req->replay_counter; + for (i = 0; i < 8; i++) + buf[7 - i] = data->replay_ctr[i]; + + hdd_debug("current replay counter: %llu in user space", + gtk_req->replay_counter); + + wlan_hdd_copy_gtk_kek(gtk_req, data); + if (data->kck) { + qdf_mem_copy(gtk_req->kck, data->kck, NL80211_KCK_LEN); + gtk_req->kck_len = NL80211_KCK_LEN; + } + gtk_req->is_fils_connection = hdd_is_fils_connection(adapter); + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) { + result = -EINVAL; + goto out; + } + status = ucfg_pmo_cache_gtk_offload_req(vdev, gtk_req); + hdd_objmgr_put_vdev(vdev); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to cache GTK Offload"); + result = qdf_status_to_os_return(status); + } +out: + if (gtk_req) + qdf_mem_free(gtk_req); + hdd_exit(); + + return result; +} + +/** + * wlan_hdd_cfg80211_set_rekey_data() - set rekey data + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @data: Pointer to rekey data + * + * This function is used to offload GTK rekeying job to the firmware. + * + * Return: 0 for success, non-zero for failure + */ +static +int wlan_hdd_cfg80211_set_rekey_data(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_gtk_rekey_data *data) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_rekey_data(wiphy, dev, data); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif /* WLAN_FEATURE_GTK_OFFLOAD */ + +/** + * __wlan_hdd_cfg80211_set_mac_acl() - set access control policy + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @param: Pointer to access control parameter + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_set_mac_acl(struct wiphy *wiphy, + struct net_device *dev, + const struct cfg80211_acl_data *params) +{ + int i; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_hostapd_state *hostapd_state; + struct sap_config *config; + struct hdd_context *hdd_ctx; + int status; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (!params) { + hdd_err("params is Null"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + + if (0 != status) + return status; + + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + + if (!hostapd_state) { + hdd_err("hostapd_state is Null"); + return -EINVAL; + } + + hdd_debug("acl policy: %d num acl entries: %d", params->acl_policy, + params->n_acl_entries); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SET_MAC_ACL, + adapter->vdev_id, adapter->device_mode); + + if (QDF_SAP_MODE == adapter->device_mode) { + config = &adapter->session.ap.sap_config; + + /* default value */ + config->num_accept_mac = 0; + config->num_deny_mac = 0; + + /** + * access control policy + * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are + * listed in hostapd.deny file. + * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow stations which are + * listed in hostapd.accept file. + */ + if (NL80211_ACL_POLICY_DENY_UNLESS_LISTED == params->acl_policy) { + config->SapMacaddr_acl = eSAP_DENY_UNLESS_ACCEPTED; + } else if (NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED == + params->acl_policy) { + config->SapMacaddr_acl = eSAP_ACCEPT_UNLESS_DENIED; + } else { + hdd_warn("Acl Policy : %d is not supported", + params->acl_policy); + return -ENOTSUPP; + } + + if (eSAP_DENY_UNLESS_ACCEPTED == config->SapMacaddr_acl) { + config->num_accept_mac = params->n_acl_entries; + for (i = 0; i < params->n_acl_entries; i++) { + hdd_debug("** Add ACL MAC entry %i in WhiletList :" + QDF_MAC_ADDR_FMT, i, + QDF_MAC_ADDR_REF( + params->mac_addrs[i].addr)); + + qdf_mem_copy(&config->accept_mac[i], + params->mac_addrs[i].addr, + QDF_MAC_ADDR_SIZE); + } + } else if (eSAP_ACCEPT_UNLESS_DENIED == config->SapMacaddr_acl) { + config->num_deny_mac = params->n_acl_entries; + for (i = 0; i < params->n_acl_entries; i++) { + hdd_debug("** Add ACL MAC entry %i in BlackList :" + QDF_MAC_ADDR_FMT, i, + QDF_MAC_ADDR_REF( + params->mac_addrs[i].addr)); + + qdf_mem_copy(&config->deny_mac[i], + params->mac_addrs[i].addr, + QDF_MAC_ADDR_SIZE); + } + } + qdf_status = wlansap_set_mac_acl( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), config); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("SAP Set Mac Acl fail"); + return -EINVAL; + } + } else { + hdd_debug("Invalid device_mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + hdd_exit(); + return 0; +} + +/** + * wlan_hdd_cfg80211_set_mac_acl() - SSR wrapper for + * __wlan_hdd_cfg80211_set_mac_acl + * @wiphy: pointer to wiphy structure + * @dev: pointer to net_device + * @params: pointer to cfg80211_acl_data + * + * Return; 0 on success, error number otherwise + */ +static int +wlan_hdd_cfg80211_set_mac_acl(struct wiphy *wiphy, + struct net_device *dev, + const struct cfg80211_acl_data *params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_mac_acl(wiphy, dev, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef WLAN_NL80211_TESTMODE +#ifdef FEATURE_WLAN_LPHB +/** + * wlan_hdd_cfg80211_lphb_ind_handler() - handle low power heart beat indication + * @hdd_ctx: Pointer to hdd context + * @lphbInd: Pointer to low power heart beat indication parameter + * + * Return: none + */ +static void wlan_hdd_cfg80211_lphb_ind_handler(void *hdd_ctx, + struct pmo_lphb_rsp *lphb_ind) +{ + struct sk_buff *skb; + + hdd_debug("LPHB indication arrived"); + + if (0 != wlan_hdd_validate_context((struct hdd_context *) hdd_ctx)) + return; + + if (!lphb_ind) { + hdd_err("invalid argument lphbInd"); + return; + } + + skb = cfg80211_testmode_alloc_event_skb(((struct hdd_context *) hdd_ctx)-> + wiphy, sizeof(*lphb_ind), GFP_ATOMIC); + if (!skb) { + hdd_err("LPHB timeout, NL buffer alloc fail"); + return; + } + + if (nla_put_u32(skb, WLAN_HDD_TM_ATTR_CMD, WLAN_HDD_TM_CMD_WLAN_HB)) { + hdd_err("WLAN_HDD_TM_ATTR_CMD put fail"); + goto nla_put_failure; + } + if (nla_put_u32(skb, WLAN_HDD_TM_ATTR_TYPE, lphb_ind->protocol_type)) { + hdd_err("WLAN_HDD_TM_ATTR_TYPE put fail"); + goto nla_put_failure; + } + if (nla_put(skb, WLAN_HDD_TM_ATTR_DATA, sizeof(*lphb_ind), + lphb_ind)) { + hdd_err("WLAN_HDD_TM_ATTR_DATA put fail"); + goto nla_put_failure; + } + cfg80211_testmode_event(skb, GFP_ATOMIC); + return; + +nla_put_failure: + hdd_err("NLA Put fail"); + kfree_skb(skb); +} +#endif /* FEATURE_WLAN_LPHB */ + +/** + * __wlan_hdd_cfg80211_testmode() - test mode + * @wiphy: Pointer to wiphy + * @data: Data pointer + * @len: Data length + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_testmode(struct wiphy *wiphy, + void *data, int len) +{ + struct nlattr *tb[WLAN_HDD_TM_ATTR_MAX + 1]; + int err; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter(); + + err = wlan_hdd_validate_context(hdd_ctx); + if (err) + return err; + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_err("Driver Modules are closed"); + return -EINVAL; + } + + err = wlan_cfg80211_nla_parse(tb, WLAN_HDD_TM_ATTR_MAX, data, + len, wlan_hdd_tm_policy); + if (err) { + hdd_err("Testmode INV ATTR"); + return err; + } + + if (!tb[WLAN_HDD_TM_ATTR_CMD]) { + hdd_err("Testmode INV CMD"); + return -EINVAL; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_TESTMODE, + NO_SESSION, nla_get_u32(tb[WLAN_HDD_TM_ATTR_CMD])); + + switch (nla_get_u32(tb[WLAN_HDD_TM_ATTR_CMD])) { +#ifdef FEATURE_WLAN_LPHB + /* Low Power Heartbeat configuration request */ + case WLAN_HDD_TM_CMD_WLAN_HB: + { + int buf_len; + void *buf; + struct pmo_lphb_req *hb_params = NULL; + struct pmo_lphb_req *hb_params_temp = NULL; + QDF_STATUS status; + + if (!tb[WLAN_HDD_TM_ATTR_DATA]) { + hdd_err("Testmode INV DATA"); + return -EINVAL; + } + + buf = nla_data(tb[WLAN_HDD_TM_ATTR_DATA]); + buf_len = nla_len(tb[WLAN_HDD_TM_ATTR_DATA]); + if (buf_len < sizeof(*hb_params_temp)) { + hdd_err("Invalid buffer length for TM_ATTR_DATA"); + return -EINVAL; + } + + hb_params_temp = (struct pmo_lphb_req *) buf; + if ((hb_params_temp->cmd == pmo_lphb_set_tcp_pararm_indid) + && (hb_params_temp->params.lphb_tcp_params. + time_period_sec == 0)) + return -EINVAL; + + if (buf_len > sizeof(*hb_params)) { + hdd_err("buf_len=%d exceeded hb_params size limit", + buf_len); + return -ERANGE; + } + + hb_params = (struct pmo_lphb_req *)qdf_mem_malloc( + sizeof(*hb_params)); + if (!hb_params) + return -ENOMEM; + + qdf_mem_zero(hb_params, sizeof(*hb_params)); + qdf_mem_copy(hb_params, buf, buf_len); + status = ucfg_pmo_lphb_config_req( + hdd_ctx->psoc, + hb_params, (void *)hdd_ctx, + wlan_hdd_cfg80211_lphb_ind_handler); + if (status != QDF_STATUS_SUCCESS) + hdd_err("LPHB Config Fail, disable"); + + qdf_mem_free(hb_params); + return 0; + } +#endif /* FEATURE_WLAN_LPHB */ + +#if defined(QCA_WIFI_FTM) + case WLAN_HDD_TM_CMD_WLAN_FTM: + { + if (QDF_GLOBAL_FTM_MODE != hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode, mode %d", + hdd_get_conparam()); + return -EINVAL; + } + + err = wlan_cfg80211_ftm_testmode_cmd(hdd_ctx->pdev, + data, len); + break; + } +#endif + default: + hdd_err("command: %d not supported", + nla_get_u32(tb[WLAN_HDD_TM_ATTR_CMD])); + return -EOPNOTSUPP; + } + + hdd_exit(); + return err; +} + +/** + * wlan_hdd_cfg80211_testmode() - test mode + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @data: Data pointer + * @len: Data length + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_cfg80211_testmode(struct wiphy *wiphy, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + struct wireless_dev *wdev, +#endif + void *data, int len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_testmode(wiphy, data, len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +#endif /* CONFIG_NL80211_TESTMODE */ + +#ifdef QCA_HT_2040_COEX +/** + * __wlan_hdd_cfg80211_set_ap_channel_width() - set ap channel bandwidth + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @chandef: Pointer to channel definition parameter + * + * Return: 0 for success, non-zero for failure + */ +static int +__wlan_hdd_cfg80211_set_ap_channel_width(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + QDF_STATUS status; + int retval = 0; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + if (!(adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE)) + return -EOPNOTSUPP; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + hdd_debug("Channel width changed to %d ", + cfg80211_get_chandef_type(chandef)); + + /* Change SAP ht2040 mode */ + status = hdd_set_sap_ht2040_mode(adapter, + cfg80211_get_chandef_type(chandef)); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Cannot set SAP HT20/40 mode!"); + retval = -EINVAL; + } + + return retval; +} + +/** + * wlan_hdd_cfg80211_set_ap_channel_width() - set ap channel bandwidth + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @chandef: Pointer to channel definition parameter + * + * Return: 0 for success, non-zero for failure + */ +static int +wlan_hdd_cfg80211_set_ap_channel_width(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_ap_channel_width(wiphy, dev, chandef); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif + +#ifdef CHANNEL_SWITCH_SUPPORTED +/** + * __wlan_hdd_cfg80211_channel_switch()- function to switch + * channel in SAP/GO + * @wiphy: wiphy pointer + * @dev: dev pointer. + * @csa_params: Change channel params + * + * This function is called to switch channel in SAP/GO + * + * Return: 0 if success else return non zero + */ +static int __wlan_hdd_cfg80211_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_csa_settings *csa_params) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + int ret; + enum phy_ch_width ch_width; + + hdd_debug("Set Freq %d", + csa_params->chandef.chan->center_freq); + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + + if (0 != ret) + return ret; + + if ((QDF_P2P_GO_MODE != adapter->device_mode) && + (QDF_SAP_MODE != adapter->device_mode)) + return -ENOTSUPP; + wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, adapter->vdev_id, + CSA_REASON_USER_INITIATED); + + ch_width = hdd_map_nl_chan_width(csa_params->chandef.width); + + ret = + hdd_softap_set_channel_change(dev, + csa_params->chandef.chan->center_freq, + ch_width, false); + return ret; +} + +enum qca_wlan_802_11_mode +hdd_convert_cfgdot11mode_to_80211mode(enum csr_cfgdot11mode mode) +{ + switch (mode) { + case eCSR_CFG_DOT11_MODE_11A: + return QCA_WLAN_802_11_MODE_11A; + case eCSR_CFG_DOT11_MODE_11B: + return QCA_WLAN_802_11_MODE_11B; + case eCSR_CFG_DOT11_MODE_11G: + return QCA_WLAN_802_11_MODE_11G; + case eCSR_CFG_DOT11_MODE_11N: + return QCA_WLAN_802_11_MODE_11N; + case eCSR_CFG_DOT11_MODE_11AC: + return QCA_WLAN_802_11_MODE_11AC; + case eCSR_CFG_DOT11_MODE_11G_ONLY: + return QCA_WLAN_802_11_MODE_11G; + case eCSR_CFG_DOT11_MODE_11N_ONLY: + return QCA_WLAN_802_11_MODE_11N; + case eCSR_CFG_DOT11_MODE_11AC_ONLY: + return QCA_WLAN_802_11_MODE_11AC; + case eCSR_CFG_DOT11_MODE_11AX: + return QCA_WLAN_802_11_MODE_11AX; + case eCSR_CFG_DOT11_MODE_11AX_ONLY: + return QCA_WLAN_802_11_MODE_11AX; + case eCSR_CFG_DOT11_MODE_ABG: + case eCSR_CFG_DOT11_MODE_AUTO: + default: + return QCA_WLAN_802_11_MODE_INVALID; + } +} + +bool hdd_is_legacy_connection(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + int connection_mode; + + connection_mode = hdd_convert_cfgdot11mode_to_80211mode( + sta_ctx->conn_info.dot11mode); + if (connection_mode == QCA_WLAN_802_11_MODE_11A || + connection_mode == QCA_WLAN_802_11_MODE_11B || + connection_mode == QCA_WLAN_802_11_MODE_11G) + return true; + else + return false; +} + +/** + * wlan_hdd_cfg80211_channel_switch()- function to switch + * channel in SAP/GO + * @wiphy: wiphy pointer + * @dev: dev pointer. + * @csa_params: Change channel params + * + * This function is called to switch channel in SAP/GO + * + * Return: 0 if success else return non zero + */ +static int wlan_hdd_cfg80211_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_csa_settings *csa_params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_channel_switch(wiphy, dev, csa_params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif + +int wlan_hdd_change_hw_mode_for_given_chnl(struct hdd_adapter *adapter, + uint32_t chan_freq, + enum policy_mgr_conn_update_reason reason) +{ + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_enter(); + + status = policy_mgr_reset_connection_update(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("clearing event failed"); + + status = policy_mgr_current_connections_update( + hdd_ctx->psoc, adapter->vdev_id, + chan_freq, reason); + switch (status) { + case QDF_STATUS_E_FAILURE: + /* + * QDF_STATUS_E_FAILURE indicates that some error has occurred + * while changing the hw mode + */ + hdd_err("ERROR: connections update failed!!"); + return -EINVAL; + + case QDF_STATUS_SUCCESS: + /* + * QDF_STATUS_SUCCESS indicates that HW mode change has been + * triggered and wait for it to finish. + */ + status = policy_mgr_wait_for_connection_update( + hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("ERROR: qdf wait for event failed!!"); + return -EINVAL; + } + if (QDF_MONITOR_MODE == adapter->device_mode) + hdd_info("Monitor mode:channel freq:%d (SMM->DBS)", chan_freq); + break; + + default: + /* + * QDF_STATUS_E_NOSUPPORT indicates that no HW mode change is + * required, so caller can proceed further. + */ + break; + + } + hdd_exit(); + + return 0; +} + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +/** + * wlan_hdd_cfg80211_set_mon_ch() - Set monitor mode capture channel + * @wiphy: Handle to struct wiphy to get handle to module context. + * @chandef: Contains information about the capture channel to be set. + * + * This interface is called if and only if monitor mode interface alone is + * active. + * + * Return: 0 success or error code on failure. + */ +static int __wlan_hdd_cfg80211_set_mon_ch(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_adapter *adapter; + struct hdd_station_ctx *sta_ctx; + struct hdd_mon_set_ch_info *ch_info; + QDF_STATUS status; + mac_handle_t mac_handle; + struct qdf_mac_addr bssid; + struct csr_roam_profile roam_profile; + struct ch_params ch_params; + int ret; + enum channel_state chan_freq_state; + uint8_t max_fw_bw; + enum phy_ch_width ch_width; + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + mac_handle = hdd_ctx->mac_handle; + + adapter = hdd_get_adapter(hdd_ctx, QDF_MONITOR_MODE); + if (!adapter) + return -EIO; + + hdd_debug("%s: set monitor mode freq %d", + adapter->dev->name, chandef->chan->center_freq); + + /* Verify channel state before accepting this request */ + chan_freq_state = + wlan_reg_get_channel_state_for_freq(hdd_ctx->pdev, + chandef->chan->center_freq); + if (chan_freq_state == CHANNEL_STATE_DISABLE || + chan_freq_state == CHANNEL_STATE_INVALID) { + hdd_err("Invalid chan freq received for monitor mode aborting"); + return -EINVAL; + } + + /* Verify the BW before accepting this request */ + ch_width = hdd_map_nl_chan_width(chandef->width); + + if (ch_width > CH_WIDTH_10MHZ || + (!cds_is_sub_20_mhz_enabled() && ch_width > CH_WIDTH_160MHZ)) { + hdd_err("invalid BW received %d", ch_width); + return -EINVAL; + } + + max_fw_bw = sme_get_vht_ch_width(); + + if ((ch_width == CH_WIDTH_160MHZ && + max_fw_bw <= WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) || + (ch_width == CH_WIDTH_80P80MHZ && + max_fw_bw <= WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ)) { + hdd_err("FW does not support this BW %d max BW supported %d", + ch_width, max_fw_bw); + return -EINVAL; + } + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + ch_info = &sta_ctx->ch_info; + roam_profile.ChannelInfo.freq_list = &ch_info->freq; + roam_profile.ChannelInfo.numOfChannels = 1; + roam_profile.phyMode = ch_info->phy_mode; + roam_profile.ch_params.ch_width = ch_width; + hdd_select_cbmode(adapter, chandef->chan->center_freq, + &roam_profile.ch_params); + + qdf_mem_copy(bssid.bytes, adapter->mac_addr.bytes, + QDF_MAC_ADDR_SIZE); + + ch_params.ch_width = ch_width; + wlan_reg_set_channel_params_for_freq(hdd_ctx->pdev, + chandef->chan->center_freq, + 0, &ch_params); + + if (wlan_hdd_change_hw_mode_for_given_chnl(adapter, + chandef->chan->center_freq, + POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN)) { + hdd_err("Failed to change hw mode"); + return -EINVAL; + } + + if (adapter->monitor_mode_vdev_up_in_progress) { + hdd_err_rl("monitor mode vdev up in progress"); + return -EBUSY; + } + + status = qdf_event_reset(&adapter->qdf_monitor_mode_vdev_up_event); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err_rl("failed to reinit monitor mode vdev up event"); + return qdf_status_to_os_return(status); + } + adapter->monitor_mode_vdev_up_in_progress = true; + + status = sme_roam_channel_change_req(mac_handle, bssid, + &roam_profile.ch_params, + &roam_profile); + if (status) { + hdd_err_rl("Failed to set sme_RoamChannel for monitor mode status: %d", + status); + adapter->monitor_mode_vdev_up_in_progress = false; + ret = qdf_status_to_os_return(status); + return ret; + } + + /* block on a completion variable until vdev up success*/ + status = qdf_wait_for_event_completion( + &adapter->qdf_monitor_mode_vdev_up_event, + WLAN_MONITOR_MODE_VDEV_UP_EVT); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err_rl("monitor vdev up event time out vdev id: %d", + adapter->vdev_id); + if (adapter->qdf_monitor_mode_vdev_up_event.force_set) + /* + * SSR/PDR has caused shutdown, which has + * forcefully set the event. + */ + hdd_err_rl("monitor mode vdev up event forcefully set"); + else if (status == QDF_STATUS_E_TIMEOUT) + hdd_err_rl("monitor mode vdev up timed out"); + else + hdd_err_rl("Failed monitor mode vdev up(status-%d)", + status); + + adapter->monitor_mode_vdev_up_in_progress = false; + return qdf_status_to_os_return(status); + } + + hdd_exit(); + + return 0; +} + +/** + * wlan_hdd_cfg80211_set_mon_ch() - Set monitor mode capture channel + * @wiphy: Handle to struct wiphy to get handle to module context. + * @chandef: Contains information about the capture channel to be set. + * + * This interface is called if and only if monitor mode interface alone is + * active. + * + * Return: 0 success or error code on failure. + */ +static int wlan_hdd_cfg80211_set_mon_ch(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_mon_ch(wiphy, chandef); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} +#endif + +#define CNT_DIFF(cur, prev) \ + ((cur >= prev) ? (cur - prev) : (cur + (MAX_COUNT - (prev) + 1))) +#define MAX_COUNT 0xffffffff +static void hdd_update_chan_info(struct hdd_context *hdd_ctx, + struct scan_chan_info *chan, + struct scan_chan_info *info, uint32_t cmd_flag) +{ + if ((info->cmd_flag != WMI_CHAN_InFO_START_RESP) && + (info->cmd_flag != WMI_CHAN_InFO_END_RESP)) + hdd_err("cmd flag is invalid: %d", info->cmd_flag); + + mutex_lock(&hdd_ctx->chan_info_lock); + + if (info->cmd_flag == WMI_CHAN_InFO_START_RESP) + qdf_mem_zero(chan, sizeof(*chan)); + + chan->freq = info->freq; + chan->noise_floor = info->noise_floor; + chan->clock_freq = info->clock_freq; + chan->cmd_flag = info->cmd_flag; + chan->cycle_count = CNT_DIFF(info->cycle_count, chan->cycle_count); + + chan->rx_clear_count = + CNT_DIFF(info->rx_clear_count, chan->rx_clear_count); + + chan->tx_frame_count = + CNT_DIFF(info->tx_frame_count, chan->tx_frame_count); + + mutex_unlock(&hdd_ctx->chan_info_lock); + +} +#undef CNT_DIFF +#undef MAX_COUNT + +#ifndef UPDATE_ASSOC_IE +#define UPDATE_ASSOC_IE BIT(0) +#endif + +#ifndef UPDATE_FILS_ERP_INFO +#define UPDATE_FILS_ERP_INFO BIT(1) +#endif + +#ifndef UPDATE_FILS_AUTH_TYPE +#define UPDATE_FILS_AUTH_TYPE BIT(2) +#endif + +#if defined(WLAN_FEATURE_FILS_SK) &&\ + (defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) ||\ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) &&\ + (defined(CFG80211_UPDATE_CONNECT_PARAMS) ||\ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))) + +/** + * hdd_update_connect_params_fils_info() - Update fils parameters based on + * the update_connect_params received from userspace + * @adapter: Pointer to hdd_adapter + * @hdd_ctx: Pointer to hdd_context + * @req: Pointer to connect params + * @changed: bitmap indicating which parameters have changed + * + * Return 0 on SUCCESS or error code on FAILURE + */ +static int +hdd_update_connect_params_fils_info(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + struct cfg80211_connect_params *req, + uint32_t changed) +{ + uint8_t *buf; + QDF_STATUS status; + mac_handle_t mac_handle; + struct csr_roam_profile *roam_profile; + struct cds_fils_connection_info *fils_info; + enum eAniAuthType auth_type; + + roam_profile = hdd_roam_profile(adapter); + fils_info = roam_profile->fils_con_info; + mac_handle = hdd_ctx->mac_handle; + if (!fils_info) { + hdd_err("No valid FILS conn info"); + return -EINVAL; + } + + fils_info->is_fils_connection = true; + if (changed & UPDATE_FILS_ERP_INFO) { + if (!wlan_hdd_fils_data_in_limits(req)) + return -EINVAL; + fils_info->key_nai_length = req->fils_erp_username_len + + sizeof(char) + + req->fils_erp_realm_len; + if (fils_info->key_nai_length > + FILS_MAX_KEYNAME_NAI_LENGTH) { + hdd_err("Key NAI Length %d", + fils_info->key_nai_length); + return -EINVAL; + } + if (req->fils_erp_username_len && req->fils_erp_username) { + buf = fils_info->keyname_nai; + qdf_mem_copy(buf, req->fils_erp_username, + req->fils_erp_username_len); + buf += req->fils_erp_username_len; + *buf++ = '@'; + qdf_mem_copy(buf, req->fils_erp_realm, + req->fils_erp_realm_len); + } + + fils_info->sequence_number = req->fils_erp_next_seq_num + 1; + fils_info->r_rk_length = req->fils_erp_rrk_len; + + if (fils_info->r_rk_length > FILS_MAX_RRK_LENGTH) { + hdd_err("r_rk_length is invalid"); + return -EINVAL; + } + + if (req->fils_erp_rrk_len && req->fils_erp_rrk) + qdf_mem_copy(fils_info->r_rk, req->fils_erp_rrk, + fils_info->r_rk_length); + + fils_info->realm_len = req->fils_erp_realm_len; + if (req->fils_erp_realm_len && req->fils_erp_realm) + qdf_mem_copy(fils_info->realm, req->fils_erp_realm, + fils_info->realm_len); + } + + if (changed & UPDATE_FILS_AUTH_TYPE) { + auth_type = wlan_hdd_get_fils_auth_type(req->auth_type); + if (auth_type == eSIR_DONOT_USE_AUTH_TYPE) { + hdd_err("invalid auth type for fils %d", + req->auth_type); + return -EINVAL; + } + + roam_profile->fils_con_info->auth_type = auth_type; + } + + hdd_debug("fils conn update: changed %x is_fils %d keyname nai len %d", + changed, roam_profile->fils_con_info->is_fils_connection, + roam_profile->fils_con_info->key_nai_length); + /* + * Update the FILS config from adapter->roam_profile to + * csr_session + */ + status = sme_update_fils_config(mac_handle, adapter->vdev_id, + roam_profile); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Update FILS connect params to csr failed %d", status); + + return 0; +} +#else + +static inline int +hdd_update_connect_params_fils_info(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + struct cfg80211_connect_params *req, + uint32_t changed) +{ + return -EINVAL; +} + +#endif + +#if defined(CFG80211_UPDATE_CONNECT_PARAMS) ||\ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) + +/** + * __wlan_hdd_cfg80211_update_connect_params - update connect params + * @wiphy: Handle to struct wiphy to get handle to module context. + * @dev: Pointer to network device + * @req: Pointer to connect params + * @changed: Bitmap used to indicate the changed params + * + * Update the connect parameters while connected to a BSS. The updated + * parameters can be used by driver/firmware for subsequent BSS selection + * (roaming) decisions and to form the Authentication/(Re)Association + * Request frames. This call does not request an immediate disassociation + * or reassociation with the current BSS, i.e., this impacts only + * subsequent (re)associations. The bits in changed are defined in enum + * cfg80211_connect_params_changed + * + * Return: zero for success, non-zero for failure + */ +static int +__wlan_hdd_cfg80211_update_connect_params(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_connect_params *req, + uint32_t changed) +{ + struct csr_roam_profile *roam_profile; + int ret; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + QDF_STATUS status; + mac_handle_t mac_handle; + + hdd_enter_dev(dev); + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return -EINVAL; + + mac_handle = hdd_ctx->mac_handle; + roam_profile = hdd_roam_profile(adapter); + + if (changed & UPDATE_ASSOC_IE) { + /* + * Validate the elements of the IE and copy it to + * roam_profile in adapter + */ + wlan_hdd_cfg80211_set_ie(adapter, req->ie, req->ie_len); + + /* + * Update this assoc IE received from user space to + * csr_session. RSO command will pick up the assoc + * IEs to be sent to firmware from the csr_session. + */ + sme_update_session_assoc_ie(mac_handle, adapter->vdev_id, + roam_profile); + } + + if ((changed & UPDATE_FILS_ERP_INFO) || + (changed & UPDATE_FILS_AUTH_TYPE)) { + ret = hdd_update_connect_params_fils_info(adapter, hdd_ctx, + req, changed); + if (ret) + return -EINVAL; + + if (!hdd_ctx->is_fils_roaming_supported) { + hdd_debug("FILS roaming support %d", + hdd_ctx->is_fils_roaming_supported); + return 0; + } + } + + if (changed) { + status = sme_send_rso_connect_params(mac_handle, + adapter->vdev_id, + roam_profile); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Update connect params to fw failed %d", + status); + } + + return 0; +} + +/** + * wlan_hdd_cfg80211_update_connect_params - SSR wrapper for + * __wlan_hdd_cfg80211_update_connect_params + * @wiphy: Pointer to wiphy structure + * @dev: Pointer to net_device + * @req: Pointer to connect params + * @changed: flags used to indicate the changed params + * + * Return: zero for success, non-zero for failure + */ +static int +wlan_hdd_cfg80211_update_connect_params(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_connect_params *req, + uint32_t changed) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_update_connect_params(wiphy, dev, + req, changed); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif + +#if defined(WLAN_FEATURE_SAE) && \ + (defined(CFG80211_EXTERNAL_AUTH_SUPPORT) || \ + LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) +#if defined(CFG80211_EXTERNAL_AUTH_AP_SUPPORT) +/** + * wlan_hdd_extauth_cache_pmkid() - Extract and cache pmkid + * @adapter: hdd vdev/net_device context + * @mac_handle: Handle to the MAC + * @params: Pointer to external auth params. + * + * Extract the PMKID and BSS from external auth params and add to the + * PMKSA Cache in CSR. + */ +static void +wlan_hdd_extauth_cache_pmkid(struct hdd_adapter *adapter, + mac_handle_t mac_handle, + struct cfg80211_external_auth_params *params) +{ + tPmkidCacheInfo *pmk_cache; + QDF_STATUS result; + + if (params->pmkid) { + pmk_cache = qdf_mem_malloc(sizeof(*pmk_cache)); + if (!pmk_cache) + return; + + qdf_mem_copy(pmk_cache->BSSID.bytes, params->bssid, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(pmk_cache->PMKID, params->pmkid, + PMKID_LEN); + result = wlan_hdd_set_pmksa_cache(adapter, pmk_cache); + if (!QDF_IS_STATUS_SUCCESS(result)) + hdd_debug("external_auth: Failed to cache PMKID"); + + qdf_mem_free(pmk_cache); + } + +} + +/** + * wlan_hdd_extauth_copy_pmkid() - Copy the pmkid received from the + * external authentication command received from the userspace. + * @params: pointer to auth params + * @pmkid: Pointer to destination pmkid buffer to be filled + * + * The caller should ensure that destination pmkid buffer is not NULL. + * + * Return: None + */ +static void +wlan_hdd_extauth_copy_pmkid(struct cfg80211_external_auth_params *params, + uint8_t *pmkid) +{ + if (params->pmkid) + qdf_mem_copy(pmkid, params->pmkid, PMKID_LEN); +} + +#else +static void +wlan_hdd_extauth_cache_pmkid(struct hdd_adapter *adapter, + mac_handle_t mac_handle, + struct cfg80211_external_auth_params *params) +{} + +static void +wlan_hdd_extauth_copy_pmkid(struct cfg80211_external_auth_params *params, + uint8_t *pmkid) +{} +#endif +/** + * __wlan_hdd_cfg80211_external_auth() - Handle external auth + * + * @wiphy: Pointer to wireless phy + * @dev: net device + * @params: Pointer to external auth params. + * Return: 0 on success, negative errno on failure + * + * Userspace sends status of the external authentication(e.g., SAE) with a peer. + * The message carries BSSID of the peer and auth status (WLAN_STATUS_SUCCESS/ + * WLAN_STATUS_UNSPECIFIED_FAILURE) in params. + * Userspace may send PMKID in params, which can be used for + * further connections. + */ +static int +__wlan_hdd_cfg80211_external_auth(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_external_auth_params *params) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int ret; + mac_handle_t mac_handle; + struct qdf_mac_addr peer_mac_addr; + uint8_t pmkid[PMKID_LEN] = {0}; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + hdd_debug("external_auth status: %d peer mac: " QDF_MAC_ADDR_FMT, + params->status, QDF_MAC_ADDR_REF(params->bssid)); + mac_handle = hdd_ctx->mac_handle; + qdf_mem_copy(peer_mac_addr.bytes, params->bssid, QDF_MAC_ADDR_SIZE); + + wlan_hdd_extauth_cache_pmkid(adapter, mac_handle, params); + + wlan_hdd_extauth_copy_pmkid(params, pmkid); + sme_handle_sae_msg(mac_handle, adapter->vdev_id, params->status, + peer_mac_addr, pmkid); + + return ret; +} + +/** + * wlan_hdd_cfg80211_external_auth() - Handle external auth + * @wiphy: Pointer to wireless phy + * @dev: net device + * @params: Pointer to external auth params + * + * Return: 0 on success, negative errno on failure + */ +static int +wlan_hdd_cfg80211_external_auth(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_external_auth_params *params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_external_auth(wiphy, dev, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif + +#if defined(WLAN_FEATURE_NAN) && \ + (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) +static int +wlan_hdd_cfg80211_start_nan(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_nan_conf *conf) +{ + return -EOPNOTSUPP; +} + +static void +wlan_hdd_cfg80211_stop_nan(struct wiphy *wiphy, struct wireless_dev *wdev) +{ +} + +static int wlan_hdd_cfg80211_add_nan_func(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_func *nan_func) +{ + return -EOPNOTSUPP; +} + +static void wlan_hdd_cfg80211_del_nan_func(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +{ +} + +static int wlan_hdd_cfg80211_nan_change_conf(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_conf *conf, + u32 changes) +{ + return -EOPNOTSUPP; +} +#endif + +/** + * wlan_hdd_chan_info_cb() - channel info callback + * @chan_info: struct scan_chan_info + * + * Store channel info into HDD context + * + * Return: None. + */ +static void wlan_hdd_chan_info_cb(struct scan_chan_info *info) +{ + struct hdd_context *hdd_ctx; + struct scan_chan_info *chan; + uint8_t idx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (wlan_hdd_validate_context(hdd_ctx) != 0) { + hdd_err("hdd_ctx is invalid"); + return; + } + + if (!hdd_ctx->chan_info) { + hdd_err("chan_info is NULL"); + return; + } + + chan = hdd_ctx->chan_info; + for (idx = 0; idx < SIR_MAX_NUM_CHANNELS; idx++) { + if (chan[idx].freq == info->freq) { + hdd_update_chan_info(hdd_ctx, &chan[idx], info, + info->cmd_flag); + hdd_debug("cmd:%d freq:%u nf:%d cc:%u rcc:%u clk:%u cmd:%d tfc:%d index:%d", + chan[idx].cmd_flag, chan[idx].freq, + chan[idx].noise_floor, + chan[idx].cycle_count, + chan[idx].rx_clear_count, + chan[idx].clock_freq, chan[idx].cmd_flag, + chan[idx].tx_frame_count, idx); + if (chan[idx].freq == 0) + break; + + } + } +} + +/** + * wlan_hdd_init_chan_info() - init chan info in hdd context + * @hdd_ctx: HDD context pointer + * + * Return: none + */ +void wlan_hdd_init_chan_info(struct hdd_context *hdd_ctx) +{ + uint32_t num_2g, num_5g, index = 0; + mac_handle_t mac_handle; + + hdd_ctx->chan_info = NULL; + if (!ucfg_scan_is_snr_monitor_enabled(hdd_ctx->psoc)) { + hdd_debug("SNR monitoring is disabled"); + return; + } + + hdd_ctx->chan_info = + qdf_mem_malloc(sizeof(struct scan_chan_info) + * NUM_CHANNELS); + if (!hdd_ctx->chan_info) + return; + mutex_init(&hdd_ctx->chan_info_lock); + + num_2g = QDF_ARRAY_SIZE(hdd_channels_2_4_ghz); + for (; index < num_2g; index++) { + hdd_ctx->chan_info[index].freq = + hdd_channels_2_4_ghz[index].center_freq; + } + + num_5g = QDF_ARRAY_SIZE(hdd_channels_5_ghz); + for (; (index - num_2g) < num_5g; index++) { + if (wlan_reg_is_dsrc_chan(hdd_ctx->pdev, + hdd_channels_5_ghz[index - num_2g].hw_value)) + continue; + hdd_ctx->chan_info[index].freq = + hdd_channels_5_ghz[index - num_2g].center_freq; + } + + index = num_2g + num_5g; + index = wlan_hdd_populate_srd_chan_info(hdd_ctx, index); + + mac_handle = hdd_ctx->mac_handle; + sme_set_chan_info_callback(mac_handle, + &wlan_hdd_chan_info_cb); +} + +/** + * wlan_hdd_deinit_chan_info() - deinit chan info in hdd context + * @hdd_ctx: hdd context pointer + * + * Return: none + */ +void wlan_hdd_deinit_chan_info(struct hdd_context *hdd_ctx) +{ + struct scan_chan_info *chan; + + chan = hdd_ctx->chan_info; + hdd_ctx->chan_info = NULL; + if (chan) + qdf_mem_free(chan); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) || defined(WITH_BACKPORTS) +static enum rate_info_bw hdd_map_hdd_bw_to_os(enum hdd_rate_info_bw hdd_bw) +{ + switch (hdd_bw) { + case HDD_RATE_BW_5: + return RATE_INFO_BW_5; + case HDD_RATE_BW_10: + return RATE_INFO_BW_10; + case HDD_RATE_BW_20: + return RATE_INFO_BW_20; + case HDD_RATE_BW_40: + return RATE_INFO_BW_40; + case HDD_RATE_BW_80: + return RATE_INFO_BW_80; + case HDD_RATE_BW_160: + return RATE_INFO_BW_160; + } + + hdd_err("Unhandled HDD_RATE_BW: %d", hdd_bw); + + return RATE_INFO_BW_20; +} + +void hdd_set_rate_bw(struct rate_info *info, enum hdd_rate_info_bw hdd_bw) +{ + info->bw = hdd_map_hdd_bw_to_os(hdd_bw); +} +#else +static enum rate_info_flags hdd_map_hdd_bw_to_os(enum hdd_rate_info_bw hdd_bw) +{ + switch (hdd_bw) { + case HDD_RATE_BW_5: + case HDD_RATE_BW_10: + case HDD_RATE_BW_20: + return (enum rate_info_flags)0; + case HDD_RATE_BW_40: + return RATE_INFO_FLAGS_40_MHZ_WIDTH; + case HDD_RATE_BW_80: + return RATE_INFO_FLAGS_80_MHZ_WIDTH; + case HDD_RATE_BW_160: + return RATE_INFO_FLAGS_160_MHZ_WIDTH; + } + + hdd_err("Unhandled HDD_RATE_BW: %d", hdd_bw); + + return (enum rate_info_flags)0; +} + +void hdd_set_rate_bw(struct rate_info *info, enum hdd_rate_info_bw hdd_bw) +{ + const enum rate_info_flags all_bws = + RATE_INFO_FLAGS_40_MHZ_WIDTH | + RATE_INFO_FLAGS_80_MHZ_WIDTH | + RATE_INFO_FLAGS_80P80_MHZ_WIDTH | + RATE_INFO_FLAGS_160_MHZ_WIDTH; + + info->flags &= ~all_bws; + info->flags |= hdd_map_hdd_bw_to_os(hdd_bw); +} +#endif + +#ifdef CFG80211_EXTERNAL_DH_UPDATE_SUPPORT +void hdd_send_update_owe_info_event(struct hdd_adapter *adapter, + uint8_t sta_addr[], + uint8_t *owe_ie, + uint32_t owe_ie_len) +{ + struct cfg80211_update_owe_info owe_info; + struct net_device *dev = adapter->dev; + + hdd_enter_dev(dev); + + qdf_mem_zero(&owe_info, sizeof(owe_info)); + qdf_mem_copy(owe_info.peer, sta_addr, ETH_ALEN); + owe_info.ie = owe_ie; + owe_info.ie_len = owe_ie_len; + + cfg80211_update_owe_info_event(dev, &owe_info, GFP_KERNEL); + + hdd_exit(); +} +#endif + +/** + * struct cfg80211_ops - cfg80211_ops + * + * @add_virtual_intf: Add virtual interface + * @del_virtual_intf: Delete virtual interface + * @change_virtual_intf: Change virtual interface + * @change_station: Change station + * @add_beacon: Add beacon in sap mode + * @del_beacon: Delete beacon in sap mode + * @set_beacon: Set beacon in sap mode + * @start_ap: Start ap + * @change_beacon: Change beacon + * @stop_ap: Stop ap + * @change_bss: Change bss + * @add_key: Add key + * @get_key: Get key + * @del_key: Delete key + * @set_default_key: Set default key + * @set_channel: Set channel + * @scan: Scan + * @connect: Connect + * @disconnect: Disconnect + * @join_ibss = Join ibss + * @leave_ibss = Leave ibss + * @set_wiphy_params = Set wiphy params + * @set_tx_power = Set tx power + * @get_tx_power = get tx power + * @remain_on_channel = Remain on channel + * @cancel_remain_on_channel = Cancel remain on channel + * @mgmt_tx = Tx management frame + * @mgmt_tx_cancel_wait = Cancel management tx wait + * @set_default_mgmt_key = Set default management key + * @set_txq_params = Set tx queue parameters + * @get_station = Get station + * @set_power_mgmt = Set power management + * @del_station = Delete station + * @add_station = Add station + * @set_pmksa = Set pmksa + * @del_pmksa = Delete pmksa + * @flush_pmksa = Flush pmksa + * @update_ft_ies = Update FT IEs + * @tdls_mgmt = Tdls management + * @tdls_oper = Tdls operation + * @set_rekey_data = Set rekey data + * @sched_scan_start = Scheduled scan start + * @sched_scan_stop = Scheduled scan stop + * @resume = Resume wlan + * @suspend = Suspend wlan + * @set_mac_acl = Set mac acl + * @testmode_cmd = Test mode command + * @set_ap_chanwidth = Set AP channel bandwidth + * @dump_survey = Dump survey + * @key_mgmt_set_pmk = Set pmk key management + * @update_connect_params = Update connect params + */ +static struct cfg80211_ops wlan_hdd_cfg80211_ops = { + .add_virtual_intf = wlan_hdd_add_virtual_intf, + .del_virtual_intf = wlan_hdd_del_virtual_intf, + .change_virtual_intf = wlan_hdd_cfg80211_change_iface, + .change_station = wlan_hdd_change_station, + .start_ap = wlan_hdd_cfg80211_start_ap, + .change_beacon = wlan_hdd_cfg80211_change_beacon, + .stop_ap = wlan_hdd_cfg80211_stop_ap, + .change_bss = wlan_hdd_cfg80211_change_bss, + .add_key = wlan_hdd_cfg80211_add_key, + .get_key = wlan_hdd_cfg80211_get_key, + .del_key = wlan_hdd_cfg80211_del_key, + .set_default_key = wlan_hdd_cfg80211_set_default_key, + .scan = wlan_hdd_cfg80211_scan, + .connect = wlan_hdd_cfg80211_connect, + .disconnect = wlan_hdd_cfg80211_disconnect, + .join_ibss = wlan_hdd_cfg80211_join_ibss, + .leave_ibss = wlan_hdd_cfg80211_leave_ibss, + .set_wiphy_params = wlan_hdd_cfg80211_set_wiphy_params, + .set_tx_power = wlan_hdd_cfg80211_set_txpower, + .get_tx_power = wlan_hdd_cfg80211_get_txpower, + .remain_on_channel = wlan_hdd_cfg80211_remain_on_channel, + .cancel_remain_on_channel = wlan_hdd_cfg80211_cancel_remain_on_channel, + .mgmt_tx = wlan_hdd_mgmt_tx, + .mgmt_tx_cancel_wait = wlan_hdd_cfg80211_mgmt_tx_cancel_wait, + .set_default_mgmt_key = wlan_hdd_set_default_mgmt_key, + .set_txq_params = wlan_hdd_set_txq_params, + .dump_station = wlan_hdd_cfg80211_dump_station, + .get_station = wlan_hdd_cfg80211_get_station, + .set_power_mgmt = wlan_hdd_cfg80211_set_power_mgmt, + .del_station = wlan_hdd_cfg80211_del_station, + .add_station = wlan_hdd_cfg80211_add_station, + .set_pmksa = wlan_hdd_cfg80211_set_pmksa, + .del_pmksa = wlan_hdd_cfg80211_del_pmksa, + .flush_pmksa = wlan_hdd_cfg80211_flush_pmksa, +#if defined(KERNEL_SUPPORT_11R_CFG80211) + .update_ft_ies = wlan_hdd_cfg80211_update_ft_ies, +#endif +#ifdef CFG80211_EXTERNAL_DH_UPDATE_SUPPORT + .update_owe_info = wlan_hdd_cfg80211_update_owe_info, +#endif +#ifdef FEATURE_WLAN_TDLS + .tdls_mgmt = wlan_hdd_cfg80211_tdls_mgmt, + .tdls_oper = wlan_hdd_cfg80211_tdls_oper, +#endif +#ifdef WLAN_FEATURE_GTK_OFFLOAD + .set_rekey_data = wlan_hdd_cfg80211_set_rekey_data, +#endif /* WLAN_FEATURE_GTK_OFFLOAD */ +#ifdef FEATURE_WLAN_SCAN_PNO + .sched_scan_start = wlan_hdd_cfg80211_sched_scan_start, + .sched_scan_stop = wlan_hdd_cfg80211_sched_scan_stop, +#endif /*FEATURE_WLAN_SCAN_PNO */ + .resume = wlan_hdd_cfg80211_resume_wlan, + .suspend = wlan_hdd_cfg80211_suspend_wlan, + .set_mac_acl = wlan_hdd_cfg80211_set_mac_acl, +#ifdef WLAN_NL80211_TESTMODE + .testmode_cmd = wlan_hdd_cfg80211_testmode, +#endif +#ifdef QCA_HT_2040_COEX + .set_ap_chanwidth = wlan_hdd_cfg80211_set_ap_channel_width, +#endif + .dump_survey = wlan_hdd_cfg80211_dump_survey, +#ifdef CHANNEL_SWITCH_SUPPORTED + .channel_switch = wlan_hdd_cfg80211_channel_switch, +#endif +#ifdef FEATURE_MONITOR_MODE_SUPPORT + .set_monitor_channel = wlan_hdd_cfg80211_set_mon_ch, +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)) || \ + defined(CFG80211_ABORT_SCAN) + .abort_scan = wlan_hdd_cfg80211_abort_scan, +#endif +#if defined(CFG80211_UPDATE_CONNECT_PARAMS) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) + .update_connect_params = wlan_hdd_cfg80211_update_connect_params, +#endif +#if defined(WLAN_FEATURE_SAE) && \ + (defined(CFG80211_EXTERNAL_AUTH_SUPPORT) || \ + LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) + .external_auth = wlan_hdd_cfg80211_external_auth, +#endif +#if defined(WLAN_FEATURE_NAN) && \ + (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) + .start_nan = wlan_hdd_cfg80211_start_nan, + .stop_nan = wlan_hdd_cfg80211_stop_nan, + .add_nan_func = wlan_hdd_cfg80211_add_nan_func, + .del_nan_func = wlan_hdd_cfg80211_del_nan_func, + .nan_change_conf = wlan_hdd_cfg80211_nan_change_conf, +#endif + +}; diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg80211.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg80211.h new file mode 100644 index 0000000000000000000000000000000000000000..0841113ce35bcb2db840eb219344c6e1a7e8c93f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg80211.h @@ -0,0 +1,734 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_cfg80211.h + * + * WLAN host device driver cfg80211 functions declaration + */ + +#if !defined(HDD_CFG80211_H__) +#define HDD_CFG80211_H__ + +#include +#include +#include +#include + +struct hdd_context; + +/* value for initial part of frames and number of bytes to be compared */ +#define GAS_INITIAL_REQ "\x04\x0a" +#define GAS_INITIAL_REQ_SIZE 2 + +#define GAS_INITIAL_RSP "\x04\x0b" +#define GAS_INITIAL_RSP_SIZE 2 + +#define GAS_COMEBACK_REQ "\x04\x0c" +#define GAS_COMEBACK_REQ_SIZE 2 + +#define GAS_COMEBACK_RSP "\x04\x0d" +#define GAS_COMEBACK_RSP_SIZE 2 + +#define P2P_PUBLIC_ACTION_FRAME "\x04\x09\x50\x6f\x9a\x09" +#define P2P_PUBLIC_ACTION_FRAME_SIZE 6 + +#define P2P_ACTION_FRAME "\x7f\x50\x6f\x9a\x09" +#define P2P_ACTION_FRAME_SIZE 5 + +#define SA_QUERY_FRAME_REQ "\x08\x00" +#define SA_QUERY_FRAME_REQ_SIZE 2 + +#define SA_QUERY_FRAME_RSP "\x08\x01" +#define SA_QUERY_FRAME_RSP_SIZE 2 + +#define WNM_BSS_ACTION_FRAME "\x0a\x07" +#define WNM_BSS_ACTION_FRAME_SIZE 2 + +#define WNM_NOTIFICATION_FRAME "\x0a\x1a" +#define WNM_NOTIFICATION_FRAME_SIZE 2 + +#define WPA_OUI_TYPE "\x00\x50\xf2\x01" +#define BLACKLIST_OUI_TYPE "\x00\x50\x00\x00" +#define WHITELIST_OUI_TYPE "\x00\x50\x00\x01" +#define WPA_OUI_TYPE_SIZE 4 +#define WMM_OUI_TYPE "\x00\x50\xf2\x02\x01" +#define WMM_OUI_TYPE_SIZE 5 + +#define VENDOR1_AP_OUI_TYPE "\x00\xE0\x4C" +#define VENDOR1_AP_OUI_TYPE_SIZE 3 + +#define BASIC_RATE_MASK 0x80 +#define RATE_MASK 0x7f + +#ifndef NL80211_AUTHTYPE_FILS_SK +#define NL80211_AUTHTYPE_FILS_SK 5 +#endif +#ifndef NL80211_AUTHTYPE_FILS_SK_PFS +#define NL80211_AUTHTYPE_FILS_SK_PFS 6 +#endif +#ifndef NL80211_AUTHTYPE_FILS_PK +#define NL80211_AUTHTYPE_FILS_PK 7 +#endif +#ifndef WLAN_AKM_SUITE_FILS_SHA256 +#define WLAN_AKM_SUITE_FILS_SHA256 0x000FAC0E +#endif +#ifndef WLAN_AKM_SUITE_FILS_SHA384 +#define WLAN_AKM_SUITE_FILS_SHA384 0x000FAC0F +#endif +#ifndef WLAN_AKM_SUITE_FT_FILS_SHA256 +#define WLAN_AKM_SUITE_FT_FILS_SHA256 0x000FAC10 +#endif +#ifndef WLAN_AKM_SUITE_FT_FILS_SHA384 +#define WLAN_AKM_SUITE_FT_FILS_SHA384 0x000FAC11 +#endif +#ifndef WLAN_AKM_SUITE_DPP_RSN +#define WLAN_AKM_SUITE_DPP_RSN 0x506f9a02 +#endif + +#define WLAN_AKM_SUITE_OWE 0x000FAC12 +#define WLAN_AKM_SUITE_EAP_SHA256 0x000FAC0B +#define WLAN_AKM_SUITE_EAP_SHA384 0x000FAC0C + + +#ifndef WLAN_AKM_SUITE_SAE +#define WLAN_AKM_SUITE_SAE 0x000FAC08 +#endif + +#ifndef WLAN_AKM_SUITE_FT_SAE +#define WLAN_AKM_SUITE_FT_SAE 0x000FAC09 +#endif + +#ifndef WLAN_AKM_SUITE_FT_EAP_SHA_384 +#define WLAN_AKM_SUITE_FT_EAP_SHA_384 0x000FAC0D +#endif + +#ifdef FEATURE_WLAN_TDLS +#define WLAN_IS_TDLS_SETUP_ACTION(action) \ + ((TDLS_SETUP_REQUEST <= action) && \ + (TDLS_SETUP_CONFIRM >= action)) +#if !defined(TDLS_MGMT_VERSION2) +#define TDLS_MGMT_VERSION2 0 +#endif +#endif + +/** + * hdd_convert_cfgdot11mode_to_80211mode() - Function to convert cfg dot11 mode + * to 80211 mode + * @mode: cfg dot11 mode + * + * Return: 80211 mode + */ +enum qca_wlan_802_11_mode +hdd_convert_cfgdot11mode_to_80211mode(enum csr_cfgdot11mode mode); + +#define HDD_SET_BIT(__param, __val) ((__param) |= (1 << (__val))) + +#define MAX_SCAN_SSID 10 + +#define IS_CHANNEL_VALID(channel) ((channel >= 0 && channel < 15) \ + || (channel >= 36 && channel <= 184)) + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) \ + || defined(BACKPORTED_CHANNEL_SWITCH_PRESENT) +#define CHANNEL_SWITCH_SUPPORTED +#endif + +#if defined(CFG80211_DEL_STA_V2) || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) || defined(WITH_BACKPORTS) +#define USE_CFG80211_DEL_STA_V2 +#endif + +#define TWT_SETUP_WAKE_INTVL_MANTISSA_MAX 0xFFFF +#define TWT_SETUP_WAKE_DURATION_MAX 0xFFFF +#define TWT_SETUP_WAKE_INTVL_EXP_MAX 31 + +/** + * enum eDFS_CAC_STATUS: CAC status + * + * @DFS_CAC_NEVER_DONE: CAC never done + * @DFS_CAC_IN_PROGRESS: CAC is in progress + * @DFS_CAC_IN_PROGRESS: CAC already done + */ +typedef enum { + DFS_CAC_NEVER_DONE, + DFS_CAC_IN_PROGRESS, + DFS_CAC_ALREADY_DONE, +} eDFS_CAC_STATUS; + +#define MAX_REQUEST_ID 0xFFFFFFFF + +/* Feature defines */ +#define WIFI_FEATURE_INFRA 0x0001 /* Basic infrastructure mode */ +#define WIFI_FEATURE_INFRA_5G 0x0002 /* Support for 5 GHz Band */ +#define WIFI_FEATURE_HOTSPOT 0x0004 /* Support for GAS/ANQP */ +#define WIFI_FEATURE_P2P 0x0008 /* Wifi-Direct */ +#define WIFI_FEATURE_SOFT_AP 0x0010 /* Soft AP */ +#define WIFI_FEATURE_EXTSCAN 0x0020 /* Extended Scan APIs */ +#define WIFI_FEATURE_NAN 0x0040 /* Neighbor Awareness + * Networking + */ +#define WIFI_FEATURE_D2D_RTT 0x0080 /* Device-to-device RTT */ +#define WIFI_FEATURE_D2AP_RTT 0x0100 /* Device-to-AP RTT */ +#define WIFI_FEATURE_BATCH_SCAN 0x0200 /* Batched Scan (legacy) */ +#define WIFI_FEATURE_PNO 0x0400 /* Preferred network offload */ +#define WIFI_FEATURE_ADDITIONAL_STA 0x0800 /* Support for two STAs */ +#define WIFI_FEATURE_TDLS 0x1000 /* Tunnel directed link + * setup + */ +#define WIFI_FEATURE_TDLS_OFFCHANNEL 0x2000 /* Support for TDLS off + * channel + */ +#define WIFI_FEATURE_EPR 0x4000 /* Enhanced power reporting */ +#define WIFI_FEATURE_AP_STA 0x8000 /* Support for AP STA + * Concurrency + */ +#define WIFI_FEATURE_LINK_LAYER_STATS 0x10000 /* Link layer stats */ +#define WIFI_FEATURE_LOGGER 0x20000 /* WiFi Logger */ +#define WIFI_FEATURE_HAL_EPNO 0x40000 /* WiFi PNO enhanced */ +#define WIFI_FEATURE_RSSI_MONITOR 0x80000 /* RSSI Monitor */ +#define WIFI_FEATURE_MKEEP_ALIVE 0x100000 /* WiFi mkeep_alive */ +#define WIFI_FEATURE_CONFIG_NDO 0x200000 /* ND offload configure */ +#define WIFI_FEATURE_TX_TRANSMIT_POWER 0x400000 /* Tx transmit power levels */ +#define WIFI_FEATURE_CONTROL_ROAMING 0x800000 /* Enable/Disable roaming */ +#define WIFI_FEATURE_IE_WHITELIST 0x1000000 /* Support Probe IE white listing */ +#define WIFI_FEATURE_SCAN_RAND 0x2000000 /* Support MAC & Probe Sequence Number randomization */ +#define WIFI_FEATURE_SET_LATENCY_MODE 0x40000000 /* Set latency mode */ + + +/* Support Tx Power Limit setting */ +#define WIFI_FEATURE_SET_TX_POWER_LIMIT 0x4000000 + +/* Add more features here */ +#define WIFI_TDLS_SUPPORT BIT(0) +#define WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT BIT(1) +#define WIIF_TDLS_OFFCHANNEL_SUPPORT BIT(2) + +#define CFG_NON_AGG_RETRY_MAX (31) +#define CFG_AGG_RETRY_MAX (31) +#define CFG_CTRL_RETRY_MAX (31) +#define CFG_PROPAGATION_DELAY_MAX (63) +#define CFG_PROPAGATION_DELAY_BASE (64) +#define CFG_AGG_RETRY_MIN (5) + +#define PCL_CHANNEL_SUPPORT_GO BIT(0) +#define PCL_CHANNEL_SUPPORT_CLI BIT(1) +#define PCL_CHANNEL_EXCLUDE_IN_GO_NEG BIT(3) + +struct cfg80211_bss * +wlan_hdd_cfg80211_update_bss_db(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info); + +#define CONNECTIVITY_CHECK_SET_ARP \ + QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_ARP +#define CONNECTIVITY_CHECK_SET_DNS \ + QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_DNS +#define CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE \ + QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE +#define CONNECTIVITY_CHECK_SET_ICMPV4 \ + QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_ICMPV4 +#define CONNECTIVITY_CHECK_SET_ICMPV6 \ + QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_ICMPV6 +#define CONNECTIVITY_CHECK_SET_TCP_SYN \ + QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_TCP_SYN +#define CONNECTIVITY_CHECK_SET_TCP_SYN_ACK \ + QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_TCP_SYN_ACK +#define CONNECTIVITY_CHECK_SET_TCP_ACK \ + QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_TCP_ACK + + +int wlan_hdd_cfg80211_pmksa_candidate_notify(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + int index, bool preauth); + +#ifdef FEATURE_WLAN_LFR_METRICS +QDF_STATUS +wlan_hdd_cfg80211_roam_metrics_preauth(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info); + +QDF_STATUS +wlan_hdd_cfg80211_roam_metrics_preauth_status(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + bool preauth_status); + +QDF_STATUS +wlan_hdd_cfg80211_roam_metrics_handover(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info); +#endif + +/** + * hdd_cfg80211_wiphy_alloc() - Allocate wiphy + * + * Allocate wiphy and hdd context. + * + * Return: hdd context on success and NULL on failure. + */ +struct hdd_context *hdd_cfg80211_wiphy_alloc(void); + +int wlan_hdd_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request); + +int wlan_hdd_cfg80211_init(struct device *dev, + struct wiphy *wiphy, struct hdd_config *config); + +void wlan_hdd_cfg80211_deinit(struct wiphy *wiphy); + +void wlan_hdd_update_wiphy(struct hdd_context *hdd_ctx); + +void wlan_hdd_update_11n_mode(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_update_wiphy_supported_band() - Updates wiphy band info when + * receive FW ready event + * @hdd_ctx: HDD context + * + * Updates wiphy band info + * + * Return: QDF Status + */ +QDF_STATUS wlan_hdd_update_wiphy_supported_band(struct hdd_context *hdd_ctx); + +int wlan_hdd_cfg80211_register(struct wiphy *wiphy); + +/** + * wlan_hdd_cfg80211_register_frames() - register frame types and callbacks + * with the PE. + * @adapter: pointer to adapter + * + * This function is used by HDD to register frame types which are interested + * by supplicant, callbacks for rx frame indication and ack. + * + * Return: 0 on success and non zero value on failure + */ +int wlan_hdd_cfg80211_register_frames(struct hdd_adapter *adapter); + +void wlan_hdd_cfg80211_deregister_frames(struct hdd_adapter *adapter); + +void hdd_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request); + +QDF_STATUS wlan_hdd_validate_operation_channel(struct hdd_adapter *adapter, + uint32_t ch_freq); + +void hdd_select_cbmode(struct hdd_adapter *adapter, uint32_t oper_freq, + struct ch_params *ch_params); + +/** + * wlan_hdd_is_ap_supports_immediate_power_save() - to find certain vendor APs + * which do not support immediate power-save. + * @ies: beacon IE of the AP which STA is connecting/connected to + * @length: beacon IE length only + * + * This API takes the IE of connected/connecting AP and determines that + * whether it has specific vendor OUI. If it finds then it will return false to + * notify that AP doesn't support immediate power-save. + * + * Return: true or false based on findings + */ +bool wlan_hdd_is_ap_supports_immediate_power_save(uint8_t *ies, int length); +int wlan_hdd_del_station(struct hdd_adapter *adapter); + +#if defined(USE_CFG80211_DEL_STA_V2) +int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *dev, + struct station_del_parameters *param); +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *mac); +#else +int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *dev, + uint8_t *mac); +#endif +#endif /* USE_CFG80211_DEL_STA_V2 */ + +int wlan_hdd_send_avoid_freq_event(struct hdd_context *hdd_ctx, + struct ch_avoid_ind_type *avoid_freq_list); + +/** + * wlan_hdd_send_hang_reason_event() - Send hang reason to the userspace + * @hdd_ctx: Pointer to hdd context + * @reason: cds recovery reason + * @data: Hang Data + * @data_len: Hang Data len + * + * Return: 0 on success or failure reason + */ +int wlan_hdd_send_hang_reason_event(struct hdd_context *hdd_ctx, + uint32_t reason, uint8_t *data, + size_t data_len); + +int wlan_hdd_send_avoid_freq_for_dnbs(struct hdd_context *hdd_ctx, + uint8_t op_chan); + +/** + * wlan_hdd_rso_cmd_status_cb() - HDD callback to read RSO command status + * @hdd_handle: opaque handle for the hdd context + * @rso_status: rso command status + * + * This callback function is invoked by firmware to update + * the RSO(ROAM SCAN OFFLOAD) command status. + * + * Return: None + */ +void wlan_hdd_rso_cmd_status_cb(hdd_handle_t hdd_handle, + struct rso_cmd_status *rso_status); + +/* + * wlan_hdd_cfg80211_unlink_bss :to inform nl80211 + * interface that BSS might have been lost. + * @adapter: adapter + * @bssid: bssid which might have been lost + * @ssid: SSID + * @ssid_len: length of the SSID + * + * Return: void + */ +void wlan_hdd_cfg80211_unlink_bss(struct hdd_adapter *adapter, + tSirMacAddr bssid, uint8_t *ssid, + uint8_t ssid_len); + +void wlan_hdd_cfg80211_acs_ch_select_evt(struct hdd_adapter *adapter); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * hdd_send_roam_scan_ch_list_event() - roam scan ch list event to user space + * @hdd_ctx: HDD context + * @vdev_id: vdev id + * @buf_len: length of frequency list + * @buf: pointer to buffer of frequency list + * + * Return: None + */ +void hdd_send_roam_scan_ch_list_event(struct hdd_context *hdd_ctx, + uint8_t vdev_id, uint16_t buf_len, + uint8_t *buf); + +int wlan_hdd_send_roam_auth_event(struct hdd_adapter *adapter, uint8_t *bssid, + uint8_t *req_rsn_ie, uint32_t req_rsn_length, uint8_t + *rsp_rsn_ie, uint32_t rsp_rsn_length, struct csr_roam_info + *roam_info_ptr); +#else +static inline int wlan_hdd_send_roam_auth_event(struct hdd_adapter *adapter, + uint8_t *bssid, uint8_t *req_rsn_ie, uint32_t req_rsn_length, + uint8_t *rsp_rsn_ie, uint32_t rsp_rsn_length, + struct csr_roam_info *roam_info_ptr) +{ + return 0; +} + +static inline +void hdd_send_roam_scan_ch_list_event(struct hdd_context *hdd_ctx, + uint8_t vdev_id, uint16_t buf_len, + uint8_t *buf) +{ +} +#endif + +int wlan_hdd_cfg80211_update_apies(struct hdd_adapter *adapter); + +/** + * wlan_hdd_request_pre_cac() - Start pre CAC in the driver + * @hdd_ctx: the HDD context to operate against + * @chan_freq: channel freq option provided by userspace + * + * Sets the driver to the required hardware mode and start an adapter for + * pre CAC which will mimic an AP. + * + * Return: Zero on success, non-zero value on error + */ +int wlan_hdd_request_pre_cac(struct hdd_context *hdd_ctx, uint32_t chan_freq); +int wlan_hdd_sap_cfg_dfs_override(struct hdd_adapter *adapter); + +int wlan_hdd_enable_dfs_chan_scan(struct hdd_context *hdd_ctx, + bool enable_dfs_channels); + +/** + * wlan_hdd_cfg80211_update_band() - Update band of operation + * @hdd_ctx: The global HDD context + * @wiphy: The wiphy being configured + * @new_band: The new bad of operation + * + * This function is called from the supplicant through a + * private ioctl to change the band value + * + * Return: 0 on success, else a negative errno if the operation could + * not be completed + */ +int wlan_hdd_cfg80211_update_band(struct hdd_context *hdd_ctx, + struct wiphy *wiphy, + enum band_info new_band); + +/** + * wlan_hdd_cfg80211_indicate_disconnect() - Indicate disconnnect to userspace + * @adapter: Pointer to adapter + * @locally_generated: True if the disconnection is internally generated. + * False if the disconnection is received from peer. + * @reason: Disconnect reason as per @enum eSirMacReasonCodes + * @disconnect_ies: IEs received in Deauth/Disassoc from peer + * @disconnect_ies_len: Length of @disconnect_ies + * + * This function is indicate disconnect to the kernel which thus indicates + * to the userspace. + * + * Return: None + */ +void +wlan_hdd_cfg80211_indicate_disconnect(struct hdd_adapter *adapter, + bool locally_generated, + enum eSirMacReasonCodes reason, + uint8_t *disconnect_ies, + uint16_t disconnect_ies_len); + +/** + * wlan_hdd_inform_bss_frame() - inform bss details to NL80211 + * @adapter: Pointer to adapter + * @bss_desc: Pointer to bss descriptor + * + * This function is used to inform the BSS details to nl80211 interface. + * + * Return: struct cfg80211_bss pointer + */ +struct cfg80211_bss * +wlan_hdd_inform_bss_frame(struct hdd_adapter *adapter, + struct bss_description *bss_desc); + +/** + * wlan_hdd_change_hw_mode_for_given_chnl() - change HW mode for given channel + * @adapter: pointer to adapter + * @chan_freq: given channel frequency + * @reason: reason for HW mode change is needed + * + * This API decides and sets hardware mode to DBS based on given channel. + * For example, some of the platforms require DBS hardware mode to operate + * in 2.4G channel + * + * Return: 0 for success and non-zero for failure + */ +int wlan_hdd_change_hw_mode_for_given_chnl(struct hdd_adapter *adapter, + uint32_t chan_freq, + enum policy_mgr_conn_update_reason reason); + +/** + * hdd_rate_info_bw: an HDD internal rate bandwidth representation + * @HDD_RATE_BW_5: 5MHz + * @HDD_RATE_BW_10: 10MHz + * @HDD_RATE_BW_20: 20MHz + * @HDD_RATE_BW_40: 40MHz + * @HDD_RATE_BW_80: 80MHz + * @HDD_RATE_BW_160: 160 MHz + */ +enum hdd_rate_info_bw { + HDD_RATE_BW_5, + HDD_RATE_BW_10, + HDD_RATE_BW_20, + HDD_RATE_BW_40, + HDD_RATE_BW_80, + HDD_RATE_BW_160, +}; + +/** + * hdd_set_rate_bw(): Set the bandwidth for the given rate_info + * @info: The rate info for which the bandwidth should be set + * @hdd_bw: HDD representation of a rate info bandwidth + */ +void hdd_set_rate_bw(struct rate_info *info, enum hdd_rate_info_bw hdd_bw); + +/* + * hdd_get_sap_operating_band: Get current operating channel + * for sap. + * @hdd_ctx: hdd context + * + * Return : Corresponding band for SAP operating channel + */ +uint8_t hdd_get_sap_operating_band(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_try_disconnect() - try disconnnect from previous connection + * @adapter: Pointer to adapter + * @reason: Mac Disconnect reason code as per @enum eSirMacReasonCodes + * + * This function is used to disconnect from previous connection + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_try_disconnect(struct hdd_adapter *adapter, + enum eSirMacReasonCodes reason); + +/** + * wlan_hdd_disconnect() - hdd disconnect api + * @adapter: Pointer to adapter + * @reason: CSR disconnect reason code as per @enum eCsrRoamDisconnectReason + * @mac_reason: Mac Disconnect reason code as per @enum eSirMacReasonCodes + * + * This function is used to issue a disconnect request to SME + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_disconnect(struct hdd_adapter *adapter, u16 reason, + tSirMacReasonCodes mac_reason); + +/** + * wlan_hdd_get_adjacent_chan(): Gets next/previous channel + * to the channel passed. + * @chan: Channel + * @upper: If "true" then next channel is returned or else + * previous channel is returned. + * + * This function returns the next/previous adjacent-channel to + * the channel passed. If "upper = true" then next channel is + * returned else previous is returned. + */ +int wlan_hdd_get_adjacent_chan(uint8_t chan, bool upper); + +/** + * wlan_hdd_merge_avoid_freqs(): Merge two tHddAvoidFreqList + * @destFreqList: Destination list in which merged frequency + * list will be available. + * @srcFreqList: Source frequency list. + * + * Merges two avoid_frequency lists + */ +int wlan_hdd_merge_avoid_freqs(struct ch_avoid_ind_type *destFreqList, + struct ch_avoid_ind_type *srcFreqList); + + +/** + * hdd_bt_activity_cb() - callback function to receive bt activity + * @hdd_handle: Opaque handle to the HDD context + * @bt_activity: specifies the kind of bt activity + * + * Return: none + */ +void hdd_bt_activity_cb(hdd_handle_t hdd_handle, uint32_t bt_activity); + +#ifdef WLAN_FEATURE_GTK_OFFLOAD +/** + * wlan_hdd_save_gtk_offload_params() - Save gtk offload parameters in STA + * context for offload operations. + * @adapter: Adapter context + * @kck_ptr: KCK buffer pointer + * @kck_len: KCK length + * @kek_ptr: KEK buffer pointer + * @kek_len: KEK length + * @replay_ctr: Pointer to 64 bit long replay counter + * @big_endian: true if replay_ctr is in big endian format + * + * Return: None + */ +void wlan_hdd_save_gtk_offload_params(struct hdd_adapter *adapter, + uint8_t *kck_ptr, uint8_t kck_len, + uint8_t *kek_ptr, uint32_t kek_len, + uint8_t *replay_ctr, bool big_endian); +#else +void wlan_hdd_save_gtk_offload_params(struct hdd_adapter *adapter, + uint8_t *kck_ptr, uint8_t kck_len, + uint8_t *kek_ptr, uint32_t kek_len, + uint8_t *replay_ctr, bool big_endian) +{} +#endif + + +/** + * wlan_hdd_flush_pmksa_cache() - flush pmksa cache for adapter + * @adapter: Adapter context + * + * Return: qdf status + */ +QDF_STATUS wlan_hdd_flush_pmksa_cache(struct hdd_adapter *adapter); + +/* + * wlan_hdd_send_mode_change_event() - API to send hw mode change event to + * userspace + * + * Return : 0 on success and errno on failure + */ +int wlan_hdd_send_mode_change_event(void); + +/** + * wlan_hdd_restore_channels() - Restore the channels which were cached + * and disabled in wlan_hdd_disable_channels api. + * @hdd_ctx: Pointer to the HDD context + * + * Return: 0 on success, Error code on failure + */ +int wlan_hdd_restore_channels(struct hdd_context *hdd_ctx); + +/** + * hdd_store_sar_config() - Store SAR config in HDD context + * @hdd_ctx: The HDD context + * @sar_limit_cmd: The sar_limit_cmd_params struct to save + * + * After SSR, the SAR configuration is lost. As SSR is hidden from + * userland, this command will not come from userspace after a SSR. To + * restore this configuration, save this in hdd context and restore + * after re-init. + * + * Return: None + */ +void hdd_store_sar_config(struct hdd_context *hdd_ctx, + struct sar_limit_cmd_params *sar_limit_cmd); + +/** + * hdd_free_sar_config() - Free the resources allocated while storing SAR config + * @hdd_ctx: HDD context + * + * The driver stores the SAR config values in HDD context so that it can be + * restored in the case SSR is invoked. Free those resources. + * + * Return: None + */ +void wlan_hdd_free_sar_config(struct hdd_context *hdd_ctx); + +/* + * wlan_hdd_send_sta_authorized_event: Function to send station authorized + * event to user space in case of SAP + * @adapter: Pointer to the adapter + * @hdd_ctx: HDD Context + * @mac_addr: MAC address of the STA for whic the Authorized event needs to + * be sent + * This api is used to send station authorized event to user space + */ +QDF_STATUS wlan_hdd_send_sta_authorized_event( + struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + const struct qdf_mac_addr *mac_addr); + +/** + * hdd_is_legacy_connection() - Is adapter connection is legacy + * @adapter: Handle to hdd_adapter + * + * Return: true if connection mode is legacy, false otherwise. + */ +bool hdd_is_legacy_connection(struct hdd_adapter *adapter); + +/** + * hdd_set_dynamic_antenna_mode() - set dynamic antenna mode + * @adapter: Pointer to network adapter + * @num_rx_chains: number of chains to be used for receiving data + * @num_tx_chains: number of chains to be used for transmitting data + * + * This function will set dynamic antenna mode + * + * Return: 0 for success + */ +int hdd_set_dynamic_antenna_mode(struct hdd_adapter *adapter, + uint8_t num_rx_chains, + uint8_t num_tx_chains); +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfr.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfr.c new file mode 100644 index 0000000000000000000000000000000000000000..4d8bc5c65f544fa28eab38cc9c7fd98fa97640d5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfr.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_cfr.c + * + * WLAN Host Device Driver CFR capture Implementation + */ + +#include +#include +#include +#include +#include "wlan_hdd_includes.h" +#include "osif_sync.h" +#include "wlan_hdd_cfr.h" +#include "wlan_cfr_ucfg_api.h" + +const struct nla_policy cfr_config_policy[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE] = {.type = NLA_FLAG}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE] = { + .type = NLA_FLAG}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK] = {.type = NLA_U64}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER] = { + .type = NLA_U32}, +}; + +static QDF_STATUS +wlan_cfg80211_cfr_set_group_config(struct wlan_objmgr_vdev *vdev, + struct nlattr *tb[]) +{ + struct cfr_wlanconfig_param params = { 0 }; + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER]) { + params.grp_id = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER]); + hdd_debug("group_id %d", params.grp_id); + } + + if (params.grp_id >= HDD_INVALID_GROUP_ID) { + hdd_err("invalid group id"); + return QDF_STATUS_E_INVAL; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA]) { + nla_memcpy(¶ms.ta[0], + tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA], + QDF_MAC_ADDR_SIZE); + hdd_debug("ta " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(¶ms.ta[0])); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK]) { + nla_memcpy(¶ms.ta_mask[0], + tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK], + QDF_MAC_ADDR_SIZE); + hdd_debug("ta_mask " QDF_FULL_MAC_FMT, + QDF_FULL_MAC_REF(¶ms.ta_mask[0])); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA]) { + nla_memcpy(¶ms.ra[0], + tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA], + QDF_MAC_ADDR_SIZE); + hdd_debug("ra " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(¶ms.ra[0])); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK]) { + nla_memcpy(¶ms.ra_mask[0], + tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK], + QDF_MAC_ADDR_SIZE); + hdd_debug("ra_mask " QDF_FULL_MAC_FMT, + QDF_FULL_MAC_REF(¶ms.ra_mask[0])); + } + + if (!qdf_is_macaddr_zero((struct qdf_mac_addr *)¶ms.ta) || + !qdf_is_macaddr_zero((struct qdf_mac_addr *)¶ms.ra) || + !qdf_is_macaddr_zero((struct qdf_mac_addr *)¶ms.ta_mask) || + !qdf_is_macaddr_zero((struct qdf_mac_addr *)¶ms.ra_mask)) { + hdd_debug("set tara config"); + ucfg_cfr_set_tara_config(vdev, ¶ms); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS]) { + params.nss = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS]); + hdd_debug("nss %d", params.nss); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW]) { + params.bw = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW]); + hdd_debug("bw %d", params.bw); + } + + if (params.nss || params.bw) { + hdd_debug("set bw nss"); + ucfg_cfr_set_bw_nss(vdev, ¶ms); + } + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER]) { + params.expected_mgmt_subtype = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER]); + hdd_debug("expected_mgmt_subtype %d(%x)", + params.expected_mgmt_subtype, + params.expected_mgmt_subtype); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER]) { + params.expected_ctrl_subtype = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER]); + hdd_debug("expected_mgmt_subtype %d(%x)", + params.expected_ctrl_subtype, + params.expected_ctrl_subtype); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER]) { + params.expected_data_subtype = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER]); + hdd_debug("expected_mgmt_subtype %d(%x)", + params.expected_data_subtype, + params.expected_data_subtype); + } + if (!params.expected_mgmt_subtype || + !params.expected_ctrl_subtype || + !params.expected_data_subtype) { + hdd_debug("set frame type"); + ucfg_cfr_set_frame_type_subtype(vdev, ¶ms); + } + + return QDF_STATUS_SUCCESS; +} + +static enum capture_type convert_vendor_cfr_capture_type( + enum qca_wlan_vendor_cfr_capture_type type) +{ + switch (type) { + case QCA_WLAN_VENDOR_CFR_DIRECT_FTM: + return RCC_DIRECTED_FTM_FILTER; + case QCA_WLAN_VENDOR_CFR_ALL_FTM_ACK: + return RCC_ALL_FTM_ACK_FILTER; + case QCA_WLAN_VENDOR_CFR_DIRECT_NDPA_NDP: + return RCC_DIRECTED_NDPA_NDP_FILTER; + case QCA_WLAN_VENDOR_CFR_TA_RA: + return RCC_TA_RA_FILTER; + case QCA_WLAN_VENDOR_CFR_ALL_PACKET: + return RCC_NDPA_NDP_ALL_FILTER; + default: + hdd_err("invalid capture type"); + return RCC_DIS_ALL_MODE; + } +} + +static int +wlan_cfg80211_cfr_set_config(struct wlan_objmgr_vdev *vdev, + struct nlattr *tb[]) +{ + struct nlattr *group[QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX + 1]; + struct nlattr *group_list; + struct cfr_wlanconfig_param params = { 0 }; + enum capture_type type; + enum qca_wlan_vendor_cfr_capture_type vendor_capture_type; + int rem = 0; + int maxtype; + int attr; + uint64_t ul_mu_user_mask = 0; + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION]) { + params.cap_dur = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION]); + ucfg_cfr_set_capture_duration(vdev, ¶ms); + hdd_debug("params.cap_dur %d", params.cap_dur); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL]) { + params.cap_intvl = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL]); + ucfg_cfr_set_capture_interval(vdev, ¶ms); + hdd_debug("params.cap_intvl %d", params.cap_intvl); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE]) { + vendor_capture_type = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE]); + if ((vendor_capture_type < QCA_WLAN_VENDOR_CFR_DIRECT_FTM) || + (vendor_capture_type > QCA_WLAN_VENDOR_CFR_ALL_PACKET)) { + hdd_err_rl("invalid capture type %d", + vendor_capture_type); + return -EINVAL; + } + type = convert_vendor_cfr_capture_type(vendor_capture_type); + ucfg_cfr_set_rcc_mode(vdev, type, 1); + hdd_debug("type %d", type); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK]) { + ul_mu_user_mask = nla_get_u64(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK]); + hdd_debug("ul_mu_user_mask_lower %d", + params.ul_mu_user_mask_lower); + } + + if (ul_mu_user_mask) { + params.ul_mu_user_mask_lower = + (uint32_t)(ul_mu_user_mask & 0xffffffff); + params.ul_mu_user_mask_lower = + (uint32_t)(ul_mu_user_mask >> 32); + hdd_debug("set ul mu user maks"); + ucfg_cfr_set_ul_mu_user_mask(vdev, ¶ms); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT]) { + params.freeze_tlv_delay_cnt_thr = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT]); + if (params.freeze_tlv_delay_cnt_thr) { + params.freeze_tlv_delay_cnt_en = 1; + ucfg_cfr_set_freeze_tlv_delay_cnt(vdev, ¶ms); + hdd_debug("freeze_tlv_delay_cnt_thr %d", + params.freeze_tlv_delay_cnt_thr); + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TABLE]) { + maxtype = QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX; + attr = QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TABLE; + nla_for_each_nested(group_list, tb[attr], rem) { + if (wlan_cfg80211_nla_parse(group, maxtype, + nla_data(group_list), + nla_len(group_list), + cfr_config_policy)) { + hdd_err("nla_parse failed for cfr config group"); + return -EINVAL; + } + wlan_cfg80211_cfr_set_group_config(vdev, group); + } + } + + return 0; +} + +static int +wlan_cfg80211_peer_cfr_capture_cfg(struct wiphy *wiphy, + struct hdd_adapter *adapter, + const void *data, + int data_len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX + 1]; + struct cfr_wlanconfig_param params = { 0 }; + struct wlan_objmgr_vdev *vdev; + uint8_t version = 0; + bool is_start_capture = false; + QDF_STATUS status; + int ret; + + if (wlan_cfg80211_nla_parse( + tb, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX, + data, + data_len, + cfr_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION]) { + version = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION]); + hdd_debug("version %d", version); + if (version != ENHANCED_CFR_VERSION) { + hdd_err("unsupported version"); + return -EFAULT; + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE]) { + is_start_capture = nla_get_flag(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE]); + } + + if (is_start_capture && + !tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP]) { + hdd_err("Invalid group bitmap"); + return -EINVAL; + } + + vdev = adapter->vdev; + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_CFR_ID); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("can't get vdev"); + return qdf_status_to_os_return(status); + } + + if (is_start_capture) { + ret = wlan_cfg80211_cfr_set_config(vdev, tb); + if (ret) { + hdd_err("set config failed"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_CFR_ID); + return ret; + } + params.en_cfg = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP]); + hdd_debug("params.en_cfg %d", params.en_cfg); + ucfg_cfr_set_en_bitmap(vdev, ¶ms); + } else { + hdd_debug("cleanup rcc mode"); + ucfg_cfr_set_rcc_mode(vdev, RCC_DIS_ALL_MODE, 0); + } + ucfg_cfr_subscribe_ppdu_desc(wlan_vdev_get_pdev(vdev), + is_start_capture); + ucfg_cfr_committed_rcc_config(vdev); + if (!is_start_capture) { + ucfg_cfr_stop_indication(vdev); + hdd_debug("stop indication done"); + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_CFR_ID); + + return 0; +} + +static int __wlan_hdd_cfg80211_peer_cfr_capture_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter; + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + wlan_cfg80211_peer_cfr_capture_cfg(wiphy, adapter, + data, data_len); + + hdd_exit(); + + return ret; +} + +int wlan_hdd_cfg80211_peer_cfr_capture_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_peer_cfr_capture_cfg(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_coex_config.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_coex_config.c new file mode 100644 index 0000000000000000000000000000000000000000..b4e83bb022d48f2212330b0e304c96c45857ece4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_coex_config.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_coex_config.c + * + * The implementation of coex configuration + * + */ + +#include "wlan_hdd_main.h" +#include "wmi_unified_param.h" +#include "wlan_hdd_coex_config.h" +#include "qca_vendor.h" +#include "wlan_osif_request_manager.h" +#include "osif_sync.h" +#include "wlan_fwol_ucfg_api.h" + +static const struct nla_policy +coex_config_three_way_policy[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX + 1] = { + [QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE] = { + .type = NLA_U32}, + [QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1] = {.type = NLA_U32}, + [QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2] = {.type = NLA_U32}, + [QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3] = {.type = NLA_U32}, + [QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4] = {.type = NLA_U32}, +}; + +static const uint32_t +config_type_to_wmi_tbl[QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_TYPE_MAX] = { + [QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET] = + WMI_COEX_CONFIG_THREE_WAY_COEX_RESET, + [QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START] = + WMI_COEX_CONFIG_THREE_WAY_COEX_START, +}; + +/** + * __wlan_hdd_cfg80211_set_coex_config() - set coex configuration + * parameters + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: pointer to limit off-channel command parameters. + * @data_len: the length in byte of limit off-channel command parameters. + * + * Return: An error code or 0 on success. + */ +static int __wlan_hdd_cfg80211_set_coex_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX + 1]; + uint32_t config_type; + struct coex_config_params coex_cfg_params = {0}; + struct wlan_fwol_coex_config config = {0}; + int errno; + QDF_STATUS status; + + hdd_enter(); + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno != 0) + return errno; + + status = ucfg_fwol_get_coex_config_params(hdd_ctx->psoc, &config); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to get coex config params"); + return -EINVAL; + } + if (!config.btc_three_way_coex_config_legacy_enable) { + hdd_err("Coex legacy feature should be enable first"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX, + data, data_len, + coex_config_three_way_policy)) { + hdd_err("Invalid coex config ATTR"); + return -EINVAL; + } + + if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE]) { + hdd_err("coex config - attr config_type failed"); + return -EINVAL; + } + + config_type = nla_get_u32( + tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE]); + if (config_type >= QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_TYPE_MAX) { + hdd_err("config_type value %d exceeded Max value %d", + config_type, + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_TYPE_MAX); + return -EINVAL; + } + coex_cfg_params.config_type = config_type_to_wmi_tbl[config_type]; + if (coex_cfg_params.config_type < + WMI_COEX_CONFIG_THREE_WAY_DELAY_PARA || + coex_cfg_params.config_type > + WMI_COEX_CONFIG_THREE_WAY_COEX_START) { + hdd_err("config_type_wmi val error %d", + coex_cfg_params.config_type); + return -EINVAL; + } + + hdd_debug("config_type %d, config_type_wmi %d", + config_type, coex_cfg_params.config_type); + + if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1]) { + hdd_err("coex config - attr priority1 failed"); + return -EINVAL; + } + coex_cfg_params.config_arg1 = nla_get_u32( + tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1]); + + hdd_debug("priority1 0x%x", coex_cfg_params.config_arg1); + + if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2]) { + hdd_err("coex config - attr priority2 failed"); + return -EINVAL; + } + coex_cfg_params.config_arg2 = nla_get_u32( + tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2]); + + hdd_debug("priority2 0x%x", coex_cfg_params.config_arg2); + + if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3]) { + hdd_err("coex config - attr priority3 failed"); + return -EINVAL; + } + coex_cfg_params.config_arg3 = nla_get_u32( + tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3]); + + hdd_debug("priority3 0x%x", coex_cfg_params.config_arg3); + + if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4]) { + hdd_err("coex config - attr priority4 failed"); + return -EINVAL; + } + coex_cfg_params.config_arg4 = nla_get_u32( + tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4]); + + hdd_debug("priority4 0x%x", coex_cfg_params.config_arg4); + + coex_cfg_params.vdev_id = adapter->vdev_id; + status = sme_send_coex_config_cmd(&coex_cfg_params); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to send coex config params"); + return -EINVAL; + } + + return 0; +} + +/** + * wlan_hdd_cfg80211_set_coex_config() - set coex configuration + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: pointer to limit off-channel command parameters. + * @data_len: the length in byte of limit off-channel command parameters. + * + * + * Return: An error code or 0 on success. + */ +int wlan_hdd_cfg80211_set_coex_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_coex_config(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_coex_config.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_coex_config.h new file mode 100644 index 0000000000000000000000000000000000000000..fbba0d03160bb0502ed90ea7d03d1bc5f0d29f21 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_coex_config.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_coex_config.h + * + * Add Vendor subcommand QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG + */ + +#ifndef __WLAN_HDD_COEX_CONFIG_H +#define __WLAN_HDD_COEX_CONFIG_H + +#ifdef FEATURE_COEX_CONFIG +#include + +/** + * wlan_hdd_cfg80211_set_coex_config() - set coex configuration + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: pointer to limit off-channel command parameters. + * @data_len: the length in byte of limit off-channel command parameters. + * + * Return: An error code or 0 on success. + */ +int wlan_hdd_cfg80211_set_coex_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +#define FEATURE_COEX_CONFIG_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG,\ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_set_coex_config \ +}, +#else /* FEATURE_COEX_CONFIG */ +#define FEATURE_COEX_CONFIG_COMMANDS +#endif /* FEATURE_COEX_CONFIG */ + +#endif /* __WLAN_HDD_COEX_CONFIG_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_conc_ut.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_conc_ut.c new file mode 100644 index 0000000000000000000000000000000000000000..a5ea9b2e1873ccc9f7fe12fb9d14101f675013d9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_conc_ut.c @@ -0,0 +1,895 @@ +/* + * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* Include files */ + +#include +#include +#include +#include +#include +#include "wlan_hdd_trace.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_hdd_conc_ut.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "cds_utils.h" +#include "wma_types.h" +#include "wma.h" +#include "wma_api.h" +#include "wlan_policy_mgr_ucfg.h" + +#define NUMBER_OF_SCENARIO 300 +#define MAX_ALLOWED_CHAR_IN_REPORT 50 + +/** + * struct report_t: Data structure to fill report + * + * @title: title of the concurrency case scenario + * @first_persona: device type of first persona + * @second_persona: device type of second persona + * @third_persona: device type of third persona + * @dbs_value: string to mention whether dbs enable or disable + * @system_conf: string to mention what is system's configuration + * @status: status field + * @result_code: string to mention whether test case passed or failed + * @reason: reason why test case failed + * @pcl: preferred channel list + * + * This structure will be used by unit test framework to fill + * report after running various concurrency scenarios. + */ +struct report_t { + char title[2 * MAX_ALLOWED_CHAR_IN_REPORT]; + char first_persona[MAX_ALLOWED_CHAR_IN_REPORT]; + char second_persona[MAX_ALLOWED_CHAR_IN_REPORT]; + char third_persona[MAX_ALLOWED_CHAR_IN_REPORT]; + char dbs_value[MAX_ALLOWED_CHAR_IN_REPORT]; + char system_conf[MAX_ALLOWED_CHAR_IN_REPORT]; + bool status; + char result_code[MAX_ALLOWED_CHAR_IN_REPORT]; + char reason[MAX_ALLOWED_CHAR_IN_REPORT]; + char pcl[2 * NUM_CHANNELS]; +}; + +static struct report_t report[NUMBER_OF_SCENARIO]; +static uint32_t report_idx; + +static uint8_t wlan_hdd_valid_type_of_persona(uint32_t sub_type) +{ + switch (sub_type) { + case PM_STA_MODE: + return WMI_VDEV_TYPE_STA; + case PM_IBSS_MODE: + return WMI_VDEV_TYPE_IBSS; + case PM_SAP_MODE: + case PM_P2P_CLIENT_MODE: + case PM_P2P_GO_MODE: + return WMI_VDEV_TYPE_AP; + default: + return WMI_VDEV_TYPE_STA; + } +} + +static const char *system_config_to_string(uint8_t idx) +{ + switch (idx) { + CASE_RETURN_STRING(PM_THROUGHPUT); + CASE_RETURN_STRING(PM_POWERSAVE); + CASE_RETURN_STRING(PM_LATENCY); + default: + return "Unknown"; + } + +} + +static const char *device_mode_to_string(uint8_t idx) +{ + switch (idx) { + CASE_RETURN_STRING(PM_STA_MODE); + CASE_RETURN_STRING(PM_SAP_MODE); + CASE_RETURN_STRING(PM_P2P_CLIENT_MODE); + CASE_RETURN_STRING(PM_P2P_GO_MODE); + CASE_RETURN_STRING(PM_IBSS_MODE); + default: + return "none"; + } +} + +static const char *pcl_type_to_string(uint8_t idx) +{ + switch (idx) { + CASE_RETURN_STRING(PM_NONE); + CASE_RETURN_STRING(PM_24G); + CASE_RETURN_STRING(PM_5G); + CASE_RETURN_STRING(PM_SCC_CH); + CASE_RETURN_STRING(PM_MCC_CH); + CASE_RETURN_STRING(PM_SCC_CH_24G); + CASE_RETURN_STRING(PM_SCC_CH_5G); + CASE_RETURN_STRING(PM_24G_SCC_CH); + CASE_RETURN_STRING(PM_5G_SCC_CH); + CASE_RETURN_STRING(PM_SCC_ON_5_SCC_ON_24_24G); + CASE_RETURN_STRING(PM_SCC_ON_5_SCC_ON_24_5G); + CASE_RETURN_STRING(PM_SCC_ON_24_SCC_ON_5_24G); + CASE_RETURN_STRING(PM_SCC_ON_24_SCC_ON_5_5G); + CASE_RETURN_STRING(PM_SCC_ON_5_SCC_ON_24); + CASE_RETURN_STRING(PM_SCC_ON_24_SCC_ON_5); + CASE_RETURN_STRING(PM_MCC_CH_24G); + CASE_RETURN_STRING(PM_MCC_CH_5G); + CASE_RETURN_STRING(PM_24G_MCC_CH); + CASE_RETURN_STRING(PM_5G_MCC_CH); + default: + return "Unknown"; + } +} + +void clean_report(struct hdd_context *hdd_ctx) +{ + uint32_t idx = 0; + + while (idx < NUMBER_OF_SCENARIO) { + qdf_mem_zero(&report[idx], sizeof(struct report_t)); + idx++; + } + report_idx = 0; +} + +void print_report(struct hdd_context *hdd_ctx) +{ + uint32_t idx = 0; + + pr_info("+----------Report start -----------+\n"); + while (idx < report_idx) { + pr_info("Idx:[%d]\nTitle:%s\nResult:[%s]\n\t1st_person[%s]\n\t2nd_persona[%s]\n\t3rd_persona[%s]\n\tDBS[%s]\n\tsystem_config[%s]\n\treason[%s]\n\tpcl[%s]\n", + idx, + report[idx].title, report[idx].result_code, + report[idx].first_persona, report[idx].second_persona, + report[idx].third_persona, report[idx].dbs_value, + report[idx].system_conf, report[idx].reason, + report[idx].pcl); + idx++; + } + pr_info("+----------Report end -----------+\n"); +} + +void fill_report(struct hdd_context *hdd_ctx, char *title, + uint32_t first_persona, uint32_t second_persona, uint32_t third_persona, + uint32_t chnl_1st_conn, uint32_t chnl_2nd_conn, uint32_t chnl_3rd_conn, + bool status, enum policy_mgr_pcl_type pcl_type, char *reason, + uint8_t *pcl) +{ + int i; + char buf[4] = {0}; + uint8_t sys_pref = 0; + + if (report_idx >= NUMBER_OF_SCENARIO) + return; + + ucfg_policy_mgr_get_sys_pref(hdd_ctx->psoc, &sys_pref); + + snprintf(report[report_idx].title, + 2 * MAX_ALLOWED_CHAR_IN_REPORT, "pcl for[%s] pcl_type[%s]", + title, pcl_type_to_string(pcl_type)); + if (chnl_1st_conn == 0) + snprintf(report[report_idx].first_persona, + MAX_ALLOWED_CHAR_IN_REPORT, "%s", + device_mode_to_string(first_persona)); + else + snprintf(report[report_idx].first_persona, + MAX_ALLOWED_CHAR_IN_REPORT, + "%s-chnl{%d}", + device_mode_to_string(first_persona), chnl_1st_conn); + if (chnl_2nd_conn == 0) + snprintf(report[report_idx].second_persona, + MAX_ALLOWED_CHAR_IN_REPORT, "%s", + device_mode_to_string(second_persona)); + else + snprintf(report[report_idx].second_persona, + MAX_ALLOWED_CHAR_IN_REPORT, + "%s-chnl{%d}", + device_mode_to_string(second_persona), chnl_2nd_conn); + if (chnl_3rd_conn == 0) + snprintf(report[report_idx].third_persona, + MAX_ALLOWED_CHAR_IN_REPORT, "%s", + device_mode_to_string(third_persona)); + else + snprintf(report[report_idx].third_persona, + MAX_ALLOWED_CHAR_IN_REPORT, + "%s-chnl{%d}", + device_mode_to_string(third_persona), chnl_3rd_conn); + + report[report_idx].status = status; + snprintf(report[report_idx].dbs_value, + MAX_ALLOWED_CHAR_IN_REPORT, "%s", + policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) + ? "enable" : "disable"); + snprintf(report[report_idx].system_conf, + MAX_ALLOWED_CHAR_IN_REPORT, "%s", + system_config_to_string(sys_pref)); + snprintf(report[report_idx].result_code, + MAX_ALLOWED_CHAR_IN_REPORT, "%s", + status ? "PASS" : "FAIL"); + snprintf(report[report_idx].reason, + MAX_ALLOWED_CHAR_IN_REPORT, + reason); + if (pcl) { + qdf_mem_zero(report[report_idx].pcl, + sizeof(report[report_idx].pcl)); + for (i = 0; i < NUM_CHANNELS; i++) { + if (pcl[i] == 0) + break; + qdf_mem_zero(buf, sizeof(buf)); + snprintf(buf, sizeof(buf), "%d ", pcl[i]); + strlcat(report[report_idx].pcl, buf, + sizeof(report[report_idx].pcl)); + strlcat(report[report_idx].pcl, ", ", + sizeof(report[report_idx].pcl)); + } + } + report_idx++; +} + +static bool wlan_hdd_validate_pcl(struct hdd_context *hdd_ctx, + enum policy_mgr_pcl_type pcl_type, uint8_t *pcl, uint32_t pcl_len, + uint8_t first_connection_chnl, uint8_t second_connection_chnl, + char *reason, uint32_t reason_length) +{ + bool status = true; + uint32_t first_idx = 0; + + if ((pcl_type != PM_NONE) && (pcl_len == 0)) { + snprintf(reason, reason_length, "no of channels = 0"); + return false; + } + + switch (pcl_type) { + case PM_NONE: + if (pcl_len != 0) { + snprintf(reason, reason_length, "no of channels>0"); + return false; + } + break; + case PM_5G: + for (first_idx = 0; first_idx < pcl_len; first_idx++) { + if (!WLAN_REG_IS_5GHZ_CH(pcl[first_idx])) { + snprintf(reason, reason_length, + "2G channel found"); + return false; + } + } + break; + case PM_24G: + for (first_idx = 0; first_idx < pcl_len; first_idx++) { + if (!WLAN_REG_IS_24GHZ_CH(pcl[first_idx])) { + snprintf(reason, reason_length, + "5G channel found"); + return false; + } + } + break; + case PM_SCC_CH: + if (second_connection_chnl > 0 && + (first_connection_chnl != second_connection_chnl)) { + snprintf(reason, reason_length, + "invalid connections"); + return false; + } + if (pcl[0] != first_connection_chnl) { + snprintf(reason, reason_length, + "No SCC found"); + return false; + } + break; + case PM_MCC_CH: + if ((pcl[0] != first_connection_chnl) && + ((second_connection_chnl > 0) && + (pcl[0] != second_connection_chnl))) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + if ((second_connection_chnl > 0) && + (pcl[1] != first_connection_chnl && + pcl[1] != second_connection_chnl)) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + break; + case PM_SCC_CH_24G: + if (second_connection_chnl > 0 && + (first_connection_chnl != second_connection_chnl)) { + snprintf(reason, reason_length, + "invalid connections"); + return false; + } + if (pcl[0] != first_connection_chnl) { + snprintf(reason, reason_length, + "No SCC found"); + return false; + } + if (!WLAN_REG_IS_24GHZ_CH(pcl[pcl_len - 1])) { + snprintf(reason, reason_length, + "No 2.4Ghz chnl"); + return false; + } + break; + case PM_SCC_CH_5G: + if (second_connection_chnl > 0 && + (first_connection_chnl != second_connection_chnl)) { + snprintf(reason, reason_length, + "invalid connections"); + return false; + } + if (pcl[0] != first_connection_chnl) { + snprintf(reason, reason_length, + "No SCC found"); + return false; + } + if (!WLAN_REG_IS_5GHZ_CH(pcl[pcl_len - 1])) { + snprintf(reason, reason_length, + "No 5Ghz chnl"); + return false; + } + break; + case PM_24G_SCC_CH: + if (!WLAN_REG_IS_24GHZ_CH(pcl[0])) { + snprintf(reason, reason_length, + "No 2.4Ghz chnl"); + return false; + } + if (second_connection_chnl > 0 && + (first_connection_chnl != second_connection_chnl)) { + snprintf(reason, reason_length, + "invalid connections"); + return false; + } + if (pcl[pcl_len-1] != first_connection_chnl) { + snprintf(reason, reason_length, + "No SCC found"); + return false; + } + break; + case PM_5G_SCC_CH: + if (!WLAN_REG_IS_5GHZ_CH(pcl[0])) { + snprintf(reason, reason_length, + "No 5Ghz chnl"); + return false; + } + if (second_connection_chnl > 0 && + (first_connection_chnl != second_connection_chnl)) { + snprintf(reason, reason_length, + "invalid connections"); + return false; + } + if (pcl[pcl_len-1] != first_connection_chnl) { + snprintf(reason, reason_length, + "No SCC found"); + return false; + } + break; + case PM_MCC_CH_24G: + if ((pcl[0] != first_connection_chnl) && + ((second_connection_chnl > 0) && + (pcl[0] != second_connection_chnl))) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + if ((second_connection_chnl > 0) && + (pcl[1] != first_connection_chnl && + pcl[1] != second_connection_chnl)) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + if (!WLAN_REG_IS_24GHZ_CH(pcl[pcl_len - 1])) { + snprintf(reason, reason_length, + "No 24Ghz chnl"); + return false; + } + break; + case PM_MCC_CH_5G: + if ((pcl[0] != first_connection_chnl) && + ((second_connection_chnl > 0) && + (pcl[0] != second_connection_chnl))) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + if ((second_connection_chnl > 0) && + (pcl[1] != first_connection_chnl && + pcl[1] != second_connection_chnl)) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + if (!WLAN_REG_IS_5GHZ_CH(pcl[pcl_len - 1])) { + snprintf(reason, reason_length, + "No 5Ghz chnl"); + return false; + } + break; + case PM_24G_MCC_CH: + if (!WLAN_REG_IS_24GHZ_CH(pcl[0])) { + snprintf(reason, reason_length, + "No 24Ghz chnl"); + return false; + } + if ((pcl[pcl_len-1] != first_connection_chnl) && + ((second_connection_chnl > 0) && + (pcl[pcl_len-1] != second_connection_chnl))) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + if ((second_connection_chnl > 0) && + (pcl[pcl_len-2] != first_connection_chnl && + pcl[pcl_len-2] != second_connection_chnl)) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + break; + case PM_5G_MCC_CH: + if (!WLAN_REG_IS_5GHZ_CH(pcl[0])) { + snprintf(reason, reason_length, + "No 5Ghz chnl"); + return false; + } + if ((pcl[pcl_len-1] != first_connection_chnl) && + ((second_connection_chnl > 0) && + (pcl[pcl_len-1] != second_connection_chnl))) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + if ((second_connection_chnl > 0) && + (pcl[pcl_len-2] != first_connection_chnl && + pcl[pcl_len-2] != second_connection_chnl)) { + snprintf(reason, reason_length, + "MCC invalid"); + return false; + } + break; + case PM_SCC_ON_5_SCC_ON_24_24G: + if (!WLAN_REG_IS_5GHZ_CH(pcl[0]) || + (pcl[0] != first_connection_chnl && + pcl[0] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 5Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_24GHZ_CH(pcl[1]) || + (pcl[1] != first_connection_chnl && + pcl[1] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 24Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_24GHZ_CH(pcl[pcl_len - 1])) { + snprintf(reason, reason_length, + "No 24Ghz chnls"); + return false; + } + break; + case PM_SCC_ON_5_SCC_ON_24_5G: + if (!WLAN_REG_IS_5GHZ_CH(pcl[0]) || + (pcl[0] != first_connection_chnl && + pcl[0] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 5Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_24GHZ_CH(pcl[1]) || + (pcl[1] != first_connection_chnl && + pcl[1] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 24Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_5GHZ_CH(pcl[pcl_len - 1])) { + snprintf(reason, reason_length, + "No 5Ghz chnls"); + return false; + } + break; + case PM_SCC_ON_24_SCC_ON_5_24G: + if (!WLAN_REG_IS_24GHZ_CH(pcl[0]) || + (pcl[0] != first_connection_chnl && + pcl[0] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 24Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_5GHZ_CH(pcl[1]) || + (pcl[1] != first_connection_chnl && + pcl[1] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 5Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_24GHZ_CH(pcl[pcl_len - 1])) { + snprintf(reason, reason_length, + "No 24Ghz chnls"); + return false; + } + break; + case PM_SCC_ON_24_SCC_ON_5_5G: + if (!WLAN_REG_IS_24GHZ_CH(pcl[0]) || + (pcl[0] != first_connection_chnl && + pcl[0] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 24Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_5GHZ_CH(pcl[1]) || + (pcl[1] != first_connection_chnl && + pcl[1] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 5Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_5GHZ_CH(pcl[pcl_len - 1])) { + snprintf(reason, reason_length, + "No 5Ghz chnls"); + return false; + } + break; + case PM_SCC_ON_5_SCC_ON_24: + if (!WLAN_REG_IS_5GHZ_CH(pcl[0]) || + (pcl[0] != first_connection_chnl && + pcl[0] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 5Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_24GHZ_CH(pcl[1]) || + (pcl[1] != first_connection_chnl && + pcl[1] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 24Ghz chnl/scc"); + return false; + } + if (pcl_len != 2) { + snprintf(reason, reason_length, + "more than 2 chnls"); + return false; + } + break; + case PM_SCC_ON_24_SCC_ON_5: + if (!WLAN_REG_IS_24GHZ_CH(pcl[0]) || + (pcl[0] != first_connection_chnl && + pcl[0] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 24Ghz chnl/scc"); + return false; + } + if (!WLAN_REG_IS_5GHZ_CH(pcl[1]) || + (pcl[1] != first_connection_chnl && + pcl[1] != second_connection_chnl)) { + snprintf(reason, reason_length, + "No 5Ghz chnl/scc"); + return false; + } + if (pcl_len != 2) { + snprintf(reason, reason_length, + "more than 2 chnls"); + return false; + } + break; + default: + snprintf(reason, reason_length, + "Unknown option"); + status = false; + } + if (status == true) { + snprintf(reason, reason_length, + "success"); + } + return status; +} + +static void wlan_hdd_map_subtypes_hdd_wma(enum policy_mgr_con_mode *dst, + enum policy_mgr_con_mode *src) +{ + /* + * wma defined sap subtype as 0 + * Rest of the mappings are same + * In future, if mapping gets changed then re-map it here + */ + if (*src == PM_SAP_MODE) + *dst = 0; + else + *dst = *src; +} + +void wlan_hdd_one_connection_scenario(struct hdd_context *hdd_ctx) +{ + enum policy_mgr_con_mode sub_type; + uint8_t pcl[NUM_CHANNELS] = {0}, + weight_list[NUM_CHANNELS] = {0}; + uint32_t pcl_len = 0, i, pcl_freqs[NUM_CHANNELS] = {0}; + bool status = false; + enum policy_mgr_pcl_type pcl_type; + char reason[20] = {0}; + QDF_STATUS ret; + struct policy_mgr_sme_cbacks sme_cbacks; + uint8_t system_pref = 0; + + ucfg_policy_mgr_get_sys_pref(hdd_ctx->psoc, &system_pref); + + sme_cbacks.sme_get_nss_for_vdev = sme_get_vdev_type_nss; + /* flush the entire table first */ + ret = policy_mgr_psoc_enable(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(ret)) { + hdd_err("Policy manager initialization failed"); + return; + } + + for (sub_type = 0; sub_type < PM_MAX_NUM_OF_MODE; sub_type++) { + /* validate one connection is created or no */ + if (policy_mgr_get_connection_count(hdd_ctx->psoc) != 0) { + hdd_err("Test failed - No. of connection is not 0"); + return; + } + qdf_mem_zero(pcl, sizeof(pcl)); + pcl_len = 0; + pcl_type = policy_mgr_get_pcl_from_first_conn_table( + sub_type, system_pref); + + /* check PCL value for second connection is correct or no */ + policy_mgr_get_pcl(hdd_ctx->psoc, sub_type, pcl_freqs, &pcl_len, + weight_list, QDF_ARRAY_SIZE(weight_list)); + for (i = 0; i < pcl_len; i++) + pcl[i] = wlan_freq_to_chan(pcl_freqs[i]); + + status = wlan_hdd_validate_pcl(hdd_ctx, + pcl_type, pcl, pcl_len, 0, 0, + reason, sizeof(reason)); + if ((pcl_type == PM_MAX_PCL_TYPE) && (pcl[0] == 0)) + continue; + + fill_report(hdd_ctx, "1 connection", sub_type, + PM_MAX_NUM_OF_MODE, + PM_MAX_NUM_OF_MODE, + 0, 0, 0, + status, pcl_type, reason, pcl); + } +} + +void wlan_hdd_two_connections_scenario(struct hdd_context *hdd_ctx, + uint8_t first_chnl, enum policy_mgr_chain_mode first_chain_mask) +{ + uint8_t vdevid = 0, tx_stream = 2, rx_stream = 2; + uint8_t type = WMI_VDEV_TYPE_STA, channel_id = first_chnl, mac_id = 1; + uint8_t pcl[NUM_CHANNELS] = {0}, + weight_list[NUM_CHANNELS] = {0}; + uint32_t pcl_len = 0, i, pcl_freqs[NUM_CHANNELS]; + enum policy_mgr_chain_mode chain_mask = first_chain_mask; + enum policy_mgr_con_mode sub_type, next_sub_type, dummy_type; + enum policy_mgr_pcl_type pcl_type; + enum policy_mgr_one_connection_mode second_index; + char reason[20] = {0}; + bool status = false; + QDF_STATUS ret; + struct policy_mgr_sme_cbacks sme_cbacks; + uint8_t system_pref = 0; + + ucfg_policy_mgr_get_sys_pref(hdd_ctx->psoc, &system_pref); + + for (sub_type = PM_STA_MODE; + sub_type < PM_MAX_NUM_OF_MODE; sub_type++) { + type = wlan_hdd_valid_type_of_persona(sub_type); + + sme_cbacks.sme_get_nss_for_vdev = sme_get_vdev_type_nss; + /* flush the entire table first */ + ret = policy_mgr_psoc_enable(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(ret)) { + hdd_err("Policy manager initialization failed"); + return; + } + + /* sub_type mapping between HDD and WMA are different */ + wlan_hdd_map_subtypes_hdd_wma(&dummy_type, &sub_type); + /* add first connection as STA */ + policy_mgr_incr_connection_count_utfw( + hdd_ctx->psoc, vdevid, tx_stream, + rx_stream, chain_mask, type, dummy_type, + wlan_chan_to_freq(channel_id), mac_id); + /* validate one connection is created or no */ + if (policy_mgr_get_connection_count(hdd_ctx->psoc) != 1) { + hdd_err("Test failed - No. of connection is not 1"); + return; + } + next_sub_type = PM_STA_MODE; + while (next_sub_type < PM_MAX_NUM_OF_MODE) { + /* get the PCL value & check the channels accordingly */ + second_index = + policy_mgr_get_second_connection_pcl_table_index( + hdd_ctx->psoc); + if (PM_MAX_ONE_CONNECTION_MODE == second_index) { + /* not valid combination*/ + hdd_err("couldn't find index for 2nd connection pcl table"); + next_sub_type++; + continue; + } + qdf_mem_zero(pcl, sizeof(pcl)); + pcl_len = 0; + pcl_type = policy_mgr_get_pcl_from_second_conn_table( + second_index, next_sub_type, system_pref, + policy_mgr_is_hw_dbs_capable( + hdd_ctx->psoc)); + /* check PCL for second connection is correct or no */ + policy_mgr_get_pcl(hdd_ctx->psoc, + next_sub_type, pcl_freqs, &pcl_len, + weight_list, + QDF_ARRAY_SIZE(weight_list)); + for (i = 0; i < pcl_len; i++) + pcl[i] = wlan_freq_to_chan(pcl_freqs[i]); + status = wlan_hdd_validate_pcl(hdd_ctx, + pcl_type, pcl, pcl_len, channel_id, 0, + reason, sizeof(reason)); + if ((pcl_type == PM_MAX_PCL_TYPE) && (pcl[0] == 0)) { + next_sub_type++; + continue; + } + fill_report(hdd_ctx, "2 connections", sub_type, + next_sub_type, + PM_MAX_NUM_OF_MODE, first_chnl, + 0, 0, status, pcl_type, reason, pcl); + next_sub_type++; + } + } +} + +void wlan_hdd_three_connections_scenario(struct hdd_context *hdd_ctx, + uint8_t first_chnl, uint8_t second_chnl, + enum policy_mgr_chain_mode chain_mask, uint8_t use_same_mac) +{ + uint8_t vdevid_1 = 0, tx_stream_1 = 2, rx_stream_1 = 2; + uint8_t vdevid_2 = 1, tx_stream_2 = 2, rx_stream_2 = 2; + uint8_t channel_id_1 = first_chnl, channel_id_2 = second_chnl; + uint8_t mac_id_1, mac_id_2; + uint8_t type_1 = WMI_VDEV_TYPE_STA, type_2 = WMI_VDEV_TYPE_STA; + uint8_t pcl[NUM_CHANNELS] = {0}, weight_list[NUM_CHANNELS] = {0}; + uint32_t pcl_len = 0, i, pcl_freqs[NUM_CHANNELS]; + enum policy_mgr_chain_mode chain_mask_1; + enum policy_mgr_chain_mode chain_mask_2; + enum policy_mgr_con_mode sub_type_1, sub_type_2, next_sub_type; + enum policy_mgr_con_mode dummy_type_1, dummy_type_2; + enum policy_mgr_pcl_type pcl_type; + enum policy_mgr_two_connection_mode third_index; + char reason[20] = {0}; + bool status = false; + QDF_STATUS ret; + struct policy_mgr_sme_cbacks sme_cbacks; + uint8_t system_pref = 0; + + ucfg_policy_mgr_get_sys_pref(hdd_ctx->psoc, &system_pref); + + /* let's set the chain_mask, mac_ids*/ + if (chain_mask == POLICY_MGR_TWO_TWO) { + mac_id_1 = 1; + mac_id_2 = 1; + chain_mask_1 = POLICY_MGR_TWO_TWO; + chain_mask_2 = POLICY_MGR_TWO_TWO; + } else if (use_same_mac == 1) { + mac_id_1 = 1; + mac_id_2 = 1; + chain_mask_1 = POLICY_MGR_ONE_ONE; + chain_mask_2 = POLICY_MGR_ONE_ONE; + } else { + mac_id_1 = 1; + mac_id_2 = 2; + chain_mask_1 = POLICY_MGR_ONE_ONE; + chain_mask_2 = POLICY_MGR_ONE_ONE; + } + + for (sub_type_1 = PM_STA_MODE; + sub_type_1 < PM_MAX_NUM_OF_MODE; sub_type_1++) { + + type_1 = wlan_hdd_valid_type_of_persona(sub_type_1); + + sme_cbacks.sme_get_nss_for_vdev = sme_get_vdev_type_nss; + /* flush the entire table first */ + ret = policy_mgr_psoc_enable(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(ret)) { + hdd_err("Policy manager initialization failed"); + return; + } + + /* sub_type mapping between HDD and WMA are different */ + wlan_hdd_map_subtypes_hdd_wma(&dummy_type_1, &sub_type_1); + /* add first connection as STA */ + policy_mgr_incr_connection_count_utfw( + hdd_ctx->psoc, vdevid_1, tx_stream_1, rx_stream_1, + chain_mask_1, type_1, dummy_type_1, + wlan_chan_to_freq(channel_id_1), mac_id_1); + /* validate one connection is created or no */ + if (policy_mgr_get_connection_count(hdd_ctx->psoc) != 1) { + hdd_err("Test fail - No. of connection not 1"); + return; + } + for (sub_type_2 = PM_STA_MODE; + sub_type_2 < PM_MAX_NUM_OF_MODE; sub_type_2++) { + + type_2 = wlan_hdd_valid_type_of_persona(sub_type_2); + /* sub_type mapping between HDD and WMA are different */ + wlan_hdd_map_subtypes_hdd_wma(&dummy_type_2, + &sub_type_2); + policy_mgr_incr_connection_count_utfw( + hdd_ctx->psoc, vdevid_2, tx_stream_2, + rx_stream_2, chain_mask_2, type_2, + dummy_type_2, + wlan_chan_to_freq(channel_id_2), mac_id_2); + /* validate two connections are created or no */ + if (policy_mgr_get_connection_count(hdd_ctx->psoc) + != 2) { + hdd_err("Test fail - No. connection not 2"); + return; + } + next_sub_type = PM_STA_MODE; + while (next_sub_type < PM_MAX_NUM_OF_MODE) { + third_index = + policy_mgr_get_third_connection_pcl_table_index( + hdd_ctx->psoc); + if (PM_MAX_TWO_CONNECTION_MODE == + third_index) { + /* not valid combination */ + next_sub_type++; + continue; + } + qdf_mem_zero(pcl, sizeof(pcl)); + pcl_len = 0; + pcl_type = + policy_mgr_get_pcl_from_third_conn_table( + third_index, next_sub_type, + system_pref, + policy_mgr_is_hw_dbs_capable( + hdd_ctx->psoc)); + policy_mgr_get_pcl( + hdd_ctx->psoc, next_sub_type, + pcl_freqs, &pcl_len, weight_list, + QDF_ARRAY_SIZE(weight_list)); + for (i = 0; i < pcl_len; i++) + pcl[i] = + wlan_freq_to_chan(pcl_freqs[i]); + status = wlan_hdd_validate_pcl(hdd_ctx, + pcl_type, pcl, pcl_len, + channel_id_1, channel_id_2, + reason, sizeof(reason)); + if ((pcl_type == PM_MAX_PCL_TYPE) && + (pcl[0] == 0)) { + next_sub_type++; + continue; + } + fill_report(hdd_ctx, "3 connections", + sub_type_1, sub_type_2, + next_sub_type, first_chnl, + second_chnl, 0, status, + pcl_type, reason, pcl); + next_sub_type++; + } + /* remove entry to make a room for next iteration */ + policy_mgr_decr_connection_count(hdd_ctx->psoc, + vdevid_2); + } + next_sub_type = PM_STA_MODE; + } +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_concurrency_matrix.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_concurrency_matrix.c new file mode 100644 index 0000000000000000000000000000000000000000..ea091bcb78d17f1ad2d62e21adecddf448ab0c7e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_concurrency_matrix.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_concurrency_matrix.c + * + * WLAN concurrency matrix functions + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include + +#define CDS_MAX_FEATURE_SET 8 +#define MAX_CONCURRENT_MATRIX \ + QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX +#define MATRIX_CONFIG_PARAM_SET_SIZE_MAX \ + QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_CONFIG_PARAM_SET_SIZE_MAX +static const struct nla_policy +wlan_hdd_get_concurrency_matrix_policy[MAX_CONCURRENT_MATRIX + 1] = { + [MATRIX_CONFIG_PARAM_SET_SIZE_MAX] = {.type = NLA_U32}, +}; + +/** + * __wlan_hdd_cfg80211_get_concurrency_matrix() - to retrieve concurrency matrix + * @wiphy: pointer phy adapter + * @wdev: pointer to wireless device structure + * @data: pointer to data buffer + * @data_len: length of data + * + * This routine will give concurrency matrix + * + * Return: int status code + */ +static int +__wlan_hdd_cfg80211_get_concurrency_matrix(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + uint32_t feature_set_matrix[CDS_MAX_FEATURE_SET] = {0}; + uint8_t i, feature_sets, max_feature_sets; + struct nlattr *tb[MAX_CONCURRENT_MATRIX + 1]; + struct sk_buff *reply_skb; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int ret; + + hdd_enter_dev(wdev->netdev); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (wlan_cfg80211_nla_parse(tb, MAX_CONCURRENT_MATRIX, data, data_len, + wlan_hdd_get_concurrency_matrix_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch max feature set */ + if (!tb[MATRIX_CONFIG_PARAM_SET_SIZE_MAX]) { + hdd_err("Attr max feature set size failed"); + return -EINVAL; + } + max_feature_sets = nla_get_u32(tb[MATRIX_CONFIG_PARAM_SET_SIZE_MAX]); + hdd_debug("Max feature set size: %d", max_feature_sets); + + /* Fill feature combination matrix */ + feature_sets = 0; + feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA | + WIFI_FEATURE_P2P; + feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA | + WIFI_FEATURE_NAN; + /* Add more feature combinations here */ + + feature_sets = QDF_MIN(feature_sets, max_feature_sets); + hdd_debug("Number of feature sets: %d", feature_sets); + hdd_debug("Feature set matrix"); + for (i = 0; i < feature_sets; i++) + hdd_debug("[%d] 0x%02X", i, feature_set_matrix[i]); + + reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) + + sizeof(u32) * feature_sets + NLMSG_HDRLEN); + if (!reply_skb) { + hdd_err("Feature set matrix: buffer alloc fail"); + return -ENOMEM; + } + + if (nla_put_u32(reply_skb, + QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET_SIZE, + feature_sets) || + nla_put(reply_skb, + QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET, + sizeof(u32) * feature_sets, + feature_set_matrix)) { + hdd_err("nla put fail"); + kfree_skb(reply_skb); + return -EINVAL; + } + return cfg80211_vendor_cmd_reply(reply_skb); +} + +#undef MAX_CONCURRENT_MATRIX +#undef MATRIX_CONFIG_PARAM_SET_SIZE_MAX + +int wlan_hdd_cfg80211_get_concurrency_matrix(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_concurrency_matrix(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_concurrency_matrix.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_concurrency_matrix.h new file mode 100644 index 0000000000000000000000000000000000000000..1bdc6f75fd4e0809747ddfa933850d62fc947c2e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_concurrency_matrix.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_CONCURRENCY_MATRIX_H +#define __WLAN_HDD_CONCURRENCY_MATRIX_H + +/** + * DOC: wlan_hdd_concurrency_matrix_h + * + * WLAN Host Device Driver concurrency matrix API specification + */ + +#ifdef FEATURE_CONCURRENCY_MATRIX +/** + * wlan_hdd_cfg80211_get_concurrency_matrix() - get concurrency matrix + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Retrieves the concurrency feature set matrix + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_get_concurrency_matrix(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_CONCURRENCY_MATRIX_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX,\ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,\ + .doit = wlan_hdd_cfg80211_get_concurrency_matrix \ +}, +#else /* FEATURE_CONCURRENCY_MATRIX */ +#define FEATURE_CONCURRENCY_MATRIX_VENDOR_COMMANDS +#endif /* FEATURE_CONCURRENCY_MATRIX */ + +#endif /* __WLAN_HDD_CONCURRENCY_MATRIX_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_data_stall_detection.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_data_stall_detection.c new file mode 100644 index 0000000000000000000000000000000000000000..9f82edbb7dccbcc7830215fcd7be999ca3c87174 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_data_stall_detection.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_data_stall_detection.c + * + * WLAN Host Device Driver Data Stall detection API implementation + */ + +#include +#include +#include "wlan_hdd_data_stall_detection.h" +#include "wlan_hdd_main.h" +#include "cdp_txrx_cmn.h" +#include "cdp_txrx_misc.h" +#include "ol_txrx_types.h" +#include "ol_defines.h" +#ifdef FEATURE_WLAN_DIAG_SUPPORT +#include "host_diag_core_event.h" +#include "host_diag_core_log.h" +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +#ifdef FEATURE_WLAN_DIAG_SUPPORT + +/** + * hdd_data_stall_send_event()- send data stall information + * @reason: data stall event subtype + * This Function sends data stall status diag event + * + * Return: void. + */ +static void hdd_data_stall_send_event(uint32_t reason) +{ + WLAN_HOST_DIAG_EVENT_DEF(sta_data_stall, + struct host_event_wlan_datastall); + qdf_mem_zero(&sta_data_stall, sizeof(sta_data_stall)); + sta_data_stall.reason = reason; + hdd_debug("Posting data stall event %x", reason); + WLAN_HOST_DIAG_EVENT_REPORT(&sta_data_stall, EVENT_WLAN_STA_DATASTALL); +} +#else +static inline void hdd_data_stall_send_event(uint32_t reason) +{ +} +#endif + +/** + * hdd_data_stall_process_event() - Process data stall event + * @message: data stall message + * + * Process data stall message + * + * Return: void + */ +static void hdd_data_stall_process_event( + struct data_stall_event_info *data_stall_info) +{ + hdd_data_stall_send_event(data_stall_info->data_stall_type); +} + +/** + * hdd_data_stall_process_cb() - Process data stall message + * @message: data stall message + * + * Process data stall message + * + * Return: void + */ +static void hdd_data_stall_process_cb( + struct data_stall_event_info *info) +{ + struct scheduler_msg msg = {0}; + struct data_stall_event_info *data_stall_event_info; + QDF_STATUS status; + + data_stall_event_info = qdf_mem_malloc(sizeof(*data_stall_event_info)); + if (!data_stall_event_info) + return; + + data_stall_event_info->data_stall_type = info->data_stall_type; + data_stall_event_info->indicator = info->indicator; + data_stall_event_info->pdev_id = info->pdev_id; + data_stall_event_info->recovery_type = info->recovery_type; + data_stall_event_info->vdev_id_bitmap = info->vdev_id_bitmap; + + sys_build_message_header(SYS_MSG_ID_DATA_STALL_MSG, &msg); + /* Save callback and data */ + msg.callback = hdd_data_stall_process_event; + msg.bodyptr = data_stall_event_info; + msg.bodyval = 0; + + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_HDD, + QDF_MODULE_ID_SYS, &msg); + + if (status != QDF_STATUS_SUCCESS) + qdf_mem_free(data_stall_event_info); +} + +int hdd_register_data_stall_detect_cb(void) +{ + QDF_STATUS status; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + /* Register the data stall callback */ + hdd_debug("Register data stall detect callback"); + status = cdp_data_stall_cb_register(soc, OL_TXRX_PDEV_ID, + hdd_data_stall_process_cb); + return qdf_status_to_os_return(status); +} + +int hdd_deregister_data_stall_detect_cb(void) +{ + QDF_STATUS status; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + /* De-Register the data stall callback */ + hdd_debug("De-Register data stall detect callback"); + status = cdp_data_stall_cb_deregister(soc, OL_TXRX_PDEV_ID, + hdd_data_stall_process_cb); + return qdf_status_to_os_return(status); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs.c new file mode 100644 index 0000000000000000000000000000000000000000..c7f369c3356f7f07d464d690f0f92bf723603b8f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs.c @@ -0,0 +1,571 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs.c + * + * This driver currently supports the following debugfs files: + * wlan_wcnss/wow_enable to enable/disable WoWL. + * wlan_wcnss/wow_pattern to configure WoWL patterns. + * wlan_wcnss/pattern_gen to configure periodic TX patterns. + */ + +#ifdef WLAN_OPEN_SOURCE +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include + +#define MAX_USER_COMMAND_SIZE_WOWL_ENABLE 8 +#define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512 +#define MAX_USER_COMMAND_SIZE_FRAME 4096 + +#define MAX_DEBUGFS_WAIT_ITERATIONS 20 +#define DEBUGFS_WAIT_SLEEP_TIME 100 + +static bool hdd_periodic_pattern_map[MAXNUM_PERIODIC_TX_PTRNS]; + +static qdf_atomic_t debugfs_thread_count; + +void hdd_debugfs_thread_increment(void) +{ + qdf_atomic_inc(&debugfs_thread_count); +} + +void hdd_debugfs_thread_decrement(void) +{ + qdf_atomic_dec(&debugfs_thread_count); +} + +int hdd_return_debugfs_threads_count(void) +{ + return qdf_atomic_read(&debugfs_thread_count); +} + +bool hdd_wait_for_debugfs_threads_completion(void) +{ + int count = MAX_DEBUGFS_WAIT_ITERATIONS; + int r; + + while (count) { + r = hdd_return_debugfs_threads_count(); + if (!r) + break; + + if (--count) { + hdd_debug("Waiting for %d debugfs threads to exit", r); + qdf_sleep(DEBUGFS_WAIT_SLEEP_TIME); + } + } + + /* at least one debugfs thread is executing */ + if (!count) { + hdd_err("Timed-out waiting for debugfs threads"); + return false; + } + + return true; +} + +/** + * __wcnss_wowpattern_write() - wow_pattern debugfs handler + * @net_dev: net_device context used to register the debugfs file + * @buf: text being written to the debugfs + * @count: size of @buf + * @ppos: (unused) offset into the virtual file system + * + * Return: number of bytes processed + */ +static ssize_t __wcnss_wowpattern_write(struct net_device *net_dev, + const char __user *buf, size_t count, + loff_t *ppos) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + char cmd[MAX_USER_COMMAND_SIZE_WOWL_PATTERN + 1]; + char *sptr, *token; + uint8_t pattern_idx = 0; + uint8_t pattern_offset = 0; + char *pattern_buf; + char *pattern_mask; + int ret; + + hdd_enter(); + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + if (!wlan_hdd_validate_modules_state(hdd_ctx)) + return -EINVAL; + + if (!sme_is_feature_supported_by_fw(WOW)) { + hdd_err("Wake-on-Wireless feature is not supported in firmware!"); + return -EINVAL; + } + + if (count > MAX_USER_COMMAND_SIZE_WOWL_PATTERN) { + hdd_err("Command length is larger than %d bytes", + MAX_USER_COMMAND_SIZE_WOWL_PATTERN); + return -EINVAL; + } + + /* Get command from user */ + if (copy_from_user(cmd, buf, count)) + return -EFAULT; + cmd[count] = '\0'; + sptr = cmd; + + /* Get pattern idx */ + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + + if (kstrtou8(token, 0, &pattern_idx)) + return -EINVAL; + + /* Get pattern offset */ + token = strsep(&sptr, " "); + + /* Delete pattern if no further argument */ + if (!token) { + hdd_del_wowl_ptrn_debugfs(adapter, pattern_idx); + + return count; + } + + if (kstrtou8(token, 0, &pattern_offset)) + return -EINVAL; + + /* Get pattern */ + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + + pattern_buf = token; + + /* Get pattern mask */ + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + + pattern_mask = token; + pattern_mask[strlen(pattern_mask) - 1] = '\0'; + + hdd_add_wowl_ptrn_debugfs(adapter, pattern_idx, pattern_offset, + pattern_buf, pattern_mask); + hdd_exit(); + return count; +} + +/** + * wcnss_wowpattern_write() - SSR wrapper for __wcnss_wowpattern_write + * @file: file pointer + * @buf: buffer + * @count: count + * @ppos: position pointer + * + * Return: 0 on success, error number otherwise + */ +static ssize_t wcnss_wowpattern_write(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct net_device *net_dev = file_inode(file)->i_private; + struct osif_vdev_sync *vdev_sync; + ssize_t err_size; + + err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (err_size) + return err_size; + + err_size = __wcnss_wowpattern_write(net_dev, buf, count, ppos); + + osif_vdev_sync_op_stop(vdev_sync); + + return err_size; +} + +/** + * wcnss_patterngen_write() - pattern_gen debugfs handler + * @net_dev: net_device context used to register the debugfs file + * @buf: text being written to the debugfs + * @count: size of @buf + * @ppos: (unused) offset into the virtual file system + * + * Return: number of bytes processed + */ +static ssize_t __wcnss_patterngen_write(struct net_device *net_dev, + const char __user *buf, size_t count, + loff_t *ppos) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + tSirAddPeriodicTxPtrn *addPeriodicTxPtrnParams; + tSirDelPeriodicTxPtrn *delPeriodicTxPtrnParams; + + char *cmd, *sptr, *token; + uint8_t pattern_idx = 0; + uint8_t pattern_duration = 0; + char *pattern_buf; + uint16_t pattern_len = 0; + uint16_t i = 0; + QDF_STATUS status; + int ret; + + hdd_enter(); + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + if (!wlan_hdd_validate_modules_state(hdd_ctx)) + return -EINVAL; + + if (!sme_is_feature_supported_by_fw(WLAN_PERIODIC_TX_PTRN)) { + hdd_err("Periodic Tx Pattern Offload feature is not supported in firmware!"); + return -EINVAL; + } + + /* Get command from user */ + if (count <= MAX_USER_COMMAND_SIZE_FRAME) + cmd = qdf_mem_malloc(count + 1); + else { + hdd_err("Command length is larger than %d bytes", + MAX_USER_COMMAND_SIZE_FRAME); + + return -EINVAL; + } + + if (!cmd) { + hdd_err("Memory allocation for cmd failed!"); + return -ENOMEM; + } + + if (copy_from_user(cmd, buf, count)) { + qdf_mem_free(cmd); + return -EFAULT; + } + cmd[count] = '\0'; + sptr = cmd; + + /* Get pattern idx */ + token = strsep(&sptr, " "); + if (!token) + goto failure; + if (kstrtou8(token, 0, &pattern_idx)) + goto failure; + + if (pattern_idx > (MAXNUM_PERIODIC_TX_PTRNS - 1)) { + hdd_err("Pattern index: %d is not in the range (0 ~ %d)", + pattern_idx, MAXNUM_PERIODIC_TX_PTRNS - 1); + + goto failure; + } + + /* Get pattern duration */ + token = strsep(&sptr, " "); + if (!token) + goto failure; + if (kstrtou8(token, 0, &pattern_duration)) + goto failure; + + /* Delete pattern using index if duration is 0 */ + if (!pattern_duration) { + if (!hdd_periodic_pattern_map[pattern_idx]) { + hdd_debug_rl("WoW pattern %d is not in the table.", + pattern_idx); + + qdf_mem_free(cmd); + return -EINVAL; + } + + delPeriodicTxPtrnParams = + qdf_mem_malloc(sizeof(tSirDelPeriodicTxPtrn)); + if (!delPeriodicTxPtrnParams) { + hdd_err("Memory allocation failed!"); + qdf_mem_free(cmd); + return -ENOMEM; + } + + delPeriodicTxPtrnParams->ucPtrnId = pattern_idx; + qdf_copy_macaddr(&delPeriodicTxPtrnParams->mac_address, + &adapter->mac_addr); + + /* Delete pattern */ + status = sme_del_periodic_tx_ptrn(hdd_ctx->mac_handle, + delPeriodicTxPtrnParams); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("sme_del_periodic_tx_ptrn() failed!"); + + qdf_mem_free(delPeriodicTxPtrnParams); + goto failure; + } + + hdd_periodic_pattern_map[pattern_idx] = false; + + qdf_mem_free(cmd); + qdf_mem_free(delPeriodicTxPtrnParams); + return count; + } + + /* + * In SAP mode allow configuration without any connection check + * In STA mode check if it's in connected state before adding + * patterns + */ + hdd_debug("device mode %d", adapter->device_mode); + if ((QDF_STA_MODE == adapter->device_mode) && + (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter)))) { + hdd_err("Not in Connected state!"); + goto failure; + } + + /* Get pattern */ + token = strsep(&sptr, " "); + if (!token) + goto failure; + + pattern_buf = token; + pattern_buf[strlen(pattern_buf) - 1] = '\0'; + pattern_len = strlen(pattern_buf); + + /* Since the pattern is a hex string, 2 characters represent 1 byte. */ + if (pattern_len % 2) { + hdd_err("Malformed pattern!"); + + goto failure; + } else + pattern_len >>= 1; + + if (pattern_len < 14 || pattern_len > PERIODIC_TX_PTRN_MAX_SIZE) { + hdd_err("Not an 802.3 frame!"); + + goto failure; + } + + addPeriodicTxPtrnParams = qdf_mem_malloc(sizeof(tSirAddPeriodicTxPtrn)); + if (!addPeriodicTxPtrnParams) { + hdd_err("Memory allocation failed!"); + qdf_mem_free(cmd); + return -ENOMEM; + } + + addPeriodicTxPtrnParams->ucPtrnId = pattern_idx; + addPeriodicTxPtrnParams->usPtrnIntervalMs = pattern_duration * 500; + addPeriodicTxPtrnParams->ucPtrnSize = pattern_len; + qdf_copy_macaddr(&addPeriodicTxPtrnParams->mac_address, + &adapter->mac_addr); + + /* Extract the pattern */ + for (i = 0; i < addPeriodicTxPtrnParams->ucPtrnSize; i++) { + addPeriodicTxPtrnParams->ucPattern[i] = + (hex_to_bin(pattern_buf[0]) << 4) + + hex_to_bin(pattern_buf[1]); + + /* Skip to next byte */ + pattern_buf += 2; + } + + /* Add pattern */ + status = sme_add_periodic_tx_ptrn(hdd_ctx->mac_handle, + addPeriodicTxPtrnParams); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("sme_add_periodic_tx_ptrn() failed!"); + + qdf_mem_free(addPeriodicTxPtrnParams); + goto failure; + } + + if (!hdd_periodic_pattern_map[pattern_idx]) + hdd_periodic_pattern_map[pattern_idx] = true; + + qdf_mem_free(cmd); + qdf_mem_free(addPeriodicTxPtrnParams); + hdd_exit(); + return count; + +failure: + hdd_err("Invalid input. Input format is: ptrn_idx duration pattern"); + qdf_mem_free(cmd); + return -EINVAL; +} + +/** + * wcnss_patterngen_write() - SSR wrapper for __wcnss_patterngen_write + * @file: file pointer + * @buf: buffer + * @count: count + * @ppos: position pointer + * + * Return: 0 on success, error number otherwise + */ +static ssize_t wcnss_patterngen_write(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct net_device *net_dev = file_inode(file)->i_private; + struct osif_vdev_sync *vdev_sync; + ssize_t err_size; + + err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (err_size) + return err_size; + + err_size = __wcnss_patterngen_write(net_dev, buf, count, ppos); + + osif_vdev_sync_op_stop(vdev_sync); + + return err_size; +} + +/** + * __wcnss_debugfs_open() - Generic debugfs open() handler + * @net_dev: net_device context used to register the debugfs file + * + * Return: Errno + */ +static int __wcnss_debugfs_open(struct net_device *net_dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + int errno; + + hdd_enter(); + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + + hdd_exit(); + + return errno; +} + +/** + * wcnss_debugfs_open() - SSR wrapper for __wcnss_debugfs_open + * @inode: inode pointer + * @file: file pointer + * + * Return: 0 on success, error number otherwise + */ +static int wcnss_debugfs_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = inode->i_private; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __wcnss_debugfs_open(net_dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static const struct file_operations fops_wowpattern = { + .write = wcnss_wowpattern_write, + .open = wcnss_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static const struct file_operations fops_patterngen = { + .write = wcnss_patterngen_write, + .open = wcnss_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/** + * hdd_debugfs_init() - Initialize debugfs interface + * @adapter: interface adapter pointer + * + * Register support for the debugfs files supported by the driver. + * + * NB: The current implementation only supports debugfs operations + * on the primary interface, i.e. wlan0 + * + * Return: QDF_STATUS_SUCCESS if all files registered, + * QDF_STATUS_E_FAILURE on failure + */ +QDF_STATUS hdd_debugfs_init(struct hdd_adapter *adapter) +{ + struct net_device *net_dev = adapter->dev; + + adapter->debugfs_phy = debugfs_create_dir(net_dev->name, 0); + + if (!adapter->debugfs_phy) + return QDF_STATUS_E_FAILURE; + + if (!debugfs_create_file("wow_pattern", 00400 | 00200, + adapter->debugfs_phy, net_dev, + &fops_wowpattern)) + return QDF_STATUS_E_FAILURE; + + if (!debugfs_create_file("pattern_gen", 00400 | 00200, + adapter->debugfs_phy, net_dev, + &fops_patterngen)) + return QDF_STATUS_E_FAILURE; + + if (wlan_hdd_create_mib_stats_file(adapter)) + return QDF_STATUS_E_FAILURE; + + if (wlan_hdd_create_ll_stats_file(adapter)) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_debugfs_exit() - Shutdown debugfs interface + * @adapter: interface adapter pointer + * + * Unregister support for the debugfs files supported by the driver. + * + * Return: None + */ +void hdd_debugfs_exit(struct hdd_adapter *adapter) +{ + debugfs_remove_recursive(adapter->debugfs_phy); + wlan_hdd_destroy_mib_stats_lock(); +} +#endif /* #ifdef WLAN_OPEN_SOURCE */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_coex.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_coex.c new file mode 100644 index 0000000000000000000000000000000000000000..930272c63505c9ef9b18fb019edeb1ce725b7aad --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_coex.c @@ -0,0 +1,668 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_coex.c + * + * This file currently supports the following debugfs: + * Get the following information coex information + * COEX STATE + * COEX DPWB STATE + * COEX TDM STATE + * COEX IDRX STATE + * COEX ANTENNA SHARING STATE + * + * Example to read the COEX STATE logging: + * sm6150:/ # cat /sys/kernel/debug/wlan/mws_coex_state + */ + +#include +#include +#include +#include "wmi_unified.h" +#include "wmi_unified_param.h" +#include "osif_sync.h" + +#define MWS_DEBUGFS_PERMS (QDF_FILE_USR_READ | \ + QDF_FILE_GRP_READ | \ + QDF_FILE_OTH_READ) + +/* wait time for mws coex info in milliseconds */ +#define WLAN_WAIT_TIME_MWS_COEX_INFO 800 + +struct mws_coex_state_priv { + struct mws_coex_state coex_state; +}; + +struct mws_coex_dpwb_state_priv { + struct mws_coex_dpwb_state dpwb_state; +}; + +struct mws_coex_tdm_state_priv { + struct mws_coex_tdm_state tdm_state; +}; + +struct mws_coex_idrx_state_priv { + struct mws_coex_idrx_state idrx_state; +}; + +struct mws_antenna_sharing_info_priv { + struct mws_antenna_sharing_info antenna_sharing; +}; + +static void hdd_debugfs_mws_coex_info_cb(void *coex_info_data, void *context, + wmi_mws_coex_cmd_id cmd_id) +{ + struct osif_request *request = NULL; + struct mws_coex_state_priv *coex_priv; + struct mws_coex_dpwb_state_priv *dpwb_priv; + struct mws_coex_tdm_state_priv *tdm_priv; + struct mws_coex_idrx_state_priv *idrx_priv; + struct mws_antenna_sharing_info_priv *antenna_priv; + + if (!coex_info_data) { + hdd_err("data is null"); + return; + } + + request = osif_request_get(context); + if (!request) { + hdd_err("obselete request"); + return; + } + + switch (cmd_id) { + case WMI_MWS_COEX_STATE: + coex_priv = osif_request_priv(request); + qdf_mem_copy(&coex_priv->coex_state, coex_info_data, + sizeof(struct mws_coex_state)); + break; + case WMI_MWS_COEX_DPWB_STATE: + dpwb_priv = osif_request_priv(request); + qdf_mem_copy(&dpwb_priv->dpwb_state, coex_info_data, + sizeof(struct mws_coex_dpwb_state)); + break; + case WMI_MWS_COEX_TDM_STATE: + tdm_priv = osif_request_priv(request); + qdf_mem_copy(&tdm_priv->tdm_state, coex_info_data, + sizeof(struct mws_coex_tdm_state)); + break; + case WMI_MWS_COEX_IDRX_STATE: + idrx_priv = osif_request_priv(request); + qdf_mem_copy(&idrx_priv->idrx_state, coex_info_data, + sizeof(struct mws_coex_idrx_state)); + break; + case WMI_MWS_COEX_ANTENNA_SHARING_STATE: + antenna_priv = osif_request_priv(request); + qdf_mem_copy(&antenna_priv->antenna_sharing, coex_info_data, + sizeof(struct mws_antenna_sharing_info)); + break; + } + + osif_request_complete(request); + osif_request_put(request); +} + +static QDF_STATUS __hdd_debugfs_mws_coex_state_read(struct hdd_context *hdd_ctx, + qdf_debugfs_file_t file) +{ + struct hdd_adapter *adapter; + QDF_STATUS status; + int rc; + struct osif_request *request; + struct mws_coex_state *coex_state; + struct mws_coex_state_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_MWS_COEX_INFO, + }; + void *cookie; + + adapter = hdd_get_first_valid_adapter(hdd_ctx); + if (!adapter) { + hdd_err("Failed to get adapter"); + return QDF_STATUS_E_INVAL; + } + + if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { + hdd_err("Interface is not enabled"); + return QDF_STATUS_E_INVAL; + } + + if (file->index > 0) + return QDF_STATUS_SUCCESS; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return QDF_STATUS_E_NOMEM; + } + + cookie = osif_request_cookie(request); + + status = sme_get_mws_coex_info(hdd_ctx->mac_handle, + adapter->vdev_id, WMI_MWS_COEX_STATE, + hdd_debugfs_mws_coex_info_cb, cookie); + + if (QDF_IS_STATUS_ERROR(status)) { + rc = qdf_status_to_os_return(status); + goto exit; + } + + rc = osif_request_wait_for_response(request); + if (rc) { + qdf_debugfs_printf(file, "Timedout while retrieving MWS coex state"); + rc = -ETIMEDOUT; + goto exit; + } + + priv = osif_request_priv(request); + coex_state = &priv->coex_state; + + qdf_debugfs_printf(file, "vdev_id = %u\n" + "coex_scheme_bitmap = %u\n" + "active_conflict_count = %u\n" + "potential_conflict_count = %u\n" + "chavd_group0_bitmap = %u\n" + "chavd_group1_bitmap = %u\n" + "chavd_group2_bitmap = %u\n" + "chavd_group3_bitmap = %u\n", + coex_state->vdev_id, + coex_state->coex_scheme_bitmap, + coex_state->active_conflict_count, + coex_state->potential_conflict_count, + coex_state->chavd_group0_bitmap, + coex_state->chavd_group1_bitmap, + coex_state->chavd_group2_bitmap, + coex_state->chavd_group3_bitmap); + +exit: + osif_request_put(request); + return qdf_status_from_os_return(rc); + } + +static QDF_STATUS hdd_debugfs_mws_coex_state_read(qdf_debugfs_file_t file, + void *arg) +{ + struct osif_psoc_sync *psoc_sync; + struct hdd_context *hdd_ctx = arg; + int ret; + QDF_STATUS status; + + ret = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), &psoc_sync); + if (ret) + return qdf_status_from_os_return(ret); + + status = __hdd_debugfs_mws_coex_state_read(hdd_ctx, file); + + osif_psoc_sync_op_stop(psoc_sync); + return qdf_status_from_os_return(ret); +} + +static QDF_STATUS __hdd_debugfs_mws_coex_dpwb_read(struct hdd_context *hdd_ctx, + qdf_debugfs_file_t file) + { + struct hdd_adapter *adapter; + QDF_STATUS status; + int rc; + struct osif_request *request; + struct mws_coex_dpwb_state *dpwb_state; + struct mws_coex_dpwb_state_priv *dpwb_priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*dpwb_priv), + .timeout_ms = WLAN_WAIT_TIME_MWS_COEX_INFO, + }; + void *cookie; + + adapter = hdd_get_first_valid_adapter(hdd_ctx); + if (!adapter) { + hdd_err("Failed to get adapter"); + return QDF_STATUS_E_INVAL; + } + + if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { + hdd_err("Interface is not enabled"); + return QDF_STATUS_E_INVAL; + } + + if (file->index > 0) + return QDF_STATUS_SUCCESS; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return QDF_STATUS_E_NOMEM; + } + + cookie = osif_request_cookie(request); + + status = sme_get_mws_coex_info(hdd_ctx->mac_handle, + adapter->vdev_id, + WMI_MWS_COEX_DPWB_STATE, + hdd_debugfs_mws_coex_info_cb, cookie); + + if (QDF_IS_STATUS_ERROR(status)) { + rc = qdf_status_to_os_return(status); + goto exit; + } + + rc = osif_request_wait_for_response(request); + if (rc) { + qdf_debugfs_printf(file, "Timedout while retrieving MWS coex dpwb state"); + rc = -ETIMEDOUT; + goto exit; + } + + dpwb_priv = osif_request_priv(request); + dpwb_state = &dpwb_priv->dpwb_state; + qdf_debugfs_printf(file, "vdev_id = %u\n" + "current_dpwb_state = %d\n" + "pnp1_value = %d\n" + "lte_dutycycle = %d\n" + "sinr_wlan_on = %d\n" + "bler_count = %u\n" + "block_count = %u\n" + "wlan_rssi_level = %u\n" + "wlan_rssi = %d\n" + "is_tdm_running = %u\n", + dpwb_state->vdev_id, + dpwb_state->current_dpwb_state, + dpwb_state->pnp1_value, + dpwb_state->lte_dutycycle, + dpwb_state->sinr_wlan_on, + dpwb_state->sinr_wlan_off, + dpwb_state->bler_count, + dpwb_state->block_count, + dpwb_state->wlan_rssi_level, + dpwb_state->wlan_rssi, + dpwb_state->is_tdm_running); + +exit: + osif_request_put(request); + return qdf_status_from_os_return(rc); +} + +static QDF_STATUS hdd_debugfs_mws_coex_dpwb_read(qdf_debugfs_file_t file, + void *arg) +{ + struct osif_psoc_sync *psoc_sync; + struct hdd_context *hdd_ctx = arg; + int ret; + QDF_STATUS status; + + ret = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), &psoc_sync); + if (ret) + return qdf_status_from_os_return(ret); + + status = __hdd_debugfs_mws_coex_dpwb_read(hdd_ctx, file); + + osif_psoc_sync_op_stop(psoc_sync); + return qdf_status_from_os_return(ret); +} + +static QDF_STATUS __hdd_debugfs_mws_tdm_state_read(struct hdd_context *hdd_ctx, + qdf_debugfs_file_t file) +{ + struct hdd_adapter *adapter; + QDF_STATUS status; + int rc; + struct mws_coex_tdm_state *tdm_state; + struct mws_coex_tdm_state_priv *tdm_priv; + struct osif_request *request; + static const struct osif_request_params params = { + .priv_size = sizeof(*tdm_priv), + .timeout_ms = WLAN_WAIT_TIME_MWS_COEX_INFO, + }; + void *cookie; + + adapter = hdd_get_first_valid_adapter(hdd_ctx); + if (!adapter) { + hdd_err("Failed to get adapter"); + return QDF_STATUS_E_INVAL; + } + + if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { + hdd_err("Interface is not enabled"); + return QDF_STATUS_E_INVAL; + } + + if (file->index > 0) + return QDF_STATUS_SUCCESS; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return QDF_STATUS_E_NOMEM; + } + + cookie = osif_request_cookie(request); + + status = sme_get_mws_coex_info(hdd_ctx->mac_handle, + adapter->vdev_id, WMI_MWS_COEX_TDM_STATE, + hdd_debugfs_mws_coex_info_cb, cookie); + + if (QDF_IS_STATUS_ERROR(status)) { + rc = qdf_status_to_os_return(status); + goto exit; + } + + rc = osif_request_wait_for_response(request); + if (rc) { + qdf_debugfs_printf(file, "Timedout while retrieving MWS coex tdm state"); + rc = -ETIMEDOUT; + goto exit; + } + + tdm_priv = osif_request_priv(request); + tdm_state = &tdm_priv->tdm_state; + + qdf_debugfs_printf(file, "vdev_id = %u\n" + "tdm_policy_bitmap = %u\n" + "tdm_sf_bitmap = %u\n", + tdm_state->vdev_id, + tdm_state->tdm_policy_bitmap, + tdm_state->tdm_sf_bitmap); + +exit: + osif_request_put(request); + return qdf_status_from_os_return(rc); +} + +static QDF_STATUS hdd_debugfs_mws_tdm_state_read(qdf_debugfs_file_t file, + void *arg) +{ + struct osif_psoc_sync *psoc_sync; + struct hdd_context *hdd_ctx = arg; + int ret; + QDF_STATUS status; + + ret = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), &psoc_sync); + if (ret) + return qdf_status_from_os_return(ret); + + status = __hdd_debugfs_mws_tdm_state_read(hdd_ctx, file); + + osif_psoc_sync_op_stop(psoc_sync); + return qdf_status_from_os_return(ret); +} + +static QDF_STATUS __hdd_debugfs_mws_coex_idrx_read(struct hdd_context *hdd_ctx, + qdf_debugfs_file_t file) +{ + struct hdd_adapter *adapter; + QDF_STATUS status; + int rc; + struct osif_request *request; + struct mws_coex_idrx_state_priv *idrx_priv; + struct mws_coex_idrx_state *idrx_state; + static const struct osif_request_params params = { + .priv_size = sizeof(*idrx_priv), + .timeout_ms = WLAN_WAIT_TIME_MWS_COEX_INFO, + }; + void *cookie; + + adapter = hdd_get_first_valid_adapter(hdd_ctx); + if (!adapter) { + hdd_err("Failed to get adapter"); + return QDF_STATUS_E_INVAL; + } + + if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { + hdd_err("Interface is not enabled"); + return QDF_STATUS_E_INVAL; + } + + if (file->index > 0) + return QDF_STATUS_SUCCESS; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return QDF_STATUS_E_NOMEM; + } + + cookie = osif_request_cookie(request); + + status = sme_get_mws_coex_info(hdd_ctx->mac_handle, + adapter->vdev_id, + WMI_MWS_COEX_IDRX_STATE, + hdd_debugfs_mws_coex_info_cb, cookie); + + if (QDF_IS_STATUS_ERROR(status)) { + rc = qdf_status_to_os_return(status); + goto exit; + } + + rc = osif_request_wait_for_response(request); + if (rc) { + qdf_debugfs_printf(file, "Timedout while retrieving MWS coex idrx state"); + rc = -ETIMEDOUT; + goto exit; + } + + idrx_priv = osif_request_priv(request); + idrx_state = &idrx_priv->idrx_state; + qdf_debugfs_printf(file, "vdev_id = %u\n" + "sub0_techid = %u\n" + "sub0_policy = %u\n" + "sub0_is_link_critical = %u\n" + "sub0_static_power = %u\n" + "sub0_rssi = %d\n" + "sub1_techid = %d\n" + "sub1_policy = %d\n" + "sub1_is_link_critical = %d\n" + "sub1_static_power = %u\n" + "sub1_rssi = %d\n", + idrx_state->vdev_id, + idrx_state->sub0_techid, + idrx_state->sub0_policy, + idrx_state->sub0_is_link_critical, + idrx_state->sub0_static_power, + idrx_state->sub0_rssi, + idrx_state->sub1_techid, + idrx_state->sub1_policy, + idrx_state->sub1_is_link_critical, + idrx_state->sub1_static_power, + idrx_state->sub1_rssi); + +exit: + osif_request_put(request); + return qdf_status_from_os_return(rc); +} + +static QDF_STATUS hdd_debugfs_mws_coex_idrx_read(qdf_debugfs_file_t file, + void *arg) +{ + struct osif_psoc_sync *psoc_sync; + struct hdd_context *hdd_ctx = arg; + int ret; + QDF_STATUS status; + + ret = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), &psoc_sync); + if (ret) + return qdf_status_from_os_return(ret); + + status = __hdd_debugfs_mws_coex_idrx_read(hdd_ctx, file); + + osif_psoc_sync_op_stop(psoc_sync); + return qdf_status_from_os_return(ret); +} + +static QDF_STATUS __hdd_debugfs_mws_antenna_sharing_read(struct hdd_context + *hdd_ctx, + qdf_debugfs_file_t + file) +{ + struct hdd_adapter *adapter; + QDF_STATUS status; + int rc; + struct mws_antenna_sharing_info *antenna_sharing; + struct mws_antenna_sharing_info_priv *antenna_priv; + struct osif_request *request; + static const struct osif_request_params params = { + .priv_size = sizeof(*antenna_priv), + .timeout_ms = WLAN_WAIT_TIME_MWS_COEX_INFO, + }; + void *cookie; + + adapter = hdd_get_first_valid_adapter(hdd_ctx); + if (!adapter) { + hdd_err("Failed to get adapter"); + return QDF_STATUS_E_INVAL; + } + + if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { + hdd_err("Interface is not enabled"); + return QDF_STATUS_E_INVAL; + } + + if (file->index > 0) + return QDF_STATUS_SUCCESS; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return QDF_STATUS_E_NOMEM; + } + + cookie = osif_request_cookie(request); + + status = sme_get_mws_coex_info(hdd_ctx->mac_handle, + adapter->vdev_id, + WMI_MWS_COEX_ANTENNA_SHARING_STATE, + hdd_debugfs_mws_coex_info_cb, cookie); + + if (QDF_IS_STATUS_ERROR(status)) { + rc = qdf_status_to_os_return(status); + goto exit; + } + + rc = osif_request_wait_for_response(request); + if (rc) { + qdf_debugfs_printf(file, "Timedout while retrieving MWS coex antenna sharing state"); + rc = -ETIMEDOUT; + goto exit; + } + + antenna_priv = osif_request_priv(request); + antenna_sharing = &antenna_priv->antenna_sharing; + qdf_debugfs_printf(file, "vdev_id = %u\n" + "coex_flags = %u\n" + "coex_config = %u\n" + "tx_chain_mask = %u\n" + "rx_chain_mask = %u\n" + "rx_nss = %u\n" + "force_mrc = %u\n" + "rssi_type = %u\n" + "chain0_rssi = %d\n" + "chain1_rssi = %d\n" + "combined_rssi = %d\n" + "imbalance = %u\n" + "mrc_threshold = %d\n" + "grant_duration = %u\n", + antenna_sharing->vdev_id, + antenna_sharing->coex_flags, + antenna_sharing->coex_config, + antenna_sharing->tx_chain_mask, + antenna_sharing->rx_chain_mask, + antenna_sharing->rx_nss, + antenna_sharing->force_mrc, + antenna_sharing->rssi_type, + antenna_sharing->chain0_rssi, + antenna_sharing->chain1_rssi, + antenna_sharing->combined_rssi, + antenna_sharing->imbalance, + antenna_sharing->mrc_threshold, + antenna_sharing->grant_duration); + +exit: + osif_request_put(request); + return qdf_status_from_os_return(rc); +} + +static QDF_STATUS hdd_debugfs_mws_antenna_sharing_read(qdf_debugfs_file_t file, + void *arg) +{ + struct osif_psoc_sync *psoc_sync; + struct hdd_context *hdd_ctx = arg; + int ret; + QDF_STATUS status; + + ret = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), &psoc_sync); + if (ret) + return qdf_status_from_os_return(ret); + + status = __hdd_debugfs_mws_antenna_sharing_read(hdd_ctx, file); + + osif_psoc_sync_op_stop(psoc_sync); + return qdf_status_from_os_return(ret); +} + +static struct qdf_debugfs_fops hdd_mws_debugfs_coex_state_fops = { + .show = hdd_debugfs_mws_coex_state_read, +}; + +static struct qdf_debugfs_fops hdd_mws_debugfs_fops_coex_dpwb_fops = { + .show = hdd_debugfs_mws_coex_dpwb_read, +}; + +static struct qdf_debugfs_fops hdd_mws_debugfs_tdm_state_fpos = { + .show = hdd_debugfs_mws_tdm_state_read, +}; + +static struct qdf_debugfs_fops hdd_mws_debugfs_idrx_state_fpos = { + .show = hdd_debugfs_mws_coex_idrx_read, +}; + +static struct qdf_debugfs_fops hdd_mws_debugfs_antenna_sharing_fpos = { + .show = hdd_debugfs_mws_antenna_sharing_read, +}; + +void hdd_debugfs_mws_coex_info_init(struct hdd_context *hdd_ctx) +{ + hdd_mws_debugfs_coex_state_fops.priv = hdd_ctx; + hdd_mws_debugfs_fops_coex_dpwb_fops.priv = hdd_ctx; + hdd_mws_debugfs_tdm_state_fpos.priv = hdd_ctx; + hdd_mws_debugfs_idrx_state_fpos.priv = hdd_ctx; + hdd_mws_debugfs_antenna_sharing_fpos.priv = hdd_ctx; + if (!qdf_debugfs_create_file("mws_coex_state", MWS_DEBUGFS_PERMS, + NULL, &hdd_mws_debugfs_coex_state_fops)) + hdd_err("Failed to create the mws coex state file"); + if (!qdf_debugfs_create_file("mws_coex_dpwb_state", MWS_DEBUGFS_PERMS, + NULL, + &hdd_mws_debugfs_fops_coex_dpwb_fops)) + hdd_err("Failed to create the mws coex dpwb file"); + if (!qdf_debugfs_create_file("mws_coex_tdm_state", MWS_DEBUGFS_PERMS, + NULL, &hdd_mws_debugfs_tdm_state_fpos)) + hdd_err("Failed to create the mws coex tdm file"); + if (!qdf_debugfs_create_file("mws_coex_idrx", MWS_DEBUGFS_PERMS, + NULL, &hdd_mws_debugfs_idrx_state_fpos)) + hdd_err("Failed to create the mws coex idrx file"); + if (!qdf_debugfs_create_file("mws_coex_antenna_sharing", + MWS_DEBUGFS_PERMS, + NULL, + &hdd_mws_debugfs_antenna_sharing_fpos)) + hdd_err("Failed to create the mws coex antenna sharing file"); +} + +void hdd_debugfs_mws_coex_info_deinit(struct hdd_context *hdd_ctx) +{ + /** + * Coex info dosent have a directory it is removed as part of qdf remove + */ +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_config.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_config.c new file mode 100644 index 0000000000000000000000000000000000000000..2e57b0db686310f5dcae9b04e89a0a39191c5171 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_config.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_config.c + * + * WLAN Host Device Driver implementation to update + * debugfs with ini configs + */ + +#include "wlan_hdd_main.h" +#include "osif_psoc_sync.h" +#include "cfg_ucfg_api.h" +#include "wlan_hdd_debugfs_config.h" + +#define DEBUGFS_CONFIG_BUF_SIZE (4096 * 8) + +/** + * struct ini_config_buf - the buffer struct to save ini configs + * @len: buffer len + * @result: the pointer to buffer + */ +struct ini_config_buf { + ssize_t len; + uint8_t result[DEBUGFS_CONFIG_BUF_SIZE]; +}; + +/** + * wlan_hdd_config_update() - Update userspace with local statistics buffer + * @buf: userspace buffer (to which data is being copied into) + * @count: max data that can be copied into buf + * @pos: offset (where data should be copied into) + * @ini_config: buffer structure for ini config info + * + * This function should copies ini configs into debugfs entry. + * + * Return: number of characters copied; 0 on no-copy + */ +static ssize_t wlan_hdd_config_update(char __user *buf, size_t count, + loff_t *pos, + struct ini_config_buf *ini_config) +{ + ssize_t ret_cnt; + + ret_cnt = simple_read_from_buffer(buf, count, pos, ini_config->result, + ini_config->len); + hdd_debug("ini config read req: count: %zu, pos: %lld", count, *pos); + + return ret_cnt; +} + +/** + * wlan_hdd_config_get() - Function to save ini config to buffer + * @hdd_ctx: hdd context used to register the debugfs file + * @ini_config: buffer structure for ini config info + * + * Return: Errno + */ +static int wlan_hdd_config_get(struct hdd_context *hdd_ctx, + struct ini_config_buf *ini_config) +{ + QDF_STATUS status; + + status = ucfg_cfg_ini_config_print(hdd_ctx->psoc, ini_config->result, + &ini_config->len, + DEBUGFS_CONFIG_BUF_SIZE); + return qdf_status_to_os_return(status); +} + +/** + * __wlan_hdd_read_config_debugfs() - function to get ini conifg + * @file: file pointer + * @buf: buffer + * @count: count + * @pos: position pointer + * + * Return: Number of bytes read on success, error number otherwise + */ +static ssize_t __wlan_hdd_read_config_debugfs(struct file *file, + char __user *buf, size_t count, + loff_t *pos) +{ + struct ini_config_buf *ini_config; + ssize_t err_size; + + ini_config = (struct ini_config_buf *)file->private_data; + if (!ini_config) + return -ENOMEM; + + err_size = wlan_hdd_config_update(buf, count, pos, ini_config); + + return err_size; +} + +/** + * wlan_hdd_read_config_debugfs() - wrapper function to get ini conifg + * @file: file pointer + * @buf: buffer + * @count: count + * @pos: position pointer + * + * Return: Number of bytes read on success, error number otherwise + */ +static ssize_t wlan_hdd_read_config_debugfs(struct file *file, + char __user *buf, size_t count, + loff_t *pos) +{ + struct hdd_context *hdd_ctx = file_inode(file)->i_private; + struct osif_psoc_sync *psoc_sync; + ssize_t err_size; + + err_size = wlan_hdd_validate_context(hdd_ctx); + if (err_size) + return err_size; + + err_size = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), + &psoc_sync); + if (err_size) + return err_size; + + err_size = __wlan_hdd_read_config_debugfs(file, buf, count, pos); + + osif_psoc_sync_op_stop(psoc_sync); + + return err_size; +} + +/** + * __wlan_hdd_open_config_debugfs() - function to open config debugfs + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: Errno + */ +static int __wlan_hdd_open_config_debugfs(struct inode *inode, + struct file *file) +{ + struct hdd_context *hdd_ctx = file_inode(file)->i_private; + struct ini_config_buf *ini_config; + ssize_t errno; + void *buf; + + buf = qdf_mem_malloc(sizeof(*ini_config)); + if (!buf) + return -ENOMEM; + + ini_config = (struct ini_config_buf *)buf; + errno = wlan_hdd_config_get(hdd_ctx, ini_config); + if (errno) { + qdf_mem_free(buf); + return errno; + } + + file->private_data = buf; + return 0; +} + +/** + * wlan_hdd_open_config_debugfs() - wrapper function to open config debugfs + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: Errno + */ +static int wlan_hdd_open_config_debugfs(struct inode *inode, struct file *file) +{ + struct hdd_context *hdd_ctx = file_inode(file)->i_private; + struct osif_psoc_sync *psoc_sync; + ssize_t errno; + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), + &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_open_config_debugfs(inode, file); + + osif_psoc_sync_op_stop(psoc_sync); + return errno; +} + +/** + * wlan_hdd_release_config_debugfs() - wrapper to release + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: Errno + */ +static int wlan_hdd_release_config_debugfs(struct inode *inode, + struct file *file) +{ + qdf_mem_free(file->private_data); + file->private_data = NULL; + + return 0; +} + +static const struct file_operations fops_config_debugfs = { + .read = wlan_hdd_read_config_debugfs, + .open = wlan_hdd_open_config_debugfs, + .release = wlan_hdd_release_config_debugfs, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int hdd_debugfs_ini_config_init(struct hdd_context *hdd_ctx) +{ + if (!debugfs_create_file("ini_config", 0444, qdf_debugfs_get_root(), + hdd_ctx, &fops_config_debugfs)) + return -EINVAL; + + return 0; +} + +void hdd_debugfs_ini_config_deinit(struct hdd_context *hdd_ctx) +{ + /* + * Config ini doesn't have a directory it is removed + * as part of qdf remove + */ +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_connect.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_connect.c new file mode 100644 index 0000000000000000000000000000000000000000..8367be50dc7e557fcb1580f6afc2bbcf39afe541 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_connect.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_connect.c + * + * WLAN Host Device Driver implementation to update + * debugfs with connect information + */ + +#include +#include +#include +#include +#include "qwlan_version.h" +#include "wmi_unified_param.h" + +/** + * wlan_hdd_version_info_debugfs() - Populate driver, FW and HW version + * @hdd_ctx: pointer to hdd context + * @buf: output buffer to hold version info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_version_info_debugfs(struct hdd_context *hdd_ctx, uint8_t *buf, + ssize_t buf_avail_len) +{ + ssize_t length = 0; + int ret_val; + + ret_val = scnprintf(buf, buf_avail_len, + "\nVERSION DETAILS\n"); + if (ret_val <= 0) + return length; + length += ret_val; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + + ret_val = scnprintf(buf + length, buf_avail_len - length, + "Host Driver Version: %s\n" + "Firmware Version: %d.%d.%d.%d.%d.%d\n" + "Hardware Version: %s\n", + QWLAN_VERSIONSTR, + hdd_ctx->fw_version_info.major_spid, + hdd_ctx->fw_version_info.minor_spid, + hdd_ctx->fw_version_info.siid, + hdd_ctx->fw_version_info.rel_id, + hdd_ctx->fw_version_info.crmid, + hdd_ctx->fw_version_info.sub_id, + hdd_ctx->target_hw_name); + if (ret_val <= 0) + return length; + + length += ret_val; + + return length; +} + +/** + * wlan_hdd_add_nss_info() - Populate NSS info + * @conn_info: station connection information + * @buf: output buffer to hold version info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_add_nss_info(struct hdd_connection_info *conn_info, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t length = 0; + int ret_val; + + if (!conn_info->conn_flag.ht_present && + !conn_info->conn_flag.vht_present) + return length; + + ret_val = scnprintf(buf, buf_avail_len, + "nss = %u\n", + conn_info->txrate.nss); + if (ret_val <= 0) + return length; + + length = ret_val; + return length; +} + +/** + * wlan_hdd_add_ht_cap_info() - Populate HT info + * @conn_info: station connection information + * @buf: output buffer to hold version info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_add_ht_cap_info(struct hdd_connection_info *conn_info, + uint8_t *buf, ssize_t buf_avail_len) +{ + struct ieee80211_ht_cap *ht_caps; + ssize_t length = 0; + int ret; + + if (!conn_info->conn_flag.ht_present) + return length; + + ht_caps = &conn_info->ht_caps; + ret = scnprintf(buf, buf_avail_len, + "ht_cap_info = %x\n" + "ampdu_params_info = %x\n" + "extended_ht_cap_info = %x\n" + "tx_BF_cap_info = %x\n" + "antenna_selection_info = %x\n" + "ht_rx_higest = %x\n" + "ht_tx_params = %x\n", + ht_caps->cap_info, + ht_caps->ampdu_params_info, + ht_caps->extended_ht_cap_info, + ht_caps->tx_BF_cap_info, + ht_caps->antenna_selection_info, + ht_caps->mcs.rx_highest, + ht_caps->mcs.tx_params); + if (ret <= 0) + return length; + + length = ret; + return length; +} + +/** + * wlan_hdd_add_vht_cap_info() - Populate VHT info + * @conn_info: station connection information + * @buf: output buffer to hold version info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_add_vht_cap_info(struct hdd_connection_info *conn_info, + uint8_t *buf, ssize_t buf_avail_len) +{ + struct ieee80211_vht_cap *vht_caps; + ssize_t length = 0; + int ret; + + if (!conn_info->conn_flag.vht_present) + return length; + + vht_caps = &conn_info->vht_caps; + ret = scnprintf(buf, buf_avail_len, + "vht_cap_info = %x\n" + "rx_mcs_map = %x\n" + "rx_highest = %x\n" + "tx_mcs_map = %x\n" + "tx_highest = %x\n", + vht_caps->vht_cap_info, + vht_caps->supp_mcs.rx_mcs_map, + vht_caps->supp_mcs.rx_highest, + vht_caps->supp_mcs.tx_mcs_map, + vht_caps->supp_mcs.tx_highest); + if (ret <= 0) + return length; + + length = ret; + return length; +} + +/** + * hdd_auth_type_str() - Get string for enum csr auth type + * @auth_type: authentication id + * + * Return: Meaningful string for enum csr auth type + */ +static +uint8_t *hdd_auth_type_str(uint32_t auth_type) +{ + switch (auth_type) { + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + return "OPEN SYSTEM"; + case eCSR_AUTH_TYPE_SHARED_KEY: + return "SHARED KEY"; + case eCSR_AUTH_TYPE_WPA: + return "WPA"; + case eCSR_AUTH_TYPE_WPA_PSK: + return "WPA PSK"; + case eCSR_AUTH_TYPE_AUTOSWITCH: + return "AUTO SWITCH"; + case eCSR_AUTH_TYPE_WPA_NONE: + return "WPA NONE"; + case eCSR_AUTH_TYPE_RSN: + return "RSN"; + case eCSR_AUTH_TYPE_RSN_PSK: + return "RSN PSK"; + case eCSR_AUTH_TYPE_FT_RSN: + return "FT RSN"; + case eCSR_AUTH_TYPE_FT_RSN_PSK: + return "FT RSN PSK"; + case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE: + return "WAPI WAI CERTIFICATE"; + case eCSR_AUTH_TYPE_WAPI_WAI_PSK: + return "WAPI WAI PSK"; + case eCSR_AUTH_TYPE_CCKM_WPA: + return "CCKM WPA"; + case eCSR_AUTH_TYPE_CCKM_RSN: + return "CCKM RSN"; + case eCSR_AUTH_TYPE_RSN_PSK_SHA256: + return "RSN PSK SHA256"; + case eCSR_AUTH_TYPE_RSN_8021X_SHA256: + return "RSN 8021X SHA256"; + case eCSR_AUTH_TYPE_FT_SAE: + return "FT SAE"; + case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384: + return "FT Suite B SHA384"; + case eCSR_NUM_OF_SUPPORT_AUTH_TYPE: + return "NUM OF SUPPORT AUTH TYPE"; + case eCSR_AUTH_TYPE_FAILED: + return "FAILED"; + case eCSR_AUTH_TYPE_NONE: + return "NONE"; + } + + return "UNKNOWN"; +} + +/** + * hdd_dot11_mode_str() - Get string for enum csr dot11 mode + * @dot11mode: dot11 mode ID + * + * Return: Meaningful string for enum csr dot11 mode + */ +static +uint8_t *hdd_dot11_mode_str(uint32_t dot11mode) +{ + switch (dot11mode) { + case eCSR_CFG_DOT11_MODE_11A: + return "DOT11 MODE 11A"; + case eCSR_CFG_DOT11_MODE_11B: + return "DOT11 MODE 11B"; + case eCSR_CFG_DOT11_MODE_11G: + return "DOT11 MODE 11G"; + case eCSR_CFG_DOT11_MODE_11N: + return "DOT11 MODE 11N"; + case eCSR_CFG_DOT11_MODE_11AC: + return "DOT11 MODE 11AC"; + case eCSR_CFG_DOT11_MODE_AUTO: + return "DOT11 MODE AUTO"; + case eCSR_CFG_DOT11_MODE_ABG: + return "DOT11 MODE 11ABG"; + } + + return "UNKNOWN"; +} + +/** + * hdd_ch_width_str() - Get string for channel width + * @ch_width: channel width from connect info + * + * Return: User readable string for channel width + */ +static +uint8_t *hdd_ch_width_str(enum phy_ch_width ch_width) +{ + switch (ch_width) { + case CH_WIDTH_20MHZ: + return "20MHz"; + case CH_WIDTH_40MHZ: + return "40MHz"; + case CH_WIDTH_80MHZ: + return "80MHz"; + case CH_WIDTH_160MHZ: + return "160MHz"; + case CH_WIDTH_80P80MHZ: + return "(80 + 80)MHz"; + case CH_WIDTH_5MHZ: + return "5MHz"; + case CH_WIDTH_10MHZ: + return "10MHz"; + case CH_WIDTH_INVALID: + /* Fallthrough */ + case CH_WIDTH_MAX: + /* Fallthrough */ + default: + return "UNKNOWN"; + } +} + +/** + * wlan_hdd_connect_info_debugfs() - Populate connect info + * @adapter: pointer to sta adapter for which connect info is required + * @buf: output buffer to hold version info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_connect_info_debugfs(struct hdd_adapter *adapter, uint8_t *buf, + ssize_t buf_avail_len) +{ + ssize_t length = 0; + struct hdd_station_ctx *hdd_sta_ctx; + struct hdd_connection_info *conn_info; + uint32_t tx_bit_rate, rx_bit_rate; + int ret_val; + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (hdd_sta_ctx->conn_info.conn_state != eConnectionState_Associated) { + ret_val = scnprintf(buf, buf_avail_len, + "\nSTA is not connected\n"); + if (ret_val >= 0) + length = ret_val; + return length; + } + + ret_val = scnprintf(buf, buf_avail_len, + "\nCONNECTION DETAILS\n"); + if (ret_val <= 0) + return length; + length += ret_val; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + + if (hdd_sta_ctx->hdd_reassoc_scenario) { + ret_val = scnprintf(buf + length, buf_avail_len - length, + "Roaming is in progress"); + if (ret_val <= 0) + return length; + length += ret_val; + } + + conn_info = &hdd_sta_ctx->conn_info; + tx_bit_rate = cfg80211_calculate_bitrate(&conn_info->txrate); + rx_bit_rate = cfg80211_calculate_bitrate(&conn_info->rxrate); + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + ret_val = scnprintf(buf + length, buf_avail_len - length, + "ssid = %s\n" + "bssid = " QDF_FULL_MAC_FMT "\n" + "connect_time = %s\n" + "auth_time = %s\n" + "freq = %u\n" + "ch_width = %s\n" + "signal = %ddBm\n" + "tx_bit_rate = %u\n" + "rx_bit_rate = %u\n" + "last_auth_type = %s\n" + "dot11mode = %s\n", + conn_info->last_ssid.SSID.ssId, + QDF_FULL_MAC_REF(conn_info->bssid.bytes), + conn_info->connect_time, + conn_info->auth_time, + conn_info->chan_freq, + hdd_ch_width_str(conn_info->ch_width), + conn_info->signal, + tx_bit_rate, + rx_bit_rate, + hdd_auth_type_str(conn_info->last_auth_type), + hdd_dot11_mode_str(conn_info->dot11mode)); + + if (ret_val <= 0) + return length; + length += ret_val; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + length += wlan_hdd_add_nss_info(conn_info, buf + length, + buf_avail_len - length); + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + + length += wlan_hdd_add_ht_cap_info(conn_info, buf + length, + buf_avail_len - length); + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + length += wlan_hdd_add_vht_cap_info(conn_info, buf + length, + buf_avail_len - length); + return length; +} + +ssize_t +wlan_hdd_debugfs_update_connect_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t len; + int ret_val; + + hdd_enter(); + + len = wlan_hdd_current_time_info_debugfs(buf, buf_avail_len); + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + + if (adapter->device_mode != QDF_STA_MODE) { + ret_val = scnprintf(buf + len, buf_avail_len - len, + "Interface is not operating STA Mode\n"); + if (ret_val <= 0) + return len; + + len += ret_val; + return len; + } + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + len += wlan_hdd_version_info_debugfs(hdd_ctx, buf + len, + buf_avail_len - len); + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + len += wlan_hdd_connect_info_debugfs(adapter, buf + len, + buf_avail_len - len); + + hdd_exit(); + + return len; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_csr.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_csr.c new file mode 100644 index 0000000000000000000000000000000000000000..720dd6f5f50c28bb85ffae32824fe837e70dc8c9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_csr.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_csr.c + * + * WLAN Host Device Driver implementation to update + * debugfs with roaming related information + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include "qwlan_version.h" +#include "wmi_unified_param.h" +#include "wlan_hdd_debugfs.h" + +ssize_t +wlan_hdd_current_time_info_debugfs(uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t length; + char time_buffer[HDD_TIME_STRING_LEN]; + int ret_val; + + qdf_get_time_of_the_day_in_hr_min_sec_usec(time_buffer, + sizeof(time_buffer)); + ret_val = scnprintf(buf, buf_avail_len, + "\nTime at which this file generated = %s\n", + time_buffer); + if (ret_val < 0) + return 0; + length = ret_val; + + return length; +} + +/** + * wlan_hdd_debugfs_update_csr() - Function to update internal debugfs buffer + * and write into user-space buffer + * @hdd_ctx: hdd context + * @adapter: adapter + * @id: used to identify file for which this info has to be read + * @buf: output buffer to write + * @buf_avail_len: length of the available buffer + * + * Return: Number of bytes read on success, zero otherwise + */ +static ssize_t +wlan_hdd_debugfs_update_csr(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + enum hdd_debugfs_file_id id, + uint8_t *buf, + ssize_t buf_avail_len) +{ + ssize_t len = 0; + + switch (id) { + case HDD_DEBUFS_FILE_ID_CONNECT_INFO: + /* populate connect info */ + len = wlan_hdd_debugfs_update_connect_info(hdd_ctx, adapter, + buf, buf_avail_len); + break; + case HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO: + /* populate roam scan stats info */ + len = wlan_hdd_debugfs_update_roam_stats(hdd_ctx, adapter, + buf, buf_avail_len); + break; + case HDD_DEBUFS_FILE_ID_OFFLOAD_INFO: + /* populate offload info */ + len = wlan_hdd_debugfs_update_filters_info(hdd_ctx, adapter, + buf, buf_avail_len); + break; + default: + hdd_err("Failed to fetch stats, unknown stats type"); + } + + return len; +} + +/** + * __wlan_hdd_read_debugfs_csr() - Function to read debug stats + * @info: buffer info allocated when the debugfs file was opened + * @buf: buffer + * @count: count + * @pos: position pointer + * + * Return: Number of bytes read on success, zero otherwise + */ +static ssize_t +__wlan_hdd_read_debugfs_csr(struct wlan_hdd_debugfs_buffer_info *info, + char __user *buf, size_t count, loff_t *pos) +{ + struct hdd_adapter *adapter = info->adapter; + struct hdd_context *hdd_ctx; + int ret; + ssize_t length; + + hdd_enter(); + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return 0; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return 0; + + if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { + hdd_err("Interface is not enabled"); + return 0; + } + + if (*pos == 0) { + info->length = wlan_hdd_debugfs_update_csr(hdd_ctx, adapter, + info->id, + info->data, + info->max_buf_len); + } + + length = simple_read_from_buffer(buf, count, pos, + info->data, info->length); + hdd_debug("length written = %zu, count: %zu, pos: %lld", + length, count, *pos); + + hdd_exit(); + return length; +} + +/** + * wlan_hdd_read_debugfs_csr() - SSR wrapper function to read stats + * @file: file pointer + * @buf: buffer + * @count: count + * @pos: position pointer + * + * Return: Number of bytes read on success, zero otherwise + */ +static ssize_t +wlan_hdd_read_debugfs_csr(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct wlan_hdd_debugfs_buffer_info *info = file->private_data; + struct osif_vdev_sync *vdev_sync; + ssize_t err_size; + + err_size = osif_vdev_sync_op_start(info->adapter->dev, &vdev_sync); + if (err_size) + return err_size; + + err_size = __wlan_hdd_read_debugfs_csr(info, buf, count, pos); + + osif_vdev_sync_op_stop(vdev_sync); + + return err_size; +} + +/** + * __wlan_hdd_open_debugfs_csr() - Allocates memory for private data + * @adapter: the HDD adapter to operate against + * @csr: file info used to register the debugfs file + * @file: file pointer + * + * Return: Errno + */ +static int __wlan_hdd_open_debugfs_csr(struct hdd_adapter *adapter, + struct hdd_debugfs_file_info *csr, + struct file *file) +{ + struct wlan_hdd_debugfs_buffer_info *info; + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter(); + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return -EINVAL; + + if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { + hdd_err("Interface is not enabled"); + return -EINVAL; + } + + info = qdf_mem_malloc(sizeof(*info)); + if (!info) { + hdd_err("Not enough memory for file private data"); + return -ENOMEM; + } + + info->data = qdf_mem_malloc(csr->buf_max_size); + if (!info->data) { + hdd_err("roam stats debugfs buffer allocation failed"); + qdf_mem_free(info); + return -ENOMEM; + } + info->length = 0; + info->max_buf_len = csr->buf_max_size; + info->id = csr->id; + info->adapter = adapter; + + file->private_data = info; + hdd_exit(); + + return 0; +} + +/** + * wlan_hdd_open_debugfs_csr() - SSR wrapper function to allocate memory for + * private data on file open + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: Errno + */ +static int wlan_hdd_open_debugfs_csr(struct inode *inode, struct file *file) +{ + struct hdd_debugfs_file_info *csr = inode->i_private; + struct hdd_adapter *adapter = qdf_container_of(csr, struct hdd_adapter, + csr_file[csr->id]); + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); + if (errno) + return errno; + + hdd_debugfs_thread_increment(); + errno = __wlan_hdd_open_debugfs_csr(adapter, csr, file); + if (errno) + hdd_debugfs_thread_decrement(); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_release_debugfs_csr() - Function to free private memory on + * release + * @file: file pointer + * + * Return: Errno + */ +static int +__wlan_hdd_release_debugfs_csr(struct file *file) +{ + struct wlan_hdd_debugfs_buffer_info *info = file->private_data; + + hdd_enter(); + + file->private_data = NULL; + qdf_mem_free(info->data); + qdf_mem_free(info); + + hdd_exit(); + + return 0; +} + +/** + * wlan_hdd_release_debugfs_csr() - SSR wrapper function to free + * private data on release + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: Errno + */ +static int wlan_hdd_release_debugfs_csr(struct inode *inode, struct file *file) +{ + struct wlan_hdd_debugfs_buffer_info *info = file->private_data; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(info->adapter->dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_release_debugfs_csr(file); + hdd_debugfs_thread_decrement(); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static const struct file_operations fops_csr_debugfs = { + .read = wlan_hdd_read_debugfs_csr, + .open = wlan_hdd_open_debugfs_csr, + .release = wlan_hdd_release_debugfs_csr, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void wlan_hdd_debugfs_csr_init(struct hdd_adapter *adapter) +{ + struct hdd_debugfs_file_info *csr; + const uint32_t max_len = HDD_DEBUGFS_FILE_NAME_MAX; + + /* + * Create debufs diagnostic files for connect, offload info + * and roam info and store in csr_file member of adapter + */ + + csr = &adapter->csr_file[HDD_DEBUFS_FILE_ID_CONNECT_INFO]; + if (!csr->entry) { + strlcpy(csr->name, "connect_info", max_len); + csr->id = HDD_DEBUFS_FILE_ID_CONNECT_INFO; + csr->buf_max_size = DEBUGFS_CONNECT_INFO_BUF_SIZE; + csr->entry = debugfs_create_file(csr->name, 0444, + adapter->debugfs_phy, + csr, &fops_csr_debugfs); + if (!csr->entry) + hdd_err("Failed to create debugfs file: %s", + csr->name); + } + + csr = &adapter->csr_file[HDD_DEBUFS_FILE_ID_OFFLOAD_INFO]; + if (!csr->entry) { + strlcpy(csr->name, "offload_info", max_len); + csr->id = HDD_DEBUFS_FILE_ID_OFFLOAD_INFO; + csr->buf_max_size = DEBUGFS_OFFLOAD_INFO_BUF_SIZE; + csr->entry = debugfs_create_file(csr->name, 0444, + adapter->debugfs_phy, + csr, &fops_csr_debugfs); + if (!csr->entry) + hdd_err("Failed to create generic_info debugfs file"); + } + + csr = &adapter->csr_file[HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO]; + if (!csr->entry) { + strlcpy(csr->name, "roam_stats", max_len); + csr->id = HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO; + csr->buf_max_size = DEBUGFS_ROAM_SCAN_STATS_INFO_BUF_SIZE; + csr->entry = debugfs_create_file(csr->name, 0444, + adapter->debugfs_phy, + csr, &fops_csr_debugfs); + if (!csr->entry) + hdd_err("Failed to create generic_info debugfs file"); + } +} + +void wlan_hdd_debugfs_csr_deinit(struct hdd_adapter *adapter) +{ + uint32_t i; + struct dentry *entry; + + for (i = 0; i < HDD_DEBUGFS_FILE_ID_MAX; i++) { + entry = adapter->csr_file[i].entry; + if (!entry) + continue; + + adapter->csr_file[i].entry = NULL; + debugfs_remove(entry); + entry = NULL; + } +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_llstat.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_llstat.c new file mode 100644 index 0000000000000000000000000000000000000000..8098c0d777deb490d3224ab14ca163748af9e9ac --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_llstat.c @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_llstat.c + * + * WLAN Host Device Driver implementation to update + * debugfs with Link Layer statistics + */ + +#include +#include "osif_sync.h" +#include +#include +#include + +struct ll_stats_buf { + ssize_t len; + uint8_t *result; +}; + +static struct ll_stats_buf ll_stats; + +static DEFINE_MUTEX(llstats_mutex); + +void hdd_debugfs_process_iface_stats(struct hdd_adapter *adapter, + void *data, uint32_t num_peers) +{ + struct wifi_interface_stats *iface_stat; + struct wifi_interface_info *iface_info; + wmi_iface_link_stats *link_stats; + wmi_wmm_ac_stats *ac_stats; + wmi_iface_offload_stats *offload_stats; + uint64_t average_tsf_offset; + int i; + ssize_t len = 0; + uint8_t *buffer; + + hdd_enter(); + + mutex_lock(&llstats_mutex); + if (!ll_stats.result) { + mutex_unlock(&llstats_mutex); + hdd_err("LL statistics buffer is NULL"); + return; + } + + iface_stat = data; + buffer = ll_stats.result; + buffer += ll_stats.len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\n\n===LL_STATS_IFACE: num_peers: %d===", num_peers); + + if (false == hdd_get_interface_info(adapter, &iface_stat->info)) { + mutex_unlock(&llstats_mutex); + hdd_err("hdd_get_interface_info get fail"); + return; + } + + iface_info = &iface_stat->info; + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nmode: %u, MAC_ADDR: "QDF_FULL_MAC_FMT", state: %u, roaming: %u, capabilities: %u, SSID: %s, BSSID_MAC: "QDF_FULL_MAC_FMT", ap_country_str: %s, country_str: %s", + iface_info->mode, + QDF_FULL_MAC_REF(iface_info->macAddr.bytes), + iface_info->state, iface_info->roaming, + iface_info->capabilities, iface_info->ssid, + QDF_FULL_MAC_REF(iface_info->bssid.bytes), + iface_info->apCountryStr, + iface_info->countryStr); + + link_stats = &iface_stat->link_stats; + average_tsf_offset = link_stats->avg_bcn_spread_offset_high; + average_tsf_offset = (average_tsf_offset << 32) | + link_stats->avg_bcn_spread_offset_low; + + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nbeacon_rx: %u, mgmt_rx: %u, mgmt_action_rx: %u, mgmt_action_tx: %u, rssi_mgmt: %d, rssi_data: %d, rssi_ack: %d, is_leaky_ap: %u, avg_rx_frms_leaked: %u, rx_leak_window: %u, average_tsf_offset: %llu, Tx RTS success count: %u, Tx RTS fail count: %u, Tx ppdu success count: %u, Tx ppdu fail count: %u, Connected duration: %u, Disconnected duration: %u, RTT ranging duration: %u, RTT responder duration: %u, Num tx probes: %u, Num beacon miss: %u,\n\nNumber of AC: %d", + link_stats->beacon_rx, link_stats->mgmt_rx, + link_stats->mgmt_action_rx, link_stats->mgmt_action_tx, + link_stats->rssi_mgmt, link_stats->rssi_data, + link_stats->rssi_ack, link_stats->is_leaky_ap, + link_stats->avg_rx_frms_leaked, + link_stats->rx_leak_window, average_tsf_offset, + link_stats->tx_rts_succ_cnt, + link_stats->tx_rts_fail_cnt, + link_stats->tx_ppdu_succ_cnt, + link_stats->tx_ppdu_fail_cnt, + link_stats->connected_duration, + link_stats->disconnected_duration, + link_stats->rtt_ranging_duration, + link_stats->rtt_responder_duration, + link_stats->num_probes_tx, link_stats->num_beacon_miss, + link_stats->num_ac); + + for (i = 0; i < link_stats->num_ac; i++) { + ac_stats = &iface_stat->ac_stats[i]; + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nac_type: %d, tx_mpdu: %u, rx_mpdu: %u, tx_mcast: %u, rx_mcast: %u, rx_ampdu: %u tx_ampdu: %u, mpdu_lost: %u, retries: %u, retries_short: %u, retries_long: %u, contention_time: min-%u max-%u avg-%u, contention num samples: %u, tx_pending_msdu: %u", + ac_stats->ac_type, + ac_stats->tx_mpdu, ac_stats->rx_mpdu, + ac_stats->tx_mcast, ac_stats->rx_mcast, + ac_stats->rx_ampdu, ac_stats->rx_ampdu, + ac_stats->mpdu_lost, ac_stats->retries, + ac_stats->retries_short, ac_stats->retries_long, + ac_stats->contention_time_min, + ac_stats->contention_time_max, + ac_stats->contention_time_avg, + ac_stats->contention_num_samples, + ac_stats->tx_pending_msdu); + } + + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\n\nNumber of offload stats: %d", + iface_stat->num_offload_stats); + + for (i = 0; i < iface_stat->num_offload_stats; i++) { + offload_stats = &iface_stat->offload_stats[i]; + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\ntype: %d, rx_count: %u, drp_count: %u, fwd_count: %u", + offload_stats->type, offload_stats->rx_count, + offload_stats->drp_count, + offload_stats->fwd_count); + } + + ll_stats.len += len; + mutex_unlock(&llstats_mutex); + hdd_exit(); +} + +void hdd_debugfs_process_peer_stats(struct hdd_adapter *adapter, void *data) +{ + struct wifi_peer_stat *peer_stat; + struct wifi_peer_info *peer_info; + struct wifi_rate_stat *rate_stat; + int i, j, num_rate; + ssize_t len = 0; + uint8_t *buffer; + + hdd_enter(); + + mutex_lock(&llstats_mutex); + if (!ll_stats.result) { + mutex_unlock(&llstats_mutex); + hdd_err("LL statistics buffer is NULL"); + return; + } + + peer_stat = data; + + buffer = ll_stats.result; + buffer += ll_stats.len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\n\n===LL_STATS_PEER_ALL : num_peers %u===", + peer_stat->num_peers); + + peer_info = peer_stat->peer_info; + for (i = 1; i <= peer_stat->num_peers; i++) { + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nType: %d, peer_mac: "QDF_FULL_MAC_FMT", capabilities: %u\nnum_rates: %d", + wmi_to_sir_peer_type(peer_info->type), + QDF_FULL_MAC_REF(peer_info->peer_macaddr.bytes), + peer_info->capabilities, peer_info->num_rate); + + num_rate = peer_info->num_rate; + for (j = 0; j < num_rate; j++) { + rate_stat = &peer_info->rate_stats[j]; + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\npreamble: %0x, nss: %0x, bw: %0x, mcs: %0x, bitrate: %0x, txmpdu: %u, rxmpdu: %u, mpdu_lost: %u, retries: %u, retries_short: %u, retries_long: %u", + rate_stat->rate.preamble, rate_stat->rate.nss, + rate_stat->rate.bw, + rate_stat->rate.rate_or_mcs_index, + rate_stat->rate.bitrate, rate_stat->tx_mpdu, + rate_stat->rx_mpdu, rate_stat->mpdu_lost, + rate_stat->retries, rate_stat->retries_short, + rate_stat->retries_long); + } + peer_info = (struct wifi_peer_info *) ((uint8_t *) + peer_stat->peer_info + (i * + sizeof(struct wifi_peer_info)) + + (num_rate * sizeof(struct wifi_rate_stat))); + } + ll_stats.len += len; + mutex_unlock(&llstats_mutex); + hdd_exit(); + +} + +void hdd_debugfs_process_radio_stats(struct hdd_adapter *adapter, + uint32_t more_data, void *data, uint32_t num_radio) +{ + int i, j; + ssize_t len = 0; + uint8_t *buffer; + struct wifi_radio_stats *radio_stat = (struct wifi_radio_stats *) data; + struct wifi_channel_stats *chan_stat; + + hdd_enter(); + + mutex_lock(&llstats_mutex); + if (!ll_stats.result) { + mutex_unlock(&llstats_mutex); + hdd_err("LL statistics buffer is NULL"); + return; + } + + buffer = ll_stats.result; + buffer += ll_stats.len; + len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\n\n===LL_STATS_RADIO: number of radios: %u===", + num_radio); + + for (i = 0; i < num_radio; i++) { + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nRadio: %u on_time: %u, tx_time: %u, rx_time: %u, on_time_scan: %u, on_time_nbd: %u, on_time_gscan: %u, on_time_roam_scan: %u, on_time_pno_scan: %u on_time_hs20: %u, on_time_host_scan: %u, on_time_lpi_scan: %u\ntotal_num_tx_pwr_levels: %u\n", + radio_stat->radio, radio_stat->on_time, + radio_stat->tx_time, radio_stat->rx_time, + radio_stat->on_time_scan, radio_stat->on_time_nbd, + radio_stat->on_time_gscan, + radio_stat->on_time_roam_scan, + radio_stat->on_time_pno_scan, + radio_stat->on_time_hs20, + radio_stat->on_time_host_scan, + radio_stat->on_time_lpi_scan, + radio_stat->total_num_tx_power_levels); + + for (j = 0; j < radio_stat->total_num_tx_power_levels; j++) { + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "%d ", radio_stat->tx_time_per_power_level[j]); + } + + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nNum channels: %d", radio_stat->num_channels); + + for (j = 0; j < radio_stat->num_channels; j++) { + chan_stat = (struct wifi_channel_stats *) + ((uint8_t *)radio_stat->channels + + (j * sizeof(struct wifi_channel_stats))); + + buffer += len; + ll_stats.len += len; + len = scnprintf(buffer, + DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len, + "\nChan width: %u, center_freq: %u, center_freq0: %u, center_freq1: %u, on_time: %u, cca_busy_time: %u", + chan_stat->channel.width, + chan_stat->channel.center_freq, + chan_stat->channel.center_freq0, + chan_stat->channel.center_freq1, + chan_stat->on_time, chan_stat->cca_busy_time); + } + + radio_stat++; + } + ll_stats.len += len; + mutex_unlock(&llstats_mutex); + hdd_exit(); +} + +static inline void wlan_hdd_llstats_free_buf(void) +{ + mutex_lock(&llstats_mutex); + qdf_mem_free(ll_stats.result); + ll_stats.result = NULL; + ll_stats.len = 0; + mutex_unlock(&llstats_mutex); +} + +static int wlan_hdd_llstats_alloc_buf(void) +{ + mutex_lock(&llstats_mutex); + if (ll_stats.result) { + mutex_unlock(&llstats_mutex); + hdd_err("Buffer is already allocated"); + return 0; + } + ll_stats.len = 0; + ll_stats.result = qdf_mem_malloc(DEBUGFS_LLSTATS_BUF_SIZE); + if (!ll_stats.result) { + mutex_unlock(&llstats_mutex); + return -EINVAL; + } + mutex_unlock(&llstats_mutex); + return 0; +} + +/** + * hdd_debugfs_stats_update() - Update userspace with local statistics buffer + * @buf: userspace buffer (to which data is being copied into) + * @count: max data that can be copied into buf + * @pos: offset (where data should be copied into) + * + * This function should copies link layer statistics buffer into debugfs + * entry. + * + * Return: number of characters copied; 0 on no-copy + */ +static ssize_t hdd_debugfs_stats_update(char __user *buf, size_t count, + loff_t *pos) +{ + ssize_t ret_cnt; + + hdd_enter(); + mutex_lock(&llstats_mutex); + if (!ll_stats.result) { + mutex_unlock(&llstats_mutex); + hdd_err("Trying to read from NULL buffer"); + return 0; + } + + ret_cnt = simple_read_from_buffer(buf, count, pos, + ll_stats.result, ll_stats.len); + mutex_unlock(&llstats_mutex); + hdd_debug("LL stats read req: count: %zu, pos: %lld", count, *pos); + + hdd_exit(); + return ret_cnt; +} + +/** + * __wlan_hdd_read_ll_stats_debugfs() - API to collect LL stats from FW + * @net_dev: net_device context used to register the debugfs file + * @buf: buffer + * @count: count + * @pos: position pointer + * + * Return: Number of bytes read on success, error number otherwise + */ +static ssize_t __wlan_hdd_read_ll_stats_debugfs(struct net_device *net_dev, + char __user *buf, size_t count, + loff_t *pos) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + ssize_t ret; + + hdd_enter(); + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + /* All the events are received and buffer is populated */ + ret = hdd_debugfs_stats_update(buf, count, pos); + hdd_debug("%zu characters written into debugfs", ret); + + hdd_exit(); + + return ret; +} + +/** + * wlan_hdd_read_ll_stats_debugfs() - SSR wrapper function to read LL debugfs + * @file: file pointer + * @buf: buffer + * @count: count + * @pos: position pointer + * + * Return: Number of bytes read on success, error number otherwise + */ +static ssize_t wlan_hdd_read_ll_stats_debugfs(struct file *file, + char __user *buf, size_t count, + loff_t *pos) +{ + struct net_device *net_dev = file_inode(file)->i_private; + struct osif_vdev_sync *vdev_sync; + ssize_t err_size; + + err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (err_size) + return err_size; + + err_size = __wlan_hdd_read_ll_stats_debugfs(net_dev, buf, count, pos); + + osif_vdev_sync_op_stop(vdev_sync); + + return err_size; +} + +/** + * __wlan_hdd_open_ll_stats_debugfs() - Function to save private on open + * @net_dev: net_device context used to register the debugfs file + * + * Return: Errno + */ +static int __wlan_hdd_open_ll_stats_debugfs(struct net_device *net_dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + int errno; + + hdd_enter(); + + errno = hdd_validate_adapter(adapter); + if (errno) + return errno; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + errno = wlan_hdd_llstats_alloc_buf(); + if (errno) + return errno; + + errno = wlan_hdd_ll_stats_get(adapter, DEBUGFS_LLSTATS_REQID, + DEBUGFS_LLSTATS_REQMASK); + if (errno) + goto free_buf; + + hdd_exit(); + + return 0; + +free_buf: + wlan_hdd_llstats_free_buf(); + + hdd_exit(); + + return errno; +} + +/** + * wlan_hdd_open_ll_stats_debugfs() - SSR wrapper function to save private + * on open + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: Errno + */ +static int wlan_hdd_open_ll_stats_debugfs(struct inode *inode, + struct file *file) +{ + struct net_device *net_dev = inode->i_private; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_open_ll_stats_debugfs(net_dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_release_ll_stats_debugfs() - Function to save private on release + * @net_dev: net_device context used to register the debugfs file + * + * Return: Errno + */ +static int __wlan_hdd_release_ll_stats_debugfs(struct net_device *net_dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter(); + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + wlan_hdd_llstats_free_buf(); + + hdd_exit(); + + return 0; +} + +/** + * wlan_hdd_release_ll_stats_debugfs() - SSR wrapper function to save private + * on release + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: Errno + */ +static int wlan_hdd_release_ll_stats_debugfs(struct inode *inode, + struct file *file) +{ + struct net_device *net_dev = file_inode(file)->i_private; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_release_ll_stats_debugfs(net_dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static const struct file_operations fops_ll_stats_debugfs = { + .read = wlan_hdd_read_ll_stats_debugfs, + .open = wlan_hdd_open_ll_stats_debugfs, + .release = wlan_hdd_release_ll_stats_debugfs, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int wlan_hdd_create_ll_stats_file(struct hdd_adapter *adapter) +{ + if (!debugfs_create_file("ll_stats", 0444, adapter->debugfs_phy, + adapter->dev, &fops_ll_stats_debugfs)) + return -EINVAL; + + return 0; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_mibstat.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_mibstat.c new file mode 100644 index 0000000000000000000000000000000000000000..dd5aadcb326adb3aadf6dd4f715f7451d094415e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_mibstat.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_mibstat.c + * + * WLAN Host Device Driver implementation to update + * debugfs with MIB statistics + */ + +#include +#include "osif_sync.h" +#include +#include +#include + +struct mib_stats_buf { + ssize_t len; + uint8_t *result; +}; + +static struct mib_stats_buf mib_stats; + +qdf_mutex_t mibstats_lock; + +void hdd_debugfs_process_mib_stats(struct hdd_adapter *adapter, + struct stats_event *stats) +{ + ssize_t len = 0; + uint8_t *buffer; + + hdd_enter(); + + qdf_mutex_acquire(&mibstats_lock); + if (!mib_stats.result) { + qdf_mutex_release(&mibstats_lock); + hdd_err("MIB statistics buffer is NULL"); + return; + } + + buffer = mib_stats.result; + buffer += mib_stats.len; + + len = scnprintf(buffer, DEBUGFS_MIBSTATS_BUF_SIZE - mib_stats.len, + "dot11RTSSuccessCount %d " + "\ndot11RTSFailureCount %d " + "\ndot11QosFailedCount %d " + "\ndot11QosRetryCount %d " + "\ndot11QosTransmittedFrameCount %d " + "\ndot11QosMPDUsReceivedCount %d " + "\ndot11TransmittedAMPDUCount %d " + "\ndot11QosACKFailureCount %d", + stats->mib_stats->mib_mac_statistics.rts_success_cnt, + stats->mib_stats->mib_mac_statistics.rts_fail_cnt, + stats->mib_stats->mib_qos_counters.qos_failed_cnt, + stats->mib_stats->mib_qos_counters.qos_retry_cnt, + stats->mib_stats->mib_qos_counters.qos_tx_frame_cnt, + stats->mib_stats->mib_qos_counters.qos_mpdu_rx_cnt, + stats->mib_stats->mib_counters_group3.tx_ampdu_cnt, + stats->mib_stats-> + mib_qos_counters.tx_qos_ack_fail_cnt_up + ); + + buffer += len; + mib_stats.len += len; + qdf_mutex_release(&mibstats_lock); + + hdd_exit(); +} + +static inline void wlan_hdd_mibstats_free_buf(void) +{ + qdf_mutex_acquire(&mibstats_lock); + qdf_mem_free(mib_stats.result); + mib_stats.result = NULL; + mib_stats.len = 0; + qdf_mutex_release(&mibstats_lock); +} + +static int wlan_hdd_mibstats_alloc_buf(void) +{ + qdf_mutex_acquire(&mibstats_lock); + if (mib_stats.result) { + qdf_mutex_release(&mibstats_lock); + hdd_err("Buffer is already allocated"); + return 0; + } + mib_stats.len = 0; + mib_stats.result = qdf_mem_malloc(DEBUGFS_MIBSTATS_BUF_SIZE); + if (!mib_stats.result) { + qdf_mutex_release(&mibstats_lock); + return -EINVAL; + } + qdf_mutex_release(&mibstats_lock); + return 0; +} + +/** + * hdd_debugfs_mib_stats_update() - Update userspace with local stats buffer + * @buf: userspace buffer (to which data is being copied into) + * @count: max data that can be copied into buf in bytes + * @pos: offset (where data should be copied into) + * + * This function copies mib statistics buffer into debugfs + * entry. + * + * Return: number of characters copied; 0 on no-copy + */ +static ssize_t hdd_debugfs_mib_stats_update(char __user *buf, + size_t count, loff_t *pos) +{ + ssize_t ret_cnt; + + hdd_enter(); + qdf_mutex_acquire(&mibstats_lock); + if (!mib_stats.result) { + qdf_mutex_release(&mibstats_lock); + hdd_err("Trying to read from NULL buffer"); + return 0; + } + + ret_cnt = simple_read_from_buffer(buf, count, pos, + mib_stats.result, + mib_stats.len); + qdf_mutex_release(&mibstats_lock); + hdd_debug("mib stats read req: count: %zu, pos: %lld", count, *pos); + + hdd_exit(); + return ret_cnt; +} + +/** + * __wlan_hdd_release_mib_stats_debugfs() - Function to free private + * memory on release + * @net_dev: net_device context used to register the debugfs file + * + * Return: Errno + */ +static int __wlan_hdd_release_mib_stats_debugfs(struct net_device *net_dev) + +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + int errno; + + hdd_enter_dev(net_dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + wlan_hdd_mibstats_free_buf(); + + hdd_exit(); + + return 0; +} + +/** + * wlan_hdd_release_mib_stats_debugfs() - SSR wrapper function to free + * private memory on release + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: Errno + */ +static int wlan_hdd_release_mib_stats_debugfs(struct inode *inode, + struct file *file) +{ + struct net_device *net_dev = file_inode(file)->i_private; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_release_mib_stats_debugfs(net_dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_read_mib_stats_debugfs() - mib_stats debugfs handler + * @net_dev: net_device context used to register the debugfs file + * @buf: text being written to the debugfs + * @count: size of @buf + * @pos: (unused) offset into the virtual file system + * + * Return: Number of bytes read on success, error number otherwise + */ +static ssize_t __wlan_hdd_read_mib_stats_debugfs(struct net_device *net_dev, + char __user *buf, + size_t count, loff_t *pos) + +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + ssize_t ret; + + hdd_enter_dev(net_dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + goto free_buf; + + if (*pos == 0) { + ret = wlan_hdd_get_mib_stats(adapter); + if (ret) + goto free_buf; + } + + /* All the events are received and buffer is populated */ + ret = hdd_debugfs_mib_stats_update(buf, count, pos); + hdd_debug("%zu characters written into debugfs", ret); + + hdd_exit(); + + return ret; + +free_buf: + wlan_hdd_mibstats_free_buf(); + + hdd_exit(); + + return ret; +} + +/** + * wlan_hdd_read_mib_stats_debugfs() - SSR wrapper function to read + * mib stats + * @file: file pointer + * @buf: buffer + * @count: count + * @pos: position pointer + * + * Return: Number of bytes read on success, error number otherwise + */ +static ssize_t wlan_hdd_read_mib_stats_debugfs(struct file *file, + char __user *buf, size_t count, + loff_t *pos) +{ + struct net_device *net_dev = file_inode(file)->i_private; + struct osif_vdev_sync *vdev_sync; + ssize_t err_size; + + err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (err_size) + return err_size; + + err_size = __wlan_hdd_read_mib_stats_debugfs(net_dev, buf, + count, pos); + + osif_vdev_sync_op_stop(vdev_sync); + + return err_size; +} + +/** + * __wlan_hdd_open_mib_stats_debugfs() - Function to save private on open + * @net_dev: net_device context used to register the debugfs file + * + * Return: Errno + */ +static int __wlan_hdd_open_mib_stats_debugfs(struct net_device *net_dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + int errno; + + hdd_enter_dev(net_dev); + + errno = hdd_validate_adapter(adapter); + if (errno) + return errno; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + errno = wlan_hdd_mibstats_alloc_buf(); + if (errno) + return errno; + + hdd_exit(); + + return 0; +} + +/** + * wlan_hdd_open_mib_stats_debugfs() - SSR wrapper to save private + * on open + * @inode: Pointer to inode structure + * @file: file pointer + * + * Return: Errno + */ +static int wlan_hdd_open_mib_stats_debugfs(struct inode *inode, + struct file *file) +{ + struct net_device *net_dev = inode->i_private; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_open_mib_stats_debugfs(net_dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static const struct file_operations fops_mib_stats = { + .read = wlan_hdd_read_mib_stats_debugfs, + .open = wlan_hdd_open_mib_stats_debugfs, + .release = wlan_hdd_release_mib_stats_debugfs, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int wlan_hdd_create_mib_stats_file(struct hdd_adapter *adapter) +{ + if (!debugfs_create_file("mib_stats", 0444, adapter->debugfs_phy, + adapter->dev, &fops_mib_stats)) + return -EINVAL; + + if (QDF_IS_STATUS_ERROR(qdf_mutex_create( + &mibstats_lock))) { + hdd_debug("mibstats lock init failed!"); + return QDF_STATUS_E_FAILURE; + } + + return 0; +} + +void wlan_hdd_destroy_mib_stats_lock(void) +{ + qdf_mutex_destroy(&mibstats_lock); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_offload.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_offload.c new file mode 100644 index 0000000000000000000000000000000000000000..06a90f25bf697e8703a2ad629ba602e85a55bfdd --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_offload.c @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_offload.c + * + * WLAN Host Device Driver implementation to update + * debugfs with offload information + */ + +#include +#include +#include +#include +#include "qwlan_version.h" +#include "wmi_unified_param.h" +#include "wlan_pmo_common_public_struct.h" +#include "wlan_pmo_ns_public_struct.h" +#include "wlan_pmo_mc_addr_filtering_public_struct.h" +#include "wlan_pmo_ucfg_api.h" + +/* IPv6 address string */ +#define IPV6_MAC_ADDRESS_STR_LEN 47 /* Including null terminator */ + +/** + * wlan_hdd_mc_addr_list_info_debugfs() - Populate mc addr list info + * @hdd_ctx: pointer to hdd context + * @adapter: pointer to adapter + * @buf: output buffer to hold mc addr list info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_mc_addr_list_info_debugfs(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, uint8_t *buf, + ssize_t buf_avail_len) +{ + ssize_t length = 0; + int ret; + uint8_t i; + struct pmo_mc_addr_list mc_addr_list = {0}; + QDF_STATUS status; + + if (!ucfg_pmo_is_mc_addr_list_enabled(hdd_ctx->psoc)) { + ret = scnprintf(buf, buf_avail_len, + "\nMC addr ini is disabled\n"); + if (ret > 0) + length = ret; + return length; + } + + status = ucfg_pmo_get_mc_addr_list(hdd_ctx->psoc, + adapter->vdev_id, + &mc_addr_list); + if (!QDF_IS_STATUS_SUCCESS(status)) { + ret = scnprintf(buf, buf_avail_len, + "\nMC addr list query is failed\n"); + if (ret > 0) + length = ret; + return length; + } + + if (mc_addr_list.mc_cnt == 0) { + ret = scnprintf(buf, buf_avail_len, + "\nMC addr list is empty\n"); + if (ret > 0) + length = ret; + return length; + } + + ret = scnprintf(buf, buf_avail_len, + "\nMC ADDR LIST DETAILS (mc_cnt = %u)\n", + mc_addr_list.mc_cnt); + if (ret <= 0) + return length; + length += ret; + + for (i = 0; i < mc_addr_list.mc_cnt; i++) { + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + QDF_FULL_MAC_FMT "\n", + QDF_FULL_MAC_REF(mc_addr_list.mc_addr[i].bytes)); + if (ret <= 0) + return length; + length += ret; + } + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + ret = scnprintf(buf + length, buf_avail_len - length, + "mc_filter_applied = %u\n", + mc_addr_list.is_filter_applied); + if (ret <= 0) + return length; + + length += ret; + return length; +} + +/** + * wlan_hdd_arp_offload_info_debugfs() - Populate arp offload info + * @hdd_ctx: pointer to hdd context + * @adapter: pointer to adapter + * @buf: output buffer to hold arp offload info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_arp_offload_info_debugfs(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, uint8_t *buf, + ssize_t buf_avail_len) +{ + ssize_t length = 0; + int ret_val; + struct pmo_arp_offload_params info = {0}; + QDF_STATUS status; + + status = ucfg_pmo_get_arp_offload_params(adapter->vdev, + &info); + if (!QDF_IS_STATUS_SUCCESS(status)) { + ret_val = scnprintf(buf, buf_avail_len, + "\nARP OFFLOAD QUERY FAILED\n"); + goto len_adj; + } + + if (!info.is_offload_applied) + ret_val = scnprintf(buf, buf_avail_len, + "\nARP OFFLOAD: DISABLED\n"); + else + ret_val = scnprintf(buf, buf_avail_len, + "\nARP OFFLOAD: ENABLED (%u.%u.%u.%u)\n", + info.host_ipv4_addr[0], + info.host_ipv4_addr[1], + info.host_ipv4_addr[2], + info.host_ipv4_addr[3]); +len_adj: + if (ret_val <= 0) + return length; + length = ret_val; + + return length; +} + +#ifdef WLAN_NS_OFFLOAD +/** + * ipv6_addr_string() - Get IPv6 addr string from array of octets + * @buffer: output buffer to hold string, caller should ensure size of + * buffer is atleast IPV6_MAC_ADDRESS_STR_LEN + * @ipv6_addr: IPv6 address array + * + * Return: None + */ +static void ipv6_addr_string(uint8_t *buffer, uint8_t *ipv6_addr) +{ + uint8_t *a = ipv6_addr; + + scnprintf(buffer, IPV6_MAC_ADDRESS_STR_LEN, + "%02x%02x::%02x%02x::%02x%02x::%02x%02x::%02x%02x::%02x%02x::%02x%02x::%02x%02x", + (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5], (a)[6], + (a)[7], (a)[8], (a)[9], (a)[10], (a)[11], (a)[12], (a)[13], + (a)[14], (a)[15]); +} + +/** + * hdd_ipv6_scope_str() - Get string for PMO NS (IPv6) Addr scope + * @scope: scope id from enum pmo_ns_addr_scope + * + * Return: Meaningful string for enum pmo_ns_addr_scope + */ +static uint8_t *hdd_ipv6_scope_str(enum pmo_ns_addr_scope scope) +{ + switch (scope) { + case PMO_NS_ADDR_SCOPE_NODELOCAL: + return "Node Local"; + case PMO_NS_ADDR_SCOPE_LINKLOCAL: + return "Link Local"; + case PMO_NS_ADDR_SCOPE_SITELOCAL: + return "Site Local"; + case PMO_NS_ADDR_SCOPE_ORGLOCAL: + return "Org Local"; + case PMO_NS_ADDR_SCOPE_GLOBAL: + return "Global"; + default: + return "Invalid"; + } +} + +/** + * wlan_hdd_ns_offload_info_debugfs() - Populate ns offload info + * @hdd_ctx: pointer to hdd context + * @adapter: pointer to adapter + * @buf: output buffer to hold ns offload info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_ns_offload_info_debugfs(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, uint8_t *buf, + ssize_t buf_avail_len) +{ + ssize_t length = 0; + int ret; + struct pmo_ns_offload_params info = {0}; + QDF_STATUS status; + uint32_t i; + + status = ucfg_pmo_get_ns_offload_params(adapter->vdev, + &info); + if (!QDF_IS_STATUS_SUCCESS(status)) { + ret = scnprintf(buf, buf_avail_len, + "\nNS OFFLOAD QUERY FAILED\n"); + if (ret <= 0) + return length; + length += ret; + + return length; + } + + ret = scnprintf(buf, buf_avail_len, + "\nNS OFFLOAD DETAILS\n"); + if (ret <= 0) + return length; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + + if (!info.is_offload_applied) { + ret = scnprintf(buf + length, buf_avail_len - length, + "NS offload is not enabled\n"); + if (ret <= 0) + return length; + length += ret; + + return length; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "NS offload enabled, %u ns addresses offloaded\n", + info.num_ns_offload_count); + if (ret <= 0) + return length; + length += ret; + + for (i = 0; i < info.num_ns_offload_count; i++) { + uint8_t ipv6_str[IPV6_MAC_ADDRESS_STR_LEN]; + uint8_t cast_string[12]; + uint8_t *scope_string; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + + ipv6_addr_string(ipv6_str, info.target_ipv6_addr[i]); + scope_string = hdd_ipv6_scope_str(info.scope[i]); + + if (info.target_ipv6_addr_ac_type[i] == + PMO_IPV6_ADDR_AC_TYPE) + strlcpy(cast_string, "(ANY CAST)", 12); + else + strlcpy(cast_string, "(UNI CAST)", 12); + + ret = scnprintf(buf + length, buf_avail_len - length, + "%u. %s %s and scope is: %s\n", + (i + 1), ipv6_str, cast_string, + scope_string); + if (ret <= 0) + return length; + length += ret; + } + + return length; +} +#else +/** + * wlan_hdd_ns_offload_info_debugfs() - Populate ns offload info + * @hdd_ctx: pointer to hdd context + * @adapter: pointer to adapter + * @buf: output buffer to hold ns offload info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_ns_offload_info_debugfs(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, uint8_t *buf, + ssize_t buf_avail_len) +{ + return 0; +} +#endif + +/** + * wlan_hdd_apf_info_debugfs() - Populate apf offload info + * @hdd_ctx: pointer to hdd context + * @adapter: pointer to adapter + * @buf: output buffer to hold apf offload info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +wlan_hdd_apf_info_debugfs(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, uint8_t *buf, + ssize_t buf_avail_len) +{ + ssize_t length = 0; + int ret_val; + bool apf_enabled; + + if (hdd_ctx->apf_version > 2) + apf_enabled = adapter->apf_context.apf_enabled; + else + apf_enabled = hdd_ctx->apf_enabled_v2; + + ret_val = scnprintf(buf, buf_avail_len, + "\nAPF OFFLOAD DETAILS, offload_applied: %u\n\n", + apf_enabled); + if (ret_val <= 0) + return length; + length = ret_val; + + return length; +} + +ssize_t +wlan_hdd_debugfs_update_filters_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t len; + int ret_val; + struct hdd_station_ctx *hdd_sta_ctx; + + hdd_enter(); + + len = wlan_hdd_current_time_info_debugfs(buf, buf_avail_len); + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + + if (adapter->device_mode != QDF_STA_MODE) { + ret_val = scnprintf(buf + len, buf_avail_len - len, + "Interface is not operating in STA mode\n"); + if (ret_val <= 0) + return len; + + len += ret_val; + return len; + } + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (hdd_sta_ctx->conn_info.conn_state != eConnectionState_Associated) { + ret_val = scnprintf(buf + len, buf_avail_len - len, + "\nSTA is not connected\n"); + if (ret_val <= 0) + return len; + + len += ret_val; + return len; + } + + len += wlan_hdd_mc_addr_list_info_debugfs(hdd_ctx, adapter, buf + len, + buf_avail_len - len); + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + len += wlan_hdd_arp_offload_info_debugfs(hdd_ctx, adapter, buf + len, + buf_avail_len - len); + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + len += wlan_hdd_ns_offload_info_debugfs(hdd_ctx, adapter, buf + len, + buf_avail_len - len); + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + len += wlan_hdd_apf_info_debugfs(hdd_ctx, adapter, buf + len, + buf_avail_len - len); + + hdd_exit(); + + return len; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_roam.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_roam.c new file mode 100644 index 0000000000000000000000000000000000000000..d843f1c741ffae8749729b8db0b32b54b6b9f400 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_debugfs_roam.c @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_debugfs_roam.c + * + * WLAN Host Device Driver implementation to update + * debugfs with roaming information + */ + +#include +#include +#include +#include +#include "qwlan_version.h" +#include "wmi_unified_param.h" +#include "wlan_osif_request_manager.h" + +/** + * hdd_roam_scan_stats_debugfs_dealloc() - Dealloc objects in hdd request mgr + * @priv: Pointer to private data of hdd request object + * + * Return: None + */ +static void hdd_roam_scan_stats_debugfs_dealloc(void *priv) +{ + struct hdd_roam_scan_stats_debugfs_priv *local_priv = priv; + struct wmi_roam_scan_stats_res *roam_scan_stats_res; + + if (!local_priv) + return; + + roam_scan_stats_res = local_priv->roam_scan_stats_res; + local_priv->roam_scan_stats_res = NULL; + + qdf_mem_free(roam_scan_stats_res); +} + +/** + * hdd_roam_scan_stats_cb() - Call back invoked from roam scan stats evt + * @context: cookie to get request object + * @res: roam scan stats response from firmware + * + * Return: None + */ +static void +hdd_roam_scan_stats_cb(void *context, struct wmi_roam_scan_stats_res *res) +{ + struct osif_request *request; + struct hdd_roam_scan_stats_debugfs_priv *priv; + struct wmi_roam_scan_stats_res *stats_res; + uint32_t total_len; + + hdd_enter(); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete roam scan stats request"); + return; + } + + if (!res) { + hdd_err("Invalid response"); + goto end; + } + + priv = osif_request_priv(request); + + total_len = sizeof(*res) + res->num_roam_scans * + sizeof(struct wmi_roam_scan_stats_params); + + stats_res = qdf_mem_malloc(total_len); + if (!stats_res) { + hdd_err("No memory for response"); + goto end; + } + + qdf_mem_copy(stats_res, res, total_len); + priv->roam_scan_stats_res = stats_res; + +end: + osif_request_complete(request); + osif_request_put(request); + + hdd_exit(); +} + +/** + * hdd_get_roam_scan_stats() - Get roam scan stats using request manager + * @hdd_ctx: hdd context + * @adapter: pointer to adapter + * + * Return: Pointer to struct wmi_roam_scan_stats_res which conatins response + * from firmware + */ +static struct +wmi_roam_scan_stats_res *hdd_get_roam_scan_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct wmi_roam_scan_stats_res *res; + struct wmi_roam_scan_stats_res *stats_res = NULL; + void *context; + struct osif_request *request; + struct hdd_roam_scan_stats_debugfs_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_FW_ROAM_STATS, + .dealloc = hdd_roam_scan_stats_debugfs_dealloc, + }; + QDF_STATUS status; + int ret; + uint32_t total_len; + + hdd_enter(); + + if (!wlan_hdd_validate_modules_state(hdd_ctx)) + return NULL; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return NULL; + } + + context = osif_request_cookie(request); + + status = sme_get_roam_scan_stats(hdd_ctx->mac_handle, + hdd_roam_scan_stats_cb, + context, adapter->vdev_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("roam scan stats request failed"); + goto cleanup; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("roam scan stats response time out"); + goto cleanup; + } + + priv = osif_request_priv(request); + res = priv->roam_scan_stats_res; + if (!res) { + hdd_err("Failure of roam scan stats response retrieval"); + goto cleanup; + } + + total_len = sizeof(*res) + res->num_roam_scans * + sizeof(struct wmi_roam_scan_stats_params); + + stats_res = qdf_mem_malloc(total_len); + if (!stats_res) { + hdd_err("No memory for response"); + goto cleanup; + } + + qdf_mem_copy(stats_res, res, total_len); + +cleanup: + osif_request_put(request); + hdd_exit(); + + return stats_res; +} + +/** + * hdd_roam_scan_trigger_to_str() - Get string for + * enum WMI_ROAM_TRIGGER_REASON_ID + * @roam_scan_trigger: roam scan trigger ID + * + * Return: Meaningful string from enum WMI_ROAM_TRIGGER_REASON_ID + */ +static char *hdd_roam_scan_trigger_to_str(uint32_t roam_scan_trigger) +{ + switch (roam_scan_trigger) { + case WMI_ROAM_TRIGGER_REASON_PER: + return "PER"; + case WMI_ROAM_TRIGGER_REASON_BMISS: + return "BEACON MISS"; + case WMI_ROAM_TRIGGER_REASON_LOW_RSSI: + return "LOW RSSI"; + case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI: + return "HIGH RSSI"; + case WMI_ROAM_TRIGGER_REASON_PERIODIC: + return "PERIODIC SCAN"; + case WMI_ROAM_TRIGGER_REASON_MAWC: + return "WMI_ROAM_TRIGGER_REASON_MAWC"; + case WMI_ROAM_TRIGGER_REASON_DENSE: + return "DENSE ENVIRONMENT"; + case WMI_ROAM_TRIGGER_REASON_BACKGROUND: + return "BACKGROUND SCAN"; + case WMI_ROAM_TRIGGER_REASON_FORCED: + return "FORCED SCAN"; + case WMI_ROAM_TRIGGER_REASON_BTM: + return "BTM TRIGGER"; + case WMI_ROAM_TRIGGER_REASON_UNIT_TEST: + return "TEST COMMMAND"; + default: + return "UNKNOWN REASON"; + } + return "UNKNOWN REASON"; +} + +/** + * hdd_roam_scan_trigger_value_to_str() - Get trigger value string for + * enum WMI_ROAM_TRIGGER_REASON_ID + * @roam_scan_trigger: roam scan trigger ID + * @bool: output pointer to hold whether to print trigger value + * + * Return: Meaningful string from trigger value + */ +static char *hdd_roam_scan_trigger_value(uint32_t roam_scan_trigger, + bool *print) +{ + *print = true; + + switch (roam_scan_trigger) { + case WMI_ROAM_TRIGGER_REASON_PER: + return "percentage"; + case WMI_ROAM_TRIGGER_REASON_LOW_RSSI: + return "dB"; + case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI: + return "dB"; + case WMI_ROAM_TRIGGER_REASON_PERIODIC: + return "ms"; + case WMI_ROAM_TRIGGER_REASON_DENSE: + return "(1 - Rx, 2 - Tx)"; + default: + *print = false; + return NULL; + } +} + +/** + * hdd_client_id_to_str() - Helper func to get meaninful string from client id + * @client_id: Id of the client which triggered roam scan in firmware + * + * Return: Meaningful string from enum WMI_SCAN_CLIENT_ID + */ +static char *hdd_client_id_to_str(uint32_t client_id) +{ + switch (client_id) { + case WMI_SCAN_CLIENT_NLO: + return "PNO"; + case WMI_SCAN_CLIENT_EXTSCAN: + return "GSCAN"; + case WMI_SCAN_CLIENT_ROAM: + return "ROAM"; + case WMI_SCAN_CLIENT_P2P: + return "P2P"; + case WMI_SCAN_CLIENT_LPI: + return "LPI"; + case WMI_SCAN_CLIENT_NAN: + return "NAN"; + case WMI_SCAN_CLIENT_ANQP: + return "ANQP"; + case WMI_SCAN_CLIENT_OBSS: + return "OBSS"; + case WMI_SCAN_CLIENT_PLM: + return "PLM"; + case WMI_SCAN_CLIENT_HOST: + return "HOST"; + default: + return "UNKNOWN"; + } + return "UNKNOWN"; +} + +/** + * hdd_roam_scan_trigger() - Print roam scan trigger info into buffer + * @scan: roam scan event data + * @buf: buffer to write roam scan trigger info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +hdd_roam_scan_trigger(struct wmi_roam_scan_stats_params *scan, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t length = 0; + int ret; + char *str; + bool print_trigger_value; + + ret = scnprintf(buf, buf_avail_len, + "Trigger reason is %s\n", + hdd_roam_scan_trigger_to_str(scan->trigger_id)); + if (ret <= 0) + return length; + + length = ret; + + str = hdd_roam_scan_trigger_value(scan->trigger_id, + &print_trigger_value); + if (!print_trigger_value || !str) + return length; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + return length; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "Trigger value is: %u %s\n", + scan->trigger_value, str); + if (ret <= 0) + return length; + + length += ret; + return length; +} + +/** + * hdd_roam_scan_chan() - Print roam scan chan freq info into buffer + * @scan: roam scan event data + * @buf: buffer to write roam scan freq info + * @buf_avail_len: available buffer length + * + * Return: No.of bytes populated by this function in buffer + */ +static ssize_t +hdd_roam_scan_chan(struct wmi_roam_scan_stats_params *scan, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t length = 0; + uint32_t i; + int ret; + + ret = scnprintf(buf, buf_avail_len, + "Num of scan channels: %u\n" + "scan channel list:", + scan->num_scan_chans); + if (ret <= 0) + return length; + + length = ret; + + for (i = 0; i < scan->num_scan_chans; i++) { + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + return length; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "%u ", scan->scan_freqs[i]); + if (ret <= 0) + return length; + + length += ret; + } + + return length; +} + +/** + * wlan_hdd_update_roam_stats() - Internal function to get roam scan stats + * @hdd_ctx: hdd context + * @adapter: pointer to adapter + * @buf: buffer to hold the stats + * @len: maximum available length in response buffer + * + * Return: Size of formatted roam scan response stats + */ +static ssize_t +wlan_hdd_update_roam_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t length = 0; + struct wmi_roam_scan_stats_res *roam_stats; + struct wmi_roam_scan_stats_params *scan; + int ret; + int rsi; /* roam scan iterator */ + int rci; /* roam candidate iterator */ + + roam_stats = hdd_get_roam_scan_stats(hdd_ctx, adapter); + if (!roam_stats) { + hdd_err("Couldn't get roam stats"); + ret = scnprintf(buf, buf_avail_len, + "Failed to fetch roam stats\n"); + if (ret <= 0) + return length; + length += ret; + return length; + } + + ret = scnprintf(buf, buf_avail_len, + "\n\nStats of last %u roam scans\n", + roam_stats->num_roam_scans); + if (ret <= 0) + goto free_mem; + length += ret; + + for (rsi = 0; rsi < roam_stats->num_roam_scans; rsi++) { + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + scan = &roam_stats->roam_scan[rsi]; + ret = scnprintf(buf + length, buf_avail_len - length, + "\nRoam scan[%u] details\n", rsi); + if (ret <= 0) + goto free_mem; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "This scan is triggered by \"%s\" scan client\n", + hdd_client_id_to_str(scan->client_id)); + + if (ret <= 0) + goto free_mem; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + length += hdd_roam_scan_trigger(scan, buf + length, + buf_avail_len - length); + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + length += hdd_roam_scan_chan(scan, buf + length, + buf_avail_len - length); + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "\nRoam Scan time: 0x%llx\n", + roam_stats->roam_scan[rsi].time_stamp); + if (ret <= 0) + goto free_mem; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + if (scan->is_roam_successful) { + ret = scnprintf(buf + length, + buf_avail_len - length, + "\nSTA roamed from " + QDF_FULL_MAC_FMT " to " + QDF_FULL_MAC_FMT "\n", + QDF_FULL_MAC_REF(scan->old_bssid), + QDF_FULL_MAC_REF(scan->new_bssid)); + } else { + ret = scnprintf(buf + length, + buf_avail_len - length, + "\nSTA is connected to " QDF_FULL_MAC_FMT + " before and after scan, not roamed\n", + QDF_FULL_MAC_REF(scan->old_bssid)); + } + if (ret <= 0) + goto free_mem; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + "Roam candidate details\n"); + if (ret <= 0) + goto free_mem; + length += ret; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + ret = scnprintf(buf + length, buf_avail_len - length, + " BSSID FREQ SCORE RSSI\n"); + if (ret <= 0) + goto free_mem; + length += ret; + + for (rci = 0; rci < scan->num_roam_candidates; rci++) { + uint8_t *bssid = scan->cand[rci].bssid; + + if (length >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + length = buf_avail_len; + goto free_mem; + } + + ret = scnprintf(buf + length, + buf_avail_len - length, + QDF_FULL_MAC_FMT " %4u %3u %3u\n", + QDF_FULL_MAC_REF(bssid), + scan->cand[rci].freq, + scan->cand[rci].score, + scan->cand[rci].rssi); + if (ret <= 0) + goto free_mem; + length += ret; + } + } + +free_mem: + qdf_mem_free(roam_stats); + return length; +} + +ssize_t +wlan_hdd_debugfs_update_roam_stats(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint8_t *buf, ssize_t buf_avail_len) +{ + ssize_t len = 0; + int ret_val; + + hdd_enter(); + + len = wlan_hdd_current_time_info_debugfs(buf, buf_avail_len - len); + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + if (adapter->device_mode != QDF_STA_MODE) { + ret_val = scnprintf(buf + len, buf_avail_len - len, + "Interface is not in STA Mode\n"); + if (ret_val <= 0) + return len; + + len += ret_val; + return len; + } + + if (len >= buf_avail_len) { + hdd_err("No sufficient buf_avail_len"); + return buf_avail_len; + } + len += wlan_hdd_update_roam_stats(hdd_ctx, adapter, buf + len, + buf_avail_len - len); + + hdd_exit(); + + return len; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_disa.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_disa.c new file mode 100644 index 0000000000000000000000000000000000000000..d021e400da071253568d16112fa6094c247a5ae2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_disa.c @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_disa.c + * + * WLAN Host Device Driver file for DISA certification + * + */ + +#include "wlan_hdd_disa.h" +#include "osif_sync.h" +#include "wlan_disa_ucfg_api.h" +#include "wlan_osif_request_manager.h" +#include "sme_api.h" +#include + +#define WLAN_WAIT_TIME_ENCRYPT_DECRYPT 1000 + + +/** + * hdd_encrypt_decrypt_msg_context - hdd encrypt/decrypt message context + * @status: status of response. 0: no error, -ENOMEM: unable to allocate + * memory for the response payload + * @request: encrypt/decrypt request + * @response: encrypt/decrypt response + */ +struct hdd_encrypt_decrypt_msg_context { + int status; + struct disa_encrypt_decrypt_req_params request; + struct disa_encrypt_decrypt_resp_params response; +}; + +/** + * hdd_encrypt_decrypt_msg_cb () - encrypt/decrypt response message handler + * @cookie: hdd request cookie + * @resp: encrypt/decrypt response parameters + * + * Return: none + */ +static void hdd_encrypt_decrypt_msg_cb(void *cookie, + struct disa_encrypt_decrypt_resp_params *resp) +{ + struct osif_request *request; + struct hdd_encrypt_decrypt_msg_context *context; + + hdd_enter(); + + if (!resp) { + hdd_err("rsp params is NULL"); + return; + } + + request = osif_request_get(cookie); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + print_hex_dump(KERN_INFO, "Data in hdd_encrypt_decrypt_msg_cb: ", + DUMP_PREFIX_NONE, 16, 1, + resp->data, + resp->data_len, 0); + + hdd_debug("vdev_id: %d status:%d data_length: %d", + resp->vdev_id, + resp->status, + resp->data_len); + + context = osif_request_priv(request); + context->response = *resp; + context->status = 0; + if (resp->data_len) { + context->response.data = + qdf_mem_malloc(sizeof(uint8_t) * + resp->data_len); + if (!context->response.data) { + hdd_err("memory allocation failed"); + context->status = -ENOMEM; + } else { + qdf_mem_copy(context->response.data, + resp->data, + resp->data_len); + } + } else { + /* make sure we don't have a rogue pointer */ + context->response.data = NULL; + } + + osif_request_complete(request); + osif_request_put(request); + hdd_exit(); +} + +/** + * hdd_post_encrypt_decrypt_msg_rsp () - send encrypt/decrypt data to user space + * @encrypt_decrypt_rsp_params: encrypt/decrypt response parameters + * + * Return: none + */ +static int hdd_post_encrypt_decrypt_msg_rsp(struct hdd_context *hdd_ctx, + struct disa_encrypt_decrypt_resp_params *resp) +{ + struct sk_buff *skb; + uint32_t nl_buf_len; + + hdd_enter(); + + nl_buf_len = resp->data_len + NLA_HDRLEN; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + if (resp->data_len) { + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA, + resp->data_len, resp->data)) { + hdd_err("put fail"); + goto nla_put_failure; + } + } + + cfg80211_vendor_cmd_reply(skb); + hdd_exit(); + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +static const struct nla_policy +encrypt_decrypt_policy[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION] = { + .type = NLA_FLAG}, + [QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID] = { + .type = NLA_U8}, +}; + +/** + * hdd_fill_encrypt_decrypt_params () - parses data from user space + * and fills encrypt/decrypt parameters + * @encrypt_decrypt_params: encrypt/decrypt request parameters + * @adapter : adapter context + * @data: Pointer to data + * @data_len: Data length + * + Return: 0 on success, negative errno on failure + */ +static int +hdd_fill_encrypt_decrypt_params(struct disa_encrypt_decrypt_req_params + *encrypt_decrypt_params, + struct hdd_adapter *adapter, + const void *data, + int data_len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX + 1]; + uint8_t len, mac_hdr_len; + uint8_t *tmp; + uint8_t fc[2]; + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX, + data, data_len, encrypt_decrypt_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + encrypt_decrypt_params->vdev_id = adapter->vdev_id; + hdd_debug("vdev_id: %d", encrypt_decrypt_params->vdev_id); + + if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION]) { + hdd_err("attr flag NEEDS_DECRYPTION not present"); + encrypt_decrypt_params->key_flag = WMI_ENCRYPT; + } else { + hdd_err("attr flag NEEDS_DECRYPTION present"); + encrypt_decrypt_params->key_flag = WMI_DECRYPT; + } + hdd_debug("Key flag: %d", encrypt_decrypt_params->key_flag); + + if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID]) { + hdd_err("attr key id failed"); + return -EINVAL; + } + encrypt_decrypt_params->key_idx = nla_get_u8(tb + [QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID]); + hdd_debug("Key Idx: %d", encrypt_decrypt_params->key_idx); + + if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER]) { + hdd_err("attr Cipher failed"); + return -EINVAL; + } + encrypt_decrypt_params->key_cipher = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER]); + hdd_debug("key_cipher: %d", encrypt_decrypt_params->key_cipher); + + if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK]) { + hdd_err("attr TK failed"); + return -EINVAL; + } + encrypt_decrypt_params->key_len = + nla_len(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK]); + if (!encrypt_decrypt_params->key_len) { + hdd_err("Invalid TK length"); + return -EINVAL; + } + hdd_debug("Key len: %d", encrypt_decrypt_params->key_len); + + if (encrypt_decrypt_params->key_len > SIR_MAC_MAX_KEY_LENGTH) + encrypt_decrypt_params->key_len = SIR_MAC_MAX_KEY_LENGTH; + + tmp = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK]); + + qdf_mem_copy(encrypt_decrypt_params->key_data, tmp, + encrypt_decrypt_params->key_len); + + print_hex_dump(KERN_INFO, "Key : ", DUMP_PREFIX_NONE, 16, 1, + &encrypt_decrypt_params->key_data, + encrypt_decrypt_params->key_len, 0); + + if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN]) { + hdd_err("attr PN failed"); + return -EINVAL; + } + len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN]); + if (!len || len > sizeof(encrypt_decrypt_params->pn)) { + hdd_err("Invalid PN length %u", len); + return -EINVAL; + } + + tmp = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN]); + + qdf_mem_copy(encrypt_decrypt_params->pn, tmp, len); + + print_hex_dump(KERN_INFO, "PN received : ", DUMP_PREFIX_NONE, 16, 1, + &encrypt_decrypt_params->pn, len, 0); + + if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA]) { + hdd_err("attr header failed"); + return -EINVAL; + } + len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA]); + if (len < MIN_MAC_HEADER_LEN) { + hdd_err("Invalid header and payload length %u", len); + return -EINVAL; + } + + hdd_debug("Header and Payload length: %d", len); + + tmp = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA]); + + print_hex_dump(KERN_INFO, "Header and Payload received: ", + DUMP_PREFIX_NONE, 16, 1, + tmp, len, 0); + + mac_hdr_len = MIN_MAC_HEADER_LEN; + + /* + * Check to find out address 4. Address 4 is present if ToDS and FromDS + * are 1 and data representation is little endian. + */ + fc[1] = *tmp; + fc[0] = *(tmp + 1); + if ((fc[0] & 0x03) == 0x03) { + hdd_err("Address 4 is present"); + mac_hdr_len += QDF_MAC_ADDR_SIZE; + } + + /* + * Check to find out Qos control field. Qos control field is present + * if msb of subtype field is 1 and data representation is + * little endian. + */ + if (fc[1] & 0x80) { + hdd_err("Qos control is present"); + mac_hdr_len += QOS_CONTROL_LEN; + } + + hdd_debug("mac_hdr_len: %d", mac_hdr_len); + + if (len < mac_hdr_len) { + hdd_err("Invalid header and payload length %u", len); + return -EINVAL; + } + qdf_mem_copy(encrypt_decrypt_params->mac_header, + tmp, mac_hdr_len); + + print_hex_dump(KERN_INFO, "Header received in request: ", + DUMP_PREFIX_NONE, 16, 1, + encrypt_decrypt_params->mac_header, + mac_hdr_len, 0); + + encrypt_decrypt_params->data_len = + len - mac_hdr_len; + + hdd_debug("Payload length: %d", encrypt_decrypt_params->data_len); + + if (encrypt_decrypt_params->data_len) { + encrypt_decrypt_params->data = + qdf_mem_malloc(sizeof(uint8_t) * + encrypt_decrypt_params->data_len); + + if (!encrypt_decrypt_params->data) { + hdd_err("memory allocation failed"); + return -ENOMEM; + } + + qdf_mem_copy(encrypt_decrypt_params->data, + tmp + mac_hdr_len, + encrypt_decrypt_params->data_len); + + print_hex_dump(KERN_INFO, "Data received in request: ", + DUMP_PREFIX_NONE, 16, 1, + encrypt_decrypt_params->data, + encrypt_decrypt_params->data_len, 0); + } + + return 0; +} + +static void hdd_encrypt_decrypt_context_dealloc(void *priv) +{ + struct hdd_encrypt_decrypt_msg_context *context = priv; + + qdf_mem_free(context->request.data); + qdf_mem_free(context->response.data); +} + +/** + * hdd_encrypt_decrypt_msg () - process encrypt/decrypt message + * @adapter : adapter context + * @hdd_ctx: hdd context + * @data: Pointer to data + * @data_len: Data length + * + Return: 0 on success, negative errno on failure + */ +static int hdd_encrypt_decrypt_msg(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + const void *data, + int data_len) +{ + QDF_STATUS qdf_status; + int ret; + void *cookie; + struct osif_request *request; + struct hdd_encrypt_decrypt_msg_context *context; + static const struct osif_request_params params = { + .priv_size = sizeof(*context), + .timeout_ms = WLAN_WAIT_TIME_ENCRYPT_DECRYPT, + .dealloc = hdd_encrypt_decrypt_context_dealloc, + }; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + context = osif_request_priv(request); + + ret = hdd_fill_encrypt_decrypt_params(&context->request, adapter, + data, data_len); + if (ret) + goto cleanup; + + cookie = osif_request_cookie(request); + + qdf_status = ucfg_disa_encrypt_decrypt_req(hdd_ctx->psoc, + &context->request, + hdd_encrypt_decrypt_msg_cb, + cookie); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Unable to post encrypt/decrypt message"); + ret = -EINVAL; + goto cleanup; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("Target response timed out"); + goto cleanup; + } + + ret = context->status; + if (ret) { + hdd_err("Target response processing failed"); + goto cleanup; + } + + ret = hdd_post_encrypt_decrypt_msg_rsp(hdd_ctx, &context->response); + if (ret) + hdd_err("Failed to post encrypt/decrypt message response"); + +cleanup: + osif_request_put(request); + + hdd_exit(); + return ret; +} + +/** + * __wlan_hdd_cfg80211_encrypt_decrypt_msg () - Encrypt/Decrypt msg + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_encrypt_decrypt_msg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = NULL; + int ret; + bool is_bmps_enabled; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + ucfg_mlme_is_bmps_enabled(hdd_ctx->psoc, &is_bmps_enabled); + if (is_bmps_enabled) { + hdd_debug("DISA is not supported when PS is enabled"); + return -EINVAL; + } + + ret = hdd_encrypt_decrypt_msg(adapter, hdd_ctx, data, data_len); + + return ret; +} + +/** + * wlan_hdd_cfg80211_encrypt_decrypt_msg () - Encrypt/Decrypt msg + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_encrypt_decrypt_msg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_encrypt_decrypt_msg(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_disa.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_disa.h new file mode 100644 index 0000000000000000000000000000000000000000..2116ea14ecdf6316c5b35c9def99a23462795fb2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_disa.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_HDD_DISA_H +#define _WLAN_HDD_DISA_H + +#include "wlan_hdd_main.h" +#include "sir_api.h" + +#ifdef WLAN_FEATURE_DISA +/** + * wlan_hdd_cfg80211_encrypt_decrypt_msg () - Encrypt/Decrypt msg + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_encrypt_decrypt_msg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); +#endif + +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_driver_ops.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_driver_ops.c new file mode 100644 index 0000000000000000000000000000000000000000..a52a5c9f5796434ecbbbe2580e516c2a0e1c1d02 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_driver_ops.c @@ -0,0 +1,2048 @@ +/* + * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "cds_api.h" +#include "qdf_status.h" +#include "qdf_lock.h" +#include "cds_sched.h" +#include "osdep.h" +#include "hif.h" +#include "htc.h" +#include "epping_main.h" +#include "osif_sync.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_power.h" +#include "wlan_logging_sock_svc.h" +#include "wma_api.h" +#include "wlan_hdd_napi.h" +#include "wlan_policy_mgr_api.h" +#include "qwlan_version.h" +#include "bmi.h" +#include +#include "cdp_txrx_bus.h" +#include "cdp_txrx_misc.h" +#include "pld_common.h" +#include "wlan_hdd_driver_ops.h" +#include "wlan_ipa_ucfg_api.h" +#include "wlan_hdd_debugfs.h" +#include "cfg_ucfg_api.h" +#include +#include +#include + +#ifdef MODULE +#define WLAN_MODULE_NAME module_name(THIS_MODULE) +#else +#define WLAN_MODULE_NAME "wlan" +#endif + +#define DISABLE_KRAIT_IDLE_PS_VAL 1 + +#define SSR_MAX_FAIL_CNT 3 +static uint8_t re_init_fail_cnt, probe_fail_cnt; + +/* An atomic flag to check if SSR cleanup has been done or not */ +static qdf_atomic_t is_recovery_cleanup_done; + +/* + * In BMI Phase we are only sending small chunk (256 bytes) of the FW image at + * a time, and wait for the completion interrupt to start the next transfer. + * During this phase, the KRAIT is entering IDLE/StandAlone(SA) Power Save(PS). + * The delay incurred for resuming from IDLE/SA PS is huge during driver load. + * So prevent APPS IDLE/SA PS durint driver load for reducing interrupt latency. + */ + +static inline void hdd_request_pm_qos(struct device *dev, int val) +{ + pld_request_pm_qos(dev, val); +} + +static inline void hdd_remove_pm_qos(struct device *dev) +{ + pld_remove_pm_qos(dev); +} + +/** + * hdd_get_bandwidth_level() - get current bandwidth level + * @data: Context + * + * Return: current bandwidth level + */ +static int hdd_get_bandwidth_level(void *data) +{ + int ret = PLD_BUS_WIDTH_NONE; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (hdd_ctx) + ret = hdd_get_current_throughput_level(hdd_ctx); + + return ret; +} + +#ifdef DP_MEM_PRE_ALLOC + +/** + * hdd_get_consistent_mem_unaligned() - API to get consistent unaligned mem + * @size: Size of memory required + * @paddr: Pointer to paddr to be filled in by API + * @ring_type: Pointer to ring type for which consistent memory is needed + * + * Return: Virtual address of consistent memory on success, else null + */ +static inline +void *hdd_get_consistent_mem_unaligned(size_t size, + qdf_dma_addr_t *paddr, + uint32_t ring_type) +{ + return hdd_get_prealloc_dma_mem_unaligned(size, paddr, ring_type); +} + +static inline +void hdd_put_consistent_mem_unaligned(void *vaddr) +{ + hdd_put_prealloc_dma_mem_unaligned(vaddr); +} +#endif + +/** + * hdd_set_recovery_in_progress() - API to set recovery in progress + * @data: Context + * @val: Value to set + * + * Return: None + */ +static void hdd_set_recovery_in_progress(void *data, uint8_t val) +{ + cds_set_recovery_in_progress(val); +} + +/** + * hdd_is_driver_unloading() - API to query if driver is unloading + * @data: Private Data + * + * Return: True/False + */ +static bool hdd_is_driver_unloading(void *data) +{ + return cds_is_driver_unloading(); +} + +/** + * hdd_is_load_or_unload_in_progress() - API to query if driver is + * loading/unloading + * @data: Private Data + * + * Return: bool + */ +static bool hdd_is_load_or_unload_in_progress(void *data) +{ + return cds_is_load_or_unload_in_progress(); +} + +/** + * hdd_is_recovery_in_progress() - API to query if recovery in progress + * @data: Private Data + * + * Return: bool + */ +static bool hdd_is_recovery_in_progress(void *data) +{ + return cds_is_driver_recovering(); +} + +/** + * hdd_is_target_ready() - API to query if target is in ready state + * @data: Private Data + * + * Return: bool + */ +static bool hdd_is_target_ready(void *data) +{ + return cds_is_target_ready(); +} + +/** + * hdd_hif_init_driver_state_callbacks() - API to initialize HIF callbacks + * @data: Private Data + * @cbk: HIF Driver State callbacks + * + * HIF should be independent of CDS calls. Pass CDS Callbacks to HIF, HIF will + * call the callbacks. + * + * Return: void + */ +static void hdd_hif_init_driver_state_callbacks(void *data, + struct hif_driver_state_callbacks *cbk) +{ + cbk->context = data; + cbk->set_recovery_in_progress = hdd_set_recovery_in_progress; + cbk->is_recovery_in_progress = hdd_is_recovery_in_progress; + cbk->is_load_unload_in_progress = hdd_is_load_or_unload_in_progress; + cbk->is_driver_unloading = hdd_is_driver_unloading; + cbk->is_target_ready = hdd_is_target_ready; + cbk->get_bandwidth_level = hdd_get_bandwidth_level; +#ifdef DP_MEM_PRE_ALLOC + cbk->prealloc_get_consistent_mem_unaligned = + hdd_get_consistent_mem_unaligned; + cbk->prealloc_put_consistent_mem_unaligned = + hdd_put_consistent_mem_unaligned; +#endif +} + +#ifdef FORCE_WAKE +void hdd_set_hif_init_phase(struct hif_opaque_softc *hif_ctx, + bool hal_init_phase) +{ + hif_srng_init_phase(hif_ctx, hal_init_phase); +} +#endif /* FORCE_WAKE */ + +/** + + * hdd_hif_set_attribute() - API to set CE attribute if memory is limited + * @hif_ctx: hif context + * + * Return: None + */ +#ifdef SLUB_MEM_OPTIMIZE +static void hdd_hif_set_attribute(struct hif_opaque_softc *hif_ctx) +{ + hif_set_attribute(hif_ctx, HIF_LOWDESC_CE_NO_PKTLOG_CFG); +} +#else +static void hdd_hif_set_attribute(struct hif_opaque_softc *hif_ctx) +{} +#endif + +/** + + * hdd_hif_set_ce_max_yield_time() - Wrapper API to set CE max yield time + * @hif_ctx: hif context + * @bus_type: underlying bus type + * @ce_service_max_yield_time: max yield time to be set + * + * Return: None + */ +#if defined(CONFIG_SLUB_DEBUG_ON) +#define CE_SNOC_MAX_YIELD_TIME_US 2000 + +static void hdd_hif_set_ce_max_yield_time(struct hif_opaque_softc *hif_ctx, + enum qdf_bus_type bus_type, + uint32_t ce_service_max_yield_time) +{ + if (bus_type == QDF_BUS_TYPE_SNOC && + ce_service_max_yield_time < CE_SNOC_MAX_YIELD_TIME_US) + ce_service_max_yield_time = CE_SNOC_MAX_YIELD_TIME_US; + + hif_set_ce_service_max_yield_time(hif_ctx, ce_service_max_yield_time); +} + +#else +static void hdd_hif_set_ce_max_yield_time(struct hif_opaque_softc *hif_ctx, + enum qdf_bus_type bus_type, + uint32_t ce_service_max_yield_time) +{ + hif_set_ce_service_max_yield_time(hif_ctx, ce_service_max_yield_time); +} +#endif + +/** + * hdd_init_cds_hif_context() - API to set CDS HIF Context + * @hif: HIF Context + * + * Return: success/failure + */ +static int hdd_init_cds_hif_context(void *hif) +{ + QDF_STATUS status; + + status = cds_set_context(QDF_MODULE_ID_HIF, hif); + + if (status) + return -ENOENT; + + return 0; +} + +/** + * hdd_deinit_cds_hif_context() - API to clear CDS HIF COntext + * + * Return: None + */ +static void hdd_deinit_cds_hif_context(void) +{ + QDF_STATUS status; + + status = cds_set_context(QDF_MODULE_ID_HIF, NULL); + + if (status) + hdd_err("Failed to reset CDS HIF Context"); +} + +/** + * to_bus_type() - Map PLD bus type to low level bus type + * @bus_type: PLD bus type + * + * Map PLD bus type to low level bus type. + * + * Return: low level bus type. + */ +static enum qdf_bus_type to_bus_type(enum pld_bus_type bus_type) +{ + switch (bus_type) { + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_PCIE: + return QDF_BUS_TYPE_PCI; + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + return QDF_BUS_TYPE_SNOC; + case PLD_BUS_TYPE_SDIO: + return QDF_BUS_TYPE_SDIO; + case PLD_BUS_TYPE_USB: + return QDF_BUS_TYPE_USB; + case PLD_BUS_TYPE_IPCI: + return QDF_BUS_TYPE_IPCI; + default: + return QDF_BUS_TYPE_NONE; + } +} + +int hdd_hif_open(struct device *dev, void *bdev, const struct hif_bus_id *bid, + enum qdf_bus_type bus_type, bool reinit) +{ + QDF_STATUS status; + int ret = 0; + struct hif_opaque_softc *hif_ctx; + qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + struct hif_driver_state_callbacks cbk; + uint32_t mode = cds_get_conparam(); + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) { + hdd_err("hdd_ctx error"); + return -EFAULT; + } + + hdd_hif_init_driver_state_callbacks(dev, &cbk); + + hif_ctx = hif_open(qdf_ctx, mode, bus_type, &cbk, hdd_ctx->psoc); + if (!hif_ctx) { + hdd_err("hif_open error"); + return -ENOMEM; + } + + ret = hdd_init_cds_hif_context(hif_ctx); + if (ret) { + hdd_err("Failed to set global HIF CDS Context err: %d", ret); + goto err_hif_close; + } + + hdd_hif_set_attribute(hif_ctx); + + status = hif_enable(hif_ctx, dev, bdev, bid, bus_type, + (reinit == true) ? HIF_ENABLE_TYPE_REINIT : + HIF_ENABLE_TYPE_PROBE); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("hif_enable failed status: %d, reinit: %d", + status, reinit); + + ret = qdf_status_to_os_return(status); + goto err_hif_close; + } else { + cds_set_target_ready(true); + ret = hdd_napi_create(); + hdd_debug("hdd_napi_create returned: %d", ret); + if (ret == 0) + hdd_debug("NAPI: no instances are created"); + else if (ret < 0) { + hdd_err("NAPI creation error, rc: 0x%x, reinit: %d", + ret, reinit); + ret = -EFAULT; + goto mark_target_not_ready; + } else { + hdd_napi_event(NAPI_EVT_INI_FILE, + (void *)hdd_ctx->napi_enable); + } + } + + hdd_hif_set_ce_max_yield_time( + hif_ctx, bus_type, + cfg_get(hdd_ctx->psoc, + CFG_DP_CE_SERVICE_MAX_YIELD_TIME)); + ucfg_pmo_psoc_set_hif_handle(hdd_ctx->psoc, hif_ctx); + hif_set_ce_service_max_rx_ind_flush(hif_ctx, + cfg_get(hdd_ctx->psoc, + CFG_DP_CE_SERVICE_MAX_RX_IND_FLUSH)); + return 0; + +mark_target_not_ready: + cds_set_target_ready(false); + +err_hif_close: + hdd_deinit_cds_hif_context(); + hif_close(hif_ctx); + return ret; +} + +void hdd_hif_close(struct hdd_context *hdd_ctx, void *hif_ctx) +{ + if (!hdd_ctx) { + hdd_err("hdd_ctx error"); + return; + } + + if (!hif_ctx) + return; + + cds_set_target_ready(false); + hif_disable(hif_ctx, HIF_DISABLE_TYPE_REMOVE); + + hdd_napi_destroy(true); + + hdd_deinit_cds_hif_context(); + hif_close(hif_ctx); + + ucfg_pmo_psoc_set_hif_handle(hdd_ctx->psoc, NULL); +} + +/** + * hdd_init_qdf_ctx() - API to initialize global QDF Device structure + * @dev: Device Pointer + * @bdev: Bus Device pointer + * @bus_type: Underlying bus type + * @bid: Bus id passed by platform driver + * + * Return: 0 - success, < 0 - failure + */ +static int hdd_init_qdf_ctx(struct device *dev, void *bdev, + enum qdf_bus_type bus_type, + const struct hif_bus_id *bid) +{ + qdf_device_t qdf_dev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!qdf_dev) { + hdd_err("Invalid QDF device"); + return -EINVAL; + } + + qdf_dev->dev = dev; + qdf_dev->drv_hdl = bdev; + qdf_dev->bus_type = bus_type; + qdf_dev->bid = bid; + + if (cds_smmu_mem_map_setup(qdf_dev, ucfg_ipa_is_present()) != + QDF_STATUS_SUCCESS) { + hdd_err("cds_smmu_mem_map_setup() failed"); + return -EFAULT; + } + + return 0; +} + +/** + * check_for_probe_defer() - API to check return value + * @ret: Return Value + * + * Return: return -EPROBE_DEFER to platform driver if return value + * is -ENOMEM. Platform driver will try to re-probe. + */ +#ifdef MODULE +static int check_for_probe_defer(int ret) +{ + return ret; +} +#else +static int check_for_probe_defer(int ret) +{ + if (ret == -ENOMEM) + return -EPROBE_DEFER; + return ret; +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) +static void hdd_abort_system_suspend(struct device *dev) +{ + qdf_pm_system_wakeup(); +} +#else +static void hdd_abort_system_suspend(struct device *dev) +{ +} +#endif + +int hdd_soc_idle_restart_lock(struct device *dev) +{ + hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_IDLE_RESTART); + + hdd_abort_system_suspend(dev); + + return 0; +} + +void hdd_soc_idle_restart_unlock(void) +{ + hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_IDLE_RESTART); +} + +static void hdd_soc_load_lock(struct device *dev) +{ + hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT); + hdd_request_pm_qos(dev, DISABLE_KRAIT_IDLE_PS_VAL); +} + +static void hdd_soc_load_unlock(struct device *dev) +{ + hdd_remove_pm_qos(dev); + hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT); +} + +static int __hdd_soc_probe(struct device *dev, + void *bdev, + const struct hif_bus_id *bid, + enum qdf_bus_type bus_type) +{ + struct hdd_context *hdd_ctx; + QDF_STATUS status; + int errno; + + hdd_info("probing driver"); + + hdd_soc_load_lock(dev); + cds_set_load_in_progress(true); + cds_set_driver_in_bad_state(false); + cds_set_recovery_in_progress(false); + + errno = hdd_init_qdf_ctx(dev, bdev, bus_type, bid); + if (errno) + goto unlock; + + dp_prealloc_init(); + + hdd_ctx = hdd_context_create(dev); + if (IS_ERR(hdd_ctx)) { + errno = PTR_ERR(hdd_ctx); + goto assert_fail_count; + } + + errno = hdd_wlan_startup(hdd_ctx); + if (errno) + goto hdd_context_destroy; + + status = hdd_psoc_create_vdevs(hdd_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + errno = qdf_status_to_os_return(status); + goto wlan_exit; + } + + probe_fail_cnt = 0; + cds_set_driver_loaded(true); + cds_set_load_in_progress(false); + hdd_start_complete(0); + + hdd_soc_load_unlock(dev); + + return 0; + +wlan_exit: + hdd_wlan_exit(hdd_ctx); + +hdd_context_destroy: + hdd_context_destroy(hdd_ctx); + +assert_fail_count: + probe_fail_cnt++; + hdd_err("consecutive probe failures:%u", probe_fail_cnt); + QDF_BUG(probe_fail_cnt < SSR_MAX_FAIL_CNT); + +unlock: + cds_set_load_in_progress(false); + hdd_soc_load_unlock(dev); + + return check_for_probe_defer(errno); +} + +/** + * hdd_soc_probe() - perform SoC probe + * @dev: kernel device being probed + * @bdev: bus device structure + * @bid: bus identifier for shared busses + * @bus_type: underlying bus type + * + * A SoC probe indicates new SoC hardware has become available and needs to be + * initialized. + * + * Return: Errno + */ +static int hdd_soc_probe(struct device *dev, + void *bdev, + const struct hif_bus_id *bid, + enum qdf_bus_type bus_type) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + hdd_info("probing driver"); + + errno = osif_psoc_sync_create_and_trans(&psoc_sync); + if (errno) + return errno; + + osif_psoc_sync_register(dev, psoc_sync); + errno = __hdd_soc_probe(dev, bdev, bid, bus_type); + if (errno) + goto destroy_sync; + + osif_psoc_sync_trans_stop(psoc_sync); + + return 0; + +destroy_sync: + osif_psoc_sync_unregister(dev); + osif_psoc_sync_wait_for_ops(psoc_sync); + + osif_psoc_sync_trans_stop(psoc_sync); + osif_psoc_sync_destroy(psoc_sync); + + return errno; +} + +static int __hdd_soc_recovery_reinit(struct device *dev, + void *bdev, + const struct hif_bus_id *bid, + enum qdf_bus_type bus_type) +{ + int errno; + + hdd_info("re-probing driver"); + + hdd_soc_load_lock(dev); + cds_set_driver_in_bad_state(false); + + errno = hdd_init_qdf_ctx(dev, bdev, bus_type, bid); + if (errno) + goto unlock; + + errno = hdd_wlan_re_init(); + if (errno) { + re_init_fail_cnt++; + goto assert_fail_count; + } + + re_init_fail_cnt = 0; + + /* + * In case of SSR within SSR we have seen the race + * where the reinit is successful and fw down is received + * which sets the recovery in progress. Now as reinit is + * successful we reset the recovery in progress here. + * So check if FW is down then don't reset the recovery + * in progress + */ + if (!qdf_is_fw_down()) + cds_set_recovery_in_progress(false); + + hdd_soc_load_unlock(dev); + hdd_start_complete(0); + + return 0; + +assert_fail_count: + hdd_err("consecutive reinit failures:%u", re_init_fail_cnt); + QDF_BUG(re_init_fail_cnt < SSR_MAX_FAIL_CNT); + +unlock: + cds_set_driver_in_bad_state(true); + hdd_soc_load_unlock(dev); + hdd_start_complete(errno); + + return check_for_probe_defer(errno); +} + +/** + * hdd_soc_recovery_reinit() - perform PDR/SSR SoC reinit + * @dev: the kernel device being re-initialized + * @bdev: bus device structure + * @bid: bus identifier for shared busses + * @bus_type: underlying bus type + * + * When communication with firmware breaks down, a SoC recovery process kicks in + * with two phases: shutdown and reinit. + * + * SSR reinit is similar to a 'probe' but happens in response to an SSR + * shutdown. The idea is to re-initialize the SoC to as close to its old, + * pre-communications-breakdown configuration as possible. This is completely + * transparent from a userspace point of view. + * + * Return: Errno + */ +static int hdd_soc_recovery_reinit(struct device *dev, + void *bdev, + const struct hif_bus_id *bid, + enum qdf_bus_type bus_type) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + /* SSR transition is initiated at the beginning of soc shutdown */ + errno = osif_psoc_sync_trans_resume(dev, &psoc_sync); + QDF_BUG(!errno); + if (errno) + return errno; + + errno = __hdd_soc_recovery_reinit(dev, bdev, bid, bus_type); + + + osif_psoc_sync_trans_stop(psoc_sync); + + return errno; +} + +static void __hdd_soc_remove(struct device *dev) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + void *hif_ctx; + + QDF_BUG(hdd_ctx); + if (!hdd_ctx) + return; + + pr_info("%s: Removing driver v%s\n", WLAN_MODULE_NAME, + QWLAN_VERSIONSTR); + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (hif_ctx) { + /* + * Trigger runtime sync resume before setting unload in progress + * such that resume can happen successfully + */ + hif_pm_runtime_sync_resume(hif_ctx); + } + + cds_set_driver_loaded(false); + cds_set_unload_in_progress(true); + + if (!hdd_wait_for_debugfs_threads_completion()) + hdd_warn("Debugfs threads are still active attempting driver unload anyway"); + + if (hdd_get_conparam() == QDF_GLOBAL_EPPING_MODE) { + hdd_wlan_stop_modules(hdd_ctx, false); + qdf_nbuf_deinit_replenish_timer(); + } else { + hdd_wlan_exit(hdd_ctx); + } + + hdd_context_destroy(hdd_ctx); + + cds_set_driver_in_bad_state(false); + cds_set_unload_in_progress(false); + + pr_info("%s: Driver De-initialized\n", WLAN_MODULE_NAME); + + dp_prealloc_deinit(); +} + +/** + * hdd_soc_remove() - perform SoC remove + * @dev: the kernel device being removed + * + * A SoC remove indicates the attached SoC hardware is about to go away and + * needs to be cleaned up. + * + * Return: void + */ +static void hdd_soc_remove(struct device *dev) +{ + __hdd_soc_remove(dev); +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +/** + * hdd_wlan_ssr_shutdown_event()- send ssr shutdown state + * + * This Function send send ssr shutdown state diag event + * + * Return: void. + */ +static void hdd_wlan_ssr_shutdown_event(void) +{ + WLAN_HOST_DIAG_EVENT_DEF(ssr_shutdown, + struct host_event_wlan_ssr_shutdown); + qdf_mem_zero(&ssr_shutdown, sizeof(ssr_shutdown)); + ssr_shutdown.status = SSR_SUB_SYSTEM_SHUTDOWN; + WLAN_HOST_DIAG_EVENT_REPORT(&ssr_shutdown, + EVENT_WLAN_SSR_SHUTDOWN_SUBSYSTEM); +} +#else +static inline void hdd_wlan_ssr_shutdown_event(void) { } +#endif + +/** + * hdd_send_hang_data() - Send hang data to userspace + * @data: Hang data + * + * Return: None + */ +static void hdd_send_hang_data(uint8_t *data, size_t data_len) +{ + enum qdf_hang_reason reason = QDF_REASON_UNSPECIFIED; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) + return; + + cds_get_recovery_reason(&reason); + cds_reset_recovery_reason(); + wlan_hdd_send_hang_reason_event(hdd_ctx, reason, data, data_len); +} + +/** + * hdd_psoc_shutdown_notify() - notify the various interested parties that the + * soc is starting recovery shutdown + * @hdd_ctx: the HDD context corresponding to the soc undergoing shutdown + * + * Return: None + */ +static void hdd_psoc_shutdown_notify(struct hdd_context *hdd_ctx) +{ + wlan_cfg80211_cleanup_scan_queue(hdd_ctx->pdev, NULL); + + if (ucfg_ipa_is_enabled()) { + ucfg_ipa_uc_force_pipe_shutdown(hdd_ctx->pdev); + + if (pld_is_fw_rejuvenate(hdd_ctx->parent_dev) || + pld_is_pdr(hdd_ctx->parent_dev)) + ucfg_ipa_fw_rejuvenate_send_msg(hdd_ctx->pdev); + } + + cds_shutdown_notifier_call(); + cds_shutdown_notifier_purge(); + + hdd_wlan_ssr_shutdown_event(); +} + +/** + * hdd_soc_recovery_cleanup() - Perform SSR related cleanup activities. + * + * This function will perform cleanup activities related to when driver + * undergoes SSR. Activities inclues stopping idle timer and invoking shutdown + * notifier. + * + * Return: None + */ +static void hdd_soc_recovery_cleanup(void) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return; + } + + /* cancel/flush any pending/active idle shutdown work */ + hdd_psoc_idle_timer_stop(hdd_ctx); + hdd_bus_bw_compute_timer_stop(hdd_ctx); + + /* nothing to do if the soc is already unloaded */ + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_info("Driver modules are already closed"); + return; + } + + if (cds_is_load_or_unload_in_progress()) { + hdd_info("Load/unload in progress, ignore SSR shutdown"); + return; + } + + hdd_psoc_shutdown_notify(hdd_ctx); +} + +static void __hdd_soc_recovery_shutdown(void) +{ + struct hdd_context *hdd_ctx; + void *hif_ctx; + + /* recovery starts via firmware down indication; ensure we got one */ + QDF_BUG(cds_is_driver_recovering()); + hdd_init_start_completion(); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return; + } + + /* + * Perform SSR related cleanup if it has not already been done as a + * part of receiving the uevent. + */ + if (!qdf_atomic_read(&is_recovery_cleanup_done)) + hdd_soc_recovery_cleanup(); + else + qdf_atomic_set(&is_recovery_cleanup_done, 0); + + if (!hdd_wait_for_debugfs_threads_completion()) + hdd_err("Debufs threads are still pending, attempting SSR anyway"); + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("Failed to get HIF context, ignore SSR shutdown"); + return; + } + + /* mask the host controller interrupts */ + hif_mask_interrupt_call(hif_ctx); + + if (!QDF_IS_EPPING_ENABLED(cds_get_conparam())) { + hif_disable_isr(hif_ctx); + hdd_wlan_shutdown(); + } +} + +/** + * hdd_soc_recovery_shutdown() - perform PDR/SSR SoC shutdown + * @dev: the device to shutdown + * + * When communication with firmware breaks down, a SoC recovery process kicks in + * with two phases: shutdown and reinit. + * + * SSR shutdown is similar to a 'remove' but without communication with + * firmware. The idea is to retain as much SoC configuration as possible, so it + * can be re-initialized to the same state after a reset. This is completely + * transparent from a userspace point of view. + * + * Return: void + */ +static void hdd_soc_recovery_shutdown(struct device *dev) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_trans_start_wait(dev, &psoc_sync); + if (errno) + return; + + osif_psoc_sync_wait_for_ops(psoc_sync); + + __hdd_soc_recovery_shutdown(); + + /* SSR transition is concluded at the end of soc re-init */ +} + +/** + * wlan_hdd_crash_shutdown() - wlan_hdd_crash_shutdown + * + * HDD crash shutdown function: This function is called by + * platform driver's crash shutdown routine + * + * Return: void + */ +static void wlan_hdd_crash_shutdown(void) +{ + QDF_STATUS ret; + WMA_HANDLE wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + hdd_err("wma_handle is null"); + return; + } + + /* + * When kernel panic happen, if WiFi FW is still active + * it may cause NOC errors/memory corruption, to avoid + * this, inject a fw crash first. + * send crash_inject to FW directly, because we are now + * in an atomic context, and preempt has been disabled, + * MCThread won't be scheduled at the moment, at the same + * time, TargetFailure event wont't be received after inject + * crash due to the same reason. + */ + ret = wma_crash_inject(wma_handle, RECOVERY_SIM_ASSERT, 0); + if (QDF_IS_STATUS_ERROR(ret)) { + hdd_err("Failed to send crash inject:%d", ret); + return; + } + + hif_crash_shutdown(cds_get_context(QDF_MODULE_ID_HIF)); +} + +/** + * wlan_hdd_notify_handler() - wlan_hdd_notify_handler + * + * This function is called by the platform driver to notify the + * COEX + * + * @state: state + * + * Return: void + */ +static void wlan_hdd_notify_handler(int state) +{ + if (!QDF_IS_EPPING_ENABLED(cds_get_conparam())) { + int ret; + + ret = hdd_wlan_notify_modem_power_state(state); + if (ret < 0) + hdd_err("Fail to send notify"); + } +} + +static int hdd_to_pmo_interface_pause(enum wow_interface_pause hdd_pause, + enum pmo_wow_interface_pause *pmo_pause) +{ + switch (hdd_pause) { + case WOW_INTERFACE_PAUSE_DEFAULT: + *pmo_pause = PMO_WOW_INTERFACE_PAUSE_DEFAULT; + break; + case WOW_INTERFACE_PAUSE_ENABLE: + *pmo_pause = PMO_WOW_INTERFACE_PAUSE_ENABLE; + break; + case WOW_INTERFACE_PAUSE_DISABLE: + *pmo_pause = PMO_WOW_INTERFACE_PAUSE_DISABLE; + break; + default: + hdd_err("Invalid interface pause: %d", hdd_pause); + return -EINVAL; + } + + return 0; +} + +static int hdd_to_pmo_resume_trigger(enum wow_resume_trigger hdd_trigger, + enum pmo_wow_resume_trigger *pmo_trigger) +{ + switch (hdd_trigger) { + case WOW_RESUME_TRIGGER_DEFAULT: + *pmo_trigger = PMO_WOW_RESUME_TRIGGER_DEFAULT; + break; + case WOW_RESUME_TRIGGER_HTC_WAKEUP: + *pmo_trigger = PMO_WOW_RESUME_TRIGGER_HTC_WAKEUP; + break; + case WOW_RESUME_TRIGGER_GPIO: + *pmo_trigger = PMO_WOW_RESUME_TRIGGER_GPIO; + break; + default: + hdd_err("Invalid resume trigger: %d", hdd_trigger); + return -EINVAL; + } + + return 0; +} + +static int +hdd_to_pmo_wow_enable_params(struct wow_enable_params *in_params, + struct pmo_wow_enable_params *out_params) +{ + int err; + + /* unit-test suspend */ + out_params->is_unit_test = in_params->is_unit_test; + + /* interface pause */ + err = hdd_to_pmo_interface_pause(in_params->interface_pause, + &out_params->interface_pause); + if (err) + return err; + + /* resume trigger */ + err = hdd_to_pmo_resume_trigger(in_params->resume_trigger, + &out_params->resume_trigger); + if (err) + return err; + + return 0; +} + +/** + * __wlan_hdd_bus_suspend() - handles platform supsend + * @wow_params: collection of wow enable override parameters + * + * Does precondtion validation. Ensures that a subsystem restart isn't in + * progress. Ensures that no load or unload is in progress. Does: + * data path suspend + * component (pmo) suspend + * hif (bus) suspend + * + * Return: 0 for success, -EFAULT for null pointers, + * -EBUSY or -EAGAIN if another opperation is in progress and + * wlan will not be ready to suspend in time. + */ +static int __wlan_hdd_bus_suspend(struct wow_enable_params wow_params) +{ + int err; + QDF_STATUS status; + struct hdd_context *hdd_ctx; + void *hif_ctx; + void *dp_soc; + struct pmo_wow_enable_params pmo_params; + int pending; + + hdd_info("starting bus suspend"); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + err = wlan_hdd_validate_context(hdd_ctx); + if (err) + return err; + + /* Wait for the stop module if already in progress */ + hdd_psoc_idle_timer_stop(hdd_ctx); + + /* If Wifi is off, return success for system suspend */ + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver Module closed; skipping suspend"); + return 0; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("Failed to get hif context"); + return -EINVAL; + } + + err = hdd_to_pmo_wow_enable_params(&wow_params, &pmo_params); + if (err) { + hdd_err("Invalid WoW enable parameters: %d", err); + return err; + } + + dp_soc = cds_get_context(QDF_MODULE_ID_SOC); + err = qdf_status_to_os_return(cdp_bus_suspend(dp_soc, OL_TXRX_PDEV_ID)); + if (err) { + hdd_err("Failed cdp bus suspend: %d", err); + return err; + } + + if (ucfg_ipa_is_tx_pending(hdd_ctx->pdev)) { + hdd_err("failed due to pending IPA TX comps"); + err = -EBUSY; + goto resume_cdp; + } + + err = hif_bus_early_suspend(hif_ctx); + if (err) { + hdd_err("Failed hif bus early suspend"); + goto resume_cdp; + } + + status = ucfg_pmo_psoc_bus_suspend_req(hdd_ctx->psoc, + QDF_SYSTEM_SUSPEND, + &pmo_params); + err = qdf_status_to_os_return(status); + if (err) { + hdd_err("Failed pmo bus suspend: %d", status); + goto late_hif_resume; + } + + hif_system_pm_set_state_suspended(hif_ctx); + + err = hif_bus_suspend(hif_ctx); + if (err) { + hdd_err("Failed hif bus suspend: %d", err); + goto resume_pmo; + } + + pending = cdp_rx_get_pending(cds_get_context(QDF_MODULE_ID_SOC)); + if (pending) { + hdd_debug("Prevent suspend, RX frame pending %d", pending); + err = -EBUSY; + goto resume_hif; + } + + /* + * Remove bus votes at the very end, after making sure there are no + * pending bus transactions from WLAN SOC for TX/RX. + */ + pld_request_bus_bandwidth(hdd_ctx->parent_dev, PLD_BUS_WIDTH_NONE); + + hdd_info("bus suspend succeeded"); + return 0; + +resume_hif: + status = hif_bus_resume(hif_ctx); + QDF_BUG(QDF_IS_STATUS_SUCCESS(status)); + +resume_pmo: + status = ucfg_pmo_psoc_bus_resume_req(hdd_ctx->psoc, + QDF_SYSTEM_SUSPEND); + QDF_BUG(QDF_IS_STATUS_SUCCESS(status)); + +late_hif_resume: + status = hif_bus_late_resume(hif_ctx); + QDF_BUG(QDF_IS_STATUS_SUCCESS(status)); + +resume_cdp: + status = cdp_bus_resume(dp_soc, OL_TXRX_PDEV_ID); + QDF_BUG(QDF_IS_STATUS_SUCCESS(status)); + hif_system_pm_set_state_on(hif_ctx); + + return err; +} + +int wlan_hdd_bus_suspend(void) +{ + struct wow_enable_params default_params = {0}; + + return __wlan_hdd_bus_suspend(default_params); +} + +#ifdef WLAN_SUSPEND_RESUME_TEST +int wlan_hdd_unit_test_bus_suspend(struct wow_enable_params wow_params) +{ + return __wlan_hdd_bus_suspend(wow_params); +} +#endif + +/** + * wlan_hdd_bus_suspend_noirq() - handle .suspend_noirq callback + * + * This function is called by the platform driver to complete the + * bus suspend callback when device interrupts are disabled by kernel. + * Call HIF and WMA suspend_noirq callbacks to make sure there is no + * wake up pending from FW before allowing suspend. + * + * Return: 0 for success and -EBUSY if FW is requesting wake up + */ +int wlan_hdd_bus_suspend_noirq(void) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + void *hif_ctx; + int errno; + uint32_t pending_events; + + hdd_debug("start bus_suspend_noirq"); + + if (!hdd_ctx) { + hdd_err_rl("hdd context is NULL"); + return -ENODEV; + } + + /* If Wifi is off, return success for system suspend */ + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver module closed; skip bus-noirq suspend"); + return 0; + } + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) { + hdd_err("Invalid HDD context: errno %d", errno); + return errno; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("hif_ctx is null"); + return -EINVAL; + } + + errno = hif_bus_suspend_noirq(hif_ctx); + if (errno) + goto done; + + errno = ucfg_pmo_psoc_is_target_wake_up_received(hdd_ctx->psoc); + if (errno == -EAGAIN) { + hdd_err("Firmware attempting wakeup, try again"); + wlan_hdd_inc_suspend_stats(hdd_ctx, + SUSPEND_FAIL_INITIAL_WAKEUP); + } + if (errno) + goto resume_hif_noirq; + + pending_events = wma_critical_events_in_flight(); + if (pending_events) { + hdd_err("%d critical event(s) in flight; try again", + pending_events); + errno = -EAGAIN; + goto resume_hif_noirq; + } + + hdd_ctx->suspend_resume_stats.suspends++; + + hdd_debug("bus_suspend_noirq done"); + return 0; + +resume_hif_noirq: + QDF_BUG(!hif_bus_resume_noirq(hif_ctx)); + +done: + hdd_err("suspend_noirq failed, status: %d", errno); + + return errno; +} + +/** + * wlan_hdd_bus_resume() - handles platform resume + * + * Does precondtion validation. Ensures that a subsystem restart isn't in + * progress. Ensures that no load or unload is in progress. Ensures that + * it has valid pointers for the required contexts. + * Calls into hif to resume the bus opperation. + * Calls into wma to handshake with firmware and notify it that the bus is up. + * Calls into ol_txrx for symetry. + * Failures are treated as catastrophic. + * + * return: error code or 0 for success + */ +int wlan_hdd_bus_resume(void) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + void *hif_ctx; + int status; + QDF_STATUS qdf_status; + void *dp_soc; + + if (cds_is_driver_recovering()) + return 0; + + hdd_info("starting bus resume"); + + if (!hdd_ctx) { + hdd_err_rl("hdd context is NULL"); + return -ENODEV; + } + + /* If Wifi is off, return success for system resume */ + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver Module closed; return success"); + return 0; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) { + hdd_err("Invalid hdd context"); + return status; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("Failed to get hif context"); + return -EINVAL; + } + + /* + * Add bus votes at the beginning, before making sure there are any + * bus transactions from WLAN SOC for TX/RX. + */ + if (hdd_is_any_adapter_connected(hdd_ctx)) { + pld_request_bus_bandwidth(hdd_ctx->parent_dev, + PLD_BUS_WIDTH_MEDIUM); + } else { + pld_request_bus_bandwidth(hdd_ctx->parent_dev, + PLD_BUS_WIDTH_NONE); + } + + status = hif_bus_resume(hif_ctx); + if (status) { + hdd_err("Failed hif bus resume"); + goto out; + } + + hif_system_pm_set_state_resuming(hif_ctx); + + qdf_status = ucfg_pmo_psoc_bus_resume_req(hdd_ctx->psoc, + QDF_SYSTEM_SUSPEND); + status = qdf_status_to_os_return(qdf_status); + if (status) { + hdd_err("Failed pmo bus resume"); + goto out; + } + + hif_system_pm_set_state_on(hif_ctx); + + status = hif_bus_late_resume(hif_ctx); + if (status) { + hdd_err("Failed hif bus late resume"); + goto out; + } + + dp_soc = cds_get_context(QDF_MODULE_ID_SOC); + qdf_status = cdp_bus_resume(dp_soc, OL_TXRX_PDEV_ID); + status = qdf_status_to_os_return(qdf_status); + if (status) { + hdd_err("Failed cdp bus resume"); + goto out; + } + + hdd_info("bus resume succeeded"); + return 0; + +out: + hif_system_pm_set_state_suspended(hif_ctx); + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state() || + cds_is_fw_down()) + return 0; + + QDF_BUG(false); + + return status; +} + +/** + * wlan_hdd_bus_resume_noirq(): handle bus resume no irq + * + * This function is called by the platform driver to do bus + * resume no IRQ before calling resume callback. Call WMA and HIF + * layers to complete the resume_noirq. + * + * Return: 0 for success and negative error code for failure + */ +int wlan_hdd_bus_resume_noirq(void) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + void *hif_ctx; + int status; + QDF_STATUS qdf_status; + + hdd_debug("starting bus_resume_noirq"); + if (cds_is_driver_recovering()) + return 0; + + if (!hdd_ctx) { + hdd_err_rl("hdd context is NULL"); + return -ENODEV; + } + + /* If Wifi is off, return success for system resume */ + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver Module closed return success"); + return 0; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) { + hdd_err("Invalid HDD context: %d", status); + return status; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) + return -EINVAL; + + qdf_status = ucfg_pmo_psoc_clear_target_wake_up(hdd_ctx->psoc); + QDF_BUG(!qdf_status); + + status = hif_bus_resume_noirq(hif_ctx); + QDF_BUG(!status); + + hdd_debug("bus_resume_noirq done"); + + return status; +} + +/** + * wlan_hdd_bus_reset_resume() - resume wlan bus after reset + * + * This function is called to tell the driver that the device has been resumed + * and it has also been reset. The driver should redo any necessary + * initialization. It is mainly used by the USB bus + * + * Return: int 0 for success, non zero for failure + */ +static int wlan_hdd_bus_reset_resume(void) +{ + struct hif_opaque_softc *scn = cds_get_context(QDF_MODULE_ID_HIF); + + if (!scn) { + hdd_err("Failed to get HIF context"); + return -EFAULT; + } + + return hif_bus_reset_resume(scn); +} + +#ifdef FEATURE_RUNTIME_PM +/** + * hdd_pld_runtime_suspend_cb() - Runtime suspend callback from PMO + * + * Return: 0 on success or error value otherwise + */ +static int hdd_pld_runtime_suspend_cb(void) +{ + qdf_device_t qdf_dev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!qdf_dev) { + hdd_err("Invalid context"); + return -EINVAL; + } + + return pld_auto_suspend(qdf_dev->dev); +} + +/** + * wlan_hdd_runtime_suspend() - suspend the wlan bus without apps suspend + * + * Each layer is responsible for its own suspend actions. wma_runtime_suspend + * takes care of the parts of the 802.11 suspend that we want to do for runtime + * suspend. + * + * Return: 0 or errno + */ +static int wlan_hdd_runtime_suspend(struct device *dev) +{ + int err; + QDF_STATUS status; + struct hdd_context *hdd_ctx; + qdf_time_t delta; + + hdd_debug("Starting runtime suspend"); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + err = wlan_hdd_validate_context(hdd_ctx); + if (err) + return err; + + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver module closed skipping runtime suspend"); + return 0; + } + + if (ucfg_scan_get_pdev_status(hdd_ctx->pdev) != + SCAN_NOT_IN_PROGRESS) { + hdd_debug("Scan in progress, ignore runtime suspend"); + return -EBUSY; + } + + if (ucfg_ipa_is_tx_pending(hdd_ctx->pdev)) { + hdd_debug("IPA TX comps pending, ignore rtpm suspend"); + return -EBUSY; + } + + status = ucfg_pmo_psoc_bus_runtime_suspend(hdd_ctx->psoc, + hdd_pld_runtime_suspend_cb); + err = qdf_status_to_os_return(status); + + hdd_ctx->runtime_suspend_done_time_stamp = + qdf_get_log_timestamp_usecs(); + delta = hdd_ctx->runtime_suspend_done_time_stamp - + hdd_ctx->runtime_resume_start_time_stamp; + + if (hdd_ctx->runtime_suspend_done_time_stamp > + hdd_ctx->runtime_resume_start_time_stamp) + hdd_debug("Runtime suspend done result: %d total cxpc up time %lu microseconds", + err, delta); + + if (status == QDF_STATUS_SUCCESS) + hdd_bus_bw_compute_timer_stop(hdd_ctx); + + hdd_debug("Runtime suspend done result: %d", err); + + return err; +} + +/** + * hdd_pld_runtime_resume_cb() - Runtime resume callback from PMO + * + * Return: 0 on success or error value otherwise + */ +static int hdd_pld_runtime_resume_cb(void) +{ + qdf_device_t qdf_dev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!qdf_dev) { + hdd_err("Invalid context"); + return -EINVAL; + } + + return pld_auto_resume(qdf_dev->dev); +} + +/** + * wlan_hdd_runtime_resume() - resume the wlan bus from runtime suspend + * + * Sets the runtime pm state and coordinates resume between hif wma and + * ol_txrx. + * + * Return: success since failure is a bug + */ +static int wlan_hdd_runtime_resume(struct device *dev) +{ + struct hdd_context *hdd_ctx; + QDF_STATUS status; + qdf_time_t delta; + + hdd_debug("Starting runtime resume"); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (cds_is_driver_recovering()) { + hdd_debug("Recovery in progress, state:0x%x", + cds_get_driver_state()); + return 0; + } + + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver module closed skipping runtime resume"); + return 0; + } + + hdd_ctx->runtime_resume_start_time_stamp = + qdf_get_log_timestamp_usecs(); + delta = hdd_ctx->runtime_resume_start_time_stamp - + hdd_ctx->runtime_suspend_done_time_stamp; + hdd_debug("Starting runtime resume total cxpc down time %lu microseconds", + delta); + + status = ucfg_pmo_psoc_bus_runtime_resume(hdd_ctx->psoc, + hdd_pld_runtime_resume_cb); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("PMO Runtime resume failed: %d", status); + } else { + if (policy_mgr_get_connection_count(hdd_ctx->psoc)) + hdd_bus_bw_compute_timer_try_start(hdd_ctx); + } + + hdd_debug("Runtime resume done"); + + return 0; +} +#endif + +/** + * wlan_hdd_pld_probe() - probe function registered to PLD + * @dev: device + * @pld_bus_type: PLD bus type + * @bdev: bus device structure + * @id: bus identifier for shared busses + * + * Return: 0 on success + */ +static int wlan_hdd_pld_probe(struct device *dev, + enum pld_bus_type pld_bus_type, + void *bdev, + void *id) +{ + enum qdf_bus_type bus_type = to_bus_type(pld_bus_type); + + if (bus_type == QDF_BUS_TYPE_NONE) { + hdd_err("Invalid bus type %d->%d", pld_bus_type, bus_type); + return -EINVAL; + } + + return hdd_soc_probe(dev, bdev, id, bus_type); +} + +/** + * wlan_hdd_pld_remove() - remove function registered to PLD + * @dev: device to remove + * @pld_bus_type: PLD bus type + * + * Return: void + */ +static void wlan_hdd_pld_remove(struct device *dev, enum pld_bus_type bus_type) +{ + hdd_enter(); + + hdd_soc_remove(dev); + + hdd_exit(); +} + +/** + * wlan_hdd_pld_idle_shutdown() - wifi module idle shutdown after interface + * inactivity timeout has trigerred idle shutdown + * @dev: device to remove + * @pld_bus_type: PLD bus type + * + * Return: 0 for success and negative error code for failure + */ +static int wlan_hdd_pld_idle_shutdown(struct device *dev, + enum pld_bus_type bus_type) +{ + return hdd_psoc_idle_shutdown(dev); +} + +/** + * wlan_hdd_pld_idle_restart() - wifi module idle restart after idle shutdown + * @dev: device to remove + * @pld_bus_type: PLD bus type + * + * Return: 0 for success and negative error code for failure + */ +static int wlan_hdd_pld_idle_restart(struct device *dev, + enum pld_bus_type bus_type) +{ + return hdd_psoc_idle_restart(dev); +} + +/** + * wlan_hdd_pld_shutdown() - shutdown function registered to PLD + * @dev: device to shutdown + * @pld_bus_type: PLD bus type + * + * Return: void + */ +static void wlan_hdd_pld_shutdown(struct device *dev, + enum pld_bus_type bus_type) +{ + hdd_enter(); + + hdd_soc_recovery_shutdown(dev); + + hdd_exit(); +} + +/** + * wlan_hdd_pld_reinit() - reinit function registered to PLD + * @dev: device + * @pld_bus_type: PLD bus type + * @bdev: bus device structure + * @id: bus identifier for shared busses + * + * Return: 0 on success + */ +static int wlan_hdd_pld_reinit(struct device *dev, + enum pld_bus_type pld_bus_type, + void *bdev, + void *id) +{ + enum qdf_bus_type bus_type = to_bus_type(pld_bus_type); + + if (bus_type == QDF_BUS_TYPE_NONE) { + hdd_err("Invalid bus type %d->%d", pld_bus_type, bus_type); + return -EINVAL; + } + + return hdd_soc_recovery_reinit(dev, bdev, id, bus_type); +} + +/** + * wlan_hdd_pld_crash_shutdown() - crash_shutdown function registered to PLD + * @dev: device + * @pld_bus_type: PLD bus type + * + * Return: void + */ +static void wlan_hdd_pld_crash_shutdown(struct device *dev, + enum pld_bus_type bus_type) +{ + wlan_hdd_crash_shutdown(); +} + +/** + * wlan_hdd_pld_suspend() - suspend function registered to PLD + * @dev: device + * @pld_bus_type: PLD bus type + * @state: PM state + * + * Return: 0 on success + */ +static int wlan_hdd_pld_suspend(struct device *dev, + enum pld_bus_type bus_type, + pm_message_t state) + +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(dev, &psoc_sync); + if (errno) + return errno; + + errno = wlan_hdd_bus_suspend(); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * wlan_hdd_pld_resume() - resume function registered to PLD + * @dev: device + * @pld_bus_type: PLD bus type + * + * Return: 0 on success + */ +static int wlan_hdd_pld_resume(struct device *dev, + enum pld_bus_type bus_type) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(dev, &psoc_sync); + if (errno) + return errno; + + errno = wlan_hdd_bus_resume(); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * wlan_hdd_pld_suspend_noirq() - handle suspend no irq + * @dev: device + * @pld_bus_type: PLD bus type + * + * Complete the actions started by suspend(). Carry out any + * additional operations required for suspending the device that might be + * racing with its driver's interrupt handler, which is guaranteed not to + * run while suspend_noirq() is being executed. Make sure to resume device + * if FW has sent initial wake up message and expecting APPS to wake up. + * + * Return: 0 on success + */ +static int wlan_hdd_pld_suspend_noirq(struct device *dev, + enum pld_bus_type bus_type) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(dev, &psoc_sync); + if (errno) + return errno; + + errno = wlan_hdd_bus_suspend_noirq(); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * wlan_hdd_pld_resume_noirq() - handle resume no irq + * @dev: device + * @pld_bus_type: PLD bus type + * + * Prepare for the execution of resume() by carrying out any + * operations required for resuming the device that might be racing with + * its driver's interrupt handler, which is guaranteed not to run while + * resume_noirq() is being executed. Make sure to clear target initial + * wake up request such that next suspend can happen cleanly. + * + * Return: 0 on success + */ +static int wlan_hdd_pld_resume_noirq(struct device *dev, + enum pld_bus_type bus_type) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(dev, &psoc_sync); + if (errno) + return errno; + + errno = wlan_hdd_bus_resume_noirq(); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * wlan_hdd_pld_reset_resume() - reset resume function registered to PLD + * @dev: device + * @pld_bus_type: PLD bus type + * + * Return: 0 on success + */ +static int wlan_hdd_pld_reset_resume(struct device *dev, + enum pld_bus_type bus_type) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(dev, &psoc_sync); + if (errno) + return errno; + + errno = wlan_hdd_bus_reset_resume(); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * wlan_hdd_pld_notify_handler() - notify_handler function registered to PLD + * @dev: device + * @pld_bus_type: PLD bus type + * @state: Modem power state + * + * Return: void + */ +static void wlan_hdd_pld_notify_handler(struct device *dev, + enum pld_bus_type bus_type, + int state) +{ + wlan_hdd_notify_handler(state); +} + +/** + * wlan_hdd_pld_uevent() - platform uevent handler + * @dev: device on which the uevent occurred + * @event_data: uevent parameters + * + * Return: None + */ +static void +wlan_hdd_pld_uevent(struct device *dev, struct pld_uevent_data *event_data) +{ + struct qdf_notifer_data hang_evt_data; + enum qdf_hang_reason reason = QDF_REASON_UNSPECIFIED; + uint8_t bus_type; + + bus_type = pld_get_bus_type(dev); + + switch (event_data->uevent) { + case PLD_FW_DOWN: + hdd_info("Received firmware down indication"); + + cds_set_target_ready(false); + cds_set_recovery_in_progress(true); + + /* Notify external threads currently waiting on firmware + * by forcefully completing waiting events with a "reset" + * status. This will cause the event to fail early instead + * of timing out. + */ + qdf_complete_wait_events(); + + /* + * In case of some platforms, uevent will come to the driver in + * process context. In that case, it is safe to complete the + * SSR cleanup activities in the same context. In case of + * other platforms, it will be invoked in interrupt context. + * Performing the cleanup in interrupt context is not ideal, + * thus defer the cleanup to be done during + * hdd_soc_recovery_shutdown + */ + if (qdf_in_interrupt() || bus_type == PLD_BUS_TYPE_PCIE) + break; + + hdd_soc_recovery_cleanup(); + qdf_atomic_set(&is_recovery_cleanup_done, 1); + + break; + case PLD_FW_HANG_EVENT: + hdd_info("Received firmware hang event"); + cds_get_recovery_reason(&reason); + hang_evt_data.hang_data = + qdf_mem_malloc(QDF_HANG_EVENT_DATA_SIZE); + if (!hang_evt_data.hang_data) + return; + hang_evt_data.offset = 0; + qdf_hang_event_notifier_call(reason, &hang_evt_data); + hang_evt_data.offset = QDF_WLAN_HANG_FW_OFFSET; + if (event_data->hang_data.hang_event_data_len >= + QDF_HANG_EVENT_DATA_SIZE / 2) + event_data->hang_data.hang_event_data_len = + QDF_HANG_EVENT_DATA_SIZE / 2; + if (event_data->hang_data.hang_event_data_len) + qdf_mem_copy((hang_evt_data.hang_data + + hang_evt_data.offset), + event_data->hang_data.hang_event_data, + event_data->hang_data.hang_event_data_len); + + hdd_send_hang_data(hang_evt_data.hang_data, + QDF_HANG_EVENT_DATA_SIZE); + qdf_mem_free(hang_evt_data.hang_data); + + break; + default: + /* other events intentionally not handled */ + hdd_debug("Received uevent %d", event_data->uevent); + break; + } + +} + +#ifdef FEATURE_RUNTIME_PM +/** + * wlan_hdd_pld_runtime_suspend() - runtime suspend function registered to PLD + * @dev: device + * @pld_bus_type: PLD bus type + * + * Return: 0 on success + */ +static int wlan_hdd_pld_runtime_suspend(struct device *dev, + enum pld_bus_type bus_type) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(dev, &psoc_sync); + if (errno) + goto out; + + errno = wlan_hdd_runtime_suspend(dev); + + osif_psoc_sync_op_stop(psoc_sync); + +out: + /* If it returns other errno to kernel, it will treat + * it as critical issue, so all the future runtime + * PM api will return error, pm runtime can't be work + * anymore. Such case found in SSR. + */ + if (errno && errno != -EAGAIN && errno != -EBUSY) + errno = -EAGAIN; + return errno; +} + +/** + * wlan_hdd_pld_runtime_resume() - runtime resume function registered to PLD + * @dev: device + * @pld_bus_type: PLD bus type + * + * Return: 0 on success + */ +static int wlan_hdd_pld_runtime_resume(struct device *dev, + enum pld_bus_type bus_type) +{ + /* As opposite to suspend, Runtime PM resume can happen + * synchronously during driver shutdown or idle shutown, + * so remove PSOC sync protection here. + */ + return wlan_hdd_runtime_resume(dev); +} +#endif + +struct pld_driver_ops wlan_drv_ops = { + .probe = wlan_hdd_pld_probe, + .remove = wlan_hdd_pld_remove, + .idle_shutdown = wlan_hdd_pld_idle_shutdown, + .idle_restart = wlan_hdd_pld_idle_restart, + .shutdown = wlan_hdd_pld_shutdown, + .reinit = wlan_hdd_pld_reinit, + .crash_shutdown = wlan_hdd_pld_crash_shutdown, + .suspend = wlan_hdd_pld_suspend, + .resume = wlan_hdd_pld_resume, + .suspend_noirq = wlan_hdd_pld_suspend_noirq, + .resume_noirq = wlan_hdd_pld_resume_noirq, + .reset_resume = wlan_hdd_pld_reset_resume, + .modem_status = wlan_hdd_pld_notify_handler, + .uevent = wlan_hdd_pld_uevent, +#ifdef FEATURE_RUNTIME_PM + .runtime_suspend = wlan_hdd_pld_runtime_suspend, + .runtime_resume = wlan_hdd_pld_runtime_resume, +#endif +}; + +int wlan_hdd_register_driver(void) +{ + return pld_register_driver(&wlan_drv_ops); +} + +void wlan_hdd_unregister_driver(void) +{ + pld_unregister_driver(); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ext_scan.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ext_scan.c new file mode 100644 index 0000000000000000000000000000000000000000..3eec3dcfefaa5a2eac0c866e2651b8a388db2605 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ext_scan.c @@ -0,0 +1,4001 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_ext_scan.c + * + * WLAN Host Device Driver EXT SCAN feature implementation + * + */ + +#ifdef FEATURE_WLAN_EXTSCAN + +#include "osif_sync.h" +#include "wlan_hdd_ext_scan.h" +#include "wlan_hdd_regulatory.h" +#include "cds_utils.h" +#include "cds_sched.h" +#include +#include "wlan_extscan_ucfg_api.h" +#include "wlan_hdd_scan.h" + +/* amount of time to wait for a synchronous request/response operation */ +#define WLAN_WAIT_TIME_EXTSCAN 1000 + +/** + * struct hdd_ext_scan_context - hdd ext scan context + * @request_id: userspace-assigned ID associated with the request + * @response_event: Ext scan wait event + * @response_status: Status returned by FW in response to a request + * @ignore_cached_results: Flag to ignore cached results or not + * @context_lock: Spinlock to serialize all context accesses + * @capability_response: Ext scan capability response data from target + * @buckets_scanned: bitmask of buckets scanned in extscan cycle + */ +struct hdd_ext_scan_context { + uint32_t request_id; + int response_status; + bool ignore_cached_results; + struct completion response_event; + spinlock_t context_lock; + struct ext_scan_capabilities_response capability_response; + uint32_t buckets_scanned; +}; +static struct hdd_ext_scan_context ext_scan_context; + +static const struct nla_policy +wlan_hdd_pno_config_policy[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID] = { + .type = NLA_BINARY, + .len = IEEE80211_MAX_SSID_LEN + 1 + }, + [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS] = { + .type = NLA_U8 + }, + [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT] = { + .type = NLA_U8 + }, + [QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID] = { + .type = NLA_U32 + }, +}; + +/** + * wlan_hdd_cfg80211_extscan_get_capabilities_rsp() - response from target + * @hdd_ctx: Pointer to hdd context + * @data: Pointer to ext scan capabilities response from fw + * + * Return: None + */ +static void +wlan_hdd_cfg80211_extscan_get_capabilities_rsp(struct hdd_context *hdd_ctx, + struct ext_scan_capabilities_response *data) +{ + struct hdd_ext_scan_context *context; + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + context = &ext_scan_context; + + spin_lock(&context->context_lock); + /* validate response received from target*/ + if (context->request_id != data->requestId) { + spin_unlock(&context->context_lock); + hdd_err("Target response id did not match. request_id: %d response_id: %d", + context->request_id, data->requestId); + return; + } + + context->capability_response = *data; + complete(&context->response_event); + spin_unlock(&context->context_lock); +} + +/* + * define short names for the global vendor params + * used by hdd_extscan_nl_fill_bss() + */ +#define PARAM_TIME_STAMP \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP +#define PARAM_SSID \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID +#define PARAM_BSSID \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID +#define PARAM_CHANNEL \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL +#define PARAM_RSSI \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI +#define PARAM_RTT \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT +#define PARAM_RTT_SD \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD +#define PARAM_BEACON_PERIOD \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD +#define PARAM_CAPABILITY \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY +#define PARAM_IE_LENGTH \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_LENGTH +#define PARAM_IE_DATA \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_DATA + +/** hdd_extscan_nl_fill_bss() - extscan nl fill bss + * @skb: socket buffer + * @ap: bss information + * @idx: nesting index + * + * Return: 0 on success; error number otherwise + */ +static int hdd_extscan_nl_fill_bss(struct sk_buff *skb, tSirWifiScanResult *ap, + int idx) +{ + struct nlattr *nla_ap; + + nla_ap = nla_nest_start(skb, idx); + if (!nla_ap) + return -EINVAL; + + if (hdd_wlan_nla_put_u64(skb, PARAM_TIME_STAMP, ap->ts) || + nla_put(skb, PARAM_SSID, sizeof(ap->ssid), ap->ssid) || + nla_put(skb, PARAM_BSSID, sizeof(ap->bssid), ap->bssid.bytes) || + nla_put_u32(skb, PARAM_CHANNEL, ap->channel) || + nla_put_s32(skb, PARAM_RSSI, ap->rssi) || + nla_put_u32(skb, PARAM_RTT, ap->rtt) || + nla_put_u32(skb, PARAM_RTT_SD, ap->rtt_sd) || + nla_put_u16(skb, PARAM_BEACON_PERIOD, ap->beaconPeriod) || + nla_put_u16(skb, PARAM_CAPABILITY, ap->capability) || + nla_put_u16(skb, PARAM_IE_LENGTH, ap->ieLength)) { + hdd_err("put fail"); + return -EINVAL; + } + + if (ap->ieLength) + if (nla_put(skb, PARAM_IE_DATA, ap->ieLength, ap->ieData)) { + hdd_err("put fail"); + return -EINVAL; + } + + nla_nest_end(skb, nla_ap); + + return 0; +} +/* + * done with short names for the global vendor params + * used by hdd_extscan_nl_fill_bss() + */ +#undef PARAM_TIME_STAMP +#undef PARAM_SSID +#undef PARAM_BSSID +#undef PARAM_CHANNEL +#undef PARAM_RSSI +#undef PARAM_RTT +#undef PARAM_RTT_SD +#undef PARAM_BEACON_PERIOD +#undef PARAM_CAPABILITY +#undef PARAM_IE_LENGTH +#undef PARAM_IE_DATA + +/** + * wlan_hdd_cfg80211_extscan_cached_results_ind() - get cached results + * @hdd_ctx: hdd global context + * @data: cached results + * + * This function reads the cached results %data, populated the NL + * attributes and sends the NL event to the upper layer. + * + * Return: none + */ +static void +wlan_hdd_cfg80211_extscan_cached_results_ind(struct hdd_context *hdd_ctx, + struct extscan_cached_scan_results *data) +{ + struct sk_buff *skb = NULL; + struct hdd_ext_scan_context *context; + struct extscan_cached_scan_result *result; + tSirWifiScanResult *ap; + uint32_t i, j, nl_buf_len; + bool ignore_cached_results = false; + + /* ENTER() intentionally not used in a frequently invoked API */ + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + context = &ext_scan_context; + spin_lock(&context->context_lock); + ignore_cached_results = context->ignore_cached_results; + spin_unlock(&context->context_lock); + + if (ignore_cached_results) { + hdd_err("Ignore the cached results received after timeout"); + return; + } + +#define EXTSCAN_CACHED_NEST_HDRLEN NLA_HDRLEN +#define EXTSCAN_CACHED_NL_FIXED_TLV \ + ((sizeof(data->request_id) + NLA_HDRLEN) + \ + (sizeof(data->num_scan_ids) + NLA_HDRLEN) + \ + (sizeof(data->more_data) + NLA_HDRLEN)) +#define EXTSCAN_CACHED_NL_SCAN_ID_TLV \ + ((sizeof(result->scan_id) + NLA_HDRLEN) + \ + (sizeof(result->flags) + NLA_HDRLEN) + \ + (sizeof(result->num_results) + NLA_HDRLEN))+ \ + (sizeof(result->buckets_scanned) + NLA_HDRLEN) +#define EXTSCAN_CACHED_NL_SCAN_RESULTS_TLV \ + ((sizeof(ap->ts) + NLA_HDRLEN) + \ + (sizeof(ap->ssid) + NLA_HDRLEN) + \ + (sizeof(ap->bssid) + NLA_HDRLEN) + \ + (sizeof(ap->channel) + NLA_HDRLEN) + \ + (sizeof(ap->rssi) + NLA_HDRLEN) + \ + (sizeof(ap->rtt) + NLA_HDRLEN) + \ + (sizeof(ap->rtt_sd) + NLA_HDRLEN) + \ + (sizeof(ap->beaconPeriod) + NLA_HDRLEN) + \ + (sizeof(ap->capability) + NLA_HDRLEN) + \ + (sizeof(ap->ieLength) + NLA_HDRLEN)) +#define EXTSCAN_CACHED_NL_SCAN_RESULTS_IE_DATA_TLV \ + (ap->ieLength + NLA_HDRLEN) + + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += EXTSCAN_CACHED_NL_FIXED_TLV; + if (data->num_scan_ids) { + nl_buf_len += sizeof(result->scan_id) + NLA_HDRLEN; + nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; + result = &data->result[0]; + for (i = 0; i < data->num_scan_ids; i++) { + nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; + nl_buf_len += EXTSCAN_CACHED_NL_SCAN_ID_TLV; + nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; + + ap = &result->ap[0]; + for (j = 0; j < result->num_results; j++) { + nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; + nl_buf_len += + EXTSCAN_CACHED_NL_SCAN_RESULTS_TLV; + if (ap->ieLength) + nl_buf_len += + EXTSCAN_CACHED_NL_SCAN_RESULTS_IE_DATA_TLV; + ap++; + } + result++; + } + } + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + goto fail; + } + hdd_debug("Req Id %u Num_scan_ids %u More Data %u", + data->request_id, data->num_scan_ids, data->more_data); + + result = &data->result[0]; + for (i = 0; i < data->num_scan_ids; i++) { + hdd_debug("[i=%d] scan_id %u flags %u num_results %u buckets scanned %u", + i, result->scan_id, result->flags, result->num_results, + result->buckets_scanned); + + ap = &result->ap[0]; + for (j = 0; j < result->num_results; j++) { + /* + * Firmware returns timestamp from ext scan start till + * BSSID was cached (in micro seconds). Add this with + * time gap between system boot up to ext scan start + * to derive the time since boot when the + * BSSID was cached. + */ + ap->ts += hdd_ctx->ext_scan_start_since_boot; + hdd_debug("Timestamp %llu " + "Ssid: %s " + "Bssid (" QDF_MAC_ADDR_FMT ") " + "Channel %u " + "Rssi %d " + "RTT %u " + "RTT_SD %u " + "Beacon Period %u " + "Capability 0x%x " + "Ie length %d", + ap->ts, + ap->ssid, + QDF_MAC_ADDR_REF(ap->bssid.bytes), + ap->channel, + ap->rssi, + ap->rtt, + ap->rtt_sd, + ap->beaconPeriod, + ap->capability, + ap->ieLength); + ap++; + } + result++; + } + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, + data->request_id) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, + data->num_scan_ids) || + nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, + data->more_data)) { + hdd_err("put fail"); + goto fail; + } + + if (data->num_scan_ids) { + struct nlattr *nla_results; + + result = &data->result[0]; + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_SCAN_ID, + result->scan_id)) { + hdd_err("put fail"); + goto fail; + } + nla_results = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_LIST); + if (!nla_results) + goto fail; + + for (i = 0; i < data->num_scan_ids; i++) { + struct nlattr *nla_result; + struct nlattr *nla_aps; + + nla_result = nla_nest_start(skb, i); + if (!nla_result) + goto fail; + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_SCAN_ID, + result->scan_id) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_FLAGS, + result->flags) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_BUCKETS_SCANNED, + result->buckets_scanned) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, + result->num_results)) { + hdd_err("put fail"); + goto fail; + } + + nla_aps = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); + if (!nla_aps) + goto fail; + + ap = &result->ap[0]; + for (j = 0; j < result->num_results; j++) { + if (hdd_extscan_nl_fill_bss(skb, ap, j)) + goto fail; + + ap++; + } + nla_nest_end(skb, nla_aps); + nla_nest_end(skb, nla_result); + result++; + } + nla_nest_end(skb, nla_results); + } + + cfg80211_vendor_cmd_reply(skb); + + if (!data->more_data) { + spin_lock(&context->context_lock); + context->response_status = 0; + complete(&context->response_event); + spin_unlock(&context->context_lock); + } + return; + +fail: + if (skb) + kfree_skb(skb); + + spin_lock(&context->context_lock); + context->response_status = -EINVAL; + spin_unlock(&context->context_lock); +} + +/** + * wlan_hdd_cfg80211_extscan_hotlist_match_ind() - hot list match ind + * @hdd_ctx: Pointer to hdd context + * @data: Pointer to ext scan result event + * + * This callback execute in atomic context and must not invoke any + * blocking calls. + * + * Return: none + */ +static void +wlan_hdd_cfg80211_extscan_hotlist_match_ind(struct hdd_context *hdd_ctx, + struct extscan_hotlist_match *data) +{ + struct sk_buff *skb = NULL; + uint32_t i, index; + int flags = cds_get_gfp_flags(); + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + if (data->ap_found) + index = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND_INDEX; + else + index = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST_INDEX; + + skb = cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, + NULL, + EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, + index, flags); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + hdd_debug("Req Id: %u Num_APs: %u MoreData: %u ap_found: %u", + data->requestId, data->numOfAps, data->moreData, + data->ap_found); + + for (i = 0; i < data->numOfAps; i++) { + data->ap[i].ts = qdf_get_monotonic_boottime(); + + hdd_debug("[i=%d] Timestamp %llu " + "Ssid: %s " + "Bssid (" QDF_MAC_ADDR_FMT ") " + "Channel %u " + "Rssi %d " + "RTT %u " + "RTT_SD %u", + i, + data->ap[i].ts, + data->ap[i].ssid, + QDF_MAC_ADDR_REF(data->ap[i].bssid.bytes), + data->ap[i].channel, + data->ap[i].rssi, + data->ap[i].rtt, data->ap[i].rtt_sd); + } + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, + data->requestId) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, + data->numOfAps)) { + hdd_err("put fail"); + goto fail; + } + + if (data->numOfAps) { + struct nlattr *aps; + + aps = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); + if (!aps) + goto fail; + + for (i = 0; i < data->numOfAps; i++) { + struct nlattr *ap; + + ap = nla_nest_start(skb, i); + if (!ap) + goto fail; + + if (hdd_wlan_nla_put_u64(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP, + data->ap[i].ts) || + nla_put(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID, + sizeof(data->ap[i].ssid), + data->ap[i].ssid) || + nla_put(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID, + sizeof(data->ap[i].bssid), + data->ap[i].bssid.bytes) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL, + data->ap[i].channel) || + nla_put_s32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI, + data->ap[i].rssi) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT, + data->ap[i].rtt) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD, + data->ap[i].rtt_sd)) + goto fail; + + nla_nest_end(skb, ap); + } + nla_nest_end(skb, aps); + + if (nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, + data->moreData)) + goto fail; + } + + cfg80211_vendor_event(skb, flags); + hdd_exit(); + return; + +fail: + kfree_skb(skb); +} + +/** + * wlan_hdd_cfg80211_extscan_signif_wifi_change_results_ind() - + * significant wifi change results indication + * @hdd_ctx: Pointer to hdd context + * @data: Pointer to signif wifi change event + * + * This callback execute in atomic context and must not invoke any + * blocking calls. + * + * Return: none + */ +static void +wlan_hdd_cfg80211_extscan_signif_wifi_change_results_ind( + struct hdd_context *hdd_ctx, + tpSirWifiSignificantChangeEvent data) +{ + struct sk_buff *skb = NULL; + tSirWifiSignificantChange *ap_info; + int32_t *rssi; + uint32_t i, j; + int flags = cds_get_gfp_flags(); + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + skb = cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, + NULL, + EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE_INDEX, + flags); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + hdd_debug("Req Id %u Num results %u More Data %u", + data->requestId, data->numResults, data->moreData); + + ap_info = &data->ap[0]; + for (i = 0; i < data->numResults; i++) { + hdd_debug("[i=%d] " + "Bssid (" QDF_MAC_ADDR_FMT ") " + "Channel %u " + "numOfRssi %d", + i, + QDF_MAC_ADDR_REF(ap_info->bssid.bytes), + ap_info->channel, ap_info->numOfRssi); + rssi = &(ap_info)->rssi[0]; + for (j = 0; j < ap_info->numOfRssi; j++) + hdd_debug("Rssi %d", *rssi++); + + ap_info = (tSirWifiSignificantChange *)((char *)ap_info + + ap_info->numOfRssi * sizeof(*rssi) + + sizeof(*ap_info)); + } + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, + data->requestId) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, + data->numResults)) { + hdd_err("put fail"); + goto fail; + } + + if (data->numResults) { + struct nlattr *aps; + + aps = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); + if (!aps) + goto fail; + + ap_info = &data->ap[0]; + for (i = 0; i < data->numResults; i++) { + struct nlattr *ap; + + ap = nla_nest_start(skb, i); + if (!ap) + goto fail; + + if (nla_put(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID, + QDF_MAC_ADDR_SIZE, ap_info->bssid.bytes) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL, + ap_info->channel) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI, + ap_info->numOfRssi) || + nla_put(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST, + sizeof(s32) * ap_info->numOfRssi, + &(ap_info)->rssi[0])) + goto fail; + + nla_nest_end(skb, ap); + + ap_info = (tSirWifiSignificantChange *)((char *)ap_info + + ap_info->numOfRssi * sizeof(*rssi) + + sizeof(*ap_info)); + } + nla_nest_end(skb, aps); + + if (nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, + data->moreData)) + goto fail; + } + + cfg80211_vendor_event(skb, flags); + return; + +fail: + kfree_skb(skb); + return; + +} + +/** + * wlan_hdd_cfg80211_extscan_full_scan_result_event() - full scan result event + * @hdd_ctx: Pointer to hdd context + * @data: Pointer to full scan result event + * + * This callback execute in atomic context and must not invoke any + * blocking calls. + * + * Return: none + */ +static void +wlan_hdd_cfg80211_extscan_full_scan_result_event(struct hdd_context *hdd_ctx, + tpSirWifiFullScanResultEvent + data) +{ + struct sk_buff *skb; + struct hdd_ext_scan_context *context; + + int flags = cds_get_gfp_flags(); + + /* ENTER() intentionally not used in a frequently invoked API */ + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + if ((sizeof(*data) + data->ap.ieLength) >= EXTSCAN_EVENT_BUF_SIZE) { + hdd_err("Frame exceeded NL size limitation, drop it!!"); + return; + } + skb = cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, + NULL, + EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT_INDEX, + flags); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + data->ap.channel = cds_chan_to_freq(data->ap.channel); + + /* + * Android does not want the time stamp from the frame. + * Instead it wants a monotonic increasing value since boot + */ + data->ap.ts = qdf_get_monotonic_boottime(); + + hdd_debug("Req Id %u More Data %u", data->requestId, + data->moreData); + hdd_debug("AP Info: Timestamp %llu Ssid: %s " + "Bssid (" QDF_MAC_ADDR_FMT ") " + "Channel %u " + "Rssi %d " + "RTT %u " + "RTT_SD %u " + "Bcn Period %d " + "Capability 0x%X " + "IE Length %d", + data->ap.ts, + data->ap.ssid, + QDF_MAC_ADDR_REF(data->ap.bssid.bytes), + data->ap.channel, + data->ap.rssi, + data->ap.rtt, + data->ap.rtt_sd, + data->ap.beaconPeriod, + data->ap.capability, data->ap.ieLength); + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, + data->requestId) || + hdd_wlan_nla_put_u64(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP, + data->ap.ts) || + nla_put(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID, + sizeof(data->ap.ssid), + data->ap.ssid) || + nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID, + sizeof(data->ap.bssid), + data->ap.bssid.bytes) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL, + data->ap.channel) || + nla_put_s32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI, + data->ap.rssi) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT, + data->ap.rtt) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD, + data->ap.rtt_sd) || + nla_put_u16(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD, + data->ap.beaconPeriod) || + nla_put_u16(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY, + data->ap.capability) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_LENGTH, + data->ap.ieLength) || + nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, + data->moreData)) { + hdd_err("nla put fail"); + goto nla_put_failure; + } + + if (data->ap.ieLength) { + if (nla_put(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_DATA, + data->ap.ieLength, data->ap.ieData)) + goto nla_put_failure; + } + + context = &ext_scan_context; + spin_lock(&context->context_lock); + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_BUCKETS_SCANNED, + context->buckets_scanned)) { + spin_unlock(&context->context_lock); + hdd_debug("Failed to include buckets_scanned"); + goto nla_put_failure; + } + spin_unlock(&context->context_lock); + + cfg80211_vendor_event(skb, flags); + return; + +nla_put_failure: + kfree_skb(skb); +} + +/** + * wlan_hdd_cfg80211_extscan_scan_res_available_event() - scan result event + * @hdd_ctx: Pointer to hdd context + * @data: Pointer to scan results available indication param + * + * This callback execute in atomic context and must not invoke any + * blocking calls. + * + * Return: none + */ +static void +wlan_hdd_cfg80211_extscan_scan_res_available_event( + struct hdd_context *hdd_ctx, + tpSirExtScanResultsAvailableIndParams data) +{ + struct sk_buff *skb; + int flags = cds_get_gfp_flags(); + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + skb = cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, + NULL, + EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE_INDEX, + flags); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + hdd_debug("Req Id %u Num results %u", + data->requestId, data->numResultsAvailable); + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, + data->requestId) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, + data->numResultsAvailable)) { + hdd_err("nla put fail"); + goto nla_put_failure; + } + + cfg80211_vendor_event(skb, flags); + hdd_exit(); + return; + +nla_put_failure: + kfree_skb(skb); +} + +/** + * wlan_hdd_cfg80211_extscan_scan_progress_event() - scan progress event + * @hdd_ctx: Pointer to hdd context + * @data: Pointer to scan event indication param + * + * This callback execute in atomic context and must not invoke any + * blocking calls. + * + * Return: none + */ +static void +wlan_hdd_cfg80211_extscan_scan_progress_event(struct hdd_context *hdd_ctx, + tpSirExtScanOnScanEventIndParams + data) +{ + struct sk_buff *skb; + int flags = cds_get_gfp_flags(); + struct hdd_ext_scan_context *context; + + /* ENTER() intentionally not used in a frequently invoked API */ + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + skb = cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, + NULL, + EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT_INDEX, + flags); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + hdd_debug("Request Id: %u Scan event type: %u Scan event status: %u buckets scanned: %u", + data->requestId, data->scanEventType, data->status, + data->buckets_scanned); + + context = &ext_scan_context; + spin_lock(&context->context_lock); + if (data->scanEventType == WIFI_EXTSCAN_CYCLE_COMPLETED_EVENT) { + context->buckets_scanned = 0; + data->scanEventType = WIFI_EXTSCAN_RESULTS_AVAILABLE; + spin_unlock(&context->context_lock); + } else if (data->scanEventType == WIFI_EXTSCAN_CYCLE_STARTED_EVENT) { + context->buckets_scanned = data->buckets_scanned; + /* No need to report to user space */ + spin_unlock(&context->context_lock); + goto nla_put_failure; + } else { + spin_unlock(&context->context_lock); + } + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, + data->requestId) || + nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_EVENT_TYPE, + data->scanEventType)) { + hdd_err("nla put fail"); + goto nla_put_failure; + } + + cfg80211_vendor_event(skb, flags); + return; + +nla_put_failure: + kfree_skb(skb); +} + +/** + * wlan_hdd_cfg80211_extscan_epno_match_found() - pno match found + * @hdd_ctx: HDD context + * @data: matched network data + * + * This function reads the matched network data and fills NL vendor attributes + * and send it to upper layer. + * This callback execute in atomic context and must not invoke any + * blocking calls. + * + * Return: 0 on success, error number otherwise + */ +static void +wlan_hdd_cfg80211_extscan_epno_match_found(struct hdd_context *hdd_ctx, + struct pno_match_found *data) +{ + struct sk_buff *skb; + uint32_t len, i; + int flags = cds_get_gfp_flags(); + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + /* + * If the number of match found APs including IE data exceeds NL 4K size + * limitation, drop that beacon/probe rsp frame. + */ + len = sizeof(*data) + + (data->num_results + sizeof(tSirWifiScanResult)); + for (i = 0; i < data->num_results; i++) + len += data->ap[i].ieLength; + + if (len >= EXTSCAN_EVENT_BUF_SIZE) { + hdd_err("Frame exceeded NL size limitation, drop it!"); + return; + } + + skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, + EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_NETWORK_FOUND_INDEX, + flags); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + hdd_debug("Req Id %u More Data %u num_results %d", + data->request_id, data->more_data, data->num_results); + for (i = 0; i < data->num_results; i++) { + data->ap[i].channel = cds_chan_to_freq(data->ap[i].channel); + hdd_debug("AP Info: Timestamp %llu) Ssid: %s " + "Bssid (" QDF_MAC_ADDR_FMT ") " + "Channel %u " + "Rssi %d " + "RTT %u " + "RTT_SD %u " + "Bcn Period %d " + "Capability 0x%X " + "IE Length %d", + data->ap[i].ts, + data->ap[i].ssid, + QDF_MAC_ADDR_REF(data->ap[i].bssid.bytes), + data->ap[i].channel, + data->ap[i].rssi, + data->ap[i].rtt, + data->ap[i].rtt_sd, + data->ap[i].beaconPeriod, + data->ap[i].capability, + data->ap[i].ieLength); + } + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, + data->request_id) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, + data->num_results) || + nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, + data->more_data)) { + hdd_err("nla put fail"); + goto fail; + } + + if (data->num_results) { + struct nlattr *nla_aps; + + nla_aps = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); + if (!nla_aps) + goto fail; + + for (i = 0; i < data->num_results; i++) { + if (hdd_extscan_nl_fill_bss(skb, &data->ap[i], i)) + goto fail; + } + nla_nest_end(skb, nla_aps); + } + + cfg80211_vendor_event(skb, flags); + return; + +fail: + kfree_skb(skb); +} + +/** + * wlan_hdd_cfg80211_passpoint_match_found() - passpoint match found + * @hddctx: HDD context + * @data: matched network data + * + * This function reads the match network %data and fill in the skb with + * NL attributes and send up the NL event + * This callback execute in atomic context and must not invoke any + * blocking calls. + * + * Return: none + */ +static void +wlan_hdd_cfg80211_passpoint_match_found(void *ctx, + struct wifi_passpoint_match *data) +{ + struct hdd_context *hdd_ctx = ctx; + struct sk_buff *skb = NULL; + uint32_t len, i, num_matches = 1, more_data = 0; + struct nlattr *nla_aps, *nla_bss; + int flags = cds_get_gfp_flags(); + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + len = sizeof(*data) + data->ap.ieLength + data->anqp_len; + if (len >= EXTSCAN_EVENT_BUF_SIZE) { + hdd_err("Result exceeded NL size limitation, drop it"); + return; + } + + skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, + EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_PASSPOINT_NETWORK_FOUND_INDEX, + flags); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + hdd_debug("Req Id %u Id %u ANQP length %u num_matches %u", + data->request_id, data->id, data->anqp_len, num_matches); + for (i = 0; i < num_matches; i++) { + hdd_debug("AP Info: Timestamp %llu Ssid: %s " + "Bssid (" QDF_MAC_ADDR_FMT ") " + "Channel %u " + "Rssi %d " + "RTT %u " + "RTT_SD %u " + "Bcn Period %d " + "Capability 0x%X " + "IE Length %d", + data->ap.ts, + data->ap.ssid, + QDF_MAC_ADDR_REF(data->ap.bssid.bytes), + data->ap.channel, + data->ap.rssi, + data->ap.rtt, + data->ap.rtt_sd, + data->ap.beaconPeriod, + data->ap.capability, + data->ap.ieLength); + } + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, + data->request_id) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES, + num_matches) || + nla_put_u8(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, + more_data)) { + hdd_err("nla put fail"); + goto fail; + } + + nla_aps = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST); + if (!nla_aps) + goto fail; + + for (i = 0; i < num_matches; i++) { + struct nlattr *nla_ap; + + nla_ap = nla_nest_start(skb, i); + if (!nla_ap) + goto fail; + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID, + data->id) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN, + data->anqp_len)) { + goto fail; + } + + if (data->anqp_len) + if (nla_put(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP, + data->anqp_len, data->anqp)) + goto fail; + + nla_bss = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); + if (!nla_bss) + goto fail; + + if (hdd_extscan_nl_fill_bss(skb, &data->ap, 0)) + goto fail; + + nla_nest_end(skb, nla_bss); + nla_nest_end(skb, nla_ap); + } + nla_nest_end(skb, nla_aps); + + cfg80211_vendor_event(skb, flags); + return; + +fail: + kfree_skb(skb); +} + +/** + * wlan_hdd_cfg80211_extscan_generic_rsp() - + * Handle a generic ExtScan Response message + * @ctx: HDD context registered with SME + * @response: The ExtScan response from firmware + * + * This function will handle a generic ExtScan response message from + * firmware and will communicate the result to the userspace thread + * that is waiting for the response. + * + * Return: none + */ +static void +wlan_hdd_cfg80211_extscan_generic_rsp(struct hdd_context *hdd_ctx, + struct sir_extscan_generic_response *response) +{ + struct hdd_ext_scan_context *context; + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx) || !response) { + hdd_err("HDD context is not valid or response(%pK) is null", + response); + return; + } + + hdd_debug("request %u status %u", + response->request_id, response->status); + + context = &ext_scan_context; + spin_lock(&context->context_lock); + if (context->request_id == response->request_id) { + context->response_status = response->status ? -EINVAL : 0; + complete(&context->response_event); + } + spin_unlock(&context->context_lock); +} + +void wlan_hdd_cfg80211_extscan_callback(hdd_handle_t hdd_handle, + const uint16_t event_id, void *msg) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + + /* ENTER() intentionally not used in a frequently invoked API */ + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + hdd_debug("Rcvd Event %d", event_id); + + switch (event_id) { + case eSIR_EXTSCAN_CACHED_RESULTS_RSP: + /* There is no need to send this response to upper layer + * Just log the message + */ + hdd_debug("Rcvd eSIR_EXTSCAN_CACHED_RESULTS_RSP"); + break; + + case eSIR_EXTSCAN_GET_CAPABILITIES_IND: + wlan_hdd_cfg80211_extscan_get_capabilities_rsp(hdd_ctx, msg); + break; + + case eSIR_EXTSCAN_HOTLIST_MATCH_IND: + wlan_hdd_cfg80211_extscan_hotlist_match_ind(hdd_ctx, msg); + break; + + case eSIR_EXTSCAN_SIGNIFICANT_WIFI_CHANGE_RESULTS_IND: + wlan_hdd_cfg80211_extscan_signif_wifi_change_results_ind(hdd_ctx, + msg); + break; + + case eSIR_EXTSCAN_CACHED_RESULTS_IND: + wlan_hdd_cfg80211_extscan_cached_results_ind(hdd_ctx, msg); + break; + + case eSIR_EXTSCAN_SCAN_RES_AVAILABLE_IND: + wlan_hdd_cfg80211_extscan_scan_res_available_event(hdd_ctx, + msg); + break; + + case eSIR_EXTSCAN_FULL_SCAN_RESULT_IND: + wlan_hdd_cfg80211_extscan_full_scan_result_event(hdd_ctx, msg); + break; + + case eSIR_EPNO_NETWORK_FOUND_IND: + wlan_hdd_cfg80211_extscan_epno_match_found(hdd_ctx, msg); + break; + + case eSIR_EXTSCAN_SCAN_PROGRESS_EVENT_IND: + wlan_hdd_cfg80211_extscan_scan_progress_event(hdd_ctx, msg); + break; + + case eSIR_PASSPOINT_NETWORK_FOUND_IND: + wlan_hdd_cfg80211_passpoint_match_found(hdd_ctx, msg); + break; + + case eSIR_EXTSCAN_START_RSP: + case eSIR_EXTSCAN_STOP_RSP: + case eSIR_EXTSCAN_SET_BSSID_HOTLIST_RSP: + case eSIR_EXTSCAN_RESET_BSSID_HOTLIST_RSP: + case eSIR_EXTSCAN_SET_SIGNIFICANT_WIFI_CHANGE_RSP: + case eSIR_EXTSCAN_RESET_SIGNIFICANT_WIFI_CHANGE_RSP: + case eSIR_EXTSCAN_SET_SSID_HOTLIST_RSP: + case eSIR_EXTSCAN_RESET_SSID_HOTLIST_RSP: + wlan_hdd_cfg80211_extscan_generic_rsp(hdd_ctx, msg); + break; + + default: + hdd_err("Unknown event type: %u", event_id); + break; + } +} + +/* + * define short names for the global vendor params + * used by wlan_hdd_send_ext_scan_capability() + */ +#define PARAM_REQUEST_ID \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID +#define PARAM_STATUS \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_STATUS +#define MAX_EXTSCAN_CACHE_SIZE \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE +#define MAX_SCAN_BUCKETS \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS +#define MAX_AP_CACHE_PER_SCAN \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN +#define MAX_RSSI_SAMPLE_SIZE \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE +#define MAX_SCAN_RPT_THRHOLD \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD +#define MAX_HOTLIST_BSSIDS \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS +#define MAX_SIGNIFICANT_WIFI_CHANGE_APS \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS +#define MAX_BSSID_HISTORY_ENTRIES \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES +#define MAX_HOTLIST_SSIDS \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS +#define MAX_NUM_EPNO_NETS \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS +#define MAX_NUM_EPNO_NETS_BY_SSID \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID +#define MAX_NUM_WHITELISTED_SSID \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID +#define MAX_NUM_BLACKLISTED_BSSID \ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_MAX_NUM_BLACKLISTED_BSSID +/** + * wlan_hdd_send_ext_scan_capability - send ext scan capability to user space + * @hdd_ctx: Pointer to hdd context + * + * Return: 0 for success, non-zero for failure + */ +static int wlan_hdd_send_ext_scan_capability(struct hdd_context *hdd_ctx) +{ + int ret; + struct sk_buff *skb; + struct ext_scan_capabilities_response *data; + uint32_t nl_buf_len; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + data = &(ext_scan_context.capability_response); + + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += (sizeof(data->requestId) + NLA_HDRLEN) + + (sizeof(data->status) + NLA_HDRLEN) + + (sizeof(data->max_scan_cache_size) + NLA_HDRLEN) + + (sizeof(data->max_scan_buckets) + NLA_HDRLEN) + + (sizeof(data->max_ap_cache_per_scan) + NLA_HDRLEN) + + (sizeof(data->max_rssi_sample_size) + NLA_HDRLEN) + + (sizeof(data->max_scan_reporting_threshold) + NLA_HDRLEN) + + (sizeof(data->max_hotlist_bssids) + NLA_HDRLEN) + + (sizeof(data->max_significant_wifi_change_aps) + NLA_HDRLEN) + + (sizeof(data->max_bssid_history_entries) + NLA_HDRLEN) + + (sizeof(data->max_hotlist_ssids) + NLA_HDRLEN) + + (sizeof(data->max_number_epno_networks) + NLA_HDRLEN) + + (sizeof(data->max_number_epno_networks_by_ssid) + NLA_HDRLEN) + + (sizeof(data->max_number_of_white_listed_ssid) + NLA_HDRLEN) + + (sizeof(data->max_number_of_black_listed_bssid) + NLA_HDRLEN); + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + + hdd_debug("Req Id %u", data->requestId); + hdd_debug("Status %u", data->status); + hdd_debug("Scan cache size %u", + data->max_scan_cache_size); + hdd_debug("Scan buckets %u", data->max_scan_buckets); + hdd_debug("Max AP per scan %u", + data->max_ap_cache_per_scan); + hdd_debug("max_rssi_sample_size %u", + data->max_rssi_sample_size); + hdd_debug("max_scan_reporting_threshold %u", + data->max_scan_reporting_threshold); + hdd_debug("max_hotlist_bssids %u", + data->max_hotlist_bssids); + hdd_debug("max_significant_wifi_change_aps %u", + data->max_significant_wifi_change_aps); + hdd_debug("max_bssid_history_entries %u", + data->max_bssid_history_entries); + hdd_debug("max_hotlist_ssids %u", data->max_hotlist_ssids); + hdd_debug("max_number_epno_networks %u", + data->max_number_epno_networks); + hdd_debug("max_number_epno_networks_by_ssid %u", + data->max_number_epno_networks_by_ssid); + hdd_debug("max_number_of_white_listed_ssid %u", + data->max_number_of_white_listed_ssid); + hdd_debug("max_number_of_black_listed_bssid (%u)", + data->max_number_of_black_listed_bssid); + + if (nla_put_u32(skb, PARAM_REQUEST_ID, data->requestId) || + nla_put_u32(skb, PARAM_STATUS, data->status) || + nla_put_u32(skb, MAX_EXTSCAN_CACHE_SIZE, + data->max_scan_cache_size) || + nla_put_u32(skb, MAX_SCAN_BUCKETS, data->max_scan_buckets) || + nla_put_u32(skb, MAX_AP_CACHE_PER_SCAN, + data->max_ap_cache_per_scan) || + nla_put_u32(skb, MAX_RSSI_SAMPLE_SIZE, + data->max_rssi_sample_size) || + nla_put_u32(skb, MAX_SCAN_RPT_THRHOLD, + data->max_scan_reporting_threshold) || + nla_put_u32(skb, MAX_HOTLIST_BSSIDS, data->max_hotlist_bssids) || + nla_put_u32(skb, MAX_SIGNIFICANT_WIFI_CHANGE_APS, + data->max_significant_wifi_change_aps) || + nla_put_u32(skb, MAX_BSSID_HISTORY_ENTRIES, + data->max_bssid_history_entries) || + nla_put_u32(skb, MAX_HOTLIST_SSIDS, data->max_hotlist_ssids) || + nla_put_u32(skb, MAX_NUM_EPNO_NETS, + data->max_number_epno_networks) || + nla_put_u32(skb, MAX_NUM_EPNO_NETS_BY_SSID, + data->max_number_epno_networks_by_ssid) || + nla_put_u32(skb, MAX_NUM_WHITELISTED_SSID, + data->max_number_of_white_listed_ssid) || + nla_put_u32(skb, MAX_NUM_BLACKLISTED_BSSID, + data->max_number_of_black_listed_bssid)) { + hdd_err("nla put fail"); + goto nla_put_failure; + } + + cfg80211_vendor_cmd_reply(skb); + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} +/* + * done with short names for the global vendor params + * used by wlan_hdd_send_ext_scan_capability() + */ +#undef PARAM_REQUEST_ID +#undef PARAM_STATUS +#undef MAX_EXTSCAN_CACHE_SIZE +#undef MAX_SCAN_BUCKETS +#undef MAX_AP_CACHE_PER_SCAN +#undef MAX_RSSI_SAMPLE_SIZE +#undef MAX_SCAN_RPT_THRHOLD +#undef MAX_HOTLIST_BSSIDS +#undef MAX_SIGNIFICANT_WIFI_CHANGE_APS +#undef MAX_BSSID_HISTORY_ENTRIES +#undef MAX_HOTLIST_SSIDS +#undef MAX_NUM_EPNO_NETS +#undef MAX_NUM_EPNO_NETS_BY_SSID +#undef MAX_NUM_WHITELISTED_SSID +#undef MAX_NUM_BLACKLISTED_BSSID + +/** + * __wlan_hdd_cfg80211_extscan_get_capabilities() - get ext scan capabilities + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: none + */ +static int +__wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int id, ret; + unsigned long rc; + struct hdd_ext_scan_context *context; + struct extscan_capabilities_params params; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; + QDF_STATUS status; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return -EINVAL; + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_err("Driver Modules are closed"); + return -EINVAL; + } + + if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { + hdd_err("extscan not supported"); + return -ENOTSUPP; + } + if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, + wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + return -EINVAL; + } + + params.request_id = nla_get_u32(tb[id]); + params.vdev_id = adapter->vdev_id; + hdd_debug("Req Id %d Vdev Id %d", params.request_id, params.vdev_id); + + context = &ext_scan_context; + spin_lock(&context->context_lock); + context->request_id = params.request_id; + INIT_COMPLETION(context->response_event); + spin_unlock(&context->context_lock); + + status = sme_ext_scan_get_capabilities(hdd_ctx->mac_handle, ¶ms); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_ext_scan_get_capabilities failed(err=%d)", + status); + return -EINVAL; + } + + rc = wait_for_completion_timeout(&context->response_event, + msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); + if (!rc) { + hdd_err("Target response timed out"); + return -ETIMEDOUT; + } + + ret = wlan_hdd_send_ext_scan_capability(hdd_ctx); + if (ret) + hdd_err("Failed to send ext scan capability to user space"); + hdd_exit(); + return ret; +} + +/** + * wlan_hdd_cfg80211_extscan_get_capabilities() - get ext scan capabilities + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_extscan_get_capabilities(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * This function parses the incoming NL vendor command data attributes and + * invokes the SME Api and blocks on a completion variable. + * Each WMI event with cached scan results data chunk results in + * function call wlan_hdd_cfg80211_extscan_cached_results_ind and each + * data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb. + * + * If timeout happens before receiving all of the data, this function sets + * a context variable @ignore_cached_results to %true, all of the next data + * chunks are checked against this variable and dropped. + * + * Return: 0 on success; error number otherwise. + */ +static int +__wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct extscan_cached_result_params params; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; + struct hdd_ext_scan_context *context; + QDF_STATUS status; + int id, retval; + unsigned long rc; + + /* ENTER_DEV() intentionally not used in a frequently invoked API */ + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + retval = wlan_hdd_validate_context(hdd_ctx); + if (0 != retval) + return -EINVAL; + + if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { + hdd_err("extscan not supported"); + return -ENOTSUPP; + } + if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, + wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + return -EINVAL; + } + + params.request_id = nla_get_u32(tb[id]); + params.vdev_id = adapter->vdev_id; + + /* Parse and fetch flush parameter */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH; + if (!tb[id]) { + hdd_err("attr flush failed"); + return -EINVAL; + } + params.flush = nla_get_u8(tb[id]); + hdd_debug("Req Id: %u Vdev Id: %d Flush: %d", + params.request_id, params.vdev_id, params.flush); + + context = &ext_scan_context; + spin_lock(&context->context_lock); + context->request_id = params.request_id; + context->ignore_cached_results = false; + INIT_COMPLETION(context->response_event); + spin_unlock(&context->context_lock); + + status = sme_get_cached_results(hdd_ctx->mac_handle, ¶ms); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_get_cached_results failed(err=%d)", status); + return -EINVAL; + } + + rc = wait_for_completion_timeout(&context->response_event, + msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); + if (!rc) { + hdd_err("Target response timed out"); + retval = -ETIMEDOUT; + spin_lock(&context->context_lock); + context->ignore_cached_results = true; + spin_unlock(&context->context_lock); + } else { + spin_lock(&context->context_lock); + retval = context->response_status; + spin_unlock(&context->context_lock); + } + return retval; +} + +/** + * wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * This function parses the incoming NL vendor command data attributes and + * invokes the SME Api and blocks on a completion variable. + * Each WMI event with cached scan results data chunk results in + * function call wlan_hdd_cfg80211_extscan_cached_results_ind and each + * data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb. + * + * If timeout happens before receiving all of the data, this function sets + * a context variable @ignore_cached_results to %true, all of the next data + * chunks are checked against this variable and dropped. + * + * Return: 0 on success; error number otherwise. + */ +int wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_extscan_get_cached_results(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_parse_ap_rssi_threshold() - parse AP RSSI threshold parameters + * @attr: netlink attribute containing the AP RSSI threshold parameters + * @ap: destination buffer for the parsed parameters + * + * This function parses the BSSID, low RSSI and high RSSI values from + * the @attr netlink attribute, storing the parsed values in @ap. + * + * Return: 0 if @attr is parsed and all required attributes are + * present, otherwise a negative errno. + */ +static int hdd_parse_ap_rssi_threshold(struct nlattr *attr, + struct ap_threshold_params *ap) +{ + struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; + int id; + + if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, + nla_data(attr), nla_len(attr), + wlan_hdd_extscan_config_policy)) { + hdd_err("nla_parse failed"); + return -EINVAL; + } + + /* Parse and fetch MAC address */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID; + if (!tb[id]) { + hdd_err("attr mac address failed"); + return -EINVAL; + } + nla_memcpy(ap->bssid.bytes, tb[id], QDF_MAC_ADDR_SIZE); + hdd_debug("BSSID: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(ap->bssid.bytes)); + + /* Parse and fetch low RSSI */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW; + if (!tb[id]) { + hdd_err("attr low RSSI failed"); + return -EINVAL; + } + ap->low = nla_get_s32(tb[id]); + hdd_debug("RSSI low %d", ap->low); + + /* Parse and fetch high RSSI */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH; + if (!tb[id]) { + hdd_err("attr high RSSI failed"); + return -EINVAL; + } + ap->high = nla_get_s32(tb[id]); + hdd_debug("RSSI High %d", ap->high); + + return 0; +} + +/** + * __wlan_hdd_cfg80211_extscan_set_bssid_hotlist() - set bssid hot list + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: none + */ +static int +__wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct extscan_bssid_hotlist_set_params *params; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; + struct nlattr *apth; + struct hdd_ext_scan_context *context; + QDF_STATUS status; + uint8_t i; + int id, rem, retval; + unsigned long rc; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + retval = wlan_hdd_validate_context(hdd_ctx); + if (0 != retval) + return -EINVAL; + + if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { + hdd_err("extscan not supported"); + return -ENOTSUPP; + } + + if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, + data, data_len, + wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + params = qdf_mem_malloc(sizeof(*params)); + if (!params) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + + /* assume the worst until proven otherwise */ + retval = -EINVAL; + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + goto fail; + } + + params->request_id = nla_get_u32(tb[id]); + hdd_debug("Req Id %d", params->request_id); + + /* Parse and fetch number of APs */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP; + if (!tb[id]) { + hdd_err("attr number of AP failed"); + goto fail; + } + + params->num_ap = nla_get_u32(tb[id]); + if (params->num_ap > WMI_WLAN_EXTSCAN_MAX_HOTLIST_APS) { + hdd_err("Number of AP: %u exceeds max: %u", + params->num_ap, WMI_WLAN_EXTSCAN_MAX_HOTLIST_APS); + goto fail; + } + params->vdev_id = adapter->vdev_id; + hdd_debug("Number of AP %d vdev Id %d", + params->num_ap, params->vdev_id); + + /* Parse and fetch lost ap sample size */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE; + if (!tb[id]) { + hdd_err("attr lost ap sample size failed"); + goto fail; + } + + params->lost_ap_sample_size = nla_get_u32(tb[id]); + hdd_debug("Lost ap sample size %d", + params->lost_ap_sample_size); + + /* Parse the AP Threshold array */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM; + if (!tb[id]) { + hdd_err("attr ap threshold failed"); + goto fail; + } + + i = 0; + nla_for_each_nested(apth, tb[id], rem) { + if (i == params->num_ap) { + hdd_warn("Ignoring excess AP"); + break; + } + + retval = hdd_parse_ap_rssi_threshold(apth, ¶ms->ap[i]); + if (retval) + goto fail; + + i++; + } + + if (i < params->num_ap) { + hdd_warn("Number of AP %u less than expected %u", + i, params->num_ap); + params->num_ap = i; + } + + context = &ext_scan_context; + spin_lock(&context->context_lock); + INIT_COMPLETION(context->response_event); + context->request_id = params->request_id; + spin_unlock(&context->context_lock); + + status = sme_set_bss_hotlist(hdd_ctx->mac_handle, params); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_set_bss_hotlist failed(err=%d)", status); + retval = qdf_status_to_os_return(status); + goto fail; + } + + /* request was sent -- wait for the response */ + rc = wait_for_completion_timeout + (&context->response_event, + msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); + + if (!rc) { + hdd_err("sme_set_bss_hotlist timed out"); + retval = -ETIMEDOUT; + } else { + spin_lock(&context->context_lock); + if (context->request_id == params->request_id) + retval = context->response_status; + else + retval = -EINVAL; + spin_unlock(&context->context_lock); + } + hdd_exit(); + +fail: + qdf_mem_free(params); + return retval; +} + +/** + * wlan_hdd_cfg80211_extscan_set_bssid_hotlist() - set ext scan bssid hotlist + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + + +/** + * __wlan_hdd_cfg80211_extscan_set_significant_change() - set significant change + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: none + */ +static int +__wlan_hdd_cfg80211_extscan_set_significant_change(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct extscan_set_sig_changereq_params *params; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; + struct nlattr *apth; + struct hdd_ext_scan_context *context; + QDF_STATUS status; + uint8_t i; + int id, rem, retval; + unsigned long rc; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + retval = wlan_hdd_validate_context(hdd_ctx); + if (0 != retval) + return -EINVAL; + + if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, + data, data_len, + wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + params = qdf_mem_malloc(sizeof(*params)); + if (!params) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + + /* assume the worst until proven otherwise */ + retval = -EINVAL; + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + goto fail; + } + + params->request_id = nla_get_u32(tb[id]); + hdd_debug("Req Id %d", params->request_id); + + /* Parse and fetch RSSI sample size */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE; + if (!tb[id]) { + hdd_err("attr RSSI sample size failed"); + goto fail; + } + params->rssi_sample_size = nla_get_u32(tb[id]); + hdd_debug("RSSI sample size %u", params->rssi_sample_size); + + /* Parse and fetch lost AP sample size */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE; + if (!tb[id]) { + hdd_err("attr lost AP sample size failed"); + goto fail; + } + params->lostap_sample_size = nla_get_u32(tb[id]); + hdd_debug("Lost AP sample size %u", params->lostap_sample_size); + + /* Parse and fetch AP min breaching */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING; + if (!tb[id]) { + hdd_err("attr AP min breaching"); + goto fail; + } + params->min_breaching = nla_get_u32(tb[id]); + hdd_debug("AP min breaching %u", params->min_breaching); + + /* Parse and fetch number of APs */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP; + if (!tb[id]) { + hdd_err("attr number of AP failed"); + goto fail; + } + params->num_ap = nla_get_u32(tb[id]); + if (params->num_ap > WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS) { + hdd_err("Number of AP %u exceeds max %u", + params->num_ap, + WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS); + goto fail; + } + + params->vdev_id = adapter->vdev_id; + hdd_debug("Number of AP %d Vdev Id %d", + params->num_ap, params->vdev_id); + + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM; + if (!tb[id]) { + hdd_err("attr ap threshold failed"); + goto fail; + } + i = 0; + nla_for_each_nested(apth, tb[id], rem) { + + if (i == params->num_ap) { + hdd_warn("Ignoring excess AP"); + break; + } + + retval = hdd_parse_ap_rssi_threshold(apth, ¶ms->ap[i]); + if (retval) + goto fail; + + i++; + } + if (i < params->num_ap) { + hdd_warn("Number of AP %u less than expected %u", + i, params->num_ap); + params->num_ap = i; + } + + context = &ext_scan_context; + spin_lock(&context->context_lock); + INIT_COMPLETION(context->response_event); + context->request_id = params->request_id; + spin_unlock(&context->context_lock); + + status = sme_set_significant_change(hdd_ctx->mac_handle, params); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_set_significant_change failed(err=%d)", status); + retval = qdf_status_to_os_return(status); + goto fail; + } + + /* request was sent -- wait for the response */ + rc = wait_for_completion_timeout(&context->response_event, + msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); + + if (!rc) { + hdd_err("sme_set_significant_change timed out"); + retval = -ETIMEDOUT; + } else { + spin_lock(&context->context_lock); + if (context->request_id == params->request_id) + retval = context->response_status; + else + retval = -EINVAL; + spin_unlock(&context->context_lock); + } + hdd_exit(); + +fail: + qdf_mem_free(params); + return retval; +} + +/** + * wlan_hdd_cfg80211_extscan_set_significant_change() - set significant change + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_extscan_set_significant_change(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_extscan_set_significant_change(wiphy, wdev, + data, + data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_extscan_update_dwell_time_limits() - update dwell times + * @req_msg: Pointer to request message + * @bkt_idx: Index of current bucket being processed + * @active_min: minimum active dwell time + * @active_max: maximum active dwell time + * @passive_min: minimum passive dwell time + * @passive_max: maximum passive dwell time + * + * Return: none + */ +static void +hdd_extscan_update_dwell_time_limits(struct wifi_scan_cmd_req_params *req_msg, + uint32_t bkt_idx, uint32_t active_min, + uint32_t active_max, uint32_t passive_min, + uint32_t passive_max) +{ + /* update per-bucket dwell times */ + if (req_msg->buckets[bkt_idx].min_dwell_time_active > + active_min) { + req_msg->buckets[bkt_idx].min_dwell_time_active = + active_min; + } + if (req_msg->buckets[bkt_idx].max_dwell_time_active < + active_max) { + req_msg->buckets[bkt_idx].max_dwell_time_active = + active_max; + } + if (req_msg->buckets[bkt_idx].min_dwell_time_passive > + passive_min) { + req_msg->buckets[bkt_idx].min_dwell_time_passive = + passive_min; + } + if (req_msg->buckets[bkt_idx].max_dwell_time_passive < + passive_max) { + req_msg->buckets[bkt_idx].max_dwell_time_passive = + passive_max; + } + /* update dwell-time across all buckets */ + if (req_msg->min_dwell_time_active > + req_msg->buckets[bkt_idx].min_dwell_time_active) { + req_msg->min_dwell_time_active = + req_msg->buckets[bkt_idx].min_dwell_time_active; + } + if (req_msg->max_dwell_time_active < + req_msg->buckets[bkt_idx].max_dwell_time_active) { + req_msg->max_dwell_time_active = + req_msg->buckets[bkt_idx].max_dwell_time_active; + } + if (req_msg->min_dwell_time_passive > + req_msg->buckets[bkt_idx].min_dwell_time_passive) { + req_msg->min_dwell_time_passive = + req_msg->buckets[bkt_idx].min_dwell_time_passive; + } + if (req_msg->max_dwell_time_passive > + req_msg->buckets[bkt_idx].max_dwell_time_passive) { + req_msg->max_dwell_time_passive = + req_msg->buckets[bkt_idx].max_dwell_time_passive; + } +} + +/** + * hdd_extscan_channel_max_reached() - channel max reached + * @req: extscan request structure + * @total_channels: total number of channels + * + * Return: true if total channels reached max, false otherwise + */ +static bool +hdd_extscan_channel_max_reached(struct wifi_scan_cmd_req_params *req, + uint8_t total_channels) +{ + if (total_channels == WMI_WLAN_EXTSCAN_MAX_CHANNELS) { + hdd_warn("max #of channels %d reached, take only first %d bucket(s)", + total_channels, req->num_buckets); + return true; + } + return false; +} + +/** + * hdd_extscan_start_fill_bucket_channel_spec() - fill bucket channel spec + * @hdd_ctx: HDD global context + * @req_msg: Pointer to request structure + * @bucket_attr: pointer to bucket attribute + * + * Return: 0 on success; error number otherwise + */ +static int hdd_extscan_start_fill_bucket_channel_spec( + struct hdd_context *hdd_ctx, + struct wifi_scan_cmd_req_params *req_msg, + struct nlattr *bucket_attr) +{ + mac_handle_t mac_handle; + struct nlattr *bucket_tb[EXTSCAN_PARAM_MAX + 1]; + struct nlattr *channel_tb[EXTSCAN_PARAM_MAX + 1]; + struct nlattr *buckets; + struct nlattr *channels; + int id, rem1, rem2; + QDF_STATUS status; + uint8_t bkt_index, j, num_channels, total_channels = 0; + uint32_t expected_buckets; + uint32_t expected_channels; + uint32_t chan_list[CFG_VALID_CHANNEL_LIST_LEN] = {0}; + uint32_t extscan_active_min_chn_time; + uint32_t min_dwell_time_active_bucket; + uint32_t max_dwell_time_active_bucket; + uint32_t min_dwell_time_passive_bucket; + uint32_t max_dwell_time_passive_bucket; + struct wifi_scan_bucket_params *bucket; + struct wifi_scan_channelspec_params *channel; + struct nlattr *channel_attr; + + ucfg_extscan_get_active_min_time(hdd_ctx->psoc, + &extscan_active_min_chn_time); + ucfg_extscan_get_active_max_time(hdd_ctx->psoc, + &max_dwell_time_active_bucket); + ucfg_extscan_get_passive_max_time(hdd_ctx->psoc, + &max_dwell_time_passive_bucket); + + min_dwell_time_active_bucket = max_dwell_time_active_bucket; + min_dwell_time_passive_bucket = max_dwell_time_passive_bucket; + + req_msg->min_dwell_time_active = + req_msg->max_dwell_time_active = max_dwell_time_active_bucket; + + req_msg->min_dwell_time_passive = + req_msg->max_dwell_time_passive = max_dwell_time_passive_bucket; + + expected_buckets = req_msg->num_buckets; + req_msg->num_buckets = 0; + bkt_index = 0; + + mac_handle = hdd_ctx->mac_handle; + nla_for_each_nested(buckets, bucket_attr, rem1) { + + if (bkt_index >= expected_buckets) { + hdd_warn("ignoring excess buckets"); + break; + } + + if (wlan_cfg80211_nla_parse(bucket_tb, EXTSCAN_PARAM_MAX, + nla_data(buckets), nla_len(buckets), + wlan_hdd_extscan_config_policy)) { + hdd_err("nla_parse failed"); + return -EINVAL; + } + + bucket = &req_msg->buckets[bkt_index]; + + /* Parse and fetch bucket spec */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX; + if (!bucket_tb[id]) { + hdd_err("attr bucket index failed"); + return -EINVAL; + } + bucket->bucket = nla_get_u8(bucket_tb[id]); + + /* Parse and fetch wifi band */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND; + if (!bucket_tb[id]) { + hdd_err("attr wifi band failed"); + return -EINVAL; + } + bucket->band = nla_get_u8(bucket_tb[id]); + + /* Parse and fetch period */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD; + if (!bucket_tb[id]) { + hdd_err("attr period failed"); + return -EINVAL; + } + bucket->period = nla_get_u32(bucket_tb[id]); + + /* Parse and fetch report events */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS; + if (!bucket_tb[id]) { + hdd_err("attr report events failed"); + return -EINVAL; + } + bucket->report_events = nla_get_u8(bucket_tb[id]); + + /* Parse and fetch max period */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD; + if (!bucket_tb[id]) { + hdd_err("attr max period failed"); + return -EINVAL; + } + bucket->max_period = nla_get_u32(bucket_tb[id]); + + /* Parse and fetch base */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE; + if (!bucket_tb[id]) { + hdd_err("attr base failed"); + return -EINVAL; + } + bucket->exponent = nla_get_u32(bucket_tb[id]); + + /* Parse and fetch step count */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT; + if (!bucket_tb[id]) { + hdd_err("attr step count failed"); + return -EINVAL; + } + bucket->step_count = nla_get_u32(bucket_tb[id]); + + hdd_debug("Bucket spec Index: %d Wifi band: %d period: %d report events: %d max period: %u base: %u Step count: %u", + bucket->bucket, + bucket->band, + bucket->period, + bucket->report_events, + bucket->max_period, + bucket->exponent, + bucket->step_count); + + /* start with known good values for bucket dwell times */ + bucket->min_dwell_time_active = max_dwell_time_active_bucket; + bucket->max_dwell_time_active = max_dwell_time_active_bucket; + bucket->min_dwell_time_passive = max_dwell_time_passive_bucket; + bucket->max_dwell_time_passive = max_dwell_time_passive_bucket; + + /* Framework shall pass the channel list if the input + * WiFi band is WMI_WIFI_BAND_UNSPECIFIED. If the + * input WiFi band is specified (any value other than + * WMI_WIFI_BAND_UNSPECIFIED) then driver populates + * the channel list. + */ + if (bucket->band != WMI_WIFI_BAND_UNSPECIFIED) { + if (hdd_extscan_channel_max_reached(req_msg, + total_channels)) + return 0; + + num_channels = 0; + hdd_debug("WiFi band is specified, driver to fill channel list"); + status = sme_get_valid_channels_by_band(mac_handle, + bucket->band, + chan_list, + &num_channels); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_get_valid_channels_by_band failed (err=%d)", + status); + return -EINVAL; + } + hdd_debug("before trimming, num_channels: %d", + num_channels); + + bucket->num_channels = + QDF_MIN(num_channels, + (WMI_WLAN_EXTSCAN_MAX_CHANNELS - + total_channels)); + hdd_debug("Adj Num channels/bucket: %d total_channels: %d", + bucket->num_channels, total_channels); + total_channels += bucket->num_channels; + + for (j = 0; j < bucket->num_channels; j++) { + channel = &bucket->channels[j]; + + channel->channel = chan_list[j]; + channel->channel_class = 0; + if ((wlan_reg_get_channel_state( + hdd_ctx->pdev, + cds_freq_to_chan(chan_list[j]))) != + CHANNEL_STATE_ENABLE) { + channel->passive = 1; + channel->dwell_time_ms = + max_dwell_time_passive_bucket; + /* reconfigure per-bucket dwell time */ + if (min_dwell_time_passive_bucket > + channel->dwell_time_ms) { + min_dwell_time_passive_bucket = + channel->dwell_time_ms; + } + if (max_dwell_time_passive_bucket < + channel->dwell_time_ms) { + max_dwell_time_passive_bucket = + channel->dwell_time_ms; + } + + } else { + channel->passive = 0; + channel->dwell_time_ms = + max_dwell_time_active_bucket; + /* reconfigure per-bucket dwell times */ + if (min_dwell_time_active_bucket > + channel->dwell_time_ms) { + min_dwell_time_active_bucket = + channel->dwell_time_ms; + } + if (max_dwell_time_active_bucket < + channel->dwell_time_ms) { + max_dwell_time_active_bucket = + channel->dwell_time_ms; + } + + } + + hdd_debug("Channel: %u Passive: %u Dwell time: %u ms Class: %u", + channel->channel, + channel->passive, + channel->dwell_time_ms, + channel->channel_class); + } + + hdd_extscan_update_dwell_time_limits( + req_msg, bkt_index, + min_dwell_time_active_bucket, + max_dwell_time_active_bucket, + min_dwell_time_passive_bucket, + max_dwell_time_passive_bucket); + + hdd_debug("bkt_index:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d", + bkt_index, + bucket->min_dwell_time_active, + bucket->max_dwell_time_active, + bucket->min_dwell_time_passive, + bucket->max_dwell_time_passive); + + bkt_index++; + req_msg->num_buckets++; + continue; + } + + /* Parse and fetch number of channels */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS; + if (!bucket_tb[id]) { + hdd_err("attr num channels failed"); + return -EINVAL; + } + bucket->num_channels = nla_get_u32(bucket_tb[id]); + hdd_debug("before trimming: num channels %d", + bucket->num_channels); + + bucket->num_channels = + QDF_MIN(bucket->num_channels, + (WMI_WLAN_EXTSCAN_MAX_CHANNELS - + total_channels)); + hdd_debug("Num channels/bucket: %d total_channels: %d", + bucket->num_channels, total_channels); + + if (hdd_extscan_channel_max_reached(req_msg, total_channels)) + return 0; + + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC; + channel_attr = bucket_tb[id]; + if (!channel_attr) { + hdd_err("attr channel spec failed"); + return -EINVAL; + } + + expected_channels = bucket->num_channels; + bucket->num_channels = 0; + + nla_for_each_nested(channels, channel_attr, rem2) { + if ((bucket->num_channels >= expected_channels) || + hdd_extscan_channel_max_reached(req_msg, + total_channels)) + break; + + if (wlan_cfg80211_nla_parse(channel_tb, + EXTSCAN_PARAM_MAX, + nla_data(channels), + nla_len(channels), + wlan_hdd_extscan_config_policy)) { + hdd_err("nla_parse failed"); + return -EINVAL; + } + + channel = &bucket->channels[bucket->num_channels]; + /* Parse and fetch channel */ + if (!channel_tb[ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]) { + hdd_err("attr channel failed"); + return -EINVAL; + } + channel->channel = + nla_get_u32(channel_tb[ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]); + hdd_debug("channel %u", + channel->channel); + + /* Parse and fetch dwell time */ + if (!channel_tb[ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]) { + hdd_err("attr dwelltime failed"); + return -EINVAL; + } + channel->dwell_time_ms = + nla_get_u32(channel_tb[ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]); + + /* Override dwell time if required */ + if (channel->dwell_time_ms < + extscan_active_min_chn_time || + channel->dwell_time_ms > + max_dwell_time_active_bucket) { + hdd_debug("WiFi band is unspecified, dwellTime:%d", + channel->dwell_time_ms); + + if ((wlan_reg_get_channel_state( + hdd_ctx->pdev, + cds_freq_to_chan( + channel->channel))) + != CHANNEL_STATE_ENABLE) { + channel->dwell_time_ms = + max_dwell_time_passive_bucket; + } else { + channel->dwell_time_ms = + max_dwell_time_active_bucket; + } + } + + hdd_debug("New Dwell time %u ms", + channel->dwell_time_ms); + + if ((wlan_reg_get_channel_state(hdd_ctx->pdev, + cds_freq_to_chan( + channel->channel))) + != CHANNEL_STATE_ENABLE) { + if (min_dwell_time_passive_bucket > + channel->dwell_time_ms) { + min_dwell_time_passive_bucket = + channel->dwell_time_ms; + } + if (max_dwell_time_passive_bucket < + channel->dwell_time_ms) { + max_dwell_time_passive_bucket = + channel->dwell_time_ms; + } + } else { + if (min_dwell_time_active_bucket > + channel->dwell_time_ms) { + min_dwell_time_active_bucket = + channel->dwell_time_ms; + } + if (max_dwell_time_active_bucket < + channel->dwell_time_ms) { + max_dwell_time_active_bucket = + channel->dwell_time_ms; + } + } + + /* Parse and fetch channel spec passive */ + if (!channel_tb[ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]) { + hdd_err("attr channel spec passive failed"); + return -EINVAL; + } + channel->passive = + nla_get_u8(channel_tb[ + QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]); + hdd_debug("Chnl spec passive %u", + channel->passive); + /* Override scan type if required */ + if ((wlan_reg_get_channel_state(hdd_ctx->pdev, + cds_freq_to_chan( + channel->channel))) + != CHANNEL_STATE_ENABLE) { + channel->passive = true; + } else { + channel->passive = false; + } + total_channels++; + bucket->num_channels++; + } + + if (bucket->num_channels != expected_channels) + hdd_warn("channels: Expected %u got %u", + expected_channels, + bucket->num_channels); + + hdd_extscan_update_dwell_time_limits( + req_msg, bkt_index, + min_dwell_time_active_bucket, + max_dwell_time_active_bucket, + min_dwell_time_passive_bucket, + max_dwell_time_passive_bucket); + + hdd_debug("bktIndex:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d", + bkt_index, + bucket->min_dwell_time_active, + bucket->max_dwell_time_active, + bucket->min_dwell_time_passive, + bucket->max_dwell_time_passive); + + bkt_index++; + req_msg->num_buckets++; + } + + hdd_debug("Global: actv_min:%d actv_max:%d pass_min:%d pass_max:%d", + req_msg->min_dwell_time_active, + req_msg->max_dwell_time_active, + req_msg->min_dwell_time_passive, + req_msg->max_dwell_time_passive); + return 0; +} + +/* + * hdd_extscan_map_usr_drv_config_flags() - map userspace to driver config flags + * @config_flags - [input] configuration flags. + * + * This function maps user space received configuration flags to + * driver representation. + * + * Return: configuration flags + */ +static uint32_t hdd_extscan_map_usr_drv_config_flags(uint32_t config_flags) +{ + uint32_t configuration_flags = 0; + + if (config_flags & EXTSCAN_LP_EXTENDED_BATCHING) + configuration_flags |= EXTSCAN_LP_EXTENDED_BATCHING; + + return configuration_flags; +} + +/** + * __wlan_hdd_cfg80211_extscan_start() - ext scan start + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Return: 0 on success; error number otherwise + */ +static int +__wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct wifi_scan_cmd_req_params *params; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; + struct hdd_ext_scan_context *context; + uint32_t num_buckets; + QDF_STATUS status; + int retval, id; + unsigned long rc; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (QDF_NDI_MODE == adapter->device_mode) { + hdd_err("Command not allowed for NDI interface"); + return -EPERM; + } + + retval = wlan_hdd_validate_context(hdd_ctx); + if (0 != retval) + return -EINVAL; + + if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { + hdd_err("extscan not supported"); + return -ENOTSUPP; + } + if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, + wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + params = qdf_mem_malloc(sizeof(*params)); + if (!params) + return -ENOMEM; + + /* assume the worst until proven otherwise */ + retval = -EINVAL; + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + goto fail; + } + + params->request_id = nla_get_u32(tb[id]); + params->vdev_id = adapter->vdev_id; + + /* Parse and fetch base period */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD; + if (!tb[id]) { + hdd_err("attr base period failed"); + goto fail; + } + params->base_period = nla_get_u32(tb[id]); + + /* Parse and fetch max AP per scan */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN; + if (!tb[id]) { + hdd_err("attr max_ap_per_scan failed"); + goto fail; + } + params->max_ap_per_scan = nla_get_u32(tb[id]); + + /* Parse and fetch report threshold percent */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT; + if (!tb[id]) { + hdd_err("attr report_threshold percent failed"); + goto fail; + } + params->report_threshold_percent = nla_get_u8(tb[id]); + + /* Parse and fetch report threshold num scans */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS; + if (!tb[id]) { + hdd_err("attr report_threshold num scans failed"); + goto fail; + } + params->report_threshold_num_scans = nla_get_u8(tb[id]); + hdd_debug("Req Id: %d Vdev Id: %d Base Period: %d Max AP per Scan: %d Report Threshold percent: %d Report Threshold num scans: %d", + params->request_id, params->vdev_id, + params->base_period, params->max_ap_per_scan, + params->report_threshold_percent, + params->report_threshold_num_scans); + + /* Parse and fetch number of buckets */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS; + if (!tb[id]) { + hdd_err("attr number of buckets failed"); + goto fail; + } + num_buckets = nla_get_u8(tb[id]); + + if (num_buckets > WMI_WLAN_EXTSCAN_MAX_BUCKETS) { + hdd_warn("Exceeded MAX number of buckets: %d", + WMI_WLAN_EXTSCAN_MAX_BUCKETS); + num_buckets = WMI_WLAN_EXTSCAN_MAX_BUCKETS; + } + hdd_debug("Input: Number of Buckets %d", num_buckets); + params->num_buckets = num_buckets; + + /* This is optional attribute, if not present set it to 0 */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS; + if (!tb[id]) + params->configuration_flags = 0; + else + params->configuration_flags = + hdd_extscan_map_usr_drv_config_flags( + nla_get_u32(tb[id])); + + params->extscan_adaptive_dwell_mode = + ucfg_scan_get_extscan_adaptive_dwell_mode(hdd_ctx->psoc); + + hdd_debug("Configuration flags: %u", + params->configuration_flags); + + /* Parse and fetch number the array of buckets */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC; + if (!tb[id]) { + hdd_err("attr bucket spec failed"); + goto fail; + } + retval = hdd_extscan_start_fill_bucket_channel_spec(hdd_ctx, params, + tb[id]); + if (retval) + goto fail; + + context = &ext_scan_context; + spin_lock(&context->context_lock); + INIT_COMPLETION(context->response_event); + context->request_id = params->request_id; + context->buckets_scanned = 0; + spin_unlock(&context->context_lock); + + status = sme_ext_scan_start(hdd_ctx->mac_handle, params); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_ext_scan_start failed(err=%d)", status); + retval = qdf_status_to_os_return(status); + goto fail; + } + + hdd_ctx->ext_scan_start_since_boot = qdf_get_monotonic_boottime(); + hdd_debug("Timestamp since boot: %llu", + hdd_ctx->ext_scan_start_since_boot); + + /* request was sent -- wait for the response */ + rc = wait_for_completion_timeout(&context->response_event, + msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); + + if (!rc) { + hdd_err("sme_ext_scan_start timed out"); + retval = -ETIMEDOUT; + } else { + spin_lock(&context->context_lock); + if (context->request_id == params->request_id) + retval = context->response_status; + else + retval = -EINVAL; + spin_unlock(&context->context_lock); + } + hdd_exit(); + +fail: + qdf_mem_free(params); + return retval; +} + +/** + * wlan_hdd_cfg80211_extscan_start() - start extscan + * @wiphy: Pointer to wireless phy. + * @wdev: Pointer to wireless device. + * @data: Pointer to input data. + * @data_len: Length of @data. + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_extscan_start(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + + +/** + * __wlan_hdd_cfg80211_extscan_stop() - ext scan stop + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: none + */ +static int +__wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct extscan_stop_req_params params; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; + struct hdd_ext_scan_context *context; + QDF_STATUS status; + int id, retval; + unsigned long rc; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + retval = wlan_hdd_validate_context(hdd_ctx); + if (0 != retval) + return -EINVAL; + + if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { + hdd_err("extscan not supported"); + return -ENOTSUPP; + } + + if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, + wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + return -EINVAL; + } + params.request_id = nla_get_u32(tb[id]); + params.vdev_id = adapter->vdev_id; + hdd_debug("Req Id %d Vdev Id %d", + params.request_id, params.vdev_id); + + context = &ext_scan_context; + spin_lock(&context->context_lock); + INIT_COMPLETION(context->response_event); + context->request_id = params.request_id; + spin_unlock(&context->context_lock); + + status = sme_ext_scan_stop(hdd_ctx->mac_handle, ¶ms); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_ext_scan_stop failed(err=%d)", status); + return qdf_status_to_os_return(status); + } + + /* request was sent -- wait for the response */ + rc = wait_for_completion_timeout(&context->response_event, + msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); + + if (!rc) { + hdd_err("sme_ext_scan_stop timed out"); + retval = -ETIMEDOUT; + } else { + spin_lock(&context->context_lock); + if (context->request_id == params.request_id) + retval = context->response_status; + else + retval = -EINVAL; + spin_unlock(&context->context_lock); + } + hdd_exit(); + return retval; +} + +/** + * wlan_hdd_cfg80211_extscan_stop() - stop extscan + * @wiphy: Pointer to wireless phy. + * @wdev: Pointer to wireless device. + * @data: Pointer to input data. + * @data_len: Length of @data. + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_extscan_stop(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + + +/** + * __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist() - reset bssid hotlist + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: none + */ +static int +__wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct extscan_bssid_hotlist_reset_params params; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; + struct hdd_ext_scan_context *context; + QDF_STATUS status; + int id, retval; + unsigned long rc; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + retval = wlan_hdd_validate_context(hdd_ctx); + if (0 != retval) + return -EINVAL; + + if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { + hdd_err("extscan not supported"); + return -ENOTSUPP; + } + + if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, + data, data_len, + wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + return -EINVAL; + } + + params.request_id = nla_get_u32(tb[id]); + params.vdev_id = adapter->vdev_id; + hdd_debug("Req Id %d vdev Id %d", params.request_id, params.vdev_id); + + context = &ext_scan_context; + spin_lock(&context->context_lock); + INIT_COMPLETION(context->response_event); + context->request_id = params.request_id; + spin_unlock(&context->context_lock); + + status = sme_reset_bss_hotlist(hdd_ctx->mac_handle, ¶ms); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_reset_bss_hotlist failed(err=%d)", status); + return qdf_status_to_os_return(status); + } + + /* request was sent -- wait for the response */ + rc = wait_for_completion_timeout + (&context->response_event, + msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); + if (!rc) { + hdd_err("sme_reset_bss_hotlist timed out"); + retval = -ETIMEDOUT; + } else { + spin_lock(&context->context_lock); + if (context->request_id == params.request_id) + retval = context->response_status; + else + retval = -EINVAL; + spin_unlock(&context->context_lock); + } + hdd_exit(); + return retval; +} + +/** + * wlan_hdd_cfg80211_extscan_reset_bssid_hotlist() - reset bssid hot list + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_extscan_reset_significant_change() - + * reset significant change + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: none + */ +static int +__wlan_hdd_cfg80211_extscan_reset_significant_change(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct extscan_capabilities_reset_params params; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; + struct hdd_ext_scan_context *context; + QDF_STATUS status; + int id, retval; + unsigned long rc; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + retval = wlan_hdd_validate_context(hdd_ctx); + if (0 != retval) + return -EINVAL; + + if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { + hdd_err("extscan not supported"); + return -ENOTSUPP; + } + + if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, + wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + return -EINVAL; + } + + params.request_id = nla_get_u32(tb[id]); + params.vdev_id = adapter->vdev_id; + hdd_debug("Req Id %d Vdev Id %d", params.request_id, params.vdev_id); + + context = &ext_scan_context; + spin_lock(&context->context_lock); + INIT_COMPLETION(context->response_event); + context->request_id = params.request_id; + spin_unlock(&context->context_lock); + + status = sme_reset_significant_change(hdd_ctx->mac_handle, ¶ms); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_reset_significant_change failed(err=%d)", + status); + return -EINVAL; + } + + /* request was sent -- wait for the response */ + rc = wait_for_completion_timeout(&context->response_event, + msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); + + if (!rc) { + hdd_err("sme_ResetSignificantChange timed out"); + retval = -ETIMEDOUT; + } else { + spin_lock(&context->context_lock); + if (context->request_id == params.request_id) + retval = context->response_status; + else + retval = -EINVAL; + spin_unlock(&context->context_lock); + } + hdd_exit(); + return retval; +} + +/** + * wlan_hdd_cfg80211_extscan_reset_significant_change() - reset significant + * change + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_extscan_reset_significant_change(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_extscan_reset_significant_change(wiphy, + wdev, + data, + data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + + +/** + * hdd_extscan_epno_fill_network() - epno fill single network + * @network: aggregate network attribute + * @nw: epno network record to be filled + * + * This function takes a single network block NL vendor attribute from + * @network and decodes it into the internal record @nw. + * + * Return: 0 on success, error number otherwise + */ +static int +hdd_extscan_epno_fill_network(struct nlattr *network, + struct wifi_epno_network_params *nw) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; + int id, ssid_len; + uint8_t *ssid; + + if (!network) { + hdd_err("attr network attr failed"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, + nla_data(network), + nla_len(network), + wlan_hdd_pno_config_policy)) { + hdd_err("nla_parse failed"); + return -EINVAL; + } + + /* Parse and fetch ssid */ + id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID; + if (!tb[id]) { + hdd_err("attr network ssid failed"); + return -EINVAL; + } + ssid_len = nla_len(tb[id]); + + /* nla_parse will detect overflow but not underflow */ + if (0 == ssid_len) { + hdd_err("zero ssid length"); + return -EINVAL; + } + + /* Decrement by 1, don't count null character */ + ssid_len--; + + nw->ssid.length = ssid_len; + hdd_debug("network ssid length %d", ssid_len); + ssid = nla_data(tb[id]); + qdf_mem_copy(nw->ssid.mac_ssid, ssid, ssid_len); + hdd_debug("Ssid (%.*s)", nw->ssid.length, nw->ssid.mac_ssid); + + /* Parse and fetch epno flags */ + id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS; + if (!tb[id]) { + hdd_err("attr epno flags failed"); + return -EINVAL; + } + nw->flags = nla_get_u8(tb[id]); + hdd_debug("flags %u", nw->flags); + + /* Parse and fetch auth bit */ + id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT; + if (!tb[id]) { + hdd_err("attr auth bit failed"); + return -EINVAL; + } + nw->auth_bit_field = nla_get_u8(tb[id]); + hdd_debug("auth bit %u", nw->auth_bit_field); + + return 0; +} + +/** + * hdd_extscan_epno_fill_network_list() - epno fill network list + * @req_msg: request message + * @networks: aggregate network list attribute + * + * This function reads the network block NL vendor attributes from + * @networks and fills in the epno request message @req_msg. + * + * Return: 0 on success, error number otherwise + */ +static int +hdd_extscan_epno_fill_network_list(struct wifi_enhanced_pno_params *req_msg, + struct nlattr *networks) +{ + struct nlattr *network; + int rem; + uint32_t index; + uint32_t expected_networks; + struct wifi_epno_network_params *nw; + + if (!networks) { + hdd_err("attr networks list failed"); + return -EINVAL; + } + + expected_networks = req_msg->num_networks; + index = 0; + + nla_for_each_nested(network, networks, rem) { + if (index == expected_networks) { + hdd_warn("ignoring excess networks"); + break; + } + + nw = &req_msg->networks[index++]; + if (hdd_extscan_epno_fill_network(network, nw)) + return -EINVAL; + } + req_msg->num_networks = index; + return 0; +} + +/** + * __wlan_hdd_cfg80211_set_epno_list() - epno set network list + * @wiphy: wiphy + * @wdev: pointer to wireless dev + * @data: data pointer + * @data_len: data length + * + * This function reads the NL vendor attributes from @data and + * fills in the epno request message. + * + * Return: 0 on success, error number otherwise + */ +static int __wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct wifi_enhanced_pno_params *req_msg; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; + struct nlattr *networks; + QDF_STATUS status; + uint32_t num_networks, len; + int id, ret_val; + + hdd_enter_dev(dev); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { + hdd_err("extscan not supported"); + return -ENOTSUPP; + } + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, + data_len, wlan_hdd_pno_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch number of networks */ + id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS; + if (!tb[id]) { + hdd_err("attr num networks failed"); + return -EINVAL; + } + + /* + * num_networks is also used as EPNO SET/RESET request. + * if num_networks is zero then it is treated as RESET. + */ + num_networks = nla_get_u32(tb[id]); + + if (num_networks > MAX_EPNO_NETWORKS) { + hdd_debug("num of nw: %d exceeded max: %d, resetting to: %d", + num_networks, MAX_EPNO_NETWORKS, MAX_EPNO_NETWORKS); + num_networks = MAX_EPNO_NETWORKS; + } + + hdd_debug("num networks %u", num_networks); + len = sizeof(*req_msg) + + (num_networks * sizeof(req_msg->networks[0])); + + req_msg = qdf_mem_malloc(len); + if (!req_msg) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + req_msg->num_networks = num_networks; + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + goto fail; + } + req_msg->request_id = nla_get_u32(tb[id]); + hdd_debug("Req Id %u", req_msg->request_id); + + req_msg->vdev_id = adapter->vdev_id; + hdd_debug("Vdev Id %d", req_msg->vdev_id); + + if (num_networks) { + /* Parse and fetch min_5ghz_rssi */ + id = QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI; + if (!tb[id]) { + hdd_err("min_5ghz_rssi id failed"); + goto fail; + } + req_msg->min_5ghz_rssi = nla_get_u32(tb[id]); + + /* Parse and fetch min_24ghz_rssi */ + id = QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI; + if (!tb[id]) { + hdd_err("min_24ghz_rssi id failed"); + goto fail; + } + req_msg->min_24ghz_rssi = nla_get_u32(tb[id]); + + /* Parse and fetch initial_score_max */ + id = QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX; + if (!tb[id]) { + hdd_err("initial_score_max id failed"); + goto fail; + } + req_msg->initial_score_max = nla_get_u32(tb[id]); + + /* Parse and fetch current_connection_bonus */ + id = QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS; + if (!tb[id]) { + hdd_err("current_connection_bonus id failed"); + goto fail; + } + req_msg->current_connection_bonus = nla_get_u32(tb[id]); + + /* Parse and fetch same_network_bonus */ + id = QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS; + if (!tb[id]) { + hdd_err("same_network_bonus id failed"); + goto fail; + } + req_msg->same_network_bonus = nla_get_u32(tb[id]); + + /* Parse and fetch secure_bonus */ + id = QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS; + if (!tb[id]) { + hdd_err("secure_bonus id failed"); + goto fail; + } + req_msg->secure_bonus = nla_get_u32(tb[id]); + + /* Parse and fetch band_5ghz_bonus */ + id = QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS; + if (!tb[id]) { + hdd_err("band_5ghz_bonus id failed"); + goto fail; + } + req_msg->band_5ghz_bonus = nla_get_u32(tb[id]); + + hdd_debug("min_5ghz_rssi: %d min_24ghz_rssi: %d", + req_msg->min_5ghz_rssi, + req_msg->min_24ghz_rssi); + hdd_debug("initial_score_max: %d current_connection_bonus:%d", + req_msg->initial_score_max, + req_msg->current_connection_bonus); + hdd_debug("Bonuses same_network: %d secure: %d band_5ghz: %d", + req_msg->same_network_bonus, + req_msg->secure_bonus, + req_msg->band_5ghz_bonus); + + id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST; + networks = tb[id]; + if (hdd_extscan_epno_fill_network_list(req_msg, networks)) + goto fail; + + } + + status = sme_set_epno_list(hdd_ctx->mac_handle, req_msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_set_epno_list failed(err=%d)", status); + goto fail; + } + + hdd_exit(); + qdf_mem_free(req_msg); + return 0; + +fail: + qdf_mem_free(req_msg); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_set_epno_list() - epno set network list + * @wiphy: wiphy + * @wdev: pointer to wireless dev + * @data: data pointer + * @data_len: data length + * + * This function reads the NL vendor attributes from %tb and + * fill in the epno request message. + * + * Return: 0 on success, error number otherwise + */ +int wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_epno_list(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_extscan_passpoint_fill_network() - passpoint fill single network + * @network: aggregate network attribute + * @nw: passpoint network record to be filled + * + * This function takes a single network block NL vendor attribute from + * @network and decodes it into the internal record @nw. + * + * Return: 0 on success, error number otherwise + */ +static int +hdd_extscan_passpoint_fill_network(struct nlattr *network, + struct wifi_passpoint_network_param *nw) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; + int id; + size_t len; + + if (!network) { + hdd_err("attr network attr failed"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, + nla_data(network), + nla_len(network), + wlan_hdd_pno_config_policy)) { + hdd_err("nla_parse failed"); + return -EINVAL; + } + + /* Parse and fetch identifier */ + id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID; + if (!tb[id]) { + hdd_err("attr passpoint id failed"); + return -EINVAL; + } + nw->id = nla_get_u32(tb[id]); + hdd_debug("Id %u", nw->id); + + /* Parse and fetch realm */ + id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM; + if (!tb[id]) { + hdd_err("attr realm failed"); + return -EINVAL; + } + len = nla_strlcpy(nw->realm, tb[id], + WMI_PASSPOINT_REALM_LEN); + /* Don't send partial realm to firmware */ + if (len >= WMI_PASSPOINT_REALM_LEN) { + hdd_err("user passed invalid realm, len:%zu", len); + return -EINVAL; + } + + hdd_debug("realm: %s", nw->realm); + + /* Parse and fetch roaming consortium ids */ + id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID; + if (!tb[id]) { + hdd_err("attr roaming consortium ids failed"); + return -EINVAL; + } + nla_memcpy(&nw->roaming_consortium_ids, tb[id], + sizeof(nw->roaming_consortium_ids)); + hdd_debug("roaming consortium ids"); + + /* Parse and fetch plmn */ + id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN; + if (!tb[id]) { + hdd_err("attr plmn failed"); + return -EINVAL; + } + nla_memcpy(&nw->plmn, tb[id], + WMI_PASSPOINT_PLMN_LEN); + hdd_debug("plmn %02x:%02x:%02x)", + nw->plmn[0], + nw->plmn[1], + nw->plmn[2]); + + return 0; +} + +/** + * hdd_extscan_passpoint_fill_networks() - passpoint fill network list + * @req_msg: request message + * @networks: aggregate network list attribute + * + * This function reads the network block NL vendor attributes from + * @networks and fills in the passpoint request message. + * + * Return: 0 on success, error number otherwise + */ +static int +hdd_extscan_passpoint_fill_networks(struct wifi_passpoint_req_param *req_msg, + struct nlattr *networks) +{ + struct nlattr *network; + int rem; + uint32_t index; + uint32_t expected_networks; + struct wifi_passpoint_network_param *nw; + + if (!networks) { + hdd_err("attr networks list failed"); + return -EINVAL; + } + + expected_networks = req_msg->num_networks; + index = 0; + + nla_for_each_nested(network, networks, rem) { + if (index == expected_networks) { + hdd_warn("ignoring excess networks"); + break; + } + + nw = &req_msg->networks[index++]; + if (hdd_extscan_passpoint_fill_network(network, nw)) + return -EINVAL; + } + req_msg->num_networks = index; + return 0; +} + +/** + * __wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list + * @wiphy: wiphy + * @wdev: pointer to wireless dev + * @data: data pointer + * @data_len: data length + * + * This function reads the NL vendor attributes from %tb and + * fill in the passpoint request message. + * + * Return: 0 on success, error number otherwise + */ +static int __wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct wifi_passpoint_req_param *req_msg; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; + struct nlattr *networks; + uint32_t num_networks; + QDF_STATUS status; + int id, ret; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, + data_len, wlan_hdd_pno_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Parse and fetch number of networks */ + id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM; + if (!tb[id]) { + hdd_err("attr num networks failed"); + return -EINVAL; + } + num_networks = nla_get_u32(tb[id]); + if (num_networks > SIR_PASSPOINT_LIST_MAX_NETWORKS) { + hdd_err("num networks %u exceeds max %u", + num_networks, SIR_PASSPOINT_LIST_MAX_NETWORKS); + return -EINVAL; + } + + hdd_debug("num networks %u", num_networks); + + req_msg = qdf_mem_malloc(sizeof(*req_msg) + + (num_networks * sizeof(req_msg->networks[0]))); + if (!req_msg) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + req_msg->num_networks = num_networks; + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + goto fail; + } + req_msg->request_id = nla_get_u32(tb[id]); + + req_msg->vdev_id = adapter->vdev_id; + hdd_debug("Req Id %u Vdev Id %d", + req_msg->request_id, req_msg->vdev_id); + + id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY; + networks = tb[id]; + if (hdd_extscan_passpoint_fill_networks(req_msg, networks)) + goto fail; + + status = sme_set_passpoint_list(hdd_ctx->mac_handle, req_msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_set_passpoint_list failed(err=%d)", status); + goto fail; + } + + hdd_exit(); + qdf_mem_free(req_msg); + return 0; + +fail: + qdf_mem_free(req_msg); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list + * @wiphy: wiphy + * @wdev: pointer to wireless dev + * @data: data pointer + * @data_len: data length + * + * This function reads the NL vendor attributes from %tb and + * fill in the passpoint request message. + * + * Return: 0 on success, error number otherwise + */ +int wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_passpoint_list(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list + * @wiphy: wiphy + * @wdev: pointer to wireless dev + * @data: data pointer + * @data_len: data length + * + * This function resets passpoint networks list + * + * Return: 0 on success, error number otherwise + */ +static int __wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct wifi_passpoint_req_param *req_msg; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; + QDF_STATUS status; + int id, ret; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, + data_len, wlan_hdd_extscan_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + req_msg = qdf_mem_malloc(sizeof(*req_msg)); + if (!req_msg) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + + /* Parse and fetch request Id */ + id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; + if (!tb[id]) { + hdd_err("attr request id failed"); + goto fail; + } + req_msg->request_id = nla_get_u32(tb[id]); + + req_msg->vdev_id = adapter->vdev_id; + hdd_debug("Req Id %u Vdev Id %d", + req_msg->request_id, req_msg->vdev_id); + + status = sme_reset_passpoint_list(hdd_ctx->mac_handle, req_msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_reset_passpoint_list failed(err=%d)", status); + goto fail; + } + + hdd_exit(); + qdf_mem_free(req_msg); + return 0; + +fail: + qdf_mem_free(req_msg); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list + * @wiphy: wiphy + * @wdev: pointer to wireless dev + * @data: data pointer + * @data_len: data length + * + * This function resets passpoint networks list + * + * Return: 0 on success, error number otherwise + */ +int wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_reset_passpoint_list(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_cfg80211_extscan_init() - Initialize the ExtScan feature + * @hdd_ctx: Global HDD context + * + * Return: none + */ +void wlan_hdd_cfg80211_extscan_init(struct hdd_context *hdd_ctx) +{ + init_completion(&ext_scan_context.response_event); + spin_lock_init(&ext_scan_context.context_lock); +} + +#endif /* FEATURE_WLAN_EXTSCAN */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ext_scan.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ext_scan.h new file mode 100644 index 0000000000000000000000000000000000000000..bf3be445f95a162793d9b9f5a940287d4fed22ff --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ext_scan.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2012-2014, 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_EXT_SCAN_H) +#define WLAN_HDD_EXT_SCAN_H + +/** + * DOC: wlan_hdd_ext_scan.h + * + * WLAN Host Device Driver EXT SCAN feature implementation + * + */ + +struct hdd_context; + +#define EXTSCAN_EVENT_BUF_SIZE 4096 + +#ifdef FEATURE_WLAN_EXTSCAN + +#include "wlan_hdd_main.h" + +/* + * Used to allocate the size of 4096 for the EXTScan NL data. + * The size of 4096 is considered assuming that all data per + * respective event fit with in the limit.Please take a call + * on the limit based on the data requirements. + */ + +int wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +int wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +int wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +int wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy, + struct wireless_dev + *wdev, const void *data, + int data_len); + +int wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy, + struct wireless_dev + *wdev, const void *data, + int data_len); + +int wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy, + struct wireless_dev + *wdev, const void *data, + int data_len); + +int wlan_hdd_cfg80211_extscan_set_significant_change(struct wiphy *wiphy, + struct wireless_dev + *wdev, const void *data, + int data_len); + +int wlan_hdd_cfg80211_extscan_reset_significant_change(struct wiphy + *wiphy, + struct + wireless_dev + *wdev, const void *data, + int data_len); + +int wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * wlan_hdd_cfg80211_extscan_callback() - ext scan callback + * @hdd_handle: Opaque handle to hdd context + * @event_id: Event identifier + * @msg: Pointer to message + * + * Return: none + */ +void wlan_hdd_cfg80211_extscan_callback(hdd_handle_t hdd_handle, + const uint16_t event_id, void *msg); + +void wlan_hdd_cfg80211_extscan_init(struct hdd_context *hdd_ctx); + +#else /* FEATURE_WLAN_EXTSCAN */ + +static inline +void wlan_hdd_cfg80211_extscan_callback(hdd_handle_t hdd_handle, + const uint16_t event_id, void *msg) +{ +} + +static inline void wlan_hdd_cfg80211_extscan_init(struct hdd_context *hdd_ctx) +{ +} + +#endif /* End of FEATURE_WLAN_EXTSCAN */ + +#endif /* end #if !defined(WLAN_HDD_EXT_SCAN_H) */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_fips.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_fips.c new file mode 100644 index 0000000000000000000000000000000000000000..c13ab57f45c8ccee23e32264cafd4a2006543814 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_fips.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_fips.c + * + * WLAN Host Device Driver FIPS Certification Feature + */ + +#include "osif_sync.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_fips.h" +#include "wlan_osif_request_manager.h" +#include "qdf_mem.h" +#include "sme_api.h" + +#define WLAN_WAIT_TIME_FIPS 5000 + +/** + * hdd_fips_context - hdd fips context + * @status: status of response. 0: no error, -ENOMEM: unable to allocate + * memory for the response payload + * @request: fips request + * @response: fips response + */ +struct hdd_fips_context { + int status; + struct fips_params request; + struct wmi_host_fips_event_param response; +}; + +/** + * hdd_fips_event_dup () - duplicate a fips event + * @dest: destination event + * @src: source event + * + * Make a "deep" duplicate of a FIPS event + * + * Return: 0 if the event was duplicated, otherwise an error + */ +static int hdd_fips_event_dup(struct wmi_host_fips_event_param *dest, + const struct wmi_host_fips_event_param *src) +{ + *dest = *src; + if (dest->data_len) { + dest->data = qdf_mem_malloc(dest->data_len); + if (!dest->data) { + hdd_err("memory allocation failed"); + return -ENOMEM; + } + qdf_mem_copy(dest->data, src->data, src->data_len); + } else { + /* make sure we don't have a rogue pointer */ + dest->data = NULL; + } + + return 0; +} + +/** + * hdd_fips_cb () - fips response message handler + * @cookie: hdd request cookie + * @response: fips response parameters + * + * Return: none + */ +static void hdd_fips_cb(void *cookie, + struct wmi_host_fips_event_param *response) +{ + struct osif_request *request; + struct hdd_fips_context *context; + + hdd_enter(); + + if (!response) { + hdd_err("response is NULL"); + return; + } + + request = osif_request_get(cookie); + if (!request) { + hdd_debug("Obsolete request"); + return; + } + + hdd_debug("pdev_id %u, status %u, data_len %u", + response->pdev_id, + response->error_status, + response->data_len); + qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + response->data, response->data_len); + + context = osif_request_priv(request); + if (response->error_status) { + context->status = -ETIMEDOUT; + } else { + context->status = hdd_fips_event_dup(&context->response, + response); + } + + osif_request_complete(request); + osif_request_put(request); + hdd_exit(); +} + +static void hdd_fips_context_dealloc(void *priv) +{ + struct hdd_fips_context *context = priv; + + qdf_mem_free(context->response.data); +} + + +static int hdd_fips_validate_request(struct iw_fips_test_request *user_request, + uint32_t request_len) +{ + uint32_t expected_data_len; + + if (request_len < sizeof(*user_request)) { + hdd_debug("Request len %u is too small", request_len); + return -EINVAL; + } + + if ((user_request->key_len != FIPS_KEY_LENGTH_128) && + (user_request->key_len != FIPS_KEY_LENGTH_256)) { + hdd_debug("Invalid key len %u", user_request->key_len); + return -EINVAL; + } + + expected_data_len = request_len - sizeof(*user_request); + if (expected_data_len != user_request->data_len) { + hdd_debug("Unexpected data_len %u for request_len %u", + user_request->data_len, request_len); + return -EINVAL; + } + + if ((user_request->mode != FIPS_ENGINE_AES_CTR) && + (user_request->mode != FIPS_ENGINE_AES_MIC)) { + hdd_debug("Invalid mode %u", user_request->mode); + return -EINVAL; + } + + if ((user_request->operation != FIPS_ENCRYPT_CMD) && + (user_request->operation != FIPS_DECRYPT_CMD)) { + hdd_debug("Invalid operation %u", user_request->operation); + return -EINVAL; + } + + return 0; +} + +static int __hdd_fips_test(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + struct iw_fips_test_request *user_request; + struct iw_fips_test_response *user_response; + uint32_t request_len; + int ret; + QDF_STATUS qdf_status; + void *cookie; + struct osif_request *request; + struct hdd_fips_context *context; + struct fips_params *fips_request; + struct wmi_host_fips_event_param *fips_response; + static const struct osif_request_params params = { + .priv_size = sizeof(*context), + .timeout_ms = WLAN_WAIT_TIME_FIPS, + .dealloc = hdd_fips_context_dealloc, + }; + + hdd_enter_dev(dev); + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + user_request = (struct iw_fips_test_request *)extra; + request_len = wrqu->data.length; + ret = hdd_fips_validate_request(user_request, request_len); + if (ret) + return ret; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + context = osif_request_priv(request); + fips_request = &context->request; + fips_request->key = &user_request->key[0]; + fips_request->key_len = user_request->key_len; + fips_request->data = &user_request->data[0]; + fips_request->data_len = user_request->data_len; + fips_request->mode = user_request->mode; + fips_request->op = user_request->operation; + fips_request->pdev_id = WMI_PDEV_ID_1ST; + + cookie = osif_request_cookie(request); + qdf_status = sme_fips_request(hdd_ctx->mac_handle, &context->request, + hdd_fips_cb, cookie); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Unable to post fips message"); + ret = -EINVAL; + goto cleanup; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("Target response timed out"); + goto cleanup; + } + + ret = context->status; + if (ret) { + hdd_err("Target response processing failed"); + goto cleanup; + } + + fips_response = &context->response; + if (fips_response->data_len != fips_request->data_len) { + hdd_err("Data length mismatch, got %u, expected %u", + fips_response->data_len, fips_request->data_len); + ret = -EINVAL; + goto cleanup; + } + user_response = (struct iw_fips_test_response *)extra; + user_response->status = context->status; + if (user_response->status) { + user_response->data_len = 0; + } else { + user_response->data_len = fips_response->data_len; + qdf_mem_copy(user_response->data, fips_response->data, + fips_response->data_len); + } + + /* + * By default wireless extensions private ioctls have either + * SET semantics (even numbered ioctls) or GET semantics (odd + * numbered ioctls). This is an even numbered ioctl so the SET + * semantics apply. This means the core kernel ioctl code took + * care of copying the request parameters from userspace to + * kernel space. However this ioctl also needs to return the + * response. Since the core kernel ioctl code doesn't support + * SET ioctls returning anything other than status, we have to + * explicitly copy the result to userspace. + */ + wrqu->data.length = sizeof(*user_response) + user_response->data_len; + if (copy_to_user(wrqu->data.pointer, user_response, wrqu->data.length)) + ret = -EFAULT; + +cleanup: + osif_request_put(request); + + hdd_exit(); + return ret; +} + +int hdd_fips_test(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_fips_test(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_fips.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_fips.h new file mode 100644 index 0000000000000000000000000000000000000000..945d5c630ff7088fe5ff16ae8e8e5ee681f90a94 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_fips.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_fips.h + * + * WLAN Host Device Driver FIPS Certification Feature + */ + +#ifndef __WLAN_HDD_FIPS_H__ +#define __WLAN_HDD_FIPS_H__ + +struct net_device; +struct iw_request_info; +union iwreq_data; +struct hdd_adapter; +void fips_test(struct hdd_adapter *adapter); + +#define FIPS_KEY_LEN 32 +struct iw_fips_test_request { + uint32_t operation; + uint32_t mode; + uint32_t key_len; + uint8_t key[FIPS_KEY_LEN]; + uint32_t data_len; + uint8_t data[0]; +}; + +struct iw_fips_test_response { + uint32_t status; + uint32_t data_len; + uint8_t data[0]; +}; + + +/** + * hdd_fips_test() - Perform FIPS test + * @dev: netdev upon which the FIPS test ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data structure + * @extra: extra payload for wireless extensions ioctl + * + * This API implements the FIPS test interface. Upon entry the @extra + * buffer will contain a FIPS test vector formated as a &struct + * iw_fips_test_request. This vector will be sent to firmware where it + * will be run through the appropriate hardware. The result of the + * operation will be sent back to userspace via @extra encoded as a + * &struct iw_fips_test_response. + * + * Return: 0 if the test vector was processed, otherwise a negative + * errno. + */ + +int hdd_fips_test(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +#endif /* __WLAN_HDD_FIPS_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ftm.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ftm.c new file mode 100644 index 0000000000000000000000000000000000000000..0892a3e2b48f1c0101ac8618492407d915119a82 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ftm.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_ftm.c + * + * This file contains the WLAN factory test mode implementation + */ + +#include "cds_sched.h" +#include +#include "sir_types.h" +#include "qdf_types.h" +#include "sir_api.h" +#include "sir_mac_prot_def.h" +#include "sme_api.h" +#include "mac_init_api.h" +#include "wlan_qct_sys.h" +#include "wlan_hdd_misc.h" +#include "i_cds_packet.h" +#include "cds_reg_service.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_lpass.h" +#include "qwlan_version.h" +#include "wma_types.h" + +#ifdef QCA_WIFI_FTM + +#include "wlan_hdd_cfg80211.h" +#include "hif.h" +#include +#include + +/** + * hdd_update_cds_config_ftm() - API to update cds configuration parameters + * for FTM mode. + * @hdd_ctx: HDD Context + * + * Return: 0 on success; errno on failure + */ + +int hdd_update_cds_config_ftm(struct hdd_context *hdd_ctx) +{ + struct cds_config_info *cds_cfg; + QDF_STATUS status; + bool self_recovery; + + cds_cfg = qdf_mem_malloc(sizeof(*cds_cfg)); + if (!cds_cfg) { + hdd_err("failed to allocate cds config"); + return -ENOMEM; + } + + status = ucfg_mlme_get_self_recovery(hdd_ctx->psoc, &self_recovery); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get self recovery ini config"); + return -EIO; + } + + cds_cfg->driver_type = QDF_DRIVER_TYPE_MFG; + hdd_lpass_populate_cds_config(cds_cfg, hdd_ctx); + cds_cfg->sub_20_channel_width = WLAN_SUB_20_CH_WIDTH_NONE; + cds_cfg->self_recovery_enabled = self_recovery; + cds_cfg->num_vdevs = hdd_ctx->config->num_vdevs; + cds_init_ini_config(cds_cfg); + + return 0; +} + +#ifdef LINUX_QCMBR + +/** + * wlan_hdd_qcmbr_ioctl() - Standard QCMBR ioctl handler + * @adapter: adapter upon which the ioctl was received + * @ifr: the ioctl request + * + * Return: 0 on success, non-zero on error + */ +static int wlan_hdd_qcmbr_ioctl(struct hdd_adapter *adapter, struct ifreq *ifr) +{ + int ret, cmd; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (get_user(cmd, (int *)ifr->ifr_data) != 0) + return QDF_STATUS_E_FAILURE; + + ret = wlan_ioctl_ftm_testmode_cmd(hdd_ctx->pdev, cmd, + (uint8_t *)ifr->ifr_data + sizeof(cmd)); + + return ret; +} + +/** + * wlan_hdd_qcmbr_unified_ioctl() - Unified QCMBR ioctl handler + * @adapter: adapter upon which the ioctl was received + * @ifr: the ioctl request + * + * Return: 0 on success, non-zero on error + */ +int wlan_hdd_qcmbr_unified_ioctl(struct hdd_adapter *adapter, + struct ifreq *ifr) +{ + int ret; + + ret = wlan_hdd_qcmbr_ioctl(adapter, ifr); + + return ret; +} + +#endif /* LINUX_QCMBR */ +#endif /* QCA_WIFI_FTM */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_fw_state.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_fw_state.c new file mode 100644 index 0000000000000000000000000000000000000000..f78e6b18b8d628e59afbd766c40e3c616072f579 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_fw_state.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_fw_state.c + * + * The implementation for getting firmware state + */ + +#include "osif_sync.h" +#include "qca_vendor.h" +#include "wlan_hdd_fw_state.h" +#include "wlan_hdd_main.h" +#include "wlan_osif_request_manager.h" +#include "wmi_unified_param.h" + +struct fw_state { + bool fw_active; +}; + +/** + * hdd_get_fw_state_cb() - Callback function to get fw state + * @context: opaque context originally passed to SME. HDD always passes + * a cookie for the request context + * + * This function receives the response/data from the lower layer and + * checks to see if the thread is still waiting then post the results to + * upper layer, if the request has timed out then ignore. + * + * Return: None + */ +static void hdd_get_fw_state_cb(void *context) +{ + struct osif_request *request; + struct fw_state *priv; + + hdd_enter(); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + priv->fw_active = true; + osif_request_complete(request); + osif_request_put(request); +} + +/** + * hdd_post_get_fw_state_rsp - send rsp to user space + * @hdd_ctx: pointer to hdd context + * @state: true for fw active, false for fw error state + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_post_get_fw_state_rsp(struct hdd_context *hdd_ctx, + bool state) +{ + struct sk_buff *skb; + enum qca_wlan_vendor_attr_fw_state fw_state; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, + sizeof(uint8_t) + + NLA_HDRLEN + + NLMSG_HDRLEN); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return -ENOMEM; + } + + if (state) + fw_state = QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE; + else + fw_state = QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR; + + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_FW_STATE, + (uint8_t)fw_state)) { + hdd_err("put fail"); + goto nla_put_failure; + } + + cfg80211_vendor_cmd_reply(skb); + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * __wlan_hdd_cfg80211_get_fw_state() - get fw state + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This function sends a request to fw and waits on a timer to + * invoke the callback. if the callback is invoked then true + * will be returned or otherwise fail status will be returned. + * + * Return: 0 on success; error number otherwise. + **/ +static int __wlan_hdd_cfg80211_get_fw_state(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + mac_handle_t mac_handle; + QDF_STATUS status; + int retval; + void *cookie; + struct osif_request *request; + struct fw_state *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + bool state = false; + + hdd_enter_dev(wdev->netdev); + + retval = wlan_hdd_validate_context(hdd_ctx); + if (retval) + return retval; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + mac_handle = hdd_ctx->mac_handle; + status = sme_get_fw_state(mac_handle, + hdd_get_fw_state_cb, + cookie); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Unable to get fw state"); + retval = qdf_status_to_os_return(status); + } else { + retval = osif_request_wait_for_response(request); + if (retval) { + hdd_err("Target response timed out"); + state = false; + } else { + priv = osif_request_priv(request); + state = priv->fw_active; + } + } + retval = hdd_post_get_fw_state_rsp(hdd_ctx, state); + if (retval) + hdd_err("Failed to post fw state"); + + osif_request_put(request); + + hdd_exit(); + return retval; +} + +/** + * wlan_hdd_cfg80211_get_fw_status() - get fw state + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * Return: 0 on success; error number otherwise. + */ +int wlan_hdd_cfg80211_get_fw_state(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_fw_state(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_gpio.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_gpio.c new file mode 100644 index 0000000000000000000000000000000000000000..b2a48695506de8baf26b5cf1cec507ba3f1edbea --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_gpio.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: defines driver functions interfacing with linux kernel + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_hdd_main.h" +#include "cfg_ucfg_api.h" +#include + +/** + * wlan_cfg80211_set_gpio_config - Set the gpio configuration + * @wiphy: pointer to wiphy + * @wdev: pointer to wireless_wdev + * @data: pointer to data + * @data_len: data length + * + * __wlan_cfg80211_set_gpio_config will forward the GPIO setting to FW by + * WMI_GPIO_CONFIG/OUTPUT_CMDID + * + * Return: 0 on success; errno on failure + */ +static int +__wlan_hdd_cfg80211_set_gpio_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter; + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + ret = wlan_cfg80211_start_gpio_config(wiphy, + hdd_ctx->psoc, + data, data_len); + hdd_exit(); + + return ret; +} + +/** + * wlan_hdd_cfg80211_set_gpio_config() - Set GPIO Configuration + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +int wlan_hdd_cfg80211_set_gpio_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + hdd_err("__DEBUG__"); + errno = __wlan_hdd_cfg80211_set_gpio_config(wiphy, + wdev, + data, + data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_green_ap.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_green_ap.c new file mode 100644 index 0000000000000000000000000000000000000000..cd37c10226ce1cac8dedfbc81da9aea3df9aca70 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_green_ap.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2012-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_green_ap.c + * + * WLAN Host Device Driver Green AP implementation + * + */ + +#include +#include +#include +#include +#include "wlan_mlme_ucfg_api.h" + +/** + * hdd_green_ap_check_enable() - to check whether to enable green ap or not + * @hdd_ctx: hdd context + * @enable_green_ap: 1 - enable green ap enabled, 0 - disbale green ap + * + * Return: 0 - success, < 0 - failure + */ +static int hdd_green_ap_check_enable(struct hdd_context *hdd_ctx, + bool *enable_green_ap) +{ + uint8_t num_sessions, mode; + QDF_STATUS status; + + for (mode = 0; + mode < QDF_MAX_NO_OF_MODE; + mode++) { + if (mode == QDF_SAP_MODE || mode == QDF_P2P_GO_MODE) + continue; + + status = policy_mgr_mode_specific_num_active_sessions( + hdd_ctx->psoc, mode, &num_sessions); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to get num sessions for mode: %d", + mode); + return -EINVAL; + } else if (num_sessions) { + *enable_green_ap = false; + hdd_debug("active sessions for mode: %d is %d disable green AP", + mode, num_sessions); + return 0; + } + } + *enable_green_ap = true; + return 0; +} + +void hdd_green_ap_add_sta(struct hdd_context *hdd_ctx) +{ + wlan_green_ap_add_sta(hdd_ctx->pdev); +} + +void hdd_green_ap_del_sta(struct hdd_context *hdd_ctx) +{ + wlan_green_ap_del_sta(hdd_ctx->pdev); +} + +int hdd_green_ap_enable_egap(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + status = ucfg_green_ap_enable_egap(hdd_ctx->pdev); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_debug("enhance green ap is not enabled, status %d", + status); + return qdf_status_to_os_return(status); + } + + return 0; +} + +int hdd_green_ap_start_state_mc(struct hdd_context *hdd_ctx, + enum QDF_OPMODE mode, bool is_session_start) +{ + struct hdd_config *cfg; + bool enable_green_ap = false; + uint8_t num_sap_sessions = 0, num_p2p_go_sessions = 0, ret = 0; + QDF_STATUS status; + bool bval = false; + uint8_t ps_enable; + + cfg = hdd_ctx->config; + if (!cfg) { + hdd_err("NULL hdd config"); + return -EINVAL; + } + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("unable to get vht_enable2x2"); + return -EINVAL; + } + + if (!bval) { + hdd_debug(" 2x2 not enabled"); + } + + if (QDF_IS_STATUS_ERROR(ucfg_green_ap_get_ps_config(hdd_ctx->pdev, + &ps_enable))) + return 0; + + if (!ps_enable) { + hdd_debug("Green AP not enabled"); + return 0; + } + + policy_mgr_mode_specific_num_active_sessions(hdd_ctx->psoc, + QDF_SAP_MODE, + &num_sap_sessions); + policy_mgr_mode_specific_num_active_sessions(hdd_ctx->psoc, + QDF_P2P_GO_MODE, + &num_p2p_go_sessions); + + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_IBSS_MODE: + if (!num_sap_sessions && !num_p2p_go_sessions) + return 0; + + if (is_session_start) { + hdd_debug("Disabling Green AP"); + ucfg_green_ap_set_ps_config(hdd_ctx->pdev, + false); + wlan_green_ap_stop(hdd_ctx->pdev); + } else { + ret = hdd_green_ap_check_enable(hdd_ctx, + &enable_green_ap); + if (!ret) { + if (enable_green_ap) { + hdd_debug("Enabling Green AP"); + ucfg_green_ap_set_ps_config( + hdd_ctx->pdev, true); + wlan_green_ap_start(hdd_ctx->pdev); + } + } else { + hdd_err("Failed to check Green AP enable status"); + } + } + break; + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + if (is_session_start) { + ret = hdd_green_ap_check_enable(hdd_ctx, + &enable_green_ap); + if (!ret) { + if (enable_green_ap) { + hdd_debug("Enabling Green AP"); + ucfg_green_ap_set_ps_config( + hdd_ctx->pdev, true); + wlan_green_ap_start(hdd_ctx->pdev); + } + } else { + hdd_err("Failed to check Green AP enable status"); + } + } else { + if (!num_sap_sessions && !num_p2p_go_sessions) { + hdd_debug("Disabling Green AP"); + ucfg_green_ap_set_ps_config(hdd_ctx->pdev, + false); + wlan_green_ap_stop(hdd_ctx->pdev); + } + } + break; + default: + break; + } + return ret; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_green_ap.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_green_ap.h new file mode 100644 index 0000000000000000000000000000000000000000..d4b8b5e54dfd60f09a481142ec8eebd1b94c06fa --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_green_ap.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_GREEN_AP_H) +#define WLAN_HDD_GREEN_AP_H + +#include "qdf_types.h" + +struct hdd_context; + +#ifdef WLAN_SUPPORT_GREEN_AP + +/** + * hdd_green_ap_add_sta() - Notify Green AP on STA association + * @hdd_ctx: Global HDD context + * + * Call this function when new node is associated + * + * Return: void + */ +void hdd_green_ap_add_sta(struct hdd_context *hdd_ctx); + +/** + * hdd_green_ap_del_sta() - Notify Green AP on STA disassociation + * @hdd_ctx: Global HDD context + * + * Call this function when new node is disassociated + * + * Return: void + */ +void hdd_green_ap_del_sta(struct hdd_context *hdd_ctx); + +/** + * hdd_green_ap_enable_egap() - Enable Enhanced Green AP + * @hdd_ctx: Global HDD context + * + * This function will enable the Enhanced Green AP feature if it is supported + * by the Green AP component. + * + * Return: 0 on success, negative errno on any failure + */ +int hdd_green_ap_enable_egap(struct hdd_context *hdd_ctx); + +/** + * hdd_green_ap_start_state_mc() - to start green AP state mc based on + * present concurrency and state of green AP state machine. + * @hdd_ctx: hdd context + * @mode: device mode + * @is_session_start: BSS start/stop + * + * Return: 0 on success, negative errno on any failure + */ +int hdd_green_ap_start_state_mc(struct hdd_context *hdd_ctx, + enum QDF_OPMODE mode, bool is_session_start); + +#else /* WLAN_SUPPORT_GREEN_AP */ +static inline +void hdd_green_ap_add_sta(struct hdd_context *hdd_ctx) +{ +} + +static inline +void hdd_green_ap_del_sta(struct hdd_context *hdd_ctx) +{ +} + +static inline +int hdd_green_ap_enable_egap(struct hdd_context *hdd_ctx) +{ + return 0; +} + +static inline +int hdd_green_ap_start_state_mc(struct hdd_context *hdd_ctx, + enum QDF_OPMODE mode, bool is_session_start) +{ + return 0; +} + +#endif /* WLAN_SUPPORT_GREEN_AP */ + +#endif /* !defined(WLAN_HDD_GREEN_AP_H) */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hang_event.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hang_event.c new file mode 100644 index 0000000000000000000000000000000000000000..ef793545b54bddc161a7de66fb94e42b0bf5813a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hang_event.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include "wlan_hdd_object_manager.h" +#include + +struct hdd_hang_event_fixed_param { + uint16_t tlv_header; + uint8_t vdev_id; + uint8_t vdev_opmode; + uint8_t vdev_state; + uint8_t vdev_substate; +} qdf_packed; + +struct hdd_scan_fixed_param { + uint16_t tlv_header; + uint8_t last_scan_reject_vdev_id; + enum scan_reject_states last_scan_reject_reason; + unsigned long last_scan_reject_timestamp; + uint8_t scan_reject_cnt; +} qdf_packed; + +static int wlan_hdd_recovery_notifier_call(struct notifier_block *block, + unsigned long state, + void *data) +{ + qdf_notif_block *notif_block = qdf_container_of(block, qdf_notif_block, + notif_block); + struct hdd_context *hdd_ctx; + struct qdf_notifer_data *hdd_hang_data = data; + uint8_t *hdd_buf_ptr; + struct hdd_adapter *adapter, *next_adapter = NULL; + uint32_t total_len; + struct wlan_objmgr_vdev *vdev; + struct hdd_hang_event_fixed_param *cmd; + struct hdd_scan_fixed_param *cmd_scan; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_RECOVERY_NOTIFIER_CALL; + + if (!data) + return NOTIFY_STOP_MASK; + + hdd_ctx = notif_block->priv_data; + if (!hdd_ctx) + return NOTIFY_STOP_MASK; + + if (state == QDF_SCAN_ATTEMPT_FAILURES) { + total_len = sizeof(*cmd_scan); + hdd_buf_ptr = hdd_hang_data->hang_data + hdd_hang_data->offset; + if (hdd_hang_data->offset + total_len > QDF_WLAN_HANG_FW_OFFSET) + return NOTIFY_STOP_MASK; + cmd_scan = (struct hdd_scan_fixed_param *)hdd_buf_ptr; + QDF_HANG_EVT_SET_HDR(&cmd_scan->tlv_header, + HANG_EVT_TAG_OS_IF_SCAN, + QDF_HANG_GET_STRUCT_TLVLEN(struct hdd_scan_fixed_param)); + cmd_scan->last_scan_reject_vdev_id = + hdd_ctx->last_scan_reject_vdev_id; + cmd_scan->last_scan_reject_reason = + hdd_ctx->last_scan_reject_reason; + cmd_scan->scan_reject_cnt = + hdd_ctx->scan_reject_cnt; + hdd_hang_data->offset += total_len; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + total_len = sizeof(*cmd); + hdd_buf_ptr = hdd_hang_data->hang_data + hdd_hang_data->offset; + if (hdd_hang_data->offset + total_len > + QDF_WLAN_HANG_FW_OFFSET) { + hdd_objmgr_put_vdev(vdev); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return NOTIFY_STOP_MASK; + } + cmd = (struct hdd_hang_event_fixed_param *)hdd_buf_ptr; + QDF_HANG_EVT_SET_HDR(&cmd->tlv_header, + HANG_EVT_TAG_OS_IF, + QDF_HANG_GET_STRUCT_TLVLEN(struct hdd_hang_event_fixed_param)); + cmd->vdev_id = wlan_vdev_get_id(vdev); + cmd->vdev_opmode = wlan_vdev_mlme_get_opmode(vdev); + cmd->vdev_state = wlan_vdev_mlme_get_state(vdev); + cmd->vdev_substate = wlan_vdev_mlme_get_substate(vdev); + hdd_hang_data->offset += total_len; + hdd_objmgr_put_vdev(vdev); + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return NOTIFY_OK; +} + +static qdf_notif_block hdd_recovery_notifier = { + .notif_block.notifier_call = wlan_hdd_recovery_notifier_call, +}; + +QDF_STATUS wlan_hdd_hang_event_notifier_register(struct hdd_context *hdd_ctx) +{ + hdd_recovery_notifier.priv_data = hdd_ctx; + return qdf_hang_event_register_notifier(&hdd_recovery_notifier); +} + +QDF_STATUS wlan_hdd_hang_event_notifier_unregister(void) +{ + return qdf_hang_event_unregister_notifier(&hdd_recovery_notifier); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_he.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_he.c new file mode 100644 index 0000000000000000000000000000000000000000..db90f32caeaddf60a7f6195984d5a9f938d749bc --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_he.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_he.c + * + * WLAN Host Device Driver file for 802.11ax (High Efficiency) support. + * + */ + +#include "wlan_hdd_main.h" +#include "wlan_hdd_he.h" +#include "osif_sync.h" +#include "wma_he.h" +#include "wlan_utility.h" +#include "wlan_mlme_ucfg_api.h" + +void hdd_update_tgt_he_cap(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *cfg) +{ + QDF_STATUS status; + tDot11fIEhe_cap he_cap_ini = {0}; + uint8_t value = 0; + + ucfg_mlme_update_tgt_he_cap(hdd_ctx->psoc, cfg); + + status = ucfg_mlme_cfg_get_vht_tx_bfee_ant_supp(hdd_ctx->psoc, + &value); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get tx_bfee_ant_supp"); + + he_cap_ini.bfee_sts_lt_80 = value; + sme_update_tgt_he_cap(hdd_ctx->mac_handle, cfg, &he_cap_ini); +} + +void wlan_hdd_check_11ax_support(struct hdd_beacon_data *beacon, + struct sap_config *config) +{ + const uint8_t *ie; + + ie = wlan_get_ext_ie_ptr_from_ext_id(HE_CAP_OUI_TYPE, HE_CAP_OUI_SIZE, + beacon->tail, beacon->tail_len); + if (ie) + config->SapHw_mode = eCSR_DOT11_MODE_11ax; +} + +int hdd_update_he_cap_in_cfg(struct hdd_context *hdd_ctx) +{ + uint32_t val; + uint32_t val1 = 0; + QDF_STATUS status; + int ret; + uint8_t enable_ul_ofdma, enable_ul_mimo; + + status = ucfg_mlme_cfg_get_he_ul_mumimo(hdd_ctx->psoc, &val); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("could not get CFG_HE_UL_MUMIMO"); + return qdf_status_to_os_return(status); + } + + /* In val, + * Bit 1 - corresponds to UL MIMO + * Bit 2 - corresponds to UL OFDMA + */ + ret = ucfg_mlme_cfg_get_enable_ul_mimo(hdd_ctx->psoc, + &enable_ul_mimo); + if (ret) + return ret; + ret = ucfg_mlme_cfg_get_enable_ul_ofdm(hdd_ctx->psoc, + &enable_ul_ofdma); + if (ret) + return ret; + if (val & 0x1 || (val >> 1) & 0x1) + val1 = enable_ul_mimo & 0x1; + + if ((val >> 1) & 0x1) + val1 |= ((enable_ul_ofdma & 0x1) << 1); + + ret = ucfg_mlme_cfg_set_he_ul_mumimo(hdd_ctx->psoc, val1); + + return ret; +} + +/* + * __wlan_hdd_cfg80211_get_he_cap() - get HE Capabilities + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 if success, non-zero for failure + */ +static int +__wlan_hdd_cfg80211_get_he_cap(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int ret; + QDF_STATUS status; + struct sk_buff *reply_skb; + uint32_t nl_buf_len; + struct he_capability he_cap; + uint8_t he_supported = 0; + + hdd_enter(); + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + nl_buf_len = NLMSG_HDRLEN; + if (sme_is_feature_supported_by_fw(DOT11AX)) { + he_supported = 1; + + status = wma_get_he_capabilities(&he_cap); + if (QDF_STATUS_SUCCESS != status) + return -EINVAL; + } else { + hdd_info("11AX: HE not supported, send only QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED"); + } + + if (he_supported) { + nl_buf_len += NLA_HDRLEN + sizeof(he_supported) + + NLA_HDRLEN + sizeof(he_cap.phy_cap) + + NLA_HDRLEN + sizeof(he_cap.mac_cap) + + NLA_HDRLEN + sizeof(he_cap.mcs) + + NLA_HDRLEN + sizeof(he_cap.ppet.numss_m1) + + NLA_HDRLEN + sizeof(he_cap.ppet.ru_bit_mask) + + NLA_HDRLEN + + sizeof(he_cap.ppet.ppet16_ppet8_ru3_ru0); + } else { + nl_buf_len += NLA_HDRLEN + sizeof(he_supported); + } + + hdd_info("11AX: he_supported: %d", he_supported); + + reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, nl_buf_len); + + if (!reply_skb) { + hdd_err("Allocate reply_skb failed"); + return -EINVAL; + } + + if (nla_put_u8(reply_skb, + QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED, he_supported)) + goto nla_put_failure; + + /* No need to populate other attributes if HE is not supported */ + if (0 == he_supported) + goto end; + + if (nla_put_u32(reply_skb, + QCA_WLAN_VENDOR_ATTR_MAC_CAPAB, he_cap.mac_cap) || + nla_put_u32(reply_skb, + QCA_WLAN_VENDOR_ATTR_HE_MCS, he_cap.mcs) || + nla_put_u32(reply_skb, + QCA_WLAN_VENDOR_ATTR_NUM_SS, he_cap.ppet.numss_m1) || + nla_put_u32(reply_skb, + QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK, + he_cap.ppet.ru_bit_mask) || + nla_put(reply_skb, + QCA_WLAN_VENDOR_ATTR_PHY_CAPAB, + sizeof(u32) * HE_MAX_PHY_CAP_SIZE, he_cap.phy_cap) || + nla_put(reply_skb, QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD, + sizeof(u32) * PSOC_HOST_MAX_NUM_SS, + he_cap.ppet.ppet16_ppet8_ru3_ru0)) + goto nla_put_failure; +end: + ret = cfg80211_vendor_cmd_reply(reply_skb); + hdd_exit(); + return ret; + +nla_put_failure: + hdd_err("nla put fail"); + kfree_skb(reply_skb); + return -EINVAL; +} + +int wlan_hdd_cfg80211_get_he_cap(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_he_cap(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd.c new file mode 100644 index 0000000000000000000000000000000000000000..bea7dcf0d8aa53dc5655a007bca9d4dbe7fc4211 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd.c @@ -0,0 +1,6968 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_hostapd.c + * + * WLAN Host Device Driver implementation + */ + +/* Include Files */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "osif_sync.h" +#include +#include +#include +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_hdd_p2p.h" +#include +#include "wni_cfg.h" +#include "wlan_hdd_misc.h" +#include +#include "pld_common.h" +#include "wma.h" +#ifdef WLAN_DEBUG +#include "wma_api.h" +#endif +#include "wlan_hdd_trace.h" +#include "qdf_str.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_hdd_cfg.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_hdd_tsf.h" +#include +#include "wlan_hdd_object_manager.h" +#include +#include +#include "wlan_hdd_he.h" +#include "wlan_dfs_tgt_api.h" +#include +#include "wlan_utility.h" +#include +#include "sir_api.h" +#include "wlan_policy_mgr_ucfg.h" +#include "sme_api.h" +#include "wlan_hdd_regulatory.h" +#include +#include +#include "wlan_mlme_ucfg_api.h" +#include "cfg_ucfg_api.h" +#include "wlan_crypto_global_api.h" +#include "wlan_action_oui_ucfg_api.h" +#include "wlan_fwol_ucfg_api.h" +#include "nan_ucfg_api.h" +#include +#include "wlan_hdd_sta_info.h" + +#define ACS_SCAN_EXPIRY_TIMEOUT_S 4 + +/* + * Defines the BIT position of 11bg/11abg/11abgn 802.11 support mode field + * of stainfo + */ +#define HDD_80211_MODE_ABGN 0 +/* Defines the BIT position of 11ac support mode field of stainfo */ +#define HDD_80211_MODE_AC 1 +/* Defines the BIT position of 11ax support mode field of stainfo */ +#define HDD_80211_MODE_AX 2 + +#define HDD_MAX_CUSTOM_START_EVENT_SIZE 64 + +#ifdef NDP_SAP_CONCURRENCY_ENABLE +#define MAX_SAP_NUM_CONCURRENCY_WITH_NAN 2 +#else +#define MAX_SAP_NUM_CONCURRENCY_WITH_NAN 1 +#endif + +#ifndef BSS_MEMBERSHIP_SELECTOR_HT_PHY +#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 +#endif + +#ifndef BSS_MEMBERSHIP_SELECTOR_VHT_PHY +#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126 +#endif + +#ifndef BSS_MEMBERSHIP_SELECTOR_SAE_H2E +#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E 123 +#endif + +#ifndef BSS_MEMBERSHIP_SELECTOR_HE_PHY +#define BSS_MEMBERSHIP_SELECTOR_HE_PHY 122 +#endif + +/* + * 11B, 11G Rate table include Basic rate and Extended rate + * The IDX field is the rate index + * The HI field is the rate when RSSI is strong or being ignored + * (in this case we report actual rate) + * The MID field is the rate when RSSI is moderate + * (in this case we cap 11b rates at 5.5 and 11g rates at 24) + * The LO field is the rate when RSSI is low + * (in this case we don't report rates, actual current rate used) + */ +static const struct index_data_rate_type supported_data_rate[] = { + /* IDX HI HM LM LO (RSSI-based index */ + {2, { 10, 10, 10, 0} }, + {4, { 20, 20, 10, 0} }, + {11, { 55, 20, 10, 0} }, + {12, { 60, 55, 20, 0} }, + {18, { 90, 55, 20, 0} }, + {22, {110, 55, 20, 0} }, + {24, {120, 90, 60, 0} }, + {36, {180, 120, 60, 0} }, + {44, {220, 180, 60, 0} }, + {48, {240, 180, 90, 0} }, + {66, {330, 180, 90, 0} }, + {72, {360, 240, 90, 0} }, + {96, {480, 240, 120, 0} }, + {108, {540, 240, 120, 0} } +}; + +/* MCS Based rate table */ +/* HT MCS parameters with Nss = 1 */ +static const struct index_data_rate_type supported_mcs_rate_nss1[] = { + /* MCS L20 L40 S20 S40 */ + {0, { 65, 135, 72, 150} }, + {1, { 130, 270, 144, 300} }, + {2, { 195, 405, 217, 450} }, + {3, { 260, 540, 289, 600} }, + {4, { 390, 810, 433, 900} }, + {5, { 520, 1080, 578, 1200} }, + {6, { 585, 1215, 650, 1350} }, + {7, { 650, 1350, 722, 1500} } +}; + +/* HT MCS parameters with Nss = 2 */ +static const struct index_data_rate_type supported_mcs_rate_nss2[] = { + /* MCS L20 L40 S20 S40 */ + {0, {130, 270, 144, 300} }, + {1, {260, 540, 289, 600} }, + {2, {390, 810, 433, 900} }, + {3, {520, 1080, 578, 1200} }, + {4, {780, 1620, 867, 1800} }, + {5, {1040, 2160, 1156, 2400} }, + {6, {1170, 2430, 1300, 2700} }, + {7, {1300, 2700, 1444, 3000} } +}; + +/* MCS Based VHT rate table */ +/* MCS parameters with Nss = 1*/ +static const struct index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = { + /* MCS L80 S80 L40 S40 L20 S40*/ + {0, {293, 325}, {135, 150}, {65, 72} }, + {1, {585, 650}, {270, 300}, {130, 144} }, + {2, {878, 975}, {405, 450}, {195, 217} }, + {3, {1170, 1300}, {540, 600}, {260, 289} }, + {4, {1755, 1950}, {810, 900}, {390, 433} }, + {5, {2340, 2600}, {1080, 1200}, {520, 578} }, + {6, {2633, 2925}, {1215, 1350}, {585, 650} }, + {7, {2925, 3250}, {1350, 1500}, {650, 722} }, + {8, {3510, 3900}, {1620, 1800}, {780, 867} }, + {9, {3900, 4333}, {1800, 2000}, {780, 867} } +}; + +/*MCS parameters with Nss = 2*/ +static const struct index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = { + /* MCS L80 S80 L40 S40 L20 S40*/ + {0, {585, 650}, {270, 300}, {130, 144} }, + {1, {1170, 1300}, {540, 600}, {260, 289} }, + {2, {1755, 1950}, {810, 900}, {390, 433} }, + {3, {2340, 2600}, {1080, 1200}, {520, 578} }, + {4, {3510, 3900}, {1620, 1800}, {780, 867} }, + {5, {4680, 5200}, {2160, 2400}, {1040, 1156} }, + {6, {5265, 5850}, {2430, 2700}, {1170, 1300} }, + {7, {5850, 6500}, {2700, 3000}, {1300, 1444} }, + {8, {7020, 7800}, {3240, 3600}, {1560, 1733} }, + {9, {7800, 8667}, {3600, 4000}, {1730, 1920} } +}; + +/* Function definitions */ + +/** + * hdd_sap_context_init() - Initialize SAP context. + * @hdd_ctx: HDD context. + * + * Initialize SAP context. + * + * Return: 0 on success. + */ +int hdd_sap_context_init(struct hdd_context *hdd_ctx) +{ + qdf_wake_lock_create(&hdd_ctx->sap_dfs_wakelock, "sap_dfs_wakelock"); + atomic_set(&hdd_ctx->sap_dfs_ref_cnt, 0); + + mutex_init(&hdd_ctx->sap_lock); + qdf_wake_lock_create(&hdd_ctx->sap_wake_lock, "qcom_sap_wakelock"); + + return 0; +} + +/** + * hdd_hostapd_init_sap_session() - To init the sap session completely + * @adapter: SAP/GO adapter + * @reinit: if called as part of reinit + * + * This API will do + * 1) sap_init_ctx() + * + * Return: 0 if success else non-zero value. + */ +static struct sap_context * +hdd_hostapd_init_sap_session(struct hdd_adapter *adapter, bool reinit) +{ + struct sap_context *sap_ctx; + QDF_STATUS status; + + if (!adapter) { + hdd_err("invalid adapter"); + return NULL; + } + + sap_ctx = adapter->session.ap.sap_context; + + if (!sap_ctx) { + hdd_err("can't allocate the sap_ctx"); + return NULL; + } + status = sap_init_ctx(sap_ctx, adapter->device_mode, + adapter->mac_addr.bytes, + adapter->vdev_id, reinit); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("wlansap_start failed!! status: %d", status); + adapter->session.ap.sap_context = NULL; + goto error; + } + return sap_ctx; +error: + wlansap_context_put(sap_ctx); + hdd_err("releasing the sap context for session-id:%d", + adapter->vdev_id); + + return NULL; +} + +/** + * hdd_hostapd_deinit_sap_session() - To de-init the sap session completely + * @adapter: SAP/GO adapter + * + * This API will do + * 1) sap_init_ctx() + * 2) sap_destroy_ctx() + * + * Return: 0 if success else non-zero value. + */ +static int hdd_hostapd_deinit_sap_session(struct hdd_adapter *adapter) +{ + struct sap_context *sap_ctx; + int status = 0; + + if (!adapter) { + hdd_err("invalid adapter"); + return -EINVAL; + } + + sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); + if (!sap_ctx) { + hdd_debug("sap context already released, nothing to be done"); + return 0; + } + + if (!QDF_IS_STATUS_SUCCESS(sap_deinit_ctx(sap_ctx))) { + hdd_err("Error stopping the sap session"); + status = -EINVAL; + } + + if (!hdd_sap_destroy_ctx(adapter)) { + hdd_err("Error closing the sap session"); + status = -EINVAL; + } + + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_debug("sap has issue closing the session"); + else + hdd_debug("sap has been closed successfully"); + + + return status; +} + +/** + * hdd_hostapd_channel_allow_suspend() - allow suspend in a channel. + * Called when, 1. bss stopped, 2. channel switch + * + * @adapter: pointer to hdd adapter + * @chan_freq: current channel frequency + * + * Return: None + */ +static void hdd_hostapd_channel_allow_suspend(struct hdd_adapter *adapter, + uint32_t chan_freq) +{ + + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_hostapd_state *hostapd_state = + WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + + hdd_debug("bss_state: %d, chan_freq: %d, dfs_ref_cnt: %d", + hostapd_state->bss_state, chan_freq, + atomic_read(&hdd_ctx->sap_dfs_ref_cnt)); + + /* Return if BSS is already stopped */ + if (hostapd_state->bss_state == BSS_STOP) + return; + + if (!wlan_reg_chan_has_dfs_attribute_for_freq(hdd_ctx->pdev, + chan_freq)) + return; + + /* Release wakelock when no more DFS channels are used */ + if (atomic_dec_and_test(&hdd_ctx->sap_dfs_ref_cnt)) { + hdd_err("DFS: allowing suspend (chan_freq: %d)", chan_freq); + qdf_wake_lock_release(&hdd_ctx->sap_dfs_wakelock, + WIFI_POWER_EVENT_WAKELOCK_DFS); + qdf_runtime_pm_allow_suspend(&hdd_ctx->runtime_context.dfs); + + } +} + +/** + * hdd_hostapd_channel_prevent_suspend() - prevent suspend in a channel. + * Called when, 1. bss started, 2. channel switch + * + * @adapter: pointer to hdd adapter + * @chna_freq: current channel frequency + * + * Return - None + */ +static void hdd_hostapd_channel_prevent_suspend(struct hdd_adapter *adapter, + uint32_t chan_freq) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_hostapd_state *hostapd_state = + WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + + hdd_debug("bss_state: %d, chan_freq: %d, dfs_ref_cnt: %d", + hostapd_state->bss_state, chan_freq, + atomic_read(&hdd_ctx->sap_dfs_ref_cnt)); + /* Return if BSS is already started && wakelock is acquired */ + if ((hostapd_state->bss_state == BSS_START) && + (atomic_read(&hdd_ctx->sap_dfs_ref_cnt) >= 1)) + return; + + if (!wlan_reg_chan_has_dfs_attribute_for_freq(hdd_ctx->pdev, + chan_freq)) + return; + + /* Acquire wakelock if we have at least one DFS channel in use */ + if (atomic_inc_return(&hdd_ctx->sap_dfs_ref_cnt) == 1) { + hdd_err("DFS: preventing suspend (chan_freq: %d)", chan_freq); + qdf_runtime_pm_prevent_suspend(&hdd_ctx->runtime_context.dfs); + qdf_wake_lock_acquire(&hdd_ctx->sap_dfs_wakelock, + WIFI_POWER_EVENT_WAKELOCK_DFS); + } +} + +/** + * hdd_sap_context_destroy() - Destroy SAP context + * + * @hdd_ctx: HDD context. + * + * Destroy SAP context. + * + * Return: None + */ +void hdd_sap_context_destroy(struct hdd_context *hdd_ctx) +{ + if (atomic_read(&hdd_ctx->sap_dfs_ref_cnt)) { + qdf_wake_lock_release(&hdd_ctx->sap_dfs_wakelock, + WIFI_POWER_EVENT_WAKELOCK_DRIVER_EXIT); + + atomic_set(&hdd_ctx->sap_dfs_ref_cnt, 0); + hdd_debug("DFS: Allowing suspend"); + } + + qdf_wake_lock_destroy(&hdd_ctx->sap_dfs_wakelock); + + mutex_destroy(&hdd_ctx->sap_lock); + qdf_wake_lock_destroy(&hdd_ctx->sap_wake_lock); +} + +/** + * __hdd_hostapd_open() - hdd open function for hostapd interface + * This is called in response to ifconfig up + * @dev: pointer to net_device structure + * + * Return - 0 for success non-zero for failure + */ +static int __hdd_hostapd_open(struct net_device *dev) +{ + struct hdd_adapter *adapter = netdev_priv(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + + hdd_enter_dev(dev); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_HOSTAPD_OPEN_REQUEST, + NO_SESSION, 0); + + /* Nothing to be done if device is unloading */ + if (cds_is_driver_unloading()) { + hdd_err("Driver is unloading can not open the hdd"); + return -EBUSY; + } + + if (cds_is_driver_recovering()) { + hdd_err("WLAN is currently recovering; Please try again."); + return -EBUSY; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + /* ensure the physical soc is up */ + ret = hdd_trigger_psoc_idle_restart(hdd_ctx); + if (ret) { + hdd_err("Failed to start WLAN modules return"); + return ret; + } + + ret = hdd_start_adapter(adapter); + if (ret) { + hdd_err("Error Initializing the AP mode: %d", ret); + return ret; + } + + set_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); + + /* Enable all Tx queues */ + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + hdd_exit(); + return 0; +} + +/** + * hdd_hostapd_open() - SSR wrapper for __hdd_hostapd_open + * @dev: pointer to net device + * + * Return: 0 on success, error number otherwise + */ +static int hdd_hostapd_open(struct net_device *net_dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_hostapd_open(net_dev); + + osif_vdev_sync_trans_stop(vdev_sync); + + return errno; +} + +/** + * __hdd_hostapd_stop() - hdd stop function for hostapd interface + * This is called in response to ifconfig down + * + * @dev: pointer to net_device structure + * + * Return - 0 for success non-zero for failure + */ +static int __hdd_hostapd_stop(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + + hdd_enter_dev(dev); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_HOSTAPD_STOP_REQUEST, + NO_SESSION, 0); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) { + set_bit(DOWN_DURING_SSR, &adapter->event_flags); + return ret; + } + + /* + * Some tests requires to do "ifconfig down" only to bring + * down the SAP/GO without killing hostapd/wpa_supplicant. + * In such case, user will do "ifconfig up" to bring-back + * the SAP/GO session. to fulfill this requirement, driver + * needs to de-init the sap session here and re-init when + * __hdd_hostapd_open() API + */ + hdd_stop_adapter(hdd_ctx, adapter); + hdd_deinit_adapter(hdd_ctx, adapter, true); + clear_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); + /* Stop all tx queues */ + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + if (!hdd_is_any_interface_open(hdd_ctx)) + hdd_psoc_idle_timer_start(hdd_ctx); + + hdd_exit(); + return 0; +} + +/** + * hdd_hostapd_stop() - SSR wrapper for__hdd_hostapd_stop + * @dev: pointer to net_device + * + * This is called in response to ifconfig down + * + * Return: 0 on success, error number otherwise + */ +int hdd_hostapd_stop(struct net_device *net_dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_hostapd_stop(net_dev); + + osif_vdev_sync_trans_stop(vdev_sync); + + return errno; +} + +/** + * hdd_hostapd_uninit() - hdd uninit function + * @dev: pointer to net_device structure + * + * This is called during the netdev unregister to uninitialize all data + * associated with the device. + * + * This function must be protected by a transition + * + * Return: None + */ +static void hdd_hostapd_uninit(struct net_device *dev) +{ + struct hdd_adapter *adapter = netdev_priv(dev); + struct hdd_context *hdd_ctx; + + hdd_enter_dev(dev); + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid magic"); + return; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("NULL hdd_ctx"); + return; + } + + hdd_deinit_adapter(hdd_ctx, adapter, true); + + /* after uninit our adapter structure will no longer be valid */ + adapter->dev = NULL; + adapter->magic = 0; + + hdd_exit(); +} + +/** + * __hdd_hostapd_change_mtu() - change mtu + * @dev: pointer to net_device + * @new_mtu: new mtu + * + * Return: 0 on success, error number otherwise + */ +static int __hdd_hostapd_change_mtu(struct net_device *dev, int new_mtu) +{ + hdd_enter_dev(dev); + + return 0; +} + +/** + * hdd_hostapd_change_mtu() - SSR wrapper for __hdd_hostapd_change_mtu + * @net_dev: pointer to net_device + * @new_mtu: new mtu + * + * Return: 0 on success, error number otherwise + */ +static int hdd_hostapd_change_mtu(struct net_device *net_dev, int new_mtu) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_hostapd_change_mtu(net_dev, new_mtu); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef QCA_HT_2040_COEX +QDF_STATUS hdd_set_sap_ht2040_mode(struct hdd_adapter *adapter, + uint8_t channel_type) +{ + QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE; + mac_handle_t mac_handle; + + hdd_debug("change HT20/40 mode"); + + if (QDF_SAP_MODE == adapter->device_mode) { + mac_handle = adapter->hdd_ctx->mac_handle; + if (!mac_handle) { + hdd_err("mac handle is null"); + return QDF_STATUS_E_FAULT; + } + qdf_ret_status = + sme_set_ht2040_mode(mac_handle, adapter->vdev_id, + channel_type, true); + if (qdf_ret_status == QDF_STATUS_E_FAILURE) { + hdd_err("Failed to change HT20/40 mode"); + return QDF_STATUS_E_FAILURE; + } + } + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * __hdd_hostapd_set_mac_address() - + * This function sets the user specified mac address using + * the command ifconfig wlanX hw ether . + * + * @dev: pointer to the net device. + * @addr: pointer to the sockaddr. + * + * Return: 0 for success, non zero for failure + */ +static int __hdd_hostapd_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *psta_mac_addr = addr; + struct hdd_adapter *adapter, *adapter_temp; + struct hdd_context *hdd_ctx; + int ret = 0; + struct qdf_mac_addr mac_addr; + + hdd_enter_dev(dev); + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + qdf_mem_copy(&mac_addr, psta_mac_addr->sa_data, sizeof(mac_addr)); + adapter_temp = hdd_get_adapter_by_macaddr(hdd_ctx, mac_addr.bytes); + if (adapter_temp) { + if (!qdf_str_cmp(adapter_temp->dev->name, dev->name)) + return 0; + hdd_err("%s adapter exist with same address " QDF_MAC_ADDR_FMT, + adapter_temp->dev->name, + QDF_MAC_ADDR_REF(mac_addr.bytes)); + return -EINVAL; + } + + if (qdf_is_macaddr_zero(&mac_addr)) { + hdd_err("MAC is all zero"); + return -EINVAL; + } + + if (qdf_is_macaddr_broadcast(&mac_addr)) { + hdd_err("MAC is Broadcast"); + return -EINVAL; + } + + if (qdf_is_macaddr_group(&mac_addr)) { + hdd_err("MAC is Multicast"); + return -EINVAL; + } + + hdd_debug("Changing MAC to " QDF_MAC_ADDR_FMT " of interface %s ", + QDF_MAC_ADDR_REF(mac_addr.bytes), + dev->name); + hdd_update_dynamic_mac(hdd_ctx, &adapter->mac_addr, &mac_addr); + memcpy(&adapter->mac_addr, psta_mac_addr->sa_data, ETH_ALEN); + memcpy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN); + hdd_exit(); + return 0; +} + +/** + * hdd_hostapd_set_mac_address() - set mac address + * @net_dev: pointer to net_device + * @addr: mac address + * + * Return: 0 on success, error number otherwise + */ +static int hdd_hostapd_set_mac_address(struct net_device *net_dev, void *addr) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_hostapd_set_mac_address(net_dev, addr); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static void hdd_clear_sta(struct hdd_adapter *adapter, + struct hdd_station_info *sta_info) +{ + struct hdd_ap_ctx *ap_ctx; + struct csr_del_sta_params del_sta_params; + + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + + if (qdf_is_macaddr_broadcast(&sta_info->sta_mac)) + return; + + wlansap_populate_del_sta_params(sta_info->sta_mac.bytes, + eSIR_MAC_DEAUTH_LEAVING_BSS_REASON, + SIR_MAC_MGMT_DISASSOC, + &del_sta_params); + + hdd_softap_sta_disassoc(adapter, &del_sta_params); +} + +static void hdd_clear_all_sta(struct hdd_adapter *adapter) +{ + struct hdd_station_info *sta_info, *tmp = NULL; + + hdd_enter_dev(adapter->dev); + + hdd_for_each_sta_ref_safe(adapter->sta_info_list, sta_info, tmp, + STA_INFO_HDD_CLEAR_ALL_STA) { + hdd_clear_sta(adapter, sta_info); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_HDD_CLEAR_ALL_STA); + } +} + +static int hdd_stop_bss_link(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + int errno; + QDF_STATUS status; + + hdd_enter(); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + status = wlansap_stop_bss( + WLAN_HDD_GET_SAP_CTX_PTR(adapter)); + if (QDF_IS_STATUS_SUCCESS(status)) + hdd_debug("Deleting SAP/P2P link!!!!!!"); + + clear_bit(SOFTAP_BSS_STARTED, &adapter->event_flags); + policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, + adapter->device_mode, + adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, + false); + errno = (status == QDF_STATUS_SUCCESS) ? 0 : -EBUSY; + } + hdd_exit(); + return errno; +} + +/** + * hdd_chan_change_notify() - Function to notify hostapd about channel change + * @hostapd_adapter: hostapd adapter + * @dev: Net device structure + * @chan_change: New channel change parameters + * @legacy_phymode: is the phymode legacy + * + * This function is used to notify hostapd about the channel change + * + * Return: Success on intimating userspace + * + */ +QDF_STATUS hdd_chan_change_notify(struct hdd_adapter *adapter, + struct net_device *dev, + struct hdd_chan_change_params chan_change, + bool legacy_phymode) +{ + struct ieee80211_channel *chan; + struct cfg80211_chan_def chandef; + enum nl80211_channel_type channel_type; + uint32_t freq; + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + + if (!mac_handle) { + hdd_err("mac_handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + freq = chan_change.chan_freq; + + chan = ieee80211_get_channel(adapter->wdev.wiphy, freq); + + if (!chan) { + hdd_err("Invalid input frequency %d for channel conversion", + freq); + return QDF_STATUS_E_FAILURE; + } + + if (legacy_phymode) { + channel_type = NL80211_CHAN_NO_HT; + } else { + switch (chan_change.chan_params.sec_ch_offset) { + case PHY_SINGLE_CHANNEL_CENTERED: + channel_type = NL80211_CHAN_HT20; + break; + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + channel_type = NL80211_CHAN_HT40MINUS; + break; + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + channel_type = NL80211_CHAN_HT40PLUS; + break; + default: + channel_type = NL80211_CHAN_NO_HT; + break; + } + } + + cfg80211_chandef_create(&chandef, chan, channel_type); + + /* cfg80211_chandef_create() does update of width and center_freq1 + * only for NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, NL80211_CHAN_HT40PLUS + * and NL80211_CHAN_HT40MINUS. + */ + switch (chan_change.chan_params.ch_width) { + case CH_WIDTH_80MHZ: + chandef.width = NL80211_CHAN_WIDTH_80; + break; + case CH_WIDTH_80P80MHZ: + chandef.width = NL80211_CHAN_WIDTH_80P80; + if (chan_change.chan_params.mhz_freq_seg1) + chandef.center_freq2 = + chan_change.chan_params.mhz_freq_seg1; + break; + case CH_WIDTH_160MHZ: + chandef.width = NL80211_CHAN_WIDTH_160; + break; + default: + break; + } + + if ((chan_change.chan_params.ch_width == CH_WIDTH_80MHZ) || + (chan_change.chan_params.ch_width == CH_WIDTH_80P80MHZ) || + (chan_change.chan_params.ch_width == CH_WIDTH_160MHZ)) { + if (chan_change.chan_params.mhz_freq_seg0) + chandef.center_freq1 = + chan_change.chan_params.mhz_freq_seg0; + } + + hdd_debug("notify: chan:%d width:%d freq1:%d freq2:%d", + chandef.chan->center_freq, chandef.width, chandef.center_freq1, + chandef.center_freq2); + + cfg80211_ch_switch_notify(dev, &chandef); + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_send_radar_event() - Function to send radar events to user space + * @hdd_context: HDD context + * @event: Type of radar event + * @dfs_info: Structure containing DFS channel and country + * @wdev: Wireless device structure + * + * This function is used to send radar events such as CAC start, CAC + * end etc., to userspace + * + * Return: Success on sending notifying userspace + * + */ +static QDF_STATUS hdd_send_radar_event(struct hdd_context *hdd_context, + eSapHddEvent event, + struct wlan_dfs_info dfs_info, + struct wireless_dev *wdev) +{ + + struct sk_buff *vendor_event; + enum qca_nl80211_vendor_subcmds_index index; + uint32_t freq, ret; + uint32_t data_size; + + if (!hdd_context) { + hdd_err("HDD context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + freq = cds_chan_to_freq(dfs_info.channel); + + switch (event) { + case eSAP_DFS_CAC_START: + index = + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED_INDEX; + data_size = sizeof(uint32_t); + break; + case eSAP_DFS_CAC_END: + index = + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED_INDEX; + data_size = sizeof(uint32_t); + break; + case eSAP_DFS_RADAR_DETECT: + index = + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED_INDEX; + data_size = sizeof(uint32_t); + break; + default: + return QDF_STATUS_E_FAILURE; + } + + vendor_event = cfg80211_vendor_event_alloc(hdd_context->wiphy, + wdev, + data_size + NLMSG_HDRLEN, + index, + GFP_KERNEL); + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed for %d", index); + return QDF_STATUS_E_FAILURE; + } + + ret = nla_put_u32(vendor_event, NL80211_ATTR_WIPHY_FREQ, freq); + + if (ret) { + hdd_err("NL80211_ATTR_WIPHY_FREQ put fail"); + kfree_skb(vendor_event); + return QDF_STATUS_E_FAILURE; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_send_conditional_chan_switch_status() - Send conditional channel switch + * status + * @hdd_ctx: HDD context + * @wdev: Wireless device structure + * @status: Status of conditional channel switch + * (0: Success, Non-zero: Failure) + * + * Sends the status of conditional channel switch to user space. This is named + * conditional channel switch because the SAP will move to the provided channel + * after some condition (pre-cac) is met. + * + * Return: None + */ +static void hdd_send_conditional_chan_switch_status(struct hdd_context *hdd_ctx, + struct wireless_dev *wdev, + bool status) +{ + struct sk_buff *event; + + hdd_enter_dev(wdev->netdev); + + if (!hdd_ctx) { + hdd_err("Invalid HDD context pointer"); + return; + } + + event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + wdev, sizeof(uint32_t) + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX, + GFP_KERNEL); + if (!event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(event, + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS, + status)) { + hdd_err("nla put failed"); + kfree_skb(event); + return; + } + + cfg80211_vendor_event(event, GFP_KERNEL); +} + +/** + * wlan_hdd_set_pre_cac_complete_status() - Set pre cac complete status + * @ap_adapter: AP adapter + * @status: Status which can be true or false + * + * Sets the status of pre cac i.e., whether it is complete or not + * + * Return: Zero on success, non-zero on failure + */ +static int wlan_hdd_set_pre_cac_complete_status(struct hdd_adapter *ap_adapter, + bool status) +{ + QDF_STATUS ret; + + ret = wlan_sap_set_pre_cac_complete_status( + WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), status); + if (QDF_IS_STATUS_ERROR(ret)) + return -EINVAL; + + return 0; +} + +/** + * __wlan_hdd_sap_pre_cac_failure() - Process the pre cac failure + * @adapter: AP adapter + * + * Deletes the pre cac adapter + * + * Return: None + */ +static void __wlan_hdd_sap_pre_cac_failure(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + + hdd_enter(); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (wlan_hdd_validate_context(hdd_ctx)) { + hdd_err("HDD context is null"); + return; + } + + wlan_hdd_release_intf_addr(hdd_ctx, adapter->mac_addr.bytes); + hdd_stop_adapter(hdd_ctx, adapter); + hdd_close_adapter(hdd_ctx, adapter, false); + + hdd_exit(); +} + +/** + * wlan_hdd_sap_pre_cac_failure() - Process the pre cac failure + * @data: AP adapter + * + * Deletes the pre cac adapter + * + * Return: None + */ +void wlan_hdd_sap_pre_cac_failure(void *data) +{ + struct hdd_adapter *adapter = data; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_trans_start_wait(adapter->dev, &vdev_sync); + if (errno) + return; + + osif_vdev_sync_unregister(adapter->dev); + osif_vdev_sync_wait_for_ops(vdev_sync); + + __wlan_hdd_sap_pre_cac_failure(data); + + osif_vdev_sync_trans_stop(vdev_sync); + osif_vdev_sync_destroy(vdev_sync); +} + +/** + * __wlan_hdd_sap_pre_cac_success() - Process the pre cac result + * @adapter: AP adapter + * + * Deletes the pre cac adapter and moves the existing SAP to the pre cac + * channel + * + * Return: None + */ +static void __wlan_hdd_sap_pre_cac_success(struct hdd_adapter *adapter) +{ + struct hdd_adapter *ap_adapter; + int i; + struct hdd_context *hdd_ctx; + + hdd_enter(); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return; + } + + wlan_hdd_release_intf_addr(hdd_ctx, adapter->mac_addr.bytes); + hdd_stop_adapter(hdd_ctx, adapter); + hdd_close_adapter(hdd_ctx, adapter, false); + + /* Prepare to switch AP from 2.4GHz channel to the pre CAC channel */ + ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); + if (!ap_adapter) { + hdd_err("failed to get SAP adapter, no restart on pre CAC channel"); + return; + } + + /* + * Setting of the pre cac complete status will ensure that on channel + * switch to the pre CAC DFS channel, there is no CAC again. + */ + wlan_hdd_set_pre_cac_complete_status(ap_adapter, true); + + wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, ap_adapter->vdev_id, + CSA_REASON_PRE_CAC_SUCCESS); + i = hdd_softap_set_channel_change(ap_adapter->dev, + ap_adapter->pre_cac_freq, + CH_WIDTH_MAX, false); + if (0 != i) { + hdd_err("failed to change channel"); + wlan_hdd_set_pre_cac_complete_status(ap_adapter, false); + } + + hdd_exit(); +} + +static void wlan_hdd_sap_pre_cac_success(void *data) +{ + struct hdd_adapter *adapter = data; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_trans_start_wait(adapter->dev, &vdev_sync); + if (errno) + return; + + osif_vdev_sync_unregister(adapter->dev); + osif_vdev_sync_wait_for_ops(vdev_sync); + + __wlan_hdd_sap_pre_cac_success(adapter); + + osif_vdev_sync_trans_stop(vdev_sync); + osif_vdev_sync_destroy(vdev_sync); +} + +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE +/** + * hdd_handle_acs_scan_event() - handle acs scan event for SAP + * @sap_event: tpSap_Event + * @adapter: struct hdd_adapter for SAP + * + * The function is to handle the eSAP_ACS_SCAN_SUCCESS_EVENT event. + * It will update scan result to cfg80211 and start a timer to flush the + * cached acs scan result. + * + * Return: QDF_STATUS_SUCCESS on success, + * other value on failure + */ +static QDF_STATUS hdd_handle_acs_scan_event(struct sap_event *sap_event, + struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + struct sap_acs_scan_complete_event *comp_evt; + QDF_STATUS qdf_status; + int freq_list_size; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return QDF_STATUS_E_FAILURE; + } + comp_evt = &sap_event->sapevt.sap_acs_scan_comp; + hdd_ctx->skip_acs_scan_status = eSAP_SKIP_ACS_SCAN; + qdf_spin_lock(&hdd_ctx->acs_skip_lock); + qdf_mem_free(hdd_ctx->last_acs_freq_list); + hdd_ctx->last_acs_freq_list = NULL; + hdd_ctx->num_of_channels = 0; + /* cache the previous ACS scan channel list . + * If the following OBSS scan chan list is covered by ACS chan list, + * we can skip OBSS Scan to save SAP starting total time. + */ + if (comp_evt->num_of_channels && comp_evt->freq_list) { + freq_list_size = comp_evt->num_of_channels * + sizeof(comp_evt->freq_list[0]); + hdd_ctx->last_acs_freq_list = qdf_mem_malloc( + freq_list_size); + if (hdd_ctx->last_acs_freq_list) { + qdf_mem_copy(hdd_ctx->last_acs_freq_list, + comp_evt->freq_list, + freq_list_size); + hdd_ctx->num_of_channels = comp_evt->num_of_channels; + } + } + qdf_spin_unlock(&hdd_ctx->acs_skip_lock); + + hdd_debug("Reusing Last ACS scan result for %d sec", + ACS_SCAN_EXPIRY_TIMEOUT_S); + qdf_mc_timer_stop(&hdd_ctx->skip_acs_scan_timer); + qdf_status = qdf_mc_timer_start(&hdd_ctx->skip_acs_scan_timer, + ACS_SCAN_EXPIRY_TIMEOUT_S * 1000); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("Failed to start ACS scan expiry timer"); + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS hdd_handle_acs_scan_event(struct sap_event *sap_event, + struct hdd_adapter *adapter) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * get_max_rate_vht() - calculate max rate for VHT mode + * @nss: num of streams + * @ch_width: channel width + * @sgi: short gi + * @vht_mcs_map: vht mcs map + * + * This function calculate max rate for VHT mode + * + * Return: max rate + */ +static int get_max_rate_vht(int nss, int ch_width, int sgi, int vht_mcs_map) +{ + const struct index_vht_data_rate_type *supported_vht_mcs_rate; + enum data_rate_11ac_max_mcs vht_max_mcs; + int maxrate = 0; + int maxidx; + + if (nss == 1) { + supported_vht_mcs_rate = supported_vht_mcs_rate_nss1; + } else if (nss == 2) { + supported_vht_mcs_rate = supported_vht_mcs_rate_nss2; + } else { + /* Not Supported */ + hdd_debug("nss %d not supported", nss); + return maxrate; + } + + vht_max_mcs = + (enum data_rate_11ac_max_mcs) + (vht_mcs_map & DATA_RATE_11AC_MCS_MASK); + + if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_7) { + maxidx = 7; + } else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_8) { + maxidx = 8; + } else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_9) { + if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) + /* MCS9 is not valid for VHT20 when nss=1,2 */ + maxidx = 8; + else + maxidx = 9; + } else { + hdd_err("vht mcs map %x not supported", + vht_mcs_map & DATA_RATE_11AC_MCS_MASK); + return maxrate; + } + + if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) { + maxrate = + supported_vht_mcs_rate[maxidx].supported_VHT20_rate[sgi]; + } else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) { + maxrate = + supported_vht_mcs_rate[maxidx].supported_VHT40_rate[sgi]; + } else if (ch_width == eHT_CHANNEL_WIDTH_80MHZ) { + maxrate = + supported_vht_mcs_rate[maxidx].supported_VHT80_rate[sgi]; + } else { + hdd_err("ch_width %d not supported", ch_width); + return maxrate; + } + + return maxrate; +} + +/** + * calculate_max_phy_rate() - calcuate maximum phy rate (100kbps) + * @mode: phymode: Legacy, 11a/b/g, HT, VHT + * @nss: num of stream (maximum num is 2) + * @ch_width: channel width + * @sgi: short gi enabled or not + * @supp_idx: max supported idx + * @ext_idx: max extended idx + * @ht_mcs_idx: max mcs index for HT + * @vht_mcs_map: mcs map for VHT + * + * return: maximum phy rate in 100kbps + */ +static int calcuate_max_phy_rate(int mode, int nss, int ch_width, + int sgi, int supp_idx, int ext_idx, + int ht_mcs_idx, int vht_mcs_map) +{ + const struct index_data_rate_type *supported_mcs_rate; + int maxidx = 12; /*default 6M mode*/ + int maxrate = 0, tmprate; + int i; + + /* check supported rates */ + if (supp_idx != 0xff && maxidx < supp_idx) + maxidx = supp_idx; + + /* check extended rates */ + if (ext_idx != 0xff && maxidx < ext_idx) + maxidx = ext_idx; + + for (i = 0; i < QDF_ARRAY_SIZE(supported_data_rate); i++) { + if (supported_data_rate[i].beacon_rate_index == maxidx) + maxrate = supported_data_rate[i].supported_rate[0]; + } + + if (mode == SIR_SME_PHY_MODE_HT) { + /* check for HT Mode */ + maxidx = ht_mcs_idx; + if (maxidx > 7) { + hdd_err("ht_mcs_idx %d is incorrect", ht_mcs_idx); + return maxrate; + } + if (nss == 1) { + supported_mcs_rate = supported_mcs_rate_nss1; + } else if (nss == 2) { + supported_mcs_rate = supported_mcs_rate_nss2; + } else { + /* Not Supported */ + hdd_err("nss %d not supported", nss); + return maxrate; + } + + if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) { + tmprate = sgi ? + supported_mcs_rate[maxidx].supported_rate[2] : + supported_mcs_rate[maxidx].supported_rate[0]; + } else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) { + tmprate = sgi ? + supported_mcs_rate[maxidx].supported_rate[3] : + supported_mcs_rate[maxidx].supported_rate[1]; + } else { + hdd_err("invalid mode %d ch_width %d", + mode, ch_width); + return maxrate; + } + + if (maxrate < tmprate) + maxrate = tmprate; + } + + if (mode == SIR_SME_PHY_MODE_VHT) { + /* check for VHT Mode */ + tmprate = get_max_rate_vht(nss, ch_width, sgi, vht_mcs_map); + if (maxrate < tmprate) + maxrate = tmprate; + } + + return maxrate; +} + +/** + * hdd_convert_dot11mode_from_phymode() - get dot11 mode from phymode + * @phymode: phymode of sta associated to SAP + * + * The function is to convert the phymode to corresponding dot11 mode + * + * Return: dot11mode. + */ + + +static int hdd_convert_dot11mode_from_phymode(int phymode) +{ + + switch (phymode) { + + case MODE_11A: + return QCA_WLAN_802_11_MODE_11A; + + case MODE_11B: + return QCA_WLAN_802_11_MODE_11B; + + case MODE_11G: + case MODE_11GONLY: + return QCA_WLAN_802_11_MODE_11G; + + case MODE_11NA_HT20: + case MODE_11NG_HT20: + case MODE_11NA_HT40: + case MODE_11NG_HT40: + return QCA_WLAN_802_11_MODE_11N; + + case MODE_11AC_VHT20: + case MODE_11AC_VHT40: + case MODE_11AC_VHT80: + case MODE_11AC_VHT20_2G: + case MODE_11AC_VHT40_2G: + case MODE_11AC_VHT80_2G: +#ifdef CONFIG_160MHZ_SUPPORT + case MODE_11AC_VHT80_80: + case MODE_11AC_VHT160: +#endif + return QCA_WLAN_802_11_MODE_11AC; + + default: + return QCA_WLAN_802_11_MODE_INVALID; + } + +} + +/** + * hdd_fill_station_info() - fill stainfo once connected + * @stainfo: peer stainfo associate to SAP + * @event: associate/reassociate event received + * + * The function is to update rate stats to stainfo + * + * Return: None. + */ +static void hdd_fill_station_info(struct hdd_adapter *adapter, + tSap_StationAssocReassocCompleteEvent *event) +{ + struct hdd_station_info *stainfo, *cache_sta_info; + struct hdd_station_info *oldest_disassoc_sta_info = NULL; + qdf_time_t oldest_disassoc_sta_ts = 0; + bool is_dot11_mode_abgn; + + stainfo = hdd_get_sta_info_by_mac(&adapter->sta_info_list, + event->staMac.bytes, + STA_INFO_FILL_STATION_INFO); + + if (!stainfo) { + hdd_err("invalid stainfo"); + return; + } + + qdf_mem_copy(&stainfo->capability, &event->capability_info, + sizeof(uint16_t)); + stainfo->freq = event->chan_info.mhz; + stainfo->sta_type = event->staType; + stainfo->dot11_mode = + hdd_convert_dot11mode_from_phymode(event->chan_info.info); + + stainfo->nss = event->chan_info.nss; + stainfo->rate_flags = event->chan_info.rate_flags; + stainfo->ampdu = event->ampdu; + stainfo->sgi_enable = event->sgi_enable; + stainfo->tx_stbc = event->tx_stbc; + stainfo->rx_stbc = event->rx_stbc; + stainfo->ch_width = event->ch_width; + stainfo->mode = event->mode; + stainfo->max_supp_idx = event->max_supp_idx; + stainfo->max_ext_idx = event->max_ext_idx; + stainfo->max_mcs_idx = event->max_mcs_idx; + stainfo->rx_mcs_map = event->rx_mcs_map; + stainfo->tx_mcs_map = event->tx_mcs_map; + stainfo->assoc_ts = qdf_system_ticks(); + stainfo->max_phy_rate = + calcuate_max_phy_rate(stainfo->mode, + stainfo->nss, + stainfo->ch_width, + stainfo->sgi_enable, + stainfo->max_supp_idx, + stainfo->max_ext_idx, + stainfo->max_mcs_idx, + stainfo->rx_mcs_map); + /* expect max_phy_rate report in kbps */ + stainfo->max_phy_rate *= 100; + + /* + * Connected Peer always supports atleast one of the + * 802.11 mode out of 11bg/11abg/11abgn, hence this field + * should always be true. + */ + is_dot11_mode_abgn = true; + stainfo->ecsa_capable = event->ecsa_capable; + + if (event->vht_caps.present) { + stainfo->vht_present = true; + hdd_copy_vht_caps(&stainfo->vht_caps, &event->vht_caps); + stainfo->support_mode |= + (stainfo->vht_present << HDD_80211_MODE_AC); + } + if (event->ht_caps.present) { + stainfo->ht_present = true; + hdd_copy_ht_caps(&stainfo->ht_caps, &event->ht_caps); + } + + stainfo->support_mode |= + (event->he_caps_present << HDD_80211_MODE_AX); + + if (event->he_caps_present && !(event->vht_caps.present || + event->ht_caps.present)) + is_dot11_mode_abgn = false; + + stainfo->support_mode |= is_dot11_mode_abgn << HDD_80211_MODE_ABGN; + /* Initialize DHCP info */ + stainfo->dhcp_phase = DHCP_PHASE_ACK; + stainfo->dhcp_nego_status = DHCP_NEGO_STOP; + + cache_sta_info = + hdd_get_sta_info_by_mac(&adapter->cache_sta_info_list, + event->staMac.bytes, + STA_INFO_FILL_STATION_INFO); + + if (!cache_sta_info) { + cache_sta_info = qdf_mem_malloc(sizeof(*cache_sta_info)); + if (!cache_sta_info) + goto exit; + + qdf_mem_copy(cache_sta_info, stainfo, sizeof(*cache_sta_info)); + cache_sta_info->is_attached = 0; + cache_sta_info->assoc_req_ies.data = + qdf_mem_malloc(event->ies_len); + if (cache_sta_info->assoc_req_ies.data) { + qdf_mem_copy(cache_sta_info->assoc_req_ies.data, + event->ies, event->ies_len); + cache_sta_info->assoc_req_ies.len = event->ies_len; + } + qdf_atomic_init(&cache_sta_info->ref_cnt); + + /* + * If cache_sta_info is not present and cache limit is not + * reached, then create and attach. Else find the cache that is + * the oldest and replace that with the new cache. + */ + if (qdf_atomic_read(&adapter->cache_sta_count) < + WLAN_MAX_STA_COUNT) { + hdd_sta_info_attach(&adapter->cache_sta_info_list, + cache_sta_info); + qdf_atomic_inc(&adapter->cache_sta_count); + } else { + struct hdd_station_info *temp_sta_info, *tmp = NULL; + struct hdd_sta_info_obj *sta_list = + &adapter->cache_sta_info_list; + + hdd_debug("reached max caching, removing oldest"); + + /* Find the oldest cached station */ + hdd_for_each_sta_ref_safe(adapter->cache_sta_info_list, + temp_sta_info, tmp, + STA_INFO_FILL_STATION_INFO) { + if (temp_sta_info->disassoc_ts && + (!oldest_disassoc_sta_ts || + qdf_system_time_after( + oldest_disassoc_sta_ts, + temp_sta_info->disassoc_ts))) { + oldest_disassoc_sta_ts = + temp_sta_info->disassoc_ts; + oldest_disassoc_sta_info = + temp_sta_info; + } + hdd_put_sta_info_ref( + sta_list, &temp_sta_info, + true, + STA_INFO_FILL_STATION_INFO); + } + + /* Remove the oldest and store the current */ + hdd_sta_info_detach(&adapter->cache_sta_info_list, + &oldest_disassoc_sta_info); + hdd_sta_info_attach(&adapter->cache_sta_info_list, + cache_sta_info); + } + } else { + hdd_put_sta_info_ref(&adapter->cache_sta_info_list, + &cache_sta_info, true, + STA_INFO_FILL_STATION_INFO); + } + + hdd_debug("cap %d %d %d %d %d %d %d %d %d %x %d", + stainfo->ampdu, + stainfo->sgi_enable, + stainfo->tx_stbc, + stainfo->rx_stbc, + stainfo->is_qos_enabled, + stainfo->ch_width, + stainfo->mode, + event->wmmEnabled, + event->chan_info.nss, + event->chan_info.rate_flags, + stainfo->max_phy_rate); + hdd_debug("rate info %d %d %d %d %d", + stainfo->max_supp_idx, + stainfo->max_ext_idx, + stainfo->max_mcs_idx, + stainfo->rx_mcs_map, + stainfo->tx_mcs_map); +exit: + hdd_put_sta_info_ref(&adapter->sta_info_list, &stainfo, true, + STA_INFO_FILL_STATION_INFO); + return; +} + +void hdd_stop_sap_due_to_invalid_channel(struct work_struct *work) +{ + struct hdd_adapter *sap_adapter = container_of(work, struct hdd_adapter, + sap_stop_bss_work); + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(sap_adapter->dev, &vdev_sync)) + return; + + hdd_debug("work started for sap session[%d]", sap_adapter->vdev_id); + wlan_hdd_stop_sap(sap_adapter); + wlansap_cleanup_cac_timer(WLAN_HDD_GET_SAP_CTX_PTR(sap_adapter)); + hdd_debug("work finished for sap"); + + osif_vdev_sync_op_stop(vdev_sync); +} + +/** + * hdd_hostapd_apply_action_oui() - Check for action_ouis to be applied on peers + * @hdd_ctx: pointer to hdd context + * @adapter: pointer to adapter + * @event: assoc complete params + * + * This function is used to check whether aggressive tx should be disabled + * based on the soft-ap configuration and action_oui ini + * gActionOUIDisableAggressiveTX + * + * Return: None + */ +static void +hdd_hostapd_apply_action_oui(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + tSap_StationAssocReassocCompleteEvent *event) +{ + bool found; + uint32_t freq; + tSirMacHTChannelWidth ch_width; + enum sir_sme_phy_mode mode; + struct action_oui_search_attr attr = {0}; + QDF_STATUS status; + + ch_width = event->ch_width; + if (ch_width != eHT_CHANNEL_WIDTH_20MHZ) + return; + + freq = event->chan_info.mhz; + if (WLAN_REG_IS_24GHZ_CH_FREQ(freq)) + attr.enable_2g = true; + else if (WLAN_REG_IS_5GHZ_CH_FREQ(freq)) + attr.enable_5g = true; + else + return; + + mode = event->mode; + if (event->vht_caps.present && mode == SIR_SME_PHY_MODE_VHT) + attr.vht_cap = true; + else if (event->ht_caps.present && mode == SIR_SME_PHY_MODE_HT) + attr.ht_cap = true; + + attr.mac_addr = (uint8_t *)(&event->staMac); + + found = ucfg_action_oui_search(hdd_ctx->psoc, + &attr, + ACTION_OUI_DISABLE_AGGRESSIVE_TX); + if (!found) + return; + + status = sme_set_peer_param(attr.mac_addr, + WMI_PEER_PARAM_DISABLE_AGGRESSIVE_TX, + true, adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to disable aggregation for peer"); +} + +static void hdd_hostapd_set_sap_key(struct hdd_adapter *adapter) +{ + struct wlan_crypto_key *crypto_key; + + crypto_key = wlan_crypto_get_key(adapter->vdev, 0); + if (!crypto_key) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + "Crypto KEY is NULL"); + return; + } + ucfg_crypto_set_key_req(adapter->vdev, crypto_key, + WLAN_CRYPTO_KEY_TYPE_UNICAST); + wma_update_set_key(adapter->vdev_id, true, 1, crypto_key->cipher_type); + ucfg_crypto_set_key_req(adapter->vdev, crypto_key, + WLAN_CRYPTO_KEY_TYPE_GROUP); + + wma_update_set_key(adapter->vdev_id, true, 0, crypto_key->cipher_type); +} + +/** + * hdd_hostapd_chan_change() - prepare new operation chan info to kernel + * @adapter: pointre to hdd_adapter + * @sap_event: pointer to sap_event + * + * Return: QDF_STATUS + */ +static QDF_STATUS hdd_hostapd_chan_change(struct hdd_adapter *adapter, + struct sap_event *sap_event) +{ + struct hdd_chan_change_params chan_change; + struct ch_params sap_ch_param = {0}; + eCsrPhyMode phy_mode; + bool legacy_phymode; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct sap_ch_selected_s *sap_chan_selected = + &sap_event->sapevt.sap_ch_selected; + + sap_ch_param.ch_width = sap_chan_selected->ch_width; + sap_ch_param.mhz_freq_seg0 = + sap_chan_selected->vht_seg0_center_ch_freq; + sap_ch_param.mhz_freq_seg1 = + sap_chan_selected->vht_seg1_center_ch_freq; + + wlan_reg_set_channel_params_for_freq( + hdd_ctx->pdev, + sap_chan_selected->pri_ch_freq, + sap_chan_selected->ht_sec_ch_freq, + &sap_ch_param); + + phy_mode = wlan_sap_get_phymode( + WLAN_HDD_GET_SAP_CTX_PTR(adapter)); + + switch (phy_mode) { + case eCSR_DOT11_MODE_11n: + case eCSR_DOT11_MODE_11n_ONLY: + case eCSR_DOT11_MODE_11ac: + case eCSR_DOT11_MODE_11ac_ONLY: + case eCSR_DOT11_MODE_11ax: + case eCSR_DOT11_MODE_11ax_ONLY: + legacy_phymode = false; + break; + default: + legacy_phymode = true; + break; + } + + chan_change.chan_freq = sap_chan_selected->pri_ch_freq; + chan_change.chan_params.ch_width = sap_chan_selected->ch_width; + chan_change.chan_params.sec_ch_offset = + sap_ch_param.sec_ch_offset; + chan_change.chan_params.mhz_freq_seg0 = + sap_chan_selected->vht_seg0_center_ch_freq; + chan_change.chan_params.mhz_freq_seg1 = + sap_chan_selected->vht_seg1_center_ch_freq; + + return hdd_chan_change_notify(adapter, adapter->dev, + chan_change, legacy_phymode); +} + +QDF_STATUS hdd_hostapd_sap_event_cb(struct sap_event *sap_event, + void *context) +{ + struct hdd_adapter *adapter; + struct hdd_ap_ctx *ap_ctx; + struct hdd_hostapd_state *hostapd_state; + struct net_device *dev; + eSapHddEvent event_id; + union iwreq_data wrqu; + uint8_t *we_custom_event_generic = NULL; + int we_event = 0; + int i = 0; + uint8_t sta_id; + QDF_STATUS qdf_status; + bool bAuthRequired = true; + char *unknownSTAEvent = NULL; + char *maxAssocExceededEvent = NULL; + uint8_t *we_custom_start_event = NULL; + char *startBssEvent; + struct hdd_context *hdd_ctx; + struct iw_michaelmicfailure msg; + uint8_t ignoreCAC = 0; + struct hdd_config *cfg = NULL; + struct wlan_dfs_info dfs_info; + uint8_t cc_len = WLAN_SVC_COUNTRY_CODE_LEN; + struct hdd_adapter *con_sap_adapter; + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSap_StationAssocReassocCompleteEvent *event; + tSap_StationSetKeyCompleteEvent *key_complete; + int ret = 0; + tSap_StationDisassocCompleteEvent *disassoc_comp; + struct hdd_station_info *stainfo, *cache_stainfo, *tmp = NULL; + mac_handle_t mac_handle; + struct sap_config *sap_config; + struct sap_context *sap_ctx = NULL; + + dev = context; + if (!dev) { + hdd_err("context is null"); + return QDF_STATUS_E_FAILURE; + } + + adapter = netdev_priv(dev); + + if ((!adapter) || + (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("invalid adapter or adapter has invalid magic"); + return QDF_STATUS_E_FAILURE; + } + + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + + if (!sap_event) { + hdd_err("sap_event is null"); + return QDF_STATUS_E_FAILURE; + } + + event_id = sap_event->sapHddEventCode; + memset(&wrqu, '\0', sizeof(wrqu)); + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return QDF_STATUS_E_FAILURE; + } + + cfg = hdd_ctx->config; + + if (!cfg) { + hdd_err("HDD config is null"); + return QDF_STATUS_E_FAILURE; + } + + mac_handle = hdd_ctx->mac_handle; + dfs_info.channel = wlan_reg_freq_to_chan( + hdd_ctx->pdev, ap_ctx->operating_chan_freq); + sme_get_country_code(mac_handle, dfs_info.country_code, &cc_len); + sta_id = sap_event->sapevt.sapStartBssCompleteEvent.staId; + sap_config = &adapter->session.ap.sap_config; + + switch (event_id) { + case eSAP_START_BSS_EVENT: + hdd_debug("BSS status = %s, channel = %u, bc sta Id = %d", + sap_event->sapevt.sapStartBssCompleteEvent. + status ? "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS", + sap_event->sapevt.sapStartBssCompleteEvent. + operating_chan_freq, + sap_event->sapevt.sapStartBssCompleteEvent.staId); + ap_ctx->operating_chan_freq = + sap_event->sapevt.sapStartBssCompleteEvent + .operating_chan_freq; + + adapter->vdev_id = + sap_event->sapevt.sapStartBssCompleteEvent.sessionId; + sap_config->chan_freq = + sap_event->sapevt.sapStartBssCompleteEvent. + operating_chan_freq; + sap_config->ch_params.ch_width = + sap_event->sapevt.sapStartBssCompleteEvent.ch_width; + + hdd_nofl_info("AP started vid %d freq %d BW %d", + adapter->vdev_id, + ap_ctx->operating_chan_freq, + sap_config->ch_params.ch_width); + + sap_config->ch_params = ap_ctx->sap_context->ch_params; + sap_config->sec_ch_freq = ap_ctx->sap_context->sec_ch_freq; + + hostapd_state->qdf_status = + sap_event->sapevt.sapStartBssCompleteEvent.status; + + qdf_atomic_set(&adapter->ch_switch_in_progress, 0); + wlansap_get_dfs_ignore_cac(mac_handle, &ignoreCAC); + if (!policy_mgr_get_dfs_master_dynamic_enabled( + hdd_ctx->psoc, adapter->vdev_id)) + ignoreCAC = true; + + /* DFS requirement: DO NOT transmit during CAC. */ + if (CHANNEL_STATE_DFS != + wlan_reg_get_channel_state_for_freq( + hdd_ctx->pdev, ap_ctx->operating_chan_freq) + || ignoreCAC + || hdd_ctx->dev_dfs_cac_status == DFS_CAC_ALREADY_DONE) + ap_ctx->dfs_cac_block_tx = false; + else + ap_ctx->dfs_cac_block_tx = true; + + ucfg_ipa_set_dfs_cac_tx(hdd_ctx->pdev, + ap_ctx->dfs_cac_block_tx); + + hdd_debug("The value of dfs_cac_block_tx[%d] for ApCtx[%pK]:%d", + ap_ctx->dfs_cac_block_tx, ap_ctx, + adapter->vdev_id); + + if (hostapd_state->qdf_status) { + hdd_err("startbss event failed!!"); + /* + * Make sure to set the event before proceeding + * for error handling otherwise caller thread will + * wait till 10 secs and no other connection will + * go through before that. + */ + hostapd_state->bss_state = BSS_STOP; + qdf_event_set(&hostapd_state->qdf_event); + goto stopbss; + } else { + sme_ch_avoid_update_req(mac_handle); + + ap_ctx->broadcast_sta_id = + sap_event->sapevt.sapStartBssCompleteEvent.staId; + + cdp_hl_fc_set_td_limit( + cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id, + ap_ctx->operating_chan_freq); + + hdd_register_tx_flow_control(adapter, + hdd_softap_tx_resume_timer_expired_handler, + hdd_softap_tx_resume_cb, + hdd_tx_flow_control_is_pause); + + hdd_register_hl_netdev_fc_timer( + adapter, + hdd_tx_resume_timer_expired_handler); + + /* @@@ need wep logic here to set privacy bit */ + qdf_status = + hdd_softap_register_bc_sta(adapter, + ap_ctx->privacy); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_warn("Failed to register BC STA %d", + qdf_status); + hdd_stop_bss_link(adapter); + } + } + + if (ucfg_ipa_is_enabled()) { + status = ucfg_ipa_wlan_evt(hdd_ctx->pdev, + adapter->dev, + adapter->device_mode, + adapter->vdev_id, + WLAN_IPA_AP_CONNECT, + adapter->dev->dev_addr); + if (status) + hdd_err("WLAN_AP_CONNECT event failed"); + } + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + wlan_hdd_auto_shutdown_enable(hdd_ctx, true); +#endif + hdd_hostapd_channel_prevent_suspend(adapter, + ap_ctx->operating_chan_freq); + + hostapd_state->bss_state = BSS_START; + hdd_start_tsf_sync(adapter); + + /* Set default key index */ + hdd_debug("default key index %hu", ap_ctx->wep_def_key_idx); + + sme_roam_set_default_key_index(mac_handle, + adapter->vdev_id, + ap_ctx->wep_def_key_idx); + + hdd_hostapd_set_sap_key(adapter); + + /* Fill the params for sending IWEVCUSTOM Event + * with SOFTAP.enabled + */ + we_custom_start_event = + qdf_mem_malloc(HDD_MAX_CUSTOM_START_EVENT_SIZE); + if (!we_custom_start_event) + goto stopbss; + + startBssEvent = "SOFTAP.enabled"; + memset(we_custom_start_event, '\0', + sizeof(HDD_MAX_CUSTOM_START_EVENT_SIZE)); + memcpy(we_custom_start_event, startBssEvent, + strlen(startBssEvent)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(startBssEvent); + we_event = IWEVCUSTOM; + we_custom_event_generic = we_custom_start_event; + hdd_ipa_set_tx_flow_info(); + sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); + if (!sap_ctx) { + hdd_err("sap ctx is null"); + return QDF_STATUS_E_FAILURE; + } + + if (sap_ctx->is_chan_change_inprogress) { + hdd_debug("check for possible hw mode change"); + status = policy_mgr_set_hw_mode_on_channel_switch( + hdd_ctx->psoc, adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("set hw mode change not done"); + } + hdd_debug("check for SAP restart"); + policy_mgr_check_concurrent_intf_and_restart_sap( + hdd_ctx->psoc); + /* + * set this event at the very end because once this events + * get set, caller thread is waiting to do further processing. + * so once this event gets set, current worker thread might get + * pre-empted by caller thread. + */ + qdf_status = qdf_event_set(&hostapd_state->qdf_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("qdf_event_set failed! status: %d", qdf_status); + goto stopbss; + } + break; /* Event will be sent after Switch-Case stmt */ + + case eSAP_STOP_BSS_EVENT: + hdd_debug("BSS stop status = %s", + sap_event->sapevt.sapStopBssCompleteEvent. + status ? "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS"); + + hdd_hostapd_channel_allow_suspend(adapter, + ap_ctx->operating_chan_freq); + + /* Invalidate the channel info. */ + ap_ctx->operating_chan_freq = 0; + + /* reset the dfs_cac_status and dfs_cac_block_tx flag only when + * the last BSS is stopped + */ + con_sap_adapter = hdd_get_con_sap_adapter(adapter, true); + if (!con_sap_adapter) { + ap_ctx->dfs_cac_block_tx = true; + hdd_ctx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; + } + hdd_nofl_info("Ap stopped vid %d reason=%d", adapter->vdev_id, + ap_ctx->bss_stop_reason); + if ((BSS_STOP_DUE_TO_MCC_SCC_SWITCH != + ap_ctx->bss_stop_reason) && + (BSS_STOP_DUE_TO_VENDOR_CONFIG_CHAN != + ap_ctx->bss_stop_reason)) { + /* + * when MCC to SCC switching or vendor subcmd + * setting sap config channel happens, key storage + * should not be cleared due to hostapd will not + * repopulate the original keys + */ + ap_ctx->group_key.keyLength = 0; + for (i = 0; i < CSR_MAX_NUM_KEY; i++) + ap_ctx->wep_key[i].keyLength = 0; + } + + /* clear the reason code in case BSS is stopped + * in another place + */ + ap_ctx->bss_stop_reason = BSS_STOP_REASON_INVALID; + ap_ctx->ap_active = false; + goto stopbss; + + case eSAP_DFS_CAC_START: + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_DFS_CAC_START_IND, + &dfs_info, + sizeof(struct wlan_dfs_info)); + hdd_ctx->dev_dfs_cac_status = DFS_CAC_IN_PROGRESS; + if (QDF_STATUS_SUCCESS != + hdd_send_radar_event(hdd_ctx, eSAP_DFS_CAC_START, + dfs_info, &adapter->wdev)) { + hdd_err("Unable to indicate CAC start NL event"); + } else { + hdd_debug("Sent CAC start to user space"); + } + + qdf_atomic_set(&adapter->ch_switch_in_progress, 0); + break; + case eSAP_DFS_CAC_INTERRUPTED: + /* + * The CAC timer did not run completely and a radar was detected + * during the CAC time. This new state will keep the tx path + * blocked since we do not want any transmission on the DFS + * channel. CAC end will only be reported here since the user + * space applications are waiting on CAC end for their state + * management. + */ + if (QDF_STATUS_SUCCESS != + hdd_send_radar_event(hdd_ctx, eSAP_DFS_CAC_END, + dfs_info, &adapter->wdev)) { + hdd_err("Unable to indicate CAC end (interrupted) event"); + } else { + hdd_debug("Sent CAC end (interrupted) to user space"); + } + break; + case eSAP_DFS_CAC_END: + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_DFS_CAC_END_IND, + &dfs_info, + sizeof(struct wlan_dfs_info)); + ap_ctx->dfs_cac_block_tx = false; + ucfg_ipa_set_dfs_cac_tx(hdd_ctx->pdev, + ap_ctx->dfs_cac_block_tx); + hdd_ctx->dev_dfs_cac_status = DFS_CAC_ALREADY_DONE; + if (QDF_STATUS_SUCCESS != + hdd_send_radar_event(hdd_ctx, eSAP_DFS_CAC_END, + dfs_info, &adapter->wdev)) { + hdd_err("Unable to indicate CAC end NL event"); + } else { + hdd_debug("Sent CAC end to user space"); + } + break; + case eSAP_DFS_RADAR_DETECT: + { + int i; + struct sap_config *sap_config = + &adapter->session.ap.sap_config; + + hdd_dfs_indicate_radar(hdd_ctx); + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_DFS_RADAR_DETECT_IND, + &dfs_info, + sizeof(struct wlan_dfs_info)); + hdd_ctx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; + for (i = 0; i < sap_config->channel_info_count; i++) { + if (sap_config->channel_info[i].ieee_chan_number + == dfs_info.channel) + sap_config->channel_info[i].flags |= + IEEE80211_CHAN_RADAR_DFS; + } + if (QDF_STATUS_SUCCESS != + hdd_send_radar_event(hdd_ctx, eSAP_DFS_RADAR_DETECT, + dfs_info, &adapter->wdev)) { + hdd_err("Unable to indicate Radar detect NL event"); + } else { + hdd_debug("Sent radar detected to user space"); + } + break; + } + case eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC: + hdd_debug("notification for radar detect during pre cac:%d", + adapter->vdev_id); + hdd_send_conditional_chan_switch_status(hdd_ctx, + &adapter->wdev, false); + hdd_ctx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; + qdf_create_work(0, &hdd_ctx->sap_pre_cac_work, + wlan_hdd_sap_pre_cac_failure, + (void *)adapter); + qdf_sched_work(0, &hdd_ctx->sap_pre_cac_work); + break; + case eSAP_DFS_PRE_CAC_END: + hdd_debug("pre cac end notification received:%d", + adapter->vdev_id); + hdd_send_conditional_chan_switch_status(hdd_ctx, + &adapter->wdev, true); + ap_ctx->dfs_cac_block_tx = false; + ucfg_ipa_set_dfs_cac_tx(hdd_ctx->pdev, + ap_ctx->dfs_cac_block_tx); + hdd_ctx->dev_dfs_cac_status = DFS_CAC_ALREADY_DONE; + + qdf_create_work(0, &hdd_ctx->sap_pre_cac_work, + wlan_hdd_sap_pre_cac_success, + (void *)adapter); + qdf_sched_work(0, &hdd_ctx->sap_pre_cac_work); + break; + case eSAP_DFS_NO_AVAILABLE_CHANNEL: + wlan_hdd_send_svc_nlink_msg + (hdd_ctx->radio_index, + WLAN_SVC_DFS_ALL_CHANNEL_UNAVAIL_IND, &dfs_info, + sizeof(struct wlan_dfs_info)); + break; + + case eSAP_STA_SET_KEY_EVENT: + /* TODO: + * forward the message to hostapd once implementation + * is done for now just print + */ + key_complete = &sap_event->sapevt.sapStationSetKeyCompleteEvent; + hdd_debug("SET Key: configured status = %s", + key_complete->status ? + "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS"); + + if (QDF_IS_STATUS_SUCCESS(key_complete->status)) { + hdd_softap_change_sta_state(adapter, + &key_complete->peerMacAddr, + OL_TXRX_PEER_STATE_AUTH); + status = wlan_hdd_send_sta_authorized_event( + adapter, hdd_ctx, + &key_complete->peerMacAddr); + + } + return QDF_STATUS_SUCCESS; + case eSAP_STA_MIC_FAILURE_EVENT: + { + memset(&msg, '\0', sizeof(msg)); + msg.src_addr.sa_family = ARPHRD_ETHER; + memcpy(msg.src_addr.sa_data, + &sap_event->sapevt.sapStationMICFailureEvent. + staMac, QDF_MAC_ADDR_SIZE); + hdd_debug("MIC MAC " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(msg.src_addr.sa_data)); + if (sap_event->sapevt.sapStationMICFailureEvent. + multicast == true) + msg.flags = IW_MICFAILURE_GROUP; + else + msg.flags = IW_MICFAILURE_PAIRWISE; + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = sizeof(msg); + we_event = IWEVMICHAELMICFAILURE; + we_custom_event_generic = (uint8_t *) &msg; + } + /* inform mic failure to nl80211 */ + cfg80211_michael_mic_failure(dev, + sap_event-> + sapevt.sapStationMICFailureEvent. + staMac.bytes, + ((sap_event->sapevt. + sapStationMICFailureEvent. + multicast == + true) ? + NL80211_KEYTYPE_GROUP : + NL80211_KEYTYPE_PAIRWISE), + sap_event->sapevt. + sapStationMICFailureEvent.keyId, + sap_event->sapevt. + sapStationMICFailureEvent.TSC, + GFP_KERNEL); + break; + + case eSAP_STA_ASSOC_EVENT: + case eSAP_STA_REASSOC_EVENT: + event = &sap_event->sapevt.sapStationAssocReassocCompleteEvent; + if (eSAP_STATUS_FAILURE == event->status) { + hdd_info("assoc failure: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wrqu.addr.sa_data)); + break; + } + + hdd_hostapd_apply_action_oui(hdd_ctx, adapter, event); + + wrqu.addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu.addr.sa_data, + &event->staMac, QDF_MAC_ADDR_SIZE); + hdd_info("associated " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wrqu.addr.sa_data)); + we_event = IWEVREGISTERED; + + if ((eCSR_ENCRYPT_TYPE_NONE == ap_ctx->encryption_type) || + (eCSR_ENCRYPT_TYPE_WEP40_STATICKEY == + ap_ctx->encryption_type) + || (eCSR_ENCRYPT_TYPE_WEP104_STATICKEY == + ap_ctx->encryption_type)) { + bAuthRequired = false; + } + + if (bAuthRequired) { + qdf_status = hdd_softap_register_sta( + adapter, + true, + ap_ctx->privacy, + (struct qdf_mac_addr *) + wrqu.addr.sa_data, + event->wmmEnabled); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("Failed to register STA %d " + QDF_MAC_ADDR_FMT, qdf_status, + QDF_MAC_ADDR_REF(wrqu.addr.sa_data)); + } else { + qdf_status = hdd_softap_register_sta( + adapter, + false, + ap_ctx->privacy, + (struct qdf_mac_addr *) + wrqu.addr.sa_data, + event->wmmEnabled); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("Failed to register STA %d " + QDF_MAC_ADDR_FMT, qdf_status, + QDF_MAC_ADDR_REF(wrqu.addr.sa_data)); + } + + sta_id = event->staId; + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_fill_station_info(adapter, event); + + if (ucfg_ipa_is_enabled()) { + status = ucfg_ipa_wlan_evt(hdd_ctx->pdev, + adapter->dev, + adapter->device_mode, + adapter->vdev_id, + WLAN_IPA_CLIENT_CONNECT_EX, + event->staMac.bytes); + if (status) + hdd_err("WLAN_CLIENT_CONNECT_EX event failed"); + } + + DPTRACE(qdf_dp_trace_mgmt_pkt(QDF_DP_TRACE_MGMT_PACKET_RECORD, + adapter->vdev_id, + QDF_TRACE_DEFAULT_PDEV_ID, + QDF_PROTO_TYPE_MGMT, QDF_PROTO_MGMT_ASSOC)); + + /* start timer in sap/p2p_go */ + if (ap_ctx->ap_active == false) { + hdd_bus_bw_compute_prev_txrx_stats(adapter); + hdd_bus_bw_compute_timer_start(hdd_ctx); + } + ap_ctx->ap_active = true; + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + wlan_hdd_auto_shutdown_enable(hdd_ctx, false); +#endif + cds_host_diag_log_work(&hdd_ctx->sap_wake_lock, + HDD_SAP_WAKE_LOCK_DURATION, + WIFI_POWER_EVENT_WAKELOCK_SAP); + qdf_wake_lock_timeout_acquire(&hdd_ctx->sap_wake_lock, + HDD_SAP_WAKE_LOCK_DURATION); + { + struct station_info *sta_info; + uint32_t ies_len = event->ies_len; + + sta_info = qdf_mem_malloc(sizeof(*sta_info)); + if (!sta_info) { + hdd_err("Failed to allocate station info"); + return QDF_STATUS_E_FAILURE; + } + + sta_info->assoc_req_ies = event->ies; + sta_info->assoc_req_ies_len = ies_len; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS) + /* + * After Kernel 4.0, it's no longer need to set + * STATION_INFO_ASSOC_REQ_IES flag, as it + * changed to use assoc_req_ies_len length to + * check the existence of request IE. + */ + sta_info->filled |= STATION_INFO_ASSOC_REQ_IES; +#endif + cfg80211_new_sta(dev, + (const u8 *)&event->staMac.bytes[0], + sta_info, GFP_KERNEL); + qdf_mem_free(sta_info); + } + /* Lets abort scan to ensure smooth authentication for client */ + if (ucfg_scan_get_vdev_status(adapter->vdev) != + SCAN_NOT_IN_PROGRESS) { + wlan_abort_scan(hdd_ctx->pdev, INVAL_PDEV_ID, + adapter->vdev_id, INVALID_SCAN_ID, + false); + } + if (adapter->device_mode == QDF_P2P_GO_MODE) { + /* send peer status indication to oem app */ + hdd_send_peer_status_ind_to_app( + &event->staMac, + ePeerConnected, + event->timingMeasCap, + adapter->vdev_id, + &event->chan_info, + adapter->device_mode); + } + + hdd_green_ap_add_sta(hdd_ctx); + break; + + case eSAP_STA_DISASSOC_EVENT: + disassoc_comp = + &sap_event->sapevt.sapStationDisassocCompleteEvent; + memcpy(wrqu.addr.sa_data, + &disassoc_comp->staMac, QDF_MAC_ADDR_SIZE); + + cache_stainfo = hdd_get_sta_info_by_mac( + &adapter->cache_sta_info_list, + disassoc_comp->staMac.bytes, + STA_INFO_HOSTAPD_SAP_EVENT_CB); + if (cache_stainfo) { + /* Cache the disassoc info */ + cache_stainfo->rssi = disassoc_comp->rssi; + cache_stainfo->tx_rate = disassoc_comp->tx_rate; + cache_stainfo->rx_rate = disassoc_comp->rx_rate; + cache_stainfo->rx_mc_bc_cnt = + disassoc_comp->rx_mc_bc_cnt; + cache_stainfo->rx_retry_cnt = + disassoc_comp->rx_retry_cnt; + cache_stainfo->reason_code = disassoc_comp->reason_code; + cache_stainfo->disassoc_ts = qdf_system_ticks(); + hdd_debug("Cache_stainfo rssi %d txrate %d rxrate %d reason_code %d", + cache_stainfo->rssi, + cache_stainfo->tx_rate, + cache_stainfo->rx_rate, + cache_stainfo->reason_code); + hdd_put_sta_info_ref(&adapter->cache_sta_info_list, + &cache_stainfo, true, + STA_INFO_HOSTAPD_SAP_EVENT_CB); + } + hdd_nofl_info("SAP disassociated " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wrqu.addr.sa_data)); + + qdf_status = qdf_event_set(&hostapd_state->qdf_sta_disassoc_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("Station Deauth event Set failed"); + + if (sap_event->sapevt.sapStationDisassocCompleteEvent.reason == + eSAP_USR_INITATED_DISASSOC) + hdd_debug(" User initiated disassociation"); + else + hdd_debug(" MAC initiated disassociation"); + we_event = IWEVEXPIRED; + + DPTRACE(qdf_dp_trace_mgmt_pkt(QDF_DP_TRACE_MGMT_PACKET_RECORD, + adapter->vdev_id, + QDF_TRACE_DEFAULT_PDEV_ID, + QDF_PROTO_TYPE_MGMT, QDF_PROTO_MGMT_DISASSOC)); + + stainfo = hdd_get_sta_info_by_mac( + &adapter->sta_info_list, + disassoc_comp->staMac.bytes, + STA_INFO_HOSTAPD_SAP_EVENT_CB); + if (!stainfo) { + hdd_err("Failed to find the right station"); + return QDF_STATUS_E_INVAL; + } + + /* Send DHCP STOP indication to FW */ + stainfo->dhcp_phase = DHCP_PHASE_ACK; + if (stainfo->dhcp_nego_status == + DHCP_NEGO_IN_PROGRESS) + hdd_post_dhcp_ind(adapter, + disassoc_comp->staMac.bytes, + WMA_DHCP_STOP_IND); + stainfo->dhcp_nego_status = DHCP_NEGO_STOP; + + hdd_softap_deregister_sta(adapter, &stainfo); + hdd_put_sta_info_ref(&adapter->sta_info_list, &stainfo, true, + STA_INFO_HOSTAPD_SAP_EVENT_CB); + + ap_ctx->ap_active = false; + + hdd_for_each_sta_ref_safe(adapter->sta_info_list, stainfo, + tmp, STA_INFO_HOSTAPD_SAP_EVENT_CB) { + if (!qdf_is_macaddr_broadcast( + &stainfo->sta_mac)) { + ap_ctx->ap_active = true; + hdd_put_sta_info_ref( + &adapter->sta_info_list, + &stainfo, true, + STA_INFO_HOSTAPD_SAP_EVENT_CB); + if (tmp) + hdd_put_sta_info_ref( + &adapter->sta_info_list, + &tmp, true, + STA_INFO_HOSTAPD_SAP_EVENT_CB); + break; + } + hdd_put_sta_info_ref(&adapter->sta_info_list, + &stainfo, true, + STA_INFO_HOSTAPD_SAP_EVENT_CB); + } + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + wlan_hdd_auto_shutdown_enable(hdd_ctx, true); +#endif + + cds_host_diag_log_work(&hdd_ctx->sap_wake_lock, + HDD_SAP_WAKE_LOCK_DURATION, + WIFI_POWER_EVENT_WAKELOCK_SAP); + qdf_wake_lock_timeout_acquire(&hdd_ctx->sap_wake_lock, + HDD_SAP_CLIENT_DISCONNECT_WAKE_LOCK_DURATION); + + /* + * Don't indicate delete station event if P2P GO and + * SSR in progress. Since supplicant will change mode + * fail and down during this time. + */ + if ((adapter->device_mode != QDF_P2P_GO_MODE) || + (!cds_is_driver_recovering())) { + cfg80211_del_sta(dev, + (const u8 *)&sap_event->sapevt. + sapStationDisassocCompleteEvent.staMac. + bytes[0], GFP_KERNEL); + hdd_debug("indicate sta deletion event"); + } + + /* Update the beacon Interval if it is P2P GO */ + qdf_status = policy_mgr_change_mcc_go_beacon_interval( + hdd_ctx->psoc, adapter->vdev_id, + adapter->device_mode); + if (QDF_STATUS_SUCCESS != qdf_status) { + hdd_err("Failed to update Beacon interval status: %d", + qdf_status); + } + if (adapter->device_mode == QDF_P2P_GO_MODE) { + /* send peer status indication to oem app */ + hdd_send_peer_status_ind_to_app(&sap_event->sapevt. + sapStationDisassocCompleteEvent. + staMac, ePeerDisconnected, + 0, + adapter->vdev_id, + NULL, + adapter->device_mode); + } + /*stop timer in sap/p2p_go */ + if (ap_ctx->ap_active == false) { + hdd_bus_bw_compute_reset_prev_txrx_stats(adapter); + hdd_bus_bw_compute_timer_try_stop(hdd_ctx); + } + + hdd_green_ap_del_sta(hdd_ctx); + break; + + case eSAP_WPS_PBC_PROBE_REQ_EVENT: + hdd_debug("WPS PBC probe req"); + return QDF_STATUS_SUCCESS; + + case eSAP_UNKNOWN_STA_JOIN: + unknownSTAEvent = qdf_mem_malloc(IW_CUSTOM_MAX + 1); + if (!unknownSTAEvent) + return QDF_STATUS_E_NOMEM; + + snprintf(unknownSTAEvent, IW_CUSTOM_MAX, + "JOIN_UNKNOWN_STA-"QDF_FULL_MAC_FMT, + QDF_FULL_MAC_REF(sap_event->sapevt.sapUnknownSTAJoin.macaddr.bytes)); + we_event = IWEVCUSTOM; /* Discovered a new node (AP mode). */ + wrqu.data.pointer = unknownSTAEvent; + wrqu.data.length = strlen(unknownSTAEvent); + we_custom_event_generic = (uint8_t *) unknownSTAEvent; + hdd_err("%s", unknownSTAEvent); + break; + + case eSAP_MAX_ASSOC_EXCEEDED: + maxAssocExceededEvent = qdf_mem_malloc(IW_CUSTOM_MAX + 1); + if (!maxAssocExceededEvent) + return QDF_STATUS_E_NOMEM; + + snprintf(maxAssocExceededEvent, IW_CUSTOM_MAX, + "Peer "QDF_FULL_MAC_FMT" denied" + " assoc due to Maximum Mobile Hotspot connections reached. Please disconnect" + " one or more devices to enable the new device connection", + QDF_FULL_MAC_REF(sap_event->sapevt.sapMaxAssocExceeded.macaddr.bytes)); + we_event = IWEVCUSTOM; /* Discovered a new node (AP mode). */ + wrqu.data.pointer = maxAssocExceededEvent; + wrqu.data.length = strlen(maxAssocExceededEvent); + we_custom_event_generic = (uint8_t *) maxAssocExceededEvent; + hdd_debug("%s", maxAssocExceededEvent); + break; + case eSAP_STA_ASSOC_IND: + if (sap_event->sapevt.sapAssocIndication.owe_ie) { + hdd_send_update_owe_info_event(adapter, + sap_event->sapevt.sapAssocIndication.staMac.bytes, + sap_event->sapevt.sapAssocIndication.owe_ie, + sap_event->sapevt.sapAssocIndication.owe_ie_len); + qdf_mem_free( + sap_event->sapevt.sapAssocIndication.owe_ie); + sap_event->sapevt.sapAssocIndication.owe_ie = NULL; + sap_event->sapevt.sapAssocIndication.owe_ie_len = 0; + } + return QDF_STATUS_SUCCESS; + + case eSAP_DISCONNECT_ALL_P2P_CLIENT: + hdd_clear_all_sta(adapter); + return QDF_STATUS_SUCCESS; + + case eSAP_MAC_TRIG_STOP_BSS_EVENT: + ret = hdd_stop_bss_link(adapter); + if (ret) + hdd_warn("hdd_stop_bss_link failed %d", ret); + return QDF_STATUS_SUCCESS; + + case eSAP_CHANNEL_CHANGE_EVENT: + if (hostapd_state->bss_state != BSS_STOP) { + /* Allow suspend for old channel */ + hdd_hostapd_channel_allow_suspend(adapter, + ap_ctx->sap_context->freq_before_ch_switch); + /* Prevent suspend for new channel */ + hdd_hostapd_channel_prevent_suspend(adapter, + sap_event->sapevt.sap_ch_selected.pri_ch_freq); + } + /* SME/PE is already updated for new operation + * channel. So update HDD layer also here. This + * resolves issue in AP-AP mode where AP1 channel is + * changed due to RADAR then CAC is going on and + * START_BSS on new channel has not come to HDD. At + * this case if AP2 is started it needs current + * operation channel for MCC DFS restriction + */ + ap_ctx->operating_chan_freq = + sap_event->sapevt.sap_ch_selected.pri_ch_freq; + ap_ctx->sap_config.acs_cfg.pri_ch_freq = + sap_event->sapevt.sap_ch_selected.pri_ch_freq; + ap_ctx->sap_config.acs_cfg.ht_sec_ch_freq = + sap_event->sapevt.sap_ch_selected.ht_sec_ch_freq; + ap_ctx->sap_config.acs_cfg.vht_seg0_center_ch_freq = + sap_event->sapevt.sap_ch_selected.vht_seg0_center_ch_freq; + ap_ctx->sap_config.acs_cfg.vht_seg1_center_ch_freq = + sap_event->sapevt.sap_ch_selected.vht_seg1_center_ch_freq; + ap_ctx->sap_config.acs_cfg.ch_width = + sap_event->sapevt.sap_ch_selected.ch_width; + + cdp_hl_fc_set_td_limit(cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id, + ap_ctx->operating_chan_freq); + sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); + if (!sap_ctx) { + hdd_err("sap ctx is null"); + return QDF_STATUS_E_FAILURE; + } + + if (sap_ctx->is_chan_change_inprogress) { + hdd_debug("check for possible hw mode change"); + status = policy_mgr_set_hw_mode_on_channel_switch( + hdd_ctx->psoc, adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("set hw mode change not done"); + } + + return hdd_hostapd_chan_change(adapter, sap_event); + case eSAP_ACS_SCAN_SUCCESS_EVENT: + return hdd_handle_acs_scan_event(sap_event, adapter); + + case eSAP_ACS_CHANNEL_SELECTED: + ap_ctx->sap_config.acs_cfg.pri_ch_freq = + sap_event->sapevt.sap_ch_selected.pri_ch_freq; + ap_ctx->sap_config.acs_cfg.ht_sec_ch_freq = + sap_event->sapevt.sap_ch_selected.ht_sec_ch_freq; + ap_ctx->sap_config.acs_cfg.vht_seg0_center_ch_freq = + sap_event->sapevt.sap_ch_selected.vht_seg0_center_ch_freq; + ap_ctx->sap_config.acs_cfg.vht_seg1_center_ch_freq = + sap_event->sapevt.sap_ch_selected.vht_seg1_center_ch_freq; + ap_ctx->sap_config.acs_cfg.ch_width = + sap_event->sapevt.sap_ch_selected.ch_width; + hdd_nofl_info("ACS Completed vid %d freq %d BW %d", + adapter->vdev_id, + ap_ctx->sap_config.acs_cfg.pri_ch_freq, + ap_ctx->sap_config.acs_cfg.ch_width); + + wlan_hdd_cfg80211_acs_ch_select_evt(adapter); + + return QDF_STATUS_SUCCESS; + case eSAP_ECSA_CHANGE_CHAN_IND: + hdd_debug("Channel change indication from peer for channel freq %d", + sap_event->sapevt.sap_chan_cng_ind.new_chan_freq); + wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, adapter->vdev_id, + CSA_REASON_PEER_ACTION_FRAME); + if (hdd_softap_set_channel_change(dev, + sap_event->sapevt.sap_chan_cng_ind.new_chan_freq, + CH_WIDTH_MAX, false)) + return QDF_STATUS_E_FAILURE; + else + return QDF_STATUS_SUCCESS; + + case eSAP_DFS_NEXT_CHANNEL_REQ: + hdd_debug("Sending next channel query to userspace"); + hdd_update_acs_timer_reason(adapter, + QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS); + return QDF_STATUS_SUCCESS; + + case eSAP_STOP_BSS_DUE_TO_NO_CHNL: + hdd_debug("Stop sap session[%d]", + adapter->vdev_id); + schedule_work(&adapter->sap_stop_bss_work); + return QDF_STATUS_SUCCESS; + + case eSAP_CHANNEL_CHANGE_RESP: + /* + * Set the ch_switch_in_progress flag to zero and also enable + * roaming once channel change process (success/failure) + * is completed + */ + qdf_atomic_set(&adapter->ch_switch_in_progress, 0); + policy_mgr_set_chan_switch_complete_evt(hdd_ctx->psoc); + wlan_hdd_enable_roaming(adapter, + RSO_SAP_CHANNEL_CHANGE); + if (CHANNEL_STATE_DFS != + wlan_reg_get_channel_state_for_freq(hdd_ctx->pdev, + ap_ctx->operating_chan_freq)) + ap_ctx->dfs_cac_block_tx = false; + + /* Check any other sap need restart */ + if (ap_ctx->sap_context->csa_reason == + CSA_REASON_UNSAFE_CHANNEL) + hdd_unsafe_channel_restart_sap(hdd_ctx); + + return hdd_hostapd_chan_change(adapter, sap_event); + default: + hdd_debug("SAP message is not handled"); + goto stopbss; + return QDF_STATUS_SUCCESS; + } + wireless_send_event(dev, we_event, &wrqu, + (char *)we_custom_event_generic); + qdf_mem_free(we_custom_start_event); + qdf_mem_free(unknownSTAEvent); + qdf_mem_free(maxAssocExceededEvent); + + return QDF_STATUS_SUCCESS; + +stopbss: + { + uint8_t *we_custom_event; + char *stopBssEvent = "STOP-BSS.response"; /* 17 */ + int event_len = strlen(stopBssEvent); + + hdd_debug("BSS stop status = %s", + sap_event->sapevt.sapStopBssCompleteEvent.status ? + "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS"); + + /* Change the BSS state now since, as we are shutting + * things down, we don't want interfaces to become + * re-enabled + */ + hostapd_state->bss_state = BSS_STOP; + hdd_stop_tsf_sync(adapter); + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + wlan_hdd_auto_shutdown_enable(hdd_ctx, true); +#endif + + /* Stop the pkts from n/w stack as we are going to free all of + * the TX WMM queues for all STAID's + */ + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + /* reclaim all resources allocated to the BSS */ + qdf_status = hdd_softap_stop_bss(adapter); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_debug("hdd_softap_stop_bss failed %d", + qdf_status); + if (ucfg_ipa_is_enabled()) { + ucfg_ipa_uc_disconnect_ap(hdd_ctx->pdev, + adapter->dev); + ucfg_ipa_cleanup_dev_iface(hdd_ctx->pdev, + adapter->dev); + } + } + + we_custom_event = + qdf_mem_malloc(HDD_MAX_CUSTOM_START_EVENT_SIZE); + if (!we_custom_event) + return QDF_STATUS_E_NOMEM; + + /* notify userspace that the BSS has stopped */ + memset(we_custom_event, '\0', + sizeof(HDD_MAX_CUSTOM_START_EVENT_SIZE)); + memcpy(we_custom_event, stopBssEvent, event_len); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = event_len; + we_event = IWEVCUSTOM; + we_custom_event_generic = we_custom_event; + wireless_send_event(dev, we_event, &wrqu, + (char *)we_custom_event_generic); + + qdf_mem_free(we_custom_start_event); + qdf_mem_free(unknownSTAEvent); + qdf_mem_free(maxAssocExceededEvent); + qdf_mem_free(we_custom_event); + + /* once the event is set, structure dev/adapter should + * not be touched since they are now subject to being deleted + * by another thread + */ + if (eSAP_STOP_BSS_EVENT == event_id) { + qdf_event_set(&hostapd_state->qdf_stop_bss_event); + hdd_bus_bw_compute_timer_try_stop(hdd_ctx); + } + + hdd_ipa_set_tx_flow_info(); + } + return QDF_STATUS_SUCCESS; +} + +static int hdd_softap_unpack_ie(mac_handle_t mac_handle, + eCsrEncryptionType *encrypt_type, + eCsrEncryptionType *mc_encrypt_type, + tCsrAuthList *akm_list, + bool *mfp_capable, + bool *mfp_required, + uint16_t gen_ie_len, uint8_t *gen_ie) +{ + uint32_t ret; + uint8_t *rsn_ie; + uint16_t rsn_ie_len, i; + tDot11fIERSN dot11_rsn_ie = {0}; + tDot11fIEWPA dot11_wpa_ie = {0}; + + if (!mac_handle) { + hdd_err("NULL mac Handle"); + return -EINVAL; + } + /* Validity checks */ + if ((gen_ie_len < QDF_MIN(DOT11F_IE_RSN_MIN_LEN, DOT11F_IE_WPA_MIN_LEN)) + || (gen_ie_len > + QDF_MAX(DOT11F_IE_RSN_MAX_LEN, DOT11F_IE_WPA_MAX_LEN))) + return -EINVAL; + /* Type check */ + if (gen_ie[0] == DOT11F_EID_RSN) { + /* Validity checks */ + if ((gen_ie_len < DOT11F_IE_RSN_MIN_LEN) || + (gen_ie_len > DOT11F_IE_RSN_MAX_LEN)) { + return QDF_STATUS_E_FAILURE; + } + /* Skip past the EID byte and length byte */ + rsn_ie = gen_ie + 2; + rsn_ie_len = gen_ie_len - 2; + /* Unpack the RSN IE */ + memset(&dot11_rsn_ie, 0, sizeof(tDot11fIERSN)); + ret = sme_unpack_rsn_ie(mac_handle, rsn_ie, rsn_ie_len, + &dot11_rsn_ie, false); + if (DOT11F_FAILED(ret)) { + hdd_err("unpack failed, 0x%x", ret); + return -EINVAL; + } + /* Copy out the encryption and authentication types */ + hdd_debug("pairwise cipher count: %d akm count:%d", + dot11_rsn_ie.pwise_cipher_suite_count, + dot11_rsn_ie.akm_suite_cnt); + /* + * Translate akms in akm suite + */ + for (i = 0; i < dot11_rsn_ie.akm_suite_cnt; i++) + akm_list->authType[i] = + hdd_translate_rsn_to_csr_auth_type( + dot11_rsn_ie.akm_suite[i]); + akm_list->numEntries = dot11_rsn_ie.akm_suite_cnt; + /* dot11_rsn_ie.pwise_cipher_suite_count */ + *encrypt_type = + hdd_translate_rsn_to_csr_encryption_type(dot11_rsn_ie. + pwise_cipher_suites[0]); + /* dot11_rsn_ie.gp_cipher_suite_count */ + *mc_encrypt_type = + hdd_translate_rsn_to_csr_encryption_type(dot11_rsn_ie. + gp_cipher_suite); + /* Set the PMKSA ID Cache for this interface */ + *mfp_capable = 0 != (dot11_rsn_ie.RSN_Cap[0] & 0x80); + *mfp_required = 0 != (dot11_rsn_ie.RSN_Cap[0] & 0x40); + } else if (gen_ie[0] == DOT11F_EID_WPA) { + /* Validity checks */ + if ((gen_ie_len < DOT11F_IE_WPA_MIN_LEN) || + (gen_ie_len > DOT11F_IE_WPA_MAX_LEN)) { + return QDF_STATUS_E_FAILURE; + } + /* Skip past the EID byte and length byte and 4 byte WiFi OUI */ + rsn_ie = gen_ie + 2 + 4; + rsn_ie_len = gen_ie_len - (2 + 4); + /* Unpack the WPA IE */ + memset(&dot11_wpa_ie, 0, sizeof(tDot11fIEWPA)); + ret = dot11f_unpack_ie_wpa(MAC_CONTEXT(mac_handle), + rsn_ie, rsn_ie_len, + &dot11_wpa_ie, false); + if (DOT11F_FAILED(ret)) { + hdd_err("unpack failed, 0x%x", ret); + return -EINVAL; + } + /* Copy out the encryption and authentication types */ + hdd_debug("WPA unicast cipher suite count: %d akm count: %d", + dot11_wpa_ie.unicast_cipher_count, + dot11_wpa_ie.auth_suite_count); + /* + * Translate akms in akm suite + */ + for (i = 0; i < dot11_wpa_ie.auth_suite_count; i++) + akm_list->authType[i] = + hdd_translate_wpa_to_csr_auth_type( + dot11_wpa_ie.auth_suites[i]); + akm_list->numEntries = dot11_wpa_ie.auth_suite_count; + /* dot11_wpa_ie.unicast_cipher_count */ + *encrypt_type = + hdd_translate_wpa_to_csr_encryption_type(dot11_wpa_ie. + unicast_ciphers[0]); + /* dot11_wpa_ie.unicast_cipher_count */ + *mc_encrypt_type = + hdd_translate_wpa_to_csr_encryption_type(dot11_wpa_ie. + multicast_cipher); + *mfp_capable = false; + *mfp_required = false; + } else { + hdd_err("gen_ie[0]: %d", gen_ie[0]); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +bool hdd_is_any_sta_connecting(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + struct hdd_station_ctx *sta_ctx; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_IS_ANY_STA_CONNECTING; + + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return false; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if ((adapter->device_mode == QDF_STA_MODE) || + (adapter->device_mode == QDF_P2P_CLIENT_MODE) || + (adapter->device_mode == QDF_P2P_DEVICE_MODE)) { + if (sta_ctx->conn_info.conn_state == + eConnectionState_Connecting) { + hdd_debug("vdev_id %d: connecting", + adapter->vdev_id); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return true; + } + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return false; +} + +int hdd_softap_set_channel_change(struct net_device *dev, int target_chan_freq, + enum phy_ch_width target_bw, bool forced) +{ + QDF_STATUS status; + int ret = 0; + struct hdd_adapter *adapter = (netdev_priv(dev)); + struct hdd_context *hdd_ctx = NULL; + struct hdd_adapter *sta_adapter; + struct hdd_station_ctx *sta_ctx; + struct sap_context *sap_ctx; + uint8_t conc_rule1 = 0; + uint8_t scc_on_lte_coex = 0; + bool is_p2p_go_session = false; + struct wlan_objmgr_vdev *vdev; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (adapter->device_mode != QDF_SAP_MODE && + adapter->device_mode != QDF_P2P_GO_MODE) + return -EINVAL; + sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); + if (!sap_ctx) + return -EINVAL; + /* + * If sta connection is in progress do not allow SAP channel change from + * user space as it may change the HW mode requirement, for which sta is + * trying to connect. + */ + if (hdd_is_any_sta_connecting(hdd_ctx)) { + hdd_err("STA connection is in progress"); + return -EBUSY; + } + + ret = hdd_validate_channel_and_bandwidth(adapter, + target_chan_freq, target_bw); + if (ret) { + hdd_err("Invalid CH and BW combo"); + return ret; + } + + sta_adapter = hdd_get_adapter(hdd_ctx, QDF_STA_MODE); + ucfg_policy_mgr_get_conc_rule1(hdd_ctx->psoc, &conc_rule1); + /* + * conc_custom_rule1: + * Force SCC for SAP + STA + * if STA is already connected then we shouldn't allow + * channel switch in SAP interface. + */ + if (sta_adapter && conc_rule1) { + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter); + if (hdd_conn_is_connected(sta_ctx)) { + hdd_err("Channel switch not allowed after STA connection with conc_custom_rule1 enabled"); + return -EBUSY; + } + } + + /* + * Set the ch_switch_in_progress flag to mimic channel change + * when a radar is found. This will enable synchronizing + * SAP and HDD states similar to that of radar indication. + * Suspend the netif queues to stop queuing Tx frames + * from upper layers. netif queues will be resumed + * once the channel change is completed and SAP will + * post eSAP_START_BSS_EVENT success event to HDD. + */ + if (qdf_atomic_inc_return(&adapter->ch_switch_in_progress) > 1) { + hdd_err("Channel switch in progress!!"); + return -EBUSY; + } + + /* + * Do SAP concurrency check to cover channel switch case as following: + * There is already existing SAP+GO combination but due to upper layer + * notifying LTE-COEX event or sending command to move one connection + * to different channel. Before moving existing connection to new + * channel, check if new channel can co-exist with the other existing + * connection. For example, SAP1 is on channel-6 and SAP2 is on + * channel-36 and lets say they are doing DBS, and upper layer sends + * LTE-COEX to move SAP1 from channel-6 to channel-149. SAP1 and + * SAP2 will end up doing MCC which may not be desirable result. It + * should will be prevented. + */ + if (!policy_mgr_allow_concurrency_csa( + hdd_ctx->psoc, + policy_mgr_convert_device_mode_to_qdf_type( + adapter->device_mode), + target_chan_freq, + adapter->vdev_id, + forced, + sap_ctx->csa_reason)) { + hdd_err("Channel switch failed due to concurrency check failure"); + qdf_atomic_set(&adapter->ch_switch_in_progress, 0); + return -EINVAL; + } + + status = + ucfg_policy_mgr_get_sta_sap_scc_lte_coex_chnl(hdd_ctx->psoc, + &scc_on_lte_coex); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("can't get STA-SAP SCC on lte coex channel setting"); + qdf_atomic_set(&adapter->ch_switch_in_progress, 0); + return -EINVAL; + } + + /* + * Reject channel change req if reassoc in progress on any adapter. + * sme_is_any_session_in_middle_of_roaming is for LFR2 and + * hdd_is_roaming_in_progress is for LFR3 + */ + if (sme_is_any_session_in_middle_of_roaming(hdd_ctx->mac_handle) || + hdd_is_roaming_in_progress(hdd_ctx)) { + hdd_info("Channel switch not allowed as reassoc in progress"); + qdf_atomic_set(&adapter->ch_switch_in_progress, 0); + return -EINVAL; + } + /* Disable Roaming on all adapters before doing channel change */ + wlan_hdd_disable_roaming(adapter, RSO_SAP_CHANNEL_CHANGE); + + /* + * Post the Channel Change request to SAP. + */ + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) { + qdf_atomic_set(&adapter->ch_switch_in_progress, 0); + wlan_hdd_enable_roaming(adapter, RSO_SAP_CHANNEL_CHANGE); + return -EINVAL; + } + if (wlan_vdev_mlme_get_opmode(vdev) == QDF_P2P_GO_MODE) + is_p2p_go_session = true; + hdd_objmgr_put_vdev(vdev); + + status = wlansap_set_channel_change_with_csa( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + target_chan_freq, + target_bw, + (forced && !scc_on_lte_coex) || is_p2p_go_session); + + if (QDF_STATUS_SUCCESS != status) { + hdd_err("SAP set channel failed for channel freq: %d, bw: %d", + target_chan_freq, target_bw); + /* + * If channel change command fails then clear the + * radar found flag and also restart the netif + * queues. + */ + qdf_atomic_set(&adapter->ch_switch_in_progress, 0); + + /* + * If Posting of the Channel Change request fails + * enable roaming on all adapters + */ + wlan_hdd_enable_roaming(adapter, + RSO_SAP_CHANNEL_CHANGE); + + ret = -EINVAL; + } + + return ret; +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +void hdd_sap_restart_with_channel_switch(struct hdd_adapter *ap_adapter, + uint32_t target_chan_freq, + uint32_t target_bw, + bool forced) +{ + struct net_device *dev = ap_adapter->dev; + int ret; + + hdd_enter(); + + if (!dev) { + hdd_err("Invalid dev pointer"); + return; + } + + ret = hdd_softap_set_channel_change(dev, target_chan_freq, + target_bw, forced); + if (ret) { + hdd_err("channel switch failed"); + return; + } +} + +void hdd_sap_restart_chan_switch_cb(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t ch_freq, + uint32_t channel_bw, + bool forced) +{ + struct hdd_adapter *ap_adapter = + wlan_hdd_get_adapter_from_vdev(psoc, vdev_id); + + if (!ap_adapter) { + hdd_err("Adapter is NULL"); + return; + } + hdd_sap_restart_with_channel_switch(ap_adapter, + ch_freq, + channel_bw, forced); +} + +void wlan_hdd_set_sap_csa_reason(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + uint8_t reason) +{ + struct sap_context *sap_ctx; + struct hdd_adapter *ap_adapter = wlan_hdd_get_adapter_from_vdev( + psoc, vdev_id); + if (!ap_adapter) { + hdd_err("ap adapter is NULL"); + return; + } + sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter); + if (sap_ctx) + sap_ctx->csa_reason = reason; +} + +QDF_STATUS wlan_hdd_get_channel_for_sap_restart( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t *ch_freq) +{ + mac_handle_t mac_handle; + struct hdd_ap_ctx *hdd_ap_ctx; + struct hdd_context *hdd_ctx; + uint8_t mcc_to_scc_switch = 0; + struct ch_params ch_params; + struct hdd_adapter *ap_adapter = wlan_hdd_get_adapter_from_vdev( + psoc, vdev_id); + uint32_t sap_ch_freq, intf_ch_freq; + struct sap_context *sap_context; + enum sap_csa_reason_code csa_reason = + CSA_REASON_CONCURRENT_STA_CHANGED_CHANNEL; + + if (!ap_adapter) { + hdd_err("ap_adapter is NULL"); + return QDF_STATUS_E_FAILURE; + } + + hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); + if (!hdd_ctx) { + hdd_err("hdd_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (!ch_freq) { + hdd_err("Null parameters"); + return QDF_STATUS_E_FAILURE; + } + + if (!test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) { + hdd_err("SOFTAP_BSS_STARTED not set"); + return QDF_STATUS_E_FAILURE; + } + + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) { + hdd_err("mac_handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + sap_context = hdd_ap_ctx->sap_context; + if (!sap_context) { + hdd_err("sap_context is null"); + return QDF_STATUS_E_FAILURE; + } + if (QDF_IS_STATUS_ERROR(wlansap_context_get(sap_context))) { + hdd_err("sap_context is invalid"); + return QDF_STATUS_E_FAILURE; + } + + intf_ch_freq = wlansap_get_chan_band_restrict(sap_context); + if (intf_ch_freq) { + csa_reason = CSA_REASON_BAND_RESTRICTED; + goto sap_restart; + } + /* + * If STA+SAP sessions are on DFS channel and STA+SAP SCC is + * enabled on DFS channel then move the SAP out of DFS channel + * as soon as STA gets disconnect. + */ + if (policy_mgr_is_sap_restart_required_after_sta_disconnect( + psoc, vdev_id, &intf_ch_freq)) { + hdd_debug("Move the sap (vdev %d) to user configured channel %u", + vdev_id, intf_ch_freq); + goto sap_restart; + } + + if (ap_adapter->device_mode == QDF_P2P_GO_MODE && + !policy_mgr_go_scc_enforced(psoc)) { + wlansap_context_put(sap_context); + hdd_debug("p2p go no scc required"); + return QDF_STATUS_E_FAILURE; + } + ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc, + &mcc_to_scc_switch); + /* + * Check if STA's channel is DFS or passive or part of LTE avoided + * channel list. In that case move SAP to other band if DBS is + * supported, return from here if DBS is not supported. + * Need to take care of 3 port cases with 2 STA iface in future. + */ + intf_ch_freq = wlansap_check_cc_intf(sap_context); + policy_mgr_get_chan_by_session_id(psoc, vdev_id, &sap_ch_freq); + hdd_debug("sap_vdev %d intf_ch: %d, orig freq: %d", + vdev_id, intf_ch_freq, sap_ch_freq); + if (QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION != + mcc_to_scc_switch) { + if (QDF_IS_STATUS_ERROR( + policy_mgr_valid_sap_conc_channel_check( + hdd_ctx->psoc, &intf_ch_freq, sap_ch_freq, vdev_id))) { + wlansap_context_put(sap_context); + hdd_debug("can't move sap to chan(freq): %u", + intf_ch_freq); + return QDF_STATUS_E_FAILURE; + } + } + +sap_restart: + if (!intf_ch_freq) { + wlansap_context_put(sap_context); + hdd_debug("interface channel is 0"); + return QDF_STATUS_E_FAILURE; + } else { + sap_context->csa_reason = csa_reason; + } + hdd_debug("SAP restart orig chan freq: %d, new freq: %d", + hdd_ap_ctx->sap_config.chan_freq, intf_ch_freq); + ch_params.ch_width = CH_WIDTH_MAX; + hdd_ap_ctx->bss_stop_reason = BSS_STOP_DUE_TO_MCC_SCC_SWITCH; + + wlan_reg_set_channel_params_for_freq(hdd_ctx->pdev, + intf_ch_freq, 0, + &ch_params); + + *ch_freq = intf_ch_freq; + hdd_debug("SAP channel change with CSA/ECSA"); + hdd_sap_restart_chan_switch_cb(psoc, vdev_id, *ch_freq, + ch_params.ch_width, false); + wlansap_context_put(sap_context); + + return QDF_STATUS_SUCCESS; +} + +#if defined(CONFIG_BAND_6GHZ) && defined(WLAN_FEATURE_11AX) +uint32_t hdd_get_ap_6ghz_capable(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + uint32_t keymgmt; + struct hdd_adapter *ap_adapter; + struct hdd_ap_ctx *ap_ctx; + struct sap_context *sap_context; + struct sap_config *sap_config; + uint32_t capable = 0; + + if (!psoc) { + hdd_err("PSOC is NULL"); + return 0; + } + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_HDD_ID_OBJ_MGR); + if (!vdev) { + hdd_err("vdev is NULL %d", vdev_id); + return 0; + } + + ap_adapter = wlan_hdd_get_adapter_from_vdev( + psoc, vdev_id); + if (!ap_adapter) { + hdd_err("ap_adapter is NULL %d", vdev_id); + wlan_objmgr_vdev_release_ref(vdev, WLAN_HDD_ID_OBJ_MGR); + return 0; + } + if (ap_adapter->device_mode != QDF_SAP_MODE) { + hdd_err("unexpected device mode %d", ap_adapter->device_mode); + wlan_objmgr_vdev_release_ref(vdev, WLAN_HDD_ID_OBJ_MGR); + return 0; + } + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); + sap_config = &ap_ctx->sap_config; + sap_context = ap_ctx->sap_context; + if (QDF_IS_STATUS_ERROR(wlansap_context_get(sap_context))) { + hdd_err("sap_context is get failed %d", vdev_id); + wlan_objmgr_vdev_release_ref(vdev, WLAN_HDD_ID_OBJ_MGR); + return 0; + } + /* SAP is allowed on 6GHz with explicit indication from user space: + * a. SAP is started on 6Ghz already. + * b. SAP is configured on 6Ghz fixed channel from userspace. + * c. SAP is configured by ACS range which includes any 6Ghz channel. + */ + if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) { + if (WLAN_REG_IS_6GHZ_CHAN_FREQ( + ap_ctx->operating_chan_freq)) + capable |= CONN_6GHZ_FLAG_ACS_OR_USR_ALLOWED; + } else { + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(sap_config->chan_freq)) + capable |= CONN_6GHZ_FLAG_ACS_OR_USR_ALLOWED; + else if (sap_context && WLAN_REG_IS_6GHZ_CHAN_FREQ( + sap_context->chan_freq)) + capable |= CONN_6GHZ_FLAG_ACS_OR_USR_ALLOWED; + } + if (wlansap_is_6ghz_included_in_acs_range(sap_context)) + capable |= CONN_6GHZ_FLAG_ACS_OR_USR_ALLOWED; + + keymgmt = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT); + if (!keymgmt || (keymgmt & (1 << WLAN_CRYPTO_KEY_MGMT_NONE | + 1 << WLAN_CRYPTO_KEY_MGMT_SAE | + 1 << WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B | + 1 << WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B_192 | + 1 << WLAN_CRYPTO_KEY_MGMT_OWE))) { + capable |= CONN_6GHZ_FLAG_SECURITY_ALLOWED; + } + capable |= CONN_6GHZ_FLAG_VALID; + hdd_debug("vdev_id %d keymgmt 0x%08x capable 0x%x", + vdev_id, keymgmt, capable); + wlansap_context_put(sap_context); + wlan_objmgr_vdev_release_ref(vdev, WLAN_HDD_ID_OBJ_MGR); + + return capable; +} +#else +uint32_t hdd_get_ap_6ghz_capable(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + return 0; +} +#endif +#endif + +#ifdef WLAN_FEATURE_TSF_PTP +static const struct ethtool_ops wlan_hostapd_ethtool_ops = { + .get_ts_info = wlan_get_ts_info, +}; +#endif + +const struct net_device_ops net_ops_struct = { + .ndo_open = hdd_hostapd_open, + .ndo_stop = hdd_hostapd_stop, + .ndo_uninit = hdd_hostapd_uninit, + .ndo_start_xmit = hdd_softap_hard_start_xmit, + .ndo_tx_timeout = hdd_softap_tx_timeout, + .ndo_get_stats = hdd_get_stats, + .ndo_set_mac_address = hdd_hostapd_set_mac_address, + .ndo_do_ioctl = hdd_ioctl, + .ndo_change_mtu = hdd_hostapd_change_mtu, + .ndo_select_queue = hdd_select_queue, +}; + +#ifdef WLAN_FEATURE_TSF_PTP +void hdd_set_ap_ops(struct net_device *dev) +{ + dev->netdev_ops = &net_ops_struct; + dev->ethtool_ops = &wlan_hostapd_ethtool_ops; +} +#else +void hdd_set_ap_ops(struct net_device *dev) +{ + dev->netdev_ops = &net_ops_struct; +} +#endif + +bool hdd_sap_create_ctx(struct hdd_adapter *adapter) +{ + hdd_debug("creating sap context"); + adapter->session.ap.sap_context = sap_create_ctx(); + if (adapter->session.ap.sap_context) + return true; + + return false; +} + +bool hdd_sap_destroy_ctx(struct hdd_adapter *adapter) +{ + struct sap_context *sap_ctx = adapter->session.ap.sap_context; + + if (adapter->session.ap.beacon) { + qdf_mem_free(adapter->session.ap.beacon); + adapter->session.ap.beacon = NULL; + } + + if (!sap_ctx) { + hdd_debug("sap context is NULL"); + return true; + } + + hdd_debug("destroying sap context"); + + if (QDF_IS_STATUS_ERROR(sap_destroy_ctx(sap_ctx))) + return false; + + adapter->session.ap.sap_context = NULL; + + return true; +} + +void hdd_sap_destroy_ctx_all(struct hdd_context *hdd_ctx, bool is_ssr) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + + /* sap_ctx is not destroyed as it will be leveraged for sap restart */ + if (is_ssr) + return; + + hdd_debug("destroying all the sap context"); + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_SAP_DESTROY_CTX_ALL) { + if (adapter->device_mode == QDF_SAP_MODE) + hdd_sap_destroy_ctx(adapter); + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_SAP_DESTROY_CTX_ALL); + } +} + +static void +hdd_indicate_peers_deleted(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct hdd_adapter *adapter; + + if (!psoc) { + hdd_err("psoc obj is NULL"); + return; + } + + adapter = wlan_hdd_get_adapter_from_vdev(psoc, vdev_id); + if (hdd_validate_adapter(adapter)) { + hdd_err("invalid adapter"); + return; + } + + hdd_sap_indicate_disconnect_for_sta(adapter); +} + +QDF_STATUS hdd_init_ap_mode(struct hdd_adapter *adapter, bool reinit) +{ + struct hdd_hostapd_state *phostapdBuf; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct sap_context *sap_context = NULL; + int ret; + enum dfs_mode acs_dfs_mode; + bool acs_with_more_param = 0; + uint8_t enable_sifs_burst = 0; + bool is_6g_sap_fd_enabled = 0; + + hdd_enter(); + + hdd_debug("SSR in progress: %d", reinit); + qdf_atomic_init(&adapter->session.ap.acs_in_progress); + + sap_context = hdd_hostapd_init_sap_session(adapter, reinit); + if (!sap_context) { + hdd_err("Invalid sap_ctx"); + goto error_release_vdev; + } + + if (!reinit) { + adapter->session.ap.sap_config.chan_freq = + hdd_ctx->acs_policy.acs_chan_freq; + acs_dfs_mode = hdd_ctx->acs_policy.acs_dfs_mode; + adapter->session.ap.sap_config.acs_dfs_mode = + wlan_hdd_get_dfs_mode(acs_dfs_mode); + } + + status = ucfg_mlme_get_acs_with_more_param(hdd_ctx->psoc, + &acs_with_more_param); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("can't get sap acs with more param, use def"); + + wlan_sap_set_acs_with_more_param(hdd_ctx->mac_handle, + acs_with_more_param); + + /* Allocate the Wireless Extensions state structure */ + phostapdBuf = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + + sme_set_curr_device_mode(hdd_ctx->mac_handle, adapter->device_mode); + /* Zero the memory. This zeros the profile structure. */ + memset(phostapdBuf, 0, sizeof(struct hdd_hostapd_state)); + + status = qdf_event_create(&phostapdBuf->qdf_event); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Hostapd HDD qdf event init failed!!"); + goto error_deinit_sap_session; + } + + status = qdf_event_create(&phostapdBuf->qdf_stop_bss_event); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Hostapd HDD stop bss event init failed!!"); + goto error_deinit_sap_session; + } + + status = qdf_event_create(&phostapdBuf->qdf_sta_disassoc_event); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Hostapd HDD sta disassoc event init failed!!"); + goto error_deinit_sap_session; + } + + status = qdf_event_create(&phostapdBuf->qdf_sta_eap_frm_done_event); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Hostapd HDD sta eap frm done event init failed!!"); + goto error_deinit_sap_session; + } + + /* Register as a wireless device */ + hdd_register_hostapd_wext(adapter->dev); + + /* Cache station count initialize to zero */ + qdf_atomic_init(&adapter->cache_sta_count); + + /* Initialize the data path module */ + hdd_softap_init_tx_rx(adapter); + + status = hdd_wmm_adapter_init(adapter); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("hdd_wmm_adapter_init() failed code: %08d [x%08x]", + status, status); + goto error_release_softap_tx_rx; + } + + set_bit(WMM_INIT_DONE, &adapter->event_flags); + + status = ucfg_get_enable_sifs_burst(hdd_ctx->psoc, &enable_sifs_burst); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to get sifs burst value, use default"); + + ret = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_BURST_ENABLE, + enable_sifs_burst, + PDEV_CMD); + if (0 != ret) + hdd_err("WMI_PDEV_PARAM_BURST_ENABLE set failed: %d", ret); + + + ucfg_mlme_is_6g_sap_fd_enabled(hdd_ctx->psoc, &is_6g_sap_fd_enabled); + hdd_debug("6g sap fd enabled %d", is_6g_sap_fd_enabled); + if (is_6g_sap_fd_enabled) + wlan_vdev_mlme_feat_ext_cap_set(adapter->vdev, + WLAN_VDEV_FEXT_FILS_DISC_6G_SAP); + + hdd_set_netdev_flags(adapter); + + if (!reinit) { + adapter->session.ap.sap_config.acs_cfg.acs_mode = false; + qdf_mem_zero(&adapter->session.ap.sap_config.acs_cfg, + sizeof(struct sap_acs_cfg)); + } + + sme_set_del_peers_ind_callback(hdd_ctx->mac_handle, + &hdd_indicate_peers_deleted); + /* rcpi info initialization */ + qdf_mem_zero(&adapter->rcpi, sizeof(adapter->rcpi)); + hdd_exit(); + + return status; + +error_release_softap_tx_rx: + hdd_unregister_wext(adapter->dev); + hdd_softap_deinit_tx_rx(adapter); +error_deinit_sap_session: + hdd_hostapd_deinit_sap_session(adapter); +error_release_vdev: + hdd_exit(); + return status; +} + +void hdd_deinit_ap_mode(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + bool rtnl_held) +{ + hdd_enter_dev(adapter->dev); + + if (test_bit(WMM_INIT_DONE, &adapter->event_flags)) { + hdd_wmm_adapter_close(adapter); + clear_bit(WMM_INIT_DONE, &adapter->event_flags); + } + qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); + if (qdf_atomic_read(&adapter->ch_switch_in_progress)) { + qdf_atomic_set(&adapter->ch_switch_in_progress, 0); + policy_mgr_set_chan_switch_complete_evt(hdd_ctx->psoc); + + /* Re-enable roaming on all connected STA vdev */ + wlan_hdd_enable_roaming(adapter, RSO_SAP_CHANNEL_CHANGE); + } + + hdd_softap_deinit_tx_rx(adapter); + /* + * if we are being called during driver unload, + * then the dev has already been invalidated. + * if we are being called at other times, then we can + * detach the wireless device handlers + */ + if (adapter->dev) { + if (rtnl_held) { + adapter->dev->wireless_handlers = NULL; + } else { + rtnl_lock(); + adapter->dev->wireless_handlers = NULL; + rtnl_unlock(); + } + } + if (hdd_hostapd_deinit_sap_session(adapter)) + hdd_err("Failed:hdd_hostapd_deinit_sap_session"); + + hdd_exit(); +} + +/** + * hdd_wlan_create_ap_dev() - create an AP-mode device + * @hdd_ctx: Global HDD context + * @mac_addr: MAC address to assign to the interface + * @name_assign_type: the name of assign type of the netdev + * @iface_name: User-visible name of the interface + * + * This function will allocate a Linux net_device and configuration it + * for an AP mode of operation. Note that the device is NOT actually + * registered with the kernel at this time. + * + * Return: A pointer to the private data portion of the net_device if + * the allocation and initialization was successful, NULL otherwise. + */ +struct hdd_adapter *hdd_wlan_create_ap_dev(struct hdd_context *hdd_ctx, + tSirMacAddr mac_addr, + unsigned char name_assign_type, + uint8_t *iface_name) +{ + struct net_device *dev; + struct hdd_adapter *adapter; + QDF_STATUS qdf_status; + + hdd_debug("iface_name = %s", iface_name); + + dev = alloc_netdev_mq(sizeof(struct hdd_adapter), iface_name, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS) + name_assign_type, +#endif + ether_setup, NUM_TX_QUEUES); + + if (!dev) + return NULL; + + adapter = netdev_priv(dev); + + /* Init the net_device structure */ + ether_setup(dev); + + /* Initialize the adapter context to zeros. */ + qdf_mem_zero(adapter, sizeof(struct hdd_adapter)); + adapter->dev = dev; + adapter->hdd_ctx = hdd_ctx; + adapter->magic = WLAN_HDD_ADAPTER_MAGIC; + adapter->vdev_id = WLAN_UMAC_VDEV_ID_MAX; + + hdd_debug("dev = %pK, adapter = %pK, concurrency_mode=0x%x", + dev, adapter, + (int)policy_mgr_get_concurrency_mode(hdd_ctx->psoc)); + + /* Init the net_device structure */ + strlcpy(dev->name, (const char *)iface_name, IFNAMSIZ); + + hdd_set_ap_ops(dev); + + dev->watchdog_timeo = HDD_TX_TIMEOUT; + dev->mtu = HDD_DEFAULT_MTU; + dev->tx_queue_len = HDD_NETDEV_TX_QUEUE_LEN; + + qdf_mem_copy(dev->dev_addr, mac_addr, sizeof(tSirMacAddr)); + qdf_mem_copy(adapter->mac_addr.bytes, mac_addr, sizeof(tSirMacAddr)); + + adapter->offloads_configured = false; + hdd_dev_setup_destructor(dev); + dev->ieee80211_ptr = &adapter->wdev; + adapter->wdev.wiphy = hdd_ctx->wiphy; + adapter->wdev.netdev = dev; + + qdf_status = qdf_event_create( + &adapter->qdf_session_open_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("failed to create session open QDF event!"); + free_netdev(adapter->dev); + return NULL; + } + + init_completion(&adapter->vdev_destroy_event); + + SET_NETDEV_DEV(dev, hdd_ctx->parent_dev); + spin_lock_init(&adapter->pause_map_lock); + adapter->start_time = adapter->last_time = qdf_system_ticks(); + + qdf_atomic_init(&adapter->ch_switch_in_progress); + + return adapter; +} + +/** + * wlan_hdd_rate_is_11g() - check if rate is 11g rate or not + * @rate: Rate to be checked + * + * Return: true if rate if 11g else false + */ +static bool wlan_hdd_rate_is_11g(u8 rate) +{ + static const u8 gRateArray[8] = {12, 18, 24, 36, 48, 72, + 96, 108}; /* actual rate * 2 */ + u8 i; + + for (i = 0; i < 8; i++) { + if (rate == gRateArray[i]) + return true; + } + return false; +} + +#ifdef QCA_HT_2040_COEX +/** + * wlan_hdd_get_sap_obss() - Get SAP OBSS enable config based on HT_CAPAB IE + * @adapter: Pointer to hostapd adapter + * + * Return: HT support channel width config value + */ +static bool wlan_hdd_get_sap_obss(struct hdd_adapter *adapter) +{ + uint32_t ret; + const uint8_t *ie; + uint8_t ht_cap_ie[DOT11F_IE_HTCAPS_MAX_LEN]; + tDot11fIEHTCaps dot11_ht_cap_ie = {0}; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_beacon_data *beacon = adapter->session.ap.beacon; + mac_handle_t mac_handle; + + mac_handle = hdd_ctx->mac_handle; + ie = wlan_get_ie_ptr_from_eid(WLAN_EID_HT_CAPABILITY, + beacon->tail, beacon->tail_len); + if (ie && ie[1]) { + qdf_mem_copy(ht_cap_ie, &ie[2], DOT11F_IE_HTCAPS_MAX_LEN); + ret = dot11f_unpack_ie_ht_caps(MAC_CONTEXT(mac_handle), + ht_cap_ie, ie[1], + &dot11_ht_cap_ie, false); + if (DOT11F_FAILED(ret)) { + hdd_err("unpack failed, ret: 0x%x", ret); + return false; + } + return dot11_ht_cap_ie.supportedChannelWidthSet; + } + + return false; +} +#else +static bool wlan_hdd_get_sap_obss(struct hdd_adapter *adapter) +{ + return false; +} +#endif +/** + * wlan_hdd_set_channel() - set channel in sap mode + * @wiphy: Pointer to wiphy structure + * @dev: Pointer to net_device structure + * @chandef: Pointer to channel definition structure + * @channel_type: Channel type + * + * Return: 0 for success non-zero for failure + */ +int wlan_hdd_set_channel(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + enum nl80211_channel_type channel_type) +{ + struct hdd_adapter *adapter = NULL; + uint32_t num_ch = 0; + int channel = 0; + int channel_seg2 = 0; + struct hdd_context *hdd_ctx; + int status; + mac_handle_t mac_handle; + struct sme_config_params *sme_config; + struct sap_config *sap_config; + + if (!dev) { + hdd_err("Called with dev = NULL"); + return -ENODEV; + } + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SET_CHANNEL, + adapter->vdev_id, channel_type); + + hdd_debug("Dev_mode %s(%d) freq %d, ch_bw %d ccfs1 %d ccfs2 %d", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode, chandef->chan->center_freq, + chandef->width, chandef->center_freq1, chandef->center_freq2); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + mac_handle = hdd_ctx->mac_handle; + + /* Check freq range */ + if ((wlan_reg_min_chan_freq() > + chandef->chan->center_freq) || + (wlan_reg_max_chan_freq() < chandef->chan->center_freq)) { + hdd_err("channel: %d is outside valid freq range", + chandef->chan->center_freq); + return -EINVAL; + } + + channel = ieee80211_frequency_to_channel(chandef->chan->center_freq); + + if (NL80211_CHAN_WIDTH_80P80 == chandef->width) { + if ((wlan_reg_min_chan_freq() > chandef->center_freq2) || + (wlan_reg_max_chan_freq() < chandef->center_freq2)) { + hdd_err("center_freq2: %d is outside valid freq range", + chandef->center_freq2); + return -EINVAL; + } + + if (chandef->center_freq2) + channel_seg2 = ieee80211_frequency_to_channel( + chandef->center_freq2); + else + hdd_err("Invalid center_freq2"); + } + + num_ch = CFG_VALID_CHANNEL_LIST_LEN; + + if (QDF_STATUS_SUCCESS != wlan_hdd_validate_operation_channel( + adapter, wlan_reg_chan_to_freq(hdd_ctx->pdev, channel))) { + hdd_err("Invalid Channel: %d", channel); + return -EINVAL; + } + + if ((adapter->device_mode == QDF_STA_MODE) || + (adapter->device_mode == QDF_P2P_CLIENT_MODE)) { + struct csr_roam_profile *roam_profile; + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (eConnectionState_IbssConnected == + sta_ctx->conn_info.conn_state) { + /* Link is up then return cant set channel */ + hdd_err("IBSS Associated, can't set the channel"); + return -EINVAL; + } + + roam_profile = hdd_roam_profile(adapter); + num_ch = roam_profile->ChannelInfo.numOfChannels = 1; + sta_ctx->conn_info.chan_freq = chandef->chan->center_freq; + roam_profile->ChannelInfo.freq_list = + &sta_ctx->conn_info.chan_freq; + } else if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + sap_config = &((WLAN_HDD_GET_AP_CTX_PTR(adapter))->sap_config); + sap_config->chan_freq = chandef->chan->center_freq; + sap_config->ch_params.center_freq_seg1 = channel_seg2; + sap_config->ch_params.center_freq_seg0 = + ieee80211_frequency_to_channel(chandef->center_freq1); + + if (QDF_SAP_MODE == adapter->device_mode) { + /* set channel to what hostapd configured */ + sap_config->chan_freq = chandef->chan->center_freq; + sap_config->ch_params.center_freq_seg1 = channel_seg2; + + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + + if (!sme_config) { + hdd_err("Unable to allocate memory for smeconfig!"); + return -ENOMEM; + } + sme_get_config_param(mac_handle, sme_config); + switch (channel_type) { + case NL80211_CHAN_HT20: + case NL80211_CHAN_NO_HT: + sme_config->csr_config.obssEnabled = false; + sap_config->sec_ch_freq = 0; + break; + case NL80211_CHAN_HT40MINUS: + sap_config->sec_ch_freq = + sap_config->chan_freq - 20; + break; + case NL80211_CHAN_HT40PLUS: + sap_config->sec_ch_freq = + sap_config->chan_freq + 20; + break; + default: + hdd_err("Error!!! Invalid HT20/40 mode !"); + qdf_mem_free(sme_config); + return -EINVAL; + } + sme_config->csr_config.obssEnabled = + wlan_hdd_get_sap_obss(adapter); + + sme_update_config(mac_handle, sme_config); + qdf_mem_free(sme_config); + } + } else { + hdd_err("Invalid device mode failed to set valid channel"); + return -EINVAL; + } + + return status; +} + +/** + * wlan_hdd_check_11gmode() - check for 11g mode + * @ie: Pointer to IE + * @require_ht: Pointer to require ht + * @require_vht: Pointer to require vht + * @pCheckRatesfor11g: Pointer to check rates for 11g mode + * @pSapHw_mode: SAP HW mode + * + * Check for 11g rate and set proper 11g only mode + * + * Return: none + */ +static void wlan_hdd_check_11gmode(const u8 *ie, u8 *require_ht, + u8 *require_vht, u8 *pCheckRatesfor11g, + eCsrPhyMode *pSapHw_mode) +{ + u8 i, num_rates = ie[0]; + + ie += 1; + for (i = 0; i < num_rates; i++) { + if (*pCheckRatesfor11g + && (true == wlan_hdd_rate_is_11g(ie[i] & RATE_MASK))) { + /* If rate set have 11g rate than change the mode + * to 11G + */ + *pSapHw_mode = eCSR_DOT11_MODE_11g; + if (ie[i] & BASIC_RATE_MASK) { + /* If we have 11g rate as basic rate, it + * means mode is 11g only mode. + */ + *pSapHw_mode = eCSR_DOT11_MODE_11g_ONLY; + *pCheckRatesfor11g = false; + } + } else { + if ((BASIC_RATE_MASK | + BSS_MEMBERSHIP_SELECTOR_HT_PHY) == ie[i]) + *require_ht = true; + else if ((BASIC_RATE_MASK | + BSS_MEMBERSHIP_SELECTOR_VHT_PHY) == ie[i]) + *require_vht = true; + } + } +} + +/** + * wlan_hdd_check_h2e() - check SAE/H2E require flag from support rate sets + * @rs: support rate or extended support rate set + * @require_h2e: pointer to store require h2e flag + * + * Return: none + */ +static void wlan_hdd_check_h2e(const tSirMacRateSet *rs, bool *require_h2e) +{ + uint8_t i; + + if (!rs || !require_h2e) + return; + + for (i = 0; i < rs->numRates; i++) { + if (rs->rate[i] == (BASIC_RATE_MASK | + BSS_MEMBERSHIP_SELECTOR_SAE_H2E)) + *require_h2e = true; + } +} + +#ifdef WLAN_FEATURE_11AX +/** + * wlan_hdd_add_extn_ie() - add extension IE + * @adapter: Pointer to hostapd adapter + * @genie: Pointer to ie to be added + * @total_ielen: Pointer to store total ie length + * @oui: Pointer to oui + * @oui_size: Size of oui + * + * Return: 0 for success non-zero for failure + */ +static int wlan_hdd_add_extn_ie(struct hdd_adapter *adapter, uint8_t *genie, + uint16_t *total_ielen, uint8_t *oui, + uint8_t oui_size) +{ + const uint8_t *ie; + uint16_t ielen = 0; + struct hdd_beacon_data *beacon = adapter->session.ap.beacon; + + ie = wlan_get_ext_ie_ptr_from_ext_id(oui, oui_size, + beacon->tail, + beacon->tail_len); + if (ie) { + ielen = ie[1] + 2; + if ((*total_ielen + ielen) <= MAX_GENIE_LEN) { + qdf_mem_copy(&genie[*total_ielen], ie, ielen); + } else { + hdd_err("**Ie Length is too big***"); + return -EINVAL; + } + *total_ielen += ielen; + } + return 0; +} +#endif + +/** + * wlan_hdd_add_hostapd_conf_vsie() - configure Vendor IE in sap mode + * @adapter: Pointer to hostapd adapter + * @genie: Pointer to Vendor IE + * @total_ielen: Pointer to store total ie length + * + * Return: none + */ +static void wlan_hdd_add_hostapd_conf_vsie(struct hdd_adapter *adapter, + uint8_t *genie, + uint16_t *total_ielen) +{ + struct hdd_beacon_data *beacon = adapter->session.ap.beacon; + int left = beacon->tail_len; + uint8_t *ptr = beacon->tail; + uint8_t elem_id, elem_len; + uint16_t ielen = 0; + bool skip_ie; + + if (!ptr || 0 == left) + return; + + while (left >= 2) { + elem_id = ptr[0]; + elem_len = ptr[1]; + left -= 2; + if (elem_len > left) { + hdd_err("**Invalid IEs eid: %d elem_len: %d left: %d**", + elem_id, elem_len, left); + return; + } + if (WLAN_ELEMID_VENDOR == elem_id) { + /* + * skipping the Vendor IE's which we don't want to + * include or it will be included by existing code. + */ + if (elem_len >= WPS_OUI_TYPE_SIZE && + (!qdf_mem_cmp(&ptr[2], WHITELIST_OUI_TYPE, + WPA_OUI_TYPE_SIZE) || + !qdf_mem_cmp(&ptr[2], BLACKLIST_OUI_TYPE, + WPA_OUI_TYPE_SIZE) || + !qdf_mem_cmp(&ptr[2], "\x00\x50\xf2\x02", + WPA_OUI_TYPE_SIZE) || + !qdf_mem_cmp(&ptr[2], WPA_OUI_TYPE, + WPA_OUI_TYPE_SIZE))) + skip_ie = true; + else + skip_ie = false; + + if (!skip_ie) { + ielen = ptr[1] + 2; + if ((*total_ielen + ielen) <= MAX_GENIE_LEN) { + qdf_mem_copy(&genie[*total_ielen], ptr, + ielen); + *total_ielen += ielen; + } else { + hdd_err("IE Length is too big IEs eid: %d elem_len: %d total_ie_lent: %d", + elem_id, elem_len, *total_ielen); + } + } + } + + left -= elem_len; + ptr += (elem_len + 2); + } +} + +/** + * wlan_hdd_add_extra_ie() - add extra ies in beacon + * @adapter: Pointer to hostapd adapter + * @genie: Pointer to extra ie + * @total_ielen: Pointer to store total ie length + * @temp_ie_id: ID of extra ie + * + * Return: none + */ +static void wlan_hdd_add_extra_ie(struct hdd_adapter *adapter, + uint8_t *genie, uint16_t *total_ielen, + uint8_t temp_ie_id) +{ + struct hdd_beacon_data *beacon = adapter->session.ap.beacon; + int left = beacon->tail_len; + uint8_t *ptr = beacon->tail; + uint8_t elem_id, elem_len; + uint16_t ielen = 0; + + if (!ptr || 0 == left) + return; + + while (left >= 2) { + elem_id = ptr[0]; + elem_len = ptr[1]; + left -= 2; + if (elem_len > left) { + hdd_err("**Invalid IEs eid: %d elem_len: %d left: %d**", + elem_id, elem_len, left); + return; + } + + if (temp_ie_id == elem_id) { + ielen = ptr[1] + 2; + if ((*total_ielen + ielen) <= MAX_GENIE_LEN) { + qdf_mem_copy(&genie[*total_ielen], ptr, ielen); + *total_ielen += ielen; + } else { + hdd_err("IE Length is too big IEs eid: %d elem_len: %d total_ie_len: %d", + elem_id, elem_len, *total_ielen); + } + } + + left -= elem_len; + ptr += (elem_len + 2); + } +} + +/** + * wlan_hdd_cfg80211_alloc_new_beacon() - alloc beacon in ap mode + * @adapter: Pointer to hostapd adapter + * @out_beacon: Location to store newly allocated beacon data + * @params: Pointer to beacon parameters + * @dtim_period: DTIM period + * + * Return: 0 for success non-zero for failure + */ +static int +wlan_hdd_cfg80211_alloc_new_beacon(struct hdd_adapter *adapter, + struct hdd_beacon_data **out_beacon, + struct cfg80211_beacon_data *params, + int dtim_period) +{ + int size; + struct hdd_beacon_data *beacon = NULL; + struct hdd_beacon_data *old = NULL; + int head_len, tail_len, proberesp_ies_len, assocresp_ies_len; + const u8 *head, *tail, *proberesp_ies, *assocresp_ies; + + hdd_enter(); + if (params->head && !params->head_len) { + hdd_err("head_len is NULL"); + return -EINVAL; + } + + old = adapter->session.ap.beacon; + + if (!params->head && !old) { + hdd_err("session: %d old and new heads points to NULL", + adapter->vdev_id); + return -EINVAL; + } + + if (params->head) { + head_len = params->head_len; + head = params->head; + } else { + head_len = old->head_len; + head = old->head; + } + + if (params->tail || !old) { + tail_len = params->tail_len; + tail = params->tail; + } else { + tail_len = old->tail_len; + tail = old->tail; + } + + if (params->proberesp_ies || !old) { + proberesp_ies_len = params->proberesp_ies_len; + proberesp_ies = params->proberesp_ies; + } else { + proberesp_ies_len = old->proberesp_ies_len; + proberesp_ies = old->proberesp_ies; + } + + if (params->assocresp_ies || !old) { + assocresp_ies_len = params->assocresp_ies_len; + assocresp_ies = params->assocresp_ies; + } else { + assocresp_ies_len = old->assocresp_ies_len; + assocresp_ies = old->assocresp_ies; + } + + size = sizeof(struct hdd_beacon_data) + head_len + tail_len + + proberesp_ies_len + assocresp_ies_len; + + beacon = qdf_mem_malloc(size); + + if (!beacon) { + hdd_err("Mem allocation for beacon failed"); + return -ENOMEM; + } + if (dtim_period) + beacon->dtim_period = dtim_period; + else if (old) + beacon->dtim_period = old->dtim_period; + /* ----------------------------------------------- + * | head | tail | proberesp_ies | assocresp_ies | + * ----------------------------------------------- + */ + beacon->head = ((u8 *) beacon) + sizeof(struct hdd_beacon_data); + beacon->tail = beacon->head + head_len; + beacon->proberesp_ies = beacon->tail + tail_len; + beacon->assocresp_ies = beacon->proberesp_ies + proberesp_ies_len; + + beacon->head_len = head_len; + beacon->tail_len = tail_len; + beacon->proberesp_ies_len = proberesp_ies_len; + beacon->assocresp_ies_len = assocresp_ies_len; + + if (head && head_len) + memcpy(beacon->head, head, head_len); + if (tail && tail_len) + memcpy(beacon->tail, tail, tail_len); + if (proberesp_ies && proberesp_ies_len) + memcpy(beacon->proberesp_ies, proberesp_ies, proberesp_ies_len); + if (assocresp_ies && assocresp_ies_len) + memcpy(beacon->assocresp_ies, assocresp_ies, assocresp_ies_len); + + *out_beacon = beacon; + + adapter->session.ap.beacon = NULL; + qdf_mem_free(old); + + return 0; + +} + +#ifdef QCA_HT_2040_COEX +static void wlan_hdd_add_sap_obss_scan_ie( + struct hdd_adapter *hostapd_adapter, uint8_t *ie_buf, uint16_t *ie_len) +{ + if (QDF_SAP_MODE == hostapd_adapter->device_mode) { + if (wlan_hdd_get_sap_obss(hostapd_adapter)) + wlan_hdd_add_extra_ie(hostapd_adapter, ie_buf, ie_len, + WLAN_EID_OVERLAP_BSS_SCAN_PARAM); + } +} +#else +static void wlan_hdd_add_sap_obss_scan_ie( + struct hdd_adapter *hostapd_adapter, uint8_t *ie_buf, uint16_t *ie_len) +{ +} +#endif + +/** + * wlan_hdd_cfg80211_update_apies() - update ap mode 11ax ies + * @adapter: Pointer to hostapd adapter + * @genie: generic IE buffer + * @total_ielen: out param to update total ielen + * + * Return: 0 for success non-zero for failure + */ + +#ifdef WLAN_FEATURE_11AX +static int hdd_update_11ax_apies(struct hdd_adapter *adapter, + uint8_t *genie, uint16_t *total_ielen) +{ + if (wlan_hdd_add_extn_ie(adapter, genie, total_ielen, + HE_CAP_OUI_TYPE, HE_CAP_OUI_SIZE)) { + hdd_err("Adding HE Cap ie failed"); + return -EINVAL; + } + + if (wlan_hdd_add_extn_ie(adapter, genie, total_ielen, + HE_OP_OUI_TYPE, HE_OP_OUI_SIZE)) { + hdd_err("Adding HE Op ie failed"); + return -EINVAL; + } + + return 0; +} +#else +static int hdd_update_11ax_apies(struct hdd_adapter *adapter, + uint8_t *genie, uint16_t *total_ielen) +{ + return 0; +} +#endif + +/** + * wlan_hdd_cfg80211_update_apies() - update ap mode ies + * @adapter: Pointer to hostapd adapter + * + * Return: 0 for success non-zero for failure + */ +int wlan_hdd_cfg80211_update_apies(struct hdd_adapter *adapter) +{ + uint8_t *genie; + uint16_t total_ielen = 0; + int ret = 0; + struct sap_config *config; + tSirUpdateIE update_ie; + struct hdd_beacon_data *beacon = NULL; + uint16_t proberesp_ies_len; + uint8_t *proberesp_ies = NULL; + mac_handle_t mac_handle; + + config = &adapter->session.ap.sap_config; + beacon = adapter->session.ap.beacon; + if (!beacon) { + hdd_err("Beacon is NULL !"); + return -EINVAL; + } + + genie = qdf_mem_malloc(MAX_GENIE_LEN); + + if (!genie) + return -ENOMEM; + + mac_handle = adapter->hdd_ctx->mac_handle; + + /* Extract and add the extended capabilities and interworking IE */ + wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, + WLAN_EID_EXT_CAPABILITY); + + wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, + WLAN_EID_INTERWORKING); + wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, + WLAN_EID_ADVERTISEMENT_PROTOCOL); + + wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, WLAN_ELEMID_RSNXE); +#ifdef FEATURE_WLAN_WAPI + if (QDF_SAP_MODE == adapter->device_mode) { + wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, + WLAN_ELEMID_WAPI); + } +#endif + + wlan_hdd_add_hostapd_conf_vsie(adapter, genie, + &total_ielen); + + ret = hdd_update_11ax_apies(adapter, genie, &total_ielen); + if (ret) + goto done; + + wlan_hdd_add_sap_obss_scan_ie(adapter, genie, &total_ielen); + + qdf_copy_macaddr(&update_ie.bssid, &adapter->mac_addr); + update_ie.vdev_id = adapter->vdev_id; + + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + update_ie.ieBufferlength = total_ielen; + update_ie.pAdditionIEBuffer = genie; + update_ie.append = false; + update_ie.notify = true; + if (sme_update_add_ie(mac_handle, + &update_ie, + eUPDATE_IE_PROBE_BCN) == + QDF_STATUS_E_FAILURE) { + hdd_err("Could not pass on Add Ie probe beacon data"); + ret = -EINVAL; + goto done; + } + wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_PROBE_BCN); + } else { + wlansap_update_sap_config_add_ie(config, + genie, + total_ielen, + eUPDATE_IE_PROBE_BCN); + } + + /* Added for Probe Response IE */ + proberesp_ies = qdf_mem_malloc(beacon->proberesp_ies_len + + MAX_GENIE_LEN); + if (!proberesp_ies) { + hdd_err("mem alloc failed for probe resp ies, size: %d", + beacon->proberesp_ies_len + MAX_GENIE_LEN); + ret = -EINVAL; + goto done; + } + qdf_mem_copy(proberesp_ies, beacon->proberesp_ies, + beacon->proberesp_ies_len); + proberesp_ies_len = beacon->proberesp_ies_len; + + wlan_hdd_add_sap_obss_scan_ie(adapter, proberesp_ies, + &proberesp_ies_len); + wlan_hdd_add_extra_ie(adapter, proberesp_ies, &proberesp_ies_len, + WLAN_ELEMID_RSNXE); + + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + update_ie.ieBufferlength = proberesp_ies_len; + update_ie.pAdditionIEBuffer = proberesp_ies; + update_ie.append = false; + update_ie.notify = false; + if (sme_update_add_ie(mac_handle, + &update_ie, + eUPDATE_IE_PROBE_RESP) == + QDF_STATUS_E_FAILURE) { + hdd_err("Could not pass on PROBE_RESP add Ie data"); + ret = -EINVAL; + goto done; + } + wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_PROBE_RESP); + } else { + wlansap_update_sap_config_add_ie(config, + proberesp_ies, + proberesp_ies_len, + eUPDATE_IE_PROBE_RESP); + } + + /* Assoc resp Add ie Data */ + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + update_ie.ieBufferlength = beacon->assocresp_ies_len; + update_ie.pAdditionIEBuffer = (uint8_t *) beacon->assocresp_ies; + update_ie.append = false; + update_ie.notify = false; + if (sme_update_add_ie(mac_handle, + &update_ie, + eUPDATE_IE_ASSOC_RESP) == + QDF_STATUS_E_FAILURE) { + hdd_err("Could not pass on Add Ie Assoc Response data"); + ret = -EINVAL; + goto done; + } + wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_ASSOC_RESP); + } else { + wlansap_update_sap_config_add_ie(config, + beacon->assocresp_ies, + beacon->assocresp_ies_len, + eUPDATE_IE_ASSOC_RESP); + } + +done: + qdf_mem_free(genie); + qdf_mem_free(proberesp_ies); + return ret; +} + +/** + * wlan_hdd_set_sap_hwmode() - set sap hw mode + * @adapter: Pointer to hostapd adapter + * + * Return: none + */ +static void wlan_hdd_set_sap_hwmode(struct hdd_adapter *adapter) +{ + struct sap_config *config = &adapter->session.ap.sap_config; + struct hdd_beacon_data *beacon = adapter->session.ap.beacon; + struct ieee80211_mgmt *mgmt_frame = + (struct ieee80211_mgmt *)beacon->head; + u8 checkRatesfor11g = true; + u8 require_ht = false, require_vht = false; + const u8 *ie; + ssize_t size; + + config->SapHw_mode = eCSR_DOT11_MODE_11b; + + size = beacon->head_len - sizeof(mgmt_frame->u.beacon) - + (sizeof(*mgmt_frame) - sizeof(mgmt_frame->u)); + + if (size <= 0) { + hdd_err_rl("Invalid length: %zu", size); + return; + } + + ie = wlan_get_ie_ptr_from_eid(WLAN_EID_SUPP_RATES, + &mgmt_frame->u.beacon.variable[0], + size); + if (ie) { + ie += 1; + wlan_hdd_check_11gmode(ie, &require_ht, &require_vht, + &checkRatesfor11g, &config->SapHw_mode); + } + + ie = wlan_get_ie_ptr_from_eid(WLAN_EID_EXT_SUPP_RATES, + beacon->tail, beacon->tail_len); + if (ie) { + ie += 1; + wlan_hdd_check_11gmode(ie, &require_ht, &require_vht, + &checkRatesfor11g, &config->SapHw_mode); + } + + if (WLAN_REG_IS_5GHZ_CH_FREQ(config->chan_freq)) + config->SapHw_mode = eCSR_DOT11_MODE_11a; + + ie = wlan_get_ie_ptr_from_eid(WLAN_EID_HT_CAPABILITY, + beacon->tail, beacon->tail_len); + if (ie) { + config->SapHw_mode = eCSR_DOT11_MODE_11n; + if (require_ht) + config->SapHw_mode = eCSR_DOT11_MODE_11n_ONLY; + } + + ie = wlan_get_ie_ptr_from_eid(WLAN_EID_VHT_CAPABILITY, + beacon->tail, beacon->tail_len); + if (ie) { + config->SapHw_mode = eCSR_DOT11_MODE_11ac; + if (require_vht) + config->SapHw_mode = eCSR_DOT11_MODE_11ac_ONLY; + } + + wlan_hdd_check_11ax_support(beacon, config); + + hdd_debug("SAP hw_mode: %d", config->SapHw_mode); +} + +/** + * wlan_hdd_config_acs() - config ACS needed parameters + * @hdd_ctx: HDD context + * @adapter: Adapter pointer + * + * This function get ACS related INI parameters and populated + * sap config and smeConfig for ACS needed configurations. + * + * Return: The QDF_STATUS code associated with performing the operation. + */ +QDF_STATUS wlan_hdd_config_acs(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct sap_config *sap_config; + struct hdd_config *ini_config; + mac_handle_t mac_handle; + uint8_t is_overlap_enable = 0; + QDF_STATUS status; + + mac_handle = hdd_ctx->mac_handle; + sap_config = &adapter->session.ap.sap_config; + ini_config = hdd_ctx->config; + + status = ucfg_policy_mgr_get_enable_overlap_chnl(hdd_ctx->psoc, + &is_overlap_enable); + if (status != QDF_STATUS_SUCCESS) + hdd_err("can't get overlap channel INI value, using default"); + sap_config->enOverLapCh = !!is_overlap_enable; + +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE + hdd_debug("HDD_ACS_SKIP_STATUS = %d", hdd_ctx->skip_acs_scan_status); + if (hdd_ctx->skip_acs_scan_status == eSAP_SKIP_ACS_SCAN) { + struct hdd_adapter *con_sap_adapter; + struct sap_config *con_sap_config = NULL; + + con_sap_adapter = hdd_get_con_sap_adapter(adapter, false); + + if (con_sap_adapter) + con_sap_config = + &con_sap_adapter->session.ap.sap_config; + + sap_config->acs_cfg.skip_scan_status = eSAP_DO_NEW_ACS_SCAN; + + if (con_sap_config && + con_sap_config->acs_cfg.acs_mode == true && + hdd_ctx->skip_acs_scan_status == eSAP_SKIP_ACS_SCAN && + con_sap_config->acs_cfg.hw_mode == + sap_config->acs_cfg.hw_mode) { + uint32_t con_sap_st_ch_freq, con_sap_end_ch_freq; + uint32_t cur_sap_st_ch_freq, cur_sap_end_ch_freq; + uint32_t bandStartChannel, bandEndChannel; + + con_sap_st_ch_freq = + con_sap_config->acs_cfg.start_ch_freq; + con_sap_end_ch_freq = + con_sap_config->acs_cfg.end_ch_freq; + cur_sap_st_ch_freq = + sap_config->acs_cfg.start_ch_freq; + cur_sap_end_ch_freq = + sap_config->acs_cfg.end_ch_freq; + + wlansap_extend_to_acs_range( + mac_handle, &cur_sap_st_ch_freq, + &cur_sap_end_ch_freq, + &bandStartChannel, &bandEndChannel); + + wlansap_extend_to_acs_range( + mac_handle, &con_sap_st_ch_freq, + &con_sap_end_ch_freq, + &bandStartChannel, &bandEndChannel); + + if (con_sap_st_ch_freq <= cur_sap_st_ch_freq && + con_sap_end_ch_freq >= cur_sap_end_ch_freq) { + sap_config->acs_cfg.skip_scan_status = + eSAP_SKIP_ACS_SCAN; + + } else if (con_sap_st_ch_freq >= cur_sap_st_ch_freq && + con_sap_end_ch_freq >= + cur_sap_end_ch_freq) { + sap_config->acs_cfg.skip_scan_status = + eSAP_DO_PAR_ACS_SCAN; + + sap_config->acs_cfg.skip_scan_range1_stch = + cur_sap_st_ch_freq; + sap_config->acs_cfg.skip_scan_range1_endch = + con_sap_st_ch_freq - 5; + sap_config->acs_cfg.skip_scan_range2_stch = + 0; + sap_config->acs_cfg.skip_scan_range2_endch = + 0; + + } else if (con_sap_st_ch_freq <= cur_sap_st_ch_freq && + con_sap_end_ch_freq <= + cur_sap_end_ch_freq) { + sap_config->acs_cfg.skip_scan_status = + eSAP_DO_PAR_ACS_SCAN; + + sap_config->acs_cfg.skip_scan_range1_stch = + con_sap_end_ch_freq + 5; + sap_config->acs_cfg.skip_scan_range1_endch = + cur_sap_end_ch_freq; + sap_config->acs_cfg.skip_scan_range2_stch = + 0; + sap_config->acs_cfg.skip_scan_range2_endch = + 0; + + } else if (con_sap_st_ch_freq >= cur_sap_st_ch_freq && + con_sap_end_ch_freq <= + cur_sap_end_ch_freq) { + sap_config->acs_cfg.skip_scan_status = + eSAP_DO_PAR_ACS_SCAN; + + sap_config->acs_cfg.skip_scan_range1_stch = + cur_sap_st_ch_freq; + sap_config->acs_cfg.skip_scan_range1_endch = + con_sap_st_ch_freq - 5; + sap_config->acs_cfg.skip_scan_range2_stch = + con_sap_end_ch_freq; + sap_config->acs_cfg.skip_scan_range2_endch = + cur_sap_end_ch_freq + 5; + + } else + sap_config->acs_cfg.skip_scan_status = + eSAP_DO_NEW_ACS_SCAN; + + + hdd_debug("SecAP ACS Skip=%d, ACS CH RANGE=%d-%d, %d-%d", + sap_config->acs_cfg.skip_scan_status, + sap_config->acs_cfg.skip_scan_range1_stch, + sap_config->acs_cfg.skip_scan_range1_endch, + sap_config->acs_cfg.skip_scan_range2_stch, + sap_config->acs_cfg.skip_scan_range2_endch); + } + } +#endif + + return QDF_STATUS_SUCCESS; +} + +/** + * wlan_hdd_sap_p2p_11ac_overrides: API to overwrite 11ac config in case of + * SAP or p2p go + * @ap_adapter: pointer to adapter + * + * This function overrides SAP / P2P Go configuration based on driver INI + * parameters for 11AC override and ACS. This overrides are done to support + * android legacy configuration method. + * + * NOTE: Non android platform supports concurrency and these overrides shall + * not be used. Also future driver based overrides shall be consolidated in this + * function only. Avoid random overrides in other location based on ini. + * + * Return: 0 for Success or Negative error codes. + */ +static int wlan_hdd_sap_p2p_11ac_overrides(struct hdd_adapter *ap_adapter) +{ + struct sap_config *sap_cfg = &ap_adapter->session.ap.sap_config; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); + uint8_t ch_width; + uint8_t sub_20_chan_width; + QDF_STATUS status; + bool sap_force_11n_for_11ac = 0; + bool go_force_11n_for_11ac = 0; + uint32_t channel_bonding_mode; + bool go_11ac_override = 0; + bool sap_11ac_override = 0; + + ucfg_mlme_get_sap_force_11n_for_11ac(hdd_ctx->psoc, + &sap_force_11n_for_11ac); + ucfg_mlme_get_go_force_11n_for_11ac(hdd_ctx->psoc, + &go_force_11n_for_11ac); + + /* Fixed channel 11AC override: + * 11AC override in qcacld is introduced for following reasons: + * 1. P2P GO also follows start_bss and since p2p GO could not be + * configured to setup VHT channel width in wpa_supplicant + * 2. Android UI does not provide advanced configuration options for SAP + * + * Default override enabled (for android). MDM shall disable this in ini + */ + /* + * sub_20 MHz channel width is incompatible with 11AC rates, hence do + * not allow 11AC rates or more than 20 MHz channel width when + * enable_sub_20_channel_width is non zero + */ + status = ucfg_mlme_get_sub_20_chan_width(hdd_ctx->psoc, + &sub_20_chan_width); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get sub_20_chan_width config"); + return -EIO; + } + + ucfg_mlme_is_go_11ac_override(hdd_ctx->psoc, + &go_11ac_override); + ucfg_mlme_is_sap_11ac_override(hdd_ctx->psoc, + &sap_11ac_override); + + if (!sub_20_chan_width && + (sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11n || + sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11ac || + sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11ac_ONLY || + sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11ax || + sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11ax_ONLY) && + ((ap_adapter->device_mode == QDF_SAP_MODE && + !sap_force_11n_for_11ac && + sap_11ac_override) || + (ap_adapter->device_mode == QDF_P2P_GO_MODE && + !go_force_11n_for_11ac && + go_11ac_override))) { + hdd_debug("** Driver force 11AC override for SAP/Go **"); + + /* 11n only shall not be overridden since it may be on purpose*/ + if (sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11n) + sap_cfg->SapHw_mode = eCSR_DOT11_MODE_11ac; + + if (!WLAN_REG_IS_24GHZ_CH_FREQ(sap_cfg->chan_freq)) { + status = + ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc, + &ch_width); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to set channel_width"); + sap_cfg->ch_width_orig = ch_width; + } else { + /* + * Allow 40 Mhz in 2.4 Ghz only if indicated by + * supplicant after OBSS scan and if 2.4 Ghz channel + * bonding is set in INI + */ + ucfg_mlme_get_channel_bonding_24ghz( + hdd_ctx->psoc, &channel_bonding_mode); + if (sap_cfg->ch_width_orig >= eHT_CHANNEL_WIDTH_40MHZ && + channel_bonding_mode) + sap_cfg->ch_width_orig = + eHT_CHANNEL_WIDTH_40MHZ; + else + sap_cfg->ch_width_orig = + eHT_CHANNEL_WIDTH_20MHZ; + } + } + + return 0; +} + +/** + * wlan_hdd_setup_driver_overrides : Overrides SAP / P2P GO Params + * @adapter: pointer to adapter struct + * + * This function overrides SAP / P2P Go configuration based on driver INI + * parameters for 11AC override and ACS. These overrides are done to support + * android legacy configuration method. + * + * NOTE: Non android platform supports concurrency and these overrides shall + * not be used. Also future driver based overrides shall be consolidated in this + * function only. Avoid random overrides in other location based on ini. + * + * Return: 0 for Success or Negative error codes. + */ +static int wlan_hdd_setup_driver_overrides(struct hdd_adapter *ap_adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); + QDF_STATUS qdf_status; + bool is_vendor_acs_support = + cfg_default(CFG_USER_AUTO_CHANNEL_SELECTION); + + qdf_status = ucfg_mlme_get_vendor_acs_support(hdd_ctx->psoc, + &is_vendor_acs_support); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("get_vendor_acs_support failed, set default"); + + if (!is_vendor_acs_support) + return wlan_hdd_sap_p2p_11ac_overrides(ap_adapter); + else + return 0; +} + +void +hdd_check_and_disconnect_sta_on_invalid_channel(struct hdd_context *hdd_ctx, + tSirMacReasonCodes reason) +{ + struct hdd_adapter *sta_adapter; + uint32_t sta_chan_freq; + + sta_chan_freq = hdd_get_operating_chan_freq(hdd_ctx, QDF_STA_MODE); + if (!sta_chan_freq) { + hdd_err("STA not connected"); + return; + } + + hdd_err("STA connected on %d", sta_chan_freq); + + if (sme_is_channel_valid(hdd_ctx->mac_handle, sta_chan_freq)) { + hdd_err("STA connected on %d and it is valid", sta_chan_freq); + return; + } + + sta_adapter = hdd_get_adapter(hdd_ctx, QDF_STA_MODE); + + if (!sta_adapter) { + hdd_err("STA adapter does not exist"); + return; + } + + hdd_err("chan %d not valid, issue disconnect", sta_chan_freq); + /* Issue Disconnect request */ + wlan_hdd_disconnect(sta_adapter, eCSR_DISCONNECT_REASON_DEAUTH, reason); +} + +#ifdef DISABLE_CHANNEL_LIST +/** + * wlan_hdd_get_wiphy_channel() - Get wiphy channel + * @wiphy: Pointer to wiphy structure + * @freq: Frequency of the channel for which the wiphy hw value is required + * + * Return: wiphy channel for valid frequency else return NULL + */ +static struct ieee80211_channel *wlan_hdd_get_wiphy_channel( + struct wiphy *wiphy, + uint32_t freq) +{ + uint32_t band_num, channel_num; + struct ieee80211_channel *wiphy_channel = NULL; + + for (band_num = 0; band_num < HDD_NUM_NL80211_BANDS; band_num++) { + for (channel_num = 0; channel_num < + wiphy->bands[band_num]->n_channels; + channel_num++) { + wiphy_channel = &(wiphy->bands[band_num]-> + channels[channel_num]); + if (wiphy_channel->center_freq == freq) + return wiphy_channel; + } + } + return wiphy_channel; +} + +int wlan_hdd_restore_channels(struct hdd_context *hdd_ctx) +{ + struct hdd_cache_channels *cache_chann; + struct wiphy *wiphy; + int freq, status, rf_channel; + int i; + struct ieee80211_channel *wiphy_channel = NULL; + + hdd_enter(); + + if (!hdd_ctx) { + hdd_err("HDD Context is NULL"); + return -EINVAL; + } + + wiphy = hdd_ctx->wiphy; + if (!wiphy) { + hdd_err("Wiphy is NULL"); + return -EINVAL; + } + + qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); + + cache_chann = hdd_ctx->original_channels; + + if (!cache_chann || !cache_chann->num_channels) { + qdf_mutex_release(&hdd_ctx->cache_channel_lock); + hdd_nofl_debug("channel list is NULL or num channels are zero"); + return -EINVAL; + } + + for (i = 0; i < cache_chann->num_channels; i++) { + freq = wlan_reg_chan_to_freq( + hdd_ctx->pdev, + cache_chann->channel_info[i].channel_num); + if (!freq) + continue; + + wiphy_channel = wlan_hdd_get_wiphy_channel(wiphy, freq); + if (!wiphy_channel) + continue; + rf_channel = wiphy_channel->hw_value; + /* + * Restore the orginal states of the channels + * only if we have cached non zero values + */ + wiphy_channel->flags = + cache_chann->channel_info[i].wiphy_status; + + hdd_debug("Restore channel %d reg_stat %d wiphy_stat 0x%x", + cache_chann->channel_info[i].channel_num, + cache_chann->channel_info[i].reg_status, + wiphy_channel->flags); + } + + qdf_mutex_release(&hdd_ctx->cache_channel_lock); + + ucfg_reg_restore_cached_channels(hdd_ctx->pdev); + status = sme_update_channel_list(hdd_ctx->mac_handle); + if (status) + hdd_err("Can't Restore channel list"); + else + /* + * Free the cache channels when the + * disabled channels are restored + */ + wlan_hdd_free_cache_channels(hdd_ctx); + hdd_exit(); + return 0; +} + +int wlan_hdd_disable_channels(struct hdd_context *hdd_ctx) +{ + struct hdd_cache_channels *cache_chann; + struct wiphy *wiphy; + int freq, status, rf_channel; + int i; + struct ieee80211_channel *wiphy_channel = NULL; + + hdd_enter(); + + if (!hdd_ctx) { + hdd_err("HDD Context is NULL"); + return -EINVAL; + } + + wiphy = hdd_ctx->wiphy; + if (!wiphy) { + hdd_err("Wiphy is NULL"); + return -EINVAL; + } + + qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); + cache_chann = hdd_ctx->original_channels; + + if (!cache_chann || !cache_chann->num_channels) { + qdf_mutex_release(&hdd_ctx->cache_channel_lock); + hdd_err("channel list is NULL or num channels are zero"); + return -EINVAL; + } + + for (i = 0; i < cache_chann->num_channels; i++) { + freq = wlan_reg_chan_to_freq(hdd_ctx->pdev, + cache_chann-> + channel_info[i].channel_num); + if (!freq) + continue; + wiphy_channel = wlan_hdd_get_wiphy_channel(wiphy, freq); + if (!wiphy_channel) + continue; + rf_channel = wiphy_channel->hw_value; + /* + * Cache the current states of + * the channels + */ + cache_chann->channel_info[i].reg_status = + wlan_reg_get_channel_state( + hdd_ctx->pdev, + rf_channel); + cache_chann->channel_info[i].wiphy_status = + wiphy_channel->flags; + hdd_debug("Disable channel %d reg_stat %d wiphy_stat 0x%x", + cache_chann->channel_info[i].channel_num, + cache_chann->channel_info[i].reg_status, + wiphy_channel->flags); + + wiphy_channel->flags |= IEEE80211_CHAN_DISABLED; + } + + qdf_mutex_release(&hdd_ctx->cache_channel_lock); + ucfg_reg_disable_cached_channels(hdd_ctx->pdev); + status = sme_update_channel_list(hdd_ctx->mac_handle); + + hdd_exit(); + return status; +} +#else +int wlan_hdd_disable_channels(struct hdd_context *hdd_ctx) +{ + return 0; +} + +int wlan_hdd_restore_channels(struct hdd_context *hdd_ctx) +{ + return 0; +} +#endif + +#ifdef DHCP_SERVER_OFFLOAD +static void wlan_hdd_set_dhcp_server_offload(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct dhcp_offload_info_params dhcp_srv_info; + uint8_t num_entries = 0; + uint8_t *srv_ip; + uint8_t num; + uint32_t temp; + uint32_t dhcp_max_num_clients; + mac_handle_t mac_handle; + QDF_STATUS status; + + if (!hdd_ctx->config->dhcp_server_ip.is_dhcp_server_ip_valid) + return; + + srv_ip = hdd_ctx->config->dhcp_server_ip.dhcp_server_ip; + dhcp_srv_info.vdev_id = adapter->vdev_id; + dhcp_srv_info.dhcp_offload_enabled = true; + + status = ucfg_fwol_get_dhcp_max_num_clients(hdd_ctx->psoc, + &dhcp_max_num_clients); + if (QDF_IS_STATUS_ERROR(status)) + return; + + dhcp_srv_info.dhcp_client_num = dhcp_max_num_clients; + + if ((srv_ip[0] >= 224) && (srv_ip[0] <= 239)) { + hdd_err("Invalid IP address (%d)! It could NOT be multicast IP address!", + srv_ip[0]); + return; + } + + if (srv_ip[IPADDR_NUM_ENTRIES - 1] >= 100) { + hdd_err("Invalid IP address (%d)! The last field must be less than 100!", + srv_ip[IPADDR_NUM_ENTRIES - 1]); + return; + } + + dhcp_srv_info.dhcp_srv_addr = 0; + for (num = 0; num < num_entries; num++) { + temp = srv_ip[num]; + dhcp_srv_info.dhcp_srv_addr |= (temp << (8 * num)); + } + + mac_handle = hdd_ctx->mac_handle; + status = sme_set_dhcp_srv_offload(mac_handle, &dhcp_srv_info); + if (QDF_IS_STATUS_SUCCESS(status)) + hdd_debug("enable DHCP Server offload successfully!"); + else + hdd_err("sme_set_dhcp_srv_offload fail!"); +} + +/** + * wlan_hdd_dhcp_offload_enable: Enable DHCP offload + * @hdd_ctx: HDD context handler + * @adapter: Adapter pointer + * + * Enables the DHCP Offload feature in firmware if it has been configured. + * + * Return: None + */ +static void wlan_hdd_dhcp_offload_enable(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + bool enable_dhcp_server_offload; + QDF_STATUS status; + + status = ucfg_fwol_get_enable_dhcp_server_offload( + hdd_ctx->psoc, + &enable_dhcp_server_offload); + if (QDF_IS_STATUS_ERROR(status)) + return; + + if (enable_dhcp_server_offload) + wlan_hdd_set_dhcp_server_offload(adapter); +} +#else +static void wlan_hdd_dhcp_offload_enable(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ +} +#endif /* DHCP_SERVER_OFFLOAD */ + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +static void wlan_hdd_set_sap_mcc_chnl_avoid(struct hdd_context *hdd_ctx) +{ + uint8_t sap_mcc_avoid = 0; + QDF_STATUS status; + + status = ucfg_mlme_get_sap_mcc_chnl_avoid(hdd_ctx->psoc, + &sap_mcc_avoid); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("can't get sap mcc chnl avoid, use def"); + wlan_sap_set_channel_avoidance(hdd_ctx->mac_handle, sap_mcc_avoid); +} +#else +static void wlan_hdd_set_sap_mcc_chnl_avoid(struct hdd_context *hdd_ctx) +{ +} +#endif + +/** + * wlan_hdd_cfg80211_start_bss() - start bss + * @adapter: Pointer to hostapd adapter + * @params: Pointer to start bss beacon parameters + * @ssid: Pointer ssid + * @ssid_len: Length of ssid + * @hidden_ssid: Hidden SSID parameter + * @check_for_concurrency: Flag to indicate if check for concurrency is needed + * + * Return: 0 for success non-zero for failure + */ +int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter, + struct cfg80211_beacon_data *params, + const u8 *ssid, size_t ssid_len, + enum nl80211_hidden_ssid hidden_ssid, + bool check_for_concurrency) +{ + struct sap_config *config; + struct hdd_beacon_data *beacon = NULL; + struct ieee80211_mgmt *mgmt_frame; + struct ieee80211_mgmt mgmt; + const uint8_t *ie = NULL; + eCsrEncryptionType rsn_encrypt_type; + eCsrEncryptionType mc_rsn_encrypt_type; + uint16_t capab_info; + int status = QDF_STATUS_SUCCESS, ret; + int qdf_status = QDF_STATUS_SUCCESS; + sap_event_cb sap_event_callback; + struct hdd_hostapd_state *hostapd_state; + mac_handle_t mac_handle; + int32_t i; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t mcc_to_scc_switch = 0, conc_rule1 = 0; + struct sme_config_params *sme_config; + bool mfp_capable = false; + bool mfp_required = false; + uint16_t prev_rsn_length = 0; + enum dfs_mode mode; + bool ignore_cac = 0; + uint8_t is_overlap_enable = 0; + uint8_t beacon_fixed_len, indoor_chnl_marking = 0; + bool sap_force_11n_for_11ac = 0; + bool go_force_11n_for_11ac = 0; + bool bval = false; + bool enable_dfs_scan = true; + + hdd_enter(); + + hdd_notify_teardown_tdls_links(hdd_ctx->psoc); + + ucfg_mlme_get_sap_force_11n_for_11ac(hdd_ctx->psoc, + &sap_force_11n_for_11ac); + ucfg_mlme_get_go_force_11n_for_11ac(hdd_ctx->psoc, + &go_force_11n_for_11ac); + + if (policy_mgr_is_hw_mode_change_in_progress(hdd_ctx->psoc)) { + status = policy_mgr_wait_for_connection_update( + hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("qdf wait for event failed!!"); + return -EINVAL; + } + } + + /* + * For STA+SAP concurrency support from GUI, first STA connection gets + * triggered and while it is in progress, SAP start also comes up. + * Once STA association is successful, STA connect event is sent to + * kernel which gets queued in kernel workqueue and supplicant won't + * process M1 received from AP and send M2 until this NL80211_CONNECT + * event is received. Workqueue is not scheduled as RTNL lock is already + * taken by hostapd thread which has issued start_bss command to driver. + * Driver cannot complete start_bss as the pending command at the head + * of the SME command pending list is hw_mode_update for STA session + * which cannot be processed as SME is in WAITforKey state for STA + * interface. The start_bss command for SAP interface is queued behind + * the hw_mode_update command and so it cannot be processed until + * hw_mode_update command is processed. This is causing a deadlock so + * disconnect the STA interface first if connection or key exchange is + * in progress and then start SAP interface. + */ + hdd_abort_ongoing_sta_connection(hdd_ctx); + + mac_handle = hdd_ctx->mac_handle; + + /* Disable Roaming on all adapters before starting bss */ + wlan_hdd_disable_roaming(adapter, RSO_START_BSS); + + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + if (!sme_config) { + hdd_err("failed to allocate memory"); + ret = -ENOMEM; + goto free; + } + + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + + config = &adapter->session.ap.sap_config; + if (!config->chan_freq) { + hdd_err("Invalid channel"); + ret = -EINVAL; + goto free; + } + + if (QDF_STATUS_SUCCESS != + ucfg_policy_mgr_get_indoor_chnl_marking(hdd_ctx->psoc, + &indoor_chnl_marking)) + hdd_err("can't get indoor channel marking, using default"); + /* Mark the indoor channel (passive) to disable */ + if (indoor_chnl_marking && adapter->device_mode == QDF_SAP_MODE) { + hdd_update_indoor_channel(hdd_ctx, true); + if (QDF_IS_STATUS_ERROR( + sme_update_channel_list(mac_handle))) { + hdd_update_indoor_channel(hdd_ctx, false); + hdd_err("Can't start BSS: update channel list failed"); + ret = -EINVAL; + goto free; + } + + /* check if STA is on indoor channel*/ + if (policy_mgr_is_force_scc(hdd_ctx->psoc)) + hdd_check_and_disconnect_sta_on_invalid_channel( + hdd_ctx, + eSIR_MAC_OPER_CHANNEL_DISABLED_INDOOR); + } + + beacon = adapter->session.ap.beacon; + + /* + * beacon_fixed_len is the fixed length of beacon + * frame which includes only mac header length and + * beacon manadatory fields like timestamp, + * beacon_int and capab_info. + * (From the reference of struct ieee80211_mgmt) + */ + beacon_fixed_len = sizeof(mgmt) - sizeof(mgmt.u) + + sizeof(mgmt.u.beacon); + if (beacon->head_len < beacon_fixed_len) { + hdd_err("Invalid beacon head len"); + ret = -EINVAL; + goto error; + } + + mgmt_frame = (struct ieee80211_mgmt *)beacon->head; + + config->beacon_int = mgmt_frame->u.beacon.beacon_int; + config->dfs_cac_offload = hdd_ctx->dfs_cac_offload; + + status = ucfg_policy_mgr_get_enable_overlap_chnl(hdd_ctx->psoc, + &is_overlap_enable); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("can't get overlap channel INI value, using default"); + config->enOverLapCh = is_overlap_enable; + + config->dtim_period = beacon->dtim_period; + + if (config->acs_cfg.acs_mode == true) { + hdd_debug("acs_chan_freq %u, acs_dfs_mode %u", + hdd_ctx->acs_policy.acs_chan_freq, + hdd_ctx->acs_policy.acs_dfs_mode); + + if (hdd_ctx->acs_policy.acs_chan_freq) + config->chan_freq = hdd_ctx->acs_policy.acs_chan_freq; + mode = hdd_ctx->acs_policy.acs_dfs_mode; + config->acs_dfs_mode = wlan_hdd_get_dfs_mode(mode); + } + + policy_mgr_update_user_config_sap_chan(hdd_ctx->psoc, + config->chan_freq); + ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc, &mcc_to_scc_switch); + + if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + ie = wlan_get_ie_ptr_from_eid(WLAN_EID_COUNTRY, + beacon->tail, beacon->tail_len); + if ((adapter->device_mode == QDF_SAP_MODE) && ie) { + if (ie[1] < IEEE80211_COUNTRY_IE_MIN_LEN) { + hdd_err("Invalid Country IE len: %d", ie[1]); + ret = -EINVAL; + goto error; + } + + if (!qdf_mem_cmp(hdd_ctx->reg.alpha2, &ie[2], + REG_ALPHA2_LEN)) + config->ieee80211d = 1; + else + config->ieee80211d = 0; + } else + config->ieee80211d = 0; + + config->countryCode[0] = hdd_ctx->reg.alpha2[0]; + config->countryCode[1] = hdd_ctx->reg.alpha2[1]; + + /* Overwrite second AP's channel with first only when: + * 1. If operating mode is single mac + * 2. or if 2nd AP is coming up on 5G band channel + */ + ret = 0; + if (!policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) || + !WLAN_REG_IS_24GHZ_CH_FREQ(config->chan_freq)) { + ret = wlan_hdd_sap_cfg_dfs_override(adapter); + if (ret < 0) + goto error; + } + if (!ret && wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, + config->chan_freq)) + hdd_ctx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; + + if (QDF_STATUS_SUCCESS != + wlan_hdd_validate_operation_channel(adapter, + config->chan_freq)) { + hdd_err("Invalid Ch_freq: %d", config->chan_freq); + ret = -EINVAL; + goto error; + } + ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc, + &enable_dfs_scan); + + /* reject SAP if DFS channel scan is not allowed */ + if (!(enable_dfs_scan) && + (CHANNEL_STATE_DFS == + wlan_reg_get_channel_state_for_freq(hdd_ctx->pdev, + config->chan_freq))) { + hdd_err("No SAP start on DFS channel"); + ret = -EOPNOTSUPP; + goto error; + } + + status = ucfg_mlme_get_dfs_ignore_cac(hdd_ctx->psoc, + &ignore_cac); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("can't get ignore cac flag"); + + wlansap_set_dfs_ignore_cac(mac_handle, ignore_cac); + + wlansap_set_dfs_preferred_channel_location(mac_handle); + + wlan_hdd_set_sap_mcc_chnl_avoid(hdd_ctx); + } else { + config->ieee80211d = 0; + } + + tgt_dfs_set_tx_leakage_threshold(hdd_ctx->pdev); + + capab_info = mgmt_frame->u.beacon.capab_info; + + config->privacy = (mgmt_frame->u.beacon.capab_info & + WLAN_CAPABILITY_PRIVACY) ? true : false; + + (WLAN_HDD_GET_AP_CTX_PTR(adapter))->privacy = config->privacy; + + /*Set wps station to configured */ + ie = wlan_hdd_get_wps_ie_ptr(beacon->tail, beacon->tail_len); + + if (ie) { + /* To access ie[15], length needs to be at least 14 */ + if (ie[1] < 14) { + hdd_err("Wps Ie Length(%hhu) is too small", + ie[1]); + ret = -EINVAL; + goto error; + } else if (memcmp(&ie[2], WPS_OUI_TYPE, WPS_OUI_TYPE_SIZE) == + 0) { + hdd_debug("WPS IE(len %d)", (ie[1] + 2)); + /* Check 15 bit of WPS IE as it contain information for + * wps state + */ + if (SAP_WPS_ENABLED_UNCONFIGURED == ie[15]) { + config->wps_state = + SAP_WPS_ENABLED_UNCONFIGURED; + } else if (SAP_WPS_ENABLED_CONFIGURED == ie[15]) { + config->wps_state = SAP_WPS_ENABLED_CONFIGURED; + } + } + } else { + config->wps_state = SAP_WPS_DISABLED; + } + /* Forward WPS PBC probe request frame up */ + config->fwdWPSPBCProbeReq = 1; + + config->RSNEncryptType = eCSR_ENCRYPT_TYPE_NONE; + config->mcRSNEncryptType = eCSR_ENCRYPT_TYPE_NONE; + (WLAN_HDD_GET_AP_CTX_PTR(adapter))->encryption_type = + eCSR_ENCRYPT_TYPE_NONE; + + config->RSNWPAReqIELength = 0; + memset(&config->RSNWPAReqIE[0], 0, sizeof(config->RSNWPAReqIE)); + ie = wlan_get_ie_ptr_from_eid(WLAN_EID_RSN, beacon->tail, + beacon->tail_len); + if (ie && ie[1]) { + config->RSNWPAReqIELength = ie[1] + 2; + if (config->RSNWPAReqIELength < sizeof(config->RSNWPAReqIE)) + memcpy(&config->RSNWPAReqIE[0], ie, + config->RSNWPAReqIELength); + else + hdd_err("RSNWPA IE MAX Length exceeded; length =%d", + config->RSNWPAReqIELength); + /* The actual processing may eventually be more extensive than + * this. Right now, just consume any PMKIDs that are sent in + * by the app. + */ + status = + hdd_softap_unpack_ie(cds_get_context + (QDF_MODULE_ID_SME), + &rsn_encrypt_type, + &mc_rsn_encrypt_type, + &config->akm_list, + &mfp_capable, + &mfp_required, + config->RSNWPAReqIE[1] + 2, + config->RSNWPAReqIE); + + if (QDF_STATUS_SUCCESS == status) { + /* Now copy over all the security attributes you have + * parsed out. Use the cipher type in the RSN IE + */ + config->RSNEncryptType = rsn_encrypt_type; + config->mcRSNEncryptType = mc_rsn_encrypt_type; + (WLAN_HDD_GET_AP_CTX_PTR(adapter))-> + encryption_type = rsn_encrypt_type; + hdd_debug("CSR Encryption: %d mcEncryption: %d num_akm_suites:%d", + rsn_encrypt_type, mc_rsn_encrypt_type, + config->akm_list.numEntries); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, + QDF_TRACE_LEVEL_DEBUG, + config->akm_list.authType, + config->akm_list.numEntries); + } + } + + ie = wlan_get_vendor_ie_ptr_from_oui(WPA_OUI_TYPE, WPA_OUI_TYPE_SIZE, + beacon->tail, beacon->tail_len); + + if (ie && ie[1] && (ie[0] == DOT11F_EID_WPA)) { + if (config->RSNWPAReqIE[0]) { + /*Mixed mode WPA/WPA2 */ + prev_rsn_length = config->RSNWPAReqIELength; + config->RSNWPAReqIELength += ie[1] + 2; + if (config->RSNWPAReqIELength < + sizeof(config->RSNWPAReqIE)) + memcpy(&config->RSNWPAReqIE[0] + + prev_rsn_length, ie, ie[1] + 2); + else + hdd_err("RSNWPA IE MAX Length exceeded; length: %d", + config->RSNWPAReqIELength); + } else { + config->RSNWPAReqIELength = ie[1] + 2; + if (config->RSNWPAReqIELength < + sizeof(config->RSNWPAReqIE)) + memcpy(&config->RSNWPAReqIE[0], ie, + config->RSNWPAReqIELength); + else + hdd_err("RSNWPA IE MAX Length exceeded; length: %d", + config->RSNWPAReqIELength); + status = hdd_softap_unpack_ie + (cds_get_context(QDF_MODULE_ID_SME), + &rsn_encrypt_type, + &mc_rsn_encrypt_type, + &config->akm_list, + &mfp_capable, &mfp_required, + config->RSNWPAReqIE[1] + 2, + config->RSNWPAReqIE); + + if (QDF_STATUS_SUCCESS == status) { + /* Now copy over all the security attributes + * you have parsed out. Use the cipher type + * in the RSN IE + */ + config->RSNEncryptType = rsn_encrypt_type; + config->mcRSNEncryptType = mc_rsn_encrypt_type; + (WLAN_HDD_GET_AP_CTX_PTR(adapter))-> + encryption_type = rsn_encrypt_type; + hdd_debug("CSR Encryption: %d mcEncryption: %d num_akm_suites:%d", + rsn_encrypt_type, mc_rsn_encrypt_type, + config->akm_list.numEntries); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, + QDF_TRACE_LEVEL_DEBUG, + config->akm_list.authType, + config->akm_list.numEntries); + } + } + } + + if (config->RSNWPAReqIELength > sizeof(config->RSNWPAReqIE)) { + hdd_err("RSNWPAReqIELength is too large"); + ret = -EINVAL; + goto error; + } + + config->SSIDinfo.ssidHidden = false; + + if (ssid) { + qdf_mem_copy(config->SSIDinfo.ssid.ssId, ssid, ssid_len); + config->SSIDinfo.ssid.length = ssid_len; + + switch (hidden_ssid) { + case NL80211_HIDDEN_SSID_NOT_IN_USE: + config->SSIDinfo.ssidHidden = eHIDDEN_SSID_NOT_IN_USE; + break; + case NL80211_HIDDEN_SSID_ZERO_LEN: + hdd_debug("HIDDEN_SSID_ZERO_LEN"); + config->SSIDinfo.ssidHidden = eHIDDEN_SSID_ZERO_LEN; + break; + case NL80211_HIDDEN_SSID_ZERO_CONTENTS: + hdd_debug("HIDDEN_SSID_ZERO_CONTENTS"); + config->SSIDinfo.ssidHidden = + eHIDDEN_SSID_ZERO_CONTENTS; + break; + default: + hdd_err("Wrong hidden_ssid param: %d", hidden_ssid); + break; + } + } + + qdf_mem_copy(config->self_macaddr.bytes, + adapter->mac_addr.bytes, + QDF_MAC_ADDR_SIZE); + + /* default value */ + config->SapMacaddr_acl = eSAP_ACCEPT_UNLESS_DENIED; + config->num_accept_mac = 0; + config->num_deny_mac = 0; + status = ucfg_policy_mgr_get_conc_rule1(hdd_ctx->psoc, &conc_rule1); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("can't get ucfg_policy_mgr_get_conc_rule1, use def"); +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + /* + * We don't want P2PGO to follow STA's channel + * so lets limit the logic for SAP only. + * Later if we decide to make p2pgo follow STA's + * channel then remove this check. + */ + if ((0 == conc_rule1) || + (conc_rule1 && (QDF_SAP_MODE == adapter->device_mode))) + config->cc_switch_mode = mcc_to_scc_switch; +#endif + + if (!(ssid && qdf_str_len(PRE_CAC_SSID) == ssid_len && + (0 == qdf_mem_cmp(ssid, PRE_CAC_SSID, ssid_len)))) { + uint16_t beacon_data_len; + + beacon_data_len = beacon->head_len - beacon_fixed_len; + + ie = wlan_get_ie_ptr_from_eid(WLAN_EID_SUPP_RATES, + &mgmt_frame->u.beacon.variable[0], + beacon_data_len); + + if (ie) { + ie++; + if (ie[0] > WLAN_SUPPORTED_RATES_IE_MAX_LEN) { + hdd_err("Invalid supported rates %d", + ie[0]); + ret = -EINVAL; + goto error; + } + config->supported_rates.numRates = ie[0]; + ie++; + for (i = 0; + i < config->supported_rates.numRates; i++) { + if (ie[i]) + config->supported_rates.rate[i] = ie[i]; + } + hdd_debug("Configured Num Supported rates: %d", + config->supported_rates.numRates); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, + QDF_TRACE_LEVEL_DEBUG, + config->supported_rates.rate, + config->supported_rates.numRates); + } + ie = wlan_get_ie_ptr_from_eid(WLAN_EID_EXT_SUPP_RATES, + beacon->tail, + beacon->tail_len); + if (ie) { + ie++; + if (ie[0] > WLAN_SUPPORTED_RATES_IE_MAX_LEN) { + hdd_err("Invalid supported rates %d", + ie[0]); + ret = -EINVAL; + goto error; + } + config->extended_rates.numRates = ie[0]; + ie++; + for (i = 0; i < config->extended_rates.numRates; i++) { + if (ie[i]) + config->extended_rates.rate[i] = ie[i]; + } + + hdd_debug("Configured Num Extended rates: %d", + config->extended_rates.numRates); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, + QDF_TRACE_LEVEL_DEBUG, + config->extended_rates.rate, + config->extended_rates.numRates); + } + + config->require_h2e = false; + wlan_hdd_check_h2e(&config->supported_rates, + &config->require_h2e); + wlan_hdd_check_h2e(&config->extended_rates, + &config->require_h2e); + } + + if (!cds_is_sub_20_mhz_enabled()) + wlan_hdd_set_sap_hwmode(adapter); + + status = ucfg_mlme_get_vht_for_24ghz(hdd_ctx->psoc, &bval); + if (QDF_IS_STATUS_ERROR(qdf_status)) + hdd_err("Failed to get vht_for_24ghz"); + + if (WLAN_REG_IS_24GHZ_CH_FREQ(config->chan_freq) && bval && + (config->SapHw_mode == eCSR_DOT11_MODE_11n || + config->SapHw_mode == eCSR_DOT11_MODE_11n_ONLY)) + config->SapHw_mode = eCSR_DOT11_MODE_11ac; + + if (((adapter->device_mode == QDF_SAP_MODE) && + (sap_force_11n_for_11ac)) || + ((adapter->device_mode == QDF_P2P_GO_MODE) && + (go_force_11n_for_11ac))) { + if (config->SapHw_mode == eCSR_DOT11_MODE_11ac || + config->SapHw_mode == eCSR_DOT11_MODE_11ac_ONLY) + config->SapHw_mode = eCSR_DOT11_MODE_11n; + } + + qdf_mem_zero(sme_config, sizeof(*sme_config)); + sme_get_config_param(mac_handle, sme_config); + /* Override hostapd.conf wmm_enabled only for 11n and 11AC configs (IOT) + * As per spec 11N/AC STA are QOS STA and may not connect or throughput + * may not be good with non QOS 11N AP + * Default: enable QOS for SAP unless WMM IE not present for 11bga + */ + sme_config->csr_config.WMMSupportMode = eCsrRoamWmmAuto; + ie = wlan_get_vendor_ie_ptr_from_oui(WMM_OUI_TYPE, WMM_OUI_TYPE_SIZE, + beacon->tail, beacon->tail_len); + if (!ie && (config->SapHw_mode == eCSR_DOT11_MODE_11a || + config->SapHw_mode == eCSR_DOT11_MODE_11g || + config->SapHw_mode == eCSR_DOT11_MODE_11b)) + sme_config->csr_config.WMMSupportMode = eCsrRoamWmmNoQos; + sme_update_config(mac_handle, sme_config); + + if (!((adapter->device_mode == QDF_SAP_MODE) && + (sap_force_11n_for_11ac)) || + ((adapter->device_mode == QDF_P2P_GO_MODE) && + (go_force_11n_for_11ac))) { + config->ch_width_orig = + hdd_map_nl_chan_width(config->ch_width_orig); + } else { + if (config->ch_width_orig >= NL80211_CHAN_WIDTH_40) + config->ch_width_orig = CH_WIDTH_40MHZ; + else + config->ch_width_orig = CH_WIDTH_20MHZ; + } + + if (wlan_hdd_setup_driver_overrides(adapter)) { + ret = -EINVAL; + goto error; + } + + config->ch_params.ch_width = config->ch_width_orig; + if ((config->ch_params.ch_width == CH_WIDTH_80P80MHZ) && + ucfg_mlme_get_restricted_80p80_bw_supp(hdd_ctx->psoc)) { + if (!((config->ch_params.center_freq_seg0 == 138 && + config->ch_params.center_freq_seg1 == 155) || + (config->ch_params.center_freq_seg1 == 138 && + config->ch_params.center_freq_seg0 == 155))) { + config->ch_params.center_freq_seg1 = 0; + config->ch_width_orig = CH_WIDTH_80MHZ; + config->ch_params.ch_width = config->ch_width_orig; + } + } + + wlan_reg_set_channel_params_for_freq(hdd_ctx->pdev, config->chan_freq, + config->sec_ch_freq, + &config->ch_params); + if (0 != wlan_hdd_cfg80211_update_apies(adapter)) { + hdd_err("SAP Not able to set AP IEs"); + ret = -EINVAL; + goto error; + } + +#ifdef WLAN_FEATURE_11W + config->mfpCapable = mfp_capable; + config->mfpRequired = mfp_required; + hdd_debug("Soft AP MFP capable %d, MFP required %d", + config->mfpCapable, config->mfpRequired); +#endif + + hdd_nofl_debug("SAP mac:" QDF_MAC_ADDR_FMT " SSID: %.*s BCNINTV:%d Freq:%d HW mode:%d privacy:%d akm:%d acs_mode:%d acs_dfs_mode %d dtim period:%d", + QDF_MAC_ADDR_REF(adapter->mac_addr.bytes), + config->SSIDinfo.ssid.length, + config->SSIDinfo.ssid.ssId, (int)config->beacon_int, + config->chan_freq, config->SapHw_mode, config->privacy, + config->authType, config->acs_cfg.acs_mode, + config->acs_dfs_mode, config->dtim_period); + + mutex_lock(&hdd_ctx->sap_lock); + if (cds_is_driver_unloading()) { + mutex_unlock(&hdd_ctx->sap_lock); + + hdd_err("The driver is unloading, ignore the bss starting"); + ret = -EINVAL; + goto error; + } + + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + mutex_unlock(&hdd_ctx->sap_lock); + + wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_ALL); + /* Bss already started. just return. */ + /* TODO Probably it should update some beacon params. */ + hdd_debug("Bss Already started...Ignore the request"); + hdd_exit(); + ret = 0; + goto free; + } + + if (check_for_concurrency) { + if (!policy_mgr_allow_concurrency(hdd_ctx->psoc, + policy_mgr_convert_device_mode_to_qdf_type( + adapter->device_mode), + config->chan_freq, HW_MODE_20_MHZ)) { + mutex_unlock(&hdd_ctx->sap_lock); + + hdd_err("This concurrency combination is not allowed"); + ret = -EINVAL; + goto error; + } + } + + if (!hdd_set_connection_in_progress(true)) { + mutex_unlock(&hdd_ctx->sap_lock); + + hdd_err("Can't start BSS: set connection in progress failed"); + ret = -EINVAL; + goto error; + } + + config->persona = adapter->device_mode; + + sap_event_callback = hdd_hostapd_sap_event_cb; + + (WLAN_HDD_GET_AP_CTX_PTR(adapter))->dfs_cac_block_tx = true; + set_bit(SOFTAP_INIT_DONE, &adapter->event_flags); + + qdf_event_reset(&hostapd_state->qdf_event); + status = wlansap_start_bss( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + sap_event_callback, config, adapter->dev); + if (!QDF_IS_STATUS_SUCCESS(status)) { + mutex_unlock(&hdd_ctx->sap_lock); + + hdd_set_connection_in_progress(false); + hdd_err("SAP Start Bss fail"); + ret = -EINVAL; + goto error; + } + + qdf_status = qdf_wait_single_event(&hostapd_state->qdf_event, + SME_CMD_START_BSS_TIMEOUT); + + wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_ALL); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status) || + !QDF_IS_STATUS_SUCCESS(hostapd_state->qdf_status)) { + mutex_unlock(&hdd_ctx->sap_lock); + + hdd_err("qdf wait for single_event failed!!"); + hdd_set_connection_in_progress(false); + sme_get_command_q_status(mac_handle); + wlansap_stop_bss(WLAN_HDD_GET_SAP_CTX_PTR(adapter)); + if (!cds_is_driver_recovering()) + QDF_ASSERT(0); + ret = -EINVAL; + goto error; + } + /* Successfully started Bss update the state bit. */ + set_bit(SOFTAP_BSS_STARTED, &adapter->event_flags); + + mutex_unlock(&hdd_ctx->sap_lock); + + /* Initialize WMM configuation */ + hdd_wmm_init(adapter); + if (hostapd_state->bss_state == BSS_START) { + policy_mgr_incr_active_session(hdd_ctx->psoc, + adapter->device_mode, + adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, + true); + } + + wlan_hdd_dhcp_offload_enable(hdd_ctx, adapter); + ucfg_p2p_status_start_bss(adapter->vdev); + + /* Check and restart SAP if it is on unsafe channel */ + hdd_unsafe_channel_restart_sap(hdd_ctx); + + hdd_set_connection_in_progress(false); + policy_mgr_nan_sap_post_enable_conc_check(hdd_ctx->psoc); + ret = 0; + goto free; + +error: + + /* Revert the indoor to passive marking if START BSS fails */ + if (indoor_chnl_marking && adapter->device_mode == QDF_SAP_MODE) { + hdd_update_indoor_channel(hdd_ctx, false); + sme_update_channel_list(mac_handle); + } + clear_bit(SOFTAP_INIT_DONE, &adapter->event_flags); + qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); + wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_ALL); + +free: + /* + * Due to audio share glitch with P2P GO due + * to roam scan on concurrent interface, disable + * roaming if "p2p_disable_roam" ini is enabled. + * Donot re-enable roaming again on other STA interface + * if p2p GO is active on any vdev. + */ + if (ucfg_p2p_is_roam_config_disabled(hdd_ctx->psoc) && + adapter->device_mode == QDF_P2P_GO_MODE) { + hdd_debug("p2p go mode, keep roam disabled"); + } else { + /* Enable Roaming after start bss in case of failure/success */ + wlan_hdd_enable_roaming(adapter, RSO_START_BSS); + } + qdf_mem_free(sme_config); + return ret; +} + +int hdd_destroy_acs_timer(struct hdd_adapter *adapter) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + + if (!adapter->session.ap.vendor_acs_timer_initialized) + return 0; + + adapter->session.ap.vendor_acs_timer_initialized = false; + + clear_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags); + if (QDF_TIMER_STATE_RUNNING == + adapter->session.ap.vendor_acs_timer.state) { + qdf_status = + qdf_mc_timer_stop(&adapter->session.ap. + vendor_acs_timer); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("Failed to stop ACS timer"); + } + + if (adapter->session.ap.vendor_acs_timer.user_data) + qdf_mem_free(adapter->session.ap.vendor_acs_timer.user_data); + + qdf_mc_timer_destroy(&adapter->session.ap.vendor_acs_timer); + + return 0; +} + +/** + * __wlan_hdd_cfg80211_stop_ap() - stop soft ap + * @wiphy: Pointer to wiphy structure + * @dev: Pointer to net_device structure + * + * Return: 0 for success non-zero for failure + */ +static int __wlan_hdd_cfg80211_stop_ap(struct wiphy *wiphy, + struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + QDF_STATUS status; + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + tSirUpdateIE update_ie; + int ret; + mac_handle_t mac_handle; + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + /* + * In case of SSR and other FW down cases, validate context will + * fail. But return success to upper layer so that it can clean up + * kernel variables like beacon interval. If the failure status + * is returned then next set beacon command will fail as beacon + * interval in not reset. + */ + if (ret) + goto exit; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + goto exit; + } + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_err("Driver module is closed; dropping request"); + goto exit; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) { + hdd_err("vdev is invalid. Hence return"); + goto exit; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_STOP_AP, + adapter->vdev_id, adapter->device_mode); + + if (!(adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE)) { + hdd_err("stop ap is given on device modes other than SAP/GO. Hence return"); + goto exit; + } + + /* Clear SOFTAP_INIT_DONE flag to mark stop_ap deinit. So that we do + * not restart SAP after SSR as SAP is already stopped from user space. + * This update is moved to start of this function to resolve stop_ap + * call during SSR case. Adapter gets cleaned up as part of SSR. + */ + clear_bit(SOFTAP_INIT_DONE, &adapter->event_flags); + hdd_debug("Device_mode %s(%d)", + qdf_opmode_str(adapter->device_mode), adapter->device_mode); + + /* + * If a STA connection is in progress in another adapter, disconnect + * the STA and complete the SAP operation. STA will reconnect + * after SAP stop is done. + */ + hdd_abort_ongoing_sta_connection(hdd_ctx); + + if (adapter->device_mode == QDF_SAP_MODE) { + wlan_hdd_del_station(adapter); + mac_handle = hdd_ctx->mac_handle; + status = wlan_hdd_flush_pmksa_cache(adapter); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("Cannot flush PMKIDCache"); + } + + cds_flush_work(&adapter->sap_stop_bss_work); + adapter->session.ap.sap_config.acs_cfg.acs_mode = false; + qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + wlan_hdd_cleanup_actionframe(adapter); + wlan_hdd_cleanup_remain_on_channel_ctx(adapter); + mutex_lock(&hdd_ctx->sap_lock); + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + struct hdd_hostapd_state *hostapd_state = + WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + + qdf_event_reset(&hostapd_state->qdf_stop_bss_event); + status = wlansap_stop_bss(WLAN_HDD_GET_SAP_CTX_PTR(adapter)); + if (QDF_IS_STATUS_SUCCESS(status)) { + qdf_status = + qdf_wait_for_event_completion(&hostapd_state-> + qdf_stop_bss_event, + SME_CMD_STOP_BSS_TIMEOUT); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("qdf wait for single_event failed!!"); + hdd_sap_indicate_disconnect_for_sta(adapter); + QDF_ASSERT(0); + } + } + clear_bit(SOFTAP_BSS_STARTED, &adapter->event_flags); + hdd_stop_tsf_sync(adapter); + + /*BSS stopped, clear the active sessions for this device mode*/ + policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, + adapter->device_mode, + adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, + false); + + /* + * Due to audio share glitch with P2P GO due + * to roam scan on concurrent interface, disable + * roaming if "p2p_disable_roam" ini is enabled. + * Re-enable roaming on other STA interface if p2p GO + * is active on any vdev. + */ + if (ucfg_p2p_is_roam_config_disabled(hdd_ctx->psoc) && + adapter->device_mode == QDF_P2P_GO_MODE) { + hdd_debug("p2p go disconnected enable roam"); + wlan_hdd_enable_roaming(adapter, RSO_START_BSS); + } + + if (adapter->session.ap.beacon) { + qdf_mem_free(adapter->session.ap.beacon); + adapter->session.ap.beacon = NULL; + } + } else { + hdd_debug("SAP already down"); + mutex_unlock(&hdd_ctx->sap_lock); + goto exit; + } + + mutex_unlock(&hdd_ctx->sap_lock); + + mac_handle = hdd_ctx->mac_handle; + if (wlan_sap_is_pre_cac_active(mac_handle)) + hdd_clean_up_pre_cac_interface(hdd_ctx); + + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Stopping the BSS"); + goto exit; + } + + qdf_copy_macaddr(&update_ie.bssid, &adapter->mac_addr); + update_ie.vdev_id = adapter->vdev_id; + update_ie.ieBufferlength = 0; + update_ie.pAdditionIEBuffer = NULL; + update_ie.append = true; + update_ie.notify = true; + if (sme_update_add_ie(mac_handle, + &update_ie, + eUPDATE_IE_PROBE_BCN) == QDF_STATUS_E_FAILURE) { + hdd_err("Could not pass on PROBE_RSP_BCN data to PE"); + } + + if (sme_update_add_ie(mac_handle, + &update_ie, + eUPDATE_IE_ASSOC_RESP) == QDF_STATUS_E_FAILURE) { + hdd_err("Could not pass on ASSOC_RSP data to PE"); + } + /* Reset WNI_CFG_PROBE_RSP Flags */ + wlan_hdd_reset_prob_rspies(adapter); + hdd_destroy_acs_timer(adapter); + + ucfg_p2p_status_stop_bss(adapter->vdev); + +exit: + if (adapter->session.ap.beacon) { + qdf_mem_free(adapter->session.ap.beacon); + adapter->session.ap.beacon = NULL; + } + + hdd_exit(); + + return 0; +} + +/** + * wlan_hdd_get_channel_bw() - get channel bandwidth + * @width: input channel width in nl80211_chan_width value + * + * Return: channel width value defined by driver + */ +static enum hw_mode_bandwidth wlan_hdd_get_channel_bw( + enum nl80211_chan_width width) +{ + enum hw_mode_bandwidth ch_bw = HW_MODE_20_MHZ; + + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + ch_bw = HW_MODE_20_MHZ; + break; + case NL80211_CHAN_WIDTH_40: + ch_bw = HW_MODE_40_MHZ; + break; + case NL80211_CHAN_WIDTH_80: + ch_bw = HW_MODE_80_MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + ch_bw = HW_MODE_80_PLUS_80_MHZ; + break; + case NL80211_CHAN_WIDTH_160: + ch_bw = HW_MODE_160_MHZ; + break; + default: + hdd_err("Invalid width: %d, using default 20MHz", width); + break; + } + + return ch_bw; +} + +/** + * wlan_hdd_cfg80211_stop_ap() - stop sap + * @wiphy: Pointer to wiphy + * @dev: Pointer to netdev + * + * Return: zero for success non-zero for failure + */ +int wlan_hdd_cfg80211_stop_ap(struct wiphy *wiphy, + struct net_device *dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + /* + * The stop_ap can be called in the same context through + * wlan_hdd_del_virtual_intf. As vdev_trans is already taking place as + * part of the del_vitrtual_intf, this vdev_op cannot start. + * Return 0 in case op is not started so that the kernel frees the + * beacon memory properly. + */ + if (errno) + return 0; + + errno = __wlan_hdd_cfg80211_stop_ap(wiphy, dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) +/* + * Beginning with 4.7 struct ieee80211_channel uses enum nl80211_band + */ +static inline +enum nl80211_band ieee80211_channel_band(const struct ieee80211_channel *chan) +{ + return chan->band; +} +#else +/* + * Prior to 4.7 struct ieee80211_channel used enum ieee80211_band. However the + * ieee80211_band enum values are assigned from enum nl80211_band so we can safely + * typecast one to another. + */ +static inline +enum nl80211_band ieee80211_channel_band(const struct ieee80211_channel *chan) +{ + enum ieee80211_band band = chan->band; + return (enum nl80211_band)band; +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) || \ + defined(CFG80211_BEACON_TX_RATE_CUSTOM_BACKPORT) +/** + * hdd_get_data_rate_from_rate_mask() - convert mask to rate + * @wiphy: Pointer to wiphy + * @band: band + * @bit_rate_mask: pointer to bit_rake_mask + * + * This function takes band and bit_rate_mask as input and + * derives the beacon_tx_rate based on the supported rates + * published as part of wiphy register. + * + * Return: data rate for success or zero for failure + */ +static uint16_t hdd_get_data_rate_from_rate_mask(struct wiphy *wiphy, + enum nl80211_band band, + struct cfg80211_bitrate_mask *bit_rate_mask) +{ + struct ieee80211_supported_band *sband = wiphy->bands[band]; + int sband_n_bitrates; + struct ieee80211_rate *sband_bitrates; + int i; + + if (sband) { + sband_bitrates = sband->bitrates; + sband_n_bitrates = sband->n_bitrates; + for (i = 0; i < sband_n_bitrates; i++) { + if (bit_rate_mask->control[band].legacy == + sband_bitrates[i].hw_value) + return sband_bitrates[i].bitrate; + } + } + return 0; +} + +/** + * hdd_update_beacon_rate() - Update beacon tx rate + * @adapter: Pointer to hdd_adapter_t + * @wiphy: Pointer to wiphy + * @params: Pointet to cfg80211_ap_settings + * + * This function updates the beacon tx rate which is provided + * as part of cfg80211_ap_settions in to the sap_config + * structure + * + * Return: none + */ +static void hdd_update_beacon_rate(struct hdd_adapter *adapter, + struct wiphy *wiphy, + struct cfg80211_ap_settings *params) +{ + struct cfg80211_bitrate_mask *beacon_rate_mask; + enum nl80211_band band; + + band = ieee80211_channel_band(params->chandef.chan); + beacon_rate_mask = ¶ms->beacon_rate; + if (beacon_rate_mask->control[band].legacy) { + adapter->session.ap.sap_config.beacon_tx_rate = + hdd_get_data_rate_from_rate_mask(wiphy, band, + beacon_rate_mask); + hdd_debug("beacon mask value %u, rate %hu", + params->beacon_rate.control[0].legacy, + adapter->session.ap.sap_config.beacon_tx_rate); + } +} +#else +static void hdd_update_beacon_rate(struct hdd_adapter *adapter, + struct wiphy *wiphy, + struct cfg80211_ap_settings *params) +{ +} +#endif + +/** + * wlan_hdd_ap_ap_force_scc_override() - force Same band SCC chan override + * @adapter: SAP adapter pointer + * @freq: SAP starting channel freq + * @new_chandef: new override SAP channel + * + * The function will override the second SAP chan to the first SAP's home + * channel if the FW doesn't support MCC and force SCC enabled in INI. + * + * Return: true if channel override + */ +static bool +wlan_hdd_ap_ap_force_scc_override(struct hdd_adapter *adapter, + uint32_t freq, + struct cfg80211_chan_def *new_chandef) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t cc_count, i; + uint32_t op_freq[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct ch_params ch_params; + enum nl80211_channel_type channel_type; + struct hdd_adapter *con_adapter; + uint8_t con_vdev_id; + uint32_t con_freq; + uint8_t mcc_to_scc_switch; + struct ieee80211_channel *ieee_chan; + + if (!hdd_ctx) { + hdd_err("hdd context is NULL"); + return false; + } + + if (!policy_mgr_concurrent_beaconing_sessions_running(hdd_ctx->psoc)) + return false; + if (policy_mgr_dual_beacon_on_single_mac_mcc_capable(hdd_ctx->psoc)) + return false; + if (!policy_mgr_dual_beacon_on_single_mac_scc_capable(hdd_ctx->psoc)) + return false; + ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc, + &mcc_to_scc_switch); + if ((mcc_to_scc_switch != + QDF_MCC_TO_SCC_SWITCH_FORCE_WITHOUT_DISCONNECTION) && + (mcc_to_scc_switch != QDF_MCC_TO_SCC_WITH_PREFERRED_BAND)) + return false; + cc_count = policy_mgr_get_mode_specific_conn_info(hdd_ctx->psoc, + &op_freq[0], + &vdev_id[0], + PM_SAP_MODE); + if (cc_count < MAX_NUMBER_OF_CONC_CONNECTIONS) + cc_count = cc_count + + policy_mgr_get_mode_specific_conn_info( + hdd_ctx->psoc, + &op_freq[cc_count], + &vdev_id[cc_count], + PM_P2P_GO_MODE); + for (i = 0 ; i < cc_count; i++) { + if (freq == op_freq[i]) + continue; + if (!policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc)) + break; + if (wlan_reg_is_same_band_freqs(freq, + op_freq[i])) + break; + } + if (i >= cc_count) + return false; + con_freq = op_freq[i]; + con_vdev_id = vdev_id[i]; + con_adapter = hdd_get_adapter_by_vdev(hdd_ctx, con_vdev_id); + if (!con_adapter) + return false; + ieee_chan = ieee80211_get_channel(hdd_ctx->wiphy, + con_freq); + if (!ieee_chan) { + hdd_err("channel converion failed"); + return false; + } + + if (!wlan_sap_get_ch_params(WLAN_HDD_GET_SAP_CTX_PTR(con_adapter), + &ch_params)) + wlan_reg_set_channel_params_for_freq(hdd_ctx->pdev, + con_freq, 0, + &ch_params); + switch (ch_params.sec_ch_offset) { + case PHY_SINGLE_CHANNEL_CENTERED: + channel_type = NL80211_CHAN_HT20; + break; + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + channel_type = NL80211_CHAN_HT40MINUS; + break; + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + channel_type = NL80211_CHAN_HT40PLUS; + break; + default: + channel_type = NL80211_CHAN_NO_HT; + break; + } + cfg80211_chandef_create(new_chandef, ieee_chan, channel_type); + switch (ch_params.ch_width) { + case CH_WIDTH_80MHZ: + new_chandef->width = NL80211_CHAN_WIDTH_80; + break; + case CH_WIDTH_80P80MHZ: + new_chandef->width = NL80211_CHAN_WIDTH_80P80; + if (ch_params.mhz_freq_seg1) + new_chandef->center_freq2 = ch_params.mhz_freq_seg1; + break; + case CH_WIDTH_160MHZ: + new_chandef->width = NL80211_CHAN_WIDTH_160; + break; + default: + break; + } + if ((ch_params.ch_width == CH_WIDTH_80MHZ) || + (ch_params.ch_width == CH_WIDTH_80P80MHZ) || + (ch_params.ch_width == CH_WIDTH_160MHZ)) { + if (ch_params.mhz_freq_seg0) + new_chandef->center_freq1 = ch_params.mhz_freq_seg0; + } + + hdd_debug("override AP freq %d to first AP(vdev_id %d) center_freq:%d width:%d freq1:%d freq2:%d ", + freq, con_vdev_id, new_chandef->chan->center_freq, + new_chandef->width, new_chandef->center_freq1, + new_chandef->center_freq2); + return true; +} + +#ifdef NDP_SAP_CONCURRENCY_ENABLE +/** + * hdd_sap_nan_check_and_disable_unsupported_ndi: Wrapper function for + * ucfg_nan_check_and_disable_unsupported_ndi + * @psoc: pointer to psoc object + * @force: When set forces NDI disable + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +hdd_sap_nan_check_and_disable_unsupported_ndi(struct wlan_objmgr_psoc *psoc, + bool force) +{ + return QDF_STATUS_SUCCESS; +} +#else +static inline QDF_STATUS +hdd_sap_nan_check_and_disable_unsupported_ndi(struct wlan_objmgr_psoc *psoc, + bool force) +{ + return ucfg_nan_check_and_disable_unsupported_ndi(psoc, force); +} +#endif + +/** + * __wlan_hdd_cfg80211_start_ap() - start soft ap mode + * @wiphy: Pointer to wiphy structure + * @dev: Pointer to net_device structure + * @params: Pointer to AP settings parameters + * + * Return: 0 for success non-zero for failure + */ +static int __wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *params) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + enum hw_mode_bandwidth channel_width; + int status; + struct sme_sta_inactivity_timeout *sta_inactivity_timer; + uint8_t channel, mandt_chnl_list = 0; + bool sta_sap_scc_on_dfs_chan; + uint16_t sta_cnt, sap_cnt; + bool val; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_chan_def new_chandef; + struct cfg80211_chan_def *chandef; + uint16_t sap_ch; + bool srd_channel_allowed, disable_nan = true; + enum QDF_OPMODE vdev_opmode; + uint8_t vdev_id_list[MAX_NUMBER_OF_CONC_CONNECTIONS], i; + + hdd_enter(); + + clear_bit(SOFTAP_INIT_DONE, &adapter->event_flags); + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_START_AP, + adapter->vdev_id, params->beacon_interval); + + if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { + hdd_err("HDD adapter magic is invalid"); + return -ENODEV; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return status; + + hdd_nofl_info("%s(vdevid-%d): START AP: Device mode %s(%d) sub20 %d", + dev->name, adapter->vdev_id, + qdf_opmode_str(adapter->device_mode), + adapter->device_mode, cds_is_sub_20_mhz_enabled()); + if (policy_mgr_is_hw_mode_change_in_progress(hdd_ctx->psoc)) { + status = policy_mgr_wait_for_connection_update( + hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("qdf wait for event failed!!"); + return -EINVAL; + } + } + + channel_width = wlan_hdd_get_channel_bw(params->chandef.width); + channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); + if (!channel) { + hdd_err("Invalid channel"); + return -EINVAL; + } + chandef = ¶ms->chandef; + if ((adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) && + wlan_hdd_ap_ap_force_scc_override(adapter, + chandef->chan->center_freq, + &new_chandef)) { + chandef = &new_chandef; + channel = ieee80211_frequency_to_channel( + chandef->chan->center_freq); + channel_width = wlan_hdd_get_channel_bw(chandef->width); + } + + if (QDF_STATUS_SUCCESS != + ucfg_policy_mgr_get_sap_mandt_chnl(hdd_ctx->psoc, &mandt_chnl_list)) + hdd_err("can't get mandatory channel list"); + if (mandt_chnl_list) { + if (WLAN_REG_IS_5GHZ_CH(channel)) { + hdd_debug("channel %hu, sap mandatory chan list enabled", + channel); + if (!policy_mgr_get_sap_mandatory_chan_list_len( + hdd_ctx->psoc)) + policy_mgr_init_sap_mandatory_2g_chan( + hdd_ctx->psoc); + + policy_mgr_add_sap_mandatory_chan( + hdd_ctx->psoc, wlan_chan_to_freq(channel)); + } else { + policy_mgr_init_sap_mandatory_2g_chan( + hdd_ctx->psoc); + } + } + + adapter->session.ap.sap_config.ch_params.center_freq_seg0 = + cds_freq_to_chan(chandef->center_freq1); + adapter->session.ap.sap_config.ch_params.center_freq_seg1 = + cds_freq_to_chan(chandef->center_freq2); + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan( + hdd_ctx->psoc); + sta_cnt = policy_mgr_get_mode_specific_conn_info(hdd_ctx->psoc, NULL, + vdev_id_list, + PM_STA_MODE); + sap_cnt = policy_mgr_get_mode_specific_conn_info(hdd_ctx->psoc, NULL, + &vdev_id_list[sta_cnt], + PM_SAP_MODE); + + hdd_debug("sta_sap_scc_on_dfs_chan %u, sta_cnt %u", + sta_sap_scc_on_dfs_chan, sta_cnt); + + /* if sta_sap_scc_on_dfs_chan ini is set, DFS master capability is + * assumed disabled in the driver. + */ + if ((wlan_reg_get_channel_state(hdd_ctx->pdev, channel) == + CHANNEL_STATE_DFS) && !sta_cnt && sta_sap_scc_on_dfs_chan && + !ucfg_policy_mgr_get_dfs_master_dynamic_enabled( + hdd_ctx->psoc, adapter->vdev_id)) { + hdd_err("SAP not allowed on DFS channel if no dfs master capability!!"); + return -EINVAL; + } + + vdev_opmode = wlan_vdev_mlme_get_opmode(adapter->vdev); + ucfg_mlme_get_srd_master_mode_for_vdev(hdd_ctx->psoc, vdev_opmode, + &srd_channel_allowed); + + if (!srd_channel_allowed && + wlan_reg_is_etsi13_srd_chan(hdd_ctx->pdev, channel)) { + hdd_err("vdev opmode %d not allowed on SRD channel.", + vdev_opmode); + return -EINVAL; + } + if (cds_is_sub_20_mhz_enabled()) { + enum channel_state ch_state; + enum phy_ch_width sub_20_ch_width = CH_WIDTH_INVALID; + struct sap_config *sap_cfg = &adapter->session.ap.sap_config; + + if (CHANNEL_STATE_DFS == wlan_reg_get_channel_state( + hdd_ctx->pdev, channel)) { + hdd_err("Can't start SAP-DFS (channel=%d)with sub 20 MHz ch wd", + channel); + return -EINVAL; + } + if (channel_width != HW_MODE_20_MHZ) { + hdd_err("Hostapd (20+ MHz) conflits with config.ini (sub 20 MHz)"); + return -EINVAL; + } + if (cds_is_5_mhz_enabled()) + sub_20_ch_width = CH_WIDTH_5MHZ; + if (cds_is_10_mhz_enabled()) + sub_20_ch_width = CH_WIDTH_10MHZ; + if (WLAN_REG_IS_5GHZ_CH(channel)) + ch_state = wlan_reg_get_5g_bonded_channel_state( + hdd_ctx->pdev, channel, + sub_20_ch_width); + else + ch_state = wlan_reg_get_2g_bonded_channel_state( + hdd_ctx->pdev, channel, + sub_20_ch_width, 0); + if (CHANNEL_STATE_DISABLE == ch_state) { + hdd_err("Given ch width not supported by reg domain"); + return -EINVAL; + } + sap_cfg->SapHw_mode = eCSR_DOT11_MODE_abg; + } + + /* Disable NAN Disc before starting P2P GO or STA+SAP or SAP+SAP */ + if (adapter->device_mode == QDF_P2P_GO_MODE || sta_cnt || + (sap_cnt > (MAX_SAP_NUM_CONCURRENCY_WITH_NAN - 1))) { + for (i = 0; i < sta_cnt + sap_cnt; i++) + if (vdev_id_list[i] == adapter->vdev_id) + disable_nan = false; + if (disable_nan) { + hdd_debug("Invalid NAN concurrency. SAP: %d STA: %d P2P_GO: %d", + sap_cnt, sta_cnt, + (adapter->device_mode == QDF_P2P_GO_MODE)); + ucfg_nan_disable_concurrency(hdd_ctx->psoc); + } + } + + /* NDI + SAP conditional supported */ + hdd_sap_nan_check_and_disable_unsupported_ndi(hdd_ctx->psoc, true); + if (!policy_mgr_nan_sap_pre_enable_conc_check( + hdd_ctx->psoc, PM_SAP_MODE, wlan_chan_to_freq(channel))) + hdd_debug("NAN disabled due to concurrency constraints"); + + /* check if concurrency is allowed */ + if (!policy_mgr_allow_concurrency(hdd_ctx->psoc, + policy_mgr_convert_device_mode_to_qdf_type( + adapter->device_mode), + wlan_chan_to_freq(channel), + channel_width)) { + hdd_err("Connection failed due to concurrency check failure"); + return -EINVAL; + } + + status = policy_mgr_reset_connection_update(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("ERR: clear event failed"); + + /* + * For Start Ap, the driver checks whether the SAP comes up in a + * different or same band( whether we require DBS or Not). + * If we dont require DBS, then the driver does nothing assuming + * the state would be already in non DBS mode, and just continues + * with vdev up on same MAC, by stoping the opportunistic timer, + * which results in a connection of 1x1 if already the state was in + * DBS. So first stop timer, and check the current hw mode. + * If the SAP comes up in band different from STA, DBS mode is already + * set. IF not, then well check for upgrade, and shift the connection + * back to single MAC 2x2 (if initial was 2x2). + */ + + policy_mgr_checkn_update_hw_mode_single_mac_mode( + hdd_ctx->psoc, wlan_chan_to_freq(channel)); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to stop DBS opportunistic timer"); + return -EINVAL; + } + + status = policy_mgr_current_connections_update( + hdd_ctx->psoc, adapter->vdev_id, + wlan_chan_to_freq(channel), + POLICY_MGR_UPDATE_REASON_START_AP); + if (status == QDF_STATUS_E_FAILURE) { + hdd_err("ERROR: connections update failed!!"); + return -EINVAL; + } + + if (QDF_STATUS_SUCCESS == status) { + status = policy_mgr_wait_for_connection_update(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("qdf wait for event failed!!"); + return -EINVAL; + } + } + + if (adapter->device_mode == QDF_P2P_GO_MODE) { + struct hdd_adapter *p2p_adapter; + + p2p_adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_DEVICE_MODE); + if (p2p_adapter) { + hdd_debug("Cancel active p2p device ROC before GO starting"); + wlan_hdd_cancel_existing_remain_on_channel( + p2p_adapter); + } + } + + if ((adapter->device_mode == QDF_SAP_MODE) + || (adapter->device_mode == QDF_P2P_GO_MODE) + ) { + struct hdd_beacon_data *old, *new; + enum nl80211_channel_type channel_type; + struct sap_config *sap_config = + &((WLAN_HDD_GET_AP_CTX_PTR(adapter))->sap_config); + + old = adapter->session.ap.beacon; + + if (old) + return -EALREADY; + + status = + wlan_hdd_cfg80211_alloc_new_beacon(adapter, &new, + ¶ms->beacon, + params->dtim_period); + + if (status != 0) { + hdd_err("Error!!! Allocating the new beacon"); + return -EINVAL; + } + adapter->session.ap.beacon = new; + + if (chandef->width < NL80211_CHAN_WIDTH_80) + channel_type = cfg80211_get_chandef_type( + chandef); + else + channel_type = NL80211_CHAN_HT40PLUS; + + + wlan_hdd_set_channel(wiphy, dev, + chandef, + channel_type); + + hdd_update_beacon_rate(adapter, wiphy, params); + + /* set authentication type */ + switch (params->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + adapter->session.ap.sap_config.authType = + eSAP_OPEN_SYSTEM; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + adapter->session.ap.sap_config.authType = + eSAP_SHARED_KEY; + break; + default: + adapter->session.ap.sap_config.authType = + eSAP_AUTO_SWITCH; + } + adapter->session.ap.sap_config.ch_width_orig = + chandef->width; + + status = + wlan_hdd_cfg80211_start_bss(adapter, + ¶ms->beacon, + params->ssid, params->ssid_len, + params->hidden_ssid, true); + + if (status != 0) { + hdd_err("Error Start bss Failed"); + goto err_start_bss; + } + + hdd_start_tsf_sync(adapter); + + if (wdev->chandef.chan->center_freq != + params->chandef.chan->center_freq) + params->chandef = wdev->chandef; + /* + * If Do_Not_Break_Stream enabled send avoid channel list + * to application. + */ + sap_ch = wlan_reg_freq_to_chan(hdd_ctx->pdev, + sap_config->chan_freq); + if (sap_ch && policy_mgr_is_dnsc_set(adapter->vdev)) + wlan_hdd_send_avoid_freq_for_dnbs(hdd_ctx, sap_ch); + + ucfg_mlme_get_sap_inactivity_override(hdd_ctx->psoc, &val); + if (val) { + sta_inactivity_timer = qdf_mem_malloc( + sizeof(*sta_inactivity_timer)); + if (!sta_inactivity_timer) { + hdd_err("Failed to allocate Memory"); + status = QDF_STATUS_E_FAILURE; + goto err_start_bss; + } + sta_inactivity_timer->session_id = adapter->vdev_id; + sta_inactivity_timer->sta_inactivity_timeout = + params->inactivity_timeout; + sme_update_sta_inactivity_timeout(hdd_ctx->mac_handle, + sta_inactivity_timer); + qdf_mem_free(sta_inactivity_timer); + } + } + + goto success; + +err_start_bss: + if (adapter->session.ap.beacon) + qdf_mem_free(adapter->session.ap.beacon); + adapter->session.ap.beacon = NULL; + +success: + hdd_exit(); + return status; +} + +/** + * wlan_hdd_cfg80211_start_ap() - start sap + * @wiphy: Pointer to wiphy + * @dev: Pointer to netdev + * @params: Pointer to start ap configuration parameters + * + * Return: zero for success non-zero for failure + */ +int wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_start_ap(wiphy, dev, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_change_beacon() - change beacon for sofatap/p2p go + * @wiphy: Pointer to wiphy structure + * @dev: Pointer to net_device structure + * @params: Pointer to change beacon parameters + * + * Return: 0 for success non-zero for failure + */ +static int __wlan_hdd_cfg80211_change_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *params) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + struct hdd_beacon_data *old, *new; + int status; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_CHANGE_BEACON, + adapter->vdev_id, adapter->device_mode); + + hdd_debug("Device_mode %s(%d)", + qdf_opmode_str(adapter->device_mode), adapter->device_mode); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + + if (0 != status) + return status; + + if (!(adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE)) { + return -EOPNOTSUPP; + } + + old = adapter->session.ap.beacon; + + if (!old) { + hdd_err("session id: %d beacon data points to NULL", + adapter->vdev_id); + return -EINVAL; + } + + status = wlan_hdd_cfg80211_alloc_new_beacon(adapter, &new, params, 0); + + if (status != QDF_STATUS_SUCCESS) { + hdd_err("new beacon alloc failed"); + return -EINVAL; + } + + adapter->session.ap.beacon = new; + hdd_debug("update beacon for P2P GO/SAP"); + status = wlan_hdd_cfg80211_start_bss(adapter, params, NULL, + 0, 0, false); + + hdd_exit(); + return status; +} + +/** + * wlan_hdd_cfg80211_change_beacon() - change beacon content in sap mode + * @wiphy: Pointer to wiphy + * @dev: Pointer to netdev + * @params: Pointer to change beacon parameters + * + * Return: zero for success non-zero for failure + */ +int wlan_hdd_cfg80211_change_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *params) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_change_beacon(wiphy, dev, params); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_sap_indicate_disconnect_for_sta() - Indicate disconnect indication + * to supplicant, if there any clients connected to SAP interface. + * @adapter: sap adapter context + * + * Return: nothing + */ +void hdd_sap_indicate_disconnect_for_sta(struct hdd_adapter *adapter) +{ + struct sap_event sap_event; + struct sap_context *sap_ctx; + struct hdd_station_info *sta_info, *tmp = NULL; + + hdd_enter(); + + sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); + if (!sap_ctx) { + hdd_err("invalid sap context"); + return; + } + + hdd_for_each_sta_ref_safe(adapter->sta_info_list, sta_info, tmp, + STA_INFO_SAP_INDICATE_DISCONNECT_FOR_STA) { + hdd_debug("sta_mac: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); + + if (qdf_is_macaddr_broadcast(&sta_info->sta_mac)) { + hdd_softap_deregister_sta(adapter, &sta_info); + hdd_put_sta_info_ref( + &adapter->sta_info_list, + &sta_info, true, + STA_INFO_SAP_INDICATE_DISCONNECT_FOR_STA); + continue; + } + + sap_event.sapHddEventCode = eSAP_STA_DISASSOC_EVENT; + + qdf_mem_copy( + &sap_event.sapevt.sapStationDisassocCompleteEvent.staMac, + &sta_info->sta_mac, sizeof(struct qdf_mac_addr)); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SAP_INDICATE_DISCONNECT_FOR_STA); + + sap_event.sapevt.sapStationDisassocCompleteEvent.reason = + eSAP_MAC_INITATED_DISASSOC; + sap_event.sapevt.sapStationDisassocCompleteEvent.status_code = + QDF_STATUS_E_RESOURCES; + hdd_hostapd_sap_event_cb(&sap_event, sap_ctx->user_context); + } + + hdd_exit(); +} + +bool hdd_is_peer_associated(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr) +{ + bool is_associated = false; + struct hdd_station_info *sta_info, *tmp = NULL; + + if (!adapter || !mac_addr) { + hdd_err("Invalid adapter or mac_addr"); + return false; + } + + hdd_for_each_sta_ref_safe(adapter->sta_info_list, sta_info, tmp, + STA_INFO_IS_PEER_ASSOCIATED) { + if (!qdf_mem_cmp(&sta_info->sta_mac, mac_addr, + QDF_MAC_ADDR_SIZE)) { + is_associated = true; + hdd_put_sta_info_ref(&adapter->sta_info_list, + &sta_info, true, + STA_INFO_IS_PEER_ASSOCIATED); + if (tmp) + hdd_put_sta_info_ref( + &adapter->sta_info_list, + &tmp, true, + STA_INFO_IS_PEER_ASSOCIATED); + break; + } + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_IS_PEER_ASSOCIATED); + } + + return is_associated; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd.h new file mode 100644 index 0000000000000000000000000000000000000000..f75bcc0ebb09822cbc2e634c8f8ad9650341dedb --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_HOSTAPD_H) +#define WLAN_HDD_HOSTAPD_H + +/** + * DOC: wlan_hdd_hostapd.h + * + * WLAN Host Device driver hostapd header file + */ + +/* Include files */ + +#include +#include +#include +#include +#include + +/* Preprocessor definitions and constants */ + +struct hdd_adapter *hdd_wlan_create_ap_dev(struct hdd_context *hdd_ctx, + tSirMacAddr macAddr, + unsigned char name_assign_type, + uint8_t *name); + +enum csr_akm_type +hdd_translate_rsn_to_csr_auth_type(uint8_t auth_suite[4]); + +/** + * hdd_softap_set_channel_change() - + * This function to support SAP channel change with CSA IE + * set in the beacons. + * + * @dev: pointer to the net device. + * @target_chan_freq: target channel frequency. + * @target_bw: Target bandwidth to move. + * If no bandwidth is specified, the value is CH_WIDTH_MAX + * @forced: Force to switch channel, ignore SCC/MCC check + * + * Return: 0 for success, non zero for failure + */ +int hdd_softap_set_channel_change(struct net_device *dev, + int target_chan_freq, + enum phy_ch_width target_bw, + bool forced); + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * hdd_sap_restart_with_channel_switch() - SAP channel change with E/CSA + * @ap_adapter: HDD adapter + * @target_chan_freq: Channel frequency to which switch must happen + * @target_bw: Bandwidth of the target channel + * @forced: Force to switch channel, ignore SCC/MCC check + * + * Invokes the necessary API to perform channel switch for the SAP or GO + * + * Return: None + */ +void hdd_sap_restart_with_channel_switch(struct hdd_adapter *adapter, + uint32_t target_chan_freq, + uint32_t target_bw, + bool forced); +/** + * hdd_sap_restart_chan_switch_cb() - Function to restart SAP with + * a different channel + * @psoc: PSOC object information + * @vdev_id: vdev id + * @ch_freq: channel to switch + * @forced: Force to switch channel, ignore SCC/MCC check + * + * This function restarts SAP with a different channel + * + * Return: None + * + */ +void hdd_sap_restart_chan_switch_cb(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t ch_freq, + uint32_t channel_bw, + bool forced); +/** + * wlan_hdd_get_channel_for_sap_restart() - Function to get + * suitable channel and restart SAP + * @psoc: PSOC object information + * @vdev_id: vdev id + * @ch_freq: channel to be returned + * + * This function gets the channel parameters to restart SAP + * + * Return: None + * + */ +QDF_STATUS wlan_hdd_get_channel_for_sap_restart( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t *ch_freq); + +/** + * hdd_get_ap_6ghz_capable() - Get ap vdev 6ghz capable flags + * @psoc: PSOC object information + * @vdev_id: vdev id + * + * This function gets 6ghz capable information based on hdd ap adapter + * context. + * + * Return: uint32_t, vdev 6g capable flags from enum conn_6ghz_flag + */ +uint32_t hdd_get_ap_6ghz_capable(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); +#endif + +/** + * wlan_hdd_set_sap_csa_reason() - Function to set + * sap csa reason + * @psoc: PSOC object information + * @vdev_id: vdev id + * @reason: reason to be updated + * + * This function sets the reason for SAP channel switch + * + * Return: None + * + */ +void wlan_hdd_set_sap_csa_reason(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + uint8_t reason); +eCsrEncryptionType +hdd_translate_rsn_to_csr_encryption_type(uint8_t cipher_suite[4]); + +eCsrEncryptionType +hdd_translate_rsn_to_csr_encryption_type(uint8_t cipher_suite[4]); + +enum csr_akm_type +hdd_translate_wpa_to_csr_auth_type(uint8_t auth_suite[4]); + +eCsrEncryptionType +hdd_translate_wpa_to_csr_encryption_type(uint8_t cipher_suite[4]); + +QDF_STATUS hdd_softap_sta_deauth(struct hdd_adapter *adapter, + struct csr_del_sta_params *param); +void hdd_softap_sta_disassoc(struct hdd_adapter *adapter, + struct csr_del_sta_params *param); + +QDF_STATUS hdd_hostapd_sap_event_cb(struct sap_event *sap_event, + void *context); +/** + * hdd_init_ap_mode() - to init the AP adaptor + * @adapter: SAP/GO adapter + * @rtnl_held: flag to indicate if RTNL lock needs to be acquired + * + * This API can be called to open the SAP session as well as + * to create and store the vdev object. It also initializes necessary + * SAP adapter related params. + */ +QDF_STATUS hdd_init_ap_mode(struct hdd_adapter *adapter, bool reinit); +/** + * hdd_deinit_ap_mode() - to deinit the AP adaptor + * @hdd_ctx: pointer to hdd_ctx + * @adapter: SAP/GO adapter + * @rtnl_held: flag to indicate if RTNL lock needs to be acquired + * + * This API can be called to close the SAP session as well as + * release the vdev object completely. It also deinitializes necessary + * SAP adapter related params. + */ +void hdd_deinit_ap_mode(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, bool rtnl_held); +void hdd_set_ap_ops(struct net_device *dev); +/** + * hdd_sap_create_ctx() - Wrapper API to create SAP context + * @adapter: pointer to adapter + * + * This wrapper API can be called to create the sap context. It will + * eventually calls SAP API to create the sap context + * + * Return: true or false based on overall success or failure + */ +bool hdd_sap_create_ctx(struct hdd_adapter *adapter); +/** + * hdd_sap_destroy_ctx() - Wrapper API to destroy SAP context + * @adapter: pointer to adapter + * + * This wrapper API can be called to destroy the sap context. It will + * eventually calls SAP API to destroy the sap context + * + * Return: true or false based on overall success or failure + */ +bool hdd_sap_destroy_ctx(struct hdd_adapter *adapter); +/** + * hdd_sap_destroy_ctx_all() - Wrapper API to destroy all SAP context + * @adapter: pointer to adapter + * @is_ssr: true if SSR is in progress + * + * This wrapper API can be called to destroy all the sap context. + * if is_ssr is true, it will return as sap_ctx will be used when + * restart sap. + * + * Return: none + */ +void hdd_sap_destroy_ctx_all(struct hdd_context *hdd_ctx, bool is_ssr); + +int hdd_hostapd_stop(struct net_device *dev); +int hdd_sap_context_init(struct hdd_context *hdd_ctx); +void hdd_sap_context_destroy(struct hdd_context *hdd_ctx); +#ifdef QCA_HT_2040_COEX +QDF_STATUS hdd_set_sap_ht2040_mode(struct hdd_adapter *adapter, + uint8_t channel_type); +#endif + +int wlan_hdd_cfg80211_stop_ap(struct wiphy *wiphy, + struct net_device *dev); + +int wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *params); + +int wlan_hdd_cfg80211_change_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *params); + +/** + * hdd_is_peer_associated - is peer connected to softap + * @adapter: pointer to softap adapter + * @mac_addr: address to check in peer list + * + * This function has to be invoked only when bss is started and is used + * to check whether station with specified addr is peer or not + * + * Return: true if peer mac, else false + */ +bool hdd_is_peer_associated(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr); + +int hdd_destroy_acs_timer(struct hdd_adapter *adapter); + +QDF_STATUS wlan_hdd_config_acs(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter); + +void hdd_sap_indicate_disconnect_for_sta(struct hdd_adapter *adapter); + +/** + * wlan_hdd_disable_channels() - Cache the channels + * and current state of the channels from the channel list + * received in the command and disable the channels on the + * wiphy and reg table. + * @hdd_ctx: Pointer to hdd context + * + * Return: 0 on success, Error code on failure + */ +int wlan_hdd_disable_channels(struct hdd_context *hdd_ctx); + +/* + * hdd_check_and_disconnect_sta_on_invalid_channel() - Disconnect STA if it is + * on invalid channel + * @hdd_ctx: pointer to hdd context + * @reason: Mac Disconnect reason code as per @enum eSirMacReasonCodes + * + * STA should be disconnected before starting the SAP if it is on indoor + * channel. + * + * Return: void + */ +void +hdd_check_and_disconnect_sta_on_invalid_channel(struct hdd_context *hdd_ctx, + tSirMacReasonCodes reason); + +/** + * hdd_stop_sap_due_to_invalid_channel() - to stop sap in case of invalid chnl + * @work: pointer to work structure + * + * Let's say SAP detected RADAR and trying to select the new channel and if no + * valid channel is found due to none of the channels are available or + * regulatory restriction then SAP needs to be stopped. so SAP state-machine + * will create a work to stop the bss + * + * stop bss has to happen through worker thread because radar indication comes + * from FW through mc thread or main host thread and if same thread is used to + * do stopbss then waiting for stopbss to finish operation will halt mc thread + * to freeze which will trigger stopbss timeout. Instead worker thread can do + * the stopbss operation while mc thread waits for stopbss to finish. + * + * Return: none + */ +void hdd_stop_sap_due_to_invalid_channel(struct work_struct *work); + +/** + * hdd_is_any_sta_connecting() - check if any sta is connecting + * @hdd_ctx: hdd context + * + * Return: true if any sta is connecting + */ +bool hdd_is_any_sta_connecting(struct hdd_context *hdd_ctx); +#endif /* end #if !defined(WLAN_HDD_HOSTAPD_H) */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd_wext.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd_wext.c new file mode 100644 index 0000000000000000000000000000000000000000..4a0518099490cdb95f504f35794a0a10722016be --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd_wext.c @@ -0,0 +1,3270 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_hostapd_wext.c + * + * Linux Wireless Extensions Implementation + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include +#include "wlan_hdd_p2p.h" +#include "wma.h" +#ifdef WLAN_DEBUG +#include "wma_api.h" +#endif +#include "wlan_hdd_power.h" +#include "wlan_policy_mgr_ucfg.h" +#include +#include +#include "wlan_dfs_utils_api.h" +#include +#include +#include "wlan_mlme_ucfg_api.h" +#include "wlan_reg_ucfg_api.h" +#include "wlan_hdd_sta_info.h" +#define WE_WLAN_VERSION 1 + +/* WEXT limitation: MAX allowed buf len for any * + * IW_PRIV_TYPE_CHAR is 2Kbytes * + */ +#define WE_SAP_MAX_STA_INFO 0x7FF + +#define RC_2_RATE_IDX(_rc) ((_rc) & 0x7) +#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) +#define RC_2_RATE_IDX_11AC(_rc) ((_rc) & 0xf) +#define HT_RC_2_STREAMS_11AC(_rc) ((((_rc) & 0x30) >> 4) + 1) + +static int hdd_sap_get_chan_width(struct hdd_adapter *adapter, int *value) +{ + struct sap_context *sap_ctx; + struct hdd_hostapd_state *hostapdstate; + + hdd_enter(); + hostapdstate = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + + if (hostapdstate->bss_state != BSS_START) { + *value = -EINVAL; + return -EINVAL; + } + + sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); + + *value = wlansap_get_chan_width(sap_ctx); + hdd_debug("chan_width = %d", *value); + + return 0; +} + +int +static __iw_softap_get_ini_cfg(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret != 0) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + hdd_debug("Printing CLD global INI Config"); + hdd_cfg_get_global_config(hdd_ctx, extra, QCSAP_IOCTL_MAX_STR_LEN); + wrqu->data.length = strlen(extra) + 1; + + return 0; +} + +int +static iw_softap_get_ini_cfg(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_get_ini_cfg(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_softap_set_two_ints_getnone() - Generic "set two integer" ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_softap_set_two_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int ret; + int *value = (int *)extra; + int sub_cmd = value[0]; + struct hdd_context *hdd_ctx; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct cdp_txrx_stats_req req = {0}; + struct hdd_station_info *sta_info; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret != 0) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + if (qdf_unlikely(!soc)) { + hdd_err("soc is NULL"); + return -EINVAL; + } + + switch (sub_cmd) { + case QCSAP_PARAM_SET_TXRX_STATS: + { + req.stats = value[1]; + req.mac_id = value[2]; + hdd_info("QCSAP_PARAM_SET_TXRX_STATS stats_id: %d mac_id: %d", + req.stats, req.mac_id); + + if (value[1] == CDP_TXRX_STATS_28) { + req.peer_addr = (char *)&adapter->mac_addr; + ret = cdp_txrx_stats_request(soc, adapter->vdev_id, + &req); + + hdd_for_each_sta_ref( + adapter->sta_info_list, sta_info, + STA_INFO_SAP_SET_TWO_INTS_GETNONE) { + hdd_debug("bss_id: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF( + sta_info->sta_mac.bytes)); + + req.peer_addr = (char *) + &sta_info->sta_mac; + ret = cdp_txrx_stats_request( + soc, adapter->vdev_id, &req); + hdd_put_sta_info_ref( + &adapter->sta_info_list, &sta_info, + true, + STA_INFO_SAP_SET_TWO_INTS_GETNONE); + } + } else { + ret = cdp_txrx_stats_request(soc, adapter->vdev_id, + &req); + } + + break; + } + + /* Firmware debug log */ + case QCSAP_IOCTL_SET_FW_CRASH_INJECT: + ret = hdd_crash_inject(adapter, value[1], value[2]); + break; + + case QCSAP_IOCTL_DUMP_DP_TRACE_LEVEL: + hdd_set_dump_dp_trace(value[1], value[2]); + break; + + case QCSAP_ENABLE_FW_PROFILE: + hdd_debug("QCSAP_ENABLE_FW_PROFILE: %d %d", + value[1], value[2]); + ret = wma_cli_set2_command(adapter->vdev_id, + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + value[1], value[2], DBG_CMD); + break; + + case QCSAP_SET_FW_PROFILE_HIST_INTVL: + hdd_debug("QCSAP_SET_FW_PROFILE_HIST_INTVL: %d %d", + value[1], value[2]); + ret = wma_cli_set2_command(adapter->vdev_id, + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + value[1], value[2], DBG_CMD); + break; + + case QCSAP_SET_WLAN_SUSPEND: + hdd_info("SAP unit-test suspend(%d, %d)", value[1], value[2]); + ret = hdd_wlan_fake_apps_suspend(hdd_ctx->wiphy, dev, + value[1], value[2]); + break; + + case QCSAP_SET_WLAN_RESUME: + ret = hdd_wlan_fake_apps_resume(hdd_ctx->wiphy, dev); + break; + + case QCSAP_SET_BA_AGEING_TIMEOUT: + hdd_info("QCSAP_SET_BA_AGEING_TIMEOUT: AC[%d] timeout[%d]", + value[1], value[2]); + /* + * value[1] : suppose to be access class, value between[0-3] + * value[2]: suppose to be duration in seconds + */ + cdp_set_ba_timeout(soc, value[1], value[2]); + break; + + default: + hdd_err("Invalid IOCTL command: %d", sub_cmd); + break; + } + + return ret; +} + +static int iw_softap_set_two_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_set_two_ints_getnone(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static void print_mac_list(struct qdf_mac_addr *macList, uint8_t size) +{ + int i; + uint8_t *macArray; + + for (i = 0; i < size; i++) { + macArray = (macList + i)->bytes; + pr_info("ACL entry %i - "QDF_MAC_ADDR_FMT"\n", + i, QDF_MAC_ADDR_REF(macArray)); + } +} + +static QDF_STATUS hdd_print_acl(struct hdd_adapter *adapter) +{ + eSapMacAddrACL acl_mode; + struct qdf_mac_addr maclist[MAX_ACL_MAC_ADDRESS]; + uint8_t listnum; + struct sap_context *sap_ctx; + + sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); + qdf_mem_zero(&maclist[0], sizeof(maclist)); + if (QDF_STATUS_SUCCESS == wlansap_get_acl_mode(sap_ctx, &acl_mode)) { + pr_info("******** ACL MODE *********\n"); + switch (acl_mode) { + case eSAP_ACCEPT_UNLESS_DENIED: + pr_info("ACL Mode = ACCEPT_UNLESS_DENIED\n"); + break; + case eSAP_DENY_UNLESS_ACCEPTED: + pr_info("ACL Mode = DENY_UNLESS_ACCEPTED\n"); + break; + case eSAP_SUPPORT_ACCEPT_AND_DENY: + pr_info("ACL Mode = ACCEPT_AND_DENY\n"); + break; + case eSAP_ALLOW_ALL: + pr_info("ACL Mode = ALLOW_ALL\n"); + break; + default: + pr_info("Invalid SAP ACL Mode = %d\n", acl_mode); + return QDF_STATUS_E_FAILURE; + } + } else { + return QDF_STATUS_E_FAILURE; + } + + if (QDF_STATUS_SUCCESS == wlansap_get_acl_accept_list(sap_ctx, + &maclist[0], + &listnum)) { + pr_info("******* WHITE LIST ***********\n"); + if (listnum <= MAX_ACL_MAC_ADDRESS) + print_mac_list(&maclist[0], listnum); + } else { + return QDF_STATUS_E_FAILURE; + } + + if (QDF_STATUS_SUCCESS == wlansap_get_acl_deny_list(sap_ctx, + &maclist[0], + &listnum)) { + pr_info("******* BLACK LIST ***********\n"); + if (listnum <= MAX_ACL_MAC_ADDRESS) + print_mac_list(&maclist[0], listnum); + } else { + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +int +static __iw_softap_setparam(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + mac_handle_t mac_handle; + int *value = (int *)extra; + int sub_cmd = value[0]; + int set_value = value[1]; + QDF_STATUS status; + int ret = 0; + struct hdd_context *hdd_ctx; + bool bval = false; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return -EINVAL; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) { + hdd_err("mac handle is null"); + return -EINVAL; + } + + switch (sub_cmd) { + case QCASAP_SET_RADAR_DBG: + hdd_debug("QCASAP_SET_RADAR_DBG called with: value: %x", + set_value); + wlan_sap_enable_phy_error_logs(mac_handle, set_value); + break; + + case QCSAP_PARAM_CLR_ACL: + if (QDF_STATUS_SUCCESS != wlansap_clear_acl( + WLAN_HDD_GET_SAP_CTX_PTR(adapter))) { + ret = -EIO; + } + break; + + case QCSAP_PARAM_ACL_MODE: + if ((eSAP_ALLOW_ALL < (eSapMacAddrACL) set_value) || + (eSAP_ACCEPT_UNLESS_DENIED > (eSapMacAddrACL) set_value)) { + hdd_err("Invalid ACL Mode value: %d", set_value); + ret = -EINVAL; + } else { + wlansap_set_acl_mode( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + set_value); + } + break; + + case QCSAP_PARAM_SET_CHANNEL_CHANGE: + if ((QDF_SAP_MODE == adapter->device_mode) || + (QDF_P2P_GO_MODE == adapter->device_mode)) { + wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, + adapter->vdev_id, + CSA_REASON_USER_INITIATED); + hdd_debug("SET Channel Change to new channel= %d", + set_value); + ret = hdd_softap_set_channel_change(dev, + wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, set_value), + CH_WIDTH_MAX, false); + } else { + hdd_err("Channel Change Failed, Device in test mode"); + ret = -EINVAL; + } + break; + + case QCSAP_PARAM_CONC_SYSTEM_PREF: + hdd_debug("New preference: %d", set_value); + ucfg_policy_mgr_set_sys_pref(hdd_ctx->psoc, set_value); + break; + + case QCSAP_PARAM_MAX_ASSOC: + if (WNI_CFG_ASSOC_STA_LIMIT_STAMIN > set_value) { + hdd_err("Invalid setMaxAssoc value %d", + set_value); + ret = -EINVAL; + } else { + if (WNI_CFG_ASSOC_STA_LIMIT_STAMAX < set_value) { + hdd_warn("setMaxAssoc %d > max allowed %d.", + set_value, + WNI_CFG_ASSOC_STA_LIMIT_STAMAX); + hdd_warn("Setting it to max allowed and continuing"); + set_value = WNI_CFG_ASSOC_STA_LIMIT_STAMAX; + } + if (ucfg_mlme_set_assoc_sta_limit(hdd_ctx->psoc, + set_value) != + QDF_STATUS_SUCCESS) { + hdd_err("CFG_ASSOC_STA_LIMIT failed"); + ret = -EIO; + } + + } + break; + + case QCSAP_PARAM_HIDE_SSID: + { + QDF_STATUS status; + + /* + * Reject hidden ssid param update if reassoc in progress on + * any adapter. sme_is_any_session_in_middle_of_roaming is for + * LFR2 and hdd_is_roaming_in_progress is for LFR3 + */ + if (hdd_is_roaming_in_progress(hdd_ctx) || + sme_is_any_session_in_middle_of_roaming(mac_handle)) { + hdd_info("Reassociation in progress"); + return -EINVAL; + } + + /* + * Disable Roaming on all adapters before start of + * start of Hidden ssid connection + */ + wlan_hdd_disable_roaming(adapter, RSO_START_BSS); + + status = sme_update_session_param(mac_handle, + adapter->vdev_id, + SIR_PARAM_SSID_HIDDEN, set_value); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("QCSAP_PARAM_HIDE_SSID failed"); + wlan_hdd_enable_roaming(adapter, RSO_START_BSS); + return -EIO; + } + break; + } + case QCSAP_PARAM_SET_MC_RATE: + { + tSirRateUpdateInd rate_update = {0}; + + hdd_debug("MC Target rate %d", set_value); + qdf_copy_macaddr(&rate_update.bssid, + &adapter->mac_addr); + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("unable to get vht_enable2x2"); + ret = -1; + } + rate_update.nss = (bval == 0) ? 0 : 1; + + rate_update.dev_mode = adapter->device_mode; + rate_update.mcastDataRate24GHz = set_value; + rate_update.mcastDataRate24GHzTxFlag = 1; + rate_update.mcastDataRate5GHz = set_value; + rate_update.bcastDataRate = -1; + status = sme_send_rate_update_ind(mac_handle, &rate_update); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("SET_MC_RATE failed"); + ret = -1; + } + break; + } + + case QCSAP_PARAM_SET_TXRX_FW_STATS: + { + hdd_debug("QCSAP_PARAM_SET_TXRX_FW_STATS val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMA_VDEV_TXRX_FWSTATS_ENABLE_CMDID, + set_value, VDEV_CMD); + break; + } + + /* Firmware debug log */ + case QCSAP_DBGLOG_LOG_LEVEL: + { + hdd_debug("QCSAP_DBGLOG_LOG_LEVEL val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_LOG_LEVEL, + set_value, DBG_CMD); + break; + } + + case QCSAP_DBGLOG_VAP_ENABLE: + { + hdd_debug("QCSAP_DBGLOG_VAP_ENABLE val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_VAP_ENABLE, + set_value, DBG_CMD); + break; + } + + case QCSAP_DBGLOG_VAP_DISABLE: + { + hdd_debug("QCSAP_DBGLOG_VAP_DISABLE val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_VAP_DISABLE, + set_value, DBG_CMD); + break; + } + + case QCSAP_DBGLOG_MODULE_ENABLE: + { + hdd_debug("QCSAP_DBGLOG_MODULE_ENABLE val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_MODULE_ENABLE, + set_value, DBG_CMD); + break; + } + + case QCSAP_DBGLOG_MODULE_DISABLE: + { + hdd_debug("QCSAP_DBGLOG_MODULE_DISABLE val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_MODULE_DISABLE, + set_value, DBG_CMD); + break; + } + + case QCSAP_DBGLOG_MOD_LOG_LEVEL: + { + hdd_debug("QCSAP_DBGLOG_MOD_LOG_LEVEL val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_MOD_LOG_LEVEL, + set_value, DBG_CMD); + break; + } + + case QCSAP_DBGLOG_TYPE: + { + hdd_debug("QCSAP_DBGLOG_TYPE val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_TYPE, + set_value, DBG_CMD); + break; + } + case QCSAP_DBGLOG_REPORT_ENABLE: + { + hdd_debug("QCSAP_DBGLOG_REPORT_ENABLE val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_REPORT_ENABLE, + set_value, DBG_CMD); + break; + } + case QCSAP_PARAM_SET_MCC_CHANNEL_LATENCY: + { + wlan_hdd_set_mcc_latency(adapter, set_value); + break; + } + + case QCSAP_PARAM_SET_MCC_CHANNEL_QUOTA: + { + hdd_debug("iwpriv cmd to set MCC quota value %dms", + set_value); + ret = wlan_hdd_go_set_mcc_p2p_quota(adapter, + set_value); + break; + } + + case QCASAP_TXRX_FWSTATS_RESET: + { + hdd_debug("WE_TXRX_FWSTATS_RESET val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMA_VDEV_TXRX_FWSTATS_RESET_CMDID, + set_value, VDEV_CMD); + break; + } + + case QCSAP_PARAM_RTSCTS: + { + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + set_value, VDEV_CMD); + if (ret) { + hdd_err("FAILED TO SET RTSCTS at SAP"); + ret = -EIO; + } + break; + } + case QCASAP_SET_11N_RATE: + { + uint8_t preamble = 0, nss = 0, rix = 0; + struct sap_config *config = + &adapter->session.ap.sap_config; + + hdd_debug("SET_HT_RATE val %d", set_value); + + if (set_value != 0xff) { + rix = RC_2_RATE_IDX(set_value); + if (set_value & 0x80) { + if (config->SapHw_mode == + eCSR_DOT11_MODE_11b + || config->SapHw_mode == + eCSR_DOT11_MODE_11b_ONLY + || config->SapHw_mode == + eCSR_DOT11_MODE_11g + || config->SapHw_mode == + eCSR_DOT11_MODE_11g_ONLY + || config->SapHw_mode == + eCSR_DOT11_MODE_abg + || config->SapHw_mode == + eCSR_DOT11_MODE_11a) { + hdd_err("Not valid mode for HT"); + ret = -EIO; + break; + } + preamble = WMI_RATE_PREAMBLE_HT; + nss = HT_RC_2_STREAMS(set_value) - 1; + } else if (set_value & 0x10) { + if (config->SapHw_mode == + eCSR_DOT11_MODE_11a) { + hdd_err("Not valid for cck"); + ret = -EIO; + break; + } + preamble = WMI_RATE_PREAMBLE_CCK; + /* Enable Short preamble always + * for CCK except 1mbps + */ + if (rix != 0x3) + rix |= 0x4; + } else { + if (config->SapHw_mode == + eCSR_DOT11_MODE_11b + || config->SapHw_mode == + eCSR_DOT11_MODE_11b_ONLY) { + hdd_err("Not valid for OFDM"); + ret = -EIO; + break; + } + preamble = WMI_RATE_PREAMBLE_OFDM; + } + set_value = hdd_assemble_rate_code(preamble, nss, rix); + } + hdd_debug("SET_HT_RATE val %d rix %d preamble %x nss %d", + set_value, rix, preamble, nss); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_FIXED_RATE, + set_value, VDEV_CMD); + break; + } + + case QCASAP_SET_VHT_RATE: + { + uint8_t preamble = 0, nss = 0, rix = 0; + struct sap_config *config = + &adapter->session.ap.sap_config; + + if (config->SapHw_mode != eCSR_DOT11_MODE_11ac && + config->SapHw_mode != eCSR_DOT11_MODE_11ac_ONLY) { + hdd_err("SET_VHT_RATE: SapHw_mode= 0x%x, ch_freq: %d", + config->SapHw_mode, config->chan_freq); + ret = -EIO; + break; + } + + if (set_value != 0xff) { + rix = RC_2_RATE_IDX_11AC(set_value); + preamble = WMI_RATE_PREAMBLE_VHT; + nss = HT_RC_2_STREAMS_11AC(set_value) - 1; + + set_value = hdd_assemble_rate_code(preamble, nss, rix); + } + hdd_debug("SET_VHT_RATE val %d rix %d preamble %x nss %d", + set_value, rix, preamble, nss); + + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_FIXED_RATE, + set_value, VDEV_CMD); + break; + } + + case QCASAP_SHORT_GI: + { + hdd_debug("QCASAP_SET_SHORT_GI val %d", set_value); + ret = hdd_we_set_short_gi(adapter, set_value); + if (ret) + hdd_err("Failed to set ShortGI value ret: %d", ret); + break; + } + + case QCSAP_SET_AMPDU: + { + hdd_debug("QCSAP_SET_AMPDU %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + GEN_VDEV_PARAM_AMPDU, + set_value, GEN_CMD); + break; + } + + case QCSAP_SET_AMSDU: + { + hdd_debug("QCSAP_SET_AMSDU %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + GEN_VDEV_PARAM_AMSDU, + set_value, GEN_CMD); + break; + } + case QCSAP_GTX_HT_MCS: + { + hdd_debug("WMI_VDEV_PARAM_GTX_HT_MCS %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_HT_MCS, + set_value, GTX_CMD); + break; + } + + case QCSAP_GTX_VHT_MCS: + { + hdd_debug("WMI_VDEV_PARAM_GTX_VHT_MCS %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_VHT_MCS, + set_value, GTX_CMD); + break; + } + + case QCSAP_GTX_USRCFG: + { + hdd_debug("WMI_VDEV_PARAM_GTX_USR_CFG %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_USR_CFG, + set_value, GTX_CMD); + break; + } + + case QCSAP_GTX_THRE: + { + hdd_debug("WMI_VDEV_PARAM_GTX_THRE %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_THRE, + set_value, GTX_CMD); + break; + } + + case QCSAP_GTX_MARGIN: + { + hdd_debug("WMI_VDEV_PARAM_GTX_MARGIN %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_MARGIN, + set_value, GTX_CMD); + break; + } + + case QCSAP_GTX_STEP: + { + hdd_debug("WMI_VDEV_PARAM_GTX_STEP %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_STEP, + set_value, GTX_CMD); + break; + } + + case QCSAP_GTX_MINTPC: + { + hdd_debug("WMI_VDEV_PARAM_GTX_MINTPC %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_MINTPC, + set_value, GTX_CMD); + break; + } + + case QCSAP_GTX_BWMASK: + { + hdd_debug("WMI_VDEV_PARAM_GTX_BWMASK %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_BW_MASK, + set_value, GTX_CMD); + break; + } + + case QCASAP_SET_TM_LEVEL: + { + hdd_debug("Set Thermal Mitigation Level %d", set_value); + (void)sme_set_thermal_level(mac_handle, set_value); + break; + } + + case QCASAP_SET_DFS_IGNORE_CAC: + { + hdd_debug("Set Dfs ignore CAC %d", set_value); + + if (adapter->device_mode != QDF_SAP_MODE) + return -EINVAL; + + ret = wlansap_set_dfs_ignore_cac(mac_handle, set_value); + break; + } + + case QCASAP_SET_DFS_TARGET_CHNL: + { + hdd_debug("Set Dfs target channel %d", set_value); + + if (adapter->device_mode != QDF_SAP_MODE) + return -EINVAL; + + ret = wlansap_set_dfs_target_chnl(mac_handle, + wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, + set_value)); + break; + } + + case QCASAP_SET_HE_BSS_COLOR: + if (adapter->device_mode != QDF_SAP_MODE) + return -EINVAL; + + status = sme_set_he_bss_color(mac_handle, adapter->vdev_id, + set_value); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("SET_HE_BSS_COLOR failed"); + return -EIO; + } + break; + case QCASAP_SET_DFS_NOL: + wlansap_set_dfs_nol( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + (eSapDfsNolType) set_value); + break; + + case QCASAP_SET_RADAR_CMD: + { + struct hdd_ap_ctx *ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + struct wlan_objmgr_pdev *pdev; + struct radar_found_info radar; + + hdd_debug("Set QCASAP_SET_RADAR_CMD val %d", set_value); + + pdev = hdd_ctx->pdev; + if (!pdev) { + hdd_err("null pdev"); + return -EINVAL; + } + + qdf_mem_zero(&radar, sizeof(radar)); + if (wlan_reg_is_dfs_for_freq(pdev, ap_ctx->operating_chan_freq)) + tgt_dfs_process_radar_ind(pdev, &radar); + else + hdd_debug("Ignore set radar, op ch_freq(%d) is not dfs", + ap_ctx->operating_chan_freq); + + break; + } + case QCASAP_TX_CHAINMASK_CMD: + { + hdd_debug("QCASAP_TX_CHAINMASK_CMD val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_TX_CHAIN_MASK, + set_value, PDEV_CMD); + ret = hdd_set_antenna_mode(adapter, hdd_ctx, set_value); + break; + } + + case QCASAP_RX_CHAINMASK_CMD: + { + hdd_debug("QCASAP_RX_CHAINMASK_CMD val %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_RX_CHAIN_MASK, + set_value, PDEV_CMD); + ret = hdd_set_antenna_mode(adapter, hdd_ctx, set_value); + break; + } + + case QCASAP_NSS_CMD: + { + hdd_debug("QCASAP_NSS_CMD val %d", set_value); + hdd_update_nss(adapter, set_value, set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_NSS, + set_value, VDEV_CMD); + break; + } + + case QCSAP_IPA_UC_STAT: + { + /* If input value is non-zero get stats */ + switch (set_value) { + case 1: + ucfg_ipa_uc_stat(hdd_ctx->pdev); + break; + case 2: + ucfg_ipa_uc_info(hdd_ctx->pdev); + break; + case 3: + ucfg_ipa_uc_rt_debug_host_dump(hdd_ctx->pdev); + break; + case 4: + ucfg_ipa_dump_info(hdd_ctx->pdev); + break; + default: + /* place holder for stats clean up + * Stats clean not implemented yet on FW and IPA + */ + break; + } + return ret; + } + + case QCASAP_SET_PHYMODE: + ret = wlan_hdd_update_phymode(adapter, set_value); + break; + + case QCASAP_DUMP_STATS: + { + hdd_debug("QCASAP_DUMP_STATS val %d", set_value); + ret = hdd_wlan_dump_stats(adapter, set_value); + break; + } + case QCASAP_CLEAR_STATS: + { + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + hdd_debug("QCASAP_CLEAR_STATS val %d", set_value); + switch (set_value) { + case CDP_HDD_STATS: + memset(&adapter->stats, 0, + sizeof(adapter->stats)); + memset(&adapter->hdd_stats, 0, + sizeof(adapter->hdd_stats)); + break; + case CDP_TXRX_HIST_STATS: + wlan_hdd_clear_tx_rx_histogram(hdd_ctx); + break; + case CDP_HDD_NETIF_OPER_HISTORY: + wlan_hdd_clear_netif_queue_history(hdd_ctx); + break; + case CDP_HIF_STATS: + hdd_clear_hif_stats(); + break; + default: + if (soc) + cdp_clear_stats(soc, OL_TXRX_PDEV_ID, + set_value); + } + break; + } + case QCSAP_START_FW_PROFILING: + hdd_debug("QCSAP_START_FW_PROFILING %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_WLAN_PROFILE_TRIGGER_CMDID, + set_value, DBG_CMD); + break; + case QCASAP_PARAM_LDPC: + ret = hdd_set_ldpc(adapter, set_value); + break; + case QCASAP_PARAM_TX_STBC: + ret = hdd_set_tx_stbc(adapter, set_value); + break; + case QCASAP_PARAM_RX_STBC: + ret = hdd_set_rx_stbc(adapter, set_value); + break; + case QCASAP_SET_11AX_RATE: + ret = hdd_set_11ax_rate(adapter, set_value, + &adapter->session.ap. + sap_config); + break; + case QCASAP_PARAM_DCM: + hdd_debug("Set WMI_VDEV_PARAM_HE_DCM: %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_HE_DCM, set_value, + VDEV_CMD); + break; + case QCASAP_PARAM_RANGE_EXT: + hdd_debug("Set WMI_VDEV_PARAM_HE_RANGE_EXT: %d", set_value); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_HE_RANGE_EXT, + set_value, VDEV_CMD); + break; + case QCSAP_SET_DEFAULT_AMPDU: + hdd_debug("QCSAP_SET_DEFAULT_AMPDU val %d", set_value); + ret = wma_cli_set_command((int)adapter->vdev_id, + (int)WMI_PDEV_PARAM_MAX_MPDUS_IN_AMPDU, + set_value, PDEV_CMD); + break; + case QCSAP_ENABLE_RTS_BURSTING: + hdd_debug("QCSAP_ENABLE_RTS_BURSTING val %d", set_value); + ret = wma_cli_set_command((int)adapter->vdev_id, + (int)WMI_PDEV_PARAM_ENABLE_RTS_SIFS_BURSTING, + set_value, PDEV_CMD); + break; + case QCSAP_SET_BTCOEX_MODE: + ret = wlan_hdd_set_btcoex_mode(adapter, set_value); + break; + case QCSAP_SET_BTCOEX_LOW_RSSI_THRESHOLD: + ret = wlan_hdd_set_btcoex_rssi_threshold(adapter, set_value); + break; + default: + hdd_err("Invalid setparam command %d value %d", + sub_cmd, set_value); + ret = -EINVAL; + break; + } + hdd_exit(); + return ret; +} + +/** + * __iw_softap_get_three() - return three value to upper layer. + * @dev: pointer of net_device of this wireless card + * @info: meta data about Request sent + * @wrqu: include request info + * @extra: buf used for in/out + * + * Return: execute result + */ +static int __iw_softap_get_three(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + uint32_t *value = (uint32_t *)extra; + uint32_t sub_cmd = value[0]; + int ret = 0; /* success */ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret != 0) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + switch (sub_cmd) { + case QCSAP_GET_TSF: + ret = hdd_indicate_tsf(adapter, value, 3); + break; + default: + hdd_err("Invalid getparam command: %d", sub_cmd); + ret = -EINVAL; + break; + } + return ret; +} + + +/** + * iw_softap_get_three() - return three value to upper layer. + * + * @dev: pointer of net_device of this wireless card + * @info: meta data about Request sent + * @wrqu: include request info + * @extra: buf used for in/Output + * + * Return: execute result + */ +static int iw_softap_get_three(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_get_three(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +int +static iw_softap_setparam(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_setparam(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +int +static __iw_softap_getparam(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + int *value = (int *)extra; + int sub_cmd = value[0]; + int ret; + struct hdd_context *hdd_ctx; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + switch (sub_cmd) { + case QCSAP_PARAM_MAX_ASSOC: + if (ucfg_mlme_get_assoc_sta_limit(hdd_ctx->psoc, value) != + QDF_STATUS_SUCCESS) { + hdd_err("CFG_ASSOC_STA_LIMIT failed"); + ret = -EIO; + } + + break; + + case QCSAP_PARAM_GET_WLAN_DBG: + { + qdf_trace_display(); + *value = 0; + break; + } + + case QCSAP_PARAM_RTSCTS: + { + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + VDEV_CMD); + break; + } + + case QCASAP_SHORT_GI: + { + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_SGI, + VDEV_CMD); + break; + } + + case QCSAP_GTX_HT_MCS: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_HT_MCS"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_HT_MCS, + GTX_CMD); + break; + } + + case QCSAP_GTX_VHT_MCS: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_VHT_MCS"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_VHT_MCS, + GTX_CMD); + break; + } + + case QCSAP_GTX_USRCFG: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_USR_CFG"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_USR_CFG, + GTX_CMD); + break; + } + + case QCSAP_GTX_THRE: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_THRE"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_THRE, + GTX_CMD); + break; + } + + case QCSAP_GTX_MARGIN: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_MARGIN"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_MARGIN, + GTX_CMD); + break; + } + + case QCSAP_GTX_STEP: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_STEP"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_STEP, + GTX_CMD); + break; + } + + case QCSAP_GTX_MINTPC: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_MINTPC"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_MINTPC, + GTX_CMD); + break; + } + + case QCSAP_GTX_BWMASK: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_BW_MASK"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_BW_MASK, + GTX_CMD); + break; + } + + case QCASAP_GET_DFS_NOL: + { + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct wlan_objmgr_pdev *pdev; + + pdev = hdd_ctx->pdev; + if (!pdev) { + hdd_err("null pdev"); + return -EINVAL; + } + + utils_dfs_print_nol_channels(pdev); + } + break; + + case QCSAP_GET_ACL: + { + hdd_debug("QCSAP_GET_ACL"); + if (hdd_print_acl(adapter) != + QDF_STATUS_SUCCESS) { + hdd_err("QCSAP_GET_ACL returned Error: not completed"); + } + *value = 0; + break; + } + + case QCASAP_TX_CHAINMASK_CMD: + { + hdd_debug("QCASAP_TX_CHAINMASK_CMD"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_TX_CHAIN_MASK, + PDEV_CMD); + break; + } + + case QCASAP_RX_CHAINMASK_CMD: + { + hdd_debug("QCASAP_RX_CHAINMASK_CMD"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_RX_CHAIN_MASK, + PDEV_CMD); + break; + } + + case QCASAP_NSS_CMD: + { + hdd_debug("QCASAP_NSS_CMD"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_NSS, + VDEV_CMD); + break; + } + case QCSAP_CAP_TSF: + ret = hdd_capture_tsf(adapter, (uint32_t *)value, 1); + break; + case QCASAP_GET_TEMP_CMD: + { + hdd_debug("QCASAP_GET_TEMP_CMD"); + ret = wlan_hdd_get_temperature(adapter, value); + break; + } + case QCSAP_GET_FW_PROFILE_DATA: + hdd_debug("QCSAP_GET_FW_PROFILE_DATA"); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + 0, DBG_CMD); + break; + case QCASAP_PARAM_LDPC: + { + ret = hdd_get_ldpc(adapter, value); + break; + } + case QCASAP_PARAM_TX_STBC: + { + ret = hdd_get_tx_stbc(adapter, value); + break; + } + case QCASAP_PARAM_RX_STBC: + { + ret = hdd_get_rx_stbc(adapter, value); + break; + } + case QCSAP_PARAM_CHAN_WIDTH: + { + ret = hdd_sap_get_chan_width(adapter, value); + break; + } + case QCASAP_PARAM_DCM: + { + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_HE_DCM, + VDEV_CMD); + break; + } + case QCASAP_PARAM_RANGE_EXT: + { + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_HE_RANGE_EXT, + VDEV_CMD); + break; + } + default: + hdd_err("Invalid getparam command: %d", sub_cmd); + ret = -EINVAL; + break; + + } + hdd_exit(); + return ret; +} + +int +static iw_softap_getparam(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_getparam(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/* Usage: + * BLACK_LIST = 0 + * WHITE_LIST = 1 + * ADD MAC = 0 + * REMOVE MAC = 1 + * + * mac addr will be accepted as a 6 octet mac address with each octet + * inputted in hex for e.g. 00:0a:f5:11:22:33 will be represented as + * 0x00 0x0a 0xf5 0x11 0x22 0x33 while using this ioctl + * + * Syntax: + * iwpriv softap.0 modify_acl + * <6 octet mac addr> + * + * Examples: + * eg 1. to add a mac addr 00:0a:f5:89:89:90 to the black list + * iwpriv softap.0 modify_acl 0x00 0x0a 0xf5 0x89 0x89 0x90 0 0 + * eg 2. to delete a mac addr 00:0a:f5:89:89:90 from white list + * iwpriv softap.0 modify_acl 0x00 0x0a 0xf5 0x89 0x89 0x90 1 1 + */ +static +int __iw_softap_modify_acl(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + uint8_t *value = (uint8_t *) extra; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + int list_type, cmd, i; + int ret; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct hdd_context *hdd_ctx; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + for (i = 0; i < QDF_MAC_ADDR_SIZE; i++) + peer_mac[i] = *(value + i); + + list_type = (int)(*(value + i)); + i++; + cmd = (int)(*(value + i)); + + hdd_debug("Modify ACL mac:" QDF_MAC_ADDR_FMT " type: %d cmd: %d", + QDF_MAC_ADDR_REF(peer_mac), list_type, cmd); + + qdf_status = wlansap_modify_acl( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + peer_mac, (eSapACLType) list_type, (eSapACLCmdType) cmd); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Modify ACL failed"); + ret = -EIO; + } + hdd_exit(); + return ret; +} + +static +int iw_softap_modify_acl(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_modify_acl(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +int +static __iw_softap_getchannel(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + struct hdd_context *hdd_ctx; + struct hdd_ap_ctx *ap_ctx; + int *value = (int *)extra; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + *value = 0; + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) + *value = wlan_reg_freq_to_chan( + hdd_ctx->pdev, + ap_ctx->operating_chan_freq); + hdd_exit(); + return 0; +} + +int +static iw_softap_getchannel(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_getchannel(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +int +static __iw_softap_set_max_tx_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + struct hdd_context *hdd_ctx; + int *value = (int *)extra; + int set_value; + int ret; + struct qdf_mac_addr bssid = QDF_MAC_ADDR_BCAST_INIT; + struct qdf_mac_addr selfMac = QDF_MAC_ADDR_BCAST_INIT; + + hdd_enter_dev(dev); + + if (!value) + return -ENOMEM; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + /* Assign correct self MAC address */ + qdf_copy_macaddr(&bssid, &adapter->mac_addr); + qdf_copy_macaddr(&selfMac, &adapter->mac_addr); + + set_value = value[0]; + if (QDF_STATUS_SUCCESS != + sme_set_max_tx_power(hdd_ctx->mac_handle, bssid, + selfMac, set_value)) { + hdd_err("Setting maximum tx power failed"); + return -EIO; + } + hdd_exit(); + return 0; +} + +int +static iw_softap_set_max_tx_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_set_max_tx_power(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifndef REMOVE_PKT_LOG +int +static __iw_softap_set_pktlog(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = netdev_priv(dev); + struct hdd_context *hdd_ctx; + int *value = (int *)extra; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + if (wrqu->data.length < 1 || wrqu->data.length > 2) { + hdd_err("pktlog: either 1 or 2 parameters are required"); + return -EINVAL; + } + + return hdd_process_pktlog_command(hdd_ctx, value[0], value[1]); +} + +int +static iw_softap_set_pktlog(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_set_pktlog(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#else +int +static iw_softap_set_pktlog(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + return -EINVAL; +} +#endif + +int +static __iw_softap_set_tx_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + struct hdd_context *hdd_ctx; + int *value = (int *)extra; + int set_value; + struct qdf_mac_addr bssid; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + qdf_copy_macaddr(&bssid, &adapter->mac_addr); + + set_value = value[0]; + if (QDF_STATUS_SUCCESS != + sme_set_tx_power(hdd_ctx->mac_handle, adapter->vdev_id, bssid, + adapter->device_mode, set_value)) { + hdd_err("Setting tx power failed"); + return -EIO; + } + hdd_exit(); + return 0; +} + +int +static iw_softap_set_tx_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_set_tx_power(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +int +static __iw_softap_getassoc_stamacaddr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + struct hdd_station_info *sta_info; + struct hdd_context *hdd_ctx; + char *buf; + int left; + int ret; + /* maclist_index must be u32 to match userspace */ + u32 maclist_index; + + hdd_enter_dev(dev); + + /* + * NOTE WELL: this is a "get" ioctl but it uses an even ioctl + * number, and even numbered iocts are supposed to have "set" + * semantics. Hence the wireless extensions support in the kernel + * won't correctly copy the result to userspace, so the ioctl + * handler itself must copy the data. Output format is 32-bit + * record length, followed by 0 or more 6-byte STA MAC addresses. + * + * Further note that due to the incorrect semantics, the "iwpriv" + * userspace application is unable to correctly invoke this API, + * hence it is not registered in the hostapd_private_args. This + * API can only be invoked by directly invoking the ioctl() system + * call. + */ + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + /* make sure userspace allocated a reasonable buffer size */ + if (wrqu->data.length < sizeof(maclist_index)) { + hdd_err("invalid userspace buffer"); + return -EINVAL; + } + + /* allocate local buffer to build the response */ + buf = qdf_mem_malloc(wrqu->data.length); + if (!buf) + return -ENOMEM; + + /* start indexing beyond where the record count will be written */ + maclist_index = sizeof(maclist_index); + left = wrqu->data.length - maclist_index; + + hdd_for_each_sta_ref(adapter->sta_info_list, sta_info, + STA_INFO_SAP_GETASSOC_STAMACADDR) { + if (!qdf_is_macaddr_broadcast(&sta_info->sta_mac)) { + memcpy(&buf[maclist_index], &sta_info->sta_mac, + QDF_MAC_ADDR_SIZE); + maclist_index += QDF_MAC_ADDR_SIZE; + left -= QDF_MAC_ADDR_SIZE; + } + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SAP_GETASSOC_STAMACADDR); + } + + *((u32 *) buf) = maclist_index; + wrqu->data.length = maclist_index; + if (copy_to_user(wrqu->data.pointer, buf, maclist_index)) { + hdd_err("failed to copy response to user buffer"); + ret = -EFAULT; + } + qdf_mem_free(buf); + hdd_exit(); + return ret; +} + +int +static iw_softap_getassoc_stamacaddr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_getassoc_stamacaddr(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/* Usage: + * mac addr will be accepted as a 6 octet mac address with each octet + * inputted in hex for e.g. 00:0a:f5:11:22:33 will be represented as + * 0x00 0x0a 0xf5 0x11 0x22 0x33 while using this ioctl + * + * Syntax: + * iwpriv softap.0 disassoc_sta <6 octet mac address> + * + * e.g. + * disassociate sta with mac addr 00:0a:f5:11:22:33 from softap + * iwpriv softap.0 disassoc_sta 0x00 0x0a 0xf5 0x11 0x22 0x33 + */ + +int +static __iw_softap_disassoc_sta(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + struct hdd_context *hdd_ctx; + uint8_t *peer_macaddr; + int ret; + struct csr_del_sta_params del_sta_params; + + hdd_enter_dev(dev); + + if (!capable(CAP_NET_ADMIN)) { + hdd_err("permission check failed"); + return -EPERM; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + /* iwpriv tool or framework calls this ioctl with + * data passed in extra (less than 16 octets); + */ + peer_macaddr = (uint8_t *) (extra); + + hdd_debug("data " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_macaddr)); + wlansap_populate_del_sta_params(peer_macaddr, + eSIR_MAC_DEAUTH_LEAVING_BSS_REASON, + SIR_MAC_MGMT_DISASSOC, + &del_sta_params); + hdd_softap_sta_disassoc(adapter, &del_sta_params); + + hdd_exit(); + return 0; +} + +int +static iw_softap_disassoc_sta(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_disassoc_sta(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_get_char_setnone() - Generic "get char" private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_get_char_setnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int ret; + int sub_cmd = wrqu->data.flags; + struct hdd_context *hdd_ctx; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret != 0) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + switch (sub_cmd) { + case QCSAP_GET_STATS: + hdd_wlan_get_stats(adapter, &(wrqu->data.length), + extra, WE_MAX_STR_LEN); + break; + case QCSAP_LIST_FW_PROFILE: + hdd_wlan_list_fw_profile(&(wrqu->data.length), + extra, WE_MAX_STR_LEN); + break; + } + + hdd_exit(); + return ret; +} + +static int iw_get_char_setnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_get_char_setnone(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int iw_get_channel_list(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + uint32_t num_channels = 0; + uint8_t i = 0; + struct hdd_adapter *hostapd_adapter = (netdev_priv(dev)); + struct channel_list_info *channel_list = + (struct channel_list_info *)extra; + struct regulatory_channel *cur_chan_list = NULL; + struct hdd_context *hdd_ctx; + int ret; + QDF_STATUS status; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(hostapd_adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + cur_chan_list = qdf_mem_malloc(sizeof(*cur_chan_list) * NUM_CHANNELS); + if (!cur_chan_list) + return -ENOMEM; + + status = ucfg_reg_get_current_chan_list(hdd_ctx->pdev, cur_chan_list); + if (status != QDF_STATUS_SUCCESS) { + hdd_err_rl("Failed to get the current channel list"); + qdf_mem_free(cur_chan_list); + return -EIO; + } + + for (i = 0; i < NUM_CHANNELS; i++) { + /* + * current channel list includes all channels. do not report + * disabled channels + */ + if (cur_chan_list[i].chan_flags & REGULATORY_CHAN_DISABLED) + continue; + + /* + * do not include 6 GHz channels since they are ambiguous with + * 2.4 GHz and 5 GHz channels. 6 GHz-aware applications should + * not be using this interface, but instead should be using the + * frequency-based interface + */ + if (wlan_reg_is_6ghz_chan_freq(cur_chan_list[i].center_freq)) + continue; + channel_list->channels[num_channels] = + cur_chan_list[i].chan_num; + num_channels++; + + } + + qdf_mem_free(cur_chan_list); + hdd_debug_rl("number of channels %d", num_channels); + channel_list->num_channels = num_channels; + wrqu->data.length = num_channels + 1; + hdd_exit(); + return 0; +} + +int iw_get_channel_list_with_cc(struct net_device *dev, + mac_handle_t mac_handle, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + uint8_t i, len; + char *buf; + uint8_t ubuf[CFG_COUNTRY_CODE_LEN] = {0}; + uint8_t ubuf_len = CFG_COUNTRY_CODE_LEN; + struct channel_list_info channel_list; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + hdd_enter_dev(dev); + memset(&channel_list, 0, sizeof(channel_list)); + + if (0 != iw_get_channel_list(dev, info, wrqu, (char *)&channel_list)) { + hdd_err_rl("GetChannelList Failed!!!"); + return -EINVAL; + } + buf = extra; + /* + * Maximum channels = WNI_CFG_VALID_CHANNEL_LIST_LEN. + * Maximum buffer needed = 5 * number of channels. + * Check if sufficient buffer is available and then + * proceed to fill the buffer. + */ + if (WE_MAX_STR_LEN < (5 * CFG_VALID_CHANNEL_LIST_LEN)) { + hdd_err_rl("Insufficient Buffer to populate channel list"); + return -EINVAL; + } + len = scnprintf(buf, WE_MAX_STR_LEN, "%u ", channel_list.num_channels); + ucfg_reg_get_cc_and_src(mac->psoc, ubuf); + /* Printing Country code in getChannelList */ + for (i = 0; i < (ubuf_len - 1); i++) + len += scnprintf(buf + len, WE_MAX_STR_LEN - len, "%c", ubuf[i]); + + for (i = 0; i < channel_list.num_channels; i++) + len += scnprintf(buf + len, WE_MAX_STR_LEN - len, " %u", channel_list.channels[i]); + wrqu->data.length = strlen(extra) + 1; + + hdd_exit(); + return 0; +} + +static +int __iw_get_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + struct hdd_context *hdd_ctx; + int ret; + QDF_STATUS status; + uint32_t length = DOT11F_IE_RSN_MAX_LEN; + uint8_t genIeBytes[DOT11F_IE_RSN_MAX_LEN]; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + /* + * Actually retrieve the RSN IE from CSR. + * (We previously sent it down in the CSR Roam Profile.) + */ + status = wlan_sap_getstation_ie_information( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + &length, genIeBytes); + if (status == QDF_STATUS_SUCCESS) { + wrqu->data.length = length; + if (length > DOT11F_IE_RSN_MAX_LEN) { + hdd_err("Invalid buffer length: %d", length); + return -E2BIG; + } + qdf_mem_copy(extra, genIeBytes, length); + hdd_debug(" RSN IE of %d bytes returned", + wrqu->data.length); + } else { + wrqu->data.length = 0; + hdd_debug(" RSN IE failed to populate"); + } + + hdd_exit(); + return 0; +} + +static +int iw_get_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_get_genie(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int +__iw_softap_stopbss(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + QDF_STATUS status; + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + struct hdd_hostapd_state *hostapd_state = + WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + + qdf_event_reset(&hostapd_state->qdf_stop_bss_event); + status = wlansap_stop_bss( + WLAN_HDD_GET_SAP_CTX_PTR(adapter)); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = + qdf_wait_for_event_completion(&hostapd_state-> + qdf_stop_bss_event, + SME_CMD_STOP_BSS_TIMEOUT); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("wait for single_event failed!!"); + QDF_ASSERT(0); + } + } + clear_bit(SOFTAP_BSS_STARTED, &adapter->event_flags); + policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, + adapter->device_mode, + adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, + false); + ret = qdf_status_to_os_return(status); + } + hdd_exit(); + return ret; +} + +static int iw_softap_stopbss(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_stopbss(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int +__iw_softap_version(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = netdev_priv(dev); + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + wrqu->data.length = hdd_wlan_get_version(hdd_ctx, WE_MAX_STR_LEN, + extra); + hdd_exit(); + return 0; +} + +static int iw_softap_version(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_version(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int hdd_softap_get_sta_info(struct hdd_adapter *adapter, + uint8_t *buf, + int size) +{ + int written; + struct hdd_station_info *sta; + + hdd_enter(); + + written = scnprintf(buf, size, "\nstaId staAddress\n"); + + hdd_for_each_sta_ref(adapter->sta_info_list, sta, + STA_INFO_SOFTAP_GET_STA_INFO) { + if (written >= size - 1) { + hdd_put_sta_info_ref(&adapter->sta_info_list, + &sta, true, + STA_INFO_SOFTAP_GET_STA_INFO); + break; + } + + if (QDF_IS_ADDR_BROADCAST(sta->sta_mac.bytes)) { + hdd_put_sta_info_ref(&adapter->sta_info_list, + &sta, true, + STA_INFO_SOFTAP_GET_STA_INFO); + continue; + } + + written += scnprintf(buf + written, size - written, + QDF_FULL_MAC_FMT + " ecsa=%d\n", + QDF_FULL_MAC_REF(sta->sta_mac.bytes), + sta->ecsa_capable); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta, true, + STA_INFO_SOFTAP_GET_STA_INFO); + } + + hdd_exit(); + + return 0; +} + +static int __iw_softap_get_channel_list(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + int ret; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + mac_handle_t mac_handle; + + hdd_enter_dev(dev); + + if (hdd_validate_adapter(adapter)) { + hdd_err_rl("Invalid adapter!!!"); + return -ENODEV; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + mac_handle = hdd_ctx->mac_handle; + + ret = iw_get_channel_list_with_cc(dev, mac_handle, + info, wrqu, extra); + + if (0 != ret) + return -EINVAL; + + hdd_exit(); + return 0; +} + +static int iw_softap_get_channel_list(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_get_channel_list(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int __iw_softap_get_sta_info(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + + hdd_enter_dev(dev); + + adapter = netdev_priv(dev); + errno = hdd_validate_adapter(adapter); + if (errno) + return errno; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + errno = hdd_check_private_wext_control(hdd_ctx, info); + if (errno) + return errno; + + errno = hdd_softap_get_sta_info(adapter, extra, WE_SAP_MAX_STA_INFO); + if (errno) { + hdd_err("Failed to get sta info; errno:%d", errno); + return errno; + } + + wrqu->data.length = strlen(extra); + + hdd_exit(); + + return 0; +} + +static int iw_softap_get_sta_info(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_get_sta_info(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int __iw_softap_get_ba_timeout(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + uint32_t i; + enum qca_wlan_ac_type duration[QCA_WLAN_AC_ALL]; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + + hdd_enter_dev(dev); + + adapter = netdev_priv(dev); + errno = hdd_validate_adapter(adapter); + if (errno) + return errno; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (!soc) { + hdd_err("Invalid handle"); + return -EINVAL; + } + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) + cdp_get_ba_timeout(soc, i, &duration[i]); + + snprintf(extra, WE_SAP_MAX_STA_INFO, + "\n|------------------------------|\n" + "|AC | BA aging timeout duration |\n" + "|--------------------------------|\n" + "|VO | %d |\n" + "|VI | %d |\n" + "|BK | %d |\n" + "|BE | %d |\n" + "|--------------------------------|\n", + duration[QCA_WLAN_AC_VO], duration[QCA_WLAN_AC_VI], + duration[QCA_WLAN_AC_BK], duration[QCA_WLAN_AC_BE]); + + wrqu->data.length = strlen(extra) + 1; + hdd_exit(); + + return 0; +} + +static int iw_softap_get_ba_timeout(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_softap_get_ba_timeout(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static +int __iw_get_softap_linkspeed(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = (netdev_priv(dev)); + struct hdd_context *hdd_ctx; + char *out_link_speed = (char *)extra; + uint32_t link_speed = 0; + int len = sizeof(uint32_t) + 1; + struct qdf_mac_addr mac_address; + char macaddr_string[MAC_ADDRESS_STR_LEN + 1]; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + int rc, ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + hdd_debug("wrqu->data.length(%d)", wrqu->data.length); + + /* Linkspeed is allowed only for P2P mode */ + if (adapter->device_mode != QDF_P2P_GO_MODE) { + hdd_err("Link Speed is not allowed in Device mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -ENOTSUPP; + } + + if (wrqu->data.length >= MAC_ADDRESS_STR_LEN - 1) { + if (copy_from_user(macaddr_string, + wrqu->data.pointer, MAC_ADDRESS_STR_LEN)) { + hdd_err("failed to copy data to user buffer"); + return -EFAULT; + } + macaddr_string[MAC_ADDRESS_STR_LEN - 1] = '\0'; + + if (!mac_pton(macaddr_string, mac_address.bytes)) { + hdd_err("String to Hex conversion Failed"); + return -EINVAL; + } + } + /* If no mac address is passed and/or its length is less than 17, + * link speed for first connected client will be returned. + */ + if (wrqu->data.length < 17 || !QDF_IS_STATUS_SUCCESS(status)) { + struct hdd_station_info *sta_info; + + hdd_for_each_sta_ref(adapter->sta_info_list, sta_info, + STA_INFO_GET_SOFTAP_LINKSPEED) { + if (!qdf_is_macaddr_broadcast(&sta_info->sta_mac)) { + qdf_copy_macaddr(&mac_address, + &sta_info->sta_mac); + status = QDF_STATUS_SUCCESS; + hdd_put_sta_info_ref( + &adapter->sta_info_list, + &sta_info, true, + STA_INFO_GET_SOFTAP_LINKSPEED); + break; + } + hdd_put_sta_info_ref(&adapter->sta_info_list, + &sta_info, true, + STA_INFO_GET_SOFTAP_LINKSPEED); + } + } + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Invalid peer macaddress"); + return -EINVAL; + } + rc = wlan_hdd_get_linkspeed_for_peermac(adapter, &mac_address, + &link_speed); + if (rc) { + hdd_err("Unable to retrieve SME linkspeed"); + return rc; + } + + /* linkspeed in units of 500 kbps */ + link_speed = link_speed / 500; + wrqu->data.length = len; + rc = snprintf(out_link_speed, len, "%u", link_speed); + if ((rc < 0) || (rc >= len)) { + /* encoding or length error? */ + hdd_err("Unable to encode link speed"); + return -EIO; + } + hdd_exit(); + return 0; +} + +static int +iw_get_softap_linkspeed(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_get_softap_linkspeed(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __iw_get_peer_rssi() - get station's rssi + * @dev: net device + * @info: iwpriv request information + * @wrqu: iwpriv command parameter + * @extra + * + * This function will call wlan_cfg80211_mc_cp_stats_get_peer_rssi + * to get rssi + * + * Return: 0 on success, otherwise error value + */ +static int +__iw_get_peer_rssi(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret, i; + struct hdd_context *hddctx; + struct stats_event *rssi_info; + char macaddrarray[MAC_ADDRESS_STR_LEN]; + struct hdd_adapter *adapter = netdev_priv(dev); + struct qdf_mac_addr macaddress = QDF_MAC_ADDR_BCAST_INIT; + + hdd_enter(); + + hddctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hddctx); + if (ret != 0) + return ret; + + ret = hdd_check_private_wext_control(hddctx, info); + if (0 != ret) + return ret; + + hdd_debug("wrqu->data.length= %d", wrqu->data.length); + + if (wrqu->data.length >= MAC_ADDRESS_STR_LEN - 1) { + if (copy_from_user(macaddrarray, + wrqu->data.pointer, + MAC_ADDRESS_STR_LEN - 1)) { + hdd_info("failed to copy data from user buffer"); + return -EFAULT; + } + + macaddrarray[MAC_ADDRESS_STR_LEN - 1] = '\0'; + hdd_debug("%s", macaddrarray); + + if (!mac_pton(macaddrarray, macaddress.bytes)) + hdd_err("String to Hex conversion Failed"); + } + + rssi_info = wlan_cfg80211_mc_cp_stats_get_peer_rssi(adapter->vdev, + macaddress.bytes, + &ret); + if (ret || !rssi_info) { + wlan_cfg80211_mc_cp_stats_free_stats_event(rssi_info); + return ret; + } + + wrqu->data.length = scnprintf(extra, IW_PRIV_SIZE_MASK, "\n"); + for (i = 0; i < rssi_info->num_peer_stats; i++) + wrqu->data.length += + scnprintf(extra + wrqu->data.length, + IW_PRIV_SIZE_MASK - wrqu->data.length, + "["QDF_FULL_MAC_FMT"] [%d]\n", + QDF_FULL_MAC_REF(rssi_info->peer_stats[i].peer_macaddr), + rssi_info->peer_stats[i].peer_rssi); + + wrqu->data.length++; + wlan_cfg80211_mc_cp_stats_free_stats_event(rssi_info); + hdd_exit(); + + return 0; +} + +/** + * iw_get_peer_rssi() - get station's rssi + * @dev: net device + * @info: iwpriv request information + * @wrqu: iwpriv command parameter + * @extra + * + * This function will call __iw_get_peer_rssi + * + * Return: 0 on success, otherwise error value + */ +static int +iw_get_peer_rssi(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_get_peer_rssi(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/* + * Note that the following ioctls were defined with semantics which + * cannot be handled by the "iwpriv" userspace application and hence + * they are not included in the hostapd_private_args array + * QCSAP_IOCTL_ASSOC_STA_MACADDR + */ + +static const struct iw_priv_args hostapd_private_args[] = { + { + QCSAP_IOCTL_SETPARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "setparam" + }, { + QCSAP_IOCTL_SETPARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" + }, { + QCSAP_PARAM_MAX_ASSOC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "setMaxAssoc" + }, { + QCSAP_PARAM_HIDE_SSID, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hideSSID" + }, { + QCSAP_PARAM_SET_MC_RATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "setMcRate" + }, { + QCSAP_PARAM_SET_TXRX_FW_STATS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "txrx_fw_stats" + }, { + QCSAP_PARAM_SET_TXRX_STATS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, + "txrx_stats" + }, { + QCSAP_PARAM_SET_MCC_CHANNEL_LATENCY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "setMccLatency" + }, { + QCSAP_PARAM_SET_MCC_CHANNEL_QUOTA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "setMccQuota" + }, { + QCSAP_PARAM_SET_CHANNEL_CHANGE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "setChanChange" + }, { + QCSAP_PARAM_CONC_SYSTEM_PREF, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "setConcSysPref" + }, +#ifdef FEATURE_FW_LOG_PARSING + /* Sub-cmds DBGLOG specific commands */ + { + QCSAP_DBGLOG_LOG_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "dl_loglevel" + }, { + QCSAP_DBGLOG_VAP_ENABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dl_vapon" + }, { + QCSAP_DBGLOG_VAP_DISABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "dl_vapoff" + }, { + QCSAP_DBGLOG_MODULE_ENABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dl_modon" + }, { + QCSAP_DBGLOG_MODULE_DISABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "dl_modoff" + }, { + QCSAP_DBGLOG_MOD_LOG_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "dl_mod_loglevel" + }, { + QCSAP_DBGLOG_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dl_type" + }, { + QCSAP_DBGLOG_REPORT_ENABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "dl_report" + }, +#endif /* FEATURE_FW_LOG_PARSING */ + { + + QCASAP_TXRX_FWSTATS_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "txrx_fw_st_rst" + }, { + QCSAP_PARAM_RTSCTS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "enablertscts" + }, { + QCASAP_SET_11N_RATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set11NRates" + }, { + QCASAP_SET_VHT_RATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set11ACRates" + }, { + QCASAP_SHORT_GI, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "enable_short_gi" + }, { + QCSAP_SET_AMPDU, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ampdu" + }, { + QCSAP_SET_AMSDU, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "amsdu" + }, { + QCSAP_GTX_HT_MCS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "gtxHTMcs" + }, { + QCSAP_GTX_VHT_MCS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "gtxVHTMcs" + }, { + QCSAP_GTX_USRCFG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "gtxUsrCfg" + }, { + QCSAP_GTX_THRE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "gtxThre" + }, { + QCSAP_GTX_MARGIN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "gtxMargin" + }, { + QCSAP_GTX_STEP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "gtxStep" + }, { + QCSAP_GTX_MINTPC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "gtxMinTpc" + }, { + QCSAP_GTX_BWMASK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "gtxBWMask" + }, { + QCSAP_PARAM_CLR_ACL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setClearAcl" + }, { + QCSAP_PARAM_ACL_MODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "setAclMode" + }, + { + QCASAP_SET_TM_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setTmLevel" + }, { + QCASAP_SET_DFS_IGNORE_CAC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setDfsIgnoreCAC" + }, { + QCASAP_SET_DFS_NOL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setdfsnol" + }, { + QCASAP_SET_DFS_TARGET_CHNL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setNextChnl" + }, { + QCASAP_SET_RADAR_CMD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "setRadar" + }, + { + QCSAP_IPA_UC_STAT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ipaucstat" + }, + { + QCASAP_TX_CHAINMASK_CMD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_txchainmask" + }, { + QCASAP_RX_CHAINMASK_CMD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_rxchainmask" + }, { + QCASAP_SET_HE_BSS_COLOR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_he_bss_clr" + }, { + QCASAP_NSS_CMD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_nss" + }, { + QCASAP_SET_PHYMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setphymode" + }, { + QCASAP_DUMP_STATS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "dumpStats" + }, { + QCASAP_CLEAR_STATS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "clearStats" + }, { + QCSAP_START_FW_PROFILING, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "startProfile" + }, { + QCASAP_PARAM_LDPC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "ldpc" + }, { + QCASAP_PARAM_TX_STBC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_tx_stbc" + }, { + QCASAP_PARAM_RX_STBC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_rx_stbc" + }, { + QCSAP_IOCTL_GETPARAM, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getparam" + }, { + QCSAP_IOCTL_GETPARAM, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" + }, { + QCSAP_PARAM_MAX_ASSOC, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getMaxAssoc" + }, { + QCSAP_PARAM_GET_WLAN_DBG, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwlandbg" + }, { + QCSAP_GTX_BWMASK, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxBWMask" + }, { + QCSAP_GTX_MINTPC, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxMinTpc" + }, { + QCSAP_GTX_STEP, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxStep" + }, { + QCSAP_GTX_MARGIN, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxMargin" + }, { + QCSAP_GTX_THRE, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxThre" + }, { + QCSAP_GTX_USRCFG, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxUsrCfg" + }, { + QCSAP_GTX_VHT_MCS, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxVHTMcs" + }, { + QCSAP_GTX_HT_MCS, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxHTMcs" + }, { + QCASAP_SHORT_GI, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_short_gi" + }, { + QCSAP_PARAM_RTSCTS, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rtscts" + }, { + QCASAP_GET_DFS_NOL, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdfsnol" + }, { + QCSAP_GET_ACL, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_acl_list" + }, { + QCASAP_PARAM_LDPC, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_ldpc" + }, { + QCASAP_PARAM_TX_STBC, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_tx_stbc" + }, { + QCASAP_PARAM_RX_STBC, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_rx_stbc" + }, { + QCSAP_PARAM_CHAN_WIDTH, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_chwidth" + }, { + QCASAP_TX_CHAINMASK_CMD, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_txchainmask" + }, { + QCASAP_RX_CHAINMASK_CMD, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_rxchainmask" + }, { + QCASAP_NSS_CMD, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_nss" + }, { + QCSAP_CAP_TSF, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "cap_tsf" + }, { + QCSAP_IOCTL_SET_NONE_GET_THREE, 0, IW_PRIV_TYPE_INT | + IW_PRIV_SIZE_FIXED | 3, "" + }, { + QCSAP_GET_TSF, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, + "get_tsf" + }, { + QCASAP_GET_TEMP_CMD, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_temp" + }, { + QCSAP_GET_FW_PROFILE_DATA, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getProfileData" + }, { + QCSAP_IOCTL_GET_STAWPAIE, + 0, IW_PRIV_TYPE_BYTE | DOT11F_IE_RSN_MAX_LEN, + "get_staWPAIE" + }, { + QCSAP_IOCTL_STOPBSS, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED, 0, + "stopbss" + }, { + QCSAP_IOCTL_VERSION, 0, IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "version" + }, { + QCSAP_IOCTL_GET_STA_INFO, 0, + IW_PRIV_TYPE_CHAR | WE_SAP_MAX_STA_INFO, "get_sta_info" + }, { + QCSAP_IOCTL_GET_CHANNEL, 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getchannel" + }, { + QCSAP_IOCTL_GET_BA_AGEING_TIMEOUT, 0, + IW_PRIV_TYPE_CHAR | WE_SAP_MAX_STA_INFO, "get_ba_timeout" + }, { + QCSAP_IOCTL_DISASSOC_STA, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 6, 0, + "disassoc_sta" + } + /* handler for main ioctl */ + , { + QCSAP_PRIV_GET_CHAR_SET_NONE, 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, "" + } + /* handler for sub-ioctl */ + , { + QCSAP_GET_STATS, 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, "getStats" + } + , { + QCSAP_LIST_FW_PROFILE, 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, "listProfile" + } + , { + QCSAP_IOCTL_PRIV_GET_SOFTAP_LINK_SPEED, + IW_PRIV_TYPE_CHAR | 18, + IW_PRIV_TYPE_CHAR | 5, "getLinkSpeed" + } + , { + QCSAP_IOCTL_PRIV_GET_RSSI, + IW_PRIV_TYPE_CHAR | 18, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, "getRSSI" + } + , { + QCSAP_IOCTL_PRIV_SET_THREE_INT_GET_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0, "" + } + , + /* handlers for sub-ioctl */ + { + WE_SET_WLAN_DBG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0, "setwlandbg" + } + , +#ifdef CONFIG_DP_TRACE + /* handlers for sub-ioctl */ + { + WE_SET_DP_TRACE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0, "set_dp_trace" + } + , +#endif + /* handlers for main ioctl */ + { + QCSAP_IOCTL_PRIV_SET_VAR_INT_GET_NONE, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, 0, "" + } + , { + WE_P2P_NOA_CMD, IW_PRIV_TYPE_INT | MAX_VAR_ARGS, 0, "SetP2pPs" + } + , { + WE_UNIT_TEST_CMD, IW_PRIV_TYPE_INT | MAX_VAR_ARGS, 0, + "setUnitTestCmd" + } +#ifdef WLAN_DEBUG + , + { + WE_SET_CHAN_AVOID, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "ch_avoid" + } +#endif + , + { + QCSAP_SET_BTCOEX_MODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_btc_mode" + } + , + { + QCSAP_SET_BTCOEX_LOW_RSSI_THRESHOLD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_btc_rssi" + } + , +#ifdef FW_THERMAL_THROTTLE_SUPPORT + { + WE_SET_THERMAL_THROTTLE_CFG, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, "set_thermal_cfg" + } + , +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + /* handlers for main ioctl */ + { + QCSAP_IOCTL_MODIFY_ACL, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 8, 0, "modify_acl" + } + , + /* handlers for main ioctl */ + { + QCSAP_IOCTL_GET_CHANNEL_LIST, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getChannelList" + } + , + /* handlers for main ioctl */ + { + QCSAP_IOCTL_SET_TX_POWER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "setTxPower" + } + , + /* handlers for main ioctl */ + { + QCSAP_IOCTL_SET_MAX_TX_POWER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setTxMaxPower" + } + , + { + QCSAP_IOCTL_SET_PKTLOG, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, "pktlog" + } + , + /* Get HDD CFG Ini param */ + { + QCSAP_IOCTL_GET_INI_CFG, + 0, IW_PRIV_TYPE_CHAR | QCSAP_IOCTL_MAX_STR_LEN, "getConfig" + } + , + /* handlers for main ioctl */ + { + /* handlers for main ioctl */ + QCSAP_IOCTL_SET_TWO_INT_GET_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "" + } + , + /* handlers for sub-ioctl */ +#ifdef CONFIG_WLAN_DEBUG_CRASH_INJECT + { + QCSAP_IOCTL_SET_FW_CRASH_INJECT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "crash_inject" + } + , +#endif + { + QCASAP_SET_RADAR_DBG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setRadarDbg" + } + , +#ifdef CONFIG_DP_TRACE + /* dump dp trace - descriptor or dp trace records */ + { + QCSAP_IOCTL_DUMP_DP_TRACE_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "dump_dp_trace" + } + , +#endif + { + QCSAP_ENABLE_FW_PROFILE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "enableProfile" + } + , + { + QCSAP_SET_FW_PROFILE_HIST_INTVL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "set_hist_intvl" + } + , +#ifdef WLAN_SUSPEND_RESUME_TEST + { + QCSAP_SET_WLAN_SUSPEND, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "wlan_suspend" + } + , + { + QCSAP_SET_WLAN_RESUME, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "wlan_resume" + } + , +#endif + { + QCSAP_SET_BA_AGEING_TIMEOUT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "set_ba_timeout" + } + , + { + QCASAP_SET_11AX_RATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_11ax_rate" + } + , + { + QCASAP_PARAM_DCM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "enable_dcm" + } + , + { + QCASAP_PARAM_RANGE_EXT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "range_ext" + } + , + { QCSAP_SET_DEFAULT_AMPDU, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "def_ampdu" + } + , + { QCSAP_ENABLE_RTS_BURSTING, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "rts_bursting" + } + , +}; + +static const iw_handler hostapd_private[] = { + /* set priv ioctl */ + [QCSAP_IOCTL_SETPARAM - SIOCIWFIRSTPRIV] = iw_softap_setparam, + /* get priv ioctl */ + [QCSAP_IOCTL_GETPARAM - SIOCIWFIRSTPRIV] = iw_softap_getparam, + [QCSAP_IOCTL_SET_NONE_GET_THREE - SIOCIWFIRSTPRIV] = + iw_softap_get_three, + /* get station genIE */ + [QCSAP_IOCTL_GET_STAWPAIE - SIOCIWFIRSTPRIV] = iw_get_genie, + /* stop bss */ + [QCSAP_IOCTL_STOPBSS - SIOCIWFIRSTPRIV] = iw_softap_stopbss, + /* get driver version */ + [QCSAP_IOCTL_VERSION - SIOCIWFIRSTPRIV] = iw_softap_version, + [QCSAP_IOCTL_GET_CHANNEL - SIOCIWFIRSTPRIV] = + iw_softap_getchannel, + [QCSAP_IOCTL_ASSOC_STA_MACADDR - SIOCIWFIRSTPRIV] = + iw_softap_getassoc_stamacaddr, + [QCSAP_IOCTL_DISASSOC_STA - SIOCIWFIRSTPRIV] = + iw_softap_disassoc_sta, + [QCSAP_PRIV_GET_CHAR_SET_NONE - SIOCIWFIRSTPRIV] = + iw_get_char_setnone, + [QCSAP_IOCTL_PRIV_SET_THREE_INT_GET_NONE - + SIOCIWFIRSTPRIV] = + iw_set_three_ints_getnone, + [QCSAP_IOCTL_PRIV_SET_VAR_INT_GET_NONE - + SIOCIWFIRSTPRIV] = + iw_set_var_ints_getnone, + [QCSAP_IOCTL_MODIFY_ACL - SIOCIWFIRSTPRIV] = + iw_softap_modify_acl, + [QCSAP_IOCTL_GET_CHANNEL_LIST - SIOCIWFIRSTPRIV] = + iw_softap_get_channel_list, + [QCSAP_IOCTL_GET_STA_INFO - SIOCIWFIRSTPRIV] = + iw_softap_get_sta_info, + [QCSAP_IOCTL_GET_BA_AGEING_TIMEOUT - SIOCIWFIRSTPRIV] = + iw_softap_get_ba_timeout, + [QCSAP_IOCTL_PRIV_GET_SOFTAP_LINK_SPEED - + SIOCIWFIRSTPRIV] = + iw_get_softap_linkspeed, + [QCSAP_IOCTL_PRIV_GET_RSSI - SIOCIWFIRSTPRIV] = + iw_get_peer_rssi, + [QCSAP_IOCTL_SET_TX_POWER - SIOCIWFIRSTPRIV] = + iw_softap_set_tx_power, + [QCSAP_IOCTL_SET_MAX_TX_POWER - SIOCIWFIRSTPRIV] = + iw_softap_set_max_tx_power, + [QCSAP_IOCTL_SET_PKTLOG - SIOCIWFIRSTPRIV] = + iw_softap_set_pktlog, + [QCSAP_IOCTL_GET_INI_CFG - SIOCIWFIRSTPRIV] = + iw_softap_get_ini_cfg, + [QCSAP_IOCTL_SET_TWO_INT_GET_NONE - SIOCIWFIRSTPRIV] = + iw_softap_set_two_ints_getnone, +}; + +const struct iw_handler_def hostapd_handler_def = { + .num_standard = 0, + .num_private = QDF_ARRAY_SIZE(hostapd_private), + .num_private_args = QDF_ARRAY_SIZE(hostapd_private_args), + .standard = NULL, + .private = (iw_handler *)hostapd_private, + .private_args = hostapd_private_args, + .get_wireless_stats = NULL, +}; + +/** + * hdd_register_wext() - register wext context + * @dev: net device handle + * + * Registers wext interface context for a given net device + * + * Returns: 0 on success, errno on failure + */ +void hdd_register_hostapd_wext(struct net_device *dev) +{ + hdd_enter_dev(dev); + /* Register as a wireless device */ + dev->wireless_handlers = (struct iw_handler_def *)&hostapd_handler_def; + + hdd_exit(); +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd_wext.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd_wext.h new file mode 100644 index 0000000000000000000000000000000000000000..d5d48c849d22adad3097bb1b7017d0b6d9f9cce4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hostapd_wext.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_HDD_HOSTAPD_WEXT_H +#define WLAN_HDD_HOSTAPD_WEXT_H + +/** + * DOC: wlan_hdd_hostapd_wext.h + * + * WLAN Host Device driver hostapd wext header file + */ + +/* Include files */ + +#include + +/* Preprocessor definitions and constants */ +#ifdef WLAN_WEXT_SUPPORT_ENABLE +void hdd_register_hostapd_wext(struct net_device *dev); +#else +static inline void hdd_register_hostapd_wext(struct net_device *dev) +{ +} +#endif + +#endif /* end #ifndef WLAN_HDD_HOSTAPD_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hw_capability.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hw_capability.c new file mode 100644 index 0000000000000000000000000000000000000000..9898c6a6e491bf4d9026f3ca1277d0b3cd1ee186 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hw_capability.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_hw_capability.c + * + * The implementation of get hw capability + * + */ + +#include "wlan_hdd_main.h" +#include "wmi_unified_param.h" +#include "wlan_hdd_hw_capability.h" +#include "qca_vendor.h" +#include "wlan_osif_request_manager.h" +#include "osif_sync.h" + +/** + * hdd_get_isolation_cb - Callback function to get isolation information + * @context: opaque context originally passed to SME. HDD always passes + * a cookie for the request context + * @isolation: pointer of isolation information + * + * This function will fill isolation information to isolation priv adapter + * + * Return: None + */ +static void hdd_get_isolation_cb(struct sir_isolation_resp *isolation, + void *context) +{ + struct osif_request *request; + struct sir_isolation_resp *priv; + + if (!isolation) { + hdd_err("Bad param"); + return; + } + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + priv->isolation_chain0 = isolation->isolation_chain0; + priv->isolation_chain1 = isolation->isolation_chain1; + priv->isolation_chain2 = isolation->isolation_chain2; + priv->isolation_chain3 = isolation->isolation_chain3; + + osif_request_complete(request); + osif_request_put(request); +} + +/** + * hdd_post_isolation - send rsp to user space + * @hdd_ctx: pointer to hdd context + * @isolation: antenna isolation information + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_post_isolation(struct hdd_context *hdd_ctx, + struct sir_isolation_resp *isolation) +{ + struct sk_buff *skb; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, + (sizeof(u8) + NLA_HDRLEN) + + (sizeof(u8) + NLA_HDRLEN) + + (sizeof(u8) + NLA_HDRLEN) + + (sizeof(u8) + NLA_HDRLEN) + + NLMSG_HDRLEN); + + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return -ENOMEM; + } + + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION, + isolation->isolation_chain0)) { + hdd_err("put fail"); + goto nla_put_failure; + } + + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION, + isolation->isolation_chain1)) { + hdd_err("put fail"); + goto nla_put_failure; + } + + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION, + isolation->isolation_chain2)) { + hdd_err("put fail"); + goto nla_put_failure; + } + + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION, + isolation->isolation_chain3)) { + hdd_err("put fail"); + goto nla_put_failure; + } + + cfg80211_vendor_cmd_reply(skb); + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * __wlan_hdd_cfg80211_get_hw_capability() - get hw capability + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Return: 0 on success; error number otherwise. + * + */ +static int __wlan_hdd_cfg80211_get_hw_capability(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct osif_request *request; + struct sir_isolation_resp *priv; + mac_handle_t mac_handle; + void *cookie; + QDF_STATUS status; + int ret; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_ANTENNA_ISOLATION, + }; + + hdd_enter(); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + + cookie = osif_request_cookie(request); + + mac_handle = hdd_ctx->mac_handle; + status = sme_get_isolation(mac_handle, + cookie, + hdd_get_isolation_cb); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to retrieve isolation"); + ret = -EFAULT; + } else { + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving isolation"); + ret = -ETIMEDOUT; + } else { + priv = osif_request_priv(request); + hdd_post_isolation(hdd_ctx, priv); + ret = 0; + } + } + osif_request_put(request); + + return ret; +} + +int wlan_hdd_cfg80211_get_hw_capability(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_hw_capability(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hw_capability.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hw_capability.h new file mode 100644 index 0000000000000000000000000000000000000000..0cbdc071a54ab62c2d5ac65b287fbb1118a42ace --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_hw_capability.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_hw_capability.h + * + * Add Vendor subcommand QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY + */ + +#ifndef __WLAN_HDD_HW_CAPABILITY_H +#define __WLAN_HDD_HW_CAPABILITY_H + +#ifdef FEATURE_HW_CAPABILITY +#include + +/** + * wlan_hdd_cfg80211_get_hw_capability() - get hardware capability + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Return: 0 on success; error number otherwise. + */ +int wlan_hdd_cfg80211_get_hw_capability(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_HW_CAPABILITY_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY,\ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_get_hw_capability \ +}, +#else /* FEATURE_HW_CAPABILITY */ +#define FEATURE_HW_CAPABILITY_COMMANDS +#endif /* FEATURE_HW_CAPABILITY */ + +#endif /* __WLAN_HDD_HW_CAPABILITY_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ioctl.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ioctl.c new file mode 100644 index 0000000000000000000000000000000000000000..41b306fd07e3c28320213adeeb76b03777ad313c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ioctl.c @@ -0,0 +1,8518 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* Include Files */ + +#include "osif_sync.h" +#include +#include +#include +#include "cfg_ucfg_api.h" +#include "wlan_hdd_trace.h" +#include "wlan_hdd_ioctl.h" +#include "wlan_hdd_power.h" +#include "wlan_hdd_regulatory.h" +#include "wlan_osif_request_manager.h" +#include "wlan_hdd_driver_ops.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_hdd_hostapd.h" +#include "scheduler_api.h" +#include "wlan_reg_ucfg_api.h" +#include "wlan_hdd_p2p.h" +#include +#include "wma.h" +#include "wlan_hdd_napi.h" +#include "wlan_mlme_ucfg_api.h" +#include "target_type.h" +#ifdef FEATURE_WLAN_ESE +#include +#include +#endif +#include "hif.h" +#include "wlan_scan_ucfg_api.h" +#include "wlan_reg_ucfg_api.h" +#include "qdf_func_tracker.h" + +#if defined(LINUX_QCMBR) +#define SIOCIOCTLTX99 (SIOCDEVPRIVATE+13) +#endif + +/* + * Size of Driver command strings from upper layer + */ +#define SIZE_OF_SETROAMMODE 11 /* size of SETROAMMODE */ +#define SIZE_OF_GETROAMMODE 11 /* size of GETROAMMODE */ +#define SIZE_OF_SETSUSPENDMODE 14 + +/* + * Size of GETCOUNTRYREV output = (sizeof("GETCOUNTRYREV") = 14) + one (space) + + * (sizeof("country_code") = 3) + + * one (NULL terminating character) + */ +#define SIZE_OF_GETCOUNTRYREV_OUTPUT 20 + +#ifdef FEATURE_WLAN_ESE +#define TID_MIN_VALUE 0 +#define TID_MAX_VALUE 15 +#endif /* FEATURE_WLAN_ESE */ + +/* + * Maximum buffer size used for returning the data back to user space + */ +#define WLAN_MAX_BUF_SIZE 1024 +#define WLAN_PRIV_DATA_MAX_LEN 8192 + +/* + * Driver miracast parameters 0-Disabled + * 1-Source, 2-Sink + */ +#define WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL 0 +#define WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL 2 + +/* + * When ever we need to print IBSSPEERINFOALL for more than 16 STA + * we will split the printing. + */ +#define NUM_OF_STA_DATA_TO_PRINT 16 + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +/** + * struct enable_ext_wow_priv - Private data structure for ext wow + * @ext_wow_should_suspend: Suspend status of ext wow + */ +struct enable_ext_wow_priv { + bool ext_wow_should_suspend; +}; +#endif + +/* + * Android DRIVER command structures + */ +struct android_wifi_reassoc_params { + unsigned char bssid[18]; + int channel; +}; + +#define ANDROID_WIFI_ACTION_FRAME_SIZE 1040 +struct android_wifi_af_params { + unsigned char bssid[18]; + int channel; + int dwell_time; + int len; + unsigned char data[ANDROID_WIFI_ACTION_FRAME_SIZE]; +}; + +/* + * Define HDD driver command handling entry, each contains a command + * string and the handler. + */ +typedef int (*hdd_drv_cmd_handler_t)(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *cmd, + uint8_t cmd_name_len, + struct hdd_priv_data *priv_data); + +/** + * struct hdd_drv_cmd - Structure to store ioctl command handling info + * @cmd: Name of the command + * @handler: Command handler to be invoked + * @args: Set to true if command expects input parameters + */ +struct hdd_drv_cmd { + const char *cmd; + hdd_drv_cmd_handler_t handler; + bool args; +}; + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +#define WLAN_WAIT_TIME_READY_TO_EXTWOW 2000 +#define WLAN_HDD_MAX_TCP_PORT 65535 +#endif + +/** + * drv_cmd_validate() - Validates for space in hdd driver command + * @command: pointer to input data (its a NULL terminated string) + * @len: length of command name + * + * This function checks for space after command name and if no space + * is found returns error. + * + * Return: 0 for success non-zero for failure + */ +static int drv_cmd_validate(uint8_t *command, int len) +{ + if (command[len] != ' ') + return -EINVAL; + + return 0; +} + +#ifdef FEATURE_WLAN_ESE +struct tsm_priv { + tAniTrafStrmMetrics tsm_metrics; +}; + +static void hdd_get_tsm_stats_cb(tAniTrafStrmMetrics tsm_metrics, + void *context) +{ + struct osif_request *request; + struct tsm_priv *priv; + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + priv = osif_request_priv(request); + priv->tsm_metrics = tsm_metrics; + osif_request_complete(request); + osif_request_put(request); + hdd_exit(); + +} + +static int hdd_get_tsm_stats(struct hdd_adapter *adapter, + const uint8_t tid, + tAniTrafStrmMetrics *tsm_metrics) +{ + struct hdd_context *hdd_ctx; + struct hdd_station_ctx *hdd_sta_ctx; + QDF_STATUS status; + int ret; + void *cookie; + struct osif_request *request; + struct tsm_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + + if (!adapter) { + hdd_err("adapter is NULL"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + status = sme_get_tsm_stats(hdd_ctx->mac_handle, hdd_get_tsm_stats_cb, + hdd_sta_ctx->conn_info.bssid, + cookie, tid); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Unable to retrieve tsm statistics"); + ret = qdf_status_to_os_return(status); + goto cleanup; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving tsm statistics"); + goto cleanup; + } + + priv = osif_request_priv(request); + *tsm_metrics = priv->tsm_metrics; + + cleanup: + osif_request_put(request); + + return ret; +} +#endif /*FEATURE_WLAN_ESE */ + +#ifdef QCA_IBSS_SUPPORT +/* + * Ibss prop IE from command will be of size: + * size = sizeof(oui) + sizeof(oui_data) + 1(Element ID) + 1(EID Length) + * OUI_DATA should be at least 3 bytes long + */ +#define WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH (3) +static uint16_t cesium_pid; + +void +hdd_get_ibss_peer_info_cb(void *context, + tSirPeerInfoRspParams *peer_info) +{ + struct hdd_adapter *adapter = context; + struct hdd_station_ctx *sta_ctx; + uint8_t i; + + if ((!adapter) || + (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("invalid adapter or adapter has invalid magic"); + return; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (peer_info && QDF_STATUS_SUCCESS == peer_info->status) { + /* validate number of peers */ + if (peer_info->numPeers > SIR_MAX_NUM_STA_IN_IBSS) { + hdd_warn("Limiting num_peers %u to %u", + peer_info->numPeers, SIR_MAX_NUM_STA_IN_IBSS); + peer_info->numPeers = SIR_MAX_NUM_STA_IN_IBSS; + } + sta_ctx->ibss_peer_info.status = peer_info->status; + sta_ctx->ibss_peer_info.numPeers = peer_info->numPeers; + + for (i = 0; i < peer_info->numPeers; i++) + sta_ctx->ibss_peer_info.peerInfoParams[i] = + peer_info->peerInfoParams[i]; + } else { + hdd_debug("peerInfo %s: status %u, numPeers %u", + peer_info ? "valid" : "null", + peer_info ? peer_info->status : QDF_STATUS_E_FAILURE, + peer_info ? peer_info->numPeers : 0); + sta_ctx->ibss_peer_info.numPeers = 0; + sta_ctx->ibss_peer_info.status = QDF_STATUS_E_FAILURE; + } + + complete(&adapter->ibss_peer_info_comp); +} + +/** + * hdd_cfg80211_get_ibss_peer_info_all() - get ibss peers' info + * @adapter: Adapter context + * + * Request function to get IBSS peer info from lower layers + * + * Return: 0 for success non-zero for failure + */ +static +QDF_STATUS hdd_cfg80211_get_ibss_peer_info_all(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + unsigned long rc; + struct qdf_mac_addr bcast = QDF_MAC_ADDR_BCAST_INIT; + + INIT_COMPLETION(adapter->ibss_peer_info_comp); + + status = sme_request_ibss_peer_info(adapter->hdd_ctx->mac_handle, + adapter, + hdd_get_ibss_peer_info_cb, + true, bcast.bytes); + + if (QDF_STATUS_SUCCESS == status) { + rc = wait_for_completion_timeout + (&adapter->ibss_peer_info_comp, + msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT)); + + /* status will be 0 if timed out */ + if (!rc) { + hdd_warn("Warning: IBSS_PEER_INFO_TIMEOUT"); + status = QDF_STATUS_E_FAILURE; + } + } else { + hdd_warn("Warning: sme_request_ibss_peer_info Request failed"); + } + + return status; +} + +/** + * hdd_cfg80211_get_ibss_peer_info() - get ibss peer info + * @adapter: Adapter context + * @sta_id: Sta index for which the peer info is requested + * + * Request function to get IBSS peer info from lower layers + * + * Return: 0 for success non-zero for failure + */ +static QDF_STATUS +hdd_cfg80211_get_ibss_peer_info(struct hdd_adapter *adapter, uint8_t *mac_addr) +{ + unsigned long rc; + QDF_STATUS status; + + INIT_COMPLETION(adapter->ibss_peer_info_comp); + + status = sme_request_ibss_peer_info(adapter->hdd_ctx->mac_handle, + adapter, + hdd_get_ibss_peer_info_cb, + false, mac_addr); + + if (QDF_STATUS_SUCCESS == status) { + rc = wait_for_completion_timeout( + &adapter->ibss_peer_info_comp, + msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT)); + + /* status = 0 on timeout */ + if (!rc) { + hdd_warn("Warning: IBSS_PEER_INFO_TIMEOUT"); + status = QDF_STATUS_E_FAILURE; + } + } else { + hdd_warn("Warning: sme_request_ibss_peer_info Request failed"); + } + + return status; +} + +/* Function header is left blank intentionally */ +static QDF_STATUS +hdd_parse_get_ibss_peer_info(uint8_t *command, + struct qdf_mac_addr *peer_macaddr) +{ + uint8_t *in_ptr = command; + size_t in_ptr_len = strlen(command); + + in_ptr = strnchr(command, in_ptr_len, SPACE_ASCII_VALUE); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + else if (SPACE_ASCII_VALUE != *in_ptr) + return QDF_STATUS_E_FAILURE; + + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + if ('\0' == *in_ptr) + return QDF_STATUS_E_FAILURE; + + in_ptr_len -= (in_ptr - command); + if (in_ptr_len < 17) + return QDF_STATUS_E_FAILURE; + + if (in_ptr[2] != ':' || in_ptr[5] != ':' || in_ptr[8] != ':' || + in_ptr[11] != ':' || in_ptr[14] != ':') + return QDF_STATUS_E_FAILURE; + + sscanf(in_ptr, "%2x:%2x:%2x:%2x:%2x:%2x", + (unsigned int *)&peer_macaddr->bytes[0], + (unsigned int *)&peer_macaddr->bytes[1], + (unsigned int *)&peer_macaddr->bytes[2], + (unsigned int *)&peer_macaddr->bytes[3], + (unsigned int *)&peer_macaddr->bytes[4], + (unsigned int *)&peer_macaddr->bytes[5]); + + return QDF_STATUS_SUCCESS; +} + +static void hdd_tx_fail_ind_callback(uint8_t *macaddr, uint8_t seq_no) +{ + int payload_len; + struct sk_buff *skb; + struct nlmsghdr *nlh; + uint8_t *data; + + payload_len = ETH_ALEN; + + if (0 == cesium_pid || !cesium_nl_srv_sock) { + hdd_err("cesium process not registered"); + return; + } + + skb = nlmsg_new(payload_len, GFP_ATOMIC); + if (!skb) { + hdd_err("nlmsg_new() failed for msg size[%d]", + NLMSG_SPACE(payload_len)); + return; + } + + nlh = nlmsg_put(skb, cesium_pid, seq_no, 0, payload_len, NLM_F_REQUEST); + + if (!nlh) { + hdd_err("nlmsg_put() failed for msg size[%d]", + NLMSG_SPACE(payload_len)); + + kfree_skb(skb); + return; + } + + data = nlmsg_data(nlh); + memcpy(data, macaddr, ETH_ALEN); + + if (nlmsg_unicast(cesium_nl_srv_sock, skb, cesium_pid) < 0) { + hdd_err("nlmsg_unicast() failed for msg size[%d]", + NLMSG_SPACE(payload_len)); + } +} + +/** + * hdd_parse_user_params() - return a pointer to the next argument + * @command: Input argument string + * @arg: Output pointer to the next argument + * + * This function parses argument stream and finds the pointer + * to the next argument + * + * Return: 0 if the next argument found; -EINVAL otherwise + */ +static int hdd_parse_user_params(uint8_t *command, uint8_t **arg) +{ + uint8_t *cursor; + + cursor = strnchr(command, strlen(command), SPACE_ASCII_VALUE); + + /* no argument remains ? */ + if (!cursor) + return -EINVAL; + + /* no space after the current arg ? */ + if (SPACE_ASCII_VALUE != *cursor) + return -EINVAL; + + cursor++; + + /* remove empty spaces */ + while (SPACE_ASCII_VALUE == *cursor) + cursor++; + + /* no argument after the spaces ? */ + if ('\0' == *cursor) + return -EINVAL; + + *arg = cursor; + + return 0; +} + +/** + * hdd_parse_ibsstx_fail_event_params - Parse params + * for SETIBSSTXFAILEVENT + * @command: Input ibss tx fail event argument + * @tx_fail_count: (Output parameter) Tx fail counter + * @pid: (Output parameter) PID + * + * Return: 0 if the parsing succeeds; -EINVAL otherwise + */ +static int hdd_parse_ibsstx_fail_event_params(uint8_t *command, + uint8_t *tx_fail_count, + uint16_t *pid) +{ + uint8_t *param = NULL; + int ret; + + ret = hdd_parse_user_params(command, ¶m); + + if (0 == ret && param) { + if (1 != sscanf(param, "%hhu", tx_fail_count)) { + ret = -EINVAL; + goto done; + } + } else { + goto done; + } + + if (0 == *tx_fail_count) { + *pid = 0; + goto done; + } + + command = param; + command++; + + ret = hdd_parse_user_params(command, ¶m); + + if (0 == ret) { + if (1 != sscanf(param, "%hu", pid)) { + ret = -EINVAL; + goto done; + } + } else { + goto done; + } + +done: + return ret; +} + +/* Function header is left blank intentionally */ +static int hdd_parse_set_ibss_oui_data_command(uint8_t *command, uint8_t *ie, + int32_t *oui_length, int32_t limit) +{ + uint8_t len; + uint8_t data; + + while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) { + command++; + limit--; + } + + len = 2; + + while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) && + (limit > 1)) { + sscanf(command, "%02x", (unsigned int *)&data); + ie[len++] = data; + command += 2; + limit -= 2; + } + + *oui_length = len - 2; + + while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) { + command++; + limit--; + } + + while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) && + (limit > 1)) { + sscanf(command, "%02x", (unsigned int *)&data); + ie[len++] = data; + command += 2; + limit -= 2; + } + + ie[0] = WLAN_ELEMID_VENDOR; + ie[1] = len - 2; + + return len; +} + +/** + * drv_cmd_set_ibss_beacon_oui_data() - set ibss oui data command + * @adapter: Pointer to adapter + * @hdd_ctx: Pointer to HDD context + * @command: Pointer to command string + * @command_len : Command length + * @priv_data : Pointer to priv data + * + * Return: + * int status code + */ +static int drv_cmd_set_ibss_beacon_oui_data(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int i = 0; + int status; + int ret = 0; + uint8_t *ibss_ie; + int32_t oui_length = 0; + uint32_t ibss_ie_length; + uint8_t *value = command; + tSirModifyIE modify_ie; + struct csr_roam_profile *roam_profile; + mac_handle_t mac_handle; + + if (QDF_IBSS_MODE != adapter->device_mode) { + hdd_debug("Device_mode %s(%d) not IBSS", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return ret; + } + + hdd_debug("received command %s", ((char *)value)); + + /* validate argument of command */ + if (strlen(value) <= command_len) { + hdd_err("No arguments in command length %zu", + strlen(value)); + ret = -EFAULT; + goto exit; + } + + /* moving to arguments of commands */ + value = value + command_len; + command_len = strlen(value); + + /* oui_data can't be less than 3 bytes */ + if (command_len < (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) { + hdd_err("Invalid SETIBSSBEACONOUIDATA command length %d", + command_len); + ret = -EFAULT; + goto exit; + } + + ibss_ie = qdf_mem_malloc(command_len); + if (!ibss_ie) { + hdd_err("Could not allocate memory for command length %d", + command_len); + ret = -ENOMEM; + goto exit; + } + + ibss_ie_length = hdd_parse_set_ibss_oui_data_command(value, ibss_ie, + &oui_length, + command_len); + if (ibss_ie_length <= (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) { + hdd_err("Could not parse command %s return length %d", + value, ibss_ie_length); + ret = -EFAULT; + qdf_mem_free(ibss_ie); + goto exit; + } + + roam_profile = hdd_roam_profile(adapter); + + qdf_copy_macaddr(&modify_ie.bssid, + roam_profile->BSSIDs.bssid); + + modify_ie.vdev_id = adapter->vdev_id; + modify_ie.notify = true; + modify_ie.ieID = WLAN_ELEMID_VENDOR; + modify_ie.ieIDLen = ibss_ie_length; + modify_ie.ieBufferlength = ibss_ie_length; + modify_ie.pIEBuffer = ibss_ie; + modify_ie.oui_length = oui_length; + + hdd_warn("ibss_ie length %d oui_length %d ibss_ie:", + ibss_ie_length, oui_length); + while (i < modify_ie.ieBufferlength) + hdd_warn("0x%x", ibss_ie[i++]); + + /* Probe Bcn modification */ + mac_handle = hdd_ctx->mac_handle; + sme_modify_add_ie(mac_handle, &modify_ie, eUPDATE_IE_PROBE_BCN); + + /* Populating probe resp frame */ + sme_modify_add_ie(mac_handle, &modify_ie, eUPDATE_IE_PROBE_RESP); + + qdf_mem_free(ibss_ie); + + status = sme_send_cesium_enable_ind(mac_handle, + adapter->vdev_id); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Could not send cesium enable indication %d", + status); + ret = -EINVAL; + goto exit; + } + +exit: + return ret; +} + +static int drv_cmd_get_ibss_peer_info_all(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + int status = QDF_STATUS_SUCCESS; + struct hdd_station_ctx *sta_ctx = NULL; + char *extra = NULL; + int idx = 0; + int length = 0; + uint8_t mac_addr[QDF_MAC_ADDR_SIZE]; + uint32_t print_break_index = 0; + + if (QDF_IBSS_MODE != adapter->device_mode) { + hdd_warn("Unsupported in mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + hdd_debug("Received GETIBSSPEERINFOALL Command"); + + /* Handle the command */ + status = hdd_cfg80211_get_ibss_peer_info_all(adapter); + if (QDF_STATUS_SUCCESS == status) { + size_t user_size = qdf_min(WLAN_MAX_BUF_SIZE, + priv_data->total_len); + + /* + * The variable extra needed to be allocated on the heap since + * amount of memory required to copy the data for 32 devices + * exceeds the size of 1024 bytes of default stack size. On + * 64 bit devices, the default max stack size of 2048 bytes + */ + extra = qdf_mem_malloc(user_size); + + if (!extra) { + hdd_err("memory allocation failed"); + ret = -ENOMEM; + goto exit; + } + + /* Copy number of stations */ + length = scnprintf(extra, user_size, "%d ", + sta_ctx->ibss_peer_info.numPeers); + print_break_index = length; + for (idx = 0; idx < sta_ctx->ibss_peer_info.numPeers; + idx++) { + int8_t rssi; + uint32_t tx_rate; + + qdf_mem_copy(mac_addr, + sta_ctx->ibss_peer_info.peerInfoParams[idx]. + mac_addr, sizeof(mac_addr)); + + tx_rate = + sta_ctx->ibss_peer_info.peerInfoParams[idx]. + txRate; + /* + * Only lower 3 bytes are rate info. Mask of the MSByte + */ + tx_rate &= 0x00FFFFFF; + + rssi = sta_ctx->ibss_peer_info.peerInfoParams[idx]. + rssi; + + length += scnprintf(extra + length, + user_size - length, + QDF_FULL_MAC_FMT" %d %d ", + QDF_FULL_MAC_REF(mac_addr), + tx_rate, rssi); + /* + * cdf_trace_msg has limitation of 512 bytes for the + * print buffer. Hence printing the data in two chunks. + * The first chunk will have the data for 16 devices + * and the second chunk will have the rest. + */ + if (idx < NUM_OF_STA_DATA_TO_PRINT) + print_break_index = length; + } + + /* + * Copy the data back into buffer, if the data to copy is + * more than 512 bytes than we will split the data and do + * it in two shots + */ + if (copy_to_user(priv_data->buf, extra, print_break_index)) { + hdd_err("Copy into user data buffer failed"); + ret = -EFAULT; + goto mem_free; + } + + /* This overwrites the last space, which we already copied */ + extra[print_break_index - 1] = '\0'; + hdd_debug("%s", extra); + + if (length > print_break_index) { + if (copy_to_user + (priv_data->buf + print_break_index, + extra + print_break_index, + length - print_break_index + 1)) { + hdd_err("Copy into user data buffer failed"); + ret = -EFAULT; + goto mem_free; + } + hdd_debug("%s", &extra[print_break_index]); + } + } else { + /* Command failed, log error */ + hdd_err("GETIBSSPEERINFOALL command failed with status code %d", + status); + ret = -EINVAL; + goto exit; + } + ret = 0; + +mem_free: + qdf_mem_free(extra); +exit: + return ret; +} + +/* Peer Info command */ +static int drv_cmd_get_ibss_peer_info(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + QDF_STATUS status; + struct hdd_station_ctx *sta_ctx = NULL; + char extra[128] = { 0 }; + uint32_t length = 0; + struct qdf_mac_addr peer_macaddr; + + if (QDF_IBSS_MODE != adapter->device_mode) { + hdd_warn("Unsupported in mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + hdd_debug("Received GETIBSSPEERINFO Command"); + + /* if there are no peers, no need to continue with the command */ + if (eConnectionState_IbssConnected != + sta_ctx->conn_info.conn_state) { + hdd_err("No IBSS Peers coalesced"); + ret = -EINVAL; + goto exit; + } + + /* Parse the incoming command buffer */ + status = hdd_parse_get_ibss_peer_info(value, &peer_macaddr); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Invalid GETIBSSPEERINFO command"); + ret = -EINVAL; + goto exit; + } + + /* Handle the command */ + status = hdd_cfg80211_get_ibss_peer_info(adapter, peer_macaddr.bytes); + if (QDF_STATUS_SUCCESS == status) { + uint32_t tx_rate = + sta_ctx->ibss_peer_info.peerInfoParams[0].txRate; + /* Only lower 3 bytes are rate info. Mask of the MSByte */ + tx_rate &= 0x00FFFFFF; + + length = scnprintf(extra, sizeof(extra), "%d %d", + (int)tx_rate, + (int)sta_ctx->ibss_peer_info. + peerInfoParams[0].rssi); + length = QDF_MIN(priv_data->total_len, length + 1); + + /* Copy the data back into buffer */ + if (copy_to_user(priv_data->buf, &extra, length)) { + hdd_err("copy data to user buffer failed GETIBSSPEERINFO command"); + ret = -EFAULT; + goto exit; + } + } else { + /* Command failed, log error */ + hdd_err("GETIBSSPEERINFO command failed with status code %d", + status); + ret = -EINVAL; + goto exit; + } + + /* Success ! */ + hdd_debug("%s", extra); + ret = 0; + +exit: + return ret; +} + +static int drv_cmd_set_ibss_tx_fail_event(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + char *value; + uint8_t tx_fail_count = 0; + uint16_t pid = 0; + mac_handle_t mac_handle; + + value = command; + + ret = hdd_parse_ibsstx_fail_event_params(value, &tx_fail_count, &pid); + + if (0 != ret) { + hdd_err("Failed to parse SETIBSSTXFAILEVENT arguments"); + goto exit; + } + + hdd_debug("tx_fail_cnt=%hhu, pid=%hu", tx_fail_count, pid); + mac_handle = hdd_ctx->mac_handle; + + if (0 == tx_fail_count) { + /* Disable TX Fail Indication */ + if (QDF_STATUS_SUCCESS == + sme_tx_fail_monitor_start_stop_ind(mac_handle, + tx_fail_count, + NULL)) { + cesium_pid = 0; + } else { + hdd_err("failed to disable TX Fail Event"); + ret = -EINVAL; + } + } else { + if (QDF_STATUS_SUCCESS == + sme_tx_fail_monitor_start_stop_ind(mac_handle, + tx_fail_count, + (void *)hdd_tx_fail_ind_callback)) { + cesium_pid = pid; + hdd_debug("Registered Cesium pid %u", + cesium_pid); + } else { + hdd_err("Failed to enable TX Fail Monitoring"); + ret = -EINVAL; + } + } + +exit: + return ret; +} +#else +/** + * drv_cmd_get_ibss_peer_info() - get ibss peer info all + * @adapter: Pointer to adapter + * @hdd_ctx: Pointer to HDD context + * @command: Pointer to command string + * @command_len : Command length + * @priv_data : Pointer to priv data + * + * This function is dummy + * + * Return: 0 + */ +static inline int +drv_cmd_get_ibss_peer_info_all(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return 0; +} + +/** + * drv_cmd_get_ibss_peer_info() - get ibss peer info + * @adapter: Pointer to adapter + * @hdd_ctx: Pointer to HDD context + * @command: Pointer to command string + * @command_len : Command length + * @priv_data : Pointer to priv data + * + * This function is dummy + * + * Return: 0 + */ +static inline int +drv_cmd_get_ibss_peer_info(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return 0; +} + +/** + * drv_cmd_set_ibss_tx_fail_event() - set ibss tx fail event + * @adapter: Pointer to adapter + * @hdd_ctx: Pointer to HDD context + * @command: Pointer to command string + * @command_len : Command length + * @priv_data : Pointer to priv data + * + * This function is dummy + * + * Return: 0 + */ +static inline int +drv_cmd_set_ibss_tx_fail_event(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return 0; +} + +/** + * drv_cmd_set_ibss_beacon_oui_data() - set ibss oui data command + * @adapter: Pointer to adapter + * @hdd_ctx: Pointer to HDD context + * @command: Pointer to command string + * @command_len : Command length + * @priv_data : Pointer to priv data + * + * This function is dummy + * + * Return: 0 + */ +static inline int +drv_cmd_set_ibss_beacon_oui_data(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return 0; +} +#endif + +static void hdd_get_band_helper(struct hdd_context *hdd_ctx, int *ui_band) +{ + enum band_info band = -1; + + ucfg_reg_get_band(hdd_ctx->pdev, &band); + switch (band) { + case BAND_ALL: + *ui_band = WLAN_HDD_UI_BAND_AUTO; + break; + + case BAND_2G: + *ui_band = WLAN_HDD_UI_BAND_2_4_GHZ; + break; + + case BAND_5G: + *ui_band = WLAN_HDD_UI_BAND_5_GHZ; + break; + + default: + hdd_warn("Invalid Band %d", band); + *ui_band = -1; + break; + } +} + +/** + * hdd_check_and_fill_freq() - to validate chan and convert into freq + * @in_chan: input as channel number or freq to be checked + * @freq: frequency for input in_chan (output parameter) + * + * This function checks input "in_chan" is channel number, if yes then fills + * appropriate frequency into "freq" out param. If the "in_param" is greater + * than WNI_CFG_CURRENT_CHANNEL_STAMAX then checks for valid frequencies. + * + * Return: true if "in_chan" is valid channel/frequency; false otherwise + */ +static bool hdd_check_and_fill_freq(uint32_t in_chan, qdf_freq_t *freq) +{ + if (in_chan <= WNI_CFG_CURRENT_CHANNEL_STAMAX) + *freq = wlan_chan_to_freq(in_chan); + else if (WLAN_REG_IS_24GHZ_CH_FREQ(in_chan) || + WLAN_REG_IS_5GHZ_CH_FREQ(in_chan) || + WLAN_REG_IS_6GHZ_CHAN_FREQ(in_chan)) + *freq = in_chan; + else + return false; + + return true; +} + +/** + * _hdd_parse_bssid_and_chan() - helper function to parse bssid and channel + * @data: input data + * @target_ap_bssid: pointer to bssid (output parameter) + * @freq: pointer to freq (output parameter) + * + * Return: 0 if parsing is successful; -EINVAL otherwise + */ +static int _hdd_parse_bssid_and_chan(const uint8_t **data, + uint8_t *bssid, + qdf_freq_t *freq) +{ + const uint8_t *in_ptr; + int v = 0; + int temp_int; + uint8_t temp_buf[32]; + + /* 12 hexa decimal digits, 5 ':' and '\0' */ + uint8_t mac_addr[18]; + + if (!data || !*data) + return -EINVAL; + + in_ptr = *data; + + in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE); + /* no argument after the command */ + if (!in_ptr) + goto error; + /* no space after the command */ + else if (SPACE_ASCII_VALUE != *in_ptr) + goto error; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) + goto error; + + v = sscanf(in_ptr, "%17s", mac_addr); + if (!((1 == v) && hdd_is_valid_mac_address(mac_addr))) { + hdd_err("Invalid MAC address or All hex inputs are not read (%d)", + v); + goto error; + } + + bssid[0] = hex_to_bin(mac_addr[0]) << 4 | + hex_to_bin(mac_addr[1]); + bssid[1] = hex_to_bin(mac_addr[3]) << 4 | + hex_to_bin(mac_addr[4]); + bssid[2] = hex_to_bin(mac_addr[6]) << 4 | + hex_to_bin(mac_addr[7]); + bssid[3] = hex_to_bin(mac_addr[9]) << 4 | + hex_to_bin(mac_addr[10]); + bssid[4] = hex_to_bin(mac_addr[12]) << 4 | + hex_to_bin(mac_addr[13]); + bssid[5] = hex_to_bin(mac_addr[15]) << 4 | + hex_to_bin(mac_addr[16]); + + /* point to the next argument */ + in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE); + /* no argument after the command */ + if (!in_ptr) + goto error; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) + goto error; + + /* get the next argument ie the channel/freq number */ + v = sscanf(in_ptr, "%31s ", temp_buf); + if (1 != v) + goto error; + + v = kstrtos32(temp_buf, 10, &temp_int); + if (v < 0 || temp_int < 0) + goto error; + else if (!hdd_check_and_fill_freq(temp_int, freq)) + goto error; + + *data = in_ptr; + return 0; +error: + *data = in_ptr; + return -EINVAL; +} + +/** + * hdd_parse_send_action_frame_v1_data() - HDD Parse send action frame data + * @command: Pointer to input data + * @bssid: Pointer to target Ap bssid + * @channel: Pointer to the Target AP channel + * @dwell_time: Pointer to the time to stay off-channel + * after transmitting action frame + * @buf: Pointer to data + * @buf_len: Pointer to data length + * + * This function parses the send action frame data passed in the format + * SENDACTIONFRAME + * + * + * Return: 0 for success non-zero for failure + */ +static int +hdd_parse_send_action_frame_v1_data(const uint8_t *command, + uint8_t *bssid, + qdf_freq_t *freq, uint8_t *dwell_time, + uint8_t **buf, uint8_t *buf_len) +{ + const uint8_t *in_ptr = command; + const uint8_t *end_ptr; + int temp_int; + int j = 0; + int i = 0; + int v = 0; + uint8_t temp_buf[32]; + uint8_t temp_u8 = 0; + + if (_hdd_parse_bssid_and_chan(&in_ptr, bssid, freq)) + return -EINVAL; + + /* point to the next argument */ + in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE); + /* no argument after the command */ + if (!in_ptr) + return -EINVAL; + /* removing empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) + return -EINVAL; + + /* getting the next argument ie the dwell time */ + v = sscanf(in_ptr, "%31s ", temp_buf); + if (1 != v) + return -EINVAL; + + v = kstrtos32(temp_buf, 10, &temp_int); + if (v < 0 || temp_int < 0) + return -EINVAL; + + *dwell_time = temp_int; + + /* point to the next argument */ + in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE); + /* no argument after the command */ + if (!in_ptr) + return -EINVAL; + /* removing empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) + return -EINVAL; + + /* find the length of data */ + end_ptr = in_ptr; + while (('\0' != *end_ptr)) + end_ptr++; + + *buf_len = end_ptr - in_ptr; + if (*buf_len <= 0) + return -EINVAL; + + /* + * Allocate the number of bytes based on the number of input characters + * whether it is even or odd. + * if the number of input characters are even, then we need N/2 byte. + * if the number of input characters are odd, then we need do (N+1)/2 + * to compensate rounding off. + * For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough. + * If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes + */ + *buf = qdf_mem_malloc((*buf_len + 1) / 2); + if (!*buf) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + + /* the buffer received from the upper layer is character buffer, + * we need to prepare the buffer taking 2 characters in to a U8 hex + * decimal number for example 7f0000f0...form a buffer to contain 7f + * in 0th location, 00 in 1st and f0 in 3rd location + */ + for (i = 0, j = 0; j < *buf_len; j += 2) { + if (j + 1 == *buf_len) { + temp_u8 = hex_to_bin(in_ptr[j]); + } else { + temp_u8 = + (hex_to_bin(in_ptr[j]) << 4) | + (hex_to_bin(in_ptr[j + 1])); + } + (*buf)[i++] = temp_u8; + } + *buf_len = i; + return 0; +} + +/** + * hdd_parse_reassoc_command_data() - HDD Parse reassoc command data + * @command: Pointer to input data (its a NULL terminated string) + * @bssid: Pointer to target Ap bssid + * @freq: Pointer to the Target AP frequency + * + * This function parses the reasoc command data passed in the format + * REASSOC + * + * Return: 0 for success non-zero for failure + */ +static int hdd_parse_reassoc_command_v1_data(const uint8_t *command, + uint8_t *bssid, + qdf_freq_t *freq) +{ + const uint8_t *in_ptr = command; + + if (_hdd_parse_bssid_and_chan(&in_ptr, bssid, freq)) + return -EINVAL; + + return 0; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS hdd_wma_send_fastreassoc_cmd(struct hdd_adapter *adapter, + const tSirMacAddr bssid, + uint32_t ch_freq) +{ + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct csr_roam_profile *roam_profile; + tSirMacAddr connected_bssid; + + roam_profile = hdd_roam_profile(adapter); + qdf_mem_copy(connected_bssid, hdd_sta_ctx->conn_info.bssid.bytes, + ETH_ALEN); + return sme_fast_reassoc(adapter->hdd_ctx->mac_handle, + roam_profile, bssid, ch_freq, + adapter->vdev_id, connected_bssid); +} +#endif + +int hdd_reassoc(struct hdd_adapter *adapter, const uint8_t *bssid, + uint32_t ch_freq, const handoff_src src) +{ + struct hdd_station_ctx *sta_ctx; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret = 0; + QDF_STATUS status; + + if (!hdd_ctx) { + hdd_err("Invalid hdd ctx"); + return -EINVAL; + } + + if (QDF_STA_MODE != adapter->device_mode) { + hdd_warn("Unsupported in mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + /* + * pHddStaCtx->conn_info.conn_state is set to disconnected only + * after the disconnect done indication from SME. If the SME is + * in the process of disconnecting, the SME Connection state is + * set to disconnected and the pHddStaCtx->conn_info.conn_state + * will still be associated till the disconnect is done. + * So check both the HDD state and SME state here. + * If not associated, no need to proceed with reassoc + */ + if ((eConnectionState_Associated != sta_ctx->conn_info.conn_state) || + (!sme_is_conn_state_connected(hdd_ctx->mac_handle, + adapter->vdev_id))) { + hdd_warn("Not associated"); + ret = -EINVAL; + goto exit; + } + + /* + * if the target bssid is same as currently associated AP, + * use the current connections's channel. + */ + if (!memcmp(bssid, sta_ctx->conn_info.bssid.bytes, + QDF_MAC_ADDR_SIZE)) { + hdd_warn("Reassoc BSSID is same as currently associated AP bssid"); + ch_freq = sta_ctx->conn_info.chan_freq; + } + + if (QDF_STATUS_SUCCESS != + wlan_hdd_validate_operation_channel(adapter, ch_freq)) { + hdd_err("Invalid Ch freq: %d", ch_freq); + ret = -EINVAL; + goto exit; + } + + /* Proceed with reassoc */ + if (roaming_offload_enabled(hdd_ctx)) { + status = hdd_wma_send_fastreassoc_cmd(adapter, bssid, ch_freq); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to send fast reassoc cmd"); + ret = -EINVAL; + } + } else { + tCsrHandoffRequest handoff; + + handoff.ch_freq = ch_freq; + handoff.src = src; + qdf_mem_copy(handoff.bssid.bytes, bssid, QDF_MAC_ADDR_SIZE); + sme_handoff_request(hdd_ctx->mac_handle, adapter->vdev_id, + &handoff); + } +exit: + return ret; +} + +/** + * hdd_parse_reassoc_v1() - parse version 1 of the REASSOC command + * @adapter: Adapter upon which the command was received + * @command: ASCII text command that was received + * + * This function parses the v1 REASSOC command with the format + * + * REASSOC xx:xx:xx:xx:xx:xx CH/FREQ + * + * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the + * BSSID and CH/FREQ is the ASCII representation of the channel/frequency. + * For example + * + * REASSOC 00:0a:0b:11:22:33 48 + * REASSOC 00:0a:0b:11:22:33 2412 + * + * Return: 0 for success non-zero for failure + */ +static int hdd_parse_reassoc_v1(struct hdd_adapter *adapter, const char *command) +{ + qdf_freq_t freq = 0; + tSirMacAddr bssid; + int ret; + + ret = hdd_parse_reassoc_command_v1_data(command, bssid, &freq); + if (ret) + hdd_err("Failed to parse reassoc command data"); + else + ret = hdd_reassoc(adapter, bssid, freq, REASSOC); + + return ret; +} + +/** + * hdd_parse_reassoc_v2() - parse version 2 of the REASSOC command + * @adapter: Adapter upon which the command was received + * @command: Command that was received, ASCII command + * followed by binary data + * @total_len: Total length of the command received + * + * This function parses the v2 REASSOC command with the format + * + * REASSOC + * + * Return: 0 for success non-zero for failure + */ +static int hdd_parse_reassoc_v2(struct hdd_adapter *adapter, + const char *command, + int total_len) +{ + struct android_wifi_reassoc_params params; + tSirMacAddr bssid; + qdf_freq_t freq = 0; + int ret; + + if (total_len < sizeof(params) + 8) { + hdd_err("Invalid command length"); + return -EINVAL; + } + + /* The params are located after "REASSOC " */ + memcpy(¶ms, command + 8, sizeof(params)); + + if (!mac_pton(params.bssid, (u8 *) &bssid)) { + hdd_err("MAC address parsing failed"); + ret = -EINVAL; + } else { + /* + * In Reassoc command, user can send channel number or frequency + * along with BSSID. If params.channel param of REASSOC command + * is less than WNI_CFG_CURRENT_CHANNEL_STAMAX, then host + * consider this as channel number else frequency. + */ + if (!hdd_check_and_fill_freq(params.channel, &freq)) + return -EINVAL; + + ret = hdd_reassoc(adapter, bssid, freq, REASSOC); + } + + return ret; +} + +/** + * hdd_parse_reassoc() - parse the REASSOC command + * @adapter: Adapter upon which the command was received + * @command: Command that was received + * @total_len: Total length of the command received + * + * There are two different versions of the REASSOC command. Version 1 + * of the command contains a parameter list that is ASCII characters + * whereas version 2 contains a combination of ASCII and binary + * payload. Determine if a version 1 or a version 2 command is being + * parsed by examining the parameters, and then dispatch the parser + * that is appropriate for the command. + * + * Return: 0 for success non-zero for failure + */ +static int hdd_parse_reassoc(struct hdd_adapter *adapter, const char *command, + int total_len) +{ + int ret; + + /* both versions start with "REASSOC " + * v1 has a bssid and channel # as an ASCII string + * REASSOC xx:xx:xx:xx:xx:xx CH/FREQ + * v2 has a C struct + * REASSOC + * + * The first field in the v2 struct is also the bssid in ASCII. + * But in the case of a v2 message the BSSID is NUL-terminated. + * Hence we can peek at that offset to see if this is V1 or V2 + * REASSOC xx:xx:xx:xx:xx:xx* + * 1111111111222222 + * 01234567890123456789012345 + */ + + if (total_len < 26) { + hdd_err("Invalid command, total_len = %d", total_len); + return -EINVAL; + } + + if (command[25]) + ret = hdd_parse_reassoc_v1(adapter, command); + else + ret = hdd_parse_reassoc_v2(adapter, command, total_len); + + return ret; +} + +/** + * hdd_sendactionframe() - send a userspace-supplied action frame + * @adapter: Adapter upon which the command was received + * @bssid: BSSID target of the action frame + * @freq: Frequency upon which to send the frame + * @dwell_time: Amount of time to dwell when the frame is sent + * @payload_len:Length of the payload + * @payload: Payload of the frame + * + * This function sends a userspace-supplied action frame + * + * Return: 0 for success non-zero for failure + */ +static int +hdd_sendactionframe(struct hdd_adapter *adapter, const uint8_t *bssid, + const qdf_freq_t freq, const uint8_t dwell_time, + const int payload_len, const uint8_t *payload) +{ + struct ieee80211_channel chan; + int frame_len, ret = 0; + uint8_t *frame; + struct ieee80211_hdr_3addr *hdr; + u64 cookie; + struct hdd_station_ctx *sta_ctx; + struct hdd_context *hdd_ctx; + tpSirMacVendorSpecificFrameHdr vendor = + (tpSirMacVendorSpecificFrameHdr) payload; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) + struct cfg80211_mgmt_tx_params params; +#endif + + if (payload_len < sizeof(tSirMacVendorSpecificFrameHdr)) { + hdd_warn("Invalid payload length: %d", payload_len); + return -EINVAL; + } + + if (QDF_STA_MODE != adapter->device_mode) { + hdd_warn("Unsupported in mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + /* if not associated, no need to send action frame */ + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + hdd_warn("Not associated"); + ret = -EINVAL; + goto exit; + } + + /* + * if the target bssid is different from currently associated AP, + * then no need to send action frame + */ + if (memcmp(bssid, sta_ctx->conn_info.bssid.bytes, + QDF_MAC_ADDR_SIZE)) { + hdd_warn("STA is not associated to this AP"); + ret = -EINVAL; + goto exit; + } + + chan.center_freq = freq; + /* Check if it is specific action frame */ + if (vendor->category == + SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY) { + static const uint8_t oui[] = { 0x00, 0x00, 0xf0 }; + + if (!qdf_mem_cmp(vendor->Oui, oui, 3)) { + /* + * if the freq number is different from operating + * freq then no need to send action frame + */ + if (freq) { + if (freq != sta_ctx->conn_info.chan_freq) { + hdd_warn("freq(%u) is different from operating freq(%u)", + freq, + sta_ctx->conn_info.chan_freq); + ret = -EINVAL; + goto exit; + } + /* + * If channel number is specified and same + * as home channel, ensure that action frame + * is sent immediately by cancelling + * roaming scans. Otherwise large dwell times + * may cause long delays in sending action + * frames. + */ + sme_abort_roam_scan(hdd_ctx->mac_handle, + adapter->vdev_id); + } else { + /* + * 0 is accepted as current home frequency, + * delayed transmission of action frame is ok. + */ + chan.center_freq = sta_ctx->conn_info.chan_freq; + } + } + } + if (chan.center_freq == 0) { + hdd_nofl_err("Invalid freq : %d", freq); + ret = -EINVAL; + goto exit; + } + + frame_len = payload_len + 24; + frame = qdf_mem_malloc(frame_len); + if (!frame) { + hdd_err("memory allocation failed"); + ret = -ENOMEM; + goto exit; + } + + hdr = (struct ieee80211_hdr_3addr *)frame; + hdr->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); + qdf_mem_copy(hdr->addr1, bssid, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(hdr->addr2, adapter->mac_addr.bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(hdr->addr3, bssid, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(hdr + 1, payload, payload_len); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) + params.chan = &chan; + params.offchan = 0; + params.wait = dwell_time; + params.buf = frame; + params.len = frame_len; + params.no_cck = 1; + params.dont_wait_for_ack = 1; + ret = wlan_hdd_mgmt_tx(NULL, &adapter->wdev, ¶ms, &cookie); +#else + ret = wlan_hdd_mgmt_tx(NULL, + &(adapter->wdev), + &chan, 0, + + dwell_time, frame, frame_len, 1, 1, &cookie); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */ + + qdf_mem_free(frame); +exit: + return ret; +} + +/** + * hdd_parse_sendactionframe_v1() - parse version 1 of the + * SENDACTIONFRAME command + * @adapter: Adapter upon which the command was received + * @command: ASCII text command that was received + * + * This function parses the v1 SENDACTIONFRAME command with the format + * + * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DW xxxxxx + * + * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the + * BSSID, CH is the ASCII representation of the channel, DW is the + * ASCII representation of the dwell time, and xxxxxx is the Hex-ASCII + * payload. For example + * + * SENDACTIONFRAME 00:0a:0b:11:22:33 48 40 aabbccddee + * + * Return: 0 for success non-zero for failure + */ +static int +hdd_parse_sendactionframe_v1(struct hdd_adapter *adapter, const char *command) +{ + qdf_freq_t freq = 0; + uint8_t dwell_time = 0; + uint8_t payload_len = 0; + uint8_t *payload = NULL; + tSirMacAddr bssid; + int ret; + + ret = hdd_parse_send_action_frame_v1_data(command, bssid, &freq, + &dwell_time, &payload, + &payload_len); + if (ret) { + hdd_nofl_err("Failed to parse send action frame data"); + } else { + ret = hdd_sendactionframe(adapter, bssid, freq, + dwell_time, payload_len, payload); + qdf_mem_free(payload); + } + + return ret; +} + +/** + * hdd_parse_sendactionframe_v2() - parse version 2 of the + * SENDACTIONFRAME command + * @adapter: Adapter upon which the command was received + * @command: Command that was received, ASCII command + * followed by binary data + * + * This function parses the v2 SENDACTIONFRAME command with the format + * + * SENDACTIONFRAME + * + * Return: 0 for success non-zero for failure + */ +static int +hdd_parse_sendactionframe_v2(struct hdd_adapter *adapter, + const char *command, int total_len) +{ + struct android_wifi_af_params *params; + tSirMacAddr bssid; + int ret; + int len_wo_payload = 0; + qdf_freq_t freq = 0; + + /* The params are located after "SENDACTIONFRAME " */ + total_len -= 16; + len_wo_payload = sizeof(*params) - ANDROID_WIFI_ACTION_FRAME_SIZE; + if (total_len <= len_wo_payload) { + hdd_err("Invalid command len"); + return -EINVAL; + } + + params = (struct android_wifi_af_params *)(command + 16); + + if (params->len <= 0 || params->len > ANDROID_WIFI_ACTION_FRAME_SIZE || + (params->len > (total_len - len_wo_payload))) { + hdd_err("Invalid payload length: %d", params->len); + return -EINVAL; + } + + if (!mac_pton(params->bssid, (u8 *)&bssid)) { + hdd_err("MAC address parsing failed"); + return -EINVAL; + } + + if (params->channel < 0 || + params->channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) { + hdd_err("Invalid channel: %d", params->channel); + return -EINVAL; + } + + if (params->dwell_time < 0) { + hdd_err("Invalid dwell_time: %d", params->dwell_time); + return -EINVAL; + } + + if (!hdd_check_and_fill_freq(params->channel, &freq)) { + hdd_err("Invalid channel: %d", params->channel); + return -EINVAL; + } + + ret = hdd_sendactionframe(adapter, bssid, freq, params->dwell_time, + params->len, params->data); + + return ret; +} + +/** + * hdd_parse_sendactionframe() - parse the SENDACTIONFRAME command + * @adapter: Adapter upon which the command was received + * @command: Command that was received + * + * There are two different versions of the SENDACTIONFRAME command. + * Version 1 of the command contains a parameter list that is ASCII + * characters whereas version 2 contains a combination of ASCII and + * binary payload. Determine if a version 1 or a version 2 command is + * being parsed by examining the parameters, and then dispatch the + * parser that is appropriate for the version of the command. + * + * Return: 0 for success non-zero for failure + */ +static int +hdd_parse_sendactionframe(struct hdd_adapter *adapter, const char *command, + int total_len) +{ + int ret; + + /* + * both versions start with "SENDACTIONFRAME " + * v1 has a bssid and other parameters as an ASCII string + * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DWELL LEN FRAME + * v2 has a C struct + * SENDACTIONFRAME + * + * The first field in the v2 struct is also the bssid in ASCII. + * But in the case of a v2 message the BSSID is NUL-terminated. + * Hence we can peek at that offset to see if this is V1 or V2 + * SENDACTIONFRAME xx:xx:xx:xx:xx:xx* + * 111111111122222222223333 + * 0123456789012345678901234567890123 + * For both the commands, a valid command must have atleast + * first 34 length of data. + */ + if (total_len < 34) { + hdd_err("Invalid command (total_len=%d)", total_len); + return -EINVAL; + } + + if (command[33]) + ret = hdd_parse_sendactionframe_v1(adapter, command); + else + ret = hdd_parse_sendactionframe_v2(adapter, command, total_len); + + return ret; +} + +/** + * hdd_parse_channellist() - HDD Parse channel list + * @hdd_ctx: hdd context + * @command: Pointer to input channel list + * @channel_freq_list: Pointer to local output array to record + * channel list + * @num_channels: Pointer to number of roam scan channels + * + * This function parses the channel list passed in the format + * SETROAMSCANCHANNELSChannel 1 + * Channel 2Channel N + * if the Number of channels (N) does not match with the actual number + * of channels passed then take the minimum of N and count of + * (Ch1, Ch2, ...Ch M). For example, if SETROAMSCANCHANNELS 3 36 40 44 48, + * only 36, 40 and 44 shall be taken. If SETROAMSCANCHANNELS 5 36 40 44 48, + * ignore 5 and take 36, 40, 44 and 48. This function does not take care of + * removing duplicate channels from the list + * + * Return: 0 for success non-zero for failure + */ +static int +hdd_parse_channellist(struct hdd_context *hdd_ctx, + const uint8_t *command, + uint32_t *channel_freq_list, + uint8_t *num_channels) +{ + const uint8_t *in_ptr = command; + int temp_int; + int j = 0; + int v = 0; + char buf[32]; + + in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); + /* no argument after the command */ + if (!in_ptr) + return -EINVAL; + else if (SPACE_ASCII_VALUE != *in_ptr) /* no space after the command */ + return -EINVAL; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) + return -EINVAL; + + /* get the first argument ie the number of channels */ + v = sscanf(in_ptr, "%31s ", buf); + if (1 != v) + return -EINVAL; + + v = kstrtos32(buf, 10, &temp_int); + if ((v < 0) || + (temp_int <= 0) || (temp_int > CFG_VALID_CHANNEL_LIST_LEN)) + return -EINVAL; + + *num_channels = temp_int; + + hdd_debug("Number of channels are: %d", *num_channels); + + for (j = 0; j < (*num_channels); j++) { + /* + * in_ptr pointing to the beginning of first space after number + * of channels + */ + in_ptr = strpbrk(in_ptr, " "); + /* no channel list after the number of channels argument */ + if (!in_ptr) { + if ((j != 0) && (*num_channels == j)) + return 0; + else + goto cnt_mismatch; + } + + /* remove empty space */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* + * no channel list after the number of channels + * argument and spaces + */ + if ('\0' == *in_ptr) { + if ((j != 0) && (*num_channels == j)) + return 0; + else + goto cnt_mismatch; + } + + v = sscanf(in_ptr, "%31s ", buf); + if (1 != v) + return -EINVAL; + + v = kstrtos32(buf, 10, &temp_int); + if ((v < 0) || + (temp_int <= 0) || + (temp_int > WNI_CFG_CURRENT_CHANNEL_STAMAX)) { + return -EINVAL; + } + channel_freq_list[j] = + wlan_reg_chan_to_freq(hdd_ctx->pdev, temp_int); + + hdd_debug("Channel %d added to preferred channel list", + channel_freq_list[j]); + } + + return 0; + +cnt_mismatch: + hdd_debug("Mismatch in ch cnt: %d and num of ch: %d", *num_channels, j); + *num_channels = 0; + return -EINVAL; + +} + +/** + * hdd_parse_set_roam_scan_channels_v1() - parse version 1 of the + * SETROAMSCANCHANNELS command + * @adapter: Adapter upon which the command was received + * @command: ASCII text command that was received + * + * This function parses the v1 SETROAMSCANCHANNELS command with the format + * + * SETROAMSCANCHANNELS N C1 C2 ... Cn + * + * Where "N" is the ASCII representation of the number of channels and + * C1 thru Cn is the ASCII representation of the channels. For example + * + * SETROAMSCANCHANNELS 4 36 40 44 48 + * + * Return: 0 for success non-zero for failure + */ +static int +hdd_parse_set_roam_scan_channels_v1(struct hdd_adapter *adapter, + const char *command) +{ + uint32_t channel_freq_list[CFG_VALID_CHANNEL_LIST_LEN] = { 0 }; + uint8_t num_chan = 0; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + mac_handle_t mac_handle; + + if (!hdd_ctx) { + hdd_err("invalid hdd ctx"); + ret = -EINVAL; + goto exit; + } + + ret = hdd_parse_channellist(hdd_ctx, command, channel_freq_list, + &num_chan); + if (ret) { + hdd_err("Failed to parse channel list information"); + goto exit; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL, + adapter->vdev_id, num_chan); + + if (num_chan > CFG_VALID_CHANNEL_LIST_LEN) { + hdd_err("number of channels (%d) supported exceeded max (%d)", + num_chan, CFG_VALID_CHANNEL_LIST_LEN); + ret = -EINVAL; + goto exit; + } + + mac_handle = hdd_ctx->mac_handle; + if (!sme_validate_channel_list(mac_handle, + channel_freq_list, num_chan)) { + hdd_err("List contains invalid channel(s)"); + ret = -EINVAL; + goto exit; + } + + status = sme_change_roam_scan_channel_list(mac_handle, + adapter->vdev_id, + channel_freq_list, + num_chan); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed to update channel list information"); + ret = -EINVAL; + goto exit; + } +exit: + return ret; +} + +/** + * hdd_parse_set_roam_scan_channels_v2() - parse version 2 of the + * SETROAMSCANCHANNELS command + * @adapter: Adapter upon which the command was received + * @command: Command that was received, ASCII command + * followed by binary data + * + * This function parses the v2 SETROAMSCANCHANNELS command with the format + * + * SETROAMSCANCHANNELS [N][C1][C2][Cn] + * + * The command begins with SETROAMSCANCHANNELS followed by a space, but + * what follows the space is an array of u08 parameters. For example + * + * SETROAMSCANCHANNELS [0x04 0x24 0x28 0x2c 0x30] + * + * Return: 0 for success non-zero for failure + */ +static int +hdd_parse_set_roam_scan_channels_v2(struct hdd_adapter *adapter, + const char *command) +{ + const uint8_t *value; + uint32_t channel_freq_list[CFG_VALID_CHANNEL_LIST_LEN] = { 0 }; + uint8_t channel; + uint8_t num_chan; + int i; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + int ret = 0; + mac_handle_t mac_handle; + + /* array of values begins after "SETROAMSCANCHANNELS " */ + value = command + 20; + + num_chan = *value++; + if (num_chan > CFG_VALID_CHANNEL_LIST_LEN) { + hdd_err("number of channels (%d) supported exceeded max (%d)", + num_chan, CFG_VALID_CHANNEL_LIST_LEN); + ret = -EINVAL; + goto exit; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL, + adapter->vdev_id, num_chan); + + + for (i = 0; i < num_chan; i++) { + channel = *value++; + if (!channel) { + hdd_err("Channels end at index %d, expected %d", + i, num_chan); + ret = -EINVAL; + goto exit; + } + + if (channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) { + hdd_err("index %d invalid channel %d", + i, channel); + ret = -EINVAL; + goto exit; + } + channel_freq_list[i] = wlan_reg_chan_to_freq(hdd_ctx->pdev, + channel); + } + + mac_handle = hdd_ctx->mac_handle; + if (!sme_validate_channel_list(mac_handle, channel_freq_list, + num_chan)) { + hdd_err("List contains invalid channel(s)"); + ret = -EINVAL; + goto exit; + } + + status = sme_change_roam_scan_channel_list(mac_handle, + adapter->vdev_id, + channel_freq_list, num_chan); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed to update channel list information"); + ret = -EINVAL; + goto exit; + } +exit: + return ret; +} + +/** + * hdd_parse_set_roam_scan_channels() - parse the + * SETROAMSCANCHANNELS command + * @adapter: Adapter upon which the command was received + * @command: Command that was received + * + * There are two different versions of the SETROAMSCANCHANNELS command. + * Version 1 of the command contains a parameter list that is ASCII + * characters whereas version 2 contains a binary payload. Determine + * if a version 1 or a version 2 command is being parsed by examining + * the parameters, and then dispatch the parser that is appropriate for + * the command. + * + * Return: 0 for success non-zero for failure + */ +static int +hdd_parse_set_roam_scan_channels(struct hdd_adapter *adapter, const char *command) +{ + const char *cursor; + char ch; + bool v1; + int ret; + + /* start after "SETROAMSCANCHANNELS " */ + cursor = command + 20; + + /* assume we have a version 1 command until proven otherwise */ + v1 = true; + + /* v1 params will only contain ASCII digits and space */ + while ((ch = *cursor++) && v1) { + if (!(isdigit(ch) || isspace(ch))) + v1 = false; + } + + if (v1) + ret = hdd_parse_set_roam_scan_channels_v1(adapter, command); + else + ret = hdd_parse_set_roam_scan_channels_v2(adapter, command); + + return ret; +} + +#ifdef FEATURE_WLAN_ESE +/** + * hdd_parse_plm_cmd() - HDD Parse Plm command + * @command: Pointer to input data + * @req: Pointer to output struct plm_req + * + * This function parses the plm command passed in the format + * CCXPLMREQ + * + * + * + * + * + * Return: 0 for success non-zero for failure + */ +static QDF_STATUS hdd_parse_plm_cmd(uint8_t *command, + struct plm_req_params *req) +{ + uint8_t *in_ptr = NULL; + int count, content = 0, ret = 0; + char buf[32]; + + /* move to argument list */ + in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* no space after the command */ + if (SPACE_ASCII_VALUE != *in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* START/STOP PLM req */ + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + req->enable = content; + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* Dialog token of radio meas req containing meas reqIE */ + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + req->diag_token = content; + hdd_debug("diag token %d", req->diag_token); + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* measurement token of meas req IE */ + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + req->meas_token = content; + hdd_debug("meas token %d", req->meas_token); + + hdd_debug("PLM req %s", req->enable ? "START" : "STOP"); + if (req->enable) { + + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* total number of bursts after which STA stops sending */ + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + if (content < 0) + return QDF_STATUS_E_FAILURE; + + req->num_bursts = content; + hdd_debug("num bursts %d", req->num_bursts); + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* burst interval in seconds */ + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + if (content <= 0) + return QDF_STATUS_E_FAILURE; + + req->burst_int = content; + hdd_debug("burst int %d", req->burst_int); + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* Meas dur in TU's,STA goes off-ch and transmit PLM bursts */ + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + if (content <= 0) + return QDF_STATUS_E_FAILURE; + + req->meas_duration = content; + hdd_debug("meas duration %d", req->meas_duration); + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* burst length of PLM bursts */ + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + if (content <= 0) + return QDF_STATUS_E_FAILURE; + + req->burst_len = content; + hdd_debug("burst len %d", req->burst_len); + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* desired tx power for transmission of PLM bursts */ + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + if (content <= 0) + return QDF_STATUS_E_FAILURE; + + req->desired_tx_pwr = content; + hdd_debug("desired tx pwr %d", req->desired_tx_pwr); + + for (count = 0; count < QDF_MAC_ADDR_SIZE; count++) { + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) + && ('\0' != *in_ptr)) + in_ptr++; + + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 16, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + req->mac_addr.bytes[count] = content; + } + + hdd_debug("MAC addr " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(req->mac_addr.bytes)); + + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* number of channels */ + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0) + return QDF_STATUS_E_FAILURE; + + if (content < 0) + return QDF_STATUS_E_FAILURE; + + content = QDF_MIN(content, CFG_VALID_CHANNEL_LIST_LEN); + req->plm_num_ch = content; + hdd_debug("num ch: %d", req->plm_num_ch); + + /* Channel numbers */ + for (count = 0; count < req->plm_num_ch; count++) { + in_ptr = strpbrk(in_ptr, " "); + + if (!in_ptr) + return QDF_STATUS_E_FAILURE; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) + && ('\0' != *in_ptr)) + in_ptr++; + + ret = sscanf(in_ptr, "%31s ", buf); + if (1 != ret) + return QDF_STATUS_E_FAILURE; + + ret = kstrtos32(buf, 10, &content); + if (ret < 0 || content <= 0 || + content > WNI_CFG_CURRENT_CHANNEL_STAMAX) + return QDF_STATUS_E_FAILURE; + + req->plm_ch_freq_list[count] = + cds_chan_to_freq(content); + hdd_debug(" ch-freq- %d", req->plm_ch_freq_list[count]); + } + } + /* If PLM START */ + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +/** + * wlan_hdd_ready_to_extwow() - Callback function for enable ext wow + * @cookie: callback context + * @is_success: suspend status of ext wow + * + * Return: none + */ +static void wlan_hdd_ready_to_extwow(void *cookie, bool is_success) +{ + struct osif_request *request = NULL; + struct enable_ext_wow_priv *priv = NULL; + + request = osif_request_get(cookie); + if (!request) { + hdd_err("Obselete request"); + return; + } + priv = osif_request_priv(request); + priv->ext_wow_should_suspend = is_success; + + osif_request_complete(request); + osif_request_put(request); +} + +static int hdd_enable_ext_wow(struct hdd_adapter *adapter, + tpSirExtWoWParams arg_params) +{ + tSirExtWoWParams params; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int rc; + struct enable_ext_wow_priv *priv = NULL; + struct osif_request *request = NULL; + void *cookie = NULL; + struct osif_request_params hdd_params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_READY_TO_EXTWOW, + }; + + qdf_mem_copy(¶ms, arg_params, sizeof(params)); + + request = osif_request_alloc(&hdd_params); + if (!request) { + hdd_err("Request Allocation Failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + status = sme_configure_ext_wow(hdd_ctx->mac_handle, ¶ms, + &wlan_hdd_ready_to_extwow, + cookie); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("sme_configure_ext_wow returned failure %d", + status); + rc = -EPERM; + goto exit; + } + + rc = osif_request_wait_for_response(request); + if (rc) { + hdd_err("Failed to get ready to extwow"); + rc = -EPERM; + goto exit; + } + + priv = osif_request_priv(request); + if (!priv->ext_wow_should_suspend) { + hdd_err("Received ready to ExtWoW failure"); + rc = -EPERM; + goto exit; + } + + if (ucfg_pmo_extwow_is_goto_suspend_enabled(hdd_ctx->psoc)) { + hdd_info("Received ready to ExtWoW. Going to suspend"); + + rc = wlan_hdd_cfg80211_suspend_wlan(hdd_ctx->wiphy, NULL); + if (rc < 0) { + hdd_err("wlan_hdd_cfg80211_suspend_wlan failed, error = %d", + rc); + goto exit; + } + rc = wlan_hdd_bus_suspend(); + if (rc) { + hdd_err("wlan_hdd_bus_suspend failed, status = %d", + rc); + wlan_hdd_cfg80211_resume_wlan(hdd_ctx->wiphy); + goto exit; + } + } + +exit: + osif_request_put(request); + return rc; +} + +static int hdd_enable_ext_wow_parser(struct hdd_adapter *adapter, int vdev_id, + int value) +{ + tSirExtWoWParams params; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int rc; + uint8_t pin1, pin2; + + rc = wlan_hdd_validate_context(hdd_ctx); + if (rc) + return rc; + + if (value < EXT_WOW_TYPE_APP_TYPE1 || + value > EXT_WOW_TYPE_APP_TYPE1_2) { + hdd_err("Invalid type: %d", value); + return -EINVAL; + } + + if (value == EXT_WOW_TYPE_APP_TYPE1 && + hdd_ctx->is_extwow_app_type1_param_set) + params.type = value; + else if (value == EXT_WOW_TYPE_APP_TYPE2 && + hdd_ctx->is_extwow_app_type2_param_set) + params.type = value; + else if (value == EXT_WOW_TYPE_APP_TYPE1_2 && + hdd_ctx->is_extwow_app_type1_param_set && + hdd_ctx->is_extwow_app_type2_param_set) + params.type = value; + else { + hdd_err("Set app params before enable it value %d", + value); + return -EINVAL; + } + + params.vdev_id = vdev_id; + pin1 = ucfg_pmo_extwow_app1_wakeup_pin_num(hdd_ctx->psoc); + pin2 = ucfg_pmo_extwow_app2_wakeup_pin_num(hdd_ctx->psoc); + params.wakeup_pin_num = pin1 | (pin2 << 8); + + return hdd_enable_ext_wow(adapter, ¶ms); +} + +static int hdd_set_app_type1_params(mac_handle_t mac_handle, + tpSirAppType1Params arg_params) +{ + tSirAppType1Params params; + QDF_STATUS status; + + qdf_mem_copy(¶ms, arg_params, sizeof(params)); + + status = sme_configure_app_type1_params(mac_handle, ¶ms); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("sme_configure_app_type1_params returned failure %d", + status); + return -EPERM; + } + + return 0; +} + +static int hdd_set_app_type1_parser(struct hdd_adapter *adapter, + char *arg, int len) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + char id[20], password[20]; + tSirAppType1Params params; + int rc; + + rc = wlan_hdd_validate_context(hdd_ctx); + if (rc) + return rc; + + if (2 != sscanf(arg, "%8s %16s", id, password)) { + hdd_err("Invalid Number of arguments"); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(tSirAppType1Params)); + params.vdev_id = adapter->vdev_id; + qdf_copy_macaddr(¶ms.wakee_mac_addr, &adapter->mac_addr); + + params.id_length = strlen(id); + qdf_mem_copy(params.identification_id, id, params.id_length); + params.pass_length = strlen(password); + qdf_mem_copy(params.password, password, params.pass_length); + + hdd_debug("%d "QDF_MAC_ADDR_FMT" %.8s %u %.16s %u", + params.vdev_id, + QDF_MAC_ADDR_REF(params.wakee_mac_addr.bytes), + params.identification_id, params.id_length, + params.password, params.pass_length); + + return hdd_set_app_type1_params(hdd_ctx->mac_handle, ¶ms); +} + +static int hdd_set_app_type2_params(mac_handle_t mac_handle, + tpSirAppType2Params arg_params) +{ + tSirAppType2Params params; + QDF_STATUS status; + + qdf_mem_copy(¶ms, arg_params, sizeof(params)); + + status = sme_configure_app_type2_params(mac_handle, ¶ms); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("sme_configure_app_type2_params returned failure %d", + status); + return -EPERM; + } + + return 0; +} + +static int hdd_set_app_type2_parser(struct hdd_adapter *adapter, + char *arg, int len) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + char mac_addr[20], rc4_key[20]; + unsigned int gateway_mac[QDF_MAC_ADDR_SIZE]; + tSirAppType2Params params; + int ret; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + memset(¶ms, 0, sizeof(tSirAppType2Params)); + + ret = sscanf(arg, "%17s %16s %x %x %x %u %u %hu %hu %u %u %u %u %u %u", + mac_addr, rc4_key, (unsigned int *)¶ms.ip_id, + (unsigned int *)¶ms.ip_device_ip, + (unsigned int *)¶ms.ip_server_ip, + (unsigned int *)¶ms.tcp_seq, + (unsigned int *)¶ms.tcp_ack_seq, + (uint16_t *)¶ms.tcp_src_port, + (uint16_t *)¶ms.tcp_dst_port, + (unsigned int *)¶ms.keepalive_init, + (unsigned int *)¶ms.keepalive_min, + (unsigned int *)¶ms.keepalive_max, + (unsigned int *)¶ms.keepalive_inc, + (unsigned int *)¶ms.tcp_tx_timeout_val, + (unsigned int *)¶ms.tcp_rx_timeout_val); + + if (ret != 15 && ret != 7) { + hdd_err("Invalid Number of arguments"); + return -EINVAL; + } + + if (6 != sscanf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", + &gateway_mac[0], &gateway_mac[1], &gateway_mac[2], + &gateway_mac[3], &gateway_mac[4], &gateway_mac[5])) { + hdd_err("Invalid MacAddress Input %s", mac_addr); + return -EINVAL; + } + + if (params.tcp_src_port > WLAN_HDD_MAX_TCP_PORT || + params.tcp_dst_port > WLAN_HDD_MAX_TCP_PORT) { + hdd_err("Invalid TCP Port Number"); + return -EINVAL; + } + + qdf_mem_copy(¶ms.gateway_mac.bytes, (uint8_t *) &gateway_mac, + QDF_MAC_ADDR_SIZE); + + params.rc4_key_len = strlen(rc4_key); + qdf_mem_copy(params.rc4_key, rc4_key, params.rc4_key_len); + + params.vdev_id = adapter->vdev_id; + + if (!params.tcp_src_port) + params.tcp_src_port = + ucfg_pmo_extwow_app2_tcp_src_port(hdd_ctx->psoc); + + if (!params.tcp_dst_port) + params.tcp_dst_port = + ucfg_pmo_extwow_app2_tcp_dst_port(hdd_ctx->psoc); + + if (!params.keepalive_init) + params.keepalive_init = + ucfg_pmo_extwow_app2_init_ping_interval(hdd_ctx->psoc); + + if (!params.keepalive_min) + params.keepalive_min = + ucfg_pmo_extwow_app2_min_ping_interval(hdd_ctx->psoc); + + if (!params.keepalive_max) + params.keepalive_max = + ucfg_pmo_extwow_app2_max_ping_interval(hdd_ctx->psoc); + + if (!params.keepalive_inc) + params.keepalive_inc = + ucfg_pmo_extwow_app2_inc_ping_interval(hdd_ctx->psoc); + + if (!params.tcp_tx_timeout_val) + params.tcp_tx_timeout_val = + ucfg_pmo_extwow_app2_tcp_tx_timeout(hdd_ctx->psoc); + + if (!params.tcp_rx_timeout_val) + params.tcp_rx_timeout_val = + ucfg_pmo_extwow_app2_tcp_rx_timeout(hdd_ctx->psoc); + + hdd_debug(QDF_MAC_ADDR_FMT" %.16s %u %u %u %u %u %u %u %u %u %u %u %u %u", + QDF_MAC_ADDR_REF(gateway_mac), rc4_key, params.ip_id, + params.ip_device_ip, params.ip_server_ip, params.tcp_seq, + params.tcp_ack_seq, params.tcp_src_port, params.tcp_dst_port, + params.keepalive_init, params.keepalive_min, + params.keepalive_max, params.keepalive_inc, + params.tcp_tx_timeout_val, params.tcp_rx_timeout_val); + + return hdd_set_app_type2_params(hdd_ctx->mac_handle, ¶ms); +} +#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */ + +/** + * hdd_parse_setmaxtxpower_command() - HDD Parse MAXTXPOWER command + * @command: Pointer to MAXTXPOWER command + * @tx_power: Pointer to tx power + * + * This function parses the MAXTXPOWER command passed in the format + * MAXTXPOWERX(Tx power in dbm) + * + * For example input commands: + * 1) MAXTXPOWER -8 -> This is translated into set max TX power to -8 dbm + * 2) MAXTXPOWER -23 -> This is translated into set max TX power to -23 dbm + * + * Return: 0 for success non-zero for failure + */ +static int hdd_parse_setmaxtxpower_command(uint8_t *command, int *tx_power) +{ + uint8_t *in_ptr = command; + int temp_int; + int v = 0; + *tx_power = 0; + + in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); + /* no argument after the command */ + if (!in_ptr) + return -EINVAL; + else if (SPACE_ASCII_VALUE != *in_ptr) /* no space after the command */ + return -EINVAL; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) + return 0; + + v = kstrtos32(in_ptr, 10, &temp_int); + + /* Range checking for passed parameter */ + if ((temp_int < HDD_MIN_TX_POWER) || (temp_int > HDD_MAX_TX_POWER)) + return -EINVAL; + + *tx_power = temp_int; + + hdd_debug("SETMAXTXPOWER: %d", *tx_power); + + return 0; +} /* End of hdd_parse_setmaxtxpower_command */ + +static int hdd_get_dwell_time(struct wlan_objmgr_psoc *psoc, uint8_t *command, + char *extra, uint8_t n, uint8_t *len) +{ + uint32_t val = 0; + + if (!psoc || !command || !extra || !len) { + hdd_err("argument passed for GETDWELLTIME is incorrect"); + return -EINVAL; + } + + if (strncmp(command, "GETDWELLTIME ACTIVE MAX", 23) == 0) { + ucfg_scan_cfg_get_active_dwelltime(psoc, &val); + *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MAX %u\n", val); + return 0; + } + if (strncmp(command, "GETDWELLTIME PASSIVE MAX", 24) == 0) { + ucfg_scan_cfg_get_passive_dwelltime(psoc, &val); + *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MAX %u\n", + val); + return 0; + } + if (strncmp(command, "GETDWELLTIME 2G MAX", 19) == 0) { + ucfg_scan_cfg_get_active_2g_dwelltime(psoc, &val); + *len = scnprintf(extra, n, "GETDWELLTIME 2G MAX %u\n", + val); + return 0; + } + if (strncmp(command, "GETDWELLTIME", 12) == 0) { + ucfg_scan_cfg_get_active_dwelltime(psoc, &val); + *len = scnprintf(extra, n, "GETDWELLTIME %u\n", val); + return 0; + } + + return -EINVAL; +} + +static int hdd_set_dwell_time(struct wlan_objmgr_psoc *psoc, uint8_t *command) +{ + uint8_t *value = command; + int retval = 0, temp = 0; + uint32_t val = 0; + + if (!psoc) { + hdd_err("psoc is null"); + return -EINVAL; + } + + if (strncmp(command, "SETDWELLTIME ACTIVE MAX", 23) == 0) { + if (drv_cmd_validate(command, 23)) + return -EINVAL; + + value = value + 24; + temp = kstrtou32(value, 10, &val); + if (temp || !cfg_in_range(CFG_ACTIVE_MAX_CHANNEL_TIME, val)) { + hdd_err_rl("argument passed for SETDWELLTIME ACTIVE MAX is incorrect"); + return -EFAULT; + } + ucfg_scan_cfg_set_active_dwelltime(psoc, val); + } else if (strncmp(command, "SETDWELLTIME PASSIVE MAX", 24) == 0) { + if (drv_cmd_validate(command, 24)) + return -EINVAL; + + value = value + 25; + temp = kstrtou32(value, 10, &val); + if (temp || !cfg_in_range(CFG_PASSIVE_MAX_CHANNEL_TIME, val)) { + hdd_err_rl("argument passed for SETDWELLTIME PASSIVE MAX is incorrect"); + return -EFAULT; + } + ucfg_scan_cfg_set_passive_dwelltime(psoc, val); + } else if (strncmp(command, "SETDWELLTIME 2G MAX", 19) == 0) { + if (drv_cmd_validate(command, 19)) + return -EINVAL; + + value = value + 20; + temp = kstrtou32(value, 10, &val); + if (temp || !cfg_in_range(CFG_ACTIVE_MAX_2G_CHANNEL_TIME, + val)) { + hdd_err_rl("argument passed for SETDWELLTIME 2G MAX is incorrect"); + return -EFAULT; + } + ucfg_scan_cfg_set_active_2g_dwelltime(psoc, val); + } else if (strncmp(command, "SETDWELLTIME", 12) == 0) { + if (drv_cmd_validate(command, 12)) + return -EINVAL; + + value = value + 13; + temp = kstrtou32(value, 10, &val); + if (temp || !cfg_in_range(CFG_ACTIVE_MAX_CHANNEL_TIME, val)) { + hdd_err_rl("argument passed for SETDWELLTIME is incorrect"); + return -EFAULT; + } + ucfg_scan_cfg_set_active_dwelltime(psoc, val); + } else { + retval = -EINVAL; + } + + return retval; +} + +struct link_status_priv { + uint8_t link_status; +}; + +/** + * hdd_conc_set_dwell_time() - Set Concurrent dwell time parameters + * @adapter: Adapter upon which the command was received + * @command: ASCII text command that is received + * + * Driver commands: + * wpa_cli DRIVER CONCSETDWELLTIME ACTIVE MAX + * wpa_cli DRIVER CONCSETDWELLTIME ACTIVE MIN + * wpa_cli DRIVER CONCSETDWELLTIME PASSIVE MAX + * wpa_cli DRIVER CONCSETDWELLTIME PASSIVE MIN + * + * Return: 0 for success non-zero for failure + */ +static int hdd_conc_set_dwell_time(struct hdd_adapter *adapter, + uint8_t *command) +{ + u8 *value = command; + int val = 0, temp = 0; + int retval = 0; + + if (strncmp(command, "CONCSETDWELLTIME ACTIVE MAX", 27) == 0) { + if (drv_cmd_validate(command, 27)) { + hdd_err("Invalid driver command"); + return -EINVAL; + } + + value = value + 28; + temp = kstrtou32(value, 10, &val); + if (temp || + !cfg_in_range(CFG_ACTIVE_MAX_CHANNEL_TIME_CONC, val)) { + hdd_err("CONC ACTIVE MAX value %d incorrect", val); + return -EFAULT; + } + ucfg_scan_cfg_set_conc_active_dwelltime( + (WLAN_HDD_GET_CTX(adapter))->psoc, val); + } else if (strncmp(command, "CONCSETDWELLTIME PASSIVE MAX", 28) == 0) { + if (drv_cmd_validate(command, 28)) { + hdd_err("Invalid driver command"); + return -EINVAL; + } + + value = value + 29; + temp = kstrtou32(value, 10, &val); + if (temp || + !cfg_in_range(CFG_PASSIVE_MAX_CHANNEL_TIME_CONC, val)) { + hdd_err("CONC PASSIVE MAX val %d incorrect", val); + return -EFAULT; + } + ucfg_scan_cfg_set_conc_passive_dwelltime( + (WLAN_HDD_GET_CTX(adapter))->psoc, val); + } else { + retval = -EINVAL; + } + + return retval; +} + +static int hdd_enable_unit_test_commands(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx) +{ + enum pld_bus_type bus_type = pld_get_bus_type(hdd_ctx->parent_dev); + u32 arg[2]; + QDF_STATUS status; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE || + hdd_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + return -EPERM; + + if (adapter->vdev_id >= WLAN_MAX_VDEVS) { + hdd_err_rl("Invalid vdev id"); + return -EINVAL; + } + + if (bus_type == PLD_BUS_TYPE_PCIE) { + arg[0] = 360; + arg[1] = 3; + + status = sme_send_unit_test_cmd(adapter->vdev_id, + WLAN_MODULE_TX, + 2, + arg); + if (status != QDF_STATUS_SUCCESS) + return qdf_status_to_os_return(status); + + arg[0] = 361; + arg[1] = 1; + + status = sme_send_unit_test_cmd(adapter->vdev_id, + WLAN_MODULE_TX, + 2, + arg); + if (status != QDF_STATUS_SUCCESS) + return qdf_status_to_os_return(status); + + if (hdd_ctx->target_type == TARGET_TYPE_QCA6390) { + arg[0] = 37; + arg[1] = 3000; + + status = sme_send_unit_test_cmd(adapter->vdev_id, + WLAN_MODULE_RX, + 2, + arg); + if (status != QDF_STATUS_SUCCESS) + return qdf_status_to_os_return(status); + } + + if (hdd_ctx->target_type == TARGET_TYPE_QCA6490) { + arg[0] = 39; + arg[1] = 3000; + + status = sme_send_unit_test_cmd(adapter->vdev_id, + WLAN_MODULE_RX, + 2, + arg); + if (status != QDF_STATUS_SUCCESS) + return qdf_status_to_os_return(status); + } + } else if (bus_type == PLD_BUS_TYPE_SNOC) { + arg[0] = 7; + arg[1] = 1; + + status = sme_send_unit_test_cmd(adapter->vdev_id, + 0x44, + 2, + arg); + if (status != QDF_STATUS_SUCCESS) + return qdf_status_to_os_return(status); + } else { + return -EINVAL; + } + return 0; +} + +static int hdd_disable_unit_test_commands(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx) +{ + enum pld_bus_type bus_type = pld_get_bus_type(hdd_ctx->parent_dev); + u32 arg[2]; + QDF_STATUS status; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE || + hdd_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + return -EPERM; + + if (adapter->vdev_id >= WLAN_MAX_VDEVS) { + hdd_err_rl("Invalid vdev id"); + return -EINVAL; + } + + if (bus_type == PLD_BUS_TYPE_PCIE) { + arg[0] = 360; + arg[1] = 0; + + status = sme_send_unit_test_cmd(adapter->vdev_id, + WLAN_MODULE_TX, + 2, + arg); + if (status != QDF_STATUS_SUCCESS) + return qdf_status_to_os_return(status); + + arg[0] = 361; + arg[1] = 0; + + status = sme_send_unit_test_cmd(adapter->vdev_id, + WLAN_MODULE_TX, + 2, + arg); + if (status != QDF_STATUS_SUCCESS) + return qdf_status_to_os_return(status); + + arg[0] = 39; + arg[1] = 0; + + status = sme_send_unit_test_cmd(adapter->vdev_id, + WLAN_MODULE_RX, + 2, + arg); + if (status != QDF_STATUS_SUCCESS) + return qdf_status_to_os_return(status); + + } else if (bus_type == PLD_BUS_TYPE_SNOC) { + arg[0] = 7; + arg[1] = 0; + + status = sme_send_unit_test_cmd(adapter->vdev_id, + 0x44, + 2, + arg); + if (status != QDF_STATUS_SUCCESS) + return qdf_status_to_os_return(status); + } else { + return -EINVAL; + } + return 0; +} + +static void hdd_get_link_status_cb(uint8_t status, void *context) +{ + struct osif_request *request; + struct link_status_priv *priv; + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + priv->link_status = status; + osif_request_complete(request); + osif_request_put(request); +} + +/** + * wlan_hdd_get_link_status() - get link status + * @adapter: pointer to the adapter + * + * This function sends a request to query the link status and waits + * on a timer to invoke the callback. if the callback is invoked then + * latest link status shall be returned or otherwise cached value + * will be returned. + * + * Return: On success, link status shall be returned. + * On error or not associated, link status 0 will be returned. + */ +static int wlan_hdd_get_link_status(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx; + QDF_STATUS status; + int ret; + void *cookie; + struct osif_request *request; + struct link_status_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_LINK_STATUS, + }; + + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + hdd_warn("Recovery in Progress. State: 0x%x Ignore!!!", + cds_get_driver_state()); + return 0; + } + + if ((QDF_STA_MODE != adapter->device_mode) && + (QDF_P2P_CLIENT_MODE != adapter->device_mode)) { + hdd_warn("Unsupported in mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return 0; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + /* If not associated, then expected link status return + * value is 0 + */ + hdd_warn("Not associated!"); + return 0; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return 0; + } + cookie = osif_request_cookie(request); + + status = sme_get_link_status(adapter->hdd_ctx->mac_handle, + hdd_get_link_status_cb, + cookie, adapter->vdev_id); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Unable to retrieve link status"); + /* return a cached value */ + } else { + /* request is sent -- wait for the response */ + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving link status"); + /* return a cached value */ + } else { + /* update the adapter with the fresh results */ + priv = osif_request_priv(request); + adapter->link_status = priv->link_status; + } + } + + /* + * either we never sent a request, we sent a request and + * received a response or we sent a request and timed out. + * regardless we are done with the request. + */ + osif_request_put(request); + + /* either callback updated adapter stats or it has cached data */ + return adapter->link_status; +} + +#ifdef FEATURE_WLAN_ESE +/** + * hdd_parse_ese_beacon_req() - Parse ese beacon request + * @command: Pointer to data + * @req: Output pointer to store parsed ie information + * + * This function parses the ese beacon request passed in the format + * CCXBEACONREQ + * Channel 1Scan Mode Meas DurationChannel N + * Scan Mode NMeas Duration N + * + * If the Number of bcn req fields (N) does not match with the + * actual number of fields passed then take N. + * and are treated + * as one pair. For example, CCXBEACONREQ 2 1 1 1 30 2 44 0 40. + * This function does not take care of removing duplicate channels from the + * list + * + * Return: 0 for success non-zero for failure + */ +static int hdd_parse_ese_beacon_req(struct wlan_objmgr_pdev *pdev, + uint8_t *command, + tCsrEseBeaconReq *req) +{ + uint8_t *in_ptr = command; + uint8_t input = 0; + uint32_t temp_int = 0; + int j = 0, i = 0, v = 0; + char buf[32]; + + in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); + if (!in_ptr) /* no argument after the command */ + return -EINVAL; + else if (SPACE_ASCII_VALUE != *in_ptr) /* no space after the command */ + return -EINVAL; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) + return -EINVAL; + + /* Getting the first argument ie Number of IE fields */ + v = sscanf(in_ptr, "%31s ", buf); + if (1 != v) + return -EINVAL; + + v = kstrtou8(buf, 10, &input); + if (v < 0) + return -EINVAL; + + input = QDF_MIN(input, SIR_ESE_MAX_MEAS_IE_REQS); + req->numBcnReqIe = input; + + hdd_debug("Number of Bcn Req Ie fields: %d", req->numBcnReqIe); + + for (j = 0; j < (req->numBcnReqIe); j++) { + for (i = 0; i < 4; i++) { + /* + * in_ptr pointing to the beginning of 1st space + * after number of ie fields + */ + in_ptr = strpbrk(in_ptr, " "); + /* no ie data after the number of ie fields argument */ + if (!in_ptr) + return -EINVAL; + + /* remove empty space */ + while ((SPACE_ASCII_VALUE == *in_ptr) + && ('\0' != *in_ptr)) + in_ptr++; + + /* + * no ie data after the number of ie fields + * argument and spaces + */ + if ('\0' == *in_ptr) + return -EINVAL; + + v = sscanf(in_ptr, "%31s ", buf); + if (1 != v) + return -EINVAL; + + v = kstrtou32(buf, 10, &temp_int); + if (v < 0) + return -EINVAL; + + switch (i) { + case 0: /* Measurement token */ + if (!temp_int) { + hdd_err("Invalid Measurement Token: %u", + temp_int); + return -EINVAL; + } + req->bcnReq[j].measurementToken = + temp_int; + break; + + case 1: /* Channel number */ + if (!temp_int || + (temp_int > + WNI_CFG_CURRENT_CHANNEL_STAMAX)) { + hdd_err("Invalid Channel Number: %u", + temp_int); + return -EINVAL; + } + req->bcnReq[j].ch_freq = + wlan_reg_chan_to_freq(pdev, temp_int); + break; + + case 2: /* Scan mode */ + if ((temp_int < eSIR_PASSIVE_SCAN) + || (temp_int > eSIR_BEACON_TABLE)) { + hdd_err("Invalid Scan Mode: %u Expected{0|1|2}", + temp_int); + return -EINVAL; + } + req->bcnReq[j].scanMode = temp_int; + break; + + case 3: /* Measurement duration */ + if ((!temp_int + && (req->bcnReq[j].scanMode != + eSIR_BEACON_TABLE)) || + (req->bcnReq[j].scanMode == + eSIR_BEACON_TABLE)) { + hdd_err("Invalid Measurement Duration: %u", + temp_int); + return -EINVAL; + } + req->bcnReq[j].measurementDuration = + temp_int; + break; + } + } + } + + for (j = 0; j < req->numBcnReqIe; j++) { + hdd_debug("Index: %d Measurement Token: %u ch_freq: %u Scan Mode: %u Measurement Duration: %u", + j, + req->bcnReq[j].measurementToken, + req->bcnReq[j].ch_freq, + req->bcnReq[j].scanMode, + req->bcnReq[j].measurementDuration); + } + + return 0; +} + +/** + * hdd_parse_get_cckm_ie() - HDD Parse and fetch the CCKM IE + * @command: Pointer to input data + * @cckm_ie: Pointer to output cckm Ie + * @cckm_ie_len: Pointer to output cckm ie length + * + * This function parses the SETCCKM IE command + * SETCCKMIE + * + * Return: 0 for success non-zero for failure + */ +static int hdd_parse_get_cckm_ie(uint8_t *command, uint8_t **cckm_ie, + uint8_t *cckm_ie_len) +{ + uint8_t *in_ptr = command; + uint8_t *end_ptr; + int j = 0; + int i = 0; + uint8_t temp_u8 = 0; + + in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); + /* no argument after the command */ + if (!in_ptr) + return -EINVAL; + else if (SPACE_ASCII_VALUE != *in_ptr) /* no space after the command */ + return -EINVAL; + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + /* no argument followed by spaces */ + if ('\0' == *in_ptr) + return -EINVAL; + + /* find the length of data */ + end_ptr = in_ptr; + while (('\0' != *end_ptr)) { + end_ptr++; + ++(*cckm_ie_len); + } + if (*cckm_ie_len <= 0) + return -EINVAL; + /* + * Allocate the number of bytes based on the number of input characters + * whether it is even or odd. + * if the number of input characters are even, then we need N / 2 byte. + * if the number of input characters are odd, then we need do + * (N + 1) / 2 to compensate rounding off. + * For example, if N = 18, then (18 + 1) / 2 = 9 bytes are enough. + * If N = 19, then we need 10 bytes, hence (19 + 1) / 2 = 10 bytes + */ + *cckm_ie = qdf_mem_malloc((*cckm_ie_len + 1) / 2); + if (!*cckm_ie) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + /* + * the buffer received from the upper layer is character buffer, + * we need to prepare the buffer taking 2 characters in to a U8 hex + * decimal number for example 7f0000f0...form a buffer to contain + * 7f in 0th location, 00 in 1st and f0 in 3rd location + */ + for (i = 0, j = 0; j < *cckm_ie_len; j += 2) { + temp_u8 = (hex_to_bin(in_ptr[j]) << 4) | + (hex_to_bin(in_ptr[j + 1])); + (*cckm_ie)[i++] = temp_u8; + } + *cckm_ie_len = i; + return 0; +} +#endif /* FEATURE_WLAN_ESE */ + +int wlan_hdd_set_mc_rate(struct hdd_adapter *adapter, int target_rate) +{ + tSirRateUpdateInd rate_update = {0}; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + bool bval = false; + + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return -EINVAL; + } + if ((QDF_IBSS_MODE != adapter->device_mode) && + (QDF_SAP_MODE != adapter->device_mode) && + (QDF_STA_MODE != adapter->device_mode)) { + hdd_err("Received SETMCRATE cmd in invalid mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + hdd_err("SETMCRATE cmd is allowed only in STA, IBSS or SOFTAP mode"); + return -EINVAL; + } + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("unable to get vht_enable2x2"); + return -EINVAL; + } + rate_update.nss = (bval == 0) ? 0 : 1; + + rate_update.dev_mode = adapter->device_mode; + rate_update.mcastDataRate24GHz = target_rate; + rate_update.mcastDataRate24GHzTxFlag = 1; + rate_update.mcastDataRate5GHz = target_rate; + rate_update.bcastDataRate = -1; + qdf_copy_macaddr(&rate_update.bssid, &adapter->mac_addr); + hdd_debug("MC Target rate %d, mac = "QDF_MAC_ADDR_FMT", dev_mode %s(%d)", + rate_update.mcastDataRate24GHz, + QDF_MAC_ADDR_REF(rate_update.bssid.bytes), + qdf_opmode_str(adapter->device_mode), adapter->device_mode); + status = sme_send_rate_update_ind(hdd_ctx->mac_handle, &rate_update); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("SETMCRATE failed"); + return -EFAULT; + } + return 0; +} + +static int drv_cmd_p2p_dev_addr(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + struct qdf_mac_addr *addr = &hdd_ctx->p2p_device_address; + size_t user_size = qdf_min(sizeof(addr->bytes), + (size_t)priv_data->total_len); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL, + adapter->vdev_id, + (unsigned int)(*(addr->bytes + 2) << 24 | + *(addr->bytes + 3) << 16 | + *(addr->bytes + 4) << 8 | + *(addr->bytes + 5))); + + + if (copy_to_user(priv_data->buf, addr->bytes, user_size)) { + hdd_err("failed to copy data to user buffer"); + return -EFAULT; + } + + return 0; +} + +/** + * drv_cmd_p2p_set_noa() - Handler for P2P_SET_NOA driver command + * @adapter: Adapter on which the command was received + * @hdd_ctx: HDD global context + * @command: Entire driver command received from userspace + * @command_len: Length of @command + * @priv_data: Pointer to ioctl private data structure + * + * This is a trivial command handler function which simply forwards the + * command to the actual command processor within the P2P module. + * + * Return: 0 on success, non-zero on failure + */ +static int drv_cmd_p2p_set_noa(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_set_p2p_noa(adapter->dev, command); +} + +/** + * drv_cmd_p2p_set_ps() - Handler for P2P_SET_PS driver command + * @adapter: Adapter on which the command was received + * @hdd_ctx: HDD global context + * @command: Entire driver command received from userspace + * @command_len: Length of @command + * @priv_data: Pointer to ioctl private data structure + * + * This is a trivial command handler function which simply forwards the + * command to the actual command processor within the P2P module. + * + * Return: 0 on success, non-zero on failure + */ +static int drv_cmd_p2p_set_ps(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_set_p2p_opps(adapter->dev, command); +} + +static int drv_cmd_set_band(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int err; + uint8_t band; + uint32_t band_bitmap; + + /* + * Parse the band value passed from userspace. The first 8 bytes + * should be "SETBAND " and the 9th byte should be a UI band value + */ + err = kstrtou8(command + command_len + 1, 10, &band); + if (err) { + hdd_err("error %d parsing userspace band parameter", err); + return err; + } + + band_bitmap = hdd_reg_legacy_setband_to_reg_wifi_band_bitmap(band); + + return hdd_reg_set_band(adapter->dev, band_bitmap); +} + +static int drv_cmd_set_wmmps(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_wmmps_helper(adapter, command); +} + +static inline int drv_cmd_country(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + char *country_code; + + country_code = strnchr(command, strlen(command), ' '); + /* no argument after the command */ + if (!country_code) + return -EINVAL; + + /* no space after the command */ + if (*country_code != SPACE_ASCII_VALUE) + return -EINVAL; + + country_code++; + + /* removing empty spaces */ + while ((*country_code == SPACE_ASCII_VALUE) && + (*country_code != '\0')) + country_code++; + + /* no or less than 2 arguments followed by spaces */ + if (*country_code == '\0' || *(country_code + 1) == '\0') + return -EINVAL; + + return hdd_reg_set_country(hdd_ctx, country_code); +} + +/** + * drv_cmd_get_country() - Helper function to get current county code + * @adapter: pointer to adapter on which request is received + * @hdd_ctx: pointer to hdd context + * @command: command name + * @command_len: command buffer length + * @priv_data: output pointer to hold current country code + * + * Return: On success 0, negative value on error. + */ +static int drv_cmd_get_country(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + uint8_t buf[SIZE_OF_GETCOUNTRYREV_OUTPUT] = {0}; + uint8_t cc[REG_ALPHA2_LEN + 1]; + int ret = 0, len; + + qdf_mem_copy(cc, hdd_ctx->reg.alpha2, REG_ALPHA2_LEN); + cc[REG_ALPHA2_LEN] = '\0'; + + len = scnprintf(buf, sizeof(buf), "%s %s", + "GETCOUNTRYREV", cc); + hdd_debug("buf = %s", buf); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, buf, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_roam_trigger(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + int8_t rssi = 0; + uint8_t lookup_threshold = cfg_default( + CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* Move pointer to ahead of SETROAMTRIGGER */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtos8(value, 10, &rssi); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]", + cfg_min(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD), + cfg_max(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD)); + ret = -EINVAL; + goto exit; + } + + lookup_threshold = abs(rssi); + + if (!cfg_in_range(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD, + lookup_threshold)) { + hdd_err("Neighbor lookup threshold value %d is out of range (Min: %d Max: %d)", + lookup_threshold, + cfg_min(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD), + cfg_max(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD)); + ret = -EINVAL; + goto exit; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL, + adapter->vdev_id, lookup_threshold); + + hdd_debug("Received Command to Set Roam trigger (Neighbor lookup threshold) = %d", + lookup_threshold); + + status = sme_set_neighbor_lookup_rssi_threshold(hdd_ctx->mac_handle, + adapter->vdev_id, + lookup_threshold); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed to set roam trigger, try again"); + ret = -EPERM; + goto exit; + } + +exit: + return ret; +} + +static int drv_cmd_get_roam_trigger(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t lookup_threshold; + int rssi; + char extra[32]; + uint8_t len = 0; + QDF_STATUS status; + + status = sme_get_neighbor_lookup_rssi_threshold(hdd_ctx->mac_handle, + adapter->vdev_id, + &lookup_threshold); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL, + adapter->vdev_id, lookup_threshold); + + hdd_debug("vdev_id: %u, lookup_threshold: %u", + adapter->vdev_id, lookup_threshold); + + rssi = (-1) * lookup_threshold; + + len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_roam_scan_period(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t roam_scan_period = 0; + uint16_t empty_scan_refresh_period = + cfg_default(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD); + + /* input refresh period is in terms of seconds */ + + /* Move pointer to ahead of SETROAMSCANPERIOD */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &roam_scan_period); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]", + (cfg_min(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD) / 1000), + (cfg_max(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD) / 1000)); + ret = -EINVAL; + goto exit; + } + + if (!ucfg_mlme_validate_scan_period(roam_scan_period * 1000)) { + ret = -EINVAL; + goto exit; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL, + adapter->vdev_id, roam_scan_period); + + empty_scan_refresh_period = roam_scan_period * 1000; + + hdd_debug("Received Command to Set roam scan period (Empty Scan refresh period) = %d", + roam_scan_period); + + sme_update_empty_scan_refresh_period(hdd_ctx->mac_handle, + adapter->vdev_id, + empty_scan_refresh_period); + +exit: + return ret; +} + +static int drv_cmd_get_roam_scan_period(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint16_t empty_scan_refresh_period; + char extra[32]; + uint8_t len; + QDF_STATUS status; + + status = sme_get_empty_scan_refresh_period(hdd_ctx->mac_handle, + adapter->vdev_id, + &empty_scan_refresh_period); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + hdd_debug("vdev_id: %u, empty_scan_refresh_period: %u", + adapter->vdev_id, empty_scan_refresh_period); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL, + adapter->vdev_id, + empty_scan_refresh_period); + + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETROAMSCANPERIOD", + (empty_scan_refresh_period / 1000)); + /* Returned value is in units of seconds */ + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_roam_scan_refresh_period(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + uint8_t roam_scan_refresh_period = 0; + uint16_t neighbor_scan_refresh_period = + cfg_default(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD); + + /* input refresh period is in terms of seconds */ + /* Move pointer to ahead of SETROAMSCANREFRESHPERIOD */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &roam_scan_refresh_period); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]", + (cfg_min(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD) + / 1000), + (cfg_max(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD) + / 1000)); + ret = -EINVAL; + goto exit; + } + + if (!cfg_in_range(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD, + roam_scan_refresh_period)) { + hdd_err("Neighbor scan results refresh period value %d is out of range (Min: %d Max: %d)", + roam_scan_refresh_period, + (cfg_min(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD) + / 1000), + (cfg_max(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD) + / 1000)); + ret = -EINVAL; + goto exit; + } + neighbor_scan_refresh_period = roam_scan_refresh_period * 1000; + + hdd_debug("Received Command to Set roam scan refresh period (Scan refresh period) = %d", + neighbor_scan_refresh_period); + + sme_set_neighbor_scan_refresh_period(hdd_ctx->mac_handle, + adapter->vdev_id, + neighbor_scan_refresh_period); + +exit: + return ret; +} + +static int drv_cmd_get_roam_scan_refresh_period(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint16_t value = + sme_get_neighbor_scan_refresh_period(hdd_ctx->mac_handle); + char extra[32]; + uint8_t len; + + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETROAMSCANREFRESHPERIOD", + (value / 1000)); + /* Returned value is in units of seconds */ + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_roam_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + mac_handle_t mac_handle; + int ret; + uint8_t *value = command; + uint8_t roam_mode = cfg_default(CFG_LFR_FEATURE_ENABLED); + + /* Move pointer to ahead of SETROAMMODE */ + value = value + SIZE_OF_SETROAMMODE + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &roam_mode); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_FEATURE_ENABLED), + cfg_max(CFG_LFR_FEATURE_ENABLED)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to Set Roam Mode = %d", + roam_mode); + /* + * Note that + * SETROAMMODE 0 is to enable LFR while + * SETROAMMODE 1 is to disable LFR, but + * notify_is_fast_roam_ini_feature_enabled 0/1 is to + * enable/disable. So, we have to invert the value + * to call sme_update_is_fast_roam_ini_feature_enabled. + */ + if (roam_mode) { + /* Roam enable */ + roam_mode = cfg_min(CFG_LFR_FEATURE_ENABLED); + } else { + /* Roam disable */ + roam_mode = cfg_max(CFG_LFR_FEATURE_ENABLED); + } + + ucfg_mlme_set_lfr_enabled(hdd_ctx->psoc, (bool)roam_mode); + mac_handle = hdd_ctx->mac_handle; + if (roam_mode) { + ucfg_mlme_set_roam_scan_offload_enabled(hdd_ctx->psoc, + (bool)roam_mode); + sme_update_is_fast_roam_ini_feature_enabled(mac_handle, + adapter->vdev_id, + roam_mode); + } else { + sme_update_is_fast_roam_ini_feature_enabled(mac_handle, + adapter->vdev_id, + roam_mode); + ucfg_mlme_set_roam_scan_offload_enabled(hdd_ctx->psoc, + roam_mode); + } + +exit: + return ret; +} + +static int drv_cmd_set_suspend_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int errno; + uint8_t *value = command; + QDF_STATUS status; + uint8_t idle_monitor; + + if (QDF_STA_MODE != adapter->device_mode) { + hdd_debug("Non-STA interface"); + return 0; + } + + /* Move pointer to ahead of SETSUSPENDMODE */ + value = value + SIZE_OF_SETSUSPENDMODE + 1; + + /* Convert the value from ascii to integer */ + errno = kstrtou8(value, 10, &idle_monitor); + if (errno < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("Range validation failed"); + return -EINVAL; + } + + hdd_debug("idle_monitor:%d", idle_monitor); + status = ucfg_pmo_tgt_psoc_send_idle_roam_suspend_mode(hdd_ctx->psoc, + idle_monitor); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_debug("Send suspend mode to fw failed"); + return -EINVAL; + } + return 0; +} + +static int drv_cmd_get_roam_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + bool roam_mode = sme_get_is_lfr_feature_enabled(hdd_ctx->mac_handle); + char extra[32]; + uint8_t len; + + /* + * roamMode value shall be inverted because the sementics is different. + */ + if (roam_mode) + roam_mode = cfg_min(CFG_LFR_FEATURE_ENABLED); + else + roam_mode = cfg_max(CFG_LFR_FEATURE_ENABLED); + + len = scnprintf(extra, sizeof(extra), "%s %d", command, roam_mode); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_roam_delta(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + uint8_t roam_rssi_diff = cfg_default(CFG_LFR_ROAM_RSSI_DIFF); + + /* Move pointer to ahead of SETROAMDELTA */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &roam_rssi_diff); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_ROAM_RSSI_DIFF), + cfg_max(CFG_LFR_ROAM_RSSI_DIFF)); + ret = -EINVAL; + goto exit; + } + + if (!cfg_in_range(CFG_LFR_ROAM_RSSI_DIFF, roam_rssi_diff)) { + hdd_err("Roam rssi diff value %d is out of range (Min: %d Max: %d)", + roam_rssi_diff, + cfg_min(CFG_LFR_ROAM_RSSI_DIFF), + cfg_max(CFG_LFR_ROAM_RSSI_DIFF)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to Set roam rssi diff = %d", + roam_rssi_diff); + + sme_update_roam_rssi_diff(hdd_ctx->mac_handle, + adapter->vdev_id, + roam_rssi_diff); + +exit: + return ret; +} + +static int drv_cmd_get_roam_delta(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t rssi_diff; + char extra[32]; + uint8_t len; + QDF_STATUS status; + + status = sme_get_roam_rssi_diff(hdd_ctx->mac_handle, adapter->vdev_id, + &rssi_diff); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + hdd_debug("vdev_id: %u, rssi_diff: %u", adapter->vdev_id, rssi_diff); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_GETROAMDELTA_IOCTL, + adapter->vdev_id, rssi_diff); + len = scnprintf(extra, sizeof(extra), "%s %d", + command, rssi_diff); + len = QDF_MIN(priv_data->total_len, len + 1); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_get_band(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + int band = -1; + char extra[32]; + uint8_t len = 0; + + hdd_get_band_helper(hdd_ctx, &band); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_GETBAND_IOCTL, + adapter->vdev_id, band); + + len = scnprintf(extra, sizeof(extra), "%s %d", command, band); + len = QDF_MIN(priv_data->total_len, len + 1); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_roam_scan_channels(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_parse_set_roam_scan_channels(adapter, command); +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static bool is_roam_ch_from_fw_supported(struct hdd_context *hdd_ctx) +{ + return hdd_ctx->roam_ch_from_fw_supported; +} + +struct roam_ch_priv { + struct roam_scan_ch_resp roam_ch; +}; + +void hdd_get_roam_scan_ch_cb(hdd_handle_t hdd_handle, + struct roam_scan_ch_resp *roam_ch, + void *context) +{ + struct osif_request *request; + struct roam_ch_priv *priv; + uint8_t *event = NULL, i = 0; + uint32_t *freq = NULL, len; + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + + hdd_debug("roam scan ch list event received : vdev_id:%d command resp: %d", + roam_ch->vdev_id, roam_ch->command_resp); + /** + * If command response is set in the response message, then it is + * getroamscanchannels command response else this event is asyncronous + * event raised by firmware. + */ + if (!roam_ch->command_resp) { + len = roam_ch->num_channels * sizeof(roam_ch->chan_list[0]); + if (!len) { + hdd_err("Invalid len"); + return; + } + event = (uint8_t *)qdf_mem_malloc(len); + if (!event) { + hdd_err("Failed to alloc event response buf vdev_id: %d", + roam_ch->vdev_id); + return; + } + freq = (uint32_t *)event; + for (i = 0; i < roam_ch->num_channels && + i < WNI_CFG_VALID_CHANNEL_LIST_LEN; i++) { + freq[i] = roam_ch->chan_list[i]; + } + + hdd_send_roam_scan_ch_list_event(hdd_ctx, roam_ch->vdev_id, + len, event); + qdf_mem_free(event); + return; + } + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + priv = osif_request_priv(request); + + priv->roam_ch.num_channels = roam_ch->num_channels; + for (i = 0; i < priv->roam_ch.num_channels && + i < WNI_CFG_VALID_CHANNEL_LIST_LEN; i++) + priv->roam_ch.chan_list[i] = roam_ch->chan_list[i]; + + osif_request_complete(request); + osif_request_put(request); +} + +static uint32_t +hdd_get_roam_chan_from_fw(struct hdd_adapter *adapter, uint32_t *chan_list, + uint8_t *num_channels) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + struct hdd_context *hdd_ctx; + int ret, i; + void *cookie; + struct osif_request *request; + struct roam_ch_priv *priv; + struct roam_scan_ch_resp *p_roam_ch; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv) + + sizeof(priv->roam_ch.chan_list[0]) * + WNI_CFG_VALID_CHANNEL_LIST_LEN, + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + + priv = osif_request_priv(request); + p_roam_ch = &priv->roam_ch; + /** channel list starts after response structure*/ + priv->roam_ch.chan_list = (uint32_t *)(p_roam_ch + 1); + cookie = osif_request_cookie(request); + status = sme_get_roam_scan_ch(hdd_ctx->mac_handle, + adapter->vdev_id, cookie); + + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to retrieve roam channels"); + ret = qdf_status_to_os_return(status); + goto cleanup; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving raom channels"); + goto cleanup; + } + + priv = osif_request_priv(request); + *num_channels = priv->roam_ch.num_channels; + for (i = 0; i < *num_channels; i++) + chan_list[i] = priv->roam_ch.chan_list[i]; + +cleanup: + osif_request_put(request); + + return ret; +} + +int +hdd_get_roam_scan_freq(struct hdd_adapter *adapter, mac_handle_t mac_handle, + uint32_t *chan_list, uint8_t *num_channels) +{ + int ret = 0; + + if (!adapter || !mac_handle || !chan_list || !num_channels) { + hdd_err("failed to get roam scan channel, invalid input"); + return -EFAULT; + } + + if (is_roam_ch_from_fw_supported(adapter->hdd_ctx)) { + ret = hdd_get_roam_chan_from_fw(adapter, chan_list, + num_channels); + if (ret != QDF_STATUS_SUCCESS) { + hdd_err("failed to get roam scan channel list from FW"); + return -EFAULT; + } + + return ret; + } + + if (sme_get_roam_scan_channel_list(mac_handle, chan_list, + num_channels, adapter->vdev_id) != + QDF_STATUS_SUCCESS) { + hdd_err("failed to get roam scan channel list"); + return -EFAULT; + } + + return ret; +} +#endif + +static int drv_cmd_get_roam_scan_channels(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint32_t freq_list[CFG_VALID_CHANNEL_LIST_LEN] = { 0 }; + uint8_t num_channels = 0; + uint8_t j = 0; + char extra[128] = { 0 }; + int len; + uint8_t chan; + + ret = hdd_get_roam_scan_freq(adapter, hdd_ctx->mac_handle, freq_list, + &num_channels); + if (ret != QDF_STATUS_SUCCESS) + goto exit; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL, + adapter->vdev_id, num_channels); + + /* + * output channel list is of the format + * [Number of roam scan channels][Channel1][Channel2]... + * copy the number of channels in the 0th index + */ + len = scnprintf(extra, sizeof(extra), "%s %d", command, + num_channels); + for (j = 0; (j < num_channels) && len <= sizeof(extra); j++) { + chan = wlan_reg_freq_to_chan(hdd_ctx->pdev, freq_list[j]); + len += scnprintf(extra + len, sizeof(extra) - len, + " %d", chan); + } + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + goto exit; + } + +exit: + return ret; +} + +static int drv_cmd_get_ccx_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + mac_handle_t mac_handle = hdd_ctx->mac_handle; + bool ese_mode = sme_get_is_ese_feature_enabled(mac_handle); + char extra[32]; + uint8_t len = 0; + struct pmkid_mode_bits pmkid_modes; + + hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes); + /* + * Check if the features PMKID/ESE/11R are supported simultaneously, + * then this operation is not permitted (return FAILURE) + */ + if (ese_mode && + (pmkid_modes.fw_okc || pmkid_modes.fw_pmksa_cache) && + sme_get_is_ft_feature_enabled(mac_handle)) { + hdd_warn("PMKID/ESE/11R are supported simultaneously hence this operation is not permitted!"); + ret = -EPERM; + goto exit; + } + + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETCCXMODE", ese_mode); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + goto exit; + } + +exit: + return ret; +} + +static int drv_cmd_get_okc_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + struct pmkid_mode_bits pmkid_modes; + char extra[32]; + uint8_t len = 0; + mac_handle_t mac_handle = hdd_ctx->mac_handle; + + hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes); + /* + * Check if the features OKC/ESE/11R are supported simultaneously, + * then this operation is not permitted (return FAILURE) + */ + if (pmkid_modes.fw_okc && + sme_get_is_ese_feature_enabled(mac_handle) && + sme_get_is_ft_feature_enabled(mac_handle)) { + hdd_warn("PMKID/ESE/11R are supported simultaneously hence this operation is not permitted!"); + ret = -EPERM; + goto exit; + } + + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETOKCMODE", pmkid_modes.fw_okc); + len = QDF_MIN(priv_data->total_len, len + 1); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + goto exit; + } + +exit: + return ret; +} + +static int drv_cmd_get_fast_roam(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + bool lfr_mode = sme_get_is_lfr_feature_enabled(hdd_ctx->mac_handle); + char extra[32]; + uint8_t len = 0; + + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETFASTROAM", lfr_mode); + len = QDF_MIN(priv_data->total_len, len + 1); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_get_fast_transition(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + bool ft = sme_get_is_ft_feature_enabled(hdd_ctx->mac_handle); + char extra[32]; + uint8_t len = 0; + + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETFASTTRANSITION", ft); + len = QDF_MIN(priv_data->total_len, len + 1); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_roam_scan_channel_min_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t min_time = cfg_default(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME); + + /* Move pointer to ahead of SETROAMSCANCHANNELMINTIME */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &min_time); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME), + cfg_max(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME)); + ret = -EINVAL; + goto exit; + } + + if (!cfg_in_range(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME, min_time)) { + hdd_err("scan min channel time value %d is out of range (Min: %d Max: %d)", + min_time, + cfg_min(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME), + cfg_max(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME)); + ret = -EINVAL; + goto exit; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL, + adapter->vdev_id, min_time); + + hdd_debug("Received Command to change channel min time = %d", + min_time); + + sme_set_neighbor_scan_min_chan_time(hdd_ctx->mac_handle, + min_time, + adapter->vdev_id); + +exit: + return ret; +} + +static int drv_cmd_send_action_frame(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_parse_sendactionframe(adapter, command, + priv_data->total_len); +} + +static int drv_cmd_get_roam_scan_channel_min_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint16_t val = sme_get_neighbor_scan_min_chan_time(hdd_ctx->mac_handle, + adapter->vdev_id); + char extra[32]; + uint8_t len = 0; + + /* value is interms of msec */ + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETROAMSCANCHANNELMINTIME", val); + len = QDF_MIN(priv_data->total_len, len + 1); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL, + adapter->vdev_id, val); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_scan_channel_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint16_t max_time = cfg_default(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME); + + /* Move pointer to ahead of SETSCANCHANNELTIME */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou16(value, 10, &max_time); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou16 failed range [%d - %d]", + cfg_min(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME), + cfg_max(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME)); + ret = -EINVAL; + goto exit; + } + + if (!cfg_in_range(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME, max_time)) { + hdd_err("lfr mode value %d is out of range (Min: %d Max: %d)", + max_time, + cfg_min(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME), + cfg_max(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to change channel max time = %d", + max_time); + + sme_set_neighbor_scan_max_chan_time(hdd_ctx->mac_handle, + adapter->vdev_id, + max_time); + +exit: + return ret; +} + +static int drv_cmd_get_scan_channel_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint16_t val = sme_get_neighbor_scan_max_chan_time(hdd_ctx->mac_handle, + adapter->vdev_id); + char extra[32]; + uint8_t len = 0; + + /* value is interms of msec */ + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETSCANCHANNELTIME", val); + len = QDF_MIN(priv_data->total_len, len + 1); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_scan_home_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint16_t val = cfg_default(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD); + + /* Move pointer to ahead of SETSCANHOMETIME */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou16(value, 10, &val); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou16 failed range [%d - %d]", + cfg_min(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD), + cfg_max(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD)); + ret = -EINVAL; + goto exit; + } + + if (!cfg_in_range(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD, val)) { + hdd_err("scan home time value %d is out of range (Min: %d Max: %d)", + val, + cfg_min(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD), + cfg_max(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to change scan home time = %d", + val); + + sme_set_neighbor_scan_period(hdd_ctx->mac_handle, + adapter->vdev_id, val); + +exit: + return ret; +} + +static int drv_cmd_get_scan_home_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint16_t val = sme_get_neighbor_scan_period(hdd_ctx->mac_handle, + adapter->vdev_id); + char extra[32]; + uint8_t len = 0; + + /* value is interms of msec */ + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETSCANHOMETIME", val); + len = QDF_MIN(priv_data->total_len, len + 1); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_roam_intra_band(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t val = cfg_default(CFG_LFR_ROAM_INTRA_BAND); + + /* Move pointer to ahead of SETROAMINTRABAND */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &val); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_ROAM_INTRA_BAND), + cfg_max(CFG_LFR_ROAM_INTRA_BAND)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to change intra band = %d", + val); + + ucfg_mlme_set_roam_intra_band(hdd_ctx->psoc, (bool)val); + policy_mgr_set_pcl_for_existing_combo( + hdd_ctx->psoc, + PM_STA_MODE); + +exit: + return ret; +} + +static int drv_cmd_get_roam_intra_band(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint16_t val = sme_get_roam_intra_band(hdd_ctx->mac_handle); + char extra[32]; + uint8_t len = 0; + + /* value is interms of msec */ + len = scnprintf(extra, sizeof(extra), "%s %d", + "GETROAMINTRABAND", val); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_scan_n_probes(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t nprobes = cfg_default(CFG_LFR_ROAM_SCAN_N_PROBES); + + /* Move pointer to ahead of SETSCANNPROBES */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &nprobes); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_ROAM_SCAN_N_PROBES), + cfg_max(CFG_LFR_ROAM_SCAN_N_PROBES)); + ret = -EINVAL; + goto exit; + } + + if (!cfg_in_range(CFG_LFR_ROAM_SCAN_N_PROBES, nprobes)) { + hdd_err("NProbes value %d is out of range (Min: %d Max: %d)", + nprobes, + cfg_min(CFG_LFR_ROAM_SCAN_N_PROBES), + cfg_max(CFG_LFR_ROAM_SCAN_N_PROBES)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to Set nProbes = %d", + nprobes); + + sme_update_roam_scan_n_probes(hdd_ctx->mac_handle, + adapter->vdev_id, nprobes); + +exit: + return ret; +} + +static int drv_cmd_get_scan_n_probes(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t val; + char extra[32]; + uint8_t len = 0; + QDF_STATUS status; + + status = sme_get_roam_scan_n_probes(hdd_ctx->mac_handle, + adapter->vdev_id, + &val); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + hdd_debug("vdev_id: %u, scan_n_probes: %u", + adapter->vdev_id, val); + + len = scnprintf(extra, sizeof(extra), "%s %d", command, val); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_scan_home_away_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint16_t home_away_time = cfg_default(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME); + + /* input value is in units of msec */ + + /* Move pointer to ahead of SETSCANHOMEAWAYTIME */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou16(value, 10, &home_away_time); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME), + cfg_max(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME)); + ret = -EINVAL; + goto exit; + } + + if (!cfg_in_range(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME, home_away_time)) { + hdd_err("home_away_time value %d is out of range (min: %d max: %d)", + home_away_time, + cfg_min(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME), + cfg_max(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to Set scan away time = %d", + home_away_time); + + sme_update_roam_scan_home_away_time(hdd_ctx->mac_handle, + adapter->vdev_id, + home_away_time, + true); + +exit: + return ret; +} + +static int drv_cmd_get_scan_home_away_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint16_t val; + char extra[32] = {0}; + uint8_t len = 0; + QDF_STATUS status; + + status = sme_get_roam_scan_home_away_time(hdd_ctx->mac_handle, + adapter->vdev_id, + &val); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + hdd_debug("vdev_id: %u, scan home away time: %u", + adapter->vdev_id, val); + + len = scnprintf(extra, sizeof(extra), "%s %d", command, val); + len = QDF_MIN(priv_data->total_len, len + 1); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_reassoc(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_parse_reassoc(adapter, command, priv_data->total_len); +} + +static int drv_cmd_set_wes_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t wes_mode = cfg_default(CFG_LFR_ENABLE_WES_MODE); + + /* Move pointer to ahead of SETWESMODE */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &wes_mode); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_ENABLE_WES_MODE), + cfg_max(CFG_LFR_ENABLE_WES_MODE)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to Set WES Mode rssi diff = %d", wes_mode); + + sme_update_wes_mode(hdd_ctx->mac_handle, wes_mode, adapter->vdev_id); + +exit: + return ret; +} + +static int drv_cmd_get_wes_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + bool wes_mode = sme_get_wes_mode(hdd_ctx->mac_handle); + char extra[32]; + uint8_t len = 0; + + len = scnprintf(extra, sizeof(extra), "%s %d", command, wes_mode); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_opportunistic_rssi_diff(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t diff = + cfg_default(CFG_LFR_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF); + + /* Move pointer to ahead of SETOPPORTUNISTICRSSIDIFF */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &diff); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed"); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to Set Opportunistic Threshold diff = %d", + diff); + + sme_set_roam_opportunistic_scan_threshold_diff(hdd_ctx->mac_handle, + adapter->vdev_id, + diff); + +exit: + return ret; +} + +static int drv_cmd_get_opportunistic_rssi_diff(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + mac_handle_t mac_handle = hdd_ctx->mac_handle; + int8_t val = sme_get_roam_opportunistic_scan_threshold_diff(mac_handle); + char extra[32]; + uint8_t len = 0; + + len = scnprintf(extra, sizeof(extra), "%s %d", command, val); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_roam_rescan_rssi_diff(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t rescan_rssi_diff = cfg_default(CFG_LFR_ROAM_RESCAN_RSSI_DIFF); + + /* Move pointer to ahead of SETROAMRESCANRSSIDIFF */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &rescan_rssi_diff); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed"); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to Set Roam Rescan RSSI Diff = %d", + rescan_rssi_diff); + + sme_set_roam_rescan_rssi_diff(hdd_ctx->mac_handle, + adapter->vdev_id, + rescan_rssi_diff); + +exit: + return ret; +} + +static int drv_cmd_get_roam_rescan_rssi_diff(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t val = sme_get_roam_rescan_rssi_diff(hdd_ctx->mac_handle); + char extra[32]; + uint8_t len = 0; + + len = scnprintf(extra, sizeof(extra), "%s %d", command, val); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_set_fast_roam(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t lfr_mode = cfg_default(CFG_LFR_FEATURE_ENABLED); + + /* Move pointer to ahead of SETFASTROAM */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &lfr_mode); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_FEATURE_ENABLED), + cfg_max(CFG_LFR_FEATURE_ENABLED)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to change lfr mode = %d", + lfr_mode); + + ucfg_mlme_set_lfr_enabled(hdd_ctx->psoc, (bool)lfr_mode); + sme_update_is_fast_roam_ini_feature_enabled(hdd_ctx->mac_handle, + adapter->vdev_id, + lfr_mode); + +exit: + return ret; +} + +static int drv_cmd_set_fast_transition(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t ft = cfg_default(CFG_LFR_FAST_TRANSITION_ENABLED); + + /* Move pointer to ahead of SETFASTROAM */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &ft); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_FAST_TRANSITION_ENABLED), + cfg_max(CFG_LFR_FAST_TRANSITION_ENABLED)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to change ft mode = %d", ft); + + ucfg_mlme_set_fast_transition_enabled(hdd_ctx->psoc, (bool)ft); + +exit: + return ret; +} + +static int drv_cmd_fast_reassoc(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + qdf_freq_t freq = 0; + tSirMacAddr bssid; + uint32_t roam_id = INVALID_ROAM_ID; + tCsrRoamModifyProfileFields mod_fields; + tCsrHandoffRequest req; + struct hdd_station_ctx *sta_ctx; + mac_handle_t mac_handle; + + if (QDF_STA_MODE != adapter->device_mode) { + hdd_warn("Unsupported in mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + /* if not associated, no need to proceed with reassoc */ + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + hdd_warn("Not associated!"); + ret = -EINVAL; + goto exit; + } + + ret = hdd_parse_reassoc_command_v1_data(value, bssid, + &freq); + if (ret) { + hdd_err("Failed to parse reassoc command data"); + goto exit; + } + + mac_handle = hdd_ctx->mac_handle; + /* + * if the target bssid is same as currently associated AP, + * issue reassoc to same AP + */ + if (!qdf_mem_cmp(bssid, sta_ctx->conn_info.bssid.bytes, + QDF_MAC_ADDR_SIZE)) { + hdd_warn("Reassoc BSSID is same as currently associated AP bssid"); + if (roaming_offload_enabled(hdd_ctx)) { + hdd_wma_send_fastreassoc_cmd( + adapter, bssid, sta_ctx->conn_info.chan_freq); + } else { + sme_get_modify_profile_fields(mac_handle, + adapter->vdev_id, + &mod_fields); + sme_roam_reassoc(mac_handle, adapter->vdev_id, + NULL, mod_fields, &roam_id, 1); + } + return 0; + } + + /* Check freq number is a valid freq number */ + if (freq && QDF_STATUS_SUCCESS != + wlan_hdd_validate_operation_channel(adapter, freq)) { + hdd_err("Invalid freq [%d]", freq); + return -EINVAL; + } + + if (roaming_offload_enabled(hdd_ctx)) { + hdd_wma_send_fastreassoc_cmd(adapter, bssid, freq); + goto exit; + } + /* Proceed with reassoc */ + req.ch_freq = freq; + req.src = FASTREASSOC; + qdf_mem_copy(req.bssid.bytes, bssid, sizeof(tSirMacAddr)); + sme_handoff_request(mac_handle, adapter->vdev_id, &req); +exit: + return ret; +} + +static int drv_cmd_set_roam_scan_control(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t roam_scan_control = 0; + + /* Move pointer to ahead of SETROAMSCANCONTROL */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &roam_scan_control); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed"); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to Set roam scan control = %d", + roam_scan_control); + + if (0 != roam_scan_control) { + ret = 0; /* return success but ignore param value "true" */ + goto exit; + } + + sme_set_roam_scan_control(hdd_ctx->mac_handle, + adapter->vdev_id, + roam_scan_control); + +exit: + return ret; +} + +static int drv_cmd_set_okc_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint32_t okc_mode; + struct pmkid_mode_bits pmkid_modes; + mac_handle_t mac_handle; + uint32_t cur_pmkid_modes; + QDF_STATUS status; + + hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes); + + /* + * Check if the features PMKID/ESE/11R are supported simultaneously, + * then this operation is not permitted (return FAILURE) + */ + mac_handle = hdd_ctx->mac_handle; + if (sme_get_is_ese_feature_enabled(mac_handle) && + pmkid_modes.fw_okc && + sme_get_is_ft_feature_enabled(mac_handle)) { + hdd_warn("PMKID/ESE/11R are supported simultaneously hence this operation is not permitted!"); + ret = -EPERM; + goto exit; + } + + /* Move pointer to ahead of SETOKCMODE */ + value = value + command_len + 1; + + /* get the current configured value */ + status = ucfg_mlme_get_pmkid_modes(hdd_ctx->psoc, + &cur_pmkid_modes); + if (status != QDF_STATUS_SUCCESS) + hdd_err("get pmkid modes failed"); + + okc_mode = cur_pmkid_modes & CFG_PMKID_MODES_OKC; + + /* Convert the value from ascii to integer */ + ret = kstrtou32(value, 10, &okc_mode); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("value out of range [0 - 1]"); + ret = -EINVAL; + goto exit; + } + + if ((okc_mode < 0) || + (okc_mode > 1)) { + hdd_err("Okc mode value %d is out of range (Min: 0 Max: 1)", + okc_mode); + ret = -EINVAL; + goto exit; + } + hdd_debug("Received Command to change okc mode = %d", + okc_mode); + + if (okc_mode) + cur_pmkid_modes |= CFG_PMKID_MODES_OKC; + else + cur_pmkid_modes &= ~CFG_PMKID_MODES_OKC; + status = ucfg_mlme_set_pmkid_modes(hdd_ctx->psoc, + cur_pmkid_modes); + if (status != QDF_STATUS_SUCCESS) { + ret = -EPERM; + hdd_err("set pmkid modes failed"); + } +exit: + return ret; +} + +static int drv_cmd_get_roam_scan_control(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + bool roam_scan_control = sme_get_roam_scan_control(hdd_ctx->mac_handle); + char extra[32]; + uint8_t len = 0; + + len = scnprintf(extra, sizeof(extra), "%s %d", + command, roam_scan_control); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_bt_coex_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + char *coex_mode; + + coex_mode = command + 11; + if ('1' == *coex_mode) { + hdd_debug("BTCOEXMODE %d", *coex_mode); + hdd_ctx->bt_coex_mode_set = true; + ret = wlan_hdd_scan_abort(adapter); + if (ret < 0) { + hdd_err("Failed to abort existing scan status: %d", + ret); + } + } else if ('2' == *coex_mode) { + hdd_debug("BTCOEXMODE %d", *coex_mode); + hdd_ctx->bt_coex_mode_set = false; + } + + return ret; +} + +static int drv_cmd_scan_active(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + hdd_ctx->ioctl_scan_mode = eSIR_ACTIVE_SCAN; + return 0; +} + +static int drv_cmd_scan_passive(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + hdd_ctx->ioctl_scan_mode = eSIR_PASSIVE_SCAN; + return 0; +} + +static int drv_cmd_get_dwell_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + char extra[32]; + uint8_t len = 0; + + memset(extra, 0, sizeof(extra)); + ret = hdd_get_dwell_time(hdd_ctx->psoc, command, extra, + sizeof(extra), &len); + len = QDF_MIN(priv_data->total_len, len + 1); + if (ret != 0 || copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + goto exit; + } + ret = len; +exit: + return ret; +} + +static int drv_cmd_set_dwell_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_set_dwell_time(hdd_ctx->psoc, command); +} + +static int drv_cmd_conc_set_dwell_time(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + u8 *command, + u8 command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_conc_set_dwell_time(adapter, command); +} + +static int drv_cmd_miracast(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + QDF_STATUS ret_status; + int ret = 0; + uint8_t filter_type = 0; + uint8_t *value; + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + value = command + 9; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &filter_type); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range"); + ret = -EINVAL; + goto exit; + } + if ((filter_type < WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL) || + (filter_type > WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL)) { + hdd_err("Accepted Values are 0 to 2. 0-Disabled, 1-Source, 2-Sink"); + ret = -EINVAL; + goto exit; + } + /* Filtertype value should be either 0-Disabled, 1-Source, 2-sink */ + hdd_ctx->miracast_value = filter_type; + + ret_status = sme_set_miracast(hdd_ctx->mac_handle, filter_type); + if (QDF_STATUS_SUCCESS != ret_status) { + hdd_err("Failed to set miracast"); + return -EBUSY; + } + ret_status = ucfg_scan_set_miracast(hdd_ctx->psoc, + filter_type ? true : false); + if (QDF_IS_STATUS_ERROR(ret_status)) { + hdd_err("Failed to set miracastn scan"); + return -EBUSY; + } + + if (policy_mgr_is_mcc_in_24G(hdd_ctx->psoc)) + return wlan_hdd_set_mas(adapter, filter_type); + +exit: + return ret; +} + +static int drv_cmd_tput_debug_mode_enable(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + u8 *command, + u8 command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_enable_unit_test_commands(adapter, hdd_ctx); +} + +static int drv_cmd_tput_debug_mode_disable(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + u8 *command, + u8 command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_disable_unit_test_commands(adapter, hdd_ctx); +} + +#ifdef FEATURE_WLAN_RMC +/* Function header is left blank intentionally */ +static int hdd_parse_setrmcenable_command(uint8_t *command, + uint8_t *rmc_enable) +{ + uint8_t *in_ptr = command; + int temp_int; + int v = 0; + char buf[32]; + *rmc_enable = 0; + + in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); + + if (!in_ptr) + return 0; + else if (SPACE_ASCII_VALUE != *in_ptr) + return 0; + + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + if ('\0' == *in_ptr) + return 0; + + v = sscanf(in_ptr, "%31s ", buf); + if (1 != v) + return -EINVAL; + + v = kstrtos32(buf, 10, &temp_int); + if (v < 0) + return -EINVAL; + + *rmc_enable = temp_int; + + hdd_debug("rmc_enable: %d", *rmc_enable); + + return 0; +} + +/* Function header is left blank intentionally */ +static int hdd_parse_setrmcactionperiod_command(uint8_t *pvalue, + uint32_t *paction_period) +{ + uint8_t *inptr = pvalue; + int temp_int; + int v = 0; + char buf[32]; + *paction_period = 0; + + inptr = strnchr(pvalue, strlen(pvalue), SPACE_ASCII_VALUE); + + if (!inptr) + return -EINVAL; + else if (SPACE_ASCII_VALUE != *inptr) + return -EINVAL; + + while ((SPACE_ASCII_VALUE == *inptr) && ('\0' != *inptr)) + inptr++; + + if ('\0' == *inptr) + return 0; + + v = sscanf(inptr, "%31s ", buf); + if (1 != v) + return -EINVAL; + + v = kstrtos32(buf, 10, &temp_int); + if (v < 0) + return -EINVAL; + + if (!cfg_in_range(CFG_RMC_ACTION_PERIOD_FREQUENCY, temp_int)) + return -EINVAL; + + *paction_period = temp_int; + + hdd_debug("action_period: %d", *paction_period); + + return 0; +} + +/* Function header is left blank intentionally */ +static int hdd_parse_setrmcrate_command(uint8_t *command, + uint32_t *rate, + enum tx_rate_info *tx_flags) +{ + uint8_t *in_ptr = command; + int temp_int; + int v = 0; + char buf[32]; + *rate = 0; + *tx_flags = 0; + + in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); + + if (!in_ptr) + return -EINVAL; + else if (SPACE_ASCII_VALUE != *in_ptr) + return -EINVAL; + + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + if ('\0' == *in_ptr) + return 0; + + v = sscanf(in_ptr, "%31s ", buf); + if (1 != v) + return -EINVAL; + + v = kstrtos32(buf, 10, &temp_int); + if (v < 0) + return -EINVAL; + + switch (temp_int) { + default: + hdd_warn("Unsupported rate: %d", temp_int); + return -EINVAL; + case 0: + case 6: + case 9: + case 12: + case 18: + case 24: + case 36: + case 48: + case 54: + *tx_flags = TX_RATE_LEGACY; + *rate = temp_int * 10; + break; + case 65: + *tx_flags = TX_RATE_HT20; + *rate = temp_int * 10; + break; + case 72: + *tx_flags = TX_RATE_HT20 | TX_RATE_SGI; + *rate = 722; + break; + } + + hdd_debug("Rate: %d", *rate); + + return 0; +} + +static int drv_cmd_set_rmc_enable(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t rmc_enable = 0; + int status; + mac_handle_t mac_handle; + + if ((QDF_IBSS_MODE != adapter->device_mode) && + (QDF_SAP_MODE != adapter->device_mode)) { + hdd_err("Received SETRMCENABLE cmd in invalid mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + hdd_err("SETRMCENABLE cmd is allowed only in IBSS/SOFTAP mode"); + ret = -EINVAL; + goto exit; + } + + status = hdd_parse_setrmcenable_command(value, &rmc_enable); + if (status) { + hdd_err("Invalid SETRMCENABLE command"); + ret = -EINVAL; + goto exit; + } + + hdd_debug("rmc_enable %d", rmc_enable); + mac_handle = hdd_ctx->mac_handle; + + if (true == rmc_enable) { + status = sme_enable_rmc(mac_handle, adapter->vdev_id); + } else if (false == rmc_enable) { + status = sme_disable_rmc(mac_handle, adapter->vdev_id); + } else { + hdd_err("Invalid SETRMCENABLE command %d", rmc_enable); + ret = -EINVAL; + goto exit; + } + + if (QDF_STATUS_SUCCESS != status) { + hdd_err("SETRMC %d failed status %d", rmc_enable, status); + ret = -EINVAL; + goto exit; + } + +exit: + return ret; +} + +static int drv_cmd_set_rmc_action_period(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint32_t action_period = 0; + int status; + mac_handle_t mac_handle; + + if ((QDF_IBSS_MODE != adapter->device_mode) && + (QDF_SAP_MODE != adapter->device_mode)) { + hdd_err("Received SETRMC cmd in invalid mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + hdd_err("SETRMC cmd is allowed only in IBSS/SOFTAP mode"); + ret = -EINVAL; + goto exit; + } + + status = hdd_parse_setrmcactionperiod_command(value, &action_period); + if (status) { + hdd_err("Invalid SETRMCACTIONPERIOD command"); + ret = -EINVAL; + goto exit; + } + + hdd_debug("action_period %d", action_period); + mac_handle = hdd_ctx->mac_handle; + + if (ucfg_mlme_set_rmc_action_period_freq(hdd_ctx->psoc, + action_period) != + QDF_STATUS_SUCCESS) { + hdd_err("Could not set SETRMCACTIONPERIOD %d", action_period); + ret = -EINVAL; + goto exit; + } + + status = sme_send_rmc_action_period(mac_handle, + adapter->vdev_id); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Could not send cesium enable indication %d", + status); + ret = -EINVAL; + goto exit; + } + +exit: + return ret; +} + +static int drv_cmd_set_rmc_tx_rate(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint32_t rate = 0; + enum tx_rate_info tx_flags = 0; + tSirRateUpdateInd params = {0}; + int status; + bool bval = false; + + if ((QDF_IBSS_MODE != adapter->device_mode) && + (QDF_SAP_MODE != adapter->device_mode)) { + hdd_err("Received SETRMCTXRATE cmd in invalid mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + hdd_err("SETRMCTXRATE cmd is allowed only in IBSS/SOFTAP mode"); + ret = -EINVAL; + goto exit; + } + + status = hdd_parse_setrmcrate_command(value, &rate, &tx_flags); + if (status) { + hdd_err("Invalid SETRMCTXRATE command"); + ret = -EINVAL; + goto exit; + } + hdd_debug("rate %d", rate); + + /* + * Fill the user specifieed RMC rate param + * and the derived tx flags. + */ + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("unable to get vht_enable2x2"); + ret = -EINVAL; + goto exit; + } + params.nss = (bval == 0) ? 0 : 1; + params.reliableMcastDataRate = rate; + params.reliableMcastDataRateTxFlag = tx_flags; + params.dev_mode = adapter->device_mode; + params.bcastDataRate = -1; + memcpy(params.bssid.bytes, + adapter->mac_addr.bytes, + sizeof(params.bssid)); + status = sme_send_rate_update_ind(hdd_ctx->mac_handle, + ¶ms); + +exit: + return ret; +} +#endif /* FEATURE_WLAN_RMC */ + +#ifdef FEATURE_WLAN_ESE +static int drv_cmd_set_ccx_roam_scan_channels(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint32_t channel_freq_list[CFG_VALID_CHANNEL_LIST_LEN] = { 0 }; + uint8_t num_channels = 0; + QDF_STATUS status; + mac_handle_t mac_handle; + + if (!hdd_ctx) { + hdd_err("invalid hdd ctx"); + ret = -EINVAL; + goto exit; + } + + ret = hdd_parse_channellist(hdd_ctx, value, channel_freq_list, + &num_channels); + if (ret) { + hdd_err("Failed to parse channel list information"); + goto exit; + } + if (num_channels > CFG_VALID_CHANNEL_LIST_LEN) { + hdd_err("number of channels (%d) supported exceeded max (%d)", + num_channels, + CFG_VALID_CHANNEL_LIST_LEN); + ret = -EINVAL; + goto exit; + } + + mac_handle = hdd_ctx->mac_handle; + if (!sme_validate_channel_list(mac_handle, channel_freq_list, + num_channels)) { + hdd_err("List contains invalid channel(s)"); + ret = -EINVAL; + goto exit; + } + + status = sme_set_ese_roam_scan_channel_list(mac_handle, + adapter->vdev_id, + channel_freq_list, + num_channels); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed to update channel list information"); + ret = -EINVAL; + goto exit; + } + +exit: + return ret; +} + +static int drv_cmd_get_tsm_stats(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + char extra[128] = { 0 }; + int len = 0; + uint8_t tid = 0; + struct hdd_station_ctx *sta_ctx; + tAniTrafStrmMetrics tsm_metrics = {0}; + + if ((QDF_STA_MODE != adapter->device_mode) && + (QDF_P2P_CLIENT_MODE != adapter->device_mode)) { + hdd_warn("Unsupported in mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + /* if not associated, return error */ + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + hdd_err("Not associated!"); + ret = -EINVAL; + goto exit; + } + + /* Move pointer to ahead of GETTSMSTATS */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &tid); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + TID_MIN_VALUE, + TID_MAX_VALUE); + ret = -EINVAL; + goto exit; + } + if ((tid < TID_MIN_VALUE) || (tid > TID_MAX_VALUE)) { + hdd_err("tid value %d is out of range (Min: %d Max: %d)", + tid, TID_MIN_VALUE, TID_MAX_VALUE); + ret = -EINVAL; + goto exit; + } + hdd_debug("Received Command to get tsm stats tid = %d", + tid); + ret = hdd_get_tsm_stats(adapter, tid, &tsm_metrics); + if (ret) { + hdd_err("failed to get tsm stats"); + goto exit; + } + hdd_debug( + "UplinkPktQueueDly(%d) UplinkPktQueueDlyHist[0](%d) UplinkPktQueueDlyHist[1](%d) UplinkPktQueueDlyHist[2](%d) UplinkPktQueueDlyHist[3](%d) UplinkPktTxDly(%u) UplinkPktLoss(%d) UplinkPktCount(%d) RoamingCount(%d) RoamingDly(%d)", + tsm_metrics.UplinkPktQueueDly, + tsm_metrics.UplinkPktQueueDlyHist[0], + tsm_metrics.UplinkPktQueueDlyHist[1], + tsm_metrics.UplinkPktQueueDlyHist[2], + tsm_metrics.UplinkPktQueueDlyHist[3], + tsm_metrics.UplinkPktTxDly, + tsm_metrics.UplinkPktLoss, + tsm_metrics.UplinkPktCount, + tsm_metrics.RoamingCount, + tsm_metrics.RoamingDly); + /* + * Output TSM stats is of the format + * GETTSMSTATS [PktQueueDly] + * [PktQueueDlyHist[0]]:[PktQueueDlyHist[1]] ...[RoamingDly] + * eg., GETTSMSTATS 10 1:0:0:161 20 1 17 8 39800 + */ + len = scnprintf(extra, + sizeof(extra), + "%s %d %d:%d:%d:%d %u %d %d %d %d", + command, + tsm_metrics.UplinkPktQueueDly, + tsm_metrics.UplinkPktQueueDlyHist[0], + tsm_metrics.UplinkPktQueueDlyHist[1], + tsm_metrics.UplinkPktQueueDlyHist[2], + tsm_metrics.UplinkPktQueueDlyHist[3], + tsm_metrics.UplinkPktTxDly, + tsm_metrics.UplinkPktLoss, + tsm_metrics.UplinkPktCount, + tsm_metrics.RoamingCount, + tsm_metrics.RoamingDly); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + goto exit; + } + +exit: + return ret; +} + +static int drv_cmd_set_cckm_ie(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + uint8_t *cckm_ie = NULL; + uint8_t cckm_ie_len = 0; + + ret = hdd_parse_get_cckm_ie(value, &cckm_ie, &cckm_ie_len); + if (ret) { + hdd_err("Failed to parse cckm ie data"); + goto exit; + } + + if (cckm_ie_len > DOT11F_IE_RSN_MAX_LEN) { + hdd_err("CCKM Ie input length is more than max[%d]", + DOT11F_IE_RSN_MAX_LEN); + if (cckm_ie) { + qdf_mem_free(cckm_ie); + cckm_ie = NULL; + } + ret = -EINVAL; + goto exit; + } + + sme_set_cckm_ie(hdd_ctx->mac_handle, adapter->vdev_id, + cckm_ie, cckm_ie_len); + if (cckm_ie) { + qdf_mem_free(cckm_ie); + cckm_ie = NULL; + } + +exit: + return ret; +} + +static int drv_cmd_ccx_beacon_req(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + tCsrEseBeaconReq req = {0}; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (QDF_STA_MODE != adapter->device_mode) { + hdd_warn("Unsupported in mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + + ret = hdd_parse_ese_beacon_req(hdd_ctx->pdev, value, &req); + if (ret) { + hdd_err("Failed to parse ese beacon req"); + goto exit; + } + + if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + hdd_debug("Not associated"); + + if (!req.numBcnReqIe) + return -EINVAL; + + hdd_indicate_ese_bcn_report_no_results(adapter, + req.bcnReq[0].measurementToken, + 0x02, /* BIT(1) set for measurement done */ + 0); /* no BSS */ + goto exit; + } + + status = sme_set_ese_beacon_request(hdd_ctx->mac_handle, + adapter->vdev_id, + &req); + + if (QDF_STATUS_E_RESOURCES == status) { + hdd_err("sme_set_ese_beacon_request failed (%d), a request already in progress", + status); + ret = -EBUSY; + goto exit; + } else if (QDF_STATUS_SUCCESS != status) { + hdd_err("sme_set_ese_beacon_request failed (%d)", + status); + ret = -EINVAL; + goto exit; + } + +exit: + return ret; +} + +/** + * drv_cmd_ccx_plm_req() - Set ESE PLM request + * @adapter: Pointer to the HDD adapter + * @hdd_ctx: Pointer to the HDD context + * @command: Driver command string + * @command_len: Driver command string length + * @priv_data: Private data coming with the driver command. Unused here + * + * This function handles driver command that sets the ESE PLM request + * + * Return: 0 on success; negative errno otherwise + */ +static int drv_cmd_ccx_plm_req(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + QDF_STATUS status; + struct plm_req_params *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return -ENOMEM; + + status = hdd_parse_plm_cmd(command, req); + if (QDF_IS_STATUS_SUCCESS(status)) { + req->vdev_id = adapter->vdev_id; + status = sme_set_plm_request(hdd_ctx->mac_handle, req); + } + qdf_mem_free(req); + + return qdf_status_to_os_return(status); +} + +/** + * drv_cmd_set_ccx_mode() - Set ESE mode + * @adapter: Pointer to the HDD adapter + * @hdd_ctx: Pointer to the HDD context + * @command: Driver command string + * @command_len: Driver command string length + * @priv_data: Private data coming with the driver command. Unused here + * + * This function handles driver command that sets ESE mode + * + * Return: 0 on success; negative errno otherwise + */ +static int drv_cmd_set_ccx_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t ese_mode = cfg_default(CFG_LFR_ESE_FEATURE_ENABLED); + struct pmkid_mode_bits pmkid_modes; + mac_handle_t mac_handle; + + hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes); + mac_handle = hdd_ctx->mac_handle; + /* + * Check if the features OKC/ESE/11R are supported simultaneously, + * then this operation is not permitted (return FAILURE) + */ + if (sme_get_is_ese_feature_enabled(mac_handle) && + pmkid_modes.fw_okc && + sme_get_is_ft_feature_enabled(mac_handle)) { + hdd_warn("OKC/ESE/11R are supported simultaneously hence this operation is not permitted!"); + ret = -EPERM; + goto exit; + } + + /* Move pointer to ahead of SETCCXMODE */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &ese_mode); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_ESE_FEATURE_ENABLED), + cfg_max(CFG_LFR_ESE_FEATURE_ENABLED)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to change ese mode = %d", ese_mode); + + sme_update_is_ese_feature_enabled(mac_handle, + adapter->vdev_id, + ese_mode); + +exit: + return ret; +} +#endif /* FEATURE_WLAN_ESE */ + +static int drv_cmd_set_mc_rate(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint32_t target_rate = 0; + + /* input value is in units of hundred kbps */ + + /* Move pointer to ahead of SETMCRATE */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer, decimal base */ + ret = kstrtouint(value, 10, &target_rate); + + ret = wlan_hdd_set_mc_rate(adapter, target_rate); + return ret; +} + +static int drv_cmd_max_tx_power(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + int tx_power; + QDF_STATUS status; + uint8_t *value = command; + struct qdf_mac_addr bssid = QDF_MAC_ADDR_BCAST_INIT; + struct qdf_mac_addr selfmac = QDF_MAC_ADDR_BCAST_INIT; + struct hdd_adapter *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_DRV_CMD_MAX_TX_POWER; + + ret = hdd_parse_setmaxtxpower_command(value, &tx_power); + if (ret) { + hdd_err("Invalid MAXTXPOWER command"); + return ret; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + /* Assign correct self MAC address */ + qdf_copy_macaddr(&bssid, + &adapter->mac_addr); + qdf_copy_macaddr(&selfmac, + &adapter->mac_addr); + + hdd_debug("Device mode %d max tx power %d selfMac: " + QDF_MAC_ADDR_FMT " bssId: " QDF_MAC_ADDR_FMT, + adapter->device_mode, tx_power, + QDF_MAC_ADDR_REF(selfmac.bytes), + QDF_MAC_ADDR_REF(bssid.bytes)); + + status = sme_set_max_tx_power(hdd_ctx->mac_handle, + bssid, selfmac, tx_power); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Set max tx power failed"); + ret = -EINVAL; + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + goto exit; + } + hdd_debug("Set max tx power success"); + hdd_adapter_dev_put_debug(adapter, dbgid); + } + +exit: + return ret; +} + +static int drv_cmd_set_dfs_scan_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t *value = command; + uint8_t dfs_scan_mode = cfg_default(CFG_LFR_ROAMING_DFS_CHANNEL); + + /* Move pointer to ahead of SETDFSSCANMODE */ + value = value + command_len + 1; + + /* Convert the value from ascii to integer */ + ret = kstrtou8(value, 10, &dfs_scan_mode); + if (ret < 0) { + /* + * If the input value is greater than max value of datatype, + * then also kstrtou8 fails + */ + hdd_err("kstrtou8 failed range [%d - %d]", + cfg_min(CFG_LFR_ROAMING_DFS_CHANNEL), + cfg_max(CFG_LFR_ROAMING_DFS_CHANNEL)); + ret = -EINVAL; + goto exit; + } + + if (!cfg_in_range(CFG_LFR_ROAMING_DFS_CHANNEL, dfs_scan_mode)) { + hdd_err("dfs_scan_mode value %d is out of range (Min: %d Max: %d)", + dfs_scan_mode, + cfg_min(CFG_LFR_ROAMING_DFS_CHANNEL), + cfg_max(CFG_LFR_ROAMING_DFS_CHANNEL)); + ret = -EINVAL; + goto exit; + } + + hdd_debug("Received Command to Set DFS Scan Mode = %d", + dfs_scan_mode); + + /* When DFS scanning is disabled, the DFS channels need to be + * removed from the operation of device. + */ + ret = wlan_hdd_enable_dfs_chan_scan(hdd_ctx, + dfs_scan_mode != ROAMING_DFS_CHANNEL_DISABLED); + if (ret < 0) { + /* Some conditions prevented it from disabling DFS channels */ + hdd_err("disable/enable DFS channel request was denied"); + goto exit; + } + + sme_update_dfs_scan_mode(hdd_ctx->mac_handle, adapter->vdev_id, + dfs_scan_mode); + +exit: + return ret; +} + +static int drv_cmd_get_dfs_scan_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + uint8_t dfs_scan_mode = sme_get_dfs_scan_mode(hdd_ctx->mac_handle); + char extra[32]; + uint8_t len = 0; + + len = scnprintf(extra, sizeof(extra), "%s %d", command, dfs_scan_mode); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_get_link_status(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + int value = wlan_hdd_get_link_status(adapter); + char extra[32]; + uint8_t len; + + len = scnprintf(extra, sizeof(extra), "%s %d", command, value); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +static int drv_cmd_enable_ext_wow(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + uint8_t *value = command; + int set_value; + + /* Move pointer to ahead of ENABLEEXTWOW */ + value = value + command_len; + + if (!(sscanf(value, "%d", &set_value))) { + hdd_info("No input identified"); + return -EINVAL; + } + + return hdd_enable_ext_wow_parser(adapter, + adapter->vdev_id, + set_value); +} + +static int drv_cmd_set_app1_params(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + + /* Move pointer to ahead of SETAPP1PARAMS */ + value = value + command_len; + + ret = hdd_set_app_type1_parser(adapter, + value, strlen(value)); + if (ret >= 0) + hdd_ctx->is_extwow_app_type1_param_set = true; + + return ret; +} + +static int drv_cmd_set_app2_params(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + + /* Move pointer to ahead of SETAPP2PARAMS */ + value = value + command_len; + + ret = hdd_set_app_type2_parser(adapter, value, strlen(value)); + if (ret >= 0) + hdd_ctx->is_extwow_app_type2_param_set = true; + + return ret; +} +#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */ + +#ifdef FEATURE_WLAN_TDLS +/** + * drv_cmd_tdls_secondary_channel_offset() - secondary tdls off channel offset + * @adapter: Pointer to the HDD adapter + * @hdd_ctx: Pointer to the HDD context + * @command: Driver command string + * @command_len: Driver command string length + * @priv_data: Private data coming with the driver command. Unused here + * + * This function handles driver command that sets the secondary tdls off channel + * offset + * + * Return: 0 on success; negative errno otherwise + */ +static int drv_cmd_tdls_secondary_channel_offset(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + int set_value; + + /* Move pointer to point the string */ + value += command_len; + + ret = sscanf(value, "%d", &set_value); + if (ret != 1) + return -EINVAL; + + hdd_debug("Tdls offchannel offset:%d", set_value); + + ret = hdd_set_tdls_secoffchanneloffset(hdd_ctx, adapter, set_value); + + return ret; +} + +/** + * drv_cmd_tdls_off_channel_mode() - set tdls off channel mode + * @adapter: Pointer to the HDD adapter + * @hdd_ctx: Pointer to the HDD context + * @command: Driver command string + * @command_len: Driver command string length + * @priv_data: Private data coming with the driver command. Unused here + * + * This function handles driver command that sets tdls off channel mode + * + * Return: 0 on success; negative errno otherwise + */ +static int drv_cmd_tdls_off_channel_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + int set_value; + + /* Move pointer to point the string */ + value += command_len; + + ret = sscanf(value, "%d", &set_value); + if (ret != 1) + return -EINVAL; + + hdd_debug("Tdls offchannel mode:%d", set_value); + + ret = hdd_set_tdls_offchannelmode(hdd_ctx, adapter, set_value); + + return ret; +} + +/** + * drv_cmd_tdls_off_channel() - set tdls off channel number + * @adapter: Pointer to the HDD adapter + * @hdd_ctx: Pointer to the HDD context + * @command: Driver command string + * @command_len: Driver command string length + * @priv_data: Private data coming with the driver command. Unused here + * + * This function handles driver command that sets tdls off channel number + * + * Return: 0 on success; negative errno otherwise + */ +static int drv_cmd_tdls_off_channel(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + int channel; + enum channel_state reg_state; + + /* Move pointer to point the string */ + value += command_len; + + ret = sscanf(value, "%d", &channel); + if (ret != 1) + return -EINVAL; + reg_state = wlan_reg_get_channel_state(hdd_ctx->pdev, channel); + + if (reg_state == CHANNEL_STATE_DFS || + reg_state == CHANNEL_STATE_DISABLE || + reg_state == CHANNEL_STATE_INVALID) { + hdd_err("reg state of the channel %d is %d and not supported", + channel, reg_state); + return -EINVAL; + } + + hdd_debug("Tdls offchannel num: %d", channel); + + ret = hdd_set_tdls_offchannel(hdd_ctx, adapter, channel); + + return ret; +} + +/** + * drv_cmd_tdls_scan() - set tdls scan type + * @adapter: Pointer to the HDD adapter + * @hdd_ctx: Pointer to the HDD context + * @command: Driver command string + * @command_len: Driver command string length + * @priv_data: Private data coming with the driver command. Unused here + * + * This function handles driver command that sets tdls scan type + * + * Return: 0 on success; negative errno otherwise + */ +static int drv_cmd_tdls_scan(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint8_t *value = command; + int set_value; + + /* Move pointer to point the string */ + value += command_len; + + ret = sscanf(value, "%d", &set_value); + if (ret != 1) + return -EINVAL; + + hdd_debug("Tdls scan type val: %d", set_value); + + ret = hdd_set_tdls_scan_type(hdd_ctx, set_value); + + return ret; +} +#endif + +static int drv_cmd_get_rssi(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret = 0; + int8_t rssi = 0; + char extra[32]; + + uint8_t len = 0; + + wlan_hdd_get_rssi(adapter, &rssi); + + len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi); + len = QDF_MIN(priv_data->total_len, len + 1); + + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("Failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +static int drv_cmd_get_linkspeed(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int ret; + uint32_t link_speed = 0; + char extra[32]; + uint8_t len = 0; + + ret = wlan_hdd_get_link_speed(adapter, &link_speed); + if (0 != ret) + return ret; + + len = scnprintf(extra, sizeof(extra), "%s %d", command, link_speed); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("Failed to copy data to user buffer"); + ret = -EFAULT; + } + + return ret; +} + +#ifdef WLAN_FEATURE_PACKET_FILTERING +/** + * hdd_set_rx_filter() - set RX filter + * @adapter: Pointer to adapter + * @action: Filter action + * @pattern: Address pattern + * + * Address pattern is most significant byte of address for example + * 0x01 for IPV4 multicast address + * 0x33 for IPV6 multicast address + * 0xFF for broadcast address + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_set_rx_filter(struct hdd_adapter *adapter, bool action, + uint8_t pattern) +{ + int ret; + uint8_t i, j; + tSirRcvFltMcAddrList *filter; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) { + hdd_err("MAC Handle is NULL"); + return -EINVAL; + } + + if (!ucfg_pmo_is_mc_addr_list_enabled(hdd_ctx->psoc)) { + hdd_warn("mc addr ini is disabled"); + return -EINVAL; + } + + /* + * If action is false it means start dropping packets + * Set addr_filter_pattern which will be used when sending + * MC/BC address list to target + */ + if (!action) + adapter->addr_filter_pattern = pattern; + else + adapter->addr_filter_pattern = 0; + + if (((adapter->device_mode == QDF_STA_MODE) || + (adapter->device_mode == QDF_P2P_CLIENT_MODE)) && + adapter->mc_addr_list.mc_cnt && + hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + + + filter = qdf_mem_malloc(sizeof(*filter)); + if (!filter) { + hdd_err("Could not allocate Memory"); + return -ENOMEM; + } + filter->action = action; + for (i = 0, j = 0; i < adapter->mc_addr_list.mc_cnt; i++) { + if (!memcmp(adapter->mc_addr_list.addr[i], + &pattern, 1)) { + memcpy(filter->multicastAddr[j].bytes, + adapter->mc_addr_list.addr[i], + sizeof(adapter->mc_addr_list.addr[i])); + + hdd_debug("%s RX filter : addr =" + QDF_MAC_ADDR_FMT, + action ? "setting" : "clearing", + QDF_MAC_ADDR_REF(filter->multicastAddr[j].bytes)); + j++; + } + if (j == SIR_MAX_NUM_MULTICAST_ADDRESS) + break; + } + filter->ulMulticastAddrCnt = j; + /* Set rx filter */ + sme_8023_multicast_list(mac_handle, adapter->vdev_id, + filter); + qdf_mem_free(filter); + } else { + hdd_debug("mode %d mc_cnt %d", + adapter->device_mode, adapter->mc_addr_list.mc_cnt); + } + + return 0; +} + +/** + * hdd_driver_rxfilter_command_handler() - RXFILTER driver command handler + * @command: Pointer to input string driver command + * @adapter: Pointer to adapter + * @action: Action to enable/disable filtering + * + * If action == false + * Start filtering out data packets based on type + * RXFILTER-REMOVE 0 -> Start filtering out unicast data packets + * RXFILTER-REMOVE 1 -> Start filtering out broadcast data packets + * RXFILTER-REMOVE 2 -> Start filtering out IPV4 mcast data packets + * RXFILTER-REMOVE 3 -> Start filtering out IPV6 mcast data packets + * + * if action == true + * Stop filtering data packets based on type + * RXFILTER-ADD 0 -> Stop filtering unicast data packets + * RXFILTER-ADD 1 -> Stop filtering broadcast data packets + * RXFILTER-ADD 2 -> Stop filtering IPV4 mcast data packets + * RXFILTER-ADD 3 -> Stop filtering IPV6 mcast data packets + * + * Current implementation only supports IPV4 address filtering by + * selectively allowing IPV4 multicast data packest based on + * address list received in .ndo_set_rx_mode + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_driver_rxfilter_command_handler(uint8_t *command, + struct hdd_adapter *adapter, + bool action) +{ + int ret = 0; + uint8_t *value; + uint8_t type; + + value = command; + /* Skip space after RXFILTER-REMOVE OR RXFILTER-ADD based on action */ + if (!action) + value = command + 16; + else + value = command + 13; + ret = kstrtou8(value, 10, &type); + if (ret < 0) { + hdd_err("kstrtou8 failed invalid input value"); + return -EINVAL; + } + + switch (type) { + case 2: + /* Set rx filter for IPV4 multicast data packets */ + ret = hdd_set_rx_filter(adapter, action, 0x01); + break; + default: + hdd_debug("Unsupported RXFILTER type %d", type); + break; + } + + return ret; +} + +/** + * drv_cmd_rx_filter_remove() - RXFILTER REMOVE driver command handler + * @adapter: Pointer to network adapter + * @hdd_ctx: Pointer to hdd context + * @command: Pointer to input command + * @command_len: Command length + * @priv_data: Pointer to private data in command + */ +static int drv_cmd_rx_filter_remove(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_driver_rxfilter_command_handler(command, adapter, false); +} + +/** + * drv_cmd_rx_filter_add() - RXFILTER ADD driver command handler + * @adapter: Pointer to network adapter + * @hdd_ctx: Pointer to hdd context + * @command: Pointer to input command + * @command_len: Command length + * @priv_data: Pointer to private data in command + */ +static int drv_cmd_rx_filter_add(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_driver_rxfilter_command_handler(command, adapter, true); +} +#endif /* WLAN_FEATURE_PACKET_FILTERING */ + +/** + * hdd_parse_setantennamode_command() - HDD Parse SETANTENNAMODE + * command + * @value: Pointer to SETANTENNAMODE command + * @mode: Pointer to antenna mode + * @reason: Pointer to reason for set antenna mode + * + * This function parses the SETANTENNAMODE command passed in the format + * SETANTENNAMODEmode + * + * Return: 0 for success non-zero for failure + */ +static int hdd_parse_setantennamode_command(const uint8_t *value) +{ + const uint8_t *in_ptr = value; + int tmp, v; + char arg1[32]; + + in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE); + + /* no argument after the command */ + if (!in_ptr) { + hdd_err("No argument after the command"); + return -EINVAL; + } + + /* no space after the command */ + if (SPACE_ASCII_VALUE != *in_ptr) { + hdd_err("No space after the command"); + return -EINVAL; + } + + /* remove empty spaces */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) { + hdd_err("No argument followed by spaces"); + return -EINVAL; + } + + /* get the argument i.e. antenna mode */ + v = sscanf(in_ptr, "%31s ", arg1); + if (1 != v) { + hdd_err("argument retrieval from cmd string failed"); + return -EINVAL; + } + + v = kstrtos32(arg1, 10, &tmp); + if (v < 0) { + hdd_err("argument string to int conversion failed"); + return -EINVAL; + } + + return tmp; +} + +/** + * hdd_is_supported_chain_mask_2x2() - Verify if supported chain + * mask is 2x2 mode + * @hdd_ctx: Pointer to hdd contex + * + * Return: true if supported chain mask 2x2 else false + */ +static bool hdd_is_supported_chain_mask_2x2(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + bool bval = false; + +/* + * Revisit and the update logic to determine the number + * of TX/RX chains supported in the system when + * antenna sharing per band chain mask support is + * brought in + */ + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + return (bval == 0x01) ? true : false; +} + +/** + * hdd_is_supported_chain_mask_1x1() - Verify if the supported + * chain mask is 1x1 + * @hdd_ctx: Pointer to hdd contex + * + * Return: true if supported chain mask 1x1 else false + */ +static bool hdd_is_supported_chain_mask_1x1(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + bool bval = false; + + /* + * Revisit and update the logic to determine the number + * of TX/RX chains supported in the system when + * antenna sharing per band chain mask support is + * brought in + */ + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + return (!bval) ? true : false; +} + +QDF_STATUS hdd_update_smps_antenna_mode(struct hdd_context *hdd_ctx, int mode) +{ + QDF_STATUS status; + uint8_t smps_mode; + uint8_t smps_enable; + mac_handle_t mac_handle; + + /* Update SME SMPS config */ + if (HDD_ANTENNA_MODE_1X1 == mode) { + smps_enable = true; + smps_mode = HDD_SMPS_MODE_STATIC; + } else { + smps_enable = false; + smps_mode = HDD_SMPS_MODE_DISABLED; + } + + hdd_debug("Update SME SMPS enable: %d mode: %d", + smps_enable, smps_mode); + mac_handle = hdd_ctx->mac_handle; + status = sme_update_mimo_power_save(mac_handle, smps_enable, + smps_mode, false); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Update SMPS config failed enable: %d mode: %d status: %d", + smps_enable, smps_mode, status); + return QDF_STATUS_E_FAILURE; + } + + hdd_ctx->current_antenna_mode = mode; + /* + * Update the user requested nss in the mac context. + * This will be used in tdls protocol engine to form tdls + * Management frames. + */ + sme_update_user_configured_nss(mac_handle, + hdd_ctx->current_antenna_mode); + + hdd_debug("Successfully switched to mode: %d x %d", + hdd_ctx->current_antenna_mode, + hdd_ctx->current_antenna_mode); + + return QDF_STATUS_SUCCESS; +} + +/** + * wlan_hdd_soc_set_antenna_mode_cb() - Callback for set antenna mode + * @status: Status of set antenna mode + * @context: callback context + * + * Callback on setting antenna mode + * + * Return: None + */ +static void +wlan_hdd_soc_set_antenna_mode_cb(enum set_antenna_mode_status status, + void *context) +{ + struct osif_request *request = NULL; + + hdd_debug("Status: %d", status); + + request = osif_request_get(context); + if (!request) { + hdd_err("obselete request"); + return; + } + + /* Signal the completion of set dual mac config */ + osif_request_complete(request); + osif_request_put(request); +} + +int hdd_set_antenna_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, int mode) +{ + struct sir_antenna_mode_param params; + QDF_STATUS status; + int ret = 0; + struct osif_request *request = NULL; + static const struct osif_request_params request_params = { + .priv_size = 0, + .timeout_ms = SME_POLICY_MGR_CMD_TIMEOUT, + }; + + switch (mode) { + case HDD_ANTENNA_MODE_1X1: + params.num_rx_chains = 1; + params.num_tx_chains = 1; + break; + case HDD_ANTENNA_MODE_2X2: + params.num_rx_chains = 2; + params.num_tx_chains = 2; + break; + default: + hdd_err("unsupported antenna mode"); + ret = -EINVAL; + goto exit; + } + + if (hdd_ctx->dynamic_nss_chains_support) + return hdd_set_dynamic_antenna_mode(adapter, + params.num_rx_chains, + params.num_tx_chains); + + if ((HDD_ANTENNA_MODE_2X2 == mode) && + (!hdd_is_supported_chain_mask_2x2(hdd_ctx))) { + hdd_err("System does not support 2x2 mode"); + ret = -EPERM; + goto exit; + } + + if ((HDD_ANTENNA_MODE_1X1 == mode) && + hdd_is_supported_chain_mask_1x1(hdd_ctx)) { + hdd_err("System only supports 1x1 mode"); + goto exit; + } + + /* Check TDLS status and update antenna mode */ + if ((QDF_STA_MODE == adapter->device_mode) && + policy_mgr_is_sta_active_connection_exists(hdd_ctx->psoc)) { + ret = wlan_hdd_tdls_antenna_switch(hdd_ctx, adapter, mode); + if (0 != ret) + goto exit; + } + + request = osif_request_alloc(&request_params); + if (!request) { + hdd_err("Request Allocation Failure"); + ret = -ENOMEM; + goto exit; + } + + params.set_antenna_mode_ctx = osif_request_cookie(request); + params.set_antenna_mode_resp = (void *)wlan_hdd_soc_set_antenna_mode_cb; + hdd_debug("Set antenna mode rx chains: %d tx chains: %d", + params.num_rx_chains, + params.num_tx_chains); + + status = sme_soc_set_antenna_mode(hdd_ctx->mac_handle, ¶ms); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("set antenna mode failed status : %d", status); + ret = -EFAULT; + goto request_put; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("send set antenna mode timed out"); + goto request_put; + } + + status = hdd_update_smps_antenna_mode(hdd_ctx, mode); + if (QDF_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto request_put; + } + ret = 0; +request_put: + osif_request_put(request); +exit: + hdd_debug("Set antenna status: %d current mode: %d", + ret, hdd_ctx->current_antenna_mode); + + return ret; +} + +/** + * drv_cmd_set_antenna_mode() - SET ANTENNA MODE driver command + * handler + * @adapter: Pointer to network adapter + * @hdd_ctx: Pointer to hdd context + * @command: Pointer to input command + * @command_len: Command length + * @priv_data: Pointer to private data in command + */ +static int drv_cmd_set_antenna_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + int mode; + uint8_t *value = command; + + mode = hdd_parse_setantennamode_command(value); + if (mode < 0) { + hdd_err("Invalid SETANTENNA command"); + return mode; + } + + hdd_debug("Processing antenna mode switch to: %d", mode); + + return hdd_set_antenna_mode(adapter, hdd_ctx, mode); +} + +/** + * hdd_get_dynamic_antenna_mode() -get the dynamic antenna mode for vdev + * @antenna_mode: pointer to antenna mode of vdev + * @dynamic_nss_chains_support: feature support for dynamic nss chains update + * @vdev: Pointer to vdev + * + * This API will check for the num of chains configured for the vdev, and fill + * that info in the antenna mode if the dynamic chains per vdev are supported. + * + * Return: None + */ +static void +hdd_get_dynamic_antenna_mode(uint32_t *antenna_mode, + bool dynamic_nss_chains_support, + struct wlan_objmgr_vdev *vdev) +{ + struct wlan_mlme_nss_chains *nss_chains_dynamic_cfg; + + if (!dynamic_nss_chains_support) + return; + + nss_chains_dynamic_cfg = ucfg_mlme_get_dynamic_vdev_config(vdev); + if (!nss_chains_dynamic_cfg) { + hdd_err("nss chain dynamic config NULL"); + return; + } + /* + * At present, this command doesn't include band, so by default + * it will return the band 2ghz present rf chains. + */ + *antenna_mode = + nss_chains_dynamic_cfg->num_rx_chains[NSS_CHAINS_BAND_2GHZ]; +} + +/** + * drv_cmd_get_antenna_mode() - GET ANTENNA MODE driver command + * handler + * @adapter: Pointer to hdd adapter + * @hdd_ctx: Pointer to hdd context + * @command: Pointer to input command + * @command_len: length of the command + * @priv_data: private data coming with the driver command + * + * Return: 0 for success non-zero for failure + */ +static inline int drv_cmd_get_antenna_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + uint32_t antenna_mode = 0; + char extra[32]; + uint8_t len = 0; + + antenna_mode = hdd_ctx->current_antenna_mode; + /* Overwrite this antenna mode if dynamic vdev chains are supported */ + hdd_get_dynamic_antenna_mode(&antenna_mode, + hdd_ctx->dynamic_nss_chains_support, + adapter->vdev); + len = scnprintf(extra, sizeof(extra), "%s %d", command, + antenna_mode); + len = QDF_MIN(priv_data->total_len, len + 1); + if (copy_to_user(priv_data->buf, &extra, len)) { + hdd_err("Failed to copy data to user buffer"); + return -EFAULT; + } + + hdd_debug("Get antenna mode: %d", antenna_mode); + + return 0; +} + +/* + * dummy (no-op) hdd driver command handler + */ +static int drv_cmd_dummy(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + hdd_debug("%s: Ignoring driver command \"%s\"", + adapter->dev->name, command); + return 0; +} + +/* + * handler for any unsupported wlan hdd driver command + */ +static int drv_cmd_invalid(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_UNSUPPORTED_IOCTL, + adapter->vdev_id, 0); + + hdd_warn("%s: Unsupported driver command \"%s\"", + adapter->dev->name, command); + + return -ENOTSUPP; +} + +/** + * drv_cmd_set_fcc_channel() - Handle fcc constraint request + * @adapter: HDD adapter + * @hdd_ctx: HDD context + * @command: command ptr, SET_FCC_CHANNEL 0/-1 is the command + * @command_len: command len + * @priv_data: private data + * + * Return: status + */ +static int drv_cmd_set_fcc_channel(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + QDF_STATUS status; + int8_t input_value; + bool fcc_constraint; + int err; + + /* + * This command would be called by user-space when it detects WLAN + * ON after airplane mode is set. When APM is set, WLAN turns off. + * But it can be turned back on. Otherwise; when APM is turned back + * off, WLAN would turn back on. So at that point the command is + * expected to come down. 0 means reduce power as per fcc constraint + * and -1 means remove constraint. + */ + + err = kstrtos8(command + command_len + 1, 10, &input_value); + if (err) { + hdd_err("error %d parsing userspace fcc parameter", err); + return err; + } + + fcc_constraint = input_value ? false : true; + hdd_debug("input_value = %d && fcc_constraint = %u", + input_value, fcc_constraint); + + status = ucfg_reg_set_fcc_constraint(hdd_ctx->pdev, fcc_constraint); + + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to %s tx power for channels 12/13", + fcc_constraint ? "restore" : "reduce"); + + return qdf_status_to_os_return(status); +} + +/** + * hdd_parse_set_channel_switch_command() - Parse and validate CHANNEL_SWITCH + * command + * @value: Pointer to the command + * @chan_number: Pointer to the channel number + * @chan_bw: Pointer to the channel bandwidth + * + * Parses and provides the channel number and channel width from the input + * command which is expected to be of the format: CHANNEL_SWITCH + * is channel number to move (where 1 = channel 1, 149 = channel 149, ...) + * is bandwidth to move (where 20 = BW 20, 40 = BW 40, 80 = BW 80) + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_parse_set_channel_switch_command(uint8_t *value, + uint32_t *chan_number, + uint32_t *chan_bw) +{ + const uint8_t *in_ptr = value; + int ret; + + in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE); + + /* no argument after the command */ + if (!in_ptr) { + hdd_err("No argument after the command"); + return -EINVAL; + } + + /* no space after the command */ + if (SPACE_ASCII_VALUE != *in_ptr) { + hdd_err("No space after the command "); + return -EINVAL; + } + + /* remove empty spaces and move the next argument */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) { + hdd_err("No argument followed by spaces"); + return -EINVAL; + } + + /* get the two arguments: channel number and bandwidth */ + ret = sscanf(in_ptr, "%u %u", chan_number, chan_bw); + if (ret != 2) { + hdd_err("Arguments retrieval from cmd string failed"); + return -EINVAL; + } + + return 0; +} + +/** + * drv_cmd_set_channel_switch() - Switch SAP/P2P-GO operating channel + * @adapter: HDD adapter + * @hdd_ctx: HDD context + * @command: Pointer to the input command CHANNEL_SWITCH + * @command_len: Command len + * @priv_data: Private data + * + * Handles private IOCTL CHANNEL_SWITCH command to switch the operating channel + * of SAP/P2P-GO + * + * Return: 0 for success, non-zero for failure + */ +static int drv_cmd_set_channel_switch(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + struct net_device *dev = adapter->dev; + int status; + uint32_t chan_number = 0, chan_bw = 0; + uint8_t *value = command; + enum phy_ch_width width; + + if ((adapter->device_mode != QDF_P2P_GO_MODE) && + (adapter->device_mode != QDF_SAP_MODE)) { + hdd_err("IOCTL CHANNEL_SWITCH not supported for mode %d", + adapter->device_mode); + return -EINVAL; + } + + status = hdd_parse_set_channel_switch_command(value, + &chan_number, &chan_bw); + if (status) { + hdd_err("Invalid CHANNEL_SWITCH command"); + return status; + } + + if ((chan_bw != 20) && (chan_bw != 40) && (chan_bw != 80)) { + hdd_err("BW %d is not allowed for CHANNEL_SWITCH", chan_bw); + return -EINVAL; + } + + if (chan_bw == 80) + width = CH_WIDTH_80MHZ; + else if (chan_bw == 40) + width = CH_WIDTH_40MHZ; + else + width = CH_WIDTH_20MHZ; + + hdd_debug("CH:%d BW:%d", chan_number, chan_bw); + + wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, adapter->vdev_id, + CSA_REASON_USER_INITIATED); + status = hdd_softap_set_channel_change(dev, + wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, chan_number), + width, true); + if (status) { + hdd_err("Set channel change fail"); + return status; + } + + return 0; +} + +#ifdef DISABLE_CHANNEL_LIST +void wlan_hdd_free_cache_channels(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + if (!hdd_ctx->original_channels) + return; + + qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); + hdd_ctx->original_channels->num_channels = 0; + if (hdd_ctx->original_channels->channel_info) { + qdf_mem_free(hdd_ctx->original_channels->channel_info); + hdd_ctx->original_channels->channel_info = NULL; + } + qdf_mem_free(hdd_ctx->original_channels); + hdd_ctx->original_channels = NULL; + qdf_mutex_release(&hdd_ctx->cache_channel_lock); + + hdd_exit(); +} + +/** + * hdd_alloc_chan_cache() - Allocate the memory to cache the channel + * info for the channels received in command SET_DISABLE_CHANNEL_LIST + * @hdd_ctx: Pointer to HDD context + * @num_chan: Number of channels for which memory needs to + * be allocated + * + * Return: 0 on success and error code on failure + */ +static int hdd_alloc_chan_cache(struct hdd_context *hdd_ctx, int num_chan) +{ + hdd_ctx->original_channels = + qdf_mem_malloc(sizeof(struct hdd_cache_channels)); + if (!hdd_ctx->original_channels) { + hdd_err("QDF_MALLOC_ERR"); + return -ENOMEM; + } + hdd_ctx->original_channels->num_channels = num_chan; + hdd_ctx->original_channels->channel_info = + qdf_mem_malloc(num_chan * + sizeof(struct hdd_cache_channel_info)); + if (!hdd_ctx->original_channels->channel_info) { + hdd_err("QDF_MALLOC_ERR"); + hdd_ctx->original_channels->num_channels = 0; + qdf_mem_free(hdd_ctx->original_channels); + hdd_ctx->original_channels = NULL; + return -ENOMEM; + } + return 0; +} + +/** + * check_disable_channels() - Check for disable channel + * @hdd_ctx: Pointer to hdd context + * @operating_channel: Current operating channel of adapter + * + * This function checks original_channels array for a specific channel + * + * Return: 0 if channel not found, 1 if channel found + */ +static bool check_disable_channels(struct hdd_context *hdd_ctx, + uint8_t operating_channel) +{ + uint32_t num_channels; + uint8_t i; + + if (!hdd_ctx || !hdd_ctx->original_channels || + !hdd_ctx->original_channels->channel_info) + return false; + + num_channels = hdd_ctx->original_channels->num_channels; + for (i = 0; i < num_channels; i++) + if (hdd_ctx->original_channels->channel_info[i].channel_num == + operating_channel) + return true; + return false; +} + +/** + * disconnect_sta_and_stop_sap() - Disconnect STA and stop SAP + * + * @hdd_ctx: Pointer to hdd context + * @reason: Disconnect reason code as per @enum eSirMacReasonCodes + * + * Disable channels provided by user and disconnect STA if it is + * connected to any AP, stop SAP and send deauthentication request + * to STAs connected to SAP. + * + * Return: None + */ +static void disconnect_sta_and_stop_sap(struct hdd_context *hdd_ctx, + enum eSirMacReasonCodes reason) +{ + struct hdd_adapter *adapter, *next = NULL; + QDF_STATUS status; + uint8_t ap_ch; + + if (!hdd_ctx) + return; + + hdd_check_and_disconnect_sta_on_invalid_channel(hdd_ctx, reason); + + status = hdd_get_front_adapter(hdd_ctx, &adapter); + while (adapter && (status == QDF_STATUS_SUCCESS)) { + if (!hdd_validate_adapter(adapter) && + adapter->device_mode == QDF_SAP_MODE) { + ap_ch = wlan_reg_freq_to_chan( + hdd_ctx->pdev, + adapter->session.ap.operating_chan_freq); + if (check_disable_channels(hdd_ctx, ap_ch)) + wlan_hdd_stop_sap(adapter); + } + + status = hdd_get_next_adapter(hdd_ctx, adapter, &next); + adapter = next; + } +} + +/** + * hdd_parse_disable_chan_cmd() - Parse the channel list received + * in command. + * @adapter: pointer to hdd adapter + * @ptr: Pointer to the command string + * + * This function parses the channel list received in the command. + * command should be a string having format + * SET_DISABLE_CHANNEL_LIST + * . + * If the command comes multiple times than this function will compare + * the channels received in the command with the channles cached in the + * first command, if the channel list matches with the cached channles, + * it returns success otherwise returns failure. + * + * Return: 0 on success, Error code on failure + */ + +static int hdd_parse_disable_chan_cmd(struct hdd_adapter *adapter, uint8_t *ptr) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t *param; + int j, i, temp_int, ret = 0, num_channels; + uint32_t parsed_channels[NUM_CHANNELS]; + bool is_command_repeated = false; + + if (!hdd_ctx) { + hdd_err("HDD Context is NULL"); + return -EINVAL; + } + + param = strnchr(ptr, strlen(ptr), ' '); + /*no argument after the command*/ + if (!param) + return -EINVAL; + + /*no space after the command*/ + else if (SPACE_ASCII_VALUE != *param) + return -EINVAL; + + param++; + + /*removing empty spaces*/ + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + /*no argument followed by spaces*/ + if ('\0' == *param) + return -EINVAL; + + /*getting the first argument ie the number of channels*/ + if (sscanf(param, "%d ", &temp_int) != 1) { + hdd_err("Cannot get number of channels from input"); + return -EINVAL; + } + + if (temp_int < 0 || temp_int > NUM_CHANNELS) { + hdd_err("Invalid Number of channel received"); + return -EINVAL; + } + + hdd_debug("Number of channel to disable are: %d", temp_int); + + if (!temp_int) { + /* + * Restore and Free the cache channels when the command is + * received with num channels as 0 + */ + wlan_hdd_restore_channels(hdd_ctx); + return 0; + } + + qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); + + if (!hdd_ctx->original_channels) { + if (hdd_alloc_chan_cache(hdd_ctx, temp_int)) { + ret = -ENOMEM; + goto mem_alloc_failed; + } + } else if (hdd_ctx->original_channels->num_channels != temp_int) { + hdd_err("Invalid Number of channels"); + ret = -EINVAL; + is_command_repeated = true; + goto parse_failed; + } else { + is_command_repeated = true; + } + num_channels = temp_int; + for (j = 0; j < num_channels; j++) { + /* + * param pointing to the beginning of first space + * after number of channels + */ + param = strpbrk(param, " "); + /*no channel list after the number of channels argument*/ + if (!param) { + hdd_err("Invalid No of channel provided in the list"); + ret = -EINVAL; + goto parse_failed; + } + + param++; + + /*removing empty space*/ + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + if ('\0' == *param) { + hdd_err("No channel is provided in the list"); + ret = -EINVAL; + goto parse_failed; + } + + if (sscanf(param, "%d ", &temp_int) != 1) { + hdd_err("Cannot read channel number"); + ret = -EINVAL; + goto parse_failed; + } + + if (!IS_CHANNEL_VALID(temp_int)) { + hdd_err("Invalid channel number received"); + ret = -EINVAL; + goto parse_failed; + } + + hdd_debug("channel[%d] = %d", j, temp_int); + parsed_channels[j] = temp_int; + } + + /*extra arguments check*/ + param = strpbrk(param, " "); + if (param) { + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + if ('\0' != *param) { + hdd_err("Invalid argument received"); + ret = -EINVAL; + goto parse_failed; + } + } + + /* + * If command is received first time, cache the channels to + * be disabled else compare the channels received in the + * command with the cached channels, if channel list matches + * return success otherewise return failure. + */ + if (!is_command_repeated) { + for (j = 0; j < num_channels; j++) + hdd_ctx->original_channels-> + channel_info[j].channel_num = + parsed_channels[j]; + + /* Cache the channel list in regulatory also */ + ucfg_reg_cache_channel_state(hdd_ctx->pdev, parsed_channels, + num_channels); + } else { + for (i = 0; i < num_channels; i++) { + for (j = 0; j < num_channels; j++) + if (hdd_ctx->original_channels-> + channel_info[i].channel_num == + parsed_channels[j]) + break; + if (j == num_channels) { + ret = -EINVAL; + goto parse_failed; + } + } + ret = 0; + } +mem_alloc_failed: + + qdf_mutex_release(&hdd_ctx->cache_channel_lock); + /* Disable the channels received in command SET_DISABLE_CHANNEL_LIST */ + if (!is_command_repeated && hdd_ctx->original_channels) { + ret = wlan_hdd_disable_channels(hdd_ctx); + if (ret) + return ret; + disconnect_sta_and_stop_sap(hdd_ctx, + eSIR_MAC_OPER_CHANNEL_BAND_CHANGE); + } + + hdd_exit(); + + return ret; + +parse_failed: + if (!is_command_repeated) + wlan_hdd_free_cache_channels(hdd_ctx); + + qdf_mutex_release(&hdd_ctx->cache_channel_lock); + hdd_exit(); + + return ret; +} + +static int drv_cmd_set_disable_chan_list(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return hdd_parse_disable_chan_cmd(adapter, command); +} + +/** + * hdd_get_disable_ch_list() - get disable channel list + * @hdd_ctx: hdd context + * @buf: buffer to hold disable channel list + * @buf_len: buffer length + * + * Return: length of data copied to buf + */ +static int hdd_get_disable_ch_list(struct hdd_context *hdd_ctx, uint8_t *buf, + uint32_t buf_len) +{ + struct hdd_cache_channel_info *ch_list; + unsigned char i, num_ch; + int len = 0; + + qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); + if (hdd_ctx->original_channels && + hdd_ctx->original_channels->num_channels && + hdd_ctx->original_channels->channel_info) { + num_ch = hdd_ctx->original_channels->num_channels; + + len = scnprintf(buf, buf_len, "%s %hhu", + "GET_DISABLE_CHANNEL_LIST", num_ch); + ch_list = hdd_ctx->original_channels->channel_info; + for (i = 0; (i < num_ch) && (len < buf_len - 1); i++) { + len += scnprintf(buf + len, buf_len - len, + " %d", ch_list[i].channel_num); + } + } + qdf_mutex_release(&hdd_ctx->cache_channel_lock); + + return len; +} + +static int drv_cmd_get_disable_chan_list(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + char extra[512] = {0}; + int max_len, copied_length; + + hdd_debug("Received Command to get disable Channels list"); + + max_len = QDF_MIN(priv_data->total_len, sizeof(extra)); + copied_length = hdd_get_disable_ch_list(hdd_ctx, extra, max_len); + if (copied_length == 0) { + hdd_err("disable channel list is not yet programmed"); + return -EINVAL; + } + + if (copy_to_user(priv_data->buf, &extra, copied_length + 1)) { + hdd_err("failed to copy data to user buffer"); + return -EFAULT; + } + + hdd_debug("data:%s", extra); + return 0; +} +#else + +static int drv_cmd_set_disable_chan_list(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return 0; +} + +void wlan_hdd_free_cache_channels(struct hdd_context *hdd_ctx) +{ +} + +static int drv_cmd_get_disable_chan_list(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return 0; +} +#endif + +#ifdef FEATURE_ANI_LEVEL_REQUEST +static int drv_cmd_get_ani_level(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + char *extra; + int copied_length = 0, j, temp_int, ret = 0; + uint8_t *param, num_freqs, num_recv_channels; + uint32_t parsed_freqs[MAX_NUM_FREQS_FOR_ANI_LEVEL]; + struct wmi_host_ani_level_event ani[MAX_NUM_FREQS_FOR_ANI_LEVEL]; + size_t user_size = priv_data->total_len; + + hdd_debug("Received Command to get ANI level"); + + param = strnchr(command, strlen(command), ' '); + + /* No argument after the command */ + if (!param) + return -EINVAL; + + /* No space after the command */ + else if (SPACE_ASCII_VALUE != *param) + return -EINVAL; + + param++; + + /* Removing empty spaces*/ + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + /*no argument followed by spaces */ + if ('\0' == *param) + return -EINVAL; + + /* Getting the first argument ie the number of channels */ + if (sscanf(param, "%d ", &temp_int) != 1) { + hdd_err("Cannot get number of freq from input"); + return -EINVAL; + } + + if (temp_int < 0 || temp_int > MAX_NUM_FREQS_FOR_ANI_LEVEL) { + hdd_err("Invalid Number of channel received"); + return -EINVAL; + } + + hdd_debug("Number of freq to fetch ANI level are: %d", temp_int); + + if (!temp_int) + return 0; + + num_freqs = temp_int; + + for (j = 0; j < num_freqs; j++) { + /* + * Param pointing to the beginning of first space + * after number of channels. + */ + param = strpbrk(param, " "); + /*no channel list after the number of channels argument*/ + if (!param) { + hdd_err("Invalid No of freq provided in the list"); + ret = -EINVAL; + goto parse_failed; + } + + param++; + + /* Removing empty space */ + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + if ('\0' == *param) { + hdd_err("No freq is provided in the list"); + ret = -EINVAL; + goto parse_failed; + } + + if (sscanf(param, "%d ", &temp_int) != 1) { + hdd_err("Cannot read freq number"); + ret = -EINVAL; + goto parse_failed; + } + + hdd_debug("channel_freq[%d] = %d", j, temp_int); + parsed_freqs[j] = temp_int; + } + + /* Extra arguments check */ + param = strpbrk(param, " "); + if (param) { + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + if ('\0' != *param) { + hdd_err("Invalid argument received"); + ret = -EINVAL; + goto parse_failed; + } + } + + qdf_mem_zero(ani, sizeof(ani)); + hdd_debug("num_freq: %d", num_freqs); + if (QDF_IS_STATUS_ERROR(wlan_hdd_get_ani_level(adapter, ani, + parsed_freqs, + num_freqs))) { + hdd_err("Unable to retrieve ani level"); + return -EINVAL; + } + + extra = qdf_mem_malloc(user_size); + if (!extra) { + hdd_err("memory allocation failed"); + ret = -ENOMEM; + goto parse_failed; + } + + /* + * Find the number of channels that are populated. If freq is not + * filled then stop count there + */ + for (num_recv_channels = 0; + (num_recv_channels < num_freqs && + ani[num_recv_channels].chan_freq); num_recv_channels++) + ; + + for (j = 0; j < num_recv_channels; j++) { + /* Sanity check for ANI level validity */ + if (ani[j].ani_level > MAX_ANI_LEVEL) + continue; + + copied_length += scnprintf(extra + copied_length, + user_size - copied_length, "%d:%d\n", + ani[j].chan_freq, ani[j].ani_level); + } + + if (copied_length == 0) { + hdd_err("ANI level not fetched"); + ret = -EINVAL; + goto free; + } + + hdd_debug("data: %s", extra); + + if (copy_to_user(priv_data->buf, extra, copied_length + 1)) { + hdd_err("failed to copy data to user buffer"); + ret = -EFAULT; + goto free; + } + +free: + qdf_mem_free(extra); + +parse_failed: + return ret; +} + +#else +static int drv_cmd_get_ani_level(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + return 0; +} +#endif + +#ifdef FUNC_CALL_MAP +static int drv_cmd_get_function_call_map(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, + uint8_t *command, + uint8_t command_len, + struct hdd_priv_data *priv_data) +{ + char *cc_buf = qdf_mem_malloc(QDF_FUNCTION_CALL_MAP_BUF_LEN); + uint8_t *param; + int temp_int; + + param = strnchr(command, strlen(command), ' '); + /*no argument after the command*/ + if (NULL == param) + return -EINVAL; + + /*no space after the command*/ + else if (SPACE_ASCII_VALUE != *param) + return -EINVAL; + + param++; + + /*removing empty spaces*/ + while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) + param++; + + /*no argument followed by spaces*/ + if ('\0' == *param) + return -EINVAL; + + /*getting the first argument */ + if (sscanf(param, "%d ", &temp_int) != 1) { + hdd_err("No option given"); + return -EINVAL; + } + + if (temp_int < 0 || temp_int > 1) { + hdd_err("Invalid option given"); + return -EINVAL; + } + + /* Read the buffer */ + if (temp_int) { + /* + * These logs are required as these indicates the start and end of the + * dump for the auto script to parse + */ + hdd_info("Function call map dump start"); + qdf_get_func_call_map(cc_buf); + qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + cc_buf, QDF_FUNCTION_CALL_MAP_BUF_LEN); + hdd_info("Function call map dump end"); + } else { + qdf_clear_func_call_map(); + hdd_info("Function call map clear"); + } + qdf_mem_free(cc_buf); + + return 0; +} +#endif + +/* + * The following table contains all supported WLAN HDD + * IOCTL driver commands and the handler for each of them. + */ +static const struct hdd_drv_cmd hdd_drv_cmds[] = { + {"P2P_DEV_ADDR", drv_cmd_p2p_dev_addr, false}, + {"P2P_SET_NOA", drv_cmd_p2p_set_noa, true}, + {"P2P_SET_PS", drv_cmd_p2p_set_ps, true}, + {"SETBAND", drv_cmd_set_band, true}, + {"SETWMMPS", drv_cmd_set_wmmps, true}, + {"COUNTRY", drv_cmd_country, true}, + {"SETCOUNTRYREV", drv_cmd_country, true}, + {"GETCOUNTRYREV", drv_cmd_get_country, false}, + {"SETSUSPENDMODE", drv_cmd_set_suspend_mode, true}, + {"SET_AP_WPS_P2P_IE", drv_cmd_dummy, false}, + {"SETROAMTRIGGER", drv_cmd_set_roam_trigger, true}, + {"GETROAMTRIGGER", drv_cmd_get_roam_trigger, false}, + {"SETROAMSCANPERIOD", drv_cmd_set_roam_scan_period, true}, + {"GETROAMSCANPERIOD", drv_cmd_get_roam_scan_period, false}, + {"SETROAMSCANREFRESHPERIOD", drv_cmd_set_roam_scan_refresh_period, + true}, + {"GETROAMSCANREFRESHPERIOD", drv_cmd_get_roam_scan_refresh_period, + false}, + {"SETROAMMODE", drv_cmd_set_roam_mode, true}, + {"GETROAMMODE", drv_cmd_get_roam_mode, false}, + {"SETROAMDELTA", drv_cmd_set_roam_delta, true}, + {"GETROAMDELTA", drv_cmd_get_roam_delta, false}, + {"GETBAND", drv_cmd_get_band, false}, + {"SETROAMSCANCHANNELS", drv_cmd_set_roam_scan_channels, true}, + {"GETROAMSCANCHANNELS", drv_cmd_get_roam_scan_channels, false}, + {"GETCCXMODE", drv_cmd_get_ccx_mode, false}, + {"GETOKCMODE", drv_cmd_get_okc_mode, false}, + {"GETFASTROAM", drv_cmd_get_fast_roam, false}, + {"GETFASTTRANSITION", drv_cmd_get_fast_transition, false}, + {"SETROAMSCANCHANNELMINTIME", drv_cmd_set_roam_scan_channel_min_time, + true}, + {"SENDACTIONFRAME", drv_cmd_send_action_frame, true}, + {"GETROAMSCANCHANNELMINTIME", drv_cmd_get_roam_scan_channel_min_time, + false}, + {"SETSCANCHANNELTIME", drv_cmd_set_scan_channel_time, true}, + {"GETSCANCHANNELTIME", drv_cmd_get_scan_channel_time, false}, + {"SETSCANHOMETIME", drv_cmd_set_scan_home_time, true}, + {"GETSCANHOMETIME", drv_cmd_get_scan_home_time, false}, + {"SETROAMINTRABAND", drv_cmd_set_roam_intra_band, true}, + {"GETROAMINTRABAND", drv_cmd_get_roam_intra_band, false}, + {"SETSCANNPROBES", drv_cmd_set_scan_n_probes, true}, + {"GETSCANNPROBES", drv_cmd_get_scan_n_probes, false}, + {"SETSCANHOMEAWAYTIME", drv_cmd_set_scan_home_away_time, true}, + {"GETSCANHOMEAWAYTIME", drv_cmd_get_scan_home_away_time, false}, + {"REASSOC", drv_cmd_reassoc, true}, + {"SETWESMODE", drv_cmd_set_wes_mode, true}, + {"GETWESMODE", drv_cmd_get_wes_mode, false}, + {"SETOPPORTUNISTICRSSIDIFF", drv_cmd_set_opportunistic_rssi_diff, + true}, + {"GETOPPORTUNISTICRSSIDIFF", drv_cmd_get_opportunistic_rssi_diff, + false}, + {"SETROAMRESCANRSSIDIFF", drv_cmd_set_roam_rescan_rssi_diff, true}, + {"GETROAMRESCANRSSIDIFF", drv_cmd_get_roam_rescan_rssi_diff, false}, + {"SETFASTROAM", drv_cmd_set_fast_roam, true}, + {"SETFASTTRANSITION", drv_cmd_set_fast_transition, true}, + {"FASTREASSOC", drv_cmd_fast_reassoc, true}, + {"SETROAMSCANCONTROL", drv_cmd_set_roam_scan_control, true}, + {"SETOKCMODE", drv_cmd_set_okc_mode, true}, + {"GETROAMSCANCONTROL", drv_cmd_get_roam_scan_control, false}, + {"BTCOEXMODE", drv_cmd_bt_coex_mode, true}, + {"SCAN-ACTIVE", drv_cmd_scan_active, false}, + {"SCAN-PASSIVE", drv_cmd_scan_passive, false}, + {"CONCSETDWELLTIME", drv_cmd_conc_set_dwell_time, true}, + {"GETDWELLTIME", drv_cmd_get_dwell_time, false}, + {"SETDWELLTIME", drv_cmd_set_dwell_time, true}, + {"MIRACAST", drv_cmd_miracast, true}, + {"TPUT_DEBUG_MODE_ENABLE", drv_cmd_tput_debug_mode_enable, false}, + {"TPUT_DEBUG_MODE_DISABLE", drv_cmd_tput_debug_mode_disable, false}, + {"SETIBSSBEACONOUIDATA", drv_cmd_set_ibss_beacon_oui_data, true}, +#ifdef FEATURE_WLAN_RMC + {"SETRMCENABLE", drv_cmd_set_rmc_enable, true}, + {"SETRMCACTIONPERIOD", drv_cmd_set_rmc_action_period, true}, + {"SETRMCTXRATE", drv_cmd_set_rmc_tx_rate, true}, +#endif + {"GETIBSSPEERINFOALL", drv_cmd_get_ibss_peer_info_all, false}, + {"GETIBSSPEERINFO", drv_cmd_get_ibss_peer_info, true}, + {"SETIBSSTXFAILEVENT", drv_cmd_set_ibss_tx_fail_event, true}, +#ifdef FEATURE_WLAN_ESE + {"SETCCXROAMSCANCHANNELS", drv_cmd_set_ccx_roam_scan_channels, true}, + {"GETTSMSTATS", drv_cmd_get_tsm_stats, true}, + {"SETCCKMIE", drv_cmd_set_cckm_ie, true}, + {"CCXBEACONREQ", drv_cmd_ccx_beacon_req, true}, + {"CCXPLMREQ", drv_cmd_ccx_plm_req, true}, + {"SETCCXMODE", drv_cmd_set_ccx_mode, true}, +#endif /* FEATURE_WLAN_ESE */ + {"SETMCRATE", drv_cmd_set_mc_rate, true}, + {"MAXTXPOWER", drv_cmd_max_tx_power, true}, + {"SETDFSSCANMODE", drv_cmd_set_dfs_scan_mode, true}, + {"GETDFSSCANMODE", drv_cmd_get_dfs_scan_mode, false}, + {"GETLINKSTATUS", drv_cmd_get_link_status, false}, +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + {"ENABLEEXTWOW", drv_cmd_enable_ext_wow, true}, + {"SETAPP1PARAMS", drv_cmd_set_app1_params, true}, + {"SETAPP2PARAMS", drv_cmd_set_app2_params, true}, +#endif +#ifdef FEATURE_WLAN_TDLS + {"TDLSSECONDARYCHANNELOFFSET", drv_cmd_tdls_secondary_channel_offset, + true}, + {"TDLSOFFCHANNELMODE", drv_cmd_tdls_off_channel_mode, true}, + {"TDLSOFFCHANNEL", drv_cmd_tdls_off_channel, true}, + {"TDLSSCAN", drv_cmd_tdls_scan, true}, +#endif + {"RSSI", drv_cmd_get_rssi, false}, + {"LINKSPEED", drv_cmd_get_linkspeed, false}, +#ifdef WLAN_FEATURE_PACKET_FILTERING + {"RXFILTER-REMOVE", drv_cmd_rx_filter_remove, true}, + {"RXFILTER-ADD", drv_cmd_rx_filter_add, true}, +#endif + {"SET_FCC_CHANNEL", drv_cmd_set_fcc_channel, true}, + {"CHANNEL_SWITCH", drv_cmd_set_channel_switch, true}, + {"SETANTENNAMODE", drv_cmd_set_antenna_mode, true}, + {"GETANTENNAMODE", drv_cmd_get_antenna_mode, false}, + {"SET_DISABLE_CHANNEL_LIST", drv_cmd_set_disable_chan_list, true}, + {"GET_DISABLE_CHANNEL_LIST", drv_cmd_get_disable_chan_list, false}, + {"GET_ANI_LEVEL", drv_cmd_get_ani_level, false}, +#ifdef FUNC_CALL_MAP + {"GET_FUNCTION_CALL_MAP", drv_cmd_get_function_call_map, true}, +#endif + {"STOP", drv_cmd_dummy, false}, + /* Deprecated commands */ + {"RXFILTER-START", drv_cmd_dummy, false}, + {"RXFILTER-STOP", drv_cmd_dummy, false}, + {"BTCOEXSCAN-START", drv_cmd_dummy, false}, + {"BTCOEXSCAN-STOP", drv_cmd_dummy, false}, +}; + +/** + * hdd_drv_cmd_process() - chooses and runs the proper + * handler based on the input command + * @adapter: Pointer to the hdd adapter + * @cmd: Pointer to the driver command + * @priv_data: Pointer to the data associated with the command + * + * This function parses the input hdd driver command and runs + * the proper handler + * + * Return: 0 for success non-zero for failure + */ +static int hdd_drv_cmd_process(struct hdd_adapter *adapter, + uint8_t *cmd, + struct hdd_priv_data *priv_data) +{ + struct hdd_context *hdd_ctx; + int i; + const int cmd_num_total = ARRAY_SIZE(hdd_drv_cmds); + uint8_t *cmd_i = NULL; + hdd_drv_cmd_handler_t handler = NULL; + int len = 0, cmd_len = 0; + uint8_t *ptr; + bool args; + + if (!adapter || !cmd || !priv_data) { + hdd_err("at least 1 param is NULL"); + return -EINVAL; + } + + /* Calculate length of the first word */ + ptr = strchrnul(cmd, ' '); + cmd_len = ptr - cmd; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + for (i = 0; i < cmd_num_total; i++) { + + cmd_i = (uint8_t *)hdd_drv_cmds[i].cmd; + handler = hdd_drv_cmds[i].handler; + len = strlen(cmd_i); + args = hdd_drv_cmds[i].args; + + if (!handler) { + hdd_err("no. %d handler is NULL", i); + return -EINVAL; + } + + if (len == cmd_len && strncasecmp(cmd, cmd_i, len) == 0) { + if (args && drv_cmd_validate(cmd, len)) + return -EINVAL; + + return handler(adapter, hdd_ctx, + cmd, len, priv_data); + } + } + + return drv_cmd_invalid(adapter, hdd_ctx, cmd, len, priv_data); +} + +/** + * hdd_driver_command() - top level wlan hdd driver command handler + * @adapter: Pointer to the hdd adapter + * @priv_data: Pointer to the raw command data + * + * This function is the top level wlan hdd driver command handler. It + * handles the command with the help of hdd_drv_cmd_process() + * + * Return: 0 for success non-zero for failure + */ +static int hdd_driver_command(struct hdd_adapter *adapter, + struct hdd_priv_data *priv_data) +{ + uint8_t *command = NULL; + int ret = 0; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_err("Driver module is closed; command can not be processed"); + return -EINVAL; + } + + /* + * Note that valid pointers are provided by caller + */ + + /* copy to local struct to avoid numerous changes to legacy code */ + if (priv_data->total_len <= 0 || + priv_data->total_len > WLAN_PRIV_DATA_MAX_LEN) { + hdd_warn("Invalid priv_data.total_len: %d!!!", + priv_data->total_len); + ret = -EINVAL; + goto exit; + } + + /* Allocate +1 for '\0' */ + command = qdf_mem_malloc(priv_data->total_len + 1); + if (!command) { + hdd_err("failed to allocate memory"); + ret = -ENOMEM; + goto exit; + } + + if (copy_from_user(command, priv_data->buf, priv_data->total_len)) { + ret = -EFAULT; + goto exit; + } + + /* Make sure the command is NUL-terminated */ + command[priv_data->total_len] = '\0'; + + hdd_debug("%s: %s", adapter->dev->name, command); + ret = hdd_drv_cmd_process(adapter, command, priv_data); + +exit: + if (command) + qdf_mem_free(command); + hdd_exit(); + return ret; +} + +#ifdef CONFIG_COMPAT +static int hdd_driver_compat_ioctl(struct hdd_adapter *adapter, struct ifreq *ifr) +{ + struct { + compat_uptr_t buf; + int used_len; + int total_len; + } compat_priv_data; + struct hdd_priv_data priv_data; + int ret = 0; + + /* + * Note that adapter and ifr have already been verified by caller, + * and HDD context has also been validated + */ + if (copy_from_user(&compat_priv_data, ifr->ifr_data, + sizeof(compat_priv_data))) { + ret = -EFAULT; + goto exit; + } + priv_data.buf = compat_ptr(compat_priv_data.buf); + priv_data.used_len = compat_priv_data.used_len; + priv_data.total_len = compat_priv_data.total_len; + ret = hdd_driver_command(adapter, &priv_data); +exit: + return ret; +} +#else /* CONFIG_COMPAT */ +static int hdd_driver_compat_ioctl(struct hdd_adapter *adapter, struct ifreq *ifr) +{ + /* will never be invoked */ + return 0; +} +#endif /* CONFIG_COMPAT */ + +static int hdd_driver_ioctl(struct hdd_adapter *adapter, struct ifreq *ifr) +{ + struct hdd_priv_data priv_data; + int ret = 0; + + /* + * Note that adapter and ifr have already been verified by caller, + * and HDD context has also been validated + */ + if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data))) + ret = -EFAULT; + else + ret = hdd_driver_command(adapter, &priv_data); + + return ret; +} + +/** + * __hdd_ioctl() - ioctl handler for wlan network interfaces + * @dev: device upon which the ioctl was received + * @ifr: ioctl request information + * @cmd: ioctl command + * + * This function does initial processing of wlan device ioctls. + * Currently two flavors of ioctls are supported. The primary ioctl + * that is supported is the (SIOCDEVPRIVATE + 1) ioctl which is used + * for Android "DRIVER" commands. The other ioctl that is + * conditionally supported is the SIOCIOCTLTX99 ioctl which is used + * for FTM on some platforms. This function simply verifies that the + * driver is in a sane state, and that the ioctl is one of the + * supported flavors, in which case flavor-specific handlers are + * dispatched. + * + * Return: 0 on success, non-zero on error + */ +static int __hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter_dev(dev); + + if (dev != adapter->dev) { + hdd_err("HDD adapter/dev inconsistency"); + ret = -ENODEV; + goto exit; + } + + if ((!ifr) || (!ifr->ifr_data)) { + hdd_err("invalid data cmd: %d", cmd); + ret = -EINVAL; + goto exit; + } +#if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR) + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + if (SIOCIOCTLTX99 == cmd) { + ret = wlan_hdd_qcmbr_unified_ioctl(adapter, ifr); + goto exit; + } + } +#endif + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + goto exit; + + switch (cmd) { + case (SIOCDEVPRIVATE + 1): + if (in_compat_syscall()) + ret = hdd_driver_compat_ioctl(adapter, ifr); + else + ret = hdd_driver_ioctl(adapter, ifr); + break; + default: + hdd_warn("unknown ioctl %d", cmd); + ret = -EINVAL; + break; + } +exit: + hdd_exit(); + return ret; +} + +/** + * hdd_ioctl() - ioctl handler (wrapper) for wlan network interfaces + * @net_dev: device upon which the ioctl was received + * @ifr: ioctl request information + * @cmd: ioctl command + * + * This function acts as an SSR-protecting wrapper to __hdd_ioctl() + * which is where the ioctls are really handled. + * + * Return: 0 on success, non-zero on error + */ +int hdd_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_ioctl(net_dev, ifr, cmd); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ioctl.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ioctl.h new file mode 100644 index 0000000000000000000000000000000000000000..92180146713f8d56e736d98402a7496daf3de36e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ioctl.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2012-2014, 2017-2019, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_IOCTL_H) +#define WLAN_HDD_IOCTL_H + +#include +#include +#include "wlan_hdd_main.h" + +extern struct sock *cesium_nl_srv_sock; + +int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +int wlan_hdd_set_mc_rate(struct hdd_adapter *adapter, int target_rate); + +/** + * hdd_update_smps_antenna_mode() - set smps and antenna mode + * @hdd_ctx: Pointer to hdd context + * @mode: antenna mode + * + * This function will set smps and antenna mode. + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_update_smps_antenna_mode(struct hdd_context *hdd_ctx, int mode); + +/** + * hdd_set_antenna_mode() - SET ANTENNA MODE command handler + * @adapter: Pointer to network adapter + * @hdd_ctx: Pointer to hdd context + * @mode: new anteena mode + */ +int hdd_set_antenna_mode(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx, int mode); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * hdd_get_roam_scan_ch_cb() - roam scan channel list callback handler + * @hdd_handle: Pointer to hdd context + * @roam_ch: pointer to roam scan ch event data + * @context: cookie + * + * Callback function to processes roam scan chaanel list event. If + * command response field in the response message is set that means + * event received as a response of GETROAMSCANCHANNELS command else + * event was rasied by firmware upon disconnection. + * + * Return: none + */ +void hdd_get_roam_scan_ch_cb(hdd_handle_t hdd_handle, + struct roam_scan_ch_resp *roam_ch, + void *context); + +/** + * hdd_get_roam_scan_freq() - roam scan freq list + * @adapter: Pointer to hdd adapter + * @mac_handle: pointer to mac_handle + * @chan_list: Pointer to hold roam scan freq list + * @num_channels: Pointer to hold num of roam scan channels in list + * + * This function gets roam scan frequencies from FW if FW is capable else + * roam scan frequencies are taken from host maintained list. + * + * Return: 0 on success else error value + */ +int +hdd_get_roam_scan_freq(struct hdd_adapter *adapter, mac_handle_t mac_handle, + uint32_t *chan_list, uint8_t *num_channels); +#else +static inline void +hdd_get_roam_scan_ch_cb(hdd_handle_t hdd_handle, + void *roam_ch, + void *context) +{ +} + +static inline int +hdd_get_roam_scan_freq(struct hdd_adapter *adapter, mac_handle_t mac_handle, + uint32_t *chan_list, uint8_t *num_channels) +{ + return -EFAULT; +} +#endif + +#ifdef QCA_IBSS_SUPPORT +/** + * hdd_get_ibss_peer_info_cb() - IBSS peer Info request callback + * @context: callback context (adapter supplied by caller) + * @peer_info: Peer info response + * + * This is an asynchronous callback function from SME when the peer info + * is received + * + * Return: 0 for success non-zero for failure + */ +void hdd_get_ibss_peer_info_cb(void *context, + tSirPeerInfoRspParams *peer_info); +#else +static inline void +hdd_get_ibss_peer_info_cb(void *context, + tSirPeerInfoRspParams *peer_info) +{ +} +#endif +#endif /* end #if !defined(WLAN_HDD_IOCTL_H) */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ipa.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ipa.c new file mode 100644 index 0000000000000000000000000000000000000000..87c831c940f4023d411f70d127a56d0cbc18b048 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ipa.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_ipa.c + * + * WLAN HDD and ipa interface implementation + */ + +/* Include Files */ +#include +#include +#include "wlan_policy_mgr_api.h" +#include "wlan_ipa_ucfg_api.h" +#include +#include +#include + +void hdd_ipa_set_tx_flow_info(void) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + struct hdd_station_ctx *sta_ctx; + struct hdd_ap_ctx *hdd_ap_ctx; + struct hdd_hostapd_state *hostapd_state; + struct qdf_mac_addr staBssid = QDF_MAC_ADDR_ZERO_INIT; + struct qdf_mac_addr p2pBssid = QDF_MAC_ADDR_ZERO_INIT; + struct qdf_mac_addr apBssid = QDF_MAC_ADDR_ZERO_INIT; + uint8_t staChannel = 0, p2pChannel = 0, apChannel = 0; + const char *p2pMode = "DEV"; + struct hdd_context *hdd_ctx; + struct cds_context *cds_ctx; +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + uint8_t targetChannel = 0; + uint8_t preAdapterChannel = 0; + uint8_t channel24; + uint8_t channel5; + struct hdd_adapter *preAdapterContext = NULL; + struct hdd_adapter *adapter2_4 = NULL; + struct hdd_adapter *adapter5 = NULL; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + struct wlan_objmgr_psoc *psoc; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_IPA_SET_TX_FLOW_INFO; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return; + } + + cds_ctx = cds_get_context(QDF_MODULE_ID_QDF); + if (!cds_ctx) { + hdd_err("Invalid CDS Context"); + return; + } + + psoc = hdd_ctx->psoc; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + switch (adapter->device_mode) { + case QDF_STA_MODE: + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (eConnectionState_Associated == + sta_ctx->conn_info.conn_state) { + staChannel = wlan_reg_freq_to_chan( + hdd_ctx->pdev, + sta_ctx->conn_info.chan_freq); + qdf_copy_macaddr(&staBssid, + &sta_ctx->conn_info.bssid); +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + targetChannel = staChannel; +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + } + break; + case QDF_P2P_CLIENT_MODE: + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (eConnectionState_Associated == + sta_ctx->conn_info.conn_state) { + p2pChannel = wlan_reg_freq_to_chan( + hdd_ctx->pdev, + sta_ctx->conn_info.chan_freq); + qdf_copy_macaddr(&p2pBssid, + &sta_ctx->conn_info.bssid); + p2pMode = "CLI"; +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + targetChannel = p2pChannel; +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + } + break; + case QDF_P2P_GO_MODE: + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + if (hostapd_state->bss_state == BSS_START + && hostapd_state->qdf_status == + QDF_STATUS_SUCCESS) { + p2pChannel = wlan_reg_freq_to_chan( + hdd_ctx->pdev, + hdd_ap_ctx->operating_chan_freq); + qdf_copy_macaddr(&p2pBssid, + &adapter->mac_addr); +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + targetChannel = p2pChannel; +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + } + p2pMode = "GO"; + break; + case QDF_SAP_MODE: + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + if (hostapd_state->bss_state == BSS_START + && hostapd_state->qdf_status == + QDF_STATUS_SUCCESS) { + apChannel = wlan_reg_freq_to_chan( + hdd_ctx->pdev, + hdd_ap_ctx->operating_chan_freq); + qdf_copy_macaddr(&apBssid, + &adapter->mac_addr); +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + targetChannel = apChannel; +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + } + break; + case QDF_IBSS_MODE: + default: + break; + } +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + if (targetChannel) { + /* + * This is first adapter detected as active + * set as default for none concurrency case + */ + if (!preAdapterChannel) { + /* If IPA UC data path is enabled, + * target should reserve extra tx descriptors + * for IPA data path. + * Then host data path should allow less TX + * packet pumping in case IPA + * data path enabled + */ + if (ucfg_ipa_uc_is_enabled() && + (QDF_SAP_MODE == adapter->device_mode)) { + adapter->tx_flow_low_watermark = + hdd_ctx->config->tx_flow_low_watermark + + WLAN_TFC_IPAUC_TX_DESC_RESERVE; + } else { + adapter->tx_flow_low_watermark = + hdd_ctx->config-> + tx_flow_low_watermark; + } + adapter->tx_flow_hi_watermark_offset = + hdd_ctx->config->tx_flow_hi_watermark_offset; + cdp_fc_ll_set_tx_pause_q_depth(soc, + adapter->vdev_id, + hdd_ctx->config-> + tx_flow_max_queue_depth); + hdd_info("MODE %d,CH %d,LWM %d,HWM %d,TXQDEP %d", + adapter->device_mode, + targetChannel, + adapter->tx_flow_low_watermark, + adapter->tx_flow_low_watermark + + adapter->tx_flow_hi_watermark_offset, + hdd_ctx->config->tx_flow_max_queue_depth); + preAdapterChannel = targetChannel; + preAdapterContext = adapter; + } else { + /* + * SCC, disable TX flow control for both + * SCC each adapter cannot reserve dedicated + * channel resource, as a result, if any adapter + * blocked OS Q by flow control, + * blocked adapter will lost chance to recover + */ + if (preAdapterChannel == targetChannel) { + /* Current adapter */ + adapter->tx_flow_low_watermark = 0; + adapter-> + tx_flow_hi_watermark_offset = 0; + cdp_fc_ll_set_tx_pause_q_depth(soc, + adapter->vdev_id, + hdd_ctx->config-> + tx_hbw_flow_max_queue_depth); + hdd_info("SCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d", + qdf_opmode_str( + adapter->device_mode), + adapter->device_mode, + targetChannel, + adapter->tx_flow_low_watermark, + adapter->tx_flow_low_watermark + + adapter-> + tx_flow_hi_watermark_offset, + hdd_ctx->config-> + tx_hbw_flow_max_queue_depth); + + if (!preAdapterContext) { + hdd_err("SCC: Previous adapter context NULL"); + hdd_adapter_dev_put_debug( + adapter, dbgid); + continue; + } + + /* Previous adapter */ + preAdapterContext-> + tx_flow_low_watermark = 0; + preAdapterContext-> + tx_flow_hi_watermark_offset = 0; + cdp_fc_ll_set_tx_pause_q_depth(soc, + preAdapterContext->vdev_id, + hdd_ctx->config-> + tx_hbw_flow_max_queue_depth); + hdd_info("SCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d", + qdf_opmode_str( + preAdapterContext->device_mode), + preAdapterContext->device_mode, + targetChannel, + preAdapterContext-> + tx_flow_low_watermark, + preAdapterContext-> + tx_flow_low_watermark + + preAdapterContext-> + tx_flow_hi_watermark_offset, + hdd_ctx->config-> + tx_hbw_flow_max_queue_depth); + } + /* + * MCC, each adapter will have dedicated + * resource + */ + else { + /* current channel is 2.4 */ + if (targetChannel <= + WLAN_HDD_TX_FLOW_CONTROL_MAX_24BAND_CH) { + channel24 = targetChannel; + channel5 = preAdapterChannel; + adapter2_4 = adapter; + adapter5 = preAdapterContext; + } else { + /* Current channel is 5 */ + channel24 = preAdapterChannel; + channel5 = targetChannel; + adapter2_4 = preAdapterContext; + adapter5 = adapter; + } + + if (!adapter5) { + hdd_err("MCC: 5GHz adapter context NULL"); + hdd_adapter_dev_put_debug( + adapter, dbgid); + continue; + } + adapter5->tx_flow_low_watermark = + hdd_ctx->config-> + tx_hbw_flow_low_watermark; + adapter5-> + tx_flow_hi_watermark_offset = + hdd_ctx->config-> + tx_hbw_flow_hi_watermark_offset; + cdp_fc_ll_set_tx_pause_q_depth(soc, + adapter5->vdev_id, + hdd_ctx->config-> + tx_hbw_flow_max_queue_depth); + hdd_info("MCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d", + qdf_opmode_str( + adapter5->device_mode), + adapter5->device_mode, + channel5, + adapter5->tx_flow_low_watermark, + adapter5-> + tx_flow_low_watermark + + adapter5-> + tx_flow_hi_watermark_offset, + hdd_ctx->config-> + tx_hbw_flow_max_queue_depth); + + if (!adapter2_4) { + hdd_err("MCC: 2.4GHz adapter context NULL"); + hdd_adapter_dev_put_debug( + adapter, dbgid); + continue; + } + adapter2_4->tx_flow_low_watermark = + hdd_ctx->config-> + tx_lbw_flow_low_watermark; + adapter2_4-> + tx_flow_hi_watermark_offset = + hdd_ctx->config-> + tx_lbw_flow_hi_watermark_offset; + cdp_fc_ll_set_tx_pause_q_depth(soc, + adapter2_4->vdev_id, + hdd_ctx->config-> + tx_lbw_flow_max_queue_depth); + hdd_info("MCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d", + qdf_opmode_str( + adapter2_4->device_mode), + adapter2_4->device_mode, + channel24, + adapter2_4-> + tx_flow_low_watermark, + adapter2_4-> + tx_flow_low_watermark + + adapter2_4-> + tx_flow_hi_watermark_offset, + hdd_ctx->config-> + tx_lbw_flow_max_queue_depth); + + } + } + } + targetChannel = 0; +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + hdd_adapter_dev_put_debug(adapter, dbgid); + } +} + +#if defined(QCA_CONFIG_SMP) && defined(PF_WAKE_UP_IDLE) +/** + * hdd_ipa_get_wake_up_idle() - Get PF_WAKE_UP_IDLE flag in the task structure + * + * Get PF_WAKE_UP_IDLE flag in the task structure + * + * Return: 1 if PF_WAKE_UP_IDLE flag is set, 0 otherwise + */ +static uint32_t hdd_ipa_get_wake_up_idle(void) +{ + return sched_get_wake_up_idle(current); +} + +/** + * hdd_ipa_set_wake_up_idle() - Set PF_WAKE_UP_IDLE flag in the task structure + * + * Set PF_WAKE_UP_IDLE flag in the task structure + * This task and any task woken by this will be waken to idle CPU + * + * Return: None + */ +static void hdd_ipa_set_wake_up_idle(bool wake_up_idle) +{ + sched_set_wake_up_idle(current, wake_up_idle); +} +#else +static uint32_t hdd_ipa_get_wake_up_idle(void) +{ + return 0; +} + +static void hdd_ipa_set_wake_up_idle(bool wake_up_idle) +{ +} +#endif + +/** + * hdd_ipa_send_to_nw_stack() - Check if IPA supports NAPI + * polling during RX + * @skb : data buffer sent to network stack + * + * If IPA LAN RX supports NAPI polling mechanism use + * netif_receive_skb instead of netif_rx_ni to forward the skb + * to network stack. + * + * Return: Return value from netif_rx_ni/netif_receive_skb + */ +static int hdd_ipa_send_to_nw_stack(qdf_nbuf_t skb) +{ + int result; + + if (qdf_ipa_get_lan_rx_napi()) + result = netif_receive_skb(skb); + else + result = netif_rx_ni(skb); + return result; +} + +#ifdef QCA_CONFIG_SMP + +/** + * hdd_ipa_aggregated_rx_ind() - Submit aggregated packets to the stack + * @skb: skb to be submitted to the stack + * + * For CONFIG_SMP systems, simply call netif_rx_ni. + * For non CONFIG_SMP systems call netif_rx till + * IPA_WLAN_RX_SOFTIRQ_THRESH. When threshold is reached call netif_rx_ni. + * In this manner, UDP/TCP packets are sent in an aggregated way to the stack. + * For IP/ICMP packets, simply call netif_rx_ni. + * + * Check if IPA supports NAPI polling then use netif_receive_skb + * instead of netif_rx_ni. + * + * Return: return value from the netif_rx_ni/netif_rx api. + */ +static int hdd_ipa_aggregated_rx_ind(qdf_nbuf_t skb) +{ + int ret; + + ret = hdd_ipa_send_to_nw_stack(skb); + return ret; +} +#else +static int hdd_ipa_aggregated_rx_ind(qdf_nbuf_t skb) +{ + struct iphdr *ip_h; + static atomic_t softirq_mitigation_cntr = + ATOMIC_INIT(IPA_WLAN_RX_SOFTIRQ_THRESH); + int result; + + ip_h = (struct iphdr *)(skb->data); + if ((skb->protocol == htons(ETH_P_IP)) && + (ip_h->protocol == IPPROTO_ICMP)) { + result = hdd_ipa_send_to_nw_stack(skb); + } else { + /* Call netif_rx_ni for every IPA_WLAN_RX_SOFTIRQ_THRESH packets + * to avoid excessive softirq's. + */ + if (atomic_dec_and_test(&softirq_mitigation_cntr)) { + result = hdd_ipa_send_to_nw_stack(skb); + atomic_set(&softirq_mitigation_cntr, + IPA_WLAN_RX_SOFTIRQ_THRESH); + } else { + result = netif_rx(skb); + } + } + + return result; +} +#endif + +void hdd_ipa_send_nbuf_to_network(qdf_nbuf_t nbuf, qdf_netdev_t dev) +{ + struct hdd_adapter *adapter = (struct hdd_adapter *) netdev_priv(dev); + int result; + unsigned int cpu_index; + uint32_t enabled; + + if (hdd_validate_adapter(adapter)) { + kfree_skb(nbuf); + return; + } + + if (cds_is_driver_unloading()) { + kfree_skb(nbuf); + return; + } + + if ((adapter->device_mode == QDF_SAP_MODE) && + (qdf_nbuf_is_ipv4_dhcp_pkt(nbuf) == true)) { + /* Send DHCP Indication to FW */ + hdd_softap_inspect_dhcp_packet(adapter, nbuf, QDF_RX); + } + + qdf_dp_trace_set_track(nbuf, QDF_RX); + + hdd_event_eapol_log(nbuf, QDF_RX); + qdf_dp_trace_log_pkt(adapter->vdev_id, + nbuf, QDF_RX, QDF_TRACE_DEFAULT_PDEV_ID); + DPTRACE(qdf_dp_trace(nbuf, + QDF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(nbuf), + sizeof(qdf_nbuf_data(nbuf)), QDF_RX)); + DPTRACE(qdf_dp_trace_data_pkt(nbuf, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_DP_TRACE_RX_PACKET_RECORD, 0, + QDF_RX)); + + /* + * Set PF_WAKE_UP_IDLE flag in the task structure + * This task and any task woken by this will be waken to idle CPU + */ + enabled = hdd_ipa_get_wake_up_idle(); + if (!enabled) + hdd_ipa_set_wake_up_idle(true); + + nbuf->dev = adapter->dev; + nbuf->protocol = eth_type_trans(nbuf, nbuf->dev); + nbuf->ip_summed = CHECKSUM_NONE; + + cpu_index = wlan_hdd_get_cpu(); + + ++adapter->hdd_stats.tx_rx_stats.rx_packets[cpu_index]; + + /* + * Update STA RX exception packet stats. + * For SAP as part of IPA HW stats are updated. + */ + + ++adapter->stats.rx_packets; + adapter->stats.rx_bytes += nbuf->len; + + result = hdd_ipa_aggregated_rx_ind(nbuf); + if (result == NET_RX_SUCCESS) + ++adapter->hdd_stats.tx_rx_stats.rx_delivered[cpu_index]; + else + ++adapter->hdd_stats.tx_rx_stats.rx_refused[cpu_index]; + + /* + * Restore PF_WAKE_UP_IDLE flag in the task structure + */ + if (!enabled) + hdd_ipa_set_wake_up_idle(false); +} + +void hdd_ipa_set_mcc_mode(bool mcc_mode) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return; + } + + ucfg_ipa_set_mcc_mode(hdd_ctx->pdev, mcc_mode); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_lpass.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_lpass.c new file mode 100644 index 0000000000000000000000000000000000000000..5bcc702c65926907afa44eb6736456ec287a7c1a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_lpass.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_lpass.c + * + * WLAN Host Device Driver LPASS feature implementation + * + */ + +/* Include Files */ +#include "wlan_hdd_main.h" +#include "wlan_hdd_lpass.h" +#include "wlan_hdd_oemdata.h" +#include +#include "qwlan_version.h" + +/** + * wlan_hdd_get_channel_info() - Get channel info + * @hdd_ctx: HDD context + * @chan_info: Pointer to the structure that stores channel info + * @chan_freq: Channel freq + * + * Fill in the channel info to chan_info structure. + */ +static void wlan_hdd_get_channel_info(struct hdd_context *hdd_ctx, + struct svc_channel_info *chan_info, + uint32_t chan_freq) +{ + uint32_t reg_info_1; + uint32_t reg_info_2; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + status = sme_get_reg_info(hdd_ctx->mac_handle, chan_freq, + ®_info_1, ®_info_2); + if (status != QDF_STATUS_SUCCESS) + return; + + chan_info->mhz = chan_freq; + chan_info->band_center_freq1 = chan_info->mhz; + chan_info->band_center_freq2 = 0; + chan_info->info = 0; + if (CHANNEL_STATE_DFS == + wlan_reg_get_channel_state_for_freq(hdd_ctx->pdev, chan_freq)) + WMI_SET_CHANNEL_FLAG(chan_info, + WMI_CHAN_FLAG_DFS); + hdd_update_channel_bw_info(hdd_ctx, chan_freq, + chan_info); + chan_info->reg_info_1 = reg_info_1; + chan_info->reg_info_2 = reg_info_2; +} + +/** + * wlan_hdd_gen_wlan_status_pack() - Create lpass adapter status package + * @data: Status data record to be created + * @adapter: Adapter whose status is to being packaged + * @sta_ctx: Station-specific context of @adapter + * @is_on: Is wlan driver loaded? + * @is_connected: Is @adapter connected to an AP? + * + * Generate a wlan vdev status package. The status info includes wlan + * on/off status, vdev ID, vdev mode, supported channels, etc. + * + * Return: 0 if package was created, otherwise a negative errno + */ +static int wlan_hdd_gen_wlan_status_pack(struct wlan_status_data *data, + struct hdd_adapter *adapter, + struct hdd_station_ctx *sta_ctx, + uint8_t is_on, uint8_t is_connected) +{ + struct hdd_context *hdd_ctx = NULL; + uint8_t buflen = WLAN_SVC_COUNTRY_CODE_LEN; + int i; + uint32_t chan_id; + uint32_t *chan_freq_list, chan_freq_len; + struct svc_channel_info *chan_info; + bool lpass_support; + QDF_STATUS status; + + if (!data) { + hdd_err("invalid data pointer"); + return -EINVAL; + } + if (!adapter) { + if (is_on) { + /* no active interface */ + data->lpss_support = 0; + data->is_on = is_on; + return 0; + } + hdd_err("invalid adapter pointer"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + status = ucfg_mlme_get_lpass_support(hdd_ctx->psoc, &lpass_support); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get LPASS support config"); + return -EIO; + } + + if (hdd_ctx->lpss_support && lpass_support) + data->lpss_support = 1; + else + data->lpss_support = 0; + + chan_freq_list = + qdf_mem_malloc(sizeof(uint32_t) * WLAN_SVC_MAX_NUM_CHAN); + if (!chan_freq_list) + return -ENOMEM; + + chan_freq_len = WLAN_SVC_MAX_NUM_CHAN; + sme_get_cfg_valid_channels(chan_freq_list, &chan_freq_len); + + data->numChannels = chan_freq_len; + + for (i = 0; i < data->numChannels; i++) { + chan_info = &data->channel_info[i]; + data->channel_list[i] = + wlan_reg_freq_to_chan(hdd_ctx->pdev, chan_freq_list[i]); + chan_id = data->channel_list[i]; + chan_info->chan_id = chan_id; + wlan_hdd_get_channel_info(hdd_ctx, + chan_info, + chan_freq_list[i]); + } + + qdf_mem_free(chan_freq_list); + + sme_get_country_code(hdd_ctx->mac_handle, data->country_code, &buflen); + data->is_on = is_on; + data->vdev_id = adapter->vdev_id; + data->vdev_mode = adapter->device_mode; + if (sta_ctx) { + data->is_connected = is_connected; + data->rssi = adapter->rssi; + data->freq = sta_ctx->conn_info.chan_freq; + if (WLAN_SVC_MAX_SSID_LEN >= + sta_ctx->conn_info.ssid.SSID.length) { + data->ssid_len = sta_ctx->conn_info.ssid.SSID.length; + memcpy(data->ssid, + sta_ctx->conn_info.ssid.SSID.ssId, + sta_ctx->conn_info.ssid.SSID.length); + } + if (QDF_MAC_ADDR_SIZE >= sizeof(sta_ctx->conn_info.bssid)) + memcpy(data->bssid, sta_ctx->conn_info.bssid.bytes, + QDF_MAC_ADDR_SIZE); + } + return 0; +} + +/** + * wlan_hdd_gen_wlan_version_pack() - Create lpass version package + * @data: Version data record to be created + * @fw_version: Version code from firmware + * @chip_id: WLAN chip ID + * @chip_name: WLAN chip name + * + * Generate a wlan software/hw version info package. The version info + * includes wlan host driver version, wlan fw driver version, wlan hw + * chip id & wlan hw chip name. + * + * Return: 0 if package was created, otherwise a negative errno + */ +static int wlan_hdd_gen_wlan_version_pack(struct wlan_version_data *data, + uint32_t fw_version, + uint32_t chip_id, + const char *chip_name) +{ + if (!data) { + hdd_err("invalid data pointer"); + return -EINVAL; + } + + data->chip_id = chip_id; + strlcpy(data->chip_name, chip_name, WLAN_SVC_MAX_STR_LEN); + if (strncmp(chip_name, "Unknown", 7)) + strlcpy(data->chip_from, "Qualcomm", WLAN_SVC_MAX_STR_LEN); + else + strlcpy(data->chip_from, "Unknown", WLAN_SVC_MAX_STR_LEN); + strlcpy(data->host_version, QWLAN_VERSIONSTR, WLAN_SVC_MAX_STR_LEN); + scnprintf(data->fw_version, WLAN_SVC_MAX_STR_LEN, "%d.%d.%d.%d", + (fw_version & 0xf0000000) >> 28, + (fw_version & 0xf000000) >> 24, + (fw_version & 0xf00000) >> 20, (fw_version & 0x7fff)); + return 0; +} + +/** + * wlan_hdd_send_status_pkg() - Send adapter status to lpass + * @adapter: Adapter whose status is to be sent to lpass + * @sta_ctx: Station-specific context of @adapter + * @is_on: Is @adapter enabled + * @is_connected: Is @adapter connected + * + * Generate wlan vdev status package and send it to a user space + * daemon through netlink. + * + * Return: none + */ +static void wlan_hdd_send_status_pkg(struct hdd_adapter *adapter, + struct hdd_station_ctx *sta_ctx, + uint8_t is_on, uint8_t is_connected) +{ + int ret = 0; + struct wlan_status_data *data = NULL; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) + return; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) + return; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return; + + if (is_on) + ret = wlan_hdd_gen_wlan_status_pack(data, adapter, sta_ctx, + is_on, is_connected); + + if (!ret) + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_WLAN_STATUS_IND, + data, sizeof(*data)); + kfree(data); +} + +/** + * wlan_hdd_send_version_pkg() - report version information to lpass + * @fw_version: Version code from firmware + * @chip_id: WLAN chip ID + * @chip_name: WLAN chip name + * + * Generate a wlan sw/hw version info package and send it to a user + * space daemon through netlink. + * + * Return: none + */ +static void wlan_hdd_send_version_pkg(uint32_t fw_version, + uint32_t chip_id, + const char *chip_name) +{ + int ret = 0; + struct wlan_version_data data; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) + return; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) + return; + + memset(&data, 0, sizeof(struct wlan_version_data)); + ret = wlan_hdd_gen_wlan_version_pack(&data, fw_version, chip_id, + chip_name); + if (!ret) + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_WLAN_VERSION_IND, + &data, sizeof(data)); +} + +/** + * wlan_hdd_send_scan_intf_info() - report scan interfaces to lpass + * @hdd_ctx: The global HDD context + * @adapter: Adapter that supports scanning. + * + * This function indicates adapter that supports scanning to lpass. + * + * Return: none + */ +static void wlan_hdd_send_scan_intf_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + if (!hdd_ctx) { + hdd_err("NULL pointer for hdd_ctx"); + return; + } + + if (!adapter) { + hdd_err("Adapter is Null"); + return; + } + + wlan_hdd_send_status_pkg(adapter, NULL, 1, 0); +} + +/* + * hdd_lpass_target_config() - Handle LPASS target configuration + * (public function documented in wlan_hdd_lpass.h) + */ +void hdd_lpass_target_config(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *target_config) +{ + hdd_ctx->lpss_support = target_config->lpss_support; +} + +/* + * hdd_lpass_populate_cds_config() - Populate LPASS configuration + * (public function documented in wlan_hdd_lpass.h) + */ +void hdd_lpass_populate_cds_config(struct cds_config_info *cds_config, + struct hdd_context *hdd_ctx) +{ + bool lpass_support = false; + QDF_STATUS status; + + status = ucfg_mlme_get_lpass_support(hdd_ctx->psoc, &lpass_support); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to get LPASS support config"); + + cds_config->is_lpass_enabled = lpass_support; +} + +/* + * hdd_lpass_populate_pmo_config() - Populate LPASS configuration + * (public function documented in wlan_hdd_lpass.h) + */ +void hdd_lpass_populate_pmo_config(struct pmo_psoc_cfg *pmo_config, + struct hdd_context *hdd_ctx) +{ + bool lpass_support = false; + QDF_STATUS status; + + status = ucfg_mlme_get_lpass_support(hdd_ctx->psoc, &lpass_support); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to get LPASS support config"); + + pmo_config->lpass_enable = lpass_support; +} + +/* + * hdd_lpass_notify_connect() - Notify LPASS of interface connect + * (public function documented in wlan_hdd_lpass.h) + */ +void hdd_lpass_notify_connect(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx; + + /* only send once per connection */ + if (adapter->rssi_send) + return; + + /* don't send if driver is unloading */ + if (cds_is_driver_unloading()) + return; + + adapter->rssi_send = true; + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + wlan_hdd_send_status_pkg(adapter, sta_ctx, 1, 1); +} + +/* + * hdd_lpass_notify_disconnect() - Notify LPASS of interface disconnect + * (public function documented in wlan_hdd_lpass.h) + */ +void hdd_lpass_notify_disconnect(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx; + + adapter->rssi_send = false; + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + wlan_hdd_send_status_pkg(adapter, sta_ctx, 1, 0); +} + +void hdd_lpass_notify_mode_change(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + + if (!adapter || adapter->device_mode != QDF_STA_MODE) + return; + + hdd_debug("Sending Lpass mode change notification"); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + wlan_hdd_send_scan_intf_info(hdd_ctx, adapter); +} + +/* + * hdd_lpass_notify_wlan_version() - Notify LPASS WLAN Host/FW version + * + * implementation note: Notify LPASS for the WLAN host/firmware version. + */ +void hdd_lpass_notify_wlan_version(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + wlan_hdd_send_version_pkg(hdd_ctx->target_fw_version, + hdd_ctx->target_hw_version, + hdd_ctx->target_hw_name); + + hdd_exit(); +} + +void hdd_lpass_notify_start(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + hdd_enter(); + + if (!adapter || adapter->device_mode != QDF_STA_MODE) + return; + + hdd_debug("Sending Start Lpass notification"); + + wlan_hdd_send_scan_intf_info(hdd_ctx, adapter); + + hdd_exit(); +} + +void hdd_lpass_notify_stop(struct hdd_context *hdd_ctx) +{ + hdd_debug("Sending Lpass stop notifcation"); + wlan_hdd_send_status_pkg(NULL, NULL, 0, 0); +} + +/* + * hdd_lpass_is_supported() - Is lpass feature supported? + * (public function documented in wlan_hdd_lpass.h) + */ +bool hdd_lpass_is_supported(struct hdd_context *hdd_ctx) +{ + bool lpass_support = false; + QDF_STATUS status; + + status = ucfg_mlme_get_lpass_support(hdd_ctx->psoc, &lpass_support); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to get LPASS support config"); + + return lpass_support; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_lpass.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_lpass.h new file mode 100644 index 0000000000000000000000000000000000000000..489624251c24942992fe5a8e9d272cfadfc7f2c0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_lpass.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_LPASS_H) +#define WLAN_HDD_LPASS_H + +struct cds_config_info; +struct wma_tgt_cfg; +struct hdd_context; +struct hdd_adapter; + +#ifdef WLAN_FEATURE_LPSS +/** + * hdd_lpass_target_config() - Handle LPASS target configuration + * @hdd_ctx: HDD global context where lpass information is stored + * @target_config: Target configuration containing lpass info + * + * This function updates the HDD context with lpass-specific + * information provided by the target. + * + * Return: none + */ +void hdd_lpass_target_config(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *target_config); + +/** + * hdd_lpass_populate_cds_config() - Populate LPASS configuration + * @cds_config: CDS configuration to populate with lpass info + * @hdd_ctx: HDD global context which contains lpass information + * + * This function seeds the CDS configuration structure with + * lpass-specific information gleaned from the HDD context. + * + * Return: none + */ +void hdd_lpass_populate_cds_config(struct cds_config_info *cds_config, + struct hdd_context *hdd_ctx); + +/** + * hdd_lpass_populate_pmo_config() - Populate LPASS configuration + * @pmo_config: PMO configuration to populate with lpass info + * @hdd_ctx: HDD global context which contains lpass information + * + * This function seeds the PMO configuration structure with + * lpass-specific information gleaned from the HDD context. + * + * Return: none + */ +void hdd_lpass_populate_pmo_config(struct pmo_psoc_cfg *pmo_config, + struct hdd_context *hdd_ctx); + +/** + * hdd_lpass_notify_connect() - Notify LPASS of interface connect + * @adapter: The adapter that connected + * + * This function is used to notify the LPASS feature that an adapter + * has connected. + * + * Return: none + */ +void hdd_lpass_notify_connect(struct hdd_adapter *adapter); + +/** + * hdd_lpass_notify_disconnect() - Notify LPASS of interface disconnect + * @adapter: The adapter that connected + * + * This function is used to notify the LPASS feature that an adapter + * has disconnected. + * + * Return: none + */ +void hdd_lpass_notify_disconnect(struct hdd_adapter *adapter); + +/** + * hdd_lpass_notify_mode_change() - Notify LPASS of interface mode change + * @adapter: The adapter whose mode was changed + * + * This function is used to notify the LPASS feature that an adapter + * had its mode changed. + * + * Return: none + */ +void hdd_lpass_notify_mode_change(struct hdd_adapter *adapter); + +/** + * hdd_lpass_notify_start() - Notify LPASS of driver start + * @hdd_ctx: The global HDD context + * @adapter: adapter for which notification is send + * + * This function is used to notify the LPASS feature that the wlan + * driver has (re-)started. + * + * Return: none + */ +void hdd_lpass_notify_start(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter); + +/** + * hdd_lpass_notify_stop() - Notify LPASS of driver stop + * @hdd_ctx: The global HDD context + * + * This function is used to notify the LPASS feature that the wlan + * driver has stopped. + * + * Return: none + */ +void hdd_lpass_notify_stop(struct hdd_context *hdd_ctx); + +/** + * hdd_lpass_is_supported() - Is lpass feature supported? + * @hdd_ctx: The global HDD context + * + * Return: true if feature is enabled and supported by firmware, false + * if the feature is not enabled or not supported by firmware. + */ +bool hdd_lpass_is_supported(struct hdd_context *hdd_ctx); + +/* + * hdd_lpass_notify_wlan_version() - Notify LPASS WLAN Host/FW version + * @hdd_ctx: The global HDD context + * + * Notify LPASS for the WLAN host/firmware and hardware version. + * + * Return: none + */ +void hdd_lpass_notify_wlan_version(struct hdd_context *hdd_ctx); +#else +static inline void hdd_lpass_target_config(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *target_config) +{ +} +static inline +void hdd_lpass_populate_cds_config(struct cds_config_info *cds_config, + struct hdd_context *hdd_ctx) +{ +} + +static inline +void hdd_lpass_populate_pmo_config(struct pmo_psoc_cfg *pmo_config, + struct hdd_context *hdd_ctx) +{ +} + +static inline void hdd_lpass_notify_connect(struct hdd_adapter *adapter) +{ +} +static inline void hdd_lpass_notify_disconnect(struct hdd_adapter *adapter) +{ +} +static inline void hdd_lpass_notify_mode_change(struct hdd_adapter *adapter) +{ +} + +static inline void hdd_lpass_notify_start(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ +} + +static inline void hdd_lpass_notify_stop(struct hdd_context *hdd_ctx) { } +static inline bool hdd_lpass_is_supported(struct hdd_context *hdd_ctx) +{ + return false; +} + +static inline void hdd_lpass_notify_wlan_version(struct hdd_context *hdd_ctx) +{ +} + +#endif + +#endif /* WLAN_HDD_LPASS_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_lro.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_lro.c new file mode 100644 index 0000000000000000000000000000000000000000..97972ecd063d9e961e3173c97a3b957465451294 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_lro.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_lro.c + * + * WLAN HDD LRO interface implementation + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define LRO_VALID_FIELDS \ + (LRO_DESC | LRO_ELIGIBILITY_CHECKED | LRO_TCP_ACK_NUM | \ + LRO_TCP_DATA_CSUM | LRO_TCP_SEQ_NUM | LRO_TCP_WIN) + +#if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ + defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) +static qdf_lro_ctx_t wlan_hdd_get_lro_ctx(struct sk_buff *skb) +{ + return (qdf_lro_ctx_t)QDF_NBUF_CB_RX_LRO_CTX(skb); +} +#else +static qdf_lro_ctx_t wlan_hdd_get_lro_ctx(struct sk_buff *skb) +{ + struct hif_opaque_softc *hif_hdl = + (struct hif_opaque_softc *)cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_hdl) { + hdd_err("hif_hdl is NULL"); + return NULL; + } + + return hif_get_lro_info(QDF_NBUF_CB_RX_CTX_ID(skb), hif_hdl); +} +#endif + +/** + * hdd_lro_rx() - LRO receive function + * @adapter: HDD adapter + * @skb: network buffer + * + * Delivers LRO eligible frames to the LRO manager + * + * Return: QDF_STATUS_SUCCESS - frame delivered to LRO manager + * QDF_STATUS_E_FAILURE - frame not delivered + */ +QDF_STATUS hdd_lro_rx(struct hdd_adapter *adapter, struct sk_buff *skb) +{ + qdf_lro_ctx_t ctx; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct qdf_lro_info info; + struct net_lro_desc *lro_desc = NULL; + + if ((adapter->dev->features & NETIF_F_LRO) != NETIF_F_LRO) + return QDF_STATUS_E_NOSUPPORT; + + ctx = wlan_hdd_get_lro_ctx(skb); + if (!ctx) { + hdd_err("LRO mgr is NULL"); + return status; + } + + info.iph = skb->data; + info.tcph = skb->data + QDF_NBUF_CB_RX_TCP_OFFSET(skb); + ctx->lro_mgr->dev = adapter->dev; + if (qdf_lro_get_info(ctx, skb, &info, (void **)&lro_desc)) { + struct net_lro_info hdd_lro_info; + + hdd_lro_info.valid_fields = LRO_VALID_FIELDS; + + hdd_lro_info.lro_desc = lro_desc; + hdd_lro_info.lro_eligible = 1; + hdd_lro_info.tcp_ack_num = QDF_NBUF_CB_RX_TCP_ACK_NUM(skb); + hdd_lro_info.tcp_data_csum = + csum_unfold(htons(QDF_NBUF_CB_RX_TCP_CHKSUM(skb))); + hdd_lro_info.tcp_seq_num = QDF_NBUF_CB_RX_TCP_SEQ_NUM(skb); + hdd_lro_info.tcp_win = QDF_NBUF_CB_RX_TCP_WIN(skb); + + lro_receive_skb_ext(ctx->lro_mgr, skb, (void *)adapter, + &hdd_lro_info); + + if (!hdd_lro_info.lro_desc->active) + qdf_lro_desc_free(ctx, lro_desc); + + status = QDF_STATUS_SUCCESS; + } else { + qdf_lro_flush_pkt(ctx, &info); + } + return status; +} + +/** + * hdd_lro_display_stats() - display LRO statistics + * @hdd_ctx: hdd context + * + * Return: none + */ +void hdd_lro_display_stats(struct hdd_context *hdd_ctx) +{ + hdd_debug("LRO stats is broken, will fix it"); +} + +QDF_STATUS +hdd_lro_set_reset(struct hdd_context *hdd_ctx, struct hdd_adapter *adapter, + uint8_t enable_flag) +{ + if ((hdd_ctx->ol_enable != CFG_LRO_ENABLED) || + (adapter->device_mode != QDF_STA_MODE)) { + hdd_debug("LRO is already Disabled"); + return 0; + } + + if (enable_flag) { + qdf_atomic_set(&hdd_ctx->vendor_disable_lro_flag, 0); + adapter->dev->features |= NETIF_F_LRO; + } else { + /* Disable LRO, Enable tcpdelack*/ + qdf_atomic_set(&hdd_ctx->vendor_disable_lro_flag, 1); + adapter->dev->features &= ~NETIF_F_LRO; + hdd_debug("LRO Disabled"); + + if (hdd_ctx->config->enable_tcp_delack) { + struct wlan_rx_tp_data rx_tp_data; + + hdd_debug("Enable TCP delack as LRO is disabled"); + rx_tp_data.rx_tp_flags = TCP_DEL_ACK_IND; + rx_tp_data.level = GET_CUR_RX_LVL(hdd_ctx); + wlan_hdd_update_tcp_rx_param(hdd_ctx, &rx_tp_data); + hdd_ctx->en_tcp_delack_no_lro = 1; + } + } + return 0; +} + +int hdd_is_lro_enabled(struct hdd_context *hdd_ctx) +{ + if (hdd_ctx->ol_enable != CFG_LRO_ENABLED) + return -EOPNOTSUPP; + + return 0; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_main.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_main.c new file mode 100644 index 0000000000000000000000000000000000000000..22d1e4bd4d05495a769ed795150cdb8e22421b73 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_main.c @@ -0,0 +1,18363 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_main.c + * + * WLAN Host Device Driver implementation + * + */ + +/* Include Files */ +#include +#include "cfg_ucfg_api.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "osif_sync.h" +#include +#include +#include +#include "wlan_hdd_trace.h" +#include "wlan_hdd_ioctl.h" +#include "wlan_hdd_ftm.h" +#include "wlan_hdd_power.h" +#include "wlan_hdd_stats.h" +#include "wlan_hdd_periodic_sta_stats.h" +#include "wlan_hdd_scan.h" +#include "wlan_policy_mgr_ucfg.h" +#include "wlan_osif_priv.h" +#include +#ifdef CONFIG_LEAK_DETECTION +#include "qdf_debug_domain.h" +#endif +#include "qdf_delayed_work.h" +#include "qdf_periodic_work.h" +#include "qdf_str.h" +#include "qdf_talloc.h" +#include "qdf_trace.h" +#include "qdf_types.h" +#include +#include +#include +#include "cdp_txrx_flow_ctrl_legacy.h" + +#include +#include +#include +#include +#include +#include "wlan_hdd_cfg80211.h" +#include "wlan_hdd_ext_scan.h" +#include "wlan_hdd_p2p.h" +#include +#include "sap_api.h" +#include +#include +#include +#include + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +#include "qdf_periodic_work.h" +#endif + +#ifdef MSM_PLATFORM +#include +#endif +#include +#include +#include +#include "qwlan_version.h" +#include "wma_types.h" +#include "wlan_hdd_tdls.h" +#ifdef FEATURE_WLAN_CH_AVOID +#include "cds_regdomain.h" +#endif /* FEATURE_WLAN_CH_AVOID */ +#include "cdp_txrx_flow_ctrl_v2.h" +#include "pld_common.h" +#include "wlan_hdd_ocb.h" +#include "wlan_hdd_nan.h" +#include "wlan_hdd_debugfs.h" +#include "wlan_hdd_debugfs_csr.h" +#include "wlan_hdd_driver_ops.h" +#include "epping_main.h" +#include "wlan_hdd_data_stall_detection.h" +#include "wlan_hdd_mpta_helper.h" + +#include +#include "hif.h" +#include "wma.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_hdd_tsf.h" +#include "bmi.h" +#include +#include "wlan_hdd_lpass.h" +#include "wlan_nan_api.h" +#include +#include "wlan_hdd_disa.h" +#include +#include "wlan_hdd_object_manager.h" +#include "cds_utils.h" +#include +#include +#include "wlan_pmo_ucfg_api.h" +#include "sir_api.h" +#include "os_if_wifi_pos.h" +#include "wifi_pos_api.h" +#include "wlan_hdd_oemdata.h" +#include "wlan_hdd_he.h" +#include "os_if_nan.h" +#include "nan_public_structs.h" +#include "nan_ucfg_api.h" +#include "wlan_reg_ucfg_api.h" +#include "wlan_dfs_ucfg_api.h" +#include "wlan_hdd_rx_monitor.h" +#include "sme_power_save_api.h" +#include "enet.h" +#include +#include +#include +#include "wlan_hdd_sysfs.h" +#include "wlan_disa_ucfg_api.h" +#include "wlan_disa_obj_mgmt_api.h" +#include "wlan_action_oui_ucfg_api.h" +#include "wlan_ipa_ucfg_api.h" +#include +#include "wlan_hdd_nud_tracking.h" +#include "wlan_hdd_apf.h" +#include "wlan_hdd_twt.h" +#include "qc_sap_ioctl.h" +#include "wlan_mlme_main.h" +#include "wlan_p2p_cfg_api.h" +#include "wlan_cfg80211_p2p.h" +#include "wlan_cfg80211_interop_issues_ap.h" +#include "wlan_tdls_cfg_api.h" +#include +#include "wlan_mlme_ucfg_api.h" +#include "wlan_fwol_ucfg_api.h" +#include "wlan_policy_mgr_ucfg.h" +#include "qdf_func_tracker.h" + +#ifdef CNSS_GENL +#include +#endif +#include "wlan_reg_ucfg_api.h" +#include "wlan_ocb_ucfg_api.h" +#include +#include "wlan_green_ap_ucfg_api.h" +#include +#include +#include +#include +#include +#include "wlan_blm_ucfg_api.h" +#include "ftm_time_sync_ucfg_api.h" +#include "ol_txrx.h" +#include "wlan_hdd_sta_info.h" +#include "mac_init_api.h" +#include "wlan_pkt_capture_ucfg_api.h" +#include +#include "cfg_nan_api.h" +#include +#include +#include "wlan_global_lmac_if_api.h" +#ifdef WLAN_FEATURE_DYNAMIC_RX_AGGREGATION +#include +#endif + +#ifdef MODULE +#define WLAN_MODULE_NAME module_name(THIS_MODULE) +#else +#define WLAN_MODULE_NAME "wlan" +#endif + +#ifdef TIMER_MANAGER +#define TIMER_MANAGER_STR " +TIMER_MANAGER" +#else +#define TIMER_MANAGER_STR "" +#endif + +#ifdef MEMORY_DEBUG +#define MEMORY_DEBUG_STR " +MEMORY_DEBUG" +#else +#define MEMORY_DEBUG_STR "" +#endif + +#ifdef PANIC_ON_BUG +#define PANIC_ON_BUG_STR " +PANIC_ON_BUG" +#else +#define PANIC_ON_BUG_STR "" +#endif + +#define MAX_NET_DEV_REF_LEAK_ITERATIONS 10 +#define NET_DEV_REF_LEAK_ITERATION_SLEEP_TIME_MS 10 + +#ifdef FEATURE_TSO +#define TSO_FEATURE_FLAGS (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_SG) +#else +#define TSO_FEATURE_FLAGS 0 +#endif + +int wlan_start_ret_val; +static DECLARE_COMPLETION(wlan_start_comp); +static qdf_atomic_t wlan_hdd_state_fops_ref; +static unsigned int dev_num = 1; +static struct cdev wlan_hdd_state_cdev; +static struct class *class; +static dev_t device; +#ifndef MODULE +static struct work_struct boot_work; +#endif + +/* the Android framework expects this param even though we don't use it */ +#define BUF_LEN 20 +static char fwpath_buffer[BUF_LEN]; +static struct kparam_string fwpath = { + .string = fwpath_buffer, + .maxlen = BUF_LEN, +}; + +static char *country_code; +static int enable_11d = -1; +static int enable_dfs_chan_scan = -1; +static bool is_mode_change_psoc_idle_shutdown; + +#define WLAN_NLINK_CESIUM 30 + +static qdf_wake_lock_t wlan_wake_lock; + +#define WOW_MAX_FILTER_LISTS 1 +#define WOW_MAX_FILTERS_PER_LIST 4 +#define WOW_MIN_PATTERN_SIZE 6 +#define WOW_MAX_PATTERN_SIZE 64 + +#define IS_IDLE_STOP (!cds_is_driver_unloading() && \ + !cds_is_driver_recovering() && !cds_is_driver_loading()) + +#define HDD_FW_VER_MAJOR_SPID(tgt_fw_ver) ((tgt_fw_ver & 0xf0000000) >> 28) +#define HDD_FW_VER_MINOR_SPID(tgt_fw_ver) ((tgt_fw_ver & 0xf000000) >> 24) +#define HDD_FW_VER_SIID(tgt_fw_ver) ((tgt_fw_ver & 0xf00000) >> 20) +#define HDD_FW_VER_CRM_ID(tgt_fw_ver) (tgt_fw_ver & 0x7fff) +#define HDD_FW_VER_SUB_ID(tgt_fw_ver_ext) \ +((tgt_fw_ver_ext & 0xf0000000) >> 28) +#define HDD_FW_VER_REL_ID(tgt_fw_ver_ext) \ +((tgt_fw_ver_ext & 0xf800000) >> 23) + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) +static const struct wiphy_wowlan_support wowlan_support_reg_init = { + .flags = WIPHY_WOWLAN_ANY | + WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_4WAY_HANDSHAKE | + WIPHY_WOWLAN_RFKILL_RELEASE, + .n_patterns = WOW_MAX_FILTER_LISTS * WOW_MAX_FILTERS_PER_LIST, + .pattern_min_len = WOW_MIN_PATTERN_SIZE, + .pattern_max_len = WOW_MAX_PATTERN_SIZE, +}; +#endif + +static const struct category_info cinfo[MAX_SUPPORTED_CATEGORY] = { + [QDF_MODULE_ID_TLSHIM] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_WMI] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_HTT] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_HDD] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_SME] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_PE] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_WMA] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_SYS] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_QDF] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_SAP] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_HDD_SOFTAP] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_HDD_DATA] = {QDF_DATA_PATH_TRACE_LEVEL}, + [QDF_MODULE_ID_HDD_SAP_DATA] = {QDF_DATA_PATH_TRACE_LEVEL}, + [QDF_MODULE_ID_HIF] = {QDF_DATA_PATH_TRACE_LEVEL}, + [QDF_MODULE_ID_HTC] = {QDF_DATA_PATH_TRACE_LEVEL}, + [QDF_MODULE_ID_TXRX] = {QDF_DATA_PATH_TRACE_LEVEL}, + [QDF_MODULE_ID_HAL] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_QDF_DEVICE] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_CFG] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_BMI] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_EPPING] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_QVIT] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_DP] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_SOC] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_OS_IF] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_TARGET_IF] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_SCHEDULER] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_MGMT_TXRX] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_PMO] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_SCAN] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_POLICY_MGR] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_P2P] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_TDLS] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_REGULATORY] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_SERIALIZATION] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_DFS] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_OBJ_MGR] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_ROAM_DEBUG] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_GREEN_AP] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_OCB] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_IPA] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_ACTION_OUI] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_CONFIG] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_MLME] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_TARGET] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_CRYPTO] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_FWOL] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_SM_ENGINE] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_CMN_MLME] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_NAN] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_CP_STATS] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_INTEROP_ISSUES_AP] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_BLACKLIST_MGR] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_DIRECT_BUF_RX] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_SPECTRAL] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_WIFIPOS] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_PKT_CAPTURE] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_FTM_TIME_SYNC] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_CFR] = {QDF_TRACE_LEVEL_ALL}, + [QDF_MODULE_ID_GPIO] = {QDF_TRACE_LEVEL_ALL}, +}; + +struct notifier_block hdd_netdev_notifier; + +struct sock *cesium_nl_srv_sock; +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +static void wlan_hdd_auto_shutdown_cb(void); +#endif + +QDF_STATUS hdd_common_roam_callback(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + adapter = wlan_hdd_get_adapter_from_vdev(psoc, session_id); + if (!adapter) + return QDF_STATUS_E_INVAL; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) + return QDF_STATUS_E_INVAL; + + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_NDI_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_DEVICE_MODE: + status = hdd_sme_roam_callback(adapter, roam_info, roam_id, + roam_status, roam_result); + break; + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + status = wlansap_roam_callback(adapter->session.ap.sap_context, + roam_info, roam_id, roam_status, + roam_result); + break; + default: + hdd_err("Wrong device mode"); + break; + } + + return status; +} +/** + * hdd_mic_flush_work() - disable and flush pending mic work + * @adapter: Pointer to hdd adapter + * + * Return: None + */ +static void +hdd_mic_flush_work(struct hdd_adapter *adapter) +{ + hdd_debug("Flush the MIC error work"); + + qdf_spin_lock_bh(&adapter->mic_work.lock); + if (adapter->mic_work.status != MIC_SCHEDULED) { + qdf_spin_unlock_bh(&adapter->mic_work.lock); + return; + } + adapter->mic_work.status = MIC_DISABLED; + qdf_spin_unlock_bh(&adapter->mic_work.lock); + + qdf_flush_work(&adapter->mic_work.work); +} + +/** + * hdd_mic_enable_work() - enable mic error work + * @adapter: Pointer to hdd adapter + * + * Return: None + */ +static void +hdd_mic_enable_work(struct hdd_adapter *adapter) +{ + hdd_debug("Enable the MIC error work"); + + qdf_spin_lock_bh(&adapter->mic_work.lock); + if (adapter->mic_work.status == MIC_DISABLED) + adapter->mic_work.status = MIC_INITIALIZED; + qdf_spin_unlock_bh(&adapter->mic_work.lock); +} + +/** + * hdd_mic_deinit_work() - deinitialize mic error work + * @hdd_adapter: Pointer to hdd adapter + * + * Return: None + */ +static void +hdd_mic_deinit_work(struct hdd_adapter *adapter) +{ + hdd_debug("DeInitialize the MIC error work"); + + if (adapter->mic_work.status != MIC_UNINITIALIZED) { + qdf_destroy_work(NULL, &adapter->mic_work.work); + + qdf_spin_lock_bh(&adapter->mic_work.lock); + adapter->mic_work.status = MIC_UNINITIALIZED; + if (adapter->mic_work.info) { + qdf_mem_free(adapter->mic_work.info); + adapter->mic_work.info = NULL; + } + qdf_spin_unlock_bh(&adapter->mic_work.lock); + qdf_spinlock_destroy(&adapter->mic_work.lock); + } +} + +/** + * hdd_process_sta_mic_error() - Indicate STA mic error to supplicant + * @adapter: Pointer to hdd adapter + * + * Return: None + */ +static void +hdd_process_sta_mic_error(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx; + struct hdd_mic_error_info *info; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (eConnectionState_Associated != + sta_ctx->conn_info.conn_state) + return; + + info = adapter->mic_work.info; + /* inform mic failure to nl80211 */ + cfg80211_michael_mic_failure(adapter->dev, + (uint8_t *)&info->ta_mac_addr, + info->multicast ? + NL80211_KEYTYPE_GROUP : + NL80211_KEYTYPE_PAIRWISE, + info->key_id, + info->tsc, + GFP_KERNEL); +} + +/** + * hdd_process_sap_mic_error() - Indicate SAP mic error to supplicant + * @adapter: Pointer to hdd adapter + * + * Return: None + */ +static void +hdd_process_sap_mic_error(struct hdd_adapter *adapter) +{ + struct hdd_mic_error_info *info; + + info = adapter->mic_work.info; + /* inform mic failure to nl80211 */ + cfg80211_michael_mic_failure(adapter->dev, + (uint8_t *)&info->ta_mac_addr, + info->multicast ? + NL80211_KEYTYPE_GROUP : + NL80211_KEYTYPE_PAIRWISE, + info->key_id, + info->tsc, + GFP_KERNEL); +} + +/** + * __hdd_process_mic_error() - Indicate mic error to supplicant + * @adapter: Pointer to hdd adapter + * + * Return: None + */ +static void +__hdd_process_mic_error(struct hdd_adapter *adapter) +{ + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + hdd_process_sta_mic_error(adapter); + } else if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + hdd_process_sap_mic_error(adapter); + } else { + hdd_err("Invalid interface type:%d", adapter->device_mode); + } +} + +/** + * hdd_process_mic_error() - process mic error work + * @data: void pointer to hdd adapter + * + * Return: None + */ +static void +hdd_process_mic_error(void *data) +{ + struct hdd_adapter *adapter = data; + struct osif_vdev_sync *vdev_sync; + + if (hdd_validate_adapter(adapter)) + goto exit; + + if (osif_vdev_sync_op_start(adapter->dev, &vdev_sync)) + goto exit; + + __hdd_process_mic_error(adapter); + + osif_vdev_sync_op_stop(vdev_sync); +exit: + qdf_spin_lock_bh(&adapter->mic_work.lock); + if (adapter->mic_work.info) { + qdf_mem_free(adapter->mic_work.info); + adapter->mic_work.info = NULL; + } + if (adapter->mic_work.status == MIC_SCHEDULED) + adapter->mic_work.status = MIC_INITIALIZED; + qdf_spin_unlock_bh(&adapter->mic_work.lock); +} + +/** + * hdd_mic_init_work() - init mic error work + * @hdd_adapter: Pointer to hdd adapter + * + * Return: None + */ +static void +hdd_mic_init_work(struct hdd_adapter *adapter) +{ + qdf_spinlock_create(&adapter->mic_work.lock); + qdf_create_work(0, &adapter->mic_work.work, + hdd_process_mic_error, adapter); + adapter->mic_work.status = MIC_INITIALIZED; + adapter->mic_work.info = NULL; +} + +void hdd_start_complete(int ret) +{ + wlan_start_ret_val = ret; + + complete(&wlan_start_comp); +} + +/** + * hdd_set_rps_cpu_mask - set RPS CPU mask for interfaces + * @hdd_ctx: pointer to struct hdd_context + * + * Return: none + */ +static void hdd_set_rps_cpu_mask(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_SET_RPS_CPU_MASK) { + hdd_send_rps_ind(adapter); + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_SET_RPS_CPU_MASK); + } +} + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +void wlan_hdd_mod_fc_timer(struct hdd_adapter *adapter, + enum netif_action_type action) +{ + if (!adapter->tx_flow_timer_initialized) + return; + + if (action == WLAN_WAKE_NON_PRIORITY_QUEUE) { + qdf_mc_timer_stop(&adapter->tx_flow_control_timer); + adapter->hdd_stats.tx_rx_stats.is_txflow_paused = false; + adapter->hdd_stats.tx_rx_stats.txflow_unpause_cnt++; + } else if (action == WLAN_STOP_NON_PRIORITY_QUEUE) { + QDF_STATUS status = + qdf_mc_timer_start(&adapter->tx_flow_control_timer, + WLAN_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); + + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to start tx_flow_control_timer"); + else + adapter-> + hdd_stats.tx_rx_stats.txflow_timer_cnt++; + + adapter->hdd_stats.tx_rx_stats.txflow_pause_cnt++; + adapter->hdd_stats.tx_rx_stats.is_txflow_paused = true; + } +} +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +void wlan_hdd_update_tcp_rx_param(struct hdd_context *hdd_ctx, void *data) +{ + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return; + } + + if (!data) { + hdd_err("Data is null"); + return; + } + if (hdd_ctx->config->enable_tcp_param_update) + wlan_hdd_send_tcp_param_update_event(hdd_ctx, data, 1); + else + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_WLAN_TP_IND, + data, + sizeof(struct wlan_rx_tp_data)); +} + +void wlan_hdd_update_tcp_tx_param(struct hdd_context *hdd_ctx, void *data) +{ + enum wlan_tp_level next_tx_level; + struct wlan_tx_tp_data *tx_tp_data; + + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return; + } + + if (!data) { + hdd_err("Data is null"); + return; + } + + tx_tp_data = (struct wlan_tx_tp_data *)data; + next_tx_level = tx_tp_data->level; + + if (hdd_ctx->config->enable_tcp_param_update) + wlan_hdd_send_tcp_param_update_event(hdd_ctx, data, 0); + else + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_WLAN_TP_TX_IND, + &next_tx_level, + sizeof(next_tx_level)); +} + +/** + * wlan_hdd_send_tcp_param_update_event() - Send vendor event to update + * TCP parameter through Wi-Fi HAL + * @hdd_ctx: Pointer to HDD context + * @data: Parameters to update + * @dir: Direction(tx/rx) to update + * + * Return: None + */ +void wlan_hdd_send_tcp_param_update_event(struct hdd_context *hdd_ctx, + void *data, + uint8_t dir) +{ + struct sk_buff *vendor_event; + uint32_t event_len; + bool tcp_limit_output = false; + bool tcp_del_ack_ind_enabled = false; + bool tcp_adv_win_scl_enabled = false; + enum wlan_tp_level next_tp_level = WLAN_SVC_TP_NONE; + + event_len = sizeof(uint8_t) + sizeof(uint8_t) + NLMSG_HDRLEN; + + if (dir == 0) /*TX Flow */ { + struct wlan_tx_tp_data *tx_tp_data = + (struct wlan_tx_tp_data *)data; + + next_tp_level = tx_tp_data->level; + + if (tx_tp_data->tcp_limit_output) { + /* TCP_LIMIT_OUTPUT_BYTES */ + event_len += sizeof(uint32_t); + tcp_limit_output = true; + } + } else if (dir == 1) /* RX Flow */ { + struct wlan_rx_tp_data *rx_tp_data = + (struct wlan_rx_tp_data *)data; + + next_tp_level = rx_tp_data->level; + + if (rx_tp_data->rx_tp_flags & TCP_DEL_ACK_IND_MASK) { + event_len += sizeof(uint32_t); /* TCP_DELACK_SEG */ + tcp_del_ack_ind_enabled = true; + } + if (rx_tp_data->rx_tp_flags & TCP_ADV_WIN_SCL_MASK) { + event_len += sizeof(uint32_t); /* TCP_ADV_WIN_SCALE */ + tcp_adv_win_scl_enabled = true; + } + } else { + hdd_err("Invalid Direction [%d]", dir); + return; + } + + vendor_event = + cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, + NULL, event_len, + QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u8( + vendor_event, + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_DIRECTION, + dir)) + goto tcp_param_change_nla_failed; + + if (nla_put_u8( + vendor_event, + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_THROUGHPUT_LEVEL, + (next_tp_level == WLAN_SVC_TP_LOW ? + QCA_WLAN_THROUGHPUT_LEVEL_LOW : + QCA_WLAN_THROUGHPUT_LEVEL_HIGH))) + goto tcp_param_change_nla_failed; + + if (tcp_limit_output && + nla_put_u32( + vendor_event, + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_LIMIT_OUTPUT_BYTES, + (next_tp_level == WLAN_SVC_TP_LOW ? + TCP_LIMIT_OUTPUT_BYTES_LOW : + TCP_LIMIT_OUTPUT_BYTES_HI))) + goto tcp_param_change_nla_failed; + + if (tcp_del_ack_ind_enabled && + (nla_put_u32( + vendor_event, + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_DELACK_SEG, + (next_tp_level == WLAN_SVC_TP_LOW ? + TCP_DEL_ACK_LOW : TCP_DEL_ACK_HI)))) + goto tcp_param_change_nla_failed; + + if (tcp_adv_win_scl_enabled && + (nla_put_u32( + vendor_event, + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_ADV_WIN_SCALE, + (next_tp_level == WLAN_SVC_TP_LOW ? + WIN_SCALE_LOW : WIN_SCALE_HI)))) + goto tcp_param_change_nla_failed; + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + return; + +tcp_param_change_nla_failed: + hdd_err("nla_put api failed"); + kfree_skb(vendor_event); +} +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + +/** + * wlan_hdd_txrx_pause_cb() - pause callback from txrx layer + * @vdev_id: vdev_id + * @action: action type + * @reason: reason type + * + * Return: none + */ +void wlan_hdd_txrx_pause_cb(uint8_t vdev_id, + enum netif_action_type action, enum netif_reason_type reason) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct hdd_adapter *adapter; + + if (!hdd_ctx) { + hdd_err("hdd ctx is NULL"); + return; + } + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + wlan_hdd_mod_fc_timer(adapter, action); + wlan_hdd_netif_queue_control(adapter, action, reason); +} + +/* + * Store WLAN driver version and timestamp info in global variables such that + * crash debugger can extract them from driver debug symbol and crashdump for + * post processing + */ +#ifdef BUILD_TAG +uint8_t g_wlan_driver_version[] = QWLAN_VERSIONSTR TIMER_MANAGER_STR MEMORY_DEBUG_STR PANIC_ON_BUG_STR "; " BUILD_TAG; +#else +uint8_t g_wlan_driver_version[] = QWLAN_VERSIONSTR TIMER_MANAGER_STR MEMORY_DEBUG_STR PANIC_ON_BUG_STR; +#endif + +int hdd_validate_channel_and_bandwidth(struct hdd_adapter *adapter, + qdf_freq_t chan_freq, + enum phy_ch_width chan_bw) +{ + mac_handle_t mac_handle; + + mac_handle = hdd_adapter_get_mac_handle(adapter); + if (!mac_handle) { + hdd_err("Invalid MAC handle"); + return -EINVAL; + } + + if (INVALID_CHANNEL == wlan_reg_get_chan_enum_for_freq(chan_freq)) { + hdd_err("Channel freq %d not in driver's valid channel list", chan_freq); + return -EOPNOTSUPP; + } + + if ((!WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq)) && + (!WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq)) && + (!WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_freq))) { + hdd_err("CH %d is not in 2.4GHz or 5GHz or 6GHz", chan_freq); + return -EINVAL; + } + + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq)) { + if (chan_bw == CH_WIDTH_80MHZ) { + hdd_err("BW80 not possible in 2.4GHz band"); + return -EINVAL; + } + if ((chan_bw != CH_WIDTH_20MHZ) && (chan_freq == 2484) && + (chan_bw != CH_WIDTH_MAX)) { + hdd_err("Only BW20 possible on channel freq 2484"); + return -EINVAL; + } + } + + if (WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq)) { + if ((chan_bw != CH_WIDTH_20MHZ) && (chan_freq == 5825) && + (chan_bw != CH_WIDTH_MAX)) { + hdd_err("Only BW20 possible on channel freq 5825"); + return -EINVAL; + } + } + + return 0; +} + +uint32_t hdd_get_adapter_home_channel(struct hdd_adapter *adapter) +{ + uint32_t home_chan_freq = 0; + struct hdd_context *hdd_ctx; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("hdd context is NULL"); + return 0; + } + + if ((adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) && + test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + home_chan_freq = adapter->session.ap.operating_chan_freq; + } else if ((adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) && + adapter->session.station.conn_info.conn_state == + eConnectionState_Associated) { + home_chan_freq = adapter->session.station.conn_info.chan_freq; + } + + return home_chan_freq; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) +static inline struct net_device *hdd_net_dev_from_notifier(void *context) +{ + struct netdev_notifier_info *info = context; + + return info->dev; +} +#else +static inline struct net_device *hdd_net_dev_from_notifier(void *context) +{ + return context; +} +#endif + +static int __hdd_netdev_notifier_call(struct net_device *net_dev, + unsigned long state) +{ + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + struct wlan_objmgr_vdev *vdev; + + hdd_enter_dev(net_dev); + + if (!net_dev->ieee80211_ptr) { + hdd_debug("ieee80211_ptr is null"); + return NOTIFY_DONE; + } + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD Context is Null"); + return NOTIFY_DONE; + } + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_debug("%s: Driver module is closed", __func__); + return NOTIFY_DONE; + } + + /* Make sure that this callback corresponds to our device. */ + adapter = hdd_get_adapter_by_iface_name(hdd_ctx, net_dev->name); + if (!adapter) { + hdd_debug("failed to look up adapter for '%s'", net_dev->name); + return NOTIFY_DONE; + } + + if (adapter != WLAN_HDD_GET_PRIV_PTR(net_dev)) { + hdd_err("HDD adapter mismatch!"); + return NOTIFY_DONE; + } + + if (cds_is_driver_recovering()) { + hdd_debug("Driver is recovering"); + return NOTIFY_DONE; + } + + if (cds_is_driver_in_bad_state()) { + hdd_debug("Driver is in failed recovery state"); + return NOTIFY_DONE; + } + + hdd_debug("%s New Net Device State = %lu", net_dev->name, state); + + switch (state) { + case NETDEV_REGISTER: + break; + + case NETDEV_UNREGISTER: + break; + + case NETDEV_UP: + sme_ch_avoid_update_req(hdd_ctx->mac_handle); + break; + + case NETDEV_DOWN: + break; + + case NETDEV_CHANGE: + if (adapter->is_link_up_service_needed) + complete(&adapter->linkup_event_var); + break; + + case NETDEV_GOING_DOWN: + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + break; + if (ucfg_scan_get_vdev_status(vdev) != + SCAN_NOT_IN_PROGRESS) { + wlan_abort_scan(hdd_ctx->pdev, INVAL_PDEV_ID, + adapter->vdev_id, INVALID_SCAN_ID, + true); + } + hdd_objmgr_put_vdev(vdev); + cds_flush_work(&adapter->scan_block_work); + /* Need to clean up blocked scan request */ + wlan_hdd_cfg80211_scan_block(adapter); + hdd_debug("Scan is not Pending from user"); + /* + * After NETDEV_GOING_DOWN, kernel calls hdd_stop.Irrespective + * of return status of hdd_stop call, kernel resets the IFF_UP + * flag after which driver does not send the cfg80211_scan_done. + * Ensure to cleanup the scan queue in NETDEV_GOING_DOWN + */ + wlan_cfg80211_cleanup_scan_queue(hdd_ctx->pdev, net_dev); + break; + case NETDEV_FEAT_CHANGE: + hdd_debug("vdev %d netdev Feature 0x%llx\n", + adapter->vdev_id, net_dev->features); + + default: + break; + } + + return NOTIFY_DONE; +} + +/** + * hdd_netdev_notifier_call() - netdev notifier callback function + * @nb: pointer to notifier block + * @state: state + * @context: notifier callback context pointer + * + * Return: 0 on success, error number otherwise. + */ +static int hdd_netdev_notifier_call(struct notifier_block *nb, + unsigned long state, + void *context) +{ + struct net_device *net_dev = hdd_net_dev_from_notifier(context); + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return NOTIFY_DONE; + + errno = __hdd_netdev_notifier_call(net_dev, state); + + osif_vdev_sync_op_stop(vdev_sync); + + return NOTIFY_DONE; +} + +struct notifier_block hdd_netdev_notifier = { + .notifier_call = hdd_netdev_notifier_call, +}; + +/* variable to hold the insmod parameters */ +static int con_mode; + +static int con_mode_ftm; +int con_mode_epping; + +/* Variable to hold connection mode including module parameter con_mode */ +static int curr_con_mode; + +/** + * hdd_map_nl_chan_width() - Map NL channel width to internal representation + * @ch_width: NL channel width + * + * Converts the NL channel width to the driver's internal representation + * + * Return: Converted channel width. In case of non matching NL channel width, + * CH_WIDTH_MAX will be returned. + */ +enum phy_ch_width hdd_map_nl_chan_width(enum nl80211_chan_width ch_width) +{ + uint8_t fw_ch_bw; + + fw_ch_bw = wma_get_vht_ch_width(); + switch (ch_width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return CH_WIDTH_20MHZ; + case NL80211_CHAN_WIDTH_40: + return CH_WIDTH_40MHZ; + case NL80211_CHAN_WIDTH_80: + return CH_WIDTH_80MHZ; + case NL80211_CHAN_WIDTH_80P80: + if (fw_ch_bw == WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ) + return CH_WIDTH_80P80MHZ; + else if (fw_ch_bw == WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) + return CH_WIDTH_160MHZ; + else + return CH_WIDTH_80MHZ; + case NL80211_CHAN_WIDTH_160: + if (fw_ch_bw >= WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) + return CH_WIDTH_160MHZ; + else + return CH_WIDTH_80MHZ; + case NL80211_CHAN_WIDTH_5: + return CH_WIDTH_5MHZ; + case NL80211_CHAN_WIDTH_10: + return CH_WIDTH_10MHZ; + default: + hdd_err("Invalid channel width %d, setting to default", + ch_width); + return CH_WIDTH_INVALID; + } +} + +#if defined(WLAN_FEATURE_NAN) && \ + (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) +/** + * wlan_hdd_convert_nan_type() - Convert nl type to qdf type + * @nl_type: NL80211 interface type + * @out_qdf_type: QDF type for the given nl_type + * + * Convert nl type to QDF type + * + * Return: QDF_STATUS_SUCCESS if converted, failure otherwise. + */ +static QDF_STATUS wlan_hdd_convert_nan_type(enum nl80211_iftype nl_type, + enum QDF_OPMODE *out_qdf_type) +{ + if (nl_type == NL80211_IFTYPE_NAN) { + *out_qdf_type = QDF_NAN_DISC_MODE; + return QDF_STATUS_SUCCESS; + } + return QDF_STATUS_E_INVAL; +} + +/** + * wlan_hdd_set_nan_if_type() - Set the NAN iftype + * @adapter: pointer to HDD adapter + * + * Set the NL80211_IFTYPE_NAN to wdev iftype. + * + * Return: None + */ +static void wlan_hdd_set_nan_if_type(struct hdd_adapter *adapter) +{ + adapter->wdev.iftype = NL80211_IFTYPE_NAN; +} + +static bool wlan_hdd_is_vdev_creation_allowed(struct wlan_objmgr_psoc *psoc) +{ + return ucfg_nan_is_vdev_creation_allowed(psoc); +} +#else +static QDF_STATUS wlan_hdd_convert_nan_type(enum nl80211_iftype nl_type, + enum QDF_OPMODE *out_qdf_type) +{ + return QDF_STATUS_E_INVAL; +} + +static void wlan_hdd_set_nan_if_type(struct hdd_adapter *adapter) +{ +} + +static bool wlan_hdd_is_vdev_creation_allowed(struct wlan_objmgr_psoc *psoc) +{ + return false; +} +#endif + +QDF_STATUS hdd_nl_to_qdf_iface_type(enum nl80211_iftype nl_type, + enum QDF_OPMODE *out_qdf_type) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + switch (nl_type) { + case NL80211_IFTYPE_ADHOC: + *out_qdf_type = QDF_IBSS_MODE; + break; + case NL80211_IFTYPE_AP: + *out_qdf_type = QDF_SAP_MODE; + break; + case NL80211_IFTYPE_MONITOR: + *out_qdf_type = QDF_MONITOR_MODE; + break; + case NL80211_IFTYPE_OCB: + *out_qdf_type = QDF_OCB_MODE; + break; + case NL80211_IFTYPE_P2P_CLIENT: + *out_qdf_type = QDF_P2P_CLIENT_MODE; + break; + case NL80211_IFTYPE_P2P_DEVICE: + *out_qdf_type = QDF_P2P_DEVICE_MODE; + break; + case NL80211_IFTYPE_P2P_GO: + *out_qdf_type = QDF_P2P_GO_MODE; + break; + case NL80211_IFTYPE_STATION: + *out_qdf_type = QDF_STA_MODE; + break; + case NL80211_IFTYPE_WDS: + *out_qdf_type = QDF_WDS_MODE; + break; + default: + status = wlan_hdd_convert_nan_type(nl_type, out_qdf_type); + if (QDF_IS_STATUS_SUCCESS(status)) + break; + hdd_err("Invalid nl80211 interface type %d", nl_type); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +uint8_t wlan_hdd_find_opclass(mac_handle_t mac_handle, uint8_t channel, + uint8_t bw_offset) +{ + uint8_t opclass = 0; + + sme_get_opclass(mac_handle, channel, bw_offset, &opclass); + return opclass; +} + +/** + * hdd_qdf_trace_enable() - configure initial QDF Trace enable + * @module_id: Module whose trace level is being configured + * @bitmask: Bitmask of log levels to be enabled + * + * Called immediately after the cfg.ini is read in order to configure + * the desired trace levels. + * + * Return: None + */ +int hdd_qdf_trace_enable(QDF_MODULE_ID module_id, uint32_t bitmask) +{ + QDF_TRACE_LEVEL level; + int qdf_print_idx = -1; + int status = -1; + /* + * if the bitmask is the default value, then a bitmask was not + * specified in cfg.ini, so leave the logging level alone (it + * will remain at the "compiled in" default value) + */ + if (CFG_QDF_TRACE_ENABLE_DEFAULT == bitmask) + return 0; + + qdf_print_idx = qdf_get_pidx(); + + /* a mask was specified. start by disabling all logging */ + status = qdf_print_set_category_verbose(qdf_print_idx, module_id, + QDF_TRACE_LEVEL_NONE, 0); + + if (QDF_STATUS_SUCCESS != status) + return -EINVAL; + /* now cycle through the bitmask until all "set" bits are serviced */ + level = QDF_TRACE_LEVEL_NONE; + while (0 != bitmask) { + if (bitmask & 1) { + status = qdf_print_set_category_verbose(qdf_print_idx, + module_id, level, 1); + if (QDF_STATUS_SUCCESS != status) + return -EINVAL; + } + + level++; + bitmask >>= 1; + } + return 0; +} + +int __wlan_hdd_validate_context(struct hdd_context *hdd_ctx, const char *func) +{ + if (!hdd_ctx) { + hdd_err("HDD context is null (via %s)", func); + return -ENODEV; + } + + if (!hdd_ctx->config) { + hdd_err("HDD config is null (via %s)", func); + return -ENODEV; + } + + if (cds_is_driver_recovering()) { + hdd_debug("Recovery in progress (via %s); state:0x%x", + func, cds_get_driver_state()); + return -EAGAIN; + } + + if (cds_is_load_or_unload_in_progress()) { + hdd_debug("Load/unload in progress (via %s); state:0x%x", + func, cds_get_driver_state()); + return -EAGAIN; + } + + if (cds_is_driver_in_bad_state()) { + hdd_debug("Driver in bad state (via %s); state:0x%x", + func, cds_get_driver_state()); + return -EAGAIN; + } + + if (cds_is_fw_down()) { + hdd_debug("FW is down (via %s); state:0x%x", + func, cds_get_driver_state()); + return -EAGAIN; + } + + return 0; +} + +int __hdd_validate_adapter(struct hdd_adapter *adapter, const char *func) +{ + if (!adapter) { + hdd_err("adapter is null (via %s)", func); + return -EINVAL; + } + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("bad adapter magic (via %s)", func); + return -EINVAL; + } + + if (!adapter->dev) { + hdd_err("adapter net_device is null (via %s)", func); + return -EINVAL; + } + + if (!(adapter->dev->flags & IFF_UP)) { + hdd_debug_rl("adapter '%s' is not up (via %s)", + adapter->dev->name, func); + return -EAGAIN; + } + + return __wlan_hdd_validate_vdev_id(adapter->vdev_id, func); +} + +int __wlan_hdd_validate_vdev_id(uint8_t vdev_id, const char *func) +{ + if (vdev_id == WLAN_UMAC_VDEV_ID_MAX) { + hdd_debug_rl("adapter is not up (via %s)", func); + return -EINVAL; + } + + if (vdev_id >= WLAN_MAX_VDEVS) { + hdd_err("bad vdev Id:%u (via %s)", vdev_id, func); + return -EINVAL; + } + + return 0; +} + +QDF_STATUS __wlan_hdd_validate_mac_address(struct qdf_mac_addr *mac_addr, + const char *func) +{ + if (!mac_addr) { + hdd_err("Received NULL mac address (via %s)", func); + return QDF_STATUS_E_INVAL; + } + + if (qdf_is_macaddr_zero(mac_addr)) { + hdd_err("MAC is all zero (via %s)", func); + return QDF_STATUS_E_INVAL; + } + + if (qdf_is_macaddr_broadcast(mac_addr)) { + hdd_err("MAC is Broadcast (via %s)", func); + return QDF_STATUS_E_INVAL; + } + + if (QDF_NET_IS_MAC_MULTICAST(mac_addr->bytes)) { + hdd_err("MAC is Multicast (via %s)", func); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wlan_hdd_validate_modules_state() - Check modules status + * @hdd_ctx: HDD context pointer + * + * Check's the driver module's state and returns true if the + * modules are enabled returns false if modules are closed. + * + * Return: True if modules are enabled or false. + */ +bool wlan_hdd_validate_modules_state(struct hdd_context *hdd_ctx) +{ + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_info("Modules not enabled, Present status: %d", + hdd_ctx->driver_status); + return false; + } + + return true; +} + +#ifdef QCA_IBSS_SUPPORT +QDF_STATUS hdd_set_ibss_power_save_params(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return QDF_STATUS_E_FAILURE; + } + + return ucfg_mlme_ibss_power_save_setup(hdd_ctx->psoc, + adapter->vdev_id); +} +#endif + +#ifdef FEATURE_RUNTIME_PM +/** + * hdd_runtime_suspend_context_init() - API to initialize HDD Runtime Contexts + * @hdd_ctx: HDD context + * + * Return: None + */ +static void hdd_runtime_suspend_context_init(struct hdd_context *hdd_ctx) +{ + struct hdd_runtime_pm_context *ctx = &hdd_ctx->runtime_context; + + qdf_runtime_lock_init(&ctx->dfs); + qdf_runtime_lock_init(&ctx->connect); + qdf_runtime_lock_init(&ctx->user); + + ctx->is_user_wakelock_acquired = false; + + wlan_scan_runtime_pm_init(hdd_ctx->pdev); +} + +/** + * hdd_runtime_suspend_context_deinit() - API to deinit HDD runtime context + * @hdd_ctx: HDD Context + * + * Return: None + */ +static void hdd_runtime_suspend_context_deinit(struct hdd_context *hdd_ctx) +{ + struct hdd_runtime_pm_context *ctx = &hdd_ctx->runtime_context; + + if (ctx->is_user_wakelock_acquired) + qdf_runtime_pm_allow_suspend(&ctx->user); + + qdf_runtime_lock_deinit(&ctx->user); + qdf_runtime_lock_deinit(&ctx->connect); + qdf_runtime_lock_deinit(&ctx->dfs); + + wlan_scan_runtime_pm_deinit(hdd_ctx->pdev); +} + +#else /* FEATURE_RUNTIME_PM */ +static void hdd_runtime_suspend_context_init(struct hdd_context *hdd_ctx) {} +static void hdd_runtime_suspend_context_deinit(struct hdd_context *hdd_ctx) {} +#endif /* FEATURE_RUNTIME_PM */ + +#define INTF_MACADDR_MASK 0x7 + +void hdd_update_macaddr(struct hdd_context *hdd_ctx, + struct qdf_mac_addr hw_macaddr, bool generate_mac_auto) +{ + int8_t i; + uint8_t macaddr_b3, tmp_br3; + + /* + * If "generate_mac_auto" is true, it indicates that all the + * addresses are derived addresses, else the first addresses + * is not derived address (It is provided by fw). + */ + if (!generate_mac_auto) { + qdf_mem_copy(hdd_ctx->provisioned_mac_addr[0].bytes, + hw_macaddr.bytes, QDF_MAC_ADDR_SIZE); + hdd_ctx->num_provisioned_addr++; + hdd_debug("hdd_ctx->provisioned_mac_addr[0]: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdd_ctx-> + provisioned_mac_addr[0].bytes)); + } else { + qdf_mem_copy(hdd_ctx->derived_mac_addr[0].bytes, + hw_macaddr.bytes, + QDF_MAC_ADDR_SIZE); + hdd_ctx->num_derived_addr++; + hdd_debug("hdd_ctx->derived_mac_addr[0]: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdd_ctx->derived_mac_addr[0].bytes)); + } + for (i = hdd_ctx->num_derived_addr; i < (QDF_MAX_CONCURRENCY_PERSONA - + hdd_ctx->num_provisioned_addr); + i++) { + qdf_mem_copy(hdd_ctx->derived_mac_addr[i].bytes, + hw_macaddr.bytes, + QDF_MAC_ADDR_SIZE); + macaddr_b3 = hdd_ctx->derived_mac_addr[i].bytes[3]; + tmp_br3 = ((macaddr_b3 >> 4 & INTF_MACADDR_MASK) + i) & + INTF_MACADDR_MASK; + macaddr_b3 += tmp_br3; + + /* XOR-ing bit-24 of the mac address. This will give enough + * mac address range before collision + */ + macaddr_b3 ^= (1 << 7); + + /* Set locally administered bit */ + hdd_ctx->derived_mac_addr[i].bytes[0] |= 0x02; + hdd_ctx->derived_mac_addr[i].bytes[3] = macaddr_b3; + hdd_debug("hdd_ctx->derived_mac_addr[%d]: " + QDF_MAC_ADDR_FMT, i, + QDF_MAC_ADDR_REF(hdd_ctx->derived_mac_addr[i].bytes)); + hdd_ctx->num_derived_addr++; + } +} + +#ifdef FEATURE_WLAN_TDLS +static int hdd_update_tdls_config(struct hdd_context *hdd_ctx) +{ + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + struct tdls_start_params tdls_cfg; + QDF_STATUS status; + struct wlan_mlme_nss_chains vdev_ini_cfg; + + /* Populate the nss chain params from ini for this vdev type */ + sme_populate_nss_chain_params(hdd_ctx->mac_handle, &vdev_ini_cfg, + QDF_TDLS_MODE, + hdd_ctx->num_rf_chains); + + cfg_tdls_set_vdev_nss_2g(hdd_ctx->psoc, + vdev_ini_cfg.rx_nss[NSS_CHAINS_BAND_2GHZ]); + cfg_tdls_set_vdev_nss_5g(hdd_ctx->psoc, + vdev_ini_cfg.rx_nss[NSS_CHAINS_BAND_5GHZ]); + hdd_init_tdls_config(&tdls_cfg); + tdls_cfg.tdls_del_all_peers = eWNI_SME_DEL_ALL_TDLS_PEERS; + tdls_cfg.tdls_update_dp_vdev_flags = CDP_UPDATE_TDLS_FLAGS; + tdls_cfg.tdls_event_cb = wlan_cfg80211_tdls_event_callback; + tdls_cfg.tdls_evt_cb_data = psoc; + tdls_cfg.tdls_peer_context = hdd_ctx; + tdls_cfg.tdls_reg_peer = hdd_tdls_register_peer; + tdls_cfg.tdls_wmm_cb = hdd_wmm_is_acm_allowed; + tdls_cfg.tdls_wmm_cb_data = psoc; + tdls_cfg.tdls_rx_cb = wlan_cfg80211_tdls_rx_callback; + tdls_cfg.tdls_rx_cb_data = psoc; + tdls_cfg.tdls_dp_vdev_update = hdd_update_dp_vdev_flags; + tdls_cfg.tdls_osif_init_cb = wlan_cfg80211_tdls_osif_priv_init; + tdls_cfg.tdls_osif_deinit_cb = wlan_cfg80211_tdls_osif_priv_deinit; + + status = ucfg_tdls_update_config(psoc, &tdls_cfg); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("failed pmo psoc configuration"); + return -EINVAL; + } + + hdd_ctx->tdls_umac_comp_active = true; + /* enable napier specific tdls data path */ + hdd_ctx->tdls_nap_active = true; + + return 0; +} +#else +static int hdd_update_tdls_config(struct hdd_context *hdd_ctx) +{ + return 0; +} +#endif + +void hdd_indicate_active_ndp_cnt(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t cnt) +{ + struct hdd_adapter *adapter = NULL; + + adapter = wlan_hdd_get_adapter_from_vdev(psoc, vdev_id); + if (adapter && cfg_nan_is_roam_config_disabled(psoc)) { + hdd_debug("vdev_id:%d%s active ndp sessions present", vdev_id, + cnt ? "" : " no more"); + if (!cnt) + wlan_hdd_enable_roaming(adapter, RSO_NDP_CON_ON_NDI); + else + wlan_hdd_disable_roaming(adapter, RSO_NDP_CON_ON_NDI); + } +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static void hdd_update_roam_offload(struct hdd_context *hdd_ctx, + struct wma_tgt_services *cfg) +{ + bool roam_offload_enable; + + ucfg_mlme_get_roaming_offload(hdd_ctx->psoc, &roam_offload_enable); + ucfg_mlme_set_roaming_offload(hdd_ctx->psoc, + roam_offload_enable & + cfg->en_roam_offload); +} +#else +static inline void hdd_update_roam_offload(struct hdd_context *hdd_ctx, + struct wma_tgt_services *cfg) +{ +} +#endif + +static void hdd_update_tgt_services(struct hdd_context *hdd_ctx, + struct wma_tgt_services *cfg) +{ + struct hdd_config *config = hdd_ctx->config; + bool arp_offload_enable; + bool mawc_enabled; +#ifdef FEATURE_WLAN_TDLS + bool tdls_support; + bool tdls_off_channel; + bool tdls_buffer_sta; + uint32_t tdls_uapsd_mask; +#endif + bool get_peer_info_enable; + + /* Set up UAPSD */ + ucfg_mlme_set_sap_uapsd_flag(hdd_ctx->psoc, cfg->uapsd); + + /* 11AX mode support */ + if ((config->dot11Mode == eHDD_DOT11_MODE_11ax || + config->dot11Mode == eHDD_DOT11_MODE_11ax_ONLY) && !cfg->en_11ax) + config->dot11Mode = eHDD_DOT11_MODE_11ac; + + /* 11AC mode support */ + if ((config->dot11Mode == eHDD_DOT11_MODE_11ac || + config->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY) && !cfg->en_11ac) + config->dot11Mode = eHDD_DOT11_MODE_AUTO; + + /* ARP offload: override user setting if invalid */ + arp_offload_enable = + ucfg_pmo_is_arp_offload_enabled(hdd_ctx->psoc); + ucfg_pmo_set_arp_offload_enabled(hdd_ctx->psoc, + arp_offload_enable & cfg->arp_offload); +#ifdef FEATURE_WLAN_SCAN_PNO + /* PNO offload */ + hdd_debug("PNO Capability in f/w = %d", cfg->pno_offload); + if (cfg->pno_offload) + ucfg_scan_set_pno_offload(hdd_ctx->psoc, true); +#endif +#ifdef FEATURE_WLAN_TDLS + cfg_tdls_get_support_enable(hdd_ctx->psoc, &tdls_support); + cfg_tdls_set_support_enable(hdd_ctx->psoc, + tdls_support & cfg->en_tdls); + + cfg_tdls_get_off_channel_enable(hdd_ctx->psoc, &tdls_off_channel); + cfg_tdls_set_off_channel_enable(hdd_ctx->psoc, + tdls_off_channel && + cfg->en_tdls_offchan); + + cfg_tdls_get_buffer_sta_enable(hdd_ctx->psoc, &tdls_buffer_sta); + cfg_tdls_set_buffer_sta_enable(hdd_ctx->psoc, + tdls_buffer_sta && + cfg->en_tdls_uapsd_buf_sta); + + cfg_tdls_get_uapsd_mask(hdd_ctx->psoc, &tdls_uapsd_mask); + if (tdls_uapsd_mask && cfg->en_tdls_uapsd_sleep_sta) + cfg_tdls_set_sleep_sta_enable(hdd_ctx->psoc, true); + else + cfg_tdls_set_sleep_sta_enable(hdd_ctx->psoc, false); +#endif + hdd_update_roam_offload(hdd_ctx, cfg); + + if (ucfg_mlme_get_sap_get_peer_info( + hdd_ctx->psoc, &get_peer_info_enable) == QDF_STATUS_SUCCESS) { + get_peer_info_enable &= cfg->get_peer_info_enabled; + ucfg_mlme_set_sap_get_peer_info(hdd_ctx->psoc, + get_peer_info_enable); + } + + ucfg_mlme_is_mawc_enabled(hdd_ctx->psoc, &mawc_enabled); + ucfg_mlme_set_mawc_enabled(hdd_ctx->psoc, + mawc_enabled & cfg->is_fw_mawc_capable); + hdd_update_tdls_config(hdd_ctx); + sme_update_tgt_services(hdd_ctx->mac_handle, cfg); + hdd_ctx->roam_ch_from_fw_supported = cfg->is_roam_scan_ch_to_host; +} + +/** + * hdd_update_vdev_nss() - sets the vdev nss + * @hdd_ctx: HDD context + * + * Sets the Nss per vdev type based on INI + * + * Return: None + */ +static void hdd_update_vdev_nss(struct hdd_context *hdd_ctx) +{ + uint8_t max_supp_nss = 1; + mac_handle_t mac_handle; + QDF_STATUS status; + bool bval; + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + if (bval && !cds_is_sub_20_mhz_enabled()) + max_supp_nss = 2; + + hdd_debug("max nss %d", max_supp_nss); + + mac_handle = hdd_ctx->mac_handle; + sme_update_vdev_type_nss(mac_handle, max_supp_nss, + NSS_CHAINS_BAND_2GHZ); + + sme_update_vdev_type_nss(mac_handle, max_supp_nss, + NSS_CHAINS_BAND_5GHZ); +} + +/** + * hdd_update_wiphy_vhtcap() - Updates wiphy vhtcap fields + * @hdd_ctx: HDD context + * + * Updates wiphy vhtcap fields + * + * Return: None + */ +static void hdd_update_wiphy_vhtcap(struct hdd_context *hdd_ctx) +{ + struct ieee80211_supported_band *band_5g = + hdd_ctx->wiphy->bands[NL80211_BAND_5GHZ]; + QDF_STATUS status; + uint8_t value = 0, value1 = 0; + uint32_t value2; + + if (!band_5g) { + hdd_debug("5GHz band disabled, skipping capability population"); + return; + } + + status = ucfg_mlme_cfg_get_vht_tx_bfee_ant_supp(hdd_ctx->psoc, + &value); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get tx_bfee_ant_supp"); + + band_5g->vht_cap.cap |= + (value << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); + + value1 = NUM_OF_SOUNDING_DIMENSIONS; + band_5g->vht_cap.cap |= + (value1 << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT); + + hdd_debug("Updated wiphy vhtcap:0x%x, CSNAntSupp:%d, NumSoundDim:%d", + band_5g->vht_cap.cap, value, value1); + + ucfg_mlme_cfg_get_vht_rx_mcs_map(hdd_ctx->psoc, &value2); + band_5g->vht_cap.vht_mcs.rx_mcs_map = value2; + + ucfg_mlme_cfg_get_vht_tx_mcs_map(hdd_ctx->psoc, &value2); + band_5g->vht_cap.vht_mcs.tx_mcs_map = value2; + +} + +static void hdd_update_tgt_ht_cap(struct hdd_context *hdd_ctx, + struct wma_tgt_ht_cap *cfg) +{ + QDF_STATUS status; + qdf_size_t value_len; + uint32_t value; + uint8_t mpdu_density; + struct mlme_ht_capabilities_info ht_cap_info; + uint8_t mcs_set[SIZE_OF_SUPPORTED_MCS_SET]; + bool b_enable1x1; + + /* get the MPDU density */ + status = ucfg_mlme_get_ht_mpdu_density(hdd_ctx->psoc, &mpdu_density); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("could not get HT MPDU Density"); + return; + } + + /* + * MPDU density: + * override user's setting if value is larger + * than the one supported by target, + * if target value is 0, then follow user's setting. + */ + if (cfg->mpdu_density && mpdu_density > cfg->mpdu_density) { + status = ucfg_mlme_set_ht_mpdu_density(hdd_ctx->psoc, + cfg->mpdu_density); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not set HT capability to CCM"); + } + + /* get the HT capability info */ + status = ucfg_mlme_get_ht_cap_info(hdd_ctx->psoc, &ht_cap_info); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("could not get HT capability info"); + return; + } + + /* check and update RX STBC */ + if (ht_cap_info.rx_stbc && !cfg->ht_rx_stbc) + ht_cap_info.rx_stbc = cfg->ht_rx_stbc; + + /* Set the LDPC capability */ + ht_cap_info.adv_coding_cap = cfg->ht_rx_ldpc; + + if (ht_cap_info.short_gi_20_mhz && !cfg->ht_sgi_20) + ht_cap_info.short_gi_20_mhz = cfg->ht_sgi_20; + + if (ht_cap_info.short_gi_40_mhz && !cfg->ht_sgi_40) + ht_cap_info.short_gi_40_mhz = cfg->ht_sgi_40; + + hdd_ctx->num_rf_chains = cfg->num_rf_chains; + hdd_ctx->ht_tx_stbc_supported = cfg->ht_tx_stbc; + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &b_enable1x1); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + b_enable1x1 = b_enable1x1 && (cfg->num_rf_chains == 2); + + status = ucfg_mlme_set_vht_enable2x2(hdd_ctx->psoc, b_enable1x1); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to set vht_enable2x2"); + + if (!b_enable1x1) + ht_cap_info.tx_stbc = 0; + + if (!(cfg->ht_tx_stbc && b_enable1x1)) + ht_cap_info.tx_stbc = 0; + + status = ucfg_mlme_set_ht_cap_info(hdd_ctx->psoc, ht_cap_info); + if (status != QDF_STATUS_SUCCESS) + hdd_err("could not set HT capability to CCM"); +#define WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES 0xff + value_len = SIZE_OF_SUPPORTED_MCS_SET; + if (ucfg_mlme_get_supported_mcs_set( + hdd_ctx->psoc, mcs_set, + &value_len) == QDF_STATUS_SUCCESS) { + hdd_debug("Read MCS rate set"); + if (cfg->num_rf_chains > SIZE_OF_SUPPORTED_MCS_SET) + cfg->num_rf_chains = SIZE_OF_SUPPORTED_MCS_SET; + + if (b_enable1x1) { + for (value = 0; value < cfg->num_rf_chains; value++) + mcs_set[value] = + WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES; + + status = ucfg_mlme_set_supported_mcs_set( + hdd_ctx->psoc, + mcs_set, + (qdf_size_t)SIZE_OF_SUPPORTED_MCS_SET); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not set MCS SET to CCM"); + } + } +#undef WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES +} + +static void hdd_update_tgt_vht_cap(struct hdd_context *hdd_ctx, + struct wma_tgt_vht_cap *cfg) +{ + QDF_STATUS status; + struct wiphy *wiphy = hdd_ctx->wiphy; + struct ieee80211_supported_band *band_5g = + wiphy->bands[HDD_NL80211_BAND_5GHZ]; + uint32_t ch_width; + struct wma_caps_per_phy caps_per_phy = {0}; + bool vht_enable_2x2; + uint32_t tx_highest_data_rate; + uint32_t rx_highest_data_rate; + + if (!band_5g) { + hdd_debug("5GHz band disabled, skipping capability population"); + return; + } + + status = ucfg_mlme_update_vht_cap(hdd_ctx->psoc, cfg); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not update vht capabilities"); + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &vht_enable_2x2); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + if (vht_enable_2x2) { + if (cfg->vht_short_gi_80 & WMI_VHT_CAP_SGI_80MHZ) { + /* Update 2x2 Highest Short GI data rate */ + tx_highest_data_rate = + VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_2_2_SGI80; + rx_highest_data_rate = + VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_2_2_SGI80; + } else { + /* Update 2x2 Rx Highest Long GI data Rate */ + tx_highest_data_rate = + VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_2_2; + rx_highest_data_rate = + VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_2_2; + } + } else if (cfg->vht_short_gi_80 & WMI_VHT_CAP_SGI_80MHZ) { + /* Update 1x1 Highest Short GI data rate */ + tx_highest_data_rate = + VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1_SGI80; + rx_highest_data_rate = + VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1_SGI80; + } else { + /* Update 1x1 Highest Long GI data rate */ + tx_highest_data_rate = VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + rx_highest_data_rate = VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + } + + status = ucfg_mlme_cfg_set_vht_rx_supp_data_rate( + hdd_ctx->psoc, + rx_highest_data_rate); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to set rx_supp_data_rate"); + + status = ucfg_mlme_cfg_set_vht_tx_supp_data_rate( + hdd_ctx->psoc, + tx_highest_data_rate); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to set tx_supp_data_rate"); + + if (WMI_VHT_CAP_MAX_MPDU_LEN_11454 == cfg->vht_max_mpdu) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + else if (WMI_VHT_CAP_MAX_MPDU_LEN_7935 == cfg->vht_max_mpdu) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + else + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; + + + if (cfg->supp_chan_width & (1 << eHT_CHANNEL_WIDTH_80P80MHZ)) { + band_5g->vht_cap.cap |= + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + ch_width = VHT_CAP_160_AND_80P80_SUPP; + } else if (cfg->supp_chan_width & (1 << eHT_CHANNEL_WIDTH_160MHZ)) { + band_5g->vht_cap.cap |= + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + ch_width = VHT_CAP_160_SUPP; + } else { + ch_width = VHT_CAP_NO_160M_SUPP; + } + + status = ucfg_mlme_set_vht_ch_width(hdd_ctx->psoc, ch_width); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("could not set the channel width"); + else + hdd_debug("supported channel width %d", ch_width); + + if (cfg->vht_rx_ldpc & WMI_VHT_CAP_RX_LDPC) { + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC; + hdd_debug("VHT RxLDPC capability is set"); + } else { + /* + * Get the RX LDPC capability for the NON DBS + * hardware mode for 5G band + */ + status = wma_get_caps_for_phyidx_hwmode(&caps_per_phy, + HW_MODE_DBS_NONE, CDS_BAND_5GHZ); + if ((QDF_IS_STATUS_SUCCESS(status)) && + (caps_per_phy.vht_5g & WMI_VHT_CAP_RX_LDPC)) { + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC; + hdd_debug("VHT RX LDPC capability is set"); + } + } + + if (cfg->vht_short_gi_80 & WMI_VHT_CAP_SGI_80MHZ) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; + if (cfg->vht_short_gi_160 & WMI_VHT_CAP_SGI_160MHZ) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; + + if (cfg->vht_tx_stbc & WMI_VHT_CAP_TX_STBC) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC; + + if (cfg->vht_rx_stbc & WMI_VHT_CAP_RX_STBC_1SS) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1; + if (cfg->vht_rx_stbc & WMI_VHT_CAP_RX_STBC_2SS) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_2; + if (cfg->vht_rx_stbc & WMI_VHT_CAP_RX_STBC_3SS) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_3; + + band_5g->vht_cap.cap |= + (cfg->vht_max_ampdu_len_exp << + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT); + + if (cfg->vht_su_bformer & WMI_VHT_CAP_SU_BFORMER) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; + if (cfg->vht_su_bformee & WMI_VHT_CAP_SU_BFORMEE) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + if (cfg->vht_mu_bformer & WMI_VHT_CAP_MU_BFORMER) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; + if (cfg->vht_mu_bformee & WMI_VHT_CAP_MU_BFORMEE) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + + if (cfg->vht_txop_ps & WMI_VHT_CAP_TXOP_PS) + band_5g->vht_cap.cap |= IEEE80211_VHT_CAP_VHT_TXOP_PS; + + band_5g->vht_cap.vht_mcs.rx_highest = cpu_to_le16(rx_highest_data_rate); + band_5g->vht_cap.vht_mcs.tx_highest = cpu_to_le16(tx_highest_data_rate); +} + +//add-begin by t2m.xiaozhan.gan,get wifi mac address +#define MAC_LEN 6 +#define MAC_STR_LEN 17 +#define MAC_CHAR_NUM 2 +#define CMDLINE "/proc/cmdline" +#define CMDLINE_LEN 1500 +static inline int is_in_range(unsigned char c, + unsigned char low, unsigned char high) +{ + if((c > high) || (c < low)){ + return 0; + }else{ + return 1; + } +} + +//convert a hex string to long integer. +static int hex_strtol(const char *p) +{ + int i; + int acc = 0; + unsigned char c; + + for(i=0; i 255) || (l < 0)){ + result = -1; + break; + } + + tmp[i] = (unsigned char)l; + + if(i == MAC_LEN - 1){ + break; //done + } + + ptr = strchr(ptr, ':'); + if(ptr == NULL){ + result = -1; + break; + } + ptr++; + } + + if(result == 0){ + memcpy((char *)wa, (char*)(&tmp), MAC_LEN); + } + + return result; +} + +static int jrd_get_mac_addr(unsigned char *eaddr) +{ + struct file *fp = NULL; + char fn[100] = {0}; + char dp[CMDLINE_LEN + 1] = {0}; + int len; + char *wifimac_pos = NULL; + char WifiMac[MAC_STR_LEN + 1] = {0}; + loff_t pos; + mm_segment_t old_fs; + //FIXME:hard code. + //Modify wifi mac address file location + strcpy(fn, CMDLINE); + fp = filp_open(fn, O_RDONLY, 0); + if(IS_ERR(fp)){ + printk(KERN_INFO "Unable to open '%s'.\n", fn); + goto err_open; + } + pos = 0; + old_fs=get_fs(); + set_fs(KERNEL_DS); + len=vfs_read(fp, dp, CMDLINE_LEN, &pos); + set_fs(old_fs); + wifimac_pos = strstr(dp, "WifiMac"); + if (!wifimac_pos) + { + printk(KERN_INFO "Can't find wifimac\n'"); + goto err_format; + } + strncpy(WifiMac, wifimac_pos+8, 17); + WifiMac[MAC_STR_LEN] = '\0'; + str2wa(WifiMac, eaddr); + filp_close(fp, NULL); + return 0; + +err_format: + filp_close(fp, NULL); +err_open: + return -1; +} +//add-end + +/** + * hdd_generate_macaddr_auto() - Auto-generate mac address + * @hdd_ctx: Pointer to the HDD context + * + * Auto-generate mac address using device serial number. + * Keep the first 3 bytes of OUI as before and replace + * the last 3 bytes with the lower 3 bytes of serial number. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int hdd_generate_macaddr_auto(struct hdd_context *hdd_ctx) +{ + unsigned int serialno = 0; + struct qdf_mac_addr mac_addr = { + {0x00, 0x0A, 0xF5, 0x00, 0x00, 0x00} + }; +//add-begin by t2m.xiaozhan.gan,get wifi mac address + int ret = 0; + unsigned char nv_mac[6]; +//add-end + serialno = pld_socinfo_get_serial_number(hdd_ctx->parent_dev); + if (serialno == 0) + return -EINVAL; + + serialno &= 0x00ffffff; + + mac_addr.bytes[3] = (serialno >> 16) & 0xff; + mac_addr.bytes[4] = (serialno >> 8) & 0xff; + mac_addr.bytes[5] = serialno & 0xff; + +//add-begin by t2m.xiaozhan.gan,get wifi mac address + ret = jrd_get_mac_addr(nv_mac); + if ((ret==-1) || (nv_mac[0]==0x00 && nv_mac[1]==0x00 && nv_mac[2]==0x00)); + else { + mac_addr.bytes[0] = nv_mac[0]; + mac_addr.bytes[1] = nv_mac[1]; + mac_addr.bytes[2] = nv_mac[2]; + mac_addr.bytes[3] = nv_mac[3]; + mac_addr.bytes[4] = nv_mac[4]; + mac_addr.bytes[5] = nv_mac[5]; + } +//add-end + + hdd_update_macaddr(hdd_ctx, mac_addr, true); + return 0; +} + +static void hdd_sar_target_config(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *cfg) +{ + hdd_ctx->sar_version = cfg->sar_version; +} + +static void hdd_update_vhtcap_2g(struct hdd_context *hdd_ctx) +{ + uint32_t chip_mode = 0; + QDF_STATUS status; + bool b2g_vht_cfg = false; + bool b2g_vht_target = false; + struct wma_caps_per_phy caps_per_phy = {0}; + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(hdd_ctx->psoc); + if (!wmi_handle) { + hdd_err("wmi handle is NULL"); + return; + } + + status = ucfg_mlme_get_vht_for_24ghz(hdd_ctx->psoc, &b2g_vht_cfg); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get 2g vht mode"); + return; + } + if (wmi_service_enabled(wmi_handle, wmi_service_ext_msg)) { + status = wma_get_caps_for_phyidx_hwmode(&caps_per_phy, + HW_MODE_DBS_NONE, + CDS_BAND_2GHZ); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get phy caps"); + return; + } + if (caps_per_phy.vht_2g) + b2g_vht_target = true; + } else { + status = wlan_reg_get_chip_mode(hdd_ctx->pdev, &chip_mode); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get chip mode"); + return; + } + b2g_vht_target = + (chip_mode & WMI_HOST_REGDMN_MODE_11AC_VHT20_2G) ? + true : false; + } + + b2g_vht_cfg = b2g_vht_cfg && b2g_vht_target; + hdd_debug("vht 2g target: %d, cfg: %d", b2g_vht_target, b2g_vht_cfg); + status = ucfg_mlme_set_vht_for_24ghz(hdd_ctx->psoc, b2g_vht_cfg); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to update 2g vht mode"); + return; + } +} + +static void hdd_extract_fw_version_info(struct hdd_context *hdd_ctx) +{ + hdd_ctx->fw_version_info.major_spid = + HDD_FW_VER_MAJOR_SPID(hdd_ctx->target_fw_version); + hdd_ctx->fw_version_info.minor_spid = + HDD_FW_VER_MINOR_SPID(hdd_ctx->target_fw_version); + hdd_ctx->fw_version_info.siid = + HDD_FW_VER_SIID(hdd_ctx->target_fw_version); + hdd_ctx->fw_version_info.crmid = + HDD_FW_VER_CRM_ID(hdd_ctx->target_fw_version); + hdd_ctx->fw_version_info.sub_id = + HDD_FW_VER_SUB_ID(hdd_ctx->target_fw_vers_ext); + hdd_ctx->fw_version_info.rel_id = + HDD_FW_VER_REL_ID(hdd_ctx->target_fw_vers_ext); +} + +#if (((defined(CONFIG_BAND_6GHZ) && defined(CFG80211_6GHZ_BAND_SUPPORTED)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))) && defined(WLAN_FEATURE_11AX)) +#if defined(CONFIG_BAND_6GHZ) && (defined(CFG80211_6GHZ_BAND_SUPPORTED) || \ + (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)) +static void +hdd_update_wiphy_he_caps_6ghz(struct hdd_context *hdd_ctx) +{ + struct ieee80211_supported_band *band_6g = + hdd_ctx->wiphy->bands[HDD_NL80211_BAND_6GHZ]; + uint8_t *phy_info = + hdd_ctx->iftype_data_6g->he_cap.he_cap_elem.phy_cap_info; + uint8_t max_fw_bw = sme_get_vht_ch_width(); + + if (!band_6g || !phy_info) { + hdd_debug("6ghz not supported in wiphy"); + return; + } + + hdd_ctx->iftype_data_6g->types_mask = + (BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP)); + hdd_ctx->iftype_data_6g->he_cap.has_he = true; + band_6g->n_iftype_data = 1; + + if (max_fw_bw >= WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) + phy_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + if (max_fw_bw >= WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) + phy_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + if (max_fw_bw >= WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ) + phy_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; + + band_6g->iftype_data = hdd_ctx->iftype_data_6g; +} +#else +static inline void +hdd_update_wiphy_he_caps_6ghz(struct hdd_context *hdd_ctx) +{ +} +#endif + +static void hdd_update_wiphy_he_cap(struct hdd_context *hdd_ctx) +{ + tDot11fIEhe_cap he_cap_cfg; + struct ieee80211_supported_band *band_2g = + hdd_ctx->wiphy->bands[HDD_NL80211_BAND_2GHZ]; + struct ieee80211_supported_band *band_5g = + hdd_ctx->wiphy->bands[HDD_NL80211_BAND_5GHZ]; + QDF_STATUS status; + uint8_t *phy_info_5g = + hdd_ctx->iftype_data_5g->he_cap.he_cap_elem.phy_cap_info; + uint8_t max_fw_bw = sme_get_vht_ch_width(); + uint32_t channel_bonding_mode_2g; + uint8_t *phy_info_2g = + hdd_ctx->iftype_data_2g->he_cap.he_cap_elem.phy_cap_info; + + status = ucfg_mlme_cfg_get_he_caps(hdd_ctx->psoc, &he_cap_cfg); + + if (QDF_IS_STATUS_ERROR(status)) + return; + + if (band_2g) { + hdd_ctx->iftype_data_2g->types_mask = + (BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP)); + hdd_ctx->iftype_data_2g->he_cap.has_he = he_cap_cfg.present; + band_2g->n_iftype_data = 1; + band_2g->iftype_data = hdd_ctx->iftype_data_2g; + + ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, + &channel_bonding_mode_2g); + if (channel_bonding_mode_2g) + phy_info_2g[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + } + if (band_5g) { + hdd_ctx->iftype_data_5g->types_mask = + (BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP)); + hdd_ctx->iftype_data_5g->he_cap.has_he = he_cap_cfg.present; + band_5g->n_iftype_data = 1; + band_5g->iftype_data = hdd_ctx->iftype_data_5g; + if (max_fw_bw >= WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) + phy_info_5g[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + if (max_fw_bw >= WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) + phy_info_5g[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + if (max_fw_bw >= WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ) + phy_info_5g[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; + } + + hdd_update_wiphy_he_caps_6ghz(hdd_ctx); +} +#else +static void hdd_update_wiphy_he_cap(struct hdd_context *hdd_ctx) +{ +} +#endif + +static void hdd_component_cfg_chan_to_freq(struct wlan_objmgr_pdev *pdev) +{ + ucfg_mlme_cfg_chan_to_freq(pdev); +} + +static uint32_t hdd_update_band_cap_from_dot11mode( + struct hdd_context *hdd_ctx, uint32_t band_capability) +{ + if (hdd_ctx->config->dot11Mode == eHDD_DOT11_MODE_AUTO) + return band_capability; + + if (hdd_ctx->config->dot11Mode == eHDD_DOT11_MODE_11b || + hdd_ctx->config->dot11Mode == eHDD_DOT11_MODE_11g || + hdd_ctx->config->dot11Mode == eHDD_DOT11_MODE_11g_ONLY || + hdd_ctx->config->dot11Mode == eHDD_DOT11_MODE_11b_ONLY) + band_capability = (band_capability & (~BIT(REG_BAND_5G))); + + if (hdd_ctx->config->dot11Mode == eHDD_DOT11_MODE_11a) + band_capability = (band_capability & (~BIT(REG_BAND_2G))); + + if (hdd_ctx->config->dot11Mode != eHDD_DOT11_MODE_11ax_ONLY && + hdd_ctx->config->dot11Mode != eHDD_DOT11_MODE_11ax) + band_capability = (band_capability & (~BIT(REG_BAND_6G))); + + qdf_debug("Update band capability %x", band_capability); + return band_capability; +} + +int hdd_update_tgt_cfg(hdd_handle_t hdd_handle, struct wma_tgt_cfg *cfg) +{ + int ret; + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + uint32_t temp_band_cap, band_capability; + struct cds_config_info *cds_cfg = cds_get_ini_config(); + uint8_t antenna_mode; + uint8_t sub_20_chan_width; + QDF_STATUS status; + mac_handle_t mac_handle; + bool bval = false; + uint8_t value = 0; + uint32_t fine_time_meas_cap = 0; + enum nss_chains_band_info band; + + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return -EINVAL; + } + ret = hdd_objmgr_create_and_store_pdev(hdd_ctx); + if (ret) { + QDF_DEBUG_PANIC("Failed to create pdev; errno:%d", ret); + return -EINVAL; + } + + hdd_debug("New pdev has been created with pdev_id = %u", + hdd_ctx->pdev->pdev_objmgr.wlan_pdev_id); + + status = dispatcher_pdev_open(hdd_ctx->pdev); + if (QDF_IS_STATUS_ERROR(status)) { + QDF_DEBUG_PANIC("dispatcher pdev open failed; status:%d", + status); + ret = qdf_status_to_os_return(status); + goto exit; + } + + status = hdd_component_pdev_open(hdd_ctx->pdev); + if (QDF_IS_STATUS_ERROR(status)) { + QDF_DEBUG_PANIC("hdd component pdev open failed; status:%d", + status); + ret = qdf_status_to_os_return(status); + goto dispatcher_close; + } + /* + * For 6GHz support this api is added to convert mlme cfgs + * channel numbers to frequency + */ + hdd_component_cfg_chan_to_freq(hdd_ctx->pdev); + + hdd_objmgr_update_tgt_max_vdev_psoc(hdd_ctx, cfg->max_intf_count); + + ucfg_ipa_set_dp_handle(hdd_ctx->psoc, + cds_get_context(QDF_MODULE_ID_SOC)); + ucfg_ipa_set_pdev_id(hdd_ctx->psoc, OL_TXRX_PDEV_ID); + ucfg_ipa_reg_sap_xmit_cb(hdd_ctx->pdev, + hdd_softap_ipa_start_xmit); + ucfg_ipa_reg_send_to_nw_cb(hdd_ctx->pdev, + hdd_ipa_send_nbuf_to_network); + + status = ucfg_mlme_get_sub_20_chan_width(hdd_ctx->psoc, + &sub_20_chan_width); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get sub_20_chan_width config"); + ret = qdf_status_to_os_return(status); + goto pdev_close; + } + + if (cds_cfg) { + if (sub_20_chan_width != + WLAN_SUB_20_CH_WIDTH_NONE && !cfg->sub_20_support) { + hdd_err("User requested sub 20 MHz channel width but unsupported by FW."); + cds_cfg->sub_20_channel_width = + WLAN_SUB_20_CH_WIDTH_NONE; + } else { + cds_cfg->sub_20_channel_width = sub_20_chan_width; + } + } + + status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_capability); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get MLME band capability"); + ret = qdf_status_to_os_return(status); + goto pdev_close; + } + + band_capability = + hdd_update_band_cap_from_dot11mode(hdd_ctx, band_capability); + + /* first store the INI band capability */ + temp_band_cap = band_capability; + + band_capability = cfg->band_cap; + hdd_ctx->is_fils_roaming_supported = + cfg->services.is_fils_roaming_supported; + + hdd_ctx->config->is_11k_offload_supported = + cfg->services.is_11k_offload_supported; + + /* + * merge the target band capability with INI setting if the merge has + * at least 1 band enabled + */ + temp_band_cap &= band_capability; + if (!temp_band_cap) + hdd_warn("ini BandCapability not supported by the target"); + else + band_capability = temp_band_cap; + + status = ucfg_mlme_set_band_capability(hdd_ctx->psoc, band_capability); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set MLME Band Capability"); + ret = qdf_status_to_os_return(status); + goto pdev_close; + } + + hdd_ctx->curr_band = band_capability; + hdd_ctx->psoc->soc_nif.user_config.band_capability = hdd_ctx->curr_band; + + status = wlan_hdd_update_wiphy_supported_band(hdd_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to update wiphy band info"); + goto pdev_close; + } + + status = ucfg_reg_set_band(hdd_ctx->pdev, band_capability); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to update regulatory band info"); + goto pdev_close; + } + + if (!cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + hdd_ctx->reg.reg_domain = cfg->reg_domain; + hdd_ctx->reg.eeprom_rd_ext = cfg->eeprom_rd_ext; + } + + /* This can be extended to other configurations like ht, vht cap... */ + + if (!qdf_is_macaddr_zero(&cfg->hw_macaddr)) + qdf_mem_copy(&hdd_ctx->hw_macaddr, &cfg->hw_macaddr, + QDF_MAC_ADDR_SIZE); + else + hdd_info("hw_mac is zero"); + + hdd_ctx->target_fw_version = cfg->target_fw_version; + hdd_ctx->target_fw_vers_ext = cfg->target_fw_vers_ext; + hdd_extract_fw_version_info(hdd_ctx); + + hdd_ctx->hw_bd_id = cfg->hw_bd_id; + qdf_mem_copy(&hdd_ctx->hw_bd_info, &cfg->hw_bd_info, + sizeof(cfg->hw_bd_info)); + + if (cfg->max_intf_count > WLAN_MAX_VDEVS) { + hdd_err("fw max vdevs (%u) > host max vdevs (%u); using %u", + cfg->max_intf_count, WLAN_MAX_VDEVS, WLAN_MAX_VDEVS); + hdd_ctx->max_intf_count = WLAN_MAX_VDEVS; + } else { + hdd_ctx->max_intf_count = cfg->max_intf_count; + } + + hdd_sar_target_config(hdd_ctx, cfg); + hdd_lpass_target_config(hdd_ctx, cfg); + + hdd_ctx->ap_arpns_support = cfg->ap_arpns_support; + + hdd_update_tgt_services(hdd_ctx, &cfg->services); + + hdd_update_tgt_ht_cap(hdd_ctx, &cfg->ht_cap); + + hdd_update_tgt_vht_cap(hdd_ctx, &cfg->vht_cap); + if (cfg->services.en_11ax && + (hdd_ctx->config->dot11Mode == eHDD_DOT11_MODE_AUTO || + hdd_ctx->config->dot11Mode == eHDD_DOT11_MODE_11ax || + hdd_ctx->config->dot11Mode == eHDD_DOT11_MODE_11ax_ONLY)) { + hdd_debug("11AX: 11ax is enabled - update HDD config"); + hdd_update_tgt_he_cap(hdd_ctx, cfg); + hdd_update_wiphy_he_cap(hdd_ctx); + } + hdd_update_tgt_twt_cap(hdd_ctx, cfg); + + for (band = NSS_CHAINS_BAND_2GHZ; band < NSS_CHAINS_BAND_MAX; band++) { + sme_modify_nss_chains_tgt_cfg(hdd_ctx->mac_handle, + QDF_STA_MODE, band); + sme_modify_nss_chains_tgt_cfg(hdd_ctx->mac_handle, + QDF_SAP_MODE, band); + sme_modify_nss_chains_tgt_cfg(hdd_ctx->mac_handle, + QDF_TDLS_MODE, band); + sme_modify_nss_chains_tgt_cfg(hdd_ctx->mac_handle, + QDF_P2P_DEVICE_MODE, + band); + sme_modify_nss_chains_tgt_cfg(hdd_ctx->mac_handle, + QDF_OCB_MODE, band); + sme_modify_nss_chains_tgt_cfg(hdd_ctx->mac_handle, + QDF_TDLS_MODE, band); + } + + hdd_update_vdev_nss(hdd_ctx); + + hdd_ctx->dynamic_nss_chains_support = + cfg->dynamic_nss_chains_support; + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, &fine_time_meas_cap); + fine_time_meas_cap &= cfg->fine_time_measurement_cap; + status = ucfg_mlme_set_fine_time_meas_cap(hdd_ctx->psoc, + fine_time_meas_cap); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed to set fine_time_meas_cap, 0x%x, ox%x", + fine_time_meas_cap, cfg->fine_time_measurement_cap); + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, + &fine_time_meas_cap); + } + + hdd_ctx->fine_time_meas_cap_target = cfg->fine_time_measurement_cap; + hdd_debug("fine_time_meas_cap: 0x%x", fine_time_meas_cap); + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + antenna_mode = (bval == 0x01) ? + HDD_ANTENNA_MODE_2X2 : HDD_ANTENNA_MODE_1X1; + hdd_update_smps_antenna_mode(hdd_ctx, antenna_mode); + hdd_debug("Init current antenna mode: %d", + hdd_ctx->current_antenna_mode); + + hdd_ctx->rcpi_enabled = cfg->rcpi_enabled; + + status = ucfg_mlme_cfg_get_vht_tx_bfee_ant_supp(hdd_ctx->psoc, + &value); + if (QDF_IS_STATUS_ERROR(status)) { + status = false; + hdd_err("set tx_bfee_ant_supp failed"); + } + + status = ucfg_mlme_set_restricted_80p80_bw_supp(hdd_ctx->psoc, + cfg->restricted_80p80_bw_supp); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set MLME restircted 80p80 BW support"); + + if ((value > MLME_VHT_CSN_BEAMFORMEE_ANT_SUPPORTED_FW_DEF) && + !cfg->tx_bfee_8ss_enabled) { + status = ucfg_mlme_cfg_set_vht_tx_bfee_ant_supp(hdd_ctx->psoc, + MLME_VHT_CSN_BEAMFORMEE_ANT_SUPPORTED_FW_DEF); + if (QDF_IS_STATUS_ERROR(status)) { + status = false; + hdd_err("set tx_bfee_ant_supp failed"); + } + } + + mac_handle = hdd_ctx->mac_handle; + + hdd_debug("txBFCsnValue %d", value); + + /* + * Update txBFCsnValue and NumSoundingDim values to vhtcap in wiphy + */ + hdd_update_wiphy_vhtcap(hdd_ctx); + + hdd_update_vhtcap_2g(hdd_ctx); + + hdd_ctx->wmi_max_len = cfg->wmi_max_len; + + wlan_config_sched_scan_plans_to_wiphy(hdd_ctx->wiphy, hdd_ctx->psoc); + /* + * This needs to be done after HDD pdev is created and stored since + * it will access the HDD pdev object lock. + */ + hdd_runtime_suspend_context_init(hdd_ctx); + + /* Configure NAN datapath features */ + hdd_nan_datapath_target_config(hdd_ctx, cfg); + ucfg_nan_set_tgt_caps(hdd_ctx->psoc, &cfg->nan_caps); + hdd_ctx->dfs_cac_offload = cfg->dfs_cac_offload; + hdd_ctx->lte_coex_ant_share = cfg->services.lte_coex_ant_share; + hdd_ctx->obss_scan_offload = cfg->services.obss_scan_offload; + status = ucfg_mlme_set_obss_detection_offload_enabled( + hdd_ctx->psoc, cfg->obss_detection_offloaded); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Couldn't pass WNI_CFG_OBSS_DETECTION_OFFLOAD to CFG"); + + status = ucfg_mlme_set_obss_color_collision_offload_enabled( + hdd_ctx->psoc, cfg->obss_color_collision_offloaded); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set WNI_CFG_OBSS_COLOR_COLLISION_OFFLOAD"); + + if (!cfg->obss_color_collision_offloaded) { + status = ucfg_mlme_set_bss_color_collision_det_sta( + hdd_ctx->psoc, + cfg->obss_color_collision_offloaded); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set CFG_BSS_CLR_COLLISION_DET_STA"); + } + + ucfg_mlme_get_bcast_twt(hdd_ctx->psoc, &bval); + if (bval) + ucfg_mlme_set_bcast_twt(hdd_ctx->psoc, cfg->bcast_twt_support); + else + hdd_debug("bcast twt is disable in ini, fw cap %d", + cfg->bcast_twt_support); + return 0; + +dispatcher_close: + dispatcher_pdev_close(hdd_ctx->pdev); +pdev_close: + hdd_component_pdev_close(hdd_ctx->pdev); +exit: + hdd_objmgr_release_and_destroy_pdev(hdd_ctx); + + return ret; +} + +bool hdd_dfs_indicate_radar(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + struct hdd_ap_ctx *ap_ctx; + bool dfs_disable_channel_switch = false; + + if (!hdd_ctx) { + hdd_info("Couldn't get hdd_ctx"); + return true; + } + + ucfg_mlme_get_dfs_disable_channel_switch(hdd_ctx->psoc, + &dfs_disable_channel_switch); + if (dfs_disable_channel_switch) { + hdd_info("skip tx block hdd_ctx=%pK, disableDFSChSwitch=%d", + hdd_ctx, dfs_disable_channel_switch); + return true; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_DFS_INDICATE_RADAR) { + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + + if ((QDF_SAP_MODE == adapter->device_mode || + QDF_P2P_GO_MODE == adapter->device_mode) && + (wlan_reg_is_passive_or_disable_for_freq(hdd_ctx->pdev, + ap_ctx->operating_chan_freq))) { + WLAN_HDD_GET_AP_CTX_PTR(adapter)->dfs_cac_block_tx = + true; + hdd_info("tx blocked for vdev: %d", + adapter->vdev_id); + if (adapter->vdev_id != WLAN_UMAC_VDEV_ID_MAX) + cdp_fc_vdev_flush( + cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id); + } + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_DFS_INDICATE_RADAR); + } + + return true; +} + +bool hdd_is_valid_mac_address(const uint8_t *mac_addr) +{ + int xdigit = 0; + int separator = 0; + + while (*mac_addr) { + if (isxdigit(*mac_addr)) { + xdigit++; + } else if (':' == *mac_addr) { + if (0 == xdigit || ((xdigit / 2) - 1) != separator) + break; + + ++separator; + } else { + /* Invalid MAC found */ + return false; + } + ++mac_addr; + } + return xdigit == 12 && (separator == 5 || separator == 0); +} + +/** + * hdd_mon_mode_ether_setup() - Update monitor mode struct net_device. + * @dev: Handle to struct net_device to be updated. + * + * Return: None + */ +static void hdd_mon_mode_ether_setup(struct net_device *dev) +{ + dev->header_ops = NULL; + dev->type = ARPHRD_IEEE80211_RADIOTAP; + dev->hard_header_len = ETH_HLEN; + dev->mtu = ETH_DATA_LEN; + dev->addr_len = ETH_ALEN; + dev->tx_queue_len = 1000; /* Ethernet wants good queues */ + dev->flags = IFF_BROADCAST|IFF_MULTICAST; + dev->priv_flags |= IFF_TX_SKB_SHARING; + + memset(dev->broadcast, 0xFF, ETH_ALEN); +} + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +/** + * hdd_mon_turn_off_ps_and_wow() - Update monitor mode struct net_device. + * @hdd_ctx: Pointer to HDD context. + * + * Return: None + */ +static void hdd_mon_turn_off_ps_and_wow(struct hdd_context *hdd_ctx) +{ + ucfg_pmo_set_power_save_mode(hdd_ctx->psoc, + PMO_PS_ADVANCED_POWER_SAVE_DISABLE); + ucfg_pmo_set_wow_enable(hdd_ctx->psoc, PMO_WOW_DISABLE_BOTH); +} + +/** + * __hdd__mon_open() - HDD Open function + * @dev: Pointer to net_device structure + * + * This is called in response to ifconfig up + * + * Return: 0 for success; non-zero for failure + */ +static int __hdd_mon_open(struct net_device *dev) +{ + int ret; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + hdd_mon_mode_ether_setup(dev); + + if (con_mode == QDF_GLOBAL_MONITOR_MODE) { + ret = hdd_trigger_psoc_idle_restart(hdd_ctx); + if (ret) { + hdd_err("Failed to start WLAN modules return"); + return ret; + } + hdd_err("hdd_wlan_start_modules() successful !"); + + if (!test_bit(SME_SESSION_OPENED, &adapter->event_flags)) { + ret = hdd_start_adapter(adapter); + if (ret) { + hdd_err("Failed to start adapter :%d", + adapter->device_mode); + return ret; + } + hdd_err("hdd_start_adapters() successful !"); + } + hdd_mon_turn_off_ps_and_wow(hdd_ctx); + set_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); + } + + ret = hdd_set_mon_rx_cb(dev); + + if (!ret) + ret = hdd_enable_monitor_mode(dev); + + if (!ret) { + hdd_set_current_throughput_level(hdd_ctx, + PLD_BUS_WIDTH_VERY_HIGH); + pld_request_bus_bandwidth(hdd_ctx->parent_dev, + PLD_BUS_WIDTH_VERY_HIGH); + } + + return ret; +} + +/** + * hdd_mon_open() - Wrapper function for __hdd_mon_open to protect it from SSR + * @dev: Pointer to net_device structure + * + * This is called in response to ifconfig up + * + * Return: 0 for success; non-zero for failure + */ +static int hdd_mon_open(struct net_device *net_dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_mon_open(net_dev); + + osif_vdev_sync_trans_stop(vdev_sync); + + return errno; +} +#endif + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * __hdd_pktcapture_open() - HDD Open function + * @dev: Pointer to net_device structure + * + * This is called in response to ifconfig up + * + * Return: 0 for success; non-zero for failure + */ +static int __hdd_pktcapture_open(struct net_device *dev) +{ + int ret; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + hdd_mon_mode_ether_setup(dev); + + ret = hdd_set_pktcapture_cb(dev, OL_TXRX_PDEV_ID); + + if (!ret) + set_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); + + return ret; +} + +/** + * hdd_pktcapture_open() - Wrapper function for hdd_pktcapture_open to + * protect it from SSR + * @dev: Pointer to net_device structure + * + * This is called in response to ifconfig up + * + * Return: 0 for success; non-zero for failure + */ +static int hdd_pktcapture_open(struct net_device *net_dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_pktcapture_open(net_dev); + + osif_vdev_sync_trans_stop(vdev_sync); + + return errno; +} +#endif + +static QDF_STATUS +wlan_hdd_update_dbs_scan_and_fw_mode_config(void) +{ + struct policy_mgr_dual_mac_config cfg = {0}; + QDF_STATUS status; + uint32_t chnl_sel_logic_conc = 0; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + uint8_t dual_mac_feature = DISABLE_DBS_CXN_AND_SCAN; + + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + /* + * ROME platform doesn't support any DBS related commands in FW, + * so if driver sends wmi command with dual_mac_config with all set to + * 0 then FW wouldn't respond back and driver would timeout on waiting + * for response. Check if FW supports DBS to eliminate ROME vs + * NON-ROME platform. + */ + if (!policy_mgr_find_if_fw_supports_dbs(hdd_ctx->psoc)) + return QDF_STATUS_SUCCESS; + + if (hdd_ctx->is_dual_mac_cfg_updated) { + hdd_debug("dual mac config has already been updated, skip"); + return QDF_STATUS_SUCCESS; + } + + cfg.scan_config = 0; + cfg.fw_mode_config = 0; + cfg.set_dual_mac_cb = policy_mgr_soc_set_dual_mac_cfg_cb; + if (policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc)) { + status = + ucfg_policy_mgr_get_chnl_select_plcy(hdd_ctx->psoc, + &chnl_sel_logic_conc); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("can't get chnl sel policy, use def"); + return status; + } + } + status = + ucfg_policy_mgr_get_dual_mac_feature(hdd_ctx->psoc, + &dual_mac_feature); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("ucfg_policy_mgr_get_dual_mac_feature failed, use def"); + return status; + } + + if (dual_mac_feature != DISABLE_DBS_CXN_AND_SCAN) { + status = policy_mgr_get_updated_scan_and_fw_mode_config( + hdd_ctx->psoc, &cfg.scan_config, + &cfg.fw_mode_config, + dual_mac_feature, + chnl_sel_logic_conc); + + if (status != QDF_STATUS_SUCCESS) { + hdd_err("wma_get_updated_scan_and_fw_mode_config failed %d", + status); + return status; + } + } + + hdd_debug("send scan_cfg: 0x%x fw_mode_cfg: 0x%x to fw", + cfg.scan_config, cfg.fw_mode_config); + + status = sme_soc_set_dual_mac_config(cfg); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("sme_soc_set_dual_mac_config failed %d", status); + return status; + } + hdd_ctx->is_dual_mac_cfg_updated = true; + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_start_adapter() - Wrapper function for device specific adapter + * @adapter: pointer to HDD adapter + * + * This function is called to start the device specific adapter for + * the mode passed in the adapter's device_mode. + * + * Return: 0 for success; non-zero for failure + */ +int hdd_start_adapter(struct hdd_adapter *adapter) +{ + + int ret; + enum QDF_OPMODE device_mode = adapter->device_mode; + + hdd_enter_dev(adapter->dev); + hdd_debug("Start_adapter for mode : %d", adapter->device_mode); + + switch (device_mode) { + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_DEVICE_MODE: + case QDF_OCB_MODE: + case QDF_STA_MODE: + case QDF_MONITOR_MODE: + case QDF_NAN_DISC_MODE: + ret = hdd_start_station_adapter(adapter); + if (ret) + goto err_start_adapter; + + hdd_nud_ignore_tracking(adapter, false); + hdd_mic_enable_work(adapter); + break; + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + ret = hdd_start_ap_adapter(adapter); + if (ret) + goto err_start_adapter; + hdd_mic_enable_work(adapter); + break; + case QDF_IBSS_MODE: + /* + * For IBSS interface is initialized as part of + * hdd_init_station_mode() + */ + goto exit_with_success; + case QDF_FTM_MODE: + /* vdevs are dynamically managed by firmware in FTM */ + hdd_register_wext(adapter->dev); + goto exit_with_success; + default: + hdd_err("Invalid session type %d", device_mode); + QDF_ASSERT(0); + goto err_start_adapter; + } + + if (hdd_set_fw_params(adapter)) + hdd_err("Failed to set the FW params for the adapter!"); + + if (adapter->vdev_id != WLAN_UMAC_VDEV_ID_MAX) { + ret = wlan_hdd_cfg80211_register_frames(adapter); + if (ret < 0) { + hdd_err("Failed to register frames - ret %d", ret); + goto err_start_adapter; + } + } + + wlan_hdd_update_dbs_scan_and_fw_mode_config(); + +exit_with_success: + hdd_exit(); + + return 0; + +err_start_adapter: + return -EINVAL; +} + +/** + * hdd_enable_power_management() - API to Enable Power Management + * + * API invokes Bus Interface Layer power management functionality + * + * Return: None + */ +static void hdd_enable_power_management(void) +{ + void *hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + + if (!hif_ctx) { + hdd_err("Bus Interface Context is Invalid"); + return; + } + + hif_enable_power_management(hif_ctx, cds_is_packet_log_enabled()); +} + +/** + * hdd_disable_power_management() - API to disable Power Management + * + * API disable Bus Interface Layer Power management functionality + * + * Return: None + */ +static void hdd_disable_power_management(void) +{ + void *hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + + if (!hif_ctx) { + hdd_err("Bus Interface Context is Invalid"); + return; + } + + hif_disable_power_management(hif_ctx); +} + +void hdd_update_hw_sw_info(struct hdd_context *hdd_ctx) +{ + void *hif_sc; + size_t target_hw_name_len; + const char *target_hw_name; + uint8_t *buf; + uint32_t buf_len; + + hif_sc = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_sc) { + hdd_err("HIF context is NULL"); + return; + } + + hif_get_hw_info(hif_sc, &hdd_ctx->target_hw_version, + &hdd_ctx->target_hw_revision, + &target_hw_name); + + if (hdd_ctx->target_hw_name) + qdf_mem_free(hdd_ctx->target_hw_name); + + target_hw_name_len = strlen(target_hw_name) + 1; + hdd_ctx->target_hw_name = qdf_mem_malloc(target_hw_name_len); + if (hdd_ctx->target_hw_name) + qdf_mem_copy(hdd_ctx->target_hw_name, target_hw_name, + target_hw_name_len); + + buf = qdf_mem_malloc(WE_MAX_STR_LEN); + if (buf) { + buf_len = hdd_wlan_get_version(hdd_ctx, WE_MAX_STR_LEN, buf); + hdd_nofl_debug("%s", buf); + qdf_mem_free(buf); + } +} + +/** + * hdd_update_cds_ac_specs_params() - update cds ac_specs params + * @hdd_ctx: Pointer to hdd context + * + * Return: none + */ +static void +hdd_update_cds_ac_specs_params(struct hdd_context *hdd_ctx) +{ + uint8_t tx_sched_wrr_param[TX_SCHED_WRR_PARAMS_NUM] = {0}; + qdf_size_t out_size = 0; + int i; + struct cds_context *cds_ctx; + + if (!hdd_ctx) + return; + + if (!hdd_ctx->config) { + /* Do nothing if hdd_ctx is invalid */ + hdd_err("%s: Warning: hdd_ctx->cfg_ini is NULL", __func__); + return; + } + + cds_ctx = cds_get_context(QDF_MODULE_ID_QDF); + + if (!cds_ctx) { + hdd_err("Invalid CDS Context"); + return; + } + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + switch (i) { + case QCA_WLAN_AC_BE: + qdf_uint8_array_parse( + cfg_get(hdd_ctx->psoc, + CFG_DP_ENABLE_TX_SCHED_WRR_BE), + tx_sched_wrr_param, + sizeof(tx_sched_wrr_param), + &out_size); + break; + case QCA_WLAN_AC_BK: + qdf_uint8_array_parse( + cfg_get(hdd_ctx->psoc, + CFG_DP_ENABLE_TX_SCHED_WRR_BK), + tx_sched_wrr_param, + sizeof(tx_sched_wrr_param), + &out_size); + break; + case QCA_WLAN_AC_VI: + qdf_uint8_array_parse( + cfg_get(hdd_ctx->psoc, + CFG_DP_ENABLE_TX_SCHED_WRR_VI), + tx_sched_wrr_param, + sizeof(tx_sched_wrr_param), + &out_size); + break; + case QCA_WLAN_AC_VO: + qdf_uint8_array_parse( + cfg_get(hdd_ctx->psoc, + CFG_DP_ENABLE_TX_SCHED_WRR_VO), + tx_sched_wrr_param, + sizeof(tx_sched_wrr_param), + &out_size); + break; + default: + break; + } + + if (out_size == TX_SCHED_WRR_PARAMS_NUM) { + cds_ctx->ac_specs[i].wrr_skip_weight = + tx_sched_wrr_param[0]; + cds_ctx->ac_specs[i].credit_threshold = + tx_sched_wrr_param[1]; + cds_ctx->ac_specs[i].send_limit = + tx_sched_wrr_param[2]; + cds_ctx->ac_specs[i].credit_reserve = + tx_sched_wrr_param[3]; + cds_ctx->ac_specs[i].discard_weight = + tx_sched_wrr_param[4]; + } + + out_size = 0; + } +} + +uint32_t hdd_wlan_get_version(struct hdd_context *hdd_ctx, + const size_t version_len, uint8_t *version) +{ + uint32_t size; + uint8_t reg_major = 0, reg_minor = 0, bdf_major = 0, bdf_minor = 0; + struct target_psoc_info *tgt_hdl; + + if (!hdd_ctx) { + hdd_err("Invalid context, HDD context is null"); + return 0; + } + + if (!version || version_len == 0) { + hdd_err("Invalid buffer pointr or buffer len\n"); + return 0; + } + tgt_hdl = wlan_psoc_get_tgt_if_handle(hdd_ctx->psoc); + if (tgt_hdl) + target_psoc_get_version_info(tgt_hdl, ®_major, ®_minor, + &bdf_major, &bdf_minor); + + size = scnprintf(version, version_len, + "Host SW:%s, FW:%d.%d.%d.%d.%d.%d, HW:%s, Board ver: %x Ref design id: %x, Customer id: %x, Project id: %x, Board Data Rev: %x, REG DB: %u:%u, BDF REG DB: %u:%u", + QWLAN_VERSIONSTR, + hdd_ctx->fw_version_info.major_spid, + hdd_ctx->fw_version_info.minor_spid, + hdd_ctx->fw_version_info.siid, + hdd_ctx->fw_version_info.rel_id, + hdd_ctx->fw_version_info.crmid, + hdd_ctx->fw_version_info.sub_id, + hdd_ctx->target_hw_name, + hdd_ctx->hw_bd_info.bdf_version, + hdd_ctx->hw_bd_info.ref_design_id, + hdd_ctx->hw_bd_info.customer_id, + hdd_ctx->hw_bd_info.project_id, + hdd_ctx->hw_bd_info.board_data_rev, + reg_major, reg_minor, bdf_major, bdf_minor); + + return size; +} + +int hdd_set_11ax_rate(struct hdd_adapter *adapter, int set_value, + struct sap_config *sap_config) +{ + uint8_t preamble = 0, nss = 0, rix = 0; + int ret; + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + + if (!sap_config) { + if (!sme_is_feature_supported_by_fw(DOT11AX)) { + hdd_err("Target does not support 11ax"); + return -EIO; + } + } else if (sap_config->SapHw_mode != eCSR_DOT11_MODE_11ax && + sap_config->SapHw_mode != eCSR_DOT11_MODE_11ax_ONLY) { + hdd_err("Invalid hw mode, SAP hw_mode= 0x%x, ch_freq = %d", + sap_config->SapHw_mode, sap_config->chan_freq); + return -EIO; + } + + if (set_value != 0xff) { + rix = RC_2_RATE_IDX_11AX(set_value); + preamble = WMI_RATE_PREAMBLE_HE; + nss = HT_RC_2_STREAMS_11AX(set_value); + + set_value = hdd_assemble_rate_code(preamble, nss, rix); + } else { + ret = sme_set_auto_rate_he_ltf(mac_handle, adapter->vdev_id, + QCA_WLAN_HE_LTF_AUTO); + } + + hdd_info("SET_11AX_RATE val %d rix %d preamble %x nss %d", + set_value, rix, preamble, nss); + + ret = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_FIXED_RATE, + set_value, VDEV_CMD); + + return ret; +} + +int hdd_assemble_rate_code(uint8_t preamble, uint8_t nss, uint8_t rate) +{ + int set_value; + + if (sme_is_feature_supported_by_fw(DOT11AX)) + set_value = WMI_ASSEMBLE_RATECODE_V1(rate, nss, preamble); + else + set_value = (preamble << 6) | (nss << 4) | rate; + + return set_value; +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +static enum policy_mgr_con_mode wlan_hdd_get_mode_for_non_connected_vdev( + struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) +{ + struct hdd_adapter *adapter = NULL; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("Adapter is NULL"); + return PM_MAX_NUM_OF_MODE; + } + + return policy_mgr_convert_device_mode_to_qdf_type( + adapter->device_mode); +} + +/** + * hdd_is_chan_switch_in_progress() - Check if any adapter has channel switch in + * progress + * + * Return: true, if any adapter has channel switch in + * progress else false + */ +static bool hdd_is_chan_switch_in_progress(void) +{ + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_IS_CHAN_SWITCH_IN_PROGRESS; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if ((adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) && + qdf_atomic_read(&adapter->ch_switch_in_progress)) { + hdd_debug("channel switch progress for vdev_id %d", + adapter->vdev_id); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return true; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return false; +} + +/** + * hdd_is_cac_in_progress() - Check if any SAP connection is performing + * CAC on DFS channel + * + * Return: true, if any of existing SAP is performing CAC + * or else false + */ +static bool hdd_is_cac_in_progress(void) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) + return false; + + return (hdd_ctx->dev_dfs_cac_status == DFS_CAC_IN_PROGRESS); +} + +static void hdd_register_policy_manager_callback( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_hdd_cbacks hdd_cbacks; + + qdf_mem_zero(&hdd_cbacks, sizeof(hdd_cbacks)); + hdd_cbacks.sap_restart_chan_switch_cb = + hdd_sap_restart_chan_switch_cb; + hdd_cbacks.wlan_hdd_get_channel_for_sap_restart = + wlan_hdd_get_channel_for_sap_restart; + hdd_cbacks.get_mode_for_non_connected_vdev = + wlan_hdd_get_mode_for_non_connected_vdev; + hdd_cbacks.hdd_get_device_mode = hdd_get_device_mode; + hdd_cbacks.hdd_is_chan_switch_in_progress = + hdd_is_chan_switch_in_progress; + hdd_cbacks.hdd_is_cac_in_progress = + hdd_is_cac_in_progress; + hdd_cbacks.wlan_hdd_set_sap_csa_reason = + wlan_hdd_set_sap_csa_reason; + hdd_cbacks.hdd_get_ap_6ghz_capable = hdd_get_ap_6ghz_capable; + hdd_cbacks.wlan_hdd_indicate_active_ndp_cnt = + hdd_indicate_active_ndp_cnt; + + if (QDF_STATUS_SUCCESS != + policy_mgr_register_hdd_cb(psoc, &hdd_cbacks)) { + hdd_err("HDD callback registration with policy manager failed"); + } +} +#else +static void hdd_register_policy_manager_callback( + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifdef WLAN_FEATURE_NAN +static void hdd_nan_register_callbacks(struct hdd_context *hdd_ctx) +{ + struct nan_callbacks cb_obj = {0}; + + cb_obj.ndi_open = hdd_ndi_open; + cb_obj.ndi_close = hdd_ndi_close; + cb_obj.ndi_start = hdd_ndi_start; + cb_obj.ndi_delete = hdd_ndi_delete; + cb_obj.drv_ndi_create_rsp_handler = hdd_ndi_drv_ndi_create_rsp_handler; + cb_obj.drv_ndi_delete_rsp_handler = hdd_ndi_drv_ndi_delete_rsp_handler; + + cb_obj.new_peer_ind = hdd_ndp_new_peer_handler; + cb_obj.peer_departed_ind = hdd_ndp_peer_departed_handler; + + os_if_nan_register_hdd_callbacks(hdd_ctx->psoc, &cb_obj); +} +#else +static inline void hdd_nan_register_callbacks(struct hdd_context *hdd_ctx) +{ +} +#endif + +#ifdef CONFIG_LEAK_DETECTION +/** + * hdd_check_for_leaks() - Perform runtime memory leak checks + * @hdd_ctx: the global HDD context + * @is_ssr: true if SSR is in progress + * + * This API triggers runtime memory leak detection. This feature enforces the + * policy that any memory allocated at runtime must also be released at runtime. + * + * Allocating memory at runtime and releasing it at unload is effectively a + * memory leak for configurations which never unload (e.g. LONU, statically + * compiled driver). Such memory leaks are NOT false positives, and must be + * fixed. + * + * Return: None + */ +static void hdd_check_for_leaks(struct hdd_context *hdd_ctx, bool is_ssr) +{ + /* DO NOT REMOVE these checks; for false positives, read above first */ + + wlan_objmgr_psoc_check_for_leaks(hdd_ctx->psoc); + + /* many adapter resources are not freed by design during SSR */ + if (is_ssr) + return; + + qdf_delayed_work_check_for_leaks(); + qdf_mc_timer_check_for_leaks(); + qdf_nbuf_map_check_for_leaks(); + qdf_periodic_work_check_for_leaks(); + qdf_mem_check_for_leaks(); +} + +#define hdd_debug_domain_set(domain) qdf_debug_domain_set(domain) +#else +static inline void hdd_check_for_leaks(struct hdd_context *hdd_ctx, bool is_ssr) +{ } + +#define hdd_debug_domain_set(domain) +#endif /* CONFIG_LEAK_DETECTION */ + +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE +/** + * hdd_skip_acs_scan_timer_handler() - skip ACS scan timer timeout handler + * @data: pointer to struct hdd_context + * + * This function will reset acs_scan_status to eSAP_DO_NEW_ACS_SCAN. + * Then new ACS request will do a fresh scan without reusing the cached + * scan information. + * + * Return: void + */ +static void hdd_skip_acs_scan_timer_handler(void *data) +{ + struct hdd_context *hdd_ctx = data; + mac_handle_t mac_handle; + + hdd_debug("ACS Scan result expired. Reset ACS scan skip"); + hdd_ctx->skip_acs_scan_status = eSAP_DO_NEW_ACS_SCAN; + qdf_spin_lock(&hdd_ctx->acs_skip_lock); + qdf_mem_free(hdd_ctx->last_acs_freq_list); + hdd_ctx->last_acs_freq_list = NULL; + hdd_ctx->num_of_channels = 0; + qdf_spin_unlock(&hdd_ctx->acs_skip_lock); + + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) + return; +} + +static void hdd_skip_acs_scan_timer_init(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + status = qdf_mc_timer_init(&hdd_ctx->skip_acs_scan_timer, + QDF_TIMER_TYPE_SW, + hdd_skip_acs_scan_timer_handler, + hdd_ctx); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to init ACS Skip timer"); + qdf_spinlock_create(&hdd_ctx->acs_skip_lock); +} + +static void hdd_skip_acs_scan_timer_deinit(struct hdd_context *hdd_ctx) +{ + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&hdd_ctx->skip_acs_scan_timer)) { + qdf_mc_timer_stop(&hdd_ctx->skip_acs_scan_timer); + } + + if (!QDF_IS_STATUS_SUCCESS + (qdf_mc_timer_destroy(&hdd_ctx->skip_acs_scan_timer))) { + hdd_err("Cannot deallocate ACS Skip timer"); + } + qdf_spin_lock(&hdd_ctx->acs_skip_lock); + qdf_mem_free(hdd_ctx->last_acs_freq_list); + hdd_ctx->last_acs_freq_list = NULL; + hdd_ctx->num_of_channels = 0; + qdf_spin_unlock(&hdd_ctx->acs_skip_lock); +} +#else +static void hdd_skip_acs_scan_timer_init(struct hdd_context *hdd_ctx) {} +static void hdd_skip_acs_scan_timer_deinit(struct hdd_context *hdd_ctx) {} +#endif + +/** + * hdd_update_country_code - Update country code + * @hdd_ctx: HDD context + * + * Update country code based on module parameter country_code + * + * Return: 0 on success and errno on failure + */ +static int hdd_update_country_code(struct hdd_context *hdd_ctx) +{ + if (!country_code) + return 0; + + return hdd_reg_set_country(hdd_ctx, country_code); +} + +/** + * wlan_hdd_init_tx_rx_histogram() - init tx/rx histogram stats + * @hdd_ctx: hdd context + * + * Return: 0 for success or error code + */ +static int wlan_hdd_init_tx_rx_histogram(struct hdd_context *hdd_ctx) +{ + hdd_ctx->hdd_txrx_hist = qdf_mem_malloc( + (sizeof(struct hdd_tx_rx_histogram) * NUM_TX_RX_HISTOGRAM)); + if (!hdd_ctx->hdd_txrx_hist) { + hdd_err("failed to alloc memory"); + return -ENOMEM; + } + return 0; +} + +/** + * wlan_hdd_deinit_tx_rx_histogram() - deinit tx/rx histogram stats + * @hdd_ctx: hdd context + * + * Return: none + */ +static void wlan_hdd_deinit_tx_rx_histogram(struct hdd_context *hdd_ctx) +{ + if (!hdd_ctx || !hdd_ctx->hdd_txrx_hist) + return; + + qdf_mem_free(hdd_ctx->hdd_txrx_hist); + hdd_ctx->hdd_txrx_hist = NULL; +} + +#ifdef WLAN_NS_OFFLOAD +/** + * hdd_wlan_unregister_ip6_notifier() - unregister IPv6 change notifier + * @hdd_ctx: Pointer to hdd context + * + * Unregister for IPv6 address change notifications. + * + * Return: None + */ +static void hdd_wlan_unregister_ip6_notifier(struct hdd_context *hdd_ctx) +{ + unregister_inet6addr_notifier(&hdd_ctx->ipv6_notifier); +} + +/** + * hdd_wlan_register_ip6_notifier() - register IPv6 change notifier + * @hdd_ctx: Pointer to hdd context + * + * Register for IPv6 address change notifications. + * + * Return: 0 on success and errno on failure. + */ +static int hdd_wlan_register_ip6_notifier(struct hdd_context *hdd_ctx) +{ + int ret; + + hdd_ctx->ipv6_notifier.notifier_call = wlan_hdd_ipv6_changed; + ret = register_inet6addr_notifier(&hdd_ctx->ipv6_notifier); + if (ret) { + hdd_err("Failed to register IPv6 notifier: %d", ret); + goto out; + } + + hdd_debug("Registered IPv6 notifier"); +out: + return ret; +} +#else +/** + * hdd_wlan_unregister_ip6_notifier() - unregister IPv6 change notifier + * @hdd_ctx: Pointer to hdd context + * + * Unregister for IPv6 address change notifications. + * + * Return: None + */ +static void hdd_wlan_unregister_ip6_notifier(struct hdd_context *hdd_ctx) +{ +} + +/** + * hdd_wlan_register_ip6_notifier() - register IPv6 change notifier + * @hdd_ctx: Pointer to hdd context + * + * Register for IPv6 address change notifications. + * + * Return: None + */ +static int hdd_wlan_register_ip6_notifier(struct hdd_context *hdd_ctx) +{ + return 0; +} +#endif + +#ifdef FEATURE_RUNTIME_PM +/** + * hdd_wlan_register_pm_qos_notifier() - register PM QOS notifier + * @hdd_ctx: Pointer to hdd context + * + * Register for PM QOS change notifications. + * + * Return: None + */ +static int hdd_wlan_register_pm_qos_notifier(struct hdd_context *hdd_ctx) +{ + int ret; + + /* if gRuntimePM is 1 then feature is enabled without CXPC */ + if (hdd_ctx->config->runtime_pm != hdd_runtime_pm_dynamic) { + hdd_debug("Dynamic Runtime PM disabled"); + return 0; + } + + qdf_spinlock_create(&hdd_ctx->pm_qos_lock); + hdd_ctx->pm_qos_notifier.notifier_call = wlan_hdd_pm_qos_notify; + ret = pm_qos_add_notifier(PM_QOS_CPU_DMA_LATENCY, + &hdd_ctx->pm_qos_notifier); + if (ret) + hdd_err("Failed to register PM_QOS notifier: %d", ret); + else + hdd_debug("PM QOS Notifier registered"); + + return ret; +} + +/** + * hdd_wlan_unregister_pm_qos_notifier() - unregister PM QOS notifier + * @hdd_ctx: Pointer to hdd context + * + * Unregister for PM QOS change notifications. + * + * Return: None + */ +static void hdd_wlan_unregister_pm_qos_notifier(struct hdd_context *hdd_ctx) +{ + int ret; + + if (hdd_ctx->config->runtime_pm != hdd_runtime_pm_dynamic) { + hdd_debug("Dynamic Runtime PM disabled"); + return; + } + + ret = pm_qos_remove_notifier(PM_QOS_CPU_DMA_LATENCY, + &hdd_ctx->pm_qos_notifier); + if (ret) + hdd_warn("Failed to remove qos notifier, err = %d\n", ret); + + qdf_spin_lock_irqsave(&hdd_ctx->pm_qos_lock); + + if (hdd_ctx->runtime_pm_prevented) { + pm_runtime_put_noidle(hdd_ctx->parent_dev); + hdd_ctx->runtime_pm_prevented = false; + } + + qdf_spin_unlock_irqrestore(&hdd_ctx->pm_qos_lock); + + qdf_spinlock_destroy(&hdd_ctx->pm_qos_lock); +} +#else +static int hdd_wlan_register_pm_qos_notifier(struct hdd_context *hdd_ctx) +{ + return 0; +} + +static void hdd_wlan_unregister_pm_qos_notifier(struct hdd_context *hdd_ctx) +{ +} +#endif + +/** + * hdd_register_notifiers - Register netdev notifiers. + * @hdd_ctx: HDD context + * + * Register netdev notifiers like IPv4 and IPv6. + * + * Return: 0 on success and errno on failure + */ +static int hdd_register_notifiers(struct hdd_context *hdd_ctx) +{ + int ret; + + ret = hdd_wlan_register_ip6_notifier(hdd_ctx); + if (ret) + goto out; + + hdd_ctx->ipv4_notifier.notifier_call = wlan_hdd_ipv4_changed; + ret = register_inetaddr_notifier(&hdd_ctx->ipv4_notifier); + if (ret) { + hdd_err("Failed to register IPv4 notifier: %d", ret); + goto unregister_ip6_notifier; + } + + ret = hdd_nud_register_netevent_notifier(hdd_ctx); + if (ret) { + hdd_err("Failed to register netevent notifier: %d", + ret); + goto unregister_inetaddr_notifier; + } + + ret = hdd_wlan_register_pm_qos_notifier(hdd_ctx); + if (ret) + goto unregister_nud_notifier; + + return 0; + +unregister_nud_notifier: + hdd_nud_unregister_netevent_notifier(hdd_ctx); +unregister_inetaddr_notifier: + unregister_inetaddr_notifier(&hdd_ctx->ipv4_notifier); +unregister_ip6_notifier: + hdd_wlan_unregister_ip6_notifier(hdd_ctx); +out: + return ret; +} + +#ifdef WLAN_FEATURE_WMI_SEND_RECV_QMI +static inline +void hdd_set_qmi_stats_enabled(struct hdd_context *hdd_ctx) +{ + wmi_set_qmi_stats(get_wmi_unified_hdl_from_psoc(hdd_ctx->psoc), + hdd_ctx->config->is_qmi_stats_enabled); +} +#else +static inline +void hdd_set_qmi_stats_enabled(struct hdd_context *hdd_ctx) +{ +} +#endif + +int hdd_wlan_start_modules(struct hdd_context *hdd_ctx, bool reinit) +{ + int ret = 0; + qdf_device_t qdf_dev; + QDF_STATUS status; + bool unint = false; + void *hif_ctx; + struct target_psoc_info *tgt_hdl; + + qdf_dev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_dev) { + hdd_err("QDF Device Context is Invalid return"); + return -EINVAL; + } + + hdd_psoc_idle_timer_stop(hdd_ctx); + + if (hdd_ctx->driver_status == DRIVER_MODULES_ENABLED) { + hdd_debug("Driver modules already Enabled"); + hdd_exit(); + return 0; + } + + switch (hdd_ctx->driver_status) { + case DRIVER_MODULES_UNINITIALIZED: + hdd_nofl_debug("Wlan transitioning (UNINITIALIZED -> CLOSED)"); + unint = true; + /* Fall through dont add break here */ + case DRIVER_MODULES_CLOSED: + hdd_nofl_debug("Wlan transitioning (CLOSED -> ENABLED)"); + + hdd_debug_domain_set(QDF_DEBUG_DOMAIN_ACTIVE); + + if (!reinit && !unint) { + ret = pld_power_on(qdf_dev->dev); + if (ret) { + hdd_err("Failed to power up device; errno:%d", + ret); + goto release_lock; + } + } + + hdd_bus_bandwidth_init(hdd_ctx); + hdd_init_adapter_ops_wq(hdd_ctx); + pld_set_fw_log_mode(hdd_ctx->parent_dev, + hdd_ctx->config->enable_fw_log); + ret = hdd_hif_open(qdf_dev->dev, qdf_dev->drv_hdl, qdf_dev->bid, + qdf_dev->bus_type, + (reinit == true) ? HIF_ENABLE_TYPE_REINIT : + HIF_ENABLE_TYPE_PROBE); + if (ret) { + hdd_err("Failed to open hif; errno: %d", ret); + goto power_down; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("hif context is null!!"); + ret = -EINVAL; + goto power_down; + } + + status = ol_cds_init(qdf_dev, hif_ctx); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("No Memory to Create BMI Context; status: %d", + status); + ret = qdf_status_to_os_return(status); + goto hif_close; + } + + if (hdd_get_conparam() == QDF_GLOBAL_EPPING_MODE) { + status = epping_open(); + if (status) { + hdd_err("Failed to open in epping mode: %d", + status); + ret = -EINVAL; + goto cds_free; + } + + status = epping_enable(qdf_dev->dev, false); + if (status) { + hdd_err("Failed to enable in epping mode : %d", + status); + epping_close(); + goto cds_free; + } + + hdd_info("epping mode enabled"); + break; + } + + ucfg_ipa_component_config_update(hdd_ctx->psoc); + + hdd_update_cds_ac_specs_params(hdd_ctx); + + status = hdd_component_psoc_open(hdd_ctx->psoc); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to Open legacy components; status: %d", + status); + ret = qdf_status_to_os_return(status); + goto cds_free; + } + + ret = hdd_update_config(hdd_ctx); + if (ret) { + hdd_err("Failed to update configuration; errno: %d", + ret); + goto cds_free; + } + + status = wbuff_module_init(); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("WBUFF init unsuccessful; status: %d", status); + + status = cds_open(hdd_ctx->psoc); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to Open CDS; status: %d", status); + ret = qdf_status_to_os_return(status); + goto psoc_close; + } + + hdd_set_qmi_stats_enabled(hdd_ctx); + + hdd_ctx->mac_handle = cds_get_context(QDF_MODULE_ID_SME); + + if (hdd_ctx->config->rx_thread_affinity_mask) + cds_set_rx_thread_cpu_mask( + hdd_ctx->config->rx_thread_affinity_mask); + + if (hdd_ctx->config->rx_thread_ul_affinity_mask) + cds_set_rx_thread_ul_cpu_mask( + hdd_ctx->config->rx_thread_ul_affinity_mask); + + /* initialize components configurations after psoc open */ + ret = hdd_update_components_config(hdd_ctx); + if (ret) { + hdd_err("Failed to update component configs; errno: %d", + ret); + goto close; + } + + status = cds_dp_open(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to Open cds post open; status: %d", + status); + ret = qdf_status_to_os_return(status); + goto close; + } + /* Set IRQ affinity for WLAN DP and CE IRQS */ + hif_config_irq_set_perf_affinity_hint(hif_ctx); + + ret = hdd_register_cb(hdd_ctx); + if (ret) { + hdd_err("Failed to register HDD callbacks!"); + goto cds_txrx_free; + } + + ret = hdd_register_notifiers(hdd_ctx); + if (ret) + goto deregister_cb; + + /* + * NAN compoenet requires certian operations like, open adapter, + * close adapter, etc. to be initiated by HDD, for those + * register HDD callbacks with UMAC's NAN componenet. + */ + hdd_nan_register_callbacks(hdd_ctx); + + status = cds_pre_enable(); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to pre-enable CDS; status: %d", status); + ret = qdf_status_to_os_return(status); + goto unregister_notifiers; + } + + hdd_register_policy_manager_callback( + hdd_ctx->psoc); + + /* + * Call this function before hdd_enable_power_management. Since + * it is required to trigger WMI_PDEV_DMA_RING_CFG_REQ_CMDID + * to FW when power save isn't enable. + */ + hdd_spectral_register_to_dbr(hdd_ctx); + + hdd_sysfs_create_driver_root_obj(); + hdd_sysfs_create_version_interface(hdd_ctx->psoc); + hdd_sysfs_create_powerstats_interface(); + hdd_sysfs_dp_aggregation_create(); + hdd_update_hw_sw_info(hdd_ctx); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_enable_power_management(); + hdd_err("in ftm mode, no need to configure cds modules"); + ret = -EINVAL; + break; + } + + ret = hdd_configure_cds(hdd_ctx); + if (ret) { + hdd_err("Failed to Enable cds modules; errno: %d", ret); + goto destroy_driver_sysfs; + } + + hdd_enable_power_management(); + + hdd_skip_acs_scan_timer_init(hdd_ctx); + + wlan_hdd_init_tx_rx_histogram(hdd_ctx); + + hdd_set_hif_init_phase(hif_ctx, false); + + break; + + default: + QDF_DEBUG_PANIC("Unknown driver state:%d", + hdd_ctx->driver_status); + ret = -EINVAL; + goto release_lock; + } + + hdd_ctx->driver_status = DRIVER_MODULES_ENABLED; + hdd_nofl_debug("Wlan transitioned (now ENABLED)"); + + ucfg_ipa_reg_rps_enable_cb(hdd_ctx->pdev, + hdd_adapter_set_rps); + hdd_exit(); + + return 0; + +destroy_driver_sysfs: + hdd_sysfs_dp_aggregation_destroy(); + hdd_sysfs_destroy_powerstats_interface(); + hdd_sysfs_destroy_version_interface(); + hdd_sysfs_destroy_driver_root_obj(); + cds_post_disable(); + +unregister_notifiers: + hdd_unregister_notifiers(hdd_ctx); + +deregister_cb: + hdd_deregister_cb(hdd_ctx); + +cds_txrx_free: + + tgt_hdl = wlan_psoc_get_tgt_if_handle(hdd_ctx->psoc); + + if (tgt_hdl && target_psoc_get_wmi_ready(tgt_hdl)) + hdd_runtime_suspend_context_deinit(hdd_ctx); + + if (hdd_ctx->pdev) { + dispatcher_pdev_close(hdd_ctx->pdev); + hdd_objmgr_release_and_destroy_pdev(hdd_ctx); + } + + cds_dp_close(hdd_ctx->psoc); + +close: + hdd_ctx->driver_status = DRIVER_MODULES_CLOSED; + hdd_info("Wlan transition aborted (now CLOSED)"); + + cds_close(hdd_ctx->psoc); + +psoc_close: + hdd_component_psoc_close(hdd_ctx->psoc); + wlan_global_lmac_if_close(hdd_ctx->psoc); + cds_deinit_ini_config(); + +cds_free: + ol_cds_free(); + +hif_close: + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + hdd_hif_close(hdd_ctx, hif_ctx); +power_down: + hdd_deinit_adapter_ops_wq(hdd_ctx); + hdd_bus_bandwidth_deinit(hdd_ctx); + if (!reinit && !unint) + pld_power_off(qdf_dev->dev); +release_lock: + if (hdd_ctx->target_hw_name) { + qdf_mem_free(hdd_ctx->target_hw_name); + hdd_ctx->target_hw_name = NULL; + } + + hdd_check_for_leaks(hdd_ctx, reinit); + hdd_debug_domain_set(QDF_DEBUG_DOMAIN_INIT); + + hdd_exit(); + + return ret; +} + +#ifdef WIFI_POS_CONVERGED +static int hdd_activate_wifi_pos(struct hdd_context *hdd_ctx) +{ + int ret = os_if_wifi_pos_register_nl(); + + if (ret) + hdd_err("os_if_wifi_pos_register_nl failed"); + + return ret; +} + +static int hdd_deactivate_wifi_pos(void) +{ + int ret = os_if_wifi_pos_deregister_nl(); + + if (ret) + hdd_err("os_if_wifi_pos_deregister_nl failed"); + + return ret; +} + +/** + * hdd_populate_wifi_pos_cfg - populates wifi_pos parameters + * @hdd_ctx: hdd context + * + * Return: status of operation + */ +static void hdd_populate_wifi_pos_cfg(struct hdd_context *hdd_ctx) +{ + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + uint16_t neighbor_scan_max_chan_time; + uint16_t neighbor_scan_min_chan_time; + + wifi_pos_set_oem_target_type(psoc, hdd_ctx->target_type); + wifi_pos_set_oem_fw_version(psoc, hdd_ctx->target_fw_version); + wifi_pos_set_drv_ver_major(psoc, QWLAN_VERSION_MAJOR); + wifi_pos_set_drv_ver_minor(psoc, QWLAN_VERSION_MINOR); + wifi_pos_set_drv_ver_patch(psoc, QWLAN_VERSION_PATCH); + wifi_pos_set_drv_ver_build(psoc, QWLAN_VERSION_BUILD); + ucfg_mlme_get_neighbor_scan_max_chan_time(psoc, + &neighbor_scan_max_chan_time); + ucfg_mlme_get_neighbor_scan_min_chan_time(psoc, + &neighbor_scan_min_chan_time); + wifi_pos_set_dwell_time_min(psoc, neighbor_scan_min_chan_time); + wifi_pos_set_dwell_time_max(psoc, neighbor_scan_max_chan_time); +} +#else +static int hdd_activate_wifi_pos(struct hdd_context *hdd_ctx) +{ + return oem_activate_service(hdd_ctx); +} + +static int hdd_deactivate_wifi_pos(void) +{ + return oem_deactivate_service(); +} + +static void hdd_populate_wifi_pos_cfg(struct hdd_context *hdd_ctx) +{ +} +#endif + +/** + * __hdd_open() - HDD Open function + * @dev: Pointer to net_device structure + * + * This is called in response to ifconfig up + * + * Return: 0 for success; non-zero for failure + */ +static int __hdd_open(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + + hdd_enter_dev(dev); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_OPEN_REQUEST, + adapter->vdev_id, adapter->device_mode); + + /* Nothing to be done if device is unloading */ + if (cds_is_driver_unloading()) { + hdd_err("Driver is unloading can not open the hdd"); + return -EBUSY; + } + + if (cds_is_driver_recovering()) { + hdd_err("WLAN is currently recovering; Please try again."); + return -EBUSY; + } + + /* + * This scenario can be hit in cases where in the wlan driver after + * registering the netdevices and there is a failure in driver + * initialization. So return error gracefully because the netdevices + * will be de-registered as part of the load failure. + */ + + if (!cds_is_driver_loaded()) { + hdd_err("Failed to start the wlan driver!!"); + return -EIO; + } + + ret = hdd_trigger_psoc_idle_restart(hdd_ctx); + if (ret) { + hdd_err("Failed to start WLAN modules return"); + return ret; + } + + if (!test_bit(SME_SESSION_OPENED, &adapter->event_flags)) { + ret = hdd_start_adapter(adapter); + if (ret) { + hdd_err("Failed to start adapter :%d", + adapter->device_mode); + return ret; + } + } + + set_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); + if (hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + hdd_debug("Enabling Tx Queues"); + /* Enable TX queues only when we are connected */ + wlan_hdd_netif_queue_control(adapter, + WLAN_START_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + } + + /* Enable carrier and transmit queues for NDI */ + if (WLAN_HDD_IS_NDI(adapter)) { + hdd_debug("Enabling Tx Queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + } + + hdd_populate_wifi_pos_cfg(hdd_ctx); + hdd_lpass_notify_start(hdd_ctx, adapter); + + return 0; +} + +/** + * hdd_open() - Wrapper function for __hdd_open to protect it from SSR + * @net_dev: Pointer to net_device structure + * + * This is called in response to ifconfig up + * + * Return: 0 for success; non-zero for failure + */ +static int hdd_open(struct net_device *net_dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_open(net_dev); + + osif_vdev_sync_trans_stop(vdev_sync); + + return errno; +} + +/** + * __hdd_stop() - HDD stop function + * @dev: Pointer to net_device structure + * + * This is called in response to ifconfig down + * + * Return: 0 for success; non-zero for failure + */ +static int __hdd_stop(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + mac_handle_t mac_handle; + + hdd_enter_dev(dev); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_STOP_REQUEST, + adapter->vdev_id, adapter->device_mode); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) { + set_bit(DOWN_DURING_SSR, &adapter->event_flags); + return ret; + } + + /* Nothing to be done if the interface is not opened */ + if (false == test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { + hdd_err("NETDEV Interface is not OPENED"); + return -ENODEV; + } + + /* Make sure the interface is marked as closed */ + clear_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); + + mac_handle = hdd_ctx->mac_handle; + + if (!wlan_hdd_is_session_type_monitor(adapter->device_mode) && + adapter->device_mode != QDF_FTM_MODE) { + hdd_debug("Disabling Auto Power save timer"); + sme_ps_disable_auto_ps_timer( + mac_handle, + adapter->vdev_id); + } + + /* + * Disable TX on the interface, after this hard_start_xmit() will not + * be called on that interface + */ + hdd_debug("Disabling queues, adapter device mode: %s(%d)", + qdf_opmode_str(adapter->device_mode), adapter->device_mode); + + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + if (adapter->device_mode == QDF_STA_MODE) + hdd_lpass_notify_stop(hdd_ctx); + + if (wlan_hdd_is_session_type_monitor(adapter->device_mode)) + hdd_reset_pktcapture_cb(OL_TXRX_PDEV_ID); + + /* + * NAN data interface is different in some sense. The traffic on NDI is + * bursty in nature and depends on the need to transfer. The service + * layer may down the interface after the usage and up again when + * required. In some sense, the NDI is expected to be available + * (like SAP) iface until NDI delete request is issued by the service + * layer. Skip BSS termination and adapter deletion for NAN Data + * interface (NDI). + */ + if (WLAN_HDD_IS_NDI(adapter)) + return 0; + + /* + * The interface is marked as down for outside world (aka kernel) + * But the driver is pretty much alive inside. The driver needs to + * tear down the existing connection on the netdev (session) + * cleanup the data pipes and wait until the control plane is stabilized + * for this interface. The call also needs to wait until the above + * mentioned actions are completed before returning to the caller. + * Notice that hdd_stop_adapter is requested not to close the session + * That is intentional to be able to scan if it is a STA/P2P interface + */ + hdd_stop_adapter(hdd_ctx, adapter); + + /* DeInit the adapter. This ensures datapath cleanup as well */ + hdd_deinit_adapter(hdd_ctx, adapter, true); + + if (!hdd_is_any_interface_open(hdd_ctx)) + hdd_psoc_idle_timer_start(hdd_ctx); + + hdd_exit(); + + return 0; +} + +/** + * hdd_stop() - Wrapper function for __hdd_stop to protect it from SSR + * @dev: pointer to net_device structure + * + * This is called in response to ifconfig down + * + * Return: 0 for success and error number for failure + */ +static int hdd_stop(struct net_device *net_dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_stop(net_dev); + + osif_vdev_sync_trans_stop(vdev_sync); + + return errno; +} + +/** + * hdd_uninit() - HDD uninit function + * @dev: Pointer to net_device structure + * + * This is called during the netdev unregister to uninitialize all data + * associated with the device + * + * This function must be protected by a transition + * + * Return: None + */ +static void hdd_uninit(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + + hdd_enter_dev(dev); + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid magic"); + goto exit; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("NULL hdd_ctx"); + goto exit; + } + + if (dev != adapter->dev) + hdd_err("Invalid device reference"); + + hdd_deinit_adapter(hdd_ctx, adapter, true); + + /* after uninit our adapter structure will no longer be valid */ + adapter->dev = NULL; + adapter->magic = 0; + +exit: + hdd_exit(); +} + +static int hdd_open_cesium_nl_sock(void) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) + struct netlink_kernel_cfg cfg = { + .groups = WLAN_NLINK_MCAST_GRP_ID, + .input = NULL + }; +#endif + int ret = 0; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) + cesium_nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_CESIUM, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)) + THIS_MODULE, +#endif + &cfg); +#else + cesium_nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_CESIUM, + WLAN_NLINK_MCAST_GRP_ID, + NULL, NULL, THIS_MODULE); +#endif + + if (!cesium_nl_srv_sock) { + hdd_err("NLINK: cesium netlink_kernel_create failed"); + ret = -ECONNREFUSED; + } + + return ret; +} + +static void hdd_close_cesium_nl_sock(void) +{ + if (cesium_nl_srv_sock) { + netlink_kernel_release(cesium_nl_srv_sock); + cesium_nl_srv_sock = NULL; + } +} + +void hdd_update_dynamic_mac(struct hdd_context *hdd_ctx, + struct qdf_mac_addr *curr_mac_addr, + struct qdf_mac_addr *new_mac_addr) +{ + uint8_t i; + + hdd_enter(); + + for (i = 0; i < QDF_MAX_CONCURRENCY_PERSONA; i++) { + if (!qdf_mem_cmp( + curr_mac_addr->bytes, + &hdd_ctx->dynamic_mac_list[i].dynamic_mac.bytes[0], + sizeof(struct qdf_mac_addr))) { + qdf_mem_copy(&hdd_ctx->dynamic_mac_list[i].dynamic_mac, + new_mac_addr->bytes, + sizeof(struct qdf_mac_addr)); + break; + } + } + + hdd_exit(); +} + +/** + * __hdd_set_mac_address() - set the user specified mac address + * @dev: Pointer to the net device. + * @addr: Pointer to the sockaddr. + * + * This function sets the user specified mac address using + * the command ifconfig wlanX hw ether . + * + * Return: 0 for success, non zero for failure + */ +static int __hdd_set_mac_address(struct net_device *dev, void *addr) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_adapter *adapter_temp; + struct hdd_context *hdd_ctx; + struct sockaddr *psta_mac_addr = addr; + QDF_STATUS qdf_ret_status = QDF_STATUS_SUCCESS; + int ret; + struct qdf_mac_addr mac_addr; + + hdd_enter_dev(dev); + + if (netif_running(dev)) { + hdd_err("On iface up, set mac address change isn't supported"); + return -EBUSY; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + qdf_mem_copy(&mac_addr, psta_mac_addr->sa_data, sizeof(mac_addr)); + adapter_temp = hdd_get_adapter_by_macaddr(hdd_ctx, mac_addr.bytes); + if (adapter_temp) { + if (!qdf_str_cmp(adapter_temp->dev->name, dev->name)) + return 0; + hdd_err("%s adapter exist with same address " QDF_MAC_ADDR_FMT, + adapter_temp->dev->name, + QDF_MAC_ADDR_REF(mac_addr.bytes)); + return -EINVAL; + } + + qdf_ret_status = wlan_hdd_validate_mac_address(&mac_addr); + if (QDF_IS_STATUS_ERROR(qdf_ret_status)) + return -EINVAL; + + hdd_nofl_debug("Changing MAC to " + QDF_MAC_ADDR_FMT " of the interface %s ", + QDF_MAC_ADDR_REF(mac_addr.bytes), dev->name); + + hdd_update_dynamic_mac(hdd_ctx, &adapter->mac_addr, &mac_addr); + memcpy(&adapter->mac_addr, psta_mac_addr->sa_data, ETH_ALEN); + memcpy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN); + + hdd_exit(); + return qdf_ret_status; +} + +/** + * hdd_set_mac_address() - Wrapper function to protect __hdd_set_mac_address() + * function from SSR + * @net_dev: pointer to net_device structure + * @addr: Pointer to the sockaddr + * + * This function sets the user specified mac address using + * the command ifconfig wlanX hw ether . + * + * Return: 0 for success. + */ +static int hdd_set_mac_address(struct net_device *net_dev, void *addr) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_set_mac_address(net_dev, addr); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static uint8_t *wlan_hdd_get_derived_intf_addr(struct hdd_context *hdd_ctx) +{ + int i, j; + + i = qdf_ffz(hdd_ctx->derived_intf_addr_mask); + if (i < 0 || i >= hdd_ctx->num_derived_addr) + return NULL; + qdf_atomic_set_bit(i, &hdd_ctx->derived_intf_addr_mask); + hdd_nofl_debug("Assigning MAC from derived list" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdd_ctx->derived_mac_addr[i].bytes)); + + /* Copy the mac in dynamic mac list at first free position */ + for (j = 0; j < QDF_MAX_CONCURRENCY_PERSONA; j++) { + if (qdf_is_macaddr_zero(&hdd_ctx-> + dynamic_mac_list[j].dynamic_mac)) + break; + } + if (j == QDF_MAX_CONCURRENCY_PERSONA) { + hdd_err("Max interfaces are up"); + return NULL; + } + + qdf_mem_copy(&hdd_ctx->dynamic_mac_list[j].dynamic_mac.bytes, + &hdd_ctx->derived_mac_addr[i].bytes, + sizeof(struct qdf_mac_addr)); + hdd_ctx->dynamic_mac_list[j].is_provisioned_mac = false; + hdd_ctx->dynamic_mac_list[j].bit_position = i; + + return hdd_ctx->derived_mac_addr[i].bytes; +} + +static uint8_t *wlan_hdd_get_provisioned_intf_addr(struct hdd_context *hdd_ctx) +{ + int i, j; + + i = qdf_ffz(hdd_ctx->provisioned_intf_addr_mask); + if (i < 0 || i >= hdd_ctx->num_provisioned_addr) + return NULL; + qdf_atomic_set_bit(i, &hdd_ctx->provisioned_intf_addr_mask); + hdd_debug("Assigning MAC from provisioned list" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdd_ctx->provisioned_mac_addr[i].bytes)); + + /* Copy the mac in dynamic mac list at first free position */ + for (j = 0; j < QDF_MAX_CONCURRENCY_PERSONA; j++) { + if (qdf_is_macaddr_zero(&hdd_ctx-> + dynamic_mac_list[j].dynamic_mac)) + break; + } + if (j == QDF_MAX_CONCURRENCY_PERSONA) { + hdd_err("Max interfaces are up"); + return NULL; + } + + qdf_mem_copy(&hdd_ctx->dynamic_mac_list[j].dynamic_mac.bytes, + &hdd_ctx->provisioned_mac_addr[i].bytes, + sizeof(struct qdf_mac_addr)); + hdd_ctx->dynamic_mac_list[j].is_provisioned_mac = true; + hdd_ctx->dynamic_mac_list[j].bit_position = i; + return hdd_ctx->provisioned_mac_addr[i].bytes; +} + +uint8_t *wlan_hdd_get_intf_addr(struct hdd_context *hdd_ctx, + enum QDF_OPMODE interface_type) +{ + uint8_t *mac_addr = NULL; + + if (qdf_atomic_test_bit(interface_type, + (unsigned long *) + (&hdd_ctx->config->provisioned_intf_pool))) + mac_addr = wlan_hdd_get_provisioned_intf_addr(hdd_ctx); + + if ((!mac_addr) && + (qdf_atomic_test_bit(interface_type, + (unsigned long *) + (&hdd_ctx->config->derived_intf_pool)))) + mac_addr = wlan_hdd_get_derived_intf_addr(hdd_ctx); + + if (!mac_addr) + hdd_err("MAC is not available in both the lists"); + return mac_addr; +} + +void wlan_hdd_release_intf_addr(struct hdd_context *hdd_ctx, + uint8_t *releaseAddr) +{ + int i; + int mac_pos_in_mask; + + for (i = 0; i < QDF_MAX_CONCURRENCY_PERSONA; i++) { + if (!memcmp(releaseAddr, + hdd_ctx->dynamic_mac_list[i].dynamic_mac.bytes, + QDF_MAC_ADDR_SIZE)) { + mac_pos_in_mask = + hdd_ctx->dynamic_mac_list[i].bit_position; + if (hdd_ctx->dynamic_mac_list[i].is_provisioned_mac) { + qdf_atomic_clear_bit( + mac_pos_in_mask, + &hdd_ctx-> + provisioned_intf_addr_mask); + hdd_debug("Releasing MAC from provisioned list"); + hdd_debug( + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(releaseAddr)); + } else { + qdf_atomic_clear_bit( + mac_pos_in_mask, &hdd_ctx-> + derived_intf_addr_mask); + hdd_debug("Releasing MAC from derived list"); + hdd_debug(QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(releaseAddr)); + } + qdf_zero_macaddr(&hdd_ctx-> + dynamic_mac_list[i].dynamic_mac); + hdd_ctx->dynamic_mac_list[i].is_provisioned_mac = + false; + hdd_ctx->dynamic_mac_list[i].bit_position = 0; + break; + } + + } + if (i == QDF_MAX_CONCURRENCY_PERSONA) + hdd_err("Releasing non existing MAC" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(releaseAddr)); +} + +/** + * __hdd_set_multicast_list() - set the multicast address list + * @dev: Pointer to the WLAN device. + * @skb: Pointer to OS packet (sk_buff). + * + * This funciton sets the multicast address list. + * + * Return: None + */ +static void __hdd_set_multicast_list(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int i = 0, errno; + struct netdev_hw_addr *ha; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct pmo_mc_addr_list_params *mc_list_request = NULL; + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + int mc_count = 0; + + if (hdd_ctx->hdd_wlan_suspended) { + hdd_err_rl("Device is system suspended"); + return; + } + + hdd_enter_dev(dev); + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) + return; + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return; + + errno = hdd_validate_adapter(adapter); + if (errno) + return; + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_debug("%s: Driver module is closed", __func__); + return; + } + + mc_list_request = qdf_mem_malloc(sizeof(*mc_list_request)); + if (!mc_list_request) + return; + + /* Delete already configured multicast address list */ + if (adapter->mc_addr_list.mc_cnt > 0) + hdd_disable_and_flush_mc_addr_list(adapter, + pmo_mc_list_change_notify); + + if (dev->flags & IFF_ALLMULTI) { + hdd_debug("allow all multicast frames"); + hdd_disable_and_flush_mc_addr_list(adapter, + pmo_mc_list_change_notify); + } else { + mc_count = netdev_mc_count(dev); + if (mc_count > ucfg_pmo_max_mc_addr_supported(psoc)) { + hdd_debug("Exceeded max MC filter addresses (%d). Allowing all MC frames by disabling MC address filtering", + ucfg_pmo_max_mc_addr_supported(psoc)); + hdd_disable_and_flush_mc_addr_list(adapter, + pmo_mc_list_change_notify); + adapter->mc_addr_list.mc_cnt = 0; + goto free_req; + } + netdev_for_each_mc_addr(ha, dev) { + if (i == mc_count) + break; + memset(&(mc_list_request->mc_addr[i].bytes), + 0, ETH_ALEN); + memcpy(&(mc_list_request->mc_addr[i].bytes), + ha->addr, ETH_ALEN); + hdd_debug("mlist[%d] = "QDF_MAC_ADDR_FMT, i, + QDF_MAC_ADDR_REF(mc_list_request->mc_addr[i].bytes)); + i++; + } + } + + adapter->mc_addr_list.mc_cnt = mc_count; + mc_list_request->psoc = psoc; + mc_list_request->vdev_id = adapter->vdev_id; + mc_list_request->count = mc_count; + + errno = hdd_cache_mc_addr_list(mc_list_request); + if (errno) { + hdd_debug("Failed to cache MC address list for vdev %u; errno:%d", + adapter->vdev_id, errno); + goto free_req; + } + + hdd_enable_mc_addr_filtering(adapter, pmo_mc_list_change_notify); + +free_req: + qdf_mem_free(mc_list_request); +} + +/** + * hdd_set_multicast_list() - SSR wrapper function for __hdd_set_multicast_list + * @net_dev: pointer to net_device + * + * Return: none + */ +static void hdd_set_multicast_list(struct net_device *net_dev) +{ + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(net_dev, &vdev_sync)) + return; + + __hdd_set_multicast_list(net_dev); + + osif_vdev_sync_op_stop(vdev_sync); +} + +#ifdef WLAN_FEATURE_TSF_PTP +static const struct ethtool_ops wlan_ethtool_ops = { + .get_ts_info = wlan_get_ts_info, +}; +#endif + +/** + * __hdd_fix_features - Adjust the feature flags needed to be updated + * @net_dev: Handle to net_device + * @features: Currently enabled feature flags + * + * Return: Adjusted feature flags on success, old feature on failure + */ +static netdev_features_t __hdd_fix_features(struct net_device *net_dev, + netdev_features_t features) +{ + netdev_features_t feature_change_req = features; + netdev_features_t feature_tso_csum; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + + if (!adapter->handle_feature_update) { + hdd_debug("Not triggered by hdd_netdev_update_features"); + return features; + } + + feature_tso_csum = hdd_get_tso_csum_feature_flags(); + if (hdd_is_legacy_connection(adapter)) + /* Disable checksum and TSO */ + feature_change_req &= ~feature_tso_csum; + else + /* Enable checksum and TSO */ + feature_change_req |= feature_tso_csum; + + hdd_debug("vdev mode %d current features 0x%llx, requesting feature change 0x%llx", + adapter->device_mode, net_dev->features, + feature_change_req); + + return feature_change_req; +} + +/** + * hdd_fix_features() - Wrapper for __hdd_fix_features to protect it from SSR + * @net_dev: Pointer to net_device structure + * @features: Updated features set + * + * Adjusts the feature request, do not update the device yet. + * + * Return: updated feature for success, incoming feature as is on failure + */ +static netdev_features_t hdd_fix_features(struct net_device *net_dev, + netdev_features_t features) +{ + int errno; + int changed_features = features; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return features; + + changed_features = __hdd_fix_features(net_dev, features); + + osif_vdev_sync_op_stop(vdev_sync); + + return changed_features; +} + +/** + * __hdd_set_features - Update device config for resultant change in feature + * @net_dev: Handle to net_device + * @features: Existing + requested feature after resolving the dependency + * + * Return: 0 on success, non zero error on failure + */ +static int __hdd_set_features(struct net_device *net_dev, + netdev_features_t features) +{ + struct hdd_adapter *adapter = netdev_priv(net_dev); + cdp_config_param_type vdev_param; + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!adapter->handle_feature_update) { + hdd_debug("Not triggered by hdd_netdev_update_features"); + return 0; + } + + if (!soc) { + hdd_err("soc handle is NULL"); + return 0; + } + + hdd_debug("vdev mode %d vdev_id %d current features 0x%llx, changed features 0x%llx", + adapter->device_mode, adapter->vdev_id, net_dev->features, + features); + + if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) + vdev_param.cdp_enable_tx_checksum = true; + else + vdev_param.cdp_enable_tx_checksum = false; + + if (cdp_txrx_set_vdev_param(soc, adapter->vdev_id, CDP_ENABLE_CSUM, + vdev_param)) + hdd_debug("Failed to set DP vdev params"); + + return 0; +} + +/** + * hdd_set_features() - Wrapper for __hdd_set_features to protect it from SSR + * @net_dev: Pointer to net_device structure + * @features: Updated features set + * + * Is called to update device configurations for changed features. + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_set_features(struct net_device *net_dev, + netdev_features_t features) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) { + /* + * Only invoke from netdev_feature_update_work expected, + * which is from CLD inside. + * Ignore others from upper stack during loading phase, + * and return success to avoid failure print from kernel. + */ + hdd_debug("VDEV in transition, ignore set_features"); + return 0; + } + + errno = __hdd_set_features(net_dev, features); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#define HDD_NETDEV_FEATURES_UPDATE_MAX_WAIT_COUNT 10 +#define HDD_NETDEV_FEATURES_UPDATE_WAIT_INTERVAL_MS 20 + +void hdd_netdev_update_features(struct hdd_adapter *adapter) +{ + struct net_device *net_dev = adapter->dev; + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + bool request_feature_update = false; + int wait_count = HDD_NETDEV_FEATURES_UPDATE_MAX_WAIT_COUNT; + + if (!soc) { + hdd_err("soc handle is NULL"); + return; + } + + if (!cdp_cfg_get(soc, cfg_dp_disable_legacy_mode_csum_offload)) + return; + + switch (adapter->device_mode) { + case QDF_STA_MODE: + if (cdp_cfg_get(soc, cfg_dp_enable_ip_tcp_udp_checksum_offload)) + request_feature_update = true; + break; + default: + break; + } + + if (request_feature_update) { + hdd_debug("Update net_dev features for device mode %d", + adapter->device_mode); + while (!adapter->delete_in_progress) { + if (rtnl_trylock()) { + adapter->handle_feature_update = true; + netdev_update_features(net_dev); + adapter->handle_feature_update = false; + rtnl_unlock(); + break; + } + + if (wait_count--) { + qdf_sleep( + HDD_NETDEV_FEATURES_UPDATE_WAIT_INTERVAL_MS); + } else { + /* + * We have failed to updated the netdev + * features for very long, so enable the queues + * now. The impact of not being able to update + * the netdev feature is lower TPUT when + * switching from legacy to non-legacy mode. + */ + hdd_err("Failed to update netdev features for device mode %d", + adapter->device_mode); + break; + } + } + } +} + +static const struct net_device_ops wlan_drv_ops = { + .ndo_open = hdd_open, + .ndo_stop = hdd_stop, + .ndo_uninit = hdd_uninit, + .ndo_start_xmit = hdd_hard_start_xmit, + .ndo_fix_features = hdd_fix_features, + .ndo_set_features = hdd_set_features, + .ndo_tx_timeout = hdd_tx_timeout, + .ndo_get_stats = hdd_get_stats, + .ndo_do_ioctl = hdd_ioctl, + .ndo_set_mac_address = hdd_set_mac_address, + .ndo_select_queue = hdd_select_queue, + .ndo_set_rx_mode = hdd_set_multicast_list, +}; + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +/* Monitor mode net_device_ops, doesnot Tx and most of operations. */ +static const struct net_device_ops wlan_mon_drv_ops = { + .ndo_open = hdd_mon_open, + .ndo_stop = hdd_stop, + .ndo_get_stats = hdd_get_stats, +}; + +#ifdef WLAN_FEATURE_TSF_PTP +/** + * hdd_set_station_ops() - update net_device ops for monitor mode + * @dev: Handle to struct net_device to be updated. + * Return: None + */ +void hdd_set_station_ops(struct net_device *dev) +{ + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) { + dev->netdev_ops = &wlan_mon_drv_ops; + } else { + dev->netdev_ops = &wlan_drv_ops; + dev->ethtool_ops = &wlan_ethtool_ops; + } +} +#else +void hdd_set_station_ops(struct net_device *dev) +{ + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + dev->netdev_ops = &wlan_mon_drv_ops; + else + dev->netdev_ops = &wlan_drv_ops; +} + +#endif +#else +#ifdef WLAN_FEATURE_TSF_PTP +void hdd_set_station_ops(struct net_device *dev) +{ + dev->netdev_ops = &wlan_drv_ops; + dev->ethtool_ops = &wlan_ethtool_ops; +} +#else +void hdd_set_station_ops(struct net_device *dev) +{ + dev->netdev_ops = &wlan_drv_ops; +} +#endif +#endif + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/* Packet Capture mode net_device_ops, doesnot Tx and most of operations. */ +static const struct net_device_ops wlan_pktcapture_drv_ops = { + .ndo_open = hdd_pktcapture_open, + .ndo_stop = hdd_stop, + .ndo_get_stats = hdd_get_stats, +}; + +static void hdd_set_pktcapture_ops(struct net_device *dev) +{ + dev->netdev_ops = &wlan_pktcapture_drv_ops; +} +#else +static void hdd_set_pktcapture_ops(struct net_device *dev) +{ +} +#endif + +/** + * hdd_alloc_station_adapter() - allocate the station hdd adapter + * @hdd_ctx: global hdd context + * @mac_addr: mac address to assign to the interface + * @name: User-visible name of the interface + * @session_type: interface type to be created + * + * hdd adapter pointer would point to the netdev->priv space, this function + * would retrieve the pointer, and setup the hdd adapter configuration. + * + * Return: the pointer to hdd adapter, otherwise NULL + */ +static struct hdd_adapter * +hdd_alloc_station_adapter(struct hdd_context *hdd_ctx, tSirMacAddr mac_addr, + unsigned char name_assign_type, const char *name, + uint8_t session_type) +{ + struct net_device *dev; + struct hdd_adapter *adapter; + struct hdd_station_ctx *sta_ctx; + QDF_STATUS qdf_status; + uint8_t latency_level; + + /* cfg80211 initialization and registration */ + dev = alloc_netdev_mq(sizeof(*adapter), name, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS) + name_assign_type, +#endif + ((cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE || + wlan_hdd_is_session_type_monitor(session_type)) ? + hdd_mon_mode_ether_setup : ether_setup), + NUM_TX_QUEUES); + + if (!dev) { + hdd_err("Failed to allocate new net_device '%s'", name); + return NULL; + } + + adapter = netdev_priv(dev); + + qdf_mem_zero(adapter, sizeof(*adapter)); + sta_ctx = &adapter->session.station; + qdf_mem_zero(sta_ctx->conn_info.peer_macaddr, + sizeof(sta_ctx->conn_info.peer_macaddr)); + adapter->dev = dev; + adapter->hdd_ctx = hdd_ctx; + adapter->magic = WLAN_HDD_ADAPTER_MAGIC; + adapter->vdev_id = WLAN_UMAC_VDEV_ID_MAX; + + qdf_status = qdf_event_create(&adapter->qdf_session_open_event); + if (QDF_IS_STATUS_ERROR(qdf_status)) + goto free_net_dev; + + qdf_status = hdd_monitor_mode_qdf_create_event(adapter, session_type); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err_rl("create monitor mode vdve up event failed"); + goto free_net_dev; + } + + init_completion(&adapter->vdev_destroy_event); + + adapter->offloads_configured = false; + adapter->is_link_up_service_needed = false; + adapter->disconnection_in_progress = false; + adapter->send_mode_change = true; + + /* Cache station count initialize to zero */ + qdf_atomic_init(&adapter->cache_sta_count); + + /* Init the net_device structure */ + strlcpy(dev->name, name, IFNAMSIZ); + + qdf_mem_copy(dev->dev_addr, mac_addr, sizeof(tSirMacAddr)); + qdf_mem_copy(adapter->mac_addr.bytes, mac_addr, sizeof(tSirMacAddr)); + dev->watchdog_timeo = HDD_TX_TIMEOUT; + + if (wlan_hdd_is_session_type_monitor(session_type)) + hdd_set_pktcapture_ops(adapter->dev); + else + hdd_set_station_ops(adapter->dev); + + hdd_dev_setup_destructor(dev); + dev->ieee80211_ptr = &adapter->wdev; + dev->tx_queue_len = HDD_NETDEV_TX_QUEUE_LEN; + adapter->wdev.wiphy = hdd_ctx->wiphy; + adapter->wdev.netdev = dev; + qdf_status = ucfg_mlme_cfg_get_wlm_level(hdd_ctx->psoc, &latency_level); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_debug("Can't get latency level"); + latency_level = + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL; + } + adapter->latency_level = latency_level; + + /* set dev's parent to underlying device */ + SET_NETDEV_DEV(dev, hdd_ctx->parent_dev); + hdd_wmm_init(adapter); + spin_lock_init(&adapter->pause_map_lock); + adapter->start_time = qdf_system_ticks(); + adapter->last_time = adapter->start_time; + + return adapter; + +free_net_dev: + free_netdev(adapter->dev); + + return NULL; +} + +static QDF_STATUS hdd_register_interface(struct hdd_adapter *adapter, bool rtnl_held) +{ + struct net_device *dev = adapter->dev; + int ret; + + hdd_enter(); + + if (rtnl_held) { + if (strnchr(dev->name, IFNAMSIZ - 1, '%')) { + + ret = dev_alloc_name(dev, dev->name); + if (ret < 0) { + hdd_err( + "unable to get dev name: %s, err = 0x%x", + dev->name, ret); + return QDF_STATUS_E_FAILURE; + } + } + + ret = register_netdevice(dev); + if (ret) { + hdd_err("register_netdevice(%s) failed, err = 0x%x", + dev->name, ret); + return QDF_STATUS_E_FAILURE; + } + } else { + ret = register_netdev(dev); + if (ret) { + hdd_err("register_netdev(%s) failed, err = 0x%x", + dev->name, ret); + return QDF_STATUS_E_FAILURE; + } + } + set_bit(NET_DEVICE_REGISTERED, &adapter->event_flags); + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_sme_close_session_callback(uint8_t vdev_id) +{ + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("Invalid HDD_CTX"); + return QDF_STATUS_E_FAILURE; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("NULL adapter"); + return QDF_STATUS_E_INVAL; + } + + if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { + hdd_err("Invalid magic"); + return QDF_STATUS_NOT_INITIALIZED; + } + + clear_bit(SME_SESSION_OPENED, &adapter->event_flags); + + /* + * We can be blocked while waiting for scheduled work to be + * flushed, and the adapter structure can potentially be freed, in + * which case the magic will have been reset. So make sure the + * magic is still good, and hence the adapter structure is still + * valid, before signaling completion + */ + if (WLAN_HDD_ADAPTER_MAGIC == adapter->magic) + complete(&adapter->vdev_destroy_event); + + return QDF_STATUS_SUCCESS; +} + +int hdd_vdev_ready(struct hdd_adapter *adapter) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + status = pmo_vdev_ready(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_objmgr_put_vdev(vdev); + return qdf_status_to_os_return(status); + } + + status = ucfg_reg_11d_vdev_created_update(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_objmgr_put_vdev(vdev); + return qdf_status_to_os_return(status); + } + + if (wma_capability_enhanced_mcast_filter()) + status = ucfg_pmo_enhanced_mc_filter_enable(vdev); + else + status = ucfg_pmo_enhanced_mc_filter_disable(vdev); + + hdd_objmgr_put_vdev(vdev); + + return qdf_status_to_os_return(status); +} + +int hdd_vdev_destroy(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + int errno; + struct hdd_context *hdd_ctx; + uint8_t vdev_id; + struct wlan_objmgr_vdev *vdev; + long rc; + + vdev_id = adapter->vdev_id; + hdd_nofl_debug("destroying vdev %d", vdev_id); + + /* vdev created sanity check */ + if (!test_bit(SME_SESSION_OPENED, &adapter->event_flags)) { + hdd_nofl_debug("vdev %u does not exist", vdev_id); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + /* + * if this is the last active connection check & stop the + * opportunistic timer first + */ + if ((policy_mgr_get_connection_count(hdd_ctx->psoc) == 1 && + policy_mgr_mode_specific_connection_count(hdd_ctx->psoc, + policy_mgr_convert_device_mode_to_qdf_type( + adapter->device_mode), NULL) == 1) || + (!policy_mgr_get_connection_count(hdd_ctx->psoc) && + !hdd_is_any_sta_connecting(hdd_ctx))) + policy_mgr_check_and_stop_opportunistic_timer(hdd_ctx->psoc, + adapter->vdev_id); + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + ucfg_pmo_del_wow_pattern(vdev); + status = ucfg_reg_11d_vdev_delete_update(vdev); + ucfg_scan_vdev_set_disable(vdev, REASON_VDEV_DOWN); + wlan_hdd_scan_abort(adapter); + hdd_objmgr_put_vdev(vdev); + + /* Disable serialization for vdev before sending vdev delete */ + wlan_ser_vdev_queue_disable(adapter->vdev); + + /* close sme session (destroy vdev in firmware via legacy API) */ + INIT_COMPLETION(adapter->vdev_destroy_event); + status = sme_vdev_delete(hdd_ctx->mac_handle, vdev); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed to delete vdev; status:%d", status); + goto release_vdev; + } + + /* block on a completion variable until sme session is closed */ + rc = wait_for_completion_timeout( + &adapter->vdev_destroy_event, + msecs_to_jiffies(SME_CMD_VDEV_CREATE_DELETE_TIMEOUT)); + if (rc) { + clear_bit(SME_SESSION_OPENED, &adapter->event_flags); + + if (status == QDF_STATUS_E_TIMEOUT) { + hdd_err("timed out waiting for sme vdev delete"); + sme_cleanup_session(hdd_ctx->mac_handle, vdev_id); + } + } + +release_vdev: + + /* do vdev logical destroy via objmgr */ + errno = hdd_objmgr_release_and_destroy_vdev(adapter); + if (errno) { + hdd_err("failed to destroy objmgr vdev; errno:%d", errno); + return errno; + } + + hdd_nofl_debug("vdev %d destroyed successfully", vdev_id); + + return 0; +} + +void +hdd_store_nss_chains_cfg_in_vdev(struct hdd_adapter *adapter) +{ + struct wlan_mlme_nss_chains vdev_ini_cfg; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct wlan_objmgr_vdev *vdev; + + /* Populate the nss chain params from ini for this vdev type */ + sme_populate_nss_chain_params(hdd_ctx->mac_handle, &vdev_ini_cfg, + adapter->device_mode, + hdd_ctx->num_rf_chains); + + vdev = hdd_objmgr_get_vdev(adapter); + /* Store the nss chain config into the vdev */ + if (vdev) { + sme_store_nss_chains_cfg_in_vdev(adapter->vdev, &vdev_ini_cfg); + hdd_objmgr_put_vdev(vdev); + } else { + hdd_err("Vdev is NULL"); + } +} + +bool hdd_is_vdev_in_conn_state(struct hdd_adapter *adapter) +{ + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_DEVICE_MODE: + return hdd_conn_is_connected( + WLAN_HDD_GET_STATION_CTX_PTR(adapter)); + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + return (test_bit(SOFTAP_BSS_STARTED, + &adapter->event_flags)); + default: + hdd_err("Device mode %d invalid", adapter->device_mode); + return 0; + } + + return 0; +} + +int hdd_vdev_create(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + int errno; + bool bval; + struct hdd_context *hdd_ctx; + struct wlan_objmgr_vdev *vdev; + struct vdev_osif_priv *osif_priv; + struct wlan_vdev_create_params vdev_params = {0}; + uint16_t max_peer_count; + + hdd_nofl_debug("creating new vdev"); + + /* do vdev create via objmgr */ + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = sme_check_for_duplicate_session(hdd_ctx->mac_handle, + adapter->mac_addr.bytes); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Duplicate session is existing with same mac address"); + errno = qdf_status_to_os_return(status); + return errno; + } + + vdev_params.opmode = adapter->device_mode; + qdf_mem_copy(vdev_params.macaddr, + adapter->mac_addr.bytes, + QDF_NET_MAC_ADDR_MAX_LEN); + + vdev_params.size_vdev_priv = sizeof(*osif_priv); + vdev = sme_vdev_create(hdd_ctx->mac_handle, &vdev_params); + if (!vdev) { + hdd_err("failed to create vdev"); + return -EINVAL; + } + + /* Initialize the vdev OS private structure*/ + osif_priv = wlan_vdev_get_ospriv(vdev); + osif_priv->wdev = adapter->dev->ieee80211_ptr; + osif_priv->legacy_osif_priv = adapter; + + if (wlan_objmgr_vdev_try_get_ref(vdev, WLAN_HDD_ID_OBJ_MGR) != + QDF_STATUS_SUCCESS) { + errno = QDF_STATUS_E_INVAL; + sme_vdev_delete(hdd_ctx->mac_handle, vdev); + wlan_objmgr_vdev_obj_delete(vdev); + return -EINVAL; + } + + set_bit(SME_SESSION_OPENED, &adapter->event_flags); + qdf_spin_lock_bh(&adapter->vdev_lock); + adapter->vdev_id = wlan_vdev_get_id(vdev); + adapter->vdev = vdev; + qdf_spin_unlock_bh(&adapter->vdev_lock); + + /* firmware ready for component communication, raise vdev_ready event */ + errno = hdd_vdev_ready(adapter); + if (errno) { + hdd_err("failed to dispatch vdev ready event: %d", errno); + goto hdd_vdev_destroy_procedure; + } + + if (adapter->device_mode == QDF_STA_MODE) { + bval = false; + status = ucfg_mlme_get_rtt_mac_randomization(hdd_ctx->psoc, + &bval); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("unable to get RTT MAC randomization value"); + + hdd_debug("setting RTT mac randomization param: %d", bval); + errno = sme_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_DISABLE_RTT_INITIATOR_RANDOM_MAC, + bval, + VDEV_CMD); + if (0 != errno) + hdd_err("RTT mac randomization param set failed %d", + errno); + } + + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + goto hdd_vdev_destroy_procedure; + + /* Max peer can be tdls peers + self peer + bss peer */ + max_peer_count = cfg_tdls_get_max_peer_count(hdd_ctx->psoc); + max_peer_count += 2; + wlan_vdev_set_max_peer_count(vdev, max_peer_count); + + hdd_objmgr_put_vdev(vdev); + } + + if (QDF_NAN_DISC_MODE == adapter->device_mode) { + sme_cli_set_command( + adapter->vdev_id, + WMI_VDEV_PARAM_ALLOW_NAN_INITIAL_DISCOVERY_OF_MP0_CLUSTER, + cfg_nan_get_support_mp0_discovery(hdd_ctx->psoc), + VDEV_CMD); + } + hdd_store_nss_chains_cfg_in_vdev(adapter); + + /* Configure vdev params */ + ucfg_fwol_configure_vdev_params(hdd_ctx->psoc, hdd_ctx->pdev, + adapter->device_mode, adapter->vdev_id); + + hdd_nofl_debug("vdev %d created successfully", adapter->vdev_id); + + return 0; + +hdd_vdev_destroy_procedure: + QDF_BUG(!hdd_vdev_destroy(adapter)); + + return errno; +} + +QDF_STATUS hdd_init_station_mode(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx = &adapter->session.station; + struct hdd_context *hdd_ctx; + QDF_STATUS status; + int ret_val; + mac_handle_t mac_handle; + bool bval = false; + uint8_t enable_sifs_burst = 0; + uint32_t fine_time_meas_cap = 0, roam_triggers; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle = hdd_ctx->mac_handle; + sme_set_curr_device_mode(mac_handle, adapter->device_mode); + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + sme_set_pdev_ht_vht_ies(mac_handle, bval); + + sme_set_vdev_ies_per_band(mac_handle, adapter->vdev_id, + adapter->device_mode); + + hdd_roam_profile_init(adapter); + hdd_register_wext(adapter->dev); + + hdd_conn_set_connection_state(adapter, eConnectionState_NotConnected); + sme_roam_reset_configs(mac_handle, adapter->vdev_id); + + /* set fast roaming capability in sme session */ + status = sme_config_fast_roaming(mac_handle, adapter->vdev_id, + true); + /* Set the default operation channel freq*/ + sta_ctx->conn_info.chan_freq = hdd_ctx->config->operating_chan_freq; + + /* Make the default Auth Type as OPEN */ + sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_OPEN_SYSTEM; + + status = hdd_init_tx_rx(adapter); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("hdd_init_tx_rx() failed, status code %08d [x%08x]", + status, status); + goto error_init_txrx; + } + + set_bit(INIT_TX_RX_SUCCESS, &adapter->event_flags); + + status = hdd_wmm_adapter_init(adapter); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("hdd_wmm_adapter_init() failed, status code %08d [x%08x]", + status, status); + goto error_wmm_init; + } + + set_bit(WMM_INIT_DONE, &adapter->event_flags); + + status = ucfg_get_enable_sifs_burst(hdd_ctx->psoc, &enable_sifs_burst); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to get sifs burst value, use default"); + + ret_val = sme_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_BURST_ENABLE, + enable_sifs_burst, + PDEV_CMD); + if (ret_val) + hdd_err("WMI_PDEV_PARAM_BURST_ENABLE set failed %d", ret_val); + + + hdd_set_netdev_flags(adapter); + + /* rcpi info initialization */ + qdf_mem_zero(&adapter->rcpi, sizeof(adapter->rcpi)); + + if (adapter->device_mode == QDF_STA_MODE) { + roam_triggers = ucfg_mlme_get_roaming_triggers(hdd_ctx->psoc); + mlme_set_roam_trigger_bitmap(hdd_ctx->psoc, adapter->vdev_id, + roam_triggers); + + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, + &fine_time_meas_cap); + sme_cli_set_command( + adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_DISABLE_RTT_RESPONDER_ROLE, + (bool)(fine_time_meas_cap & WMI_FW_STA_RTT_RESPR), + VDEV_CMD); + sme_cli_set_command( + adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_DISABLE_RTT_INITIATOR_ROLE, + (bool)(fine_time_meas_cap & WMI_FW_STA_RTT_INITR), + VDEV_CMD); + } + + return QDF_STATUS_SUCCESS; + +error_wmm_init: + clear_bit(INIT_TX_RX_SUCCESS, &adapter->event_flags); + hdd_deinit_tx_rx(adapter); +error_init_txrx: + hdd_unregister_wext(adapter->dev); + QDF_BUG(!hdd_vdev_destroy(adapter)); + + return status; +} + +static char *net_dev_ref_debug_string_from_id(wlan_net_dev_ref_dbgid dbgid) +{ + static const char *strings[] = { + "NET_DEV_HOLD_ID_RESERVED", + "NET_DEV_HOLD_GET_STA_CONNECTION_IN_PROGRESS", + "NET_DEV_HOLD_CHECK_DFS_CHANNEL_FOR_ADAPTER", + "NET_DEV_HOLD_GET_SAP_OPERATING_BAND", + "NET_DEV_HOLD_RECOVERY_NOTIFIER_CALL", + "NET_DEV_HOLD_IS_ANY_STA_CONNECTING", + "NET_DEV_HOLD_SAP_DESTROY_CTX_ALL", + "NET_DEV_HOLD_DRV_CMD_MAX_TX_POWER", + "NET_DEV_HOLD_IPA_SET_TX_FLOW_INFO", + "NET_DEV_HOLD_SET_RPS_CPU_MASK", + "NET_DEV_HOLD_DFS_INDICATE_RADAR", + "NET_DEV_HOLD_MAX_STA_INTERFACE_UP_COUNT_REACHED", + "NET_DEV_HOLD_IS_CHAN_SWITCH_IN_PROGRESS", + "NET_DEV_HOLD_STA_DESTROY_CTX_ALL", + "NET_DEV_HOLD_CHECK_FOR_EXISTING_MACADDR", + "NET_DEV_HOLD_DEINIT_ALL_ADAPTERS", + "NET_DEV_HOLD_STOP_ALL_ADAPTERS", + "NET_DEV_HOLD_RESET_ALL_ADAPTERS", + "NET_DEV_HOLD_IS_ANY_INTERFACE_OPEN", + "NET_DEV_HOLD_START_ALL_ADAPTERS", + "NET_DEV_HOLD_GET_ADAPTER_BY_RAND_MACADDR", + "NET_DEV_HOLD_GET_ADAPTER_BY_MACADDR", + "NET_DEV_HOLD_GET_ADAPTER_BY_VDEV", + "NET_DEV_HOLD_ADAPTER_GET_BY_REFERENCE", + "NET_DEV_HOLD_GET_ADAPTER_BY_IFACE_NAME", + "NET_DEV_HOLD_GET_ADAPTER", + "NET_DEV_HOLD_GET_OPERATING_CHAN_FREQ", + "NET_DEV_HOLD_UNREGISTER_WEXT_ALL_ADAPTERS", + "NET_DEV_HOLD_ABORT_MAC_SCAN_ALL_ADAPTERS", + "NET_DEV_HOLD_ABORT_SCHED_SCAN_ALL_ADAPTERS", + "NET_DEV_HOLD_GET_FIRST_VALID_ADAPTER", + "NET_DEV_HOLD_CLEAR_RPS_CPU_MASK", + "NET_DEV_HOLD_BUS_BW_WORK_HANDLER", + "NET_DEV_HOLD_DISPLAY_NETIF_QUEUE_HISTORY_COMPACT", + "NET_DEV_HOLD_DISPLAY_NETIF_QUEUE_HISTORY", + "NET_DEV_HOLD_CLEAR_NETIF_QUEUE_HISTORY", + "NET_DEV_HOLD_UNSAFE_CHANNEL_RESTART_SAP", + "NET_DEV_HOLD_INDICATE_MGMT_FRAME", + "NET_DEV_HOLD_STATE_INFO_DUMP", + "NET_DEV_HOLD_DISABLE_ROAMING", + "NET_DEV_HOLD_ENABLE_ROAMING", + "NET_DEV_HOLD_AUTO_SHUTDOWN_ENABLE", + "NET_DEV_HOLD_GET_CON_SAP_ADAPTER", + "NET_DEV_HOLD_IS_ANY_ADAPTER_CONNECTED", + "NET_DEV_HOLD_IS_ROAMING_IN_PROGRESS", + "NET_DEV_HOLD_DEL_P2P_INTERFACE", + "NET_DEV_HOLD_IS_NDP_ALLOWED", + "NET_DEV_HOLD_NDI_OPEN", + "NET_DEV_HOLD_SEND_OEM_REG_RSP_NLINK_MSG", + "NET_DEV_HOLD_PERIODIC_STA_STATS_DISPLAY", + "NET_DEV_HOLD_SUSPEND_WLAN", + "NET_DEV_HOLD_RESUME_WLAN", + "NET_DEV_HOLD_SSR_RESTART_SAP", + "NET_DEV_HOLD_SEND_DEFAULT_SCAN_IES", + "NET_DEV_HOLD_CFG80211_SUSPEND_WLAN", + "NET_DEV_HOLD_COUNTRY_CHANGE_UPDATE_STA", + "NET_DEV_HOLD_COUNTRY_CHANGE_UPDATE_SAP", + "NET_DEV_HOLD_CACHE_STATION_STATS_CB", + "NET_DEV_HOLD_DISPLAY_TXRX_STATS", + "NET_DEV_HOLD_ID_MAX"}; + int32_t num_dbg_strings = QDF_ARRAY_SIZE(strings); + + if (dbgid >= num_dbg_strings) { + char *ret = ""; + + hdd_err("Debug string not found for debug id %d", dbgid); + return ret; + } + + return (char *)strings[dbgid]; +} + +static void hdd_check_for_net_dev_ref_leak(struct hdd_adapter *adapter) +{ + int i, id; + + for (id = 0; id < NET_DEV_HOLD_ID_MAX; id++) { + for (i = 0; i < MAX_NET_DEV_REF_LEAK_ITERATIONS; i++) { + if (!qdf_atomic_read( + &adapter->net_dev_hold_ref_count[id])) + break; + hdd_info("net_dev held for debug id %s", + net_dev_ref_debug_string_from_id(id)); + qdf_sleep(NET_DEV_REF_LEAK_ITERATION_SLEEP_TIME_MS); + } + if (i == MAX_NET_DEV_REF_LEAK_ITERATIONS) { + hdd_err("net_dev hold reference leak detected for debug id: %s", + net_dev_ref_debug_string_from_id(id)); + QDF_BUG(0); + } + } +} + +/** + * hdd_deinit_station_mode() - De-initialize the station adapter + * @hdd_ctx: global hdd context + * @adapter: HDD adapter + * @rtnl_held: Used to indicate whether or not the caller is holding + * the kernel rtnl_mutex + * + * This function De-initializes the STA/P2P/OCB adapter. + * + * Return: None. + */ +static void hdd_deinit_station_mode(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + bool rtnl_held) +{ + hdd_enter_dev(adapter->dev); + + if (adapter->dev) { + if (rtnl_held) + adapter->dev->wireless_handlers = NULL; + else { + rtnl_lock(); + adapter->dev->wireless_handlers = NULL; + rtnl_unlock(); + } + } + + if (test_bit(INIT_TX_RX_SUCCESS, &adapter->event_flags)) { + hdd_deinit_tx_rx(adapter); + clear_bit(INIT_TX_RX_SUCCESS, &adapter->event_flags); + } + + if (test_bit(WMM_INIT_DONE, &adapter->event_flags)) { + hdd_wmm_adapter_close(adapter); + clear_bit(WMM_INIT_DONE, &adapter->event_flags); + } + + + hdd_exit(); +} + +void hdd_deinit_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + bool rtnl_held) +{ + hdd_enter(); + + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_MONITOR_MODE: + case QDF_P2P_DEVICE_MODE: + case QDF_IBSS_MODE: + case QDF_NDI_MODE: + case QDF_NAN_DISC_MODE: + { + hdd_deinit_station_mode(hdd_ctx, adapter, rtnl_held); + break; + } + + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + { + hdd_deinit_ap_mode(hdd_ctx, adapter, rtnl_held); + break; + } + + default: + break; + } + + hdd_exit(); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) && \ + defined(WLAN_FEATURE_11AX) +void hdd_cleanup_conn_info(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (!hdd_sta_ctx) + return; + + if (hdd_sta_ctx->cache_conn_info.he_operation) { + qdf_mem_free(hdd_sta_ctx->cache_conn_info.he_operation); + hdd_sta_ctx->cache_conn_info.he_operation = NULL; + } +} + +void hdd_sta_destroy_ctx_all(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_STA_DESTROY_CTX_ALL) { + if (adapter->device_mode == QDF_STA_MODE) + hdd_cleanup_conn_info(adapter); + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_STA_DESTROY_CTX_ALL); + } +} +#endif + +static void hdd_cleanup_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + bool rtnl_held) +{ + struct net_device *dev = NULL; + + if (adapter) + dev = adapter->dev; + else { + hdd_err("adapter is Null"); + return; + } + + hdd_nud_deinit_tracking(adapter); + hdd_mic_deinit_work(adapter); + qdf_mutex_destroy(&adapter->disconnection_status_lock); + hdd_periodic_sta_stats_mutex_destroy(adapter); + hdd_apf_context_destroy(adapter); + qdf_spinlock_destroy(&adapter->vdev_lock); + hdd_sta_info_deinit(&adapter->sta_info_list); + hdd_sta_info_deinit(&adapter->cache_sta_info_list); + + wlan_hdd_debugfs_csr_deinit(adapter); + if (adapter->device_mode == QDF_STA_MODE) + hdd_sysfs_destroy_adapter_root_obj(adapter); + + hdd_debugfs_exit(adapter); + + /* + * The adapter is marked as closed. When hdd_wlan_exit() call returns, + * the driver is almost closed and cannot handle either control + * messages or data. However, unregister_netdevice() call above will + * eventually invoke hdd_stop(ndo_close) driver callback, which attempts + * to close the active connections(basically excites control path) which + * is not right. Setting this flag helps hdd_stop() to recognize that + * the interface is closed and restricts any operations on that + */ + clear_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); + + if (test_bit(NET_DEVICE_REGISTERED, &adapter->event_flags)) { + if (rtnl_held) + unregister_netdevice(dev); + else + unregister_netdev(dev); + /* + * Note that the adapter is no longer valid at this point + * since the memory has been reclaimed + */ + } +} + +static QDF_STATUS hdd_check_for_existing_macaddr(struct hdd_context *hdd_ctx, + tSirMacAddr mac_addr) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_CHECK_FOR_EXISTING_MACADDR; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (!qdf_mem_cmp(adapter->mac_addr.bytes, + mac_addr, sizeof(tSirMacAddr))) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return QDF_STATUS_E_FAILURE; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef CONFIG_FW_LOGS_BASED_ON_INI +/** + * hdd_set_fw_log_params() - Set log parameters to FW + * @hdd_ctx: HDD Context + * @adapter: HDD Adapter + * + * This function set the FW Debug log level based on the INI. + * + * Return: None + */ +static void hdd_set_fw_log_params(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + QDF_STATUS status; + uint16_t enable_fw_log_level, enable_fw_log_type; + int ret; + + if (QDF_GLOBAL_FTM_MODE == cds_get_conparam() || + (!hdd_ctx->config->enable_fw_log)) { + hdd_debug("enable_fw_log not enabled in INI or in FTM mode return"); + return; + } + + /* Enable FW logs based on INI configuration */ + status = ucfg_fwol_get_enable_fw_log_type(hdd_ctx->psoc, + &enable_fw_log_type); + if (QDF_IS_STATUS_ERROR(status)) + return; + ret = sme_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_TYPE, + enable_fw_log_type, + DBG_CMD); + if (ret != 0) + hdd_err("Failed to enable FW log type ret %d", + ret); + + status = ucfg_fwol_get_enable_fw_log_level(hdd_ctx->psoc, + &enable_fw_log_level); + if (QDF_IS_STATUS_ERROR(status)) + return; + ret = sme_cli_set_command(adapter->vdev_id, + WMI_DBGLOG_LOG_LEVEL, + enable_fw_log_level, + DBG_CMD); + if (ret != 0) + hdd_err("Failed to enable FW log level ret %d", + ret); + + sme_enable_fw_module_log_level(hdd_ctx->mac_handle, + adapter->vdev_id); +} +#else +static void hdd_set_fw_log_params(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ +} + +#endif + +/** + * hdd_configure_chain_mask() - programs chain mask to firmware + * @adapter: HDD adapter + * + * Return: 0 on success or errno on failure + */ +static int hdd_configure_chain_mask(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + status = ucfg_mlme_configure_chain_mask(hdd_ctx->psoc, + adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + goto error; + + return 0; + +error: + hdd_debug("WMI PDEV set param failed"); + return -EINVAL; +} + +/** + * hdd_send_coex_config_params() - Send coex config params to FW + * @hdd_ctx: HDD context + * @adapter: Primary adapter context + * + * This function is used to send all coex config related params to FW + * + * Return: 0 on success and -EINVAL on failure + */ +static int hdd_send_coex_config_params(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct coex_config_params coex_cfg_params = {0}; + struct wlan_fwol_coex_config config = {0}; + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + QDF_STATUS status; + + if (!adapter) { + hdd_err("adapter is invalid"); + goto err; + } + + if (!psoc) { + hdd_err("HDD psoc is invalid"); + goto err; + } + + status = ucfg_fwol_get_coex_config_params(psoc, &config); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to get coex config params"); + goto err; + } + + coex_cfg_params.vdev_id = adapter->vdev_id; + coex_cfg_params.config_type = WMI_COEX_CONFIG_TX_POWER; + coex_cfg_params.config_arg1 = config.max_tx_power_for_btc; + + status = sme_send_coex_config_cmd(&coex_cfg_params); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to send coex Tx power"); + goto err; + } + + coex_cfg_params.config_type = WMI_COEX_CONFIG_HANDOVER_RSSI; + coex_cfg_params.config_arg1 = config.wlan_low_rssi_threshold; + + status = sme_send_coex_config_cmd(&coex_cfg_params); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to send coex handover RSSI"); + goto err; + } + + coex_cfg_params.config_type = WMI_COEX_CONFIG_BTC_MODE; + coex_cfg_params.config_arg1 = config.btc_mode; + + status = sme_send_coex_config_cmd(&coex_cfg_params); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to send coex BTC mode"); + goto err; + } + + coex_cfg_params.config_type = WMI_COEX_CONFIG_ANTENNA_ISOLATION; + coex_cfg_params.config_arg1 = config.antenna_isolation; + + status = sme_send_coex_config_cmd(&coex_cfg_params); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to send coex antenna isolation"); + goto err; + } + + coex_cfg_params.config_type = WMI_COEX_CONFIG_BT_LOW_RSSI_THRESHOLD; + coex_cfg_params.config_arg1 = config.bt_low_rssi_threshold; + + status = sme_send_coex_config_cmd(&coex_cfg_params); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to send coex BT low RSSI threshold"); + goto err; + } + + coex_cfg_params.config_type = WMI_COEX_CONFIG_BT_INTERFERENCE_LEVEL; + coex_cfg_params.config_arg1 = config.bt_interference_low_ll; + coex_cfg_params.config_arg2 = config.bt_interference_low_ul; + coex_cfg_params.config_arg3 = config.bt_interference_medium_ll; + coex_cfg_params.config_arg4 = config.bt_interference_medium_ul; + coex_cfg_params.config_arg5 = config.bt_interference_high_ll; + coex_cfg_params.config_arg6 = config.bt_interference_high_ul; + + status = sme_send_coex_config_cmd(&coex_cfg_params); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to send coex BT interference level"); + goto err; + } + + if (wlan_hdd_mpta_helper_enable(&coex_cfg_params, &config)) + goto err; + + coex_cfg_params.config_type = + WMI_COEX_CONFIG_BT_SCO_ALLOW_WLAN_2G_SCAN; + coex_cfg_params.config_arg1 = config.bt_sco_allow_wlan_2g_scan; + + status = sme_send_coex_config_cmd(&coex_cfg_params); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to send coex BT sco allow wlan 2g scan"); + goto err; + } + + return 0; +err: + return -EINVAL; +} + +/** + * hdd_set_fw_params() - Set parameters to firmware + * @adapter: HDD adapter + * + * This function Sets various parameters to fw once the + * adapter is started. + * + * Return: 0 on success or errno on failure + */ +int hdd_set_fw_params(struct hdd_adapter *adapter) +{ + int ret; + uint16_t upper_brssi_thresh, lower_brssi_thresh, rts_profile; + bool enable_dtim_1chrx; + QDF_STATUS status; + struct hdd_context *hdd_ctx; + bool bval = false; + uint8_t enable_tx_sch_delay; + uint32_t dtim_sel_diversity, enable_secondary_rate; + bool sap_xlna_bypass; + + hdd_enter_dev(adapter->dev); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) + return -EINVAL; + + if (cds_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_debug("FTM Mode is active; nothing to do"); + return 0; + } + + ret = -1; + if (QDF_IS_STATUS_SUCCESS(ucfg_fwol_get_lprx_enable(hdd_ctx->psoc, + &bval))) { + ret = sme_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_DTIM_SYNTH, + bval, PDEV_CMD); + } + if (ret) { + hdd_err("Failed to set LPRx"); + goto error; + } + + ucfg_mlme_get_dtim_selection_diversity(hdd_ctx->psoc, + &dtim_sel_diversity); + + ret = sme_cli_set_command( + adapter->vdev_id, + WMI_PDEV_PARAM_1CH_DTIM_OPTIMIZED_CHAIN_SELECTION, + dtim_sel_diversity, PDEV_CMD); + if (ret) { + hdd_err("Failed to set DTIM_OPTIMIZED_CHAIN_SELECTION"); + goto error; + } + + ret = -1; + if (QDF_IS_STATUS_SUCCESS(ucfg_fwol_get_enable_tx_sch_delay( + hdd_ctx->psoc, &enable_tx_sch_delay))) { + ret = sme_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_TX_SCH_DELAY, + enable_tx_sch_delay, PDEV_CMD); + } + if (ret) { + hdd_err("Failed to set WMI_PDEV_PARAM_TX_SCH_DELAY"); + goto error; + } + + ret = -1; + if (QDF_IS_STATUS_SUCCESS(ucfg_fwol_get_enable_secondary_rate( + hdd_ctx->psoc, &enable_secondary_rate))) { + ret = sme_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_SECONDARY_RETRY_ENABLE, + enable_secondary_rate, PDEV_CMD); + } + if (ret) { + hdd_err("Failed to set WMI_PDEV_PARAM_SECONDARY_RETRY_ENABLE"); + goto error; + } + + ret = -1; + if (QDF_IS_STATUS_SUCCESS(ucfg_fwol_get_sap_xlna_bypass( + hdd_ctx->psoc, &sap_xlna_bypass))) { + ret = sme_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_SET_SAP_XLNA_BYPASS, + sap_xlna_bypass, PDEV_CMD); + } + if (ret) { + hdd_err("Failed to set WMI_PDEV_PARAM_SET_SAP_XLNA_BYPASS"); + goto error; + } + + if (adapter->device_mode == QDF_STA_MODE) { + status = ucfg_get_upper_brssi_thresh(hdd_ctx->psoc, + &upper_brssi_thresh); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + sme_set_smps_cfg(adapter->vdev_id, + HDD_STA_SMPS_PARAM_UPPER_BRSSI_THRESH, + upper_brssi_thresh); + + status = ucfg_get_lower_brssi_thresh(hdd_ctx->psoc, + &lower_brssi_thresh); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + sme_set_smps_cfg(adapter->vdev_id, + HDD_STA_SMPS_PARAM_LOWER_BRSSI_THRESH, + lower_brssi_thresh); + + status = ucfg_get_enable_dtim_1chrx(hdd_ctx->psoc, + &enable_dtim_1chrx); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + sme_set_smps_cfg(adapter->vdev_id, + HDD_STA_SMPS_PARAM_DTIM_1CHRX_ENABLE, + enable_dtim_1chrx); + } + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + if (bval) { + hdd_debug("configuring 2x2 mode fw params"); + + ret = sme_set_cck_tx_fir_override(hdd_ctx->mac_handle, + adapter->vdev_id); + if (ret) { + hdd_err("WMI_PDEV_PARAM_ENABLE_CCK_TXFIR_OVERRIDE set failed %d", + ret); + goto error; + } + + hdd_configure_chain_mask(adapter); + } else { +#define HDD_DTIM_1CHAIN_RX_ID 0x5 +#define HDD_SMPS_PARAM_VALUE_S 29 + hdd_debug("configuring 1x1 mode fw params"); + + /* + * Disable DTIM 1 chain Rx when in 1x1, + * we are passing two value + * as param_id << 29 | param_value. + * Below param_value = 0(disable) + */ + ret = sme_cli_set_command(adapter->vdev_id, + WMI_STA_SMPS_PARAM_CMDID, + HDD_DTIM_1CHAIN_RX_ID << + HDD_SMPS_PARAM_VALUE_S, + VDEV_CMD); + if (ret) { + hdd_err("DTIM 1 chain set failed %d", ret); + goto error; + } + +#undef HDD_DTIM_1CHAIN_RX_ID +#undef HDD_SMPS_PARAM_VALUE_S + + hdd_configure_chain_mask(adapter); + } + + ret = sme_set_enable_mem_deep_sleep(hdd_ctx->mac_handle, + adapter->vdev_id); + if (ret) { + hdd_err("WMI_PDEV_PARAM_HYST_EN set failed %d", ret); + goto error; + } + + status = ucfg_fwol_get_rts_profile(hdd_ctx->psoc, &rts_profile); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + ret = sme_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + rts_profile, + VDEV_CMD); + if (ret) { + hdd_err("FAILED TO SET RTSCTS Profile ret:%d", ret); + goto error; + } + + hdd_set_fw_log_params(hdd_ctx, adapter); + + ret = hdd_send_coex_config_params(hdd_ctx, adapter); + if (ret) { + hdd_warn("Error initializing coex config params"); + goto error; + } + + hdd_exit(); + + return 0; + +error: + return -EINVAL; +} + +/** + * hdd_init_completion() - Initialize Completion Variables + * @adapter: HDD adapter + * + * This function Initialize the completion variables for + * a particular adapter + * + * Return: None + */ +static void hdd_init_completion(struct hdd_adapter *adapter) +{ + init_completion(&adapter->disconnect_comp_var); + init_completion(&adapter->roaming_comp_var); + init_completion(&adapter->linkup_event_var); + init_completion(&adapter->sta_authorized_event); + init_completion(&adapter->offchannel_tx_event); + init_completion(&adapter->tx_action_cnf_event); + init_completion(&adapter->ibss_peer_info_comp); + init_completion(&adapter->lfr_fw_status.disable_lfr_event); +} + +static void hdd_reset_locally_admin_bit(struct hdd_context *hdd_ctx, + tSirMacAddr mac_addr) +{ + int i; + /* + * Reset locally administered bit for dynamic_mac_list + * also as while releasing the MAC address for any + * interface mac will be compared with dynamic mac list + */ + for (i = 0; i < QDF_MAX_CONCURRENCY_PERSONA; i++) { + if (!qdf_mem_cmp( + mac_addr, + &hdd_ctx-> + dynamic_mac_list[i].dynamic_mac.bytes[0], + sizeof(struct qdf_mac_addr))) { + WLAN_HDD_RESET_LOCALLY_ADMINISTERED_BIT( + hdd_ctx-> + dynamic_mac_list[i].dynamic_mac.bytes); + break; + } + } + /* + * Reset locally administered bit if the device mode is + * STA + */ + WLAN_HDD_RESET_LOCALLY_ADMINISTERED_BIT(mac_addr); + hdd_debug("locally administered bit reset in sta mode: " + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(mac_addr)); +} + +static void wlan_hdd_cfg80211_scan_block_cb(struct work_struct *work) +{ + struct hdd_adapter *adapter = + container_of(work, struct hdd_adapter, scan_block_work); + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(adapter->dev, &vdev_sync)) + return; + + wlan_hdd_cfg80211_scan_block(adapter); + + osif_vdev_sync_op_stop(vdev_sync); +} + +static u8 hdd_get_mode_specific_interface_count(struct hdd_context *hdd_ctx, + enum QDF_OPMODE mode) +{ + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + u8 intf_count = 0; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_GET_MODE_SPECIFIC_INTERFACE_COUNT; + + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->device_mode == mode) + intf_count++; + + hdd_adapter_dev_put_debug(adapter, dbgid); + } + return intf_count; +} + +/** + * hdd_open_adapter() - open and setup the hdd adatper + * @hdd_ctx: global hdd context + * @session_type: type of the interface to be created + * @iface_name: User-visible name of the interface + * @mac_addr: MAC address to assign to the interface + * @name_assign_type: the name of assign type of the netdev + * @rtnl_held: the rtnl lock hold flag + * + * This function open and setup the hdd adpater according to the device + * type request, assign the name, the mac address assigned, and then prepared + * the hdd related parameters, queue, lock and ready to start. + * + * Return: the pointer of hdd adapter, otherwise NULL. + */ +struct hdd_adapter *hdd_open_adapter(struct hdd_context *hdd_ctx, uint8_t session_type, + const char *iface_name, tSirMacAddr mac_addr, + unsigned char name_assign_type, + bool rtnl_held) +{ + struct net_device *ndev = NULL; + struct hdd_adapter *adapter = NULL; + u8 intf_count = 0; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t i; + + if (hdd_ctx->current_intf_count >= WLAN_MAX_VDEVS) { + /* + * Max limit reached on the number of vdevs configured by the + * host. Return error + */ + hdd_err("Unable to add virtual intf: currentVdevCnt=%d,hostConfiguredVdevCnt=%d", + hdd_ctx->current_intf_count, hdd_ctx->max_intf_count); + return NULL; + } + + status = wlan_hdd_validate_mac_address((struct qdf_mac_addr *)mac_addr); + if (QDF_IS_STATUS_ERROR(status)) { + /* Not received valid mac_addr */ + hdd_err("Unable to add virtual intf: Not able to get valid mac address"); + return NULL; + } + + status = hdd_check_for_existing_macaddr(hdd_ctx, mac_addr); + if (QDF_STATUS_E_FAILURE == status) { + hdd_err("Duplicate MAC addr: " QDF_MAC_ADDR_FMT + " already exists", + QDF_MAC_ADDR_REF(mac_addr)); + return NULL; + } + + switch (session_type) { + case QDF_STA_MODE: + if (!hdd_ctx->config->mac_provision) { + hdd_reset_locally_admin_bit(hdd_ctx, mac_addr); + /* + * After resetting locally administered bit + * again check if the new mac address is already + * exists. + */ + status = hdd_check_for_existing_macaddr(hdd_ctx, + mac_addr); + if (QDF_STATUS_E_FAILURE == status) { + hdd_err("Duplicate MAC addr: " QDF_MAC_ADDR_FMT + " already exists", + QDF_MAC_ADDR_REF(mac_addr)); + return NULL; + } + } + /* Check for max no of supported VDEVs before creating + * another one. + */ + intf_count = hdd_get_mode_specific_interface_count( + hdd_ctx, + session_type); + if (CFG_TGT_DEFAULT_MAX_STA_VDEVS && + (intf_count >= CFG_TGT_DEFAULT_MAX_STA_VDEVS)) { + hdd_err("Max limit reached sta vdev-current %d max %d", + intf_count, CFG_TGT_DEFAULT_MAX_STA_VDEVS); + return NULL; + } + + /* fall through */ + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_DEVICE_MODE: + case QDF_OCB_MODE: + case QDF_NDI_MODE: + case QDF_MONITOR_MODE: + case QDF_NAN_DISC_MODE: + adapter = hdd_alloc_station_adapter(hdd_ctx, mac_addr, + name_assign_type, + iface_name, session_type); + + if (!adapter) { + hdd_err("failed to allocate adapter for session %d", + session_type); + return NULL; + } + + ndev = adapter->dev; + + if (QDF_P2P_CLIENT_MODE == session_type) + adapter->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT; + else if (QDF_P2P_DEVICE_MODE == session_type) + adapter->wdev.iftype = NL80211_IFTYPE_P2P_DEVICE; + else if (QDF_MONITOR_MODE == session_type) + adapter->wdev.iftype = NL80211_IFTYPE_MONITOR; + else if (QDF_NAN_DISC_MODE == session_type) + wlan_hdd_set_nan_if_type(adapter); + else + adapter->wdev.iftype = NL80211_IFTYPE_STATION; + + adapter->device_mode = session_type; + + + /* + * Workqueue which gets scheduled in IPv4 notification + * callback + */ + INIT_WORK(&adapter->ipv4_notifier_work, + hdd_ipv4_notifier_work_queue); + +#ifdef WLAN_NS_OFFLOAD + /* + * Workqueue which gets scheduled in IPv6 + * notification callback. + */ + INIT_WORK(&adapter->ipv6_notifier_work, + hdd_ipv6_notifier_work_queue); +#endif + status = hdd_register_interface(adapter, rtnl_held); + if (QDF_STATUS_SUCCESS != status) + goto err_free_netdev; + + /* Stop the Interface TX queue. */ + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + hdd_nud_init_tracking(adapter); + hdd_mic_init_work(adapter); + + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_DEVICE_MODE) + hdd_sysfs_create_adapter_root_obj(adapter); + qdf_mutex_create(&adapter->disconnection_status_lock); + hdd_periodic_sta_stats_mutex_create(adapter); + + break; + + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + adapter = hdd_wlan_create_ap_dev(hdd_ctx, mac_addr, + name_assign_type, + (uint8_t *) iface_name); + if (!adapter) { + hdd_err("failed to allocate adapter for session %d", + session_type); + return NULL; + } + + ndev = adapter->dev; + + adapter->wdev.iftype = + (session_type == + QDF_SAP_MODE) ? NL80211_IFTYPE_AP : + NL80211_IFTYPE_P2P_GO; + adapter->device_mode = session_type; + + status = hdd_register_interface(adapter, rtnl_held); + if (QDF_STATUS_SUCCESS != status) + goto err_free_netdev; + + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + hdd_mic_init_work(adapter); + + /* + * Workqueue which gets scheduled in IPv4 notification + * callback + */ + INIT_WORK(&adapter->ipv4_notifier_work, + hdd_ipv4_notifier_work_queue); + +#ifdef WLAN_NS_OFFLOAD + /* + * Workqueue which gets scheduled in IPv6 + * notification callback. + */ + INIT_WORK(&adapter->ipv6_notifier_work, + hdd_ipv6_notifier_work_queue); +#endif + + break; + case QDF_FTM_MODE: + adapter = hdd_alloc_station_adapter(hdd_ctx, mac_addr, + name_assign_type, + iface_name, session_type); + if (!adapter) { + hdd_err("Failed to allocate adapter for FTM mode"); + return NULL; + } + + ndev = adapter->dev; + + adapter->wdev.iftype = NL80211_IFTYPE_STATION; + adapter->device_mode = session_type; + status = hdd_register_interface(adapter, rtnl_held); + if (QDF_STATUS_SUCCESS != status) + goto err_free_netdev; + + /* Stop the Interface TX queue. */ + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + hdd_mic_init_work(adapter); + + break; + default: + hdd_err("Invalid session type %d", session_type); + QDF_ASSERT(0); + return NULL; + } + + status = hdd_adapter_feature_update_work_init(adapter); + if (QDF_IS_STATUS_ERROR(status)) + goto err_cleanup_adapter; + + adapter->upgrade_udp_qos_threshold = QCA_WLAN_AC_BK; + qdf_spinlock_create(&adapter->vdev_lock); + qdf_atomic_init(&hdd_ctx->num_latency_critical_clients); + + hdd_init_completion(adapter); + INIT_WORK(&adapter->scan_block_work, wlan_hdd_cfg80211_scan_block_cb); + INIT_WORK(&adapter->sap_stop_bss_work, + hdd_stop_sap_due_to_invalid_channel); + qdf_list_create(&adapter->blocked_scan_request_q, WLAN_MAX_SCAN_COUNT); + qdf_mutex_create(&adapter->blocked_scan_request_q_lock); + qdf_event_create(&adapter->acs_complete_event); + qdf_event_create(&adapter->peer_cleanup_done); + hdd_sta_info_init(&adapter->sta_info_list); + hdd_sta_info_init(&adapter->cache_sta_info_list); + qdf_atomic_init(&adapter->gro_disallowed); + + for (i = 0; i < NET_DEV_HOLD_ID_MAX; i++) + qdf_atomic_init( + &adapter->net_dev_hold_ref_count[NET_DEV_HOLD_ID_MAX]); + + /* Add it to the hdd's session list. */ + status = hdd_add_adapter_back(hdd_ctx, adapter); + if (QDF_STATUS_SUCCESS != status) + goto err_destroy_adapter_features_update_work; + + hdd_apf_context_init(adapter); + + policy_mgr_set_concurrency_mode(hdd_ctx->psoc, session_type); + + /* Adapter successfully added. Increment the vdev count */ + hdd_ctx->current_intf_count++; + + hdd_debug("current_intf_count=%d", hdd_ctx->current_intf_count); + + hdd_check_and_restart_sap_with_non_dfs_acs(); + + if (QDF_STATUS_SUCCESS != hdd_debugfs_init(adapter)) + hdd_err("Interface %s wow debug_fs init failed", + netdev_name(adapter->dev)); + + hdd_debug("%s interface created. iftype: %d", netdev_name(adapter->dev), + session_type); + + if (adapter->device_mode == QDF_STA_MODE) + wlan_hdd_debugfs_csr_init(adapter); + + hdd_periodic_sta_stats_init(adapter); + + return adapter; + +err_destroy_adapter_features_update_work: + hdd_adapter_feature_update_work_deinit(adapter); + +err_cleanup_adapter: + if (adapter) { + hdd_cleanup_adapter(hdd_ctx, adapter, rtnl_held); + adapter = NULL; + } + +err_free_netdev: + if (ndev) + free_netdev(ndev); + + return NULL; +} + +static void __hdd_close_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + bool rtnl_held) +{ + qdf_list_destroy(&adapter->blocked_scan_request_q); + qdf_mutex_destroy(&adapter->blocked_scan_request_q_lock); + policy_mgr_clear_concurrency_mode(hdd_ctx->psoc, adapter->device_mode); + qdf_event_destroy(&adapter->acs_complete_event); + qdf_event_destroy(&adapter->peer_cleanup_done); + hdd_adapter_feature_update_work_deinit(adapter); + hdd_cleanup_adapter(hdd_ctx, adapter, rtnl_held); + + if (hdd_ctx->current_intf_count != 0) + hdd_ctx->current_intf_count--; +} + +void hdd_close_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + bool rtnl_held) +{ + /* + * Stop the global bus bandwidth timer while touching the adapter list + * to avoid bad memory access by the timer handler. + */ + hdd_bus_bw_compute_timer_stop(hdd_ctx); + + hdd_check_for_net_dev_ref_leak(adapter); + hdd_remove_adapter(hdd_ctx, adapter); + __hdd_close_adapter(hdd_ctx, adapter, rtnl_held); + + /* conditionally restart the bw timer */ + hdd_bus_bw_compute_timer_try_start(hdd_ctx); +} + +void hdd_close_all_adapters(struct hdd_context *hdd_ctx, bool rtnl_held) +{ + struct hdd_adapter *adapter; + struct osif_vdev_sync *vdev_sync; + + hdd_enter(); + + while (QDF_IS_STATUS_SUCCESS(hdd_get_front_adapter( + hdd_ctx, &adapter))) { + hdd_check_for_net_dev_ref_leak(adapter); + hdd_remove_front_adapter(hdd_ctx, &adapter); + vdev_sync = osif_vdev_sync_unregister(adapter->dev); + if (vdev_sync) + osif_vdev_sync_wait_for_ops(vdev_sync); + + wlan_hdd_release_intf_addr(hdd_ctx, adapter->mac_addr.bytes); + __hdd_close_adapter(hdd_ctx, adapter, rtnl_held); + + if (vdev_sync) + osif_vdev_sync_destroy(vdev_sync); + } + + hdd_exit(); +} + +void wlan_hdd_reset_prob_rspies(struct hdd_adapter *adapter) +{ + struct qdf_mac_addr *bssid = NULL; + tSirUpdateIE update_ie; + mac_handle_t mac_handle; + + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + { + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + bssid = &sta_ctx->conn_info.bssid; + break; + } + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + case QDF_IBSS_MODE: + { + bssid = &adapter->mac_addr; + break; + } + case QDF_FTM_MODE: + case QDF_P2P_DEVICE_MODE: + default: + /* + * wlan_hdd_reset_prob_rspies should not have been called + * for these kind of devices + */ + hdd_err("Unexpected request for the current device type %d", + adapter->device_mode); + return; + } + + qdf_copy_macaddr(&update_ie.bssid, bssid); + update_ie.vdev_id = adapter->vdev_id; + update_ie.ieBufferlength = 0; + update_ie.pAdditionIEBuffer = NULL; + update_ie.append = true; + update_ie.notify = false; + mac_handle = hdd_adapter_get_mac_handle(adapter); + if (sme_update_add_ie(mac_handle, + &update_ie, + eUPDATE_IE_PROBE_RESP) == QDF_STATUS_E_FAILURE) { + hdd_err("Could not pass on PROBE_RSP_BCN data to PE"); + } +} + +/** + * hdd_ipa_ap_disconnect_evt() - Indicate wlan ipa ap disconnect event + * @hdd_ctx: hdd context + * @adapter: hdd adapter + * + * Return: None + */ +static inline +void hdd_ipa_ap_disconnect_evt(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + if (ucfg_ipa_is_enabled()) { + ucfg_ipa_uc_disconnect_ap(hdd_ctx->pdev, + adapter->dev); + ucfg_ipa_cleanup_dev_iface(hdd_ctx->pdev, + adapter->dev); + } +} + +static void +hdd_peer_cleanup(struct hdd_context *hdd_ctx, struct hdd_adapter *adapter) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + + /* Check if there is any peer present on the adapter */ + if (!hdd_any_valid_peer_present(adapter)) + return; + + if (adapter->device_mode == QDF_NDI_MODE) + qdf_status = ucfg_nan_disable_ndi(hdd_ctx->psoc, + adapter->vdev_id); + + if (QDF_IS_STATUS_ERROR(qdf_status)) + return; + + qdf_status = qdf_wait_for_event_completion(&adapter->peer_cleanup_done, + WLAN_WAIT_PEER_CLEANUP); + if (QDF_IS_STATUS_ERROR(qdf_status)) + hdd_debug("peer_cleanup_done wait fail"); +} + +#ifdef FUNC_CALL_MAP + +/** + * hdd_dump_func_call_map() - Dump the function call map + * + * Return: None + */ + +static void hdd_dump_func_call_map(void) +{ + char *cc_buf; + + cc_buf = qdf_mem_malloc(QDF_FUNCTION_CALL_MAP_BUF_LEN); + /* + * These logs are required as these indicates the start and end of the + * dump for the auto script to parse + */ + hdd_info("Function call map dump start"); + qdf_get_func_call_map(cc_buf); + qdf_trace_hex_dump(QDF_MODULE_ID_HDD, + QDF_TRACE_LEVEL_DEBUG, cc_buf, QDF_FUNCTION_CALL_MAP_BUF_LEN); + hdd_info("Function call map dump end"); + qdf_mem_free(cc_buf); +} +#else +static inline void hdd_dump_func_call_map(void) +{ +} +#endif + +QDF_STATUS hdd_stop_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct hdd_station_ctx *sta_ctx; + struct sap_context *sap_ctx; + struct csr_roam_profile *roam_profile; + union iwreq_data wrqu; + tSirUpdateIE update_ie; + unsigned long rc; + struct sap_config *sap_config; + mac_handle_t mac_handle; + struct wlan_objmgr_vdev *vdev; + enum eSirMacReasonCodes reason = eSIR_MAC_IFACE_DOWN; + + hdd_enter(); + + if (adapter->vdev_id != WLAN_UMAC_VDEV_ID_MAX) + wlan_hdd_cfg80211_deregister_frames(adapter); + + hdd_nud_ignore_tracking(adapter, true); + hdd_nud_reset_tracking(adapter); + hdd_nud_flush_work(adapter); + hdd_mic_flush_work(adapter); + hdd_stop_tsf_sync(adapter); + cds_flush_work(&adapter->scan_block_work); + wlan_hdd_cfg80211_scan_block(adapter); + + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + mac_handle = hdd_ctx->mac_handle; + + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_IBSS_MODE: + case QDF_P2P_DEVICE_MODE: + case QDF_NDI_MODE: + case QDF_NAN_DISC_MODE: + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (adapter->device_mode == QDF_NDI_MODE || + hdd_conn_is_connected(sta_ctx) || + hdd_is_connecting(sta_ctx)) { + INIT_COMPLETION(adapter->disconnect_comp_var); + + roam_profile = hdd_roam_profile(adapter); + if (cds_is_driver_recovering()) + reason = eSIR_MAC_DEVICE_RECOVERY; + + /* For NDI do not use roam_profile */ + if (adapter->device_mode == QDF_NDI_MODE) { + hdd_peer_cleanup(hdd_ctx, adapter); + status = sme_roam_disconnect( + mac_handle, + adapter->vdev_id, + eCSR_DISCONNECT_REASON_NDI_DELETE, + reason); + } else if (roam_profile->BSSType == + eCSR_BSS_TYPE_START_IBSS) + status = sme_roam_disconnect( + mac_handle, + adapter->vdev_id, + eCSR_DISCONNECT_REASON_IBSS_LEAVE, + reason); + else if (adapter->device_mode == QDF_STA_MODE) { + rc = wlan_hdd_disconnect( + adapter, + eCSR_DISCONNECT_REASON_DEAUTH, + reason); + if (rc != 0 && ucfg_ipa_is_enabled()) { + hdd_err("STA disconnect failed"); + ucfg_ipa_uc_cleanup_sta(hdd_ctx->pdev, + adapter->dev); + } + } else { + status = sme_roam_disconnect( + mac_handle, + adapter->vdev_id, + eCSR_DISCONNECT_REASON_UNSPECIFIED, + reason); + } + /* success implies disconnect is queued */ + if (QDF_IS_STATUS_SUCCESS(status) && + adapter->device_mode != QDF_STA_MODE) { + rc = wait_for_completion_timeout( + &adapter->disconnect_comp_var, + msecs_to_jiffies + (SME_DISCONNECT_TIMEOUT)); + if (!rc) + hdd_warn("disconn_comp_var wait fail"); + if (adapter->device_mode == QDF_NDI_MODE) + hdd_cleanup_ndi(hdd_ctx, adapter); + } + if (QDF_IS_STATUS_ERROR(status)) + hdd_warn("failed to post disconnect"); + + memset(&wrqu, '\0', sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN); + wireless_send_event(adapter->dev, SIOCGIWAP, &wrqu, + NULL); + } + + if ((adapter->device_mode == QDF_NAN_DISC_MODE || + (adapter->device_mode == QDF_STA_MODE && + !ucfg_nan_is_vdev_creation_allowed(hdd_ctx->psoc))) && + ucfg_is_nan_disable_supported(hdd_ctx->psoc) && + ucfg_is_nan_disc_active(hdd_ctx->psoc)) + ucfg_disable_nan_discovery(hdd_ctx->psoc, NULL, 0); + + wlan_hdd_scan_abort(adapter); + wlan_hdd_cleanup_actionframe(adapter); + wlan_hdd_cleanup_remain_on_channel_ctx(adapter); + hdd_clear_fils_connection_info(adapter); + + status = wlan_hdd_flush_pmksa_cache(adapter); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("Cannot flush PMKIDCache"); + + hdd_deregister_hl_netdev_fc_timer(adapter); + + hdd_deregister_tx_flow_control(adapter); + +#ifdef WLAN_OPEN_SOURCE + cancel_work_sync(&adapter->ipv4_notifier_work); +#ifdef WLAN_NS_OFFLOAD + cancel_work_sync(&adapter->ipv6_notifier_work); +#endif +#endif + + if (adapter->device_mode == QDF_STA_MODE) { + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + wlan_cfg80211_sched_scan_stop(vdev); + hdd_objmgr_put_vdev(vdev); + } + + ucfg_ipa_flush_pending_vdev_events(hdd_ctx->pdev, + adapter->vdev_id); + } + hdd_cleanup_conn_info(adapter); + hdd_vdev_destroy(adapter); + break; + + case QDF_MONITOR_MODE: + wlan_hdd_scan_abort(adapter); + hdd_deregister_hl_netdev_fc_timer(adapter); + hdd_deregister_tx_flow_control(adapter); + status = hdd_monitor_mode_vdev_status(adapter); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err_rl("stop failed montior mode"); + sme_delete_mon_session(mac_handle, adapter->vdev_id); + hdd_vdev_destroy(adapter); + break; + + case QDF_SAP_MODE: + wlan_hdd_scan_abort(adapter); + /* Diassociate with all the peers before stop ap post */ + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + if (wlan_hdd_del_station(adapter)) + hdd_sap_indicate_disconnect_for_sta(adapter); + } + status = wlan_hdd_flush_pmksa_cache(adapter); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("Cannot flush PMKIDCache"); + + sap_config = &adapter->session.ap.sap_config; + wlansap_reset_sap_config_add_ie(sap_config, eUPDATE_IE_ALL); + + ucfg_ipa_flush(hdd_ctx->pdev); + + /* don't flush pre-cac destroy if we are destroying pre-cac */ + sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); + if (!wlan_sap_is_pre_cac_context(sap_ctx) && + (hdd_ctx->sap_pre_cac_work.fn)) + cds_flush_work(&hdd_ctx->sap_pre_cac_work); + wlansap_cleanup_cac_timer(sap_ctx); + + /* fallthrough */ + + case QDF_P2P_GO_MODE: + cds_flush_work(&adapter->sap_stop_bss_work); + if (qdf_atomic_read(&adapter->session.ap.acs_in_progress)) { + hdd_info("ACS in progress, wait for complete"); + qdf_wait_for_event_completion( + &adapter->acs_complete_event, + ACS_COMPLETE_TIMEOUT); + } + wlan_hdd_undo_acs(adapter); + + if (adapter->device_mode == QDF_P2P_GO_MODE) + wlan_hdd_cleanup_remain_on_channel_ctx(adapter); + + hdd_deregister_hl_netdev_fc_timer(adapter); + hdd_deregister_tx_flow_control(adapter); + hdd_destroy_acs_timer(adapter); + + mutex_lock(&hdd_ctx->sap_lock); + if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + status = wlansap_stop_bss( + WLAN_HDD_GET_SAP_CTX_PTR(adapter)); + + if (QDF_IS_STATUS_SUCCESS(status)) { + struct hdd_hostapd_state *hostapd_state = + WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + qdf_event_reset(&hostapd_state-> + qdf_stop_bss_event); + status = qdf_wait_for_event_completion( + &hostapd_state->qdf_stop_bss_event, + SME_CMD_STOP_BSS_TIMEOUT); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failure waiting for wlansap_stop_bss %d", + status); + hdd_ipa_ap_disconnect_evt(hdd_ctx, + adapter); + } + } else { + hdd_err("failure in wlansap_stop_bss"); + } + + clear_bit(SOFTAP_BSS_STARTED, &adapter->event_flags); + policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, + adapter->device_mode, + adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, + adapter->device_mode, + false); + + qdf_copy_macaddr(&update_ie.bssid, + &adapter->mac_addr); + update_ie.vdev_id = adapter->vdev_id; + update_ie.ieBufferlength = 0; + update_ie.pAdditionIEBuffer = NULL; + update_ie.append = false; + update_ie.notify = false; + + /* Probe bcn reset */ + status = sme_update_add_ie(mac_handle, &update_ie, + eUPDATE_IE_PROBE_BCN); + if (status == QDF_STATUS_E_FAILURE) + hdd_err("Could not pass PROBE_RSP_BCN to PE"); + + /* Assoc resp reset */ + status = sme_update_add_ie(mac_handle, &update_ie, + eUPDATE_IE_ASSOC_RESP); + if (status == QDF_STATUS_E_FAILURE) + hdd_err("Could not pass ASSOC_RSP to PE"); + + /* Reset WNI_CFG_PROBE_RSP Flags */ + wlan_hdd_reset_prob_rspies(adapter); + } + + /* + * Note to restart sap after SSR driver needs below information + * and is not cleared/freed on purpose in case of SAP SSR + */ + if (!cds_is_driver_recovering()) { + clear_bit(SOFTAP_INIT_DONE, &adapter->event_flags); + qdf_mem_free(adapter->session.ap.beacon); + adapter->session.ap.beacon = NULL; + } + + /* Clear all the cached sta info */ + hdd_clear_cached_sta_info(adapter); + + /* + * If Do_Not_Break_Stream was enabled clear avoid channel list. + */ + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + if (policy_mgr_is_dnsc_set(vdev)) + wlan_hdd_send_avoid_freq_for_dnbs(hdd_ctx, 0); + hdd_objmgr_put_vdev(vdev); + } + +#ifdef WLAN_OPEN_SOURCE + cancel_work_sync(&adapter->ipv4_notifier_work); +#ifdef WLAN_NS_OFFLOAD + cancel_work_sync(&adapter->ipv6_notifier_work); +#endif +#endif + sap_release_vdev_ref(WLAN_HDD_GET_SAP_CTX_PTR(adapter)); + + if (adapter->device_mode == QDF_SAP_MODE) { + ucfg_ipa_flush_pending_vdev_events(hdd_ctx->pdev, + adapter->vdev_id); + } + + hdd_vdev_destroy(adapter); + + mutex_unlock(&hdd_ctx->sap_lock); + break; + case QDF_OCB_MODE: + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + cdp_clear_peer(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID, + sta_ctx->conn_info.peer_macaddr[0]); + hdd_deregister_hl_netdev_fc_timer(adapter); + hdd_deregister_tx_flow_control(adapter); + hdd_vdev_destroy(adapter); + break; + default: + break; + } + + if (adapter->scan_info.default_scan_ies) { + qdf_mem_free(adapter->scan_info.default_scan_ies); + adapter->scan_info.default_scan_ies = NULL; + adapter->scan_info.default_scan_ies_len = 0; + } + + /* This function should be invoked at the end of this api*/ + hdd_dump_func_call_map(); + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_deinit_all_adapters - deinit all adapters + * @hdd_ctx: HDD context + * @rtnl_held: True if RTNL lock held + * + */ +void hdd_deinit_all_adapters(struct hdd_context *hdd_ctx, bool rtnl_held) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + + hdd_enter(); + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_DEINIT_ALL_ADAPTERS) { + hdd_deinit_adapter(hdd_ctx, adapter, rtnl_held); + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_DEINIT_ALL_ADAPTERS); + } + + hdd_exit(); +} + +QDF_STATUS hdd_stop_all_adapters(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + + hdd_enter(); + + if (hdd_ctx->sap_pre_cac_work.fn) + cds_flush_work(&hdd_ctx->sap_pre_cac_work); + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_STOP_ALL_ADAPTERS) { + hdd_stop_adapter(hdd_ctx, adapter); + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_STOP_ALL_ADAPTERS); + } + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +void hdd_set_netdev_flags(struct hdd_adapter *adapter) +{ + bool enable_csum = false; + bool enable_lro; + enum QDF_OPMODE device_mode; + struct hdd_context *hdd_ctx; + ol_txrx_soc_handle soc; + uint64_t temp; + + if (!adapter || !adapter->dev) { + hdd_err("invalid input!"); + return; + } + device_mode = adapter->device_mode; + + hdd_ctx = adapter->hdd_ctx; + soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!soc || !hdd_ctx) { + hdd_err("invalid SOC or HDD context!"); + return; + } + + /* Determine device_mode specific configuration */ + + enable_lro = !!cdp_cfg_get(soc, cfg_dp_lro_enable); + enable_csum = !!cdp_cfg_get(soc, + cfg_dp_enable_ip_tcp_udp_checksum_offload); + switch (device_mode) { + case QDF_P2P_DEVICE_MODE: + case QDF_P2P_CLIENT_MODE: + enable_csum = !!cdp_cfg_get(soc, + cfg_dp_enable_p2p_ip_tcp_udp_checksum_offload); + break; + case QDF_P2P_GO_MODE: + enable_csum = !!cdp_cfg_get(soc, + cfg_dp_enable_p2p_ip_tcp_udp_checksum_offload); + enable_lro = false; + break; + case QDF_SAP_MODE: + enable_lro = false; + break; + case QDF_NDI_MODE: + case QDF_NAN_DISC_MODE: + enable_csum = !!cdp_cfg_get(soc, + cfg_dp_enable_nan_ip_tcp_udp_checksum_offload); + break; + default: + break; + } + + /* Set netdev flags */ + + /* + * In case of USB tethering, LRO is disabled. If SSR happened + * during that time, then as part of SSR init, do not enable + * the LRO again. Keep the LRO state same as before SSR. + */ + if (enable_lro && !(qdf_atomic_read(&hdd_ctx->vendor_disable_lro_flag))) + adapter->dev->features |= NETIF_F_LRO; + + if (enable_csum) + adapter->dev->features |= + (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); + + if (cdp_cfg_get(soc, cfg_dp_tso_enable) && enable_csum) + adapter->dev->features |= TSO_FEATURE_FLAGS; + + if (cdp_cfg_get(soc, cfg_dp_sg_enable)) + adapter->dev->features |= NETIF_F_SG; + + adapter->dev->features |= NETIF_F_RXCSUM; + temp = (uint64_t)adapter->dev->features; + + hdd_debug("adapter mode %u dev feature 0x%llx", device_mode, temp); +} + +static void hdd_reset_scan_operation(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_IBSS_MODE: + case QDF_P2P_DEVICE_MODE: + case QDF_NDI_MODE: + wlan_hdd_scan_abort(adapter); + wlan_hdd_cleanup_remain_on_channel_ctx(adapter); + if (adapter->device_mode == QDF_STA_MODE) { + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + wlan_cfg80211_sched_scan_stop(vdev); + hdd_objmgr_put_vdev(vdev); + } + } + break; + case QDF_P2P_GO_MODE: + wlan_hdd_cleanup_remain_on_channel_ctx(adapter); + break; + case QDF_SAP_MODE: + qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); + break; + default: + break; + } +} + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +/** + * hdd_adapter_abort_tx_flow() - Abort the tx flow control + * @pAdapter: pointer to hdd_adapter_t + * + * Resume tx and stop the tx flow control timer if the tx is paused + * and the flow control timer is running. This function is called by + * SSR to avoid the inconsistency of tx status before and after SSR. + * + * Return: void + */ +static void hdd_adapter_abort_tx_flow(struct hdd_adapter *adapter) +{ + if (adapter->hdd_stats.tx_rx_stats.is_txflow_paused && + QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state( + &adapter->tx_flow_control_timer)) { + hdd_tx_resume_timer_expired_handler(adapter); + qdf_mc_timer_stop(&adapter->tx_flow_control_timer); + } +} +#else +static void hdd_adapter_abort_tx_flow(struct hdd_adapter *adapter) +{ +} +#endif + +QDF_STATUS hdd_reset_all_adapters(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + bool value; + struct wlan_objmgr_vdev *vdev; + + hdd_enter(); + + ucfg_mlme_get_sap_internal_restart(hdd_ctx->psoc, &value); + + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_RESET_ALL_ADAPTERS) { + hdd_info("[SSR] reset adapter with device mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + + + hdd_adapter_abort_tx_flow(adapter); + if ((adapter->device_mode == QDF_STA_MODE) || + (adapter->device_mode == QDF_P2P_CLIENT_MODE)) { + /* Stop tdls timers */ + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + hdd_notify_tdls_reset_adapter(vdev); + hdd_objmgr_put_vdev(vdev); + } + } + + if (value && + adapter->device_mode == QDF_SAP_MODE) { + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + } else { + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + } + + /* + * Clear fc flag if it was set before SSR to avoid TX queues + * permanently stopped after SSR. + * Here WLAN_START_ALL_NETIF_QUEUE will actually not start any + * queue since it's blocked by reason WLAN_CONTROL_PATH. + */ + if (adapter->pause_map & (1 << WLAN_DATA_FLOW_CONTROL)) + wlan_hdd_netif_queue_control(adapter, + WLAN_START_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + + hdd_reset_scan_operation(hdd_ctx, adapter); + hdd_deinit_tx_rx(adapter); + + if (test_bit(WMM_INIT_DONE, &adapter->event_flags)) { + hdd_wmm_adapter_close(adapter); + clear_bit(WMM_INIT_DONE, &adapter->event_flags); + } + + if (adapter->device_mode != QDF_SAP_MODE && + adapter->device_mode != QDF_P2P_GO_MODE && + adapter->device_mode != QDF_FTM_MODE) + hdd_set_disconnect_status(adapter, false); + + hdd_stop_adapter(hdd_ctx, adapter); + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_RESET_ALL_ADAPTERS); + } + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +bool hdd_is_any_interface_open(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_IS_ANY_INTERFACE_OPEN; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_info("FTM mode, don't close the module"); + return true; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags) || + test_bit(SME_SESSION_OPENED, &adapter->event_flags)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return true; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return false; +} + +bool hdd_is_interface_up(struct hdd_adapter *adapter) +{ + if (test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) + return true; + else + return false; +} + +#if defined CFG80211_CONNECT_BSS || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) +#if defined CFG80211_CONNECT_TIMEOUT_REASON_CODE || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) +/** + * hdd_convert_timeout_reason() - Convert to kernel specific enum + * @timeout_reason: reason for connect timeout + * + * This function is used to convert host timeout + * reason enum to kernel specific enum. + * + * Return: nl timeout enum + */ +static enum nl80211_timeout_reason hdd_convert_timeout_reason( + tSirResultCodes timeout_reason) +{ + switch (timeout_reason) { + case eSIR_SME_JOIN_TIMEOUT_RESULT_CODE: + return NL80211_TIMEOUT_SCAN; + case eSIR_SME_AUTH_TIMEOUT_RESULT_CODE: + return NL80211_TIMEOUT_AUTH; + case eSIR_SME_ASSOC_TIMEOUT_RESULT_CODE: + return NL80211_TIMEOUT_ASSOC; + default: + return NL80211_TIMEOUT_UNSPECIFIED; + } +} + +/** + * hdd_cfg80211_connect_timeout() - API to send connection timeout reason + * @dev: network device + * @bssid: bssid to which we want to associate + * @timeout_reason: reason for connect timeout + * + * This API is used to send connection timeout reason to supplicant + * + * Return: void + */ +static void hdd_cfg80211_connect_timeout(struct net_device *dev, + const u8 *bssid, + tSirResultCodes timeout_reason) +{ + enum nl80211_timeout_reason nl_timeout_reason; + + nl_timeout_reason = hdd_convert_timeout_reason(timeout_reason); + + cfg80211_connect_timeout(dev, bssid, NULL, 0, GFP_KERNEL, + nl_timeout_reason); +} + +/** + * __hdd_connect_bss() - API to send connection status to supplicant + * @dev: network device + * @bssid: bssid to which we want to associate + * @req_ie: Request Information Element + * @req_ie_len: len of the req IE + * @resp_ie: Response IE + * @resp_ie_len: len of ht response IE + * @status: status + * @gfp: Kernel Flag + * @timeout_reason: reason for connect timeout + * + * Return: void + */ +static void __hdd_connect_bss(struct net_device *dev, const u8 *bssid, + struct cfg80211_bss *bss, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, int status, gfp_t gfp, + tSirResultCodes timeout_reason) +{ + enum nl80211_timeout_reason nl_timeout_reason; + + nl_timeout_reason = hdd_convert_timeout_reason(timeout_reason); + + cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len, + resp_ie, resp_ie_len, status, gfp, + nl_timeout_reason); +} +#else +#if defined CFG80211_CONNECT_TIMEOUT || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) +static void hdd_cfg80211_connect_timeout(struct net_device *dev, + const u8 *bssid, + tSirResultCodes timeout_reason) +{ + cfg80211_connect_timeout(dev, bssid, NULL, 0, GFP_KERNEL); +} +#endif + +static void __hdd_connect_bss(struct net_device *dev, const u8 *bssid, + struct cfg80211_bss *bss, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, int status, gfp_t gfp, + tSirResultCodes timeout_reason) +{ + cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len, + resp_ie, resp_ie_len, status, gfp); +} +#endif + +/** + * hdd_connect_bss() - API to send connection status to supplicant + * @dev: network device + * @bssid: bssid to which we want to associate + * @req_ie: Request Information Element + * @req_ie_len: len of the req IE + * @resp_ie: Response IE + * @resp_ie_len: len of ht response IE + * @status: status + * @gfp: Kernel Flag + * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp + * @timeout_reason: reason for connect timeout + * + * The API is a wrapper to send connection status to supplicant + * + * Return: Void + */ +#if defined CFG80211_CONNECT_TIMEOUT || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) +static void hdd_connect_bss(struct net_device *dev, const u8 *bssid, + struct cfg80211_bss *bss, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, int status, gfp_t gfp, + bool connect_timeout, + tSirResultCodes timeout_reason) +{ + if (connect_timeout) + hdd_cfg80211_connect_timeout(dev, bssid, timeout_reason); + else + __hdd_connect_bss(dev, bssid, bss, req_ie, req_ie_len, resp_ie, + resp_ie_len, status, gfp, timeout_reason); +} +#else +static void hdd_connect_bss(struct net_device *dev, const u8 *bssid, + struct cfg80211_bss *bss, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, int status, gfp_t gfp, + bool connect_timeout, + tSirResultCodes timeout_reason) +{ + __hdd_connect_bss(dev, bssid, bss, req_ie, req_ie_len, resp_ie, + resp_ie_len, status, gfp, timeout_reason); +} +#endif + +#if defined(WLAN_FEATURE_FILS_SK) +#if (defined(CFG80211_CONNECT_DONE) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) +#if defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) +/** + * hdd_populate_fils_params() - Populate FILS keys to connect response + * @fils_params: connect response to supplicant + * @fils_kek: FILS kek + * @fils_kek_len: FILS kek length + * @pmk: FILS PMK + * @pmk_len: FILS PMK length + * @pmkid: PMKID + * @fils_seq_num: FILS Seq number + * + * Return: None + */ +static void hdd_populate_fils_params(struct cfg80211_connect_resp_params + *fils_params, const uint8_t *fils_kek, + size_t fils_kek_len, const uint8_t *pmk, + size_t pmk_len, const uint8_t *pmkid, + uint16_t fils_seq_num) +{ + /* Increament seq number to be used for next FILS */ + fils_params->fils_erp_next_seq_num = fils_seq_num + 1; + fils_params->update_erp_next_seq_num = true; + fils_params->fils_kek = fils_kek; + fils_params->fils_kek_len = fils_kek_len; + fils_params->pmk = pmk; + fils_params->pmk_len = pmk_len; + fils_params->pmkid = pmkid; + hdd_debug("FILS erp_next_seq_num:%d", + fils_params->fils_erp_next_seq_num); +} +#else +static inline void hdd_populate_fils_params(struct cfg80211_connect_resp_params + *fils_params, const uint8_t + *fils_kek, size_t fils_kek_len, + const uint8_t *pmk, size_t pmk_len, + const uint8_t *pmkid, + uint16_t fils_seq_num) +{ } +#endif + +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +/** + * hdd_populate_fils_params() - Populate FILS keys to connect response + * @fils_params: connect response to supplicant + * @fils_kek: FILS kek + * @fils_kek_len: FILS kek length + * @pmk: FILS PMK + * @pmk_len: FILS PMK length + * @pmkid: PMKID + * @fils_seq_num: FILS Seq number + * + * Return: None + */ +static void hdd_populate_fils_params(struct cfg80211_connect_resp_params + *fils_params, const uint8_t *fils_kek, + size_t fils_kek_len, const uint8_t *pmk, + size_t pmk_len, const uint8_t *pmkid, + uint16_t fils_seq_num) +{ + /* Increament seq number to be used for next FILS */ + fils_params->fils.erp_next_seq_num = fils_seq_num + 1; + fils_params->fils.update_erp_next_seq_num = true; + fils_params->fils.kek = fils_kek; + fils_params->fils.kek_len = fils_kek_len; + fils_params->fils.pmk = pmk; + fils_params->fils.pmk_len = pmk_len; + fils_params->fils.pmkid = pmkid; + hdd_debug("FILS erp_next_seq_num:%d", + fils_params->fils.erp_next_seq_num); +} +#endif /* CFG80211_CONNECT_DONE */ + +#if defined(CFG80211_CONNECT_DONE) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + +void hdd_update_hlp_info(struct net_device *dev, + struct csr_roam_info *roam_info) +{ + struct sk_buff *skb; + uint16_t skb_len; + struct llc_snap_hdr_t *llc_hdr; + QDF_STATUS status; + uint8_t *hlp_data; + uint16_t hlp_data_len; + struct fils_join_rsp_params *roam_fils_params + = roam_info->fils_join_rsp; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + if (!roam_fils_params) { + hdd_err("FILS Roam Param NULL"); + return; + } + + if (!roam_fils_params->hlp_data_len) { + hdd_debug("FILS HLP Data NULL, len %d", + roam_fils_params->hlp_data_len); + return; + } + + hlp_data = roam_fils_params->hlp_data; + hlp_data_len = roam_fils_params->hlp_data_len; + + /* Calculate skb length */ + skb_len = (2 * ETH_ALEN) + hlp_data_len; + skb = qdf_nbuf_alloc(NULL, skb_len, 0, 4, false); + if (!skb) { + hdd_err("HLP packet nbuf alloc fails"); + return; + } + + qdf_mem_copy(skb_put(skb, ETH_ALEN), roam_fils_params->dst_mac.bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(skb_put(skb, ETH_ALEN), roam_fils_params->src_mac.bytes, + QDF_MAC_ADDR_SIZE); + + llc_hdr = (struct llc_snap_hdr_t *) hlp_data; + if (IS_SNAP(llc_hdr)) { + hlp_data += LLC_SNAP_HDR_OFFSET_ETHERTYPE; + hlp_data_len += LLC_SNAP_HDR_OFFSET_ETHERTYPE; + } + + qdf_mem_copy(skb_put(skb, hlp_data_len), hlp_data, hlp_data_len); + + /* + * This HLP packet is formed from HLP info encapsulated + * in assoc response frame which is AEAD encrypted. + * Hence, this checksum validation can be set unnecessary. + * i.e. network layer need not worry about checksum. + */ + skb->ip_summed = CHECKSUM_UNNECESSARY; + + status = hdd_rx_packet_cbk(adapter, skb); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Sending HLP packet fails"); + return; + } + hdd_debug("send HLP packet to netif successfully"); +} + +/** + * hdd_connect_done() - Wrapper API to call cfg80211_connect_done + * @dev: network device + * @bssid: bssid to which we want to associate + * @bss: cfg80211 bss info + * @roam_info: information about connected bss + * @req_ie: Request Information Element + * @req_ie_len: len of the req IE + * @resp_ie: Response IE + * @resp_ie_len: len of ht response IE + * @status: status + * @gfp: allocation flags + * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp + * @timeout_reason: reason for connect timeout + * + * This API is used as wrapper to send FILS key/sequence number + * params etc. to supplicant in case of FILS connection + * + * Return: None + */ +static void hdd_connect_done(struct net_device *dev, const u8 *bssid, + struct cfg80211_bss *bss, + struct csr_roam_info *roam_info, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, u16 status, + gfp_t gfp, bool connect_timeout, + tSirResultCodes timeout_reason) +{ + struct cfg80211_connect_resp_params fils_params; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct fils_join_rsp_params *roam_fils_params = + roam_info->fils_join_rsp; + + qdf_mem_zero(&fils_params, sizeof(fils_params)); + + if (!roam_fils_params) { + fils_params.status = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else { + fils_params.status = status; + fils_params.bssid = bssid; + fils_params.timeout_reason = + hdd_convert_timeout_reason(timeout_reason); + fils_params.req_ie = req_ie; + fils_params.req_ie_len = req_ie_len; + fils_params.resp_ie = resp_ie; + fils_params.resp_ie_len = resp_ie_len; + fils_params.bss = bss; + hdd_populate_fils_params(&fils_params, roam_fils_params->kek, + roam_fils_params->kek_len, + roam_fils_params->fils_pmk, + roam_fils_params->fils_pmk_len, + roam_fils_params->fils_pmkid, + roam_info->fils_seq_num); + hdd_save_gtk_params(adapter, roam_info, false); + } + hdd_debug("FILS indicate connect status %d", fils_params.status); + + cfg80211_connect_done(dev, &fils_params, gfp); + + if (roam_fils_params && roam_fils_params->hlp_data_len) + hdd_update_hlp_info(dev, roam_info); + + /* Clear all the FILS key info */ + if (roam_fils_params && roam_fils_params->fils_pmk) + qdf_mem_free(roam_fils_params->fils_pmk); + if (roam_fils_params) + qdf_mem_free(roam_fils_params); + roam_info->fils_join_rsp = NULL; +} +#else /* CFG80211_CONNECT_DONE */ +static inline void +hdd_connect_done(struct net_device *dev, const u8 *bssid, + struct cfg80211_bss *bss, struct csr_roam_info *roam_info, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, u16 status, + gfp_t gfp, bool connect_timeout, + tSirResultCodes timeout_reason) +{ } +#endif +#endif /* WLAN_FEATURE_FILS_SK */ + +#if defined(WLAN_FEATURE_FILS_SK) && \ + (defined(CFG80211_CONNECT_DONE) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))) +/** + * hdd_fils_update_connect_results() - API to send fils connection status to + * supplicant. + * @dev: network device + * @bssid: bssid to which we want to associate + * @bss: cfg80211 bss info + * @roam_info: information about connected bss + * @req_ie: Request Information Element + * @req_ie_len: len of the req IE + * @resp_ie: Response IE + * @resp_ie_len: len of ht response IE + * @status: status + * @gfp: allocation flags + * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp + * @timeout_reason: reason for connect timeout + * + * The API is a wrapper to send connection status to supplicant + * + * Return: 0 if success else failure + */ +static int hdd_fils_update_connect_results(struct net_device *dev, + const u8 *bssid, + struct cfg80211_bss *bss, + struct csr_roam_info *roam_info, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, u16 status, gfp_t gfp, + bool connect_timeout, + tSirResultCodes timeout_reason) +{ + hdd_enter(); + if (!roam_info || !roam_info->is_fils_connection) + return -EINVAL; + + hdd_connect_done(dev, bssid, bss, roam_info, req_ie, req_ie_len, + resp_ie, resp_ie_len, status, gfp, connect_timeout, + timeout_reason); + return 0; +} +#else +static inline int hdd_fils_update_connect_results(struct net_device *dev, + const u8 *bssid, + struct cfg80211_bss *bss, + struct csr_roam_info *roam_info, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, u16 status, gfp_t gfp, + bool connect_timeout, + tSirResultCodes timeout_reason) +{ + return -EINVAL; +} +#endif + +/** + * hdd_connect_result() - API to send connection status to supplicant + * @dev: network device + * @bssid: bssid to which we want to associate + * @roam_info: information about connected bss + * @req_ie: Request Information Element + * @req_ie_len: len of the req IE + * @resp_ie: Response IE + * @resp_ie_len: len of ht response IE + * @status: status + * @gfp: Kernel Flag + * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp + * @timeout_reason: reason for connect timeout + * + * The API is a wrapper to send connection status to supplicant + * and allow runtime suspend + * + * Return: Void + */ +void hdd_connect_result(struct net_device *dev, const u8 *bssid, + struct csr_roam_info *roam_info, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, u16 status, gfp_t gfp, + bool connect_timeout, + tSirResultCodes timeout_reason) +{ + struct hdd_adapter *adapter = netdev_priv(dev); + struct cfg80211_bss *bss = NULL; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (WLAN_STATUS_SUCCESS == status) { + struct ieee80211_channel *chan; + + chan = ieee80211_get_channel(adapter->wdev.wiphy, + roam_info->bss_desc->chan_freq); + bss = wlan_cfg80211_get_bss(adapter->wdev.wiphy, chan, bssid, + roam_info->u.pConnectedProfile->SSID.ssId, + roam_info->u.pConnectedProfile->SSID.length); + } + + if (hdd_fils_update_connect_results(dev, bssid, bss, + roam_info, req_ie, req_ie_len, resp_ie, + resp_ie_len, status, gfp, connect_timeout, + timeout_reason) != 0) { + hdd_connect_bss(dev, bssid, bss, req_ie, + req_ie_len, resp_ie, resp_ie_len, + status, gfp, connect_timeout, timeout_reason); + } + + qdf_runtime_pm_allow_suspend(&hdd_ctx->runtime_context.connect); + hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_CONNECT); +} +#else +void hdd_connect_result(struct net_device *dev, const u8 *bssid, + struct csr_roam_info *roam_info, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, u16 status, gfp_t gfp, + bool connect_timeout, + tSirResultCodes timeout_reason) +{ + struct hdd_adapter *adapter = netdev_priv(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + cfg80211_connect_result(dev, bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, status, gfp); + + qdf_runtime_pm_allow_suspend(&hdd_ctx->runtime_context.connect); + hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_CONNECT); +} +#endif + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +int wlan_hdd_set_mon_chan(struct hdd_adapter *adapter, qdf_freq_t freq, + uint32_t bandwidth) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct hdd_mon_set_ch_info *ch_info = &sta_ctx->ch_info; + QDF_STATUS status; + struct qdf_mac_addr bssid; + struct csr_roam_profile roam_profile; + struct ch_params ch_params; + enum phy_ch_width max_fw_bw; + enum phy_ch_width ch_width; + int ret; + + if (hdd_get_conparam() != QDF_GLOBAL_MONITOR_MODE) { + hdd_err("Not supported, device is not in monitor mode"); + return -EINVAL; + } + + /* Verify the BW before accepting this request */ + ch_width = bandwidth; + + if (ch_width > CH_WIDTH_10MHZ || + (!cds_is_sub_20_mhz_enabled() && ch_width > CH_WIDTH_160MHZ)) { + hdd_err("invalid BW received %d", ch_width); + return -EINVAL; + } + + max_fw_bw = sme_get_vht_ch_width(); + + hdd_debug("max fw BW %d ch width %d", max_fw_bw, ch_width); + if ((ch_width == CH_WIDTH_160MHZ && + max_fw_bw <= WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) || + (ch_width == CH_WIDTH_80P80MHZ && + max_fw_bw <= WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ)) { + hdd_err("FW does not support this BW %d max BW supported %d", + ch_width, max_fw_bw); + return -EINVAL; + } + + ret = hdd_validate_channel_and_bandwidth(adapter, freq, bandwidth); + if (ret) { + hdd_err("Invalid CH and BW combo"); + return ret; + } + + hdd_debug("Set monitor mode frequency %d", freq); + qdf_mem_zero(&roam_profile, sizeof(roam_profile)); + roam_profile.ChannelInfo.freq_list = &ch_info->freq; + roam_profile.ChannelInfo.numOfChannels = 1; + roam_profile.phyMode = ch_info->phy_mode; + roam_profile.ch_params.ch_width = bandwidth; + hdd_select_cbmode(adapter, freq, + &roam_profile.ch_params); + + qdf_mem_copy(bssid.bytes, adapter->mac_addr.bytes, + QDF_MAC_ADDR_SIZE); + + ch_params.ch_width = bandwidth; + wlan_reg_set_channel_params_for_freq(hdd_ctx->pdev, freq, 0, + &ch_params); + + if (ch_params.ch_width == CH_WIDTH_INVALID) { + hdd_err("Invalid capture channel or bandwidth for a country"); + return -EINVAL; + } + if (wlan_hdd_change_hw_mode_for_given_chnl(adapter, freq, + POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN)) { + hdd_err("Failed to change hw mode"); + return -EINVAL; + } + + if (adapter->monitor_mode_vdev_up_in_progress) { + hdd_err_rl("monitor mode vdev up in progress"); + return -EBUSY; + } + + status = qdf_event_reset(&adapter->qdf_monitor_mode_vdev_up_event); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err_rl("failed to reinit monitor mode vdev up event"); + return qdf_status_to_os_return(status); + } + adapter->monitor_mode_vdev_up_in_progress = true; + + status = sme_roam_channel_change_req(hdd_ctx->mac_handle, + bssid, &roam_profile.ch_params, + &roam_profile); + if (status) { + hdd_err("Status: %d Failed to set sme_roam Channel for monitor mode", + status); + adapter->monitor_mode_vdev_up_in_progress = false; + return qdf_status_to_os_return(status); + } + + adapter->mon_chan_freq = freq; + adapter->mon_bandwidth = bandwidth; + + /* block on a completion variable until vdev up success*/ + status = qdf_wait_for_event_completion( + &adapter->qdf_monitor_mode_vdev_up_event, + WLAN_MONITOR_MODE_VDEV_UP_EVT); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err_rl("monitor vdev up event time out vdev id: %d", + adapter->vdev_id); + if (adapter->qdf_monitor_mode_vdev_up_event.force_set) + /* + * SSR/PDR has caused shutdown, which has + * forcefully set the event. + */ + hdd_err_rl("monitor mode vdev up event forcefully set"); + else if (status == QDF_STATUS_E_TIMEOUT) + hdd_err("monitor mode vdev up timed out"); + else + hdd_err_rl("Failed monitor mode vdev up(status-%d)", + status); + + adapter->monitor_mode_vdev_up_in_progress = false; + } + + return qdf_status_to_os_return(status); +} +#endif + +#if defined MSM_PLATFORM && (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 19, 0)) +/** + * hdd_stop_p2p_go() - call cfg80211 API to stop P2P GO + * @adapter: pointer to adapter + * + * This function calls cfg80211 API to stop P2P GO + * + * Return: None + */ +static void hdd_stop_p2p_go(struct hdd_adapter *adapter) +{ + hdd_debug("[SSR] send stop ap to supplicant"); + cfg80211_ap_stopped(adapter->dev, GFP_KERNEL); +} + +static inline void hdd_delete_sta(struct hdd_adapter *adapter) +{ +} + +#else +static void hdd_stop_p2p_go(struct hdd_adapter *adapter) +{ + hdd_debug("[SSR] send stop iface ap to supplicant"); + cfg80211_stop_iface(adapter->hdd_ctx->wiphy, &adapter->wdev, + GFP_KERNEL); +} + +/** + * hdd_delete_sta() - call cfg80211 API to delete STA + * @adapter: pointer to adapter + * + * This function calls cfg80211 API to delete STA + * + * Return: None + */ +static void hdd_delete_sta(struct hdd_adapter *adapter) +{ + struct qdf_mac_addr bcast_mac = QDF_MAC_ADDR_BCAST_INIT; + + hdd_debug("[SSR] send restart supplicant"); + /* event supplicant to restart */ + cfg80211_del_sta(adapter->dev, + (const u8 *)&bcast_mac.bytes[0], + GFP_KERNEL); +} +#endif + +QDF_STATUS hdd_start_all_adapters(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + eConnectionState conn_state; + bool value; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_START_ALL_ADAPTERS; + + hdd_enter(); + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (!hdd_is_interface_up(adapter) && + adapter->device_mode != QDF_NDI_MODE) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + hdd_debug("[SSR] start adapter with device mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + + hdd_wmm_init(adapter); + + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_DEVICE_MODE: + case QDF_NAN_DISC_MODE: + + conn_state = (WLAN_HDD_GET_STATION_CTX_PTR(adapter)) + ->conn_info.conn_state; + + hdd_start_station_adapter(adapter); + /* Open the gates for HDD to receive Wext commands */ + adapter->is_link_up_service_needed = false; + + /* Indicate disconnect event to supplicant + * if associated previously + */ + if (eConnectionState_Associated == conn_state || + eConnectionState_IbssConnected == conn_state || + eConnectionState_NotConnected == conn_state || + eConnectionState_IbssDisconnected == conn_state || + eConnectionState_Disconnecting == conn_state) { + union iwreq_data wrqu; + + memset(&wrqu, '\0', sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN); + wireless_send_event(adapter->dev, SIOCGIWAP, + &wrqu, NULL); + adapter->session.station. + hdd_reassoc_scenario = false; + + /* indicate disconnected event to nl80211 */ + wlan_hdd_cfg80211_indicate_disconnect( + adapter, true, + eSIR_MAC_DEVICE_RECOVERY, + NULL, 0); + } else if (eConnectionState_Connecting == conn_state) { + /* + * Indicate connect failure to supplicant if we + * were in the process of connecting + */ + hdd_connect_result(adapter->dev, NULL, NULL, + NULL, 0, NULL, 0, + WLAN_STATUS_ASSOC_DENIED_UNSPEC, + GFP_KERNEL, false, 0); + } + if ((adapter->device_mode == QDF_NAN_DISC_MODE || + (adapter->device_mode == QDF_STA_MODE && + !ucfg_nan_is_vdev_creation_allowed( + hdd_ctx->psoc))) && + cds_is_driver_recovering()) + ucfg_nan_disable_ind_to_userspace( + hdd_ctx->psoc); + + hdd_register_tx_flow_control(adapter, + hdd_tx_resume_timer_expired_handler, + hdd_tx_resume_cb, + hdd_tx_flow_control_is_pause); + + hdd_register_hl_netdev_fc_timer( + adapter, + hdd_tx_resume_timer_expired_handler); + + hdd_lpass_notify_start(hdd_ctx, adapter); + hdd_nud_ignore_tracking(adapter, false); + hdd_mic_enable_work(adapter); + break; + + case QDF_SAP_MODE: + ucfg_mlme_get_sap_internal_restart(hdd_ctx->psoc, + &value); + if (value) + hdd_start_ap_adapter(adapter); + + hdd_mic_enable_work(adapter); + + break; + + case QDF_P2P_GO_MODE: + hdd_delete_sta(adapter); + break; + case QDF_MONITOR_MODE: + if (wlan_hdd_is_session_type_monitor( + QDF_MONITOR_MODE)) { + hdd_set_pktcapture_cb(adapter->dev, + OL_TXRX_PDEV_ID); + break; + } + hdd_start_station_adapter(adapter); + hdd_set_mon_rx_cb(adapter->dev); + + /* + * Do not set channel for monitor mode if monitor iface + * went down during SSR, as for set channels host sends + * vdev start command to FW. For the interfaces went + * down during SSR, host stops those adapters by sending + * vdev stop/down/delete commands to FW. So FW doesn't + * sends response for vdev start and vdev start response + * timer expires and thus host triggers ASSERT. + */ + if (!test_bit(DOWN_DURING_SSR, &adapter->event_flags)) + wlan_hdd_set_mon_chan( + adapter, adapter->mon_chan_freq, + adapter->mon_bandwidth); + break; + case QDF_NDI_MODE: + hdd_ndi_start(adapter->dev->name, 0); + default: + break; + } + /* + * Action frame registered in one adapter which will + * applicable to all interfaces + */ + wlan_hdd_cfg80211_register_frames(adapter); + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (!hdd_is_interface_up(adapter)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + if (adapter->device_mode == QDF_P2P_GO_MODE) + hdd_stop_p2p_go(adapter); + + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +void hdd_adapter_dev_hold_debug(struct hdd_adapter *adapter, + wlan_net_dev_ref_dbgid dbgid) +{ + if (dbgid >= NET_DEV_HOLD_ID_MAX) { + hdd_err("Invalid debug id: %d", dbgid); + QDF_BUG(0); + } + dev_hold(adapter->dev); + qdf_atomic_inc(&adapter->net_dev_hold_ref_count[dbgid]); +} + +void hdd_adapter_dev_put_debug(struct hdd_adapter *adapter, + wlan_net_dev_ref_dbgid dbgid) +{ + if (dbgid >= NET_DEV_HOLD_ID_MAX) { + hdd_err("Invalid debug id: %d", dbgid); + QDF_BUG(0); + } + + if (qdf_atomic_dec_return( + &adapter->net_dev_hold_ref_count[dbgid]) < 0) { + hdd_err("dev_put detected without dev_hold for debug id: %s", + net_dev_ref_debug_string_from_id(dbgid)); + QDF_BUG(0); + } + + dev_put(adapter->dev); +} + +QDF_STATUS hdd_get_front_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter **out_adapter) +{ + QDF_STATUS status; + qdf_list_node_t *node; + + *out_adapter = NULL; + + qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock); + status = qdf_list_peek_front(&hdd_ctx->hdd_adapters, &node); + qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + *out_adapter = qdf_container_of(node, struct hdd_adapter, node); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_get_next_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *current_adapter, + struct hdd_adapter **out_adapter) +{ + QDF_STATUS status; + qdf_list_node_t *node; + + *out_adapter = NULL; + + qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock); + status = qdf_list_peek_next(&hdd_ctx->hdd_adapters, + ¤t_adapter->node, + &node); + qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + *out_adapter = qdf_container_of(node, struct hdd_adapter, node); + + return status; +} + +QDF_STATUS hdd_get_front_adapter_no_lock(struct hdd_context *hdd_ctx, + struct hdd_adapter **out_adapter) +{ + QDF_STATUS status; + qdf_list_node_t *node; + + *out_adapter = NULL; + + status = qdf_list_peek_front(&hdd_ctx->hdd_adapters, &node); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + *out_adapter = qdf_container_of(node, struct hdd_adapter, node); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_get_next_adapter_no_lock(struct hdd_context *hdd_ctx, + struct hdd_adapter *current_adapter, + struct hdd_adapter **out_adapter) +{ + QDF_STATUS status; + qdf_list_node_t *node; + + if (!current_adapter) + return QDF_STATUS_E_INVAL; + + *out_adapter = NULL; + + status = qdf_list_peek_next(&hdd_ctx->hdd_adapters, + ¤t_adapter->node, + &node); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + *out_adapter = qdf_container_of(node, struct hdd_adapter, node); + + return status; +} + +QDF_STATUS hdd_remove_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock); + status = qdf_list_remove_node(&hdd_ctx->hdd_adapters, &adapter->node); + qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock); + + return status; +} + +QDF_STATUS hdd_remove_front_adapter(struct hdd_context *hdd_ctx, + struct hdd_adapter **out_adapter) +{ + QDF_STATUS status; + qdf_list_node_t *node; + + *out_adapter = NULL; + + qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock); + status = qdf_list_remove_front(&hdd_ctx->hdd_adapters, &node); + qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + *out_adapter = qdf_container_of(node, struct hdd_adapter, node); + + return status; +} + +QDF_STATUS hdd_add_adapter_back(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock); + status = qdf_list_insert_back(&hdd_ctx->hdd_adapters, &adapter->node); + qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock); + + return status; +} + +QDF_STATUS hdd_add_adapter_front(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock); + status = qdf_list_insert_front(&hdd_ctx->hdd_adapters, &adapter->node); + qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock); + + return status; +} + +QDF_STATUS hdd_adapter_iterate(hdd_adapter_iterate_cb cb, void *context) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *cache[HDD_MAX_ADAPTERS]; + struct hdd_adapter *adapter; + uint32_t n_cache = 0; + QDF_STATUS ret = QDF_STATUS_SUCCESS; + QDF_STATUS status; + int i; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (unlikely(!hdd_ctx)) { + hdd_err("HDD context is Null"); + return QDF_STATUS_E_FAILURE; + } + + qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock); + for (hdd_get_front_adapter_no_lock(hdd_ctx, &adapter); adapter; + hdd_get_next_adapter_no_lock(hdd_ctx, adapter, &adapter)) { + cache[n_cache++] = adapter; + } + qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock); + + for (i = 0; i < n_cache - 1; i++) { + adapter = hdd_adapter_get_by_reference(hdd_ctx, cache[i]); + if (!adapter) { + /* + * detected remove while iterating + * concurrency failure + */ + ret = QDF_STATUS_E_FAILURE; + continue; + } + + status = cb(adapter, context); + hdd_adapter_put(adapter); + if (status != QDF_STATUS_SUCCESS) + return status; + } + + return ret; +} + +struct hdd_adapter *hdd_get_adapter_by_rand_macaddr( + struct hdd_context *hdd_ctx, tSirMacAddr mac_addr) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_GET_ADAPTER_BY_RAND_MACADDR; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if ((adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE || + adapter->device_mode == QDF_P2P_DEVICE_MODE) && + ucfg_p2p_check_random_mac(hdd_ctx->psoc, + adapter->vdev_id, mac_addr)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return adapter; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return NULL; +} + +struct hdd_adapter *hdd_get_adapter_by_macaddr(struct hdd_context *hdd_ctx, + tSirMacAddr mac_addr) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_ADAPTER_BY_MACADDR; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (!qdf_mem_cmp(adapter->mac_addr.bytes, + mac_addr, sizeof(tSirMacAddr))) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return adapter; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return NULL; +} + +struct hdd_adapter *hdd_get_adapter_by_vdev(struct hdd_context *hdd_ctx, + uint32_t vdev_id) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_ADAPTER_BY_VDEV; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->vdev_id == vdev_id) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return adapter; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return NULL; +} + +struct hdd_adapter *hdd_adapter_get_by_reference(struct hdd_context *hdd_ctx, + struct hdd_adapter *reference) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_ADAPTER_GET_BY_REFERENCE; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter == reference) { + dev_hold(adapter->dev); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + break; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return adapter; +} + +void hdd_adapter_put(struct hdd_adapter *adapter) +{ + dev_put(adapter->dev); +} + +struct hdd_adapter *hdd_get_adapter_by_iface_name(struct hdd_context *hdd_ctx, + const char *iface_name) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_ADAPTER_BY_IFACE_NAME; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (!qdf_str_cmp(adapter->dev->name, iface_name)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return adapter; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return NULL; +} + +/** + * hdd_get_adapter() - to get adapter matching the mode + * @hdd_ctx: hdd context + * @mode: adapter mode + * + * This routine will return the pointer to adapter matching + * with the passed mode. + * + * Return: pointer to adapter or null + */ +struct hdd_adapter *hdd_get_adapter(struct hdd_context *hdd_ctx, + enum QDF_OPMODE mode) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_ADAPTER; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->device_mode == mode) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return adapter; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return NULL; +} + +enum QDF_OPMODE hdd_get_device_mode(uint32_t vdev_id) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("Invalid HDD context"); + return QDF_MAX_NO_OF_MODE; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("Invalid HDD adapter"); + return QDF_MAX_NO_OF_MODE; + } + + return adapter->device_mode; +} + +uint32_t hdd_get_operating_chan_freq(struct hdd_context *hdd_ctx, + enum QDF_OPMODE mode) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + uint32_t oper_chan_freq = 0; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_OPERATING_CHAN_FREQ; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (mode == adapter->device_mode) { + oper_chan_freq = + hdd_get_adapter_home_channel(adapter); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + break; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return oper_chan_freq; +} + +static inline QDF_STATUS hdd_unregister_wext_all_adapters(struct hdd_context * + hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_UNREGISTER_WEXT_ALL_ADAPTERS; + + hdd_enter(); + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE || + adapter->device_mode == QDF_IBSS_MODE || + adapter->device_mode == QDF_P2P_DEVICE_MODE || + adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + hdd_unregister_wext(adapter->dev); + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_abort_mac_scan_all_adapters(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_ABORT_MAC_SCAN_ALL_ADAPTERS; + + hdd_enter(); + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE || + adapter->device_mode == QDF_IBSS_MODE || + adapter->device_mode == QDF_P2P_DEVICE_MODE || + adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + wlan_abort_scan(hdd_ctx->pdev, INVAL_PDEV_ID, + adapter->vdev_id, INVALID_SCAN_ID, + true); + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_abort_sched_scan_all_adapters() - stops scheduled (PNO) scans for all + * adapters + * @hdd_ctx: The HDD context containing the adapters to operate on + * + * return: QDF_STATUS_SUCCESS + */ +static QDF_STATUS hdd_abort_sched_scan_all_adapters(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + int err; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_ABORT_SCHED_SCAN_ALL_ADAPTERS; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE || + adapter->device_mode == QDF_IBSS_MODE || + adapter->device_mode == QDF_P2P_DEVICE_MODE || + adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + err = wlan_hdd_sched_scan_stop(adapter->dev); + if (err) + hdd_err("Unable to stop scheduled scan"); + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return QDF_STATUS_SUCCESS; +} + +void hdd_set_disconnect_status(struct hdd_adapter *adapter, bool status) +{ + qdf_mutex_acquire(&adapter->disconnection_status_lock); + adapter->disconnection_in_progress = status; + qdf_mutex_release(&adapter->disconnection_status_lock); +} + +/** + * hdd_unregister_notifiers - Unregister netdev notifiers. + * @hdd_ctx: HDD context + * + * Unregister netdev notifiers like IPv4 and IPv6. + * + * Return: None. + */ +void hdd_unregister_notifiers(struct hdd_context *hdd_ctx) +{ + hdd_wlan_unregister_pm_qos_notifier(hdd_ctx); + hdd_nud_unregister_netevent_notifier(hdd_ctx); + hdd_wlan_unregister_ip6_notifier(hdd_ctx); + + unregister_inetaddr_notifier(&hdd_ctx->ipv4_notifier); +} + +/** + * hdd_exit_netlink_services - Exit netlink services + * @hdd_ctx: HDD context + * + * Exit netlink services like cnss_diag, cesium netlink socket, ptt socket and + * nl service. + * + * Return: None. + */ +static void hdd_exit_netlink_services(struct hdd_context *hdd_ctx) +{ + spectral_scan_deactivate_service(); + cnss_diag_deactivate_service(); + hdd_close_cesium_nl_sock(); + ptt_sock_deactivate_svc(); + hdd_deactivate_wifi_pos(); + + nl_srv_exit(); +} + +/** + * hdd_init_netlink_services- Init netlink services + * @hdd_ctx: HDD context + * + * Init netlink services like cnss_diag, cesium netlink socket, ptt socket and + * nl service. + * + * Return: 0 on success and errno on failure. + */ +static int hdd_init_netlink_services(struct hdd_context *hdd_ctx) +{ + int ret; + + ret = wlan_hdd_nl_init(hdd_ctx); + if (ret) { + hdd_err("nl_srv_init failed: %d", ret); + goto out; + } + cds_set_radio_index(hdd_ctx->radio_index); + + ret = hdd_activate_wifi_pos(hdd_ctx); + if (ret) { + hdd_err("hdd_activate_wifi_pos failed: %d", ret); + goto err_nl_srv; + } + + ptt_sock_activate_svc(); + + ret = hdd_open_cesium_nl_sock(); + if (ret) + hdd_err("hdd_open_cesium_nl_sock failed ret: %d", ret); + + ret = cnss_diag_activate_service(); + if (ret) { + hdd_err("cnss_diag_activate_service failed: %d", ret); + goto err_close_cesium; + } + + spectral_scan_activate_service(hdd_ctx); + + return 0; + +err_close_cesium: + hdd_close_cesium_nl_sock(); + ptt_sock_deactivate_svc(); + hdd_deactivate_wifi_pos(); +err_nl_srv: + nl_srv_exit(); +out: + return ret; +} + +/** + * hdd_rx_wake_lock_destroy() - Destroy RX wakelock + * @hdd_ctx: HDD context. + * + * Destroy RX wakelock. + * + * Return: None. + */ +static void hdd_rx_wake_lock_destroy(struct hdd_context *hdd_ctx) +{ + qdf_wake_lock_destroy(&hdd_ctx->rx_wake_lock); +} + +/** + * hdd_rx_wake_lock_create() - Create RX wakelock + * @hdd_ctx: HDD context. + * + * Create RX wakelock. + * + * Return: None. + */ +static void hdd_rx_wake_lock_create(struct hdd_context *hdd_ctx) +{ + qdf_wake_lock_create(&hdd_ctx->rx_wake_lock, "qcom_rx_wakelock"); +} + +/** + * hdd_context_deinit() - Deinitialize HDD context + * @hdd_ctx: HDD context. + * + * Deinitialize HDD context along with all the feature specific contexts but + * do not free hdd context itself. Caller of this API is supposed to free + * HDD context. + * + * return: 0 on success and errno on failure. + */ +static int hdd_context_deinit(struct hdd_context *hdd_ctx) +{ + qdf_wake_lock_destroy(&hdd_ctx->monitor_mode_wakelock); + + wlan_hdd_cfg80211_deinit(hdd_ctx->wiphy); + + hdd_sap_context_destroy(hdd_ctx); + + hdd_rx_wake_lock_destroy(hdd_ctx); + + hdd_scan_context_destroy(hdd_ctx); + + qdf_list_destroy(&hdd_ctx->hdd_adapters); + + return 0; +} + +void hdd_context_destroy(struct hdd_context *hdd_ctx) +{ + wlan_hdd_sar_timers_deinit(hdd_ctx); + + cds_set_context(QDF_MODULE_ID_HDD, NULL); + + hdd_exit_netlink_services(hdd_ctx); + + hdd_context_deinit(hdd_ctx); + + hdd_objmgr_release_and_destroy_psoc(hdd_ctx); + + qdf_mem_free(hdd_ctx->config); + hdd_ctx->config = NULL; + cfg_release(); + + qdf_delayed_work_destroy(&hdd_ctx->psoc_idle_timeout_work); + wiphy_free(hdd_ctx->wiphy); +} + +/** + * wlan_destroy_bug_report_lock() - Destroy bug report lock + * + * This function is used to destroy bug report lock + * + * Return: None + */ +static void wlan_destroy_bug_report_lock(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + hdd_err("cds context is NULL"); + return; + } + + qdf_spinlock_destroy(&p_cds_context->bug_report_lock); +} + +#ifdef DISABLE_CHANNEL_LIST +static void wlan_hdd_cache_chann_mutex_destroy(struct hdd_context *hdd_ctx) +{ + qdf_mutex_destroy(&hdd_ctx->cache_channel_lock); +} +#else +static void wlan_hdd_cache_chann_mutex_destroy(struct hdd_context *hdd_ctx) +{ +} +#endif + +void hdd_wlan_exit(struct hdd_context *hdd_ctx) +{ + struct wiphy *wiphy = hdd_ctx->wiphy; + + hdd_enter(); + + hdd_debugfs_ini_config_deinit(hdd_ctx); + hdd_debugfs_mws_coex_info_deinit(hdd_ctx); + hdd_psoc_idle_timer_stop(hdd_ctx); + + /* + * Powersave Offload Case + * Disable Idle Power Save Mode + */ + hdd_set_idle_ps_config(hdd_ctx, false); + /* clear the scan queue in all the scenarios */ + wlan_cfg80211_cleanup_scan_queue(hdd_ctx->pdev, NULL); + + if (hdd_ctx->driver_status != DRIVER_MODULES_CLOSED) { + hdd_unregister_wext_all_adapters(hdd_ctx); + /* + * Cancel any outstanding scan requests. We are about to close + * all of our adapters, but an adapter structure is what SME + * passes back to our callback function. Hence if there + * are any outstanding scan requests then there is a + * race condition between when the adapter is closed and + * when the callback is invoked. We try to resolve that + * race condition here by canceling any outstanding scans + * before we close the adapters. + * Note that the scans may be cancelled in an asynchronous + * manner, so ideally there needs to be some kind of + * synchronization. Rather than introduce a new + * synchronization here, we will utilize the fact that we are + * about to Request Full Power, and since that is synchronized, + * the expectation is that by the time Request Full Power has + * completed, all scans will be cancelled + */ + hdd_abort_mac_scan_all_adapters(hdd_ctx); + hdd_abort_sched_scan_all_adapters(hdd_ctx); + + if (wlan_hdd_is_session_type_monitor(QDF_MONITOR_MODE)) + hdd_reset_pktcapture_cb(OL_TXRX_PDEV_ID); + + hdd_stop_all_adapters(hdd_ctx); + hdd_deinit_all_adapters(hdd_ctx, false); + } + + unregister_netdevice_notifier(&hdd_netdev_notifier); + + qdf_dp_trace_deinit(); + + hdd_wlan_stop_modules(hdd_ctx, false); + + hdd_driver_memdump_deinit(); + + qdf_nbuf_deinit_replenish_timer(); + + if (QDF_GLOBAL_MONITOR_MODE == hdd_get_conparam()) { + hdd_info("Release wakelock for monitor mode!"); + qdf_wake_lock_release(&hdd_ctx->monitor_mode_wakelock, + WIFI_POWER_EVENT_WAKELOCK_MONITOR_MODE); + } + + qdf_spinlock_destroy(&hdd_ctx->hdd_adapter_lock); + qdf_spinlock_destroy(&hdd_ctx->connection_status_lock); + wlan_hdd_cache_chann_mutex_destroy(hdd_ctx); + + osif_request_manager_deinit(); + + hdd_close_all_adapters(hdd_ctx, false); + + wlansap_global_deinit(); + /* + * If there is re_init failure wiphy would have already de-registered + * check the wiphy status before un-registering again + */ + if (wiphy && wiphy->registered) { + wiphy_unregister(wiphy); + wlan_hdd_cfg80211_deinit(wiphy); + hdd_lpass_notify_stop(hdd_ctx); + } + + hdd_exit_netlink_services(hdd_ctx); +#ifdef FEATURE_WLAN_CH_AVOID + mutex_destroy(&hdd_ctx->avoid_freq_lock); +#endif + + /* This function should be invoked at the end of this api*/ + hdd_dump_func_call_map(); +} + +/** + * hdd_wlan_notify_modem_power_state() - notify FW with modem power status + * @state: state + * + * This function notifies FW with modem power status + * + * Return: 0 if successful, error number otherwise + */ +int hdd_wlan_notify_modem_power_state(int state) +{ + int status; + QDF_STATUS qdf_status; + struct hdd_context *hdd_ctx; + mac_handle_t mac_handle; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) + return -EINVAL; + + qdf_status = sme_notify_modem_power_state(mac_handle, state); + if (QDF_STATUS_SUCCESS != qdf_status) { + hdd_err("Fail to send notification with modem power state %d", + state); + return -EINVAL; + } + return 0; +} + +/** + * + * hdd_post_cds_enable_config() - HDD post cds start config helper + * @adapter - Pointer to the HDD + * + * Return: None + */ +QDF_STATUS hdd_post_cds_enable_config(struct hdd_context *hdd_ctx) +{ + QDF_STATUS qdf_ret_status; + + /* + * Send ready indication to the HDD. This will kick off the MAC + * into a 'running' state and should kick off an initial scan. + */ + qdf_ret_status = sme_hdd_ready_ind(hdd_ctx->mac_handle); + if (!QDF_IS_STATUS_SUCCESS(qdf_ret_status)) { + hdd_err("sme_hdd_ready_ind() failed with status code %08d [x%08x]", + qdf_ret_status, qdf_ret_status); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +struct hdd_adapter *hdd_get_first_valid_adapter(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_FIRST_VALID_ADAPTER; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter && adapter->magic == WLAN_HDD_ADAPTER_MAGIC) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return adapter; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return NULL; +} + +/** + * hdd_rx_mic_error_ind() - MIC error indication handler + * @psoc: opaque handle for UMAC psoc object + * @pdev_id: physical device instance id + * @mic_failure_info: mic failure information + * + * This function indicates the Mic failure to the supplicant + * + * Return: None + */ +static void +hdd_rx_mic_error_ind(struct cdp_ctrl_objmgr_psoc *psoc, uint8_t pdev_id, + struct cdp_rx_mic_err_info *mic_failure_info) +{ + struct wiphy *wiphy; + struct pdev_osif_priv *pdev_priv; + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + struct hdd_mic_error_info *hdd_mic_info; + struct wlan_objmgr_pdev *pdev; + + if (!psoc) + return; + + pdev = wlan_objmgr_get_pdev_by_id((struct wlan_objmgr_psoc *)psoc, + pdev_id, WLAN_MLME_SB_ID); + if (!pdev) + return; + + pdev_priv = wlan_pdev_get_ospriv(pdev); + wiphy = pdev_priv->wiphy; + hdd_ctx = wiphy_priv(wiphy); + + if (wlan_hdd_validate_context(hdd_ctx)) + goto release_ref_and_return; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, mic_failure_info->vdev_id); + if (hdd_validate_adapter(adapter)) + goto release_ref_and_return; + + hdd_mic_info = qdf_mem_malloc(sizeof(*hdd_mic_info)); + if (!hdd_mic_info) + goto release_ref_and_return; + + qdf_copy_macaddr(&hdd_mic_info->ta_mac_addr, + &mic_failure_info->ta_mac_addr); + hdd_mic_info->multicast = mic_failure_info->multicast; + hdd_mic_info->key_id = mic_failure_info->key_id; + qdf_mem_copy(&hdd_mic_info->tsc, &mic_failure_info->tsc, + SIR_CIPHER_SEQ_CTR_SIZE); + hdd_mic_info->vdev_id = mic_failure_info->vdev_id; + + qdf_spin_lock_bh(&adapter->mic_work.lock); + if (adapter->mic_work.status != MIC_INITIALIZED) { + qdf_spin_unlock_bh(&adapter->mic_work.lock); + qdf_mem_free(hdd_mic_info); + goto release_ref_and_return; + } + /* + * Store mic error info pointer in adapter + * for freeing up the alocated memory in case + * the work scheduled below is flushed or deinitialized. + */ + adapter->mic_work.status = MIC_SCHEDULED; + adapter->mic_work.info = hdd_mic_info; + qdf_sched_work(0, &adapter->mic_work.work); + qdf_spin_unlock_bh(&adapter->mic_work.lock); + +release_ref_and_return: + wlan_objmgr_pdev_release_ref(pdev, WLAN_MLME_SB_ID); +} + +/* wake lock APIs for HDD */ +void hdd_prevent_suspend(uint32_t reason) +{ + qdf_wake_lock_acquire(&wlan_wake_lock, reason); +} + +void hdd_allow_suspend(uint32_t reason) +{ + qdf_wake_lock_release(&wlan_wake_lock, reason); +} + +void hdd_prevent_suspend_timeout(uint32_t timeout, uint32_t reason) +{ + cds_host_diag_log_work(&wlan_wake_lock, timeout, reason); + qdf_wake_lock_timeout_acquire(&wlan_wake_lock, timeout); +} + +/* Initialize channel list in sme based on the country code */ +QDF_STATUS hdd_set_sme_chan_list(struct hdd_context *hdd_ctx) +{ + return sme_init_chan_list(hdd_ctx->mac_handle, + hdd_ctx->reg.alpha2, + hdd_ctx->reg.cc_src); +} + +/** + * hdd_is_5g_supported() - check if hardware supports 5GHz + * @hdd_ctx: Pointer to the hdd context + * + * HDD function to know if hardware supports 5GHz + * + * Return: true if hardware supports 5GHz + */ +bool hdd_is_5g_supported(struct hdd_context *hdd_ctx) +{ + if (!hdd_ctx) + return true; + + if (hdd_ctx->curr_band != BAND_2G) + return true; + else + return false; +} + +bool hdd_is_2g_supported(struct hdd_context *hdd_ctx) +{ + if (!hdd_ctx) + return false; + + if (hdd_ctx->curr_band != BAND_5G) + return true; + else + return false; +} + +static int hdd_wiphy_init(struct hdd_context *hdd_ctx) +{ + struct wiphy *wiphy; + int ret_val; + uint32_t channel_bonding_mode; + + wiphy = hdd_ctx->wiphy; + + /* + * The channel information in + * wiphy needs to be initialized before wiphy registration + */ + ret_val = hdd_regulatory_init(hdd_ctx, wiphy); + if (ret_val) { + hdd_err("regulatory init failed"); + return ret_val; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) + wiphy->wowlan = &wowlan_support_reg_init; +#else + wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | + WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_4WAY_HANDSHAKE | + WIPHY_WOWLAN_RFKILL_RELEASE; + + wiphy->wowlan.n_patterns = (WOW_MAX_FILTER_LISTS * + WOW_MAX_FILTERS_PER_LIST); + wiphy->wowlan.pattern_min_len = WOW_MIN_PATTERN_SIZE; + wiphy->wowlan.pattern_max_len = WOW_MAX_PATTERN_SIZE; +#endif + ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, + &channel_bonding_mode); + if (hdd_ctx->obss_scan_offload) { + hdd_debug("wmi_service_obss_scan supported"); + } else if (channel_bonding_mode) { + hdd_debug("enable wpa_supp obss_scan"); + wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN; + } + + /* registration of wiphy dev with cfg80211 */ + ret_val = wlan_hdd_cfg80211_register(wiphy); + if (0 > ret_val) { + hdd_err("wiphy registration failed"); + return ret_val; + } + + /* Check the kernel version for upstream commit aced43ce780dc5 that + * has support for processing user cell_base hints when wiphy is + * self managed or check the backport flag for the same. + */ +#if defined CFG80211_USER_HINT_CELL_BASE_SELF_MANAGED || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) + hdd_send_wiphy_regd_sync_event(hdd_ctx); +#endif + + pld_increment_driver_load_cnt(hdd_ctx->parent_dev); + + return ret_val; +} + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +/** + * hdd_display_periodic_stats() - Function to display periodic stats + * @hdd_ctx - handle to hdd context + * @bool data_in_interval - true, if data detected in bw time interval + * + * The periodicity is determined by hdd_ctx->config->periodic_stats_disp_time. + * Stats show up in wlan driver logs. + * + * Returns: None + */ +static void hdd_display_periodic_stats(struct hdd_context *hdd_ctx, + bool data_in_interval) +{ + static uint32_t counter; + static bool data_in_time_period; + ol_txrx_soc_handle soc; + uint32_t periodic_stats_disp_time = 0; + + ucfg_mlme_stats_get_periodic_display_time(hdd_ctx->psoc, + &periodic_stats_disp_time); + if (!periodic_stats_disp_time) + return; + + soc = cds_get_context(QDF_MODULE_ID_SOC); + if (!soc) { + hdd_err("soc is NULL"); + return; + } + + counter++; + if (data_in_interval) + data_in_time_period = data_in_interval; + + if (counter * hdd_ctx->config->bus_bw_compute_interval >= + periodic_stats_disp_time * 1000) { + if (data_in_time_period) { + wlan_hdd_display_txrx_stats(hdd_ctx); + dp_txrx_ext_dump_stats(soc, CDP_DP_RX_THREAD_STATS); + cdp_display_stats(soc, + CDP_RX_RING_STATS, + QDF_STATS_VERBOSITY_LEVEL_LOW); + cdp_display_stats(soc, + CDP_TXRX_PATH_STATS, + QDF_STATS_VERBOSITY_LEVEL_LOW); + cdp_display_stats(soc, + CDP_DUMP_TX_FLOW_POOL_INFO, + QDF_STATS_VERBOSITY_LEVEL_LOW); + wlan_hdd_display_netif_queue_history + (hdd_ctx, QDF_STATS_VERBOSITY_LEVEL_LOW); + qdf_dp_trace_dump_stats(); + } + counter = 0; + data_in_time_period = false; + } +} + +/** + * hdd_clear_rps_cpu_mask - clear RPS CPU mask for interfaces + * @hdd_ctx: pointer to struct hdd_context + * + * Return: none + */ +static void hdd_clear_rps_cpu_mask(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_CLEAR_RPS_CPU_MASK) { + hdd_send_rps_disable_ind(adapter); + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_CLEAR_RPS_CPU_MASK); + } +} + +#if defined(CLD_PM_QOS) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) +#define PLD_REMOVE_PM_QOS(x) +#define PLD_REQUEST_PM_QOS(x, y) +/** + * hdd_pm_qos_update_cpu_mask() - Prepare CPU mask for PM_qos voting + * @mask: return variable of cpumask for the TPUT + * @high_throughput: only update high cores mask for high TPUT + * + * Return: none + */ +static inline void hdd_pm_qos_update_cpu_mask(cpumask_t *mask, + bool high_throughput) +{ + cpumask_set_cpu(0, mask); + cpumask_set_cpu(1, mask); + cpumask_set_cpu(2, mask); + cpumask_set_cpu(3, mask); + + if (high_throughput) { + /* For high TPUT include GOLD mask also */ + cpumask_set_cpu(4, mask); + cpumask_set_cpu(5, mask); + cpumask_set_cpu(6, mask); + } +} + +#ifdef MSM_PLATFORM +#define COPY_CPU_MASK(a, b) cpumask_copy(a, b) +#define DUMP_CPU_AFFINE() hdd_info("Set cpu_mask %*pb for affine_cores", \ + cpumask_pr_args(&hdd_ctx->pm_qos_req.cpus_affine)) +#else +#define COPY_CPU_MASK(a, b) /* no-op*/ +#define DUMP_CPU_AFFINE() /* no-op*/ +#endif + +/** + * hdd_pm_qos_update_request() - API to request for pm_qos + * @hdd_ctx: handle to hdd context + * @pm_qos_cpu_mask: cpu_mask to apply + * + * Return: none + */ +static inline void hdd_pm_qos_update_request(struct hdd_context *hdd_ctx, + cpumask_t *pm_qos_cpu_mask) +{ + COPY_CPU_MASK(&hdd_ctx->pm_qos_req.cpus_affine, pm_qos_cpu_mask); + + /* Latency value to be read from INI */ + if (cpumask_empty(pm_qos_cpu_mask)) + pm_qos_update_request(&hdd_ctx->pm_qos_req, + PM_QOS_DEFAULT_VALUE); + else + pm_qos_update_request(&hdd_ctx->pm_qos_req, 1); +} + +#if defined(CONFIG_SMP) && defined(MSM_PLATFORM) +/** + * hdd_update_pm_qos_affine_cores() - Update PM_qos request for AFFINE_CORES + * @hdd_ctx: handle to hdd context + * + * Return: none + */ +static inline void hdd_update_pm_qos_affine_cores(struct hdd_context *hdd_ctx) +{ + hdd_ctx->pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + qdf_cpumask_clear(&hdd_ctx->pm_qos_req.cpus_affine); + hdd_pm_qos_update_cpu_mask(&hdd_ctx->pm_qos_req.cpus_affine, false); +} +#else +static inline void hdd_update_pm_qos_affine_cores(struct hdd_context *hdd_ctx) +{ +} +#endif +static inline void hdd_pm_qos_add_request(struct hdd_context *hdd_ctx) +{ + hdd_update_pm_qos_affine_cores(hdd_ctx); + pm_qos_add_request(&hdd_ctx->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + DUMP_CPU_AFFINE(); + hdd_info("Set cpu_mask %*pb for affine_cores", + cpumask_pr_args(&hdd_ctx->pm_qos_req.cpus_affine)); +} + +static inline void hdd_pm_qos_remove_request(struct hdd_context *hdd_ctx) +{ + pm_qos_remove_request(&hdd_ctx->pm_qos_req); +} +#else +#define PLD_REMOVE_PM_QOS(x) pld_remove_pm_qos(x) +#define PLD_REQUEST_PM_QOS(x, y) pld_request_pm_qos(x, y) + +static inline void hdd_pm_qos_add_request(struct hdd_context *hdd_ctx) +{ +} + +static inline void hdd_pm_qos_remove_request(struct hdd_context *hdd_ctx) +{ +} + +static inline void hdd_pm_qos_update_cpu_mask(cpumask_t *mask, + bool high_throughput) +{ +} + +static inline void hdd_pm_qos_update_request(struct hdd_context *hdd_ctx, + cpumask_t *pm_qos_cpu_mask) +{ +} +#endif + +/** + * hdd_pld_request_bus_bandwidth() - Function to control bus bandwidth + * @hdd_ctx - handle to hdd context + * @tx_packets - transmit packet count + * @rx_packets - receive packet count + * + * The function controls the bus bandwidth and dynamic control of + * tcp delayed ack configuration + * + * Returns: None + */ + +static void hdd_pld_request_bus_bandwidth(struct hdd_context *hdd_ctx, + const uint64_t tx_packets, + const uint64_t rx_packets) +{ + uint16_t index = 0; + bool vote_level_change = false; + bool rx_level_change = false; + bool tx_level_change = false; + bool rxthread_high_tput_req = false; + bool dptrace_high_tput_req; + u64 total_pkts = tx_packets + rx_packets; + uint64_t avg_tx = 0, avg_rx = 0; + uint64_t no_rx_offload_pkts = 0, avg_no_rx_offload_pkts = 0; + uint64_t rx_offload_pkts = 0, avg_rx_offload_pkts = 0; + uint64_t no_tx_offload_pkts = 0, avg_no_tx_offload_pkts = 0; + uint64_t tx_offload_pkts = 0, avg_tx_offload_pkts = 0; + enum pld_bus_width_type next_vote_level = PLD_BUS_WIDTH_IDLE; + static enum wlan_tp_level next_rx_level = WLAN_SVC_TP_NONE; + enum wlan_tp_level next_tx_level = WLAN_SVC_TP_NONE; + uint32_t delack_timer_cnt = hdd_ctx->config->tcp_delack_timer_count; + uint32_t bus_low_cnt_threshold = hdd_ctx->config->bus_low_cnt_threshold; + cpumask_t pm_qos_cpu_mask; + bool is_rx_pm_qos_high = false; + bool is_tx_pm_qos_high = false; + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + int i; + + cpumask_clear(&pm_qos_cpu_mask); + + if (total_pkts > hdd_ctx->config->bus_bw_very_high_threshold) + next_vote_level = PLD_BUS_WIDTH_VERY_HIGH; + else if (total_pkts > hdd_ctx->config->bus_bw_high_threshold) + next_vote_level = PLD_BUS_WIDTH_HIGH; + else if (total_pkts > hdd_ctx->config->bus_bw_medium_threshold) + next_vote_level = PLD_BUS_WIDTH_MEDIUM; + else if (total_pkts > hdd_ctx->config->bus_bw_low_threshold) + next_vote_level = PLD_BUS_WIDTH_LOW; + else + next_vote_level = PLD_BUS_WIDTH_IDLE; + + dptrace_high_tput_req = + next_vote_level > PLD_BUS_WIDTH_IDLE ? true : false; + + if (next_vote_level == PLD_BUS_WIDTH_LOW) { + if (++hdd_ctx->bus_low_vote_cnt >= bus_low_cnt_threshold) + qdf_atomic_set(&hdd_ctx->low_tput_gro_enable, 1); + } else { + if (qdf_atomic_read(&hdd_ctx->low_tput_gro_enable) && + hdd_ctx->enable_dp_rx_threads) { + /* flush pending rx pkts when LOW->IDLE */ + hdd_debug("flush queued GRO pkts"); + for (i = 0; i < cdp_get_num_rx_contexts(soc); i++) { + dp_rx_gro_flush_ind(soc, i); + } + } + hdd_ctx->bus_low_vote_cnt = 0; + qdf_atomic_set(&hdd_ctx->low_tput_gro_enable, 0); + } + + if (hdd_ctx->cur_vote_level != next_vote_level) { + hdd_debug("BW Vote level %d, tx_packets: %lld, rx_packets: %lld", + next_vote_level, tx_packets, rx_packets); + + hdd_ctx->cur_vote_level = next_vote_level; + vote_level_change = true; + + /* + * 11g/a clients are latency sensitive, and any delay in DDR + * access for fetching the packet can cause throughput drop. + * For 11g/a clients LOW voting level is not sufficient for + * peak throughput. Vote for higher DDR frequency if latency + * critical connections are present. + */ + if (hdd_ctx->config->enable_latency_crit_clients && + (next_vote_level == PLD_BUS_WIDTH_LOW || + next_vote_level == PLD_BUS_WIDTH_IDLE) && + qdf_atomic_read(&hdd_ctx->num_latency_critical_clients)) + pld_request_bus_bandwidth(hdd_ctx->parent_dev, + PLD_BUS_WIDTH_LOW_LATENCY); + else + pld_request_bus_bandwidth(hdd_ctx->parent_dev, + next_vote_level); + + if ((next_vote_level == PLD_BUS_WIDTH_LOW) || + (next_vote_level == PLD_BUS_WIDTH_IDLE)) { + if (hdd_ctx->hbw_requested) { + PLD_REMOVE_PM_QOS(hdd_ctx->parent_dev); + hdd_ctx->hbw_requested = false; + } + if (hdd_ctx->dynamic_rps) + hdd_clear_rps_cpu_mask(hdd_ctx); + } else { + if (!hdd_ctx->hbw_requested) { + PLD_REQUEST_PM_QOS(hdd_ctx->parent_dev, 1); + hdd_ctx->hbw_requested = true; + } + if (hdd_ctx->dynamic_rps) + hdd_set_rps_cpu_mask(hdd_ctx); + } + + if (hdd_ctx->config->rx_thread_ul_affinity_mask) { + if (next_vote_level == PLD_BUS_WIDTH_HIGH && + tx_packets > + hdd_ctx->config->bus_bw_high_threshold && + rx_packets > + hdd_ctx->config->bus_bw_low_threshold) + cds_sched_handle_rx_thread_affinity_req(true); + else if (next_vote_level != PLD_BUS_WIDTH_HIGH) + cds_sched_handle_rx_thread_affinity_req(false); + } + + if (hdd_ctx->config->napi_cpu_affinity_mask) + hdd_napi_apply_throughput_policy(hdd_ctx, + tx_packets, + rx_packets); + + if (rx_packets < hdd_ctx->config->bus_bw_low_threshold) + hdd_disable_rx_ol_for_low_tput(hdd_ctx, true); + else + hdd_disable_rx_ol_for_low_tput(hdd_ctx, false); + + /* + * force disable pktlog and only re-enable based + * on ini config + */ + if (next_vote_level >= PLD_BUS_WIDTH_HIGH) + hdd_pktlog_enable_disable(hdd_ctx, false, + 0, 0); + else if (cds_is_packet_log_enabled()) + hdd_pktlog_enable_disable(hdd_ctx, true, + 0, 0); + } + + qdf_dp_trace_apply_tput_policy(dptrace_high_tput_req); + + /* + * Includes tcp+udp, if perf core is required for tcp, then + * perf core is also required for udp. + */ + no_rx_offload_pkts = hdd_ctx->no_rx_offload_pkt_cnt; + hdd_ctx->no_rx_offload_pkt_cnt = 0; + rx_offload_pkts = rx_packets - no_rx_offload_pkts; + + avg_no_rx_offload_pkts = (no_rx_offload_pkts + + hdd_ctx->prev_no_rx_offload_pkts) / 2; + hdd_ctx->prev_no_rx_offload_pkts = no_rx_offload_pkts; + + avg_rx_offload_pkts = (rx_offload_pkts + + hdd_ctx->prev_rx_offload_pkts) / 2; + hdd_ctx->prev_rx_offload_pkts = rx_offload_pkts; + + avg_rx = avg_no_rx_offload_pkts + avg_rx_offload_pkts; + /* + * Takes care to set Rx_thread affinity for below case + * 1)LRO/GRO not supported ROME case + * 2)when rx_ol is disabled in cases like concurrency etc + * 3)For UDP cases + */ + if (avg_no_rx_offload_pkts > hdd_ctx->config->bus_bw_high_threshold) { + rxthread_high_tput_req = true; + is_rx_pm_qos_high = true; + } else { + rxthread_high_tput_req = false; + is_rx_pm_qos_high = false; + } + + hdd_pm_qos_update_cpu_mask(&pm_qos_cpu_mask, is_rx_pm_qos_high); + + if (cds_sched_handle_throughput_req(rxthread_high_tput_req)) + hdd_warn("Rx thread high_tput(%d) affinity request failed", + rxthread_high_tput_req); + + /* fine-tuning parameters for RX Flows */ + if (avg_rx > hdd_ctx->config->tcp_delack_thres_high) { + if ((hdd_ctx->cur_rx_level != WLAN_SVC_TP_HIGH) && + (++hdd_ctx->rx_high_ind_cnt == delack_timer_cnt)) { + next_rx_level = WLAN_SVC_TP_HIGH; + } + } else { + hdd_ctx->rx_high_ind_cnt = 0; + next_rx_level = WLAN_SVC_TP_LOW; + } + + if (hdd_ctx->cur_rx_level != next_rx_level) { + struct wlan_rx_tp_data rx_tp_data = {0}; + + hdd_ctx->cur_rx_level = next_rx_level; + rx_level_change = true; + /* Send throughput indication only if it is enabled. + * Disabling tcp_del_ack will revert the tcp stack behavior + * to default delayed ack. Note that this will disable the + * dynamic delayed ack mechanism across the system + */ + if (hdd_ctx->en_tcp_delack_no_lro) { + rx_tp_data.rx_tp_flags |= TCP_DEL_ACK_IND; + } + + if (hdd_ctx->config->enable_tcp_adv_win_scale) + rx_tp_data.rx_tp_flags |= TCP_ADV_WIN_SCL; + + rx_tp_data.level = next_rx_level; + wlan_hdd_update_tcp_rx_param(hdd_ctx, &rx_tp_data); + } + + no_tx_offload_pkts = hdd_ctx->no_tx_offload_pkt_cnt; + hdd_ctx->no_tx_offload_pkt_cnt = 0; + tx_offload_pkts = tx_packets - no_tx_offload_pkts; + + avg_no_tx_offload_pkts = (no_tx_offload_pkts + + hdd_ctx->prev_no_tx_offload_pkts) / 2; + hdd_ctx->prev_no_tx_offload_pkts = no_tx_offload_pkts; + + avg_tx_offload_pkts = (tx_offload_pkts + + hdd_ctx->prev_tx_offload_pkts) / 2; + hdd_ctx->prev_tx_offload_pkts = tx_offload_pkts; + + avg_tx = avg_no_tx_offload_pkts + avg_tx_offload_pkts; + + /* fine-tuning parameters for TX Flows */ + hdd_ctx->prev_tx = tx_packets; + + if (avg_no_tx_offload_pkts > hdd_ctx->config->bus_bw_high_threshold) + is_tx_pm_qos_high = true; + else + is_tx_pm_qos_high = false; + + hdd_pm_qos_update_cpu_mask(&pm_qos_cpu_mask, is_tx_pm_qos_high); + + if (avg_tx > hdd_ctx->config->tcp_tx_high_tput_thres) + next_tx_level = WLAN_SVC_TP_HIGH; + else + next_tx_level = WLAN_SVC_TP_LOW; + + if ((hdd_ctx->config->enable_tcp_limit_output) && + (hdd_ctx->cur_tx_level != next_tx_level)) { + struct wlan_tx_tp_data tx_tp_data = {0}; + hdd_ctx->cur_tx_level = next_tx_level; + tx_level_change = true; + tx_tp_data.level = next_tx_level; + tx_tp_data.tcp_limit_output = true; + wlan_hdd_update_tcp_tx_param(hdd_ctx, &tx_tp_data); + } + + index = hdd_ctx->hdd_txrx_hist_idx; + if (vote_level_change || tx_level_change || rx_level_change) { + /* Clear all the mask if no silver/gold vote is required */ + if (next_vote_level < PLD_BUS_WIDTH_HIGH) { + is_rx_pm_qos_high = false; + is_tx_pm_qos_high = false; + cpumask_clear(&pm_qos_cpu_mask); + } + + hdd_ctx->hdd_txrx_hist[index].next_tx_level = next_tx_level; + hdd_ctx->hdd_txrx_hist[index].next_rx_level = next_rx_level; + hdd_ctx->hdd_txrx_hist[index].is_rx_pm_qos_high = + is_rx_pm_qos_high; + hdd_ctx->hdd_txrx_hist[index].is_tx_pm_qos_high = + is_tx_pm_qos_high; + hdd_ctx->hdd_txrx_hist[index].next_vote_level = next_vote_level; + hdd_ctx->hdd_txrx_hist[index].interval_rx = rx_packets; + hdd_ctx->hdd_txrx_hist[index].interval_tx = tx_packets; + hdd_ctx->hdd_txrx_hist[index].qtime = qdf_get_log_timestamp(); + hdd_ctx->hdd_txrx_hist_idx++; + hdd_ctx->hdd_txrx_hist_idx &= NUM_TX_RX_HISTOGRAM_MASK; + + /* Clear all the mask if no silver/gold vote is required */ + if (next_vote_level < PLD_BUS_WIDTH_MEDIUM) + cpumask_clear(&pm_qos_cpu_mask); + + hdd_pm_qos_update_request(hdd_ctx, &pm_qos_cpu_mask); + } + + hdd_display_periodic_stats(hdd_ctx, (total_pkts > 0) ? true : false); + + hdd_periodic_sta_stats_display(hdd_ctx); +} + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK +/** + * hdd_set_driver_del_ack_enable() - set driver delayed ack enabled flag + * @vdev_id: vdev id + * @hdd_ctx: handle to hdd context + * @rx_packets: receive packet count + * + * Return: none + */ +static inline +void hdd_set_driver_del_ack_enable(uint16_t vdev_id, + struct hdd_context *hdd_ctx, + uint64_t rx_packets) +{ + struct hdd_config *cfg = hdd_ctx->config; + + cdp_vdev_set_driver_del_ack_enable(cds_get_context(QDF_MODULE_ID_SOC), + vdev_id, rx_packets, + cfg->bus_bw_compute_interval, + cfg->del_ack_threshold_high, + cfg->del_ack_threshold_low); +} +#else +static inline +void hdd_set_driver_del_ack_enable(uint16_t vdev_id, + struct hdd_context *hdd_ctx, + uint64_t rx_packets) +{ +} +#endif + +#ifdef WDI3_STATS_UPDATE +static inline +void hdd_ipa_set_perf_level(struct hdd_context *hdd_ctx, + uint64_t *tx_pkts, uint64_t *rx_pkts, + uint32_t *ipa_tx_pkts, uint32_t *ipa_rx_pkts) +{ +} +#else +static void hdd_ipa_set_perf_level(struct hdd_context *hdd_ctx, + uint64_t *tx_pkts, uint64_t *rx_pkts, + uint32_t *ipa_tx_pkts, uint32_t *ipa_rx_pkts) +{ + if (ucfg_ipa_is_fw_wdi_activated(hdd_ctx->pdev)) { + ucfg_ipa_uc_stat_query(hdd_ctx->pdev, ipa_tx_pkts, + ipa_rx_pkts); + *tx_pkts += *ipa_tx_pkts; + *rx_pkts += *ipa_rx_pkts; + + ucfg_ipa_set_perf_level(hdd_ctx->pdev, *tx_pkts, *rx_pkts); + ucfg_ipa_uc_stat_request(hdd_ctx->pdev, 2); + } +} +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE +static inline +void hdd_set_vdev_bundle_require_flag(uint16_t vdev_id, + struct hdd_context *hdd_ctx, + uint64_t tx_bytes) +{ + struct hdd_config *cfg = hdd_ctx->config; + + cdp_vdev_set_bundle_require_flag(cds_get_context(QDF_MODULE_ID_SOC), + vdev_id, tx_bytes, + cfg->bus_bw_compute_interval, + cfg->pkt_bundle_threshold_high, + cfg->pkt_bundle_threshold_low); +} +#else +static inline +void hdd_set_vdev_bundle_require_flag(uint16_t vdev_id, + struct hdd_context *hdd_ctx, + uint64_t tx_bytes) +{ +} +#endif + +#define HDD_BW_GET_DIFF(_x, _y) (unsigned long)((ULONG_MAX - (_y)) + (_x) + 1) + +#ifdef WLAN_FEATURE_DYNAMIC_RX_AGGREGATION +static enum qdisc_filter_status +__hdd_check_for_prio_filter_in_clsact_qdisc(struct tcf_block *block, + uint32_t prio) +{ + struct tcf_chain *chain; + struct tcf_proto *tp; + enum qdisc_filter_status ret = QDISC_FILTER_PRIO_MISMATCH; + + if (!rtnl_trylock()) + return QDISC_FILTER_RTNL_LOCK_FAIL; + + list_for_each_entry(chain, &block->chain_list, list) { + for (tp = rtnl_dereference(chain->filter_chain); tp; + tp = rtnl_dereference(tp->next)) { + if (tp->prio == (prio << 16)) + ret = QDISC_FILTER_PRIO_MATCH; + } + } + rtnl_unlock(); + + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +/** + * hdd_check_for_prio_filter_in_clsact_qdisc() - Check if priority 3 filter + * is configured in the ingress clsact qdisc + * @qdisc: pointer to clsact qdisc + * + * Return: true if priority 3 filter is present else false + */ +static enum qdisc_filter_status +hdd_check_for_prio_filter_in_clsact_qdisc(struct Qdisc *qdisc, uint32_t prio) +{ + const struct Qdisc_class_ops *cops; + struct tcf_block *ingress_block; + + cops = qdisc->ops->cl_ops; + if (qdf_unlikely(!cops || !cops->tcf_block)) + return QDISC_FILTER_PRIO_MISMATCH; + + ingress_block = cops->tcf_block(qdisc, TC_H_MIN_INGRESS, NULL); + if (qdf_unlikely(!ingress_block)) + return QDISC_FILTER_PRIO_MISMATCH; + + return __hdd_check_for_prio_filter_in_clsact_qdisc(ingress_block, prio); +} +#else +static enum qdisc_filter_status +hdd_check_for_prio_filter_in_clsact_qdisc(struct Qdisc *qdisc, uint32_t prio) +{ + const struct Qdisc_class_ops *cops; + struct tcf_block *ingress_block; + + cops = qdisc->ops->cl_ops; + if (qdf_unlikely(!cops || !cops->tcf_block)) + return QDISC_FILTER_PRIO_MISMATCH; + + ingress_block = cops->tcf_block(qdisc, TC_H_MIN_INGRESS); + if (qdf_unlikely(!ingress_block)) + return QDISC_FILTER_PRIO_MISMATCH; + + return __hdd_check_for_prio_filter_in_clsact_qdisc(ingress_block, prio); +} +#endif + +/** + * hdd_rx_check_qdisc_for_adapter() - Check if any ingress qdisc is configured + * for given adapter + * @adapter: pointer to HDD adapter context + * @rx_ctx_id: Rx context id + * + * The function checks if ingress qdisc is registered for a given + * net device. + * + * Return: None + */ +static void +hdd_rx_check_qdisc_for_adapter(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + struct netdev_queue *ingress_q; + struct Qdisc *ingress_qdisc; + bool disable_gro = false; + enum qdisc_filter_status ret; + + if (!adapter->dev->ingress_queue) + goto reset_wl; + + rcu_read_lock(); + + ingress_q = rcu_dereference(adapter->dev->ingress_queue); + if (qdf_unlikely(!ingress_q)) + goto reset; + + ingress_qdisc = rcu_dereference(ingress_q->qdisc); + if (qdf_unlikely(!ingress_qdisc)) + goto reset; + + if (qdf_str_eq(ingress_qdisc->ops->id, "ingress")) { + disable_gro = true; + } else if (qdf_str_eq(ingress_qdisc->ops->id, "clsact")) { + ret = hdd_check_for_prio_filter_in_clsact_qdisc(ingress_qdisc, + hdd_ctx->dp_agg_param.tc_ingress_prio); + + if (ret == QDISC_FILTER_RTNL_LOCK_FAIL) { + rcu_read_unlock(); + return; + } else if (ret == QDISC_FILTER_PRIO_MISMATCH) { + goto reset; + } + + disable_gro = true; + } + + if (disable_gro) { + rcu_read_unlock(); + + if (qdf_likely(qdf_atomic_read(&adapter->gro_disallowed))) + return; + + hdd_debug("ingress qdisc/filter configured disable GRO"); + qdf_atomic_set(&adapter->gro_disallowed, 1); + + return; + } + +reset: + rcu_read_unlock(); + +reset_wl: + if (qdf_unlikely(qdf_atomic_read(&adapter->gro_disallowed))) { + hdd_debug("ingress qdisc/filter removed enable GRO"); + qdf_atomic_set(&adapter->gro_disallowed, 0); + } +} +#else +static inline void +hdd_rx_check_qdisc_for_adapter(struct hdd_adapter *adapter) +{ +} +#endif + +static void __hdd_bus_bw_work_handler(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter = NULL, *next_adapter = NULL, + *con_sap_adapter = NULL; + uint64_t tx_packets = 0, rx_packets = 0, tx_bytes = 0; + uint64_t fwd_tx_packets = 0, fwd_rx_packets = 0; + uint64_t fwd_tx_packets_diff = 0, fwd_rx_packets_diff = 0; + uint64_t total_tx = 0, total_rx = 0; + A_STATUS ret; + bool connected = false; + uint32_t ipa_tx_packets = 0, ipa_rx_packets = 0; + uint64_t sta_tx_bytes = 0, sap_tx_bytes = 0; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_BUS_BW_WORK_HANDLER; + + if (wlan_hdd_validate_context(hdd_ctx)) + goto stop_work; + + if (hdd_ctx->is_wiphy_suspended) + return; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + /* + * Validate magic so we don't end up accessing + * an invalid adapter. + */ + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + if ((adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) && + WLAN_HDD_GET_STATION_CTX_PTR(adapter)->conn_info.conn_state + != eConnectionState_Associated) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + if ((adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) && + WLAN_HDD_GET_AP_CTX_PTR(adapter)->ap_active == false) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + if (hdd_ctx->dp_agg_param.tc_based_dyn_gro || + hdd_ctx->ol_enable == CFG_DYNAMIC_GRO_ENABLED) + hdd_rx_check_qdisc_for_adapter(adapter); + + tx_packets += HDD_BW_GET_DIFF(adapter->stats.tx_packets, + adapter->prev_tx_packets); + rx_packets += HDD_BW_GET_DIFF(adapter->stats.rx_packets, + adapter->prev_rx_packets); + tx_bytes = HDD_BW_GET_DIFF(adapter->stats.tx_bytes, + adapter->prev_tx_bytes); + + if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE || + adapter->device_mode == QDF_IBSS_MODE || + adapter->device_mode == QDF_NDI_MODE) { + + ret = cdp_get_intra_bss_fwd_pkts_count( + cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id, + &fwd_tx_packets, &fwd_rx_packets); + if (ret == A_OK) { + fwd_tx_packets_diff += HDD_BW_GET_DIFF( + fwd_tx_packets, + adapter->prev_fwd_tx_packets); + fwd_rx_packets_diff += HDD_BW_GET_DIFF( + fwd_tx_packets, + adapter->prev_fwd_rx_packets); + } + } + + if (adapter->device_mode == QDF_SAP_MODE) { + con_sap_adapter = adapter; + sap_tx_bytes = adapter->stats.tx_bytes; + } + + if (adapter->device_mode == QDF_STA_MODE) + sta_tx_bytes = adapter->stats.tx_bytes; + + hdd_set_driver_del_ack_enable(adapter->vdev_id, hdd_ctx, + rx_packets); + + hdd_set_vdev_bundle_require_flag(adapter->vdev_id, hdd_ctx, + tx_bytes); + + total_rx += adapter->stats.rx_packets; + total_tx += adapter->stats.tx_packets; + + qdf_spin_lock_bh(&hdd_ctx->bus_bw_lock); + adapter->prev_tx_packets = adapter->stats.tx_packets; + adapter->prev_rx_packets = adapter->stats.rx_packets; + adapter->prev_fwd_tx_packets = fwd_tx_packets; + adapter->prev_fwd_rx_packets = fwd_rx_packets; + adapter->prev_tx_bytes = adapter->stats.tx_bytes; + qdf_spin_unlock_bh(&hdd_ctx->bus_bw_lock); + connected = true; + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + if (!connected) { + hdd_err("bus bandwidth timer running in disconnected state"); + goto stop_work; + } + + /* add intra bss forwarded tx and rx packets */ + tx_packets += fwd_tx_packets_diff; + rx_packets += fwd_rx_packets_diff; + + /* Send embedded Tx packet bytes on STA & SAP interface to IPA driver */ + ucfg_ipa_update_tx_stats(hdd_ctx->pdev, sta_tx_bytes, sap_tx_bytes); + + hdd_ipa_set_perf_level(hdd_ctx, &tx_packets, &rx_packets, + &ipa_tx_packets, &ipa_rx_packets); + if (con_sap_adapter) { + con_sap_adapter->stats.tx_packets += ipa_tx_packets; + con_sap_adapter->stats.rx_packets += ipa_rx_packets; + } + + hdd_pld_request_bus_bandwidth(hdd_ctx, tx_packets, rx_packets); + + return; + +stop_work: + qdf_periodic_work_stop_async(&hdd_ctx->bus_bw_work); +} + +static void hdd_bus_bw_work_handler(void *context) +{ + struct hdd_context *hdd_ctx = context; + struct qdf_op_sync *op_sync; + + if (qdf_op_protect(&op_sync)) + return; + + __hdd_bus_bw_work_handler(hdd_ctx); + + qdf_op_unprotect(op_sync); +} + +int hdd_bus_bandwidth_init(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + hdd_enter(); + + qdf_spinlock_create(&hdd_ctx->bus_bw_lock); + + hdd_pm_qos_add_request(hdd_ctx); + + status = qdf_periodic_work_create(&hdd_ctx->bus_bw_work, + hdd_bus_bw_work_handler, + hdd_ctx); + + hdd_exit(); + + return qdf_status_to_os_return(status); +} + +void hdd_bus_bandwidth_deinit(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + /* it is expecting the timer has been stopped or not started + * when coming deinit. + */ + QDF_BUG(!qdf_periodic_work_stop_sync(&hdd_ctx->bus_bw_work)); + + qdf_periodic_work_destroy(&hdd_ctx->bus_bw_work); + qdf_spinlock_destroy(&hdd_ctx->bus_bw_lock); + hdd_pm_qos_remove_request(hdd_ctx); + + hdd_exit(); +} +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + +/** + * __hdd_adapter_param_update_work() - Gist of the work to process + * netdev feature update. + * @adapter: pointer to adapter structure + * + * This function assumes that the adapter pointer is always valid. + * So the caller shoudl always validate adapter pointer before calling + * this function + * + * Returns: None + */ +static inline void +__hdd_adapter_param_update_work(struct hdd_adapter *adapter) +{ + /** + * This check is needed in case the work got scheduled after the + * interface got disconnected. During disconnection, the network queues + * are paused and hence should not be, mistakenly, restarted here. + * There are two approaches to handle this case + * 1) Flush the work during disconnection + * 2) Check for connected state in work + * + * Since the flushing of work during disconnection will need to be + * done at multiple places or entry points, instead its preferred to + * check the connection state and skip the operation here. + */ + if (!hdd_adapter_is_connected_sta(adapter)) + return; + + hdd_netdev_update_features(adapter); + + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); +} + +/** + * hdd_adapter_param_update_work() - work to process the netdev features + * update. + * @arg: private data passed to work + * + * Returns: None + */ +static void hdd_adapter_param_update_work(void *arg) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct hdd_adapter *adapter = arg; + struct osif_vdev_sync *vdev_sync; + int errno; + + if (!hdd_ctx) { + hdd_err("Invalid hdd context"); + return; + } + + hdd_adapter_ops_record_event(hdd_ctx, + WLAN_HDD_ADAPTER_OPS_WORK_SCHED, + WLAN_INVALID_VDEV_ID); + + if (hdd_validate_adapter(adapter)) { + hdd_err("netdev features update request for invalid adapter"); + return; + } + + errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); + if (errno) + return; + + __hdd_adapter_param_update_work(adapter); + + osif_vdev_sync_op_stop(vdev_sync); +} + +QDF_STATUS hdd_init_adapter_ops_wq(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + hdd_ctx->adapter_ops_wq = + qdf_alloc_high_prior_ordered_workqueue("hdd_adapter_ops_wq"); + if (!hdd_ctx->adapter_ops_wq) + return QDF_STATUS_E_NOMEM; + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +void hdd_deinit_adapter_ops_wq(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + qdf_flush_workqueue(0, hdd_ctx->adapter_ops_wq); + qdf_destroy_workqueue(0, hdd_ctx->adapter_ops_wq); + + hdd_exit(); +} + +QDF_STATUS hdd_adapter_feature_update_work_init(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + hdd_enter(); + + status = qdf_create_work(0, &adapter->netdev_features_update_work, + hdd_adapter_param_update_work, adapter); + + hdd_exit(); + + return status; +} + +void hdd_adapter_feature_update_work_deinit(struct hdd_adapter *adapter) +{ + hdd_enter(); + + qdf_cancel_work(&adapter->netdev_features_update_work); + qdf_flush_work(&adapter->netdev_features_update_work); + + hdd_exit(); +} + +static uint8_t *convert_level_to_string(uint32_t level) +{ + switch (level) { + /* initialize the wlan sub system */ + case WLAN_SVC_TP_NONE: + return "NONE"; + case WLAN_SVC_TP_LOW: + return "LOW"; + case WLAN_SVC_TP_MEDIUM: + return "MED"; + case WLAN_SVC_TP_HIGH: + return "HIGH"; + default: + return "INVAL"; + } +} + +/** + * wlan_hdd_display_tx_rx_histogram() - display tx rx histogram + * @hdd_ctx: hdd context + * + * Return: none + */ +void wlan_hdd_display_tx_rx_histogram(struct hdd_context *hdd_ctx) +{ + int i; + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH + hdd_nofl_debug("BW compute Interval: %d ms", + hdd_ctx->config->bus_bw_compute_interval); + hdd_nofl_debug("BW TH - Very High: %d High: %d Med: %d Low: %d", + hdd_ctx->config->bus_bw_very_high_threshold, + hdd_ctx->config->bus_bw_high_threshold, + hdd_ctx->config->bus_bw_medium_threshold, + hdd_ctx->config->bus_bw_low_threshold); + hdd_nofl_debug("Enable TCP DEL ACK: %d", + hdd_ctx->en_tcp_delack_no_lro); + hdd_nofl_debug("TCP DEL High TH: %d TCP DEL Low TH: %d", + hdd_ctx->config->tcp_delack_thres_high, + hdd_ctx->config->tcp_delack_thres_low); + hdd_nofl_debug("TCP TX HIGH TP TH: %d (Use to set tcp_output_bytes_limit)", + hdd_ctx->config->tcp_tx_high_tput_thres); +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + + hdd_nofl_debug("Total entries: %d Current index: %d", + NUM_TX_RX_HISTOGRAM, hdd_ctx->hdd_txrx_hist_idx); + + hdd_nofl_debug("[index][timestamp]: interval_rx, interval_tx, bus_bw_level, RX TP Level, TX TP Level, Rx:Tx pm_qos"); + + for (i = 0; i < NUM_TX_RX_HISTOGRAM; i++) { + /* using hdd_log to avoid printing function name */ + if (hdd_ctx->hdd_txrx_hist[i].qtime > 0) + hdd_nofl_debug("[%3d][%15llu]: %6llu, %6llu, %s, %s, %s, %s:%s", + i, hdd_ctx->hdd_txrx_hist[i].qtime, + hdd_ctx->hdd_txrx_hist[i].interval_rx, + hdd_ctx->hdd_txrx_hist[i].interval_tx, + convert_level_to_string( + hdd_ctx->hdd_txrx_hist[i]. + next_vote_level), + convert_level_to_string( + hdd_ctx->hdd_txrx_hist[i]. + next_rx_level), + convert_level_to_string( + hdd_ctx->hdd_txrx_hist[i]. + next_tx_level), + hdd_ctx->hdd_txrx_hist[i].is_rx_pm_qos_high ? + "HIGH" : "LOW", + hdd_ctx->hdd_txrx_hist[i].is_tx_pm_qos_high ? + "HIGH" : "LOW"); + } +} + +/** + * wlan_hdd_clear_tx_rx_histogram() - clear tx rx histogram + * @hdd_ctx: hdd context + * + * Return: none + */ +void wlan_hdd_clear_tx_rx_histogram(struct hdd_context *hdd_ctx) +{ + hdd_ctx->hdd_txrx_hist_idx = 0; + qdf_mem_zero(hdd_ctx->hdd_txrx_hist, + (sizeof(struct hdd_tx_rx_histogram) * NUM_TX_RX_HISTOGRAM)); +} + +/* length of the netif queue log needed per adapter */ +#define ADAP_NETIFQ_LOG_LEN ((20 * WLAN_REASON_TYPE_MAX) + 50) + +/** + * + * hdd_display_netif_queue_history_compact() - display compact netifq history + * @hdd_ctx: hdd context + * + * Return: none + */ +static void +hdd_display_netif_queue_history_compact(struct hdd_context *hdd_ctx) +{ + int adapter_num = 0; + int i; + int bytes_written; + u32 tbytes; + qdf_time_t total, pause, unpause, curr_time, delta; + char temp_str[20 * WLAN_REASON_TYPE_MAX]; + char *comb_log_str; + uint32_t comb_log_str_size; + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_DISPLAY_NETIF_QUEUE_HISTORY_COMPACT; + + comb_log_str_size = (ADAP_NETIFQ_LOG_LEN * WLAN_MAX_VDEVS) + 1; + comb_log_str = qdf_mem_malloc(comb_log_str_size); + if (!comb_log_str) + return; + + bytes_written = 0; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + curr_time = qdf_system_ticks(); + total = curr_time - adapter->start_time; + delta = curr_time - adapter->last_time; + + if (adapter->pause_map) { + pause = adapter->total_pause_time + delta; + unpause = adapter->total_unpause_time; + } else { + unpause = adapter->total_unpause_time + delta; + pause = adapter->total_pause_time; + } + + tbytes = 0; + qdf_mem_zero(temp_str, sizeof(temp_str)); + for (i = WLAN_CONTROL_PATH; i < WLAN_REASON_TYPE_MAX; i++) { + if (adapter->queue_oper_stats[i].pause_count == 0) + continue; + tbytes += + snprintf( + &temp_str[tbytes], + (tbytes >= sizeof(temp_str) ? + 0 : sizeof(temp_str) - tbytes), + "%d(%d,%d) ", + i, + adapter->queue_oper_stats[i]. + pause_count, + adapter->queue_oper_stats[i]. + unpause_count); + } + if (tbytes >= sizeof(temp_str)) + hdd_warn("log truncated"); + + bytes_written += snprintf(&comb_log_str[bytes_written], + bytes_written >= comb_log_str_size ? 0 : + comb_log_str_size - bytes_written, + "[%d %d] (%d) %u/%ums %s|", + adapter->vdev_id, adapter->device_mode, + adapter->pause_map, + qdf_system_ticks_to_msecs(pause), + qdf_system_ticks_to_msecs(total), + temp_str); + + adapter_num++; + /* dev_put has to be done here */ + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + /* using QDF_TRACE to avoid printing function name */ + QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO_LOW, + "STATS |%s", comb_log_str); + + if (bytes_written >= comb_log_str_size) + hdd_warn("log string truncated"); + + qdf_mem_free(comb_log_str); +} + +/* Max size of a single netdev tx queue state string. e.g. "1: 0x1" */ +#define HDD_NETDEV_TX_Q_STATE_STRLEN 15 +/** + * wlan_hdd_display_adapter_netif_queue_stats() - display adapter based + * netif queue stats + * @adapter: hdd adapter + * + * Return: none + */ +static void +wlan_hdd_display_adapter_netif_queue_stats(struct hdd_adapter *adapter) +{ + int i; + qdf_time_t total, pause, unpause, curr_time, delta; + struct hdd_netif_queue_history *q_hist_ptr; + char q_status_buf[NUM_TX_QUEUES * HDD_NETDEV_TX_Q_STATE_STRLEN] = {0}; + + hdd_nofl_debug("Netif queue operation statistics:"); + hdd_nofl_debug("vdev_id %d device mode %d", + adapter->vdev_id, adapter->device_mode); + hdd_nofl_debug("Current pause_map %x", adapter->pause_map); + curr_time = qdf_system_ticks(); + total = curr_time - adapter->start_time; + delta = curr_time - adapter->last_time; + if (adapter->pause_map) { + pause = adapter->total_pause_time + delta; + unpause = adapter->total_unpause_time; + } else { + unpause = adapter->total_unpause_time + delta; + pause = adapter->total_pause_time; + } + hdd_nofl_debug("Total: %ums Pause: %ums Unpause: %ums", + qdf_system_ticks_to_msecs(total), + qdf_system_ticks_to_msecs(pause), + qdf_system_ticks_to_msecs(unpause)); + hdd_nofl_debug("reason_type: pause_cnt: unpause_cnt: pause_time"); + + for (i = WLAN_CONTROL_PATH; i < WLAN_REASON_TYPE_MAX; i++) { + qdf_time_t pause_delta = 0; + + if (adapter->pause_map & (1 << i)) + pause_delta = delta; + + /* using hdd_log to avoid printing function name */ + hdd_nofl_debug("%s: %d: %d: %ums", + hdd_reason_type_to_string(i), + adapter->queue_oper_stats[i].pause_count, + adapter->queue_oper_stats[i]. + unpause_count, + qdf_system_ticks_to_msecs( + adapter->queue_oper_stats[i]. + total_pause_time + pause_delta)); + } + + hdd_nofl_debug("Netif queue operation history: Total entries: %d current index %d(-1) time %u", + WLAN_HDD_MAX_HISTORY_ENTRY, + adapter->history_index, + qdf_system_ticks_to_msecs(qdf_system_ticks())); + + hdd_nofl_debug("%2s%20s%50s%30s%10s %s", + "#", "time(ms)", "action_type", "reason_type", + "pause_map", "netdev-queue-status"); + + for (i = 0; i < WLAN_HDD_MAX_HISTORY_ENTRY; i++) { + /* using hdd_log to avoid printing function name */ + if (adapter->queue_oper_history[i].time == 0) + continue; + q_hist_ptr = &adapter->queue_oper_history[i]; + wlan_hdd_dump_queue_history_state(q_hist_ptr, + q_status_buf, + sizeof(q_status_buf)); + hdd_nofl_debug("%2d%20u%50s%30s%10x %s", + i, qdf_system_ticks_to_msecs( + adapter->queue_oper_history[i].time), + hdd_action_type_to_string( + adapter->queue_oper_history[i]. + netif_action), + hdd_reason_type_to_string( + adapter->queue_oper_history[i]. + netif_reason), + adapter->queue_oper_history[i].pause_map, + q_status_buf); + } +} + +void +wlan_hdd_display_adapter_netif_queue_history(struct hdd_adapter *adapter) +{ + wlan_hdd_display_adapter_netif_queue_stats(adapter); +} + +/** + * wlan_hdd_display_netif_queue_history() - display netif queue history + * @hdd_ctx: hdd context + * + * Return: none + */ +void +wlan_hdd_display_netif_queue_history(struct hdd_context *hdd_ctx, + enum qdf_stats_verbosity_level verb_lvl) +{ + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = + NET_DEV_HOLD_DISPLAY_NETIF_QUEUE_HISTORY; + + if (verb_lvl == QDF_STATS_VERBOSITY_LEVEL_LOW) { + hdd_display_netif_queue_history_compact(hdd_ctx); + return; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->vdev_id == CDP_INVALID_VDEV_ID) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + wlan_hdd_display_adapter_netif_queue_stats(adapter); + /* dev_put has to be done here */ + hdd_adapter_dev_put_debug(adapter, dbgid); + } +} + +/** + * wlan_hdd_clear_netif_queue_history() - clear netif queue operation history + * @hdd_ctx: hdd context + * + * Return: none + */ +void wlan_hdd_clear_netif_queue_history(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_CLEAR_NETIF_QUEUE_HISTORY; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + qdf_mem_zero(adapter->queue_oper_stats, + sizeof(adapter->queue_oper_stats)); + qdf_mem_zero(adapter->queue_oper_history, + sizeof(adapter->queue_oper_history)); + adapter->history_index = 0; + adapter->start_time = adapter->last_time = qdf_system_ticks(); + adapter->total_pause_time = 0; + adapter->total_unpause_time = 0; + hdd_adapter_dev_put_debug(adapter, dbgid); + } +} + +#ifdef WLAN_FEATURE_OFFLOAD_PACKETS +/** + * hdd_init_offloaded_packets_ctx() - Initialize offload packets context + * @hdd_ctx: hdd global context + * + * Return: none + */ +static void hdd_init_offloaded_packets_ctx(struct hdd_context *hdd_ctx) +{ + uint8_t i; + + mutex_init(&hdd_ctx->op_ctx.op_lock); + for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) { + hdd_ctx->op_ctx.op_table[i].request_id = MAX_REQUEST_ID; + hdd_ctx->op_ctx.op_table[i].pattern_id = i; + } +} +#else +static void hdd_init_offloaded_packets_ctx(struct hdd_context *hdd_ctx) +{ +} +#endif + +#ifdef WLAN_FEATURE_WOW_PULSE +/** + * wlan_hdd_set_wow_pulse() - call SME to send wmi cmd of wow pulse + * @hdd_ctx: struct hdd_context structure pointer + * @enable: enable or disable this behaviour + * + * Return: int + */ +static int wlan_hdd_set_wow_pulse(struct hdd_context *hdd_ctx, bool enable) +{ + struct wow_pulse_mode wow_pulse_set_info; + QDF_STATUS status; + + hdd_debug("wow pulse enable flag is %d", enable); + + if (!ucfg_pmo_is_wow_pulse_enabled(hdd_ctx->psoc)) + return 0; + + /* prepare the request to send to SME */ + if (enable == true) { + wow_pulse_set_info.wow_pulse_enable = true; + wow_pulse_set_info.wow_pulse_pin = + ucfg_pmo_get_wow_pulse_pin(hdd_ctx->psoc); + + wow_pulse_set_info.wow_pulse_interval_high = + ucfg_pmo_get_wow_pulse_interval_high(hdd_ctx->psoc); + + wow_pulse_set_info.wow_pulse_interval_low = + ucfg_pmo_get_wow_pulse_interval_low(hdd_ctx->psoc); + } else { + wow_pulse_set_info.wow_pulse_enable = false; + wow_pulse_set_info.wow_pulse_pin = 0; + wow_pulse_set_info.wow_pulse_interval_low = 0; + wow_pulse_set_info.wow_pulse_interval_high = 0; + } + hdd_debug("enable %d pin %d low %d high %d", + wow_pulse_set_info.wow_pulse_enable, + wow_pulse_set_info.wow_pulse_pin, + wow_pulse_set_info.wow_pulse_interval_low, + wow_pulse_set_info.wow_pulse_interval_high); + + status = sme_set_wow_pulse(&wow_pulse_set_info); + if (QDF_STATUS_E_FAILURE == status) { + hdd_debug("sme_set_wow_pulse failure!"); + return -EIO; + } + hdd_debug("sme_set_wow_pulse success!"); + return 0; +} +#else +static inline int wlan_hdd_set_wow_pulse(struct hdd_context *hdd_ctx, bool enable) +{ + return 0; +} +#endif + +#ifdef WLAN_FEATURE_FASTPATH + +/** + * hdd_enable_fastpath() - Enable fastpath if enabled in config INI + * @hdd_cfg: hdd config + * @context: lower layer context + * + * Return: none + */ +void hdd_enable_fastpath(struct hdd_context *hdd_ctx, + void *context) +{ + if (cfg_get(hdd_ctx->psoc, CFG_DP_ENABLE_FASTPATH)) + hif_enable_fastpath(context); +} +#endif + +#if defined(FEATURE_WLAN_CH_AVOID) +/** + * hdd_set_thermal_level_cb() - set thermal level callback function + * @hdd_handle: opaque handle for the hdd context + * @level: thermal level + * + * Change IPA data path to SW path when the thermal throttle level greater + * than 0, and restore the original data path when throttle level is 0 + * + * Return: none + */ +static void hdd_set_thermal_level_cb(hdd_handle_t hdd_handle, u_int8_t level) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + + /* Change IPA to SW path when throttle level greater than 0 */ + if (level > THROTTLE_LEVEL_0) + ucfg_ipa_send_mcc_scc_msg(hdd_ctx->pdev, true); + else + /* restore original concurrency mode */ + ucfg_ipa_send_mcc_scc_msg(hdd_ctx->pdev, hdd_ctx->mcc_mode); +} +#else +/** + * hdd_set_thermal_level_cb() - set thermal level callback function + * @hdd_handle: opaque handle for the hdd context + * @level: thermal level + * + * Change IPA data path to SW path when the thermal throttle level greater + * than 0, and restore the original data path when throttle level is 0 + * + * Return: none + */ +static void hdd_set_thermal_level_cb(hdd_handle_t hdd_handle, u_int8_t level) +{ +} +#endif + +/** + * hdd_switch_sap_channel() - Move SAP to the given channel + * @adapter: AP adapter + * @channel: Channel + * @forced: Force to switch channel, ignore SCC/MCC check + * + * Moves the SAP interface by invoking the function which + * executes the callback to perform channel switch using (E)CSA. + * + * Return: None + */ +void hdd_switch_sap_channel(struct hdd_adapter *adapter, uint8_t channel, + bool forced) +{ + struct hdd_ap_ctx *hdd_ap_ctx; + struct hdd_context *hdd_ctx; + mac_handle_t mac_handle; + + if (!adapter) { + hdd_err("invalid adapter"); + return; + } + + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + + mac_handle = hdd_adapter_get_mac_handle(adapter); + if (!mac_handle) { + hdd_err("invalid MAC handle"); + return; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_debug("chan:%d width:%d", + channel, hdd_ap_ctx->sap_config.ch_width_orig); + + policy_mgr_change_sap_channel_with_csa( + hdd_ctx->psoc, adapter->vdev_id, + wlan_chan_to_freq(channel), + hdd_ap_ctx->sap_config.ch_width_orig, forced); +} + +int hdd_update_acs_timer_reason(struct hdd_adapter *adapter, uint8_t reason) +{ + struct hdd_external_acs_timer_context *timer_context; + int status; + QDF_STATUS qdf_status; + + set_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags); + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&adapter->session. + ap.vendor_acs_timer)) { + qdf_mc_timer_stop(&adapter->session.ap.vendor_acs_timer); + } + timer_context = (struct hdd_external_acs_timer_context *) + adapter->session.ap.vendor_acs_timer.user_data; + timer_context->reason = reason; + qdf_status = + qdf_mc_timer_start(&adapter->session.ap.vendor_acs_timer, + WLAN_VENDOR_ACS_WAIT_TIME); + if (qdf_status != QDF_STATUS_SUCCESS) { + hdd_err("failed to start external acs timer"); + return -ENOSPC; + } + /* Update config to application */ + status = hdd_cfg80211_update_acs_config(adapter, reason); + hdd_info("Updated ACS config to nl with reason %d", reason); + + return status; +} + +#if defined(FEATURE_WLAN_CH_AVOID) +/** + * hdd_store_sap_restart_channel() - store sap restart channel + * @restart_chan: restart channel + * @restart_chan_store: pointer to restart channel store + * + * The function will store new sap restart channel. + * + * Return - none + */ +static void +hdd_store_sap_restart_channel(uint8_t restart_chan, uint8_t *restart_chan_store) +{ + uint8_t i; + + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + if (*(restart_chan_store + i) == restart_chan) + return; + + if (*(restart_chan_store + i)) + continue; + + *(restart_chan_store + i) = restart_chan; + return; + } +} + +/** + * hdd_unsafe_channel_restart_sap() - restart sap if sap is on unsafe channel + * @hdd_ctx: hdd context pointer + * + * hdd_unsafe_channel_restart_sap check all unsafe channel list + * and if ACS is enabled, driver will ask userspace to restart the + * sap. User space on LTE coex indication restart driver. + * + * Return - none + */ +void hdd_unsafe_channel_restart_sap(struct hdd_context *hdd_ctxt) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + uint32_t i; + bool found = false; + uint8_t restart_chan_store[SAP_MAX_NUM_SESSION] = {0}; + uint8_t restart_chan, ap_chan; + uint8_t scc_on_lte_coex = 0; + uint32_t restart_freq, ap_chan_freq; + bool value; + QDF_STATUS status; + bool is_acs_support_for_dfs_ltecoex = cfg_default(CFG_USER_ACS_DFS_LTE); + bool is_vendor_acs_support = + cfg_default(CFG_USER_AUTO_CHANNEL_SELECTION); + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_UNSAFE_CHANNEL_RESTART_SAP; + + hdd_for_each_adapter_dev_held_safe(hdd_ctxt, adapter, next_adapter, + dbgid) { + if (!(adapter->device_mode == QDF_SAP_MODE && + adapter->session.ap.sap_config.acs_cfg.acs_mode)) { + hdd_debug_rl("skip device mode:%d acs:%d", + adapter->device_mode, + adapter->session.ap.sap_config.acs_cfg.acs_mode); + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + ap_chan = wlan_reg_freq_to_chan( + hdd_ctxt->pdev, + adapter->session.ap.operating_chan_freq); + ap_chan_freq = adapter->session.ap.operating_chan_freq; + + found = false; + status = + ucfg_policy_mgr_get_sta_sap_scc_lte_coex_chnl(hdd_ctxt->psoc, + &scc_on_lte_coex); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("can't get scc on lte coex chnl, use def"); + /* + * If STA+SAP is doing SCC & g_sta_sap_scc_on_lte_coex_chan + * is set, no need to move SAP. + */ + if ((policy_mgr_is_sta_sap_scc( + hdd_ctxt->psoc, + adapter->session.ap.operating_chan_freq) && + scc_on_lte_coex) || + policy_mgr_nan_sap_scc_on_unsafe_ch_chk( + hdd_ctxt->psoc, + adapter->session.ap.operating_chan_freq)) { + hdd_debug("SAP allowed in unsafe SCC channel"); + } else { + for (i = 0; i < hdd_ctxt->unsafe_channel_count; i++) { + if (ap_chan_freq == + hdd_ctxt->unsafe_channel_list[i]) { + found = true; + hdd_debug("op ch freq:%d is unsafe", + ap_chan_freq); + break; + } + } + } + if (!found) { + hdd_store_sap_restart_channel( + ap_chan, + restart_chan_store); + hdd_debug("ch:%d is safe. no need to change channel", + ap_chan); + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + status = ucfg_mlme_get_acs_support_for_dfs_ltecoex( + hdd_ctxt->psoc, + &is_acs_support_for_dfs_ltecoex); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("get_acs_support_for_dfs_ltecoex failed,set def"); + + status = ucfg_mlme_get_vendor_acs_support( + hdd_ctxt->psoc, + &is_vendor_acs_support); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("get_vendor_acs_support failed, set default"); + + if (is_vendor_acs_support && is_acs_support_for_dfs_ltecoex) { + hdd_update_acs_timer_reason(adapter, + QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX); + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + restart_chan = 0; + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + if (!restart_chan_store[i]) + continue; + + if (policy_mgr_is_force_scc(hdd_ctxt->psoc) && + WLAN_REG_IS_SAME_BAND_CHANNELS( + restart_chan_store[i], + ap_chan)) { + restart_chan = restart_chan_store[i]; + break; + } + } + if (!restart_chan) { + restart_freq = + wlansap_get_safe_channel_from_pcl_and_acs_range( + WLAN_HDD_GET_SAP_CTX_PTR(adapter)); + restart_chan = wlan_reg_freq_to_chan(hdd_ctxt->pdev, + restart_freq); + } + if (!restart_chan) { + hdd_err("fail to restart SAP"); + } else { + /* + * SAP restart due to unsafe channel. While + * restarting the SAP, make sure to clear + * acs_channel, channel to reset to + * 0. Otherwise these settings will override + * the ACS while restart. + */ + hdd_ctxt->acs_policy.acs_chan_freq = + AUTO_CHANNEL_SELECT; + ucfg_mlme_get_sap_internal_restart(hdd_ctxt->psoc, + &value); + if (value) { + wlan_hdd_set_sap_csa_reason(hdd_ctxt->psoc, + adapter->vdev_id, + CSA_REASON_UNSAFE_CHANNEL); + hdd_switch_sap_channel(adapter, restart_chan, + true); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return; + } + else { + hdd_debug("sending coex indication"); + wlan_hdd_send_svc_nlink_msg( + hdd_ctxt->radio_index, + WLAN_SVC_LTE_COEX_IND, NULL, 0); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return; + } + } + /* dev_put has to be done here */ + hdd_adapter_dev_put_debug(adapter, dbgid); + } +} + +/** + * hdd_init_channel_avoidance() - Initialize channel avoidance + * @hdd_ctx: HDD global context + * + * Initialize the channel avoidance logic by retrieving the unsafe + * channel list from the platform driver and plumbing the data + * down to the lower layers. Then subscribe to subsequent channel + * avoidance events. + * + * Return: None + */ +static void hdd_init_channel_avoidance(struct hdd_context *hdd_ctx) +{ + uint16_t unsafe_channel_count; + int index; + + pld_get_wlan_unsafe_channel(hdd_ctx->parent_dev, + hdd_ctx->unsafe_channel_list, + &(hdd_ctx->unsafe_channel_count), + sizeof(uint16_t) * NUM_CHANNELS); + + hdd_debug("num of unsafe channels is %d", + hdd_ctx->unsafe_channel_count); + + unsafe_channel_count = QDF_MIN((uint16_t)hdd_ctx->unsafe_channel_count, + (uint16_t)NUM_CHANNELS); + + for (index = 0; index < unsafe_channel_count; index++) { + hdd_debug("channel frequency %d is not safe", + hdd_ctx->unsafe_channel_list[index]); + + } + + ucfg_policy_mgr_init_chan_avoidance( + hdd_ctx->psoc, + (qdf_freq_t *)hdd_ctx->unsafe_channel_list, + hdd_ctx->unsafe_channel_count); +} + +static void hdd_lte_coex_restart_sap(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx) +{ + uint8_t restart_chan; + uint32_t restart_freq; + + restart_freq = wlansap_get_safe_channel_from_pcl_and_acs_range( + WLAN_HDD_GET_SAP_CTX_PTR(adapter)); + + restart_chan = wlan_reg_freq_to_chan(hdd_ctx->pdev, + restart_freq); + + if (!restart_chan) { + hdd_alert("fail to restart SAP"); + return; + } + + /* SAP restart due to unsafe channel. While restarting + * the SAP, make sure to clear acs_channel, channel to + * reset to 0. Otherwise these settings will override + * the ACS while restart. + */ + hdd_ctx->acs_policy.acs_chan_freq = AUTO_CHANNEL_SELECT; + + hdd_debug("sending coex indication"); + + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_LTE_COEX_IND, NULL, 0); + wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, adapter->vdev_id, + CSA_REASON_LTE_COEX); + hdd_switch_sap_channel(adapter, restart_chan, true); +} + +int hdd_clone_local_unsafe_chan(struct hdd_context *hdd_ctx, + uint16_t **local_unsafe_list, uint16_t *local_unsafe_list_count) +{ + uint32_t size; + uint16_t *unsafe_list; + uint16_t chan_count; + + if (!hdd_ctx || !local_unsafe_list_count || !local_unsafe_list_count) + return -EINVAL; + + chan_count = QDF_MIN(hdd_ctx->unsafe_channel_count, + NUM_CHANNELS); + if (chan_count) { + size = chan_count * sizeof(hdd_ctx->unsafe_channel_list[0]); + unsafe_list = qdf_mem_malloc(size); + if (!unsafe_list) + return -ENOMEM; + qdf_mem_copy(unsafe_list, hdd_ctx->unsafe_channel_list, size); + } else { + unsafe_list = NULL; + } + + *local_unsafe_list = unsafe_list; + *local_unsafe_list_count = chan_count; + + return 0; +} + +bool hdd_local_unsafe_channel_updated(struct hdd_context *hdd_ctx, + uint16_t *local_unsafe_list, uint16_t local_unsafe_list_count) +{ + int i, j; + + if (local_unsafe_list_count != hdd_ctx->unsafe_channel_count) + return true; + if (local_unsafe_list_count == 0) + return false; + for (i = 0; i < local_unsafe_list_count; i++) { + for (j = 0; j < local_unsafe_list_count; j++) + if (local_unsafe_list[i] == + hdd_ctx->unsafe_channel_list[j]) + break; + if (j >= local_unsafe_list_count) + break; + } + if (i >= local_unsafe_list_count) { + hdd_info("unsafe chan list same"); + return false; + } + + return true; +} +#else +static void hdd_init_channel_avoidance(struct hdd_context *hdd_ctx) +{ +} + +static inline void hdd_lte_coex_restart_sap(struct hdd_adapter *adapter, + struct hdd_context *hdd_ctx) +{ + hdd_debug("Channel avoidance is not enabled; Abort SAP restart"); +} +#endif /* defined(FEATURE_WLAN_CH_AVOID) */ + +QDF_STATUS +wlan_hdd_get_adapter_by_vdev_id_from_objmgr(struct hdd_context *hdd_ctx, + struct hdd_adapter **adapter, + struct wlan_objmgr_vdev *vdev) +{ + *adapter = NULL; + if (!hdd_ctx) + return QDF_STATUS_E_INVAL; + + if (!vdev) { + hdd_err("null vdev object"); + return QDF_STATUS_E_INVAL; + } + + *adapter = vdev->vdev_nif.osdev->legacy_osif_priv; + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_indicate_mgmt_frame() - Wrapper to indicate management frame to + * user space + * @frame_ind: Management frame data to be informed. + * + * This function is used to indicate management frame to + * user space + * + * Return: None + * + */ +void hdd_indicate_mgmt_frame(tSirSmeMgmtFrameInd *frame_ind) +{ + struct hdd_context *hdd_ctx = NULL; + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + int i, num_adapters; + uint8_t vdev_id[WLAN_MAX_VDEVS]; + struct ieee80211_mgmt *mgmt = + (struct ieee80211_mgmt *)frame_ind->frameBuf; + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_INDICATE_MGMT_FRAME; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + if (frame_ind->frame_len < ieee80211_hdrlen(mgmt->frame_control)) { + hdd_err(" Invalid frame length"); + return; + } + + if (SME_SESSION_ID_ANY == frame_ind->sessionId) { + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + adapter = + hdd_get_adapter_by_vdev(hdd_ctx, i); + if (adapter) + break; + } + } else if (SME_SESSION_ID_BROADCAST == frame_ind->sessionId) { + num_adapters = 0; + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, + next_adapter, dbgid) { + vdev_id[num_adapters] = adapter->vdev_id; + num_adapters++; + /* dev_put has to be done here */ + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + adapter = NULL; + + for (i = 0; i < num_adapters; i++) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + hdd_ctx->psoc, + vdev_id[i], + WLAN_OSIF_ID); + + if (!vdev) + continue; + + status = wlan_hdd_get_adapter_by_vdev_id_from_objmgr( + hdd_ctx, &adapter, vdev); + + if (QDF_IS_STATUS_ERROR(status) || !adapter) { + wlan_objmgr_vdev_release_ref(vdev, + WLAN_OSIF_ID); + continue; + } + + hdd_indicate_mgmt_frame_to_user(adapter, + frame_ind->frame_len, + frame_ind->frameBuf, + frame_ind->frameType, + frame_ind->rx_freq, + frame_ind->rxRssi, + frame_ind->rx_flags); + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); + } + + adapter = NULL; + } else { + adapter = hdd_get_adapter_by_vdev(hdd_ctx, + frame_ind->sessionId); + } + + if ((adapter) && + (WLAN_HDD_ADAPTER_MAGIC == adapter->magic)) + hdd_indicate_mgmt_frame_to_user(adapter, + frame_ind->frame_len, + frame_ind->frameBuf, + frame_ind->frameType, + frame_ind->rx_freq, + frame_ind->rxRssi, + frame_ind->rx_flags); +} + +void hdd_acs_response_timeout_handler(void *context) +{ + struct hdd_external_acs_timer_context *timer_context = + (struct hdd_external_acs_timer_context *)context; + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + uint8_t reason; + + hdd_enter(); + if (!timer_context) { + hdd_err("invlaid timer context"); + return; + } + adapter = timer_context->adapter; + reason = timer_context->reason; + + + if ((!adapter) || + (adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) { + hdd_err("invalid adapter or adapter has invalid magic"); + return; + } + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + if (test_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags)) + clear_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags); + else + return; + + hdd_err("ACS timeout happened for %s reason %d", + adapter->dev->name, reason); + + switch (reason) { + /* SAP init case */ + case QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT: + wlan_sap_set_vendor_acs(WLAN_HDD_GET_SAP_CTX_PTR(adapter), + false); + wlan_hdd_cfg80211_start_acs(adapter); + break; + /* DFS detected on current channel */ + case QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS: + wlan_sap_update_next_channel( + WLAN_HDD_GET_SAP_CTX_PTR(adapter), 0, 0); + sme_update_new_channel_event(hdd_ctx->mac_handle, + adapter->vdev_id); + break; + /* LTE coex event on current channel */ + case QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX: + hdd_lte_coex_restart_sap(adapter, hdd_ctx); + break; + default: + hdd_info("invalid reason for timer invoke"); + + } +} + +/** + * hdd_override_ini_config - Override INI config + * @hdd_ctx: HDD context + * + * Override INI config based on module parameter. + * + * Return: None + */ +static void hdd_override_ini_config(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + if (0 == enable_dfs_chan_scan || 1 == enable_dfs_chan_scan) { + ucfg_scan_cfg_set_dfs_chan_scan_allowed(hdd_ctx->psoc, + enable_dfs_chan_scan); + hdd_debug("Module enable_dfs_chan_scan set to %d", + enable_dfs_chan_scan); + } + if (0 == enable_11d || 1 == enable_11d) { + status = ucfg_mlme_set_11d_enabled(hdd_ctx->psoc, enable_11d); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to set 11d_enable flag"); + } + + if (hdd_ctx->config->action_oui_enable && !ucfg_action_oui_enabled()) { + hdd_ctx->config->action_oui_enable = 0; + hdd_err("Ignore action oui ini, since no action_oui component"); + } + + if (QDF_GLOBAL_MONITOR_MODE == cds_get_conparam()) + hdd_override_all_ps(hdd_ctx); +} + +#ifdef ENABLE_MTRACE_LOG +static void hdd_set_mtrace_for_each(struct hdd_context *hdd_ctx) +{ + uint8_t module_id = 0; + int qdf_print_idx = -1; + + qdf_print_idx = qdf_get_pidx(); + for (module_id = 0; module_id < QDF_MODULE_ID_MAX; module_id++) + qdf_print_set_category_verbose( + qdf_print_idx, + module_id, QDF_TRACE_LEVEL_TRACE, + hdd_ctx->config->enable_mtrace); +} +#else +static void hdd_set_mtrace_for_each(struct hdd_context *hdd_ctx) +{ +} + +#endif + +/** + * hdd_log_level_to_bitmask() - user space log level to host log bitmask + * @user_log_level: user space log level + * + * Convert log level from user space to host log level bitmask. + * + * Return: Bitmask of log levels to be enabled + */ +static uint32_t hdd_log_level_to_bitmask(enum host_log_level user_log_level) +{ + QDF_TRACE_LEVEL host_trace_level; + uint32_t bitmask; + + switch (user_log_level) { + case HOST_LOG_LEVEL_NONE: + host_trace_level = QDF_TRACE_LEVEL_NONE; + break; + case HOST_LOG_LEVEL_FATAL: + host_trace_level = QDF_TRACE_LEVEL_FATAL; + break; + case HOST_LOG_LEVEL_ERROR: + host_trace_level = QDF_TRACE_LEVEL_ERROR; + break; + case HOST_LOG_LEVEL_WARN: + host_trace_level = QDF_TRACE_LEVEL_WARN; + break; + case HOST_LOG_LEVEL_INFO: + host_trace_level = QDF_TRACE_LEVEL_INFO_LOW; + break; + case HOST_LOG_LEVEL_DEBUG: + host_trace_level = QDF_TRACE_LEVEL_DEBUG; + break; + case HOST_LOG_LEVEL_TRACE: + host_trace_level = QDF_TRACE_LEVEL_TRACE; + break; + default: + host_trace_level = QDF_TRACE_LEVEL_TRACE; + break; + } + + bitmask = (1 << (host_trace_level + 1)) - 1; + + return bitmask; +} + +/** + * hdd_set_trace_level_for_each - Set trace level for each INI config + * @hdd_ctx - HDD context + * + * Set trace level for each module based on INI config. + * + * Return: None + */ +static void hdd_set_trace_level_for_each(struct hdd_context *hdd_ctx) +{ + uint8_t host_module_log[QDF_MODULE_ID_MAX * 2]; + qdf_size_t host_module_log_num = 0; + QDF_MODULE_ID module_id; + uint32_t bitmask; + uint32_t i; + + hdd_qdf_trace_enable(QDF_MODULE_ID_DP, 0x7f); + + qdf_uint8_array_parse(cfg_get(hdd_ctx->psoc, + CFG_ENABLE_HOST_MODULE_LOG_LEVEL), + host_module_log, + QDF_MODULE_ID_MAX * 2, + &host_module_log_num); + + for (i = 0; i + 1 < host_module_log_num; i += 2) { + module_id = host_module_log[i]; + bitmask = hdd_log_level_to_bitmask(host_module_log[i + 1]); + if (module_id < QDF_MODULE_ID_MAX && + module_id >= QDF_MODULE_ID_MIN) + hdd_qdf_trace_enable(module_id, bitmask); + } + + hdd_set_mtrace_for_each(hdd_ctx); +} + +/** + * hdd_context_init() - Initialize HDD context + * @hdd_ctx: HDD context. + * + * Initialize HDD context along with all the feature specific contexts. + * + * return: 0 on success and errno on failure. + */ +static int hdd_context_init(struct hdd_context *hdd_ctx) +{ + int ret; + + hdd_ctx->ioctl_scan_mode = eSIR_ACTIVE_SCAN; + hdd_ctx->max_intf_count = WLAN_MAX_VDEVS; + + init_completion(&hdd_ctx->mc_sus_event_var); + init_completion(&hdd_ctx->ready_to_suspend); + + qdf_spinlock_create(&hdd_ctx->connection_status_lock); + qdf_spinlock_create(&hdd_ctx->hdd_adapter_lock); + + qdf_list_create(&hdd_ctx->hdd_adapters, 0); + + ret = hdd_scan_context_init(hdd_ctx); + if (ret) + goto list_destroy; + + hdd_rx_wake_lock_create(hdd_ctx); + + ret = hdd_sap_context_init(hdd_ctx); + if (ret) + goto scan_destroy; + + wlan_hdd_cfg80211_extscan_init(hdd_ctx); + + hdd_init_offloaded_packets_ctx(hdd_ctx); + + ret = wlan_hdd_cfg80211_init(hdd_ctx->parent_dev, hdd_ctx->wiphy, + hdd_ctx->config); + if (ret) + goto sap_destroy; + + qdf_wake_lock_create(&hdd_ctx->monitor_mode_wakelock, + "monitor_mode_wakelock"); + + return 0; + +sap_destroy: + hdd_sap_context_destroy(hdd_ctx); + +scan_destroy: + hdd_scan_context_destroy(hdd_ctx); + hdd_rx_wake_lock_destroy(hdd_ctx); +list_destroy: + qdf_list_destroy(&hdd_ctx->hdd_adapters); + + return ret; +} + +void hdd_psoc_idle_timer_start(struct hdd_context *hdd_ctx) +{ + uint32_t timeout_ms = hdd_ctx->config->iface_change_wait_time; + enum wake_lock_reason reason = + WIFI_POWER_EVENT_WAKELOCK_IFACE_CHANGE_TIMER; + + if (!timeout_ms) { + hdd_info("psoc idle timer is disabled"); + return; + } + + hdd_debug("Starting psoc idle timer"); + timeout_ms += HDD_PSOC_IDLE_SHUTDOWN_SUSPEND_DELAY; + qdf_delayed_work_start(&hdd_ctx->psoc_idle_timeout_work, timeout_ms); + hdd_prevent_suspend_timeout(timeout_ms, reason); +} + +void hdd_psoc_idle_timer_stop(struct hdd_context *hdd_ctx) +{ + qdf_delayed_work_stop_sync(&hdd_ctx->psoc_idle_timeout_work); + hdd_debug("Stopped psoc idle timer"); +} + +/** + * __hdd_psoc_idle_shutdown() - perform an idle shutdown on the given psoc + * @hdd_ctx: the hdd context which should be shutdown + * + * When no interfaces are "up" on a psoc, an idle shutdown timer is started. + * If no interfaces are brought up before the timer expires, we do an + * "idle shutdown," cutting power to the physical SoC to save power. This is + * done completely transparently from the perspective of userspace. + * + * Return: None + */ +static int __hdd_psoc_idle_shutdown(struct hdd_context *hdd_ctx) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + hdd_enter(); + + errno = osif_psoc_sync_trans_start(hdd_ctx->parent_dev, &psoc_sync); + if (errno) { + hdd_info("psoc busy, abort idle shutdown; errno:%d", errno); + errno = -EAGAIN; + goto exit; + } + + osif_psoc_sync_wait_for_ops(psoc_sync); + errno = hdd_wlan_stop_modules(hdd_ctx, false); + + osif_psoc_sync_trans_stop(psoc_sync); + +exit: + hdd_exit(); + return errno; +} + +static int __hdd_mode_change_psoc_idle_shutdown(struct hdd_context *hdd_ctx) +{ + is_mode_change_psoc_idle_shutdown = false; + return hdd_wlan_stop_modules(hdd_ctx, true); +} + +int hdd_psoc_idle_shutdown(struct device *dev) +{ + int ret; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) { + hdd_err_rl("hdd ctx is null"); + return -EINVAL; + } + + if (is_mode_change_psoc_idle_shutdown) + ret = __hdd_mode_change_psoc_idle_shutdown(hdd_ctx); + else + ret = __hdd_psoc_idle_shutdown(hdd_ctx); + + return ret; +} + +static int __hdd_psoc_idle_restart(struct hdd_context *hdd_ctx) +{ + int ret; + + ret = hdd_soc_idle_restart_lock(hdd_ctx->parent_dev); + if (ret) + return ret; + + ret = hdd_wlan_start_modules(hdd_ctx, false); + + hdd_soc_idle_restart_unlock(); + + return ret; +} + +int hdd_psoc_idle_restart(struct device *dev) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) { + hdd_err_rl("hdd ctx is null"); + return -EINVAL; + } + + return __hdd_psoc_idle_restart(hdd_ctx); +} + +int hdd_trigger_psoc_idle_restart(struct hdd_context *hdd_ctx) +{ + int ret; + + QDF_BUG(rtnl_is_locked()); + + hdd_psoc_idle_timer_stop(hdd_ctx); + if (hdd_ctx->driver_status == DRIVER_MODULES_ENABLED) { + hdd_nofl_debug("Driver modules already Enabled"); + return 0; + } + + ret = hdd_soc_idle_restart_lock(hdd_ctx->parent_dev); + if (ret) + return ret; + ret = pld_idle_restart(hdd_ctx->parent_dev, hdd_psoc_idle_restart); + hdd_soc_idle_restart_unlock(); + + return ret; +} + +/** + * hdd_psoc_idle_timeout_callback() - Handler for psoc idle timeout + * @priv: pointer to hdd context + * + * Return: None + */ +static void hdd_psoc_idle_timeout_callback(void *priv) +{ + int ret; + struct hdd_context *hdd_ctx = priv; + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + hdd_info("Psoc idle timeout elapsed; starting psoc shutdown"); + + ret = pld_idle_shutdown(hdd_ctx->parent_dev, hdd_psoc_idle_shutdown); + if (-EAGAIN == ret || hdd_ctx->is_wiphy_suspended) { + hdd_debug("System suspend in progress. Restart idle shutdown timer"); + hdd_psoc_idle_timer_start(hdd_ctx); + } + + /* Clear the recovery flag for PCIe discrete soc after idle shutdown*/ + if (PLD_BUS_TYPE_PCIE == pld_get_bus_type(hdd_ctx->parent_dev)) + cds_set_recovery_in_progress(false); +} + +#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE +static void hdd_set_wlan_logging(struct hdd_context *hdd_ctx) +{ + wlan_logging_set_log_to_console(hdd_ctx->config-> + wlan_logging_to_console); + wlan_logging_set_active(hdd_ctx->config->wlan_logging_enable); +} +#else +static void hdd_set_wlan_logging(struct hdd_context *hdd_ctx) +{ } +#endif + +#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE +static void hdd_init_wlan_logging_params(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->wlan_logging_enable = cfg_get(psoc, CFG_WLAN_LOGGING_SUPPORT); + + config->wlan_logging_to_console = + cfg_get(psoc, CFG_WLAN_LOGGING_CONSOLE_SUPPORT); + config->host_log_custom_nl_proto = + cfg_get(psoc, CFG_HOST_LOG_CUSTOM_NETLINK_PROTO); +} +#else +static void hdd_init_wlan_logging_params(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +static void hdd_init_wlan_auto_shutdown(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->wlan_auto_shutdown = cfg_get(psoc, CFG_WLAN_AUTO_SHUTDOWN); +} +#else +static void hdd_init_wlan_auto_shutdown(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifndef REMOVE_PKT_LOG +static void hdd_init_packet_log(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->enable_packet_log = cfg_get(psoc, CFG_ENABLE_PACKET_LOG); +} +#else +static void hdd_init_packet_log(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifdef ENABLE_MTRACE_LOG +static void hdd_init_mtrace_log(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->enable_mtrace = cfg_get(psoc, CFG_ENABLE_MTRACE); +} +#else +static void hdd_init_mtrace_log(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifdef FEATURE_RUNTIME_PM +static void hdd_init_runtime_pm(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->runtime_pm = cfg_get(psoc, CFG_ENABLE_RUNTIME_PM); +} +#else +static void hdd_init_runtime_pm(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) + +{ +} +#endif + +#ifdef WLAN_FEATURE_WMI_SEND_RECV_QMI +static void hdd_init_qmi_stats(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->is_qmi_stats_enabled = cfg_get(psoc, CFG_ENABLE_QMI_STATS); +} +#else +static void hdd_init_qmi_stats(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) + +{ +} +#endif + +#ifdef FEATURE_WLAN_DYNAMIC_CVM +static void hdd_init_vc_mode_cfg_bitmap(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->vc_mode_cfg_bitmap = cfg_get(psoc, CFG_VC_MODE_BITMAP); +} +#else +static void hdd_init_vc_mode_cfg_bitmap(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifdef DHCP_SERVER_OFFLOAD +static void +hdd_init_dhcp_server_ip(struct hdd_context *hdd_ctx) +{ + uint8_t num_entries; + + hdd_ctx->config->dhcp_server_ip.is_dhcp_server_ip_valid = true; + hdd_string_to_u8_array(cfg_get(hdd_ctx->psoc, CFG_DHCP_SERVER_IP_NAME), + hdd_ctx->config->dhcp_server_ip.dhcp_server_ip, + &num_entries, IPADDR_NUM_ENTRIES); + + if (num_entries != IPADDR_NUM_ENTRIES) { + hdd_err("Incorrect IP address (%s) assigned for DHCP server!", + cfg_get(hdd_ctx->psoc, CFG_DHCP_SERVER_IP_NAME)); + hdd_config->dhcp_server_ip.is_dhcp_server_ip_valid = false; + } +} +#else +static void +hdd_init_dhcp_server_ip(struct hdd_context *hdd_ctx) +{ +} +#endif + +#ifdef SAR_SAFETY_FEATURE +static void hdd_sar_cfg_update(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->sar_safety_timeout = cfg_get(psoc, CFG_SAR_SAFETY_TIMEOUT); + config->sar_safety_unsolicited_timeout = + cfg_get(psoc, CFG_SAR_SAFETY_UNSOLICITED_TIMEOUT); + config->sar_safety_req_resp_timeout = + cfg_get(psoc, CFG_SAR_SAFETY_REQ_RESP_TIMEOUT); + config->sar_safety_req_resp_retry = + cfg_get(psoc, CFG_SAR_SAFETY_REQ_RESP_RETRIES); + config->sar_safety_index = cfg_get(psoc, CFG_SAR_SAFETY_INDEX); + config->sar_safety_sleep_index = + cfg_get(psoc, CFG_SAR_SAFETY_SLEEP_INDEX); + config->enable_sar_safety = + cfg_get(psoc, CFG_ENABLE_SAR_SAFETY_FEATURE); + config->config_sar_safety_sleep_index = + cfg_get(psoc, CFG_CONFIG_SAR_SAFETY_SLEEP_MODE_INDEX); +} +#else +static void hdd_sar_cfg_update(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +/** + * hdd_cfg_params_init() - Initialize hdd params in hdd_config strucuture + * @hdd_ctx - Pointer to HDD context + * + * Return: None + */ +static void hdd_cfg_params_init(struct hdd_context *hdd_ctx) +{ + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + struct hdd_config *config = hdd_ctx->config; + if (!psoc) { + hdd_err("Invalid psoc"); + return; + } + + if (!config) { + hdd_err("Invalid hdd config"); + return; + } + + config->dot11Mode = cfg_get(psoc, CFG_HDD_DOT11_MODE); + config->bug_on_reinit_failure = cfg_get(psoc, + CFG_BUG_ON_REINIT_FAILURE); + + config->is_ramdump_enabled = cfg_get(psoc, + CFG_ENABLE_RAMDUMP_COLLECTION); + + config->iface_change_wait_time = cfg_get(psoc, + CFG_INTERFACE_CHANGE_WAIT); + + config->multicast_host_fw_msgs = cfg_get(psoc, + CFG_MULTICAST_HOST_FW_MSGS); + + config->private_wext_control = cfg_get(psoc, CFG_PRIVATE_WEXT_CONTROL); + config->enablefwprint = cfg_get(psoc, CFG_ENABLE_FW_UART_PRINT); + config->enable_fw_log = cfg_get(psoc, CFG_ENABLE_FW_LOG); + config->operating_chan_freq = cfg_get(psoc, CFG_OPERATING_FREQUENCY); + config->num_vdevs = cfg_get(psoc, CFG_NUM_VDEV_ENABLE); + qdf_str_lcopy(config->enable_concurrent_sta, + cfg_get(psoc, CFG_ENABLE_CONCURRENT_STA), + CFG_CONCURRENT_IFACE_MAX_LEN); + qdf_str_lcopy(config->dbs_scan_selection, + cfg_get(psoc, CFG_DBS_SCAN_SELECTION), + CFG_DBS_SCAN_PARAM_LENGTH); + config->inform_bss_rssi_raw = cfg_get(psoc, CFG_INFORM_BSS_RSSI_RAW); + config->mac_provision = cfg_get(psoc, CFG_ENABLE_MAC_PROVISION); + config->provisioned_intf_pool = + cfg_get(psoc, CFG_PROVISION_INTERFACE_POOL); + config->derived_intf_pool = cfg_get(psoc, CFG_DERIVED_INTERFACE_POOL); + config->action_oui_enable = cfg_get(psoc, CFG_ENABLE_ACTION_OUI); + config->advertise_concurrent_operation = + cfg_get(psoc, + CFG_ADVERTISE_CONCURRENT_OPERATION); + qdf_str_lcopy(config->action_oui_str[0], + cfg_get(psoc, CFG_ACTION_OUI_CONNECT_1X1), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str[1], + cfg_get(psoc, CFG_ACTION_OUI_ITO_EXTENSION), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str[2], + cfg_get(psoc, CFG_ACTION_OUI_CCKM_1X1), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str[3], + cfg_get(psoc, CFG_ACTION_OUI_ITO_ALTERNATE), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str[4], + cfg_get(psoc, CFG_ACTION_OUI_SWITCH_TO_11N_MODE), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str[5], + cfg_get(psoc, + CFG_ACTION_OUI_CONNECT_1X1_WITH_1_CHAIN), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str[6], + cfg_get(psoc, + CFG_ACTION_OUI_DISABLE_AGGRESSIVE_TX), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str[ACTION_OUI_FORCE_MAX_NSS], + cfg_get(psoc, CFG_ACTION_OUI_FORCE_MAX_NSS), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str + [ACTION_OUI_DISABLE_AGGRESSIVE_EDCA], + cfg_get(psoc, + CFG_ACTION_OUI_DISABLE_AGGRESSIVE_EDCA), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str[ACTION_OUI_DISABLE_TWT], + cfg_get(psoc, CFG_ACTION_OUI_DISABLE_TWT), + ACTION_OUI_MAX_STR_LEN); + qdf_str_lcopy(config->action_oui_str[ACTION_OUI_HOST_RECONN], + cfg_get(psoc, CFG_ACTION_OUI_RECONN_ASSOCTIMEOUT), + ACTION_OUI_MAX_STR_LEN); + + config->is_unit_test_framework_enabled = + cfg_get(psoc, CFG_ENABLE_UNIT_TEST_FRAMEWORK); + config->disable_channel = cfg_get(psoc, CFG_ENABLE_DISABLE_CHANNEL); + config->enable_sar_conversion = cfg_get(psoc, CFG_SAR_CONVERSION); + config->is_wow_disabled = cfg_get(psoc, CFG_WOW_DISABLE); + config->nb_commands_interval = + cfg_get(psoc, CFG_NB_COMMANDS_RATE_LIMIT); + + hdd_periodic_sta_stats_config(config, psoc); + hdd_init_vc_mode_cfg_bitmap(config, psoc); + hdd_init_runtime_pm(config, psoc); + hdd_init_wlan_auto_shutdown(config, psoc); + hdd_init_wlan_logging_params(config, psoc); + hdd_init_packet_log(config, psoc); + hdd_init_mtrace_log(config, psoc); + hdd_init_dhcp_server_ip(hdd_ctx); + hdd_dp_cfg_update(psoc, hdd_ctx); + hdd_sar_cfg_update(config, psoc); + hdd_init_qmi_stats(config, psoc); +} + +struct hdd_context *hdd_context_create(struct device *dev) +{ + QDF_STATUS status; + int ret = 0; + struct hdd_context *hdd_ctx; + + hdd_enter(); + + hdd_ctx = hdd_cfg80211_wiphy_alloc(); + if (!hdd_ctx) { + ret = -ENOMEM; + goto err_out; + } + + status = qdf_delayed_work_create(&hdd_ctx->psoc_idle_timeout_work, + hdd_psoc_idle_timeout_callback, + hdd_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + ret = qdf_status_to_os_return(status); + goto wiphy_dealloc; + } + + hdd_ctx->parent_dev = dev; + hdd_ctx->last_scan_reject_vdev_id = WLAN_UMAC_VDEV_ID_MAX; + + hdd_ctx->config = qdf_mem_malloc(sizeof(struct hdd_config)); + if (!hdd_ctx->config) { + ret = -ENOMEM; + goto err_free_hdd_context; + } + + status = cfg_parse(WLAN_INI_FILE); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to parse cfg %s; status:%d\n", + WLAN_INI_FILE, status); + ret = qdf_status_to_os_return(status); + goto err_free_config; + } + + ret = hdd_objmgr_create_and_store_psoc(hdd_ctx, DEFAULT_PSOC_ID); + if (ret) { + QDF_DEBUG_PANIC("Psoc creation fails!"); + goto err_release_store; + } + + hdd_cfg_params_init(hdd_ctx); + + /* apply multiplier config, if not already set via module parameter */ + if (qdf_timer_get_multiplier() == 1) + qdf_timer_set_multiplier(cfg_get(hdd_ctx->psoc, + CFG_TIMER_MULTIPLIER)); + hdd_debug("set timer multiplier: %u", qdf_timer_get_multiplier()); + + cds_set_fatal_event(cfg_get(hdd_ctx->psoc, + CFG_ENABLE_FATAL_EVENT_TRIGGER)); + + hdd_override_ini_config(hdd_ctx); + + ret = hdd_context_init(hdd_ctx); + + if (ret) + goto err_hdd_objmgr_destroy; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE || + hdd_get_conparam() == QDF_GLOBAL_EPPING_MODE) + goto skip_multicast_logging; + + cds_set_multicast_logging(hdd_ctx->config->multicast_host_fw_msgs); + ret = hdd_init_netlink_services(hdd_ctx); + if (ret) + goto err_deinit_hdd_context; + + hdd_set_wlan_logging(hdd_ctx); + qdf_atomic_init(&hdd_ctx->adapter_ops_history.index); + +skip_multicast_logging: + hdd_set_trace_level_for_each(hdd_ctx); + + cds_set_context(QDF_MODULE_ID_HDD, hdd_ctx); + + wlan_hdd_sar_timers_init(hdd_ctx); + + hdd_exit(); + + return hdd_ctx; + +err_deinit_hdd_context: + hdd_context_deinit(hdd_ctx); + +err_hdd_objmgr_destroy: + hdd_objmgr_release_and_destroy_psoc(hdd_ctx); + +err_release_store: + cfg_release(); + +err_free_config: + qdf_mem_free(hdd_ctx->config); + +err_free_hdd_context: + qdf_delayed_work_destroy(&hdd_ctx->psoc_idle_timeout_work); + +wiphy_dealloc: + wiphy_free(hdd_ctx->wiphy); + +err_out: + return ERR_PTR(ret); +} + +/** + * hdd_start_station_adapter()- Start the Station Adapter + * @adapter: HDD adapter + * + * This function initializes the adapter for the station mode. + * + * Return: 0 on success or errno on failure. + */ +int hdd_start_station_adapter(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + bool reset; + + hdd_enter_dev(adapter->dev); + if (test_bit(SME_SESSION_OPENED, &adapter->event_flags)) { + hdd_err("session is already opened, %d", + adapter->vdev_id); + return qdf_status_to_os_return(QDF_STATUS_SUCCESS); + } + + ret = hdd_vdev_create(adapter); + if (ret) { + hdd_err("failed to create vdev: %d", ret); + return ret; + } + status = hdd_init_station_mode(adapter); + + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Error Initializing station mode: %d", status); + return qdf_status_to_os_return(status); + } + + hdd_register_tx_flow_control(adapter, + hdd_tx_resume_timer_expired_handler, + hdd_tx_resume_cb, + hdd_tx_flow_control_is_pause); + + hdd_register_hl_netdev_fc_timer(adapter, + hdd_tx_resume_timer_expired_handler); + + status = ucfg_mlme_cfg_get_wlm_reset(hdd_ctx->psoc, &reset); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("could not get the wlm reset flag"); + reset = false; + } + + if (!reset) { + status = sme_set_wlm_latency_level(hdd_ctx->mac_handle, + adapter->vdev_id, + adapter->latency_level); + if (QDF_IS_STATUS_ERROR(status)) + hdd_warn("set wlm mode failed, %u", status); + else + hdd_debug("set wlm mode %d", adapter->latency_level); + } + + hdd_exit(); + + return 0; +} + +/** + * hdd_start_ap_adapter()- Start AP Adapter + * @adapter: HDD adapter + * + * This function initializes the adapter for the AP mode. + * + * Return: 0 on success errno on failure. + */ +int hdd_start_ap_adapter(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + bool is_ssr = false; + int ret; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t fine_time_meas_cap = 0; + + hdd_enter(); + + if (test_bit(SME_SESSION_OPENED, &adapter->event_flags)) { + hdd_err("session is already opened, %d", + adapter->vdev_id); + return qdf_status_to_os_return(QDF_STATUS_SUCCESS); + } + /* + * In SSR case no need to create new sap context. + * Otherwise create sap context first and then create + * vdev as while creating the vdev, driver needs to + * register SAP callback and that callback uses sap context + */ + if (adapter->session.ap.sap_context) { + is_ssr = true; + } else if (!hdd_sap_create_ctx(adapter)) { + hdd_err("sap creation failed"); + return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); + } + + ret = hdd_vdev_create(adapter); + if (ret) { + hdd_err("failed to create vdev, status:%d", ret); + goto sap_destroy_ctx; + } + + status = sap_acquire_vdev_ref(hdd_ctx->psoc, + WLAN_HDD_GET_SAP_CTX_PTR(adapter), + adapter->vdev_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to get vdev ref for sap for session_id: %u", + adapter->vdev_id); + ret = qdf_status_to_os_return(status); + goto sap_vdev_destroy; + } + + if (adapter->device_mode == QDF_SAP_MODE) { + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, + &fine_time_meas_cap); + sme_cli_set_command( + adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_DISABLE_RTT_RESPONDER_ROLE, + (bool)(fine_time_meas_cap & WMI_FW_AP_RTT_RESPR), + VDEV_CMD); + sme_cli_set_command( + adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_DISABLE_RTT_INITIATOR_ROLE, + (bool)(fine_time_meas_cap & WMI_FW_AP_RTT_INITR), + VDEV_CMD); + } + + status = hdd_init_ap_mode(adapter, is_ssr); + + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Error Initializing the AP mode: %d", status); + ret = qdf_status_to_os_return(status); + goto sap_release_ref; + } + + hdd_register_tx_flow_control(adapter, + hdd_softap_tx_resume_timer_expired_handler, + hdd_softap_tx_resume_cb, + hdd_tx_flow_control_is_pause); + + hdd_register_hl_netdev_fc_timer(adapter, + hdd_tx_resume_timer_expired_handler); + + hdd_exit(); + return 0; + +sap_release_ref: + sap_release_vdev_ref(WLAN_HDD_GET_SAP_CTX_PTR(adapter)); +sap_vdev_destroy: + hdd_vdev_destroy(adapter); +sap_destroy_ctx: + hdd_sap_destroy_ctx(adapter); + return ret; +} + +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +/** + * hdd_txrx_populate_cds_config() - Populate txrx cds configuration + * @cds_cfg: CDS Configuration + * @hdd_ctx: Pointer to hdd context + * + * Return: none + */ +static inline void hdd_txrx_populate_cds_config(struct cds_config_info + *cds_cfg, + struct hdd_context *hdd_ctx) +{ + cds_cfg->tx_flow_stop_queue_th = + cfg_get(hdd_ctx->psoc, CFG_DP_TX_FLOW_STOP_QUEUE_TH); + cds_cfg->tx_flow_start_queue_offset = + cfg_get(hdd_ctx->psoc, CFG_DP_TX_FLOW_START_QUEUE_OFFSET); + /* configuration for DP RX Threads */ + cds_cfg->enable_dp_rx_threads = hdd_ctx->enable_dp_rx_threads; +} +#else +static inline void hdd_txrx_populate_cds_config(struct cds_config_info + *cds_cfg, + struct hdd_context *hdd_ctx) +{ +} +#endif + +/** + * hdd_update_cds_config() - API to update cds configuration parameters + * @hdd_ctx: HDD Context + * + * Return: 0 for Success, errno on failure + */ +static int hdd_update_cds_config(struct hdd_context *hdd_ctx) +{ + struct cds_config_info *cds_cfg; + int value; + uint8_t band_capability; + uint32_t band_bitmap; + uint8_t ito_repeat_count; + bool crash_inject; + bool self_recovery; + bool fw_timeout_crash; + QDF_STATUS status; + + cds_cfg = qdf_mem_malloc(sizeof(*cds_cfg)); + if (!cds_cfg) + return -ENOMEM; + + cds_cfg->driver_type = QDF_DRIVER_TYPE_PRODUCTION; + ucfg_mlme_get_sap_max_modulated_dtim(hdd_ctx->psoc, + &cds_cfg->sta_maxlimod_dtim); + + status = ucfg_mlme_get_crash_inject(hdd_ctx->psoc, &crash_inject); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get crash inject ini config"); + goto exit; + } + + status = ucfg_mlme_get_self_recovery(hdd_ctx->psoc, &self_recovery); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get self recovery ini config"); + goto exit; + } + + status = ucfg_mlme_get_fw_timeout_crash(hdd_ctx->psoc, + &fw_timeout_crash); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get fw timeout crash ini config"); + goto exit; + } + + status = ucfg_mlme_get_ito_repeat_count(hdd_ctx->psoc, + &ito_repeat_count); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get ITO repeat count ini config"); + goto exit; + } + + cds_cfg->force_target_assert_enabled = crash_inject; + + ucfg_mlme_get_sap_max_offload_peers(hdd_ctx->psoc, &value); + cds_cfg->ap_maxoffload_peers = value; + ucfg_mlme_get_sap_max_offload_reorder_buffs(hdd_ctx->psoc, + &value); + cds_cfg->ap_maxoffload_reorderbuffs = value; + + cds_cfg->reorder_offload = + cfg_get(hdd_ctx->psoc, CFG_DP_REORDER_OFFLOAD_SUPPORT); + + /* IPA micro controller data path offload resource config item */ + cds_cfg->uc_offload_enabled = ucfg_ipa_uc_is_enabled(); + + cds_cfg->enable_rxthread = hdd_ctx->enable_rxthread; + ucfg_mlme_get_sap_max_peers(hdd_ctx->psoc, &value); + cds_cfg->max_station = value; + cds_cfg->sub_20_channel_width = WLAN_SUB_20_CH_WIDTH_NONE; + cds_cfg->max_msdus_per_rxinorderind = + cfg_get(hdd_ctx->psoc, CFG_DP_MAX_MSDUS_PER_RXIND); + cds_cfg->self_recovery_enabled = self_recovery; + cds_cfg->fw_timeout_crash = fw_timeout_crash; + + cds_cfg->ito_repeat_count = ito_repeat_count; + + status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_bitmap); + if (QDF_IS_STATUS_ERROR(status)) + goto exit; + + band_capability = wlan_reg_band_bitmap_to_band_info(band_bitmap); + cds_cfg->bandcapability = band_capability; + cds_cfg->num_vdevs = hdd_ctx->config->num_vdevs; + cds_cfg->enable_tx_compl_tsf64 = + hdd_tsf_is_tsf64_tx_set(hdd_ctx); + hdd_txrx_populate_cds_config(cds_cfg, hdd_ctx); + hdd_lpass_populate_cds_config(cds_cfg, hdd_ctx); + cds_init_ini_config(cds_cfg); + return 0; + +exit: + qdf_mem_free(cds_cfg); + return -EINVAL; +} + +/** + * hdd_update_user_config() - API to update user configuration + * parameters to obj mgr which are used by multiple components + * @hdd_ctx: HDD Context + * + * Return: 0 for Success, errno on failure + */ +static int hdd_update_user_config(struct hdd_context *hdd_ctx) +{ + struct wlan_objmgr_psoc_user_config *user_config; + uint8_t band_capability; + uint32_t band_bitmap; + QDF_STATUS status; + bool value = false; + + status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_bitmap); + if (QDF_IS_STATUS_ERROR(status)) + return -EIO; + + user_config = qdf_mem_malloc(sizeof(*user_config)); + if (!user_config) + return -ENOMEM; + + user_config->dot11_mode = hdd_ctx->config->dot11Mode; + status = ucfg_mlme_is_11d_enabled(hdd_ctx->psoc, &value); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Invalid 11d_enable flag"); + user_config->is_11d_support_enabled = value; + + value = false; + status = ucfg_mlme_is_11h_enabled(hdd_ctx->psoc, &value); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Invalid 11h_enable flag"); + user_config->is_11h_support_enabled = value; + band_capability = wlan_reg_band_bitmap_to_band_info(band_bitmap); + user_config->band_capability = band_capability; + wlan_objmgr_psoc_set_user_config(hdd_ctx->psoc, user_config); + + qdf_mem_free(user_config); + return 0; +} + +/** + * hdd_init_thermal_info - Initialize thermal level + * @hdd_ctx: HDD context + * + * Initialize thermal level at SME layer and set the thermal level callback + * which would be called when a configured thermal threshold is hit. + * + * Return: 0 on success and errno on failure + */ +static int hdd_init_thermal_info(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + mac_handle_t mac_handle = hdd_ctx->mac_handle; + + status = sme_init_thermal_info(mac_handle); + + if (!QDF_IS_STATUS_SUCCESS(status)) + return qdf_status_to_os_return(status); + + sme_add_set_thermal_level_callback(mac_handle, + hdd_set_thermal_level_cb); + + return 0; + +} + +#if defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK) +/** + * hdd_hold_rtnl_lock - Hold RTNL lock + * + * Hold RTNL lock + * + * Return: True if held and false otherwise + */ +static inline bool hdd_hold_rtnl_lock(void) +{ + rtnl_lock(); + return true; +} + +/** + * hdd_release_rtnl_lock - Release RTNL lock + * + * Release RTNL lock + * + * Return: None + */ +static inline void hdd_release_rtnl_lock(void) +{ + rtnl_unlock(); +} +#else +static inline bool hdd_hold_rtnl_lock(void) { return false; } +static inline void hdd_release_rtnl_lock(void) { } +#endif + +#if !defined(REMOVE_PKT_LOG) + +/* MAX iwpriv command support */ +#define PKTLOG_SET_BUFF_SIZE 3 +#define PKTLOG_CLEAR_BUFF 4 +/* Set Maximum pktlog file size to 64MB */ +#define MAX_PKTLOG_SIZE 64 + +/** + * hdd_pktlog_set_buff_size() - set pktlog buffer size + * @hdd_ctx: hdd context + * @set_value2: pktlog buffer size value + * + * + * Return: 0 for success or error. + */ +static int hdd_pktlog_set_buff_size(struct hdd_context *hdd_ctx, int set_value2) +{ + struct sir_wifi_start_log start_log = { 0 }; + QDF_STATUS status; + + start_log.ring_id = RING_ID_PER_PACKET_STATS; + start_log.verbose_level = WLAN_LOG_LEVEL_OFF; + start_log.ini_triggered = cds_is_packet_log_enabled(); + start_log.user_triggered = 1; + start_log.size = set_value2; + start_log.is_pktlog_buff_clear = false; + + status = sme_wifi_start_logger(hdd_ctx->mac_handle, start_log); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_wifi_start_logger failed(err=%d)", status); + hdd_exit(); + return -EINVAL; + } + + return 0; +} + +/** + * hdd_pktlog_clear_buff() - clear pktlog buffer + * @hdd_ctx: hdd context + * + * Return: 0 for success or error. + */ +static int hdd_pktlog_clear_buff(struct hdd_context *hdd_ctx) +{ + struct sir_wifi_start_log start_log; + QDF_STATUS status; + + start_log.ring_id = RING_ID_PER_PACKET_STATS; + start_log.verbose_level = WLAN_LOG_LEVEL_OFF; + start_log.ini_triggered = cds_is_packet_log_enabled(); + start_log.user_triggered = 1; + start_log.size = 0; + start_log.is_pktlog_buff_clear = true; + + status = sme_wifi_start_logger(hdd_ctx->mac_handle, start_log); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_wifi_start_logger failed(err=%d)", status); + hdd_exit(); + return -EINVAL; + } + + return 0; +} + + +/** + * hdd_process_pktlog_command() - process pktlog command + * @hdd_ctx: hdd context + * @set_value: value set by user + * @set_value2: pktlog buffer size value + * + * This function process pktlog command. + * set_value2 only matters when set_value is 3 (set buff size) + * otherwise we ignore it. + * + * Return: 0 for success or error. + */ +int hdd_process_pktlog_command(struct hdd_context *hdd_ctx, uint32_t set_value, + int set_value2) +{ + int ret; + bool enable; + uint8_t user_triggered = 0; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + hdd_debug("set pktlog %d, set size %d", set_value, set_value2); + + if (set_value > PKTLOG_CLEAR_BUFF) { + hdd_err("invalid pktlog value %d", set_value); + return -EINVAL; + } + + if (set_value == PKTLOG_SET_BUFF_SIZE) { + if (set_value2 <= 0) { + hdd_err("invalid pktlog size %d", set_value2); + return -EINVAL; + } else if (set_value2 > MAX_PKTLOG_SIZE) { + hdd_err_rl("Pktlog size is large. max value is %uMB.", + MAX_PKTLOG_SIZE); + return -EINVAL; + } + return hdd_pktlog_set_buff_size(hdd_ctx, set_value2); + } else if (set_value == PKTLOG_CLEAR_BUFF) { + return hdd_pktlog_clear_buff(hdd_ctx); + } + + /* + * set_value = 0 then disable packetlog + * set_value = 1 enable packetlog forcefully + * set_vlaue = 2 then disable packetlog if disabled through ini or + * enable packetlog with AUTO type. + */ + enable = ((set_value > 0) && cds_is_packet_log_enabled()) ? + true : false; + + if (1 == set_value) { + enable = true; + user_triggered = 1; + } + + return hdd_pktlog_enable_disable(hdd_ctx, enable, user_triggered, 0); +} + +/** + * hdd_pktlog_enable_disable() - Enable/Disable packet logging + * @hdd_ctx: HDD context + * @enable_disable_flag: Flag to enable/disable + * @user_triggered: triggered through iwpriv + * @size: buffer size to be used for packetlog + * + * Return: 0 on success; error number otherwise + */ +int hdd_pktlog_enable_disable(struct hdd_context *hdd_ctx, + bool enable_disable_flag, + uint8_t user_triggered, int size) +{ + struct sir_wifi_start_log start_log; + QDF_STATUS status; + + if (hdd_ctx->is_pktlog_enabled && enable_disable_flag) + return 0; + + if ((!hdd_ctx->is_pktlog_enabled) && (!enable_disable_flag)) + return 0; + + start_log.ring_id = RING_ID_PER_PACKET_STATS; + start_log.verbose_level = + enable_disable_flag ? + WLAN_LOG_LEVEL_ACTIVE : WLAN_LOG_LEVEL_OFF; + start_log.ini_triggered = cds_is_packet_log_enabled(); + start_log.user_triggered = user_triggered; + start_log.size = size; + start_log.is_pktlog_buff_clear = false; + /* + * Use "is_iwpriv_command" flag to distinguish iwpriv command from other + * commands. Host uses this flag to decide whether to send pktlog + * disable command to fw without sending pktlog enable command + * previously. For eg, If vendor sends pktlog disable command without + * sending pktlog enable command, then host discards the packet + * but for iwpriv command, host will send it to fw. + */ + start_log.is_iwpriv_command = 1; + + status = sme_wifi_start_logger(hdd_ctx->mac_handle, start_log); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_wifi_start_logger failed(err=%d)", status); + hdd_exit(); + return -EINVAL; + } + + hdd_ctx->is_pktlog_enabled = enable_disable_flag; + + return 0; +} +#endif /* REMOVE_PKT_LOG */ + +void hdd_free_mac_address_lists(struct hdd_context *hdd_ctx) +{ + hdd_debug("Resetting MAC address lists"); + qdf_mem_zero(hdd_ctx->provisioned_mac_addr, + sizeof(hdd_ctx->provisioned_mac_addr)); + qdf_mem_zero(hdd_ctx->derived_mac_addr, + sizeof(hdd_ctx->derived_mac_addr)); + hdd_ctx->num_provisioned_addr = 0; + hdd_ctx->num_derived_addr = 0; + hdd_ctx->provisioned_intf_addr_mask = 0; + hdd_ctx->derived_intf_addr_mask = 0; +} + +/** + * hdd_get_platform_wlan_mac_buff() - API to query platform driver + * for MAC address + * @dev: Device Pointer + * @num: Number of Valid Mac address + * + * Return: Pointer to MAC address buffer + */ +static uint8_t *hdd_get_platform_wlan_mac_buff(struct device *dev, + uint32_t *num) +{ + return pld_get_wlan_mac_address(dev, num); +} + +/** + * hdd_get_platform_wlan_derived_mac_buff() - API to query platform driver + * for derived MAC address + * @dev: Device Pointer + * @num: Number of Valid Mac address + * + * Return: Pointer to MAC address buffer + */ +static uint8_t *hdd_get_platform_wlan_derived_mac_buff(struct device *dev, + uint32_t *num) +{ + return pld_get_wlan_derived_mac_address(dev, num); +} + +/** + * hdd_populate_random_mac_addr() - API to populate random mac addresses + * @hdd_ctx: HDD Context + * @num: Number of random mac addresses needed + * + * Generate random addresses using bit manipulation on the base mac address + * + * Return: None + */ +void hdd_populate_random_mac_addr(struct hdd_context *hdd_ctx, uint32_t num) +{ + uint32_t idx = hdd_ctx->num_derived_addr; + uint32_t iter; + uint8_t *buf = NULL; + uint8_t macaddr_b3, tmp_br3; + /* + * Consider first provisioned mac address as source address to derive + * remaining addresses + */ + + uint8_t *src = hdd_ctx->provisioned_mac_addr[0].bytes; + + for (iter = 0; iter < num; ++iter, ++idx) { + buf = hdd_ctx->derived_mac_addr[idx].bytes; + qdf_mem_copy(buf, src, QDF_MAC_ADDR_SIZE); + macaddr_b3 = buf[3]; + tmp_br3 = ((macaddr_b3 >> 4 & INTF_MACADDR_MASK) + idx) & + INTF_MACADDR_MASK; + macaddr_b3 += tmp_br3; + macaddr_b3 ^= (1 << INTF_MACADDR_MASK); + buf[0] |= 0x02; + buf[3] = macaddr_b3; + hdd_debug(QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(buf)); + hdd_ctx->num_derived_addr++; + } +} + +/** + * hdd_platform_wlan_mac() - API to get mac addresses from platform driver + * @hdd_ctx: HDD Context + * + * API to get mac addresses from platform driver and update the driver + * structures and configure FW with the base mac address. + * Return: int + */ +static int hdd_platform_wlan_mac(struct hdd_context *hdd_ctx) +{ + uint32_t no_of_mac_addr, iter; + uint32_t max_mac_addr = QDF_MAX_CONCURRENCY_PERSONA; + uint32_t mac_addr_size = QDF_MAC_ADDR_SIZE; + uint8_t *addr, *buf; + struct device *dev = hdd_ctx->parent_dev; + tSirMacAddr mac_addr; + QDF_STATUS status; + + addr = hdd_get_platform_wlan_mac_buff(dev, &no_of_mac_addr); + + if (no_of_mac_addr == 0 || !addr) { + hdd_debug("No mac configured from platform driver"); + return -EINVAL; + } + + hdd_free_mac_address_lists(hdd_ctx); + + if (no_of_mac_addr > max_mac_addr) + no_of_mac_addr = max_mac_addr; + + qdf_mem_copy(&mac_addr, addr, mac_addr_size); + + for (iter = 0; iter < no_of_mac_addr; ++iter, addr += mac_addr_size) { + buf = hdd_ctx->provisioned_mac_addr[iter].bytes; + qdf_mem_copy(buf, addr, QDF_MAC_ADDR_SIZE); + hdd_info("provisioned MAC Addr [%d]" QDF_MAC_ADDR_FMT, iter, + QDF_MAC_ADDR_REF(buf)); + } + + + hdd_ctx->num_provisioned_addr = no_of_mac_addr; + + if (hdd_ctx->config->mac_provision) { + addr = hdd_get_platform_wlan_derived_mac_buff(dev, + &no_of_mac_addr); + + if (no_of_mac_addr == 0 || !addr) + hdd_warn("No derived address from platform driver"); + else if (no_of_mac_addr > + (max_mac_addr - hdd_ctx->num_provisioned_addr)) + no_of_mac_addr = (max_mac_addr - + hdd_ctx->num_provisioned_addr); + + for (iter = 0; iter < no_of_mac_addr; ++iter, + addr += mac_addr_size) { + buf = hdd_ctx->derived_mac_addr[iter].bytes; + qdf_mem_copy(buf, addr, QDF_MAC_ADDR_SIZE); + hdd_debug("derived MAC Addr [%d]" QDF_MAC_ADDR_FMT, iter, + QDF_MAC_ADDR_REF(buf)); + } + hdd_ctx->num_derived_addr = no_of_mac_addr; + } + + no_of_mac_addr = hdd_ctx->num_provisioned_addr + + hdd_ctx->num_derived_addr; + if (no_of_mac_addr < max_mac_addr) + hdd_populate_random_mac_addr(hdd_ctx, max_mac_addr - + no_of_mac_addr); + + status = sme_set_custom_mac_addr(mac_addr); + if (!QDF_IS_STATUS_SUCCESS(status)) + return -EAGAIN; + + return 0; +} + +/** + * hdd_update_mac_addr_to_fw() - API to update wlan mac addresses to FW + * @hdd_ctx: HDD Context + * + * Update MAC address to FW. If MAC address passed by FW is invalid, host + * will generate its own MAC and update it to FW. + * + * Return: 0 for success + * Non-zero error code for failure + */ +static int hdd_update_mac_addr_to_fw(struct hdd_context *hdd_ctx) +{ + tSirMacAddr custom_mac_addr; + QDF_STATUS status; + + if (hdd_ctx->num_provisioned_addr) + qdf_mem_copy(&custom_mac_addr, + &hdd_ctx->provisioned_mac_addr[0].bytes[0], + sizeof(tSirMacAddr)); + else + qdf_mem_copy(&custom_mac_addr, + &hdd_ctx->derived_mac_addr[0].bytes[0], + sizeof(tSirMacAddr)); + status = sme_set_custom_mac_addr(custom_mac_addr); + if (!QDF_IS_STATUS_SUCCESS(status)) + return -EAGAIN; + return 0; +} + +/** + * hdd_initialize_mac_address() - API to get wlan mac addresses + * @hdd_ctx: HDD Context + * + * Get MAC addresses from platform driver or wlan_mac.bin. If platform driver + * is provisioned with mac addresses, driver uses it, else it will use + * wlan_mac.bin to update HW MAC addresses. + * + * Return: None + */ +static int hdd_initialize_mac_address(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + int ret; + bool update_mac_addr_to_fw = true; + + ret = hdd_platform_wlan_mac(hdd_ctx); + if (!ret) { + hdd_info("using MAC address from platform driver"); + return ret; + } else if (hdd_ctx->config->mac_provision) { + hdd_err("getting MAC address from platform driver failed"); + return ret; + } + + status = hdd_update_mac_config(hdd_ctx); + if (QDF_IS_STATUS_SUCCESS(status)) { + hdd_info("using MAC address from wlan_mac.bin"); + return 0; + } + + hdd_info("using default MAC address"); + + /* Use fw provided MAC */ + if (!qdf_is_macaddr_zero(&hdd_ctx->hw_macaddr)) { + hdd_update_macaddr(hdd_ctx, hdd_ctx->hw_macaddr, false); + update_mac_addr_to_fw = false; + return 0; + } else if (hdd_generate_macaddr_auto(hdd_ctx) != 0) { + struct qdf_mac_addr mac_addr; + + hdd_err("MAC failure from device serial no."); + qdf_get_random_bytes(&mac_addr, sizeof(mac_addr)); + /* + * Reset multicast bit (bit-0) and set + * locally-administered bit + */ + mac_addr.bytes[0] = 0x2; + hdd_update_macaddr(hdd_ctx, mac_addr, true); + } + + if (update_mac_addr_to_fw) { + ret = hdd_update_mac_addr_to_fw(hdd_ctx); + if (ret) + hdd_err("MAC address out-of-sync, ret:%d", ret); + } + return ret; +} + +static int hdd_set_smart_chainmask_enabled(struct hdd_context *hdd_ctx) +{ + int vdev_id = 0; + QDF_STATUS status; + bool smart_chainmask_enabled; + int param_id = WMI_PDEV_PARAM_SMART_CHAINMASK_SCHEME; + int vpdev = PDEV_CMD; + int ret; + + status = ucfg_get_smart_chainmask_enabled(hdd_ctx->psoc, + &smart_chainmask_enabled); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + ret = sme_cli_set_command(vdev_id, param_id, + (int)smart_chainmask_enabled, vpdev); + if (ret) + hdd_err("WMI_PDEV_PARAM_SMART_CHAINMASK_SCHEME failed %d", ret); + + return ret; +} + +static int hdd_set_alternative_chainmask_enabled(struct hdd_context *hdd_ctx) +{ + int vdev_id = 0; + QDF_STATUS status; + int param_id = WMI_PDEV_PARAM_ALTERNATIVE_CHAINMASK_SCHEME; + bool alternative_chainmask_enabled; + int vpdev = PDEV_CMD; + int ret; + + status = ucfg_get_alternative_chainmask_enabled( + hdd_ctx->psoc, + &alternative_chainmask_enabled); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + ret = sme_cli_set_command(vdev_id, param_id, + (int)alternative_chainmask_enabled, vpdev); + if (ret) + hdd_err("WMI_PDEV_PARAM_ALTERNATIVE_CHAINMASK_SCHEME failed %d", + ret); + + return ret; +} + +static int hdd_set_ani_enabled(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + int vdev_id = 0; + int param_id = WMI_PDEV_PARAM_ANI_ENABLE; + bool value; + int vpdev = PDEV_CMD; + int ret; + + status = ucfg_fwol_get_ani_enabled(hdd_ctx->psoc, &value); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + ret = sme_cli_set_command(vdev_id, param_id, (int)value, vpdev); + if (ret) + hdd_err("WMI_PDEV_PARAM_ANI_ENABLE failed %d", ret); + + return ret; +} + +/** + * hdd_pre_enable_configure() - Configurations prior to cds_enable + * @hdd_ctx: HDD context + * + * Pre configurations to be done at lower layer before calling cds enable. + * + * Return: 0 on success and errno on failure. + */ +static int hdd_pre_enable_configure(struct hdd_context *hdd_ctx) +{ + int ret; + uint8_t val = 0; + uint8_t max_retry = 0; + QDF_STATUS status; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + cdp_register_pause_cb(soc, wlan_hdd_txrx_pause_cb); + /* Register HL netdev flow control callback */ + cdp_hl_fc_register(soc, OL_TXRX_PDEV_ID, wlan_hdd_txrx_pause_cb); + /* Register rx mic error indication handler */ + cdp_register_rx_mic_error_ind_handler(soc, hdd_rx_mic_error_ind); + + /* + * Note that the cds_pre_enable() sequence triggers the cfg download. + * The cfg download must occur before we update the SME config + * since the SME config operation must access the cfg database + */ + status = hdd_set_sme_config(hdd_ctx); + + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed hdd_set_sme_config: %d", status); + ret = qdf_status_to_os_return(status); + goto out; + } + + status = hdd_set_policy_mgr_user_cfg(hdd_ctx); + if (QDF_STATUS_SUCCESS != status) { + hdd_alert("Failed hdd_set_policy_mgr_user_cfg: %d", status); + ret = qdf_status_to_os_return(status); + goto out; + } + + status = ucfg_mlme_get_tx_chainmask_1ss(hdd_ctx->psoc, &val); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Get tx_chainmask_1ss from mlme failed"); + ret = qdf_status_to_os_return(status); + goto out; + } + ret = sme_cli_set_command(0, WMI_PDEV_PARAM_TX_CHAIN_MASK_1SS, val, + PDEV_CMD); + if (0 != ret) { + hdd_err("WMI_PDEV_PARAM_TX_CHAIN_MASK_1SS failed %d", ret); + goto out; + } + + wlan_mlme_get_mgmt_max_retry(hdd_ctx->psoc, &max_retry); + ret = sme_cli_set_command(0, WMI_PDEV_PARAM_MGMT_RETRY_LIMIT, max_retry, + PDEV_CMD); + if (0 != ret) { + hdd_err("WMI_PDEV_PARAM_MGMT_RETRY_LIMIT failed %d", ret); + goto out; + } + + ret = hdd_set_smart_chainmask_enabled(hdd_ctx); + if (ret) + goto out; + + ret = hdd_set_alternative_chainmask_enabled(hdd_ctx); + if (ret) + goto out; + + ret = hdd_set_ani_enabled(hdd_ctx); + if (ret) + goto out; + + /* Configure global firmware params */ + ret = ucfg_fwol_configure_global_params(hdd_ctx->psoc, hdd_ctx->pdev); + if (ret) + goto out; + + status = hdd_set_sme_chan_list(hdd_ctx); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to init channel list: %d", status); + ret = qdf_status_to_os_return(status); + goto out; + } + + if (!hdd_update_config_cfg(hdd_ctx)) { + hdd_err("config update failed"); + ret = -EINVAL; + goto out; + } + hdd_init_channel_avoidance(hdd_ctx); + +out: + return ret; +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * wlan_hdd_p2p_lo_event_callback - P2P listen offload stop event handler + * @context: context registered with sme_register_p2p_lo_event(). HDD + * always registers a hdd context pointer + * @evt:event structure pointer + * + * This is the p2p listen offload stop event handler, it sends vendor + * event back to supplicant to notify the stop reason. + * + * Return: None + */ +static void wlan_hdd_p2p_lo_event_callback(void *context, + struct sir_p2p_lo_event *evt) +{ + struct hdd_context *hdd_ctx = context; + struct sk_buff *vendor_event; + struct hdd_adapter *adapter; + + hdd_enter(); + + if (!hdd_ctx) { + hdd_err("Invalid HDD context pointer"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, evt->vdev_id); + if (!adapter) { + hdd_err("Cannot find adapter by vdev_id = %d", + evt->vdev_id); + return; + } + + vendor_event = + cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + &(adapter->wdev), sizeof(uint32_t) + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON, + evt->reason_code)) { + hdd_err("nla put failed"); + kfree_skb(vendor_event); + return; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + hdd_debug("Sent P2P_LISTEN_OFFLOAD_STOP event for vdev_id = %d", + evt->vdev_id); +} +#else +static void wlan_hdd_p2p_lo_event_callback(void *context, + struct sir_p2p_lo_event *evt) +{ +} +#endif + +#ifdef FEATURE_WLAN_DYNAMIC_CVM +static inline int hdd_set_vc_mode_config(struct hdd_context *hdd_ctx) +{ + return sme_set_vc_mode_config(hdd_ctx->config->vc_mode_cfg_bitmap); +} +#else +static inline int hdd_set_vc_mode_config(struct hdd_context *hdd_ctx) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * hdd_adaptive_dwelltime_init() - initialization for adaptive dwell time config + * @hdd_ctx: HDD context + * + * This function sends the adaptive dwell time config configuration to the + * firmware via WMA + * + * Return: 0 - success, < 0 - failure + */ +static int hdd_adaptive_dwelltime_init(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + struct adaptive_dwelltime_params dwelltime_params; + + status = ucfg_fwol_get_all_adaptive_dwelltime_params(hdd_ctx->psoc, + &dwelltime_params); + status = ucfg_fwol_set_adaptive_dwelltime_config(&dwelltime_params); + + hdd_debug("Sending Adaptive Dwelltime Configuration to fw"); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to send Adaptive Dwelltime configuration!"); + return -EAGAIN; + } + return 0; +} + +int hdd_dbs_scan_selection_init(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + struct wmi_dbs_scan_sel_params dbs_scan_params; + uint32_t i = 0; + uint8_t count = 0, numentries = 0; + uint8_t dual_mac_feature; + uint8_t dbs_scan_config[CDS_DBS_SCAN_PARAM_PER_CLIENT + * CDS_DBS_SCAN_CLIENTS_MAX]; + + status = ucfg_policy_mgr_get_dual_mac_feature(hdd_ctx->psoc, + &dual_mac_feature); + + if (status != QDF_STATUS_SUCCESS) { + hdd_err("can't get dual mac feature flag"); + return -EINVAL; + } + /* check if DBS is enabled or supported */ + if ((dual_mac_feature == DISABLE_DBS_CXN_AND_SCAN) || + (dual_mac_feature == ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN)) + return -EINVAL; + + hdd_string_to_u8_array(hdd_ctx->config->dbs_scan_selection, + dbs_scan_config, &numentries, + (CDS_DBS_SCAN_PARAM_PER_CLIENT + * CDS_DBS_SCAN_CLIENTS_MAX)); + + if (!numentries) { + hdd_debug("Do not send scan_selection_config"); + return 0; + } + + /* hdd_set_fw_log_params */ + dbs_scan_params.num_clients = 0; + while (count < (numentries - 2)) { + dbs_scan_params.module_id[i] = dbs_scan_config[count]; + dbs_scan_params.num_dbs_scans[i] = dbs_scan_config[count + 1]; + dbs_scan_params.num_non_dbs_scans[i] = + dbs_scan_config[count + 2]; + dbs_scan_params.num_clients++; + hdd_debug("module:%d NDS:%d NNDS:%d", + dbs_scan_params.module_id[i], + dbs_scan_params.num_dbs_scans[i], + dbs_scan_params.num_non_dbs_scans[i]); + count += CDS_DBS_SCAN_PARAM_PER_CLIENT; + i++; + } + + dbs_scan_params.pdev_id = 0; + + hdd_debug("clients:%d pdev:%d", + dbs_scan_params.num_clients, dbs_scan_params.pdev_id); + + status = sme_set_dbs_scan_selection_config(hdd_ctx->mac_handle, + &dbs_scan_params); + hdd_debug("Sending DBS Scan Selection Configuration to fw"); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to send DBS Scan selection configuration!"); + return -EAGAIN; + } + return 0; +} + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +/** + * hdd_set_auto_shutdown_cb() - Set auto shutdown callback + * @hdd_ctx: HDD context + * + * Set auto shutdown callback to get indications from firmware to indicate + * userspace to shutdown WLAN after a configured amount of inactivity. + * + * Return: 0 on success and errno on failure. + */ +static int hdd_set_auto_shutdown_cb(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + if (!hdd_ctx->config->wlan_auto_shutdown) + return 0; + + status = sme_set_auto_shutdown_cb(hdd_ctx->mac_handle, + wlan_hdd_auto_shutdown_cb); + if (status != QDF_STATUS_SUCCESS) + hdd_err("Auto shutdown feature could not be enabled: %d", + status); + + return qdf_status_to_os_return(status); +} +#else +static int hdd_set_auto_shutdown_cb(struct hdd_context *hdd_ctx) +{ + return 0; +} +#endif + +#ifdef MWS_COEX +/** + * hdd_set_mws_coex() - Set MWS coex configurations + * @hdd_ctx: HDD context + * + * This function sends MWS-COEX 4G quick FTDM and + * MWS-COEX 5G-NR power limit to FW + * + * Return: 0 on success and errno on failure. + */ +static int hdd_init_mws_coex(struct hdd_context *hdd_ctx) +{ + int ret = 0; + uint32_t mws_coex_4g_quick_tdm = 0, mws_coex_5g_nr_pwr_limit = 0; + uint32_t mws_coex_pcc_channel_avoid_delay = 0; + uint32_t mws_coex_scc_channel_avoid_delay = 0; + + ucfg_mlme_get_mws_coex_4g_quick_tdm(hdd_ctx->psoc, + &mws_coex_4g_quick_tdm); + + ret = sme_cli_set_command(0, WMI_PDEV_PARAM_MWSCOEX_4G_ALLOW_QUICK_FTDM, + mws_coex_4g_quick_tdm, + PDEV_CMD); + if (ret) { + hdd_warn("Unable to send MWS-COEX 4G quick FTDM policy"); + return ret; + } + + ucfg_mlme_get_mws_coex_5g_nr_pwr_limit(hdd_ctx->psoc, + &mws_coex_5g_nr_pwr_limit); + + ret = sme_cli_set_command(0, WMI_PDEV_PARAM_MWSCOEX_SET_5GNR_PWR_LIMIT, + mws_coex_5g_nr_pwr_limit, + PDEV_CMD); + if (ret) { + hdd_warn("Unable to send MWS-COEX 4G quick FTDM policy"); + return ret; + } + + ucfg_mlme_get_mws_coex_pcc_channel_avoid_delay( + hdd_ctx->psoc, + &mws_coex_pcc_channel_avoid_delay); + + ret = sme_cli_set_command(0, WMI_PDEV_PARAM_MWSCOEX_PCC_CHAVD_DELAY, + mws_coex_pcc_channel_avoid_delay, + PDEV_CMD); + if (ret) + return ret; + + ucfg_mlme_get_mws_coex_scc_channel_avoid_delay( + hdd_ctx->psoc, + &mws_coex_scc_channel_avoid_delay); + + ret = sme_cli_set_command(0, WMI_PDEV_PARAM_MWSCOEX_SCC_CHAVD_DELAY, + mws_coex_scc_channel_avoid_delay, + PDEV_CMD); + return ret; +} +#else +static int hdd_init_mws_coex(struct hdd_context *hdd_ctx) +{ + return 0; +} +#endif + +/** + * hdd_features_init() - Init features + * @hdd_ctx: HDD context + * + * Initialize features and their feature context after WLAN firmware is up. + * + * Return: 0 on success and errno on failure. + */ +static int hdd_features_init(struct hdd_context *hdd_ctx) +{ + struct tx_power_limit hddtxlimit; + QDF_STATUS status; + int ret; + mac_handle_t mac_handle; + struct hdd_config *cfg; + bool b_cts2self, is_imps_enabled; + + hdd_enter(); + + ret = hdd_update_country_code(hdd_ctx); + if (ret) { + hdd_err("Failed to update country code; errno:%d", ret); + return -EINVAL; + } + + ret = hdd_init_mws_coex(hdd_ctx); + if (ret) + hdd_warn("Error initializing mws-coex"); + + cfg = hdd_ctx->config; + /* FW capabilities received, Set the Dot11 mode */ + mac_handle = hdd_ctx->mac_handle; + sme_setdef_dot11mode(mac_handle); + + ucfg_mlme_is_imps_enabled(hdd_ctx->psoc, &is_imps_enabled); + hdd_set_idle_ps_config(hdd_ctx, is_imps_enabled); + + /* Send Enable/Disable data stall detection cmd to FW */ + sme_cli_set_command(0, WMI_PDEV_PARAM_DATA_STALL_DETECT_ENABLE, + cdp_cfg_get(cds_get_context(QDF_MODULE_ID_SOC), + cfg_dp_enable_data_stall), PDEV_CMD); + + ucfg_mlme_get_go_cts2self_for_sta(hdd_ctx->psoc, &b_cts2self); + if (b_cts2self) + sme_set_cts2self_for_p2p_go(mac_handle); + + if (hdd_set_vc_mode_config(hdd_ctx)) + hdd_warn("Error in setting Voltage Corner mode config to FW"); + + if (hdd_rx_ol_init(hdd_ctx)) + hdd_err("Unable to initialize Rx LRO/GRO in fw"); + + if (hdd_adaptive_dwelltime_init(hdd_ctx)) + hdd_err("Unable to send adaptive dwelltime setting to FW"); + + if (hdd_dbs_scan_selection_init(hdd_ctx)) + hdd_err("Unable to send DBS scan selection setting to FW"); + + ret = hdd_init_thermal_info(hdd_ctx); + if (ret) { + hdd_err("Error while initializing thermal information"); + return ret; + } + + /** + * In case of SSR/PDR, if pktlog was enabled manually before + * SSR/PDR, then enable it again automatically after Wlan + * device up. + * During SSR/PDR, pktlog will be disabled as part of + * hdd_features_deinit if pktlog is enabled in ini. + * Re-enable pktlog in SSR case, if pktlog is enabled in ini. + */ + if (cds_is_packet_log_enabled() || + (cds_is_driver_recovering() && hdd_ctx->is_pktlog_enabled)) + hdd_pktlog_enable_disable(hdd_ctx, true, 0, 0); + + hddtxlimit.txPower2g = ucfg_get_tx_power(hdd_ctx->psoc, BAND_2G); + hddtxlimit.txPower5g = ucfg_get_tx_power(hdd_ctx->psoc, BAND_5G); + status = sme_txpower_limit(mac_handle, &hddtxlimit); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Error setting txlimit in sme: %d", status); + + wlan_hdd_tsf_init(hdd_ctx); + + status = sme_enable_disable_chanavoidind_event(mac_handle, 0); + if (QDF_IS_STATUS_ERROR(status) && (status != QDF_STATUS_E_NOSUPPORT)) { + hdd_err("Failed to disable Chan Avoidance Indication"); + return -EINVAL; + } + + /* register P2P Listen Offload event callback */ + if (wma_is_p2p_lo_capable()) + sme_register_p2p_lo_event(mac_handle, hdd_ctx, + wlan_hdd_p2p_lo_event_callback); + + ret = hdd_set_auto_shutdown_cb(hdd_ctx); + + if (ret) + return -EINVAL; + + wlan_hdd_init_chan_info(hdd_ctx); + wlan_hdd_twt_init(hdd_ctx); + + hdd_exit(); + return 0; +} + +/** + * hdd_features_deinit() - Deinit features + * @hdd_ctx: HDD context + * + * De-Initialize features and their feature context. + * + * Return: none. + */ +static void hdd_features_deinit(struct hdd_context *hdd_ctx) +{ + wlan_hdd_twt_deinit(hdd_ctx); + wlan_hdd_deinit_chan_info(hdd_ctx); + wlan_hdd_tsf_deinit(hdd_ctx); + if (cds_is_packet_log_enabled()) + hdd_pktlog_enable_disable(hdd_ctx, false, 0, 0); +} + +/** + * hdd_register_bcn_cb() - register scan beacon callback + * @hdd_ctx - Pointer to the HDD context + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS hdd_register_bcn_cb(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + status = ucfg_scan_register_bcn_cb(hdd_ctx->psoc, + wlan_cfg80211_inform_bss_frame, + SCAN_CB_TYPE_INFORM_BCN); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("failed to register SCAN_CB_TYPE_INFORM_BCN with status code %08d [x%08x]", + status, status); + return status; + } + + status = ucfg_scan_register_bcn_cb(hdd_ctx->psoc, + wlan_cfg80211_unlink_bss_list, + SCAN_CB_TYPE_UNLINK_BSS); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("failed to refister SCAN_CB_TYPE_FLUSH_BSS with status code %08d [x%08x]", + status, status); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_v2_flow_pool_map() - Flow pool create callback when vdev is active + * @vdev_id: vdev_id, corresponds to flow_pool + * + * Return: none. + */ +static void hdd_v2_flow_pool_map(int vdev_id) +{ + QDF_STATUS status; + + status = cdp_flow_pool_map(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID, + vdev_id); + /* + * For Adrastea flow control v2 is based on FW MAP events, + * so this above callback is not implemented. + * Hence this is not actual failure. Dont return failure + */ + if ((status != QDF_STATUS_SUCCESS) && + (status != QDF_STATUS_E_INVAL)) { + hdd_err("vdev_id: %d, failed to create flow pool status %d", + vdev_id, status); + } +} + +/** + * hdd_v2_flow_pool_unmap() - Flow pool create callback when vdev is not active + * @vdev_id: vdev_id, corresponds to flow_pool + * + * Return: none. + */ +static void hdd_v2_flow_pool_unmap(int vdev_id) +{ + cdp_flow_pool_unmap(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID, vdev_id); +} + +/** + * hdd_action_oui_config() - Configure action_oui strings + * @hdd_ctx: pointer to hdd context + * + * This is a HDD wrapper function which invokes ucfg api + * of action_oui component to parse action oui strings. + * + * Return: None + */ +static void hdd_action_oui_config(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + uint32_t id; + uint8_t *str; + + if (!hdd_ctx->config->action_oui_enable) + return; + + for (id = 0; id < ACTION_OUI_MAXIMUM_ID; id++) { + str = hdd_ctx->config->action_oui_str[id]; + if (!qdf_str_len(str)) + continue; + + status = ucfg_action_oui_parse(hdd_ctx->psoc, str, id); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to parse action_oui str: %u", id); + } +} + +/** + * hdd_action_oui_send() - Send action_oui extensions to firmware + * @hdd_ctx: pointer to hdd context + * + * This is a HDD wrapper function which invokes ucfg api + * of action_oui component to send action oui extensions to firmware. + * + * Return: None + */ +static void hdd_action_oui_send(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + if (!hdd_ctx->config->action_oui_enable) + return; + + status = ucfg_action_oui_send(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to send one or all action_ouis"); +} + +static void hdd_hastings_bt_war_initialize(struct hdd_context *hdd_ctx) +{ + if (hdd_ctx->config->iface_change_wait_time) + hdd_hastings_bt_war_disable_fw(hdd_ctx); + else + hdd_hastings_bt_war_enable_fw(hdd_ctx); +} + +/** + * hdd_configure_cds() - Configure cds modules + * @hdd_ctx: HDD context + * @adapter: Primary adapter context + * + * Enable Cds modules after WLAN firmware is up. + * + * Return: 0 on success and errno on failure. + */ +int hdd_configure_cds(struct hdd_context *hdd_ctx) +{ + int ret; + QDF_STATUS status; + int set_value; + mac_handle_t mac_handle; + bool enable_rts_sifsbursting; + uint8_t enable_phy_reg_retention; + uint8_t max_mpdus_inampdu, is_force_1x1 = 0; + uint32_t num_abg_tx_chains = 0; + uint16_t num_11b_tx_chains = 0; + uint16_t num_11ag_tx_chains = 0; + struct policy_mgr_dp_cbacks dp_cbs = {0}; + bool value; + enum pmo_auto_pwr_detect_failure_mode auto_power_fail_mode; + bool bval = false; + qdf_device_t qdf_ctx; + + mac_handle = hdd_ctx->mac_handle; + + hdd_action_oui_send(hdd_ctx); + status = ucfg_policy_mgr_get_force_1x1(hdd_ctx->psoc, &is_force_1x1); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to get force 1x1 value"); + goto out; + } + if (is_force_1x1) + sme_cli_set_command(0, (int)WMI_PDEV_PARAM_SET_IOT_PATTERN, + 1, PDEV_CMD); + /* set chip power save failure detected callback */ + sme_set_chip_pwr_save_fail_cb(mac_handle, + hdd_chip_pwr_save_fail_detected_cb); + + status = ucfg_get_max_mpdus_inampdu(hdd_ctx->psoc, + &max_mpdus_inampdu); + if (status) { + hdd_err("Failed to get max mpdus in ampdu value"); + goto out; + } + + if (max_mpdus_inampdu) { + set_value = max_mpdus_inampdu; + sme_cli_set_command(0, (int)WMI_PDEV_PARAM_MAX_MPDUS_IN_AMPDU, + set_value, PDEV_CMD); + } + + status = ucfg_get_enable_rts_sifsbursting(hdd_ctx->psoc, + &enable_rts_sifsbursting); + if (status) { + hdd_err("Failed to get rts sifs bursting value"); + goto out; + } + + if (enable_rts_sifsbursting) { + set_value = enable_rts_sifsbursting; + sme_cli_set_command(0, + (int)WMI_PDEV_PARAM_ENABLE_RTS_SIFS_BURSTING, + set_value, PDEV_CMD); + } + + ucfg_mlme_get_sap_get_peer_info(hdd_ctx->psoc, &value); + if (value) { + set_value = value; + sme_cli_set_command(0, + (int)WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE, + set_value, PDEV_CMD); + } + + status = ucfg_mlme_get_num_11b_tx_chains(hdd_ctx->psoc, + &num_11b_tx_chains); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to get num_11b_tx_chains"); + goto out; + } + + status = ucfg_mlme_get_num_11ag_tx_chains(hdd_ctx->psoc, + &num_11ag_tx_chains); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to get num_11ag_tx_chains"); + goto out; + } + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + if (!bval) { + if (num_11b_tx_chains > 1) + num_11b_tx_chains = 1; + if (num_11ag_tx_chains > 1) + num_11ag_tx_chains = 1; + } + WMI_PDEV_PARAM_SET_11B_TX_CHAIN_NUM(num_abg_tx_chains, + num_11b_tx_chains); + WMI_PDEV_PARAM_SET_11AG_TX_CHAIN_NUM(num_abg_tx_chains, + num_11ag_tx_chains); + sme_cli_set_command(0, (int)WMI_PDEV_PARAM_ABG_MODE_TX_CHAIN_NUM, + num_abg_tx_chains, PDEV_CMD); + + if (!ucfg_reg_is_regdb_offloaded(hdd_ctx->psoc)) + ucfg_reg_program_default_cc(hdd_ctx->pdev, + hdd_ctx->reg.reg_domain); + + ret = hdd_pre_enable_configure(hdd_ctx); + if (ret) { + hdd_err("Failed to pre-configure cds"); + goto out; + } + + /* Always get latest IPA resources allocated from cds_open and configure + * IPA module before configuring them to FW. Sequence required as crash + * observed otherwise. + */ + + qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_ctx) { + hdd_err("QDF device context is NULL"); + goto out; + } + + if (ucfg_ipa_uc_ol_init(hdd_ctx->pdev, qdf_ctx)) { + hdd_err("Failed to setup pipes"); + goto out; + } + + /* + * Start CDS which starts up the SME/MAC/HAL modules and everything + * else + */ + status = cds_enable(hdd_ctx->psoc); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("cds_enable failed"); + goto out; + } + + status = hdd_post_cds_enable_config(hdd_ctx); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("hdd_post_cds_enable_config failed"); + goto cds_disable; + } + status = hdd_register_bcn_cb(hdd_ctx); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("hdd_register_bcn_cb failed"); + goto cds_disable; + } + + ret = hdd_features_init(hdd_ctx); + if (ret) + goto cds_disable; + + /* Donot disable rx offload on concurrency for lithium based targets */ + if (!(hdd_ctx->target_type == TARGET_TYPE_QCA6290 || + hdd_ctx->target_type == TARGET_TYPE_QCA6390 || + hdd_ctx->target_type == TARGET_TYPE_QCA6490)) + if (hdd_ctx->ol_enable) + dp_cbs.hdd_disable_rx_ol_in_concurrency = + hdd_disable_rx_ol_in_concurrency; + dp_cbs.hdd_set_rx_mode_rps_cb = hdd_set_rx_mode_rps; + dp_cbs.hdd_ipa_set_mcc_mode_cb = hdd_ipa_set_mcc_mode; + dp_cbs.hdd_v2_flow_pool_map = hdd_v2_flow_pool_map; + dp_cbs.hdd_v2_flow_pool_unmap = hdd_v2_flow_pool_unmap; + status = policy_mgr_register_dp_cb(hdd_ctx->psoc, &dp_cbs); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_debug("Failed to register DP cb with Policy Manager"); + goto cds_disable; + } + status = policy_mgr_register_mode_change_cb(hdd_ctx->psoc, + wlan_hdd_send_mode_change_event); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_debug("Failed to register mode change cb with Policy Manager"); + goto cds_disable; + } + + if (hdd_green_ap_enable_egap(hdd_ctx)) + hdd_debug("enhance green ap is not enabled"); + + if (0 != wlan_hdd_set_wow_pulse(hdd_ctx, true)) + hdd_debug("Failed to set wow pulse"); + + sme_cli_set_command(0, WMI_PDEV_PARAM_GCMP_SUPPORT_ENABLE, + ucfg_fwol_get_gcmp_enable(hdd_ctx->psoc), PDEV_CMD); + + auto_power_fail_mode = + ucfg_pmo_get_auto_power_fail_mode(hdd_ctx->psoc); + sme_cli_set_command(0, WMI_PDEV_AUTO_DETECT_POWER_FAILURE, + auto_power_fail_mode, PDEV_CMD); + + status = ucfg_get_enable_phy_reg_retention(hdd_ctx->psoc, + &enable_phy_reg_retention); + + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + if (enable_phy_reg_retention) + wma_cli_set_command(0, WMI_PDEV_PARAM_FAST_PWR_TRANSITION, + enable_phy_reg_retention, PDEV_CMD); + + hdd_hastings_bt_war_initialize(hdd_ctx); + + wlan_hdd_hang_event_notifier_register(hdd_ctx); + return 0; + +cds_disable: + cds_disable(hdd_ctx->psoc); + +out: + return -EINVAL; +} + +/** + * hdd_deconfigure_cds() -De-Configure cds + * @hdd_ctx: HDD context + * + * Deconfigure Cds modules before WLAN firmware is down. + * + * Return: 0 on success and errno on failure. + */ +static int hdd_deconfigure_cds(struct hdd_context *hdd_ctx) +{ + QDF_STATUS qdf_status; + int ret = 0; + + hdd_enter(); + + wlan_hdd_hang_event_notifier_unregister(); + /* De-init features */ + hdd_features_deinit(hdd_ctx); + + qdf_status = policy_mgr_deregister_mode_change_cb(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_debug("Failed to deregister mode change cb with Policy Manager"); + + qdf_status = cds_disable(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to Disable the CDS Modules! :%d", + qdf_status); + ret = -EINVAL; + } + + if (ucfg_ipa_uc_ol_deinit(hdd_ctx->pdev) != QDF_STATUS_SUCCESS) { + hdd_err("Failed to disconnect pipes"); + ret = -EINVAL; + } + + hdd_exit(); + return ret; +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +static void hdd_deregister_policy_manager_callback( + struct wlan_objmgr_psoc *psoc) +{ + if (QDF_STATUS_SUCCESS != + policy_mgr_deregister_hdd_cb(psoc)) { + hdd_err("HDD callback deregister with policy manager failed"); + } +} +#else +static void hdd_deregister_policy_manager_callback( + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +int hdd_wlan_stop_modules(struct hdd_context *hdd_ctx, bool ftm_mode) +{ + void *hif_ctx; + qdf_device_t qdf_ctx; + QDF_STATUS qdf_status; + bool is_recovery_stop = cds_is_driver_recovering(); + int ret = 0; + int debugfs_threads; + struct target_psoc_info *tgt_hdl; + + hdd_enter(); + qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_ctx) { + hdd_err("QDF device context NULL"); + return -EINVAL; + } + + cds_set_module_stop_in_progress(true); + + debugfs_threads = hdd_return_debugfs_threads_count(); + + if (debugfs_threads > 0 || hdd_ctx->is_wiphy_suspended) { + hdd_warn("Debugfs threads %d, wiphy suspend %d", + debugfs_threads, hdd_ctx->is_wiphy_suspended); + + if (IS_IDLE_STOP && !ftm_mode) { + hdd_psoc_idle_timer_start(hdd_ctx); + cds_set_module_stop_in_progress(false); + + hdd_bus_bw_compute_timer_stop(hdd_ctx); + return -EAGAIN; + } + } + + hdd_bus_bw_compute_timer_stop(hdd_ctx); + hdd_deregister_policy_manager_callback(hdd_ctx->psoc); + + /* free user wowl patterns */ + hdd_free_user_wowl_ptrns(); + + switch (hdd_ctx->driver_status) { + case DRIVER_MODULES_UNINITIALIZED: + hdd_debug("Modules not initialized just return"); + goto done; + case DRIVER_MODULES_CLOSED: + hdd_debug("Modules already closed"); + goto done; + case DRIVER_MODULES_ENABLED: + hdd_debug("Wlan transitioning (CLOSED <- ENABLED)"); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE || + hdd_get_conparam() == QDF_GLOBAL_EPPING_MODE) + break; + + wlan_hdd_deinit_tx_rx_histogram(hdd_ctx); + + hdd_skip_acs_scan_timer_deinit(hdd_ctx); + + hdd_disable_power_management(); + if (hdd_deconfigure_cds(hdd_ctx)) { + hdd_err("Failed to de-configure CDS"); + QDF_ASSERT(0); + ret = -EINVAL; + } + hdd_debug("successfully Disabled the CDS modules!"); + + break; + default: + QDF_DEBUG_PANIC("Unknown driver state:%d", + hdd_ctx->driver_status); + ret = -EINVAL; + goto done; + } + + hdd_sysfs_dp_aggregation_destroy(); + hdd_sysfs_destroy_powerstats_interface(); + hdd_sysfs_destroy_version_interface(); + hdd_sysfs_destroy_driver_root_obj(); + hdd_debug("Closing CDS modules!"); + + if (hdd_get_conparam() != QDF_GLOBAL_EPPING_MODE) { + qdf_status = cds_post_disable(); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to process post CDS disable! :%d", + qdf_status); + ret = -EINVAL; + QDF_ASSERT(0); + } + + hdd_unregister_notifiers(hdd_ctx); + /* De-register the SME callbacks */ + hdd_deregister_cb(hdd_ctx); + + hdd_runtime_suspend_context_deinit(hdd_ctx); + + qdf_status = cds_dp_close(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_warn("Failed to stop CDS DP: %d", qdf_status); + ret = -EINVAL; + QDF_ASSERT(0); + } + + qdf_status = cds_close(hdd_ctx->psoc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_warn("Failed to stop CDS: %d", qdf_status); + ret = -EINVAL; + QDF_ASSERT(0); + } + + qdf_status = wbuff_module_deinit(); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("WBUFF de-init unsuccessful; status: %d", + qdf_status); + + hdd_component_pdev_close(hdd_ctx->pdev); + + hdd_component_psoc_close(hdd_ctx->psoc); + dispatcher_pdev_close(hdd_ctx->pdev); + ret = hdd_objmgr_release_and_destroy_pdev(hdd_ctx); + if (ret) { + hdd_err("Failed to destroy pdev; errno:%d", ret); + QDF_ASSERT(0); + } + wlan_global_lmac_if_close(hdd_ctx->psoc); + } + + /* + * Reset total mac phy during module stop such that during + * next module start same psoc is used to populate new service + * ready data + */ + tgt_hdl = wlan_psoc_get_tgt_if_handle(hdd_ctx->psoc); + if (tgt_hdl) + target_psoc_set_total_mac_phy_cnt(tgt_hdl, 0); + + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("Hif context is Null"); + ret = -EINVAL; + } + + if (hdd_ctx->target_hw_name) { + qdf_mem_free(hdd_ctx->target_hw_name); + hdd_ctx->target_hw_name = NULL; + } + + if (hdd_get_conparam() == QDF_GLOBAL_EPPING_MODE) { + epping_disable(); + epping_close(); + } + + hdd_hif_close(hdd_ctx, hif_ctx); + + ol_cds_free(); + + if (IS_IDLE_STOP) { + ret = pld_power_off(qdf_ctx->dev); + if (ret) + hdd_err("Failed to power down device; errno:%d", ret); + } + + /* Free the cache channels of the command SET_DISABLE_CHANNEL_LIST */ + wlan_hdd_free_cache_channels(hdd_ctx); + hdd_driver_mem_cleanup(); + + /* Free the resources allocated while storing SAR config. These needs + * to be freed only in the case when it is not SSR. As in the case of + * SSR, the values needs to be intact so that it can be restored during + * reinit path. + */ + if (!is_recovery_stop) + wlan_hdd_free_sar_config(hdd_ctx); + + hdd_sap_destroy_ctx_all(hdd_ctx, is_recovery_stop); + hdd_sta_destroy_ctx_all(hdd_ctx); + pld_request_bus_bandwidth(hdd_ctx->parent_dev, PLD_BUS_WIDTH_NONE); + hdd_deinit_adapter_ops_wq(hdd_ctx); + hdd_bus_bandwidth_deinit(hdd_ctx); + hdd_check_for_leaks(hdd_ctx, is_recovery_stop); + hdd_debug_domain_set(QDF_DEBUG_DOMAIN_INIT); + + /* Once the firmware sequence is completed reset this flag */ + hdd_ctx->imps_enabled = false; + hdd_ctx->is_dual_mac_cfg_updated = false; + hdd_ctx->driver_status = DRIVER_MODULES_CLOSED; + hdd_debug("Wlan transitioned (now CLOSED)"); + +done: + cds_set_module_stop_in_progress(false); + + hdd_exit(); + + return ret; +} + +#ifdef WLAN_FEATURE_MEMDUMP_ENABLE +/** + * hdd_state_info_dump() - prints state information of hdd layer + * @buf: buffer pointer + * @size: size of buffer to be filled + * + * This function is used to dump state information of hdd layer + * + * Return: None + */ +static void hdd_state_info_dump(char **buf_ptr, uint16_t *size) +{ + struct hdd_context *hdd_ctx; + struct hdd_station_ctx *hdd_sta_ctx; + struct hdd_adapter *adapter, *next_adapter = NULL; + uint16_t len = 0; + char *buf = *buf_ptr; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_STATE_INFO_DUMP; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("Failed to get hdd context "); + return; + } + + hdd_debug("size of buffer: %d", *size); + + len += scnprintf(buf + len, *size - len, + "\n is_wiphy_suspended %d", hdd_ctx->is_wiphy_suspended); + len += scnprintf(buf + len, *size - len, + "\n is_scheduler_suspended %d", + hdd_ctx->is_scheduler_suspended); + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter->dev) + len += scnprintf(buf + len, *size - len, + "\n device name: %s", + adapter->dev->name); + len += scnprintf(buf + len, *size - len, + "\n device_mode: %d", adapter->device_mode); + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + len += scnprintf(buf + len, *size - len, + "\n conn_state: %d", + hdd_sta_ctx->conn_info.conn_state); + break; + + default: + break; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + *size -= len; + *buf_ptr += len; +} + +/** + * hdd_register_debug_callback() - registration function for hdd layer + * to print hdd state information + * + * Return: None + */ +static void hdd_register_debug_callback(void) +{ + qdf_register_debug_callback(QDF_MODULE_ID_HDD, &hdd_state_info_dump); +} +#else /* WLAN_FEATURE_MEMDUMP_ENABLE */ +static void hdd_register_debug_callback(void) +{ +} +#endif /* WLAN_FEATURE_MEMDUMP_ENABLE */ + +/* + * wlan_init_bug_report_lock() - Initialize bug report lock + * + * This function is used to create bug report lock + * + * Return: None + */ +static void wlan_init_bug_report_lock(void) +{ + struct cds_context *p_cds_context; + + p_cds_context = cds_get_global_context(); + if (!p_cds_context) { + hdd_err("cds context is NULL"); + return; + } + + qdf_spinlock_create(&p_cds_context->bug_report_lock); +} + +#ifdef CONFIG_DP_TRACE +void hdd_dp_trace_init(struct hdd_config *config) +{ + bool live_mode = DP_TRACE_CONFIG_DEFAULT_LIVE_MODE; + uint8_t thresh = DP_TRACE_CONFIG_DEFAULT_THRESH; + uint16_t thresh_time_limit = DP_TRACE_CONFIG_DEFAULT_THRESH_TIME_LIMIT; + uint8_t verbosity = DP_TRACE_CONFIG_DEFAULT_VERBOSTY; + uint32_t proto_bitmap = DP_TRACE_CONFIG_DEFAULT_BITMAP; + uint8_t config_params[DP_TRACE_CONFIG_NUM_PARAMS]; + uint8_t num_entries = 0; + uint32_t bw_compute_interval; + + qdf_dp_set_proto_event_bitmap(config->dp_proto_event_bitmap); + + if (!config->enable_dp_trace) { + hdd_err("dp trace is disabled from ini"); + return; + } + + hdd_string_to_u8_array(config->dp_trace_config, config_params, + &num_entries, sizeof(config_params)); + + /* calculating, num bw timer intervals in a second (1000ms) */ + bw_compute_interval = GET_BW_COMPUTE_INTV(config); + if (bw_compute_interval <= 1000 && bw_compute_interval > 0) + thresh_time_limit = 1000 / bw_compute_interval; + else if (bw_compute_interval > 1000) { + hdd_err("busBandwidthComputeInterval > 1000, using 1000"); + thresh_time_limit = 1; + } else + hdd_err("busBandwidthComputeInterval is 0, using defaults"); + + switch (num_entries) { + case 4: + proto_bitmap = config_params[3]; + /* fall through */ + case 3: + verbosity = config_params[2]; + /* fall through */ + case 2: + thresh = config_params[1]; + /* fall through */ + case 1: + live_mode = config_params[0]; + /* fall through */ + default: + hdd_debug("live_mode %u thresh %u time_limit %u verbosity %u bitmap 0x%x", + live_mode, thresh, thresh_time_limit, + verbosity, proto_bitmap); + }; + + qdf_dp_trace_init(live_mode, thresh, thresh_time_limit, + verbosity, proto_bitmap); + +} +#endif + +#ifdef DISABLE_CHANNEL_LIST +static QDF_STATUS wlan_hdd_cache_chann_mutex_create(struct hdd_context *hdd_ctx) +{ + return qdf_mutex_create(&hdd_ctx->cache_channel_lock); +} +#else +static QDF_STATUS wlan_hdd_cache_chann_mutex_create(struct hdd_context *hdd_ctx) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +static QDF_STATUS hdd_open_adapter_no_trans(struct hdd_context *hdd_ctx, + enum QDF_OPMODE op_mode, + const char *iface_name, + uint8_t *mac_addr_bytes) +{ + struct osif_vdev_sync *vdev_sync; + struct hdd_adapter *adapter; + QDF_STATUS status; + int errno; + + QDF_BUG(rtnl_is_locked()); + + errno = osif_vdev_sync_create(hdd_ctx->parent_dev, &vdev_sync); + if (errno) + return qdf_status_from_os_return(errno); + + adapter = hdd_open_adapter(hdd_ctx, op_mode, iface_name, + mac_addr_bytes, NET_NAME_UNKNOWN, true); + if (!adapter) { + status = QDF_STATUS_E_INVAL; + goto destroy_sync; + } + + osif_vdev_sync_register(adapter->dev, vdev_sync); + + return QDF_STATUS_SUCCESS; + +destroy_sync: + osif_vdev_sync_destroy(vdev_sync); + + return status; +} + +#ifdef WLAN_OPEN_P2P_INTERFACE +/** + * hdd_open_p2p_interface - Open P2P interface + * @hdd_ctx: HDD context + * + * Return: QDF_STATUS + */ +static QDF_STATUS hdd_open_p2p_interface(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + bool p2p_dev_addr_admin; + bool is_p2p_locally_administered = false; + + cfg_p2p_get_device_addr_admin(hdd_ctx->psoc, &p2p_dev_addr_admin); + + if (p2p_dev_addr_admin) { + if (hdd_ctx->num_provisioned_addr && + !(hdd_ctx->provisioned_mac_addr[0].bytes[0] & 0x02)) { + hdd_ctx->p2p_device_address = + hdd_ctx->provisioned_mac_addr[0]; + + /* + * Generate the P2P Device Address. This consists of + * the device's primary MAC address with the locally + * administered bit set. + */ + + hdd_ctx->p2p_device_address.bytes[0] |= 0x02; + is_p2p_locally_administered = true; + } else if (!(hdd_ctx->derived_mac_addr[0].bytes[0] & 0x02)) { + hdd_ctx->p2p_device_address = + hdd_ctx->derived_mac_addr[0]; + /* + * Generate the P2P Device Address. This consists of + * the device's primary MAC address with the locally + * administered bit set. + */ + hdd_ctx->p2p_device_address.bytes[0] |= 0x02; + is_p2p_locally_administered = true; + } + } + if (!is_p2p_locally_administered) { + uint8_t *p2p_dev_addr; + + p2p_dev_addr = wlan_hdd_get_intf_addr(hdd_ctx, + QDF_P2P_DEVICE_MODE); + if (!p2p_dev_addr) { + hdd_err("Failed to get MAC address for new p2p device"); + return QDF_STATUS_E_INVAL; + } + + qdf_mem_copy(hdd_ctx->p2p_device_address.bytes, + p2p_dev_addr, QDF_MAC_ADDR_SIZE); + } + + status = hdd_open_adapter_no_trans(hdd_ctx, QDF_P2P_DEVICE_MODE, + "p2p%d", + hdd_ctx->p2p_device_address.bytes); + if (QDF_IS_STATUS_ERROR(status)) { + if (!is_p2p_locally_administered) + wlan_hdd_release_intf_addr(hdd_ctx, + hdd_ctx->p2p_device_address.bytes); + hdd_err("Failed to open p2p interface"); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} +#else +static inline QDF_STATUS hdd_open_p2p_interface(struct hdd_context *hdd_ctx) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +static QDF_STATUS hdd_open_ocb_interface(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + uint8_t *mac_addr; + + mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, QDF_OCB_MODE); + if (!mac_addr) + return QDF_STATUS_E_INVAL; + + status = hdd_open_adapter_no_trans(hdd_ctx, QDF_OCB_MODE, + "wlanocb%d", mac_addr); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_hdd_release_intf_addr(hdd_ctx, mac_addr); + hdd_err("Failed to open 802.11p interface"); + } + + return status; +} + +static QDF_STATUS hdd_open_concurrent_interface(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + const char *iface_name; + uint8_t *mac_addr; + + if (qdf_str_eq(hdd_ctx->config->enable_concurrent_sta, "")) + return QDF_STATUS_SUCCESS; + + iface_name = hdd_ctx->config->enable_concurrent_sta; + mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, QDF_STA_MODE); + if (!mac_addr) + return QDF_STATUS_E_INVAL; + + status = hdd_open_adapter_no_trans(hdd_ctx, QDF_STA_MODE, + iface_name, mac_addr); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_hdd_release_intf_addr(hdd_ctx, mac_addr); + hdd_err("Failed to open concurrent station interface"); + } + + return status; +} + +static QDF_STATUS +hdd_open_adapters_for_mission_mode(struct hdd_context *hdd_ctx) +{ + enum dot11p_mode dot11p_mode; + QDF_STATUS status; + uint8_t *mac_addr; + + ucfg_mlme_get_dot11p_mode(hdd_ctx->psoc, &dot11p_mode); + + /* Create only 802.11p interface? */ + if (dot11p_mode == CFG_11P_STANDALONE) + return hdd_open_ocb_interface(hdd_ctx); + + mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, QDF_STA_MODE); + if (!mac_addr) + return QDF_STATUS_E_INVAL; + + status = hdd_open_adapter_no_trans(hdd_ctx, QDF_STA_MODE, + "wlan%d", mac_addr); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_hdd_release_intf_addr(hdd_ctx, mac_addr); + return status; + } + + /* opening concurrent STA is best effort, continue on error */ + hdd_open_concurrent_interface(hdd_ctx); + + status = hdd_open_p2p_interface(hdd_ctx); + if (status) + goto err_close_adapters; + + /* + * Create separate interface (wifi-aware0) for NAN. All NAN commands + * should go on this new interface. + */ + if (wlan_hdd_is_vdev_creation_allowed(hdd_ctx->psoc)) { + mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, QDF_NAN_DISC_MODE); + if (!mac_addr) + goto err_close_adapters; + + status = hdd_open_adapter_no_trans(hdd_ctx, QDF_NAN_DISC_MODE, + "wifi-aware%d", mac_addr); + if (status) { + wlan_hdd_release_intf_addr(hdd_ctx, mac_addr); + goto err_close_adapters; + } + } + /* Open 802.11p Interface */ + if (dot11p_mode == CFG_11P_CONCURRENT) { + status = hdd_open_ocb_interface(hdd_ctx); + if (QDF_IS_STATUS_ERROR(status)) + goto err_close_adapters; + } + + return QDF_STATUS_SUCCESS; + +err_close_adapters: + hdd_close_all_adapters(hdd_ctx, true); + + return status; +} + +static QDF_STATUS hdd_open_adapters_for_ftm_mode(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + uint8_t *mac_addr; + + mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, QDF_FTM_MODE); + if (!mac_addr) + return QDF_STATUS_E_INVAL; + + status = hdd_open_adapter_no_trans(hdd_ctx, QDF_FTM_MODE, + "wlan%d", mac_addr); + + if (QDF_IS_STATUS_ERROR(status)) { + wlan_hdd_release_intf_addr(hdd_ctx, mac_addr); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +hdd_open_adapters_for_monitor_mode(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + uint8_t *mac_addr; + + mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, QDF_MONITOR_MODE); + if (!mac_addr) + return QDF_STATUS_E_INVAL; + + status = hdd_open_adapter_no_trans(hdd_ctx, QDF_MONITOR_MODE, + "wlan%d", mac_addr); + + if (QDF_IS_STATUS_ERROR(status)) { + wlan_hdd_release_intf_addr(hdd_ctx, mac_addr); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS hdd_open_adapters_for_epping_mode(struct hdd_context *hdd_ctx) +{ + epping_enable_adapter(); + return QDF_STATUS_SUCCESS; +} + +typedef QDF_STATUS (*hdd_open_mode_handler)(struct hdd_context *hdd_ctx); + +static const hdd_open_mode_handler +hdd_open_mode_handlers[QDF_GLOBAL_MAX_MODE] = { + [QDF_GLOBAL_MISSION_MODE] = hdd_open_adapters_for_mission_mode, + [QDF_GLOBAL_FTM_MODE] = hdd_open_adapters_for_ftm_mode, + [QDF_GLOBAL_MONITOR_MODE] = hdd_open_adapters_for_monitor_mode, + [QDF_GLOBAL_EPPING_MODE] = hdd_open_adapters_for_epping_mode, +}; + +static QDF_STATUS hdd_open_adapters_for_mode(struct hdd_context *hdd_ctx, + enum QDF_GLOBAL_MODE driver_mode) +{ + QDF_STATUS status; + + if (driver_mode < 0 || + driver_mode >= QDF_GLOBAL_MAX_MODE || + !hdd_open_mode_handlers[driver_mode]) { + hdd_err("Driver mode %d not supported", driver_mode); + return -ENOTSUPP; + } + + hdd_hold_rtnl_lock(); + status = hdd_open_mode_handlers[driver_mode](hdd_ctx); + hdd_release_rtnl_lock(); + + return status; +} + +int hdd_wlan_startup(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + int errno; + bool is_imps_enabled; + + hdd_enter(); + + hdd_action_oui_config(hdd_ctx); + + qdf_nbuf_init_replenish_timer(); + + status = wlan_hdd_cache_chann_mutex_create(hdd_ctx); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + +#ifdef FEATURE_WLAN_CH_AVOID + mutex_init(&hdd_ctx->avoid_freq_lock); +#endif + + osif_request_manager_init(); + hdd_driver_memdump_init(); + + hdd_dp_trace_init(hdd_ctx->config); + + errno = hdd_wlan_start_modules(hdd_ctx, false); + if (errno) { + hdd_err("Failed to start modules; errno:%d", errno); + goto memdump_deinit; + } + + if (hdd_get_conparam() == QDF_GLOBAL_EPPING_MODE) + return 0; + + wlan_hdd_update_wiphy(hdd_ctx); + + hdd_ctx->mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (!hdd_ctx->mac_handle) { + hdd_err("Mac Handle is null"); + goto stop_modules; + } + + errno = hdd_wiphy_init(hdd_ctx); + if (errno) { + hdd_err("Failed to initialize wiphy; errno:%d", errno); + goto stop_modules; + } + + errno = hdd_initialize_mac_address(hdd_ctx); + if (errno) { + hdd_err("MAC initializtion failed: %d", errno); + goto unregister_wiphy; + } + + errno = register_netdevice_notifier(&hdd_netdev_notifier); + if (errno) { + hdd_err("register_netdevice_notifier failed; errno:%d", errno); + goto unregister_wiphy; + } + + wlan_hdd_update_11n_mode(hdd_ctx); + + hdd_lpass_notify_wlan_version(hdd_ctx); + + status = wlansap_global_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto unregister_notifiers; + + ucfg_mlme_is_imps_enabled(hdd_ctx->psoc, &is_imps_enabled); + hdd_set_idle_ps_config(hdd_ctx, is_imps_enabled); + hdd_debugfs_mws_coex_info_init(hdd_ctx); + hdd_debugfs_ini_config_init(hdd_ctx); + wlan_cfg80211_init_interop_issues_ap(hdd_ctx->pdev); + + hdd_exit(); + + return 0; + +unregister_notifiers: + unregister_netdevice_notifier(&hdd_netdev_notifier); + +unregister_wiphy: + qdf_dp_trace_deinit(); + wiphy_unregister(hdd_ctx->wiphy); + +stop_modules: + hdd_wlan_stop_modules(hdd_ctx, false); + +memdump_deinit: + hdd_driver_memdump_deinit(); + osif_request_manager_deinit(); + qdf_nbuf_deinit_replenish_timer(); + + if (cds_is_fw_down()) + hdd_err("Not setting the complete event as fw is down"); + else + hdd_start_complete(errno); + + hdd_exit(); + + return errno; +} + +QDF_STATUS hdd_psoc_create_vdevs(struct hdd_context *hdd_ctx) +{ + enum QDF_GLOBAL_MODE driver_mode = hdd_get_conparam(); + QDF_STATUS status; + + status = hdd_open_adapters_for_mode(hdd_ctx, driver_mode); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to create vdevs; status:%d", status); + return status; + } + + if (hdd_ctx->rps) + hdd_set_rps_cpu_mask(hdd_ctx); + + if (driver_mode != QDF_GLOBAL_FTM_MODE && + driver_mode != QDF_GLOBAL_EPPING_MODE) + hdd_psoc_idle_timer_start(hdd_ctx); + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_wlan_update_target_info() - update target type info + * @hdd_ctx: HDD context + * @context: hif context + * + * Update target info received from firmware in hdd context + * Return:None + */ + +void hdd_wlan_update_target_info(struct hdd_context *hdd_ctx, void *context) +{ + struct hif_target_info *tgt_info = hif_get_target_info_handle(context); + + if (!tgt_info) { + hdd_err("Target info is Null"); + return; + } + + hdd_ctx->target_type = tgt_info->target_type; +} + +void hdd_get_nud_stats_cb(void *data, struct rsp_stats *rsp, void *context) +{ + struct hdd_context *hdd_ctx = (struct hdd_context *)data; + int status; + struct hdd_adapter *adapter = NULL; + struct osif_request *request = NULL; + + hdd_enter(); + + if (!rsp) { + hdd_err("data is null"); + return; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status != 0) + return; + + request = osif_request_get(context); + if (!request) { + hdd_err("obselete request"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, rsp->vdev_id); + if ((!adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("Invalid adapter or adapter has invalid magic"); + osif_request_put(request); + return; + } + + hdd_debug("rsp->arp_req_enqueue :%x", rsp->arp_req_enqueue); + hdd_debug("rsp->arp_req_tx_success :%x", rsp->arp_req_tx_success); + hdd_debug("rsp->arp_req_tx_failure :%x", rsp->arp_req_tx_failure); + hdd_debug("rsp->arp_rsp_recvd :%x", rsp->arp_rsp_recvd); + hdd_debug("rsp->out_of_order_arp_rsp_drop_cnt :%x", + rsp->out_of_order_arp_rsp_drop_cnt); + hdd_debug("rsp->dad_detected :%x", rsp->dad_detected); + hdd_debug("rsp->connect_status :%x", rsp->connect_status); + hdd_debug("rsp->ba_session_establishment_status :%x", + rsp->ba_session_establishment_status); + + adapter->hdd_stats.hdd_arp_stats.rx_fw_cnt = rsp->arp_rsp_recvd; + adapter->dad |= rsp->dad_detected; + adapter->con_status = rsp->connect_status; + + /* Flag true indicates connectivity check stats present. */ + if (rsp->connect_stats_present) { + hdd_debug("rsp->tcp_ack_recvd :%x", rsp->tcp_ack_recvd); + hdd_debug("rsp->icmpv4_rsp_recvd :%x", rsp->icmpv4_rsp_recvd); + adapter->hdd_stats.hdd_tcp_stats.rx_fw_cnt = rsp->tcp_ack_recvd; + adapter->hdd_stats.hdd_icmpv4_stats.rx_fw_cnt = + rsp->icmpv4_rsp_recvd; + } + + osif_request_complete(request); + osif_request_put(request); + + hdd_exit(); +} + +#ifdef WLAN_FEATURE_MOTION_DETECTION +/** + * hdd_md_host_evt_cb - Callback for Motion Detection Event + * @ctx: HDD context + * @event: motion detect event + * + * Callback for Motion Detection Event. Re-enables Motion + * Detection again upon event + * + * Return: QDF_STATUS QDF_STATUS_SUCCESS on Success and + * QDF_STATUS_E_FAILURE on failure + */ +QDF_STATUS hdd_md_host_evt_cb(void *ctx, struct sir_md_evt *event) +{ + struct hdd_adapter *adapter = NULL; + struct hdd_context *hdd_ctx; + struct sme_motion_det_en motion_det; + + if (!ctx || !event) + return QDF_STATUS_E_INVAL; + + hdd_ctx = (struct hdd_context *)ctx; + if (wlan_hdd_validate_context(hdd_ctx)) + return QDF_STATUS_E_INVAL; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, event->vdev_id); + if (!adapter || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return QDF_STATUS_E_INVAL; + } + + /* When motion is detected, reset the motion_det_in_progress flag */ + if (event->status) + adapter->motion_det_in_progress = false; + + hdd_debug("Motion Detection CB vdev_id=%u, status=%u", + event->vdev_id, event->status); + + if (adapter->motion_detection_mode) { + motion_det.vdev_id = event->vdev_id; + motion_det.enable = 1; + hdd_debug("Motion Detect CB -> Enable Motion Detection again"); + sme_motion_det_enable(hdd_ctx->mac_handle, &motion_det); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_md_bl_evt_cb - Callback for Motion Detection Baseline Event + * @ctx: HDD context + * @event: motion detect baseline event + * + * Callback for Motion Detection Baseline completion + * + * Return: QDF_STATUS QDF_STATUS_SUCCESS on Success and + * QDF_STATUS_E_FAILURE on failure + */ +QDF_STATUS hdd_md_bl_evt_cb(void *ctx, struct sir_md_bl_evt *event) +{ + struct hdd_adapter *adapter = NULL; + struct hdd_context *hdd_ctx; + + if (!ctx || !event) + return QDF_STATUS_E_INVAL; + + hdd_ctx = (struct hdd_context *)ctx; + if (wlan_hdd_validate_context(hdd_ctx)) + return QDF_STATUS_E_INVAL; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, event->vdev_id); + if (!adapter || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return QDF_STATUS_E_INVAL; + } + + hdd_debug("Motion Detection Baseline CB vdev id=%u, baseline val = %d", + event->vdev_id, event->bl_baseline_value); + + adapter->motion_det_baseline_value = event->bl_baseline_value; + + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +/** + * hdd_register_cb - Register HDD callbacks. + * @hdd_ctx: HDD context + * + * Register the HDD callbacks to CDS/SME. + * + * Return: 0 for success or Error code for failure + */ +int hdd_register_cb(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + int ret = 0; + mac_handle_t mac_handle; + + hdd_enter(); + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("in ftm mode, no need to register callbacks"); + return ret; + } + + mac_handle = hdd_ctx->mac_handle; + + sme_register_oem_data_rsp_callback(mac_handle, + hdd_send_oem_data_rsp_msg); + + sme_register_mgmt_frame_ind_callback(mac_handle, + hdd_indicate_mgmt_frame); + sme_set_tsfcb(mac_handle, hdd_get_tsf_cb, hdd_ctx); + sme_stats_ext_register_callback(mac_handle, + wlan_hdd_cfg80211_stats_ext_callback); + + sme_ext_scan_register_callback(mac_handle, + wlan_hdd_cfg80211_extscan_callback); + sme_stats_ext2_register_callback(mac_handle, + wlan_hdd_cfg80211_stats_ext2_callback); + + sme_set_rssi_threshold_breached_cb(mac_handle, + hdd_rssi_threshold_breached); + + sme_set_link_layer_stats_ind_cb(mac_handle, + wlan_hdd_cfg80211_link_layer_stats_callback); + + sme_rso_cmd_status_cb(mac_handle, wlan_hdd_rso_cmd_status_cb); + + sme_set_link_layer_ext_cb(mac_handle, + wlan_hdd_cfg80211_link_layer_stats_ext_callback); + sme_update_hidden_ssid_status_cb(mac_handle, + hdd_hidden_ssid_enable_roaming); + + status = sme_set_lost_link_info_cb(mac_handle, + hdd_lost_link_info_cb); + + wlan_hdd_register_cp_stats_cb(hdd_ctx); + + /* print error and not block the startup process */ + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("set lost link info callback failed"); + + ret = hdd_register_data_stall_detect_cb(); + if (ret) { + hdd_err("Register data stall detect detect callback failed."); + return ret; + } + + wlan_hdd_dcc_register_for_dcc_stats_event(hdd_ctx); + + sme_register_set_connection_info_cb(mac_handle, + hdd_set_connection_in_progress, + hdd_is_connection_in_progress); + + status = sme_set_bt_activity_info_cb(mac_handle, + hdd_bt_activity_cb); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("set bt activity info callback failed"); + + status = sme_register_tx_queue_cb(mac_handle, + hdd_tx_queue_cb); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Register tx queue callback failed"); + +#ifdef WLAN_FEATURE_MOTION_DETECTION + sme_set_md_host_evt_cb(mac_handle, hdd_md_host_evt_cb, (void *)hdd_ctx); + sme_set_md_bl_evt_cb(mac_handle, hdd_md_bl_evt_cb, (void *)hdd_ctx); +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + + mac_register_sesssion_open_close_cb(hdd_ctx->mac_handle, + hdd_sme_close_session_callback, + hdd_common_roam_callback); + + sme_set_roam_scan_ch_event_cb(mac_handle, hdd_get_roam_scan_ch_cb); + status = sme_set_monitor_mode_cb(mac_handle, + hdd_sme_monitor_mode_callback); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err_rl("Register monitor mode callback failed"); + + hdd_exit(); + + return ret; +} + +/** + * hdd_deregister_cb() - De-Register HDD callbacks. + * @hdd_ctx: HDD context + * + * De-Register the HDD callbacks to CDS/SME. + * + * Return: void + */ +void hdd_deregister_cb(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + int ret; + mac_handle_t mac_handle; + + hdd_enter(); + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("in ftm mode, no need to deregister callbacks"); + return; + } + + mac_handle = hdd_ctx->mac_handle; + + sme_deregister_tx_queue_cb(mac_handle); + + sme_reset_link_layer_stats_ind_cb(mac_handle); + sme_reset_rssi_threshold_breached_cb(mac_handle); + + sme_stats_ext_deregister_callback(mac_handle); + + status = sme_reset_tsfcb(mac_handle); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to de-register tsfcb the callback:%d", + status); + + ret = hdd_deregister_data_stall_detect_cb(); + if (ret) + hdd_err("Failed to de-register data stall detect event callback"); + + sme_deregister_oem_data_rsp_callback(mac_handle); + + hdd_exit(); +} + +/** + * hdd_softap_sta_deauth() - handle deauth req from HDD + * @adapter: Pointer to the HDD adapter + * @param: Params to the operation + * + * This to take counter measure to handle deauth req from HDD + * + * Return: None + */ +QDF_STATUS hdd_softap_sta_deauth(struct hdd_adapter *adapter, + struct csr_del_sta_params *param) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAULT; + + hdd_enter(); + + /* Ignore request to deauth bcmc station */ + if (param->peerMacAddr.bytes[0] & 0x1) + return qdf_status; + + qdf_status = + wlansap_deauth_sta(WLAN_HDD_GET_SAP_CTX_PTR(adapter), + param); + + hdd_exit(); + return qdf_status; +} + +/** + * hdd_softap_sta_disassoc() - take counter measure to handle deauth req from HDD + * @adapter: Pointer to the HDD + * @param: pointer to station deletion parameters + * + * This to take counter measure to handle deauth req from HDD + * + * Return: None + */ +void hdd_softap_sta_disassoc(struct hdd_adapter *adapter, + struct csr_del_sta_params *param) +{ + hdd_enter(); + + /* Ignore request to disassoc bcmc station */ + if (param->peerMacAddr.bytes[0] & 0x1) + return; + + wlansap_disassoc_sta(WLAN_HDD_GET_SAP_CTX_PTR(adapter), + param); +} + +void wlan_hdd_disable_roaming(struct hdd_adapter *cur_adapter, + uint32_t mlme_operation_requestor) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(cur_adapter); + struct csr_roam_profile *roam_profile; + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + struct hdd_station_ctx *sta_ctx; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_DISABLE_ROAMING; + + if (!policy_mgr_is_sta_active_connection_exists(hdd_ctx->psoc)) + return; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + roam_profile = hdd_roam_profile(adapter); + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (cur_adapter->vdev_id != adapter->vdev_id && + adapter->device_mode == QDF_STA_MODE && + hdd_conn_is_connected(sta_ctx)) { + hdd_debug("%d Disable roaming", adapter->vdev_id); + sme_stop_roaming(hdd_ctx->mac_handle, + adapter->vdev_id, + REASON_DRIVER_DISABLED, + mlme_operation_requestor); + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } +} + +void wlan_hdd_enable_roaming(struct hdd_adapter *cur_adapter, + uint32_t mlme_operation_requestor) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(cur_adapter); + struct csr_roam_profile *roam_profile; + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + struct hdd_station_ctx *sta_ctx; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_ENABLE_ROAMING; + + if (!policy_mgr_is_sta_active_connection_exists(hdd_ctx->psoc)) + return; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + roam_profile = hdd_roam_profile(adapter); + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (cur_adapter->vdev_id != adapter->vdev_id && + adapter->device_mode == QDF_STA_MODE && + hdd_conn_is_connected(sta_ctx)) { + hdd_debug("%d Enable roaming", adapter->vdev_id); + sme_start_roaming(hdd_ctx->mac_handle, + adapter->vdev_id, + REASON_DRIVER_ENABLED, + mlme_operation_requestor); + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } +} + +/** + * nl_srv_bcast_svc() - Wrapper function to send bcast msgs to SVC mcast group + * @skb: sk buffer pointer + * + * Sends the bcast message to SVC multicast group with generic nl socket + * if CNSS_GENL is enabled. Else, use the legacy netlink socket to send. + * + * Return: None + */ +static void nl_srv_bcast_svc(struct sk_buff *skb) +{ +#ifdef CNSS_GENL + nl_srv_bcast(skb, CLD80211_MCGRP_SVC_MSGS, WLAN_NL_MSG_SVC); +#else + nl_srv_bcast(skb); +#endif +} + +void wlan_hdd_send_svc_nlink_msg(int radio, int type, void *data, int len) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + tAniMsgHdr *ani_hdr; + void *nl_data = NULL; + int flags = GFP_KERNEL; + struct radio_index_tlv *radio_info; + int tlv_len; + + if (in_interrupt() || irqs_disabled() || in_atomic()) + flags = GFP_ATOMIC; + + skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), flags); + + if (!skb) + return; + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_type = WLAN_NL_MSG_SVC; + + ani_hdr = NLMSG_DATA(nlh); + ani_hdr->type = type; + + switch (type) { + case WLAN_SVC_FW_CRASHED_IND: + case WLAN_SVC_FW_SHUTDOWN_IND: + case WLAN_SVC_LTE_COEX_IND: + case WLAN_SVC_WLAN_AUTO_SHUTDOWN_IND: + case WLAN_SVC_WLAN_AUTO_SHUTDOWN_CANCEL_IND: + ani_hdr->length = 0; + nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr))); + break; + case WLAN_SVC_WLAN_STATUS_IND: + case WLAN_SVC_WLAN_VERSION_IND: + case WLAN_SVC_DFS_CAC_START_IND: + case WLAN_SVC_DFS_CAC_END_IND: + case WLAN_SVC_DFS_RADAR_DETECT_IND: + case WLAN_SVC_DFS_ALL_CHANNEL_UNAVAIL_IND: + case WLAN_SVC_WLAN_TP_IND: + case WLAN_SVC_WLAN_TP_TX_IND: + case WLAN_SVC_RPS_ENABLE_IND: + case WLAN_SVC_CORE_MINFREQ: + ani_hdr->length = len; + nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + len)); + nl_data = (char *)ani_hdr + sizeof(tAniMsgHdr); + memcpy(nl_data, data, len); + break; + + default: + hdd_err("WLAN SVC: Attempt to send unknown nlink message %d", + type); + kfree_skb(skb); + return; + } + + /* + * Add radio index at the end of the svc event in TLV format + * to maintain the backward compatibility with userspace + * applications. + */ + + tlv_len = 0; + + if ((sizeof(*ani_hdr) + len + sizeof(struct radio_index_tlv)) + < WLAN_NL_MAX_PAYLOAD) { + radio_info = (struct radio_index_tlv *)((char *) ani_hdr + + sizeof(*ani_hdr) + len); + radio_info->type = (unsigned short) WLAN_SVC_WLAN_RADIO_INDEX; + radio_info->length = (unsigned short) sizeof(radio_info->radio); + radio_info->radio = radio; + tlv_len = sizeof(*radio_info); + hdd_debug("Added radio index tlv - radio index %d", + radio_info->radio); + } + + nlh->nlmsg_len += tlv_len; + skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + len + tlv_len)); + + nl_srv_bcast_svc(skb); +} + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +void wlan_hdd_auto_shutdown_cb(void) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) + return; + + hdd_debug("Wlan Idle. Sending Shutdown event.."); + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_WLAN_AUTO_SHUTDOWN_IND, NULL, 0); +} + +void wlan_hdd_auto_shutdown_enable(struct hdd_context *hdd_ctx, bool enable) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + bool ap_connected = false, sta_connected = false; + mac_handle_t mac_handle; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_AUTO_SHUTDOWN_ENABLE; + + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) + return; + + if (hdd_ctx->config->wlan_auto_shutdown == 0) + return; + + if (enable == false) { + if (sme_set_auto_shutdown_timer(mac_handle, 0) != + QDF_STATUS_SUCCESS) { + hdd_err("Failed to stop wlan auto shutdown timer"); + } + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_WLAN_AUTO_SHUTDOWN_CANCEL_IND, NULL, 0); + return; + } + + /* To enable shutdown timer check conncurrency */ + if (policy_mgr_concurrent_open_sessions_running(hdd_ctx->psoc)) { + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, + next_adapter, dbgid) { + if (adapter->device_mode == QDF_STA_MODE) { + if (WLAN_HDD_GET_STATION_CTX_PTR(adapter)-> + conn_info.conn_state == + eConnectionState_Associated) { + sta_connected = true; + hdd_adapter_dev_put_debug(adapter, + dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug( + next_adapter, + dbgid); + break; + } + } + + if (adapter->device_mode == QDF_SAP_MODE) { + if (WLAN_HDD_GET_AP_CTX_PTR(adapter)-> + ap_active == true) { + ap_connected = true; + hdd_adapter_dev_put_debug(adapter, + dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug( + next_adapter, + dbgid); + break; + } + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + } + + if (ap_connected == true || sta_connected == true) { + hdd_debug("CC Session active. Shutdown timer not enabled"); + return; + } + + if (sme_set_auto_shutdown_timer(mac_handle, + hdd_ctx->config->wlan_auto_shutdown) + != QDF_STATUS_SUCCESS) + hdd_err("Failed to start wlan auto shutdown timer"); + else + hdd_info("Auto Shutdown timer for %d seconds enabled", + hdd_ctx->config->wlan_auto_shutdown); +} +#endif + +struct hdd_adapter * +hdd_get_con_sap_adapter(struct hdd_adapter *this_sap_adapter, + bool check_start_bss) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(this_sap_adapter); + struct hdd_adapter *adapter, *con_sap_adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_CON_SAP_ADAPTER; + + con_sap_adapter = NULL; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (adapter && ((adapter->device_mode == QDF_SAP_MODE) || + (adapter->device_mode == QDF_P2P_GO_MODE)) && + adapter != this_sap_adapter) { + if (check_start_bss) { + if (test_bit(SOFTAP_BSS_STARTED, + &adapter->event_flags)) { + con_sap_adapter = adapter; + hdd_adapter_dev_put_debug(adapter, + dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug( + next_adapter, + dbgid); + break; + } + } else { + con_sap_adapter = adapter; + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + break; + } + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return con_sap_adapter; +} + +static inline bool hdd_adapter_is_sta(struct hdd_adapter *adapter) +{ + return adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE; +} + +static inline bool hdd_adapter_is_ap(struct hdd_adapter *adapter) +{ + return adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE; +} + +bool hdd_is_any_adapter_connected(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_IS_ANY_ADAPTER_CONNECTED; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (hdd_adapter_is_sta(adapter) && + WLAN_HDD_GET_STATION_CTX_PTR(adapter)-> + conn_info.conn_state == eConnectionState_Associated) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return true; + } + + if (hdd_adapter_is_ap(adapter) && + WLAN_HDD_GET_AP_CTX_PTR(adapter)->ap_active) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return true; + } + + if (adapter->device_mode == QDF_NDI_MODE && + WLAN_HDD_GET_STATION_CTX_PTR(adapter)-> + conn_info.conn_state == eConnectionState_NdiConnected) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return true; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return false; +} + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +static void __hdd_bus_bw_compute_timer_start(struct hdd_context *hdd_ctx) +{ + qdf_periodic_work_start(&hdd_ctx->bus_bw_work, + hdd_ctx->config->bus_bw_compute_interval); +} + +void hdd_bus_bw_compute_timer_start(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + __hdd_bus_bw_compute_timer_start(hdd_ctx); + + hdd_exit(); +} + +void hdd_bus_bw_compute_timer_try_start(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + if (hdd_is_any_adapter_connected(hdd_ctx)) + __hdd_bus_bw_compute_timer_start(hdd_ctx); + + hdd_exit(); +} + +static void __hdd_bus_bw_compute_timer_stop(struct hdd_context *hdd_ctx) +{ + if (!qdf_periodic_work_stop_sync(&hdd_ctx->bus_bw_work)) + goto exit; + + ucfg_ipa_set_perf_level(hdd_ctx->pdev, 0, 0); + hdd_reset_tcp_delack(hdd_ctx); + hdd_reset_tcp_adv_win_scale(hdd_ctx); + cdp_pdev_reset_driver_del_ack(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID); + cdp_pdev_reset_bundle_require_flag(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID); + +exit: + /** + * This check if for the case where the bus bw timer is forcibly + * stopped. We should remove the bus bw voting, if no adapter is + * connected + */ + if (!hdd_is_any_adapter_connected(hdd_ctx)) { + qdf_atomic_set(&hdd_ctx->num_latency_critical_clients, 0); + hdd_ctx->cur_vote_level = PLD_BUS_WIDTH_NONE; + pld_request_bus_bandwidth(hdd_ctx->parent_dev, + PLD_BUS_WIDTH_NONE); + } +} + +void hdd_bus_bw_compute_timer_stop(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + __hdd_bus_bw_compute_timer_stop(hdd_ctx); + + hdd_exit(); +} + +void hdd_bus_bw_compute_timer_try_stop(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + if (!hdd_is_any_adapter_connected(hdd_ctx)) + __hdd_bus_bw_compute_timer_stop(hdd_ctx); + + hdd_exit(); +} + +void hdd_bus_bw_compute_prev_txrx_stats(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + qdf_spin_lock_bh(&hdd_ctx->bus_bw_lock); + adapter->prev_tx_packets = adapter->stats.tx_packets; + adapter->prev_rx_packets = adapter->stats.rx_packets; + adapter->prev_tx_bytes = adapter->stats.tx_bytes; + cdp_get_intra_bss_fwd_pkts_count(cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id, + &adapter->prev_fwd_tx_packets, + &adapter->prev_fwd_rx_packets); + qdf_spin_unlock_bh(&hdd_ctx->bus_bw_lock); +} + +void hdd_bus_bw_compute_reset_prev_txrx_stats(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + qdf_spin_lock_bh(&hdd_ctx->bus_bw_lock); + adapter->prev_tx_packets = 0; + adapter->prev_rx_packets = 0; + adapter->prev_fwd_tx_packets = 0; + adapter->prev_fwd_rx_packets = 0; + adapter->prev_tx_bytes = 0; + qdf_spin_unlock_bh(&hdd_ctx->bus_bw_lock); +} + +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + +/** + * wlan_hdd_stop_sap() - This function stops bss of SAP. + * @ap_adapter: SAP adapter + * + * This function will process the stopping of sap adapter. + * + * Return: None + */ +void wlan_hdd_stop_sap(struct hdd_adapter *ap_adapter) +{ + struct hdd_ap_ctx *hdd_ap_ctx; + struct hdd_hostapd_state *hostapd_state; + QDF_STATUS qdf_status; + struct hdd_context *hdd_ctx; + + if (!ap_adapter) { + hdd_err("ap_adapter is NULL here"); + return; + } + + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); + hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + mutex_lock(&hdd_ctx->sap_lock); + if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) { + wlan_hdd_del_station(ap_adapter); + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter); + hdd_debug("Now doing SAP STOPBSS"); + qdf_event_reset(&hostapd_state->qdf_stop_bss_event); + if (QDF_STATUS_SUCCESS == wlansap_stop_bss(hdd_ap_ctx-> + sap_context)) { + qdf_status = qdf_wait_for_event_completion(&hostapd_state-> + qdf_stop_bss_event, + SME_CMD_STOP_BSS_TIMEOUT); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + mutex_unlock(&hdd_ctx->sap_lock); + hdd_err("SAP Stop Failed"); + return; + } + } + clear_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags); + policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, + ap_adapter->device_mode, + ap_adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, ap_adapter->device_mode, + false); + hdd_debug("SAP Stop Success"); + } else { + hdd_err("Can't stop ap because its not started"); + } + mutex_unlock(&hdd_ctx->sap_lock); +} + +/** + * wlan_hdd_start_sap() - this function starts bss of SAP. + * @ap_adapter: SAP adapter + * + * This function will process the starting of sap adapter. + * + * Return: None + */ +void wlan_hdd_start_sap(struct hdd_adapter *ap_adapter, bool reinit) +{ + struct hdd_ap_ctx *hdd_ap_ctx; + struct hdd_hostapd_state *hostapd_state; + QDF_STATUS qdf_status; + struct hdd_context *hdd_ctx; + struct sap_config *sap_config; + + if (!ap_adapter) { + hdd_err("ap_adapter is NULL here"); + return; + } + + if (QDF_SAP_MODE != ap_adapter->device_mode) { + hdd_err("SoftAp role has not been enabled"); + return; + } + + hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter); + sap_config = &ap_adapter->session.ap.sap_config; + + mutex_lock(&hdd_ctx->sap_lock); + if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) + goto end; + + if (0 != wlan_hdd_cfg80211_update_apies(ap_adapter)) { + hdd_err("SAP Not able to set AP IEs"); + goto end; + } + wlan_reg_set_channel_params_for_freq( + hdd_ctx->pdev, + hdd_ap_ctx->sap_config.chan_freq, + 0, &hdd_ap_ctx->sap_config.ch_params); + + qdf_event_reset(&hostapd_state->qdf_event); + if (wlansap_start_bss(hdd_ap_ctx->sap_context, hdd_hostapd_sap_event_cb, + &hdd_ap_ctx->sap_config, + ap_adapter->dev) + != QDF_STATUS_SUCCESS) + goto end; + + hdd_debug("Waiting for SAP to start"); + qdf_status = qdf_wait_single_event(&hostapd_state->qdf_event, + SME_CMD_START_BSS_TIMEOUT); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("SAP Start failed"); + goto end; + } + hdd_info("SAP Start Success"); + wlansap_reset_sap_config_add_ie(sap_config, eUPDATE_IE_ALL); + set_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags); + if (hostapd_state->bss_state == BSS_START) { + policy_mgr_incr_active_session(hdd_ctx->psoc, + ap_adapter->device_mode, + ap_adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, ap_adapter->device_mode, + true); + } + mutex_unlock(&hdd_ctx->sap_lock); + + return; +end: + wlansap_reset_sap_config_add_ie(sap_config, eUPDATE_IE_ALL); + mutex_unlock(&hdd_ctx->sap_lock); + /* SAP context and beacon cleanup will happen during driver unload + * in hdd_stop_adapter + */ + hdd_err("SAP restart after SSR failed! Reload WLAN and try SAP again"); + /* Free the beacon memory in case of failure in the sap restart */ + if (ap_adapter->session.ap.beacon) { + qdf_mem_free(ap_adapter->session.ap.beacon); + ap_adapter->session.ap.beacon = NULL; + } +} + +#ifdef QCA_CONFIG_SMP +/** + * wlan_hdd_get_cpu() - get cpu_index + * + * Return: cpu_index + */ +int wlan_hdd_get_cpu(void) +{ + int cpu_index = get_cpu(); + + put_cpu(); + return cpu_index; +} +#endif + +/** + * hdd_get_fwpath() - get framework path + * + * This function is used to get the string written by + * userspace to start the wlan driver + * + * Return: string + */ +const char *hdd_get_fwpath(void) +{ + return fwpath.string; +} + +static inline int hdd_state_query_cb(void) +{ + return !!wlan_hdd_validate_context(cds_get_context(QDF_MODULE_ID_HDD)); +} + +static int __hdd_op_protect_cb(void **out_sync, const char *func) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) + return -EAGAIN; + + return __osif_psoc_sync_op_start(hdd_ctx->parent_dev, + (struct osif_psoc_sync **)out_sync, + func); +} + +static void __hdd_op_unprotect_cb(void *sync, const char *func) +{ + __osif_psoc_sync_op_stop(sync, func); +} + +/** + * hdd_init() - Initialize Driver + * + * This function initilizes CDS global context with the help of cds_init. This + * has to be the first function called after probe to get a valid global + * context. + * + * Return: 0 for success, errno on failure + */ +int hdd_init(void) +{ + QDF_STATUS status; + + status = cds_init(); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to allocate CDS context"); + return -ENOMEM; + } + + qdf_op_callbacks_register(__hdd_op_protect_cb, __hdd_op_unprotect_cb); + + wlan_init_bug_report_lock(); + +#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE + wlan_logging_sock_init_svc(); +#endif + + hdd_trace_init(); + hdd_register_debug_callback(); + wlan_roam_debug_init(); + + return 0; +} + +/** + * hdd_deinit() - Deinitialize Driver + * + * This function frees CDS global context with the help of cds_deinit. This + * has to be the last function call in remove callback to free the global + * context. + */ +void hdd_deinit(void) +{ + wlan_roam_debug_deinit(); + +#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE + wlan_logging_sock_deinit_svc(); +#endif + + wlan_destroy_bug_report_lock(); + qdf_op_callbacks_register(NULL, NULL); + cds_deinit(); +} + +#ifdef QCA_WIFI_NAPIER_EMULATION +#define HDD_WLAN_START_WAIT_TIME ((CDS_WMA_TIMEOUT + 5000) * 100) +#else +#define HDD_WLAN_START_WAIT_TIME (CDS_WMA_TIMEOUT + 5000) +#endif + +static void hdd_set_adapter_wlm_def_level(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_ADAPTER; + int ret; + QDF_STATUS qdf_status; + uint8_t latency_level; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) + return; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret != 0) + return; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + qdf_status = ucfg_mlme_cfg_get_wlm_level(hdd_ctx->psoc, + &latency_level); + if (QDF_IS_STATUS_ERROR(qdf_status)) + adapter->latency_level = + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL; + else + adapter->latency_level = latency_level; + + adapter->upgrade_udp_qos_threshold = QCA_WLAN_AC_BK; + hdd_debug("UDP packets qos reset to: %d", + adapter->upgrade_udp_qos_threshold); + hdd_adapter_dev_put_debug(adapter, dbgid); + } +} + +static int wlan_hdd_state_ctrl_param_open(struct inode *inode, + struct file *file) +{ + qdf_atomic_inc(&wlan_hdd_state_fops_ref); + + return 0; +} + +static void __hdd_inform_wifi_off(void) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + int ret; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret != 0) + return; + + ucfg_blm_wifi_off(hdd_ctx->pdev); +} + +static void hdd_inform_wifi_off(void) +{ + int ret; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct osif_psoc_sync *psoc_sync; + + if (!hdd_ctx) { + hdd_err("HDD context is Null"); + return; + } + + ret = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), &psoc_sync); + if (ret) + return; + + hdd_set_adapter_wlm_def_level(hdd_ctx); + __hdd_inform_wifi_off(); + + osif_psoc_sync_op_stop(psoc_sync); +} + +void hdd_init_start_completion(void) +{ + INIT_COMPLETION(wlan_start_comp); +} + +#if defined CFG80211_USER_HINT_CELL_BASE_SELF_MANAGED || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) +static void hdd_inform_wifi_on(void) +{ + int ret; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct osif_psoc_sync *psoc_sync; + + hdd_nofl_debug("inform regdomain for wifi on"); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return; + if (!wlan_hdd_validate_modules_state(hdd_ctx)) + return; + if (!hdd_ctx->wiphy) + return; + ret = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), &psoc_sync); + if (ret) + return; + if (hdd_ctx->wiphy->registered) + hdd_send_wiphy_regd_sync_event(hdd_ctx); + + osif_psoc_sync_op_stop(psoc_sync); +} +#else +static void hdd_inform_wifi_on(void) +{ +} +#endif + +static ssize_t wlan_hdd_state_ctrl_param_write(struct file *filp, + const char __user *user_buf, + size_t count, + loff_t *f_pos) +{ + char buf[3]; + static const char wlan_off_str[] = "OFF"; + static const char wlan_on_str[] = "ON"; + int ret; + unsigned long rc; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + bool turning_on = false; + + if (copy_from_user(buf, user_buf, 3)) { + pr_err("Failed to read buffer\n"); + return -EINVAL; + } + + if (strncmp(buf, wlan_off_str, strlen(wlan_off_str)) == 0) { + hdd_info("Wifi turning off from UI\n"); + hdd_inform_wifi_off(); + goto exit; + } + + if (strncmp(buf, wlan_on_str, strlen(wlan_on_str)) == 0) { + hdd_info("Wifi Turning On from UI\n"); + turning_on = true; + } + + if (strncmp(buf, wlan_on_str, strlen(wlan_on_str)) != 0) { + pr_err("Invalid value received from framework"); + goto exit; + } + + if (!cds_is_driver_loaded() || cds_is_driver_recovering()) { + rc = wait_for_completion_timeout(&wlan_start_comp, + msecs_to_jiffies(HDD_WLAN_START_WAIT_TIME)); + if (!rc) { + pr_err("Timed-out!!"); + ret = -EINVAL; + return ret; + } + } + + /* + * Flush idle shutdown work for cases to synchronize the wifi on + * during the idle shutdown. + */ + if (hdd_ctx) + hdd_psoc_idle_timer_stop(hdd_ctx); +exit: + if (turning_on) + hdd_inform_wifi_on(); + + return count; +} + +/** + * wlan_hdd_state_ctrl_param_release() - Release callback for /dev/wlan. + * + * @inode: struct inode pinter. + * @file: struct file pointer. + * + * Release callback that would be invoked when the file operations has + * completed fully. This is implemented to provide a reference count mechanism + * via which the driver can wait till all possible usage of the /dev/wlan + * file is completed. + * + * Return: Success + */ +static int wlan_hdd_state_ctrl_param_release(struct inode *inode, + struct file *file) +{ + qdf_atomic_dec(&wlan_hdd_state_fops_ref); + + return 0; +} + +const struct file_operations wlan_hdd_state_fops = { + .owner = THIS_MODULE, + .open = wlan_hdd_state_ctrl_param_open, + .write = wlan_hdd_state_ctrl_param_write, + .release = wlan_hdd_state_ctrl_param_release, +}; + +static int wlan_hdd_state_ctrl_param_create(void) +{ + unsigned int wlan_hdd_state_major = 0; + int ret; + struct device *dev; + + init_completion(&wlan_start_comp); + qdf_atomic_init(&wlan_hdd_state_fops_ref); + + device = MKDEV(wlan_hdd_state_major, 0); + + ret = alloc_chrdev_region(&device, 0, dev_num, "qcwlanstate"); + if (ret) { + pr_err("Failed to register qcwlanstate"); + goto dev_alloc_err; + } + wlan_hdd_state_major = MAJOR(device); + + class = class_create(THIS_MODULE, WLAN_MODULE_NAME); + if (IS_ERR(class)) { + pr_err("wlan_hdd_state class_create error"); + goto class_err; + } + + dev = device_create(class, NULL, device, NULL, WLAN_MODULE_NAME); + if (IS_ERR(dev)) { + pr_err("wlan_hdd_statedevice_create error"); + goto err_class_destroy; + } + + cdev_init(&wlan_hdd_state_cdev, &wlan_hdd_state_fops); + + wlan_hdd_state_cdev.owner = THIS_MODULE; + + ret = cdev_add(&wlan_hdd_state_cdev, device, dev_num); + if (ret) { + pr_err("Failed to add cdev error"); + goto cdev_add_err; + } + + pr_info("wlan_hdd_state %s major(%d) initialized", + WLAN_MODULE_NAME, wlan_hdd_state_major); + + return 0; + +cdev_add_err: + device_destroy(class, device); +err_class_destroy: + class_destroy(class); +class_err: + unregister_chrdev_region(device, dev_num); +dev_alloc_err: + return -ENODEV; +} + +static void wlan_hdd_state_ctrl_param_destroy(void) +{ + cdev_del(&wlan_hdd_state_cdev); + device_destroy(class, device); + class_destroy(class); + unregister_chrdev_region(device, dev_num); + + pr_info("Device node unregistered"); +} + +/** + * hdd_component_init() - Initialize all components + * + * Return: QDF_STATUS + */ +static QDF_STATUS hdd_component_init(void) +{ + QDF_STATUS status; + + /* initialize converged components */ + status = ucfg_mlme_global_init(); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = dispatcher_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto mlme_global_deinit; + + status = target_if_init(wma_get_psoc_from_scn_handle); + if (QDF_IS_STATUS_ERROR(status)) + goto dispatcher_deinit; + + /* initialize non-converged components */ + status = ucfg_mlme_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto target_if_deinit; + + status = ucfg_fwol_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto mlme_deinit; + + status = disa_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto fwol_deinit; + + status = pmo_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto disa_deinit; + + status = ucfg_ocb_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto pmo_deinit; + + status = ipa_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto ocb_deinit; + + status = ucfg_action_oui_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto ipa_deinit; + + status = nan_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto action_oui_deinit; + + status = ucfg_p2p_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto nan_deinit; + + status = ucfg_interop_issues_ap_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto p2p_deinit; + + status = policy_mgr_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto interop_issues_ap_deinit; + + status = ucfg_tdls_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto policy_deinit; + + status = ucfg_blm_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto tdls_deinit; + + status = ucfg_pkt_capture_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto blm_deinit; + + status = ucfg_ftm_timesync_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto pkt_capture_deinit; + + return QDF_STATUS_SUCCESS; + +pkt_capture_deinit: + ucfg_pkt_capture_deinit(); +blm_deinit: + ucfg_blm_deinit(); +tdls_deinit: + ucfg_tdls_deinit(); +policy_deinit: + policy_mgr_deinit(); +interop_issues_ap_deinit: + ucfg_interop_issues_ap_deinit(); +p2p_deinit: + ucfg_p2p_deinit(); +nan_deinit: + nan_deinit(); +action_oui_deinit: + ucfg_action_oui_deinit(); +ipa_deinit: + ipa_deinit(); +ocb_deinit: + ucfg_ocb_deinit(); +pmo_deinit: + pmo_deinit(); +disa_deinit: + disa_deinit(); +fwol_deinit: + ucfg_fwol_deinit(); +mlme_deinit: + ucfg_mlme_deinit(); +target_if_deinit: + target_if_deinit(); +dispatcher_deinit: + dispatcher_deinit(); +mlme_global_deinit: + ucfg_mlme_global_deinit(); + + return status; +} + +/** + * hdd_component_deinit() - Deinitialize all components + * + * Return: None + */ +static void hdd_component_deinit(void) +{ + /* deinitialize non-converged components */ + ucfg_ftm_timesync_deinit(); + ucfg_pkt_capture_deinit(); + ucfg_blm_deinit(); + ucfg_tdls_deinit(); + policy_mgr_deinit(); + ucfg_interop_issues_ap_deinit(); + ucfg_p2p_deinit(); + nan_deinit(); + ucfg_action_oui_deinit(); + ipa_deinit(); + ucfg_ocb_deinit(); + pmo_deinit(); + disa_deinit(); + ucfg_fwol_deinit(); + ucfg_mlme_deinit(); + + /* deinitialize converged components */ + target_if_deinit(); + dispatcher_deinit(); + ucfg_mlme_global_deinit(); +} + +QDF_STATUS hdd_component_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + + status = ucfg_mlme_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = ucfg_blm_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto err_blm; + + status = ucfg_fwol_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto err_fwol; + + status = ucfg_pmo_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto err_pmo; + + status = ucfg_policy_mgr_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto err_plcy_mgr; + + status = ucfg_p2p_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto err_p2p; + + status = ucfg_tdls_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto err_tdls; + + status = ucfg_nan_psoc_open(psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto err_nan; + + return status; + +err_nan: + ucfg_nan_psoc_close(psoc); +err_tdls: + ucfg_tdls_psoc_close(psoc); +err_p2p: + ucfg_p2p_psoc_close(psoc); +err_plcy_mgr: + ucfg_pmo_psoc_close(psoc); +err_pmo: + ucfg_fwol_psoc_close(psoc); +err_fwol: + ucfg_blm_psoc_close(psoc); +err_blm: + ucfg_mlme_psoc_close(psoc); + + return status; +} + +void hdd_component_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + ucfg_tdls_psoc_close(psoc); + ucfg_p2p_psoc_close(psoc); + ucfg_policy_mgr_psoc_close(psoc); + ucfg_pmo_psoc_close(psoc); + ucfg_fwol_psoc_close(psoc); + ucfg_blm_psoc_close(psoc); + ucfg_mlme_psoc_close(psoc); +} + +void hdd_component_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + ocb_psoc_enable(psoc); + disa_psoc_enable(psoc); + nan_psoc_enable(psoc); + p2p_psoc_enable(psoc); + ucfg_interop_issues_ap_psoc_enable(psoc); + policy_mgr_psoc_enable(psoc); + ucfg_tdls_psoc_enable(psoc); +} + +void hdd_component_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + ucfg_tdls_psoc_disable(psoc); + policy_mgr_psoc_disable(psoc); + ucfg_interop_issues_ap_psoc_disable(psoc); + p2p_psoc_disable(psoc); + nan_psoc_disable(psoc); + disa_psoc_disable(psoc); + ocb_psoc_disable(psoc); +} + +QDF_STATUS hdd_component_pdev_open(struct wlan_objmgr_pdev *pdev) +{ + return ucfg_mlme_pdev_open(pdev); +} + +void hdd_component_pdev_close(struct wlan_objmgr_pdev *pdev) +{ + ucfg_mlme_pdev_close(pdev); +} + +static QDF_STATUS hdd_qdf_print_init(void) +{ + QDF_STATUS status; + int qdf_print_idx; + + status = qdf_print_setup(); + if (QDF_IS_STATUS_ERROR(status)) { + pr_err("Failed qdf_print_setup; status:%u\n", status); + return status; + } + + qdf_print_idx = qdf_print_ctrl_register(cinfo, NULL, NULL, "MCL_WLAN"); + if (qdf_print_idx < 0) { + pr_err("Failed to register for qdf_print_ctrl\n"); + return QDF_STATUS_E_FAILURE; + } + + qdf_set_pidx(qdf_print_idx); + + return QDF_STATUS_SUCCESS; +} + +static void hdd_qdf_print_deinit(void) +{ + int qdf_pidx = qdf_get_pidx(); + + qdf_set_pidx(-1); + qdf_print_ctrl_cleanup(qdf_pidx); + + /* currently, no qdf print 'un-setup'*/ +} + +static QDF_STATUS hdd_qdf_init(void) +{ + QDF_STATUS status; + + status = hdd_qdf_print_init(); + if (QDF_IS_STATUS_ERROR(status)) + goto exit; + + status = qdf_debugfs_init(); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to init debugfs; status:%u", status); + goto print_deinit; + } + + qdf_lock_stats_init(); + qdf_mem_init(); + qdf_delayed_work_feature_init(); + qdf_periodic_work_feature_init(); + qdf_mc_timer_manager_init(); + qdf_event_list_init(); + + status = qdf_talloc_feature_init(); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to init talloc; status:%u", status); + goto event_deinit; + } + + status = qdf_cpuhp_init(); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to init cpuhp; status:%u", status); + goto talloc_deinit; + } + + status = qdf_trace_spin_lock_init(); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to init spinlock; status:%u", status); + goto cpuhp_deinit; + } + + qdf_trace_init(); + qdf_register_debugcb_init(); + + return QDF_STATUS_SUCCESS; + +cpuhp_deinit: + qdf_cpuhp_deinit(); +talloc_deinit: + qdf_talloc_feature_deinit(); +event_deinit: + qdf_event_list_destroy(); + qdf_mc_timer_manager_exit(); + qdf_periodic_work_feature_deinit(); + qdf_delayed_work_feature_deinit(); + qdf_mem_exit(); + qdf_lock_stats_deinit(); + qdf_debugfs_exit(); +print_deinit: + hdd_qdf_print_deinit(); + +exit: + return status; +} + +static void hdd_qdf_deinit(void) +{ + /* currently, no debugcb deinit */ + + qdf_trace_deinit(); + + /* currently, no trace spinlock deinit */ + + qdf_cpuhp_deinit(); + qdf_talloc_feature_deinit(); + qdf_event_list_destroy(); + qdf_mc_timer_manager_exit(); + qdf_periodic_work_feature_deinit(); + qdf_delayed_work_feature_deinit(); + qdf_mem_exit(); + qdf_lock_stats_deinit(); + qdf_debugfs_exit(); + hdd_qdf_print_deinit(); +} + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +static bool is_monitor_mode_supported(void) +{ + return true; +} +#else +static bool is_monitor_mode_supported(void) +{ + pr_err("Monitor mode not supported!"); + return false; +} +#endif + +#ifdef WLAN_FEATURE_EPPING +static bool is_epping_mode_supported(void) +{ + return true; +} +#else +static bool is_epping_mode_supported(void) +{ + pr_err("Epping mode not supported!"); + return false; +} +#endif + +#ifdef QCA_WIFI_FTM +static bool is_ftm_mode_supported(void) +{ + return true; +} +#else +static bool is_ftm_mode_supported(void) +{ + pr_err("FTM mode not supported!"); + return false; +} +#endif + +/** + * is_con_mode_valid() check con mode is valid or not + * @mode: global con mode + * + * Return: TRUE on success FALSE on failure + */ +static bool is_con_mode_valid(enum QDF_GLOBAL_MODE mode) +{ + switch (mode) { + case QDF_GLOBAL_MONITOR_MODE: + return is_monitor_mode_supported(); + case QDF_GLOBAL_EPPING_MODE: + return is_epping_mode_supported(); + case QDF_GLOBAL_FTM_MODE: + return is_ftm_mode_supported(); + case QDF_GLOBAL_MISSION_MODE: + return true; + default: + return false; + } +} + +static void hdd_stop_present_mode(struct hdd_context *hdd_ctx, + enum QDF_GLOBAL_MODE curr_mode) +{ + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) + return; + + switch (curr_mode) { + case QDF_GLOBAL_MONITOR_MODE: + hdd_info("Release wakelock for monitor mode!"); + qdf_wake_lock_release(&hdd_ctx->monitor_mode_wakelock, + WIFI_POWER_EVENT_WAKELOCK_MONITOR_MODE); + /* fallthrough */ + case QDF_GLOBAL_MISSION_MODE: + case QDF_GLOBAL_FTM_MODE: + hdd_abort_mac_scan_all_adapters(hdd_ctx); + wlan_cfg80211_cleanup_scan_queue(hdd_ctx->pdev, NULL); + hdd_stop_all_adapters(hdd_ctx); + hdd_deinit_all_adapters(hdd_ctx, false); + + break; + default: + break; + } +} + +static void hdd_cleanup_present_mode(struct hdd_context *hdd_ctx, + enum QDF_GLOBAL_MODE curr_mode) +{ + int driver_status; + + driver_status = hdd_ctx->driver_status; + + switch (curr_mode) { + case QDF_GLOBAL_MISSION_MODE: + case QDF_GLOBAL_MONITOR_MODE: + case QDF_GLOBAL_FTM_MODE: + hdd_close_all_adapters(hdd_ctx, false); + break; + case QDF_GLOBAL_EPPING_MODE: + epping_disable(); + epping_close(); + break; + default: + return; + } +} + +static int +hdd_parse_driver_mode(const char *mode_str, enum QDF_GLOBAL_MODE *out_mode) +{ + QDF_STATUS status; + uint32_t mode; + + *out_mode = QDF_GLOBAL_MAX_MODE; + + status = qdf_uint32_parse(mode_str, &mode); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + if (mode >= QDF_GLOBAL_MAX_MODE) + return -ERANGE; + + *out_mode = (enum QDF_GLOBAL_MODE)mode; + + return 0; +} + +static int hdd_mode_change_psoc_idle_shutdown(struct device *dev) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) + return -EINVAL; + + return hdd_wlan_stop_modules(hdd_ctx, true); +} + +static int hdd_mode_change_psoc_idle_restart(struct device *dev) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + int ret; + + if (!hdd_ctx) + return -EINVAL; + ret = hdd_soc_idle_restart_lock(dev); + if (ret) + return ret; + ret = hdd_wlan_start_modules(hdd_ctx, false); + hdd_soc_idle_restart_unlock(); + + return ret; +} + +/** + * __hdd_driver_mode_change() - Handles a driver mode change + * @hdd_ctx: Pointer to the global HDD context + * @next_mode: the driver mode to transition to + * + * This function is invoked when user updates con_mode using sys entry, + * to initialize and bring-up driver in that specific mode. + * + * Return: Errno + */ +static int __hdd_driver_mode_change(struct hdd_context *hdd_ctx, + enum QDF_GLOBAL_MODE next_mode) +{ + enum QDF_GLOBAL_MODE curr_mode; + int errno; + + hdd_info("Driver mode changing to %d", next_mode); + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (!is_con_mode_valid(next_mode)) { + hdd_err_rl("Requested driver mode is invalid"); + return -EINVAL; + } + + curr_mode = hdd_get_conparam(); + if (curr_mode == next_mode) { + hdd_err_rl("Driver is already in the requested mode"); + return 0; + } + + hdd_psoc_idle_timer_stop(hdd_ctx); + + /* ensure adapters are stopped */ + hdd_stop_present_mode(hdd_ctx, curr_mode); + + if (DRIVER_MODULES_CLOSED != hdd_ctx->driver_status) { + is_mode_change_psoc_idle_shutdown = true; + errno = pld_idle_shutdown(hdd_ctx->parent_dev, + hdd_mode_change_psoc_idle_shutdown); + if (errno) { + is_mode_change_psoc_idle_shutdown = false; + hdd_err("Stop wlan modules failed"); + return errno; + } + } + + /* Cleanup present mode before switching to new mode */ + hdd_cleanup_present_mode(hdd_ctx, curr_mode); + + hdd_set_conparam(next_mode); + + errno = pld_idle_restart(hdd_ctx->parent_dev, + hdd_mode_change_psoc_idle_restart); + if (errno) { + hdd_err("Start wlan modules failed: %d", errno); + return errno; + } + + errno = hdd_open_adapters_for_mode(hdd_ctx, next_mode); + if (errno) { + hdd_err("Failed to open adapters"); + return errno; + } + + if (next_mode == QDF_GLOBAL_MONITOR_MODE) { + struct hdd_adapter *adapter = + hdd_get_adapter(hdd_ctx, QDF_MONITOR_MODE); + + QDF_BUG(adapter); + if (!adapter) { + hdd_err("Failed to get monitor adapter"); + return -EINVAL; + } + + errno = hdd_start_adapter(adapter); + if (errno) { + hdd_err("Failed to start monitor adapter"); + return errno; + } + + hdd_info("Acquire wakelock for monitor mode"); + qdf_wake_lock_acquire(&hdd_ctx->monitor_mode_wakelock, + WIFI_POWER_EVENT_WAKELOCK_MONITOR_MODE); + } + + /* con_mode is a global module parameter */ + con_mode = next_mode; + hdd_info("Driver mode successfully changed to %d", next_mode); + + if (con_mode == QDF_GLOBAL_FTM_MODE) + pld_request_bus_bandwidth(hdd_ctx->parent_dev, + PLD_BUS_WIDTH_VERY_HIGH); + else if (con_mode == QDF_GLOBAL_MISSION_MODE) + pld_request_bus_bandwidth(hdd_ctx->parent_dev, + PLD_BUS_WIDTH_NONE); + + return 0; +} + +static int hdd_driver_mode_change(enum QDF_GLOBAL_MODE mode) +{ + struct osif_driver_sync *driver_sync; + struct hdd_context *hdd_ctx; + QDF_STATUS status; + int errno; + + hdd_enter(); + + status = osif_driver_sync_trans_start_wait(&driver_sync); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to start 'mode change'; status:%u", status); + errno = qdf_status_to_os_return(status); + goto exit; + } + + osif_driver_sync_wait_for_ops(driver_sync); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + goto trans_stop; + + errno = __hdd_driver_mode_change(hdd_ctx, mode); + +trans_stop: + osif_driver_sync_trans_stop(driver_sync); + +exit: + hdd_exit(); + + return errno; +} + +static int hdd_set_con_mode(enum QDF_GLOBAL_MODE mode) +{ + con_mode = mode; + + return 0; +} + +static int (*hdd_set_con_mode_cb)(enum QDF_GLOBAL_MODE mode) = hdd_set_con_mode; + +static void hdd_driver_mode_change_register(void) +{ + hdd_set_con_mode_cb = hdd_driver_mode_change; +} + +static void hdd_driver_mode_change_unregister(void) +{ + hdd_set_con_mode_cb = hdd_set_con_mode; +} + +static int con_mode_handler(const char *kmessage, const struct kernel_param *kp) +{ + enum QDF_GLOBAL_MODE mode; + int errno; + + errno = hdd_parse_driver_mode(kmessage, &mode); + if (errno) { + hdd_err_rl("Failed to parse driver mode '%s'", kmessage); + return errno; + } + + return hdd_set_con_mode_cb(mode); +} + +/** + * hdd_driver_load() - Perform the driver-level load operation + * + * Note: this is used in both static and DLKM driver builds + * + * Return: Errno + */ +static int hdd_driver_load(void) +{ + struct osif_driver_sync *driver_sync; + QDF_STATUS status; + int errno; + + pr_err("%s: Loading driver v%s\n", WLAN_MODULE_NAME, + g_wlan_driver_version); + + status = hdd_qdf_init(); + if (QDF_IS_STATUS_ERROR(status)) { + errno = qdf_status_to_os_return(status); + goto exit; + } + + osif_sync_init(); + + status = osif_driver_sync_create_and_trans(&driver_sync); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to init driver sync; status:%u", status); + errno = qdf_status_to_os_return(status); + goto sync_deinit; + } + + errno = hdd_init(); + if (errno) { + hdd_err("Failed to init HDD; errno:%d", errno); + goto trans_stop; + } + + status = hdd_component_init(); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to init components; status:%u", status); + errno = qdf_status_to_os_return(status); + goto hdd_deinit; + } + + status = qdf_wake_lock_create(&wlan_wake_lock, "wlan"); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to create wake lock; status:%u", status); + errno = qdf_status_to_os_return(status); + goto comp_deinit; + } + + hdd_set_conparam(con_mode); + + errno = wlan_hdd_state_ctrl_param_create(); + if (errno) { + hdd_err("Failed to create ctrl param; errno:%d", errno); + goto wakelock_destroy; + } + + errno = pld_init(); + if (errno) { + hdd_err("Failed to init PLD; errno:%d", errno); + goto param_destroy; + } + + hdd_driver_mode_change_register(); + + osif_driver_sync_register(driver_sync); + osif_driver_sync_trans_stop(driver_sync); + + /* psoc probe can happen in registration; do after 'load' transition */ + errno = wlan_hdd_register_driver(); + if (errno) { + hdd_err("Failed to register driver; errno:%d", errno); + goto pld_deinit; + } + + hdd_debug("%s: driver loaded", WLAN_MODULE_NAME); + + return 0; + +pld_deinit: + status = osif_driver_sync_trans_start(&driver_sync); + QDF_BUG(QDF_IS_STATUS_SUCCESS(status)); + + osif_driver_sync_unregister(); + osif_driver_sync_wait_for_ops(driver_sync); + + hdd_driver_mode_change_unregister(); + pld_deinit(); + + hdd_start_complete(errno); + /* Wait for any ref taken on /dev/wlan to be released */ + while (qdf_atomic_read(&wlan_hdd_state_fops_ref)) + ; +param_destroy: + wlan_hdd_state_ctrl_param_destroy(); +wakelock_destroy: + qdf_wake_lock_destroy(&wlan_wake_lock); +comp_deinit: + hdd_component_deinit(); +hdd_deinit: + hdd_deinit(); +trans_stop: + osif_driver_sync_trans_stop(driver_sync); + osif_driver_sync_destroy(driver_sync); +sync_deinit: + osif_sync_deinit(); + hdd_qdf_deinit(); + +exit: + return errno; +} + +#ifdef MODULE +/** + * hdd_driver_unload() - Performs the driver-level unload operation + * + * Note: this is used in both static and DLKM driver builds + * + * Return: None + */ +static void hdd_driver_unload(void) +{ + struct osif_driver_sync *driver_sync; + struct hdd_context *hdd_ctx; + QDF_STATUS status; + void *hif_ctx; + + pr_info("%s: Unloading driver v%s\n", WLAN_MODULE_NAME, + QWLAN_VERSIONSTR); + + /* + * Wait for any trans to complete and then start the driver trans + * for the unload. This will ensure that the driver trans proceeds only + * after all trans have been completed. As a part of this trans, set + * the driver load/unload flag to further ensure that any upcoming + * trans are rejected via wlan_hdd_validate_context. + */ + status = osif_driver_sync_trans_start_wait(&driver_sync); + QDF_BUG(QDF_IS_STATUS_SUCCESS(status)); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to unload wlan; status:%u", status); + return; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (hif_ctx) { + /* + * Trigger runtime sync resume before setting unload in progress + * such that resume can happen successfully + */ + hif_pm_runtime_sync_resume(hif_ctx); + } + + cds_set_driver_loaded(false); + cds_set_unload_in_progress(true); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (hdd_ctx) { + hdd_psoc_idle_timer_stop(hdd_ctx); + /* + * Runtime PM sync resume may have started the bus bandwidth + * periodic work hence stop it. + */ + hdd_bus_bw_compute_timer_stop(hdd_ctx); + } + + /* + * Stop the trans before calling unregister_driver as that involves a + * call to pld_remove which in itself is a psoc transaction + */ + osif_driver_sync_trans_stop(driver_sync); + + /* trigger SoC remove */ + wlan_hdd_unregister_driver(); + + status = osif_driver_sync_trans_start_wait(&driver_sync); + QDF_BUG(QDF_IS_STATUS_SUCCESS(status)); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to unload wlan; status:%u", status); + return; + } + + osif_driver_sync_unregister(); + osif_driver_sync_wait_for_ops(driver_sync); + + hdd_driver_mode_change_unregister(); + pld_deinit(); + wlan_hdd_state_ctrl_param_destroy(); + hdd_set_conparam(0); + qdf_wake_lock_destroy(&wlan_wake_lock); + hdd_component_deinit(); + hdd_deinit(); + + osif_driver_sync_trans_stop(driver_sync); + osif_driver_sync_destroy(driver_sync); + + osif_sync_deinit(); + + hdd_qdf_deinit(); +} + +/** + * hdd_module_init() - Module init helper + * + * Module init helper function used by both module and static driver. + * + * Return: 0 for success, errno on failure + */ +static int hdd_module_init(void) +{ + if (hdd_driver_load()) + return -EINVAL; + + return 0; +} +/** + * hdd_module_exit() - Exit function + * + * This is the driver exit point (invoked when module is unloaded using rmmod) + * + * Return: None + */ +static void __exit hdd_module_exit(void) +{ + hdd_driver_unload(); +} +#else +static void wlan_hdd_boot_fn(struct work_struct *work) +{ + hdd_driver_load(); +} + +static int __init hdd_module_init(void) +{ + INIT_WORK(&boot_work, wlan_hdd_boot_fn); + schedule_work(&boot_work); + + return 0; +} +#endif + +static int fwpath_changed_handler(const char *kmessage, + const struct kernel_param *kp) +{ + return param_set_copystring(kmessage, kp); +} + +static int con_mode_handler_ftm(const char *kmessage, + const struct kernel_param *kp) +{ + int ret; + + ret = param_set_int(kmessage, kp); + + if (con_mode_ftm != QDF_GLOBAL_FTM_MODE) { + pr_err("Only FTM mode supported!"); + return -ENOTSUPP; + } + + hdd_set_conparam(con_mode_ftm); + con_mode = con_mode_ftm; + + return ret; +} + +#ifdef WLAN_FEATURE_EPPING +static int con_mode_handler_epping(const char *kmessage, + const struct kernel_param *kp) +{ + int ret; + + ret = param_set_int(kmessage, kp); + + if (con_mode_epping != QDF_GLOBAL_EPPING_MODE) { + pr_err("Only EPPING mode supported!"); + return -ENOTSUPP; + } + + hdd_set_conparam(con_mode_epping); + con_mode = con_mode_epping; + + return ret; +} +#endif + +/** + * hdd_get_conparam() - driver exit point + * + * This is the driver exit point (invoked when module is unloaded using rmmod) + * + * Return: enum QDF_GLOBAL_MODE + */ +enum QDF_GLOBAL_MODE hdd_get_conparam(void) +{ + return (enum QDF_GLOBAL_MODE) curr_con_mode; +} + +void hdd_set_conparam(int32_t con_param) +{ + curr_con_mode = con_param; +} + +/** + * hdd_clean_up_pre_cac_interface() - Clean up the pre cac interface + * @hdd_ctx: HDD context + * + * Cleans up the pre cac interface, if it exists + * + * Return: None + */ +void hdd_clean_up_pre_cac_interface(struct hdd_context *hdd_ctx) +{ + uint8_t vdev_id; + QDF_STATUS status; + struct hdd_adapter *precac_adapter; + + status = wlan_sap_get_pre_cac_vdev_id(hdd_ctx->mac_handle, &vdev_id); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed to get pre cac vdev id"); + return; + } + + precac_adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!precac_adapter) { + hdd_err("invalid pre cac adapter"); + return; + } + + qdf_create_work(0, &hdd_ctx->sap_pre_cac_work, + wlan_hdd_sap_pre_cac_failure, + (void *)precac_adapter); + qdf_sched_work(0, &hdd_ctx->sap_pre_cac_work); + +} + +/** + * hdd_svc_fw_crashed_ind() - API to send FW CRASHED IND to Userspace + * + * Return: void + */ +static void hdd_svc_fw_crashed_ind(void) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + hdd_ctx ? wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_FW_CRASHED_IND, + NULL, 0) : 0; +} + +/** + * hdd_update_ol_config - API to update ol configuration parameters + * @hdd_ctx: HDD context + * + * Return: void + */ +static void hdd_update_ol_config(struct hdd_context *hdd_ctx) +{ + struct ol_config_info cfg = {0}; + struct ol_context *ol_ctx = cds_get_context(QDF_MODULE_ID_BMI); + bool self_recovery = false; + QDF_STATUS status; + + if (!ol_ctx) + return; + + status = ucfg_mlme_get_self_recovery(hdd_ctx->psoc, &self_recovery); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to get self recovery ini config"); + + cfg.enable_self_recovery = self_recovery; + cfg.enable_uart_print = hdd_ctx->config->enablefwprint; + cfg.enable_fw_log = hdd_ctx->config->enable_fw_log; + cfg.enable_ramdump_collection = hdd_ctx->config->is_ramdump_enabled; + cfg.enable_lpass_support = hdd_lpass_is_supported(hdd_ctx); + + ol_init_ini_config(ol_ctx, &cfg); + ol_set_fw_crashed_cb(ol_ctx, hdd_svc_fw_crashed_ind); +} + +#ifdef FEATURE_RUNTIME_PM +/** + * hdd_populate_runtime_cfg() - populate runtime configuration + * @hdd_ctx: hdd context + * @cfg: pointer to the configuration memory being populated + * + * Return: void + */ +static void hdd_populate_runtime_cfg(struct hdd_context *hdd_ctx, + struct hif_config_info *cfg) +{ + cfg->enable_runtime_pm = hdd_ctx->config->runtime_pm; + cfg->runtime_pm_delay = + ucfg_pmo_get_runtime_pm_delay(hdd_ctx->psoc); +} +#else +static void hdd_populate_runtime_cfg(struct hdd_context *hdd_ctx, + struct hif_config_info *cfg) +{ +} +#endif + +/** + * hdd_update_hif_config - API to update HIF configuration parameters + * @hdd_ctx: HDD Context + * + * Return: void + */ +static void hdd_update_hif_config(struct hdd_context *hdd_ctx) +{ + struct hif_opaque_softc *scn = cds_get_context(QDF_MODULE_ID_HIF); + struct hif_config_info cfg = {0}; + bool prevent_link_down = false; + bool self_recovery = false; + QDF_STATUS status; + + if (!scn) + return; + + status = ucfg_mlme_get_prevent_link_down(hdd_ctx->psoc, + &prevent_link_down); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to get prevent_link_down config"); + + status = ucfg_mlme_get_self_recovery(hdd_ctx->psoc, &self_recovery); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to get self recovery ini config"); + + cfg.enable_self_recovery = self_recovery; + hdd_populate_runtime_cfg(hdd_ctx, &cfg); + cfg.rx_softirq_max_yield_duration_ns = + cfg_get(hdd_ctx->psoc, + CFG_DP_RX_SOFTIRQ_MAX_YIELD_TIME_NS); + + hif_init_ini_config(scn, &cfg); + + if (prevent_link_down) + hif_vote_link_up(scn); +} + +#ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT +/** + * hdd_update_dp_config_rx_softirq_limits() - Update DP rx softirq limit config + * datapath + * @hdd_ctx: HDD Context + * @params: pointer to cdp_config_params to be updated + * + * Void + */ +static +void hdd_update_dp_config_rx_softirq_limits(struct hdd_context *hdd_ctx, + struct cdp_config_params *params) +{ + params->tx_comp_loop_pkt_limit = cfg_get(hdd_ctx->psoc, + CFG_DP_TX_COMP_LOOP_PKT_LIMIT); + params->rx_reap_loop_pkt_limit = cfg_get(hdd_ctx->psoc, + CFG_DP_RX_REAP_LOOP_PKT_LIMIT); + params->rx_hp_oos_update_limit = cfg_get(hdd_ctx->psoc, + CFG_DP_RX_HP_OOS_UPDATE_LIMIT); +} +#else +static +void hdd_update_dp_config_rx_softirq_limits(struct hdd_context *hdd_ctx, + struct cdp_config_params *params) +{ +} +#endif /* WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT */ + +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +static void +hdd_update_dp_config_queue_threshold(struct hdd_context *hdd_ctx, + struct cdp_config_params *params) +{ + params->tx_flow_stop_queue_threshold = + cfg_get(hdd_ctx->psoc, CFG_DP_TX_FLOW_STOP_QUEUE_TH); + params->tx_flow_start_queue_offset = + cfg_get(hdd_ctx->psoc, + CFG_DP_TX_FLOW_START_QUEUE_OFFSET); +} +#else +static inline void +hdd_update_dp_config_queue_threshold(struct hdd_context *hdd_ctx, + struct cdp_config_params *params) +{ +} +#endif + +/** + * hdd_update_dp_config() - Propagate config parameters to Lithium + * datapath + * @hdd_ctx: HDD Context + * + * Return: 0 for success/errno for failure + */ +static int hdd_update_dp_config(struct hdd_context *hdd_ctx) +{ + struct cdp_config_params params = {0}; + QDF_STATUS status; + void *soc; + + soc = cds_get_context(QDF_MODULE_ID_SOC); + params.tso_enable = cfg_get(hdd_ctx->psoc, CFG_DP_TSO); + params.lro_enable = cfg_get(hdd_ctx->psoc, CFG_DP_LRO); + hdd_update_dp_config_queue_threshold(hdd_ctx, ¶ms); + params.flow_steering_enable = + cfg_get(hdd_ctx->psoc, CFG_DP_FLOW_STEERING_ENABLED); + params.napi_enable = hdd_ctx->napi_enable; + params.p2p_tcp_udp_checksumoffload = + cfg_get(hdd_ctx->psoc, + CFG_DP_P2P_TCP_UDP_CKSUM_OFFLOAD); + params.nan_tcp_udp_checksumoffload = + cfg_get(hdd_ctx->psoc, + CFG_DP_NAN_TCP_UDP_CKSUM_OFFLOAD); + params.tcp_udp_checksumoffload = + cfg_get(hdd_ctx->psoc, + CFG_DP_TCP_UDP_CKSUM_OFFLOAD); + params.ipa_enable = ucfg_ipa_is_enabled(); + params.gro_enable = cfg_get(hdd_ctx->psoc, CFG_DP_GRO); + params.tx_comp_loop_pkt_limit = cfg_get(hdd_ctx->psoc, + CFG_DP_TX_COMP_LOOP_PKT_LIMIT); + params.rx_reap_loop_pkt_limit = cfg_get(hdd_ctx->psoc, + CFG_DP_RX_REAP_LOOP_PKT_LIMIT); + params.rx_hp_oos_update_limit = cfg_get(hdd_ctx->psoc, + CFG_DP_RX_HP_OOS_UPDATE_LIMIT); + hdd_update_dp_config_rx_softirq_limits(hdd_ctx, ¶ms); + + status = cdp_update_config_parameters(soc, ¶ms); + if (status) { + hdd_err("Failed to attach config parameters"); + return status; + } + + return 0; +} + +/** + * hdd_update_config() - Initialize driver per module ini parameters + * @hdd_ctx: HDD Context + * + * API is used to initialize all driver per module configuration parameters + * Return: 0 for success, errno for failure + */ +int hdd_update_config(struct hdd_context *hdd_ctx) +{ + int ret; + + if (ucfg_pmo_is_ns_offloaded(hdd_ctx->psoc)) + hdd_ctx->ns_offload_enable = true; + + hdd_update_ol_config(hdd_ctx); + hdd_update_hif_config(hdd_ctx); + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) + ret = hdd_update_cds_config_ftm(hdd_ctx); + else + ret = hdd_update_cds_config(hdd_ctx); + ret = hdd_update_user_config(hdd_ctx); + + hdd_update_regdb_offload_config(hdd_ctx); + + return ret; +} + +/** + * hdd_update_pmo_config - API to update pmo configuration parameters + * @hdd_ctx: HDD context + * + * Return: void + */ +static int hdd_update_pmo_config(struct hdd_context *hdd_ctx) +{ + struct pmo_psoc_cfg psoc_cfg = {0}; + QDF_STATUS status; + enum pmo_wow_enable_type wow_enable; + + ucfg_pmo_get_psoc_config(hdd_ctx->psoc, &psoc_cfg); + + /* + * Value of hdd_ctx->wowEnable can be, + * 0 - Disable both magic pattern match and pattern byte match. + * 1 - Enable magic pattern match on all interfaces. + * 2 - Enable pattern byte match on all interfaces. + * 3 - Enable both magic patter and pattern byte match on + * all interfaces. + */ + wow_enable = ucfg_pmo_get_wow_enable(hdd_ctx->psoc); + psoc_cfg.magic_ptrn_enable = (wow_enable & 0x01) ? true : false; + psoc_cfg.ptrn_match_enable_all_vdev = + (wow_enable & 0x02) ? true : false; + psoc_cfg.ap_arpns_support = hdd_ctx->ap_arpns_support; + psoc_cfg.d0_wow_supported = wma_d0_wow_is_supported(); + ucfg_mlme_get_sap_max_modulated_dtim(hdd_ctx->psoc, + &psoc_cfg.sta_max_li_mod_dtim); + + + hdd_lpass_populate_pmo_config(&psoc_cfg, hdd_ctx); + + status = ucfg_pmo_update_psoc_config(hdd_ctx->psoc, &psoc_cfg); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("failed pmo psoc configuration; status:%d", status); + + return qdf_status_to_os_return(status); +} + +void hdd_update_ie_whitelist_attr(struct probe_req_whitelist_attr *ie_whitelist, + struct hdd_context *hdd_ctx) +{ + struct wlan_fwol_ie_whitelist whitelist = {0}; + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + QDF_STATUS status; + bool is_ie_whitelist_enable = false; + uint8_t i = 0; + + status = ucfg_fwol_get_ie_whitelist(psoc, &is_ie_whitelist_enable); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to get IE whitelist param"); + return; + } + + ie_whitelist->white_list = is_ie_whitelist_enable; + if (!ie_whitelist->white_list) + return; + + status = ucfg_fwol_get_all_whitelist_params(psoc, &whitelist); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to get all whitelist params"); + return; + } + + ie_whitelist->ie_bitmap[0] = whitelist.ie_bitmap_0; + ie_whitelist->ie_bitmap[1] = whitelist.ie_bitmap_1; + ie_whitelist->ie_bitmap[2] = whitelist.ie_bitmap_2; + ie_whitelist->ie_bitmap[3] = whitelist.ie_bitmap_3; + ie_whitelist->ie_bitmap[4] = whitelist.ie_bitmap_4; + ie_whitelist->ie_bitmap[5] = whitelist.ie_bitmap_5; + ie_whitelist->ie_bitmap[6] = whitelist.ie_bitmap_6; + ie_whitelist->ie_bitmap[7] = whitelist.ie_bitmap_7; + + ie_whitelist->num_vendor_oui = whitelist.no_of_probe_req_ouis; + for (i = 0; i < ie_whitelist->num_vendor_oui; i++) + ie_whitelist->voui[i] = whitelist.probe_req_voui[i]; +} + +uint32_t hdd_limit_max_per_index_score(uint32_t per_index_score) +{ + uint8_t i, score; + + for (i = 0; i < MAX_INDEX_PER_INI; i++) { + score = WLAN_GET_SCORE_PERCENTAGE(per_index_score, i); + if (score > MAX_INDEX_SCORE) + WLAN_SET_SCORE_PERCENTAGE(per_index_score, + MAX_INDEX_SCORE, i); + } + + return per_index_score; +} + +QDF_STATUS hdd_update_score_config( + struct scoring_config *score_config, struct hdd_context *hdd_ctx) +{ + struct hdd_config *cfg = hdd_ctx->config; + QDF_STATUS status; + struct wlan_mlme_nss_chains vdev_ini_cfg; + bool bval = false; + uint32_t channel_bonding_mode; + + qdf_mem_zero(&vdev_ini_cfg, sizeof(struct wlan_mlme_nss_chains)); + /* Populate the nss chain params from ini for this vdev type */ + sme_populate_nss_chain_params(hdd_ctx->mac_handle, &vdev_ini_cfg, + QDF_STA_MODE, + hdd_ctx->num_rf_chains); + + score_config->vdev_nss_24g = vdev_ini_cfg.rx_nss[NSS_CHAINS_BAND_2GHZ]; + score_config->vdev_nss_5g = vdev_ini_cfg.rx_nss[NSS_CHAINS_BAND_5GHZ]; + + sme_update_score_config(hdd_ctx->mac_handle, score_config); + + ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, + &channel_bonding_mode); + score_config->cb_mode_24G = channel_bonding_mode; + ucfg_mlme_get_channel_bonding_5ghz(hdd_ctx->psoc, + &channel_bonding_mode); + score_config->cb_mode_5G = channel_bonding_mode; + + if (cfg->dot11Mode == eHDD_DOT11_MODE_AUTO || + cfg->dot11Mode == eHDD_DOT11_MODE_11ax || + cfg->dot11Mode == eHDD_DOT11_MODE_11ax_ONLY) + score_config->he_cap = 1; + + if (score_config->he_cap || + cfg->dot11Mode == eHDD_DOT11_MODE_11ac || + cfg->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY) + score_config->vht_cap = 1; + + if (score_config->vht_cap || cfg->dot11Mode == eHDD_DOT11_MODE_11n || + cfg->dot11Mode == eHDD_DOT11_MODE_11n_ONLY) + score_config->ht_cap = 1; + + status = ucfg_mlme_get_vht_for_24ghz(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to get vht_for_24ghz"); + if (score_config->vht_cap && bval) + score_config->vht_24G_cap = 1; + + status = ucfg_mlme_get_vht_enable_tx_bf(hdd_ctx->psoc, + &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable_tx_bf"); + + if (bval) + score_config->beamformee_cap = 1; + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_update_dfs_config() - API to update dfs configuration parameters. + * @hdd_ctx: HDD context + * + * Return: 0 if success else err + */ +static int hdd_update_dfs_config(struct hdd_context *hdd_ctx) +{ + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + struct dfs_user_config dfs_cfg = {0}; + QDF_STATUS status; + + ucfg_mlme_get_dfs_filter_offload(hdd_ctx->psoc, + &dfs_cfg.dfs_is_phyerr_filter_offload); + status = ucfg_dfs_update_config(psoc, &dfs_cfg); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed dfs psoc configuration"); + return -EINVAL; + } + + return 0; +} + +/** + * hdd_update_scan_config - API to update scan configuration parameters + * @hdd_ctx: HDD context + * + * Return: 0 if success else err + */ +static int hdd_update_scan_config(struct hdd_context *hdd_ctx) +{ + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + struct scan_user_cfg scan_cfg; + QDF_STATUS status; + uint32_t mcast_mcc_rest_time = 0; + + qdf_mem_zero(&scan_cfg, sizeof(scan_cfg)); + status = ucfg_mlme_get_sta_miracast_mcc_rest_time(hdd_ctx->psoc, + &mcast_mcc_rest_time); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("ucfg_mlme_get_sta_miracast_mcc_rest_time, use def"); + return -EIO; + } + scan_cfg.sta_miracast_mcc_rest_time = mcast_mcc_rest_time; + hdd_update_ie_whitelist_attr(&scan_cfg.ie_whitelist, hdd_ctx); + + status = hdd_update_score_config(&scan_cfg.score_config, hdd_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to update scoring config"); + return -EINVAL; + } + + status = ucfg_scan_update_user_config(psoc, &scan_cfg); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("failed pmo psoc configuration"); + return -EINVAL; + } + + return 0; +} + +int hdd_update_components_config(struct hdd_context *hdd_ctx) +{ + int ret; + + ret = hdd_update_pmo_config(hdd_ctx); + if (ret) + return ret; + + ret = hdd_update_scan_config(hdd_ctx); + if (ret) + return ret; + + ret = hdd_update_tdls_config(hdd_ctx); + if (ret) + return ret; + + ret = hdd_update_dp_config(hdd_ctx); + if (ret) + return ret; + + ret = hdd_update_dfs_config(hdd_ctx); + if (ret) + return ret; + + ret = hdd_update_regulatory_config(hdd_ctx); + + return ret; +} + +/** + * wlan_hdd_get_dfs_mode() - get ACS DFS mode + * @mode : cfg80211 DFS mode + * + * Return: return SAP ACS DFS mode else return ACS_DFS_MODE_NONE + */ +enum sap_acs_dfs_mode wlan_hdd_get_dfs_mode(enum dfs_mode mode) +{ + switch (mode) { + case DFS_MODE_ENABLE: + return ACS_DFS_MODE_ENABLE; + case DFS_MODE_DISABLE: + return ACS_DFS_MODE_DISABLE; + case DFS_MODE_DEPRIORITIZE: + return ACS_DFS_MODE_DEPRIORITIZE; + default: + hdd_debug("ACS dfs mode is NONE"); + return ACS_DFS_MODE_NONE; + } +} + +/** + * hdd_enable_disable_ca_event() - enable/disable channel avoidance event + * @hddctx: pointer to hdd context + * @set_value: enable/disable + * + * When Host sends vendor command enable, FW will send *ONE* CA ind to + * Host(even though it is duplicate). When Host send vendor command + * disable,FW doesn't perform any action. Whenever any change in + * CA *and* WLAN is in SAP/P2P-GO mode, FW sends CA ind to host. + * + * return - 0 on success, appropriate error values on failure. + */ +int hdd_enable_disable_ca_event(struct hdd_context *hdd_ctx, uint8_t set_value) +{ + QDF_STATUS status; + + if (0 != wlan_hdd_validate_context(hdd_ctx)) + return -EAGAIN; + + status = sme_enable_disable_chanavoidind_event(hdd_ctx->mac_handle, + set_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to send chan avoid command to SME"); + return -EINVAL; + } + return 0; +} + +/** + * hdd_set_roaming_in_progress() - to set the roaming in progress flag + * @value: value to set + * + * This function will set the passed value to roaming in progress flag. + * + * Return: None + */ +void hdd_set_roaming_in_progress(bool value) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return; + } + + hdd_ctx->roaming_in_progress = value; + hdd_debug("Roaming in Progress set to %d", value); + if (!hdd_ctx->roaming_in_progress) { + /* Reset scan reject params on successful roam complete */ + hdd_debug("Reset scan reject params"); + hdd_init_scan_reject_params(hdd_ctx); + } +} + +bool hdd_is_roaming_in_progress(struct hdd_context *hdd_ctx) +{ + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return false; + } + + if (hdd_ctx->roaming_in_progress) + hdd_debug("Roaming is in progress"); + + return hdd_ctx->roaming_in_progress; +} + +/** + * struct hdd_is_connection_in_progress_priv - adapter connection info + * @out_vdev_id: id of vdev where connection is occurring + * @out_reason: scan reject reason + */ +struct hdd_is_connection_in_progress_priv { + uint8_t out_vdev_id; + enum scan_reject_states out_reason; + bool connection_in_progress; +}; + +/** + * hdd_is_connection_in_progress_iterator() - Check adapter connection based + * on device mode + * @adapter: current adapter of interest + * @context: user context supplied + * + * Check if connection is in progress for the current adapter according to the + * device mode + * + * Return: + * * QDF_STATUS_SUCCESS if iteration should continue + * * QDF_STATUS_E_ABORTED if iteration should be aborted + */ +static QDF_STATUS hdd_is_connection_in_progress_iterator( + struct hdd_adapter *adapter, + void *ctx) +{ + struct hdd_station_ctx *hdd_sta_ctx; + uint8_t *sta_mac; + struct hdd_context *hdd_ctx; + mac_handle_t mac_handle; + struct hdd_station_info *sta_info; + struct hdd_is_connection_in_progress_priv *context = ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return QDF_STATUS_E_ABORTED; + } + + mac_handle = hdd_ctx->mac_handle; + + if (((QDF_STA_MODE == adapter->device_mode) + || (QDF_P2P_CLIENT_MODE == adapter->device_mode) + || (QDF_P2P_DEVICE_MODE == adapter->device_mode)) + && (eConnectionState_Connecting == + (WLAN_HDD_GET_STATION_CTX_PTR(adapter))-> + conn_info.conn_state)) { + hdd_debug("%pK(%d) mode %d Connection is in progress", + WLAN_HDD_GET_STATION_CTX_PTR(adapter), + adapter->vdev_id, adapter->device_mode); + + context->out_vdev_id = adapter->vdev_id; + context->out_reason = CONNECTION_IN_PROGRESS; + context->connection_in_progress = true; + + return QDF_STATUS_E_ABORTED; + } + /* + * sme_neighbor_middle_of_roaming is for LFR2 + * hdd_is_roaming_in_progress is for LFR3 + */ + if (((QDF_STA_MODE == adapter->device_mode) && + sme_neighbor_middle_of_roaming( + mac_handle, + adapter->vdev_id)) || + hdd_is_roaming_in_progress(hdd_ctx)) { + hdd_debug("%pK(%d) mode %d Reassociation in progress", + WLAN_HDD_GET_STATION_CTX_PTR(adapter), + adapter->vdev_id, adapter->device_mode); + + context->out_vdev_id = adapter->vdev_id; + context->out_reason = REASSOC_IN_PROGRESS; + context->connection_in_progress = true; + return QDF_STATUS_E_ABORTED; + } + + if ((QDF_STA_MODE == adapter->device_mode) || + (QDF_P2P_CLIENT_MODE == adapter->device_mode) || + (QDF_P2P_DEVICE_MODE == adapter->device_mode)) { + hdd_sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if ((eConnectionState_Associated == + hdd_sta_ctx->conn_info.conn_state) + && sme_is_sta_key_exchange_in_progress( + mac_handle, adapter->vdev_id)) { + sta_mac = (uint8_t *)&(adapter->mac_addr.bytes[0]); + hdd_debug("client " QDF_MAC_ADDR_FMT + " is in middle of WPS/EAPOL exchange.", + QDF_MAC_ADDR_REF(sta_mac)); + + context->out_vdev_id = adapter->vdev_id; + context->out_reason = EAPOL_IN_PROGRESS; + context->connection_in_progress = true; + + return QDF_STATUS_E_ABORTED; + } + } else if ((QDF_SAP_MODE == adapter->device_mode) || + (QDF_P2P_GO_MODE == adapter->device_mode)) { + hdd_for_each_sta_ref(adapter->sta_info_list, sta_info, + STA_INFO_CONNECTION_IN_PROGRESS_ITERATOR) { + if (sta_info->peer_state != + OL_TXRX_PEER_STATE_CONN) { + hdd_put_sta_info_ref( + &adapter->sta_info_list, &sta_info, true, + STA_INFO_CONNECTION_IN_PROGRESS_ITERATOR); + continue; + } + + sta_mac = sta_info->sta_mac.bytes; + hdd_debug("client " QDF_MAC_ADDR_FMT + " of SAP/GO is in middle of WPS/EAPOL exchange", + QDF_MAC_ADDR_REF(sta_mac)); + + context->out_vdev_id = adapter->vdev_id; + context->out_reason = SAP_EAPOL_IN_PROGRESS; + context->connection_in_progress = true; + + hdd_put_sta_info_ref( + &adapter->sta_info_list, &sta_info, true, + STA_INFO_CONNECTION_IN_PROGRESS_ITERATOR); + + return QDF_STATUS_E_ABORTED; + } + if (hdd_ctx->connection_in_progress) { + hdd_debug("AP/GO: vdev %d connection is in progress", + adapter->vdev_id); + context->out_reason = SAP_CONNECTION_IN_PROGRESS; + context->out_vdev_id = adapter->vdev_id; + context->connection_in_progress = true; + + return QDF_STATUS_E_ABORTED; + } + } + + if (ucfg_nan_is_enable_disable_in_progress(hdd_ctx->psoc)) { + context->out_reason = NAN_ENABLE_DISABLE_IN_PROGRESS; + context->out_vdev_id = NAN_PSEUDO_VDEV_ID; + context->connection_in_progress = true; + + return QDF_STATUS_E_ABORTED; + } + + return QDF_STATUS_SUCCESS; +} + +bool hdd_is_connection_in_progress(uint8_t *out_vdev_id, + enum scan_reject_states *out_reason) +{ + struct hdd_is_connection_in_progress_priv hdd_conn; + hdd_adapter_iterate_cb cb; + + hdd_conn.out_vdev_id = 0; + hdd_conn.out_reason = SCAN_REJECT_DEFAULT; + hdd_conn.connection_in_progress = false; + + cb = hdd_is_connection_in_progress_iterator; + + hdd_adapter_iterate(cb, &hdd_conn); + + if (hdd_conn.connection_in_progress && out_vdev_id && out_reason) { + *out_vdev_id = hdd_conn.out_vdev_id; + *out_reason = hdd_conn.out_reason; + } + + return hdd_conn.connection_in_progress; +} + +/** + * hdd_restart_sap() - to restart SAP in driver internally + * @ap_adapter: Pointer to SAP struct hdd_adapter structure + * + * Return: None + */ +void hdd_restart_sap(struct hdd_adapter *ap_adapter) +{ + struct hdd_ap_ctx *hdd_ap_ctx; + struct hdd_hostapd_state *hostapd_state; + QDF_STATUS qdf_status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); + struct sap_config *sap_config; + void *sap_ctx; + + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); + sap_config = &hdd_ap_ctx->sap_config; + sap_ctx = hdd_ap_ctx->sap_context; + + mutex_lock(&hdd_ctx->sap_lock); + if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) { + wlan_hdd_del_station(ap_adapter); + hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter); + qdf_event_reset(&hostapd_state->qdf_stop_bss_event); + if (QDF_STATUS_SUCCESS == wlansap_stop_bss(sap_ctx)) { + qdf_status = + qdf_wait_for_event_completion(&hostapd_state-> + qdf_stop_bss_event, + SME_CMD_STOP_BSS_TIMEOUT); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("SAP Stop Failed"); + goto end; + } + } + clear_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags); + policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, + ap_adapter->device_mode, ap_adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, ap_adapter->device_mode, + false); + hdd_err("SAP Stop Success"); + + if (0 != wlan_hdd_cfg80211_update_apies(ap_adapter)) { + hdd_err("SAP Not able to set AP IEs"); + wlansap_reset_sap_config_add_ie(sap_config, + eUPDATE_IE_ALL); + goto end; + } + + qdf_event_reset(&hostapd_state->qdf_event); + if (wlansap_start_bss(sap_ctx, hdd_hostapd_sap_event_cb, + sap_config, + ap_adapter->dev) != QDF_STATUS_SUCCESS) { + hdd_err("SAP Start Bss fail"); + wlansap_reset_sap_config_add_ie(sap_config, + eUPDATE_IE_ALL); + goto end; + } + + hdd_info("Waiting for SAP to start"); + qdf_status = + qdf_wait_single_event(&hostapd_state->qdf_event, + SME_CMD_START_BSS_TIMEOUT); + wlansap_reset_sap_config_add_ie(sap_config, + eUPDATE_IE_ALL); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("SAP Start failed"); + goto end; + } + hdd_err("SAP Start Success"); + set_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags); + if (hostapd_state->bss_state == BSS_START) { + policy_mgr_incr_active_session(hdd_ctx->psoc, + ap_adapter->device_mode, + ap_adapter->vdev_id); + hdd_green_ap_start_state_mc(hdd_ctx, + ap_adapter->device_mode, + true); + } + } +end: + mutex_unlock(&hdd_ctx->sap_lock); +} + +/** + * hdd_check_and_restart_sap_with_non_dfs_acs() - Restart SAP + * with non dfs acs + * + * Restarts SAP in non-DFS ACS mode when STA-AP mode DFS is not supported + * + * Return: None + */ +void hdd_check_and_restart_sap_with_non_dfs_acs(void) +{ + struct hdd_adapter *ap_adapter; + struct hdd_context *hdd_ctx; + struct cds_context *cds_ctx; + uint8_t restart_chan; + uint32_t restart_freq; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return; + } + + cds_ctx = cds_get_context(QDF_MODULE_ID_QDF); + if (!cds_ctx) { + hdd_err("Invalid CDS Context"); + return; + } + + if (policy_mgr_get_concurrency_mode(hdd_ctx->psoc) + != (QDF_STA_MASK | QDF_SAP_MASK)) { + hdd_debug("Concurrency mode is not SAP"); + return; + } + + ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); + if (ap_adapter && + test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags) && + wlan_reg_is_dfs_for_freq( + hdd_ctx->pdev, + ap_adapter->session.ap.operating_chan_freq)) { + if (policy_mgr_get_dfs_master_dynamic_enabled( + hdd_ctx->psoc, ap_adapter->vdev_id)) + return; + hdd_warn("STA-AP Mode DFS not supported, Switch SAP channel to Non DFS"); + + restart_freq = + wlansap_get_safe_channel_from_pcl_and_acs_range( + WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter)); + + restart_chan = wlan_reg_freq_to_chan(hdd_ctx->pdev, + restart_freq); + if (!restart_chan || + wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, restart_freq)) + restart_chan = SAP_DEFAULT_5GHZ_CHANNEL; + wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, ap_adapter->vdev_id, + CSA_REASON_STA_CONNECT_DFS_TO_NON_DFS); + hdd_switch_sap_channel(ap_adapter, restart_chan, true); + } +} + +/** + * hdd_set_connection_in_progress() - to set the connection in + * progress flag + * @value: value to set + * + * This function will set the passed value to connection in progress flag. + * If value is previously being set to true then no need to set it again. + * + * Return: true if value is being set correctly and false otherwise. + */ +bool hdd_set_connection_in_progress(bool value) +{ + bool status = true; + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return false; + } + + qdf_spin_lock(&hdd_ctx->connection_status_lock); + /* + * if the value is set to true previously and if someone is + * trying to make it true again then it could be some race + * condition being triggered. Avoid this situation by returning + * false + */ + if (hdd_ctx->connection_in_progress && value) + status = false; + else + hdd_ctx->connection_in_progress = value; + qdf_spin_unlock(&hdd_ctx->connection_status_lock); + return status; +} + +int wlan_hdd_send_p2p_quota(struct hdd_adapter *adapter, int set_value) +{ + if (!adapter) { + hdd_err("Invalid adapter"); + return -EINVAL; + } + hdd_info("Send MCC P2P QUOTA to WMA: %d", set_value); + sme_cli_set_command(adapter->vdev_id, + WMA_VDEV_MCC_SET_TIME_QUOTA, + set_value, VDEV_CMD); + return 0; + +} + +int wlan_hdd_send_mcc_latency(struct hdd_adapter *adapter, int set_value) +{ + if (!adapter) { + hdd_err("Invalid adapter"); + return -EINVAL; + } + + hdd_info("Send MCC latency WMA: %d", set_value); + sme_cli_set_command(adapter->vdev_id, + WMA_VDEV_MCC_SET_TIME_LATENCY, + set_value, VDEV_CMD); + return 0; +} + +struct hdd_adapter *wlan_hdd_get_adapter_from_vdev(struct wlan_objmgr_psoc + *psoc, uint8_t vdev_id) +{ + struct hdd_adapter *adapter = NULL; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + /* + * Currently PSOC is not being used. But this logic will + * change once we have the converged implementation of + * HDD context per PSOC in place. This would break if + * multiple vdev objects reuse the vdev id. + */ + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) + hdd_err("Get adapter by vdev id failed"); + + return adapter; +} + +int hdd_get_rssi_snr_by_bssid(struct hdd_adapter *adapter, const uint8_t *bssid, + int8_t *rssi, int8_t *snr) +{ + QDF_STATUS status; + mac_handle_t mac_handle; + struct csr_roam_profile *roam_profile; + + roam_profile = hdd_roam_profile(adapter); + mac_handle = hdd_adapter_get_mac_handle(adapter); + status = sme_get_rssi_snr_by_bssid(mac_handle, + roam_profile, bssid, rssi, snr); + if (QDF_STATUS_SUCCESS != status) { + hdd_warn("sme_get_rssi_snr_by_bssid failed"); + return -EINVAL; + } + + return 0; +} + +/** + * hdd_reset_limit_off_chan() - reset limit off-channel command parameters + * @adapter - HDD adapter + * + * Return: 0 on success and non zero value on failure + */ +int hdd_reset_limit_off_chan(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + int ret; + QDF_STATUS status; + uint8_t sys_pref = 0; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret < 0) + return ret; + + ucfg_policy_mgr_get_sys_pref(hdd_ctx->psoc, + &sys_pref); + /* set the system preferece to default */ + policy_mgr_set_cur_conc_system_pref(hdd_ctx->psoc, sys_pref); + + /* clear the bitmap */ + adapter->active_ac = 0; + + hdd_debug("reset ac_bitmap for session %hu active_ac %0x", + adapter->vdev_id, adapter->active_ac); + + status = sme_send_limit_off_channel_params(hdd_ctx->mac_handle, + adapter->vdev_id, + false, 0, 0, false); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("failed to reset limit off chan params"); + ret = -EINVAL; + } + + return ret; +} + +void hdd_set_rx_mode_rps(bool enable) +{ + struct cds_config_info *cds_cfg = cds_get_ini_config(); + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + + if (!cds_cfg) + return; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) + return; + + adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); + if (!adapter) + return; + + if (!hdd_ctx->rps && cds_cfg->uc_offload_enabled) { + if (enable && !cds_cfg->rps_enabled) + hdd_send_rps_ind(adapter); + else if (!enable && cds_cfg->rps_enabled) + hdd_send_rps_disable_ind(adapter); + } +} + +void hdd_hidden_ssid_enable_roaming(hdd_handle_t hdd_handle, uint8_t vdev_id) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + struct hdd_adapter *adapter; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + + if (!adapter) { + hdd_err("Adapter is null"); + return; + } + /* enable roaming on all adapters once hdd get hidden ssid rsp */ + wlan_hdd_enable_roaming(adapter, RSO_START_BSS); +} + +#ifdef WLAN_FEATURE_PKT_CAPTURE + +/** + * wlan_hdd_is_session_type_monitor() - check if session type is MONITOR + * @session_type: session type + * + * Return: True - if session type for adapter is monitor, else False + * + */ +bool wlan_hdd_is_session_type_monitor(uint8_t session_type) +{ + if (cds_is_pktcapture_enabled() && + cds_get_conparam() != QDF_GLOBAL_MONITOR_MODE && + session_type == QDF_MONITOR_MODE) + return true; + else + return false; +} + +/** + * wlan_hdd_check_mon_concurrency() - check if MONITOR and STA concurrency + * is UP when packet capture mode is enabled. + * @void + * + * Return: True - if STA and monitor concurrency is there, else False + * + */ +bool wlan_hdd_check_mon_concurrency(void) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return -EINVAL; + } + + if (cds_is_pktcapture_enabled()) { + if (policy_mgr_get_concurrency_mode(hdd_ctx->psoc) == + (QDF_STA_MASK | QDF_MONITOR_MASK)) { + hdd_err("STA + MON mode is UP"); + return true; + } + } + return false; +} + +/** + * wlan_hdd_del_monitor() - delete monitor interface + * @hdd_ctx: pointer to hdd context + * @adapter: adapter to be deleted + * @rtnl_held: rtnl lock held + * + * This function is invoked to delete monitor interface. + * + * Return: None + */ +void wlan_hdd_del_monitor(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, bool rtnl_held) +{ + wlan_hdd_release_intf_addr(hdd_ctx, adapter->mac_addr.bytes); + hdd_stop_adapter(hdd_ctx, adapter); + hdd_close_adapter(hdd_ctx, adapter, true); + + hdd_open_p2p_interface(hdd_ctx); +} + +/** + * wlan_hdd_add_monitor_check() - check for monitor intf and add if needed + * @hdd_ctx: pointer to hdd context + * @adapter: output pointer to hold created monitor adapter + * @type: type of the interface + * @name: name of the interface + * @rtnl_held: True if RTNL lock is held + * @name_assign_type: the name of assign type of the netdev + * + * Return: 0 - on success + * err code - on failure + */ +int +wlan_hdd_add_monitor_check(struct hdd_context *hdd_ctx, + struct hdd_adapter **adapter, + enum nl80211_iftype type, const char *name, + bool rtnl_held, unsigned char name_assign_type) +{ + struct hdd_adapter *sta_adapter; + struct hdd_adapter *mon_adapter; + uint32_t mode; + uint8_t num_open_session = 0; + + if (!cds_is_pktcapture_enabled()) + return 0; + + /* + * If add interface request is for monitor mode, then it can run in + * parallel with only one station interface. + * If there is no existing station interface return error + */ + if (type != NL80211_IFTYPE_MONITOR) + return 0; + + if (QDF_STATUS_SUCCESS != policy_mgr_mode_specific_num_open_sessions( + hdd_ctx->psoc, + QDF_MONITOR_MODE, + &num_open_session)) + return -EINVAL; + + if (num_open_session) { + hdd_err("monitor mode already exists, only one is possible"); + return -EBUSY; + } + + /* Ensure there is only one station interface */ + if (QDF_STATUS_SUCCESS != policy_mgr_mode_specific_num_open_sessions( + hdd_ctx->psoc, + QDF_STA_MODE, + &num_open_session)) + return -EINVAL; + + if (num_open_session != 1) { + hdd_err("cannot add monitor mode, due to %u sta interfaces", + num_open_session); + return -EINVAL; + } + + sta_adapter = hdd_get_adapter(hdd_ctx, QDF_STA_MODE); + if (!sta_adapter) { + hdd_err("No station adapter"); + return -EINVAL; + } + + if (QDF_STATUS_SUCCESS != policy_mgr_mode_specific_num_open_sessions( + hdd_ctx->psoc, + QDF_SAP_MODE, + &num_open_session)) + return -EINVAL; + + if (num_open_session) { + hdd_err("cannot add monitor mode, due to SAP concurrency"); + return -EINVAL; + } + + /* delete p2p interface */ + for (mode = QDF_P2P_CLIENT_MODE; mode <= QDF_P2P_DEVICE_MODE; mode++) { + struct hdd_adapter *adapter; + + if (mode == QDF_FTM_MODE || + mode == QDF_MONITOR_MODE || + mode == QDF_IBSS_MODE) + continue; + + adapter = hdd_get_adapter(hdd_ctx, mode); + if (adapter) { + struct osif_vdev_sync *vdev_sync; + + vdev_sync = osif_vdev_sync_unregister(adapter->dev); + if (vdev_sync) + osif_vdev_sync_wait_for_ops(vdev_sync); + + wlan_hdd_release_intf_addr(hdd_ctx, + adapter->mac_addr.bytes); + hdd_stop_adapter(hdd_ctx, adapter); + hdd_deinit_adapter(hdd_ctx, adapter, true); + hdd_close_adapter(hdd_ctx, adapter, rtnl_held); + + if (vdev_sync) + osif_vdev_sync_destroy(vdev_sync); + } + } + + mon_adapter = hdd_open_adapter(hdd_ctx, QDF_MONITOR_MODE, name, + wlan_hdd_get_intf_addr( + hdd_ctx, + QDF_MONITOR_MODE), + name_assign_type, rtnl_held); + if (!mon_adapter) { + hdd_err("hdd_open_adapter failed"); + hdd_open_p2p_interface(hdd_ctx); + return -EINVAL; + } + + *adapter = mon_adapter; + return 0; +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +#ifdef FEATURE_MONITOR_MODE_SUPPORT + +void hdd_sme_monitor_mode_callback(uint8_t vdev_id) +{ + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err_rl("Invalid HDD_CTX"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err_rl("NULL adapter"); + return; + } + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err_rl("Invalid magic"); + return; + } + + if (adapter->magic == WLAN_HDD_ADAPTER_MAGIC) + qdf_event_set(&adapter->qdf_monitor_mode_vdev_up_event); + + hdd_debug("monitor mode vdev up completed"); + adapter->monitor_mode_vdev_up_in_progress = false; +} + +QDF_STATUS hdd_monitor_mode_qdf_create_event(struct hdd_adapter *adapter, + uint8_t session_type) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + if (session_type == QDF_MONITOR_MODE) { + qdf_status = qdf_event_create( + &adapter->qdf_monitor_mode_vdev_up_event); + } + return qdf_status; +} + +QDF_STATUS hdd_monitor_mode_vdev_status(struct hdd_adapter *adapter) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!adapter->monitor_mode_vdev_up_in_progress) + return status; + + /* block on a completion variable until vdev up success*/ + status = qdf_wait_for_event_completion( + &adapter->qdf_monitor_mode_vdev_up_event, + WLAN_MONITOR_MODE_VDEV_UP_EVT); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err_rl("monitor mode vdev up event time out vdev id: %d", + adapter->vdev_id); + if (adapter->qdf_monitor_mode_vdev_up_event.force_set) + /* + * SSR/PDR has caused shutdown, which has + * forcefully set the event. + */ + hdd_err_rl("monitor mode vdev up event forcefully set"); + else if (status == QDF_STATUS_E_TIMEOUT) + hdd_err_rl("mode vdev up event timed out"); + else + hdd_err_rl("Failed to wait for monitor vdev up(status-%d)", + status); + + adapter->monitor_mode_vdev_up_in_progress = false; + return status; + } + + return QDF_STATUS_SUCCESS; +} +#endif + +/* Register the module init/exit functions */ +#ifdef MODULE +module_init(hdd_module_init); +module_exit(hdd_module_exit); +#else +late_initcall(hdd_module_init); +#endif + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Qualcomm Atheros, Inc."); +MODULE_DESCRIPTION("WLAN HOST DEVICE DRIVER"); + +static const struct kernel_param_ops con_mode_ops = { + .set = con_mode_handler, + .get = param_get_int, +}; + +static const struct kernel_param_ops con_mode_ftm_ops = { + .set = con_mode_handler_ftm, + .get = param_get_int, +}; + +#ifdef WLAN_FEATURE_EPPING +static const struct kernel_param_ops con_mode_epping_ops = { + .set = con_mode_handler_epping, + .get = param_get_int, +}; +#endif + +static const struct kernel_param_ops fwpath_ops = { + .set = fwpath_changed_handler, + .get = param_get_string, +}; + +module_param_cb(con_mode, &con_mode_ops, &con_mode, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + +module_param_cb(con_mode_ftm, &con_mode_ftm_ops, &con_mode_ftm, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + +#ifdef WLAN_FEATURE_EPPING +module_param_cb(con_mode_epping, &con_mode_epping_ops, + &con_mode_epping, 0644); +#endif + +module_param_cb(fwpath, &fwpath_ops, &fwpath, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + +module_param(enable_dfs_chan_scan, int, S_IRUSR | S_IRGRP | S_IROTH); + +module_param(enable_11d, int, S_IRUSR | S_IRGRP | S_IROTH); + +module_param(country_code, charp, S_IRUSR | S_IRGRP | S_IROTH); + +static int timer_multiplier_get_handler(char *buffer, + const struct kernel_param *kp) +{ + return scnprintf(buffer, PAGE_SIZE, "%u", qdf_timer_get_multiplier()); +} + +static int timer_multiplier_set_handler(const char *kmessage, + const struct kernel_param *kp) +{ + QDF_STATUS status; + uint32_t scalar; + + status = qdf_uint32_parse(kmessage, &scalar); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + if (!cfg_in_range(CFG_TIMER_MULTIPLIER, scalar)) + return -ERANGE; + + qdf_timer_set_multiplier(scalar); + + return 0; +} + +static const struct kernel_param_ops timer_multiplier_ops = { + .get = timer_multiplier_get_handler, + .set = timer_multiplier_set_handler, +}; + +module_param_cb(timer_multiplier, &timer_multiplier_ops, NULL, 0644); + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_memdump.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_memdump.c new file mode 100644 index 0000000000000000000000000000000000000000..954a574f02b9170c02a0dc0d0d59e936ee2b725d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_memdump.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_memdump.c + * + * WLAN Host Device Driver file for dumping firmware memory + * + */ + +#include +#include +#include +#include /* Necessary because we use the proc fs */ +#include /* for copy_to_user */ +#include "osif_sync.h" +#include +#include + +#ifdef MULTI_IF_NAME +#define PROCFS_DRIVER_DUMP_DIR "debugdriver" MULTI_IF_NAME +#else +#define PROCFS_DRIVER_DUMP_DIR "debugdriver" +#endif +#define PROCFS_DRIVER_DUMP_NAME "driverdump" +#define PROCFS_DRIVER_DUMP_PERM 0444 + +static struct proc_dir_entry *proc_file_driver, *proc_dir_driver; + +/** memdump_get_file_data() - get data available in proc file + * + * @file - handle for the proc file. + * + * This function is used to retrieve the data passed while + * creating proc file entry. + * + * Return: void pointer to hdd_context + */ +static void *memdump_get_file_data(struct file *file) +{ + void *hdd_ctx; + + hdd_ctx = PDE_DATA(file_inode(file)); + return hdd_ctx; +} + +void hdd_driver_mem_cleanup(void) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("Invalid HDD context"); + return; + } + + if (hdd_ctx->driver_dump_mem) { + qdf_mem_free(hdd_ctx->driver_dump_mem); + hdd_ctx->driver_dump_mem = NULL; + } +} + + +/** + * __hdd_driver_memdump_read() - perform read operation in driver + * memory dump proc file + * @file - handle for the proc file. + * @buf - pointer to user space buffer. + * @count - number of bytes to be read. + * @pos - offset in the from buffer. + * + * This function performs read operation for the driver memory dump proc file. + * + * Return: number of bytes read on success + * negative error code in case of failure + * 0 in case of no more data + */ +static ssize_t __hdd_driver_memdump_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + int status; + QDF_STATUS qdf_status; + struct hdd_context *hdd_ctx; + size_t no_of_bytes_read = 0; + + hdd_ctx = memdump_get_file_data(file); + + hdd_debug("Read req for size:%zu pos:%llu", count, *pos); + status = wlan_hdd_validate_context(hdd_ctx); + if (status != 0) + return -EINVAL; + + mutex_lock(&hdd_ctx->memdump_lock); + if (*pos < 0) { + hdd_err("Invalid start offset for memdump read"); + mutex_unlock(&hdd_ctx->memdump_lock); + return -EINVAL; + } + + if (!count || + (hdd_ctx->driver_dump_size && *pos >= hdd_ctx->driver_dump_size)) { + mutex_unlock(&hdd_ctx->memdump_lock); + hdd_debug("No more data to copy"); + return 0; + } + + if (*pos == 0 || !hdd_ctx->driver_dump_mem) { + /* Allocate memory for Driver memory dump */ + if (!hdd_ctx->driver_dump_mem) { + hdd_ctx->driver_dump_mem = + qdf_mem_malloc(DRIVER_MEM_DUMP_SIZE); + if (!hdd_ctx->driver_dump_mem) { + mutex_unlock(&hdd_ctx->memdump_lock); + return -ENOMEM; + } + } + + qdf_status = qdf_state_info_dump_all(hdd_ctx->driver_dump_mem, + DRIVER_MEM_DUMP_SIZE, + &hdd_ctx->driver_dump_size); + /* + * If qdf_status is QDF_STATUS_E_NOMEM, then memory allocated is + * insufficient to dump driver information. This print can give + * information to allocate more memory if more information from + * each layer is added in future. + */ + if (qdf_status != QDF_STATUS_SUCCESS) + hdd_err("Error in dump driver information, status %d", + qdf_status); + hdd_debug("driver_dump_size: %d", hdd_ctx->driver_dump_size); + } + + if (count > hdd_ctx->driver_dump_size - *pos) + no_of_bytes_read = hdd_ctx->driver_dump_size - *pos; + else + no_of_bytes_read = count; + + if (copy_to_user(buf, hdd_ctx->driver_dump_mem + *pos, + no_of_bytes_read)) { + hdd_err("copy to user space failed"); + mutex_unlock(&hdd_ctx->memdump_lock); + return -EFAULT; + } + + /* offset(pos) should be updated here based on the copy done */ + *pos += no_of_bytes_read; + + /* Entire driver memory dump copy completed */ + if (*pos >= hdd_ctx->driver_dump_size) + hdd_driver_mem_cleanup(); + + mutex_unlock(&hdd_ctx->memdump_lock); + + return no_of_bytes_read; +} + +/** + * hdd_driver_memdump_read() - perform read operation in driver + * memory dump proc file + * @file - handle for the proc file. + * @buf - pointer to user space buffer. + * @count - number of bytes to be read. + * @pos - offset in the from buffer. + * + * This function performs read operation for the driver memory dump proc file. + * + * Return: number of bytes read on success + * negative error code in case of failure + * 0 in case of no more data + */ +static ssize_t hdd_driver_memdump_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct osif_driver_sync *driver_sync; + ssize_t err_size; + + err_size = osif_driver_sync_op_start(&driver_sync); + if (err_size) + return err_size; + + err_size = __hdd_driver_memdump_read(file, buf, count, pos); + + osif_driver_sync_op_stop(driver_sync); + + return err_size; +} + +/** + * struct driver_dump_fops - file operations for driver dump feature + * @read - read function for driver dump operation. + * + * This structure initialize the file operation handle for memory + * dump feature + */ +static const struct file_operations driver_dump_fops = { + .read = hdd_driver_memdump_read, +}; + +/** + * hdd_driver_memdump_procfs_init() - Initialize procfs for driver memory dump + * @hdd_ctx Pointer to hdd context + * + * This function create file under proc file system to be used later for + * processing driver memory dump + * + * Return: 0 on success, error code otherwise. + */ +static int hdd_driver_memdump_procfs_init(struct hdd_context *hdd_ctx) +{ + proc_dir_driver = proc_mkdir(PROCFS_DRIVER_DUMP_DIR, NULL); + if (!proc_dir_driver) { + pr_debug("Could not initialize /proc/%s\n", + PROCFS_DRIVER_DUMP_DIR); + return -ENOMEM; + } + + proc_file_driver = proc_create_data(PROCFS_DRIVER_DUMP_NAME, + PROCFS_DRIVER_DUMP_PERM, proc_dir_driver, + &driver_dump_fops, hdd_ctx); + if (!proc_file_driver) { + remove_proc_entry(PROCFS_DRIVER_DUMP_NAME, proc_dir_driver); + pr_debug("Could not initialize /proc/%s\n", + PROCFS_DRIVER_DUMP_NAME); + return -ENOMEM; + } + + pr_debug("/proc/%s/%s created\n", PROCFS_DRIVER_DUMP_DIR, + PROCFS_DRIVER_DUMP_NAME); + return 0; +} + +/** + * hdd_driver_memdump_procfs_remove() - Remove file/dir under procfs + * for driver memory dump + * + * This function removes file/dir under proc file system that was + * processing driver memory dump + * + * Return: None + */ +static void hdd_driver_memdump_procfs_remove(void) +{ + remove_proc_entry(PROCFS_DRIVER_DUMP_NAME, proc_dir_driver); + pr_debug("/proc/%s/%s removed\n", PROCFS_DRIVER_DUMP_DIR, + PROCFS_DRIVER_DUMP_NAME); + remove_proc_entry(PROCFS_DRIVER_DUMP_DIR, NULL); + pr_debug("/proc/%s removed\n", PROCFS_DRIVER_DUMP_DIR); +} + +/** + * hdd_driver_memdump_init() - Intialization function for driver + * memory dump feature + * + * This function creates proc file for driver memdump feature + * + * Return - 0 on success, error otherwise + */ +int hdd_driver_memdump_init(void) +{ + int status; + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("Invalid HDD context"); + return -EINVAL; + } + + mutex_init(&hdd_ctx->memdump_lock); + + status = hdd_driver_memdump_procfs_init(hdd_ctx); + if (status) { + hdd_err("Failed to create proc file"); + return status; + } + + return 0; +} + +/** + * hdd_driver_memdump_deinit() - De initialize driver memdump feature + * + * This function removes proc file created for driver memdump feature. + * + * Return: None + */ +void hdd_driver_memdump_deinit(void) +{ + hdd_driver_memdump_procfs_remove(); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_mpta_helper.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_mpta_helper.c new file mode 100644 index 0000000000000000000000000000000000000000..726ecce26dcf658fb0ff0b647c32c5b3e52ac9bb --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_mpta_helper.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_mpta_helper.c + * + * The implementation of mpta helper configuration + */ + +#include "wlan_hdd_main.h" +#include "wmi_unified_param.h" +#include "wlan_hdd_mpta_helper.h" +#include "qca_vendor.h" +#include "wlan_osif_request_manager.h" +#include "osif_sync.h" + +static const struct nla_policy +qca_wlan_vendor_mpta_helper_attr[QCA_MPTA_HELPER_VENDOR_ATTR_MAX + 1] = { + [QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE] = {.type = NLA_U32 }, + [QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION] = {.type = NLA_U32 }, + [QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION] = { + .type = NLA_U32 }, + [QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION] = {.type = NLA_U32 }, + [QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION] = { + .type = NLA_U32 }, + [QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION] = {.type = NLA_U32 }, + [QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION] = {.type = NLA_U32 }, + [QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN] = {.type = NLA_U32 }, + [QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION] = {.type = NLA_U32 }, +}; + +/** + * __wlan_hdd_cfg80211_mpta_helper_config() - update + * tri-radio coex status by mpta helper + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Return: 0 on success; error number otherwise. + * + */ +static int +__wlan_hdd_cfg80211_mpta_helper_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_MPTA_HELPER_VENDOR_ATTR_MAX + 1]; + struct coex_config_params coex_cfg_params = {0}; + int errno; + QDF_STATUS status; + + hdd_enter_dev(wdev->netdev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (wlan_cfg80211_nla_parse(tb, QCA_MPTA_HELPER_VENDOR_ATTR_MAX, data, + data_len, + qca_wlan_vendor_mpta_helper_attr)) { + hdd_err("invalid attr"); + return -EINVAL; + } + + /* if no attributes specified, return -EINVAL */ + errno = -EINVAL; + + if (tb[QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE]) { + coex_cfg_params.config_type = + WMI_COEX_CONFIG_MPTA_HELPER_ZIGBEE_STATE; + coex_cfg_params.config_arg1 = nla_get_u32 + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE]); + + status = sme_send_coex_config_cmd(&coex_cfg_params); + + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set zigbee STATE"); + return -EINVAL; + } + + errno = 0; + } + + if ((tb[QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION]) && + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION])) { + coex_cfg_params.config_type = + WMI_COEX_CONFIG_MPTA_HELPER_INT_OCS_PARAMS; + coex_cfg_params.config_arg1 = nla_get_u32 + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION]); + coex_cfg_params.config_arg2 = nla_get_u32 + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION]); + + status = sme_send_coex_config_cmd(&coex_cfg_params); + + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set int OCS duration"); + return -EINVAL; + } + + errno = 0; + } + + if ((tb[QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION]) && + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION])) { + coex_cfg_params.config_type = + WMI_COEX_CONFIG_MPTA_HELPER_MON_OCS_PARAMS; + coex_cfg_params.config_arg1 = nla_get_u32 + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION]); + coex_cfg_params.config_arg2 = nla_get_u32 + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION]); + + status = sme_send_coex_config_cmd(&coex_cfg_params); + + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set mon OCS duration"); + return -EINVAL; + } + + errno = 0; + } + + if ((tb[QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION]) && + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION])) { + coex_cfg_params.config_type = + WMI_COEX_CONFIG_MPTA_HELPER_INT_MON_DURATION; + coex_cfg_params.config_arg1 = nla_get_u32 + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION]); + coex_cfg_params.config_arg2 = nla_get_u32 + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION]); + + status = sme_send_coex_config_cmd(&coex_cfg_params); + + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set int mon duration"); + return -EINVAL; + } + + errno = 0; + } + + if (tb[QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN]) { + coex_cfg_params.config_type = + WMI_COEX_CONFIG_MPTA_HELPER_ZIGBEE_CHANNEL; + coex_cfg_params.config_arg1 = nla_get_u32 + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN]); + + status = sme_send_coex_config_cmd(&coex_cfg_params); + + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set zigbee chan"); + return -EINVAL; + } + + errno = 0; + } + + if (tb[QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION]) { + coex_cfg_params.config_type = + WMI_COEX_CONFIG_MPTA_HELPER_WLAN_MUTE_DURATION; + coex_cfg_params.config_arg1 = nla_get_u32 + (tb[QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION]); + + status = sme_send_coex_config_cmd(&coex_cfg_params); + + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set wlan mute duration"); + return -EINVAL; + } + + errno = 0; + } + + return errno; +} + +/** + * wlan_hdd_cfg80211_mpta_helper_config() - update + * tri-radio coex status by mpta helper + * @wiphy: wiphy device pointer + * @wdev: wireless device pointer + * @data: Vendor command data buffer + * @data_len: Buffer length + * + * Return: 0 on success; error number otherwise. + * + */ +int +wlan_hdd_cfg80211_mpta_helper_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_mpta_helper_config( + wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_mpta_helper_enable() - enable/disable mpta helper + * according to cfg from INI + * @coex_cfg_params: pointer of coex config command params + * @config: pointer of BTC config items + * + * Return: 0 on success; error number otherwise. + * + */ +int +wlan_hdd_mpta_helper_enable(struct coex_config_params *coex_cfg_params, + struct wlan_fwol_coex_config *config) +{ + QDF_STATUS status; + + coex_cfg_params->config_type = WMI_COEX_CONFIG_MPTA_HELPER_ENABLE; + coex_cfg_params->config_arg1 = config->btc_mpta_helper_enable; + + status = sme_send_coex_config_cmd(coex_cfg_params); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to send coex MPTA Helper Enable"); + return -EINVAL; + } + + return 0; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nan.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nan.c new file mode 100644 index 0000000000000000000000000000000000000000..8ff27af80bf60f8bf5b45f523cf304e5f3a88506 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nan.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_nan.c + * + * WLAN Host Device Driver NAN API implementation + */ + +#include +#include +#include +#include +#include +#include "sme_api.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_nan.h" +#include "osif_sync.h" +#include +#include "cfg_nan_api.h" +#include "os_if_nan.h" + +/** + * wlan_hdd_nan_is_supported() - HDD NAN support query function + * @hdd_ctx: Pointer to hdd context + * + * This function is called to determine if NAN is supported by the + * driver and by the firmware. + * + * Return: true if NAN is supported by the driver and firmware + */ +bool wlan_hdd_nan_is_supported(struct hdd_context *hdd_ctx) +{ + return cfg_nan_get_enable(hdd_ctx->psoc) && + sme_is_feature_supported_by_fw(NAN); +} + +/** + * __wlan_hdd_cfg80211_nan_ext_request() - cfg80211 NAN extended request handler + * @wiphy: driver's wiphy struct + * @wdev: wireless device to which the request is targeted + * @data: actual request data (netlink-encapsulated) + * @data_len: length of @data + * + * Handles NAN Extended vendor commands, sends the command to NAN component + * which parses and forwards the NAN requests. + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_nan_ext_request(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret_val; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + hdd_enter_dev(wdev->netdev); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err_rl("Command not allowed in FTM mode"); + return -EPERM; + } + + if (!wlan_hdd_nan_is_supported(hdd_ctx)) { + hdd_debug("NAN is not supported"); + return -EPERM; + } + + return os_if_process_nan_req(hdd_ctx->psoc, adapter->vdev_id, + data, data_len); +} + +int wlan_hdd_cfg80211_nan_ext_request(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) + +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_nan_ext_request(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nan_datapath.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nan_datapath.c new file mode 100644 index 0000000000000000000000000000000000000000..6bea10ccb12594da278db83e99a8d6a23af9a4d5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nan_datapath.c @@ -0,0 +1,1009 @@ +/* + * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_nan_datapath.c + * + * WLAN Host Device Driver nan datapath API implementation + */ +#include +#include +#include +#include +#include +#include "wlan_hdd_includes.h" +#include "wlan_hdd_p2p.h" +#include "osif_sync.h" +#include "wma_api.h" +#include "wlan_hdd_assoc.h" +#include "sme_nan_datapath.h" +#include "wlan_hdd_object_manager.h" +#include +#include "os_if_nan.h" +#include "wlan_nan_api.h" +#include "nan_public_structs.h" +#include "cfg_nan_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "qdf_util.h" +#include +#include "wlan_fwol_ucfg_api.h" + +/** + * hdd_nan_datapath_target_config() - Configure NAN datapath features + * @hdd_ctx: Pointer to HDD context + * @cfg: Pointer to target device capability information + * + * NAN datapath functionality is enabled if it is enabled in + * .ini file and also supported on target device. + * + * Return: None + */ +void hdd_nan_datapath_target_config(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *tgt_cfg) +{ + hdd_ctx->nan_datapath_enabled = + cfg_nan_get_datapath_enable(hdd_ctx->psoc) && + tgt_cfg->nan_datapath_enabled; + hdd_debug("NAN Datapath Enable: %d (Host: %d FW: %d)", + hdd_ctx->nan_datapath_enabled, + cfg_nan_get_datapath_enable(hdd_ctx->psoc), + tgt_cfg->nan_datapath_enabled); +} + +/** + * hdd_close_ndi() - close NAN Data interface + * @adapter: adapter context + * + * Close the adapter if start BSS fails + * + * Returns: 0 on success, negative error code otherwise + */ +static int hdd_close_ndi(struct hdd_adapter *adapter) +{ + int errno; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_enter(); + + /* check if the adapter is in NAN Data mode */ + if (QDF_NDI_MODE != adapter->device_mode) { + hdd_err("Interface is not in NDI mode"); + return -EINVAL; + } + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + +#ifdef WLAN_OPEN_SOURCE + cancel_work_sync(&adapter->ipv4_notifier_work); +#endif + hdd_deregister_hl_netdev_fc_timer(adapter); + hdd_deregister_tx_flow_control(adapter); + +#ifdef WLAN_NS_OFFLOAD +#ifdef WLAN_OPEN_SOURCE + cancel_work_sync(&adapter->ipv6_notifier_work); +#endif +#endif + errno = hdd_vdev_destroy(adapter); + if (errno) + hdd_err("failed to destroy vdev: %d", errno); + + /* We are good to close the adapter */ + hdd_close_adapter(hdd_ctx, adapter, true); + + hdd_exit(); + return 0; +} + +/** + * hdd_is_ndp_allowed() - Indicates if NDP is allowed + * @hdd_ctx: hdd context + * + * NDP is not allowed with any other role active except STA. + * + * Return: true if allowed, false otherwise + */ +#ifdef NDP_SAP_CONCURRENCY_ENABLE +static bool hdd_is_ndp_allowed(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + struct hdd_station_ctx *sta_ctx; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_IS_NDP_ALLOWED; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + switch (adapter->device_mode) { + case QDF_P2P_GO_MODE: + if (test_bit(SOFTAP_BSS_STARTED, + &adapter->event_flags)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return false; + } + break; + case QDF_P2P_CLIENT_MODE: + case QDF_IBSS_MODE: + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (hdd_conn_is_connected(sta_ctx) || + hdd_is_connecting(sta_ctx)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return false; + } + break; + default: + break; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return true; +} +#else +static bool hdd_is_ndp_allowed(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + struct hdd_station_ctx *sta_ctx; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_IS_NDP_ALLOWED; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + switch (adapter->device_mode) { + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + if (test_bit(SOFTAP_BSS_STARTED, + &adapter->event_flags)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return false; + } + break; + case QDF_P2P_CLIENT_MODE: + case QDF_IBSS_MODE: + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (hdd_conn_is_connected(sta_ctx) || + hdd_is_connecting(sta_ctx)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return false; + } + break; + default: + break; + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + return true; +} +#endif /* NDP_SAP_CONCURRENCY_ENABLE */ + +/** + * hdd_ndi_start_bss() - Start BSS on NAN data interface + * @adapter: adapter context + * @operating_channel: channel on which the BSS to be started + * + * Return: 0 on success, error value on failure + */ +static int hdd_ndi_start_bss(struct hdd_adapter *adapter, + uint8_t operating_channel) +{ + QDF_STATUS status; + uint32_t roam_id; + struct csr_roam_profile *roam_profile; + mac_handle_t mac_handle; + uint8_t wmm_mode = 0; + struct hdd_context *hdd_ctx; + uint8_t value = 0; + uint32_t oper_freq; + + hdd_enter(); + + roam_profile = hdd_roam_profile(adapter); + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + status = ucfg_mlme_get_wmm_mode(hdd_ctx->psoc, &wmm_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get wmm_mode failed"); + return -EINVAL; + } + + if (HDD_WMM_USER_MODE_NO_QOS == wmm_mode) { + /* QoS not enabled in cfg file*/ + roam_profile->uapsd_mask = 0; + } else { + /* QoS enabled, update uapsd mask from cfg file*/ + status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc, &value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_mask failed"); + return -EINVAL; + } + roam_profile->uapsd_mask = value; + } + + roam_profile->csrPersona = adapter->device_mode; + + if (!operating_channel) + operating_channel = NAN_SOCIAL_CHANNEL_2_4GHZ; + oper_freq = wlan_reg_chan_to_freq(hdd_ctx->pdev, operating_channel); + + roam_profile->ChannelInfo.numOfChannels = 1; + roam_profile->ChannelInfo.freq_list = &oper_freq; + + roam_profile->SSIDs.numOfSSIDs = 1; + roam_profile->SSIDs.SSIDList->SSID.length = 0; + + roam_profile->phyMode = eCSR_DOT11_MODE_11ac; + roam_profile->BSSType = eCSR_BSS_TYPE_NDI; + roam_profile->BSSIDs.numOfBSSIDs = 1; + qdf_mem_copy((void *)(roam_profile->BSSIDs.bssid), + &adapter->mac_addr.bytes[0], + QDF_MAC_ADDR_SIZE); + + roam_profile->AuthType.numEntries = 1; + roam_profile->AuthType.authType[0] = eCSR_AUTH_TYPE_OPEN_SYSTEM; + roam_profile->EncryptionType.numEntries = 1; + roam_profile->EncryptionType.encryptionType[0] = eCSR_ENCRYPT_TYPE_NONE; + + mac_handle = hdd_adapter_get_mac_handle(adapter); + status = sme_roam_connect(mac_handle, adapter->vdev_id, + roam_profile, &roam_id); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("NDI sme_RoamConnect session %d failed with status %d -> NotConnected", + adapter->vdev_id, status); + /* change back to NotConnected */ + hdd_conn_set_connection_state(adapter, + eConnectionState_NotConnected); + } else { + hdd_info("sme_RoamConnect issued successfully for NDI"); + } + + roam_profile->ChannelInfo.freq_list = NULL; + roam_profile->ChannelInfo.numOfChannels = 0; + + hdd_exit(); + + return 0; +} + +/** + * hdd_get_random_nan_mac_addr() - generate random non pre-existent mac address + * @hdd_ctx: hdd context pointer + * @mac_addr: mac address buffer to populate + * + * Return: status of operation + */ +static int hdd_get_random_nan_mac_addr(struct hdd_context *hdd_ctx, + struct qdf_mac_addr *mac_addr) +{ + struct hdd_adapter *adapter; + uint8_t pos, bit_pos, byte_pos, mask; + uint8_t i, attempts, max_attempt = 16; + bool found; + + for (attempts = 0; attempts < max_attempt; attempts++) { + found = false; + /* if NDI is present next addr is required to be 1 bit apart */ + adapter = hdd_get_adapter(hdd_ctx, QDF_NDI_MODE); + if (adapter) { + hdd_debug("NDI already exists, deriving next mac"); + qdf_mem_copy(mac_addr, &adapter->mac_addr, + sizeof(*mac_addr)); + qdf_get_random_bytes(&pos, sizeof(pos)); + /* skipping byte 0, 5 leaves 8*4=32 positions */ + pos = pos % 32; + bit_pos = pos % 8; + byte_pos = pos / 8; + mask = 1 << bit_pos; + /* flip the required bit */ + mac_addr->bytes[byte_pos + 1] ^= mask; + } else { + qdf_get_random_bytes(mac_addr, sizeof(*mac_addr)); + /* + * Reset multicast bit (bit-0) and set + * locally-administered bit + */ + mac_addr->bytes[0] = 0x2; + + /* + * to avoid potential conflict with FW's generated NMI + * mac addr, host sets LSB if 6th byte to 0 + */ + mac_addr->bytes[5] &= 0xFE; + } + for (i = 0; i < hdd_ctx->num_provisioned_addr; i++) { + if ((!qdf_mem_cmp(hdd_ctx-> + provisioned_mac_addr[i].bytes, + mac_addr, sizeof(*mac_addr)))) { + found = true; + break; + } + } + + if (found) + continue; + + for (i = 0; i < hdd_ctx->num_derived_addr; i++) { + if ((!qdf_mem_cmp(hdd_ctx-> + derived_mac_addr[i].bytes, + mac_addr, sizeof(*mac_addr)))) { + found = true; + break; + } + } + if (found) + continue; + + adapter = hdd_get_adapter_by_macaddr(hdd_ctx, mac_addr->bytes); + if (!adapter) + return 0; + } + + hdd_err("unable to get non-pre-existing mac address in %d attempts", + max_attempt); + + return -EINVAL; +} + +void hdd_ndp_event_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint32_t roam_id, eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ + bool success; + struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(adapter->vdev); + + if (roam_status == eCSR_ROAM_NDP_STATUS_UPDATE) { + switch (roam_result) { + case eCSR_ROAM_RESULT_NDI_CREATE_RSP: + success = (roam_info->ndp.ndi_create_params.status == + NAN_DATAPATH_RSP_STATUS_SUCCESS); + hdd_debug("posting ndi create status: %d (%s) to umac", + success, success ? "Success" : "Failure"); + os_if_nan_post_ndi_create_rsp(psoc, adapter->vdev_id, + success); + return; + case eCSR_ROAM_RESULT_NDI_DELETE_RSP: + success = (roam_info->ndp.ndi_create_params.status == + NAN_DATAPATH_RSP_STATUS_SUCCESS); + hdd_debug("posting ndi delete status: %d (%s) to umac", + success, success ? "Success" : "Failure"); + os_if_nan_post_ndi_delete_rsp(psoc, adapter->vdev_id, + success); + return; + default: + hdd_err("in correct roam_result: %d", roam_result); + return; + } + } else { + hdd_err("in correct roam_status: %d", roam_status); + return; + } +} + +/** + * __wlan_hdd_cfg80211_process_ndp_cmds() - handle NDP request + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This function is invoked to handle vendor command + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_process_ndp_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int data_len) +{ + int ret_val; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err_rl("Command not allowed in FTM mode"); + return -EPERM; + } + + if (!WLAN_HDD_IS_NDP_ENABLED(hdd_ctx)) { + hdd_debug("NAN datapath is not enabled"); + return -EPERM; + } + + return os_if_nan_process_ndp_cmd(hdd_ctx->psoc, data, data_len, + hdd_is_ndp_allowed(hdd_ctx)); +} + +/** + * wlan_hdd_cfg80211_process_ndp_cmd() - handle NDP request + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * This function is called to send a NAN request to + * firmware. This is an SSR-protected wrapper function. + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_process_ndp_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + /* This call is intentionally not protected by op_start/op_stop, due to + * the various protection needs of the callbacks dispatched within. + */ + return __wlan_hdd_cfg80211_process_ndp_cmd(wiphy, wdev, + data, data_len); +} + +static int update_ndi_state(struct hdd_adapter *adapter, uint32_t state) +{ + return os_if_nan_set_ndi_state(adapter->vdev, state); +} + +/** + * hdd_init_nan_data_mode() - initialize nan data mode + * @adapter: adapter context + * + * Returns: 0 on success negative error code on error + */ +int hdd_init_nan_data_mode(struct hdd_adapter *adapter) +{ + struct net_device *wlan_dev = adapter->dev; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + int32_t ret_val; + mac_handle_t mac_handle; + bool bval = false; + uint8_t enable_sifs_burst = 0; + + ret_val = hdd_vdev_create(adapter); + if (ret_val) { + hdd_err("failed to create vdev: %d", ret_val); + return ret_val; + } + + mac_handle = hdd_ctx->mac_handle; + + /* Configure self HT/VHT capabilities */ + sme_set_curr_device_mode(mac_handle, adapter->device_mode); + + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + + sme_set_pdev_ht_vht_ies(mac_handle, bval); + sme_set_vdev_ies_per_band(mac_handle, adapter->vdev_id, + adapter->device_mode); + + hdd_roam_profile_init(adapter); + hdd_register_wext(wlan_dev); + + status = hdd_init_tx_rx(adapter); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("hdd_init_tx_rx() init failed, status %d", status); + ret_val = -EAGAIN; + goto error_init_txrx; + } + + set_bit(INIT_TX_RX_SUCCESS, &adapter->event_flags); + + status = hdd_wmm_adapter_init(adapter); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("hdd_wmm_adapter_init() failed, status %d", status); + ret_val = -EAGAIN; + goto error_wmm_init; + } + + set_bit(WMM_INIT_DONE, &adapter->event_flags); + + status = ucfg_get_enable_sifs_burst(hdd_ctx->psoc, &enable_sifs_burst); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to get sifs burst value, use default"); + + ret_val = wma_cli_set_command((int)adapter->vdev_id, + (int)WMI_PDEV_PARAM_BURST_ENABLE, + enable_sifs_burst, + PDEV_CMD); + if (0 != ret_val) + hdd_err("WMI_PDEV_PARAM_BURST_ENABLE set failed %d", ret_val); + + hdd_set_netdev_flags(adapter); + + update_ndi_state(adapter, NAN_DATA_NDI_CREATING_STATE); + return ret_val; + +error_wmm_init: + clear_bit(INIT_TX_RX_SUCCESS, &adapter->event_flags); + hdd_deinit_tx_rx(adapter); + +error_init_txrx: + hdd_unregister_wext(wlan_dev); + + QDF_BUG(!hdd_vdev_destroy(adapter)); + + return ret_val; +} + +int hdd_ndi_open(char *iface_name) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + struct qdf_mac_addr random_ndi_mac; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + uint8_t ndi_adapter_count = 0; + uint8_t *ndi_mac_addr; + + hdd_enter(); + if (!hdd_ctx) { + hdd_err("hdd_ctx null"); + return -EINVAL; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_NDI_OPEN) { + if (WLAN_HDD_IS_NDI(adapter)) + ndi_adapter_count++; + hdd_adapter_dev_put_debug(adapter, NET_DEV_HOLD_NDI_OPEN); + } + if (ndi_adapter_count >= MAX_NDI_ADAPTERS) { + hdd_err("Can't allow more than %d NDI adapters", + MAX_NDI_ADAPTERS); + return -EINVAL; + } + + if (cfg_nan_get_ndi_mac_randomize(hdd_ctx->psoc)) { + if (hdd_get_random_nan_mac_addr(hdd_ctx, &random_ndi_mac)) { + hdd_err("get random mac address failed"); + return -EFAULT; + } + ndi_mac_addr = &random_ndi_mac.bytes[0]; + } else { + ndi_mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, QDF_NDI_MODE); + if (!ndi_mac_addr) { + hdd_err("get intf address failed"); + return -EFAULT; + } + } + + adapter = hdd_open_adapter(hdd_ctx, QDF_NDI_MODE, iface_name, + ndi_mac_addr, NET_NAME_UNKNOWN, true); + if (!adapter) { + if (!cfg_nan_get_ndi_mac_randomize(hdd_ctx->psoc)) + wlan_hdd_release_intf_addr(hdd_ctx, ndi_mac_addr); + hdd_err("hdd_open_adapter failed"); + return -EINVAL; + } + + hdd_exit(); + return 0; +} + +int hdd_ndi_start(char *iface_name, uint16_t transaction_id) +{ + int ret; + QDF_STATUS status; + uint8_t op_channel; + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + hdd_enter(); + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return -EINVAL; + } + + adapter = hdd_get_adapter_by_iface_name(hdd_ctx, iface_name); + if (!adapter) { + hdd_err("adapter is null"); + return -EINVAL; + } + + /* create nan vdev */ + status = hdd_init_nan_data_mode(adapter); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("failed to init nan data intf, status :%d", status); + ret = -EFAULT; + goto err_handler; + } + + /* + * Create transaction id is required to be saved since the firmware + * does not honor the transaction id for create request + */ + ucfg_nan_set_ndp_create_transaction_id(adapter->vdev, + transaction_id); + ucfg_nan_set_ndi_state(adapter->vdev, + NAN_DATA_NDI_CREATING_STATE); + + /* + * The NAN data interface has been created at this point. + * Unlike traditional device modes, where the higher application + * layer initiates connect / join / start, the NAN data + * interface does not have any such formal requests. The NDI + * create request is responsible for starting the BSS as well. + * Use the 5GHz Band NAN Social channel for BSS start if target + * supports it, since a 2.4GHz channel will require a DBS HW mode change + * first on a DBS 2x2 MAC target. Use a 2.4 GHz Band NAN Social channel + * if the target is not 5GHz capable. + */ + + if (hdd_is_5g_supported(hdd_ctx)) + op_channel = NAN_SOCIAL_CHANNEL_5GHZ_LOWER_BAND; + else + op_channel = NAN_SOCIAL_CHANNEL_2_4GHZ; + + if (hdd_ndi_start_bss(adapter, op_channel)) { + hdd_err("NDI start bss failed"); + ret = -EFAULT; + goto err_handler; + } + + hdd_exit(); + return 0; + +err_handler: + + /* Start BSS failed, delete the interface */ + hdd_close_ndi(adapter); + return ret; +} + +int hdd_ndi_delete(uint8_t vdev_id, char *iface_name, uint16_t transaction_id) +{ + int ret; + struct hdd_adapter *adapter; + struct hdd_station_ctx *sta_ctx; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return -EINVAL; + } + + /* check if adapter by vdev_id is valid NDI */ + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter || !WLAN_HDD_IS_NDI(adapter)) { + hdd_err("NAN data interface %s is not available", iface_name); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (!sta_ctx) { + hdd_err("sta_ctx is NULL"); + return -EINVAL; + } + + os_if_nan_set_ndp_delete_transaction_id(adapter->vdev, + transaction_id); + os_if_nan_set_ndi_state(adapter->vdev, NAN_DATA_NDI_DELETING_STATE); + + /* Delete the interface */ + ret = __wlan_hdd_del_virtual_intf(hdd_ctx->wiphy, &adapter->wdev); + if (ret) + hdd_err("NDI delete request failed"); + else + hdd_err("NDI delete request successfully issued"); + + return ret; +} + +void hdd_ndi_drv_ndi_create_rsp_handler(uint8_t vdev_id, + struct nan_datapath_inf_create_rsp *ndi_rsp) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + struct hdd_station_ctx *sta_ctx; + struct csr_roam_info *roam_info; + struct bss_description tmp_bss_descp = {0}; + uint16_t ndp_inactivity_timeout = 0; + uint16_t ndp_keep_alive_period; + struct qdf_mac_addr bc_mac_addr = QDF_MAC_ADDR_BCAST_INIT; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("adapter is null"); + return; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (!sta_ctx) { + hdd_err("sta_ctx is null"); + return; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + + if (ndi_rsp->status == QDF_STATUS_SUCCESS) { + hdd_alert("NDI interface successfully created"); + os_if_nan_set_ndp_create_transaction_id(adapter->vdev, 0); + os_if_nan_set_ndi_state(adapter->vdev, + NAN_DATA_NDI_CREATED_STATE); + wlan_hdd_netif_queue_control(adapter, + WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + if (QDF_IS_STATUS_ERROR(cfg_nan_get_ndp_inactivity_timeout( + hdd_ctx->psoc, &ndp_inactivity_timeout))) + hdd_err("Failed to fetch inactivity timeout value"); + + sme_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_NDP_INACTIVITY_TIMEOUT, + ndp_inactivity_timeout, VDEV_CMD); + + if (QDF_IS_STATUS_SUCCESS(cfg_nan_get_ndp_keepalive_period( + hdd_ctx->psoc, + &ndp_keep_alive_period))) + sme_cli_set_command( + adapter->vdev_id, + WMI_VDEV_PARAM_NDP_KEEPALIVE_TIMEOUT, + ndp_keep_alive_period, VDEV_CMD); + } else { + hdd_alert("NDI interface creation failed with reason %d", + ndi_rsp->reason /* create_reason */); + } + + hdd_save_peer(sta_ctx, &bc_mac_addr); + qdf_copy_macaddr(&roam_info->bssid, &bc_mac_addr); + hdd_roam_register_sta(adapter, roam_info, &tmp_bss_descp); + + qdf_mem_free(roam_info); +} + +void hdd_ndi_close(uint8_t vdev_id) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("adapter is null"); + return; + } + + hdd_close_ndi(adapter); +} + +void hdd_ndi_drv_ndi_delete_rsp_handler(uint8_t vdev_id) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + struct hdd_station_ctx *sta_ctx; + struct qdf_mac_addr bc_mac_addr = QDF_MAC_ADDR_BCAST_INIT; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("adapter is null"); + return; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (!sta_ctx) { + hdd_err("sta_ctx is null"); + return; + } + + hdd_delete_peer(sta_ctx, &bc_mac_addr); + + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + /* + * For NAN Data interface, the close session results in the final + * indication to the userspace + */ + if (adapter->device_mode == QDF_NDI_MODE) + hdd_ndp_session_end_handler(adapter); + + complete(&adapter->disconnect_comp_var); +} + +void hdd_ndp_session_end_handler(struct hdd_adapter *adapter) +{ + os_if_nan_ndi_session_end(adapter->vdev); +} + +/** + * hdd_ndp_new_peer_handler() - NDP new peer indication handler + * @adapter: pointer to adapter context + * @ind_params: indication parameters + * + * Return: none + */ +int hdd_ndp_new_peer_handler(uint8_t vdev_id, uint16_t sta_id, + struct qdf_mac_addr *peer_mac_addr, bool fist_peer) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + struct hdd_station_ctx *sta_ctx; + struct bss_description tmp_bss_descp = {0}; + struct csr_roam_info *roam_info; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return -EINVAL; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("adapter is null"); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (!sta_ctx) { + hdd_err("sta_ctx is null"); + return -EINVAL; + } + + /* save peer in ndp ctx */ + if (!hdd_save_peer(sta_ctx, peer_mac_addr)) { + hdd_err("Ndp peer table full. cannot save new peer"); + return -EPERM; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return -ENOMEM; + qdf_copy_macaddr(&roam_info->bssid, peer_mac_addr); + + /* this function is called for each new peer */ + hdd_roam_register_sta(adapter, roam_info, &tmp_bss_descp); + + qdf_copy_macaddr(&roam_info->bssid, peer_mac_addr); + + /* perform following steps for first new peer ind */ + if (fist_peer) { + hdd_debug("Set ctx connection state to connected"); + /* Disable LRO/GRO for NDI Mode */ + if (hdd_ctx->ol_enable && + !ucfg_is_nan_dbs_supported(hdd_ctx->psoc)) { + hdd_debug("Disable LRO/GRO in NDI Mode"); + hdd_disable_rx_ol_in_concurrency(true); + } + + hdd_bus_bw_compute_prev_txrx_stats(adapter); + hdd_bus_bw_compute_timer_start(hdd_ctx); + sta_ctx->conn_info.conn_state = eConnectionState_NdiConnected; + hdd_wmm_connect(adapter, roam_info, eCSR_BSS_TYPE_NDI); + wlan_hdd_netif_queue_control( + adapter, + WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + /* + * This is called only for first peer. So, no.of NDP sessions + * are always 1 + */ + if (!ucfg_is_ndi_dbs_supported(hdd_ctx->psoc)) + hdd_indicate_active_ndp_cnt(hdd_ctx->psoc, vdev_id, 1); + } + qdf_mem_free(roam_info); + return 0; +} + +void hdd_cleanup_ndi(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (sta_ctx->conn_info.conn_state != eConnectionState_NdiConnected) { + hdd_debug("NDI has no NDPs"); + return; + } + sta_ctx->conn_info.conn_state = eConnectionState_NdiDisconnected; + hdd_conn_set_connection_state(adapter, + eConnectionState_NdiDisconnected); + hdd_debug("Stop netif tx queues."); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + hdd_bus_bw_compute_reset_prev_txrx_stats(adapter); + hdd_bus_bw_compute_timer_try_stop(hdd_ctx); + if ((hdd_ctx->ol_enable && + !ucfg_is_nan_dbs_supported(hdd_ctx->psoc)) && + ((policy_mgr_get_connection_count(hdd_ctx->psoc) == 0) || + ((policy_mgr_get_connection_count(hdd_ctx->psoc) == 1) && + (policy_mgr_mode_specific_connection_count( + hdd_ctx->psoc, + PM_STA_MODE, + NULL) == 1)))) { + hdd_debug("Enable LRO/GRO"); + hdd_disable_rx_ol_in_concurrency(false); + } +} + +/** + * hdd_ndp_peer_departed_handler() - Handle NDP peer departed indication + * @adapter: pointer to adapter context + * @ind_params: indication parameters + * + * Return: none + */ +void hdd_ndp_peer_departed_handler(uint8_t vdev_id, uint16_t sta_id, + struct qdf_mac_addr *peer_mac_addr, bool last_peer) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + struct hdd_station_ctx *sta_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("adapter is null"); + return; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (!sta_ctx) { + hdd_err("sta_ctx is null"); + return; + } + + hdd_delete_peer(sta_ctx, peer_mac_addr); + + if (last_peer) { + hdd_debug("No more ndp peers."); + hdd_cleanup_ndi(hdd_ctx, adapter); + qdf_event_set(&adapter->peer_cleanup_done); + /* + * This is called only for last peer. So, no.of NDP sessions + * are always 0 + */ + if (!ucfg_is_ndi_dbs_supported(hdd_ctx->psoc)) + hdd_indicate_active_ndp_cnt(hdd_ctx->psoc, vdev_id, 0); + } +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nan_datapath.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nan_datapath.h new file mode 100644 index 0000000000000000000000000000000000000000..0d0c1b73260ee38b157578ff6f592fc3ade7bb4f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nan_datapath.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_nan_datapath.h + * + * WLAN Host Device Driver nan datapath API specification + */ +#ifndef __WLAN_HDD_NAN_DATAPATH_H +#define __WLAN_HDD_NAN_DATAPATH_H + +struct hdd_context; +struct hdd_config; +struct hdd_adapter; +struct wireless_dev; + +/* NAN Social channels */ +#define NAN_SOCIAL_CHANNEL_2_4GHZ 6 +#define NAN_SOCIAL_CHANNEL_5GHZ_LOWER_BAND 44 +#define NAN_SOCIAL_CHANNEL_5GHZ_UPPER_BAND 149 + +#define NDP_BROADCAST_STAID (0) + +#ifdef WLAN_FEATURE_NAN + +#define MAX_NDI_ADAPTERS 2 + +#define WLAN_HDD_IS_NDI(adapter) ((adapter)->device_mode == QDF_NDI_MODE) + +#define WLAN_HDD_IS_NDI_CONNECTED(adapter) ( \ + eConnectionState_NdiConnected ==\ + (adapter)->session.station.conn_info.conn_state) + +void hdd_nan_datapath_target_config(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *cfg); +void hdd_ndp_event_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint32_t roam_id, eRoamCmdStatus roam_status, + eCsrRoamResult roam_result); +int wlan_hdd_cfg80211_process_ndp_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int data_len); +int hdd_init_nan_data_mode(struct hdd_adapter *adapter); +void hdd_ndp_session_end_handler(struct hdd_adapter *adapter); + +/** + * hdd_cleanup_ndi(): Cleanup NDI state/resources + * @hdd_ctx: HDD context + * @adapter: Pointer to the NDI adapter + * + * Cleanup NDI state/resources allocated when NDPs are created on that NDI. + * + * Return: None + */ + +void hdd_cleanup_ndi(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter); + +/** + * hdd_ndi_start(): Start NDI adapter and create NDI vdev + * @iface_name: NDI interface name + * @transaction_id: Transaction id given by framework to start the NDI. + * Framework expects this in the immediate response when + * the NDI is created by it. + * + * Create NDI move interface and vdev. + * + * Return: 0 upon success + */ +int hdd_ndi_start(char *iface_name, uint16_t transaction_id); +#else +#define WLAN_HDD_IS_NDI(adapter) (false) +#define WLAN_HDD_IS_NDI_CONNECTED(adapter) (false) + +static inline void hdd_nan_datapath_target_config(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *cfg) +{ +} +static inline void hdd_ndp_event_handler(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ +} +static inline int wlan_hdd_cfg80211_process_ndp_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int data_len) +{ + return 0; +} +static inline int hdd_init_nan_data_mode(struct hdd_adapter *adapter) +{ + return 0; +} +static inline void hdd_ndp_session_end_handler(struct hdd_adapter *adapter) +{ +} + +static inline void hdd_cleanup_ndi(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ +} + +static inline int hdd_ndi_start(char *iface_name, uint16_t transaction_id) +{ + return 0; +} +#endif /* WLAN_FEATURE_NAN */ + +enum nan_datapath_state; +struct nan_datapath_inf_create_rsp; + +int hdd_ndi_open(char *iface_name); +int hdd_ndi_delete(uint8_t vdev_id, char *iface_name, uint16_t transaction_id); +void hdd_ndi_close(uint8_t vdev_id); +void hdd_ndi_drv_ndi_create_rsp_handler(uint8_t vdev_id, + struct nan_datapath_inf_create_rsp *ndi_rsp); +void hdd_ndi_drv_ndi_delete_rsp_handler(uint8_t vdev_id); +int hdd_ndp_new_peer_handler(uint8_t vdev_id, uint16_t sta_id, + struct qdf_mac_addr *peer_mac_addr, bool fist_peer); +void hdd_ndp_peer_departed_handler(uint8_t vdev_id, uint16_t sta_id, + struct qdf_mac_addr *peer_mac_addr, bool last_peer); +#endif /* __WLAN_HDD_NAN_DATAPATH_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_napi.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_napi.c new file mode 100644 index 0000000000000000000000000000000000000000..689f978f4b23bdef9ced2b3ec39dc7472316c8ff --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_napi.c @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2015-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_napi.c + * + * WLAN HDD NAPI interface implementation + */ +#include /* get_cpu */ + +#include "wlan_hdd_napi.h" +#include "cds_api.h" /* cds_get_context */ +#include "hif.h" /* hif_map_service...*/ +#include "wlan_hdd_main.h" /* hdd_err/warn... */ +#include "qdf_types.h" /* QDF_MODULE_ID_... */ +#include "ce_api.h" + +/* guaranteed to be initialized to zero/NULL by the standard */ +static struct qca_napi_data *hdd_napi_ctx; + +/** + * hdd_napi_get_all() - return the whole NAPI structure from HIF + * + * Gets to the data structure common to all NAPI instances. + * + * Return: + * NULL : probably NAPI not initialized yet. + * : the address of the whole NAPI structure + */ +struct qca_napi_data *hdd_napi_get_all(void) +{ + struct qca_napi_data *rp = NULL; + struct hif_opaque_softc *hif; + + NAPI_DEBUG("-->"); + + hif = cds_get_context(QDF_MODULE_ID_HIF); + if (unlikely(!hif)) + QDF_ASSERT(hif); /* WARN */ + else + rp = hif_napi_get_all(hif); + + NAPI_DEBUG("<-- [addr=%pK]", rp); + return rp; +} + +/** + * hdd_napi_get_map() - get a copy of napi pipe map + * + * Return: + * uint32_t : copy of pipe map + */ +static uint32_t hdd_napi_get_map(void) +{ + uint32_t map = 0; + + NAPI_DEBUG("-->"); + /* cache once, use forever */ + if (!hdd_napi_ctx) + hdd_napi_ctx = hdd_napi_get_all(); + if (hdd_napi_ctx) + map = hdd_napi_ctx->ce_map; + + NAPI_DEBUG("<-- [map=0x%08x]", map); + return map; +} + +/** + * hdd_napi_create() - creates the NAPI structures for a given netdev + * + * Creates NAPI instances. This function is called + * unconditionally during initialization. It creates + * napi structures through the proper HTC/HIF calls. + * The structures are disabled on creation. + * + * Return: + * single-queue: <0: err, >0=id, 0 (should not happen) + * multi-queue: bitmap of created instances (0: none) + */ +int hdd_napi_create(void) +{ + struct hif_opaque_softc *hif_ctx; + int rc = 0; + struct hdd_context *hdd_ctx; + uint8_t feature_flags = 0; + struct qca_napi_data *napid = hdd_napi_get_all(); + + NAPI_DEBUG("-->"); + + if (!napid) { + hdd_err("unable to retrieve napi structure"); + rc = -EFAULT; + goto exit; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (unlikely(!hif_ctx)) { + QDF_ASSERT(hif_ctx); + rc = -EFAULT; + goto exit; + } + + feature_flags = QCA_NAPI_FEATURE_CPU_CORRECTION | + QCA_NAPI_FEATURE_IRQ_BLACKLISTING | + QCA_NAPI_FEATURE_CORE_CTL_BOOST; + + rc = hif_napi_create(hif_ctx, hdd_napi_poll, + QCA_NAPI_BUDGET, + QCA_NAPI_DEF_SCALE, + feature_flags); + if (rc < 0) { + hdd_err("ERR(%d) creating NAPI instances", + rc); + goto exit; + } + + hdd_debug("napi instances were created. Map=0x%x", rc); + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (unlikely(!hdd_ctx)) { + QDF_ASSERT(0); + rc = -EFAULT; + goto exit; + } + + rc = hdd_napi_event(NAPI_EVT_INI_FILE, + (void *)hdd_ctx->napi_enable); + napid->user_cpu_affin_mask = + hdd_ctx->config->napi_cpu_affinity_mask; + + exit: + NAPI_DEBUG("<-- [rc=%d]", rc); + return rc; +} + +/** + * hdd_napi_destroy() - destroys the NAPI structures for a given netdev + * @force: if set, will force-disable the instance before _del'ing + * + * Destroy NAPI instances. This function is called + * unconditionally during module removal. It destroy + * napi structures through the proper HTC/HIF calls. + * + * Return: + * number of NAPI instances destroyed + */ +int hdd_napi_destroy(int force) +{ + int rc = 0; + int i; + uint32_t hdd_napi_map = hdd_napi_get_map(); + + NAPI_DEBUG("--> (force=%d)", force); + if (hdd_napi_map) { + struct hif_opaque_softc *hif_ctx; + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (unlikely(!hif_ctx)) + QDF_ASSERT(hif_ctx); + else + for (i = 0; i < CE_COUNT_MAX; i++) + if (hdd_napi_map & (0x01 << i)) { + if (0 <= hif_napi_destroy( + hif_ctx, + NAPI_PIPE2ID(i), force)) { + rc++; + hdd_napi_map &= ~(0x01 << i); + } else + hdd_err("cannot destroy napi %d: (pipe:%d), f=%d\n", + i, + NAPI_PIPE2ID(i), force); + } + } else { + struct hif_opaque_softc *hif_ctx; + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + + if (unlikely(!hif_ctx)) + QDF_ASSERT(hif_ctx); + else + rc = hif_napi_cpu_deinit(hif_ctx); + } + + /* if all instances are removed, it is likely that hif_context has been + * removed as well, so the cached value of the napi context also needs + * to be removed + */ + if (force) + QDF_ASSERT(hdd_napi_map == 0); + if (0 == hdd_napi_map) + hdd_napi_ctx = NULL; + + NAPI_DEBUG("<-- [rc=%d]", rc); + return rc; +} + +/** + * hdd_napi_enabled() - checks if NAPI is enabled (for a given id) + * @id: the id of the NAPI to check (any= -1) + * + * Return: + * int: 0 = false (NOT enabled) + * !0 = true (enabbled) + */ +int hdd_napi_enabled(int id) +{ + struct hif_opaque_softc *hif; + int rc = 0; /* NOT enabled */ + + hif = cds_get_context(QDF_MODULE_ID_HIF); + if (unlikely(!hif)) + QDF_ASSERT(hif); /* WARN_ON; rc = 0 */ + else if (-1 == id) + rc = hif_napi_enabled(hif, id); + else + rc = hif_napi_enabled(hif, NAPI_ID2PIPE(id)); + return rc; +} + +/** + * hdd_napi_event() - relay the event detected by HDD to HIF NAPI event handler + * @event: event code + * @data : event-specific auxiliary data + * + * See function documentation in hif_napi.c::hif_napi_event for list of events + * and how each of them is handled. + * + * Return: + * < 0: error code + * = 0: event handled successfully + */ +int hdd_napi_event(enum qca_napi_event event, void *data) +{ + int rc = -EFAULT; /* assume err */ + struct hif_opaque_softc *hif; + + NAPI_DEBUG("-->(event=%d, aux=%pK)", event, data); + + hif = cds_get_context(QDF_MODULE_ID_HIF); + if (unlikely(!hif)) + QDF_ASSERT(hif); + else + rc = hif_napi_event(hif, event, data); + + NAPI_DEBUG("<--[rc=%d]", rc); + return rc; +} + +#if defined HELIUMPLUS && defined MSM_PLATFORM +/** + * hdd_napi_perfd_cpufreq() - set/reset min CPU freq for cores + * @req_state: high/low + * + * Send a message to cnss-daemon through netlink. cnss-daemon, + * in turn, sends a message to perf-daemon. + * If freq > 0, this is a set request. It sets the min frequency of the + * cores of the specified cluster to provided freq value (in KHz). + * If freq == 0, then the freq lock is removed (and frequency returns to + * system default). + * + * Semantical Alert: + * There can be at most one lock active at a time. Each "set" request must + * be followed by a "reset" request. Perfd behaviour is undefined otherwise. + * + * Return: == 0: netlink message sent to cnss-daemon + * < 0: failure to send the message + */ +static int hdd_napi_perfd_cpufreq(enum qca_napi_tput_state req_state) +{ + int rc = 0; + struct wlan_core_minfreq req; + struct hdd_context *hdd_ctx; + + NAPI_DEBUG("-> (%d)", req_state); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (unlikely(!hdd_ctx)) { + hdd_err("cannot get hdd_context"); + rc = -EFAULT; + goto hnpc_ret; + } + + switch (req_state) { + case QCA_NAPI_TPUT_LO: + req.magic = WLAN_CORE_MINFREQ_MAGIC; + req.reserved = 0; /* unused */ + req.coremask = 0; /* not valid */ + req.freq = 0; /* reset */ + break; + case QCA_NAPI_TPUT_HI: + req.magic = WLAN_CORE_MINFREQ_MAGIC; + req.reserved = 0; /* unused */ + req.coremask = 0x0f0; /* perf cluster */ + req.freq = 700; /* KHz */ + break; + default: + hdd_err("invalid req_state (%d)", req_state); + rc = -EINVAL; + goto hnpc_ret; + } /* switch */ + + NAPI_DEBUG("CPU min freq to %d", + (req.freq == 0)?"Resetting":"Setting", req.freq); + /* the following service function returns void */ + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_CORE_MINFREQ, + &req, sizeof(struct wlan_core_minfreq)); +hnpc_ret: + NAPI_DEBUG("<--[rc=%d]", rc); + return rc; +} + +/** + * hdd_napi_apply_throughput_policy() - implement the throughput action policy + * @hddctx: HDD context + * @tx_packets: number of tx packets in the last interval + * @rx_packets: number of rx packets in the last interval + * + * Called by hdd_bus_bw_compute_cb, checks the number of packets in the last + * interval, and determines the desired napi throughput state (HI/LO). If + * the desired state is different from the current, then it invokes the + * event handler to switch to the desired state. + * + * The policy implementation is limited to this function and + * The current policy is: determine the NAPI mode based on the condition: + * (total number of packets > medium threshold) + * - tx packets are included because: + * a- tx-completions arrive at one of the rx CEs + * b- in TCP, a lof of TX implies ~(tx/2) rx (ACKs) + * c- so that we can use the same normalized criteria in ini file + * - medium-threshold (default: 500 packets / 10 ms), because + * we would like to be more reactive. + * + * Return: 0 : no action taken, or action return code + * !0: error, or action error code + */ +static int napi_tput_policy_delay; +int hdd_napi_apply_throughput_policy(struct hdd_context *hddctx, + uint64_t tx_packets, + uint64_t rx_packets) +{ + int rc = 0; + uint64_t packets = tx_packets + rx_packets; + enum qca_napi_tput_state req_state; + struct qca_napi_data *napid = hdd_napi_get_all(); + int enabled; + + NAPI_DEBUG("-->%s(tx=%lld, rx=%lld)", __func__, tx_packets, rx_packets); + + if (unlikely(napi_tput_policy_delay < 0)) + napi_tput_policy_delay = 0; + if (napi_tput_policy_delay > 0) { + NAPI_DEBUG("%s: delaying policy; delay-count=%d", + __func__, napi_tput_policy_delay); + napi_tput_policy_delay--; + + /* make sure the next timer call calls us */ + hddctx->cur_vote_level = -1; + + return rc; + } + + if (!napid) { + hdd_err("ERR: napid NULL"); + return rc; + } + + enabled = hdd_napi_enabled(HDD_NAPI_ANY); + if (!enabled) { + hdd_err("ERR: napi not enabled"); + return rc; + } + + if (packets > hddctx->config->bus_bw_high_threshold) + req_state = QCA_NAPI_TPUT_HI; + else + req_state = QCA_NAPI_TPUT_LO; + + if (req_state != napid->napi_mode) { + /* [re]set the floor frequency of high cluster */ + rc = hdd_napi_perfd_cpufreq(req_state); + /* blacklist/boost_mode on/off */ + rc = hdd_napi_event(NAPI_EVT_TPUT_STATE, (void *)req_state); + } + return rc; +} + +/** + * hdd_napi_serialize() - serialize all NAPI activities + * @is_on: 1="serialize" or 0="de-serialize" + * + * Start/stop "serial-NAPI-mode". + * NAPI serial mode describes a state where all NAPI operations are forced to be + * run serially. This is achieved by ensuring all NAPI instances are run on the + * same CPU, so forced to be serial. + * NAPI life-cycle: + * - Interrupt is received for a given CE. + * - In the ISR, the interrupt is masked and corresponding NAPI instance + * is scheduled, to be run as a bottom-half. + * - Bottom-half starts with a poll call (by the net_rx softirq). There may be + * one of more subsequent calls until the work is complete. + * - Once the work is complete, the poll handler enables the interrupt and + * the cycle re-starts. + * + * Return: <0: error-code (operation failed) + * =0: success + * >0: status (not used) + */ +int hdd_napi_serialize(int is_on) +{ + int rc; + struct hdd_context *hdd_ctx; +#define POLICY_DELAY_FACTOR (1) + rc = hif_napi_serialize(cds_get_context(QDF_MODULE_ID_HIF), is_on); + if ((rc == 0) && (is_on == 0)) { + /* apply throughput policy after one timeout */ + napi_tput_policy_delay = POLICY_DELAY_FACTOR; + + /* make sure that bus_bandwidth trigger is executed */ + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (hdd_ctx) + hdd_ctx->cur_vote_level = -1; + + } + return rc; +} +#endif /* HELIUMPLUS && MSM_PLATFORM */ + +/** + * hdd_napi_poll() - NAPI poll function + * @napi : pointer to NAPI struct + * @budget: the pre-declared budget + * + * Implementation of poll function. This function is called + * by kernel during softirq processing. + * + * NOTE FOR THE MAINTAINER: + * Make sure this is very close to the ce_tasklet code. + * + * Return: + * int: the amount of work done ( <= budget ) + */ +int hdd_napi_poll(struct napi_struct *napi, int budget) +{ + return hif_napi_poll(cds_get_context(QDF_MODULE_ID_HIF), napi, budget); +} + +/** + * hdd_display_napi_stats() - print NAPI stats + * + * Return: == 0: success; !=0: failure + */ +int hdd_display_napi_stats(void) +{ + int i, j, k, n; /* NAPI, CPU, bucket indices, bucket buf write index*/ + int max; + struct qca_napi_data *napid; + struct qca_napi_info *napii; + struct qca_napi_stat *napis; + /* + * Expecting each NAPI bucket item to need at max 5 numerals + space for + * formatting. For example "10000 " Thus the array needs to have + * (5 + 1) * QCA_NAPI_NUM_BUCKETS bytes of space. Leaving one space at + * the end of the "buf" arrary for end of string char. + */ + char buf[6 * QCA_NAPI_NUM_BUCKETS + 1] = {'\0'}; + + napid = hdd_napi_get_all(); + if (!napid) { + hdd_err("%s unable to retrieve napi structure", __func__); + return -EFAULT; + } + hdd_nofl_info("[NAPI %u][BL %d]: scheds polls comps done t-lim p-lim corr max_time napi-buckets(%d)", + napid->napi_mode, + hif_napi_cpu_blacklist(napid, BLACKLIST_QUERY), + QCA_NAPI_NUM_BUCKETS); + + for (i = 0; i < CE_COUNT_MAX; i++) + if (napid->ce_map & (0x01 << i)) { + napii = napid->napis[i]; + if (!napii) + continue; + + for (j = 0; j < num_possible_cpus(); j++) { + napis = &(napii->stats[j]); + n = 0; + max = sizeof(buf); + for (k = 0; k < QCA_NAPI_NUM_BUCKETS; k++) { + n += scnprintf( + buf + n, max - n, + " %d", + napis->napi_budget_uses[k]); + } + + if (napis->napi_schedules != 0) + hdd_nofl_info("NAPI[%2d]CPU[%d]: %7d %7d %7d %7d %5d %5d %5d %9llu %s", + i, j, + napis->napi_schedules, + napis->napi_polls, + napis->napi_completes, + napis->napi_workdone, + napis->time_limit_reached, + napis-> + rxpkt_thresh_reached, + napis->cpu_corrected, + napis->napi_max_poll_time, + buf); + } + } + + hif_napi_stats(napid); + return 0; +} + +/** + * hdd_clear_napi_stats() - clear NAPI stats + * + * Return: == 0: success; !=0: failure + */ +int hdd_clear_napi_stats(void) +{ + int i, j; + struct qca_napi_data *napid; + struct qca_napi_info *napii; + struct qca_napi_stat *napis; + + napid = hdd_napi_get_all(); + if (!napid) { + hdd_err("%s unable to retrieve napi structure", __func__); + return -EFAULT; + } + + for (i = 0; i < CE_COUNT_MAX; i++) + if (napid->ce_map & (0x01 << i)) { + napii = napid->napis[i]; + for (j = 0; j < NR_CPUS; j++) { + napis = &(napii->stats[j]); + qdf_mem_zero(napis, + sizeof(struct qca_napi_stat)); + } + } + + return 0; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nud_tracking.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nud_tracking.c new file mode 100644 index 0000000000000000000000000000000000000000..51eaac61234ee4d84ff3968d66f60572524c8ca2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nud_tracking.c @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains nud event tracking main function definitions + */ + +#include "osif_sync.h" +#include "wlan_hdd_main.h" +#include "wlan_blm_ucfg_api.h" +#include "hdd_dp_cfg.h" + +void hdd_nud_set_gateway_addr(struct hdd_adapter *adapter, + struct qdf_mac_addr gw_mac_addr) +{ + qdf_mem_copy(adapter->nud_tracking.gw_mac_addr.bytes, + gw_mac_addr.bytes, + sizeof(struct qdf_mac_addr)); + adapter->nud_tracking.is_gw_updated = true; +} + +void hdd_nud_incr_gw_rx_pkt_cnt(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr) +{ + if (!adapter->nud_tracking.is_gw_rx_pkt_track_enabled) + return; + + if (!adapter->nud_tracking.is_gw_updated) + return; + + if (qdf_is_macaddr_equal(&adapter->nud_tracking.gw_mac_addr, + mac_addr)) + qdf_atomic_inc(&adapter + ->nud_tracking.tx_rx_stats.gw_rx_packets); +} + +void hdd_nud_flush_work(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (adapter->device_mode == QDF_STA_MODE && + hdd_ctx->config->enable_nud_tracking) { + hdd_debug("Flush the NUD work"); + qdf_disable_work(&adapter->nud_tracking.nud_event_work); + } +} + +void hdd_nud_deinit_tracking(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (adapter->device_mode == QDF_STA_MODE && + hdd_ctx->config->enable_nud_tracking) { + hdd_debug("DeInitialize the NUD tracking"); + qdf_destroy_work(NULL, &adapter->nud_tracking.nud_event_work); + } +} + +void hdd_nud_ignore_tracking(struct hdd_adapter *adapter, bool ignoring) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (adapter->device_mode == QDF_STA_MODE && + hdd_ctx->config->enable_nud_tracking) + adapter->nud_tracking.ignore_nud_tracking = ignoring; +} + +void hdd_nud_reset_tracking(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (adapter->device_mode == QDF_STA_MODE && + hdd_ctx->config->enable_nud_tracking) { + hdd_debug("Reset the NUD tracking"); + + qdf_zero_macaddr(&adapter->nud_tracking.gw_mac_addr); + adapter->nud_tracking.is_gw_updated = false; + qdf_mem_zero(&adapter->nud_tracking.tx_rx_stats, + sizeof(struct hdd_nud_tx_rx_stats)); + + adapter->nud_tracking.curr_state = NUD_NONE; + qdf_atomic_set(&adapter + ->nud_tracking.tx_rx_stats.gw_rx_packets, 0); + } +} + +/** + * hdd_nud_stats_info() - display wlan NUD stats info + * @hdd_adapter: Pointer to hdd adapter + * + * Return: None + */ +static void hdd_nud_stats_info(struct hdd_adapter *adapter) +{ + struct netdev_queue *txq; + int i = 0; + + hdd_debug("**** NUD STATS: ****"); + hdd_debug("NUD Probe Tx : %d", + adapter->nud_tracking.tx_rx_stats.pre_tx_packets); + hdd_debug("NUD Probe Ack : %d", + adapter->nud_tracking.tx_rx_stats.pre_tx_acked); + hdd_debug("NUD Probe Rx : %d", + adapter->nud_tracking.tx_rx_stats.pre_rx_packets); + hdd_debug("NUD Failure Tx : %d", + adapter->nud_tracking.tx_rx_stats.post_tx_packets); + hdd_debug("NUD Failure Ack : %d", + adapter->nud_tracking.tx_rx_stats.post_tx_acked); + hdd_debug("NUD Failure Rx : %d", + adapter->nud_tracking.tx_rx_stats.post_rx_packets); + hdd_debug("NUD Gateway Rx : %d", + qdf_atomic_read(&adapter + ->nud_tracking.tx_rx_stats.gw_rx_packets)); + + hdd_debug("carrier state: %d", netif_carrier_ok(adapter->dev)); + + for (i = 0; i < NUM_TX_QUEUES; i++) { + txq = netdev_get_tx_queue(adapter->dev, i); + hdd_debug("Queue: %d status: %d txq->trans_start: %lu", + i, netif_tx_queue_stopped(txq), txq->trans_start); + } + + hdd_debug("Current pause_map value %x", adapter->pause_map); +} + +/** + * hdd_nud_capture_stats() - capture wlan NUD stats + * @hdd_adapter: Pointer to hdd adapter + * @nud_state: NUD state for which stats to capture + * + * Return: None + */ +static void hdd_nud_capture_stats(struct hdd_adapter *adapter, + uint8_t nud_state) +{ + switch (nud_state) { + case NUD_INCOMPLETE: + case NUD_PROBE: + adapter->nud_tracking.tx_rx_stats.pre_tx_packets = + adapter->stats.tx_packets; + adapter->nud_tracking.tx_rx_stats.pre_rx_packets = + adapter->stats.rx_packets; + adapter->nud_tracking.tx_rx_stats.pre_tx_acked = + hdd_txrx_get_tx_ack_count(adapter); + break; + case NUD_FAILED: + adapter->nud_tracking.tx_rx_stats.post_tx_packets = + adapter->stats.tx_packets; + adapter->nud_tracking.tx_rx_stats.post_rx_packets = + adapter->stats.rx_packets; + adapter->nud_tracking.tx_rx_stats.post_tx_acked = + hdd_txrx_get_tx_ack_count(adapter); + break; + default: + break; + } +} + +/** + * hdd_nud_honour_failure() - check if nud failure to be honored + * @hdd_adapter: Pointer to hdd_adapter + * + * Return: true if nud failure to be honored, else false. + */ +static bool hdd_nud_honour_failure(struct hdd_adapter *adapter) +{ + uint32_t tx_transmitted; + uint32_t tx_acked; + uint32_t gw_rx_pkt; + + tx_transmitted = adapter->nud_tracking.tx_rx_stats.post_tx_packets - + adapter->nud_tracking.tx_rx_stats.pre_tx_packets; + tx_acked = adapter->nud_tracking.tx_rx_stats.post_tx_acked - + adapter->nud_tracking.tx_rx_stats.pre_tx_acked; + gw_rx_pkt = qdf_atomic_read(&adapter + ->nud_tracking.tx_rx_stats.gw_rx_packets); + + if (!tx_transmitted || !tx_acked || !gw_rx_pkt) { + hdd_debug("NUD_FAILURE_HONORED [mac:"QDF_MAC_ADDR_FMT"]", + QDF_MAC_ADDR_REF(adapter->nud_tracking.gw_mac_addr.bytes)); + hdd_nud_stats_info(adapter); + return true; + } + hdd_debug("NUD_FAILURE_NOT_HONORED [mac:"QDF_MAC_ADDR_FMT"]", + QDF_MAC_ADDR_REF(adapter->nud_tracking.gw_mac_addr.bytes)); + hdd_nud_stats_info(adapter); + return false; +} + +/** + * hdd_nud_set_tracking() - set the NUD tracking info + * @hdd_adapter: Pointer to hdd_adapter + * @nud_state: Current NUD state to set + * @capture_enabled: GW Rx packet to be capture or not + * + * Return: None + */ +static void hdd_nud_set_tracking(struct hdd_adapter *adapter, + uint8_t nud_state, + bool capture_enabled) +{ + adapter->nud_tracking.curr_state = nud_state; + qdf_atomic_set(&adapter->nud_tracking.tx_rx_stats.gw_rx_packets, 0); + adapter->nud_tracking.is_gw_rx_pkt_track_enabled = capture_enabled; +} + +static void +hdd_handle_nud_fail_sta(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct reject_ap_info ap_info; + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + qdf_mutex_acquire(&adapter->disconnection_status_lock); + if (adapter->disconnection_in_progress) { + qdf_mutex_release(&adapter->disconnection_status_lock); + hdd_debug("Disconnect is in progress"); + return; + } + qdf_mutex_release(&adapter->disconnection_status_lock); + + if (hdd_is_roaming_in_progress(hdd_ctx)) { + hdd_debug("Roaming already in progress, cannot trigger roam."); + return; + } + + hdd_debug("nud fail detected, try roaming to better BSSID, vdev id: %d", + adapter->vdev_id); + + ap_info.bssid = sta_ctx->conn_info.bssid; + ap_info.reject_ap_type = DRIVER_AVOID_TYPE; + ap_info.reject_reason = REASON_NUD_FAILURE; + ap_info.source = ADDED_BY_DRIVER; + ucfg_blm_add_bssid_to_reject_list(hdd_ctx->pdev, &ap_info); + + if (roaming_offload_enabled(hdd_ctx)) + sme_roam_invoke_nud_fail(hdd_ctx->mac_handle, + adapter->vdev_id); +} + +static void +hdd_handle_nud_fail_non_sta(struct hdd_adapter *adapter) +{ + int status; + + qdf_mutex_acquire(&adapter->disconnection_status_lock); + if (adapter->disconnection_in_progress) { + qdf_mutex_release(&adapter->disconnection_status_lock); + hdd_debug("Disconnect is in progress"); + return; + } + + adapter->disconnection_in_progress = true; + qdf_mutex_release(&adapter->disconnection_status_lock); + + hdd_debug("Disconnecting vdev with vdev id: %d", + adapter->vdev_id); + /* Issue Disconnect */ + status = wlan_hdd_disconnect(adapter, eCSR_DISCONNECT_REASON_DEAUTH, + eSIR_MAC_GATEWAY_REACHABILITY_FAILURE); + if (0 != status) { + hdd_err("wlan_hdd_disconnect failed, status: %d", + status); + hdd_set_disconnect_status(adapter, false); + } +} + +#ifdef WLAN_NUD_TRACKING +static bool +hdd_is_roam_after_nud_enabled(struct hdd_config *config) +{ + if (config->enable_nud_tracking == ROAM_AFTER_NUD_FAIL || + config->enable_nud_tracking == DISCONNECT_AFTER_ROAM_FAIL) + return true; + + return false; +} +#else +static bool +hdd_is_roam_after_nud_enabled(struct hdd_config *config) +{ + return false; +} +#endif + +/** + * __hdd_nud_failure_work() - work for nud event + * @adapter: Pointer to hdd_adapter + * + * Return: None + */ +static void __hdd_nud_failure_work(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + eConnectionState conn_state; + int status; + + hdd_enter(); + + status = hdd_validate_adapter(adapter); + if (status) + return; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return; + + conn_state = (WLAN_HDD_GET_STATION_CTX_PTR(adapter)) + ->conn_info.conn_state; + + if (eConnectionState_Associated != conn_state) { + hdd_debug("Not in Connected State"); + return; + } + if (adapter->nud_tracking.curr_state != NUD_FAILED) { + hdd_debug("Not in NUD_FAILED state"); + return; + } + + if (adapter->device_mode == QDF_STA_MODE && + hdd_is_roam_after_nud_enabled(hdd_ctx->config)) { + hdd_handle_nud_fail_sta(hdd_ctx, adapter); + return; + } + hdd_handle_nud_fail_non_sta(adapter); + + hdd_exit(); +} + +/** + * hdd_nud_failure_work() - work for nud event + * @data: Pointer to hdd_adapter + * + * Return: None + */ +static void hdd_nud_failure_work(void *data) +{ + struct hdd_adapter *adapter = data; + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(adapter->dev, &vdev_sync)) + return; + + __hdd_nud_failure_work(adapter); + + osif_vdev_sync_op_stop(vdev_sync); +} + +void hdd_nud_init_tracking(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (adapter->device_mode == QDF_STA_MODE && + hdd_ctx->config->enable_nud_tracking) { + hdd_debug("Initialize the NUD tracking"); + + qdf_zero_macaddr(&adapter->nud_tracking.gw_mac_addr); + qdf_mem_zero(&adapter->nud_tracking.tx_rx_stats, + sizeof(struct hdd_nud_tx_rx_stats)); + + adapter->nud_tracking.curr_state = NUD_NONE; + adapter->nud_tracking.ignore_nud_tracking = false; + adapter->nud_tracking.is_gw_updated = false; + + qdf_atomic_init(&adapter + ->nud_tracking.tx_rx_stats.gw_rx_packets); + qdf_create_work(0, &adapter->nud_tracking.nud_event_work, + hdd_nud_failure_work, adapter); + } +} + +/** + * hdd_nud_process_failure_event() - processing NUD_FAILED event + * @hdd_adapter: Pointer to hdd_adapter + * + * Return: None + */ +static void hdd_nud_process_failure_event(struct hdd_adapter *adapter) +{ + uint8_t curr_state; + + curr_state = adapter->nud_tracking.curr_state; + if (curr_state == NUD_PROBE || curr_state == NUD_INCOMPLETE) { + hdd_nud_capture_stats(adapter, NUD_FAILED); + if (hdd_nud_honour_failure(adapter)) { + adapter->nud_tracking.curr_state = NUD_FAILED; + qdf_sched_work(0, &adapter + ->nud_tracking.nud_event_work); + } else { + hdd_debug("NUD_START [0x%x]", NUD_INCOMPLETE); + hdd_nud_capture_stats(adapter, NUD_INCOMPLETE); + hdd_nud_set_tracking(adapter, NUD_INCOMPLETE, true); + } + } else { + hdd_debug("NUD FAILED -> Current State [0x%x]", curr_state); + } +} + +/** + * hdd_nud_filter_netevent() - filter netevents for STA interface + * @neighbour: Pointer to neighbour + * + * Return: None + */ +static void hdd_nud_filter_netevent(struct neighbour *neigh) +{ + int status; + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + eConnectionState conn_state; + const struct net_device *netdev = neigh->dev; + + hdd_enter(); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return; + + adapter = hdd_get_adapter_by_macaddr(hdd_ctx, netdev->dev_addr); + + if (!adapter) + return; + + status = hdd_validate_adapter(adapter); + if (status) + return; + + if (adapter->nud_tracking.ignore_nud_tracking) { + hdd_debug("NUD Tracking is Disabled"); + return; + } + + if (!adapter->nud_tracking.is_gw_updated) + return; + + if (adapter->device_mode != QDF_STA_MODE) + return; + + conn_state = (WLAN_HDD_GET_STATION_CTX_PTR(adapter)) + ->conn_info.conn_state; + + if (eConnectionState_Associated != conn_state) { + hdd_debug("Not in Connected State"); + return; + } + + if (!qdf_is_macaddr_equal(&adapter->nud_tracking.gw_mac_addr, + (struct qdf_mac_addr *)&neigh->ha[0])) + return; + + switch (neigh->nud_state) { + case NUD_PROBE: + case NUD_INCOMPLETE: + hdd_debug("NUD_START [0x%x]", neigh->nud_state); + hdd_nud_capture_stats(adapter, neigh->nud_state); + hdd_nud_set_tracking(adapter, + neigh->nud_state, + true); + break; + + case NUD_REACHABLE: + hdd_debug("NUD_REACHABLE [0x%x]", neigh->nud_state); + hdd_nud_set_tracking(adapter, NUD_NONE, false); + break; + + case NUD_FAILED: + hdd_debug("NUD_FAILED [0x%x]", neigh->nud_state); + /* + * This condition is to handle the scenario where NUD_FAILED + * events are received without any NUD_PROBE/INCOMPLETE event + * post roaming. Nud state is set to NONE as part of roaming. + * NUD_FAILED is not honored when the curr state is any state + * other than NUD_PROBE/INCOMPLETE so post roaming, nud state + * is moved to NUD_PROBE to honor future NUD_FAILED events. + */ + if (adapter->nud_tracking.curr_state == NUD_NONE) { + hdd_nud_capture_stats(adapter, NUD_PROBE); + hdd_nud_set_tracking(adapter, NUD_PROBE, true); + } else { + hdd_nud_process_failure_event(adapter); + } + break; + default: + hdd_debug("NUD Event For Other State [0x%x]", + neigh->nud_state); + break; + } + hdd_exit(); +} + +/** + * __hdd_nud_netevent_cb() - netevent callback + * @neighbor: neighbor used in the nud event + * + * Return: None + */ +static void __hdd_nud_netevent_cb(struct neighbour *neighbor) +{ + hdd_enter(); + hdd_nud_filter_netevent(neighbor); + hdd_exit(); +} + +/** + * hdd_nud_netevent_cb() - netevent callback + * @nb: Pointer to notifier block + * @event: Net Event triggered + * @data: Pointer to neighbour struct + * + * Callback for netevent + * + * Return: 0 on success + */ +static int hdd_nud_netevent_cb(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct neighbour *neighbor = data; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(neighbor->dev, &vdev_sync); + if (errno) + return errno; + + switch (event) { + case NETEVENT_NEIGH_UPDATE: + __hdd_nud_netevent_cb(neighbor); + break; + default: + break; + } + + osif_vdev_sync_op_stop(vdev_sync); + + return 0; +} + +static struct notifier_block wlan_netevent_nb = { + .notifier_call = hdd_nud_netevent_cb +}; + +int hdd_nud_register_netevent_notifier(struct hdd_context *hdd_ctx) +{ + int ret = 0; + + if (hdd_ctx->config->enable_nud_tracking) { + ret = register_netevent_notifier(&wlan_netevent_nb); + if (!ret) + hdd_debug("Registered netevent notifier"); + } + return ret; +} + +void hdd_nud_unregister_netevent_notifier(struct hdd_context *hdd_ctx) +{ + int ret; + + if (hdd_ctx->config->enable_nud_tracking) { + ret = unregister_netevent_notifier(&wlan_netevent_nb); + if (!ret) + hdd_debug("Unregistered netevent notifier"); + } +} + +void hdd_nud_indicate_roam(struct hdd_adapter *adapter) +{ + hdd_nud_set_tracking(adapter, NUD_NONE, false); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nud_tracking.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nud_tracking.h new file mode 100644 index 0000000000000000000000000000000000000000..446346ba61193dcc0088ee4011bd236b440b30e1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_nud_tracking.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: contains nud event tracking function declarations + */ + +#ifndef _WLAN_NUD_TRACKING_H_ +#define _WLAN_NUD_TRACKING_H_ + +#ifdef WLAN_NUD_TRACKING + +/** + * struct hdd_nud_tx_rx_stats - Capture tx and rx count during NUD tracking + * @pre_tx_packets: Number of tx packets at NUD_PROBE event + * @pre_tx_acked: Number of tx acked at NUD_PROBE event + * @pre_rx_packets: Number of rx packets at NUD_PROBE event + * @post_tx_packets: Number of tx packets at NUD_FAILED event + * @post_tx_acked: Number of tx acked at NUD_FAILED event + * @post_rx_packets: Number of rx packets at NUD_FAILED event + * @gw_rx_packets: Number of rx packets from the registered gateway + * during the period from NUD_PROBE to NUD_FAILED + */ +struct hdd_nud_tx_rx_stats { + uint32_t pre_tx_packets; + uint32_t pre_tx_acked; + uint32_t pre_rx_packets; + uint32_t post_tx_packets; + uint32_t post_tx_acked; + uint32_t post_rx_packets; + qdf_atomic_t gw_rx_packets; +}; + + /** + * struct hdd_nud_tracking_info - structure to keep track for NUD information + * @curr_state: current state of NUD machine + * @ignore_nud_tracking: true if nud tracking is not required else false + * @tx_rx_stats: Number of packets during NUD tracking + * @gw_mac_addr: gateway mac address for which NUD events are tracked + * @nud_event_work: work to be scheduled during NUD_FAILED + * @is_gw_rx_pkt_track_enabled: true if rx pkt capturing is enabled for GW, + * else false + * @is_gw_updated: true if GW is updated for NUD Tracking + */ +struct hdd_nud_tracking_info { + uint8_t curr_state; + bool ignore_nud_tracking; + struct hdd_nud_tx_rx_stats tx_rx_stats; + struct qdf_mac_addr gw_mac_addr; + qdf_work_t nud_event_work; + bool is_gw_rx_pkt_track_enabled; + bool is_gw_updated; +}; + +/** + * hdd_nud_set_gateway_addr() - set gateway mac address + * @adapter: Pointer to adapter + * @gw_mac_addr: mac address to be set + * + * Return: none + */ +void hdd_nud_set_gateway_addr(struct hdd_adapter *adapter, + struct qdf_mac_addr gw_mac_addr); + +/** + * hdd_nud_incr_gw_rx_pkt_cnt() - Increment rx count for gateway + * @adapter: Pointer to adapter + * @mac_addr: Gateway mac address + * + * Return: None + */ +void hdd_nud_incr_gw_rx_pkt_cnt(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr); + +/** + * hdd_nud_init_tracking() - initialize NUD tracking + * @hdd_adapter: Pointer to hdd adapter + * + * Return: None + */ +void hdd_nud_init_tracking(struct hdd_adapter *adapter); + +/** + * hdd_nud_reset_tracking() - reset NUD tracking + * @hdd_adapter: Pointer to hdd adapter + * + * Return: None + */ +void hdd_nud_reset_tracking(struct hdd_adapter *adapter); + +/** + * hdd_nud_deinit_tracking() - deinitialize NUD tracking + * @hdd_adapter: Pointer to hdd adapter + * + * Return: None + */ +void hdd_nud_deinit_tracking(struct hdd_adapter *adapter); + +/** + * hdd_nud_ignore_tracking() - set/reset nud trackig status + * @data: Pointer to hdd_adapter + * @ignoring: Ignore status to set + * + * Return: None + */ +void hdd_nud_ignore_tracking(struct hdd_adapter *adapter, + bool ignoring); + +/** + * hdd_nud_register_netevent_notifier - Register netevent notifiers. + * @hdd_ctx: HDD context + * + * Register netevent notifiers. + * + * Return: 0 on success and errno on failure + */ +int hdd_nud_register_netevent_notifier(struct hdd_context *hdd_ctx); + +/** + * hdd_nud_unregister_netevent_notifier - Unregister netevent notifiers. + * @hdd_ctx: HDD context + * + * Unregister netevent notifiers. + * + * Return: None + */ +void hdd_nud_unregister_netevent_notifier(struct hdd_context *hdd_ctx); + +/** + * hdd_nud_flush_work() - flush pending nud work + * @adapter: Pointer to hdd adapter + * + * Return: None + */ +void hdd_nud_flush_work(struct hdd_adapter *adapter); + +/** + * hdd_nud_indicate_roam() - reset NUD when roaming happens + * @adapter: Pointer to hdd adapter + * + * Return: None + */ +void hdd_nud_indicate_roam(struct hdd_adapter *adapter); + +#else +static inline void hdd_nud_set_gateway_addr(struct hdd_adapter *adapter, + struct qdf_mac_addr gw_mac_addr) +{ +} + +static inline void hdd_nud_incr_gw_rx_pkt_cnt(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr) +{ +} + +static inline void hdd_nud_init_tracking(struct hdd_adapter *adapter) +{ +} + +static inline void hdd_nud_reset_tracking(struct hdd_adapter *adapter) +{ +} + +static inline void hdd_nud_deinit_tracking(struct hdd_adapter *adapter) +{ +} + +static inline void hdd_nud_ignore_tracking(struct hdd_adapter *adapter, + bool status) +{ +} + +static inline int +hdd_nud_register_netevent_notifier(struct hdd_context *hdd_ctx) +{ + return 0; +} + +static inline void +hdd_nud_unregister_netevent_notifier(struct hdd_context *hdd_ctx) +{ +} + +static inline void +hdd_nud_flush_work(struct hdd_adapter *adapter) +{ +} + +static inline void +hdd_nud_indicate_roam(struct hdd_adapter *adapter) +{ +} +#endif /* WLAN_NUD_TRACKING */ +#endif /* end of _WLAN_NUD_TRACKING_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_object_manager.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_object_manager.c new file mode 100644 index 0000000000000000000000000000000000000000..fa7a5206d3522a8c7065213c37145dc90de92bf3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_object_manager.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: HDD object manager API source file to create/destroy PSOC, + * PDEV, VDEV and PEER objects. + */ + +#include +#include +#include +#include +#include + +#define LOW_2GHZ_FREQ 2312 +#define HIGH_2GHZ_FREQ 2732 +#define LOW_5GHZ_FREQ 4912 + +#ifdef CONFIG_BAND_6GHZ +#define HIGH_5GHZ_FREQ 7200 +#else +#define HIGH_5GHZ_FREQ 5930 +#endif + +#define HIGH_5GHZ_FREQ_NO_6GHZ 5930 + +static void hdd_init_pdev_os_priv(struct hdd_context *hdd_ctx, + struct pdev_osif_priv *os_priv) +{ + /* Initialize the OS private structure*/ + os_priv->wiphy = hdd_ctx->wiphy; + os_priv->legacy_osif_priv = hdd_ctx; + wlan_cfg80211_scan_priv_init(hdd_ctx->pdev); + os_if_spectral_netlink_init(hdd_ctx->pdev); +} + +static void hdd_deinit_pdev_os_priv(struct wlan_objmgr_pdev *pdev) +{ + os_if_spectral_netlink_deinit(pdev); + wlan_cfg80211_scan_priv_deinit(pdev); +} +static void hdd_init_psoc_qdf_ctx(struct wlan_objmgr_psoc *psoc) +{ + qdf_device_t qdf_ctx; + + qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_ctx) { + hdd_err("qdf ctx is null, can't set to soc object"); + return; + } + + wlan_psoc_set_qdf_dev(psoc, qdf_ctx); +} + +int hdd_objmgr_create_and_store_psoc(struct hdd_context *hdd_ctx, + uint8_t psoc_id) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + + psoc = wlan_objmgr_psoc_obj_create(psoc_id, WLAN_DEV_OL); + if (!psoc) + return -ENOMEM; + + status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_HDD_ID_OBJ_MGR); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to acquire psoc ref; status:%d", status); + QDF_BUG(false); + goto psoc_destroy; + } + + hdd_init_psoc_qdf_ctx(psoc); + hdd_ctx->psoc = psoc; + + return 0; + +psoc_destroy: + wlan_objmgr_psoc_obj_delete(psoc); + + return qdf_status_to_os_return(status); +} + +int hdd_objmgr_release_and_destroy_psoc(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + + hdd_ctx->psoc = NULL; + + QDF_BUG(psoc); + if (!psoc) + return -EINVAL; + + wlan_objmgr_print_ref_all_objects_per_psoc(psoc); + + status = wlan_objmgr_psoc_obj_delete(psoc); + wlan_objmgr_psoc_release_ref(psoc, WLAN_HDD_ID_OBJ_MGR); + + return qdf_status_to_os_return(status); +} + +void hdd_objmgr_update_tgt_max_vdev_psoc(struct hdd_context *hdd_ctx, + uint8_t max_vdev) +{ + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + + if (!psoc) { + hdd_err("Psoc NULL"); + return; + } + + wlan_psoc_set_max_vdev_count(psoc, max_vdev); +} + +int hdd_objmgr_create_and_store_pdev(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + struct wlan_objmgr_pdev *pdev; + struct pdev_osif_priv *priv; + struct wlan_psoc_host_hal_reg_capabilities_ext *reg_cap_ptr; + + if (!psoc) { + hdd_err("Psoc NULL"); + return -EINVAL; + } + + priv = qdf_mem_malloc(sizeof(*priv)); + if (!priv) { + hdd_err("pdev os obj create failed"); + return -ENOMEM; + } + + reg_cap_ptr = ucfg_reg_get_hal_reg_cap(psoc); + if (!reg_cap_ptr) { + hdd_err("Failed to get reg capability"); + status = QDF_STATUS_E_INVAL; + goto free_priv; + } + reg_cap_ptr->phy_id = 0; + reg_cap_ptr->low_2ghz_chan = LOW_2GHZ_FREQ; + reg_cap_ptr->high_2ghz_chan = HIGH_2GHZ_FREQ; + reg_cap_ptr->low_5ghz_chan = LOW_5GHZ_FREQ; + reg_cap_ptr->high_5ghz_chan = HIGH_5GHZ_FREQ; + + if (!wlan_reg_is_6ghz_supported(psoc)) { + hdd_debug("disabling 6ghz channels"); + reg_cap_ptr->high_5ghz_chan = HIGH_5GHZ_FREQ_NO_6GHZ; + } + + pdev = wlan_objmgr_pdev_obj_create(psoc, priv); + if (!pdev) { + hdd_err("pdev obj create failed"); + status = QDF_STATUS_E_NOMEM; + goto free_priv; + } + + + status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_HDD_ID_OBJ_MGR); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to acquire pdev ref; status:%d", status); + QDF_BUG(false); + goto pdev_destroy; + } + + status = target_if_alloc_pdev_tgt_info(pdev); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("pdev tgt info alloc failed"); + goto pdev_destroy; + } + + hdd_ctx->pdev = pdev; + sme_store_pdev(hdd_ctx->mac_handle, hdd_ctx->pdev); + hdd_init_pdev_os_priv(hdd_ctx, priv); + return 0; + +pdev_destroy: + wlan_objmgr_pdev_obj_delete(pdev); +free_priv: + qdf_mem_free(priv); + + return qdf_status_to_os_return(status); +} + +int hdd_objmgr_release_and_destroy_pdev(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + struct wlan_objmgr_pdev *pdev = hdd_ctx->pdev; + struct pdev_osif_priv *osif_priv; + + hdd_ctx->pdev = NULL; + + QDF_BUG(pdev); + if (!pdev) + return -EINVAL; + + target_if_free_pdev_tgt_info(pdev); + + hdd_deinit_pdev_os_priv(pdev); + osif_priv = wlan_pdev_get_ospriv(pdev); + wlan_pdev_reset_ospriv(pdev); + qdf_mem_free(osif_priv); + + status = wlan_objmgr_pdev_obj_delete(pdev); + wlan_objmgr_pdev_release_ref(pdev, WLAN_HDD_ID_OBJ_MGR); + + return qdf_status_to_os_return(status); +} + + +int hdd_objmgr_release_and_destroy_vdev(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + qdf_spin_lock_bh(&adapter->vdev_lock); + vdev = adapter->vdev; + adapter->vdev = NULL; + adapter->vdev_id = WLAN_UMAC_VDEV_ID_MAX; + qdf_spin_unlock_bh(&adapter->vdev_lock); + + QDF_BUG(vdev); + if (!vdev) + return -EINVAL; + + status = wlan_objmgr_vdev_obj_delete(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_HDD_ID_OBJ_MGR); + + return qdf_status_to_os_return(status); +} + +struct wlan_objmgr_vdev *__hdd_objmgr_get_vdev(struct hdd_adapter *adapter, + const char *func) +{ + struct wlan_objmgr_vdev *vdev = NULL; + QDF_STATUS status; + + if (!adapter) { + hdd_err("Adapter is NULL (via %s)", func); + return NULL; + } + + qdf_spin_lock_bh(&adapter->vdev_lock); + vdev = adapter->vdev; + if (vdev) { + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_OSIF_ID); + if (QDF_IS_STATUS_ERROR(status)) + vdev = NULL; + } + qdf_spin_unlock_bh(&adapter->vdev_lock); + + if (!vdev) + hdd_debug("VDEV is NULL (via %s)", func); + + return vdev; +} + +void __hdd_objmgr_put_vdev(struct wlan_objmgr_vdev *vdev, const char *func) +{ + if (!vdev) { + hdd_err("VDEV is NULL (via %s)", func); + return; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); +} + +int hdd_objmgr_set_peer_mlme_auth_state(struct wlan_objmgr_vdev *vdev, + bool is_authenticated) +{ + struct wlan_objmgr_peer *peer; + + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_OSIF_ID); + if (!peer) { + hdd_err("peer is null"); + return -EINVAL; + } + + wlan_peer_obj_lock(peer); + wlan_peer_mlme_set_auth_state(peer, is_authenticated); + wlan_peer_obj_unlock(peer); + + wlan_objmgr_peer_release_ref(peer, WLAN_OSIF_ID); + return 0; +} + +int hdd_objmgr_set_peer_mlme_state(struct wlan_objmgr_vdev *vdev, + enum wlan_peer_state peer_state) +{ + struct wlan_objmgr_peer *peer; + + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_OSIF_ID); + if (!peer) { + hdd_err("peer is null"); + return -EINVAL; + } + + wlan_peer_obj_lock(peer); + wlan_peer_mlme_set_state(peer, WLAN_ASSOC_STATE); + wlan_peer_obj_unlock(peer); + + wlan_objmgr_peer_release_ref(peer, WLAN_OSIF_ID); + return 0; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_object_manager.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_object_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..cf877677b77a49c6bcdf68d7ae7b372727caf408 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_object_manager.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_HDD_OBJECT_MANAGER_H) +#define WLAN_HDD_OBJECT_MANAGER_H +/** + * DOC: HDD object manager API file to create/destroy psoc, pdev, vdev + * and peer objects by calling object manager APIs + * + * Common object model has 1 : N mapping between PSOC and PDEV but for MCL + * PSOC and PDEV has 1 : 1 mapping. + * + * MCL object model view is: + * + * -------- + * | PSOC | + * -------- + * | + * | + * -------------------------- + * | PDEV | + * -------------------------- + * | | + * | | + * | | + * ---------- ------------- + * | vdev 0 | | vdev n | + * ---------- ------------- + * | | | | + * ---------- ---------- ---------- ---------- + * | peer 1 | | peer n | | peer 1 | | peer n | + * ---------- ---------- ---------- ----------- + * + */ +#include "wlan_hdd_main.h" +#include +#include +#include +#include +#include +#include + +/** + * hdd_objmgr_create_and_store_psoc() - Create psoc and store in hdd context + * @hdd_ctx: Hdd context + * @psoc_id: Psoc Id + * + * This API creates Psoc object with given @psoc_id and store the psoc reference + * to hdd context + * + * Return: 0 for success, negative error code for failure + */ +int hdd_objmgr_create_and_store_psoc(struct hdd_context *hdd_ctx, + uint8_t psoc_id); + +/** + * hdd_objmgr_release_and_destroy_psoc() - Deletes the psoc object + * @hdd_ctx: Hdd context + * + * This API deletes psoc object and release its reference from hdd context + * + * Return: 0 for success, negative error code for failure + */ +int hdd_objmgr_release_and_destroy_psoc(struct hdd_context *hdd_ctx); + +/** + * hdd_objmgr_update_tgt_max_vdev_psoc() - Update target max vdev number + * @hdd_ctx: Hdd context + * + * This API update target max vdev number to psoc object + * + * Return: None + */ +void hdd_objmgr_update_tgt_max_vdev_psoc(struct hdd_context *hdd_ctx, + uint8_t max_vdev); + +/** + * hdd_objmgr_create_and_store_pdev() - Create pdev and store in hdd context + * @hdd_ctx: Hdd context + * + * This API creates the pdev object and store the pdev reference to hdd context + * + * Return: 0 for success, negative error code for failure + */ +int hdd_objmgr_create_and_store_pdev(struct hdd_context *hdd_ctx); + +/** + * hdd_objmgr_release_and_destroy_pdev() - Deletes the pdev object + * @hdd_ctx: Hdd context + * + * This API deletes pdev object and release its reference from hdd context + * + * Return: 0 for success, negative error code for failure + */ +int hdd_objmgr_release_and_destroy_pdev(struct hdd_context *hdd_ctx); + +/** + * hdd_objmgr_release_and_destroy_vdev() - Delete vdev and remove from adapter + * @adapter: hdd adapter + * + * This API deletes vdev object and release its reference from hdd adapter + * + * Return: 0 for success, negative error code for failure + */ +int hdd_objmgr_release_and_destroy_vdev(struct hdd_adapter *adapter); + +/** + * hdd_objmgr_get_vdev() - Get reference of vdev from adapter + * @adapter: hdd adapter + * + * This API gets vdev object reference from hdd adapter + * + * Return: pointer to vdev object for success, NULL for failure + */ +#define hdd_objmgr_get_vdev(adapter) \ + __hdd_objmgr_get_vdev(adapter, __func__) +struct wlan_objmgr_vdev *__hdd_objmgr_get_vdev(struct hdd_adapter *adapter, + const char *func); + +/** + * hdd_objmgr_put_vdev() - Release reference of vdev object + * @vdev: pointer to vdev object + * + * This API releases vdev object reference which was acquired using + * hdd_objmgr_get_vdev(). + * + * Return: void + */ +#define hdd_objmgr_put_vdev(vdev) \ + __hdd_objmgr_put_vdev(vdev, __func__) +void __hdd_objmgr_put_vdev(struct wlan_objmgr_vdev *vdev, const char *func); + +/** + * hdd_objmgr_set_peer_mlme_auth_state() - set the peer mlme auth state + * @vdev: vdev pointer + * @is_authenticated: Peer mlme auth state true/false + * + * This API set the peer mlme auth state + * + * Return: 0 for success, negative error code for failure + */ +int hdd_objmgr_set_peer_mlme_auth_state(struct wlan_objmgr_vdev *vdev, + bool is_authenticated); + +/** + * hdd_objmgr_set_peer_mlme_state() - set the peer mlme state + * @vdev: vdev pointer + * @peer_state: Peer mlme state + * + * This API set the peer mlme state + * + * Return: 0 for success, negative error code for failure + */ +int hdd_objmgr_set_peer_mlme_state(struct wlan_objmgr_vdev *vdev, + enum wlan_peer_state peer_state); + +#endif /* end #if !defined(WLAN_HDD_OBJECT_MANAGER_H) */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ocb.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ocb.c new file mode 100644 index 0000000000000000000000000000000000000000..4302abb9fc96c81a7471e4629579d122b9bdb460 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ocb.c @@ -0,0 +1,2076 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_ocb.c + * + * WLAN Host Device Driver 802.11p OCB implementation + */ + +#include "cds_sched.h" +#include "wlan_hdd_assoc.h" +#include "osif_sync.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_ocb.h" +#include "wlan_hdd_trace.h" +#include "wlan_osif_request_manager.h" +#include "wlan_tgt_def_config.h" +#include "sch_api.h" +#include "wma_api.h" +#include +#include +#include +#include "wlan_ocb_public_structs.h" +#include "wlan_ocb_ucfg_api.h" +#include +#include +#include +#include +#include "ol_txrx.h" + +/* Structure definitions for WLAN_SET_DOT11P_CHANNEL_SCHED */ +#define AIFSN_MIN (2) +#define AIFSN_MAX (15) +#define CW_MIN (1) +#define CW_MAX (10) + +/* Maximum time(ms) to wait for OCB operations */ +#define WLAN_WAIT_TIME_OCB_CMD 1500 + +/** + * dot11p_validate_qos_params() - Check if QoS parameters are valid + * @qos_params: Array of QoS parameters + * + * Return: 0 on success. error code on failure. + */ +static int dot11p_validate_qos_params(struct ocb_wmm_param qos_params[]) +{ + int i; + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + if ((!qos_params[i].aifsn) && (!qos_params[i].cwmin) + && (!qos_params[i].cwmax)) + continue; + + /* Validate AIFSN */ + if ((qos_params[i].aifsn < AIFSN_MIN) + || (qos_params[i].aifsn > AIFSN_MAX)) { + hdd_err("Invalid QoS parameter aifsn %d", + qos_params[i].aifsn); + return -EINVAL; + } + + /* Validate CWMin */ + if ((qos_params[i].cwmin < CW_MIN) + || (qos_params[i].cwmin > CW_MAX)) { + hdd_err("Invalid QoS parameter cwmin %d", + qos_params[i].cwmin); + return -EINVAL; + } + + /* Validate CWMax */ + if ((qos_params[i].cwmax < CW_MIN) + || (qos_params[i].cwmax > CW_MAX)) { + hdd_err("Invalid QoS parameter cwmax %d", + qos_params[i].cwmax); + return -EINVAL; + } + } + + return 0; +} + +/** + * dot11p_validate_channel() - validates a DSRC channel + * @center_freq: the channel's center frequency + * @bandwidth: the channel's bandwidth + * @tx_power: transmit power + * @reg_power: (output) the max tx power from the regulatory domain + * @antenna_max: (output) the max antenna gain from the regulatory domain + * + * Return: 0 if the channel is valid, error code otherwise. + */ +static int dot11p_validate_channel(struct wiphy *wiphy, + uint32_t channel_freq, uint32_t bandwidth, + uint32_t tx_power, uint8_t *reg_power, + uint8_t *antenna_max) +{ + int band_idx, channel_idx; + struct ieee80211_supported_band *current_band; + struct ieee80211_channel *current_channel; + + for (band_idx = 0; band_idx < HDD_NUM_NL80211_BANDS; band_idx++) { + current_band = wiphy->bands[band_idx]; + if (!current_band) + continue; + + for (channel_idx = 0; channel_idx < current_band->n_channels; + channel_idx++) { + current_channel = ¤t_band->channels[channel_idx]; + + if (channel_freq == current_channel->center_freq) { + if (current_channel->flags & + IEEE80211_CHAN_DISABLED) + return -EINVAL; + + if (reg_power) + *reg_power = + current_channel->max_reg_power; + if (antenna_max) + *antenna_max = + current_channel-> + max_antenna_gain; + + switch (bandwidth) { + case 0: + if (current_channel->flags & + IEEE80211_CHAN_NO_10MHZ) + bandwidth = 5; + else if (current_channel->flags & + IEEE80211_CHAN_NO_20MHZ) + bandwidth = 10; + else + bandwidth = 20; + break; + case 5: + break; + case 10: + if (current_channel->flags & + IEEE80211_CHAN_NO_10MHZ) + return -EINVAL; + break; + case 20: + if (current_channel->flags & + IEEE80211_CHAN_NO_20MHZ) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (tx_power > current_channel->max_power) + return -EINVAL; + + return 0; + } + } + } + + return -EINVAL; +} + +/** + * hdd_ocb_validate_config() - Validates the config data + * @config: configuration to be validated + * + * Return: 0 on success. + */ +static int hdd_ocb_validate_config(struct hdd_adapter *adapter, + struct ocb_config *config) +{ + int i; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + for (i = 0; i < config->channel_count; i++) { + if (dot11p_validate_channel(hdd_ctx->wiphy, + config->channels[i].chan_freq, + config->channels[i].bandwidth, + config->channels[i].max_pwr, + &config->channels[i].reg_pwr, + &config->channels[i].antenna_max)) { + hdd_err("Invalid channel frequency %d", + config->channels[i].chan_freq); + return -EINVAL; + } + if (dot11p_validate_qos_params(config->channels[i].qos_params)) + return -EINVAL; + } + + return 0; +} + +/** + * hdd_ocb_register_sta() - Register station with Transport Layer + * @adapter: Pointer to HDD Adapter + * + * This function should be invoked in the OCB Set Schedule callback + * to enable the data path in the TL by calling RegisterSTAClient + * + * Return: 0 on success. -1 on failure. + */ +static int hdd_ocb_register_sta(struct hdd_adapter *adapter) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct ol_txrx_desc_type sta_desc = {0}; + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct ol_txrx_ops txrx_ops; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct cdp_pdev *pdev = cds_get_context(QDF_MODULE_ID_TXRX); + + qdf_status = cdp_peer_register_ocb_peer(soc, + adapter->mac_addr.bytes); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Error registering OCB Self Peer!"); + return -EINVAL; + } + + WLAN_ADDR_COPY(sta_desc.peer_addr.bytes, adapter->mac_addr.bytes); + sta_desc.is_qos_enabled = 1; + + /* Register the vdev transmit and receive functions */ + qdf_mem_zero(&txrx_ops, sizeof(txrx_ops)); + txrx_ops.rx.rx = hdd_rx_packet_cbk; + + cdp_vdev_register(soc, adapter->vdev_id, (ol_osif_vdev_handle)adapter, + &txrx_ops); + txrx_ops.rx.stats_rx = hdd_tx_rx_collect_connectivity_stats_info; + adapter->tx_fn = txrx_ops.tx.tx; + + qdf_status = cdp_peer_register(soc, OL_TXRX_PDEV_ID, &sta_desc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to register. Status= %d [0x%08X]", + qdf_status, qdf_status); + return -EINVAL; + } + + qdf_copy_macaddr(&sta_ctx->conn_info.peer_macaddr[0], + &adapter->mac_addr); + + return 0; +} + +/** + * hdd_ocb_config_new() - Creates a new OCB configuration + * @num_channels: the number of channels + * @num_schedule: the schedule size + * @ndl_chan_list_len: length in bytes of the NDL chan blob + * @ndl_active_state_list_len: length in bytes of the active state blob + * + * Return: A pointer to the OCB configuration struct, NULL on failure. + */ +static +struct ocb_config *hdd_ocb_config_new(uint32_t num_channels, + uint32_t num_schedule, + uint32_t ndl_chan_list_len, + uint32_t ndl_active_state_list_len) +{ + struct ocb_config *ret = 0; + uint32_t len; + void *cursor; + + if (num_channels > CFG_TGT_NUM_OCB_CHANNELS || + num_schedule > CFG_TGT_NUM_OCB_SCHEDULES) + return NULL; + + len = sizeof(*ret) + + num_channels * sizeof(struct ocb_config_chan) + + num_schedule * sizeof(struct ocb_config_schdl) + + ndl_chan_list_len + + ndl_active_state_list_len; + + cursor = qdf_mem_malloc(len); + if (!cursor) + goto fail; + + ret = cursor; + cursor += sizeof(*ret); + + ret->channel_count = num_channels; + ret->channels = cursor; + cursor += num_channels * sizeof(*ret->channels); + + ret->schedule_size = num_schedule; + ret->schedule = cursor; + cursor += num_schedule * sizeof(*ret->schedule); + + ret->dcc_ndl_chan_list = cursor; + cursor += ndl_chan_list_len; + + ret->dcc_ndl_active_state_list = cursor; + cursor += ndl_active_state_list_len; + + return ret; + +fail: + qdf_mem_free(ret); + return NULL; +} + +struct hdd_ocb_set_config_priv { + int status; +}; + + +/** + * hdd_ocb_set_config_callback() - OCB set config callback function + * @context_ptr: OCB call context + * @response_ptr: Pointer to response structure + * + * This function is registered as a callback with the lower layers + * and is used to respond with the status of a OCB set config command. + */ +static void hdd_ocb_set_config_callback(void *context_ptr, void *response_ptr) +{ + struct osif_request *request; + struct hdd_ocb_set_config_priv *priv; + struct ocb_set_config_response *response = response_ptr; + + request = osif_request_get(context_ptr); + if (!request) { + hdd_err("Obsolete request"); + return; + } + priv = osif_request_priv(request); + + if (response && response->status) + hdd_warn("Operation failed: %d", response->status); + + if (response && (response->status == OCB_CHANNEL_CONFIG_SUCCESS)) + priv->status = 0; + else + priv->status = -EINVAL; + + osif_request_complete(request); + osif_request_put(request); +} + +/** + * hdd_ocb_set_config_req() - Send an OCB set config request + * @adapter: a pointer to the adapter + * @config: a pointer to the OCB configuration + * + * Return: 0 on success. + */ +static int hdd_ocb_set_config_req(struct hdd_adapter *adapter, + struct ocb_config *config) +{ + int rc; + QDF_STATUS status; + void *cookie; + struct osif_request *request; + struct hdd_ocb_set_config_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_OCB_CMD, + }; + + if (hdd_ocb_validate_config(adapter, config)) { + hdd_err("The configuration is invalid"); + return -EINVAL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + status = ucfg_ocb_set_channel_config(adapter->vdev, config, + hdd_ocb_set_config_callback, + cookie); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set channel config."); + rc = qdf_status_to_os_return(status); + goto end; + } + + /* Wait for the function to complete. */ + rc = osif_request_wait_for_response(request); + if (rc) { + hdd_err("Operation timed out"); + goto end; + } + + priv = osif_request_priv(request); + rc = priv->status; + if (rc) { + hdd_err("Operation failed: %d", rc); + goto end; + } + + /* + * OCB set config command successful. + * Open the TX data path + */ + if (!hdd_ocb_register_sta(adapter)) + wlan_hdd_netif_queue_control(adapter, + WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + + /* fall through */ +end: + osif_request_put(request); + + return rc; +} + +#ifdef WLAN_WEXT_SUPPORT_ENABLE +/** + * __iw_set_dot11p_channel_sched() - Handler for WLAN_SET_DOT11P_CHANNEL_SCHED + * ioctl + * @dev: Pointer to net_device structure + * @iw_request_info: IW Request Info + * @wrqu: IW Request Userspace Data Pointer + * @extra: IW Request Kernel Data Pointer + * + * Return: 0 on success + */ +static int __iw_set_dot11p_channel_sched(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int rc; + struct dot11p_channel_sched *sched; + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct ocb_config *config = NULL; + uint8_t *mac_addr; + int i, j; + struct ocb_config_chan *curr_chan; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + rc = wlan_hdd_validate_context(hdd_ctx); + if (0 != rc) + return rc; + + rc = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != rc) + return rc; + + if (adapter->device_mode != QDF_OCB_MODE) { + hdd_err("Device not in OCB mode!"); + return -EINVAL; + } + + sched = (struct dot11p_channel_sched *)extra; + + /* Scheduled slots same as num channels for compatibility */ + config = hdd_ocb_config_new(sched->num_channels, sched->num_channels, + 0, 0); + if (!config) { + hdd_err("Failed to allocate memory!"); + return -ENOMEM; + } + + /* Identify the vdev interface */ + config->vdev_id = adapter->vdev_id; + + /* Release all the mac addresses used for OCB */ + for (i = 0; i < adapter->ocb_mac_addr_count; i++) { + wlan_hdd_release_intf_addr(hdd_ctx, + adapter->ocb_mac_address[i].bytes); + } + adapter->ocb_mac_addr_count = 0; + + config->channel_count = 0; + for (i = 0; i < sched->num_channels; i++) { + if (0 == sched->channels[i].channel_freq) + continue; + + curr_chan = &(config->channels[config->channel_count]); + + curr_chan->chan_freq = sched->channels[i].channel_freq; + /* + * tx_power is divided by 2 because ocb_channel.tx_power is + * in half dB increments and ocb_config_channel.max_pwr + * is in 1 dB increments. + */ + curr_chan->max_pwr = sched->channels[i].tx_power / 2; + curr_chan->bandwidth = sched->channels[i].channel_bandwidth; + /* assume 10 as default if not provided */ + if (curr_chan->bandwidth == 0) + curr_chan->bandwidth = 10; + + /* + * Setup locally administered mac addresses for each channel. + * First channel uses the adapter's address. + */ + if (i == 0) { + qdf_copy_macaddr(&curr_chan->mac_address, + &adapter->mac_addr); + } else { + mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, + adapter->device_mode); + if (!mac_addr) { + hdd_err("Cannot obtain mac address"); + rc = -EINVAL; + goto fail; + } + qdf_mem_copy(config->channels[ + config->channel_count].mac_address.bytes, + mac_addr, sizeof(tSirMacAddr)); + /* Save the mac address to release later */ + qdf_mem_copy(adapter->ocb_mac_address[ + adapter->ocb_mac_addr_count].bytes, + mac_addr, QDF_MAC_ADDR_SIZE); + adapter->ocb_mac_addr_count++; + } + + for (j = 0; j < QCA_WLAN_AC_ALL; j++) { + curr_chan->qos_params[j].aifsn = + sched->channels[i].qos_params[j].aifsn; + curr_chan->qos_params[j].cwmin = + sched->channels[i].qos_params[j].cwmin; + curr_chan->qos_params[j].cwmax = + sched->channels[i].qos_params[j].cwmax; + } + + config->channel_count++; + } + + /* + * Scheduled slots same as num channels for compatibility with + * legacy use. + */ + for (i = 0; i < sched->num_channels; i++) { + config->schedule[i].chan_freq = sched->channels[i].channel_freq; + config->schedule[i].guard_interval = + sched->channels[i].start_guard_interval; + config->schedule[i].total_duration = + sched->channels[i].duration; + } + + rc = hdd_ocb_set_config_req(adapter, config); + if (rc) { + hdd_err("Error while setting OCB config"); + goto fail; + } + + rc = 0; + +fail: + qdf_mem_free(config); + return rc; +} + +/** + * iw_set_dot11p_channel_sched() - IOCTL interface for setting channel schedule + * @dev: Pointer to net_device structure + * @iw_request_info: IW Request Info + * @wrqu: IW Request Userspace Data Pointer + * @extra: IW Request Kernel Data Pointer + * + * Return: 0 on success. + */ +int iw_set_dot11p_channel_sched(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_dot11p_channel_sched(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif /* WLAN_WEXT_SUPPORT_ENABLE */ + +static const struct nla_policy qca_wlan_vendor_ocb_set_config_policy[ + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY] = { + .type = NLA_BINARY + }, + [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY] = { + .type = NLA_BINARY + }, + [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY] = { + .type = NLA_BINARY + }, + [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY] = { + .type = NLA_BINARY + }, + [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS] = { + .type = NLA_U32 + }, +}; + +static const struct nla_policy qca_wlan_vendor_ocb_set_utc_time_policy[ + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE] = { + .type = NLA_BINARY, .len = SIZE_UTC_TIME + }, + [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR] = { + .type = NLA_BINARY, .len = SIZE_UTC_TIME_ERROR + }, +}; + +static const struct nla_policy qca_wlan_vendor_ocb_start_timing_advert_policy[ + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE] = { + .type = NLA_U32 + }, +}; + +static const struct nla_policy qca_wlan_vendor_ocb_stop_timing_advert_policy[ + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ] = { + .type = NLA_U32 + }, +}; + +static const struct nla_policy qca_wlan_vendor_ocb_get_tsf_timer_resp[] = { + [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW] = { + .type = NLA_U32 + }, +}; + +static const struct nla_policy qca_wlan_vendor_dcc_get_stats[] = { + [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY] = { + .type = NLA_BINARY + }, +}; + +static const struct nla_policy qca_wlan_vendor_dcc_get_stats_resp[] = { + [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY] = { + .type = NLA_BINARY + }, +}; + +static const struct nla_policy qca_wlan_vendor_dcc_clear_stats[] = { + [QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP] = { + .type = NLA_U32 + }, +}; + +static const struct nla_policy qca_wlan_vendor_dcc_update_ndl[ + QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] = { + .type = NLA_BINARY + }, + [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY] = { + .type = NLA_BINARY + }, +}; + +/** + * struct wlan_hdd_ocb_config_channel + * @chan_freq: frequency of the channel + * @bandwidth: bandwidth of the channel, either 10 or 20 MHz + * @mac_address: MAC address assigned to this channel + * @qos_params: QoS parameters + * @max_pwr: maximum transmit power of the channel (1/2 dBm) + * @min_pwr: minimum transmit power of the channel (1/2 dBm) + */ +struct wlan_hdd_ocb_config_channel { + uint32_t chan_freq; + uint32_t bandwidth; + uint16_t flags; + uint8_t reserved[4]; + struct sir_qos_params qos_params[QCA_WLAN_AC_ALL]; + uint32_t max_pwr; + uint32_t min_pwr; +}; + +static void wlan_hdd_ocb_config_channel_to_ocb_config_channel( + struct ocb_config_chan *dest, + struct wlan_hdd_ocb_config_channel *src, + uint32_t channel_count) +{ + uint32_t i; + + qdf_mem_zero(dest, channel_count * sizeof(*dest)); + + for (i = 0; i < channel_count; i++) { + dest[i].chan_freq = src[i].chan_freq; + dest[i].bandwidth = src[i].bandwidth; + qdf_mem_copy(dest[i].qos_params, src[i].qos_params, + sizeof(dest[i].qos_params)); + /* + * max_pwr and min_pwr are divided by 2 because + * ocb_channel_param.max_pwr and min_pwr + * are in 1/2 dB increments and + * ocb_config_channel.max_pwr and min_pwr are in + * 1 dB increments. + */ + dest[i].max_pwr = src[i].max_pwr / 2; + dest[i].min_pwr = (src[i].min_pwr + 1) / 2; + dest[i].flags = src[i].flags; + } +} + +/** + * __wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +static int __wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1]; + struct nlattr *channel_array; + struct nlattr *sched_array; + struct nlattr *ndl_chan_list; + uint32_t ndl_chan_list_len; + struct nlattr *ndl_active_state_list; + uint32_t ndl_active_state_list_len; + uint32_t flags = 0; + int i; + uint32_t channel_count, schedule_size; + struct ocb_config *config; + int rc = -EINVAL; + uint8_t *mac_addr; + + hdd_enter_dev(dev); + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (adapter->device_mode != QDF_OCB_MODE) { + hdd_err("Device not in OCB mode!"); + return -EINVAL; + } + + /* Parse the netlink message */ + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX, + data, data_len, + qca_wlan_vendor_ocb_set_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Get the number of channels in the schedule */ + if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]) { + hdd_err("CHANNEL_COUNT is not present"); + return -EINVAL; + } + channel_count = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]); + + /* Get the size of the channel schedule */ + if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]) { + hdd_err("SCHEDULE_SIZE is not present"); + return -EINVAL; + } + schedule_size = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]); + + /* Get the ndl chan array and the ndl active state array. */ + ndl_chan_list = + tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY]; + ndl_chan_list_len = (ndl_chan_list ? nla_len(ndl_chan_list) : 0); + + ndl_active_state_list = + tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY]; + ndl_active_state_list_len = (ndl_active_state_list ? + nla_len(ndl_active_state_list) : 0); + + /* Get the flags */ + if (tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS]) + flags = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS]); + + config = hdd_ocb_config_new(channel_count, schedule_size, + ndl_chan_list_len, + ndl_active_state_list_len); + if (!config) { + hdd_err("Failed to allocate memory!"); + return -ENOMEM; + } + + config->channel_count = channel_count; + config->schedule_size = schedule_size; + config->flags = flags; + + /* Read the channel array */ + channel_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY]; + if (!channel_array) { + hdd_err("No channel present"); + goto fail; + } + if (nla_len(channel_array) != channel_count * + sizeof(struct wlan_hdd_ocb_config_channel)) { + hdd_err("CHANNEL_ARRAY is not the correct size"); + goto fail; + } + wlan_hdd_ocb_config_channel_to_ocb_config_channel( + config->channels, nla_data(channel_array), channel_count); + + /* Identify the vdev interface */ + config->vdev_id = adapter->vdev_id; + + /* Release all the mac addresses used for OCB */ + for (i = 0; i < adapter->ocb_mac_addr_count; i++) { + wlan_hdd_release_intf_addr(hdd_ctx, + adapter->ocb_mac_address[i].bytes); + } + adapter->ocb_mac_addr_count = 0; + + /* + * Setup locally administered mac addresses for each channel. + * First channel uses the adapter's address. + */ + for (i = 0; i < config->channel_count; i++) { + if (i == 0) { + qdf_copy_macaddr(&config->channels[i].mac_address, + &adapter->mac_addr); + } else { + mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, + adapter->device_mode); + if (!mac_addr) { + hdd_err("Cannot obtain mac address"); + goto fail; + } + qdf_mem_copy(config->channels[i].mac_address.bytes, + mac_addr, QDF_MAC_ADDR_SIZE); + /* Save the mac address to release later */ + qdf_copy_macaddr(&adapter->ocb_mac_address[ + adapter->ocb_mac_addr_count], + &config->channels[i].mac_address); + adapter->ocb_mac_addr_count++; + } + } + + /* Read the schedule array */ + sched_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY]; + if (!sched_array) { + hdd_err("No channel present"); + goto fail; + } + if (nla_len(sched_array) != schedule_size * sizeof(*config->schedule)) { + hdd_err("SCHEDULE_ARRAY is not the correct size"); + goto fail; + } + qdf_mem_copy(config->schedule, nla_data(sched_array), + nla_len(sched_array)); + + /* Copy the NDL chan array */ + if (ndl_chan_list_len) { + config->dcc_ndl_chan_list_len = ndl_chan_list_len; + qdf_mem_copy(config->dcc_ndl_chan_list, nla_data(ndl_chan_list), + nla_len(ndl_chan_list)); + } + + /* Copy the NDL active state array */ + if (ndl_active_state_list_len) { + config->dcc_ndl_active_state_list_len = + ndl_active_state_list_len; + qdf_mem_copy(config->dcc_ndl_active_state_list, + nla_data(ndl_active_state_list), + nla_len(ndl_active_state_list)); + } + + rc = hdd_ocb_set_config_req(adapter, config); + if (rc) + hdd_err("Error while setting OCB config: %d", rc); + +fail: + qdf_mem_free(config); + return rc; +} + +/** + * wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +int wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_ocb_set_config(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for set UTC time command + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +static int __wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1]; + struct nlattr *utc_attr; + struct nlattr *time_error_attr; + struct ocb_utc_param *utc; + int rc = -EINVAL; + + hdd_enter_dev(dev); + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (adapter->device_mode != QDF_OCB_MODE) { + hdd_err("Device not in OCB mode!"); + return -EINVAL; + } + + if (!wma_is_vdev_up(adapter->vdev_id)) { + hdd_err("The device has not been started"); + return -EINVAL; + } + + /* Parse the netlink message */ + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX, + data, data_len, + qca_wlan_vendor_ocb_set_utc_time_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Read the UTC time */ + utc_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE]; + if (!utc_attr) { + hdd_err("UTC_TIME is not present"); + return -EINVAL; + } + if (nla_len(utc_attr) != SIZE_UTC_TIME) { + hdd_err("UTC_TIME is not the correct size"); + return -EINVAL; + } + + /* Read the time error */ + time_error_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR]; + if (!time_error_attr) { + hdd_err("UTC_TIME is not present"); + return -EINVAL; + } + if (nla_len(time_error_attr) != SIZE_UTC_TIME_ERROR) { + hdd_err("UTC_TIME is not the correct size"); + return -EINVAL; + } + + utc = qdf_mem_malloc(sizeof(*utc)); + if (!utc) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + utc->vdev_id = adapter->vdev_id; + qdf_mem_copy(utc->utc_time, nla_data(utc_attr), SIZE_UTC_TIME); + qdf_mem_copy(utc->time_error, nla_data(time_error_attr), + SIZE_UTC_TIME_ERROR); + + if (ucfg_ocb_set_utc_time(adapter->vdev, utc) != + QDF_STATUS_SUCCESS) { + hdd_err("Error while setting UTC time"); + rc = -EINVAL; + } else { + rc = 0; + } + + qdf_mem_free(utc); + return rc; +} + +/** + * wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for the set UTC time command + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +int wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_ocb_set_utc_time(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for start TA cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +static int +__wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1]; + struct ocb_timing_advert_param *timing_advert; + int rc = -EINVAL; + + hdd_enter_dev(dev); + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (adapter->device_mode != QDF_OCB_MODE) { + hdd_err("Device not in OCB mode!"); + return -EINVAL; + } + + if (!wma_is_vdev_up(adapter->vdev_id)) { + hdd_err("The device has not been started"); + return -EINVAL; + } + + timing_advert = qdf_mem_malloc(sizeof(*timing_advert)); + if (!timing_advert) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + timing_advert->vdev_id = adapter->vdev_id; + + /* Parse the netlink message */ + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX, + data, data_len, + qca_wlan_vendor_ocb_start_timing_advert_policy)) { + hdd_err("Invalid ATTR"); + goto fail; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]) { + hdd_err("CHANNEL_FREQ is not present"); + goto fail; + } + timing_advert->chan_freq = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]); + + if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]) { + hdd_err("REPEAT_RATE is not present"); + goto fail; + } + timing_advert->repeat_rate = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]); + + timing_advert->template_length = + sme_ocb_gen_timing_advert_frame(hdd_ctx->mac_handle, + *(tSirMacAddr *)&adapter->mac_addr.bytes, + &timing_advert->template_value, + &timing_advert->timestamp_offset, + &timing_advert->time_value_offset); + if (timing_advert->template_length <= 0) { + hdd_err("Error while generating the TA frame"); + goto fail; + } + + if (ucfg_ocb_start_timing_advert(adapter->vdev, timing_advert) != + QDF_STATUS_SUCCESS) { + hdd_err("Error while starting timing advert"); + rc = -EINVAL; + } else { + rc = 0; + } + +fail: + if (timing_advert->template_value) + qdf_mem_free(timing_advert->template_value); + qdf_mem_free(timing_advert); + return rc; +} + +/** + * wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for the start TA cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +int wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_ocb_start_timing_advert(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +static int +__wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1]; + struct ocb_timing_advert_param *timing_advert; + int rc = -EINVAL; + + hdd_enter_dev(dev); + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (adapter->device_mode != QDF_OCB_MODE) { + hdd_err("Device not in OCB mode!"); + return -EINVAL; + } + + if (!wma_is_vdev_up(adapter->vdev_id)) { + hdd_err("The device has not been started"); + return -EINVAL; + } + + timing_advert = qdf_mem_malloc(sizeof(*timing_advert)); + if (!timing_advert) { + hdd_err("qdf_mem_malloc failed"); + return -ENOMEM; + } + timing_advert->vdev_id = adapter->vdev_id; + + /* Parse the netlink message */ + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX, + data, data_len, + qca_wlan_vendor_ocb_stop_timing_advert_policy)) { + hdd_err("Invalid ATTR"); + goto fail; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]) { + hdd_err("CHANNEL_FREQ is not present"); + goto fail; + } + timing_advert->chan_freq = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]); + + if (ucfg_ocb_stop_timing_advert(adapter->vdev, timing_advert) != + QDF_STATUS_SUCCESS) { + hdd_err("Error while stopping timing advert"); + rc = -EINVAL; + } else { + rc = 0; + } + +fail: + qdf_mem_free(timing_advert); + return rc; +} + +/** + * wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +int wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_ocb_stop_timing_advert(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +struct hdd_ocb_get_tsf_timer_priv { + struct ocb_get_tsf_timer_response response; + int status; +}; + +/** + * hdd_ocb_get_tsf_timer_callback() - Callback to get TSF command + * @context_ptr: request context + * @response_ptr: response data + */ +static void hdd_ocb_get_tsf_timer_callback(void *context_ptr, + void *response_ptr) +{ + struct osif_request *request; + struct hdd_ocb_get_tsf_timer_priv *priv; + struct ocb_get_tsf_timer_response *response = response_ptr; + + request = osif_request_get(context_ptr); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + if (response) { + priv->response = *response; + priv->status = 0; + } else { + priv->status = -EINVAL; + } + osif_request_complete(request); + osif_request_put(request); +} + +static int +hdd_ocb_get_tsf_timer_reply(struct wiphy *wiphy, + struct ocb_get_tsf_timer_response *response) +{ + uint32_t nl_buf_len; + struct sk_buff *nl_resp; + int rc; + + /* Allocate the buffer for the response. */ + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += 2 * (NLA_HDRLEN + sizeof(uint32_t)); + nl_resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, nl_buf_len); + if (!nl_resp) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + /* Populate the response. */ + rc = nla_put_u32(nl_resp, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH, + response->timer_high); + if (rc) + goto end; + rc = nla_put_u32(nl_resp, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW, + response->timer_low); + if (rc) + goto end; + + /* Send the response. */ + rc = cfg80211_vendor_cmd_reply(nl_resp); + nl_resp = NULL; + if (rc) { + hdd_err("cfg80211_vendor_cmd_reply failed: %d", rc); + goto end; + } +end: + if (nl_resp) + kfree_skb(nl_resp); + + return rc; +} + +/** + * __wlan_hdd_cfg80211_ocb_get_tsf_timer() - Interface for get TSF timer cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +static int +__wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int rc; + struct ocb_get_tsf_timer_param tsf_request = {0}; + QDF_STATUS status; + void *cookie; + struct osif_request *request; + struct hdd_ocb_get_tsf_timer_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_OCB_CMD, + }; + + hdd_enter_dev(dev); + + rc = wlan_hdd_validate_context(hdd_ctx); + if (rc) + return rc; + + if (adapter->device_mode != QDF_OCB_MODE) { + hdd_err("Device not in OCB mode!"); + return -EINVAL; + } + + if (!wma_is_vdev_up(adapter->vdev_id)) { + hdd_err("The device has not been started"); + return -EINVAL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + tsf_request.vdev_id = adapter->vdev_id; + status = ucfg_ocb_get_tsf_timer(adapter->vdev, &tsf_request, + hdd_ocb_get_tsf_timer_callback, + cookie); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get tsf timer."); + rc = qdf_status_to_os_return(status); + goto end; + } + + rc = osif_request_wait_for_response(request); + if (rc) { + hdd_err("Operation timed out"); + goto end; + } + + priv = osif_request_priv(request); + rc = priv->status; + if (rc) { + hdd_err("Operation failed: %d", rc); + goto end; + } + + hdd_debug("Got TSF timer response, high=%d, low=%d", + priv->response.timer_high, + priv->response.timer_low); + + /* Send the response. */ + rc = hdd_ocb_get_tsf_timer_reply(wiphy, &priv->response); + if (rc) { + hdd_err("hdd_ocb_get_tsf_timer_reply failed: %d", rc); + goto end; + } + + /* fall through */ +end: + osif_request_put(request); + + return rc; +} + +/** + * wlan_hdd_cfg80211_ocb_get_tsf_timer() - Interface for get TSF timer cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +int wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_ocb_get_tsf_timer(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +struct hdd_dcc_stats_priv { + struct ocb_dcc_get_stats_response *response; + int status; +}; + +static void hdd_dcc_get_stats_dealloc(void *context_ptr) +{ + struct hdd_dcc_stats_priv *priv = context_ptr; + + qdf_mem_free(priv->response); + priv->response = NULL; +} + +/** + * hdd_dcc_get_stats_callback() - Callback to get stats command + * @context_ptr: request context + * @response_ptr: response data + */ +static void hdd_dcc_get_stats_callback(void *context_ptr, void *response_ptr) +{ + struct osif_request *request; + struct hdd_dcc_stats_priv *priv; + struct ocb_dcc_get_stats_response *response = response_ptr; + struct ocb_dcc_get_stats_response *hdd_resp; + + request = osif_request_get(context_ptr); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + if (!response) { + priv->status = -EINVAL; + goto end; + } + + priv->response = qdf_mem_malloc(sizeof(*response) + + response->channel_stats_array_len); + if (!priv->response) { + priv->status = -ENOMEM; + goto end; + } + + hdd_resp = priv->response; + *hdd_resp = *response; + hdd_resp->channel_stats_array = (void *)hdd_resp + sizeof(*hdd_resp); + qdf_mem_copy(hdd_resp->channel_stats_array, + response->channel_stats_array, + response->channel_stats_array_len); + priv->status = 0; + +end: + osif_request_complete(request); + osif_request_put(request); +} + +static int +hdd_dcc_get_stats_send_reply(struct wiphy *wiphy, + struct ocb_dcc_get_stats_response *response) +{ + uint32_t nl_buf_len; + struct sk_buff *nl_resp; + int rc; + + /* Allocate the buffer for the response. */ + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += NLA_HDRLEN + sizeof(uint32_t); + nl_buf_len += NLA_HDRLEN + response->channel_stats_array_len; + nl_resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, nl_buf_len); + if (!nl_resp) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + /* Populate the response. */ + rc = nla_put_u32(nl_resp, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT, + response->num_channels); + if (rc) + goto end; + rc = nla_put(nl_resp, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY, + response->channel_stats_array_len, + response->channel_stats_array); + if (rc) + goto end; + + /* Send the response. */ + rc = cfg80211_vendor_cmd_reply(nl_resp); + nl_resp = NULL; + if (rc) { + hdd_err("cfg80211_vendor_cmd_reply failed: %d", rc); + goto end; + } +end: + if (nl_resp) + kfree_skb(nl_resp); + + return rc; +} + +/** + * __wlan_hdd_cfg80211_dcc_get_stats() - Interface for get dcc stats + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +static int __wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + uint32_t channel_count = 0; + uint32_t request_array_len = 0; + void *request_array = 0; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX + 1]; + int rc; + struct ocb_dcc_get_stats_param dcc_request = {0}; + QDF_STATUS status; + void *cookie; + struct osif_request *request; + struct hdd_dcc_stats_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_OCB_CMD, + .dealloc = hdd_dcc_get_stats_dealloc, + }; + + hdd_enter_dev(dev); + + rc = wlan_hdd_validate_context(hdd_ctx); + if (rc) + return rc; + + if (adapter->device_mode != QDF_OCB_MODE) { + hdd_err("Device not in OCB mode!"); + return -EINVAL; + } + + if (!wma_is_vdev_up(adapter->vdev_id)) { + hdd_err("The device has not been started"); + return -EINVAL; + } + + /* Parse the netlink message */ + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX, + data, data_len, + qca_wlan_vendor_dcc_get_stats)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Validate all the parameters are present */ + if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] || + !tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]) { + hdd_err("Parameters are not present."); + return -EINVAL; + } + + channel_count = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT]); + request_array_len = nla_len( + tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]); + request_array = nla_data( + tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]); + + /* Check channel count. Per 11p spec, max 2 channels allowed */ + if (!channel_count || channel_count > CFG_TGT_NUM_OCB_CHANNELS) { + hdd_err("Invalid channel_count %d", channel_count); + return -EINVAL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + dcc_request.vdev_id = adapter->vdev_id; + dcc_request.channel_count = channel_count; + dcc_request.request_array_len = request_array_len; + dcc_request.request_array = request_array; + + status = ucfg_ocb_dcc_get_stats(adapter->vdev, &dcc_request, + hdd_dcc_get_stats_callback, + cookie); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get DCC stats."); + rc = qdf_status_to_os_return(status); + goto end; + } + + /* Wait for the function to complete. */ + rc = osif_request_wait_for_response(request); + if (rc) { + hdd_err("Operation timed out"); + goto end; + } + + priv = osif_request_priv(request); + rc = priv->status; + if (rc) { + hdd_err("Operation failed: %d", rc); + goto end; + } + + /* Send the response. */ + rc = hdd_dcc_get_stats_send_reply(wiphy, priv->response); + if (rc) { + hdd_err("hdd_dcc_get_stats_send_reply failed: %d", rc); + goto end; + } + + /* fall through */ +end: + osif_request_put(request); + + return rc; +} + +/** + * wlan_hdd_cfg80211_dcc_get_stats() - Interface for get dcc stats + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +int wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_dcc_get_stats(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +static int __wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX + 1]; + + hdd_enter_dev(dev); + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (adapter->device_mode != QDF_OCB_MODE) { + hdd_err("Device not in OCB mode!"); + return -EINVAL; + } + + if (!wma_is_vdev_up(adapter->vdev_id)) { + hdd_err("The device has not been started"); + return -EINVAL; + } + + /* Parse the netlink message */ + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX, + data, data_len, + qca_wlan_vendor_dcc_clear_stats)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Verify that the parameter is present */ + if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP]) { + hdd_err("Parameters are not present."); + return -EINVAL; + } + + if (ucfg_ocb_dcc_clear_stats(adapter->vdev, adapter->vdev_id, + nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP])) != + QDF_STATUS_SUCCESS) { + hdd_err("Failed to clear DCC stats."); + return -EINVAL; + } + + return 0; +} + +/** + * wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +int wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_dcc_clear_stats(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +struct hdd_dcc_update_ndl_priv { + int status; +}; + +/** + * hdd_dcc_update_ndl_callback() - Callback to update NDL command + * @context_ptr: request context + * @response_ptr: response data + */ +static void hdd_dcc_update_ndl_callback(void *context_ptr, void *response_ptr) +{ + struct osif_request *request; + struct hdd_dcc_update_ndl_priv *priv; + struct ocb_dcc_update_ndl_response *response = response_ptr; + + request = osif_request_get(context_ptr); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + if (response && (0 == response->status)) + priv->status = 0; + else + priv->status = -EINVAL; + + osif_request_complete(request); + osif_request_put(request); +} + +/** + * __wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +static int __wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1]; + struct ocb_dcc_update_ndl_param dcc_request; + uint32_t channel_count; + uint32_t ndl_channel_array_len; + void *ndl_channel_array; + uint32_t ndl_active_state_array_len; + void *ndl_active_state_array; + int rc; + QDF_STATUS status; + void *cookie; + struct osif_request *request; + struct hdd_dcc_update_ndl_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_OCB_CMD, + }; + + hdd_enter_dev(dev); + + rc = wlan_hdd_validate_context(hdd_ctx); + if (rc) + return rc; + + if (adapter->device_mode != QDF_OCB_MODE) { + hdd_err("Device not in OCB mode!"); + return -EINVAL; + } + + if (!wma_is_vdev_up(adapter->vdev_id)) { + hdd_err("The device has not been started"); + return -EINVAL; + } + + /* Parse the netlink message */ + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX, + data, data_len, + qca_wlan_vendor_dcc_update_ndl)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + /* Verify that the parameter is present */ + if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] || + !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] || + !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]) { + hdd_err("Parameters are not present."); + return -EINVAL; + } + + channel_count = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT]); + ndl_channel_array_len = nla_len( + tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]); + ndl_channel_array = nla_data( + tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]); + ndl_active_state_array_len = nla_len( + tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]); + ndl_active_state_array = nla_data( + tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]); + + /* Check channel count. Per 11p spec, max 2 channels allowed */ + if (!channel_count || channel_count > CFG_TGT_NUM_OCB_CHANNELS) { + hdd_err("Invalid channel_count %d", channel_count); + return -EINVAL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + /* Copy the parameters to the request structure. */ + dcc_request.vdev_id = adapter->vdev_id; + dcc_request.channel_count = channel_count; + dcc_request.dcc_ndl_chan_list_len = ndl_channel_array_len; + dcc_request.dcc_ndl_chan_list = ndl_channel_array; + dcc_request.dcc_ndl_active_state_list_len = ndl_active_state_array_len; + dcc_request.dcc_ndl_active_state_list = ndl_active_state_array; + + status = ucfg_ocb_dcc_update_ndl(adapter->vdev, &dcc_request, + hdd_dcc_update_ndl_callback, + cookie); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to update NDL."); + rc = qdf_status_to_os_return(status); + goto end; + } + + /* Wait for the function to complete. */ + rc = osif_request_wait_for_response(request); + if (rc) { + hdd_err("Operation timed out"); + goto end; + } + + priv = osif_request_priv(request); + rc = priv->status; + if (rc) { + hdd_err("Operation failed: %d", rc); + goto end; + } + + /* fall through */ +end: + osif_request_put(request); + + return rc; +} + +/** + * wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd + * @wiphy: pointer to the wiphy + * @wdev: pointer to the wdev + * @data: The netlink data + * @data_len: The length of the netlink data in bytes + * + * Return: 0 on success. + */ +int wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_dcc_update_ndl(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_dcc_stats_event_callback() - Callback to get stats event + * @context_ptr: request context + * @response_ptr: response data + */ +static void wlan_hdd_dcc_stats_event_callback(void *context_ptr, + void *response_ptr) +{ + struct hdd_context *hdd_ctx = (struct hdd_context *)context_ptr; + struct ocb_dcc_get_stats_response *resp = response_ptr; + struct sk_buff *vendor_event; + + hdd_enter(); + + vendor_event = + cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, sizeof(uint32_t) + resp->channel_stats_array_len + + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT, + resp->num_channels) || + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY, + resp->channel_stats_array_len, + resp->channel_stats_array)) { + hdd_err("nla put failed"); + kfree_skb(vendor_event); + return; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); +} + +/** + * wlan_hdd_dcc_register_for_dcc_stats_event() - Register for dcc stats events + * @hdd_ctx: hdd context + */ +void wlan_hdd_dcc_register_for_dcc_stats_event(struct hdd_context *hdd_ctx) +{ + int rc; + + rc = ucfg_ocb_register_for_dcc_stats_event(hdd_ctx->pdev, hdd_ctx, + wlan_hdd_dcc_stats_event_callback); + if (rc) + hdd_err("Register DCC stats callback failed: %d", rc); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ocb.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ocb.h new file mode 100644 index 0000000000000000000000000000000000000000..1bd92c9bc098ec34e6fcb70ec0adb4ecbfc169f9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ocb.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2015-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_OCB_H +#define __WLAN_HDD_OCB_H + +#include +#include "sir_api.h" + +#define WLAN_OCB_CHANNEL_MAX 5 + +/** + * struct ocb_qos_params - QoS Parameters for each AC + * @aifsn: Arbitration Inter-Frame Spacing + * @cwmin: Contention Window (Min) + * @cwmax: Contention Window (Max) + */ +struct ocb_qos_params { + uint8_t aifsn; + uint8_t cwmin; + uint8_t cwmax; +}; + +/** + * struct ocb_channel - Parameters for each OCB channel + * @channel_freq: Channel Center Frequency (MHz) + * @duration: Channel Duration (ms) + * @start_guard_interval: Start Guard Interval (ms) + * @channel_bandwidth: Channel Bandwidth (MHz) + * @tx_power: Transmit Power (1/2 dBm) + * @tx_rate: Transmit Data Rate (mbit) + * @qos_params: Array of QoS Parameters + * @per_packet_rx_stats: Enable per packet RX statistics + */ +struct ocb_channel { + uint32_t channel_freq; + uint32_t duration; + uint32_t start_guard_interval; + uint32_t channel_bandwidth; + uint32_t tx_power; + uint32_t tx_rate; + struct ocb_qos_params qos_params[QCA_WLAN_AC_ALL]; + uint32_t per_packet_rx_stats; +}; + +/** + * struct dot11p_channel_sched - OCB channel schedule + * @num_channels: Number of channels + * @channels: Array of channel parameters + * @off_channel_tx: Enable off channel TX + */ +struct dot11p_channel_sched { + uint32_t num_channels; + struct ocb_channel channels[WLAN_OCB_CHANNEL_MAX]; + uint32_t off_channel_tx; +}; + +/** + * enum qca_wlan_vendor_attr_ocb_set_config - vendor subcmd to set ocb config + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT: + * number of channels in the configuration + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE: size of the schedule + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY: array of channels + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY: + * array of channels to be scheduled + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY: + * array of NDL channel information + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY: + * array of NDL active state configuration + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS: + * flag to set the absolute expiry + */ +enum qca_wlan_vendor_attr_ocb_set_config { + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ocb_set_utc_time - vendor subcmd to set UTC time + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE: + * the UTC time as an array of 10 bytes + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR: + * the time error as an array of 5 bytes + */ +enum qca_wlan_vendor_attr_ocb_set_utc_time { + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ocb_start_timing_advert - vendor subcmd to start + sending timing advert + frames + * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ: + * channel frequency on which to send the frames + * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE: + * number of times the frame is sent in 5 seconds + */ +enum qca_wlan_vendor_attr_ocb_start_timing_advert { + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ocb_stop_timing_advert - vendor subcmd to stop + * timing advert + * @QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ: + * the channel frequency on which to stop the timing advert + */ +enum qca_wlan_vendor_attr_ocb_stop_timing_advert { + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_dcc_get_tsf_response - vendor subcmd to get TSF + * timer value + * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH: + * higher 32 bits of the timer + * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW: + * lower 32 bits of the timer + */ +enum qca_wlan_vendor_attr_ocb_get_tsf_resp { + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_dcc_get_stats - vendor subcmd to get + * dcc stats + * @QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT: + * the number of channels in the request array + * @QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY + * array of the channel and information being requested + */ +enum qca_wlan_vendor_attr_dcc_get_stats { + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX = + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_dcc_get_stats_resp - response event from get + * dcc stats + * @QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT: + * the number of channels in the request array + * @QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY + * array of the information being requested + */ +enum qca_wlan_vendor_attr_dcc_get_stats_resp { + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_MAX = + QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_dcc_clear_stats - vendor subcmd to clear DCC stats + * @QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP: + * mask of the type of stats to be cleared + */ +enum qca_wlan_vendor_attr_dcc_clear_stats { + QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP, + QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX = + QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ocb_set_config - vendor subcmd to update dcc + * @QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT: + * number of channels in the configuration + * @QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY: the array of NDL + * channel info + * @QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY: the array of + * NDL active states + */ +enum qca_wlan_vendor_attr_dcc_update_ndl { + QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT, + QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY, + QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY, + QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX = + QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_AFTER_LAST - 1, +}; + +struct hdd_context; + +#ifdef WLAN_WEXT_SUPPORT_ENABLE + +#ifdef WLAN_FEATURE_DSRC +int iw_set_dot11p_channel_sched(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +#else +static inline +int iw_set_dot11p_channel_sched(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + return 0; +} +#endif +#endif + +#ifdef WLAN_FEATURE_DSRC +int wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +int wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +void wlan_hdd_dcc_register_for_dcc_stats_event(struct hdd_context *hdd_ctx); + +void wlan_hdd_dcc_stats_event(void *context_ptr, void *response_ptr); +#else +static inline int wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return 0; +} + +static inline int wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return 0; +} + +static inline int wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return 0; +} + +static inline int wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return 0; +} + +static inline int wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return 0; +} + +static inline int wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return 0; +} + +static inline int wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return 0; +} + +static inline int wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return 0; +} + +static inline void wlan_hdd_dcc_register_for_dcc_stats_event( + struct hdd_context *hdd_ctx) +{ +} + +static inline void wlan_hdd_dcc_stats_event(void *context_ptr, + void *response_ptr) +{ +} +#endif + +#endif /* __WLAN_HDD_OCB_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_oemdata.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_oemdata.c new file mode 100644 index 0000000000000000000000000000000000000000..ca6e470a5ed744277d669a91917e655a3f0e8c0d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_oemdata.c @@ -0,0 +1,1385 @@ +/* + * Copyright (c) 2012-2019, 2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_oemdata.c + * + * Support for generic OEM Data Request handling + * + */ + +#if defined(FEATURE_OEM_DATA_SUPPORT) || defined(FEATURE_OEM_DATA) +#include +#include +#include +#include +#include +#include "osif_sync.h" +#include +#include +#include "qwlan_version.h" +#include "cds_utils.h" +#include "wma.h" +#include "sme_api.h" +#include "wlan_nlink_srv.h" +#include "wlan_hdd_oemdata.h" +#include "wlan_osif_request_manager.h" +#include "wlan_hdd_main.h" +#ifdef FEATURE_OEM_DATA_SUPPORT +#ifdef CNSS_GENL +#include +#endif + +static struct hdd_context *p_hdd_ctx; + +/** + * populate_oem_data_cap() - populate oem capabilities + * @adapter: device adapter + * @data_cap: pointer to populate the capabilities + * + * Return: error code + */ +static int populate_oem_data_cap(struct hdd_adapter *adapter, + struct oem_data_cap *data_cap) +{ + QDF_STATUS status; + struct hdd_config *config; + uint32_t num_chan, i; + uint32_t *chan_freq_list; + uint8_t band_capability; + uint32_t band_bitmap; + uint16_t neighbor_scan_min_chan_time; + uint16_t neighbor_scan_max_chan_time; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + config = hdd_ctx->config; + if (!config) { + hdd_err("HDD configuration is null"); + return -EINVAL; + } + + status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_bitmap); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get MLME band capability"); + return -EIO; + } + + band_capability = wlan_reg_band_bitmap_to_band_info(band_bitmap); + + chan_freq_list = + qdf_mem_malloc(sizeof(uint32_t) * OEM_CAP_MAX_NUM_CHANNELS); + if (!chan_freq_list) + return -ENOMEM; + + strlcpy(data_cap->oem_target_signature, OEM_TARGET_SIGNATURE, + OEM_TARGET_SIGNATURE_LEN); + data_cap->oem_target_type = hdd_ctx->target_type; + data_cap->oem_fw_version = hdd_ctx->target_fw_version; + data_cap->driver_version.major = QWLAN_VERSION_MAJOR; + data_cap->driver_version.minor = QWLAN_VERSION_MINOR; + data_cap->driver_version.patch = QWLAN_VERSION_PATCH; + data_cap->driver_version.build = QWLAN_VERSION_BUILD; + ucfg_mlme_get_neighbor_scan_max_chan_time(psoc, + &neighbor_scan_max_chan_time); + ucfg_mlme_get_neighbor_scan_min_chan_time(psoc, + &neighbor_scan_min_chan_time); + data_cap->allowed_dwell_time_min = neighbor_scan_min_chan_time; + data_cap->allowed_dwell_time_max = neighbor_scan_max_chan_time; + data_cap->curr_dwell_time_min = + sme_get_neighbor_scan_min_chan_time(hdd_ctx->mac_handle, + adapter->vdev_id); + data_cap->curr_dwell_time_max = + sme_get_neighbor_scan_max_chan_time(hdd_ctx->mac_handle, + adapter->vdev_id); + data_cap->supported_bands = band_capability; + + /* request for max num of channels */ + num_chan = OEM_CAP_MAX_NUM_CHANNELS; + status = sme_get_cfg_valid_channels(&chan_freq_list[0], &num_chan); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("failed to get valid channel list, status: %d", status); + qdf_mem_free(chan_freq_list); + return -EINVAL; + } + + /* make sure num channels is not more than chan list array */ + if (num_chan > OEM_CAP_MAX_NUM_CHANNELS) { + hdd_err("Num of channels-%d > length-%d of chan_freq_list", + num_chan, OEM_CAP_MAX_NUM_CHANNELS); + qdf_mem_free(chan_freq_list); + return -ENOMEM; + } + + data_cap->num_channels = num_chan; + for (i = 0; i < num_chan; i++) { + data_cap->channel_list[i] = + wlan_reg_freq_to_chan(hdd_ctx->pdev, chan_freq_list[i]); + } + + qdf_mem_free(chan_freq_list); + return 0; +} + +/** + * iw_get_oem_data_cap() - Get OEM Data Capabilities + * @dev: net device upon which the request was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl data payload + * + * This function gets the capability information for OEM Data Request + * and Response. + * + * Return: 0 for success, negative errno value on failure + */ +int iw_get_oem_data_cap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct oem_data_cap *oem_data_cap = (void *)extra; + struct hdd_adapter *adapter = netdev_priv(dev); + struct hdd_context *hdd_ctx; + int errno; + + hdd_enter(); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + qdf_mem_zero(oem_data_cap, sizeof(*oem_data_cap)); + errno = populate_oem_data_cap(adapter, oem_data_cap); + if (errno) { + hdd_err("Failed to populate oem data capabilities"); + return errno; + } + + hdd_exit(); + return 0; +} + +/** + * send_oem_reg_rsp_nlink_msg() - send oem registration response + * + * This function sends oem message to registered application process + * + * Return: none + */ +static void send_oem_reg_rsp_nlink_msg(void) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + tAniMsgHdr *ani_hdr; + uint8_t *buf; + uint8_t *num_interfaces; + uint8_t *device_mode; + uint8_t *vdev_id; + struct hdd_adapter *adapter, *next_adapter = NULL; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_SEND_OEM_REG_RSP_NLINK_MSG; + + /* OEM msg is always to a specific process & cannot be a broadcast */ + if (p_hdd_ctx->oem_pid == 0) { + hdd_err("invalid dest pid"); + return; + } + + skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), GFP_KERNEL); + if (!skb) + return; + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_type = WLAN_NL_MSG_OEM; + ani_hdr = NLMSG_DATA(nlh); + ani_hdr->type = ANI_MSG_APP_REG_RSP; + + /* Fill message body: + * First byte will be number of interfaces, followed by + * two bytes for each interfaces + * - one byte for device mode + * - one byte for vdev id + */ + buf = (char *)((char *)ani_hdr + sizeof(tAniMsgHdr)); + num_interfaces = buf++; + *num_interfaces = 0; + + /* Iterate through each adapter and fill device mode and vdev id */ + hdd_for_each_adapter_dev_held_safe(p_hdd_ctx, adapter, next_adapter, + dbgid) { + device_mode = buf++; + vdev_id = buf++; + *device_mode = adapter->device_mode; + *vdev_id = adapter->vdev_id; + (*num_interfaces)++; + hdd_debug("num_interfaces: %d, device_mode: %d, vdev_id: %d", + *num_interfaces, *device_mode, + *vdev_id); + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + ani_hdr->length = + sizeof(uint8_t) + (*num_interfaces) * 2 * sizeof(uint8_t); + nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + ani_hdr->length)); + + skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + ani_hdr->length))); + + hdd_debug("sending App Reg Response length: %d to pid: %d", + ani_hdr->length, p_hdd_ctx->oem_pid); + + (void)nl_srv_ucast_oem(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT); +} + +/** + * send_oem_err_rsp_nlink_msg() - send oem error response + * @app_pid: PID of oem application process + * @error_code: response error code + * + * This function sends error response to oem app + * + * Return: none + */ +static void send_oem_err_rsp_nlink_msg(int32_t app_pid, uint8_t error_code) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + tAniMsgHdr *ani_hdr; + uint8_t *buf; + + skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), GFP_KERNEL); + if (!skb) + return; + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_type = WLAN_NL_MSG_OEM; + ani_hdr = NLMSG_DATA(nlh); + ani_hdr->type = ANI_MSG_OEM_ERROR; + ani_hdr->length = sizeof(uint8_t); + nlh->nlmsg_len = NLMSG_LENGTH(sizeof(tAniMsgHdr) + ani_hdr->length); + + /* message body will contain one byte of error code */ + buf = (char *)((char *)ani_hdr + sizeof(tAniMsgHdr)); + *buf = error_code; + + skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + ani_hdr->length)); + + hdd_debug("sending oem error response to pid: %d", app_pid); + + (void)nl_srv_ucast_oem(skb, app_pid, MSG_DONTWAIT); +} + +/** + * hdd_send_oem_data_rsp_msg() - send oem data response + * @oem_data_rsp: the actual OEM Data Response message + * + * This function sends an OEM Data Response message to a registered + * application process over the netlink socket. + * + * Return: 0 for success, non zero for failure + */ +void hdd_send_oem_data_rsp_msg(struct oem_data_rsp *oem_data_rsp) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + tAniMsgHdr *ani_hdr; + uint8_t *oem_data; + + /* + * OEM message is always to a specific process and cannot be a broadcast + */ + if (p_hdd_ctx->oem_pid == 0) { + hdd_err("invalid dest pid"); + return; + } + + if (oem_data_rsp->rsp_len > OEM_DATA_RSP_SIZE) { + hdd_err("invalid length of Oem Data response"); + return; + } + + skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) + OEM_DATA_RSP_SIZE), + GFP_KERNEL); + if (!skb) + return; + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_type = WLAN_NL_MSG_OEM; + ani_hdr = NLMSG_DATA(nlh); + ani_hdr->type = ANI_MSG_OEM_DATA_RSP; + + ani_hdr->length = oem_data_rsp->rsp_len; + nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + ani_hdr->length)); + oem_data = (uint8_t *) ((char *)ani_hdr + sizeof(tAniMsgHdr)); + qdf_mem_copy(oem_data, oem_data_rsp->data, oem_data_rsp->rsp_len); + + skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + ani_hdr->length))); + + hdd_debug("sending Oem Data Response of len : %d to pid: %d", + oem_data_rsp->rsp_len, p_hdd_ctx->oem_pid); + + (void)nl_srv_ucast_oem(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT); +} + +/** + * oem_process_data_req_msg() - process oem data request + * @oem_data_len: Length to OEM Data buffer + * @oem_data: Pointer to OEM Data buffer + * + * This function sends oem message to SME + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS oem_process_data_req_msg(int oem_data_len, char *oem_data) +{ + struct oem_data_req oem_data_req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* for now, STA interface only */ + if (!hdd_get_adapter(p_hdd_ctx, QDF_STA_MODE) && + !hdd_get_adapter(p_hdd_ctx, QDF_SAP_MODE)) { + hdd_err("No adapter for STA or SAP mode"); + return QDF_STATUS_E_FAILURE; + } + + if (!oem_data) { + hdd_err("oem_data is null"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_zero(&oem_data_req, sizeof(oem_data_req)); + + oem_data_req.data = qdf_mem_malloc(oem_data_len); + if (!oem_data_req.data) { + hdd_err("malloc failed for data req buffer"); + return QDF_STATUS_E_NOMEM; + } + + oem_data_req.data_len = oem_data_len; + qdf_mem_copy(oem_data_req.data, oem_data, oem_data_len); + + status = sme_oem_req_cmd(p_hdd_ctx->mac_handle, &oem_data_req); + + qdf_mem_free(oem_data_req.data); + oem_data_req.data = NULL; + + return status; +} + +void hdd_update_channel_bw_info(struct hdd_context *hdd_ctx, + uint32_t chan_freq, void *chan_info) +{ + struct ch_params ch_params = {0}; + enum wlan_phymode phy_mode; + uint16_t fw_phy_mode; + uint32_t wni_dot11_mode; + struct hdd_channel_info *hdd_chan_info = chan_info; + + wni_dot11_mode = sme_get_wni_dot11_mode(hdd_ctx->mac_handle); + + /* Passing CH_WIDTH_MAX will give the max bandwidth supported */ + ch_params.ch_width = CH_WIDTH_MAX; + + wlan_reg_set_channel_params_for_freq( + hdd_ctx->pdev, chan_freq, 0, &ch_params); + if (ch_params.center_freq_seg0) + hdd_chan_info->band_center_freq1 = + cds_chan_to_freq(ch_params.center_freq_seg0); + + if (ch_params.ch_width < CH_WIDTH_INVALID) { + phy_mode = wma_chan_phy_mode(chan_freq, ch_params.ch_width, + wni_dot11_mode); + } + else + /* + * If channel width is CH_WIDTH_INVALID, It mean channel is + * invalid and should not have been received in channel info + * req. Set invalid phymode in this case. + */ + phy_mode = WLAN_PHYMODE_AUTO; + + fw_phy_mode = wma_host_to_fw_phymode(phy_mode); + + hdd_debug("chan %d dot11_mode %d ch_width %d sec offset %d freq_seg0 %d phy_mode %d fw_phy_mode %d", + chan_freq, wni_dot11_mode, ch_params.ch_width, + ch_params.sec_ch_offset, + hdd_chan_info->band_center_freq1, phy_mode, fw_phy_mode); + + WMI_SET_CHANNEL_MODE(hdd_chan_info, fw_phy_mode); +} + +/** + * oem_process_channel_info_req_msg() - process oem channel_info request + * @numOfChannels: number of channels + * @chanList: list of channel information + * + * This function responds with channel info to oem process + * + * Return: 0 for success, non zero for failure + */ +static int oem_process_channel_info_req_msg(int numOfChannels, char *chanList) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + tAniMsgHdr *ani_hdr; + struct hdd_channel_info *pHddChanInfo; + struct hdd_channel_info hddChanInfo; + uint32_t reg_info_1; + uint32_t reg_info_2; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + int i; + uint8_t *buf; + uint32_t chan_freq; + + /* OEM msg is always to a specific process and cannot be a broadcast */ + if (p_hdd_ctx->oem_pid == 0) { + hdd_err("invalid dest pid"); + return -EPERM; + } + + skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) + sizeof(uint8_t) + + numOfChannels * sizeof(*pHddChanInfo)), + GFP_KERNEL); + if (!skb) + return -ENOMEM; + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_type = WLAN_NL_MSG_OEM; + ani_hdr = NLMSG_DATA(nlh); + ani_hdr->type = ANI_MSG_CHANNEL_INFO_RSP; + + ani_hdr->length = + sizeof(uint8_t) + numOfChannels * sizeof(*pHddChanInfo); + nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + ani_hdr->length)); + + /* First byte of message body will have num of channels */ + buf = (char *)((char *)ani_hdr + sizeof(tAniMsgHdr)); + *buf++ = numOfChannels; + + /* Next follows channel info struct for each channel id. + * If chan id is wrong or SME returns failure for a channel + * then fill in 0 in channel info for that particular channel + */ + for (i = 0; i < numOfChannels; i++) { + pHddChanInfo = (struct hdd_channel_info *) ((char *)buf + + i * + sizeof(*pHddChanInfo)); + + chan_freq = wlan_reg_legacy_chan_to_freq( + p_hdd_ctx->pdev, chanList[i]); + status = sme_get_reg_info(p_hdd_ctx->mac_handle, chan_freq, + ®_info_1, ®_info_2); + if (QDF_STATUS_SUCCESS == status) { + /* copy into hdd chan info struct */ + hddChanInfo.reserved0 = 0; + hddChanInfo.mhz = chan_freq; + hddChanInfo.band_center_freq1 = hddChanInfo.mhz; + hddChanInfo.band_center_freq2 = 0; + + hddChanInfo.info = 0; + if (CHANNEL_STATE_DFS == + wlan_reg_get_channel_state_for_freq( + p_hdd_ctx->pdev, chan_freq)) + WMI_SET_CHANNEL_FLAG(&hddChanInfo, + WMI_CHAN_FLAG_DFS); + + hdd_update_channel_bw_info(p_hdd_ctx, + chan_freq, &hddChanInfo); + hddChanInfo.reg_info_1 = reg_info_1; + hddChanInfo.reg_info_2 = reg_info_2; + } else { + /* channel info is not returned, fill in zeros in + * channel info struct + */ + hdd_debug("sme_get_reg_info failed for chan: %d, fill 0s", + chan_freq); + hddChanInfo.reserved0 = 0; + hddChanInfo.mhz = chan_freq; + hddChanInfo.band_center_freq1 = 0; + hddChanInfo.band_center_freq2 = 0; + hddChanInfo.info = 0; + hddChanInfo.reg_info_1 = 0; + hddChanInfo.reg_info_2 = 0; + } + qdf_mem_copy(pHddChanInfo, &hddChanInfo, + sizeof(*pHddChanInfo)); + } + + skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + ani_hdr->length))); + + hdd_debug("sending channel info resp for num channels (%d) to pid (%d)", + numOfChannels, p_hdd_ctx->oem_pid); + + (void)nl_srv_ucast_oem(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT); + + return 0; +} + +/** + * oem_process_set_cap_req_msg() - process oem set capability request + * @oem_cap_len: Length of OEM capability + * @oem_cap: Pointer to OEM capability buffer + * @app_pid: process ID, to which rsp message is to be sent + * + * This function sends oem message to SME + * + * Return: error code + */ +static int oem_process_set_cap_req_msg(int oem_cap_len, + char *oem_cap, int32_t app_pid) +{ + QDF_STATUS status; + int error_code; + struct sk_buff *skb; + struct nlmsghdr *nlh; + tAniMsgHdr *ani_hdr; + uint8_t *buf; + + if (!oem_cap) { + hdd_err("oem_cap is null"); + return -EINVAL; + } + + status = sme_oem_update_capability(p_hdd_ctx->mac_handle, + (struct sme_oem_capability *)oem_cap); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("error updating rm capability, status: %d", status); + error_code = qdf_status_to_os_return(status); + + skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_type = WLAN_NL_MSG_OEM; + ani_hdr = NLMSG_DATA(nlh); + ani_hdr->type = ANI_MSG_SET_OEM_CAP_RSP; + /* 64 bit alignment */ + ani_hdr->length = sizeof(error_code); + nlh->nlmsg_len = NLMSG_LENGTH(sizeof(tAniMsgHdr) + ani_hdr->length); + + /* message body will contain only status code */ + buf = (char *)((char *)ani_hdr + sizeof(tAniMsgHdr)); + qdf_mem_copy(buf, &error_code, ani_hdr->length); + + skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + ani_hdr->length)); + + hdd_debug("sending oem response to pid %d", app_pid); + + (void)nl_srv_ucast_oem(skb, app_pid, MSG_DONTWAIT); + + return error_code; +} + +/** + * oem_process_get_cap_req_msg() - process oem get capability request + * + * This function process the get capability request from OEM and responds + * with the capability. + * + * Return: error code + */ +static int oem_process_get_cap_req_msg(void) +{ + int error_code; + struct oem_get_capability_rsp *cap_rsp; + struct oem_data_cap data_cap = { {0} }; + struct sme_oem_capability oem_cap; + struct hdd_adapter *adapter; + struct sk_buff *skb; + struct nlmsghdr *nlh; + tAniMsgHdr *ani_hdr; + uint8_t *buf; + + /* for now, STA interface only */ + adapter = hdd_get_adapter(p_hdd_ctx, QDF_STA_MODE); + if (!adapter) { + hdd_err("No adapter for STA mode"); + return -EINVAL; + } + + error_code = populate_oem_data_cap(adapter, &data_cap); + if (0 != error_code) + return error_code; + + skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) + sizeof(*cap_rsp)), + GFP_KERNEL); + if (!skb) + return -ENOMEM; + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_type = WLAN_NL_MSG_OEM; + ani_hdr = NLMSG_DATA(nlh); + ani_hdr->type = ANI_MSG_GET_OEM_CAP_RSP; + + ani_hdr->length = sizeof(*cap_rsp); + nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + ani_hdr->length)); + + buf = (char *)((char *)ani_hdr + sizeof(tAniMsgHdr)); + qdf_mem_copy(buf, &data_cap, sizeof(data_cap)); + + buf = (char *) buf + sizeof(data_cap); + qdf_mem_zero(&oem_cap, sizeof(oem_cap)); + sme_oem_get_capability(p_hdd_ctx->mac_handle, &oem_cap); + qdf_mem_copy(buf, &oem_cap, sizeof(oem_cap)); + + skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + ani_hdr->length))); + hdd_info("send rsp to oem-pid:%d for get_capability", + p_hdd_ctx->oem_pid); + + (void)nl_srv_ucast_oem(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT); + return 0; +} + +void hdd_send_peer_status_ind_to_oem_app(struct qdf_mac_addr *peer_mac, + uint8_t peer_status, + uint8_t peer_capability, + uint8_t vdev_id, + struct oem_channel_info *chan_info, + enum QDF_OPMODE dev_mode) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + tAniMsgHdr *ani_hdr; + struct peer_status_info *peer_info; + + if (!p_hdd_ctx) { + hdd_err("HDD Ctx is null"); + return; + } + + /* check if oem app has registered and pid is valid */ + if ((!p_hdd_ctx->oem_app_registered) || (p_hdd_ctx->oem_pid == 0)) { + hdd_info("OEM app is not registered(%d) or pid is invalid(%d)", + p_hdd_ctx->oem_app_registered, + p_hdd_ctx->oem_pid); + return; + } + + skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) + + sizeof(*peer_info)), + GFP_KERNEL); + if (!skb) + return; + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_type = WLAN_NL_MSG_OEM; + ani_hdr = NLMSG_DATA(nlh); + ani_hdr->type = ANI_MSG_PEER_STATUS_IND; + + ani_hdr->length = sizeof(*peer_info); + nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + ani_hdr->length)); + + peer_info = (struct peer_status_info *) ((char *)ani_hdr + sizeof(tAniMsgHdr)); + qdf_mem_zero(peer_info, sizeof(*peer_info)); + qdf_mem_copy(peer_info->peer_mac_addr, peer_mac->bytes, + sizeof(peer_mac->bytes)); + peer_info->peer_status = peer_status; + peer_info->vdev_id = vdev_id; + peer_info->peer_capability = peer_capability; + /* Set 0th bit of reserved0 for STA mode */ + if (QDF_STA_MODE == dev_mode) + peer_info->reserved0 |= 0x01; + + if (chan_info) { + peer_info->peer_chan_info.reserved0 = 0; + peer_info->peer_chan_info.mhz = chan_info->mhz; + peer_info->peer_chan_info.band_center_freq1 = + chan_info->band_center_freq1; + peer_info->peer_chan_info.band_center_freq2 = + chan_info->band_center_freq2; + peer_info->peer_chan_info.info = chan_info->info; + peer_info->peer_chan_info.reg_info_1 = chan_info->reg_info_1; + peer_info->peer_chan_info.reg_info_2 = chan_info->reg_info_2; + } + skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + ani_hdr->length))); + + hdd_info("sending peer " QDF_MAC_ADDR_FMT + " status(%d), peer_capability(%d), vdev_id(%d)," + " to oem app pid(%d), center freq 1 (%d), center freq 2 (%d)," + " info (0x%x), frequency (%d),reg info 1 (0x%x)," + " reg info 2 (0x%x)", + QDF_MAC_ADDR_REF(peer_mac->bytes), + peer_status, peer_capability, + vdev_id, p_hdd_ctx->oem_pid, + peer_info->peer_chan_info.band_center_freq1, + peer_info->peer_chan_info.band_center_freq2, + peer_info->peer_chan_info.info, + peer_info->peer_chan_info.mhz, + peer_info->peer_chan_info.reg_info_1, + peer_info->peer_chan_info.reg_info_2); + + (void)nl_srv_ucast_oem(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT); +} + +/** + * oem_app_reg_req_handler() - function to handle APP registration request + * from userspace + * @hdd_ctx: handle to HDD context + * @msg_hdr: pointer to ANI message header + * @pid: Process ID + * + * Return: 0 if success, error code otherwise + */ +static int oem_app_reg_req_handler(struct hdd_context *hdd_ctx, + tAniMsgHdr *msg_hdr, int pid) +{ + char *sign_str = NULL; + + /* Registration request is only allowed for Qualcomm Application */ + hdd_debug("Received App Req Req from App pid: %d len: %d", + pid, msg_hdr->length); + + sign_str = (char *)((char *)msg_hdr + sizeof(tAniMsgHdr)); + if ((OEM_APP_SIGNATURE_LEN == msg_hdr->length) && + (0 == strncmp(sign_str, OEM_APP_SIGNATURE_STR, + OEM_APP_SIGNATURE_LEN))) { + hdd_debug("Valid App Req Req from oem app pid: %d", pid); + + hdd_ctx->oem_app_registered = true; + hdd_ctx->oem_pid = pid; + send_oem_reg_rsp_nlink_msg(); + } else { + hdd_err("Invalid signature in App Reg Req from pid: %d", pid); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_SIGNATURE); + return -EPERM; + } + + return 0; +} + +/** + * oem_data_req_handler() - function to handle data_req from userspace + * @hdd_ctx: handle to HDD context + * @msg_hdr: pointer to ANI message header + * @pid: Process ID + * + * Return: 0 if success, error code otherwise + */ +static int oem_data_req_handler(struct hdd_context *hdd_ctx, + tAniMsgHdr *msg_hdr, int pid) +{ + hdd_debug("Received Oem Data Request length: %d from pid: %d", + msg_hdr->length, pid); + + if ((!hdd_ctx->oem_app_registered) || + (pid != hdd_ctx->oem_pid)) { + /* either oem app is not registered yet or pid is different */ + hdd_err("OEM DataReq: app not registered(%d) or incorrect pid(%d)", + hdd_ctx->oem_app_registered, pid); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_APP_NOT_REGISTERED); + return -EPERM; + } + + if ((!msg_hdr->length) || (OEM_DATA_REQ_SIZE < msg_hdr->length)) { + hdd_err("Invalid length (%d) in Oem Data Request", + msg_hdr->length); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_MESSAGE_LENGTH); + return -EPERM; + } + + oem_process_data_req_msg(msg_hdr->length, + (char *) ((char *)msg_hdr + + sizeof(tAniMsgHdr))); + + return 0; +} + +/** + * oem_chan_info_req_handler() - function to handle chan_info_req from userspace + * @hdd_ctx: handle to HDD context + * @msg_hdr: pointer to ANI message header + * @pid: Process ID + * + * Return: 0 if success, error code otherwise + */ +static int oem_chan_info_req_handler(struct hdd_context *hdd_ctx, + tAniMsgHdr *msg_hdr, int pid) +{ + hdd_debug("Received channel info request, num channel(%d) from pid: %d", + msg_hdr->length, pid); + + if ((!hdd_ctx->oem_app_registered) || + (pid != hdd_ctx->oem_pid)) { + /* either oem app is not registered yet or pid is different */ + hdd_err("Chan InfoReq: app not registered(%d) or incorrect pid(%d)", + hdd_ctx->oem_app_registered, pid); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_APP_NOT_REGISTERED); + return -EPERM; + } + + /* message length contains list of channel ids */ + if ((!msg_hdr->length) || + (CFG_VALID_CHANNEL_LIST_LEN < msg_hdr->length)) { + hdd_err("Invalid length (%d) in channel info request", + msg_hdr->length); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_MESSAGE_LENGTH); + return -EPERM; + } + oem_process_channel_info_req_msg(msg_hdr->length, + (char *)((char *)msg_hdr + sizeof(tAniMsgHdr))); + + return 0; +} + +/** + * oem_set_cap_req_handler() - function to handle set_cap_req from userspace + * @hdd_ctx: handle to HDD context + * @msg_hdr: pointer to ANI message header + * @pid: Process ID + * + * Return: 0 if success, error code otherwise + */ +static int oem_set_cap_req_handler(struct hdd_context *hdd_ctx, + tAniMsgHdr *msg_hdr, int pid) +{ + hdd_info("Received set oem cap req of length:%d from pid: %d", + msg_hdr->length, pid); + + if ((!hdd_ctx->oem_app_registered) || + (pid != hdd_ctx->oem_pid)) { + /* oem app is not registered yet or pid is different */ + hdd_err("set_oem_capability : app not registered(%d) or incorrect pid(%d)", + hdd_ctx->oem_app_registered, pid); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_APP_NOT_REGISTERED); + return -EPERM; + } + + if ((!msg_hdr->length) || + (sizeof(struct sme_oem_capability) < msg_hdr->length)) { + hdd_err("Invalid length (%d) in set_oem_capability", + msg_hdr->length); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_MESSAGE_LENGTH); + return -EPERM; + } + + oem_process_set_cap_req_msg(msg_hdr->length, (char *) + ((char *)msg_hdr + sizeof(tAniMsgHdr)), + pid); + return 0; +} + +/** + * oem_get_cap_req_handler() - function to handle get_cap_req from userspace + * @hdd_ctx: handle to HDD context + * @msg_hdr: pointer to ANI message header + * @pid: Process ID + * + * Return: 0 if success, error code otherwise + */ +static int oem_get_cap_req_handler(struct hdd_context *hdd_ctx, + tAniMsgHdr *msg_hdr, int pid) +{ + hdd_info("Rcvd get oem capability req - length:%d from pid: %d", + msg_hdr->length, pid); + + if ((!hdd_ctx->oem_app_registered) || + (pid != hdd_ctx->oem_pid)) { + /* oem app is not registered yet or pid is different */ + hdd_err("get_oem_capability : app not registered(%d) or incorrect pid(%d)", + hdd_ctx->oem_app_registered, pid); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_APP_NOT_REGISTERED); + return -EPERM; + } + + oem_process_get_cap_req_msg(); + return 0; +} + +/** + * oem_request_dispatcher() - OEM command dispatcher API + * @msg_hdr: ANI Message Header + * @pid: process id + * + * This API is used to dispatch the command from OEM depending + * on the type of the message received. + * + * Return: None + */ +static void oem_request_dispatcher(tAniMsgHdr *msg_hdr, int pid) +{ + switch (msg_hdr->type) { + case ANI_MSG_APP_REG_REQ: + oem_app_reg_req_handler(p_hdd_ctx, msg_hdr, pid); + break; + + case ANI_MSG_OEM_DATA_REQ: + oem_data_req_handler(p_hdd_ctx, msg_hdr, pid); + break; + + case ANI_MSG_CHANNEL_INFO_REQ: + oem_chan_info_req_handler(p_hdd_ctx, msg_hdr, pid); + break; + + case ANI_MSG_SET_OEM_CAP_REQ: + oem_set_cap_req_handler(p_hdd_ctx, msg_hdr, pid); + break; + + case ANI_MSG_GET_OEM_CAP_REQ: + oem_get_cap_req_handler(p_hdd_ctx, msg_hdr, pid); + break; + + default: + hdd_err("Received Invalid message type (%d), length (%d)", + msg_hdr->type, msg_hdr->length); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_MESSAGE_TYPE); + } +} + +#ifdef CNSS_GENL +/** + * oem_cmd_handler() - API to handle OEM commands + * @data: Pointer to data + * @data_len: length of the received data + * @ctx: Pointer to the context + * @pid: Process id + * + * This API handles the command from OEM application from user space and + * send back event to user space if necessary. + * + * Return: None + */ +static void oem_cmd_handler(const void *data, int data_len, void *ctx, int pid) +{ + tAniMsgHdr *msg_hdr; + int msg_len; + int ret; + struct nlattr *tb[CLD80211_ATTR_MAX + 1]; + + ret = wlan_hdd_validate_context(p_hdd_ctx); + if (ret) { + hdd_err("hdd ctx validate fails"); + return; + } + + /* + * audit note: it is ok to pass a NULL policy here since only + * one attribute is parsed and it is explicitly validated + */ + if (wlan_cfg80211_nla_parse(tb, CLD80211_ATTR_MAX, + data, data_len, NULL)) { + hdd_err("Invalid ATTR"); + return; + } + + if (!tb[CLD80211_ATTR_DATA]) { + hdd_err("attr ATTR_DATA failed"); + return; + } + + msg_len = nla_len(tb[CLD80211_ATTR_DATA]); + if (msg_len < sizeof(*msg_hdr)) { + hdd_err("runt ATTR_DATA size %d", msg_len); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_NULL_MESSAGE_HEADER); + return; + } + + msg_hdr = nla_data(tb[CLD80211_ATTR_DATA]); + if (msg_len < (sizeof(*msg_hdr) + msg_hdr->length)) { + hdd_err("Invalid nl msg len %d, msg hdr len %d", + msg_len, msg_hdr->length); + send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_MESSAGE_LENGTH); + return; + } + + oem_request_dispatcher(msg_hdr, pid); +} + +int oem_activate_service(struct hdd_context *hdd_ctx) +{ + p_hdd_ctx = hdd_ctx; + register_cld_cmd_cb(WLAN_NL_MSG_OEM, oem_cmd_handler, NULL); + return 0; +} + +int oem_deactivate_service(void) +{ + deregister_cld_cmd_cb(WLAN_NL_MSG_OEM); + return 0; +} +#else + +/* + * Callback function invoked by Netlink service for all netlink + * messages (from user space) addressed to WLAN_NL_MSG_OEM + */ + +/** + * oem_msg_callback() - callback invoked by netlink service + * @skb: skb with netlink message + * + * This function gets invoked by netlink service when a message + * is received from user space addressed to WLAN_NL_MSG_OEM + * + * Return: zero on success + * On error, error number will be returned. + */ +static int oem_msg_callback(struct sk_buff *skb) +{ + struct nlmsghdr *nlh; + tAniMsgHdr *msg_hdr; + int ret; + + nlh = (struct nlmsghdr *)skb->data; + if (!nlh) { + hdd_err("Netlink header null"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(p_hdd_ctx); + if (ret) + return ret; + + msg_hdr = NLMSG_DATA(nlh); + + if (!msg_hdr) { + hdd_err("Message header null"); + send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid, + OEM_ERR_NULL_MESSAGE_HEADER); + return -EPERM; + } + + if (nlh->nlmsg_len < + NLMSG_LENGTH(sizeof(tAniMsgHdr) + msg_hdr->length)) { + hdd_err("Invalid nl msg len, nlh->nlmsg_len (%d), msg_hdr->len (%d)", + nlh->nlmsg_len, msg_hdr->length); + send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid, + OEM_ERR_INVALID_MESSAGE_LENGTH); + return -EPERM; + } + + oem_request_dispatcher(msg_hdr, nlh->nlmsg_pid); + return 0; +} + +static int __oem_msg_callback(struct sk_buff *skb) +{ + struct hdd_context *hdd_ctx = p_hdd_ctx; + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + errno = osif_psoc_sync_op_start(hdd_ctx->parent_dev, &psoc_sync); + if (errno) + return errno; + + errno = oem_msg_callback(skb); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +int oem_activate_service(struct hdd_context *hdd_ctx) +{ + p_hdd_ctx = hdd_ctx; + + /* Register the msg handler for msgs addressed to WLAN_NL_MSG_OEM */ + return nl_srv_register(WLAN_NL_MSG_OEM, __oem_msg_callback); +} + +int oem_deactivate_service(void) +{ + /* Deregister the msg handler for msgs addressed to WLAN_NL_MSG_OEM */ + return nl_srv_unregister(WLAN_NL_MSG_OEM, __oem_msg_callback); +} + +#endif +#endif + +#ifdef FEATURE_OEM_DATA +static const struct nla_policy +oem_data_attr_policy[QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA] = { + .type = NLA_BINARY, + .len = OEM_DATA_MAX_SIZE + }, + + [QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED] = {.type = NLA_FLAG}, +}; + +void hdd_oem_event_handler_cb(const struct oem_data *oem_event_data, + uint8_t vdev_id) +{ + struct sk_buff *vendor_event; + struct osif_request *request; + uint32_t len; + int ret; + struct oem_data *oem_data; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct hdd_adapter *hdd_adapter = hdd_get_adapter_by_vdev(hdd_ctx, + vdev_id); + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return; + + if (hdd_validate_adapter(hdd_adapter)) + return; + + if (!oem_event_data || !(oem_event_data->data)) { + hdd_err("Invalid oem event data"); + return; + } + + if (hdd_adapter->response_expected) { + request = osif_request_get(hdd_adapter->cookie); + if (!request) { + hdd_err("Invalid request"); + return; + } + + oem_data = osif_request_priv(request); + oem_data->data_len = oem_event_data->data_len; + oem_data->data = qdf_mem_malloc(oem_data->data_len); + if (!oem_data->data) { + hdd_err("Memory allocation failure"); + return; + } + qdf_mem_copy(oem_data->data, oem_event_data->data, + oem_data->data_len); + oem_data->vdev_id = hdd_adapter->vdev_id; + osif_request_complete(request); + osif_request_put(request); + } else { + len = nla_total_size(oem_event_data->data_len) + NLMSG_HDRLEN; + vendor_event = + cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, NULL, len, + QCA_NL80211_VENDOR_SUBCMD_OEM_DATA_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + ret = nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA, + oem_event_data->data_len, oem_event_data->data); + if (ret) { + hdd_err("OEM event put fails status %d", ret); + kfree_skb(vendor_event); + return; + } + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + } + + hdd_exit(); +} + +/** + *wlan_hdd_free_oem_data: delete data of priv data + *@priv: osif request private data + * + *Return: void + */ +static void wlan_hdd_free_oem_data(void *priv) +{ + struct oem_data *local_priv = priv; + + if (!local_priv) + return; + + if (local_priv->data) { + qdf_mem_free(local_priv->data); + local_priv->data = NULL; + } +} + +/** + * __wlan_hdd_cfg80211_oem_data_handler() - the handler for oem data + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +static int +__wlan_hdd_cfg80211_oem_data_handler(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + QDF_STATUS status = QDF_STATUS_SUCCESS; + int ret; + struct sk_buff *skb = NULL; + struct oem_data oem_data = {0}; + struct oem_data *get_oem_data = NULL; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_MAX + 1]; + struct osif_request *request = NULL; + struct oem_data *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_GET_OEM_DATA, + .dealloc = wlan_hdd_free_oem_data, + }; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (hdd_validate_adapter(adapter)) + return -EINVAL; + + if (adapter->oem_data_in_progress) { + hdd_err("oem request already in progress"); + return -EBUSY; + } + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_MAX, + data, data_len, oem_data_attr_policy)) { + hdd_err("Invalid attributes"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA]) { + hdd_err("oem data is missing!"); + return -EINVAL; + } + + oem_data.data_len = + nla_len(tb[QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA]); + if (!oem_data.data_len) { + hdd_err("oem data len is 0!"); + return -EINVAL; + } + oem_data.vdev_id = adapter->vdev_id; + oem_data.data = nla_data(tb[QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA]); + + if (tb[QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED]) + adapter->response_expected = nla_get_flag( + tb[QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED]); + + if (adapter->response_expected) { + int skb_len = 0; + + adapter->oem_data_in_progress = true; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("request allocation failure"); + ret = -ENOMEM; + goto err; + } + + adapter->cookie = osif_request_cookie(request); + + status = sme_oem_data_cmd(hdd_ctx->mac_handle, + hdd_oem_event_handler_cb, + &oem_data, adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failure while sending command to fw"); + ret = -EAGAIN; + goto err; + } + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("Timedout while retrieving oem get data"); + goto err; + } + + get_oem_data = osif_request_priv(request); + if (!get_oem_data || !(get_oem_data->data)) { + hdd_err("invalid get_oem_data"); + ret = -EINVAL; + goto err; + } + + skb_len = NLMSG_HDRLEN + NLA_HDRLEN + get_oem_data->data_len; + + skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wiphy, + skb_len); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + ret = -ENOMEM; + goto err; + } + + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA, + get_oem_data->data_len, get_oem_data->data)) { + hdd_err("nla put failure"); + kfree_skb(skb); + ret = -EINVAL; + goto err; + } + wlan_cfg80211_vendor_cmd_reply(skb); + + } else { + status = sme_oem_data_cmd(hdd_ctx->mac_handle, + hdd_oem_event_handler_cb, + &oem_data, adapter->vdev_id); + return qdf_status_to_os_return(status); + } + +err: + if (request) + osif_request_put(request); + adapter->oem_data_in_progress = false; + adapter->response_expected = false; + + return ret; + +} + +int wlan_hdd_cfg80211_oem_data_handler(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int ret; + + ret = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (ret) + return ret; + + ret = __wlan_hdd_cfg80211_oem_data_handler(wiphy, wdev, + data, data_len); + osif_vdev_sync_op_stop(vdev_sync); + + return ret; +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ota_test.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ota_test.c new file mode 100644 index 0000000000000000000000000000000000000000..ca95ea1ee483fb1a1f6d6205155a8f476820f5dc --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ota_test.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_ota_test.c + * + * WLAN OTA test functions + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include + +static const struct +nla_policy +qca_wlan_vendor_ota_test_policy +[QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE] = {.type = NLA_U8 }, +}; + +/** + * __wlan_hdd_cfg80211_set_ota_test() - enable/disable OTA test + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_set_ota_test(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX + 1]; + uint8_t ota_enable = 0; + QDF_STATUS status; + uint32_t current_roam_state; + mac_handle_t mac_handle; + + hdd_enter_dev(dev); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_hdd_validate_context(hdd_ctx) != 0) + return -EINVAL; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX, + data, data_len, + qca_wlan_vendor_ota_test_policy)) { + hdd_err("invalid attr"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE]) { + hdd_err("attr ota test failed"); + return -EINVAL; + } + + ota_enable = nla_get_u8( + tb[QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE]); + + hdd_debug(" OTA test enable = %d", ota_enable); + if (ota_enable != 1) { + hdd_err("Invalid value, only enable test mode is supported!"); + return -EINVAL; + } + + mac_handle = hdd_ctx->mac_handle; + current_roam_state = + sme_get_current_roam_state(mac_handle, adapter->vdev_id); + status = sme_stop_roaming(mac_handle, adapter->vdev_id, + eCsrHddIssued, RSO_INVALID_REQUESTOR); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Enable/Disable roaming failed"); + return -EINVAL; + } + + status = sme_ps_enable_disable(mac_handle, adapter->vdev_id, + SME_PS_DISABLE); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Enable/Disable power save failed"); + /* restore previous roaming setting */ + if (current_roam_state == eCSR_ROAMING_STATE_JOINING || + current_roam_state == eCSR_ROAMING_STATE_JOINED) + status = sme_start_roaming(mac_handle, + adapter->vdev_id, + eCsrHddIssued, + RSO_INVALID_REQUESTOR); + else if (current_roam_state == eCSR_ROAMING_STATE_STOP || + current_roam_state == eCSR_ROAMING_STATE_IDLE) + status = sme_stop_roaming(mac_handle, + adapter->vdev_id, + eCsrHddIssued, + RSO_INVALID_REQUESTOR); + + if (status != QDF_STATUS_SUCCESS) + hdd_err("Restoring roaming state failed"); + + return -EINVAL; + } + return 0; +} + +int wlan_hdd_cfg80211_set_ota_test(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_ota_test(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ota_test.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ota_test.h new file mode 100644 index 0000000000000000000000000000000000000000..9d950b808171c14243caa90e2d2b1dc540394406 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_ota_test.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_OTA_TEST_H +#define __WLAN_HDD_OTA_TEST_H + +/** + * DOC: wlan_hdd_ota_test_h + * + * WLAN Host Device Driver OTA test API specification + */ + +#ifdef FEATURE_OTA_TEST +/** + * wlan_hdd_cfg80211_set_ota_test () - Enable or disable OTA test + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_set_ota_test(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_OTA_TEST_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OTA_TEST, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_set_ota_test \ +}, +#else /* FEATURE_OTA_TEST */ +#define FEATURE_OTA_TEST_VENDOR_COMMANDS +#endif /* FEATURE_OTA_TEST */ + +#endif /* __WLAN_HDD_OTA_TEST_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p.c new file mode 100644 index 0000000000000000000000000000000000000000..b47d4b072b96497dde1db781e26bba1e87e1be25 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p.c @@ -0,0 +1,1413 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * + * @file wlan_hdd_p2p.c + * + * @brief WLAN Host Device Driver implementation for P2P commands interface + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include "sme_api.h" +#include "sme_qos_api.h" +#include "wlan_hdd_p2p.h" +#include "sap_api.h" +#include "wlan_hdd_main.h" +#include "qdf_trace.h" +#include +#include +#include +#include +#include "wlan_hdd_tdls.h" +#include "wlan_hdd_trace.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "cds_sched.h" +#include "wlan_policy_mgr_api.h" +#include "cds_utils.h" +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_cfg80211_p2p.h" +#include "wlan_p2p_cfg_api.h" +#include "wlan_policy_mgr_ucfg.h" +#include "nan_ucfg_api.h" + +/* Ms to Time Unit Micro Sec */ +#define MS_TO_TU_MUS(x) ((x) * 1024) +#define MAX_MUS_VAL (INT_MAX / 1024) + +#ifdef WLAN_FEATURE_P2P_DEBUG +#define MAX_P2P_ACTION_FRAME_TYPE 9 +const char *p2p_action_frame_type[] = { "GO Negotiation Request", + "GO Negotiation Response", + "GO Negotiation Confirmation", + "P2P Invitation Request", + "P2P Invitation Response", + "Device Discoverability Request", + "Device Discoverability Response", + "Provision Discovery Request", + "Provision Discovery Response"}; + +#endif +#define MAX_TDLS_ACTION_FRAME_TYPE 11 +const char *tdls_action_frame_type[] = { "TDLS Setup Request", + "TDLS Setup Response", + "TDLS Setup Confirm", + "TDLS Teardown", + "TDLS Peer Traffic Indication", + "TDLS Channel Switch Request", + "TDLS Channel Switch Response", + "TDLS Peer PSM Request", + "TDLS Peer PSM Response", + "TDLS Peer Traffic Response", + "TDLS Discovery Request"}; + +void wlan_hdd_cancel_existing_remain_on_channel(struct hdd_adapter *adapter) +{ + if (!adapter) { + hdd_err("null adapter"); + return; + } + + ucfg_p2p_cleanup_roc_by_vdev(adapter->vdev); +} + +int wlan_hdd_check_remain_on_channel(struct hdd_adapter *adapter) +{ + if (QDF_P2P_GO_MODE != adapter->device_mode) + wlan_hdd_cancel_existing_remain_on_channel(adapter); + + return 0; +} + +/* Clean up RoC context at hdd_stop_adapter*/ +void wlan_hdd_cleanup_remain_on_channel_ctx(struct hdd_adapter *adapter) +{ + if (!adapter) { + hdd_err("null adapter"); + return; + } + + ucfg_p2p_cleanup_roc_by_vdev(adapter->vdev); +} + +void wlan_hdd_cleanup_actionframe(struct hdd_adapter *adapter) +{ + if (!adapter) { + hdd_err("null adapter"); + return; + } + + ucfg_p2p_cleanup_tx_by_vdev(adapter->vdev); +} + +static int __wlan_hdd_cfg80211_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, + u64 *cookie) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + QDF_STATUS status; + int ret; + + hdd_enter(); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + /* Disable NAN Discovery if enabled */ + ucfg_nan_disable_concurrency(hdd_ctx->psoc); + + status = wlan_cfg80211_roc(adapter->vdev, chan, duration, cookie); + hdd_debug("remain on channel request, status:%d, cookie:0x%llx", + status, *cookie); + + return qdf_status_to_os_return(status); +} + +int wlan_hdd_cfg80211_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, u64 *cookie) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_remain_on_channel(wiphy, wdev, chan, + duration, cookie); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int +__wlan_hdd_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +{ + QDF_STATUS status; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + status = wlan_cfg80211_cancel_roc(adapter->vdev, cookie); + hdd_debug("cancel remain on channel, status:%d", status); + + return 0; +} + +int wlan_hdd_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_cancel_remain_on_channel(wiphy, wdev, + cookie); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_validate_and_override_offchan() - To validate and override offchan + * @adapter: hdd adapter of vdev + * @chan: channel info of mgmt to be sent + * @offchan: off channel flag to check and override + * + * This function is to validate the channel info against adapter current state + * and home channel, if off channel not needed, override offchan flag. + * + * Return: None + */ +static void +wlan_hdd_validate_and_override_offchan(struct hdd_adapter *adapter, + struct ieee80211_channel *chan, + bool *offchan) +{ + uint8_t home_ch; + + if (!offchan || !chan || !(*offchan)) + return; + + home_ch = hdd_get_adapter_home_channel(adapter); + + if (ieee80211_frequency_to_channel(chan->center_freq) == home_ch) { + hdd_debug("override offchan to 0 at home channel %d", home_ch); + *offchan = false; + } +} + +static int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *chan, bool offchan, + unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie) +{ + QDF_STATUS status; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t type; + uint8_t sub_type; + QDF_STATUS qdf_status; + int ret; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) { + hdd_err("wlan_hdd_validate_context return:%d", ret); + return ret; + } + + type = WLAN_HDD_GET_TYPE_FRM_FC(buf[0]); + sub_type = WLAN_HDD_GET_SUBTYPE_FRM_FC(buf[0]); + + /* When frame to be transmitted is auth mgmt, then trigger + * sme_send_mgmt_tx to send auth frame without need for policy manager. + * Where as wlan_cfg80211_mgmt_tx requires roc and requires approval + * from policy manager. + */ + if ((adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_SAP_MODE) && + (type == SIR_MAC_MGMT_FRAME && + sub_type == SIR_MAC_MGMT_AUTH)) { + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_SME, + TRACE_CODE_HDD_SEND_MGMT_TX, + wlan_vdev_get_id(adapter->vdev), 0); + + qdf_status = sme_send_mgmt_tx(hdd_ctx->mac_handle, + adapter->vdev_id, buf, len); + + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + return 0; + else + return -EINVAL; + } + + hdd_debug("device_mode:%d type:%d sub_type:%d chan:%d", + adapter->device_mode, type, sub_type, + chan ? chan->center_freq : 0); + hdd_debug("wait:%d offchan:%d do_not_wait_ack:%d", + wait, offchan, dont_wait_for_ack); + + wlan_hdd_validate_and_override_offchan(adapter, chan, &offchan); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_OS_IF, + TRACE_CODE_HDD_SEND_MGMT_TX, + wlan_vdev_get_id(adapter->vdev), 0); + + status = wlan_cfg80211_mgmt_tx(adapter->vdev, chan, offchan, wait, buf, + len, no_cck, dont_wait_for_ack, cookie); + hdd_debug("mgmt tx, status:%d, cookie:0x%llx", status, *cookie); + + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) +int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie) +#else +int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *chan, bool offchan, + unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie) +#endif /* LINUX_VERSION_CODE */ +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) + errno = __wlan_hdd_mgmt_tx(wiphy, wdev, params->chan, params->offchan, + params->wait, params->buf, params->len, + params->no_cck, params->dont_wait_for_ack, + cookie); +#else + errno = __wlan_hdd_mgmt_tx(wiphy, wdev, chan, offchan, + wait, buf, len, no_cck, + dont_wait_for_ack, cookie); +#endif /* LINUX_VERSION_CODE */ + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +{ + QDF_STATUS status; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + status = wlan_cfg80211_mgmt_tx_cancel(adapter->vdev, cookie); + hdd_debug("cancel mgmt tx, status:%d", status); + + return 0; +} + +int wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct wireless_dev *wdev, u64 cookie) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(wiphy, wdev, cookie); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_set_p2p_noa + * + ***FUNCTION: + * This function is called from hdd_hostapd_ioctl function when Driver + * get P2P_SET_NOA command from wpa_supplicant using private ioctl + * + ***LOGIC: + * Fill noa Struct According to P2P Power save Option and Pass it to SME layer + * + ***ASSUMPTIONS: + * + * + ***NOTE: + * + * @param dev Pointer to net device structure + * @param command Pointer to command + * + * @return Status + */ + +int hdd_set_p2p_noa(struct net_device *dev, uint8_t *command) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct p2p_ps_config noa = {0}; + int count, duration, interval; + char *param; + int ret; + + param = strnchr(command, strlen(command), ' '); + if (!param) { + hdd_err("strnchr failed to find delimeter"); + return -EINVAL; + } + param++; + ret = sscanf(param, "%d %d %d", &count, &interval, &duration); + if (ret != 3) { + hdd_err("P2P_SET GO noa: fail to read params, ret=%d", + ret); + return -EINVAL; + } + if (count < 0 || interval < 0 || duration < 0 || + interval > MAX_MUS_VAL || duration > MAX_MUS_VAL) { + hdd_err("Invalid NOA parameters"); + return -EINVAL; + } + hdd_debug("P2P_SET GO noa: count=%d interval=%d duration=%d", + count, interval, duration); + duration = MS_TO_TU_MUS(duration); + interval = MS_TO_TU_MUS(interval); + /* PS Selection + * Periodic noa (2) + * Single NOA (4) + */ + noa.opp_ps = 0; + noa.ct_window = 0; + if (count == 1) { + if (duration > interval) + duration = interval; + noa.duration = 0; + noa.single_noa_duration = duration; + noa.ps_selection = P2P_POWER_SAVE_TYPE_SINGLE_NOA; + } else { + if (count && (duration >= interval)) { + hdd_err("Duration should be less than interval"); + return -EINVAL; + } + noa.duration = duration; + noa.single_noa_duration = 0; + noa.ps_selection = P2P_POWER_SAVE_TYPE_PERIODIC_NOA; + } + noa.interval = interval; + noa.count = count; + noa.vdev_id = adapter->vdev_id; + + hdd_debug("P2P_PS_ATTR:opp ps %d ct window %d duration %d " + "interval %d count %d single noa duration %d " + "ps selection %x", noa.opp_ps, + noa.ct_window, noa.duration, noa.interval, + noa.count, noa.single_noa_duration, noa.ps_selection); + + return wlan_hdd_set_power_save(adapter, &noa); +} + +/** + * hdd_set_p2p_opps + * + ***FUNCTION: + * This function is called from hdd_hostapd_ioctl function when Driver + * get P2P_SET_PS command from wpa_supplicant using private ioctl + * + ***LOGIC: + * Fill noa Struct According to P2P Power save Option and Pass it to SME layer + * + ***ASSUMPTIONS: + * + * + ***NOTE: + * + * @param dev Pointer to net device structure + * @param command Pointer to command + * + * @return Status + */ + +int hdd_set_p2p_opps(struct net_device *dev, uint8_t *command) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct p2p_ps_config noa = {0}; + char *param; + int legacy_ps, opp_ps, ctwindow; + int ret; + + param = strnchr(command, strlen(command), ' '); + if (!param) { + hdd_err("strnchr failed to find delimiter"); + return -EINVAL; + } + param++; + ret = sscanf(param, "%d %d %d", &legacy_ps, &opp_ps, &ctwindow); + if (ret != 3) { + hdd_err("P2P_SET GO PS: fail to read params, ret=%d", ret); + return -EINVAL; + } + + if ((opp_ps != -1) && (opp_ps != 0) && (opp_ps != 1)) { + hdd_err("Invalid opp_ps value:%d", opp_ps); + return -EINVAL; + } + + /* P2P spec: 3.3.2 Power Management and discovery: + * CTWindow should be at least 10 TU. + * P2P spec: Table 27 - CTWindow and OppPS Parameters field format: + * CTWindow and OppPS Parameters together is 8 bits. + * CTWindow uses 7 bits (0-6, Bit 7 is for OppPS) + * 0 indicates that there shall be no CTWindow + */ + if ((ctwindow != -1) && (ctwindow != 0) && + (!((ctwindow >= 10) && (ctwindow <= 127)))) { + hdd_err("Invalid CT window value:%d", ctwindow); + return -EINVAL; + } + + hdd_debug("P2P_SET GO PS: legacy_ps=%d opp_ps=%d ctwindow=%d", + legacy_ps, opp_ps, ctwindow); + + /* PS Selection + * Opportunistic Power Save (1) + */ + + /* From wpa_cli user need to use separate command to set ct_window + * and Opps when user want to set ct_window during that time other + * parameters values are coming from wpa_supplicant as -1. + * Example : User want to set ct_window with 30 then wpa_cli command : + * P2P_SET ctwindow 30 + * Command Received at hdd_hostapd_ioctl is as below: + * P2P_SET_PS -1 -1 30 (legacy_ps = -1, opp_ps = -1, ctwindow = 30) + * + * e.g., 1: P2P_SET_PS 1 1 30 + * Driver sets the Opps and CTwindow as 30 and send it to FW. + * e.g., 2: P2P_SET_PS 1 -1 15 + * Driver caches the CTwindow value but not send the command to FW. + * e.g., 3: P2P_SET_PS 1 1 -1 + * Driver sends the command to FW with Opps enabled and CT window as + * 15 (last cached CTWindow value). + * (or) : P2P_SET_PS 1 1 20 + * Driver sends the command to FW with opps enabled and CT window + * as 20. + * + * legacy_ps param remains unused until required in the future. + */ + if (ctwindow != -1) + adapter->ctw = ctwindow; + + /* Send command to FW when OppPS is either enabled(1)/disbaled(0) */ + if (opp_ps != -1) { + adapter->ops = opp_ps; + noa.opp_ps = adapter->ops; + noa.ct_window = adapter->ctw; + noa.duration = 0; + noa.single_noa_duration = 0; + noa.interval = 0; + noa.count = 0; + noa.ps_selection = P2P_POWER_SAVE_TYPE_OPPORTUNISTIC; + noa.vdev_id = adapter->vdev_id; + + hdd_debug("P2P_PS_ATTR: opp ps %d ct window %d duration %d interval %d count %d single noa duration %d ps selection %x", + noa.opp_ps, noa.ct_window, + noa.duration, noa.interval, noa.count, + noa.single_noa_duration, + noa.ps_selection); + + wlan_hdd_set_power_save(adapter, &noa); + } + + return 0; +} + +int hdd_set_p2p_ps(struct net_device *dev, void *msgData) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct p2p_ps_config noa = {0}; + struct p2p_app_set_ps *pappnoa = (struct p2p_app_set_ps *) msgData; + + noa.opp_ps = pappnoa->opp_ps; + noa.ct_window = pappnoa->ct_window; + noa.duration = pappnoa->duration; + noa.interval = pappnoa->interval; + noa.count = pappnoa->count; + noa.single_noa_duration = pappnoa->single_noa_duration; + noa.ps_selection = pappnoa->ps_selection; + noa.vdev_id = adapter->vdev_id; + + return wlan_hdd_set_power_save(adapter, &noa); +} + +/** + * __wlan_hdd_add_virtual_intf() - Add virtual interface + * @wiphy: wiphy pointer + * @name: User-visible name of the interface + * @name_assign_type: the name of assign type of the netdev + * @nl80211_iftype: (virtual) interface types + * @flags: moniter configuraiton flags (not used) + * @vif_params: virtual interface parameters (not used) + * + * Return: the pointer of wireless dev, otherwise ERR_PTR. + */ +static +struct wireless_dev *__wlan_hdd_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_adapter *adapter = NULL; + bool p2p_dev_addr_admin = false; + enum QDF_OPMODE mode; + QDF_STATUS status; + int ret; + + hdd_enter(); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return ERR_PTR(-EINVAL); + } + + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) { + hdd_err("Concurrency not allowed with standalone monitor mode"); + return ERR_PTR(-EINVAL); + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ERR_PTR(ret); + + if (wlan_hdd_check_mon_concurrency()) + return ERR_PTR(-EINVAL); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_ADD_VIRTUAL_INTF, + NO_SESSION, type); + + status = hdd_nl_to_qdf_iface_type(type, &mode); + if (QDF_IS_STATUS_ERROR(status)) + return ERR_PTR(qdf_status_to_os_return(status)); + + switch (mode) { + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_STA_MODE: + break; + default: + mode = QDF_STA_MODE; + break; + } + + adapter = hdd_get_adapter(hdd_ctx, QDF_STA_MODE); + if (adapter && !wlan_hdd_validate_vdev_id(adapter->vdev_id)) { + if (ucfg_scan_get_vdev_status(adapter->vdev) != + SCAN_NOT_IN_PROGRESS) { + wlan_abort_scan(hdd_ctx->pdev, INVAL_PDEV_ID, + adapter->vdev_id, INVALID_SCAN_ID, + false); + } + } + + adapter = NULL; + ret = wlan_hdd_add_monitor_check(hdd_ctx, &adapter, type, name, + true, name_assign_type); + if (ret) + return ERR_PTR(-EINVAL); + if (adapter) { + hdd_exit(); + return adapter->dev->ieee80211_ptr; + } + + adapter = NULL; + cfg_p2p_get_device_addr_admin(hdd_ctx->psoc, &p2p_dev_addr_admin); + if (p2p_dev_addr_admin && + (mode == QDF_P2P_GO_MODE || mode == QDF_P2P_CLIENT_MODE)) { + /* + * Generate the P2P Interface Address. this address must be + * different from the P2P Device Address. + */ + struct qdf_mac_addr p2p_device_address = + hdd_ctx->p2p_device_address; + p2p_device_address.bytes[4] ^= 0x80; + adapter = hdd_open_adapter(hdd_ctx, mode, name, + p2p_device_address.bytes, + name_assign_type, true); + } else { + uint8_t *device_address; + + device_address = wlan_hdd_get_intf_addr(hdd_ctx, mode); + if (!device_address) + return ERR_PTR(-EINVAL); + + adapter = hdd_open_adapter(hdd_ctx, mode, name, + device_address, + name_assign_type, true); + if (!adapter) + wlan_hdd_release_intf_addr(hdd_ctx, device_address); + } + + if (!adapter) { + hdd_err("hdd_open_adapter failed"); + return ERR_PTR(-ENOSPC); + } + + adapter->delete_in_progress = false; + + /* ensure physcial soc is up */ + ret = hdd_trigger_psoc_idle_restart(hdd_ctx); + if (ret) { + hdd_err("Failed to start the wlan_modules"); + goto close_adapter; + } + + if (hdd_ctx->rps) + hdd_send_rps_ind(adapter); + + hdd_exit(); + + return adapter->dev->ieee80211_ptr; + +close_adapter: + hdd_close_adapter(hdd_ctx, adapter, true); + + return ERR_PTR(-EINVAL); +} + +static struct wireless_dev * +_wlan_hdd_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct wireless_dev *wdev; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_create_and_trans(wiphy_dev(wiphy), &vdev_sync); + if (errno) + return ERR_PTR(errno); + + wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type, + type, flags, params); + + if (IS_ERR_OR_NULL(wdev)) + goto destroy_sync; + + osif_vdev_sync_register(wdev->netdev, vdev_sync); + osif_vdev_sync_trans_stop(vdev_sync); + + return wdev; + +destroy_sync: + osif_vdev_sync_trans_stop(vdev_sync); + osif_vdev_sync_destroy(vdev_sync); + + return wdev; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + struct vif_params *params) +{ + return _wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type, + type, ¶ms->flags, params); +} +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) || defined(WITH_BACKPORTS) +struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + return _wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type, + type, flags, params); +} +#else +struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy, + const char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + return _wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type, + type, flags, params); +} +#endif + +int __wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct net_device *dev = wdev->netdev; + struct hdd_context *hdd_ctx = (struct hdd_context *) wiphy_priv(wiphy); + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int errno; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + /* + * Clear SOFTAP_INIT_DONE flag to mark SAP unload, so that we do + * not restart SAP after SSR as SAP is already stopped from user space. + */ + clear_bit(SOFTAP_INIT_DONE, &adapter->event_flags); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_DEL_VIRTUAL_INTF, + adapter->vdev_id, adapter->device_mode); + + hdd_debug("Device_mode %s(%d)", + qdf_opmode_str(adapter->device_mode), adapter->device_mode); + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + /* ensure physical soc is up */ + errno = hdd_trigger_psoc_idle_restart(hdd_ctx); + if (errno) + return errno; + + if (adapter->device_mode == QDF_SAP_MODE && + wlan_sap_is_pre_cac_active(hdd_ctx->mac_handle)) { + hdd_clean_up_pre_cac_interface(hdd_ctx); + } else if (wlan_hdd_is_session_type_monitor( + adapter->device_mode)) { + wlan_hdd_del_monitor(hdd_ctx, adapter, TRUE); + hdd_reset_pktcapture_cb(OL_TXRX_PDEV_ID); + } else { + wlan_hdd_release_intf_addr(hdd_ctx, + adapter->mac_addr.bytes); + hdd_stop_adapter(hdd_ctx, adapter); + hdd_deinit_adapter(hdd_ctx, adapter, true); + hdd_close_adapter(hdd_ctx, adapter, true); + } + + hdd_exit(); + + return 0; +} + +int wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); + + adapter->delete_in_progress = true; + errno = osif_vdev_sync_trans_start_wait(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + osif_vdev_sync_unregister(wdev->netdev); + osif_vdev_sync_wait_for_ops(vdev_sync); + + errno = __wlan_hdd_del_virtual_intf(wiphy, wdev); + + osif_vdev_sync_trans_stop(vdev_sync); + osif_vdev_sync_destroy(vdev_sync); + + return errno; +} + +/** + * hdd_is_qos_action_frame() - check if frame is QOS action frame + * @pb_frames: frame pointer + * @frame_len: frame length + * + * Return: true if it is QOS action frame else false. + */ +static inline bool +hdd_is_qos_action_frame(uint8_t *pb_frames, uint32_t frame_len) +{ + if (frame_len <= WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1) { + hdd_debug("Not a QOS frame len: %d", frame_len); + return false; + } + + return ((pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET] == + WLAN_HDD_QOS_ACTION_FRAME) && + (pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1] == + WLAN_HDD_QOS_MAP_CONFIGURE)); +} + +#if defined(WLAN_FEATURE_SAE) && defined(CFG80211_EXTERNAL_AUTH_AP_SUPPORT) +/** + * wlan_hdd_set_rxmgmt_external_auth_flag() - Set the EXTERNAL_AUTH flag + * @nl80211_flag: flags to be sent to nl80211 from enum nl80211_rxmgmt_flags + * + * Set the flag NL80211_RXMGMT_FLAG_EXTERNAL_AUTH if supported. + */ +static void +wlan_hdd_set_rxmgmt_external_auth_flag(enum nl80211_rxmgmt_flags *nl80211_flag) +{ + *nl80211_flag |= NL80211_RXMGMT_FLAG_EXTERNAL_AUTH; +} +#else +static void +wlan_hdd_set_rxmgmt_external_auth_flag(enum nl80211_rxmgmt_flags *nl80211_flag) +{ +} +#endif + +/** + * wlan_hdd_cfg80211_convert_rxmgmt_flags() - Convert RXMGMT value + * @nl80211_flag: Flags to be sent to nl80211 from enum nl80211_rxmgmt_flags + * @flag: flags set by driver(SME/PE) from enum rxmgmt_flags + * + * Convert driver internal RXMGMT flag value to nl80211 defined RXMGMT flag + * Return: void + */ +static void +wlan_hdd_cfg80211_convert_rxmgmt_flags(enum rxmgmt_flags flag, + enum nl80211_rxmgmt_flags *nl80211_flag) +{ + + if (flag & RXMGMT_FLAG_EXTERNAL_AUTH) { + wlan_hdd_set_rxmgmt_external_auth_flag(nl80211_flag); + } + +} + +static void +__hdd_indicate_mgmt_frame_to_user(struct hdd_adapter *adapter, + uint32_t frm_len, uint8_t *pb_frames, + uint8_t frame_type, uint32_t rx_freq, + int8_t rx_rssi, enum rxmgmt_flags rx_flags) +{ + uint8_t type = 0; + uint8_t sub_type = 0; + struct hdd_context *hdd_ctx; + uint8_t *dest_addr; + enum nl80211_rxmgmt_flags nl80211_flag = 0; + + hdd_debug("Frame Type = %d Frame Length = %d freq = %d", + frame_type, frm_len, rx_freq); + + if (!adapter) { + hdd_err("adapter is NULL"); + return; + } + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (!frm_len) { + hdd_err("Frame Length is Invalid ZERO"); + return; + } + + if (!pb_frames) { + hdd_err("pbFrames is NULL"); + return; + } + + type = WLAN_HDD_GET_TYPE_FRM_FC(pb_frames[0]); + sub_type = WLAN_HDD_GET_SUBTYPE_FRM_FC(pb_frames[0]); + + /* Get adapter from Destination mac address of the frame */ + if ((type == SIR_MAC_MGMT_FRAME) && + (sub_type != SIR_MAC_MGMT_PROBE_REQ) && + !qdf_is_macaddr_broadcast( + (struct qdf_mac_addr *)&pb_frames[WLAN_HDD_80211_FRM_DA_OFFSET])) { + dest_addr = &pb_frames[WLAN_HDD_80211_FRM_DA_OFFSET]; + adapter = hdd_get_adapter_by_macaddr(hdd_ctx, dest_addr); + if (!adapter) + adapter = hdd_get_adapter_by_rand_macaddr(hdd_ctx, + dest_addr); + if (!adapter) { + /* + * Under assumtion that we don't receive any action + * frame with BCST as destination, + * we are dropping action frame + */ + hdd_err("adapter for action frame is NULL Macaddr = " + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(dest_addr)); + hdd_debug("Frame Type = %d Frame Length = %d subType = %d", + frame_type, frm_len, sub_type); + /* + * We will receive broadcast management frames + * in OCB mode + */ + adapter = hdd_get_adapter(hdd_ctx, QDF_OCB_MODE); + if (!adapter || !qdf_is_macaddr_broadcast( + (struct qdf_mac_addr *)dest_addr)) { + /* + * Under assumtion that we don't + * receive any action frame with BCST + * as destination, we are dropping + * action frame + */ + return; + } + } + } + + if (!adapter->dev) { + hdd_err("adapter->dev is NULL"); + return; + } + + if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { + hdd_err("adapter has invalid magic"); + return; + } + + /* Channel indicated may be wrong. TODO */ + /* Indicate an action frame. */ + + if (hdd_is_qos_action_frame(pb_frames, frm_len)) + sme_update_dsc_pto_up_mapping(hdd_ctx->mac_handle, + adapter->dscp_to_up_map, + adapter->vdev_id); + + /* Indicate Frame Over Normal Interface */ + hdd_debug("Indicate Frame over NL80211 sessionid : %d, idx :%d", + adapter->vdev_id, adapter->dev->ifindex); + + wlan_hdd_cfg80211_convert_rxmgmt_flags(rx_flags, &nl80211_flag); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) + cfg80211_rx_mgmt(adapter->dev->ieee80211_ptr, + rx_freq, rx_rssi * 100, pb_frames, + frm_len, NL80211_RXMGMT_FLAG_ANSWERED | nl80211_flag); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + cfg80211_rx_mgmt(adapter->dev->ieee80211_ptr, + rx_freq, rx_rssi * 100, pb_frames, + frm_len, NL80211_RXMGMT_FLAG_ANSWERED, + GFP_ATOMIC); +#else + cfg80211_rx_mgmt(adapter->dev->ieee80211_ptr, rx_freq, + rx_rssi * 100, + pb_frames, frm_len, GFP_ATOMIC); +#endif /* LINUX_VERSION_CODE */ +} + +void hdd_indicate_mgmt_frame_to_user(struct hdd_adapter *adapter, + uint32_t frm_len, uint8_t *pb_frames, + uint8_t frame_type, uint32_t rx_freq, + int8_t rx_rssi, enum rxmgmt_flags rx_flags) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); + if (errno) + return; + + __hdd_indicate_mgmt_frame_to_user(adapter, frm_len, pb_frames, + frame_type, rx_freq, + rx_rssi, rx_flags); + osif_vdev_sync_op_stop(vdev_sync); +} + +int wlan_hdd_set_power_save(struct hdd_adapter *adapter, + struct p2p_ps_config *ps_config) +{ + struct wlan_objmgr_psoc *psoc; + struct hdd_context *hdd_ctx; + QDF_STATUS status; + + if (!adapter || !ps_config) { + hdd_err("null param, adapter:%pK, ps_config:%pK", + adapter, ps_config); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + psoc = hdd_ctx->psoc; + if (!psoc) { + hdd_err("psoc is null"); + return -EINVAL; + } + + hdd_debug("opp ps:%d, ct window:%d, duration:%d, interval:%d, count:%d, single noa duration:%d, ps selection:%d, vdev id:%d", + ps_config->opp_ps, ps_config->ct_window, + ps_config->duration, ps_config->interval, + ps_config->count, ps_config->single_noa_duration, + ps_config->ps_selection, ps_config->vdev_id); + + status = ucfg_p2p_set_ps(psoc, ps_config); + hdd_debug("p2p set power save, status:%d", status); + + return qdf_status_to_os_return(status); +} + +/** + * wlan_hdd_update_mcc_p2p_quota() - Function to Update P2P + * quota to FW + * @adapter: Pointer to HDD adapter + * @is_set: 0-reset, 1-set + * + * This function passes down the value of MAS to UMAC + * + * Return: none + * + */ +static void wlan_hdd_update_mcc_p2p_quota(struct hdd_adapter *adapter, + bool is_set) +{ + + hdd_info("Set/reset P2P quota: %d", is_set); + if (is_set) { + if (adapter->device_mode == QDF_STA_MODE) + wlan_hdd_set_mcc_p2p_quota(adapter, + 100 - HDD_DEFAULT_MCC_P2P_QUOTA + ); + else if (adapter->device_mode == QDF_P2P_GO_MODE) + wlan_hdd_go_set_mcc_p2p_quota(adapter, + HDD_DEFAULT_MCC_P2P_QUOTA); + else + wlan_hdd_set_mcc_p2p_quota(adapter, + HDD_DEFAULT_MCC_P2P_QUOTA); + } else { + if (adapter->device_mode == QDF_P2P_GO_MODE) + wlan_hdd_go_set_mcc_p2p_quota(adapter, + HDD_RESET_MCC_P2P_QUOTA); + else + wlan_hdd_set_mcc_p2p_quota(adapter, + HDD_RESET_MCC_P2P_QUOTA); + } +} + +int32_t wlan_hdd_set_mas(struct hdd_adapter *adapter, uint8_t mas_value) +{ + struct hdd_context *hdd_ctx; + uint8_t enable_mcc_adaptive_sch = 0; + + if (!adapter) { + hdd_err("Adapter is NULL"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return -EINVAL; + } + + if (mas_value) { + hdd_info("Miracast is ON. Disable MAS and configure P2P quota"); + ucfg_policy_mgr_get_mcc_adaptive_sch(hdd_ctx->psoc, + &enable_mcc_adaptive_sch); + if (enable_mcc_adaptive_sch) { + ucfg_policy_mgr_set_dynamic_mcc_adaptive_sch( + hdd_ctx->psoc, false); + + if (QDF_STATUS_SUCCESS != sme_set_mas(false)) { + hdd_err("Failed to disable MAS"); + return -EAGAIN; + } + } + + /* Config p2p quota */ + wlan_hdd_update_mcc_p2p_quota(adapter, true); + } else { + hdd_info("Miracast is OFF. Enable MAS and reset P2P quota"); + wlan_hdd_update_mcc_p2p_quota(adapter, false); + + ucfg_policy_mgr_get_mcc_adaptive_sch(hdd_ctx->psoc, + &enable_mcc_adaptive_sch); + if (enable_mcc_adaptive_sch) { + ucfg_policy_mgr_set_dynamic_mcc_adaptive_sch( + hdd_ctx->psoc, true); + + if (QDF_STATUS_SUCCESS != sme_set_mas(true)) { + hdd_err("Failed to enable MAS"); + return -EAGAIN; + } + } + } + + return 0; +} + +/** + * set_first_connection_operating_channel() - Function to set + * first connection oerating channel + * @adapter: adapter data + * @set_value: Quota value for the interface + * @dev_mode: Device mode + * This function is used to set the first adapter operating + * channel + * + * Return: operating channel updated in set value + * + */ +static uint32_t set_first_connection_operating_channel( + struct hdd_context *hdd_ctx, uint32_t set_value, + enum QDF_OPMODE dev_mode) +{ + uint8_t operating_channel; + uint32_t oper_chan_freq; + + oper_chan_freq = hdd_get_operating_chan_freq(hdd_ctx, dev_mode); + if (!oper_chan_freq) { + hdd_err(" First adpter operating channel is invalid"); + return -EINVAL; + } + operating_channel = wlan_reg_freq_to_chan(hdd_ctx->pdev, + oper_chan_freq); + + hdd_info("First connection channel No.:%d and quota:%dms", + operating_channel, set_value); + /* Move the time quota for first channel to bits 15-8 */ + set_value = set_value << 8; + + /* + * Store the channel number of 1st channel at bits 7-0 + * of the bit vector + */ + return set_value | operating_channel; +} + +/** + * set_second_connection_operating_channel() - Function to set + * second connection oerating channel + * @adapter: adapter data + * @set_value: Quota value for the interface + * @vdev_id: vdev id + * + * This function is used to set the first adapter operating + * channel + * + * Return: operating channel updated in set value + * + */ +static uint32_t set_second_connection_operating_channel( + struct hdd_context *hdd_ctx, uint32_t set_value, + uint8_t vdev_id) +{ + uint8_t operating_channel; + + operating_channel = wlan_freq_to_chan( + policy_mgr_get_mcc_operating_channel( + hdd_ctx->psoc, vdev_id)); + + if (operating_channel == 0) { + hdd_err("Second adapter operating channel is invalid"); + return -EINVAL; + } + + hdd_info("Second connection channel No.:%d and quota:%dms", + operating_channel, set_value); + /* + * Now move the time quota and channel number of the + * 1st adapter to bits 23-16 and bits 15-8 of the bit + * vector, respectively. + */ + set_value = set_value << 8; + + /* + * Set the channel number for 2nd MCC vdev at bits + * 7-0 of set_value + */ + return set_value | operating_channel; +} + +/** + * wlan_hdd_set_mcc_p2p_quota() - Function to set quota for P2P + * @psoc: PSOC object information + * @set_value: Qouta value for the interface + * @operating_channel First adapter operating channel + * @vdev_id vdev id + * + * This function is used to set the quota for P2P cases + * + * Return: Configuration message posting status, SUCCESS or Fail + * + */ +int wlan_hdd_set_mcc_p2p_quota(struct hdd_adapter *adapter, + uint32_t set_value) +{ + int32_t ret = 0; + uint32_t concurrent_state; + struct hdd_context *hdd_ctx; + uint32_t sta_cli_bit_mask = QDF_STA_MASK | QDF_P2P_CLIENT_MASK; + uint32_t sta_go_bit_mask = QDF_STA_MASK | QDF_P2P_GO_MASK; + + if (!adapter) { + hdd_err("Invalid adapter"); + return -EFAULT; + } + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return -EINVAL; + } + + concurrent_state = policy_mgr_get_concurrency_mode( + hdd_ctx->psoc); + /* + * Check if concurrency mode is active. + * Need to modify this code to support MCC modes other than STA/P2P + */ + if (((concurrent_state & sta_cli_bit_mask) == sta_cli_bit_mask) || + ((concurrent_state & sta_go_bit_mask) == sta_go_bit_mask)) { + hdd_info("STA & P2P are both enabled"); + + /* + * The channel numbers for both adapters and the time + * quota for the 1st adapter, i.e., one specified in cmd + * are formatted as a bit vector then passed on to WMA + * +***********************************************************+ + * |bit 31-24 | bit 23-16 | bits 15-8 | bits 7-0 | + * | Unused | Quota for | chan. # for | chan. # for | + * | | 1st chan. | 1st chan. | 2nd chan. | + * +***********************************************************+ + */ + + set_value = set_first_connection_operating_channel( + hdd_ctx, set_value, adapter->device_mode); + + set_value = set_second_connection_operating_channel( + hdd_ctx, set_value, adapter->vdev_id); + + + ret = wlan_hdd_send_p2p_quota(adapter, set_value); + } else { + hdd_info("MCC is not active. Exit w/o setting latency"); + } + + return ret; +} + +int wlan_hdd_go_set_mcc_p2p_quota(struct hdd_adapter *hostapd_adapter, + uint32_t set_value) +{ + return wlan_hdd_set_mcc_p2p_quota(hostapd_adapter, set_value); +} + +void wlan_hdd_set_mcc_latency(struct hdd_adapter *adapter, int set_value) +{ + uint32_t concurrent_state; + struct hdd_context *hdd_ctx; + uint32_t sta_cli_bit_mask = QDF_STA_MASK | QDF_P2P_CLIENT_MASK; + uint32_t sta_go_bit_mask = QDF_STA_MASK | QDF_P2P_GO_MASK; + + if (!adapter) { + hdd_err("Invalid adapter"); + return; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return; + } + + concurrent_state = policy_mgr_get_concurrency_mode( + hdd_ctx->psoc); + /** + * Check if concurrency mode is active. + * Need to modify this code to support MCC modes other than STA/P2P + */ + if (((concurrent_state & sta_cli_bit_mask) == sta_cli_bit_mask) || + ((concurrent_state & sta_go_bit_mask) == sta_go_bit_mask)) { + hdd_info("STA & P2P are both enabled"); + /* + * The channel number and latency are formatted in + * a bit vector then passed on to WMA layer. + * +**********************************************+ + * |bits 31-16 | bits 15-8 | bits 7-0 | + * | Unused | latency - Chan. 1 | channel no. | + * +**********************************************+ + */ + set_value = set_first_connection_operating_channel( + hdd_ctx, set_value, adapter->device_mode); + + wlan_hdd_send_mcc_latency(adapter, set_value); + } else { + hdd_info("MCC is not active. Exit w/o setting latency"); + } +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p_listen_offload.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p_listen_offload.c new file mode 100644 index 0000000000000000000000000000000000000000..e562b932d7b2b00b0202952380ac93f2e80fe133 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p_listen_offload.c @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_vendor_p2p_listen_offload.c + * + * WLAN p2p listen offload functions + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* P2P listen offload device types parameters length in bytes */ +#define P2P_LO_MAX_REQ_DEV_TYPE_COUNT (10) +#define P2P_LO_WPS_DEV_TYPE_LEN (8) +#define P2P_LO_DEV_TYPE_MAX_LEN \ + (P2P_LO_MAX_REQ_DEV_TYPE_COUNT * P2P_LO_WPS_DEV_TYPE_LEN) + +static const struct nla_policy +p2p_listen_offload_policy[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL] = { .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD] = { .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL] = { + .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT] = { .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES] = { + .type = NLA_BINARY, + .len = P2P_LO_DEV_TYPE_MAX_LEN }, + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE] = { + .type = NLA_BINARY, + .len = MAX_GENIE_LEN }, + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG] = { + .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL] = { .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON] = { + .type = NLA_U8 }, +}; + +/** + * wlan_hdd_listen_offload_start() - hdd set listen offload start + * @adapter: adapter context + * @params: listen offload parameters + * + * This function sets listen offload start parameters. + * + * Return: 0 on success, others on failure + */ +static int wlan_hdd_listen_offload_start(struct hdd_adapter *adapter, + struct sir_p2p_lo_start *params) +{ + struct wlan_objmgr_psoc *psoc; + struct p2p_lo_start lo_start; + struct hdd_context *hdd_ctx; + QDF_STATUS status; + + if (!adapter || !params) { + hdd_err("null param, adapter:%pK, params:%pK", + adapter, params); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + psoc = hdd_ctx->psoc; + if (!psoc) { + hdd_err("psoc is null"); + return -EINVAL; + } + + lo_start.vdev_id = params->vdev_id; + lo_start.ctl_flags = params->ctl_flags; + lo_start.freq = params->freq; + lo_start.period = params->period; + lo_start.interval = params->interval; + lo_start.count = params->count; + lo_start.device_types = params->device_types; + lo_start.dev_types_len = params->dev_types_len; + lo_start.probe_resp_tmplt = params->probe_resp_tmplt; + lo_start.probe_resp_len = params->probe_resp_len; + + status = ucfg_p2p_lo_start(psoc, &lo_start); + hdd_debug("p2p listen offload start, status:%d", status); + + return qdf_status_to_os_return(status); +} + +/** + * __wlan_hdd_cfg80211_p2p_lo_start () - start P2P Listen Offload + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function is to process the p2p listen offload start vendor + * command. It parses the input parameters and invoke WMA API to + * send the command to firmware. + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_p2p_lo_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1]; + struct sir_p2p_lo_start params; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + if ((adapter->device_mode != QDF_P2P_DEVICE_MODE) && + (adapter->device_mode != QDF_P2P_CLIENT_MODE) && + (adapter->device_mode != QDF_P2P_GO_MODE)) { + hdd_err("Invalid device mode %d", adapter->device_mode); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX, + data, data_len, + p2p_listen_offload_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(params)); + + if (!tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG]) + params.ctl_flags = 1; /* set to default value */ + else + params.ctl_flags = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG]); + + if (!tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE]) { + hdd_err("Attribute parsing failed"); + return -EINVAL; + } + + params.vdev_id = adapter->vdev_id; + params.freq = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL]); + if ((params.freq != 2412) && (params.freq != 2437) && + (params.freq != 2462)) { + hdd_err("Invalid listening channel: %d", params.freq); + return -EINVAL; + } + + params.period = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD]); + if (!((params.period > 0) && (params.period < UINT_MAX))) { + hdd_err("Invalid period: %d", params.period); + return -EINVAL; + } + + params.interval = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL]); + if (!((params.interval > 0) && (params.interval < UINT_MAX))) { + hdd_err("Invalid interval: %d", params.interval); + return -EINVAL; + } + + params.count = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT]); + if (!((params.count >= 0) && (params.count < UINT_MAX))) { + hdd_err("Invalid count: %d", params.count); + return -EINVAL; + } + + params.device_types = nla_data(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES]); + if (!params.device_types) { + hdd_err("Invalid device types"); + return -EINVAL; + } + + params.dev_types_len = nla_len(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES]); + /* device type length has to be multiple of P2P_LO_WPS_DEV_TYPE_LEN */ + if (0 != (params.dev_types_len % P2P_LO_WPS_DEV_TYPE_LEN)) { + hdd_err("Invalid device type length: %d", params.dev_types_len); + return -EINVAL; + } + + params.probe_resp_tmplt = nla_data(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE]); + if (!params.probe_resp_tmplt) { + hdd_err("Invalid probe response template"); + return -EINVAL; + } + + /* + * IEs minimum length should be 2 bytes: 1 byte for element id + * and 1 byte for element id length. + */ + params.probe_resp_len = nla_len(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE]); + if (params.probe_resp_len < MIN_GENIE_LEN) { + hdd_err("Invalid probe resp template length: %d", + params.probe_resp_len); + return -EINVAL; + } + + hdd_debug("P2P LO params: freq=%d, period=%d, interval=%d, count=%d", + params.freq, params.period, params.interval, params.count); + + return wlan_hdd_listen_offload_start(adapter, ¶ms); +} + +int wlan_hdd_cfg80211_p2p_lo_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_p2p_lo_start(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_listen_offload_stop() - hdd set listen offload stop + * @adapter: adapter context + * + * This function sets listen offload stop parameters. + * + * Return: 0 on success, others on failure + */ +static int wlan_hdd_listen_offload_stop(struct hdd_adapter *adapter) +{ + struct wlan_objmgr_psoc *psoc; + struct hdd_context *hdd_ctx; + uint32_t vdev_id; + QDF_STATUS status; + + if (!adapter) { + hdd_err("adapter is null, adapter:%pK", adapter); + return -EINVAL; + } + + vdev_id = (uint32_t)adapter->vdev_id; + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + psoc = hdd_ctx->psoc; + if (!psoc) { + hdd_err("psoc is null"); + return -EINVAL; + } + + status = ucfg_p2p_lo_stop(psoc, vdev_id); + hdd_debug("p2p listen offload stop, status:%d", status); + + return qdf_status_to_os_return(status); +} + +/** + * __wlan_hdd_cfg80211_p2p_lo_stop () - stop P2P Listen Offload + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function is to process the p2p listen offload stop vendor + * command. It invokes WMA API to send command to firmware. + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_adapter *adapter; + struct net_device *dev = wdev->netdev; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + if ((adapter->device_mode != QDF_P2P_DEVICE_MODE) && + (adapter->device_mode != QDF_P2P_CLIENT_MODE) && + (adapter->device_mode != QDF_P2P_GO_MODE)) { + hdd_err("Invalid device mode"); + return -EINVAL; + } + + return wlan_hdd_listen_offload_stop(adapter); +} + +int wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_p2p_lo_stop(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p_listen_offload.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p_listen_offload.h new file mode 100644 index 0000000000000000000000000000000000000000..f039aca82a8eff53398e9d24641248dc862ab842 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p_listen_offload.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_P2P_LISTEN_OFFLOAD_H +#define __WLAN_HDD_P2P_LISTEN_OFFLOAD_H + +/** + * DOC: wlan_hdd_p2p_listen_offload_h + * + * WLAN Host Device Driver p2p listen offload API specification + */ + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * __wlan_hdd_cfg80211_p2p_lo_start() - start P2P Listen Offload + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function is to process the p2p listen offload start vendor + * command. It parses the input parameters and invoke WMA API to + * send the command to firmware. + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_p2p_lo_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * wlan_hdd_cfg80211_p2p_lo_stop() - stop P2P Listen Offload + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function inovkes internal __wlan_hdd_cfg80211_p2p_lo_stop() + * to process p2p listen offload stop vendor command. + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_P2P_LISTEN_OFFLOAD_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = \ + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_p2p_lo_start \ +}, \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = \ + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_p2p_lo_stop \ +}, +#else /* FEATURE_P2P_LISTEN_OFFLOAD */ +#define FEATURE_P2P_LISTEN_OFFLOAD_VENDOR_COMMANDS +#endif /* FEATURE_P2P_LISTEN_OFFLOAD */ + +#endif /* __WLAN_HDD_P2P_LISTEN_OFFLOAD_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_packet_filter.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_packet_filter.c new file mode 100644 index 0000000000000000000000000000000000000000..a9c6f2641529c6d71aebb313e99a5183d12757fe --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_packet_filter.c @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_packet_filter.c + * + * WLAN Host Device Driver implementation + * + */ + +/* Include Files */ +#include "wlan_hdd_packet_filter_api.h" +#include "wlan_hdd_packet_filter_rules.h" + +int hdd_enable_default_pkt_filters(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + uint8_t filters = 0, i = 0, filter_id = 1; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is Null!!!"); + return -EINVAL; + } + if (hdd_ctx->user_configured_pkt_filter_rules) { + hdd_info("user has defined pkt filter run hence skipping default packet filter rule"); + return 0; + } + + filters = ucfg_pmo_get_pkt_filter_bitmap(hdd_ctx->psoc); + + while (filters != 0) { + if (filters & 0x1) { + hdd_err("setting filter[%d], of id = %d", + i+1, filter_id); + packet_filter_default_rules[i].filter_id = filter_id; + wlan_hdd_set_filter(hdd_ctx, + &packet_filter_default_rules[i], + adapter->vdev_id); + filter_id++; + } + filters = filters >> 1; + i++; + } + + return 0; +} + +int hdd_disable_default_pkt_filters(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + uint8_t filters = 0, i = 0, filter_id = 1; + + struct pkt_filter_cfg packet_filter_default_rules = {0}; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is Null!!!"); + return -EINVAL; + } + + if (hdd_ctx->user_configured_pkt_filter_rules) { + hdd_info("user has defined pkt filter run hence skipping default packet filter rule"); + return 0; + } + + filters = ucfg_pmo_get_pkt_filter_bitmap(hdd_ctx->psoc); + + while (filters != 0) { + if (filters & 0x1) { + hdd_err("Clearing filter[%d], of id = %d", + i+1, filter_id); + packet_filter_default_rules.filter_action = + HDD_RCV_FILTER_CLEAR; + packet_filter_default_rules.filter_id = filter_id; + wlan_hdd_set_filter(hdd_ctx, + &packet_filter_default_rules, + adapter->vdev_id); + filter_id++; + } + filters = filters >> 1; + i++; + } + + return 0; +} + +int wlan_hdd_set_filter(struct hdd_context *hdd_ctx, + struct pkt_filter_cfg *request, + uint8_t vdev_id) +{ + struct pmo_rcv_pkt_fltr_cfg *pmo_set_pkt_fltr_req = NULL; + struct pmo_rcv_pkt_fltr_clear_param *pmo_clr_pkt_fltr_param = NULL; + int i = 0; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!ucfg_pmo_is_pkt_filter_enabled(hdd_ctx->psoc)) { + hdd_warn("Packet filtering disabled in ini"); + return 0; + } + + /* Debug display of request components. */ + hdd_debug("Packet Filter Request : FA %d params %d", + request->filter_action, request->num_params); + + switch (request->filter_action) { + case HDD_RCV_FILTER_SET: + hdd_debug("Set Packet Filter Request for Id: %d", + request->filter_id); + + pmo_set_pkt_fltr_req = + qdf_mem_malloc(sizeof(*pmo_set_pkt_fltr_req)); + if (!pmo_set_pkt_fltr_req) { + hdd_err("unable to allocate pmo_set_pkt_fltr_req"); + return QDF_STATUS_E_NOMEM; + } + + pmo_set_pkt_fltr_req->filter_id = request->filter_id; + if (request->num_params >= HDD_MAX_CMP_PER_PACKET_FILTER) { + hdd_err("Number of Params exceed Max limit %d", + request->num_params); + status = QDF_STATUS_E_INVAL; + goto out; + } + pmo_set_pkt_fltr_req->num_params = request->num_params; + pmo_set_pkt_fltr_req->coalesce_time = 0; + pmo_set_pkt_fltr_req->filter_type = PMO_RCV_FILTER_TYPE_FILTER_PKT; + for (i = 0; i < request->num_params; i++) { + pmo_set_pkt_fltr_req->params_data[i].protocol_layer = + request->params_data[i].protocol_layer; + pmo_set_pkt_fltr_req->params_data[i].compare_flag = + request->params_data[i].compare_flag; + pmo_set_pkt_fltr_req->params_data[i].data_offset = + request->params_data[i].data_offset; + pmo_set_pkt_fltr_req->params_data[i].data_length = + request->params_data[i].data_length; + pmo_set_pkt_fltr_req->params_data[i].reserved = 0; + + if (request->params_data[i].data_offset > + SIR_MAX_FILTER_TEST_DATA_OFFSET) { + hdd_err("Invalid data offset %u for param %d (max = %d)", + request->params_data[i].data_offset, + i, + SIR_MAX_FILTER_TEST_DATA_OFFSET); + status = QDF_STATUS_E_INVAL; + goto out; + } + + if (request->params_data[i].data_length > + SIR_MAX_FILTER_TEST_DATA_LEN) { + hdd_err("Error invalid data length %d", + request->params_data[i].data_length); + status = QDF_STATUS_E_INVAL; + goto out; + } + + hdd_debug("Proto %d Comp Flag %d Filter Type %d", + request->params_data[i].protocol_layer, + request->params_data[i].compare_flag, + pmo_set_pkt_fltr_req->filter_type); + + hdd_debug("Data Offset %d Data Len %d", + request->params_data[i].data_offset, + request->params_data[i].data_length); + + if (sizeof( + pmo_set_pkt_fltr_req->params_data[i].compare_data) + < (request->params_data[i].data_length)) { + hdd_err("Error invalid data length %d", + request->params_data[i].data_length); + status = QDF_STATUS_E_INVAL; + goto out; + } + + memcpy( + &pmo_set_pkt_fltr_req->params_data[i].compare_data, + request->params_data[i].compare_data, + request->params_data[i].data_length); + memcpy(&pmo_set_pkt_fltr_req->params_data[i].data_mask, + request->params_data[i].data_mask, + request->params_data[i].data_length); + + hdd_debug("CData %d CData %d CData %d CData %d CData %d CData %d", + request->params_data[i].compare_data[0], + request->params_data[i].compare_data[1], + request->params_data[i].compare_data[2], + request->params_data[i].compare_data[3], + request->params_data[i].compare_data[4], + request->params_data[i].compare_data[5]); + + hdd_debug("MData %d MData %d MData %d MData %d MData %d MData %d", + request->params_data[i].data_mask[0], + request->params_data[i].data_mask[1], + request->params_data[i].data_mask[2], + request->params_data[i].data_mask[3], + request->params_data[i].data_mask[4], + request->params_data[i].data_mask[5]); + } + + + status= ucfg_pmo_set_pkt_filter(hdd_ctx->psoc, + pmo_set_pkt_fltr_req, + vdev_id); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failure to execute Set Filter"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + break; + + case HDD_RCV_FILTER_CLEAR: + hdd_debug("Clear Packet Filter Request for Id: %d", + request->filter_id); + + pmo_clr_pkt_fltr_param = qdf_mem_malloc( + sizeof(*pmo_clr_pkt_fltr_param)); + if (!pmo_clr_pkt_fltr_param) { + hdd_err("unable to allocate pmo_clr_pkt_fltr_param"); + return QDF_STATUS_E_NOMEM; + } + + pmo_clr_pkt_fltr_param->filter_id = request->filter_id; + status = ucfg_pmo_clear_pkt_filter(hdd_ctx->psoc, + pmo_clr_pkt_fltr_param, + vdev_id); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failure to execute Clear Filter"); + status = QDF_STATUS_E_INVAL; + goto out; + } + break; + + default: + hdd_err("Packet Filter Request: Invalid %d", + request->filter_action); + return -EINVAL; + } + +out: + if (pmo_set_pkt_fltr_req) + qdf_mem_free(pmo_set_pkt_fltr_req); + if (pmo_clr_pkt_fltr_param) + qdf_mem_free(pmo_clr_pkt_fltr_param); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_periodic_sta_stats.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_periodic_sta_stats.c new file mode 100644 index 0000000000000000000000000000000000000000..887813ba1ce96f0824cc52fd75eb369951486c14 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_periodic_sta_stats.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_periodic_sta_stats.c + * + * WLAN Host Device Driver periodic STA statistics related implementation + * + */ + +#include "wlan_hdd_main.h" +#include "cfg_ucfg_api.h" +#include "wlan_hdd_periodic_sta_stats.h" + +void hdd_periodic_sta_stats_config(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->periodic_stats_timer_interval = + cfg_get(psoc, CFG_PERIODIC_STATS_TIMER_INTERVAL); + config->periodic_stats_timer_duration = + cfg_get(psoc, CFG_PERIODIC_STATS_TIMER_DURATION); +} + +void hdd_periodic_sta_stats_init(struct hdd_adapter *adapter) +{ + adapter->is_sta_periodic_stats_enabled = false; +} + +void hdd_periodic_sta_stats_display(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + struct hdd_stats sta_stats; + struct hdd_config *hdd_cfg; + char *dev_name; + bool should_log; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_PERIODIC_STA_STATS_DISPLAY; + + if (!hdd_ctx) + return; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + should_log = false; + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + hdd_cfg = hdd_ctx->config; + qdf_mutex_acquire(&adapter->sta_periodic_stats_lock); + + if (!adapter->is_sta_periodic_stats_enabled) { + qdf_mutex_release(&adapter->sta_periodic_stats_lock); + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + adapter->periodic_stats_timer_counter++; + if ((adapter->periodic_stats_timer_counter * + GET_BW_COMPUTE_INTV(hdd_cfg)) >= + hdd_cfg->periodic_stats_timer_interval) { + should_log = true; + + adapter->periodic_stats_timer_count--; + if (adapter->periodic_stats_timer_count == 0) + adapter->is_sta_periodic_stats_enabled = false; + adapter->periodic_stats_timer_counter = 0; + } + qdf_mutex_release(&adapter->sta_periodic_stats_lock); + + if (should_log) { + dev_name = WLAN_HDD_GET_DEV_NAME(adapter); + sta_stats = adapter->hdd_stats; + hdd_nofl_info("%s: Tx ARP requests: %d", dev_name, + sta_stats.hdd_arp_stats.tx_arp_req_count); + hdd_nofl_info("%s: Rx ARP responses: %d", dev_name, + sta_stats.hdd_arp_stats.rx_arp_rsp_count); + hdd_nofl_info("%s: Tx DNS requests: %d", dev_name, + sta_stats.hdd_dns_stats.tx_dns_req_count); + hdd_nofl_info("%s: Rx DNS responses: %d", dev_name, + sta_stats.hdd_dns_stats.rx_dns_rsp_count); + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } +} + +void hdd_periodic_sta_stats_start(struct hdd_adapter *adapter) +{ + struct hdd_config *hdd_cfg = adapter->hdd_ctx->config; + + if ((adapter->device_mode == QDF_STA_MODE) && + (hdd_cfg->periodic_stats_timer_interval > 0)) { + qdf_mutex_acquire(&adapter->sta_periodic_stats_lock); + + adapter->periodic_stats_timer_count = + hdd_cfg->periodic_stats_timer_duration / + hdd_cfg->periodic_stats_timer_interval; + adapter->periodic_stats_timer_counter = 0; + if (adapter->periodic_stats_timer_count > 0) + adapter->is_sta_periodic_stats_enabled = true; + + qdf_mutex_release(&adapter->sta_periodic_stats_lock); + } +} + +void hdd_periodic_sta_stats_stop(struct hdd_adapter *adapter) +{ + struct hdd_config *hdd_cfg = adapter->hdd_ctx->config; + + if ((adapter->device_mode == QDF_STA_MODE) && + (hdd_cfg->periodic_stats_timer_interval > 0)) { + qdf_mutex_acquire(&adapter->sta_periodic_stats_lock); + + /* Stop the periodic ARP and DNS stats timer */ + adapter->periodic_stats_timer_count = 0; + adapter->is_sta_periodic_stats_enabled = false; + + qdf_mutex_release(&adapter->sta_periodic_stats_lock); + } +} + +void hdd_periodic_sta_stats_mutex_create(struct hdd_adapter *adapter) +{ + qdf_mutex_create(&adapter->sta_periodic_stats_lock); +} + +void hdd_periodic_sta_stats_mutex_destroy(struct hdd_adapter *adapter) +{ + qdf_mutex_destroy(&adapter->sta_periodic_stats_lock); +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_power.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_power.c new file mode 100644 index 0000000000000000000000000000000000000000..f27a1eee511c52fb4d266cb37e10b19ebfe6f03b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_power.c @@ -0,0 +1,2896 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_power.c + * + * WLAN power management functions + * + */ + +/* Include files */ + +#include +#include +#include +#include "osif_sync.h" +#include +#if defined(WLAN_OPEN_SOURCE) && defined(CONFIG_HAS_WAKELOCK) +#include +#endif +#include "qdf_types.h" +#include "sme_api.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "hif.h" +#include "hif_unit_test_suspend.h" +#include "sme_power_save_api.h" +#include "wlan_policy_mgr_api.h" +#include "cdp_txrx_flow_ctrl_v2.h" +#include "pld_common.h" +#include "wlan_hdd_driver_ops.h" +#include +#include "scheduler_api.h" +#include "cds_utils.h" +#include "wlan_hdd_packet_filter_api.h" +#include "wlan_cfg80211_scan.h" +#include +#include "wlan_ipa_ucfg_api.h" +#include +#include "wlan_p2p_ucfg_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_osif_request_manager.h" +#include +#include "wlan_hdd_thermal.h" + +/* Preprocessor definitions and constants */ +#ifdef QCA_WIFI_NAPIER_EMULATION +#define HDD_SSR_BRING_UP_TIME 3000000 +#else +#define HDD_SSR_BRING_UP_TIME 30000 +#endif + +/* Type declarations */ + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +void hdd_wlan_suspend_resume_event(uint8_t state) +{ + WLAN_HOST_DIAG_EVENT_DEF(suspend_state, struct host_event_suspend); + qdf_mem_zero(&suspend_state, sizeof(suspend_state)); + + suspend_state.state = state; + WLAN_HOST_DIAG_EVENT_REPORT(&suspend_state, EVENT_WLAN_SUSPEND_RESUME); +} + +/** + * hdd_wlan_offload_event()- send offloads event + * @type: offload type + * @state: enabled or disabled + * + * This Function send offloads enable/disable diag event + * + * Return: void. + */ + +void hdd_wlan_offload_event(uint8_t type, uint8_t state) +{ + WLAN_HOST_DIAG_EVENT_DEF(host_offload, struct host_event_offload_req); + qdf_mem_zero(&host_offload, sizeof(host_offload)); + + host_offload.offload_type = type; + host_offload.state = state; + + WLAN_HOST_DIAG_EVENT_REPORT(&host_offload, EVENT_WLAN_OFFLOAD_REQ); +} +#endif + +#ifdef WLAN_FEATURE_PKT_CAPTURE + +/* timeout in msec to wait for RX_THREAD to suspend */ +#define HDD_MONTHREAD_SUSPEND_TIMEOUT 200 + +void wlan_hdd_mon_thread_resume(struct hdd_context *hdd_ctx) +{ + if (hdd_ctx->is_ol_mon_thread_suspended) { + cds_resume_mon_thread(); + hdd_ctx->is_ol_mon_thread_suspended = false; + } +} + +int wlan_hdd_mon_thread_suspend(struct hdd_context *hdd_ctx) +{ + p_cds_sched_context cds_sched_context = get_cds_sched_ctxt(); + int rc; + + if (!cds_sched_context) + return -EINVAL; + + set_bit(RX_SUSPEND_EVENT, + &cds_sched_context->sched_mon_ctx.ol_mon_event_flag); + wake_up_interruptible(&cds_sched_context-> + sched_mon_ctx.ol_mon_wait_queue); + rc = wait_for_completion_timeout( + &cds_sched_context->sched_mon_ctx.ol_suspend_mon_event, + msecs_to_jiffies(HDD_MONTHREAD_SUSPEND_TIMEOUT)); + if (!rc) { + clear_bit(RX_SUSPEND_EVENT, + &cds_sched_context->sched_mon_ctx.ol_mon_event_flag); + hdd_err("Failed to stop tl_shim mon thread"); + return -EINVAL; + } + hdd_ctx->is_ol_mon_thread_suspended = true; + + return 0; +} +#endif + +#ifdef QCA_CONFIG_SMP + +/* timeout in msec to wait for RX_THREAD to suspend */ +#define HDD_RXTHREAD_SUSPEND_TIMEOUT 200 + +void wlan_hdd_rx_thread_resume(struct hdd_context *hdd_ctx) +{ + if (hdd_ctx->is_ol_rx_thread_suspended) { + cds_resume_rx_thread(); + hdd_ctx->is_ol_rx_thread_suspended = false; + } +} + +int wlan_hdd_rx_thread_suspend(struct hdd_context *hdd_ctx) +{ + p_cds_sched_context cds_sched_context = get_cds_sched_ctxt(); + int rc; + + if (!cds_sched_context) + return 0; + + /* Suspend tlshim rx thread */ + set_bit(RX_SUSPEND_EVENT, &cds_sched_context->ol_rx_event_flag); + wake_up_interruptible(&cds_sched_context->ol_rx_wait_queue); + rc = wait_for_completion_timeout(&cds_sched_context-> + ol_suspend_rx_event, + msecs_to_jiffies + (HDD_RXTHREAD_SUSPEND_TIMEOUT) + ); + if (!rc) { + clear_bit(RX_SUSPEND_EVENT, + &cds_sched_context->ol_rx_event_flag); + hdd_err("Failed to stop tl_shim rx thread"); + return -EINVAL; + } + hdd_ctx->is_ol_rx_thread_suspended = true; + + return 0; +} +#endif /* QCA_CONFIG_SMP */ + +/** + * hdd_enable_gtk_offload() - enable GTK offload + * @adapter: pointer to the adapter + * + * Central function to enable GTK offload. + * + * Return: nothing + */ +static void hdd_enable_gtk_offload(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + status = ucfg_pmo_enable_gtk_offload_in_fwr(adapter->vdev); + if (status != QDF_STATUS_SUCCESS) + hdd_info("Failed to enable gtk offload"); +} + +/** + * hdd_disable_gtk_offload() - disable GTK offload + * @adapter: pointer to the adapter + * + * Central function to disable GTK offload. + * + * Return: nothing + */ +static void hdd_disable_gtk_offload(struct hdd_adapter *adapter) +{ + struct pmo_gtk_rsp_req gtk_rsp_request; + QDF_STATUS status; + + /* ensure to get gtk rsp first before disable it*/ + gtk_rsp_request.callback = wlan_hdd_cfg80211_update_replay_counter_cb; + + /* Passing as void* as PMO does not know legacy HDD adapter type */ + gtk_rsp_request.callback_context = (void *)adapter; + + status = ucfg_pmo_get_gtk_rsp(adapter->vdev, >k_rsp_request); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to send get gtk rsp status:%d", status); + return; + } + + hdd_debug("send get_gtk_rsp successful"); + status = ucfg_pmo_disable_gtk_offload_in_fwr(adapter->vdev); + if (status != QDF_STATUS_SUCCESS) + hdd_info("Failed to disable gtk offload"); +} + +#ifdef WLAN_NS_OFFLOAD +/** + * __wlan_hdd_ipv6_changed() - IPv6 notifier callback function + * @net_dev: net_device whose IP address changed + * @event: event from kernel, NETDEV_UP or NETDEV_DOWN + * + * This is a callback function that is registered with the kernel via + * register_inet6addr_notifier() which allows the driver to be + * notified when there is an IPv6 address change. + * + * Return: None + */ +static void __wlan_hdd_ipv6_changed(struct net_device *net_dev, + unsigned long event) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + int errno; + + hdd_enter_dev(net_dev); + + errno = hdd_validate_adapter(adapter); + if (errno || adapter->dev != net_dev) + goto exit; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + goto exit; + + /* Only need to be notified for ipv6_add_addr + * No need for ipv6_del_addr or addrconf_ifdown + */ + if (event == NETDEV_UP && + (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE)) { + hdd_debug("invoking sme_dhcp_done_ind"); + sme_dhcp_done_ind(hdd_ctx->mac_handle, adapter->vdev_id); + schedule_work(&adapter->ipv6_notifier_work); + } + +exit: + hdd_exit(); +} + +int wlan_hdd_ipv6_changed(struct notifier_block *nb, + unsigned long data, void *context) +{ + struct inet6_ifaddr *ifa = context; + struct net_device *net_dev = ifa->idev->dev; + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(net_dev, &vdev_sync)) + return NOTIFY_DONE; + + __wlan_hdd_ipv6_changed(net_dev, data); + + osif_vdev_sync_op_stop(vdev_sync); + + return NOTIFY_DONE; +} + +/** + * hdd_fill_ipv6_uc_addr() - fill IPv6 unicast addresses + * @idev: pointer to net device + * @ipv6addr: destination array to fill IPv6 addresses + * @ipv6addr_type: IPv6 Address type + * @scope_array: scope of ipv6 addr + * @count: number of IPv6 addresses + * + * This is the IPv6 utility function to populate unicast addresses. + * + * Return: 0 on success, error number otherwise. + */ +static int hdd_fill_ipv6_uc_addr(struct inet6_dev *idev, + uint8_t ipv6_uc_addr[][QDF_IPV6_ADDR_SIZE], + uint8_t *ipv6addr_type, + enum pmo_ns_addr_scope *scope_array, + uint32_t *count) +{ + struct inet6_ifaddr *ifa; + struct list_head *p; + uint32_t scope; + + read_lock_bh(&idev->lock); + list_for_each(p, &idev->addr_list) { + if (*count >= PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA) { + read_unlock_bh(&idev->lock); + return -EINVAL; + } + ifa = list_entry(p, struct inet6_ifaddr, if_list); + if (ifa->flags & IFA_F_DADFAILED) + continue; + scope = ipv6_addr_src_scope(&ifa->addr); + switch (scope) { + case IPV6_ADDR_SCOPE_GLOBAL: + case IPV6_ADDR_SCOPE_LINKLOCAL: + qdf_mem_copy(ipv6_uc_addr[*count], &ifa->addr.s6_addr, + sizeof(ifa->addr.s6_addr)); + ipv6addr_type[*count] = PMO_IPV6_ADDR_UC_TYPE; + scope_array[*count] = ucfg_pmo_ns_addr_scope(scope); + hdd_debug("Index %d scope = %s UC-Address: %pI6", + *count, (scope == IPV6_ADDR_SCOPE_LINKLOCAL) ? + "LINK LOCAL" : "GLOBAL", ipv6_uc_addr[*count]); + *count += 1; + break; + default: + hdd_warn("The Scope %d is not supported", scope); + } + } + + read_unlock_bh(&idev->lock); + return 0; +} + +/** + * hdd_fill_ipv6_ac_addr() - fill IPv6 anycast addresses + * @idev: pointer to net device + * @ipv6addr: destination array to fill IPv6 addresses + * @ipv6addr_type: IPv6 Address type + * @scope_array: scope of ipv6 addr + * @count: number of IPv6 addresses + * + * This is the IPv6 utility function to populate anycast addresses. + * + * Return: 0 on success, error number otherwise. + */ +static int hdd_fill_ipv6_ac_addr(struct inet6_dev *idev, + uint8_t ipv6_ac_addr[][QDF_IPV6_ADDR_SIZE], + uint8_t *ipv6addr_type, + enum pmo_ns_addr_scope *scope_array, + uint32_t *count) +{ + struct ifacaddr6 *ifaca; + uint32_t scope; + + read_lock_bh(&idev->lock); + for (ifaca = idev->ac_list; ifaca; ifaca = ifaca->aca_next) { + if (*count >= PMO_MAC_NUM_TARGET_IPV6_NS_OFFLOAD_NA) { + read_unlock_bh(&idev->lock); + return -EINVAL; + } + /* For anycast addr no DAD */ + scope = ipv6_addr_src_scope(&ifaca->aca_addr); + switch (scope) { + case IPV6_ADDR_SCOPE_GLOBAL: + case IPV6_ADDR_SCOPE_LINKLOCAL: + qdf_mem_copy(ipv6_ac_addr[*count], &ifaca->aca_addr, + sizeof(ifaca->aca_addr)); + ipv6addr_type[*count] = PMO_IPV6_ADDR_AC_TYPE; + scope_array[*count] = ucfg_pmo_ns_addr_scope(scope); + hdd_debug("Index %d scope = %s AC-Address: %pI6", + *count, (scope == IPV6_ADDR_SCOPE_LINKLOCAL) ? + "LINK LOCAL" : "GLOBAL", ipv6_ac_addr[*count]); + *count += 1; + break; + default: + hdd_warn("The Scope %d is not supported", scope); + } + } + + read_unlock_bh(&idev->lock); + return 0; +} + +void hdd_enable_ns_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + struct inet6_dev *in6_dev; + struct pmo_ns_req *ns_req; + QDF_STATUS status; + int errno; + + hdd_enter(); + + if (!psoc) { + hdd_err("psoc is NULL"); + goto out; + } + + in6_dev = __in6_dev_get(adapter->dev); + if (!in6_dev) { + hdd_err("IPv6 dev does not exist. Failed to request NSOffload"); + goto out; + } + + ns_req = qdf_mem_malloc(sizeof(*ns_req)); + if (!ns_req) + goto out; + + ns_req->psoc = psoc; + ns_req->vdev_id = adapter->vdev_id; + ns_req->trigger = trigger; + ns_req->count = 0; + + /* check if offload cache and send is required or not */ + status = ucfg_pmo_ns_offload_check(psoc, trigger, adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_debug("NS offload is not required"); + goto free_req; + } + + /* Unicast Addresses */ + errno = hdd_fill_ipv6_uc_addr(in6_dev, ns_req->ipv6_addr, + ns_req->ipv6_addr_type, ns_req->scope, + &ns_req->count); + if (errno) { + hdd_disable_ns_offload(adapter, trigger); + hdd_debug("Max supported addresses: disabling NS offload"); + goto free_req; + } + + /* Anycast Addresses */ + errno = hdd_fill_ipv6_ac_addr(in6_dev, ns_req->ipv6_addr, + ns_req->ipv6_addr_type, ns_req->scope, + &ns_req->count); + if (errno) { + hdd_disable_ns_offload(adapter, trigger); + hdd_debug("Max supported addresses: disabling NS offload"); + goto free_req; + } + + /* cache ns request */ + status = ucfg_pmo_cache_ns_offload_req(ns_req); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to cache ns request; status:%d", status); + goto free_req; + } + + /* enable ns request */ + status = ucfg_pmo_enable_ns_offload_in_fwr(adapter->vdev, trigger); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to enable ns offload; status:%d", status); + goto free_req; + } + + hdd_wlan_offload_event(SIR_IPV6_NS_OFFLOAD, SIR_OFFLOAD_ENABLE); + +free_req: + qdf_mem_free(ns_req); + +out: + hdd_exit(); +} + +void hdd_disable_ns_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_enter(); + + status = ucfg_pmo_ns_offload_check(hdd_ctx->psoc, trigger, + adapter->vdev_id); + if (status != QDF_STATUS_SUCCESS) { + hdd_debug("Flushing of NS offload not required"); + goto out; + } + + status = ucfg_pmo_flush_ns_offload_req(adapter->vdev); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to flush NS Offload"); + goto out; + } + + status = ucfg_pmo_disable_ns_offload_in_fwr(adapter->vdev, trigger); + if (status != QDF_STATUS_SUCCESS) + hdd_err("Failed to disable NS Offload"); + else + hdd_wlan_offload_event(SIR_IPV6_NS_OFFLOAD, + SIR_OFFLOAD_DISABLE); +out: + hdd_exit(); + +} + +/** + * hdd_send_ps_config_to_fw() - Check user pwr save config set/reset PS + * @adapter: pointer to hdd adapter + * + * This function checks the power save configuration saved in MAC context + * and sends power save config to FW. + * + * Return: None + */ +static void hdd_send_ps_config_to_fw(struct hdd_adapter *adapter) +{ + struct mac_context *mac_ctx; + struct hdd_context *hdd_ctx; + + if (hdd_validate_adapter(adapter)) + return; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_ctx = MAC_CONTEXT(hdd_ctx->mac_handle); + + if (mac_ctx->usr_cfg_ps_enable) + sme_ps_enable_disable(hdd_ctx->mac_handle, adapter->vdev_id, + SME_PS_ENABLE); + else + sme_ps_enable_disable(hdd_ctx->mac_handle, adapter->vdev_id, + SME_PS_DISABLE); +} + +/** + * __hdd_ipv6_notifier_work_queue() - IPv6 notification work function + * @adapter: adapter whose IP address changed + * + * This function performs the work initially trigged by a callback + * from the IPv6 netdev notifier. Since this means there has been a + * change in IPv6 state for the interface, the NS offload is + * reconfigured. + * + * Return: None + */ +static void __hdd_ipv6_notifier_work_queue(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + int errno; + + hdd_enter(); + + errno = hdd_validate_adapter(adapter); + if (errno) + goto exit; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + goto exit; + + hdd_enable_ns_offload(adapter, pmo_ipv6_change_notify); + + hdd_send_ps_config_to_fw(adapter); +exit: + hdd_exit(); +} + +void hdd_ipv6_notifier_work_queue(struct work_struct *work) +{ + struct hdd_adapter *adapter = container_of(work, struct hdd_adapter, + ipv6_notifier_work); + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(adapter->dev, &vdev_sync)) + return; + + __hdd_ipv6_notifier_work_queue(adapter); + + osif_vdev_sync_op_stop(vdev_sync); +} +#endif /* WLAN_NS_OFFLOAD */ + +static void hdd_enable_hw_filter(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + hdd_enter(); + + status = ucfg_pmo_enable_hw_filter_in_fwr(adapter->vdev); + if (status != QDF_STATUS_SUCCESS) + hdd_info("Failed to enable hardware filter"); + + hdd_exit(); +} + +static void hdd_disable_hw_filter(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + hdd_enter(); + + status = ucfg_pmo_disable_hw_filter_in_fwr(adapter->vdev); + if (status != QDF_STATUS_SUCCESS) + hdd_info("Failed to disable hardware filter"); + + hdd_exit(); +} + +static void hdd_enable_action_frame_patterns(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + hdd_enter(); + + status = ucfg_pmo_enable_action_frame_patterns(adapter->vdev, + QDF_SYSTEM_SUSPEND); + if (QDF_IS_STATUS_ERROR(status)) + hdd_info("Failed to enable action frame patterns"); + + hdd_exit(); +} + +static void hdd_disable_action_frame_patterns(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + hdd_enter(); + + status = ucfg_pmo_disable_action_frame_patterns(adapter->vdev); + if (QDF_IS_STATUS_ERROR(status)) + hdd_info("Failed to disable action frame patterns"); + + hdd_exit(); +} + +void hdd_enable_host_offloads(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ + hdd_enter(); + + if (!ucfg_pmo_is_vdev_supports_offload(adapter->vdev)) { + hdd_debug("offload is not supported on vdev opmode %d", + adapter->device_mode); + goto out; + } + + if (!ucfg_pmo_is_vdev_connected(adapter->vdev)) { + hdd_debug("offload is not supported on disconnected vdevs"); + goto out; + } + + hdd_debug("enable offloads"); + hdd_enable_gtk_offload(adapter); + hdd_enable_arp_offload(adapter, trigger); + hdd_enable_ns_offload(adapter, trigger); + hdd_enable_mc_addr_filtering(adapter, trigger); + if (adapter->device_mode != QDF_NDI_MODE) + hdd_enable_hw_filter(adapter); + hdd_enable_action_frame_patterns(adapter); +out: + hdd_exit(); + +} + +void hdd_disable_host_offloads(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ + hdd_enter(); + + if (!ucfg_pmo_is_vdev_supports_offload(adapter->vdev)) { + hdd_info("offload is not supported on this vdev opmode: %d", + adapter->device_mode); + goto out; + } + + if (!ucfg_pmo_is_vdev_connected(adapter->vdev)) { + hdd_info("vdev is not connected"); + goto out; + } + + hdd_debug("disable offloads"); + hdd_disable_gtk_offload(adapter); + hdd_disable_arp_offload(adapter, trigger); + hdd_disable_ns_offload(adapter, trigger); + hdd_disable_mc_addr_filtering(adapter, trigger); + if (adapter->device_mode != QDF_NDI_MODE) + hdd_disable_hw_filter(adapter); + hdd_disable_action_frame_patterns(adapter); +out: + hdd_exit(); + +} + +/** + * hdd_lookup_ifaddr() - Lookup interface address data by name + * @adapter: the adapter whose name should be searched for + * + * return in_ifaddr pointer on success, NULL for failure + */ +static struct in_ifaddr *hdd_lookup_ifaddr(struct hdd_adapter *adapter) +{ + struct in_ifaddr *ifa; + struct in_device *in_dev; + + if (!adapter) { + hdd_err("adapter is null"); + return NULL; + } + + in_dev = __in_dev_get_rtnl(adapter->dev); + if (!in_dev) { + hdd_err("Failed to get in_device"); + return NULL; + } + + /* lookup address data by interface name */ + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + if (!strcmp(adapter->dev->name, ifa->ifa_label)) + return ifa; + } + + return NULL; +} + +/** + * hdd_populate_ipv4_addr() - Populates the adapter's IPv4 address + * @adapter: the adapter whose IPv4 address is desired + * @ipv4_addr: the address of the array to copy the IPv4 address into + * + * return: zero for success; non-zero for failure + */ +static int hdd_populate_ipv4_addr(struct hdd_adapter *adapter, + uint8_t *ipv4_addr) +{ + struct in_ifaddr *ifa; + int i; + + if (!adapter) { + hdd_err("adapter is null"); + return -EINVAL; + } + + if (!ipv4_addr) { + hdd_err("ipv4_addr is null"); + return -EINVAL; + } + + ifa = hdd_lookup_ifaddr(adapter); + if (!ifa || !ifa->ifa_local) { + hdd_err("ipv4 address not found"); + return -EINVAL; + } + + /* convert u32 to byte array */ + for (i = 0; i < 4; i++) + ipv4_addr[i] = (ifa->ifa_local >> i * 8) & 0xff; + + return 0; +} + +/** + * hdd_set_grat_arp_keepalive() - Enable grat APR keepalive + * @adapter: the HDD adapter to configure + * + * This configures gratuitous APR keepalive based on the adapter's current + * connection information, specifically IPv4 address and BSSID + * + * return: zero for success, non-zero for failure + */ +static int hdd_set_grat_arp_keepalive(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + int exit_code; + struct hdd_context *hdd_ctx; + struct hdd_station_ctx *sta_ctx; + struct keep_alive_req req = { + .packetType = SIR_KEEP_ALIVE_UNSOLICIT_ARP_RSP, + .dest_macaddr = QDF_MAC_ADDR_BCAST_INIT, + }; + + if (!adapter) { + hdd_err("adapter is null"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("hdd_ctx is null"); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (!sta_ctx) { + hdd_err("sta_ctx is null"); + return -EINVAL; + } + + exit_code = hdd_populate_ipv4_addr(adapter, req.hostIpv4Addr); + if (exit_code) { + hdd_err("Failed to populate ipv4 address"); + return exit_code; + } + + /* according to RFC5227, sender/target ip address should be the same */ + qdf_mem_copy(&req.destIpv4Addr, &req.hostIpv4Addr, + sizeof(req.destIpv4Addr)); + + qdf_copy_macaddr(&req.bssid, &sta_ctx->conn_info.bssid); + ucfg_mlme_get_sta_keep_alive_period(hdd_ctx->psoc, &req.timePeriod); + req.sessionId = adapter->vdev_id; + + hdd_debug("Setting gratuitous ARP keepalive; ipv4_addr:%u.%u.%u.%u", + req.hostIpv4Addr[0], req.hostIpv4Addr[1], + req.hostIpv4Addr[2], req.hostIpv4Addr[3]); + + status = sme_set_keep_alive(hdd_ctx->mac_handle, req.sessionId, &req); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set keepalive"); + return qdf_status_to_os_return(status); + } + + return 0; +} + +/** + * __hdd_ipv4_notifier_work_queue() - IPv4 notification work function + * @adapter: adapter whose IP address changed + * + * This function performs the work initially trigged by a callback + * from the IPv4 netdev notifier. Since this means there has been a + * change in IPv4 state for the interface, the ARP offload is + * reconfigured. Also, Updates the HLP IE info with IP address info + * to fw if LFR3 is enabled + * + * Return: None + */ +static void __hdd_ipv4_notifier_work_queue(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + int errno; + struct csr_roam_profile *roam_profile; + struct in_ifaddr *ifa; + enum station_keepalive_method val; + QDF_STATUS status; + + hdd_enter(); + + errno = hdd_validate_adapter(adapter); + if (errno) + goto exit; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + goto exit; + + hdd_enable_arp_offload(adapter, pmo_ipv4_change_notify); + + status = ucfg_mlme_get_sta_keepalive_method(hdd_ctx->psoc, &val); + if (QDF_IS_STATUS_ERROR(status)) + goto exit; + + if (val == MLME_STA_KEEPALIVE_GRAT_ARP) + hdd_set_grat_arp_keepalive(adapter); + + hdd_debug("FILS Roaming support: %d", + hdd_ctx->is_fils_roaming_supported); + roam_profile = hdd_roam_profile(adapter); + + ifa = hdd_lookup_ifaddr(adapter); + if (ifa && hdd_ctx->is_fils_roaming_supported) + sme_send_hlp_ie_info(hdd_ctx->mac_handle, adapter->vdev_id, + roam_profile, ifa->ifa_local); + hdd_send_ps_config_to_fw(adapter); +exit: + hdd_exit(); +} + +void hdd_ipv4_notifier_work_queue(struct work_struct *work) +{ + struct hdd_adapter *adapter = container_of(work, struct hdd_adapter, + ipv4_notifier_work); + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(adapter->dev, &vdev_sync)) + return; + + __hdd_ipv4_notifier_work_queue(adapter); + + osif_vdev_sync_op_stop(vdev_sync); +} + +/** + * __wlan_hdd_ipv4_changed() - IPv4 notifier callback function + * @net_dev: the net_device whose IP address changed + * + * This is a callback function that is registered with the kernel via + * register_inetaddr_notifier() which allows the driver to be + * notified when there is an IPv4 address change. + * + * Return: None + */ +static void __wlan_hdd_ipv4_changed(struct net_device *net_dev) +{ + struct in_ifaddr *ifa; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + struct hdd_context *hdd_ctx; + int errno; + + hdd_enter_dev(net_dev); + + errno = hdd_validate_adapter(adapter); + if (errno || adapter->dev != net_dev) + goto exit; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + goto exit; + + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + hdd_debug("invoking sme_dhcp_done_ind"); + sme_dhcp_done_ind(hdd_ctx->mac_handle, adapter->vdev_id); + + if (!ucfg_pmo_is_arp_offload_enabled(hdd_ctx->psoc)) { + hdd_debug("Offload not enabled"); + goto exit; + } + + ifa = hdd_lookup_ifaddr(adapter); + if (ifa && ifa->ifa_local) + schedule_work(&adapter->ipv4_notifier_work); + } + +exit: + hdd_exit(); +} + +int wlan_hdd_ipv4_changed(struct notifier_block *nb, + unsigned long data, void *context) +{ + struct in_ifaddr *ifa = context; + struct net_device *net_dev = ifa->ifa_dev->dev; + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(net_dev, &vdev_sync)) + return NOTIFY_DONE; + + __wlan_hdd_ipv4_changed(net_dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return NOTIFY_DONE; +} + +#ifdef FEATURE_RUNTIME_PM +int wlan_hdd_pm_qos_notify(struct notifier_block *nb, unsigned long curr_val, + void *context) +{ + struct hdd_context *hdd_ctx = container_of(nb, struct hdd_context, + pm_qos_notifier); + void *hif_ctx; + + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug_rl("Driver Module closed; skipping pm qos notify"); + return 0; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("Hif context is Null"); + return -EINVAL; + } + + hdd_debug("PM QOS update: runtime_pm_prevented %d Current value: %ld", + hdd_ctx->runtime_pm_prevented, curr_val); + qdf_spin_lock_irqsave(&hdd_ctx->pm_qos_lock); + + if (!hdd_ctx->runtime_pm_prevented && + curr_val != PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE) { + hif_pm_runtime_get_noresume(hif_ctx, RTPM_ID_QOS_NOTIFY); + hdd_ctx->runtime_pm_prevented = true; + } else if (hdd_ctx->runtime_pm_prevented && + curr_val == PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE) { + hif_pm_runtime_put(hif_ctx, RTPM_ID_QOS_NOTIFY); + hdd_ctx->runtime_pm_prevented = false; + } + + qdf_spin_unlock_irqrestore(&hdd_ctx->pm_qos_lock); + + return NOTIFY_DONE; +} +#endif + +/** + * hdd_get_ipv4_local_interface() - get ipv4 local interafce from iface list + * @adapter: Adapter context for which ARP offload is to be configured + * + * Return: + * ifa - on successful operation, + * NULL - on failure of operation + */ +static struct in_ifaddr *hdd_get_ipv4_local_interface( + struct hdd_adapter *adapter) +{ + struct in_ifaddr **ifap = NULL; + struct in_ifaddr *ifa = NULL; + struct in_device *in_dev; + + in_dev = __in_dev_get_rtnl(adapter->dev); + if (in_dev) { + for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; + ifap = &ifa->ifa_next) { + if (!strcmp(adapter->dev->name, ifa->ifa_label)) { + /* if match break */ + return ifa; + } + } + } + ifa = NULL; + + return ifa; +} + +void hdd_enable_arp_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + QDF_STATUS status; + struct pmo_arp_req *arp_req; + struct in_ifaddr *ifa; + + arp_req = qdf_mem_malloc(sizeof(*arp_req)); + if (!arp_req) + return; + + arp_req->psoc = psoc; + arp_req->vdev_id = adapter->vdev_id; + arp_req->trigger = trigger; + + status = ucfg_pmo_check_arp_offload(psoc, trigger, adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_debug("ARP offload not required"); + goto free_req; + } + + ifa = hdd_get_ipv4_local_interface(adapter); + if (!ifa || !ifa->ifa_local) { + hdd_info("IP Address is not assigned"); + status = QDF_STATUS_NOT_INITIALIZED; + goto free_req; + } + + arp_req->ipv4_addr = (uint32_t)ifa->ifa_local; + + status = ucfg_pmo_cache_arp_offload_req(arp_req); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed to cache arp offload req; status:%d", status); + goto free_req; + } + + status = ucfg_pmo_enable_arp_offload_in_fwr(adapter->vdev, trigger); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed arp offload config in fw; status:%d", status); + goto free_req; + } + + hdd_wlan_offload_event(PMO_IPV4_ARP_REPLY_OFFLOAD, PMO_OFFLOAD_ENABLE); + +free_req: + qdf_mem_free(arp_req); +} + +void hdd_disable_arp_offload(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + status = ucfg_pmo_check_arp_offload(hdd_ctx->psoc, trigger, + adapter->vdev_id); + if (status != QDF_STATUS_SUCCESS) { + hdd_debug("Flushing of ARP offload not required"); + return; + } + + status = ucfg_pmo_flush_arp_offload_req(adapter->vdev); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Failed to flush arp Offload"); + return; + } + + status = ucfg_pmo_disable_arp_offload_in_fwr(adapter->vdev, + trigger); + if (status == QDF_STATUS_SUCCESS) + hdd_wlan_offload_event(PMO_IPV4_ARP_REPLY_OFFLOAD, + PMO_OFFLOAD_DISABLE); + else + hdd_info("fail to disable arp offload"); +} + +void hdd_enable_mc_addr_filtering(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + if (!hdd_adapter_is_connected_sta(adapter)) + return; + + status = ucfg_pmo_enable_mc_addr_filtering_in_fwr(hdd_ctx->psoc, + adapter->vdev_id, + trigger); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("failed to enable mc list; status:%d", status); + +} + +void hdd_disable_mc_addr_filtering(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + if (!hdd_adapter_is_connected_sta(adapter)) + return; + + status = ucfg_pmo_disable_mc_addr_filtering_in_fwr(hdd_ctx->psoc, + adapter->vdev_id, + trigger); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("failed to disable mc list; status:%d", status); +} + +int hdd_cache_mc_addr_list(struct pmo_mc_addr_list_params *mc_list_config) +{ + QDF_STATUS status; + + status = ucfg_pmo_cache_mc_addr_list(mc_list_config); + + return qdf_status_to_os_return(status); +} + +void hdd_disable_and_flush_mc_addr_list(struct hdd_adapter *adapter, + enum pmo_offload_trigger trigger) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + + if (!hdd_adapter_is_connected_sta(adapter)) + goto flush_mc_list; + + /* disable mc list first because the mc list is cached in PMO */ + status = ucfg_pmo_disable_mc_addr_filtering_in_fwr(hdd_ctx->psoc, + adapter->vdev_id, + trigger); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("failed to disable mc list; status:%d", status); + +flush_mc_list: + status = ucfg_pmo_flush_mc_addr_list(hdd_ctx->psoc, + adapter->vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + hdd_debug("failed to flush mc list; status:%d", status); + +} + +/** + * hdd_update_conn_state_mask(): record info needed by wma_suspend_req + * @adapter: adapter to get info from + * @conn_state_mask: mask of connection info + * + * currently only need to send connection info. + */ +static void hdd_update_conn_state_mask(struct hdd_adapter *adapter, + uint32_t *conn_state_mask) +{ + + eConnectionState conn_state; + struct hdd_station_ctx *sta_ctx; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + conn_state = sta_ctx->conn_info.conn_state; + + if (conn_state == eConnectionState_Associated || + conn_state == eConnectionState_IbssConnected) + *conn_state_mask |= (1 << adapter->vdev_id); +} + +/** + * hdd_suspend_wlan() - Driver suspend function + * @callback: Callback function to invoke when driver is ready to suspend + * @callbackContext: Context to pass back to @callback function + * + * Return: 0 on success else error code. + */ +static int +hdd_suspend_wlan(void) +{ + struct hdd_context *hdd_ctx; + QDF_STATUS status; + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + uint32_t conn_state_mask = 0; + + hdd_info("WLAN being suspended by OS"); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is Null"); + return -EINVAL; + } + + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + hdd_info("Recovery in Progress. State: 0x%x Ignore suspend!!!", + cds_get_driver_state()); + return -EINVAL; + } + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_SUSPEND_WLAN) { + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) { + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_SUSPEND_WLAN); + continue; + } + + /* stop all TX queues before suspend */ + hdd_debug("Disabling queues for dev mode %s", + qdf_opmode_str(adapter->device_mode)); + wlan_hdd_netif_queue_control(adapter, + WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + + if (adapter->device_mode == QDF_STA_MODE) + status = hdd_enable_default_pkt_filters(adapter); + + /* Configure supported OffLoads */ + hdd_enable_host_offloads(adapter, pmo_apps_suspend); + hdd_update_conn_state_mask(adapter, &conn_state_mask); + hdd_adapter_dev_put_debug(adapter, NET_DEV_HOLD_SUSPEND_WLAN); + } + + status = ucfg_pmo_psoc_user_space_suspend_req(hdd_ctx->psoc, + QDF_SYSTEM_SUSPEND); + if (status != QDF_STATUS_SUCCESS) + return -EAGAIN; + + hdd_ctx->hdd_wlan_suspended = true; + + hdd_configure_sar_sleep_index(hdd_ctx); + + hdd_wlan_suspend_resume_event(HDD_WLAN_EARLY_SUSPEND); + + return 0; +} + +/** + * hdd_resume_wlan() - Driver resume function + * + * Return: 0 on success else error code. + */ +static int hdd_resume_wlan(void) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter, *next_adapter = NULL; + QDF_STATUS status; + + hdd_info("WLAN being resumed by OS"); + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is Null"); + return -EINVAL; + } + + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + hdd_info("Recovery in Progress. State: 0x%x Ignore resume!!!", + cds_get_driver_state()); + return -EINVAL; + } + + hdd_ctx->hdd_wlan_suspended = false; + hdd_wlan_suspend_resume_event(HDD_WLAN_EARLY_RESUME); + + /*loop through all adapters. Concurrency */ + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_RESUME_WLAN) { + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) { + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_RESUME_WLAN); + continue; + } + + /* Disable supported OffLoads */ + hdd_disable_host_offloads(adapter, pmo_apps_resume); + + /* wake the tx queues */ + hdd_debug("Enabling queues for dev mode %s", + qdf_opmode_str(adapter->device_mode)); + wlan_hdd_netif_queue_control(adapter, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + + if (adapter->device_mode == QDF_STA_MODE) + status = hdd_disable_default_pkt_filters(adapter); + + hdd_adapter_dev_put_debug(adapter, NET_DEV_HOLD_RESUME_WLAN); + } + + ucfg_ipa_resume(hdd_ctx->pdev); + status = ucfg_pmo_psoc_user_space_resume_req(hdd_ctx->psoc, + QDF_SYSTEM_SUSPEND); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + hdd_configure_sar_resume_index(hdd_ctx); + + return 0; +} + +void hdd_svc_fw_shutdown_ind(struct device *dev) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + hdd_ctx ? wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_FW_SHUTDOWN_IND, + NULL, 0) : 0; +} + +/** + * hdd_ssr_restart_sap() - restart sap on SSR + * @hdd_ctx: hdd context + * + * Return: nothing + */ +static void hdd_ssr_restart_sap(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + + hdd_enter(); + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_SSR_RESTART_SAP) { + if (adapter->device_mode == QDF_SAP_MODE) { + if (test_bit(SOFTAP_INIT_DONE, &adapter->event_flags)) { + hdd_debug("Restart prev SAP session"); + wlan_hdd_start_sap(adapter, true); + } + } + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_SSR_RESTART_SAP); + } + + hdd_exit(); +} + +QDF_STATUS hdd_wlan_shutdown(void) +{ + struct hdd_context *hdd_ctx; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + hdd_info("WLAN driver shutting down!"); + + /* Get the HDD context. */ + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is Null"); + return QDF_STATUS_E_FAILURE; + } + + hdd_set_connection_in_progress(false); + policy_mgr_clear_concurrent_session_count(hdd_ctx->psoc); + + hdd_debug("Invoking packetdump deregistration API"); + wlan_deregister_txrx_packetdump(OL_TXRX_PDEV_ID); + + /* resume wlan threads before adapter reset which does vdev destroy */ + if (hdd_ctx->is_scheduler_suspended) { + scheduler_resume(); + hdd_ctx->is_scheduler_suspended = false; + hdd_ctx->is_wiphy_suspended = false; + ucfg_pmo_resume_all_components(hdd_ctx->psoc, + QDF_SYSTEM_SUSPEND); + } + + wlan_hdd_rx_thread_resume(hdd_ctx); + + dp_txrx_resume(cds_get_context(QDF_MODULE_ID_SOC)); + + if (cds_is_pktcapture_enabled()) + wlan_hdd_mon_thread_resume(hdd_ctx); + + /* + * After SSR, FW clear its txrx stats. In host, + * as adapter is intact so those counts are still + * available. Now if agains Set stats command comes, + * then host will increment its counts start from its + * last saved value, i.e., count before SSR, and FW will + * increment its count from 0. This will finally sends a + * mismatch of packet counts b/w host and FW to framework + * that will create ambiquity. Therfore, Resetting the host + * counts here so that after SSR both FW and host start + * increment their counts from 0. + */ + hdd_reset_all_adapters_connectivity_stats(hdd_ctx); + + hdd_reset_all_adapters(hdd_ctx); + + ucfg_ipa_uc_ssr_cleanup(hdd_ctx->pdev); + + /* Flush cached rx frame queue */ + if (soc) + cdp_flush_cache_rx_queue(soc); + + /* De-register the HDD callbacks */ + hdd_deregister_cb(hdd_ctx); + + hdd_wlan_stop_modules(hdd_ctx, false); + + hdd_lpass_notify_stop(hdd_ctx); + + hdd_info("WLAN driver shutdown complete"); + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +/** + * hdd_wlan_ssr_reinit_event()- send ssr reinit state + * + * This Function send send ssr reinit state diag event + * + * Return: void. + */ +static void hdd_wlan_ssr_reinit_event(void) +{ + WLAN_HOST_DIAG_EVENT_DEF(ssr_reinit, struct host_event_wlan_ssr_reinit); + qdf_mem_zero(&ssr_reinit, sizeof(ssr_reinit)); + ssr_reinit.status = SSR_SUB_SYSTEM_REINIT; + WLAN_HOST_DIAG_EVENT_REPORT(&ssr_reinit, + EVENT_WLAN_SSR_REINIT_SUBSYSTEM); +} +#else +static inline void hdd_wlan_ssr_reinit_event(void) +{ + +} +#endif + +/** + * hdd_send_default_scan_ies - send default scan ies to fw + * + * This function is used to send default scan ies to fw + * in case of wlan re-init + * + * Return: void + */ +static void hdd_send_default_scan_ies(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter, *next_adapter = NULL; + + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + NET_DEV_HOLD_SEND_DEFAULT_SCAN_IES) { + if (hdd_is_interface_up(adapter) && + (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_DEVICE_MODE) && + adapter->scan_info.default_scan_ies) { + sme_set_default_scan_ie(hdd_ctx->mac_handle, + adapter->vdev_id, + adapter->scan_info.default_scan_ies, + adapter->scan_info.default_scan_ies_len); + } + hdd_adapter_dev_put_debug(adapter, + NET_DEV_HOLD_SEND_DEFAULT_SCAN_IES); + } +} + +/** + * hdd_is_interface_down_during_ssr - Check if the interface went down during + * SSR + * @hdd_ctx: HDD context + * + * Check if any of the interface went down while the device is recovering. + * If the interface went down close the session. + */ +static void hdd_is_interface_down_during_ssr(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter = NULL, *pnext = NULL; + QDF_STATUS status; + + hdd_enter(); + + status = hdd_get_front_adapter(hdd_ctx, &adapter); + while (adapter && status == QDF_STATUS_SUCCESS) { + if (test_bit(DOWN_DURING_SSR, &adapter->event_flags)) { + clear_bit(DOWN_DURING_SSR, &adapter->event_flags); + hdd_stop_adapter(hdd_ctx, adapter); + hdd_deinit_adapter(hdd_ctx, adapter, true); + clear_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); + } + status = hdd_get_next_adapter(hdd_ctx, adapter, &pnext); + adapter = pnext; + } + + hdd_exit(); +} + +/** + * hdd_restore_sar_config - Restore the saved SAR config after SSR + * @hdd_ctx: HDD context + * + * Restore the SAR config that was lost during SSR. + * + * Return: None + */ +static void hdd_restore_sar_config(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + if (!hdd_ctx->sar_cmd_params) + return; + + status = sme_set_sar_power_limits(hdd_ctx->mac_handle, + hdd_ctx->sar_cmd_params); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Unable to configured SAR after SSR"); +} + +QDF_STATUS hdd_wlan_re_init(void) +{ + struct hdd_context *hdd_ctx = NULL; + struct hdd_adapter *adapter; + int ret; + bool bug_on_reinit_failure = CFG_BUG_ON_REINIT_FAILURE_DEFAULT; + bool value; + + hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_REINIT); + + /* Get the HDD context */ + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is Null"); + goto err_ctx_null; + } + bug_on_reinit_failure = hdd_ctx->config->bug_on_reinit_failure; + + adapter = hdd_get_first_valid_adapter(hdd_ctx); + if (!adapter) + hdd_err("Failed to get adapter"); + + hdd_dp_trace_init(hdd_ctx->config); + + ret = hdd_wlan_start_modules(hdd_ctx, true); + if (ret) { + hdd_err("Failed to start wlan after error"); + goto err_re_init; + } + + hdd_update_hw_sw_info(hdd_ctx); + + wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, + WLAN_SVC_FW_CRASHED_IND, NULL, 0); + + /* Restart all adapters */ + hdd_start_all_adapters(hdd_ctx); + + hdd_init_scan_reject_params(hdd_ctx); + + hdd_set_roaming_in_progress(false); + complete(&adapter->roaming_comp_var); + hdd_ctx->bt_coex_mode_set = false; + + /* Allow the phone to go to sleep */ + hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_REINIT); + /* set chip power save failure detected callback */ + sme_set_chip_pwr_save_fail_cb(hdd_ctx->mac_handle, + hdd_chip_pwr_save_fail_detected_cb); + + hdd_restore_thermal_mitigation_config(hdd_ctx); + hdd_restore_sar_config(hdd_ctx); + + hdd_send_default_scan_ies(hdd_ctx); + hdd_info("WLAN host driver reinitiation completed!"); + + ucfg_mlme_get_sap_internal_restart(hdd_ctx->psoc, &value); + if (value) + hdd_ssr_restart_sap(hdd_ctx); + hdd_is_interface_down_during_ssr(hdd_ctx); + hdd_wlan_ssr_reinit_event(); + + if (hdd_ctx->hdd_wlan_suspended) + hdd_ctx->hdd_wlan_suspended = false; + + return QDF_STATUS_SUCCESS; + +err_re_init: + qdf_dp_trace_deinit(); + +err_ctx_null: + /* Allow the phone to go to sleep */ + hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_REINIT); + if (bug_on_reinit_failure) + QDF_BUG(0); + return -EPERM; +} + +int wlan_hdd_set_powersave(struct hdd_adapter *adapter, + bool allow_power_save, uint32_t timeout) +{ + mac_handle_t mac_handle; + struct hdd_context *hdd_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool is_bmps_enabled; + + if (!adapter) { + hdd_err("Adapter NULL"); + return -ENODEV; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("hdd context is NULL"); + return -EINVAL; + } + + hdd_debug("Allow power save: %d", allow_power_save); + mac_handle = hdd_ctx->mac_handle; + + /* + * This is a workaround for defective AP's that send a disassoc + * immediately after WPS connection completes. Defer powersave by a + * small amount if the affected AP is detected. + */ + if (allow_power_save && + adapter->device_mode == QDF_STA_MODE && + !adapter->session.station.ap_supports_immediate_power_save) { + timeout = AUTO_PS_DEFER_TIMEOUT_MS; + hdd_debug("Defer power-save due to AP spec non-conformance"); + } + + if (allow_power_save) { + if (QDF_STA_MODE == adapter->device_mode || + QDF_P2P_CLIENT_MODE == adapter->device_mode) { + hdd_debug("Disabling Auto Power save timer"); + status = sme_ps_disable_auto_ps_timer(mac_handle, + adapter->vdev_id); + if (status != QDF_STATUS_SUCCESS) + goto end; + } + + ucfg_mlme_is_bmps_enabled(hdd_ctx->psoc, &is_bmps_enabled); + if (is_bmps_enabled) { + hdd_debug("Wlan driver Entering Power save"); + + /* + * Enter Power Save command received from GUI + * this means DHCP is completed + */ + if (timeout) { + status = sme_ps_enable_auto_ps_timer(mac_handle, + adapter->vdev_id, + timeout); + if (status != QDF_STATUS_SUCCESS) + goto end; + } else { + status = sme_ps_enable_disable(mac_handle, + adapter->vdev_id, + SME_PS_ENABLE); + if (status != QDF_STATUS_SUCCESS) + goto end; + } + } else { + hdd_debug("Power Save is not enabled in the cfg"); + } + } else { + hdd_debug("Wlan driver Entering Full Power"); + + /* + * Enter Full power command received from GUI + * this means we are disconnected + */ + status = sme_ps_disable_auto_ps_timer(mac_handle, + adapter->vdev_id); + + if (status != QDF_STATUS_SUCCESS) + goto end; + + ucfg_mlme_is_bmps_enabled(hdd_ctx->psoc, &is_bmps_enabled); + if (is_bmps_enabled) + status = sme_ps_enable_disable(mac_handle, + adapter->vdev_id, + SME_PS_DISABLE); + } + +end: + return qdf_status_to_os_return(status); +} + +static void wlan_hdd_print_suspend_fail_stats(struct hdd_context *hdd_ctx) +{ + struct suspend_resume_stats *stats = &hdd_ctx->suspend_resume_stats; + + hdd_err("ipa:%d, radar:%d, roam:%d, scan:%d, initial_wakeup:%d", + stats->suspend_fail[SUSPEND_FAIL_IPA], + stats->suspend_fail[SUSPEND_FAIL_RADAR], + stats->suspend_fail[SUSPEND_FAIL_ROAM], + stats->suspend_fail[SUSPEND_FAIL_SCAN], + stats->suspend_fail[SUSPEND_FAIL_INITIAL_WAKEUP]); +} + +void wlan_hdd_inc_suspend_stats(struct hdd_context *hdd_ctx, + enum suspend_fail_reason reason) +{ + wlan_hdd_print_suspend_fail_stats(hdd_ctx); + hdd_ctx->suspend_resume_stats.suspend_fail[reason]++; + wlan_hdd_print_suspend_fail_stats(hdd_ctx); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) +static inline void +hdd_sched_scan_results(struct wiphy *wiphy, uint64_t reqid) +{ + cfg80211_sched_scan_results(wiphy); +} +#else +static inline void +hdd_sched_scan_results(struct wiphy *wiphy, uint64_t reqid) +{ + cfg80211_sched_scan_results(wiphy, reqid); +} +#endif + +/** + * __wlan_hdd_cfg80211_resume_wlan() - cfg80211 resume callback + * @wiphy: Pointer to wiphy + * + * This API is called when cfg80211 driver resumes driver updates + * latest sched_scan scan result(if any) to cfg80211 database + * + * Return: integer status + */ +static int __wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + QDF_STATUS status = QDF_STATUS_SUCCESS; + int exit_code; + + hdd_enter(); + + if (cds_is_driver_recovering()) { + hdd_debug("Driver is recovering; Skipping resume"); + exit_code = 0; + goto exit_with_code; + } + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam() || + QDF_GLOBAL_MONITOR_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in mode %d", + hdd_get_conparam()); + exit_code = -EINVAL; + goto exit_with_code; + } + + if (hdd_ctx->config->is_wow_disabled) { + hdd_err("wow is disabled"); + return -EINVAL; + } + + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver is not enabled; Skipping resume"); + exit_code = 0; + goto exit_with_code; + } + + status = hdd_resume_wlan(); + if (status != QDF_STATUS_SUCCESS) { + exit_code = 0; + goto exit_with_code; + } + /* Resume control path scheduler */ + if (hdd_ctx->is_scheduler_suspended) { + scheduler_resume(); + hdd_ctx->is_scheduler_suspended = false; + } + /* Resume all components registered to pmo */ + status = ucfg_pmo_resume_all_components(hdd_ctx->psoc, + QDF_SYSTEM_SUSPEND); + if (status != QDF_STATUS_SUCCESS) { + exit_code = 0; + goto exit_with_code; + } + /* Resume tlshim Rx thread */ + if (hdd_ctx->enable_rxthread) + wlan_hdd_rx_thread_resume(hdd_ctx); + + if (hdd_ctx->enable_dp_rx_threads) + dp_txrx_resume(cds_get_context(QDF_MODULE_ID_SOC)); + + if (cds_is_pktcapture_enabled()) + wlan_hdd_mon_thread_resume(hdd_ctx); + + ucfg_pmo_notify_system_resume(hdd_ctx->psoc); + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_RESUME_WLAN, + NO_SESSION, hdd_ctx->is_wiphy_suspended); + + hdd_ctx->is_wiphy_suspended = false; + + hdd_ctx->suspend_resume_stats.resumes++; + exit_code = 0; + +exit_with_code: + hdd_exit(); + return exit_code; +} + +static int _wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy) +{ + void *hif_ctx; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int errno; + + if(!hdd_ctx) { + hdd_err_rl("hdd context is null"); + return -ENODEV; + } + + /* If Wifi is off, return success for system resume */ + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver Modules not Enabled "); + return 0; + } + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + errno = __wlan_hdd_cfg80211_resume_wlan(wiphy); + hif_pm_runtime_put(hif_ctx, RTPM_ID_SUSPEND_RESUME); + + return errno; +} + +int wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = _wlan_hdd_cfg80211_resume_wlan(wiphy); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +static void hdd_suspend_cb(void) +{ + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return; + } + + complete(&hdd_ctx->mc_sus_event_var); +} + +/** + * __wlan_hdd_cfg80211_suspend_wlan() - cfg80211 suspend callback + * @wiphy: Pointer to wiphy + * @wow: Pointer to wow + * + * This API is called when cfg80211 driver suspends + * + * Return: integer status + */ +static int __wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy, + struct cfg80211_wowlan *wow) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_adapter *adapter, *next_adapter = NULL; + mac_handle_t mac_handle; + int rc; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_CFG80211_SUSPEND_WLAN; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam() || + QDF_GLOBAL_MONITOR_MODE == hdd_get_conparam()) { + hdd_err_rl("Command not allowed in mode %d", + hdd_get_conparam()); + return -EINVAL; + } + + rc = wlan_hdd_validate_context(hdd_ctx); + if (0 != rc) + return rc; + + /* Wait for the stop module if already in progress */ + hdd_psoc_idle_timer_stop(hdd_ctx); + + if (hdd_ctx->config->is_wow_disabled) { + hdd_info_rl("wow is disabled"); + return -EINVAL; + } + + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver Modules not Enabled "); + return 0; + } + + mac_handle = hdd_ctx->mac_handle; + + /* If RADAR detection is in progress (HDD), prevent suspend. The flag + * "dfs_cac_block_tx" is set to true when RADAR is found and stay true + * until CAC is done for a SoftAP which is in started state. + */ + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + if (QDF_SAP_MODE == adapter->device_mode) { + if (BSS_START == + WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter)->bss_state && + true == + WLAN_HDD_GET_AP_CTX_PTR(adapter)-> + dfs_cac_block_tx) { + hdd_err("RADAR detection in progress, do not allow suspend"); + wlan_hdd_inc_suspend_stats(hdd_ctx, + SUSPEND_FAIL_RADAR); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return -EAGAIN; + } else if (!ucfg_pmo_get_enable_sap_suspend( + hdd_ctx->psoc)) { + /* return -EOPNOTSUPP if SAP does not support + * suspend + */ + hdd_err("SAP does not support suspend!!"); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return -EOPNOTSUPP; + } + } else if (QDF_P2P_GO_MODE == adapter->device_mode) { + if (!ucfg_pmo_get_enable_sap_suspend( + hdd_ctx->psoc)) { + /* return -EOPNOTSUPP if GO does not support + * suspend + */ + hdd_err("GO does not support suspend!!"); + hdd_adapter_dev_put_debug(adapter, dbgid); + if (next_adapter) + hdd_adapter_dev_put_debug(next_adapter, + dbgid); + return -EOPNOTSUPP; + } + } + hdd_adapter_dev_put_debug(adapter, dbgid); + } + /* p2p cleanup task based on scheduler */ + ucfg_p2p_cleanup_tx_by_psoc(hdd_ctx->psoc); + ucfg_p2p_cleanup_roc_by_psoc(hdd_ctx->psoc); + + if (hdd_is_connection_in_progress(NULL, NULL)) { + hdd_err_rl("Connection is in progress, rejecting suspend"); + return -EINVAL; + } + + /* flush any pending powersave timers */ + hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, + dbgid) { + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + sme_ps_timer_flush_sync(mac_handle, adapter->vdev_id); + hdd_adapter_dev_put_debug(adapter, dbgid); + } + + /* + * Suspend all components registered to pmo, abort ongoing scan and + * don't allow new scan any more before scheduler thread suspended. + */ + if (ucfg_pmo_suspend_all_components(hdd_ctx->psoc, + QDF_SYSTEM_SUSPEND)) { + hdd_err("Some components not ready to suspend!"); + return -EAGAIN; + } + + /* + * Suspend IPA early before proceeding to suspend other entities like + * firmware to avoid any race conditions. + */ + if (ucfg_ipa_suspend(hdd_ctx->pdev)) { + hdd_err("IPA not ready to suspend!"); + wlan_hdd_inc_suspend_stats(hdd_ctx, SUSPEND_FAIL_IPA); + return -EAGAIN; + } + + /* Suspend control path scheduler */ + scheduler_register_hdd_suspend_callback(hdd_suspend_cb); + scheduler_set_event_mask(MC_SUSPEND_EVENT); + scheduler_wake_up_controller_thread(); + + /* Wait for suspend confirmation from scheduler */ + rc = wait_for_completion_timeout(&hdd_ctx->mc_sus_event_var, + msecs_to_jiffies(WLAN_WAIT_TIME_MCTHREAD_SUSPEND)); + if (!rc) { + scheduler_clear_event_mask(MC_SUSPEND_EVENT); + hdd_err("Failed to stop mc thread"); + goto resume_tx; + } + hdd_ctx->is_scheduler_suspended = true; + + if (hdd_ctx->enable_rxthread) { + if (wlan_hdd_rx_thread_suspend(hdd_ctx)) + goto resume_ol_rx; + } + + if (hdd_ctx->enable_dp_rx_threads) { + if (dp_txrx_suspend(cds_get_context(QDF_MODULE_ID_SOC))) + goto resume_ol_rx; + } + + if (cds_is_pktcapture_enabled()) { + if (wlan_hdd_mon_thread_suspend(hdd_ctx)) + goto resume_dp_thread; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SUSPEND_WLAN, + NO_SESSION, hdd_ctx->is_wiphy_suspended); + + if (hdd_suspend_wlan() < 0) { + hdd_err("Failed to suspend WLAN"); + goto resume_dp_thread; + } + + hdd_ctx->is_wiphy_suspended = true; + + hdd_exit(); + return 0; + +resume_dp_thread: + if (hdd_ctx->enable_dp_rx_threads) + dp_txrx_resume(cds_get_context(QDF_MODULE_ID_SOC)); + + /* Resume tlshim MON thread */ + if (cds_is_pktcapture_enabled()) + wlan_hdd_mon_thread_resume(hdd_ctx); + +resume_ol_rx: + /* Resume tlshim Rx thread */ + wlan_hdd_rx_thread_resume(hdd_ctx); + scheduler_resume(); + hdd_ctx->is_scheduler_suspended = false; +resume_tx: + hdd_resume_wlan(); + return -ETIME; + +} + +static int _wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy, + struct cfg80211_wowlan *wow) +{ + void *hif_ctx; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int errno; + + if(!hdd_ctx) { + hdd_err_rl("hdd context is null"); + return -ENODEV; + } + + /* If Wifi is off, return success for system suspend */ + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver Modules not Enabled "); + return 0; + } + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + errno = hif_pm_runtime_get_sync(hif_ctx, RTPM_ID_SUSPEND_RESUME); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_suspend_wlan(wiphy, wow); + if (errno) { + hif_pm_runtime_put(hif_ctx, RTPM_ID_SUSPEND_RESUME); + return errno; + } + + return errno; +} + +int wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy, + struct cfg80211_wowlan *wow) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = _wlan_hdd_cfg80211_suspend_wlan(wiphy, wow); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +/** + * hdd_stop_dhcp_ind() - API to stop DHCP sequence + * @adapter: Adapter on which DHCP needs to be stopped + * + * Release the wakelock held for DHCP process and allow + * the runtime pm to continue + * + * Return: None + */ +static void hdd_stop_dhcp_ind(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_debug("DHCP stop indicated through power save"); + sme_dhcp_stop_ind(hdd_ctx->mac_handle, adapter->device_mode, + adapter->mac_addr.bytes, + adapter->vdev_id); + hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DHCP); + qdf_runtime_pm_allow_suspend(&hdd_ctx->runtime_context.connect); +} + +/** + * hdd_start_dhcp_ind() - API to start DHCP sequence + * @adapter: Adapter on which DHCP needs to be stopped + * + * Prevent APPS suspend and the runtime suspend during + * DHCP sequence + * + * Return: None + */ +static void hdd_start_dhcp_ind(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_debug("DHCP start indicated through power save"); + qdf_runtime_pm_prevent_suspend(&hdd_ctx->runtime_context.connect); + hdd_prevent_suspend_timeout(HDD_WAKELOCK_TIMEOUT_CONNECT, + WIFI_POWER_EVENT_WAKELOCK_DHCP); + sme_dhcp_start_ind(hdd_ctx->mac_handle, adapter->device_mode, + adapter->mac_addr.bytes, + adapter->vdev_id); +} + +/** + * __wlan_hdd_cfg80211_set_power_mgmt() - set cfg80211 power management config + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @allow_power_save: is wlan allowed to go into power save mode + * @timeout: Timeout value in ms + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool allow_power_save, + int timeout) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + int status; + + hdd_enter(); + + if (timeout < 0) { + hdd_debug("User space timeout: %d; Enter full power or power save", + timeout); + timeout = 0; + } + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SET_POWER_MGMT, + adapter->vdev_id, timeout); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + + if (0 != status) + return status; + + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_debug("Driver Module not enabled return success"); + return 0; + } + + status = wlan_hdd_set_powersave(adapter, allow_power_save, timeout); + + allow_power_save ? hdd_stop_dhcp_ind(adapter) : + hdd_start_dhcp_ind(adapter); + + hdd_exit(); + return status; +} + +int wlan_hdd_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool allow_power_save, + int timeout) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_power_mgmt(wiphy, dev, allow_power_save, + timeout); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_set_txpower() - set TX power + * @wiphy: Pointer to wiphy + * @wdev: Pointer to network device + * @type: TX power setting type + * @mbm: TX power in mBm + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_set_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct hdd_context *hdd_ctx = (struct hdd_context *) wiphy_priv(wiphy); + mac_handle_t mac_handle; + struct hdd_adapter *adapter; + struct qdf_mac_addr bssid = QDF_MAC_ADDR_BCAST_INIT; + struct qdf_mac_addr selfmac; + QDF_STATUS status; + int errno; + int dbm; + + hdd_enter(); + + if (!wdev) { + hdd_err("wdev is null, set tx power failed"); + return -EIO; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SET_TXPOWER, + NO_SESSION, type); + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + qdf_copy_macaddr(&bssid, &adapter->mac_addr); + } else { + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (eConnectionState_Associated == + sta_ctx->conn_info.conn_state) + qdf_copy_macaddr(&bssid, &sta_ctx->conn_info.bssid); + } + + qdf_copy_macaddr(&selfmac, &adapter->mac_addr); + + mac_handle = hdd_ctx->mac_handle; + + dbm = MBM_TO_DBM(mbm); + + /* + * the original implementation of this function expected power + * values in dBm instead of mBm. If the conversion from mBm to + * dBm is zero, then assume dBm was passed. + */ + if (!dbm) + dbm = mbm; + + status = ucfg_mlme_set_current_tx_power_level(hdd_ctx->psoc, dbm); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("sme_cfg_set_int failed for tx power %hu, %d", + dbm, status); + return -EIO; + } + + hdd_debug("Set tx power level %d dbm", dbm); + + switch (type) { + /* Automatically determine transmit power */ + case NL80211_TX_POWER_AUTOMATIC: + /* Fall through */ + case NL80211_TX_POWER_LIMITED: + /* Limit TX power by the mBm parameter */ + status = sme_set_max_tx_power(mac_handle, bssid, selfmac, dbm); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Setting maximum tx power failed, %d", status); + return -EIO; + } + break; + + case NL80211_TX_POWER_FIXED: /* Fix TX power to the mBm parameter */ + hdd_err("NL80211_TX_POWER_FIXED not supported"); + return -EOPNOTSUPP; + + default: + hdd_err("Invalid power setting type %d", type); + return -EIO; + } + + hdd_exit(); + return 0; +} + +int wlan_hdd_cfg80211_set_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_txpower(wiphy, wdev, type, mbm); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +static void wlan_hdd_get_tx_power(struct hdd_adapter *adapter, int *dbm) + +{ + wlan_cfg80211_mc_cp_stats_get_tx_power(adapter->vdev, dbm); +} + +#ifdef FEATURE_ANI_LEVEL_REQUEST +static void hdd_get_ani_level_cb(struct wmi_host_ani_level_event *ani, + uint8_t num, void *context) +{ + struct osif_request *request; + struct ani_priv *priv; + uint8_t min_recv_freqs = QDF_MIN(num, MAX_NUM_FREQS_FOR_ANI_LEVEL); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + /* propagate response back to requesting thread */ + priv = osif_request_priv(request); + priv->ani = qdf_mem_malloc(min_recv_freqs * + sizeof(struct wmi_host_ani_level_event)); + if (!priv->ani) + goto complete; + + priv->num_freq = min_recv_freqs; + qdf_mem_copy(priv->ani, ani, + min_recv_freqs * sizeof(struct wmi_host_ani_level_event)); + +complete: + osif_request_complete(request); + osif_request_put(request); +} + +/** + * wlan_hdd_get_ani_level_dealloc() - Dealloc mem allocated in priv data + * @priv: the priv data + * + * Return: None + */ +static void wlan_hdd_get_ani_level_dealloc(void *priv) +{ + struct ani_priv *ani = priv; + + if (ani->ani) + qdf_mem_free(ani->ani); +} + +QDF_STATUS wlan_hdd_get_ani_level(struct hdd_adapter *adapter, + struct wmi_host_ani_level_event *ani, + uint32_t *parsed_freqs, + uint8_t num_freqs) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + QDF_STATUS status; + void *cookie; + struct osif_request *request; + struct ani_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = 1000, + .dealloc = wlan_hdd_get_ani_level_dealloc, + }; + + if (!hdd_ctx) { + hdd_err("Invalid HDD context"); + return QDF_STATUS_E_INVAL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return QDF_STATUS_E_NOMEM; + } + cookie = osif_request_cookie(request); + + status = sme_get_ani_level(hdd_ctx->mac_handle, parsed_freqs, + num_freqs, hdd_get_ani_level_cb, cookie); + + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to retrieve ani level"); + goto complete; + } else { + /* request was sent -- wait for the response */ + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving ANI level"); + status = QDF_STATUS_E_TIMEOUT; + goto complete; + } + } + + priv = osif_request_priv(request); + + qdf_mem_copy(ani, priv->ani, sizeof(struct wmi_host_ani_level_event) * + priv->num_freq); + +complete: + /* + * either we never sent a request, we sent a request and + * received a response or we sent a request and timed out. + * regardless we are done with the request. + */ + osif_request_put(request); + + hdd_exit(); + return status; +} +#endif + +/** + * __wlan_hdd_cfg80211_get_txpower() - get TX power + * @wiphy: Pointer to wiphy + * @wdev: Pointer to network device + * @dbm: Pointer to TX power in dbm + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_get_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) +{ + + struct hdd_context *hdd_ctx = (struct hdd_context *) wiphy_priv(wiphy); + struct net_device *ndev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); + int status; + struct hdd_station_ctx *sta_ctx; + static bool is_rate_limited; + + hdd_enter_dev(ndev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + *dbm = 0; + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + /* Validate adapter sessionId */ + status = wlan_hdd_validate_vdev_id(adapter->vdev_id); + if (status) + return status; + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (sta_ctx->hdd_reassoc_scenario) { + hdd_debug("Roaming is in progress, rej this req"); + return -EINVAL; + } + if (sta_ctx->conn_info.conn_state != + eConnectionState_Associated) { + hdd_debug("Not associated"); + return 0; + } + break; + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + if (!test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + hdd_debug("SAP is not started yet"); + return 0; + } + break; + default: + hdd_debug_rl("Current interface is not supported for get tx_power"); + return 0; + } + + HDD_IS_RATE_LIMIT_REQ(is_rate_limited, + hdd_ctx->config->nb_commands_interval); + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED || + is_rate_limited) { + hdd_debug("Modules not enabled/rate limited, use cached stats"); + /* Send cached data to upperlayer*/ + *dbm = adapter->hdd_stats.class_a_stat.max_pwr; + return 0; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_GET_TXPOWER, + adapter->vdev_id, adapter->device_mode); + + wlan_hdd_get_tx_power(adapter, dbm); + hdd_debug("power: %d", *dbm); + + return 0; +} + +int wlan_hdd_cfg80211_get_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_txpower(wiphy, wdev, dbm); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +int hdd_set_power_config(struct hdd_context *hddctx, + struct hdd_adapter *adapter, + uint8_t power) +{ + QDF_STATUS status; + + if (adapter->device_mode != QDF_STA_MODE && + adapter->device_mode != QDF_P2P_CLIENT_MODE) { + hdd_info("Advanced power save only allowed in STA/P2P-Client modes:%d", + adapter->device_mode); + return -EINVAL; + } + + if (power > PMO_PS_ADVANCED_POWER_SAVE_ENABLE || + power < PMO_PS_ADVANCED_POWER_SAVE_DISABLE) { + hdd_err("invalid power value: %d", power); + return -EINVAL; + } + + if (ucfg_pmo_get_max_ps_poll(hddctx->psoc)) { + hdd_info("Disable advanced power save since max ps poll is enabled"); + power = PMO_PS_ADVANCED_POWER_SAVE_DISABLE; + } + + status = wma_set_power_config(adapter->vdev_id, power); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("failed to configure power: %d", status); + return -EINVAL; + } + + /* cache latest userspace power save config to reapply after SSR */ + ucfg_pmo_set_power_save_mode(hddctx->psoc, power); + + return 0; +} + + +#ifdef WLAN_SUSPEND_RESUME_TEST +static struct net_device *g_dev; +static struct wiphy *g_wiphy; +static enum wow_resume_trigger g_resume_trigger; + +#define HDD_FA_SUSPENDED_BIT (0) +static unsigned long fake_apps_state; + +/** + * __hdd_wlan_fake_apps_resume() - The core logic for + * hdd_wlan_fake_apps_resume() skipping the call to hif_fake_apps_resume(), + * which is only need for non-irq resume + * @wiphy: the kernel wiphy struct for the device being resumed + * @dev: the kernel net_device struct for the device being resumed + * + * Return: none, calls QDF_BUG() on failure + */ +static void __hdd_wlan_fake_apps_resume(struct wiphy *wiphy, + struct net_device *dev) +{ + struct hif_opaque_softc *hif_ctx; + qdf_device_t qdf_dev; + + hdd_info("Unit-test resume WLAN"); + + qdf_dev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_dev) { + hdd_err("Failed to get QDF device context"); + QDF_BUG(0); + return; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("Failed to get HIF context"); + return; + } + + if (!test_and_clear_bit(HDD_FA_SUSPENDED_BIT, &fake_apps_state)) { + hdd_alert("Not unit-test suspended; Nothing to do"); + return; + } + + /* simulate kernel disable irqs */ + QDF_BUG(!hif_apps_wake_irq_disable(hif_ctx)); + + QDF_BUG(!wlan_hdd_bus_resume_noirq()); + + /* simulate kernel enable irqs */ + QDF_BUG(!hif_apps_irqs_enable(hif_ctx)); + + QDF_BUG(!wlan_hdd_bus_resume()); + + QDF_BUG(!wlan_hdd_cfg80211_resume_wlan(wiphy)); + + if (g_resume_trigger == WOW_RESUME_TRIGGER_HTC_WAKEUP) + hif_vote_link_down(hif_ctx); + + dev->watchdog_timeo = HDD_TX_TIMEOUT; + + hdd_alert("Unit-test resume succeeded"); +} + +/** + * hdd_wlan_fake_apps_resume_irq_callback() - Irq callback function for resuming + * from unit-test initiated suspend from irq wakeup signal + * + * Resume wlan after getting very 1st CE interrupt from target + * + * Return: none + */ +static void hdd_wlan_fake_apps_resume_irq_callback(void) +{ + hdd_info("Trigger unit-test resume WLAN"); + + QDF_BUG(g_wiphy); + QDF_BUG(g_dev); + __hdd_wlan_fake_apps_resume(g_wiphy, g_dev); + g_wiphy = NULL; + g_dev = NULL; +} + +int hdd_wlan_fake_apps_suspend(struct wiphy *wiphy, struct net_device *dev, + enum wow_interface_pause pause_setting, + enum wow_resume_trigger resume_setting) +{ + int errno; + qdf_device_t qdf_dev; + struct hif_opaque_softc *hif_ctx; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct wow_enable_params wow_params = { + .is_unit_test = true, + .interface_pause = pause_setting, + .resume_trigger = resume_setting + }; + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (!hdd_ctx->config->is_unit_test_framework_enabled) { + hdd_warn_rl("UT framework is disabled"); + return -EINVAL; + } + + hdd_info("Unit-test suspend WLAN"); + + if (pause_setting < WOW_INTERFACE_PAUSE_DEFAULT || + pause_setting >= WOW_INTERFACE_PAUSE_COUNT) { + hdd_err("Invalid interface pause %d (expected range [0, 2])", + pause_setting); + return -EINVAL; + } + + if (resume_setting < WOW_RESUME_TRIGGER_DEFAULT || + resume_setting >= WOW_RESUME_TRIGGER_COUNT) { + hdd_err("Invalid resume trigger %d (expected range [0, 2])", + resume_setting); + return -EINVAL; + } + + qdf_dev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf_dev) { + hdd_err("Failed to get QDF device context"); + return -EINVAL; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("Failed to get HIF context"); + return -EINVAL; + } + + if (test_and_set_bit(HDD_FA_SUSPENDED_BIT, &fake_apps_state)) { + hdd_alert("Already unit-test suspended; Nothing to do"); + return 0; + } + + /* pci link is needed to wakeup from HTC wakeup trigger */ + if (resume_setting == WOW_RESUME_TRIGGER_HTC_WAKEUP) + hif_vote_link_up(hif_ctx); + + errno = wlan_hdd_cfg80211_suspend_wlan(wiphy, NULL); + if (errno) + goto link_down; + + errno = wlan_hdd_unit_test_bus_suspend(wow_params); + if (errno) + goto cfg80211_resume; + + /* simulate kernel disabling irqs */ + errno = hif_apps_irqs_disable(hif_ctx); + if (errno) + goto bus_resume; + + errno = wlan_hdd_bus_suspend_noirq(); + if (errno) + goto enable_irqs; + + /* pass wiphy/dev to callback via global variables */ + g_wiphy = wiphy; + g_dev = dev; + g_resume_trigger = resume_setting; + hif_ut_apps_suspend(hif_ctx, hdd_wlan_fake_apps_resume_irq_callback); + + /* re-enable wake irq */ + errno = hif_apps_wake_irq_enable(hif_ctx); + if (errno) + goto fake_apps_resume; + + /* + * Tell the kernel not to worry if TX queues aren't moving. This is + * expected since we are suspending the wifi hardware, but not APPS + */ + dev->watchdog_timeo = INT_MAX; + + hdd_alert("Unit-test suspend succeeded"); + + return 0; + +fake_apps_resume: + hif_ut_apps_resume(hif_ctx); + +enable_irqs: + QDF_BUG(!hif_apps_irqs_enable(hif_ctx)); + +bus_resume: + QDF_BUG(!wlan_hdd_bus_resume()); + +cfg80211_resume: + QDF_BUG(!wlan_hdd_cfg80211_resume_wlan(wiphy)); + +link_down: + hif_vote_link_down(hif_ctx); + + clear_bit(HDD_FA_SUSPENDED_BIT, &fake_apps_state); + hdd_err("Unit-test suspend failed: %d", errno); + + return errno; +} + +int hdd_wlan_fake_apps_resume(struct wiphy *wiphy, struct net_device *dev) +{ + struct hif_opaque_softc *hif_ctx; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + if (!hdd_ctx->config->is_unit_test_framework_enabled) { + hdd_warn_rl("UT framework is disabled"); + return -EINVAL; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (!hif_ctx) { + hdd_err("Failed to get HIF context"); + return -EINVAL; + } + + hif_ut_apps_resume(hif_ctx); + __hdd_wlan_fake_apps_resume(wiphy, dev); + + return 0; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_regulatory.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_regulatory.c new file mode 100644 index 0000000000000000000000000000000000000000..3bc1b0a86e882e82d3d50dbf3fd85dac196a33ad --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_regulatory.c @@ -0,0 +1,1523 @@ +/* + * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_regulatory.c + * + * hdd regulatory implementation + */ + +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_hdd_main.h" +#include +#include "wlan_hdd_regulatory.h" +#include +#include "cds_regdomain.h" +#include "cds_utils.h" +#include "pld_common.h" +#include +#include "wlan_policy_mgr_ucfg.h" + +#define REG_RULE_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0) + +#define REG_RULE_2467_2472 REG_RULE(2467-10, 2472+10, 40, 0, 20, \ + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) + +#define REG_RULE_2484 REG_RULE(2484-10, 2484+10, 20, 0, 20, \ + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | \ + NL80211_RRF_NO_OFDM) + +#define REG_RULE_5180_5320 REG_RULE(5180-10, 5320+10, 160, 0, 20, \ + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) + +#define REG_RULE_5500_5720 REG_RULE(5500-10, 5720+10, 160, 0, 20, \ + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) + +#define REG_RULE_5745_5925 REG_RULE(5745-10, 5925+10, 80, 0, 20, \ + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) + +static bool init_by_driver; +static bool init_by_reg_core; + +struct regulatory_channel reg_channels[NUM_CHANNELS]; + +static const struct ieee80211_regdomain +hdd_world_regrules_60_61_62 = { + .n_reg_rules = 6, + .alpha2 = "00", + .reg_rules = { + REG_RULE_2412_2462, + REG_RULE_2467_2472, + REG_RULE_2484, + REG_RULE_5180_5320, + REG_RULE_5500_5720, + REG_RULE_5745_5925, + } +}; + +static const struct ieee80211_regdomain +hdd_world_regrules_63_65 = { + .n_reg_rules = 4, + .alpha2 = "00", + .reg_rules = { + REG_RULE_2412_2462, + REG_RULE_2467_2472, + REG_RULE_5180_5320, + REG_RULE_5745_5925, + } +}; + +static const struct ieee80211_regdomain +hdd_world_regrules_64 = { + .n_reg_rules = 3, + .alpha2 = "00", + .reg_rules = { + REG_RULE_2412_2462, + REG_RULE_5180_5320, + REG_RULE_5745_5925, + } +}; + +static const struct ieee80211_regdomain +hdd_world_regrules_66_69 = { + .n_reg_rules = 4, + .alpha2 = "00", + .reg_rules = { + REG_RULE_2412_2462, + REG_RULE_5180_5320, + REG_RULE_5500_5720, + REG_RULE_5745_5925, + } +}; + +static const struct ieee80211_regdomain +hdd_world_regrules_67_68_6A_6C = { + .n_reg_rules = 5, + .alpha2 = "00", + .reg_rules = { + REG_RULE_2412_2462, + REG_RULE_2467_2472, + REG_RULE_5180_5320, + REG_RULE_5500_5720, + REG_RULE_5745_5925, + } +}; + +/** + * hdd_get_world_regrules() - get the appropriate world regrules + * @reg: regulatory data + * + * Return: regulatory rules ptr + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) +static const struct ieee80211_regdomain *hdd_get_world_regrules( + struct regulatory *reg) +{ + struct reg_dmn_pair *regpair = + (struct reg_dmn_pair *)reg->regpair; + + switch (regpair->reg_dmn_pair) { + case 0x60: + case 0x61: + case 0x62: + return &hdd_world_regrules_60_61_62; + case 0x63: + case 0x65: + return &hdd_world_regrules_63_65; + case 0x64: + return &hdd_world_regrules_64; + case 0x66: + case 0x69: + return &hdd_world_regrules_66_69; + case 0x67: + case 0x68: + case 0x6A: + case 0x6C: + return &hdd_world_regrules_67_68_6A_6C; + default: + hdd_warn("invalid world mode in BDF"); + return &hdd_world_regrules_60_61_62; + } +} + +/** + * hdd_is_world_regdomain() - whether world regdomain + * @reg_domain: integer regulatory domain + * + * Return: bool + */ +static bool hdd_is_world_regdomain(uint32_t reg_domain) +{ + uint32_t temp_regd = reg_domain & ~WORLD_ROAMING_FLAG; + + return ((temp_regd & CTRY_FLAG) != CTRY_FLAG) && + ((temp_regd & WORLD_ROAMING_MASK) == + WORLD_ROAMING_PREFIX); +} + +/** + * hdd_update_regulatory_info() - update regulatory info + * @hdd_ctx: hdd context + * + * Return: Error Code + */ +static int hdd_update_regulatory_info(struct hdd_context *hdd_ctx) +{ + uint32_t country_code; + + country_code = cds_get_country_from_alpha2(hdd_ctx->reg.alpha2); + + hdd_ctx->reg.reg_domain = CTRY_FLAG; + hdd_ctx->reg.reg_domain |= country_code; + + return cds_fill_some_regulatory_info(&hdd_ctx->reg); + +} +#endif + +/** + * hdd_reset_global_reg_params - Reset global static reg params + * + * This function is helpful in static driver to reset + * the global params. + * + * Return: void + */ +void hdd_reset_global_reg_params(void) +{ + init_by_driver = false; + init_by_reg_core = false; +} + +static void reg_program_config_vars(struct hdd_context *hdd_ctx, + struct reg_config_vars *config_vars) +{ + uint8_t indoor_chnl_marking = 0; + uint32_t band_capability = 0, scan_11d_interval = 0; + bool indoor_chan_enabled = false; + uint32_t restart_beaconing = 0; + uint8_t enable_srd_chan; + QDF_STATUS status; + bool country_priority = 0; + bool value = false; + bool enable_dfs_scan = true; + + status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_capability); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to get MLME band cap, defaulting to BAND_ALL"); + + status = ucfg_policy_mgr_get_indoor_chnl_marking(hdd_ctx->psoc, + &indoor_chnl_marking); + if (QDF_STATUS_SUCCESS != status) + hdd_err("can't get indoor channel marking, using default"); + + status = ucfg_mlme_is_11d_enabled(hdd_ctx->psoc, &value); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Invalid 11d_enable flag"); + config_vars->enable_11d_support = value; + + ucfg_mlme_get_nol_across_regdmn(hdd_ctx->psoc, &value); + config_vars->retain_nol_across_regdmn_update = value; + + ucfg_mlme_get_scan_11d_interval(hdd_ctx->psoc, &scan_11d_interval); + config_vars->scan_11d_interval = scan_11d_interval; + + ucfg_mlme_get_sap_country_priority(hdd_ctx->psoc, + &country_priority); + config_vars->userspace_ctry_priority = country_priority; + + ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc, + &enable_dfs_scan); + + config_vars->dfs_enabled = enable_dfs_scan; + + ucfg_mlme_get_indoor_channel_support(hdd_ctx->psoc, + &indoor_chan_enabled); + config_vars->indoor_chan_enabled = indoor_chan_enabled; + + config_vars->force_ssc_disable_indoor_channel = indoor_chnl_marking; + config_vars->band_capability = band_capability; + + ucfg_mlme_get_restart_beaconing_on_ch_avoid(hdd_ctx->psoc, + &restart_beaconing); + config_vars->restart_beaconing = restart_beaconing; + + ucfg_mlme_get_etsi_srd_chan_in_master_mode(hdd_ctx->psoc, + &enable_srd_chan); + config_vars->enable_srd_chan_in_master_mode = enable_srd_chan; + + ucfg_mlme_get_11d_in_world_mode(hdd_ctx->psoc, + &config_vars->enable_11d_in_world_mode); +} + +/** + * hdd_regulatory_wiphy_init() - regulatory wiphy init + * @hdd_ctx: hdd context + * @reg: regulatory data + * @wiphy: wiphy structure + * + * Return: void + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS) +static void hdd_regulatory_wiphy_init(struct hdd_context *hdd_ctx, + struct regulatory *reg, + struct wiphy *wiphy) +{ + const struct ieee80211_regdomain *reg_rules; + int chan_num; + struct ieee80211_channel *chan; + + if (hdd_is_world_regdomain(reg->reg_domain)) { + reg_rules = hdd_get_world_regrules(reg); + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + } else { + wiphy->regulatory_flags |= REGULATORY_STRICT_REG; + reg_rules = &hdd_world_regrules_60_61_62; + } + + /* + * save the original driver regulatory flags + */ + hdd_ctx->reg.reg_flags = wiphy->regulatory_flags; + wiphy_apply_custom_regulatory(wiphy, reg_rules); + + /* + * disable 2.4 Ghz channels that dont have 20 mhz bw + */ + for (chan_num = 0; + chan_num < wiphy->bands[HDD_NL80211_BAND_2GHZ]->n_channels; + chan_num++) { + chan = &(wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels[chan_num]); + if (chan->flags & IEEE80211_CHAN_NO_20MHZ) + chan->flags |= IEEE80211_CHAN_DISABLED; + } + + /* + * restore the driver regulatory flags since + * wiphy_apply_custom_regulatory may have + * changed them + */ + wiphy->regulatory_flags = hdd_ctx->reg.reg_flags; + +} +#else +static void hdd_regulatory_wiphy_init(struct hdd_context *hdd_ctx, + struct regulatory *reg, + struct wiphy *wiphy) +{ + const struct ieee80211_regdomain *reg_rules; + + if (hdd_is_world_regdomain(reg->reg_domain)) { + reg_rules = hdd_get_world_regrules(reg); + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + } else { + wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; + reg_rules = &hdd_world_regrules_60_61_62; + } + + /* + * save the original driver regulatory flags + */ + hdd_ctx->reg.reg_flags = wiphy->flags; + wiphy_apply_custom_regulatory(wiphy, reg_rules); + + /* + * restore the driver regulatory flags since + * wiphy_apply_custom_regulatory may have + * changed them + */ + wiphy->flags = hdd_ctx->reg.reg_flags; + +} +#endif + +/** + * is_wiphy_custom_regulatory() - is custom regulatory defined + * @wiphy: wiphy + * + * Return: int + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS) +static int is_wiphy_custom_regulatory(struct wiphy *wiphy) +{ + + return wiphy->regulatory_flags & REGULATORY_CUSTOM_REG; +} +#else +static int is_wiphy_custom_regulatory(struct wiphy *wiphy) +{ + return wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY; +} +#endif + +/** + * hdd_modify_wiphy() - modify wiphy + * @wiphy: wiphy + * @chan: channel structure + * + * Return: void + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) +static void hdd_modify_wiphy(struct wiphy *wiphy, + struct ieee80211_channel *chan) +{ + const struct ieee80211_reg_rule *reg_rule; + + if (is_wiphy_custom_regulatory(wiphy)) { + reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); + if (!IS_ERR(reg_rule)) { + chan->flags &= ~IEEE80211_CHAN_DISABLED; + + if (!(reg_rule->flags & NL80211_RRF_DFS)) { + hdd_debug("Remove dfs restriction for %u", + chan->center_freq); + chan->flags &= ~IEEE80211_CHAN_RADAR; + } + + if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) { + hdd_debug("Remove passive restriction for %u", + chan->center_freq); + chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + } + + if (!(reg_rule->flags & NL80211_RRF_NO_IBSS)) { + hdd_debug("Remove no ibss restriction for %u", + chan->center_freq); + chan->flags &= ~IEEE80211_CHAN_NO_IBSS; + } + + chan->max_power = + MBM_TO_DBM(reg_rule->power_rule.max_eirp); + } + } +} +#endif + +/** + * hdd_set_dfs_region() - set the dfs_region + * @dfs_region: the dfs_region to set + * + * Return: void + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS) +static void hdd_set_dfs_region(struct hdd_context *hdd_ctx, + enum dfs_reg dfs_reg) +{ + wlan_reg_set_dfs_region(hdd_ctx->pdev, dfs_reg); +} +#endif + +/** + * hdd_process_regulatory_data() - process regulatory data + * @hdd_ctx: hdd context + * @wiphy: wiphy + * @reset: whether to reset channel data + * + * Return: void + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) +static void hdd_process_regulatory_data(struct hdd_context *hdd_ctx, + struct wiphy *wiphy, + bool reset) +{ + int band_num; + int chan_num; + enum channel_enum chan_enum = CHAN_ENUM_1; + struct ieee80211_channel *wiphy_chan, *wiphy_chan_144 = NULL; + struct regulatory_channel *cds_chan; + uint8_t band_capability, indoor_chnl_marking = 0; + bool indoor; + QDF_STATUS status; + + band_capability = hdd_ctx->curr_band; + + status = ucfg_policy_mgr_get_indoor_chnl_marking(hdd_ctx->psoc, + &indoor_chnl_marking); + if (QDF_STATUS_SUCCESS != status) + hdd_err("can't get indoor channel marking, using default"); + + for (band_num = 0; band_num < HDD_NUM_NL80211_BANDS; band_num++) { + + if (!wiphy->bands[band_num]) + continue; + + for (chan_num = 0; + chan_num < wiphy->bands[band_num]->n_channels && + chan_enum < NUM_CHANNELS; + chan_num++) { + wiphy_chan = + &(wiphy->bands[band_num]->channels[chan_num]); + cds_chan = &(reg_channels[chan_enum]); + cds_chan->chan_flags = 0; + if (CHAN_ENUM_144 == chan_enum) + wiphy_chan_144 = wiphy_chan; + + chan_enum++; + + if (!reset) + hdd_modify_wiphy(wiphy, wiphy_chan); + + if (indoor_chnl_marking && + (wiphy_chan->flags & IEEE80211_CHAN_INDOOR_ONLY)) + cds_chan->chan_flags |= + REGULATORY_CHAN_INDOOR_ONLY; + + if (wiphy_chan->flags & IEEE80211_CHAN_DISABLED) { + cds_chan->state = CHANNEL_STATE_DISABLE; + cds_chan->chan_flags |= + REGULATORY_CHAN_DISABLED; + } else if (wiphy_chan->flags & + (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_PASSIVE_SCAN)) { + cds_chan->state = CHANNEL_STATE_DFS; + if (wiphy_chan->flags & IEEE80211_CHAN_RADAR) + cds_chan->chan_flags |= + REGULATORY_CHAN_RADAR; + if (wiphy_chan->flags & + IEEE80211_CHAN_PASSIVE_SCAN) + cds_chan->chan_flags |= + REGULATORY_CHAN_NO_IR; + } else if (wiphy_chan->flags & + IEEE80211_CHAN_INDOOR_ONLY) { + cds_chan->chan_flags |= + REGULATORY_CHAN_INDOOR_ONLY; + + ucfg_mlme_get_indoor_channel_support( + hdd_ctx->psoc, + &indoor); + if (!indoor) { + cds_chan->state = CHANNEL_STATE_DFS; + wiphy_chan->flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + cds_chan->chan_flags |= + REGULATORY_CHAN_NO_IR; + } else + cds_chan->state = CHANNEL_STATE_ENABLE; + } else + cds_chan->state = CHANNEL_STATE_ENABLE; + cds_chan->tx_power = wiphy_chan->max_power; + if (wiphy_chan->flags & IEEE80211_CHAN_NO_10MHZ) + cds_chan->max_bw = 5; + else if (wiphy_chan->flags & IEEE80211_CHAN_NO_20MHZ) + cds_chan->max_bw = 10; + /* + * IEEE80211_CHAN_NO_HT40 is defined as 0x30 in kernel + * 4th BIT representing IEEE80211_CHAN_NO_HT40PLUS + * 5th BIT representing IEEE80211_CHAN_NO_HT40MINUS + * + * In order to claim no 40Mhz support value of + * wiphy_chan->flags needs to be 0x30. + * 0x20 and 0x10 values shows that either HT40+ or + * HT40- is not supported based on BIT set but they + * can support 40Mhz Operation. + */ + else if ((wiphy_chan->flags & IEEE80211_CHAN_NO_HT40) == + IEEE80211_CHAN_NO_HT40) + cds_chan->max_bw = 20; + else if (wiphy_chan->flags & IEEE80211_CHAN_NO_80MHZ) + cds_chan->max_bw = 40; + else if (wiphy_chan->flags & IEEE80211_CHAN_NO_160MHZ) + cds_chan->max_bw = 80; + else + cds_chan->max_bw = 160; + } + } + + if (0 == (hdd_ctx->reg.eeprom_rd_ext & + (1 << WMI_REG_EXT_FCC_CH_144))) { + cds_chan = &(reg_channels[CHAN_ENUM_144]); + cds_chan->state = CHANNEL_STATE_DISABLE; + if (wiphy_chan_144) + wiphy_chan_144->flags |= IEEE80211_CHAN_DISABLED; + } + + wlan_hdd_cfg80211_update_band(hdd_ctx, wiphy, band_capability); +} + +/** + * hdd_regulatory_init_no_offload() - regulatory init + * @hdd_ctx: hdd context + * @wiphy: wiphy + * + * Return: int + */ +static int hdd_regulatory_init_no_offload(struct hdd_context *hdd_ctx, + struct wiphy *wiphy) +{ + int ret_val; + struct regulatory *reg_info; + enum dfs_reg dfs_reg; + struct reg_config_vars config_vars; + + reg_info = &hdd_ctx->reg; + + ret_val = cds_fill_some_regulatory_info(reg_info); + if (ret_val) { + hdd_err("incorrect BDF regulatory data"); + return ret_val; + } + + hdd_set_dfs_region(hdd_ctx, DFS_FCC_REGION); + + hdd_regulatory_wiphy_init(hdd_ctx, reg_info, wiphy); + + hdd_process_regulatory_data(hdd_ctx, wiphy, true); + + reg_info->cc_src = SOURCE_DRIVER; + + ucfg_reg_set_default_country(hdd_ctx->psoc, reg_info->alpha2); + + cds_fill_and_send_ctl_to_fw(reg_info); + + wlan_reg_get_dfs_region(hdd_ctx->pdev, &dfs_reg); + + reg_program_config_vars(hdd_ctx, &config_vars); + ucfg_reg_set_config_vars(hdd_ctx->psoc, config_vars); + ucfg_reg_program_mas_chan_list(hdd_ctx->psoc, + reg_channels, + hdd_ctx->reg.alpha2, + dfs_reg); + + return 0; +} +#endif + +/** + * hdd_modify_indoor_channel_state_flags() - modify wiphy flags and cds state + * @wiphy_chan: wiphy channel number + * @cds_chan: cds channel structure + * @disable: Disable/enable the flags + * + * Modify wiphy flags and cds state if channel is indoor. + * + * Return: void + */ +void hdd_modify_indoor_channel_state_flags( + struct hdd_context *hdd_ctx, + struct ieee80211_channel *wiphy_chan, + struct regulatory_channel *cds_chan, + enum channel_enum chan_enum, int chan_num, bool disable) +{ + bool indoor_support; + + ucfg_mlme_get_indoor_channel_support(hdd_ctx->psoc, &indoor_support); + + /* Mark indoor channel to disable in wiphy and cds */ + if (disable) { + if (wiphy_chan->flags & IEEE80211_CHAN_INDOOR_ONLY) { + wiphy_chan->flags |= + IEEE80211_CHAN_DISABLED; + hdd_info("Mark indoor channel %d as disable", + cds_chan->center_freq); + cds_chan->state = + CHANNEL_STATE_DISABLE; + } + } else { + if (wiphy_chan->flags & IEEE80211_CHAN_INDOOR_ONLY) { + wiphy_chan->flags &= + ~IEEE80211_CHAN_DISABLED; + /* + * Indoor channels may be marked as dfs / enable + * during regulatory processing + */ + if ((wiphy_chan->flags & + (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_PASSIVE_SCAN)) || + ((indoor_support == false) && + (wiphy_chan->flags & + IEEE80211_CHAN_INDOOR_ONLY))) + cds_chan->state = + CHANNEL_STATE_DFS; + else + cds_chan->state = + CHANNEL_STATE_ENABLE; + hdd_debug("Mark indoor channel %d as cds_chan state %d", + cds_chan->chan_num, cds_chan->state); + } + } + +} + +void hdd_update_indoor_channel(struct hdd_context *hdd_ctx, + bool disable) +{ + int band_num; + int chan_num; + enum channel_enum chan_enum = CHAN_ENUM_2412; + struct ieee80211_channel *wiphy_chan, *wiphy_chan_144 = NULL; + struct regulatory_channel *cds_chan; + uint8_t band_capability; + struct wiphy *wiphy = hdd_ctx->wiphy; + + hdd_enter(); + hdd_debug("mark indoor channel disable: %d", disable); + + band_capability = hdd_ctx->curr_band; + for (band_num = 0; band_num < HDD_NUM_NL80211_BANDS; band_num++) { + + if (!wiphy->bands[band_num]) + continue; + + for (chan_num = 0; + chan_num < wiphy->bands[band_num]->n_channels && + chan_enum < NUM_CHANNELS; + chan_num++) { + + wiphy_chan = + &(wiphy->bands[band_num]->channels[chan_num]); + cds_chan = &(reg_channels[chan_enum]); + if (chan_enum == CHAN_ENUM_5720) + wiphy_chan_144 = wiphy_chan; + + chan_enum++; + hdd_modify_indoor_channel_state_flags(hdd_ctx, + wiphy_chan, cds_chan, + chan_enum, chan_num, disable); + } + } + + /* Notify the regulatory domain to update the channel list */ + if (QDF_IS_STATUS_ERROR(ucfg_reg_notify_sap_event(hdd_ctx->pdev, + disable))) { + hdd_err("Failed to notify sap event"); + } + hdd_exit(); + +} + +/** + * hdd_program_country_code() - process channel information from country code + * @hdd_ctx: hddc context + * + * Return: void + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +void hdd_program_country_code(struct hdd_context *hdd_ctx) +{ +} +#else +void hdd_program_country_code(struct hdd_context *hdd_ctx) +{ + struct wiphy *wiphy = hdd_ctx->wiphy; + uint8_t *country_alpha2 = hdd_ctx->reg.alpha2; + + if (!init_by_reg_core && !init_by_driver) { + init_by_driver = true; + if (('0' != country_alpha2[0]) || + ('0' != country_alpha2[1])) + regulatory_hint(wiphy, country_alpha2); + } +} +#endif + +int hdd_reg_set_country(struct hdd_context *hdd_ctx, char *country_code) +{ + QDF_STATUS status; + uint8_t cc[REG_ALPHA2_LEN + 1]; + + if (!country_code) { + hdd_err("country_code is null"); + return -EINVAL; + } + + qdf_mem_copy(cc, country_code, REG_ALPHA2_LEN); + cc[REG_ALPHA2_LEN] = '\0'; + + status = ucfg_reg_set_country(hdd_ctx->pdev, cc); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set country"); + + return qdf_status_to_os_return(status); +} + +uint32_t hdd_reg_legacy_setband_to_reg_wifi_band_bitmap(uint8_t qca_setband) +{ + uint32_t band_bitmap = 0; + + switch (qca_setband) { + case QCA_SETBAND_AUTO: + band_bitmap |= (BIT(REG_BAND_2G) | BIT(REG_BAND_5G)); + break; + case QCA_SETBAND_5G: + band_bitmap |= BIT(REG_BAND_5G); + break; + case QCA_SETBAND_2G: + band_bitmap |= BIT(REG_BAND_2G); + break; + default: + hdd_err("Invalid band value %u", qca_setband); + return 0; + } + + return band_bitmap; +} + +int hdd_reg_set_band(struct net_device *dev, uint32_t band_bitmap) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + uint32_t current_band; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (!band_bitmap) { + hdd_err("Can't disable all bands"); + return -EINVAL; + } + + hdd_debug("change band to %u", band_bitmap); + + if (ucfg_reg_get_band(hdd_ctx->pdev, ¤t_band) != + QDF_STATUS_SUCCESS) { + hdd_debug("Failed to get current band config"); + return -EIO; + } + + if (current_band == band_bitmap) { + hdd_debug("band is the same so not updating"); + return 0; + } + + hdd_ctx->curr_band = wlan_reg_band_bitmap_to_band_info(band_bitmap); + + if (QDF_IS_STATUS_ERROR(ucfg_reg_set_band(hdd_ctx->pdev, + band_bitmap))) { + hdd_err("Failed to set the band bitmap value to %u", + band_bitmap); + return -EINVAL; + } + + return 0; +} + +/** + * hdd_restore_custom_reg_settings() - restore custom reg settings + * @wiphy: wiphy structure + * @country_alpha2: alpha2 of the country + * @reset: whether wiphy is reset + * + * Return: void + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS) +static void hdd_restore_custom_reg_settings(struct wiphy *wiphy, + uint8_t *country_alpha2, + bool *reset) +{ +} +#else +static void hdd_restore_custom_reg_settings(struct wiphy *wiphy, + uint8_t *country_alpha2, + bool *reset) +{ + struct ieee80211_supported_band *sband; + enum nl80211_band band; + struct ieee80211_channel *chan; + int i; + + if ((country_alpha2[0] == '0') && + (country_alpha2[1] == '0') && + (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)) { + + for (band = 0; band < HDD_NUM_NL80211_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + chan->flags = chan->orig_flags; + chan->max_antenna_gain = chan->orig_mag; + chan->max_power = chan->orig_mpwr; + } + } + *reset = true; + } +} +#endif + +/** + * hdd_restore_reg_flags() - restore regulatory flags + * @flags: regulatory flags + * + * Return: void + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS) +static void hdd_restore_reg_flags(struct wiphy *wiphy, uint32_t flags) +{ + wiphy->regulatory_flags = flags; +} +#else +static void hdd_restore_reg_flags(struct wiphy *wiphy, uint32_t flags) +{ + wiphy->flags = flags; +} +#endif + +/** + * hdd_reg_notifier() - regulatory notifier + * @wiphy: wiphy + * @request: regulatory request + * + * Return: void + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +void hdd_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + char country[REG_ALPHA2_LEN + 1] = {0}; + + hdd_debug("country: %c%c, initiator %d, dfs_region: %d", + request->alpha2[0], + request->alpha2[1], + request->initiator, + request->dfs_region); + + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_USER: + + if (request->user_reg_hint_type != + NL80211_USER_REG_HINT_CELL_BASE) + return; + + qdf_mem_copy(country, request->alpha2, QDF_MIN( + sizeof(request->alpha2), sizeof(country))); + status = ucfg_reg_set_country(hdd_ctx->pdev, country); + break; + case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + case NL80211_REGDOM_SET_BY_DRIVER: + default: + break; + } + + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("Failed to set country"); +} +#else +void hdd_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + bool reset = false; + enum dfs_reg dfs_reg; + struct reg_config_vars config_vars; + int ret_val; + + hdd_debug("country: %c%c, initiator %d, dfs_region: %d", + request->alpha2[0], + request->alpha2[1], + request->initiator, + request->dfs_region); + + if (!hdd_ctx) { + hdd_err("invalid hdd_ctx pointer"); + return; + } + + if (cds_is_driver_unloading() || cds_is_driver_recovering() || + cds_is_driver_in_bad_state()) { + hdd_err("%s: unloading or ssr in progress, ignore", + __func__); + return; + } + + if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { + hdd_err("Driver module is closed; dropping request"); + return; + } + + if (hdd_ctx->is_wiphy_suspended == true) { + hdd_err("%s: system/cfg80211 is already suspend", __func__); + return; + } + + if (('K' == request->alpha2[0]) && + ('R' == request->alpha2[1])) + request->dfs_region = (enum nl80211_dfs_regions)DFS_KR_REGION; + + if (('C' == request->alpha2[0]) && + ('N' == request->alpha2[1])) + request->dfs_region = (enum nl80211_dfs_regions)DFS_CN_REGION; + + /* first check if this callback is in response to the driver callback */ + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_USER: + + if ((false == init_by_driver) && + (false == init_by_reg_core)) { + + if (NL80211_REGDOM_SET_BY_CORE == request->initiator) + return; + init_by_reg_core = true; + } + + if ((NL80211_REGDOM_SET_BY_DRIVER == request->initiator) && + (true == init_by_driver)) { + + /* + * restore the driver regulatory flags since + * regulatory_hint may have + * changed them + */ + hdd_restore_reg_flags(wiphy, hdd_ctx->reg.reg_flags); + } + + if (NL80211_REGDOM_SET_BY_CORE == request->initiator) { + hdd_ctx->reg.cc_src = SOURCE_CORE; + if (is_wiphy_custom_regulatory(wiphy)) + reset = true; + } else if (NL80211_REGDOM_SET_BY_DRIVER == request->initiator) { + hdd_ctx->reg.cc_src = SOURCE_DRIVER; + sme_set_cc_src(hdd_ctx->mac_handle, SOURCE_DRIVER); + } else { + hdd_ctx->reg.cc_src = SOURCE_USERSPACE; + hdd_restore_custom_reg_settings(wiphy, + request->alpha2, + &reset); + } + + hdd_ctx->reg.alpha2[0] = request->alpha2[0]; + hdd_ctx->reg.alpha2[1] = request->alpha2[1]; + + ret_val = hdd_update_regulatory_info(hdd_ctx); + if (ret_val) { + hdd_err("invalid reg info, do not process"); + return; + } + + hdd_process_regulatory_data(hdd_ctx, wiphy, reset); + + sme_generic_change_country_code(hdd_ctx->mac_handle, + hdd_ctx->reg.alpha2); + + cds_fill_and_send_ctl_to_fw(&hdd_ctx->reg); + + hdd_set_dfs_region(hdd_ctx, request->dfs_region); + wlan_reg_get_dfs_region(hdd_ctx->pdev, &dfs_reg); + + reg_program_config_vars(hdd_ctx, &config_vars); + ucfg_reg_set_config_vars(hdd_ctx->psoc, config_vars); + ucfg_reg_program_mas_chan_list(hdd_ctx->psoc, + reg_channels, + hdd_ctx->reg.alpha2, + dfs_reg); + break; + + default: + break; + } +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +static void fill_wiphy_channel(struct ieee80211_channel *wiphy_chan, + struct regulatory_channel *cur_chan) +{ + + wiphy_chan->flags = 0; + wiphy_chan->max_power = cur_chan->tx_power; + + if (cur_chan->chan_flags & REGULATORY_CHAN_DISABLED) + wiphy_chan->flags |= IEEE80211_CHAN_DISABLED; + if (cur_chan->chan_flags & REGULATORY_CHAN_NO_IR) + wiphy_chan->flags |= IEEE80211_CHAN_NO_IR; + if (cur_chan->chan_flags & REGULATORY_CHAN_RADAR) + wiphy_chan->flags |= IEEE80211_CHAN_RADAR; + if (cur_chan->chan_flags & REGULATORY_CHAN_NO_OFDM) + wiphy_chan->flags |= IEEE80211_CHAN_NO_OFDM; + if (cur_chan->chan_flags & REGULATORY_CHAN_INDOOR_ONLY) + wiphy_chan->flags |= IEEE80211_CHAN_INDOOR_ONLY; + + if (cur_chan->max_bw < 10) + wiphy_chan->flags |= IEEE80211_CHAN_NO_10MHZ; + if (cur_chan->max_bw < 20) + wiphy_chan->flags |= IEEE80211_CHAN_NO_20MHZ; + if (cur_chan->max_bw < 40) + wiphy_chan->flags |= IEEE80211_CHAN_NO_HT40; + if (cur_chan->max_bw < 80) + wiphy_chan->flags |= IEEE80211_CHAN_NO_80MHZ; + if (cur_chan->max_bw < 160) + wiphy_chan->flags |= IEEE80211_CHAN_NO_160MHZ; + + wiphy_chan->orig_flags = wiphy_chan->flags; +} + +static void fill_wiphy_band_channels(struct wiphy *wiphy, + struct regulatory_channel *cur_chan_list, + uint8_t band_id) +{ + uint32_t wiphy_num_chan, wiphy_index; + uint32_t chan_cnt; + struct ieee80211_channel *wiphy_chan; + + if (!wiphy->bands[band_id]) + return; + + wiphy_num_chan = wiphy->bands[band_id]->n_channels; + wiphy_chan = wiphy->bands[band_id]->channels; + + for (wiphy_index = 0; wiphy_index < wiphy_num_chan; wiphy_index++) { + for (chan_cnt = 0; chan_cnt < NUM_CHANNELS; chan_cnt++) { + if (wiphy_chan[wiphy_index].center_freq == + cur_chan_list[chan_cnt].center_freq) { + fill_wiphy_channel(&(wiphy_chan[wiphy_index]), + &(cur_chan_list[chan_cnt])); + break; + } + } + } +} + +#ifdef FEATURE_WLAN_CH_AVOID +/** + * hdd_ch_avoid_ind() - Avoid notified channels from FW handler + * @adapter: HDD adapter pointer + * @indParam: Channel avoid notification parameter + * + * Avoid channel notification from FW handler. + * FW will send un-safe channel list to avoid over wrapping. + * hostapd should not use notified channel + * + * Return: None + */ +void hdd_ch_avoid_ind(struct hdd_context *hdd_ctxt, + struct unsafe_ch_list *unsafe_chan_list, + struct ch_avoid_ind_type *avoid_freq_list) +{ + uint16_t *local_unsafe_list; + uint16_t local_unsafe_list_count; + uint8_t i; + + /* Basic sanity */ + if (!hdd_ctxt) { + hdd_err("Invalid arguments"); + return; + } + + mutex_lock(&hdd_ctxt->avoid_freq_lock); + qdf_mem_copy(&hdd_ctxt->coex_avoid_freq_list, avoid_freq_list, + sizeof(struct ch_avoid_ind_type)); + mutex_unlock(&hdd_ctxt->avoid_freq_lock); + + if (hdd_clone_local_unsafe_chan(hdd_ctxt, + &local_unsafe_list, + &local_unsafe_list_count) != 0) { + hdd_err("failed to clone cur unsafe chan list"); + return; + } + + /* clear existing unsafe channel cache */ + hdd_ctxt->unsafe_channel_count = 0; + qdf_mem_zero(hdd_ctxt->unsafe_channel_list, + sizeof(hdd_ctxt->unsafe_channel_list)); + + hdd_ctxt->unsafe_channel_count = unsafe_chan_list->chan_cnt; + + for (i = 0; i < unsafe_chan_list->chan_cnt; i++) { + hdd_ctxt->unsafe_channel_list[i] = + unsafe_chan_list->chan_freq_list[i]; + } + hdd_debug("number of unsafe channels is %d ", + hdd_ctxt->unsafe_channel_count); + + if (pld_set_wlan_unsafe_channel(hdd_ctxt->parent_dev, + hdd_ctxt->unsafe_channel_list, + hdd_ctxt->unsafe_channel_count)) { + hdd_err("Failed to set unsafe channel"); + + /* clear existing unsafe channel cache */ + hdd_ctxt->unsafe_channel_count = 0; + qdf_mem_zero(hdd_ctxt->unsafe_channel_list, + sizeof(hdd_ctxt->unsafe_channel_list)); + qdf_mem_free(local_unsafe_list); + return; + } + + mutex_lock(&hdd_ctxt->avoid_freq_lock); + if (hdd_ctxt->dnbs_avoid_freq_list.ch_avoid_range_cnt) + if (wlan_hdd_merge_avoid_freqs(avoid_freq_list, + &hdd_ctxt->dnbs_avoid_freq_list)) { + mutex_unlock(&hdd_ctxt->avoid_freq_lock); + hdd_debug("unable to merge avoid freqs"); + qdf_mem_free(local_unsafe_list); + return; + } + mutex_unlock(&hdd_ctxt->avoid_freq_lock); + /* + * first update the unsafe channel list to the platform driver and + * send the avoid freq event to the application + */ + wlan_hdd_send_avoid_freq_event(hdd_ctxt, avoid_freq_list); + + if (!hdd_ctxt->unsafe_channel_count) { + hdd_debug("no unsafe channels - not restarting SAP"); + qdf_mem_free(local_unsafe_list); + return; + } + if (hdd_local_unsafe_channel_updated(hdd_ctxt, + local_unsafe_list, + local_unsafe_list_count)) + hdd_unsafe_channel_restart_sap(hdd_ctxt); + qdf_mem_free(local_unsafe_list); + +} +#endif + +#if defined CFG80211_USER_HINT_CELL_BASE_SELF_MANAGED || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) +static void map_nl_reg_rule_flags(uint16_t drv_reg_rule_flag, + uint32_t *regd_rule_flag) +{ + if (drv_reg_rule_flag & REGULATORY_CHAN_NO_IR) + *regd_rule_flag |= NL80211_RRF_NO_IR; + if (drv_reg_rule_flag & REGULATORY_CHAN_RADAR) + *regd_rule_flag |= NL80211_RRF_DFS; + if (drv_reg_rule_flag & REGULATORY_CHAN_INDOOR_ONLY) + *regd_rule_flag |= NL80211_RRF_NO_OUTDOOR; + if (drv_reg_rule_flag & REGULATORY_CHAN_NO_OFDM) + *regd_rule_flag |= NL80211_RRF_NO_OFDM; + *regd_rule_flag |= NL80211_RRF_AUTO_BW; +} + +/** + * dfs_reg_to_nl80211_dfs_regions() - convert dfs_reg to nl80211_dfs_regions + * @dfs_region: DFS region + * + * Return: nl80211_dfs_regions + */ +static enum nl80211_dfs_regions dfs_reg_to_nl80211_dfs_regions( + enum dfs_reg dfs_region) +{ + switch (dfs_region) { + case DFS_UNINIT_REGION: + return NL80211_DFS_UNSET; + case DFS_FCC_REGION: + return NL80211_DFS_FCC; + case DFS_ETSI_REGION: + return NL80211_DFS_ETSI; + case DFS_MKK_REGION: + return NL80211_DFS_JP; + default: + return NL80211_DFS_UNSET; + } +} + +/** + * hdd_set_dfs_pri_multiplier() - Set dfs_pri_multiplier for ETSI region + * @dfs_region: DFS region + * + * Return: none + */ +#ifdef DFS_PRI_MULTIPLIER +static void hdd_set_dfs_pri_multiplier(struct hdd_context *hdd_ctx, + enum dfs_reg dfs_region) +{ + if (dfs_region == DFS_ETSI_REGION) + wlan_sap_set_dfs_pri_multiplier(hdd_ctx->mac_handle); +} +#else +static inline void hdd_set_dfs_pri_multiplier(struct hdd_context *hdd_ctx, + enum dfs_reg dfs_region) +{ +} +#endif + +void hdd_send_wiphy_regd_sync_event(struct hdd_context *hdd_ctx) +{ + struct ieee80211_regdomain *regd; + struct ieee80211_reg_rule *regd_rules; + struct reg_rule_info reg_rules_struct; + struct reg_rule_info *reg_rules; + QDF_STATUS status; + uint8_t i; + + if (!hdd_ctx) { + hdd_err("hdd_ctx is NULL"); + return; + } + + status = ucfg_reg_get_regd_rules(hdd_ctx->pdev, ®_rules_struct); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("could not get reg rules"); + return; + } + + reg_rules = ®_rules_struct; + if (!reg_rules->num_of_reg_rules) { + hdd_err("no reg rules %d", reg_rules->num_of_reg_rules); + return; + } + + regd = qdf_mem_malloc((reg_rules->num_of_reg_rules * + sizeof(*regd_rules) + sizeof(*regd))); + if (!regd) { + hdd_err("mem alloc failed for reg rules"); + return; + } + + regd->n_reg_rules = reg_rules->num_of_reg_rules; + qdf_mem_copy(regd->alpha2, reg_rules->alpha2, REG_ALPHA2_LEN + 1); + regd->dfs_region = + dfs_reg_to_nl80211_dfs_regions(reg_rules->dfs_region); + + hdd_set_dfs_pri_multiplier(hdd_ctx, reg_rules->dfs_region); + + regd_rules = regd->reg_rules; + hdd_debug("Regulatory Domain %s", regd->alpha2); + hdd_debug("start freq\tend freq\t@ max_bw\tant_gain\tpwr\tflags"); + for (i = 0; i < reg_rules->num_of_reg_rules; i++) { + regd_rules[i].freq_range.start_freq_khz = + reg_rules->reg_rules[i].start_freq * 1000; + regd_rules[i].freq_range.end_freq_khz = + reg_rules->reg_rules[i].end_freq * 1000; + regd_rules[i].freq_range.max_bandwidth_khz = + reg_rules->reg_rules[i].max_bw * 1000; + regd_rules[i].power_rule.max_antenna_gain = + reg_rules->reg_rules[i].ant_gain * 100; + regd_rules[i].power_rule.max_eirp = + reg_rules->reg_rules[i].reg_power * 100; + map_nl_reg_rule_flags(reg_rules->reg_rules[i].flags, + ®d_rules[i].flags); + hdd_debug("%d KHz\t%d KHz\t@ %d KHz\t%d\t\t%d\t%d", + regd_rules[i].freq_range.start_freq_khz, + regd_rules[i].freq_range.end_freq_khz, + regd_rules[i].freq_range.max_bandwidth_khz, + regd_rules[i].power_rule.max_antenna_gain, + regd_rules[i].power_rule.max_eirp, + regd_rules[i].flags); + } + + regulatory_set_wiphy_regd(hdd_ctx->wiphy, regd); + + hdd_debug("regd sync event sent with reg rules info"); + qdf_mem_free(regd); +} +#endif + +#if defined(CONFIG_BAND_6GHZ) && (defined(CFG80211_6GHZ_BAND_SUPPORTED) || \ + (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)) +static void +fill_wiphy_6ghz_band_channels(struct wiphy *wiphy, + struct regulatory_channel *chan_list) +{ + fill_wiphy_band_channels(wiphy, chan_list, NL80211_BAND_6GHZ); +} +#else +static void +fill_wiphy_6ghz_band_channels(struct wiphy *wiphy, + struct regulatory_channel *chan_list) +{ +} +#endif + +#define HDD_MAX_CHAN_INFO_LOG 192 + +/** + * hdd_regulatory_chanlist_dump() - Dump regulatory channel list info + * @chan_list: regulatory channel list + * + * Return: void + */ +static void hdd_regulatory_chanlist_dump(struct regulatory_channel *chan_list) +{ + uint32_t i; + uint8_t info[HDD_MAX_CHAN_INFO_LOG]; + int len = 0; + struct regulatory_channel *chan; + uint32_t count = 0; + int ret; + + hdd_debug("start (freq MHz, tx power dBm):"); + for (i = 0; i < NUM_CHANNELS; i++) { + chan = &chan_list[i]; + if ((chan->chan_flags & REGULATORY_CHAN_DISABLED)) + continue; + count++; + ret = scnprintf(info + len, sizeof(info) - len, "%d %d ", + chan->center_freq, chan->tx_power); + if (ret <= 0) + break; + len += ret; + if (len >= (sizeof(info) - 20)) { + hdd_debug("%s", info); + len = 0; + } + } + if (len > 0) + hdd_debug("%s", info); + hdd_debug("end total_count %d", count); +} + +static void hdd_regulatory_dyn_cbk(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev, + struct regulatory_channel *chan_list, + struct avoid_freq_ind_data *avoid_freq_ind, + void *arg) +{ + struct wiphy *wiphy; + struct pdev_osif_priv *pdev_priv; + struct hdd_context *hdd_ctx; + enum country_src cc_src; + uint8_t alpha2[REG_ALPHA2_LEN + 1]; + + pdev_priv = wlan_pdev_get_ospriv(pdev); + wiphy = pdev_priv->wiphy; + hdd_ctx = wiphy_priv(wiphy); + + hdd_debug("process channel list update from regulatory"); + hdd_regulatory_chanlist_dump(chan_list); + + fill_wiphy_band_channels(wiphy, chan_list, NL80211_BAND_2GHZ); + fill_wiphy_band_channels(wiphy, chan_list, NL80211_BAND_5GHZ); + fill_wiphy_6ghz_band_channels(wiphy, chan_list); + cc_src = ucfg_reg_get_cc_and_src(hdd_ctx->psoc, alpha2); + qdf_mem_copy(hdd_ctx->reg.alpha2, alpha2, REG_ALPHA2_LEN + 1); + sme_set_cc_src(hdd_ctx->mac_handle, cc_src); + + /* Check the kernel version for upstream commit aced43ce780dc5 that + * has support for processing user cell_base hints when wiphy is + * self managed or check the backport flag for the same. + */ +#if defined CFG80211_USER_HINT_CELL_BASE_SELF_MANAGED || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) + if (wiphy->registered) + hdd_send_wiphy_regd_sync_event(hdd_ctx); +#endif + + if (avoid_freq_ind) { + hdd_ch_avoid_ind(hdd_ctx, &avoid_freq_ind->chan_list, + &avoid_freq_ind->freq_list); + } else { + hdd_config_tdls_with_band_switch(hdd_ctx); + + sme_generic_change_country_code(hdd_ctx->mac_handle, + hdd_ctx->reg.alpha2); + /*Check whether need restart SAP/P2p Go*/ + policy_mgr_check_concurrent_intf_and_restart_sap(hdd_ctx->psoc); + } +} + +int hdd_update_regulatory_config(struct hdd_context *hdd_ctx) +{ + struct reg_config_vars config_vars; + + reg_program_config_vars(hdd_ctx, &config_vars); + ucfg_reg_set_config_vars(hdd_ctx->psoc, config_vars); + return 0; +} + +int hdd_regulatory_init(struct hdd_context *hdd_ctx, struct wiphy *wiphy) +{ + bool offload_enabled; + struct regulatory_channel *cur_chan_list; + enum country_src cc_src; + uint8_t alpha2[REG_ALPHA2_LEN + 1]; + + cur_chan_list = qdf_mem_malloc(sizeof(*cur_chan_list) * NUM_CHANNELS); + if (!cur_chan_list) { + return -ENOMEM; + } + + ucfg_reg_register_chan_change_callback(hdd_ctx->psoc, + hdd_regulatory_dyn_cbk, + NULL); + + + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; + /* Check the kernel version for upstream commit aced43ce780dc5 that + * has support for processing user cell_base hints when wiphy is + * self managed or check the backport flag for the same. + */ +#if defined CFG80211_USER_HINT_CELL_BASE_SELF_MANAGED || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) + wiphy->features |= NL80211_FEATURE_CELL_BASE_REG_HINTS; +#endif + wiphy->reg_notifier = hdd_reg_notifier; + offload_enabled = ucfg_reg_is_regdb_offloaded(hdd_ctx->psoc); + hdd_debug("regulatory offload_enabled %d", offload_enabled); + if (offload_enabled) { + hdd_ctx->reg_offload = true; + ucfg_reg_get_current_chan_list(hdd_ctx->pdev, + cur_chan_list); + hdd_regulatory_chanlist_dump(cur_chan_list); + fill_wiphy_band_channels(wiphy, cur_chan_list, + NL80211_BAND_2GHZ); + fill_wiphy_band_channels(wiphy, cur_chan_list, + NL80211_BAND_5GHZ); + fill_wiphy_6ghz_band_channels(wiphy, cur_chan_list); + cc_src = ucfg_reg_get_cc_and_src(hdd_ctx->psoc, alpha2); + qdf_mem_copy(hdd_ctx->reg.alpha2, alpha2, REG_ALPHA2_LEN + 1); + sme_set_cc_src(hdd_ctx->mac_handle, cc_src); + } else { + hdd_ctx->reg_offload = false; + } + + qdf_mem_free(cur_chan_list); + return 0; +} + +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) +int hdd_regulatory_init(struct hdd_context *hdd_ctx, struct wiphy *wiphy) +{ + hdd_ctx->reg_offload = false; + wiphy->reg_notifier = hdd_reg_notifier; + wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS; + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; + hdd_regulatory_init_no_offload(hdd_ctx, wiphy); + + return 0; +} + +#else +int hdd_regulatory_init(struct hdd_context *hdd_ctx, struct wiphy *wiphy) +{ + hdd_ctx->reg_offload = false; + wiphy->reg_notifier = hdd_reg_notifier; + wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS; + wiphy->country_ie_pref |= NL80211_COUNTRY_IE_IGNORE_CORE; + hdd_regulatory_init_no_offload(hdd_ctx, wiphy); + + return 0; +} +#endif + +void hdd_update_regdb_offload_config(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + bool ignore_fw_reg_offload_ind = false; + + status = ucfg_mlme_get_ignore_fw_reg_offload_ind( + hdd_ctx->psoc, + &ignore_fw_reg_offload_ind); + if (!ignore_fw_reg_offload_ind) { + hdd_debug("regdb offload is based on firmware capability"); + return; + } + + hdd_debug("Ignore regdb offload Indication from FW"); + ucfg_set_ignore_fw_reg_offload_ind(hdd_ctx->psoc); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rssi_monitor.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rssi_monitor.c new file mode 100644 index 0000000000000000000000000000000000000000..06c21aa6d42db41c08eb906279c2cc13f63c275c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rssi_monitor.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_rssi_monitor.c + * + * WLAN rssi monitoring functions + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include + +/* + * define short names for the global vendor params + * used by __wlan_hdd_cfg80211_monitor_rssi() + */ +#define PARAM_MAX QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX +#define PARAM_REQUEST_ID QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID +#define PARAM_CONTROL QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL +#define PARAM_MIN_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI +#define PARAM_MAX_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI + +/** + * __wlan_hdd_cfg80211_monitor_rssi() - monitor rssi + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int +__wlan_hdd_cfg80211_monitor_rssi(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[PARAM_MAX + 1]; + struct rssi_monitor_param req; + QDF_STATUS status; + int ret; + uint32_t control; + mac_handle_t mac_handle; + static const struct nla_policy policy[PARAM_MAX + 1] = { + [PARAM_REQUEST_ID] = { .type = NLA_U32 }, + [PARAM_CONTROL] = { .type = NLA_U32 }, + [PARAM_MIN_RSSI] = { .type = NLA_S8 }, + [PARAM_MAX_RSSI] = { .type = NLA_S8 }, + }; + + hdd_enter_dev(dev); + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) { + hdd_err("invalid session id: %d", adapter->vdev_id); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + hdd_err("Not in Connected state!"); + return -ENOTSUPP; + } + + if (wlan_cfg80211_nla_parse(tb, PARAM_MAX, data, data_len, policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[PARAM_REQUEST_ID]) { + hdd_err("attr request id failed"); + return -EINVAL; + } + + if (!tb[PARAM_CONTROL]) { + hdd_err("attr control failed"); + return -EINVAL; + } + + req.request_id = nla_get_u32(tb[PARAM_REQUEST_ID]); + req.vdev_id = adapter->vdev_id; + control = nla_get_u32(tb[PARAM_CONTROL]); + + if (control == QCA_WLAN_RSSI_MONITORING_START) { + req.control = true; + if (!tb[PARAM_MIN_RSSI]) { + hdd_err("attr min rssi failed"); + return -EINVAL; + } + + if (!tb[PARAM_MAX_RSSI]) { + hdd_err("attr max rssi failed"); + return -EINVAL; + } + + req.min_rssi = nla_get_s8(tb[PARAM_MIN_RSSI]); + req.max_rssi = nla_get_s8(tb[PARAM_MAX_RSSI]); + + if (!(req.min_rssi < req.max_rssi)) { + hdd_warn("min_rssi: %d must be less than max_rssi: %d", + req.min_rssi, req.max_rssi); + return -EINVAL; + } + hdd_debug("Min_rssi: %d Max_rssi: %d", + req.min_rssi, req.max_rssi); + + } else if (control == QCA_WLAN_RSSI_MONITORING_STOP) { + req.control = false; + } else { + hdd_err("Invalid control cmd: %d", control); + return -EINVAL; + } + hdd_debug("Request Id: %u vdev id: %d Control: %d", + req.request_id, req.vdev_id, req.control); + + mac_handle = hdd_ctx->mac_handle; + status = sme_set_rssi_monitoring(mac_handle, &req); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_set_rssi_monitoring failed(err=%d)", status); + return -EINVAL; + } + + return 0; +} + +/* + * done with short names for the global vendor params + * used by __wlan_hdd_cfg80211_monitor_rssi() + */ +#undef PARAM_MAX +#undef PARAM_CONTROL +#undef PARAM_REQUEST_ID +#undef PARAM_MAX_RSSI +#undef PARAM_MIN_RSSI + +int +wlan_hdd_cfg80211_monitor_rssi(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_monitor_rssi(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +void hdd_rssi_threshold_breached(hdd_handle_t hdd_handle, + struct rssi_breach_event *data) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + struct sk_buff *skb; + + hdd_enter(); + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + if (!data) { + hdd_err("data is null"); + return; + } + + skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, + EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI_INDEX, + GFP_KERNEL); + + if (!skb) { + hdd_err("mem alloc failed"); + return; + } + + hdd_debug("Req Id: %u Current rssi: %d", + data->request_id, data->curr_rssi); + hdd_debug("Current BSSID: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(data->curr_bssid.bytes)); + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID, + data->request_id) || + nla_put(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID, + sizeof(data->curr_bssid), data->curr_bssid.bytes) || + nla_put_s8(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI, + data->curr_rssi)) { + hdd_err("nla put fail"); + goto fail; + } + + cfg80211_vendor_event(skb, GFP_KERNEL); + return; + +fail: + kfree_skb(skb); +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rssi_monitor.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rssi_monitor.h new file mode 100644 index 0000000000000000000000000000000000000000..ac621c477bf4ffcf45e4125c205ea5b2df12ac51 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rssi_monitor.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_RSSI_MONITOR_H +#define __WLAN_HDD_RSSI_MONITOR_H + +/** + * DOC: wlan_hdd_rssi_monitor_h + * + * WLAN Host Device Driver RSSI monitoring API specification + */ + +#ifdef FEATURE_RSSI_MONITOR +/** + * wlan_hdd_cfg80211_monitor_rssi() - SSR wrapper to rssi monitoring + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +int +wlan_hdd_cfg80211_monitor_rssi(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len); + +/** + * hdd_rssi_threshold_breached() - rssi breached NL event + * @hdd_handle: HDD handle + * @data: rssi breached event data + * + * This function reads the rssi breached event %data and fill in the skb with + * NL attributes and send up the NL event. + * + * Return: none + */ +void hdd_rssi_threshold_breached(hdd_handle_t hdd_handle, + struct rssi_breach_event *data); + +#define FEATURE_RSSI_MONITOR_VENDOR_EVENTS \ +[QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI_INDEX] = { \ + .vendor_id = QCA_NL80211_VENDOR_ID, \ + .subcmd = QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI \ +}, + +#define FEATURE_RSSI_MONITOR_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_monitor_rssi \ +}, + +#else /* FEATURE_RSSI_MONITOR */ +static inline +void hdd_rssi_threshold_breached(hdd_handle_t hdd_handle, + struct rssi_breach_event *data) +{ +} + +#define FEATURE_RSSI_MONITOR_VENDOR_EVENTS +#define FEATURE_RSSI_MONITOR_VENDOR_COMMANDS +#endif /* FEATURE_RSSI_MONITOR */ + +#endif /* __WLAN_HDD_RSSI_MONITOR_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rx_monitor.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rx_monitor.c new file mode 100644 index 0000000000000000000000000000000000000000..082134e59cdad8f86196bf5898263770e6321861 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rx_monitor.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "wlan_hdd_includes.h" +#include +#include +#include +#include +#include +#include +#include "wlan_hdd_rx_monitor.h" +#include "ol_txrx.h" + +/** + * hdd_rx_monitor_callback(): Callback function for receive monitor mode + * @vdev: Handle to vdev object + * @mpdu: pointer to mpdu to be delivered to os + * @rx_status: receive status + * + * Returns: None + */ +void hdd_rx_monitor_callback(ol_osif_vdev_handle context, + qdf_nbuf_t rxbuf, + void *rx_status) +{ + struct hdd_adapter *adapter; + int rxstat; + struct sk_buff *skb; + struct sk_buff *skb_next; + unsigned int cpu_index; + + qdf_assert(context); + qdf_assert(rxbuf); + + adapter = (struct hdd_adapter *)context; + if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "invalid adapter %pK", adapter); + return; + } + + cpu_index = wlan_hdd_get_cpu(); + + /* walk the chain until all are processed */ + skb = (struct sk_buff *)rxbuf; + while (skb) { + skb_next = skb->next; + skb->dev = adapter->dev; + + ++adapter->hdd_stats.tx_rx_stats.rx_packets[cpu_index]; + ++adapter->stats.rx_packets; + adapter->stats.rx_bytes += skb->len; + + /* Remove SKB from internal tracking table before submitting + * it to stack + */ + qdf_net_buf_debug_release_skb(skb); + + /* + * If this is not a last packet on the chain + * Just put packet into backlog queue, not scheduling RX sirq + */ + if (skb->next) { + rxstat = netif_rx(skb); + } else { + /* + * This is the last packet on the chain + * Scheduling rx sirq + */ + rxstat = netif_rx_ni(skb); + } + + if (NET_RX_SUCCESS == rxstat) + ++adapter-> + hdd_stats.tx_rx_stats.rx_delivered[cpu_index]; + else + ++adapter->hdd_stats.tx_rx_stats.rx_refused[cpu_index]; + + skb = skb_next; + } +} + +/** + * hdd_monitor_set_rx_monitor_cb(): Set rx monitor mode callback function + * @txrx: pointer to txrx ops + * @rx_monitor_cb: pointer to callback function + * + * Returns: None + */ +void hdd_monitor_set_rx_monitor_cb(struct ol_txrx_ops *txrx, + ol_txrx_rx_mon_fp rx_monitor_cb) +{ + txrx->rx.mon = rx_monitor_cb; +} + +/** + * hdd_enable_monitor_mode() - Enable monitor mode + * @dev: Pointer to the net_device structure + * + * This function invokes cdp interface API to enable + * monitor mode configuration on the hardware. In this + * case sends HTT messages to FW to setup hardware rings + * + * Return: 0 for success; non-zero for failure + */ +int hdd_enable_monitor_mode(struct net_device *dev) +{ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint8_t vdev_id; + + hdd_enter_dev(dev); + + vdev_id = cdp_get_mon_vdev_from_pdev(soc, OL_TXRX_PDEV_ID); + if (vdev_id < 0) + return -EINVAL; + + return cdp_set_monitor_mode(soc, vdev_id, false); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rx_monitor.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rx_monitor.h new file mode 100644 index 0000000000000000000000000000000000000000..49a8cb07b8c2afb9f0e3e2dacf16d7dd7e446fb6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_rx_monitor.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_RX_MONITOR_H +#define __WLAN_HDD_RX_MONITOR_H + +struct ol_txrx_ops; + +#if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ + defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) +void hdd_monitor_set_rx_monitor_cb(struct ol_txrx_ops *txrx, + ol_txrx_rx_mon_fp rx_monitor_cb); + +void hdd_rx_monitor_callback(ol_osif_vdev_handle vdev, + qdf_nbuf_t mpdu, + void *rx_status); + +int hdd_enable_monitor_mode(struct net_device *dev); +#else +static inline void hdd_monitor_set_rx_monitor_cb(struct ol_txrx_ops *txrx, + ol_txrx_rx_mon_fp rx_monitor_cb){ } +static inline void hdd_rx_monitor_callback(ol_osif_vdev_handle vdev, + qdf_nbuf_t mpdu, + void *rx_status){ } +static inline int hdd_enable_monitor_mode(struct net_device *dev) +{ + return 0; +} +#endif /* CONFIG_LITHIUM */ + +#endif /* __WLAN_HDD_RX_MONITOR_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sap_cond_chan_switch.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sap_cond_chan_switch.c new file mode 100644 index 0000000000000000000000000000000000000000..5bfa6a928a8c200f5f40b437d356ee9937f0afd0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sap_cond_chan_switch.c @@ -0,0 +1,555 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_sap_cond_chan_switch.c + * + * WLAN SAP conditional channel switch functions + * + */ + +#include +#include +#include +#include +#include +#include "osif_sync.h" +#include +#include +#include + +/** + * wlan_hdd_set_pre_cac_status() - Set the pre cac status + * @pre_cac_adapter: AP adapter used for pre cac + * @status: Status (true or false) + * + * Sets the status of pre cac i.e., whether the pre cac is active or not + * + * Return: Zero on success, non-zero on failure + */ +static int wlan_hdd_set_pre_cac_status(struct hdd_adapter *pre_cac_adapter, + bool status) +{ + QDF_STATUS ret; + + ret = wlan_sap_set_pre_cac_status( + WLAN_HDD_GET_SAP_CTX_PTR(pre_cac_adapter), status); + if (QDF_IS_STATUS_ERROR(ret)) + return -EINVAL; + + return 0; +} + +/** + * wlan_hdd_set_chan_before_pre_cac() - Save the channel before pre cac + * @ap_adapter: AP adapter + * @chan_before_pre_cac: Channel + * + * Saves the channel which the AP was beaconing on before moving to the pre + * cac channel. If radar is detected on the pre cac channel, this saved + * channel will be used for AP operations. + * + * Return: Zero on success, non-zero on failure + */ +static int wlan_hdd_set_chan_before_pre_cac(struct hdd_adapter *ap_adapter, + uint8_t chan_before_pre_cac) +{ + QDF_STATUS ret; + + ret = wlan_sap_set_chan_before_pre_cac( + WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), chan_before_pre_cac); + if (QDF_IS_STATUS_ERROR(ret)) + return -EINVAL; + + return 0; +} + +/** + * wlan_hdd_validate_and_get_pre_cac_ch() - Validate and get pre cac channel + * @hdd_ctx: HDD context + * @ap_adapter: AP adapter + * @chan_freq: Channel frequency requested by userspace + * @pre_cac_chan_freq: Pointer to the pre CAC channel frequency storage + * + * Validates the channel provided by userspace. If user provided channel 0, + * a valid outdoor channel must be selected from the regulatory channel. + * + * Return: Zero on success and non zero value on error + */ +static int wlan_hdd_validate_and_get_pre_cac_ch(struct hdd_context *hdd_ctx, + struct hdd_adapter *ap_adapter, + uint32_t chan_freq, + uint32_t *pre_cac_chan_freq) +{ + uint32_t i; + QDF_STATUS status; + uint32_t weight_len = 0; + uint32_t len = CFG_VALID_CHANNEL_LIST_LEN; + uint32_t freq_list[NUM_CHANNELS] = {0}; + uint8_t pcl_weights[NUM_CHANNELS] = {0}; + mac_handle_t mac_handle; + + if (!chan_freq) { + /* Channel is not obtained from PCL because PCL may not have + * the entire channel list. For example: if SAP is up on + * channel 6 and PCL is queried for the next SAP interface, + * if SCC is preferred, the PCL will contain only the channel + * 6. But, we are in need of a DFS channel. So, going with the + * first channel from the valid channel list. + */ + status = policy_mgr_get_valid_chans(hdd_ctx->psoc, + freq_list, &len); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get channel list"); + return -EINVAL; + } + policy_mgr_update_with_safe_channel_list(hdd_ctx->psoc, + freq_list, &len, + pcl_weights, + weight_len); + for (i = 0; i < len; i++) { + if (wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, + freq_list[i])) { + *pre_cac_chan_freq = freq_list[i]; + break; + } + } + if (*pre_cac_chan_freq == 0) { + hdd_err("unable to find outdoor channel"); + return -EINVAL; + } + } else { + /* Only when driver selects a channel, check is done for + * unnsafe and NOL channels. When user provides a fixed channel + * the user is expected to take care of this. + */ + mac_handle = hdd_ctx->mac_handle; + if (!sme_is_channel_valid(mac_handle, chan_freq) || + !wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, chan_freq)) { + hdd_err("Invalid channel for pre cac:%d", chan_freq); + return -EINVAL; + } + *pre_cac_chan_freq = chan_freq; + } + hdd_debug("selected pre cac channel:%d", *pre_cac_chan_freq); + return 0; +} + +/** + * __wlan_hdd_request_pre_cac() - Start pre CAC in the driver + * @hdd_ctx: the HDD context to operate against + * @chan_freq: Channel frequency option provided by userspace + * @out_adapter: out parameter for the newly created pre-cac adapter + * + * Sets the driver to the required hardware mode and start an adapter for + * pre CAC which will mimic an AP. + * + * Return: Zero on success, non-zero value on error + */ +static int __wlan_hdd_request_pre_cac(struct hdd_context *hdd_ctx, + uint32_t chan_freq, + struct hdd_adapter **out_adapter) +{ + uint8_t *mac_addr; + uint32_t pre_cac_chan_freq = 0; + int ret; + struct hdd_adapter *ap_adapter, *pre_cac_adapter; + struct hdd_ap_ctx *hdd_ap_ctx; + QDF_STATUS status; + struct wiphy *wiphy; + struct net_device *dev; + struct cfg80211_chan_def chandef; + enum nl80211_channel_type channel_type; + struct ieee80211_channel *chan; + mac_handle_t mac_handle; + bool val; + + if (!policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc)) { + hdd_debug("Pre CAC is not supported on non-dbs platforms"); + return -EINVAL; + } + + if (policy_mgr_get_connection_count(hdd_ctx->psoc) > 1) { + hdd_err("pre cac not allowed in concurrency"); + return -EINVAL; + } + + ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); + if (!ap_adapter) { + hdd_err("unable to get SAP adapter"); + return -EINVAL; + } + + mac_handle = hdd_ctx->mac_handle; + val = wlan_sap_is_pre_cac_active(mac_handle); + if (val) { + hdd_err("pre cac is already in progress"); + return -EINVAL; + } + + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); + if (!hdd_ap_ctx) { + hdd_err("SAP context is NULL"); + return -EINVAL; + } + + if (wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, + hdd_ap_ctx->operating_chan_freq)) { + hdd_err("SAP is already on DFS channel:%d", + hdd_ap_ctx->operating_chan_freq); + return -EINVAL; + } + + if (!WLAN_REG_IS_24GHZ_CH_FREQ(hdd_ap_ctx->operating_chan_freq)) { + hdd_err("pre CAC alllowed only when SAP is in 2.4GHz:%d", + hdd_ap_ctx->operating_chan_freq); + return -EINVAL; + } + + mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, QDF_SAP_MODE); + if (!mac_addr) { + hdd_err("can't add virtual intf: Not getting valid mac addr"); + return -EINVAL; + } + + hdd_debug("channel: %d", chan_freq); + + ret = wlan_hdd_validate_and_get_pre_cac_ch( + hdd_ctx, ap_adapter, chan_freq, &pre_cac_chan_freq); + if (ret != 0) { + hdd_err("can't validate pre-cac channel"); + goto release_intf_addr_and_return_failure; + } + + hdd_debug("starting pre cac SAP adapter"); + + /* Starting a SAP adapter: + * Instead of opening an adapter, we could just do a SME open session + * for AP type. But, start BSS would still need an adapter. + * So, this option is not taken. + * + * hdd open adapter is going to register this precac interface with + * user space. This interface though exposed to user space will be in + * DOWN state. Consideration was done to avoid this registration to the + * user space. But, as part of SAP operations multiple events are sent + * to user space. Some of these events received from unregistered + * interface was causing crashes. So, retaining the registration. + * + * So, this interface would remain registered and will remain in DOWN + * state for the CAC duration. We will add notes in the feature + * announcement to not use this temporary interface for any activity + * from user space. + */ + pre_cac_adapter = hdd_open_adapter(hdd_ctx, QDF_SAP_MODE, "precac%d", + mac_addr, NET_NAME_UNKNOWN, true); + if (!pre_cac_adapter) { + hdd_err("error opening the pre cac adapter"); + goto release_intf_addr_and_return_failure; + } + + /* + * This interface is internally created by the driver. So, no interface + * up comes for this interface from user space and hence starting + * the adapter internally. + */ + if (hdd_start_adapter(pre_cac_adapter)) { + hdd_err("error starting the pre cac adapter"); + goto close_pre_cac_adapter; + } + + hdd_debug("preparing for start ap/bss on the pre cac adapter"); + + wiphy = hdd_ctx->wiphy; + dev = pre_cac_adapter->dev; + + /* Since this is only a dummy interface lets us use the IEs from the + * other active SAP interface. In regular scenarios, these IEs would + * come from the user space entity + */ + pre_cac_adapter->session.ap.beacon = qdf_mem_malloc( + sizeof(*ap_adapter->session.ap.beacon)); + if (!pre_cac_adapter->session.ap.beacon) { + hdd_err("failed to alloc mem for beacon"); + goto stop_close_pre_cac_adapter; + } + qdf_mem_copy(pre_cac_adapter->session.ap.beacon, + ap_adapter->session.ap.beacon, + sizeof(*pre_cac_adapter->session.ap.beacon)); + pre_cac_adapter->session.ap.sap_config.ch_width_orig = + ap_adapter->session.ap.sap_config.ch_width_orig; + pre_cac_adapter->session.ap.sap_config.authType = + ap_adapter->session.ap.sap_config.authType; + + /* Premise is that on moving from 2.4GHz to 5GHz, the SAP will continue + * to operate on the same bandwidth as that of the 2.4GHz operations. + * Only bandwidths 20MHz/40MHz are possible on 2.4GHz band. + */ + switch (ap_adapter->session.ap.sap_config.ch_width_orig) { + case CH_WIDTH_20MHZ: + channel_type = NL80211_CHAN_HT20; + break; + case CH_WIDTH_40MHZ: + if (ap_adapter->session.ap.sap_config.sec_ch_freq > + ap_adapter->session.ap.sap_config.chan_freq) + channel_type = NL80211_CHAN_HT40PLUS; + else + channel_type = NL80211_CHAN_HT40MINUS; + break; + default: + channel_type = NL80211_CHAN_NO_HT; + break; + } + + chan = ieee80211_get_channel(wiphy, pre_cac_chan_freq); + if (!chan) { + hdd_err("channel converion failed"); + goto stop_close_pre_cac_adapter; + } + cfg80211_chandef_create(&chandef, chan, channel_type); + + hdd_debug("orig width:%d channel_type:%d freq:%d", + ap_adapter->session.ap.sap_config.ch_width_orig, + channel_type, pre_cac_chan_freq); + /* + * Doing update after opening and starting pre-cac adapter will make + * sure that driver won't do hardware mode change if there are any + * initial hick-ups or issues in pre-cac adapter's configuration. + * Since current SAP is in 2.4GHz and pre CAC channel is in 5GHz, this + * connection update should result in DBS mode + */ + status = policy_mgr_update_and_wait_for_connection_update( + hdd_ctx->psoc, ap_adapter->vdev_id, pre_cac_chan_freq, + POLICY_MGR_UPDATE_REASON_PRE_CAC); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("error in moving to DBS mode"); + goto stop_close_pre_cac_adapter; + } + + ret = wlan_hdd_set_channel(wiphy, dev, &chandef, channel_type); + if (ret != 0) { + hdd_err("failed to set channel"); + goto stop_close_pre_cac_adapter; + } + + status = wlan_hdd_cfg80211_start_bss(pre_cac_adapter, NULL, + PRE_CAC_SSID, qdf_str_len(PRE_CAC_SSID), + NL80211_HIDDEN_SSID_NOT_IN_USE, false); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("start bss failed"); + goto stop_close_pre_cac_adapter; + } + + /* + * The pre cac status is set here. But, it would not be reset explicitly + * anywhere, since after the pre cac success/failure, the pre cac + * adapter itself would be removed. + */ + ret = wlan_hdd_set_pre_cac_status(pre_cac_adapter, true); + if (ret != 0) { + hdd_err("failed to set pre cac status"); + goto stop_close_pre_cac_adapter; + } + + ret = wlan_hdd_set_chan_before_pre_cac( + ap_adapter, + wlan_reg_freq_to_chan( + hdd_ctx->pdev, + hdd_ap_ctx->operating_chan_freq)); + if (ret != 0) { + hdd_err("failed to set channel before pre cac"); + goto stop_close_pre_cac_adapter; + } + + ap_adapter->pre_cac_freq = pre_cac_chan_freq; + + *out_adapter = pre_cac_adapter; + + return 0; + +stop_close_pre_cac_adapter: + hdd_stop_adapter(hdd_ctx, pre_cac_adapter); + qdf_mem_free(pre_cac_adapter->session.ap.beacon); + pre_cac_adapter->session.ap.beacon = NULL; +close_pre_cac_adapter: + hdd_close_adapter(hdd_ctx, pre_cac_adapter, false); +release_intf_addr_and_return_failure: + /* + * Release the interface address as the adapter + * failed to start, if you don't release then next + * adapter which is trying to come wouldn't get valid + * mac address. Remember we have limited pool of mac addresses + */ + wlan_hdd_release_intf_addr(hdd_ctx, mac_addr); + return -EINVAL; +} + +int wlan_hdd_request_pre_cac(struct hdd_context *hdd_ctx, uint32_t chan_freq) +{ + struct hdd_adapter *adapter; + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_create_and_trans(hdd_ctx->parent_dev, + &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_request_pre_cac(hdd_ctx, chan_freq, &adapter); + if (errno) + goto destroy_sync; + + osif_vdev_sync_register(adapter->dev, vdev_sync); + osif_vdev_sync_trans_stop(vdev_sync); + + return 0; + +destroy_sync: + osif_vdev_sync_trans_stop(vdev_sync); + osif_vdev_sync_destroy(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_conditional_chan_switch() - Conditional channel switch + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Processes the conditional channel switch request and invokes the helper + * APIs to process the channel switch request. + * + * Return: 0 on success, negative errno on failure + */ +static int +__wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter; + struct nlattr + *tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX + 1]; + uint32_t freq_len, i; + uint32_t *freq; + bool is_dfs_mode_enabled = false; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_STATUS_SUCCESS != ucfg_mlme_get_dfs_master_capability( + hdd_ctx->psoc, &is_dfs_mode_enabled)) { + hdd_err("Failed to get dfs master capability"); + return -EINVAL; + } + + if (!is_dfs_mode_enabled) { + hdd_err("DFS master capability is not present in the driver"); + return -EINVAL; + } + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + if (adapter->device_mode != QDF_SAP_MODE) { + hdd_err("Invalid device mode %d", adapter->device_mode); + return -EINVAL; + } + + /* + * audit note: it is ok to pass a NULL policy here since only + * one attribute is parsed which is array of frequencies and + * it is explicitly validated for both under read and over read + */ + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX, + data, data_len, NULL)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]) { + hdd_err("Frequency list is missing"); + return -EINVAL; + } + + freq_len = nla_len( + tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST])/ + sizeof(uint32_t); + + if (freq_len > NUM_CHANNELS) { + hdd_err("insufficient space to hold channels"); + return -ENOMEM; + } + + hdd_debug("freq_len=%d", freq_len); + + freq = nla_data( + tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]); + + for (i = 0; i < freq_len; i++) + hdd_debug("freq[%d]=%d", i, freq[i]); + + /* + * The input frequency list from user space is designed to be a + * priority based frequency list. This is only to accommodate any + * future request. But, current requirement is only to perform CAC + * on a single channel. So, the first entry from the list is picked. + * + * If channel is zero, any channel in the available outdoor regulatory + * domain will be selected. + */ + ret = wlan_hdd_request_pre_cac(hdd_ctx, freq[0]); + if (ret) { + hdd_err("pre cac request failed with reason:%d", ret); + return ret; + } + + return 0; +} + +int wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_conditional_chan_switch(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sap_cond_chan_switch.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sap_cond_chan_switch.h new file mode 100644 index 0000000000000000000000000000000000000000..1ea89eb1e00c2846a55cbc905a32faa7a26701f0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sap_cond_chan_switch.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_SAP_COND_CHAN_SWITCH_H +#define __WLAN_HDD_SAP_COND_CHAN_SWITCH_H + +/** + * DOC: wlan_hdd_sap_cond_chan_switch_h + * + * WLAN Host Device Driver SAP conditional channel switch API specification + */ + +#ifdef FEATURE_SAP_COND_CHAN_SWITCH +/** + * wlan_hdd_cfg80211_conditional_chan_switch() - SAP conditional channel switch + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Invokes internal API __wlan_hdd_cfg80211_conditional_chan_switch() + * to process the conditional channel switch request. + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_SAP_COND_CHAN_SWITCH_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = \ + QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_conditional_chan_switch \ +}, +#else /* FEATURE_SAP_COND_CHAN_SWITCH */ +#define FEATURE_SAP_COND_CHAN_SWITCH_VENDOR_COMMANDS +#endif /* FEATURE_SAP_COND_CHAN_SWITCH */ + +#endif /* __WLAN_HDD_SAP_COND_CHAN_SWITCH_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sar_limits.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sar_limits.c new file mode 100644 index 0000000000000000000000000000000000000000..f29df9023d9097b5844b6b906f14712cc8a21a82 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sar_limits.c @@ -0,0 +1,633 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_vendor_sar_limits.c + * + * WLAN SAR limits functions + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include + +#define WLAN_WAIT_TIME_SAR 5000 +/** + * hdd_sar_context - hdd sar context + * @event: sar limit event + */ +struct hdd_sar_context { + struct sar_limit_event event; +}; + +static u32 hdd_sar_wmi_to_nl_enable(uint32_t wmi_value) +{ + switch (wmi_value) { + default: + case WMI_SAR_FEATURE_OFF: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE; + case WMI_SAR_FEATURE_ON_SET_0: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0; + case WMI_SAR_FEATURE_ON_SET_1: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1; + case WMI_SAR_FEATURE_ON_SET_2: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2; + case WMI_SAR_FEATURE_ON_SET_3: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3; + case WMI_SAR_FEATURE_ON_SET_4: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4; + case WMI_SAR_FEATURE_ON_USER_DEFINED: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER; + case WMI_SAR_FEATURE_ON_SAR_V2_0: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0; + } +} + +static u32 hdd_sar_wmi_to_nl_band(uint32_t wmi_value) +{ + switch (wmi_value) { + default: + case WMI_SAR_2G_ID: + return HDD_NL80211_BAND_2GHZ; + case WMI_SAR_5G_ID: + return HDD_NL80211_BAND_5GHZ; + } +} + +static u32 hdd_sar_wmi_to_nl_modulation(uint32_t wmi_value) +{ + switch (wmi_value) { + default: + case WMI_SAR_MOD_CCK: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK; + case WMI_SAR_MOD_OFDM: + return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM; + } +} + +/** + * hdd_sar_cb () - sar response message handler + * @cookie: hdd request cookie + * @event: sar response event + * + * Return: none + */ +static void hdd_sar_cb(void *cookie, + struct sar_limit_event *event) +{ + struct osif_request *request; + struct hdd_sar_context *context; + + hdd_enter(); + + if (!event) { + hdd_err("response is NULL"); + return; + } + + request = osif_request_get(cookie); + if (!request) { + hdd_debug("Obsolete request"); + return; + } + + context = osif_request_priv(request); + context->event = *event; + osif_request_complete(request); + osif_request_put(request); + + hdd_exit(); +} + +static uint32_t hdd_sar_get_response_len(const struct sar_limit_event *event) +{ + uint32_t len; + uint32_t row_len; + + len = NLMSG_HDRLEN; + /* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE */ + len += NLA_HDRLEN + sizeof(u32); + /* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS */ + len += NLA_HDRLEN + sizeof(u32); + + /* nest */ + row_len = NLA_HDRLEN; + /* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND */ + row_len += NLA_HDRLEN + sizeof(u32); + /* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN */ + row_len += NLA_HDRLEN + sizeof(u32); + /* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION */ + row_len += NLA_HDRLEN + sizeof(u32); + /* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT */ + row_len += NLA_HDRLEN + sizeof(u32); + + /* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC */ + len += NLA_HDRLEN + (row_len * event->num_limit_rows); + + return len; +} + +static int hdd_sar_fill_response(struct sk_buff *skb, + const struct sar_limit_event *event) +{ + int errno; + u32 value; + u32 attr; + struct nlattr *nla_spec_attr; + struct nlattr *nla_row_attr; + uint32_t row; + const struct sar_limit_event_row *event_row; + + attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE; + value = hdd_sar_wmi_to_nl_enable(event->sar_enable); + errno = nla_put_u32(skb, attr, value); + if (errno) + return errno; + + attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS; + value = event->num_limit_rows; + errno = nla_put_u32(skb, attr, value); + if (errno) + return errno; + + attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC; + nla_spec_attr = nla_nest_start(skb, attr); + if (!nla_spec_attr) + return -EINVAL; + + for (row = 0, event_row = event->sar_limit_row; + row < event->num_limit_rows; + row++, event_row++) { + nla_row_attr = nla_nest_start(skb, attr); + if (!nla_row_attr) + return -EINVAL; + + attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND; + value = hdd_sar_wmi_to_nl_band(event_row->band_id); + errno = nla_put_u32(skb, attr, value); + if (errno) + return errno; + + attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN; + value = event_row->chain_id; + errno = nla_put_u32(skb, attr, value); + if (errno) + return errno; + + attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION; + value = hdd_sar_wmi_to_nl_modulation(event_row->mod_id); + errno = nla_put_u32(skb, attr, value); + if (errno) + return errno; + + attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT; + value = event_row->limit_value; + errno = nla_put_u32(skb, attr, value); + if (errno) + return errno; + + nla_nest_end(skb, nla_row_attr); + } + nla_nest_end(skb, nla_spec_attr); + + return 0; +} + +static int hdd_sar_send_response(struct wiphy *wiphy, + const struct sar_limit_event *event) +{ + uint32_t len; + struct sk_buff *skb; + int errno; + + len = hdd_sar_get_response_len(event); + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + errno = hdd_sar_fill_response(skb, event); + if (errno) { + kfree_skb(skb); + return errno; + } + + return cfg80211_vendor_cmd_reply(skb); +} + +/** + * __wlan_hdd_get_sar_power_limits() - Get SAR power limits + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * This function is used to retrieve Specific Absorption Rate limit specs. + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_get_sar_power_limits(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct osif_request *request; + struct hdd_sar_context *context; + mac_handle_t mac_handle; + void *cookie; + QDF_STATUS status; + int ret; + static const struct osif_request_params params = { + .priv_size = sizeof(*context), + .timeout_ms = WLAN_WAIT_TIME_SAR, + }; + + hdd_enter(); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + + cookie = osif_request_cookie(request); + + mac_handle = hdd_ctx->mac_handle; + status = sme_get_sar_power_limits(mac_handle, hdd_sar_cb, cookie); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Unable to post sar message"); + ret = -EINVAL; + goto cleanup; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("Target response timed out"); + goto cleanup; + } + + context = osif_request_priv(request); + ret = hdd_sar_send_response(wiphy, &context->event); + +cleanup: + osif_request_put(request); + + return ret; +} + +int wlan_hdd_cfg80211_get_sar_power_limits(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_get_sar_power_limits(wiphy, wdev, data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +#ifdef SAR_SAFETY_FEATURE +void hdd_disable_sar(struct hdd_context *hdd_ctx) +{ + struct sar_limit_cmd_params *sar_limit_cmd; + struct sar_limit_cmd_row *row; + QDF_STATUS status; + + if (hdd_ctx->sar_version != SAR_VERSION_2) { + hdd_nofl_debug("FW SAR version: %d", hdd_ctx->sar_version); + return; + } + + sar_limit_cmd = qdf_mem_malloc(sizeof(struct sar_limit_cmd_params)); + if (!sar_limit_cmd) + return; + + /* + * Need two rows to hold the per-chain V2 power index + */ + row = qdf_mem_malloc(2 * sizeof(*row)); + if (!row) + goto config_sar_failed; + + sar_limit_cmd->sar_enable = WMI_SAR_FEATURE_OFF; + sar_limit_cmd->commit_limits = 1; + sar_limit_cmd->num_limit_rows = 2; + sar_limit_cmd->sar_limit_row_list = row; + row[0].limit_value = 0; + row[1].limit_value = 0; + row[0].chain_id = 0; + row[1].chain_id = 1; + row[0].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK; + row[1].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK; + + hdd_nofl_debug("Disable the SAR limit index for both the chains"); + + status = sme_set_sar_power_limits(hdd_ctx->mac_handle, sar_limit_cmd); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_nofl_err("Failed to set sar power limits"); + goto config_sar_failed; + } + + /* After SSR, the SAR configuration is lost. As SSR is hidden from + * userland, this command will not come from userspace after a SSR. To + * restore this configuration, save this in hdd context and restore + * after re-init. + */ + hdd_store_sar_config(hdd_ctx, sar_limit_cmd); + return; + +config_sar_failed: + + if (sar_limit_cmd) { + qdf_mem_free(sar_limit_cmd->sar_limit_row_list); + qdf_mem_free(sar_limit_cmd); + } +} + +void hdd_configure_sar_index(struct hdd_context *hdd_ctx, uint32_t sar_index) +{ + struct sar_limit_cmd_params *sar_limit_cmd; + struct sar_limit_cmd_row *row; + QDF_STATUS status; + + if (hdd_ctx->sar_version != SAR_VERSION_2) { + hdd_nofl_debug("FW SAR version: %d", hdd_ctx->sar_version); + return; + } + + sar_limit_cmd = qdf_mem_malloc(sizeof(struct sar_limit_cmd_params)); + if (!sar_limit_cmd) + return; + + /* + * Need two rows to hold the per-chain V2 power index + */ + row = qdf_mem_malloc(2 * sizeof(*row)); + if (!row) + goto config_sar_failed; + + sar_limit_cmd->sar_enable = WMI_SAR_FEATURE_ON_SAR_V2_0; + sar_limit_cmd->commit_limits = 1; + sar_limit_cmd->num_limit_rows = 2; + sar_limit_cmd->sar_limit_row_list = row; + row[0].limit_value = sar_index; + row[1].limit_value = sar_index; + row[0].chain_id = 0; + row[1].chain_id = 1; + row[0].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK; + row[1].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK; + + hdd_nofl_debug("Configure POW_Limit Index: %d for both the chains", + row->limit_value); + + status = sme_set_sar_power_limits(hdd_ctx->mac_handle, sar_limit_cmd); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_nofl_err("Failed to set sar power limits"); + goto config_sar_failed; + } + + /* + * After SSR, the SAR configuration is lost. As SSR is hidden from + * userland, this command will not come from userspace after a SSR. To + * restore this configuration, save this in hdd context and restore + * after re-init. + */ + hdd_store_sar_config(hdd_ctx, sar_limit_cmd); + return; + +config_sar_failed: + + if (sar_limit_cmd) { + qdf_mem_free(sar_limit_cmd->sar_limit_row_list); + qdf_mem_free(sar_limit_cmd); + } +} + +void hdd_configure_sar_sleep_index(struct hdd_context *hdd_ctx) +{ + if (!hdd_ctx->config->enable_sar_safety) + return; + + if (hdd_ctx->config->config_sar_safety_sleep_index) { + hdd_nofl_debug("Configure SAR sleep index %d", + hdd_ctx->config->sar_safety_sleep_index); + hdd_configure_sar_index( + hdd_ctx, + hdd_ctx->config->sar_safety_sleep_index); + } else { + hdd_nofl_debug("Disable SAR"); + hdd_disable_sar(hdd_ctx); + } +} + +void hdd_configure_sar_resume_index(struct hdd_context *hdd_ctx) +{ + if (!hdd_ctx->config->enable_sar_safety) + return; + + hdd_nofl_debug("Configure SAR safety index %d on wlan resume", + hdd_ctx->config->sar_safety_index); + hdd_configure_sar_index(hdd_ctx, + hdd_ctx->config->sar_safety_index); +} + +static void hdd_send_sar_unsolicited_event(struct hdd_context *hdd_ctx) +{ + struct sk_buff *vendor_event; + uint32_t len; + + if (!hdd_ctx) { + hdd_err_rl("hdd context is null"); + return; + } + + len = NLMSG_HDRLEN; + vendor_event = + cfg80211_vendor_event_alloc( + hdd_ctx->wiphy, NULL, len, + QCA_NL80211_VENDOR_SUBCMD_REQUEST_SAR_LIMITS_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); +} + +static void hdd_sar_unsolicited_timer_cb(void *user_data) +{ + struct hdd_context *hdd_ctx = (struct hdd_context *)user_data; + uint8_t i = 0; + QDF_STATUS status; + + hdd_nofl_debug("Sar unsolicited timer expired"); + + qdf_atomic_set(&hdd_ctx->sar_safety_req_resp_event_in_progress, 1); + + for (i = 0; i < hdd_ctx->config->sar_safety_req_resp_retry; i++) { + qdf_event_reset(&hdd_ctx->sar_safety_req_resp_event); + hdd_send_sar_unsolicited_event(hdd_ctx); + status = qdf_wait_for_event_completion( + &hdd_ctx->sar_safety_req_resp_event, + hdd_ctx->config->sar_safety_req_resp_timeout); + if (QDF_IS_STATUS_SUCCESS(status)) + break; + } + qdf_atomic_set(&hdd_ctx->sar_safety_req_resp_event_in_progress, 0); + + if (i >= hdd_ctx->config->sar_safety_req_resp_retry) + hdd_configure_sar_index(hdd_ctx, + hdd_ctx->config->sar_safety_index); +} + +static void hdd_sar_safety_timer_cb(void *user_data) +{ + struct hdd_context *hdd_ctx = (struct hdd_context *)user_data; + + hdd_nofl_debug("Sar safety timer expires"); + hdd_configure_sar_index(hdd_ctx, hdd_ctx->config->sar_safety_index); +} + +void wlan_hdd_sar_unsolicited_timer_start(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + if (!hdd_ctx->config->enable_sar_safety) + return; + + if (qdf_atomic_read( + &hdd_ctx->sar_safety_req_resp_event_in_progress) > 0) + return; + + if (QDF_TIMER_STATE_RUNNING != + qdf_mc_timer_get_current_state( + &hdd_ctx->sar_safety_unsolicited_timer)) { + status = qdf_mc_timer_start( + &hdd_ctx->sar_safety_unsolicited_timer, + hdd_ctx->config->sar_safety_unsolicited_timeout); + + if (QDF_IS_STATUS_SUCCESS(status)) + hdd_nofl_debug("sar unsolicited timer started"); + } +} + +void wlan_hdd_sar_timers_reset(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + if (!hdd_ctx->config->enable_sar_safety) + return; + + if (hdd_ctx->sar_version != SAR_VERSION_2) + return; + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&hdd_ctx->sar_safety_timer)) { + status = qdf_mc_timer_stop(&hdd_ctx->sar_safety_timer); + if (QDF_IS_STATUS_SUCCESS(status)) + hdd_nofl_debug("sar safety timer stopped"); + } + + status = qdf_mc_timer_start(&hdd_ctx->sar_safety_timer, + hdd_ctx->config->sar_safety_timeout); + if (QDF_IS_STATUS_SUCCESS(status)) + hdd_nofl_debug("sar safety timer started"); + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state( + &hdd_ctx->sar_safety_unsolicited_timer)) { + status = qdf_mc_timer_stop( + &hdd_ctx->sar_safety_unsolicited_timer); + if (QDF_IS_STATUS_SUCCESS(status)) + hdd_nofl_debug("sar unsolicited timer stopped"); + } + + qdf_event_set(&hdd_ctx->sar_safety_req_resp_event); +} + +void wlan_hdd_sar_timers_init(struct hdd_context *hdd_ctx) +{ + if (!hdd_ctx->config->enable_sar_safety) + return; + + hdd_enter(); + + qdf_mc_timer_init(&hdd_ctx->sar_safety_timer, QDF_TIMER_TYPE_SW, + hdd_sar_safety_timer_cb, hdd_ctx); + + qdf_mc_timer_init(&hdd_ctx->sar_safety_unsolicited_timer, + QDF_TIMER_TYPE_SW, + hdd_sar_unsolicited_timer_cb, hdd_ctx); + + qdf_atomic_init(&hdd_ctx->sar_safety_req_resp_event_in_progress); + qdf_event_create(&hdd_ctx->sar_safety_req_resp_event); + + hdd_exit(); +} + +void wlan_hdd_sar_timers_deinit(struct hdd_context *hdd_ctx) +{ + if (!hdd_ctx->config->enable_sar_safety) + return; + + hdd_enter(); + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&hdd_ctx->sar_safety_timer)) + qdf_mc_timer_stop(&hdd_ctx->sar_safety_timer); + + qdf_mc_timer_destroy(&hdd_ctx->sar_safety_timer); + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state( + &hdd_ctx->sar_safety_unsolicited_timer)) + qdf_mc_timer_stop(&hdd_ctx->sar_safety_unsolicited_timer); + + qdf_mc_timer_destroy(&hdd_ctx->sar_safety_unsolicited_timer); + + qdf_event_destroy(&hdd_ctx->sar_safety_req_resp_event); + + hdd_exit(); +} +#endif + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sar_limits.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sar_limits.h new file mode 100644 index 0000000000000000000000000000000000000000..40180047e614edae1854349c3fce91db9a40ccae --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sar_limits.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_SAR_LIMITS_H +#define __WLAN_HDD_SAR_LIMITS_H + +/** + * DOC: wlan_hdd_sar_limits_h + * + * WLAN Host Device Driver SAR limits API specification + */ + +#if defined(FEATURE_SAR_LIMITS) && defined(SAR_SAFETY_FEATURE) +/** + * wlan_hdd_sar_unsolicited_timer_start() - Start SAR unsolicited timer + * @hdd_ctx: Pointer to HDD context + * + * This function checks the state of the sar unsolicited timer, if the + * sar_unsolicited_timer is not runnig, it starts the timer. + * + * Return: None + */ +void wlan_hdd_sar_unsolicited_timer_start(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_sar_safety_timer_reset() - Reset SAR sefety timer + * @hdd_ctx: Pointer to HDD context + * + * This function checks the state of the sar safety timer, if the + * sar_safety_timer is not runnig, it starts the timer else it stops + * the timer and start the timer again. + * + * Return: None + */ +void wlan_hdd_sar_timers_reset(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_sar_timers_init() - Initialize SAR timers + * @hdd_ctx: Pointer to HDD context + * + * This function initializes sar timers. + * Return: None + */ +void wlan_hdd_sar_timers_init(struct hdd_context *hdd_ctx); + +/** + * wlan_hdd_sar_timers_deinit() - De-initialize SAR timers + * @hdd_ctx: Pointer to HDD context + * + * This function de-initializes sar timers. + * Return: None + */ +void wlan_hdd_sar_timers_deinit(struct hdd_context *hdd_ctx); + +/** + * hdd_configure_sar_index() - configures SAR index to the FW + * @hdd_ctx: Pointer to HDD context + * @sar_index: sar index which needs to be configured to FW + * + * This function configures SAR power index on both the chains + * for SAR version2 + * + * Return: None + */ +void hdd_configure_sar_index(struct hdd_context *hdd_ctx, uint32_t sar_index); + +/** + * hdd_disable_sar() - Disable SAR feature to FW + * @hdd_ctx: Pointer to HDD context + * + * This function Disables SAR power index on both the chains + * + * Return: None + */ +void hdd_disable_sar(struct hdd_context *hdd_ctx); + +/** + * hdd_configure_sar_sleep_index() - Configure SAR sleep index to FW + * @hdd_ctx: Pointer to HDD context + * + * This function configures SAR sleep index on both the chains + * + * Return: None + */ +void hdd_configure_sar_sleep_index(struct hdd_context *hdd_ctx); + +/** + * hdd_configure_sar_resume_index() - Configure SAR resume index to FW + * @hdd_ctx: Pointer to HDD context + * + * This function configures SAR resume index on both the chains + * + * Return: None + */ +void hdd_configure_sar_resume_index(struct hdd_context *hdd_ctx); + +#else +static inline void wlan_hdd_sar_unsolicited_timer_start( + struct hdd_context *hdd_ctx) +{ +} + +static inline void wlan_hdd_sar_timers_reset(struct hdd_context *hdd_ctx) +{ +} + +static inline void wlan_hdd_sar_timers_init(struct hdd_context *hdd_ctx) +{ +} + +static inline void wlan_hdd_sar_timers_deinit(struct hdd_context *hdd_ctx) +{ +} + +static inline void hdd_configure_sar_index(struct hdd_context *hdd_ctx, + uint32_t sar_index) +{ +} + +static inline void hdd_disable_sar(struct hdd_context *hdd_ctx) +{ +} + +static inline void hdd_configure_sar_sleep_index(struct hdd_context *hdd_ctx) +{ +} + +static inline void hdd_configure_sar_resume_index(struct hdd_context *hdd_ctx) +{ +} + +#endif + +#ifdef FEATURE_SAR_LIMITS +/** + * wlan_hdd_cfg80211_get_sar_power_limits() - Get SAR power limits + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Wrapper function of __wlan_hdd_cfg80211_get_sar_power_limits() + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_get_sar_power_limits(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_SAR_LIMITS_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_get_sar_power_limits \ +}, +#else /* FEATURE_SAR_LIMITS */ +#define FEATURE_SAR_LIMITS_VENDOR_COMMANDS +#endif /* FEATURE_SAR_LIMITS */ + +#endif /* __WLAN_HDD_SAR_LIMITS_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_scan.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_scan.c new file mode 100644 index 0000000000000000000000000000000000000000..1b427fdcc443f1d92005076036c7cf9ceee87099 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_scan.c @@ -0,0 +1,1598 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_scan.c + * + * WLAN Host Device Driver scan implementation + */ + +#include +#include + +#include "wlan_hdd_includes.h" +#include "cds_api.h" +#include "cds_api.h" +#include "ani_global.h" +#include "dot11f.h" +#include "cds_sched.h" +#include "osif_sync.h" +#include "wlan_hdd_p2p.h" +#include "wlan_hdd_trace.h" +#include "wlan_hdd_scan.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_hdd_power.h" +#include "wma_api.h" +#include "cds_utils.h" +#include "wlan_p2p_ucfg_api.h" +#include "cfg_ucfg_api.h" + +#include +#include +#include "wlan_utility.h" +#include "wlan_hdd_object_manager.h" +#include "nan_ucfg_api.h" + +#define SCAN_DONE_EVENT_BUF_SIZE 4096 +#define RATE_MASK 0x7f + +/** + * hdd_vendor_scan_callback() - Scan completed callback event + * @hddctx: HDD context + * @req : Scan request + * @aborted : true scan aborted false scan success + * + * This function sends scan completed callback event to NL. + * + * Return: none + */ +static void hdd_vendor_scan_callback(struct hdd_adapter *adapter, + struct cfg80211_scan_request *req, + bool aborted) +{ + struct hdd_context *hddctx = WLAN_HDD_GET_CTX(adapter); + struct sk_buff *skb; + struct nlattr *attr; + int i; + uint8_t scan_status; + uint64_t cookie; + + hdd_enter(); + + if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { + hdd_err("Invalid adapter magic"); + qdf_mem_free(req); + return; + } + skb = cfg80211_vendor_event_alloc(hddctx->wiphy, &(adapter->wdev), + SCAN_DONE_EVENT_BUF_SIZE + 4 + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE_INDEX, + GFP_KERNEL); + + if (!skb) { + hdd_err("skb alloc failed"); + qdf_mem_free(req); + return; + } + + cookie = (uintptr_t)req; + attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS); + if (!attr) + goto nla_put_failure; + for (i = 0; i < req->n_ssids; i++) { + if (nla_put(skb, i, req->ssids[i].ssid_len, + req->ssids[i].ssid)) { + hdd_err("Failed to add ssid"); + goto nla_put_failure; + } + } + nla_nest_end(skb, attr); + attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES); + if (!attr) + goto nla_put_failure; + for (i = 0; i < req->n_channels; i++) { + if (nla_put_u32(skb, i, req->channels[i]->center_freq)) { + hdd_err("Failed to add channel"); + goto nla_put_failure; + } + } + nla_nest_end(skb, attr); + + if (req->ie && + nla_put(skb, QCA_WLAN_VENDOR_ATTR_SCAN_IE, req->ie_len, + req->ie)) { + hdd_err("Failed to add scan ie"); + goto nla_put_failure; + } + if (req->flags && + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, req->flags)) { + hdd_err("Failed to add scan flags"); + goto nla_put_failure; + } + if (hdd_wlan_nla_put_u64(skb, + QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, + cookie)) { + hdd_err("Failed to add scan cookie"); + goto nla_put_failure; + } + scan_status = (aborted == true) ? VENDOR_SCAN_STATUS_ABORTED : + VENDOR_SCAN_STATUS_NEW_RESULTS; + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_SCAN_STATUS, scan_status)) { + hdd_err("Failed to add scan staus"); + goto nla_put_failure; + } + cfg80211_vendor_event(skb, GFP_KERNEL); + hdd_info("scan complete event sent to NL"); + qdf_mem_free(req); + return; + +nla_put_failure: + kfree_skb(skb); + qdf_mem_free(req); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) +/** + * hdd_cfg80211_scan_done() - Scan completed callback to cfg80211 + * @adapter: Pointer to the adapter + * @req : Scan request + * @aborted : true scan aborted false scan success + * + * This function notifies scan done to cfg80211 + * + * Return: none + */ +static void hdd_cfg80211_scan_done(struct hdd_adapter *adapter, + struct cfg80211_scan_request *req, + bool aborted) +{ + struct cfg80211_scan_info info = { + .aborted = aborted + }; + + if (adapter->dev->flags & IFF_UP) + cfg80211_scan_done(req, &info); +} +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) +/** + * hdd_cfg80211_scan_done() - Scan completed callback to cfg80211 + * @adapter: Pointer to the adapter + * @req : Scan request + * @aborted : true scan aborted false scan success + * + * This function notifies scan done to cfg80211 + * + * Return: none + */ +static void hdd_cfg80211_scan_done(struct hdd_adapter *adapter, + struct cfg80211_scan_request *req, + bool aborted) +{ + if (adapter->dev->flags & IFF_UP) + cfg80211_scan_done(req, aborted); +} +#else +/** + * hdd_cfg80211_scan_done() - Scan completed callback to cfg80211 + * @adapter: Pointer to the adapter + * @req : Scan request + * @aborted : true scan aborted false scan success + * + * This function notifies scan done to cfg80211 + * + * Return: none + */ +static void hdd_cfg80211_scan_done(struct hdd_adapter *adapter, + struct cfg80211_scan_request *req, + bool aborted) +{ + cfg80211_scan_done(req, aborted); +} +#endif + +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE +/** + * wlan_hdd_sap_skip_scan_check() - The function will check OBSS + * scan skip or not for SAP. + * @hdd_ctx: pointer to hdd context. + * @request: pointer to scan request. + * + * This function will check the scan request's chan list against the + * previous ACS scan chan list. If all the chan are covered by + * previous ACS scan, we can skip the scan and return scan complete + * to save the SAP starting time. + * + * Return: true to skip the scan, + * false to continue the scan + */ +static bool wlan_hdd_sap_skip_scan_check(struct hdd_context *hdd_ctx, + struct cfg80211_scan_request *request) +{ + int i, j; + bool skip; + + hdd_debug("HDD_ACS_SKIP_STATUS = %d", + hdd_ctx->skip_acs_scan_status); + if (hdd_ctx->skip_acs_scan_status != eSAP_SKIP_ACS_SCAN) + return false; + qdf_spin_lock(&hdd_ctx->acs_skip_lock); + if (!hdd_ctx->last_acs_freq_list || + hdd_ctx->num_of_channels == 0 || + request->n_channels == 0) { + qdf_spin_unlock(&hdd_ctx->acs_skip_lock); + return false; + } + skip = true; + for (i = 0; i < request->n_channels ; i++) { + bool find = false; + + for (j = 0; j < hdd_ctx->num_of_channels; j++) { + if (hdd_ctx->last_acs_freq_list[j] == + request->channels[i]->center_freq) { + find = true; + break; + } + } + if (!find) { + skip = false; + hdd_debug("Freq %d isn't in ACS freq list", + request->channels[i]->center_freq); + break; + } + } + qdf_spin_unlock(&hdd_ctx->acs_skip_lock); + return skip; +} +#else +static bool wlan_hdd_sap_skip_scan_check(struct hdd_context *hdd_ctx, + struct cfg80211_scan_request *request) +{ + return false; +} +#endif + +void wlan_hdd_cfg80211_scan_block(struct hdd_adapter *adapter) +{ + struct cfg80211_scan_request *request; + struct scan_req *blocked_scan_req; + qdf_list_node_t *node; + + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("HDD adapter context is invalid"); + return; + } + + qdf_mutex_acquire(&adapter->blocked_scan_request_q_lock); + + while (!qdf_list_empty(&adapter->blocked_scan_request_q)) { + qdf_list_remove_front(&adapter->blocked_scan_request_q, + &node); + blocked_scan_req = qdf_container_of(node, struct scan_req, + node); + request = blocked_scan_req->scan_request; + request->n_ssids = 0; + request->n_channels = 0; + if (blocked_scan_req->source == NL_SCAN) { + hdd_err("Scan aborted. Null result sent"); + hdd_cfg80211_scan_done(adapter, request, true); + } else { + hdd_err("Vendor scan aborted. Null result sent"); + hdd_vendor_scan_callback(adapter, request, true); + } + qdf_mem_free(blocked_scan_req); + } + + qdf_mutex_release(&adapter->blocked_scan_request_q_lock); +} + +void hdd_init_scan_reject_params(struct hdd_context *hdd_ctx) +{ + if (hdd_ctx) { + hdd_ctx->last_scan_reject_timestamp = 0; + hdd_ctx->last_scan_reject_vdev_id = WLAN_UMAC_VDEV_ID_MAX; + hdd_ctx->last_scan_reject_reason = 0; + hdd_ctx->scan_reject_cnt = 0; + } +} + +void hdd_reset_scan_reject_params(struct hdd_context *hdd_ctx, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ + if ((roam_status == eCSR_ROAM_ASSOCIATION_FAILURE) || + (roam_status == eCSR_ROAM_CANCELLED) || + (roam_result == eCSR_ROAM_RESULT_ASSOCIATED)) { + hdd_debug("Reset scan reject params"); + hdd_init_scan_reject_params(hdd_ctx); + } +} + +/* + * wlan_hdd_update_scan_ies() - API to update the scan IEs of scan request + * with already stored default scan IEs + * + * @adapter: Pointer to HDD adapter + * @scan_info: Pointer to scan info in HDD adapter + * @scan_ie: Pointer to scan IE in scan request + * @scan_ie_len: Pointer to scan IE length in scan request + * + * Return: 0 on success; error number otherwise + */ +static int wlan_hdd_update_scan_ies(struct hdd_adapter *adapter, + struct hdd_scan_info *scan_info, uint8_t *scan_ie, + uint16_t *scan_ie_len) +{ + uint16_t rem_len = scan_info->default_scan_ies_len; + uint8_t *temp_ie = scan_info->default_scan_ies; + uint8_t *current_ie; + const uint8_t *mbo_ie; + uint8_t elem_id; + uint16_t elem_len; + bool add_ie = false; + + if (!scan_info->default_scan_ies_len || !scan_info->default_scan_ies) + return 0; + + mbo_ie = wlan_get_vendor_ie_ptr_from_oui(MBO_OUI_TYPE, + MBO_OUI_TYPE_SIZE, scan_ie, + *scan_ie_len); + while (rem_len >= 2) { + current_ie = temp_ie; + elem_id = *temp_ie++; + elem_len = *temp_ie++; + rem_len -= 2; + + if (elem_len > rem_len) { + hdd_err("Invalid element len %d for elem %d", elem_len, + elem_id); + return 0; + } + + switch (elem_id) { + case DOT11F_EID_EXTCAP: + if (!wlan_get_ie_ptr_from_eid(DOT11F_EID_EXTCAP, + scan_ie, *scan_ie_len)) + add_ie = true; + break; + case WLAN_ELEMID_VENDOR: + /* Donot add MBO IE if its already present */ + if ((!mbo_ie && + 0 == qdf_mem_cmp(&temp_ie[0], MBO_OUI_TYPE, + MBO_OUI_TYPE_SIZE)) || + (0 == qdf_mem_cmp(&temp_ie[0], QCN_OUI_TYPE, + QCN_OUI_TYPE_SIZE))) + add_ie = true; + break; + } + + if (add_ie && (((*scan_ie_len) + elem_len) > + SIR_MAC_MAX_ADD_IE_LENGTH)){ + hdd_err("Not enough buffer to save default scan IE's"); + return 0; + } + + if (add_ie) { + qdf_mem_copy(scan_ie + (*scan_ie_len), + current_ie, elem_len + 2); + (*scan_ie_len) += (elem_len + 2); + add_ie = false; + } + + temp_ie += elem_len; + rem_len -= elem_len; + } + return 0; +} + +static int +wlan_hdd_enqueue_blocked_scan_request(struct net_device *dev, + struct cfg80211_scan_request *request, + uint8_t source) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct scan_req *blocked_scan_req = + qdf_mem_malloc(sizeof(*blocked_scan_req)); + int ret = 0; + + if (!blocked_scan_req) + return -EINVAL; + + blocked_scan_req->dev = dev; + blocked_scan_req->scan_request = request; + blocked_scan_req->source = source; + blocked_scan_req->scan_id = 0; + + qdf_mutex_acquire(&adapter->blocked_scan_request_q_lock); + if (qdf_list_size(&adapter->blocked_scan_request_q) < + WLAN_MAX_SCAN_COUNT) + qdf_list_insert_back(&adapter->blocked_scan_request_q, + &blocked_scan_req->node); + else + ret = -EINVAL; + qdf_mutex_release(&adapter->blocked_scan_request_q_lock); + + if (ret) { + hdd_err("Maximum number of block scan request reached!"); + qdf_mem_free(blocked_scan_req); + } + + return ret; +} + +/* Define short name to use in cds_trigger_recovery */ +#define SCAN_FAILURE QDF_SCAN_ATTEMPT_FAILURES + +/** + * __wlan_hdd_cfg80211_scan() - API to process cfg80211 scan request + * @wiphy: Pointer to wiphy + * @dev: Pointer to net device + * @request: Pointer to scan request + * @source: scan request source(NL/Vendor scan) + * + * This API responds to scan trigger and update cfg80211 scan database + * later, scan dump command can be used to receive scan results + * + * Return: 0 for success, non zero for failure + */ +static int __wlan_hdd_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request, + uint8_t source) +{ + struct net_device *dev = request->wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_config *cfg_param = NULL; + int status; + struct hdd_scan_info *scan_info = NULL; + struct hdd_adapter *con_sap_adapter; + uint32_t con_dfs_ch_freq; + uint8_t curr_vdev_id; + enum scan_reject_states curr_reason; + static uint32_t scan_ebusy_cnt; + struct scan_params params = {0}; + bool self_recovery; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS qdf_status; + bool enable_connected_scan; + + if (cds_is_fw_down()) { + hdd_err("firmware is down, scan cmd cannot be processed"); + return -EINVAL; + } + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return status; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_SCAN, + adapter->vdev_id, request->n_channels); + + if (!sme_is_session_id_valid(hdd_ctx->mac_handle, adapter->vdev_id)) + return -EINVAL; + + qdf_status = ucfg_mlme_get_self_recovery(hdd_ctx->psoc, &self_recovery); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err("Failed to get self recovery ini config"); + return -EIO; + } + + enable_connected_scan = ucfg_scan_is_connected_scan_enabled( + hdd_ctx->psoc); + if ((eConnectionState_Associated == + WLAN_HDD_GET_STATION_CTX_PTR(adapter)-> + conn_info.conn_state) && + (!enable_connected_scan)) { + hdd_info("enable_connected_scan is false, Aborting scan"); + if (wlan_hdd_enqueue_blocked_scan_request(dev, request, source)) + return -EAGAIN; + schedule_work(&adapter->scan_block_work); + return 0; + } + + /* + * IBSS vdev does not need to scan to establish + * IBSS connection. If IBSS vdev need to support scan, + * Firmware need to make the change to add self peer + * per mac for IBSS vdev. + * NDI does not need scan from userspace to establish connection + * and it does not support scan request either. + */ + if (QDF_IBSS_MODE == adapter->device_mode || + QDF_NDI_MODE == adapter->device_mode) { + hdd_err("Scan not supported for %s", + qdf_opmode_str(adapter->device_mode)); + return -EINVAL; + } + + cfg_param = hdd_ctx->config; + scan_info = &adapter->scan_info; + + /* Block All Scan during DFS operation and send null scan result */ + con_sap_adapter = hdd_get_con_sap_adapter(adapter, true); + if (con_sap_adapter) { + con_dfs_ch_freq = + con_sap_adapter->session.ap.sap_config.chan_freq; + if (con_dfs_ch_freq == AUTO_CHANNEL_SELECT) + con_dfs_ch_freq = + con_sap_adapter->session.ap.operating_chan_freq; + + if (!policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) && + wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, con_dfs_ch_freq) && + !policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan( + hdd_ctx->psoc)) { + /* Provide empty scan result during DFS operation since + * scanning not supported during DFS. Reason is + * following case: + * DFS is supported only in SCC for MBSSID Mode. + * We shall not return EBUSY or ENOTSUPP as when Primary + * AP is operating in DFS channel and secondary AP is + * started. Though we force SCC in driver, the hostapd + * issues obss scan before starting secAP. This results + * in MCC in DFS mode. Thus we return null scan result. + * If we return scan failure hostapd fails secondary AP + * startup. + */ + hdd_err("##In DFS Master mode. Scan aborted"); + if (wlan_hdd_enqueue_blocked_scan_request(dev, request, + source)) + return -EAGAIN; + schedule_work(&adapter->scan_block_work); + return 0; + } + } + + /* Check if scan is allowed at this point of time */ + if (hdd_is_connection_in_progress(&curr_vdev_id, &curr_reason)) { + scan_ebusy_cnt++; + hdd_err_rl("Scan not allowed. scan_ebusy_cnt: %d Session %d Reason %d", + scan_ebusy_cnt, curr_vdev_id, curr_reason); + if (hdd_ctx->last_scan_reject_vdev_id != curr_vdev_id || + hdd_ctx->last_scan_reject_reason != curr_reason || + !hdd_ctx->last_scan_reject_timestamp) { + hdd_ctx->last_scan_reject_vdev_id = curr_vdev_id; + hdd_ctx->last_scan_reject_reason = curr_reason; + hdd_ctx->last_scan_reject_timestamp = jiffies + + msecs_to_jiffies(SCAN_REJECT_THRESHOLD_TIME); + hdd_ctx->scan_reject_cnt = 0; + } else { + hdd_ctx->scan_reject_cnt++; + if ((hdd_ctx->scan_reject_cnt >= + SCAN_REJECT_THRESHOLD) && + qdf_system_time_after(jiffies, + hdd_ctx->last_scan_reject_timestamp)) { + hdd_err("scan reject threshold reached Session %d Reason %d count %d reject timestamp %lu jiffies %lu", + curr_vdev_id, curr_reason, + hdd_ctx->scan_reject_cnt, + hdd_ctx->last_scan_reject_timestamp, + jiffies); + hdd_ctx->last_scan_reject_timestamp = 0; + hdd_ctx->scan_reject_cnt = 0; + if (cds_is_fatal_event_enabled()) { + cds_flush_logs(WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_REASON_SCAN_NOT_ALLOWED, + false, + self_recovery); + } else { + hdd_err("Triggering SSR due to scan stuck"); + cds_trigger_recovery(SCAN_FAILURE); + } + } + } + return -EBUSY; + } + + hdd_init_scan_reject_params(hdd_ctx); + + /* Check whether SAP scan can be skipped or not */ + if (adapter->device_mode == QDF_SAP_MODE && + wlan_hdd_sap_skip_scan_check(hdd_ctx, request)) { + hdd_debug("sap scan skipped"); + if (wlan_hdd_enqueue_blocked_scan_request(dev, request, source)) + return -EAGAIN; + schedule_work(&adapter->scan_block_work); + return 0; + } + + params.source = source; + params.default_ie.len = 0; + /* Store the Scan IE's in Adapter*/ + if (request->ie_len) { + if (request->ie_len > SIR_MAC_MAX_ADD_IE_LENGTH) { + hdd_debug("Invalid ie_len: %zu", request->ie_len); + return -EINVAL; + } + + /* save this for future association (join requires this) */ + memset(&scan_info->scan_add_ie, 0, sizeof(scan_info->scan_add_ie)); + memcpy(scan_info->scan_add_ie.addIEdata, request->ie, + request->ie_len); + scan_info->scan_add_ie.length = request->ie_len; + + wlan_hdd_update_scan_ies(adapter, scan_info, + scan_info->scan_add_ie.addIEdata, + &scan_info->scan_add_ie.length); + } else { + if (scan_info->default_scan_ies && + scan_info->default_scan_ies_len) { + qdf_mem_copy(scan_info->scan_add_ie.addIEdata, + scan_info->default_scan_ies, + scan_info->default_scan_ies_len); + scan_info->scan_add_ie.length = + scan_info->default_scan_ies_len; + params.default_ie.ptr = + qdf_mem_malloc(scan_info->default_scan_ies_len); + if (params.default_ie.ptr) { + qdf_mem_copy(params.default_ie.ptr, + scan_info->default_scan_ies, + scan_info->default_scan_ies_len); + params.default_ie.len = + scan_info->default_scan_ies_len; + } + } + } + + if ((QDF_STA_MODE == adapter->device_mode) || + (QDF_P2P_CLIENT_MODE == adapter->device_mode) || + (QDF_P2P_DEVICE_MODE == adapter->device_mode)) { + struct csr_roam_profile *roam_profile = + hdd_roam_profile(adapter); + + roam_profile->pAddIEScan = + scan_info->scan_add_ie.addIEdata; + roam_profile->nAddIEScanLength = + scan_info->scan_add_ie.length; + } + + if (QDF_P2P_CLIENT_MODE == adapter->device_mode || + QDF_P2P_DEVICE_MODE == adapter->device_mode) { + /* Disable NAN Discovery if enabled */ + ucfg_nan_disable_concurrency(hdd_ctx->psoc); + } + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) { + status = -EINVAL; + goto error; + } + + if ((request->n_ssids == 1) && (request->ssids) && + (request->ssids[0].ssid_len > 7) && + !qdf_mem_cmp(&request->ssids[0], "DIRECT-", 7)) + ucfg_p2p_status_scan(vdev); + + /* If this a scan on SAP adapter, use scan priority high */ + if (adapter->device_mode == QDF_SAP_MODE) + params.priority = SCAN_PRIORITY_HIGH; + else + /* Use default scan priority */ + params.priority = SCAN_PRIORITY_COUNT; + + status = wlan_cfg80211_scan(vdev, request, ¶ms); + hdd_objmgr_put_vdev(vdev); +error: + if (params.default_ie.ptr) + qdf_mem_free(params.default_ie.ptr); + + return status; +} + +#undef SCAN_FAILURE + +/** + * wlan_hdd_cfg80211_scan() - API to process cfg80211 scan request + * @wiphy: Pointer to wiphy + * @dev: Pointer to net device + * @request: Pointer to scan request + * + * This API responds to scan trigger and update cfg80211 scan database + * later, scan dump command can be used to receive scan results + * + * Return: 0 for success, non zero for failure + */ +int wlan_hdd_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(request->wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_scan(wiphy, request, NL_SCAN); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_get_rates() -API to get the rates from scan request + * @wiphy: Pointer to wiphy + * @band: Band + * @rates: array of rates + * @rate_count: number of rates + * + * Return: o for failure, rate bitmap for success + */ +static uint32_t wlan_hdd_get_rates(struct wiphy *wiphy, + enum nl80211_band band, + const u8 *rates, unsigned int rate_count) +{ + uint32_t j, count, rate_bitmap = 0; + uint32_t rate; + bool found; + + for (count = 0; count < rate_count; count++) { + rate = ((rates[count]) & RATE_MASK) * 5; + found = false; + for (j = 0; j < wiphy->bands[band]->n_bitrates; j++) { + if (wiphy->bands[band]->bitrates[j].bitrate == rate) { + found = true; + rate_bitmap |= (1 << j); + break; + } + } + if (!found) + return 0; + } + return rate_bitmap; +} + +/** + * wlan_hdd_send_scan_start_event() -API to send the scan start event + * @wiphy: Pointer to wiphy + * @wdev: Pointer to net device + * @cookie: scan identifier + * + * Return: return 0 on success and negative error code on failure + */ +static int wlan_hdd_send_scan_start_event(struct wiphy *wiphy, + struct wireless_dev *wdev, uint64_t cookie) +{ + struct sk_buff *skb; + int ret; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u64) + + NLA_HDRLEN + NLMSG_HDRLEN); + if (!skb) { + hdd_err(" reply skb alloc failed"); + return -ENOMEM; + } + + if (hdd_wlan_nla_put_u64(skb, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, + cookie)) { + hdd_err("nla put fail"); + kfree_skb(skb); + return -EINVAL; + } + + ret = cfg80211_vendor_cmd_reply(skb); + + /* Send a scan started event to supplicant */ + skb = cfg80211_vendor_event_alloc(wiphy, wdev, + sizeof(u64) + 4 + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_SCAN_INDEX, GFP_KERNEL); + if (!skb) { + hdd_err("skb alloc failed"); + return -ENOMEM; + } + + if (hdd_wlan_nla_put_u64(skb, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, + cookie)) { + kfree_skb(skb); + return -EINVAL; + } + cfg80211_vendor_event(skb, GFP_KERNEL); + + return ret; +} + +/** + * wlan_hdd_copy_bssid() - API to copy the bssid to vendor Scan request + * @request: Pointer to vendor scan request + * @bssid: Pointer to BSSID + * + * This API copies the specific BSSID received from Supplicant and copies it to + * the vendor Scan request + * + * Return: None + */ +#if defined(CFG80211_SCAN_BSSID) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) +static inline void wlan_hdd_copy_bssid(struct cfg80211_scan_request *request, + uint8_t *bssid) +{ + qdf_mem_copy(request->bssid, bssid, QDF_MAC_ADDR_SIZE); +} +#else +static inline void wlan_hdd_copy_bssid(struct cfg80211_scan_request *request, + uint8_t *bssid) +{ +} +#endif + +static void hdd_process_vendor_acs_response(struct hdd_adapter *adapter) +{ + if (test_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags)) { + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&adapter->session. + ap.vendor_acs_timer)) { + qdf_mc_timer_stop(&adapter->session. + ap.vendor_acs_timer); + } + } +} + +#if defined(CFG80211_SCAN_RANDOM_MAC_ADDR) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +/** + * wlan_hdd_vendor_scan_random_attr() - check and fill scan randomization attrs + * @wiphy: Pointer to wiphy + * @request: Pointer to scan request + * @wdev: Pointer to wireless device + * @tb: Pointer to nl attributes + * + * This function is invoked to check whether vendor scan needs + * probe req source addr, if so populates mac_addr and mac_addr_mask + * in scan request with nl attrs. + * + * Return: 0 - on success, negative value on failure + */ +static int wlan_hdd_vendor_scan_random_attr(struct wiphy *wiphy, + struct cfg80211_scan_request *request, + struct wireless_dev *wdev, + struct nlattr **tb) +{ + uint32_t i; + int32_t len = QDF_MAC_ADDR_SIZE; + + if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) + return 0; + + if (!(wiphy->features & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR) || + (wdev->current_bss)) { + hdd_err("SCAN RANDOMIZATION not supported"); + return -EOPNOTSUPP; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC] && + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK]) { + qdf_mem_zero(request->mac_addr, len); + qdf_mem_zero(request->mac_addr_mask, len); + request->mac_addr[0] = 0x2; + request->mac_addr_mask[0] = 0x3; + + return 0; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC] || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK]) + return -EINVAL; + + if ((nla_len(tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC]) != len) || + (nla_len(tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK]) != len)) + return -EINVAL; + + qdf_mem_copy(request->mac_addr, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC]), len); + + qdf_mem_copy(request->mac_addr_mask, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK]), len); + + /* avoid configure on multicast address */ + if (!cds_is_group_addr(request->mac_addr_mask) || + cds_is_group_addr(request->mac_addr)) + return -EINVAL; + + for (i = 0; i < ETH_ALEN; i++) + request->mac_addr[i] &= request->mac_addr_mask[i]; + + return 0; +} +#else +static int wlan_hdd_vendor_scan_random_attr(struct wiphy *wiphy, + struct cfg80211_scan_request *request, + struct wireless_dev *wdev, + struct nlattr **tb) +{ + return 0; +} +#endif + +static const +struct nla_policy scan_policy[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE] = {.type = NLA_FLAG}, + [QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE] = {.type = NLA_U64}, + [QCA_WLAN_VENDOR_ATTR_SCAN_IE] = {.type = NLA_BINARY, + .len = MAX_DEFAULT_SCAN_IE_LEN}, + [QCA_WLAN_VENDOR_ATTR_SCAN_MAC] = {.type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK] = {.type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE}, +}; + +/** + * __wlan_hdd_cfg80211_vendor_scan() - API to process venor scan request + * @wiphy: Pointer to wiphy + * @wdev: Pointer to net device + * @data : Pointer to the data + * @data_len : length of the data + * + * API to process venor scan request. + * + * Return: return 0 on success and negative error code on failure + */ +static int __wlan_hdd_cfg80211_vendor_scan(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, + int data_len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + struct cfg80211_scan_request *request = NULL; + struct nlattr *attr; + enum nl80211_band band; + uint32_t n_channels = 0, n_ssid = 0; + uint32_t tmp, count, j; + size_t len, ie_len = 0; + struct ieee80211_channel *chan; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); + int ret; + + hdd_enter_dev(wdev->netdev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) { + /* + * During SSR, if -EBUSY is returned then OBSS vendor scan is + * not issued immediately. + */ + if (ret == -EAGAIN) + return -EBUSY; + + return ret; + } + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + data, data_len, scan_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) { + nla_for_each_nested(attr, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES], tmp) + n_channels++; + } else { + for (band = 0; band < HDD_NUM_NL80211_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + if (n_channels > NUM_CHANNELS) { + hdd_err("Exceed max number of channels: %d", n_channels); + return -EINVAL; + } + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) + nla_for_each_nested(attr, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], tmp) + n_ssid++; + + if (MAX_SCAN_SSID < n_ssid) { + hdd_err("Exceed max number of SSID: %d", n_ssid); + return -EINVAL; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_IE]) + ie_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_SCAN_IE]); + + len = sizeof(*request) + (sizeof(*request->ssids) * n_ssid) + + (sizeof(*request->channels) * n_channels) + ie_len; + + request = qdf_mem_malloc(len); + if (!request) + goto error; + if (n_ssid) + request->ssids = (void *)&request->channels[n_channels]; + request->n_ssids = n_ssid; + if (ie_len) { + if (request->ssids) + request->ie = (void *)(request->ssids + n_ssid); + else + request->ie = (void *)(request->channels + n_channels); + } + + request->ie_len = ie_len; + count = 0; + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) { + nla_for_each_nested(attr, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES], + tmp) { + if (nla_len(attr) != sizeof(uint32_t)) { + hdd_err("len is not correct for frequency %d", + count); + goto error; + } + chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); + if (!chan) + goto error; + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + request->channels[count] = chan; + count++; + } + } else { + for (band = 0; band < HDD_NUM_NL80211_BANDS; band++) { + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; + j++) { + chan = &wiphy->bands[band]->channels[j]; + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + request->channels[count] = chan; + count++; + } + } + } + + if (!count) + goto error; + + request->n_channels = count; + count = 0; + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) { + int ssid_length; + + nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], + tmp) { + ssid_length = nla_len(attr); + if ((ssid_length > WLAN_SSID_MAX_LEN) || + (ssid_length < 0)) { + hdd_err("SSID Len %d is not correct for network %d", + ssid_length, count); + goto error; + } + + request->ssids[count].ssid_len = ssid_length; + memcpy(request->ssids[count].ssid, nla_data(attr), + ssid_length); + count++; + } + } + + if (ie_len) + nla_memcpy((void *)request->ie, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_IE], ie_len); + + for (count = 0; count < HDD_NUM_NL80211_BANDS; count++) + if (wiphy->bands[count]) + request->rates[count] = + (1 << wiphy->bands[count]->n_bitrates) - 1; + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES]) { + nla_for_each_nested(attr, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES], + tmp) { + band = nla_type(attr); + if (band >= HDD_NUM_NL80211_BANDS) + continue; + if (!wiphy->bands[band]) + continue; + request->rates[band] = + wlan_hdd_get_rates(wiphy, + band, nla_data(attr), + nla_len(attr)); + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS]) { + request->flags = + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS]); + if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && + !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) { + hdd_err("LOW PRIORITY SCAN not supported"); + goto error; + } + + if (wlan_hdd_vendor_scan_random_attr(wiphy, request, wdev, tb)) + goto error; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_BSSID]) { + if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_SCAN_BSSID]) < + QDF_MAC_ADDR_SIZE) { + hdd_err("invalid bssid length"); + goto error; + } + wlan_hdd_copy_bssid(request, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_SCAN_BSSID])); + } + + /* Check if external acs was requested on this adapter */ + hdd_process_vendor_acs_response(adapter); + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE]) + request->no_cck = + nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE]); + request->wdev = wdev; + request->wiphy = wiphy; + request->scan_start = jiffies; + + ret = __wlan_hdd_cfg80211_scan(wiphy, request, VENDOR_SCAN); + if (0 != ret) { + hdd_err("Scan Failed. Ret = %d", ret); + qdf_mem_free(request); + return ret; + } + ret = wlan_hdd_send_scan_start_event(wiphy, wdev, (uintptr_t)request); + + return ret; +error: + hdd_err("Scan Request Failed"); + qdf_mem_free(request); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_vendor_scan() -API to process venor scan request + * @wiphy: Pointer to wiphy + * @dev: Pointer to net device + * @data : Pointer to the data + * @data_len : length of the data + * + * This is called from userspace to request scan. + * + * Return: Return the Success or Failure code. + */ +int wlan_hdd_cfg80211_vendor_scan(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_vendor_scan(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_vendor_abort_scan() - API to process vendor command for + * abort scan + * @wiphy: Pointer to wiphy + * @wdev: Pointer to net device + * @data : Pointer to the data + * @data_len : length of the data + * + * API to process vendor abort scan + * + * Return: zero for success and non zero for failure + */ +static int __wlan_hdd_vendor_abort_scan( + struct wiphy *wiphy, const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + wlan_vendor_abort_scan(hdd_ctx->pdev, data, data_len); + + return ret; +} + +/** + * wlan_hdd_vendor_abort_scan() - API to process vendor command for + * abort scan + * @wiphy: Pointer to wiphy + * @wdev: Pointer to net device + * @data : Pointer to the data + * @data_len : length of the data + * + * This is called from supplicant to abort scan + * + * Return: zero for success and non zero for failure + */ +int wlan_hdd_vendor_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_vendor_abort_scan(wiphy, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_scan_abort() - abort ongoing scan + * @adapter: Pointer to interface adapter + * + * Return: 0 for success, non zero for failure + */ +int wlan_hdd_scan_abort(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + wlan_abort_scan(hdd_ctx->pdev, INVAL_PDEV_ID, + adapter->vdev_id, INVALID_SCAN_ID, true); + + return 0; +} + +#ifdef FEATURE_WLAN_SCAN_PNO +/** + * __wlan_hdd_cfg80211_sched_scan_start() - cfg80211 scheduled scan(pno) start + * @wiphy: Pointer to wiphy + * @dev: Pointer network device + * @request: Pointer to cfg80211 scheduled scan start request + * + * Return: 0 for success, non zero for failure + */ +static int __wlan_hdd_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct + cfg80211_sched_scan_request + *request) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + struct wlan_objmgr_vdev *vdev; + int ret; + bool pno_offload_enabled; + uint8_t scan_backoff_multiplier; + bool enable_connected_scan; + enum QDF_GLOBAL_MODE curr_mode; + + curr_mode = hdd_get_conparam(); + + if (QDF_GLOBAL_FTM_MODE == curr_mode || + QDF_GLOBAL_MONITOR_MODE == curr_mode) { + hdd_err_rl("Command not allowed in FTM/Monitor mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_info("Sched scans only supported on STA ifaces"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + pno_offload_enabled = ucfg_scan_is_pno_offload_enabled(hdd_ctx->psoc); + if (!pno_offload_enabled) { + hdd_debug("Pno Offload is not enabled"); + return -EINVAL; + } + + enable_connected_scan = ucfg_scan_is_connected_scan_enabled( + hdd_ctx->psoc); + if ((eConnectionState_Associated == + WLAN_HDD_GET_STATION_CTX_PTR(adapter)-> + conn_info.conn_state) && + (!enable_connected_scan)) { + hdd_info("enable_connected_scan is false, Aborting scan"); + return -EBUSY; + } + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + scan_backoff_multiplier = + ucfg_get_scan_backoff_multiplier(hdd_ctx->psoc); + ret = wlan_cfg80211_sched_scan_start(vdev, request, + scan_backoff_multiplier); + hdd_objmgr_put_vdev(vdev); + + return ret; +} + +/** + * wlan_hdd_cfg80211_sched_scan_start() - cfg80211 scheduled scan(pno) start + * @wiphy: Pointer to wiphy + * @dev: Pointer network device + * @request: Pointer to cfg80211 scheduled scan start request + * + * Return: 0 for success, non zero for failure + */ +int wlan_hdd_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request + *request) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_sched_scan_start(wiphy, dev, request); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +int wlan_hdd_sched_scan_stop(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + struct wlan_objmgr_vdev *vdev; + int ret; + bool pno_offload_enabled; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_err("HDD context is Null"); + return -EINVAL; + } + + pno_offload_enabled = ucfg_scan_is_pno_offload_enabled(hdd_ctx->psoc); + if (!pno_offload_enabled) { + hdd_debug("PnoOffload is not enabled!!!"); + return -EINVAL; + } + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + ret = wlan_cfg80211_sched_scan_stop(vdev); + hdd_objmgr_put_vdev(vdev); + + return ret; +} + +/** + * __wlan_hdd_cfg80211_sched_scan_stop() - stop cfg80211 scheduled scan(pno) + * @dev: Pointer network device + * + * This is a wrapper around wlan_hdd_sched_scan_stop() that returns success + * in the event that the driver is currently recovering or unloading. This + * prevents a race condition where we get a scan stop from kernel during + * a driver unload from PLD. + * + * Return: 0 for success, non zero for failure + */ +static int __wlan_hdd_cfg80211_sched_scan_stop(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int errno; + enum QDF_GLOBAL_MODE curr_mode; + + curr_mode = hdd_get_conparam(); + + if (QDF_GLOBAL_FTM_MODE == curr_mode || + QDF_GLOBAL_MONITOR_MODE == curr_mode) { + hdd_err_rl("Command not allowed in FTM/Monitor mode"); + return -EINVAL; + } + + /* The return 0 is intentional when Recovery and Load/Unload in + * progress. We did observe a crash due to a return of + * failure in sched_scan_stop , especially for a case where the unload + * of the happens at the same time. The function + * __cfg80211_stop_sched_scan was clearing rdev->sched_scan_req only + * when the sched_scan_stop returns success. If it returns a failure , + * then its next invocation due to the clean up of the second interface + * will have the dev pointer corresponding to the first one leading to + * a crash. + */ + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + hdd_info("Recovery in Progress. State: 0x%x Ignore!!!", + cds_get_driver_state()); + return 0; + } + + if (cds_is_load_or_unload_in_progress()) { + hdd_info("Unload/Load in Progress, state: 0x%x. Ignore!!!", + cds_get_driver_state()); + return 0; + } + + errno = hdd_validate_adapter(adapter); + if (errno) + return errno; + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_info("Sched scans only supported on STA ifaces"); + return -EINVAL; + } + + errno = wlan_hdd_validate_context(WLAN_HDD_GET_CTX(adapter)); + if (errno) + return errno; + + errno = wlan_hdd_sched_scan_stop(dev); + + return errno; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) +int wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_sched_scan_stop(dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#else +int wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev, + uint64_t reqid) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_sched_scan_stop(dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif /* KERNEL_VERSION(4, 12, 0) */ +#endif /*FEATURE_WLAN_SCAN_PNO */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)) || \ + defined(CFG80211_ABORT_SCAN) +/** + * __wlan_hdd_cfg80211_abort_scan() - cfg80211 abort scan api + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wireless device structure + * + * This function is used to abort an ongoing scan + * + * Return: None + */ +static void __wlan_hdd_cfg80211_abort_scan(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int ret; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return; + + wlan_cfg80211_abort_scan(hdd_ctx->pdev); + + hdd_exit(); +} + +/** + * wlan_hdd_cfg80211_abort_scan - cfg80211 abort scan api + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wireless device structure + * + * Wrapper to __wlan_hdd_cfg80211_abort_scan() - + * function is used to abort an ongoing scan + * + * Return: None + */ +void wlan_hdd_cfg80211_abort_scan(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return; + + __wlan_hdd_cfg80211_abort_scan(wiphy, wdev); + + osif_psoc_sync_op_stop(psoc_sync); +} +#endif + +/** + * hdd_scan_context_destroy() - Destroy scan context + * @hdd_ctx: HDD context. + * + * Destroy scan context. + * + * Return: None. + */ +void hdd_scan_context_destroy(struct hdd_context *hdd_ctx) +{ +} + +/** + * hdd_scan_context_init() - Initialize scan context + * @hdd_ctx: HDD context. + * + * Initialize scan related resources like spin lock and lists. + * + * Return: 0 on success and errno on failure. + */ +int hdd_scan_context_init(struct hdd_context *hdd_ctx) +{ + return 0; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_scan.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_scan.h new file mode 100644 index 0000000000000000000000000000000000000000..3f7020e3b3907b2a0abd2c23dfbcc37256f522ca --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_scan.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_scan.h + * + * WLAN Host Device Driver scan related implementation + * + */ + +#if !defined(WLAN_HDD_SCAN_H) +#define WLAN_HDD_SCAN_H + +#include "wlan_hdd_main.h" +#include "csr_inside_api.h" +#include + +#define EXTSCAN_PARAM_MAX QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + +int hdd_scan_context_init(struct hdd_context *hdd_ctx); +void hdd_scan_context_destroy(struct hdd_context *hdd_ctx); + +int wlan_hdd_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request); + +#ifdef FEATURE_WLAN_SCAN_PNO +int wlan_hdd_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request + *request); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) +/** + * wlan_hdd_cfg80211_sched_scan_stop() - stop cfg80211 scheduled (PNO) scan + * @wiphy: Pointer to wiphy + * @dev: Pointer network device + * + * Note, this returns success if the driver is recovering or unloading to + * prevent race conditions between PLD initiating an unload and kernel + * initiating a scheduled scan stop via cfg80211. Unload is expected to stop + * any pending scheduled scans in this case. + * + * Return: 0 for success, non zero for failure + */ +int wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev); +#else +int wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev, + uint64_t reqid); + +#endif /* KERNEL_VERSION(4, 12, 0) */ + +/** + * wlan_hdd_sched_scan_stop() - stop scheduled (PNO) scans + * @dev: Pointer network device + * + * Return: 0 for success, non zero for failure + */ +int wlan_hdd_sched_scan_stop(struct net_device *dev); +#else +static inline int wlan_hdd_sched_scan_stop(struct net_device *dev) +{ + return 0; +} +#endif /* End of FEATURE_WLAN_SCAN_PNO */ + +int wlan_hdd_cfg80211_vendor_scan(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, + int data_len); + +/** + * wlan_hdd_vendor_abort_scan() - API to process vendor command for + * abort scan + * @wiphy: Pointer to wiphy + * @wdev: Pointer to net device + * @data : Pointer to the data + * @data_len : length of the data + * + * This is called from supplicant to abort scan + * + * Return: zero for success and non zero for failure. + */ +int wlan_hdd_vendor_abort_scan( + struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)) || \ + defined(CFG80211_ABORT_SCAN) +void wlan_hdd_cfg80211_abort_scan(struct wiphy *wiphy, + struct wireless_dev *wdev); +#endif + +/** + * hdd_init_scan_reject_params() - init scan reject params + * @hdd_ctx: hdd contxt + * + * Return: None + */ +void hdd_init_scan_reject_params(struct hdd_context *hdd_ctx); + +/** + * hdd_reset_scan_reject_params() - reset scan reject params per roam stats + * @hdd_ctx: hdd contxt + * @roam_status: roam status + * @roam_result: roam result + * + * Return: None + */ +void hdd_reset_scan_reject_params(struct hdd_context *hdd_ctx, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result); + +/** + * wlan_hdd_cfg80211_scan_block() - scan block handler + * @adapter: HDD adapter to work against + * + * Return: none + */ +void wlan_hdd_cfg80211_scan_block(struct hdd_adapter *adapter); + +static const +struct nla_policy wlan_hdd_extscan_config_policy[EXTSCAN_PARAM_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CLASS] = {.type = NLA_U8}, + + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS] = { + .type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS] = { + .type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH] = { + .type = NLA_U8}, + + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID] = { + .type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW] = { + .type = NLA_S32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH] = { + .type = NLA_S32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_CHANNEL] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_SSID] = { + .type = NLA_BINARY, + .len = IEEE80211_MAX_SSID_LEN + 1 }, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE] = { + .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_NUM_SSID] = { + .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_BAND] = { + .type = NLA_U8 }, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW] = { + .type = NLA_S32 }, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH] = { + .type = NLA_S32 }, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS] = { + .type = NLA_U32 }, + [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE] = { + .type = NLA_U32}, +}; + +#endif /* end #if !defined(WLAN_HDD_SCAN_H) */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_softap_tx_rx.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_softap_tx_rx.c new file mode 100644 index 0000000000000000000000000000000000000000..05aafb47ce3be939b4ab366122c476612a4c5ad3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_softap_tx_rx.c @@ -0,0 +1,1453 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* denote that this file does not allow legacy hddLog */ +#define HDD_DISALLOW_LEGACY_HDDLOG 1 + +/* Include files */ +#include +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_ucfg_api.h" +#include +#include "wlan_ipa_ucfg_api.h" +#include "wlan_policy_mgr_ucfg.h" +#include +#include "wlan_hdd_sta_info.h" +#include "ol_defines.h" +#include + +/* Preprocessor definitions and constants */ +#undef QCA_HDD_SAP_DUMP_SK_BUFF + +/* Type declarations */ + +/* Function definitions and documenation */ +#ifdef QCA_HDD_SAP_DUMP_SK_BUFF +/** + * hdd_softap_dump_sk_buff() - Dump an skb + * @skb: skb to dump + * + * Return: None + */ +static void hdd_softap_dump_sk_buff(struct sk_buff *skb) +{ + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: head = %pK ", __func__, skb->head); + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO, + "%s: tail = %pK ", __func__, skb->tail); + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: end = %pK ", __func__, skb->end); + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: len = %d ", __func__, skb->len); + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: data_len = %d ", __func__, skb->data_len); + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: mac_len = %d", __func__, skb->mac_len); + + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x ", skb->data[0], + skb->data[1], skb->data[2], skb->data[3], skb->data[4], + skb->data[5], skb->data[6], skb->data[7]); + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", skb->data[8], + skb->data[9], skb->data[10], skb->data[11], skb->data[12], + skb->data[13], skb->data[14], skb->data[15]); +} +#else +static void hdd_softap_dump_sk_buff(struct sk_buff *skb) +{ +} +#endif + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +void hdd_softap_tx_resume_timer_expired_handler(void *adapter_context) +{ + struct hdd_adapter *adapter = (struct hdd_adapter *) adapter_context; + + if (!adapter) { + hdd_err("NULL adapter"); + return; + } + + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); +} + +/** + * hdd_softap_tx_resume_false() - Resume OS TX Q false leads to queue disabling + * @adapter: pointer to hdd adapter + * @tx_resume: TX Q resume trigger + * + * + * Return: None + */ +static void +hdd_softap_tx_resume_false(struct hdd_adapter *adapter, bool tx_resume) +{ + if (true == tx_resume) + return; + + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + + if (QDF_TIMER_STATE_STOPPED == + qdf_mc_timer_get_current_state(&adapter-> + tx_flow_control_timer)) { + QDF_STATUS status; + + status = qdf_mc_timer_start(&adapter->tx_flow_control_timer, + WLAN_SAP_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); + + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to start tx_flow_control_timer"); + else + adapter->hdd_stats.tx_rx_stats.txflow_timer_cnt++; + } +} + +void hdd_softap_tx_resume_cb(void *adapter_context, bool tx_resume) +{ + struct hdd_adapter *adapter = (struct hdd_adapter *) adapter_context; + + if (!adapter) { + hdd_err("NULL adapter"); + return; + } + + /* Resume TX */ + if (true == tx_resume) { + if (QDF_TIMER_STATE_STOPPED != + qdf_mc_timer_get_current_state(&adapter-> + tx_flow_control_timer)) { + qdf_mc_timer_stop(&adapter->tx_flow_control_timer); + } + + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + } + hdd_softap_tx_resume_false(adapter, tx_resume); +} + +static inline struct sk_buff *hdd_skb_orphan(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int need_orphan = 0; + + if (adapter->tx_flow_low_watermark > 0) { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 19, 0)) + /* + * The TCP TX throttling logic is changed a little after + * 3.19-rc1 kernel, the TCP sending limit will be smaller, + * which will throttle the TCP packets to the host driver. + * The TCP UP LINK throughput will drop heavily. In order to + * fix this issue, need to orphan the socket buffer asap, which + * will call skb's destructor to notify the TCP stack that the + * SKB buffer is unowned. And then the TCP stack will pump more + * packets to host driver. + * + * The TX packets might be dropped for UDP case in the iperf + * testing. So need to be protected by follow control. + */ + need_orphan = 1; +#else + if (hdd_ctx->config->tx_orphan_enable) + need_orphan = 1; +#endif + } else if (hdd_ctx->config->tx_orphan_enable) { + if (qdf_nbuf_is_ipv4_tcp_pkt(skb) || + qdf_nbuf_is_ipv6_tcp_pkt(skb)) + need_orphan = 1; + } + + if (need_orphan) { + skb_orphan(skb); + ++adapter->hdd_stats.tx_rx_stats.tx_orphaned; + } else + skb = skb_unshare(skb, GFP_ATOMIC); + + return skb; +} +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + +#define IEEE8021X_AUTH_TYPE_EAP 0 +#define EAP_CODE_OFFSET 18 +#define EAP_CODE_FAILURE 4 + +/* Wait EAP Failure frame timeout in (MS) */ +#define EAP_FRM_TIME_OUT 80 + +/** + * hdd_softap_inspect_tx_eap_pkt() - Inspect eap pkt tx/tx-completion + * @adapter: pointer to hdd adapter + * @skb: sk_buff + * @tx_comp: tx sending or tx completion + * + * Inspect the EAP-Failure pkt tx sending and tx completion. + * + * Return: void + */ +static void hdd_softap_inspect_tx_eap_pkt(struct hdd_adapter *adapter, + struct sk_buff *skb, + bool tx_comp) +{ + struct qdf_mac_addr *mac_addr; + uint8_t *data; + uint8_t auth_type, eap_code; + struct hdd_station_info *sta_info; + struct hdd_hostapd_state *hapd_state; + + if (qdf_likely(QDF_NBUF_CB_GET_PACKET_TYPE(skb) != + QDF_NBUF_CB_PACKET_TYPE_EAPOL) || skb->len < (EAP_CODE_OFFSET + 1)) + return; + + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state() || + cds_is_load_or_unload_in_progress()) { + hdd_debug("Recovery/(Un)load in Progress. Ignore!!!"); + return; + } + if (adapter->device_mode != QDF_P2P_GO_MODE) + return; + hapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + if (!hapd_state || hapd_state->bss_state != BSS_START) { + hdd_debug("Hostapd State is not START"); + return; + } + data = skb->data; + auth_type = *(uint8_t *)(data + EAPOL_PACKET_TYPE_OFFSET); + if (auth_type != IEEE8021X_AUTH_TYPE_EAP) + return; + eap_code = *(uint8_t *)(data + EAP_CODE_OFFSET); + if (eap_code != EAP_CODE_FAILURE) + return; + mac_addr = (struct qdf_mac_addr *)skb->data; + sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, + mac_addr->bytes, + STA_INFO_SOFTAP_INSPECT_TX_EAP_PKT); + if (!sta_info) + return; + if (tx_comp) { + hdd_debug("eap_failure frm tx done "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_addr->bytes)); + qdf_atomic_clear_bit(PENDING_TYPE_EAP_FAILURE, + &sta_info->pending_eap_frm_type); + qdf_event_set(&hapd_state->qdf_sta_eap_frm_done_event); + } else { + hdd_debug("eap_failure frm tx pending "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_addr->bytes)); + qdf_event_reset(&hapd_state->qdf_sta_eap_frm_done_event); + qdf_atomic_set_bit(PENDING_TYPE_EAP_FAILURE, + &sta_info->pending_eap_frm_type); + QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_NOTIFY_COMP(skb) = 1; + } + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_INSPECT_TX_EAP_PKT); +} + +void hdd_softap_check_wait_for_tx_eap_pkt(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr) +{ + struct hdd_station_info *sta_info; + QDF_STATUS qdf_status; + struct hdd_hostapd_state *hapd_state; + + if (adapter->device_mode != QDF_P2P_GO_MODE) + return; + hapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); + if (!hapd_state || hapd_state->bss_state != BSS_START) { + hdd_err("Hostapd State is not START"); + return; + } + sta_info = hdd_get_sta_info_by_mac( + &adapter->sta_info_list, + mac_addr->bytes, + STA_INFO_SOFTAP_CHECK_WAIT_FOR_TX_EAP_PKT); + if (!sta_info) + return; + if (qdf_atomic_test_bit(PENDING_TYPE_EAP_FAILURE, + &sta_info->pending_eap_frm_type)) { + hdd_debug("eap_failure frm pending "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_addr->bytes)); + qdf_status = qdf_wait_for_event_completion( + &hapd_state->qdf_sta_eap_frm_done_event, + EAP_FRM_TIME_OUT); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_debug("eap_failure tx timeout"); + } + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_CHECK_WAIT_FOR_TX_EAP_PKT); +} + +#ifdef SAP_DHCP_FW_IND +/** + * hdd_post_dhcp_ind() - Send DHCP START/STOP indication to FW + * @adapter: pointer to hdd adapter + * @sta_id: peer station ID + * @type: WMA message type + * + * Return: error number + */ +int hdd_post_dhcp_ind(struct hdd_adapter *adapter, uint8_t *mac_addr, + uint16_t type) +{ + tAniDHCPInd pmsg; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + hdd_debug("Post DHCP indication,sta_mac=" QDF_MAC_ADDR_FMT + " , type=%d", QDF_MAC_ADDR_REF(mac_addr), type); + + if (!adapter) { + hdd_err("NULL adapter"); + return -EINVAL; + } + + pmsg.msgType = type; + pmsg.msgLen = (uint16_t) sizeof(tAniDHCPInd); + pmsg.device_mode = adapter->device_mode; + qdf_mem_copy(pmsg.adapterMacAddr.bytes, + adapter->mac_addr.bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(pmsg.peerMacAddr.bytes, + mac_addr, + QDF_MAC_ADDR_SIZE); + + status = wma_process_dhcp_ind(cds_get_context(QDF_MODULE_ID_WMA), + &pmsg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: Post DHCP Ind MSG fail", __func__); + return -EFAULT; + } + + return 0; +} + +#define DHCP_CLIENT_MAC_ADDR_OFFSET 0x46 + +/** + * hdd_softap_notify_dhcp_ind() - Notify SAP for DHCP indication for tx desc + * @context: pointer to HDD context + * @netbuf: pointer to OS packet (sk_buff) + * + * Return: None + */ +static void hdd_softap_notify_dhcp_ind(void *context, struct sk_buff *netbuf) +{ + struct hdd_ap_ctx *hdd_ap_ctx; + uint8_t *dest_mac_addr; + struct hdd_adapter *adapter = context; + + if (hdd_validate_adapter(adapter)) + return; + + hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + if (!hdd_ap_ctx) { + hdd_err("HDD sap context is NULL"); + return; + } + + dest_mac_addr = netbuf->data + DHCP_CLIENT_MAC_ADDR_OFFSET; + + hdd_post_dhcp_ind(adapter, dest_mac_addr, WMA_DHCP_STOP_IND); +} + +int hdd_softap_inspect_dhcp_packet(struct hdd_adapter *adapter, + struct sk_buff *skb, + enum qdf_proto_dir dir) +{ + enum qdf_proto_subtype subtype = QDF_PROTO_INVALID; + struct hdd_station_info *hdd_sta_info; + int errno = 0; + struct qdf_mac_addr *src_mac; + + if (((adapter->device_mode == QDF_SAP_MODE) || + (adapter->device_mode == QDF_P2P_GO_MODE)) && + ((dir == QDF_TX && QDF_NBUF_CB_PACKET_TYPE_DHCP == + QDF_NBUF_CB_GET_PACKET_TYPE(skb)) || + (dir == QDF_RX && qdf_nbuf_is_ipv4_dhcp_pkt(skb) == true))) { + + src_mac = (struct qdf_mac_addr *)(skb->data + + DHCP_CLIENT_MAC_ADDR_OFFSET); + + subtype = qdf_nbuf_get_dhcp_subtype(skb); + hdd_sta_info = hdd_get_sta_info_by_mac( + &adapter->sta_info_list, + src_mac->bytes, + STA_INFO_SOFTAP_INSPECT_DHCP_PACKET); + if (!hdd_sta_info) { + hdd_debug("Station not found"); + return -EINVAL; + } + + hdd_debug("ENTER: type=%d, phase=%d, nego_status=%d", + subtype, + hdd_sta_info->dhcp_phase, + hdd_sta_info->dhcp_nego_status); + + switch (subtype) { + case QDF_PROTO_DHCP_DISCOVER: + if (dir != QDF_RX) + break; + if (hdd_sta_info->dhcp_nego_status == DHCP_NEGO_STOP) + errno = hdd_post_dhcp_ind( + adapter, + hdd_sta_info->sta_mac.bytes, + WMA_DHCP_START_IND); + hdd_sta_info->dhcp_phase = DHCP_PHASE_DISCOVER; + hdd_sta_info->dhcp_nego_status = DHCP_NEGO_IN_PROGRESS; + break; + case QDF_PROTO_DHCP_OFFER: + hdd_sta_info->dhcp_phase = DHCP_PHASE_OFFER; + break; + case QDF_PROTO_DHCP_REQUEST: + if (dir != QDF_RX) + break; + if (hdd_sta_info->dhcp_nego_status == DHCP_NEGO_STOP) + errno = hdd_post_dhcp_ind( + adapter, + hdd_sta_info->sta_mac.bytes, + WMA_DHCP_START_IND); + hdd_sta_info->dhcp_nego_status = DHCP_NEGO_IN_PROGRESS; + case QDF_PROTO_DHCP_DECLINE: + if (dir == QDF_RX) + hdd_sta_info->dhcp_phase = DHCP_PHASE_REQUEST; + break; + case QDF_PROTO_DHCP_ACK: + case QDF_PROTO_DHCP_NACK: + hdd_sta_info->dhcp_phase = DHCP_PHASE_ACK; + if (hdd_sta_info->dhcp_nego_status == + DHCP_NEGO_IN_PROGRESS) { + hdd_debug("Setting NOTIFY_COMP Flag"); + QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_NOTIFY_COMP(skb) + = 1; + } + hdd_sta_info->dhcp_nego_status = DHCP_NEGO_STOP; + break; + default: + break; + } + + hdd_debug("EXIT: phase=%d, nego_status=%d", + hdd_sta_info->dhcp_phase, + hdd_sta_info->dhcp_nego_status); + hdd_put_sta_info_ref(&adapter->sta_info_list, &hdd_sta_info, + true, STA_INFO_SOFTAP_INSPECT_DHCP_PACKET); + } + + return errno; +} +#else +static void hdd_softap_notify_dhcp_ind(void *context, struct sk_buff *netbuf) +{ +} +#endif + +/** + * __hdd_softap_hard_start_xmit() - Transmit a frame + * @skb: pointer to OS packet (sk_buff) + * @dev: pointer to network device + * + * Function registered with the Linux OS for transmitting + * packets. This version of the function directly passes + * the packet to Transport Layer. + * In case of any packet drop or error, log the error with + * INFO HIGH/LOW/MEDIUM to avoid excessive logging in kmsg. + * + * Return: None + */ +static void __hdd_softap_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + sme_ac_enum_type ac = SME_AC_BE; + struct hdd_adapter *adapter = (struct hdd_adapter *) netdev_priv(dev); + struct hdd_ap_ctx *ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + struct qdf_mac_addr *dest_mac_addr, *mac_addr; + static struct qdf_mac_addr bcast_mac_addr = QDF_MAC_ADDR_BCAST_INIT; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint32_t num_seg; + struct hdd_station_info *sta_info = NULL; + + ++adapter->hdd_stats.tx_rx_stats.tx_called; + adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt = 0; + + /* Prevent this function from being called during SSR since TL + * context may not be reinitialized at this time which may + * lead to a crash. + */ + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state() || + cds_is_load_or_unload_in_progress()) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Recovery/(Un)load in Progress. Ignore!!!", + __func__); + goto drop_pkt; + } + + if (hdd_ctx->hdd_wlan_suspended) { + hdd_err_rl("Device is system suspended, drop pkt"); + goto drop_pkt; + } + + /* + * If the device is operating on a DFS Channel + * then check if SAP is in CAC WAIT state and + * drop the packets. In CAC WAIT state device + * is expected not to transmit any frames. + * SAP starts Tx only after the BSS START is + * done. + */ + if (ap_ctx->dfs_cac_block_tx) + goto drop_pkt; + + if (ap_ctx->hostapd_state.bss_state != BSS_START) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: SAP is not in START state (%d). Ignore!!!", + __func__, + ap_ctx->hostapd_state.bss_state); + goto drop_pkt; + } + + /* + * If a transmit function is not registered, drop packet + */ + if (!adapter->tx_fn) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: TX function not registered by the data path", + __func__); + goto drop_pkt; + } + + wlan_hdd_classify_pkt(skb); + + dest_mac_addr = (struct qdf_mac_addr *)skb->data; + + /* In case of mcast, fetch the bcast sta_info. Else use the pkt addr */ + if (QDF_NBUF_CB_GET_IS_MCAST(skb)) + mac_addr = &bcast_mac_addr; + else + mac_addr = dest_mac_addr; + + sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, + mac_addr->bytes, + STA_INFO_SOFTAP_HARD_START_XMIT); + + if (!QDF_NBUF_CB_GET_IS_BCAST(skb) && !QDF_NBUF_CB_GET_IS_MCAST(skb)) { + if (!sta_info) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Failed to find right station", __func__); + goto drop_pkt; + } + + if (sta_info->is_deauth_in_progress) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s: STA " QDF_MAC_ADDR_FMT + "deauth in progress", __func__, + QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); + goto drop_pkt; + } + + if (sta_info->peer_state != OL_TXRX_PEER_STATE_CONN && + sta_info->peer_state != OL_TXRX_PEER_STATE_AUTH) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Station not connected yet", __func__); + goto drop_pkt; + } + + if (sta_info->peer_state == OL_TXRX_PEER_STATE_CONN) { + if (ntohs(skb->protocol) != HDD_ETHERTYPE_802_1_X) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s: NON-EAPOL packet in non-Authenticated state", + __func__); + goto drop_pkt; + } + } + } + + if (QDF_NBUF_CB_GET_IS_BCAST(skb) || QDF_NBUF_CB_GET_IS_MCAST(skb)) + hdd_get_tx_resource( + adapter, &adapter->mac_addr, + WLAN_SAP_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); + else + hdd_get_tx_resource( + adapter, dest_mac_addr, + WLAN_SAP_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); + + /* Get TL AC corresponding to Qdisc queue index/AC. */ + ac = hdd_qdisc_ac_to_tl_ac[skb->queue_mapping]; + ++adapter->hdd_stats.tx_rx_stats.tx_classified_ac[ac]; + +#if defined(IPA_OFFLOAD) + if (!qdf_nbuf_ipa_owned_get(skb)) { +#endif + + skb = hdd_skb_orphan(adapter, skb); + if (!skb) + goto drop_pkt_accounting; + +#if defined(IPA_OFFLOAD) + } else { + /* + * Clear the IPA ownership after check it to avoid ipa_free_skb + * is called when Tx completed for intra-BSS Tx packets + */ + qdf_nbuf_ipa_owned_clear(skb); + } +#endif + + /* + * Add SKB to internal tracking table before further processing + * in WLAN driver. + */ + qdf_net_buf_debug_acquire_skb(skb, __FILE__, __LINE__); + + adapter->stats.tx_bytes += skb->len; + + if (sta_info) { + sta_info->tx_bytes += skb->len; + + if (qdf_nbuf_is_tso(skb)) { + num_seg = qdf_nbuf_get_tso_num_seg(skb); + adapter->stats.tx_packets += num_seg; + sta_info->tx_packets += num_seg; + } else { + ++adapter->stats.tx_packets; + sta_info->tx_packets++; + hdd_ctx->no_tx_offload_pkt_cnt++; + } + sta_info->last_tx_rx_ts = qdf_system_ticks(); + } + + QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_NOTIFY_COMP(skb) = 0; + + hdd_softap_inspect_dhcp_packet(adapter, skb, QDF_TX); + hdd_softap_inspect_tx_eap_pkt(adapter, skb, false); + + hdd_event_eapol_log(skb, QDF_TX); + QDF_NBUF_CB_TX_PACKET_TRACK(skb) = QDF_NBUF_TX_PKT_DATA_TRACK; + QDF_NBUF_UPDATE_TX_PKT_COUNT(skb, QDF_NBUF_TX_PKT_HDD); + qdf_dp_trace_set_track(skb, QDF_TX); + DPTRACE(qdf_dp_trace(skb, QDF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, qdf_nbuf_data_addr(skb), + sizeof(qdf_nbuf_data(skb)), + QDF_TX)); + + /* check whether need to linearize skb, like non-linear udp data */ + if (hdd_skb_nontso_linearize(skb) != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s: skb %pK linearize failed. drop the pkt", + __func__, skb); + ++adapter->hdd_stats.tx_rx_stats.tx_dropped_ac[ac]; + goto drop_pkt_and_release_skb; + } + + if (adapter->tx_fn(soc, adapter->vdev_id, (qdf_nbuf_t)skb)) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Failed to send packet to txrx for sta: " + QDF_MAC_ADDR_FMT, __func__, + QDF_MAC_ADDR_REF(dest_mac_addr->bytes)); + ++adapter->hdd_stats.tx_rx_stats.tx_dropped_ac[ac]; + goto drop_pkt_and_release_skb; + } + netif_trans_update(dev); + + wlan_hdd_sar_unsolicited_timer_start(hdd_ctx); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_HARD_START_XMIT); + + return; + +drop_pkt_and_release_skb: + qdf_net_buf_debug_release_skb(skb); +drop_pkt: + qdf_dp_trace_data_pkt(skb, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_DP_TRACE_DROP_PACKET_RECORD, 0, + QDF_TX); + kfree_skb(skb); + +drop_pkt_accounting: + if (sta_info) + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_HARD_START_XMIT); + ++adapter->stats.tx_dropped; + ++adapter->hdd_stats.tx_rx_stats.tx_dropped; +} + +netdev_tx_t hdd_softap_hard_start_xmit(struct sk_buff *skb, + struct net_device *net_dev) +{ + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(net_dev, &vdev_sync)) { + hdd_debug_rl("Operation on net_dev is not permitted"); + kfree_skb(skb); + return NETDEV_TX_OK; + } + + __hdd_softap_hard_start_xmit(skb, net_dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return NETDEV_TX_OK; +} + +QDF_STATUS hdd_softap_ipa_start_xmit(qdf_nbuf_t nbuf, qdf_netdev_t dev) +{ + if (NETDEV_TX_OK == hdd_softap_hard_start_xmit( + (struct sk_buff *)nbuf, + (struct net_device *)dev)) + return QDF_STATUS_SUCCESS; + else + return QDF_STATUS_E_FAILURE; +} + +static void __hdd_softap_tx_timeout(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + struct netdev_queue *txq; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + int i; + + DPTRACE(qdf_dp_trace(NULL, QDF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT, + QDF_TRACE_DEFAULT_PDEV_ID, + NULL, 0, QDF_TX)); + /* Getting here implies we disabled the TX queues for too + * long. Queues are disabled either because of disassociation + * or low resource scenarios. In case of disassociation it is + * ok to ignore this. But if associated, we have do possible + * recovery here + */ + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: Recovery in Progress. Ignore!!!", __func__); + return; + } + + if (hdd_ctx->hdd_wlan_suspended) { + hdd_debug("wlan is suspended, ignore timeout"); + return; + } + + TX_TIMEOUT_TRACE(dev, QDF_MODULE_ID_HDD_SAP_DATA); + + for (i = 0; i < NUM_TX_QUEUES; i++) { + txq = netdev_get_tx_queue(dev, i); + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_DEBUG, + "Queue: %d status: %d txq->trans_start: %lu", + i, netif_tx_queue_stopped(txq), txq->trans_start); + } + + wlan_hdd_display_adapter_netif_queue_history(adapter); + + cdp_dump_flow_pool_info(cds_get_context(QDF_MODULE_ID_SOC)); + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "carrier state: %d", netif_carrier_ok(dev)); + + ++adapter->hdd_stats.tx_rx_stats.tx_timeout_cnt; + ++adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt; + + if (adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt > + HDD_TX_STALL_THRESHOLD) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "Detected data stall due to continuous TX timeouts"); + adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt = 0; + + if (cdp_cfg_get(soc, cfg_dp_enable_data_stall)) + cdp_post_data_stall_event(soc, + DATA_STALL_LOG_INDICATOR_HOST_DRIVER, + DATA_STALL_LOG_HOST_SOFTAP_TX_TIMEOUT, + OL_TXRX_PDEV_ID, 0xFF, + DATA_STALL_LOG_RECOVERY_TRIGGER_PDR); + } +} + +void hdd_softap_tx_timeout(struct net_device *net_dev) +{ + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(net_dev, &vdev_sync)) + return; + + __hdd_softap_tx_timeout(net_dev); + + osif_vdev_sync_op_stop(vdev_sync); +} + +void hdd_softap_init_tx_rx(struct hdd_adapter *adapter) +{ + qdf_mem_zero(&adapter->stats, sizeof(struct net_device_stats)); +} + +QDF_STATUS hdd_softap_deinit_tx_rx(struct hdd_adapter *adapter) +{ + QDF_BUG(adapter); + if (!adapter) + return QDF_STATUS_E_FAILURE; + + adapter->tx_fn = NULL; + + return QDF_STATUS_SUCCESS; +} + +static void +hdd_reset_sta_info_during_reattach(struct hdd_station_info *sta_info) +{ + sta_info->in_use = 0; + sta_info->sta_id = 0; + sta_info->sta_type = 0; + qdf_mem_zero(&sta_info->sta_mac, QDF_MAC_ADDR_SIZE); + sta_info->peer_state = 0; + sta_info->is_qos_enabled = 0; + sta_info->is_deauth_in_progress = 0; + sta_info->nss = 0; + sta_info->rate_flags = 0; + sta_info->ecsa_capable = 0; + sta_info->max_phy_rate = 0; + sta_info->tx_packets = 0; + sta_info->tx_bytes = 0; + sta_info->rx_packets = 0; + sta_info->rx_bytes = 0; + sta_info->last_tx_rx_ts = 0; + sta_info->assoc_ts = 0; + sta_info->disassoc_ts = 0; + sta_info->tx_rate = 0; + sta_info->rx_rate = 0; + sta_info->ampdu = 0; + sta_info->sgi_enable = 0; + sta_info->tx_stbc = 0; + sta_info->rx_stbc = 0; + sta_info->ch_width = 0; + sta_info->mode = 0; + sta_info->max_supp_idx = 0; + sta_info->max_ext_idx = 0; + sta_info->max_mcs_idx = 0; + sta_info->rx_mcs_map = 0; + sta_info->tx_mcs_map = 0; + sta_info->freq = 0; + sta_info->dot11_mode = 0; + sta_info->ht_present = 0; + sta_info->vht_present = 0; + qdf_mem_zero(&sta_info->ht_caps, sizeof(sta_info->ht_caps)); + qdf_mem_zero(&sta_info->vht_caps, sizeof(sta_info->vht_caps)); + sta_info->reason_code = 0; + sta_info->rssi = 0; + sta_info->dhcp_phase = 0; + sta_info->dhcp_nego_status = 0; + sta_info->capability = 0; + sta_info->support_mode = 0; + sta_info->rx_retry_cnt = 0; + sta_info->rx_mc_bc_cnt = 0; + + if (sta_info->assoc_req_ies.len) { + qdf_mem_free(sta_info->assoc_req_ies.data); + sta_info->assoc_req_ies.data = NULL; + sta_info->assoc_req_ies.len = 0; + } + + sta_info->pending_eap_frm_type = 0; +} + +/** + * hdd_sta_info_re_attach() - Re-Attach the station info structure into the list + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: The station info structure that is to be attached to the + * container object. + * + * This function re-attaches the station if it gets re-connect after + * disconnecting and before its all references are released. + * + * Return: QDF STATUS SUCCESS on successful attach, error code otherwise + */ + +static QDF_STATUS hdd_sta_info_re_attach( + struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info *sta_info, + struct qdf_mac_addr *sta_mac) +{ + if (!sta_info_container || !sta_info) { + hdd_err("Parameter(s) null"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); + + if (sta_info->is_attached) { + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); + hdd_err("sta info is alredy attached"); + return QDF_STATUS_SUCCESS; + } + + hdd_reset_sta_info_during_reattach(sta_info); + /* Add one extra ref for reattach */ + hdd_take_sta_info_ref(sta_info_container, sta_info, false, + STA_INFO_ATTACH_DETACH); + qdf_mem_copy(&sta_info->sta_mac, sta_mac, sizeof(struct qdf_mac_addr)); + sta_info->is_attached = true; + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_softap_init_tx_rx_sta(struct hdd_adapter *adapter, + struct qdf_mac_addr *sta_mac) +{ + struct hdd_station_info *sta_info; + QDF_STATUS status; + + sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, + sta_mac->bytes, + STA_INFO_SOFTAP_INIT_TX_RX_STA); + + if (sta_info) { + hdd_err("Reinit of in use station " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sta_mac->bytes)); + status = hdd_sta_info_re_attach(&adapter->sta_info_list, + sta_info, sta_mac); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_INIT_TX_RX_STA); + return status; + } + + sta_info = qdf_mem_malloc(sizeof(struct hdd_station_info)); + if (!sta_info) + return QDF_STATUS_E_NOMEM; + + sta_info->is_deauth_in_progress = false; + qdf_mem_copy(&sta_info->sta_mac, sta_mac, sizeof(struct qdf_mac_addr)); + + status = hdd_sta_info_attach(&adapter->sta_info_list, sta_info); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to attach station: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sta_mac->bytes)); + qdf_mem_free(sta_info); + } + + return status; +} + +/** + * hdd_softap_tsf_timestamp_rx() - time stamp Rx netbuf + * @context: pointer to HDD context + * @netbuf: pointer to a Rx netbuf + * + * Return: None + */ +#ifdef WLAN_FEATURE_TSF_PLUS +static inline void hdd_softap_tsf_timestamp_rx(struct hdd_context *hdd_ctx, + qdf_nbuf_t netbuf) +{ + uint64_t target_time; + + if (!hdd_tsf_is_rx_set(hdd_ctx)) + return; + + target_time = ktime_to_us(netbuf->tstamp); + hdd_rx_timestamp(netbuf, target_time); +} +#else +static inline void hdd_softap_tsf_timestamp_rx(struct hdd_context *hdd_ctx, + qdf_nbuf_t netbuf) +{ +} +#endif + +/** + * hdd_softap_notify_tx_compl_cbk() - callback to notify tx completion + * @skb: pointer to skb data + * @adapter: pointer to vdev apdapter + * + * Return: None + */ +static void hdd_softap_notify_tx_compl_cbk(struct sk_buff *skb, + void *context) +{ + int errno; + struct hdd_adapter *adapter = context; + + errno = hdd_validate_adapter(adapter); + if (errno) + return; + + if (QDF_NBUF_CB_PACKET_TYPE_DHCP == QDF_NBUF_CB_GET_PACKET_TYPE(skb)) { + hdd_debug("sending DHCP indication"); + hdd_softap_notify_dhcp_ind(context, skb); + } else if (QDF_NBUF_CB_GET_PACKET_TYPE(skb) == + QDF_NBUF_CB_PACKET_TYPE_EAPOL) { + hdd_softap_inspect_tx_eap_pkt(adapter, skb, true); + } +} + +QDF_STATUS hdd_softap_rx_packet_cbk(void *adapter_context, qdf_nbuf_t rx_buf) +{ + struct hdd_adapter *adapter = NULL; + QDF_STATUS qdf_status; + unsigned int cpu_index; + struct sk_buff *skb = NULL; + struct sk_buff *next = NULL; + struct hdd_context *hdd_ctx = NULL; + struct qdf_mac_addr *src_mac; + struct hdd_station_info *sta_info; + + /* Sanity check on inputs */ + if (unlikely((!adapter_context) || (!rx_buf))) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: Null params being passed", __func__); + return QDF_STATUS_E_FAILURE; + } + + adapter = (struct hdd_adapter *)adapter_context; + if (unlikely(WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "Magic cookie(%x) for adapter sanity verification is invalid", + adapter->magic); + return QDF_STATUS_E_FAILURE; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (unlikely(!hdd_ctx)) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: HDD context is Null", __func__); + return QDF_STATUS_E_FAILURE; + } + + /* walk the chain until all are processed */ + next = (struct sk_buff *)rx_buf; + + while (next) { + skb = next; + next = skb->next; + skb->next = NULL; + +/* Debug code, remove later */ +#if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ + defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "%s: skb %pK skb->len %d\n", __func__, skb, skb->len); +#endif + + hdd_softap_dump_sk_buff(skb); + + skb->dev = adapter->dev; + + if (unlikely(!skb->dev)) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, + QDF_TRACE_LEVEL_ERROR, + "%s: ERROR!!Invalid netdevice", __func__); + qdf_nbuf_free(skb); + continue; + } + cpu_index = wlan_hdd_get_cpu(); + ++adapter->hdd_stats.tx_rx_stats.rx_packets[cpu_index]; + ++adapter->stats.rx_packets; + adapter->stats.rx_bytes += skb->len; + + /* Send DHCP Indication to FW */ + src_mac = (struct qdf_mac_addr *)(skb->data + + QDF_NBUF_SRC_MAC_OFFSET); + sta_info = hdd_get_sta_info_by_mac( + &adapter->sta_info_list, + (uint8_t *)src_mac, + STA_INFO_SOFTAP_RX_PACKET_CBK); + + if (sta_info) { + sta_info->rx_packets++; + sta_info->rx_bytes += skb->len; + sta_info->last_tx_rx_ts = qdf_system_ticks(); + hdd_softap_inspect_dhcp_packet(adapter, skb, QDF_RX); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, + true, + STA_INFO_SOFTAP_RX_PACKET_CBK); + } + + if (qdf_unlikely(qdf_nbuf_is_ipv4_eapol_pkt(skb) && + qdf_mem_cmp(qdf_nbuf_data(skb) + + QDF_NBUF_DEST_MAC_OFFSET, + adapter->mac_addr.bytes, + QDF_MAC_ADDR_SIZE))) { + qdf_nbuf_free(skb); + continue; + } + + hdd_event_eapol_log(skb, QDF_RX); + qdf_dp_trace_log_pkt(adapter->vdev_id, + skb, QDF_RX, QDF_TRACE_DEFAULT_PDEV_ID); + DPTRACE(qdf_dp_trace(skb, + QDF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(skb), + sizeof(qdf_nbuf_data(skb)), QDF_RX)); + DPTRACE(qdf_dp_trace_data_pkt(skb, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_DP_TRACE_RX_PACKET_RECORD, 0, QDF_RX)); + + skb->protocol = eth_type_trans(skb, skb->dev); + + /* hold configurable wakelock for unicast traffic */ + if (!hdd_is_current_high_throughput(hdd_ctx) && + hdd_ctx->config->rx_wakelock_timeout && + skb->pkt_type != PACKET_BROADCAST && + skb->pkt_type != PACKET_MULTICAST) { + cds_host_diag_log_work(&hdd_ctx->rx_wake_lock, + hdd_ctx->config->rx_wakelock_timeout, + WIFI_POWER_EVENT_WAKELOCK_HOLD_RX); + qdf_wake_lock_timeout_acquire(&hdd_ctx->rx_wake_lock, + hdd_ctx->config-> + rx_wakelock_timeout); + } + + /* Remove SKB from internal tracking table before submitting + * it to stack + */ + qdf_net_buf_debug_release_skb(skb); + + hdd_softap_tsf_timestamp_rx(hdd_ctx, skb); + + qdf_status = hdd_rx_deliver_to_stack(adapter, skb); + + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + ++adapter->hdd_stats.tx_rx_stats.rx_delivered[cpu_index]; + else + ++adapter->hdd_stats.tx_rx_stats.rx_refused[cpu_index]; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_softap_deregister_sta(struct hdd_adapter *adapter, + struct hdd_station_info **sta_info) +{ + struct hdd_context *hdd_ctx; + struct qdf_mac_addr *mac_addr; + struct hdd_station_info *sta = *sta_info; + + if (!adapter) { + hdd_err("NULL adapter"); + return QDF_STATUS_E_INVAL; + } + + if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { + hdd_err("Invalid adapter magic"); + return QDF_STATUS_E_INVAL; + } + + if (!sta) { + hdd_err("Invalid station"); + return QDF_STATUS_E_INVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return QDF_STATUS_E_INVAL; + } + + /* + * If the address is a broadcast address then the CDP layers expects + * the self mac address of the adapter. + */ + if (QDF_IS_ADDR_BROADCAST(sta->sta_mac.bytes)) + mac_addr = &adapter->mac_addr; + else + mac_addr = &sta->sta_mac; + + if (ucfg_ipa_is_enabled()) { + if (ucfg_ipa_wlan_evt(hdd_ctx->pdev, adapter->dev, + adapter->device_mode, + adapter->vdev_id, + WLAN_IPA_CLIENT_DISCONNECT, + mac_addr->bytes) != QDF_STATUS_SUCCESS) + hdd_debug("WLAN_CLIENT_DISCONNECT event failed"); + } + hdd_sta_info_detach(&adapter->sta_info_list, &sta); + + ucfg_mlme_update_oce_flags(hdd_ctx->pdev); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS hdd_softap_register_sta(struct hdd_adapter *adapter, + bool auth_required, + bool privacy_required, + struct qdf_mac_addr *sta_mac, + bool wmm_enabled) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct ol_txrx_desc_type txrx_desc = {0}; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct ol_txrx_ops txrx_ops; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct hdd_ap_ctx *ap_ctx; + struct hdd_station_info *sta_info; + + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + + /* + * If the address is a broadcast address, then provide the self mac addr + * to the data path. Else provide the mac address of the connected peer. + */ + if (qdf_is_macaddr_broadcast(sta_mac) && ap_ctx) + qdf_mem_copy(&txrx_desc.peer_addr, &adapter->mac_addr, + QDF_MAC_ADDR_SIZE); + else + qdf_mem_copy(&txrx_desc.peer_addr, sta_mac, + QDF_MAC_ADDR_SIZE); + + qdf_status = hdd_softap_init_tx_rx_sta(adapter, sta_mac); + sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, + sta_mac->bytes, + STA_INFO_SOFTAP_REGISTER_STA); + + if (!sta_info) { + hdd_debug("STA not found"); + return QDF_STATUS_E_INVAL; + } + + txrx_desc.is_qos_enabled = wmm_enabled; + + /* Register the vdev transmit and receive functions */ + qdf_mem_zero(&txrx_ops, sizeof(txrx_ops)); + + txrx_ops.tx.tx_comp = hdd_softap_notify_tx_compl_cbk; + + if (adapter->hdd_ctx->enable_dp_rx_threads) { + txrx_ops.rx.rx = hdd_rx_pkt_thread_enqueue_cbk; + txrx_ops.rx.rx_stack = hdd_softap_rx_packet_cbk; + txrx_ops.rx.rx_flush = hdd_rx_flush_packet_cbk; + txrx_ops.rx.rx_gro_flush = hdd_rx_thread_gro_flush_ind_cbk; + } else { + txrx_ops.rx.rx = hdd_softap_rx_packet_cbk; + txrx_ops.rx.rx_stack = NULL; + txrx_ops.rx.rx_flush = NULL; + } + + cdp_vdev_register(soc, + adapter->vdev_id, + (ol_osif_vdev_handle)adapter, + &txrx_ops); + adapter->tx_fn = txrx_ops.tx.tx; + + qdf_status = cdp_peer_register(soc, OL_TXRX_PDEV_ID, &txrx_desc); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_debug("cdp_peer_register() failed to register. Status = %d [0x%08X]", + qdf_status, qdf_status); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_REGISTER_STA); + return qdf_status; + } + + /* if ( WPA ), tell TL to go to 'connected' and after keys come to the + * driver then go to 'authenticated'. For all other authentication + * types (those that do not require upper layer authentication) we can + * put TL directly into 'authenticated' state + */ + + sta_info->is_qos_enabled = wmm_enabled; + + if (!auth_required) { + hdd_debug("open/shared auth STA MAC= " QDF_MAC_ADDR_FMT + ". Changing TL state to AUTHENTICATED at Join time", + QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); + + /* Connections that do not need Upper layer auth, + * transition TL directly to 'Authenticated' state. + */ + qdf_status = hdd_change_peer_state(adapter, + txrx_desc.peer_addr.bytes, + OL_TXRX_PEER_STATE_AUTH, + false); + + sta_info->peer_state = OL_TXRX_PEER_STATE_AUTH; + if (!qdf_is_macaddr_broadcast(sta_mac)) + qdf_status = wlan_hdd_send_sta_authorized_event( + adapter, hdd_ctx, + sta_mac); + } else { + + hdd_debug("ULA auth STA MAC = " QDF_MAC_ADDR_FMT + ". Changing TL state to CONNECTED at Join time", + QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); + + qdf_status = hdd_change_peer_state(adapter, + txrx_desc.peer_addr.bytes, + OL_TXRX_PEER_STATE_CONN, + false); + + sta_info->peer_state = OL_TXRX_PEER_STATE_CONN; + } + + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_REGISTER_STA); + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, + WLAN_CONTROL_PATH); + ucfg_mlme_update_oce_flags(hdd_ctx->pdev); + return qdf_status; +} + +/** + * hdd_softap_register_bc_sta() - Register the SoftAP broadcast STA + * @adapter: pointer to adapter context + * @privacy_required: should 802.11 privacy bit be set? + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS hdd_softap_register_bc_sta(struct hdd_adapter *adapter, + bool privacy_required) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct qdf_mac_addr broadcast_macaddr = QDF_MAC_ADDR_BCAST_INIT; + struct hdd_ap_ctx *ap_ctx; + uint8_t sta_id; + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + sta_id = ap_ctx->broadcast_sta_id; + + if (sta_id >= WLAN_MAX_STA_COUNT) { + hdd_err("Error: Invalid sta_id: %u", sta_id); + return qdf_status; + } + + qdf_status = hdd_softap_register_sta(adapter, false, + privacy_required, + &broadcast_macaddr, 0); + + return qdf_status; +} + +QDF_STATUS hdd_softap_stop_bss(struct hdd_adapter *adapter) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint8_t indoor_chnl_marking = 0; + struct hdd_context *hdd_ctx; + struct hdd_ap_ctx *ap_ctx; + struct hdd_station_info *sta_info, *tmp = NULL; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); + + status = ucfg_policy_mgr_get_indoor_chnl_marking(hdd_ctx->psoc, + &indoor_chnl_marking); + if (QDF_STATUS_SUCCESS != status) + hdd_err("can't get indoor channel marking, using default"); + /* This is stop bss callback running in scheduler thread so do not + * driver unload in progress check otherwise it can lead to peer + * object leak + */ + + hdd_for_each_sta_ref_safe(adapter->sta_info_list, sta_info, tmp, + STA_INFO_SOFTAP_STOP_BSS) { + status = hdd_softap_deregister_sta(adapter, &sta_info); + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_STOP_BSS); + } + + if (adapter->device_mode == QDF_SAP_MODE && + !hdd_ctx->config->disable_channel) + wlan_hdd_restore_channels(hdd_ctx); + + /* Mark the indoor channel (passive) to enable */ + if (indoor_chnl_marking && adapter->device_mode == QDF_SAP_MODE) { + hdd_update_indoor_channel(hdd_ctx, false); + sme_update_channel_list(hdd_ctx->mac_handle); + } + + if (ucfg_ipa_is_enabled()) { + if (ucfg_ipa_wlan_evt(hdd_ctx->pdev, + adapter->dev, + adapter->device_mode, + adapter->vdev_id, + WLAN_IPA_AP_DISCONNECT, + adapter->dev->dev_addr) != + QDF_STATUS_SUCCESS) + hdd_err("WLAN_AP_DISCONNECT event failed"); + } + + return status; +} + +QDF_STATUS hdd_softap_change_sta_state(struct hdd_adapter *adapter, + struct qdf_mac_addr *sta_mac, + enum ol_txrx_peer_state state) +{ + QDF_STATUS qdf_status; + struct hdd_station_info *sta_info; + struct qdf_mac_addr mac_addr; + + hdd_enter_dev(adapter->dev); + + sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, + sta_mac->bytes, + STA_INFO_SOFTAP_CHANGE_STA_STATE); + + if (!sta_info) { + hdd_debug("Failed to find right station MAC: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sta_mac->bytes)); + return QDF_STATUS_E_INVAL; + } + + if (qdf_is_macaddr_broadcast(&sta_info->sta_mac)) + qdf_mem_copy(&mac_addr, &adapter->mac_addr, QDF_MAC_ADDR_SIZE); + else + qdf_mem_copy(&mac_addr, sta_mac, QDF_MAC_ADDR_SIZE); + + qdf_status = + hdd_change_peer_state(adapter, mac_addr.bytes, + state, false); + hdd_debug("Station " QDF_MAC_ADDR_FMT " changed to state %d", + QDF_MAC_ADDR_REF(mac_addr.bytes), state); + + if (QDF_STATUS_SUCCESS == qdf_status) { + sta_info->peer_state = OL_TXRX_PEER_STATE_AUTH; + p2p_peer_authorized(adapter->vdev, sta_mac->bytes); + } + + hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, + STA_INFO_SOFTAP_CHANGE_STA_STATE); + hdd_exit(); + return qdf_status; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_spectralscan.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_spectralscan.c new file mode 100644 index 0000000000000000000000000000000000000000..6733472649d077718b6473e10dadcb292ce3478d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_spectralscan.c @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_spectral_scan.c + * + * WLAN Host Device Driver Spectral Scan Implementation + */ + +#include +#include +#include +#include +#include "osif_sync.h" +#include "wlan_hdd_includes.h" +#include "cds_api.h" +#include "ani_global.h" +#include "wlan_cfg80211_spectral.h" +#include "wlan_hdd_spectralscan.h" +#include +#include "wma.h" +#ifdef CNSS_GENL +#include +#endif + +/** + * __wlan_hdd_cfg80211_spectral_scan_start() - start spectral scan + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function starts spectral scan + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_spectral_scan_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter; + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + wlan_spectral_update_rx_chainmask(adapter); + ret = wlan_cfg80211_spectral_scan_config_and_start(wiphy, + hdd_ctx->pdev, + data, data_len); + hdd_exit(); + + return ret; +} + +/** + * __wlan_hdd_cfg80211_spectral_scan_stop() - stop spectral scan + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function stops spectral scan + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_spectral_scan_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_cfg80211_spectral_scan_stop(wiphy, hdd_ctx->pdev, + data, data_len); + hdd_exit(); + + return ret; +} + +/** + * __wlan_hdd_cfg80211_spectral_scan_get_config() - spectral scan get config + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function to get the spectral scan configuration + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_spectral_scan_get_config( + struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_cfg80211_spectral_scan_get_config(wiphy, hdd_ctx->pdev, + data, data_len); + hdd_exit(); + + return ret; +} + +/** + * __wlan_hdd_cfg80211_spectral_scan_get_diag_stats() - get diag stats + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function gets the spectral scan diag stats + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_spectral_scan_get_diag_stats( + struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_cfg80211_spectral_scan_get_diag_stats(wiphy, + hdd_ctx->pdev, + data, data_len); + hdd_exit(); + + return ret; +} + +/** + * __wlan_hdd_cfg80211_spectral_scan_get_cap_info() - get spectral caps + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function gets spectral scan configured capabilities + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_spectral_scan_get_cap_info( + struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_cfg80211_spectral_scan_get_cap(wiphy, hdd_ctx->pdev, + data, data_len); + hdd_exit(); + + return ret; +} + +/* + * __wlan_hdd_cfg80211_spectral_scan_get_status() - get spectral scan + * status + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function gets current status of spectral scan + * + * Return: 0 on success and errno on failure + */ +static int __wlan_hdd_cfg80211_spectral_scan_get_status( + struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_cfg80211_spectral_scan_get_status(wiphy, hdd_ctx->pdev, + data, data_len); + hdd_exit(); + + return ret; +} + +int wlan_hdd_cfg80211_spectral_scan_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_spectral_scan_start(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +int wlan_hdd_cfg80211_spectral_scan_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_spectral_scan_stop(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +int wlan_hdd_cfg80211_spectral_scam_get_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_spectral_scan_get_config(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +int wlan_hdd_cfg80211_spectral_scan_get_diag_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_spectral_scan_get_diag_stats(wiphy, wdev, + data, + data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +int wlan_hdd_cfg80211_spectral_scan_get_cap_info(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_spectral_scan_get_cap_info(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +int wlan_hdd_cfg80211_spectral_scan_get_status(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_spectral_scan_get_status(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +void hdd_spectral_register_to_dbr(struct hdd_context *hdd_ctx) +{ + ucfg_spectral_register_to_dbr(hdd_ctx->pdev); +} + +#if defined(CNSS_GENL) && defined(WLAN_CONV_SPECTRAL_ENABLE) +static void spectral_get_version(struct wlan_objmgr_pdev *pdev, + uint32_t *version, + uint32_t *sub_version) +{ + struct spectral_cp_request sscan_req; + QDF_STATUS status; + + if (!pdev || !version || !sub_version) { + hdd_err("invalid param"); + return; + } + + sscan_req.ss_mode = SPECTRAL_SCAN_MODE_NORMAL; + sscan_req.req_id = SPECTRAL_GET_CAPABILITY_INFO; + status = ucfg_spectral_control(pdev, &sscan_req); + if (QDF_IS_STATUS_ERROR(status)) { + *version = SPECTRAL_VERSION_2; + *sub_version = SPECTRAL_SUB_VERSION_0; + hdd_err("get spectral cap fail"); + return; + } + + switch (sscan_req.caps_req.sscan_caps.hw_gen) { + case 0: + *version = SPECTRAL_VERSION_1; + *sub_version = SPECTRAL_SUB_VERSION_0; + break; + case 1: + *version = SPECTRAL_VERSION_2; + *sub_version = SPECTRAL_SUB_VERSION_1; + break; + case 2: + *version = SPECTRAL_VERSION_3; + *sub_version = SPECTRAL_SUB_VERSION_0; + break; + default: + *version = SPECTRAL_VERSION_2; + *sub_version = SPECTRAL_SUB_VERSION_0; + hdd_err("invalid hw gen"); + break; + } +} + +static void send_spectral_scan_reg_rsp_msg(struct hdd_context *hdd_ctx) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct spectral_scan_msg_v *rsp_msg; + int err; + + skb = alloc_skb(NLMSG_SPACE(sizeof(struct spectral_scan_msg_v)), + GFP_KERNEL); + if (!skb) { + hdd_err("Skb allocation failed"); + return; + } + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_pid = 0; + nlh->nlmsg_flags = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_type = WLAN_NL_MSG_SPECTRAL_SCAN; + + rsp_msg = NLMSG_DATA(nlh); + rsp_msg->msg_type = SPECTRAL_SCAN_REGISTER_RSP; + rsp_msg->pid = hdd_ctx->sscan_pid; + spectral_get_version(hdd_ctx->pdev, &rsp_msg->version, + &rsp_msg->sub_version); + + nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct spectral_scan_msg_v)); + skb_put(skb, NLMSG_SPACE(sizeof(struct spectral_scan_msg_v))); + + hdd_info("sending App Reg Response to process pid %d", + hdd_ctx->sscan_pid); + + err = nl_srv_ucast(skb, hdd_ctx->sscan_pid, MSG_DONTWAIT, + WLAN_NL_MSG_SPECTRAL_SCAN, CLD80211_MCGRP_OEM_MSGS); + + if (err < 0) + hdd_err("SPECTRAL: failed to send to spectral scan reg" + " response"); +} + +/** + * __spectral_scan_msg_handler() - API to handle spectral scan command + * @data: Data received + * @data_len: length of the data received + * @ctx: Pointer to stored context + * @pid: Process ID + * + * API to handle spectral scan commands from user space + * + * Return: None + */ +static void __spectral_scan_msg_handler(const void *data, int data_len, + void *ctx, int pid) +{ + struct spectral_scan_msg *ss_msg = NULL; + struct nlattr *tb[CLD80211_ATTR_MAX + 1]; + struct hdd_context *hdd_ctx; + int ret; + + hdd_ctx = (struct hdd_context *)cds_get_context(QDF_MODULE_ID_HDD); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return; + + /* + * audit note: it is ok to pass a NULL policy here since only + * one attribute is parsed and it is explicitly validated + */ + if (wlan_cfg80211_nla_parse(tb, CLD80211_ATTR_MAX, data, + data_len, NULL)) { + hdd_err("nla parse fails"); + return; + } + + if (!tb[CLD80211_ATTR_DATA]) { + hdd_err("attr VENDOR_DATA fails"); + return; + } + + if (nla_len(tb[CLD80211_ATTR_DATA]) < sizeof(*ss_msg)) { + hdd_err_rl("Invalid length for ATTR_DATA"); + return; + } + + ss_msg = (struct spectral_scan_msg *)nla_data(tb[CLD80211_ATTR_DATA]); + + if (!ss_msg) { + hdd_err("data NULL"); + return; + } + + switch (ss_msg->msg_type) { + case SPECTRAL_SCAN_REGISTER_REQ: + hdd_ctx->sscan_pid = ss_msg->pid; + hdd_debug("spectral scan application registered, pid=%d", + hdd_ctx->sscan_pid); + send_spectral_scan_reg_rsp_msg(hdd_ctx); + ucfg_spectral_scan_set_ppid(hdd_ctx->pdev, + hdd_ctx->sscan_pid); + break; + default: + hdd_warn("invalid message type %d", ss_msg->msg_type); + break; + } +} + +static void spectral_scan_msg_handler(const void *data, int data_len, + void *ctx, int pid) +{ + struct device *dev = ctx; + struct osif_psoc_sync *psoc_sync; + + if (osif_psoc_sync_op_start(dev, &psoc_sync)) + return; + + __spectral_scan_msg_handler(data, data_len, ctx, pid); + + osif_psoc_sync_op_stop(psoc_sync); +} + +void spectral_scan_activate_service(struct hdd_context *hdd_ctx) +{ + register_cld_cmd_cb(WLAN_NL_MSG_SPECTRAL_SCAN, + spectral_scan_msg_handler, hdd_ctx->parent_dev); +} + +void spectral_scan_deactivate_service(void) +{ + deregister_cld_cmd_cb(WLAN_NL_MSG_SPECTRAL_SCAN); +} + +QDF_STATUS wlan_spectral_update_rx_chainmask(struct hdd_adapter *adapter) +{ + uint32_t version; + uint32_t sub_version; + uint32_t chainmask_2g = 0; + uint32_t chainmask_5g = 0; + uint32_t chainmask; + uint8_t home_chan; + uint8_t pdev_id; + + spectral_get_version(adapter->hdd_ctx->pdev, &version, &sub_version); + if (version != SPECTRAL_VERSION_3) + return QDF_STATUS_SUCCESS; + + home_chan = hdd_get_adapter_home_channel(adapter); + pdev_id = wlan_objmgr_pdev_get_pdev_id(adapter->hdd_ctx->pdev); + wma_get_rx_chainmask(pdev_id, &chainmask_2g, &chainmask_5g); + chainmask = home_chan > MAX_24GHZ_CHANNEL ? chainmask_5g : chainmask_2g; + wlan_vdev_mlme_set_rxchainmask(adapter->vdev, chainmask); + + return QDF_STATUS_SUCCESS; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sta_info.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sta_info.c new file mode 100644 index 0000000000000000000000000000000000000000..6b1f81a15eab2c648668f7976e79893c9981a23f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sta_info.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_sta_info.c + * + * Store and manage station info structure. + * + */ + +#include +#include "wlan_hdd_sta_info.h" + +#define HDD_MAX_PEERS 32 + +char *sta_info_string_from_dbgid(wlan_sta_info_dbgid id) +{ + static const char *strings[] = { + "STA_INFO_ID_RESERVED", + "STA_INFO_CFG80211_GET_LINK_PROPERTIES", + "STA_INFO_SOFTAP_INSPECT_TX_EAP_PKT", + "STA_INFO_SOFTAP_CHECK_WAIT_FOR_TX_EAP_PKT", + "STA_INFO_SOFTAP_INSPECT_DHCP_PACKET", + "STA_INFO_SOFTAP_HARD_START_XMIT", + "STA_INFO_SOFTAP_INIT_TX_RX_STA", + "STA_INFO_SOFTAP_RX_PACKET_CBK", + "STA_INFO_SOFTAP_REGISTER_STA", + "STA_INFO_GET_CACHED_STATION_REMOTE", + "STA_INFO_HDD_GET_STATION_REMOTE", + "STA_INFO_WLAN_HDD_GET_STATION_REMOTE", + "STA_INFO_SOFTAP_DEAUTH_CURRENT_STA", + "STA_INFO_SOFTAP_DEAUTH_ALL_STA", + "STA_INFO_CFG80211_DEL_STATION", + "STA_INFO_HDD_CLEAR_ALL_STA", + "STA_INFO_FILL_STATION_INFO", + "STA_INFO_HOSTAPD_SAP_EVENT_CB", + "STA_INFO_SAP_INDICATE_DISCONNECT_FOR_STA", + "STA_INFO_IS_PEER_ASSOCIATED", + "STA_INFO_SAP_SET_TWO_INTS_GETNONE", + "STA_INFO_SAP_GETASSOC_STAMACADDR", + "STA_INFO_SOFTAP_GET_STA_INFO", + "STA_INFO_GET_SOFTAP_LINKSPEED", + "STA_INFO_CONNECTION_IN_PROGRESS_ITERATOR", + "STA_INFO_SOFTAP_STOP_BSS", + "STA_INFO_SOFTAP_CHANGE_STA_STATE", + "STA_INFO_CLEAR_CACHED_STA_INFO", + "STA_INFO_ATTACH_DETACH", + "STA_INFO_SHOW", + "STA_INFO_ID_MAX"}; + int32_t num_dbg_strings = QDF_ARRAY_SIZE(strings); + + if (id >= num_dbg_strings) { + char *ret = ""; + + hdd_err("Debug string not found for debug id %d", id); + return ret; + } + + return (char *)strings[id]; +} + +QDF_STATUS hdd_sta_info_init(struct hdd_sta_info_obj *sta_info_container) +{ + if (!sta_info_container) { + hdd_err("Parameter null"); + return QDF_STATUS_E_INVAL; + } + + qdf_spinlock_create(&sta_info_container->sta_obj_lock); + qdf_list_create(&sta_info_container->sta_obj, HDD_MAX_PEERS); + + return QDF_STATUS_SUCCESS; +} + +void hdd_sta_info_deinit(struct hdd_sta_info_obj *sta_info_container) +{ + if (!sta_info_container) { + hdd_err("Parameter null"); + return; + } + + qdf_list_destroy(&sta_info_container->sta_obj); + qdf_spinlock_destroy(&sta_info_container->sta_obj_lock); +} + +QDF_STATUS hdd_sta_info_attach(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info *sta_info) +{ + if (!sta_info_container || !sta_info) { + hdd_err("Parameter(s) null"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); + + hdd_take_sta_info_ref(sta_info_container, sta_info, false, + STA_INFO_ATTACH_DETACH); + qdf_list_insert_front(&sta_info_container->sta_obj, + &sta_info->sta_node); + sta_info->is_attached = true; + + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); + + return QDF_STATUS_SUCCESS; +} + +void hdd_sta_info_detach(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info **sta_info) +{ + struct hdd_station_info *info; + + if (!sta_info_container || !sta_info) { + hdd_err("Parameter(s) null"); + return; + } + + info = *sta_info; + + if (!info) + return; + + qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); + + if (info->is_attached) { + info->is_attached = false; + hdd_put_sta_info_ref(sta_info_container, sta_info, false, + STA_INFO_ATTACH_DETACH); + } else { + hdd_info("Stainfo is already detached"); + } + + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); +} + +struct hdd_station_info *hdd_get_sta_info_by_mac( + struct hdd_sta_info_obj *sta_info_container, + const uint8_t *mac_addr, + wlan_sta_info_dbgid sta_info_dbgid) +{ + struct hdd_station_info *sta_info = NULL; + + if (!mac_addr || !sta_info_container) { + hdd_err("Parameter(s) null"); + return NULL; + } + + qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); + + qdf_list_for_each(&sta_info_container->sta_obj, sta_info, sta_node) { + if (qdf_is_macaddr_equal(&sta_info->sta_mac, + (struct qdf_mac_addr *)mac_addr)) { + hdd_take_sta_info_ref(sta_info_container, + sta_info, false, sta_info_dbgid); + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); + return sta_info; + } + } + + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); + + return NULL; +} + +void hdd_take_sta_info_ref(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info *sta_info, + bool lock_required, + wlan_sta_info_dbgid sta_info_dbgid) +{ + if (!sta_info_container || !sta_info) { + hdd_err("Parameter(s) null"); + return; + } + + if (sta_info_dbgid >= STA_INFO_ID_MAX) { + hdd_err("Invalid sta_info debug id %d", sta_info_dbgid); + return; + } + + if (lock_required) + qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); + + qdf_atomic_inc(&sta_info->ref_cnt); + qdf_atomic_inc(&sta_info->ref_cnt_dbgid[sta_info_dbgid]); + + if (lock_required) + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); +} + +void +hdd_put_sta_info_ref(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info **sta_info, bool lock_required, + wlan_sta_info_dbgid sta_info_dbgid) +{ + struct hdd_station_info *info; + struct qdf_mac_addr addr; + + if (!sta_info_container || !sta_info) { + hdd_err("Parameter(s) null"); + return; + } + + info = *sta_info; + + if (!info) { + hdd_err("station info NULL"); + return; + } + + if (sta_info_dbgid >= STA_INFO_ID_MAX) { + hdd_err("Invalid sta_info debug id %d", sta_info_dbgid); + return; + } + + if (lock_required) + qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); + + /* + * In case the put_ref is called more than twice for a single take_ref, + * this will result in either a BUG or page fault. In both the cases, + * the root cause would be known and the buggy put_ref can be taken + * care of. + */ + if (!qdf_atomic_read(&info->ref_cnt_dbgid[sta_info_dbgid])) { + hdd_err("Sta_info ref count put is detected without get for debug id %s", + sta_info_string_from_dbgid(sta_info_dbgid)); + + QDF_BUG(0); + } + + qdf_atomic_dec(&info->ref_cnt); + qdf_atomic_dec(&info->ref_cnt_dbgid[sta_info_dbgid]); + + if (qdf_atomic_read(&info->ref_cnt)) { + if (lock_required) + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); + return; + } + + qdf_copy_macaddr(&addr, &info->sta_mac); + if (info->assoc_req_ies.len) { + qdf_mem_free(info->assoc_req_ies.data); + info->assoc_req_ies.data = NULL; + info->assoc_req_ies.len = 0; + } + + qdf_list_remove_node(&sta_info_container->sta_obj, &info->sta_node); + qdf_mem_free(info); + *sta_info = NULL; + + if (lock_required) + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); + + hdd_nofl_debug("STA_INFO: " QDF_MAC_ADDR_FMT " freed", + QDF_MAC_ADDR_REF(addr.bytes)); +} + +void hdd_clear_cached_sta_info(struct hdd_adapter *adapter) +{ + struct hdd_station_info *sta_info = NULL, *tmp = NULL; + + if (!adapter) { + hdd_err("Parameter null"); + return; + } + + hdd_for_each_sta_ref_safe(adapter->cache_sta_info_list, sta_info, tmp, + STA_INFO_CLEAR_CACHED_STA_INFO) { + hdd_sta_info_detach(&adapter->cache_sta_info_list, &sta_info); + hdd_put_sta_info_ref(&adapter->cache_sta_info_list, &sta_info, + true, STA_INFO_CLEAR_CACHED_STA_INFO); + } +} + +QDF_STATUS +hdd_get_front_sta_info_no_lock(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info **out_sta_info) +{ + QDF_STATUS status; + qdf_list_node_t *node; + + *out_sta_info = NULL; + + status = qdf_list_peek_front(&sta_info_container->sta_obj, &node); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + *out_sta_info = + qdf_container_of(node, struct hdd_station_info, sta_node); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +hdd_get_next_sta_info_no_lock(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info *current_sta_info, + struct hdd_station_info **out_sta_info) +{ + QDF_STATUS status; + qdf_list_node_t *node; + + if (!current_sta_info) + return QDF_STATUS_E_INVAL; + + *out_sta_info = NULL; + + status = qdf_list_peek_next(&sta_info_container->sta_obj, + ¤t_sta_info->sta_node, + &node); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + *out_sta_info = + qdf_container_of(node, struct hdd_station_info, sta_node); + + return QDF_STATUS_SUCCESS; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sta_info.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sta_info.h new file mode 100644 index 0000000000000000000000000000000000000000..8a337034b518c73ac7e9a65f2411d08b8b89ce6f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sta_info.h @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_sta_info.h + * + * Store and manage station info structure. + * + */ +#if !defined(__WLAN_HDD_STA_INFO_H) +#define __WLAN_HDD_STA_INFO_H + +#include +#include "qdf_lock.h" +#include "qdf_types.h" +#include "qdf_list.h" +#include "sap_api.h" +#include "cdp_txrx_cmn_struct.h" +#include "sir_mac_prot_def.h" +#include +#include + +/* Opaque handle for abstraction */ +#define hdd_sta_info_entry qdf_list_node_t + +/** + * struct dhcp_phase - Per Peer DHCP Phases + * @DHCP_PHASE_ACK: upon receiving DHCP_ACK/NAK message in REQUEST phase or + * DHCP_DELINE message in OFFER phase + * @DHCP_PHASE_DISCOVER: upon receiving DHCP_DISCOVER message in ACK phase + * @DHCP_PHASE_OFFER: upon receiving DHCP_OFFER message in DISCOVER phase + * @DHCP_PHASE_REQUEST: upon receiving DHCP_REQUEST message in OFFER phase or + * ACK phase (Renewal process) + */ +enum dhcp_phase { + DHCP_PHASE_ACK, + DHCP_PHASE_DISCOVER, + DHCP_PHASE_OFFER, + DHCP_PHASE_REQUEST +}; + +/** + * struct dhcp_nego_status - Per Peer DHCP Negotiation Status + * @DHCP_NEGO_STOP: when the peer is in ACK phase or client disassociated + * @DHCP_NEGO_IN_PROGRESS: when the peer is in DISCOVER or REQUEST + * (Renewal process) phase + */ +enum dhcp_nego_status { + DHCP_NEGO_STOP, + DHCP_NEGO_IN_PROGRESS +}; + +/** + * Pending frame type of EAP_FAILURE, bit number used in "pending_eap_frm_type" + * of sta_info. + */ +#define PENDING_TYPE_EAP_FAILURE 0 + +/** + * enum wlan_sta_info_dbgid - sta info put/get debug id + * @STA_INFO_ID_RESERVED: Reserved + * @STA_INFO_CFG80211_GET_LINK_PROPERTIES: Get link properties + * @STA_INFO_SOFTAP_INSPECT_TX_EAP_PKT: Inspect the EAP-Failure + * @STA_INFO_SOFTAP_CHECK_WAIT_FOR_TX_EAP_PKT: Check and wait for eap failure + * pkt tx completion + * @STA_INFO_SOFTAP_INSPECT_DHCP_PACKET: Inspect DHCP packet + * @STA_INFO_SOFTAP_HARD_START_XMIT: Transmit a frame + * @STA_INFO_SOFTAP_INIT_TX_RX_STA: Initialize Tx/Rx for a softap station + * @STA_INFO_SOFTAP_RX_PACKET_CBK: Receive packet handler + * @STA_INFO_SOFTAP_REGISTER_STA: Register a SoftAP STA + * @STA_INFO_GET_CACHED_STATION_REMOTE: Get cached peer's info + * @STA_INFO_HDD_GET_STATION_REMOTE: Get remote peer's info + * @STA_INFO_WLAN_HDD_GET_STATION_REMOTE: NL80211_CMD_GET_STATION handler for + * SoftAP + * @STA_INFO_SOFTAP_DEAUTH_CURRENT_STA: Deauth current sta + * @STA_INFO_SOFTAP_DEAUTH_ALL_STA: Deauth all sta in the sta list + * @STA_INFO_CFG80211_DEL_STATION: CFG80211 del station handler + * @STA_INFO_HDD_CLEAR_ALL_STA: Clear all stations + * @STA_INFO_FILL_STATION_INFO: Fill stainfo for connected station + * @STA_INFO_HOSTAPD_SAP_EVENT_CB: SAP event handler + * @STA_INFO_SAP_INDICATE_DISCONNECT_FOR_STA: Indicating disconnect indication + * to the supplicant + * @STA_INFO_IS_PEER_ASSOCIATED: Is peer connected to softap + * @STA_INFO_SAP_SET_TWO_INTS_GETNONE: Generic "set two integer" ioctl handler + * @STA_INFO_SAP_GETASSOC_STAMACADDR: Handler to get assoc station mac address + * @STA_INFO_SOFTAP_GET_STA_INFO: Get station info handler + * @STA_INFO_GET_SOFTAP_LINKSPEED: Get link speed handler + * @STA_INFO_CONNECTION_IN_PROGRESS_ITERATOR: Check adapter connection based on + * device mode + * @STA_INFO_SOFTAP_STOP_BSS: Stop BSS + * @STA_INFO_SOFTAP_CHANGE_STA_STATE: Change the state of a SoftAP station + * @STA_INFO_CLEAR_CACHED_STA_INFO: Clear the cached sta info + * @STA_INFO_ATTACH_DETACH: Station info attach/detach + * @STA_INFO_SHOW: Station info show + * + */ +/* + * New value added to the enum must also be reflected in function + * sta_info_string_from_dbgid() + */ +typedef enum { + STA_INFO_ID_RESERVED = 0, + STA_INFO_CFG80211_GET_LINK_PROPERTIES = 1, + STA_INFO_SOFTAP_INSPECT_TX_EAP_PKT = 2, + STA_INFO_SOFTAP_CHECK_WAIT_FOR_TX_EAP_PKT = 3, + STA_INFO_SOFTAP_INSPECT_DHCP_PACKET = 4, + STA_INFO_SOFTAP_HARD_START_XMIT = 5, + STA_INFO_SOFTAP_INIT_TX_RX_STA = 6, + STA_INFO_SOFTAP_RX_PACKET_CBK = 7, + STA_INFO_SOFTAP_REGISTER_STA = 8, + STA_INFO_GET_CACHED_STATION_REMOTE = 9, + STA_INFO_HDD_GET_STATION_REMOTE = 10, + STA_INFO_WLAN_HDD_GET_STATION_REMOTE = 11, + STA_INFO_SOFTAP_DEAUTH_CURRENT_STA = 12, + STA_INFO_SOFTAP_DEAUTH_ALL_STA = 13, + STA_INFO_CFG80211_DEL_STATION = 14, + STA_INFO_HDD_CLEAR_ALL_STA = 15, + STA_INFO_FILL_STATION_INFO = 16, + STA_INFO_HOSTAPD_SAP_EVENT_CB = 17, + STA_INFO_SAP_INDICATE_DISCONNECT_FOR_STA = 18, + STA_INFO_IS_PEER_ASSOCIATED = 19, + STA_INFO_SAP_SET_TWO_INTS_GETNONE = 20, + STA_INFO_SAP_GETASSOC_STAMACADDR = 21, + STA_INFO_SOFTAP_GET_STA_INFO = 22, + STA_INFO_GET_SOFTAP_LINKSPEED = 23, + STA_INFO_CONNECTION_IN_PROGRESS_ITERATOR = 24, + STA_INFO_SOFTAP_STOP_BSS = 25, + STA_INFO_SOFTAP_CHANGE_STA_STATE = 26, + STA_INFO_CLEAR_CACHED_STA_INFO = 27, + STA_INFO_ATTACH_DETACH = 28, + STA_INFO_SHOW = 29, + + STA_INFO_ID_MAX, +} wlan_sta_info_dbgid; + +/** + * sta_info_string_from_dbgid() - Convert dbgid to respective string + * @id - debug id + * + * Debug support function to convert dbgid to string. + * Please note to add new string in the array at index equal to + * its enum value in wlan_sta_info_dbgid. + */ +char *sta_info_string_from_dbgid(wlan_sta_info_dbgid id); + +/** + * struct hdd_station_info - Per station structure kept in HDD for + * multiple station support for SoftAP + * @in_use: Is the station entry in use? + * @sta_id: Station ID reported back from HAL (through SAP). + * Broadcast uses station ID zero by default. + * @sta_type: Type of station i.e. p2p client or infrastructure station + * @sta_mac: MAC address of the station + * @peer_state: Current Station state so HDD knows how to deal with packet + * queue. Most recent states used to change TLSHIM STA state. + * @is_qos_enabled: Track QoS status of station + * @is_deauth_in_progress: The station entry for which Deauth is in progress + * @nss: Number of spatial streams supported + * @rate_flags: Rate Flags for this connection + * @ecsa_capable: Extended CSA capabilities + * @max_phy_rate: Calcuated maximum phy rate based on mode, nss, mcs etc. + * @tx_packets: The number of frames from host to firmware + * @tx_bytes: Bytes send to current station + * @rx_packets: Packets received from current station + * @rx_bytes: Bytes received from current station + * @last_tx_rx_ts: Last tx/rx timestamp with current station + * @assoc_ts: Current station association timestamp + * @tx_rate: Tx rate with current station reported from F/W + * @rx_rate: Rx rate with current station reported from F/W + * @ampdu: Ampdu enable or not of the station + * @sgi_enable: Short GI enable or not of the station + * @tx_stbc: Tx Space-time block coding enable/disable + * @rx_stbc: Rx Space-time block coding enable/disable + * @ch_width: Channel Width of the connection + * @mode: Mode of the connection + * @max_supp_idx: Max supported rate index of the station + * @max_ext_idx: Max extended supported rate index of the station + * @max_mcs_idx: Max supported mcs index of the station + * @rx_mcs_map: VHT Rx mcs map + * @tx_mcs_map: VHT Tx mcs map + * @freq : Frequency of the current station + * @dot11_mode: 802.11 Mode of the connection + * @ht_present: HT caps present or not in the current station + * @vht_present: VHT caps present or not in the current station + * @ht_caps: HT capabilities of current station + * @vht_caps: VHT capabilities of current station + * @reason_code: Disconnection reason code for current station + * @rssi: RSSI of the current station reported from F/W + * @capability: Capability information of current station + * @support_mode: Max supported mode of a station currently + * connected to sap + * @rx_retry_cnt: Number of rx retries received from current station + * Currently this feature is not supported from FW + * @rx_mc_bc_cnt: Multicast broadcast packet count received from + * current station + * MSB of rx_mc_bc_cnt indicates whether FW supports rx_mc_bc_cnt + * feature or not, if first bit is 1 it indicates that FW supports this + * feature, if it is 0 it indicates FW doesn't support this feature + * @tx_failed: the number of tx failed frames + * @peer_rssi_per_chain: the average value of RSSI (dbm) per chain + * @tx_retry_succeed: the number of frames retried but successfully transmit + * @rx_last_pkt_rssi: the rssi (dbm) calculate by last packet + * @tx_retry: the number of retried frames from host to firmware + * @tx_retry_exhaust: the number of frames retried but finally failed + * from host to firmware + * @tx_total_fw: the number of all frames from firmware to remote station + * @tx_retry_fw: the number of retried frames from firmware to remote station + * @tx_retry_exhaust_fw: the number of frames retried but finally failed from + * firmware to remote station + * @sta_info: The sta_info node for the station info list maintained in adapter + * @assoc_req_ies: Assoc request IEs of the peer station + * @ref_cnt: Reference count to synchronize sta_info access + * @ref_cnt_dbgid: Reference count to debug sta_info synchronization issues + * @pending_eap_frm_type: EAP frame type in tx queue without tx completion + * @is_attached: Flag to check if the stainfo is attached/detached + */ +struct hdd_station_info { + bool in_use; + uint8_t sta_id; + eStationType sta_type; + struct qdf_mac_addr sta_mac; + enum ol_txrx_peer_state peer_state; + bool is_qos_enabled; + bool is_deauth_in_progress; + uint8_t nss; + uint32_t rate_flags; + uint8_t ecsa_capable; + uint32_t max_phy_rate; + uint32_t tx_packets; + uint64_t tx_bytes; + uint32_t rx_packets; + uint64_t rx_bytes; + qdf_time_t last_tx_rx_ts; + qdf_time_t assoc_ts; + qdf_time_t disassoc_ts; + uint32_t tx_rate; + uint32_t rx_rate; + bool ampdu; + bool sgi_enable; + bool tx_stbc; + bool rx_stbc; + tSirMacHTChannelWidth ch_width; + uint8_t mode; + uint8_t max_supp_idx; + uint8_t max_ext_idx; + uint8_t max_mcs_idx; + uint8_t rx_mcs_map; + uint8_t tx_mcs_map; + uint32_t freq; + uint8_t dot11_mode; + bool ht_present; + bool vht_present; + struct ieee80211_ht_cap ht_caps; + struct ieee80211_vht_cap vht_caps; + uint32_t reason_code; + int8_t rssi; + enum dhcp_phase dhcp_phase; + enum dhcp_nego_status dhcp_nego_status; + uint16_t capability; + uint8_t support_mode; + uint32_t rx_retry_cnt; + uint32_t rx_mc_bc_cnt; + uint32_t tx_failed; + uint32_t peer_rssi_per_chain[WMI_MAX_CHAINS]; + uint32_t tx_retry_succeed; + uint32_t rx_last_pkt_rssi; + uint32_t tx_retry; + uint32_t tx_retry_exhaust; + uint32_t tx_total_fw; + uint32_t tx_retry_fw; + uint32_t tx_retry_exhaust_fw; + qdf_list_node_t sta_node; + struct wlan_ies assoc_req_ies; + qdf_atomic_t ref_cnt; + qdf_atomic_t ref_cnt_dbgid[STA_INFO_ID_MAX]; + unsigned long pending_eap_frm_type; + bool is_attached; +}; + +/** + * struct hdd_sta_info_obj - Station info container structure + * @sta_obj: The sta info object that stores the sta_info + * @sta_obj_lock: Lock to protect the sta_obj read/write access + */ +struct hdd_sta_info_obj { + qdf_list_t sta_obj; + qdf_spinlock_t sta_obj_lock; +}; + +/** + * hdd_put_sta_info_ref() - Release sta_info ref for synchronization + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: Station info structure to be released. + * @lock_required: Flag to acquire lock or not + * @sta_info_dbgid: Debug ID of the caller API + * + * Return: None + */ +void hdd_put_sta_info_ref(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info **sta_info, + bool lock_required, + wlan_sta_info_dbgid sta_info_dbgid); + +/** + * hdd_take_sta_info_ref() - Increment sta info ref. + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: Station info structure to be released. + * @lock_required: Flag to acquire lock or not + * @sta_info_dbgid: Debug ID of the caller API + * + * This function has to be accompanied by hdd_put_sta_info when the work with + * the sta info is done. Failure to do so will result in a mem leak. + * + * Return: None + */ +void hdd_take_sta_info_ref(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info *sta_info, + bool lock_required, + wlan_sta_info_dbgid sta_info_dbgid); + +/** + * hdd_get_front_sta_info_no_lock() - Get the first sta_info from the sta list + * This API doesnot use any lock in it's implementation. It is the caller's + * directive to ensure concurrency safety. + * + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @out_sta_info: The station info structure that acts as the container object. + * + * Return: QDF_STATUS + */ +QDF_STATUS +hdd_get_front_sta_info_no_lock(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info **out_sta_info); + +/** + * hdd_get_next_sta_info_no_lock() - Get the next sta_info from the sta list + * This API doesnot use any lock in it's implementation. It is the caller's + * directive to ensure concurrency safety. + * + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @out_sta_info: The station info structure that acts as the container object. + * + * Return: QDF_STATUS + */ +QDF_STATUS +hdd_get_next_sta_info_no_lock(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info *current_sta_info, + struct hdd_station_info **out_sta_info); + +/* Abstract wrapper to check sta_info validity */ +#define __hdd_is_station_valid(sta_info) sta_info + +/** + * __hdd_take_ref_and_fetch_front_sta_info - Helper macro to lock, fetch front + * sta_info, take ref and unlock. + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: The station info structure that acts as the iterator object. + */ +#define __hdd_take_ref_and_fetch_front_sta_info(sta_info_container, sta_info, \ + sta_info_dbgid) \ + qdf_spin_lock_bh(&sta_info_container.sta_obj_lock), \ + hdd_get_front_sta_info_no_lock(&sta_info_container, &sta_info), \ + (sta_info) ? hdd_take_sta_info_ref(&sta_info_container, \ + sta_info, false, sta_info_dbgid) : \ + (false), \ + qdf_spin_unlock_bh(&sta_info_container.sta_obj_lock) + +/** + * __hdd_take_ref_and_fetch_next_sta_info - Helper macro to lock, fetch next + * sta_info, take ref and unlock. + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: The station info structure that acts as the iterator object. + */ +#define __hdd_take_ref_and_fetch_next_sta_info(sta_info_container, sta_info, \ + sta_info_dbgid) \ + qdf_spin_lock_bh(&sta_info_container.sta_obj_lock), \ + hdd_get_next_sta_info_no_lock(&sta_info_container, sta_info, \ + &sta_info), \ + (sta_info) ? hdd_take_sta_info_ref(&sta_info_container, \ + sta_info, false, sta_info_dbgid) : \ + (false), \ + qdf_spin_unlock_bh(&sta_info_container.sta_obj_lock) + +/** + * hdd_for_each_sta_ref - Iterate over each station stored in the sta info + * container with ref taken + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: The station info structure that acts as the iterator object. + * @sta_info_dbgid: Debug ID of the caller API + * + * The sta_info will contain the structure that is fetched for that particular + * iteration. + * + * ***** NOTE ***** + * Before the end of each iteration, dev_put(adapter->dev) must be + * called. Not calling this will keep hold of a reference, thus preventing + * unregister of the netdevice. + * + * Usage example: + * hdd_for_each_sta_ref(sta_info_container, sta_info, sta_info_dbgid) { + * + * + * hdd_put_sta_info_ref(sta_info_container, sta_info, true, + * sta_info_dbgid) + * } + */ +#define hdd_for_each_sta_ref(sta_info_container, sta_info, sta_info_dbgid) \ + for (__hdd_take_ref_and_fetch_front_sta_info(sta_info_container, \ + sta_info, sta_info_dbgid);\ + __hdd_is_station_valid(sta_info); \ + __hdd_take_ref_and_fetch_next_sta_info(sta_info_container, \ + sta_info, sta_info_dbgid)) + +/** + * __hdd_take_ref_and_fetch_front_sta_info_safe - Helper macro to lock, fetch + * front sta_info, take ref and unlock in a delete safe manner. + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: The station info structure that acts as the iterator object. + */ +#define __hdd_take_ref_and_fetch_front_sta_info_safe(sta_info_container, \ + sta_info, next_sta_info, \ + sta_info_dbgid) \ + qdf_spin_lock_bh(&sta_info_container.sta_obj_lock), \ + hdd_get_front_sta_info_no_lock(&sta_info_container, &sta_info), \ + (sta_info) ? hdd_take_sta_info_ref(&sta_info_container, \ + sta_info, false, sta_info_dbgid) : \ + (false), \ + hdd_get_next_sta_info_no_lock(&sta_info_container, sta_info, \ + &next_sta_info), \ + (next_sta_info) ? hdd_take_sta_info_ref(&sta_info_container, \ + next_sta_info, false, \ + sta_info_dbgid) : \ + (false), \ + qdf_spin_unlock_bh(&sta_info_container.sta_obj_lock) + +/** + * __hdd_take_ref_and_fetch_next_sta_info_safe - Helper macro to lock, fetch + * next sta_info, take ref and unlock. + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: The station info structure that acts as the iterator object. + */ +#define __hdd_take_ref_and_fetch_next_sta_info_safe(sta_info_container, \ + sta_info, next_sta_info, \ + sta_info_dbgid) \ + sta_info = next_sta_info, \ + qdf_spin_lock_bh(&sta_info_container.sta_obj_lock), \ + hdd_get_next_sta_info_no_lock(&sta_info_container, sta_info, \ + &next_sta_info), \ + (next_sta_info) ? hdd_take_sta_info_ref(&sta_info_container, \ + next_sta_info, false, \ + sta_info_dbgid) : \ + (false), \ + qdf_spin_unlock_bh(&sta_info_container.sta_obj_lock) + +/** + * hdd_for_each_sta_ref_safe - Iterate over each station stored in the sta info + * container in a delete safe manner + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: The station info structure that acts as the iterator object. + * @next_sta_info: A temporary node for maintaing del safe. + * @sta_info_dbgid: Debug ID of the caller API + * + * The sta_info will contain the structure that is fetched for that particular + * iteration. The next_sta_info is used to store the next station before the + * current station is deleted so as to provide a safe way to iterate the list + * while deletion is undergoing. + * + * ***** NOTE ***** + * Before the end of each iteration, hdd_put_sta_info_ref must be + * called. Not calling this will keep hold of a reference, thus preventing + * deletion of the station info + * + * Usage example: + * hdd_for_each_sta_ref_safe(sta_info_container, sta_info, next_sta_info, + * sta_info_dbgid) { + * + * + * hdd_put_sta_info_ref(sta_info_container, sta_info, true, + * sta_info_dbgid) + * } + */ +#define hdd_for_each_sta_ref_safe(sta_info_container, sta_info, next_sta_info, \ + sta_info_dbgid) \ + for (__hdd_take_ref_and_fetch_front_sta_info_safe(sta_info_container, \ + sta_info, \ + next_sta_info, \ + sta_info_dbgid); \ + __hdd_is_station_valid(sta_info); \ + __hdd_take_ref_and_fetch_next_sta_info_safe(sta_info_container, \ + sta_info, \ + next_sta_info, \ + sta_info_dbgid)) + +/** + * wlan_sta_info_init() - Initialise the wlan hdd station info container obj + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * + * Return: QDF_STATUS_SUCCESS on success, failure code otherwise + */ +QDF_STATUS hdd_sta_info_init(struct hdd_sta_info_obj *sta_info_container); + +/** + * wlan_sta_info_deinit() - Deinit the wlan hdd station info container obj + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * + * Return: None + */ +void hdd_sta_info_deinit(struct hdd_sta_info_obj *sta_info_container); + +/** + * hdd_sta_info_detach() - Detach the station info structure from the list + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: The station info structure that has to be detached from the + * container object. + * + * Return: None + */ +void hdd_sta_info_detach(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info **sta_info); + +/** + * hdd_sta_info_attach() - Attach the station info structure into the list + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: The station info structure that is to be attached to the + * container object. + * + * Return: QDF STATUS SUCCESS on successful attach, error code otherwise + */ +QDF_STATUS hdd_sta_info_attach(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info *sta_info); + +/** + * hdd_get_sta_info_by_mac() - Find the sta_info structure by mac addr + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @mac_addr: The mac addr by which the sta_info has to be fetched. + * @sta_info_dbgid: Debug ID of the caller API + * + * Return: Pointer to the hdd_station_info structure which contains the mac + * address passed + */ +struct hdd_station_info *hdd_get_sta_info_by_mac( + struct hdd_sta_info_obj *sta_info_container, + const uint8_t *mac_addr, + wlan_sta_info_dbgid sta_info_dbgid); + +/** + * hdd_clear_cached_sta_info() - Clear the cached sta info from the container + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * + * Return: None + */ +void hdd_clear_cached_sta_info(struct hdd_adapter *hdd_adapter); + +#endif /* __WLAN_HDD_STA_INFO_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_station_info.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_station_info.c new file mode 100644 index 0000000000000000000000000000000000000000..93ba5f17ef6593bea36ad2b8417578e7f027ce03 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_station_info.c @@ -0,0 +1,1828 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_station_info.c + * + * WLAN station info functions + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_mlme_ucfg_api.h" +#include "wlan_hdd_sta_info.h" +#include +#include +#include +#include +#include "wlan_hdd_object_manager.h" + +/* + * define short names for the global vendor params + * used by __wlan_hdd_cfg80211_get_station_cmd() + */ +#define STATION_INVALID \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INVALID +#define STATION_INFO \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO +#define STATION_ASSOC_FAIL_REASON \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_ASSOC_FAIL_REASON +#define STATION_REMOTE \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_REMOTE +#define STATION_MAX \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX + +#define STA_INFO_CONNECT_FAIL_REASON_CODE \ + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE + +/* define short names for get station info attributes */ +#define LINK_INFO_STANDARD_NL80211_ATTR \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_LINK_STANDARD_NL80211_ATTR +#define AP_INFO_STANDARD_NL80211_ATTR \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_STANDARD_NL80211_ATTR +#define INFO_ROAM_COUNT \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ROAM_COUNT +#define INFO_AKM \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AKM +#define WLAN802_11_MODE \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_802_11_MODE +#define AP_INFO_HS20_INDICATION \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_HS20_INDICATION +#define HT_OPERATION \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_HT_OPERATION +#define VHT_OPERATION \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_VHT_OPERATION +#define HE_OPERATION \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_HE_OPERATION +#define INFO_ASSOC_FAIL_REASON \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ASSOC_FAIL_REASON +#define REMOTE_MAX_PHY_RATE \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_MAX_PHY_RATE +#define REMOTE_TX_PACKETS \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_PACKETS +#define REMOTE_TX_BYTES \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_BYTES +#define REMOTE_RX_PACKETS \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_PACKETS +#define REMOTE_RX_BYTES \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_BYTES +#define REMOTE_LAST_TX_RATE \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_LAST_TX_RATE +#define REMOTE_LAST_RX_RATE \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_LAST_RX_RATE +#define REMOTE_WMM \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_WMM +#define REMOTE_SUPPORTED_MODE \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_SUPPORTED_MODE +#define REMOTE_AMPDU \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_AMPDU +#define REMOTE_TX_STBC \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_STBC +#define REMOTE_RX_STBC \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_STBC +#define REMOTE_CH_WIDTH\ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_CH_WIDTH +#define REMOTE_SGI_ENABLE\ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_SGI_ENABLE +#define REMOTE_PAD\ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_PAD +#define REMOTE_RX_RETRY_COUNT \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_RETRY_COUNT +#define REMOTE_RX_BC_MC_COUNT \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_BC_MC_COUNT +#define REMOTE_TX_FAILURE \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_FAILURE +#define REMOTE_AVG_RSSI_PER_CHAIN \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_AVG_RSSI_PER_CHAIN +#define REMOTE_TX_RETRY_SUCCEED \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY_SUCCEED +#define REMOTE_RX_LAST_PKT_RSSI \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_LAST_PKT_RSSI +#define REMOTE_TX_RETRY \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY +#define REMOTE_TX_RETRY_EXHAUST \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY_EXHAUST +#define REMOTE_TX_TOTAL_FW \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_TOTAL_FW +#define REMOTE_TX_RETRY_FW \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY_FW +#define REMOTE_TX_RETRY_EXHAUST_FW \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY_EXHAUST_FW +#define DISCONNECT_REASON \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_DRIVER_DISCONNECT_REASON +#define BEACON_IES \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_BEACON_IES +#define ASSOC_REQ_IES \ + QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ASSOC_REQ_IES + +/* + * MSB of rx_mc_bc_cnt indicates whether FW supports rx_mc_bc_cnt + * feature or not, if first bit is 1 it indictes that FW supports this + * feature, if it is 0 it indicates FW doesn't support this feature + */ +#define HDD_STATION_INFO_RX_MC_BC_COUNT (1 << 31) + + +static const struct nla_policy +hdd_get_station_policy[STATION_MAX + 1] = { + [STATION_INFO] = {.type = NLA_FLAG}, + [STATION_ASSOC_FAIL_REASON] = {.type = NLA_FLAG}, + [STATION_REMOTE] = {.type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, +}; + +const struct nla_policy +hdd_get_sta_policy[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC] = {.type = NLA_BINARY, + .len = QDF_MAC_ADDR_SIZE}, +}; + +static int hdd_get_sta_congestion(struct hdd_adapter *adapter, + uint32_t *congestion) +{ + QDF_STATUS status; + struct cca_stats cca_stats; + + status = ucfg_mc_cp_stats_cca_stats_get(adapter->vdev, &cca_stats); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + *congestion = cca_stats.congestion; + return 0; +} + +/** + * hdd_get_station_assoc_fail() - Handle get station assoc fail + * @hdd_ctx: HDD context within host driver + * @wdev: wireless device + * + * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION_ASSOC_FAIL. + * Validate cmd attributes and send the station info to upper layers. + * + * Return: Success(0) or reason code for failure + */ +static int hdd_get_station_assoc_fail(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct sk_buff *skb = NULL; + uint32_t nl_buf_len; + struct hdd_station_ctx *hdd_sta_ctx; + uint32_t congestion; + + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += sizeof(uint32_t); + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (nla_put_u32(skb, INFO_ASSOC_FAIL_REASON, + hdd_sta_ctx->conn_info.assoc_status_code)) { + hdd_err("put fail"); + goto fail; + } + + if (hdd_get_sta_congestion(adapter, &congestion)) + congestion = 0; + + hdd_info("congestion:%d", congestion); + if (nla_put_u32(skb, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, + congestion)) { + hdd_err("put fail"); + goto fail; + } + + return cfg80211_vendor_cmd_reply(skb); +fail: + if (skb) + kfree_skb(skb); + return -EINVAL; +} + +/** + * hdd_map_auth_type() - transform auth type specific to + * vendor command + * @auth_type: csr auth type + * + * Return: Success(0) or reason code for failure + */ +static int hdd_convert_auth_type(uint32_t auth_type) +{ + uint32_t ret_val; + + switch (auth_type) { + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + ret_val = QCA_WLAN_AUTH_TYPE_OPEN; + break; + case eCSR_AUTH_TYPE_SHARED_KEY: + ret_val = QCA_WLAN_AUTH_TYPE_SHARED; + break; + case eCSR_AUTH_TYPE_WPA: + ret_val = QCA_WLAN_AUTH_TYPE_WPA; + break; + case eCSR_AUTH_TYPE_WPA_PSK: + ret_val = QCA_WLAN_AUTH_TYPE_WPA_PSK; + break; + case eCSR_AUTH_TYPE_AUTOSWITCH: + ret_val = QCA_WLAN_AUTH_TYPE_AUTOSWITCH; + break; + case eCSR_AUTH_TYPE_WPA_NONE: + ret_val = QCA_WLAN_AUTH_TYPE_WPA_NONE; + break; + case eCSR_AUTH_TYPE_RSN: + ret_val = QCA_WLAN_AUTH_TYPE_RSN; + break; + case eCSR_AUTH_TYPE_RSN_PSK: + ret_val = QCA_WLAN_AUTH_TYPE_RSN_PSK; + break; + case eCSR_AUTH_TYPE_FT_RSN: + ret_val = QCA_WLAN_AUTH_TYPE_FT; + break; + case eCSR_AUTH_TYPE_FT_RSN_PSK: + ret_val = QCA_WLAN_AUTH_TYPE_FT_PSK; + break; + case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE: + ret_val = QCA_WLAN_AUTH_TYPE_WAI; + break; + case eCSR_AUTH_TYPE_WAPI_WAI_PSK: + ret_val = QCA_WLAN_AUTH_TYPE_WAI_PSK; + break; + case eCSR_AUTH_TYPE_CCKM_WPA: + ret_val = QCA_WLAN_AUTH_TYPE_CCKM_WPA; + break; + case eCSR_AUTH_TYPE_CCKM_RSN: + ret_val = QCA_WLAN_AUTH_TYPE_CCKM_RSN; + break; + case eCSR_AUTH_TYPE_RSN_PSK_SHA256: + ret_val = QCA_WLAN_AUTH_TYPE_SHA256_PSK; + break; + case eCSR_AUTH_TYPE_RSN_8021X_SHA256: + ret_val = QCA_WLAN_AUTH_TYPE_SHA256; + break; + case eCSR_AUTH_TYPE_FT_SAE: + ret_val = QCA_WLAN_AUTH_TYPE_FT_SAE; + break; + case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384: + ret_val = QCA_WLAN_AUTH_TYPE_FT_SUITEB_EAP_SHA384; + break; + case eCSR_NUM_OF_SUPPORT_AUTH_TYPE: + case eCSR_AUTH_TYPE_FAILED: + case eCSR_AUTH_TYPE_NONE: + default: + ret_val = QCA_WLAN_AUTH_TYPE_INVALID; + break; + } + return ret_val; +} + +/** + * hdd_map_dot_11_mode() - transform dot11mode type specific to + * vendor command + * @dot11mode: dot11mode + * + * Return: Success(0) or reason code for failure + */ +static int hdd_convert_dot11mode(uint32_t dot11mode) +{ + uint32_t ret_val; + + switch (dot11mode) { + case eCSR_CFG_DOT11_MODE_11A: + ret_val = QCA_WLAN_802_11_MODE_11A; + break; + case eCSR_CFG_DOT11_MODE_11B: + ret_val = QCA_WLAN_802_11_MODE_11B; + break; + case eCSR_CFG_DOT11_MODE_11G: + ret_val = QCA_WLAN_802_11_MODE_11G; + break; + case eCSR_CFG_DOT11_MODE_11N: + ret_val = QCA_WLAN_802_11_MODE_11N; + break; + case eCSR_CFG_DOT11_MODE_11AC: + ret_val = QCA_WLAN_802_11_MODE_11AC; + break; + case eCSR_CFG_DOT11_MODE_AUTO: + case eCSR_CFG_DOT11_MODE_ABG: + default: + ret_val = QCA_WLAN_802_11_MODE_INVALID; + } + return ret_val; +} + +/** + * hdd_add_tx_bitrate() - add tx bitrate attribute + * @skb: pointer to sk buff + * @hdd_sta_ctx: pointer to hdd station context + * @idx: attribute index + * + * Return: Success(0) or reason code for failure + */ +static int32_t hdd_add_tx_bitrate(struct sk_buff *skb, + struct hdd_station_ctx *hdd_sta_ctx, + int idx) +{ + struct nlattr *nla_attr; + uint32_t bitrate, bitrate_compat; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) { + hdd_err("nla_nest_start failed"); + goto fail; + } + + /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ + if (hdd_conn_is_connected(hdd_sta_ctx)) + bitrate = cfg80211_calculate_bitrate( + &hdd_sta_ctx->cache_conn_info.max_tx_bitrate); + else + bitrate = cfg80211_calculate_bitrate( + &hdd_sta_ctx->cache_conn_info.txrate); + /* report 16-bit bitrate only if we can */ + bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; + + if (bitrate > 0) { + if (nla_put_u32(skb, NL80211_RATE_INFO_BITRATE32, bitrate)) { + hdd_err("put fail bitrate: %u", bitrate); + goto fail; + } + } else { + hdd_err("Invalid bitrate: %u", bitrate); + } + + if (bitrate_compat > 0) { + if (nla_put_u16(skb, NL80211_RATE_INFO_BITRATE, + bitrate_compat)) { + hdd_err("put fail bitrate_compat: %u", bitrate_compat); + goto fail; + } + } else { + hdd_err("Invalid bitrate_compat: %u", bitrate_compat); + } + + if (nla_put_u8(skb, NL80211_RATE_INFO_VHT_NSS, + hdd_sta_ctx->cache_conn_info.txrate.nss)) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_get_max_tx_bitrate() - Get the max tx bitrate of the AP + * @hdd_ctx: hdd context + * @adapter: hostapd interface + * + * THis function gets the MAX supported rate by AP and cache + * it into connection info structure + * + * Return: None + */ +static void hdd_get_max_tx_bitrate(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct station_info sinfo; + enum tx_rate_info tx_rate_flags; + uint8_t tx_mcs_index, tx_nss = 1; + uint16_t my_tx_rate; + struct hdd_station_ctx *hdd_sta_ctx; + struct wlan_objmgr_vdev *vdev; + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + qdf_mem_zero(&sinfo, sizeof(struct station_info)); + + sinfo.signal = adapter->rssi; + tx_rate_flags = adapter->hdd_stats.class_a_stat.tx_rx_rate_flags; + tx_mcs_index = adapter->hdd_stats.class_a_stat.tx_mcs_index; + my_tx_rate = adapter->hdd_stats.class_a_stat.tx_rate; + + if (!(tx_rate_flags & TX_RATE_LEGACY)) { + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + /* + * Take static NSS for reporting max rates. + * NSS from FW is not reliable as it changes + * as per the environment quality. + */ + tx_nss = wlan_vdev_mlme_get_nss(vdev); + hdd_objmgr_put_vdev(vdev); + } else { + tx_nss = adapter->hdd_stats.class_a_stat.tx_nss; + } + hdd_check_and_update_nss(hdd_ctx, &tx_nss, NULL); + + if (tx_mcs_index == INVALID_MCS_IDX) + tx_mcs_index = 0; + } + if (hdd_report_max_rate(hdd_ctx->mac_handle, &sinfo.txrate, + sinfo.signal, tx_rate_flags, tx_mcs_index, + my_tx_rate, tx_nss)) { + hdd_sta_ctx->cache_conn_info.max_tx_bitrate = sinfo.txrate; + hdd_debug("Reporting max tx rate flags %d mcs %d nss %d bw %d", + sinfo.txrate.flags, sinfo.txrate.mcs, + sinfo.txrate.nss, sinfo.txrate.bw); + } +} + +/** + * hdd_add_sta_info() - add station info attribute + * @skb: pointer to sk buff + * @hdd_sta_ctx: pointer to hdd station context + * @idx: attribute index + * + * Return: Success(0) or reason code for failure + */ +static int32_t hdd_add_sta_info(struct sk_buff *skb, + struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + int idx) +{ + struct nlattr *nla_attr; + struct hdd_station_ctx *hdd_sta_ctx; + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (!hdd_sta_ctx) { + hdd_err("Invalid sta ctx"); + goto fail; + } + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) { + hdd_err("nla_nest_start failed"); + goto fail; + } + + if (nla_put_u8(skb, NL80211_STA_INFO_SIGNAL, + (hdd_sta_ctx->cache_conn_info.signal + 100))) { + hdd_err("put fail"); + goto fail; + } + if (hdd_conn_is_connected(hdd_sta_ctx)) + hdd_get_max_tx_bitrate(hdd_ctx, adapter); + + if (hdd_add_tx_bitrate(skb, hdd_sta_ctx, NL80211_STA_INFO_TX_BITRATE)) { + hdd_err("hdd_add_tx_bitrate failed"); + goto fail; + } + + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_survey_info() - add survey info attribute + * @skb: pointer to sk buff + * @hdd_sta_ctx: pointer to hdd station context + * @idx: attribute index + * + * Return: Success(0) or reason code for failure + */ +static int32_t hdd_add_survey_info(struct sk_buff *skb, + struct hdd_station_ctx *hdd_sta_ctx, + int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + if (nla_put_u32(skb, NL80211_SURVEY_INFO_FREQUENCY, + hdd_sta_ctx->cache_conn_info.chan_freq) || + nla_put_u8(skb, NL80211_SURVEY_INFO_NOISE, + (hdd_sta_ctx->cache_conn_info.noise + 100))) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_link_standard_info() - add link info attribute + * @skb: pointer to sk buff + * @hdd_sta_ctx: pointer to hdd station context + * @idx: attribute index + * + * Return: Success(0) or reason code for failure + */ +static int32_t +hdd_add_link_standard_info(struct sk_buff *skb, + struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, int idx) +{ + struct nlattr *nla_attr; + struct hdd_station_ctx *hdd_sta_ctx; + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (!hdd_sta_ctx) { + hdd_err("Invalid sta ctx"); + goto fail; + } + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) { + hdd_err("nla_nest_start failed"); + goto fail; + } + + if (nla_put(skb, + NL80211_ATTR_SSID, + hdd_sta_ctx->cache_conn_info.last_ssid.SSID.length, + hdd_sta_ctx->cache_conn_info.last_ssid.SSID.ssId)) { + hdd_err("put fail"); + goto fail; + } + if (nla_put(skb, NL80211_ATTR_MAC, QDF_MAC_ADDR_SIZE, + hdd_sta_ctx->cache_conn_info.bssid.bytes)) { + hdd_err("put bssid failed"); + goto fail; + } + if (hdd_add_survey_info(skb, hdd_sta_ctx, NL80211_ATTR_SURVEY_INFO)) { + hdd_err("hdd_add_survey_info failed"); + goto fail; + } + + if (hdd_add_sta_info(skb, hdd_ctx, adapter, NL80211_ATTR_STA_INFO)) { + hdd_err("hdd_add_sta_info failed"); + goto fail; + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_ap_standard_info() - add ap info attribute + * @skb: pointer to sk buff + * @hdd_sta_ctx: pointer to hdd station context + * @idx: attribute index + * + * Return: Success(0) or reason code for failure + */ +static int32_t +hdd_add_ap_standard_info(struct sk_buff *skb, + struct hdd_station_ctx *hdd_sta_ctx, int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_present) + if (nla_put(skb, NL80211_ATTR_VHT_CAPABILITY, + sizeof(hdd_sta_ctx->cache_conn_info.vht_caps), + &hdd_sta_ctx->cache_conn_info.vht_caps)) { + hdd_err("put fail"); + goto fail; + } + if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_present) + if (nla_put(skb, NL80211_ATTR_HT_CAPABILITY, + sizeof(hdd_sta_ctx->cache_conn_info.ht_caps), + &hdd_sta_ctx->cache_conn_info.ht_caps)) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) && \ + defined(WLAN_FEATURE_11AX) +static int32_t hdd_add_he_oper_info( + struct sk_buff *skb, + struct hdd_station_ctx *hdd_sta_ctx) +{ + int32_t ret = 0; + + if (!hdd_sta_ctx->cache_conn_info.he_oper_len || + !hdd_sta_ctx->cache_conn_info.he_operation) + return ret; + + if (nla_put(skb, HE_OPERATION, + hdd_sta_ctx->cache_conn_info.he_oper_len, + hdd_sta_ctx->cache_conn_info.he_operation)) + ret = -EINVAL; + + qdf_mem_free(hdd_sta_ctx->cache_conn_info.he_operation); + hdd_sta_ctx->cache_conn_info.he_operation = NULL; + hdd_sta_ctx->cache_conn_info.he_oper_len = 0; + return ret; +} + +static int32_t hdd_get_he_op_len(struct hdd_station_ctx *hdd_sta_ctx) +{ + return hdd_sta_ctx->cache_conn_info.he_oper_len; +} +#else +static inline uint32_t hdd_add_he_oper_info( + struct sk_buff *skb, + struct hdd_station_ctx *hdd_sta_ctx) +{ + return 0; +} + +static uint32_t hdd_get_he_op_len(struct hdd_station_ctx *hdd_sta_ctx) +{ + return 0; +} +#endif + +/** + * hdd_get_station_info() - send BSS information to supplicant + * @hdd_ctx: pointer to hdd context + * @adapter: pointer to adapter + * + * Return: 0 if success else error status + */ +static int hdd_get_station_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct sk_buff *skb = NULL; + uint8_t *tmp_hs20 = NULL, *ies = NULL; + uint32_t nl_buf_len, ie_len = 0, hdd_he_op_len = 0; + struct hdd_station_ctx *hdd_sta_ctx; + QDF_STATUS status; + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += sizeof(hdd_sta_ctx-> + cache_conn_info.last_ssid.SSID.length) + + QDF_MAC_ADDR_SIZE + + sizeof(hdd_sta_ctx->cache_conn_info.chan_freq) + + sizeof(hdd_sta_ctx->cache_conn_info.noise) + + sizeof(hdd_sta_ctx->cache_conn_info.signal) + + (sizeof(uint32_t) * 2) + + sizeof(hdd_sta_ctx->cache_conn_info.txrate.nss) + + sizeof(hdd_sta_ctx->cache_conn_info.roam_count) + + sizeof(hdd_sta_ctx->cache_conn_info.last_auth_type) + + sizeof(hdd_sta_ctx->cache_conn_info.dot11mode) + + sizeof(uint32_t); + if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_present) + nl_buf_len += sizeof(hdd_sta_ctx->cache_conn_info.vht_caps); + if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_present) + nl_buf_len += sizeof(hdd_sta_ctx->cache_conn_info.ht_caps); + if (hdd_sta_ctx->cache_conn_info.conn_flag.hs20_present) { + tmp_hs20 = (uint8_t *)&(hdd_sta_ctx-> + cache_conn_info.hs20vendor_ie); + nl_buf_len += (sizeof(hdd_sta_ctx-> + cache_conn_info.hs20vendor_ie) - 1); + } + if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_op_present) + nl_buf_len += sizeof(hdd_sta_ctx-> + cache_conn_info.ht_operation); + if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_op_present) + nl_buf_len += sizeof(hdd_sta_ctx-> + cache_conn_info.vht_operation); + status = sme_get_prev_connected_bss_ies(hdd_ctx->mac_handle, + adapter->vdev_id, + &ies, &ie_len); + if (QDF_IS_STATUS_SUCCESS(status)) + nl_buf_len += ie_len; + + hdd_he_op_len = hdd_get_he_op_len(hdd_sta_ctx); + nl_buf_len += hdd_he_op_len; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + if (hdd_add_link_standard_info(skb, hdd_ctx, adapter, + LINK_INFO_STANDARD_NL80211_ATTR)) { + hdd_err("put fail"); + goto fail; + } + if (hdd_add_ap_standard_info(skb, hdd_sta_ctx, + AP_INFO_STANDARD_NL80211_ATTR)) { + hdd_err("put fail"); + goto fail; + } + if (nla_put_u32(skb, INFO_ROAM_COUNT, + hdd_sta_ctx->cache_conn_info.roam_count) || + nla_put_u32(skb, INFO_AKM, + hdd_convert_auth_type( + hdd_sta_ctx->cache_conn_info.last_auth_type)) || + nla_put_u32(skb, WLAN802_11_MODE, + hdd_convert_dot11mode( + hdd_sta_ctx->cache_conn_info.dot11mode))) { + hdd_err("put fail"); + goto fail; + } + if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_op_present) + if (nla_put(skb, HT_OPERATION, + (sizeof(hdd_sta_ctx->cache_conn_info.ht_operation)), + &hdd_sta_ctx->cache_conn_info.ht_operation)) { + hdd_err("put fail"); + goto fail; + } + if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_op_present) + if (nla_put(skb, VHT_OPERATION, + (sizeof(hdd_sta_ctx-> + cache_conn_info.vht_operation)), + &hdd_sta_ctx->cache_conn_info.vht_operation)) { + hdd_err("put fail"); + goto fail; + } + if (hdd_add_he_oper_info(skb, hdd_sta_ctx)) { + hdd_err("put fail"); + goto fail; + } + if (hdd_sta_ctx->cache_conn_info.conn_flag.hs20_present) + if (nla_put(skb, AP_INFO_HS20_INDICATION, + (sizeof(hdd_sta_ctx->cache_conn_info.hs20vendor_ie) + - 1), + tmp_hs20 + 1)) { + hdd_err("put fail"); + goto fail; + } + + if (nla_put_u32(skb, DISCONNECT_REASON, + adapter->last_disconnect_reason)) { + hdd_err("Failed to put disconect reason"); + goto fail; + } + + if (ie_len) { + if (nla_put(skb, BEACON_IES, ie_len, ies)) { + hdd_err("Failed to put beacon IEs"); + goto fail; + } + qdf_mem_free(ies); + ie_len = 0; + } + + return cfg80211_vendor_cmd_reply(skb); +fail: + if (skb) + kfree_skb(skb); + qdf_mem_free(ies); + ie_len = 0; + return -EINVAL; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) +static inline int32_t remote_station_put_u64(struct sk_buff *skb, + int32_t attrtype, + uint64_t value) +{ + return nla_put_u64_64bit(skb, attrtype, value, REMOTE_PAD); +} +#else +static inline int32_t remote_station_put_u64(struct sk_buff *skb, + int32_t attrtype, + uint64_t value) +{ + return nla_put_u64(skb, attrtype, value); +} +#endif + +/** + * hdd_add_survey_info_sap_get_len - get data length used in + * hdd_add_survey_info_sap() + * + * This function calculates the data length used in hdd_add_survey_info_sap() + * + * Return: total data length used in hdd_add_survey_info_sap() + */ +static uint32_t hdd_add_survey_info_sap_get_len(void) +{ + return ((NLA_HDRLEN) + (sizeof(uint32_t) + NLA_HDRLEN)); +} + +/** + * hdd_add_survey_info - add survey info attribute + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds survey info attribute to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int32_t hdd_add_survey_info_sap(struct sk_buff *skb, + struct hdd_station_info *stainfo, + int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + if (nla_put_u32(skb, NL80211_SURVEY_INFO_FREQUENCY, + stainfo->freq)) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_tx_bitrate_sap_get_len - get data length used in + * hdd_add_tx_bitrate_sap() + * + * This function calculates the data length used in hdd_add_tx_bitrate_sap() + * + * Return: total data length used in hdd_add_tx_bitrate_sap() + */ +static uint32_t hdd_add_tx_bitrate_sap_get_len(void) +{ + return ((NLA_HDRLEN) + (sizeof(uint8_t) + NLA_HDRLEN)); +} + +static uint32_t hdd_add_sta_capability_get_len(void) +{ + return nla_total_size(sizeof(uint16_t)); +} + +/** + * hdd_add_tx_bitrate_sap - add vhs nss info attribute + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds vht nss attribute to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int hdd_add_tx_bitrate_sap(struct sk_buff *skb, + struct hdd_station_info *stainfo, + int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + + if (nla_put_u8(skb, NL80211_RATE_INFO_VHT_NSS, + stainfo->nss)) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_sta_info_sap_get_len - get data length used in + * hdd_add_sta_info_sap() + * + * This function calculates the data length used in hdd_add_sta_info_sap() + * + * Return: total data length used in hdd_add_sta_info_sap() + */ +static uint32_t hdd_add_sta_info_sap_get_len(void) +{ + return ((NLA_HDRLEN) + (sizeof(uint8_t) + NLA_HDRLEN) + + hdd_add_tx_bitrate_sap_get_len() + + hdd_add_sta_capability_get_len()); +} + +/** + * hdd_add_sta_info_sap - add sta signal info attribute + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds sta signal attribute to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int32_t hdd_add_sta_info_sap(struct sk_buff *skb, int8_t rssi, + struct hdd_station_info *stainfo, int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + + if (nla_put_u8(skb, NL80211_STA_INFO_SIGNAL, + rssi - HDD_NOISE_FLOOR_DBM)) { + hdd_err("put fail"); + goto fail; + } + if (hdd_add_tx_bitrate_sap(skb, stainfo, NL80211_STA_INFO_TX_BITRATE)) + goto fail; + + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_link_standard_info_sap_get_len - get data length used in + * hdd_add_link_standard_info_sap() + * + * This function calculates the data length used in + * hdd_add_link_standard_info_sap() + * + * Return: total data length used in hdd_add_link_standard_info_sap() + */ +static uint32_t hdd_add_link_standard_info_sap_get_len(void) +{ + return ((NLA_HDRLEN) + + hdd_add_survey_info_sap_get_len() + + hdd_add_sta_info_sap_get_len() + + (sizeof(uint32_t) + NLA_HDRLEN)); +} + +/** + * hdd_add_link_standard_info_sap - add add link info attribut + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds link info attribut to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int hdd_add_link_standard_info_sap(struct sk_buff *skb, int8_t rssi, + struct hdd_station_info *stainfo, + int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + if (hdd_add_survey_info_sap(skb, stainfo, NL80211_ATTR_SURVEY_INFO)) + goto fail; + if (hdd_add_sta_info_sap(skb, rssi, stainfo, NL80211_ATTR_STA_INFO)) + goto fail; + + if (nla_put_u32(skb, NL80211_ATTR_REASON_CODE, stainfo->reason_code)) { + hdd_err("Reason code put fail"); + goto fail; + } + if (nla_put_u16(skb, NL80211_ATTR_STA_CAPABILITY, + stainfo->capability)) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_ap_standard_info_sap_get_len - get data length used in + * hdd_add_ap_standard_info_sap() + * @stainfo: station information + * + * This function calculates the data length used in + * hdd_add_ap_standard_info_sap() + * + * Return: total data length used in hdd_add_ap_standard_info_sap() + */ +static uint32_t hdd_add_ap_standard_info_sap_get_len( + struct hdd_station_info *stainfo) +{ + uint32_t len; + + len = NLA_HDRLEN; + if (stainfo->vht_present) + len += (sizeof(stainfo->vht_caps) + NLA_HDRLEN); + if (stainfo->ht_present) + len += (sizeof(stainfo->ht_caps) + NLA_HDRLEN); + + return len; +} + +/** + * hdd_add_ap_standard_info_sap - add HT and VHT info attributes + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds HT and VHT info attributes to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int hdd_add_ap_standard_info_sap(struct sk_buff *skb, + struct hdd_station_info *stainfo, + int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + + if (stainfo->vht_present) { + if (nla_put(skb, NL80211_ATTR_VHT_CAPABILITY, + sizeof(stainfo->vht_caps), + &stainfo->vht_caps)) { + hdd_err("put fail"); + goto fail; + } + } + if (stainfo->ht_present) { + if (nla_put(skb, NL80211_ATTR_HT_CAPABILITY, + sizeof(stainfo->ht_caps), + &stainfo->ht_caps)) { + hdd_err("put fail"); + goto fail; + } + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_decode_ch_width - decode channel band width based + * @ch_width: encoded enum value holding channel band width + * + * This function decodes channel band width from the given encoded enum value. + * + * Returns: decoded channel band width. + */ +static uint8_t hdd_decode_ch_width(tSirMacHTChannelWidth ch_width) +{ + switch (ch_width) { + case 0: + return 20; + case 1: + return 40; + case 2: + return 80; + case 3: + case 4: + return 160; + default: + hdd_debug("invalid enum: %d", ch_width); + return 20; + } +} + +/** + * hdd_get_peer_stats - get the peer stats + * @vdev_id: vdev id + * @mac_addr: mac address + * @stainfo: station info pointer + * + * Return: None + */ +static void hdd_get_peer_stats(uint8_t vdev_id, + struct qdf_mac_addr mac_addr, + struct hdd_station_info *stainfo) +{ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct cdp_peer_stats *peer_stats; + QDF_STATUS status; + + peer_stats = qdf_mem_malloc(sizeof(*peer_stats)); + if (!peer_stats) + return; + + status = cdp_host_get_peer_stats(soc, vdev_id, mac_addr.bytes, + peer_stats); + if (status == QDF_STATUS_SUCCESS) { + stainfo->rx_retry_cnt = peer_stats->rx.rx_retries; + stainfo->rx_mc_bc_cnt = peer_stats->rx.multicast.num + + peer_stats->rx.bcast.num; + } + + qdf_mem_free(peer_stats); +} + +/** + * hdd_get_cached_station_remote() - get cached(deleted) peer's info + * @hdd_ctx: hdd context + * @adapter: hostapd interface + * @mac_addr: mac address of requested peer + * + * This function collect and indicate the cached(deleted) peer's info + * + * Return: 0 on success, otherwise error value + */ + +static int hdd_get_cached_station_remote(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + struct qdf_mac_addr mac_addr) +{ + struct hdd_station_info *stainfo; + struct sk_buff *skb = NULL; + uint32_t nl_buf_len = NLMSG_HDRLEN; + uint8_t channel_width; + + stainfo = hdd_get_sta_info_by_mac(&adapter->cache_sta_info_list, + mac_addr.bytes, + STA_INFO_GET_CACHED_STATION_REMOTE); + + if (!stainfo) { + hdd_err("peer " QDF_MAC_ADDR_FMT " not found", + QDF_MAC_ADDR_REF(mac_addr.bytes)); + return -EINVAL; + } + + nl_buf_len += hdd_add_link_standard_info_sap_get_len() + + hdd_add_ap_standard_info_sap_get_len(stainfo) + + (sizeof(stainfo->dot11_mode) + NLA_HDRLEN) + + (sizeof(stainfo->ch_width) + NLA_HDRLEN) + + (sizeof(stainfo->tx_rate) + NLA_HDRLEN) + + (sizeof(stainfo->rx_rate) + NLA_HDRLEN) + + (sizeof(stainfo->support_mode) + NLA_HDRLEN) + + + (sizeof(stainfo->rx_mc_bc_cnt) + + NLA_HDRLEN) + + (sizeof(stainfo->rx_retry_cnt) + + NLA_HDRLEN); + if (stainfo->assoc_req_ies.len) + nl_buf_len += stainfo->assoc_req_ies.len + NLA_HDRLEN; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hdd_put_sta_info_ref(&adapter->cache_sta_info_list, + &stainfo, true, + STA_INFO_GET_CACHED_STATION_REMOTE); + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + if (hdd_add_link_standard_info_sap(skb, stainfo->rssi, stainfo, + LINK_INFO_STANDARD_NL80211_ATTR)) { + hdd_err("link standard put fail"); + goto fail; + } + + if (hdd_add_ap_standard_info_sap(skb, stainfo, + AP_INFO_STANDARD_NL80211_ATTR)) { + hdd_err("ap standard put fail"); + goto fail; + } + + /* upper layer expects decoded channel BW */ + channel_width = hdd_decode_ch_width(stainfo->ch_width); + + if (nla_put_u32(skb, REMOTE_SUPPORTED_MODE, + stainfo->support_mode) || + nla_put_u8(skb, REMOTE_CH_WIDTH, channel_width)) { + hdd_err("remote ch put fail"); + goto fail; + } + /* Convert the data from kbps to mbps as expected by the user space */ + if (nla_put_u32(skb, REMOTE_LAST_TX_RATE, stainfo->tx_rate / 1000)) { + hdd_err("tx rate put fail"); + goto fail; + } + /* Convert the data from kbps to mbps as expected by the user space */ + if (nla_put_u32(skb, REMOTE_LAST_RX_RATE, stainfo->rx_rate / 1000)) { + hdd_err("rx rate put fail"); + goto fail; + } + if (nla_put_u32(skb, WLAN802_11_MODE, stainfo->dot11_mode)) { + hdd_err("dot11 mode put fail"); + goto fail; + } + + if (!(stainfo->rx_mc_bc_cnt & HDD_STATION_INFO_RX_MC_BC_COUNT)) { + hdd_debug("rx mc bc count is not supported by FW"); + } + + else if (nla_put_u32(skb, REMOTE_RX_BC_MC_COUNT, + (stainfo->rx_mc_bc_cnt & + (~HDD_STATION_INFO_RX_MC_BC_COUNT)))) { + hdd_err("rx mc bc put fail"); + goto fail; + } + + /* Currently rx_retry count is not supported */ + if (stainfo->rx_retry_cnt) { + if (nla_put_u32(skb, REMOTE_RX_RETRY_COUNT, + stainfo->rx_retry_cnt)) { + hdd_err("rx retry count put fail"); + goto fail; + } + } + + if (stainfo->assoc_req_ies.len) { + if (nla_put(skb, ASSOC_REQ_IES, stainfo->assoc_req_ies.len, + stainfo->assoc_req_ies.data)) { + hdd_err("Failed to put assoc req IEs"); + goto fail; + } + } + hdd_sta_info_detach(&adapter->cache_sta_info_list, &stainfo); + hdd_put_sta_info_ref(&adapter->cache_sta_info_list, &stainfo, true, + STA_INFO_GET_CACHED_STATION_REMOTE); + qdf_atomic_dec(&adapter->cache_sta_count); + + return cfg80211_vendor_cmd_reply(skb); +fail: + hdd_put_sta_info_ref(&adapter->cache_sta_info_list, &stainfo, true, + STA_INFO_GET_CACHED_STATION_REMOTE); + if (skb) + kfree_skb(skb); + + return -EINVAL; +} + +/** + * hdd_get_cached_station_remote() - get connected peer's info + * @hdd_ctx: hdd context + * @adapter: hostapd interface + * @mac_addr: mac address of requested peer + * + * This function collect and indicate the connected peer's info + * + * Return: 0 on success, otherwise error value + */ +static int hdd_get_connected_station_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + struct qdf_mac_addr mac_addr, + struct hdd_station_info *stainfo) +{ + struct sk_buff *skb = NULL; + struct nlattr *attr; + uint32_t nl_buf_len; + struct sir_peer_info_ext peer_info; + bool txrx_rate = true; + bool value; + QDF_STATUS status; + int i; + + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += hdd_add_link_standard_info_sap_get_len() + + (NLA_ALIGN(sizeof(stainfo->max_phy_rate)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_packets)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_bytes)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->rx_packets)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->rx_bytes)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->rx_mc_bc_cnt)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->is_qos_enabled)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->ch_width)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->dot11_mode)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->mode)) + NLA_HDRLEN); + + hdd_get_peer_stats(adapter->vdev_id, mac_addr, stainfo); + + status = ucfg_mlme_get_sap_get_peer_info(hdd_ctx->psoc, &value); + if (status != QDF_STATUS_SUCCESS) + hdd_err("Unable to fetch sap ger peer info"); + if (!value || + wlan_hdd_get_peer_info(adapter, mac_addr, &peer_info)) { + hdd_err("fail to get tx/rx rate"); + txrx_rate = false; + } else { + stainfo->tx_failed = peer_info.tx_failed; + + stainfo->rx_last_pkt_rssi = peer_info.rssi; + if (stainfo->rssi == 0) + stainfo->rssi = peer_info.rssi; + + for (i = 0; i < WMI_MAX_CHAINS; i++) + stainfo->peer_rssi_per_chain[i] = + peer_info.peer_rssi_per_chain[i]; + + stainfo->tx_retry_succeed = + peer_info.tx_retries - peer_info.tx_failed; + + stainfo->tx_retry = 0; + stainfo->tx_retry_exhaust = 0; + + stainfo->tx_total_fw = peer_info.tx_packets; + stainfo->tx_retry_fw = peer_info.tx_retries; + stainfo->tx_retry_exhaust_fw = peer_info.tx_failed; + + stainfo->tx_rate = peer_info.tx_rate; + stainfo->rx_rate = peer_info.rx_rate; + + nl_buf_len += + (NLA_ALIGN(sizeof(stainfo->rx_retry_cnt)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_failed)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->rx_last_pkt_rssi)) + + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_retry_succeed)) + + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_retry)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_retry_exhaust)) + + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_total_fw)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_retry_fw)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_retry_exhaust_fw)) + + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_rate)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->rx_rate)) + NLA_HDRLEN); + + nl_buf_len += NLA_HDRLEN; + for (i = 0; i < WMI_MAX_CHAINS; i++) + nl_buf_len += + (NLA_ALIGN(sizeof(stainfo->peer_rssi_per_chain[i])) + + NLA_HDRLEN); + } + + /* below info is only valid for HT/VHT mode */ + if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) + nl_buf_len += + (NLA_ALIGN(sizeof(stainfo->ampdu)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->tx_stbc)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->rx_stbc)) + NLA_HDRLEN) + + (NLA_ALIGN(sizeof(stainfo->sgi_enable)) + NLA_HDRLEN); + + hdd_info("buflen %d hdrlen %d", nl_buf_len, NLMSG_HDRLEN); + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, + nl_buf_len); + if (!skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + goto fail; + } + + hdd_info("stainfo"); + hdd_info("maxrate %x tx_pkts %x tx_bytes %llx", + stainfo->max_phy_rate, stainfo->tx_packets, + stainfo->tx_bytes); + hdd_info("rx_pkts %x rx_bytes %llx mode %x", + stainfo->rx_packets, stainfo->rx_bytes, + stainfo->mode); + if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) { + hdd_info("ampdu %d tx_stbc %d rx_stbc %d", + stainfo->ampdu, stainfo->tx_stbc, + stainfo->rx_stbc); + hdd_info("wmm %d chwidth %d sgi %d", + stainfo->is_qos_enabled, + stainfo->ch_width, + stainfo->sgi_enable); + } + + if (hdd_add_link_standard_info_sap(skb, stainfo->rssi, stainfo, + LINK_INFO_STANDARD_NL80211_ATTR)) { + hdd_err("link standard put fail"); + goto fail; + } + + if (nla_put_u32(skb, REMOTE_RX_BC_MC_COUNT, + (stainfo->rx_mc_bc_cnt & + (~HDD_STATION_INFO_RX_MC_BC_COUNT))) || + nla_put_u8(skb, REMOTE_CH_WIDTH, stainfo->ch_width) || + nla_put_u32(skb, WLAN802_11_MODE, stainfo->dot11_mode) || + nla_put_u32(skb, REMOTE_MAX_PHY_RATE, stainfo->max_phy_rate) || + nla_put_u32(skb, REMOTE_TX_PACKETS, stainfo->tx_packets) || + remote_station_put_u64(skb, REMOTE_TX_BYTES, stainfo->tx_bytes) || + nla_put_u32(skb, REMOTE_RX_PACKETS, stainfo->rx_packets) || + remote_station_put_u64(skb, REMOTE_RX_BYTES, stainfo->rx_bytes) || + nla_put_u8(skb, REMOTE_WMM, stainfo->is_qos_enabled) || + nla_put_u8(skb, REMOTE_SUPPORTED_MODE, stainfo->mode)) { + hdd_err("put fail"); + goto fail; + } + + if (txrx_rate) { + if (nla_put_u32(skb, REMOTE_TX_FAILURE, stainfo->tx_failed) || + nla_put_u32(skb, REMOTE_RX_RETRY_COUNT, + stainfo->rx_retry_cnt) || + nla_put_u32(skb, REMOTE_TX_RETRY_SUCCEED, + stainfo->tx_retry_succeed) || + nla_put_u32(skb, REMOTE_RX_LAST_PKT_RSSI, + stainfo->rx_last_pkt_rssi) || + nla_put_u32(skb, REMOTE_TX_RETRY, stainfo->tx_retry) || + nla_put_u32(skb, REMOTE_TX_RETRY_EXHAUST, + stainfo->tx_retry_exhaust) || + nla_put_u32(skb, REMOTE_TX_TOTAL_FW, + stainfo->tx_total_fw) || + nla_put_u32(skb, REMOTE_TX_RETRY_FW, + stainfo->tx_retry_fw) || + nla_put_u32(skb, REMOTE_TX_RETRY_EXHAUST_FW, + stainfo->tx_retry_exhaust_fw) || + nla_put_u32(skb, REMOTE_LAST_TX_RATE, stainfo->tx_rate) || + nla_put_u32(skb, REMOTE_LAST_RX_RATE, stainfo->rx_rate)) { + hdd_err("put fail"); + goto fail; + } + + attr = nla_nest_start(skb, REMOTE_AVG_RSSI_PER_CHAIN); + if (!attr) + goto fail; + for (i = 0; i < WMI_MAX_CHAINS; i++) { + if (nla_put_u32(skb, i, + stainfo->peer_rssi_per_chain[i])) { + hdd_err("Failed to rssi per chain"); + goto fail; + } + } + nla_nest_end(skb, attr); + + hdd_info("tx_rate %x rx_rate %x", + stainfo->tx_rate, stainfo->rx_rate); + } + + if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) { + if (nla_put_u8(skb, REMOTE_AMPDU, stainfo->ampdu) || + nla_put_u8(skb, REMOTE_TX_STBC, stainfo->tx_stbc) || + nla_put_u8(skb, REMOTE_RX_STBC, stainfo->rx_stbc) || + nla_put_u8(skb, REMOTE_SGI_ENABLE, stainfo->sgi_enable)) { + hdd_err("put fail"); + goto fail; + } + } + + return cfg80211_vendor_cmd_reply(skb); + +fail: + if (skb) + kfree_skb(skb); + + return -EINVAL; +} + +/** + * hdd_get_station_remote() - get remote peer's info + * @hdd_ctx: hdd context + * @adapter: hostapd interface + * @mac_addr: mac address of requested peer + * + * This function collect and indicate the remote peer's info + * + * Return: 0 on success, otherwise error value + */ +static int hdd_get_station_remote(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + struct qdf_mac_addr mac_addr) +{ + int status = 0; + bool is_associated = false; + struct hdd_station_info *stainfo = + hdd_get_sta_info_by_mac( + &adapter->sta_info_list, + mac_addr.bytes, + STA_INFO_HDD_GET_STATION_REMOTE); + + if (!stainfo) { + status = hdd_get_cached_station_remote(hdd_ctx, adapter, + mac_addr); + return status; + } + + is_associated = hdd_is_peer_associated(adapter, &mac_addr); + if (!is_associated) { + status = hdd_get_cached_station_remote(hdd_ctx, adapter, + mac_addr); + hdd_put_sta_info_ref(&adapter->sta_info_list, &stainfo, true, + STA_INFO_HDD_GET_STATION_REMOTE); + return status; + } + + status = hdd_get_connected_station_info(hdd_ctx, adapter, + mac_addr, stainfo); + hdd_put_sta_info_ref(&adapter->sta_info_list, &stainfo, true, + STA_INFO_HDD_GET_STATION_REMOTE); + return status; +} + +/** + * __hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd + * @wiphy: corestack handler + * @wdev: wireless device + * @data: data + * @data_len: data length + * + * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION. + * Validate cmd attributes and send the station info to upper layers. + * + * Return: Success(0) or reason code for failure + */ +static int +__hdd_cfg80211_get_station_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX + 1]; + int32_t status; + + hdd_enter_dev(dev); + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + status = -EPERM; + goto out; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status != 0) + goto out; + + status = wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX, + data, data_len, + hdd_get_station_policy); + if (status) { + hdd_err("Invalid ATTR"); + goto out; + } + + /* Parse and fetch Command Type*/ + if (tb[STATION_INFO]) { + status = hdd_get_station_info(hdd_ctx, adapter); + } else if (tb[STATION_ASSOC_FAIL_REASON]) { + status = hdd_get_station_assoc_fail(hdd_ctx, adapter); + } else if (tb[STATION_REMOTE]) { + struct qdf_mac_addr mac_addr; + + if (adapter->device_mode != QDF_SAP_MODE && + adapter->device_mode != QDF_P2P_GO_MODE) { + hdd_err("invalid device_mode:%d", adapter->device_mode); + status = -EINVAL; + goto out; + } + + nla_memcpy(mac_addr.bytes, tb[STATION_REMOTE], + QDF_MAC_ADDR_SIZE); + + hdd_debug("STATION_REMOTE " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_addr.bytes)); + + status = hdd_get_station_remote(hdd_ctx, adapter, mac_addr); + } else { + hdd_err("get station info cmd type failed"); + status = -EINVAL; + goto out; + } + hdd_exit(); +out: + return status; +} + +int32_t hdd_cfg80211_get_station_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_cfg80211_get_station_cmd(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static uint32_t +hdd_get_connect_fail_reason_code_len(struct hdd_adapter *adapter) +{ + if (adapter->connect_req_status == STATUS_SUCCESS) + return 0; + + return nla_total_size(sizeof(uint32_t)); +} + +/** + * hdd_get_umac_to_osif_connect_fail_reason() - Convert to qca internal connect + * fail reason + * @internal_reason: Mac reason code of type @wlan_status_code + * + * Check if it is internal status code and convert it to the + * enum qca_sta_connect_fail_reason_codes. + * + * Return: Reason code of type enum qca_sta_connect_fail_reason_codes + */ +static enum qca_sta_connect_fail_reason_codes +hdd_get_umac_to_osif_connect_fail_reason(enum wlan_status_code internal_reason) +{ + enum qca_sta_connect_fail_reason_codes reason = 0; + + if (internal_reason < STATUS_PROP_START) + return reason; + + switch (internal_reason) { + case STATUS_NO_NETWORK_FOUND: + reason = QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND; + break; + case STATUS_AUTH_TX_FAIL: + reason = QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL; + break; + case STATUS_AUTH_NO_ACK_RECEIVED: + reason = QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED; + break; + case STATUS_AUTH_NO_RESP_RECEIVED: + reason = QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED; + break; + case STATUS_ASSOC_TX_FAIL: + reason = QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL; + break; + case STATUS_ASSOC_NO_ACK_RECEIVED: + reason = QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED; + break; + case STATUS_ASSOC_NO_RESP_RECEIVED: + reason = QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED; + break; + default: + hdd_debug("QCA code not present for internal status code %d", + internal_reason); + } + + return reason; +} + +/** + * hdd_add_connect_fail_reason_code() - Fills connect fail reason code + * @skb: pointer to skb + * @adapter: pointer to hdd adapter + * + * Return: on success 0 else error code + */ +static int hdd_add_connect_fail_reason_code(struct sk_buff *skb, + struct hdd_adapter *adapter) +{ + uint32_t reason; + + reason = hdd_get_umac_to_osif_connect_fail_reason( + adapter->connect_req_status); + if (!reason) + return 0; + + if (nla_put_u32(skb, STA_INFO_CONNECT_FAIL_REASON_CODE, reason)) { + hdd_err("put fail"); + return -EINVAL; + } + + return 0; +} + +/** + * hdd_get_station_info_ex() - send STA info to userspace, for STA mode only + * @hdd_ctx: pointer to hdd context + * @adapter: pointer to adapter + * + * Return: 0 if success else error status + */ +static int hdd_get_station_info_ex(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter) +{ + struct sk_buff *skb; + uint32_t nl_buf_len = 0, connect_fail_rsn_len; + + connect_fail_rsn_len = hdd_get_connect_fail_reason_code_len(adapter); + nl_buf_len = connect_fail_rsn_len; + + nl_buf_len += NLMSG_HDRLEN; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hdd_err_rl("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + if (connect_fail_rsn_len) { + if (hdd_add_connect_fail_reason_code(skb, adapter)) { + hdd_err_rl("hdd_add_connect_fail_reason_code fail"); + return -ENOMEM; + } + } + + return cfg80211_vendor_cmd_reply(skb); +} + +/** + * __hdd_cfg80211_get_sta_info_cmd() - Handle get sta info vendor cmd + * @wiphy: pointer to wireless phy + * @wdev: wireless device + * @data: data + * @data_len: data length + * + * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO. + * Validate cmd attributes and send the station info to upper layers. + * + * Return: Success(0) or reason code for failure + */ +static int +__hdd_cfg80211_get_sta_info_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX + 1]; + struct qdf_mac_addr mac_addr; + int32_t status; + + hdd_enter_dev(dev); + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE || + hdd_get_conparam() == QDF_GLOBAL_MONITOR_MODE) { + hdd_err_rl("Command not allowed in FTM / Monitor mode"); + status = -EPERM; + goto out; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status != 0) + goto out; + + status = wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX, + data, data_len, + hdd_get_sta_policy); + if (status) { + hdd_err_rl("Invalid ATTR"); + goto out; + } + + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + status = hdd_get_station_info_ex(hdd_ctx, adapter); + break; + case QDF_SAP_MODE: + case QDF_P2P_GO_MODE: + if (!tb[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC]) { + hdd_err_rl("MAC address is not present"); + status = -EINVAL; + goto out; + } + + nla_memcpy(mac_addr.bytes, + tb[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC], + QDF_MAC_ADDR_SIZE); + hdd_debug("STA " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_addr.bytes)); + break; + default: + hdd_err_rl("Invalid device_mode: %d", adapter->device_mode); + status = -EINVAL; + goto out; + } + + hdd_exit(); +out: + return status; +} + +int32_t hdd_cfg80211_get_sta_info_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_cfg80211_get_sta_info_cmd(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_station_info.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_station_info.h new file mode 100644 index 0000000000000000000000000000000000000000..8bf904cbce28014f8716d4ee3d28ddbcdec8f8dc --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_station_info.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_STATION_INFO_H +#define __WLAN_HDD_STATION_INFO_H + +/** + * DOC: wlan_hdd_station_info_h + * + * WLAN Host Device Driver STATION info API specification + */ + +#ifdef FEATURE_STATION_INFO +/** + * wlan_hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd + * @wiphy: corestack handler + * @wdev: wireless device + * @data: data + * @data_len: data length + * + * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION. + * Validate cmd attributes and send the station info to upper layers. + * + * Return: Success(0) or reason code for failure + */ +int32_t hdd_cfg80211_get_station_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * hdd_cfg80211_get_sta_info_cmd() - Handle get sta info vendor cmd + * @wiphy: corestack handler + * @wdev: wireless device + * @data: data + * @data_len: data length + * + * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO. + * Validate cmd attributes and send the sta info to upper layers. + * + * Return: Success(0) or reason code for failure + */ +int32_t hdd_cfg80211_get_sta_info_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_STATION_INFO_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_STATION, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = hdd_cfg80211_get_station_cmd \ +}, \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = hdd_cfg80211_get_sta_info_cmd, \ +}, +#else /* FEATURE_STATION_INFO */ +#define FEATURE_STATION_INFO_VENDOR_COMMANDS +#endif /* FEATURE_STATION_INFO */ + +#endif /* __WLAN_HDD_STATION_INFO_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_stats.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_stats.c new file mode 100644 index 0000000000000000000000000000000000000000..2ee7cbef652bbd21be297820419623ae9cdc8791 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_stats.c @@ -0,0 +1,6345 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_stats.c + * + * WLAN Host Device Driver statistics related implementation + * + */ + +#include "wlan_hdd_stats.h" +#include "sme_api.h" +#include "cds_sched.h" +#include "osif_sync.h" +#include "wlan_hdd_trace.h" +#include "wlan_hdd_lpass.h" +#include "hif.h" +#include +#include "wma_api.h" +#include "wlan_hdd_hostapd.h" +#include "wlan_osif_request_manager.h" +#include "wlan_hdd_debugfs_llstat.h" +#include "wlan_hdd_debugfs_mibstat.h" +#include "wlan_reg_services_api.h" +#include +#include "wlan_cp_stats_mc_ucfg_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_hdd_sta_info.h" +#include "cdp_txrx_host_stats.h" +#include "cdp_txrx_misc.h" +#include "wlan_hdd_object_manager.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS) +#define HDD_INFO_SIGNAL STATION_INFO_SIGNAL +#define HDD_INFO_SIGNAL_AVG STATION_INFO_SIGNAL_AVG +#define HDD_INFO_TX_PACKETS STATION_INFO_TX_PACKETS +#define HDD_INFO_TX_RETRIES STATION_INFO_TX_RETRIES +#define HDD_INFO_TX_FAILED STATION_INFO_TX_FAILED +#define HDD_INFO_TX_BITRATE STATION_INFO_TX_BITRATE +#define HDD_INFO_RX_BITRATE STATION_INFO_RX_BITRATE +#define HDD_INFO_TX_BYTES STATION_INFO_TX_BYTES +#define HDD_INFO_CHAIN_SIGNAL_AVG STATION_INFO_CHAIN_SIGNAL_AVG +#define HDD_INFO_RX_BYTES STATION_INFO_RX_BYTES +#define HDD_INFO_RX_PACKETS STATION_INFO_RX_PACKETS +#define HDD_INFO_TX_BYTES64 0 +#define HDD_INFO_RX_BYTES64 0 +#define HDD_INFO_INACTIVE_TIME 0 +#define HDD_INFO_CONNECTED_TIME 0 +#define HDD_INFO_RX_MPDUS 0 +#define HDD_INFO_FCS_ERROR_COUNT 0 +#else +#define HDD_INFO_SIGNAL BIT(NL80211_STA_INFO_SIGNAL) +#define HDD_INFO_SIGNAL_AVG BIT(NL80211_STA_INFO_SIGNAL_AVG) +#define HDD_INFO_TX_PACKETS BIT(NL80211_STA_INFO_TX_PACKETS) +#define HDD_INFO_TX_RETRIES BIT(NL80211_STA_INFO_TX_RETRIES) +#define HDD_INFO_TX_FAILED BIT(NL80211_STA_INFO_TX_FAILED) +#define HDD_INFO_TX_BITRATE BIT(NL80211_STA_INFO_TX_BITRATE) +#define HDD_INFO_RX_BITRATE BIT(NL80211_STA_INFO_RX_BITRATE) +#define HDD_INFO_TX_BYTES BIT(NL80211_STA_INFO_TX_BYTES) +#define HDD_INFO_CHAIN_SIGNAL_AVG BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG) +#define HDD_INFO_RX_BYTES BIT(NL80211_STA_INFO_RX_BYTES) +#define HDD_INFO_RX_PACKETS BIT(NL80211_STA_INFO_RX_PACKETS) +#define HDD_INFO_TX_BYTES64 BIT(NL80211_STA_INFO_TX_BYTES64) +#define HDD_INFO_RX_BYTES64 BIT(NL80211_STA_INFO_RX_BYTES64) +#define HDD_INFO_INACTIVE_TIME BIT(NL80211_STA_INFO_INACTIVE_TIME) +#define HDD_INFO_CONNECTED_TIME BIT(NL80211_STA_INFO_CONNECTED_TIME) +#define HDD_INFO_RX_MPDUS BIT_ULL(NL80211_STA_INFO_RX_MPDUS) +#define HDD_INFO_FCS_ERROR_COUNT BIT_ULL(NL80211_STA_INFO_FCS_ERROR_COUNT) +#endif /* kernel version less than 4.0.0 && no_backport */ + +#define HDD_LINK_STATS_MAX 5 + +/* 11B, 11G Rate table include Basic rate and Extended rate + * The IDX field is the rate index + * The HI field is the rate when RSSI is strong or being ignored + * (in this case we report actual rate) + * The MID field is the rate when RSSI is moderate + * (in this case we cap 11b rates at 5.5 and 11g rates at 24) + * The LO field is the rate when RSSI is low + * (in this case we don't report rates, actual current rate used) + */ +static const struct index_data_rate_type supported_data_rate[] = { + /* IDX HI HM LM LO (RSSI-based index */ + {2, { 10, 10, 10, 0} }, + {4, { 20, 20, 10, 0} }, + {11, { 55, 20, 10, 0} }, + {12, { 60, 55, 20, 0} }, + {18, { 90, 55, 20, 0} }, + {22, {110, 55, 20, 0} }, + {24, {120, 90, 60, 0} }, + {36, {180, 120, 60, 0} }, + {44, {220, 180, 60, 0} }, + {48, {240, 180, 90, 0} }, + {66, {330, 180, 90, 0} }, + {72, {360, 240, 90, 0} }, + {96, {480, 240, 120, 0} }, + {108, {540, 240, 120, 0} } +}; +/* MCS Based rate table HT MCS parameters with Nss = 1 */ +static struct index_data_rate_type supported_mcs_rate_nss1[] = { +/* MCS L20 L40 S20 S40 */ + {0, {65, 135, 72, 150} }, + {1, {130, 270, 144, 300} }, + {2, {195, 405, 217, 450} }, + {3, {260, 540, 289, 600} }, + {4, {390, 810, 433, 900} }, + {5, {520, 1080, 578, 1200} }, + {6, {585, 1215, 650, 1350} }, + {7, {650, 1350, 722, 1500} } +}; + +/* HT MCS parameters with Nss = 2 */ +static struct index_data_rate_type supported_mcs_rate_nss2[] = { +/* MCS L20 L40 S20 S40 */ + {0, {130, 270, 144, 300} }, + {1, {260, 540, 289, 600} }, + {2, {390, 810, 433, 900} }, + {3, {520, 1080, 578, 1200} }, + {4, {780, 1620, 867, 1800} }, + {5, {1040, 2160, 1156, 2400} }, + {6, {1170, 2430, 1300, 2700} }, + {7, {1300, 2700, 1444, 3000} } +}; + +/* MCS Based VHT rate table MCS parameters with Nss = 1*/ +static struct index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = { +/* MCS L80 S80 L40 S40 L20 S40*/ + {0, {293, 325}, {135, 150}, {65, 72} }, + {1, {585, 650}, {270, 300}, {130, 144} }, + {2, {878, 975}, {405, 450}, {195, 217} }, + {3, {1170, 1300}, {540, 600}, {260, 289} }, + {4, {1755, 1950}, {810, 900}, {390, 433} }, + {5, {2340, 2600}, {1080, 1200}, {520, 578} }, + {6, {2633, 2925}, {1215, 1350}, {585, 650} }, + {7, {2925, 3250}, {1350, 1500}, {650, 722} }, + {8, {3510, 3900}, {1620, 1800}, {780, 867} }, + {9, {3900, 4333}, {1800, 2000}, {780, 867} } +}; + +/*MCS parameters with Nss = 2*/ +static struct index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = { +/* MCS L80 S80 L40 S40 L20 S40*/ + {0, {585, 650}, {270, 300}, {130, 144} }, + {1, {1170, 1300}, {540, 600}, {260, 289} }, + {2, {1755, 1950}, {810, 900}, {390, 433} }, + {3, {2340, 2600}, {1080, 1200}, {520, 578} }, + {4, {3510, 3900}, {1620, 1800}, {780, 867} }, + {5, {4680, 5200}, {2160, 2400}, {1040, 1156} }, + {6, {5265, 5850}, {2430, 2700}, {1170, 1300} }, + {7, {5850, 6500}, {2700, 3000}, {1300, 1444} }, + {8, {7020, 7800}, {3240, 3600}, {1560, 1733} }, + {9, {7800, 8667}, {3600, 4000}, {1730, 1920} } +}; + +/*array index points to MCS and array value points respective rssi*/ +static int rssi_mcs_tbl[][12] = { +/* MCS 0 1 2 3 4 5 6 7 8 9 10 11*/ + {-82, -79, -77, -74, -70, -66, -65, -64, -59, -57, -52, -48}, /* 20 */ + {-79, -76, -74, -71, -67, -63, -62, -61, -56, -54, -49, -45}, /* 40 */ + {-76, -73, -71, -68, -64, -60, -59, -58, -53, -51, -46, -42} /* 80 */ +}; + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + +/** + * struct hdd_ll_stats - buffered hdd link layer stats + * @ll_stats_node: pointer to next stats buffered in scheduler thread context + * @result_param_id: Received link layer stats ID + * @result: received stats from FW + * @more_data: if more stats are pending + * @no_of_radios: no of radios + * @no_of_peers: no of peers + */ +struct hdd_ll_stats { + qdf_list_node_t ll_stats_node; + u32 result_param_id; + void *result; + u32 more_data; + union { + u32 no_of_radios; + u32 no_of_peers; + } stats_nradio_npeer; +}; + +/** + * struct hdd_ll_stats_priv - hdd link layer stats private + * @ll_stats: head to different link layer stats received in scheduler + * thread context + * @request_id: userspace-assigned link layer stats request id + * @request_bitmap: userspace-assigned link layer stats request bitmap + * @ll_stats_lock: Lock to serially access request_bitmap + */ +struct hdd_ll_stats_priv { + qdf_list_t ll_stats_q; + uint32_t request_id; + uint32_t request_bitmap; + qdf_spinlock_t ll_stats_lock; +}; + +/* + * Used to allocate the size of 4096 for the link layer stats. + * The size of 4096 is considered assuming that all data per + * respective event fit with in the limit.Please take a call + * on the limit based on the data requirements on link layer + * statistics. + */ +#define LL_STATS_EVENT_BUF_SIZE 4096 + +/** + * put_wifi_rate_stat() - put wifi rate stats + * @stats: Pointer to stats context + * @vendor_event: Pointer to vendor event + * + * Return: bool + */ +static bool put_wifi_rate_stat(struct wifi_rate_stat *stats, + struct sk_buff *vendor_event) +{ + if (nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE, + stats->rate.preamble) || + nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS, + stats->rate.nss) || + nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW, + stats->rate.bw) || + nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX, + stats->rate.rate_or_mcs_index) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE, + stats->rate.bitrate) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU, + stats->tx_mpdu) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU, + stats->rx_mpdu) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST, + stats->mpdu_lost) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES, + stats->retries) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT, + stats->retries_short) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG, + stats->retries_long)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return false; + } + + return true; +} + +/** + * put_wifi_peer_rates() - put wifi peer rate info + * @stats: Pointer to stats context + * @vendor_event: Pointer to vendor event + * + * Return: bool + */ +static bool put_wifi_peer_rates(struct wifi_peer_info *stats, + struct sk_buff *vendor_event) +{ + uint32_t i; + struct wifi_rate_stat *rate_stat; + int nest_id; + struct nlattr *info; + struct nlattr *rates; + + /* no rates is ok */ + if (!stats->num_rate) + return true; + + nest_id = QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO; + info = nla_nest_start(vendor_event, nest_id); + if (!info) + return false; + + for (i = 0; i < stats->num_rate; i++) { + rates = nla_nest_start(vendor_event, i); + if (!rates) + return false; + rate_stat = &stats->rate_stats[i]; + if (!put_wifi_rate_stat(rate_stat, vendor_event)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return false; + } + nla_nest_end(vendor_event, rates); + } + nla_nest_end(vendor_event, info); + + return true; +} + +/** + * put_wifi_peer_info() - put wifi peer info + * @stats: Pointer to stats context + * @vendor_event: Pointer to vendor event + * + * Return: bool + */ +static bool put_wifi_peer_info(struct wifi_peer_info *stats, + struct sk_buff *vendor_event) +{ + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE, + wmi_to_sir_peer_type(stats->type)) || + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS, + QDF_MAC_ADDR_SIZE, &stats->peer_macaddr.bytes[0]) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES, + stats->capabilities) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES, + stats->num_rate)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return false; + } + + return put_wifi_peer_rates(stats, vendor_event); +} + +/** + * put_wifi_wmm_ac_stat() - put wifi wmm ac stats + * @stats: Pointer to stats context + * @vendor_event: Pointer to vendor event + * + * Return: bool + */ +static bool put_wifi_wmm_ac_stat(wmi_wmm_ac_stats *stats, + struct sk_buff *vendor_event) +{ + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC, + stats->ac_type) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU, + stats->tx_mpdu) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU, + stats->rx_mpdu) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST, + stats->tx_mcast) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST, + stats->rx_mcast) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU, + stats->rx_ampdu) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU, + stats->tx_ampdu) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST, + stats->mpdu_lost) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES, + stats->retries) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT, + stats->retries_short) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG, + stats->retries_long) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN, + stats->contention_time_min) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX, + stats->contention_time_max) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG, + stats->contention_time_avg) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES, + stats->contention_num_samples)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return false; + } + + return true; +} + +/** + * put_wifi_interface_info() - put wifi interface info + * @stats: Pointer to stats context + * @vendor_event: Pointer to vendor event + * + * Return: bool + */ +static bool put_wifi_interface_info(struct wifi_interface_info *stats, + struct sk_buff *vendor_event) +{ + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE, + stats->mode) || + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR, + QDF_MAC_ADDR_SIZE, stats->macAddr.bytes) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE, + stats->state) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING, + stats->roaming) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES, + stats->capabilities) || + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID, + strlen(stats->ssid), stats->ssid) || + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID, + QDF_MAC_ADDR_SIZE, stats->bssid.bytes) || + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR, + CFG_COUNTRY_CODE_LEN, stats->apCountryStr) || + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR, + CFG_COUNTRY_CODE_LEN, stats->countryStr)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return false; + } + + return true; +} + +/** + * put_wifi_iface_stats() - put wifi interface stats + * @if_stat: Pointer to interface stats context + * @num_peer: Number of peers + * @vendor_event: Pointer to vendor event + * + * Return: bool + */ +static bool put_wifi_iface_stats(struct wifi_interface_stats *if_stat, + u32 num_peers, struct sk_buff *vendor_event) +{ + int i = 0; + struct nlattr *wmm_info; + struct nlattr *wmm_stats; + u64 average_tsf_offset; + wmi_iface_link_stats *link_stats = &if_stat->link_stats; + + if (!put_wifi_interface_info(&if_stat->info, vendor_event)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return false; + + } + + average_tsf_offset = link_stats->avg_bcn_spread_offset_high; + average_tsf_offset = (average_tsf_offset << 32) | + link_stats->avg_bcn_spread_offset_low; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE, + QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_IFACE) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS, + num_peers) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX, + link_stats->beacon_rx) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX, + link_stats->mgmt_rx) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX, + link_stats->mgmt_action_rx) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX, + link_stats->mgmt_action_tx) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT, + link_stats->rssi_mgmt) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA, + link_stats->rssi_data) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK, + link_stats->rssi_ack) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED, + link_stats->is_leaky_ap) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED, + link_stats->avg_rx_frms_leaked) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME, + link_stats->rx_leak_window) || + hdd_wlan_nla_put_u64(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET, + average_tsf_offset) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT, + if_stat->rts_succ_cnt) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT, + if_stat->rts_fail_cnt) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT, + if_stat->ppdu_succ_cnt) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT, + if_stat->ppdu_fail_cnt)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return false; + } + + wmm_info = nla_nest_start(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO); + if (!wmm_info) + return false; + + for (i = 0; i < WIFI_AC_MAX; i++) { + wmm_stats = nla_nest_start(vendor_event, i); + if (!wmm_stats) + return false; + + if (!put_wifi_wmm_ac_stat(&if_stat->ac_stats[i], + vendor_event)) { + hdd_err("put_wifi_wmm_ac_stat Fail"); + return false; + } + + nla_nest_end(vendor_event, wmm_stats); + } + nla_nest_end(vendor_event, wmm_info); + return true; +} + +/** + * hdd_map_device_to_ll_iface_mode() - map device to link layer interface mode + * @device_mode: Device mode + * + * Return: interface mode + */ +static tSirWifiInterfaceMode hdd_map_device_to_ll_iface_mode(int device_mode) +{ + switch (device_mode) { + case QDF_STA_MODE: + return WIFI_INTERFACE_STA; + case QDF_SAP_MODE: + return WIFI_INTERFACE_SOFTAP; + case QDF_P2P_CLIENT_MODE: + return WIFI_INTERFACE_P2P_CLIENT; + case QDF_P2P_GO_MODE: + return WIFI_INTERFACE_P2P_GO; + case QDF_IBSS_MODE: + return WIFI_INTERFACE_IBSS; + default: + /* Return Interface Mode as STA for all the unsupported modes */ + return WIFI_INTERFACE_STA; + } +} + +bool hdd_get_interface_info(struct hdd_adapter *adapter, + struct wifi_interface_info *info) +{ + struct hdd_station_ctx *sta_ctx; + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + /* pre-existing layering violation */ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + info->mode = hdd_map_device_to_ll_iface_mode(adapter->device_mode); + + qdf_copy_macaddr(&info->macAddr, &adapter->mac_addr); + + if (((QDF_STA_MODE == adapter->device_mode) || + (QDF_P2P_CLIENT_MODE == adapter->device_mode) || + (QDF_P2P_DEVICE_MODE == adapter->device_mode))) { + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (eConnectionState_NotConnected == + sta_ctx->conn_info.conn_state) { + info->state = WIFI_DISCONNECTED; + } + if (eConnectionState_Connecting == + sta_ctx->conn_info.conn_state) { + hdd_debug("Session ID %d, Connection is in progress", + adapter->vdev_id); + info->state = WIFI_ASSOCIATING; + } + if ((eConnectionState_Associated == + sta_ctx->conn_info.conn_state) && + (!sta_ctx->conn_info.is_authenticated)) { + hdd_err("client " QDF_MAC_ADDR_FMT + " is in the middle of WPS/EAPOL exchange.", + QDF_MAC_ADDR_REF(adapter->mac_addr.bytes)); + info->state = WIFI_AUTHENTICATING; + } + if (eConnectionState_Associated == + sta_ctx->conn_info.conn_state) { + info->state = WIFI_ASSOCIATED; + qdf_copy_macaddr(&info->bssid, + &sta_ctx->conn_info.bssid); + qdf_mem_copy(info->ssid, + sta_ctx->conn_info.ssid.SSID.ssId, + sta_ctx->conn_info.ssid.SSID.length); + /* + * NULL Terminate the string + */ + info->ssid[sta_ctx->conn_info.ssid.SSID.length] = 0; + } + } + + qdf_mem_copy(info->countryStr, + mac->scan.countryCodeCurrent, CFG_COUNTRY_CODE_LEN); + + qdf_mem_copy(info->apCountryStr, + mac->scan.countryCodeCurrent, CFG_COUNTRY_CODE_LEN); + + return true; +} + +/** + * hdd_link_layer_process_peer_stats() - This function is called after + * @adapter: Pointer to device adapter + * @more_data: More data + * @peer_stat: Pointer to stats data + * + * Receiving Link Layer Peer statistics from FW.This function converts + * the firmware data to the NL data and sends the same to the kernel/upper + * layers. + * + * Return: None + */ +static void hdd_link_layer_process_peer_stats(struct hdd_adapter *adapter, + u32 more_data, + struct wifi_peer_stat *peer_stat) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct wifi_peer_info *peer_info; + struct sk_buff *vendor_event; + int status, i; + struct nlattr *peers; + int num_rate; + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return; + + hdd_nofl_debug("LL_STATS_PEER_ALL : num_peers %u, more data = %u", + peer_stat->num_peers, more_data); + + /* + * Allocate a size of 4096 for the peer stats comprising + * each of size = sizeof (struct wifi_peer_info) + num_rate * + * sizeof (struct wifi_rate_stat).Each field is put with an + * NL attribute.The size of 4096 is considered assuming + * that number of rates shall not exceed beyond 50 with + * the sizeof (struct wifi_rate_stat) being 32. + */ + vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, + LL_STATS_EVENT_BUF_SIZE); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE, + QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_PEER) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA, + more_data) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS, + peer_stat->num_peers)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + + kfree_skb(vendor_event); + return; + } + + peer_info = (struct wifi_peer_info *) ((uint8_t *) + peer_stat->peer_info); + + if (peer_stat->num_peers) { + struct nlattr *peer_nest; + + peer_nest = nla_nest_start(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO); + if (!peer_nest) { + hdd_err("nla_nest_start failed"); + kfree_skb(vendor_event); + return; + } + + for (i = 1; i <= peer_stat->num_peers; i++) { + peers = nla_nest_start(vendor_event, i); + if (!peers) { + hdd_err("nla_nest_start failed"); + kfree_skb(vendor_event); + return; + } + + num_rate = peer_info->num_rate; + + if (!put_wifi_peer_info(peer_info, vendor_event)) { + hdd_err("put_wifi_peer_info fail"); + kfree_skb(vendor_event); + return; + } + + peer_info = (struct wifi_peer_info *) + ((uint8_t *)peer_stat->peer_info + + (i * sizeof(struct wifi_peer_info)) + + (num_rate * sizeof(struct wifi_rate_stat))); + nla_nest_end(vendor_event, peers); + } + nla_nest_end(vendor_event, peer_nest); + } + + cfg80211_vendor_cmd_reply(vendor_event); +} + +/** + * hdd_link_layer_process_iface_stats() - This function is called after + * @adapter: Pointer to device adapter + * @if_stat: Pointer to stats data + * @num_peers: Number of peers + * + * Receiving Link Layer Interface statistics from FW.This function converts + * the firmware data to the NL data and sends the same to the kernel/upper + * layers. + * + * Return: None + */ +static void +hdd_link_layer_process_iface_stats(struct hdd_adapter *adapter, + struct wifi_interface_stats *if_stat, + u32 num_peers) +{ + struct sk_buff *vendor_event; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + /* + * There is no need for wlan_hdd_validate_context here. This is a NB + * operation that will come with DSC synchronization. This ensures that + * no driver transition will take place as long as this operation is + * not complete. Thus the need to check validity of hdd_context is not + * required. + */ + + /* + * Allocate a size of 4096 for the interface stats comprising + * sizeof (struct wifi_interface_stats *).The size of 4096 is considered + * assuming that all these fit with in the limit.Please take + * a call on the limit based on the data requirements on + * interface statistics. + */ + vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, + LL_STATS_EVENT_BUF_SIZE); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return; + } + + hdd_debug("WMI_LINK_STATS_IFACE Data"); + + if (!hdd_get_interface_info(adapter, &if_stat->info)) { + hdd_err("hdd_get_interface_info get fail"); + kfree_skb(vendor_event); + return; + } + + if (!put_wifi_iface_stats(if_stat, num_peers, vendor_event)) { + hdd_err("put_wifi_iface_stats fail"); + kfree_skb(vendor_event); + return; + } + + cfg80211_vendor_cmd_reply(vendor_event); +} + +/** + * hdd_llstats_radio_fill_channels() - radio stats fill channels + * @adapter: Pointer to device adapter + * @radiostat: Pointer to stats data + * @vendor_event: vendor event + * + * Return: 0 on success; errno on failure + */ +static int hdd_llstats_radio_fill_channels(struct hdd_adapter *adapter, + struct wifi_radio_stats *radiostat, + struct sk_buff *vendor_event) +{ + struct wifi_channel_stats *channel_stats; + struct nlattr *chlist; + struct nlattr *chinfo; + int i; + + chlist = nla_nest_start(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO); + if (!chlist) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + for (i = 0; i < radiostat->num_channels; i++) { + channel_stats = (struct wifi_channel_stats *) ((uint8_t *) + radiostat->channels + + (i * sizeof(struct wifi_channel_stats))); + + chinfo = nla_nest_start(vendor_event, i); + if (!chinfo) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH, + channel_stats->channel.width) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ, + channel_stats->channel.center_freq) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0, + channel_stats->channel.center_freq0) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1, + channel_stats->channel.center_freq1) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME, + channel_stats->on_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME, + channel_stats->cca_busy_time)) { + hdd_err("nla_put failed"); + return -EINVAL; + } + nla_nest_end(vendor_event, chinfo); + } + nla_nest_end(vendor_event, chlist); + + return 0; +} + +/** + * hdd_llstats_post_radio_stats() - post radio stats + * @adapter: Pointer to device adapter + * @more_data: More data + * @radiostat: Pointer to stats data + * @num_radio: Number of radios + * + * Return: 0 on success; errno on failure + */ +static int hdd_llstats_post_radio_stats(struct hdd_adapter *adapter, + u32 more_data, + struct wifi_radio_stats *radiostat, + u32 num_radio) +{ + struct sk_buff *vendor_event; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + + /* + * Allocate a size of 4096 for the Radio stats comprising + * sizeof (struct wifi_radio_stats) + num_channels * sizeof + * (struct wifi_channel_stats).Each channel data is put with an + * NL attribute.The size of 4096 is considered assuming that + * number of channels shall not exceed beyond 60 with the + * sizeof (struct wifi_channel_stats) being 24 bytes. + */ + + vendor_event = cfg80211_vendor_cmd_alloc_reply_skb( + hdd_ctx->wiphy, + LL_STATS_EVENT_BUF_SIZE); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE, + QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_RADIO) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA, + more_data) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS, + num_radio) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID, + radiostat->radio) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME, + radiostat->on_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME, + radiostat->tx_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME, + radiostat->rx_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN, + radiostat->on_time_scan) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD, + radiostat->on_time_nbd) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN, + radiostat->on_time_gscan) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN, + radiostat->on_time_roam_scan) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN, + radiostat->on_time_pno_scan) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20, + radiostat->on_time_hs20) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS, + radiostat->total_num_tx_power_levels) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS, + radiostat->num_channels)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + + goto failure; + } + + if (radiostat->total_num_tx_power_levels) { + ret = + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL, + sizeof(u32) * + radiostat->total_num_tx_power_levels, + radiostat->tx_time_per_power_level); + qdf_mem_free(radiostat->tx_time_per_power_level); + radiostat->tx_time_per_power_level = NULL; + if (ret) { + qdf_mem_free(radiostat->channels); + radiostat->channels = NULL; + hdd_err("nla_put fail"); + goto failure; + } + } + + if (radiostat->num_channels) { + ret = hdd_llstats_radio_fill_channels(adapter, radiostat, + vendor_event); + qdf_mem_free(radiostat->channels); + radiostat->channels = NULL; + if (ret) + goto failure; + } + + cfg80211_vendor_cmd_reply(vendor_event); + return 0; + +failure: + kfree_skb(vendor_event); + return -EINVAL; +} + +/** + * hdd_link_layer_process_radio_stats() - This function is called after + * @adapter: Pointer to device adapter + * @more_data: More data + * @radio_stat: Pointer to stats data + * @num_radios: Number of radios + * + * Receiving Link Layer Radio statistics from FW.This function converts + * the firmware data to the NL data and sends the same to the kernel/upper + * layers. + * + * Return: None + */ +static void +hdd_link_layer_process_radio_stats(struct hdd_adapter *adapter, + u32 more_data, + struct wifi_radio_stats *radio_stat, + u32 num_radio) +{ + int i, nr, ret; + struct wifi_radio_stats *radio_stat_save = radio_stat; + + /* + * There is no need for wlan_hdd_validate_context here. This is a NB + * operation that will come with DSC synchronization. This ensures that + * no driver transition will take place as long as this operation is + * not complete. Thus the need to check validity of hdd_context is not + * required. + */ + + for (i = 0; i < num_radio; i++) { + hdd_nofl_debug("LL_STATS_RADIO" + " radio: %u on_time: %u tx_time: %u rx_time: %u" + " on_time_scan: %u on_time_nbd: %u" + " on_time_gscan: %u on_time_roam_scan: %u" + " on_time_pno_scan: %u on_time_hs20: %u" + " num_channels: %u total_num_tx_pwr_levels: %u" + " on_time_host_scan: %u, on_time_lpi_scan: %u", + radio_stat->radio, radio_stat->on_time, + radio_stat->tx_time, radio_stat->rx_time, + radio_stat->on_time_scan, radio_stat->on_time_nbd, + radio_stat->on_time_gscan, + radio_stat->on_time_roam_scan, + radio_stat->on_time_pno_scan, + radio_stat->on_time_hs20, + radio_stat->num_channels, + radio_stat->total_num_tx_power_levels, + radio_stat->on_time_host_scan, + radio_stat->on_time_lpi_scan); + radio_stat++; + } + + radio_stat = radio_stat_save; + for (nr = 0; nr < num_radio; nr++) { + ret = hdd_llstats_post_radio_stats(adapter, more_data, + radio_stat, num_radio); + if (ret) + return; + + radio_stat++; + } + + hdd_exit(); +} + +static void hdd_process_ll_stats(tSirLLStatsResults *results, + struct osif_request *request) +{ + struct hdd_ll_stats_priv *priv = osif_request_priv(request); + struct hdd_ll_stats *stats = NULL; + size_t stat_size = 0; + + if (!(priv->request_bitmap & results->paramId)) + return; + + if (results->paramId & WMI_LINK_STATS_RADIO) { + struct wifi_radio_stats *rs_results, *stat_result; + u64 channel_size = 0, pwr_lvl_size = 0; + int i; + stats = qdf_mem_malloc(sizeof(*stats)); + if (!stats) + goto exit; + + stat_size = sizeof(struct wifi_radio_stats) * + results->num_radio; + stats->result_param_id = WMI_LINK_STATS_RADIO; + stat_result = qdf_mem_malloc(stat_size); + if (!stat_result) { + qdf_mem_free(stats); + goto exit; + } + stats->result = stat_result; + rs_results = (struct wifi_radio_stats *)results->results; + qdf_mem_copy(stats->result, results->results, stat_size); + for (i = 0; i < results->num_radio; i++) { + channel_size = rs_results->num_channels * + sizeof(struct wifi_channel_stats); + pwr_lvl_size = sizeof(uint32_t) * + rs_results->total_num_tx_power_levels; + + if (rs_results->total_num_tx_power_levels && + rs_results->tx_time_per_power_level) { + stat_result->tx_time_per_power_level = + qdf_mem_malloc(pwr_lvl_size); + if (!stat_result->tx_time_per_power_level) { + while (i-- > 0) { + stat_result--; + qdf_mem_free(stat_result-> + tx_time_per_power_level); + qdf_mem_free(stat_result-> + channels); + } + qdf_mem_free(stat_result); + qdf_mem_free(stats); + goto exit; + } + qdf_mem_copy(stat_result->tx_time_per_power_level, + rs_results->tx_time_per_power_level, + pwr_lvl_size); + } + if (channel_size) { + stat_result->channels = + qdf_mem_malloc(channel_size); + if (!stat_result->channels) { + qdf_mem_free(stat_result-> + tx_time_per_power_level); + while (i-- > 0) { + stat_result--; + qdf_mem_free(stat_result-> + tx_time_per_power_level); + qdf_mem_free(stat_result-> + channels); + } + qdf_mem_free(stats->result); + qdf_mem_free(stats); + goto exit; + } + qdf_mem_copy(stat_result->channels, + rs_results->channels, + channel_size); + } + rs_results++; + stat_result++; + } + stats->stats_nradio_npeer.no_of_radios = results->num_radio; + stats->more_data = results->moreResultToFollow; + if (!results->moreResultToFollow) + priv->request_bitmap &= ~stats->result_param_id; + } else if (results->paramId & WMI_LINK_STATS_IFACE) { + stats = qdf_mem_malloc(sizeof(*stats)); + if (!stats) + goto exit; + + stats->result_param_id = WMI_LINK_STATS_IFACE; + stats->stats_nradio_npeer.no_of_peers = results->num_peers; + stats->result = qdf_mem_malloc(sizeof(struct + wifi_interface_stats)); + if (!stats->result) { + qdf_mem_free(stats); + goto exit; + } + qdf_mem_copy(stats->result, results->results, + sizeof(struct wifi_interface_stats)); + if (!results->num_peers) + priv->request_bitmap &= ~(WMI_LINK_STATS_ALL_PEER); + priv->request_bitmap &= ~stats->result_param_id; + } else if (results->paramId & WMI_LINK_STATS_ALL_PEER) { + struct wifi_peer_stat *peer_stat = (struct wifi_peer_stat *) + results->results; + struct wifi_peer_info *peer_info = NULL; + u64 num_rate = 0, peers, rates; + int i; + stats = qdf_mem_malloc(sizeof(*stats)); + if (!stats) + goto exit; + + peer_info = (struct wifi_peer_info *)peer_stat->peer_info; + for (i = 1; i <= peer_stat->num_peers; i++) { + num_rate += peer_info->num_rate; + peer_info = (struct wifi_peer_info *)((uint8_t *) + peer_info + sizeof(struct wifi_peer_info) + + (peer_info->num_rate * + sizeof(struct wifi_rate_stat))); + } + + peers = sizeof(struct wifi_peer_info) * peer_stat->num_peers; + rates = sizeof(struct wifi_rate_stat) * num_rate; + stat_size = sizeof(struct wifi_peer_stat) + peers + rates; + stats->result_param_id = WMI_LINK_STATS_ALL_PEER; + + stats->result = qdf_mem_malloc(stat_size); + if (!stats->result) { + qdf_mem_free(stats); + goto exit; + } + + qdf_mem_copy(stats->result, results->results, stat_size); + stats->more_data = results->moreResultToFollow; + if (!results->moreResultToFollow) + priv->request_bitmap &= ~stats->result_param_id; + } else { + hdd_err("INVALID LL_STATS_NOTIFY RESPONSE"); + } + /* send indication to caller thread */ + if (stats) + qdf_list_insert_back(&priv->ll_stats_q, &stats->ll_stats_node); + + if (!priv->request_bitmap) +exit: + osif_request_complete(request); +} + +static void hdd_debugfs_process_ll_stats(struct hdd_adapter *adapter, + tSirLLStatsResults *results, + struct osif_request *request) +{ + struct hdd_ll_stats_priv *priv = osif_request_priv(request); + + if (results->paramId & WMI_LINK_STATS_RADIO) { + hdd_debugfs_process_radio_stats(adapter, + results->moreResultToFollow, + results->results, + results->num_radio); + if (!results->moreResultToFollow) + priv->request_bitmap &= ~(WMI_LINK_STATS_RADIO); + } else if (results->paramId & WMI_LINK_STATS_IFACE) { + hdd_debugfs_process_iface_stats(adapter, results->results, + results->num_peers); + + /* Firmware doesn't send peerstats event if no peers are + * connected. HDD should not wait for any peerstats in + * this case and return the status to middleware after + * receiving iface stats + */ + + if (!results->num_peers) + priv->request_bitmap &= ~(WMI_LINK_STATS_ALL_PEER); + + priv->request_bitmap &= ~(WMI_LINK_STATS_IFACE); + } else if (results->paramId & WMI_LINK_STATS_ALL_PEER) { + hdd_debugfs_process_peer_stats(adapter, results->results); + if (!results->moreResultToFollow) + priv->request_bitmap &= ~(WMI_LINK_STATS_ALL_PEER); + } else { + hdd_err("INVALID LL_STATS_NOTIFY RESPONSE"); + } + + if (!priv->request_bitmap) + osif_request_complete(request); +} + +void wlan_hdd_cfg80211_link_layer_stats_callback(hdd_handle_t hdd_handle, + int indication_type, + tSirLLStatsResults *results, + void *cookie) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + struct hdd_ll_stats_priv *priv; + struct hdd_adapter *adapter = NULL; + int status; + struct osif_request *request; + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, + results->ifaceId); + + if (!adapter) { + hdd_err("vdev_id %d does not exist with host", + results->ifaceId); + return; + } + + switch (indication_type) { + case SIR_HAL_LL_STATS_RESULTS_RSP: + { + hdd_nofl_debug("LL_STATS RESP paramID = 0x%x, ifaceId = %u, respId= %u , moreResultToFollow = %u, num radio = %u result = %pK", + results->paramId, results->ifaceId, + results->rspId, results->moreResultToFollow, + results->num_radio, results->results); + + request = osif_request_get(cookie); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + + /* validate response received from target */ + if (priv->request_id != results->rspId) { + hdd_err("Request id %d response id %d request bitmap 0x%x response bitmap 0x%x", + priv->request_id, results->rspId, + priv->request_bitmap, results->paramId); + osif_request_put(request); + return; + } + + if (results->rspId == DEBUGFS_LLSTATS_REQID) { + hdd_debugfs_process_ll_stats(adapter, results, request); + } else { + qdf_spin_lock(&priv->ll_stats_lock); + if (priv->request_bitmap) + hdd_process_ll_stats(results, request); + qdf_spin_unlock(&priv->ll_stats_lock); + } + + osif_request_put(request); + break; + } + default: + hdd_warn("invalid event type %d", indication_type); + break; + } +} + +void hdd_lost_link_info_cb(hdd_handle_t hdd_handle, + struct sir_lost_link_info *lost_link_info) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + int status; + struct hdd_adapter *adapter; + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return; + + if (!lost_link_info) { + hdd_err("lost_link_info is NULL"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, lost_link_info->vdev_id); + if (!adapter) { + hdd_err("invalid adapter"); + return; + } + + adapter->rssi_on_disconnect = lost_link_info->rssi; + hdd_debug("rssi on disconnect %d", adapter->rssi_on_disconnect); +} + +const struct +nla_policy + qca_wlan_vendor_ll_set_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING] = { + .type = NLA_U32}, +}; + +/** + * __wlan_hdd_cfg80211_ll_stats_set() - set link layer stats + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: int + */ +static int +__wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int status; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX + 1]; + tSirLLStatsSetReq req; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return -EINVAL; + + if (hdd_validate_adapter(adapter)) + return -EINVAL; + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_debug("Cannot set LL_STATS for device mode %d", + adapter->device_mode); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb_vendor, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX, + (struct nlattr *)data, data_len, + qca_wlan_vendor_ll_set_policy)) { + hdd_err("maximum attribute not present"); + return -EINVAL; + } + + if (!tb_vendor + [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD]) { + hdd_err("MPDU size Not present"); + return -EINVAL; + } + + if (!tb_vendor + [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING]) { + hdd_err("Stats Gathering Not Present"); + return -EINVAL; + } + + /* Shall take the request Id if the Upper layers pass. 1 For now. */ + req.reqId = 1; + + req.mpduSizeThreshold = + nla_get_u32(tb_vendor + [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD]); + + req.aggressiveStatisticsGathering = + nla_get_u32(tb_vendor + [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING]); + + req.staId = adapter->vdev_id; + + hdd_debug("LL_STATS_SET reqId = %d, staId = %d, mpduSizeThreshold = %d, Statistics Gathering = %d", + req.reqId, req.staId, + req.mpduSizeThreshold, + req.aggressiveStatisticsGathering); + + if (QDF_STATUS_SUCCESS != sme_ll_stats_set_req(hdd_ctx->mac_handle, + &req)) { + hdd_err("sme_ll_stats_set_req Failed"); + return -EINVAL; + } + + adapter->is_link_layer_stats_set = true; + hdd_exit(); + return 0; +} + +/** + * wlan_hdd_cfg80211_ll_stats_set() - set ll stats + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 if success, non-zero for failure + */ +int wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_ll_stats_set(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +const struct +nla_policy + qca_wlan_vendor_ll_get_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX + 1] = { + /* Unsigned 32bit value provided by the caller issuing the GET stats + * command. When reporting + * the stats results, the driver uses the same value to indicate + * which GET request the results + * correspond to. + */ + [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID] = {.type = NLA_U32}, + + /* Unsigned 32bit value . bit mask to identify what statistics are + * requested for retrieval + */ + [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK] = {.type = NLA_U32} +}; + +static void wlan_hdd_handle_ll_stats(struct hdd_adapter *adapter, + struct hdd_ll_stats *stats, + int ret) +{ + switch (stats->result_param_id) { + case WMI_LINK_STATS_RADIO: + { + struct wifi_radio_stats *radio_stat = stats->result; + int i, num_radio = stats->stats_nradio_npeer.no_of_radios; + + if (ret == -ETIMEDOUT) { + for (i = 0; i < num_radio; i++) { + if (radio_stat->num_channels) + qdf_mem_free(radio_stat->channels); + if (radio_stat->total_num_tx_power_levels) + qdf_mem_free(radio_stat-> + tx_time_per_power_level); + radio_stat++; + } + return; + } + hdd_link_layer_process_radio_stats(adapter, stats->more_data, + radio_stat, num_radio); + } + break; + case WMI_LINK_STATS_IFACE: + hdd_link_layer_process_iface_stats(adapter, stats->result, + stats->stats_nradio_npeer. + no_of_peers); + break; + case WMI_LINK_STATS_ALL_PEER: + hdd_link_layer_process_peer_stats(adapter, + stats->more_data, + stats->result); + break; + default: + hdd_err("not requested event"); + } +} + +static void wlan_hdd_dealloc_ll_stats(void *priv) +{ + struct hdd_ll_stats_priv *ll_stats_priv = priv; + struct hdd_ll_stats *stats = NULL; + QDF_STATUS status; + qdf_list_node_t *ll_node; + + if (!ll_stats_priv) + return; + + qdf_spin_lock(&ll_stats_priv->ll_stats_lock); + status = qdf_list_remove_front(&ll_stats_priv->ll_stats_q, &ll_node); + qdf_spin_unlock(&ll_stats_priv->ll_stats_lock); + while (QDF_IS_STATUS_SUCCESS(status)) { + stats = qdf_container_of(ll_node, struct hdd_ll_stats, + ll_stats_node); + + if (stats->result_param_id == WMI_LINK_STATS_RADIO) { + struct wifi_radio_stats *radio_stat = stats->result; + int i; + int num_radio = stats->stats_nradio_npeer.no_of_radios; + + for (i = 0; i < num_radio; i++) { + if (radio_stat->num_channels) + qdf_mem_free(radio_stat->channels); + if (radio_stat->total_num_tx_power_levels) + qdf_mem_free(radio_stat-> + tx_time_per_power_level); + radio_stat++; + } + } + + qdf_mem_free(stats->result); + qdf_mem_free(stats); + qdf_spin_lock(&ll_stats_priv->ll_stats_lock); + status = qdf_list_remove_front(&ll_stats_priv->ll_stats_q, + &ll_node); + qdf_spin_unlock(&ll_stats_priv->ll_stats_lock); + } + qdf_list_destroy(&ll_stats_priv->ll_stats_q); +} + +static int wlan_hdd_send_ll_stats_req(struct hdd_adapter *adapter, + tSirLLStatsGetReq *req) +{ + int ret = 0; + struct hdd_ll_stats_priv *priv; + struct hdd_ll_stats *stats = NULL; + struct osif_request *request; + qdf_list_node_t *ll_node; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + void *cookie; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_LL_STATS, + .dealloc = wlan_hdd_dealloc_ll_stats, + }; + + hdd_enter(); + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request Allocation Failure"); + return -ENOMEM; + } + + cookie = osif_request_cookie(request); + + priv = osif_request_priv(request); + + priv->request_id = req->reqId; + priv->request_bitmap = req->paramIdMask; + qdf_spinlock_create(&priv->ll_stats_lock); + qdf_list_create(&priv->ll_stats_q, HDD_LINK_STATS_MAX); + + if (QDF_STATUS_SUCCESS != + sme_ll_stats_get_req(hdd_ctx->mac_handle, req, + cookie)) { + hdd_err("sme_ll_stats_get_req Failed"); + ret = -EINVAL; + goto exit; + } + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("Target response timed out request id %d request bitmap 0x%x", + priv->request_id, priv->request_bitmap); + qdf_spin_lock(&priv->ll_stats_lock); + priv->request_bitmap = 0; + qdf_spin_unlock(&priv->ll_stats_lock); + ret = -ETIMEDOUT; + } + qdf_spin_lock(&priv->ll_stats_lock); + status = qdf_list_remove_front(&priv->ll_stats_q, &ll_node); + qdf_spin_unlock(&priv->ll_stats_lock); + while (QDF_IS_STATUS_SUCCESS(status)) { + stats = qdf_container_of(ll_node, struct hdd_ll_stats, + ll_stats_node); + wlan_hdd_handle_ll_stats(adapter, stats, ret); + qdf_mem_free(stats->result); + qdf_mem_free(stats); + qdf_spin_lock(&priv->ll_stats_lock); + status = qdf_list_remove_front(&priv->ll_stats_q, &ll_node); + qdf_spin_unlock(&priv->ll_stats_lock); + } + qdf_list_destroy(&priv->ll_stats_q); +exit: + hdd_exit(); + osif_request_put(request); + + return ret; +} + +int wlan_hdd_ll_stats_get(struct hdd_adapter *adapter, uint32_t req_id, + uint32_t req_mask) +{ + int errno; + tSirLLStatsGetReq get_req; + struct hdd_station_ctx *hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_warn("Command not allowed in FTM mode"); + return -EPERM; + } + + if (hddstactx->hdd_reassoc_scenario) { + hdd_err("Roaming in progress, cannot process the request"); + return -EBUSY; + } + + if (!adapter->is_link_layer_stats_set) { + hdd_info("LL_STATs not set"); + return -EINVAL; + } + + get_req.reqId = req_id; + get_req.paramIdMask = req_mask; + get_req.staId = adapter->vdev_id; + + rtnl_lock(); + errno = wlan_hdd_send_ll_stats_req(adapter, &get_req); + rtnl_unlock(); + if (errno) + hdd_err("Send LL stats req failed, id:%u, mask:%d, session:%d", + req_id, req_mask, adapter->vdev_id); + + hdd_exit(); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_ll_stats_get() - get link layer stats + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: int + */ +static int +__wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX + 1]; + tSirLLStatsGetReq LinkLayerStatsGetReq; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_station_ctx *hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + /* ENTER() intentionally not used in a frequently invoked API */ + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return -EINVAL; + + if (!adapter->is_link_layer_stats_set) { + hdd_nofl_debug("is_link_layer_stats_set: %d", + adapter->is_link_layer_stats_set); + return -EINVAL; + } + + if (hddstactx->hdd_reassoc_scenario) { + hdd_err("Roaming in progress, cannot process the request"); + return -EBUSY; + } + + if (wlan_cfg80211_nla_parse(tb_vendor, + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX, + (struct nlattr *)data, data_len, + qca_wlan_vendor_ll_get_policy)) { + hdd_err("max attribute not present"); + return -EINVAL; + } + + if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID]) { + hdd_err("Request Id Not present"); + return -EINVAL; + } + + if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK]) { + hdd_err("Req Mask Not present"); + return -EINVAL; + } + + LinkLayerStatsGetReq.reqId = + nla_get_u32(tb_vendor + [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID]); + LinkLayerStatsGetReq.paramIdMask = + nla_get_u32(tb_vendor + [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK]); + + LinkLayerStatsGetReq.staId = adapter->vdev_id; + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + ret = wlan_hdd_send_ll_stats_req(adapter, &LinkLayerStatsGetReq); + if (0 != ret) { + hdd_err("Failed to send LL stats request (id:%u)", + LinkLayerStatsGetReq.reqId); + return ret; + } + + hdd_exit(); + return 0; +} + +#ifdef WLAN_FEATURE_WMI_SEND_RECV_QMI +/** + * wlan_hdd_qmi_get_sync_resume() - Get operation to trigger RTPM + * sync resume without WoW exit + * @hdd_ctx: hdd context + * @dev: device context + * + * Returns: 0 for success, non-zero for failure + */ +static inline +int wlan_hdd_qmi_get_sync_resume(struct hdd_context *hdd_ctx, + struct device *dev) +{ + if (!hdd_ctx->config->is_qmi_stats_enabled) { + hdd_debug("periodic stats over qmi is disabled"); + return 0; + } + + return pld_qmi_send_get(dev); +} + +/** + * wlan_hdd_qmi_put_suspend() - Put operation to trigger RTPM suspend + * without WoW entry + * @hdd_ctx: hdd context + * @dev: device context + * + * Returns: 0 for success, non-zero for failure + */ +static inline +int wlan_hdd_qmi_put_suspend(struct hdd_context *hdd_ctx, + struct device *dev) +{ + if (!hdd_ctx->config->is_qmi_stats_enabled) { + hdd_debug("periodic stats over qmi is disabled"); + return 0; + } + + return pld_qmi_send_put(dev); +} +#else +static inline +int wlan_hdd_qmi_get_sync_resume(struct hdd_context *hdd_ctx, + struct device *dev) +{ + return 0; +} + +static inline int wlan_hdd_qmi_put_suspend(struct hdd_context *hdd_ctx, + struct device *dev) +{ + return 0; +} +#endif /* end if of WLAN_FEATURE_WMI_SEND_RECV_QMI */ + +/** + * wlan_hdd_cfg80211_ll_stats_get() - get ll stats + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 if success, non-zero for failure + */ +int wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct osif_vdev_sync *vdev_sync; + int errno; + qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + errno = wlan_hdd_validate_context(hdd_ctx); + if (0 != errno) + return -EINVAL; + + if (!qdf_ctx) + return -EINVAL; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = wlan_hdd_qmi_get_sync_resume(hdd_ctx, qdf_ctx->dev); + if (errno) + goto end; + + errno = __wlan_hdd_cfg80211_ll_stats_get(wiphy, wdev, data, data_len); + + wlan_hdd_qmi_put_suspend(hdd_ctx, qdf_ctx->dev); + +end: + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +const struct +nla_policy + qca_wlan_vendor_ll_clr_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP] = {.type = NLA_U8}, +}; + +/** + * __wlan_hdd_cfg80211_ll_stats_clear() - clear link layer stats + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: int + */ +static int +__wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX + 1]; + tSirLLStatsClearReq LinkLayerStatsClearReq; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + u32 statsClearReqMask; + u8 stopReq; + int errno; + QDF_STATUS status; + struct sk_buff *skb; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return -EINVAL; + + if (!adapter->is_link_layer_stats_set) { + hdd_warn("is_link_layer_stats_set : %d", + adapter->is_link_layer_stats_set); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb_vendor, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX, + (struct nlattr *)data, data_len, + qca_wlan_vendor_ll_clr_policy)) { + hdd_err("STATS_CLR_MAX is not present"); + return -EINVAL; + } + + if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] || + !tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ]) { + hdd_err("Error in LL_STATS CLR CONFIG PARA"); + return -EINVAL; + } + + statsClearReqMask = LinkLayerStatsClearReq.statsClearReqMask = + nla_get_u32(tb_vendor + [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK]); + + stopReq = LinkLayerStatsClearReq.stopReq = + nla_get_u8(tb_vendor + [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ]); + + /* + * Shall take the request Id if the Upper layers pass. 1 For now. + */ + LinkLayerStatsClearReq.reqId = 1; + + LinkLayerStatsClearReq.staId = adapter->vdev_id; + + hdd_debug("LL_STATS_CLEAR reqId = %d, staId = %d, statsClearReqMask = 0x%X, stopReq = %d", + LinkLayerStatsClearReq.reqId, + LinkLayerStatsClearReq.staId, + LinkLayerStatsClearReq.statsClearReqMask, + LinkLayerStatsClearReq.stopReq); + + status = sme_ll_stats_clear_req(hdd_ctx->mac_handle, + &LinkLayerStatsClearReq); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("stats clear request failed, %d", status); + return -EINVAL; + } + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, + 2 * sizeof(u32) + + 2 * NLMSG_HDRLEN); + if (!skb) { + hdd_err("skb allocation failed"); + return -ENOMEM; + } + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK, + statsClearReqMask) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP, + stopReq)) { + hdd_err("LL_STATS_CLR put fail"); + kfree_skb(skb); + return -EINVAL; + } + + /* If the ask is to stop the stats collection + * as part of clear (stopReq = 1), ensure + * that no further requests of get go to the + * firmware by having is_link_layer_stats_set set + * to 0. However it the stopReq as part of + * the clear request is 0, the request to get + * the statistics are honoured as in this case + * the firmware is just asked to clear the + * statistics. + */ + if (stopReq == 1) + adapter->is_link_layer_stats_set = false; + + hdd_exit(); + + return cfg80211_vendor_cmd_reply(skb); +} + +/** + * wlan_hdd_cfg80211_ll_stats_clear() - clear ll stats + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 if success, non-zero for failure + */ +int wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_ll_stats_clear(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_clear_link_layer_stats() - clear link layer stats + * @adapter: pointer to adapter + * + * Wrapper function to clear link layer stats. + * return - void + */ +void wlan_hdd_clear_link_layer_stats(struct hdd_adapter *adapter) +{ + tSirLLStatsClearReq link_layer_stats_clear_req; + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + + link_layer_stats_clear_req.statsClearReqMask = WIFI_STATS_IFACE_AC | + WIFI_STATS_IFACE_ALL_PEER; + link_layer_stats_clear_req.stopReq = 0; + link_layer_stats_clear_req.reqId = 1; + link_layer_stats_clear_req.staId = adapter->vdev_id; + sme_ll_stats_clear_req(mac_handle, &link_layer_stats_clear_req); +} + +/** + * hdd_populate_per_peer_ps_info() - populate per peer sta's PS info + * @wifi_peer_info: peer information + * @vendor_event: buffer for vendor event + * + * Return: 0 success + */ +static inline int +hdd_populate_per_peer_ps_info(struct wifi_peer_info *wifi_peer_info, + struct sk_buff *vendor_event) +{ + if (!wifi_peer_info) { + hdd_err("Invalid pointer to peer info."); + return -EINVAL; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE, + wifi_peer_info->power_saving) || + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS, + QDF_MAC_ADDR_SIZE, &wifi_peer_info->peer_macaddr)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail."); + return -EINVAL; + } + return 0; +} + +/** + * hdd_populate_wifi_peer_ps_info() - populate peer sta's power state + * @data: stats for peer STA + * @vendor_event: buffer for vendor event + * + * Return: 0 success + */ +static int hdd_populate_wifi_peer_ps_info(struct wifi_peer_stat *data, + struct sk_buff *vendor_event) +{ + uint32_t peer_num, i; + struct wifi_peer_info *wifi_peer_info; + struct nlattr *peer_info, *peers; + + if (!data) { + hdd_err("Invalid pointer to Wifi peer stat."); + return -EINVAL; + } + + peer_num = data->num_peers; + if (peer_num == 0) { + hdd_err("Peer number is zero."); + return -EINVAL; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM, + peer_num)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return -EINVAL; + } + + peer_info = nla_nest_start(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG); + if (!peer_info) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + for (i = 0; i < peer_num; i++) { + wifi_peer_info = &data->peer_info[i]; + peers = nla_nest_start(vendor_event, i); + + if (!peers) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + if (hdd_populate_per_peer_ps_info(wifi_peer_info, vendor_event)) + return -EINVAL; + + nla_nest_end(vendor_event, peers); + } + nla_nest_end(vendor_event, peer_info); + + return 0; +} + +/** + * hdd_populate_tx_failure_info() - populate TX failure info + * @tx_fail: TX failure info + * @skb: buffer for vendor event + * + * Return: 0 Success + */ +static inline int +hdd_populate_tx_failure_info(struct sir_wifi_iface_tx_fail *tx_fail, + struct sk_buff *skb) +{ + int status = 0; + + if (!tx_fail || !skb) + return -EINVAL; + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID, + tx_fail->tid) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU, + tx_fail->msdu_num) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS, + tx_fail->status)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + status = -EINVAL; + } + + return status; +} + +/** + * hdd_populate_wifi_channel_cca_info() - put channel cca info to vendor event + * @info: cca info array for all channels + * @vendor_event: vendor event buffer + * + * Return: 0 Success, EINVAL failure + */ +static int +hdd_populate_wifi_channel_cca_info(struct sir_wifi_chan_cca_stats *cca, + struct sk_buff *vendor_event) +{ + /* There might be no CCA info for a channel */ + if (!cca) + return 0; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME, + cca->idle_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME, + cca->tx_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME, + cca->rx_in_bss_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME, + cca->rx_out_bss_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY, + cca->rx_busy_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD, + cca->rx_in_bad_cond_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD, + cca->tx_in_bad_cond_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL, + cca->wlan_not_avail_time) || + nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID, + cca->vdev_id)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return -EINVAL; + } + return 0; +} + +/** + * hdd_populate_wifi_signal_info - put chain signal info + * @info: RF chain signal info + * @skb: vendor event buffer + * + * Return: 0 Success, EINVAL failure + */ +static int +hdd_populate_wifi_signal_info(struct sir_wifi_peer_signal_stats *peer_signal, + struct sk_buff *skb) +{ + uint32_t i, chain_count; + struct nlattr *chains, *att; + + /* There might be no signal info for a peer */ + if (!peer_signal) + return 0; + + chain_count = peer_signal->num_chain < WIFI_MAX_CHAINS ? + peer_signal->num_chain : WIFI_MAX_CHAINS; + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM, + chain_count)) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return -EINVAL; + } + + att = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL); + if (!att) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + for (i = 0; i < chain_count; i++) { + chains = nla_nest_start(skb, i); + + if (!chains) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + hdd_debug("SNR=%d, NF=%d, Rx=%d, Tx=%d", + peer_signal->per_ant_snr[i], + peer_signal->nf[i], + peer_signal->per_ant_rx_mpdus[i], + peer_signal->per_ant_tx_mpdus[i]); + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR, + peer_signal->per_ant_snr[i]) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF, + peer_signal->nf[i]) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU, + peer_signal->per_ant_rx_mpdus[i]) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU, + peer_signal->per_ant_tx_mpdus[i])) { + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return -EINVAL; + } + nla_nest_end(skb, chains); + } + nla_nest_end(skb, att); + + return 0; +} + +/** + * hdd_populate_wifi_wmm_ac_tx_info() - put AC TX info + * @info: tx info + * @skb: vendor event buffer + * + * Return: 0 Success, EINVAL failure + */ +static int +hdd_populate_wifi_wmm_ac_tx_info(struct sir_wifi_tx *tx_stats, + struct sk_buff *skb) +{ + uint32_t *agg_size, *succ_mcs, *fail_mcs, *delay; + + /* There might be no TX info for a peer */ + if (!tx_stats) + return 0; + + agg_size = tx_stats->mpdu_aggr_size; + succ_mcs = tx_stats->success_mcs; + fail_mcs = tx_stats->fail_mcs; + delay = tx_stats->delay; + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU, + tx_stats->msdus) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU, + tx_stats->mpdus) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU, + tx_stats->ppdus) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES, + tx_stats->bytes) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP, + tx_stats->drops) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES, + tx_stats->drop_bytes) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY, + tx_stats->retries) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK, + tx_stats->failed) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM, + tx_stats->aggr_len) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM, + tx_stats->success_mcs_len) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM, + tx_stats->fail_mcs_len) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE, + tx_stats->delay_len)) + goto put_attr_fail; + + if (agg_size) { + if (nla_put(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR, + tx_stats->aggr_len, agg_size)) + goto put_attr_fail; + } + + if (succ_mcs) { + if (nla_put(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS, + tx_stats->success_mcs_len, succ_mcs)) + goto put_attr_fail; + } + + if (fail_mcs) { + if (nla_put(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS, + tx_stats->fail_mcs_len, fail_mcs)) + goto put_attr_fail; + } + + if (delay) { + if (nla_put(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY, + tx_stats->delay_len, delay)) + goto put_attr_fail; + } + return 0; + +put_attr_fail: + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return -EINVAL; +} + +/** + * hdd_populate_wifi_wmm_ac_rx_info() - put AC RX info + * @info: rx info + * @skb: vendor event buffer + * + * Return: 0 Success, EINVAL failure + */ +static int +hdd_populate_wifi_wmm_ac_rx_info(struct sir_wifi_rx *rx_stats, + struct sk_buff *skb) +{ + uint32_t *mcs, *aggr; + + /* There might be no RX info for a peer */ + if (!rx_stats) + return 0; + + aggr = rx_stats->mpdu_aggr; + mcs = rx_stats->mcs; + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU, + rx_stats->mpdus) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES, + rx_stats->bytes) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU, + rx_stats->ppdus) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES, + rx_stats->ppdu_bytes) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST, + rx_stats->mpdu_lost) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY, + rx_stats->mpdu_retry) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP, + rx_stats->mpdu_dup) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD, + rx_stats->mpdu_discard) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM, + rx_stats->aggr_len) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM, + rx_stats->mcs_len)) + goto put_attr_fail; + + if (aggr) { + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR, + rx_stats->aggr_len, aggr)) + goto put_attr_fail; + } + + if (mcs) { + if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS, + rx_stats->mcs_len, mcs)) + goto put_attr_fail; + } + + return 0; + +put_attr_fail: + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return -EINVAL; +} + +/** + * hdd_populate_wifi_wmm_ac_info() - put WMM AC info + * @info: per AC stats + * @skb: vendor event buffer + * + * Return: 0 Success, EINVAL failure + */ +static int +hdd_populate_wifi_wmm_ac_info(struct sir_wifi_ll_ext_wmm_ac_stats *ac_stats, + struct sk_buff *skb) +{ + struct nlattr *wmm; + + wmm = nla_nest_start(skb, ac_stats->type); + if (!wmm) + goto nest_start_fail; + + if (hdd_populate_wifi_wmm_ac_tx_info(ac_stats->tx_stats, skb) || + hdd_populate_wifi_wmm_ac_rx_info(ac_stats->rx_stats, skb)) + goto put_attr_fail; + + nla_nest_end(skb, wmm); + return 0; + +nest_start_fail: + hdd_err("nla_nest_start failed"); + return -EINVAL; + +put_attr_fail: + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return -EINVAL; +} + +/** + * hdd_populate_wifi_ll_ext_peer_info() - put per peer info + * @info: peer stats + * @skb: vendor event buffer + * + * Return: 0 Success, EINVAL failure + */ +static int +hdd_populate_wifi_ll_ext_peer_info(struct sir_wifi_ll_ext_peer_stats *peers, + struct sk_buff *skb) +{ + uint32_t i; + struct nlattr *wmm_ac; + + if (nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID, + peers->peer_id) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID, + peers->vdev_id) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES, + peers->sta_ps_inds) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION, + peers->sta_ps_durs) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ, + peers->rx_probe_reqs) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT, + peers->rx_oth_mgmts) || + nla_put(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS, + QDF_MAC_ADDR_SIZE, peers->mac_address) || + hdd_populate_wifi_signal_info(&peers->peer_signal_stats, skb)) { + hdd_err("put peer signal attr failed"); + return -EINVAL; + } + + wmm_ac = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS); + if (!wmm_ac) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + for (i = 0; i < WLAN_MAX_AC; i++) { + if (hdd_populate_wifi_wmm_ac_info(&peers->ac_stats[i], skb)) { + hdd_err("put WMM AC attr failed"); + return -EINVAL; + } + } + + nla_nest_end(skb, wmm_ac); + return 0; +} + +/** + * hdd_populate_wifi_ll_ext_stats() - put link layer extension stats + * @info: link layer stats + * @skb: vendor event buffer + * + * Return: 0 Success, EINVAL failure + */ +static int +hdd_populate_wifi_ll_ext_stats(struct sir_wifi_ll_ext_stats *stats, + struct sk_buff *skb) +{ + uint32_t i; + struct nlattr *peer, *peer_info, *channels, *channel_info; + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE, + stats->trigger_cond_id) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP, + stats->cca_chgd_bitmap) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP, + stats->sig_chgd_bitmap) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP, + stats->tx_chgd_bitmap) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP, + stats->rx_chgd_bitmap) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM, + stats->channel_num) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM, + stats->peer_num)) { + goto put_attr_fail; + } + + channels = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS); + if (!channels) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + for (i = 0; i < stats->channel_num; i++) { + channel_info = nla_nest_start(skb, i); + if (!channel_info) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + if (hdd_populate_wifi_channel_cca_info(&stats->cca[i], skb)) + goto put_attr_fail; + nla_nest_end(skb, channel_info); + } + nla_nest_end(skb, channels); + + peer_info = nla_nest_start(skb, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER); + if (!peer_info) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + for (i = 0; i < stats->peer_num; i++) { + peer = nla_nest_start(skb, i); + if (!peer) { + hdd_err("nla_nest_start failed"); + return -EINVAL; + } + + if (hdd_populate_wifi_ll_ext_peer_info(&stats->peer_stats[i], + skb)) + goto put_attr_fail; + nla_nest_end(skb, peer); + } + + nla_nest_end(skb, peer_info); + return 0; + +put_attr_fail: + hdd_err("QCA_WLAN_VENDOR_ATTR put fail"); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_link_layer_stats_ext_callback() - Callback for LL ext + * @ctx: HDD context + * @rsp: msg from FW + * + * This function is an extension of + * wlan_hdd_cfg80211_link_layer_stats_callback. It converts + * monitoring parameters offloaded to NL data and send the same to the + * kernel/upper layers. + * + * Return: None + */ +void wlan_hdd_cfg80211_link_layer_stats_ext_callback(hdd_handle_t ctx, + tSirLLStatsResults *rsp) +{ + struct hdd_context *hdd_ctx; + struct sk_buff *skb; + uint32_t param_id, index; + struct hdd_adapter *adapter; + struct wifi_peer_stat *peer_stats; + uint8_t *results; + int status; + + hdd_enter(); + + if (!rsp) { + hdd_err("Invalid result."); + return; + } + + hdd_ctx = hdd_handle_to_context(ctx); + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, rsp->ifaceId); + + if (!adapter) { + hdd_err("vdev_id %d does not exist with host.", + rsp->ifaceId); + return; + } + + index = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT_INDEX; + skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, LL_STATS_EVENT_BUF_SIZE + NLMSG_HDRLEN, + index, GFP_KERNEL); + if (!skb) { + hdd_err("cfg80211_vendor_event_alloc failed."); + return; + } + + results = rsp->results; + param_id = rsp->paramId; + hdd_info("LL_STATS RESP paramID = 0x%x, ifaceId = %u, result = %pK", + rsp->paramId, rsp->ifaceId, rsp->results); + if (param_id & WMI_LL_STATS_EXT_PS_CHG) { + peer_stats = (struct wifi_peer_stat *)results; + status = hdd_populate_wifi_peer_ps_info(peer_stats, skb); + } else if (param_id & WMI_LL_STATS_EXT_TX_FAIL) { + struct sir_wifi_iface_tx_fail *tx_fail; + + tx_fail = (struct sir_wifi_iface_tx_fail *)results; + status = hdd_populate_tx_failure_info(tx_fail, skb); + } else if (param_id & WMI_LL_STATS_EXT_MAC_COUNTER) { + hdd_info("MAC counters stats"); + status = hdd_populate_wifi_ll_ext_stats( + (struct sir_wifi_ll_ext_stats *) + rsp->results, skb); + } else { + hdd_info("Unknown link layer stats"); + status = -EINVAL; + } + + if (status == 0) + cfg80211_vendor_event(skb, GFP_KERNEL); + else + kfree_skb(skb); + hdd_exit(); +} + +static const struct nla_policy +qca_wlan_vendor_ll_ext_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR] = { + .type = NLA_U32 + }, + [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF] = { + .type = NLA_U32 + }, +}; + +/** + * __wlan_hdd_cfg80211_ll_stats_ext_set_param - config monitor parameters + * @wiphy: wiphy handle + * @wdev: wdev handle + * @data: user layer input + * @data_len: length of user layer input + * + * this function is called in ssr protected environment. + * + * return: 0 success, none zero for failure + */ +static int __wlan_hdd_cfg80211_ll_stats_ext_set_param(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + QDF_STATUS status; + int errno; + uint32_t period; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct sir_ll_ext_stats_threshold thresh = {0,}; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX + 1]; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_warn("command not allowed in ftm mode"); + return -EPERM; + } + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return -EPERM; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX, + (struct nlattr *)data, data_len, + qca_wlan_vendor_ll_ext_policy)) { + hdd_err("maximum attribute not present"); + return -EPERM; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD]) { + period = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD]); + + if (period != 0 && period < LL_STATS_MIN_PERIOD) + period = LL_STATS_MIN_PERIOD; + + /* + * Only enable/disbale counters. + * Keep the last threshold settings. + */ + goto set_period; + } + + /* global thresh is not enabled */ + if (!tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD]) { + thresh.global = false; + hdd_warn("global thresh is not set"); + } else { + thresh.global_threshold = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD]); + thresh.global = true; + hdd_debug("globle thresh is %d", thresh.global_threshold); + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL]) { + thresh.global = false; + hdd_warn("global thresh is not enabled"); + } else { + thresh.global = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL]); + hdd_debug("global is %d", thresh.global); + } + + thresh.enable_bitmap = false; + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP]) { + thresh.tx_bitmap = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP]); + thresh.enable_bitmap = true; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP]) { + thresh.rx_bitmap = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP]); + thresh.enable_bitmap = true; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP]) { + thresh.cca_bitmap = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP]); + thresh.enable_bitmap = true; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP]) { + thresh.signal_bitmap = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP]); + thresh.enable_bitmap = true; + } + + if (!thresh.global && !thresh.enable_bitmap) { + hdd_warn("threshold will be disabled."); + thresh.enable = false; + + /* Just disable threshold */ + goto set_thresh; + } else { + thresh.enable = true; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU]) { + thresh.tx.msdu = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU]) { + thresh.tx.mpdu = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU]) { + thresh.tx.ppdu = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES]) { + thresh.tx.bytes = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP]) { + thresh.tx.msdu_drop = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES]) { + thresh.tx.byte_drop = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY]) { + thresh.tx.mpdu_retry = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK]) { + thresh.tx.mpdu_fail = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK]) { + thresh.tx.ppdu_fail = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR]) { + thresh.tx.aggregation = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS]) { + thresh.tx.succ_mcs = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS]) { + thresh.tx.fail_mcs = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY]) { + thresh.tx.delay = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU]) { + thresh.rx.mpdu = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES]) { + thresh.rx.bytes = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU]) { + thresh.rx.ppdu = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES]) { + thresh.rx.ppdu_bytes = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST]) { + thresh.rx.mpdu_lost = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY]) { + thresh.rx.mpdu_retry = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP]) { + thresh.rx.mpdu_dup = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD]) { + thresh.rx.mpdu_discard = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR]) { + thresh.rx.aggregation = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS]) { + thresh.rx.mcs = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES]) { + thresh.rx.ps_inds = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION]) { + thresh.rx.ps_durs = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ]) { + thresh.rx.probe_reqs = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT]) { + thresh.rx.other_mgmt = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME]) { + thresh.cca.idle_time = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME]) { + thresh.cca.tx_time = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME]) { + thresh.cca.rx_in_bss_time = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME]) { + thresh.cca.rx_out_bss_time = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY]) { + thresh.cca.rx_busy_time = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD]) { + thresh.cca.rx_in_bad_cond_time = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD]) { + thresh.cca.tx_in_bad_cond_time = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL]) { + thresh.cca.wlan_not_avail_time = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR]) { + thresh.signal.snr = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF]) { + thresh.signal.nf = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF]); + } + +set_thresh: + hdd_info("send thresh settings to target"); + status = sme_ll_stats_set_thresh(hdd_ctx->mac_handle, &thresh); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("sme_ll_stats_set_thresh failed."); + return -EINVAL; + } + return 0; + +set_period: + hdd_info("send period to target"); + errno = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_STATS_OBSERVATION_PERIOD, + period, PDEV_CMD); + if (errno) { + hdd_err("wma_cli_set_command set_period failed."); + return -EINVAL; + } + return 0; +} + +/** + * wlan_hdd_cfg80211_ll_stats_ext_set_param - config monitor parameters + * @wiphy: wiphy handle + * @wdev: wdev handle + * @data: user layer input + * @data_len: length of user layer input + * + * return: 0 success, einval failure + */ +int wlan_hdd_cfg80211_ll_stats_ext_set_param(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_ll_stats_ext_set_param(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +#ifdef WLAN_FEATURE_STATS_EXT +/** + * __wlan_hdd_cfg80211_stats_ext_request() - ext stats request + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: int + */ +static int __wlan_hdd_cfg80211_stats_ext_request(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + tStatsExtRequestReq stats_ext_req; + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int ret_val; + QDF_STATUS status; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + + hdd_enter_dev(dev); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + stats_ext_req.request_data_len = data_len; + stats_ext_req.request_data = (void *)data; + + status = cdp_request_rx_hw_stats(soc, adapter->vdev_id); + + if (QDF_STATUS_SUCCESS != status) { + hdd_err_rl("Failed to get hw stats: %u", status); + ret_val = -EINVAL; + } + + status = sme_stats_ext_request(adapter->vdev_id, &stats_ext_req); + + if (QDF_STATUS_SUCCESS != status) { + hdd_err_rl("Failed to get fw stats: %u", status); + ret_val = -EINVAL; + } + + return ret_val; +} + +/** + * wlan_hdd_cfg80211_stats_ext_request() - ext stats request + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: int + */ +int wlan_hdd_cfg80211_stats_ext_request(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_stats_ext_request(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +void wlan_hdd_cfg80211_stats_ext_callback(hdd_handle_t hdd_handle, + struct stats_ext_event *data) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + struct sk_buff *vendor_event; + int status; + int ret_val; + struct hdd_adapter *adapter; + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, data->vdev_id); + if (!adapter) { + hdd_err("vdev_id %d does not exist with host", data->vdev_id); + return; + } + + vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, + data->event_data_len + + sizeof(uint32_t) + + NLMSG_HDRLEN + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + ret_val = nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_IFINDEX, + adapter->dev->ifindex); + if (ret_val) { + hdd_err("QCA_WLAN_VENDOR_ATTR_IFINDEX put fail"); + kfree_skb(vendor_event); + + return; + } + + ret_val = nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_STATS_EXT, + data->event_data_len, data->event_data); + + if (ret_val) { + hdd_err("QCA_WLAN_VENDOR_ATTR_STATS_EXT put fail"); + kfree_skb(vendor_event); + + return; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + +} + +void +wlan_hdd_cfg80211_stats_ext2_callback(hdd_handle_t hdd_handle, + struct sir_sme_rx_aggr_hole_ind *pmsg) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + int status; + uint32_t data_size, hole_info_size; + struct sk_buff *vendor_event; + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return; + + if (!pmsg) { + hdd_err("msg received here is null"); + return; + } + + hole_info_size = (pmsg->hole_cnt)*sizeof(pmsg->hole_info_array[0]); + data_size = sizeof(struct sir_sme_rx_aggr_hole_ind) + hole_info_size; + + vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, + data_size + NLMSG_HDRLEN + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("vendor_event_alloc failed for STATS_EXT2"); + return; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM, + pmsg->hole_cnt)) { + hdd_err("%s put fail", + "QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM"); + kfree_skb(vendor_event); + return; + } + if (nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO, + hole_info_size, + (void *)(pmsg->hole_info_array))) { + hdd_err("%s put fail", + "QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO"); + kfree_skb(vendor_event); + return; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); +} + +#endif /* End of WLAN_FEATURE_STATS_EXT */ + +#ifdef LINKSPEED_DEBUG_ENABLED +#define linkspeed_dbg(format, args...) pr_info(format, ## args) +#else +#define linkspeed_dbg(format, args...) +#endif /* LINKSPEED_DEBUG_ENABLED */ + +/** + * wlan_hdd_fill_summary_stats() - populate station_info summary stats + * @stats: summary stats to use as a source + * @info: kernel station_info struct to use as a destination + * @vdev_id: stats get from which vdev id + * + * Return: None + */ +static void wlan_hdd_fill_summary_stats(tCsrSummaryStatsInfo *stats, + struct station_info *info, + uint8_t vdev_id) +{ + int i; + struct cds_vdev_dp_stats dp_stats; + uint32_t orig_cnt; + + info->rx_packets = stats->rx_frm_cnt; + info->tx_packets = 0; + info->tx_retries = 0; + info->tx_failed = 0; + + for (i = 0; i < WIFI_MAX_AC; ++i) { + info->tx_packets += stats->tx_frm_cnt[i]; + info->tx_retries += stats->multiple_retry_cnt[i]; + info->tx_failed += stats->fail_cnt[i]; + } + + if (cds_dp_get_vdev_stats(vdev_id, &dp_stats)) { + orig_cnt = info->tx_retries; + info->tx_retries = dp_stats.tx_retries; + hdd_debug("vdev %d tx retries adjust from %d to %d", + vdev_id, orig_cnt, info->tx_retries); + } + + info->filled |= HDD_INFO_TX_PACKETS | + HDD_INFO_TX_RETRIES | + HDD_INFO_TX_FAILED | + HDD_INFO_RX_PACKETS; +} + +/** + * wlan_hdd_get_sap_stats() - get aggregate SAP stats + * @adapter: sap adapter to get stats for + * @info: kernel station_info struct to populate + * + * Fetch the vdev-level aggregate stats for the given SAP adapter. This is to + * support "station dump" and "station get" for SAP vdevs, even though they + * aren't technically stations. + * + * Return: errno + */ +static int +wlan_hdd_get_sap_stats(struct hdd_adapter *adapter, struct station_info *info) +{ + int ret; + + ret = wlan_hdd_get_station_stats(adapter); + if (ret) { + hdd_err("Failed to get SAP stats; status:%d", ret); + return ret; + } + + wlan_hdd_fill_summary_stats(&adapter->hdd_stats.summary_stat, + info, + adapter->vdev_id); + + return 0; +} + +/** + * hdd_get_max_rate_legacy() - get max rate for legacy mode + * @stainfo: stainfo pointer + * @rssidx: rssi index + * + * This function will get max rate for legacy mode + * + * Return: max rate on success, otherwise 0 + */ +static uint32_t hdd_get_max_rate_legacy(struct hdd_station_info *stainfo, + uint8_t rssidx) +{ + uint32_t maxrate = 0; + /*Minimum max rate, 6Mbps*/ + int maxidx = 12; + int i; + + /* check supported rates */ + if (stainfo->max_supp_idx != 0xff && + maxidx < stainfo->max_supp_idx) + maxidx = stainfo->max_supp_idx; + + /* check extended rates */ + if (stainfo->max_ext_idx != 0xff && + maxidx < stainfo->max_ext_idx) + maxidx = stainfo->max_ext_idx; + + for (i = 0; i < QDF_ARRAY_SIZE(supported_data_rate); i++) { + if (supported_data_rate[i].beacon_rate_index == maxidx) + maxrate = + supported_data_rate[i].supported_rate[rssidx]; + } + + hdd_debug("maxrate %d", maxrate); + + return maxrate; +} + +/** + * hdd_get_max_rate_ht() - get max rate for ht mode + * @stainfo: stainfo pointer + * @stats: fw txrx status pointer + * @rate_flags: rate flags + * @nss: number of streams + * @maxrate: returned max rate buffer pointer + * @max_mcs_idx: max mcs idx + * @report_max: report max rate or actual rate + * + * This function will get max rate for ht mode + * + * Return: None + */ +static void hdd_get_max_rate_ht(struct hdd_station_info *stainfo, + struct hdd_fw_txrx_stats *stats, + uint32_t rate_flags, + uint8_t nss, + uint32_t *maxrate, + uint8_t *max_mcs_idx, + bool report_max) +{ + struct index_data_rate_type *supported_mcs_rate; + uint32_t tmprate; + uint8_t flag = 0, mcsidx; + int8_t rssi = stats->rssi; + int mode; + int i; + + if (rate_flags & TX_RATE_HT40) + mode = 1; + else + mode = 0; + + if (rate_flags & TX_RATE_HT40) + flag |= 1; + if (rate_flags & TX_RATE_SGI) + flag |= 2; + + supported_mcs_rate = (struct index_data_rate_type *) + ((nss == 1) ? &supported_mcs_rate_nss1 : + &supported_mcs_rate_nss2); + + if (stainfo->max_mcs_idx == 0xff) { + hdd_err("invalid max_mcs_idx"); + /* report real mcs idx */ + mcsidx = stats->tx_rate.mcs; + } else { + mcsidx = stainfo->max_mcs_idx; + } + + if (!report_max) { + for (i = 0; i < mcsidx; i++) { + if (rssi <= rssi_mcs_tbl[mode][i]) { + mcsidx = i; + break; + } + } + if (mcsidx < stats->tx_rate.mcs) + mcsidx = stats->tx_rate.mcs; + } + + tmprate = supported_mcs_rate[mcsidx].supported_rate[flag]; + + hdd_debug("tmprate %d mcsidx %d", tmprate, mcsidx); + + *maxrate = tmprate; + *max_mcs_idx = mcsidx; +} + +/** + * hdd_get_max_rate_vht() - get max rate for vht mode + * @stainfo: stainfo pointer + * @stats: fw txrx status pointer + * @rate_flags: rate flags + * @nss: number of streams + * @maxrate: returned max rate buffer pointer + * @max_mcs_idx: max mcs idx + * @report_max: report max rate or actual rate + * + * This function will get max rate for vht mode + * + * Return: None + */ +static void hdd_get_max_rate_vht(struct hdd_station_info *stainfo, + struct hdd_fw_txrx_stats *stats, + uint32_t rate_flags, + uint8_t nss, + uint32_t *maxrate, + uint8_t *max_mcs_idx, + bool report_max) +{ + struct index_vht_data_rate_type *supported_vht_mcs_rate; + uint32_t tmprate = 0; + uint32_t vht_max_mcs; + uint8_t flag = 0, mcsidx = INVALID_MCS_IDX; + int8_t rssi = stats->rssi; + int mode; + int i; + + supported_vht_mcs_rate = (struct index_vht_data_rate_type *) + ((nss == 1) ? + &supported_vht_mcs_rate_nss1 : + &supported_vht_mcs_rate_nss2); + + if (rate_flags & TX_RATE_VHT80) + mode = 2; + else if (rate_flags & TX_RATE_VHT40) + mode = 1; + else + mode = 0; + + if (rate_flags & + (TX_RATE_VHT20 | TX_RATE_VHT40 | TX_RATE_VHT80)) { + vht_max_mcs = + (enum data_rate_11ac_max_mcs) + (stainfo->tx_mcs_map & DATA_RATE_11AC_MCS_MASK); + if (rate_flags & TX_RATE_SGI) + flag |= 1; + + if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_7) { + mcsidx = 7; + } else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_8) { + mcsidx = 8; + } else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_9) { + /* + * 'IEEE_P802.11ac_2013.pdf' page 325, 326 + * - MCS9 is valid for VHT20 when Nss = 3 or Nss = 6 + * - MCS9 is not valid for VHT20 when Nss = 1,2,4,5,7,8 + */ + if ((rate_flags & TX_RATE_VHT20) && + (nss != 3 && nss != 6)) + mcsidx = 8; + else + mcsidx = 9; + } else { + hdd_err("invalid vht_max_mcs"); + /* report real mcs idx */ + mcsidx = stats->tx_rate.mcs; + } + + if (!report_max) { + for (i = 0; i <= mcsidx; i++) { + if (rssi <= rssi_mcs_tbl[mode][i]) { + mcsidx = i; + break; + } + } + if (mcsidx < stats->tx_rate.mcs) + mcsidx = stats->tx_rate.mcs; + } + + if (rate_flags & TX_RATE_VHT80) + tmprate = + supported_vht_mcs_rate[mcsidx].supported_VHT80_rate[flag]; + else if (rate_flags & TX_RATE_VHT40) + tmprate = + supported_vht_mcs_rate[mcsidx].supported_VHT40_rate[flag]; + else if (rate_flags & TX_RATE_VHT20) + tmprate = + supported_vht_mcs_rate[mcsidx].supported_VHT20_rate[flag]; + } + + hdd_debug("tmprate %d mcsidx %d", tmprate, mcsidx); + + *maxrate = tmprate; + *max_mcs_idx = mcsidx; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) +/** + * hdd_fill_bw_mcs() - fill ch width and mcs flags + * @stainfo: stainfo pointer + * @rate_flags: HDD rate flags + * @mcsidx: mcs index + * @nss: number of streams + * @vht: vht mode or not + * + * This function will fill ch width and mcs flags + * + * Return: None + */ +static void hdd_fill_bw_mcs(struct station_info *sinfo, + enum tx_rate_info rate_flags, + uint8_t mcsidx, + uint8_t nss, + bool vht) +{ + if (vht) { + sinfo->txrate.nss = nss; + sinfo->txrate.mcs = mcsidx; + sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS; + if (rate_flags & TX_RATE_VHT80) + sinfo->txrate.bw = RATE_INFO_BW_80; + else if (rate_flags & TX_RATE_VHT40) + sinfo->txrate.bw = RATE_INFO_BW_40; + else if (rate_flags & TX_RATE_VHT20) + sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS; + } else { + sinfo->txrate.mcs = (nss - 1) << 3; + sinfo->txrate.mcs |= mcsidx; + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + if (rate_flags & TX_RATE_HT40) + sinfo->txrate.bw = RATE_INFO_BW_40; + } +} +#else +/** + * hdd_fill_bw_mcs() - fill ch width and mcs flags + * @stainfo: stainfo pointer + * @rate_flags: HDD rate flags + * @mcsidx: mcs index + * @nss: number of streams + * @vht: vht mode or not + * + * This function will fill ch width and mcs flags + * + * Return: None + */ +static void hdd_fill_bw_mcs(struct station_info *sinfo, + enum tx_rate_info rate_flags, + uint8_t mcsidx, + uint8_t nss, + bool vht) +{ + if (vht) { + sinfo->txrate.nss = nss; + sinfo->txrate.mcs = mcsidx; + sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS; + if (rate_flags & TX_RATE_VHT80) + sinfo->txrate.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; + else if (rate_flags & TX_RATE_VHT40) + sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + else if (rate_flags & TX_RATE_VHT20) + sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS; + } else { + sinfo->txrate.mcs = (nss - 1) << 3; + sinfo->txrate.mcs |= mcsidx; + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + if (rate_flags & TX_RATE_HT40) + sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + } +} +#endif + +/** + * hdd_fill_bw_mcs_vht() - fill ch width and mcs flags for VHT mode + * @stainfo: stainfo pointer + * @rate_flags: HDD rate flags + * @mcsidx: mcs index + * @nss: number of streams + * + * This function will fill ch width and mcs flags for VHT mode + * + * Return: None + */ +static void hdd_fill_bw_mcs_vht(struct station_info *sinfo, + enum tx_rate_info rate_flags, + uint8_t mcsidx, + uint8_t nss) +{ + hdd_fill_bw_mcs(sinfo, rate_flags, mcsidx, nss, true); +} + +/** + * hdd_fill_sinfo_rate_info() - fill rate info of sinfo struct + * @sinfo: station_info struct pointer + * @rate_flags: HDD rate flags + * @mcsidx: mcs index + * @nss: number of streams + * @maxrate: data rate (kbps) + * + * This function will fill rate info of sinfo struct + * + * Return: None + */ +static void hdd_fill_sinfo_rate_info(struct station_info *sinfo, + uint32_t rate_flags, + uint8_t mcsidx, + uint8_t nss, + uint32_t maxrate) +{ + if (rate_flags & TX_RATE_LEGACY) { + /* provide to the UI in units of 100kbps */ + sinfo->txrate.legacy = maxrate; + } else { + /* must be MCS */ + if (rate_flags & + (TX_RATE_VHT80 | + TX_RATE_VHT40 | + TX_RATE_VHT20)) + hdd_fill_bw_mcs_vht(sinfo, rate_flags, mcsidx, nss); + + if (rate_flags & (TX_RATE_HT20 | TX_RATE_HT40)) + hdd_fill_bw_mcs(sinfo, rate_flags, mcsidx, nss, false); + + if (rate_flags & TX_RATE_SGI) { + if (!(sinfo->txrate.flags & RATE_INFO_FLAGS_VHT_MCS)) + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } + } + + hdd_info("flag %x mcs %d legacy %d nss %d", + sinfo->txrate.flags, + sinfo->txrate.mcs, + sinfo->txrate.legacy, + sinfo->txrate.nss); +} + +/** + * hdd_fill_station_info_flags() - fill flags of sinfo struct + * @sinfo: station_info struct pointer + * + * This function will fill flags of sinfo struct + * + * Return: None + */ +static void hdd_fill_station_info_flags(struct station_info *sinfo) +{ + sinfo->filled |= HDD_INFO_SIGNAL | + HDD_INFO_TX_BYTES | + HDD_INFO_TX_BYTES64 | + HDD_INFO_TX_BITRATE | + HDD_INFO_TX_PACKETS | + HDD_INFO_TX_RETRIES | + HDD_INFO_TX_FAILED | + HDD_INFO_RX_BYTES | + HDD_INFO_RX_BYTES64 | + HDD_INFO_RX_PACKETS | + HDD_INFO_INACTIVE_TIME | + HDD_INFO_CONNECTED_TIME; +} + +/** + * hdd_fill_rate_info() - fill rate info of sinfo + * @psoc: psoc context + * @sinfo: station_info struct pointer + * @stainfo: stainfo pointer + * @stats: fw txrx status pointer + * + * This function will fill rate info of sinfo + * + * Return: None + */ +static void hdd_fill_rate_info(struct wlan_objmgr_psoc *psoc, + struct station_info *sinfo, + struct hdd_station_info *stainfo, + struct hdd_fw_txrx_stats *stats) +{ + enum tx_rate_info rate_flags; + uint8_t mcsidx = 0xff; + uint32_t myrate, maxrate, tmprate; + int rssidx; + int nss = 1; + int link_speed_rssi_high = 0; + int link_speed_rssi_mid = 0; + int link_speed_rssi_low = 0; + uint32_t link_speed_rssi_report = 0; + + ucfg_mlme_stats_get_cfg_values(psoc, + &link_speed_rssi_high, + &link_speed_rssi_mid, + &link_speed_rssi_low, + &link_speed_rssi_report); + + hdd_info("reportMaxLinkSpeed %d", link_speed_rssi_report); + /* convert to 100kbps expected in rate table */ + myrate = stats->tx_rate.rate / 100; + rate_flags = stainfo->rate_flags; + if (!(rate_flags & TX_RATE_LEGACY)) { + nss = stainfo->nss; + if (ucfg_mlme_stats_is_link_speed_report_actual(psoc)) { + /* Get current rate flags if report actual */ + if (stats->tx_rate.rate_flags) + rate_flags = + stats->tx_rate.rate_flags; + nss = stats->tx_rate.nss; + } + + if (stats->tx_rate.mcs == INVALID_MCS_IDX) + rate_flags = TX_RATE_LEGACY; + } + + if (!ucfg_mlme_stats_is_link_speed_report_actual(psoc)) { + /* we do not want to necessarily report the current speed */ + if (ucfg_mlme_stats_is_link_speed_report_max(psoc)) { + /* report the max possible speed */ + rssidx = 0; + } else if (ucfg_mlme_stats_is_link_speed_report_max_scaled( + psoc)) { + /* report the max possible speed with RSSI scaling */ + if (stats->rssi >= link_speed_rssi_high) { + /* report the max possible speed */ + rssidx = 0; + } else if (stats->rssi >= link_speed_rssi_mid) { + /* report middle speed */ + rssidx = 1; + } else if (stats->rssi >= link_speed_rssi_low) { + /* report middle speed */ + rssidx = 2; + } else { + /* report actual speed */ + rssidx = 3; + } + } else { + /* unknown, treat as eHDD_LINK_SPEED_REPORT_MAX */ + hdd_err("Invalid value for reportMaxLinkSpeed: %u", + link_speed_rssi_report); + rssidx = 0; + } + + maxrate = hdd_get_max_rate_legacy(stainfo, rssidx); + + /* + * Get MCS Rate Set -- + * Only if we are connected in non legacy mode and not + * reporting actual speed + */ + if ((rssidx != 3) && + !(rate_flags & TX_RATE_LEGACY)) { + hdd_get_max_rate_vht(stainfo, + stats, + rate_flags, + nss, + &tmprate, + &mcsidx, + rssidx == 0); + + if (maxrate < tmprate && + mcsidx != INVALID_MCS_IDX) + maxrate = tmprate; + + if (mcsidx == INVALID_MCS_IDX) + hdd_get_max_rate_ht(stainfo, + stats, + rate_flags, + nss, + &tmprate, + &mcsidx, + rssidx == 0); + + if (maxrate < tmprate && + mcsidx != INVALID_MCS_IDX) + maxrate = tmprate; + } else if (!(rate_flags & TX_RATE_LEGACY)) { + maxrate = myrate; + mcsidx = stats->tx_rate.mcs; + } + + /* + * make sure we report a value at least as big as our + * current rate + */ + if ((maxrate < myrate) || (maxrate == 0)) { + maxrate = myrate; + if (!(rate_flags & TX_RATE_LEGACY)) { + mcsidx = stats->tx_rate.mcs; + /* + * 'IEEE_P802.11ac_2013.pdf' page 325, 326 + * - MCS9 is valid for VHT20 when Nss = 3 or + * Nss = 6 + * - MCS9 is not valid for VHT20 when + * Nss = 1,2,4,5,7,8 + */ + if ((rate_flags & TX_RATE_VHT20) && + (mcsidx > 8) && + (nss != 3 && nss != 6)) + mcsidx = 8; + } + } + } else { + /* report current rate instead of max rate */ + maxrate = myrate; + if (!(rate_flags & TX_RATE_LEGACY)) + mcsidx = stats->tx_rate.mcs; + } + + hdd_fill_sinfo_rate_info(sinfo, + rate_flags, + mcsidx, + nss, + maxrate); +} + +/** + * wlan_hdd_fill_station_info() - fill station_info struct + * @psoc: psoc context + * @sinfo: station_info struct pointer + * @stainfo: stainfo pointer + * @stats: fw txrx status pointer + * + * This function will fill station_info struct + * + * Return: None + */ +static void wlan_hdd_fill_station_info(struct wlan_objmgr_psoc *psoc, + struct station_info *sinfo, + struct hdd_station_info *stainfo, + struct hdd_fw_txrx_stats *stats) +{ + qdf_time_t curr_time, dur; + + curr_time = qdf_system_ticks(); + dur = curr_time - stainfo->assoc_ts; + sinfo->connected_time = qdf_system_ticks_to_msecs(dur) / 1000; + dur = curr_time - stainfo->last_tx_rx_ts; + sinfo->inactive_time = qdf_system_ticks_to_msecs(dur); + sinfo->signal = stats->rssi; + sinfo->tx_bytes = stats->tx_bytes; + sinfo->tx_packets = stats->tx_packets; + sinfo->rx_bytes = stats->rx_bytes; + sinfo->rx_packets = stats->rx_packets; + sinfo->tx_failed = stats->tx_failed; + sinfo->tx_retries = stats->tx_retries; + + /* tx rate info */ + hdd_fill_rate_info(psoc, sinfo, stainfo, stats); + + hdd_fill_station_info_flags(sinfo); + + /* dump sta info*/ + hdd_info("dump stainfo"); + hdd_info("con_time %d inact_time %d tx_pkts %d rx_pkts %d", + sinfo->connected_time, sinfo->inactive_time, + sinfo->tx_packets, sinfo->rx_packets); + hdd_info("failed %d retries %d tx_bytes %lld rx_bytes %lld", + sinfo->tx_failed, sinfo->tx_retries, + sinfo->tx_bytes, sinfo->rx_bytes); + hdd_info("rssi %d mcs %d legacy %d nss %d flags %x", + sinfo->signal, sinfo->txrate.mcs, + sinfo->txrate.legacy, sinfo->txrate.nss, + sinfo->txrate.flags); +} + +/** + * hdd_get_rate_flags_ht() - get HT rate flags based on rate, nss and mcs + * @rate: Data rate (100 kbps) + * @nss: Number of streams + * @mcs: HT mcs index + * + * This function is used to construct HT rate flag with rate, nss and mcs + * + * Return: rate flags for success, 0 on failure. + */ +static uint8_t hdd_get_rate_flags_ht(uint32_t rate, + uint8_t nss, + uint8_t mcs) +{ + struct index_data_rate_type *mcs_rate; + uint8_t flags = 0; + + mcs_rate = (struct index_data_rate_type *) + ((nss == 1) ? &supported_mcs_rate_nss1 : + &supported_mcs_rate_nss2); + + if (rate == mcs_rate[mcs].supported_rate[0]) { + flags |= TX_RATE_HT20; + } else if (rate == mcs_rate[mcs].supported_rate[1]) { + flags |= TX_RATE_HT40; + } else if (rate == mcs_rate[mcs].supported_rate[2]) { + flags |= TX_RATE_HT20; + flags |= TX_RATE_SGI; + } else if (rate == mcs_rate[mcs].supported_rate[3]) { + flags |= TX_RATE_HT40; + flags |= TX_RATE_SGI; + } else { + hdd_err("invalid params rate %d nss %d mcs %d", + rate, nss, mcs); + } + + return flags; +} + +/** + * hdd_get_rate_flags_vht() - get VHT rate flags based on rate, nss and mcs + * @rate: Data rate (100 kbps) + * @nss: Number of streams + * @mcs: VHT mcs index + * + * This function is used to construct VHT rate flag with rate, nss and mcs + * + * Return: rate flags for success, 0 on failure. + */ +static uint8_t hdd_get_rate_flags_vht(uint32_t rate, + uint8_t nss, + uint8_t mcs) +{ + struct index_vht_data_rate_type *mcs_rate; + uint8_t flags = 0; + + mcs_rate = (struct index_vht_data_rate_type *) + ((nss == 1) ? + &supported_vht_mcs_rate_nss1 : + &supported_vht_mcs_rate_nss2); + + if (rate == mcs_rate[mcs].supported_VHT80_rate[0]) { + flags |= TX_RATE_VHT80; + } else if (rate == mcs_rate[mcs].supported_VHT80_rate[1]) { + flags |= TX_RATE_VHT80; + flags |= TX_RATE_SGI; + } else if (rate == mcs_rate[mcs].supported_VHT40_rate[0]) { + flags |= TX_RATE_VHT40; + } else if (rate == mcs_rate[mcs].supported_VHT40_rate[1]) { + flags |= TX_RATE_VHT40; + flags |= TX_RATE_SGI; + } else if (rate == mcs_rate[mcs].supported_VHT20_rate[0]) { + flags |= TX_RATE_VHT20; + } else if (rate == mcs_rate[mcs].supported_VHT20_rate[1]) { + flags |= TX_RATE_VHT20; + flags |= TX_RATE_SGI; + } else { + hdd_err("invalid params rate %d nss %d mcs %d", + rate, nss, mcs); + } + + return flags; +} + +/** + * hdd_get_rate_flags() - get HT/VHT rate flags based on rate, nss and mcs + * @rate: Data rate (100 kbps) + * @mode: Tx/Rx mode + * @nss: Number of streams + * @mcs: Mcs index + * + * This function is used to construct rate flag with rate, nss and mcs + * + * Return: rate flags for success, 0 on failure. + */ +static uint8_t hdd_get_rate_flags(uint32_t rate, + uint8_t mode, + uint8_t nss, + uint8_t mcs) +{ + uint8_t flags = 0; + + if (mode == SIR_SME_PHY_MODE_HT) + flags = hdd_get_rate_flags_ht(rate, nss, mcs); + else if (mode == SIR_SME_PHY_MODE_VHT) + flags = hdd_get_rate_flags_vht(rate, nss, mcs); + else + hdd_err("invalid mode param %d", mode); + + return flags; +} + +/** + * wlan_hdd_fill_rate_info() - fill HDD rate info from SIR peer info + * @txrx_stats: pointer to txrx stats to be filled with rate info + * @peer_info: SIR peer info pointer + * + * This function is used to fill HDD rate info rom SIR peer info + * + * Return: None + */ +static void wlan_hdd_fill_rate_info(struct hdd_fw_txrx_stats *txrx_stats, + struct sir_peer_info_ext *peer_info) +{ + uint8_t flags; + uint32_t rate_code; + + /* tx rate info */ + txrx_stats->tx_rate.rate = peer_info->tx_rate; + rate_code = peer_info->tx_rate_code; + + if ((WMI_GET_HW_RATECODE_PREAM_V1(rate_code)) == + WMI_RATE_PREAMBLE_HT) + txrx_stats->tx_rate.mode = SIR_SME_PHY_MODE_HT; + else if ((WMI_GET_HW_RATECODE_PREAM_V1(rate_code)) == + WMI_RATE_PREAMBLE_VHT) + txrx_stats->tx_rate.mode = SIR_SME_PHY_MODE_VHT; + else + txrx_stats->tx_rate.mode = SIR_SME_PHY_MODE_LEGACY; + + txrx_stats->tx_rate.nss = WMI_GET_HW_RATECODE_NSS_V1(rate_code) + 1; + txrx_stats->tx_rate.mcs = WMI_GET_HW_RATECODE_RATE_V1(rate_code); + + flags = hdd_get_rate_flags(txrx_stats->tx_rate.rate / 100, + txrx_stats->tx_rate.mode, + txrx_stats->tx_rate.nss, + txrx_stats->tx_rate.mcs); + + txrx_stats->tx_rate.rate_flags = flags; + + hdd_debug("tx: mode %d nss %d mcs %d rate_flags %x flags %x", + txrx_stats->tx_rate.mode, + txrx_stats->tx_rate.nss, + txrx_stats->tx_rate.mcs, + txrx_stats->tx_rate.rate_flags, + flags); + + /* rx rate info */ + txrx_stats->rx_rate.rate = peer_info->rx_rate; + rate_code = peer_info->rx_rate_code; + + if ((WMI_GET_HW_RATECODE_PREAM_V1(rate_code)) == + WMI_RATE_PREAMBLE_HT) + txrx_stats->rx_rate.mode = SIR_SME_PHY_MODE_HT; + else if ((WMI_GET_HW_RATECODE_PREAM_V1(rate_code)) == + WMI_RATE_PREAMBLE_VHT) + txrx_stats->rx_rate.mode = SIR_SME_PHY_MODE_VHT; + else + txrx_stats->rx_rate.mode = SIR_SME_PHY_MODE_LEGACY; + + txrx_stats->rx_rate.nss = WMI_GET_HW_RATECODE_NSS_V1(rate_code) + 1; + txrx_stats->rx_rate.mcs = WMI_GET_HW_RATECODE_RATE_V1(rate_code); + + flags = hdd_get_rate_flags(txrx_stats->rx_rate.rate / 100, + txrx_stats->rx_rate.mode, + txrx_stats->rx_rate.nss, + txrx_stats->rx_rate.mcs); + + txrx_stats->rx_rate.rate_flags = flags; + + hdd_info("rx: mode %d nss %d mcs %d rate_flags %x flags %x", + txrx_stats->rx_rate.mode, + txrx_stats->rx_rate.nss, + txrx_stats->rx_rate.mcs, + txrx_stats->rx_rate.rate_flags, + flags); +} + +/** + * wlan_hdd_get_station_remote() - NL80211_CMD_GET_STATION handler for SoftAP + * @wiphy: pointer to wiphy + * @dev: pointer to net_device structure + * @mac: request peer mac address + * @sinfo: pointer to station_info struct + * + * This function will get remote peer info from fw and fill sinfo struct + * + * Return: 0 on success, otherwise error value + */ +static int wlan_hdd_get_station_remote(struct wiphy *wiphy, + struct net_device *dev, + const u8 *mac, + struct station_info *sinfo) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hddctx = wiphy_priv(wiphy); + struct hdd_station_info *stainfo = NULL; + struct qdf_mac_addr macaddr; + struct sir_peer_info_ext peer_info; + struct hdd_fw_txrx_stats txrx_stats; + int status, i; + + status = wlan_hdd_validate_context(hddctx); + if (status != 0) + return status; + + hdd_debug("Peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(mac)); + + stainfo = hdd_get_sta_info_by_mac(&adapter->sta_info_list, mac, + STA_INFO_WLAN_HDD_GET_STATION_REMOTE); + if (!stainfo) { + hdd_err("peer "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(mac)); + return -EINVAL; + } + + qdf_mem_copy(macaddr.bytes, mac, QDF_MAC_ADDR_SIZE); + status = wlan_hdd_get_peer_info(adapter, macaddr, &peer_info); + if (status) { + hdd_put_sta_info_ref(&adapter->sta_info_list, &stainfo, true, + STA_INFO_WLAN_HDD_GET_STATION_REMOTE); + hdd_err("fail to get peer info from fw"); + return -EPERM; + } + + qdf_mem_zero(&txrx_stats, sizeof(txrx_stats)); + txrx_stats.tx_packets = peer_info.tx_packets; + txrx_stats.tx_bytes = peer_info.tx_bytes; + txrx_stats.rx_packets = peer_info.rx_packets; + txrx_stats.rx_bytes = peer_info.rx_bytes; + txrx_stats.tx_retries = peer_info.tx_retries; + txrx_stats.tx_failed = peer_info.tx_failed; + txrx_stats.tx_succeed = peer_info.tx_succeed; + txrx_stats.rssi = peer_info.rssi + WLAN_HDD_TGT_NOISE_FLOOR_DBM; + for (i = 0; i < WMI_MAX_CHAINS; i++) + txrx_stats.peer_rssi_per_chain[i] = + peer_info.peer_rssi_per_chain[i] + + WLAN_HDD_TGT_NOISE_FLOOR_DBM; + wlan_hdd_fill_rate_info(&txrx_stats, &peer_info); + wlan_hdd_fill_station_info(hddctx->psoc, sinfo, stainfo, &txrx_stats); + hdd_put_sta_info_ref(&adapter->sta_info_list, &stainfo, true, + STA_INFO_WLAN_HDD_GET_STATION_REMOTE); + + return status; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) && \ + defined(WLAN_FEATURE_11AX) +/** + * hdd_map_he_gi_to_os() - map txrate_gi to os guard interval + * @guard_interval: guard interval get from fw rate + * + * Return: os guard interval value + */ +static inline uint8_t hdd_map_he_gi_to_os(enum txrate_gi guard_interval) +{ + switch (guard_interval) { + case TXRATE_GI_0_8_US: + return NL80211_RATE_INFO_HE_GI_0_8; + case TXRATE_GI_1_6_US: + return NL80211_RATE_INFO_HE_GI_1_6; + case TXRATE_GI_3_2_US: + return NL80211_RATE_INFO_HE_GI_3_2; + default: + return NL80211_RATE_INFO_HE_GI_0_8; + } +} + +/** + * wlan_hdd_fill_os_he_rateflags() - Fill HE related rate_info + * @os_rate: rate info for os + * @rate_flags: rate flags + * @dcm: dcm from rate + * @guard_interval: guard interval from rate + * + * Return: none + */ +static void wlan_hdd_fill_os_he_rateflags(struct rate_info *os_rate, + enum tx_rate_info rate_flags, + uint8_t dcm, + enum txrate_gi guard_interval) +{ + /* as fw not yet report ofdma to host, so we doesn't + * fill RATE_INFO_BW_HE_RU. + */ + if (rate_flags & (TX_RATE_HE80 | TX_RATE_HE40 | + TX_RATE_HE20 | TX_RATE_HE160)) { + if (rate_flags & TX_RATE_HE160) + hdd_set_rate_bw(os_rate, HDD_RATE_BW_160); + else if (rate_flags & TX_RATE_HE80) + hdd_set_rate_bw(os_rate, HDD_RATE_BW_80); + else if (rate_flags & TX_RATE_HE40) + hdd_set_rate_bw(os_rate, HDD_RATE_BW_40); + + os_rate->flags |= RATE_INFO_FLAGS_HE_MCS; + + os_rate->he_gi = hdd_map_he_gi_to_os(guard_interval); + os_rate->he_dcm = dcm; + } +} +#else +static void wlan_hdd_fill_os_he_rateflags(struct rate_info *os_rate, + enum tx_rate_info rate_flags, + uint8_t dcm, + enum txrate_gi guard_interval) +{} +#endif + +/** + * wlan_hdd_fill_os_rate_info() - Fill os related rate_info + * @rate_flags: rate flags + * @legacy_rate: 802.11abg rate + * @os_rate: rate info for os + * @mcs_index: mcs + * @dcm: dcm from rate + * @guard_interval: guard interval from rate + * + * Return: none + */ +static void wlan_hdd_fill_os_rate_info(enum tx_rate_info rate_flags, + uint16_t legacy_rate, + struct rate_info *os_rate, + uint8_t mcs_index, uint8_t nss, + uint8_t dcm, + enum txrate_gi guard_interval) +{ + if (rate_flags & TX_RATE_LEGACY) { + os_rate->legacy = legacy_rate; + hdd_debug("Reporting legacy rate %d", os_rate->legacy); + return; + } + + /* assume basic BW. anything else will override this later */ + hdd_set_rate_bw(os_rate, HDD_RATE_BW_20); + os_rate->mcs = mcs_index; + os_rate->nss = nss; + + wlan_hdd_fill_os_he_rateflags(os_rate, rate_flags, dcm, guard_interval); + + if (rate_flags & (TX_RATE_VHT160 | TX_RATE_VHT80 | TX_RATE_VHT40 | + TX_RATE_VHT20)) { + if (rate_flags & TX_RATE_VHT160) + hdd_set_rate_bw(os_rate, HDD_RATE_BW_160); + else if (rate_flags & TX_RATE_VHT80) + hdd_set_rate_bw(os_rate, HDD_RATE_BW_80); + else if (rate_flags & TX_RATE_VHT40) + hdd_set_rate_bw(os_rate, HDD_RATE_BW_40); + os_rate->flags |= RATE_INFO_FLAGS_VHT_MCS; + } + + if (rate_flags & (TX_RATE_HT20 | TX_RATE_HT40)) { + if (rate_flags & TX_RATE_HT40) + hdd_set_rate_bw(os_rate, + HDD_RATE_BW_40); + os_rate->flags |= RATE_INFO_FLAGS_MCS; + } + + if (rate_flags & TX_RATE_SGI) + os_rate->flags |= RATE_INFO_FLAGS_SHORT_GI; +} + +bool hdd_report_max_rate(mac_handle_t mac_handle, + struct rate_info *rate, + int8_t signal, + enum tx_rate_info rate_flags, + uint8_t mcs_index, + uint16_t fw_rate, uint8_t nss) +{ + uint8_t i, j, rssidx = 0; + uint16_t max_rate = 0; + uint32_t vht_mcs_map; + bool is_vht20_mcs9 = false; + uint16_t current_rate = 0; + qdf_size_t or_leng = CSR_DOT11_SUPPORTED_RATES_MAX; + uint8_t operational_rates[CSR_DOT11_SUPPORTED_RATES_MAX]; + uint8_t extended_rates[CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX]; + qdf_size_t er_leng = CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX; + uint8_t mcs_rates[SIZE_OF_BASIC_MCS_SET]; + qdf_size_t mcs_leng = SIZE_OF_BASIC_MCS_SET; + struct index_data_rate_type *supported_mcs_rate; + enum data_rate_11ac_max_mcs vht_max_mcs; + uint8_t max_mcs_idx = 0; + uint8_t rate_flag = 1; + int mode = 0, max_ht_idx; + QDF_STATUS stat = QDF_STATUS_E_FAILURE; + struct hdd_context *hdd_ctx; + int link_speed_rssi_high = 0; + int link_speed_rssi_mid = 0; + int link_speed_rssi_low = 0; + uint32_t link_speed_rssi_report = 0; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return false; + } + + ucfg_mlme_stats_get_cfg_values(hdd_ctx->psoc, + &link_speed_rssi_high, + &link_speed_rssi_mid, + &link_speed_rssi_low, + &link_speed_rssi_report); + + if (ucfg_mlme_stats_is_link_speed_report_max_scaled(hdd_ctx->psoc)) { + /* report the max possible speed with RSSI scaling */ + if (signal >= link_speed_rssi_high) { + /* report the max possible speed */ + rssidx = 0; + } else if (signal >= link_speed_rssi_mid) { + /* report middle speed */ + rssidx = 1; + } else if (signal >= link_speed_rssi_low) { + /* report middle speed */ + rssidx = 2; + } else { + /* report actual speed */ + rssidx = 3; + } + } + + /* Get Basic Rate Set */ + if (0 != ucfg_mlme_get_opr_rate_set(hdd_ctx->psoc, + operational_rates, &or_leng)) { + hdd_err("cfg get returned failure"); + /*To keep GUI happy */ + return false; + } + + for (i = 0; i < or_leng; i++) { + for (j = 0; + j < ARRAY_SIZE(supported_data_rate); j++) { + /* Validate Rate Set */ + if (supported_data_rate[j].beacon_rate_index == + (operational_rates[i] & 0x7F)) { + current_rate = + supported_data_rate[j]. + supported_rate[rssidx]; + break; + } + } + /* Update MAX rate */ + max_rate = (current_rate > max_rate) ? current_rate : max_rate; + } + + /* Get Extended Rate Set */ + if (0 != ucfg_mlme_get_ext_opr_rate_set(hdd_ctx->psoc, + extended_rates, + &er_leng)) { + hdd_err("cfg get returned failure"); + /*To keep GUI happy */ + return false; + } + + for (i = 0; i < er_leng; i++) { + for (j = 0; j < ARRAY_SIZE(supported_data_rate); j++) { + if (supported_data_rate[j].beacon_rate_index == + (extended_rates[i] & 0x7F)) { + current_rate = supported_data_rate[j]. + supported_rate[rssidx]; + break; + } + } + /* Update MAX rate */ + max_rate = (current_rate > max_rate) ? current_rate : max_rate; + } + /* Get MCS Rate Set -- + * Only if we are connected in non legacy mode and not reporting + * actual speed + */ + if ((3 != rssidx) && !(rate_flags & TX_RATE_LEGACY)) { + if (0 != ucfg_mlme_get_current_mcs_set(hdd_ctx->psoc, + mcs_rates, + &mcs_leng)) { + hdd_err("cfg get returned failure"); + /*To keep GUI happy */ + return false; + } + rate_flag = 0; + + if (rate_flags & (TX_RATE_VHT80 | TX_RATE_HE80 | + TX_RATE_HE160 | TX_RATE_VHT160)) + mode = 2; + else if (rate_flags & (TX_RATE_HT40 | + TX_RATE_VHT40 | TX_RATE_HE40)) + mode = 1; + else + mode = 0; + + if (rate_flags & (TX_RATE_VHT20 | TX_RATE_VHT40 | + TX_RATE_VHT80 | TX_RATE_HE20 | TX_RATE_HE40 | + TX_RATE_HE80 | TX_RATE_HE160 | TX_RATE_VHT160)) { + stat = ucfg_mlme_cfg_get_vht_tx_mcs_map(hdd_ctx->psoc, + &vht_mcs_map); + if (QDF_IS_STATUS_ERROR(stat)) + hdd_err("failed to get tx_mcs_map"); + + stat = ucfg_mlme_get_vht20_mcs9(hdd_ctx->psoc, + &is_vht20_mcs9); + if (QDF_IS_STATUS_ERROR(stat)) + hdd_err("Failed to get VHT20 MCS9 enable val"); + + vht_max_mcs = (enum data_rate_11ac_max_mcs) + (vht_mcs_map & DATA_RATE_11AC_MCS_MASK); + if (rate_flags & TX_RATE_SGI) + rate_flag |= 1; + + if (DATA_RATE_11AC_MAX_MCS_7 == vht_max_mcs) { + max_mcs_idx = 7; + } else if (DATA_RATE_11AC_MAX_MCS_8 == vht_max_mcs) { + max_mcs_idx = 8; + } else if (DATA_RATE_11AC_MAX_MCS_9 == vht_max_mcs) { + /* + * If the ini enable_vht20_mcs9 is disabled, + * then max mcs index should not be set to 9 + * for TX_RATE_VHT20 + */ + if (!is_vht20_mcs9 && + (rate_flags & TX_RATE_VHT20)) + max_mcs_idx = 8; + else + max_mcs_idx = 9; + } + + if (rate_flags & (TX_RATE_HE20 | TX_RATE_HE40 | + TX_RATE_HE80 | TX_RATE_HE160)) + max_mcs_idx = 11; + + if (rssidx != 0) { + for (i = 0; i <= max_mcs_idx; i++) { + if (signal <= rssi_mcs_tbl[mode][i]) { + max_mcs_idx = i; + break; + } + } + } + + max_mcs_idx = (max_mcs_idx > mcs_index) ? + max_mcs_idx : mcs_index; + } else { + if (rate_flags & TX_RATE_HT40) + rate_flag |= 1; + if (rate_flags & TX_RATE_SGI) + rate_flag |= 2; + + supported_mcs_rate = + (struct index_data_rate_type *) + ((nss == 1) ? &supported_mcs_rate_nss1 : + &supported_mcs_rate_nss2); + + max_ht_idx = MAX_HT_MCS_IDX; + if (rssidx != 0) { + for (i = 0; i < MAX_HT_MCS_IDX; i++) { + if (signal <= rssi_mcs_tbl[mode][i]) { + max_ht_idx = i + 1; + break; + } + } + } + + for (i = 0; i < mcs_leng; i++) { + for (j = 0; j < max_ht_idx; j++) { + if (supported_mcs_rate[j]. + beacon_rate_index == + mcs_rates[i]) { + current_rate = + supported_mcs_rate[j]. + supported_rate + [rate_flag]; + max_mcs_idx = + supported_mcs_rate[j]. + beacon_rate_index; + break; + } + } + + if ((j < MAX_HT_MCS_IDX) && + (current_rate > max_rate)) + max_rate = current_rate; + } + + if (nss == 2) + max_mcs_idx += MAX_HT_MCS_IDX; + max_mcs_idx = (max_mcs_idx > mcs_index) ? + max_mcs_idx : mcs_index; + } + } + + else if (!(rate_flags & TX_RATE_LEGACY)) { + max_rate = fw_rate; + max_mcs_idx = mcs_index; + } + /* report a value at least as big as current rate */ + if ((max_rate < fw_rate) || (0 == max_rate)) { + max_rate = fw_rate; + } + hdd_debug("RLMS %u, rate_flags 0x%x, max_rate %d mcs %d nss %d", + link_speed_rssi_report, rate_flags, + max_rate, max_mcs_idx, nss); + wlan_hdd_fill_os_rate_info(rate_flags, max_rate, rate, + max_mcs_idx, nss, 0, 0); + + return true; +} + +/** + * hdd_report_actual_rate() - Fill the actual rate stats. + * + * @rate_flags: The rate flags computed from rate + * @my_rate: The rate from fw stats + * @rate: The station_info struct member strust rate_info to be filled + * @mcs_index: The mcs index computed from rate + * @nss: The NSS from fw stats + * @dcm: the dcm computed from rate + * @guard_interval: the guard interval computed from rate + * + * Return: None + */ +static void hdd_report_actual_rate(enum tx_rate_info rate_flags, + uint16_t my_rate, + struct rate_info *rate, uint8_t mcs_index, + uint8_t nss, uint8_t dcm, + enum txrate_gi guard_interval) +{ + /* report current rate instead of max rate */ + wlan_hdd_fill_os_rate_info(rate_flags, my_rate, rate, + mcs_index, nss, dcm, guard_interval); +} + +/** + * hdd_wlan_fill_per_chain_rssi_stats() - Fill per chain rssi stats + * + * @sinfo: The station_info structure to be filled. + * @adapter: The HDD adapter structure + * + * Return: None + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) +static inline +void hdd_wlan_fill_per_chain_rssi_stats(struct station_info *sinfo, + struct hdd_adapter *adapter) +{ + bool rssi_stats_valid = false; + uint8_t i; + + sinfo->signal_avg = WLAN_HDD_TGT_NOISE_FLOOR_DBM; + for (i = 0; i < NUM_CHAINS_MAX; i++) { + sinfo->chain_signal_avg[i] = + adapter->hdd_stats.per_chain_rssi_stats.rssi[i]; + sinfo->chains |= 1 << i; + if (sinfo->chain_signal_avg[i] > sinfo->signal_avg && + sinfo->chain_signal_avg[i] != 0) + sinfo->signal_avg = sinfo->chain_signal_avg[i]; + + hdd_debug("RSSI for chain %d, vdev_id %d is %d", + i, adapter->vdev_id, sinfo->chain_signal_avg[i]); + + if (!rssi_stats_valid && sinfo->chain_signal_avg[i]) + rssi_stats_valid = true; + } + + if (rssi_stats_valid) { + sinfo->filled |= HDD_INFO_CHAIN_SIGNAL_AVG; + sinfo->filled |= HDD_INFO_SIGNAL_AVG; + } +} + +#else + +static inline +void hdd_wlan_fill_per_chain_rssi_stats(struct station_info *sinfo, + struct hdd_adapter *adapter) +{ +} + +#endif + +#if defined(CFG80211_RX_FCS_ERROR_REPORTING_SUPPORT) +static void hdd_fill_fcs_and_mpdu_count(struct hdd_adapter *adapter, + struct station_info *sinfo) +{ + sinfo->rx_mpdu_count = adapter->hdd_stats.peer_stats.rx_count; + sinfo->fcs_err_count = adapter->hdd_stats.peer_stats.fcs_count; + hdd_debug("RX mpdu count %d fcs_err_count %d", + sinfo->rx_mpdu_count, sinfo->fcs_err_count); + sinfo->filled |= HDD_INFO_FCS_ERROR_COUNT | HDD_INFO_RX_MPDUS; +} +#else +static void hdd_fill_fcs_and_mpdu_count(struct hdd_adapter *adapter, + struct station_info *sinfo) +{ +} +#endif + +void hdd_check_and_update_nss(struct hdd_context *hdd_ctx, + uint8_t *tx_nss, uint8_t *rx_nss) +{ + if (tx_nss && (*tx_nss > 1) && + policy_mgr_is_current_hwmode_dbs(hdd_ctx->psoc) && + !policy_mgr_is_hw_dbs_2x2_capable(hdd_ctx->psoc)) { + hdd_debug("Hw mode is DBS, Reduce tx nss(%d) to 1", *tx_nss); + (*tx_nss)--; + } + + if (rx_nss && (*rx_nss > 1) && + policy_mgr_is_current_hwmode_dbs(hdd_ctx->psoc) && + !policy_mgr_is_hw_dbs_2x2_capable(hdd_ctx->psoc)) { + hdd_debug("Hw mode is DBS, Reduce rx nss(%d) to 1", *rx_nss); + (*rx_nss)--; + } +} + +/** + * wlan_hdd_get_sta_stats() - get aggregate STA stats + * @wiphy: wireless phy + * @adapter: STA adapter to get stats for + * @mac: mac address of sta + * @sinfo: kernel station_info struct to populate + * + * Fetch the vdev-level aggregate stats for the given STA adapter. This is to + * support "station dump" and "station get" for STA vdevs + * + * Return: errno + */ +static int wlan_hdd_get_sta_stats(struct wiphy *wiphy, + struct hdd_adapter *adapter, + const uint8_t *mac, + struct station_info *sinfo) +{ + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + enum tx_rate_info rate_flags, tx_rate_flags, rx_rate_flags; + uint8_t tx_mcs_index, rx_mcs_index; + struct hdd_context *hdd_ctx = (struct hdd_context *) wiphy_priv(wiphy); + mac_handle_t mac_handle; + int8_t snr = 0; + uint16_t my_tx_rate, my_rx_rate; + uint8_t tx_nss = 1, rx_nss = 1, tx_dcm, rx_dcm; + enum txrate_gi tx_gi, rx_gi; + int32_t rcpi_value; + int link_speed_rssi_high = 0; + int link_speed_rssi_mid = 0; + int link_speed_rssi_low = 0; + uint32_t link_speed_rssi_report = 0; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_GET_STA, + adapter->vdev_id, 0); + + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + hdd_debug("Not associated"); + /*To keep GUI happy */ + return 0; + } + + if (sta_ctx->hdd_reassoc_scenario) { + hdd_debug("Roaming is in progress, cannot continue with this request"); + /* + * supplicant reports very low rssi to upper layer + * and handover happens to cellular. + * send the cached rssi when get_station + */ + sinfo->signal = adapter->rssi; + sinfo->filled |= HDD_INFO_SIGNAL; + return 0; + } + + ucfg_mlme_stats_get_cfg_values(hdd_ctx->psoc, + &link_speed_rssi_high, + &link_speed_rssi_mid, + &link_speed_rssi_low, + &link_speed_rssi_report); + + if (hdd_ctx->rcpi_enabled) + wlan_hdd_get_rcpi(adapter, (uint8_t *)mac, &rcpi_value, + RCPI_MEASUREMENT_TYPE_AVG_MGMT); + + wlan_hdd_get_station_stats(adapter); + + adapter->rssi = adapter->hdd_stats.summary_stat.rssi; + snr = adapter->hdd_stats.summary_stat.snr; + + /* for new connection there might be no valid previous RSSI */ + if (!adapter->rssi) { + hdd_get_rssi_snr_by_bssid(adapter, + sta_ctx->conn_info.bssid.bytes, + &adapter->rssi, &snr); + } + + /* If RSSi is reported as positive then it is invalid */ + if (adapter->rssi > 0) { + hdd_debug_rl("RSSI invalid %d", adapter->rssi); + adapter->rssi = 0; + adapter->hdd_stats.summary_stat.rssi = 0; + } + + sinfo->signal = adapter->rssi; + hdd_debug("snr: %d, rssi: %d", + adapter->hdd_stats.summary_stat.snr, + adapter->hdd_stats.summary_stat.rssi); + sta_ctx->conn_info.signal = sinfo->signal; + sta_ctx->conn_info.noise = + sta_ctx->conn_info.signal - snr; + sta_ctx->cache_conn_info.signal = sinfo->signal; + sta_ctx->cache_conn_info.noise = sta_ctx->conn_info.noise; + sinfo->filled |= HDD_INFO_SIGNAL; + + /* + * we notify connect to lpass here instead of during actual + * connect processing because rssi info is not accurate during + * actual connection. lpass will ensure the notification is + * only processed once per association. + */ + hdd_lpass_notify_connect(adapter); + + rate_flags = adapter->hdd_stats.class_a_stat.tx_rx_rate_flags; + tx_rate_flags = rx_rate_flags = rate_flags; + + tx_mcs_index = adapter->hdd_stats.class_a_stat.tx_mcs_index; + rx_mcs_index = adapter->hdd_stats.class_a_stat.rx_mcs_index; + mac_handle = hdd_ctx->mac_handle; + + /* convert to the UI units of 100kbps */ + my_tx_rate = adapter->hdd_stats.class_a_stat.tx_rate; + my_rx_rate = adapter->hdd_stats.class_a_stat.rx_rate; + + tx_dcm = adapter->hdd_stats.class_a_stat.tx_dcm; + rx_dcm = adapter->hdd_stats.class_a_stat.rx_dcm; + tx_gi = adapter->hdd_stats.class_a_stat.tx_gi; + rx_gi = adapter->hdd_stats.class_a_stat.rx_gi; + + if (!(rate_flags & TX_RATE_LEGACY)) { + tx_nss = adapter->hdd_stats.class_a_stat.tx_nss; + rx_nss = adapter->hdd_stats.class_a_stat.rx_nss; + + hdd_check_and_update_nss(hdd_ctx, &tx_nss, &rx_nss); + + if (ucfg_mlme_stats_is_link_speed_report_actual( + hdd_ctx->psoc)) { + /* Get current rate flags if report actual */ + /* WMA fails to find mcs_index for legacy tx rates */ + if (tx_mcs_index == INVALID_MCS_IDX && my_tx_rate) + tx_rate_flags = TX_RATE_LEGACY; + else + tx_rate_flags = + adapter->hdd_stats.class_a_stat.tx_mcs_rate_flags; + + if (rx_mcs_index == INVALID_MCS_IDX && my_rx_rate) + rx_rate_flags = TX_RATE_LEGACY; + else + rx_rate_flags = + adapter->hdd_stats.class_a_stat.rx_mcs_rate_flags; + } + + if (tx_mcs_index == INVALID_MCS_IDX) + tx_mcs_index = 0; + if (rx_mcs_index == INVALID_MCS_IDX) + rx_mcs_index = 0; + } + + hdd_debug("[RSSI %d, RLMS %u, rssi high %d, rssi mid %d, rssi low %d]-" + "[Rate info: TX: %d, RX: %d]-[Rate flags: TX: 0x%x, RX: 0x%x]" + "-[MCS Index: TX: %d, RX: %d]-[NSS: TX: %d, RX: %d]-" + "[dcm: TX: %d, RX: %d]-[guard interval: TX: %d, RX: %d", + sinfo->signal, link_speed_rssi_report, + link_speed_rssi_high, link_speed_rssi_mid, + link_speed_rssi_low, my_tx_rate, my_rx_rate, + (int)tx_rate_flags, (int)rx_rate_flags, (int)tx_mcs_index, + (int)rx_mcs_index, (int)tx_nss, (int)rx_nss, + (int)tx_dcm, (int)rx_dcm, (int)tx_gi, (int)rx_gi); + + if (!ucfg_mlme_stats_is_link_speed_report_actual(hdd_ctx->psoc)) { + bool tx_rate_calc, rx_rate_calc; + uint8_t tx_nss_max, rx_nss_max; + + /* + * Take static NSS for reporting max rates. NSS from the FW + * is not reliable as it changes as per the environment + * quality. + */ + tx_nss_max = wlan_vdev_mlme_get_nss(adapter->vdev); + rx_nss_max = wlan_vdev_mlme_get_nss(adapter->vdev); + + hdd_check_and_update_nss(hdd_ctx, &tx_nss_max, &rx_nss_max); + + tx_rate_calc = hdd_report_max_rate(mac_handle, &sinfo->txrate, + sinfo->signal, + tx_rate_flags, + tx_mcs_index, + my_tx_rate, + tx_nss_max); + + rx_rate_calc = hdd_report_max_rate(mac_handle, &sinfo->rxrate, + sinfo->signal, + rx_rate_flags, + rx_mcs_index, + my_rx_rate, + rx_nss_max); + + if (!tx_rate_calc || !rx_rate_calc) + /* Keep GUI happy */ + return 0; + } else { + + /* Fill TX stats */ + hdd_report_actual_rate(tx_rate_flags, my_tx_rate, + &sinfo->txrate, tx_mcs_index, + tx_nss, tx_dcm, tx_gi); + + + /* Fill RX stats */ + hdd_report_actual_rate(rx_rate_flags, my_rx_rate, + &sinfo->rxrate, rx_mcs_index, + rx_nss, rx_dcm, rx_gi); + } + + wlan_hdd_fill_summary_stats(&adapter->hdd_stats.summary_stat, + sinfo, + adapter->vdev_id); + sinfo->tx_bytes = adapter->stats.tx_bytes; + sinfo->rx_bytes = adapter->stats.rx_bytes; + sinfo->rx_packets = adapter->stats.rx_packets; + + hdd_fill_fcs_and_mpdu_count(adapter, sinfo); + + qdf_mem_copy(&sta_ctx->conn_info.txrate, + &sinfo->txrate, sizeof(sinfo->txrate)); + qdf_mem_copy(&sta_ctx->cache_conn_info.txrate, + &sinfo->txrate, sizeof(sinfo->txrate)); + + qdf_mem_copy(&sta_ctx->conn_info.rxrate, + &sinfo->rxrate, sizeof(sinfo->rxrate)); + + sinfo->filled |= HDD_INFO_TX_BITRATE | + HDD_INFO_RX_BITRATE | + HDD_INFO_TX_BYTES | + HDD_INFO_RX_BYTES | + HDD_INFO_RX_PACKETS; + + if (tx_rate_flags & TX_RATE_LEGACY) { + hdd_debug("[TX: Reporting legacy rate %d pkt cnt %d]-" + "[RX: Reporting legacy rate %d pkt cnt %d]", + sinfo->txrate.legacy, sinfo->tx_packets, + sinfo->rxrate.legacy, sinfo->rx_packets); + } else { + hdd_debug("[TX: Reporting MCS rate %d, flags 0x%x pkt cnt %d, nss %d, bw %d]-" + "[RX: Reporting MCS rate %d, flags 0x%x pkt cnt %d, nss %d, bw %d]", + sinfo->txrate.mcs, sinfo->txrate.flags, + sinfo->tx_packets, sinfo->txrate.nss, + sinfo->rxrate.bw, sinfo->rxrate.mcs, + sinfo->rxrate.flags, sinfo->rx_packets, + sinfo->rxrate.nss, sinfo->rxrate.bw); + } + + hdd_wlan_fill_per_chain_rssi_stats(sinfo, adapter); + + hdd_exit(); + + return 0; +} + +/** + * __wlan_hdd_cfg80211_get_station() - get station statistics + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @mac: Pointer to mac + * @sinfo: Pointer to station info + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *mac, + struct station_info *sinfo) +{ + int status; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + bool get_peer_info_enable; + QDF_STATUS qdf_status; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + if (adapter->device_mode == QDF_SAP_MODE) { + qdf_status = ucfg_mlme_get_sap_get_peer_info( + hdd_ctx->psoc, &get_peer_info_enable); + if (qdf_status == QDF_STATUS_SUCCESS && get_peer_info_enable) { + status = wlan_hdd_get_station_remote(wiphy, dev, + mac, sinfo); + if (!status) + return 0; + } + return wlan_hdd_get_sap_stats(adapter, sinfo); + } else { + return wlan_hdd_get_sta_stats(wiphy, adapter, mac, sinfo); + } +} + +/** + * _wlan_hdd_cfg80211_get_station() - get station statistics + * + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @mac: Pointer to mac + * @sinfo: Pointer to station info + * + * This API tries runtime PM suspend right away after getting station + * statistics. + * + * Return: 0 for success, non-zero for failure + */ +static int _wlan_hdd_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *mac, + struct station_info *sinfo) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + int errno; + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (!qdf_ctx) + return -EINVAL; + + errno = wlan_hdd_qmi_get_sync_resume(hdd_ctx, qdf_ctx->dev); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_get_station(wiphy, dev, mac, sinfo); + + wlan_hdd_qmi_put_suspend(hdd_ctx, qdf_ctx->dev); + + return errno; +} + +/** + * wlan_hdd_cfg80211_get_station() - get station statistics + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @mac: Pointer to mac + * @sinfo: Pointer to station info + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, const uint8_t *mac, + struct station_info *sinfo) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = _wlan_hdd_cfg80211_get_station(wiphy, dev, mac, sinfo); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_dump_station() - dump station statistics + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @idx: variable to determine whether to get stats or not + * @mac: Pointer to mac + * @sinfo: Pointer to station info + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, + int idx, u8 *mac, + struct station_info *sinfo) +{ + hdd_debug("%s: idx %d", __func__, idx); + if (idx != 0) + return -ENOENT; + qdf_mem_copy(mac, dev->dev_addr, QDF_MAC_ADDR_SIZE); + return __wlan_hdd_cfg80211_get_station(wiphy, dev, mac, sinfo); +} + +/** + * wlan_hdd_cfg80211_dump_station() - dump station statistics + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @idx: variable to determine whether to get stats or not + * @mac: Pointer to mac + * @sinfo: Pointer to station info + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, + int idx, u8 *mac, + struct station_info *sinfo) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_dump_station(wiphy, dev, idx, mac, sinfo); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_get_stats() - Function to retrieve interface statistics + * @dev: pointer to network device + * + * This function is the ndo_get_stats method for all netdevs + * registered with the kernel + * + * Return: pointer to net_device_stats structure + */ +struct net_device_stats *hdd_get_stats(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + hdd_enter_dev(dev); + return &adapter->stats; +} + + +/* + * time = cycle_count * cycle + * cycle = 1 / clock_freq + * Since the unit of clock_freq reported from + * FW is MHZ, and we want to calculate time in + * ms level, the result is + * time = cycle / (clock_freq * 1000) + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) +static bool wlan_fill_survey_result(struct survey_info *survey, int opfreq, + struct scan_chan_info *chan_info, + struct ieee80211_channel *channels) +{ + uint64_t clock_freq = chan_info->clock_freq * 1000; + + if (channels->center_freq != (uint16_t)chan_info->freq) + return false; + + survey->channel = channels; + survey->noise = chan_info->noise_floor; + survey->filled = SURVEY_INFO_NOISE_DBM; + + if (opfreq == chan_info->freq) + survey->filled |= SURVEY_INFO_IN_USE; + + if (clock_freq == 0) + return true; + + survey->time = qdf_do_div(chan_info->cycle_count, clock_freq); + + survey->time_busy = qdf_do_div(chan_info->rx_clear_count, clock_freq); + + survey->time_tx = qdf_do_div(chan_info->tx_frame_count, clock_freq); + + survey->filled |= SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_TX; + return true; +} +#else +static bool wlan_fill_survey_result(struct survey_info *survey, int opfreq, + struct scan_chan_info *chan_info, + struct ieee80211_channel *channels) +{ + uint64_t clock_freq = chan_info->clock_freq * 1000; + + if (channels->center_freq != (uint16_t)chan_info->freq) + return false; + + survey->channel = channels; + survey->noise = chan_info->noise_floor; + survey->filled = SURVEY_INFO_NOISE_DBM; + + if (opfreq == chan_info->freq) + survey->filled |= SURVEY_INFO_IN_USE; + + if (clock_freq == 0) + return true; + + survey->channel_time = qdf_do_div(chan_info->cycle_count, clock_freq); + + survey->channel_time_busy = qdf_do_div(chan_info->rx_clear_count, + clock_freq); + + survey->channel_time_tx = qdf_do_div(chan_info->tx_frame_count, + clock_freq); + + survey->filled |= SURVEY_INFO_CHANNEL_TIME | + SURVEY_INFO_CHANNEL_TIME_BUSY | + SURVEY_INFO_CHANNEL_TIME_TX; + return true; +} +#endif + +static bool wlan_hdd_update_survey_info(struct wiphy *wiphy, + struct hdd_adapter *adapter, + struct survey_info *survey, int idx) +{ + bool filled = false; + int i, j = 0; + uint32_t opfreq = 0; /* Initialization Required */ + struct hdd_context *hdd_ctx; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + sme_get_operation_channel(hdd_ctx->mac_handle, &opfreq, + adapter->vdev_id); + + mutex_lock(&hdd_ctx->chan_info_lock); + + for (i = 0; i < HDD_NUM_NL80211_BANDS && !filled; i++) { + if (!wiphy->bands[i]) + continue; + + for (j = 0; j < wiphy->bands[i]->n_channels && !filled; j++) { + struct ieee80211_supported_band *band = wiphy->bands[i]; + + filled = wlan_fill_survey_result(survey, opfreq, + &hdd_ctx->chan_info[idx], + &band->channels[j]); + } + } + mutex_unlock(&hdd_ctx->chan_info_lock); + + return filled; +} + +/** + * __wlan_hdd_cfg80211_dump_survey() - get survey related info + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @idx: Index + * @survey: Pointer to survey info + * + * Return: 0 for success, non-zero for failure + */ +static int __wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy, + struct net_device *dev, + int idx, struct survey_info *survey) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + struct hdd_station_ctx *sta_ctx; + int status; + bool filled = false; + + if (idx > NUM_CHANNELS - 1) + return -EINVAL; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return status; + + if (!hdd_ctx->chan_info) { + hdd_debug("chan_info is NULL"); + return -EINVAL; + } + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (!ucfg_scan_is_snr_monitor_enabled(hdd_ctx->psoc)) + return -ENONET; + + if (sta_ctx->hdd_reassoc_scenario) { + hdd_debug("Roaming in progress, hence return"); + return -ENONET; + } + + filled = wlan_hdd_update_survey_info(wiphy, adapter, survey, idx); + + if (!filled) + return -ENONET; + + return 0; +} + +/** + * wlan_hdd_cfg80211_dump_survey() - get survey related info + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @idx: Index + * @survey: Pointer to survey info + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy, + struct net_device *dev, + int idx, struct survey_info *survey) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_dump_survey(wiphy, dev, idx, survey); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_display_hif_stats() - display hif stats + * + * Return: none + * + */ +void hdd_display_hif_stats(void) +{ + void *hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + + if (!hif_ctx) + return; + + hif_display_stats(hif_ctx); +} + +/** + * hdd_clear_hif_stats() - clear hif stats + * + * Return: none + */ +void hdd_clear_hif_stats(void) +{ + void *hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + + if (!hif_ctx) + return; + hif_clear_stats(hif_ctx); +} + +/** + * hdd_is_rcpi_applicable() - validates RCPI request + * @adapter: adapter upon which the measurement is requested + * @mac_addr: peer addr for which measurement is requested + * @rcpi_value: pointer to where the RCPI should be returned + * @reassoc: used to return cached RCPI during reassoc + * + * Return: true for success, false for failure + */ + +static bool hdd_is_rcpi_applicable(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr, + int32_t *rcpi_value, + bool *reassoc) +{ + struct hdd_station_ctx *hdd_sta_ctx; + + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (hdd_sta_ctx->conn_info.conn_state != + eConnectionState_Associated) + return false; + + if (hdd_sta_ctx->hdd_reassoc_scenario) { + /* return the cached rcpi, if mac addr matches */ + hdd_debug("Roaming in progress, return cached RCPI"); + if (!qdf_mem_cmp(&adapter->rcpi.mac_addr, + mac_addr, sizeof(*mac_addr))) { + *rcpi_value = adapter->rcpi.rcpi; + *reassoc = true; + return true; + } + return false; + } + + if (qdf_mem_cmp(mac_addr, &hdd_sta_ctx->conn_info.bssid, + sizeof(*mac_addr))) { + hdd_err("mac addr is different from bssid connected"); + return false; + } + } else if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + if (!test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { + hdd_err("Invalid rcpi request, softap not started"); + return false; + } + + /* check if peer mac addr is associated to softap */ + if (!hdd_is_peer_associated(adapter, mac_addr)) { + hdd_err("invalid peer mac-addr: not associated"); + return false; + } + } else { + hdd_err("Invalid rcpi request"); + return false; + } + + *reassoc = false; + return true; +} + +/** + * wlan_hdd_get_rcpi_cb() - callback function for rcpi response + * @context: Pointer to rcpi context + * @rcpi_req: Pointer to rcpi response + * + * Return: None + */ +static void wlan_hdd_get_rcpi_cb(void *context, struct qdf_mac_addr mac_addr, + int32_t rcpi, QDF_STATUS status) +{ + struct osif_request *request; + struct rcpi_info *priv; + + if (!context) { + hdd_err("No rcpi context"); + return; + } + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete RCPI request"); + return; + } + + priv = osif_request_priv(request); + priv->mac_addr = mac_addr; + + if (!QDF_IS_STATUS_SUCCESS(status)) { + priv->rcpi = 0; + hdd_err("Error in computing RCPI"); + } else { + priv->rcpi = rcpi; + } + + osif_request_complete(request); + osif_request_put(request); +} + +/** + * wlan_hdd_get_rcpi() - local function to get RCPI + * @adapter: adapter upon which the measurement is requested + * @mac: peer addr for which measurement is requested + * @rcpi_value: pointer to where the RCPI should be returned + * @measurement_type: type of rcpi measurement + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_get_rcpi(struct hdd_adapter *adapter, + uint8_t *mac, + int32_t *rcpi_value, + enum rcpi_measurement_type measurement_type) +{ + struct hdd_context *hdd_ctx; + int status = 0, ret = 0; + struct qdf_mac_addr mac_addr; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct sme_rcpi_req *rcpi_req; + void *cookie; + struct rcpi_info *priv; + struct osif_request *request; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_RCPI, + }; + bool reassoc; + + hdd_enter(); + + /* initialize the rcpi value to zero, useful in error cases */ + *rcpi_value = 0; + + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (!adapter) { + hdd_warn("adapter context is NULL"); + return -EINVAL; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return -EINVAL; + + if (!hdd_ctx->rcpi_enabled) { + hdd_debug("RCPI not supported"); + return -EINVAL; + } + + if (!mac) { + hdd_warn("RCPI peer mac-addr is NULL"); + return -EINVAL; + } + + qdf_mem_copy(&mac_addr, mac, QDF_MAC_ADDR_SIZE); + + if (!hdd_is_rcpi_applicable(adapter, &mac_addr, rcpi_value, &reassoc)) + return -EINVAL; + if (reassoc) + return 0; + + rcpi_req = qdf_mem_malloc(sizeof(*rcpi_req)); + if (!rcpi_req) + return -EINVAL; + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + qdf_mem_free(rcpi_req); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + rcpi_req->mac_addr = mac_addr; + rcpi_req->session_id = adapter->vdev_id; + rcpi_req->measurement_type = measurement_type; + rcpi_req->rcpi_callback = wlan_hdd_get_rcpi_cb; + rcpi_req->rcpi_context = cookie; + + qdf_status = sme_get_rcpi(hdd_ctx->mac_handle, rcpi_req); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Unable to retrieve RCPI"); + status = qdf_status_to_os_return(qdf_status); + goto out; + } + + /* request was sent -- wait for the response */ + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving RCPI"); + status = -EINVAL; + goto out; + } + + /* update the adapter with the fresh results */ + priv = osif_request_priv(request); + adapter->rcpi.mac_addr = priv->mac_addr; + adapter->rcpi.rcpi = priv->rcpi; + if (qdf_mem_cmp(&mac_addr, &priv->mac_addr, sizeof(mac_addr))) { + hdd_err("mis match of mac addr from call-back"); + status = -EINVAL; + goto out; + } + + *rcpi_value = adapter->rcpi.rcpi; + hdd_debug("RCPI = %d", *rcpi_value); +out: + qdf_mem_free(rcpi_req); + osif_request_put(request); + + hdd_exit(); + return status; +} + +#ifdef WLAN_FEATURE_MIB_STATS +QDF_STATUS wlan_hdd_get_mib_stats(struct hdd_adapter *adapter) +{ + int ret = 0; + struct stats_event *stats; + + if (!adapter) { + hdd_err("Invalid context, adapter"); + return QDF_STATUS_E_FAULT; + } + + stats = wlan_cfg80211_mc_cp_stats_get_mib_stats( + adapter->vdev, + &ret); + if (ret || !stats) { + wlan_cfg80211_mc_cp_stats_free_stats_event(stats); + return ret; + } + + hdd_debugfs_process_mib_stats(adapter, stats); + + wlan_cfg80211_mc_cp_stats_free_stats_event(stats); + return ret; +} +#endif + +QDF_STATUS wlan_hdd_get_rssi(struct hdd_adapter *adapter, int8_t *rssi_value) +{ + int ret = 0, i; + struct hdd_station_ctx *sta_ctx; + struct stats_event *rssi_info; + + if (!adapter) { + hdd_err("Invalid context, adapter"); + return QDF_STATUS_E_FAULT; + } + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + hdd_err("Recovery in Progress. State: 0x%x Ignore!!!", + cds_get_driver_state()); + /* return a cached value */ + *rssi_value = adapter->rssi; + return QDF_STATUS_SUCCESS; + } + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + hdd_debug("Not associated!, rssi on disconnect %d", + adapter->rssi_on_disconnect); + *rssi_value = adapter->rssi_on_disconnect; + return QDF_STATUS_SUCCESS; + } + + if (sta_ctx->hdd_reassoc_scenario) { + hdd_debug("Roaming in progress, return cached RSSI"); + *rssi_value = adapter->rssi; + return QDF_STATUS_SUCCESS; + } + + rssi_info = wlan_cfg80211_mc_cp_stats_get_peer_rssi( + adapter->vdev, + sta_ctx->conn_info.bssid.bytes, + &ret); + if (ret || !rssi_info) { + wlan_cfg80211_mc_cp_stats_free_stats_event(rssi_info); + return ret; + } + + for (i = 0; i < rssi_info->num_peer_stats; i++) { + if (!qdf_mem_cmp(rssi_info->peer_stats[i].peer_macaddr, + sta_ctx->conn_info.bssid.bytes, + QDF_MAC_ADDR_SIZE)) { + *rssi_value = rssi_info->peer_stats[i].peer_rssi; + hdd_debug("RSSI = %d", *rssi_value); + wlan_cfg80211_mc_cp_stats_free_stats_event(rssi_info); + return QDF_STATUS_SUCCESS; + } + } + + wlan_cfg80211_mc_cp_stats_free_stats_event(rssi_info); + hdd_err("bss peer not present in returned result"); + return QDF_STATUS_E_FAULT; +} + +struct snr_priv { + int8_t snr; +}; + +/** + * hdd_get_snr_cb() - "Get SNR" callback function + * @snr: Current SNR of the station + * @sta_id: ID of the station + * @context: opaque context originally passed to SME. HDD always passes + * a cookie for the request context + * + * Return: None + */ +static void hdd_get_snr_cb(int8_t snr, void *context) +{ + struct osif_request *request; + struct snr_priv *priv; + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + /* propagate response back to requesting thread */ + priv = osif_request_priv(request); + priv->snr = snr; + osif_request_complete(request); + osif_request_put(request); +} + +QDF_STATUS wlan_hdd_get_snr(struct hdd_adapter *adapter, int8_t *snr) +{ + struct hdd_context *hdd_ctx; + struct hdd_station_ctx *sta_ctx; + QDF_STATUS status; + int ret; + void *cookie; + struct osif_request *request; + struct snr_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + + hdd_enter(); + + if (!adapter) { + hdd_err("Invalid context, adapter"); + return QDF_STATUS_E_FAULT; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return QDF_STATUS_E_FAULT; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return QDF_STATUS_E_FAULT; + } + cookie = osif_request_cookie(request); + + status = sme_get_snr(hdd_ctx->mac_handle, hdd_get_snr_cb, + sta_ctx->conn_info.bssid, cookie); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Unable to retrieve RSSI"); + /* we'll returned a cached value below */ + } else { + /* request was sent -- wait for the response */ + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving SNR"); + /* we'll now returned a cached value below */ + } else { + /* update the adapter with the fresh results */ + priv = osif_request_priv(request); + adapter->snr = priv->snr; + } + } + + /* + * either we never sent a request, we sent a request and + * received a response or we sent a request and timed out. + * regardless we are done with the request. + */ + osif_request_put(request); + + *snr = adapter->snr; + hdd_exit(); + return QDF_STATUS_SUCCESS; +} + +struct linkspeed_priv { + struct link_speed_info linkspeed_info; +}; + +static void +hdd_get_link_speed_cb(struct link_speed_info *linkspeed_info, void *context) +{ + struct osif_request *request; + struct linkspeed_priv *priv; + + if (!linkspeed_info) { + hdd_err("NULL linkspeed"); + return; + } + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + priv->linkspeed_info = *linkspeed_info; + osif_request_complete(request); + osif_request_put(request); +} + +int wlan_hdd_get_linkspeed_for_peermac(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_address, + uint32_t *linkspeed) +{ + int ret; + QDF_STATUS status; + void *cookie; + struct link_speed_info *linkspeed_info; + struct osif_request *request; + struct linkspeed_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + + if ((!adapter) || (!linkspeed)) { + hdd_err("NULL argument"); + return -EINVAL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + ret = -ENOMEM; + goto return_cached_value; + } + + cookie = osif_request_cookie(request); + priv = osif_request_priv(request); + + linkspeed_info = &priv->linkspeed_info; + qdf_copy_macaddr(&linkspeed_info->peer_macaddr, mac_address); + status = sme_get_link_speed(adapter->hdd_ctx->mac_handle, + linkspeed_info, + cookie, hdd_get_link_speed_cb); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Unable to retrieve statistics for link speed"); + ret = qdf_status_to_os_return(status); + goto cleanup; + } + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving link speed"); + goto cleanup; + } + adapter->estimated_linkspeed = linkspeed_info->estLinkSpeed; + +cleanup: + /* + * either we never sent a request, we sent a request and + * received a response or we sent a request and timed out. + * regardless we are done with the request. + */ + osif_request_put(request); + +return_cached_value: + *linkspeed = adapter->estimated_linkspeed; + + return ret; +} + +int wlan_hdd_get_link_speed(struct hdd_adapter *adapter, uint32_t *link_speed) +{ + struct hdd_context *hddctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_station_ctx *hdd_stactx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + int ret; + + ret = wlan_hdd_validate_context(hddctx); + if (ret) + return ret; + + /* Linkspeed is allowed only for P2P mode */ + if (adapter->device_mode != QDF_P2P_CLIENT_MODE) { + hdd_err("Link Speed is not allowed in Device mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -ENOTSUPP; + } + + if (eConnectionState_Associated != hdd_stactx->conn_info.conn_state) { + /* we are not connected so we don't have a classAstats */ + *link_speed = 0; + } else { + struct qdf_mac_addr bssid; + + qdf_copy_macaddr(&bssid, &hdd_stactx->conn_info.bssid); + + ret = wlan_hdd_get_linkspeed_for_peermac(adapter, &bssid, + link_speed); + if (ret) { + hdd_err("Unable to retrieve SME linkspeed"); + return ret; + } + /* linkspeed in units of 500 kbps */ + *link_speed = (*link_speed) / 500; + } + return 0; +} + +struct peer_info_priv { + struct sir_peer_sta_ext_info peer_sta_ext_info; +}; + +/** + * wlan_hdd_get_peer_info_cb() - get peer info callback + * @sta_info: pointer of peer information + * @context: get peer info callback context + * + * This function will fill stats info to peer info priv + * + */ +static void wlan_hdd_get_peer_info_cb(struct sir_peer_info_ext_resp *sta_info, + void *context) +{ + struct osif_request *request; + struct peer_info_priv *priv; + uint8_t sta_num; + + if ((!sta_info) || (!context)) { + hdd_err("Bad param, sta_info [%pK] context [%pK]", + sta_info, context); + return; + } + + if (!sta_info->count) { + hdd_err("Fail to get remote peer info"); + return; + } + + if (sta_info->count > MAX_PEER_STA) { + hdd_warn("Exceed max peer number %d", sta_info->count); + sta_num = MAX_PEER_STA; + } else { + sta_num = sta_info->count; + } + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + + priv->peer_sta_ext_info.sta_num = sta_num; + qdf_mem_copy(&priv->peer_sta_ext_info.info, + sta_info->info, + sta_num * sizeof(sta_info->info[0])); + + osif_request_complete(request); + osif_request_put(request); +} + +int wlan_hdd_get_peer_info(struct hdd_adapter *adapter, + struct qdf_mac_addr macaddress, + struct sir_peer_info_ext *peer_info_ext) +{ + QDF_STATUS status; + void *cookie; + int ret; + struct sir_peer_info_ext_req peer_info_req; + struct osif_request *request; + struct peer_info_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + + if (!adapter) { + hdd_err("adapter is NULL"); + return -EFAULT; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + + cookie = osif_request_cookie(request); + priv = osif_request_priv(request); + + qdf_mem_copy(&peer_info_req.peer_macaddr, &macaddress, + QDF_MAC_ADDR_SIZE); + peer_info_req.sessionid = adapter->vdev_id; + peer_info_req.reset_after_request = 0; + status = sme_get_peer_info_ext(adapter->hdd_ctx->mac_handle, + &peer_info_req, + cookie, + wlan_hdd_get_peer_info_cb); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Unable to retrieve statistics for peer info"); + ret = -EFAULT; + } else { + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving peer info"); + ret = -EFAULT; + } else { + /* only support one peer by now */ + *peer_info_ext = priv->peer_sta_ext_info.info[0]; + ret = 0; + } + } + + osif_request_put(request); + + return ret; +} + +int wlan_hdd_get_station_stats(struct hdd_adapter *adapter) +{ + int ret = 0; + struct stats_event *stats; + struct wlan_mlme_nss_chains *dynamic_cfg; + uint32_t tx_nss, rx_nss; + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + + stats = wlan_cfg80211_mc_cp_stats_get_station_stats(vdev, + &ret); + if (ret || !stats) { + wlan_cfg80211_mc_cp_stats_free_stats_event(stats); + goto out; + } + + /* save summary stats to legacy location */ + qdf_mem_copy(adapter->hdd_stats.summary_stat.retry_cnt, + stats->vdev_summary_stats[0].stats.retry_cnt, + sizeof(adapter->hdd_stats.summary_stat.retry_cnt)); + qdf_mem_copy(adapter->hdd_stats.summary_stat.multiple_retry_cnt, + stats->vdev_summary_stats[0].stats.multiple_retry_cnt, + sizeof(adapter->hdd_stats.summary_stat.multiple_retry_cnt)); + qdf_mem_copy(adapter->hdd_stats.summary_stat.tx_frm_cnt, + stats->vdev_summary_stats[0].stats.tx_frm_cnt, + sizeof(adapter->hdd_stats.summary_stat.tx_frm_cnt)); + qdf_mem_copy(adapter->hdd_stats.summary_stat.fail_cnt, + stats->vdev_summary_stats[0].stats.fail_cnt, + sizeof(adapter->hdd_stats.summary_stat.fail_cnt)); + adapter->hdd_stats.summary_stat.snr = + stats->vdev_summary_stats[0].stats.snr; + adapter->hdd_stats.summary_stat.rssi = + stats->vdev_summary_stats[0].stats.rssi; + adapter->hdd_stats.summary_stat.rx_frm_cnt = + stats->vdev_summary_stats[0].stats.rx_frm_cnt; + adapter->hdd_stats.summary_stat.frm_dup_cnt = + stats->vdev_summary_stats[0].stats.frm_dup_cnt; + adapter->hdd_stats.summary_stat.rts_fail_cnt = + stats->vdev_summary_stats[0].stats.rts_fail_cnt; + adapter->hdd_stats.summary_stat.ack_fail_cnt = + stats->vdev_summary_stats[0].stats.ack_fail_cnt; + adapter->hdd_stats.summary_stat.rts_succ_cnt = + stats->vdev_summary_stats[0].stats.rts_succ_cnt; + adapter->hdd_stats.summary_stat.rx_discard_cnt = + stats->vdev_summary_stats[0].stats.rx_discard_cnt; + adapter->hdd_stats.summary_stat.rx_error_cnt = + stats->vdev_summary_stats[0].stats.rx_error_cnt; + adapter->hdd_stats.peer_stats.rx_count = + stats->peer_adv_stats->rx_count; + adapter->hdd_stats.peer_stats.rx_bytes = + stats->peer_adv_stats->rx_bytes; + adapter->hdd_stats.peer_stats.fcs_count = + stats->peer_adv_stats->fcs_count; + + dynamic_cfg = mlme_get_dynamic_vdev_config(vdev); + if (!dynamic_cfg) { + hdd_err("nss chain dynamic config NULL"); + wlan_cfg80211_mc_cp_stats_free_stats_event(stats); + ret = -EINVAL; + goto out; + } + + switch (hdd_conn_get_connected_band(&adapter->session.station)) { + case BAND_2G: + tx_nss = dynamic_cfg->tx_nss[NSS_CHAINS_BAND_2GHZ]; + rx_nss = dynamic_cfg->rx_nss[NSS_CHAINS_BAND_2GHZ]; + break; + case BAND_5G: + tx_nss = dynamic_cfg->tx_nss[NSS_CHAINS_BAND_5GHZ]; + rx_nss = dynamic_cfg->rx_nss[NSS_CHAINS_BAND_5GHZ]; + break; + default: + tx_nss = wlan_vdev_mlme_get_nss(vdev); + rx_nss = wlan_vdev_mlme_get_nss(vdev); + } + /* Intersection of self and AP's NSS capability */ + if (tx_nss > wlan_vdev_mlme_get_nss(vdev)) + tx_nss = wlan_vdev_mlme_get_nss(vdev); + + if (rx_nss > wlan_vdev_mlme_get_nss(vdev)) + rx_nss = wlan_vdev_mlme_get_nss(vdev); + + /* save class a stats to legacy location */ + adapter->hdd_stats.class_a_stat.tx_nss = tx_nss; + adapter->hdd_stats.class_a_stat.rx_nss = rx_nss; + adapter->hdd_stats.class_a_stat.tx_rate = stats->tx_rate; + adapter->hdd_stats.class_a_stat.rx_rate = stats->rx_rate; + adapter->hdd_stats.class_a_stat.tx_rx_rate_flags = stats->tx_rate_flags; + adapter->hdd_stats.class_a_stat.tx_mcs_index = + sme_get_mcs_idx(stats->tx_rate, stats->tx_rate_flags, + &adapter->hdd_stats.class_a_stat.tx_nss, + &adapter->hdd_stats.class_a_stat.tx_dcm, + &adapter->hdd_stats.class_a_stat.tx_gi, + &adapter->hdd_stats.class_a_stat. + tx_mcs_rate_flags); + + adapter->hdd_stats.class_a_stat.rx_mcs_index = + sme_get_mcs_idx(stats->rx_rate, stats->tx_rate_flags, + &adapter->hdd_stats.class_a_stat.rx_nss, + &adapter->hdd_stats.class_a_stat.rx_dcm, + &adapter->hdd_stats.class_a_stat.rx_gi, + &adapter->hdd_stats.class_a_stat. + rx_mcs_rate_flags); + + /* save per chain rssi to legacy location */ + qdf_mem_copy(adapter->hdd_stats.per_chain_rssi_stats.rssi, + stats->vdev_chain_rssi[0].chain_rssi, + sizeof(stats->vdev_chain_rssi[0].chain_rssi)); + wlan_cfg80211_mc_cp_stats_free_stats_event(stats); + +out: + hdd_objmgr_put_vdev(vdev); + return ret; +} + +struct temperature_priv { + int temperature; +}; + +/** + * hdd_get_temperature_cb() - "Get Temperature" callback function + * @temperature: measured temperature + * @context: callback context + * + * This function is passed to sme_get_temperature() as the callback + * function to be invoked when the temperature measurement is + * available. + * + * Return: None + */ +static void hdd_get_temperature_cb(int temperature, void *context) +{ + struct osif_request *request; + struct temperature_priv *priv; + + hdd_enter(); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + priv->temperature = temperature; + osif_request_complete(request); + osif_request_put(request); + hdd_exit(); +} + +int wlan_hdd_get_temperature(struct hdd_adapter *adapter, int *temperature) +{ + QDF_STATUS status; + int ret; + void *cookie; + struct osif_request *request; + struct temperature_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + + hdd_enter(); + if (!adapter) { + hdd_err("adapter is NULL"); + return -EPERM; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + status = sme_get_temperature(adapter->hdd_ctx->mac_handle, cookie, + hdd_get_temperature_cb); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Unable to retrieve temperature"); + } else { + ret = osif_request_wait_for_response(request); + if (ret) { + hdd_err("SME timed out while retrieving temperature"); + } else { + /* update the adapter with the fresh results */ + priv = osif_request_priv(request); + if (priv->temperature) + adapter->temperature = priv->temperature; + } + } + + /* + * either we never sent a request, we sent a request and + * received a response or we sent a request and timed out. + * regardless we are done with the request. + */ + osif_request_put(request); + + *temperature = adapter->temperature; + hdd_exit(); + return 0; +} + +void wlan_hdd_display_txrx_stats(struct hdd_context *ctx) +{ + struct hdd_adapter *adapter = NULL, *next_adapter = NULL; + struct hdd_tx_rx_stats *stats; + int i = 0; + uint32_t total_rx_pkt, total_rx_dropped, + total_rx_delv, total_rx_refused; + wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_CACHE_STATION_STATS_CB; + + hdd_for_each_adapter_dev_held_safe(ctx, adapter, next_adapter, + dbgid) { + total_rx_pkt = 0; + total_rx_dropped = 0; + total_rx_delv = 0; + total_rx_refused = 0; + stats = &adapter->hdd_stats.tx_rx_stats; + + if (adapter->vdev_id == INVAL_VDEV_ID) { + hdd_adapter_dev_put_debug(adapter, dbgid); + continue; + } + + hdd_debug("adapter: %u", adapter->vdev_id); + for (; i < NUM_CPUS; i++) { + total_rx_pkt += stats->rx_packets[i]; + total_rx_dropped += stats->rx_dropped[i]; + total_rx_delv += stats->rx_delivered[i]; + total_rx_refused += stats->rx_refused[i]; + } + + /* dev_put has to be done here */ + hdd_adapter_dev_put_debug(adapter, dbgid); + + hdd_debug("TX - called %u, dropped %u orphan %u", + stats->tx_called, stats->tx_dropped, + stats->tx_orphaned); + + for (i = 0; i < NUM_CPUS; i++) { + if (stats->rx_packets[i] == 0) + continue; + hdd_debug("Rx CPU[%d]: packets %u, dropped %u, delivered %u, refused %u", + i, stats->rx_packets[i], stats->rx_dropped[i], + stats->rx_delivered[i], stats->rx_refused[i]); + } + hdd_debug("RX - packets %u, dropped %u, unsolict_arp_n_mcast_drp %u, delivered %u, refused %u GRO - agg %u drop %u non-agg %u flush_skip %u low_tput_flush %u disabled(conc %u low-tput %u)", + total_rx_pkt, total_rx_dropped, + qdf_atomic_read(&stats->rx_usolict_arp_n_mcast_drp), + total_rx_delv, + total_rx_refused, stats->rx_aggregated, + stats->rx_gro_dropped, stats->rx_non_aggregated, + stats->rx_gro_flush_skip, + stats->rx_gro_low_tput_flush, + qdf_atomic_read(&ctx->disable_rx_ol_in_concurrency), + qdf_atomic_read(&ctx->disable_rx_ol_in_low_tput)); + } +} + +#ifdef QCA_SUPPORT_CP_STATS +/** + * hdd_lost_link_cp_stats_info_cb() - callback function to get lost + * link information + * @stats_ev: Stats event pointer + * FW sends vdev stats on vdev down, this callback is registered + * with cp_stats component to get the last available vdev stats + * From the FW. + * + * Return: None + */ + +static void hdd_lost_link_cp_stats_info_cb(void *stats_ev) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct hdd_adapter *adapter; + struct stats_event *ev = stats_ev; + uint8_t i; + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + for (i = 0; i < ev->num_summary_stats; i++) { + adapter = hdd_get_adapter_by_vdev( + hdd_ctx, + ev->vdev_summary_stats[i].vdev_id); + if (!adapter) { + hdd_debug("invalid adapter"); + continue; + } + adapter->rssi_on_disconnect = + ev->vdev_summary_stats[i].stats.rssi; + hdd_debug("rssi %d for " QDF_MAC_ADDR_FMT, + adapter->rssi_on_disconnect, + QDF_MAC_ADDR_REF(adapter->mac_addr.bytes)); + } +} + +void wlan_hdd_register_cp_stats_cb(struct hdd_context *hdd_ctx) +{ + ucfg_mc_cp_stats_register_lost_link_info_cb( + hdd_ctx->psoc, + hdd_lost_link_cp_stats_info_cb); +} +#endif + +QDF_STATUS hdd_update_sta_arp_stats(struct hdd_adapter *adapter) +{ + struct cdp_peer_stats *peer_stats; + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct hdd_arp_stats_s *arp_stats; + QDF_STATUS status; + + peer_stats = qdf_mem_malloc(sizeof(*peer_stats)); + if (!peer_stats) + return QDF_STATUS_E_NOMEM; + + status = cdp_host_get_peer_stats(cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id, + sta_ctx->conn_info.bssid.bytes, + peer_stats); + + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(peer_stats); + return status; + } + + arp_stats = &adapter->hdd_stats.hdd_arp_stats; + + arp_stats->tx_host_fw_sent = + arp_stats->tx_arp_req_count - arp_stats->tx_dropped; + arp_stats->tx_ack_cnt = arp_stats->tx_host_fw_sent - + peer_stats->tx.no_ack_count[QDF_PROTO_ARP_REQ]; + qdf_mem_free(peer_stats); + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_stats.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..7e60de61acaf8cc5d8939af00928f9614b3351a9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_stats.h @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_stats.h + * + * WLAN Host Device Driver statistics related implementation + * + */ + +#if !defined(WLAN_HDD_STATS_H) +#define WLAN_HDD_STATS_H + +#include "wlan_hdd_main.h" + +#define INVALID_MCS_IDX 255 +#define MAX_HT_MCS_IDX 8 +#define MAX_VHT_MCS_IDX 10 + +#define DATA_RATE_11AC_MCS_MASK 0x03 + +/* LL stats get request time out value */ +#define WLAN_WAIT_TIME_LL_STATS 800 + +#define WLAN_HDD_TGT_NOISE_FLOOR_DBM (-96) + +/** + * struct index_vht_data_rate_type - vht data rate type + * @beacon_rate_index: Beacon rate index + * @supported_VHT80_rate: VHT80 rate + * @supported_VHT40_rate: VHT40 rate + * @supported_VHT20_rate: VHT20 rate + */ +struct index_vht_data_rate_type { + uint8_t beacon_rate_index; + uint16_t supported_VHT80_rate[2]; + uint16_t supported_VHT40_rate[2]; + uint16_t supported_VHT20_rate[2]; +}; + +/** + * enum - data_rate_11ac_max_mcs + * @DATA_RATE_11AC_MAX_MCS_7: MCS7 rate + * @DATA_RATE_11AC_MAX_MCS_8: MCS8 rate + * @DATA_RATE_11AC_MAX_MCS_9: MCS9 rate + * @DATA_RATE_11AC_MAX_MCS_NA:i Not applicable + */ +enum data_rate_11ac_max_mcs { + DATA_RATE_11AC_MAX_MCS_7, + DATA_RATE_11AC_MAX_MCS_8, + DATA_RATE_11AC_MAX_MCS_9, + DATA_RATE_11AC_MAX_MCS_NA +}; + +/** + * struct index_data_rate_type - non vht data rate type + * @beacon_rate_index: Beacon rate index + * @supported_rate: Supported rate table + */ +struct index_data_rate_type { + uint8_t beacon_rate_index; + uint16_t supported_rate[4]; +}; + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + +/** + * wlan_hdd_cfg80211_ll_stats_set() - set link layer stats + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: int + */ +int wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * wlan_hdd_cfg80211_ll_stats_get() - get link layer stats + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: int + */ +int wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + + +/** + * wlan_hdd_cfg80211_ll_stats_clear() - clear link layer stats + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: int + */ +int wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +void wlan_hdd_clear_link_layer_stats(struct hdd_adapter *adapter); + +static inline bool hdd_link_layer_stats_supported(void) +{ + return true; +} + +/** + * __wlan_hdd_cfg80211_ll_stats_ext_set_param - config monitor parameters + * @wiphy: wiphy handle + * @wdev: wdev handle + * @data: user layer input + * @data_len: length of user layer input + * + * return: 0 success, einval failure + */ +int wlan_hdd_cfg80211_ll_stats_ext_set_param(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); +/** + * hdd_get_interface_info() - get interface info + * @adapter: Pointer to device adapter + * @info: Pointer to interface info + * + * Return: bool + */ +bool hdd_get_interface_info(struct hdd_adapter *adapter, + struct wifi_interface_info *info); + +/** + * wlan_hdd_ll_stats_get() - Get Link Layer statistics from FW + * @adapter: Pointer to device adapter + * @req_id: request id + * @req_mask: bitmask used by FW for the request + * + * Return: 0 on success and error code otherwise + */ +int wlan_hdd_ll_stats_get(struct hdd_adapter *adapter, uint32_t req_id, + uint32_t req_mask); + +/** + * wlan_hdd_cfg80211_link_layer_stats_callback() - This function is called + * @hdd_handle: Handle to HDD context + * @indication_type: Indication type + * @results: Pointer to results + * @cookie: Callback context + * + * After receiving Link Layer indications from FW.This callback converts the + * firmware data to the NL data and send the same to the kernel/upper layers. + * + * Return: None + */ +void wlan_hdd_cfg80211_link_layer_stats_callback(hdd_handle_t hdd_handle, + int indication_type, + tSirLLStatsResults *results, + void *cookie); + +/** + * wlan_hdd_cfg80211_link_layer_stats_ext_callback() - Callback for LL ext + * @ctx: HDD context + * @rsp: msg from FW + * + * This function is an extension of + * wlan_hdd_cfg80211_link_layer_stats_callback. It converts + * monitoring parameters offloaded to NL data and send the same to the + * kernel/upper layers. + * + * Return: None. + */ +void wlan_hdd_cfg80211_link_layer_stats_ext_callback(hdd_handle_t ctx, + tSirLLStatsResults *rsp); + +/** + * hdd_lost_link_info_cb() - callback function to get lost link information + * @hdd_handle: Opaque handle for the HDD context + * @lost_link_info: lost link information + * + * Return: none + */ +void hdd_lost_link_info_cb(hdd_handle_t hdd_handle, + struct sir_lost_link_info *lost_link_info); + +#else /* WLAN_FEATURE_LINK_LAYER_STATS */ + +static inline bool hdd_link_layer_stats_supported(void) +{ + return false; +} + +static inline int +wlan_hdd_cfg80211_ll_stats_ext_set_param(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + return -EINVAL; +} + +static inline int +wlan_hdd_ll_stats_get(struct hdd_adapter *adapter, uint32_t req_id, + uint32_t req_mask) +{ + return -EINVAL; +} + +static inline void +wlan_hdd_clear_link_layer_stats(struct hdd_adapter *adapter) +{ +} + +static inline void +wlan_hdd_cfg80211_link_layer_stats_callback(hdd_handle_t hdd_handle, + int indication_type, + tSirLLStatsResults *results, + void *cookie) +{ +} + +static inline void +wlan_hdd_cfg80211_link_layer_stats_ext_callback(hdd_handle_t ctx, + tSirLLStatsResults *rsp) +{ +} + +static inline void +hdd_lost_link_info_cb(hdd_handle_t hdd_handle, + struct sir_lost_link_info *lost_link_info) +{ +} + +#endif /* End of WLAN_FEATURE_LINK_LAYER_STATS */ + +#ifdef WLAN_FEATURE_STATS_EXT +/** + * wlan_hdd_cfg80211_stats_ext_request() - ext stats request + * @wiphy: Pointer to wiphy + * @wdev: Pointer to wdev + * @data: Pointer to data + * @data_len: Data length + * + * Return: int + */ +int wlan_hdd_cfg80211_stats_ext_request(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#endif /* End of WLAN_FEATURE_STATS_EXT */ + +/** + * wlan_hdd_cfg80211_get_station() - get station statistics + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @mac: Pointer to mac + * @sinfo: Pointer to station info + * + * Return: 0 for success, non-zero for failure + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +int wlan_hdd_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, const uint8_t *mac, + struct station_info *sinfo); +#else +int wlan_hdd_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, uint8_t *mac, + struct station_info *sinfo); +#endif + +/** + * wlan_hdd_cfg80211_dump_station() - dump station statistics + * @wiphy: Pointer to wiphy + * @dev: Pointer to network device + * @idx: variable to determine whether to get stats or not + * @mac: Pointer to mac + * @sinfo: Pointer to station info + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, + int idx, u8 *mac, + struct station_info *sinfo); + +struct net_device_stats *hdd_get_stats(struct net_device *dev); + +int wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy, + struct net_device *dev, + int idx, struct survey_info *survey); + +void hdd_display_hif_stats(void); +void hdd_clear_hif_stats(void); + +/** + * wlan_hdd_cfg80211_stats_ext_callback() - ext stats callback + * @hdd_handle: Opaque handle to HDD context + * @data: ext stats payload + * + * Return: nothing + */ +void wlan_hdd_cfg80211_stats_ext_callback(hdd_handle_t hdd_handle, + struct stats_ext_event *data); + +/** + * wlan_hdd_cfg80211_stats_ext2_callback() - stats_ext2_callback + * @hdd_handle: opaque handle to the hdd context + * @pmsg: sir_sme_rx_aggr_hole_ind + * + * Return: void + */ +void +wlan_hdd_cfg80211_stats_ext2_callback(hdd_handle_t hdd_handle, + struct sir_sme_rx_aggr_hole_ind *pmsg); + +/** + * wlan_hdd_get_rcpi() - Wrapper to get current RCPI + * @adapter: adapter upon which the measurement is requested + * @mac: peer addr for which measurement is requested + * @rcpi_value: pointer to where the RCPI should be returned + * @measurement_type: type of rcpi measurement + * + * This is a wrapper function for getting RCPI, invoke this function only + * when rcpi support is enabled in firmware + * + * Return: 0 for success, non-zero for failure + */ +int wlan_hdd_get_rcpi(struct hdd_adapter *adapter, uint8_t *mac, + int32_t *rcpi_value, + enum rcpi_measurement_type measurement_type); + +#ifdef WLAN_FEATURE_MIB_STATS +/** + * wlan_hdd_get_mib_stats() - Get the mib statistics + * @adapter: adapter upon which the measurement is requested + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS wlan_hdd_get_mib_stats(struct hdd_adapter *adapter); +#endif + +/** + * wlan_hdd_get_rssi() - Get the current RSSI + * @adapter: adapter upon which the measurement is requested + * @rssi_value: pointer to where the RSSI should be returned + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS wlan_hdd_get_rssi(struct hdd_adapter *adapter, int8_t *rssi_value); + +/** + * wlan_hdd_get_snr() - Get the current SNR + * @adapter: adapter upon which the measurement is requested + * @snr: pointer to where the SNR should be returned + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS wlan_hdd_get_snr(struct hdd_adapter *adapter, int8_t *snr); + +/** + * wlan_hdd_get_linkspeed_for_peermac() - Get link speed for a peer + * @adapter: adapter upon which the peer is active + * @mac_address: MAC address of the peer + * @linkspeed: pointer to memory where returned link speed is to be placed + * + * This function will send a query to SME for the linkspeed of the + * given peer, and then wait for the callback to be invoked. + * + * Return: 0 if linkspeed data is available, negative errno otherwise + */ +int wlan_hdd_get_linkspeed_for_peermac(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_address, + uint32_t *linkspeed); + +/** + * wlan_hdd_get_link_speed() - get link speed + * @adapter: pointer to the adapter + * @link_speed: pointer to link speed + * + * This function fetches per bssid link speed. + * + * Return: if associated, link speed shall be returned. + * if not associated, link speed of 0 is returned. + * On error, error number will be returned. + */ +int wlan_hdd_get_link_speed(struct hdd_adapter *adapter, uint32_t *link_speed); + +/** + * wlan_hdd_get_peer_info() - get peer info + * @adapter: hostapd interface + * @macaddress: request peer mac address + * @peer_info_ext: one peer extended info retrieved + * + * This function will call sme_get_peer_info_ext to get peer info + * + * Return: 0 on success, otherwise error value + */ +int wlan_hdd_get_peer_info(struct hdd_adapter *adapter, + struct qdf_mac_addr macaddress, + struct sir_peer_info_ext *peer_info_ext); +/** + * wlan_hdd_get_station_stats() - Get station statistics + * @adapter: adapter for which statistics are desired + * + * Return: status of operation + */ +int wlan_hdd_get_station_stats(struct hdd_adapter *adapter); + +/** + * wlan_hdd_get_temperature() - get current device temperature + * @adapter: device upon which the request was made + * @temperature: pointer to where the temperature is to be returned + * + * Return: 0 if a temperature value (either current or cached) was + * returned, otherwise a negative errno is returned. + * + */ +int wlan_hdd_get_temperature(struct hdd_adapter *adapter, int *temperature); + +/** + * wlan_hdd_display_txrx_stats() - display HDD txrx stats summary + * @hdd_ctx: hdd context + * + * Display TXRX Stats for all adapters + * + * Return: none + */ +void wlan_hdd_display_txrx_stats(struct hdd_context *hdd_ctx); + +/** + * hdd_report_max_rate() - Fill the max rate stats in the station info structure + * to be sent to the userspace. + * + * @mac_handle: The mac handle + * @rate: The station_info tx/rx rate to be filled + * @signal: signal from station_info + * @rate_flags: TX/RX rate flags computed from tx/rx rate + * @mcs_index; The TX/RX mcs index computed from tx/rx rate + * @fw_rate: The tx/rx rate from fw stats + * @nss: The TX/RX NSS from fw stats + * + * Return: True if fill is successful + */ +bool hdd_report_max_rate(mac_handle_t mac_handle, + struct rate_info *rate, + int8_t signal, + enum tx_rate_info rate_flags, + uint8_t mcs_index, + uint16_t fw_rate, uint8_t nss); + +/** + * hdd_check_and_update_nss() - Check and update NSS as per DBS capability + * @hdd_ctx: HDD Context pointer + * @tx_nss: pointer to variable storing the tx_nss + * @rx_nss: pointer to variable storing the rx_nss + * + * The parameters include the NSS obtained from the FW or static NSS. This NSS + * could be invalid in the case the current HW mode is DBS where the connection + * are 1x1. Rectify these NSS values as per the current HW mode. + * + * Return: none + */ +void hdd_check_and_update_nss(struct hdd_context *hdd_ctx, + uint8_t *tx_nss, uint8_t *rx_nss); +#ifdef QCA_SUPPORT_CP_STATS +/** + * wlan_hdd_register_cp_stats_cb() - Register hdd stats specific + * callbacks to the cp stats component + * @hdd_ctx: hdd context + * + * Return: none + */ + +void wlan_hdd_register_cp_stats_cb(struct hdd_context *hdd_ctx); +#else +static inline void wlan_hdd_register_cp_stats_cb(struct hdd_context *hdd_ctx) {} +#endif + +/** + * hdd_update_sta_arp_stats() - update arp stats + * @adapter: adapter context + * + * Return: An error code or 0 on success. + */ +QDF_STATUS hdd_update_sta_arp_stats(struct hdd_adapter *adapter); +#endif /* end #if !defined(WLAN_HDD_STATS_H) */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_subnet_detect.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_subnet_detect.c new file mode 100644 index 0000000000000000000000000000000000000000..fb2ae30cfdcb70b53e45f18db5f7b698506fbaa9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_subnet_detect.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_subnet_detect.c + * + * WLAN Host Device Driver subnet detect API implementation + */ + +#include +#include +#include +#include +#include +#include "sme_api.h" +#include "osif_sync.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_subnet_detect.h" +#include + +/* + * define short names for the global vendor params + * used by __wlan_hdd_cfg80211_set_gateway_params() + */ +#define PARAM_MAC_ADDR QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_GW_MAC_ADDR +#define PARAM_IPV4_ADDR QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_IPV4_ADDR +#define PARAM_IPV6_ADDR QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_IPV6_ADDR + +static const struct nla_policy + policy[QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_MAX + 1] = { + [PARAM_MAC_ADDR] = { + .type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE + }, + [PARAM_IPV4_ADDR] = { + .type = NLA_UNSPEC, + .len = QDF_IPV4_ADDR_SIZE + }, + [PARAM_IPV6_ADDR] = { + .type = NLA_UNSPEC, + .len = QDF_IPV6_ADDR_SIZE + } +}; + +/** + * __wlan_hdd_cfg80211_set_gateway_params() - set gateway params + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_set_gateway_params(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_MAX + 1]; + struct gateway_update_req_param req = { 0 }; + int ret; + QDF_STATUS status; + bool subnet_detection_enabled; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + /* user may have disabled the feature in INI */ + ucfg_mlme_is_subnet_detection_enabled(hdd_ctx->psoc, + &subnet_detection_enabled); + if (!subnet_detection_enabled) { + hdd_debug("LFR Subnet Detection disabled in INI"); + return -ENOTSUPP; + } + + /* The gateway parameters are only valid in the STA persona + * and only in the connected state. + */ + if (QDF_STA_MODE != adapter->device_mode) { + hdd_debug("Received GW param update for non-STA mode adapter"); + return -ENOTSUPP; + } + + if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + hdd_debug("Received GW param update in disconnected state!"); + return -ENOTSUPP; + } + + /* Extract NL parameters + * mac_addr: 6 bytes + * ipv4 addr: 4 bytes + * ipv6 addr: 16 bytes + */ + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_GW_PARAM_CONFIG_MAX, + data, data_len, policy)) { + hdd_err("Invalid ATTR list"); + return -EINVAL; + } + + if (!tb[PARAM_MAC_ADDR]) { + hdd_err("request mac addr failed"); + return -EINVAL; + } + nla_memcpy(req.gw_mac_addr.bytes, tb[PARAM_MAC_ADDR], + QDF_MAC_ADDR_SIZE); + + /* req ipv4_addr_type and ipv6_addr_type are initially false due + * to zeroing the struct + */ + if (tb[PARAM_IPV4_ADDR]) { + nla_memcpy(req.ipv4_addr, tb[PARAM_IPV4_ADDR], + QDF_IPV4_ADDR_SIZE); + req.ipv4_addr_type = true; + } + + if (tb[PARAM_IPV6_ADDR]) { + nla_memcpy(&req.ipv6_addr, tb[PARAM_IPV6_ADDR], + QDF_IPV6_ADDR_SIZE); + req.ipv6_addr_type = true; + } + + if (!req.ipv4_addr_type && !req.ipv6_addr_type) { + hdd_err("invalid ipv4 or ipv6 gateway address"); + return -EINVAL; + } + + req.max_retries = 3; + req.timeout = 100; /* in milliseconds */ + req.vdev_id = adapter->vdev_id; + + hdd_debug("Configuring gateway for session %d", req.vdev_id); + hdd_debug("mac:"QDF_MAC_ADDR_FMT", ipv4:%pI4 (type %d), ipv6:%pI6c (type %d)", + QDF_MAC_ADDR_REF(req.gw_mac_addr.bytes), + req.ipv4_addr, req.ipv4_addr_type, + req.ipv6_addr, req.ipv6_addr_type); + + hdd_nud_set_gateway_addr(adapter, req.gw_mac_addr); + + status = sme_gateway_param_update(hdd_ctx->mac_handle, &req); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("sme_gateway_param_update failed(err=%d)", status); + ret = -EINVAL; + } + + hdd_exit(); + return ret; +} + +/** + * wlan_hdd_cfg80211_set_gateway_params() - set gateway parameters + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * The API is invoked by the user space to set the gateway parameters + * such as mac address and the IP address which is used for detecting + * the IP subnet change + * + * Return: 0 on success; errno on failure + */ +int wlan_hdd_cfg80211_set_gateway_params(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_gateway_params(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#undef PARAM_MAC_ADDR +#undef PARAM_IPV4_ADDR +#undef PARAM_IPV6_ADDR diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_subnet_detect.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_subnet_detect.h new file mode 100644 index 0000000000000000000000000000000000000000..2d832009bdc17e510c72fdcbc772cc765c00fbef --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_subnet_detect.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_SUBNET_DETECT_H +#define __WLAN_HDD_SUBNET_DETECT_H + +/** + * DOC: wlan_hdd_subnet_detect.h + * + * WLAN Host Device Driver subnet detect API specification + */ + +#ifdef FEATURE_LFR_SUBNET_DETECTION +struct wiphy; +struct wireless_dev; + +int wlan_hdd_cfg80211_set_gateway_params(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int data_len); +#endif /* FEATURE_LFR_SUBNET_DETECTION */ +#endif /* __WLAN_HDD_SUBNET_DETECT_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sysfs.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sysfs.c new file mode 100644 index 0000000000000000000000000000000000000000..2e1c0739f084e1d4bccdd20334ab10c201aaea4d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_sysfs.c @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_sysfs.c + * + * WLAN Host Device Driver implementation + * + */ + +#include +#include +#include +#include +#include "wlan_hdd_includes.h" +#include "wlan_hdd_sysfs.h" +#include "qwlan_version.h" +#include "cds_api.h" +#include +#include +#ifdef WLAN_POWER_DEBUG +#include +#endif +#include "osif_sync.h" +#if defined(WLAN_SUPPORT_RX_FISA) +#include "dp_fisa_rx.h" +#endif + +#define MAX_PSOC_ID_SIZE 10 + +#ifdef MULTI_IF_NAME +#define DRIVER_NAME MULTI_IF_NAME +#else +#define DRIVER_NAME "wifi" +#endif + +static struct kobject *wlan_kobject; +static struct kobject *driver_kobject; +static struct kobject *fw_kobject; +static struct kobject *psoc_kobject; + +#if defined(WLAN_SUPPORT_RX_FISA) +static inline +void hdd_rx_skip_fisa(ol_txrx_soc_handle dp_soc, uint32_t value) +{ + dp_rx_skip_fisa(dp_soc, value); +} +#else +static inline +void hdd_rx_skip_fisa(ol_txrx_soc_handle dp_soc, uint32_t value) +{ +} +#endif + +static ssize_t __show_driver_version(char *buf) +{ + return scnprintf(buf, PAGE_SIZE, QWLAN_VERSIONSTR); +} + +static ssize_t show_driver_version(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct osif_psoc_sync *psoc_sync; + ssize_t length; + int errno; + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + errno = osif_psoc_sync_op_start(hdd_ctx->parent_dev, &psoc_sync); + if (errno) + return errno; + + length = __show_driver_version(buf); + + osif_psoc_sync_op_stop(psoc_sync); + + return length; +} + +static ssize_t __show_fw_version(struct hdd_context *hdd_ctx, + char *buf) +{ + hdd_debug("Rcvd req for FW version"); + + return scnprintf(buf, PAGE_SIZE, + "FW:%d.%d.%d.%d.%d.%d HW:%s Board version: %x Ref design id: %x Customer id: %x Project id: %x Board Data Rev: %x\n", + hdd_ctx->fw_version_info.major_spid, + hdd_ctx->fw_version_info.minor_spid, + hdd_ctx->fw_version_info.siid, + hdd_ctx->fw_version_info.rel_id, + hdd_ctx->fw_version_info.crmid, + hdd_ctx->fw_version_info.sub_id, + hdd_ctx->target_hw_name, + hdd_ctx->hw_bd_info.bdf_version, + hdd_ctx->hw_bd_info.ref_design_id, + hdd_ctx->hw_bd_info.customer_id, + hdd_ctx->hw_bd_info.project_id, + hdd_ctx->hw_bd_info.board_data_rev); +} + +static ssize_t show_fw_version(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct osif_psoc_sync *psoc_sync; + ssize_t length; + int errno; + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + errno = osif_psoc_sync_op_start(hdd_ctx->parent_dev, &psoc_sync); + if (errno) + return errno; + + length = __show_fw_version(hdd_ctx, buf); + + osif_psoc_sync_op_stop(psoc_sync); + + return length; +}; + +#ifdef WLAN_POWER_DEBUG +struct power_stats_priv { + struct power_stats_response power_stats; +}; + +static void hdd_power_debugstats_dealloc(void *priv) +{ + struct power_stats_priv *stats = priv; + + qdf_mem_free(stats->power_stats.debug_registers); + stats->power_stats.debug_registers = NULL; +} + +static void hdd_power_debugstats_cb(struct power_stats_response *response, + void *context) +{ + struct osif_request *request; + struct power_stats_priv *priv; + uint32_t *debug_registers; + uint32_t debug_registers_len; + + hdd_enter(); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + + /* copy fixed-sized data */ + priv->power_stats = *response; + + /* copy variable-size data */ + if (response->num_debug_register) { + debug_registers_len = (sizeof(response->debug_registers[0]) * + response->num_debug_register); + debug_registers = qdf_mem_malloc(debug_registers_len); + priv->power_stats.debug_registers = debug_registers; + if (debug_registers) { + qdf_mem_copy(debug_registers, + response->debug_registers, + debug_registers_len); + } else { + hdd_err("Power stats memory alloc fails!"); + priv->power_stats.num_debug_register = 0; + } + } + osif_request_complete(request); + osif_request_put(request); + hdd_exit(); +} + +static ssize_t __show_device_power_stats(struct hdd_context *hdd_ctx, + char *buf) +{ + QDF_STATUS status; + struct power_stats_response *chip_power_stats; + ssize_t ret_cnt = 0; + int j; + void *cookie; + struct osif_request *request; + struct power_stats_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + .dealloc = hdd_power_debugstats_dealloc, + }; + + hdd_enter(); + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + status = sme_power_debug_stats_req(hdd_ctx->mac_handle, + hdd_power_debugstats_cb, + cookie); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("chip power stats request failed"); + ret_cnt = qdf_status_to_os_return(status); + goto cleanup; + } + + ret_cnt = osif_request_wait_for_response(request); + if (ret_cnt) { + hdd_err("Target response timed out Power stats"); + sme_reset_power_debug_stats_cb(hdd_ctx->mac_handle); + ret_cnt = -ETIMEDOUT; + goto cleanup; + } + priv = osif_request_priv(request); + chip_power_stats = &priv->power_stats; + + ret_cnt += scnprintf(buf, PAGE_SIZE, + "POWER DEBUG STATS\n=================\n" + "cumulative_sleep_time_ms: %d\n" + "cumulative_total_on_time_ms: %d\n" + "deep_sleep_enter_counter: %d\n" + "last_deep_sleep_enter_tstamp_ms: %d\n" + "debug_register_fmt: %d\n" + "num_debug_register: %d\n", + chip_power_stats->cumulative_sleep_time_ms, + chip_power_stats->cumulative_total_on_time_ms, + chip_power_stats->deep_sleep_enter_counter, + chip_power_stats->last_deep_sleep_enter_tstamp_ms, + chip_power_stats->debug_register_fmt, + chip_power_stats->num_debug_register); + + for (j = 0; j < chip_power_stats->num_debug_register; j++) { + if ((PAGE_SIZE - ret_cnt) > 0) + ret_cnt += scnprintf(buf + ret_cnt, + PAGE_SIZE - ret_cnt, + "debug_registers[%d]: 0x%x\n", j, + chip_power_stats->debug_registers[j]); + else + j = chip_power_stats->num_debug_register; + } + +cleanup: + osif_request_put(request); + hdd_exit(); + return ret_cnt; +} + +static ssize_t show_device_power_stats(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + struct osif_psoc_sync *psoc_sync; + ssize_t length; + int errno; + + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + errno = osif_psoc_sync_op_start(hdd_ctx->parent_dev, &psoc_sync); + if (errno) + return errno; + + length = __show_device_power_stats(hdd_ctx, buf); + + osif_psoc_sync_op_stop(psoc_sync); + + return length; +} +#endif + +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS +struct beacon_reception_stats_priv { + struct bcn_reception_stats_rsp beacon_stats; +}; + +static void hdd_beacon_debugstats_cb(struct bcn_reception_stats_rsp + *response, + void *context) +{ + struct osif_request *request; + struct beacon_reception_stats_priv *priv; + + hdd_enter(); + + request = osif_request_get(context); + if (!request) { + hdd_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + + /* copy fixed-sized data */ + priv->beacon_stats = *response; + + osif_request_complete(request); + osif_request_put(request); + hdd_exit(); +} + +static ssize_t __show_beacon_reception_stats(struct net_device *net_dev, + char *buf) +{ + struct hdd_adapter *adapter = netdev_priv(net_dev); + struct bcn_reception_stats_rsp *beacon_stats; + int ret_val, j; + void *cookie; + struct osif_request *request; + struct beacon_reception_stats_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_STATS, + }; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + QDF_STATUS status; + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) { + hdd_err("hdd ctx is invalid"); + return ret_val; + } + + if (!adapter || adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { + hdd_err("Invalid adapter or adapter has invalid magic"); + return -EINVAL; + } + + if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) { + hdd_err("Interface is not enabled"); + return -EINVAL; + } + + if (!(adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE)) { + hdd_err("Beacon Reception Stats only supported in STA or P2P CLI modes!"); + return -ENOTSUPP; + } + + if (!hdd_adapter_is_connected_sta(adapter)) { + hdd_err("Adapter is not in connected state"); + return -EINVAL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + status = sme_beacon_debug_stats_req(hdd_ctx->mac_handle, + adapter->vdev_id, + hdd_beacon_debugstats_cb, + cookie); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("chip power stats request failed"); + ret_val = -EINVAL; + goto cleanup; + } + + ret_val = osif_request_wait_for_response(request); + if (ret_val) { + hdd_err("Target response timed out Power stats"); + ret_val = -ETIMEDOUT; + goto cleanup; + } + priv = osif_request_priv(request); + beacon_stats = &priv->beacon_stats; + + ret_val += scnprintf(buf, PAGE_SIZE, + "BEACON RECEPTION STATS\n=================\n" + "vdev id: %u\n" + "Total Beacon Count: %u\n" + "Total Beacon Miss Count: %u\n", + beacon_stats->vdev_id, + beacon_stats->total_bcn_cnt, + beacon_stats->total_bmiss_cnt); + + ret_val += scnprintf(buf + ret_val, PAGE_SIZE - ret_val, + "Beacon Miss Bit map "); + + for (j = 0; j < MAX_BCNMISS_BITMAP; j++) { + if ((PAGE_SIZE - ret_val) > 0) { + ret_val += scnprintf(buf + ret_val, + PAGE_SIZE - ret_val, + "[0x%x] ", + beacon_stats->bmiss_bitmap[j]); + } + } + + if ((PAGE_SIZE - ret_val) > 0) + ret_val += scnprintf(buf + ret_val, + PAGE_SIZE - ret_val, + "\n"); +cleanup: + osif_request_put(request); + hdd_exit(); + return ret_val; +} + +static ssize_t show_beacon_reception_stats(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct net_device *net_dev = container_of(dev, struct net_device, dev); + struct osif_vdev_sync *vdev_sync; + ssize_t err_size; + + err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (err_size) + return err_size; + + err_size = __show_beacon_reception_stats(net_dev, buf); + + osif_vdev_sync_op_stop(vdev_sync); + + return err_size; +} + +static DEVICE_ATTR(beacon_stats, 0444, + show_beacon_reception_stats, NULL); +#endif + +static struct kobj_attribute dr_ver_attribute = + __ATTR(driver_version, 0440, show_driver_version, NULL); +static struct kobj_attribute fw_ver_attribute = + __ATTR(version, 0440, show_fw_version, NULL); +#ifdef WLAN_POWER_DEBUG +static struct kobj_attribute power_stats_attribute = + __ATTR(power_stats, 0444, show_device_power_stats, NULL); +#endif + +void hdd_sysfs_create_version_interface(struct wlan_objmgr_psoc *psoc) +{ + int error = 0; + uint32_t psoc_id; + char buf[MAX_PSOC_ID_SIZE]; + + if (!driver_kobject || !wlan_kobject) { + hdd_err("could not get driver kobject!"); + return; + } + + error = sysfs_create_file(wlan_kobject, &dr_ver_attribute.attr); + if (error) { + hdd_err("could not create wlan sysfs file"); + return; + } + + fw_kobject = kobject_create_and_add("fw", wlan_kobject); + if (!fw_kobject) { + hdd_err("could not allocate fw kobject"); + goto free_fw_kobj; + } + + psoc_id = wlan_psoc_get_nif_phy_version(psoc); + scnprintf(buf, PAGE_SIZE, "%d", psoc_id); + + psoc_kobject = kobject_create_and_add(buf, fw_kobject); + if (!psoc_kobject) { + hdd_err("could not allocate psoc kobject"); + goto free_fw_kobj; + } + + error = sysfs_create_file(psoc_kobject, &fw_ver_attribute.attr); + if (error) { + hdd_err("could not create fw sysfs file"); + goto free_psoc_kobj; + } + + return; + +free_psoc_kobj: + kobject_put(psoc_kobject); + psoc_kobject = NULL; + +free_fw_kobj: + kobject_put(fw_kobject); + fw_kobject = NULL; +} + +void hdd_sysfs_destroy_version_interface(void) +{ + if (psoc_kobject) { + kobject_put(psoc_kobject); + psoc_kobject = NULL; + kobject_put(fw_kobject); + fw_kobject = NULL; + } +} + +int +hdd_sysfs_validate_and_copy_buf(char *dest_buf, size_t dest_buf_size, + char const *source_buf, size_t source_buf_size) +{ + if (source_buf_size > (dest_buf_size - 1)) { + hdd_err_rl("Command length is larger than %zu bytes", + dest_buf_size); + return -EINVAL; + } + + /* sysfs already provides kernel space buffer so copy from user + * is not needed. Doing this extra copy operation just to ensure + * the local buf is properly null-terminated. + */ + strlcpy(dest_buf, source_buf, dest_buf_size); + /* default 'echo' cmd takes new line character to here */ + if (dest_buf[source_buf_size - 1] == '\n') + dest_buf[source_buf_size - 1] = '\0'; + + return 0; +} + +static ssize_t +__hdd_sysfs_dp_aggregation_show(struct hdd_context *hdd_ctx, + struct kobj_attribute *attr, char *buf) +{ + if (!wlan_hdd_validate_modules_state(hdd_ctx)) + return -EINVAL; + + hdd_debug("dp_aggregation: %d", + qdf_atomic_read(&hdd_ctx->dp_agg_param.rx_aggregation)); + + return 0; +} + +static ssize_t hdd_sysfs_dp_aggregation_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct osif_psoc_sync *psoc_sync; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + ssize_t errno_size; + int ret; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret != 0) + return ret; + + errno_size = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), + &psoc_sync); + if (errno_size) + return errno_size; + + errno_size = __hdd_sysfs_dp_aggregation_show(hdd_ctx, attr, buf); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno_size; +} + +static ssize_t +__hdd_sysfs_dp_aggregation_store(struct hdd_context *hdd_ctx, + struct kobj_attribute *attr, const char *buf, + size_t count) +{ + char buf_local[MAX_SYSFS_USER_COMMAND_SIZE_LENGTH + 1]; + char *sptr, *token; + uint32_t value; + int ret; + ol_txrx_soc_handle dp_soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!wlan_hdd_validate_modules_state(hdd_ctx) || !dp_soc) + return -EINVAL; + + ret = hdd_sysfs_validate_and_copy_buf(buf_local, sizeof(buf_local), + buf, count); + + if (ret) { + hdd_err_rl("invalid input"); + return ret; + } + + sptr = buf_local; + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &value)) + return -EINVAL; + + hdd_debug("dp_aggregation: %d", value); + + hdd_rx_skip_fisa(dp_soc, value); + qdf_atomic_set(&hdd_ctx->dp_agg_param.rx_aggregation, !!value); + + return count; +} + +static ssize_t +hdd_sysfs_dp_aggregation_store(struct kobject *kobj, + struct kobj_attribute *attr, + char const *buf, size_t count) +{ + struct osif_psoc_sync *psoc_sync; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + ssize_t errno_size; + int ret; + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret != 0) + return ret; + + errno_size = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), + &psoc_sync); + if (errno_size) + return errno_size; + + errno_size = __hdd_sysfs_dp_aggregation_store(hdd_ctx, attr, + buf, count); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno_size; +} + +static struct kobj_attribute dp_aggregation_attribute = + __ATTR(dp_aggregation, 0664, hdd_sysfs_dp_aggregation_show, + hdd_sysfs_dp_aggregation_store); + +int hdd_sysfs_dp_aggregation_create(void) +{ + int error; + + if (!driver_kobject) { + hdd_err("could not get driver kobject!"); + return -EINVAL; + } + + error = sysfs_create_file(driver_kobject, + &dp_aggregation_attribute.attr); + if (error) + hdd_err("could not create dp_aggregation sysfs file"); + + return error; +} + +void +hdd_sysfs_dp_aggregation_destroy(void) +{ + if (!driver_kobject) { + hdd_err("could not get driver kobject!"); + return; + } + + sysfs_remove_file(driver_kobject, &dp_aggregation_attribute.attr); +} + +#ifdef WLAN_POWER_DEBUG +void hdd_sysfs_create_powerstats_interface(void) +{ + int error; + + if (!driver_kobject) { + hdd_err("could not get driver kobject!"); + return; + } + + error = sysfs_create_file(driver_kobject, &power_stats_attribute.attr); + if (error) + hdd_err("could not create power_stats sysfs file"); +} + +void hdd_sysfs_destroy_powerstats_interface(void) +{ + if (!driver_kobject) { + hdd_err("could not get driver kobject!"); + return; + } + sysfs_remove_file(driver_kobject, &power_stats_attribute.attr); +} + +void hdd_sysfs_create_driver_root_obj(void) +{ + driver_kobject = kobject_create_and_add(DRIVER_NAME, kernel_kobj); + if (!driver_kobject) { + hdd_err("could not allocate driver kobject"); + return; + } + + wlan_kobject = kobject_create_and_add("wlan", driver_kobject); + if (!wlan_kobject) { + hdd_err("could not allocate wlan kobject"); + kobject_put(driver_kobject); + driver_kobject = NULL; + } +} + +void hdd_sysfs_destroy_driver_root_obj(void) +{ + if (wlan_kobject) { + kobject_put(wlan_kobject); + wlan_kobject = NULL; + } + + if (driver_kobject) { + kobject_put(driver_kobject); + driver_kobject = NULL; + } +} +#endif + +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS +static int hdd_sysfs_create_bcn_reception_interface(struct hdd_adapter + *adapter) +{ + int error; + + error = device_create_file(&adapter->dev->dev, &dev_attr_beacon_stats); + if (error) + hdd_err("could not create beacon stats sysfs file"); + + return error; +} + +void hdd_sysfs_create_adapter_root_obj(struct hdd_adapter *adapter) +{ + hdd_sysfs_create_bcn_reception_interface(adapter); +} + +static void hdd_sysfs_destroy_bcn_reception_interface(struct hdd_adapter + *adapter) +{ + device_remove_file(&adapter->dev->dev, &dev_attr_beacon_stats); +} + +void hdd_sysfs_destroy_adapter_root_obj(struct hdd_adapter *adapter) +{ + hdd_sysfs_destroy_bcn_reception_interface(adapter); +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tdls.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tdls.c new file mode 100644 index 0000000000000000000000000000000000000000..f4341236e486fd8628479c4cdcac10190b776874 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tdls.c @@ -0,0 +1,939 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_tdls.c + * + * WLAN Host Device Driver implementation for TDLS + */ + +#include +#include +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_hdd_tdls.h" +#include "wlan_hdd_cfg80211.h" +#include "wlan_hdd_assoc.h" +#include "sme_api.h" +#include "cds_sched.h" +#include "wma_types.h" +#include "wlan_policy_mgr_api.h" +#include +#include "wlan_tdls_cfg_api.h" +#include "wlan_hdd_object_manager.h" +#include + +/** + * enum qca_wlan_vendor_tdls_trigger_mode_hdd_map: Maps the user space TDLS + * trigger mode in the host driver. + * @WLAN_HDD_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: TDLS Connection and + * disconnection handled by user space. + * @WLAN_HDD_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: TDLS connection and + * disconnection controlled by host driver based on data traffic. + * @WLAN_HDD_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: TDLS connection and + * disconnection jointly controlled by user space and host driver. + */ +enum qca_wlan_vendor_tdls_trigger_mode_hdd_map { + WLAN_HDD_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT, + WLAN_HDD_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT, + WLAN_HDD_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = + ((QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT | + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT) << 1), +}; + +/** + * wlan_hdd_tdls_get_all_peers() - dump all TDLS peer info into output string + * @adapter: HDD adapter + * @buf: output string buffer to hold the peer info + * @buflen: the size of output string buffer + * + * Return: The size (in bytes) of the valid peer info in the output buffer + */ +int wlan_hdd_tdls_get_all_peers(struct hdd_adapter *adapter, + char *buf, int buflen) +{ + int len; + struct hdd_context *hdd_ctx; + struct wlan_objmgr_vdev *vdev; + int ret; + + hdd_enter(); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (0 != (wlan_hdd_validate_context(hdd_ctx))) { + len = scnprintf(buf, buflen, + "\nHDD context is not valid\n"); + return len; + } + + if ((QDF_STA_MODE != adapter->device_mode) && + (QDF_P2P_CLIENT_MODE != adapter->device_mode)) { + len = scnprintf(buf, buflen, + "\nNo TDLS support for this adapter\n"); + return len; + } + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) { + len = scnprintf(buf, buflen, "\nVDEV is NULL\n"); + return len; + } + ret = wlan_cfg80211_tdls_get_all_peers(vdev, buf, buflen); + hdd_objmgr_put_vdev(vdev); + + return ret; +} + +static const struct nla_policy + wlan_hdd_tdls_config_enable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX + + 1] = { + [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR] = { + .type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS] = {.type = + NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS] = {.type = + NLA_U32}, +}; +static const struct nla_policy + wlan_hdd_tdls_config_disable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX + + 1] = { + [QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR] = { + .type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE}, +}; +static const struct nla_policy + wlan_hdd_tdls_config_state_change_policy[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX + + 1] = { + [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR] = { + .type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON] = {.type = NLA_S32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS] = {.type = + NLA_U32}, +}; +static const struct nla_policy + wlan_hdd_tdls_config_get_status_policy +[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR] = { + .type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON] = {.type = NLA_S32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS] = { + .type = NLA_U32}, +}; + +static const struct nla_policy + wlan_hdd_tdls_mode_configuration_policy + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD] = { + .type = NLA_S32}, + [QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD] = { + .type = NLA_S32}, +}; + +/** + * __wlan_hdd_cfg80211_exttdls_get_status() - handle get status cfg80211 command + * @wiphy: wiphy + * @wdev: wireless dev + * @data: netlink buffer with the mac address of the peer to get the status for + * @data_len: length of data in bytes + */ +static int +__wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + /* TODO */ + return 0; +} + +/** + * __wlan_hdd_cfg80211_configure_tdls_mode() - configure the tdls mode + * @wiphy: wiphy + * @wdev: wireless dev + * @data: netlink buffer + * @data_len: length of data in bytes + * + * Return 0 for success and error code for failure + */ +static int +__wlan_hdd_cfg80211_configure_tdls_mode(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX + 1]; + int ret; + uint32_t trigger_mode; + + hdd_enter_dev(dev); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return -EINVAL; + + if (!adapter) + return -EINVAL; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX, + data, data_len, + wlan_hdd_tdls_mode_configuration_policy)) { + hdd_err("Invalid attribute"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE]) { + hdd_err("attr tdls trigger mode failed"); + return -EINVAL; + } + trigger_mode = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE]); + hdd_debug("TDLS trigger mode %d", trigger_mode); + + if (hdd_ctx->tdls_umac_comp_active) { + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + ret = wlan_cfg80211_tdls_configure_mode(vdev, + trigger_mode); + hdd_objmgr_put_vdev(vdev); + return ret; + } + + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_configure_tdls_mode() - configure tdls mode + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Return: Return the Success or Failure code. + */ +int wlan_hdd_cfg80211_configure_tdls_mode(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_configure_tdls_mode(wiphy, wdev, data, + data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_cfg80211_exttdls_get_status() - get ext tdls status + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Return: Return the Success or Failure code. + */ +int wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_exttdls_get_status(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_exttdls_enable() - enable an externally controllable + * TDLS peer and set parameters + * wiphy: wiphy + * @wdev: wireless dev pointer + * @data: netlink buffer with peer MAC address and configuration parameters + * @data_len: size of data in bytes + * + * This function sets channel, operation class, maximum latency and minimal + * bandwidth parameters on a TDLS peer that's externally controllable. + * + * Return: 0 for success; negative errno otherwise + */ +static int +__wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + /* TODO */ + return 0; +} + +/** + * wlan_hdd_cfg80211_exttdls_enable() - enable ext tdls + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Return: Return the Success or Failure code. + */ +int wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_exttdls_enable(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_exttdls_disable() - disable an externally controllable + * TDLS peer + * wiphy: wiphy + * @wdev: wireless dev pointer + * @data: netlink buffer with peer MAC address + * @data_len: size of data in bytes + * + * This function disables an externally controllable TDLS peer + * + * Return: 0 for success; negative errno otherwise + */ +static int __wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + /* TODO */ + return 0; +} + +/** + * wlan_hdd_cfg80211_exttdls_disable() - disable ext tdls + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the data to be passed via vendor interface + * @data_len:Length of the data to be passed + * + * Return: Return the Success or Failure code. + */ +int wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_exttdls_disable(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#if TDLS_MGMT_VERSION2 +/** + * __wlan_hdd_cfg80211_tdls_mgmt() - handle management actions on a given peer + * @wiphy: wiphy + * @dev: net device + * @peer: MAC address of the TDLS peer + * @action_code: action code + * @dialog_token: dialog token + * @status_code: status code + * @peer_capability: peer capability + * @buf: additional IE to include + * @len: length of buf in bytes + * + * Return: 0 if success; negative errno otherwise + */ +static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + const u8 *buf, size_t len) +#else +/** + * __wlan_hdd_cfg80211_tdls_mgmt() - handle management actions on a given peer + * @wiphy: wiphy + * @dev: net device + * @peer: MAC address of the TDLS peer + * @action_code: action code + * @dialog_token: dialog token + * @status_code: status code + * @buf: additional IE to include + * @len: length of buf in bytes + * + * Return: 0 if success; negative errno otherwise + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) +static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, const uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + bool initiator, const uint8_t *buf, + size_t len) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, const uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + const uint8_t *buf, size_t len) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) +static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + const uint8_t *buf, size_t len) +#else +static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, const uint8_t *buf, + size_t len) +#endif +#endif +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + bool tdls_support; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)) +#if !(TDLS_MGMT_VERSION2) + u32 peer_capability; + + peer_capability = 0; +#endif +#endif + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_TDLS_MGMT, + adapter->vdev_id, action_code); + + if (wlan_hdd_validate_context(hdd_ctx)) + return -EINVAL; + + cfg_tdls_get_support_enable(hdd_ctx->psoc, &tdls_support); + if (!tdls_support) { + hdd_debug("TDLS Disabled in INI OR not enabled in FW. " + "Cannot process TDLS commands"); + return -ENOTSUPP; + } + + if (hdd_ctx->tdls_umac_comp_active) { + struct wlan_objmgr_vdev *vdev; + int ret; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + ret = wlan_cfg80211_tdls_mgmt(vdev, peer, + action_code, dialog_token, + status_code, peer_capability, + buf, len); + hdd_objmgr_put_vdev(vdev); + return ret; + } + + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_tdls_mgmt() - cfg80211 tdls mgmt handler function + * @wiphy: Pointer to wiphy structure. + * @dev: Pointer to net_device structure. + * @peer: peer address + * @action_code: action code + * @dialog_token: dialog token + * @status_code: status code + * @peer_capability: peer capability + * @buf: buffer + * @len: Length of @buf + * + * This is the cfg80211 tdls mgmt handler function which invokes + * the internal function @__wlan_hdd_cfg80211_tdls_mgmt with + * SSR protection. + * + * Return: 0 for success, error number on failure. + */ +#if TDLS_MGMT_VERSION2 +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, + u8 *peer, u8 action_code, + u8 dialog_token, + u16 status_code, u32 peer_capability, + const u8 *buf, size_t len) +#else /* TDLS_MGMT_VERSION2 */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS) +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, u8 action_code, + u8 dialog_token, u16 status_code, + u32 peer_capability, bool initiator, + const u8 *buf, size_t len) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, u8 action_code, + u8 dialog_token, u16 status_code, + u32 peer_capability, const u8 *buf, + size_t len) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, + u8 *peer, u8 action_code, + u8 dialog_token, + u16 status_code, u32 peer_capability, + const u8 *buf, size_t len) +#else +int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, + struct net_device *dev, + u8 *peer, u8 action_code, + u8 dialog_token, + u16 status_code, const u8 *buf, + size_t len) +#endif +#endif +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + +#if TDLS_MGMT_VERSION2 + errno = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code, + dialog_token, status_code, + peer_capability, buf, len); +#else /* TDLS_MGMT_VERSION2 */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS) + errno = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code, + dialog_token, status_code, + peer_capability, initiator, + buf, len); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) + errno = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code, + dialog_token, status_code, + peer_capability, buf, len); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) + errno = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code, + dialog_token, status_code, + peer_capability, buf, len); +#else + errno = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code, + dialog_token, status_code, + buf, len); +#endif +#endif + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __wlan_hdd_cfg80211_tdls_oper() - helper function to handle cfg80211 operation + * on an TDLS peer + * @wiphy: wiphy + * @dev: net device + * @peer: MAC address of the TDLS peer + * @oper: cfg80211 TDLS operation + * + * Return: 0 on success; negative errno otherwise + */ +static int __wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *peer, + enum nl80211_tdls_operation oper) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int status; + bool tdls_support; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EINVAL; + } + + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + cfg_tdls_get_support_enable(hdd_ctx->psoc, &tdls_support); + if (!tdls_support) { + hdd_debug("TDLS Disabled in INI OR not enabled in FW. " + "Cannot process TDLS commands"); + return -ENOTSUPP; + } + + qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, + TRACE_CODE_HDD_CFG80211_TDLS_OPER, + adapter->vdev_id, oper); + + if (!peer) { + hdd_err("Invalid arguments"); + return -EINVAL; + } + + status = wlan_hdd_validate_context(hdd_ctx); + + if (0 != status) + return status; + + if (hdd_ctx->tdls_umac_comp_active) { + struct wlan_objmgr_vdev *vdev; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + status = wlan_cfg80211_tdls_oper(vdev, peer, oper); + hdd_objmgr_put_vdev(vdev); + hdd_exit(); + return status; + } + + hdd_exit(); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_tdls_oper() - handle cfg80211 operation on an TDLS peer + * @wiphy: wiphy + * @dev: net device + * @peer: MAC address of the TDLS peer + * @oper: cfg80211 TDLS operation + * + * Return: 0 on success; negative errno otherwise + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) +int wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy, + struct net_device *dev, + const uint8_t *peer, + enum nl80211_tdls_operation oper) +#else +int wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy, + struct net_device *dev, + uint8_t *peer, + enum nl80211_tdls_operation oper) +#endif +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_tdls_oper(wiphy, dev, peer, oper); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +int hdd_set_tdls_offchannel(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + int offchannel) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (hdd_ctx->tdls_umac_comp_active) { + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + status = ucfg_set_tdls_offchannel(vdev, + offchannel); + hdd_objmgr_put_vdev(vdev); + } + } + return qdf_status_to_os_return(status); +} + +int hdd_set_tdls_secoffchanneloffset(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + int offchanoffset) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (hdd_ctx->tdls_umac_comp_active) { + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + status = ucfg_set_tdls_secoffchanneloffset(vdev, + offchanoffset); + hdd_objmgr_put_vdev(vdev); + } + } + return qdf_status_to_os_return(status); +} + +int hdd_set_tdls_offchannelmode(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + int offchanmode) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + bool tdls_off_ch; + + if (cfg_tdls_get_off_channel_enable( + hdd_ctx->psoc, &tdls_off_ch) != + QDF_STATUS_SUCCESS) { + hdd_err("cfg get tdls off ch failed"); + return qdf_status_to_os_return(status); + } + if (!tdls_off_ch) { + hdd_debug("tdls off ch is false, do nothing"); + return qdf_status_to_os_return(status); + } + + if (hdd_ctx->tdls_umac_comp_active) { + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + status = ucfg_set_tdls_offchan_mode(vdev, + offchanmode); + hdd_objmgr_put_vdev(vdev); + } + } + return qdf_status_to_os_return(status); +} + +/** + * hdd_set_tdls_scan_type - set scan during active tdls session + * @hdd_ctx: ptr to hdd context. + * @val: scan type value: 0 or 1. + * + * Set scan type during tdls session. If set to 1, that means driver + * shall maintain tdls link and allow scan regardless if tdls peer is + * buffer sta capable or not and/or if device is sleep sta capable or + * not. If tdls peer is not buffer sta capable then during scan there + * will be loss of Rx packets and Tx would stop when device moves away + * from tdls channel. If set to 0, then driver shall teardown tdls link + * before initiating scan if peer is not buffer sta capable and device + * is not sleep sta capable. By default, scan type is set to 0. + * + * Return: success (0) or failure (errno value) + */ +int hdd_set_tdls_scan_type(struct hdd_context *hdd_ctx, int val) +{ + if ((val != 0) && (val != 1)) { + hdd_err("Incorrect value of tdls scan type: %d", val); + return -EINVAL; + } + + cfg_tdls_set_scan_enable(hdd_ctx->psoc, (bool)val); + + return 0; +} + +/** + * wlan_hdd_tdls_antenna_switch() - Dynamic TDLS antenna switch 1x1 <-> 2x2 + * antenna mode in standalone station + * @hdd_ctx: Pointer to hdd contex + * @adapter: Pointer to hdd adapter + * + * Return: 0 if success else non zero + */ +int wlan_hdd_tdls_antenna_switch(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + uint32_t mode) +{ + if (hdd_ctx->tdls_umac_comp_active) { + struct wlan_objmgr_vdev *vdev; + int ret; + + vdev = hdd_objmgr_get_vdev(adapter); + if (!vdev) + return -EINVAL; + ret = wlan_tdls_antenna_switch(vdev, mode); + hdd_objmgr_put_vdev(vdev); + return ret; + } + + return 0; +} + +QDF_STATUS hdd_tdls_register_peer(void *userdata, uint32_t vdev_id, + const uint8_t *mac, uint8_t qos) +{ + struct hdd_adapter *adapter; + struct hdd_context *hddctx; + + hddctx = userdata; + if (!hddctx) { + hdd_err("Invalid hddctx"); + return QDF_STATUS_E_INVAL; + } + adapter = hdd_get_adapter_by_vdev(hddctx, vdev_id); + if (!adapter) { + hdd_err("Invalid adapter"); + return QDF_STATUS_E_FAILURE; + } + + return hdd_roam_register_tdlssta(adapter, mac, qos); +} + +void hdd_init_tdls_config(struct tdls_start_params *tdls_cfg) +{ + tdls_cfg->tdls_send_mgmt_req = eWNI_SME_TDLS_SEND_MGMT_REQ; + tdls_cfg->tdls_add_sta_req = eWNI_SME_TDLS_ADD_STA_REQ; + tdls_cfg->tdls_del_sta_req = eWNI_SME_TDLS_DEL_STA_REQ; + tdls_cfg->tdls_update_peer_state = WMA_UPDATE_TDLS_PEER_STATE; +} + +void hdd_config_tdls_with_band_switch(struct hdd_context *hdd_ctx) +{ + struct wlan_objmgr_vdev *tdls_obj_vdev; + int offchmode; + uint32_t current_band; + bool tdls_off_ch; + + if (!hdd_ctx) { + hdd_err("Invalid hdd_ctx"); + return; + } + + if (ucfg_reg_get_band(hdd_ctx->pdev, ¤t_band) != + QDF_STATUS_SUCCESS) { + hdd_err("Failed to get current band config"); + return; + } + + /** + * If all bands are supported, in below condition off channel enable + * orig is false and nothing is need to do + * 1. band switch does not happen. + * 2. band switch happens and it already restores + * 3. tdls off channel is disabled by default. + * If 2g or 5g is not supported. Disable tdls off channel only when + * tdls off channel is enabled currently. + */ + if ((current_band & BIT(REG_BAND_2G)) && + (current_band & BIT(REG_BAND_5G))) { + if (cfg_tdls_get_off_channel_enable_orig( + hdd_ctx->psoc, &tdls_off_ch) != + QDF_STATUS_SUCCESS) { + hdd_err("cfg get tdls off ch orig failed"); + return; + } + if (!tdls_off_ch) { + hdd_debug("tdls off ch orig is false, do nothing"); + return; + } + offchmode = ENABLE_CHANSWITCH; + cfg_tdls_restore_off_channel_enable(hdd_ctx->psoc); + } else { + if (cfg_tdls_get_off_channel_enable( + hdd_ctx->psoc, &tdls_off_ch) != + QDF_STATUS_SUCCESS) { + hdd_err("cfg get tdls off ch failed"); + return; + } + if (!tdls_off_ch) { + hdd_debug("tdls off ch is false, do nothing"); + return; + } + offchmode = DISABLE_CHANSWITCH; + cfg_tdls_store_off_channel_enable(hdd_ctx->psoc); + cfg_tdls_set_off_channel_enable(hdd_ctx->psoc, false); + } + tdls_obj_vdev = ucfg_get_tdls_vdev(hdd_ctx->psoc, WLAN_TDLS_NB_ID); + if (tdls_obj_vdev) { + ucfg_set_tdls_offchan_mode(tdls_obj_vdev, offchmode); + wlan_objmgr_vdev_release_ref(tdls_obj_vdev, WLAN_TDLS_NB_ID); + } +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_thermal.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_thermal.c new file mode 100644 index 0000000000000000000000000000000000000000..46cf12f00d23f5a2c27ce2eaaa2f0d9a5b6d24c2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_thermal.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_thermal.c + * + * WLAN Host Device Driver implementation for thermal mitigation handling + */ + +#include +#include +#include "wlan_osif_priv.h" +#include "qdf_trace.h" +#include "wlan_hdd_main.h" +#include "osif_sync.h" +#include +#include +#include "sme_api.h" +#include "wlan_hdd_thermal.h" +#include "wlan_hdd_cfg80211.h" +#include +#include "wlan_fwol_ucfg_api.h" + +static const struct nla_policy + wlan_hdd_thermal_mitigation_policy + [QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL] = { + .type = NLA_U32}, +}; + +/** + * __wlan_hdd_cfg80211_set_thermal_mitigation_policy() - Set the thermal policy + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Length of @data + * + * Return: 0 on success, negative errno on failure + */ +static int +__wlan_hdd_cfg80211_set_thermal_mitigation_policy(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX + 1]; + bool enable = true; + uint32_t dc, dc_off_percent, level, cmd_type; + uint32_t prio = 0, target_temp = 0; + struct wlan_fwol_thermal_temp thermal_temp = {0}; + QDF_STATUS status; + + hdd_enter(); + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err_rl("Command not allowed in FTM mode"); + return -EPERM; + } + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX, + (struct nlattr *)data, data_len, + wlan_hdd_thermal_mitigation_policy)) { + hdd_err_rl("Invalid attribute"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE]) { + hdd_err_rl("attr thermal cmd value failed"); + return -EINVAL; + } + + cmd_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE]); + if (cmd_type != QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL) { + hdd_err_rl("invalid thermal cmd value"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL]) { + hdd_err_rl("attr thermal throttle set failed"); + return -EINVAL; + } + level = + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL]); + + hdd_debug("thermal mitigation level %d", level); + + status = ucfg_fwol_get_thermal_temp(hdd_ctx->psoc, &thermal_temp); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err_rl("Failed to get fwol thermal obj"); + return qdf_status_to_os_return(status); + } + + switch (level) { + case QCA_WLAN_VENDOR_THERMAL_LEVEL_EMERGENCY: + dc_off_percent = thermal_temp.throttle_dutycycle_level[5]; + break; + case QCA_WLAN_VENDOR_THERMAL_LEVEL_CRITICAL: + dc_off_percent = thermal_temp.throttle_dutycycle_level[4]; + break; + case QCA_WLAN_VENDOR_THERMAL_LEVEL_SEVERE: + dc_off_percent = thermal_temp.throttle_dutycycle_level[3]; + break; + case QCA_WLAN_VENDOR_THERMAL_LEVEL_MODERATE: + dc_off_percent = thermal_temp.throttle_dutycycle_level[2]; + break; + case QCA_WLAN_VENDOR_THERMAL_LEVEL_LIGHT: + dc_off_percent = thermal_temp.throttle_dutycycle_level[1]; + break; + case QCA_WLAN_VENDOR_THERMAL_LEVEL_NONE: + enable = false; + dc_off_percent = thermal_temp.throttle_dutycycle_level[0]; + break; + default: + hdd_debug("Invalid thermal state"); + return -EINVAL; + } + + dc = thermal_temp.thermal_sampling_time; + hdd_debug("dc %d dc_off_per %d", dc, dc_off_percent); + + status = sme_set_thermal_throttle_cfg(hdd_ctx->mac_handle, + enable, + dc, + dc_off_percent, + prio, + target_temp); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err_rl("Failed to set throttle configuration %d", status); + else { + /* + * After SSR, the thermal mitigation level is lost. + * As SSR is hidden from userland, this command will not come + * from userspace after a SSR. To restore this configuration, + * save this in hdd context and restore after re-init. + */ + hdd_ctx->dutycycle_off_percent = dc_off_percent; + } + + return qdf_status_to_os_return(status); +} + +/** + * wlan_hdd_cfg80211_set_thermal_mitigation_policy() - set thermal + * mitigation policy + * @wiphy: wiphy pointer + * @wdev: pointer to struct wireless_dev + * @data: pointer to incoming NL vendor data + * @data_len: length of @data + * + * Return: 0 on success; error number otherwise. + */ +int +wlan_hdd_cfg80211_set_thermal_mitigation_policy(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_set_thermal_mitigation_policy(wiphy, wdev, + data, + data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +} + +bool wlan_hdd_thermal_config_support(void) +{ + return true; +} + +QDF_STATUS hdd_restore_thermal_mitigation_config(struct hdd_context *hdd_ctx) +{ + bool enable = true; + uint32_t dc, dc_off_percent = 0; + uint32_t prio = 0, target_temp = 0; + struct wlan_fwol_thermal_temp thermal_temp = {0}; + QDF_STATUS status; + + status = ucfg_fwol_get_thermal_temp(hdd_ctx->psoc, &thermal_temp); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err_rl("Failed to get fwol thermal obj"); + return status; + } + + dc_off_percent = hdd_ctx->dutycycle_off_percent; + dc = thermal_temp.thermal_sampling_time; + + if(!dc_off_percent) + enable = false; + + hdd_debug("dc %d dc_off_per %d enable %d", dc, dc_off_percent, enable); + + status = sme_set_thermal_throttle_cfg(hdd_ctx->mac_handle, + enable, + dc, + dc_off_percent, + prio, + target_temp); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err_rl("Failed to set throttle configuration %d", status); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_thermal.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_thermal.h new file mode 100644 index 0000000000000000000000000000000000000000..304a43d590ad3a9fa6fb5af0d54cec5111cea3c1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_thermal.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __HDD_THERMAL_H +#define __HDD_THERMAL_H +/** + * DOC: wlan_hdd_thermal.h + * WLAN Host Device Driver thermal mitigation include file + */ + +#include +#include + +#ifdef FW_THERMAL_THROTTLE_SUPPORT + +int +wlan_hdd_cfg80211_set_thermal_mitigation_policy(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + +/** + * wlan_hdd_thermal_config_support() - thermal mitigation support + * + * Return: true if thermal mitigation support enabled otherwise false + */ +bool wlan_hdd_thermal_config_support(void); + +/** + * hdd_restore_thermal_mitigation_config - Restore the saved thermal config + * @hdd_ctx: HDD context + * + * Restore the thermal mitigation config afetr SSR. + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_restore_thermal_mitigation_config(struct hdd_context *hdd_ctx); + +#define FEATURE_THERMAL_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV, \ + .doit = wlan_hdd_cfg80211_set_thermal_mitigation_policy \ +}, +#else +#define FEATURE_THERMAL_VENDOR_COMMANDS + +static inline bool wlan_hdd_thermal_config_support(void) +{ + return false; +} + +static inline +QDF_STATUS hdd_restore_thermal_mitigation_config(struct hdd_context *hdd_ctx) +{ + return false; +} + +#endif /* FEATURE_THERMAL_VENDOR_COMMANDS */ +#endif /* __HDD_THERMAL_H */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_trace.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_trace.c new file mode 100644 index 0000000000000000000000000000000000000000..39b8377d9b831fd3c68615e43d87cdcfd7bf5fc5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_trace.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HDD_TRACE_RECORD + +/** + * DOC: wlan_hdd_trace.c + * + * WLAN Host Device Driver trace implementation + * + */ + +#include "qdf_trace.h" +#include "qdf_types.h" +#include "wlan_hdd_trace.h" +#include "wlan_hdd_main.h" + +/** + * hdd_trace_dump() - Dump an HDD-specific trace record + * @mac: (unused) global MAC handle + * @record: trace record that was previously recorded + * @index: index of the trace record + * + * Return: none + */ +static void +hdd_trace_dump(void *mac, tp_qdf_trace_record record, uint16_t index) +{ + if (TRACE_CODE_HDD_RX_SME_MSG == record->code) + hdd_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", + index, record->qtime, record->time, + record->session, "RX SME MSG:", + get_e_roam_cmd_status_str(record->data), + record->data); + else + hdd_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", + index, record->qtime, record->time, + record->session, "HDD Event:", + hdd_trace_event_string(record->code), + record->data); +} + +/** + * hdd_trace_init() - HDD trace subsystem initialization + * + * Registers HDD with the debug trace subsystem + * + * Return: none + */ +void hdd_trace_init(void) +{ + qdf_trace_register(QDF_MODULE_ID_HDD, hdd_trace_dump); +} + +#endif /* ifdef HDD_TRACE_RECORD */ diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tsf.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tsf.c new file mode 100644 index 0000000000000000000000000000000000000000..d5de120c740ab9754cca7db165afde083601997a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tsf.c @@ -0,0 +1,2400 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * wlan_hdd_tsf.c - WLAN Host Device Driver tsf related implementation + */ + +#include "osif_sync.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_tsf.h" +#include "wma_api.h" +#include "wlan_fwol_ucfg_api.h" +#include +#include +#if defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ) || \ + defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) +#include +#endif + +#include "ol_txrx_api.h" + +#ifdef WLAN_FEATURE_TSF_PLUS +#ifndef WLAN_FEATURE_TSF_PLUS_NOIRQ +#ifndef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC +static int tsf_gpio_irq_num = -1; +#endif +#endif +#endif +static struct completion tsf_sync_get_completion_evt; +#define WLAN_TSF_SYNC_GET_TIMEOUT 2000 +#define WLAN_HDD_CAPTURE_TSF_REQ_TIMEOUT_MS 500 +#define WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS 100 +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC +#define WLAN_HDD_SOFTAP_INTERVAL_TIMES 1 +#else +#define WLAN_HDD_SOFTAP_INTERVAL_TIMES 100 +#endif +#define OUTPUT_HIGH 1 +#define OUTPUT_LOW 0 + +#ifdef WLAN_FEATURE_TSF_PLUS +#ifdef WLAN_FEATURE_TSF_PLUS_NOIRQ +static void hdd_update_timestamp(struct hdd_adapter *adapter); +#else +static void +hdd_update_timestamp(struct hdd_adapter *adapter, + uint64_t target_time, uint64_t host_time); +#endif +#endif + +/** + * enum hdd_tsf_op_result - result of tsf operation + * + * HDD_TSF_OP_SUCC: succeed + * HDD_TSF_OP_FAIL: fail + */ +enum hdd_tsf_op_result { + HDD_TSF_OP_SUCC, + HDD_TSF_OP_FAIL +}; + +#ifdef WLAN_FEATURE_TSF_PLUS +#ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC +#define WLAN_HDD_CAPTURE_TSF_RESYNC_INTERVAL 1 +#else +#define WLAN_HDD_CAPTURE_TSF_RESYNC_INTERVAL 9 +#endif +static inline void hdd_set_th_sync_status(struct hdd_adapter *adapter, + bool initialized) +{ + qdf_atomic_set(&adapter->tsf_sync_ready_flag, + (initialized ? 1 : 0)); +} + +static inline bool hdd_get_th_sync_status(struct hdd_adapter *adapter) +{ + return qdf_atomic_read(&adapter->tsf_sync_ready_flag) != 0; +} + +#else +static inline bool hdd_get_th_sync_status(struct hdd_adapter *adapter) +{ + return true; +} +#endif + +static +enum hdd_tsf_get_state hdd_tsf_check_conn_state(struct hdd_adapter *adapter) +{ + enum hdd_tsf_get_state ret = TSF_RETURN; + struct hdd_station_ctx *hdd_sta_ctx; + + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (hdd_sta_ctx->conn_info.conn_state != + eConnectionState_Associated) { + hdd_err("failed to cap tsf, not connect with ap"); + ret = TSF_STA_NOT_CONNECTED_NO_TSF; + } + } else if ((adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) && + !(test_bit(SOFTAP_BSS_STARTED, + &adapter->event_flags))) { + hdd_err("Soft AP / P2p GO not beaconing"); + ret = TSF_SAP_NOT_STARTED_NO_TSF; + } + return ret; +} + +static bool hdd_tsf_is_initialized(struct hdd_adapter *adapter) +{ + struct hdd_context *hddctx; + + if (!adapter) { + hdd_err("invalid adapter"); + return false; + } + + hddctx = WLAN_HDD_GET_CTX(adapter); + if (!hddctx) { + hdd_err("invalid hdd context"); + return false; + } + + if (!qdf_atomic_read(&hddctx->tsf_ready_flag) || + !hdd_get_th_sync_status(adapter)) { + hdd_err("TSF is not initialized"); + return false; + } + + return true; +} + +#if (defined(WLAN_FEATURE_TSF_PLUS_NOIRQ) && \ + defined(WLAN_FEATURE_TSF_PLUS)) || \ + defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) || \ + defined(WLAN_FEATURE_TSF_TIMER_SYNC) +/** + * hdd_tsf_reset_gpio() - Reset TSF GPIO used for host timer sync + * @adapter: pointer to adapter + * + * This function send WMI command to reset GPIO configured in FW after + * TSF get operation. + * + * Return: TSF_RETURN on Success, TSF_RESET_GPIO_FAIL on failure + */ +static int hdd_tsf_reset_gpio(struct hdd_adapter *adapter) +{ + /* No GPIO Host timer sync for integrated WIFI Device */ + return TSF_RETURN; +} + +/** + * hdd_tsf_set_gpio() - Set TSF GPIO used for host timer sync + * @hdd_ctx: pointer to hdd context + * + * This function is a dummy function for adrastea arch + * + * Return: QDF_STATUS_SUCCESS on Success + */ + +static QDF_STATUS hdd_tsf_set_gpio(struct hdd_context *hdd_ctx) +{ + return QDF_STATUS_SUCCESS; +} +#else +static int hdd_tsf_reset_gpio(struct hdd_adapter *adapter) +{ + int ret; + + ret = wma_cli_set_command((int)adapter->vdev_id, + (int)GEN_PARAM_RESET_TSF_GPIO, + adapter->vdev_id, + GEN_CMD); + + if (ret != 0) { + hdd_err("tsf reset GPIO fail "); + ret = TSF_RESET_GPIO_FAIL; + } else { + ret = TSF_RETURN; + } + return ret; +} + +/** + * hdd_tsf_set_gpio() - Set TSF GPIO used for host timer sync + * @hdd_ctx: pointer to hdd context + * + * This function check GPIO and set GPIO as IRQ to FW side on + * none Adrastea arch + * + * Return: QDF_STATUS_SUCCESS on Success, others on Failure. + */ +static QDF_STATUS hdd_tsf_set_gpio(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + uint32_t tsf_gpio_pin = TSF_GPIO_PIN_INVALID; + + status = ucfg_fwol_get_tsf_gpio_pin(hdd_ctx->psoc, &tsf_gpio_pin); + if (QDF_IS_STATUS_ERROR(status)) + return QDF_STATUS_E_INVAL; + + if (tsf_gpio_pin == TSF_GPIO_PIN_INVALID) + return QDF_STATUS_E_INVAL; + + status = sme_set_tsf_gpio(hdd_ctx->mac_handle, + tsf_gpio_pin); + + return status; +} +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS +static bool hdd_tsf_is_ptp_enabled(struct hdd_context *hdd) +{ + uint32_t tsf_ptp_options; + + if (hdd && QDF_IS_STATUS_SUCCESS( + ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options))) + return !!tsf_ptp_options; + else + return false; +} + +bool hdd_tsf_is_tx_set(struct hdd_context *hdd) +{ + uint32_t tsf_ptp_options; + + if (hdd && QDF_IS_STATUS_SUCCESS( + ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options))) + return tsf_ptp_options & CFG_SET_TSF_PTP_OPT_TX; + else + return false; +} + +bool hdd_tsf_is_rx_set(struct hdd_context *hdd) +{ + uint32_t tsf_ptp_options; + + if (hdd && QDF_IS_STATUS_SUCCESS( + ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options))) + return tsf_ptp_options & CFG_SET_TSF_PTP_OPT_RX; + else + return false; +} + +bool hdd_tsf_is_raw_set(struct hdd_context *hdd) +{ + uint32_t tsf_ptp_options; + + if (hdd && QDF_IS_STATUS_SUCCESS( + ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options))) + return tsf_ptp_options & CFG_SET_TSF_PTP_OPT_RAW; + else + return false; +} + +bool hdd_tsf_is_dbg_fs_set(struct hdd_context *hdd) +{ + uint32_t tsf_ptp_options; + + if (hdd && QDF_IS_STATUS_SUCCESS( + ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options))) + return tsf_ptp_options & CFG_SET_TSF_DBG_FS; + else + return false; +} + +bool hdd_tsf_is_tsf64_tx_set(struct hdd_context *hdd) +{ + uint32_t tsf_ptp_options; + + if (hdd && QDF_IS_STATUS_SUCCESS( + ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options))) + return tsf_ptp_options & CFG_SET_TSF_PTP_OPT_TSF64_TX; + else + return false; +} +#else + +static bool hdd_tsf_is_ptp_enabled(struct hdd_context *hdd) +{ + return false; +} +#endif + +#ifdef WLAN_FEATURE_TSF_PLUS +static inline +uint64_t hdd_get_monotonic_host_time(struct hdd_context *hdd_ctx) +{ + return hdd_tsf_is_raw_set(hdd_ctx) ? + ktime_get_ns() : ktime_get_real_ns(); +} +#endif + +#if defined(WLAN_FEATURE_TSF_PLUS) && \ + defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) +#define MAX_CONTINUOUS_RETRY_CNT 10 +static uint32_t +hdd_wlan_retry_tsf_cap(struct hdd_adapter *adapter) +{ + struct hdd_context *hddctx; + int count = adapter->continuous_cap_retry_count; + + hddctx = WLAN_HDD_GET_CTX(adapter); + if (count == MAX_CONTINUOUS_RETRY_CNT) { + hdd_debug("Max retry countr reached"); + return 0; + } + qdf_atomic_set(&hddctx->cap_tsf_flag, 0); + count++; + adapter->continuous_cap_retry_count = count; + return (count * WLAN_HDD_CAPTURE_TSF_REQ_TIMEOUT_MS); +} + +static void +hdd_wlan_restart_tsf_cap(struct hdd_adapter *adapter) +{ + struct hdd_context *hddctx; + int count = adapter->continuous_cap_retry_count; + + hddctx = WLAN_HDD_GET_CTX(adapter); + if (count == MAX_CONTINUOUS_RETRY_CNT) { + hdd_debug("Restart TSF CAP"); + qdf_atomic_set(&hddctx->cap_tsf_flag, 0); + adapter->continuous_cap_retry_count = 0; + qdf_mc_timer_start(&adapter->host_target_sync_timer, + WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS); + } +} + +static void +hdd_update_host_time(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctx; + u64 host_time; + char *name = NULL; + + hdd_ctx = adapter->hdd_ctx; + + if (!hdd_tsf_is_initialized(adapter)) { + hdd_err("tsf is not init, exit"); + return; + } + + host_time = hdd_get_monotonic_host_time(hdd_ctx); + hdd_update_timestamp(adapter, 0, host_time); + name = adapter->dev->name; + + hdd_debug("iface: %s - host_time: %llu", + (!name ? "none" : name), host_time); +} + +static +void hdd_tsf_ext_gpio_sync_work(void *data) +{ + QDF_STATUS status; + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + uint32_t tsf_sync_gpio_pin = TSF_GPIO_PIN_INVALID; + + adapter = data; + hdd_ctx = adapter->hdd_ctx; + status = ucfg_fwol_get_tsf_sync_host_gpio_pin(hdd_ctx->psoc, + &tsf_sync_gpio_pin); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("tsf sync gpio host pin error"); + return; + } + gpio_set_value(tsf_sync_gpio_pin, OUTPUT_HIGH); + hdd_update_host_time(adapter); + usleep_range(50, 100); + gpio_set_value(tsf_sync_gpio_pin, OUTPUT_LOW); + + status = wma_cli_set_command((int)adapter->vdev_id, + (int)GEN_PARAM_CAPTURE_TSF, + adapter->vdev_id, GEN_CMD); + if (status != QDF_STATUS_SUCCESS) { + hdd_err("cap tsf fail"); + qdf_mc_timer_stop(&adapter->host_capture_req_timer); + qdf_mc_timer_destroy(&adapter->host_capture_req_timer); + } +} + +static void +hdd_tsf_gpio_sync_work_init(struct hdd_adapter *adapter) +{ + qdf_create_work(0, &adapter->gpio_tsf_sync_work, + hdd_tsf_ext_gpio_sync_work, adapter); +} + +static void +hdd_tsf_gpio_sync_work_deinit(struct hdd_adapter *adapter) +{ + qdf_destroy_work(0, &adapter->gpio_tsf_sync_work); +} + +static void +hdd_tsf_stop_ext_gpio_sync(struct hdd_adapter *adapter) +{ + qdf_cancel_work(&adapter->gpio_tsf_sync_work); +} + +static void +hdd_tsf_start_ext_gpio_sync(struct hdd_adapter *adapter) +{ + qdf_sched_work(0, &adapter->gpio_tsf_sync_work); +} + +static bool hdd_tsf_cap_sync_send(struct hdd_adapter *adapter) +{ + hdd_tsf_start_ext_gpio_sync(adapter); + return true; +} +#elif defined(WLAN_FEATURE_TSF_PLUS) && \ + !defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) +static void +hdd_wlan_restart_tsf_cap(struct hdd_adapter *adapter) +{ +} + +static void +hdd_tsf_gpio_sync_work_init(struct hdd_adapter *adapter) +{ +} + +static void +hdd_tsf_gpio_sync_work_deinit(struct hdd_adapter *adapter) +{ +} + +static void +hdd_tsf_stop_ext_gpio_sync(struct hdd_adapter *adapter) +{ +} + +static void +hdd_tsf_start_ext_gpio_sync(struct hdd_adapter *adapter) +{ +} + +static bool +hdd_tsf_cap_sync_send(struct hdd_adapter *adapter) +{ + hdd_tsf_start_ext_gpio_sync(adapter); + return false; +} + +#else +static bool hdd_tsf_cap_sync_send(struct hdd_adapter *adapter) +{ + return false; +} +#endif + +static enum hdd_tsf_op_result hdd_capture_tsf_internal( + struct hdd_adapter *adapter, uint32_t *buf, int len) +{ + int ret; + struct hdd_context *hddctx; + qdf_mc_timer_t *cap_timer; + + if (!adapter || !buf) { + hdd_err("invalid pointer"); + return HDD_TSF_OP_FAIL; + } + + if (len != 1) + return HDD_TSF_OP_FAIL; + + hddctx = WLAN_HDD_GET_CTX(adapter); + if (!hddctx) { + hdd_err("invalid hdd context"); + return HDD_TSF_OP_FAIL; + } + + if (!hdd_tsf_is_initialized(adapter)) { + buf[0] = TSF_NOT_READY; + return HDD_TSF_OP_SUCC; + } + + buf[0] = hdd_tsf_check_conn_state(adapter); + if (buf[0] != TSF_RETURN) + return HDD_TSF_OP_SUCC; + + if (qdf_atomic_inc_return(&hddctx->cap_tsf_flag) > 1) { + hdd_err("current in capture state"); + buf[0] = TSF_CURRENT_IN_CAP_STATE; + return HDD_TSF_OP_SUCC; + } + + /* record adapter for cap_tsf_irq_handler */ + hddctx->cap_tsf_context = adapter; + + hdd_info("+ioctl issue cap tsf cmd"); + cap_timer = &adapter->host_capture_req_timer; + qdf_mc_timer_init(cap_timer, QDF_TIMER_TYPE_SW, + hdd_capture_req_timer_expired_handler, + (void *)adapter); + qdf_mc_timer_start(cap_timer, WLAN_HDD_CAPTURE_TSF_REQ_TIMEOUT_MS); + + /* Reset TSF value for new capture */ + adapter->cur_target_time = 0; + + buf[0] = TSF_RETURN; + init_completion(&tsf_sync_get_completion_evt); + + if (hdd_tsf_cap_sync_send(adapter)) + return HDD_TSF_OP_SUCC; + + ret = wma_cli_set_command((int)adapter->vdev_id, + (int)GEN_PARAM_CAPTURE_TSF, + adapter->vdev_id, GEN_CMD); + if (QDF_STATUS_SUCCESS != ret) { + hdd_err("cap tsf fail"); + buf[0] = TSF_CAPTURE_FAIL; + hddctx->cap_tsf_context = NULL; + qdf_atomic_set(&hddctx->cap_tsf_flag, 0); + qdf_mc_timer_stop(&adapter->host_capture_req_timer); + qdf_mc_timer_destroy(&adapter->host_capture_req_timer); + + return HDD_TSF_OP_SUCC; + } + hdd_info("-ioctl return cap tsf cmd"); + return HDD_TSF_OP_SUCC; +} + +static enum hdd_tsf_op_result hdd_indicate_tsf_internal( + struct hdd_adapter *adapter, uint32_t *buf, int len) +{ + int ret; + struct hdd_context *hddctx; + + if (!adapter || !buf) { + hdd_err("invalid pointer"); + return HDD_TSF_OP_FAIL; + } + + if (len != 3) + return HDD_TSF_OP_FAIL; + + hddctx = WLAN_HDD_GET_CTX(adapter); + if (!hddctx) { + hdd_err("invalid hdd context"); + return HDD_TSF_OP_FAIL; + } + + buf[1] = 0; + buf[2] = 0; + + if (!hdd_tsf_is_initialized(adapter)) { + buf[0] = TSF_NOT_READY; + return HDD_TSF_OP_SUCC; + } + + buf[0] = hdd_tsf_check_conn_state(adapter); + if (buf[0] != TSF_RETURN) + return HDD_TSF_OP_SUCC; + + if (adapter->cur_target_time == 0) { + hdd_info("TSF value not received"); + buf[0] = TSF_NOT_RETURNED_BY_FW; + return HDD_TSF_OP_SUCC; + } + + buf[0] = TSF_RETURN; + buf[1] = (uint32_t)(adapter->cur_target_time & 0xffffffff); + buf[2] = (uint32_t)((adapter->cur_target_time >> 32) & + 0xffffffff); + + if (!qdf_atomic_read(&hddctx->cap_tsf_flag)) { + hdd_info("old: status=%u, tsf_low=%u, tsf_high=%u", + buf[0], buf[1], buf[2]); + return HDD_TSF_OP_SUCC; + } + + ret = hdd_tsf_reset_gpio(adapter); + if (0 != ret) { + hdd_err("reset tsf gpio fail"); + buf[0] = TSF_RESET_GPIO_FAIL; + return HDD_TSF_OP_SUCC; + } + hddctx->cap_tsf_context = NULL; + qdf_atomic_set(&hddctx->cap_tsf_flag, 0); + hdd_info("get tsf cmd,status=%u, tsf_low=%u, tsf_high=%u", + buf[0], buf[1], buf[2]); + + return HDD_TSF_OP_SUCC; +} + +#ifdef WLAN_FEATURE_TSF_PLUS +/* unit for target time: us; host time: ns */ +#define HOST_TO_TARGET_TIME_RATIO NSEC_PER_USEC +#define MAX_ALLOWED_DEVIATION_NS (100 * NSEC_PER_USEC) +#define MAX_CONTINUOUS_ERROR_CNT 3 + +/* to distinguish 32-bit overflow case, this inverval should: + * equal or less than (1/2 * OVERFLOW_INDICATOR32 us) + */ +#if defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ) || \ + defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) +#define WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC 2 +#else +#define WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC 4 +#endif +#define OVERFLOW_INDICATOR32 (((int64_t)0x1) << 32) +#define CAP_TSF_TIMER_FIX_SEC 1 + +/** + * TS_STATUS - timestamp status + * + * HDD_TS_STATUS_WAITING: one of the stamp-pair + * is not updated + * HDD_TS_STATUS_READY: valid tstamp-pair + * HDD_TS_STATUS_INVALID: invalid tstamp-pair + */ +enum hdd_ts_status { + HDD_TS_STATUS_WAITING, + HDD_TS_STATUS_READY, + HDD_TS_STATUS_INVALID +}; + +static +enum hdd_tsf_op_result __hdd_start_tsf_sync(struct hdd_adapter *adapter) +{ + QDF_STATUS ret; + + if (!hdd_get_th_sync_status(adapter)) { + hdd_err("Host Target sync has not initialized"); + return HDD_TSF_OP_FAIL; + } + + hdd_tsf_gpio_sync_work_init(adapter); + ret = qdf_mc_timer_start(&adapter->host_target_sync_timer, + WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS); + if (ret != QDF_STATUS_SUCCESS && ret != QDF_STATUS_E_ALREADY) { + hdd_err("Failed to start timer, ret: %d", ret); + return HDD_TSF_OP_FAIL; + } + return HDD_TSF_OP_SUCC; +} + +static +enum hdd_tsf_op_result __hdd_stop_tsf_sync(struct hdd_adapter *adapter) +{ + QDF_STATUS ret; + + if (!hdd_get_th_sync_status(adapter)) { + hdd_err("Host Target sync has not initialized"); + return HDD_TSF_OP_SUCC; + } + + ret = qdf_mc_timer_stop(&adapter->host_target_sync_timer); + if (ret != QDF_STATUS_SUCCESS) { + hdd_err("Failed to stop timer, ret: %d", ret); + return HDD_TSF_OP_FAIL; + } + hdd_tsf_stop_ext_gpio_sync(adapter); + hdd_tsf_gpio_sync_work_deinit(adapter); + return HDD_TSF_OP_SUCC; +} + +static inline void hdd_reset_timestamps(struct hdd_adapter *adapter) +{ + qdf_spin_lock_bh(&adapter->host_target_sync_lock); + adapter->cur_host_time = 0; + adapter->cur_target_time = 0; + adapter->last_host_time = 0; + adapter->last_target_time = 0; + qdf_spin_unlock_bh(&adapter->host_target_sync_lock); +} + +/** + * hdd_check_timestamp_status() - return the tstamp status + * + * @last_target_time: the last saved target time + * @last_sync_time: the last saved sync time + * @cur_target_time : new target time + * @cur_sync_time : new sync time + * + * This function check the new timstamp-pair(cur_host_time/cur_target_time)or + * (cur_qtime_time/cur_target_time) + * Return: + * HDD_TS_STATUS_WAITING: cur_sync_time or cur_sync_time is 0 + * HDD_TS_STATUS_READY: cur_target_time/cur_host_time is a valid pair, + * and can be saved + * HDD_TS_STATUS_INVALID: cur_target_time/cur_sync_time is a invalid pair, + * should be discard + */ +static +enum hdd_ts_status hdd_check_timestamp_status( + uint64_t last_target_time, + uint64_t last_sync_time, + uint64_t cur_target_time, + uint64_t cur_sync_time) +{ + uint64_t delta_ns, delta_target_time, delta_sync_time; + + /* one or more are not updated, need to wait */ + if (cur_target_time == 0 || cur_sync_time == 0) + return HDD_TS_STATUS_WAITING; + + /* init value, it's the first time to update the pair */ + if (last_target_time == 0 && last_sync_time == 0) + return HDD_TS_STATUS_READY; + + /* the new values should be greater than the saved values */ + if ((cur_target_time <= last_target_time) || + (cur_sync_time <= last_sync_time)) { + hdd_err("Invalid timestamps!last_target_time: %llu;" + "last_sync_time: %llu; cur_target_time: %llu;" + "cur_sync_time: %llu", + last_target_time, last_sync_time, + cur_target_time, cur_sync_time); + return HDD_TS_STATUS_INVALID; + } + + delta_target_time = (cur_target_time - last_target_time) * + NSEC_PER_USEC; + delta_sync_time = cur_sync_time - last_sync_time; + + /* + * DO NOT use abs64() , a big uint64 value might be turned to + * a small int64 value + */ + delta_ns = ((delta_target_time > delta_sync_time) ? + (delta_target_time - delta_sync_time) : + (delta_sync_time - delta_target_time)); + hdd_warn("timestamps deviation - delta: %llu ns", delta_ns); + /* the deviation should be smaller than a threshold */ + if (delta_ns > MAX_ALLOWED_DEVIATION_NS) { + hdd_warn("Invalid timestamps - delta: %llu ns", delta_ns); + return HDD_TS_STATUS_INVALID; + } + return HDD_TS_STATUS_READY; +} + +static inline bool hdd_tsf_is_in_cap(struct hdd_adapter *adapter) +{ + struct hdd_context *hddctx; + + hddctx = WLAN_HDD_GET_CTX(adapter); + if (!hddctx) + return false; + + return qdf_atomic_read(&hddctx->cap_tsf_flag) > 0; +} + +/* define 64bit plus/minus to deal with overflow */ +static inline int hdd_64bit_plus(uint64_t x, int64_t y, uint64_t *ret) +{ + if ((y < 0 && (-y) > x) || + (y > 0 && (y > U64_MAX - x))) { + *ret = 0; + return -EINVAL; + } + + *ret = x + y; + return 0; +} + +static inline int hdd_uint64_plus(uint64_t x, uint64_t y, uint64_t *ret) +{ + if (!ret) + return -EINVAL; + + if (x > (U64_MAX - y)) { + *ret = 0; + return -EINVAL; + } + + *ret = x + y; + return 0; +} + +static inline int hdd_uint64_minus(uint64_t x, uint64_t y, uint64_t *ret) +{ + if (!ret) + return -EINVAL; + + if (x < y) { + *ret = 0; + return -EINVAL; + } + + *ret = x - y; + return 0; +} + +static inline int32_t hdd_get_hosttime_from_targettime( + struct hdd_adapter *adapter, uint64_t target_time, + uint64_t *host_time) +{ + int32_t ret = -EINVAL; + int64_t delta32_target; + bool in_cap_state; + int64_t normal_interval_target; + + in_cap_state = hdd_tsf_is_in_cap(adapter); + + /* + * To avoid check the lock when it's not capturing tsf + * (the tstamp-pair won't be changed) + */ + if (in_cap_state) + qdf_spin_lock_bh(&adapter->host_target_sync_lock); + + hdd_wlan_restart_tsf_cap(adapter); + /* at present, target_time is only 32bit in fact */ + delta32_target = (int64_t)((target_time & U32_MAX) - + (adapter->last_target_time & U32_MAX)); + + normal_interval_target = WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC * + qdf_do_div(NSEC_PER_SEC, HOST_TO_TARGET_TIME_RATIO); + + if (delta32_target < + (normal_interval_target - OVERFLOW_INDICATOR32)) + delta32_target += OVERFLOW_INDICATOR32; + else if (delta32_target > + (OVERFLOW_INDICATOR32 - normal_interval_target)) + delta32_target -= OVERFLOW_INDICATOR32; + + ret = hdd_64bit_plus(adapter->last_host_time, + HOST_TO_TARGET_TIME_RATIO * delta32_target, + host_time); + + if (in_cap_state) + qdf_spin_unlock_bh(&adapter->host_target_sync_lock); + + return ret; +} + +static inline int32_t hdd_get_targettime_from_hosttime( + struct hdd_adapter *adapter, uint64_t host_time, + uint64_t *target_time) +{ + int32_t ret = -EINVAL; + bool in_cap_state; + + if (!adapter || host_time == 0) + return ret; + + in_cap_state = hdd_tsf_is_in_cap(adapter); + if (in_cap_state) + qdf_spin_lock_bh(&adapter->host_target_sync_lock); + + if (host_time < adapter->last_host_time) + ret = hdd_uint64_minus(adapter->last_target_time, + qdf_do_div(adapter->last_host_time - + host_time, + HOST_TO_TARGET_TIME_RATIO), + target_time); + else + ret = hdd_uint64_plus(adapter->last_target_time, + qdf_do_div(host_time - + adapter->last_host_time, + HOST_TO_TARGET_TIME_RATIO), + target_time); + + if (in_cap_state) + qdf_spin_unlock_bh(&adapter->host_target_sync_lock); + + return ret; +} + +/** + * hdd_get_soctime_from_tsf64time() - return get status + * + * @adapter: Adapter pointer + * @tsf64_time: current tsf64time, us + * @soc_time: current soc time(qtime), ns + * + * This function get current soc time from current tsf64 time + * Returun int32_t value to tell get success or fail. + * + * Return: + * 0: success + * other: fail + * + */ +static inline int32_t hdd_get_soctime_from_tsf64time( + struct hdd_adapter *adapter, uint64_t tsf64_time, + uint64_t *soc_time) +{ + int32_t ret = -EINVAL; + uint64_t delta64_tsf64time; + uint64_t delta64_soctime; + bool in_cap_state; + + in_cap_state = hdd_tsf_is_in_cap(adapter); + + /* + * To avoid check the lock when it's not capturing tsf + * (the tstamp-pair won't be changed) + */ + if (in_cap_state) + qdf_spin_lock_bh(&adapter->host_target_sync_lock); + + /* at present, target_time is 64bit (g_tsf64), us*/ + if (tsf64_time > adapter->last_target_global_tsf_time) { + delta64_tsf64time = tsf64_time - + adapter->last_target_global_tsf_time; + delta64_soctime = delta64_tsf64time * NSEC_PER_USEC; + + /* soc_time (ns)*/ + ret = hdd_uint64_plus(adapter->last_tsf_sync_soc_time, + delta64_soctime, soc_time); + } else { + delta64_tsf64time = adapter->last_target_global_tsf_time - + tsf64_time; + delta64_soctime = delta64_tsf64time * NSEC_PER_USEC; + + /* soc_time (ns)*/ + ret = hdd_uint64_minus(adapter->last_tsf_sync_soc_time, + delta64_soctime, soc_time); + } + + if (in_cap_state) + qdf_spin_unlock_bh(&adapter->host_target_sync_lock); + + return ret; +} + +static void hdd_capture_tsf_timer_expired_handler(void *arg) +{ + uint32_t tsf_op_resp; + struct hdd_adapter *adapter; + + if (!arg) + return; + + adapter = (struct hdd_adapter *)arg; + hdd_capture_tsf_internal(adapter, &tsf_op_resp, 1); +} + +#ifndef WLAN_FEATURE_TSF_PLUS_NOIRQ +#ifndef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC +static irqreturn_t hdd_tsf_captured_irq_handler(int irq, void *arg) +{ + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + uint64_t host_time; + char *name = NULL; + + if (!arg) + return IRQ_NONE; + + if (irq != tsf_gpio_irq_num) + return IRQ_NONE; + + hdd_ctx = (struct hdd_context *)arg; + host_time = hdd_get_monotonic_host_time(hdd_ctx); + + adapter = hdd_ctx->cap_tsf_context; + if (!adapter) + return IRQ_HANDLED; + + if (!hdd_tsf_is_initialized(adapter)) { + hdd_err("tsf is not init, ignore irq"); + return IRQ_HANDLED; + } + + hdd_update_timestamp(adapter, 0, host_time); + if (adapter->dev) + name = adapter->dev->name; + + hdd_info("irq: %d - iface: %s - host_time: %llu", + irq, (!name ? "none" : name), host_time); + + return IRQ_HANDLED; +} +#endif +#endif + +void hdd_capture_req_timer_expired_handler(void *arg) +{ + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + QDF_TIMER_STATE capture_req_timer_status; + qdf_mc_timer_t *sync_timer; + int interval; + int ret; + + if (!arg) + return; + adapter = (struct hdd_adapter *)arg; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) { + hdd_warn("invalid hdd context"); + return; + } + + if (!hdd_tsf_is_initialized(adapter)) { + qdf_mc_timer_destroy(&adapter->host_capture_req_timer); + hdd_warn("tsf not init"); + return; + } + + qdf_spin_lock_bh(&adapter->host_target_sync_lock); + adapter->cur_host_time = 0; + adapter->cur_target_time = 0; + qdf_spin_unlock_bh(&adapter->host_target_sync_lock); + + ret = hdd_tsf_reset_gpio(adapter); + if (0 != ret) + hdd_info("reset tsf gpio fail"); + + hdd_ctx->cap_tsf_context = NULL; + qdf_atomic_set(&hdd_ctx->cap_tsf_flag, 0); + qdf_mc_timer_destroy(&adapter->host_capture_req_timer); + + sync_timer = &adapter->host_target_sync_timer; + capture_req_timer_status = + qdf_mc_timer_get_current_state(sync_timer); + + if (capture_req_timer_status == QDF_TIMER_STATE_UNUSED) { + hdd_warn("invalid timer status"); + return; + } + + interval = WLAN_HDD_CAPTURE_TSF_RESYNC_INTERVAL * MSEC_PER_SEC; + qdf_mc_timer_start(sync_timer, interval); +} + +#ifdef WLAN_FEATURE_TSF_PLUS_NOIRQ +static void hdd_update_timestamp(struct hdd_adapter *adapter) +{ + int interval = 0; + enum hdd_ts_status sync_status; + + if (!adapter) + return; + + /* on ADREASTEA ach, Qtime is used to sync host and tsf time as a + * intermedia there is no IRQ to sync up TSF-HOST, so host time in ns + * and target in us will be updated at the same time in WMI command + * callback + */ + + qdf_spin_lock_bh(&adapter->host_target_sync_lock); + sync_status = + hdd_check_timestamp_status(adapter->last_target_time, + adapter->last_tsf_sync_soc_time, + adapter->cur_target_time, + adapter->cur_tsf_sync_soc_time); + hdd_info("sync_status %d", sync_status); + switch (sync_status) { + case HDD_TS_STATUS_INVALID: + if (++adapter->continuous_error_count < + MAX_CONTINUOUS_ERROR_CNT) { + interval = + WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS; + adapter->cur_target_time = 0; + adapter->cur_tsf_sync_soc_time = 0; + break; + } + hdd_warn("Reach the max continuous error count"); + /* + * fall through: + * If reach MAX_CONTINUOUS_ERROR_CNT, treat it as a + * valid pair + */ + case HDD_TS_STATUS_READY: + adapter->last_target_time = adapter->cur_target_time; + adapter->last_target_global_tsf_time = + adapter->cur_target_global_tsf_time; + adapter->last_tsf_sync_soc_time = + adapter->cur_tsf_sync_soc_time; + adapter->cur_target_time = 0; + adapter->cur_target_global_tsf_time = 0; + adapter->cur_tsf_sync_soc_time = 0; + hdd_info("ts-pair updated: target: %llu; g_target:%llu, Qtime: %llu", + adapter->last_target_time, + adapter->last_target_global_tsf_time, + adapter->last_tsf_sync_soc_time); + + /* + * TSF-HOST need to be updated in at most + * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, it couldn't be achieved + * if the timer interval is also + * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, due to processing or + * schedule delay. So deduct several seconds from + * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC. + * Without this change, hdd_get_hosttime_from_targettime() will + * get wrong host time when it's longer than + * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC from last + * TSF-HOST update. + */ + interval = (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC - + CAP_TSF_TIMER_FIX_SEC) * MSEC_PER_SEC; + + adapter->continuous_error_count = 0; + adapter->continuous_cap_retry_count = 0; + hdd_debug("ts-pair updated: interval: %d", + interval); + break; + case HDD_TS_STATUS_WAITING: + interval = 0; + hdd_warn("TS status is waiting due to one or more pair not updated"); + break; + } + qdf_spin_unlock_bh(&adapter->host_target_sync_lock); + + if (interval > 0) + qdf_mc_timer_start(&adapter->host_target_sync_timer, interval); +} + +static ssize_t __hdd_wlan_tsf_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hdd_station_ctx *hdd_sta_ctx; + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + uint64_t tsf_sync_qtime, host_time, reg_qtime, qtime; + ssize_t size; + + struct net_device *net_dev = container_of(dev, struct net_device, dev); + + adapter = (struct hdd_adapter *)(netdev_priv(net_dev)); + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) + return scnprintf(buf, PAGE_SIZE, "Invalid device\n"); + + if (!hdd_get_th_sync_status(adapter)) + return scnprintf(buf, PAGE_SIZE, + "TSF sync is not initialized\n"); + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (eConnectionState_Associated != hdd_sta_ctx->conn_info.conn_state && + (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE)) + return scnprintf(buf, PAGE_SIZE, "NOT connected\n"); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (!hdd_ctx) + return scnprintf(buf, PAGE_SIZE, "Invalid HDD context\n"); + + tsf_sync_qtime = adapter->last_tsf_sync_soc_time; + do_div(tsf_sync_qtime, NSEC_PER_USEC); + + reg_qtime = qdf_get_log_timestamp(); + host_time = hdd_get_monotonic_host_time(hdd_ctx); + + qtime = qdf_log_timestamp_to_usecs(reg_qtime); + do_div(host_time, NSEC_PER_USEC); + + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + size = scnprintf(buf, PAGE_SIZE, "%s%llu %llu "QDF_FULL_MAC_FMT" %llu %llu\n", + buf, adapter->last_target_time, + tsf_sync_qtime, + QDF_FULL_MAC_REF(hdd_sta_ctx->conn_info.bssid.bytes), + qtime, host_time); + } else { + size = scnprintf(buf, PAGE_SIZE, "%s%llu %llu "QDF_FULL_MAC_FMT" %llu %llu\n", + buf, adapter->last_target_time, + tsf_sync_qtime, + QDF_FULL_MAC_REF(adapter->mac_addr.bytes), + qtime, host_time); + } + + return size; +} + +static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf) +{ + uint32_t tsf_op_resp[3]; + struct hdd_context *hddctx; + + hddctx = WLAN_HDD_GET_CTX(adapter); + hdd_indicate_tsf_internal(adapter, tsf_op_resp, 3); + hdd_update_timestamp(adapter); +} +#else +static void hdd_update_timestamp(struct hdd_adapter *adapter, + uint64_t target_time, uint64_t host_time) +{ + int interval = 0; + enum hdd_ts_status sync_status; + + if (!adapter) + return; + + /* host time is updated in IRQ context, it's always before target time, + * and so no need to try update last_host_time at present; + * since the interval of capturing TSF + * (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC) is long enough, host and target + * time are updated in pairs, and one by one, we can return here to + * avoid requiring spin lock, and to speed up the IRQ processing. + */ + if (host_time > 0) + adapter->cur_host_time = host_time; + + qdf_spin_lock_bh(&adapter->host_target_sync_lock); + if (target_time > 0) + adapter->cur_target_time = target_time; + + sync_status = hdd_check_timestamp_status(adapter->last_target_time, + adapter->last_host_time, + adapter->cur_target_time, + adapter->cur_host_time); + hdd_info("sync_status %d", sync_status); + switch (sync_status) { + case HDD_TS_STATUS_INVALID: + if (++adapter->continuous_error_count < + MAX_CONTINUOUS_ERROR_CNT) { + interval = + WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS; + adapter->cur_target_time = 0; + adapter->cur_host_time = 0; + break; + } + hdd_warn("Reach the max continuous error count"); + /* + * fall through: + * If reach MAX_CONTINUOUS_ERROR_CNT, treat it as a + * valid pair + */ + case HDD_TS_STATUS_READY: + adapter->last_target_time = adapter->cur_target_time; + adapter->last_host_time = adapter->cur_host_time; + adapter->cur_target_time = 0; + adapter->cur_host_time = 0; + hdd_info("ts-pair updated: target: %llu; host: %llu", + adapter->last_target_time, + adapter->last_host_time); + + /* + * TSF-HOST need to be updated in at most + * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, it couldn't be achieved + * if the timer interval is also + * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, due to processing or + * schedule delay. So deduct several seconds from + * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC. + * Without this change, hdd_get_hosttime_from_targettime() will + * get wrong host time when it's longer than + * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC from last + * TSF-HOST update. + */ + interval = (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC - + CAP_TSF_TIMER_FIX_SEC) * MSEC_PER_SEC; + if (adapter->device_mode == QDF_SAP_MODE || + adapter->device_mode == QDF_P2P_GO_MODE) { + interval *= WLAN_HDD_SOFTAP_INTERVAL_TIMES; + } + + adapter->continuous_error_count = 0; + adapter->continuous_cap_retry_count = 0; + hdd_debug("ts-pair updated: interval: %d", + interval); + break; + case HDD_TS_STATUS_WAITING: + interval = 0; + hdd_warn("TS status is waiting due to one or more pair not updated"); + + if (!target_time && !host_time) + interval = hdd_wlan_retry_tsf_cap(adapter); + break; + } + qdf_spin_unlock_bh(&adapter->host_target_sync_lock); + + if (interval > 0) + qdf_mc_timer_start(&adapter->host_target_sync_timer, interval); +} + +static ssize_t __hdd_wlan_tsf_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hdd_station_ctx *hdd_sta_ctx; + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + ssize_t size; + uint64_t host_time, target_time; + + struct net_device *net_dev = container_of(dev, struct net_device, dev); + + adapter = (struct hdd_adapter *)(netdev_priv(net_dev)); + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) + return scnprintf(buf, PAGE_SIZE, "Invalid device\n"); + + if (!hdd_get_th_sync_status(adapter)) + return scnprintf(buf, PAGE_SIZE, + "TSF sync is not initialized\n"); + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (eConnectionState_Associated != hdd_sta_ctx->conn_info.conn_state && + (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE)) + return scnprintf(buf, PAGE_SIZE, "NOT connected\n"); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_ctx) + return scnprintf(buf, PAGE_SIZE, "Invalid HDD context\n"); + + host_time = hdd_get_monotonic_host_time(hdd_ctx); + + if (hdd_get_targettime_from_hosttime(adapter, host_time, + &target_time)) { + size = scnprintf(buf, PAGE_SIZE, "Invalid timestamp\n"); + } else { + if (adapter->device_mode == QDF_STA_MODE || + adapter->device_mode == QDF_P2P_CLIENT_MODE) { + size = scnprintf(buf, PAGE_SIZE, "%s%llu %llu "QDF_FULL_MAC_FMT"\n", + buf, target_time, host_time, + QDF_FULL_MAC_REF(hdd_sta_ctx->conn_info.bssid.bytes)); + } else { + size = scnprintf(buf, PAGE_SIZE, "%s%llu %llu "QDF_FULL_MAC_FMT"\n", + buf, target_time, host_time, + QDF_FULL_MAC_REF(adapter->mac_addr.bytes)); + } + } + + return size; +} + +static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf) +{ + uint32_t tsf_op_resp[3]; + + hdd_indicate_tsf_internal(adapter, tsf_op_resp, 3); + hdd_update_timestamp(adapter, tsf, 0); +} +#endif + +static ssize_t hdd_wlan_tsf_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *net_dev = container_of(dev, struct net_device, dev); + struct osif_vdev_sync *vdev_sync; + ssize_t err_size; + + err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (err_size) + return err_size; + + err_size = __hdd_wlan_tsf_show(dev, attr, buf); + + osif_vdev_sync_op_stop(vdev_sync); + + return err_size; +} + +static DEVICE_ATTR(tsf, 0400, hdd_wlan_tsf_show, NULL); + +static enum hdd_tsf_op_result hdd_tsf_sync_init(struct hdd_adapter *adapter) +{ + QDF_STATUS ret; + struct hdd_context *hddctx; + struct net_device *net_dev; + + if (!adapter) + return HDD_TSF_OP_FAIL; + + hddctx = WLAN_HDD_GET_CTX(adapter); + if (!hddctx) { + hdd_err("invalid hdd context"); + return HDD_TSF_OP_FAIL; + } + + if (!qdf_atomic_read(&hddctx->tsf_ready_flag)) { + hdd_err("TSF feature has NOT been initialized"); + return HDD_TSF_OP_FAIL; + } + + if (hdd_get_th_sync_status(adapter)) { + hdd_err("Host Target sync has been initialized!!"); + return HDD_TSF_OP_SUCC; + } + + qdf_spinlock_create(&adapter->host_target_sync_lock); + + hdd_reset_timestamps(adapter); + + ret = qdf_mc_timer_init(&adapter->host_target_sync_timer, + QDF_TIMER_TYPE_SW, + hdd_capture_tsf_timer_expired_handler, + (void *)adapter); + if (ret != QDF_STATUS_SUCCESS) { + hdd_err("Failed to init timer, ret: %d", ret); + goto fail; + } + + net_dev = adapter->dev; + if (net_dev && hdd_tsf_is_dbg_fs_set(hddctx)) + device_create_file(&net_dev->dev, &dev_attr_tsf); + hdd_set_th_sync_status(adapter, true); + + return HDD_TSF_OP_SUCC; +fail: + hdd_set_th_sync_status(adapter, false); + return HDD_TSF_OP_FAIL; +} + +static enum hdd_tsf_op_result hdd_tsf_sync_deinit(struct hdd_adapter *adapter) +{ + QDF_STATUS ret; + struct hdd_context *hddctx; + struct net_device *net_dev; + + if (!adapter) + return HDD_TSF_OP_FAIL; + + if (!hdd_get_th_sync_status(adapter)) { + hdd_err("Host Target sync has not been initialized!!"); + return HDD_TSF_OP_SUCC; + } + + hdd_set_th_sync_status(adapter, false); + ret = qdf_mc_timer_destroy(&adapter->host_target_sync_timer); + if (ret != QDF_STATUS_SUCCESS) + hdd_err("Failed to destroy timer, ret: %d", ret); + + hddctx = WLAN_HDD_GET_CTX(adapter); + + /* reset the cap_tsf flag and gpio if needed */ + if (hddctx && qdf_atomic_read(&hddctx->cap_tsf_flag) && + hddctx->cap_tsf_context == adapter) { + int reset_ret = hdd_tsf_reset_gpio(adapter); + + if (reset_ret) + hdd_err("Failed to reset tsf gpio, ret:%d", + reset_ret); + hddctx->cap_tsf_context = NULL; + qdf_atomic_set(&hddctx->cap_tsf_flag, 0); + } + + hdd_reset_timestamps(adapter); + + net_dev = adapter->dev; + if (net_dev && hdd_tsf_is_dbg_fs_set(hddctx)) { + struct device *dev = &net_dev->dev; + + device_remove_file(dev, &dev_attr_tsf); + } + return HDD_TSF_OP_SUCC; +} + +#ifdef CONFIG_HL_SUPPORT +static inline +enum hdd_tsf_op_result hdd_netbuf_timestamp(qdf_nbuf_t netbuf, + uint64_t target_time) +{ + struct hdd_adapter *adapter; + struct net_device *net_dev = netbuf->dev; + + if (!net_dev) + return HDD_TSF_OP_FAIL; + + adapter = (struct hdd_adapter *)(netdev_priv(net_dev)); + if (adapter && adapter->magic == WLAN_HDD_ADAPTER_MAGIC && + hdd_get_th_sync_status(adapter)) { + uint64_t host_time; + int32_t ret = hdd_get_hosttime_from_targettime(adapter, + target_time, &host_time); + if (!ret) { + netbuf->tstamp = ns_to_ktime(host_time); + return HDD_TSF_OP_SUCC; + } + } + + return HDD_TSF_OP_FAIL; +} + +#else +static inline +enum hdd_tsf_op_result hdd_netbuf_timestamp(qdf_nbuf_t netbuf, + uint64_t target_time) +{ + struct hdd_adapter *adapter; + struct net_device *net_dev = netbuf->dev; + struct skb_shared_hwtstamps hwtstamps; + + if (!net_dev) + return HDD_TSF_OP_FAIL; + + adapter = (struct hdd_adapter *)(netdev_priv(net_dev)); + if (adapter && adapter->magic == WLAN_HDD_ADAPTER_MAGIC && + hdd_get_th_sync_status(adapter)) { + uint64_t tsf64_time = target_time; + uint64_t soc_time = 0;/*ns*/ + int32_t ret = hdd_get_soctime_from_tsf64time(adapter, + tsf64_time, &soc_time); + if (!ret) { + hwtstamps.hwtstamp = soc_time; + *skb_hwtstamps(netbuf) = hwtstamps; + netbuf->tstamp = ktime_set(0, 0); + return HDD_TSF_OP_SUCC; + } + } + + return HDD_TSF_OP_FAIL; +} +#endif + +int hdd_start_tsf_sync(struct hdd_adapter *adapter) +{ + enum hdd_tsf_op_result ret; + + if (!adapter) + return -EINVAL; + + ret = hdd_tsf_sync_init(adapter); + if (ret != HDD_TSF_OP_SUCC) { + hdd_err("Failed to init tsf sync, ret: %d", ret); + return -EINVAL; + } + + return (__hdd_start_tsf_sync(adapter) == + HDD_TSF_OP_SUCC) ? 0 : -EINVAL; +} + +int hdd_stop_tsf_sync(struct hdd_adapter *adapter) +{ + enum hdd_tsf_op_result ret; + + if (!adapter) + return -EINVAL; + + ret = __hdd_stop_tsf_sync(adapter); + if (ret != HDD_TSF_OP_SUCC) + return -EINVAL; + + ret = hdd_tsf_sync_deinit(adapter); + if (ret != HDD_TSF_OP_SUCC) { + hdd_err("Failed to deinit tsf sync, ret: %d", ret); + return -EINVAL; + } + return 0; +} + +int hdd_tx_timestamp(qdf_nbuf_t netbuf, uint64_t target_time) +{ + struct sock *sk = netbuf->sk; + + if (!sk) + return -EINVAL; + + if ((skb_shinfo(netbuf)->tx_flags & SKBTX_HW_TSTAMP) && + !(skb_shinfo(netbuf)->tx_flags & SKBTX_IN_PROGRESS)) { + struct sock_exterr_skb *serr; + qdf_nbuf_t new_netbuf; + int err; + + if (hdd_netbuf_timestamp(netbuf, target_time) != + HDD_TSF_OP_SUCC) + return -EINVAL; + + new_netbuf = qdf_nbuf_clone(netbuf); + if (!new_netbuf) + return -ENOMEM; + + serr = SKB_EXT_ERR(new_netbuf); + memset(serr, 0, sizeof(*serr)); + serr->ee.ee_errno = ENOMSG; + serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING; + + err = sock_queue_err_skb(sk, new_netbuf); + if (err) { + qdf_nbuf_free(new_netbuf); + return err; + } + + return 0; + } + return -EINVAL; +} + +int hdd_rx_timestamp(qdf_nbuf_t netbuf, uint64_t target_time) +{ + if (hdd_netbuf_timestamp(netbuf, target_time) == + HDD_TSF_OP_SUCC) + return 0; + + /* reset tstamp when failed */ + netbuf->tstamp = ktime_set(0, 0); + return -EINVAL; +} + +static inline int __hdd_capture_tsf(struct hdd_adapter *adapter, + uint32_t *buf, int len) +{ + if (!adapter || !buf) { + hdd_err("invalid pointer"); + return -EINVAL; + } + + if (len != 1) + return -EINVAL; + + buf[0] = TSF_DISABLED_BY_TSFPLUS; + + return 0; +} + +static inline int __hdd_indicate_tsf(struct hdd_adapter *adapter, + uint32_t *buf, int len) +{ + if (!adapter || !buf) { + hdd_err("invalid pointer"); + return -EINVAL; + } + + if (len != 3) + return -EINVAL; + + buf[0] = TSF_DISABLED_BY_TSFPLUS; + buf[1] = 0; + buf[2] = 0; + + return 0; +} + +#if defined(WLAN_FEATURE_TSF_PLUS_NOIRQ) +static inline +enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx) +{ + + if (hdd_tsf_is_tx_set(hdd_ctx)) + ol_register_timestamp_callback(hdd_tx_timestamp); + return HDD_TSF_OP_SUCC; +} + +static inline +enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + QDF_TIMER_STATE capture_req_timer_status; + qdf_mc_timer_t *cap_timer; + struct hdd_adapter *adapter, *adapternode_ptr, *next_ptr; + + if (hdd_tsf_is_tx_set(hdd_ctx)) + ol_deregister_timestamp_callback(); + + status = hdd_get_front_adapter(hdd_ctx, &adapternode_ptr); + + while (adapternode_ptr && QDF_STATUS_SUCCESS == status) { + adapter = adapternode_ptr; + status = + hdd_get_next_adapter(hdd_ctx, adapternode_ptr, &next_ptr); + adapternode_ptr = next_ptr; + if (adapter->host_capture_req_timer.state == 0) + continue; + cap_timer = &adapter->host_capture_req_timer; + capture_req_timer_status = + qdf_mc_timer_get_current_state(cap_timer); + + if (capture_req_timer_status != QDF_TIMER_STATE_UNUSED) { + qdf_mc_timer_stop(cap_timer); + status = + qdf_mc_timer_destroy(cap_timer); + if (status != QDF_STATUS_SUCCESS) + hdd_err("remove timer failed: %d", status); + } + } + + return HDD_TSF_OP_SUCC; +} + +#elif defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) +static +enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx) +{ + int ret; + QDF_STATUS status; + uint32_t tsf_sync_gpio_pin = TSF_GPIO_PIN_INVALID; + + status = ucfg_fwol_get_tsf_sync_host_gpio_pin(hdd_ctx->psoc, + &tsf_sync_gpio_pin); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("tsf gpio irq host pin error"); + goto fail; + } + + if (tsf_sync_gpio_pin == TSF_GPIO_PIN_INVALID) { + hdd_err("gpio host pin is invalid"); + goto fail; + } + + ret = gpio_request(tsf_sync_gpio_pin, "wlan_tsf"); + if (ret) { + hdd_err("gpio host pin is invalid"); + goto fail; + } + + ret = gpio_direction_output(tsf_sync_gpio_pin, OUTPUT_LOW); + if (ret) { + hdd_err("gpio host pin is invalid"); + goto fail_free_gpio; + } + + if (hdd_tsf_is_tx_set(hdd_ctx)) + ol_register_timestamp_callback(hdd_tx_timestamp); + + return HDD_TSF_OP_SUCC; + +fail_free_gpio: + gpio_free(tsf_sync_gpio_pin); +fail: + return HDD_TSF_OP_FAIL; +} + +static +enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + uint32_t tsf_sync_gpio_pin = TSF_GPIO_PIN_INVALID; + + status = ucfg_fwol_get_tsf_sync_host_gpio_pin(hdd_ctx->psoc, + &tsf_sync_gpio_pin); + if (QDF_IS_STATUS_ERROR(status)) + return QDF_STATUS_E_INVAL; + + if (tsf_sync_gpio_pin == TSF_GPIO_PIN_INVALID) + return QDF_STATUS_E_INVAL; + + if (hdd_tsf_is_tx_set(hdd_ctx)) + ol_deregister_timestamp_callback(); + + gpio_free(tsf_sync_gpio_pin); + return HDD_TSF_OP_SUCC; +} + +#elif defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ) +static +enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx) +{ + int ret; + QDF_STATUS status; + uint32_t tsf_irq_gpio_pin = TSF_GPIO_PIN_INVALID; + + status = ucfg_fwol_get_tsf_irq_host_gpio_pin(hdd_ctx->psoc, + &tsf_irq_gpio_pin); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("tsf gpio irq host pin error"); + goto fail; + } + + if (tsf_irq_gpio_pin == TSF_GPIO_PIN_INVALID) { + hdd_err("gpio host pin is invalid"); + goto fail; + } + + ret = gpio_request(tsf_irq_gpio_pin, "wlan_tsf"); + if (ret) { + hdd_err("gpio host pin is invalid"); + goto fail; + } + + ret = gpio_direction_input(tsf_irq_gpio_pin); + if (ret) { + hdd_err("gpio host pin is invalid"); + goto fail_free_gpio; + } + + tsf_gpio_irq_num = gpio_to_irq(tsf_irq_gpio_pin); + if (tsf_gpio_irq_num < 0) { + hdd_err("fail to get irq: %d", tsf_gpio_irq_num); + goto fail_free_gpio; + } + + ret = request_irq(tsf_gpio_irq_num, hdd_tsf_captured_irq_handler, + IRQF_SHARED | IRQF_TRIGGER_RISING, "wlan_tsf", + hdd_ctx); + + if (ret) { + hdd_err("Failed to register irq handler: %d", ret); + goto fail_free_gpio; + } + + if (hdd_tsf_is_tx_set(hdd_ctx)) + ol_register_timestamp_callback(hdd_tx_timestamp); + + return HDD_TSF_OP_SUCC; + +fail_free_gpio: + gpio_free(tsf_irq_gpio_pin); +fail: + tsf_gpio_irq_num = -1; + return HDD_TSF_OP_FAIL; +} + +static +enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + uint32_t tsf_irq_gpio_pin = TSF_GPIO_PIN_INVALID; + + status = ucfg_fwol_get_tsf_irq_host_gpio_pin(hdd_ctx->psoc, + &tsf_irq_gpio_pin); + if (QDF_IS_STATUS_ERROR(status)) + return QDF_STATUS_E_INVAL; + + if (tsf_irq_gpio_pin == TSF_GPIO_PIN_INVALID) + return QDF_STATUS_E_INVAL; + + if (hdd_tsf_is_tx_set(hdd_ctx)) + ol_deregister_timestamp_callback(); + + if (tsf_gpio_irq_num >= 0) { + free_irq(tsf_gpio_irq_num, hdd_ctx); + tsf_gpio_irq_num = -1; + gpio_free(tsf_irq_gpio_pin); + } + + return HDD_TSF_OP_SUCC; +} + +#elif defined(WLAN_FEATURE_TSF_TIMER_SYNC) +static inline +enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx) +{ + return HDD_TSF_OP_SUCC; +} + +static inline +enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx) +{ + return HDD_TSF_OP_SUCC; +} +#else +static inline +enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx) +{ + int ret; + + ret = cnss_common_register_tsf_captured_handler( + hdd_ctx->parent_dev, + hdd_tsf_captured_irq_handler, + (void *)hdd_ctx); + if (ret != 0) { + hdd_err("Failed to register irq handler: %d", ret); + return HDD_TSF_OP_FAIL; + } + + if (hdd_tsf_is_tx_set(hdd_ctx)) + ol_register_timestamp_callback(hdd_tx_timestamp); + return HDD_TSF_OP_SUCC; +} + +static inline +enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx) +{ + int ret; + + if (hdd_tsf_is_tx_set(hdd_ctx)) + ol_deregister_timestamp_callback(); + + ret = cnss_common_unregister_tsf_captured_handler( + hdd_ctx->parent_dev, + (void *)hdd_ctx); + if (ret != 0) { + hdd_err("Failed to unregister irq handler, ret:%d", + ret); + ret = HDD_TSF_OP_FAIL; + } + + return HDD_TSF_OP_SUCC; +} +#endif + +void hdd_tsf_notify_wlan_state_change(struct hdd_adapter *adapter, + eConnectionState old_state, + eConnectionState new_state) +{ + if (!adapter) + return; + + if (old_state != eConnectionState_Associated && + new_state == eConnectionState_Associated) + hdd_start_tsf_sync(adapter); + else if (old_state == eConnectionState_Associated && + new_state != eConnectionState_Associated) + hdd_stop_tsf_sync(adapter); +} +#else +static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf) +{ +} + +static inline int __hdd_indicate_tsf(struct hdd_adapter *adapter, + uint32_t *buf, int len) +{ + return (hdd_indicate_tsf_internal(adapter, buf, len) == + HDD_TSF_OP_SUCC) ? 0 : -EINVAL; +} + +static inline int __hdd_capture_tsf(struct hdd_adapter *adapter, + uint32_t *buf, int len) +{ + return (hdd_capture_tsf_internal(adapter, buf, len) == + HDD_TSF_OP_SUCC) ? 0 : -EINVAL; +} + +static inline +enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx) +{ + return HDD_TSF_OP_SUCC; +} + +static inline +enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx) +{ + return HDD_TSF_OP_SUCC; +} +#endif /* WLAN_FEATURE_TSF_PLUS */ + +int hdd_capture_tsf(struct hdd_adapter *adapter, uint32_t *buf, int len) +{ + return __hdd_capture_tsf(adapter, buf, len); +} + +int hdd_indicate_tsf(struct hdd_adapter *adapter, uint32_t *buf, int len) +{ + return __hdd_indicate_tsf(adapter, buf, len); +} + +#ifdef WLAN_FEATURE_TSF_PTP +int wlan_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) + +{ + struct hdd_adapter *adapter = netdev_priv(dev); + struct osif_vdev_sync *vdev_sync; + struct hdd_context *hdd_ctx; + int errno; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return -EINVAL; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return -EAGAIN; + + info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + if (hdd_ctx->ptp_clock) + info->phc_index = ptp_clock_index(hdd_ctx->ptp_clock); + else + info->phc_index = -1; + + osif_vdev_sync_op_stop(vdev_sync); + return 0; +} + +#if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE) +/** + * wlan_ptp_gettime() - return fw ts info to uplayer + * @ptp: pointer to ptp_clock_info. + * @ts: pointer to timespec. + * + * Return: Describe the execute result of this routine + */ +static int wlan_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts) +{ + uint64_t host_time, target_time = 0; + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + uint32_t tsf_reg_read_enabled; + struct osif_psoc_sync *psoc_sync; + int errno, status = 0; + + hdd_ctx = qdf_container_of(ptp, struct hdd_context, ptp_cinfo); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return -EINVAL; + + errno = osif_psoc_sync_op_start(hdd_ctx->parent_dev, &psoc_sync); + if (errno) + return -EAGAIN; + + adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_GO_MODE); + if (!adapter) { + adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_CLIENT_MODE); + if (!adapter) { + adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); + if (!adapter) + adapter = hdd_get_adapter(hdd_ctx, + QDF_STA_MODE); + if (!adapter) { + status = -EOPNOTSUPP; + goto end; + } + } + } + + host_time = hdd_get_monotonic_host_time(hdd_ctx); + if (hdd_get_targettime_from_hosttime(adapter, host_time, + &target_time)) { + hdd_err("get invalid target timestamp"); + status = -EINVAL; + goto end; + } + *ts = ns_to_timespec(target_time * NSEC_PER_USEC); + +end: + osif_psoc_sync_op_stop(psoc_sync); + return status; +} + +/** + * wlan_hdd_phc_init() - phc init + * @hdd_ctx: pointer to the hdd_contex. + * + * Return: NULL + */ +static void wlan_hdd_phc_init(struct hdd_context *hdd_ctx) +{ + hdd_ctx->ptp_cinfo.gettime = wlan_ptp_gettime; + + hdd_ctx->ptp_clock = ptp_clock_register(&hdd_ctx->ptp_cinfo, + hdd_ctx->parent_dev); +} + +/** + * wlan_hdd_phc_deinit() - phc deinit + * @hdd_ctx: pointer to the hdd_contex. + * + * Return: NULL + */ +static void wlan_hdd_phc_deinit(struct hdd_context *hdd_ctx) +{ + hdd_ctx->ptp_cinfo.gettime = NULL; + + if (hdd_ctx->ptp_clock) { + ptp_clock_unregister(hdd_ctx->ptp_clock); + hdd_ctx->ptp_clock = NULL; + } +} + +#else +static int wlan_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + uint64_t host_time, target_time = 0; + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + struct osif_psoc_sync *psoc_sync; + int errno, status = 0; + + hdd_ctx = qdf_container_of(ptp, struct hdd_context, ptp_cinfo); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return -EINVAL; + + errno = osif_psoc_sync_op_start(hdd_ctx->parent_dev, &psoc_sync); + if (errno) + return -EAGAIN; + + adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_GO_MODE); + if (!adapter) { + adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_CLIENT_MODE); + if (!adapter) { + adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); + if (!adapter) + adapter = hdd_get_adapter(hdd_ctx, + QDF_STA_MODE); + if (!adapter) { + status = -EOPNOTSUPP; + goto end; + } + } + } + + host_time = hdd_get_monotonic_host_time(hdd_ctx); + if (hdd_get_targettime_from_hosttime(adapter, host_time, + &target_time)) { + hdd_err("get invalid target timestamp"); + status = -EINVAL; + goto end; + } + *ts = ns_to_timespec64(target_time * NSEC_PER_USEC); + +end: + osif_psoc_sync_op_stop(psoc_sync); + return status; +} + +static void wlan_hdd_phc_init(struct hdd_context *hdd_ctx) +{ + hdd_ctx->ptp_cinfo.gettime64 = wlan_ptp_gettime; + hdd_ctx->ptp_clock = ptp_clock_register(&hdd_ctx->ptp_cinfo, + hdd_ctx->parent_dev); +} + +static void wlan_hdd_phc_deinit(struct hdd_context *hdd_ctx) +{ + hdd_ctx->ptp_cinfo.gettime64 = NULL; + + if (hdd_ctx->ptp_clock) { + ptp_clock_unregister(hdd_ctx->ptp_clock); + hdd_ctx->ptp_clock = NULL; + } +} + +#endif +#else + +static void wlan_hdd_phc_init(struct hdd_context *hdd_ctx) +{ +} + +static void wlan_hdd_phc_deinit(struct hdd_context *hdd_ctx) +{ +} +#endif /* WLAN_FEATURE_TSF_PTP */ + +/** + * hdd_get_tsf_cb() - handle tsf callback + * @pcb_cxt: pointer to the hdd_contex + * @ptsf: pointer to struct stsf + * + * This function handle the event that reported by firmware at first. + * The event contains the vdev_id, current tsf value of this vdev, + * tsf value is 64bits, discripted in two varaible tsf_low and tsf_high. + * These two values each is uint32. + * + * Return: 0 for success or non-zero negative failure code + */ +int hdd_get_tsf_cb(void *pcb_cxt, struct stsf *ptsf) +{ + struct hdd_context *hddctx; + struct hdd_adapter *adapter; + int ret; + uint64_t tsf_sync_soc_time; + QDF_STATUS status; + QDF_TIMER_STATE capture_req_timer_status; + qdf_mc_timer_t *capture_timer; + + if (!pcb_cxt || !ptsf) { + hdd_err("HDD context is not valid"); + return -EINVAL; + } + + hddctx = (struct hdd_context *)pcb_cxt; + ret = wlan_hdd_validate_context(hddctx); + if (0 != ret) + return -EINVAL; + + adapter = hdd_get_adapter_by_vdev(hddctx, ptsf->vdev_id); + + if (!adapter) { + hdd_err("failed to find adapter"); + return -EINVAL; + } + + if (!hdd_tsf_is_initialized(adapter)) { + hdd_err("tsf is not init, ignore tsf event"); + return -EINVAL; + } + + hdd_info("tsf cb handle event, device_mode is %d", + adapter->device_mode); + + capture_timer = &adapter->host_capture_req_timer; + capture_req_timer_status = + qdf_mc_timer_get_current_state(capture_timer); + if (capture_req_timer_status == QDF_TIMER_STATE_UNUSED) { + hdd_warn("invalid timer status"); + return -EINVAL; + } + + qdf_mc_timer_stop(capture_timer); + status = qdf_mc_timer_destroy(capture_timer); + if (status != QDF_STATUS_SUCCESS) + hdd_warn("destroy cap req timer fail, ret: %d", status); + + adapter->cur_target_time = ((uint64_t)ptsf->tsf_high << 32 | + ptsf->tsf_low); + + adapter->cur_target_global_tsf_time = + ((uint64_t)ptsf->global_tsf_high << 32 | + ptsf->global_tsf_low); + tsf_sync_soc_time = ((uint64_t)ptsf->soc_timer_high << 32 | + ptsf->soc_timer_low); + adapter->cur_tsf_sync_soc_time = + qdf_log_timestamp_to_usecs(tsf_sync_soc_time) * NSEC_PER_USEC; + complete(&tsf_sync_get_completion_evt); + hdd_update_tsf(adapter, adapter->cur_target_time); + hdd_info("Vdev=%u, tsf_low=%u, tsf_high=%u ptsf->soc_timer_low=%u ptsf->soc_timer_high=%u", + ptsf->vdev_id, ptsf->tsf_low, ptsf->tsf_high, + ptsf->soc_timer_low, ptsf->soc_timer_high); + return 0; +} + +static const struct nla_policy tsf_policy[QCA_WLAN_VENDOR_ATTR_TSF_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_TSF_CMD] = {.type = NLA_U32}, +}; + +/** + * __wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Handle TSF SET / GET operation from userspace + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_MAX + 1]; + int status, ret; + struct sk_buff *reply_skb; + uint32_t tsf_op_resp[3] = {0}, tsf_cmd; + + hdd_enter_dev(wdev->netdev); + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) + return -EINVAL; + + if (wlan_cfg80211_nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_TSF_MAX, + data, data_len, tsf_policy)) { + hdd_err("Invalid TSF cmd"); + return -EINVAL; + } + + if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]) { + hdd_err("Invalid TSF cmd"); + return -EINVAL; + } + tsf_cmd = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]); + + if (tsf_cmd == QCA_TSF_CAPTURE || tsf_cmd == QCA_TSF_SYNC_GET) { + hdd_capture_tsf(adapter, tsf_op_resp, 1); + switch (tsf_op_resp[0]) { + case TSF_RETURN: + status = 0; + break; + case TSF_CURRENT_IN_CAP_STATE: + status = -EALREADY; + break; + case TSF_STA_NOT_CONNECTED_NO_TSF: + case TSF_SAP_NOT_STARTED_NO_TSF: + status = -EPERM; + break; + default: + case TSF_CAPTURE_FAIL: + status = -EINVAL; + break; + } + } + if (status < 0) + goto end; + + if (tsf_cmd == QCA_TSF_SYNC_GET) { + ret = wait_for_completion_timeout(&tsf_sync_get_completion_evt, + msecs_to_jiffies(WLAN_TSF_SYNC_GET_TIMEOUT)); + if (ret == 0) { + status = -ETIMEDOUT; + goto end; + } + } + + if (tsf_cmd == QCA_TSF_GET || tsf_cmd == QCA_TSF_SYNC_GET) { + hdd_indicate_tsf(adapter, tsf_op_resp, 3); + switch (tsf_op_resp[0]) { + case TSF_RETURN: + status = 0; + break; + case TSF_NOT_RETURNED_BY_FW: + status = -EINPROGRESS; + break; + case TSF_STA_NOT_CONNECTED_NO_TSF: + case TSF_SAP_NOT_STARTED_NO_TSF: + status = -EPERM; + break; + default: + status = -EINVAL; + break; + } + if (status != 0) + goto end; + + reply_skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL, + sizeof(uint64_t) * 2 + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX, + GFP_KERNEL); + if (!reply_skb) { + hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + status = -ENOMEM; + goto end; + } + if (hdd_wlan_nla_put_u64(reply_skb, + QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE, + adapter->cur_target_time) || + hdd_wlan_nla_put_u64(reply_skb, + QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE, + adapter->cur_tsf_sync_soc_time)) { + hdd_err("nla put fail"); + kfree_skb(reply_skb); + status = -EINVAL; + goto end; + } + status = cfg80211_vendor_cmd_reply(reply_skb); + } + +end: + hdd_info("TSF operation %d status: %d", tsf_cmd, status); + return status; +} + +int wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_handle_tsf_cmd(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * wlan_hdd_tsf_init() - set callback to handle tsf value. + * @hdd_ctx: pointer to the struct hdd_context + * + * This function set the callback to sme module, the callback will be + * called when a tsf event is reported by firmware + * + * Return: none + */ +void wlan_hdd_tsf_init(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + if (!hdd_ctx) + return; + + if (qdf_atomic_inc_return(&hdd_ctx->tsf_ready_flag) > 1) + return; + + qdf_atomic_init(&hdd_ctx->cap_tsf_flag); + + status = hdd_tsf_set_gpio(hdd_ctx); + + if (QDF_STATUS_SUCCESS != status) { + hdd_debug("set tsf GPIO failed, status: %d", status); + goto fail; + } + + if (wlan_hdd_tsf_plus_init(hdd_ctx) != HDD_TSF_OP_SUCC) + goto fail; + + if (hdd_tsf_is_ptp_enabled(hdd_ctx)) + wlan_hdd_phc_init(hdd_ctx); + + return; + +fail: + qdf_atomic_set(&hdd_ctx->tsf_ready_flag, 0); +} + +void wlan_hdd_tsf_deinit(struct hdd_context *hdd_ctx) +{ + if (!hdd_ctx) + return; + + if (!qdf_atomic_read(&hdd_ctx->tsf_ready_flag)) + return; + + if (hdd_tsf_is_ptp_enabled(hdd_ctx)) + wlan_hdd_phc_deinit(hdd_ctx); + wlan_hdd_tsf_plus_deinit(hdd_ctx); + qdf_atomic_set(&hdd_ctx->tsf_ready_flag, 0); + qdf_atomic_set(&hdd_ctx->cap_tsf_flag, 0); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_twt.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_twt.c new file mode 100644 index 0000000000000000000000000000000000000000..0d9cdce2e637eb6cee67332f49d00b55385a9971 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_twt.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC : wlan_hdd_twt.c + * + * WLAN Host Device Driver file for TWT (Target Wake Time) support. + * + */ + +#include "wlan_hdd_twt.h" +#include "wlan_hdd_main.h" +#include "wlan_hdd_cfg.h" +#include "sme_api.h" +#include "wma_twt.h" + +void hdd_update_tgt_twt_cap(struct hdd_context *hdd_ctx, + struct wma_tgt_cfg *cfg) +{ + struct wma_tgt_services *services = &cfg->services; + bool enable_twt = false; + + ucfg_mlme_get_enable_twt(hdd_ctx->psoc, &enable_twt); + hdd_debug("TWT: enable_twt=%d, tgt Req=%d, Res=%d", + enable_twt, services->twt_requestor, + services->twt_responder); + + ucfg_mlme_set_twt_requestor(hdd_ctx->psoc, + QDF_MIN(services->twt_requestor, + enable_twt)); + + ucfg_mlme_set_twt_responder(hdd_ctx->psoc, + QDF_MIN(services->twt_responder, + enable_twt)); + + /* + * Currently broadcast TWT is not supported + */ + ucfg_mlme_set_bcast_twt(hdd_ctx->psoc, + QDF_MIN(0, enable_twt)); +} + +void hdd_send_twt_enable_cmd(struct hdd_context *hdd_ctx) +{ + uint8_t pdev_id = hdd_ctx->pdev->pdev_objmgr.wlan_pdev_id; + bool req_val = 0, resp_val = 0, bcast_val = 0; + uint32_t congestion_timeout = 0; + + ucfg_mlme_get_twt_requestor(hdd_ctx->psoc, &req_val); + ucfg_mlme_get_twt_responder(hdd_ctx->psoc, &resp_val); + ucfg_mlme_get_bcast_twt(hdd_ctx->psoc, &bcast_val); + ucfg_mlme_get_twt_congestion_timeout(hdd_ctx->psoc, + &congestion_timeout); + + hdd_debug("TWT cfg req:%d, responder:%d, bcast:%d, pdev:%d, cong:%d", + req_val, resp_val, bcast_val, pdev_id, congestion_timeout); + + if (req_val || resp_val || bcast_val) + wma_send_twt_enable_cmd(pdev_id, congestion_timeout, bcast_val); +} + +QDF_STATUS hdd_send_twt_disable_cmd(struct hdd_context *hdd_ctx) +{ + uint8_t pdev_id = hdd_ctx->pdev->pdev_objmgr.wlan_pdev_id; + + hdd_debug("TWT disable cmd :pdev:%d", pdev_id); + + wma_send_twt_disable_cmd(pdev_id); + + return qdf_wait_single_event(&hdd_ctx->twt_disable_comp_evt, + TWT_DISABLE_COMPLETE_TIMEOUT); +} + +/** + * hdd_twt_enable_comp_cb() - TWT enable complete event callback + * @hdd_handle: opaque handle for the global HDD Context + * @twt_event: TWT event data received from the target + * + * Return: None + */ +static void +hdd_twt_enable_comp_cb(hdd_handle_t hdd_handle, + struct wmi_twt_enable_complete_event_param *params) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + enum twt_status prev_state; + + if (!hdd_ctx) { + hdd_err("TWT: Invalid HDD Context"); + return; + } + prev_state = hdd_ctx->twt_state; + if (params->status == WMI_HOST_ENABLE_TWT_STATUS_OK || + params->status == WMI_HOST_ENABLE_TWT_STATUS_ALREADY_ENABLED) { + switch (prev_state) { + case TWT_FW_TRIGGER_ENABLE_REQUESTED: + hdd_ctx->twt_state = TWT_FW_TRIGGER_ENABLED; + break; + case TWT_HOST_TRIGGER_ENABLE_REQUESTED: + hdd_ctx->twt_state = TWT_HOST_TRIGGER_ENABLED; + break; + default: + break; + } + } + if (params->status == WMI_HOST_ENABLE_TWT_INVALID_PARAM || + params->status == WMI_HOST_ENABLE_TWT_STATUS_UNKNOWN_ERROR) + hdd_ctx->twt_state = TWT_INIT; + + hdd_debug("TWT: pdev ID:%d, status:%d State transitioned from %d to %d", + params->pdev_id, params->status, + prev_state, hdd_ctx->twt_state); +} + +/** + * hdd_twt_disable_comp_cb() - TWT disable complete event callback + * @hdd_handle: opaque handle for the global HDD Context + * + * Return: None + */ +static void +hdd_twt_disable_comp_cb(hdd_handle_t hdd_handle) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + enum twt_status prev_state; + QDF_STATUS status; + + if (!hdd_ctx) { + hdd_err("TWT: Invalid HDD Context"); + return; + } + prev_state = hdd_ctx->twt_state; + hdd_ctx->twt_state = TWT_DISABLED; + + hdd_debug("TWT: State transitioned from %d to %d", + prev_state, hdd_ctx->twt_state); + + status = qdf_event_set(&hdd_ctx->twt_disable_comp_evt); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to set twt_disable_comp_evt"); +} + +void wlan_hdd_twt_init(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + hdd_ctx->twt_state = TWT_INIT; + status = sme_register_twt_enable_complete_cb(hdd_ctx->mac_handle, + hdd_twt_enable_comp_cb); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Register twt enable complete failed"); + return; + } + + status = sme_register_twt_disable_complete_cb(hdd_ctx->mac_handle, + hdd_twt_disable_comp_cb); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Register twt disable complete failed"); + goto twt_init_fail; + } + + status = qdf_event_create(&hdd_ctx->twt_disable_comp_evt); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("twt_disable_comp_evt init failed"); + sme_deregister_twt_disable_complete_cb(hdd_ctx->mac_handle); + goto twt_init_fail; + } + + hdd_send_twt_enable_cmd(hdd_ctx); + return; + +twt_init_fail: + + sme_deregister_twt_enable_complete_cb(hdd_ctx->mac_handle); +} + +void wlan_hdd_twt_deinit(struct hdd_context *hdd_ctx) +{ + QDF_STATUS status; + + status = sme_deregister_twt_disable_complete_cb(hdd_ctx->mac_handle); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("De-register of twt disable cb failed: %d", status); + status = sme_deregister_twt_enable_complete_cb(hdd_ctx->mac_handle); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("De-register of twt enable cb failed: %d", status); + + if (!QDF_IS_STATUS_SUCCESS(qdf_event_destroy( + &hdd_ctx->twt_disable_comp_evt))) + hdd_err("Failed to destroy twt_disable_comp_evt"); + + hdd_ctx->twt_state = TWT_CLOSED; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tx_power.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tx_power.c new file mode 100644 index 0000000000000000000000000000000000000000..0e23de09595ed31be6e33006f32b9f91d87a9c77 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tx_power.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_tx_power.c + * + * WLAN tx power setting functions + * + */ + +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TXPOWER_SCALE 4 + +static const struct nla_policy +txpower_scale_policy[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE] = { .type = NLA_U8 }, +}; + +/** + * __wlan_hdd_cfg80211_txpower_scale () - txpower scaling + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_txpower_scale(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter; + int ret; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX + 1]; + uint8_t scale_value; + QDF_STATUS status; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX, + data, data_len, txpower_scale_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE]) { + hdd_err("attr tx power scale failed"); + return -EINVAL; + } + + scale_value = nla_get_u8(tb + [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE]); + + if (scale_value > MAX_TXPOWER_SCALE) { + hdd_err("Invalid tx power scale level"); + return -EINVAL; + } + + status = wma_set_tx_power_scale(adapter->vdev_id, scale_value); + + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Set tx power scale failed"); + return -EINVAL; + } + + return 0; +} + +int wlan_hdd_cfg80211_txpower_scale(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_txpower_scale(wiphy, wdev, data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static const struct nla_policy txpower_scale_decr_db_policy +[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB] = { .type = NLA_U8 }, +}; + +/** + * __wlan_hdd_cfg80211_txpower_scale_decr_db () - txpower scaling + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +static int +__wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter; + int ret; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX + 1]; + uint8_t scale_value; + QDF_STATUS status; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX, + data, data_len, + txpower_scale_decr_db_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB]) { + hdd_err("attr tx power decrease db value failed"); + return -EINVAL; + } + + scale_value = nla_get_u8(tb + [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB]); + + status = wma_set_tx_power_scale_decr_db(adapter->vdev_id, + scale_value); + + if (status != QDF_STATUS_SUCCESS) { + hdd_err("Set tx power decrease db failed"); + return -EINVAL; + } + + return 0; +} + +int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_txpower_scale_decr_db(wiphy, wdev, + data, data_len); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tx_power.h b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tx_power.h new file mode 100644 index 0000000000000000000000000000000000000000..3ba2792b2300398fa81eb162aaa935ddccac7afe --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tx_power.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_HDD_TX_POWER_H +#define __WLAN_HDD_TX_POWER_H + +/** + * DOC: wlan_hdd_tx_power_h + * + * WLAN Host Device Driver TX power setting API specification + */ + +#ifdef FEATURE_TX_POWER +/** + * wlan_hdd_cfg80211_txpower_scale () - txpower scaling + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_txpower_scale(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +/** + * wlan_hdd_cfg80211_txpower_scale_decr_db () - txpower scaling + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * Return: 0 on success, negative errno on failure + */ +int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +#define FEATURE_TX_POWER_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_txpower_scale \ +}, \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = \ + QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE_DECR_DB, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_hdd_cfg80211_txpower_scale_decr_db \ +}, +#else /* FEATURE_TX_POWER */ +#define FEATURE_TX_POWER_VENDOR_COMMANDS +#endif /* FEATURE_TX_POWER */ + +#endif /* __WLAN_HDD_TX_POWER_H */ + diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tx_rx.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tx_rx.c new file mode 100644 index 0000000000000000000000000000000000000000..ba6fcf423c2304552efd96b406e1edb516854243 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_tx_rx.c @@ -0,0 +1,3553 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_tx_rx.c + * + * Linux HDD Tx/RX APIs + */ + +/* denote that this file does not allow legacy hddLog */ +#define HDD_DISALLOW_LEGACY_HDDLOG 1 +#include "osif_sync.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "sap_api.h" +#include "wlan_hdd_wmm.h" +#include "wlan_hdd_tdls.h" +#include "wlan_hdd_ocb.h" +#include "wlan_hdd_lro.h" +#include +#include +#include +#include +#include "wlan_hdd_nan_datapath.h" +#include "pld_common.h" +#include +#include "wlan_hdd_rx_monitor.h" +#include "wlan_hdd_power.h" +#include "wlan_hdd_cfg80211.h" +#include +#include +#include "wma_api.h" + +#include "wlan_hdd_nud_tracking.h" +#include "dp_txrx.h" +#if defined(WLAN_SUPPORT_RX_FISA) +#include "dp_fisa_rx.h" +#endif +#include +#include "cfg_ucfg_api.h" +#include "target_type.h" +#include "wlan_hdd_object_manager.h" +#include "nan_public_structs.h" +#include "nan_ucfg_api.h" +#include + +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +/* + * Mapping Linux AC interpretation to SME AC. + * Host has 5 tx queues, 4 flow-controlled queues for regular traffic and + * one non-flow-controlled queue for high priority control traffic(EOPOL, DHCP). + * The fifth queue is mapped to AC_VO to allow for proper prioritization. + */ +const uint8_t hdd_qdisc_ac_to_tl_ac[] = { + SME_AC_VO, + SME_AC_VI, + SME_AC_BE, + SME_AC_BK, + SME_AC_VO, +}; + +#else +const uint8_t hdd_qdisc_ac_to_tl_ac[] = { + SME_AC_VO, + SME_AC_VI, + SME_AC_BE, + SME_AC_BK, +}; + +#endif + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +void hdd_register_hl_netdev_fc_timer(struct hdd_adapter *adapter, + qdf_mc_timer_callback_t timer_callback) +{ + if (!adapter->tx_flow_timer_initialized) { + qdf_mc_timer_init(&adapter->tx_flow_control_timer, + QDF_TIMER_TYPE_SW, timer_callback, adapter); + adapter->tx_flow_timer_initialized = true; + } +} + +/** + * hdd_deregister_hl_netdev_fc_timer() - Deregister HL Flow Control Timer + * @adapter: adapter handle + * + * Return: none + */ +void hdd_deregister_hl_netdev_fc_timer(struct hdd_adapter *adapter) +{ + if (adapter->tx_flow_timer_initialized) { + qdf_mc_timer_stop(&adapter->tx_flow_control_timer); + qdf_mc_timer_destroy(&adapter->tx_flow_control_timer); + adapter->tx_flow_timer_initialized = false; + } +} + +/** + * hdd_tx_resume_timer_expired_handler() - TX Q resume timer handler + * @adapter_context: pointer to vdev adapter + * + * Return: None + */ +void hdd_tx_resume_timer_expired_handler(void *adapter_context) +{ + struct hdd_adapter *adapter = (struct hdd_adapter *)adapter_context; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + u32 p_qpaused; + u32 np_qpaused; + + if (!adapter) { + hdd_err("invalid adapter context"); + return; + } + + cdp_display_stats(soc, CDP_DUMP_TX_FLOW_POOL_INFO, + QDF_STATS_VERBOSITY_LEVEL_LOW); + wlan_hdd_display_netif_queue_history(hdd_ctx, + QDF_STATS_VERBOSITY_LEVEL_LOW); + hdd_debug("Enabling queues"); + spin_lock_bh(&adapter->pause_map_lock); + p_qpaused = adapter->pause_map & BIT(WLAN_DATA_FLOW_CONTROL_PRIORITY); + np_qpaused = adapter->pause_map & BIT(WLAN_DATA_FLOW_CONTROL); + spin_unlock_bh(&adapter->pause_map_lock); + + if (p_qpaused) { + wlan_hdd_netif_queue_control(adapter, + WLAN_NETIF_PRIORITY_QUEUE_ON, + WLAN_DATA_FLOW_CONTROL_PRIORITY); + cdp_hl_fc_set_os_queue_status(soc, + adapter->vdev_id, + WLAN_NETIF_PRIORITY_QUEUE_ON); + } + if (np_qpaused) { + wlan_hdd_netif_queue_control(adapter, + WLAN_WAKE_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); + cdp_hl_fc_set_os_queue_status(soc, + adapter->vdev_id, + WLAN_WAKE_NON_PRIORITY_QUEUE); + } +} + +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +/** + * hdd_tx_resume_timer_expired_handler() - TX Q resume timer handler + * @adapter_context: pointer to vdev adapter + * + * If Blocked OS Q is not resumed during timeout period, to prevent + * permanent stall, resume OS Q forcefully. + * + * Return: None + */ +void hdd_tx_resume_timer_expired_handler(void *adapter_context) +{ + struct hdd_adapter *adapter = (struct hdd_adapter *) adapter_context; + + if (!adapter) { + /* INVALID ARG */ + return; + } + + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); +} + +/** + * hdd_tx_resume_false() - Resume OS TX Q false leads to queue disabling + * @adapter: pointer to hdd adapter + * @tx_resume: TX Q resume trigger + * + * + * Return: None + */ +static void +hdd_tx_resume_false(struct hdd_adapter *adapter, bool tx_resume) +{ + if (true == tx_resume) + return; + + /* Pause TX */ + hdd_debug("Disabling queues"); + wlan_hdd_netif_queue_control(adapter, WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + + if (QDF_TIMER_STATE_STOPPED == + qdf_mc_timer_get_current_state(&adapter-> + tx_flow_control_timer)) { + QDF_STATUS status; + + status = qdf_mc_timer_start(&adapter->tx_flow_control_timer, + WLAN_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); + + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to start tx_flow_control_timer"); + else + adapter->hdd_stats.tx_rx_stats.txflow_timer_cnt++; + } + + adapter->hdd_stats.tx_rx_stats.txflow_pause_cnt++; + adapter->hdd_stats.tx_rx_stats.is_txflow_paused = true; +} + +static inline struct sk_buff *hdd_skb_orphan(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int need_orphan = 0; + + if (adapter->tx_flow_low_watermark > 0) { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 19, 0)) + /* + * The TCP TX throttling logic is changed a little after + * 3.19-rc1 kernel, the TCP sending limit will be smaller, + * which will throttle the TCP packets to the host driver. + * The TCP UP LINK throughput will drop heavily. In order to + * fix this issue, need to orphan the socket buffer asap, which + * will call skb's destructor to notify the TCP stack that the + * SKB buffer is unowned. And then the TCP stack will pump more + * packets to host driver. + * + * The TX packets might be dropped for UDP case in the iperf + * testing. So need to be protected by follow control. + */ + need_orphan = 1; +#else + if (hdd_ctx->config->tx_orphan_enable) + need_orphan = 1; +#endif + } else if (hdd_ctx->config->tx_orphan_enable) { + if (qdf_nbuf_is_ipv4_tcp_pkt(skb) || + qdf_nbuf_is_ipv6_tcp_pkt(skb)) + need_orphan = 1; + } + + if (need_orphan) { + skb_orphan(skb); + ++adapter->hdd_stats.tx_rx_stats.tx_orphaned; + } else + skb = skb_unshare(skb, GFP_ATOMIC); + + return skb; +} + +/** + * hdd_tx_resume_cb() - Resume OS TX Q. + * @adapter_context: pointer to vdev apdapter + * @tx_resume: TX Q resume trigger + * + * Q was stopped due to WLAN TX path low resource condition + * + * Return: None + */ +void hdd_tx_resume_cb(void *adapter_context, bool tx_resume) +{ + struct hdd_adapter *adapter = (struct hdd_adapter *) adapter_context; + struct hdd_station_ctx *hdd_sta_ctx = NULL; + + if (!adapter) { + /* INVALID ARG */ + return; + } + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + + /* Resume TX */ + if (true == tx_resume) { + if (QDF_TIMER_STATE_STOPPED != + qdf_mc_timer_get_current_state(&adapter-> + tx_flow_control_timer)) { + qdf_mc_timer_stop(&adapter->tx_flow_control_timer); + } + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + adapter->hdd_stats.tx_rx_stats.is_txflow_paused = false; + adapter->hdd_stats.tx_rx_stats.txflow_unpause_cnt++; + } + hdd_tx_resume_false(adapter, tx_resume); +} + +bool hdd_tx_flow_control_is_pause(void *adapter_context) +{ + struct hdd_adapter *adapter = (struct hdd_adapter *) adapter_context; + + if ((!adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + /* INVALID ARG */ + hdd_err("invalid adapter %pK", adapter); + return false; + } + + return adapter->pause_map & (1 << WLAN_DATA_FLOW_CONTROL); +} + +void hdd_register_tx_flow_control(struct hdd_adapter *adapter, + qdf_mc_timer_callback_t timer_callback, + ol_txrx_tx_flow_control_fp flow_control_fp, + ol_txrx_tx_flow_control_is_pause_fp flow_control_is_pause_fp) +{ + if (adapter->tx_flow_timer_initialized == false) { + qdf_mc_timer_init(&adapter->tx_flow_control_timer, + QDF_TIMER_TYPE_SW, + timer_callback, + adapter); + adapter->tx_flow_timer_initialized = true; + } + cdp_fc_register(cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id, flow_control_fp, adapter, + flow_control_is_pause_fp); +} + +/** + * hdd_deregister_tx_flow_control() - Deregister TX Flow control + * @adapter: adapter handle + * + * Return: none + */ +void hdd_deregister_tx_flow_control(struct hdd_adapter *adapter) +{ + cdp_fc_deregister(cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id); + if (adapter->tx_flow_timer_initialized == true) { + qdf_mc_timer_stop(&adapter->tx_flow_control_timer); + qdf_mc_timer_destroy(&adapter->tx_flow_control_timer); + adapter->tx_flow_timer_initialized = false; + } +} + +void hdd_get_tx_resource(struct hdd_adapter *adapter, + struct qdf_mac_addr *mac_addr, uint16_t timer_value) +{ + if (false == + cdp_fc_get_tx_resource(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID, + *mac_addr, + adapter->tx_flow_low_watermark, + adapter->tx_flow_hi_watermark_offset)) { + hdd_debug("Disabling queues lwm %d hwm offset %d", + adapter->tx_flow_low_watermark, + adapter->tx_flow_hi_watermark_offset); + wlan_hdd_netif_queue_control(adapter, WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + if ((adapter->tx_flow_timer_initialized == true) && + (QDF_TIMER_STATE_STOPPED == + qdf_mc_timer_get_current_state(&adapter-> + tx_flow_control_timer))) { + qdf_mc_timer_start(&adapter->tx_flow_control_timer, + timer_value); + adapter->hdd_stats.tx_rx_stats.txflow_timer_cnt++; + adapter->hdd_stats.tx_rx_stats.txflow_pause_cnt++; + adapter->hdd_stats.tx_rx_stats.is_txflow_paused = true; + } + } +} +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + +uint32_t hdd_txrx_get_tx_ack_count(struct hdd_adapter *adapter) +{ + return cdp_get_tx_ack_stats(cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id); +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +/** + * qdf_event_eapol_log() - send event to wlan diag + * @skb: skb ptr + * @dir: direction + * @eapol_key_info: eapol key info + * + * Return: None + */ +void hdd_event_eapol_log(struct sk_buff *skb, enum qdf_proto_dir dir) +{ + int16_t eapol_key_info; + + WLAN_HOST_DIAG_EVENT_DEF(wlan_diag_event, struct host_event_wlan_eapol); + + if ((dir == QDF_TX && + (QDF_NBUF_CB_PACKET_TYPE_EAPOL != + QDF_NBUF_CB_GET_PACKET_TYPE(skb)))) + return; + else if (!qdf_nbuf_is_ipv4_eapol_pkt(skb)) + return; + + eapol_key_info = (uint16_t)(*(uint16_t *) + (skb->data + EAPOL_KEY_INFO_OFFSET)); + + wlan_diag_event.event_sub_type = + (dir == QDF_TX ? + WIFI_EVENT_DRIVER_EAPOL_FRAME_TRANSMIT_REQUESTED : + WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED); + wlan_diag_event.eapol_packet_type = (uint8_t)(*(uint8_t *) + (skb->data + EAPOL_PACKET_TYPE_OFFSET)); + wlan_diag_event.eapol_key_info = eapol_key_info; + wlan_diag_event.eapol_rate = 0; + qdf_mem_copy(wlan_diag_event.dest_addr, + (skb->data + QDF_NBUF_DEST_MAC_OFFSET), + sizeof(wlan_diag_event.dest_addr)); + qdf_mem_copy(wlan_diag_event.src_addr, + (skb->data + QDF_NBUF_SRC_MAC_OFFSET), + sizeof(wlan_diag_event.src_addr)); + + WLAN_HOST_DIAG_EVENT_REPORT(&wlan_diag_event, EVENT_WLAN_EAPOL); +} +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +int hdd_set_udp_qos_upgrade_config(struct hdd_adapter *adapter, + uint8_t priority) +{ + if (adapter->device_mode != QDF_STA_MODE) { + hdd_info_rl("Data priority upgrade only allowed in STA mode:%d", + adapter->device_mode); + return -EINVAL; + } + + if (priority >= QCA_WLAN_AC_ALL) { + hdd_err_rl("Invlid data priority: %d", priority); + return -EINVAL; + } + + adapter->upgrade_udp_qos_threshold = priority; + + hdd_debug("UDP packets qos upgrade to: %d", priority); + + return 0; +} + +/** + * wlan_hdd_classify_pkt() - classify packet + * @skb - sk buff + * + * Return: none + */ +void wlan_hdd_classify_pkt(struct sk_buff *skb) +{ + struct ethhdr *eh = (struct ethhdr *)skb->data; + + qdf_mem_zero(skb->cb, sizeof(skb->cb)); + + /* check destination mac address is broadcast/multicast */ + if (is_broadcast_ether_addr((uint8_t *)eh)) + QDF_NBUF_CB_GET_IS_BCAST(skb) = true; + else if (is_multicast_ether_addr((uint8_t *)eh)) + QDF_NBUF_CB_GET_IS_MCAST(skb) = true; + + if (qdf_nbuf_is_ipv4_arp_pkt(skb)) + QDF_NBUF_CB_GET_PACKET_TYPE(skb) = + QDF_NBUF_CB_PACKET_TYPE_ARP; + else if (qdf_nbuf_is_ipv4_dhcp_pkt(skb)) + QDF_NBUF_CB_GET_PACKET_TYPE(skb) = + QDF_NBUF_CB_PACKET_TYPE_DHCP; + else if (qdf_nbuf_is_ipv4_eapol_pkt(skb)) + QDF_NBUF_CB_GET_PACKET_TYPE(skb) = + QDF_NBUF_CB_PACKET_TYPE_EAPOL; + else if (qdf_nbuf_is_ipv4_wapi_pkt(skb)) + QDF_NBUF_CB_GET_PACKET_TYPE(skb) = + QDF_NBUF_CB_PACKET_TYPE_WAPI; + else if (qdf_nbuf_is_icmp_pkt(skb)) + QDF_NBUF_CB_GET_PACKET_TYPE(skb) = + QDF_NBUF_CB_PACKET_TYPE_ICMP; + else if (qdf_nbuf_is_icmpv6_pkt(skb)) + QDF_NBUF_CB_GET_PACKET_TYPE(skb) = + QDF_NBUF_CB_PACKET_TYPE_ICMPv6; +} + +/** + * hdd_clear_tx_rx_connectivity_stats() - clear connectivity stats + * @hdd_ctx: pointer to HDD Station Context + * + * Return: None + */ +static void hdd_clear_tx_rx_connectivity_stats(struct hdd_adapter *adapter) +{ + hdd_debug("Clear txrx connectivity stats"); + qdf_mem_zero(&adapter->hdd_stats.hdd_arp_stats, + sizeof(adapter->hdd_stats.hdd_arp_stats)); + qdf_mem_zero(&adapter->hdd_stats.hdd_dns_stats, + sizeof(adapter->hdd_stats.hdd_dns_stats)); + qdf_mem_zero(&adapter->hdd_stats.hdd_tcp_stats, + sizeof(adapter->hdd_stats.hdd_tcp_stats)); + qdf_mem_zero(&adapter->hdd_stats.hdd_icmpv4_stats, + sizeof(adapter->hdd_stats.hdd_icmpv4_stats)); + adapter->pkt_type_bitmap = 0; + adapter->track_arp_ip = 0; + qdf_mem_zero(adapter->dns_payload, adapter->track_dns_domain_len); + adapter->track_dns_domain_len = 0; + adapter->track_src_port = 0; + adapter->track_dest_port = 0; + adapter->track_dest_ipv4 = 0; +} + +void hdd_reset_all_adapters_connectivity_stats(struct hdd_context *hdd_ctx) +{ + struct hdd_adapter *adapter = NULL, *next = NULL; + QDF_STATUS status; + + hdd_enter(); + + status = hdd_get_front_adapter(hdd_ctx, &adapter); + + while (adapter && QDF_STATUS_SUCCESS == status) { + hdd_clear_tx_rx_connectivity_stats(adapter); + status = hdd_get_next_adapter(hdd_ctx, adapter, &next); + adapter = next; + } + + hdd_exit(); +} + +/** + * hdd_is_tx_allowed() - check if Tx is allowed based on current peer state + * @skb: pointer to OS packet (sk_buff) + * @vdev_id: virtual interface id + * @peer_mac: Peer mac address + * + * This function gets the peer state from DP and check if it is either + * in OL_TXRX_PEER_STATE_CONN or OL_TXRX_PEER_STATE_AUTH. Only EAP packets + * are allowed when peer_state is OL_TXRX_PEER_STATE_CONN. All packets + * allowed when peer_state is OL_TXRX_PEER_STATE_AUTH. + * + * Return: true if Tx is allowed and false otherwise. + */ +static inline bool hdd_is_tx_allowed(struct sk_buff *skb, uint8_t vdev_id, + uint8_t *peer_mac) +{ + enum ol_txrx_peer_state peer_state; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + QDF_BUG(soc); + + peer_state = cdp_peer_state_get(soc, vdev_id, peer_mac); + if (likely(OL_TXRX_PEER_STATE_AUTH == peer_state)) + return true; + if (OL_TXRX_PEER_STATE_CONN == peer_state && + (ntohs(skb->protocol) == HDD_ETHERTYPE_802_1_X + || IS_HDD_ETHERTYPE_WAI(skb))) + return true; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_INFO_HIGH, + FL("Invalid peer state for Tx: %d"), peer_state); + return false; +} + +/** + * hdd_tx_rx_is_dns_domain_name_match() - function to check whether dns + * domain name in the received skb matches with the tracking dns domain + * name or not + * + * @skb: pointer to skb + * @adapter: pointer to adapter + * + * Returns: true if matches else false + */ +static bool hdd_tx_rx_is_dns_domain_name_match(struct sk_buff *skb, + struct hdd_adapter *adapter) +{ + uint8_t *domain_name; + + if (adapter->track_dns_domain_len == 0) + return false; + + /* check OOB , is strncmp accessing data more than skb->len */ + if ((adapter->track_dns_domain_len + + QDF_NBUF_PKT_DNS_NAME_OVER_UDP_OFFSET) > qdf_nbuf_len(skb)) + return false; + + domain_name = qdf_nbuf_get_dns_domain_name(skb, + adapter->track_dns_domain_len); + if (strncmp(domain_name, adapter->dns_payload, + adapter->track_dns_domain_len) == 0) + return true; + else + return false; +} + +void hdd_tx_rx_collect_connectivity_stats_info(struct sk_buff *skb, + void *context, + enum connectivity_stats_pkt_status action, + uint8_t *pkt_type) +{ + uint32_t pkt_type_bitmap; + struct hdd_adapter *adapter = NULL; + + adapter = (struct hdd_adapter *)context; + if (unlikely(adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "Magic cookie(%x) for adapter sanity verification is invalid", + adapter->magic); + return; + } + + /* ARP tracking is done already. */ + pkt_type_bitmap = adapter->pkt_type_bitmap; + pkt_type_bitmap &= ~CONNECTIVITY_CHECK_SET_ARP; + + if (!pkt_type_bitmap) + return; + + switch (action) { + case PKT_TYPE_REQ: + case PKT_TYPE_TX_HOST_FW_SENT: + if (qdf_nbuf_is_icmp_pkt(skb)) { + if (qdf_nbuf_data_is_icmpv4_req(skb) && + (adapter->track_dest_ipv4 == + qdf_nbuf_get_icmpv4_tgt_ip(skb))) { + *pkt_type = CONNECTIVITY_CHECK_SET_ICMPV4; + if (action == PKT_TYPE_REQ) { + ++adapter->hdd_stats.hdd_icmpv4_stats. + tx_icmpv4_req_count; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : ICMPv4 Req packet", + __func__); + } else + /* host receives tx completion */ + ++adapter->hdd_stats.hdd_icmpv4_stats. + tx_host_fw_sent; + } + } else if (qdf_nbuf_is_ipv4_tcp_pkt(skb)) { + if (qdf_nbuf_data_is_tcp_syn(skb) && + (adapter->track_dest_port == + qdf_nbuf_data_get_tcp_dst_port(skb))) { + *pkt_type = CONNECTIVITY_CHECK_SET_TCP_SYN; + if (action == PKT_TYPE_REQ) { + ++adapter->hdd_stats.hdd_tcp_stats. + tx_tcp_syn_count; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : TCP Syn packet", + __func__); + } else + /* host receives tx completion */ + ++adapter->hdd_stats.hdd_tcp_stats. + tx_tcp_syn_host_fw_sent; + } else if ((adapter->hdd_stats.hdd_tcp_stats. + is_tcp_syn_ack_rcv || adapter->hdd_stats. + hdd_tcp_stats.is_tcp_ack_sent) && + qdf_nbuf_data_is_tcp_ack(skb) && + (adapter->track_dest_port == + qdf_nbuf_data_get_tcp_dst_port(skb))) { + *pkt_type = CONNECTIVITY_CHECK_SET_TCP_ACK; + if (action == PKT_TYPE_REQ && + adapter->hdd_stats.hdd_tcp_stats. + is_tcp_syn_ack_rcv) { + ++adapter->hdd_stats.hdd_tcp_stats. + tx_tcp_ack_count; + adapter->hdd_stats.hdd_tcp_stats. + is_tcp_syn_ack_rcv = false; + adapter->hdd_stats.hdd_tcp_stats. + is_tcp_ack_sent = true; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : TCP Ack packet", + __func__); + } else if (action == PKT_TYPE_TX_HOST_FW_SENT && + adapter->hdd_stats.hdd_tcp_stats. + is_tcp_ack_sent) { + /* host receives tx completion */ + ++adapter->hdd_stats.hdd_tcp_stats. + tx_tcp_ack_host_fw_sent; + adapter->hdd_stats.hdd_tcp_stats. + is_tcp_ack_sent = false; + } + } + } else if (qdf_nbuf_is_ipv4_udp_pkt(skb)) { + if (qdf_nbuf_data_is_dns_query(skb) && + hdd_tx_rx_is_dns_domain_name_match(skb, adapter)) { + *pkt_type = CONNECTIVITY_CHECK_SET_DNS; + if (action == PKT_TYPE_REQ) { + ++adapter->hdd_stats.hdd_dns_stats. + tx_dns_req_count; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : DNS query packet", + __func__); + } else + /* host receives tx completion */ + ++adapter->hdd_stats.hdd_dns_stats. + tx_host_fw_sent; + } + } + break; + + case PKT_TYPE_RSP: + if (qdf_nbuf_is_icmp_pkt(skb)) { + if (qdf_nbuf_data_is_icmpv4_rsp(skb) && + (adapter->track_dest_ipv4 == + qdf_nbuf_get_icmpv4_src_ip(skb))) { + ++adapter->hdd_stats.hdd_icmpv4_stats. + rx_icmpv4_rsp_count; + *pkt_type = + CONNECTIVITY_CHECK_SET_ICMPV4; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : ICMPv4 Res packet", __func__); + } + } else if (qdf_nbuf_is_ipv4_tcp_pkt(skb)) { + if (qdf_nbuf_data_is_tcp_syn_ack(skb) && + (adapter->track_dest_port == + qdf_nbuf_data_get_tcp_src_port(skb))) { + ++adapter->hdd_stats.hdd_tcp_stats. + rx_tcp_syn_ack_count; + adapter->hdd_stats.hdd_tcp_stats. + is_tcp_syn_ack_rcv = true; + *pkt_type = + CONNECTIVITY_CHECK_SET_TCP_SYN_ACK; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : TCP Syn ack packet", __func__); + } + } else if (qdf_nbuf_is_ipv4_udp_pkt(skb)) { + if (qdf_nbuf_data_is_dns_response(skb) && + hdd_tx_rx_is_dns_domain_name_match(skb, adapter)) { + ++adapter->hdd_stats.hdd_dns_stats. + rx_dns_rsp_count; + *pkt_type = CONNECTIVITY_CHECK_SET_DNS; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : DNS response packet", __func__); + } + } + break; + + case PKT_TYPE_TX_DROPPED: + switch (*pkt_type) { + case CONNECTIVITY_CHECK_SET_ICMPV4: + ++adapter->hdd_stats.hdd_icmpv4_stats.tx_dropped; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : ICMPv4 Req packet dropped", __func__); + break; + case CONNECTIVITY_CHECK_SET_TCP_SYN: + ++adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_dropped; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : TCP syn packet dropped", __func__); + break; + case CONNECTIVITY_CHECK_SET_TCP_ACK: + ++adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_dropped; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : TCP ack packet dropped", __func__); + break; + case CONNECTIVITY_CHECK_SET_DNS: + ++adapter->hdd_stats.hdd_dns_stats.tx_dropped; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : DNS query packet dropped", __func__); + break; + default: + break; + } + break; + case PKT_TYPE_RX_DELIVERED: + switch (*pkt_type) { + case CONNECTIVITY_CHECK_SET_ICMPV4: + ++adapter->hdd_stats.hdd_icmpv4_stats.rx_delivered; + break; + case CONNECTIVITY_CHECK_SET_TCP_SYN_ACK: + ++adapter->hdd_stats.hdd_tcp_stats.rx_delivered; + break; + case CONNECTIVITY_CHECK_SET_DNS: + ++adapter->hdd_stats.hdd_dns_stats.rx_delivered; + break; + default: + break; + } + break; + case PKT_TYPE_RX_REFUSED: + switch (*pkt_type) { + case CONNECTIVITY_CHECK_SET_ICMPV4: + ++adapter->hdd_stats.hdd_icmpv4_stats.rx_refused; + break; + case CONNECTIVITY_CHECK_SET_TCP_SYN_ACK: + ++adapter->hdd_stats.hdd_tcp_stats.rx_refused; + break; + case CONNECTIVITY_CHECK_SET_DNS: + ++adapter->hdd_stats.hdd_dns_stats.rx_refused; + break; + default: + break; + } + break; + case PKT_TYPE_TX_ACK_CNT: + switch (*pkt_type) { + case CONNECTIVITY_CHECK_SET_ICMPV4: + ++adapter->hdd_stats.hdd_icmpv4_stats.tx_ack_cnt; + break; + case CONNECTIVITY_CHECK_SET_TCP_SYN: + ++adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_ack_cnt; + break; + case CONNECTIVITY_CHECK_SET_TCP_ACK: + ++adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_ack_cnt; + break; + case CONNECTIVITY_CHECK_SET_DNS: + ++adapter->hdd_stats.hdd_dns_stats.tx_ack_cnt; + break; + default: + break; + } + break; + default: + break; + } +} + +/** + * hdd_is_xmit_allowed_on_ndi() - Verify if xmit is allowed on NDI + * @adapter: The adapter structure + * + * Return: True if xmit is allowed on NDI and false otherwise + */ +static bool hdd_is_xmit_allowed_on_ndi(struct hdd_adapter *adapter) +{ + enum nan_datapath_state state; + + state = ucfg_nan_get_ndi_state(adapter->vdev); + return (state == NAN_DATA_NDI_CREATED_STATE || + state == NAN_DATA_CONNECTED_STATE || + state == NAN_DATA_CONNECTING_STATE || + state == NAN_DATA_PEER_CREATE_STATE); +} + +/** + * hdd_get_transmit_mac_addr() - Get the mac address to validate the xmit + * @adapter: The adapter structure + * @skb: The network buffer + * @mac_addr_tx_allowed: The mac address to be filled + * + * Return: None + */ +static +void hdd_get_transmit_mac_addr(struct hdd_adapter *adapter, struct sk_buff *skb, + struct qdf_mac_addr *mac_addr_tx_allowed) +{ + struct hdd_station_ctx *sta_ctx = &adapter->session.station; + bool is_mc_bc_addr = false; + + if (QDF_NBUF_CB_GET_IS_BCAST(skb) || QDF_NBUF_CB_GET_IS_MCAST(skb)) + is_mc_bc_addr = true; + + if (adapter->device_mode == QDF_IBSS_MODE) { + if (is_mc_bc_addr) + qdf_copy_macaddr(mac_addr_tx_allowed, + &adapter->mac_addr); + else + qdf_copy_macaddr(mac_addr_tx_allowed, + (struct qdf_mac_addr *)skb->data); + } else if (adapter->device_mode == QDF_NDI_MODE && + hdd_is_xmit_allowed_on_ndi(adapter)) { + if (is_mc_bc_addr) + qdf_copy_macaddr(mac_addr_tx_allowed, + &adapter->mac_addr); + else + qdf_copy_macaddr(mac_addr_tx_allowed, + (struct qdf_mac_addr *)skb->data); + } else { + if (sta_ctx->conn_info.conn_state == + eConnectionState_Associated) + qdf_copy_macaddr(mac_addr_tx_allowed, + &sta_ctx->conn_info.bssid); + } +} + +#ifdef HANDLE_BROADCAST_EAPOL_TX_FRAME +/** + * wlan_hdd_fix_broadcast_eapol() - Fix broadcast eapol + * @adapter: pointer to adapter + * @skb: pointer to OS packet (sk_buff) + * + * Override DA of broadcast eapol with bssid addr. + * + * Return: None + */ +static void wlan_hdd_fix_broadcast_eapol(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + struct ethhdr *eh = (struct ethhdr *)skb->data; + unsigned char *ap_mac_addr = + &adapter->session.station.conn_info.bssid.bytes[0]; + + if (qdf_unlikely((QDF_NBUF_CB_GET_PACKET_TYPE(skb) == + QDF_NBUF_CB_PACKET_TYPE_EAPOL) && + QDF_NBUF_CB_GET_IS_BCAST(skb))) { + hdd_debug("SA: "QDF_MAC_ADDR_FMT " override DA: "QDF_MAC_ADDR_FMT " with AP mac address "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(&eh->h_source[0]), + QDF_MAC_ADDR_REF(&eh->h_dest[0]), + QDF_MAC_ADDR_REF(ap_mac_addr)); + + qdf_mem_copy(&eh->h_dest, ap_mac_addr, QDF_MAC_ADDR_SIZE); + } +} +#else +static void wlan_hdd_fix_broadcast_eapol(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ +} +#endif /* HANDLE_BROADCAST_EAPOL_TX_FRAME */ + +/** + * __hdd_hard_start_xmit() - Transmit a frame + * @skb: pointer to OS packet (sk_buff) + * @dev: pointer to network device + * + * Function registered with the Linux OS for transmitting + * packets. This version of the function directly passes + * the packet to Transport Layer. + * In case of any packet drop or error, log the error with + * INFO HIGH/LOW/MEDIUM to avoid excessive logging in kmsg. + * + * Return: None + */ +static void __hdd_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + QDF_STATUS status; + sme_ac_enum_type ac; + enum sme_qos_wmmuptype up; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + bool granted; + struct hdd_station_ctx *sta_ctx = &adapter->session.station; + struct qdf_mac_addr mac_addr; + struct qdf_mac_addr mac_addr_tx_allowed = QDF_MAC_ADDR_ZERO_INIT; + uint8_t pkt_type = 0; + bool is_arp = false; + struct wlan_objmgr_vdev *vdev; + struct hdd_context *hdd_ctx; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + +#ifdef QCA_WIFI_FTM + if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { + kfree_skb(skb); + return; + } +#endif + + ++adapter->hdd_stats.tx_rx_stats.tx_called; + adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt = 0; + qdf_mem_copy(mac_addr.bytes, skb->data, sizeof(mac_addr.bytes)); + + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state() || + cds_is_load_or_unload_in_progress()) { + QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_HDD_DATA, + "Recovery/(Un)load in progress, dropping the packet"); + goto drop_pkt; + } + + hdd_ctx = adapter->hdd_ctx; + if (wlan_hdd_validate_context(hdd_ctx)) { + QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_HDD_DATA, + "Invalid HDD context"); + goto drop_pkt; + } + + if (hdd_ctx->hdd_wlan_suspended) { + hdd_err_rl("Device is system suspended, drop pkt"); + goto drop_pkt; + } + + wlan_hdd_classify_pkt(skb); + if (QDF_NBUF_CB_GET_PACKET_TYPE(skb) == QDF_NBUF_CB_PACKET_TYPE_ARP) { + if (qdf_nbuf_data_is_arp_req(skb) && + (adapter->track_arp_ip == qdf_nbuf_get_arp_tgt_ip(skb))) { + is_arp = true; + ++adapter->hdd_stats.hdd_arp_stats.tx_arp_req_count; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s : ARP packet", __func__); + } + } + /* track connectivity stats */ + if (adapter->pkt_type_bitmap) + hdd_tx_rx_collect_connectivity_stats_info(skb, adapter, + PKT_TYPE_REQ, &pkt_type); + + hdd_get_transmit_mac_addr(adapter, skb, &mac_addr_tx_allowed); + if (qdf_is_macaddr_zero(&mac_addr_tx_allowed)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_INFO_HIGH, + "tx not allowed, transmit operation suspended"); + goto drop_pkt; + } + + hdd_get_tx_resource(adapter, &mac_addr, + WLAN_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); + + /* Get TL AC corresponding to Qdisc queue index/AC. */ + ac = hdd_qdisc_ac_to_tl_ac[skb->queue_mapping]; + + if (!qdf_nbuf_ipa_owned_get(skb)) { + skb = hdd_skb_orphan(adapter, skb); + if (!skb) + goto drop_pkt_accounting; + } + + /* + * Add SKB to internal tracking table before further processing + * in WLAN driver. + */ + qdf_net_buf_debug_acquire_skb(skb, __FILE__, __LINE__); + + /* + * user priority from IP header, which is already extracted and set from + * select_queue call back function + */ + up = skb->priority; + + ++adapter->hdd_stats.tx_rx_stats.tx_classified_ac[ac]; +#ifdef HDD_WMM_DEBUG + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "%s: Classified as ac %d up %d", __func__, ac, up); +#endif /* HDD_WMM_DEBUG */ + + if (HDD_PSB_CHANGED == adapter->psb_changed) { + /* + * Function which will determine acquire admittance for a + * WMM AC is required or not based on psb configuration done + * in the framework + */ + hdd_wmm_acquire_access_required(adapter, ac); + } + /* + * Make sure we already have access to this access category + * or it is EAPOL or WAPI frame during initial authentication which + * can have artifically boosted higher qos priority. + */ + + if (((adapter->psb_changed & (1 << ac)) && + likely(adapter->hdd_wmm_status.ac_status[ac]. + is_access_allowed)) || + ((sta_ctx->conn_info.is_authenticated == false) && + (QDF_NBUF_CB_PACKET_TYPE_EAPOL == + QDF_NBUF_CB_GET_PACKET_TYPE(skb) || + QDF_NBUF_CB_PACKET_TYPE_WAPI == + QDF_NBUF_CB_GET_PACKET_TYPE(skb)))) { + granted = true; + } else { + status = hdd_wmm_acquire_access(adapter, ac, &granted); + adapter->psb_changed |= (1 << ac); + } + + if (!granted) { + bool isDefaultAc = false; + /* + * ADDTS request for this AC is sent, for now + * send this packet through next available lower + * Access category until ADDTS negotiation completes. + */ + while (!likely + (adapter->hdd_wmm_status.ac_status[ac]. + is_access_allowed)) { + switch (ac) { + case SME_AC_VO: + ac = SME_AC_VI; + up = SME_QOS_WMM_UP_VI; + break; + case SME_AC_VI: + ac = SME_AC_BE; + up = SME_QOS_WMM_UP_BE; + break; + case SME_AC_BE: + ac = SME_AC_BK; + up = SME_QOS_WMM_UP_BK; + break; + default: + ac = SME_AC_BK; + up = SME_QOS_WMM_UP_BK; + isDefaultAc = true; + break; + } + if (isDefaultAc) + break; + } + skb->priority = up; + skb->queue_mapping = hdd_linux_up_to_ac_map[up]; + } + + adapter->stats.tx_bytes += skb->len; + + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + ucfg_tdls_update_tx_pkt_cnt(vdev, &mac_addr); + hdd_objmgr_put_vdev(vdev); + } + + if (qdf_nbuf_is_tso(skb)) { + adapter->stats.tx_packets += qdf_nbuf_get_tso_num_seg(skb); + } else { + ++adapter->stats.tx_packets; + hdd_ctx->no_tx_offload_pkt_cnt++; + } + + hdd_event_eapol_log(skb, QDF_TX); + QDF_NBUF_CB_TX_PACKET_TRACK(skb) = QDF_NBUF_TX_PKT_DATA_TRACK; + QDF_NBUF_UPDATE_TX_PKT_COUNT(skb, QDF_NBUF_TX_PKT_HDD); + + qdf_dp_trace_set_track(skb, QDF_TX); + + DPTRACE(qdf_dp_trace(skb, QDF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, qdf_nbuf_data_addr(skb), + sizeof(qdf_nbuf_data(skb)), + QDF_TX)); + + if (!hdd_is_tx_allowed(skb, adapter->vdev_id, + mac_addr_tx_allowed.bytes)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + FL("Tx not allowed for sta: " + QDF_MAC_ADDR_FMT), QDF_MAC_ADDR_REF( + mac_addr_tx_allowed.bytes)); + ++adapter->hdd_stats.tx_rx_stats.tx_dropped_ac[ac]; + goto drop_pkt_and_release_skb; + } + + /* check whether need to linearize skb, like non-linear udp data */ + if (hdd_skb_nontso_linearize(skb) != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_INFO_HIGH, + "%s: skb %pK linearize failed. drop the pkt", + __func__, skb); + ++adapter->hdd_stats.tx_rx_stats.tx_dropped_ac[ac]; + goto drop_pkt_and_release_skb; + } + + /* + * If a transmit function is not registered, drop packet + */ + if (!adapter->tx_fn) { + QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: TX function not registered by the data path", + __func__); + ++adapter->hdd_stats.tx_rx_stats.tx_dropped_ac[ac]; + goto drop_pkt_and_release_skb; + } + + wlan_hdd_fix_broadcast_eapol(adapter, skb); + + if (adapter->tx_fn(soc, adapter->vdev_id, (qdf_nbuf_t)skb)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Failed to send packet to txrx for sta_id: " + QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(mac_addr.bytes)); + ++adapter->hdd_stats.tx_rx_stats.tx_dropped_ac[ac]; + goto drop_pkt_and_release_skb; + } + + netif_trans_update(dev); + + wlan_hdd_sar_unsolicited_timer_start(hdd_ctx); + + return; + +drop_pkt_and_release_skb: + qdf_net_buf_debug_release_skb(skb); +drop_pkt: + + /* track connectivity stats */ + if (adapter->pkt_type_bitmap) + hdd_tx_rx_collect_connectivity_stats_info(skb, adapter, + PKT_TYPE_TX_DROPPED, + &pkt_type); + qdf_dp_trace_data_pkt(skb, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_DP_TRACE_DROP_PACKET_RECORD, 0, + QDF_TX); + kfree_skb(skb); + +drop_pkt_accounting: + + ++adapter->stats.tx_dropped; + ++adapter->hdd_stats.tx_rx_stats.tx_dropped; + if (is_arp) { + ++adapter->hdd_stats.hdd_arp_stats.tx_dropped; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_INFO_HIGH, + "%s : ARP packet dropped", __func__); + } +} + +/** + * hdd_hard_start_xmit() - Wrapper function to protect + * __hdd_hard_start_xmit from SSR + * @skb: pointer to OS packet + * @net_dev: pointer to net_device structure + * + * Function called by OS if any packet needs to transmit. + * + * Return: Always returns NETDEV_TX_OK + */ +netdev_tx_t hdd_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev) +{ + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(net_dev, &vdev_sync)) { + hdd_debug_rl("Operation on net_dev is not permitted"); + kfree_skb(skb); + return NETDEV_TX_OK; + } + + __hdd_hard_start_xmit(skb, net_dev); + + osif_vdev_sync_op_stop(vdev_sync); + + return NETDEV_TX_OK; +} + +/** + * __hdd_tx_timeout() - TX timeout handler + * @dev: pointer to network device + * + * This function is registered as a netdev ndo_tx_timeout method, and + * is invoked by the kernel if the driver takes too long to transmit a + * frame. + * + * Return: None + */ +static void __hdd_tx_timeout(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + struct netdev_queue *txq; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + u64 diff_jiffies; + int i = 0; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (hdd_ctx->hdd_wlan_suspended) { + hdd_debug("Device is suspended, ignore WD timeout"); + return; + } + + TX_TIMEOUT_TRACE(dev, QDF_MODULE_ID_HDD_DATA); + DPTRACE(qdf_dp_trace(NULL, QDF_DP_TRACE_HDD_TX_TIMEOUT, + QDF_TRACE_DEFAULT_PDEV_ID, + NULL, 0, QDF_TX)); + + /* Getting here implies we disabled the TX queues for too + * long. Queues are disabled either because of disassociation + * or low resource scenarios. In case of disassociation it is + * ok to ignore this. But if associated, we have do possible + * recovery here + */ + + for (i = 0; i < NUM_TX_QUEUES; i++) { + txq = netdev_get_tx_queue(dev, i); + hdd_debug("Queue: %d status: %d txq->trans_start: %lu", + i, netif_tx_queue_stopped(txq), txq->trans_start); + } + + hdd_debug("carrier state: %d", netif_carrier_ok(dev)); + + wlan_hdd_display_adapter_netif_queue_history(adapter); + + cdp_dump_flow_pool_info(cds_get_context(QDF_MODULE_ID_SOC)); + + ++adapter->hdd_stats.tx_rx_stats.tx_timeout_cnt; + ++adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt; + + diff_jiffies = jiffies - + adapter->hdd_stats.tx_rx_stats.jiffies_last_txtimeout; + + if ((adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt > 1) && + (diff_jiffies > (HDD_TX_TIMEOUT * 2))) { + /* + * In case when there is no traffic is running, it may + * possible tx time-out may once happen and later system + * recovered then continuous tx timeout count has to be + * reset as it is gets modified only when traffic is running. + * If over a period of time if this count reaches to threshold + * then host triggers a false subsystem restart. In genuine + * time out case kernel will call the tx time-out back to back + * at interval of HDD_TX_TIMEOUT. Here now check if previous + * TX TIME out has occurred more than twice of HDD_TX_TIMEOUT + * back then host may recovered here from data stall. + */ + adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt = 0; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "Reset continuous tx timeout stat"); + } + + adapter->hdd_stats.tx_rx_stats.jiffies_last_txtimeout = jiffies; + + if (adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt > + HDD_TX_STALL_THRESHOLD) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "Data stall due to continuous TX timeouts"); + adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt = 0; + + if (cdp_cfg_get(soc, cfg_dp_enable_data_stall)) + cdp_post_data_stall_event(soc, + DATA_STALL_LOG_INDICATOR_HOST_DRIVER, + DATA_STALL_LOG_HOST_STA_TX_TIMEOUT, + OL_TXRX_PDEV_ID, 0xFF, + DATA_STALL_LOG_RECOVERY_TRIGGER_PDR); + } +} + +/** + * hdd_tx_timeout() - Wrapper function to protect __hdd_tx_timeout from SSR + * @net_dev: pointer to net_device structure + * + * Function called by OS if there is any timeout during transmission. + * Since HDD simply enqueues packet and returns control to OS right away, + * this would never be invoked + * + * Return: none + */ +void hdd_tx_timeout(struct net_device *net_dev) +{ + struct osif_vdev_sync *vdev_sync; + + if (osif_vdev_sync_op_start(net_dev, &vdev_sync)) + return; + + __hdd_tx_timeout(net_dev); + + osif_vdev_sync_op_stop(vdev_sync); +} + +/** + * @hdd_init_tx_rx() - Initialize Tx/RX module + * @adapter: pointer to adapter context + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_init_tx_rx(struct hdd_adapter *adapter) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!adapter) { + hdd_err("adapter is NULL"); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + return status; +} + +/** + * @hdd_deinit_tx_rx() - Deinitialize Tx/RX module + * @adapter: pointer to adapter context + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, + * QDF_STATUS_SUCCESS otherwise + */ +QDF_STATUS hdd_deinit_tx_rx(struct hdd_adapter *adapter) +{ + QDF_BUG(adapter); + if (!adapter) + return QDF_STATUS_E_FAILURE; + + adapter->tx_fn = NULL; + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +/** + * hdd_mon_rx_packet_cbk() - Receive callback registered with OL layer. + * @context: [in] pointer to qdf context + * @rxBuf: [in] pointer to rx qdf_nbuf + * + * TL will call this to notify the HDD when one or more packets were + * received for a registered STA. + * + * Return: QDF_STATUS_E_FAILURE if any errors encountered, QDF_STATUS_SUCCESS + * otherwise + */ +static QDF_STATUS hdd_mon_rx_packet_cbk(void *context, qdf_nbuf_t rxbuf) +{ + struct hdd_adapter *adapter; + int rxstat; + struct sk_buff *skb; + struct sk_buff *skb_next; + unsigned int cpu_index; + + /* Sanity check on inputs */ + if ((!context) || (!rxbuf)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: Null params being passed", __func__); + return QDF_STATUS_E_FAILURE; + } + + adapter = (struct hdd_adapter *)context; + if ((!adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "invalid adapter %pK", adapter); + return QDF_STATUS_E_FAILURE; + } + + cpu_index = wlan_hdd_get_cpu(); + + /* walk the chain until all are processed */ + skb = (struct sk_buff *) rxbuf; + while (skb) { + skb_next = skb->next; + skb->dev = adapter->dev; + + ++adapter->hdd_stats.tx_rx_stats.rx_packets[cpu_index]; + ++adapter->stats.rx_packets; + adapter->stats.rx_bytes += skb->len; + + /* Remove SKB from internal tracking table before submitting + * it to stack + */ + qdf_net_buf_debug_release_skb(skb); + + /* + * If this is not a last packet on the chain + * Just put packet into backlog queue, not scheduling RX sirq + */ + if (skb->next) { + rxstat = netif_rx(skb); + } else { + /* + * This is the last packet on the chain + * Scheduling rx sirq + */ + rxstat = netif_rx_ni(skb); + } + + if (NET_RX_SUCCESS == rxstat) + ++adapter-> + hdd_stats.tx_rx_stats.rx_delivered[cpu_index]; + else + ++adapter->hdd_stats.tx_rx_stats.rx_refused[cpu_index]; + + skb = skb_next; + } + + return QDF_STATUS_SUCCESS; +} +#endif + +/* + * hdd_is_mcast_replay() - checks if pkt is multicast replay + * @skb: packet skb + * + * Return: true if replayed multicast pkt, false otherwise + */ +static bool hdd_is_mcast_replay(struct sk_buff *skb) +{ + struct ethhdr *eth; + + eth = eth_hdr(skb); + if (unlikely(skb->pkt_type == PACKET_MULTICAST)) { + if (unlikely(ether_addr_equal(eth->h_source, + skb->dev->dev_addr))) + return true; + } + return false; +} + +/** + * hdd_is_arp_local() - check if local or non local arp + * @skb: pointer to sk_buff + * + * Return: true if local arp or false otherwise. + */ +static bool hdd_is_arp_local(struct sk_buff *skb) +{ + struct arphdr *arp; + struct in_ifaddr **ifap = NULL; + struct in_ifaddr *ifa = NULL; + struct in_device *in_dev; + unsigned char *arp_ptr; + __be32 tip; + + arp = (struct arphdr *)skb->data; + if (arp->ar_op == htons(ARPOP_REQUEST)) { + /* if fail to acquire rtnl lock, assume it's local arp */ + if (!rtnl_trylock()) + return true; + + in_dev = __in_dev_get_rtnl(skb->dev); + if (in_dev) { + for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; + ifap = &ifa->ifa_next) { + if (!strcmp(skb->dev->name, ifa->ifa_label)) + break; + } + } + + if (ifa && ifa->ifa_local) { + arp_ptr = (unsigned char *)(arp + 1); + arp_ptr += (skb->dev->addr_len + 4 + + skb->dev->addr_len); + memcpy(&tip, arp_ptr, 4); + hdd_debug("ARP packet: local IP: %x dest IP: %x", + ifa->ifa_local, tip); + if (ifa->ifa_local == tip) { + rtnl_unlock(); + return true; + } + } + rtnl_unlock(); + } + + return false; +} + +/** + * hdd_is_rx_wake_lock_needed() - check if wake lock is needed + * @skb: pointer to sk_buff + * + * RX wake lock is needed for: + * 1) Unicast data packet OR + * 2) Local ARP data packet + * + * Return: true if wake lock is needed or false otherwise. + */ +static bool hdd_is_rx_wake_lock_needed(struct sk_buff *skb) +{ + if ((skb->pkt_type != PACKET_BROADCAST && + skb->pkt_type != PACKET_MULTICAST) || hdd_is_arp_local(skb)) + return true; + + return false; +} + +#ifdef RECEIVE_OFFLOAD +/** + * hdd_resolve_rx_ol_mode() - Resolve Rx offload method, LRO or GRO + * @hdd_ctx: pointer to HDD Station Context + * + * Return: None + */ +static void hdd_resolve_rx_ol_mode(struct hdd_context *hdd_ctx) +{ + void *soc; + + soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!(cdp_cfg_get(soc, cfg_dp_lro_enable) ^ + cdp_cfg_get(soc, cfg_dp_gro_enable))) { + cdp_cfg_get(soc, cfg_dp_lro_enable) && + cdp_cfg_get(soc, cfg_dp_gro_enable) ? + hdd_debug("Can't enable both LRO and GRO, disabling Rx offload") : + hdd_debug("LRO and GRO both are disabled"); + hdd_ctx->ol_enable = 0; + } else if (cdp_cfg_get(soc, cfg_dp_lro_enable)) { + hdd_debug("Rx offload LRO is enabled"); + hdd_ctx->ol_enable = CFG_LRO_ENABLED; + } else if (cdp_cfg_get(soc, cfg_dp_tc_based_dyn_gro_enable)) { + hdd_debug("Dynamic Rx offload GRO is enabled"); + hdd_ctx->ol_enable = CFG_DYNAMIC_GRO_ENABLED; + } else { + hdd_debug("Rx offload: GRO is enabled"); + hdd_ctx->ol_enable = CFG_GRO_ENABLED; + } +} + +#ifdef WLAN_FEATURE_DYNAMIC_RX_AGGREGATION +/** + * hdd_gro_rx_bh_disable() - GRO RX/flush function. + * @napi_to_use: napi to be used to give packets to the stack, gro flush + * @skb: pointer to sk_buff + * + * Function calls napi_gro_receive for the skb. If the skb indicates that a + * flush needs to be done (set by the lower DP layer), the function also calls + * napi_gro_flush. Local softirqs are disabled (and later enabled) while making + * napi_gro__ calls. + * + * Return: QDF_STATUS_SUCCESS if not dropped by napi_gro_receive or + * QDF error code. + */ +static QDF_STATUS hdd_gro_rx_bh_disable(struct hdd_adapter *adapter, + struct napi_struct *napi_to_use, + struct sk_buff *skb) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + gro_result_t gro_res; + uint32_t rx_aggregation; + uint8_t rx_ctx_id = QDF_NBUF_CB_RX_CTX_ID(skb); + int32_t gro_disallowed; + + rx_aggregation = qdf_atomic_read(&hdd_ctx->dp_agg_param.rx_aggregation); + gro_disallowed = qdf_atomic_read(&adapter->gro_disallowed); + + skb_set_hash(skb, QDF_NBUF_CB_RX_FLOW_ID(skb), PKT_HASH_TYPE_L4); + + local_bh_disable(); + gro_res = napi_gro_receive(napi_to_use, skb); + + if (hdd_get_current_throughput_level(hdd_ctx) == PLD_BUS_WIDTH_IDLE || + !rx_aggregation || gro_disallowed) { + if (gro_res != GRO_DROP && gro_res != GRO_NORMAL) { + adapter->hdd_stats.tx_rx_stats. + rx_gro_low_tput_flush++; + napi_gro_flush(napi_to_use, false); + } + if (!rx_aggregation) + hdd_ctx->dp_agg_param.gro_force_flush[rx_ctx_id] = 1; + if (gro_disallowed) + adapter->gro_flushed[rx_ctx_id] = 1; + } + local_bh_enable(); + + if (gro_res == GRO_DROP) + status = QDF_STATUS_E_GRO_DROP; + + return status; +} + +#else /* WLAN_FEATURE_DYNAMIC_RX_AGGREGATION */ + +/** + * hdd_gro_rx_bh_disable() - GRO RX/flush function. + * @napi_to_use: napi to be used to give packets to the stack, gro flush + * @skb: pointer to sk_buff + * + * Function calls napi_gro_receive for the skb. If the skb indicates that a + * flush needs to be done (set by the lower DP layer), the function also calls + * napi_gro_flush. Local softirqs are disabled (and later enabled) while making + * napi_gro__ calls. + * + * Return: QDF_STATUS_SUCCESS if not dropped by napi_gro_receive or + * QDF error code. + */ +static QDF_STATUS hdd_gro_rx_bh_disable(struct hdd_adapter *adapter, + struct napi_struct *napi_to_use, + struct sk_buff *skb) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + gro_result_t gro_res; + + skb_set_hash(skb, QDF_NBUF_CB_RX_FLOW_ID(skb), PKT_HASH_TYPE_L4); + + local_bh_disable(); + gro_res = napi_gro_receive(napi_to_use, skb); + + if (hdd_get_current_throughput_level(hdd_ctx) == PLD_BUS_WIDTH_IDLE) { + if (gro_res != GRO_DROP && gro_res != GRO_NORMAL) { + adapter->hdd_stats.tx_rx_stats.rx_gro_low_tput_flush++; + napi_gro_flush(napi_to_use, false); + } + } + local_bh_enable(); + + if (gro_res == GRO_DROP) + status = QDF_STATUS_E_GRO_DROP; + + return status; +} +#endif /* WLAN_FEATURE_DYNAMIC_RX_AGGREGATION */ + +/** + * hdd_gro_rx_dp_thread() - Handle Rx procesing via GRO for DP thread + * @adapter: pointer to adapter context + * @skb: pointer to sk_buff + * + * Return: QDF_STATUS_SUCCESS if processed via GRO or non zero return code + */ +static +QDF_STATUS hdd_gro_rx_dp_thread(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + struct napi_struct *napi_to_use = NULL; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!adapter->hdd_ctx->enable_dp_rx_threads) { + hdd_dp_err_rl("gro not supported without DP RX thread!"); + return status; + } + + napi_to_use = + dp_rx_get_napi_context(cds_get_context(QDF_MODULE_ID_SOC), + QDF_NBUF_CB_RX_CTX_ID(skb)); + + if (!napi_to_use) { + hdd_dp_err_rl("no napi to use for GRO!"); + return status; + } + + status = hdd_gro_rx_bh_disable(adapter, napi_to_use, skb); + + return status; +} + +/** + * hdd_gro_rx_legacy() - Handle Rx processing via GRO for ihelium based targets + * @adapter: pointer to adapter context + * @skb: pointer to sk_buff + * + * Supports GRO for only station mode + * + * Return: QDF_STATUS_SUCCESS if processed via GRO or non zero return code + */ +static +QDF_STATUS hdd_gro_rx_legacy(struct hdd_adapter *adapter, struct sk_buff *skb) +{ + struct qca_napi_info *qca_napii; + struct qca_napi_data *napid; + struct napi_struct *napi_to_use; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + + /* Only enabling it for STA mode like LRO today */ + if (QDF_STA_MODE != adapter->device_mode) + return QDF_STATUS_E_NOSUPPORT; + + if (qdf_atomic_read(&hdd_ctx->disable_rx_ol_in_low_tput) || + qdf_atomic_read(&hdd_ctx->disable_rx_ol_in_concurrency)) + return QDF_STATUS_E_NOSUPPORT; + + napid = hdd_napi_get_all(); + if (unlikely(!napid)) + goto out; + + qca_napii = hif_get_napi(QDF_NBUF_CB_RX_CTX_ID(skb), napid); + if (unlikely(!qca_napii)) + goto out; + + /* + * As we are breaking context in Rxthread mode, there is rx_thread NAPI + * corresponds each hif_napi. + */ + if (adapter->hdd_ctx->enable_rxthread) + napi_to_use = &qca_napii->rx_thread_napi; + else + napi_to_use = &qca_napii->napi; + + status = hdd_gro_rx_bh_disable(adapter, napi_to_use, skb); +out: + + return status; +} + +/** + * hdd_rxthread_napi_gro_flush() - GRO flush callback for NAPI+Rx_Thread Rx mode + * @data: hif NAPI context + * + * Return: none + */ +static void hdd_rxthread_napi_gro_flush(void *data) +{ + struct qca_napi_info *qca_napii = (struct qca_napi_info *)data; + + local_bh_disable(); + /* + * As we are breaking context in Rxthread mode, there is rx_thread NAPI + * corresponds each hif_napi. + */ + napi_gro_flush(&qca_napii->rx_thread_napi, false); + local_bh_enable(); +} + +/** + * hdd_hif_napi_gro_flush() - GRO flush callback for NAPI Rx mode + * @data: hif NAPI context + * + * Return: none + */ +static void hdd_hif_napi_gro_flush(void *data) +{ + struct qca_napi_info *qca_napii = (struct qca_napi_info *)data; + + local_bh_disable(); + napi_gro_flush(&qca_napii->napi, false); + local_bh_enable(); +} + +#ifdef FEATURE_LRO +/** + * hdd_qdf_lro_flush() - LRO flush wrapper + * @data: hif NAPI context + * + * Return: none + */ +static void hdd_qdf_lro_flush(void *data) +{ + struct qca_napi_info *qca_napii = (struct qca_napi_info *)data; + qdf_lro_ctx_t qdf_lro_ctx = qca_napii->lro_ctx; + + qdf_lro_flush(qdf_lro_ctx); +} +#else +static void hdd_qdf_lro_flush(void *data) +{ +} +#endif + +/** + * hdd_register_rx_ol() - Register LRO/GRO rx processing callbacks + * @hdd_ctx: pointer to hdd_ctx + * @lithium_based_target: whether its a lithium arch based target or not + * + * Return: none + */ +static void hdd_register_rx_ol_cb(struct hdd_context *hdd_ctx, + bool lithium_based_target) +{ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!hdd_ctx) { + hdd_err("HDD context is NULL"); + return; + } + + hdd_ctx->en_tcp_delack_no_lro = 0; + + if (!hdd_is_lro_enabled(hdd_ctx)) { + cdp_register_rx_offld_flush_cb(soc, hdd_qdf_lro_flush); + hdd_ctx->receive_offload_cb = hdd_lro_rx; + hdd_debug("LRO is enabled"); + } else if (hdd_ctx->ol_enable == CFG_GRO_ENABLED || + hdd_ctx->ol_enable == CFG_DYNAMIC_GRO_ENABLED) { + qdf_atomic_set(&hdd_ctx->dp_agg_param.rx_aggregation, 1); + if (lithium_based_target) { + /* no flush registration needed, it happens in DP thread */ + hdd_ctx->receive_offload_cb = hdd_gro_rx_dp_thread; + } else { + /*ihelium based targets */ + if (hdd_ctx->enable_rxthread) + cdp_register_rx_offld_flush_cb(soc, + hdd_rxthread_napi_gro_flush); + else + cdp_register_rx_offld_flush_cb(soc, + hdd_hif_napi_gro_flush); + hdd_ctx->receive_offload_cb = hdd_gro_rx_legacy; + } + hdd_debug("GRO is enabled"); + } else if (HDD_MSM_CFG(hdd_ctx->config->enable_tcp_delack)) { + hdd_ctx->en_tcp_delack_no_lro = 1; + hdd_debug("TCP Del ACK is enabled"); + } +} + +/** + * hdd_rx_ol_send_config() - Send RX offload configuration to FW + * @hdd_ctx: pointer to hdd_ctx + * + * This function is only used for non lithium targets. Lithium based targets are + * sending LRO config to FW in vdev attach implemented in cmn DP layer. + * + * Return: 0 on success, non zero on failure + */ +static int hdd_rx_ol_send_config(struct hdd_context *hdd_ctx) +{ + struct cdp_lro_hash_config lro_config = {0}; + /* + * This will enable flow steering and Toeplitz hash + * So enable it for LRO or GRO processing. + */ + if (cfg_get(hdd_ctx->psoc, CFG_DP_GRO) || + cfg_get(hdd_ctx->psoc, CFG_DP_LRO)) { + lro_config.lro_enable = 1; + lro_config.tcp_flag = TCPHDR_ACK; + lro_config.tcp_flag_mask = TCPHDR_FIN | TCPHDR_SYN | + TCPHDR_RST | TCPHDR_ACK | + TCPHDR_URG | TCPHDR_ECE | + TCPHDR_CWR; + } + + get_random_bytes(lro_config.toeplitz_hash_ipv4, + (sizeof(lro_config.toeplitz_hash_ipv4[0]) * + LRO_IPV4_SEED_ARR_SZ)); + + get_random_bytes(lro_config.toeplitz_hash_ipv6, + (sizeof(lro_config.toeplitz_hash_ipv6[0]) * + LRO_IPV6_SEED_ARR_SZ)); + + if (wma_lro_init(&lro_config)) + return -EAGAIN; + else + hdd_debug("LRO Config: lro_enable: 0x%x tcp_flag 0x%x tcp_flag_mask 0x%x", + lro_config.lro_enable, lro_config.tcp_flag, + lro_config.tcp_flag_mask); + + return 0; +} + +int hdd_rx_ol_init(struct hdd_context *hdd_ctx) +{ + int ret = 0; + bool lithium_based_target = false; + + if (hdd_ctx->target_type == TARGET_TYPE_QCA6290 || + hdd_ctx->target_type == TARGET_TYPE_QCA6390 || + hdd_ctx->target_type == TARGET_TYPE_QCA6490) + lithium_based_target = true; + + hdd_resolve_rx_ol_mode(hdd_ctx); + hdd_register_rx_ol_cb(hdd_ctx, lithium_based_target); + + if (!lithium_based_target) { + ret = hdd_rx_ol_send_config(hdd_ctx); + if (ret) { + hdd_ctx->ol_enable = 0; + hdd_err("Failed to send LRO/GRO configuration! %u", ret); + return ret; + } + } + + return 0; +} + +void hdd_disable_rx_ol_in_concurrency(bool disable) +{ + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (!hdd_ctx) { + hdd_err("hdd_ctx is NULL"); + return; + } + + if (disable) { + if (HDD_MSM_CFG(hdd_ctx->config->enable_tcp_delack)) { + struct wlan_rx_tp_data rx_tp_data; + + hdd_info("Enable TCP delack as LRO disabled in concurrency"); + rx_tp_data.rx_tp_flags = TCP_DEL_ACK_IND; + rx_tp_data.level = GET_CUR_RX_LVL(hdd_ctx); + wlan_hdd_update_tcp_rx_param(hdd_ctx, &rx_tp_data); + hdd_ctx->en_tcp_delack_no_lro = 1; + } + qdf_atomic_set(&hdd_ctx->disable_rx_ol_in_concurrency, 1); + } else { + if (HDD_MSM_CFG(hdd_ctx->config->enable_tcp_delack)) { + hdd_info("Disable TCP delack as LRO is enabled"); + hdd_ctx->en_tcp_delack_no_lro = 0; + hdd_reset_tcp_delack(hdd_ctx); + } + qdf_atomic_set(&hdd_ctx->disable_rx_ol_in_concurrency, 0); + } +} + +void hdd_disable_rx_ol_for_low_tput(struct hdd_context *hdd_ctx, bool disable) +{ + if (disable) + qdf_atomic_set(&hdd_ctx->disable_rx_ol_in_low_tput, 1); + else + qdf_atomic_set(&hdd_ctx->disable_rx_ol_in_low_tput, 0); +} + +#else /* RECEIVE_OFFLOAD */ +int hdd_rx_ol_init(struct hdd_context *hdd_ctx) +{ + hdd_err("Rx_OL, LRO/GRO not supported"); + return -EPERM; +} + +void hdd_disable_rx_ol_in_concurrency(bool disable) +{ +} + +void hdd_disable_rx_ol_for_low_tput(struct hdd_context *hdd_ctx, bool disable) +{ +} +#endif /* RECEIVE_OFFLOAD */ + +#ifdef WLAN_FEATURE_TSF_PLUS +static inline void hdd_tsf_timestamp_rx(struct hdd_context *hdd_ctx, + qdf_nbuf_t netbuf, + uint64_t target_time) +{ + if (!hdd_tsf_is_rx_set(hdd_ctx)) + return; + + hdd_rx_timestamp(netbuf, target_time); +} +#else +static inline void hdd_tsf_timestamp_rx(struct hdd_context *hdd_ctx, + qdf_nbuf_t netbuf, + uint64_t target_time) +{ +} +#endif + +QDF_STATUS hdd_rx_thread_gro_flush_ind_cbk(void *adapter, int rx_ctx_id) +{ + struct hdd_adapter *hdd_adapter = adapter; + + if (qdf_unlikely((!hdd_adapter) || (!hdd_adapter->hdd_ctx))) { + hdd_err("Null params being passed"); + return QDF_STATUS_E_FAILURE; + } + + if (hdd_is_low_tput_gro_enable(hdd_adapter->hdd_ctx)) { + hdd_adapter->hdd_stats.tx_rx_stats.rx_gro_flush_skip++; + return QDF_STATUS_SUCCESS; + } + + return dp_rx_gro_flush_ind(cds_get_context(QDF_MODULE_ID_SOC), + rx_ctx_id); +} + +QDF_STATUS hdd_rx_pkt_thread_enqueue_cbk(void *adapter, + qdf_nbuf_t nbuf_list) +{ + struct hdd_adapter *hdd_adapter; + uint8_t vdev_id; + qdf_nbuf_t head_ptr; + + if (qdf_unlikely(!adapter || !nbuf_list)) { + hdd_err("Null params being passed"); + return QDF_STATUS_E_FAILURE; + } + + hdd_adapter = (struct hdd_adapter *)adapter; + if (hdd_validate_adapter(hdd_adapter)) { + hdd_err_rl("adapter validate failed"); + return QDF_STATUS_E_FAILURE; + } + + vdev_id = hdd_adapter->vdev_id; + + if (vdev_id >= WLAN_UMAC_VDEV_ID_MAX) { + hdd_info_rl("Vdev invalid. Dropping packets"); + qdf_nbuf_list_free(nbuf_list); + return QDF_STATUS_E_NETDOWN; + } + + head_ptr = nbuf_list; + while (head_ptr) { + qdf_nbuf_cb_update_vdev_id(head_ptr, vdev_id); + head_ptr = qdf_nbuf_next(head_ptr); + } + + return dp_rx_enqueue_pkt(cds_get_context(QDF_MODULE_ID_SOC), nbuf_list); +} + +#ifdef CONFIG_HL_SUPPORT +QDF_STATUS hdd_rx_deliver_to_stack(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + int status = QDF_STATUS_E_FAILURE; + int netif_status; + + adapter->hdd_stats.tx_rx_stats.rx_non_aggregated++; + hdd_ctx->no_rx_offload_pkt_cnt++; + netif_status = netif_rx_ni(skb); + + if (netif_status == NET_RX_SUCCESS) + status = QDF_STATUS_SUCCESS; + + return status; +} +#else + +#ifdef WLAN_FEATURE_DYNAMIC_RX_AGGREGATION +#if defined(WLAN_SUPPORT_RX_FISA) +/** + * hdd_set_fisa_disallowed_for_vdev() - Set fisa disallowed bit for a vdev + * @soc: DP soc handle + * @vdev_id: Vdev id + * @rx_ctx_id: rx context id + * @val: Enable or disable + * + * The function sets the fisa disallowed flag for a given vdev + * + * Return: None + */ +static inline +void hdd_set_fisa_disallowed_for_vdev(ol_txrx_soc_handle soc, uint8_t vdev_id, + uint8_t rx_ctx_id, uint8_t val) +{ + dp_set_fisa_disallowed_for_vdev(soc, vdev_id, rx_ctx_id, val); +} +#else +static inline +void hdd_set_fisa_disallowed_for_vdev(ol_txrx_soc_handle soc, uint8_t vdev_id, + uint8_t rx_ctx_id, uint8_t val) +{ +} +#endif + +QDF_STATUS hdd_rx_deliver_to_stack(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + int status = QDF_STATUS_E_FAILURE; + int netif_status; + bool skb_receive_offload_ok = false; + uint8_t rx_ctx_id; + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (QDF_NBUF_CB_RX_TCP_PROTO(skb) && + !QDF_NBUF_CB_RX_PEER_CACHED_FRM(skb)) + skb_receive_offload_ok = true; + + if (hdd_ctx->ol_enable == CFG_DYNAMIC_GRO_ENABLED || + hdd_ctx->ol_enable == CFG_GRO_ENABLED) + rx_ctx_id = 0; + else + rx_ctx_id = QDF_NBUF_CB_RX_CTX_ID(skb); + + if (qdf_atomic_read(&adapter->gro_disallowed) == 0 && + adapter->gro_flushed[rx_ctx_id] != 0) { + if (qdf_likely(soc)) + hdd_set_fisa_disallowed_for_vdev(soc, adapter->vdev_id, + rx_ctx_id, 0); + adapter->gro_flushed[rx_ctx_id] = 0; + } else if (qdf_atomic_read(&adapter->gro_disallowed) && + adapter->gro_flushed[rx_ctx_id] == 0) { + if (qdf_likely(soc)) + hdd_set_fisa_disallowed_for_vdev(soc, adapter->vdev_id, + rx_ctx_id, 1); + } + + if (skb_receive_offload_ok && hdd_ctx->receive_offload_cb && + !hdd_ctx->dp_agg_param.gro_force_flush[rx_ctx_id] && + !adapter->gro_flushed[rx_ctx_id]) { + status = hdd_ctx->receive_offload_cb(adapter, skb); + + if (QDF_IS_STATUS_SUCCESS(status)) { + adapter->hdd_stats.tx_rx_stats.rx_aggregated++; + return status; + } + + if (status == QDF_STATUS_E_GRO_DROP) { + adapter->hdd_stats.tx_rx_stats.rx_gro_dropped++; + return status; + } + } + + /* + * The below case handles the scenario when rx_aggregation is + * re-enabled dynamically, in which case gro_force_flush needs + * to be reset to 0 to allow GRO. + */ + if (qdf_atomic_read(&hdd_ctx->dp_agg_param.rx_aggregation) && + hdd_ctx->dp_agg_param.gro_force_flush[rx_ctx_id]) + hdd_ctx->dp_agg_param.gro_force_flush[rx_ctx_id] = 0; + + adapter->hdd_stats.tx_rx_stats.rx_non_aggregated++; + + /* Account for GRO/LRO ineligible packets, mostly UDP */ + hdd_ctx->no_rx_offload_pkt_cnt++; + + if (qdf_likely(hdd_ctx->enable_dp_rx_threads || + hdd_ctx->enable_rxthread)) { + local_bh_disable(); + netif_status = netif_receive_skb(skb); + local_bh_enable(); + } else if (qdf_unlikely(QDF_NBUF_CB_RX_PEER_CACHED_FRM(skb))) { + /* + * Frames before peer is registered to avoid contention with + * NAPI softirq. + * Refer fix: + * qcacld-3.0: Do netif_rx_ni() for frames received before + * peer assoc + */ + netif_status = netif_rx_ni(skb); + } else { /* NAPI Context */ + netif_status = netif_receive_skb(skb); + } + + if (netif_status == NET_RX_SUCCESS) + status = QDF_STATUS_SUCCESS; + + return status; +} + +#else /* WLAN_FEATURE_DYNAMIC_RX_AGGREGATION */ + +QDF_STATUS hdd_rx_deliver_to_stack(struct hdd_adapter *adapter, + struct sk_buff *skb) +{ + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + int status = QDF_STATUS_E_FAILURE; + int netif_status; + bool skb_receive_offload_ok = false; + + if (QDF_NBUF_CB_RX_TCP_PROTO(skb) && + !QDF_NBUF_CB_RX_PEER_CACHED_FRM(skb)) + skb_receive_offload_ok = true; + + if (skb_receive_offload_ok && hdd_ctx->receive_offload_cb) { + status = hdd_ctx->receive_offload_cb(adapter, skb); + + if (QDF_IS_STATUS_SUCCESS(status)) { + adapter->hdd_stats.tx_rx_stats.rx_aggregated++; + return status; + } + + if (status == QDF_STATUS_E_GRO_DROP) { + adapter->hdd_stats.tx_rx_stats.rx_gro_dropped++; + return status; + } + } + + adapter->hdd_stats.tx_rx_stats.rx_non_aggregated++; + + /* Account for GRO/LRO ineligible packets, mostly UDP */ + hdd_ctx->no_rx_offload_pkt_cnt++; + + if (qdf_likely(hdd_ctx->enable_dp_rx_threads || + hdd_ctx->enable_rxthread)) { + local_bh_disable(); + netif_status = netif_receive_skb(skb); + local_bh_enable(); + } else if (qdf_unlikely(QDF_NBUF_CB_RX_PEER_CACHED_FRM(skb))) { + /* + * Frames before peer is registered to avoid contention with + * NAPI softirq. + * Refer fix: + * qcacld-3.0: Do netif_rx_ni() for frames received before + * peer assoc + */ + netif_status = netif_rx_ni(skb); + } else { /* NAPI Context */ + netif_status = netif_receive_skb(skb); + } + + if (netif_status == NET_RX_SUCCESS) + status = QDF_STATUS_SUCCESS; + + return status; +} +#endif /* WLAN_FEATURE_DYNAMIC_RX_AGGREGATION */ +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)) +static bool hdd_is_gratuitous_arp_unsolicited_na(struct sk_buff *skb) +{ + return false; +} +#else +static bool hdd_is_gratuitous_arp_unsolicited_na(struct sk_buff *skb) +{ + return cfg80211_is_gratuitous_arp_unsolicited_na(skb); +} +#endif + +QDF_STATUS hdd_rx_flush_packet_cbk(void *adapter_context, uint8_t vdev_id) +{ + struct hdd_adapter *adapter; + struct hdd_context *hdd_ctx; + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (qdf_unlikely(!soc)) + return QDF_STATUS_E_FAILURE; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (unlikely(!hdd_ctx)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: HDD context is Null", __func__); + return QDF_STATUS_E_FAILURE; + } + + adapter = hdd_adapter_get_by_reference(hdd_ctx, adapter_context); + if (!adapter) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: Adapter reference is Null", __func__); + return QDF_STATUS_E_FAILURE; + } + + /* do fisa flush for this vdev */ + if (hdd_ctx->config->fisa_enable) + hdd_rx_fisa_flush_by_vdev_id(soc, vdev_id); + + if (hdd_ctx->enable_dp_rx_threads) + dp_txrx_flush_pkts_by_vdev_id(soc, vdev_id); + + hdd_adapter_put(adapter); + + return QDF_STATUS_SUCCESS; +} + +#if defined(WLAN_SUPPORT_RX_FISA) +QDF_STATUS hdd_rx_fisa_cbk(void *dp_soc, void *dp_vdev, qdf_nbuf_t nbuf_list) +{ + return dp_fisa_rx((struct dp_soc *)dp_soc, (struct dp_vdev *)dp_vdev, + nbuf_list); +} + +QDF_STATUS hdd_rx_fisa_flush_by_ctx_id(void *dp_soc, int ring_num) +{ + return dp_rx_fisa_flush_by_ctx_id((struct dp_soc *)dp_soc, ring_num); +} + +QDF_STATUS hdd_rx_fisa_flush_by_vdev_id(void *dp_soc, uint8_t vdev_id) +{ + return dp_rx_fisa_flush_by_vdev_id((struct dp_soc *)dp_soc, vdev_id); +} +#endif + +QDF_STATUS hdd_rx_packet_cbk(void *adapter_context, + qdf_nbuf_t rxBuf) +{ + struct hdd_adapter *adapter = NULL; + struct hdd_context *hdd_ctx = NULL; + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct sk_buff *skb = NULL; + struct sk_buff *next = NULL; + struct hdd_station_ctx *sta_ctx = NULL; + unsigned int cpu_index; + struct qdf_mac_addr *mac_addr, *dest_mac_addr; + bool wake_lock = false; + uint8_t pkt_type = 0; + bool track_arp = false; + struct wlan_objmgr_vdev *vdev; + + /* Sanity check on inputs */ + if (unlikely((!adapter_context) || (!rxBuf))) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: Null params being passed", __func__); + return QDF_STATUS_E_FAILURE; + } + + adapter = (struct hdd_adapter *)adapter_context; + if (unlikely(WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "Magic cookie(%x) for adapter sanity verification is invalid", + adapter->magic); + return QDF_STATUS_E_FAILURE; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (unlikely(!hdd_ctx)) { + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: HDD context is Null", __func__); + return QDF_STATUS_E_FAILURE; + } + + cpu_index = wlan_hdd_get_cpu(); + + next = (struct sk_buff *)rxBuf; + + while (next) { + skb = next; + next = skb->next; + skb->next = NULL; + + if (qdf_nbuf_is_ipv4_arp_pkt(skb)) { + if (qdf_nbuf_data_is_arp_rsp(skb) && + (adapter->track_arp_ip == + qdf_nbuf_get_arp_src_ip(skb))) { + ++adapter->hdd_stats.hdd_arp_stats. + rx_arp_rsp_count; + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, + QDF_TRACE_LEVEL_DEBUG, + "%s: ARP packet received", + __func__); + track_arp = true; + } + } + /* track connectivity stats */ + if (adapter->pkt_type_bitmap) + hdd_tx_rx_collect_connectivity_stats_info(skb, adapter, + PKT_TYPE_RSP, &pkt_type); + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if ((sta_ctx->conn_info.proxy_arp_service) && + hdd_is_gratuitous_arp_unsolicited_na(skb)) { + qdf_atomic_inc(&adapter->hdd_stats.tx_rx_stats. + rx_usolict_arp_n_mcast_drp); + /* Remove SKB from internal tracking table before + * submitting it to stack. + */ + qdf_nbuf_free(skb); + continue; + } + + hdd_event_eapol_log(skb, QDF_RX); + qdf_dp_trace_log_pkt(adapter->vdev_id, skb, QDF_RX, + QDF_TRACE_DEFAULT_PDEV_ID); + + DPTRACE(qdf_dp_trace(skb, + QDF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD, + QDF_TRACE_DEFAULT_PDEV_ID, + qdf_nbuf_data_addr(skb), + sizeof(qdf_nbuf_data(skb)), QDF_RX)); + + DPTRACE(qdf_dp_trace_data_pkt(skb, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_DP_TRACE_RX_PACKET_RECORD, + 0, QDF_RX)); + + dest_mac_addr = (struct qdf_mac_addr *)(skb->data); + mac_addr = (struct qdf_mac_addr *)(skb->data+QDF_MAC_ADDR_SIZE); + + if (!hdd_is_current_high_throughput(hdd_ctx)) { + vdev = hdd_objmgr_get_vdev(adapter); + if (vdev) { + ucfg_tdls_update_rx_pkt_cnt(vdev, mac_addr, + dest_mac_addr); + hdd_objmgr_put_vdev(vdev); + } + } + + skb->dev = adapter->dev; + skb->protocol = eth_type_trans(skb, skb->dev); + ++adapter->hdd_stats.tx_rx_stats.rx_packets[cpu_index]; + ++adapter->stats.rx_packets; + adapter->stats.rx_bytes += skb->len; + + /* Incr GW Rx count for NUD tracking based on GW mac addr */ + hdd_nud_incr_gw_rx_pkt_cnt(adapter, mac_addr); + + /* Check & drop replayed mcast packets (for IPV6) */ + if (hdd_ctx->config->multicast_replay_filter && + hdd_is_mcast_replay(skb)) { + qdf_atomic_inc(&adapter->hdd_stats.tx_rx_stats. + rx_usolict_arp_n_mcast_drp); + qdf_nbuf_free(skb); + continue; + } + + /* hold configurable wakelock for unicast traffic */ + if (!hdd_is_current_high_throughput(hdd_ctx) && + hdd_ctx->config->rx_wakelock_timeout && + sta_ctx->conn_info.is_authenticated) + wake_lock = hdd_is_rx_wake_lock_needed(skb); + + if (wake_lock) { + cds_host_diag_log_work(&hdd_ctx->rx_wake_lock, + hdd_ctx->config->rx_wakelock_timeout, + WIFI_POWER_EVENT_WAKELOCK_HOLD_RX); + qdf_wake_lock_timeout_acquire(&hdd_ctx->rx_wake_lock, + hdd_ctx->config-> + rx_wakelock_timeout); + } + + /* Remove SKB from internal tracking table before submitting + * it to stack + */ + qdf_net_buf_debug_release_skb(skb); + + hdd_tsf_timestamp_rx(hdd_ctx, skb, ktime_to_us(skb->tstamp)); + + qdf_status = hdd_rx_deliver_to_stack(adapter, skb); + + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + ++adapter->hdd_stats.tx_rx_stats. + rx_delivered[cpu_index]; + if (track_arp) + ++adapter->hdd_stats.hdd_arp_stats. + rx_delivered; + /* track connectivity stats */ + if (adapter->pkt_type_bitmap) + hdd_tx_rx_collect_connectivity_stats_info( + skb, adapter, + PKT_TYPE_RX_DELIVERED, &pkt_type); + } else { + ++adapter->hdd_stats.tx_rx_stats.rx_refused[cpu_index]; + if (track_arp) + ++adapter->hdd_stats.hdd_arp_stats.rx_refused; + + /* track connectivity stats */ + if (adapter->pkt_type_bitmap) + hdd_tx_rx_collect_connectivity_stats_info( + skb, adapter, + PKT_TYPE_RX_REFUSED, &pkt_type); + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_reason_type_to_string() - return string conversion of reason type + * @reason: reason type + * + * This utility function helps log string conversion of reason type. + * + * Return: string conversion of device mode, if match found; + * "Unknown" otherwise. + */ +const char *hdd_reason_type_to_string(enum netif_reason_type reason) +{ + switch (reason) { + CASE_RETURN_STRING(WLAN_CONTROL_PATH); + CASE_RETURN_STRING(WLAN_DATA_FLOW_CONTROL); + CASE_RETURN_STRING(WLAN_FW_PAUSE); + CASE_RETURN_STRING(WLAN_TX_ABORT); + CASE_RETURN_STRING(WLAN_VDEV_STOP); + CASE_RETURN_STRING(WLAN_PEER_UNAUTHORISED); + CASE_RETURN_STRING(WLAN_THERMAL_MITIGATION); + CASE_RETURN_STRING(WLAN_DATA_FLOW_CONTROL_PRIORITY); + default: + return "Invalid"; + } +} + +/** + * hdd_action_type_to_string() - return string conversion of action type + * @action: action type + * + * This utility function helps log string conversion of action_type. + * + * Return: string conversion of device mode, if match found; + * "Unknown" otherwise. + */ +const char *hdd_action_type_to_string(enum netif_action_type action) +{ + + switch (action) { + CASE_RETURN_STRING(WLAN_STOP_ALL_NETIF_QUEUE); + CASE_RETURN_STRING(WLAN_START_ALL_NETIF_QUEUE); + CASE_RETURN_STRING(WLAN_WAKE_ALL_NETIF_QUEUE); + CASE_RETURN_STRING(WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER); + CASE_RETURN_STRING(WLAN_START_ALL_NETIF_QUEUE_N_CARRIER); + CASE_RETURN_STRING(WLAN_NETIF_TX_DISABLE); + CASE_RETURN_STRING(WLAN_NETIF_TX_DISABLE_N_CARRIER); + CASE_RETURN_STRING(WLAN_NETIF_CARRIER_ON); + CASE_RETURN_STRING(WLAN_NETIF_CARRIER_OFF); + CASE_RETURN_STRING(WLAN_NETIF_PRIORITY_QUEUE_ON); + CASE_RETURN_STRING(WLAN_NETIF_PRIORITY_QUEUE_OFF); + CASE_RETURN_STRING(WLAN_NETIF_VO_QUEUE_ON); + CASE_RETURN_STRING(WLAN_NETIF_VO_QUEUE_OFF); + CASE_RETURN_STRING(WLAN_NETIF_VI_QUEUE_ON); + CASE_RETURN_STRING(WLAN_NETIF_VI_QUEUE_OFF); + CASE_RETURN_STRING(WLAN_NETIF_BE_BK_QUEUE_OFF); + CASE_RETURN_STRING(WLAN_WAKE_NON_PRIORITY_QUEUE); + CASE_RETURN_STRING(WLAN_STOP_NON_PRIORITY_QUEUE); + default: + return "Invalid"; + } +} + +/** + * wlan_hdd_update_queue_oper_stats - update queue operation statistics + * @adapter: adapter handle + * @action: action type + * @reason: reason type + */ +static void wlan_hdd_update_queue_oper_stats(struct hdd_adapter *adapter, + enum netif_action_type action, enum netif_reason_type reason) +{ + switch (action) { + case WLAN_STOP_ALL_NETIF_QUEUE: + case WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER: + case WLAN_NETIF_BE_BK_QUEUE_OFF: + case WLAN_NETIF_VI_QUEUE_OFF: + case WLAN_NETIF_VO_QUEUE_OFF: + case WLAN_NETIF_PRIORITY_QUEUE_OFF: + case WLAN_STOP_NON_PRIORITY_QUEUE: + adapter->queue_oper_stats[reason].pause_count++; + break; + case WLAN_START_ALL_NETIF_QUEUE: + case WLAN_WAKE_ALL_NETIF_QUEUE: + case WLAN_START_ALL_NETIF_QUEUE_N_CARRIER: + case WLAN_NETIF_VI_QUEUE_ON: + case WLAN_NETIF_VO_QUEUE_ON: + case WLAN_NETIF_PRIORITY_QUEUE_ON: + case WLAN_WAKE_NON_PRIORITY_QUEUE: + adapter->queue_oper_stats[reason].unpause_count++; + break; + default: + break; + } +} + +/** + * hdd_netdev_queue_is_locked() + * @txq: net device tx queue + * + * For SMP system, always return false and we could safely rely on + * __netif_tx_trylock(). + * + * Return: true locked; false not locked + */ +#ifdef QCA_CONFIG_SMP +static inline bool hdd_netdev_queue_is_locked(struct netdev_queue *txq) +{ + return false; +} +#else +static inline bool hdd_netdev_queue_is_locked(struct netdev_queue *txq) +{ + return txq->xmit_lock_owner != -1; +} +#endif + +/** + * wlan_hdd_update_txq_timestamp() - update txq timestamp + * @dev: net device + * + * Return: none + */ +static void wlan_hdd_update_txq_timestamp(struct net_device *dev) +{ + struct netdev_queue *txq; + int i; + + for (i = 0; i < NUM_TX_QUEUES; i++) { + txq = netdev_get_tx_queue(dev, i); + + /* + * On UP system, kernel will trigger watchdog bite if spinlock + * recursion is detected. Unfortunately recursion is possible + * when it is called in dev_queue_xmit() context, where stack + * grabs the lock before calling driver's ndo_start_xmit + * callback. + */ + if (!hdd_netdev_queue_is_locked(txq)) { + if (__netif_tx_trylock(txq)) { + txq_trans_update(txq); + __netif_tx_unlock(txq); + } + } + } +} + +/** + * wlan_hdd_update_unpause_time() - update unpause time + * @adapter: adapter handle + * + * Return: none + */ +static void wlan_hdd_update_unpause_time(struct hdd_adapter *adapter) +{ + qdf_time_t curr_time = qdf_system_ticks(); + + adapter->total_unpause_time += curr_time - adapter->last_time; + adapter->last_time = curr_time; +} + +/** + * wlan_hdd_update_pause_time() - update pause time + * @adapter: adapter handle + * + * Return: none + */ +static void wlan_hdd_update_pause_time(struct hdd_adapter *adapter, + uint32_t temp_map) +{ + qdf_time_t curr_time = qdf_system_ticks(); + uint8_t i; + qdf_time_t pause_time; + + pause_time = curr_time - adapter->last_time; + adapter->total_pause_time += pause_time; + adapter->last_time = curr_time; + + for (i = 0; i < WLAN_REASON_TYPE_MAX; i++) { + if (temp_map & (1 << i)) { + adapter->queue_oper_stats[i].total_pause_time += + pause_time; + break; + } + } + +} + +uint32_t +wlan_hdd_dump_queue_history_state(struct hdd_netif_queue_history *queue_history, + char *buf, uint32_t size) +{ + unsigned int i; + unsigned int index = 0; + + for (i = 0; i < NUM_TX_QUEUES; i++) { + index += qdf_scnprintf(buf + index, + size - index, + "%u:0x%lx ", + i, queue_history->tx_q_state[i]); + } + + return index; +} + +/** + * wlan_hdd_update_queue_history_state() - Save a copy of dev TX queues state + * @adapter: adapter handle + * + * Save netdev TX queues state into adapter queue history. + * + * Return: None + */ +static void +wlan_hdd_update_queue_history_state(struct net_device *dev, + struct hdd_netif_queue_history *q_hist) +{ + unsigned int i = 0; + uint32_t num_tx_queues = 0; + struct netdev_queue *txq = NULL; + + num_tx_queues = qdf_min(dev->num_tx_queues, (uint32_t)NUM_TX_QUEUES); + + for (i = 0; i < num_tx_queues; i++) { + txq = netdev_get_tx_queue(dev, i); + q_hist->tx_q_state[i] = txq->state; + } +} + +/** + * wlan_hdd_stop_non_priority_queue() - stop non prority queues + * @adapter: adapter handle + * + * Return: None + */ +static inline void wlan_hdd_stop_non_priority_queue(struct hdd_adapter *adapter) +{ + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_VO); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_VI); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_BE); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_BK); +} + +/** + * wlan_hdd_wake_non_priority_queue() - wake non prority queues + * @adapter: adapter handle + * + * Return: None + */ +static inline void wlan_hdd_wake_non_priority_queue(struct hdd_adapter *adapter) +{ + netif_wake_subqueue(adapter->dev, HDD_LINUX_AC_VO); + netif_wake_subqueue(adapter->dev, HDD_LINUX_AC_VI); + netif_wake_subqueue(adapter->dev, HDD_LINUX_AC_BE); + netif_wake_subqueue(adapter->dev, HDD_LINUX_AC_BK); +} + +/** + * wlan_hdd_netif_queue_control() - Use for netif_queue related actions + * @adapter: adapter handle + * @action: action type + * @reason: reason type + * + * This is single function which is used for netif_queue related + * actions like start/stop of network queues and on/off carrier + * option. + * + * Return: None + */ +void wlan_hdd_netif_queue_control(struct hdd_adapter *adapter, + enum netif_action_type action, enum netif_reason_type reason) +{ + uint32_t temp_map; + uint8_t index; + struct hdd_netif_queue_history *txq_hist_ptr; + + if ((!adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) || + (!adapter->dev)) { + hdd_err("adapter is invalid"); + return; + } + + switch (action) { + + case WLAN_NETIF_CARRIER_ON: + netif_carrier_on(adapter->dev); + break; + + case WLAN_NETIF_CARRIER_OFF: + netif_carrier_off(adapter->dev); + break; + + case WLAN_STOP_ALL_NETIF_QUEUE: + spin_lock_bh(&adapter->pause_map_lock); + if (!adapter->pause_map) { + netif_tx_stop_all_queues(adapter->dev); + wlan_hdd_update_txq_timestamp(adapter->dev); + wlan_hdd_update_unpause_time(adapter); + } + adapter->pause_map |= (1 << reason); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_STOP_NON_PRIORITY_QUEUE: + spin_lock_bh(&adapter->pause_map_lock); + if (!adapter->pause_map) { + wlan_hdd_stop_non_priority_queue(adapter); + wlan_hdd_update_txq_timestamp(adapter->dev); + wlan_hdd_update_unpause_time(adapter); + } + adapter->pause_map |= (1 << reason); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_NETIF_PRIORITY_QUEUE_ON: + spin_lock_bh(&adapter->pause_map_lock); + temp_map = adapter->pause_map; + adapter->pause_map &= ~(1 << reason); + netif_wake_subqueue(adapter->dev, HDD_LINUX_AC_HI_PRIO); + wlan_hdd_update_pause_time(adapter, temp_map); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_NETIF_PRIORITY_QUEUE_OFF: + spin_lock_bh(&adapter->pause_map_lock); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_HI_PRIO); + wlan_hdd_update_txq_timestamp(adapter->dev); + wlan_hdd_update_unpause_time(adapter); + adapter->pause_map |= (1 << reason); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_NETIF_BE_BK_QUEUE_OFF: + spin_lock_bh(&adapter->pause_map_lock); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_BK); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_BE); + wlan_hdd_update_txq_timestamp(adapter->dev); + wlan_hdd_update_unpause_time(adapter); + adapter->pause_map |= (1 << reason); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_NETIF_VI_QUEUE_OFF: + spin_lock_bh(&adapter->pause_map_lock); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_VI); + wlan_hdd_update_txq_timestamp(adapter->dev); + wlan_hdd_update_unpause_time(adapter); + adapter->pause_map |= (1 << reason); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_NETIF_VI_QUEUE_ON: + spin_lock_bh(&adapter->pause_map_lock); + temp_map = adapter->pause_map; + adapter->pause_map &= ~(1 << reason); + netif_wake_subqueue(adapter->dev, HDD_LINUX_AC_VI); + wlan_hdd_update_pause_time(adapter, temp_map); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_NETIF_VO_QUEUE_OFF: + spin_lock_bh(&adapter->pause_map_lock); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_VO); + wlan_hdd_update_txq_timestamp(adapter->dev); + wlan_hdd_update_unpause_time(adapter); + adapter->pause_map |= (1 << reason); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_NETIF_VO_QUEUE_ON: + spin_lock_bh(&adapter->pause_map_lock); + temp_map = adapter->pause_map; + adapter->pause_map &= ~(1 << reason); + netif_wake_subqueue(adapter->dev, HDD_LINUX_AC_VO); + wlan_hdd_update_pause_time(adapter, temp_map); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_START_ALL_NETIF_QUEUE: + spin_lock_bh(&adapter->pause_map_lock); + temp_map = adapter->pause_map; + adapter->pause_map &= ~(1 << reason); + if (!adapter->pause_map) { + netif_tx_start_all_queues(adapter->dev); + wlan_hdd_update_pause_time(adapter, temp_map); + } + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_WAKE_ALL_NETIF_QUEUE: + spin_lock_bh(&adapter->pause_map_lock); + temp_map = adapter->pause_map; + adapter->pause_map &= ~(1 << reason); + if (!adapter->pause_map) { + netif_tx_wake_all_queues(adapter->dev); + wlan_hdd_update_pause_time(adapter, temp_map); + } + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_WAKE_NON_PRIORITY_QUEUE: + spin_lock_bh(&adapter->pause_map_lock); + temp_map = adapter->pause_map; + adapter->pause_map &= ~(1 << reason); + if (!adapter->pause_map) { + wlan_hdd_wake_non_priority_queue(adapter); + wlan_hdd_update_pause_time(adapter, temp_map); + } + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER: + spin_lock_bh(&adapter->pause_map_lock); + if (!adapter->pause_map) { + netif_tx_stop_all_queues(adapter->dev); + wlan_hdd_update_txq_timestamp(adapter->dev); + wlan_hdd_update_unpause_time(adapter); + } + adapter->pause_map |= (1 << reason); + netif_carrier_off(adapter->dev); + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_START_ALL_NETIF_QUEUE_N_CARRIER: + spin_lock_bh(&adapter->pause_map_lock); + netif_carrier_on(adapter->dev); + temp_map = adapter->pause_map; + adapter->pause_map &= ~(1 << reason); + if (!adapter->pause_map) { + netif_tx_start_all_queues(adapter->dev); + wlan_hdd_update_pause_time(adapter, temp_map); + } + spin_unlock_bh(&adapter->pause_map_lock); + break; + + case WLAN_NETIF_ACTION_TYPE_NONE: + break; + + default: + hdd_err("unsupported action %d", action); + } + + spin_lock_bh(&adapter->pause_map_lock); + if (adapter->pause_map & (1 << WLAN_PEER_UNAUTHORISED)) + wlan_hdd_process_peer_unauthorised_pause(adapter); + + index = adapter->history_index++; + if (adapter->history_index == WLAN_HDD_MAX_HISTORY_ENTRY) + adapter->history_index = 0; + spin_unlock_bh(&adapter->pause_map_lock); + + wlan_hdd_update_queue_oper_stats(adapter, action, reason); + + adapter->queue_oper_history[index].time = qdf_system_ticks(); + adapter->queue_oper_history[index].netif_action = action; + adapter->queue_oper_history[index].netif_reason = reason; + adapter->queue_oper_history[index].pause_map = adapter->pause_map; + + txq_hist_ptr = &adapter->queue_oper_history[index]; + + wlan_hdd_update_queue_history_state(adapter->dev, txq_hist_ptr); +} + +void hdd_print_netdev_txq_status(struct net_device *dev) +{ + unsigned int i; + + if (!dev) + return; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + + hdd_debug("netdev tx queue[%u] state:0x%lx", + i, txq->state); + } +} + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * hdd_set_pktcapture_cb() - Set pkt capture mode callback + * @dev: Pointer to net_device structure + * @pdev_id: pdev id + * + * Return: 0 on success; non-zero for failure + */ +int hdd_set_pktcapture_cb(struct net_device *dev, uint8_t pdev_id) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + return cdp_register_pktcapture_cb(soc, pdev_id, adapter, + hdd_mon_rx_packet_cbk); +} + +/** + * hdd_reset_pktcapture_cb() - Reset pkt capture mode callback + * @pdev_id: pdev id + * + * Return: None + */ +void hdd_reset_pktcapture_cb(uint8_t pdev_id) +{ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + cdp_deregister_pktcapture_cb(soc, pdev_id); +} +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +/** + * hdd_set_mon_rx_cb() - Set Monitor mode Rx callback + * @dev: Pointer to net_device structure + * + * Return: 0 for success; non-zero for failure + */ +int hdd_set_mon_rx_cb(struct net_device *dev) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + QDF_STATUS qdf_status; + struct ol_txrx_desc_type sta_desc = {0}; + struct ol_txrx_ops txrx_ops; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + WLAN_ADDR_COPY(sta_desc.peer_addr.bytes, adapter->mac_addr.bytes); + qdf_mem_zero(&txrx_ops, sizeof(txrx_ops)); + txrx_ops.rx.rx = hdd_mon_rx_packet_cbk; + hdd_monitor_set_rx_monitor_cb(&txrx_ops, hdd_rx_monitor_callback); + cdp_vdev_register(soc, adapter->vdev_id, + (ol_osif_vdev_handle)adapter, + &txrx_ops); + /* peer is created wma_vdev_attach->wma_create_peer */ + qdf_status = cdp_peer_register(soc, OL_TXRX_PDEV_ID, &sta_desc); + if (QDF_STATUS_SUCCESS != qdf_status) { + hdd_err("cdp_peer_register() failed to register. Status= %d [0x%08X]", + qdf_status, qdf_status); + goto exit; + } + + qdf_status = sme_create_mon_session(hdd_ctx->mac_handle, + adapter->mac_addr.bytes, + adapter->vdev_id); + if (QDF_STATUS_SUCCESS != qdf_status) { + hdd_err("sme_create_mon_session() failed to register. Status= %d [0x%08X]", + qdf_status, qdf_status); + } + +exit: + ret = qdf_status_to_os_return(qdf_status); + return ret; +} +#endif + +/** + * hdd_send_rps_ind() - send rps indication to daemon + * @adapter: adapter context + * + * If RPS feature enabled by INI, send RPS enable indication to daemon + * Indication contents is the name of interface to find correct sysfs node + * Should send all available interfaces + * + * Return: none + */ +void hdd_send_rps_ind(struct hdd_adapter *adapter) +{ + int i; + uint8_t cpu_map_list_len = 0; + struct hdd_context *hdd_ctxt = NULL; + struct wlan_rps_data rps_data; + struct cds_config_info *cds_cfg; + + cds_cfg = cds_get_ini_config(); + + if (!adapter) { + hdd_err("adapter is NULL"); + return; + } + + if (!cds_cfg) { + hdd_err("cds_cfg is NULL"); + return; + } + + hdd_ctxt = WLAN_HDD_GET_CTX(adapter); + rps_data.num_queues = NUM_TX_QUEUES; + + hdd_debug("cpu_map_list '%s'", hdd_ctxt->config->cpu_map_list); + + /* in case no cpu map list is provided, simply return */ + if (!strlen(hdd_ctxt->config->cpu_map_list)) { + hdd_debug("no cpu map list found"); + goto err; + } + + if (QDF_STATUS_SUCCESS != + hdd_hex_string_to_u16_array(hdd_ctxt->config->cpu_map_list, + rps_data.cpu_map_list, + &cpu_map_list_len, + WLAN_SVC_IFACE_NUM_QUEUES)) { + hdd_err("invalid cpu map list"); + goto err; + } + + rps_data.num_queues = + (cpu_map_list_len < rps_data.num_queues) ? + cpu_map_list_len : rps_data.num_queues; + + for (i = 0; i < rps_data.num_queues; i++) { + hdd_debug("cpu_map_list[%d] = 0x%x", + i, rps_data.cpu_map_list[i]); + } + + strlcpy(rps_data.ifname, adapter->dev->name, + sizeof(rps_data.ifname)); + wlan_hdd_send_svc_nlink_msg(hdd_ctxt->radio_index, + WLAN_SVC_RPS_ENABLE_IND, + &rps_data, sizeof(rps_data)); + + cds_cfg->rps_enabled = true; + + return; + +err: + hdd_debug("Wrong RPS configuration. enabling rx_thread"); + cds_cfg->rps_enabled = false; +} + +/** + * hdd_send_rps_disable_ind() - send rps disable indication to daemon + * @adapter: adapter context + * + * Return: none + */ +void hdd_send_rps_disable_ind(struct hdd_adapter *adapter) +{ + struct hdd_context *hdd_ctxt = NULL; + struct wlan_rps_data rps_data; + struct cds_config_info *cds_cfg; + + cds_cfg = cds_get_ini_config(); + + if (!adapter) { + hdd_err("adapter is NULL"); + return; + } + + if (!cds_cfg) { + hdd_err("cds_cfg is NULL"); + return; + } + + hdd_ctxt = WLAN_HDD_GET_CTX(adapter); + rps_data.num_queues = NUM_TX_QUEUES; + + hdd_info("Set cpu_map_list 0"); + + qdf_mem_zero(&rps_data.cpu_map_list, sizeof(rps_data.cpu_map_list)); + + strlcpy(rps_data.ifname, adapter->dev->name, sizeof(rps_data.ifname)); + wlan_hdd_send_svc_nlink_msg(hdd_ctxt->radio_index, + WLAN_SVC_RPS_ENABLE_IND, + &rps_data, sizeof(rps_data)); + + cds_cfg->rps_enabled = false; +} + +#ifdef IPA_LAN_RX_NAPI_SUPPORT +void hdd_adapter_set_rps(uint8_t vdev_id, bool enable) +{ + struct hdd_context *hdd_ctx; + struct hdd_adapter *adapter; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) + return; + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err_rl("Adapter not found for vdev_id: %d", vdev_id); + return; + } + + hdd_debug("Set RPS to %d for vdev_id %d", enable, vdev_id); + if (!hdd_ctx->rps) { + if (enable) + hdd_send_rps_ind(adapter); + else + hdd_send_rps_disable_ind(adapter); + } +} +#endif + +void hdd_tx_queue_cb(hdd_handle_t hdd_handle, uint32_t vdev_id, + enum netif_action_type action, + enum netif_reason_type reason) +{ + struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); + struct hdd_adapter *adapter; + + /* + * Validating the context is not required here. + * if there is a driver unload/SSR in progress happening in a + * different context and it has been scheduled to run and + * driver got a firmware event of sta kick out, then it is + * good to disable the Tx Queue to stop the influx of traffic. + */ + if (!hdd_ctx) { + hdd_err("Invalid context passed"); + return; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (!adapter) { + hdd_err("vdev_id %d does not exist with host", vdev_id); + return; + } + hdd_debug("Tx Queue action %d on vdev %d", action, vdev_id); + + wlan_hdd_netif_queue_control(adapter, action, reason); +} + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +/** + * hdd_reset_tcp_delack() - Reset tcp delack value to default + * @hdd_ctx: Handle to hdd context + * + * Function used to reset TCP delack value to its default value + * + * Return: None + */ +void hdd_reset_tcp_delack(struct hdd_context *hdd_ctx) +{ + enum wlan_tp_level next_level = WLAN_SVC_TP_LOW; + struct wlan_rx_tp_data rx_tp_data = {0}; + + rx_tp_data.rx_tp_flags |= TCP_DEL_ACK_IND; + rx_tp_data.level = next_level; + hdd_ctx->rx_high_ind_cnt = 0; + wlan_hdd_update_tcp_rx_param(hdd_ctx, &rx_tp_data); +} + +void hdd_reset_tcp_adv_win_scale(struct hdd_context *hdd_ctx) +{ + enum wlan_tp_level next_level = WLAN_SVC_TP_NONE; + struct wlan_rx_tp_data rx_tp_data = {0}; + + rx_tp_data.rx_tp_flags |= TCP_ADV_WIN_SCL; + rx_tp_data.level = next_level; + hdd_ctx->cur_rx_level = WLAN_SVC_TP_NONE; + wlan_hdd_update_tcp_rx_param(hdd_ctx, &rx_tp_data); +} + +/** + * hdd_is_current_high_throughput() - Check if vote level is high + * @hdd_ctx: Handle to hdd context + * + * Function used to check if vote level is high + * + * Return: True if vote level is high + */ +#ifdef RX_PERFORMANCE +bool hdd_is_current_high_throughput(struct hdd_context *hdd_ctx) +{ + if (hdd_ctx->cur_vote_level < PLD_BUS_WIDTH_MEDIUM) + return false; + else + return true; +} +#endif +#endif + +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +/** + * hdd_ini_tx_flow_control() - Initialize INIs concerned about tx flow control + * @config: pointer to hdd config + * @psoc: pointer to psoc obj + * + * Return: none + */ +static void hdd_ini_tx_flow_control(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->tx_flow_low_watermark = + cfg_get(psoc, CFG_DP_LL_TX_FLOW_LWM); + config->tx_flow_hi_watermark_offset = + cfg_get(psoc, CFG_DP_LL_TX_FLOW_HWM_OFFSET); + config->tx_flow_max_queue_depth = + cfg_get(psoc, CFG_DP_LL_TX_FLOW_MAX_Q_DEPTH); + config->tx_lbw_flow_low_watermark = + cfg_get(psoc, CFG_DP_LL_TX_LBW_FLOW_LWM); + config->tx_lbw_flow_hi_watermark_offset = + cfg_get(psoc, CFG_DP_LL_TX_LBW_FLOW_HWM_OFFSET); + config->tx_lbw_flow_max_queue_depth = + cfg_get(psoc, CFG_DP_LL_TX_LBW_FLOW_MAX_Q_DEPTH); + config->tx_hbw_flow_low_watermark = + cfg_get(psoc, CFG_DP_LL_TX_HBW_FLOW_LWM); + config->tx_hbw_flow_hi_watermark_offset = + cfg_get(psoc, CFG_DP_LL_TX_HBW_FLOW_HWM_OFFSET); + config->tx_hbw_flow_max_queue_depth = + cfg_get(psoc, CFG_DP_LL_TX_HBW_FLOW_MAX_Q_DEPTH); +} +#else +static void hdd_ini_tx_flow_control(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH +/** + * hdd_ini_tx_flow_control() - Initialize INIs concerned about bus bandwidth + * @config: pointer to hdd config + * @psoc: pointer to psoc obj + * + * Return: none + */ +static void hdd_ini_bus_bandwidth(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->bus_bw_very_high_threshold = + cfg_get(psoc, CFG_DP_BUS_BANDWIDTH_VERY_HIGH_THRESHOLD); + config->bus_bw_high_threshold = + cfg_get(psoc, CFG_DP_BUS_BANDWIDTH_HIGH_THRESHOLD); + config->bus_bw_medium_threshold = + cfg_get(psoc, CFG_DP_BUS_BANDWIDTH_MEDIUM_THRESHOLD); + config->bus_bw_low_threshold = + cfg_get(psoc, CFG_DP_BUS_BANDWIDTH_LOW_THRESHOLD); + config->bus_bw_compute_interval = + cfg_get(psoc, CFG_DP_BUS_BANDWIDTH_COMPUTE_INTERVAL); + config->bus_low_cnt_threshold = + cfg_get(psoc, CFG_DP_BUS_LOW_BW_CNT_THRESHOLD); + config->enable_latency_crit_clients = + cfg_get(psoc, CFG_DP_BUS_HANDLE_LATENCY_CRITICAL_CLIENTS); +} + +/** + * hdd_ini_tx_flow_control() - Initialize INIs concerned about tcp settings + * @config: pointer to hdd config + * @psoc: pointer to psoc obj + * + * Return: none + */ +static void hdd_ini_tcp_settings(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->enable_tcp_limit_output = + cfg_get(psoc, CFG_DP_ENABLE_TCP_LIMIT_OUTPUT); + config->enable_tcp_adv_win_scale = + cfg_get(psoc, CFG_DP_ENABLE_TCP_ADV_WIN_SCALE); + config->enable_tcp_delack = + cfg_get(psoc, CFG_DP_ENABLE_TCP_DELACK); + config->tcp_delack_thres_high = + cfg_get(psoc, CFG_DP_TCP_DELACK_THRESHOLD_HIGH); + config->tcp_delack_thres_low = + cfg_get(psoc, CFG_DP_TCP_DELACK_THRESHOLD_LOW); + config->tcp_delack_timer_count = + cfg_get(psoc, CFG_DP_TCP_DELACK_TIMER_COUNT); + config->tcp_tx_high_tput_thres = + cfg_get(psoc, CFG_DP_TCP_TX_HIGH_TPUT_THRESHOLD); + config->enable_tcp_param_update = + cfg_get(psoc, CFG_DP_ENABLE_TCP_PARAM_UPDATE); +} +#else +static void hdd_ini_bus_bandwidth(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} + +static void hdd_ini_tcp_settings(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ + +/** + * hdd_set_rx_mode_value() - set rx_mode values + * @hdd_ctx: hdd context + * + * Return: none + */ +static void hdd_set_rx_mode_value(struct hdd_context *hdd_ctx) +{ + uint32_t rx_mode = hdd_ctx->config->rx_mode; + enum QDF_GLOBAL_MODE con_mode = 0; + + con_mode = hdd_get_conparam(); + + /* RPS has higher priority than dynamic RPS when both bits are set */ + if (rx_mode & CFG_ENABLE_RPS && rx_mode & CFG_ENABLE_DYNAMIC_RPS) + rx_mode &= ~CFG_ENABLE_DYNAMIC_RPS; + + if (rx_mode & CFG_ENABLE_RX_THREAD && rx_mode & CFG_ENABLE_RPS) { + hdd_warn("rx_mode wrong configuration. Make it default"); + rx_mode = CFG_RX_MODE_DEFAULT; + } + + if (rx_mode & CFG_ENABLE_RX_THREAD) + hdd_ctx->enable_rxthread = true; + else if (rx_mode & CFG_ENABLE_DP_RX_THREADS) { + if (con_mode == QDF_GLOBAL_MONITOR_MODE) + hdd_ctx->enable_dp_rx_threads = false; + else + hdd_ctx->enable_dp_rx_threads = true; + } + + if (rx_mode & CFG_ENABLE_RPS) + hdd_ctx->rps = true; + + if (rx_mode & CFG_ENABLE_NAPI) + hdd_ctx->napi_enable = true; + + if (rx_mode & CFG_ENABLE_DYNAMIC_RPS) + hdd_ctx->dynamic_rps = true; + + hdd_debug("rx_mode:%u dp_rx_threads:%u rx_thread:%u napi:%u rps:%u dynamic rps %u", + rx_mode, hdd_ctx->enable_dp_rx_threads, + hdd_ctx->enable_rxthread, hdd_ctx->napi_enable, + hdd_ctx->rps, hdd_ctx->dynamic_rps); +} + +#ifdef CONFIG_DP_TRACE +static void +hdd_dp_dp_trace_cfg_update(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + qdf_size_t array_out_size; + + config->enable_dp_trace = cfg_get(psoc, CFG_DP_ENABLE_DP_TRACE); + qdf_uint8_array_parse(cfg_get(psoc, CFG_DP_DP_TRACE_CONFIG), + config->dp_trace_config, + sizeof(config->dp_trace_config), &array_out_size); + config->dp_proto_event_bitmap = cfg_get(psoc, + CFG_DP_PROTO_EVENT_BITMAP); +} +#else +static void +hdd_dp_dp_trace_cfg_update(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifdef WLAN_NUD_TRACKING +static void +hdd_dp_nud_tracking_cfg_update(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->enable_nud_tracking = cfg_get(psoc, CFG_DP_ENABLE_NUD_TRACKING); +} +#else +static void +hdd_dp_nud_tracking_cfg_update(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK +static void hdd_ini_tcp_del_ack_settings(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->del_ack_threshold_high = + cfg_get(psoc, CFG_DP_DRIVER_TCP_DELACK_HIGH_THRESHOLD); + config->del_ack_threshold_low = + cfg_get(psoc, CFG_DP_DRIVER_TCP_DELACK_LOW_THRESHOLD); + config->del_ack_enable = + cfg_get(psoc, CFG_DP_DRIVER_TCP_DELACK_ENABLE); + config->del_ack_pkt_count = + cfg_get(psoc, CFG_DP_DRIVER_TCP_DELACK_PKT_CNT); + config->del_ack_timer_value = + cfg_get(psoc, CFG_DP_DRIVER_TCP_DELACK_TIMER_VALUE); +} +#else +static void hdd_ini_tcp_del_ack_settings(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +#ifdef WLAN_SUPPORT_TXRX_HL_BUNDLE +static void hdd_dp_hl_bundle_cfg_update(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ + config->pkt_bundle_threshold_high = + cfg_get(psoc, CFG_DP_HL_BUNDLE_HIGH_TH); + config->pkt_bundle_threshold_low = + cfg_get(psoc, CFG_DP_HL_BUNDLE_LOW_TH); + config->pkt_bundle_timer_value = + cfg_get(psoc, CFG_DP_HL_BUNDLE_TIMER_VALUE); + config->pkt_bundle_size = + cfg_get(psoc, CFG_DP_HL_BUNDLE_SIZE); +} +#else +static void hdd_dp_hl_bundle_cfg_update(struct hdd_config *config, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif + +void hdd_dp_cfg_update(struct wlan_objmgr_psoc *psoc, + struct hdd_context *hdd_ctx) +{ + struct hdd_config *config; + uint16_t cfg_len; + + config = hdd_ctx->config; + cfg_len = qdf_str_len(cfg_get(psoc, CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST)) + + 1; + hdd_ini_tx_flow_control(config, psoc); + hdd_ini_bus_bandwidth(config, psoc); + hdd_ini_tcp_settings(config, psoc); + + hdd_ini_tcp_del_ack_settings(config, psoc); + + hdd_dp_hl_bundle_cfg_update(config, psoc); + + config->napi_cpu_affinity_mask = + cfg_get(psoc, CFG_DP_NAPI_CE_CPU_MASK); + config->rx_thread_ul_affinity_mask = + cfg_get(psoc, CFG_DP_RX_THREAD_UL_CPU_MASK); + config->rx_thread_affinity_mask = + cfg_get(psoc, CFG_DP_RX_THREAD_CPU_MASK); + config->fisa_enable = cfg_get(psoc, CFG_DP_RX_FISA_ENABLE); + if (cfg_len < CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST_LEN) { + qdf_str_lcopy(config->cpu_map_list, + cfg_get(psoc, CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST), + cfg_len); + } else { + hdd_err("ini string length greater than max size %d", + CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST_LEN); + cfg_len = qdf_str_len(cfg_default(CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST)); + qdf_str_lcopy(config->cpu_map_list, + cfg_default(CFG_DP_RPS_RX_QUEUE_CPU_MAP_LIST), + cfg_len); + } + config->tx_orphan_enable = cfg_get(psoc, CFG_DP_TX_ORPHAN_ENABLE); + config->rx_mode = cfg_get(psoc, CFG_DP_RX_MODE); + hdd_set_rx_mode_value(hdd_ctx); + config->multicast_replay_filter = + cfg_get(psoc, CFG_DP_FILTER_MULTICAST_REPLAY); + config->rx_wakelock_timeout = + cfg_get(psoc, CFG_DP_RX_WAKELOCK_TIMEOUT); + config->num_dp_rx_threads = cfg_get(psoc, CFG_DP_NUM_DP_RX_THREADS); + config->cfg_wmi_credit_cnt = cfg_get(psoc, CFG_DP_HTC_WMI_CREDIT_CNT); + hdd_dp_dp_trace_cfg_update(config, psoc); + hdd_dp_nud_tracking_cfg_update(config, psoc); +} + +bool wlan_hdd_rx_rpm_mark_last_busy(struct hdd_context *hdd_ctx, + void *hif_ctx) +{ + uint64_t duration_us, dp_rx_busy_us, current_us; + uint32_t rpm_delay_ms; + + if (!hif_pm_runtime_is_dp_rx_busy(hif_ctx)) + return false; + + dp_rx_busy_us = hif_pm_runtime_get_dp_rx_busy_mark(hif_ctx); + current_us = qdf_get_log_timestamp_usecs(); + duration_us = (unsigned long)((ULONG_MAX - dp_rx_busy_us) + + current_us + 1); + rpm_delay_ms = ucfg_pmo_get_runtime_pm_delay(hdd_ctx->psoc); + + if (duration_us < (rpm_delay_ms * 1000)) + return true; + else + return false; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_wext.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_wext.c new file mode 100644 index 0000000000000000000000000000000000000000..62b25d8a3556f69e1c1536c50cb6b1b946ce7f7b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_wext.c @@ -0,0 +1,11050 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_hdd_wext.c + * + * Linux Wireless Extensions Implementation + */ + +#include +#include +#include +#include +#include +#include +#include "osif_sync.h" +#include +#include +#include "scheduler_api.h" +#include +#include +#include +#include "sir_params.h" +#include "csr_api.h" +#include "csr_inside_api.h" +#include "sme_rrm_internal.h" +#include +#include "dot11f.h" +#include +#include +#include +#include "utils_api.h" +#include "wlan_hdd_p2p.h" +#ifdef FEATURE_WLAN_TDLS +#include "wlan_hdd_tdls.h" +#endif + +#include "cds_ieee80211_common.h" +#include "ol_if_athvar.h" +#include "dbglog_host.h" +#include "wma.h" + +#include + +#include "wlan_hdd_power.h" +#include "qwlan_version.h" +#include "wlan_hdd_host_offload.h" + +#include +#include + +#include "wlan_hdd_misc.h" + +#include "qc_sap_ioctl.h" +#include "sme_api.h" +#include "wma_types.h" +#include "qdf_delayed_work_test.h" +#include "qdf_hashtable_test.h" +#include "qdf_periodic_work_test.h" +#include "qdf_ptr_hash_test.h" +#include "qdf_slist_test.h" +#include "qdf_talloc_test.h" +#include "qdf_str.h" +#include "qdf_trace.h" +#include "qdf_tracker_test.h" +#include "qdf_types_test.h" +#include "wlan_hdd_assoc.h" +#include "wlan_hdd_ioctl.h" +#include "wlan_hdd_scan.h" +#include "sme_power_save_api.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_hdd_conc_ut.h" +#include "wlan_hdd_fips.h" +#include "wlan_hdd_tsf.h" +#include "wlan_hdd_ocb.h" +#include "wlan_hdd_napi.h" +#include "cdp_txrx_flow_ctrl_legacy.h" +#include "wlan_hdd_nan_datapath.h" +#include "wlan_hdd_stats.h" +#ifdef WLAN_SUSPEND_RESUME_TEST +#include "wlan_hdd_driver_ops.h" +#include "hif.h" +#endif +#include "pld_common.h" +#include "wlan_hdd_lro.h" +#include "cds_utils.h" +#include "wlan_osif_request_manager.h" +#include "os_if_wifi_pos.h" +#include +#include +#include "wlan_dsc_test.h" +#include +#include "wlan_hdd_regulatory.h" +#include "wlan_reg_ucfg_api.h" +#include "wlan_hdd_packet_filter_api.h" +#include "wlan_cp_stats_mc_ucfg_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "cfg_mlme_sta.h" +#include "wlan_mlme_public_struct.h" +#include "cfg_ucfg_api.h" +#include "wlan_policy_mgr_ucfg.h" +#include "wlan_mlme_public_struct.h" +#include "cfg_ucfg_api.h" +#include "cfg_mlme_threshold.h" +#include "wlan_pmo_cfg.h" +#include "wlan_pmo_ucfg_api.h" +#include "dp_txrx.h" +#include "wlan_fwol_ucfg_api.h" + +/* Private ioctls and their sub-ioctls */ +#define WLAN_PRIV_SET_INT_GET_NONE (SIOCIWFIRSTPRIV + 0) +#define WE_SET_11D_STATE 1 +#define WE_WOWL 2 +#define WE_SET_POWER 3 +/* + * + * setMaxAssoc - Sets the maximum number of associated stations + * + * @INPUT: 1 to 32 + * + * @OUTPUT: None + * + * This IOTCL sets the maximum number of associated stations + * + * @E.g: iwpriv wlan0 setMaxAssoc + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_MAX_ASSOC 4 +/* + * + * scan_disable - Disable scan + * + * @INPUT: set_value + * + * @OUTPUT: None + * + * This IOCTL is used to set disable scan + * + * @E.g: iwpriv wlan0 scan_disable 1 + * + * Supported Feature: Scan + * + * Usage: Internal/External + * + * + */ +#define WE_SET_SCAN_DISABLE 5 +/* + * + * inactivityTO - sets the timeout value for inactivity data while + * in power save mode + * + * @INPUT: int1…..int255 + * + * @OUTPUT: None + * + * This IOCTL set the timeout value for inactivity data in power save mode + * + * @E.g: iwpriv wlan0 inactivityTO 20 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_DATA_INACTIVITY_TO 6 +/* + * + * setMaxTxPower - Dynamically sets the maximum transmission power + * + * @INPUT: Transmission power in dBm + * + * @OUTPUT: None + * + * This IOCTL dynamically sets the maximum transmission power + * This setting does not persist over reboots + * + * @E.g: iwpriv wlan0 setMaxTxPower + */ +#define WE_SET_MAX_TX_POWER 7 + +#ifdef HASTINGS_BT_WAR +/* Temporary WAR for Hastings 1.1 only */ +#define WE_SET_HASTINGS_BT_WAR 8 +#endif + +#define WE_SET_TM_LEVEL 9 + +/* + * + * setphymode - Set the phymode dynamically + * + * @INPUT: 0 IEEE80211_MODE_AUTO to 22 IEEE80211_MODE_11AGN + * + * @OUTPUT: None + * + * This IOCTL sets the phymode dynamically + * + * @E.g: iwpriv wlan0 setphymode 10 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_PHYMODE 10 +/* + * + * nss - Set the number of spatial streams + * + * @INPUT: int1…..int3 + * + * @OUTPUT: None + * + * This IOCTL sets the number of spatial streams. Supported values are 1 and 2 + * + * @E.g: iwpriv wlan0 nss 2 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_NSS 11 +/* + * + * ldpc - Enables or disables LDPC + * + * @INPUT: 0 – Disable, 1 - Enable + * + * @OUTPUT: None + * + * This IOCTL enables or disables LDPC + * + * @E.g: iwpriv wlan0 ldpc 1 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_LDPC 12 +/* + * + * tx_stbc - Enables or disables tx_stbc + * + * @INPUT: Int 0 – Disable, 1 - Enable + * + * @OUTPUT: None + * + * This IOTCL used to enables or disables tx_stbc + * + * @E.g: iwpriv wlan0 tx_stbc + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_TX_STBC 13 +/* + * + * rx_stbc - Set the rx_stbc parameter + * + * @INPUT: Int 0 – Disable, 1 - Enable + * + * @OUTPUT: None + * + * This IOTCL used to set rx_stbc parameter + * + * @E.g: iwpriv wlan0 rx_stbc + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_RX_STBC 14 +/* + * + * shortgi - Sets the short-guard interval + * + * @INPUT: Fixed Rate: 0 - 400ns, 1 - 800ns, 2 - 1600ns, 3 - 3200us + * Auto Rate: 8 - 400ns, 9 - 800ns, 10 - 1600ns, 11 - 3200us + * + * @OUTPUT: None + * + * This IOCTL sets the short-guard interval. + * + * @E.g: iwpriv wlan0 shortgi + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_SHORT_GI 15 +/* + * + * enablertscts - enables or disables rts/cts. + * + * @INPUT: 1-Enable , 0-Disable + * + * @OUTPUT: None + * + * This IOCTL enables or disables rts/cts. + * + * @E.g: iwpriv wlan0 enablertscts + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_RTSCTS 16 +/* + * + * chwidth - Set the channel bandwidth + * + * @INPUT: 0-20mhz to 3-160mhz + * + * @OUTPUT: None + * + * This IOTCL used to set the channel bandwidth + * + * @E.g: iwpriv wlan0 chwidth 1 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_CHWIDTH 17 +#define WE_SET_ANI_EN_DIS 18 +#define WE_SET_ANI_POLL_PERIOD 19 +#define WE_SET_ANI_LISTEN_PERIOD 20 +#define WE_SET_ANI_OFDM_LEVEL 21 +#define WE_SET_ANI_CCK_LEVEL 22 +/* + * + * cwmenable - Enables or disables the dynamic channel bandwidth + * + * @INPUT: 0-Disable, 1-Enable + * + * @OUTPUT: None + * + * This IOTCL used to enables or disables the dynamic channel bandwidth + * + * @E.g: iwpriv wlan0 cwmenable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_DYNAMIC_BW 23 +/* + * + * txchainmask - This IOCTL sets the current Tx chain mask + * + * @INPUT: Mask Value + * + * @OUTPUT: None + * + * This IOCTL sets the current Tx chain mask + * + * @E.g: iwpriv wlan0 txchainmask 1 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_TX_CHAINMASK 24 +/* + * + * rxchainmask - Sets the current Rx chain mask + * + * @INPUT: Mask Value + * + * @OUTPUT: None + * + * This IOCTL sets the current Rx chain mask. This command is the + * equivalent to setting in gSetRxChainmask1x1 in WCNSS_qcom_cfg.ini. + * + * @E.g: iwpriv wlan0 rxchainmask + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_RX_CHAINMASK 25 +/* + * + * set11NRates - Fixes the Tx data rate of the 11N mode. + * + * @INPUT: 0x1b to 0x8f + * + * @OUTPUT: None + * + * This IOCTL fixes the Tx data rate of the 11N mode. + * + * @E.g: iwpriv wlan0 set11NRates 0x85 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_11N_RATE 26 +/* + * + * ampdu - Set the the maximum subframe of ampdu + * + * @INPUT: int 1 to int 63 + * + * @OUTPUT: None + * + * This IOCTL sets the maximum subframe of ampdu. + * + * @E.g: iwpriv wlan0 ampdu 9 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_AMPDU 27 +/* + * + * amsdu - Sets the maximum subframe of amsdu. + * + * @INPUT: int 1 to int 31 + * + * @OUTPUT: None + * + * This IOCTL sets the maximum subframe of amsdu. + * + * @E.g: iwpriv wlan0 amsdu 9 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_AMSDU 28 +/* + * + * txpow2g - current 2 GHz Tx power setting + * + * @INPUT: Tx power in dBm + * + * @OUTPUT: None + * + * This IOTCL used to set 2 ghz tx power + * + * @E.g: iwpriv wlan0 txpow2g + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_TXPOW_2G 29 +/* + * + * txpow5g - Current 5 GHz tx power setting + * + * @INPUT: Tx power in dBm + * + * @OUTPUT: None + * + * This IOTCL used to set the 5 ghz txpower + * + * @E.g: iwpriv wlan0 txpow5g + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_TXPOW_5G 30 +/* Private ioctl for firmware debug log */ +#define WE_DBGLOG_LOG_LEVEL 31 +#define WE_DBGLOG_VAP_ENABLE 32 +#define WE_DBGLOG_VAP_DISABLE 33 +#define WE_DBGLOG_MODULE_ENABLE 34 +#define WE_DBGLOG_MODULE_DISABLE 35 +#define WE_DBGLOG_MOD_LOG_LEVEL 36 +#define WE_DBGLOG_TYPE 37 +#define WE_SET_TXRX_FWSTATS 38 +/* + * + * set11ACRates - Fixes the Tx data rate of 11AC + * + * @INPUT: 0x1 to 0x9 + * + * @OUTPUT: None + * + * This IOCTL fixes the Tx data rate of 11AC. + * + * @E.g: iwpriv wlan0 set11ACRates 0x9 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_VHT_RATE 39 +#define WE_DBGLOG_REPORT_ENABLE 40 +#define WE_TXRX_FWSTATS_RESET 41 +/* + * + * setTxMaxPower2G - Set the maximum transmit power for the 2.4-GHz band + * + * @INPUT: Transmission power in dBm + * + * @OUTPUT: None + * + * This IOCTL sets the maximum transmit power for the 2.4-GHz band + * This setting does not persist over reboots + * + * @E.g: iwpriv wlan0 setTxMaxPower2G 10 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_MAX_TX_POWER_2_4 42 +/* + * + * setTxMaxPower5G - Set the maximum transmit power for the 5-GHz band + * + * @INPUT: Transmission power in dBm + * + * @OUTPUT: None + * + * This IOCTL sets the maximum transmit power for the 5-GHz band + * This setting does not persist over reboots + * + * @E.g: iwpriv wlan0 setTxMaxPower5G 10 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_MAX_TX_POWER_5_0 43 +#define WE_SET_PKTLOG 44 +/* Private ioctl for packet power save */ +#define WE_PPS_PAID_MATCH 45 +#define WE_PPS_GID_MATCH 46 +#define WE_PPS_EARLY_TIM_CLEAR 47 +#define WE_PPS_EARLY_DTIM_CLEAR 48 +#define WE_PPS_EOF_PAD_DELIM 49 +#define WE_PPS_MACADDR_MISMATCH 50 +#define WE_PPS_DELIM_CRC_FAIL 51 +#define WE_PPS_GID_NSTS_ZERO 52 +/* + * + * rssi_chk - Check the rssi + * + * @INPUT: One argument as input + * + * @OUTPUT: rssi + * wlan0 rssi_chk:56 + * + * This IOTCL used to chek rssi + * + * @E.g: iwpriv wlan0 rssi_chk + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_PPS_RSSI_CHECK 53 +/* + * + * htsmps - Sets the htsmps + * + * @INPUT: Atleast one int argument + * + * @OUTPUT: None + * + * This IOTCL used to set htsmps + * + * @E.g: iwpriv wlan0 htsmps + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_HTSMPS 55 +/* Private ioctl for QPower */ +#define WE_SET_QPOWER_MAX_PSPOLL_COUNT 56 +#define WE_SET_QPOWER_MAX_TX_BEFORE_WAKE 57 +#define WE_SET_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL 58 +#define WE_SET_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL 59 +/* GTX Commands */ +/* + * + * gtxHTMcs - Set the tx HTM value + * + * @INPUT: Atleast one int orgument + * + * @OUTPUT: None + * + * This IOTCL sets htm tx value + * + * @E.g: iwpriv wlan0 gtxHTMcs + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_GTX_HT_MCS 62 +/* + * + * gtxVHTMcs - Set gtxVHTMcs value + * + * @INPUT: Atleast one int argument + * + * @OUTPUT: None + * + * This IOTCL used to set gtxVHTMcs value + * + * @E.g: iwpriv wlan0 gtxVHTMcs + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_GTX_VHT_MCS 63 +/* + * + * gtxUsrCfg - Host request for GTX mask + * + * @INPUT: Atleast one int orgument + * + * @OUTPUT: None + * + * This IOTCL used send the host request for GTX mask + * + * @E.g: iwpriv wlan0 gtxUsrCfg + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_GTX_USRCFG 64 +/* + * + * gtxThre - Set the tx threshold + * + * @INPUT: Atleast one int argument + * + * @OUTPUT: None + * + * This IOTCL used to set tx threshold + * + * @E.g: iwpriv wlan0 gtxThre + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_GTX_THRE 65 +/* + * + * gtxMargin - Set the gtxMargin + * + * @INPUT: 1 to 32 + * + * @OUTPUT: None + * + * This IOTCL use dto set gtxMargin + * + * @E.g: iwpriv wlan0 gtxMargini + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_GTX_MARGIN 66 +/* + * + * gtxStep - Set the gtxStep + * + * @INPUT: None + * + * @OUTPUT: None + * + * This IOTCL used to sets gtxStep + * + * @E.g: iwpriv wlan0 gtxStep + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_GTX_STEP 67 +/* + * + * gtxMinTpc - Sets the gtxMinTpc + * + * @INPUT: Atleast one int argument + * + * @OUTPUT: None + * + * This IOTCL sets the tx MinTpc + * + * @E.g: iwpriv wlan0 gtxMinTpc + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_GTX_MINTPC 68 +/* + * + * gtxBWMask - Sets the BW mask (20/40/80/160 Mhz) + * + * @INPUT: Mask value + * + * @OUTPUT: None + * + * This IOTCL used to set gtxBWMask + * + * @E.g: iwpriv wlan0 gtxBWMask + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + +#define WE_SET_GTX_BWMASK 69 +/* + * + * setMccLatency - Sets the MCC latency value during STA-P2P concurrency + * + * @INPUT: set_value + * + * @OUTPUT: None + * + * This IOCTL is used to set the MCC latency value in milliseconds + * during STA-P2P concurrency. + * + * If 0ms latency is provided, then FW will set to a default. + * Otherwise, latency must be at least 30ms. + * + * @E.g: iwpriv wlan0 setMccLatency 40 + * + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define WE_MCC_CONFIG_LATENCY 70 + +/* + * + * setMccQuota- Set the quota for P2P cases + * + * @INPUT: set_value [0,100] + * + * @OUTPUT: None + * + * This IOCTL is used to set the quota in milliseconds for P2P_GO/STA. + * + * Currently used to set time quota for 2 MCC vdevs/adapters using + * (operating channel, quota) for each mode. + * The info is provided run time using iwpriv command: + * iwpriv setMccQuota . + * Note: the quota provided in command is for the same mode in cmd. + * HDD checks if MCC mode is active, gets the second mode and its + * operating chan. + * Quota for the 2nd role is calculated as 100 - quota of first mode. + * + * @E.g: iwpriv wlan0 setMccQuota 50 + * iwpriv p2p0 setMccQuota 50 + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define WE_MCC_CONFIG_QUOTA 71 +/* Private IOCTL for debug connection issues */ +#define WE_SET_DEBUG_LOG 72 +#ifdef WE_SET_TX_POWER +#undef WE_SET_TX_POWER +#endif + +/* + * + * setTxPower - Set the current transmit power + * + * @INPUT: Transmission power in dBm + * + * @OUTPUT: None + * + * This IOCTL sets the current transmit power. + * This setting does not persist over reboots. + * + * @E.g: iwpriv wlan0 setTxPower 10 + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_TX_POWER 74 +/* Private ioctl for earlyrx power save feature */ +#define WE_SET_EARLY_RX_ADJUST_ENABLE 75 +#define WE_SET_EARLY_RX_TGT_BMISS_NUM 76 +#define WE_SET_EARLY_RX_BMISS_SAMPLE_CYCLE 77 +#define WE_SET_EARLY_RX_SLOP_STEP 78 +#define WE_SET_EARLY_RX_INIT_SLOP 79 +#define WE_SET_EARLY_RX_ADJUST_PAUSE 80 +/* + * + * setMcRate - Set the data rate for multicast data + * + * @INPUT: 1 to 32 + * + * @OUTPUT: None + * + * This IOCTL sets the data rate for multicast data. Note that this command + * is allowed only in STA, IBSS, or QCMobileAP mode + * + * @E.g: iwpriv wlan0 setMcRate + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_MC_RATE 81 +#define WE_SET_EARLY_RX_DRIFT_SAMPLE 82 +/* Private ioctl for packet power save */ +/* + * + * 5g_ebt - Sets the 5g_ebt + * + * @INPUT: + * + * @OUTPUT: None + * + * This IOTCL used to set 5g_ebt + * + * @E.g: iwpriv wlan0 5g_ebt + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_PPS_5G_EBT 83 +/* + * + * cts_cbw - Set CTS channel BW for dynamic BW adjustment + * + * @INPUT: 20 t0 160 + * + * @OUTPUT: None + * + * This IOTCL used to set CTS channel BW for dynamic BW adjustment + * + * @E.g: iwpriv wlan0 cts_cbw + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_CTS_CBW 84 +#define WE_DUMP_STATS 85 +#define WE_CLEAR_STATS 86 +/* Private sub ioctl for starting/stopping the profiling */ +#define WE_START_FW_PROFILE 87 + +/* + * + * setChanChange - Initiate channel change + * + * @INPUT: channel number to switch to. + * + * @OUTPUT: None + * + * This IOCTL is used to initiate a channel change. + * If called on STA/CLI interface it will send the + * ECSA action frame to the connected SAP/GO asking to + * initiate the ECSA, if supported. + * If called on SAP/GO interface it will initiate + * ECSA and ask connected peers to move to new channel. + * + * @E.g: iwpriv wlan0 setChanChange + * iwpriv wlan0 setChanChange 1 + * + * Supported Feature: ECSA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_CHANNEL 88 +#define WE_SET_CONC_SYSTEM_PREF 89 + +/* + * + * set_11ax_rate - set 11ax rates to FW + * + * @INPUT: rate code + * + * @OUTPUT: None + * + * This IOCTL fixes the Tx data rate of 11AX. + * + * @E.g: iwpriv wlan0 set_11ax_rate + * + * Supported Feature: STA/SAP + * + * Usage: Internal + * + * + */ +#define WE_SET_11AX_RATE 91 + +/* + * + * enable_dcm - enable Dual Carrier Modulation(DCM) + * + * @INPUT: 0/1 + * + * @OUTPUT: None + * + * This IOCTL enables/disables DCM. + * + * @E.g: iwpriv wlan0 enable_dcm <0/1> + * + * Supported Feature: STA/SAP + * + * Usage: Internal + * + * + */ +#define WE_SET_DCM 92 + +/* + * + * range_ext - enable Range extension + * + * @INPUT: 0/1 + * + * @OUTPUT: None + * + * This IOCTL enables/disables Range extension. + * + * @E.g: iwpriv wlan0 range_ext <1/0> + * + * Supported Feature: STA/SAP + * + * Usage: Internal + * + * + */ +#define WE_SET_RANGE_EXT 93 + +/* + * + * wow_ito - sets the timeout value for inactivity data while + * in power save mode during wow + * + * @INPUT: int + * + * @OUTPUT: None + * + * This IOCTL set the timeout value for inactivity data in power save mode + * + * @E.g: iwpriv wlan0 wow_ito 20 + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define WE_SET_WOW_DATA_INACTIVITY_TO 94 + +/* + * + * pdev_reset - reset the pdev + * + * @INPUT: Reset command to initiate: + * TX_FLUSH = 1 + * WARM_RESET = 2 + * COLD_RESET = 3 + * WARM_RESET_RESTORE_CAL = 4 + * COLD_RESET_RESTORE_CAL = 5 + * + * @OUTPUT: None + * + * This IOCTL is used to reset the pdev. The primary use is + * for internal testing. It is not expected that this will + * be used on a production device. + * + * @E.g: iwpriv wlan0 pdev_reset + * iwpriv wlan0 pdev_reset 1 + * + * Supported Feature: None + * + * Usage: Internal + * + * + */ +#define WE_SET_PDEV_RESET 95 + +/* + * setModDTIM - Change Modulated DTIM + * + * @INPUT: set_value. + * + * @OUTPUT: None + * + * This IOCTL is used to change modulated DTIM + * value without WIFI OFF/ON. + * + * @E.g: iwpriv wlan0 setModDTIM + * iwpriv wlan0 setModDTIM 2 + * + * Supported Feature: N/A + * + * Usage: Internal/External + * + * + */ +#define WE_SET_MODULATED_DTIM 96 + +#ifdef WLAN_FEATURE_MOTION_DETECTION +#define WE_MOTION_DET_START_STOP 97 +#define WE_MOTION_DET_BASE_LINE_START_STOP 98 +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +/* + * set_btc_mode Set BTCoexMode + * + * @INPUT: set_value. + * + * @OUTPUT: None + * + * This IOCTL is used to set the BT COex operating mode + * Allowed values are 0(TDD), 1(FDD), 2(Hybrid) + * + * @E.g: iwpriv wlan0 set_btc_mode + * iwpriv wlan0 set_btc_mode 2 + * + * Supported Feature: N/A + * + * Usage: Internal/External + * + * + */ +#define WE_SET_BTCOEX_MODE 99 + +/* + * set_btc_rssi- Set WLAN low RSSI threshold for BTCOex + * + * @INPUT: set_value. + * + * @OUTPUT: None + * + * This IOCTL is used to modify the threshold at which + * the COex mode changes from TDD to Hybrid mode + * Allowed values are from -100 to 0 + * + * @E.g: iwpriv wlan0 set_btc_rssi + * iwpriv wlan0 set_btc_rssi -70 + * + * Supported Feature: N/A + * + * Usage: Internal/External + * + * + */ +#define WE_SET_BTCOEX_RSSI_THRESHOLD 100 + +/* Private ioctls and their sub-ioctls */ +#define WLAN_PRIV_SET_NONE_GET_INT (SIOCIWFIRSTPRIV + 1) +#define WE_GET_11D_STATE 1 +#define WE_GET_WLAN_DBG 4 +#define WE_GET_MAX_ASSOC 6 +/* 7 is unused */ +#define WE_GET_SAP_AUTO_CHANNEL_SELECTION 8 + +/* + * + * getconcurrency - Get concurrency mode + * + * @INPUT: None + * + * @OUTPUT: It shows concurrency value + * Bit 0:STA 1:SAP 2:P2P_Client 3:P2P_GO + * 4:FTM 5:IBSS 6:Monitor 7:P2P_Device + * 8:OCB 9:EPPING 10:QVIT 11:NDI + * + * This IOCTL is used to retrieve concurrency mode. + * + * @E.g: iwpriv wlan0 getconcurrency + * wlan0 getconcurrency:5 + * Above value shows STA+P2P_Client + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define WE_GET_CONCURRENCY_MODE 9 +/* + * + * get_nss - Get the number of spatial STBC streams (NSS) + * + * @INPUT: None + * + * @OUTPUT: NSS + * wlan0 get_nss:2 + * + * This IOTCL used to get the number of spatial STBC streams + * + * @E.g: iwpriv wlan0 get_nss + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_NSS 11 +/* + * + * get_ldpc - This IOCTL gets the low density parity check (LDPC) + * + * @INPUT: None + * + * @OUTPUT: ldpc + * wlan0 get_ldpc:1 + * + * This IOTCL used to gets the low density parity check (LDPC) + * + * @E.g: iwpriv wlan0 get_ldpc + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_LDPC 12 +/* + * + * get_tx_stbc - Get the value of the current Tx space time block code (STBC) + * + * @INPUT: None + * + * @OUTPUT: TXSTBC + * wlan0 get_tx_stbc:1 + * + * This IOTCL get the value of the current Tx space time block code (STBC) + * + * @E.g: iwpriv wlan0 get_tx_stbc + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_TX_STBC 13 +/* + * + * get_rx_stbc - Gets the value of the current Rx STBC + * + * @INPUT: None + * + * @OUTPUT: Rx STBC + * wlan0 get_rx_stbc:1 + * + * This IOTCL used to get the value of the current Rx STBC + * + * @E.g: iwpriv wlan0 get_rx_stbc + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_RX_STBC 14 +/* + * + * get_shortgi - Get the value of the current short GI setting + * + * @INPUT: None + * + * @OUTPUT: Enable/disable of shortgi + * wlan0 get_shortgi:1 + * + * This IOCTL gets the value of the current short GI setting + * + * @E.g: iwpriv wlan0 get_shortgi + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_SHORT_GI 15 +/* + * + * get_rtscts - Get the value of the current RTS/CTS setting. + * + * @INPUT: None + * + * @OUTPUT: Enable/disable of RTS/CTS + * wlan0 get_rtscts:33 + * + * This IOTCL get the value of the current RTS/CTS setting. + * + * @E.g: iwpriv wlan0 get_rtscts + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_RTSCTS 16 +/* + * + * get_chwidth - Get the current channel width setting + * + * @INPUT: None + * + * @OUTPUT: channel width + * wlan0 get_chwidth:0 + * + * This IOTCL get the current channel width setting. + * + * @E.g: iwpriv wlan0 get_chwidth + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_CHWIDTH 17 +/* + * + * get_anienable - Get the anienable + * + * @INPUT: None + * + * @OUTPUT: + * wlan0 get_anienable:0 + * + * This IOTCL get the anienable + * + * @E.g: iwpriv wlan0 get_anienable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_ANI_EN_DIS 18 +/* + * + * get_aniplen - Get the aniplen + * + * @INPUT: None + * + * @OUTPUT: + * wlan0 get_aniplen:0 + * + * This IOTCL get the aniplen + * + * @E.g: iwpriv wlan0 get_aniplen + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_ANI_POLL_PERIOD 19 +/* + * + * get_anilislen- Get the anilislen + * + * @INPUT: None + * + * @OUTPUT: + * wlan0 get_anilislen:0 + * + * This IOTCL used to get anilislen + * + * @E.g: iwpriv wlan0 get_anilislen + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_ANI_LISTEN_PERIOD 20 +/* + * + * get_aniofdmlvl - Get the OFDM level + * + * @INPUT: None + * + * @OUTPUT: OFDM + * wlan0 get_aniofdmlvl:0 + * + * This IOTCL used to get ofdm level + * + * @E.g: iwpriv wlan0 get_aniofdmlvl + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_ANI_OFDM_LEVEL 21 +/* + * + * get_aniccklvl - Get the cck level + * + * @INPUT: None + * + * @OUTPUT: + * wlan0 get_aniccklvl:0 + * + * This IOTCL used to get cck level + * + * @E.g: iwpriv wlan0 get_aniccklvl + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_ANI_CCK_LEVEL 22 +/* + * + * get_cwmenable - Get the value of the dynamic channel bandwidth setting + * + * @INPUT: None + * + * @OUTPUT: Enable/disable dynamic channel bandwidth + * wlan0 get_cwmenable:0 + * + * This IOTCL get the value of the dynamic channel bandwidth setting + * + * @E.g: iwpriv wlan0 get_cwmenable + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_DYNAMIC_BW 23 +/* + * + * get_txchainmask - Get the txchainmask that was set + * + * @INPUT: None + * + * @OUTPUT: txchainmask + * wlan0 get_txchainmask:1 + * + * This IOCTL gets the txchainmask that was set + * This command is useful if it was previously set + * + * @E.g: iwpriv wlan0 get_txchainmask + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_TX_CHAINMASK 24 +/* + * + * get_rxchainmask - Get the rxchainmask that was set + * + * @INPUT: None + * + * @OUTPUT: rxchainmask + * wlan0 get_rxchainmask:1 + * + * This IOCTL gets the rxchainmask that was set + * This command is useful only if it was previously set. + * + * @E.g: iwpriv wlan0 get_rxchainmask + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_RX_CHAINMASK 25 +/* + * + * get_11nrate - Get the fixed Tx data rate + * + * @INPUT: None + * + * @OUTPUT: Using this command does not return the same value as set + * wlan0 get_11nrate:0 + * + * This IOCTL gets the fixed Tx data rate + * This command is useful only if setting the fixed Tx rate. + * + * @E.g: iwpriv wlan0 get_11nrate + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_11N_RATE 26 +/* + * + * get_ampdu - Get the maximum subframe of ampdu + * + * @INPUT: None + * + * @OUTPUT: Maximum subframe of ampdu + * wlan0 get_ampdu:1 + * + * This IOCTL gets the maximum subframe of ampdu + * This command is useful only if setting ampdu. + * + * @E.g: iwpriv wlan0 get_ampdu + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_AMPDU 27 +/* + * + * get_amsdu - Get the maximum subframe of amsdu + * + * @INPUT: None + * + * @OUTPUT: Maximum subframe of amsdu + * wlan0 get_amsdu:1 + * + * This IOCTL gets the maximum subframe of amsdu. + * This command is useful only if setting amsdu + * + * @E.g: iwpriv wlan0 get_amsdu + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_AMSDU 28 +/* + * + * get_txpow2g - Get the current 2 GHz Tx power setting + * + * @INPUT: None + * + * @OUTPUT: Tx Power in dbm + * wlan0 get_txpow2g:0 + * + * This IOCTL gets the current 2 GHz Tx power setting + * This command is useful if setting Tx power + * + * @E.g: iwpriv wlan0 get_txpow2g + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_TXPOW_2G 29 +/* + * + * get_txpow5g - Get the current 5 GHz Tx power setting + * + * @INPUT: None + * + * @OUTPUT: Tx Power in dbm + * wlan0 get_txpow5g:0 + * + * This IOCTL gets the current 5 GHz Tx power setting + * This command is useful if setting Tx power + * + * @E.g: iwpriv wlan0 get_txpow5g + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_TXPOW_5G 30 +/* 31 is unused */ +#define WE_GET_PPS_PAID_MATCH 32 +#define WE_GET_PPS_GID_MATCH 33 +#define WE_GET_PPS_EARLY_TIM_CLEAR 34 +#define WE_GET_PPS_EARLY_DTIM_CLEAR 35 +#define WE_GET_PPS_EOF_PAD_DELIM 36 +#define WE_GET_PPS_MACADDR_MISMATCH 37 +#define WE_GET_PPS_DELIM_CRC_FAIL 38 +#define WE_GET_PPS_GID_NSTS_ZERO 39 +#define WE_GET_PPS_RSSI_CHECK 40 +/* Private ioctl for QPower */ +#define WE_GET_QPOWER_MAX_PSPOLL_COUNT 41 +#define WE_GET_QPOWER_MAX_TX_BEFORE_WAKE 42 +#define WE_GET_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL 43 +#define WE_GET_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL 44 +/* GTX Commands */ +/* + * + * get_gtxHTMcs - Get the tx HTM + * + * @INPUT: None + * + * @OUTPUT: HTM + * wlan0 get_gtxHTMcs:32896 + * + * This IOTCL used to get HTM + * + * @E.g: iwpriv wlan0 get_gtxHTMcs + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_GTX_HT_MCS 47 +/* + * + * get_gtxVHTMcs - Get the VHTM + * + * @INPUT: None + * + * @OUTPUT: VHTM + * wlan0 get_gtxVHTMcs:524800 + * + * This IOTCL used to get the VHTM + * + * @E.g: iwpriv wlan0 get_gtxVHTMcs + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_GTX_VHT_MCS 48 +/* + * + * get_gtxUsrCfg - Get the tx cfg + * + * @INPUT: None + * + * @OUTPUT: TXCFG + * wlan0 get_gtxUsrCfg:32 + * + * This IOTCL used to get the tx cfg + * + * @E.g: iwpriv wlan0 get_gtxUsrCfg + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_GTX_USRCFG 49 +/* + * + * get_gtxThre - Get the tx threshold + * + * @INPUT: None + * + * @OUTPUT: Threshold + * wlan0 get_gtxThre:3 + * + * This IOCTL is used to get tx threshold + * + * @E.g: iwpriv wlan0 get_gtxThre + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_GTX_THRE 50 +/* + * + * get_gtxMargin - Get the tx margin + * + * @INPUT: None + * + * @OUTPUT: GTXMARGIN + * wlan0 get_gtxMargin:2 + * + * This IOCTL is used to set tx margin + * + * @E.g: iwpriv wlan0 get_gtxMargin + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_GTX_MARGIN 51 +/* + * + * get_gtxStep - Get the tx step + * + * @INPUT: None + * + * @OUTPUT: GTXSTEP + * wlan0 get_gtxStep:0 + * + * This IOCTL is used to get the gtx step + * + * @E.g: iwpriv wlan0 get_gtxStep + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_GTX_STEP 52 +/* + * + * get_gtxMinTpc - Get the tx miminum tpc + * + * @INPUT: None + * + * @OUTPUT: TPC + * wlan0 get_gtxMinTpc:0 + * + * This IOCTL is used to get tx miminum tpc + * + * @E.g: iwpriv wlan0 get_gtxMinTpc + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_GTX_MINTPC 53 +/* + * + * get_gtxBWMask - Get the tx BW MASK + * + * @INPUT: None + * + * @OUTPUT: MASK + * wlan0 get_gtxBWMask:15 + * + * This IOCTL is used get gtx bw mask + * + * @E.g: iwpriv wlan0 get_gtxBWMask + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_GTX_BWMASK 54 +#define WE_GET_TEMPERATURE 56 +#define WE_CAP_TSF 58 +#define WE_GET_ROAM_SYNCH_DELAY 59 + +/* + * + * get_dcm - Get dcm enablement value + * + * @INPUT: None + * + * @OUTPUT: 0/1 + * wlan0 get_dcm + * + * This IOCTL is used get dcm value + * + * Supported Feature: STA/SAP + * + * Usage: Internal + * + * + */ +#define WE_GET_DCM 60 + +/* + * + * get_dcm - Get range extension enablement value + * + * @INPUT: None + * + * @OUTPUT: 0/1 + * wlan0 get_range_ext + * + * This IOCTL is used get range_extension value + * + * Supported Feature: STA/SAP + * + * Usage: Internal + * + * + */ +#define WE_GET_RANGE_EXT 61 + +/* Private ioctls and their sub-ioctls */ +#define WLAN_PRIV_SET_INT_GET_INT (SIOCIWFIRSTPRIV + 2) + +/* Private ioctls and their sub-ioctls */ +#define WLAN_PRIV_SET_CHAR_GET_NONE (SIOCIWFIRSTPRIV + 3) +#define WE_WOWL_ADD_PTRN 1 +#define WE_WOWL_DEL_PTRN 2 +/* + * + * neighbor - Send neighbor report request + * + * @INPUT: string + * + * @OUTPUT: None + * + * This IOCTL create a Neighbor report request and send it to peer + * + * @E.g: iwpriv wlan0 neighbor "SSID" + * + * Supported Feature: 11k + * + * Usage: Internal/External + * + * + */ +#define WE_NEIGHBOR_REPORT_REQUEST 3 +/* + * + * set_ap_wps_ie - Set the P2P IE of the probe response + * + * @INPUT: string + * + * @OUTPUT: None + * + * This IOCTL sets the P2P IE of the probe response + * + * @E.g: iwpriv wlan0 set_ap_wps_ie abcd + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_SET_AP_WPS_IE 4 + +/* 5 is unused */ + +/* + * + * unit_test - execute component-level unit tests + * + * @INPUT: string - the name of the component to test. + * All tests are executed if unspecified + * @OUTPUT: None + * + * Usage: Internal only + * + */ +#define WE_UNIT_TEST 6 + +/* Private ioctls and their sub-ioctls */ +#define WLAN_PRIV_SET_THREE_INT_GET_NONE (SIOCIWFIRSTPRIV + 4) +#define WE_SET_WLAN_DBG 1 +#define WE_SET_DP_TRACE 2 +#define WE_SET_FW_TEST 4 + +/* Private ioctls and their sub-ioctls */ +#define WLAN_PRIV_GET_CHAR_SET_NONE (SIOCIWFIRSTPRIV + 5) +#define WE_WLAN_VERSION 1 +#define WE_GET_STATS 2 +/* + * + * getConfig - gets the values of all configurations listed in WCNSS + * + * @INPUT: None + * + * @OUTPUT: Current configuration to the sys log + * wlan0 getConfig: WLAN configuration written to system log + * + * This IOCTL gets the values of all configurations listed in WCNSS + * + * @E.g: iwpriv wlan0 getConfig + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_CFG 3 +#define WE_GET_WMM_STATUS 4 +/* + * + * getChannelList - Get the available channel list while in QCMobileAP + * + * @INPUT: None + * + * @OUTPUT: Channel list + * wlan0 getChannelList:36 US 1..165 + * + * This IOCTL gets the available channel list while in QCMobileAP + * + * @E.g: iwpriv wlan0 getChannelList + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_CHANNEL_LIST 5 +/* + * + * getRSSI - Get the Received Signal Strength Indicator + * + * @INPUT: None + * + * @OUTPUT: RSSI + * wlan0 getRSSI:rsssi=-32 + * + * This IOCTL gets the Received Signal Strength Indicator (RSSI) + * + * @E.g: iwpriv wlan0 getRSSI + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_RSSI 6 + +/* + * + * getSuspendStats - Get suspend/resume stats + * + * @INPUT: None + * + * @OUTPUT: character string containing formatted suspend/resume stats + * + * This ioctl is used to get suspend/resume stats formatted for display. + * Currently it includes suspend/resume counts, wow wake up reasons, and + * suspend fail reasons. + * + * @E.g: iwpriv wlan0 getSuspendStats + * iwpriv wlan0 getSuspendStats + * + * Supported Feature: suspend/resume + * + * Usage: Internal + * + * + */ +#define WE_GET_SUSPEND_RESUME_STATS 7 +#ifdef FEATURE_WLAN_TDLS +/* + * + * getTdlsPeers - Get all TDLS peers. + * + * @INPUT: None + * + * @OUTPUT: Returns the MAC address of all the TDLS peers + * wlan0 getTdlsPeers: + * MAC Id cap up RSSI + * --------------------------------- + * 00:0a:f5:0e:bd:18 2 Y Y -44 + * 00:0a:f5:bf:0e:12 0 N N 0 + * + * This IOCTL is used to get all TDLS peers. + * + * @E.g: iwpriv wlan0 getTdlsPeers + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define WE_GET_TDLS_PEERS 8 +#endif +#ifdef WLAN_FEATURE_11W +/* + * + * getPMFInfo - get the PMF info of the connected session + * + * @INPUT: None + * + * @OUTPUT: + * wlan0 getPMFInfo: + * BSSID E4:F4:C6:0A:E0:36, Is PMF Assoc? 0 + * Number of Unprotected Disassocs 0 + * Number of Unprotected Deauths 0 + * + * This IOCTL is used to get the PMF stats/status of the current + * connection. + * + * @e.g:iwpriv wlan0 getPMFInfo + * + * Supported Feature: PMF + * + * Usage: Internal/External + * + * + */ +#define WE_GET_11W_INFO 9 +#endif +#define WE_GET_STATES 10 +/* + * + * getIbssSTAs - get ibss sta info + * + * @INPUT: None + * + * @OUTPUT: Give the MAC of the IBSS STA + * wlan0 getIbssSTAs: + * 1 .8c:fd:f0:01:9c:bf + * + * This IOCTL is used to get ibss sta info + * + * @E.g: iwpriv wlan0 getIbssSTAs + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define WE_GET_IBSS_STA_INFO 11 +/* + * + * getphymode - Get the current phymode. + * + * @INPUT: None + * + * @OUTPUT: In phymode + * wlan0 getphymode:AUTO MODE + * + * This IOCTL used to gets the current phymode. + * + * @E.g: iwpriv wlan0 getphymode + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WE_GET_PHYMODE 12 + +/* + * + * getOemDataCap - Get the oem data caps. + * + * @INPUT: None + * + * @OUTPUT: oem data capability + * + * This IOCTL used to gets the current oem data cap. + * + * @E.g: iwpriv wlan0 getOemDataCap + * + * Usage: Internal/External + * + * + */ +#define WE_GET_OEM_DATA_CAP 13 + +/* + * + * getSNR - Enable SNR Monitoring + * + * @INPUT: None + * + * @OUTPUT: Signal strength/ratio + * wlan0 getSNR:1 + * + * This IOCTL is used to get ibss sta info + * + * @E.g: iwpriv wlan0 getSNR + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ + +#define WE_GET_SNR 14 +#define WE_LIST_FW_PROFILE 15 + +/* + * + * + * get_ba_timeout - to get timeout for each AC + * + * @INPUT: None + * + * @OUTPUT: displays timeout value for each access class + * + * @E.g.: iwpriv wlan0 get_ba_timeout + * + * Usage: Internal + * + * + */ +#define WE_GET_BA_AGEING_TIMEOUT 16 + +/* + * + * + * sta_cxn_info - STA connection information + * + * @INPUT: none + * + * @OUTPUT: STA's connection information + * + * This IOCTL is used to get connection's information. + * + * @E.g: iwpriv wlan0 get_cxn_info + * + * Usage: Internal + * + * + */ +#define WE_GET_STA_CXN_INFO 17 + +/* Private ioctls and their sub-ioctls */ +#define WLAN_PRIV_SET_NONE_GET_NONE (SIOCIWFIRSTPRIV + 6) + +/* + * + * reassoc - Trigger STA re-association to the connected AP + * + * @INPUT: None + * + * @OUTPUT: None + * + * This IOCTL is used to trigger STA reassociation to the connected AP. + * + * @E.g: iwpriv wlan0 reassoc + * + * Supported Feature: Roaming + * + * Usage: Internal + * + * + */ +#define WE_SET_REASSOC_TRIGGER 8 +/* + * + * ibssPeerInfoAll - Print the ibss peers's MAC, rate and RSSI + * + * @INPUT: None + * + * @OUTPUT: print ibss peer in info logs + * peer_info->numIBSSPeers = 1 + * PEER ADDR : 8c:fd:f0:01:9c:bf TxRate: 1 Mbps RSSI: -35 + * + * This IOCTL is used to rint the ibss peers's MAC, rate and RSSI + * in info logs + * + * @E.g: iwpriv wlan0 ibssPeerInfoAll + * + * Supported Feature: IBSS + * + * Usage: Internal/External + * + * + */ +#define WE_IBSS_GET_PEER_INFO_ALL 10 +/* Sub ioctls 11 to 16 are not used */ +#define WE_GET_FW_PROFILE_DATA 18 +/* + * + * stop_obss_scan - Stop obss scan + * + * @INPUT: None + * + * @OUTPUT: None + * + * This IOCTL is used to stop obss scan + * + * @E.g: iwpriv wlan0 stop_obss_scan + * + * Supported Feature: Scan + * + * Usage: Internal/External + * + * + */ +#define WE_STOP_OBSS_SCAN 19 + +/* Private ioctls and their sub-ioctls */ +#define WLAN_PRIV_SET_VAR_INT_GET_NONE (SIOCIWFIRSTPRIV + 7) + +#define WE_P2P_NOA_CMD 2 +/* subcommands 3 is unused */ + +#define WE_MAC_PWR_DEBUG_CMD 4 + +/* subcommand 5 is unused */ +/* subcommand 6 is unused */ + +#define WE_UNIT_TEST_CMD 7 + +#define WE_MTRACE_DUMP_CMD 8 +#define WE_MTRACE_SELECTIVE_MODULE_LOG_ENABLE_CMD 9 + + +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING +#define WE_LED_FLASHING_PARAM 10 +#endif + +/* + * + * pm_clist - Increments the index value of the concurrent connection list + * and update with the input parameters provided. + * + * @INPUT: Following 8 arguments: + * @vdev_id: vdev id + * @tx_streams: TX streams + * @rx_streams: RX streams + * @chain_mask: Chain mask + * @type: vdev_type + * AP:1 STA:2 IBSS:3 Monitor:4 NAN:5 OCB:6 NDI:7 + * @sub_type: vdev_subtype + * P2P_Device:1 P2P_Client:2 P2P_GO:3 + * Proxy_STA:4 Mesh:5 Mesh_11s:6 + * @channel: Channel + * @mac: Mac id + * + * @OUTPUT: None + * + * This IOCTL is used to increments the index value of the concurrent connection + * list and update with the input parameters provided. + * + * @E.g: iwpriv wlan0 pm_clist vdev_id tx_streams rx_streams chain_mask type + * sub_type channel mac + * iwpriv wlan0 pm_clist 1 2 2 1 2 3 10 1 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_MANAGER_CLIST_CMD 11 + +/* + * + * pm_dlist - Delete the index from the concurrent connection list that is + * present in the given vdev_id. + * + * @INPUT: delete_all, vdev_id + * @delete_all: delete all indices + * @vdev_id: vdev id + * + * @OUTPUT: None + * + * This IOCTL is used to delete the index from the concurrent connection list + * that is present in the given vdev_id. + * + * @E.g: iwpriv wlan0 pm_dlist delete_all vdev_id + * iwpriv wlan0 pm_dlist 0 1 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_MANAGER_DLIST_CMD 12 + +/* + * + * pm_dbs - Set dbs capability and system preference + * + * @INPUT: dbs, system_pref + * @dbs: Value of DBS capability to be set + * @system_pref: System preference + * 0:PM_THROUGHPUT 1: PM_POWERSAVE 2: PM_LATENCY + * + * @OUTPUT: None + * + * This IOCTL is used to set dbs capability and system preference. + * + * @E.g: iwpriv wlan0 pm_dbs dbs system_pref + * iwpriv wlan0 pm_dbs 1 0 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_MANAGER_DBS_CMD 13 + +/* + * + * pm_pcl - Set pcl for concurrency mode. + * + * @INPUT: policy_mgr_con_mode + * @policy_mgr_con_mode: concurrency mode for PCL table + * 0:STA 1:SAP 2:P2P_Client 3:P2P_GO 4:IBSS + * + * @OUTPUT: None + * + * This IOCTL is used to set pcl for concurrency mode. + * + * @E.g: iwpriv wlan0 pm_pcl policy_mgr_con_mode + * iwpriv wlan0 pm_pcl 0 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_MANAGER_PCL_CMD 14 + +/* + * + * pm_cinfo - Shows the concurrent connection list. + * + * @INPUT: None + * + * @OUTPUT: None + * + * This IOCTL is used to show the concurrent connection list. + * + * @E.g: iwpriv wlan0 pm_cinfo + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_MANAGER_CINFO_CMD 15 + +/* + * + * pm_ulist - Updates the index value of the concurrent connection list + * with the input parameters provided. + * + * @INPUT: Following 8 arguments: + * @vdev_id: vdev id + * @tx_streams: TX streams + * @rx_streams: RX streams + * @chain_mask: Chain mask + * @type: vdev_type + * AP:1 STA:2 IBSS:3 Monitor:4 NAN:5 OCB:6 NDI:7 + * @sub_type: vdev_subtype + * P2P_Device:1 P2P_Client:2 P2P_GO:3 + * Proxy_STA:4 Mesh:5 Mesh_11s:6 + * @channel: Channel + * @mac: Mac id + * + * @OUTPUT: None + * + * This IOCTL is used to updates the index value of the concurrent + * connection list with the input parameters provided. + * + * @E.g: iwpriv wlan0 pm_ulist vdev_id tx_streams rx_streams chain_mask type + * sub_type channel mac + * iwpriv wlan0 pm_ulist 1 2 2 1 2 3 10 1 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_MANAGER_ULIST_CMD 16 + +/* + * + * pm_query_action - Initiate actions needed on current connections as + * per the channel provided. + * + * @INPUT: channel + * @channel: Channel on which new connection will be. + * + * @OUTPUT: None + * + * This IOCTL is used to initiate actions needed on current connections + * as per the channel provided. + * + * @E.g: iwpriv wlan0 pm_query_action channel + * iwpriv wlan0 pm_query_action 6 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_MANAGER_QUERY_ACTION_CMD 17 + +/* + * + * pm_query_allow - Checks for allowed concurrency combination + * + * @INPUT: mode, channel, bandwidth + * @mode: new connection mode + * 0:STA 1:SAP 2:P2P_Client 3:P2P_GO 4:IBSS + * @channel: channel on which new connection is coming up + * @bandwidth: Bandwidth requested by the connection + * 0:None 1:5MHz 2:10MHz 3:20MHz + * 4:40MHz 5:80MHz 6:80+80MHz 7:160MHz + * + * @OUTPUT: None + * + * This IOCTL is used to checks for allowed concurrency combination. + * + * @E.g: iwpriv wlan0 pm_query_allow mode channel bandwidth + * iwpriv wlan0 pm_query_allow 0 6 4 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_MANAGER_QUERY_ALLOW_CMD 18 + +/* + * + * pm_run_scenario - Create scenario with number of connections provided. + * + * @INPUT: num_of_conn + * @num_of_conn: the number of connections (values: 1~3) + * + * @OUTPUT: None + * + * This IOCTL is used to create scenario with the number of connections + * provided. + * + * @E.g: iwpriv wlan0 pm_run_scenario num_of_conn + * iwpriv wlan0 pm_run_scenario 1 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_MANAGER_SCENARIO_CMD 19 + +/* + * + * pm_set_hw_mode - Set hardware for single/dual mac. + * + * @INPUT: hw_mode + * 0:single mac 1:dual mac + * 2: 2x2 5g + 1x1 2g dbs mode + * 3: 2x2 2g + 1x1 5g dbs mode + * + * @OUTPUT: None + * + * This IOCTL is used to set hardware for single/dual mac. + * + * @E.g: iwpriv wlan0 pm_set_hw_mode hw_mode + * iwpriv wlan0 pm_set_hw_mode 1 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_POLICY_SET_HW_MODE_CMD 20 + +/* + * + * ch_avoid - unit test SAP channel avoidance + * + * @INPUT: chan avoid ranges + * + * @OUTPUT: none + * + * This IOCTL is used to fake a channel avoidance event. + * To test SAP/GO chan switch during chan avoid event process. + * + * @E.g: iwpriv wlan0 ch_avoid 2452 2462 + * + * Supported Feature: SAP chan avoidance. + * + * Usage: Internal + * + * + */ +#define WE_SET_CHAN_AVOID 21 + +/* + * + * set_scan_cfg - Set dual MAC scan config parameters. + * + * @INPUT: dbs, dbs_plus_agile_scan, single_mac_scan_with_dbs + * @dbs: Value of DBS bit + * @dbs_plus_agile_scan: Value of DBS plus agile scan bit + * @single_mac_scan_with_dbs: Value of Single MAC scan with DBS + * + * @OUTPUT: None + * + * This IOCTL is used to set the dual MAC scan config. + * + * @E.g: iwpriv wlan0 set_scan_cfg dbs dbs_plus_agile_scan + * single_mac_scan_with_dbs + * iwpriv wlan0 set_scan_cfg 1 0 1 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_SET_DUAL_MAC_SCAN_CONFIG 21 + +/* + * + * set_fw_mode_cfg - Sets the dual mac FW mode config + * + * @INPUT: dbs, dfs + * @dbs: DBS bit + * @dfs: Agile DFS bit + * + * @OUTPUT: None + * + * This IOCTL is used to set the dual mac FW mode config. + * + * @E.g: iwpriv wlan0 set_fw_mode_cfg dbs dfs + * iwpriv wlan0 set_fw_mode_cfg 1 1 + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define WE_SET_DUAL_MAC_FW_MODE_CONFIG 22 +#define WE_SET_MON_MODE_CHAN 23 +/* + * + * txrx_stats - TXRX statistics query + * + * @INPUT: query category, mac id (default mac id is 0) + * + * @OUTPUT: TXRX statistics result + * + * This IOCTL is used to get TXRX statistics counters. + * + * @E.g: iwpriv wlan0 txrx_stats 21 0 + * iwpriv wlan0 txrx_stats 21 1 + * + * Usage: Internal + * + * + */ +#define WE_SET_TXRX_STATS 24 + + +#ifdef FEATURE_WLAN_TDLS +#undef MAX_VAR_ARGS +#define MAX_VAR_ARGS 11 +#else +#undef MAX_VAR_ARGS +#define MAX_VAR_ARGS 9 +#endif + +#ifdef WLAN_FEATURE_MOTION_DETECTION +#undef MAX_VAR_ARGS +#define MAX_VAR_ARGS 15 +#endif /* WLAN_FEATURE_MOTION_DETECTION */ +#define WE_MOTION_DET_CONFIG_PARAM 25 +#define WE_MOTION_DET_BASE_LINE_CONFIG_PARAM 26 + +#define WE_SET_THERMAL_THROTTLE_CFG 27 +/* + * + * fips_test - Perform a FIPS test + * + * @INPUT: Binary representation of the following packed structure + * + * @OUTPUT: Binary representation of the following packed structure + * + * This IOCTL is used to perform FIPS certification testing + * + * @E.g: iwpriv wlan0 fips_test + * + * iwpriv wlan0 fips_test + * + * Supported Feature: FIPS + * + * Usage: Internal + * + * + */ +#define WLAN_PRIV_FIPS_TEST (SIOCIWFIRSTPRIV + 8) + +/* Private ioctls (with no sub-ioctls) */ +/* note that they must be odd so that they have "get" semantics */ +/* + * + * addTspec - Add TSPEC for each AC + * + * @INPUT: 19 TSPEC params + * @[arg0]: handle + * @[arg1]: tid + * @[arg2]: dir + * @[arg3]: psb + * @[arg4]: up + * @[arg5]: nomMsduSize + * @[arg6]: maxMsduSize + * @[arg7]: minDataRate + * @[arg8]: meanDataRate + * @[arg9]: peakDataRate + * @[arg10]: maxBurstSize + * @[arg11]: minPhyRate + * @[arg12]: sba + * @[arg13]: minServiceIntv + * @[arg14]: suspendIntv + * @[arg15]: burstSizeDefn + * @[arg16]: ackPolicy + * @[arg17]: inactivityPeriod + * @[arg18]: maxServiceIntv + * + * @OUTPUT: Success/Failure + * + * This IOCTL is used to add TSPEC for each AC. + * + * @E.g: iwpriv wlan0 addTspec + * + * + * + * + * + * iwpriv wlan0 addTspec 7001 6 2 1 6 0x80D0 0x80D0 0x14500 0x14500 0x14500 + * 0 0x5B8D80 0x2001 20 2000 0 0 0 2000 + * wlan0 addTspec:3 + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define WLAN_PRIV_ADD_TSPEC (SIOCIWFIRSTPRIV + 9) +/* + * + * delTspec - Delete TSPEC entry for each AC + * + * @INPUT: 1 TSPEC param + * @[arg0]: handle + * + * @OUTPUT: Success/Failure + * + * This IOCTL is used to delete TSPEC entry for each AC. + * + * @E.g: iwpriv wlan0 delTspec + * iwpriv wlan0 delTspec 7001 + * wlan0 delTspec:16 + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define WLAN_PRIV_DEL_TSPEC (SIOCIWFIRSTPRIV + 11) +/* + * + * getTspec - Get TSPEC entry for each AC + * + * @INPUT: 1 TSPEC param + * @[arg0]: handle + * + * @OUTPUT: Success/Failure + * + * This IOCTL is used to get TSPEC entry for each AC. + * + * @E.g: iwpriv wlan0 getTspec + * iwpriv wlan0 getTspec 7001 + * wlan0 delTspec:18 + * + * Supported Feature: WMM + * + * Usage: Internal/External + * + * + */ +#define WLAN_PRIV_GET_TSPEC (SIOCIWFIRSTPRIV + 13) + +/* (SIOCIWFIRSTPRIV + 10) is currently unused */ +/* (SIOCIWFIRSTPRIV + 12) is currently unused */ +/* (SIOCIWFIRSTPRIV + 14) is currently unused */ +#define WLAN_PRIV_SET_NONE_GET_THREE_INT (SIOCIWFIRSTPRIV + 15) +#define WE_GET_TSF 1 +/* (SIOCIWFIRSTPRIV + 16) is currently unused */ + +#ifdef FEATURE_WLM_STATS +/* + * + * + * get_wlm_stats - Get stats from FW for game latency + * + * @INPUT: BITMASK inform of decimal number + * + * @OUTPUT: HEX string given by FW + * + * This IOCTL is used to get game latency related STATS from FW + * + * @E.g.: iwpriv wlan0 get_wlm_stats 1 + * + * Usage: internal + * + * + */ +#define WLAN_GET_WLM_STATS (SIOCIWFIRSTPRIV + 17) +#endif + +/* (SIOCIWFIRSTPRIV + 19) is currently unused */ + +#define WLAN_PRIV_SET_FTIES (SIOCIWFIRSTPRIV + 20) + +/* Private ioctl for setting the host offload feature */ +#define WLAN_PRIV_SET_HOST_OFFLOAD (SIOCIWFIRSTPRIV + 18) + +/* Private ioctl to get the statistics */ +#define WLAN_GET_WLAN_STATISTICS (SIOCIWFIRSTPRIV + 21) + +/* Private ioctl to set the Keep Alive Params */ +/* + * + * setKeepAlive - Set the keep alive feature + * + * @INPUT: 28 bytes of information in the order of packet type, time period + * host IPv4 address, destination IPv4 address, destination MAC address, bssID + * + * @OUTPUT: None + * + * This IOCTL sets the keep alive feature to send either NULL + * or unsolicited ARP response packets + * + * @E.g: iwpriv wlan0 setKeepAlive + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WLAN_SET_KEEPALIVE_PARAMS (SIOCIWFIRSTPRIV + 22) + +#ifdef WLAN_FEATURE_PACKET_FILTERING +/* Private ioctl to set the packet filtering params */ +#define WLAN_SET_PACKET_FILTER_PARAMS (SIOCIWFIRSTPRIV + 23) +#endif + + +#ifdef FEATURE_WLAN_SCAN_PNO +/* Private ioctl to get the statistics */ +#define WLAN_SET_PNO (SIOCIWFIRSTPRIV + 24) +#endif +/* + * + * SETBAND - Set the operational band + * + * @INPUT: 0 to Auto, 1 to 5 GHz and 2 to 2.4 GHz + * + * @OUTPUT: None + * + * This IOCTL Set the operational band If the new band is different from the + * current operational band, it aborts the pending scan requests, flushes + * the existing scan results, and then change * the band capability + * + * @E.g: iwpriv wlan0 SETBAND + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WLAN_SET_BAND_CONFIG (SIOCIWFIRSTPRIV + 25) + +#define WLAN_PRIV_SET_MCBC_FILTER (SIOCIWFIRSTPRIV + 26) +/* (SIOCIWFIRSTPRIV + 27) is currently unused */ + +/* Private ioctls and their sub-ioctls */ +#define WLAN_PRIV_SET_TWO_INT_GET_NONE (SIOCIWFIRSTPRIV + 28) +#define WE_SET_SMPS_PARAM 1 +#define WE_SET_FW_CRASH_INJECT 2 +#define WE_DUMP_DP_TRACE_LEVEL 3 +/* Private sub ioctl for enabling and setting histogram interval of profiling */ +#define WE_ENABLE_FW_PROFILE 4 +#define WE_SET_FW_PROFILE_HIST_INTVL 5 + +/* Private sub-ioctl for initiating WoW suspend without Apps suspend */ +#define WE_SET_WLAN_SUSPEND 6 +#define WE_SET_WLAN_RESUME 7 + +/* + * + * log_buffer - prints host/target related communication logs via dmesg + * + * @INPUT: Log Id, Count + * + * Log Id: + * 0) HTC_CREDIT_HISTORY_LOG + * 1) COMMAND_LOG, + * 2) COMMAND_TX_CMP_LOG, + * 3) MGMT_COMMAND_LOG, + * 4) MGMT_COMMAND_TX_CMP_LOG, + * 5) EVENT_LOG, + * 6) RX_EVENT_LOG, + * 7) MGMT_EVENT_LOG + * + * @OUTPUT: None + * + * @E.g: + * # print up to 10 of the most recent records from HTC Credit History + * iwpriv wlan0 log_buffer 0 10 + * # print up to 3 of the most recent records from Event Log + * iwpriv wlan0 log_buffer 5 3 + * + * Supported Feature: WLAN Trace + * + * Usage: Internal/External + * + * + */ +#define WE_LOG_BUFFER 8 + +/* + * + * set_ba_timeout - sets Block ACK aging timeout value for each Access class + * + * @INPUT: Access Class [0:BK, 1:BE, 2:VI, 3:VO], Timeout value + * + * @OUTPUT: None + * + * @E.g.: + * # to set duration of 2 seconds for BE + * iwpriv wlan0 set_ba_timeout 1 2 + * # to set duration of 3 seconds for VO + * iwpriv wlan0 set_ba_timeout 3 3 + * + * Usage: Internal + * + * + */ +#define WE_SET_BA_AGEING_TIMEOUT 9 + +enum host_target_comm_log { + HTC_CREDIT_HISTORY_LOG = 0, + COMMAND_LOG, + COMMAND_TX_CMP_LOG, + MGMT_COMMAND_LOG, + MGMT_COMMAND_TX_CMP_LOG, + EVENT_LOG, + RX_EVENT_LOG, + MGMT_EVENT_LOG +}; + +/* (SIOCIWFIRSTPRIV + 29) is currently unused */ + +/* 802.11p IOCTL */ +#define WLAN_SET_DOT11P_CHANNEL_SCHED (SIOCIWFIRSTPRIV + 30) + +/* + * + * getLinkSpeed - Gets the current link speed in Mbps + * + * @INPUT: None + * + * @OUTPUT: linkspeed in mbps + * wlan0 getLinkSpeed:7 + * + * This IOCTL is used get the current link speed in Mbps + * + * @E.g: iwpriv wlan0 getLinkSpeed + * + * Supported Feature: STA + * + * Usage: Internal/External + * + * + */ +#define WLAN_GET_LINK_SPEED (SIOCIWFIRSTPRIV + 31) + +#define WLAN_STATS_INVALID 0 +#define WLAN_STATS_RETRY_CNT 1 +#define WLAN_STATS_MUL_RETRY_CNT 2 +#define WLAN_STATS_TX_FRM_CNT 3 +#define WLAN_STATS_RX_FRM_CNT 4 +#define WLAN_STATS_FRM_DUP_CNT 5 +#define WLAN_STATS_FAIL_CNT 6 +#define WLAN_STATS_RTS_FAIL_CNT 7 +#define WLAN_STATS_ACK_FAIL_CNT 8 +#define WLAN_STATS_RTS_SUC_CNT 9 +#define WLAN_STATS_RX_DISCARD_CNT 10 +#define WLAN_STATS_RX_ERROR_CNT 11 +#define WLAN_STATS_TX_BYTE_CNT 12 + +#define WLAN_STATS_RX_BYTE_CNT 13 +#define WLAN_STATS_RX_RATE 14 +#define WLAN_STATS_TX_RATE 15 + +#define WLAN_STATS_RX_UC_BYTE_CNT 16 +#define WLAN_STATS_RX_MC_BYTE_CNT 17 +#define WLAN_STATS_RX_BC_BYTE_CNT 18 +#define WLAN_STATS_TX_UC_BYTE_CNT 19 +#define WLAN_STATS_TX_MC_BYTE_CNT 20 +#define WLAN_STATS_TX_BC_BYTE_CNT 21 + +#define FILL_TLV(__p, __type, __size, __val, __tlen) do { \ + if ((__tlen + __size + 2) < WE_MAX_STR_LEN) { \ + *__p++ = __type; \ + *__p++ = __size; \ + memcpy(__p, __val, __size); \ + __p += __size; \ + __tlen += __size + 2; \ + } else { \ + hdd_err("FILL_TLV Failed!!!"); \ + } \ + } while (0) + +#define TX_PER_TRACKING_DEFAULT_RATIO 5 +#define TX_PER_TRACKING_MAX_RATIO 10 +#define TX_PER_TRACKING_DEFAULT_WATERMARK 5 + +#define WLAN_ADAPTER 0 +#define P2P_ADAPTER 1 + +/** + * mem_alloc_copy_from_user_helper - copy from user helper + * @wrqu_data: wireless extensions request data + * @len: length of @wrqu_data + * + * Helper function to allocate buffer and copy user data. + * + * Return: On success return a pointer to a kernel buffer containing a + * copy of the userspace data (with an additional NUL character + * appended for safety). On failure return %NULL. + */ +void *mem_alloc_copy_from_user_helper(const __user void *wrqu_data, size_t len) +{ + u8 *ptr = NULL; + + /* in order to protect the code, an extra byte is post + * appended to the buffer and the null termination is added. + * However, when allocating (len+1) byte of memory, we need to + * make sure that there is no uint overflow when doing + * addition. In theory check len < UINT_MAX protects the uint + * overflow. For wlan private ioctl, the buffer size is much + * less than UINT_MAX, as a good guess, now, it is assumed + * that the private command buffer size is no greater than 4K + * (4096 bytes). So we use 4096 as the upper boundary for now. + */ + if (len > MAX_USER_COMMAND_SIZE) { + hdd_err("Invalid length: %zu max: %u", + len, MAX_USER_COMMAND_SIZE); + return NULL; + } + + ptr = qdf_mem_malloc(len + 1); + if (!ptr) { + hdd_err("unable to allocate memory"); + return NULL; + } + + if (copy_from_user(ptr, wrqu_data, len)) { + hdd_err("failed to copy data to user buffer"); + qdf_mem_free(ptr); + return NULL; + } + ptr[len] = '\0'; + return ptr; +} + +/** + * hdd_priv_get_data() - Get pointer to ioctl private data + * @p_priv_data: pointer to iw_point struct to be filled + * @wrqu: Pointer to IOCTL Data received from userspace + * + * Helper function to get compatible struct iw_point passed to ioctl + * + * Return - 0 if p_priv_data successfully filled, error otherwise + */ +int hdd_priv_get_data(struct iw_point *p_priv_data, union iwreq_data *wrqu) +{ + if ((!p_priv_data) || (!wrqu)) + return -EINVAL; + +#ifdef CONFIG_COMPAT + if (in_compat_syscall()) { + struct compat_iw_point *p_compat_priv_data; + + /* Compat task: + * typecast to compat structure and copy the members. + */ + p_compat_priv_data = (struct compat_iw_point *)&wrqu->data; + + p_priv_data->pointer = compat_ptr(p_compat_priv_data->pointer); + p_priv_data->length = p_compat_priv_data->length; + p_priv_data->flags = p_compat_priv_data->flags; + } else { +#endif /* #ifdef CONFIG_COMPAT */ + + /* Non compat task: directly copy the structure. */ + memcpy(p_priv_data, &wrqu->data, sizeof(struct iw_point)); + +#ifdef CONFIG_COMPAT + } +#endif /* #ifdef CONFIG_COMPAT */ + + return 0; +} + +static int hdd_check_wext_control(enum hdd_wext_control wext_control, + struct iw_request_info *info) +{ + switch (wext_control) { + default: + case hdd_wext_disabled: + hdd_err_rl("Rejecting disabled ioctl %x", info->cmd); + return -ENOTSUPP; + case hdd_wext_deprecated: + hdd_nofl_debug("Using deprecated ioctl %x", info->cmd); + return 0; + case hdd_wext_enabled: + return 0; + } +} + +int hdd_check_private_wext_control(struct hdd_context *hdd_ctx, + struct iw_request_info *info) +{ + return hdd_check_wext_control(hdd_ctx->config->private_wext_control, + info); +} + +/** + * hdd_wlan_get_stats() - Get txrx stats in SAP mode + * @adapter: Pointer to the hdd adapter. + * @length: Size of the data copied + * @buffer: Pointer to char buffer. + * @buf_len: Length of the char buffer. + * + * This function called when the "iwpriv wlan0 get_stats" command is given. + * It used to collect the txrx stats when the device is configured in SAP mode. + * + * Return - none + */ +void hdd_wlan_get_stats(struct hdd_adapter *adapter, uint16_t *length, + char *buffer, uint16_t buf_len) +{ + struct hdd_tx_rx_stats *stats = &adapter->hdd_stats.tx_rx_stats; + uint32_t len = 0; + uint32_t total_rx_pkt = 0, total_rx_dropped = 0; + uint32_t total_rx_delv = 0, total_rx_refused = 0; + int i = 0; + struct hdd_context *hdd_ctx = adapter->hdd_ctx; + + for (; i < NUM_CPUS; i++) { + total_rx_pkt += stats->rx_packets[i]; + total_rx_dropped += stats->rx_dropped[i]; + total_rx_delv += stats->rx_delivered[i]; + total_rx_refused += stats->rx_refused[i]; + } + + len = scnprintf(buffer, buf_len, + "\nTransmit[%lu] - " + "called %u, dropped %u orphan %u," + "\n[dropped] BK %u, BE %u, VI %u, VO %u" + "\n[classified] BK %u, BE %u, VI %u, VO %u" + "\n\nReceive[%lu] - " + "packets %u, dropped %u, unsolict_arp_n_mcast_drp %u, delivered %u, refused %u\n" + "GRO - agg %u non-agg %u flush_skip %u low_tput_flush %u disabled(conc %u low-tput %u)\n", + qdf_system_ticks(), + stats->tx_called, + stats->tx_dropped, + stats->tx_orphaned, + stats->tx_dropped_ac[SME_AC_BK], + stats->tx_dropped_ac[SME_AC_BE], + stats->tx_dropped_ac[SME_AC_VI], + stats->tx_dropped_ac[SME_AC_VO], + stats->tx_classified_ac[SME_AC_BK], + stats->tx_classified_ac[SME_AC_BE], + stats->tx_classified_ac[SME_AC_VI], + stats->tx_classified_ac[SME_AC_VO], + qdf_system_ticks(), + total_rx_pkt, total_rx_dropped, + qdf_atomic_read(&stats->rx_usolict_arp_n_mcast_drp), + total_rx_delv, + total_rx_refused, + stats->rx_aggregated, stats->rx_non_aggregated, + stats->rx_gro_flush_skip, + stats->rx_gro_low_tput_flush, + qdf_atomic_read(&hdd_ctx->disable_rx_ol_in_concurrency), + qdf_atomic_read(&hdd_ctx->disable_rx_ol_in_low_tput)); + + for (i = 0; i < NUM_CPUS; i++) { + if (stats->rx_packets[i] == 0) + continue; + len += scnprintf(buffer + len, buf_len - len, + "Rx CPU[%d]:" + "packets %u, dropped %u, delivered %u, refused %u\n", + i, stats->rx_packets[i], stats->rx_dropped[i], + stats->rx_delivered[i], stats->rx_refused[i]); + } + + len += scnprintf(buffer + len, buf_len - len, + "\nTX_FLOW" + "\nCurrent status: %s" + "\ntx-flow timer start count %u" + "\npause count %u, unpause count %u", + (stats->is_txflow_paused == true ? "PAUSED" : "UNPAUSED"), + stats->txflow_timer_cnt, + stats->txflow_pause_cnt, + stats->txflow_unpause_cnt); + + len += cdp_stats(cds_get_context(QDF_MODULE_ID_SOC), + adapter->vdev_id, &buffer[len], (buf_len - len)); + *length = len + 1; +} + +/** + * wlan_hdd_write_suspend_resume_stats() - Writes suspend/resume stats to buffer + * @hdd_ctx: The Hdd context owning the stats to be written + * @buffer: The char buffer to write to + * @max_len: The maximum number of chars to write + * + * This assumes hdd_ctx has already been validated, and buffer is not NULL. + * + * Return - length of written content, negative number on error + */ +static int wlan_hdd_write_suspend_resume_stats(struct hdd_context *hdd_ctx, + char *buffer, uint16_t max_len) +{ + int ret; + QDF_STATUS status; + struct suspend_resume_stats *sr_stats; + + sr_stats = &hdd_ctx->suspend_resume_stats; + ret = scnprintf(buffer, max_len, + "\n" + "Suspends: %u\n" + "Resumes: %u\n" + "\n" + "Suspend Fail Reasons\n" + "\tIPA: %u\n" + "\tRadar: %u\n" + "\tRoam: %u\n" + "\tScan: %u\n" + "\tInitial Wakeup: %u\n" + "\n", + sr_stats->suspends, sr_stats->resumes, + sr_stats->suspend_fail[SUSPEND_FAIL_IPA], + sr_stats->suspend_fail[SUSPEND_FAIL_RADAR], + sr_stats->suspend_fail[SUSPEND_FAIL_ROAM], + sr_stats->suspend_fail[SUSPEND_FAIL_SCAN], + sr_stats->suspend_fail[SUSPEND_FAIL_INITIAL_WAKEUP]); + + status = ucfg_mc_cp_stats_write_wow_stats(hdd_ctx->psoc, + &buffer[ret], max_len - ret, + &ret); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get WoW stats"); + return qdf_status_to_os_return(status); + } + + return ret; +} + +/** + * hdd_wlan_list_fw_profile() - Get fw profiling points + * @length: Size of the data copied + * @buffer: Pointer to char buffer. + * @buf_len: Length of the char buffer. + * + * This function called when the "iwpriv wlan0 listProfile" command is given. + * It is used to get the supported profiling points in FW. + * + * Return - none + */ +void hdd_wlan_list_fw_profile(uint16_t *length, + char *buffer, uint16_t buf_len) +{ + uint32_t len = 0; + + len = scnprintf(buffer, buf_len, + "PROF_CPU_IDLE: %u\n" + "PROF_PPDU_PROC: %u\n" + "PROF_PPDU_POST: %u\n" + "PROF_HTT_TX_INPUT: %u\n" + "PROF_MSDU_ENQ: %u\n" + "PROF_PPDU_POST_HAL: %u\n" + "PROF_COMPUTE_TX_TIME: %u\n", + PROF_CPU_IDLE, + PROF_PPDU_PROC, + PROF_PPDU_POST, + PROF_HTT_TX_INPUT, + PROF_MSDU_ENQ, + PROF_PPDU_POST_HAL, + PROF_COMPUTE_TX_TIME); + + *length = len + 1; +} + +#define HDD_DUMP_STAT_HELP(STAT_ID) \ + hdd_nofl_info("%u -- %s", STAT_ID, (# STAT_ID)) +/** + * hdd_display_stats_help() - print statistics help + * + * Return: none + */ +static void hdd_display_stats_help(void) +{ + hdd_nofl_info("iwpriv wlan0 dumpStats [option] - dump statistics"); + hdd_nofl_info("iwpriv wlan0 clearStats [option] - clear statistics"); + hdd_nofl_info("options:"); + HDD_DUMP_STAT_HELP(CDP_TXRX_PATH_STATS); + HDD_DUMP_STAT_HELP(CDP_TXRX_HIST_STATS); + HDD_DUMP_STAT_HELP(CDP_TXRX_TSO_STATS); + HDD_DUMP_STAT_HELP(CDP_HDD_NETIF_OPER_HISTORY); + HDD_DUMP_STAT_HELP(CDP_DUMP_TX_FLOW_POOL_INFO); + HDD_DUMP_STAT_HELP(CDP_TXRX_DESC_STATS); + HDD_DUMP_STAT_HELP(CDP_HIF_STATS); + HDD_DUMP_STAT_HELP(CDP_NAPI_STATS); + HDD_DUMP_STAT_HELP(CDP_DP_NAPI_STATS); + HDD_DUMP_STAT_HELP(CDP_DP_RX_THREAD_STATS); +} + +/** + * hdd_wlan_dump_stats() - display dump Stats + * @adapter: adapter handle + * @value: value from user + * + * Return: 0 => success, error code on failure + */ +int hdd_wlan_dump_stats(struct hdd_adapter *adapter, int value) +{ + int ret = 0; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_debug("%d", value); + + switch (value) { + case CDP_TXRX_HIST_STATS: + wlan_hdd_display_tx_rx_histogram(hdd_ctx); + break; + case CDP_HDD_NETIF_OPER_HISTORY: + wlan_hdd_display_adapter_netif_queue_history(adapter); + break; + case CDP_HIF_STATS: + hdd_display_hif_stats(); + break; + case CDP_LRO_STATS: + hdd_lro_display_stats(hdd_ctx); + break; + case CDP_NAPI_STATS: + if (hdd_display_napi_stats()) { + hdd_err("error displaying napi stats"); + ret = -EFAULT; + } + break; + case CDP_DP_RX_THREAD_STATS: + dp_txrx_ext_dump_stats(cds_get_context(QDF_MODULE_ID_SOC), + CDP_DP_RX_THREAD_STATS); + break; + case CDP_DISCONNECT_STATS: + sme_display_disconnect_stats(hdd_ctx->mac_handle, + adapter->vdev_id); + break; + default: + status = cdp_display_stats(cds_get_context(QDF_MODULE_ID_SOC), + value, + QDF_STATS_VERBOSITY_LEVEL_HIGH); + if (status == QDF_STATUS_E_INVAL) { + hdd_display_stats_help(); + ret = -EINVAL; + } + break; + } + return ret; +} + +#ifdef QCA_IBSS_SUPPORT +/** + * hdd_wlan_get_ibss_peer_info_all() - Print all IBSS peers + * @adapter: Adapter upon which the IBSS clients are active + * + * Return: QDF_STATUS_STATUS if the peer information was retrieved and + * displayed, otherwise an appropriate QDF_STATUS_E_* failure code. + */ +static QDF_STATUS hdd_wlan_get_ibss_peer_info_all(struct hdd_adapter *adapter) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + tSirPeerInfoRspParams *peer_info = &sta_ctx->ibss_peer_info; + struct qdf_mac_addr bcast = QDF_MAC_ADDR_BCAST_INIT; + int i; + + INIT_COMPLETION(adapter->ibss_peer_info_comp); + status = sme_request_ibss_peer_info(mac_handle, adapter, + hdd_get_ibss_peer_info_cb, + true, bcast.bytes); + + if (QDF_STATUS_SUCCESS == status) { + unsigned long rc; + + rc = wait_for_completion_timeout + (&adapter->ibss_peer_info_comp, + msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT)); + if (!rc) { + hdd_err("failed wait on ibss_peer_info_comp"); + return QDF_STATUS_E_FAILURE; + } + + /** Print the peer info */ + hdd_debug("peer_info->numIBSSPeers = %d ", + (int)peer_info->numPeers); + for (i = 0; i < peer_info->numPeers; i++) { + uint8_t mac_addr[QDF_MAC_ADDR_SIZE]; + uint32_t tx_rate; + + tx_rate = peer_info->peerInfoParams[i].txRate; + qdf_mem_copy(mac_addr, + peer_info->peerInfoParams[i].mac_addr, + sizeof(mac_addr)); + + hdd_debug(" PEER ADDR : "QDF_MAC_ADDR_FMT" TxRate: %d Mbps RSSI: %d", + QDF_MAC_ADDR_REF(mac_addr), (int)tx_rate, + (int)peer_info->peerInfoParams[i].rssi); + } + } else { + hdd_warn("Warning: sme_request_ibss_peer_info Request failed"); + } + + return status; +} +#else +/** + * hdd_wlan_get_ibss_peer_info() - Print IBSS peer information + * @adapter: Adapter upon which the IBSS client is active + * @sta_id: Station index of the IBSS peer + * + * This function is dummy + * + * Return: QDF_STATUS_STATUS + */ +static inline QDF_STATUS +hdd_wlan_get_ibss_peer_info(struct hdd_adapter *adapter, + uint8_t sta_id) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_wlan_get_ibss_peer_info_all() - Print all IBSS peers + * @adapter: Adapter upon which the IBSS clients are active + * + * This function is dummy + * + * Return: QDF_STATUS_STATUS + */ +static inline QDF_STATUS +hdd_wlan_get_ibss_peer_info_all(struct hdd_adapter *adapter) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * hdd_get_ldpc() - Get adapter LDPC + * @adapter: adapter being queried + * @value: where to store the value + * + * Return: 0 on success, negative errno on failure + */ +int hdd_get_ldpc(struct hdd_adapter *adapter, int *value) +{ + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + int ret; + + hdd_enter(); + ret = sme_get_ht_config(mac_handle, adapter->vdev_id, + WNI_CFG_HT_CAP_INFO_ADVANCE_CODING); + if (ret < 0) { + hdd_err("Failed to get LDPC value"); + } else { + *value = ret; + ret = 0; + } + return ret; +} + +int hdd_set_ldpc(struct hdd_adapter *adapter, int value) +{ + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + int ret; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct mlme_ht_capabilities_info ht_cap_info; + + hdd_debug("%d", value); + + if (!mac_handle) { + hdd_err("NULL Mac handle"); + return -EINVAL; + } + + status = ucfg_mlme_get_ht_cap_info(hdd_ctx->psoc, &ht_cap_info); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed to get HT capability info"); + return -EIO; + } + + ht_cap_info.adv_coding_cap = value; + status = ucfg_mlme_set_ht_cap_info(hdd_ctx->psoc, ht_cap_info); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed to set HT capability info"); + return -EIO; + } + status = ucfg_mlme_cfg_set_vht_ldpc_coding_cap(hdd_ctx->psoc, value); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set VHT LDPC capability info"); + return -EIO; + } + ret = sme_update_ht_config(mac_handle, adapter->vdev_id, + WNI_CFG_HT_CAP_INFO_ADVANCE_CODING, + value); + if (ret) + hdd_err("Failed to set LDPC value"); + ret = sme_update_he_ldpc_supp(mac_handle, adapter->vdev_id, value); + if (ret) + hdd_err("Failed to set HE LDPC value"); + ret = sme_set_auto_rate_ldpc(mac_handle, adapter->vdev_id, + (value ? 0 : 1)); + + return ret; +} + +/** + * hdd_get_tx_stbc() - Get adapter TX STBC + * @adapter: adapter being queried + * @value: where to store the value + * + * Return: 0 on success, negative errno on failure + */ +int hdd_get_tx_stbc(struct hdd_adapter *adapter, int *value) +{ + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + int ret; + + hdd_enter(); + ret = sme_get_ht_config(mac_handle, adapter->vdev_id, + WNI_CFG_HT_CAP_INFO_TX_STBC); + if (ret < 0) { + hdd_err("Failed to get TX STBC value"); + } else { + *value = ret; + ret = 0; + } + + return ret; +} + +int hdd_set_tx_stbc(struct hdd_adapter *adapter, int value) +{ + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + QDF_STATUS status; + struct mlme_ht_capabilities_info ht_cap_info; + + hdd_debug("%d", value); + + if (!mac_handle) { + hdd_err("NULL Mac handle"); + return -EINVAL; + } + + if (value) { + /* make sure HT capabilities allow this */ + status = ucfg_mlme_get_ht_cap_info(hdd_ctx->psoc, + &ht_cap_info); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed to get HT capability info"); + return -EIO; + } + if (!ht_cap_info.tx_stbc) { + hdd_err("TX STBC not supported"); + return -EINVAL; + } + } + ret = sme_update_ht_config(mac_handle, adapter->vdev_id, + WNI_CFG_HT_CAP_INFO_TX_STBC, + value); + if (ret) + hdd_err("Failed to set TX STBC value"); + ret = sme_update_he_tx_stbc_cap(mac_handle, adapter->vdev_id, value); + if (ret) + hdd_err("Failed to set HE TX STBC value"); + + return ret; +} + +/** + * hdd_get_rx_stbc() - Get adapter RX STBC + * @adapter: adapter being queried + * @value: where to store the value + * + * Return: 0 on success, negative errno on failure + */ +int hdd_get_rx_stbc(struct hdd_adapter *adapter, int *value) +{ + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + int ret; + + hdd_enter(); + ret = sme_get_ht_config(mac_handle, adapter->vdev_id, + WNI_CFG_HT_CAP_INFO_RX_STBC); + if (ret < 0) { + hdd_err("Failed to get RX STBC value"); + } else { + *value = ret; + ret = 0; + } + + return ret; +} + +int hdd_set_rx_stbc(struct hdd_adapter *adapter, int value) +{ + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int ret; + QDF_STATUS status; + struct mlme_ht_capabilities_info ht_cap_info; + + hdd_debug("%d", value); + + if (!mac_handle) { + hdd_err("NULL Mac handle"); + return -EINVAL; + } + + if (value) { + /* make sure HT capabilities allow this */ + status = ucfg_mlme_get_ht_cap_info(hdd_ctx->psoc, + &ht_cap_info); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("Failed to get HT capability info"); + return -EIO; + } + if (!ht_cap_info.rx_stbc) { + hdd_warn("RX STBC not supported"); + return -EINVAL; + } + } + ret = sme_update_ht_config(mac_handle, adapter->vdev_id, + WNI_CFG_HT_CAP_INFO_RX_STBC, + value); + if (ret) + hdd_err("Failed to set RX STBC value"); + + ret = sme_update_he_rx_stbc_cap(mac_handle, adapter->vdev_id, value); + if (ret) + hdd_err("Failed to set HE RX STBC value"); + + return ret; +} + +/** + * iw_get_linkspeed() - Get current link speed ioctl + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: extra ioctl buffer + * + * Return: 0 on success, non-zero on error + */ +static int __iw_get_linkspeed(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + char *out_link_speed = (char *)extra; + int len = sizeof(uint32_t) + 1; + uint32_t link_speed = 0; + struct hdd_context *hdd_ctx; + int ret; + int rc; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + ret = wlan_hdd_get_link_speed(adapter, &link_speed); + if (0 != ret) + return ret; + + wrqu->data.length = len; + /* return the linkspeed as a string */ + rc = snprintf(out_link_speed, len, "%u", link_speed); + if ((rc < 0) || (rc >= len)) { + /* encoding or length error? */ + hdd_err("Unable to encode link speed"); + return -EIO; + } + + hdd_exit(); + /* a value is being successfully returned */ + return 0; +} + +static int iw_get_linkspeed(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_get_linkspeed(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef FEATURE_WLM_STATS +static void wlan_get_wlm_stats_cb(void *cookie, const char *data) +{ + struct osif_request *request; + char *priv; + + request = osif_request_get(cookie); + if (!request) { + hdd_err("Obsolete request"); + return; + } + priv = osif_request_priv(request); + strlcpy(priv, data, WE_MAX_STR_LEN); + osif_request_complete(request); + osif_request_put(request); +} + +static int wlan_get_wlm_stats(struct hdd_adapter *adapter, uint32_t bitmask, + char *response) +{ + struct osif_request *request; + void *cookie; + int errno; + char *priv; + static const struct osif_request_params params = { + .priv_size = WE_MAX_STR_LEN, + .timeout_ms = 2000, + }; + + if (!adapter) { + hdd_err("NULL argument"); + return -EINVAL; + } + request = osif_request_alloc(¶ms); + if (!request) { + hdd_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + errno = wma_wlm_stats_req(adapter->vdev_id, bitmask, + params.priv_size, + wlan_get_wlm_stats_cb, cookie); + if (errno) { + hdd_err("Request failed be sent, %d", errno); + goto cleanup; + } + errno = osif_request_wait_for_response(request); + if (errno) { + hdd_err("Timeout happened, can't complete the req"); + goto cleanup; + } + priv = osif_request_priv(request); + strlcpy(response, priv, params.priv_size); + +cleanup: + osif_request_put(request); + + return errno; +} + +/* + * Due to a limitation in iwpriv the "get_wlm_stats" ioctl is defined + * to take as input a variable-length string as opposed to taking a + * single integer "bitmask" value. Hence we must have a buffer large + * enough to hold a string representing the largest possible + * value. MAX_INT = 2,147,483,647 which can be fit in 10 chars. + * Round up to 12 to hold the trailing NUL and be a multiple of 4. + */ +#define WLM_USER_DATA_SIZE 12 + +static int __iw_get_wlm_stats(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_point priv_data; + char user_data[WLM_USER_DATA_SIZE] = {0}; + uint32_t bitmask = 0; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + int errno; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + errno = wlan_hdd_validate_context(hdd_ctx); + if (errno) + return errno; + + if (!capable(CAP_NET_ADMIN)) { + hdd_err("permission check failed"); + return -EPERM; + } + + /* + * Since this is GETTER iwpriv ioctl, driver needs to + * copy SET data from user space to kernel space. + * Helper function to get iwreq_data with compat handling. + */ + if (hdd_priv_get_data(&priv_data, wrqu)) + return -EINVAL; + + /* + * priv_data.pointer should be pointing to data given + * to iwpriv command. + * + * For example "iwpriv wlan0 get_wlm_stats 1234" + * + * priv_data.pointer should be pointing to "1234" + * priv_data.length should be zero as this GETTER iwpriv ioctl + */ + if (!priv_data.pointer) { + hdd_err("NULL data pointer"); + return -EINVAL; + } + + /* + * ideally driver should have used priv_data.length to copy + * data from priv_data.pointer but this iwpriv IOCTL has been + * declared as GETTER in nature which makes length field zero + * for input arguments but priv_data.pointer still points to + * user's input argument (just doesn't pass the length of the + * argument) + */ + if (copy_from_user(user_data, priv_data.pointer, + sizeof(user_data) - 1)) { + hdd_err("failed to copy data from user buffer"); + return -EFAULT; + } + + /* + * user data is given in ascii, convert ascii to integer + */ + if (kstrtou32(user_data, 0, &bitmask)) { + hdd_err("failed to parse input %s", user_data); + return -EFAULT; + } + + if (wlan_get_wlm_stats(adapter, bitmask, extra)) { + hdd_err("returning failure"); + return -EFAULT; + } + wrqu->data.length = strlen(extra) + 1; + + return 0; +} + +static int iw_get_wlm_stats(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct osif_vdev_sync *vdev_sync; + int errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_get_wlm_stats(net_dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif /* FEATURE_WLM_STATS */ + +int wlan_hdd_update_phymode(struct hdd_adapter *adapter, int new_phymode) +{ + struct net_device *net = adapter->dev; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle = hdd_ctx->mac_handle; + bool band_24 = false, band_5g = false; + bool ch_bond24 = false, ch_bond5g = false; + struct sme_config_params *sme_config = NULL; + struct csr_config_params *csr_config; + uint32_t chwidth = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + uint8_t vhtchanwidth; + eCsrPhyMode phymode = -EIO, old_phymode; + enum hdd_dot11_mode hdd_dot11mode = hdd_ctx->config->dot11Mode; + enum band_info curr_band = BAND_ALL; + int retval = 0; + uint32_t band_capability; + QDF_STATUS status; + uint32_t channel_bonding_mode; + + if (!mac_handle) + return -EINVAL; + + old_phymode = sme_get_phy_mode(mac_handle); + + ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, + &channel_bonding_mode); + if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE != + sme_get_cb_phy_state_from_cb_ini_value(channel_bonding_mode)) + ch_bond24 = true; + + ucfg_mlme_get_channel_bonding_5ghz(hdd_ctx->psoc, + &channel_bonding_mode); + if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE != + sme_get_cb_phy_state_from_cb_ini_value(channel_bonding_mode)) + ch_bond5g = true; + + status = wlan_mlme_get_band_capability(hdd_ctx->psoc, &band_capability); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get MLME Band capability"); + return -EIO; + } + + if (band_capability == BAND_ALL) + band_24 = band_5g = true; + else if (band_capability == BAND_2G) + band_24 = true; + else if (band_capability == BAND_5G) + band_5g = true; + + status = ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc, &vhtchanwidth); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to get channel_width"); + + hdd_debug("ch_bond24=%d ch_bond5g=%d band_24=%d band_5g=%d VHT_ch_width=%u", + ch_bond24, ch_bond5g, band_24, band_5g, vhtchanwidth); + + switch (new_phymode) { + case IEEE80211_MODE_AUTO: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_AUTO); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_AUTO) == 0) { + phymode = eCSR_DOT11_MODE_AUTO; + hdd_dot11mode = eHDD_DOT11_MODE_AUTO; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; + curr_band = BAND_ALL; + vhtchanwidth = eHT_CHANNEL_WIDTH_80MHZ; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_11A: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_11a); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_5_GHZ) == 0) { + phymode = eCSR_DOT11_MODE_11a; + hdd_dot11mode = eHDD_DOT11_MODE_11a; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + curr_band = BAND_5G; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_11B: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_11b); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_2_4_GHZ) == 0) { + phymode = eCSR_DOT11_MODE_11b; + hdd_dot11mode = eHDD_DOT11_MODE_11b; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + curr_band = BAND_2G; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_11G: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_11g); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_2_4_GHZ) == 0) { + phymode = eCSR_DOT11_MODE_11g; + hdd_dot11mode = eHDD_DOT11_MODE_11g; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + curr_band = BAND_2G; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + /* UMAC doesn't have option to set MODE_11NA/MODE_11NG as phymode + * so setting phymode as eCSR_DOT11_MODE_11n and updating the band + * and channel bonding in configuration to reflect MODE_11NA/MODE_11NG + */ + case IEEE80211_MODE_11NA_HT20: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_11n); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_5_GHZ) == 0) { + phymode = eCSR_DOT11_MODE_11n; + hdd_dot11mode = eHDD_DOT11_MODE_11n; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + curr_band = BAND_5G; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_11NA_HT40: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_11n); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_5_GHZ) == 0) { + phymode = eCSR_DOT11_MODE_11n; + hdd_dot11mode = eHDD_DOT11_MODE_11n; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; + curr_band = BAND_5G; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_11NG_HT20: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_11n); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_2_4_GHZ) == 0) { + phymode = eCSR_DOT11_MODE_11n; + hdd_dot11mode = eHDD_DOT11_MODE_11n; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + curr_band = BAND_2G; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_11NG_HT40: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_11n); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_2_4_GHZ) == 0) { + phymode = eCSR_DOT11_MODE_11n; + hdd_dot11mode = eHDD_DOT11_MODE_11n; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; + curr_band = BAND_2G; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_11AC_VHT20: + case IEEE80211_MODE_11AC_VHT40: + case IEEE80211_MODE_11AC_VHT80: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_11ac); + phymode = eCSR_DOT11_MODE_11ac; + hdd_dot11mode = eHDD_DOT11_MODE_11ac; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; + if (band_5g && band_24) { + curr_band = BAND_ALL; + break; + } else if (band_5g) { + curr_band = BAND_5G; + break; + } else if (new_phymode != IEEE80211_MODE_11AC_VHT80) { + curr_band = BAND_2G; + break; + } + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_AUTO) == 0) { + curr_band = BAND_ALL; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_2G_AUTO: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_AUTO); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_2_4_GHZ) == 0) { + phymode = eCSR_DOT11_MODE_AUTO; + hdd_dot11mode = eHDD_DOT11_MODE_AUTO; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; + curr_band = BAND_2G; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_5G_AUTO: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_AUTO); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_5_GHZ) == 0) { + phymode = eCSR_DOT11_MODE_AUTO; + hdd_dot11mode = eHDD_DOT11_MODE_AUTO; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; + vhtchanwidth = eHT_CHANNEL_WIDTH_80MHZ; + curr_band = BAND_5G; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + case IEEE80211_MODE_11AGN: + sme_set_phy_mode(mac_handle, eCSR_DOT11_MODE_11n); + if (hdd_reg_set_band(net, WLAN_HDD_UI_BAND_AUTO) == 0) { + phymode = eCSR_DOT11_MODE_11n; + hdd_dot11mode = eHDD_DOT11_MODE_11n; + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; + curr_band = BAND_ALL; + } else { + sme_set_phy_mode(mac_handle, old_phymode); + return -EIO; + } + break; + default: + return -EIO; + } + + switch (new_phymode) { + case IEEE80211_MODE_11AC_VHT20: + chwidth = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + vhtchanwidth = eHT_CHANNEL_WIDTH_20MHZ; + break; + case IEEE80211_MODE_11AC_VHT40: + vhtchanwidth = eHT_CHANNEL_WIDTH_40MHZ; + break; + case IEEE80211_MODE_11AC_VHT80: + vhtchanwidth = eHT_CHANNEL_WIDTH_80MHZ; + break; + default: + status = ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc, + &vhtchanwidth); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to get channel_width"); + break; + } + + if (phymode != -EIO) { + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + if (!sme_config) { + hdd_err("Failed to allocate memory for sme_config"); + return -ENOMEM; + } + qdf_mem_zero(sme_config, sizeof(*sme_config)); + sme_get_config_param(mac_handle, sme_config); + csr_config = &sme_config->csr_config; + csr_config->phyMode = phymode; +#ifdef QCA_HT_2040_COEX + if (phymode == eCSR_DOT11_MODE_11n && + chwidth == WNI_CFG_CHANNEL_BONDING_MODE_DISABLE) { + csr_config->obssEnabled = false; + status = sme_set_ht2040_mode(mac_handle, + adapter->vdev_id, + eHT_CHAN_HT20, false); + if (status == QDF_STATUS_E_FAILURE) { + hdd_err("Failed to disable OBSS"); + retval = -EIO; + goto free; + } + } else if (phymode == eCSR_DOT11_MODE_11n && + chwidth == WNI_CFG_CHANNEL_BONDING_MODE_ENABLE) { + csr_config->obssEnabled = true; + status = sme_set_ht2040_mode(mac_handle, + adapter->vdev_id, + eHT_CHAN_HT20, true); + if (status == QDF_STATUS_E_FAILURE) { + hdd_err("Failed to enable OBSS"); + retval = -EIO; + goto free; + } + } +#endif + status = ucfg_mlme_set_band_capability(hdd_ctx->psoc, + curr_band); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed to set MLME band capability"); + goto free; + } + + if (curr_band == BAND_2G) { + status = ucfg_mlme_set_11h_enabled(hdd_ctx->psoc, 0); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to set 11h_enable flag"); + goto free; + } + } + if (curr_band == BAND_2G) + csr_config->channelBondingMode24GHz = chwidth; + else if (curr_band == BAND_5G) + csr_config->channelBondingMode5GHz = chwidth; + else { + csr_config->channelBondingMode24GHz = chwidth; + csr_config->channelBondingMode5GHz = chwidth; + } + sme_update_config(mac_handle, sme_config); + + hdd_ctx->config->dot11Mode = hdd_dot11mode; + ucfg_mlme_set_channel_bonding_24ghz( + hdd_ctx->psoc, + csr_config->channelBondingMode24GHz); + ucfg_mlme_set_channel_bonding_5ghz( + hdd_ctx->psoc, + csr_config->channelBondingMode5GHz); + if (hdd_update_config_cfg(hdd_ctx) == false) { + hdd_err("could not update config_dat"); + retval = -EIO; + goto free; + } + + if (band_5g) { + struct ieee80211_supported_band *band; + + ucfg_mlme_get_channel_bonding_5ghz( + hdd_ctx->psoc, &channel_bonding_mode); + band = hdd_ctx->wiphy->bands[HDD_NL80211_BAND_5GHZ]; + if (channel_bonding_mode) + band->ht_cap.cap |= + IEEE80211_HT_CAP_SUP_WIDTH_20_40; + else + band->ht_cap.cap &= + ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + + hdd_debug("New_Phymode= %d ch_bonding=%d band=%d VHT_ch_width=%u", + phymode, chwidth, curr_band, vhtchanwidth); + } + +free: + if (sme_config) + qdf_mem_free(sme_config); + return retval; +} + +static int hdd_validate_pdev_reset(int value) +{ + if ((value < 1) || (value > 5)) { + hdd_warn(" Invalid value %d: Use any one of the below values\n" + " TX_FLUSH = 1\n" + " WARM_RESET = 2\n" + " COLD_RESET = 3\n" + " WARM_RESET_RESTORE_CAL = 4\n" + " COLD_RESET_RESTORE_CAL = 5", value); + + return -EINVAL; + } + + return 0; +} + +static int hdd_handle_pdev_reset(struct hdd_adapter *adapter, int value) +{ + int ret; + + hdd_debug("%d", value); + + ret = hdd_validate_pdev_reset(value); + if (ret) + return ret; + + ret = wma_cli_set_command(adapter->vdev_id, + WMI_PDEV_PARAM_PDEV_RESET, + value, PDEV_CMD); + + return ret; +} + +static int hdd_we_set_ch_width(struct hdd_adapter *adapter, int ch_width) +{ + int errno; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t bonding_mode; + struct sme_config_params *sme_config; + mac_handle_t mac_handle; + + mac_handle = hdd_ctx->mac_handle; + if (!mac_handle) + return -EINVAL; + + /* updating channel bonding only on 5Ghz */ + hdd_debug("WMI_VDEV_PARAM_CHWIDTH val %d", ch_width); + + switch (ch_width) { + case eHT_CHANNEL_WIDTH_20MHZ: + bonding_mode = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + break; + + case eHT_CHANNEL_WIDTH_40MHZ: + case eHT_CHANNEL_WIDTH_80MHZ: + bonding_mode = 1; + break; + + default: + hdd_err("Invalid channel width 0->20 1->40 2->80"); + return -EINVAL; + } + + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + if (!sme_config) { + hdd_err("failed to allocate memory for sme_config"); + return -ENOMEM; + } + + errno = wma_cli_set_command(adapter->vdev_id, WMI_VDEV_PARAM_CHWIDTH, + ch_width, VDEV_CMD); + if (errno) + goto free_config; + + sme_get_config_param(mac_handle, sme_config); + sme_config->csr_config.channelBondingMode5GHz = bonding_mode; + sme_config->csr_config.channelBondingMode24GHz = bonding_mode; + sme_update_config(mac_handle, sme_config); + +free_config: + qdf_mem_free(sme_config); + + return errno; +} + +static int hdd_we_set_11d_state(struct hdd_adapter *adapter, int state_11d) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + bool enable_11d; + mac_handle_t mac_handle = hdd_ctx->mac_handle; + QDF_STATUS status; + + if (!mac_handle) + return -EINVAL; + + switch (state_11d) { + case ENABLE_11D: + enable_11d = true; + break; + case DISABLE_11D: + enable_11d = false; + break; + default: + return -EINVAL; + } + + status = ucfg_mlme_set_11d_enabled(hdd_ctx->psoc, enable_11d); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Invalid 11d_enable flag"); + + hdd_debug("11D state=%d", enable_11d); + + return 0; +} + +static int hdd_we_set_power(struct hdd_adapter *adapter, int value) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle = hdd_ctx->mac_handle; + + if (!mac_handle) + return -EINVAL; + + switch (value) { + case 1: + /* Enable PowerSave */ + sme_save_usr_ps_cfg(mac_handle, true); + sme_ps_enable_disable(mac_handle, adapter->vdev_id, + SME_PS_ENABLE); + return 0; + case 2: + /* Disable PowerSave */ + sme_ps_enable_disable(mac_handle, adapter->vdev_id, + SME_PS_DISABLE); + sme_save_usr_ps_cfg(mac_handle, false); + return 0; + case 3: + /* Enable UASPD */ + sme_ps_uapsd_enable(mac_handle, adapter->vdev_id); + return 0; + case 4: + /* Disable UASPD */ + sme_ps_uapsd_disable(mac_handle, adapter->vdev_id); + return 0; + default: + hdd_err("Invalid value %d", value); + return -EINVAL; + } +} + +static int hdd_we_set_max_assoc(struct hdd_adapter *adapter, int value) +{ + struct hdd_context *hdd_ctx; + QDF_STATUS status; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = ucfg_mlme_set_assoc_sta_limit(hdd_ctx->psoc, value); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("cfg set failed, value %d status %d", value, status); + + return qdf_status_to_os_return(status); +} + +static int hdd_we_set_data_inactivity_timeout(struct hdd_adapter *adapter, + int inactivity_timeout) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + + status = ucfg_mlme_set_ps_data_inactivity_timeout(hdd_ctx->psoc, + inactivity_timeout); + + return qdf_status_to_os_return(status); +} + +static int hdd_we_set_wow_data_inactivity_timeout(struct hdd_adapter *adapter, + int value) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle = hdd_ctx->mac_handle; + + if (!mac_handle) + return -EINVAL; + + if (!cfg_in_range(CFG_PMO_WOW_DATA_INACTIVITY_TIMEOUT, value)) { + hdd_err_rl("Invalid value %d", value); + return -EINVAL; + } + + ucfg_pmo_set_wow_data_inactivity_timeout(hdd_ctx->psoc, (uint8_t)value); + + return 0; +} + +static int hdd_we_set_tx_power(struct hdd_adapter *adapter, int value) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + mac_handle_t mac_handle = hdd_ctx->mac_handle; + QDF_STATUS status; + + if (!mac_handle) + return -EINVAL; + + status = sme_set_tx_power(mac_handle, adapter->vdev_id, + sta_ctx->conn_info.bssid, + adapter->device_mode, value); + + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("cfg set failed, value %d status %d", value, status); + + return qdf_status_to_os_return(status); +} + +static int hdd_we_set_max_tx_power(struct hdd_adapter *adapter, int value) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + mac_handle_t mac_handle = hdd_ctx->mac_handle; + QDF_STATUS status; + + if (!mac_handle) + return -EINVAL; + + status = sme_set_max_tx_power(mac_handle, + sta_ctx->conn_info.bssid, + sta_ctx->conn_info.bssid, + value); + + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("cfg set failed, value %d status %d", value, status); + + return qdf_status_to_os_return(status); +} + +static int hdd_we_set_max_tx_power_2_4(struct hdd_adapter *adapter, int power) +{ + QDF_STATUS status; + + hdd_debug("power %d dBm", power); + status = sme_set_max_tx_power_per_band(BAND_2G, power); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("cfg set failed, value %d status %d", power, status); + + return qdf_status_to_os_return(status); +} + +static int hdd_we_set_max_tx_power_5_0(struct hdd_adapter *adapter, int power) +{ + QDF_STATUS status; + + hdd_debug("power %d dBm", power); + status = sme_set_max_tx_power_per_band(BAND_5G, power); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("cfg set failed, value %d status %d", power, status); + + return qdf_status_to_os_return(status); +} + +#ifdef HASTINGS_BT_WAR + +static bool hdd_hastings_bt_war_applicable(struct hdd_context *hdd_ctx) +{ + struct pld_soc_info info; + int errno; + + errno = pld_get_soc_info(hdd_ctx->parent_dev, &info); + if (errno) + return false; + + /* check for HST 1.1 values */ + + if (info.device_version.family_number != 0x04) + return false; + + if (info.device_version.device_number != 0x0A) + return false; + + if (info.device_version.major_version != 0x01) + return false; + + return true; +} + +/* + * replicate logic: + * iwpriv wlan0 setUnitTestCmd 19 2 23 1 => enable WAR + * iwpriv wlan0 setUnitTestCmd 19 2 23 0 => disable WAR + */ + +#define HASTINGS_WAR_FW_PARAM_ID 23 + +static int hdd_hastings_bt_war_set_fw(struct hdd_context *hdd_ctx, + uint32_t value) +{ + uint32_t vdev_id = 0; /* not used */ + uint32_t module_id = WLAN_MODULE_WAL; + uint32_t arg_count = 2; + uint32_t arg[2] = {HASTINGS_WAR_FW_PARAM_ID, value}; + QDF_STATUS status; + + if (!hdd_hastings_bt_war_applicable(hdd_ctx)) + return 0; + + status = wma_form_unit_test_cmd_and_send(vdev_id, module_id, + arg_count, arg); + + return qdf_status_to_os_return(status); +} + +int hdd_hastings_bt_war_enable_fw(struct hdd_context *hdd_ctx) +{ + return hdd_hastings_bt_war_set_fw(hdd_ctx, 1); +} + +int hdd_hastings_bt_war_disable_fw(struct hdd_context *hdd_ctx) +{ + return hdd_hastings_bt_war_set_fw(hdd_ctx, 0); +} + +/* value to restore the config when the WAR is disabled */ +static uint32_t iface_change_wait_time_checkpoint; +static void checkpoint_iface_change_wait_time(struct hdd_context *hdd_ctx) +{ + struct hdd_config *config = hdd_ctx->config; + + /* did we already checkpoint a value ? */ + if (iface_change_wait_time_checkpoint) + return; + + /* checkpoint current value */ + iface_change_wait_time_checkpoint = config->iface_change_wait_time; + + /* was the timer enabled when we checkpointed? */ + if (iface_change_wait_time_checkpoint) + return; + + /* WAR was enabled at boot, use default when disable */ + iface_change_wait_time_checkpoint = + cfg_default(CFG_INTERFACE_CHANGE_WAIT); +} + +static int hdd_hastings_war_enable(struct hdd_context *hdd_ctx) +{ + struct hdd_config *config = hdd_ctx->config; + + config->iface_change_wait_time = 0; + + return hdd_hastings_bt_war_enable_fw(hdd_ctx); +} + +static int hdd_hastings_war_disable(struct hdd_context *hdd_ctx) +{ + struct hdd_config *config = hdd_ctx->config; + + config->iface_change_wait_time = iface_change_wait_time_checkpoint; + + return hdd_hastings_bt_war_disable_fw(hdd_ctx); +} + +static int hdd_we_set_hastings_bt_war(struct hdd_adapter *adapter, int enable) +{ + int errno; + struct hdd_context *hdd_ctx; + + errno = hdd_validate_adapter(adapter); + if (errno) + return errno; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (!hdd_hastings_bt_war_applicable(hdd_ctx)) + return 0; + + checkpoint_iface_change_wait_time(hdd_ctx); + + return enable ? + hdd_hastings_war_enable(hdd_ctx) : + hdd_hastings_war_disable(hdd_ctx); +} +#endif + +static int hdd_we_set_tm_level(struct hdd_adapter *adapter, int level) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle = hdd_ctx->mac_handle; + QDF_STATUS status; + + if (!mac_handle) + return -EINVAL; + + hdd_debug("Thermal Mitigation Level %d", level); + status = sme_set_thermal_level(mac_handle, level); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("cfg set failed, value %d status %d", level, status); + + return qdf_status_to_os_return(status); +} + +static int hdd_we_set_nss(struct hdd_adapter *adapter, int nss) +{ + QDF_STATUS status; + + hdd_debug("NSS %d", nss); + + if ((nss > 2) || (nss <= 0)) { + hdd_err("Invalid NSS: %d", nss); + return -EINVAL; + } + + status = hdd_update_nss(adapter, nss, nss); + if (QDF_IS_STATUS_ERROR(status)) + hdd_err("cfg set failed, value %d status %d", nss, status); + + return qdf_status_to_os_return(status); +} + +int hdd_we_set_short_gi(struct hdd_adapter *adapter, int sgi) +{ + mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; + int errno; + + hdd_debug("Short GI %d", sgi); + + if (!mac_handle) { + hdd_err("NULL Mac handle"); + return -EINVAL; + } + + if (sgi & HDD_AUTO_RATE_SGI) + errno = sme_set_auto_rate_he_sgi(mac_handle, + adapter->vdev_id, + sgi); + else + errno = sme_update_ht_config(mac_handle, + adapter->vdev_id, + WNI_CFG_HT_CAP_INFO_SHORT_GI_20MHZ, + sgi); + if (errno) + hdd_err("cfg set failed, value %d status %d", sgi, errno); + + return errno; +} + +static int hdd_we_set_rtscts(struct hdd_adapter *adapter, int rtscts) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle = hdd_ctx->mac_handle; + uint32_t value; + uint32_t rts_threshold_val; + QDF_STATUS status; + int errno; + + hdd_debug("RTSCTS %d", rtscts); + + if (!mac_handle) { + hdd_err("NULL Mac handle"); + return -EINVAL; + } + + status = ucfg_mlme_get_rts_threshold(hdd_ctx->psoc, + &rts_threshold_val); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Get rts threshold failed, status %d", status); + return -EINVAL; + } + + if ((rtscts & HDD_RTSCTS_EN_MASK) == HDD_RTSCTS_ENABLE) { + value = rts_threshold_val; + } else if (((rtscts & HDD_RTSCTS_EN_MASK) == 0) || + ((rtscts & HDD_RTSCTS_EN_MASK) == HDD_CTS_ENABLE)) { + value = cfg_max(CFG_RTS_THRESHOLD); + } else { + hdd_err_rl("Invalid value %d", rtscts); + return -EINVAL; + } + + errno = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + rtscts, VDEV_CMD); + if (errno) { + hdd_err("Failed to set firmware, errno %d", errno); + return errno; + } + + status = ucfg_mlme_set_rts_threshold(hdd_ctx->psoc, value); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Set rts threshold failed, status %d", status); + return -EINVAL; + } + + return 0; +} + +static int hdd_we_set_11n_rate(struct hdd_adapter *adapter, int rate_code) +{ + uint8_t preamble = 0, nss = 0, rix = 0; + int errno; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + enum wlan_phymode peer_phymode; + uint8_t *peer_mac = adapter->session.station.conn_info.bssid.bytes; + + hdd_debug("Rate code %d", rate_code); + + if (rate_code != 0xff) { + rix = RC_2_RATE_IDX(rate_code); + if (rate_code & 0x80) { + preamble = WMI_RATE_PREAMBLE_HT; + nss = HT_RC_2_STREAMS(rate_code) - 1; + } else { + status = ucfg_mlme_get_peer_phymode(hdd_ctx->psoc, + peer_mac, + &peer_phymode); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set rate"); + return 0; + } + if (IS_WLAN_PHYMODE_HE(peer_phymode)) { + hdd_err("Do not set legacy rate %d in HE mode", + rate_code); + return 0; + } + nss = 0; + rix = RC_2_RATE_IDX(rate_code); + if (rate_code & 0x10) { + preamble = WMI_RATE_PREAMBLE_CCK; + if (rix != 0x3) + /* Enable Short preamble + * always for CCK except 1mbps + */ + rix |= 0x4; + } else { + preamble = WMI_RATE_PREAMBLE_OFDM; + } + } + rate_code = hdd_assemble_rate_code(preamble, nss, rix); + } + + hdd_debug("WMI_VDEV_PARAM_FIXED_RATE val %d rix %d preamble %x nss %d", + rate_code, rix, preamble, nss); + + errno = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_FIXED_RATE, + rate_code, VDEV_CMD); + if (errno) + hdd_err("Failed to set firmware, errno %d", errno); + + return errno; +} + +static int hdd_we_set_vht_rate(struct hdd_adapter *adapter, int rate_code) +{ + uint8_t preamble = 0, nss = 0, rix = 0; + int errno; + + hdd_debug("Rate code %d", rate_code); + + if (rate_code != 0xff) { + rix = RC_2_RATE_IDX_11AC(rate_code); + preamble = WMI_RATE_PREAMBLE_VHT; + nss = HT_RC_2_STREAMS_11AC(rate_code) - 1; + rate_code = hdd_assemble_rate_code(preamble, nss, rix); + } + + hdd_debug("WMI_VDEV_PARAM_FIXED_RATE val %d rix %d preamble %x nss %d", + rate_code, rix, preamble, nss); + + errno = wma_cli_set_command(adapter->vdev_id, + WMI_VDEV_PARAM_FIXED_RATE, + rate_code, VDEV_CMD); + if (errno) + hdd_err("Failed to set firmware, errno %d", errno); + + return errno; +} + +static int hdd_we_set_ampdu(struct hdd_adapter *adapter, int ampdu) +{ + hdd_debug("AMPDU %d", ampdu); + + return wma_cli_set_command(adapter->vdev_id, + GEN_VDEV_PARAM_AMPDU, + ampdu, GEN_CMD); +} + +static int hdd_we_set_amsdu(struct hdd_adapter *adapter, int amsdu) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + mac_handle_t mac_handle = hdd_ctx->mac_handle; + int errno; + QDF_STATUS status; + + hdd_debug("AMSDU %d", amsdu); + + if (!mac_handle) { + hdd_err("NULL Mac handle"); + return -EINVAL; + } + + status = ucfg_mlme_set_max_amsdu_num(hdd_ctx->psoc, + amsdu); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to set Max AMSDU Num to cfg"); + return -EINVAL; + } + + if (amsdu > 1) + sme_set_amsdu(mac_handle, true); + else + sme_set_amsdu(mac_handle, false); + + errno = wma_cli_set_command(adapter->vdev_id, + GEN_VDEV_PARAM_AMSDU, + amsdu, GEN_CMD); + if (errno) { + hdd_err("Failed to set firmware, errno %d", errno); + return errno; + } + + return 0; +} + +static int hdd_we_clear_stats(struct hdd_adapter *adapter, int option) +{ + QDF_STATUS status; + + hdd_debug("option %d", option); + + switch (option) { + case CDP_HDD_STATS: + memset(&adapter->stats, 0, sizeof(adapter->stats)); + memset(&adapter->hdd_stats, 0, sizeof(adapter->hdd_stats)); + break; + case CDP_TXRX_HIST_STATS: + wlan_hdd_clear_tx_rx_histogram(adapter->hdd_ctx); + break; + case CDP_HDD_NETIF_OPER_HISTORY: + wlan_hdd_clear_netif_queue_history(adapter->hdd_ctx); + break; + case CDP_HIF_STATS: + hdd_clear_hif_stats(); + break; + case CDP_NAPI_STATS: + hdd_clear_napi_stats(); + break; + default: + status = cdp_clear_stats(cds_get_context(QDF_MODULE_ID_SOC), + OL_TXRX_PDEV_ID, + option); + if (status != QDF_STATUS_SUCCESS) + hdd_debug("Failed to dump stats for option: %d", + option); + break; + } + + return 0; +} + +static int hdd_we_set_green_tx_param(struct hdd_adapter *adapter, + green_tx_param id, + const char *id_string, + int value) +{ + int errno; + + hdd_debug("%s %d", id_string, value); + errno = wma_cli_set_command(adapter->vdev_id, id, value, GTX_CMD); + if (errno) + hdd_err("Failed to set firmware, errno %d", errno); + + return errno; +} + +#define hdd_we_set_green_tx_param(adapter, id, value) \ + hdd_we_set_green_tx_param(adapter, id, #id, value) + +static int hdd_we_set_gtx_ht_mcs(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_green_tx_param(adapter, + WMI_VDEV_PARAM_GTX_HT_MCS, + value); +} + +static int hdd_we_set_gtx_vht_mcs(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_green_tx_param(adapter, + WMI_VDEV_PARAM_GTX_VHT_MCS, + value); +} + +static int hdd_we_set_gtx_usrcfg(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_green_tx_param(adapter, + WMI_VDEV_PARAM_GTX_USR_CFG, + value); +} + +static int hdd_we_set_gtx_thre(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_green_tx_param(adapter, + WMI_VDEV_PARAM_GTX_THRE, + value); +} + +static int hdd_we_set_gtx_margin(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_green_tx_param(adapter, + WMI_VDEV_PARAM_GTX_MARGIN, + value); +} + +static int hdd_we_set_gtx_step(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_green_tx_param(adapter, + WMI_VDEV_PARAM_GTX_STEP, + value); +} + +static int hdd_we_set_gtx_mintpc(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_green_tx_param(adapter, + WMI_VDEV_PARAM_GTX_MINTPC, + value); +} + +static int hdd_we_set_gtx_bwmask(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_green_tx_param(adapter, + WMI_VDEV_PARAM_GTX_BW_MASK, + value); +} + +static int hdd_we_packet_power_save(struct hdd_adapter *adapter, + packet_power_save id, + const char *id_string, + int value) +{ + int errno; + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_err_rl("Not supported in mode %d", adapter->device_mode); + return -EINVAL; + } + + hdd_debug("%s %d", id_string, value); + errno = wma_cli_set_command(adapter->vdev_id, id, value, PPS_CMD); + if (errno) + hdd_err("Failed to set firmware, errno %d", errno); + + return errno; +} + +#define hdd_we_packet_power_save(adapter, id, value) \ + hdd_we_packet_power_save(adapter, id, #id, value) + +static int hdd_we_pps_paid_match(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_PAID_MATCH, + value); +} + +static int hdd_we_pps_gid_match(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_GID_MATCH, + value); +} + +static int hdd_we_pps_early_tim_clear(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_EARLY_TIM_CLEAR, + value); +} + +static int hdd_we_pps_early_dtim_clear(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_EARLY_DTIM_CLEAR, + value); +} + +static int hdd_we_pps_eof_pad_delim(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_EOF_PAD_DELIM, + value); +} + +static int hdd_we_pps_macaddr_mismatch(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_MACADDR_MISMATCH, + value); +} + +static int hdd_we_pps_delim_crc_fail(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_DELIM_CRC_FAIL, + value); +} + +static int hdd_we_pps_gid_nsts_zero(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_GID_NSTS_ZERO, + value); +} + +static int hdd_we_pps_rssi_check(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_RSSI_CHECK, + value); +} + +static int hdd_we_pps_5g_ebt(struct hdd_adapter *adapter, int value) +{ + return hdd_we_packet_power_save(adapter, + WMI_VDEV_PPS_5G_EBT, + value); +} + +static int hdd_we_set_qpower(struct hdd_adapter *adapter, + enum wmi_sta_powersave_param id, + const char *id_string, + int value) +{ + int errno; + + hdd_debug("%s %d", id_string, value); + errno = wma_cli_set_command(adapter->vdev_id, id, value, QPOWER_CMD); + if (errno) + hdd_err("Failed to set firmware, errno %d", errno); + + return errno; +} + +#define hdd_we_set_qpower(adapter, id, value) \ + hdd_we_set_qpower(adapter, id, #id, value) + +static int +hdd_we_set_qpower_max_pspoll_count(struct hdd_adapter *adapter, int value) +{ + enum wmi_sta_powersave_param id = + WMI_STA_PS_PARAM_QPOWER_PSPOLL_COUNT; + + return hdd_we_set_qpower(adapter, id, value); +} + +static int +hdd_we_set_qpower_max_tx_before_wake(struct hdd_adapter *adapter, int value) +{ + enum wmi_sta_powersave_param id = + WMI_STA_PS_PARAM_QPOWER_MAX_TX_BEFORE_WAKE; + + return hdd_we_set_qpower(adapter, id, value); +} + +static int +hdd_we_set_qpower_spec_pspoll_wake_interval(struct hdd_adapter *adapter, + int value) +{ + enum wmi_sta_powersave_param id = + WMI_STA_PS_PARAM_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL; + + return hdd_we_set_qpower(adapter, id, value); +} + +static int +hdd_we_set_qpower_spec_max_spec_nodata_pspoll(struct hdd_adapter *adapter, + int value) +{ + enum wmi_sta_powersave_param id = + WMI_STA_PS_PARAM_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL; + + return hdd_we_set_qpower(adapter, id, value); +} + +static int hdd_we_set_pdev(struct hdd_adapter *adapter, + WMI_PDEV_PARAM id, + const char *id_string, + int value) +{ + int errno; + + hdd_debug("%s %d", id_string, value); + errno = wma_cli_set_command(adapter->vdev_id, id, value, PDEV_CMD); + if (errno) + hdd_err("Failed to set firmware, errno %d", errno); + + return errno; +} + +#define hdd_we_set_pdev(adapter, id, value) \ + hdd_we_set_pdev(adapter, id, #id, value) + +static int hdd_we_set_ani_en_dis(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_ANI_ENABLE, + value); +} + +static int hdd_we_set_ani_poll_period(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_ANI_POLL_PERIOD, + value); +} + +static int hdd_we_set_ani_listen_period(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + value); +} + +static int hdd_we_set_ani_ofdm_level(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + value); +} + +static int hdd_we_set_ani_cck_level(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_ANI_CCK_LEVEL, + value); +} + +static int hdd_we_set_dynamic_bw(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_DYNAMIC_BW, + value); +} + +static int hdd_we_set_cts_cbw(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_CTS_CBW, + value); +} + +static int hdd_we_set_tx_chainmask(struct hdd_adapter *adapter, int value) +{ + int errno; + + errno = hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_TX_CHAIN_MASK, + value); + if (errno) + return errno; + + return hdd_set_antenna_mode(adapter, adapter->hdd_ctx, value); +} + +static int hdd_we_set_rx_chainmask(struct hdd_adapter *adapter, int value) +{ + int errno; + + errno = hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_RX_CHAIN_MASK, + value); + if (errno) + return errno; + + return hdd_set_antenna_mode(adapter, adapter->hdd_ctx, value); +} + +static int hdd_we_set_txpow_2g(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + value); +} + +static int hdd_we_set_txpow_5g(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_pdev(adapter, + WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + value); +} + +static int hdd_we_set_vdev(struct hdd_adapter *adapter, + int id, + const char *id_string, + int value) +{ + int errno; + + hdd_debug("%s %d", id_string, value); + errno = wma_cli_set_command(adapter->vdev_id, id, value, VDEV_CMD); + if (errno) + hdd_err("Failed to set firmware, errno %d", errno); + + return errno; +} + +#define hdd_we_set_vdev(adapter, id, value) \ + hdd_we_set_vdev(adapter, id, #id, value) + +static int hdd_we_set_txrx_fwstats(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_vdev(adapter, + WMA_VDEV_TXRX_FWSTATS_ENABLE_CMDID, + value); +} + +static int hdd_we_txrx_fwstats_reset(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_vdev(adapter, + WMA_VDEV_TXRX_FWSTATS_RESET_CMDID, + value); +} + +static int hdd_we_set_htsmps(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_vdev(adapter, + WMI_STA_SMPS_FORCE_MODE_CMDID, + value); +} + +static int hdd_we_set_early_rx_adjust_enable(struct hdd_adapter *adapter, + int value) +{ + if ((value != 0) && (value != 1)) + return -EINVAL; + + return hdd_we_set_vdev(adapter, + WMI_VDEV_PARAM_EARLY_RX_ADJUST_ENABLE, + value); +} + +static int hdd_we_set_early_rx_tgt_bmiss_num(struct hdd_adapter *adapter, + int value) +{ + return hdd_we_set_vdev(adapter, + WMI_VDEV_PARAM_EARLY_RX_TGT_BMISS_NUM, + value); +} + +static int hdd_we_set_early_rx_bmiss_sample_cycle(struct hdd_adapter *adapter, + int value) +{ + return hdd_we_set_vdev(adapter, + WMI_VDEV_PARAM_EARLY_RX_BMISS_SAMPLE_CYCLE, + value); +} + +static int hdd_we_set_early_rx_slop_step(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_vdev(adapter, + WMI_VDEV_PARAM_EARLY_RX_SLOP_STEP, + value); +} + +static int hdd_we_set_early_rx_init_slop(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_vdev(adapter, + WMI_VDEV_PARAM_EARLY_RX_INIT_SLOP, + value); +} + +static int hdd_we_set_early_rx_adjust_pause(struct hdd_adapter *adapter, + int value) +{ + if ((value != 0) && (value != 1)) + return -EINVAL; + + return hdd_we_set_vdev(adapter, + WMI_VDEV_PARAM_EARLY_RX_ADJUST_PAUSE, + value); +} + +static int hdd_we_set_early_rx_drift_sample(struct hdd_adapter *adapter, + int value) +{ + return hdd_we_set_vdev(adapter, + WMI_VDEV_PARAM_EARLY_RX_DRIFT_SAMPLE, + value); +} + +static int hdd_we_set_dcm(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_vdev(adapter, + WMI_VDEV_PARAM_HE_DCM, + value); +} + +static int hdd_we_set_range_ext(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_vdev(adapter, + WMI_VDEV_PARAM_HE_RANGE_EXT, + value); +} + +static int hdd_we_set_dbg(struct hdd_adapter *adapter, + int id, + const char *id_string, + int value) +{ + int errno; + + hdd_debug("%s %d", id_string, value); + errno = wma_cli_set_command(adapter->vdev_id, id, value, DBG_CMD); + if (errno) + hdd_err("Failed to set firmware, errno %d", errno); + + return errno; +} + +#define hdd_we_set_dbg(adapter, id, value) \ + hdd_we_set_dbg(adapter, id, #id, value) + +static int hdd_we_dbglog_log_level(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_dbg(adapter, + WMI_DBGLOG_LOG_LEVEL, + value); +} + +static int hdd_we_dbglog_vap_enable(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_dbg(adapter, + WMI_DBGLOG_VAP_ENABLE, + value); +} + +static int hdd_we_dbglog_vap_disable(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_dbg(adapter, + WMI_DBGLOG_VAP_DISABLE, + value); +} + +static int hdd_we_dbglog_module_enable(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_dbg(adapter, + WMI_DBGLOG_MODULE_ENABLE, + value); +} + +static int hdd_we_dbglog_module_disable(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_dbg(adapter, + WMI_DBGLOG_MODULE_DISABLE, + value); +} + +static int hdd_we_dbglog_mod_log_level(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_dbg(adapter, + WMI_DBGLOG_MOD_LOG_LEVEL, + value); +} + +static int hdd_we_dbglog_type(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_dbg(adapter, + WMI_DBGLOG_TYPE, + value); +} + +static int hdd_we_dbglog_report_enable(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_dbg(adapter, + WMI_DBGLOG_REPORT_ENABLE, + value); +} + +static int hdd_we_start_fw_profile(struct hdd_adapter *adapter, int value) +{ + return hdd_we_set_dbg(adapter, + WMI_WLAN_PROFILE_TRIGGER_CMDID, + value); +} + +static int hdd_we_set_channel(struct hdd_adapter *adapter, int channel) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + + hdd_debug("Set Channel %d Session ID %d mode %d", channel, + adapter->vdev_id, adapter->device_mode); + + if (!hdd_ctx->mac_handle) + return -EINVAL; + + switch (adapter->device_mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + /* supported */ + break; + default: + hdd_err("change channel not supported for device mode %d", + adapter->device_mode); + return -EINVAL; + } + + status = sme_ext_change_channel(hdd_ctx->mac_handle, channel, + adapter->vdev_id); + if (status != QDF_STATUS_SUCCESS) + hdd_err("Error in change channel status %d", status); + + return qdf_status_to_os_return(status); +} + +static int hdd_we_mcc_config_latency(struct hdd_adapter *adapter, int latency) +{ + hdd_debug("MCC latency %d", latency); + + wlan_hdd_set_mcc_latency(adapter, latency); + + return 0; +} + +static int hdd_we_mcc_config_quota(struct hdd_adapter *adapter, int quota) +{ + hdd_debug("MCC quota %dms", quota); + + return wlan_hdd_set_mcc_p2p_quota(adapter, quota); +} + +static int hdd_we_set_debug_log(struct hdd_adapter *adapter, int value) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (!hdd_ctx->mac_handle) + return -EINVAL; + + sme_update_connect_debug(hdd_ctx->mac_handle, value); + + return 0; +} + +static int hdd_we_set_scan_disable(struct hdd_adapter *adapter, int value) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_debug("%d", value); + + if (!hdd_ctx->psoc) + return -EINVAL; + + if (value) + ucfg_scan_psoc_set_disable(hdd_ctx->psoc, REASON_USER_SPACE); + else + ucfg_scan_psoc_set_enable(hdd_ctx->psoc, REASON_USER_SPACE); + + return 0; +} + +static int hdd_we_set_conc_system_pref(struct hdd_adapter *adapter, + int preference) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_debug("%d", preference); + + if (!hdd_ctx->psoc) + return -EINVAL; + + ucfg_policy_mgr_set_sys_pref(hdd_ctx->psoc, preference); + + return 0; +} + +static int hdd_we_set_11ax_rate(struct hdd_adapter *adapter, int rate) +{ + return hdd_set_11ax_rate(adapter, rate, NULL); +} + +static int hdd_we_set_modulated_dtim(struct hdd_adapter *adapter, int value) +{ + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_debug("%d", value); + + if (!hdd_ctx->psoc) + return -EINVAL; + + if ((value < cfg_max(CFG_PMO_ENABLE_MODULATED_DTIM)) || + (value > cfg_max(CFG_PMO_ENABLE_MODULATED_DTIM))) { + hdd_err("Invalid value %d", value); + return -EINVAL; + } + + ucfg_policy_mgr_set_sys_pref(hdd_ctx->psoc, value); + + return 0; +} + +#ifdef WLAN_FEATURE_MOTION_DETECTION +/** + * hdd_we_motion_det_start_stop - start/stop motion detection + * @adapter: hdd adapter + * @value: start/stop value to set + * + * Return: 0 on success, error on failure + */ +static int hdd_we_motion_det_start_stop(struct hdd_adapter *adapter, int value) +{ + struct sme_motion_det_en motion_det; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (value < 0 || value > 1) { + hdd_err("Invalid value %d in mt_start", value); + return -EINVAL; + } + + if (!adapter->motion_det_cfg) { + hdd_err("Motion Detection config values not available"); + return -EINVAL; + } + + if (!adapter->motion_det_baseline_value) { + hdd_err("Motion Detection Baselining not started/completed"); + return -EAGAIN; + } + + motion_det.vdev_id = adapter->vdev_id; + motion_det.enable = value; + + if (value) { + /* For motion detection start, set motion_det_in_progress */ + adapter->motion_det_in_progress = true; + } else { + /* For motion detection stop, reset motion_det_in_progress */ + adapter->motion_det_in_progress = false; + adapter->motion_detection_mode = 0; + } + + sme_motion_det_enable(hdd_ctx->mac_handle, &motion_det); + + return 0; +} + +/** + * hdd_we_motion_det_base_line_start_stop - start/stop md baselining + * @adapter: hdd adapter + * @value: start/stop value to set + * + * Return: 0 on success, error on failure + */ +static int hdd_we_motion_det_base_line_start_stop(struct hdd_adapter *adapter, + int value) +{ + struct sme_motion_det_base_line_en motion_det_base_line; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (value < 0 || value > 1) { + hdd_err("Invalid value %d in mt_bl_start", value); + return -EINVAL; + } + + /* Do not send baselining start/stop during motion detection phase */ + if (adapter->motion_det_in_progress) { + hdd_err("Motion detection still in progress, try later"); + return -EAGAIN; + } + + motion_det_base_line.vdev_id = adapter->vdev_id; + motion_det_base_line.enable = value; + sme_motion_det_base_line_enable(hdd_ctx->mac_handle, + &motion_det_base_line); + + return 0; +} +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +int wlan_hdd_set_btcoex_mode(struct hdd_adapter *adapter, int value) +{ + struct coex_config_params coex_cfg_params = {0}; + + coex_cfg_params.config_type = WMI_COEX_CONFIG_BTC_MODE; + coex_cfg_params.config_arg1 = value; + coex_cfg_params.vdev_id = adapter->vdev_id; + + if (value < cfg_min(CFG_BTC_MODE) || value > cfg_max(CFG_BTC_MODE)) { + hdd_err_rl("Invalid value %d", value); + return -EINVAL; + } + + if (QDF_IS_STATUS_ERROR(sme_send_coex_config_cmd(&coex_cfg_params))) { + hdd_err_rl("Failed to send coex BTC mode"); + return -EINVAL; + } + + return 0; +} + +int wlan_hdd_set_btcoex_rssi_threshold(struct hdd_adapter *adapter, int value) +{ + struct coex_config_params coex_cfg_params = {0}; + + coex_cfg_params.config_type = WMI_COEX_CONFIG_BT_LOW_RSSI_THRESHOLD; + coex_cfg_params.config_arg1 = value; + coex_cfg_params.vdev_id = adapter->vdev_id; + + if (value < cfg_min(CFG_WLAN_LOW_RSSI_THRESHOLD) || + value > cfg_max(CFG_WLAN_LOW_RSSI_THRESHOLD)) { + hdd_err_rl("Invalid value %d", value); + return -EINVAL; + } + + if (QDF_IS_STATUS_ERROR(sme_send_coex_config_cmd(&coex_cfg_params))) { + hdd_err_rl("Failed to send coex BTC RSSI Threshold"); + return -EINVAL; + } + return 0; +} +typedef int (*setint_getnone_fn)(struct hdd_adapter *adapter, int value); +static const setint_getnone_fn setint_getnone_cb[] = { + [WE_SET_11D_STATE] = hdd_we_set_11d_state, + [WE_SET_POWER] = hdd_we_set_power, + [WE_SET_MAX_ASSOC] = hdd_we_set_max_assoc, + [WE_SET_DATA_INACTIVITY_TO] = hdd_we_set_data_inactivity_timeout, + [WE_SET_WOW_DATA_INACTIVITY_TO] = + hdd_we_set_wow_data_inactivity_timeout, + [WE_SET_MC_RATE] = wlan_hdd_set_mc_rate, + [WE_SET_TX_POWER] = hdd_we_set_tx_power, + [WE_SET_MAX_TX_POWER] = hdd_we_set_max_tx_power, + [WE_SET_MAX_TX_POWER_2_4] = hdd_we_set_max_tx_power_2_4, + [WE_SET_MAX_TX_POWER_5_0] = hdd_we_set_max_tx_power_5_0, +#ifdef HASTINGS_BT_WAR + [WE_SET_HASTINGS_BT_WAR] = hdd_we_set_hastings_bt_war, +#endif + [WE_SET_TM_LEVEL] = hdd_we_set_tm_level, + [WE_SET_PHYMODE] = wlan_hdd_update_phymode, + [WE_SET_NSS] = hdd_we_set_nss, + [WE_SET_GTX_HT_MCS] = hdd_we_set_gtx_ht_mcs, + [WE_SET_GTX_VHT_MCS] = hdd_we_set_gtx_vht_mcs, + [WE_SET_GTX_USRCFG] = hdd_we_set_gtx_usrcfg, + [WE_SET_GTX_THRE] = hdd_we_set_gtx_thre, + [WE_SET_GTX_MARGIN] = hdd_we_set_gtx_margin, + [WE_SET_GTX_STEP] = hdd_we_set_gtx_step, + [WE_SET_GTX_MINTPC] = hdd_we_set_gtx_mintpc, + [WE_SET_GTX_BWMASK] = hdd_we_set_gtx_bwmask, + [WE_SET_LDPC] = hdd_set_ldpc, + [WE_SET_TX_STBC] = hdd_set_tx_stbc, + [WE_SET_RX_STBC] = hdd_set_rx_stbc, + [WE_SET_SHORT_GI] = hdd_we_set_short_gi, + [WE_SET_RTSCTS] = hdd_we_set_rtscts, + [WE_SET_CHWIDTH] = hdd_we_set_ch_width, + [WE_SET_ANI_EN_DIS] = hdd_we_set_ani_en_dis, + [WE_SET_ANI_POLL_PERIOD] = hdd_we_set_ani_poll_period, + [WE_SET_ANI_LISTEN_PERIOD] = hdd_we_set_ani_listen_period, + [WE_SET_ANI_OFDM_LEVEL] = hdd_we_set_ani_ofdm_level, + [WE_SET_ANI_CCK_LEVEL] = hdd_we_set_ani_cck_level, + [WE_SET_DYNAMIC_BW] = hdd_we_set_dynamic_bw, + [WE_SET_CTS_CBW] = hdd_we_set_cts_cbw, + [WE_SET_11N_RATE] = hdd_we_set_11n_rate, + [WE_SET_VHT_RATE] = hdd_we_set_vht_rate, + [WE_SET_AMPDU] = hdd_we_set_ampdu, + [WE_SET_AMSDU] = hdd_we_set_amsdu, + [WE_SET_TX_CHAINMASK] = hdd_we_set_tx_chainmask, + [WE_SET_RX_CHAINMASK] = hdd_we_set_rx_chainmask, + [WE_SET_TXPOW_2G] = hdd_we_set_txpow_2g, + [WE_SET_TXPOW_5G] = hdd_we_set_txpow_5g, + [WE_DBGLOG_LOG_LEVEL] = hdd_we_dbglog_log_level, + [WE_DBGLOG_VAP_ENABLE] = hdd_we_dbglog_vap_enable, + [WE_DBGLOG_VAP_DISABLE] = hdd_we_dbglog_vap_disable, + [WE_DBGLOG_MODULE_ENABLE] = hdd_we_dbglog_module_enable, + [WE_DBGLOG_MODULE_DISABLE] = hdd_we_dbglog_module_disable, + [WE_DBGLOG_MOD_LOG_LEVEL] = hdd_we_dbglog_mod_log_level, + [WE_DBGLOG_TYPE] = hdd_we_dbglog_type, + [WE_DBGLOG_REPORT_ENABLE] = hdd_we_dbglog_report_enable, + [WE_SET_TXRX_FWSTATS] = hdd_we_set_txrx_fwstats, + [WE_TXRX_FWSTATS_RESET] = hdd_we_txrx_fwstats_reset, + [WE_DUMP_STATS] = hdd_wlan_dump_stats, + [WE_CLEAR_STATS] = hdd_we_clear_stats, + [WE_PPS_PAID_MATCH] = hdd_we_pps_paid_match, + [WE_PPS_GID_MATCH] = hdd_we_pps_gid_match, + [WE_PPS_EARLY_TIM_CLEAR] = hdd_we_pps_early_tim_clear, + [WE_PPS_EARLY_DTIM_CLEAR] = hdd_we_pps_early_dtim_clear, + [WE_PPS_EOF_PAD_DELIM] = hdd_we_pps_eof_pad_delim, + [WE_PPS_MACADDR_MISMATCH] = hdd_we_pps_macaddr_mismatch, + [WE_PPS_DELIM_CRC_FAIL] = hdd_we_pps_delim_crc_fail, + [WE_PPS_GID_NSTS_ZERO] = hdd_we_pps_gid_nsts_zero, + [WE_PPS_RSSI_CHECK] = hdd_we_pps_rssi_check, + [WE_PPS_5G_EBT] = hdd_we_pps_5g_ebt, + [WE_SET_HTSMPS] = hdd_we_set_htsmps, + [WE_SET_QPOWER_MAX_PSPOLL_COUNT] = hdd_we_set_qpower_max_pspoll_count, + [WE_SET_QPOWER_MAX_TX_BEFORE_WAKE] = + hdd_we_set_qpower_max_tx_before_wake, + [WE_SET_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL] = + hdd_we_set_qpower_spec_pspoll_wake_interval, + [WE_SET_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL] = + hdd_we_set_qpower_spec_max_spec_nodata_pspoll, + [WE_MCC_CONFIG_LATENCY] = hdd_we_mcc_config_latency, + [WE_MCC_CONFIG_QUOTA] = hdd_we_mcc_config_quota, + [WE_SET_DEBUG_LOG] = hdd_we_set_debug_log, + [WE_SET_EARLY_RX_ADJUST_ENABLE] = hdd_we_set_early_rx_adjust_enable, + [WE_SET_EARLY_RX_TGT_BMISS_NUM] = hdd_we_set_early_rx_tgt_bmiss_num, + [WE_SET_EARLY_RX_BMISS_SAMPLE_CYCLE] = + hdd_we_set_early_rx_bmiss_sample_cycle, + [WE_SET_EARLY_RX_SLOP_STEP] = hdd_we_set_early_rx_slop_step, + [WE_SET_EARLY_RX_INIT_SLOP] = hdd_we_set_early_rx_init_slop, + [WE_SET_EARLY_RX_ADJUST_PAUSE] = hdd_we_set_early_rx_adjust_pause, + [WE_SET_EARLY_RX_DRIFT_SAMPLE] = hdd_we_set_early_rx_drift_sample, + [WE_SET_SCAN_DISABLE] = hdd_we_set_scan_disable, + [WE_START_FW_PROFILE] = hdd_we_start_fw_profile, + [WE_SET_CHANNEL] = hdd_we_set_channel, + [WE_SET_CONC_SYSTEM_PREF] = hdd_we_set_conc_system_pref, + [WE_SET_11AX_RATE] = hdd_we_set_11ax_rate, + [WE_SET_DCM] = hdd_we_set_dcm, + [WE_SET_RANGE_EXT] = hdd_we_set_range_ext, + [WE_SET_PDEV_RESET] = hdd_handle_pdev_reset, + [WE_SET_MODULATED_DTIM] = hdd_we_set_modulated_dtim, +#ifdef WLAN_FEATURE_MOTION_DETECTION + [WE_MOTION_DET_START_STOP] = hdd_we_motion_det_start_stop, + [WE_MOTION_DET_BASE_LINE_START_STOP] = + hdd_we_motion_det_base_line_start_stop, +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + [WE_SET_BTCOEX_MODE] = wlan_hdd_set_btcoex_mode, + [WE_SET_BTCOEX_RSSI_THRESHOLD] = wlan_hdd_set_btcoex_rssi_threshold, +}; + +static setint_getnone_fn hdd_get_setint_getnone_cb(int param) +{ + if (param < 0) + return NULL; + + if (param >= QDF_ARRAY_SIZE(setint_getnone_cb)) + return NULL; + + return setint_getnone_cb[param]; +} + +/** + * iw_setint_getnone() - Generic "set integer" private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_setint_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + setint_getnone_fn cb; + int *value = (int *)extra; + int sub_cmd = value[0]; + int set_value = value[1]; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (ret) + return ret; + + cb = hdd_get_setint_getnone_cb(sub_cmd); + if (!cb) { + hdd_debug("Invalid sub command %d", sub_cmd); + return -EINVAL; + } + + ret = cb(adapter, set_value); + + hdd_exit(); + + return ret; +} + +static int iw_setint_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_setint_getnone(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __iw_setnone_get_threeint() - return three value to up layer. + * + * @dev: pointer of net_device of this wireless card + * @info: meta data about Request sent + * @wrqu: include request info + * @extra: buf used for in/Output + * + * Return: execute result + */ +static int __iw_setnone_get_threeint(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; /* success */ + uint32_t *value = (int *)extra; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_enter_dev(dev); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + hdd_debug("param = %d", value[0]); + switch (value[0]) { + case WE_GET_TSF: + ret = hdd_indicate_tsf(adapter, value, 3); + break; + default: + hdd_err("Invalid IOCTL get_value command %d", value[0]); + break; + } + return ret; +} + +/** + * iw_setnone_get_threeint() - return three value to up layer. + * + * @dev: pointer of net_device of this wireless card + * @info: meta data about Request sent + * @wrqu: include request info + * @extra: buf used for in/Output + * + * Return: execute result + */ +static int iw_setnone_get_threeint(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_setnone_get_threeint(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef WLAN_UNIT_TEST +typedef uint32_t (*hdd_ut_callback)(void); + +struct hdd_ut_entry { + const hdd_ut_callback callback; + const char *name; +}; + +struct hdd_ut_entry hdd_ut_entries[] = { + { .name = "dsc", .callback = dsc_unit_test }, + { .name = "qdf_delayed_work", .callback = qdf_delayed_work_unit_test }, + { .name = "qdf_ht", .callback = qdf_ht_unit_test }, + { .name = "qdf_periodic_work", + .callback = qdf_periodic_work_unit_test }, + { .name = "qdf_ptr_hash", .callback = qdf_ptr_hash_unit_test }, + { .name = "qdf_slist", .callback = qdf_slist_unit_test }, + { .name = "qdf_talloc", .callback = qdf_talloc_unit_test }, + { .name = "qdf_tracker", .callback = qdf_tracker_unit_test }, + { .name = "qdf_types", .callback = qdf_types_unit_test }, +}; + +#define hdd_for_each_ut_entry(cursor) \ + for (cursor = hdd_ut_entries; \ + cursor < hdd_ut_entries + ARRAY_SIZE(hdd_ut_entries); \ + cursor++) + +static struct hdd_ut_entry *hdd_ut_lookup(const char *name) +{ + struct hdd_ut_entry *entry; + + hdd_for_each_ut_entry(entry) { + if (qdf_str_eq(entry->name, name)) + return entry; + } + + return NULL; +} + +static uint32_t hdd_ut_single(const struct hdd_ut_entry *entry) +{ + uint32_t errors; + + hdd_nofl_info("START: '%s'", entry->name); + + errors = entry->callback(); + if (errors) + hdd_nofl_err("FAIL: '%s' with %u errors", entry->name, errors); + else + hdd_nofl_info("PASS: '%s'", entry->name); + + return errors; +} + +static int hdd_we_unit_test(struct hdd_context *hdd_ctx, const char *name) +{ + struct hdd_ut_entry *entry; + uint32_t errors = 0; + + hdd_nofl_info("Unit tests begin"); + + if (!name || !name[0] || qdf_str_eq(name, "all")) { + hdd_for_each_ut_entry(entry) + errors += hdd_ut_single(entry); + } else { + entry = hdd_ut_lookup(name); + if (entry) + errors += hdd_ut_single(entry); + else + hdd_nofl_err("Unit test '%s' not found", name); + } + + hdd_nofl_info("Unit tests complete"); + + return errors ? -EPERM : 0; +} +#else +static int hdd_we_unit_test(struct hdd_context *hdd_ctx, const char *name) +{ + return -EOPNOTSUPP; +} +#endif /* WLAN_UNIT_TEST */ + +/** + * iw_setchar_getnone() - Generic "set string" private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_setchar_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int sub_cmd; + int ret; + char *str_arg = NULL; + struct hdd_adapter *adapter = (netdev_priv(dev)); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct iw_point s_priv_data; + bool neighbor_report_req_support = false; + + hdd_enter_dev(dev); + + if (!capable(CAP_NET_ADMIN)) { + hdd_err("permission check failed"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + /* helper function to get iwreq_data with compat handling. */ + if (hdd_priv_get_data(&s_priv_data, wrqu)) + return -EINVAL; + + /* make sure all params are correctly passed to function */ + if ((!s_priv_data.pointer) || (0 == s_priv_data.length)) + return -EINVAL; + + sub_cmd = s_priv_data.flags; + + /* ODD number is used for set, copy data using copy_from_user */ + str_arg = mem_alloc_copy_from_user_helper(s_priv_data.pointer, + s_priv_data.length); + if (!str_arg) { + hdd_err("mem_alloc_copy_from_user_helper fail"); + return -ENOMEM; + } + + hdd_debug("Received length: %d data: %s", + s_priv_data.length, str_arg); + + switch (sub_cmd) { + case WE_WOWL_ADD_PTRN: + hdd_debug("ADD_PTRN"); + if (!hdd_add_wowl_ptrn(adapter, str_arg)) + ret = -EINVAL; + break; + case WE_WOWL_DEL_PTRN: + hdd_debug("DEL_PTRN"); + if (!hdd_del_wowl_ptrn(adapter, str_arg)) + ret = -EINVAL; + break; + case WE_NEIGHBOR_REPORT_REQUEST: + { + tRrmNeighborReq request; + tRrmNeighborRspCallbackInfo callback; + bool rrm_enabled = false; + + ucfg_wlan_mlme_get_rrm_enabled(hdd_ctx->psoc, + &rrm_enabled); + + if (rrm_enabled) { + request.neighbor_report_offload = false; + request.no_ssid = + (s_priv_data.length - 1) ? false : true; + hdd_debug("Neighbor Request ssid present %d", + request.no_ssid); + if (!request.no_ssid) { + request.ssid.length = + (s_priv_data.length - 1) > + 32 ? 32 : (s_priv_data.length - 1); + qdf_mem_copy(request.ssid.ssId, + str_arg, + request.ssid.length); + } + + /* + * If 11k offload is supported by FW and enabled + * in the ini, set the offload to true + */ + if (QDF_IS_STATUS_ERROR( + ucfg_fwol_is_neighbor_report_req_supported( + hdd_ctx->psoc, &neighbor_report_req_support))) + hdd_err("Neighbor report req bit get fail"); + + if (hdd_ctx->config->is_11k_offload_supported && + neighbor_report_req_support) { + hdd_debug("Neighbor report offloaded to FW"); + request.neighbor_report_offload = true; + } + + callback.neighborRspCallback = NULL; + callback.neighborRspCallbackContext = NULL; + callback.timeout = 5000; /* 5 seconds */ + sme_neighbor_report_request( + hdd_ctx->mac_handle, + adapter->vdev_id, + &request, + &callback); + } else { + hdd_err("Ignoring neighbor request as RRM not enabled"); + ret = -EINVAL; + } + } + break; + case WE_SET_AP_WPS_IE: + hdd_debug("Received WE_SET_AP_WPS_IE, won't process"); + break; + case WE_UNIT_TEST: + ret = hdd_we_unit_test(hdd_ctx, str_arg); + break; + default: + { + hdd_err("Invalid sub command %d", sub_cmd); + ret = -EINVAL; + break; + } + } + + qdf_mem_free(str_arg); + hdd_exit(); + + return ret; +} + +static int iw_setchar_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_setchar_getnone(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_setnone_getint() - Generic "get integer" private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_setnone_getint(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + mac_handle_t mac_handle; + int *value = (int *)extra; + int ret; + struct sme_config_params *sme_config; + struct hdd_context *hdd_ctx; + QDF_STATUS status; + bool bval = false; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + if (!sme_config) { + hdd_err("failed to allocate memory for sme_config"); + return -ENOMEM; + } + + mac_handle = hdd_ctx->mac_handle; + switch (value[0]) { + case WE_GET_11D_STATE: + { + status = ucfg_mlme_is_11d_enabled(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Invalid 11d_enable flag"); + *value = bval; + hdd_debug("11D state=%d!!", *value); + + break; + } + + case WE_GET_WLAN_DBG: + { + qdf_trace_display(); + *value = 0; + break; + } + case WE_GET_MAX_ASSOC: + { + if (ucfg_mlme_get_assoc_sta_limit(hdd_ctx->psoc, value) != + QDF_STATUS_SUCCESS) { + hdd_err("CFG_ASSOC_STA_LIMIT failed"); + ret = -EIO; + } + + break; + } + + case WE_GET_CONCURRENCY_MODE: + { + *value = policy_mgr_get_concurrency_mode(hdd_ctx->psoc); + + hdd_debug("concurrency mode=%d", *value); + break; + } + + case WE_GET_NSS: + { + sme_get_config_param(mac_handle, sme_config); + status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("unable to get vht_enable2x2"); + *value = (bval == 0) ? 1 : 2; + if (!policy_mgr_is_hw_dbs_2x2_capable(hdd_ctx->psoc) && + policy_mgr_is_current_hwmode_dbs(hdd_ctx->psoc)) + *value = *value - 1; + + hdd_debug("GET_NSS: Current NSS:%d", *value); + break; + } + + case WE_GET_GTX_HT_MCS: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_HT_MCS"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_HT_MCS, + GTX_CMD); + break; + } + + case WE_GET_GTX_VHT_MCS: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_VHT_MCS"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_VHT_MCS, + GTX_CMD); + break; + } + + case WE_GET_GTX_USRCFG: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_USR_CFG"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_USR_CFG, + GTX_CMD); + break; + } + + case WE_GET_GTX_THRE: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_THRE"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_THRE, + GTX_CMD); + break; + } + + case WE_GET_GTX_MARGIN: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_MARGIN"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_MARGIN, + GTX_CMD); + break; + } + + case WE_GET_GTX_STEP: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_STEP"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_STEP, + GTX_CMD); + break; + } + + case WE_GET_GTX_MINTPC: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_MINTPC"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_MINTPC, + GTX_CMD); + break; + } + + case WE_GET_GTX_BWMASK: + { + hdd_debug("GET WMI_VDEV_PARAM_GTX_BW_MASK"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_GTX_BW_MASK, + GTX_CMD); + break; + } + + case WE_GET_LDPC: + { + ret = hdd_get_ldpc(adapter, value); + break; + } + + case WE_GET_TX_STBC: + { + ret = hdd_get_tx_stbc(adapter, value); + break; + } + + case WE_GET_RX_STBC: + { + ret = hdd_get_rx_stbc(adapter, value); + break; + } + + case WE_GET_SHORT_GI: + { + hdd_debug("GET WMI_VDEV_PARAM_SGI"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_SGI, + VDEV_CMD); + break; + } + + case WE_GET_RTSCTS: + { + hdd_debug("GET WMI_VDEV_PARAM_ENABLE_RTSCTS"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + VDEV_CMD); + break; + } + + case WE_GET_CHWIDTH: + { + hdd_debug("GET WMI_VDEV_PARAM_CHWIDTH"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_CHWIDTH, + VDEV_CMD); + break; + } + + case WE_GET_ANI_EN_DIS: + { + hdd_debug("GET WMI_PDEV_PARAM_ANI_ENABLE"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANI_ENABLE, + PDEV_CMD); + break; + } + + case WE_GET_ANI_POLL_PERIOD: + { + hdd_debug("GET WMI_PDEV_PARAM_ANI_POLL_PERIOD"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANI_POLL_PERIOD, + PDEV_CMD); + break; + } + + case WE_GET_ANI_LISTEN_PERIOD: + { + hdd_debug("GET WMI_PDEV_PARAM_ANI_LISTEN_PERIOD"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + PDEV_CMD); + break; + } + + case WE_GET_ANI_OFDM_LEVEL: + { + hdd_debug("GET WMI_PDEV_PARAM_ANI_OFDM_LEVEL"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + PDEV_CMD); + break; + } + + case WE_GET_ANI_CCK_LEVEL: + { + hdd_debug("GET WMI_PDEV_PARAM_ANI_CCK_LEVEL"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_ANI_CCK_LEVEL, + PDEV_CMD); + break; + } + + case WE_GET_DYNAMIC_BW: + { + hdd_debug("GET WMI_PDEV_PARAM_ANI_CCK_LEVEL"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_DYNAMIC_BW, + PDEV_CMD); + break; + } + + case WE_GET_11N_RATE: + { + hdd_debug("GET WMI_VDEV_PARAM_FIXED_RATE"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_FIXED_RATE, + VDEV_CMD); + break; + } + + case WE_GET_AMPDU: + { + hdd_debug("GET AMPDU"); + *value = wma_cli_get_command(adapter->vdev_id, + GEN_VDEV_PARAM_AMPDU, + GEN_CMD); + break; + } + + case WE_GET_AMSDU: + { + hdd_debug("GET AMSDU"); + *value = wma_cli_get_command(adapter->vdev_id, + GEN_VDEV_PARAM_AMSDU, + GEN_CMD); + break; + } + + case WE_GET_ROAM_SYNCH_DELAY: + { + hdd_debug("GET ROAM SYNCH DELAY"); + *value = wma_cli_get_command(adapter->vdev_id, + GEN_VDEV_ROAM_SYNCH_DELAY, + GEN_CMD); + break; + } + + case WE_GET_TX_CHAINMASK: + { + hdd_debug("GET WMI_PDEV_PARAM_TX_CHAIN_MASK"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_TX_CHAIN_MASK, + PDEV_CMD); + break; + } + + case WE_GET_RX_CHAINMASK: + { + hdd_debug("GET WMI_PDEV_PARAM_RX_CHAIN_MASK"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_RX_CHAIN_MASK, + PDEV_CMD); + break; + } + + case WE_GET_TXPOW_2G: + { + uint8_t txpow2g = 0; + + hdd_debug("GET WMI_PDEV_PARAM_TXPOWER_LIMIT2G"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + PDEV_CMD); + ucfg_mlme_get_current_tx_power_level(hdd_ctx->psoc, &txpow2g); + hdd_debug("2G tx_power %d", txpow2g); + break; + } + + case WE_GET_TXPOW_5G: + { + uint8_t txpow5g = 0; + + hdd_debug("GET WMI_PDEV_PARAM_TXPOWER_LIMIT5G"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + PDEV_CMD); + ucfg_mlme_get_current_tx_power_level(hdd_ctx->psoc, &txpow5g); + hdd_debug("5G tx_power %d", txpow5g); + break; + } + + case WE_GET_PPS_PAID_MATCH: + { + hdd_debug("GET WMI_VDEV_PPS_PAID_MATCH"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PPS_PAID_MATCH, + PPS_CMD); + break; + } + + case WE_GET_PPS_GID_MATCH: + { + hdd_debug("GET WMI_VDEV_PPS_GID_MATCH"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PPS_GID_MATCH, + PPS_CMD); + break; + } + + case WE_GET_PPS_EARLY_TIM_CLEAR: + { + hdd_debug("GET WMI_VDEV_PPS_EARLY_TIM_CLEAR"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PPS_EARLY_TIM_CLEAR, + PPS_CMD); + break; + } + + case WE_GET_PPS_EARLY_DTIM_CLEAR: + { + hdd_debug("GET WMI_VDEV_PPS_EARLY_DTIM_CLEAR"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PPS_EARLY_DTIM_CLEAR, + PPS_CMD); + break; + } + + case WE_GET_PPS_EOF_PAD_DELIM: + { + hdd_debug("GET WMI_VDEV_PPS_EOF_PAD_DELIM"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PPS_EOF_PAD_DELIM, + PPS_CMD); + break; + } + + case WE_GET_PPS_MACADDR_MISMATCH: + { + hdd_debug("GET WMI_VDEV_PPS_MACADDR_MISMATCH"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PPS_MACADDR_MISMATCH, + PPS_CMD); + break; + } + + case WE_GET_PPS_DELIM_CRC_FAIL: + { + hdd_debug("GET WMI_VDEV_PPS_DELIM_CRC_FAIL"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PPS_DELIM_CRC_FAIL, + PPS_CMD); + break; + } + + case WE_GET_PPS_GID_NSTS_ZERO: + { + hdd_debug("GET WMI_VDEV_PPS_GID_NSTS_ZERO"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PPS_GID_NSTS_ZERO, + PPS_CMD); + break; + } + + case WE_GET_PPS_RSSI_CHECK: + { + + hdd_debug("GET WMI_VDEV_PPS_RSSI_CHECK"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PPS_RSSI_CHECK, + PPS_CMD); + break; + } + + case WE_GET_QPOWER_MAX_PSPOLL_COUNT: + { + hdd_debug("WE_GET_QPOWER_MAX_PSPOLL_COUNT"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_STA_PS_PARAM_QPOWER_PSPOLL_COUNT, + QPOWER_CMD); + break; + } + + case WE_GET_QPOWER_MAX_TX_BEFORE_WAKE: + { + hdd_debug("WE_GET_QPOWER_MAX_TX_BEFORE_WAKE"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_STA_PS_PARAM_QPOWER_MAX_TX_BEFORE_WAKE, + QPOWER_CMD); + break; + } + + case WE_GET_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL: + { + hdd_debug("WE_GET_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_STA_PS_PARAM_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL, + QPOWER_CMD); + break; + } + + case WE_GET_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL: + { + hdd_debug("WE_GET_QPOWER_MAX_PSPOLL_COUNT"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_STA_PS_PARAM_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL, + QPOWER_CMD); + break; + } + case WE_CAP_TSF: + ret = hdd_capture_tsf(adapter, (uint32_t *)value, 1); + break; + case WE_GET_TEMPERATURE: + { + hdd_debug("WE_GET_TEMPERATURE"); + ret = wlan_hdd_get_temperature(adapter, value); + break; + } + case WE_GET_DCM: + hdd_debug("GET WMI_VDEV_PARAM_HE_DCM"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_HE_DCM, + VDEV_CMD); + break; + case WE_GET_RANGE_EXT: + hdd_debug("GET WMI_VDEV_PARAM_HE_RANGE_EXT"); + *value = wma_cli_get_command(adapter->vdev_id, + WMI_VDEV_PARAM_HE_RANGE_EXT, + VDEV_CMD); + break; + default: + { + hdd_err("Invalid IOCTL get_value command %d", + value[0]); + break; + } + } + hdd_exit(); + qdf_mem_free(sme_config); + return ret; +} + +static int iw_setnone_getint(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_setnone_getint(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int hdd_set_fwtest(int argc, int cmd, int value) +{ + struct set_fwtest_params *fw_test; + + /* check for max number of arguments */ + if (argc > (WMA_MAX_NUM_ARGS) || + argc != HDD_FWTEST_PARAMS) { + hdd_err("Too Many args %d", argc); + return -EINVAL; + } + /* + * check if number of arguments are 3 then, check + * then set the default value for sounding interval. + */ + if (HDD_FWTEST_PARAMS == argc) { + if (HDD_FWTEST_SU_PARAM_ID == cmd && 0 == value) + value = HDD_FWTEST_SU_DEFAULT_VALUE; + if (HDD_FWTEST_MU_PARAM_ID == cmd && 0 == value) + value = HDD_FWTEST_MU_DEFAULT_VALUE; + } + /* check sounding interval value should not exceed to max */ + if (value > HDD_FWTEST_MAX_VALUE) { + hdd_err("Invalid arguments value should not exceed max: %d", + value); + return -EINVAL; + } + fw_test = qdf_mem_malloc(sizeof(*fw_test)); + if (!fw_test) { + hdd_err("qdf_mem_malloc failed for fw_test"); + return -ENOMEM; + } + fw_test->arg = cmd; + fw_test->value = value; + if (QDF_STATUS_SUCCESS != sme_set_fw_test(fw_test)) { + qdf_mem_free(fw_test); + hdd_err("Not able to post FW_TEST_CMD message to WMA"); + return -EINVAL; + } + return 0; +} + +/** + * iw_set_three_ints_getnone() - Generic "set 3 params" private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_set_three_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int *value = (int *)extra; + int sub_cmd = value[0]; + int ret; + uint8_t dual_mac_feature = DISABLE_DBS_CXN_AND_SCAN; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + + hdd_enter_dev(dev); + + if (!capable(CAP_NET_ADMIN)) { + hdd_err("permission check failed"); + return -EPERM; + } + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + switch (sub_cmd) { + + case WE_SET_WLAN_DBG: + qdf_print_set_category_verbose(qdf_get_pidx(), value[1], + value[2], value[3]); + break; + case WE_SET_DP_TRACE: + qdf_dp_trace_set_value(value[1], value[2], value[3]); + break; + + case WE_SET_DUAL_MAC_SCAN_CONFIG: + hdd_debug("Ioctl to set dual mac scan config"); + status = + ucfg_policy_mgr_get_dual_mac_feature(hdd_ctx->psoc, + &dual_mac_feature); + if (status != QDF_STATUS_SUCCESS) + hdd_err("can't get dual mac feature val, use def"); + if (dual_mac_feature == DISABLE_DBS_CXN_AND_SCAN) { + hdd_err("Dual mac feature is disabled from INI"); + return -EPERM; + } + hdd_debug("%d %d %d", value[1], value[2], value[3]); + policy_mgr_set_dual_mac_scan_config(hdd_ctx->psoc, + value[1], value[2], value[3]); + break; + case WE_SET_FW_TEST: + { + ret = hdd_set_fwtest(value[1], value[2], value[3]); + if (ret) { + hdd_err("Not able to set fwtest %d", ret); + return ret; + } + } + break; + default: + hdd_err("Invalid IOCTL command %d", sub_cmd); + break; + + } + hdd_exit(); + return ret; +} + +int iw_set_three_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_three_ints_getnone(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * hdd_connection_state_string() - Get connection state string + * @connection_state: enum to be converted to a string + * + * Return: the string equivalent of @connection_state + */ +static const char * +hdd_connection_state_string(eConnectionState connection_state) +{ + switch (connection_state) { + CASE_RETURN_STRING(eConnectionState_NotConnected); + CASE_RETURN_STRING(eConnectionState_Connecting); + CASE_RETURN_STRING(eConnectionState_Associated); + CASE_RETURN_STRING(eConnectionState_IbssDisconnected); + CASE_RETURN_STRING(eConnectionState_IbssConnected); + CASE_RETURN_STRING(eConnectionState_Disconnecting); + default: + return "UNKNOWN"; + } +} + +#if defined(FEATURE_OEM_DATA_SUPPORT) +/** + * iw_get_oem_data_cap_wrapper() - wrapper function to call legacy or new + * wifi_pos api to get oem data caps + * @dev: net device upon which the request was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl data payload + * + * Return: 0 for success, negative errno value on failure + */ +static inline int iw_get_oem_data_cap_wrapper(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + return iw_get_oem_data_cap(dev, info, wrqu, extra); +} +#elif defined(WIFI_POS_CONVERGED) +static inline int iw_get_oem_data_cap_wrapper(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + return os_if_wifi_pos_populate_caps(hdd_ctx->psoc, + (struct wifi_pos_driver_caps *)extra); +} +#else +static inline int iw_get_oem_data_cap_wrapper(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + return -ENOTSUPP; +} +#endif + +#ifdef WLAN_UNIT_TEST +static int hdd_get_sta_cxn_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + char *extra) +{ + QDF_STATUS status; + + status = sme_get_sta_cxn_info(hdd_ctx->mac_handle, adapter->vdev_id, + extra, WE_MAX_STR_LEN); + if (status != QDF_STATUS_SUCCESS) + qdf_scnprintf(extra, WE_MAX_STR_LEN, + "\nNo active connection"); + + return 0; +} +#else +static int hdd_get_sta_cxn_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + char *extra) +{ + qdf_scnprintf(extra, WE_MAX_STR_LEN, + "\nNot supported"); + return -ENOTSUPP; +} +#endif + +/** + * iw_get_char_setnone() - Generic "get string" private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_get_char_setnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int sub_cmd = wrqu->data.flags; + struct hdd_context *hdd_ctx; + mac_handle_t mac_handle; + int ret; + QDF_STATUS status; + uint8_t value; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + mac_handle = hdd_ctx->mac_handle; + switch (sub_cmd) { + case WE_WLAN_VERSION: + { + wrqu->data.length = hdd_wlan_get_version(hdd_ctx, + WE_MAX_STR_LEN, extra); + break; + } + + case WE_GET_STATS: + { + hdd_wlan_get_stats(adapter, &(wrqu->data.length), + extra, WE_MAX_STR_LEN); + break; + } + + case WE_GET_SUSPEND_RESUME_STATS: + { + ret = wlan_hdd_write_suspend_resume_stats(hdd_ctx, extra, + WE_MAX_STR_LEN); + if (ret >= 0) { + wrqu->data.length = ret; + ret = 0; + } + + break; + } + + case WE_LIST_FW_PROFILE: + hdd_wlan_list_fw_profile(&(wrqu->data.length), + extra, WE_MAX_STR_LEN); + break; + + /* The case prints the current state of the HDD, SME, CSR, PE, + * TL it can be extended for WDI Global State as well. And + * currently it only checks P2P_CLIENT adapter. P2P_DEVICE + * and P2P_GO have not been added as of now. + */ + case WE_GET_STATES: + { + int buf = 0, len = 0; + int adapter_num = 0; + int count = 0, check = 1; + + struct hdd_station_ctx *sta_ctx = NULL; + + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + struct hdd_adapter *stat_adapter = NULL; + + /* Print wlan0 or p2p0 states based on the adapter_num + * by using the correct adapter + */ + while (adapter_num < 2) { + if (WLAN_ADAPTER == adapter_num) { + stat_adapter = adapter; + buf = + scnprintf(extra + len, + WE_MAX_STR_LEN - len, + "\n\n wlan0 States:-"); + len += buf; + } else if (P2P_ADAPTER == adapter_num) { + buf = + scnprintf(extra + len, + WE_MAX_STR_LEN - len, + "\n\n p2p0 States:-"); + len += buf; + + if (!hdd_ctx) { + buf = + scnprintf(extra + len, + WE_MAX_STR_LEN - + len, + "\n hdd_ctx is NULL"); + len += buf; + break; + } + + /* Printing p2p0 states only in the + * case when the device is configured + * as a p2p_client + */ + stat_adapter = + hdd_get_adapter(hdd_ctx, + QDF_P2P_CLIENT_MODE); + if (!stat_adapter) { + buf = + scnprintf(extra + len, + WE_MAX_STR_LEN - + len, + "\n Device not configured as P2P_CLIENT."); + len += buf; + break; + } + } + + if (!mac_handle) { + buf = scnprintf(extra + len, + WE_MAX_STR_LEN - len, + "\n mac_handle is NULL"); + len += buf; + break; + } + sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(stat_adapter); + + + buf = + scnprintf(extra + len, WE_MAX_STR_LEN - len, + "\n HDD Conn State - %s " + "\n\n SME State:" + "\n Neighbour Roam State - %s" + "\n CSR State - %s" + "\n CSR Substate - %s", + hdd_connection_state_string + (sta_ctx->conn_info.conn_state), + mac_trace_get_neighbour_roam_state + (sme_get_neighbor_roam_state + (mac_handle, stat_adapter->vdev_id)), + mac_trace_getcsr_roam_state + (sme_get_current_roam_state + (mac_handle, stat_adapter->vdev_id)), + mac_trace_getcsr_roam_sub_state + (sme_get_current_roam_sub_state + (mac_handle, stat_adapter->vdev_id)) + ); + len += buf; + adapter_num++; + } + + if (mac_handle) { + /* Printing Lim State starting with global lim states */ + buf = + scnprintf(extra + len, WE_MAX_STR_LEN - len, + "\n\n LIM STATES:-" + "\n Global Sme State - %s " + "\n Global mlm State - %s " "\n", + mac_trace_get_lim_sme_state + (sme_get_lim_sme_state(mac_handle)), + mac_trace_get_lim_mlm_state + (sme_get_lim_mlm_state(mac_handle)) + ); + len += buf; + + while (check < 3 && count < 255) { + if (sme_is_lim_session_valid(mac_handle, count)) { + buf = + scnprintf(extra + len, + WE_MAX_STR_LEN - + len, + "\n Lim Valid Session %d:-" + "\n PE Sme State - %s " + "\n PE Mlm State - %s " + "\n", check, + mac_trace_get_lim_sme_state + (sme_get_lim_sme_session_state + (mac_handle, count)), + mac_trace_get_lim_mlm_state + (sme_get_lim_mlm_session_state + (mac_handle, count)) + ); + + len += buf; + check++; + } + count++; + } + } + + wrqu->data.length = strlen(extra) + 1; + break; + } + + case WE_GET_CFG: + { + hdd_debug("Printing CLD global INI Config"); + hdd_cfg_get_global_config(WLAN_HDD_GET_CTX(adapter), + extra, + QCSAP_IOCTL_MAX_STR_LEN); + wrqu->data.length = strlen(extra) + 1; + break; + } + case WE_GET_RSSI: + { + int8_t s7Rssi = 0; + + wlan_hdd_get_rssi(adapter, &s7Rssi); + snprintf(extra, WE_MAX_STR_LEN, "rssi=%d", s7Rssi); + wrqu->data.length = strlen(extra) + 1; + break; + } + + case WE_GET_WMM_STATUS: + { + snprintf(extra, WE_MAX_STR_LEN, + "\nDir: 0=up, 1=down, 3=both\n" + "|------------------------|\n" + "|AC | ACM |Admitted| Dir |\n" + "|------------------------|\n" + "|VO | %d | %3s | %d |\n" + "|VI | %d | %3s | %d |\n" + "|BE | %d | %3s | %d |\n" + "|BK | %d | %3s | %d |\n" + "|------------------------|\n", + adapter->hdd_wmm_status. + ac_status[SME_AC_VO].is_access_required, + adapter->hdd_wmm_status. + ac_status[SME_AC_VO]. + is_access_allowed ? "YES" : "NO", + adapter->hdd_wmm_status. + ac_status[SME_AC_VO].tspec. + ts_info.direction, + adapter->hdd_wmm_status. + ac_status[SME_AC_VI].is_access_required, + adapter->hdd_wmm_status. + ac_status[SME_AC_VI]. + is_access_allowed ? "YES" : "NO", + adapter->hdd_wmm_status. + ac_status[SME_AC_VI].tspec. + ts_info.direction, + adapter->hdd_wmm_status. + ac_status[SME_AC_BE].is_access_required, + adapter->hdd_wmm_status. + ac_status[SME_AC_BE]. + is_access_allowed ? "YES" : "NO", + adapter->hdd_wmm_status. + ac_status[SME_AC_BE].tspec. + ts_info.direction, + adapter->hdd_wmm_status. + ac_status[SME_AC_BK].is_access_required, + adapter->hdd_wmm_status. + ac_status[SME_AC_BK]. + is_access_allowed ? "YES" : "NO", + adapter->hdd_wmm_status. + ac_status[SME_AC_BK].tspec. + ts_info.direction); + + wrqu->data.length = strlen(extra) + 1; + break; + } + + case WE_GET_BA_AGEING_TIMEOUT: + { + uint32_t i; + enum qca_wlan_ac_type duration[QCA_WLAN_AC_ALL]; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!soc) { + hdd_err("Invalid SOC handle"); + break; + } + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) + cdp_get_ba_timeout(soc, i, &duration[i]); + + snprintf(extra, WE_MAX_STR_LEN, + "\n|------------------------------|\n" + "|AC | BA aging timeout duration |\n" + "|--------------------------------|\n" + "|VO | %d |\n" + "|VI | %d |\n" + "|BK | %d |\n" + "|BE | %d |\n" + "|--------------------------------|\n", + duration[QCA_WLAN_AC_VO], duration[QCA_WLAN_AC_VI], + duration[QCA_WLAN_AC_BK], duration[QCA_WLAN_AC_BE]); + + wrqu->data.length = strlen(extra) + 1; + break; + } + + case WE_GET_CHANNEL_LIST: + { + if (0 != + iw_get_channel_list_with_cc(dev, mac_handle, + info, wrqu, extra)) + return -EINVAL; + break; + } +#ifdef FEATURE_WLAN_TDLS + case WE_GET_TDLS_PEERS: + { + wrqu->data.length = + wlan_hdd_tdls_get_all_peers(adapter, extra, + WE_MAX_STR_LEN) + 1; + break; + } +#endif +#ifdef WLAN_FEATURE_11W + case WE_GET_11W_INFO: + { + struct csr_roam_profile *roam_profile = + hdd_roam_profile(adapter); + + hdd_debug("WE_GET_11W_ENABLED = %d", + roam_profile->MFPEnabled); + + snprintf(extra, WE_MAX_STR_LEN, + "\n BSSID %02X:%02X:%02X:%02X:%02X:%02X, Is PMF Assoc? %d" + "\n Number of Unprotected Disassocs %d" + "\n Number of Unprotected Deauths %d", + roam_profile->BSSIDs.bssid->bytes[0], + roam_profile->BSSIDs.bssid->bytes[1], + roam_profile->BSSIDs.bssid->bytes[2], + roam_profile->BSSIDs.bssid->bytes[3], + roam_profile->BSSIDs.bssid->bytes[4], + roam_profile->BSSIDs.bssid->bytes[5], + roam_profile->MFPEnabled, + adapter->hdd_stats.hdd_pmf_stats. + num_unprot_disassoc_rx, + adapter->hdd_stats.hdd_pmf_stats. + num_unprot_deauth_rx); + + wrqu->data.length = strlen(extra) + 1; + break; + } +#endif + case WE_GET_IBSS_STA_INFO: + { + struct hdd_station_ctx *sta_ctx = + WLAN_HDD_GET_STATION_CTX_PTR(adapter); + int idx = 0; + int length = 0, buf = 0; + + for (idx = 0; idx < MAX_PEERS; idx++) { + if (!hdd_is_valid_mac_address( + sta_ctx->conn_info.peer_macaddr[idx].bytes)) + continue; + + buf = snprintf + ((extra + length), + WE_MAX_STR_LEN - length, + "\n" QDF_FULL_MAC_FMT "\n", + QDF_FULL_MAC_REF(sta_ctx->conn_info. + peer_macaddr[idx].bytes)); + length += buf; + } + wrqu->data.length = strlen(extra) + 1; + break; + } + case WE_GET_PHYMODE: + { + bool ch_bond24 = false, ch_bond5g = false; + struct hdd_context *hddctx = WLAN_HDD_GET_CTX(adapter); + eCsrPhyMode phymode; + enum band_info current_band; + struct sme_config_params *sme_config; + + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + if (!sme_config) { + hdd_err("Out of memory"); + ret = -ENOMEM; + break; + } + + sme_get_config_param(mac_handle, sme_config); + if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE != + sme_config->csr_config.channelBondingMode24GHz) + ch_bond24 = true; + + if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE != + sme_config->csr_config.channelBondingMode5GHz) + ch_bond5g = true; + + qdf_mem_free(sme_config); + + phymode = sme_get_phy_mode(mac_handle); + if ((QDF_STATUS_SUCCESS != + ucfg_reg_get_band(hddctx->pdev, ¤t_band))) { + hdd_err("Failed to get current band config"); + return -EIO; + } + + switch (phymode) { + case eCSR_DOT11_MODE_AUTO: + snprintf(extra, WE_MAX_STR_LEN, "AUTO MODE"); + break; + case eCSR_DOT11_MODE_11n: + case eCSR_DOT11_MODE_11n_ONLY: + if (current_band == BAND_2G) { + if (ch_bond24) + snprintf(extra, WE_MAX_STR_LEN, + "11NGHT40"); + else + snprintf(extra, WE_MAX_STR_LEN, + "11NGHT20"); + } else if (current_band == BAND_5G) { + if (ch_bond5g) + snprintf(extra, WE_MAX_STR_LEN, + "11NAHT40"); + else + snprintf(extra, WE_MAX_STR_LEN, + "11NAHT20"); + } else { + snprintf(extra, WE_MAX_STR_LEN, "11N"); + } + break; + case eCSR_DOT11_MODE_abg: + snprintf(extra, WE_MAX_STR_LEN, "11ABG"); + break; + case eCSR_DOT11_MODE_11a: + snprintf(extra, WE_MAX_STR_LEN, "11A"); + break; + case eCSR_DOT11_MODE_11b: + case eCSR_DOT11_MODE_11b_ONLY: + snprintf(extra, WE_MAX_STR_LEN, "11B"); + break; + case eCSR_DOT11_MODE_11g: + case eCSR_DOT11_MODE_11g_ONLY: + snprintf(extra, WE_MAX_STR_LEN, "11G"); + break; + case eCSR_DOT11_MODE_11ac: + case eCSR_DOT11_MODE_11ac_ONLY: + status = + ucfg_mlme_get_vht_channel_width(hddctx->psoc, + &value); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to set channel_width"); + if (value == eHT_CHANNEL_WIDTH_20MHZ) + snprintf(extra, WE_MAX_STR_LEN, + "11ACVHT20"); + else if (value == eHT_CHANNEL_WIDTH_40MHZ) + snprintf(extra, WE_MAX_STR_LEN, + "11ACVHT40"); + else if (value == eHT_CHANNEL_WIDTH_80MHZ) + snprintf(extra, WE_MAX_STR_LEN, + "11ACVHT80"); + else if (value == eHT_CHANNEL_WIDTH_160MHZ) + snprintf(extra, WE_MAX_STR_LEN, + "11ACVHT160"); + break; + case eCSR_DOT11_MODE_11ax: + case eCSR_DOT11_MODE_11ax_ONLY: + status = + ucfg_mlme_get_vht_channel_width(hddctx->psoc, + &value); + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to set channel_width"); + + /* currently using vhtChannelWidth */ + if (value == eHT_CHANNEL_WIDTH_20MHZ) + snprintf(extra, WE_MAX_STR_LEN, + "11AX_HE_20"); + else if (value == eHT_CHANNEL_WIDTH_40MHZ) + snprintf(extra, WE_MAX_STR_LEN, + "11AX_HE_40"); + else if (value == eHT_CHANNEL_WIDTH_80MHZ) + snprintf(extra, WE_MAX_STR_LEN, + "11AX_HE_80"); + else if (value == eHT_CHANNEL_WIDTH_160MHZ) + snprintf(extra, WE_MAX_STR_LEN, + "11AX_HE_160"); + break; + } + + wrqu->data.length = strlen(extra) + 1; + break; + } + + case WE_GET_OEM_DATA_CAP: + return iw_get_oem_data_cap_wrapper(dev, info, wrqu, extra); + case WE_GET_SNR: + { + int8_t s7snr = 0; + int status = 0; + bool enable_snr_monitoring; + struct hdd_context *hdd_ctx; + struct hdd_station_ctx *sta_ctx; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + status = wlan_hdd_validate_context(hdd_ctx); + if (status) + return status; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + enable_snr_monitoring = + ucfg_scan_is_snr_monitor_enabled(hdd_ctx->psoc); + if (!enable_snr_monitoring || + eConnectionState_Associated != + sta_ctx->conn_info.conn_state) { + hdd_err("getSNR failed: Enable SNR Monitoring-%d, ConnectionState-%d", + enable_snr_monitoring, + sta_ctx->conn_info.conn_state); + return -ENONET; + } + wlan_hdd_get_snr(adapter, &s7snr); + snprintf(extra, WE_MAX_STR_LEN, "snr=%d", s7snr); + wrqu->data.length = strlen(extra) + 1; + break; + } + + case WE_GET_STA_CXN_INFO: + ret = hdd_get_sta_cxn_info(hdd_ctx, adapter, extra); + wrqu->data.length = strlen(extra) + 1; + break; + + default: + hdd_err("Invalid IOCTL command %d", sub_cmd); + break; + } + + hdd_exit(); + return ret; +} + +static int iw_get_char_setnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_get_char_setnone(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_setnone_getnone() - Generic "action" private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_setnone_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + mac_handle_t mac_handle; + int ret; + int sub_cmd; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + +#ifdef CONFIG_COMPAT + /* this ioctl is a special case where a sub-ioctl is used and both + * the number of get and set args is 0. in this specific case the + * logic in iwpriv places the sub_cmd in the data.flags portion of + * the iwreq. unfortunately the location of this field will be + * different between 32-bit and 64-bit userspace, and the standard + * compat support in the kernel does not handle this case. so we + * need to explicitly handle it here. + */ + if (in_compat_syscall()) { + struct compat_iw_point *compat_iw_point = + (struct compat_iw_point *)&wrqu->data; + sub_cmd = compat_iw_point->flags; + } else { + sub_cmd = wrqu->data.flags; + } +#else + sub_cmd = wrqu->data.flags; +#endif + + mac_handle = hdd_ctx->mac_handle; + switch (sub_cmd) { + case WE_GET_FW_PROFILE_DATA: + ret = wma_cli_set_command(adapter->vdev_id, + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + 0, DBG_CMD); + break; + + case WE_IBSS_GET_PEER_INFO_ALL: + hdd_wlan_get_ibss_peer_info_all(adapter); + break; + + case WE_SET_REASSOC_TRIGGER: + { + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + tSirMacAddr bssid; + uint32_t roam_id = INVALID_ROAM_ID; + uint8_t operating_ch = + wlan_reg_freq_to_chan( + hdd_ctx->pdev, + adapter->session.station.conn_info.chan_freq); + tCsrRoamModifyProfileFields mod_fields; + + sme_get_modify_profile_fields(mac_handle, adapter->vdev_id, + &mod_fields); + if (roaming_offload_enabled(hdd_ctx)) { + qdf_mem_copy(bssid, + &adapter->session.station.conn_info.bssid, + sizeof(bssid)); + hdd_wma_send_fastreassoc_cmd(adapter, + bssid, operating_ch); + } else { + sme_roam_reassoc(mac_handle, adapter->vdev_id, + NULL, mod_fields, &roam_id, 1); + } + return 0; + } + + case WE_STOP_OBSS_SCAN: + /* + * 1.OBSS Scan is mandatory while operating in 2.4GHz + * 2.OBSS scan is stopped by Firmware during the disassociation + * 3.OBSS stop comamnd is added for debugging purpose + */ + if (!mac_handle) { + hdd_err("mac_handle context is NULL"); + return -EINVAL; + } + sme_ht40_stop_obss_scan(mac_handle, adapter->vdev_id); + break; + + default: + hdd_err("unknown ioctl %d", sub_cmd); + break; + } + hdd_exit(); + return ret; +} + +static int iw_setnone_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_setnone_getnone(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef MPC_UT_FRAMEWORK +static void +hdd_policy_mgr_set_hw_mode_ut(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, int cmd) +{ + enum hw_mode_ss_config mac0_ss; + enum hw_mode_bandwidth mac0_bw; + enum hw_mode_ss_config mac1_ss; + enum hw_mode_bandwidth mac1_bw; + enum hw_mode_mac_band_cap mac0_band_cap; + enum hw_mode_dbs_capab dbs; + enum policy_mgr_conc_next_action action; + + switch (cmd) { + case 0: + hdd_debug("set hw mode for single mac"); + mac0_ss = HW_MODE_SS_2x2; + mac0_bw = HW_MODE_80_MHZ; + mac1_ss = HW_MODE_SS_0x0; + mac1_bw = HW_MODE_BW_NONE; + mac0_band_cap = HW_MODE_MAC_BAND_NONE; + dbs = HW_MODE_DBS_NONE; + action = PM_SINGLE_MAC; + break; + case 1: + hdd_debug("set hw mode for dual mac"); + mac0_ss = HW_MODE_SS_1x1; + mac0_bw = HW_MODE_80_MHZ; + mac1_ss = HW_MODE_SS_1x1; + mac1_bw = HW_MODE_40_MHZ; + mac0_band_cap = HW_MODE_MAC_BAND_NONE; + dbs = HW_MODE_DBS; + action = PM_DBS; + break; + case 2: + hdd_debug("set hw mode for 2x2 5g + 1x1 2g"); + mac0_ss = HW_MODE_SS_2x2; + mac0_bw = HW_MODE_80_MHZ; + mac1_ss = HW_MODE_SS_1x1; + mac1_bw = HW_MODE_40_MHZ; + mac0_band_cap = HW_MODE_MAC_BAND_5G; + dbs = HW_MODE_DBS; + action = PM_DBS1; + break; + case 3: + hdd_debug("set hw mode for 2x2 2g + 1x1 5g"); + mac0_ss = HW_MODE_SS_2x2; + mac0_bw = HW_MODE_40_MHZ; + mac1_ss = HW_MODE_SS_1x1; + mac1_bw = HW_MODE_40_MHZ; + mac0_band_cap = HW_MODE_MAC_BAND_2G; + dbs = HW_MODE_DBS; + action = PM_DBS2; + break; + default: + hdd_err("unknown cmd %d", cmd); + return; + } + policy_mgr_pdev_set_hw_mode(hdd_ctx->psoc, adapter->vdev_id, + mac0_ss, mac0_bw, mac1_ss, mac1_bw, + mac0_band_cap, dbs, HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + POLICY_MGR_UPDATE_REASON_UT, PM_NOP, + action); +} + +static int iw_get_policy_manager_ut_ops(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, int sub_cmd, int *apps_args) +{ + switch (sub_cmd) { + case WE_POLICY_MANAGER_CLIST_CMD: + { + hdd_debug(" is called"); + if ((apps_args[0] < 0) || (apps_args[1] < 0) || + (apps_args[2] < 0) || (apps_args[3] < 0) || + (apps_args[4] < 0) || (apps_args[5] < 0) || + (apps_args[6] < 0) || (apps_args[7] < 0)) { + hdd_err("Invalid input params received for the IOCTL"); + return 0; + } + policy_mgr_incr_connection_count_utfw(hdd_ctx->psoc, + apps_args[0], apps_args[1], apps_args[2], apps_args[3], + apps_args[4], apps_args[5], apps_args[6], apps_args[7]); + } + break; + + case WE_POLICY_MANAGER_DLIST_CMD: + { + hdd_debug(" is called"); + if ((apps_args[0] < 0) || (apps_args[1] < 0)) { + hdd_err("Invalid input params received for the IOCTL"); + return 0; + } + policy_mgr_decr_connection_count_utfw(hdd_ctx->psoc, + apps_args[0], apps_args[1]); + } + break; + + case WE_POLICY_MANAGER_ULIST_CMD: + { + hdd_debug(" is called"); + if ((apps_args[0] < 0) || (apps_args[1] < 0) || + (apps_args[2] < 0) || (apps_args[3] < 0) || + (apps_args[4] < 0) || (apps_args[5] < 0) || + (apps_args[6] < 0) || (apps_args[7] < 0)) { + hdd_err("Invalid input params received for the IOCTL"); + return 0; + } + policy_mgr_update_connection_info_utfw( + hdd_ctx->psoc, + apps_args[0], apps_args[1], apps_args[2], apps_args[3], + apps_args[4], apps_args[5], + wlan_chan_to_freq(apps_args[6]), apps_args[7]); + } + break; + + case WE_POLICY_MANAGER_DBS_CMD: + { + hdd_debug(" is called"); + if (apps_args[0] == 0) + wma_set_dbs_capability_ut(0); + else + wma_set_dbs_capability_ut(1); + + if (apps_args[1] >= PM_THROUGHPUT && + apps_args[1] <= PM_LATENCY) { + hdd_debug("setting system pref to [%d]\n", + apps_args[1]); + ucfg_policy_mgr_set_sys_pref(hdd_ctx->psoc, + apps_args[1]); + } + } + break; + + case WE_POLICY_MANAGER_PCL_CMD: + { + uint32_t pcl[NUM_CHANNELS] = {0}; + uint8_t weight_list[NUM_CHANNELS] = {0}; + uint32_t pcl_len = 0, i = 0; + + hdd_debug(" is called"); + + if (apps_args[0] < 0) { + hdd_err("Invalid input param received for the IOCTL"); + return 0; + } + policy_mgr_get_pcl(hdd_ctx->psoc, apps_args[0], + pcl, &pcl_len, + weight_list, QDF_ARRAY_SIZE(weight_list)); + hdd_debug("PCL Freq list for role[%d] is {", apps_args[0]); + for (i = 0 ; i < pcl_len; i++) + hdd_debug(" %d, ", pcl[i]); + hdd_debug("}--------->\n"); + } + break; + + case WE_POLICY_SET_HW_MODE_CMD: + { + hdd_debug("pm_set_hw_mode cmd %d", apps_args[0]); + hdd_policy_mgr_set_hw_mode_ut(hdd_ctx, adapter, apps_args[0]); + } + break; + + case WE_POLICY_MANAGER_QUERY_ACTION_CMD: + { + hdd_debug(" is called"); + if (apps_args[0] < 0) { + hdd_err("Invalid input params received for the IOCTL"); + return 0; + } + policy_mgr_current_connections_update( + hdd_ctx->psoc, adapter->vdev_id, + wlan_chan_to_freq(apps_args[0]), + POLICY_MGR_UPDATE_REASON_UT); + } + break; + + case WE_POLICY_MANAGER_QUERY_ALLOW_CMD: + { + bool allow; + + hdd_debug(" is called"); + if ((apps_args[0] < 0) || (apps_args[1] < 0) || + (apps_args[2] < 0)) { + hdd_err("Invalid input params received for the IOCTL"); + return 0; + } + allow = policy_mgr_allow_concurrency( + hdd_ctx->psoc, apps_args[0], + wlan_chan_to_freq(apps_args[1]), apps_args[2]); + hdd_debug("allow %d {0 = don't allow, 1 = allow}", allow); + } + break; + + case WE_POLICY_MANAGER_SCENARIO_CMD: + { + clean_report(hdd_ctx); + if (apps_args[0] == 1) { + wlan_hdd_one_connection_scenario(hdd_ctx); + } else if (apps_args[0] == 2) { + wlan_hdd_two_connections_scenario(hdd_ctx, + 6, POLICY_MGR_TWO_TWO); + wlan_hdd_two_connections_scenario(hdd_ctx, + 36, POLICY_MGR_TWO_TWO); + wlan_hdd_two_connections_scenario(hdd_ctx, + 6, POLICY_MGR_ONE_ONE); + wlan_hdd_two_connections_scenario(hdd_ctx, + 36, POLICY_MGR_ONE_ONE); + } else if (apps_args[0] == 3) { + /* MCC on same band with 2x2 same mac*/ + wlan_hdd_three_connections_scenario(hdd_ctx, + 6, 11, POLICY_MGR_TWO_TWO, 0); + /* MCC on diff band with 2x2 same mac*/ + wlan_hdd_three_connections_scenario(hdd_ctx, + 6, 36, POLICY_MGR_TWO_TWO, 0); + /* MCC on diff band with 1x1 diff mac */ + wlan_hdd_three_connections_scenario(hdd_ctx, + 36, 6, POLICY_MGR_ONE_ONE, 0); + /* MCC on diff band with 1x1 same mac */ + wlan_hdd_three_connections_scenario(hdd_ctx, + 36, 6, POLICY_MGR_ONE_ONE, 1); + /* SCC on same band with 2x2 same mac */ + wlan_hdd_three_connections_scenario(hdd_ctx, + 36, 36, POLICY_MGR_TWO_TWO, 0); + /* SCC on same band with 1x1 same mac */ + wlan_hdd_three_connections_scenario(hdd_ctx, + 36, 36, POLICY_MGR_ONE_ONE, 1); + /* MCC on same band with 2x2 same mac */ + wlan_hdd_three_connections_scenario(hdd_ctx, + 36, 149, POLICY_MGR_TWO_TWO, 0); + /* MCC on same band with 1x1 same mac */ + wlan_hdd_three_connections_scenario(hdd_ctx, + 36, 149, POLICY_MGR_ONE_ONE, 1); + } + print_report(hdd_ctx); + } + break; + } + return 0; +} +#else +static int iw_get_policy_manager_ut_ops(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, int sub_cmd, int *apps_args) +{ + return 0; +} +#endif + +/** + * hdd_ch_avoid_unit_cmd - unit test ch avoidance + * @hdd_ctx: hdd_context + * @num_args: input args number + * @apps_args: args data ptr + * + * This is to inject a ch avoid event to do unit test SAP chan avoidance. + * + * Return: void + */ +#ifdef WLAN_DEBUG +static void hdd_ch_avoid_unit_cmd(struct hdd_context *hdd_ctx, + int num_args, int *apps_args) +{ + struct ch_avoid_ind_type ch_avoid; + int cnt = 0, i; + + if (num_args < 2 || num_args > CH_AVOID_MAX_RANGE * 2 || + num_args % 2 != 0) + return; + hdd_info("simulate ch avoid num_args %d", num_args); + for (i = 0; i < num_args && i < CH_AVOID_MAX_RANGE * 2; i++) { + ch_avoid.avoid_freq_range[cnt].start_freq = + apps_args[i]; + ch_avoid.avoid_freq_range[cnt].end_freq = + apps_args[++i]; + + hdd_info("simulate ch avoid [%d %d]", + ch_avoid.avoid_freq_range[cnt].start_freq, + ch_avoid.avoid_freq_range[cnt].end_freq); + cnt++; + } + ch_avoid.ch_avoid_range_cnt = cnt; + ucfg_reg_unit_simulate_ch_avoid(hdd_ctx->psoc, &ch_avoid); +} +#else +static void hdd_ch_avoid_unit_cmd(struct hdd_context *hdd_ctx, + int num_args, int *apps_args) +{ +} +#endif + +#ifdef FW_THERMAL_THROTTLE_SUPPORT +/** + * hdd_send_thermal_mgmt_cmd - Send thermal management params + * @mac_handle: Opaque handle to the global MAC context + * @lower_thresh_deg: Lower threshold value of Temperature + * @higher_thresh_deg: Higher threshold value of Temperature + * + * Return: QDF_STATUS + */ +#ifndef QCN7605_SUPPORT +static QDF_STATUS hdd_send_thermal_mgmt_cmd(mac_handle_t mac_handle, + uint16_t lower_thresh_deg, + uint16_t higher_thresh_deg) +{ + return sme_set_thermal_mgmt(mac_handle, lower_thresh_deg, + higher_thresh_deg); +} +#else +static QDF_STATUS hdd_send_thermal_mgmt_cmd(mac_handle_t mac_handle, + uint16_t lower_thresh_deg, + uint16_t higher_thresh_deg) +{ + return QDF_STATUS_SUCCESS; +} +#endif +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + +/** + * __iw_set_var_ints_getnone - Generic "set many" private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * This is an SSR-protected generic handler for private ioctls which + * take multiple arguments. Note that this implementation is also + * somewhat unique in that it is shared by both STA-mode and SAP-mode + * interfaces. + * + * Return: 0 on success, non-zero on error + */ +static int __iw_set_var_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + mac_handle_t mac_handle; + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + int sub_cmd; + int *apps_args = (int *) extra; + struct hdd_context *hdd_ctx; + int ret, num_args; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct cdp_txrx_stats_req req = {0}; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + mac_handle = hdd_ctx->mac_handle; + sub_cmd = wrqu->data.flags; + num_args = wrqu->data.length; + + hdd_debug("Received length %d", wrqu->data.length); + + switch (sub_cmd) { + case WE_P2P_NOA_CMD: + { + struct p2p_app_set_ps p2p_noa; + + if (adapter->device_mode != QDF_P2P_GO_MODE) { + hdd_err("Setting NoA is not allowed in Device mode %s(%d)", + qdf_opmode_str(adapter->device_mode), + adapter->device_mode); + return -EINVAL; + } + + p2p_noa.opp_ps = apps_args[0]; + p2p_noa.ct_window = apps_args[1]; + p2p_noa.duration = apps_args[2]; + p2p_noa.interval = apps_args[3]; + p2p_noa.count = apps_args[4]; + p2p_noa.single_noa_duration = apps_args[5]; + p2p_noa.ps_selection = apps_args[6]; + + hdd_debug("P2P_NOA_ATTR:opp ps %d ct window %d duration %d interval %d count %d single noa duration %d ps selection %x", + apps_args[0], apps_args[1], apps_args[2], + apps_args[3], apps_args[4], + apps_args[5], apps_args[6]); + + hdd_set_p2p_ps(dev, &p2p_noa); + + } + break; + + case WE_MTRACE_SELECTIVE_MODULE_LOG_ENABLE_CMD: + { + hdd_debug("SELECTIVE_MODULE_LOG %d arg1 %d arg2", + apps_args[0], apps_args[1]); + qdf_trace_enable(apps_args[0], apps_args[1]); + } + break; + + case WE_MTRACE_DUMP_CMD: + { + hdd_debug("MTRACE_DUMP code %d session %d count %d bitmask_of_module %d ", + apps_args[0], apps_args[1], + apps_args[2], apps_args[3]); + qdf_trace_dump_all((void *)mac_handle, apps_args[0], + apps_args[1], apps_args[2], + apps_args[3]); + + } + break; + + case WE_POLICY_MANAGER_CINFO_CMD: + { + struct policy_mgr_conc_connection_info *conn_info; + uint32_t i = 0, len = 0; + + hdd_info(" is called"); + conn_info = policy_mgr_get_conn_info(&len); + pr_info("+--------------------------+\n"); + for (i = 0; i < len; i++) { + pr_info("|table_index[%d]\t\t\n", i); + pr_info("|\t|vdev_id - %-10d|\n", conn_info->vdev_id); + pr_info("|\t|freq - %-10d|\n", conn_info->freq); + pr_info("|\t|bw - %-10d|\n", conn_info->bw); + pr_info("|\t|mode - %-10d|\n", conn_info->mode); + pr_info("|\t|mac - %-10d|\n", conn_info->mac); + pr_info("|\t|in_use - %-10d|\n", conn_info->in_use); + pr_info("+--------------------------+\n"); + conn_info++; + } + } + break; + + case WE_UNIT_TEST_CMD: + { + QDF_STATUS status; + + if ((apps_args[0] < WLAN_MODULE_ID_MIN) || + (apps_args[0] >= WLAN_MODULE_ID_MAX)) { + hdd_err_rl("Invalid MODULE ID %d", apps_args[0]); + return -EINVAL; + } + if ((apps_args[1] > (WMA_MAX_NUM_ARGS)) || + (apps_args[1] < 0)) { + hdd_err_rl("Too Many/Few args %d", apps_args[1]); + return -EINVAL; + } + + if (adapter->vdev_id >= WLAN_MAX_VDEVS) { + hdd_err_rl("Invalid vdev id"); + return -EINVAL; + } + + status = sme_send_unit_test_cmd(adapter->vdev_id, + apps_args[0], + apps_args[1], + &apps_args[2]); + if (QDF_STATUS_SUCCESS != status) { + hdd_err("sme_send_unit_test_cmd returned %d", status); + return -EINVAL; + } + } + break; +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING + case WE_LED_FLASHING_PARAM: + { + int i; + + if (num_args != 4) { + hdd_err("gpio_control: 4 parameters are required"); + return -EINVAL; + } + for (i = 0; i < num_args; i++) { + if (apps_args[i] >= 0x7fffffff) { + hdd_err("gpio_control: parameter should be less than 0x7fffffff"); + return -EINVAL; + } + } + sme_set_led_flashing(mac_handle, + 0, apps_args[0], apps_args[1]); + sme_set_led_flashing(mac_handle, + 1, apps_args[2], apps_args[3]); + } + break; +#endif + case WE_SET_PKTLOG: + { + int ret; + + if (num_args < 1 || num_args > 2) { + hdd_err("pktlog: either 1 or 2 parameters are required"); + return -EINVAL; + } + + ret = hdd_process_pktlog_command(hdd_ctx, apps_args[0], + apps_args[1]); + if (ret) + return ret; + break; + } + case WE_MAC_PWR_DEBUG_CMD: + { + struct sir_mac_pwr_dbg_cmd mac_pwr_dbg_args; + int i, j; + + if (num_args < 3) { + hdd_err("number of arguments can't be null %d", + num_args); + return -EINVAL; + } + if (num_args - 3 != apps_args[2]) { + hdd_err("arg list of size %d doesn't match num_args %d", + num_args-3, apps_args[2]); + return -EINVAL; + } + if ((apps_args[1] < WLAN_MODULE_ID_MIN) || + (apps_args[1] >= WLAN_MODULE_ID_MAX)) { + hdd_err("Invalid MODULE ID %d", apps_args[1]); + return -EINVAL; + } + if (apps_args[2] > (MAX_POWER_DBG_ARGS_SUPPORTED)) { + hdd_err("Too Many args %d", apps_args[2]); + return -EINVAL; + } + mac_pwr_dbg_args.pdev_id = apps_args[0]; + mac_pwr_dbg_args.module_id = apps_args[1]; + mac_pwr_dbg_args.num_args = apps_args[2]; + + for (i = 0, j = 3; i < mac_pwr_dbg_args.num_args; i++, j++) + mac_pwr_dbg_args.args[i] = apps_args[j]; + + if (QDF_STATUS_SUCCESS != + sme_process_mac_pwr_dbg_cmd(mac_handle, + adapter->vdev_id, + &mac_pwr_dbg_args)) { + return -EINVAL; + } + } + break; + case WE_POLICY_MANAGER_CLIST_CMD: + case WE_POLICY_MANAGER_DLIST_CMD: + case WE_POLICY_MANAGER_ULIST_CMD: + case WE_POLICY_MANAGER_DBS_CMD: + case WE_POLICY_MANAGER_PCL_CMD: + case WE_POLICY_SET_HW_MODE_CMD: + case WE_POLICY_MANAGER_QUERY_ACTION_CMD: + case WE_POLICY_MANAGER_QUERY_ALLOW_CMD: + case WE_POLICY_MANAGER_SCENARIO_CMD: + { + if (!hdd_ctx->config->is_unit_test_framework_enabled) { + hdd_warn_rl("UT framework is disabled"); + return -EINVAL; + } + iw_get_policy_manager_ut_ops(hdd_ctx, adapter, + sub_cmd, apps_args); + } + break; + case WE_SET_CHAN_AVOID: + { + hdd_ch_avoid_unit_cmd(hdd_ctx, num_args, apps_args); + } + break; + case WE_SET_TXRX_STATS: + { + req.stats = apps_args[0]; + /* default value of secondary parameter is 0(mac_id) */ + req.mac_id = apps_args[1]; + + hdd_debug("WE_SET_TXRX_STATS stats cmd: %d mac_id: %d", + req.stats, req.mac_id); + if (qdf_unlikely(!soc)) { + hdd_err("soc is NULL"); + return -EINVAL; + } + + if (apps_args[0] == CDP_TXRX_STATS_28) { + if (sta_ctx->conn_info.is_authenticated) { + hdd_debug("ap mac addr: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sta_ctx->conn_info.bssid.bytes)); + req.peer_addr = + (char *)&sta_ctx->conn_info.bssid; + } + } + ret = cdp_txrx_stats_request(soc, adapter->vdev_id, &req); + break; + } +#ifdef WLAN_FEATURE_MOTION_DETECTION + case WE_MOTION_DET_CONFIG_PARAM: + { + struct sme_motion_det_cfg motion_det_cfg; + + if (num_args != 15) { + hdd_err_rl("mt_config: Invalid no of args"); + return -EINVAL; + } + + motion_det_cfg.vdev_id = adapter->vdev_id; + motion_det_cfg.time_t1 = apps_args[0]; + motion_det_cfg.time_t2 = apps_args[1]; + motion_det_cfg.n1 = apps_args[2]; + motion_det_cfg.n2 = apps_args[3]; + motion_det_cfg.time_t1_gap = apps_args[4]; + motion_det_cfg.time_t2_gap = apps_args[5]; + motion_det_cfg.coarse_K = apps_args[6]; + motion_det_cfg.fine_K = apps_args[7]; + motion_det_cfg.coarse_Q = apps_args[8]; + motion_det_cfg.fine_Q = apps_args[9]; + motion_det_cfg.md_coarse_thr_high = apps_args[10]; + motion_det_cfg.md_fine_thr_high = apps_args[11]; + motion_det_cfg.md_coarse_thr_low = apps_args[12]; + motion_det_cfg.md_fine_thr_low = apps_args[13]; + adapter->motion_detection_mode = apps_args[14]; + sme_motion_det_config(hdd_ctx->mac_handle, &motion_det_cfg); + adapter->motion_det_cfg = true; + } + break; + case WE_MOTION_DET_BASE_LINE_CONFIG_PARAM: + { + struct sme_motion_det_base_line_cfg motion_det_base_line_cfg; + + if (num_args != 4) { + hdd_err_rl("mt_bl_config: Invalid no of args"); + return -EINVAL; + } + + motion_det_base_line_cfg.vdev_id = adapter->vdev_id; + motion_det_base_line_cfg.bl_time_t = apps_args[0]; + motion_det_base_line_cfg.bl_packet_gap = apps_args[1]; + motion_det_base_line_cfg.bl_n = apps_args[2]; + motion_det_base_line_cfg.bl_num_meas = apps_args[3]; + sme_motion_det_base_line_config(hdd_ctx->mac_handle, + &motion_det_base_line_cfg); + } + break; +#endif /* WLAN_FEATURE_MOTION_DETECTION */ +#ifdef FW_THERMAL_THROTTLE_SUPPORT + case WE_SET_THERMAL_THROTTLE_CFG: + { + QDF_STATUS status; + + if (num_args != 7) { + hdd_err_rl("set_thermal_cfg: Invalid no of args"); + return -EINVAL; + } + + /* Check for valid inputs */ + if (apps_args[0] < 0 || apps_args[0] > 1 || apps_args[1] < 0 || + apps_args[2] < 0 || apps_args[2] > 100 || + apps_args[3] < 0 || apps_args[3] > 3 || apps_args[4] < 0 || + apps_args[5] < 0 || apps_args[6] < 0 || + apps_args[5] <= apps_args[4]) + return -EINVAL; + + status = sme_set_thermal_throttle_cfg(hdd_ctx->mac_handle, + apps_args[0], + apps_args[1], + apps_args[2], + apps_args[3], + apps_args[6]); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + if (!apps_args[6]) { + status = hdd_send_thermal_mgmt_cmd(hdd_ctx->mac_handle, + apps_args[4], + apps_args[5]); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + } + break; + } +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + default: + { + hdd_err("Invalid IOCTL command %d", sub_cmd); + } + break; + } + hdd_exit(); + return 0; +} + +/** + * iw_hdd_set_var_ints_getnone() - set var ints getnone callback + * @dev: pointer to net_device structure + * @info: pointer to iw_request_info structure + * @wrqu: pointer to iwreq_data + * @extra; extra + * + * Return: 0 on success, error number otherwise + * + */ +static int iw_hdd_set_var_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + union iwreq_data u_priv_wrqu; + int apps_args[MAX_VAR_ARGS] = {0}; + int errno, num_args; + struct osif_vdev_sync *vdev_sync; + + if (!capable(CAP_NET_ADMIN)) { + hdd_err("permission check failed"); + return -EPERM; + } + + /* Helper function to get iwreq_data with compat handling. */ + if (hdd_priv_get_data(&u_priv_wrqu.data, wrqu)) + return -EINVAL; + + if (!u_priv_wrqu.data.pointer) { + hdd_err("NULL data pointer"); + return -EINVAL; + } + + num_args = u_priv_wrqu.data.length; + if (num_args > MAX_VAR_ARGS) + num_args = MAX_VAR_ARGS; + + if (copy_from_user(apps_args, u_priv_wrqu.data.pointer, + sizeof(int) * num_args)) { + hdd_err("failed to copy data from user buffer"); + return -EFAULT; + } + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_var_ints_getnone(dev, info, &u_priv_wrqu, + (char *)&apps_args); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_set_var_ints_getnone - Generic "set many" private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * This is a generic handler for private ioctls which take multiple + * arguments. Note that this implementation is also somewhat unique + * in that it is shared by both STA-mode and SAP-mode interfaces. + * + * Return: 0 on success, non-zero on error + */ +int iw_set_var_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_var_ints_getnone(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_add_tspec - Add TSpec private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_add_tspec(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + hdd_wlan_wmm_status_e *wmm_status = (hdd_wlan_wmm_status_e *) extra; + int params[HDD_WLAN_WMM_PARAM_COUNT]; + struct sme_qos_wmmtspecinfo tspec; + uint32_t handle; + struct iw_point s_priv_data; + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + /* make sure the application is sufficiently priviledged */ + /* note that the kernel will do this for "set" ioctls, but since */ + /* this ioctl wants to return status to user space it must be */ + /* defined as a "get" ioctl */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* we must be associated in order to add a tspec */ + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + /* since we are defined to be a "get" ioctl, and since the number */ + /* of params exceeds the number of params that wireless extensions */ + /* will pass down in the iwreq_data, we must copy the "set" params. */ + /* We must handle the compat for iwreq_data in 32U/64K environment. */ + + /* helper function to get iwreq_data with compat handling. */ + if (hdd_priv_get_data(&s_priv_data, wrqu)) { + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + /* make sure all params are correctly passed to function */ + if ((!s_priv_data.pointer) || + (HDD_WLAN_WMM_PARAM_COUNT != s_priv_data.length)) { + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + /* from user space ourselves */ + if (copy_from_user(¶ms, s_priv_data.pointer, sizeof(params))) { + /* hmmm, can't get them */ + return -EIO; + } + /* clear the tspec */ + memset(&tspec, 0, sizeof(tspec)); + + /* validate the handle */ + handle = params[HDD_WLAN_WMM_PARAM_HANDLE]; + if (HDD_WMM_HANDLE_IMPLICIT == handle) { + /* that one is reserved */ + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + /* validate the TID */ + if (params[HDD_WLAN_WMM_PARAM_TID] > 7) { + /* out of range */ + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + tspec.ts_info.tid = params[HDD_WLAN_WMM_PARAM_TID]; + + /* validate the direction */ + switch (params[HDD_WLAN_WMM_PARAM_DIRECTION]) { + case HDD_WLAN_WMM_DIRECTION_UPSTREAM: + tspec.ts_info.direction = SME_QOS_WMM_TS_DIR_UPLINK; + break; + + case HDD_WLAN_WMM_DIRECTION_DOWNSTREAM: + tspec.ts_info.direction = SME_QOS_WMM_TS_DIR_DOWNLINK; + break; + + case HDD_WLAN_WMM_DIRECTION_BIDIRECTIONAL: + tspec.ts_info.direction = SME_QOS_WMM_TS_DIR_BOTH; + break; + + default: + /* unknown */ + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + + tspec.ts_info.psb = params[HDD_WLAN_WMM_PARAM_APSD]; + + /* validate the user priority */ + if (params[HDD_WLAN_WMM_PARAM_USER_PRIORITY] >= SME_QOS_WMM_UP_MAX) { + /* out of range */ + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + tspec.ts_info.up = params[HDD_WLAN_WMM_PARAM_USER_PRIORITY]; + if (0 > tspec.ts_info.up || SME_QOS_WMM_UP_MAX < tspec.ts_info.up) { + hdd_err("***ts_info.up out of bounds***"); + return 0; + } + + hdd_debug("TS_INFO PSB %d UP %d !!!", + tspec.ts_info.psb, tspec.ts_info.up); + + tspec.nominal_msdu_size = params[HDD_WLAN_WMM_PARAM_NOMINAL_MSDU_SIZE]; + tspec.maximum_msdu_size = params[HDD_WLAN_WMM_PARAM_MAXIMUM_MSDU_SIZE]; + tspec.min_data_rate = params[HDD_WLAN_WMM_PARAM_MINIMUM_DATA_RATE]; + tspec.mean_data_rate = params[HDD_WLAN_WMM_PARAM_MEAN_DATA_RATE]; + tspec.peak_data_rate = params[HDD_WLAN_WMM_PARAM_PEAK_DATA_RATE]; + tspec.max_burst_size = params[HDD_WLAN_WMM_PARAM_MAX_BURST_SIZE]; + tspec.min_phy_rate = params[HDD_WLAN_WMM_PARAM_MINIMUM_PHY_RATE]; + tspec.surplus_bw_allowance = + params[HDD_WLAN_WMM_PARAM_SURPLUS_BANDWIDTH_ALLOWANCE]; + tspec.min_service_interval = + params[HDD_WLAN_WMM_PARAM_SERVICE_INTERVAL]; + tspec.max_service_interval = + params[HDD_WLAN_WMM_PARAM_MAX_SERVICE_INTERVAL]; + tspec.suspension_interval = + params[HDD_WLAN_WMM_PARAM_SUSPENSION_INTERVAL]; + tspec.inactivity_interval = + params[HDD_WLAN_WMM_PARAM_INACTIVITY_INTERVAL]; + + tspec.ts_info.burst_size_defn = + params[HDD_WLAN_WMM_PARAM_BURST_SIZE_DEFN]; + + /* validate the ts info ack policy */ + switch (params[HDD_WLAN_WMM_PARAM_ACK_POLICY]) { + case TS_INFO_ACK_POLICY_NORMAL_ACK: + tspec.ts_info.ack_policy = SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK; + break; + + case TS_INFO_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK: + tspec.ts_info.ack_policy = + SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK; + break; + + default: + /* unknown */ + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + + *wmm_status = hdd_wmm_addts(adapter, handle, &tspec); + hdd_exit(); + return 0; +} + +static int iw_add_tspec(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_add_tspec(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_del_tspec - Delete TSpec private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_del_tspec(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + int *params = (int *)extra; + hdd_wlan_wmm_status_e *wmm_status = (hdd_wlan_wmm_status_e *) extra; + uint32_t handle; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + /* make sure the application is sufficiently priviledged */ + /* note that the kernel will do this for "set" ioctls, but since */ + /* this ioctl wants to return status to user space it must be */ + /* defined as a "get" ioctl */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* although we are defined to be a "get" ioctl, the params we require */ + /* will fit in the iwreq_data, therefore unlike iw_add_tspec() there */ + /* is no need to copy the params from user space */ + + /* validate the handle */ + handle = params[HDD_WLAN_WMM_PARAM_HANDLE]; + if (HDD_WMM_HANDLE_IMPLICIT == handle) { + /* that one is reserved */ + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + + *wmm_status = hdd_wmm_delts(adapter, handle); + hdd_exit(); + return 0; +} + +static int iw_del_tspec(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_del_tspec(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_get_tspec - Get TSpec private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_get_tspec(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + int *params = (int *)extra; + hdd_wlan_wmm_status_e *wmm_status = (hdd_wlan_wmm_status_e *) extra; + uint32_t handle; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + /* although we are defined to be a "get" ioctl, the params we require */ + /* will fit in the iwreq_data, therefore unlike iw_add_tspec() there */ + /* is no need to copy the params from user space */ + + /* validate the handle */ + handle = params[HDD_WLAN_WMM_PARAM_HANDLE]; + if (HDD_WMM_HANDLE_IMPLICIT == handle) { + /* that one is reserved */ + *wmm_status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + return 0; + } + + *wmm_status = hdd_wmm_checkts(adapter, handle); + hdd_exit(); + return 0; +} + +static int iw_get_tspec(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_get_tspec(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_set_fties - Set FT IEs private ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Each time the supplicant has the auth_request or reassoc request + * IEs ready they are pushed to the driver. The driver will in turn + * use it to send out the auth req and reassoc req for 11r FT Assoc. + * + * Return: 0 on success, non-zero on error + */ +static int __iw_set_fties(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + if (!wrqu->data.length) { + hdd_err("called with 0 length IEs"); + return -EINVAL; + } + if (!wrqu->data.pointer) { + hdd_err("called with NULL IE"); + return -EINVAL; + } + /* Added for debug on reception of Re-assoc Req. */ + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + hdd_debug("Called with Ie of length = %d when not associated", + wrqu->data.length); + hdd_debug("Should be Re-assoc Req IEs"); + } + hdd_debug("called with Ie of length = %d", wrqu->data.length); + + /* Pass the received FT IEs to SME */ + sme_set_ft_ies(hdd_ctx->mac_handle, adapter->vdev_id, + extra, wrqu->data.length); + hdd_exit(); + return 0; +} + +static int iw_set_fties(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_fties(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_set_dynamic_mcbc_filter() - Set Dynamic MCBC Filter ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * This IOCTL is OBSOLETE as of Jan 30, 2017. We are leaving it here for the + * time being to provide guidance in migrating to standard APIs. + * + * Return: 0 on success, non-zero on error + */ +static int iw_set_dynamic_mcbc_filter(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + hdd_err("\n" + "setMCBCFilter is obsolete. Use the following instead:\n" + "Configure multicast filtering via the ‘ip’ command.\n" + "\tip maddr add 11:22:33:44:55:66 dev wlan0 # allow traffic to address\n" + "\tip maddr del 11:22:33:44:55:66 dev wlan0 # undo allow\n" + "Configure broadcast filtering via ini item, 'g_enable_non_arp_bc_hw_filter.'\n" + "\tg_enable_non_arp_bc_hw_filter=1 # drop all non-ARP broadcast traffic\n" + "\tg_enable_non_arp_bc_hw_filter=0 # allow all broadcast traffic"); + return -EINVAL; +} + +/** + * iw_set_host_offload - Set host offload ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_set_host_offload(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct host_offload_req *user_request = + (struct host_offload_req *) extra; + struct sir_host_offload_req offload_request; + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + hdd_err("dev is not in CONNECTED state, ignore!!!"); + return -EINVAL; + } + + /* Debug display of request components. */ + switch (user_request->offloadType) { + case WLAN_IPV4_ARP_REPLY_OFFLOAD: + hdd_debug("Host offload request: ARP reply"); + switch (user_request->enableOrDisable) { + case WLAN_OFFLOAD_DISABLE: + hdd_debug(" disable"); + break; + case WLAN_OFFLOAD_ARP_AND_BC_FILTER_ENABLE: + hdd_debug(" BC Filtering enable"); + /* fallthrough */ + case WLAN_OFFLOAD_ENABLE: + hdd_debug(" ARP offload enable"); + hdd_debug(" IP address: %pI4", + user_request->params.hostIpv4Addr); + } + break; + + case WLAN_IPV6_NEIGHBOR_DISCOVERY_OFFLOAD: + hdd_debug("Host offload request: neighbor discovery"); + switch (user_request->enableOrDisable) { + case WLAN_OFFLOAD_DISABLE: + hdd_debug(" disable"); + break; + case WLAN_OFFLOAD_ENABLE: + hdd_debug(" enable"); + hdd_debug(" IP address: %pI6c", + user_request->params.hostIpv6Addr); + } + } + + qdf_mem_zero(&offload_request, sizeof(offload_request)); + offload_request.offloadType = user_request->offloadType; + offload_request.enableOrDisable = user_request->enableOrDisable; + qdf_mem_copy(&offload_request.params, &user_request->params, + sizeof(user_request->params)); + qdf_mem_copy(&offload_request.bssid, &user_request->bssId.bytes, + QDF_MAC_ADDR_SIZE); + + if (QDF_STATUS_SUCCESS != + sme_set_host_offload(hdd_ctx->mac_handle, + adapter->vdev_id, &offload_request)) { + hdd_err("Failure to execute host offload request"); + return -EINVAL; + } + hdd_exit(); + return 0; +} + +static int iw_set_host_offload(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_host_offload(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * iw_set_keepalive_params - Set keepalive params ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_set_keepalive_params(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct keep_alive_req *request = (struct keep_alive_req *)extra; + struct hdd_context *hdd_ctx; + int ret; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + if (wrqu->data.length != sizeof(*request)) { + hdd_err("Invalid length %d", wrqu->data.length); + return -EINVAL; + } + + if (request->timePeriod > cfg_max(CFG_INFRA_STA_KEEP_ALIVE_PERIOD)) { + hdd_err("Value of timePeriod %d exceed Max limit %d", + request->timePeriod, + cfg_max(CFG_INFRA_STA_KEEP_ALIVE_PERIOD)); + return -EINVAL; + } + + /* Debug display of request components. */ + hdd_debug("Set Keep Alive Request : TimePeriod %d size %zu", + request->timePeriod, sizeof(struct keep_alive_req)); + + switch (request->packetType) { + case WLAN_KEEP_ALIVE_NULL_PKT: + hdd_debug("Keep Alive Request: Tx NULL"); + break; + + case WLAN_KEEP_ALIVE_UNSOLICIT_ARP_RSP: + hdd_debug("Keep Alive Request: Tx UnSolicited ARP RSP"); + + hdd_debug("Host IP address: %d.%d.%d.%d", + request->hostIpv4Addr[0], request->hostIpv4Addr[1], + request->hostIpv4Addr[2], request->hostIpv4Addr[3]); + + hdd_debug("Dest IP address: %d.%d.%d.%d", + request->destIpv4Addr[0], request->destIpv4Addr[1], + request->destIpv4Addr[2], request->destIpv4Addr[3]); + + hdd_debug("Dest MAC address: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(request->dest_macaddr.bytes)); + break; + } + + hdd_debug("Keep alive period %d", request->timePeriod); + + if (QDF_STATUS_SUCCESS != + sme_set_keep_alive(hdd_ctx->mac_handle, + adapter->vdev_id, request)) { + hdd_err("Failure to execute Keep Alive"); + return -EINVAL; + } + hdd_exit(); + return 0; +} + +static int iw_set_keepalive_params(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_keepalive_params(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef WLAN_FEATURE_PACKET_FILTERING +/** + * validate_packet_filter_params_size() - Validate the size of the params rcvd + * @priv_data: Pointer to the priv data from user space + * @request: Pointer to the struct containing the copied data from user space + * + * Return: False on invalid length, true otherwise + */ +static bool validate_packet_filter_params_size(struct pkt_filter_cfg *request, + uint16_t length) +{ + int max_params_size, rcvd_params_size; + + max_params_size = HDD_MAX_CMP_PER_PACKET_FILTER * + sizeof(struct pkt_filter_param_cfg); + + if (length < sizeof(struct pkt_filter_cfg) - max_params_size) { + hdd_err("Less than minimum number of arguments needed"); + return false; + } + + rcvd_params_size = request->num_params * + sizeof(struct pkt_filter_param_cfg); + + if (length != sizeof(struct pkt_filter_cfg) - + max_params_size + rcvd_params_size) { + hdd_err("Arguments do not match the number of params provided"); + return false; + } + + return true; +} + +/** + * __iw_set_packet_filter_params() - set packet filter parameters in target + * @dev: Pointer to netdev + * @info: Pointer to iw request info + * @wrqu: Pointer to data + * @extra: Pointer to extra data + * + * Return: 0 on success, non-zero on error + */ +static int __iw_set_packet_filter_params(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret; + struct hdd_context *hdd_ctx; + struct iw_point priv_data; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct pkt_filter_cfg *request = NULL; + + if (!capable(CAP_NET_ADMIN)) { + hdd_err("permission check failed"); + return -EPERM; + } + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + if (hdd_priv_get_data(&priv_data, wrqu)) { + hdd_err("failed to get priv data"); + return -EINVAL; + } + + if ((!priv_data.pointer) || (0 == priv_data.length)) { + hdd_err("invalid priv data %pK or invalid priv data length %d", + priv_data.pointer, priv_data.length); + return -EINVAL; + } + + if (adapter->device_mode != QDF_STA_MODE) { + hdd_err("Packet filter not supported for this mode :%d", + adapter->device_mode); + return -ENOTSUPP; + } + + if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { + hdd_err("Packet filter not supported in disconnected state"); + return -ENOTSUPP; + } + + /* copy data using copy_from_user */ + request = mem_alloc_copy_from_user_helper(priv_data.pointer, + priv_data.length); + + if (!request) { + hdd_err("mem_alloc_copy_from_user_helper fail"); + return -ENOMEM; + } + + if (!validate_packet_filter_params_size(request, priv_data.length)) { + hdd_err("Invalid priv data length %d", priv_data.length); + qdf_mem_free(request); + return -EINVAL; + } + + if (request->filter_action == HDD_RCV_FILTER_SET) + hdd_ctx->user_configured_pkt_filter_rules |= + 1 << request->filter_id; + else if (request->filter_action == HDD_RCV_FILTER_CLEAR) + hdd_ctx->user_configured_pkt_filter_rules &= + ~(1 << request->filter_id); + + ret = wlan_hdd_set_filter(hdd_ctx, request, adapter->vdev_id); + + qdf_mem_free(request); + hdd_exit(); + return ret; +} + +/** + * iw_set_packet_filter_params() - set packet filter parameters in target + * @dev: Pointer to netdev + * @info: Pointer to iw request info + * @wrqu: Pointer to data + * @extra: Pointer to extra data + * + * Return: 0 on success, non-zero on error + */ +static int iw_set_packet_filter_params(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_packet_filter_params(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif + +static int hdd_get_wlan_stats(struct hdd_adapter *adapter) +{ + return wlan_hdd_get_station_stats(adapter); +} + +static int __iw_get_statistics(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret; + char *p; + int tlen; + struct hdd_station_ctx *sta_ctx; + tCsrSummaryStatsInfo *summary_stats; + tCsrGlobalClassAStatsInfo *class_a_stats; + tCsrGlobalClassDStatsInfo *class_d_stats; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) { + wrqu->data.length = 0; + return 0; + } + + hdd_get_wlan_stats(adapter); + + summary_stats = &(adapter->hdd_stats.summary_stat); + class_a_stats = &(adapter->hdd_stats.class_a_stat); + class_d_stats = &(adapter->hdd_stats.class_d_stat); + + p = extra; + tlen = 0; + + FILL_TLV(p, WLAN_STATS_RETRY_CNT, + sizeof(summary_stats->retry_cnt), + &(summary_stats->retry_cnt[0]), tlen); + + FILL_TLV(p, WLAN_STATS_MUL_RETRY_CNT, + sizeof(summary_stats->multiple_retry_cnt), + &(summary_stats->multiple_retry_cnt[0]), tlen); + + FILL_TLV(p, WLAN_STATS_TX_FRM_CNT, + sizeof(summary_stats->tx_frm_cnt), + &(summary_stats->tx_frm_cnt[0]), tlen); + + FILL_TLV(p, WLAN_STATS_RX_FRM_CNT, + sizeof(summary_stats->rx_frm_cnt), + &(summary_stats->rx_frm_cnt), tlen); + + FILL_TLV(p, WLAN_STATS_FRM_DUP_CNT, + sizeof(summary_stats->frm_dup_cnt), + &(summary_stats->frm_dup_cnt), tlen); + + FILL_TLV(p, WLAN_STATS_FAIL_CNT, + sizeof(summary_stats->fail_cnt), + &(summary_stats->fail_cnt[0]), tlen); + + FILL_TLV(p, WLAN_STATS_RTS_FAIL_CNT, + sizeof(summary_stats->rts_fail_cnt), + &(summary_stats->rts_fail_cnt), tlen); + + FILL_TLV(p, WLAN_STATS_ACK_FAIL_CNT, + sizeof(summary_stats->ack_fail_cnt), + &(summary_stats->ack_fail_cnt), tlen); + + FILL_TLV(p, WLAN_STATS_RTS_SUC_CNT, + sizeof(summary_stats->rts_succ_cnt), + &(summary_stats->rts_succ_cnt), tlen); + + FILL_TLV(p, WLAN_STATS_RX_DISCARD_CNT, + sizeof(summary_stats->rx_discard_cnt), + &(summary_stats->rx_discard_cnt), tlen); + + FILL_TLV(p, WLAN_STATS_RX_ERROR_CNT, + sizeof(summary_stats->rx_error_cnt), + &(summary_stats->rx_error_cnt), tlen); + + FILL_TLV(p, WLAN_STATS_TX_BYTE_CNT, + sizeof(class_d_stats->tx_uc_byte_cnt[0]), + &(class_d_stats->tx_uc_byte_cnt[0]), tlen); + + FILL_TLV(p, WLAN_STATS_RX_BYTE_CNT, + sizeof(class_d_stats->rx_byte_cnt), + &(class_d_stats->rx_byte_cnt), tlen); + + FILL_TLV(p, WLAN_STATS_RX_RATE, + sizeof(class_d_stats->rx_rate), + &(class_d_stats->rx_rate), tlen); + + /* Transmit rate, in units of 500 kbit/sec */ + FILL_TLV(p, WLAN_STATS_TX_RATE, + sizeof(class_a_stats->tx_rate), + &(class_a_stats->tx_rate), tlen); + + FILL_TLV(p, WLAN_STATS_RX_UC_BYTE_CNT, + sizeof(class_d_stats->rx_uc_byte_cnt[0]), + &(class_d_stats->rx_uc_byte_cnt[0]), tlen); + FILL_TLV(p, WLAN_STATS_RX_MC_BYTE_CNT, + sizeof(class_d_stats->rx_mc_byte_cnt), + &(class_d_stats->rx_mc_byte_cnt), tlen); + FILL_TLV(p, WLAN_STATS_RX_BC_BYTE_CNT, + sizeof(class_d_stats->rx_bc_byte_cnt), + &(class_d_stats->rx_bc_byte_cnt), tlen); + FILL_TLV(p, WLAN_STATS_TX_UC_BYTE_CNT, + sizeof(class_d_stats->tx_uc_byte_cnt[0]), + &(class_d_stats->tx_uc_byte_cnt[0]), tlen); + FILL_TLV(p, WLAN_STATS_TX_MC_BYTE_CNT, + sizeof(class_d_stats->tx_mc_byte_cnt), + &(class_d_stats->tx_mc_byte_cnt), tlen); + FILL_TLV(p, WLAN_STATS_TX_BC_BYTE_CNT, + sizeof(class_d_stats->tx_bc_byte_cnt), + &(class_d_stats->tx_bc_byte_cnt), tlen); + + wrqu->data.length = tlen; + + hdd_exit(); + + return 0; +} + +static int iw_get_statistics(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_get_statistics(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +#ifdef FEATURE_WLAN_SCAN_PNO +/*Max Len for PNO notification*/ +#define MAX_PNO_NOTIFY_LEN 100 +static void found_pref_network_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *args) +{ + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + union iwreq_data wrqu; + char buf[MAX_PNO_NOTIFY_LEN + 1]; + + wlan_vdev_obj_lock(vdev); + osif_priv = wlan_vdev_get_ospriv(vdev); + wlan_vdev_obj_unlock(vdev); + if (!osif_priv) { + hdd_err("osif_priv is null"); + return; + } + + wdev = osif_priv->wdev; + if (!wdev) { + hdd_err("wdev is null"); + return; + } + + hdd_debug("A preferred network was found"); + + /* create the event */ + qdf_mem_zero(&wrqu, sizeof(wrqu)); + qdf_mem_zero(buf, sizeof(buf)); + + snprintf(buf, MAX_PNO_NOTIFY_LEN, + "QCOM: Found preferred network:"); + + wrqu.data.pointer = buf; + wrqu.data.length = strlen(buf); + + /* send the event */ + + wireless_send_event(wdev->netdev, IWEVCUSTOM, &wrqu, buf); +} + +/** + * __iw_set_pno() - Preferred Network Offload ioctl handler + * @dev: device upon which the ioctl was received + * @info: ioctl request information + * @wrqu: ioctl request data + * @extra: ioctl extra data + * + * This function parses a Preferred Network Offload command + * Input is string based and expected to be of the form: + * + * + * when enabling: + * + * for each network: + * + * + * + * + * + * + * e.g: + * 1 2 4 test 0 0 3 1 6 11 2 40 5 test2 4 4 6 1 2 3 4 5 6 1 0 5 2 1 + * + * this translates into: + * ----------------------------- + * enable PNO + * 2 networks + * Network 1: + * test - with authentication type 0 and encryption type 0, + * search on 3 channels: 1 6 and 11, + * SSID bcast type is unknown (directed probe will be sent if + * AP not found) and must meet -40dBm RSSI + * Network 2: + * test2 - with authentication type 4 and encryption type 4, + * search on 6 channels 1, 2, 3, 4, 5 and 6 + * bcast type is non-bcast (directed probe will be sent) + * and must not meet any RSSI threshold + * scan every 5 seconds 2 times + * enable on suspend + */ +static int __iw_set_pno(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + uint8_t value; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + int ret = 0; + int offset; + char *ptr, *data; + uint8_t i, j, params; + QDF_STATUS status; + size_t len; + + /* request is a large struct, so we make it static to avoid + * stack overflow. This API is only invoked via ioctl, so it + * is serialized by the kernel rtnl_lock and hence does not + * need to be reentrant + */ + static struct pno_scan_req_params req; + + hdd_enter_dev(dev); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + vdev = wlan_objmgr_get_vdev_by_macaddr_from_pdev(hdd_ctx->pdev, + dev->dev_addr, + WLAN_OSIF_ID); + if (!vdev) { + hdd_err("vdev object is NULL"); + return -EIO; + } + + /* making sure argument string ends with '\0' */ + len = (wrqu->data.length + 1); + data = qdf_mem_malloc(len); + if (!data) { + hdd_err("fail to allocate memory %zu", len); + ret = -EINVAL; + goto exit; + } + qdf_mem_copy(data, extra, (len-1)); + ptr = data; + + hdd_debug("PNO data len %d data %s", wrqu->data.length, data); + + if (1 != sscanf(ptr, " %hhu %n", &value, &offset)) { + hdd_err("PNO enable input is not valid %s", ptr); + ret = -EINVAL; + goto exit; + } + + if (!value) { + status = ucfg_scan_pno_stop(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to disabled PNO"); + ret = -EINVAL; + } else { + hdd_debug("PNO scan disabled"); + } + goto exit; + } + + if (ucfg_scan_get_pno_in_progress(vdev)) { + hdd_debug("pno is already in progress"); + ret = -EBUSY; + goto exit; + } + + ptr += offset; + + if (1 != sscanf(ptr, " %hhu %n", &value, &offset)) { + hdd_err("PNO count input not valid %s", ptr); + ret = -EINVAL; + goto exit; + } + req.networks_cnt = value; + + hdd_debug("PNO enable networks count %d offset %d", + req.networks_cnt, offset); + + if ((0 == req.networks_cnt) || + (req.networks_cnt > SCAN_PNO_MAX_SUPP_NETWORKS)) { + hdd_err("Network count %d invalid", + req.networks_cnt); + ret = -EINVAL; + goto exit; + } + + ptr += offset; + + for (i = 0; i < req.networks_cnt; i++) { + + req.networks_list[i].ssid.length = 0; + + params = sscanf(ptr, " %hhu %n", + &(req.networks_list[i].ssid.length), + &offset); + + if (1 != params) { + hdd_err("PNO ssid length input is not valid %s", ptr); + ret = -EINVAL; + goto exit; + } + + if ((0 == req.networks_list[i].ssid.length) || + (req.networks_list[i].ssid.length > 32)) { + hdd_err("SSID Len %d is not correct for network %d", + req.networks_list[i].ssid.length, i); + ret = -EINVAL; + goto exit; + } + + /* Advance to SSID */ + ptr += offset; + + memcpy(req.networks_list[i].ssid.ssid, ptr, + req.networks_list[i].ssid.length); + ptr += req.networks_list[i].ssid.length; + + params = sscanf(ptr, " %u %u %hhu %n", + &(req.networks_list[i].authentication), + &(req.networks_list[i].encryption), + &(req.networks_list[i].channel_cnt), + &offset); + + if (3 != params) { + hdd_err("Incorrect cmd %s", ptr); + ret = -EINVAL; + goto exit; + } + + hdd_debug("PNO len %d ssid %.*s auth %d encry %d channel count %d offset %d", + req.networks_list[i].ssid.length, + req.networks_list[i].ssid.length, + req.networks_list[i].ssid.ssid, + req.networks_list[i].authentication, + req.networks_list[i].encryption, + req.networks_list[i].channel_cnt, offset); + + /* Advance to channel list */ + ptr += offset; + + if (SCAN_PNO_MAX_NETW_CHANNELS_EX < + req.networks_list[i].channel_cnt) { + hdd_err("Incorrect number of channels"); + ret = -EINVAL; + goto exit; + } + + if (0 != req.networks_list[i].channel_cnt) { + for (j = 0; j < req.networks_list[i].channel_cnt; + j++) { + if (1 != sscanf(ptr, " %hhu %n", &value, + &offset)) { + hdd_err("PNO network channel is not valid %s", + ptr); + ret = -EINVAL; + goto exit; + } + if (!IS_CHANNEL_VALID(value)) { + hdd_err("invalid channel: %hhu", value); + ret = -EINVAL; + goto exit; + } + req.networks_list[i].channels[j] = + cds_chan_to_freq(value); + /* Advance to next channel number */ + ptr += offset; + } + } + + if (1 != sscanf(ptr, " %u %n", + &(req.networks_list[i].bc_new_type), + &offset)) { + hdd_err("PNO broadcast network type is not valid %s", + ptr); + ret = -EINVAL; + goto exit; + } + if (req.networks_list[i].bc_new_type > 2) { + hdd_err("invalid bcast nw type: %u", + req.networks_list[i].bc_new_type); + ret = -EINVAL; + goto exit; + } + + hdd_debug("PNO bcastNetwType %d offset %d", + req.networks_list[i].bc_new_type, offset); + + /* Advance to rssi Threshold */ + ptr += offset; + if (1 != sscanf(ptr, " %d %n", + &(req.networks_list[i].rssi_thresh), + &offset)) { + hdd_err("PNO rssi threshold input is not valid %s", + ptr); + ret = -EINVAL; + goto exit; + } + hdd_debug("PNO rssi %d offset %d", + req.networks_list[i].rssi_thresh, offset); + /* Advance to next network */ + ptr += offset; + } /* For ucNetworkCount */ + + req.fast_scan_period = 0; + if (sscanf(ptr, " %u %n", &(req.fast_scan_period), &offset) > 0) { + req.fast_scan_period *= MSEC_PER_SEC; + ptr += offset; + } + if (req.fast_scan_period == 0) { + hdd_err("invalid fast scan period %u", + req.fast_scan_period); + ret = -EINVAL; + goto exit; + } + + req.fast_scan_max_cycles = 0; + if (sscanf(ptr, " %hhu %n", &value, + &offset) > 0) + ptr += offset; + req.fast_scan_max_cycles = value; + + wlan_pdev_obj_lock(hdd_ctx->pdev); + psoc = wlan_pdev_get_psoc(hdd_ctx->pdev); + wlan_pdev_obj_unlock(hdd_ctx->pdev); + ucfg_scan_register_pno_cb(psoc, + found_pref_network_cb, NULL); + + ucfg_scan_get_pno_def_params(vdev, &req); + status = ucfg_scan_pno_start(vdev, &req); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to enable PNO"); + ret = -EINVAL; + } + +exit: + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); + + qdf_mem_free(data); + return ret; +} + +static int iw_set_pno(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_pno(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} +#endif /* FEATURE_WLAN_SCAN_PNO */ + +static int __iw_set_band_config(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct hdd_context *hdd_ctx; + int ret; + int *value = (int *)extra; + + hdd_enter_dev(dev); + + if (!capable(CAP_NET_ADMIN)) { + hdd_err("permission check failed"); + return -EPERM; + } + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + return hdd_reg_set_band(dev, value[0]); +} + +static int iw_set_band_config(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_band_config(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +static int printk_adapter(void *priv, const char *fmt, ...) +{ + int ret; + va_list args; + + va_start(args, fmt); + ret = vprintk(fmt, args); + ret += printk("\n"); + va_end(args); + + return ret; +} + +static void hdd_ioctl_log_buffer(int log_id, uint32_t count) +{ + qdf_abstract_print *print = &printk_adapter; + + switch (log_id) { + case HTC_CREDIT_HISTORY_LOG: + cds_print_htc_credit_history(count, print, NULL); + break; + case COMMAND_LOG: + wma_print_wmi_cmd_log(count, print, NULL); + break; + case COMMAND_TX_CMP_LOG: + wma_print_wmi_cmd_tx_cmp_log(count, print, NULL); + break; + case MGMT_COMMAND_LOG: + wma_print_wmi_mgmt_cmd_log(count, print, NULL); + break; + case MGMT_COMMAND_TX_CMP_LOG: + wma_print_wmi_mgmt_cmd_tx_cmp_log(count, print, NULL); + break; + case EVENT_LOG: + wma_print_wmi_event_log(count, print, NULL); + break; + case RX_EVENT_LOG: + wma_print_wmi_rx_event_log(count, print, NULL); + break; + case MGMT_EVENT_LOG: + wma_print_wmi_mgmt_event_log(count, print, NULL); + break; + default: + print(NULL, "Invalid Log Id %d", log_id); + break; + } +} + +#ifdef CONFIG_WLAN_DEBUG_CRASH_INJECT +int hdd_crash_inject(struct hdd_adapter *adapter, uint32_t v1, uint32_t v2) +{ + struct hdd_context *hdd_ctx; + int ret; + bool crash_inject; + QDF_STATUS status; + + hdd_debug("WE_SET_FW_CRASH_INJECT: %d %d", + v1, v2); + pr_err("SSR is triggered by iwpriv CRASH_INJECT: %d %d\n", + v1, v2); + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + status = ucfg_mlme_get_crash_inject(hdd_ctx->psoc, &crash_inject); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Failed to get crash inject ini config"); + return 0; + } + + if (!crash_inject) { + hdd_err("Crash Inject ini disabled, Ignore Crash Inject"); + return 0; + } + + if (v1 == 3) { + cds_trigger_recovery(QDF_REASON_UNSPECIFIED); + return 0; + } + ret = wma_cli_set2_command(adapter->vdev_id, + GEN_PARAM_CRASH_INJECT, + v1, v2, GEN_CMD); + return ret; +} +#endif + +#ifdef CONFIG_DP_TRACE +void hdd_set_dump_dp_trace(uint16_t cmd_type, uint16_t count) +{ + hdd_debug("WE_DUMP_DP_TRACE_LEVEL: %d %d", + cmd_type, count); + if (cmd_type == DUMP_DP_TRACE) + qdf_dp_trace_dump_all(count, QDF_TRACE_DEFAULT_PDEV_ID); + else if (cmd_type == ENABLE_DP_TRACE_LIVE_MODE) + qdf_dp_trace_enable_live_mode(); + else if (cmd_type == CLEAR_DP_TRACE_BUFFER) + qdf_dp_trace_clear_buffer(); + else if (cmd_type == DISABLE_DP_TRACE_LIVE_MODE) + qdf_dp_trace_disable_live_mode(); +} +#endif + +static int __iw_set_two_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + int *value = (int *)extra; + int sub_cmd = value[0]; + int ret; + uint8_t dual_mac_feature = DISABLE_DBS_CXN_AND_SCAN; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + + hdd_enter_dev(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (0 != ret) + return ret; + + ret = hdd_check_private_wext_control(hdd_ctx, info); + if (0 != ret) + return ret; + + switch (sub_cmd) { + case WE_SET_SMPS_PARAM: + hdd_debug("WE_SET_SMPS_PARAM val %d %d", value[1], value[2]); + ret = wma_cli_set_command(adapter->vdev_id, + WMI_STA_SMPS_PARAM_CMDID, + value[1] << WMA_SMPS_PARAM_VALUE_S + | value[2], + VDEV_CMD); + break; + case WE_SET_FW_CRASH_INJECT: + ret = hdd_crash_inject(adapter, value[1], value[2]); + break; + case WE_ENABLE_FW_PROFILE: + hdd_err("WE_ENABLE_FW_PROFILE: %d %d", + value[1], value[2]); + ret = wma_cli_set2_command(adapter->vdev_id, + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + value[1], value[2], DBG_CMD); + break; + case WE_SET_FW_PROFILE_HIST_INTVL: + hdd_err("WE_SET_FW_PROFILE_HIST_INTVL: %d %d", + value[1], value[2]); + ret = wma_cli_set2_command(adapter->vdev_id, + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + value[1], value[2], DBG_CMD); + break; + case WE_SET_DUAL_MAC_FW_MODE_CONFIG: + hdd_debug("Ioctl to set dual fw mode config"); + status = + ucfg_policy_mgr_get_dual_mac_feature(hdd_ctx->psoc, + &dual_mac_feature); + if (status != QDF_STATUS_SUCCESS) + hdd_err("can't get dual mac feature val, use def"); + if (dual_mac_feature == DISABLE_DBS_CXN_AND_SCAN) { + hdd_err("Dual mac feature is disabled from INI"); + return -EPERM; + } + hdd_debug("%d %d", value[1], value[2]); + policy_mgr_set_dual_mac_fw_mode_config(hdd_ctx->psoc, + value[1], value[2]); + break; + case WE_DUMP_DP_TRACE_LEVEL: + hdd_set_dump_dp_trace(value[1], value[2]); + break; + case WE_SET_MON_MODE_CHAN: + if (value[1] > 256) + ret = wlan_hdd_set_mon_chan(adapter, value[1], + value[2]); + else + ret = wlan_hdd_set_mon_chan( + adapter, + wlan_reg_legacy_chan_to_freq( + hdd_ctx->pdev, value[1]), + value[2]); + break; + case WE_SET_WLAN_SUSPEND: + ret = hdd_wlan_fake_apps_suspend(hdd_ctx->wiphy, dev, + value[1], value[2]); + break; + case WE_SET_WLAN_RESUME: + ret = hdd_wlan_fake_apps_resume(hdd_ctx->wiphy, dev); + break; + case WE_LOG_BUFFER: { + int log_id = value[1]; + uint32_t count = value[2] < 0 ? 0 : value[2]; + + hdd_ioctl_log_buffer(log_id, count); + + break; + } + case WE_SET_BA_AGEING_TIMEOUT: + { + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!soc) { + hdd_err("Invalid handles"); + break; + } + cdp_set_ba_timeout(soc, value[1], value[2]); + break; + } + default: + hdd_err("Invalid IOCTL command %d", sub_cmd); + break; + } + + return ret; +} + +static int iw_set_two_ints_getnone(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(dev, &vdev_sync); + if (errno) + return errno; + + errno = __iw_set_two_ints_getnone(dev, info, wrqu, extra); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/* Define the Wireless Extensions to the Linux Network Device structure */ + +static const iw_handler we_private[] = { + + [WLAN_PRIV_SET_INT_GET_NONE - SIOCIWFIRSTPRIV] = iw_setint_getnone, + [WLAN_PRIV_SET_NONE_GET_INT - SIOCIWFIRSTPRIV] = iw_setnone_getint, + [WLAN_PRIV_SET_CHAR_GET_NONE - SIOCIWFIRSTPRIV] = iw_setchar_getnone, + [WLAN_PRIV_SET_THREE_INT_GET_NONE - SIOCIWFIRSTPRIV] = + iw_set_three_ints_getnone, + [WLAN_PRIV_GET_CHAR_SET_NONE - SIOCIWFIRSTPRIV] = iw_get_char_setnone, + [WLAN_PRIV_SET_NONE_GET_NONE - SIOCIWFIRSTPRIV] = iw_setnone_getnone, + [WLAN_PRIV_SET_VAR_INT_GET_NONE - SIOCIWFIRSTPRIV] = + iw_hdd_set_var_ints_getnone, + [WLAN_PRIV_SET_NONE_GET_THREE_INT - SIOCIWFIRSTPRIV] = + iw_setnone_get_threeint, +#ifdef WLAN_FEATURE_FIPS + [WLAN_PRIV_FIPS_TEST - SIOCIWFIRSTPRIV] = hdd_fips_test, +#endif + [WLAN_PRIV_ADD_TSPEC - SIOCIWFIRSTPRIV] = iw_add_tspec, + [WLAN_PRIV_DEL_TSPEC - SIOCIWFIRSTPRIV] = iw_del_tspec, + [WLAN_PRIV_GET_TSPEC - SIOCIWFIRSTPRIV] = iw_get_tspec, + [WLAN_PRIV_SET_FTIES - SIOCIWFIRSTPRIV] = iw_set_fties, + [WLAN_PRIV_SET_HOST_OFFLOAD - SIOCIWFIRSTPRIV] = iw_set_host_offload, + [WLAN_GET_WLAN_STATISTICS - SIOCIWFIRSTPRIV] = iw_get_statistics, + [WLAN_SET_KEEPALIVE_PARAMS - SIOCIWFIRSTPRIV] = + iw_set_keepalive_params, +#ifdef WLAN_FEATURE_PACKET_FILTERING + [WLAN_SET_PACKET_FILTER_PARAMS - SIOCIWFIRSTPRIV] = + iw_set_packet_filter_params, +#endif +#ifdef FEATURE_WLAN_SCAN_PNO + [WLAN_SET_PNO - SIOCIWFIRSTPRIV] = iw_set_pno, +#endif + [WLAN_SET_BAND_CONFIG - SIOCIWFIRSTPRIV] = iw_set_band_config, + [WLAN_PRIV_SET_MCBC_FILTER - SIOCIWFIRSTPRIV] = + iw_set_dynamic_mcbc_filter, + [WLAN_GET_LINK_SPEED - SIOCIWFIRSTPRIV] = iw_get_linkspeed, +#ifdef FEATURE_WLM_STATS + [WLAN_GET_WLM_STATS - SIOCIWFIRSTPRIV] = iw_get_wlm_stats, +#endif + [WLAN_PRIV_SET_TWO_INT_GET_NONE - SIOCIWFIRSTPRIV] = + iw_set_two_ints_getnone, + [WLAN_SET_DOT11P_CHANNEL_SCHED - SIOCIWFIRSTPRIV] = + iw_set_dot11p_channel_sched, +}; + +/*Maximum command length can be only 15 */ +static const struct iw_priv_args we_private_args[] = { + + /* handlers for main ioctl */ + {WLAN_PRIV_SET_INT_GET_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + ""}, + + /* handlers for sub-ioctl */ + {WE_SET_11D_STATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "set11Dstate"}, + + {WE_WOWL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "wowl"}, + + {WE_SET_POWER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setPower"}, + + {WE_SET_MAX_ASSOC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setMaxAssoc"}, + + {WE_SET_SCAN_DISABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "scan_disable"}, + + {WE_SET_DATA_INACTIVITY_TO, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "inactivityTO"}, + + {WE_SET_WOW_DATA_INACTIVITY_TO, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "wow_ito"}, + + {WE_SET_MAX_TX_POWER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setMaxTxPower"}, + + {WE_SET_TX_POWER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setTxPower"}, + + {WE_SET_MC_RATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setMcRate"}, + + {WE_SET_MAX_TX_POWER_2_4, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setTxMaxPower2G"}, + + {WE_SET_MAX_TX_POWER_5_0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setTxMaxPower5G"}, + +#ifndef REMOVE_PKT_LOG + {WE_SET_PKTLOG, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pktlog"}, +#endif + + /* SAP has TxMax whereas STA has MaxTx, adding TxMax for STA + * as well to keep same syntax as in SAP. Now onwards, STA + * will support both + */ + {WE_SET_MAX_TX_POWER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setTxMaxPower"}, + +#ifdef HASTINGS_BT_WAR + {WE_SET_HASTINGS_BT_WAR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "hastings_bt_war"}, +#endif + + {WE_SET_TM_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setTmLevel"}, + + {WE_SET_PHYMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "setphymode"}, + + {WE_SET_NSS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "nss"}, + + {WE_SET_LDPC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "ldpc"}, + + {WE_SET_TX_STBC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "tx_stbc"}, + + {WE_SET_RX_STBC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "rx_stbc"}, + + {WE_SET_SHORT_GI, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "shortgi"}, + + {WE_SET_RTSCTS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "enablertscts"}, + + {WE_SET_CHWIDTH, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "chwidth"}, + + {WE_SET_ANI_EN_DIS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "anienable"}, + + {WE_SET_ANI_POLL_PERIOD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "aniplen"}, + + {WE_SET_ANI_LISTEN_PERIOD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "anilislen"}, + + {WE_SET_ANI_OFDM_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "aniofdmlvl"}, + + {WE_SET_ANI_CCK_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "aniccklvl"}, + + {WE_SET_DYNAMIC_BW, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "cwmenable"}, + + {WE_SET_CTS_CBW, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "cts_cbw" }, + + {WE_SET_GTX_HT_MCS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "gtxHTMcs"}, + + {WE_SET_GTX_VHT_MCS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "gtxVHTMcs"}, + + {WE_SET_GTX_USRCFG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "gtxUsrCfg"}, + + {WE_SET_GTX_THRE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "gtxThre"}, + + {WE_SET_GTX_MARGIN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "gtxMargin"}, + + {WE_SET_GTX_STEP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "gtxStep"}, + + {WE_SET_GTX_MINTPC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "gtxMinTpc"}, + + {WE_SET_GTX_BWMASK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "gtxBWMask"}, + + {WE_SET_TX_CHAINMASK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "txchainmask"}, + + {WE_SET_RX_CHAINMASK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "rxchainmask"}, + + {WE_SET_11N_RATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "set11NRates"}, + + {WE_SET_VHT_RATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "set11ACRates"}, + + {WE_SET_AMPDU, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "ampdu"}, + + {WE_SET_AMSDU, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "amsdu"}, + + {WE_SET_TXPOW_2G, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "txpow2g"}, + + {WE_SET_TXPOW_5G, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "txpow5g"}, + +#ifdef FEATURE_FW_LOG_PARSING + /* Sub-cmds DBGLOG specific commands */ + {WE_DBGLOG_LOG_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "dl_loglevel"}, + + {WE_DBGLOG_VAP_ENABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "dl_vapon"}, + + {WE_DBGLOG_VAP_DISABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "dl_vapoff"}, + + {WE_DBGLOG_MODULE_ENABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "dl_modon"}, + + {WE_DBGLOG_MODULE_DISABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "dl_modoff"}, + + {WE_DBGLOG_MOD_LOG_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "dl_mod_loglevel"}, + + {WE_DBGLOG_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "dl_type"}, + + {WE_DBGLOG_REPORT_ENABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "dl_report"}, +#endif /* FEATURE_FW_LOG_PARSING */ + + {WE_SET_TXRX_FWSTATS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "txrx_fw_stats"}, + + {WE_SET_TXRX_STATS, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "txrx_stats"}, +#ifdef FW_THERMAL_THROTTLE_SUPPORT + {WE_SET_THERMAL_THROTTLE_CFG, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "set_thermal_cfg"}, +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + {WE_SET_BTCOEX_MODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_btc_mode" }, + {WE_SET_BTCOEX_RSSI_THRESHOLD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_btc_rssi" }, + {WE_TXRX_FWSTATS_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "txrx_fw_st_rst"}, + + {WE_PPS_PAID_MATCH, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "paid_match"}, + + {WE_PPS_GID_MATCH, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "gid_match"}, + + {WE_PPS_EARLY_TIM_CLEAR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "tim_clear"}, + + {WE_PPS_EARLY_DTIM_CLEAR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "dtim_clear"}, + + {WE_PPS_EOF_PAD_DELIM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "eof_delim"}, + + {WE_PPS_MACADDR_MISMATCH, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "mac_match"}, + + {WE_PPS_DELIM_CRC_FAIL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "delim_fail"}, + + {WE_PPS_GID_NSTS_ZERO, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "nsts_zero"}, + + {WE_PPS_RSSI_CHECK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "rssi_chk"}, + + {WE_PPS_5G_EBT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "5g_ebt"}, + + {WE_SET_HTSMPS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "htsmps"}, + + {WE_SET_QPOWER_MAX_PSPOLL_COUNT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_qpspollcnt"}, + + {WE_SET_QPOWER_MAX_TX_BEFORE_WAKE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_qtxwake"}, + + {WE_SET_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_qwakeintv"}, + + {WE_SET_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_qnodatapoll"}, + + /* handlers for MCC time quota and latency sub ioctls */ + {WE_MCC_CONFIG_LATENCY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setMccLatency"}, + + {WE_MCC_CONFIG_QUOTA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setMccQuota"}, + + {WE_SET_DEBUG_LOG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setDbgLvl"}, + + /* handlers for early_rx power save */ + {WE_SET_EARLY_RX_ADJUST_ENABLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "erx_enable"}, + + {WE_SET_EARLY_RX_TGT_BMISS_NUM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "erx_bmiss_val"}, + + {WE_SET_EARLY_RX_BMISS_SAMPLE_CYCLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "erx_bmiss_smpl"}, + + {WE_SET_EARLY_RX_SLOP_STEP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "erx_slop_step"}, + + {WE_SET_EARLY_RX_INIT_SLOP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "erx_init_slop"}, + + {WE_SET_EARLY_RX_ADJUST_PAUSE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "erx_adj_pause"}, + + {WE_SET_EARLY_RX_DRIFT_SAMPLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "erx_dri_sample"}, + + {WE_DUMP_STATS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "dumpStats"}, + + {WE_CLEAR_STATS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "clearStats"}, + + {WE_START_FW_PROFILE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "startProfile"}, + + {WE_SET_CHANNEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setChanChange" }, + + {WE_SET_CONC_SYSTEM_PREF, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setConcSysPref" }, + + {WE_SET_PDEV_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "pdev_reset" }, + + {WE_SET_MODULATED_DTIM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setModDTIM" }, + + {WLAN_PRIV_SET_NONE_GET_INT, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + ""}, + + /* handlers for sub-ioctl */ + {WE_GET_11D_STATE, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get11Dstate"}, + + {WE_GET_WLAN_DBG, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getwlandbg"}, + + {WE_GET_MAX_ASSOC, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getMaxAssoc"}, + + {WE_GET_SAP_AUTO_CHANNEL_SELECTION, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getAutoChannel"}, + + {WE_GET_CONCURRENCY_MODE, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getconcurrency"}, + + {WE_GET_NSS, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_nss"}, + + {WE_GET_LDPC, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_ldpc"}, + + {WE_GET_TX_STBC, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_tx_stbc"}, + + {WE_GET_RX_STBC, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_rx_stbc"}, + + {WE_GET_SHORT_GI, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_shortgi"}, + + {WE_GET_RTSCTS, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_rtscts"}, + + {WE_GET_CHWIDTH, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_chwidth"}, + + {WE_GET_ANI_EN_DIS, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_anienable"}, + + {WE_GET_ANI_POLL_PERIOD, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_aniplen"}, + + {WE_GET_ANI_LISTEN_PERIOD, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_anilislen"}, + + {WE_GET_ANI_OFDM_LEVEL, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_aniofdmlvl"}, + + {WE_GET_ANI_CCK_LEVEL, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_aniccklvl"}, + + {WE_GET_DYNAMIC_BW, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_cwmenable"}, + + {WE_GET_GTX_HT_MCS, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxHTMcs"}, + + {WE_GET_GTX_VHT_MCS, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxVHTMcs"}, + + {WE_GET_GTX_USRCFG, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxUsrCfg"}, + + {WE_GET_GTX_THRE, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxThre"}, + + {WE_GET_GTX_MARGIN, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxMargin"}, + + {WE_GET_GTX_STEP, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxStep"}, + + {WE_GET_GTX_MINTPC, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxMinTpc"}, + + {WE_GET_GTX_BWMASK, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gtxBWMask"}, + + {WE_GET_TX_CHAINMASK, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_txchainmask"}, + + {WE_GET_RX_CHAINMASK, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_rxchainmask"}, + + {WE_GET_11N_RATE, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_11nrate"}, + + {WE_GET_AMPDU, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_ampdu"}, + + {WE_GET_AMSDU, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_amsdu"}, + + {WE_GET_TXPOW_2G, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_txpow2g"}, + + {WE_GET_TXPOW_5G, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_txpow5g"}, + + {WE_GET_PPS_PAID_MATCH, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_paid_match"}, + + {WE_GET_PPS_GID_MATCH, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_gid_match"}, + + {WE_GET_PPS_EARLY_TIM_CLEAR, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_tim_clear"}, + + {WE_GET_PPS_EARLY_DTIM_CLEAR, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_dtim_clear"}, + + {WE_GET_PPS_EOF_PAD_DELIM, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_eof_delim"}, + + {WE_GET_PPS_MACADDR_MISMATCH, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_mac_match"}, + + {WE_GET_PPS_DELIM_CRC_FAIL, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_delim_fail"}, + + {WE_GET_PPS_GID_NSTS_ZERO, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_nsts_zero"}, + + {WE_GET_PPS_RSSI_CHECK, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_rssi_chk"}, + + {WE_GET_QPOWER_MAX_PSPOLL_COUNT, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_qpspollcnt"}, + + {WE_GET_QPOWER_MAX_TX_BEFORE_WAKE, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_qtxwake"}, + + {WE_GET_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_qwakeintv"}, + + {WE_GET_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_qnodatapoll"}, + + {WE_CAP_TSF, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "cap_tsf"}, + + {WE_GET_TEMPERATURE, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_temp"}, + + {WE_GET_DCM, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_dcm"}, + + {WE_GET_RANGE_EXT, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_range_ext"}, + + /* handlers for main ioctl */ + {WLAN_PRIV_SET_CHAR_GET_NONE, + IW_PRIV_TYPE_CHAR | 512, + 0, + ""}, + + /* handlers for sub-ioctl */ + {WE_WOWL_ADD_PTRN, + IW_PRIV_TYPE_CHAR | 512, + 0, + "wowlAddPtrn"}, + + {WE_WOWL_DEL_PTRN, + IW_PRIV_TYPE_CHAR | 512, + 0, + "wowlDelPtrn"}, + + /* handlers for sub-ioctl */ + {WE_NEIGHBOR_REPORT_REQUEST, + IW_PRIV_TYPE_CHAR | 512, + 0, + "neighbor"}, + + {WE_SET_AP_WPS_IE, + IW_PRIV_TYPE_CHAR | 512, + 0, + "set_ap_wps_ie"}, + +#ifdef WLAN_UNIT_TEST + {WE_UNIT_TEST, + IW_PRIV_TYPE_CHAR | 512, + 0, + "unit_test"}, +#endif + + /* handlers for main ioctl */ + {WLAN_PRIV_SET_THREE_INT_GET_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, + 0, + ""}, + + /* handlers for sub-ioctl */ + {WE_SET_WLAN_DBG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, + 0, + "setwlandbg"}, + +#ifdef CONFIG_DP_TRACE + /* handlers for sub-ioctl */ + {WE_SET_DP_TRACE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, + 0, + "set_dp_trace"}, +#endif + + {WE_SET_FW_TEST, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, + 0, "fw_test"}, + + /* handlers for main ioctl */ + {WLAN_PRIV_SET_NONE_GET_THREE_INT, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, + ""}, + + {WE_GET_TSF, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, + "get_tsf"}, + + {WE_SET_DUAL_MAC_SCAN_CONFIG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, + 0, + "set_scan_cfg"}, + + /* handlers for main ioctl */ + {WLAN_PRIV_GET_CHAR_SET_NONE, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + ""}, + + /* handlers for sub-ioctl */ + {WE_WLAN_VERSION, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "version"}, + + {WE_GET_STATS, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getStats"}, + + {WE_GET_SUSPEND_RESUME_STATS, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getSuspendStats"}, + + {WE_LIST_FW_PROFILE, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "listProfile"}, + + {WE_GET_STATES, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getHostStates"}, + + {WE_GET_CFG, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getConfig"}, + + {WE_GET_RSSI, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getRSSI"}, + + {WE_GET_WMM_STATUS, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getWmmStatus"}, + + {WE_GET_CHANNEL_LIST, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getChannelList"}, +#ifdef FEATURE_WLAN_TDLS + {WE_GET_TDLS_PEERS, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getTdlsPeers"}, +#endif +#ifdef WLAN_FEATURE_11W + {WE_GET_11W_INFO, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getPMFInfo"}, +#endif + {WE_GET_STA_CXN_INFO, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "get_cxn_info" }, + + {WE_GET_IBSS_STA_INFO, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getIbssSTAs"}, + + {WE_GET_PHYMODE, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getphymode"}, +#if defined(FEATURE_OEM_DATA_SUPPORT) || defined(WIFI_POS_CONVERGED) + {WE_GET_OEM_DATA_CAP, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getOemDataCap"}, +#endif + {WE_GET_SNR, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "getSNR"}, + + {WE_GET_BA_AGEING_TIMEOUT, + 0, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "get_ba_timeout"}, + + /* handlers for main ioctl */ + {WLAN_PRIV_SET_NONE_GET_NONE, + 0, + 0, + ""}, + + /* handlers for sub-ioctl */ + {WE_IBSS_GET_PEER_INFO_ALL, + 0, + 0, + "ibssPeerInfoAll"}, + + {WE_GET_FW_PROFILE_DATA, + 0, + 0, + "getProfileData"}, + + {WE_SET_REASSOC_TRIGGER, + 0, + 0, + "reassoc"}, + + {WE_STOP_OBSS_SCAN, + 0, + 0, + "stop_obss_scan"}, + /* handlers for main ioctl */ + {WLAN_PRIV_SET_VAR_INT_GET_NONE, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + ""}, + +#ifdef TRACE_RECORD + /* handlers for sub-ioctl */ + {WE_MTRACE_SELECTIVE_MODULE_LOG_ENABLE_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "setdumplog"}, + + {WE_MTRACE_DUMP_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "dumplog"}, +#endif + + {WE_POLICY_MANAGER_CINFO_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_cinfo"}, + +#ifdef MPC_UT_FRAMEWORK + {WE_POLICY_MANAGER_CLIST_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_clist"}, + + {WE_POLICY_MANAGER_DLIST_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_dlist"}, + + {WE_POLICY_MANAGER_DBS_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_dbs"}, + + {WE_POLICY_MANAGER_PCL_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_pcl"}, + + {WE_POLICY_MANAGER_ULIST_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_ulist"}, + + {WE_POLICY_MANAGER_QUERY_ACTION_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_query_action"}, + + {WE_POLICY_MANAGER_QUERY_ALLOW_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_query_allow"}, + + {WE_POLICY_MANAGER_SCENARIO_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_run_scenario"}, + + {WE_POLICY_SET_HW_MODE_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "pm_set_hw_mode"}, +#endif + {WE_UNIT_TEST_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "setUnitTestCmd"}, + + {WE_MAC_PWR_DEBUG_CMD, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "halPwrDebug"}, +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING + {WE_LED_FLASHING_PARAM, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "gpio_control"}, +#endif +#ifdef WLAN_DEBUG + {WE_SET_CHAN_AVOID, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "ch_avoid"}, +#endif + /* handlers for main ioctl */ + {WLAN_PRIV_FIPS_TEST, + IW_PRIV_TYPE_BYTE | WE_MAX_STR_LEN, + IW_PRIV_TYPE_BYTE | WE_MAX_STR_LEN, + "fips_test"}, + + /* handlers for main ioctl */ + {WLAN_PRIV_ADD_TSPEC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | HDD_WLAN_WMM_PARAM_COUNT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "addTspec"}, + + /* handlers for main ioctl */ + {WLAN_PRIV_DEL_TSPEC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "delTspec"}, + + /* handlers for main ioctl */ + {WLAN_PRIV_GET_TSPEC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getTspec"}, + + /* handlers for main ioctl - host offload */ + {WLAN_PRIV_SET_HOST_OFFLOAD, + IW_PRIV_TYPE_BYTE | sizeof(struct host_offload_req), + 0, + "setHostOffload"}, + + {WLAN_GET_WLAN_STATISTICS, + 0, + IW_PRIV_TYPE_BYTE | WE_MAX_STR_LEN, + "getWlanStats"}, + + {WLAN_SET_KEEPALIVE_PARAMS, + IW_PRIV_TYPE_BYTE | sizeof(struct keep_alive_req) | + IW_PRIV_SIZE_FIXED, + 0, + "setKeepAlive"}, +#ifdef WLAN_FEATURE_PACKET_FILTERING + {WLAN_SET_PACKET_FILTER_PARAMS, + IW_PRIV_TYPE_BYTE | + sizeof(struct pkt_filter_cfg), + 0, + "setPktFilter"}, +#endif +#ifdef FEATURE_WLAN_SCAN_PNO + {WLAN_SET_PNO, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + 0, + "setpno"}, +#endif + {WLAN_SET_BAND_CONFIG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "SETBAND"}, + + {WLAN_PRIV_SET_MCBC_FILTER, + 0, + 0, + "setMCBCFilter"}, + + {WLAN_GET_LINK_SPEED, + IW_PRIV_TYPE_CHAR | 18, + IW_PRIV_TYPE_CHAR | 5, + "getLinkSpeed"}, + +#ifdef FEATURE_WLM_STATS + {WLAN_GET_WLM_STATS, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, + "get_wlm_stats"}, +#endif + + /* handlers for main ioctl */ + {WLAN_PRIV_SET_TWO_INT_GET_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, + ""}, + + {WE_SET_SMPS_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "set_smps_param"}, + + {WLAN_SET_DOT11P_CHANNEL_SCHED, + IW_PRIV_TYPE_BYTE | sizeof(struct dot11p_channel_sched), + 0, "set_dot11p" }, + +#ifdef CONFIG_WLAN_DEBUG_CRASH_INJECT + {WE_SET_FW_CRASH_INJECT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "crash_inject"}, + +#endif +#if defined(WMI_INTERFACE_EVENT_LOGGING) || defined(FEATURE_HTC_CREDIT_HISTORY) + {WE_LOG_BUFFER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "log_buffer"}, + +#endif + {WE_SET_BA_AGEING_TIMEOUT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "set_ba_timeout"}, + +#ifdef WLAN_SUSPEND_RESUME_TEST + {WE_SET_WLAN_SUSPEND, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "wlan_suspend"}, + + {WE_SET_WLAN_RESUME, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "wlan_resume"}, +#endif + {WE_ENABLE_FW_PROFILE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "enableProfile"}, + + {WE_SET_FW_PROFILE_HIST_INTVL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "set_hist_intvl"}, + + {WE_SET_DUAL_MAC_FW_MODE_CONFIG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "set_fw_mode_cfg"}, +#ifdef CONFIG_DP_TRACE + {WE_DUMP_DP_TRACE_LEVEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "dump_dp_trace"}, +#endif +#ifdef FEATURE_MONITOR_MODE_SUPPORT + {WE_SET_MON_MODE_CHAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + 0, "setMonChan"}, +#endif + {WE_GET_ROAM_SYNCH_DELAY, + 0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "hostroamdelay"}, + + {WE_SET_11AX_RATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "set_11ax_rate"}, + + {WE_SET_DCM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "enable_dcm"}, + + {WE_SET_RANGE_EXT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "range_ext"}, + + {WLAN_PRIV_SET_FTIES, + IW_PRIV_TYPE_CHAR | MAX_FTIE_SIZE, + 0, + "set_ft_ies"}, + +#ifdef WLAN_FEATURE_MOTION_DETECTION + {WE_MOTION_DET_START_STOP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "mt_start"}, + + {WE_MOTION_DET_BASE_LINE_START_STOP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, + "mt_bl_start"}, + + {WE_MOTION_DET_CONFIG_PARAM, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "mt_config"}, + + {WE_MOTION_DET_BASE_LINE_CONFIG_PARAM, + IW_PRIV_TYPE_INT | MAX_VAR_ARGS, + 0, + "mt_bl_config"}, +#endif /* WLAN_FEATURE_MOTION_DETECTION */ +}; + +const struct iw_handler_def we_handler_def = { + .num_standard = 0, + .num_private = QDF_ARRAY_SIZE(we_private), + .num_private_args = QDF_ARRAY_SIZE(we_private_args), + + .standard = NULL, + .private = (iw_handler *) we_private, + .private_args = we_private_args, + .get_wireless_stats = NULL, +}; + +void hdd_register_wext(struct net_device *dev) +{ + hdd_enter_dev(dev); + + dev->wireless_handlers = &we_handler_def; + + hdd_exit(); +} + +void hdd_unregister_wext(struct net_device *dev) +{ + hdd_enter_dev(dev); + + rtnl_lock(); + dev->wireless_handlers = NULL; + rtnl_unlock(); + + hdd_exit(); +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_wmm.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_wmm.c new file mode 100644 index 0000000000000000000000000000000000000000..118415bf905ec39cf6105b8b4134e5f4360f7e81 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_wmm.c @@ -0,0 +1,2752 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: HDD WMM + * + * This module (wlan_hdd_wmm.h interface + wlan_hdd_wmm.c implementation) + * houses all the logic for WMM in HDD. + * + * On the control path, it has the logic to setup QoS, modify QoS and delete + * QoS (QoS here refers to a TSPEC). The setup QoS comes in two flavors: an + * explicit application invoked and an internal HDD invoked. The implicit QoS + * is for applications that do NOT call the custom QCT WLAN OIDs for QoS but + * which DO mark their traffic for priortization. It also has logic to start, + * update and stop the U-APSD trigger frame generation. It also has logic to + * read WMM related config parameters from the registry. + * + * On the data path, it has the logic to figure out the WMM AC of an egress + * packet and when to signal TL to serve a particular AC queue. It also has the + * logic to retrieve a packet based on WMM priority in response to a fetch from + * TL. + * + * The remaining functions are utility functions for information hiding. + */ + +/* Include files */ +#include +#include +#include +#include +#include +#include +#include +#include "osif_sync.h" +#include "os_if_fwol.h" +#include +#include +#include +#include +#include +#include +#include "sme_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "cfg_ucfg_api.h" + +#define HDD_WMM_UP_TO_AC_MAP_SIZE 8 +#define DSCP(x) x + +const uint8_t hdd_wmm_up_to_ac_map[] = { + SME_AC_BE, + SME_AC_BK, + SME_AC_BK, + SME_AC_BE, + SME_AC_VI, + SME_AC_VI, + SME_AC_VO, + SME_AC_VO +}; + +/** + * enum hdd_wmm_linuxac: AC/Queue Index values for Linux Qdisc to + * operate on different traffic. + */ +#ifdef QCA_LL_TX_FLOW_CONTROL_V2 +void wlan_hdd_process_peer_unauthorised_pause(struct hdd_adapter *adapter) +{ + /* Enable HI_PRIO queue */ + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_VO); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_VI); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_BE); + netif_stop_subqueue(adapter->dev, HDD_LINUX_AC_BK); + netif_wake_subqueue(adapter->dev, HDD_LINUX_AC_HI_PRIO); + +} +#else +void wlan_hdd_process_peer_unauthorised_pause(struct hdd_adapter *adapter) +{ +} +#endif + +/* Linux based UP -> AC Mapping */ +const uint8_t hdd_linux_up_to_ac_map[HDD_WMM_UP_TO_AC_MAP_SIZE] = { + HDD_LINUX_AC_BE, + HDD_LINUX_AC_BK, + HDD_LINUX_AC_BK, + HDD_LINUX_AC_BE, + HDD_LINUX_AC_VI, + HDD_LINUX_AC_VI, + HDD_LINUX_AC_VO, + HDD_LINUX_AC_VO +}; + +#ifndef WLAN_MDM_CODE_REDUCTION_OPT +/** + * hdd_wmm_enable_tl_uapsd() - function which decides whether and + * how to update UAPSD parameters in TL + * + * @qos_context: [in] the pointer the QoS instance control block + * + * Return: None + */ +static void hdd_wmm_enable_tl_uapsd(struct hdd_wmm_qos_context *qos_context) +{ + struct hdd_adapter *adapter = qos_context->adapter; + sme_ac_enum_type ac_type = qos_context->ac_type; + struct hdd_wmm_ac_status *ac = &adapter->hdd_wmm_status.ac_status[ac_type]; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + QDF_STATUS status; + uint32_t service_interval; + uint32_t suspension_interval; + enum sme_qos_wmm_dir_type direction; + bool psb; + uint32_t delayed_trgr_frm_int; + + /* The TSPEC must be valid */ + if (ac->is_tspec_valid == false) { + hdd_err("Invoked with invalid TSPEC"); + return; + } + /* determine the service interval */ + if (ac->tspec.min_service_interval) { + service_interval = ac->tspec.min_service_interval; + } else if (ac->tspec.max_service_interval) { + service_interval = ac->tspec.max_service_interval; + } else { + /* no service interval is present in the TSPEC */ + /* this is OK, there just won't be U-APSD */ + hdd_debug("No service interval supplied"); + service_interval = 0; + } + + /* determine the suspension interval & direction */ + suspension_interval = ac->tspec.suspension_interval; + direction = ac->tspec.ts_info.direction; + psb = ac->tspec.ts_info.psb; + + /* if we have previously enabled U-APSD, have any params changed? */ + if ((ac->is_uapsd_info_valid) && + (ac->uapsd_service_interval == service_interval) && + (ac->uapsd_suspension_interval == suspension_interval) && + (ac->uapsd_direction == direction) && + (ac->is_uapsd_enabled == psb)) { + hdd_debug("No change in U-APSD parameters"); + return; + } + + ucfg_mlme_get_tl_delayed_trgr_frm_int(hdd_ctx->psoc, + &delayed_trgr_frm_int); + /* everything is in place to notify TL */ + status = + sme_enable_uapsd_for_ac(ac_type, ac->tspec.ts_info.tid, + ac->tspec.ts_info.up, + service_interval, suspension_interval, + direction, psb, adapter->vdev_id, + delayed_trgr_frm_int); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to enable U-APSD for AC=%d", ac_type); + return; + } + /* stash away the parameters that were used */ + ac->is_uapsd_info_valid = true; + ac->uapsd_service_interval = service_interval; + ac->uapsd_suspension_interval = suspension_interval; + ac->uapsd_direction = direction; + ac->is_uapsd_enabled = psb; + + hdd_debug("Enabled UAPSD in TL srv_int=%d susp_int=%d dir=%d AC=%d", + service_interval, suspension_interval, direction, ac_type); + +} + +/** + * hdd_wmm_disable_tl_uapsd() - function which decides whether + * to disable UAPSD parameters in TL + * + * @qos_context: [in] the pointer the QoS instance control block + * + * Return: None + */ +static void hdd_wmm_disable_tl_uapsd(struct hdd_wmm_qos_context *qos_context) +{ + struct hdd_adapter *adapter = qos_context->adapter; + sme_ac_enum_type ac_type = qos_context->ac_type; + struct hdd_wmm_ac_status *ac = &adapter->hdd_wmm_status.ac_status[ac_type]; + QDF_STATUS status; + + /* have we previously enabled UAPSD? */ + if (ac->is_uapsd_info_valid == true) { + status = sme_disable_uapsd_for_ac(ac_type, adapter->vdev_id); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Failed to disable U-APSD for AC=%d", ac_type); + } else { + /* TL no longer has valid UAPSD info */ + ac->is_uapsd_info_valid = false; + hdd_debug("Disabled UAPSD in TL for AC=%d", ac_type); + } + } +} + +#endif + +/** + * hdd_wmm_free_context() - function which frees a QoS context + * + * @qos_context: [in] the pointer the QoS instance control block + * + * Return: None + */ +static void hdd_wmm_free_context(struct hdd_wmm_qos_context *qos_context) +{ + struct hdd_adapter *adapter; + + hdd_debug("Entered, context %pK", qos_context); + + if (unlikely((!qos_context) || + (HDD_WMM_CTX_MAGIC != qos_context->magic))) { + /* must have been freed in another thread */ + return; + } + /* get pointer to the adapter context */ + adapter = qos_context->adapter; + + /* take the mutex since we're manipulating the context list */ + mutex_lock(&adapter->hdd_wmm_status.mutex); + + /* make sure nobody thinks this is a valid context */ + qos_context->magic = 0; + + /* unlink the context */ + list_del(&qos_context->node); + + /* done manipulating the list */ + mutex_unlock(&adapter->hdd_wmm_status.mutex); + + /* reclaim memory */ + qdf_mem_free(qos_context); + +} + +#ifndef WLAN_MDM_CODE_REDUCTION_OPT +/** + * hdd_wmm_notify_app() - function which notifies an application + * of changes in state of it flow + * + * @qos_context: [in] the pointer the QoS instance control block + * + * Return: None + */ +#define MAX_NOTIFY_LEN 50 +static void hdd_wmm_notify_app(struct hdd_wmm_qos_context *qos_context) +{ + struct hdd_adapter *adapter; + union iwreq_data wrqu; + char buf[MAX_NOTIFY_LEN + 1]; + + hdd_debug("Entered, context %pK", qos_context); + + if (unlikely((!qos_context) || + (HDD_WMM_CTX_MAGIC != qos_context->magic))) { + hdd_err("Invalid QoS Context"); + return; + } + + /* create the event */ + memset(&wrqu, 0, sizeof(wrqu)); + memset(buf, 0, sizeof(buf)); + + snprintf(buf, MAX_NOTIFY_LEN, "QCOM: TS change[%u: %u]", + (unsigned int)qos_context->handle, + (unsigned int)qos_context->status); + + wrqu.data.pointer = buf; + wrqu.data.length = strlen(buf); + + /* get pointer to the adapter */ + adapter = qos_context->adapter; + + /* send the event */ + hdd_debug("Sending [%s]", buf); + wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu, buf); +} + +#ifdef FEATURE_WLAN_ESE +/** + * hdd_wmm_inactivity_timer_cb() - inactivity timer callback function + * + * @user_data: opaque user data registered with the timer. In the + * case of this timer, the associated wmm QoS context is registered. + * + * This timer handler function is called for every inactivity interval + * per AC. This function gets the current transmitted packets on the + * given AC, and checks if there was any TX activity from the previous + * interval. If there was no traffic then it would delete the TS that + * was negotiated on that AC. + * + * Return: None + */ +static void hdd_wmm_inactivity_timer_cb(void *user_data) +{ + struct hdd_wmm_qos_context *qos_context = user_data; + struct hdd_adapter *adapter; + struct hdd_wmm_ac_status *ac; + hdd_wlan_wmm_status_e status; + QDF_STATUS qdf_status; + uint32_t traffic_count = 0; + sme_ac_enum_type ac_type; + + if (!qos_context) { + hdd_err("invalid user data"); + return; + } + ac_type = qos_context->ac_type; + + adapter = qos_context->adapter; + if ((!adapter) || + (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { + hdd_err("invalid adapter: %pK", adapter); + return; + } + + ac = &adapter->hdd_wmm_status.ac_status[ac_type]; + + /* Get the Tx stats for this AC. */ + traffic_count = + adapter->hdd_stats.tx_rx_stats.tx_classified_ac[qos_context-> + ac_type]; + + hdd_warn("WMM inactivity check for AC=%d, count=%u, last=%u", + ac_type, traffic_count, ac->last_traffic_count); + if (ac->last_traffic_count == traffic_count) { + /* there is no traffic activity, delete the TSPEC for this AC */ + status = hdd_wmm_delts(adapter, qos_context->handle); + hdd_warn("Deleted TS on AC %d, due to inactivity with status = %d!!!", + ac_type, status); + } else { + ac->last_traffic_count = traffic_count; + if (ac->inactivity_timer.state == QDF_TIMER_STATE_STOPPED) { + /* Restart the timer */ + qdf_status = + qdf_mc_timer_start(&ac->inactivity_timer, + ac->inactivity_time); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Restarting inactivity timer failed on AC %d", + ac_type); + } + } else { + QDF_ASSERT(qdf_mc_timer_get_current_state + (&ac->inactivity_timer) == + QDF_TIMER_STATE_STOPPED); + } + } +} + +/** + * hdd_wmm_enable_inactivity_timer() - + * function to enable the traffic inactivity timer for the given AC + * + * @qos_context: [in] pointer to qos_context + * @inactivity_time: [in] value of the inactivity interval in millisecs + * + * When a QoS-Tspec is successfully setup, if the inactivity interval + * time specified in the AddTS parameters is non-zero, this function + * is invoked to start a traffic inactivity timer for the given AC. + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS +hdd_wmm_enable_inactivity_timer(struct hdd_wmm_qos_context *qos_context, + uint32_t inactivity_time) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct hdd_adapter *adapter = qos_context->adapter; + sme_ac_enum_type ac_type = qos_context->ac_type; + struct hdd_wmm_ac_status *ac; + + adapter = qos_context->adapter; + ac = &adapter->hdd_wmm_status.ac_status[ac_type]; + + qdf_status = qdf_mc_timer_init(&ac->inactivity_timer, + QDF_TIMER_TYPE_SW, + hdd_wmm_inactivity_timer_cb, + qos_context); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Initializing inactivity timer failed on AC %d", + ac_type); + return qdf_status; + } + /* Start the inactivity timer */ + qdf_status = qdf_mc_timer_start(&ac->inactivity_timer, + inactivity_time); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Starting inactivity timer failed on AC %d", + ac_type); + qdf_status = qdf_mc_timer_destroy(&ac->inactivity_timer); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("Failed to destroy inactivity timer"); + + return qdf_status; + } + ac->inactivity_time = inactivity_time; + /* Initialize the current tx traffic count on this AC */ + ac->last_traffic_count = + adapter->hdd_stats.tx_rx_stats.tx_classified_ac[qos_context-> + ac_type]; + qos_context->is_inactivity_timer_running = true; + return qdf_status; +} + +/** + * hdd_wmm_disable_inactivity_timer() - + * function to disable the traffic inactivity timer for the given AC. + * + * @qos_context: [in] pointer to qos_context + * + * This function is invoked to disable the traffic inactivity timer + * for the given AC. This is normally done when the TS is deleted. + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS +hdd_wmm_disable_inactivity_timer(struct hdd_wmm_qos_context *qos_context) +{ + struct hdd_adapter *adapter = qos_context->adapter; + sme_ac_enum_type ac_type = qos_context->ac_type; + struct hdd_wmm_ac_status *ac = &adapter->hdd_wmm_status.ac_status[ac_type]; + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + + /* Clear the timer and the counter */ + ac->inactivity_time = 0; + ac->last_traffic_count = 0; + + if (qos_context->is_inactivity_timer_running == true) { + qos_context->is_inactivity_timer_running = false; + qdf_status = qdf_mc_timer_stop(&ac->inactivity_timer); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + hdd_err("Failed to stop inactivity timer"); + return qdf_status; + } + qdf_status = qdf_mc_timer_destroy(&ac->inactivity_timer); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + hdd_err("Failed to destroy inactivity timer:Timer started"); + } + + return qdf_status; +} +#else + +static QDF_STATUS +hdd_wmm_disable_inactivity_timer(struct hdd_wmm_qos_context *qos_context) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_WLAN_ESE */ + +/** + * hdd_wmm_sme_callback() - callback for QoS notifications + * + * @mac_handle: [in] the MAC handle + * @context : [in] the HDD callback context + * @tspec_info : [in] the TSPEC params + * @sme_status : [in] the QoS related SME status + * @flow_id: [in] the unique identifier of the flow + * + * This callback is registered by HDD with SME for receiving QoS + * notifications. Even though this function has a static scope it + * gets called externally through some function pointer magic (so + * there is a need for rigorous parameter checking). + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS hdd_wmm_sme_callback(mac_handle_t mac_handle, + void *context, + struct sme_qos_wmmtspecinfo *tspec_info, + enum sme_qos_statustype sme_status, + uint32_t flow_id) +{ + struct hdd_wmm_qos_context *qos_context = context; + struct hdd_adapter *adapter; + sme_ac_enum_type ac_type; + struct hdd_wmm_ac_status *ac; + + hdd_debug("Entered, context %pK", qos_context); + + if (unlikely((!qos_context) || + (HDD_WMM_CTX_MAGIC != qos_context->magic))) { + hdd_err("Invalid QoS Context"); + return QDF_STATUS_E_FAILURE; + } + + adapter = qos_context->adapter; + ac_type = qos_context->ac_type; + ac = &adapter->hdd_wmm_status.ac_status[ac_type]; + + hdd_debug("status %d flowid %d info %pK", + sme_status, flow_id, tspec_info); + + switch (sme_status) { + + case SME_QOS_STATUS_SETUP_SUCCESS_IND: + hdd_debug("Setup is complete"); + + /* there will always be a TSPEC returned with this + * status, even if a TSPEC is not exchanged OTA + */ + if (tspec_info) { + ac->is_tspec_valid = true; + memcpy(&ac->tspec, + tspec_info, sizeof(ac->tspec)); + } + ac->is_access_allowed = true; + ac->was_access_granted = true; + ac->is_access_pending = false; + ac->has_access_failed = false; + + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + + hdd_debug("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_SETUP_SUCCESS; + hdd_wmm_notify_app(qos_context); + } + +#ifdef FEATURE_WLAN_ESE + /* Check if the inactivity interval is specified */ + if (tspec_info && tspec_info->inactivity_interval) { + hdd_debug("Inactivity timer value = %d for AC=%d", + tspec_info->inactivity_interval, ac_type); + hdd_wmm_enable_inactivity_timer(qos_context, + tspec_info-> + inactivity_interval); + } +#endif /* FEATURE_WLAN_ESE */ + + /* notify TL to enable trigger frames if necessary */ + hdd_wmm_enable_tl_uapsd(qos_context); + + break; + + case SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY: + hdd_debug("Setup is complete (U-APSD set previously)"); + + ac->is_access_allowed = true; + ac->was_access_granted = true; + ac->is_access_pending = false; + + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + + hdd_debug("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_UAPSD_EXISTING; + hdd_wmm_notify_app(qos_context); + } + + break; + + case SME_QOS_STATUS_SETUP_FAILURE_RSP: + hdd_err("Setup failed"); + /* QoS setup failed */ + + ac->is_access_pending = false; + ac->has_access_failed = true; + ac->is_access_allowed = false; + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + + hdd_debug("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_SETUP_FAILED; + + hdd_wmm_notify_app(qos_context); + } + + /* disable the inactivity timer */ + hdd_wmm_disable_inactivity_timer(qos_context); + + /* Setting up QoS Failed, QoS context can be released. + * SME is releasing this flow information and if HDD + * doesn't release this context, next time if + * application uses the same handle to set-up QoS, HDD + * (as it has QoS context for this handle) will issue + * Modify QoS request to SME but SME will reject as now + * it has no information for this flow. + */ + hdd_wmm_free_context(qos_context); + break; + + case SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP: + hdd_err("Setup Invalid Params, notify TL"); + /* QoS setup failed */ + ac->is_access_allowed = false; + + if (HDD_WMM_HANDLE_IMPLICIT == qos_context->handle) { + + /* we note the failure, but we also mark + * access as allowed so that the packets will + * flow. Note that the MAC will "do the right + * thing" + */ + ac->is_access_pending = false; + ac->has_access_failed = true; + ac->is_access_allowed = true; + + } else { + hdd_debug("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + hdd_wmm_notify_app(qos_context); + } + break; + + case SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP: + hdd_err("Setup failed, not a QoS AP"); + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + hdd_info("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM; + hdd_wmm_notify_app(qos_context); + } + break; + + case SME_QOS_STATUS_SETUP_REQ_PENDING_RSP: + hdd_debug("Setup pending"); + /* not a callback status -- ignore if we get it */ + break; + + case SME_QOS_STATUS_SETUP_MODIFIED_IND: + hdd_debug("Setup modified"); + if (tspec_info) { + /* update the TSPEC */ + ac->is_tspec_valid = true; + memcpy(&ac->tspec, + tspec_info, sizeof(ac->tspec)); + + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + hdd_debug("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_MODIFIED; + hdd_wmm_notify_app(qos_context); + } + /* need to tell TL to update its UAPSD handling */ + hdd_wmm_enable_tl_uapsd(qos_context); + } + break; + + case SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP: + if (HDD_WMM_HANDLE_IMPLICIT == qos_context->handle) { + + /* this was triggered by implicit QoS so we + * know packets are pending + */ + ac->is_access_pending = false; + ac->was_access_granted = true; + ac->is_access_allowed = true; + + } else { + hdd_debug("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_NO_UAPSD; + hdd_wmm_notify_app(qos_context); + } + break; + + case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING: + /* nothing to do for now */ + break; + + case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED: + hdd_err("Setup successful but U-APSD failed"); + + if (HDD_WMM_HANDLE_IMPLICIT == qos_context->handle) { + + /* QoS setup was successful but setting U=APSD + * failed. Since the OTA part of the request + * was successful, we don't mark this as a + * failure. the packets will flow. Note that + * the MAC will "do the right thing" + */ + ac->was_access_granted = true; + ac->is_access_allowed = true; + ac->has_access_failed = false; + ac->is_access_pending = false; + + } else { + hdd_info("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_SETUP_UAPSD_SET_FAILED; + hdd_wmm_notify_app(qos_context); + } + + /* Since U-APSD portion failed disabled trigger frame + * generation + */ + hdd_wmm_disable_tl_uapsd(qos_context); + + break; + + case SME_QOS_STATUS_RELEASE_SUCCESS_RSP: + hdd_debug("Release is complete"); + + if (tspec_info) { + hdd_debug("flows still active"); + + /* there is still at least one flow active for + * this AC so update the AC state + */ + memcpy(&ac->tspec, + tspec_info, sizeof(ac->tspec)); + + /* need to tell TL to update its UAPSD handling */ + hdd_wmm_enable_tl_uapsd(qos_context); + } else { + hdd_debug("last flow"); + + /* this is the last flow active for this AC so + * update the AC state + */ + ac->is_tspec_valid = false; + + /* DELTS is successful, do not allow */ + ac->is_access_allowed = false; + + /* need to tell TL to update its UAPSD handling */ + hdd_wmm_disable_tl_uapsd(qos_context); + } + + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + hdd_debug("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_RELEASE_SUCCESS; + hdd_wmm_notify_app(qos_context); + } + /* disable the inactivity timer */ + hdd_wmm_disable_inactivity_timer(qos_context); + + /* we are done with this flow */ + hdd_wmm_free_context(qos_context); + break; + + case SME_QOS_STATUS_RELEASE_FAILURE_RSP: + hdd_debug("Release failure"); + + /* we don't need to update our state or TL since + * nothing has changed + */ + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + hdd_debug("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_RELEASE_FAILED; + hdd_wmm_notify_app(qos_context); + } + + break; + + case SME_QOS_STATUS_RELEASE_QOS_LOST_IND: + hdd_debug("QOS Lost indication received"); + + /* current TSPEC is no longer valid */ + ac->is_tspec_valid = false; + /* AP has sent DELTS, do not allow */ + ac->is_access_allowed = false; + + /* need to tell TL to update its UAPSD handling */ + hdd_wmm_disable_tl_uapsd(qos_context); + + if (HDD_WMM_HANDLE_IMPLICIT == qos_context->handle) { + /* we no longer have implicit access granted */ + ac->was_access_granted = false; + ac->has_access_failed = false; + } else { + hdd_debug("Explicit Qos, notifying user space"); + + /* this was triggered by an application */ + qos_context->status = HDD_WLAN_WMM_STATUS_LOST; + hdd_wmm_notify_app(qos_context); + } + + /* disable the inactivity timer */ + hdd_wmm_disable_inactivity_timer(qos_context); + + /* we are done with this flow */ + hdd_wmm_free_context(qos_context); + break; + + case SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP: + hdd_debug("Release pending"); + /* not a callback status -- ignore if we get it */ + break; + + case SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP: + hdd_err("Release Invalid Params"); + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM; + hdd_wmm_notify_app(qos_context); + } + break; + + case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND: + hdd_debug("Modification is complete, notify TL"); + + /* there will always be a TSPEC returned with this + * status, even if a TSPEC is not exchanged OTA + */ + if (tspec_info) { + ac->is_tspec_valid = true; + memcpy(&ac->tspec, + tspec_info, sizeof(ac->tspec)); + } + + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS; + hdd_wmm_notify_app(qos_context); + } + /* notify TL to enable trigger frames if necessary */ + hdd_wmm_enable_tl_uapsd(qos_context); + + break; + + case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY: + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_UAPSD_EXISTING; + hdd_wmm_notify_app(qos_context); + } + break; + + case SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP: + /* the flow modification failed so we'll leave in + * place whatever existed beforehand + */ + + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_MODIFY_FAILED; + hdd_wmm_notify_app(qos_context); + } + break; + + case SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP: + hdd_debug("modification pending"); + /* not a callback status -- ignore if we get it */ + break; + + case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP: + /* the flow modification was successful but no QoS + * changes required + */ + + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_NO_UAPSD; + hdd_wmm_notify_app(qos_context); + } + break; + + case SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP: + /* invalid params -- notify the application */ + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_MODIFY_FAILED_BAD_PARAM; + hdd_wmm_notify_app(qos_context); + } + break; + + case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_PENDING: + /* nothing to do for now. when APSD is established we'll have work to do */ + break; + + case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED: + hdd_err("Modify successful but U-APSD failed"); + + /* QoS modification was successful but setting U=APSD + * failed. This will always be an explicit QoS + * instance, so all we can do is notify the + * application and let it clean up. + */ + if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) { + /* this was triggered by an application */ + qos_context->status = + HDD_WLAN_WMM_STATUS_MODIFY_UAPSD_SET_FAILED; + hdd_wmm_notify_app(qos_context); + } + /* Since U-APSD portion failed disabled trigger frame + * generation + */ + hdd_wmm_disable_tl_uapsd(qos_context); + + break; + + case SME_QOS_STATUS_HANDING_OFF: + /* no roaming so we won't see this */ + break; + + case SME_QOS_STATUS_OUT_OF_APSD_POWER_MODE_IND: + /* need to tell TL to stop trigger frame generation */ + hdd_wmm_disable_tl_uapsd(qos_context); + break; + + case SME_QOS_STATUS_INTO_APSD_POWER_MODE_IND: + /* need to tell TL to start sending trigger frames again */ + hdd_wmm_enable_tl_uapsd(qos_context); + break; + + default: + hdd_err("unexpected SME Status=%d", sme_status); + QDF_ASSERT(0); + } + + /* if Tspec only allows downstream traffic then access is not + * allowed + */ + if (ac->is_tspec_valid && + (ac->tspec.ts_info.direction == + SME_QOS_WMM_TS_DIR_DOWNLINK)) { + ac->is_access_allowed = false; + } + /* if we have valid Tpsec or if ACM bit is not set, allow access */ + if ((ac->is_tspec_valid && + (ac->tspec.ts_info.direction != + SME_QOS_WMM_TS_DIR_DOWNLINK)) || !ac->is_access_required) { + ac->is_access_allowed = true; + } + + hdd_debug("complete, access for TL AC %d is%sallowed", + ac_type, ac->is_access_allowed ? " " : " not "); + + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * hdd_wmmps_helper() - Function to set uapsd psb dynamically + * + * @adapter: [in] pointer to adapter structure + * @ptr: [in] pointer to command buffer + * + * Return: Zero on success, appropriate error on failure. + */ +int hdd_wmmps_helper(struct hdd_adapter *adapter, uint8_t *ptr) +{ + if (!adapter) { + hdd_err("adapter is NULL"); + return -EINVAL; + } + if (!ptr) { + hdd_err("ptr is NULL"); + return -EINVAL; + } + /* convert ASCII to integer */ + adapter->configured_psb = ptr[9] - '0'; + adapter->psb_changed = HDD_PSB_CHANGED; + + return 0; +} + +/** + * __hdd_wmm_do_implicit_qos() - Function which will attempt to setup + * QoS for any AC requiring it. + * @qos_context: the QoS context to operate against + * + * Return: none + */ +static void __hdd_wmm_do_implicit_qos(struct hdd_wmm_qos_context *qos_context) +{ + struct hdd_adapter *adapter; + sme_ac_enum_type ac_type; + struct hdd_wmm_ac_status *ac; +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + enum sme_qos_statustype sme_status; +#endif + struct sme_qos_wmmtspecinfo tspec; + struct hdd_context *hdd_ctx; + mac_handle_t mac_handle; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t dir_ac, mask = 0; + uint16_t nom_msdu_size_ac = 0; + uint32_t rate_ac = 0; + uint16_t sba_ac = 0; + uint32_t uapsd_value = 0; + bool is_ts_burst_enable; + enum mlme_ts_info_ack_policy ack_policy; + + hdd_debug("Entered, context %pK", qos_context); + + adapter = qos_context->adapter; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + mac_handle = hdd_ctx->mac_handle; + + ac_type = qos_context->ac_type; + ac = &adapter->hdd_wmm_status.ac_status[ac_type]; + + hdd_debug("adapter %pK ac_type %d", adapter, ac_type); + + if (!ac->is_access_needed) { + hdd_err("AC %d doesn't need service", ac_type); + qos_context->magic = 0; + qdf_mem_free(qos_context); + return; + } + + ac->is_access_pending = true; + ac->is_access_needed = false; + + memset(&tspec, 0, sizeof(tspec)); + + tspec.ts_info.psb = adapter->configured_psb; + + switch (ac_type) { + case SME_AC_VO: + tspec.ts_info.up = SME_QOS_WMM_UP_VO; + /* Check if there is any valid configuration from framework */ + if (HDD_PSB_CFG_INVALID == adapter->configured_psb) { + status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc, + &mask); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_mask failed"); + return; + } + tspec.ts_info.psb = (mask & SME_QOS_UAPSD_VO) ? 1 : 0; + } + status = ucfg_mlme_get_wmm_dir_ac_vo(hdd_ctx->psoc, + &dir_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get infra_dir_ac_vo failed"); + return; + } + tspec.ts_info.direction = dir_ac; + + tspec.ts_info.tid = 255; + + status = ucfg_mlme_get_wmm_uapsd_vo_srv_intv(hdd_ctx->psoc, + &uapsd_value); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Get uapsd_srv_intv failed"); + return; + } + tspec.min_service_interval = uapsd_value; + + status = ucfg_mlme_get_wmm_uapsd_vo_sus_intv(hdd_ctx->psoc, + &uapsd_value); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Get uapsd_vo_sus_intv failed"); + return; + } + tspec.suspension_interval = uapsd_value; + + status = ucfg_mlme_get_wmm_mean_data_rate_ac_vo(hdd_ctx->psoc, + &rate_ac); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Get mean_data_rate_ac_vo failed"); + return; + } + tspec.mean_data_rate = rate_ac; + + status = ucfg_mlme_get_wmm_min_phy_rate_ac_vo(hdd_ctx->psoc, + &rate_ac); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Get min_phy_rate_ac_vo failed"); + return; + } + tspec.min_phy_rate = rate_ac; + + status = ucfg_mlme_get_wmm_nom_msdu_size_ac_vo(hdd_ctx->psoc, + &nom_msdu_size_ac); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Get nom_msdu_size_ac_vo failed"); + return; + } + tspec.nominal_msdu_size = nom_msdu_size_ac; + + status = ucfg_mlme_get_wmm_sba_ac_vo(hdd_ctx->psoc, + &sba_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get sba_ac_vo failed"); + return; + } + tspec.surplus_bw_allowance = sba_ac; + + break; + case SME_AC_VI: + tspec.ts_info.up = SME_QOS_WMM_UP_VI; + /* Check if there is any valid configuration from framework */ + if (HDD_PSB_CFG_INVALID == adapter->configured_psb) { + status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc, + &mask); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_mask failed"); + return; + } + tspec.ts_info.psb = (mask & SME_QOS_UAPSD_VI) ? 1 : 0; + } + status = ucfg_mlme_get_wmm_dir_ac_vi( + hdd_ctx->psoc, &dir_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get infra_dir_ac_vi failed"); + return; + } + tspec.ts_info.direction = dir_ac; + + tspec.ts_info.tid = 255; + status = ucfg_mlme_get_wmm_uapsd_vi_srv_intv( + hdd_ctx->psoc, &uapsd_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_vi_srv_intv failed"); + return; + } + tspec.min_service_interval = uapsd_value; + + status = ucfg_mlme_get_wmm_uapsd_vi_sus_intv( + hdd_ctx->psoc, &uapsd_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_vi_sus_intv failed"); + return; + } + tspec.suspension_interval = uapsd_value; + + status = ucfg_mlme_get_wmm_mean_data_rate_ac_vi( + hdd_ctx->psoc, &rate_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get mean_data_rate_ac_vi failed"); + return; + } + tspec.mean_data_rate = rate_ac; + + status = ucfg_mlme_get_wmm_min_phy_rate_ac_vi( + hdd_ctx->psoc, &rate_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get min_phy_rate_ac_vi failed"); + return; + } + tspec.min_phy_rate = rate_ac; + + status = ucfg_mlme_get_wmm_nom_msdu_size_ac_vi( + hdd_ctx->psoc, &nom_msdu_size_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get nom_msdu_size_ac_vi failed"); + return; + } + tspec.nominal_msdu_size = nom_msdu_size_ac; + + status = ucfg_mlme_get_wmm_sba_ac_vi( + hdd_ctx->psoc, &sba_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get sba_ac_vi failed"); + return; + } + tspec.surplus_bw_allowance = sba_ac; + + break; + default: + case SME_AC_BE: + tspec.ts_info.up = SME_QOS_WMM_UP_BE; + /* Check if there is any valid configuration from framework */ + if (HDD_PSB_CFG_INVALID == adapter->configured_psb) { + status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc, + &mask); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_mask failed"); + return; + } + tspec.ts_info.psb = (mask & SME_QOS_UAPSD_BE) ? 1 : 0; + } + status = ucfg_mlme_get_wmm_dir_ac_be(hdd_ctx->psoc, &dir_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get infra_dir_ac_be failed"); + return; + } + tspec.ts_info.direction = dir_ac; + + tspec.ts_info.tid = 255; + status = ucfg_mlme_get_wmm_uapsd_be_srv_intv(hdd_ctx->psoc, + &uapsd_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_vi_srv_intv failed"); + return; + } + tspec.min_service_interval = uapsd_value; + + status = ucfg_mlme_get_wmm_uapsd_be_sus_intv(hdd_ctx->psoc, + &uapsd_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_vi_sus_intv failed"); + return; + } + tspec.suspension_interval = uapsd_value; + + status = ucfg_mlme_get_wmm_mean_data_rate_ac_be(hdd_ctx->psoc, + &rate_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get mean_data_rate_ac_be failed"); + return; + } + tspec.mean_data_rate = rate_ac; + + status = ucfg_mlme_get_wmm_min_phy_rate_ac_be(hdd_ctx->psoc, + &rate_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get min_phy_rate_ac_be failed"); + return; + } + tspec.min_phy_rate = rate_ac; + + status = ucfg_mlme_get_wmm_nom_msdu_size_ac_be(hdd_ctx->psoc, + &nom_msdu_size_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get nom_msdu_size_ac_be failed"); + return; + } + tspec.nominal_msdu_size = nom_msdu_size_ac; + + status = ucfg_mlme_get_wmm_sba_ac_be(hdd_ctx->psoc, &sba_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get sba_ac_be failed"); + return; + } + tspec.surplus_bw_allowance = sba_ac; + + break; + case SME_AC_BK: + tspec.ts_info.up = SME_QOS_WMM_UP_BK; + /* Check if there is any valid configuration from framework */ + if (HDD_PSB_CFG_INVALID == adapter->configured_psb) { + status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc, + &mask); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_mask failed"); + return; + } + tspec.ts_info.psb = (mask & SME_QOS_UAPSD_BK) ? 1 : 0; + } + + status = ucfg_mlme_get_wmm_dir_ac_bk(hdd_ctx->psoc, &dir_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get infra_dir_ac_bk failed"); + return; + } + tspec.ts_info.direction = dir_ac; + + tspec.ts_info.tid = 255; + status = ucfg_mlme_get_wmm_uapsd_bk_srv_intv(hdd_ctx->psoc, + &uapsd_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_bk_srv_intv failed"); + return; + } + tspec.min_service_interval = uapsd_value; + + status = ucfg_mlme_get_wmm_uapsd_bk_sus_intv(hdd_ctx->psoc, + &uapsd_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_bk_sus_intv failed"); + return; + } + tspec.suspension_interval = uapsd_value; + + status = ucfg_mlme_get_wmm_mean_data_rate_ac_bk(hdd_ctx->psoc, + &rate_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get mean_data_rate_ac_bk failed"); + return; + } + tspec.mean_data_rate = rate_ac; + + status = ucfg_mlme_get_wmm_min_phy_rate_ac_bk(hdd_ctx->psoc, + &rate_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get min_phy_rate_ac_bk failed"); + return; + } + tspec.min_phy_rate = rate_ac; + + status = + ucfg_mlme_get_wmm_nom_msdu_size_ac_bk(hdd_ctx->psoc, + &nom_msdu_size_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get nom_msdu_size_ac_bk failed"); + return; + } + tspec.nominal_msdu_size = nom_msdu_size_ac; + + status = ucfg_mlme_get_wmm_sba_ac_bk(hdd_ctx->psoc, &sba_ac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get sba_ac_bk failed"); + return; + } + tspec.surplus_bw_allowance = sba_ac; + + break; + } +#ifdef FEATURE_WLAN_ESE + ucfg_mlme_get_inactivity_interval(hdd_ctx->psoc, &uapsd_value); + tspec.inactivity_interval = uapsd_value; +#endif + ucfg_mlme_get_is_ts_burst_size_enable(hdd_ctx->psoc, + &is_ts_burst_enable); + tspec.ts_info.burst_size_defn = is_ts_burst_enable; + + ucfg_mlme_get_ts_info_ack_policy(hdd_ctx->psoc, &ack_policy); + switch (ack_policy) { + case TS_INFO_ACK_POLICY_NORMAL_ACK: + tspec.ts_info.ack_policy = + SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK; + break; + + case TS_INFO_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK: + tspec.ts_info.ack_policy = + SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK; + break; + + default: + /* unknown */ + tspec.ts_info.ack_policy = + SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK; + } + + if (tspec.ts_info.ack_policy == + SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK) { + if (!sme_qos_is_ts_info_ack_policy_valid(mac_handle, &tspec, + adapter->vdev_id)) { + tspec.ts_info.ack_policy = + SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK; + } + } + + mutex_lock(&adapter->hdd_wmm_status.mutex); + list_add(&qos_context->node, &adapter->hdd_wmm_status.context_list); + mutex_unlock(&adapter->hdd_wmm_status.mutex); + +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_status = sme_qos_setup_req(mac_handle, + adapter->vdev_id, + &tspec, + hdd_wmm_sme_callback, + qos_context, + tspec.ts_info.up, + &qos_context->flow_id); + + hdd_debug("sme_qos_setup_req returned %d flowid %d", + sme_status, qos_context->flow_id); + + /* need to check the return values and act appropriately */ + switch (sme_status) { + case SME_QOS_STATUS_SETUP_REQ_PENDING_RSP: + case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING: + /* setup is pending, so no more work to do now. all + * further work will be done in hdd_wmm_sme_callback() + */ + hdd_debug("Setup is pending, no further work"); + + break; + + case SME_QOS_STATUS_SETUP_FAILURE_RSP: + /* disable the inactivity timer */ + hdd_wmm_disable_inactivity_timer(qos_context); + + /* we can't tell the difference between when a request + * fails because AP rejected it versus when SME + * encountered an internal error. in either case SME + * won't ever reference this context so free the + * record + */ + hdd_wmm_free_context(qos_context); + + /* fall through and start packets flowing */ + case SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP: + /* no ACM in effect, no need to setup U-APSD */ + case SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY: + /* no ACM in effect, U-APSD is desired but was already setup */ + + /* for these cases everything is already setup so we + * can signal TL that it has work to do + */ + hdd_debug("Setup is complete, notify TL"); + + ac->is_access_allowed = true; + ac->was_access_granted = true; + ac->is_access_pending = false; + + break; + + default: + hdd_err("unexpected SME Status=%d", sme_status); + QDF_ASSERT(0); + } +#endif + +} + +/** + * hdd_wmm_do_implicit_qos() - SSR wraper function for hdd_wmm_do_implicit_qos + * @work: pointer to work_struct + * + * Return: none + */ +static void hdd_wmm_do_implicit_qos(struct work_struct *work) +{ + struct hdd_wmm_qos_context *qos_ctx = + container_of(work, struct hdd_wmm_qos_context, + implicit_qos_work); + struct osif_vdev_sync *vdev_sync; + + if (qos_ctx->magic != HDD_WMM_CTX_MAGIC) { + hdd_err("Invalid QoS Context"); + return; + } + + if (osif_vdev_sync_op_start(qos_ctx->adapter->dev, &vdev_sync)) + return; + + __hdd_wmm_do_implicit_qos(qos_ctx); + + osif_vdev_sync_op_stop(vdev_sync); +} + +QDF_STATUS hdd_send_dscp_up_map_to_fw(struct hdd_adapter *adapter) +{ + uint32_t *dscp_to_up_map = adapter->dscp_to_up_map; + struct wlan_objmgr_vdev *vdev = adapter->vdev; + int ret; + + if (vdev) { + /* Send DSCP to TID map table to FW */ + ret = os_if_fwol_send_dscp_up_map_to_fw(vdev, dscp_to_up_map); + if (ret && ret != -EOPNOTSUPP) + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_fill_dscp_to_up_map() - Fill up dscp_to_up_map table with default values + * @dscp_to_up_map: Array of DSCP-to-UP map + * + * This function will fill up the DSCP-to-UP map table with default values. + * + * Return: QDF_STATUS enumeration + */ +static inline void hdd_fill_dscp_to_up_map( + enum sme_qos_wmmuptype *dscp_to_up_map) +{ + uint8_t dscp; + + /* + * DSCP to User Priority Lookup Table + * By default use the 3 Precedence bits of DSCP as the User Priority + * + * In case of changing the default map values, need to take care of + * hdd_custom_dscp_up_map as well. + */ + for (dscp = 0; dscp <= WLAN_MAX_DSCP; dscp++) + dscp_to_up_map[dscp] = dscp >> 3; + + /* Special case for Expedited Forwarding (DSCP 46) in default mapping */ + dscp_to_up_map[DSCP(46)] = SME_QOS_WMM_UP_VO; +} + +#ifdef WLAN_CUSTOM_DSCP_UP_MAP +/** + * hdd_custom_dscp_up_map() - Customize dscp_to_up_map based on RFC8325 + * @dscp_to_up_map: Array of DSCP-to-UP map + * + * This function will customize the DSCP-to-UP map table based on RFC8325.. + * + * Return: QDF_STATUS enumeration + */ +static inline QDF_STATUS hdd_custom_dscp_up_map( + enum sme_qos_wmmuptype *dscp_to_up_map) +{ + /* + * Customizing few of DSCP to UP mapping based on RFC8325, + * those are different from default hdd_fill_dscp_to_up_map values. + * So, below changes are always relative to hdd_fill_dscp_to_up_map. + */ + dscp_to_up_map[DSCP(10)] = SME_QOS_WMM_UP_BE; + dscp_to_up_map[DSCP(12)] = SME_QOS_WMM_UP_BE; + dscp_to_up_map[DSCP(14)] = SME_QOS_WMM_UP_BE; + dscp_to_up_map[DSCP(16)] = SME_QOS_WMM_UP_BE; + + dscp_to_up_map[DSCP(18)] = SME_QOS_WMM_UP_EE; + dscp_to_up_map[DSCP(20)] = SME_QOS_WMM_UP_EE; + dscp_to_up_map[DSCP(22)] = SME_QOS_WMM_UP_EE; + + dscp_to_up_map[DSCP(24)] = SME_QOS_WMM_UP_CL; + dscp_to_up_map[DSCP(26)] = SME_QOS_WMM_UP_CL; + dscp_to_up_map[DSCP(28)] = SME_QOS_WMM_UP_CL; + dscp_to_up_map[DSCP(30)] = SME_QOS_WMM_UP_CL; + + dscp_to_up_map[DSCP(44)] = SME_QOS_WMM_UP_VO; + + dscp_to_up_map[DSCP(48)] = SME_QOS_WMM_UP_NC; + + return QDF_STATUS_SUCCESS; +} +#else +static inline QDF_STATUS hdd_custom_dscp_up_map( + enum sme_qos_wmmuptype *dscp_to_up_map) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif /* WLAN_CUSTOM_DSCP_UP_MAP */ + +/** + * hdd_wmm_init() - initialize the WMM DSCP configuation + * @adapter : [in] pointer to Adapter context + * + * This function will initialize the WMM DSCP configuation of an + * adapter to an initial state. The configuration can later be + * overwritten via application APIs or via QoS Map sent OTA. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_init(struct hdd_adapter *adapter) +{ + enum sme_qos_wmmuptype *dscp_to_up_map = adapter->dscp_to_up_map; + struct wlan_objmgr_psoc *psoc = adapter->hdd_ctx->psoc; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!psoc) { + hdd_err("Invalid psoc handle"); + return QDF_STATUS_E_FAILURE; + } + + hdd_fill_dscp_to_up_map(dscp_to_up_map); + + if (hdd_custom_dscp_up_map(dscp_to_up_map) == QDF_STATUS_SUCCESS) { + /* Send DSCP to TID map table to FW */ + status = hdd_send_dscp_up_map_to_fw(adapter); + } + + return status; +} + +/** + * hdd_wmm_adapter_init() - initialize the WMM configuration of an adapter + * @adapter: [in] pointer to Adapter context + * + * This function will initialize the WMM configuation and status of an + * adapter to an initial state. The configuration can later be + * overwritten via application APIs + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_adapter_init(struct hdd_adapter *adapter) +{ + struct hdd_wmm_ac_status *ac_status; + sme_ac_enum_type ac_type; + + hdd_enter(); + + adapter->hdd_wmm_status.qap = false; + INIT_LIST_HEAD(&adapter->hdd_wmm_status.context_list); + mutex_init(&adapter->hdd_wmm_status.mutex); + + for (ac_type = 0; ac_type < WLAN_MAX_AC; ac_type++) { + ac_status = &adapter->hdd_wmm_status.ac_status[ac_type]; + ac_status->is_access_required = false; + ac_status->is_access_needed = false; + ac_status->is_access_pending = false; + ac_status->has_access_failed = false; + ac_status->was_access_granted = false; + ac_status->is_access_allowed = false; + ac_status->is_tspec_valid = false; + ac_status->is_uapsd_info_valid = false; + } + /* Invalid value(0xff) to indicate psb not configured through + * framework initially. + */ + adapter->configured_psb = HDD_PSB_CFG_INVALID; + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_wmm_adapter_clear() - Function which will clear the WMM status + * for all the ACs + * + * @adapter: [in] pointer to Adapter context + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_adapter_clear(struct hdd_adapter *adapter) +{ + struct hdd_wmm_ac_status *ac_status; + sme_ac_enum_type ac_type; + + hdd_enter(); + for (ac_type = 0; ac_type < WLAN_MAX_AC; ac_type++) { + ac_status = &adapter->hdd_wmm_status.ac_status[ac_type]; + ac_status->is_access_required = false; + ac_status->is_access_needed = false; + ac_status->is_access_pending = false; + ac_status->has_access_failed = false; + ac_status->was_access_granted = false; + ac_status->is_access_allowed = false; + ac_status->is_tspec_valid = false; + ac_status->is_uapsd_info_valid = false; + } + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_wmm_close() - WMM close function + * @adapter: [in] pointer to adapter context + * + * Function which will perform any necessary work to to clean up the + * WMM functionality prior to the kernel module unload. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_adapter_close(struct hdd_adapter *adapter) +{ + struct hdd_wmm_qos_context *qos_context; + + hdd_enter(); + + /* free any context records that we still have linked */ + while (!list_empty(&adapter->hdd_wmm_status.context_list)) { + qos_context = + list_first_entry(&adapter->hdd_wmm_status.context_list, + struct hdd_wmm_qos_context, node); + + hdd_wmm_disable_inactivity_timer(qos_context); + + if (qos_context->handle == HDD_WMM_HANDLE_IMPLICIT + && qos_context->magic == HDD_WMM_CTX_MAGIC) + cds_flush_work(&qos_context->implicit_qos_work); + + hdd_wmm_free_context(qos_context); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_check_and_upgrade_udp_qos() - Check and upgrade the qos for UDP packets + * if the current set priority is below the + * pre-configured threshold for upgrade. + * @adapter: [in] pointer to the adapter context (Should not be invalid) + * @skb: [in] pointer to the packet to be transmitted + * @user_pri: [out] priority set for this packet + * + * This function checks if the packet is a UDP packet and upgrades its + * priority if its below the pre-configured upgrade threshold. + * The upgrade order is as below: + * BK -> BE -> VI -> VO + * + * Return: none + */ +static inline void +hdd_check_and_upgrade_udp_qos(struct hdd_adapter *adapter, + qdf_nbuf_t skb, + enum sme_qos_wmmuptype *user_pri) +{ + /* Upgrade UDP pkt priority alone */ + if (!(qdf_nbuf_is_ipv4_udp_pkt(skb) || qdf_nbuf_is_ipv6_udp_pkt(skb))) + return; + + switch (adapter->upgrade_udp_qos_threshold) { + case QCA_WLAN_AC_BK: + break; + case QCA_WLAN_AC_BE: + if (*user_pri == qca_wlan_ac_to_sme_qos(QCA_WLAN_AC_BK)) + *user_pri = qca_wlan_ac_to_sme_qos(QCA_WLAN_AC_BE); + + break; + case QCA_WLAN_AC_VI: + case QCA_WLAN_AC_VO: + if (*user_pri < + qca_wlan_ac_to_sme_qos(adapter->upgrade_udp_qos_threshold)) + *user_pri = qca_wlan_ac_to_sme_qos( + adapter->upgrade_udp_qos_threshold); + + break; + default: + break; + } +} + +/** + * hdd_wmm_classify_pkt() - Function which will classify an OS packet + * into a WMM AC based on DSCP + * + * @adapter: adapter upon which the packet is being transmitted + * @skb: pointer to network buffer + * @user_pri: user priority of the OS packet + * @is_eapol: eapol packet flag + * + * Return: None + */ +static +void hdd_wmm_classify_pkt(struct hdd_adapter *adapter, + struct sk_buff *skb, + enum sme_qos_wmmuptype *user_pri, + bool *is_eapol) +{ + unsigned char dscp; + unsigned char tos; + union generic_ethhdr *eth_hdr; + struct iphdr *ip_hdr; + struct ipv6hdr *ipv6hdr; + unsigned char *pkt; + + /* this code is executed for every packet therefore + * all debug code is kept conditional + */ + +#ifdef HDD_WMM_DEBUG + hdd_enter(); +#endif /* HDD_WMM_DEBUG */ + + pkt = skb->data; + eth_hdr = (union generic_ethhdr *)pkt; + +#ifdef HDD_WMM_DEBUG + hdd_debug("proto is 0x%04x", skb->protocol); +#endif /* HDD_WMM_DEBUG */ + + if (eth_hdr->eth_II.h_proto == htons(ETH_P_IP)) { + /* case 1: Ethernet II IP packet */ + ip_hdr = (struct iphdr *)&pkt[sizeof(eth_hdr->eth_II)]; + tos = ip_hdr->tos; +#ifdef HDD_WMM_DEBUG + hdd_debug("Ethernet II IP Packet, tos is %d", tos); +#endif /* HDD_WMM_DEBUG */ + + } else if (eth_hdr->eth_II.h_proto == htons(ETH_P_IPV6)) { + ipv6hdr = ipv6_hdr(skb); + tos = ntohs(*(const __be16 *)ipv6hdr) >> 4; +#ifdef HDD_WMM_DEBUG + hdd_debug("Ethernet II IPv6 Packet, tos is %d", tos); +#endif /* HDD_WMM_DEBUG */ + } else if ((ntohs(eth_hdr->eth_II.h_proto) < WLAN_MIN_PROTO) && + (eth_hdr->eth_8023.h_snap.dsap == WLAN_SNAP_DSAP) && + (eth_hdr->eth_8023.h_snap.ssap == WLAN_SNAP_SSAP) && + (eth_hdr->eth_8023.h_snap.ctrl == WLAN_SNAP_CTRL) && + (eth_hdr->eth_8023.h_proto == htons(ETH_P_IP))) { + /* case 2: 802.3 LLC/SNAP IP packet */ + ip_hdr = (struct iphdr *)&pkt[sizeof(eth_hdr->eth_8023)]; + tos = ip_hdr->tos; +#ifdef HDD_WMM_DEBUG + hdd_debug("802.3 LLC/SNAP IP Packet, tos is %d", tos); +#endif /* HDD_WMM_DEBUG */ + } else if (eth_hdr->eth_II.h_proto == htons(ETH_P_8021Q)) { + /* VLAN tagged */ + + if (eth_hdr->eth_IIv.h_vlan_encapsulated_proto == + htons(ETH_P_IP)) { + /* case 3: Ethernet II vlan-tagged IP packet */ + ip_hdr = + (struct iphdr *) + &pkt[sizeof(eth_hdr->eth_IIv)]; + tos = ip_hdr->tos; +#ifdef HDD_WMM_DEBUG + hdd_debug("Ether II VLAN tagged IP Packet, tos is %d", + tos); +#endif /* HDD_WMM_DEBUG */ + } else if ((ntohs(eth_hdr->eth_IIv.h_vlan_encapsulated_proto) + < WLAN_MIN_PROTO) && + (eth_hdr->eth_8023v.h_snap.dsap == + WLAN_SNAP_DSAP) + && (eth_hdr->eth_8023v.h_snap.ssap == + WLAN_SNAP_SSAP) + && (eth_hdr->eth_8023v.h_snap.ctrl == + WLAN_SNAP_CTRL) + && (eth_hdr->eth_8023v.h_proto == + htons(ETH_P_IP))) { + /* case 4: 802.3 LLC/SNAP vlan-tagged IP packet */ + ip_hdr = + (struct iphdr *) + &pkt[sizeof(eth_hdr->eth_8023v)]; + tos = ip_hdr->tos; +#ifdef HDD_WMM_DEBUG + hdd_debug("802.3 LLC/SNAP VLAN tagged IP Packet, tos is %d", + tos); +#endif /* HDD_WMM_DEBUG */ + } else { + /* default */ +#ifdef HDD_WMM_DEBUG + hdd_warn("VLAN tagged Unhandled Protocol, using default tos"); +#endif /* HDD_WMM_DEBUG */ + tos = 0; + } + } else { + /* default */ +#ifdef HDD_WMM_DEBUG + hdd_warn("Unhandled Protocol, using default tos"); +#endif /* HDD_WMM_DEBUG */ + /* Give the highest priority to 802.1x packet */ + if (eth_hdr->eth_II.h_proto == + htons(HDD_ETHERTYPE_802_1_X)) { + tos = 0xC0; + *is_eapol = true; + } else + tos = 0; + } + + dscp = (tos >> 2) & 0x3f; + *user_pri = adapter->dscp_to_up_map[dscp]; + + /* + * Upgrade the priority, if the user priority of this packet is + * less than the configured threshold. + */ + hdd_check_and_upgrade_udp_qos(adapter, skb, user_pri); + +#ifdef HDD_WMM_DEBUG + hdd_debug("tos is %d, dscp is %d, up is %d", tos, dscp, *user_pri); +#endif /* HDD_WMM_DEBUG */ +} + +/** + * __hdd_get_queue_index() - get queue index + * @up: user priority + * + * Return: queue_index + */ +static uint16_t __hdd_get_queue_index(uint16_t up) +{ + if (qdf_unlikely(up >= ARRAY_SIZE(hdd_linux_up_to_ac_map))) + return HDD_LINUX_AC_BE; + return hdd_linux_up_to_ac_map[up]; +} + +#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || \ + defined(QCA_HL_NETDEV_FLOW_CONTROL) || \ + defined(QCA_LL_PDEV_TX_FLOW_CONTROL) +/** + * hdd_get_queue_index() - get queue index + * @up: user priority + * @is_eapol: is_eapol flag + * + * Return: queue_index + */ +static +uint16_t hdd_get_queue_index(uint16_t up, bool is_eapol) +{ + if (qdf_unlikely(is_eapol == true)) + return HDD_LINUX_AC_HI_PRIO; + return __hdd_get_queue_index(up); +} +#else +static +uint16_t hdd_get_queue_index(uint16_t up, bool is_eapol) +{ + return __hdd_get_queue_index(up); +} +#endif + +/** + * hdd_wmm_select_queue() - Function which will classify the packet + * according to linux qdisc expectation. + * + * @dev: [in] pointer to net_device structure + * @skb: [in] pointer to os packet + * + * Return: Qdisc queue index + */ +static uint16_t hdd_wmm_select_queue(struct net_device *dev, + struct sk_buff *skb) +{ + enum sme_qos_wmmuptype up = SME_QOS_WMM_UP_BE; + uint16_t index; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + bool is_crtical = false; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + int status; + enum qdf_proto_subtype proto_subtype; + + status = wlan_hdd_validate_context(hdd_ctx); + if (status != 0) { + skb->priority = SME_QOS_WMM_UP_BE; + return HDD_LINUX_AC_BE; + } + + /* Get the user priority from IP header */ + hdd_wmm_classify_pkt(adapter, skb, &up, &is_crtical); + spin_lock_bh(&adapter->pause_map_lock); + if ((adapter->pause_map & (1 << WLAN_DATA_FLOW_CONTROL)) && + !(adapter->pause_map & (1 << WLAN_DATA_FLOW_CONTROL_PRIORITY))) { + if (qdf_nbuf_is_ipv4_arp_pkt(skb)) + is_crtical = true; + else if (qdf_nbuf_is_icmpv6_pkt(skb)) { + proto_subtype = qdf_nbuf_get_icmpv6_subtype(skb); + switch (proto_subtype) { + case QDF_PROTO_ICMPV6_NA: + case QDF_PROTO_ICMPV6_NS: + is_crtical = true; + break; + default: + break; + } + } + } + spin_unlock_bh(&adapter->pause_map_lock); + skb->priority = up; + index = hdd_get_queue_index(skb->priority, is_crtical); + + return index; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + return hdd_wmm_select_queue(dev, skb); +} +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev, + select_queue_fallback_t fallback) +{ + return hdd_wmm_select_queue(dev, skb); +} +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback) +{ + return hdd_wmm_select_queue(dev, skb); +} +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv) +{ + return hdd_wmm_select_queue(dev, skb); +} +#else +uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb) +{ + return hdd_wmm_select_queue(dev, skb); +} +#endif + + +/** + * hdd_wmm_acquire_access_required() - Function which will determine + * acquire admittance for a WMM AC is required or not based on psb configuration + * done in framework + * + * @adapter: [in] pointer to adapter structure + * @ac_type: [in] WMM AC type of OS packet + * + * Return: void + */ +void hdd_wmm_acquire_access_required(struct hdd_adapter *adapter, + sme_ac_enum_type ac_type) +{ + /* Each bit in the LSB nibble indicates 1 AC. + * Clearing the particular bit in LSB nibble to indicate + * access required + */ + switch (ac_type) { + case SME_AC_BK: + /* clear first bit */ + adapter->psb_changed &= ~SME_QOS_UAPSD_CFG_BK_CHANGED_MASK; + break; + case SME_AC_BE: + /* clear second bit */ + adapter->psb_changed &= ~SME_QOS_UAPSD_CFG_BE_CHANGED_MASK; + break; + case SME_AC_VI: + /* clear third bit */ + adapter->psb_changed &= ~SME_QOS_UAPSD_CFG_VI_CHANGED_MASK; + break; + case SME_AC_VO: + /* clear fourth bit */ + adapter->psb_changed &= ~SME_QOS_UAPSD_CFG_VO_CHANGED_MASK; + break; + default: + hdd_err("Invalid AC Type"); + break; + } +} + +/** + * hdd_wmm_acquire_access() - Function which will attempt to acquire + * admittance for a WMM AC + * + * @adapter: [in] pointer to adapter context + * @ac_type: [in] WMM AC type of OS packet + * @granted: [out] pointer to bool flag when indicates if access + * has been granted or not + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_acquire_access(struct hdd_adapter *adapter, + sme_ac_enum_type ac_type, bool *granted) +{ + struct hdd_wmm_qos_context *qos_context; + struct hdd_context *hdd_ctx; + bool enable; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "%s: Entered for AC %d", __func__, ac_type); + + status = ucfg_mlme_get_implicit_qos_is_enabled(hdd_ctx->psoc, &enable); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get implicit_qos_is_enabled failed"); + } + if (!hdd_wmm_is_active(adapter) || !(enable) || + !adapter->hdd_wmm_status.ac_status[ac_type].is_access_required) { + /* either we don't want QoS or the AP doesn't support + * QoS or we don't want to do implicit QoS + */ + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "%s: QoS not configured on both ends ", __func__); + + *granted = + adapter->hdd_wmm_status.ac_status[ac_type]. + is_access_allowed; + + return QDF_STATUS_SUCCESS; + } + /* do we already have an implicit QoS request pending for this AC? */ + if ((adapter->hdd_wmm_status.ac_status[ac_type].is_access_needed) || + (adapter->hdd_wmm_status.ac_status[ac_type].is_access_pending)) { + /* request already pending so we need to wait for that + * response + */ + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "%s: Implicit QoS for TL AC %d already scheduled", + __func__, ac_type); + + *granted = false; + return QDF_STATUS_SUCCESS; + } + /* did we already fail to establish implicit QoS for this AC? + * (if so, access should have been granted when the failure + * was handled) + */ + if (adapter->hdd_wmm_status.ac_status[ac_type].has_access_failed) { + /* request previously failed + * allow access, but we'll be downgraded + */ + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "%s: Implicit QoS for TL AC %d previously failed", + __func__, ac_type); + + if (!adapter->hdd_wmm_status.ac_status[ac_type]. + is_access_required) { + adapter->hdd_wmm_status.ac_status[ac_type]. + is_access_allowed = true; + *granted = true; + } else { + adapter->hdd_wmm_status.ac_status[ac_type]. + is_access_allowed = false; + *granted = false; + } + + return QDF_STATUS_SUCCESS; + } + /* we need to establish implicit QoS */ + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "%s: Need to schedule implicit QoS for TL AC %d, adapter is %pK", + __func__, ac_type, adapter); + + adapter->hdd_wmm_status.ac_status[ac_type].is_access_needed = true; + + qos_context = qdf_mem_malloc(sizeof(*qos_context)); + if (!qos_context) { + /* no memory for QoS context. Nothing we can do but + * let data flow + */ + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, + "%s: Unable to allocate context", __func__); + adapter->hdd_wmm_status.ac_status[ac_type].is_access_allowed = + true; + *granted = true; + return QDF_STATUS_SUCCESS; + } + + qos_context->ac_type = ac_type; + qos_context->adapter = adapter; + qos_context->flow_id = 0; + qos_context->handle = HDD_WMM_HANDLE_IMPLICIT; + qos_context->magic = HDD_WMM_CTX_MAGIC; + qos_context->is_inactivity_timer_running = false; + + INIT_WORK(&qos_context->implicit_qos_work, hdd_wmm_do_implicit_qos); + + QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, + "%s: Scheduling work for AC %d, context %pK", + __func__, ac_type, qos_context); + + schedule_work(&qos_context->implicit_qos_work); + + /* caller will need to wait until the work takes place and + * TSPEC negotiation completes + */ + *granted = false; + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_wmm_assoc() - Function which will handle the housekeeping + * required by WMM when association takes place + * + * @adapter: [in] pointer to adapter context + * @roam_info: [in] pointer to roam information + * @bss_type: [in] type of BSS + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_assoc(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + eCsrRoamBssType bss_type) +{ + uint8_t uapsd_mask; + QDF_STATUS status; + uint32_t srv_value = 0; + uint32_t sus_value = 0; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t delayed_trgr_frm_int; + + /* when we associate we need to notify TL if it needs to + * enable UAPSD for any access categories + */ + + hdd_enter(); + + if (roam_info->fReassocReq) { + /* when we reassociate we should continue to use + * whatever parameters were previously established. + * if we are reassociating due to a U-APSD change for + * a particular Access Category, then the change will + * be communicated to HDD via the QoS callback + * associated with the given flow, and U-APSD + * parameters will be updated there + */ + + hdd_debug("Reassoc so no work, Exiting"); + + return QDF_STATUS_SUCCESS; + } + /* get the negotiated UAPSD Mask */ + uapsd_mask = + roam_info->u.pConnectedProfile->modifyProfileFields.uapsd_mask; + + hdd_debug("U-APSD mask is 0x%02x", (int)uapsd_mask); + + ucfg_mlme_get_tl_delayed_trgr_frm_int(hdd_ctx->psoc, + &delayed_trgr_frm_int); + + if (uapsd_mask & HDD_AC_VO) { + status = ucfg_mlme_get_wmm_uapsd_vo_srv_intv(hdd_ctx->psoc, + &srv_value); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Get uapsd_srv_intv failed"); + return QDF_STATUS_SUCCESS; + } + status = ucfg_mlme_get_wmm_uapsd_vo_sus_intv(hdd_ctx->psoc, + &sus_value); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("Get uapsd_vo_sus_intv failed"); + return QDF_STATUS_SUCCESS; + } + + status = sme_enable_uapsd_for_ac( + SME_AC_VO, 7, 7, srv_value, sus_value, + SME_QOS_WMM_TS_DIR_BOTH, 1, + adapter->vdev_id, + delayed_trgr_frm_int); + + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(status)); + } + + if (uapsd_mask & HDD_AC_VI) { + status = ucfg_mlme_get_wmm_uapsd_vi_srv_intv( + hdd_ctx->psoc, &srv_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_vi_srv_intv failed"); + return QDF_STATUS_SUCCESS; + } + status = ucfg_mlme_get_wmm_uapsd_vi_sus_intv( + hdd_ctx->psoc, &sus_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_vi_sus_intv failed"); + return QDF_STATUS_SUCCESS; + } + + status = sme_enable_uapsd_for_ac( + SME_AC_VI, 5, 5, srv_value, sus_value, + SME_QOS_WMM_TS_DIR_BOTH, 1, + adapter->vdev_id, + delayed_trgr_frm_int); + + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(status)); + } + + if (uapsd_mask & HDD_AC_BK) { + status = ucfg_mlme_get_wmm_uapsd_bk_srv_intv(hdd_ctx->psoc, + &srv_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_bk_srv_intv failed"); + return QDF_STATUS_SUCCESS; + } + status = ucfg_mlme_get_wmm_uapsd_bk_sus_intv(hdd_ctx->psoc, + &sus_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_bk_sus_intv failed"); + return QDF_STATUS_SUCCESS; + } + + status = sme_enable_uapsd_for_ac( + SME_AC_BK, 2, 2, srv_value, sus_value, + SME_QOS_WMM_TS_DIR_BOTH, 1, + adapter->vdev_id, + delayed_trgr_frm_int); + + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(status)); + } + + if (uapsd_mask & HDD_AC_BE) { + status = ucfg_mlme_get_wmm_uapsd_be_srv_intv(hdd_ctx->psoc, + &srv_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_be_srv_intv failed"); + return QDF_STATUS_SUCCESS; + } + status = ucfg_mlme_get_wmm_uapsd_be_sus_intv(hdd_ctx->psoc, + &sus_value); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("Get uapsd_be_sus_intv failed"); + return QDF_STATUS_SUCCESS; + } + + status = sme_enable_uapsd_for_ac( + SME_AC_BE, 3, 3, srv_value, sus_value, + SME_QOS_WMM_TS_DIR_BOTH, 1, + adapter->vdev_id, + delayed_trgr_frm_int); + + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(status)); + } + + status = sme_update_dsc_pto_up_mapping(hdd_ctx->mac_handle, + adapter->dscp_to_up_map, + adapter->vdev_id); + + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_wmm_init(adapter); + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +static const uint8_t acm_mask_bit[WLAN_MAX_AC] = { + 0x4, /* SME_AC_BK */ + 0x8, /* SME_AC_BE */ + 0x2, /* SME_AC_VI */ + 0x1 /* SME_AC_VO */ +}; + +/** + * hdd_wmm_connect() - Function which will handle the housekeeping + * required by WMM when a connection is established + * + * @adapter : [in] pointer to adapter context + * @roam_info: [in] pointer to roam information + * @bss_type : [in] type of BSS + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS hdd_wmm_connect(struct hdd_adapter *adapter, + struct csr_roam_info *roam_info, + eCsrRoamBssType bss_type) +{ + int ac; + bool qap; + bool qos_connection; + uint8_t acm_mask; + mac_handle_t mac_handle; + + if ((eCSR_BSS_TYPE_INFRASTRUCTURE == bss_type) && + roam_info && roam_info->u.pConnectedProfile) { + qap = roam_info->u.pConnectedProfile->qap; + qos_connection = roam_info->u.pConnectedProfile->qosConnection; + acm_mask = roam_info->u.pConnectedProfile->acm_mask; + } else { + qap = true; + qos_connection = true; + acm_mask = 0x0; + } + + hdd_debug("qap is %d, qos_connection is %d, acm_mask is 0x%x", + qap, qos_connection, acm_mask); + + adapter->hdd_wmm_status.qap = qap; + adapter->hdd_wmm_status.qos_connection = qos_connection; + mac_handle = hdd_adapter_get_mac_handle(adapter); + + for (ac = 0; ac < WLAN_MAX_AC; ac++) { + if (qap && qos_connection && (acm_mask & acm_mask_bit[ac])) { + hdd_debug("ac %d on", ac); + + /* admission is required */ + adapter->hdd_wmm_status.ac_status[ac]. + is_access_required = true; + adapter->hdd_wmm_status.ac_status[ac]. + is_access_allowed = false; + adapter->hdd_wmm_status.ac_status[ac]. + was_access_granted = false; + /* after reassoc if we have valid tspec, allow access */ + if (adapter->hdd_wmm_status.ac_status[ac]. + is_tspec_valid + && (adapter->hdd_wmm_status.ac_status[ac]. + tspec.ts_info.direction != + SME_QOS_WMM_TS_DIR_DOWNLINK)) { + adapter->hdd_wmm_status.ac_status[ac]. + is_access_allowed = true; + } + if (!roam_info->fReassocReq && + !sme_neighbor_roam_is11r_assoc( + mac_handle, + adapter->vdev_id) && + !sme_roam_is_ese_assoc(roam_info)) { + adapter->hdd_wmm_status.ac_status[ac]. + is_tspec_valid = false; + adapter->hdd_wmm_status.ac_status[ac]. + is_access_allowed = false; + } + } else { + hdd_debug("ac %d off", ac); + /* admission is not required so access is allowed */ + adapter->hdd_wmm_status.ac_status[ac]. + is_access_required = false; + adapter->hdd_wmm_status.ac_status[ac]. + is_access_allowed = true; + } + + } + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_wmm_is_active() - Function which will determine if WMM is + * active on the current connection + * + * @adapter: [in] pointer to adapter context + * + * Return: true if WMM is enabled, false if WMM is not enabled + */ +bool hdd_wmm_is_active(struct hdd_adapter *adapter) +{ + if ((!adapter->hdd_wmm_status.qos_connection) || + (!adapter->hdd_wmm_status.qap)) { + return false; + } else { + return true; + } +} + +bool hdd_wmm_is_acm_allowed(uint8_t vdev_id) +{ + struct hdd_adapter *adapter; + struct hdd_wmm_ac_status *wmm_ac_status; + struct hdd_context *hdd_ctx; + + hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + if (!hdd_ctx) { + hdd_err("Unable to fetch the hdd context"); + return false; + } + + adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); + if (hdd_validate_adapter(adapter)) { + hdd_err("Invalid adapter"); + return false; + } + + wmm_ac_status = adapter->hdd_wmm_status.ac_status; + + if (hdd_wmm_is_active(adapter) && + !(wmm_ac_status[QCA_WLAN_AC_VI].is_access_allowed)) + return false; + return true; +} + +/** + * hdd_wmm_addts() - Function which will add a traffic spec at the + * request of an application + * + * @adapter : [in] pointer to adapter context + * @handle : [in] handle to uniquely identify a TS + * @tspec : [in] pointer to the traffic spec + * + * Return: HDD_WLAN_WMM_STATUS_* + */ +hdd_wlan_wmm_status_e hdd_wmm_addts(struct hdd_adapter *adapter, + uint32_t handle, + struct sme_qos_wmmtspecinfo *tspec) +{ + struct hdd_wmm_qos_context *qos_context; + hdd_wlan_wmm_status_e status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS; +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + enum sme_qos_statustype sme_status; +#endif + bool found = false; + mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); + + hdd_debug("Entered with handle 0x%x", handle); + + /* see if a context already exists with the given handle */ + mutex_lock(&adapter->hdd_wmm_status.mutex); + list_for_each_entry(qos_context, + &adapter->hdd_wmm_status.context_list, node) { + if (qos_context->handle == handle) { + found = true; + break; + } + } + mutex_unlock(&adapter->hdd_wmm_status.mutex); + if (found) { + /* record with that handle already exists */ + hdd_err("Record already exists with handle 0x%x", handle); + + /* Application is trying to modify some of the Tspec + * params. Allow it + */ + sme_status = sme_qos_modify_req(mac_handle, + tspec, qos_context->flow_id); + + /* need to check the return value and act appropriately */ + switch (sme_status) { + case SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP: + status = HDD_WLAN_WMM_STATUS_MODIFY_PENDING; + break; + case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP: + status = + HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_NO_UAPSD; + break; + case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY: + status = + HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_UAPSD_EXISTING; + break; + case SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP: + status = HDD_WLAN_WMM_STATUS_MODIFY_FAILED_BAD_PARAM; + break; + case SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP: + status = HDD_WLAN_WMM_STATUS_MODIFY_FAILED; + break; + case SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP: + status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM; + break; + default: + /* we didn't get back one of the + * SME_QOS_STATUS_MODIFY_* status codes + */ + hdd_err("unexpected SME Status=%d", + sme_status); + QDF_ASSERT(0); + return HDD_WLAN_WMM_STATUS_MODIFY_FAILED; + } + + /* we were successful, save the status */ + mutex_lock(&adapter->hdd_wmm_status.mutex); + if (qos_context->magic == HDD_WMM_CTX_MAGIC) + qos_context->status = status; + mutex_unlock(&adapter->hdd_wmm_status.mutex); + + return status; + } + + qos_context = qdf_mem_malloc(sizeof(*qos_context)); + if (!qos_context) { + /* no memory for QoS context. Nothing we can do */ + hdd_err("Unable to allocate QoS context"); + return HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE; + } + /* we assume the tspec has already been validated by the caller */ + + qos_context->handle = handle; + if (tspec->ts_info.up < HDD_WMM_UP_TO_AC_MAP_SIZE) + qos_context->ac_type = hdd_wmm_up_to_ac_map[tspec->ts_info.up]; + else { + hdd_err("ts_info.up (%d) larger than max value (%d), use default ac_type (%d)", + tspec->ts_info.up, + HDD_WMM_UP_TO_AC_MAP_SIZE - 1, hdd_wmm_up_to_ac_map[0]); + qos_context->ac_type = hdd_wmm_up_to_ac_map[0]; + } + qos_context->adapter = adapter; + qos_context->flow_id = 0; + qos_context->magic = HDD_WMM_CTX_MAGIC; + qos_context->is_inactivity_timer_running = false; + + hdd_debug("Setting up QoS, context %pK", qos_context); + + mutex_lock(&adapter->hdd_wmm_status.mutex); + list_add(&qos_context->node, &adapter->hdd_wmm_status.context_list); + mutex_unlock(&adapter->hdd_wmm_status.mutex); + +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_status = sme_qos_setup_req(mac_handle, + adapter->vdev_id, + tspec, + hdd_wmm_sme_callback, + qos_context, + tspec->ts_info.up, + &qos_context->flow_id); + + hdd_debug("sme_qos_setup_req returned %d flowid %d", + sme_status, qos_context->flow_id); + + /* need to check the return value and act appropriately */ + switch (sme_status) { + case SME_QOS_STATUS_SETUP_REQ_PENDING_RSP: + status = HDD_WLAN_WMM_STATUS_SETUP_PENDING; + break; + case SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP: + status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_NO_UAPSD; + break; + case SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY: + status = + HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_UAPSD_EXISTING; + break; + case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING: + status = HDD_WLAN_WMM_STATUS_SETUP_PENDING; + break; + case SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP: + /* disable the inactivity timer */ + hdd_wmm_disable_inactivity_timer(qos_context); + hdd_wmm_free_context(qos_context); + return HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM; + case SME_QOS_STATUS_SETUP_FAILURE_RSP: + /* disable the inactivity timer */ + hdd_wmm_disable_inactivity_timer(qos_context); + /* we can't tell the difference between when a request + * fails because AP rejected it versus when SME + * encounterd an internal error + */ + hdd_wmm_free_context(qos_context); + return HDD_WLAN_WMM_STATUS_SETUP_FAILED; + case SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP: + /* disable the inactivity timer */ + hdd_wmm_disable_inactivity_timer(qos_context); + hdd_wmm_free_context(qos_context); + return HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM; + default: + /* disable the inactivity timer */ + hdd_wmm_disable_inactivity_timer(qos_context); + /* we didn't get back one of the + * SME_QOS_STATUS_SETUP_* status codes + */ + hdd_wmm_free_context(qos_context); + hdd_err("unexpected SME Status=%d", sme_status); + QDF_ASSERT(0); + return HDD_WLAN_WMM_STATUS_SETUP_FAILED; + } +#endif + + /* we were successful, save the status */ + mutex_lock(&adapter->hdd_wmm_status.mutex); + if (qos_context->magic == HDD_WMM_CTX_MAGIC) + qos_context->status = status; + mutex_unlock(&adapter->hdd_wmm_status.mutex); + + return status; +} + +/** + * hdd_wmm_delts() - Function which will delete a traffic spec at the + * request of an application + * + * @adapter: [in] pointer to adapter context + * @handle: [in] handle to uniquely identify a TS + * + * Return: HDD_WLAN_WMM_STATUS_* + */ +hdd_wlan_wmm_status_e hdd_wmm_delts(struct hdd_adapter *adapter, + uint32_t handle) +{ + struct hdd_wmm_qos_context *qos_context; + bool found = false; + sme_ac_enum_type ac_type = 0; + uint32_t flow_id = 0; + hdd_wlan_wmm_status_e status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS; +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + enum sme_qos_statustype sme_status; + mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); +#endif + + hdd_debug("Entered with handle 0x%x", handle); + + /* locate the context with the given handle */ + mutex_lock(&adapter->hdd_wmm_status.mutex); + list_for_each_entry(qos_context, + &adapter->hdd_wmm_status.context_list, node) { + if (qos_context->handle == handle) { + found = true; + ac_type = qos_context->ac_type; + flow_id = qos_context->flow_id; + break; + } + } + mutex_unlock(&adapter->hdd_wmm_status.mutex); + + if (false == found) { + /* we didn't find the handle */ + hdd_info("handle 0x%x not found", handle); + return HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM; + } + + hdd_debug("found handle 0x%x, flow %d, AC %d, context %pK", + handle, flow_id, ac_type, qos_context); + +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_status = sme_qos_release_req(mac_handle, adapter->vdev_id, + flow_id); + + hdd_debug("SME flow %d released, SME status %d", flow_id, sme_status); + + switch (sme_status) { + case SME_QOS_STATUS_RELEASE_SUCCESS_RSP: + /* this flow is the only one on that AC, so go ahead + * and update our TSPEC state for the AC + */ + adapter->hdd_wmm_status.ac_status[ac_type].is_tspec_valid = + false; + adapter->hdd_wmm_status.ac_status[ac_type].is_access_allowed = + false; + + /* need to tell TL to stop trigger timer, etc */ + hdd_wmm_disable_tl_uapsd(qos_context); + + /* disable the inactivity timer */ + hdd_wmm_disable_inactivity_timer(qos_context); + + /* we are done with this context */ + hdd_wmm_free_context(qos_context); + + /* SME must not fire any more callbacks for this flow + * since the context is no longer valid + */ + + return HDD_WLAN_WMM_STATUS_RELEASE_SUCCESS; + + case SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP: + /* do nothing as we will get a response from SME */ + status = HDD_WLAN_WMM_STATUS_RELEASE_PENDING; + break; + + case SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP: + /* nothing we can do with the existing flow except leave it */ + status = HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM; + break; + + case SME_QOS_STATUS_RELEASE_FAILURE_RSP: + /* nothing we can do with the existing flow except leave it */ + status = HDD_WLAN_WMM_STATUS_RELEASE_FAILED; + break; + + default: + /* we didn't get back one of the + * SME_QOS_STATUS_RELEASE_* status codes + */ + hdd_err("unexpected SME Status=%d", sme_status); + QDF_ASSERT(0); + status = HDD_WLAN_WMM_STATUS_RELEASE_FAILED; + } + +#endif + mutex_lock(&adapter->hdd_wmm_status.mutex); + if (qos_context->magic == HDD_WMM_CTX_MAGIC) + qos_context->status = status; + mutex_unlock(&adapter->hdd_wmm_status.mutex); + + return status; +} + +/** + * hdd_wmm_checkts() - Function which will return the status of a traffic + * spec at the request of an application + * + * @adapter: [in] pointer to adapter context + * @handle: [in] handle to uniquely identify a TS + * + * Return: HDD_WLAN_WMM_STATUS_* + */ +hdd_wlan_wmm_status_e hdd_wmm_checkts(struct hdd_adapter *adapter, uint32_t handle) +{ + struct hdd_wmm_qos_context *qos_context; + hdd_wlan_wmm_status_e status = HDD_WLAN_WMM_STATUS_LOST; + + hdd_debug("Entered with handle 0x%x", handle); + + /* locate the context with the given handle */ + mutex_lock(&adapter->hdd_wmm_status.mutex); + list_for_each_entry(qos_context, + &adapter->hdd_wmm_status.context_list, node) { + if (qos_context->handle == handle) { + hdd_debug("found handle 0x%x, context %pK", + handle, qos_context); + + status = qos_context->status; + break; + } + } + mutex_unlock(&adapter->hdd_wmm_status.mutex); + return status; +} diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_wowl.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_wowl.c new file mode 100644 index 0000000000000000000000000000000000000000..ce93843d345148e384c2bec4a56596e614566612 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_wowl.c @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file wlan_hdd_wowl.c + * + * @brief wake up on WLAN API file + */ + +/* Include Files */ + +#include "qdf_str.h" +#include +#include +#include + +/* Preprocessor Definitions and Constants */ +#define WOWL_INTER_PTRN_TOKENIZER ';' +#define WOWL_INTRA_PTRN_TOKENIZER ':' + +/* Type Declarations */ + +static char *g_hdd_wowl_ptrns[WOWL_MAX_PTRNS_ALLOWED]; +static bool g_hdd_wowl_ptrns_debugfs[WOWL_MAX_PTRNS_ALLOWED] = { 0 }; + +static uint8_t g_hdd_wowl_ptrns_count; + +static inline int find_ptrn_len(const char *ptrn) +{ + int len = 0; + + while (*ptrn != '\0' && *ptrn != WOWL_INTER_PTRN_TOKENIZER) { + len++; + ptrn++; + } + return len; +} + +/** + * dump_hdd_wowl_ptrn() - log wow patterns + * @ptrn: pointer to wow pattern + * + * Return: none + */ +static void dump_hdd_wowl_ptrn(struct pmo_wow_add_pattern *ptrn) +{ + hdd_debug("Dumping WOW pattern"); + hdd_nofl_debug("Pattern Id = 0x%x", ptrn->pattern_id); + hdd_nofl_debug("Pattern Byte Offset = 0x%x", ptrn->pattern_byte_offset); + hdd_nofl_debug("Pattern_size = 0x%x", ptrn->pattern_size); + hdd_nofl_debug("Pattern_mask_size = 0x%x", ptrn->pattern_mask_size); + hdd_nofl_debug("Pattern: "); + qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + ptrn->pattern, ptrn->pattern_size); + hdd_nofl_debug("pattern_mask: "); + qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + ptrn->pattern_mask, ptrn->pattern_mask_size); +} + +static QDF_STATUS +hdd_get_num_wow_filters(struct hdd_context *hdd_ctx, uint8_t *num_filters) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; + + status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_HDD_ID_OBJ_MGR); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + *num_filters = ucfg_pmo_get_num_wow_filters(hdd_ctx->psoc); + + wlan_objmgr_psoc_release_ref(psoc, WLAN_HDD_ID_OBJ_MGR); + + return QDF_STATUS_SUCCESS; +} + +/** + * hdd_add_wowl_ptrn() - Function which will add the WoWL pattern to be + * used when PBM filtering is enabled + * @adapter: pointer to the adapter + * @ptrn: pointer to the pattern string to be added + * + * Return: false if any errors encountered, true otherwise + */ +bool hdd_add_wowl_ptrn(struct hdd_adapter *adapter, const char *ptrn) +{ + struct pmo_wow_add_pattern wow_pattern; + int i, empty_slot, len, offset; + QDF_STATUS status; + const char *temp; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t num_filters; + bool invalid_ptrn = false; + + status = hdd_get_num_wow_filters(hdd_ctx, &num_filters); + if (QDF_IS_STATUS_ERROR(status)) + return false; + + /* There has to have at least 1 byte for each field (pattern + * size, mask size, pattern, mask) e.g. PP:QQ:RR:SS ==> 11 + * chars + */ + len = find_ptrn_len(ptrn); + while (len >= 11) { + empty_slot = -1; + + /* check if pattern is already configured */ + for (i = num_filters - 1; i >= 0; i--) { + if (!g_hdd_wowl_ptrns[i]) { + empty_slot = i; + continue; + } + + if (strlen(g_hdd_wowl_ptrns[i]) == len) { + if (!memcmp(ptrn, g_hdd_wowl_ptrns[i], len)) { + hdd_err("WoWL pattern '%s' already configured", + g_hdd_wowl_ptrns[i]); + ptrn += len; + goto next_ptrn; + } + } + } + + /* Maximum number of patterns have been configured already */ + if (empty_slot == -1) { + hdd_err("Max WoW patterns (%u) reached", num_filters); + return false; + } + + /* Validate the pattern */ + if (ptrn[2] != WOWL_INTRA_PTRN_TOKENIZER || + ptrn[5] != WOWL_INTRA_PTRN_TOKENIZER) { + hdd_err("Malformed pattern string. Skip!"); + invalid_ptrn = true; + ptrn += len; + goto next_ptrn; + } + + /* Extract the pattern size */ + wow_pattern.pattern_size = + (hex_to_bin(ptrn[0]) * 0x10) + hex_to_bin(ptrn[1]); + + /* Extract the pattern mask size */ + wow_pattern.pattern_mask_size = + (hex_to_bin(ptrn[3]) * 0x10) + hex_to_bin(ptrn[4]); + + if (wow_pattern.pattern_size > PMO_WOWL_BCAST_PATTERN_MAX_SIZE + || wow_pattern.pattern_mask_size > + WOWL_PTRN_MASK_MAX_SIZE) { + hdd_err("Invalid length specified. Skip!"); + invalid_ptrn = true; + ptrn += len; + goto next_ptrn; + } + + /* compute the offset of tokenizer after the pattern */ + offset = 5 + 2 * wow_pattern.pattern_size + 1; + if ((offset >= len) || + (ptrn[offset] != WOWL_INTRA_PTRN_TOKENIZER)) { + hdd_err("Malformed pattern string..skip!"); + invalid_ptrn = true; + ptrn += len; + goto next_ptrn; + } + + /* compute the end of pattern sring */ + offset = offset + 2 * wow_pattern.pattern_mask_size; + if (offset + 1 != len) { + /* offset begins with 0 */ + hdd_err("Malformed pattern string...skip!"); + invalid_ptrn = true; + ptrn += len; + goto next_ptrn; + } + + temp = ptrn; + + /* Now advance to where pattern begins */ + ptrn += 6; + + /* Extract the pattern */ + for (i = 0; i < wow_pattern.pattern_size; i++) { + wow_pattern.pattern[i] = + (hex_to_bin(ptrn[0]) * 0x10) + + hex_to_bin(ptrn[1]); + ptrn += 2; /* skip to next byte */ + } + + /* Skip over the ':' separator after the pattern */ + ptrn++; + + /* Extract the pattern Mask */ + for (i = 0; i < wow_pattern.pattern_mask_size; i++) { + wow_pattern.pattern_mask[i] = + (hex_to_bin(ptrn[0]) * 0x10) + + hex_to_bin(ptrn[1]); + ptrn += 2; /* skip to next byte */ + } + + /* All is good. Store the pattern locally */ + g_hdd_wowl_ptrns[empty_slot] = qdf_mem_malloc(len + 1); + if (!g_hdd_wowl_ptrns[empty_slot]) { + hdd_err("memory allocation failure"); + return false; + } + + memcpy(g_hdd_wowl_ptrns[empty_slot], temp, len); + g_hdd_wowl_ptrns[empty_slot][len] = '\0'; + wow_pattern.pattern_id = empty_slot; + wow_pattern.pattern_byte_offset = 0; + + /* Register the pattern downstream */ + status = ucfg_pmo_add_wow_user_pattern( + adapter->vdev, &wow_pattern); + if (QDF_IS_STATUS_ERROR(status)) { + /* Add failed, so invalidate the local storage */ + hdd_err("sme_wowl_add_bcast_pattern failed with error code (%d)", + status); + qdf_mem_free(g_hdd_wowl_ptrns[empty_slot]); + g_hdd_wowl_ptrns[empty_slot] = NULL; + } + + dump_hdd_wowl_ptrn(&wow_pattern); + +next_ptrn: + if (*ptrn == WOWL_INTER_PTRN_TOKENIZER) { + /* move past the tokenizer */ + ptrn += 1; + len = find_ptrn_len(ptrn); + continue; + } else { + break; + } + } + + if (invalid_ptrn) + return false; + + return true; +} + +/** + * hdd_del_wowl_ptrn() - Function which will remove a WoWL pattern + * @adapter: pointer to the adapter + * @ptrn: pointer to the pattern string to be removed + * + * Return: false if any errors encountered, true otherwise + */ +bool hdd_del_wowl_ptrn(struct hdd_adapter *adapter, const char *ptrn) +{ + uint8_t id; + bool patternFound = false; + QDF_STATUS status; + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint8_t num_filters; + + status = hdd_get_num_wow_filters(hdd_ctx, &num_filters); + if (QDF_IS_STATUS_ERROR(status)) + return false; + + /* lookup pattern */ + for (id = 0; id < num_filters; id++) { + if (!g_hdd_wowl_ptrns[id]) + continue; + + if (qdf_str_eq(ptrn, g_hdd_wowl_ptrns[id])) { + patternFound = true; + break; + } + } + + /* If pattern present, remove it from downstream */ + if (!patternFound) + return false; + + status = ucfg_pmo_del_wow_user_pattern(adapter->vdev, id); + if (QDF_IS_STATUS_ERROR(status)) + return false; + + /* Remove from local storage as well */ + hdd_err("Deleted pattern with id %d [%s]", id, g_hdd_wowl_ptrns[id]); + + qdf_mem_free(g_hdd_wowl_ptrns[id]); + g_hdd_wowl_ptrns[id] = NULL; + + return true; +} + +/** + * hdd_add_wowl_ptrn_debugfs() - Function which will add a WoW pattern + * sent from debugfs interface + * @adapter: pointer to the adapter + * @pattern_idx: index of the pattern to be added + * @pattern_offset: offset of the pattern in the frame payload + * @pattern_buf: pointer to the pattern hex string to be added + * @pattern_mask: pointer to the pattern mask hex string + * + * Return: false if any errors encountered, true otherwise + */ +bool hdd_add_wowl_ptrn_debugfs(struct hdd_adapter *adapter, uint8_t pattern_idx, + uint8_t pattern_offset, char *pattern_buf, + char *pattern_mask) +{ + struct pmo_wow_add_pattern wow_pattern; + QDF_STATUS qdf_ret_status; + uint16_t pattern_len, mask_len, i; + + if (pattern_idx > (WOWL_MAX_PTRNS_ALLOWED - 1)) { + hdd_err("WoW pattern index %d is out of range (0 ~ %d)", + pattern_idx, WOWL_MAX_PTRNS_ALLOWED - 1); + + return false; + } + + pattern_len = strlen(pattern_buf); + + /* Since the pattern is a hex string, 2 characters represent 1 byte. */ + if (pattern_len % 2) { + hdd_err("Malformed WoW pattern!"); + + return false; + } + + pattern_len >>= 1; + if (!pattern_len || pattern_len > WOWL_PTRN_MAX_SIZE) { + hdd_err("WoW pattern length %d is out of range (1 ~ %d).", + pattern_len, WOWL_PTRN_MAX_SIZE); + + return false; + } + + wow_pattern.pattern_id = pattern_idx; + wow_pattern.pattern_byte_offset = pattern_offset; + wow_pattern.pattern_size = pattern_len; + + if (wow_pattern.pattern_size > PMO_WOWL_BCAST_PATTERN_MAX_SIZE) { + hdd_err("WoW pattern size (%d) greater than max (%d)", + wow_pattern.pattern_size, + PMO_WOWL_BCAST_PATTERN_MAX_SIZE); + return false; + } + /* Extract the pattern */ + for (i = 0; i < wow_pattern.pattern_size; i++) { + wow_pattern.pattern[i] = + (hex_to_bin(pattern_buf[0]) << 4) + + hex_to_bin(pattern_buf[1]); + + /* Skip to next byte */ + pattern_buf += 2; + } + + /* Get pattern mask size by pattern length */ + wow_pattern.pattern_mask_size = pattern_len >> 3; + if (pattern_len % 8) + wow_pattern.pattern_mask_size += 1; + + mask_len = strlen(pattern_mask); + if ((mask_len % 2) + || (wow_pattern.pattern_mask_size != (mask_len >> 1))) { + hdd_err("Malformed WoW pattern mask!"); + + return false; + } + if (wow_pattern.pattern_mask_size > WOWL_PTRN_MASK_MAX_SIZE) { + hdd_err("WoW pattern mask size (%d) greater than max (%d)", + wow_pattern.pattern_mask_size, + WOWL_PTRN_MASK_MAX_SIZE); + return false; + } + /* Extract the pattern mask */ + for (i = 0; i < wow_pattern.pattern_mask_size; i++) { + wow_pattern.pattern_mask[i] = + (hex_to_bin(pattern_mask[0]) << 4) + + hex_to_bin(pattern_mask[1]); + + /* Skip to next byte */ + pattern_mask += 2; + } + + /* Register the pattern downstream */ + qdf_ret_status = ucfg_pmo_add_wow_user_pattern( + adapter->vdev, &wow_pattern); + if (!QDF_IS_STATUS_SUCCESS(qdf_ret_status)) { + hdd_err("pmo_wow_user_pattern failed with error code (%d).", + qdf_ret_status); + + return false; + } + + /* All is good. */ + if (!g_hdd_wowl_ptrns_debugfs[pattern_idx]) { + g_hdd_wowl_ptrns_debugfs[pattern_idx] = 1; + g_hdd_wowl_ptrns_count++; + } + + dump_hdd_wowl_ptrn(&wow_pattern); + + return true; +} + +/** + * hdd_del_wowl_ptrn_debugfs() - Function which will remove a WoW pattern + * sent from debugfs interface + * @adapter: pointer to the adapter + * @pattern_idx: index of the pattern to be removed + * + * Return: false if any errors encountered, true otherwise + */ +bool hdd_del_wowl_ptrn_debugfs(struct hdd_adapter *adapter, + uint8_t pattern_idx) +{ + QDF_STATUS qdf_ret_status; + + if (pattern_idx > (WOWL_MAX_PTRNS_ALLOWED - 1)) { + hdd_err("WoW pattern index %d is not in the range (0 ~ %d).", + pattern_idx, WOWL_MAX_PTRNS_ALLOWED - 1); + + return false; + } + + if (!g_hdd_wowl_ptrns_debugfs[pattern_idx]) { + hdd_err("WoW pattern %d is not in the table.", + pattern_idx); + + return false; + } + + qdf_ret_status = ucfg_pmo_del_wow_user_pattern( + adapter->vdev, pattern_idx); + if (!QDF_IS_STATUS_SUCCESS(qdf_ret_status)) { + hdd_err("sme_wowl_del_bcast_pattern failed with error code (%d).", + qdf_ret_status); + + return false; + } + + g_hdd_wowl_ptrns_debugfs[pattern_idx] = 0; + g_hdd_wowl_ptrns_count--; + + return true; +} + +void hdd_free_user_wowl_ptrns(void) +{ + int i; + + for (i = 0; i < WOWL_MAX_PTRNS_ALLOWED; ++i) { + if (g_hdd_wowl_ptrns[i]) { + qdf_mem_free(g_hdd_wowl_ptrns[i]); + g_hdd_wowl_ptrns[i] = NULL; + } + } +} diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/ani_global.h b/drivers/staging/qcacld-3.0/core/mac/inc/ani_global.h new file mode 100644 index 0000000000000000000000000000000000000000..662b3dd287a4e8dfad8b1b74726955b779a9ba8a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/ani_global.h @@ -0,0 +1,860 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ANIGLOBAL_H +#define _ANIGLOBAL_H + +#include "qdf_types.h" +#include "sir_common.h" +#include "ani_system_defs.h" +#include "sys_def.h" +#include "dph_global.h" +#include "lim_global.h" +#include "sch_global.h" +#include "sys_global.h" +#include "sir_api.h" + +#include "csr_api.h" +#include "sme_ft_api.h" +#include "csr_support.h" +#include "sme_internal.h" +#include "sap_api.h" +#include "csr_internal.h" + +#include "sme_rrm_internal.h" +#include "rrm_global.h" + +#include +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_mlme_public_struct.h" + +/** + * MAC_CONTEXT() - Convert an opaque mac handle into a mac context + * @handle: MAC handle to be converted + * + * Given an opaque mac handle this function will return the mac + * context that is associated with that handle. + * + * This is the inverse function of MAC_HANDLE() + * + * Return: mac context for @handle + */ +static inline struct mac_context *MAC_CONTEXT(mac_handle_t handle) +{ + return (struct mac_context *)handle; +} + +/** + * MAC_HANDLE() - Convert a mac context into an opaque mac handle + * @mac: MAC context to be converted + * + * Given a mac context this function will return the opaque mac handle + * that is associated with that handle. + * + * This is the inverse function of MAC_CONTEXT() + * + * Return: opaque handle for @mac + */ +static inline mac_handle_t MAC_HANDLE(struct mac_context *mac) +{ + return (mac_handle_t)mac; +} + +#define ANI_DRIVER_TYPE(mac) (((struct mac_context *)(mac))->gDriverType) + +/* ------------------------------------------------------------------- */ +/* Bss Qos Caps bit map definition */ +#define LIM_BSS_CAPS_OFFSET_HCF 0 +#define LIM_BSS_CAPS_OFFSET_WME 1 +#define LIM_BSS_CAPS_OFFSET_WSM 2 + +#define LIM_BSS_CAPS_HCF (1 << LIM_BSS_CAPS_OFFSET_HCF) +#define LIM_BSS_CAPS_WME (1 << LIM_BSS_CAPS_OFFSET_WME) +#define LIM_BSS_CAPS_WSM (1 << LIM_BSS_CAPS_OFFSET_WSM) + +/* cap should be one of HCF/WME/WSM */ +#define LIM_BSS_CAPS_GET(cap, val) (((val) & (LIM_BSS_CAPS_ ## cap)) >> LIM_BSS_CAPS_OFFSET_ ## cap) +#define LIM_BSS_CAPS_SET(cap, val) ((val) |= (LIM_BSS_CAPS_ ## cap)) +#define LIM_BSS_CAPS_CLR(cap, val) ((val) &= (~(LIM_BSS_CAPS_ ## cap))) + +/* 40 beacons per heart beat interval is the default + 1 to count the rest */ +#define MAX_NO_BEACONS_PER_HEART_BEAT_INTERVAL 41 + +#define SPACE_ASCII_VALUE 32 + +#define WLAN_HOST_SEQ_NUM_MIN 2048 +#define WLAN_HOST_SEQ_NUM_MAX 4095 +#define LOW_SEQ_NUM_MASK 0x000F +#define HIGH_SEQ_NUM_MASK 0x0FF0 +#define HIGH_SEQ_NUM_OFFSET 4 +#define DEF_HE_AUTO_SGI_LTF 0x0F07 + +/** + * enum log_event_type - Type of event initiating bug report + * @WLAN_LOG_TYPE_NON_FATAL: Non fatal event + * @WLAN_LOG_TYPE_FATAL: Fatal event + * + * Enum indicating the type of event that is initiating the bug report + */ +enum log_event_type { + WLAN_LOG_TYPE_NON_FATAL, + WLAN_LOG_TYPE_FATAL, +}; + +/** + * enum log_event_indicator - Module triggering bug report + * @WLAN_LOG_INDICATOR_UNUSED: Unused + * @WLAN_LOG_INDICATOR_FRAMEWORK: Framework triggers bug report + * @WLAN_LOG_INDICATOR_HOST_DRIVER: Host driver triggers bug report + * @WLAN_LOG_INDICATOR_FIRMWARE: FW initiates bug report + * @WLAN_LOG_INDICATOR_HOST_ONLY: Host triggers fatal event bug report + * + * Enum indicating the module that triggered the bug report + */ +enum log_event_indicator { + WLAN_LOG_INDICATOR_UNUSED, + WLAN_LOG_INDICATOR_FRAMEWORK, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_INDICATOR_FIRMWARE, + WLAN_LOG_INDICATOR_HOST_ONLY, +}; + +/** + * enum log_event_host_reason_code - Reason code for bug report + * @WLAN_LOG_REASON_CODE_UNUSED: Unused + * @WLAN_LOG_REASON_ROAM_FAIL: Driver initiated roam has failed + * @WLAN_LOG_REASON_DATA_STALL: Unable to send/receive data due to low resource + * scenario for a prolonged period + * @WLAN_LOG_REASON_SME_COMMAND_STUCK: SME command is stuck in SME active queue + * @WLAN_LOG_REASON_QUEUE_FULL: Defer queue becomes full for a prolonged period + * @WLAN_LOG_REASON_POWER_COLLAPSE_FAIL: Unable to allow apps power collapse + * for a prolonged period + * @WLAN_LOG_REASON_MALLOC_FAIL: Memory allocation Fails + * @WLAN_LOG_REASON_VOS_MSG_UNDER_RUN: VOS Core runs out of message wrapper + * @WLAN_LOG_REASON_HDD_TIME_OUT: Wait for event Timeout in HDD layer + @WLAN_LOG_REASON_SME_OUT_OF_CMD_BUFL sme out of cmd buffer + * @WLAN_LOG_REASON_NO_SCAN_RESULTS: no scan results to report from HDD + * This enum contains the different reason codes for bug report + * @WLAN_LOG_REASON_SCAN_NOT_ALLOWED: scan not allowed due to connection states + * @WLAN_LOG_REASON_HB_FAILURE: station triggered heart beat failure with AP + * @WLAN_LOG_REASON_ROAM_HO_FAILURE: Handover failed during LFR3 roaming + * @WLAN_LOG_REASON_DISCONNECT: Disconnect because of some failure + */ +enum log_event_host_reason_code { + WLAN_LOG_REASON_CODE_UNUSED, + WLAN_LOG_REASON_ROAM_FAIL, + WLAN_LOG_REASON_DATA_STALL, + WLAN_LOG_REASON_SME_COMMAND_STUCK, + WLAN_LOG_REASON_QUEUE_FULL, + WLAN_LOG_REASON_POWER_COLLAPSE_FAIL, + WLAN_LOG_REASON_MALLOC_FAIL, + WLAN_LOG_REASON_VOS_MSG_UNDER_RUN, + WLAN_LOG_REASON_HDD_TIME_OUT, + WLAN_LOG_REASON_SME_OUT_OF_CMD_BUF, + WLAN_LOG_REASON_NO_SCAN_RESULTS, + WLAN_LOG_REASON_SCAN_NOT_ALLOWED, + WLAN_LOG_REASON_HB_FAILURE, + WLAN_LOG_REASON_ROAM_HO_FAILURE, + WLAN_LOG_REASON_DISCONNECT +}; + + +/** + * enum userspace_log_level - Log level at userspace + * @LOG_LEVEL_NO_COLLECTION: verbose_level 0 corresponds to no collection + * @LOG_LEVEL_NORMAL_COLLECT: verbose_level 1 correspond to normal log level, + * with minimal user impact. this is the default value + * @LOG_LEVEL_ISSUE_REPRO: verbose_level 2 are enabled when user is lazily + * trying to reproduce a problem, wifi performances and power can be impacted + * but device should not otherwise be significantly impacted + * @LOG_LEVEL_ACTIVE: verbose_level 3+ are used when trying to + * actively debug a problem + * + * Various log levels defined in the userspace for logging applications + */ +enum userspace_log_level { + LOG_LEVEL_NO_COLLECTION, + LOG_LEVEL_NORMAL_COLLECT, + LOG_LEVEL_ISSUE_REPRO, + LOG_LEVEL_ACTIVE, +}; + +/** + * enum wifi_driver_log_level - Log level defined in the driver for logging + * @WLAN_LOG_LEVEL_OFF: No logging + * @WLAN_LOG_LEVEL_NORMAL: Default logging + * @WLAN_LOG_LEVEL_REPRO: Normal debug level + * @WLAN_LOG_LEVEL_ACTIVE: Active debug level + * + * Log levels defined for logging by the wifi driver + */ +enum wifi_driver_log_level { + WLAN_LOG_LEVEL_OFF, + WLAN_LOG_LEVEL_NORMAL, + WLAN_LOG_LEVEL_REPRO, + WLAN_LOG_LEVEL_ACTIVE, +}; + +/** + * enum wifi_logging_ring_id - Ring id of logging entities + * @RING_ID_WAKELOCK: Power events ring id + * @RING_ID_CONNECTIVITY: Connectivity event ring id + * @RING_ID_PER_PACKET_STATS: Per packet statistic ring id + * @RING_ID_DRIVER_DEBUG: Driver debug messages ring id + * @RING_ID_FIRMWARE_DEBUG: Firmware debug messages ring id + * + * This enum has the ring id values of logging rings + */ +enum wifi_logging_ring_id { + RING_ID_WAKELOCK, + RING_ID_CONNECTIVITY, + RING_ID_PER_PACKET_STATS, + RING_ID_DRIVER_DEBUG, + RING_ID_FIRMWARE_DEBUG, +}; + +/* ------------------------------------------------------------------- */ +/* Change channel generic scheme */ +typedef void (*CHANGE_CHANNEL_CALLBACK)(struct mac_context *mac, QDF_STATUS status, + uint32_t *data, + struct pe_session *pe_session); + +/* / LIM global definitions */ +struct lim_ibss_info { + void *mac_hdr; + void *beacon; +}; + +typedef struct sDialogueToken { + /* bytes 0-3 */ + uint16_t assocId; + uint8_t token; + uint8_t rsvd1; + /* Bytes 4-7 */ + uint16_t tid; + uint8_t rsvd2[2]; + + struct sDialogueToken *next; +} tDialogueToken, *tpDialogueToken; + +typedef struct sLimTimers { + /* TIMERS IN LIM ARE NOT SUPPOSED TO BE ZEROED OUT DURING RESET. */ + /* DURING lim_initialize DONOT ZERO THEM OUT. */ + +/* STA SPECIFIC TIMERS */ + + TX_TIMER gLimPreAuthClnupTimer; + + /* Association related timers */ + TX_TIMER gLimAssocFailureTimer; + TX_TIMER gLimReassocFailureTimer; + + /* / Wait for Probe after Heartbeat failure timer on STA */ + TX_TIMER gLimProbeAfterHBTimer; + + /* Authentication related timers */ + TX_TIMER gLimAuthFailureTimer; + + /* Join Failure timeout on STA */ + TX_TIMER gLimJoinFailureTimer; + + /* CNF_WAIT timer */ + TX_TIMER *gpLimCnfWaitTimer; + + TX_TIMER gLimAddtsRspTimer; /* max wait for a response */ + + /* Update OLBC Cache Timer */ + TX_TIMER gLimUpdateOlbcCacheTimer; + + TX_TIMER gLimChannelSwitchTimer; + TX_TIMER gLimFTPreAuthRspTimer; + + TX_TIMER gLimPeriodicJoinProbeReqTimer; + TX_TIMER gLimDisassocAckTimer; + TX_TIMER gLimDeauthAckTimer; + TX_TIMER g_lim_periodic_auth_retry_timer; + + /* SAE authentication related timer */ + TX_TIMER sae_auth_timer; + +/* ********************TIMER SECTION ENDS************************************************** */ +/* ALL THE FIELDS BELOW THIS CAN BE ZEROED OUT in lim_initialize */ +/* **************************************************************************************** */ + +} tLimTimers; + +typedef struct { + void *pMlmDisassocReq; + void *pMlmDeauthReq; +} tLimDisassocDeauthCnfReq; + +typedef struct sAniSirLim { + /* //////////////////////////////////// TIMER RELATED START /////////////////////////////////////////// */ + + tLimTimers lim_timers; + /* / Flag to track if LIM timers are created or not */ + uint32_t gLimTimersCreated; + + /* //////////////////////////////////// TIMER RELATED END /////////////////////////////////////////// */ + + struct lim_scan_channel_status scan_channel_status; + + uint8_t gLimCurrentBssUapsd; + + /* */ + /* Store the BSS Index returned by HAL during */ + /* WMA_ADD_BSS_RSP here. */ + /* */ + + /* For now: */ + /* This will be used during WMA_SET_BSSKEY_REQ in */ + /* order to set the GTK */ + /* Later: */ + /* There could be other interfaces needing this info */ + /* */ + + /* */ + /* Due to the asynchronous nature of the interface */ + /* between PE <-> HAL, some transient information */ + /* like this needs to be cached. */ + /* This is cached upon receipt of eWNI_SME_SETCONTEXT_REQ. */ + /* This is released while posting LIM_MLM_SETKEYS_CNF */ + /* */ + void *gpLimMlmSetKeysReq; + + /* //////////////////////////////////////// BSS RELATED END /////////////////////////////////////////// */ + + /* //////////////////////////////////////// IBSS RELATED START /////////////////////////////////////////// */ + /* This indicates whether this STA coalesced and adapter to peer's capabilities or not. */ + uint8_t gLimIbssCoalescingHappened; + + /* / Definition for storing IBSS peers BSS description */ + tLimIbssPeerNode *gLimIbssPeerList; + uint32_t gLimNumIbssPeers; + uint32_t ibss_retry_cnt; + + /* ibss info - params for which ibss to join while coalescing */ + struct lim_ibss_info ibss_info; + + /* //////////////////////////////////////// IBSS RELATED END /////////////////////////////////////////// */ + + /* //////////////////////////////////////// STATS/COUNTER RELATED START /////////////////////////////////////////// */ + + uint16_t maxStation; + uint16_t maxBssId; + + uint32_t gLimNumBeaconsRcvd; + uint32_t gLimNumBeaconsIgnored; + + uint32_t gLimNumDeferredMsgs; + + /* / Variable to keep track of number of currently associated STAs */ + uint16_t gLimNumOfAniSTAs; /* count of ANI peers */ + + tSirMacAddr gLimHeartBeatApMac[2]; + uint8_t gLimHeartBeatApMacIndex; + + /* Statistics to keep track of no. beacons rcvd in heart beat interval */ + uint16_t + gLimHeartBeatBeaconStats[MAX_NO_BEACONS_PER_HEART_BEAT_INTERVAL]; + +#ifdef WLAN_DEBUG + /* Debug counters */ + uint32_t numTot, numBbt, numProtErr, numLearn, numLearnIgnore; + uint32_t numSme, numMAC[4][16]; + + /* Debug counter to track number of Assoc Req frame drops */ + /* when received in sta->mlmState other than LINK_ESTABLISED */ + uint32_t gLimNumAssocReqDropInvldState; + /* counters to track rejection of Assoc Req due to Admission Control */ + uint32_t gLimNumAssocReqDropACRejectTS; + uint32_t gLimNumAssocReqDropACRejectSta; + /* Debug counter to track number of Reassoc Req frame drops */ + /* when received in sta->mlmState other than LINK_ESTABLISED */ + uint32_t gLimNumReassocReqDropInvldState; + /* Debug counter to track number of Hash Miss event that */ + /* will not cause a sending of de-auth/de-associate frame */ + uint32_t gLimNumHashMissIgnored; + + /* Debug counter to track number of Beacon frames */ + /* received in unexpected state */ + uint32_t gLimUnexpBcnCnt; + + /* Debug counter to track number of Beacon frames */ + /* received in wt-join-state that do have SSID mismatch */ + uint32_t gLimBcnSSIDMismatchCnt; + + /* Debug counter to track number of Link establishments on STA/BP */ + uint32_t gLimNumLinkEsts; + + /* Debug counter to track number of Rx cleanup */ + uint32_t gLimNumRxCleanup; + + /* Debug counter to track different parse problem */ + uint32_t gLim11bStaAssocRejectCount; + +#endif + + /* //////////////////////////////////////// STATS/COUNTER RELATED END /////////////////////////////////////////// */ + + /* //////////////////////////////////////// STATES RELATED START /////////////////////////////////////////// */ + /* Counts Heartbeat failures */ + uint8_t gLimHBfailureCntInLinkEstState; + uint8_t gLimProbeFailureAfterHBfailedCnt; + uint8_t gLimHBfailureCntInOtherStates; + + /** + * This variable indicates whether LIM module need to + * send response to host. Used to identify whether a request + * is generated internally within LIM module or by host + */ + uint8_t gLimRspReqd; + + /* / Previous SME State */ + tLimSmeStates gLimPrevSmeState; + + /* / MLM State visible across all Sirius modules */ + tLimMlmStates gLimMlmState; + + /* / Previous MLM State */ + tLimMlmStates gLimPrevMlmState; + + /* Can be set to invalid channel. If it is invalid, HAL */ + /* should move to previous valid channel or stay in the */ + /* current channel. CB state goes along with channel to resume to */ + uint16_t gResumeChannel; + ePhyChanBondState gResumePhyCbState; + + /* Change channel generic scheme */ + CHANGE_CHANNEL_CALLBACK gpchangeChannelCallback; + uint32_t *gpchangeChannelData; + + /* / SME State visible across all Sirius modules */ + tLimSmeStates gLimSmeState; + /* / This indicates whether we're an AP, STA in BSS/IBSS */ + tLimSystemRole gLimSystemRole; + + /* Number of STAs that do not support short preamble */ + tLimNoShortParams gLimNoShortParams; + + /* Number of STAs that do not support short slot time */ + tLimNoShortSlotParams gLimNoShortSlotParams; + + /* */ + /* ---------------- DPH ----------------------- */ + uint32_t gLimPhyMode; + + /* ---------------- DPH ----------------------- */ + + /* //////////////////////////////////////// STATES RELATED END /////////////////////////////////////////// */ + + /* //////////////////////////////////////// MISC RELATED START /////////////////////////////////////////// */ + + /* Deferred Queue Parameters */ + tLimDeferredMsgQParams gLimDeferredMsgQ; + + /* addts request if any - only one can be outstanding at any time */ + tSirAddtsReq gLimAddtsReq; + uint8_t gLimAddtsSent; + uint8_t gLimAddtsRspTimerCount; + + /* protection related config cache */ + tCfgProtection cfgProtection; + + uint8_t gLimProtectionControl; + /* This flag will remain to be set except while LIM is waiting for specific response messages */ + /* from HAL. e.g when LIM issues ADD_STA req it will clear this flag and when it will receive */ + /* the response the flag will be set. */ + uint8_t gLimProcessDefdMsgs; + + /* UAPSD flag used on AP */ + uint8_t gUapsdEnable; + + /* Used on STA for AC downgrade. This is a dynamic mask + * setting which keep tracks of ACs being admitted. + * If bit is set to 0: That partiular AC is not admitted + * If bit is set to 1: That particular AC is admitted + */ + uint8_t gAcAdmitMask[SIR_MAC_DIRECTION_DIRECT]; + + /* dialogue token List head/tail for Action frames request sent. */ + tpDialogueToken pDialogueTokenHead; + tpDialogueToken pDialogueTokenTail; + + tLimTspecInfo tspecInfo[LIM_NUM_TSPEC_MAX]; + + /* admission control policy information */ + tLimAdmitPolicyInfo admitPolicyInfo; +#ifdef FEATURE_WLAN_TDLS + uint8_t gLimTDLSBufStaEnabled; + uint8_t gLimTDLSUapsdMask; + uint8_t gLimTDLSOffChannelEnabled; + uint8_t gLimTDLSWmmMode; +#endif + /* //////////////////////////////////////// MISC RELATED END /////////////////////////////////////////// */ + + /* ASSOC RELATED START */ + + /* Place holder for current authentication request */ + /* being handled */ + tLimMlmAuthReq *gpLimMlmAuthReq; + + /* Reason code to determine the channel change context while sending */ + /* WMA_CHNL_SWITCH_REQ message to HAL */ + uint32_t channelChangeReasonCode; + + /* / MAC level Pre-authentication related globals */ + tSirMacChanNum gLimPreAuthChannelNumber; + tAniAuthType gLimPreAuthType; + tSirMacAddr gLimPreAuthPeerAddr; + uint32_t gLimNumPreAuthContexts; + tLimPreAuthTable gLimPreAuthTimerTable; + + /* Place holder for Pre-authentication node list */ + struct tLimPreAuthNode *pLimPreAuthList; + + /* Assoc or ReAssoc Response Data/Frame */ + void *gLimAssocResponseData; + + /* One cache for each overlap and associated case. */ + tCacheParams protStaOverlapCache[LIM_PROT_STA_OVERLAP_CACHE_SIZE]; + tCacheParams protStaCache[LIM_PROT_STA_CACHE_SIZE]; + + /* ASSOC RELATED END */ + + /* ////////////////////////////// HT RELATED ////////////////////////////////////////// */ + /* */ + /* The following global LIM variables maintain/manage */ + /* the runtime configurations related to 802.11n */ + + /* 802.11n Station detected HT capability in Beacon Frame */ + uint8_t htCapabilityPresentInBeacon; + + /* 802.11 HT capability: Enabled or Disabled */ + uint8_t htCapability; + + uint8_t gHTGreenfield; + + uint8_t gHTShortGI40Mhz; + uint8_t gHTShortGI20Mhz; + + /* Set to 0 for 3839 octets */ + /* Set to 1 for 7935 octets */ + uint8_t gHTMaxAmsduLength; + + /* DSSS/CCK at 40 MHz: Enabled 1 or Disabled */ + uint8_t gHTDsssCckRate40MHzSupport; + + /* PSMP Support: Enabled 1 or Disabled 0 */ + uint8_t gHTPSMPSupport; + + /* L-SIG TXOP Protection used only if peer support available */ + uint8_t gHTLsigTXOPProtection; + + /* MIMO Power Save */ + tSirMacHTMIMOPowerSaveState gHTMIMOPSState; + + /* */ + /* A-MPDU Density */ + /* 000 - No restriction */ + /* 001 - 1/8 usec */ + /* 010 - 1/4 usec */ + /* 011 - 1/2 usec */ + /* 100 - 1 usec */ + /* 101 - 2 usec */ + /* 110 - 4 usec */ + /* 111 - 8 usec */ + /* */ + uint8_t gHTAMpduDensity; + + bool gMaxAmsduSizeEnabled; + /* Maximum Tx/Rx A-MPDU factor */ + uint8_t gHTMaxRxAMpduFactor; + + /* */ + /* Scheduled PSMP related - Service Interval Granularity */ + /* 000 - 5 ms */ + /* 001 - 10 ms */ + /* 010 - 15 ms */ + /* 011 - 20 ms */ + /* 100 - 25 ms */ + /* 101 - 30 ms */ + /* 110 - 35 ms */ + /* 111 - 40 ms */ + /* */ + uint8_t gHTServiceIntervalGranularity; + + /* Indicates whether an AP wants to associate PSMP enabled Stations */ + uint8_t gHTControlledAccessOnly; + + /* OBss Mode . set when we have Non HT STA is associated or with in overlap bss */ + uint8_t gHTObssMode; + + /* Identifies the current Operating Mode */ + tSirMacHTOperatingMode gHTOperMode; + + /* Indicates if PCO is activated in the BSS */ + uint8_t gHTPCOActive; + + /* */ + /* If PCO is active, indicates which PCO phase to use */ + /* 0 - switch to 20 MHz phase */ + /* 1 - switch to 40 MHz phase */ + /* */ + uint8_t gHTPCOPhase; + + /* */ + /* Used only in beacons. For PR, this is set to 0 */ + /* 0 - Primary beacon */ + /* 1 - Secondary beacon */ + /* */ + uint8_t gHTSecondaryBeacon; + + /* */ + /* Dual CTS Protection */ + /* 0 - Use RTS/CTS */ + /* 1 - Dual CTS Protection is used */ + /* */ + uint8_t gHTDualCTSProtection; + + /* */ + /* Identifies a single STBC MCS that shall ne used for */ + /* STBC control frames and STBC beacons */ + /* */ + uint8_t gHTSTBCBasicMCS; + + uint8_t gHTNonGFDevicesPresent; + + /* HT RELATED END */ + + /* wsc info required to form the wsc IE */ + tLimWscIeInfo wscIeInfo; + struct pe_session *gpSession; /* Pointer to session table */ + + qdf_mutex_t lim_frame_register_lock; + qdf_list_t gLimMgmtFrameRegistratinQueue; + uint32_t tdls_frm_session_id; + + struct pe_session *pe_session; + uint8_t reAssocRetryAttempt; + tLimDisassocDeauthCnfReq limDisassocDeauthCnfReq; + uint8_t deferredMsgCnt; + uint8_t deauthMsgCnt; + uint8_t disassocMsgCnt; + uint8_t gLimIbssStaLimit; + + QDF_STATUS(*sme_msg_callback) + (struct mac_context *mac, struct scheduler_msg *msg); + QDF_STATUS(*stop_roaming_callback) + (mac_handle_t mac, uint8_t session_id, uint8_t reason, + uint32_t requestor); + uint8_t retry_packet_cnt; + uint8_t beacon_probe_rsp_cnt_per_scan; + wlan_scan_requester req_id; + QDF_STATUS (*sme_bcn_rcv_callback)(hdd_handle_t hdd_handle, + struct wlan_beacon_report *beacon_report); +} tAniSirLim, *tpAniSirLim; + +struct mgmt_frm_reg_info { + qdf_list_node_t node; /* MUST be first element */ + uint16_t frameType; + uint16_t matchLen; + uint16_t sessionId; + uint8_t matchData[1]; +}; + +typedef struct sRrmContext { + struct rrm_config_param rrmConfig; + tRrmSMEContext rrmSmeContext[MAX_MEASUREMENT_REQUEST]; + tRrmPEContext rrmPEContext; +} tRrmContext, *tpRrmContext; + +/** + * enum tx_ack_status - Indicate TX status + * @LIM_ACK_NOT_RCD: Default status while waiting for ack status. + * @LIM_ACK_RCD_SUCCESS: Ack is received. + * @LIM_ACK_RCD_FAILURE: No Ack received. + * @LIM_TX_FAILED: Failed to TX + * + * Indicate if driver is waiting for ACK status of auth or ACK received for AUTH + * OR NO ACK is received for the auth sent. + */ +enum tx_ack_status { + LIM_ACK_NOT_RCD, + LIM_ACK_RCD_SUCCESS, + LIM_ACK_RCD_FAILURE, + LIM_TX_FAILED, +}; + +/** + * struct vdev_type_nss - vdev type nss structure + * @sta: STA Nss value. + * @sap: SAP Nss value. + * @p2p_go: P2P GO Nss value. + * @p2p_cli: P2P CLI Nss value. + * @p2p_dev: P2P device Nss value. + * @ibss: IBSS Nss value. + * @tdls: TDLS Nss value. + * @ocb: OCB Nss value. + * @nan: NAN Nss value. + * + * Holds the Nss values of different vdev types. + */ +struct vdev_type_nss { + uint8_t sta; + uint8_t sap; + uint8_t p2p_go; + uint8_t p2p_cli; + uint8_t p2p_dev; + uint8_t ibss; + uint8_t tdls; + uint8_t ocb; + uint8_t nan; + uint8_t ndi; +}; + +/** + * struct mgmt_beacon_probe_filter + * @num_sta_sessions: Number of active PE STA sessions + * @sta_bssid: Array of PE STA session's peer BSSIDs + * @num_ibss_sessions: Number of active PE IBSS sessions + * @ibss_ssid: Array of PE IBSS session's SSID + * @num_sap_session: Number of active PE SAP sessions + * @sap_channel: Array of PE SAP session's channels + * + * Used to filter the STA/IBSS/SAP beacons/probes required in PE and + * drop other unwanted beacon/probe response frames + */ +struct mgmt_beacon_probe_filter { + uint8_t num_sta_sessions; + tSirMacAddr sta_bssid[WLAN_MAX_VDEVS]; + uint8_t num_ibss_sessions; + tSirMacSSid ibss_ssid[WLAN_MAX_VDEVS]; + uint8_t num_sap_sessions; + uint8_t sap_channel[WLAN_MAX_VDEVS]; +}; + +#ifdef FEATURE_ANI_LEVEL_REQUEST +struct ani_level_params { + void (*ani_level_cb)(struct wmi_host_ani_level_event *ani, uint8_t num, + void *context); + void *context; +}; +#endif + +/** + * struct mac_context - Global MAC context + */ +struct mac_context { + enum qdf_driver_type gDriverType; + struct wlan_mlme_chain_cfg fw_chain_cfg; + struct wlan_mlme_cfg *mlme_cfg; + tAniSirLim lim; + uint8_t nud_fail_behaviour; + struct sch_context sch; + tAniSirSys sys; + + /* PAL/HDD handle */ + hdd_handle_t hdd_handle; + + struct sme_context sme; + tSapStruct sap; + struct csr_scanstruct scan; + struct csr_roamstruct roam; + tRrmContext rrm; + uint8_t isCoalesingInIBSSAllowed; + uint8_t lteCoexAntShare; + uint8_t beacon_offload; + bool pmf_offload; + bool is_fils_roaming_supported; + uint32_t f_sta_miracast_mcc_rest_time_val; +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + csr_readyToExtWoWCallback readyToExtWoWCallback; + void *readyToExtWoWContext; +#endif + struct vdev_type_nss vdev_type_nss_2g; + struct vdev_type_nss vdev_type_nss_5g; + + uint16_t mgmtSeqNum; + uint32_t sta_sap_scc_on_dfs_chan; + sir_mgmt_frame_ind_callback mgmt_frame_ind_cb; + qdf_atomic_t global_cmd_id; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + void (*chan_info_cb)(struct scan_chan_info *chan_info); + void (*del_peers_ind_cb)(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + /* Based on INI parameter */ + uint32_t dual_mac_feature_disable; + + enum country_src reg_hint_src; + uint32_t rx_packet_drop_counter; + enum tx_ack_status auth_ack_status; + enum tx_ack_status assoc_ack_status; + uint8_t user_configured_nss; + bool ignore_assoc_disallowed; + uint32_t peer_rssi; + uint32_t peer_txrate; + uint32_t peer_rxrate; + uint32_t rx_retry_cnt; + uint32_t rx_mc_bc_cnt; + /* 11k Offload Support */ + bool is_11k_offload_supported; + uint8_t reject_addba_req; + bool usr_cfg_ps_enable; + uint16_t usr_cfg_ba_buff_size; + bool is_usr_cfg_amsdu_enabled; + uint8_t no_ack_policy_cfg[QCA_WLAN_AC_ALL]; + uint32_t he_sgi_ltf_cfg_bit_mask; + uint8_t usr_cfg_tx_bfee_nsts; + struct mgmt_beacon_probe_filter bcn_filter; + tSirMacEdcaParamRecord usr_mu_edca_params[QCA_WLAN_AC_ALL]; + bool usr_cfg_mu_edca_params; + bool he_om_ctrl_cfg_bw_set; + uint8_t he_om_ctrl_cfg_bw; + bool he_om_ctrl_cfg_nss_set; + uint8_t he_om_ctrl_cfg_nss; + bool he_om_ctrl_cfg_ul_mu_dis; + bool he_om_ctrl_cfg_tx_nsts_set; + uint8_t he_om_ctrl_cfg_tx_nsts; + bool he_om_ctrl_ul_mu_data_dis; +#ifdef WLAN_FEATURE_11AX + tDot11fIEhe_cap he_cap_2g; + tDot11fIEhe_cap he_cap_5g; +#endif + bool obss_scan_offload; + bool bcn_reception_stats; + csr_session_close_cb session_close_cb; + csr_roam_complete_cb session_roam_complete_cb; +#ifdef FEATURE_ANI_LEVEL_REQUEST + struct ani_level_params ani_params; +#endif +}; + +#ifdef FEATURE_WLAN_TDLS + +#define RFC1042_HDR_LENGTH (6) +#define GET_BE16(x) ((uint16_t) (((x)[0] << 8) | (x)[1])) +#define ETH_TYPE_89_0d (0x890d) +#define ETH_TYPE_LEN (2) +#define PAYLOAD_TYPE_TDLS_SIZE (1) +#define PAYLOAD_TYPE_TDLS (2) + +#endif + +#endif /* _ANIGLOBAL_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/ani_system_defs.h b/drivers/staging/qcacld-3.0/core/mac/inc/ani_system_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..63a1b772d2a8a166b7cabfb221e91e16466be1d2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/ani_system_defs.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file ani_system_defs.h contains definitions used by + * various ANI entities + * Author: Chandra Modumudi + * Date: 09/18/2002 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#ifndef __ANI_SYSTEM_DEFS_H +#define __ANI_SYSTEM_DEFS_H + +#include "sir_types.h" +#include "sir_mac_prot_def.h" +#include "wlan_crypto_global_def.h" + +/* This is to force compiler to use the maximum of an int for enum */ +#define SIR_MAX_ENUM_SIZE 0x7FFFFFFF + +#ifndef false +#undef false +#define false 0 +#endif +#ifndef true +#undef true +#define true 1 +#endif + +/* / Authentication type enum used with peer */ +typedef enum eAniAuthType { + eSIR_OPEN_SYSTEM, + eSIR_SHARED_KEY, + eSIR_FT_AUTH, + eSIR_AUTH_TYPE_SAE = 3, +#if defined FEATURE_WLAN_ESE + eSIR_LEAP_AUTH = 0x80, +#endif + SIR_FILS_SK_WITHOUT_PFS = 4, + SIR_FILS_SK_WITH_PFS = 5, + SIR_FILS_PK_AUTH = 6, + eSIR_AUTH_TYPE_OWE, + eSIR_AUTO_SWITCH, + eSIR_DONOT_USE_AUTH_TYPE = SIR_MAX_ENUM_SIZE +} tAniAuthType; + +enum ani_akm_type { + ANI_AKM_TYPE_NONE, + ANI_AKM_TYPE_RSN, + ANI_AKM_TYPE_RSN_PSK, + ANI_AKM_TYPE_FT_RSN, + ANI_AKM_TYPE_FT_RSN_PSK, + ANI_AKM_TYPE_RSN_PSK_SHA256, + ANI_AKM_TYPE_RSN_8021X_SHA256, + ANI_AKM_TYPE_SAE, + ANI_AKM_TYPE_FT_SAE, + ANI_AKM_TYPE_SUITEB_EAP_SHA256, + ANI_AKM_TYPE_SUITEB_EAP_SHA384, + ANI_AKM_TYPE_FT_SUITEB_EAP_SHA384, + ANI_AKM_TYPE_FILS_SHA256, + ANI_AKM_TYPE_FILS_SHA384, + ANI_AKM_TYPE_FT_FILS_SHA256, + ANI_AKM_TYPE_FT_FILS_SHA384, + ANI_AKM_TYPE_OWE, +#ifdef FEATURE_WLAN_ESE + ANI_AKM_TYPE_CCKM, +#endif + ANI_AKM_TYPE_OSEN, + ANI_AKM_TYPE_DPP_RSN, + ANI_AKM_TYPE_WPA, + ANI_AKM_TYPE_WPA_PSK, + ANI_NUM_OF_SUPPORT_AKM_TYPE, + ANI_AKM_TYPE_UNKNOWN = 0xff, +}; + +/* / Encryption type enum used with peer */ +typedef enum eAniEdType { + eSIR_ED_NONE, + eSIR_ED_WEP40, + eSIR_ED_WEP104, + eSIR_ED_TKIP, + eSIR_ED_CCMP, +#if defined(FEATURE_WLAN_WAPI) + eSIR_ED_WPI, +#endif + /*DPU HW treats encryption mode 4 plus RMF bit set in TX BD as BIP. + Thus while setting BIP encryption mode in corresponding DPU Desc + eSIR_ED_AES_128_CMAC should be set to eSIR_ED_CCMP */ + eSIR_ED_AES_128_CMAC, + /* Firmware uses key length to find GCMP 128 or 256 */ + eSIR_ED_GCMP, + eSIR_ED_GCMP_256, + eSIR_ED_AES_GMAC_128, + eSIR_ED_AES_GMAC_256, + eSIR_ED_NOT_IMPLEMENTED = SIR_MAX_ENUM_SIZE +} tAniEdType; + +/* / Enum to specify whether key is used */ +/* / for TX only, RX only or both */ +typedef enum eAniKeyDirection { + eSIR_TX_ONLY, + eSIR_RX_ONLY, + eSIR_TX_RX, + eSIR_TX_DEFAULT, + eSIR_DONOT_USE_KEY_DIRECTION = SIR_MAX_ENUM_SIZE +} tAniKeyDirection; + +typedef struct sAniSSID { + uint8_t length; + uint8_t ssId[WLAN_SSID_MAX_LEN]; +} tAniSSID, *tpAniSSID; + +/* / RSN IE information */ +typedef struct sSirRSNie { + uint16_t length; + uint8_t rsnIEdata[WLAN_MAX_IE_LEN + 2]; +} tSirRSNie, *tpSirRSNie; + +typedef struct sSirWAPIie { + uint16_t length; + uint8_t wapiIEdata[WLAN_MAX_IE_LEN + 2]; +} tSirWAPIie, *tpSirWAPIie; +/* / Additional IE information : */ +/* / This can include WSC IE, P2P IE, and/or FTIE from upper layer. */ +/* / MAC layer transparently convey these IE info between peer STA and upper layer, */ +/* / but never requires to parse it. */ +typedef struct sSirAddie { + uint16_t length; + uint8_t addIEdata[SIR_MAC_MAX_ADD_IE_LENGTH + 2]; +} tSirAddie, *tpSirAddie; + +#ifdef FEATURE_WLAN_ESE + +/* The CCKM IE needs to be in the */ +/* Join and Reassoc Req. */ +typedef struct sSirCCKMie { + uint16_t length; + uint8_t cckmIEdata[WLAN_MAX_IE_LEN + 2]; +} tSirCCKMie, *tpSirCCKMie; + +#endif + +/* / Definition for Encryption Keys */ +typedef struct sSirKeys { + uint8_t keyId; + uint8_t unicast; /* 0 for multicast */ + tAniKeyDirection keyDirection; + uint8_t keyRsc[WLAN_CRYPTO_RSC_SIZE]; /* Usage is unknown */ + uint8_t paeRole; /* =1 for authenticator, */ + /* =0 for supplicant */ + uint16_t keyLength; + uint8_t key[SIR_MAC_MAX_KEY_LENGTH]; +} tSirKeys, *tpSirKeys; + +/* / Definition for Keying material */ +typedef struct sSirKeyMaterial { + uint16_t length; /* This is the length of all */ + /* data that follows */ + tAniEdType edType; /* Encryption/Decryption type */ + uint8_t numKeys; + tSirKeys key[1]; +} tSirKeyMaterial, *tpSirKeyMaterial; + +#define SIR_CIPHER_SEQ_CTR_SIZE 6 +/* / Definition for MIC failure indication */ +typedef struct sSirMicFailureInfo { + tSirMacAddr srcMacAddr; /* address used to compute MIC */ + tSirMacAddr taMacAddr; /* transmitter address */ + tSirMacAddr dstMacAddr; + bool multicast; + uint8_t IV1; /* first byte of IV */ + uint8_t keyId; /* second byte of IV */ + uint8_t TSC[SIR_CIPHER_SEQ_CTR_SIZE]; /* sequence number */ + tSirMacAddr rxMacAddr; /* receive address */ + +} tSirMicFailureInfo, *tpSirMicFailureInfo; + +typedef struct sTrafStrmMetrics { + uint16_t UplinkPktQueueDly; + uint16_t UplinkPktQueueDlyHist[4]; + uint32_t UplinkPktTxDly; + uint16_t UplinkPktLoss; + uint16_t UplinkPktCount; + uint8_t RoamingCount; + uint16_t RoamingDly; +} qdf_packed tTrafStrmMetrics, *tpTrafStrmMetrics; + +typedef struct sBcnReportFields { + uint8_t ChanNum; + uint8_t Spare; + uint16_t MeasDuration; + uint8_t PhyType; + uint8_t RecvSigPower; + tSirMacAddr Bssid; + uint32_t ParentTsf; + uint32_t TargetTsf[2]; + uint16_t BcnInterval; + uint16_t CapabilityInfo; +} qdf_packed tBcnReportFields, *tpBcnReportFields; + +#endif /* __ANI_SYSTEM_DEFS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/mac_init_api.h b/drivers/staging/qcacld-3.0/core/mac/inc/mac_init_api.h new file mode 100644 index 0000000000000000000000000000000000000000..1190df8e306c55dbbf5122f997c1a3e624c9b3c0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/mac_init_api.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * mac_init_api.c - Header file for mac level init functions + * Author: Dinesh Upadhyay + * Date: 04/23/2007 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------------- + * + */ +#ifndef __MAC_INIT_API_H +#define __MAC_INIT_API_H + +#include "ani_global.h" +#include "sir_types.h" + +/** + * struct mac_start_params - parameters needed when starting the MAC + * @driver_type: Operating mode of the driver + */ +struct mac_start_params { + enum qdf_driver_type driver_type; +}; + +/** + * mac_start() - Start all MAC modules + * @mac_handle: Opaque handle to the MAC context + * @params: Parameters needed to start the MAC + * + * This function is called to start MAC. This function will start all + * the mac modules. + * + * Return: QDF_STATUS_SUCCESS if the MAC was successfully started. Any + * other value means that there was an issue with starting the + * MAC and the MAC should not be considered operational. + */ +QDF_STATUS mac_start(mac_handle_t mac_handle, + struct mac_start_params *params); + +/** + * mac_stop() - Stop all MAC modules + * @mac_handle: Opaque handle to the MAC context + * + * This function is called to stop MAC. This function will stop all + * the mac modules. + * + * Return: QDF_STATUS_SUCCESS if the MAC was successfully stopped. Any + * other value means that there was an issue with stopping the + * MAC, but the caller should still consider the MAC to be + * stopped. + */ +QDF_STATUS mac_stop(mac_handle_t mac_handle); + +/** + * mac_open() - Open the MAC + * @psoc: SOC global object + * @mac_handle: Pointer to where the MAC handle is to be stored + * @hdd_handle: Opaque handle to the HDD context + * @cds_cfg: Initial configuration + * + * This function will be called during init. This function is suppose + * to allocate all the memory with the global context will be + * allocated here. + * + * Return: QDF_STATUS_SUCCESS if the MAC was successfully opened and a + * MAC handle was returned to the caller. Any other value + * means the MAC was not opened. + */ +QDF_STATUS mac_open(struct wlan_objmgr_psoc *psoc, mac_handle_t *mac_handle, + hdd_handle_t hdd_handle, struct cds_config_info *cds_cfg); + +/** + * mac_close() - close the MAC + * @mac_handle: Opaque handle to the MAC context returned by mac_open() + * + * This function will be called in shutdown sequence from HDD. All the + * allocated memory with global context will be freed here. + * + * Return: QDF_STATUS_SUCCESS if the MAC was successfully closed. Any + * other value means that there was an issue with closing the + * MAC, but the caller should still consider the MAC to be + * closed. + */ +QDF_STATUS mac_close(mac_handle_t mac_handle); + +/** + * mac_register_sesssion_open_close_cb() - register open/close session cb + * @mac_handle: Opaque handle to the MAC context + * @close_session: callback to be registered with SME for closing the session + * @callback: Common callback to hdd for all modes + */ +void mac_register_sesssion_open_close_cb(mac_handle_t mac_handle, + csr_session_close_cb close_session, + csr_roam_complete_cb callback); + +#endif /* __MAC_INIT_API_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/mac_trace.h b/drivers/staging/qcacld-3.0/core/mac/inc/mac_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..90150d056bd9831726f28a3e8d68d7737f30141f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/mac_trace.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2013-2016, 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + * \file mac_trace.h + + * \brief definition for trace related APIs + + \author Sunit Bhatia + + ========================================================================*/ + +#ifndef __MAC_TRACE_H +#define __MAC_TRACE_H + +#include "ani_global.h" +#include "qdf_trace.h" + +#define MAC_TRACE_GET_MODULE_ID(data) ((data >> 8) & 0xff) +#define MAC_TRACE_GET_MSG_ID(data) (data & 0xffff) + +/** + * mac_trace() - Main function used for MAC Trace + * @mac_ctx: Global MAC context + * @code: trace code + * @session: session id + * @data: data to be traced. + * + * Return: None + */ +static inline void mac_trace(struct mac_context *mac_ctx, uint8_t code, + uint16_t session, uint32_t data) +{ + qdf_trace(QDF_MODULE_ID_PE, code, session, data); +} + +#ifdef TRACE_RECORD + +#define eLOG_NODROP_MISSED_BEACON_SCENARIO 0 +#define eLOG_PROC_DEAUTH_FRAME_SCENARIO 1 + +uint8_t *mac_trace_get_lim_msg_string(uint16_t limMsg); +uint8_t *mac_trace_get_sme_msg_string(uint16_t smeMsg); +uint8_t *mac_trace_get_info_log_string(uint16_t infoLog); + +#endif +uint8_t *mac_trace_get_wma_msg_string(uint16_t wmaMsg); +uint8_t *mac_trace_get_neighbour_roam_state(uint16_t neighbourRoamState); +uint8_t *mac_trace_getcsr_roam_state(uint16_t csr_roamState); +uint8_t *mac_trace_getcsr_roam_sub_state(uint16_t csr_roamSubState); +uint8_t *mac_trace_get_lim_sme_state(uint16_t limState); +uint8_t *mac_trace_get_lim_mlm_state(uint16_t mlmState); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/qwlan_version.h b/drivers/staging/qcacld-3.0/core/mac/inc/qwlan_version.h new file mode 100644 index 0000000000000000000000000000000000000000..97805cbfce99284af501736c7669bc6035558e9c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/qwlan_version.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef QWLAN_VERSION_H +#define QWLAN_VERSION_H +/*=========================================================================== + + FILE: + qwlan_version.h + + BRIEF DESCRIPTION: + WLAN Host Version file. + Build number automatically updated by build scripts. + + ===========================================================================*/ + +#define QWLAN_VERSION_MAJOR 5 +#define QWLAN_VERSION_MINOR 2 +#define QWLAN_VERSION_PATCH 022 +#define QWLAN_VERSION_EXTRA "T" +#define QWLAN_VERSION_BUILD 11 + +#define QWLAN_VERSIONSTR "5.2.022.11T" + +#endif /* QWLAN_VERSION_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/sir_api.h b/drivers/staging/qcacld-3.0/core/mac/inc/sir_api.h new file mode 100644 index 0000000000000000000000000000000000000000..e9c073c61ea3c80a463b190ffd30ee9e6d1bad8c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/sir_api.h @@ -0,0 +1,5820 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file sir_api.h contains definitions exported by + * Sirius software. + * Author: Chandra Modumudi + * Date: 04/16/2002 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#ifndef __SIR_API_H +#define __SIR_API_H + +/* legacy definition */ +typedef void *tpAniSirGlobal; + +struct mac_context; +#include "qdf_types.h" +#include "cds_regdomain.h" +#include "sir_types.h" +#include "sir_mac_prot_def.h" +#include "ani_system_defs.h" +#include "sir_params.h" +#include "cds_regdomain.h" +#include "wmi_unified.h" +#include "wmi_unified_param.h" +#include "ol_txrx_htt_api.h" +#include "wlan_reg_services_api.h" +#include +#include "wlan_policy_mgr_api.h" +#include "wlan_tdls_public_structs.h" +#include "qca_vendor.h" +#include "wlan_cp_stats_mc_defs.h" + +#define OFFSET_OF(structType, fldName) (&((structType *)0)->fldName) + +/* / Max supported channel list */ +#define SIR_MAX_SUPPORTED_CHANNEL_LIST 96 +#define CFG_VALID_CHANNEL_LIST_LEN 100 +#define CFG_COUNTRY_CODE_LEN 3 + +#define SIR_MDIE_SIZE 3 /* MD ID(2 bytes), Capability(1 byte) */ + +#define SIR_MAX_ELEMENT_ID 255 + +#define SIR_BCN_REPORT_MAX_BSS_DESC 4 + +#define SIR_NUM_11B_RATES 4 /* 1,2,5.5,11 */ +#define SIR_NUM_11A_RATES 8 /* 6,9,12,18,24,36,48,54 */ + +typedef uint8_t tSirIpv4Addr[QDF_IPV4_ADDR_SIZE]; + +#define SIR_VERSION_STRING_LEN 64 +typedef uint8_t tSirVersionString[SIR_VERSION_STRING_LEN]; + +/* Periodic Tx pattern offload feature */ +#define PERIODIC_TX_PTRN_MAX_SIZE 1536 +#define MAXNUM_PERIODIC_TX_PTRNS 6 + +/* FW response timeout values in milli seconds */ +#define SIR_PEER_ASSOC_TIMEOUT (4000) /* 4 seconds */ + +#ifdef FEATURE_RUNTIME_PM +/* Add extra PMO_RESUME_TIMEOUT for runtime PM resume timeout */ +#define SIR_DELETE_STA_TIMEOUT (4000 + PMO_RESUME_TIMEOUT) +#define SIR_VDEV_PLCY_MGR_TIMEOUT (2000 + PMO_RESUME_TIMEOUT) +#else +#define SIR_DELETE_STA_TIMEOUT (4000) /* 4 seconds */ +#define SIR_VDEV_PLCY_MGR_TIMEOUT (2000) +#endif + +/* This should not be greater than MAX_NUMBER_OF_CONC_CONNECTIONS */ +#define MAX_VDEV_SUPPORTED 4 + +#define MAX_POWER_DBG_ARGS_SUPPORTED 8 +#define QOS_MAP_MAX_EX 21 +#define QOS_MAP_RANGE_NUM 8 +#define QOS_MAP_LEN_MIN (QOS_MAP_RANGE_NUM * 2) +#define QOS_MAP_LEN_MAX \ + (QOS_MAP_LEN_MIN + 2 * QOS_MAP_MAX_EX) +#define NUM_CHAINS_MAX 2 + +/* Maximum number of realms present in fils indication element */ +#define SIR_MAX_REALM_COUNT 7 +/* Realm length */ +#define SIR_REALM_LEN 2 +/* Cache ID length */ +#define CACHE_ID_LEN 2 + +/* Maximum peer station number query one time */ +#define MAX_PEER_STA 12 + +/* Maximum number of peers for SAP */ +#ifndef SIR_SAP_MAX_NUM_PEERS +#define SIR_SAP_MAX_NUM_PEERS 32 +#endif + +#define SIR_KRK_KEY_LEN 16 +#define SIR_BTK_KEY_LEN 32 +#define SIR_KCK_KEY_LEN 16 +#define KCK_192BIT_KEY_LEN 24 +#define KCK_256BIT_KEY_LEN 32 + +#define SIR_KEK_KEY_LEN 16 +#define SIR_KEK_KEY_LEN_FILS 64 +#define KEK_256BIT_KEY_LEN 32 + +#define SIR_REPLAY_CTR_LEN 8 +#define SIR_PMK_LEN 48 +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +#define SIR_UAPSD_BITOFFSET_ACVO 0 +#define SIR_UAPSD_BITOFFSET_ACVI 1 +#define SIR_UAPSD_BITOFFSET_ACBK 2 +#define SIR_UAPSD_BITOFFSET_ACBE 3 + +#define SIR_UAPSD_FLAG_ACVO (1 << SIR_UAPSD_BITOFFSET_ACVO) +#define SIR_UAPSD_FLAG_ACVI (1 << SIR_UAPSD_BITOFFSET_ACVI) +#define SIR_UAPSD_FLAG_ACBK (1 << SIR_UAPSD_BITOFFSET_ACBK) +#define SIR_UAPSD_FLAG_ACBE (1 << SIR_UAPSD_BITOFFSET_ACBE) +#define SIR_UAPSD_GET(ac, mask) (((mask) & (SIR_UAPSD_FLAG_ ## ac)) >> SIR_UAPSD_BITOFFSET_ ## ac) + +#endif + +struct scheduler_msg; + +/** + * enum sir_roam_op_code - Operation to be done by the callback. + * @SIR_ROAM_SYNCH_PROPAGATION: Propagate the new BSS info after roaming. + * @SIR_ROAMING_DEREGISTER_STA: Deregister the old STA after roaming. + * @SIR_ROAMING_START: Firmware started roaming operation + * @SIR_ROAMING_ABORT: Firmware aborted roaming operation, still connected. + * @SIR_ROAM_SYNCH_COMPLETE: Roam sync propagation is complete. + * @SIR_ROAMING_INVOKE_FAIL: Firmware roaming failed. + * @SIR_ROAMING_DEAUTH: Firmware indicates deauth. + */ +enum sir_roam_op_code { + SIR_ROAM_SYNCH_PROPAGATION = 1, + SIR_ROAMING_DEREGISTER_STA, + SIR_ROAMING_START, + SIR_ROAMING_ABORT, + SIR_ROAM_SYNCH_COMPLETE, + SIR_ROAM_SYNCH_NAPI_OFF, + SIR_ROAMING_INVOKE_FAIL, + SIR_ROAMING_DEAUTH, +}; +/** + * Module ID definitions. + */ +enum { + SIR_HAL_MODULE_ID = 0x10, + SIR_LIM_MODULE_ID = 0x13, + SIR_SME_MODULE_ID, +}; + +#define SIR_WMA_MODULE_ID SIR_HAL_MODULE_ID + +/* Type declarations used by Firmware and Host software */ + +/* Scan type enum used in scan request */ +typedef enum eSirScanType { + eSIR_PASSIVE_SCAN, + eSIR_ACTIVE_SCAN, + eSIR_BEACON_TABLE, +} tSirScanType; + +/* Rsn Capabilities structure */ +struct rsn_caps { + uint16_t PreAuthSupported:1; + uint16_t NoPairwise:1; + uint16_t PTKSAReplayCounter:2; + uint16_t GTKSAReplayCounter:2; + uint16_t MFPRequired:1; + uint16_t MFPCapable:1; + uint16_t Reserved:8; +}; + +/** + * struct roam_scan_ch_resp - roam scan chan list response to userspace + * @vdev_id: vdev id + * @num_channels: number of roam scan channels + * @command_resp: command response or async event + * @chan_list: list of roam scan channels + */ +struct roam_scan_ch_resp { + uint16_t vdev_id; + uint16_t num_channels; + uint32_t command_resp; + uint32_t *chan_list; +}; + +/** + * struct wlan_beacon_report - Beacon info to be send to userspace + * @vdev_id: vdev id + * @ssid: ssid present in beacon + * @bssid: bssid present in beacon + * @frequency: channel frequency in MHz + * @beacon_interval: Interval between two consecutive beacons + * @time_stamp: time stamp at which beacon received from AP + * @boot_time: Boot time when beacon received + */ +struct wlan_beacon_report { + uint8_t vdev_id; + struct wlan_ssid ssid; + struct qdf_mac_addr bssid; + uint32_t frequency; + uint16_t beacon_interval; + qdf_time_t time_stamp; + qdf_time_t boot_time; +}; + + +/* / Result codes Firmware return to Host SW */ +typedef enum eSirResultCodes { + eSIR_SME_SUCCESS, + eSIR_LOGE_EXCEPTION, + eSIR_SME_INVALID_PARAMETERS = 500, + eSIR_SME_UNEXPECTED_REQ_RESULT_CODE, + eSIR_SME_RESOURCES_UNAVAILABLE, + /* Unable to find a BssDescription */ + eSIR_SME_SCAN_FAILED, + /* matching requested scan criteria */ + eSIR_SME_BSS_ALREADY_STARTED_OR_JOINED, + eSIR_SME_LOST_LINK_WITH_PEER_RESULT_CODE, + eSIR_SME_REFUSED, + eSIR_SME_JOIN_DEAUTH_FROM_AP_DURING_ADD_STA, + eSIR_SME_JOIN_TIMEOUT_RESULT_CODE, + eSIR_SME_AUTH_TIMEOUT_RESULT_CODE, + eSIR_SME_ASSOC_TIMEOUT_RESULT_CODE, + eSIR_SME_REASSOC_TIMEOUT_RESULT_CODE, + eSIR_SME_MAX_NUM_OF_PRE_AUTH_REACHED, + eSIR_SME_AUTH_REFUSED, + eSIR_SME_INVALID_WEP_DEFAULT_KEY, + eSIR_SME_NO_KEY_MAPPING_KEY_FOR_PEER, + eSIR_SME_ASSOC_REFUSED, + eSIR_SME_REASSOC_REFUSED, + /* Recvd Deauth while join/pre-auth */ + eSIR_SME_DEAUTH_WHILE_JOIN, + eSIR_SME_STA_NOT_AUTHENTICATED, + eSIR_SME_STA_NOT_ASSOCIATED, + eSIR_SME_ALREADY_JOINED_A_BSS, + /* Given in SME_SCAN_RSP msg */ + eSIR_SME_MORE_SCAN_RESULTS_FOLLOW, + /* that more SME_SCAN_RSP */ + /* messages are following. */ + /* SME_SCAN_RSP message with */ + /* eSIR_SME_SUCCESS status */ + /* code is the last one. */ + /* Sent in SME_JOIN/REASSOC_RSP */ + eSIR_SME_INVALID_ASSOC_RSP_RXED, + /* messages upon receiving */ + /* invalid Re/Assoc Rsp frame. */ + /* STOP BSS triggered by MIC failures: MAC software to + * disassoc all stations + */ + eSIR_SME_MIC_COUNTER_MEASURES, + /* with MIC_FAILURE reason code and perform the stop bss operation */ + /* didn't get rsp from peer within timeout interval */ + eSIR_SME_ADDTS_RSP_TIMEOUT, + /* didn't get success rsp from HAL */ + eSIR_SME_ADDTS_RSP_FAILED, + /* failed to send ch switch act frm */ + eSIR_SME_CHANNEL_SWITCH_FAIL, + eSIR_SME_INVALID_STATE, + /* SIR_HAL_SIR_HAL_INIT_SCAN_RSP returned failed status */ + eSIR_SME_HAL_SCAN_INIT_FAILED, + /* SIR_HAL_END_SCAN_RSP returned failed status */ + eSIR_SME_HAL_SCAN_END_FAILED, + /* SIR_HAL_FINISH_SCAN_RSP returned failed status */ + eSIR_SME_HAL_SCAN_FINISH_FAILED, + /* Failed to send a message to HAL */ + eSIR_SME_HAL_SEND_MESSAGE_FAIL, + /* Failed to stop the bss */ + eSIR_SME_STOP_BSS_FAILURE, + eSIR_SME_WOWL_ENTER_REQ_FAILED, + eSIR_SME_WOWL_EXIT_REQ_FAILED, + eSIR_SME_FT_REASSOC_TIMEOUT_FAILURE, + eSIR_SME_FT_REASSOC_FAILURE, + eSIR_SME_SEND_ACTION_FAIL, + eSIR_SME_DEAUTH_STATUS, + eSIR_PNO_SCAN_SUCCESS, + eSIR_SME_INVALID_SESSION, + eSIR_SME_PEER_CREATE_FAILED, + eSIR_DONOT_USE_RESULT_CODE = SIR_MAX_ENUM_SIZE +} tSirResultCodes; + +#ifdef WLAN_FEATURE_FILS_SK +struct fils_join_rsp_params { + uint8_t *fils_pmk; + uint8_t fils_pmk_len; + uint8_t fils_pmkid[PMKID_LEN]; + uint8_t kek[MAX_KEK_LEN]; + uint8_t kek_len; + uint8_t tk[MAX_TK_LEN]; + uint8_t tk_len; + uint8_t gtk_len; + uint8_t gtk[MAX_GTK_LEN]; + struct qdf_mac_addr dst_mac; + struct qdf_mac_addr src_mac; + uint16_t hlp_data_len; + uint8_t hlp_data[FILS_MAX_HLP_DATA_LEN]; +}; +#endif + +#define RMENABLEDCAP_MAX_LEN 5 + +struct rrm_config_param { + uint8_t rrm_enabled; + bool sap_rrm_enabled; + uint8_t max_randn_interval; + uint8_t rm_capability[RMENABLEDCAP_MAX_LEN]; +}; + +const char *lim_bss_type_to_string(const uint16_t bss_type); +/** + * struct supported_rates - stores rates/MCS supported + * @llbRates: 11b rates in unit of 500kbps + * @llaRates: 11a rates in unit of 500kbps + * @supportedMCSSet: supported basic MCS, 0-76 bits used, remaining reserved + * bits 0-15 and 32 should be set. + * @rxHighestDataRate: RX Highest Supported Data Rate defines the highest data + * rate that the STA is able to receive, in unites of 1Mbps + * This value is derived from "Supported MCS Set field" + * inside the HT capability element. + * @vhtRxMCSMap: Indicates the Maximum MCS(VHT) that can be received for each + * number of spacial streams + * @vhtRxHighestDataRate: Indicate the highest VHT data rate that the STA is + * able to receive + * @vhtTxMCSMap: Indicates the Maximum MCS(VHT) that can be transmitted for + * each number of spacial streams + * @vhtTxHighestDataRate: Indicate the highest VHT data rate that the STA is + * able to transmit + * @he_rx_mcs: Indicates the Maximum MCS(HE) that can be received for each + * number of spacial streams + * @he_tx_mcs: Indicates the Maximum MCS(HE) that can be transmitted for each + * number of spacial streams + */ +struct supported_rates { + uint16_t llbRates[SIR_NUM_11B_RATES]; + uint16_t llaRates[SIR_NUM_11A_RATES]; + uint8_t supportedMCSSet[SIR_MAC_MAX_SUPPORTED_MCS_SET]; + uint16_t rxHighestDataRate; + uint16_t vhtRxMCSMap; + uint16_t vhtRxHighestDataRate; + uint16_t vhtTxMCSMap; + uint16_t vhtTxHighestDataRate; +#ifdef WLAN_FEATURE_11AX + uint16_t rx_he_mcs_map_lt_80; + uint16_t tx_he_mcs_map_lt_80; + uint16_t rx_he_mcs_map_160; + uint16_t tx_he_mcs_map_160; + uint16_t rx_he_mcs_map_80_80; + uint16_t tx_he_mcs_map_80_80; +#endif +}; + +struct register_mgmt_frame { + uint16_t messageType; + uint16_t length; + uint8_t sessionId; + bool registerFrame; + uint16_t frameType; + uint16_t matchLen; + uint8_t matchData[1]; +}; + +/* / Generic type for sending a response message */ +/* / with result code to host software */ +typedef struct sSirSmeRsp { + uint16_t messageType; /* eWNI_SME_*_RSP */ + uint16_t length; + uint8_t vdev_id; + tSirResultCodes status_code; + struct wlan_objmgr_psoc *psoc; +} tSirSmeRsp, *tpSirSmeRsp; + +/* / Definition for indicating all modules ready on STA */ +struct sme_ready_req { + uint16_t messageType; /* eWNI_SME_SYS_READY_IND */ + uint16_t length; + void *csr_roam_synch_cb; + QDF_STATUS (*csr_roam_auth_event_handle_cb)(struct mac_context *mac, + uint8_t vdev_id, + struct qdf_mac_addr bssid); + void *pe_roam_synch_cb; + void *stop_roaming_cb; + QDF_STATUS (*sme_msg_cb)(struct mac_context *mac, + struct scheduler_msg *msg); + QDF_STATUS (*pe_disconnect_cb) (struct mac_context *mac, + uint8_t vdev_id, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len, + uint16_t reason_code); + void *csr_roam_pmkid_req_cb; +}; + +/** + * struct s_sir_set_hw_mode - Set HW mode request + * @messageType: Message type + * @length: Length of the message + * @set_hw: Params containing the HW mode index and callback + */ +struct s_sir_set_hw_mode { + uint16_t messageType; + uint16_t length; + struct policy_mgr_hw_mode set_hw; +}; + +/** + * struct sir_set_dual_mac_cfg - Set Dual mac config request + * @message_type: Message type + * @length: Length of the message + * @set_dual_mac: Params containing the dual mac config and callback + */ +struct sir_set_dual_mac_cfg { + uint16_t message_type; + uint16_t length; + struct policy_mgr_dual_mac_config set_dual_mac; +}; + +/** + * struct sir_antenna_mode_param - antenna mode param + * @num_tx_chains: Number of TX chains + * @num_rx_chains: Number of RX chains + * @set_antenna_mode_resp: callback to set antenna mode command + * @set_antenna_mode_ctx: callback context to set antenna mode command + */ +struct sir_antenna_mode_param { + uint32_t num_tx_chains; + uint32_t num_rx_chains; + void *set_antenna_mode_resp; + void *set_antenna_mode_ctx; +}; + +/** + * struct sir_set_antenna_mode - Set antenna mode request + * @message_type: Message type + * @length: Length of the message + * @set_antenna_mode: Params containing antenna mode params + */ +struct sir_set_antenna_mode { + uint16_t message_type; + uint16_t length; + struct sir_antenna_mode_param set_antenna_mode; +}; + +/** + * enum bss_type - Enum for BSS type used in scanning/joining etc. + * + * @eSIR_INFRASTRUCTURE_MODE: Infrastructure station + * @eSIR_INFRA_AP_MODE: softAP mode + * @eSIR_IBSS_MODE: IBSS mode + * @eSIR_AUTO_MODE: Auto role + * @eSIR_MONITOR_MODE: Monitor mode + * @eSIR_NDI_MODE: NAN datapath mode + */ +enum bss_type { + eSIR_INFRASTRUCTURE_MODE, + eSIR_INFRA_AP_MODE, + eSIR_IBSS_MODE, + eSIR_AUTO_MODE, + eSIR_MONITOR_MODE, + eSIR_NDI_MODE, + eSIR_DONOT_USE_BSS_TYPE = SIR_MAX_ENUM_SIZE +}; + +/* / Power Capability info used in 11H */ +struct power_cap_info { + uint8_t minTxPower; + uint8_t maxTxPower; +}; + +/* / Supported Channel info used in 11H */ +struct supported_channels { + uint8_t numChnl; + uint8_t channelList[SIR_MAX_SUPPORTED_CHANNEL_LIST]; +}; + +typedef enum eSirNwType { + eSIR_11A_NW_TYPE, + eSIR_11B_NW_TYPE, + eSIR_11G_NW_TYPE, + eSIR_11N_NW_TYPE, + eSIR_11AC_NW_TYPE, + eSIR_11AX_NW_TYPE, + eSIR_DONOT_USE_NW_TYPE = SIR_MAX_ENUM_SIZE +} tSirNwType; + +/* HT configuration values */ +struct ht_config { + /* Enable/Disable receiving LDPC coded packets */ + uint32_t ht_rx_ldpc:1; + /* Enable/Disable TX STBC */ + uint32_t ht_tx_stbc:1; + /* Enable/Disable RX STBC */ + uint32_t ht_rx_stbc:2; + /* Enable/Disable SGI */ + uint32_t ht_sgi20:1; + uint32_t ht_sgi40:1; + uint32_t unused:27; +}; + +/** + * struct sir_vht_config - VHT capabilities + * @max_mpdu_len: MPDU length + * @supported_channel_widthset: channel width set + * @ldpc_coding: LDPC coding capability + * @shortgi80: short GI 80 support + * @shortgi160and80plus80: short Gi 160 & 80+80 support + * @tx_stbc; Tx STBC cap + * @tx_stbc: Rx STBC cap + * @su_beam_former: SU beam former cap + * @su_beam_formee: SU beam formee cap + * @csnof_beamformer_antSup: Antenna support for beamforming + * @num_soundingdim: Sound dimensions + * @mu_beam_former: MU beam former cap + * @mu_beam_formee: MU beam formee cap + * @vht_txops: TXOP power save + * @htc_vhtcap: HTC VHT capability + * @max_ampdu_lenexp: AMPDU length + * @vht_link_adapt: VHT link adapatation capable + * @rx_antpattern: Rx Antenna pattern + * @tx_antpattern: Tx Antenna pattern + */ +struct sir_vht_config { + uint32_t max_mpdu_len:2; + uint32_t supported_channel_widthset:2; + uint32_t ldpc_coding:1; + uint32_t shortgi80:1; + uint32_t shortgi160and80plus80:1; + uint32_t tx_stbc:1; + uint32_t rx_stbc:3; + uint32_t su_beam_former:1; + uint32_t su_beam_formee:1; + uint32_t csnof_beamformer_antSup:3; + uint32_t num_soundingdim:3; + uint32_t mu_beam_former:1; + uint32_t mu_beam_formee:1; + uint32_t vht_txops:1; + uint32_t htc_vhtcap:1; + uint32_t max_ampdu_lenexp:3; + uint32_t vht_link_adapt:2; + uint32_t rx_antpattern:1; + uint32_t tx_antpattern:1; + uint32_t extended_nss_bw_supp:2; + uint8_t max_nsts_total:2; + uint8_t vht_extended_nss_bw_cap:1; +}; + + +struct add_ie_params { + uint16_t probeRespDataLen; + uint8_t *probeRespData_buff; + uint16_t assocRespDataLen; + uint8_t *assocRespData_buff; + uint16_t probeRespBCNDataLen; + uint8_t *probeRespBCNData_buff; +}; + +/* / Definition for kick starting BSS */ +/* / ---> MAC */ +/** + * Usage of ssId, numSSID & ssIdList: + * --------------------------------- + * 1. ssId.length of zero indicates that Broadcast/Suppress SSID + * feature is enabled. + * 2. If ssId.length is zero, MAC SW will advertise NULL SSID + * and interpret the SSID list from numSSID & ssIdList. + * 3. If ssId.length is non-zero, MAC SW will advertise the SSID + * specified in the ssId field and it is expected that + * application will set numSSID to one (only one SSID present + * in the list) and SSID in the list is same as ssId field. + * 4. Application will always set numSSID >= 1. + */ +/* ***** NOTE: Please make sure all codes are updated if inserting field into + * this structure..********** */ +struct start_bss_req { + uint16_t messageType; /* eWNI_SME_START_BSS_REQ */ + uint16_t length; + uint8_t vdev_id; + struct qdf_mac_addr bssid; + struct qdf_mac_addr self_macaddr; + uint16_t beaconInterval; + uint8_t dot11mode; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + uint8_t cc_switch_mode; +#endif + enum bss_type bssType; + tSirMacSSid ssId; + uint32_t oper_ch_freq; + ePhyChanBondState cbMode; + uint8_t vht_channel_width; + uint8_t center_freq_seg0; + uint8_t center_freq_seg1; + uint8_t sec_ch_offset; + + uint8_t privacy; + uint8_t apUapsdEnable; + uint8_t ssidHidden; + bool fwdWPSPBCProbeReq; + bool protEnabled; + bool obssProtEnabled; + uint16_t ht_capab; + tAniAuthType authType; + uint32_t dtimPeriod; + uint8_t wps_state; + uint8_t isCoalesingInIBSSAllowed; /* Coalesing on/off knob */ + enum QDF_OPMODE bssPersona; + + uint8_t txLdpcIniFeatureEnabled; + + tSirRSNie rsnIE; /* RSN IE to be sent in */ + /* Beacon and Probe */ + /* Response frames */ + tSirNwType nwType; /* Indicates 11a/b/g */ + tSirMacRateSet operationalRateSet; /* Has 11a or 11b rates */ + tSirMacRateSet extendedRateSet; /* Has 11g rates */ + struct ht_config ht_config; + struct sir_vht_config vht_config; +#ifdef WLAN_FEATURE_11AX + tDot11fIEhe_cap he_config; +#endif +#ifdef WLAN_FEATURE_11W + bool pmfCapable; + bool pmfRequired; +#endif + + struct add_ie_params add_ie_params; + + bool obssEnabled; + uint8_t sap_dot11mc; + uint16_t beacon_tx_rate; + bool vendor_vht_sap; + uint32_t cac_duration_ms; + uint32_t dfs_regdomain; + +}; + +#define GET_IE_LEN_IN_BSS(lenInBss) (lenInBss + sizeof(lenInBss) - \ + ((uintptr_t)OFFSET_OF(struct bss_description,\ + ieFields))) + +#define WSCIE_PROBE_RSP_LEN (317 + 2) + +#ifdef WLAN_FEATURE_FILS_SK +/* struct fils_ind_elements: elements parsed from fils indication present + * in beacon/probe resp + * @realm_cnt: number of realm present + * @realm: realms + * @is_fils_sk_supported: if FILS SK supported + * @is_cache_id_present: if cache id present + * @cache_id: cache id + */ +struct fils_ind_elements { + uint16_t realm_cnt; + uint8_t realm[SIR_MAX_REALM_COUNT][SIR_REALM_LEN]; + bool is_fils_sk_supported; + bool is_cache_id_present; + uint8_t cache_id[CACHE_ID_LEN]; +}; +#endif + +struct bss_description { + /* offset of the ieFields from bssId. */ + uint16_t length; + tSirMacAddr bssId; + unsigned long scansystimensec; + uint32_t timeStamp[2]; + uint16_t beaconInterval; + uint16_t capabilityInfo; + tSirNwType nwType; /* Indicates 11a/b/g */ + int8_t rssi; + int8_t rssi_raw; + int8_t sinr; + /* channel frequency what peer sent in beacon/probersp. */ + uint32_t chan_freq; + /* Based on system time, not a relative time. */ + uint64_t received_time; + uint32_t parentTSF; + uint32_t startTSF[2]; + uint8_t mdiePresent; + /* MDIE for 11r, picked from the beacons */ + uint8_t mdie[SIR_MDIE_SIZE]; +#ifdef FEATURE_WLAN_ESE + uint16_t QBSSLoad_present; + uint16_t QBSSLoad_avail; +#endif + /* whether it is from a probe rsp */ + uint8_t fProbeRsp; + tSirMacSeqCtl seq_ctrl; + uint32_t tsf_delta; + struct scan_mbssid_info mbssid_info; +#ifdef WLAN_FEATURE_FILS_SK + struct fils_ind_elements fils_info_element; +#endif + uint32_t assoc_disallowed; + uint32_t adaptive_11r_ap; + uint32_t mbo_oce_enabled_ap; +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) + uint32_t is_single_pmk; +#endif + /* Please keep the structure 4 bytes aligned above the ieFields */ + uint32_t ieFields[1]; +}; + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +struct ht_profile { + uint8_t dot11mode; + uint8_t htCapability; + uint8_t htSupportedChannelWidthSet; + uint8_t htRecommendedTxWidthSet; + ePhyChanBondState htSecondaryChannelOffset; + uint8_t vhtCapability; + uint8_t apCenterChan; + uint8_t apChanWidth; +}; +#endif +/* / Definition for response message to previously */ +/* / issued start BSS request */ +/* / MAC ---> */ +struct start_bss_rsp { + uint16_t messageType; /* eWNI_SME_START_BSS_RSP */ + uint16_t length; + uint8_t sessionId; + tSirResultCodes status_code; + enum bss_type bssType; /* Add new type for WDS mode */ + uint16_t beaconInterval; /* Beacon Interval for both type */ + uint32_t staId; /* Station ID for Self */ +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + struct ht_profile ht_profile; +#endif + struct bss_description bssDescription; /* Peer BSS description */ +}; + +struct report_channel_list { + uint8_t num_channels; + uint32_t chan_freq_lst[SIR_ESE_MAX_MEAS_IE_REQS]; +}; + +#ifdef FEATURE_OEM_DATA_SUPPORT +struct oem_data_req { + uint32_t data_len; + uint8_t *data; +}; + +struct oem_data_rsp { + uint32_t rsp_len; + uint8_t *data; +}; +#endif /* FEATURE_OEM_DATA_SUPPORT */ + +#ifdef FEATURE_WLAN_ESE +typedef struct ese_wmm_tspec_ie { + uint16_t traffic_type:1; + uint16_t tsid:4; + uint16_t direction:2; + uint16_t access_policy:2; + uint16_t aggregation:1; + uint16_t psb:1; + uint16_t user_priority:3; + uint16_t tsinfo_ack_pol:2; + uint8_t tsinfo_rsvd:7; + uint8_t burst_size_defn:1; + uint16_t size:15; + uint16_t fixed:1; + uint16_t max_msdu_size; + uint32_t min_service_int; + uint32_t max_service_int; + uint32_t inactivity_int; + uint32_t suspension_int; + uint32_t service_start_time; + uint32_t min_data_rate; + uint32_t mean_data_rate; + uint32_t peak_data_rate; + uint32_t burst_size; + uint32_t delay_bound; + uint32_t min_phy_rate; + uint16_t surplus_bw_allowance; + uint16_t medium_time; +} qdf_packed ese_wmm_tspec_ie; + +typedef struct sTspecInfo { + uint8_t valid; + struct mac_tspec_ie tspec; +} tTspecInfo; + +#define SIR_ESE_MAX_TSPEC_IES 4 +typedef struct sESETspecTspecInfo { + uint8_t numTspecs; + tTspecInfo tspec[SIR_ESE_MAX_TSPEC_IES]; +} tESETspecInfo; + +struct tsm_ie { + uint8_t tsid; + uint8_t state; + uint16_t msmt_interval; +}; + +struct tsm_ie_ind { + struct tsm_ie tsm_ie; + uint8_t sessionId; +}; + +typedef struct sAniTrafStrmMetrics { + uint16_t UplinkPktQueueDly; + uint16_t UplinkPktQueueDlyHist[4]; + uint32_t UplinkPktTxDly; + uint16_t UplinkPktLoss; + uint16_t UplinkPktCount; + uint8_t RoamingCount; + uint16_t RoamingDly; +} tAniTrafStrmMetrics, *tpAniTrafStrmMetrics; + +typedef struct sAniGetTsmStatsReq { + /* Common for all types are requests */ + uint16_t msgType; /* message type is same as the request type */ + uint16_t msgLen; /* length of the entire request */ + uint8_t tid; /* traffic id */ + struct qdf_mac_addr bssId; + void *tsmStatsCallback; + void *pDevContext; /* device context */ +} tAniGetTsmStatsReq, *tpAniGetTsmStatsReq; + +typedef struct sAniGetTsmStatsRsp { + /* Common for all types are responses */ + uint16_t msgType; /* + * message type is same as + * the request type + */ + uint16_t msgLen; /* + * length of the entire request, + * includes the pStatsBuf length too + */ + uint8_t sessionId; + uint32_t rc; /* success/failure */ + struct qdf_mac_addr bssid; /* bssid to get the tsm stats for */ + tAniTrafStrmMetrics tsmMetrics; + void *tsmStatsReq; /* tsm stats request backup */ +} tAniGetTsmStatsRsp, *tpAniGetTsmStatsRsp; + +struct ese_bcn_report_bss_info { + tBcnReportFields bcnReportFields; + uint8_t ieLen; + uint8_t *pBuf; +}; + +struct ese_bcn_report_rsp { + uint16_t measurementToken; + uint8_t flag; /* Flag to report measurement done and more data */ + uint8_t numBss; + struct ese_bcn_report_bss_info + bcnRepBssInfo[SIR_BCN_REPORT_MAX_BSS_DESC]; +}; + +#define TSRS_11AG_RATE_6MBPS 0xC +#define TSRS_11B_RATE_5_5MBPS 0xB + +struct ese_tsrs_ie { + uint8_t tsid; + uint8_t rates[8]; +}; + +struct ese_tsm_ie { + uint8_t tsid; + uint8_t state; + uint16_t msmt_interval; +}; + +typedef struct sTSMStats { + uint8_t tid; + struct qdf_mac_addr bssid; + tTrafStrmMetrics tsmMetrics; +} tTSMStats, *tpTSMStats; +typedef struct sEseTSMContext { + uint8_t tid; + struct ese_tsm_ie tsmInfo; + tTrafStrmMetrics tsmMetrics; +} tEseTSMContext, *tpEseTSMContext; +typedef struct sEsePEContext { + tEseTSMContext tsm; +} tEsePEContext, *tpEsePEContext; + +#endif /* FEATURE_WLAN_ESE */ + +/* / Definition for join request */ +/* / ---> MAC */ +struct join_req { + uint16_t messageType; /* eWNI_SME_JOIN_REQ */ + uint16_t length; + uint8_t vdev_id; + tSirMacSSid ssId; + tSirMacAddr self_mac_addr; /* self Mac address */ + enum bss_type bsstype; /* add new type for BT-AMP STA and AP Modules */ + uint8_t dot11mode; /* to support BT-AMP */ +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + uint8_t cc_switch_mode; +#endif + enum QDF_OPMODE staPersona; /* Persona */ + bool wps_registration; + ePhyChanBondState cbMode; /* Pass CB mode value in Join. */ + + /*This contains the UAPSD Flag for all 4 AC + * B0: AC_VO UAPSD FLAG + * B1: AC_VI UAPSD FLAG + * B2: AC_BK UAPSD FLAG + * B3: AC_BE UASPD FLAG + */ + uint8_t uapsdPerAcBitmask; + + tSirMacRateSet operationalRateSet; /* Has 11a or 11b rates */ + tSirMacRateSet extendedRateSet; /* Has 11g rates */ + tSirRSNie rsnIE; /* RSN IE to be sent in */ + /* (Re) Association Request */ +#ifdef FEATURE_WLAN_ESE + /* CCMK IE to be included as handler for join and reassoc is */ + tSirCCKMie cckmIE; + /* the same. The join will never carry cckm, but will be set to */ + /* 0. */ +#endif + + tSirAddie addIEScan; /* Additional IE to be sent in */ + /* (unicast) Probe Request at the time of join */ + + tSirAddie addIEAssoc; /* Additional IE to be sent in */ + /* (Re) Association Request */ + + tAniEdType UCEncryptionType; + + tAniEdType MCEncryptionType; + enum ani_akm_type akm; + +#ifdef WLAN_FEATURE_11W + tAniEdType MgmtEncryptionType; +#endif + + bool is11Rconnection; + bool is_adaptive_11r_connection; +#ifdef FEATURE_WLAN_ESE + bool isESEFeatureIniEnabled; + bool isESEconnection; + tESETspecInfo eseTspecInfo; +#endif + + bool isFastTransitionEnabled; + bool isFastRoamIniFeatureEnabled; + + uint8_t txLdpcIniFeatureEnabled; + struct ht_config ht_config; + struct sir_vht_config vht_config; +#ifdef WLAN_FEATURE_11AX + tDot11fIEhe_cap he_config; +#endif + uint8_t enableVhtpAid; + uint8_t enableVhtGid; + uint8_t enableAmpduPs; + uint8_t enableHtSmps; + uint8_t htSmps; + bool send_smps_action; + bool he_with_wep_tkip; + uint8_t max_amsdu_num; + bool isWMEenabled; + bool isQosEnabled; + bool isOSENConnection; + struct rrm_config_param rrm_config; + bool spectrumMgtIndicator; + struct power_cap_info powerCap; + struct supported_channels supportedChannels; + bool enable_bcast_probe_rsp; +#ifdef WLAN_FEATURE_FILS_SK + struct cds_fils_connection_info fils_con_info; +#endif + bool sae_pmk_cached; + /* Pls make this as last variable in struct */ + bool force_24ghz_in_ht20; + bool force_rsne_override; + bool supported_nss_1x1; + uint8_t vdev_nss; + uint8_t nss; + bool nss_forced_1x1; + bool enable_session_twt_support; + struct bss_description bssDescription; + /* + * WARNING: Pls make bssDescription as last variable in struct + * join_req as it has ieFields followed after this bss + * description. Adding a variable after this corrupts the ieFields + */ +}; + +/* / Definition for response message to previously issued join request */ +/* / MAC ---> */ +struct join_rsp { + uint16_t messageType; /* eWNI_SME_JOIN_RSP */ + uint16_t length; + uint8_t vdev_id; /* Session ID */ + tSirResultCodes status_code; + tAniAuthType authType; + uint32_t vht_channel_width; + /* It holds reasonCode when join fails due to deauth/disassoc frame. + * Otherwise it holds status code. + */ + uint16_t protStatusCode; + uint16_t aid; + uint32_t beaconLength; + uint32_t assocReqLength; + uint32_t assocRspLength; + uint32_t parsedRicRspLen; + uint8_t uapsd_mask; +#ifdef FEATURE_WLAN_ESE + uint32_t tspecIeLen; +#endif + uint32_t staId; /* Station ID for peer */ + + /*Timing measurement capability */ + uint8_t timingMeasCap; + +#ifdef FEATURE_WLAN_TDLS + /* TDLS prohibited and TDLS channel switch prohibited are set as + * per ExtCap IE in received assoc/re-assoc response from AP + */ + bool tdls_prohibited; + bool tdls_chan_swit_prohibited; +#endif + uint8_t nss; + uint32_t max_rate_flags; + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + struct ht_profile ht_profile; +#endif + bool supported_nss_1x1; + tDot11fIEHTCaps ht_caps; + tDot11fIEVHTCaps vht_caps; + tDot11fIEHTInfo ht_operation; + tDot11fIEVHTOperation vht_operation; +#ifdef WLAN_FEATURE_11AX + tDot11fIEhe_op he_operation; +#endif + tDot11fIEhs20vendor_ie hs20vendor_ie; + bool is_fils_connection; + uint16_t fils_seq_num; +#ifdef WLAN_FEATURE_FILS_SK + struct fils_join_rsp_params *fils_join_rsp; +#endif + uint8_t frames[1]; +}; + +struct oem_channel_info { + uint32_t mhz; + uint32_t band_center_freq1; + uint32_t band_center_freq2; + uint32_t info; + uint32_t reg_info_1; + uint32_t reg_info_2; + uint8_t nss; + uint32_t rate_flags; + uint8_t sec_ch_offset; + enum phy_ch_width ch_width; +}; + +enum sir_sme_phy_mode { + SIR_SME_PHY_MODE_LEGACY = 0, + SIR_SME_PHY_MODE_HT = 1, + SIR_SME_PHY_MODE_VHT = 2 +}; + +/* / Definition for Association indication from peer */ +/* / MAC ---> */ +struct assoc_ind { + uint16_t messageType; /* eWNI_SME_ASSOC_IND */ + uint16_t length; + uint8_t sessionId; + tSirMacAddr peerMacAddr; + uint16_t aid; + tSirMacAddr bssId; /* Self BSSID */ + uint16_t staId; /* Station ID for peer */ + tAniAuthType authType; + enum ani_akm_type akm_type; + tAniSSID ssId; /* SSID used by STA to associate */ + tSirWAPIie wapiIE; /* WAPI IE received from peer */ + tSirRSNie rsnIE; /* RSN IE received from peer */ + /* Additional IE received from peer, which possibly include + * WSC IE and/or P2P IE + */ + tSirAddie addIE; + + /* powerCap & supportedChannels are present only when */ + /* spectrumMgtIndicator flag is set */ + bool spectrumMgtIndicator; + struct power_cap_info powerCap; + struct supported_channels supportedChannels; + bool wmmEnabledSta; /* if present - STA is WMM enabled */ + bool reassocReq; + /* Required for indicating the frames to upper layer */ + uint32_t beaconLength; + uint8_t *beaconPtr; + uint32_t assocReqLength; + uint8_t *assocReqPtr; + + /* Timing measurement capability */ + uint8_t timingMeasCap; + struct oem_channel_info chan_info; + bool ampdu; + bool sgi_enable; + bool tx_stbc; + bool rx_stbc; + tSirMacHTChannelWidth ch_width; + enum sir_sme_phy_mode mode; + uint8_t max_supp_idx; + uint8_t max_ext_idx; + uint8_t max_mcs_idx; + uint8_t rx_mcs_map; + uint8_t tx_mcs_map; + /* Extended CSA capability of station */ + uint8_t ecsa_capable; + tDot11fIEHTCaps HTCaps; + tDot11fIEVHTCaps VHTCaps; + bool he_caps_present; + tSirMacCapabilityInfo capability_info; + bool is_sae_authenticated; + const uint8_t *owe_ie; + uint32_t owe_ie_len; + uint16_t owe_status; + bool need_assoc_rsp_tx_cb; +}; + +/** + * struct owe_assoc_ind - owe association indication + * @node : List entry element + * @assoc_ind: pointer to assoc ind + */ +struct owe_assoc_ind { + qdf_list_node_t node; + struct assoc_ind *assoc_ind; +}; + +/* / Definition for Association confirm */ +/* / ---> MAC */ +struct assoc_cnf { + uint16_t messageType; /* eWNI_SME_ASSOC_CNF */ + uint16_t length; + tSirResultCodes status_code; + struct qdf_mac_addr bssid; /* Self BSSID */ + struct qdf_mac_addr peer_macaddr; + uint16_t aid; + enum mac_status_code mac_status_code; + uint8_t *owe_ie; + uint32_t owe_ie_len; + bool need_assoc_rsp_tx_cb; +}; + +/* / Enum definition for Wireless medium status change codes */ +typedef enum eSirSmeStatusChangeCode { + eSIR_SME_DEAUTH_FROM_PEER, + eSIR_SME_DISASSOC_FROM_PEER, + eSIR_SME_LOST_LINK_WITH_PEER, + eSIR_SME_CHANNEL_SWITCH, + eSIR_SME_JOINED_NEW_BSS, + eSIR_SME_LEAVING_BSS, + eSIR_SME_IBSS_ACTIVE, + eSIR_SME_IBSS_INACTIVE, + eSIR_SME_RADAR_DETECTED, + eSIR_SME_AP_CAPS_CHANGED, +} tSirSmeStatusChangeCode; + +struct new_bss_info { + struct qdf_mac_addr bssId; + uint32_t freq; + uint8_t reserved; + tSirMacSSid ssId; +}; + +struct ap_new_caps { + uint16_t capabilityInfo; + struct qdf_mac_addr bssId; + tSirMacSSid ssId; +}; + +/** + * Table below indicates what information is passed for each of + * the Wireless Media status change notifications: + * + * Status Change code Status change info + * ---------------------------------------------------------------------- + * eSIR_SME_DEAUTH_FROM_PEER Reason code received in DEAUTH frame + * eSIR_SME_DISASSOC_FROM_PEER Reason code received in DISASSOC frame + * eSIR_SME_LOST_LINK_WITH_PEER None + * eSIR_SME_CHANNEL_SWITCH New channel number + * eSIR_SME_JOINED_NEW_BSS BSSID, SSID and channel number + * eSIR_SME_LEAVING_BSS None + * eSIR_SME_IBSS_ACTIVE Indicates that another STA joined + * IBSS apart from this STA that + * started IBSS + * eSIR_SME_IBSS_INACTIVE Indicates that only this STA is left + * in IBSS + * eSIR_SME_RADAR_DETECTED Indicates that radar is detected + * eSIR_SME_AP_CAPS_CHANGED Indicates that capabilities of the AP + * that STA is currently associated with + * have changed. + */ + +/* / Definition for Wireless medium status change notification */ +struct wm_status_change_ntf { + uint16_t messageType; /* eWNI_SME_WM_STATUS_CHANGE_NTF */ + uint16_t length; + uint8_t sessionId; /* Session ID */ + tSirSmeStatusChangeCode statusChangeCode; + struct qdf_mac_addr bssid; /* Self BSSID */ + union { + /* eSIR_SME_DEAUTH_FROM_PEER */ + uint16_t deAuthReasonCode; + /* eSIR_SME_DISASSOC_FROM_PEER */ + uint16_t disassocReasonCode; + /* none for eSIR_SME_LOST_LINK_WITH_PEER */ + /* eSIR_SME_CHANNEL_SWITCH */ + uint32_t new_freq; + /* eSIR_SME_JOINED_NEW_BSS */ + struct new_bss_info newBssInfo; + /* none for eSIR_SME_LEAVING_BSS */ + /* none for eSIR_SME_IBSS_ACTIVE */ + /* none for eSIR_SME_IBSS_INACTIVE */ + /* none for eSIR_SME_RADAR_DETECTED */ + /* eSIR_SME_AP_CAPS_CHANGED */ + struct ap_new_caps apNewCaps; + } statusChangeInfo; +}; + +/* Definition for Disassociation request */ +struct disassoc_req { + uint16_t messageType; /* eWNI_SME_DISASSOC_REQ */ + uint16_t length; + uint8_t sessionId; /* Session ID */ + struct qdf_mac_addr bssid; /* Peer BSSID */ + struct qdf_mac_addr peer_macaddr; + uint16_t reasonCode; + /* This flag tells LIM whether to send the disassoc OTA or not */ + /* This will be set in while handing off from one AP to other */ + uint8_t doNotSendOverTheAir; + bool process_ho_fail; +}; + +/* / Definition for Disassociation response */ +struct disassoc_rsp { + uint16_t messageType; /* eWNI_SME_DISASSOC_RSP */ + uint16_t length; + uint8_t sessionId; /* Session ID */ + tSirResultCodes status_code; + struct qdf_mac_addr peer_macaddr; + uint16_t staId; +}; + +/* / Definition for Disassociation indication from peer */ +struct disassoc_ind { + uint16_t messageType; /* eWNI_SME_DISASSOC_IND */ + uint16_t length; + uint8_t vdev_id; + tSirResultCodes status_code; + struct qdf_mac_addr bssid; + struct qdf_mac_addr peer_macaddr; + uint16_t staId; + uint32_t reasonCode; + bool from_ap; +}; + +/* / Definition for Disassociation confirm */ +/* / MAC ---> */ +struct disassoc_cnf { + uint16_t messageType; /* eWNI_SME_DISASSOC_CNF */ + uint16_t length; + uint8_t vdev_id; + tSirResultCodes status_code; + struct qdf_mac_addr bssid; + struct qdf_mac_addr peer_macaddr; +}; + +/** + * struct sir_sme_discon_done_ind - disconnect done indiaction + * @message_type: msg type + * @length: length of msg + * @session_id: session id of the indication + * @reason_code: reason for disconnect indication + * @peer_mac: peer mac + */ +struct sir_sme_discon_done_ind { + uint16_t message_type; + uint16_t length; + uint8_t session_id; + tSirResultCodes reason_code; + tSirMacAddr peer_mac; +}; + +/* / Definition for Deauthetication request */ +struct deauth_req { + uint16_t messageType; /* eWNI_SME_DEAUTH_REQ */ + uint16_t length; + uint8_t vdev_id; /* Session ID */ + struct qdf_mac_addr bssid; /* AP BSSID */ + struct qdf_mac_addr peer_macaddr; + uint16_t reasonCode; +}; + +/** + * struct deauth_retry_params - deauth retry params + * @peer_mac: peer mac + * @reason_code: reason for disconnect indication + * @retry_cnt: retry count + */ +struct deauth_retry_params { + struct qdf_mac_addr peer_macaddr; + uint16_t reason_code; + uint8_t retry_cnt; +}; + +/* / Definition for Deauthetication response */ +struct deauth_rsp { + uint16_t messageType; /* eWNI_SME_DEAUTH_RSP */ + uint16_t length; + uint8_t sessionId; /* Session ID */ + tSirResultCodes status_code; + struct qdf_mac_addr peer_macaddr; +}; + +/* / Definition for Deauthetication indication from peer */ +struct deauth_ind { + uint16_t messageType; /* eWNI_SME_DEAUTH_IND */ + uint16_t length; + uint8_t vdev_id; + tSirResultCodes status_code; + struct qdf_mac_addr bssid; /* AP BSSID */ + struct qdf_mac_addr peer_macaddr; + + uint16_t staId; + uint32_t reasonCode; + int8_t rssi; + bool from_ap; +}; + +/* / Definition for Deauthetication confirm */ +struct deauth_cnf { + uint16_t messageType; /* eWNI_SME_DEAUTH_CNF */ + uint16_t length; + uint8_t vdev_id; + tSirResultCodes status_code; + struct qdf_mac_addr bssid; + struct qdf_mac_addr peer_macaddr; +}; + +/* / Definition for stop BSS request message */ +struct stop_bss_req { + uint16_t messageType; /* eWNI_SME_STOP_BSS_REQ */ + uint16_t length; + uint8_t sessionId; /* Session ID */ + tSirResultCodes reasonCode; + struct qdf_mac_addr bssid; /* Self BSSID */ +}; + +/* / Definition for Channel Switch indication for station */ +/* / MAC ---> */ +struct switch_channel_ind { + uint16_t messageType; /* eWNI_SME_SWITCH_CHL_IND */ + uint16_t length; + uint8_t sessionId; + uint32_t freq; + struct ch_params chan_params; + struct qdf_mac_addr bssid; /* BSSID */ + QDF_STATUS status; +}; + +/* / Definition for MIC failure indication */ +/* / MAC ---> */ +/* / MAC reports this each time a MIC failure occures on Rx TKIP packet */ +struct mic_failure_ind { + uint16_t messageType; /* eWNI_SME_MIC_FAILURE_IND */ + uint16_t length; + uint8_t sessionId; + struct qdf_mac_addr bssId; + tSirMicFailureInfo info; +}; + +struct missed_beacon_ind { + uint16_t messageType; /* eWNI_SME_MISSED_BEACON_IND */ + uint16_t length; + uint8_t bss_idx; +}; + +/* / Definition for Set Context request */ +/* / ---> MAC */ +struct set_context_req { + uint16_t messageType; /* eWNI_SME_SET_CONTEXT_REQ */ + uint16_t length; + uint8_t vdev_id; /* vdev ID */ + struct qdf_mac_addr peer_macaddr; + struct qdf_mac_addr bssid; /* BSSID */ + tSirKeyMaterial keyMaterial; +}; + +/* / Definition for Set Context response */ +/* / MAC ---> */ +struct set_context_rsp { + uint16_t messageType; /* eWNI_SME_SET_CONTEXT_RSP */ + uint16_t length; + uint8_t sessionId; /* Session ID */ + tSirResultCodes status_code; + struct qdf_mac_addr peer_macaddr; +}; + +typedef struct sAniGetSnrReq { + /* Common for all types are requests */ + uint16_t msgType; /* message type is same as the request type */ + uint16_t msgLen; /* length of the entire request */ + uint8_t sessionId; + void *snrCallback; + void *pDevContext; /* device context */ + int8_t snr; +} tAniGetSnrReq, *tpAniGetSnrReq; + +/* generic country code change request MSG structure */ +typedef struct sAniGenericChangeCountryCodeReq { + uint16_t msgType; /* message type is same as the request type */ + uint16_t msgLen; /* length of the entire request */ + uint8_t countryCode[CFG_COUNTRY_CODE_LEN]; /* 3 char country code */ +} tAniGenericChangeCountryCodeReq, *tpAniGenericChangeCountryCodeReq; + +/** + * struct sAniDHCPStopInd - DHCP Stop indication message + * @msgType: message type is same as the request type + * @msgLen: length of the entire request + * @device_mode: Mode of the device(ex:STA, AP) + * @adapterMacAddr: MAC address of the adapter + * @peerMacAddr: MAC address of the connected peer + */ +typedef struct sAniDHCPStopInd { + uint16_t msgType; + uint16_t msgLen; + uint8_t device_mode; + struct qdf_mac_addr adapterMacAddr; + struct qdf_mac_addr peerMacAddr; +} tAniDHCPInd, *tpAniDHCPInd; + +typedef struct sAniTXFailMonitorInd { + uint16_t msgType; /* message type is same as the request type */ + uint16_t msgLen; /* length of the entire request */ + uint8_t tx_fail_count; + void *txFailIndCallback; +} tAniTXFailMonitorInd, *tpAniTXFailMonitorInd; + + +/**********************PE Statistics end*************************/ + +typedef struct sSirP2PNoaAttr { +#ifdef ANI_BIG_BYTE_ENDIAN + uint32_t index:8; + uint32_t oppPsFlag:1; + uint32_t ctWin:7; + uint32_t rsvd1:16; +#else + uint32_t rsvd1:16; + uint32_t ctWin:7; + uint32_t oppPsFlag:1; + uint32_t index:8; +#endif + +#ifdef ANI_BIG_BYTE_ENDIAN + uint32_t uNoa1IntervalCnt:8; + uint32_t rsvd2:24; +#else + uint32_t rsvd2:24; + uint32_t uNoa1IntervalCnt:8; +#endif + uint32_t uNoa1Duration; + uint32_t uNoa1Interval; + uint32_t uNoa1StartTime; + +#ifdef ANI_BIG_BYTE_ENDIAN + uint32_t uNoa2IntervalCnt:8; + uint32_t rsvd3:24; +#else + uint32_t rsvd3:24; + uint32_t uNoa2IntervalCnt:8; +#endif + uint32_t uNoa2Duration; + uint32_t uNoa2Interval; + uint32_t uNoa2StartTime; +} tSirP2PNoaAttr, *tpSirP2PNoaAttr; + +typedef struct sSirTclasInfo { + tSirMacTclasIE tclas; + uint8_t version; /* applies only for classifier type ip */ + union { + tSirMacTclasParamEthernet eth; + tSirMacTclasParamIPv4 ipv4; + tSirMacTclasParamIPv6 ipv6; + tSirMacTclasParam8021dq t8021dq; + } qdf_packed tclasParams; +} qdf_packed tSirTclasInfo; + +typedef struct sSirAddtsReqInfo { + uint8_t dialogToken; + struct mac_tspec_ie tspec; + + uint8_t numTclas; /* number of Tclas elements */ + tSirTclasInfo tclasInfo[SIR_MAC_TCLASIE_MAXNUM]; + uint8_t tclasProc; +#if defined(FEATURE_WLAN_ESE) + struct ese_tsrs_ie tsrsIE; + uint8_t tsrsPresent:1; +#endif + uint8_t wmeTspecPresent:1; + uint8_t wsmTspecPresent:1; + uint8_t lleTspecPresent:1; + uint8_t tclasProcPresent:1; +} tSirAddtsReqInfo, *tpSirAddtsReqInfo; + +typedef struct sSirAddtsRspInfo { + uint8_t dialogToken; + enum mac_status_code status; + tSirMacTsDelayIE delay; + + struct mac_tspec_ie tspec; + uint8_t numTclas; /* number of Tclas elements */ + tSirTclasInfo tclasInfo[SIR_MAC_TCLASIE_MAXNUM]; + uint8_t tclasProc; + tSirMacScheduleIE schedule; +#ifdef FEATURE_WLAN_ESE + struct ese_tsm_ie tsmIE; + uint8_t tsmPresent:1; +#endif + uint8_t wmeTspecPresent:1; + uint8_t wsmTspecPresent:1; + uint8_t lleTspecPresent:1; + uint8_t tclasProcPresent:1; + uint8_t schedulePresent:1; +} tSirAddtsRspInfo, *tpSirAddtsRspInfo; + +/* / Add a tspec as defined */ +typedef struct sSirAddtsReq { + uint16_t messageType; /* eWNI_SME_ADDTS_REQ */ + uint16_t length; + uint8_t sessionId; /* Session ID */ + struct qdf_mac_addr bssid; /* BSSID */ + uint32_t timeout; /* in ms */ + uint8_t rspReqd; + tSirAddtsReqInfo req; +} tSirAddtsReq, *tpSirAddtsReq; + +typedef struct sSirAddtsRsp { + uint16_t messageType; /* eWNI_SME_ADDTS_RSP */ + uint16_t length; + uint8_t sessionId; /* sme sessionId Added for BT-AMP support */ + uint32_t rc; /* return code */ + tSirAddtsRspInfo rsp; +} tSirAddtsRsp, *tpSirAddtsRsp; + +typedef struct sSirDeltsReq { + uint16_t messageType; /* eWNI_SME_DELTS_REQ */ + uint16_t length; + uint8_t sessionId; /* Session ID */ + struct qdf_mac_addr bssid; /* BSSID */ + uint16_t aid; /* use 0 if macAddr is being specified */ + struct qdf_mac_addr macaddr; /* only on AP to specify the STA */ + uint8_t rspReqd; + struct delts_req_info req; +} tSirDeltsReq, *tpSirDeltsReq; + +typedef struct sSirDeltsRsp { + uint16_t messageType; /* eWNI_SME_DELTS_RSP */ + uint16_t length; + uint8_t sessionId; + uint32_t rc; + uint16_t aid; /* use 0 if macAddr is being specified */ + struct qdf_mac_addr macaddr; /* only on AP to specify the STA */ + struct delts_req_info rsp; +} tSirDeltsRsp, *tpSirDeltsRsp; + +typedef struct sSirAggrQosReqInfo { + uint16_t tspecIdx; + tSirAddtsReqInfo aggrAddTsInfo[QCA_WLAN_AC_ALL]; +} tSirAggrQosReqInfo, *tpSirAggrQosReqInfo; + +typedef struct sSirAggrQosReq { + uint16_t messageType; /* eWNI_SME_ADDTS_REQ */ + uint16_t length; + uint8_t sessionId; /* Session ID */ + struct qdf_mac_addr bssid; /* BSSID */ + uint32_t timeout; /* in ms */ + uint8_t rspReqd; + tSirAggrQosReqInfo aggrInfo; +} tSirAggrQosReq; + +typedef struct sSirAggrQosRspInfo { + uint16_t tspecIdx; + tSirAddtsRspInfo aggrRsp[QCA_WLAN_AC_ALL]; +} tSirAggrQosRspInfo, *tpSirAggrQosRspInfo; + +typedef struct sSirAggrQosRsp { + uint16_t messageType; + uint16_t length; + uint8_t sessionId; + tSirAggrQosRspInfo aggrInfo; +} tSirAggrQosRsp, *tpSirAggrQosRsp; + + +struct qos_map_set { + uint8_t present; + uint8_t num_dscp_exceptions; + uint8_t dscp_exceptions[QOS_MAP_MAX_EX][2]; + uint8_t dscp_range[QOS_MAP_RANGE_NUM][2]; +}; + +typedef struct sSmeIbssPeerInd { + uint16_t mesgType; + uint16_t mesgLen; + uint8_t sessionId; + + struct qdf_mac_addr peer_addr; + + /* Beacon will be appended for new Peer indication. */ +} tSmeIbssPeerInd, *tpSmeIbssPeerInd; + +struct ibss_peer_inactivity_ind { + uint8_t bss_idx; + struct qdf_mac_addr peer_addr; +}; + +/** + * struct lim_channel_status + * @channelfreq: Channel freq + * @noise_floor: Noise Floor value + * @rx_clear_count: rx clear count + * @cycle_count: cycle count + * @chan_tx_pwr_range: channel tx power per range in 0.5dBm steps + * @chan_tx_pwr_throughput: channel tx power per throughput + * @rx_frame_count: rx frame count (cumulative) + * @bss_rx_cycle_count: BSS rx cycle count + * @rx_11b_mode_data_duration: b-mode data rx time (units are microseconds) + * @tx_frame_count: BSS tx cycle count + * @mac_clk_mhz: sample frequency + * @channel_id: channel index + * @cmd_flags: indicate which stat event is this status coming from + */ +struct lim_channel_status { + uint32_t channelfreq; + uint32_t noise_floor; + uint32_t rx_clear_count; + uint32_t cycle_count; + uint32_t chan_tx_pwr_range; + uint32_t chan_tx_pwr_throughput; + uint32_t rx_frame_count; + uint32_t bss_rx_cycle_count; + uint32_t rx_11b_mode_data_duration; + uint32_t tx_frame_count; + uint32_t mac_clk_mhz; + uint32_t channel_id; + uint32_t cmd_flags; +}; + +/** + * struct lim_scan_channel_status + * @total_channel: total number of be scanned channel + * @channel_status_list: channel status info store in this array + */ +struct lim_scan_channel_status { + uint8_t total_channel; + struct lim_channel_status + channel_status_list[SIR_MAX_SUPPORTED_CHANNEL_LIST]; +}; + +typedef struct sSmeMaxAssocInd { + uint16_t mesgType; /* eWNI_SME_MAX_ASSOC_EXCEEDED */ + uint16_t mesgLen; + uint8_t sessionId; + /* the new peer that got rejected max assoc limit reached */ + struct qdf_mac_addr peer_mac; +} tSmeMaxAssocInd, *tpSmeMaxAssocInd; + +#define SIR_MAX_NAME_SIZE 64 +#define SIR_MAX_TEXT_SIZE 32 + +typedef struct sSirName { + uint8_t num_name; + uint8_t name[SIR_MAX_NAME_SIZE]; +} tSirName; + +typedef struct sSirText { + uint8_t num_text; + uint8_t text[SIR_MAX_TEXT_SIZE]; +} tSirText; + +#define SIR_WPS_PROBRSP_VER_PRESENT 0x00000001 +#define SIR_WPS_PROBRSP_STATE_PRESENT 0x00000002 +#define SIR_WPS_PROBRSP_APSETUPLOCK_PRESENT 0x00000004 +#define SIR_WPS_PROBRSP_SELECTEDREGISTRA_PRESENT 0x00000008 +#define SIR_WPS_PROBRSP_DEVICEPASSWORDID_PRESENT 0x00000010 +#define SIR_WPS_PROBRSP_SELECTEDREGISTRACFGMETHOD_PRESENT 0x00000020 +#define SIR_WPS_PROBRSP_RESPONSETYPE_PRESENT 0x00000040 +#define SIR_WPS_PROBRSP_UUIDE_PRESENT 0x00000080 +#define SIR_WPS_PROBRSP_MANUFACTURE_PRESENT 0x00000100 +#define SIR_WPS_PROBRSP_MODELNAME_PRESENT 0x00000200 +#define SIR_WPS_PROBRSP_MODELNUMBER_PRESENT 0x00000400 +#define SIR_WPS_PROBRSP_SERIALNUMBER_PRESENT 0x00000800 +#define SIR_WPS_PROBRSP_PRIMARYDEVICETYPE_PRESENT 0x00001000 +#define SIR_WPS_PROBRSP_DEVICENAME_PRESENT 0x00002000 +#define SIR_WPS_PROBRSP_CONFIGMETHODS_PRESENT 0x00004000 +#define SIR_WPS_PROBRSP_RF_BANDS_PRESENT 0x00008000 + +typedef struct sSirWPSProbeRspIE { + uint32_t FieldPresent; + uint32_t Version; /* Version. 0x10 = version 1.0, 0x11 = etc. */ + uint32_t wpsState; /* 1 = unconfigured, 2 = configured. */ + bool APSetupLocked; /* Must be included if value is true */ + /* + * BOOL: indicates if the user has recently activated a Registrar to + * add an Enrollee. + */ + bool SelectedRegistra; + uint16_t DevicePasswordID; /* Device Password ID */ + /* Selected Registrar config method */ + uint16_t SelectedRegistraCfgMethod; + uint8_t ResponseType; /* Response type */ + uint8_t UUID_E[16]; /* Unique identifier of the AP. */ + tSirName Manufacture; + tSirText ModelName; + tSirText ModelNumber; + tSirText SerialNumber; + /* Device Category ID: 1Computer, 2Input Device, ... */ + uint32_t PrimaryDeviceCategory; + /* Vendor specific OUI for Device Sub Category */ + uint8_t PrimaryDeviceOUI[4]; + /* + Device Sub Category ID: 1-PC, 2-Server if Device Category ID + * is computer + */ + uint32_t DeviceSubCategory; + tSirText DeviceName; + uint16_t ConfigMethod; /* Configuaration method */ + uint8_t RFBand; /* RF bands available on the AP */ +} tSirWPSProbeRspIE; + +#define SIR_WPS_BEACON_VER_PRESENT 0x00000001 +#define SIR_WPS_BEACON_STATE_PRESENT 0x00000002 +#define SIR_WPS_BEACON_APSETUPLOCK_PRESENT 0x00000004 +#define SIR_WPS_BEACON_SELECTEDREGISTRA_PRESENT 0x00000008 +#define SIR_WPS_BEACON_DEVICEPASSWORDID_PRESENT 0x00000010 +#define SIR_WPS_BEACON_SELECTEDREGISTRACFGMETHOD_PRESENT 0x00000020 +#define SIR_WPS_BEACON_UUIDE_PRESENT 0x00000080 +#define SIR_WPS_BEACON_RF_BANDS_PRESENT 0x00000100 +#define SIR_WPS_UUID_LEN 16 + +typedef struct sSirWPSBeaconIE { + uint32_t FieldPresent; + uint32_t Version; /* Version. 0x10 = version 1.0, 0x11 = etc. */ + uint32_t wpsState; /* 1 = unconfigured, 2 = configured. */ + bool APSetupLocked; /* Must be included if value is true */ + /* + * BOOL: indicates if the user has recently activated a Registrar to + * add an Enrollee. + */ + bool SelectedRegistra; + uint16_t DevicePasswordID; /* Device Password ID */ + /* Selected Registrar config method */ + uint16_t SelectedRegistraCfgMethod; + uint8_t UUID_E[SIR_WPS_UUID_LEN]; /* Unique identifier of the AP. */ + uint8_t RFBand; /* RF bands available on the AP */ +} tSirWPSBeaconIE; + +#define SIR_WPS_ASSOCRSP_VER_PRESENT 0x00000001 +#define SIR_WPS_ASSOCRSP_RESPONSETYPE_PRESENT 0x00000002 + +typedef struct sSirWPSAssocRspIE { + uint32_t FieldPresent; + uint32_t Version; + uint8_t ResposeType; +} tSirWPSAssocRspIE; + +typedef struct sSirAPWPSIEs { + tSirWPSProbeRspIE SirWPSProbeRspIE; /*WPS Set Probe Respose IE */ + tSirWPSBeaconIE SirWPSBeaconIE; /*WPS Set Beacon IE */ + tSirWPSAssocRspIE SirWPSAssocRspIE; /*WPS Set Assoc Response IE */ +} tSirAPWPSIEs, *tpSiriAPWPSIEs; + +struct update_config { + uint16_t messageType; /* eWNI_SME_UPDATE_CONFIG */ + uint16_t length; + uint8_t vdev_id; + uint16_t capab; + uint32_t value; +}; + +/* + * enum sir_update_session_param_type - session param type + * @SIR_PARAM_SSID_HIDDEN: ssidHidden parameter + * @SIR_PARAM_IGNORE_ASSOC_DISALLOWED: ignore_assoc_disallowed parameter + */ +enum sir_update_session_param_type { + SIR_PARAM_SSID_HIDDEN, + SIR_PARAM_IGNORE_ASSOC_DISALLOWED, +}; + +/* + * struct sir_update_session_param + * @message_type: SME message type + * @length: size of struct sir_update_session_param + * @vdev_id: vdev ID + * @param_type: parameter to be updated + * @param_val: Parameter value to update + */ +struct sir_update_session_param { + uint16_t message_type; + uint16_t length; + uint8_t vdev_id; + uint32_t param_type; + uint32_t param_val; +}; + +/** + * struct sir_set_he_bss_color + * @message_type: SME message type + * @length: size of struct sir_set_he_bss_color + * @vdev_id: vdev ID + * @bss_color: bss color value + */ +struct sir_set_he_bss_color { + uint16_t message_type; + uint16_t length; + uint8_t vdev_id; + uint8_t bss_color; +}; + +/** + * struct sir_create_session - Used for creating session in monitor mode + * @type: SME host message type. + * @msg_len: Length of the message. + * @bss_id: bss_id for creating the session. + */ +struct sir_create_session { + uint16_t type; + uint16_t msg_len; + uint8_t vdev_id; + struct qdf_mac_addr bss_id; +}; + +/** + * struct sir_delete_session - Used for deleting session in monitor mode + * @type: SME host message type. + * @msg_len: Length of the message. + * @vdev_id: vdev id. + */ +struct sir_delete_session { + uint16_t type; + uint16_t msg_len; + uint8_t vdev_id; +}; + +/* Beacon Interval */ +struct change_bi_params { + uint16_t messageType; + uint16_t length; + uint16_t beaconInterval; /* Beacon Interval */ + struct qdf_mac_addr bssid; + uint8_t sessionId; /* Session ID */ +}; + +#ifdef QCA_HT_2040_COEX +struct set_ht2040_mode { + uint16_t messageType; + uint16_t length; + uint8_t cbMode; + bool obssEnabled; + struct qdf_mac_addr bssid; + uint8_t sessionId; /* Session ID */ +}; +#endif + +#define SIR_WPS_PBC_WALK_TIME 120 /* 120 Second */ + +typedef struct sSirWPSPBCSession { + struct sSirWPSPBCSession *next; + struct qdf_mac_addr addr; + uint8_t uuid_e[SIR_WPS_UUID_LEN]; + uint32_t timestamp; +} tSirWPSPBCSession; + +typedef struct sSirWPSPBCProbeReq { + struct qdf_mac_addr peer_macaddr; + uint16_t probeReqIELen; + uint8_t probeReqIE[512]; +} tSirWPSPBCProbeReq, *tpSirWPSPBCProbeReq; + +/* probereq from peer, when wsc is enabled */ +typedef struct sSirSmeProbeReqInd { + uint16_t messageType; /* eWNI_SME_WPS_PBC_PROBE_REQ_IND */ + uint16_t length; + uint8_t sessionId; + struct qdf_mac_addr bssid; + tSirWPSPBCProbeReq WPSPBCProbeReq; +} tSirSmeProbeReqInd, *tpSirSmeProbeReqInd; + +#define SIR_ROAM_MAX_CHANNELS 80 +#define SIR_ROAM_SCAN_MAX_PB_REQ_SIZE 450 +/* Occupied channel list remains static */ +#define CHANNEL_LIST_STATIC 1 +/* Occupied channel list can be dynamic */ +#define CHANNEL_LIST_DYNAMIC 2 + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +#define SIR_ROAM_SCAN_PSK_SIZE 48 +#define SIR_ROAM_R0KH_ID_MAX_LEN 48 +#endif +/* SME -> HAL - This is the host offload request. */ +#define SIR_IPV6_NS_OFFLOAD 2 +#define SIR_OFFLOAD_DISABLE 0 +#define SIR_OFFLOAD_ENABLE 1 + +struct sir_host_offload_req { + uint8_t offloadType; + uint8_t enableOrDisable; + uint32_t num_ns_offload_count; + union { + uint8_t hostIpv4Addr[QDF_IPV4_ADDR_SIZE]; + uint8_t hostIpv6Addr[QDF_IPV6_ADDR_SIZE]; + } params; + struct qdf_mac_addr bssid; +}; + +/* Packet Types. */ +#define SIR_KEEP_ALIVE_NULL_PKT 1 +#define SIR_KEEP_ALIVE_UNSOLICIT_ARP_RSP 2 + +/* Keep Alive request. */ +struct keep_alive_req { + uint8_t packetType; + uint32_t timePeriod; + tSirIpv4Addr hostIpv4Addr; + tSirIpv4Addr destIpv4Addr; + struct qdf_mac_addr dest_macaddr; + struct qdf_mac_addr bssid; + uint8_t sessionId; +}; + +/** + * enum rxmgmt_flags - flags for received management frame. + * @RXMGMT_FLAG_NONE: Default value to indicate no flags are set. + * @RXMGMT_FLAG_EXTERNAL_AUTH: frame can be used for external authentication + * by upper layers. + */ +enum rxmgmt_flags { + RXMGMT_FLAG_NONE, + RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1, +}; + +typedef struct sSirSmeMgmtFrameInd { + uint16_t frame_len; + uint32_t rx_freq; + uint8_t sessionId; + uint8_t frameType; + int8_t rxRssi; + enum rxmgmt_flags rx_flags; + uint8_t frameBuf[1]; /* variable */ +} tSirSmeMgmtFrameInd, *tpSirSmeMgmtFrameInd; + +typedef void (*sir_mgmt_frame_ind_callback)(tSirSmeMgmtFrameInd *frame_ind); +/** + * struct sir_sme_mgmt_frame_cb_req - Register a + * management frame callback req + * + * @message_type: message id + * @length: msg length + * @callback: callback for management frame indication + */ +struct sir_sme_mgmt_frame_cb_req { + uint16_t message_type; + uint16_t length; + sir_mgmt_frame_ind_callback callback; +}; + +#ifdef WLAN_FEATURE_11W +typedef struct sSirSmeUnprotMgmtFrameInd { + uint8_t sessionId; + uint8_t frameType; + uint8_t frameLen; + uint8_t frameBuf[1]; /* variable */ +} tSirSmeUnprotMgmtFrameInd, *tpSirSmeUnprotMgmtFrameInd; +#endif + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + +typedef enum ext_wow_type { + EXT_WOW_TYPE_APP_TYPE1, /* wow type: only enable wakeup for app type1 */ + EXT_WOW_TYPE_APP_TYPE2, /* wow type: only enable wakeup for app type2 */ + EXT_WOW_TYPE_APP_TYPE1_2, /* wow type: enable wakeup for app type1&2 */ +} EXT_WOW_TYPE; + +typedef struct { + uint8_t vdev_id; + EXT_WOW_TYPE type; + uint32_t wakeup_pin_num; +} tSirExtWoWParams, *tpSirExtWoWParams; + +typedef struct { + uint8_t vdev_id; + struct qdf_mac_addr wakee_mac_addr; + uint8_t identification_id[8]; + uint8_t password[16]; + uint32_t id_length; + uint32_t pass_length; +} tSirAppType1Params, *tpSirAppType1Params; + +typedef struct { + uint8_t vdev_id; + + uint8_t rc4_key[16]; + uint32_t rc4_key_len; + + /** ip header parameter */ + uint32_t ip_id; /* NC id */ + uint32_t ip_device_ip; /* NC IP address */ + uint32_t ip_server_ip; /* Push server IP address */ + + /** tcp header parameter */ + uint16_t tcp_src_port; /* NC TCP port */ + uint16_t tcp_dst_port; /* Push server TCP port */ + uint32_t tcp_seq; + uint32_t tcp_ack_seq; + + uint32_t keepalive_init; /* Initial ping interval */ + uint32_t keepalive_min; /* Minimum ping interval */ + uint32_t keepalive_max; /* Maximum ping interval */ + uint32_t keepalive_inc; /* Increment of ping interval */ + + struct qdf_mac_addr gateway_mac; + uint32_t tcp_tx_timeout_val; + uint32_t tcp_rx_timeout_val; +} tSirAppType2Params, *tpSirAppType2Params; +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +typedef struct { + uint8_t acvo_uapsd:1; + uint8_t acvi_uapsd:1; + uint8_t acbk_uapsd:1; + uint8_t acbe_uapsd:1; + uint8_t reserved:4; +} tSirAcUapsd, *tpSirAcUapsd; +#endif + +typedef struct { + tSirMacSSid ssId; + uint8_t currAPbssid[QDF_MAC_ADDR_SIZE]; + uint32_t authentication; + uint8_t encryption; + uint8_t mcencryption; + tAniEdType gp_mgmt_cipher_suite; + uint8_t ChannelCount; + uint32_t chan_freq_cache[SIR_ROAM_MAX_CHANNELS]; +#ifdef WLAN_FEATURE_11W + bool mfp_enabled; +#endif +} tSirRoamNetworkType; + +typedef enum { + SIR_ROAMING_DFS_CHANNEL_DISABLED = 0, + SIR_ROAMING_DFS_CHANNEL_ENABLED_NORMAL = 1, + SIR_ROAMING_DFS_CHANNEL_ENABLED_ACTIVE = 2 +} eSirDFSRoamScanMode; +#define MAX_SSID_ALLOWED_LIST 4 +#define MAX_BSSID_AVOID_LIST 16 +#define MAX_BSSID_FAVORED 16 +/** + * struct roam_ext_params - Structure holding roaming parameters + * @num_bssid_avoid_list: The number of BSSID's that we should + * avoid connecting to. It is like a + * blacklist of BSSID's. + * @num_ssid_allowed_list: The number of SSID profiles that are + * in the Whitelist. When roaming, we + * consider the BSSID's with this SSID + * also for roaming apart from the connected one's + * @num_bssid_favored: Number of BSSID's which have a preference over + * others + * @ssid_allowed_list: Whitelist SSID's + * @bssid_avoid_list: Blacklist SSID's + * @bssid_favored: Favorable BSSID's + * @bssid_favored_factor: RSSI to be added to this BSSID to prefer it + * @raise_rssi_thresh_5g: The RSSI threshold below which the + * raise_factor_5g (boost factor) should be + * applied. + * @drop_rssi_thresh_5g: The RSSI threshold beyond which the + * drop_factor_5g (penalty factor) should be + * applied + * @raise_rssi_type_5g: Algorithm to apply the boost factor + * @raise_factor_5g: Boost factor + * @drop_rssi_type_5g: Algorithm to apply the penalty factor + * @drop_factor_5g: Penalty factor + * @max_raise_rssi_5g: Maximum amount of Boost that can added + * @max_drop_rssi_5g: Maximum amount of penalty that can be subtracted + * @good_rssi_threshold: The Lookup UP threshold beyond which roaming + * scan should be performed. + * @rssi_diff: RSSI difference for the AP to be better over the + * current AP to avoid ping pong effects + * @good_rssi_roam: Lazy Roam + * @rssi_reject_list: RSSI reject list (APs rejected by OCE, BTM) + * @bg_scan_bad_rssi_thresh: Bad RSSI threshold to perform bg scan. + * @bad_rssi_thresh_offset_2g: Offset from Bad RSSI threshold for 2G to 5G Roam + * @bg_scan_client_bitmap: Bitmap to identify the client scans to snoop. + * @roam_data_rssi_threshold_triggers: Bad data RSSI threshold to roam + * @roam_data_rssi_threshold: Bad data RSSI threshold to roam + * @rx_data_inactivity_time: rx duration to check data RSSI + * + * This structure holds all the key parameters related to + * initial connection and also roaming connections. + * */ +struct roam_ext_params { + uint8_t num_bssid_avoid_list; + uint8_t num_ssid_allowed_list; + uint8_t num_bssid_favored; + tSirMacSSid ssid_allowed_list[MAX_SSID_ALLOWED_LIST]; + struct qdf_mac_addr bssid_avoid_list[MAX_BSSID_AVOID_LIST]; + struct qdf_mac_addr bssid_favored[MAX_BSSID_FAVORED]; + uint8_t bssid_favored_factor[MAX_BSSID_FAVORED]; + int raise_rssi_thresh_5g; + int drop_rssi_thresh_5g; + uint8_t raise_rssi_type_5g; + uint8_t raise_factor_5g; + uint8_t drop_rssi_type_5g; + uint8_t drop_factor_5g; + int max_raise_rssi_5g; + int max_drop_rssi_5g; + int alert_rssi_threshold; + int rssi_diff; + int good_rssi_roam; + int dense_rssi_thresh_offset; + int dense_min_aps_cnt; + int initial_dense_status; + int traffic_threshold; + uint8_t num_rssi_rejection_ap; + struct reject_ap_config_params + rssi_reject_bssid_list[MAX_RSSI_AVOID_BSSID_LIST]; + int8_t bg_scan_bad_rssi_thresh; + uint8_t roam_bad_rssi_thresh_offset_2g; + uint32_t bg_scan_client_bitmap; + uint32_t roam_data_rssi_threshold_triggers; + int32_t roam_data_rssi_threshold; + uint32_t rx_data_inactivity_time; +}; + +/** + * struct pmkid_mode_bits - Bit flags for PMKID usage in RSN IE + * @fw_okc: Opportunistic key caching enable in firmware + * @fw_pmksa_cache: PMKSA caching enable in firmware, remember previously + * visited BSSID/PMK pairs + */ +struct pmkid_mode_bits { + uint32_t fw_okc:1; + uint32_t fw_pmksa_cache:1; + uint32_t unused:30; +}; + +/** + * struct lca_disallow_config_params - LCA[Last Connected AP] + * disallow config params + * @disallow_duration: LCA AP disallowed duration + * @rssi_channel_penalization: RSSI channel Penalization + * @num_disallowed_aps: Maximum number of AP's in LCA list + * + */ +struct lca_disallow_config_params { + uint32_t disallow_duration; + uint32_t rssi_channel_penalization; + uint32_t num_disallowed_aps; +}; + +/** + * struct mawc_params - Motion Aided Wireless Connectivity configuration + * @mawc_enabled: Global configuration for MAWC (Roaming/PNO/ExtScan) + * @mawc_roam_enabled: MAWC roaming enable/disable + * @mawc_roam_traffic_threshold: Traffic threshold in kBps for MAWC roaming + * @mawc_roam_ap_rssi_threshold: AP RSSI threshold for MAWC roaming + * @mawc_roam_rssi_high_adjust: High Adjustment value for suppressing scan + * @mawc_roam_rssi_low_adjust: Low Adjustment value for suppressing scan + */ +struct mawc_params { + bool mawc_enabled; + bool mawc_roam_enabled; + uint32_t mawc_roam_traffic_threshold; + int8_t mawc_roam_ap_rssi_threshold; + uint8_t mawc_roam_rssi_high_adjust; + uint8_t mawc_roam_rssi_low_adjust; +}; + +/** + * struct roam_init_params - Firmware roam module initialization parameters + * @vdev_id: vdev for which the roaming has to be enabled/disabled + * @enable: flag to init/deinit roam module + */ +struct roam_init_params { + uint8_t vdev_id; + uint8_t enable; +}; + +/** + * struct roam_sync_timeout_timer_info - Info related to roam sync timer + * @vdev_id: Vdev id for which host waiting roam sync ind from fw + */ +struct roam_sync_timeout_timer_info { + uint8_t vdev_id; +}; + +struct roam_offload_scan_req { + uint16_t message_type; + uint16_t length; + bool RoamScanOffloadEnabled; + struct mawc_params mawc_roam_params; + int8_t LookupThreshold; + int8_t rssi_thresh_offset_5g; + uint8_t delay_before_vdev_stop; + uint8_t OpportunisticScanThresholdDiff; + uint8_t RoamRescanRssiDiff; + uint8_t RoamRssiDiff; + struct rsn_caps rsn_caps; + int32_t rssi_abs_thresh; + uint8_t ChannelCacheType; + uint8_t Command; + uint8_t reason; + uint16_t NeighborScanTimerPeriod; + uint16_t neighbor_scan_min_timer_period; + uint16_t NeighborScanChannelMinTime; + uint16_t NeighborScanChannelMaxTime; + uint16_t EmptyRefreshScanPeriod; + bool IsESEAssoc; + bool is_11r_assoc; + uint8_t nProbes; + uint16_t HomeAwayTime; + tSirRoamNetworkType ConnectedNetwork; + struct mobility_domain_info mdid; + uint8_t sessionId; + uint8_t RoamBmissFirstBcnt; + uint8_t RoamBmissFinalBcnt; + uint8_t RoamBeaconRssiWeight; + eSirDFSRoamScanMode allowDFSChannelRoam; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + uint8_t roam_offload_enabled; + bool enable_self_bss_roam; + uint8_t PSK_PMK[SIR_ROAM_SCAN_PSK_SIZE]; + uint32_t pmk_len; + uint8_t Prefer5GHz; + uint8_t RoamRssiCatGap; + uint8_t Select5GHzMargin; + uint8_t KRK[SIR_KRK_KEY_LEN]; + uint8_t BTK[SIR_BTK_KEY_LEN]; + uint32_t ReassocFailureTimeout; + tSirAcUapsd AcUapsd; + uint8_t R0KH_ID[SIR_ROAM_R0KH_ID_MAX_LEN]; + uint32_t R0KH_ID_Length; + uint8_t RoamKeyMgmtOffloadEnabled; + struct pmkid_mode_bits pmkid_modes; + bool is_adaptive_11r_connection; + bool is_sae_single_pmk; + bool enable_ft_im_roaming; + /* Idle/Disconnect roam parameters */ + struct wmi_idle_roam_params idle_roam_params; + struct wmi_disconnect_roam_params disconnect_roam_params; +#endif + struct roam_ext_params roam_params; + struct roam_triggers roam_triggers; + uint8_t middle_of_roaming; + uint32_t hi_rssi_scan_max_count; + uint32_t hi_rssi_scan_rssi_delta; + uint32_t hi_rssi_scan_delay; + int32_t hi_rssi_scan_rssi_ub; + uint8_t early_stop_scan_enable; + int8_t early_stop_scan_min_threshold; + int8_t early_stop_scan_max_threshold; + enum scan_dwelltime_adaptive_mode roamscan_adaptive_dwell_mode; + tSirAddie assoc_ie; + struct lca_disallow_config_params lca_config_params; + struct scoring_param score_params; +#ifdef WLAN_FEATURE_FILS_SK + bool is_fils_connection; + struct roam_fils_params roam_fils_params; +#endif + uint32_t btm_offload_config; + uint32_t btm_solicited_timeout; + uint32_t btm_max_attempt_cnt; + uint32_t btm_sticky_time; + uint32_t rct_validity_timer; + uint32_t disassoc_timer_threshold; + uint32_t btm_trig_min_candidate_score; + struct wmi_11k_offload_params offload_11k_params; + uint32_t ho_delay_for_rx; + uint32_t roam_preauth_retry_count; + uint32_t roam_preauth_no_ack_timeout; + uint32_t min_delay_btw_roam_scans; + uint32_t roam_trigger_reason_bitmask; + bool roam_force_rssi_trigger; + /* bss load triggered roam related params */ + bool bss_load_trig_enabled; + struct wmi_bss_load_config bss_load_config; + bool roaming_scan_policy; + uint32_t roam_scan_inactivity_time; + uint32_t roam_inactive_data_packet_count; + uint32_t roam_scan_period_after_inactivity; + uint32_t btm_query_bitmask; + struct roam_trigger_min_rssi min_rssi_params[NUM_OF_ROAM_MIN_RSSI]; + struct roam_trigger_score_delta score_delta_param[NUM_OF_ROAM_TRIGGERS]; + uint32_t full_roam_scan_period; +}; + +struct roam_offload_scan_rsp { + uint8_t sessionId; + uint32_t reason; +}; + +/*--------------------------------------------------------------------------- + Packet Filtering Parameters + ---------------------------------------------------------------------------*/ +#define SIR_MAX_FILTER_TEST_DATA_LEN 8 +#define SIR_MAX_FILTER_TEST_DATA_OFFSET 200 +#define SIR_MAX_NUM_MULTICAST_ADDRESS 240 + +/* */ +/* Multicast Address List Parameters */ +/* */ +typedef struct sSirRcvFltMcAddrList { + uint32_t ulMulticastAddrCnt; + struct qdf_mac_addr multicastAddr[SIR_MAX_NUM_MULTICAST_ADDRESS]; + struct qdf_mac_addr self_macaddr; + struct qdf_mac_addr bssid; + uint8_t action; +} tSirRcvFltMcAddrList, *tpSirRcvFltMcAddrList; + +/** + * struct sir_wifi_start_log - Structure to store the params sent to start/ + * stop logging + * @name: Attribute which indicates the type of logging like per packet + * statistics, connectivity etc. + * @verbose_level: Verbose level which can be 0,1,2,3 + * @is_iwpriv_command: Set 1 for iwpriv command + * @ini_triggered: triggered using ini + * @user_triggered: triggered by user + * @size: pktlog buffer size + * @is_pktlog_buff_clear: clear the pktlog buffer + */ +struct sir_wifi_start_log { + uint32_t ring_id; + uint32_t verbose_level; + uint32_t is_iwpriv_command; + bool ini_triggered; + uint8_t user_triggered; + int size; + bool is_pktlog_buff_clear; +}; + + +/** + * struct sir_pcl_list - Format of PCL + * @pcl_list: List of preferred channels + * @weight_list: Weights of the PCL + * @pcl_len: Number of channels in the PCL + */ +struct sir_pcl_list { + uint32_t pcl_len; + uint8_t pcl_list[NUM_CHANNELS]; + uint8_t weight_list[NUM_CHANNELS]; +}; + +/** + * struct sir_pcl_chan_weights - Params to get the valid weighed list + * @pcl_list: Preferred channel list already sorted in the order of preference + * @pcl_len: Length of the PCL + * @saved_chan_list: Valid channel list updated as part of + * WMA_UPDATE_CHAN_LIST_REQ + * @saved_num_chan: Length of the valid channel list + * @weighed_valid_list: Weights of the valid channel list. This will have one + * to one mapping with valid_chan_list. FW expects channel order and size to be + * as per the list provided in WMI_SCAN_CHAN_LIST_CMDID. + * @weight_list: Weights assigned by policy manager + */ +struct sir_pcl_chan_weights { + uint8_t pcl_list[NUM_CHANNELS]; + uint32_t pcl_len; + uint8_t saved_chan_list[NUM_CHANNELS]; + uint32_t saved_num_chan; + uint8_t weighed_valid_list[NUM_CHANNELS]; + uint8_t weight_list[NUM_CHANNELS]; +}; + +/** + * struct sir_hw_mode_params - HW mode params + * @mac0_tx_ss: MAC0 Tx spatial stream + * @mac0_rx_ss: MAC0 Rx spatial stream + * @mac1_tx_ss: MAC1 Tx spatial stream + * @mac1_rx_ss: MAC1 Rx spatial stream + * @mac0_bw: MAC0 bandwidth + * @mac1_bw: MAC1 bandwidth + * @dbs_cap: DBS capabality + * @agile_dfs_cap: Agile DFS capabality + */ +struct sir_hw_mode_params { + uint8_t mac0_tx_ss; + uint8_t mac0_rx_ss; + uint8_t mac1_tx_ss; + uint8_t mac1_rx_ss; + uint8_t mac0_bw; + uint8_t mac1_bw; + uint8_t dbs_cap; + uint8_t agile_dfs_cap; + uint8_t sbs_cap; +}; + +/** + * struct sir_set_hw_mode_resp - HW mode response + * @status: Status + * @cfgd_hw_mode_index: Configured HW mode index + * @num_vdev_mac_entries: Number of vdev-mac id entries + * @vdev_mac_map: vdev id-mac id map + */ +struct sir_set_hw_mode_resp { + uint32_t status; + uint32_t cfgd_hw_mode_index; + uint32_t num_vdev_mac_entries; + struct policy_mgr_vdev_mac_map vdev_mac_map[MAX_VDEV_SUPPORTED]; +}; + +/** + * struct sir_hw_mode_trans_ind - HW mode transition indication + * @old_hw_mode_index: Index of old HW mode + * @new_hw_mode_index: Index of new HW mode + * @num_vdev_mac_entries: Number of vdev-mac id entries + * @vdev_mac_map: vdev id-mac id map + */ +struct sir_hw_mode_trans_ind { + uint32_t old_hw_mode_index; + uint32_t new_hw_mode_index; + uint32_t num_vdev_mac_entries; + struct policy_mgr_vdev_mac_map vdev_mac_map[MAX_VDEV_SUPPORTED]; +}; + +/** + * struct sir_dual_mac_config_resp - Dual MAC config response + * @status: Status of setting the dual mac configuration + */ +struct sir_dual_mac_config_resp { + uint32_t status; +}; + +/** + * enum set_antenna_mode_status - Status of set antenna mode + * command + * @SET_ANTENNA_MODE_STATUS_OK: command successful + * @SET_ANTENNA_MODE_STATUS_EINVAL: invalid antenna mode + * @SET_ANTENNA_MODE_STATUS_ECANCELED: mode change cancelled + * @SET_ANTENNA_MODE_STATUS_ENOTSUP: mode not supported + */ +enum set_antenna_mode_status { + SET_ANTENNA_MODE_STATUS_OK, + SET_ANTENNA_MODE_STATUS_EINVAL, + SET_ANTENNA_MODE_STATUS_ECANCELED, + SET_ANTENNA_MODE_STATUS_ENOTSUP, +}; + +/** + * struct sir_antenna_mode_resp - set antenna mode response + * @status: Status of setting the antenna mode + */ +struct sir_antenna_mode_resp { + enum set_antenna_mode_status status; +}; + +/* Reset AP Caps Changed */ +typedef struct sSirResetAPCapsChange { + uint16_t messageType; + uint16_t length; + struct qdf_mac_addr bssId; +} tSirResetAPCapsChange, *tpSirResetAPCapsChange; + +/* / Definition for Candidate found indication from FW */ +typedef struct sSirSmeCandidateFoundInd { + uint16_t messageType; /* eWNI_SME_CANDIDATE_FOUND_IND */ + uint16_t length; + uint8_t sessionId; /* Session Identifier */ +} tSirSmeCandidateFoundInd, *tpSirSmeCandidateFoundInd; + +#ifdef WLAN_FEATURE_11W +typedef struct sSirWlanExcludeUnencryptParam { + bool excludeUnencrypt; + struct qdf_mac_addr bssid; +} tSirWlanExcludeUnencryptParam, *tpSirWlanExcludeUnencryptParam; +#endif + +typedef enum { + P2P_SCAN_TYPE_SEARCH = 1, /* P2P Search */ + P2P_SCAN_TYPE_LISTEN /* P2P Listen */ +} tSirP2pScanType; + +typedef struct sAniHandoffReq { + /* Common for all types are requests */ + uint16_t msgType; /* message type is same as the request type */ + uint16_t msgLen; /* length of the entire request */ + uint8_t sessionId; + uint8_t bssid[QDF_MAC_ADDR_SIZE]; + uint32_t ch_freq; + uint8_t handoff_src; +} tAniHandoffReq, *tpAniHandoffReq; + +/** + * sir_scan_event_type - scan event types used in LIM + * @SIR_SCAN_EVENT_STARTED - scan command accepted by FW + * @SIR_SCAN_EVENT_COMPLETED - scan has been completed by FW + * @SIR_SCAN_EVENT_BSS_CHANNEL - FW is going to move to HOME channel + * @SIR_SCAN_EVENT_FOREIGN_CHANNEL - FW is going to move to FORIEGN channel + * @SIR_SCAN_EVENT_DEQUEUED - scan request got dequeued + * @SIR_SCAN_EVENT_PREEMPTED - preempted by other high priority scan + * @SIR_SCAN_EVENT_START_FAILED - scan start failed + * @SIR_SCAN_EVENT_RESTARTED - scan restarted + * @SIR_SCAN_EVENT_MAX - max value for event type +*/ +enum sir_scan_event_type { + SIR_SCAN_EVENT_STARTED = 0x1, + SIR_SCAN_EVENT_COMPLETED = 0x2, + SIR_SCAN_EVENT_BSS_CHANNEL = 0x4, + SIR_SCAN_EVENT_FOREIGN_CHANNEL = 0x8, + SIR_SCAN_EVENT_DEQUEUED = 0x10, + SIR_SCAN_EVENT_PREEMPTED = 0x20, + SIR_SCAN_EVENT_START_FAILED = 0x40, + SIR_SCAN_EVENT_RESTARTED = 0x80, + SIR_SCAN_EVENT_MAX = 0x8000 +}; + +typedef struct sSirScanOffloadEvent { + enum sir_scan_event_type event; + tSirResultCodes reasonCode; + uint32_t chanFreq; + uint32_t requestor; + uint32_t scanId; + tSirP2pScanType p2pScanType; + uint8_t sessionId; +} tSirScanOffloadEvent, *tpSirScanOffloadEvent; + +/** + * struct sSirUpdateChanParam - channel parameters + * @freq: Frequency of the channel + * @pwr: power level + * @dfsSet: is dfs supported or not + * @half_rate: is the channel operating at 10MHz + * @quarter_rate: is the channel operating at 5MHz + * @nan_disabled: is NAN disabled on @freq + */ +typedef struct sSirUpdateChanParam { + uint32_t freq; + uint8_t pwr; + bool dfsSet; + bool half_rate; + bool quarter_rate; + bool nan_disabled; +} tSirUpdateChanParam, *tpSirUpdateChanParam; + +typedef struct sSirUpdateChan { + uint8_t numChan; + uint8_t ht_en; + uint8_t vht_en; + uint8_t vht_24_en; + bool he_en; + tSirUpdateChanParam chanParam[1]; +} tSirUpdateChanList, *tpSirUpdateChanList; + +typedef enum eSirAddonPsReq { + eSIR_ADDON_NOTHING, + eSIR_ADDON_ENABLE_UAPSD, + eSIR_ADDON_DISABLE_UAPSD +} tSirAddonPsReq; + +#ifdef FEATURE_WLAN_CH_AVOID +typedef struct sSirChAvoidUpdateReq { + uint32_t reserved_param; +} tSirChAvoidUpdateReq; +#endif /* FEATURE_WLAN_CH_AVOID */ + +struct link_speed_info { + /* MAC Address for the peer */ + struct qdf_mac_addr peer_macaddr; + uint32_t estLinkSpeed; /* Linkspeed from firmware */ +}; + +/** + * struct sir_peer_info_ext_req - peer info request struct + * @peer_macaddr: MAC address + * @sessionid: vdev id + * @reset_after_request: fw reset statistics after query + * + * peer info request message's struct + */ +struct sir_peer_info_ext_req { + struct qdf_mac_addr peer_macaddr; + uint8_t sessionid; + uint8_t reset_after_request; +}; + +/** + * struct sir_peer_info_ext - peer info information struct + * (refer to station_info struct in Kernel) + * @peer_macaddr: MAC address + * @tx_packets: packets transmitted to this station + * @tx_bytes: bytes transmitted to this station + * @rx_packets: packets received from this station + * @rx_bytes: bytes received from this station + * @tx_retries: cumulative retry counts + * @tx_failed: the number of failed frames + * @tx_succeed: the number of succeed frames + * @rssi: the signal strength + * @tx_rate: last used tx bitrate (kbps) + * @tx_rate_code: last tx rate code (last_tx_rate_code of wmi_peer_stats_info) + * @rx_rate: last used rx bitrate (kbps) + * @rx_rate_code: last rx rate code (last_rx_rate_code of wmi_peer_stats_info) + * @peer_rssi_per_chain: the average value of RSSI (dbm) per chain + * + * a station's information + */ +struct sir_peer_info_ext { + struct qdf_mac_addr peer_macaddr; + uint32_t tx_packets; + uint64_t tx_bytes; + uint32_t rx_packets; + uint64_t rx_bytes; + uint32_t tx_retries; + uint32_t tx_failed; + uint32_t tx_succeed; + int32_t rssi; + uint32_t tx_rate; + uint32_t tx_rate_code; + uint32_t rx_rate; + uint32_t rx_rate_code; + int32_t peer_rssi_per_chain[WMI_MAX_CHAINS]; +}; + +/** + * struct sir_peer_info_ext_resp - all peers' information struct + * @count: peer's number + * @info: peer information + * + * all station's information + */ +struct sir_peer_info_ext_resp { + uint8_t count; + struct sir_peer_info_ext info[0]; +}; + +/** + * @sta_num: number of peer station which has valid info + * @info: peer extended information + * + * all SAP peer station's extended information retrieved + */ +struct sir_peer_sta_ext_info { + uint8_t sta_num; + struct sir_peer_info_ext info[MAX_PEER_STA]; +}; + +/** + * struct sir_isolation_resp - isolation info related structure + * @isolation_chain0: isolation value for chain 0 + * @isolation_chain1: isolation value for chain 1 + * @isolation_chain2: isolation value for chain 2 + * @isolation_chain3: isolation value for chain 3 + */ +struct sir_isolation_resp { + uint32_t isolation_chain0:8, + isolation_chain1:8, + isolation_chain2:8, + isolation_chain3:8; +}; + +typedef struct sSirAddPeriodicTxPtrn { + /* MAC Address for the adapter */ + struct qdf_mac_addr mac_address; + uint8_t ucPtrnId; /* Pattern ID */ + uint16_t ucPtrnSize; /* Pattern size */ + uint32_t usPtrnIntervalMs; /* In msec */ + uint8_t ucPattern[PERIODIC_TX_PTRN_MAX_SIZE]; /* Pattern buffer */ +} tSirAddPeriodicTxPtrn, *tpSirAddPeriodicTxPtrn; + +typedef struct sSirDelPeriodicTxPtrn { + /* MAC Address for the adapter */ + struct qdf_mac_addr mac_address; + uint8_t ucPtrnId; /* Pattern ID */ +} tSirDelPeriodicTxPtrn, *tpSirDelPeriodicTxPtrn; + +/*--------------------------------------------------------------------------- +* tSirIbssGetPeerInfoReqParams +*--------------------------------------------------------------------------*/ +typedef struct { + bool allPeerInfoReqd; /* If set, all IBSS peers stats are reported */ + struct qdf_mac_addr peer_mac; + /* of peer with staIdx is reported */ +} tSirIbssGetPeerInfoReqParams, *tpSirIbssGetPeerInfoReqParams; + +/** + * typedef struct - tSirIbssGetPeerInfoParams + * @mac_addr: mac address received from target + * @txRate: TX rate + * @mcsIndex: MCS index + * @rssi: RSSI + */ +typedef struct { + uint8_t mac_addr[QDF_MAC_ADDR_SIZE]; + uint32_t txRate; + uint32_t mcsIndex; + int8_t rssi; +} tSirIbssPeerInfoParams; + +typedef struct { + uint32_t status; + uint8_t numPeers; + tSirIbssPeerInfoParams peerInfoParams[32]; +} tSirPeerInfoRspParams, *tpSirIbssPeerInfoRspParams; + +/*--------------------------------------------------------------------------- +* tSirIbssGetPeerInfoRspParams +*--------------------------------------------------------------------------*/ +typedef struct { + uint16_t mesgType; + uint16_t mesgLen; + tSirPeerInfoRspParams ibssPeerInfoRspParams; +} tSirIbssGetPeerInfoRspParams, *tpSirIbssGetPeerInfoRspParams; + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +typedef struct { + uint16_t mesgType; + uint16_t mesgLen; + bool status; +} tSirReadyToExtWoWInd, *tpSirReadyToExtWoWInd; +#endif +typedef struct sSirRateUpdateInd { + uint8_t nss; /* 0: 1x1, 1: 2x2 */ + struct qdf_mac_addr bssid; + enum QDF_OPMODE dev_mode; + int32_t bcastDataRate; /* bcast rate unit Mbpsx10, -1:not used */ + + /* + * 0 implies MCAST RA, positive value implies fixed rate, + * -1 implies ignore this param + */ + int32_t reliableMcastDataRate; /* unit Mbpsx10 */ + + /* TX flag to differentiate between HT20, HT40 etc */ + enum tx_rate_info reliableMcastDataRateTxFlag; + + /* + * MCAST(or BCAST) fixed data rate in 2.4 GHz, unit Mbpsx10, + * 0 implies ignore + */ + uint32_t mcastDataRate24GHz; + + /* TX flag to differentiate between HT20, HT40 etc */ + enum tx_rate_info mcastDataRate24GHzTxFlag; + + /* + * MCAST(or BCAST) fixed data rate in 5 GHz, + * unit Mbpsx10, 0 implies ignore + */ + uint32_t mcastDataRate5GHz; + + /* TX flag to differentiate between HT20, HT40 etc */ + enum tx_rate_info mcastDataRate5GHzTxFlag; + +} tSirRateUpdateInd, *tpSirRateUpdateInd; + +#define SIR_DFS_MAX_20M_SUB_CH 8 +#define SIR_80MHZ_START_CENTER_CH_DIFF 6 + +typedef struct sSirSmeDfsChannelList { + uint32_t nchannels; + /* Ch num including bonded channels on which the RADAR is present */ + uint8_t channels[SIR_DFS_MAX_20M_SUB_CH]; +} tSirSmeDfsChannelList, *tpSirSmeDfsChannelList; + +typedef struct sSirSmeDfsEventInd { + uint32_t sessionId; + tSirSmeDfsChannelList chan_list; + uint32_t dfs_radar_status; + int use_nol; +} tSirSmeDfsEventInd, *tpSirSmeDfsEventInd; + +typedef struct sSirChanChangeRequest { + uint16_t messageType; + uint16_t messageLen; + uint32_t target_chan_freq; + uint8_t sec_ch_offset; + enum phy_ch_width ch_width; + uint8_t center_freq_seg_0; + uint8_t center_freq_seg_1; + uint8_t bssid[QDF_MAC_ADDR_SIZE]; + uint32_t dot11mode; + tSirNwType nw_type; + tSirMacRateSet operational_rateset; + tSirMacRateSet extended_rateset; + uint32_t cac_duration_ms; + uint32_t dfs_regdomain; +} tSirChanChangeRequest, *tpSirChanChangeRequest; + +typedef struct sSirChanChangeResponse { + uint8_t sessionId; + uint32_t new_op_freq; + uint8_t channelChangeStatus; +} tSirChanChangeResponse, *tpSirChanChangeResponse; + +typedef struct sSirStartBeaconIndication { + uint16_t messageType; + uint16_t messageLen; + uint8_t beaconStartStatus; + uint8_t bssid[QDF_MAC_ADDR_SIZE]; +} tSirStartBeaconIndication, *tpSirStartBeaconIndication; + +/* additional IE type */ +typedef enum tUpdateIEsType { + eUPDATE_IE_NONE, + eUPDATE_IE_PROBE_BCN, + eUPDATE_IE_PROBE_RESP, + eUPDATE_IE_ASSOC_RESP, + + /* Add type above this line */ + /* this is used to reset all buffer */ + eUPDATE_IE_ALL, + eUPDATE_IE_MAX +} eUpdateIEsType; + +/* Modify particular IE in addition IE for prob resp Bcn */ +typedef struct sSirModifyIE { + struct qdf_mac_addr bssid; + uint16_t vdev_id; + bool notify; + uint8_t ieID; + uint8_t ieIDLen; /*ie length as per spec */ + uint16_t ieBufferlength; + uint8_t *pIEBuffer; + int32_t oui_length; + +} tSirModifyIE, *tpSirModifyIE; + +struct send_add_ba_req { + uint8_t mac_addr[QDF_MAC_ADDR_SIZE]; + struct addba_send_params param; +}; + +/* Message format for Update IE message sent to PE */ +typedef struct sSirModifyIEsInd { + uint16_t msgType; + uint16_t msgLen; + tSirModifyIE modifyIE; + eUpdateIEsType updateType; +} tSirModifyIEsInd, *tpSirModifyIEsInd; + +/* Message format for Update IE message sent to PE */ +typedef struct sSirUpdateIE { + struct qdf_mac_addr bssid; + uint16_t vdev_id; + bool append; + bool notify; + uint16_t ieBufferlength; + uint8_t *pAdditionIEBuffer; +} tSirUpdateIE, *tpSirUpdateIE; + +/* Message format for Update IE message sent to PE */ +typedef struct sSirUpdateIEsInd { + uint16_t msgType; + uint16_t msgLen; + tSirUpdateIE updateIE; + eUpdateIEsType updateType; +} tSirUpdateIEsInd, *tpSirUpdateIEsInd; + +/* Message format for requesting channel switch announcement to lower layers */ +typedef struct sSirDfsCsaIeRequest { + uint16_t msgType; + uint16_t msgLen; + uint32_t target_chan_freq; + uint8_t csaIeRequired; + uint8_t bssid[QDF_MAC_ADDR_SIZE]; + struct ch_params ch_params; + uint8_t ch_switch_beacon_cnt; + uint8_t ch_switch_mode; + uint8_t dfs_ch_switch_disable; +} tSirDfsCsaIeRequest, *tpSirDfsCsaIeRequest; + +/* Indication from lower layer indicating the completion of first beacon send + * after the beacon template update + */ +typedef struct sSirFirstBeaconTxCompleteInd { + uint16_t messageType; /* eWNI_SME_MISSED_BEACON_IND */ + uint16_t length; + uint8_t bss_idx; +} tSirFirstBeaconTxCompleteInd, *tpSirFirstBeaconTxCompleteInd; + +typedef struct sSirSmeCSAIeTxCompleteRsp { + uint8_t sessionId; + uint8_t chanSwIeTxStatus; +} tSirSmeCSAIeTxCompleteRsp, *tpSirSmeCSAIeTxCompleteRsp; + +/* Thermal Mitigation*/ + +typedef struct { + uint16_t minTempThreshold; + uint16_t maxTempThreshold; +} t_thermal_level_info, *tp_thermal_level_info; + +typedef enum { + WLAN_WMA_THERMAL_LEVEL_0, + WLAN_WMA_THERMAL_LEVEL_1, + WLAN_WMA_THERMAL_LEVEL_2, + WLAN_WMA_THERMAL_LEVEL_3, + WLAN_WMA_MAX_THERMAL_LEVELS +} t_thermal_level; + +#define WLAN_THROTTLE_DUTY_CYCLE_LEVEL_MAX (4) + +typedef struct { + /* Array of thermal levels */ + t_thermal_level_info thermalLevels[WLAN_WMA_MAX_THERMAL_LEVELS]; + uint8_t thermalCurrLevel; + uint8_t thermalMgmtEnabled; + uint32_t throttlePeriod; + uint8_t throttle_duty_cycle_tbl[WLAN_THROTTLE_DUTY_CYCLE_LEVEL_MAX]; +} t_thermal_mgmt, *tp_thermal_mgmt; + +struct tx_power_limit { + /* Thermal limits for 2g and 5g */ + uint32_t txPower2g; + uint32_t txPower5g; +}; + +enum bad_peer_thresh_levels { + WLAN_WMA_IEEE80211_B_LEVEL = 0, + WLAN_WMA_IEEE80211_AG_LEVEL, + WLAN_WMA_IEEE80211_N_LEVEL, + WLAN_WMA_IEEE80211_AC_LEVEL, + WLAN_WMA_IEEE80211_AX_LEVEL, + WLAN_WMA_IEEE80211_MAX_LEVEL, +}; + +#define NUM_OF_RATE_THRESH_MAX (4) +struct t_bad_peer_info { + uint32_t cond; + uint32_t delta; + uint32_t percentage; + uint32_t thresh[NUM_OF_RATE_THRESH_MAX]; + uint32_t txlimit; +}; + +struct t_bad_peer_txtcl_config { + /* Array of thermal levels */ + struct t_bad_peer_info threshold[WLAN_WMA_IEEE80211_MAX_LEVEL]; + uint32_t enable; + uint32_t period; + uint32_t txq_limit; + uint32_t tgt_backoff; + uint32_t tgt_report_prd; +}; + +/* notify MODEM power state to FW */ +typedef struct { + uint32_t param; +} tSirModemPowerStateInd, *tpSirModemPowerStateInd; + +#ifdef WLAN_FEATURE_STATS_EXT +typedef struct { + uint32_t vdev_id; + uint32_t event_data_len; + uint8_t event_data[]; +} tSirStatsExtEvent, *tpSirStatsExtEvent; +#endif + +struct roam_offload_synch_ind { + uint16_t messageType; /*eWNI_SME_ROAM_OFFLOAD_SYNCH_IND */ + uint16_t length; + uint16_t beaconProbeRespOffset; + uint16_t beaconProbeRespLength; + uint16_t reassocRespOffset; + uint16_t reassocRespLength; + uint16_t reassoc_req_offset; + uint16_t reassoc_req_length; + uint8_t isBeacon; + uint8_t roamed_vdev_id; + struct qdf_mac_addr bssid; + struct qdf_mac_addr self_mac; + int8_t txMgmtPower; + uint32_t authStatus; + uint8_t rssi; + uint8_t roamReason; + uint32_t chan_freq; + uint8_t kck[KCK_256BIT_KEY_LEN]; + uint8_t kck_len; + uint32_t kek_len; + uint8_t kek[SIR_KEK_KEY_LEN_FILS]; + uint32_t pmk_len; + uint8_t pmk[SIR_PMK_LEN]; + uint8_t pmkid[PMKID_LEN]; + bool update_erp_next_seq_num; + uint16_t next_erp_seq_num; + uint8_t replay_ctr[SIR_REPLAY_CTR_LEN]; + void *add_bss_params; + struct join_rsp *join_rsp; + uint16_t aid; + struct sir_hw_mode_trans_ind hw_mode_trans_ind; + uint8_t nss; + struct qdf_mac_addr dst_mac; + struct qdf_mac_addr src_mac; + uint16_t hlp_data_len; + uint8_t hlp_data[FILS_MAX_HLP_DATA_LEN]; + bool is_ft_im_roam; + enum wlan_phymode phy_mode; /*phy mode sent by fw */ +}; + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +struct handoff_failure_ind { + uint8_t vdev_id; + struct qdf_mac_addr bssid; +}; + +struct roam_offload_synch_fail { + uint8_t session_id; +}; + +#endif + +/** + * struct sir_wisa_params - WISA Mode Parameters + * @mode: WISA mode + * @session_id: Session ID of vdev + */ +struct sir_wisa_params { + bool mode; + uint8_t vdev_id; +}; + +/** + * typedef enum wifi_scan_flags - wifi scan flags + * @WIFI_SCAN_FLAG_INTERRUPTED: Indicates that scan results are not complete + * because probes were not sent on some channels + */ +typedef enum { + WIFI_SCAN_FLAG_INTERRUPTED = 1, +} wifi_scan_flags; + +typedef enum { + WIFI_BAND_UNSPECIFIED, + WIFI_BAND_BG = 1, /* 2.4 GHz */ + WIFI_BAND_A = 2, /* 5 GHz without DFS */ + WIFI_BAND_ABG = 3, /* 2.4 GHz + 5 GHz; no DFS */ + WIFI_BAND_A_DFS_ONLY = 4, /* 5 GHz DFS only */ + /* 5 is reserved */ + WIFI_BAND_A_WITH_DFS = 6, /* 5 GHz with DFS */ + WIFI_BAND_ABG_WITH_DFS = 7, /* 2.4 GHz + 5 GHz with DFS */ + + /* Keep it last */ + WIFI_BAND_MAX +} tWifiBand; + +#ifdef FEATURE_WLAN_EXTSCAN + +#define WLAN_EXTSCAN_MAX_CHANNELS 36 +#define WLAN_EXTSCAN_MAX_BUCKETS 16 +#define WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS 64 + +typedef enum { + eSIR_EXTSCAN_INVALID, + eSIR_EXTSCAN_START_RSP, + eSIR_EXTSCAN_STOP_RSP, + eSIR_EXTSCAN_CACHED_RESULTS_RSP, + eSIR_EXTSCAN_SET_BSSID_HOTLIST_RSP, + eSIR_EXTSCAN_RESET_BSSID_HOTLIST_RSP, + eSIR_EXTSCAN_SET_SIGNIFICANT_WIFI_CHANGE_RSP, + eSIR_EXTSCAN_RESET_SIGNIFICANT_WIFI_CHANGE_RSP, + + eSIR_EXTSCAN_GET_CAPABILITIES_IND, + eSIR_EXTSCAN_HOTLIST_MATCH_IND, + eSIR_EXTSCAN_SIGNIFICANT_WIFI_CHANGE_RESULTS_IND, + eSIR_EXTSCAN_CACHED_RESULTS_IND, + eSIR_EXTSCAN_SCAN_RES_AVAILABLE_IND, + eSIR_EXTSCAN_SCAN_PROGRESS_EVENT_IND, + eSIR_EXTSCAN_FULL_SCAN_RESULT_IND, + eSIR_EPNO_NETWORK_FOUND_IND, + eSIR_PASSPOINT_NETWORK_FOUND_IND, + eSIR_EXTSCAN_SET_SSID_HOTLIST_RSP, + eSIR_EXTSCAN_RESET_SSID_HOTLIST_RSP, + + /* Keep this last */ + eSIR_EXTSCAN_CALLBACK_TYPE_MAX, +} tSirExtScanCallbackType; + +/** + * enum wifi_extscan_event_type - extscan event type + * @WIFI_EXTSCAN_RESULTS_AVAILABLE: reported when REPORT_EVENTS_EACH_SCAN is set + * and a scan cycle completes. WIFI_SCAN_THRESHOLD_NUM_SCANS or + * WIFI_SCAN_THRESHOLD_PERCENT can be reported instead if the + * reason for the event is available; however, at most one of + * these events should be reported per scan. + * @WIFI_EXTSCAN_THRESHOLD_NUM_SCANS: can be reported when + * REPORT_EVENTS_EACH_SCAN is not set and + * report_threshold_num_scans is reached. + * @WIFI_EXTSCAN_THRESHOLD_PERCENT: can be reported when REPORT_EVENTS_EACH_SCAN + * is not set and report_threshold_percent is reached. + * @WIFI_SCAN_DISABLED: reported when currently executing gscans are disabled + * start_gscan will need to be called again in order to continue + * scanning. + * @WIFI_EXTSCAN_BUCKET_STARTED_EVENT: Bucket started event + * This event is consumed in driver only. + * @WIFI_EXTSCAN_CYCLE_STARTED_EVENT: Cycle started event. + * This event is consumed in driver only. + * @WIFI_EXTSCAN_CYCLE_COMPLETED_EVENT: Cycle complete event. This event + * triggers @WIFI_EXTSCAN_RESULTS_AVAILABLE to the user space. + */ +enum wifi_extscan_event_type { + WIFI_EXTSCAN_RESULTS_AVAILABLE, + WIFI_EXTSCAN_THRESHOLD_NUM_SCANS, + WIFI_EXTSCAN_THRESHOLD_PERCENT, + WIFI_SCAN_DISABLED, + + WIFI_EXTSCAN_BUCKET_STARTED_EVENT = 0x10, + WIFI_EXTSCAN_CYCLE_STARTED_EVENT, + WIFI_EXTSCAN_CYCLE_COMPLETED_EVENT, +}; + +/** + * enum extscan_configuration_flags - extscan config flags + * @EXTSCAN_LP_EXTENDED_BATCHING: extended batching + */ +enum extscan_configuration_flags { + EXTSCAN_LP_EXTENDED_BATCHING = 0x00000001, +}; + +/** + * struct ext_scan_capabilities_response - extscan capabilities response data + * @requestId: request identifier + * @status: status + * @max_scan_cache_size: total space allocated for scan (in bytes) + * @max_scan_buckets: maximum number of channel buckets + * @max_ap_cache_per_scan: maximum number of APs that can be stored per scan + * @max_rssi_sample_size: number of RSSI samples used for averaging RSSI + * @ax_scan_reporting_threshold: max possible report_threshold + * @max_hotlist_bssids: maximum number of entries for hotlist APs + * @max_significant_wifi_change_aps: maximum number of entries for + * significant wifi change APs + * @max_bssid_history_entries: number of BSSID/RSSI entries that device can hold + * @max_hotlist_ssids: maximum number of entries for hotlist SSIDs + * @max_number_epno_networks: max number of epno entries + * @max_number_epno_networks_by_ssid: max number of epno entries + * if ssid is specified, that is, epno entries for + * which an exact match is required, + * or entries corresponding to hidden ssids + * @max_number_of_white_listed_ssid: max number of white listed SSIDs + * @max_number_of_black_listed_bssid: max number of black listed BSSIDs + */ +struct ext_scan_capabilities_response { + uint32_t requestId; + uint32_t status; + + uint32_t max_scan_cache_size; + uint32_t max_scan_buckets; + uint32_t max_ap_cache_per_scan; + uint32_t max_rssi_sample_size; + uint32_t max_scan_reporting_threshold; + + uint32_t max_hotlist_bssids; + uint32_t max_significant_wifi_change_aps; + + uint32_t max_bssid_history_entries; + uint32_t max_hotlist_ssids; + uint32_t max_number_epno_networks; + uint32_t max_number_epno_networks_by_ssid; + uint32_t max_number_of_white_listed_ssid; + uint32_t max_number_of_black_listed_bssid; +}; + +typedef struct { + /* Time of discovery */ + uint64_t ts; + + /* Null terminated SSID */ + uint8_t ssid[WLAN_SSID_MAX_LEN + 1]; + + struct qdf_mac_addr bssid; + + /* Frequency in MHz */ + uint32_t channel; + + /* RSSI in dBm */ + int32_t rssi; + + /* RTT in nanoseconds */ + uint32_t rtt; + + /* Standard deviation in rtt */ + uint32_t rtt_sd; + + /* Period advertised in the beacon */ + uint16_t beaconPeriod; + + /* Capabilities advertised in the beacon */ + uint16_t capability; + + uint16_t ieLength; + + uint8_t ieData[]; +} tSirWifiScanResult, *tpSirWifiScanResult; + +/** + * struct extscan_hotlist_match - extscan hotlist match + * @requestId: request identifier + * @numOfAps: number of bssids retrieved by the scan + * @moreData: 0 - for last fragment + * 1 - still more fragment(s) coming + * @ap: wifi scan result + */ +struct extscan_hotlist_match { + uint32_t requestId; + bool moreData; + bool ap_found; + uint32_t numOfAps; + tSirWifiScanResult ap[]; +}; + +/** + * struct extscan_cached_scan_result - extscan cached scan result + * @scan_id: a unique identifier for the scan unit + * @flags: a bitmask with additional information about scan + * @num_results: number of bssids retrieved by the scan + * @buckets_scanned: bitmask of buckets scanned in current extscan cycle + * @ap: wifi scan bssid results info + */ +struct extscan_cached_scan_result { + uint32_t scan_id; + uint32_t flags; + uint32_t num_results; + uint32_t buckets_scanned; + tSirWifiScanResult *ap; +}; + +/** + * struct extscan_cached_scan_results - extscan cached scan results + * @request_id: request identifier + * @more_data: 0 - for last fragment + * 1 - still more fragment(s) coming + * @num_scan_ids: number of scan ids + * @result: wifi scan result + */ +struct extscan_cached_scan_results { + uint32_t request_id; + bool more_data; + uint32_t num_scan_ids; + struct extscan_cached_scan_result *result; +}; + + +/** + * struct tSirWifiFullScanResultEvent - extscan full scan event + * @request_id: request identifier + * @moreData: 0 - for last fragment + * 1 - still more fragment(s) coming + * @ap: bssid info + * + * Reported when each probe response is received, if report_events + * enabled in struct wifi_scan_cmd_req_params + */ +typedef struct { + uint32_t requestId; + bool moreData; + tSirWifiScanResult ap; +} tSirWifiFullScanResultEvent, *tpSirWifiFullScanResultEvent; + +/** + * struct pno_match_found - epno match found + * @request_id: request identifier + * @moreData: 0 - for last fragment + * 1 - still more fragment(s) coming + * @num_results: number of bssids, driver sends this event to upper layer + * for every beacon, hence %num_results is always set to 1. + * @ap: bssid info + * + * Reported when each beacon probe response is received with + * epno match found tag. + */ +struct pno_match_found { + uint32_t request_id; + bool more_data; + uint32_t num_results; + tSirWifiScanResult ap[]; +}; + +/** + * struct sir_extscan_generic_response - + * Generic ExtScan Response structure + * @request_id: ID of the request + * @status: operation status returned by firmware + */ +struct sir_extscan_generic_response { + uint32_t request_id; + uint32_t status; +}; + +typedef struct { + struct qdf_mac_addr bssid; + uint32_t channel; + uint32_t numOfRssi; + + /* Rssi history in db */ + int32_t rssi[]; +} tSirWifiSignificantChange, *tpSirWifiSignificantChange; + +typedef struct { + uint32_t requestId; + + bool moreData; + uint32_t numResults; + tSirWifiSignificantChange ap[]; +} tSirWifiSignificantChangeEvent, *tpSirWifiSignificantChangeEvent; + +typedef struct { + uint32_t requestId; + uint32_t numResultsAvailable; +} tSirExtScanResultsAvailableIndParams, *tpSirExtScanResultsAvailableIndParams; + +typedef struct { + uint32_t requestId; + uint32_t status; + uint8_t scanEventType; + uint32_t buckets_scanned; +} tSirExtScanOnScanEventIndParams, *tpSirExtScanOnScanEventIndParams; + +#define MAX_EPNO_NETWORKS 64 + +#define SIR_PASSPOINT_LIST_MAX_NETWORKS 8 + +/** + * struct wifi_passpoint_match - wifi passpoint network match + * @id: network block identifier for the matched network + * @anqp_len: length of ANQP blob + * @ap: scan result, with channel and beacon information + * @anqp: ANQP data, in the information_element format + */ +struct wifi_passpoint_match { + uint32_t request_id; + uint32_t id; + uint32_t anqp_len; + tSirWifiScanResult ap; + uint8_t anqp[]; +}; +#endif /* FEATURE_WLAN_EXTSCAN */ + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +struct auto_shutdown_cmd { + uint32_t timer_val; +}; +#endif + +#ifdef WLAN_POWER_DEBUG +/** + * struct power_stats_response - Power stats response + * @cumulative_sleep_time_ms: cumulative sleep time in ms + * @cumulative_total_on_time_ms: total awake time in ms + * @deep_sleep_enter_counter: deep sleep enter counter + * @last_deep_sleep_enter_tstamp_ms: last deep sleep enter timestamp + * @debug_register_fmt: debug registers format + * @num_debug_register: number of debug registers + * @debug_registers: Pointer to the debug registers buffer + */ +struct power_stats_response { + uint32_t cumulative_sleep_time_ms; + uint32_t cumulative_total_on_time_ms; + uint32_t deep_sleep_enter_counter; + uint32_t last_deep_sleep_enter_tstamp_ms; + uint32_t debug_register_fmt; + uint32_t num_debug_register; + uint32_t *debug_registers; +}; +#endif + +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS +#define MAX_BCNMISS_BITMAP 8 +/** + * struct bcn_reception_stats_rsp - beacon stats response + * @total_bcn_cnt: total beacon count (tbtt instances) + * @total_bmiss_cnt: Total beacon miss count in last 255 beacons, max 255 + * @bmiss_bitmap: This bitmap indicates the status of the last 255 beacons. + * If a bit is set, that means the corresponding beacon was missed. + * Bit 0 of bmiss_bitmap[0] represents the most recent beacon. + * The total_bcn_cnt field indicates how many bits within bmiss_bitmap + * are valid. + */ +struct bcn_reception_stats_rsp { + uint32_t vdev_id; + uint32_t total_bcn_cnt; + uint32_t total_bmiss_cnt; + uint32_t bmiss_bitmap[MAX_BCNMISS_BITMAP]; +}; +#endif + +/** + * struct lfr_firmware_status - LFR status in firmware + * @is_disabled: Is LFR disabled in FW + * @disable_lfr_event: Disable attempt done in FW + */ +struct lfr_firmware_status { + uint32_t is_disabled; + struct completion disable_lfr_event; +}; + +/** + * struct rso_cmd_status - RSO Command status + * @vdev_id: Vdev ID for which RSO command sent + * @status: Status of RSO command sent to FW + */ +struct rso_cmd_status { + uint32_t vdev_id; + bool status; +}; + +enum { + SIR_AP_RX_DATA_OFFLOAD = 0x00, + SIR_STA_RX_DATA_OFFLOAD = 0x01, +}; + +/** + * struct sir_set_vdev_ies_per_band + * @msg_type: message type + * @len: message length + * @vdev_id: vdev id + * + * Message wrapper structure for eWNI_SME_SET_VDEV_IES_PER_BAND. + */ +struct sir_set_vdev_ies_per_band { + uint16_t msg_type; + uint16_t len; + uint32_t vdev_id; + uint16_t dot11_mode; + enum QDF_OPMODE device_mode; +}; + +/** + * struct sir_set_ht_vht_cfg - ht, vht IE config + * @msg_type: message type + * @len: message length + * @pdev_id: pdev id + * @nss: Nss value + * @dot11mode: Dot11 mode. + * + * Message wrapper structure for set HT/VHT IE req. + */ +struct sir_set_ht_vht_cfg { + uint16_t msg_type; + uint16_t len; + uint32_t pdev_id; + uint32_t nss; + uint32_t dot11mode; +}; + +#define WIFI_INVALID_PEER_ID (-1) +#define WIFI_INVALID_VDEV_ID (-1) +#define WIFI_MAX_AC (4) +#define RATE_STAT_MCS_MASK (0xFF00) +#define RATE_STAT_GET_MCS_INDEX(x) (((x) & RATE_STAT_MCS_MASK) >> 8) + +typedef struct { + uint32_t paramId; + uint8_t ifaceId; + uint32_t rspId; + uint32_t moreResultToFollow; + uint32_t nr_received; + union { + uint32_t num_peers; + uint32_t num_radio; + }; + + uint32_t peer_event_number; + /* Variable length field - Do not add anything after this */ + uint8_t results[0]; +} tSirLLStatsResults, *tpSirLLStatsResults; + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS +/*--------------------------------------------------------------------------- + WLAN_HAL_LL_NOTIFY_STATS + ---------------------------------------------------------------------------*/ + +/******************************LINK LAYER Statistics**********************/ + +typedef struct { + uint32_t reqId; + uint8_t staId; + uint32_t mpduSizeThreshold; + uint32_t aggressiveStatisticsGathering; +} tSirLLStatsSetReq, *tpSirLLStatsSetReq; + +typedef struct { + uint32_t reqId; + uint8_t staId; + uint32_t paramIdMask; +} tSirLLStatsGetReq, *tpSirLLStatsGetReq; + +typedef struct { + uint32_t reqId; + uint8_t staId; + uint32_t statsClearReqMask; + uint8_t stopReq; +} tSirLLStatsClearReq, *tpSirLLStatsClearReq; + +typedef enum { + WIFI_DISCONNECTED = 0, + WIFI_AUTHENTICATING = 1, + WIFI_ASSOCIATING = 2, + WIFI_ASSOCIATED = 3, + WIFI_EAPOL_STARTED = 4, /* if done by firmware/driver */ + WIFI_EAPOL_COMPLETED = 5, /* if done by firmware/driver */ +} tSirWifiConnectionState; + +typedef enum { + WIFI_ROAMING_IDLE = 0, + WIFI_ROAMING_ACTIVE = 1, +} tSirWifiRoamState; + +typedef enum { + WIFI_INTERFACE_STA = 0, + WIFI_INTERFACE_SOFTAP = 1, + WIFI_INTERFACE_IBSS = 2, + WIFI_INTERFACE_P2P_CLIENT = 3, + WIFI_INTERFACE_P2P_GO = 4, + WIFI_INTERFACE_NAN = 5, + WIFI_INTERFACE_MESH = 6, + WIFI_INTERFACE_NDI = 7, +} tSirWifiInterfaceMode; + +/* set for QOS association */ +#define WIFI_CAPABILITY_QOS 0x00000001 +/* set for protected assoc (802.11 beacon frame control protected bit set) */ +#define WIFI_CAPABILITY_PROTECTED 0x00000002 +/* set if 802.11 Extended Capabilities element interworking bit is set */ +#define WIFI_CAPABILITY_INTERWORKING 0x00000004 +/* set for HS20 association */ +#define WIFI_CAPABILITY_HS20 0x00000008 +/* set is 802.11 Extended Capabilities element UTF-8 SSID bit is set */ +#define WIFI_CAPABILITY_SSID_UTF8 0x00000010 +/* set is 802.11 Country Element is present */ +#define WIFI_CAPABILITY_COUNTRY 0x00000020 + +struct wifi_interface_info { + /* tSirWifiInterfaceMode */ + /* interface mode */ + uint8_t mode; + /* interface mac address (self) */ + struct qdf_mac_addr macAddr; + /* tSirWifiConnectionState */ + /* connection state (valid for STA, CLI only) */ + uint8_t state; + /* tSirWifiRoamState */ + /* roaming state */ + uint32_t roaming; + /* WIFI_CAPABILITY_XXX (self) */ + uint32_t capabilities; + /* null terminated SSID */ + uint8_t ssid[33]; + /* bssid */ + struct qdf_mac_addr bssid; + /* country string advertised by AP */ + uint8_t apCountryStr[CFG_COUNTRY_CODE_LEN]; + /* country string for this association */ + uint8_t countryStr[CFG_COUNTRY_CODE_LEN]; +}; + +/** + * struct wifi_channel_info - channel information + * @width: channel width (20, 40, 80, 80+80, 160) + * @center_freq: primary 20 MHz channel + * @center_freq0: center frequency (MHz) first segment + * @center_freq1: center frequency (MHz) second segment + */ +struct wifi_channel_info { + enum phy_ch_width width; + uint32_t center_freq; + uint32_t center_freq0; + uint32_t center_freq1; +}; + +/** + * struct wifi_rate_info - wifi rate information + * @preamble: 0:OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved + * @nss: 0:1x1, 1:2x2, 3:3x3, 4:4x4 + * @bw: 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz + * @rate_or_mcs_index: + * * OFDM/CCK: rate code per ieee std in units of 0.5mbps + * * HT/VHT: mcs index + * @reserved: reserved + * @bitrate: bitrate units of 100 Kbps + */ +struct wifi_rate_info { + uint32_t preamble:3; + uint32_t nss:2; + uint32_t bw:3; + uint32_t rate_or_mcs_index:8; + uint32_t reserved:16; + uint32_t bitrate; +}; + +/** + * struct wifi_channel_stats - channel statistics + * @channel: channel for which the stats are applicable + * @on_time: msecs the radio is awake + * @cca_busy_time: secs the CCA register is busy + */ +struct wifi_channel_stats { + struct wifi_channel_info channel; + uint32_t on_time; + uint32_t cca_busy_time; +}; + +/** + * struct wifi_radio_stats - per-radio statistics + * @radio: wifi radio for which the stats are applicable + * @on_time: msecs the radio is awake + * @tx_time: msecs the radio is transmitting + * @rx_time: msecs the radio is in active receive + * @on_time_scan: msecs the radio is awake due to all scan + * @on_time_nbd: msecs the radio is awake due to NAN + * @on_time_gscan: msecs the radio is awake due to Gscan + * @on_time_roam_scan: msecs the radio is awake due to roam scan + * @on_time_pno_scan: msecs the radio is awake due to PNO scan + * @on_time_hs20: msecs the radio is awake due to HS2.0 scans and GAS exchange + * @on_time_host_scan: msecs the radio is awake due to Host initiated scan + * @on_time_lpi_scan: msecs the radio is awake due to LPI scan + * @total_num_tx_power_levels: @tx_time_per_power_level record count + * @tx_time_per_power_level: tx time (in milliseconds) per TPC level (0.5 dBm) + * @num_channels: @channels record count + * @channels: per-channel statistics + */ +struct wifi_radio_stats { + uint32_t radio; + uint32_t on_time; + uint32_t tx_time; + uint32_t rx_time; + uint32_t on_time_scan; + uint32_t on_time_nbd; + uint32_t on_time_gscan; + uint32_t on_time_roam_scan; + uint32_t on_time_pno_scan; + uint32_t on_time_hs20; + uint32_t on_time_host_scan; + uint32_t on_time_lpi_scan; + uint32_t total_num_tx_power_levels; + uint32_t *tx_time_per_power_level; + uint32_t num_channels; + struct wifi_channel_stats *channels; +}; + +/** + * struct wifi_rate_stat - per rate statistics + * @rate: rate information + * @tx_mpdu: number of successfully transmitted data pkts (ACK rcvd) + * @rx_mpdu: number of received data pkts + * @mpdu_lost: number of data packet losses (no ACK) + * @retries: total number of data pkt retries * + * @retries_short: number of short data pkt retries + * @retries_long: number of long data pkt retries + */ +struct wifi_rate_stat { + struct wifi_rate_info rate; + uint32_t tx_mpdu; + uint32_t rx_mpdu; + uint32_t mpdu_lost; + uint32_t retries; + uint32_t retries_short; + uint32_t retries_long; +}; + +/* wifi peer type */ +typedef enum { + WIFI_PEER_STA, + WIFI_PEER_AP, + WIFI_PEER_P2P_GO, + WIFI_PEER_P2P_CLIENT, + WIFI_PEER_NAN, + WIFI_PEER_TDLS, + WIFI_PEER_INVALID, +} tSirWifiPeerType; + +/** + * struct wifi_peer_info - per peer information + * @type: peer type (AP, TDLS, GO etc.) + * @peer_macaddr: mac address + * @capabilities: peer WIFI_CAPABILITY_XXX + * @power_saving: peer power saving mode + * @num_rate: number of rates + * @rate_stats: per rate statistics, number of entries = @num_rate + */ +struct wifi_peer_info { + enum wmi_peer_type type; + struct qdf_mac_addr peer_macaddr; + uint32_t capabilities; + union { + uint32_t power_saving; + uint32_t num_rate; + }; + struct wifi_rate_stat rate_stats[0]; +}; + +/** + * struct wifi_interface_stats - Interface statistics + * @info: struct containing the current state of the interface + * @rts_succ_cnt: number of RTS/CTS sequence success + * @rts_fail_cnt: number of RTS/CTS sequence failures + * @ppdu_succ_cnt: number of PPDUs transmitted + * @ppdu_fail_cnt: number of PPDUs that failed to transmit + * @link_stats: link-level statistics + * @ac_stats: per-Access Category statistics + * @num_offload_stats: @offload_stats record count + * @offload_stats: per-offload statistics + * + * Statistics corresponding to 2nd most LSB in wifi statistics bitmap + * for getting statistics + */ +struct wifi_interface_stats { + struct wifi_interface_info info; + uint32_t rts_succ_cnt; + uint32_t rts_fail_cnt; + uint32_t ppdu_succ_cnt; + uint32_t ppdu_fail_cnt; + wmi_iface_link_stats link_stats; + wmi_wmm_ac_stats ac_stats[WIFI_AC_MAX]; + uint32_t num_offload_stats; + wmi_iface_offload_stats offload_stats[WMI_OFFLOAD_STATS_TYPE_MAX]; +}; + +/** + * struct wifi_peer_stat - peer statistics + * @num_peers: number of peers + * @peer_info: per peer statistics + * + * Peer statistics - corresponding to 3rd most LSB in + * wifi statistics bitmap for getting statistics + */ +struct wifi_peer_stat { + uint32_t num_peers; + struct wifi_peer_info peer_info[0]; +}; + +/* wifi statistics bitmap for getting statistics */ +#define WMI_LINK_STATS_RADIO 0x00000001 +#define WMI_LINK_STATS_IFACE 0x00000002 +#define WMI_LINK_STATS_ALL_PEER 0x00000004 +#define WMI_LINK_STATS_PER_PEER 0x00000008 + +/* wifi statistics bitmap for clearing statistics */ +/* all radio statistics */ +#define WIFI_STATS_RADIO 0x00000001 +/* cca_busy_time (within radio statistics) */ +#define WIFI_STATS_RADIO_CCA 0x00000002 +/* all channel statistics (within radio statistics) */ +#define WIFI_STATS_RADIO_CHANNELS 0x00000004 +/* all scan statistics (within radio statistics) */ +#define WIFI_STATS_RADIO_SCAN 0x00000008 +/* all interface statistics */ +#define WIFI_STATS_IFACE 0x00000010 +/* all tx rate statistics (within interface statistics) */ +#define WIFI_STATS_IFACE_TXRATE 0x00000020 +/* all ac statistics (within interface statistics) */ +#define WIFI_STATS_IFACE_AC 0x00000040 +/* all contention (min, max, avg) statistics (within ac statistics) */ +#define WIFI_STATS_IFACE_CONTENTION 0x00000080 +/* All peer stats on this interface */ +#define WIFI_STATS_IFACE_ALL_PEER 0x00000100 +/* Clear particular peer stats depending on the peer_mac */ +#define WIFI_STATS_IFACE_PER_PEER 0x00000200 + +/** + * struct sir_wifi_iface_tx_fail - TX failure event + * @tid: TX TID + * @msdu_num: TX MSDU failed counter + * @status: TX status from HTT message. + * Only failure status will be involved. + */ +struct sir_wifi_iface_tx_fail { + uint8_t tid; + uint16_t msdu_num; + enum htt_tx_status status; +}; + +/** + * struct sir_wifi_chan_cca_stats - channal CCA stats + * @vdev_id: vdev ID + * @idle_time: percentage of idle time, no TX, no RX, no interference + * @tx_time: percentage of time transmitting packets + * @rx_in_bss_time: percentage of time receiving packets in current BSS + * @rx_out_bss_time: percentage of time receiving packets not in current BSS + * @rx_busy_time: percentage of time interference detected + * @rx_in_bad_cond_time: percentage of time receiving packets with errors + * or packets flagged as retransmission or seqnum discontinued. + * @tx_in_bad_cond_time: percentage of time the device transmitted packets + * that haven't been ACKed. + * @wlan_not_avail_time: percentage of time the chip is unable to + * work in normal conditions. + */ +struct sir_wifi_chan_cca_stats { + uint32_t vdev_id; + uint32_t idle_time; + uint32_t tx_time; + uint32_t rx_in_bss_time; + uint32_t rx_out_bss_time; + uint32_t rx_busy_time; + uint32_t rx_in_bad_cond_time; + uint32_t tx_in_bad_cond_time; + uint32_t wlan_not_avail_time; +}; + +#define WIFI_MAX_CHAINS 8 + +/** + * struct sir_wifi_peer_signal_stats - peer signal stats + * @vdev_id: vdev ID + * @peer_id: peer ID + * @per_ant_snr: per antenna SNR + * @nf: peer background noise + * @per_ant_rx_mpdus: MPDUs received per antenna + * @per_ant_tx_mpdus: MPDUs transferred per antenna + * @num_chain: valid chain count + */ +struct sir_wifi_peer_signal_stats { + uint32_t vdev_id; + uint32_t peer_id; + + /* per antenna SNR in current bss */ + int32_t per_ant_snr[WIFI_MAX_CHAINS]; + + /* Background noise */ + int32_t nf[WIFI_MAX_CHAINS]; + + uint32_t per_ant_rx_mpdus[WIFI_MAX_CHAINS]; + uint32_t per_ant_tx_mpdus[WIFI_MAX_CHAINS]; + uint32_t num_chain; +}; + +#define WIFI_VDEV_NUM 4 +#define WFIF_MCS_NUM 10 +#define WIFI_AGGR_NUM 8 +#define WIFI_DELAY_SIZE 11 + +/** + * struct sir_wifi_tx - per AC tx stats + * @msdus: number of totoal MSDUs on MAC layer in the period + * @mpdus: number of totoal MPDUs on MAC layer in the period + * @ppdus: number of totoal PPDUs on PHY layer in the period + * @bytes: bytes of tx data on MAC layer in the period + * @drops: number of TX packets cancelled due to any reason in the period, + * such as WMM limitation/bandwidth limitation/radio congestion + * @drop_bytes: bytes of dropped TX packets in the period + * @retries: number of unacked transmissions of MPDUs + * @failed: number of packets have not been ACKed despite retried + * @aggr_len: length of the MPDU aggregation size buffer + * @mpdu_aggr_size: histogram of MPDU aggregation size + * @success_mcs_len: length of success mcs buffer + * @success_mcs: histogram of successed received MPDUs encoding rate + * @fail_mcs_len: length of failed mcs buffer + * @fail_mcs: histogram of failed received MPDUs encoding rate + * @delay_len: length of the delay histofram buffer + * @delay: histogram of delays on MAC layer + */ +struct sir_wifi_tx { + uint32_t msdus; + uint32_t mpdus; + uint32_t ppdus; + uint32_t bytes; + uint32_t drops; + uint32_t drop_bytes; + uint32_t retries; + uint32_t failed; + uint32_t aggr_len; + uint32_t *mpdu_aggr_size; + uint32_t success_mcs_len; + uint32_t *success_mcs; + uint32_t fail_mcs_len; + uint32_t *fail_mcs; + uint32_t delay_len; + uint32_t *delay; +}; + +/** + * struct sir_wifi_rx - per AC rx stats + * @mpdus: number of RX packets on MAC layer + * @bytes: bytes of RX packets on MAC layer + * @ppdus: number of RX packets on PHY layer + * @ppdu_bytes: bytes of RX packets on PHY layer + * @mpdu_lost: number of discontinuity in seqnum + * @mpdu_retry: number of RX packets flagged as retransmissions + * @mpdu_dup: number of RX packets identified as duplicates + * @mpdu_discard: number of RX packets discarded + * @aggr_len: length of MPDU aggregation histogram buffer + * @mpdu_aggr: histogram of MPDU aggregation size + * @mcs_len: length of mcs histogram buffer + * @mcs: histogram of encoding rate. + */ +struct sir_wifi_rx { + uint32_t mpdus; + uint32_t bytes; + uint32_t ppdus; + uint32_t ppdu_bytes; + uint32_t mpdu_lost; + uint32_t mpdu_retry; + uint32_t mpdu_dup; + uint32_t mpdu_discard; + uint32_t aggr_len; + uint32_t *mpdu_aggr; + uint32_t mcs_len; + uint32_t *mcs; +}; + +/** + * struct sir_wifi_ll_ext_wmm_ac_stats - stats for WMM AC + * @type: WMM AC type + * @tx_stats: pointer to TX stats + * @rx_stats: pointer to RX stats + */ +struct sir_wifi_ll_ext_wmm_ac_stats { + uint32_t type; + struct sir_wifi_tx *tx_stats; + struct sir_wifi_rx *rx_stats; +}; + +/** + * struct sir_wifi_ll_ext_peer_stats - per peer stats + * @peer_id: peer ID + * @vdev_id: VDEV ID + * @mac_address: MAC address + * @sta_ps_inds: how many times STAs go to sleep + * @sta_ps_durs: total sleep time of STAs (units in ms) + * @rx_probe_reqs: number of probe requests received + * @rx_oth_mgmts: number of other management frames received, + * not including probe requests + * @peer_signal_stat: signal stats + * @ac_stats: WMM BE/BK/VI/VO stats + */ +struct sir_wifi_ll_ext_peer_stats { + uint32_t peer_id; + uint32_t vdev_id; + tSirMacAddr mac_address; + uint32_t sta_ps_inds; + uint32_t sta_ps_durs; + uint32_t rx_probe_reqs; + uint32_t rx_oth_mgmts; + struct sir_wifi_peer_signal_stats peer_signal_stats; + struct sir_wifi_ll_ext_wmm_ac_stats ac_stats[WIFI_MAX_AC]; +}; + +/** + * struct sir_wifi_ll_ext_stats - link layer stats report + * @trigger_cond_id: Indicate what triggered this event. + * 1: timeout. 2: threshold + * @cca_chgd_bitmap: Bitmap to indicate changed channel CCA stats + * which exceeded the thresholds + * @sig_chgd_bitmap: Bitmap to indicate changed peer signal stats + * which exceeded the thresholds + * @tx_chgd_bitmap: Bitmap to indicate changed TX counters + * which exceeded the thresholds + * @rx_chgd_bitmap: Bitmap to indicate changed RX counters + * which exceeded the thresholds + * @chan_cca_stats: channel CCA stats + * @peer_signal_stats: peer signal stats + * @tx_mpdu_aggr_array_len: length of TX MPDU aggregation buffer + * @tx_succ_mcs_array_len: length of mcs buffer for ACKed MPDUs + * @tx_fail_mcs_array_len: length of mcs buffer for no-ACKed MPDUs + * @tx_delay_array_len: length of delay stats buffer + * @rx_mpdu_aggr_array_len: length of RX MPDU aggregation buffer + * @rx_mcs_array_len: length of RX mcs stats buffer + * @peer_stats: peer stats + * @cca: physical channel CCA stats + * @stats: pointer to stats data buffer. + * + * Structure of the whole statictics is like this: + * --------------------------------- + * | trigger_cond_i | + * +-------------------------------+ + * | cca_chgd_bitmap | + * +-------------------------------+ + * | sig_chgd_bitmap | + * +-------------------------------+ + * | tx_chgd_bitmap | + * +-------------------------------+ + * | rx_chgd_bitmap | + * +-------------------------------+ + * | peer_num | + * +-------------------------------+ + * | channel_num | + * +-------------------------------+ + * | tx_mpdu_aggr_array_len | + * +-------------------------------+ + * | tx_succ_mcs_array_len | + * +-------------------------------+ + * | tx_fail_mcs_array_len | + * +-------------------------------+ + * | tx_delay_array_len | + * +-------------------------------+ + * | rx_mpdu_aggr_array_len | + * +-------------------------------+ + * | rx_mcs_array_len | + * +-------------------------------+ + * | pointer to CCA stats | + * +-------------------------------+ + * | pointer to peer stats | + * +-------------------------------+ + * | CCA stats | + * +-------------------------------+ + * | peer_stats |----+ + * +-------------------------------+ | + * | per peer signals stats |<---+ + * | peer0 ~ peern | | + * +-------------------------------+ | + * | TX aggr/mcs parameters array | | + * | Length of this buffer is | | + * | configurable for user layer. |<-+ | + * +-------------------------------+ | | + * | per peer tx stats |--+ | + * | BE | <--+ + * | BK | | + * | VI | | + * | VO | | + * +-------------------------------+ | + * | TX aggr/mcs parameters array | | + * | Length of this buffer is | | + * | configurable for user layer. |<-+ | + * +-------------------------------+ | | + * | peer peer rx stats |--+ | + * | BE | <--+ + * | BE | + * | BK | + * | VI | + * | VO | + * --------------------------------- + */ +struct sir_wifi_ll_ext_stats { + uint32_t trigger_cond_id; + uint32_t cca_chgd_bitmap; + uint32_t sig_chgd_bitmap; + uint32_t tx_chgd_bitmap; + uint32_t rx_chgd_bitmap; + uint8_t peer_num; + uint8_t channel_num; + uint32_t tx_mpdu_aggr_array_len; + uint32_t tx_succ_mcs_array_len; + uint32_t tx_fail_mcs_array_len; + uint32_t tx_delay_array_len; + uint32_t rx_mpdu_aggr_array_len; + uint32_t rx_mcs_array_len; + struct sir_wifi_ll_ext_peer_stats *peer_stats; + struct sir_wifi_chan_cca_stats *cca; + uint8_t stats[]; +}; + +/** + * struct sir_channel_cca_threshold - threshold for channel CCA + * @idle_time: idle time, no TX, no RX, no interference + * @tx_time: time transmitting packets + * @rx_in_bss_time: time receiving packets in current BSSs + * @rx_out_bss_time: time receiving packets not in current BSSs + * @rx_busy_time: time interference detected + * @rx_in_bad_cond_time: receiving packets with errors + * @tx_in_bad_cond_time: time transmitted packets not been ACKed + * @wlan_not_avail_time: wlan card cannot work + */ +struct sir_channel_cca_threshold { + uint32_t idle_time; + uint32_t tx_time; + uint32_t rx_in_bss_time; + uint32_t rx_out_bss_time; + uint32_t rx_busy_time; + uint32_t rx_in_bad_cond_time; + uint32_t tx_in_bad_cond_time; + uint32_t wlan_not_avail_time; +}; + +/** + * struct sir_signal_threshold - threshold for per peer sigbal + * @snr: signal to noise rate + * @nf: noise floor + */ +struct sir_signal_threshold { + uint32_t snr; + uint32_t nf; +}; + +/** + * struct sir_tx_threshold - threshold for TX + * @msdu: TX MSDUs on MAC layer + * @mpdu: TX MPDUs on MAC layer + * @ppdu: TX PPDUs on MAC layer + * @bytes: TX bytes on MAC layer + * @msdu_drop: drooped MSDUs + * @byte_drop: dropped Bytes + * @mpdu_retry: MPDU not acked + * @ppdu_fail: PPDUs which received no block ack + * @aggregation: aggregation size + * @succ_mcs: histogram of encoding rate for acked PPDUs + * @fail_mcs: histogram of encoding rate for no-acked PPDUs + */ +struct sir_tx_threshold { + uint32_t msdu; + uint32_t mpdu; + uint32_t ppdu; + uint32_t bytes; + uint32_t msdu_drop; + uint32_t byte_drop; + uint32_t mpdu_retry; + uint32_t mpdu_fail; + uint32_t ppdu_fail; + uint32_t aggregation; + uint32_t succ_mcs; + uint32_t fail_mcs; + uint32_t delay; +}; + +/** + * struct sir_rx_threshold - threshold for RX + * @mpdu: RX MPDUs on MAC layer + * @bytes: RX bytes on MAC layer + * @ppdu: RX PPDU on PHY layer + * @ppdu_bytes: RX bytes on PHY layer + * @disorder: discontinuity in seqnum + * @mpdu_retry: MPDUs flagged as retry + * @mpdu_dup: MPDUs identified as duplicated + * @aggregation: aggregation size + * @mcs: histogram of encoding rate for PPDUs + * @ps_inds: power save indication + * @ps_durs: total time in power save + * @probe_reqs: probe request received + * @other_mgmt: other MGMT frames received + */ +struct sir_rx_threshold { + uint32_t mpdu; + uint32_t bytes; + uint32_t ppdu; + uint32_t ppdu_bytes; + uint32_t disorder; + uint32_t mpdu_lost; + uint32_t mpdu_retry; + uint32_t mpdu_dup; + uint32_t mpdu_discard; + uint32_t aggregation; + uint32_t mcs; + uint32_t ps_inds; + uint32_t ps_durs; + uint32_t probe_reqs; + uint32_t other_mgmt; +}; + +/** + * struct sir_wifi_ll_ext_stats_threshold - Threshold for stats update + * @period: MAC counter indication period (unit in ms) + * @enable: if threshold mechnism is enabled or disabled + * @enable_bitmap: whether dedicated threshold is enabed. + * Every MAC counter has a dedicated threshold. If the dedicated + * threshold is not set in the bitmap, global threshold will take + * effect. + * @global: whether clobal threshold is enabled. + * When both global and dedicated threshold are disabled, MAC counter + * will indicate stats periodically. + * @global_threshold: global threshold value + * @cca_bitmap: bitmap for CCA. + * Bit0: idle time + * Bit1: tx time + * Bit2: RX in BSS + * Bit3: RX out of BSS + * Bit4: medium busy + * Bit5: RX bad + * Bit6: TX bad + * Bit7: WLAN card not available + * @signal_bitmap: + * Bit0: Per channel SNR counter + * Bit1: Per channel noise floor counter + * @tx_bitmap: bitmap for TX counters + * Bit0: TX counter unit in MSDU + * Bit1: TX counter unit in MPDU + * Bit2: TX counter unit in PPDU + * Bit3: TX counter unit in byte + * Bit4: Dropped MSDUs + * Bit5: Dropped Bytes + * Bit6: MPDU retry counter + * Bit7: MPDU failure counter + * Bit8: PPDU failure counter + * Bit9: MPDU aggregation counter + * Bit10: MCS counter for ACKed MPDUs + * Bit11: MCS counter for Failed MPDUs + * Bit12: TX Delay counter + * @rx_bitmap:bitmap for RX counters + * Bit0: MAC RX counter unit in MPDU + * Bit1: MAC RX counter unit in byte + * Bit2: PHY RX counter unit in PPDU + * Bit3: PHY RX counter unit in byte + * Bit4: Disorder counter + * Bit5: Retry counter + * Bit6: Duplication counter + * Bit7: Discard counter + * Bit8: MPDU aggregation size counter + * Bit9: MCS counter + * Bit10: Peer STA power state change (wake to sleep) counter + * Bit11: Peer STA power save counter, total time in PS mode + * Bit12: Probe request counter + * Bit13: Other management frames counter + * @cca_thresh: CCA threshold + * @signal_thresh: signal threshold + * @tx_thresh: TX threshold + * @rx_thresh: RX threshold + * + * Generally, Link layer statistics is reported periodically. But if the + * variation of one stats of compared to the pervious notification exceeds + * a threshold, FW will report the new stats immediately. + * This structure contains threshold for different counters. + */ +struct sir_ll_ext_stats_threshold { + uint32_t period; + uint32_t enable; + uint32_t enable_bitmap; + uint32_t global; + uint32_t global_threshold; + uint32_t cca_bitmap; + uint32_t signal_bitmap; + uint32_t tx_bitmap; + uint32_t rx_bitmap; + struct sir_channel_cca_threshold cca; + struct sir_signal_threshold signal; + struct sir_tx_threshold tx; + struct sir_rx_threshold rx; +}; + +#define LL_STATS_MIN_PERIOD 10 +#define LL_STATS_INVALID_PERIOD 0xFFFFFFFF + +/* Result ID for LL stats extension */ +#define WMI_LL_STATS_EXT_PS_CHG 0x00000100 +#define WMI_LL_STATS_EXT_TX_FAIL 0x00000200 +#define WMI_LL_STATS_EXT_MAC_COUNTER 0x00000400 +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +typedef struct sAniGetLinkStatus { + uint16_t msgType; /* message type is same as the request type */ + uint16_t msgLen; /* length of the entire request */ + uint8_t linkStatus; + uint8_t sessionId; +} tAniGetLinkStatus, *tpAniGetLinkStatus; + +/** + * struct sir_lost_link_info - lost link information structure. + * + * @vdev_id: vdev_id from WMA. some modules call sessionId. + * @rssi: rssi at disconnection time. + * + * driver uses this structure to communicate information collected at + * disconnection time. + */ +struct sir_lost_link_info { + uint32_t vdev_id; + int32_t rssi; +}; + +/* find the size of given member within a structure */ +#ifndef member_size +#define member_size(type, member) (sizeof(((type *)0)->member)) +#endif + +#define RTT_INVALID 0x00 +#define RTT_TIMING_MEAS_CAPABILITY 0x01 +#define RTT_FINE_TIME_MEAS_INITIATOR_CAPABILITY 0x02 +#define RTT_FINE_TIME_MEAS_RESPONDER_CAPABILITY 0x03 + +/* number of neighbor reports that we can handle in Neighbor Report Response */ +#define MAX_SUPPORTED_NEIGHBOR_RPT 15 + +/** + * struct sir_stats_avg_factor + * @vdev_id: session id + * @stats_avg_factor: average factor + */ +struct sir_stats_avg_factor { + uint8_t vdev_id; + uint16_t stats_avg_factor; +}; + +/** + * struct sir_guard_time_request + * @vdev_id: session id + * @guard_time: guard time + */ +struct sir_guard_time_request { + uint8_t vdev_id; + uint32_t guard_time; +}; + +/* Max number of rates allowed in Supported Rates IE */ +#define MAX_NUM_SUPPORTED_RATES (8) + +/** + * struct rssi_breach_event - rssi breached event structure + * @request_id: request id + * @session_id: session id + * @curr_rssi: current rssi + * @curr_bssid: current bssid + */ +struct rssi_breach_event { + uint32_t request_id; + uint32_t session_id; + int8_t curr_rssi; + struct qdf_mac_addr curr_bssid; +}; + +/** + * struct chip_pwr_save_fail_detected_params - chip power save failure detected + * event params + * @failure_reason_code:failure reason code + * @wake_lock_bitmap:bitmap for modules voting against sleep for long duration. + */ +struct chip_pwr_save_fail_detected_params { + uint32_t failure_reason_code; + uint32_t wake_lock_bitmap[4]; +}; + +#define MAX_NUM_FW_SEGMENTS 4 + +/** + * DEFAULT_SCAN_IE_ID - Identifier for the collection of IE's added + * by default to the probe request + */ +#define DEFAULT_SCAN_IE_ID 256 + + /* MAX_DEFAULT_SCAN_IE_LEN - Maxmimum length of Default Scan IE's */ +#define MAX_DEFAULT_SCAN_IE_LEN 2048 + + /* Extended Capabilities IE header(IE Id + IE Length) length */ +#define EXT_CAP_IE_HDR_LEN 2 + +/** + * struct hdd_default_scan_ie - HDD default scan IE structure + * @message_type: message type to be set with eWNI_SME_DEFAULT_SCAN_IE + * @length: length of the struct hdd_default_scan_ie + * @vdev_id: vdev_id + * @ie_len: Default scan IE length + * @ie_data: Pointer to default scan IE data + */ +struct hdd_default_scan_ie { + uint16_t message_type; + uint16_t length; + uint16_t vdev_id; + uint16_t ie_len; + uint8_t ie_data[MAX_DEFAULT_SCAN_IE_LEN]; +}; + +/** + * struct vdev_ie_info - IE info + * @vdev_id - vdev for which the IE is being sent + * @ie_id - ID of the IE + * @length - length of the IE data + * @band - indicates IE is intended for which band + * @data - IE data + * + * This structure is used to store the IE information. + */ +struct vdev_ie_info { + uint32_t vdev_id; + uint32_t ie_id; + uint32_t length; + uint32_t band; + uint8_t *data; +}; + +/** + * struct send_extcap_ie - used to pass send_extcap_ie msg from SME to PE + * @type - MSG type + * @length - length of the message + * @seesion_id - session_id for which the message is intended for + * + * This structure is used to pass send_extcap_ie msg from SME to PE + */ +struct send_extcap_ie { + uint16_t msg_type; /* eWNI_SME_SET_IE_REQ */ + uint16_t length; + uint8_t session_id; +}; + +typedef void (*antenna_mode_cb)(uint32_t status, void *context); + +/** + * struct cfg_action_frm_tb_ppdu - action frame TB PPDU cfg + * @vdev_id - vdev id + * @cfg - enable/disable + * @frm_len - frame length + * @data - frame data + * + * This structure is used to cfg action frame tb ppdu. + */ +struct cfg_action_frm_tb_ppdu { + uint8_t vdev_id; + uint8_t cfg; + uint8_t frm_len; + uint8_t *data; +}; + +/** + * struct sir_nss_update_request + * @msgType: nss update msg type + * @msgLen: length of the msg + * @new_nss: new spatial stream value + * @ch_width: channel width - optional + * @vdev_id: session id + */ +struct sir_nss_update_request { + uint16_t msgType; + uint16_t msgLen; + uint8_t new_nss; + uint8_t ch_width; + uint32_t vdev_id; +}; + +/** + * enum sir_bcn_update_reason: bcn update reason + * @REASON_DEFAULT: reason default + * @REASON_NSS_UPDATE: If NSS is updated + * @REASON_CONFIG_UPDATE: Config update + * @REASON_SET_HT2040: HT2040 update + * @REASON_COLOR_CHANGE: Color change + * @REASON_CHANNEL_SWITCH: channel switch + */ +enum sir_bcn_update_reason { + REASON_DEFAULT = 0, + REASON_NSS_UPDATE = 1, + REASON_CONFIG_UPDATE = 2, + REASON_SET_HT2040 = 3, + REASON_COLOR_CHANGE = 4, + REASON_CHANNEL_SWITCH = 5, +}; + +/** + * struct sir_bcn_update_rsp + * + * @vdev_id: session for which bcn was updated + * @reason: bcn update reason + * @status: status of the beacon sent to FW + */ +struct sir_bcn_update_rsp { + uint8_t vdev_id; + enum sir_bcn_update_reason reason; + QDF_STATUS status; +}; + +struct sir_qos_params { + uint8_t aifsn; + uint8_t cwmin; + uint8_t cwmax; +}; + +/** + * struct sir_sme_ext_change_chan_req - channel change request + * @message_type: message id + * @length: msg length + * @new_channel: new channel + * @vdev_id: vdev id + */ +struct sir_sme_ext_cng_chan_req { + uint16_t message_type; /* eWNI_SME_EXT_CHANGE_CHANNEL */ + uint16_t length; + uint32_t new_channel; + uint8_t vdev_id; +}; + +#define IGNORE_NUD_FAIL 0 +#define DISCONNECT_AFTER_NUD_FAIL 1 +#define ROAM_AFTER_NUD_FAIL 2 +#define DISCONNECT_AFTER_ROAM_FAIL 3 + +/** + * struct sir_sme_ext_change_chan_ind. + * @session_id: session id + * @new_chan_freq: new channel frequency to change to + */ +struct sir_sme_ext_cng_chan_ind { + uint8_t session_id; + uint32_t new_chan_freq; +}; + +/** + * struct stsf - the basic stsf structure + * + * @vdev_id: vdev id + * @tsf_low: low 32bits of tsf + * @tsf_high: high 32bits of tsf + * @soc_timer_low: low 32bits of synced SOC timer value + * @soc_timer_high: high 32bits of synced SOC timer value + * @global_tsf_low: low 32bits of tsf64 + * @global_tsf_high: high 32bits of tsf64 + * + * driver use this struct to store the tsf info + */ +struct stsf { + uint32_t vdev_id; + uint32_t tsf_low; + uint32_t tsf_high; + uint32_t soc_timer_low; + uint32_t soc_timer_high; + uint32_t global_tsf_low; + uint32_t global_tsf_high; +}; + +#define SIR_BCN_FLT_MAX_ELEMS_IE_LIST 8 +/** + * struct beacon_filter_param - parameters for beacon filtering + * @vdev_id: vdev id + * @ie_map: bitwise map of IEs that needs to be filtered + * + */ +struct beacon_filter_param { + uint32_t vdev_id; + uint32_t ie_map[SIR_BCN_FLT_MAX_ELEMS_IE_LIST]; +}; + +/** + * struct adaptive_dwelltime_params - the adaptive dwelltime params + * @vdev_id: vdev id + * @is_enabled: Adaptive dwell time is enabled/disabled + * @dwelltime_mode: global default adaptive dwell mode + * @lpf_weight: weight to calculate the average low pass + * filter for channel congestion + * @passive_mon_intval: intval to monitor wifi activity in passive scan in msec + * @wifi_act_threshold: % of wifi activity used in passive scan 0-100 + * + */ +struct adaptive_dwelltime_params { + uint32_t vdev_id; + bool is_enabled; + uint8_t dwelltime_mode; + uint8_t lpf_weight; + uint8_t passive_mon_intval; + uint8_t wifi_act_threshold; +}; + +/** + * struct csa_offload_params - CSA offload request parameters + * @channel: channel + * @switch_mode: switch mode + * @sec_chan_offset: second channel offset + * @new_ch_width: new channel width + * @new_ch_freq_seg1: channel center freq 1 + * @new_ch_freq_seg2: channel center freq 2 + * @ies_present_flag: IE present flag + */ +struct csa_offload_params { + uint8_t channel; + uint32_t csa_chan_freq; + uint8_t switch_mode; + uint8_t sec_chan_offset; + uint8_t new_ch_width; + uint8_t new_op_class; + uint8_t new_ch_freq_seg1; + uint8_t new_ch_freq_seg2; + uint32_t ies_present_flag; + tSirMacAddr bssId; +}; + +/** + * enum obss_ht40_scancmd_type - obss scan command type + * @HT40_OBSS_SCAN_PARAM_START: OBSS scan start + * @HT40_OBSS_SCAN_PARAM_UPDATE: OBSS scan param update + */ +enum obss_ht40_scancmd_type { + HT40_OBSS_SCAN_PARAM_START, + HT40_OBSS_SCAN_PARAM_UPDATE +}; + +/** + * struct sme_obss_ht40_scanind_msg - sme obss scan params + * @msg_type: message type + * @length: message length + * @mac_addr: mac address + */ +struct sme_obss_ht40_scanind_msg { + uint16_t msg_type; + uint16_t length; + struct qdf_mac_addr mac_addr; +}; + +/** + * struct obss_ht40_scanind - ht40 obss scan request + * @cmd: message type + * @scan_type: message length + * @obss_passive_dwelltime: obss passive dwelltime + * @obss_active_dwelltime: obss active dwelltime + * @obss_width_trigger_interval: scan interval + * @obss_passive_total_per_channel: total passive scan time per channel + * @obss_active_total_per_channel: total active scan time per channel + * @bsswidth_ch_trans_delay: OBSS transition delay time + * @obss_activity_threshold: OBSS activity threshold + * @self_sta_idx: self sta identification + * @bss_id: BSS index + * @fortymhz_intolerent: Ht40mhz intolerance + * @channel_count: channel count + * @chan_freq_list: List of channel frequencies in MHz + * @current_operatingclass: operating class + * @iefield_len: ie's length + * @iefiled: ie's information + */ +struct obss_ht40_scanind { + enum obss_ht40_scancmd_type cmd; + enum eSirScanType scan_type; + /* In TUs */ + uint16_t obss_passive_dwelltime; + uint16_t obss_active_dwelltime; + /* In seconds */ + uint16_t obss_width_trigger_interval; + /* In TU's */ + uint16_t obss_passive_total_per_channel; + uint16_t obss_active_total_per_channel; + uint16_t bsswidth_ch_trans_delay; + uint16_t obss_activity_threshold; + uint8_t self_sta_idx; + uint8_t bss_id; + uint8_t fortymhz_intolerent; + uint8_t channel_count; + uint32_t chan_freq_list[SIR_ROAM_MAX_CHANNELS]; + uint8_t current_operatingclass; + uint16_t iefield_len; + uint8_t iefield[SIR_ROAM_SCAN_MAX_PB_REQ_SIZE]; +}; + +/** + * struct obss_scanparam - OBSS scan parameters + * @obss_passive_dwelltime: message type + * @obss_active_dwelltime: message length + * @obss_width_trigger_interval: obss passive dwelltime + * @obss_passive_total_per_channel: obss passive total scan time + * @obss_active_total_per_channel: obss active total scan time + * @bsswidth_ch_trans_delay: OBSS transition delay time + * @obss_activity_threshold: OBSS activity threshold + */ +struct obss_scanparam { + uint16_t obss_passive_dwelltime; + uint16_t obss_active_dwelltime; + uint16_t obss_width_trigger_interval; + uint16_t obss_passive_total_per_channel; + uint16_t obss_active_total_per_channel; + uint16_t bsswidth_ch_trans_delay; + uint16_t obss_activity_threshold; +}; + +/** + * struct sir_apf_set_offload - set apf filter instructions + * @session_id: session identifier + * @version: host apf version + * @filter_id: Filter ID for APF filter + * @total_length: The total length of the full instruction + * total_length equal to 0 means reset + * @current_offset: current offset, 0 means start a new setting + * @current_length: Length of current @program + * @program: APF instructions + */ +struct sir_apf_set_offload { + uint8_t session_id; + uint32_t version; + uint32_t filter_id; + uint32_t total_length; + uint32_t current_offset; + uint32_t current_length; + uint8_t *program; +}; + +/** + * struct sir_apf_offload_capabilities - get apf Capabilities + * @apf_version: fw's implement version + * @max_apf_filters: max filters that fw supports + * @max_bytes_for_apf_inst: the max bytes that can be used as apf instructions + */ +struct sir_apf_get_offload { + uint32_t apf_version; + uint32_t max_apf_filters; + uint32_t max_bytes_for_apf_inst; +}; + +#ifdef WLAN_FEATURE_NAN +#define IFACE_NAME_SIZE 64 + +/** + * enum ndp_accept_policy - nan data path accept policy + * @NDP_ACCEPT_POLICY_NONE: the framework will decide the policy + * @NDP_ACCEPT_POLICY_ALL: accept policy offloaded to fw + * + */ +enum ndp_accept_policy { + NDP_ACCEPT_POLICY_NONE = 0, + NDP_ACCEPT_POLICY_ALL = 1, +}; + +/** + * enum ndp_self_role - nan data path role + * @NDP_ROLE_INITIATOR: initiator of nan data path request + * @NDP_ROLE_RESPONDER: responder to nan data path request + * + */ +enum ndp_self_role { + NDP_ROLE_INITIATOR = 0, + NDP_ROLE_RESPONDER = 1, +}; + +/** + * enum ndp_response_code - responder's response code to nan data path request + * @NDP_RESPONSE_ACCEPT: ndp request accepted + * @NDP_RESPONSE_REJECT: ndp request rejected + * @NDP_RESPONSE_DEFER: ndp request deferred until later (response to follow + * any time later) + * + */ +enum ndp_response_code { + NDP_RESPONSE_ACCEPT = 0, + NDP_RESPONSE_REJECT = 1, + NDP_RESPONSE_DEFER = 2, +}; + +/** + * enum ndp_end_type - NDP end type + * @NDP_END_TYPE_UNSPECIFIED: type is unspecified + * @NDP_END_TYPE_PEER_UNAVAILABLE: type is peer unavailable + * @NDP_END_TYPE_OTA_FRAME: NDP end frame received from peer + * + */ +enum ndp_end_type { + NDP_END_TYPE_UNSPECIFIED = 0x00, + NDP_END_TYPE_PEER_UNAVAILABLE = 0x01, + NDP_END_TYPE_OTA_FRAME = 0x02, +}; + +/** + * enum ndp_end_reason_code - NDP end reason code + * @NDP_END_REASON_UNSPECIFIED: reason is unspecified + * @NDP_END_REASON_INACTIVITY: reason is peer inactivity + * @NDP_END_REASON_PEER_DATA_END: data end indication received from peer + * + */ +enum ndp_end_reason_code { + NDP_END_REASON_UNSPECIFIED = 0x00, + NDP_END_REASON_INACTIVITY = 0x01, + NDP_END_REASON_PEER_DATA_END = 0x02, +}; + +/** + * enum nan_status_type - NDP status type + * @NDP_RSP_STATUS_SUCCESS: request was successful + * @NDP_RSP_STATUS_ERROR: request failed + */ +enum nan_status_type { + NDP_RSP_STATUS_SUCCESS = 0x00, + NDP_RSP_STATUS_ERROR = 0x01, +}; + +/** + * enum nan_reason_code - NDP command rsp reason code value + * @NDP_UNSUPPORTED_CONCURRENCY: Will be used in unsupported concurrency cases + * @NDP_NAN_DATA_IFACE_CREATE_FAILED: ndi create failed + * @NDP_NAN_DATA_IFACE_DELETE_FAILED: ndi delete failed + * @NDP_DATA_INITIATOR_REQ_FAILED: data initiator request failed + * @NDP_DATA_RESPONDER_REQ_FAILED: data responder request failed + * @NDP_INVALID_SERVICE_INSTANCE_ID: invalid service instance id + * @NDP_INVALID_NDP_INSTANCE_ID: invalid ndp instance id + * @NDP_INVALID_RSP_CODE: invalid response code in ndp responder request + * @NDP_INVALID_APP_INFO_LEN: invalid app info length + * @NDP_NMF_REQ_FAIL: OTA nan mgmt frame failure for data request + * @NDP_NMF_RSP_FAIL: OTA nan mgmt frame failure for data response + * @NDP_NMF_CNF_FAIL: OTA nan mgmt frame failure for confirm + * @NDP_END_FAILED: ndp end failed + * @NDP_NMF_END_REQ_FAIL: OTA nan mgmt frame failure for data end + * @NDP_VENDOR_SPECIFIC_ERROR: other vendor specific failures + */ +enum nan_reason_code { + NDP_UNSUPPORTED_CONCURRENCY = 9000, + NDP_NAN_DATA_IFACE_CREATE_FAILED = 9001, + NDP_NAN_DATA_IFACE_DELETE_FAILED = 9002, + NDP_DATA_INITIATOR_REQ_FAILED = 9003, + NDP_DATA_RESPONDER_REQ_FAILED = 9004, + NDP_INVALID_SERVICE_INSTANCE_ID = 9005, + NDP_INVALID_NDP_INSTANCE_ID = 9006, + NDP_INVALID_RSP_CODE = 9007, + NDP_INVALID_APP_INFO_LEN = 9008, + NDP_NMF_REQ_FAIL = 9009, + NDP_NMF_RSP_FAIL = 9010, + NDP_NMF_CNF_FAIL = 9011, + NDP_END_FAILED = 9012, + NDP_NMF_END_REQ_FAIL = 9013, + /* 9500 onwards vendor specific error codes */ + NDP_VENDOR_SPECIFIC_ERROR = 9500, +}; + +/** + * struct ndp_cfg - ndp configuration + * @tag: unique identifier + * @ndp_cfg_len: ndp configuration length + * @ndp_cfg: variable length ndp configuration + * + */ +struct ndp_cfg { + uint32_t tag; + uint32_t ndp_cfg_len; + uint8_t *ndp_cfg; +}; + +/** + * struct ndp_qos_cfg - ndp qos configuration + * @tag: unique identifier + * @ndp_qos_cfg_len: ndp qos configuration length + * @ndp_qos_cfg: variable length ndp qos configuration + * + */ +struct ndp_qos_cfg { + uint32_t tag; + uint32_t ndp_qos_cfg_len; + uint8_t ndp_qos_cfg[]; +}; + +/** + * struct ndp_app_info - application info shared during ndp setup + * @tag: unique identifier + * @ndp_app_info_len: ndp app info length + * @ndp_app_info: variable length application information + * + */ +struct ndp_app_info { + uint32_t tag; + uint32_t ndp_app_info_len; + uint8_t *ndp_app_info; +}; + +/** + * struct ndp_scid - structure to hold sceurity context identifier + * @scid_len: length of scid + * @scid: scid + * + */ +struct ndp_scid { + uint32_t scid_len; + uint8_t *scid; +}; + +/** + * struct ndp_pmk - structure to hold pairwise master key + * @pmk_len: length of pairwise master key + * @pmk: buffer containing pairwise master key + * + */ +struct ndp_pmk { + uint32_t pmk_len; + uint8_t *pmk; +}; + +/** + * struct ndi_create_rsp - ndi create response params + * @status: request status + * @reason: reason if any + * + */ +struct ndi_create_rsp { + uint32_t status; + uint32_t reason; + uint8_t sta_id; +}; + +/** + * struct ndi_delete_rsp - ndi delete response params + * @status: request status + * @reason: reason if any + * + */ +struct ndi_delete_rsp { + uint32_t status; + uint32_t reason; +}; + +/** + * struct ndp_initiator_req - ndp initiator request params + * @transaction_id: unique identifier + * @vdev_id: session id of the interface over which ndp is being created + * @channel: suggested channel for ndp creation + * @channel_cfg: channel config, 0=no channel, 1=optional, 2=mandatory + * @service_instance_id: Service identifier + * @peer_discovery_mac_addr: Peer's discovery mac address + * @self_ndi_mac_addr: self NDI mac address + * @ndp_config: ndp configuration params + * @ndp_info: ndp application info + * @ncs_sk_type: indicates NCS_SK_128 or NCS_SK_256 + * @pmk: pairwise master key + * + */ +struct ndp_initiator_req { + uint32_t transaction_id; + uint32_t vdev_id; + uint32_t channel; + uint32_t channel_cfg; + uint32_t service_instance_id; + struct qdf_mac_addr peer_discovery_mac_addr; + struct qdf_mac_addr self_ndi_mac_addr; + struct ndp_cfg ndp_config; + struct ndp_app_info ndp_info; + uint32_t ncs_sk_type; + struct ndp_pmk pmk; +}; + +/** + * struct ndp_initiator_rsp - response event from FW + * @transaction_id: unique identifier + * @vdev_id: session id of the interface over which ndp is being created + * @ndp_instance_id: locally created NDP instance ID + * @status: status of the ndp request + * @reason: reason for failure if any + * + */ +struct ndp_initiator_rsp { + uint32_t transaction_id; + uint32_t vdev_id; + uint32_t ndp_instance_id; + uint32_t status; + uint32_t reason; +}; + +/** + * struct ndp_indication_event - create ndp indication on the responder + * @vdev_id: session id of the interface over which ndp is being created + * @service_instance_id: Service identifier + * @peer_discovery_mac_addr: Peer's discovery mac address + * @peer_mac_addr: Peer's NDI mac address + * @ndp_initiator_mac_addr: NDI mac address of the peer initiating NDP + * @ndp_instance_id: locally created NDP instance ID + * @role: self role for NDP + * @ndp_accept_policy: accept policy configured by the upper layer + * @ndp_config: ndp configuration params + * @ndp_info: ndp application info + * @ncs_sk_type: indicates NCS_SK_128 or NCS_SK_256 + * @scid: security context identifier + * + */ +struct ndp_indication_event { + uint32_t vdev_id; + uint32_t service_instance_id; + struct qdf_mac_addr peer_discovery_mac_addr; + struct qdf_mac_addr peer_mac_addr; + uint32_t ndp_instance_id; + enum ndp_self_role role; + enum ndp_accept_policy policy; + struct ndp_cfg ndp_config; + struct ndp_app_info ndp_info; + uint32_t ncs_sk_type; + struct ndp_scid scid; +}; + +/** + * struct ndp_responder_req - responder's response to ndp create request + * @transaction_id: unique identifier + * @vdev_id: session id of the interface over which ndp is being created + * @ndp_instance_id: locally created NDP instance ID + * @ndp_rsp: response to the ndp create request + * @ndp_config: ndp configuration params + * @ndp_info: ndp application info + * @pmk: pairwise master key + * @ncs_sk_type: indicates NCS_SK_128 or NCS_SK_256 + * + */ +struct ndp_responder_req { + uint32_t transaction_id; + uint32_t vdev_id; + uint32_t ndp_instance_id; + enum ndp_response_code ndp_rsp; + struct ndp_cfg ndp_config; + struct ndp_app_info ndp_info; + struct ndp_pmk pmk; + uint32_t ncs_sk_type; +}; + +/** + * struct ndp_responder_rsp_event - response to responder's request + * @transaction_id: unique identifier + * @vdev_id: session id of the interface over which ndp is being created + * @status: command status + * @reason: reason for failure if any + * @peer_mac_addr: Peer's mac address + * @create_peer: Flag to indicate to create peer + */ +struct ndp_responder_rsp_event { + uint32_t transaction_id; + uint32_t vdev_id; + uint32_t status; + uint32_t reason; + struct qdf_mac_addr peer_mac_addr; + bool create_peer; +}; + +/** + * struct ndp_confirm_event - ndp confirmation event from FW + * @vdev_id: session id of the interface over which ndp is being created + * @ndp_instance_id: ndp instance id for which confirm is being generated + * @reason_code : reason code(opaque to driver) + * @num_active_ndps_on_peer: number of ndp instances on peer + * @peer_ndi_mac_addr: peer NDI mac address + * @rsp_code: ndp response code + * @ndp_info: ndp application info + * + */ +struct ndp_confirm_event { + uint32_t vdev_id; + uint32_t ndp_instance_id; + uint32_t reason_code; + uint32_t num_active_ndps_on_peer; + struct qdf_mac_addr peer_ndi_mac_addr; + enum ndp_response_code rsp_code; + struct ndp_app_info ndp_info; +}; + +/** + * struct ndp_end_req - ndp end request + * @transaction_id: unique transaction identifier + * @num_ndp_instances: number of ndp instances to be terminated + * @ndp_ids: pointer to array of ndp_instance_id to be terminated + * + */ +struct ndp_end_req { + uint32_t transaction_id; + uint32_t num_ndp_instances; + uint32_t *ndp_ids; +}; + +/** + * struct peer_ndp_map - mapping of NDP instances to peer to VDEV + * @vdev_id: session id of the interface over which ndp is being created + * @peer_ndi_mac_addr: peer NDI mac address + * @num_active_ndp_sessions: number of active NDP sessions on the peer + * @type: NDP end indication type + * @reason_code: NDP end indication reason code + * @ndp_instance_id: NDP instance ID + * + */ +struct peer_ndp_map { + uint32_t vdev_id; + struct qdf_mac_addr peer_ndi_mac_addr; + uint32_t num_active_ndp_sessions; + enum ndp_end_type type; + enum ndp_end_reason_code reason_code; + uint32_t ndp_instance_id; +}; + +/** + * struct ndp_end_rsp_event - firmware response to ndp end request + * @transaction_id: unique identifier for the request + * @status: status of operation + * @reason: reason(opaque to host driver) + * + */ +struct ndp_end_rsp_event { + uint32_t transaction_id; + uint32_t status; + uint32_t reason; +}; + +/** + * struct ndp_end_indication_event - ndp termination notification from FW + * @num_ndp_ids: number of NDP ids + * @ndp_map: mapping of NDP instances to peer and vdev + * + */ +struct ndp_end_indication_event { + uint32_t num_ndp_ids; + struct peer_ndp_map ndp_map[]; +}; + +/** + * struct ndp_schedule_update_req - ndp schedule update request + * @transaction_id: unique identifier + * @vdev_id: session id of the interface over which ndp is being created + * @ndp_instance_id: ndp instance id for which schedule update is requested + * @ndp_qos: new set of qos parameters + * + */ +struct ndp_schedule_update_req { + uint32_t transaction_id; + uint32_t vdev_id; + uint32_t ndp_instance_id; + struct ndp_qos_cfg ndp_qos; +}; + +/** + * struct ndp_schedule_update_rsp - ndp schedule update response + * @transaction_id: unique identifier + * @vdev_id: session id of the interface over which ndp is being created + * @status: status of the request + * @reason: reason code for failure if any + * + */ +struct ndp_schedule_update_rsp { + uint32_t transaction_id; + uint32_t vdev_id; + uint32_t status; + uint32_t reason; +}; + +/** + * struct sme_ndp_peer_ind - ndp peer indication + * @msg_type: message id + * @msg_len: message length + * @session_id: session id + * @peer_mac_addr: peer mac address + * @sta_id: station id + * + */ +struct sme_ndp_peer_ind { + uint16_t msg_type; + uint16_t msg_len; + uint8_t session_id; + struct qdf_mac_addr peer_mac_addr; + uint16_t sta_id; +}; +#endif /* WLAN_FEATURE_NAN */ + +/** + * struct sir_p2p_lo_start - p2p listen offload start + * @vdev_id: vdev identifier + * @ctl_flags: control flag + * @freq: p2p listen frequency + * @period: listen offload period + * @interval: listen offload interval + * @count: number listen offload intervals + * @device_types: device types + * @dev_types_len: device types length + * @probe_resp_tmplt: probe response template + * @probe_resp_len: probe response template length + */ +struct sir_p2p_lo_start { + uint32_t vdev_id; + uint32_t ctl_flags; + uint32_t freq; + uint32_t period; + uint32_t interval; + uint32_t count; + uint8_t *device_types; + uint32_t dev_types_len; + uint8_t *probe_resp_tmplt; + uint32_t probe_resp_len; +}; + +/** + * struct sir_p2p_lo_event - P2P listen offload stop event + * @vdev_id: vdev identifier + * @reason_code: P2P listen offload stop reason + */ +struct sir_p2p_lo_event { + uint32_t vdev_id; + uint32_t reason_code; +}; + +/** + * struct sir_hal_pwr_dbg_cmd - unit test command parameters + * @pdev_id: pdev id + * @module_id: module id + * @num_args: number of arguments + * @args: arguments + */ +struct sir_mac_pwr_dbg_cmd { + uint32_t pdev_id; + uint32_t module_id; + uint32_t num_args; + uint32_t args[MAX_POWER_DBG_ARGS_SUPPORTED]; +}; + +/** + * struct sme_send_disassoc_frm_req - send disassoc request frame + * @msg_type: message type + * @length: length of message + * @vdev_id: vdev id + * @peer_mac: peer mac address + * @reason: reason for disassoc + * @wait_for_ack: wait for acknowledgment + **/ + struct sme_send_disassoc_frm_req { + uint16_t msg_type; + uint16_t length; + uint8_t vdev_id; + uint8_t peer_mac[6]; + uint16_t reason; + uint8_t wait_for_ack; + }; + +/** + * struct sme_update_access_policy_vendor_ie - update vendor ie and access + * policy + * @msg_type: message id + * @msg_len: message length + * @vdev_id: vdev id + * @ie: vendor ie + * @access_policy: access policy for vendor ie + */ +struct sme_update_access_policy_vendor_ie { + uint16_t msg_type; + uint16_t length; + uint32_t vdev_id; + uint8_t ie[WLAN_MAX_IE_LEN + 2]; + uint8_t access_policy; +}; + +/** + * struct sme_tx_fail_cnt_threshold - tx failure count for disconnect to fw + * @session_id: Session id + * @tx_fail_cnt_threshold: Tx failure count to do disconnect + */ +struct sme_tx_fail_cnt_threshold { + uint8_t session_id; + uint32_t tx_fail_cnt_threshold; +}; + +/** + * struct sme_short_retry_limit - transmission retry limit for short frames. + * @session_id: Session id + * @short_retry_limit: tranmission retry limit for short frame. + * + */ +struct sme_short_retry_limit { + uint8_t session_id; + uint32_t short_retry_limit; +}; + +/** + * struct sme_long_retry_limit - tranmission retry limit for long frames + * @session_id: Session id + * @short_retry_limit: tranmission retry limit for long frames. + * + */ +struct sme_long_retry_limit { + uint8_t session_id; + uint32_t long_retry_limit; +}; + +/** + * struct sme_addba_accept - Allow/reject the addba request frame + * @session_id: Session id + * @addba_accept: Allow/reject the addba request frame + */ +struct sme_addba_accept { + uint8_t session_id; + uint8_t addba_accept; +}; + +/** + * struct sme_sta_inactivity_timeout - set sta_inactivity_timeout + * @session_id: session Id. + * @sta_inactivity_timeout: Timeout to disconnect STA after there + * is no activity. + */ +struct sme_sta_inactivity_timeout { + uint8_t session_id; + uint32_t sta_inactivity_timeout; +}; + +/* + * struct wow_pulse_mode - WoW Pulse set cmd struct + * @wow_pulse_enable: enable or disable this feature + * @wow_pulse_pin: GPIO PIN for Pulse + * @wow_pulse_interval_low: Pulse interval low + * @wow_pulse_interval_high: Pulse interval high + * + * SME uses this structure to configure wow pulse info + * and send it to WMA + */ +struct wow_pulse_mode { + bool wow_pulse_enable; + uint8_t wow_pulse_pin; + uint16_t wow_pulse_interval_high; + uint16_t wow_pulse_interval_low; +}; + + +/** + * umac_send_mb_message_to_mac(): post message to umac + * @msg: opaque message pointer + * + * Return: QDF status + */ +QDF_STATUS umac_send_mb_message_to_mac(void *msg); + +/** + * struct scan_chan_info - channel info + * @freq: radio frequence + * @cmd flag: cmd flag + * @noise_floor: noise floor + * @cycle_count: cycle count + * @rx_clear_count: rx clear count + * @tx_frame_count: TX frame count + * @clock_freq: clock frequence MHZ + */ +struct scan_chan_info { + uint32_t freq; + uint32_t cmd_flag; + uint32_t noise_floor; + uint32_t cycle_count; + uint32_t rx_clear_count; + uint32_t tx_frame_count; + uint32_t clock_freq; +}; + +/** + * enum wow_resume_trigger - resume trigger override setting values + * @WOW_RESUME_TRIGGER_DEFAULT: fw to use platform default resume trigger + * @WOW_RESUME_TRIGGER_HTC_WAKEUP: force fw to use HTC Wakeup to resume + * @WOW_RESUME_TRIGGER_GPIO: force fw to use GPIO to resume + * @WOW_RESUME_TRIGGER_COUNT: number of resume trigger options + */ +enum wow_resume_trigger { + /* always first */ + WOW_RESUME_TRIGGER_DEFAULT = 0, + WOW_RESUME_TRIGGER_HTC_WAKEUP, + WOW_RESUME_TRIGGER_GPIO, + /* always last */ + WOW_RESUME_TRIGGER_COUNT +}; + +/** + * enum wow_interface_pause - interface pause override setting values + * @WOW_INTERFACE_PAUSE_DEFAULT: use platform default interface pause setting + * @WOW_INTERFACE_PAUSE_ENABLE: force interface pause setting to enabled + * @WOW_INTERFACE_PAUSE_DISABLE: force interface pause setting to disabled + * @WOW_INTERFACE_PAUSE_COUNT: number of interface pause options + */ +enum wow_interface_pause { + /* always first */ + WOW_INTERFACE_PAUSE_DEFAULT = 0, + WOW_INTERFACE_PAUSE_ENABLE, + WOW_INTERFACE_PAUSE_DISABLE, + /* always last */ + WOW_INTERFACE_PAUSE_COUNT +}; + +/** + * struct wow_enable_params - A collection of wow enable override parameters + * @is_unit_test: true to notify fw this is a unit-test suspend + * @interface_pause: used to override the interface pause indication sent to fw + * @resume_trigger: used to force fw to use a particular resume method + */ +struct wow_enable_params { + bool is_unit_test; + enum wow_interface_pause interface_pause; + enum wow_resume_trigger resume_trigger; +}; + +#define HE_LTF_1X 0 +#define HE_LTF_2X 1 +#define HE_LTF_4X 2 + +#define HE_LTF_ALL 0x7 +#define HE_SGI_MASK 0xFF00 + +#define AUTO_RATE_GI_400NS 8 +#define AUTO_RATE_GI_800NS 9 +#define AUTO_RATE_GI_1600NS 10 +#define AUTO_RATE_GI_3200NS 11 + +#define AUTO_RATE_LDPC_DIS_BIT 16 + +#define SET_AUTO_RATE_SGI_VAL(set_val, bit_mask) \ + (set_val = (set_val & HE_LTF_ALL) | bit_mask) + +#define SET_AUTO_RATE_HE_LTF_VAL(set_val, bit_mask) \ + (set_val = (set_val & HE_SGI_MASK) | bit_mask) + +#ifdef WLAN_FEATURE_11AX +#define HE_CAP_OUI_TYPE "\x23" +#define HE_CAP_OUI_SIZE 1 +#define HE_OP_OUI_TYPE "\x24" +#define HE_OP_OUI_SIZE 1 + +#define HE_RU_ALLOC_INDX0_MASK (0x01 << 0) +#define HE_RU_ALLOC_INDX1_MASK (0x01 << 1) +#define HE_RU_ALLOC_INDX2_MASK (0x01 << 2) +#define HE_RU_ALLOC_INDX3_MASK (0x01 << 3) + +/* 3 bits for NSS and 4 bits for RU Index */ +#define HE_PPET_NSS_LEN 3 +#define HE_PEPT_RU_IDX_LEN 4 +#define HE_PPET_NSS_RU_LEN (HE_PPET_NSS_LEN + HE_PEPT_RU_IDX_LEN) +#define HE_PPET_SIZE 3 +#define HE_BYTE_SIZE 8 + +struct ppet_hdr { + uint8_t nss:3; + uint8_t ru_idx_mask:4; + uint8_t remaining:1; +}; + +/* MAX PPET size = 7 bits + (max_nss X max_ru_number X 6) = 25 bytes */ +#define HE_MAX_PPET_SIZE WNI_CFG_HE_PPET_LEN + +#define HE_MAX_PHY_CAP_SIZE 3 + +#define HE_CH_WIDTH_GET_BIT(ch_wd, bit) (((ch_wd) >> (bit)) & 1) +#define HE_CH_WIDTH_COMBINE(b0, b1, b2, b3, b4, b5, b6) \ + ((uint8_t)(b0) | ((b1) << 1) | ((b2) << 2) | ((b3) << 3) | \ + ((b4) << 4) | ((b5) << 5) | ((b6) << 6)) +#define HE_CH_WIDTH_CLR_BIT(ch_wd, bit) (((ch_wd) >> (bit)) & ~1) + +/* + * MCS values are interpreted as in IEEE 11ax-D1.4 spec onwards + * +-----------------------------------------------------+ + * | SS8 | SS7 | SS6 | SS5 | SS4 | SS3 | SS2 | SS1 | + * +-----------------------------------------------------+ + * | 15-14 | 13-12 | 11-10 | 9-8 | 7-6 | 5-4 | 3-2 | 1-0 | + * +-----------------------------------------------------+ + */ +#define HE_MCS_NSS_SHIFT(nss) (((nss) - 1) << 1) +#define HE_MCS_MSK_4_NSS(nss) (3 << HE_MCS_NSS_SHIFT(nss)) +#define HE_MCS_INV_MSK_4_NSS(nss) (~HE_MCS_MSK_4_NSS(nss)) +#define HE_GET_MCS_4_NSS(mcs_set, nss) \ + (((mcs_set) >> HE_MCS_NSS_SHIFT(nss)) & 3) +#define HE_SET_MCS_4_NSS(mcs_set, mcs, nss) \ + (((mcs_set) & HE_MCS_INV_MSK_4_NSS(nss)) | \ + ((mcs) << HE_MCS_NSS_SHIFT(nss))) +#define HE_MCS_IS_NSS_ENABLED(mcs_set, nss) \ + ((HE_MCS_MSK_4_NSS(nss) & (mcs_set)) != HE_MCS_MSK_4_NSS(nss)) + +#define HE_MCS_ALL_DISABLED 0xFFFF + +#define HE_MCS_0_7 0x0 +#define HE_MCS_0_9 0x1 +#define HE_MCS_0_11 0x2 +#define HE_MCS_DISABLE 0x3 + +#define HE_6G_MIN_MPDU_START_SAPCE_BIT_POS 0 +#define HE_6G_MAX_AMPDU_LEN_EXP_BIT_POS 3 +#define HE_6G_MAX_MPDU_LEN_BIT_POS 6 +#define HE_6G_SMPS_BIT_POS 9 +#define HE_6G_RD_RESP_BIT_POS 11 +#define HE_6G_RX_ANT_PATTERN_BIT_POS 12 +#define HE_6G_TX_ANT_PATTERN_BIT_POS 13 + +/* + * Following formuala has been arrived at using karnaugh map and unit tested + * with sample code. Take MCS for each NSS as 2 bit value first and solve for + * 2 bit intersection of NSS. Use following table/Matrix as guide for solving + * K-Maps + * MCS 1\MCS 2 00 01 10 11 + * 00 00 00 00 11 + * 01 00 01 01 11 + * 10 00 01 10 11 + * 11 11 11 11 11 + * if output MCS is o1o0, then as per K-map reduction: + * o0 = m1.m0 | n1.n0 | (~m1).m0.(n1^n0) | (~n1).n0.(m1^m0) + * o1 = m1.m0 | n1.n0 | m1.(~m0).n1.(~n0) + * + * Please note: Calculating MCS intersection is 80211 protocol specific and + * should be implemented in PE. WMA can use this macro rather than calling any + * lim API to do the intersection. + */ +#define HE_INTERSECT_MCS_BITS_PER_NSS(m1, m0, n1, n0) \ + (((m1 & m0) | (n1 & n0) | (((~m1) & m0) & (n1 ^ n0)) | \ + (((~n1) & n0) & (m1 ^ m0))) | (((m1 & m0) | (n1 & n0) | \ + (m1 & ~m0 & n1 & ~n0)) << 1)) + +/* following takes MCS as 2 bits */ +#define HE_INTERSECT_MCS_PER_NSS(mcs_1, mcs_2) \ + HE_INTERSECT_MCS_BITS_PER_NSS((mcs_1 >> 1), (mcs_1 & 1), \ + (mcs_2 >> 1), (mcs_2 & 1)) + +/* following takes MCS as 16 bits */ +#define HE_INTERSECT_MCS(mcs_1, mcs_2) ( \ + HE_INTERSECT_MCS_PER_NSS(HE_GET_MCS_4_NSS(mcs_1, 1), \ + HE_GET_MCS_4_NSS(mcs_2, 1)) << HE_MCS_NSS_SHIFT(1) | \ + HE_INTERSECT_MCS_PER_NSS(HE_GET_MCS_4_NSS(mcs_1, 2), \ + HE_GET_MCS_4_NSS(mcs_2, 2)) << HE_MCS_NSS_SHIFT(2) | \ + HE_INTERSECT_MCS_PER_NSS(HE_GET_MCS_4_NSS(mcs_1, 3), \ + HE_GET_MCS_4_NSS(mcs_2, 3)) << HE_MCS_NSS_SHIFT(3) | \ + HE_INTERSECT_MCS_PER_NSS(HE_GET_MCS_4_NSS(mcs_1, 4), \ + HE_GET_MCS_4_NSS(mcs_2, 4)) << HE_MCS_NSS_SHIFT(4) | \ + HE_INTERSECT_MCS_PER_NSS(HE_GET_MCS_4_NSS(mcs_1, 5), \ + HE_GET_MCS_4_NSS(mcs_2, 5)) << HE_MCS_NSS_SHIFT(5) | \ + HE_INTERSECT_MCS_PER_NSS(HE_GET_MCS_4_NSS(mcs_1, 6), \ + HE_GET_MCS_4_NSS(mcs_2, 6)) << HE_MCS_NSS_SHIFT(6) | \ + HE_INTERSECT_MCS_PER_NSS(HE_GET_MCS_4_NSS(mcs_1, 7), \ + HE_GET_MCS_4_NSS(mcs_2, 7)) << HE_MCS_NSS_SHIFT(7) | \ + HE_INTERSECT_MCS_PER_NSS(HE_GET_MCS_4_NSS(mcs_1, 8), \ + HE_GET_MCS_4_NSS(mcs_2, 8)) << HE_MCS_NSS_SHIFT(8)) + +/** + * struct he_capability - to store 11ax HE capabilities + * @phy_cap: HE PHY capabilities + * @mac_cap: HE MAC capabilities + * @mcs: HE MCS + * @ppet: HE PPE threshold + */ +struct he_capability { + uint32_t phy_cap[HE_MAX_PHY_CAP_SIZE]; + uint32_t mac_cap; + uint32_t mcs; + struct wlan_psoc_host_ppe_threshold ppet; +}; +#endif + +#define HE_GET_NSS(mcs, nss) \ + do { \ + (nss) = 0; \ + while ((((mcs) >> ((nss)*2)) & 3) != 3 && nss < 8) \ + (nss)++; \ + } while (0) + +/** + * struct rsp_stats - arp packet stats + * @arp_req_enqueue: fw tx count + * @arp_req_tx_success: tx ack count + * @arp_req_tx_failure: tx ack fail count + * @arp_rsp_recvd: rx fw count + * @out_of_order_arp_rsp_drop_cnt: out of order count + * @dad_detected: dad detected + * @connect_status: connection status + * @ba_session_establishment_status: BA session status + * @connect_stats_present: connectivity stats present or not + * @tcp_ack_recvd: tcp syn ack's count + * @icmpv4_rsp_recvd: icmpv4 responses count + */ +struct rsp_stats { + uint32_t vdev_id; + uint32_t arp_req_enqueue; + uint32_t arp_req_tx_success; + uint32_t arp_req_tx_failure; + uint32_t arp_rsp_recvd; + uint32_t out_of_order_arp_rsp_drop_cnt; + uint32_t dad_detected; + uint32_t connect_status; + uint32_t ba_session_establishment_status; + bool connect_stats_present; + uint32_t tcp_ack_recvd; + uint32_t icmpv4_rsp_recvd; +}; + +/** + * struct set_arp_stats_params - set/reset arp stats + * @vdev_id: session id + * @flag: enable/disable stats + * @pkt_type: type of packet(1 - arp) + * @ip_addr: subnet ipv4 address in case of encrypted packets + * @pkt_type_bitmap: pkt bitmap + * @tcp_src_port: tcp src port for pkt tracking + * @tcp_dst_port: tcp dst port for pkt tracking + * @icmp_ipv4: target ipv4 address to track ping packets + * @reserved: reserved + */ +struct set_arp_stats_params { + uint32_t vdev_id; + uint8_t flag; + uint8_t pkt_type; + uint32_t ip_addr; + uint32_t pkt_type_bitmap; + uint32_t tcp_src_port; + uint32_t tcp_dst_port; + uint32_t icmp_ipv4; + uint32_t reserved; +}; + +/** + * struct get_arp_stats_params - get arp stats from firmware + * @pkt_type: packet type(1 - ARP) + * @vdev_id: session id + */ +struct get_arp_stats_params { + uint8_t pkt_type; + uint32_t vdev_id; +}; + +typedef void (*sme_rcpi_callback)(void *context, struct qdf_mac_addr mac_addr, + int32_t rcpi, QDF_STATUS status); +/** + * struct sme_rcpi_req - structure for querying rcpi info + * @session_id: session for which rcpi is required + * @measurement_type: type of measurement from enum rcpi_measurement_type + * @rcpi_callback: callback function to be invoked for rcpi response + * @rcpi_context: context info for rcpi callback + * @mac_addr: peer addr for which rcpi is required + */ +struct sme_rcpi_req { + uint32_t session_id; + enum rcpi_measurement_type measurement_type; + sme_rcpi_callback rcpi_callback; + void *rcpi_context; + struct qdf_mac_addr mac_addr; +}; + +/* + * @SCAN_REJECT_DEFAULT: default value + * @CONNECTION_IN_PROGRESS: connection is in progress + * @REASSOC_IN_PROGRESS: reassociation is in progress + * @EAPOL_IN_PROGRESS: STA/P2P-CLI is in middle of EAPOL/WPS exchange + * @SAP_EAPOL_IN_PROGRESS: SAP/P2P-GO is in middle of EAPOL/WPS exchange + * @SAP_CONNECTION_IN_PROGRESS: SAP/P2P-GO is in middle of connection. + * @NAN_ENABLE_DISABLE_IN_PROGRESS: NAN is in middle of enable/disable discovery + */ +enum scan_reject_states { + SCAN_REJECT_DEFAULT = 0, + CONNECTION_IN_PROGRESS, + REASSOC_IN_PROGRESS, + EAPOL_IN_PROGRESS, + SAP_EAPOL_IN_PROGRESS, + SAP_CONNECTION_IN_PROGRESS, + NAN_ENABLE_DISABLE_IN_PROGRESS, +}; + +/** + * sir_sme_rx_aggr_hole_ind - sme rx aggr hole indication + * @hole_cnt: num of holes detected + * @hole_info_array: hole info + */ +struct sir_sme_rx_aggr_hole_ind { + uint32_t hole_cnt; + uint32_t hole_info_array[]; +}; + +/** + * struct sir_set_rx_reorder_timeout_val - rx reorder timeout + * @rx_timeout_pri: reorder timeout for AC + * rx_timeout_pri[0] : AC_VO + * rx_timeout_pri[1] : AC_VI + * rx_timeout_pri[2] : AC_BE + * rx_timeout_pri[3] : AC_BK + */ +struct sir_set_rx_reorder_timeout_val { + uint32_t rx_timeout_pri[4]; +}; + +/** + * struct sir_peer_set_rx_blocksize - set rx blocksize + * @vdev_id: vdev id + * @peer_macaddr: peer mac address + * @rx_block_ack_win_limit: windows size limitation + */ +struct sir_peer_set_rx_blocksize { + uint32_t vdev_id; + struct qdf_mac_addr peer_macaddr; + uint32_t rx_block_ack_win_limit; +}; + +/** + * struct sir_rssi_disallow_lst - Structure holding Rssi based avoid candidate + * list + * @node: Node pointer + * @bssid: BSSID of the AP + * @retry_delay: Retry delay received during last rejection in ms + * @ expected_rssi: RSSI at which STA can initate + * @time_during_rejection: Timestamp during last rejection in millisec + * @reject_reason: reason to add the BSSID to BLM + * @source: Source of adding the BSSID to BLM + * @original_timeout: original timeout sent by the AP + * @received_time: Timestamp when the AP was added to the Blacklist + */ +struct sir_rssi_disallow_lst { + qdf_list_node_t node; + struct qdf_mac_addr bssid; + uint32_t retry_delay; + int8_t expected_rssi; + qdf_time_t time_during_rejection; + enum blm_reject_ap_reason reject_reason; + enum blm_reject_ap_source source; + uint32_t original_timeout; + qdf_time_t received_time; +}; + +/** + * struct chain_rssi_result - chain rssi result + * num_chains_valid: valid chain num + * @chain_rssi: chain rssi result as dBm unit + * @chain_evm: error vector magnitude + * @ant_id: antenna id + */ +#define CHAIN_MAX_NUM 8 +struct chain_rssi_result { + uint32_t num_chains_valid; + uint32_t chain_rssi[CHAIN_MAX_NUM]; + int32_t chain_evm[CHAIN_MAX_NUM]; + uint32_t ant_id[CHAIN_MAX_NUM]; +}; + +/** + * struct get_chain_rssi_req_params - get chain rssi req params + * @peer_macaddr: specific peer mac address + * @session_id: session id + */ +struct get_chain_rssi_req_params { + struct qdf_mac_addr peer_macaddr; + uint8_t session_id; +}; + +/* + * struct sir_limit_off_chan - limit off-channel command parameters + * @vdev_id: vdev id + * @is_tos_active: status of the traffic (active/inactive) + * @max_off_chan_time: max allowed off channel time + * @rest_time: home channel time + * @skip_dfs_chans: skip dfs channels during scan + */ +struct sir_limit_off_chan { + uint8_t vdev_id; + bool is_tos_active; + uint32_t max_off_chan_time; + uint32_t rest_time; + bool skip_dfs_chans; +}; + +typedef void (*roam_scan_stats_cb)(void *context, + struct wmi_roam_scan_stats_res *res); + +/** + * struct sir_roam_scan_stats - Stores roam scan context + * @vdev_id: vdev id + * @cb: callback to be invoked for roam scan stats response + * @context: context of callback + */ +struct sir_roam_scan_stats { + uint32_t vdev_id; + roam_scan_stats_cb cb; + void *context; +}; + +/** + * struct sae_info - SAE info used for commit/confirm messages + * @msg_type: Message type + * @msg_len: length of message + * @vdev_id: vdev id + * @peer_mac_addr: peer MAC address + * @ssid: SSID + */ +struct sir_sae_info { + uint16_t msg_type; + uint16_t msg_len; + uint32_t vdev_id; + struct qdf_mac_addr peer_mac_addr; + tSirMacSSid ssid; +}; + +/** + * struct sir_sae_msg - SAE msg used for message posting + * @message_type: message type + * @length: message length + * @vdev_id: vdev id + * @sae_status: SAE status, 0: Success, Non-zero: Failure. + * @peer_mac_addr: peer MAC address + */ +struct sir_sae_msg { + uint16_t message_type; + uint16_t length; + uint16_t vdev_id; + uint8_t sae_status; + tSirMacAddr peer_mac_addr; +}; + +/** + * struct set_pcl_req - Request message to set the PCL + * @chan_weights: PCL channel weights + * @band_mask: Supported band mask + */ +struct set_pcl_req { + struct wmi_pcl_chan_weights chan_weights; + uint32_t band_mask; +}; + +#ifdef WLAN_FEATURE_MOTION_DETECTION +/** + * struct sir_md_evt - motion detection event status + * @vdev_id: vdev id + * @status: md event status + */ +struct sir_md_evt { + uint8_t vdev_id; + uint32_t status; +}; + +/** + * struct sir_md_bl_evt - motion detection baseline event values + * @vdev_id: vdev id + * @bl_baseline_value: baseline correlation value calculated during baselining + * @bl_max_corr_reserved: max corr value obtained during baselining phase in % + * @bl_min_corr_reserved: min corr value obtained during baselining phase in % + */ +struct sir_md_bl_evt { + uint8_t vdev_id; + uint32_t bl_baseline_value; + uint32_t bl_max_corr_reserved; + uint32_t bl_min_corr_reserved; +}; +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +#ifdef WLAN_MWS_INFO_DEBUGFS +/** + * struct sir_get_mws_coex_info - Get MWS coex info + * @vdev_id: vdev id + * @cmd_id: wmi mws-coex command IDs + */ +struct sir_get_mws_coex_info { + uint32_t vdev_id; + uint32_t cmd_id; +}; +#endif /* WLAN_MWS_INFO_DEBUGFS */ + +/* + * struct sir_update_session_txq_edca_param + * @message_type: SME message type + * @length: size of struct sir_update_session_txq_edca_param + * @vdev_id: vdev ID + * @txq_edca_params: txq edca parameter to update + */ +struct sir_update_session_txq_edca_param { + uint16_t message_type; + uint16_t length; + uint8_t vdev_id; + tSirMacEdcaParamRecord txq_edca_params; +}; +#endif /* __SIR_API_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/sir_mac_prop_exts.h b/drivers/staging/qcacld-3.0/core/mac/inc/sir_mac_prop_exts.h new file mode 100644 index 0000000000000000000000000000000000000000..fff1294342297ce65c95958d11b2473380423e46 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/sir_mac_prop_exts.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011-2015, 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file sir_mac_prop_exts.h contains the MAC protocol + * extensions to support ANI feature set. + * Author: Chandra Modumudi + * Date: 11/27/02 + */ +#ifndef __MAC_PROP_EXTS_H +#define __MAC_PROP_EXTS_H + +#include "sir_types.h" +#include "sir_api.h" +#include "ani_system_defs.h" + +/* / EID (Element ID) definitions */ + +#define PROP_CAPABILITY_GET(bitname, value) \ + (((value) >> SIR_MAC_PROP_CAPABILITY_ ## bitname) & 1) + +#define IS_DOT11_MODE_HT(dot11Mode) \ + (((dot11Mode == MLME_DOT11_MODE_11N) || \ + (dot11Mode == MLME_DOT11_MODE_11N_ONLY) || \ + (dot11Mode == MLME_DOT11_MODE_11AC) || \ + (dot11Mode == MLME_DOT11_MODE_11AC_ONLY) || \ + (dot11Mode == MLME_DOT11_MODE_11AX) || \ + (dot11Mode == MLME_DOT11_MODE_11AX_ONLY) || \ + (dot11Mode == MLME_DOT11_MODE_ALL)) ? true:false) + +#define IS_DOT11_MODE_VHT(dot11Mode) \ + (((dot11Mode == MLME_DOT11_MODE_11AC) || \ + (dot11Mode == MLME_DOT11_MODE_11AC_ONLY) || \ + (dot11Mode == MLME_DOT11_MODE_11AX) || \ + (dot11Mode == MLME_DOT11_MODE_11AX_ONLY) || \ + (dot11Mode == MLME_DOT11_MODE_ALL)) ? true:false) + +#define IS_DOT11_MODE_HE(dot11Mode) \ + (((dot11Mode == MLME_DOT11_MODE_11AX) || \ + (dot11Mode == MLME_DOT11_MODE_11AX_ONLY) || \ + (dot11Mode == MLME_DOT11_MODE_ALL)) ? true:false) + +#define IS_DOT11_MODE_11B(dot11Mode) \ + ((dot11Mode == MLME_DOT11_MODE_11B) ? true:false) + +#define IS_BSS_VHT_CAPABLE(vhtCaps) \ + ((vhtCaps).present && \ + ((vhtCaps).rxMCSMap != 0xFFFF) && \ + ((vhtCaps).txMCSMap != 0xFFFF)) + +#define WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ 0 +#define WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ 1 +#define WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ 2 +#define WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ 3 + +#endif /* __MAC_PROP_EXTS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/sir_mac_prot_def.h b/drivers/staging/qcacld-3.0/core/mac/inc/sir_mac_prot_def.h new file mode 100644 index 0000000000000000000000000000000000000000..a7623d4188d5e8b2d670f3c08db9e8b181b1020b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/sir_mac_prot_def.h @@ -0,0 +1,2020 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file sir_mac_prot_def.h contains the MAC/PHY protocol + * definitions used across various projects. + */ + +#ifndef __MAC_PROT_DEFS_H +#define __MAC_PROT_DEFS_H + +#include + +#include "cds_api.h" +#include "sir_types.h" +#include "wni_cfg.h" +#include + +/* /Capability information related */ +#define CAPABILITY_INFO_DELAYED_BA_BIT 14 +#define CAPABILITY_INFO_IMMEDIATE_BA_BIT 15 + +/* / 11h MAC defaults */ +#define SIR_11A_CHANNEL_BEGIN 34 +#define SIR_11A_CHANNEL_END 165 +#define SIR_11B_CHANNEL_BEGIN 1 +#define SIR_11B_CHANNEL_END 14 +#define SIR_11A_FREQUENCY_OFFSET 4 +#define SIR_11B_FREQUENCY_OFFSET 1 +#define SIR_11P_CHANNEL_BEGIN 170 +#define SIR_11P_CHANNEL_END 184 + +/* / Current version of 802.11 */ +#define SIR_MAC_PROTOCOL_VERSION 0 + +/* Frame Type definitions */ + +#define SIR_MAC_MGMT_FRAME 0x0 +#define SIR_MAC_CTRL_FRAME 0x1 +#define SIR_MAC_DATA_FRAME 0x2 + +/* Data frame subtype definitions */ +#define SIR_MAC_DATA_DATA 0 +#define SIR_MAC_DATA_DATA_ACK 1 +#define SIR_MAC_DATA_DATA_POLL 2 +#define SIR_MAC_DATA_DATA_ACK_POLL 3 +#define SIR_MAC_DATA_NULL 4 +#define SIR_MAC_DATA_NULL_ACK 5 +#define SIR_MAC_DATA_NULL_POLL 6 +#define SIR_MAC_DATA_NULL_ACK_POLL 7 +#define SIR_MAC_DATA_QOS_DATA 8 +#define SIR_MAC_DATA_QOS_DATA_ACK 9 +#define SIR_MAC_DATA_QOS_DATA_POLL 10 +#define SIR_MAC_DATA_QOS_DATA_ACK_POLL 11 +#define SIR_MAC_DATA_QOS_NULL 12 +#define SIR_MAC_DATA_QOS_NULL_ACK 13 +#define SIR_MAC_DATA_QOS_NULL_POLL 14 +#define SIR_MAC_DATA_QOS_NULL_ACK_POLL 15 + +#define SIR_MAC_DATA_QOS_MASK 8 +#define SIR_MAC_DATA_NULL_MASK 4 +#define SIR_MAC_DATA_POLL_MASK 2 +#define SIR_MAC_DATA_ACK_MASK 1 + +/* Management frame subtype definitions */ + +#define SIR_MAC_MGMT_ASSOC_REQ 0x0 +#define SIR_MAC_MGMT_ASSOC_RSP 0x1 +#define SIR_MAC_MGMT_REASSOC_REQ 0x2 +#define SIR_MAC_MGMT_REASSOC_RSP 0x3 +#define SIR_MAC_MGMT_PROBE_REQ 0x4 +#define SIR_MAC_MGMT_PROBE_RSP 0x5 +#define SIR_MAC_MGMT_TIME_ADVERT 0x6 +#define SIR_MAC_MGMT_BEACON 0x8 +#define SIR_MAC_MGMT_ATIM 0x9 +#define SIR_MAC_MGMT_DISASSOC 0xA +#define SIR_MAC_MGMT_AUTH 0xB +#define SIR_MAC_MGMT_DEAUTH 0xC +#define SIR_MAC_MGMT_ACTION 0xD +#define SIR_MAC_MGMT_RESERVED15 0xF + +#define SIR_MAC_ACTION_TX 1 +#define SIR_MAC_ACTION_RX 2 + +#define SIR_MAC_BA_POLICY_IMMEDIATE 1 +#define SIR_MAC_BA_DEFAULT_BUFF_SIZE 64 + +#define MAX_BA_BUFF_SIZE 256 + +#ifdef ANI_SUPPORT_11H +#define SIR_MAC_BASIC_MEASUREMENT_TYPE 0 +#define SIR_MAC_CCA_MEASUREMENT_TYPE 1 +#define SIR_MAC_RPI_MEASUREMENT_TYPE 2 +#endif /* ANI_SUPPORT_11H */ + +/* RRM related. */ +/* Refer IEEE Std 802.11k-2008, Section 7.3.2.21, table 7.29 */ + +#define SIR_MAC_RRM_CHANNEL_LOAD_TYPE 3 +#define SIR_MAC_RRM_NOISE_HISTOGRAM_BEACON 4 +#define SIR_MAC_RRM_BEACON_TYPE 5 +#define SIR_MAC_RRM_FRAME_TYPE 6 +#define SIR_MAC_RRM_STA_STATISTICS_TYPE 7 +#define SIR_MAC_RRM_LCI_TYPE 8 +#define SIR_MAC_RRM_TSM_TYPE 9 +#define SIR_MAC_RRM_LOCATION_CIVIC_TYPE 11 +#define SIR_MAC_RRM_FINE_TIME_MEAS_TYPE 16 + +/* VHT Action Field */ +#define SIR_MAC_VHT_GID_NOTIFICATION 1 +#define SIR_MAC_VHT_OPMODE_NOTIFICATION 2 + +#define SIR_MAC_VHT_OPMODE_SIZE 3 + +#define NUM_OF_SOUNDING_DIMENSIONS 1 /*Nss - 1, (Nss = 2 for 2x2)*/ +/* HT Action Field Codes */ +#define SIR_MAC_SM_POWER_SAVE 1 + +/* block acknowledgment action frame types */ +#define SIR_MAC_ACTION_VENDOR_SPECIFIC 9 +#define SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY 0x7F +#define SIR_MAC_ACTION_P2P_SUBTYPE_PRESENCE_RSP 2 + +/* Public Action for 20/40 BSS Coexistence */ +#define SIR_MAC_ACTION_2040_BSS_COEXISTENCE 0 +#define SIR_MAC_ACTION_EXT_CHANNEL_SWITCH_ID 4 + +/* Public Action frames for GAS */ +#define SIR_MAC_ACTION_GAS_INITIAL_REQUEST 0x0A +#define SIR_MAC_ACTION_GAS_INITIAL_RESPONSE 0x0B +#define SIR_MAC_ACTION_GAS_COMEBACK_REQUEST 0x0C +#define SIR_MAC_ACTION_GAS_COMEBACK_RESPONSE 0x0D + +/* Protected Dual of Public Action(PDPA) frames Action field */ +#define SIR_MAC_PDPA_GAS_INIT_REQ 10 +#define SIR_MAC_PDPA_GAS_INIT_RSP 11 +#define SIR_MAC_PDPA_GAS_COMEBACK_REQ 12 +#define SIR_MAC_PDPA_GAS_COMEBACK_RSP 13 + +/* ----------------------------------------------------------------------------- */ +/* EID (Element ID) definitions */ +/* and their min/max lengths */ +/* ----------------------------------------------------------------------------- */ + +#define SIR_MAC_TIM_EID_MIN 3 + +#define SIR_MAC_WPA_EID 221 + +#define SIR_MAC_MAX_SUPPORTED_MCS_SET 16 + +#define VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1 390 +#define VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1 390 +#define VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_2_2 780 +#define VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_2_2 780 + +#define VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1_SGI80 433 +#define VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1_SGI80 433 +#define VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_2_2_SGI80 866 +#define VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_2_2_SGI80 866 + +#define VHT_CAP_NO_160M_SUPP 0 +#define VHT_CAP_160_SUPP 1 +#define VHT_CAP_160_AND_80P80_SUPP 2 + +#define VHT_NO_EXTD_NSS_BW_SUPP 0 +#define VHT_EXTD_NSS_80_HALF_NSS_160 1 +#define VHT_EXTD_NSS_80_HALF_NSS_80P80 2 +#define VHT_EXTD_NSS_80_3QUART_NSS_80P80 3 +#define VHT_EXTD_NSS_160_HALF_NSS_80P80 1 +#define VHT_EXTD_NSS_160_3QUART_NSS_80P80 2 +#define VHT_EXTD_NSS_2X_NSS_160_1X_NSS_80P80 3 +#define VHT_EXTD_NSS_2X_NSS_80_1X_NSS_80P80 3 + +#define VHT_MAX_NSS 8 + +#define VHT_MCS_1x1 0xFFFC +#define VHT_MCS_2x2 0xFFF3 + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +#define SIR_MAC_QCOM_VENDOR_EID 200 +#define SIR_MAC_QCOM_VENDOR_OUI "\x00\xA0\xC6" +#define SIR_MAC_QCOM_VENDOR_SIZE 3 +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +#define SIR_MAC_MAX_ADD_IE_LENGTH 2048 + +/* / Minimum length of each IE */ +#define SIR_MAC_RSN_IE_MIN_LENGTH 2 +#define SIR_MAC_WPA_IE_MIN_LENGTH 6 + +#ifdef FEATURE_WLAN_ESE +#define ESE_VERSION_4 4 +#define ESE_VERSION_SUPPORTED ESE_VERSION_4 + +/* When station sends Radio Management Cap. */ +/* State should be normal=1 */ +/* Mbssid Mask should be 0 */ +#define RM_STATE_NORMAL 1 +#endif + +#define SIR_MAC_OUI_VERSION_1 1 + +/* OWE DH Parameter element https://tools.ietf.org/html/rfc8110 */ +#define SIR_DH_PARAMETER_ELEMENT_EXT_EID 32 + +/* OUI and type definition for WPA IE in network byte order */ +#define SIR_MAC_WPA_OUI 0x01F25000 +#define SIR_MAC_WSC_OUI "\x00\x50\xf2\x04" +#define SIR_MAC_WSC_OUI_SIZE 4 +#define SIR_MAC_P2P_OUI "\x50\x6f\x9a\x09" +#define SIR_MAC_P2P_OUI_SIZE 4 +#define SIR_P2P_NOA_ATTR 12 +#define SIR_MAX_NOA_ATTR_LEN 31 +#define SIR_P2P_IE_HEADER_LEN 6 + +#define SIR_MAC_CISCO_OUI "\x00\x40\x96" +#define SIR_MAC_CISCO_OUI_SIZE 3 + +#define SIR_MAC_QCN_OUI_TYPE "\x8c\xfd\xf0\x01" +#define SIR_MAC_QCN_OUI_TYPE_SIZE 4 + +/* MBO OUI definitions */ +#define SIR_MAC_MBO_OUI "\x50\x6f\x9a\x16" +#define SIR_MAC_MBO_OUI_SIZE 4 +#define SIR_MBO_ELEM_OFFSET (2 + SIR_MAC_MBO_OUI_SIZE) + +/* min size of wme oui header: oui(3) + type + subtype + version */ +#define SIR_MAC_OUI_WME_HDR_MIN 6 + +/* ----------------------------------------------------------------------------- */ + +/* OFFSET definitions for fixed fields in Management frames */ + +/* Beacon/Probe Response offsets */ +#define SIR_MAC_B_PR_CAPAB_OFFSET 10 +#define SIR_MAC_B_PR_SSID_OFFSET 12 + +/* Association/Reassociation offsets */ +#define SIR_MAC_REASSOC_REQ_SSID_OFFSET 10 + +/* Association Request offsets */ +#define SIR_MAC_ASSOC_REQ_SSID_OFFSET 4 + +/* / Transaction sequence number definitions (used in Authentication frames) */ +#define SIR_MAC_AUTH_FRAME_1 1 +#define SIR_MAC_AUTH_FRAME_2 2 +#define SIR_MAC_AUTH_FRAME_3 3 +#define SIR_MAC_AUTH_FRAME_4 4 + +/* / Protocol defined MAX definitions */ +#define SIR_MAC_MAX_NUMBER_OF_RATES 12 +#define SIR_MAC_MAX_NUM_OF_DEFAULT_KEYS 4 +#define SIR_MAC_KEY_LENGTH 13 /* WEP Maximum key length size */ +#define SIR_MAC_AUTH_CHALLENGE_LENGTH 253 +#define SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH 128 +#define SIR_MAC_WEP_IV_LENGTH 4 +#define SIR_MAC_WEP_ICV_LENGTH 4 +#define SIR_MAC_CHALLENGE_ID_LEN 2 + +/* 2 bytes each for auth algo number, transaction number and status code */ +#define SIR_MAC_AUTH_FRAME_INFO_LEN 6 +/* 2 bytes for ID and length + SIR_MAC_AUTH_CHALLENGE_LENGTH */ +#define SIR_MAC_AUTH_CHALLENGE_BODY_LEN (SIR_MAC_CHALLENGE_ID_LEN + \ + SIR_MAC_AUTH_CHALLENGE_LENGTH) + +/* / MAX key length when ULA is used */ +#define SIR_MAC_MAX_KEY_LENGTH 32 + +/* / Macro definitions for get/set on FC fields */ +#define SIR_MAC_GET_PROT_VERSION(x) ((((uint16_t) x) & 0x0300) >> 8) +#define SIR_MAC_GET_FRAME_TYPE(x) ((((uint16_t) x) & 0x0C00) >> 8) +#define SIR_MAC_GET_FRAME_SUB_TYPE(x) ((((uint16_t) x) & 0xF000) >> 12) +#define SIR_MAC_GET_WEP_BIT_IN_FC(x) (((uint16_t) x) & 0x0040) +#define SIR_MAC_SET_PROT_VERSION(x) ((uint16_t) x) +#define SIR_MAC_SET_FRAME_TYPE(x) (((uint16_t) x) << 2) +#define SIR_MAC_SET_FRAME_SUB_TYPE(x) (((uint16_t) x) << 4) +#define SIR_MAC_SET_WEP_BIT_IN_FC(x) (((uint16_t) x) << 14) + +/* / Macro definitions for get/set on capabilityInfo bits */ +#define SIR_MAC_GET_ESS(x) (((uint16_t) x) & 0x0001) +#define SIR_MAC_GET_IBSS(x) ((((uint16_t) x) & 0x0002) >> 1) +#define SIR_MAC_GET_CF_POLLABLE(x) ((((uint16_t) x) & 0x0004) >> 2) +#define SIR_MAC_GET_CF_POLL_REQ(x) ((((uint16_t) x) & 0x0008) >> 3) +#define SIR_MAC_GET_PRIVACY(x) ((((uint16_t) x) & 0x0010) >> 4) +#define SIR_MAC_GET_SHORT_PREAMBLE(x) ((((uint16_t) x) & 0x0020) >> 5) +#define SIR_MAC_GET_SPECTRUM_MGMT(x) ((((uint16_t) x) & 0x0100) >> 8) +#define SIR_MAC_GET_QOS(x) ((((uint16_t) x) & 0x0200) >> 9) +#define SIR_MAC_GET_SHORT_SLOT_TIME(x) ((((uint16_t) x) & 0x0400) >> 10) +#define SIR_MAC_GET_APSD(x) ((((uint16_t) x) & 0x0800) >> 11) +#define SIR_MAC_GET_RRM(x) ((((uint16_t) x) & 0x1000) >> 12) +#define SIR_MAC_GET_BLOCK_ACK(x) ((((uint16_t) x) & 0xc000) >> CAPABILITY_INFO_DELAYED_BA_BIT) +#define SIR_MAC_SET_ESS(x) (((uint16_t) x) | 0x0001) +#define SIR_MAC_SET_IBSS(x) (((uint16_t) x) | 0x0002) +#define SIR_MAC_SET_CF_POLLABLE(x) (((uint16_t) x) | 0x0004) +#define SIR_MAC_SET_CF_POLL_REQ(x) (((uint16_t) x) | 0x0008) +#define SIR_MAC_SET_PRIVACY(x) (((uint16_t) x) | 0x0010) +#define SIR_MAC_SET_SHORT_PREAMBLE(x) (((uint16_t) x) | 0x0020) +#define SIR_MAC_SET_SPECTRUM_MGMT(x) (((uint16_t) x) | 0x0100) +#define SIR_MAC_SET_QOS(x) (((uint16_t) x) | 0x0200) +#define SIR_MAC_SET_SHORT_SLOT_TIME(x) (((uint16_t) x) | 0x0400) +#define SIR_MAC_SET_APSD(x) (((uint16_t) x) | 0x0800) +#define SIR_MAC_SET_RRM(x) (((uint16_t) x) | 0x1000) +#define SIR_MAC_SET_GROUP_ACK(x) (((uint16_t) x) | 0x4000) + +#define SIR_MAC_GET_VHT_MAX_AMPDU_EXPO(x) ((((uint32_t) x) & 0x03800000) >> 23) + +/* bitname must be one of the above, eg ESS, CF_POLLABLE, etc. */ +#define SIR_MAC_CLEAR_CAPABILITY(u16value, bitname) \ + ((u16value) &= (~(SIR_MAC_SET_ ## bitname(0)))) + +#define IS_WES_MODE_ENABLED(x) \ + ((x)->mlme_cfg->lfr.wes_mode_enabled) + +#define SIR_MAC_VENDOR_AP_1_OUI "\x00\x0C\x43" +#define SIR_MAC_VENDOR_AP_1_OUI_LEN 3 + +#define SIR_MAC_VENDOR_AP_3_OUI "\x00\x03\x7F" +#define SIR_MAC_VENDOR_AP_3_OUI_LEN 3 + +#define SIR_MAC_VENDOR_AP_4_OUI "\x8C\xFD\xF0" +#define SIR_MAC_VENDOR_AP_4_OUI_LEN 3 + +/* Maximum allowable size of a beacon and probe rsp frame */ +#define SIR_MAX_BEACON_SIZE 512 +#define SIR_MAX_PROBE_RESP_SIZE 512 + +/* Status Code (present in Management response frames) enum */ +/* (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */ + +enum mac_status_code { + eSIR_MAC_SUCCESS_STATUS = 0, /* Reserved */ + eSIR_MAC_UNSPEC_FAILURE_STATUS = 1, /* Unspecified reason */ + /* 802.11 reserved 2-9 */ + /* + WMM status codes(standard 1.1 table 9) + Table 9 ADDTS Response Status Codes + Value Operation + 0 Admission accepted + 1 Invalid parameters + 2 Reserved + 3 Refused + 4-255 Reserved + */ + eSIR_MAC_WME_INVALID_PARAMS_STATUS = 1, /* ?? */ + eSIR_MAC_WME_REFUSED_STATUS = 3, /* ?? */ + eSIR_MAC_CAPABILITIES_NOT_SUPPORTED_STATUS = 10, /* Cannot support all requested capabilities in the Capability Information field */ + eSIR_MAC_INABLITY_TO_CONFIRM_ASSOC_STATUS = 11, /* Reassociation denied due to inability to confirm that association exists */ + eSIR_MAC_OUTSIDE_SCOPE_OF_SPEC_STATUS = 12, /* Association denied due to reason outside the scope of this standard */ + eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS = 13, /* Responding station does not support the specified authentication algorithm */ + eSIR_MAC_AUTH_FRAME_OUT_OF_SEQ_STATUS = 14, /* Received an Authentication frame with authentication transaction sequence number */ + /* out of expected sequence */ + eSIR_MAC_CHALLENGE_FAILURE_STATUS = 15, /* Authentication rejected because of challenge failure */ + eSIR_MAC_AUTH_RSP_TIMEOUT_STATUS = 16, /* Authentication rejected due to timeout waiting for next frame in sequence */ + eSIR_MAC_MAX_ASSOC_STA_REACHED_STATUS = 17, /* Association denied because AP is unable to handle additional associated stations */ + eSIR_MAC_BASIC_RATES_NOT_SUPPORTED_STATUS = 18, /* Association denied due to requesting station not supporting all of the data rates in the */ + /* BSSBasicRateSet parameter */ + eSIR_MAC_SHORT_PREAMBLE_NOT_SUPPORTED_STATUS = 19, /* Association denied due to requesting station not supporting the short preamble */ + /* option */ + eSIR_MAC_PBCC_NOT_SUPPORTED_STATUS = 20, /* Association denied due to requesting station not supporting the PBCC modulation */ + /* option */ + eSIR_MAC_CHANNEL_AGILITY_NOT_SUPPORTED_STATUS = 21, /* Association denied due to requesting station not supporting the Channel Agility */ + /* option */ + eSIR_MAC_SPECTRUM_MGMT_REQD_STATUS = 22, /* Association request rejected because Spectrum Management capability is required */ + eSIR_MAC_PWR_CAPABILITY_BAD_STATUS = 23, /* Association request rejected because the information in the Power Capability */ + /* element is unacceptable */ + eSIR_MAC_SPRTD_CHANNELS_BAD_STATUS = 24, /* Association request rejected because the information in the Supported Channels */ + /* element is unacceptable */ + eSIR_MAC_SHORT_SLOT_NOT_SUPPORTED_STATUS = 25, /* Association denied due to requesting station not supporting the Short Slot Time */ + /* option */ + eSIR_MAC_DSSS_OFDM_NOT_SUPPORTED_STATUS = 26, /* Association denied due to requesting station not supporting the DSSS-OFDM option */ + /* reserved 27-29 */ + eSIR_MAC_TRY_AGAIN_LATER = 30, /* Association request rejected temporarily, try again later */ +#ifdef WLAN_FEATURE_11W + eSIR_MAC_ROBUST_MGMT_FRAMES_POLICY_VIOLATION_STATUS = 31, /* Robust management frames policy violation */ +#endif + eSIR_MAC_QOS_UNSPECIFIED_FAILURE_STATUS = 32, /* Unspecified, QoS-related failure */ + eSIR_MAC_QAP_NO_BANDWIDTH_STATUS = 33, /* Association denied because QoS AP has insufficient bandwidth to handle another */ + /* QoS STA */ + /* + * Association denied due to excessive frame loss rates + * and/or poor conditions/RSSI on cur channel + */ + eSIR_MAC_XS_FRAME_LOSS_POOR_CHANNEL_RSSI_STATUS = 34, + /* rent operating channel */ + eSIR_MAC_STA_QOS_NOT_SUPPORTED_STATUS = 35, /* Association (with QoS BSS) denied because the requesting STA does not support the */ + /* QoS facility */ + eSIR_MAC_STA_BLK_ACK_NOT_SUPPORTED_STATUS = 36, /* Reserved */ + eSIR_MAC_REQ_DECLINED_STATUS = 37, /* The request has been declined */ + eSIR_MAC_INVALID_PARAM_STATUS = 38, /* The request has not been successful as one or more parameters have invalid values */ + eSIR_MAC_TS_NOT_HONOURED_STATUS = 39, /* The TS has not been created because the request cannot be honored; however, a suggested */ + /* TSPEC is provided so that the initiating STA may attempt to set another TS */ + /* with the suggested changes to the TSPEC */ + eSIR_MAC_INVALID_IE_STATUS = 40, /* Invalid information element, i.e., an information element defined in this standard for */ + /* which the content does not meet the specifications in Clause 7 */ + eSIR_MAC_INVALID_GROUP_CIPHER_STATUS = 41, /* Invalid group cipher */ + eSIR_MAC_INVALID_PAIRWISE_CIPHER_STATUS = 42, /* Invalid pairwise cipher */ + eSIR_MAC_INVALID_AKMP_STATUS = 43, /* Invalid AKMP */ + eSIR_MAC_UNSUPPORTED_RSN_IE_VERSION_STATUS = 44, /* Unsupported RSN information element version */ + eSIR_MAC_INVALID_RSN_IE_CAPABILITIES_STATUS = 45, /* Invalid RSN information element capabilities */ + eSIR_MAC_CIPHER_SUITE_REJECTED_STATUS = 46, /* Cipher suite rejected because of security policy */ + eSIR_MAC_TS_NOT_CREATED_STATUS = 47, /* The TS has not been created; however, the HC may be capable of creating a TS, in */ + /* response to a request, after the time indicated in the TS Delay element */ + eSIR_MAC_DL_NOT_ALLOWED_STATUS = 48, /* Direct link is not allowed in the BSS by policy */ + eSIR_MAC_DEST_STA_NOT_KNOWN_STATUS = 49, /* The Destination STA is not present within this BSS */ + eSIR_MAC_DEST_STA_NOT_QSTA_STATUS = 50, /* The Destination STA is not a QoS STA */ + eSIR_MAC_INVALID_LISTEN_INTERVAL_STATUS = 51, /* Association denied because the ListenInterval is too large */ + + eSIR_MAC_INVALID_FT_ACTION_FRAME_COUNT = 52, + eSIR_MAC_INVALID_PMKID = 53, +#ifdef FEATURE_WLAN_ESE + eSIR_MAC_ESE_UNSPECIFIED_QOS_FAILURE_STATUS = 200, /* ESE-Unspecified, QoS related failure in (Re)Assoc response frames */ + eSIR_MAC_ESE_TSPEC_REQ_REFUSED_STATUS = 201, /* ESE-TSPEC request refused due to AP's policy configuration in AddTs Rsp, (Re)Assoc Rsp. */ + eSIR_MAC_ESE_ASSOC_DENIED_INSUFF_BW_STATUS = 202, /* ESE-Assoc denied due to insufficient bandwidth to handle new TS in (Re)Assoc Rsp. */ + eSIR_MAC_ESE_INVALID_PARAMETERS_STATUS = 203, /* ESE-Invalid parameters. (Re)Assoc request had one or more TSPEC parameters with */ + /* invalid values. */ +#endif +}; + +/** + * Reason Code (present in Deauthentication/Disassociation + * Management frames) enum + */ +typedef enum eSirMacReasonCodes { + eSIR_MAC_UNSPEC_FAILURE_REASON = 1, /* Unspecified reason */ + eSIR_MAC_PREV_AUTH_NOT_VALID_REASON = 2, /* Previous authentication no longer valid */ + eSIR_MAC_DEAUTH_LEAVING_BSS_REASON = 3, /* Deauthenticated because sending station is leaving (or has left) IBSS or ESS */ + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON = 4, /* Disassociated due to inactivity */ + eSIR_MAC_DISASSOC_DUE_TO_DISABILITY_REASON = 5, /* Disassociated because AP is unable to handle all currently associated stations */ + eSIR_MAC_CLASS2_FRAME_FROM_NON_AUTH_STA_REASON = 6, /* Class 2 frame received from nonauthenticated station */ + eSIR_MAC_CLASS3_FRAME_FROM_NON_ASSOC_STA_REASON = 7, /* Class 3 frame received from nonassociated station */ + eSIR_MAC_DISASSOC_LEAVING_BSS_REASON = 8, /* Disassociated because sending station is leaving (or has left) BSS */ + eSIR_MAC_STA_NOT_PRE_AUTHENTICATED_REASON = 9, /* Station requesting (re)association is not authenticated with responding station */ + eSIR_MAC_PWR_CAPABILITY_BAD_REASON = 10, /* Disassociated because the information in the Power Capability element is unacceptable */ + eSIR_MAC_SPRTD_CHANNELS_BAD_REASON = 11, /* Disassociated because the information in the Supported Channels element is unacceptable */ + eSIR_MAC_BSS_TRANSITION_DISASSOC = 12, + eSIR_MAC_INVALID_IE_REASON = 13, /* Invalid information element, i.e., an information element defined in this standard for */ + /* which the content does not meet the specifications in Clause 7 */ + eSIR_MAC_MIC_FAILURE_REASON = 14, /* Message integrity code (MIC) failure */ + eSIR_MAC_4WAY_HANDSHAKE_TIMEOUT_REASON = 15, /* 4-Way Handshake timeout */ + eSIR_MAC_GR_KEY_UPDATE_TIMEOUT_REASON = 16, /* Group Key Handshake timeout */ + eSIR_MAC_RSN_IE_MISMATCH_REASON = 17, /* Information element in 4-Way Handshake different from (Re)Association Request/Probe */ + /* Response/Beacon frame */ + eSIR_MAC_INVALID_MC_CIPHER_REASON = 18, /* Invalid group cipher */ + eSIR_MAC_INVALID_UC_CIPHER_REASON = 19, /* Invalid pairwise cipher */ + eSIR_MAC_INVALID_AKMP_REASON = 20, /* Invalid AKMP */ + eSIR_MAC_UNSUPPORTED_RSN_IE_VER_REASON = 21, /* Unsupported RSN information element version */ + eSIR_MAC_INVALID_RSN_CAPABILITIES_REASON = 22, /* Invalid RSN information element capabilities */ + eSIR_MAC_1X_AUTH_FAILURE_REASON = 23, /* IEEE 802.1X authentication failed */ + eSIR_MAC_CIPHER_SUITE_REJECTED_REASON = 24, /* Cipher suite rejected because of the security policy */ +#ifdef FEATURE_WLAN_TDLS + eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE = 25, /* TDLS direct link teardown due to TDLS peer STA unreachable via the TDLS direct link */ + eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON = 26, /* TDLS direct link teardown for unspecified reason */ +#endif + /* reserved 27 - 30 */ +#ifdef WLAN_FEATURE_11W + eSIR_MAC_ROBUST_MGMT_FRAMES_POLICY_VIOLATION = 31, /* Robust management frames policy violation */ +#endif + eSIR_MAC_QOS_UNSPECIFIED_REASON = 32, /* Disassociated for unspecified, QoS-related reason */ + eSIR_MAC_QAP_NO_BANDWIDTH_REASON = 33, /* Disassociated because QoS AP lacks sufficient bandwidth for this QoS STA */ + eSIR_MAC_XS_UNACKED_FRAMES_REASON = 34, /* Disassociated because excessive number of frames need to be acknowledged, but are not */ + /* acknowledged due to AP transmissions and/or poor channel conditions */ + eSIR_MAC_BAD_TXOP_USE_REASON = 35, /* Disassociated because STA is transmitting outside the limits of its TXOPs */ + eSIR_MAC_PEER_STA_REQ_LEAVING_BSS_REASON = 36, /* Requested from peer STA as the STA is leaving the BSS (or resetting) */ + eSIR_MAC_PEER_REJECT_MECHANISIM_REASON = 37, /* Requested from peer STA as it does not want to use the mechanism */ + eSIR_MAC_MECHANISM_NOT_SETUP_REASON = 38, /* Requested from peer STA as the STA received frames using the mechanism for which a */ + /* setup is required */ + eSIR_MAC_PEER_TIMEDOUT_REASON = 39, /* Requested from peer STA due to timeout */ + eSIR_MAC_CIPHER_NOT_SUPPORTED_REASON = 45, /* Peer STA does not support the requested cipher suite */ + eSIR_MAC_DISASSOC_DUE_TO_FTHANDOFF_REASON = 46, /* FT reason */ + eSIR_MAC_POOR_RSSI_CONDITIONS = 71, /* Disassociated due to poor RSSI conditions */ + + /* reserved 47 - 65535. */ + + /* + * Internal reason codes: Add any internal reason code just after + * eSIR_MAC_REASON_PROP_START and decrease the value of + * eSIR_MAC_REASON_PROP_START accordingly. + */ + eSIR_MAC_REASON_PROP_START = 65519, + eSIR_MAC_HOST_TRIGGERED_ROAM_FAILURE = 65519, + eSIR_MAC_FW_TRIGGERED_ROAM_FAILURE = 65520, + eSIR_MAC_GATEWAY_REACHABILITY_FAILURE = 65521, + eSIR_MAC_UNSUPPORTED_CHANNEL_CSA = 65522, + eSIR_MAC_OPER_CHANNEL_DISABLED_INDOOR = 65523, + eSIR_MAC_OPER_CHANNEL_USER_DISABLED = 65524, + eSIR_MAC_DEVICE_RECOVERY = 65525, + eSIR_MAC_KEY_TIMEOUT = 65526, + eSIR_MAC_OPER_CHANNEL_BAND_CHANGE = 65527, + eSIR_MAC_IFACE_DOWN = 65528, + eSIR_MAC_PEER_XRETRY_FAIL = 65529, + eSIR_MAC_PEER_INACTIVITY = 65530, + eSIR_MAC_SA_QUERY_TIMEOUT = 65531, + eSIR_MAC_CHANNEL_SWITCH_FAILED = 65532, + eSIR_MAC_BEACON_MISSED = 65533, + eSIR_MAC_USER_TRIGGERED_ROAM_FAILURE = 65534, +} tSirMacReasonCodes; + +/* / Frame control field format (2 bytes) */ +typedef struct sSirMacFrameCtl { + +#ifndef ANI_LITTLE_BIT_ENDIAN + + uint8_t subType:4; + uint8_t type:2; + uint8_t protVer:2; + + uint8_t order:1; + uint8_t wep:1; + uint8_t moreData:1; + uint8_t powerMgmt:1; + uint8_t retry:1; + uint8_t moreFrag:1; + uint8_t fromDS:1; + uint8_t toDS:1; + +#else + + uint8_t protVer:2; + uint8_t type:2; + uint8_t subType:4; + + uint8_t toDS:1; + uint8_t fromDS:1; + uint8_t moreFrag:1; + uint8_t retry:1; + uint8_t powerMgmt:1; + uint8_t moreData:1; + uint8_t wep:1; + uint8_t order:1; + +#endif + +} qdf_packed tSirMacFrameCtl, *tpSirMacFrameCtl; + +/* / Sequence control field */ +typedef struct sSirMacSeqCtl { + +#ifndef ANI_LITTLE_BIT_ENDIAN + + uint8_t seqNumLo:4; + uint8_t fragNum:4; + + uint8_t seqNumHi:8; + +#else + + uint8_t fragNum:4; + uint8_t seqNumLo:4; + uint8_t seqNumHi:8; + +#endif +} qdf_packed tSirMacSeqCtl, *tpSirMacSeqCtl; + +/* / QoS control field */ +typedef struct sSirMacQosCtl { + +#ifndef ANI_LITTLE_BIT_ENDIAN + + uint8_t rsvd:1; + uint8_t ackPolicy:2; + uint8_t esop_txopUnit:1; + uint8_t tid:4; + + uint8_t txop:8; + +#else + + uint8_t tid:4; + uint8_t esop_txopUnit:1; + uint8_t ackPolicy:2; + uint8_t rsvd:1; + + uint8_t txop:8; + +#endif +} qdf_packed tSirMacQosCtl, *tpSirMacQosCtl; + +/* / Length (in bytes) of MAC header in 3 address format */ +#define SIR_MAC_HDR_LEN_3A 24 + +typedef uint8_t tSirMacAddr[ETH_ALEN]; + +/* / 3 address MAC data header format (24/26 bytes) */ +typedef struct sSirMacDot3Hdr { + tSirMacAddr da; + tSirMacAddr sa; + uint16_t length; +} qdf_packed tSirMacDot3Hdr, *tpSirMacDot3Hdr; + +/* / 3 address MAC data header format (24/26 bytes) */ +typedef struct sSirMacDataHdr3a { + tSirMacFrameCtl fc; + uint8_t durationLo; + uint8_t durationHi; + tSirMacAddr addr1; + tSirMacAddr addr2; + tSirMacAddr addr3; + tSirMacSeqCtl seqControl; + tSirMacQosCtl qosControl; +} qdf_packed tSirMacDataHdr3a, *tpSirMacDataHdr3a; + +/* / Management header format */ +typedef struct sSirMacMgmtHdr { + tSirMacFrameCtl fc; + uint8_t durationLo; + uint8_t durationHi; + tSirMacAddr da; + tSirMacAddr sa; + tSirMacAddr bssId; + tSirMacSeqCtl seqControl; +} qdf_packed tSirMacMgmtHdr, *tpSirMacMgmtHdr; + +/* / ERP information field */ +typedef struct sSirMacErpInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint8_t reserved:5; + uint8_t barkerPreambleMode:1; + uint8_t useProtection:1; + uint8_t nonErpPresent:1; +#else + uint8_t nonErpPresent:1; + uint8_t useProtection:1; + uint8_t barkerPreambleMode:1; + uint8_t reserved:5; +#endif +} qdf_packed tSirMacErpInfo, *tpSirMacErpInfo; + +/* / Capability information field */ +typedef struct sSirMacCapabilityInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint16_t immediateBA:1; + uint16_t delayedBA:1; + uint16_t dsssOfdm:1; + uint16_t rrm:1; + uint16_t apsd:1; + uint16_t shortSlotTime:1; + uint16_t qos:1; + uint16_t spectrumMgt:1; + uint16_t channelAgility:1; + uint16_t pbcc:1; + uint16_t shortPreamble:1; + uint16_t privacy:1; + uint16_t cfPollReq:1; + uint16_t cfPollable:1; + uint16_t ibss:1; + uint16_t ess:1; +#else + uint16_t ess:1; + uint16_t ibss:1; + uint16_t cfPollable:1; + uint16_t cfPollReq:1; + uint16_t privacy:1; + uint16_t shortPreamble:1; + uint16_t pbcc:1; + uint16_t channelAgility:1; + uint16_t spectrumMgt:1; + uint16_t qos:1; + uint16_t shortSlotTime:1; + uint16_t apsd:1; + uint16_t rrm:1; + uint16_t dsssOfdm:1; + uint16_t delayedBA:1; + uint16_t immediateBA:1; +#endif +} qdf_packed tSirMacCapabilityInfo, *tpSirMacCapabilityInfo; + +typedef struct sSirMacCfParamSet { + uint8_t cfpCount; + uint8_t cfpPeriod; + uint16_t cfpMaxDuration; + uint16_t cfpDurRemaining; +} qdf_packed tSirMacCfParamSet; + +typedef struct sSirMacTim { + uint8_t dtimCount; + uint8_t dtimPeriod; + uint8_t bitmapControl; + uint8_t bitmapLength; + uint8_t bitmap[251]; +} qdf_packed tSirMacTim; + +/* 12 Bytes long because this structure can be used to represent rate */ +/* and extended rate set IEs */ +/* The parser assume this to be at least 12 */ +typedef struct sSirMacRateSet { + uint8_t numRates; + uint8_t rate[WLAN_SUPPORTED_RATES_IE_MAX_LEN]; +} qdf_packed tSirMacRateSet; + +/** struct merged_mac_rate_set - merged mac rate set + * @num_rates: num of rates + * @rate: rate list + */ +struct merged_mac_rate_set { + uint8_t num_rates; + uint8_t rate[2 * WLAN_SUPPORTED_RATES_IE_MAX_LEN]; +}; + +/* Reserve 1 byte for NULL character in the SSID name field to print in %s */ +typedef struct sSirMacSSid { + uint8_t length; + uint8_t ssId[WLAN_SSID_MAX_LEN +1]; +} qdf_packed tSirMacSSid; + +typedef struct sSirMacWpaInfo { + uint8_t length; + uint8_t info[WLAN_MAX_IE_LEN]; +} qdf_packed tSirMacWpaInfo, *tpSirMacWpaInfo, +tSirMacRsnInfo, *tpSirMacRsnInfo; + +typedef struct sSirMacWapiInfo { + uint8_t length; + uint8_t info[WLAN_MAX_IE_LEN]; +} qdf_packed tSirMacWapiInfo, *tpSirMacWapiInfo; + +typedef struct sSirMacFHParamSet { + uint16_t dwellTime; + uint8_t hopSet; + uint8_t hopPattern; + uint8_t hopIndex; +} tSirMacFHParamSet, *tpSirMacFHParamSet; + +typedef struct sSirMacIBSSParams { + uint16_t atim; +} tSirMacIBSSParams, *tpSirMacIBSSParams; + +typedef struct sSirMacRRMEnabledCap { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint8_t reserved:6; + uint8_t AntennaInformation:1; + uint8_t BSSAvailAdmission:1; + uint8_t BssAvgAccessDelay:1; + uint8_t RSNIMeasurement:1; + uint8_t RCPIMeasurement:1; + uint8_t NeighborTSFOffset:1; + uint8_t MeasurementPilotEnabled:1; + uint8_t MeasurementPilot:3; + uint8_t nonOperatinChanMax:3; + uint8_t operatingChanMax:3; + uint8_t RRMMIBEnabled:1; + uint8_t APChanReport:1; + uint8_t triggeredTCM:1; + uint8_t TCMCapability:1; + uint8_t LCIAzimuth:1; + uint8_t LCIMeasurement:1; + uint8_t statistics:1; + uint8_t NoiseHistogram:1; + uint8_t ChannelLoad:1; + uint8_t FrameMeasurement:1; + uint8_t BeaconRepCond:1; + uint8_t BeaconTable:1; + uint8_t BeaconActive:1; + uint8_t BeaconPassive:1; + uint8_t repeated:1; + uint8_t parallel:1; + uint8_t NeighborRpt:1; + uint8_t LinkMeasurement:1; + uint8_t present; +#else + uint8_t present; + uint8_t LinkMeasurement:1; + uint8_t NeighborRpt:1; + uint8_t parallel:1; + uint8_t repeated:1; + uint8_t BeaconPassive:1; + uint8_t BeaconActive:1; + uint8_t BeaconTable:1; + uint8_t BeaconRepCond:1; + uint8_t FrameMeasurement:1; + uint8_t ChannelLoad:1; + uint8_t NoiseHistogram:1; + uint8_t statistics:1; + uint8_t LCIMeasurement:1; + uint8_t LCIAzimuth:1; + uint8_t TCMCapability:1; + uint8_t triggeredTCM:1; + uint8_t APChanReport:1; + uint8_t RRMMIBEnabled:1; + uint8_t operatingChanMax:3; + uint8_t nonOperatinChanMax:3; + uint8_t MeasurementPilot:3; + uint8_t MeasurementPilotEnabled:1; + uint8_t NeighborTSFOffset:1; + uint8_t RCPIMeasurement:1; + uint8_t RSNIMeasurement:1; + uint8_t BssAvgAccessDelay:1; + uint8_t BSSAvailAdmission:1; + uint8_t AntennaInformation:1; + uint8_t reserved:6; +#endif +} tSirMacRRMEnabledCap, *tpSirMacRRMEnabledCap; + +#define MU_EDCA_DEF_AIFSN 0 +#define MU_EDCA_DEF_CW_MAX 15 +#define MU_EDCA_DEF_CW_MIN 15 +#define MU_EDCA_DEF_TIMER 255 +/* access category record */ +typedef struct sSirMacAciAifsn { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint8_t rsvd:1; + uint8_t aci:2; + uint8_t acm:1; + uint8_t aifsn:4; +#else + uint8_t aifsn:4; + uint8_t acm:1; + uint8_t aci:2; + uint8_t rsvd:1; +#endif +} qdf_packed tSirMacAciAifsn; + +/* contention window size */ +typedef struct sSirMacCW { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint8_t max:4; + uint8_t min:4; +#else + uint8_t min:4; + uint8_t max:4; +#endif +} qdf_packed tSirMacCW; + +typedef struct sSirMacEdcaParamRecord { + tSirMacAciAifsn aci; + tSirMacCW cw; + union { + uint16_t txoplimit; + uint16_t mu_edca_timer; + }; + uint8_t no_ack; +} qdf_packed tSirMacEdcaParamRecord; + +typedef struct sSirMacQosInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint8_t uapsd:1; + uint8_t txopreq:1; + uint8_t qreq:1; + uint8_t qack:1; + uint8_t count:4; +#else + uint8_t count:4; + uint8_t qack:1; + uint8_t qreq:1; + uint8_t txopreq:1; + uint8_t uapsd:1; +#endif +} qdf_packed tSirMacQosInfo; + +typedef struct sSirMacQosInfoStation { +#ifdef ANI_LITTLE_BIT_ENDIAN + uint8_t acvo_uapsd:1; + uint8_t acvi_uapsd:1; + uint8_t acbk_uapsd:1; + uint8_t acbe_uapsd:1; + uint8_t qack:1; + uint8_t maxSpLen:2; + uint8_t moreDataAck:1; +#else + uint8_t moreDataAck:1; + uint8_t maxSpLen:2; + uint8_t qack:1; + uint8_t acbe_uapsd:1; + uint8_t acbk_uapsd:1; + uint8_t acvi_uapsd:1; + uint8_t acvo_uapsd:1; +#endif +} qdf_packed tSirMacQosInfoStation, *tpSirMacQosInfoStation; + +typedef struct sSirMacEdcaParamSetIE { + uint8_t type; + uint8_t length; + tSirMacQosInfo qosInfo; + uint8_t rsvd; + tSirMacEdcaParamRecord acbe; /* best effort */ + tSirMacEdcaParamRecord acbk; /* background */ + tSirMacEdcaParamRecord acvi; /* video */ + tSirMacEdcaParamRecord acvo; /* voice */ +} qdf_packed tSirMacEdcaParamSetIE; + +/* ts info direction field can take any of these values */ +#define SIR_MAC_DIRECTION_UPLINK 0 +#define SIR_MAC_DIRECTION_DNLINK 1 +#define SIR_MAC_DIRECTION_DIRECT 2 +#define SIR_MAC_DIRECTION_BIDIR 3 + +/* access policy */ +/* reserved 0 */ +#define SIR_MAC_ACCESSPOLICY_EDCA 1 +#define SIR_MAC_ACCESSPOLICY_HCCA 2 +#define SIR_MAC_ACCESSPOLICY_BOTH 3 + +/* frame classifier types */ +#define SIR_MAC_TCLASTYPE_ETHERNET 0 +#define SIR_MAC_TCLASTYPE_TCPUDPIP 1 +#define SIR_MAC_TCLASTYPE_8021DQ 2 +/* reserved 3-255 */ + +typedef struct sSirMacTclasParamEthernet { + tSirMacAddr srcAddr; + tSirMacAddr dstAddr; + uint16_t type; +} qdf_packed tSirMacTclasParamEthernet; + +typedef struct sSirMacTclasParamIPv4 { + uint8_t version; + uint8_t srcIpAddr[4]; + uint8_t dstIpAddr[4]; + uint16_t srcPort; + uint16_t dstPort; + uint8_t dscp; + uint8_t protocol; + uint8_t rsvd; +} qdf_packed tSirMacTclasParamIPv4; + +#define SIR_MAC_TCLAS_IPV4 4 + +typedef struct sSirMacTclasParamIPv6 { + uint8_t version; + uint8_t srcIpAddr[16]; + uint8_t dstIpAddr[16]; + uint16_t srcPort; + uint16_t dstPort; + uint8_t flowLabel[3]; +} qdf_packed tSirMacTclasParamIPv6; + +typedef struct sSirMacTclasParam8021dq { + uint16_t tag; +} qdf_packed tSirMacTclasParam8021dq; + +typedef struct sSirMacTclasIE { + uint8_t type; + uint8_t length; + uint8_t userPrio; + uint8_t classifierType; + uint8_t classifierMask; +} qdf_packed tSirMacTclasIE; + +typedef struct sSirMacTsDelayIE { + uint8_t type; + uint8_t length; + uint32_t delay; +} qdf_packed tSirMacTsDelayIE; + +typedef struct sSirMacScheduleInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint16_t rsvd:9; + uint16_t direction:2; + uint16_t tsid:4; + uint16_t aggregation:1; +#else + uint16_t aggregation:1; + uint16_t tsid:4; + uint16_t direction:2; + uint16_t rsvd:9; +#endif +} qdf_packed tSirMacScheduleInfo; + +typedef struct sSirMacScheduleIE { + uint8_t type; + uint8_t length; + tSirMacScheduleInfo info; + uint32_t svcStartTime; + uint32_t svcInterval; + uint16_t maxSvcDuration; + uint16_t specInterval; +} qdf_packed tSirMacScheduleIE; + +typedef struct sSirMacQosCapabilityIE { + uint8_t type; + uint8_t length; + tSirMacQosInfo qosInfo; +} qdf_packed tSirMacQosCapabilityIE; + +typedef struct sSirMacQosCapabilityStaIE { + uint8_t type; + uint8_t length; + tSirMacQosInfoStation qosInfo; +} qdf_packed tSirMacQosCapabilityStaIE; + +typedef uint32_t tSirMacTimeStamp[2]; + +typedef uint16_t tSirMacBeaconInterval; + +typedef uint16_t tSirMacListenInterval; + +typedef uint8_t tSirMacChanNum; + +/* IE definitions */ +typedef struct sSirMacSSidIE { + uint8_t type; + tSirMacSSid ssId; +} qdf_packed tSirMacSSidIE; + +typedef struct sSirMacRateSetIE { + uint8_t type; + tSirMacRateSet supportedRateSet; +} qdf_packed tSirMacRateSetIE; + +typedef struct sSirMacDsParamSetIE { + uint8_t type; + uint8_t length; + tSirMacChanNum channelNumber; +} qdf_packed tSirMacDsParamSetIE; + +typedef struct sSirMacCfParamSetIE { + uint8_t type; + uint8_t length; + tSirMacCfParamSet cfParams; +} qdf_packed tSirMacCfParamSetIE; + +typedef struct sSirMacChanInfo { + uint32_t first_freq; + uint8_t numChannels; + int8_t maxTxPower; +} qdf_packed tSirMacChanInfo; + +typedef struct sSirMacNonErpPresentIE { + uint8_t type; + uint8_t length; + uint8_t erp; +} qdf_packed tSirMacNonErpPresentIE; + +typedef struct sSirMacPowerCapabilityIE { + uint8_t type; + uint8_t length; + uint8_t minTxPower; + uint8_t maxTxPower; +} tSirMacPowerCapabilityIE; + +typedef struct sSirMacSupportedChannelIE { + uint8_t type; + uint8_t length; + uint8_t supportedChannels[96]; +} tSirMacSupportedChannelIE; + +typedef struct sSirMacMeasReqField { + uint8_t channelNumber; + uint8_t measStartTime[8]; + uint16_t measDuration; +} tSirMacMeasReqField, *tpSirMacMeasReqField; + +typedef struct sSirMacMeasReqIE { + uint8_t type; + uint8_t length; + uint8_t measToken; + uint8_t measReqMode; + uint8_t measType; + tSirMacMeasReqField measReqField; +} tSirMacMeasReqIE, *tpSirMacMeasReqIE; + +/* VHT Capabilities Info */ +typedef struct sSirMacVHTCapabilityInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint32_t extended_nss_bw_supp:2; + uint32_t txAntPattern:1; + uint32_t rxAntPattern:1; + uint32_t vhtLinkAdaptCap:2; + uint32_t maxAMPDULenExp:3; + uint32_t htcVHTCap:1; + uint32_t vhtTXOPPS:1; + uint32_t muBeamformeeCap:1; + uint32_t muBeamformerCap:1; + uint32_t numSoundingDim:3; + uint32_t csnofBeamformerAntSup:3; + uint32_t suBeamformeeCap:1; + uint32_t suBeamFormerCap:1; + uint32_t rxSTBC:3; + uint32_t txSTBC:1; + uint32_t shortGI160and80plus80MHz:1; + uint32_t shortGI80MHz:1; + uint32_t ldpcCodingCap:1; + uint32_t supportedChannelWidthSet:2; + uint32_t maxMPDULen:2; +#else + uint32_t maxMPDULen:2; + uint32_t supportedChannelWidthSet:2; + uint32_t ldpcCodingCap:1; + uint32_t shortGI80MHz:1; + uint32_t shortGI160and80plus80MHz:1; + uint32_t txSTBC:1; + uint32_t rxSTBC:3; + uint32_t suBeamFormerCap:1; + uint32_t suBeamformeeCap:1; + uint32_t csnofBeamformerAntSup:3; + uint32_t numSoundingDim:3; + uint32_t muBeamformerCap:1; + uint32_t muBeamformeeCap:1; + uint32_t vhtTXOPPS:1; + uint32_t htcVHTCap:1; + uint32_t maxAMPDULenExp:3; + uint32_t vhtLinkAdaptCap:2; + uint32_t rxAntPattern:1; + uint32_t txAntPattern:1; + uint32_t extended_nss_bw_supp:2; +#endif +} qdf_packed tSirMacVHTCapabilityInfo; + +typedef struct sSirMacVHTTxSupDataRateInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint16_t reserved:2; + uint16_t vht_extended_nss_bw_cap:1; + uint16_t txSupDataRate:13; +#else + uint16_t txSupDataRate:13; + uint16_t vht_extended_nss_bw_cap:1; + uint16_t reserved:2; +#endif +} qdf_packed tSirMacVHTTxSupDataRateInfo; + +typedef struct sSirMacVHTRxSupDataRateInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint16_t max_nsts_total:3; + uint16_t rxSupDataRate:13; +#else + uint16_t rxSupDataRate:13; + uint16_t max_nsts_total:3; +#endif +} qdf_packed tSirMacVHTRxSupDataRateInfo; + +/** + * struct sSirVhtMcsInfo - VHT MCS information + * @rx_mcs_map: RX MCS map 2 bits for each stream, total 8 streams + * @rx_highest: Indicates highest long GI VHT PPDU data rate + * STA can receive. Rate expressed in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest RX data rate supported. + * @tx_mcs_map: TX MCS map 2 bits for each stream, total 8 streams + * @tx_highest: Indicates highest long GI VHT PPDU data rate + * STA can transmit. Rate expressed in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest TX data rate supported. + */ +typedef struct sSirVhtMcsInfo { + uint16_t rxMcsMap; + uint16_t rxHighest; + uint16_t txMcsMap; + uint16_t txHighest; +} tSirVhtMcsInfo; + +/** + * struct sSirVHtCap - VHT capabilities + * + * This structure is the "VHT capabilities element" as + * described in 802.11ac D3.0 8.4.2.160 + * @vht_cap_info: VHT capability info + * @supp_mcs: VHT MCS supported rates + */ +typedef struct sSirVHtCap { + uint32_t vhtCapInfo; + tSirVhtMcsInfo suppMcs; +} tSirVHTCap; + +/* */ +/* Determines the current operating mode of the 802.11n STA */ +/* */ + +typedef enum eSirMacHTOperatingMode { + eSIR_HT_OP_MODE_PURE, /* No Protection */ + eSIR_HT_OP_MODE_OVERLAP_LEGACY, /* Overlap Legacy device present, protection is optional */ + eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT, /* No legacy device, but 20 MHz HT present */ + eSIR_HT_OP_MODE_MIXED /* Protetion is required */ +} tSirMacHTOperatingMode; + +/* Spatial Multiplexing(SM) Power Save mode */ +typedef enum eSirMacHTMIMOPowerSaveState { + eSIR_HT_MIMO_PS_STATIC = 0, /* Static SM Power Save mode */ + eSIR_HT_MIMO_PS_DYNAMIC = 1, /* Dynamic SM Power Save mode */ + eSIR_HT_MIMO_PS_NA = 2, /* reserved */ + eSIR_HT_MIMO_PS_NO_LIMIT = 3 /* SM Power Save disabled */ +} tSirMacHTMIMOPowerSaveState; + +typedef enum eSirMacHTChannelWidth { + eHT_CHANNEL_WIDTH_20MHZ = 0, + eHT_CHANNEL_WIDTH_40MHZ = 1, + eHT_CHANNEL_WIDTH_80MHZ = 2, + eHT_CHANNEL_WIDTH_160MHZ = 3, + eHT_CHANNEL_WIDTH_80P80MHZ = 4, + eHT_MAX_CHANNEL_WIDTH +} tSirMacHTChannelWidth; + +typedef enum eSirMacHTChannelType { + eHT_CHAN_NO_HT = 0, + eHT_CHAN_HT20 = 1, + eHT_CHAN_HT40MINUS = 2, + eHT_CHAN_HT40PLUS = 3 +} tSirMacHTChannelType; + +/* Packet struct for HT capability */ +typedef struct sHtCaps { + uint16_t advCodingCap:1; + uint16_t supportedChannelWidthSet:1; + uint16_t mimoPowerSave:2; + uint16_t greenField:1; + uint16_t shortGI20MHz:1; + uint16_t shortGI40MHz:1; + uint16_t txSTBC:1; + uint16_t rxSTBC:2; + uint16_t delayedBA:1; + uint16_t maximalAMSDUsize:1; + uint16_t dsssCckMode40MHz:1; + uint16_t psmp:1; + uint16_t stbcControlFrame:1; + uint16_t lsigTXOPProtection:1; + uint8_t maxRxAMPDUFactor:2; + uint8_t mpduDensity:3; + uint8_t reserved1:3; + uint8_t supportedMCSSet[16]; + uint16_t pco:1; + uint16_t transitionTime:2; + uint16_t reserved2:5; + uint16_t mcsFeedback:2; + uint16_t reserved3:6; + uint32_t txBF:1; + uint32_t rxStaggeredSounding:1; + uint32_t txStaggeredSounding:1; + uint32_t rxZLF:1; + uint32_t txZLF:1; + uint32_t implicitTxBF:1; + uint32_t calibration:2; + uint32_t explicitCSITxBF:1; + uint32_t explicitUncompressedSteeringMatrix:1; + uint32_t explicitBFCSIFeedback:3; + uint32_t explicitUncompressedSteeringMatrixFeedback:3; + uint32_t explicitCompressedSteeringMatrixFeedback:3; + uint32_t csiNumBFAntennae:2; + uint32_t uncompressedSteeringMatrixBFAntennae:2; + uint32_t compressedSteeringMatrixBFAntennae:2; + uint32_t reserved4:7; + uint8_t antennaSelection:1; + uint8_t explicitCSIFeedbackTx:1; + uint8_t antennaIndicesFeedbackTx:1; + uint8_t explicitCSIFeedback:1; + uint8_t antennaIndicesFeedback:1; + uint8_t rxAS:1; + uint8_t txSoundingPPDUs:1; + uint8_t reserved5:1; + +} qdf_packed tHtCaps; + +/* Supported MCS set */ +#define SIZE_OF_SUPPORTED_MCS_SET 16 +#define SIZE_OF_BASIC_MCS_SET 16 +#define VALID_MCS_SIZE 77 /* 0-76 */ +#define MCS_RX_HIGHEST_SUPPORTED_RATE_BYTE_OFFSET 10 +#define VALID_MAX_MCS_INDEX 8 + +/* */ +/* The following enums will be used to get the "current" HT Capabilities of */ +/* the local STA in a generic fashion. In other words, the following enums */ +/* identify the HT capabilities that can be queried or set. */ +/* */ +typedef enum eHTCapability { + eHT_LSIG_TXOP_PROTECTION, + eHT_STBC_CONTROL_FRAME, + eHT_PSMP, + eHT_DSSS_CCK_MODE_40MHZ, + eHT_MAX_AMSDU_LENGTH, + eHT_MAX_AMSDU_NUM, + eHT_RX_STBC, + eHT_TX_STBC, + eHT_SHORT_GI_40MHZ, + eHT_SHORT_GI_20MHZ, + eHT_GREENFIELD, + eHT_MIMO_POWER_SAVE, + eHT_SUPPORTED_CHANNEL_WIDTH_SET, + eHT_ADVANCED_CODING, + eHT_MAX_RX_AMPDU_FACTOR, + eHT_MPDU_DENSITY, + eHT_PCO, + eHT_TRANSITION_TIME, + eHT_MCS_FEEDBACK, + eHT_TX_BEAMFORMING, + eHT_ANTENNA_SELECTION, + /* The following come under Additional HT Capabilities */ + eHT_SI_GRANULARITY, + eHT_CONTROLLED_ACCESS, + eHT_RIFS_MODE, + eHT_RECOMMENDED_TX_WIDTH_SET, + eHT_EXTENSION_CHANNEL_OFFSET, + eHT_OP_MODE, + eHT_BASIC_STBC_MCS, + eHT_DUAL_CTS_PROTECTION, + eHT_LSIG_TXOP_PROTECTION_FULL_SUPPORT, + eHT_PCO_ACTIVE, + eHT_PCO_PHASE +} tHTCapability; + +/* HT Parameters Info */ +typedef struct sSirMacHTParametersInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint8_t reserved:3; + uint8_t mpduDensity:3; /* Dynamic state */ + uint8_t maxRxAMPDUFactor:2; /* Dynamic state */ +#else + uint8_t maxRxAMPDUFactor:2; + uint8_t mpduDensity:3; + uint8_t reserved:3; +#endif +} qdf_packed tSirMacHTParametersInfo; + +/* Extended HT Capabilities Info */ +typedef struct sSirMacExtendedHTCapabilityInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint16_t reserved2:6; + uint16_t mcsFeedback:2; /* Static via CFG */ + uint16_t reserved1:5; + uint16_t transitionTime:2; /* Static via CFG */ + uint16_t pco:1; /* Static via CFG */ +#else + uint16_t pco:1; + uint16_t transitionTime:2; + uint16_t reserved1:5; + uint16_t mcsFeedback:2; + uint16_t reserved2:6; +#endif +} qdf_packed tSirMacExtendedHTCapabilityInfo; + +/* IEEE 802.11n/D7.0 - 7.3.2.57.4 */ +/* Part of the "supported MCS set field" */ +typedef struct sSirMacRxHighestSupportRate { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint16_t reserved:6; + uint16_t rate:10; +#else + uint16_t rate:10; + uint16_t reserved:6; +#endif +} qdf_packed tSirMacRxHighestSupportRate, *tpSirMacRxHighestSupportRate; + +/* Transmit Beam Forming Capabilities Info */ +typedef struct sSirMacTxBFCapabilityInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint32_t reserved:7; + uint32_t compressedSteeringMatrixBFAntennae:2; /* Static via CFG */ + /* Static via CFG */ + uint32_t uncompressedSteeringMatrixBFAntennae:2; + uint32_t csiNumBFAntennae:2; /* Static via CFG */ + /* Static via CFG */ + uint32_t explicitCompressedSteeringMatrixFeedback:3; + /* Static via CFG */ + uint32_t explicitUncompressedSteeringMatrixFeedback:3; + uint32_t explicitBFCSIFeedback:3; /* Static via CFG */ + uint32_t explicitUncompressedSteeringMatrix:1; /* Static via CFG */ + uint32_t explicitCSITxBF:1; /* Static via CFG */ + uint32_t calibration:2; /* Static via CFG */ + uint32_t implicitTxBF:1; /* Static via CFG */ + uint32_t txZLF:1; /* Static via CFG */ + uint32_t rxZLF:1; /* Static via CFG */ + uint32_t txStaggeredSounding:1; /* Static via CFG */ + uint32_t rxStaggeredSounding:1; /* Static via CFG */ + uint32_t txBF:1; /* Static via CFG */ +#else + uint32_t txBF:1; + uint32_t rxStaggeredSounding:1; + uint32_t txStaggeredSounding:1; + uint32_t rxZLF:1; + uint32_t txZLF:1; + uint32_t implicitTxBF:1; + uint32_t calibration:2; + uint32_t explicitCSITxBF:1; + uint32_t explicitUncompressedSteeringMatrix:1; + uint32_t explicitBFCSIFeedback:3; + uint32_t explicitUncompressedSteeringMatrixFeedback:3; + uint32_t explicitCompressedSteeringMatrixFeedback:3; + uint32_t csiNumBFAntennae:2; + uint32_t uncompressedSteeringMatrixBFAntennae:2; + uint32_t compressedSteeringMatrixBFAntennae:2; + uint32_t reserved:7; +#endif +} qdf_packed tSirMacTxBFCapabilityInfo; + +/* Antenna Selection Capability Info */ +typedef struct sSirMacASCapabilityInfo { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint8_t reserved2:1; + uint8_t txSoundingPPDUs:1; /* Static via CFG */ + uint8_t rxAS:1; /* Static via CFG */ + uint8_t antennaIndicesFeedback:1; /* Static via CFG */ + uint8_t explicitCSIFeedback:1; /* Static via CFG */ + uint8_t antennaIndicesFeedbackTx:1; /* Static via CFG */ + uint8_t explicitCSIFeedbackTx:1; /* Static via CFG */ + uint8_t antennaSelection:1; /* Static via CFG */ +#else + uint8_t antennaSelection:1; + uint8_t explicitCSIFeedbackTx:1; + uint8_t antennaIndicesFeedbackTx:1; + uint8_t explicitCSIFeedback:1; + uint8_t antennaIndicesFeedback:1; + uint8_t rxAS:1; + uint8_t txSoundingPPDUs:1; + uint8_t reserved2:1; +#endif +} qdf_packed tSirMacASCapabilityInfo; + +typedef struct sSirMacAuthFrameBody { + uint16_t authAlgoNumber; + uint16_t authTransactionSeqNumber; + uint16_t authStatusCode; + uint8_t type; /* = WLAN_ELEMID_CHALLENGE */ + uint8_t length; /* = SIR_MAC_AUTH_CHALLENGE_LENGTH */ + uint8_t challengeText[SIR_MAC_AUTH_CHALLENGE_LENGTH]; +#ifdef WLAN_FEATURE_FILS_SK + tSirMacRsnInfo rsn_ie; + struct mac_ft_ie ft_ie; + uint8_t assoc_delay_info; + uint8_t session[SIR_FILS_SESSION_LENGTH]; + uint8_t wrapped_data_len; + uint8_t wrapped_data[SIR_FILS_WRAPPED_DATA_MAX_SIZE]; + uint8_t nonce[SIR_FILS_NONCE_LENGTH]; +#endif +} qdf_packed tSirMacAuthFrameBody, *tpSirMacAuthFrameBody; + +/* / Common header for all action frames */ +typedef struct sSirMacActionFrameHdr { + uint8_t category; + uint8_t actionID; +} qdf_packed tSirMacActionFrameHdr, *tpSirMacActionFrameHdr; + +typedef struct sSirMacVendorSpecificFrameHdr { + uint8_t category; + uint8_t Oui[4]; +} qdf_packed tSirMacVendorSpecificFrameHdr, +*tpSirMacVendorSpecificFrameHdr; + +typedef struct sSirMacVendorSpecificPublicActionFrameHdr { + uint8_t category; + uint8_t actionID; + uint8_t Oui[4]; + uint8_t OuiSubType; + uint8_t dialogToken; +} qdf_packed tSirMacVendorSpecificPublicActionFrameHdr, +*tpSirMacVendorSpecificPublicActionFrameHdr; + +typedef struct sSirMacMeasActionFrameHdr { + uint8_t category; + uint8_t actionID; + uint8_t dialogToken; +} tSirMacMeasActionFrameHdr, *tpSirMacMeasActionFrameHdr; + +#ifdef ANI_SUPPORT_11H +typedef struct sSirMacTpcReqActionFrame { + tSirMacMeasActionFrameHdr actionHeader; + uint8_t type; + uint8_t length; +} tSirMacTpcReqActionFrame, *tpSirMacTpcReqActionFrame; +typedef struct sSirMacMeasReqActionFrame { + tSirMacMeasActionFrameHdr actionHeader; + tSirMacMeasReqIE measReqIE; +} tSirMacMeasReqActionFrame, *tpSirMacMeasReqActionFrame; +#endif + +typedef struct sSirMacNeighborReportReq { + uint8_t dialogToken; + uint8_t ssid_present; + tSirMacSSid ssid; +} tSirMacNeighborReportReq, *tpSirMacNeighborReportReq; + +typedef struct sSirMacLinkReport { + uint8_t dialogToken; + uint8_t txPower; + uint8_t rxAntenna; + uint8_t txAntenna; + uint8_t rcpi; + uint8_t rsni; +} tSirMacLinkReport, *tpSirMacLinkReport; + +#define BEACON_REPORT_MAX_IES 224 /* Refer IEEE 802.11k-2008, Table 7-31d */ +/* Max number of beacon reports per channel supported in the driver */ +#define MAX_BEACON_REPORTS 8 +/* Offset of IEs after Fixed Fields in Beacon Frame */ +#define BEACON_FRAME_IES_OFFSET 12 + +/** + * struct bcn_report_frame_body_frag_id - beacon report reported frame body + * fragment ID sub element params + * @id: report ID + * @frag_id: fragment ID + * @more_frags: more frags present or not present + */ +struct bcn_report_frame_body_frag_id { + uint8_t id; + uint8_t frag_id; + bool more_frags; +}; + +/** + * struct sSirMacBeaconReport - Beacon Report Structure + * @regClass: Regulatory Class + * @channel: Channel for which the current report is being sent + * @measStartTime: RRM scan start time for this report + * @measDuration: Scan duration for the current channel + * @phyType: Condensed Phy Type + * @bcnProbeRsp: Beacon or probe response being reported + * @rsni: Received signal-to-noise indication + * @rcpi: Received Channel Power indication + * @bssid: BSSID of the AP requesting the beacon report + * @antennaId: Number of Antennas used for measurement + * @parentTSF: measuring STA's TSF timer value + * @numIes: Number of IEs included in the beacon frames + * @last_bcn_report_ind_support: Support for Last beacon report indication + * @is_last_bcn_report: Is the current report last or more reports present + * @frame_body_frag_id: Reported Frame Body Frag Id sub-element params + * @Ies: IEs included in the beacon report + */ +typedef struct sSirMacBeaconReport { + uint8_t regClass; + uint8_t channel; + uint8_t measStartTime[8]; + uint8_t measDuration; + uint8_t phyType; + uint8_t bcnProbeRsp; + uint8_t rsni; + uint8_t rcpi; + tSirMacAddr bssid; + uint8_t antennaId; + uint32_t parentTSF; + uint8_t numIes; + uint8_t last_bcn_report_ind_support; + uint8_t is_last_bcn_report; + struct bcn_report_frame_body_frag_id frame_body_frag_id; + uint8_t Ies[BEACON_REPORT_MAX_IES]; + +} tSirMacBeaconReport, *tpSirMacBeaconReport; + +#define RADIO_REPORTS_MAX_IN_A_FRAME 4 +typedef struct sSirMacRadioMeasureReport { + uint8_t token; + uint8_t refused; + uint8_t incapable; + uint8_t type; + union { + tSirMacBeaconReport beaconReport; + } report; + +} tSirMacRadioMeasureReport, *tpSirMacRadioMeasureReport; + +#ifdef WLAN_FEATURE_11AX +struct he_cap_network_endian { + uint32_t htc_he:1; + uint32_t twt_request:1; + uint32_t twt_responder:1; + uint32_t fragmentation:2; + uint32_t max_num_frag_msdu_amsdu_exp:3; + uint32_t min_frag_size:2; + uint32_t trigger_frm_mac_pad:2; + uint32_t multi_tid_aggr_rx_supp:3; + uint32_t he_link_adaptation:2; + uint32_t all_ack:1; + uint32_t trigd_rsp_sched:1; + uint32_t a_bsr:1; + uint32_t broadcast_twt:1; + uint32_t ba_32bit_bitmap:1; + uint32_t mu_cascade:1; + uint32_t ack_enabled_multitid:1; + uint32_t reserved:1; + uint32_t omi_a_ctrl:1; + uint32_t ofdma_ra:1; + uint32_t max_ampdu_len_exp_ext:2; + uint32_t amsdu_frag:1; + uint32_t flex_twt_sched:1; + uint32_t rx_ctrl_frame:1; + + uint16_t bsrp_ampdu_aggr:1; + uint16_t qtp:1; + uint16_t a_bqr:1; + uint16_t spatial_reuse_param_rspder:1; + uint16_t ndp_feedback_supp:1; + uint16_t ops_supp:1; + uint16_t amsdu_in_ampdu:1; + uint16_t multi_tid_aggr_tx_supp:3; + uint16_t he_sub_ch_sel_tx_supp:1; + uint16_t ul_2x996_tone_ru_supp:1; + uint16_t om_ctrl_ul_mu_data_dis_rx:1; + uint16_t he_dynamic_smps:1; + uint16_t punctured_sounding_supp:1; + uint16_t ht_vht_trg_frm_rx_supp:1; + + uint32_t reserved2:1; + uint32_t chan_width:7; + uint32_t rx_pream_puncturing:4; + uint32_t device_class:1; + uint32_t ldpc_coding:1; + uint32_t he_1x_ltf_800_gi_ppdu:1; + uint32_t midamble_tx_rx_max_nsts:2; + uint32_t he_4x_ltf_3200_gi_ndp:1; + uint32_t tb_ppdu_tx_stbc_lt_80mhz:1; + uint32_t rx_stbc_lt_80mhz:1; + uint32_t doppler:2; + uint32_t ul_mu:2; + uint32_t dcm_enc_tx:3; + uint32_t dcm_enc_rx:3; + uint32_t ul_he_mu:1; + uint32_t su_beamformer:1; + + uint32_t su_beamformee:1; + uint32_t mu_beamformer:1; + uint32_t bfee_sts_lt_80:3; + uint32_t bfee_sts_gt_80:3; + uint32_t num_sounding_lt_80:3; + uint32_t num_sounding_gt_80:3; + uint32_t su_feedback_tone16:1; + uint32_t mu_feedback_tone16:1; + uint32_t codebook_su:1; + uint32_t codebook_mu:1; + uint32_t beamforming_feedback:3; + uint32_t he_er_su_ppdu:1; + uint32_t dl_mu_mimo_part_bw:1; + uint32_t ppet_present:1; + uint32_t srp:1; + uint32_t power_boost:1; + uint32_t he_ltf_800_gi_4x:1; + uint32_t max_nc:3; + uint32_t tb_ppdu_tx_stbc_gt_80mhz:1; + uint32_t rx_stbc_gt_80mhz:1; + + uint16_t er_he_ltf_800_gi_4x:1; + uint16_t he_ppdu_20_in_40Mhz_2G:1; + uint16_t he_ppdu_20_in_160_80p80Mhz:1; + uint16_t he_ppdu_80_in_160_80p80Mhz:1; + uint16_t er_1x_he_ltf_gi:1; + uint16_t midamble_tx_rx_1x_he_ltf:1; + uint16_t dcm_max_bw:2; + uint16_t longer_than_16_he_sigb_ofdm_sym:1; + uint16_t non_trig_cqi_feedback:1; + uint16_t tx_1024_qam_lt_242_tone_ru:1; + uint16_t rx_1024_qam_lt_242_tone_ru:1; + uint16_t rx_full_bw_su_he_mu_compress_sigb:1; + uint16_t rx_full_bw_su_he_mu_non_cmpr_sigb:1; + uint16_t reserved3:2; + + uint8_t reserved4; + + uint16_t rx_he_mcs_map_lt_80; + uint16_t tx_he_mcs_map_lt_80; + uint16_t rx_he_mcs_map_160; + uint16_t tx_he_mcs_map_160; + uint16_t rx_he_mcs_map_80_80; + uint16_t tx_he_mcs_map_80_80; +} qdf_packed; + +struct he_ops_network_endian { + uint16_t default_pe:3; + uint16_t twt_required:1; + uint16_t txop_rts_threshold:10; + uint16_t vht_oper_present:1; + uint16_t co_located_bss:1; + uint8_t er_su_disable:1; + uint8_t reserved1:7; + uint8_t bss_color:6; + uint8_t partial_bss_col:1; + uint8_t bss_col_disabled:1; + uint8_t basic_mcs_nss[2]; + union { + struct { + uint8_t chan_width; + uint8_t center_freq_seg0; + uint8_t center_freq_seg1; + } info; /* vht_oper_present = 1 */ + } vht_oper; + union { + struct { + uint8_t data; + } info; /* co_located_bss = 1 */ + } maxbssid_ind; +} qdf_packed; + +/* HE Capabilities Info */ +struct he_capability_info { +#ifndef ANI_LITTLE_BIT_ENDIAN + uint32_t rx_ctrl_frame:1; + uint32_t flex_twt_sched:1; + uint32_t amsdu_frag:1; + uint32_t max_ampdu_len_exp_ext:2; + uint32_t ofdma_ra:1; + uint32_t omi_a_ctrl:1; + uint32_t reserved:1; + uint32_t ack_enabled_multitid:1; + uint32_t mu_cascade:1; + uint32_t ba_32bit_bitmap:1; + uint32_t broadcast_twt:1; + uint32_t a_bsr:1; + uint32_t trigd_rsp_sched:1; + uint32_t all_ack:1; + uint32_t he_link_adaptation:2; + uint32_t multi_tid_aggr_rx_supp:3; + uint32_t trigger_frm_mac_pad:2; + uint32_t min_frag_size:2; + uint32_t max_num_frag_msdu_amsdu_exp:3; + uint32_t fragmentation:2; + uint32_t twt_responder:1; + uint32_t twt_request:1; + uint32_t htc_he:1; + + uint16_t ht_vht_trg_frm_rx_supp:1; + uint16_t punctured_sounding_supp:1; + uint16_t he_dynamic_smps:1; + uint16_t om_ctrl_ul_mu_data_dis_rx:1; + uint16_t ul_2x996_tone_ru_supp:1; + uint16_t he_sub_ch_sel_tx_supp:1; + uint16_t multi_tid_aggr_tx_supp:3; + uint16_t amsdu_in_ampdu:1; + uint16_t ops_supp:1; + uint16_t ndp_feedback_supp:1; + uint16_t spatial_reuse_param_rspder:1; + uint16_t a_bqr:1; + uint16_t qtp:1; + uint16_t bsrp_ampdu_aggr:1; + + uint32_t su_beamformer:1; + uint32_t ul_he_mu:1; + uint32_t dcm_enc_rx:3; + uint32_t dcm_enc_tx:3; + uint32_t ul_mu:2; + uint32_t doppler:2; + uint32_t rx_stbc_lt_80mhz:1; + uint32_t tb_ppdu_tx_stbc_lt_80mhz:1; + uint32_t he_4x_ltf_3200_gi_ndp:1; + uint32_t midamble_tx_rx_max_nsts:2; + uint32_t he_1x_ltf_800_gi_ppdu:1; + uint32_t ldpc_coding:1; + uint32_t device_class:1; + uint32_t rx_pream_puncturing:4; + uint32_t chan_width:7; + uint32_t reserved2:1; + + uint32_t rx_stbc_gt_80mhz:1; + uint32_t tb_ppdu_tx_stbc_gt_80mhz:1; + uint32_t max_nc:3; + uint32_t he_ltf_800_gi_4x:1; + uint32_t power_boost:1; + uint32_t srp:1; + uint32_t ppet_present:1; + uint32_t dl_mu_mimo_part_bw:1; + uint32_t he_er_su_ppdu:1; + uint32_t beamforming_feedback:3; + uint32_t codebook_mu:1; + uint32_t codebook_su:1; + uint32_t mu_feedback_tone16:1; + uint32_t su_feedback_tone16:1; + uint32_t num_sounding_gt_80:3; + uint32_t num_sounding_lt_80:3; + uint32_t bfee_sts_gt_80:3; + uint32_t bfee_sts_lt_80:3; + uint32_t mu_beamformer:1; + uint32_t su_beamformee:1; + + uint16_t reserved3:2; + uint16_t rx_full_bw_su_he_mu_non_cmpr_sigb:1; + uint16_t rx_full_bw_su_he_mu_compress_sigb:1; + uint16_t rx_1024_qam_lt_242_tone_ru:1; + uint16_t tx_1024_qam_lt_242_tone_ru:1; + uint16_t non_trig_cqi_feedback:1; + uint16_t longer_than_16_he_sigb_ofdm_sym:1; + uint16_t dcm_max_bw:2; + uint16_t midamble_tx_rx_1x_he_ltf:1; + uint16_t er_1x_he_ltf_gi:1; + uint16_t he_ppdu_80_in_160_80p80Mhz:1; + uint16_t he_ppdu_20_in_160_80p80Mhz:1; + uint16_t he_ppdu_20_in_40Mhz_2G:1; + uint16_t er_he_ltf_800_gi_4x:1; + + uint8_t reserved4; + + uint16_t tx_he_mcs_map_80_80; + uint16_t rx_he_mcs_map_80_80; + uint16_t tx_he_mcs_map_160; + uint16_t rx_he_mcs_map_160; + uint16_t tx_he_mcs_map_lt_80; + uint16_t rx_he_mcs_map_lt_80; +#else + uint32_t htc_he:1; + uint32_t twt_request:1; + uint32_t twt_responder:1; + uint32_t fragmentation:2; + uint32_t max_num_frag_msdu_amsdu_exp:3; + uint32_t min_frag_size:2; + uint32_t trigger_frm_mac_pad:2; + uint32_t multi_tid_aggr_rx_supp:3; + uint32_t he_link_adaptation:2; + uint32_t all_ack:1; + uint32_t trigd_rsp_sched:1; + uint32_t a_bsr:1; + uint32_t broadcast_twt:1; + uint32_t ba_32bit_bitmap:1; + uint32_t mu_cascade:1; + uint32_t ack_enabled_multitid:1; + uint32_t reserved:1; + uint32_t omi_a_ctrl:1; + uint32_t ofdma_ra:1; + uint32_t max_ampdu_len_exp_ext:2; + uint32_t amsdu_frag:1; + uint32_t flex_twt_sched:1; + uint32_t rx_ctrl_frame:1; + + uint16_t bsrp_ampdu_aggr:1; + uint16_t qtp:1; + uint16_t a_bqr:1; + uint16_t spatial_reuse_param_rspder:1; + uint16_t ndp_feedback_supp:1; + uint16_t ops_supp:1; + uint16_t amsdu_in_ampdu:1; + uint16_t multi_tid_aggr_tx_supp:3; + uint16_t he_sub_ch_sel_tx_supp:1; + uint16_t ul_2x996_tone_ru_supp:1; + uint16_t om_ctrl_ul_mu_data_dis_rx:1; + uint16_t he_dynamic_smps:1; + uint16_t punctured_sounding_supp:1; + uint16_t ht_vht_trg_frm_rx_supp:1; + + uint32_t reserved2:1; + uint32_t chan_width:7; + uint32_t rx_pream_puncturing:4; + uint32_t device_class:1; + uint32_t ldpc_coding:1; + uint32_t he_1x_ltf_800_gi_ppdu:1; + uint32_t midamble_tx_rx_max_nsts:2; + uint32_t he_4x_ltf_3200_gi_ndp:1; + uint32_t tb_ppdu_tx_stbc_lt_80mhz:1; + uint32_t rx_stbc_lt_80mhz:1; + uint32_t doppler:2; + uint32_t ul_mu:2; + uint32_t dcm_enc_tx:3; + uint32_t dcm_enc_rx:3; + uint32_t ul_he_mu:1; + uint32_t su_beamformer:1; + + uint32_t su_beamformee:1; + uint32_t mu_beamformer:1; + uint32_t bfee_sts_lt_80:3; + uint32_t bfee_sts_gt_80:3; + uint32_t num_sounding_lt_80:3; + uint32_t num_sounding_gt_80:3; + uint32_t su_feedback_tone16:1; + uint32_t mu_feedback_tone16:1; + uint32_t codebook_su:1; + uint32_t codebook_mu:1; + uint32_t beamforming_feedback:3; + uint32_t he_er_su_ppdu:1; + uint32_t dl_mu_mimo_part_bw:1; + uint32_t ppet_present:1; + uint32_t srp:1; + uint32_t power_boost:1; + uint32_t he_ltf_800_gi_4x:1; + uint32_t max_nc:3; + uint32_t tb_ppdu_tx_stbc_gt_80mhz:1; + uint32_t rx_stbc_gt_80mhz:1; + + uint16_t er_he_ltf_800_gi_4x:1; + uint16_t he_ppdu_20_in_40Mhz_2G:1; + uint16_t he_ppdu_20_in_160_80p80Mhz:1; + uint16_t he_ppdu_80_in_160_80p80Mhz:1; + uint16_t er_1x_he_ltf_gi:1; + uint16_t midamble_tx_rx_1x_he_ltf:1; + uint16_t dcm_max_bw:2; + uint16_t longer_than_16_he_sigb_ofdm_sym:1; + uint16_t non_trig_cqi_feedback:1; + uint16_t tx_1024_qam_lt_242_tone_ru:1; + uint16_t rx_1024_qam_lt_242_tone_ru:1; + uint16_t rx_full_bw_su_he_mu_compress_sigb:1; + uint16_t rx_full_bw_su_he_mu_non_cmpr_sigb:1; + uint16_t reserved3:2; + + uint8_t reserved4; + + uint16_t rx_he_mcs_map_lt_80; + uint16_t tx_he_mcs_map_lt_80; + uint16_t rx_he_mcs_map_160; + uint16_t tx_he_mcs_map_160; + uint16_t rx_he_mcs_map_80_80; + uint16_t tx_he_mcs_map_80_80; +#endif +} qdf_packed; +#endif + +/* + * frame parser does not include optional 160 and 80+80 mcs set for MIN IE len + */ +#define SIR_MAC_HE_CAP_MIN_LEN (DOT11F_IE_HE_CAP_MIN_LEN) +#define HE_CAP_160M_MCS_MAP_LEN 4 +#define HE_CAP_80P80_MCS_MAP_LEN 4 +#define HE_CAP_OUI_LEN 3 + +/* QOS action frame definitions */ + +/* max number of possible tclas elements in any frame */ +#define SIR_MAC_TCLASIE_MAXNUM 2 + +/* 11b rate encoding in MAC format */ + +#define SIR_MAC_RATE_1 0x02 +#define SIR_MAC_RATE_2 0x04 +#define SIR_MAC_RATE_5_5 0x0B +#define SIR_MAC_RATE_11 0x16 + +/* 11a/g rate encoding in MAC format */ + +#define SIR_MAC_RATE_6 0x0C +#define SIR_MAC_RATE_9 0x12 +#define SIR_MAC_RATE_12 0x18 +#define SIR_MAC_RATE_18 0x24 +#define SIR_MAC_RATE_24 0x30 +#define SIR_MAC_RATE_36 0x48 +#define SIR_MAC_RATE_48 0x60 +#define SIR_MAC_RATE_54 0x6C + +/* ANI legacy supported rates */ +#define SIR_MAC_RATE_72 0x01 +#define SIR_MAC_RATE_96 0x03 +#define SIR_MAC_RATE_108 0x05 + +/* ANI enhanced rates */ +#define SIR_MAC_RATE_42 1000 +#define SIR_MAC_RATE_84 1001 +#define SIR_MAC_RATE_126 1002 +#define SIR_MAC_RATE_144 1003 +#define SIR_MAC_RATE_168 1004 +#define SIR_MAC_RATE_192 1005 +#define SIR_MAC_RATE_216 1006 +#define SIR_MAC_RATE_240 1007 + +#define SIR_MAC_RATE_1_BITMAP (1<<0) +#define SIR_MAC_RATE_2_BITMAP (1<<1) +#define SIR_MAC_RATE_5_5_BITMAP (1<<2) +#define SIR_MAC_RATE_11_BITMAP (1<<3) +#define SIR_MAC_RATE_6_BITMAP (1<<4) +#define SIR_MAC_RATE_9_BITMAP (1<<5) +#define SIR_MAC_RATE_12_BITMAP (1<<6) +#define SIR_MAC_RATE_18_BITMAP (1<<7) +#define SIR_MAC_RATE_24_BITMAP (1<<8) +#define SIR_MAC_RATE_36_BITMAP (1<<9) +#define SIR_MAC_RATE_48_BITMAP (1<<10) +#define SIR_MAC_RATE_54_BITMAP (1<<11) + +#define sirIsArate(x) ((((uint8_t)x) == SIR_MAC_RATE_6) || \ + (((uint8_t)x) == SIR_MAC_RATE_9) || \ + (((uint8_t)x) == SIR_MAC_RATE_12) || \ + (((uint8_t)x) == SIR_MAC_RATE_18) || \ + (((uint8_t)x) == SIR_MAC_RATE_24) || \ + (((uint8_t)x) == SIR_MAC_RATE_36) || \ + (((uint8_t)x) == SIR_MAC_RATE_48) || \ + (((uint8_t)x) == SIR_MAC_RATE_54)) + +#define sirIsBrate(x) ((((uint8_t)x) == SIR_MAC_RATE_1) || \ + (((uint8_t)x) == SIR_MAC_RATE_2) || \ + (((uint8_t)x) == SIR_MAC_RATE_5_5) || \ + (((uint8_t)x) == SIR_MAC_RATE_11)) + +#define sirIsGrate(x) ((((uint8_t)x) == SIR_MAC_RATE_1) || \ + (((uint8_t)x) == SIR_MAC_RATE_2) || \ + (((uint8_t)x) == SIR_MAC_RATE_5_5) || \ + (((uint8_t)x) == SIR_MAC_RATE_11) || \ + (((uint8_t)x) == SIR_MAC_RATE_6) || \ + (((uint8_t)x) == SIR_MAC_RATE_9) || \ + (((uint8_t)x) == SIR_MAC_RATE_12) || \ + (((uint8_t)x) == SIR_MAC_RATE_18) || \ + (((uint8_t)x) == SIR_MAC_RATE_24) || \ + (((uint8_t)x) == SIR_MAC_RATE_36) || \ + (((uint8_t)x) == SIR_MAC_RATE_48) || \ + (((uint8_t)x) == SIR_MAC_RATE_54)) + +#define SIR_MAC_MIN_IE_LEN 2 /* Minimum IE length for IE validation */ + +#define SIR_MAC_TI_TYPE_ASSOC_COMEBACK 3 + +#define SIR_MAC_VHT_CAP_MAX_MPDU_LEN 0 +#define SIR_MAC_VHT_CAP_SUPP_CH_WIDTH_SET 2 +#define SIR_MAC_VHT_CAP_LDPC_CODING_CAP 4 +#define SIR_MAC_VHT_CAP_SHORTGI_80MHZ 5 +#define SIR_MAC_VHT_CAP_SHORTGI_160_80_80MHZ 6 +#define SIR_MAC_VHT_CAP_TXSTBC 7 +#define SIR_MAC_VHT_CAP_RXSTBC 8 +#define SIR_MAC_VHT_CAP_SU_BEAMFORMER_CAP 11 +#define SIR_MAC_VHT_CAP_SU_BEAMFORMEE_CAP 12 +#define SIR_MAC_VHT_CAP_CSN_BEAMORMER_ANT_SUP 13 +#define SIR_MAC_VHT_CAP_NUM_SOUNDING_DIM 16 +#define SIR_MAC_VHT_CAP_NUM_BEAM_FORMER_CAP 19 +#define SIR_MAC_VHT_CAP_NUM_BEAM_FORMEE_CAP 20 +#define SIR_MAC_VHT_CAP_TXOPPS 21 +#define SIR_MAC_VHT_CAP_HTC_CAP 22 +#define SIR_MAC_VHT_CAP_MAX_AMDU_LEN_EXPO 23 +#define SIR_MAC_VHT_CAP_LINK_ADAPT_CAP 26 +#define SIR_MAC_VHT_CAP_RX_ANTENNA_PATTERN 28 +#define SIR_MAC_VHT_CAP_TX_ANTENNA_PATTERN 29 +#define SIR_MAC_VHT_CAP_EXTD_NSS_BW 30 + +#define SIR_MAC_HT_CAP_ADVCODING_S 0 +#define SIR_MAC_HT_CAP_CHWIDTH40_S 1 +#define SIR_MAC_HT_CAP_SMPOWERSAVE_DYNAMIC_S 2 +#define SIR_MAC_HT_CAP_SM_RESERVED_S 3 +#define SIR_MAC_HT_CAP_GREENFIELD_S 4 +#define SIR_MAC_HT_CAP_SHORTGI20MHZ_S 5 +#define SIR_MAC_HT_CAP_SHORTGI40MHZ_S 6 +#define SIR_MAC_HT_CAP_TXSTBC_S 7 +#define SIR_MAC_HT_CAP_RXSTBC_S 8 +#define SIR_MAC_HT_CAP_DELAYEDBLKACK_S 10 +#define SIR_MAC_HT_CAP_MAXAMSDUSIZE_S 11 +#define SIR_MAC_HT_CAP_DSSSCCK40_S 12 +#define SIR_MAC_HT_CAP_PSMP_S 13 +#define SIR_MAC_HT_CAP_INTOLERANT40_S 14 +#define SIR_MAC_HT_CAP_LSIGTXOPPROT_S 15 + +#define SIR_MAC_TXSTBC 1 +#define SIR_MAC_RXSTBC 1 + +#endif /* __MAC_PROT_DEFS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/sir_types.h b/drivers/staging/qcacld-3.0/core/mac/inc/sir_types.h new file mode 100644 index 0000000000000000000000000000000000000000..eec1a9b114c4c856a92469b7e0b4ad187738f5f3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/sir_types.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011-2016,2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file sir_types.h contains the common types + * + * Author: V. K. Kandarpa + * Date: 04/12/2002 + */ + +#ifndef __SIR_TYPES_H +#define __SIR_TYPES_H + +#include +#include + +/** + * typedef mac_handle_t - MAC Handle + * + * Handle to the MAC. The MAC handle is returned to the HDD from the + * UMAC on Open. The MAC handle is an input to all UMAC function + * calls and represents an opaque handle to the UMAC instance that is + * tied to the HDD instance + * + * The UMAC must be able to derive it's internal instance structure + * pointer through this handle. + */ +/* + * NOTE WELL: struct opaque_mac_handle is not defined anywhere. This + * reference is used to help ensure that a mac_handle_t is never used + * where a different handle type is expected + */ +struct opaque_mac_handle; +typedef struct opaque_mac_handle *mac_handle_t; + +/** + * typedef hdd_handle_t - HDD Handle + * + * Handle to the HDD. The HDD handle is given to the UMAC from the + * HDD on Open. The HDD handle is an input to all HDD/PAL function + * calls and represents an opaque handle to the HDD instance that is + * tied to the UMAC instance + * + * The HDD must be able to derive it's internal instance structure + * pointer through this handle. + */ +/* + * NOTE WELL: struct opaque_hdd_handle is not defined anywhere. This + * reference is used to help ensure that a hdd_handle_t is never used + * where a different handle type is expected + */ +struct opaque_hdd_handle; +typedef struct opaque_hdd_handle *hdd_handle_t; + +#define HAL_NUM_ASSOC_STA 32 +#define HAL_NUM_STA 41 + +#endif /* __SIR_TYPES_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/wlan_tgt_def_config.h b/drivers/staging/qcacld-3.0/core/mac/inc/wlan_tgt_def_config.h new file mode 100644 index 0000000000000000000000000000000000000000..2dc52fc0bfa6286e287dbe68bfa7daa3593e9b76 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/wlan_tgt_def_config.h @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2011, 2014-2019 The Linux Foundation. All rights reserved. + * + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_TGT_DEF_CONFIG_H__ +#define __WLAN_TGT_DEF_CONFIG_H__ + +/* + * set of default target config , that can be over written by platform + */ + +/* + * default limit of 8 VAPs per device. + */ +/* Rome PRD support 4 vdevs */ +#define CFG_TGT_NUM_VDEV 4 + +/* + * We would need 1 AST entry per peer. Scale it by a + * factor of 2 to minimize hash collisions. + * TODO: This scaling factor would be taken care inside the WAL in the future. + */ +#define CFG_TGT_NUM_PEER_AST 2 + +/* # of WDS entries to support. + */ +#define CFG_TGT_WDS_ENTRIES 0 + +/* MAC DMA burst size. 0: 128B - default, 1: 256B, 2: 64B + */ +#define CFG_TGT_DEFAULT_DMA_BURST_SIZE 0 + +/* Fixed delimiters to be inserted after every MPDU + */ +#define CFG_TGT_DEFAULT_MAC_AGGR_DELIM 0 + +/* + * This value may need to be fine tuned, but a constant value will + * probably always be appropriate; it is probably not necessary to + * determine this value dynamically. + */ +#define CFG_TGT_AST_SKID_LIMIT 16 + +/* + * total number of peers per device. + */ +#define CFG_TGT_NUM_PEERS 14 + +/* + * In offload mode target supports features like WOW, chatter and other + * protocol offloads. In order to support them some functionalities like + * reorder buffering, PN checking need to be done in target. This determines + * maximum number of peers supported by target in offload mode + */ + +/* + * The current firmware implementation requires the number of offload peers + * should be (number of vdevs + 1). + + * The reason for this is the firmware clubbed the self peer and offload peer + * in the same pool. So if the firmware wanted to support n vdevs then the + * number of offload peer must be n+1 of which n buffers will be used for + * self peer and the remaining 1 is used for offload peer to support chatter + * mode for single STA. + + * Technically the macro should be 1 however the current firmware requires n+1. + + * TODO: This MACRO need to be modified in the future, if the firmware modified + * to allocate buffers for self peer and offload peer independently. + */ + +#define CFG_TGT_NUM_OFFLOAD_PEERS (CFG_TGT_NUM_VDEV + 1) + +/* + * Number of reorder buffers used in offload mode + */ +#define CFG_TGT_NUM_OFFLOAD_REORDER_BUFFS 4 + +/* + * keys per peer node + */ +#define CFG_TGT_NUM_PEER_KEYS 2 +/* + * total number of data TX and RX TIDs + */ +#define CFG_TGT_NUM_TIDS (2 * (CFG_TGT_NUM_PEERS + CFG_TGT_NUM_VDEV + 2)) +/* + * set this to 0x7 (Peregrine = 3 chains). + * need to be set dynamically based on the HW capability. + */ +#define CFG_TGT_DEFAULT_TX_CHAIN_MASK 0x7 +/* + * set this to 0x7 (Peregrine = 3 chains). + * need to be set dynamically based on the HW capability. + */ +#define CFG_TGT_DEFAULT_RX_CHAIN_MASK 0x7 +/* 100 ms for video, best-effort, and background */ +#define CFG_TGT_RX_TIMEOUT_LO_PRI 100 +/* 40 ms for voice*/ +#define CFG_TGT_RX_TIMEOUT_HI_PRI 40 + +/* AR9888 unified is default in ethernet mode */ +#define CFG_TGT_RX_DECAP_MODE (0x2) +/* Decap to native Wifi header */ +#define CFG_TGT_RX_DECAP_MODE_NWIFI (0x1) +/* Decap to raw mode header */ +#define CFG_TGT_RX_DECAP_MODE_RAW (0x0) + +/* maximum number of pending scan requests */ +#define CFG_TGT_DEFAULT_SCAN_MAX_REQS 0x4 + +/* maximum number of VDEV that could use BMISS offload */ +#define CFG_TGT_DEFAULT_BMISS_OFFLOAD_MAX_VDEV 0x3 + +/* maximum number of VDEV offload Roaming to support */ +#ifndef CFG_TGT_DEFAULT_ROAM_OFFLOAD_MAX_VDEV +#define CFG_TGT_DEFAULT_ROAM_OFFLOAD_MAX_VDEV 0x3 +#endif + +/* maximum number of STA VDEVs */ +#ifndef CFG_TGT_DEFAULT_MAX_STA_VDEVS +#define CFG_TGT_DEFAULT_MAX_STA_VDEVS 0 +#endif + +/* maximum number of AP profiles pushed to offload Roaming */ +#define CFG_TGT_DEFAULT_ROAM_OFFLOAD_MAX_PROFILES 0x8 + +/* maximum number of VDEV offload GTK to support */ +#ifndef CFG_TGT_DEFAULT_GTK_OFFLOAD_MAX_VDEV +#define CFG_TGT_DEFAULT_GTK_OFFLOAD_MAX_VDEV 0x3 +#endif + +/* default: mcast->ucast disabled if ATH_SUPPORT_MCAST2UCAST not defined */ +#ifndef ATH_SUPPORT_MCAST2UCAST +#define CFG_TGT_DEFAULT_NUM_MCAST_GROUPS 0 +#define CFG_TGT_DEFAULT_NUM_MCAST_TABLE_ELEMS 0 +#define CFG_TGT_DEFAULT_MCAST2UCAST_MODE 0 /* disabled */ +#else +/* (for testing) small multicast group membership table enabled */ +#define CFG_TGT_DEFAULT_NUM_MCAST_GROUPS 4 +#define CFG_TGT_DEFAULT_NUM_MCAST_TABLE_ELEMS 16 +#define CFG_TGT_DEFAULT_MCAST2UCAST_MODE 2 +#endif + +#define CFG_TGT_MAX_MULTICAST_FILTER_ENTRIES 32 +/* + * Specify how much memory the target should allocate for a debug log of + * tx PPDU meta-information (how large the PPDU was, when it was sent, + * whether it was successful, etc.) + * The size of the log records is configurable, from a minimum of 28 bytes + * to a maximum of about 300 bytes. A typical configuration would result + * in each log record being about 124 bytes. + * Thus, 1KB of log space can hold about 30 small records, 3 large records, + * or about 8 typical-sized records. + */ +#define CFG_TGT_DEFAULT_TX_DBG_LOG_SIZE 1024 /* bytes */ + +/* target based fragment timeout and MPDU duplicate detection */ +#define CFG_TGT_DEFAULT_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 0 + +/* Default VoW configuration + */ +#define CFG_TGT_DEFAULT_VOW_CONFIG 0 + +/* + * total number of descriptors to use in the target + */ +#ifndef CFG_TGT_NUM_MSDU_DESC +#define CFG_TGT_NUM_MSDU_DESC (1024 + 32) +#endif + +/* + * Maximum number of frag table entries + */ +#define CFG_TGT_MAX_FRAG_TABLE_ENTRIES 10 + +/* + * Maximum number of VDEV that beacon tx offload will support + */ +#define CFG_TGT_DEFAULT_BEACON_TX_OFFLOAD_MAX_VDEV 3 + +/* + * number of vdevs that can support tdls + */ +#define CFG_TGT_NUM_TDLS_VDEVS 1 + +/* + * number of peers that each Tdls vdev can track + */ +#ifndef CFG_TGT_NUM_TDLS_CONN_TABLE_ENTRIES +#define CFG_TGT_NUM_TDLS_CONN_TABLE_ENTRIES 8 +#endif + +/* + * number of TDLS concurrent sleep STAs + */ +#define CFG_TGT_NUM_TDLS_CONC_SLEEP_STAS 1 + +/* + * number of TDLS concurrent buffer STAs + */ +#define CFG_TGT_NUM_TDLS_CONC_BUFFER_STAS 1 + +/* + * ht enable highest MCS by default + */ +#define CFG_TGT_DEFAULT_GTX_HT_MASK 0x8080 +/* + * vht enable highest MCS by default + */ +#define CFG_TGT_DEFAULT_GTX_VHT_MASK 0x80200 +/* + * threshold to enable GTX + */ +#define CFG_TGT_DEFAULT_GTX_PER_THRESHOLD 3 +/* + * margin to move back when per > margin + threshold + */ +#define CFG_TGT_DEFAULT_GTX_PER_MARGIN 2 +/* + * step for every move + */ +#define CFG_TGT_DEFAULT_GTX_TPC_STEP 1 +/* + * lowest TPC + */ +#define CFG_TGT_DEFAULT_GTX_TPC_MIN 0 +/* + * enable all BW 20/40/80/160 + */ +#define CFG_TGT_DEFAULT_GTX_BW_MASK 0xf + +/* + * number of vdevs that can support OCB + */ +#define CFG_TGT_NUM_OCB_VDEVS 1 + +/* + * maximum number of channels that can do OCB + */ +#define CFG_TGT_NUM_OCB_CHANNELS 2 + +/* + * maximum number of channels in an OCB schedule + */ +#define CFG_TGT_NUM_OCB_SCHEDULES 2 + +#endif /*__WLAN_TGT_DEF_CONFIG_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/wlan_tgt_def_config_hl.h b/drivers/staging/qcacld-3.0/core/mac/inc/wlan_tgt_def_config_hl.h new file mode 100644 index 0000000000000000000000000000000000000000..5de6d8d4f7fe36b73fb0fecc32bab8203643bea1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/wlan_tgt_def_config_hl.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_TGT_DEF_CONFIG_H__ +#define __WLAN_TGT_DEF_CONFIG_H__ + +/* + * TODO: please help to consider if we need a separate config file from LL case. + */ + +/* + * set of default target config , that can be over written by platform + */ + +#ifdef QCA_SUPPORT_INTEGRATED_SOC +#define CFG_TGT_NUM_VDEV 3 /*STA, P2P device, P2P GO/Cli*/ +#else +/* + * default limit of VAPs per device. + */ +#define CFG_TGT_NUM_VDEV 3 +#endif +/* + * We would need 1 AST entry per peer. Scale it by a factor of 2 to minimize + * hash collisions. + * TODO: This scaling factor would be taken care inside the WAL in the future. + */ +#define CFG_TGT_NUM_PEER_AST 2 + +/* # of WDS entries to support. + */ +#define CFG_TGT_WDS_ENTRIES 2 + +/* MAC DMA burst size. 0: 128B - default, 1: 256B, 2: 64B + */ +#define CFG_TGT_DEFAULT_DMA_BURST_SIZE 0 + +/* Fixed delimiters to be inserted after every MPDU + */ +#define CFG_TGT_DEFAULT_MAC_AGGR_DELIM 0 + +/* + * This value may need to be fine tuned, but a constant value will + * probably always be appropriate; it is probably not necessary to + * determine this value dynamically. + */ +#ifndef CFG_TGT_AST_SKID_LIMIT +#define CFG_TGT_AST_SKID_LIMIT 6 +#endif +/* + * total number of peers per device. + * currently set to 8 to bring up IP3.9 for memory size problem + */ +#define CFG_TGT_NUM_PEERS 8 +/* + * max number of peers per device. + */ +#define CFG_TGT_NUM_PEERS_MAX 8 +/* + * In offload mode target supports features like WOW, chatter and other + * protocol offloads. In order to support them some functionalities like + * reorder buffering, PN checking need to be done in target. This determines + * maximum number of peers supported by target in offload mode + */ +#define CFG_TGT_NUM_OFFLOAD_PEERS 0 +/* + * Number of reorder buffers used in offload mode + */ +#define CFG_TGT_NUM_OFFLOAD_REORDER_BUFFS 0 +/* + * keys per peer node + */ +#define CFG_TGT_NUM_PEER_KEYS 2 +/* + * total number of TX/RX data TIDs + */ +#define CFG_TGT_NUM_TIDS (2 * (CFG_TGT_NUM_PEERS + \ + CFG_TGT_NUM_VDEV)) +/* + * max number of Tx TIDS + */ +#define CFG_TGT_NUM_TIDS_MAX (2 * (CFG_TGT_NUM_PEERS_MAX + \ + CFG_TGT_NUM_VDEV)) +/* + * number of multicast keys. + */ +#define CFG_TGT_NUM_MCAST_KEYS 8 +/* + * A value of 3 would probably suffice - one for the control stack, one for + * the data stack, and one for debugging. + * This value may need to be fine tuned, but a constant value will + * probably always be appropriate; it is probably not necessary to + * determine this value dynamically. + */ +#define CFG_TGT_NUM_PDEV_HANDLERS 8 +/* + * A value of 3 would probably suffice - one for the control stack, one for + * the data stack, and one for debugging. + * This value may need to be fine tuned, but a constant value will + * probably always be appropriate; it is probably not necessary to + * determine this value dynamically. + */ +#define CFG_TGT_NUM_VDEV_HANDLERS 4 +/* + * set this to 8: + * one for WAL interals (connection pause) + * one for the control stack, + * one for the data stack + * and one for debugging + * This value may need to be fine tuned, but a constant value will + * probably always be appropriate; it is probably not necessary to + * determine this value dynamically. + */ +#define CFG_TGT_NUM_HANDLERS 14 +/* + * set this to 3: one for the control stack, one for + * the data stack, and one for debugging. + * This value may need to be fine tuned, but a constant value will + * probably always be appropriate; it is probably not necessary to + * determine this value dynamically. + */ +#define CFG_TGT_NUM_PEER_HANDLERS 32 +/* + * set this to 0x7 (Peregrine = 3 chains). + * need to be set dynamically based on the HW capability. + * this is rome + */ +#define CFG_TGT_DEFAULT_TX_CHAIN_MASK 0x3 +/* + * set this to 0x7 (Peregrine = 3 chains). + * need to be set dynamically based on the HW capability. + * this is rome + */ +#define CFG_TGT_DEFAULT_RX_CHAIN_MASK 0x3 +/* 100 ms for video, best-effort, and background */ +#define CFG_TGT_RX_TIMEOUT_LO_PRI 100 +/* 40 ms for voice*/ +#define CFG_TGT_RX_TIMEOUT_HI_PRI 40 + +/* AR9888 unified is default in ethernet mode */ +#define CFG_TGT_RX_DECAP_MODE (0x2) +/* Decap to native Wifi header */ +#define CFG_TGT_RX_DECAP_MODE_NWIFI (0x1) + +/* Decap to raw mode header */ +#define CFG_TGT_RX_DECAP_MODE_RAW (0x0) + +/* maximum number of pending scan requests */ +#define CFG_TGT_DEFAULT_SCAN_MAX_REQS 0x4 + +/* maximum number of scan event handlers */ +#define CFG_TGT_DEFAULT_SCAN_MAX_HANDLERS 0x4 + +/* maximum number of VDEV that could use BMISS offload */ +#define CFG_TGT_DEFAULT_BMISS_OFFLOAD_MAX_VDEV 0x2 + +/* maximum number of VDEV offload Roaming to support */ +#ifndef CFG_TGT_DEFAULT_ROAM_OFFLOAD_MAX_VDEV +#define CFG_TGT_DEFAULT_ROAM_OFFLOAD_MAX_VDEV 0x2 +#endif + +/* maximum number of STA VDEVs */ +#ifndef CFG_TGT_DEFAULT_MAX_STA_VDEVS +#define CFG_TGT_DEFAULT_MAX_STA_VDEVS 0 +#endif + +/* maximum number of AP profiles pushed to offload Roaming */ +#define CFG_TGT_DEFAULT_ROAM_OFFLOAD_MAX_PROFILES 0x8 + +/* maximum number of VDEV offload GTK to support */ +#ifndef CFG_TGT_DEFAULT_GTK_OFFLOAD_MAX_VDEV +#define CFG_TGT_DEFAULT_GTK_OFFLOAD_MAX_VDEV 0x2 +#endif +/* default: mcast->ucast disabled */ + +#define CFG_TGT_DEFAULT_NUM_MCAST_GROUPS 0 +#define CFG_TGT_DEFAULT_NUM_MCAST_TABLE_ELEMS 0 +#define CFG_TGT_DEFAULT_MCAST2UCAST_MODE 0 /* disabled */ + +/* + * Specify how much memory the target should allocate for a debug log of + * tx PPDU meta-information (how large the PPDU was, when it was sent, + * whether it was successful, etc.) + * The size of the log records is configurable, from a minimum of 28 bytes + * to a maximum of about 300 bytes. A typical configuration would result + * in each log record being about 124 bytes. + * Thus, 1KB of log space can hold about 30 small records, 3 large records, + * or about 8 typical-sized records. + */ +#define CFG_TGT_DEFAULT_TX_DBG_LOG_SIZE 1024 /* bytes */ + +/* target based fragment timeout and MPDU duplicate detection */ +#define CFG_TGT_DEFAULT_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 0 +/* Default VoW configuration + */ +#define CFG_TGT_DEFAULT_VOW_CONFIG 0 + +/* + * total number of descriptors to use in the target + */ +#ifndef CFG_TGT_NUM_MSDU_DESC +#define CFG_TGT_NUM_MSDU_DESC (32) +#endif +/* + * Maximum number of frag table entries + */ +#define CFG_TGT_MAX_FRAG_TABLE_ENTRIES 2 + +/* + * number of vdevs that can support tdls + */ +#define CFG_TGT_NUM_TDLS_VDEVS 1 + +/* + * number of peers that each Tdls vdev can track + */ +#ifndef CFG_TGT_NUM_TDLS_CONN_TABLE_ENTRIES +#define CFG_TGT_NUM_TDLS_CONN_TABLE_ENTRIES 8 +#endif +/* + * number of TDLS concurrent sleep STAs + */ +#define CFG_TGT_NUM_TDLS_CONC_SLEEP_STAS 1 + +/* + * number of TDLS concurrent buffer STAs + */ +#define CFG_TGT_NUM_TDLS_CONC_BUFFER_STAS 1 + +#define CFG_TGT_MAX_MULTICAST_FILTER_ENTRIES 16 + +/* + * Maximum number of VDEV that beacon tx offload will support + */ +#ifndef CFG_TGT_DEFAULT_BEACON_TX_OFFLOAD_MAX_VDEV +/* For Naples/Rome/Tufello */ +#ifdef HIF_SDIO +#define CFG_TGT_DEFAULT_BEACON_TX_OFFLOAD_MAX_VDEV 2 +#else +#define CFG_TGT_DEFAULT_BEACON_TX_OFFLOAD_MAX_VDEV 1 +#endif +#endif /* CFG_TGT_DEFAULT_BEACON_TX_OFFLOAD_MAX_VDEV */ + +/* + * ht enable highest MCS by default + */ +#define CFG_TGT_DEFAULT_GTX_HT_MASK 0x8080 +/* + * vht enable highest MCS by default + */ +#define CFG_TGT_DEFAULT_GTX_VHT_MASK 0x80200 +/* + * threshold to enable GTX + */ +#define CFG_TGT_DEFAULT_GTX_PER_THRESHOLD 3 +/* + * margin to move back when per > margin + threshold + */ +#define CFG_TGT_DEFAULT_GTX_PER_MARGIN 2 +/* + * step for every move + */ +#define CFG_TGT_DEFAULT_GTX_TPC_STEP 1 +/* + * lowest TPC + */ +#define CFG_TGT_DEFAULT_GTX_TPC_MIN 0 +/* + * enable all BW 20/40/80/160 + */ +#define CFG_TGT_DEFAULT_GTX_BW_MASK 0xf + +/* + * number of vdevs that can support OCB + */ +#define CFG_TGT_NUM_OCB_VDEVS 1 + +/* + * maximum number of channels that can do OCB + */ +#define CFG_TGT_NUM_OCB_CHANNELS 2 + +/* + * maximum number of channels in an OCB schedule + */ +#define CFG_TGT_NUM_OCB_SCHEDULES 2 + +#endif /*__WLAN_TGT_DEF_CONFIG_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/wni_api.h b/drivers/staging/qcacld-3.0/core/mac/inc/wni_api.h new file mode 100644 index 0000000000000000000000000000000000000000..58ac862944eca82d9d3871a83c9ff79c5b957afa --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/wni_api.h @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file wni_api.h contains message definitions exported by + * Sirius software modules. + * NOTE: See projects/sirius/include/sir_api.h for structure + * definitions of the host/FW messages. + * + * Author: Chandra Modumudi + * Date: 04/11/2002 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#ifndef __WNI_API_H +#define __WNI_API_H + +#include "sir_api.h" + +#define SIR_SME_MSG_TYPES_BEGIN (SIR_SME_MODULE_ID << 8) + +enum eWniMsgTypes { + eWNI_SME_MSG_TYPES_BEGIN = SIR_SME_MSG_TYPES_BEGIN, + eWNI_SME_SYS_READY_IND = SIR_SME_MSG_TYPES_BEGIN + 1, + eWNI_SME_JOIN_REQ = SIR_SME_MSG_TYPES_BEGIN + 2, + eWNI_SME_JOIN_RSP = SIR_SME_MSG_TYPES_BEGIN + 3, + eWNI_SME_SETCONTEXT_RSP = SIR_SME_MSG_TYPES_BEGIN + 5, + eWNI_SME_REASSOC_REQ = SIR_SME_MSG_TYPES_BEGIN + 6, + eWNI_SME_REASSOC_RSP = SIR_SME_MSG_TYPES_BEGIN + 7, + eWNI_SME_DISASSOC_REQ = SIR_SME_MSG_TYPES_BEGIN + 8, + eWNI_SME_DISASSOC_RSP = SIR_SME_MSG_TYPES_BEGIN + 9, + eWNI_SME_DISASSOC_IND = SIR_SME_MSG_TYPES_BEGIN + 10, + eWNI_SME_DISASSOC_CNF = SIR_SME_MSG_TYPES_BEGIN + 11, + eWNI_SME_DEAUTH_REQ = SIR_SME_MSG_TYPES_BEGIN + 12, + eWNI_SME_DEAUTH_RSP = SIR_SME_MSG_TYPES_BEGIN + 13, + eWNI_SME_DEAUTH_IND = SIR_SME_MSG_TYPES_BEGIN + 14, + eWNI_SME_DISCONNECT_DONE_IND = SIR_SME_MSG_TYPES_BEGIN + 15, + eWNI_SME_WM_STATUS_CHANGE_NTF = SIR_SME_MSG_TYPES_BEGIN + 16, + eWNI_SME_IBSS_NEW_PEER_IND = SIR_SME_MSG_TYPES_BEGIN + 17, + eWNI_SME_IBSS_PEER_DEPARTED_IND = SIR_SME_MSG_TYPES_BEGIN + 18, + eWNI_SME_START_BSS_REQ = SIR_SME_MSG_TYPES_BEGIN + 19, + eWNI_SME_START_BSS_RSP = SIR_SME_MSG_TYPES_BEGIN + 20, + eWNI_SME_ASSOC_IND = SIR_SME_MSG_TYPES_BEGIN + 21, + eWNI_SME_ASSOC_CNF = SIR_SME_MSG_TYPES_BEGIN + 22, + eWNI_SME_SWITCH_CHL_IND = SIR_SME_MSG_TYPES_BEGIN + 23, + eWNI_SME_STOP_BSS_REQ = SIR_SME_MSG_TYPES_BEGIN + 24, + eWNI_SME_STOP_BSS_RSP = SIR_SME_MSG_TYPES_BEGIN + 25, + eWNI_SME_DEAUTH_CNF = SIR_SME_MSG_TYPES_BEGIN + 26, + eWNI_SME_MIC_FAILURE_IND = SIR_SME_MSG_TYPES_BEGIN + 27, + eWNI_SME_ADDTS_REQ = SIR_SME_MSG_TYPES_BEGIN + 28, + eWNI_SME_ADDTS_RSP = SIR_SME_MSG_TYPES_BEGIN + 29, + eWNI_SME_DELTS_REQ = SIR_SME_MSG_TYPES_BEGIN + 30, + eWNI_SME_DELTS_RSP = SIR_SME_MSG_TYPES_BEGIN + 31, + eWNI_SME_DELTS_IND = SIR_SME_MSG_TYPES_BEGIN + 32, + /* + * unused SIR_SME_MSG_TYPES_BEGIN + 33 to + * to SIR_SME_MSG_TYPES_BEGIN + 35 + */ + eWNI_SME_ASSOC_IND_UPPER_LAYER = SIR_SME_MSG_TYPES_BEGIN + 36, + eWNI_SME_WPS_PBC_PROBE_REQ_IND = SIR_SME_MSG_TYPES_BEGIN + 37, + eWNI_SME_UPPER_LAYER_ASSOC_CNF = SIR_SME_MSG_TYPES_BEGIN + 38, + eWNI_SME_SESSION_UPDATE_PARAM = SIR_SME_MSG_TYPES_BEGIN + 39, + eWNI_SME_CHNG_MCC_BEACON_INTERVAL = SIR_SME_MSG_TYPES_BEGIN + 40, + eWNI_SME_GET_SNR_REQ = SIR_SME_MSG_TYPES_BEGIN + 41, + + eWNI_SME_RRM_MSG_TYPE_BEGIN = SIR_SME_MSG_TYPES_BEGIN + 42, + + eWNI_SME_NEIGHBOR_REPORT_REQ_IND = SIR_SME_MSG_TYPES_BEGIN + 43, + eWNI_SME_NEIGHBOR_REPORT_IND = SIR_SME_MSG_TYPES_BEGIN + 44, + eWNI_SME_BEACON_REPORT_REQ_IND = SIR_SME_MSG_TYPES_BEGIN + 45, + eWNI_SME_BEACON_REPORT_RESP_XMIT_IND = SIR_SME_MSG_TYPES_BEGIN + 46, + + /* unused SIR_SME_MSG_TYPES_BEGIN + 47, */ + /* unused SIR_SME_MSG_TYPES_BEGIN + 48, */ + + eWNI_SME_FT_PRE_AUTH_REQ = SIR_SME_MSG_TYPES_BEGIN + 49, + eWNI_SME_FT_PRE_AUTH_RSP = SIR_SME_MSG_TYPES_BEGIN + 50, + eWNI_SME_FT_AGGR_QOS_REQ = SIR_SME_MSG_TYPES_BEGIN + 52, + eWNI_SME_FT_AGGR_QOS_RSP = SIR_SME_MSG_TYPES_BEGIN + 53, + +#if defined FEATURE_WLAN_ESE + eWNI_SME_ESE_ADJACENT_AP_REPORT = SIR_SME_MSG_TYPES_BEGIN + 54, +#endif + + eWNI_SME_REGISTER_MGMT_FRAME_REQ = SIR_SME_MSG_TYPES_BEGIN + 55, + eWNI_SME_GENERIC_CHANGE_COUNTRY_CODE = SIR_SME_MSG_TYPES_BEGIN + 56, + eWNI_SME_MAX_ASSOC_EXCEEDED = SIR_SME_MSG_TYPES_BEGIN + 57, +#ifdef FEATURE_WLAN_TDLS + eWNI_SME_TDLS_SEND_MGMT_REQ = SIR_SME_MSG_TYPES_BEGIN + 58, + eWNI_SME_TDLS_SEND_MGMT_RSP = SIR_SME_MSG_TYPES_BEGIN + 59, + eWNI_SME_TDLS_ADD_STA_REQ = SIR_SME_MSG_TYPES_BEGIN + 60, + eWNI_SME_TDLS_ADD_STA_RSP = SIR_SME_MSG_TYPES_BEGIN + 61, + eWNI_SME_TDLS_DEL_STA_REQ = SIR_SME_MSG_TYPES_BEGIN + 62, + eWNI_SME_TDLS_DEL_STA_RSP = SIR_SME_MSG_TYPES_BEGIN + 63, + eWNI_SME_TDLS_DEL_STA_IND = SIR_SME_MSG_TYPES_BEGIN + 64, + eWNI_SME_TDLS_DEL_ALL_PEER_IND = SIR_SME_MSG_TYPES_BEGIN + 65, + eWNI_SME_MGMT_FRM_TX_COMPLETION_IND = SIR_SME_MSG_TYPES_BEGIN + 66, + eWNI_SME_TDLS_LINK_ESTABLISH_REQ = SIR_SME_MSG_TYPES_BEGIN + 67, + eWNI_SME_TDLS_LINK_ESTABLISH_RSP = SIR_SME_MSG_TYPES_BEGIN + 68, + eWNI_SME_TDLS_SHOULD_DISCOVER = SIR_SME_MSG_TYPES_BEGIN + 69, + eWNI_SME_TDLS_SHOULD_TEARDOWN = SIR_SME_MSG_TYPES_BEGIN + 70, + eWNI_SME_TDLS_PEER_DISCONNECTED = SIR_SME_MSG_TYPES_BEGIN + 71, +#endif + /* NOTE: If you are planning to add more mesages, please make sure that */ + /* SIR_LIM_ITC_MSG_TYPES_BEGIN is moved appropriately. It is set as */ + /* SIR_LIM_MSG_TYPES_BEGIN+0xB0 = 12B0 (which means max of 176 messages and */ + /* eWNI_SME_TDLS_DEL_STA_RSP = 175. */ + /* Should fix above issue to enable TDLS_INTERNAL */ + eWNI_SME_RESET_AP_CAPS_CHANGED = SIR_SME_MSG_TYPES_BEGIN + 73, +#ifdef WLAN_FEATURE_11W + eWNI_SME_UNPROT_MGMT_FRM_IND = SIR_SME_MSG_TYPES_BEGIN + 74, +#endif +#ifdef WLAN_FEATURE_GTK_OFFLOAD + eWNI_PMC_GTK_OFFLOAD_GETINFO_RSP = SIR_SME_MSG_TYPES_BEGIN + 75, +#endif /* WLAN_FEATURE_GTK_OFFLOAD */ + /*ROAM candidate indication from FW */ + eWNI_SME_CANDIDATE_FOUND_IND = SIR_SME_MSG_TYPES_BEGIN + 76, + /*upper layer requested handoff to driver in STA mode */ + eWNI_SME_HANDOFF_REQ = SIR_SME_MSG_TYPES_BEGIN + 77, + /*Fwd the LFR scan offload rsp from FW to SME */ + eWNI_SME_ROAM_SCAN_OFFLOAD_RSP = SIR_SME_MSG_TYPES_BEGIN + 78, + eWNI_SME_IBSS_PEER_INFO_RSP = SIR_SME_MSG_TYPES_BEGIN + 79, + eWNI_SME_GET_TSM_STATS_REQ = SIR_SME_MSG_TYPES_BEGIN + 80, + eWNI_SME_GET_TSM_STATS_RSP = SIR_SME_MSG_TYPES_BEGIN + 81, + eWNI_SME_TSM_IE_IND = SIR_SME_MSG_TYPES_BEGIN + 82, + + /* DFS EVENTS */ + /* RADAR found indication from DFS */ + eWNI_SME_DFS_RADAR_FOUND = SIR_SME_MSG_TYPES_BEGIN + 83, + /* Channel Change Request from SAP */ + eWNI_SME_CHANNEL_CHANGE_REQ = SIR_SME_MSG_TYPES_BEGIN + 84, + /* Channel Change Response from WMA */ + eWNI_SME_CHANNEL_CHANGE_RSP = SIR_SME_MSG_TYPES_BEGIN + 85, + /* Start Beacon Transmission. */ + eWNI_SME_START_BEACON_REQ = SIR_SME_MSG_TYPES_BEGIN + 86, + /* Transmit CSA IE in beacons */ + eWNI_SME_DFS_BEACON_CHAN_SW_IE_REQ = SIR_SME_MSG_TYPES_BEGIN + 87, + /* To indicate completion of CSA IE */ + eWNI_SME_DFS_CSAIE_TX_COMPLETE_IND = SIR_SME_MSG_TYPES_BEGIN + 88, + /* update in beacons/probe rsp */ + eWNI_SME_STATS_EXT_EVENT = SIR_SME_MSG_TYPES_BEGIN + 89, + /* Unused SIR_SME_MSG_TYPES_BEGIN + 90 */ + eWNI_SME_GET_PEER_INFO_EXT_IND = SIR_SME_MSG_TYPES_BEGIN + 91, + /* indicates Additional IE from hdd to PE */ + eWNI_SME_UPDATE_ADDITIONAL_IES = SIR_SME_MSG_TYPES_BEGIN + 93, + /* To indicate IE modify from hdd to PE */ + eWNI_SME_MODIFY_ADDITIONAL_IES = SIR_SME_MSG_TYPES_BEGIN + 94, +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + eWNI_SME_AUTO_SHUTDOWN_IND = SIR_SME_MSG_TYPES_BEGIN + 95, +#endif +#ifdef QCA_HT_2040_COEX + eWNI_SME_SET_HT_2040_MODE = SIR_SME_MSG_TYPES_BEGIN + 96, +#endif +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + /* Hand Off Failure Ind from WMA to SME */ + eWNI_SME_HO_FAIL_IND = SIR_SME_MSG_TYPES_BEGIN + 97, +#endif +#ifdef WLAN_FEATURE_NAN + eWNI_SME_NAN_EVENT = SIR_SME_MSG_TYPES_BEGIN + 98, +#endif + eWNI_SME_LINK_STATUS_IND = SIR_SME_MSG_TYPES_BEGIN + 99, +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + eWNI_SME_READY_TO_EXTWOW_IND = SIR_SME_MSG_TYPES_BEGIN + 100, +#endif + eWNI_SME_MSG_GET_TEMPERATURE_IND = SIR_SME_MSG_TYPES_BEGIN + 101, + eWNI_SME_SNR_IND = SIR_SME_MSG_TYPES_BEGIN + 102, +#ifdef FEATURE_WLAN_EXTSCAN + eWNI_SME_EXTSCAN_FULL_SCAN_RESULT_IND = SIR_SME_MSG_TYPES_BEGIN + 103, + eWNI_SME_EPNO_NETWORK_FOUND_IND = SIR_SME_MSG_TYPES_BEGIN + 104, +#endif + eWNI_SME_SET_HW_MODE_REQ = SIR_SME_MSG_TYPES_BEGIN + 105, + eWNI_SME_SET_HW_MODE_RESP = SIR_SME_MSG_TYPES_BEGIN + 106, + eWNI_SME_HW_MODE_TRANS_IND = SIR_SME_MSG_TYPES_BEGIN + 107, + eWNI_SME_NSS_UPDATE_REQ = SIR_SME_MSG_TYPES_BEGIN + 108, + eWNI_SME_NSS_UPDATE_RSP = SIR_SME_MSG_TYPES_BEGIN + 109, + eWNI_SME_OCB_SET_CONFIG_RSP = SIR_SME_MSG_TYPES_BEGIN + 110, + eWNI_SME_OCB_GET_TSF_TIMER_RSP = SIR_SME_MSG_TYPES_BEGIN + 111, + eWNI_SME_DCC_GET_STATS_RSP = SIR_SME_MSG_TYPES_BEGIN + 112, + eWNI_SME_DCC_UPDATE_NDL_RSP = SIR_SME_MSG_TYPES_BEGIN + 113, + eWNI_SME_DCC_STATS_EVENT = SIR_SME_MSG_TYPES_BEGIN + 114, + eWNI_SME_SET_DUAL_MAC_CFG_REQ = SIR_SME_MSG_TYPES_BEGIN + 115, + eWNI_SME_SET_DUAL_MAC_CFG_RESP = SIR_SME_MSG_TYPES_BEGIN + 116, + eWNI_SME_SET_THERMAL_LEVEL_IND = SIR_SME_MSG_TYPES_BEGIN + 117, + eWNI_SME_SET_IE_REQ = SIR_SME_MSG_TYPES_BEGIN + 118, + eWNI_SME_EXT_CHANGE_CHANNEL = SIR_SME_MSG_TYPES_BEGIN + 119, + eWNI_SME_EXT_CHANGE_CHANNEL_IND = SIR_SME_MSG_TYPES_BEGIN + 120, + eWNI_SME_REGISTER_MGMT_FRAME_CB = SIR_SME_MSG_TYPES_BEGIN + 121, + /* START and UPDATE OBSS SCAN Indication*/ + eWNI_SME_HT40_OBSS_SCAN_IND = SIR_SME_MSG_TYPES_BEGIN + 122, + eWNI_SME_SET_ANTENNA_MODE_REQ = SIR_SME_MSG_TYPES_BEGIN + 123, + eWNI_SME_SET_ANTENNA_MODE_RESP = SIR_SME_MSG_TYPES_BEGIN + 124, + eWNI_SME_TSF_EVENT = SIR_SME_MSG_TYPES_BEGIN + 125, + eWNI_SME_MON_INIT_SESSION = SIR_SME_MSG_TYPES_BEGIN + 126, + eWNI_SME_PDEV_SET_HT_VHT_IE = SIR_SME_MSG_TYPES_BEGIN + 127, + eWNI_SME_SET_VDEV_IES_PER_BAND = SIR_SME_MSG_TYPES_BEGIN + 128, + eWNI_SME_SEND_DISASSOC_FRAME = SIR_SME_MSG_TYPES_BEGIN + 129, + eWNI_SME_UPDATE_ACCESS_POLICY_VENDOR_IE = SIR_SME_MSG_TYPES_BEGIN + 130, + eWNI_SME_DEFAULT_SCAN_IE = SIR_SME_MSG_TYPES_BEGIN + 131, + eWNI_SME_ROAM_INVOKE = SIR_SME_MSG_TYPES_BEGIN + 132, + eWNI_SME_ROAM_SCAN_OFFLOAD_REQ = SIR_SME_MSG_TYPES_BEGIN + 133, + eWNI_SME_LOST_LINK_INFO_IND = SIR_SME_MSG_TYPES_BEGIN + 134, + eWNI_SME_DEL_ALL_TDLS_PEERS = SIR_SME_MSG_TYPES_BEGIN + 135, + eWNI_SME_RSO_CMD_STATUS_IND = SIR_SME_MSG_TYPES_BEGIN + 136, + eWMI_SME_LL_STATS_IND = SIR_SME_MSG_TYPES_BEGIN + 137, + eWNI_SME_DFS_CAC_COMPLETE = SIR_SME_MSG_TYPES_BEGIN + 138, + eWNI_SME_UPDATE_CONFIG = SIR_SME_MSG_TYPES_BEGIN + 139, + eWNI_SME_BT_ACTIVITY_INFO_IND = SIR_SME_MSG_TYPES_BEGIN + 140, + eWNI_SME_SET_HE_BSS_COLOR = SIR_SME_MSG_TYPES_BEGIN + 141, + eWNI_SME_TRIGGER_SAE = SIR_SME_MSG_TYPES_BEGIN + 142, + eWNI_SME_SEND_MGMT_FRAME_TX = SIR_SME_MSG_TYPES_BEGIN + 143, + eWNI_SME_SEND_SAE_MSG = SIR_SME_MSG_TYPES_BEGIN + 144, + eWNI_SME_SET_ADDBA_ACCEPT = SIR_SME_MSG_TYPES_BEGIN + 145, + eWNI_SME_UPDATE_EDCA_PROFILE = SIR_SME_MSG_TYPES_BEGIN + 146, + WNI_SME_UPDATE_MU_EDCA_PARAMS = SIR_SME_MSG_TYPES_BEGIN + 147, + eWNI_SME_CSA_RESTART_REQ = SIR_SME_MSG_TYPES_BEGIN + 148, + eWNI_SME_CSA_RESTART_RSP = SIR_SME_MSG_TYPES_BEGIN + 149, + WNI_SME_CFG_ACTION_FRM_HE_TB_PPDU = SIR_SME_MSG_TYPES_BEGIN + 150, + /* To indicate Hidden ssid start complition to upper layer */ + eWNI_SME_HIDDEN_SSID_RESTART_RSP = SIR_SME_MSG_TYPES_BEGIN + 151, + eWNI_SME_FW_STATUS_IND = SIR_SME_MSG_TYPES_BEGIN + 152, + eWNI_SME_STA_CSA_CONTINUE_REQ = SIR_SME_MSG_TYPES_BEGIN + 153, + WNI_SME_REGISTER_BCN_REPORT_SEND_CB = SIR_SME_MSG_TYPES_BEGIN + 154, + eWNI_SME_ANTENNA_ISOLATION_RSP = SIR_SME_MSG_TYPES_BEGIN + 155, + eWNI_SME_MON_DEINIT_SESSION = SIR_SME_MSG_TYPES_BEGIN + 156, + eWNI_SME_VDEV_DELETE_REQ = SIR_SME_MSG_TYPES_BEGIN + 157, + eWNI_SME_VDEV_DELETE_RSP = SIR_SME_MSG_TYPES_BEGIN + 158, + eWNI_SME_ROAM_INIT_PARAM = SIR_SME_MSG_TYPES_BEGIN + 159, + eWNI_SME_ROAM_SEND_PER_REQ = SIR_SME_MSG_TYPES_BEGIN + 160, + eWNI_SME_GET_ROAM_SCAN_CH_LIST_EVENT = + SIR_SME_MSG_TYPES_BEGIN + 161, + eWNI_SME_MONITOR_MODE_VDEV_UP = SIR_SME_MSG_TYPES_BEGIN + 162, + eWNI_SME_UPDATE_SESSION_EDCA_TXQ_PARAMS = SIR_SME_MSG_TYPES_BEGIN + 163, + eWNI_SME_ABORT_CONN_TIMER = SIR_SME_MSG_TYPES_BEGIN + 164, + eWNI_SME_MSG_TYPES_END = SIR_SME_MSG_TYPES_BEGIN + 165 +}; + +typedef struct sAniCfgTxRateCtrs { +/* add the rate counters here */ + unsigned long TxFrames_1Mbps; + unsigned long TxFrames_2Mbps; + unsigned long TxFrames_5_5Mbps; + unsigned long TxFrames_6Mbps; + unsigned long TxFrames_9Mbps; + unsigned long TxFrames_11Mbps; + unsigned long TxFrames_12Mbps; + unsigned long TxFrames_18Mbps; + unsigned long TxFrames_24Mbps; + unsigned long TxFrames_36Mbps; + unsigned long TxFrames_48Mbps; + unsigned long TxFrames_54Mbps; + unsigned long TxFrames_72Mbps; + unsigned long TxFrames_96Mbps; + unsigned long TxFrames_108Mbps; + +} tAniCfgTxRateCtrs, *tpAniCfgTxRateCtrs; +#endif /* __WNI_API_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/wni_cfg.h b/drivers/staging/qcacld-3.0/core/mac/inc/wni_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..1ec42dc951684cc6aad4cec4dadade7bbb7426fe --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/inc/wni_cfg.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WNICFG_H +#define __WNICFG_H + +/* + * String parameter lengths + */ + +#define WNI_CFG_VALID_CHANNEL_LIST_LEN 100 +#define WNI_CFG_COUNTRY_CODE_LEN 3 +#define WNI_CFG_PROBE_RSP_ADDNIE_DATA1_LEN 255 +#define WNI_CFG_ASSOC_RSP_ADDNIE_DATA_LEN 255 +#define WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN 255 +#define WNI_CFG_WPS_UUID_LEN 16 +#define WNI_CFG_HE_PPET_LEN 25 + +/* + * Integer parameter min/max/default values + */ +#define WNI_CFG_PHY_MODE_11A 0 +#define WNI_CFG_PHY_MODE_11B 1 +#define WNI_CFG_PHY_MODE_11G 2 +#define WNI_CFG_PHY_MODE_NONE 3 + +#define WNI_CFG_CURRENT_CHANNEL_STAMIN 0 +#define WNI_CFG_CURRENT_CHANNEL_STAMAX 173 + +#define WNI_CFG_EDCA_PROFILE_ANI 0 +#define WNI_CFG_EDCA_PROFILE_WMM 1 +#define WNI_CFG_EDCA_PROFILE_ETSI_EUROPE 3 +#define WNI_CFG_EDCA_PROFILE_MAX 4 + +#define WNI_CFG_ADMIT_POLICY_ADMIT_ALL 0 +#define WNI_CFG_ADMIT_POLICY_REJECT_ALL 1 +#define WNI_CFG_ADMIT_POLICY_BW_FACTOR 2 + +#define WNI_CFG_CHANNEL_BONDING_MODE_DISABLE 0 +#define WNI_CFG_CHANNEL_BONDING_MODE_ENABLE 1 + +#define WNI_CFG_BLOCK_ACK_ENABLED_DELAYED 0 +#define WNI_CFG_BLOCK_ACK_ENABLED_IMMEDIATE 1 + +#define WNI_CFG_HT_CAP_INFO_ADVANCE_CODING 0 +#define WNI_CFG_HT_CAP_INFO_SHORT_GI_20MHZ 5 +#define WNI_CFG_HT_CAP_INFO_SHORT_GI_40MHZ 6 +#define WNI_CFG_HT_CAP_INFO_TX_STBC 7 +#define WNI_CFG_HT_CAP_INFO_RX_STBC 8 + +#define WNI_CFG_GREENFIELD_CAPABILITY_DISABLE 0 + +/* + * WNI_CFG_VHT_CSN_BEAMFORMEE_ANT_SUPPORTED_FW_DEF + 1 is + * assumed to be the default fw supported BF antennas, if fw + * says it supports 8 antennas in rx ready event and if + * gTxBFCsnValue INI value is configured above 3, set + * the same to WNI_CFG_VHT_CSN_BEAMFORMEE_ANT_SUPPORTED. + * Otherwise, fall back and set fw default value[3]. + */ + +#define WNI_CFG_WPS_ENABLE_AP 1 + +#define WNI_CFG_ASSOC_STA_LIMIT_STAMIN 1 +#define WNI_CFG_ASSOC_STA_LIMIT_STAMAX 32 + +#define WNI_CFG_REMOVE_TIME_SYNC_CMD_STAMIN 0 +#define WNI_CFG_REMOVE_TIME_SYNC_CMD_STAMAX 1 +#define WNI_CFG_REMOVE_TIME_SYNC_CMD_STADEF 0 + +#define CFG_STA_MAGIC_DWORD 0xbeefbeef + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/cfg/cfgUtil/dot11f.frms b/drivers/staging/qcacld-3.0/core/mac/src/cfg/cfgUtil/dot11f.frms new file mode 100644 index 0000000000000000000000000000000000000000..ed3381df616fe11339aa5c3d01971fb36350a875 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/cfg/cfgUtil/dot11f.frms @@ -0,0 +1,4428 @@ +/* + * Copyright (c) 2006-2007, 2014-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file dot11f.frms + * + * \brief Primary 'frames' file for the MAC parser + * + * + * This file defines several 802.11 frames (along with their associated + * constituents) in a little language called "frames". When run through the + * 'framesc' program, it will generate C code for working with these frames: + * C structs representing the 802.11 frame together with functions for + * packing & unpacking them. + * + * For more information on the "frames" language, run 'framesc --help'... + * + * + */ + + +// Tell framesc what types to use for... +%8-bit-type uint8_t // 8, +%16-bit-type uint16_t // 16, +%32-bit-type uint32_t // & 32-bit unsigned integral types. These can also + // be specified on the command line. + +// Define some mnemonic constants; these are just for our use with the frames +// files we're compiling. IOW, they won't result in any C code being +// emitted. + +const EID_SSID = 0; +const EID_SUPP_RATES = 1; +const EID_FH_PARAM_SET = 2; +const EID_DS_PARAM_SET = 3; +const EID_CF_PARAM_SET = 4; +const EID_TIM = 5; +const EID_IBSS_PARAM_SET = 6; +const EID_COUNTRY = 7; +const EID_FH_PATTERN = 8; +const EID_FH_PATT_TABLE = 9; +const EID_REQUEST = 10; +const EID_QBSS_LOAD = 11; +const EID_EDCA_PARAM_SET = 12; +const EID_TSPEC = 13; +const EID_TCLAS = 14; +const EID_SCHEDULE = 15; +const EID_CHALLENGE_TEXT = 16; +const EID_POWER_CONSTRAINTS = 32; +const EID_POWER_CAPABILITY = 33; +const EID_TPC_REQUEST = 34; +const EID_TPC_REPORT = 35; +const EID_SUPPORTED_CHANNELS = 36; +const EID_CHANNEL_SWITCH_ANN = 37; +const EID_MEAS_REQUEST = 38; +const EID_MEAS_REPORT = 39; +const EID_QUIET = 40; +const EID_ERP_INFO = 42; +const EID_TS_DELAY = 43; +const EID_TCLASS_PROC = 44; +const EID_HT_CAPABILITIES = 45; +const EID_QOS_CAPABILITY = 46; +const EID_RSN = 48; +const EID_EXT_SUPP_RATES = 50; +const EID_AP_CHAN_REPORT = 51; +const EID_NEIGHBOR_REPORT = 52; +const EID_RCPI = 53; +const EID_FT_MOBILITY_DOMAIN = 54; +const EID_FT_INFO = 55; +const EID_TIMEOUT_INTERVAL = 56; +const EID_FT_RIC_DATA = 57; +const EID_SUPPORTED_OPER_CLASSES = 59; +const EID_EXT_CHAN_SWITCH = 60; +const EID_HT_INFO = 61; +const EID_SEC_CHAN_OFFSET = 62; +const EID_RSNI = 65; +const EID_RRM_MEAS_PILOT_TX_INFO = 66; +const EID_WAPI = 68; +const EID_TIME_ADVERTISEMENT = 69; +const EID_RRM_ENABLED_CAPS = 70; +const EID_MULTIPLE_BSSID = 71; +const EID_20_40_BSS_COEXISTENCE = 72; +const EID_20_40_BSS_INTOLERANT_REPORT= 73; +const EID_OBSS_SCAN_PARAMETERS = 74; +const EID_FT_RIC_DESCRIPTOR = 75; +const EID_LINK_IDENTIFIER = 101; +const EID_PTI_CONTROL = 105; +const EID_PU_BUFFER_STATUS = 106; +const EID_QOS_MAP_SET = 110; +const EID_ESE_SPECIFIC = 150; +const EID_ESE_CCKM_SPECIFIC = 156; +const EID_ADDBA_EXTN_ELEMENT = 159; +const EID_VHT_CAPABILITIES = 191; +const EID_VHT_OPERATION_ELEMENT = 192; +const EID_VHT_EXT_BSS_LOAD = 193; +const EID_AID = 197; +const EID_EXT_CAP = 127; +const EID_OPERATING_MODE = 199; +const EID_WIDER_BW_CHANNEL_SWITCH_ANN= 194; +const VHT_TRANSMIT_POWER_ENVELOPE = 195; +const EID_CHANNEL_SWITCH_WRAPPER = 196; +const EID_VENDOR_SPECIFIC = 221; +const EID_FILS_INDICATION = 240; +const EID_FRAGMENT_IE = 242; +/** + * Extended Element ID + * + * As part of IEEE-802.11-2016 spec, extended element ID is introduced(9.4.2.1) + * Elements are defined to have a common general format consisting of a 1 octet + * Element ID field, a 1 octet Length field, an optional 1 octet Element ID + * Extension field, and a variable-length element-specific Information field. + * Each element is identified by the contents of the Element ID and, when + * present, Element ID Extension fields as defined in this standard. An Extended + * Element ID is a combination of an Element ID and an Element ID Extension for + * those elements that have a defined Element ID Extension. The Length field + * specifies the number of octets following the Length field. The presence of + * the Element ID Extension field is determined by the Element ID field having + * value of 255 + */ +const EID_EXTN_ID_ELEMENT = 255; + +const SIR_MAC_PROP_EXT_RATES_TYPE = 0; +const SIR_MAC_PROP_AP_NAME_TYPE = 1; +const SIR_MAC_PROP_HCF_TYPE = 2; +const SIR_MAC_PROP_WDS_TYPE = 3; +const SIR_MAC_PROP_BP_IND_TYPE = 4; +const SIR_MAC_PROP_NEIGHBOR_BSS_TYPE = 5; +const SIR_MAC_PROP_LOAD_INFO_TYPE = 6; +const SIR_MAC_PROP_ASSOC_TYPE = 7; +const SIR_MAC_PROP_LOAD_BALANCE_TYPE = 8; +const SIR_MAC_PROP_LL_ATTR_TYPE = 9; +const SIR_MAC_PROP_CAPABILITY = 10; +const SIR_MAC_PROP_VERSION = 11; +const SIR_MAC_PROP_EDCAPARAMS = 12; +const SIR_MAC_PROP_CHANNEL_SWITCH = 15; +const SIR_MAC_PROP_QUIET_BSS = 16; +const SIR_MAC_PROP_TRIG_STA_BK_SCAN = 17; + +const ANI_WDS_INFO_MAX_LENGTH = 64; +const SIR_MAC_MAX_NUMBER_OF_RATES = 12; +const HT_MAX_SUPPORTED_MCS_SET = 16; +const MAX_SUPPORTED_NEIGHBOR_RPT = 15; +const MAX_QOS_DSCP_DATA_LEN = 58; +const QOS_DSCP_RANGE_LEN = 16; + +///////////////////////////////////////////////////////////////////////////// +// Wi-Fi Protected Setup TLV Identifiers // +// WSC Version 2.0.0 Table 28 // +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// Wi-Fi Simple Configuration TLV Identifiers // +// WFA Vendor Extension Subelements // +///////////////////////////////////////////////////////////////////////////// +const TLV_VERSION2 = 0; +const TLV_AUTHORIZED_MAC = 1; +const TLV_NETWORK_KEY_SHAREABLE = 2; +const TLV_REQUEST_TO_ENROLL = 3; +const TLV_SETTINGS_DELAY_TIME = 4; + +const TLV_VERSION = 0x104A; +const TLV_WI_FI_SIMPLE_CONFIG_STATE = 0x1044; +const TLV_AP_SETUP_LOCKED = 0x1057; +const TLV_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053; +const TLV_DEVICE_PASSWORD_ID = 0x1012; +const TLV_UUID_E = 0x1047; +const TLV_UUID_R = 0x1048; +const TLV_RF_BANDS = 0x103C; +const TLV_REQUEST_TYPE = 0x103A; +const TLV_RESPONSE_TYPE = 0x103B; +const TLV_CONFIG_METHODS = 0x1008; +const TLV_PRIMARY_DEVICE_TYPE = 0x1054; +const TLV_ASSOCIATION_STATE = 0x1002; +const TLV_CONFIGURATION_ERROR = 0x1009; +const TLV_MANUFACTURER = 0x1021; +const TLV_MODEL_NAME = 0x1023; +const TLV_MODEL_NUMBER = 0x1024; +const TLV_SERIAL_NUMBER = 0x1042; +const TLV_DEVICE_NAME = 0x1011; +const TLV_SELECTED_REGISTRAR = 0x1041; +const TLV_VENDOR_EXTENSION = 0x1049; +const TLV_REQUESTED_DEVICE_TYPE = 0x106A; + +///////////////////////////////////////////////////////////////////////////// +// Wi-Fi Direct/P2P TLV Identifiers // +///////////////////////////////////////////////////////////////////////////// +const TLV_P2P_STATUS = 0; +const TLV_MINOR_REASON_CODE = 1; +const TLV_P2P_CAPABILITY = 2; +const TLV_P2P_DEVICE_ID = 3; +const TLV_P2P_GROUP_OWNER_INTENT = 4; +const TLV_CONFIGURATION_TIMEOUT = 5; +const TLV_LISTEN_CHANNEL = 6; +const TLV_P2P_GROUP_BSSID = 7; +const TLV_EXTENDED_LISTEN_TIMING = 8; +const TLV_INTENDED_P2P_INTERFACE_ADDRESS = 9; +const TLV_P2P_MANAGEABILITY = 10; +const TLV_CHANNEL_LIST = 11; +const TLV_NOTICE_OF_ABSENCE = 12; +const TLV_P2P_DEVICE_INFO = 13; +const TLV_P2P_GROUP_INFO = 14; +const TLV_P2P_GROUP_ID = 15; +const TLV_P2P_INTERFACE = 16; +const TLV_OPERATING_CHANNEL = 17; +const TLV_INVITATION_FLAGS = 18; +const TLV_P2P_VENDOR_SPECIFIC = 221; + + +///////////////////////////////////////////////////////////////////////////// +// MBO-OCE Attributes (0, 151-255: Reserved) // +///////////////////////////////////////////////////////////////////////////// +const TLV_MBO_AP_CAP_ATTR = 1; +const TLV_NON_PREFERRED_CHAN_REPORT_ATTR = 2; +const TLV_CELLULAR_DATA_CAP_ATTR = 3; +const TLV_ASSOC_DISSALLOWED_ATTR = 4; +const TLV_CELLULAR_DATA_CON_PREF_ATTR = 5; +const TLV_TRANSITION_REASON_CODE_ATTR = 6; +const TLV_TRANSITION_REJECT_REASON_CODE_ATTR = 7; +const TLV_ASSOC_RETRY_DELAY_ATTR = 8; + +// 9-100 : Reserved for MBO // + +// OCE ATTRIBUTES // +const TLV_OCE_CAP_IND_ATTR = 101; +const TLV_RSSI_ASSOC_REJ_ATTR = 102; +const TLV_REDUCED_WAN_METRICS_ATTR = 103; +// 104-150 : Reserved for OCE + + + +///////////////////////////////////////////////////////////////////////////// +// Fixed Fields + +FF AuthAlgo (2) // C.f. Sec. 7.3.1.1 +{ + algo, 2; +} + +FF AuthSeqNo (2) // 7.3.1.2 +{ + no, 2; +} + +FF BeaconInterval (2) // 7.3.1.3 +{ + interval, 2; +} + +FF Capabilities (2) // 7.3.1.4 +{ + { + ess: 1; + ibss: 1; + cfPollable: 1; + cfPollReq: 1; + privacy: 1; + shortPreamble: 1; + pbcc: 1; + channelAgility: 1; + spectrumMgt: 1; + qos: 1; + shortSlotTime: 1; + apsd: 1; + rrm: 1; + dsssOfdm: 1; + delayedBA: 1; + immediateBA: 1; + } +} + +FF CurrentAPAddress(6) // 7.3.1.5 +{ + mac[6]; +} + +FF ListenInterval (2) // 7.3.1.6 +{ + interval, 2; +} + +FF Reason (2) // 7.3.1.7 +{ + code, 2; +} + +FF AID (2) // 7.3.1.8 +{ + associd, 2; +} + +FF Status (2) // 7.3.1.9 +{ + status, 2; +} + +FF TimeStamp (8) // 7.3.1.10 +{ + timestamp, 8; +} + +FF Category (1) // 7.3.1.11 +{ + category, 1; +} + +FF Action (1) // 7.3.1.11 +{ + action, 1; +} + +FF TransactionId (2) // 7.3.1.11 +{ + transId[2]; +} + +FF DialogToken (1) // 7.3.1.12 +{ + token, 1; +} + +FF StatusCode (1) // WMM Spec 2.2.10 +{ + statusCode, 1; +} + +FF p2p_action_oui (4) +{ + oui_data[4]; +} + +FF p2p_action_subtype (1) +{ + subtype, 1; +} + +FF OperatingMode (1) +{ + { + //Operating Mode field + chanWidth: 2; + reserved: 2; + rxNSS: 3; + rxNSSType: 1; + } +} + +FF SMPowerModeSet (1) //7.3.1.25 +{ + { + PowerSave_En: 1; + Mode: 1; + reserved: 6; + } +} + +FF TSInfo (3) // 7.3.2.30 +{ + { + traffic_type: 1; + tsid: 4; + direction: 2; + access_policy: 2; + aggregation: 1; + psb: 1; + user_priority: 3; + tsinfo_ack_pol: 2; + schedule: 1; + unused: 15; + } +} + +FF NumOfRepetitions (2) +{ + repetitions, 2; +} + +FF TxPower (1) +{ + txPower, 1; +} + +FF MaxTxPower (1) +{ + maxTxPower, 1; +} +FF TPCEleID (1) +{ + TPCId, 1; +} +FF TPCEleLen (1) +{ + TPCLen, 1; +} +FF LinkMargin (1) +{ + linkMargin, 1; +} +FF RxAntennaId (1) +{ + antennaId, 1; +} +FF TxAntennaId (1) +{ + antennaId, 1; +} +FF RCPI (1) +{ + rcpi, 1; +} +FF RSNI (1) +{ + rsni, 1; +} + +FF VhtMembershipStatusArray(8) // 8.4.1.51 +{ + membershipStatusArray[8]; +} + +FF VhtUserPositionArray(16) // 8.4.1.52 +{ + userPositionArray[16]; +} + +FF ext_chan_switch_ann_action(4) +{ + { + switch_mode: 8; + op_class: 8; + new_channel: 8; + switch_count: 8; + } +} + +FF addba_param_set(2) +{ + { + amsdu_supp: 1; + policy: 1; + tid: 4; + buff_size: 10; + } +} + +FF ba_timeout(2) +{ + timeout, 2; +} + +FF ba_start_seq_ctrl(2) +{ + { + frag_number: 4; + ssn: 12; + } +} + +FF vendor_oui (3) +{ + oui_data[3]; +} + +FF vendor_action_subtype (1) +{ + subtype, 1; +} + +IE addba_extn_element(EID_ADDBA_EXTN_ELEMENT) +{ + { + no_fragmentation: 1; + he_frag_operation: 2; + reserved: 5; + } +} + +FF delba_param_set(2) +{ + { + reserved: 11; + initiator: 1; + tid: 4; + } +} + +///////////////////////////////////////////////////////////////////////////// +// TLVs // +///////////////////////////////////////////////////////////////////////////// + +/** + * \brief Version + * + * WPS 1.0h + * Version specifies the Easy Setup version. The one-byte field is broken + * into a four-bit major part using the top MSBs and four-bit minor part + * using the LSBs. As an example, version 3.2 would be 0x32. + * + * WSC 2.0.0 + * Deprecated Version mechanism. This attribute is always set to value 0x10 + * (version 1.0) for backwards compatibility. Version 1.0h of the specification + * did not fully describe the version negotiation mechanism and version 2.0 + * introduced a new subelement (Version2) for indicating the version number + * to avoid potential interoperability issues with deployed 1.0h-based devices. + * + */ + +TLV Version( TLV_VERSION ) ( 2 : 2 ) MSB +{ + { + minor: 4; + major: 4; + } +} + +/// Wi-Fi Protected Setup State +TLV WPSState( TLV_WI_FI_SIMPLE_CONFIG_STATE ) ( 2 : 2 ) MSB +{ + state, 1; +} + +/** + * \brief AP Setup Locked + * + * + * This variable indicates that the AP has entered a state in which it will + * refuse to allow an external Registrar to attempt to run the Registration + * Protocol using the AP?s PIN (with the AP acting as Enrollee). The AP + * should enter this state if it believes a brute force attack is underway + * against the AP?s PIN. + * + * When the AP is in this state, it MUST continue to allow other Enrollees to + * connect and run the Registration Protocol with any external Registrars or + * the AP's built-in Registrar (if any). It is only the use of the AP' PIN + * for adding external Registrars that is disabled in this state. + * + * The AP Setup Locked state can be reset to FALSE through an authenticated + * call to SetAPSettings. APs may provide other implementation-specific + * methods of resetting the AP Setup Locked state as well. + * + * + */ + +TLV APSetupLocked( TLV_AP_SETUP_LOCKED ) ( 2 : 2 ) MSB +{ + fLocked, 1; +} + +/** + * \brief Selected Registrar Config Methods + * + * + * This attribute has the same values that Config Methods have. It is used in + * Probe Response messages to convey the Config Methods of the selected + * Registrar. + * + * + */ + +TLV SelectedRegistrarConfigMethods ( TLV_SELECTED_REGISTRAR_CONFIG_METHODS ) ( 2 : 2 ) MSB +{ + methods, 2; +} + +/** + * \brief UUID-E + * + * + * The universally unique identifier (UUID) element is a unique GUID + * generated by the Enrollee. It uniquely identifies an operational device + * and should survive reboots and resets. The UUID is provided in binary + * format. If the device also supports UPnP, then the UUID corresponds to the + * UPnP UUID. + * + * + */ + +TLV UUID_E ( TLV_UUID_E ) ( 2 : 2 ) MSB +{ + uuid[ 16 ]; +} + +/** + * \brief UUID-R + * + * + * The universally unique identifier (UUID) element is a unique GUID + * generated by the Registrar. It uniquely identifies an operational device + * and should survive reboots and resets. The UUID is provided in binary + * format. If the device also supports UPnP, then the UUID corresponds to the + * UPnP UUID. + * + * + */ + +TLV UUID_R ( TLV_UUID_R ) ( 2 : 2 ) MSB +{ + uuid[ 16 ]; +} + +/** + * \brief RF Bands + * + * + \code + + 0x01 2.4GHz + 0x02 5.0GHz + + \endcode + * + * + */ + +TLV RFBands ( TLV_RF_BANDS ) ( 2 : 2 ) MSB +{ + bands, 1; +} + + +/** + * \brief Selected Registrar + * + * + * This field indicates that a Registrar has been selected by a user and that + * an Enrollee should proceed with setting up an 802.1X uncontrolled data + * port with the Registrar. + * + * + */ + +TLV SelectedRegistrar ( TLV_SELECTED_REGISTRAR ) ( 2 : 2 ) MSB +{ + selected, 1; +} + +/** + * \brief Config Methods + * + * + * The Config Methods Data component lists the configuration methods the + * Enrollee or Registrar supports. The list is a bitwise OR of values from + * the table below. In addition to Config Methods, APs and STAs that support + * the UPnP Management Interface must support the Permitted Config Methods + * attribute, which is used to control the Config Methods that are enabled on + * that AP. + * + \code + + Value Hardware Interface + 0x0001 USBA (Flash Drive) + 0x0002 Ethernet + 0x0004 Label + 0x0008 Display + 0x0010 External NFC Token + 0x0020 Integrated NFC Token + 0x0040 NFC Interface + 0x0080 PushButton + 0x0100 Keypad + + \endcode + * + * + */ + +TLV ConfigMethods ( TLV_CONFIG_METHODS ) ( 2 : 2 ) MSB +{ + methods, 2; +} + +/** + * \brief Association State + * + * + * The Association State component shows the configuration and previous + * association state of the wireless station when sending a Discovery + * request. + * + \code + + Association State Description + 0 Not Associated + 1 Connection Success + 2 Configuration Failure + 3 Association Failure + 4 IP Failure + + \endcode + * + * + */ + +TLV AssociationState ( TLV_ASSOCIATION_STATE ) ( 2 : 2 ) MSB +{ + state, 2; +} + +/** + * \brief Configuration Error + * + * + * The Configuration Error component shows the result of the device + * attempting to configure itself and to associate with the WLAN. + * + \code + + Configuration Error Description + 0 No Error + 1 OOB Interface Read Error + 2 Decryption CRC Failure + 3 2.4 channel not supported + 4 5.0 channel not supported + 5 Signal too weak + 6 Network auth failure + 7 Network association failure + 8 No DHCP response + 9 Failed DHCP config + 10 IP address conflict + 11 Couldn't connect to Registrar + 12 Multiple PBC sessions detected + 13 Rogue activity suspected + 14 Device busy + 15 Setup locked + 16 Message Timeout + 17 Registration Session Timeout + 18 Device Password Auth Failure + + \endcode + * + * The Device busy error is returned if the sending device is unable to + * respond to the request due to some internal conflict or resource + * contention issue. For example, if a device is only capable of performing a + * single instance of the Registration Protocol at a time, it may return this + * error in response to attempts to start another instance in the middle of + * an active session. + * + * + */ + +TLV ConfigurationError ( TLV_CONFIGURATION_ERROR ) ( 2 : 2 ) MSB +{ + error, 2; +} + +TLV Manufacturer ( TLV_MANUFACTURER ) ( 2 : 2 ) MSB +{ + name[ 0..64 ]; +} + +TLV ModelName ( TLV_MODEL_NAME ) ( 2 : 2 ) MSB +{ + text[ 0..32 ]; +} + +TLV ModelNumber ( TLV_MODEL_NUMBER ) ( 2 : 2 ) MSB +{ + text[ 0..32 ]; +} + +TLV SerialNumber ( TLV_SERIAL_NUMBER ) ( 2 : 2 ) MSB +{ + text[ 0..32 ]; +} + +TLV DeviceName ( TLV_DEVICE_NAME ) ( 2 : 2 ) MSB +{ + text[ 0..32 ]; +} + +/** + * \brief Device Password ID + * + * + * This attribute is used to identify a device password. There are six + * predefined values and ten reserved values. If the Device Password ID is + * Default, the Enrollee should use its PIN password (from the label or + * display). This password may correspond to the label, display, or a + * user-defined password that has been configured to replace the original + * device password. + * + * User-specified indicates that the user has overridden the password with a + * manually selected value. Machine-specified indicates that the original + * PIN password has been overridden by a strong, machinegenerated device + * password value. The Rekey value indicates that the device's 256-bit + * rekeying password will be used. The PushButton value indicates that the + * PIN is the all-zero value reserved for the PushButton Configuration + * method. + * + * The Registrar-specified value indicates a PIN that has been obtained from + * the Registrar (via a display or other out-of-band method). This value may + * be further augmented with the optional 'Identity' attribute in M1. This + * augmentation is useful when multiple predefined UserID/PIN pairs have been + * established by a Registrar such as an authenticator used for Hotspot + * access. If the Device Password ID in M1 is not one of the predefined or + * reserved values, it corresponds to a password given to the Registrar as an + * OOB Device Password. + * + \code + + Value Description + + 0x0000 Default (PIN) + 0x0001 User-specified + 0x0002 Machine-specified + 0x0003 Rekey + 0x0004 PushButton + 0x0005 Registrar-specified + 0x0006 - 0x000F Reserved + + \endcode + * + * + */ + +TLV DevicePasswordID ( TLV_DEVICE_PASSWORD_ID ) ( 2 : 2 ) MSB +{ + id, 2; +} + + +/** + * \brief Primary Device Type + * + * + * This attribute contains the primary type of the device. Its format + * follows: + * + \code + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Attribute ID | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Category ID | OUI (1-2) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | OUI (3-4) | Sub Category ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + \endcode + * + * Vendor-specific sub-categories are designated by setting the OUI to the + * value associated with that vendor. Note that a four-byte subdivided OUI + * is used. For the predefined values, the Wi-Fi Alliance OUI of 00 50 F2 04 + * is used. The predefined values for Category ID and Sub Category ID are + * provided in the next table. There is no way to indicate a vendor-specific + * main device category. The OUI applies only to the interpretation of the + * Sub Category. If a vendor does not use sub categories for their OUI, the + * three-byte OUI occupies the first three bytes of the OUI field and the + * fourth byte is set to zero. + * + * + \code + + Category ID Value Sub Category ID Value + Computer 1 PC 1 + Server 2 + Media Center 3 + Input Device 2 + Printers, Scanners, Printer 1 + Faxes and Copiers 3 Scanner 2 + Camera 4 Digital Still Camera 1 + Storage 5 NAS 1 + Network AP 1 + Infrastructure 6 Router 2 + Switch 3 + Displays 7 Television 1 + Electronic Picture Frame 2 + Projector 3 + Multimedia Devices 8 DAR 1 + PVR 2 + MCX 3 + Gaming Devices 9 Xbox 1 + Xbox360 2 + Playstation 3 + Telephone 10 Windows Mobile 1 + + \endcode + * + * + */ + +TLV PrimaryDeviceType ( TLV_PRIMARY_DEVICE_TYPE ) ( 2 : 2 ) MSB +{ + primary_category, 2; + oui[ 4 ]; + sub_category, 2; +} + + +/** + * \brief Request Type + * + * + * The Request Type component specifies the mode in which the device will + * operate in for this setup exchange. If the device is an Enrollee, it may + * send only discovery messages or it may also request that the Registrar + * proceed with opening a data connection. This protocol allows Enrollees to + * more efficiently discover devices on the network. + + * If the device indicates that it intends to engage setup either as a + * Registrar or an Enrollee, the Access Point continues to indicate that it + * will operate as an AP in the response. The Request Type attribute is + * carried throughout the 802.1X data channel setup process in the Wi-Fi + * Protected Setup IE. There are two sub-types of Registrars: WLAN Manager + * Registrar indicates that this Registrar intends to manage the AP or STA + * settings using UPnP. It will derive a UPnP AP or STA Management key. The + * ordinary Registrar type indicates that this Registrar does not intend to + * subsequently manage the Enrollee's settings. APs must not derive AP + * Management Keys for an ordinary Registrar. If a Registrar does not intend + * to be a WLAN Manager Registrar, it should set the Request Type to + * Registrar. Doing so avoids needlessly consuming resources on the AP. + + \code + + Request Type Value Description + 0x00 Enrollee, Info only + 0x01 Enrollee, open 802.1X + 0x02 Registrar + 0x03 WLAN Manager Registrar + + \endcode + * + * + */ + +TLV RequestType ( TLV_REQUEST_TYPE ) ( 2 : 2 ) MSB +{ + reqType, 1; +} + +/** + * \brief Response Type + * + * + * The Response Type component specifies the operational mode of the + * device for this setup exchange. The Response Type IE is carried + * throughout the 802.1X data channel setup process. + + \code + + Response Type Value Description + 0x00 Enrollee, Info only + 0x01 Enrollee, open 802.1X + 0x02 Registrar + 0x03 AP + +\endcode + * + * + */ + +TLV ResponseType ( TLV_RESPONSE_TYPE ) ( 2 : 2 ) MSB +{ + resType, 1; +} + + +/////////////////////////////////////////////////////////////////////////// +// WiFi Direct/P2P TLVs // +/////////////////////////////////////////////////////////////////////////// + +/** + * \brief P2P Status Attribute + */ + +TLV P2PStatus ( TLV_P2P_STATUS ) ( 1 : 2 ) LSB +{ + status, 1; +} + + +/** + * \brief Minor Reason Code Attribute + */ + +TLV MinorReasonCode ( TLV_MINOR_REASON_CODE ) ( 1 : 2 ) LSB +{ + minorReasonCode, 1; +} + + +/** + * \brief P2P Capability Attribute + */ + +TLV P2PCapability ( TLV_P2P_CAPABILITY ) ( 1 : 2 ) LSB +{ + deviceCapability, 1; + groupCapability, 1; +} + + +/** + * \brief P2P Device Id Attribute + */ + +TLV P2PDeviceId ( TLV_P2P_DEVICE_ID ) ( 1 : 2 ) LSB +{ + P2PDeviceAddress[6]; +} + +/** + * \brief Listen Channel Attribute + */ + +TLV ListenChannel ( TLV_LISTEN_CHANNEL ) ( 1 : 2 ) LSB +{ + countryString[3]; + regulatoryClass, 1; + channel, 1; +} + +/** + * \brief Extended Listen Attribute + */ + +TLV ExtendedListenTiming ( TLV_EXTENDED_LISTEN_TIMING ) ( 1 : 2 ) LSB +{ + availibilityPeriod, 2; + availibilityInterval, 2; +} + + +/** + * \brief P2P Manageability Attribute + */ + +TLV P2PManageability ( TLV_P2P_MANAGEABILITY ) ( 1 : 2 ) LSB +{ + manageability, 1; +} + + +/** + * \brief Notice of Absence + */ + +TLV NoticeOfAbsence ( TLV_NOTICE_OF_ABSENCE ) ( 1 : 2 ) LSB +{ + index, 1; + CTSWindowOppPS, 1; + NoADesc[0..36]; +} + +/** + * \brief P2P Device Info Attribute + */ + +TLV P2PDeviceInfo ( TLV_P2P_DEVICE_INFO ) ( 1 : 2 ) LSB +{ + P2PDeviceAddress[6]; + configMethod, 2 , FLIPBYTEORDER; + primaryDeviceType[8]; + MANDATORYTLV DeviceName; +} + + +/** + * \brief P2P Group Info Attribute + */ + +TLV P2PGroupInfo ( TLV_P2P_GROUP_INFO ) ( 1 : 2 ) LSB +{ + P2PClientInfoDesc[0..1024]; +} + + +/** + * \brief P2P Interface Attribute + */ + +TLV P2PInterface ( TLV_P2P_INTERFACE ) ( 1 : 2 ) LSB +{ + P2PDeviceAddress[6]; +} + + +/** + * \brief Operating Channel Attribute + */ + +TLV OperatingChannel ( TLV_OPERATING_CHANNEL ) ( 1 : 2 ) LSB +{ + countryString[3]; + regulatoryClass, 1; + channel, 1; +} + +/////////////////////////////////////////////////////////////////////////// +// MBO-OCE ATTR TLVs // +/////////////////////////////////////////////////////////////////////////// + +TLV mbo_ap_cap ( TLV_MBO_AP_CAP_ATTR ) ( 1 : 1 ) LSB +{ + mbo_cap_ind, 1; +} + +TLV non_prefferd_chan_rep ( TLV_NON_PREFERRED_CHAN_REPORT_ATTR ) ( 1 : 1 ) LSB +{ + oper_class, 1; + channel_report[3..254]; +} + +TLV cellular_data_cap ( TLV_CELLULAR_DATA_CAP_ATTR ) ( 1 : 1 ) LSB +{ + cellular_connectivity, 1; +} + +TLV assoc_disallowed ( TLV_ASSOC_DISSALLOWED_ATTR ) ( 1 : 1 ) LSB +{ + reason_code, 1; +} + +TLV cellular_data_con_pref ( TLV_CELLULAR_DATA_CON_PREF_ATTR ) ( 1 : 1 ) LSB +{ + cellular_preference, 1; +} + +TLV transition_reason ( TLV_TRANSITION_REASON_CODE_ATTR ) ( 1 : 1 ) LSB +{ + transition_reason_code, 1; +} + +TLV transition_reject_reason ( TLV_TRANSITION_REJECT_REASON_CODE_ATTR ) ( 1 : 1 ) LSB +{ + transition_reject_code, 1; +} + +TLV assoc_retry_delay ( TLV_ASSOC_RETRY_DELAY_ATTR ) ( 1 : 1 ) LSB +{ + delay, 2; +} + +// OCE Attributes // + +TLV oce_cap ( TLV_OCE_CAP_IND_ATTR ) ( 1 : 1 ) LSB +{ + { + oce_release: 3; + is_sta_cfon : 1; + non_oce_ap_present : 1; + reserved: 3; + } +} + +TLV rssi_assoc_rej ( TLV_RSSI_ASSOC_REJ_ATTR ) ( 1 : 1 ) LSB +{ + delta_rssi, 1; + retry_delay, 1; +} + +TLV reduced_wan_metrics ( TLV_REDUCED_WAN_METRICS_ATTR ) ( 1 : 1 ) LSB +{ + { + downlink_av_cap: 4; + uplink_av_cap : 4; + } +} + +/** + * \brief Vendor Extension + * + * This variable permits vendor extensions in the Wi-Fi Simple + * Configuration TLV framework. The Vendor Extension figure + * illustrates the implementation of vendor extensions. Vendor + * ID is the SMI network management private enterprise code + * + * +-----------+----------------------+ + * | Vendor ID | Vendor Data | + * +-----------+----------------------+ + * |<--- 3 --->|<----- 1 - 1021 ----->| + * + */ + +TLV VendorExtension ( TLV_VENDOR_EXTENSION ) ( 2 : 2 ) MSB +{ + /* + * vendorId is the SMI network management private enterprise code. + * WFA Vendor ID 0x00372A + * + */ + vendorId[ 3 ]; + + /** + * \breif Version2 + * + * The Version2 field specifies the version Wi-Fi Simple + * Configuration implemented by the device sending this attribute. + * The one-byte field is broken into a four-bit major part using + * the top MSBs and four-bit minor part using the LSBs. As an example, + * version 3.2 would be 0x32. This subelement was added in the + * specification version 2.0 and if the subelement is not included + * in a message, the transmitter of the message is assumed to + * use version 1.0. + * + */ + OPTIONALTLV TLV Version2 ( TLV_VERSION2 ) ( 1 : 1 ) MSB + { + { + minor: 4; + major: 4; + } + } + /** + * \brief AuthorizedMACs + * + * This subelement contains a list of Enrollee MAC addresses (each + * being six bytes in length) that have been registered to start WSC. + * The AP includes this field in Beacon and Probe Response frames so + * Enrollees can tell if they have been registered to start WSC. There + * may be multiple Enrollees active on the network, but not all of them have + * been registered to start WSC. This element allows an Enrollee to detect + * if they should start WSC with the AP. The AuthorizedMACs field augments + * the use of the Selected Registrar. + * + */ + OPTIONALTLV TLV AuthorizedMACs ( TLV_AUTHORIZED_MAC ) ( 1 : 1 ) MSB + { + mac[6]; + } + + /** + * \brief Request to Enroll + * + * This optional subelement in the WSC IE in Probe Request or M1 indicates + * the desire to enroll in the network by setting its value to TRUE. If the + * Registrar gets this subelement it can use this as a trigger that a device + * wants to enroll (maybe an indication can be shown to the user). The device + * must set it to FALSE after the registration protocol completion. + * + */ + OPTIONALTLV TLV RequestToEnroll( TLV_REQUEST_TO_ENROLL ) ( 1 : 1 ) MSB + { + req, 1; + } +} + +/** + * \brief Requested Device Type + * + * This attribute contains the requested device type of a Wi-Fi + * Direct device. + * + * This attribute allows a device to specify the Primary Device Type + * or the Secondary Device Type of other devices it is interested in. + * Only a device that receives a Probe Request containing a WSC IE with + * this attribute and with a Primary Device Type or Secondary Device Type + * that matches the Requested Device Type will respond with a Probe Response. + * + * Its format and contents is identical to the 'Primary Device Type'. + * + * Both the Category ID and Sub Category ID can be used as a filter. If only + * looking for devices with a certain Category ID, the OUI and Sub Category ID + * fields will have to be set to zero. + * + */ +TLV RequestDeviceType ( TLV_REQUESTED_DEVICE_TYPE ) ( 2 : 2 ) MSB +{ + primary_category, 2; + oui[ 4 ]; + sub_category, 2; +} + +///////////////////////////////////////////////////////////////////////////// +// Information Elements + +IE SSID (EID_SSID) // C.f. Sec. 7.3.2.1 +{ + ssid[0..32]; +} + +IE SuppRates (EID_SUPP_RATES) // 7.3.2.2 +{ + rates[0..SIR_MAC_MAX_NUMBER_OF_RATES]; +} + +IE FHParamSet (EID_FH_PARAM_SET) // 7.3.2.3 +{ + dwell_time, 2; + hop_set, 1; + hop_pattern, 1; + hop_index, 1; +} + +IE DSParams (EID_DS_PARAM_SET) // 7.3.2.4 +{ + curr_channel, 1; +} + +IE CFParams (EID_CF_PARAM_SET) // 7.3.2.5 +{ + cfp_count, 1; + cfp_period, 1; + cfp_maxduration, 2; + cfp_durremaining, 2; +} + +IE TIM (EID_TIM) // 7.3.2.6 +{ + dtim_count, 1; + dtim_period, 1; + bmpctl, 1; + vbmp[1..251]; +} + +IE IBSSParams (EID_IBSS_PARAM_SET) // 7.3.2.7 +{ + atim, 2; +} + +IE ChallengeText (EID_CHALLENGE_TEXT) // 7.3.2.8 +{ + text[1..253]; +} + +IE RequestedInfo (EID_REQUEST) // 7.3.2.12 +{ + requested_eids[0..255]; +} + +IE Country (EID_COUNTRY) // 7.3.2.9 +{ + country[3]; + OPTIONAL triplets[3][0..84]; +} + +IE FHParams (EID_FH_PATTERN) // 7.3.2.10 +{ + radix, 1; + nchannels, 1; +} + +IE FHPattTable (EID_FH_PATT_TABLE) // 7.3.2.11 +{ + flag, 1; + nsets, 1; + modulus, 1; + offset, 1; + randtable[0..251]; +} + +IE ERPInfo (EID_ERP_INFO) // 7.3.2.13 +{ + { + non_erp_present : 1; + use_prot: 1; + barker_preamble: 1; + unused: 5; + } +} + +IE ExtSuppRates (EID_EXT_SUPP_RATES) // 7.3.2.14 +{ + rates[1..SIR_MAC_MAX_NUMBER_OF_RATES]; +} + +IE PowerConstraints (EID_POWER_CONSTRAINTS) // 7.3.2.15 +{ + localPowerConstraints, 1; +} + +IE PowerCaps (EID_POWER_CAPABILITY) // 7.3.2.16 +{ + minTxPower, 1; + maxTxPower, 1; +} + +IE TPCRequest (EID_TPC_REQUEST) // 7.3.2.17 +{ } + +IE TPCReport (EID_TPC_REPORT) // 7.3.2.18 +{ + tx_power, 1; + link_margin, 1; +} + +IE SuppChannels (EID_SUPPORTED_CHANNELS) // 7.2.3.19 +{ + bands[2][0..48]; +} + +IE SuppOperatingClasses (EID_SUPPORTED_OPER_CLASSES) +{ + classes[1..32]; +} + +IE ChanSwitchAnn (EID_CHANNEL_SWITCH_ANN) // 7.3.2.20 +{ + switchMode, 1; + newChannel, 1; + switchCount, 1; +} + +IE ext_chan_switch_ann (EID_EXT_CHAN_SWITCH) // 8.4.2.55 +{ + switch_mode, 1; + new_reg_class, 1; + new_channel, 1; + switch_count, 1; +} + +IE sec_chan_offset_ele (EID_SEC_CHAN_OFFSET) // 7.3.2.20a +{ + secondaryChannelOffset, 1; +} + +IE Quiet (EID_QUIET) // 7.3.2.23 +{ + count, 1; + period, 1; + duration, 2; + offset, 2; +} + +IE RSN (EID_RSN) // 7.3.2.25 +{ + // The version is 2 octets, and we only support version 1. + version, 2 MUSTBE 1; + // The next four octets will be the Optional Group Cipher Suite + OPTIONAL gp_cipher_suite[4]; + // The IE *may* stop here; if there's any more, we should see two more + // octets giving the number of Pairwise Cipher Suites + OPTIONAL pwise_cipher_suite_count, 2; + // I don't see anything in the Standard limiting the number of Pairwise + // Cypher Suites, other than the maximum length of an IE, which limits us + // to 61. However, that seems needlessly wasteful of space. + pwise_cipher_suites[4][0..6] COUNTIS pwise_cipher_suite_count; + // Optional count of AKM suite selectors + OPTIONAL akm_suite_cnt, 2; + // Again, I see nothing in the Standard explicitly limiting the number of + // AKM suite selectors other than the maximum size of an IE. + akm_suite[4][0..6] COUNTIS akm_suite_cnt; + OPTIONAL RSN_Cap[2]; + // Finally, the IE may contain zero or more PMKIDs: + OPTIONAL pmkid_count, 2; + pmkid[16][0..4] COUNTIS pmkid_count; + OPTIONAL gp_mgmt_cipher_suite[4]; +} + +IE RSNOpaque (EID_RSN) // 7.3.2.25 +{ + data[ 0..253 ]; +} + +IE WAPI (EID_WAPI) // 7.3.2.25 +{ + // The version is 2 octets, and we only support version 1. + version, 2 MUSTBE 1; + // count of AKM suite selectors + akm_suite_count, 2; + // Again, I see nothing in the Standard explicitly limiting the number of + // AKM suite selectors other than the maximum size of an IE. + akm_suites[4][0..4] COUNTIS akm_suite_count; + // we should see two more + // octets giving the number of Unicast Cipher Suites + unicast_cipher_suite_count, 2; + // I don't see anything in the Standard limiting the number of Pairwise + // Cypher Suites, other than the maximum length of an IE, which limits us + // to 61. However, that seems needlessly wasteful of space. + unicast_cipher_suites[4][0..4] COUNTIS unicast_cipher_suite_count; + // The next four octets will be the Multicast Cipher Suite + multicast_cipher_suite[4]; + // WAPI capabilities + { + preauth: 1; + reserved: 15; + } + // Finally, the IE may contain zero or more BKIDs: + OPTIONAL bkid_count, 2; + bkid[16][0..4] COUNTIS bkid_count; +} + +IE WAPIOpaque (EID_WAPI) // 7.3.2.25 +{ + data[ 6..253 ]; +} + +IE QBSSLoad (EID_QBSS_LOAD) // 7.3.2.28 +{ + stacount, 2; + chautil, 1; + avail, 2; +} + +IE EDCAParamSet (EID_EDCA_PARAM_SET) // 7.3.2.29 +{ + qos, 1; // ToDo: This is a bitfield whose format + // depends on whether this is from an AP + // or a STA, information which I'm not + // sure we have at parse time... + reserved, 1; + { + acbe_aifsn: 4; + acbe_acm: 1; + acbe_aci: 2; + unused1: 1; + } + { + acbe_acwmin: 4; + acbe_acwmax: 4; + } + acbe_txoplimit, 2; + { + acbk_aifsn: 4; + acbk_acm: 1; + acbk_aci: 2; + unused2: 1; + } + { + acbk_acwmin: 4; + acbk_acwmax: 4; + } + acbk_txoplimit, 2; + { + acvi_aifsn: 4; + acvi_acm: 1; + acvi_aci: 2; + unused3: 1; + } + { + acvi_acwmin: 4; + acvi_acwmax: 4; + } + acvi_txoplimit, 2; + { + acvo_aifsn: 4; + acvo_acm: 1; + acvo_aci: 2; + unused4: 1; + } + { + acvo_acwmin: 4; + acvo_acwmax: 4; + } + acvo_txoplimit, 2; +} + +IE TSPEC (EID_TSPEC) // 7.3.2.30 +{ + + // TS Info + { + traffic_type: 1; + tsid: 4; + direction: 2; + access_policy: 2; + aggregation: 1; + psb: 1; + user_priority: 3; + tsinfo_ack_pol: 2; + } + { + schedule: 1; + unused: 7; + } + + // Nominal MSDU Size + { + size: 15; + fixed: 1; + } + + max_msdu_size, 2; + min_service_int, 4; + max_service_int, 4; + inactivity_int, 4; + suspension_int, 4; + service_start_time, 4; + min_data_rate, 4; + mean_data_rate, 4; + peak_data_rate, 4; + burst_size, 4; + delay_bound, 4; + min_phy_rate, 4; + surplus_bw_allowance, 2; + medium_time, 2; + +} // End IE TSPEC. + +IE TCLAS (EID_TCLAS) // 7.3.2.31 +{ + user_priority, 1; + classifier_type, 1; + classifier_mask, 1; + UNION info (DISCRIMINATOR classifier_type) + { + EthParams (classifier_type IS 0) + { + source[6]; + dest[6]; + type, 2; + } + IpParams (classifier_type IS 1) + { + version, 1; + UNION params (DISCRIMINATOR version) + { + IpV4Params (version IS 4) + { + source[4]; + dest[4]; + src_port, 2; + dest_port, 2; + DSCP, 1; + proto, 1; + reserved, 1; + } + IpV6Params (version IS 6) + { + source[16]; + dest[16]; + src_port, 2; + dest_port, 2; + flow_label[3]; + } + }; + } + Params8021dq (classifier_type IS 2) + { + tag_type, 2; + } + }; +} // End IE TCLASS + +const EID_BCN_REPORT_FRAME_BODY = 1; +IE BeaconReportFrmBody (EID_BCN_REPORT_FRAME_BODY) +{ + reportedFields[0..224]; +} + +const EID_BCN_REPORT_FRAME_BODY_FRAGMENT_ID = 2; +IE beacon_report_frm_body_fragment_id (EID_BCN_REPORT_FRAME_BODY_FRAGMENT_ID) +{ + // Data + { + beacon_report_id: 8; + fragment_id_number: 7; + more_fragments: 1; + } +} + +const EID_BCN_REPORT_LAST_BEACON_REPORT_INDICATION = 164; +IE last_beacon_report_indication (EID_BCN_REPORT_LAST_BEACON_REPORT_INDICATION) +{ + last_fragment, 1; +} + +IE MeasurementReport (EID_MEAS_REPORT) // 7.3.2.22 +{ + token, 1; + // Measurement Report Mode + { + late: 1; + incapable: 1; + refused: 1; + unused: 5; + } + type, 1; + OPTIONAL UNION report (DISCRIMINATOR type) + { + Basic (type IS 0) // 7.3.2.22.1 + { + channel, 1; + meas_start_time, 8; + meas_duration, 2; + // Map + { + bss: 1; + ofdm_preamble: 1; + unid_signal: 1; + rader: 1; + unmeasured: 1; + unused: 3; + } + } + CCA (type IS 1) + { + channel, 1; + meas_start_time, 8; + meas_duration, 2; + cca_busy_fraction, 1; + } + RPIHistogram (type IS 2) + { + channel, 1; + meas_start_time, 8; + meas_duration, 2; + rpi0_density, 1; + rpi1_density, 1; + rpi2_density, 1; + rpi3_density, 1; + rpi4_density, 1; + rpi5_density, 1; + rpi6_density, 1; + rpi7_density, 1; + } + Beacon (type IS 5) + { + regClass, 1; + channel, 1; + meas_start_time, 8; + meas_duration, 2; + // reported_frame_info, + { + condensed_PHY: 7; + reported_frame_type: 1; + } + RCPI, 1; + RSNI, 1; + BSSID[6]; + antenna_id, 1; + parent_TSF, 4; + OPTIE BeaconReportFrmBody; + OPTIE beacon_report_frm_body_fragment_id; + OPTIE last_beacon_report_indication; + //IE vendor_specific + } + }; +} + +IE TSDelay (EID_TS_DELAY) // 7.3.2.32 +{ + delay, 4; +} + +IE TCLASSPROC (EID_TCLASS_PROC) // 7.3.2.33 +{ + processing, 1; +} + +IE Schedule (EID_SCHEDULE) // 7.3.2.34 +{ + { + aggregation: 1; + tsid: 4; + direction: 2; + reserved: 9; + } + service_start_time, 4; + service_interval, 4; + max_service_dur, 2; + spec_interval, 2; +} + +IE QOSCapsAp (EID_QOS_CAPABILITY) // 7.3.2.35 +{ + { + count: 4; + qack: 1; + qreq: 1; + txopreq: 1; + reserved: 1; + } +} + +IE QOSCapsStation (EID_QOS_CAPABILITY) // 7.3.2.35 +{ + { + acvo_uapsd: 1; + acvi_uapsd: 1; + acbk_uapsd: 1; + acbe_uapsd: 1; + qack: 1; + max_sp_length: 2; + more_data_ack: 1; + } +} + +IE LinkIdentifier (EID_LINK_IDENTIFIER) // 7.3.2.62 +{ + bssid[6]; + InitStaAddr[6]; + RespStaAddr[6]; +} + +IE WPA (EID_VENDOR_SPECIFIC) OUI (0x00, 0x50, 0xF2, 0x01) +{ + // This IE's first two octets should be interpreted as a version number; + // we only support version 1. + version, 2 MUSTBE 1; + // A four-octet Multicast Cipher may or may not appear next (hence the + // OPTIONAL keyword) + OPTIONAL multicast_cipher[4]; + // Optional Unicast Cipher count + OPTIONAL unicast_cipher_count, 2; + // Next comes an array of four-octet Cipher Suite selectors; the COUNTIS + // clause indicates that the actual number of selectors seen is in the + // member 'unicast_cipher_count'. + unicast_ciphers[4][0..4] COUNTIS unicast_cipher_count; + // (Optional) Authentication suites: + OPTIONAL auth_suite_count, 2; + auth_suites[4][0..4] COUNTIS auth_suite_count; + // This field is declared optional as per bugs 15234, 14755, & 14991. + OPTIONAL caps, 2; +} + +IE WPAOpaque (EID_VENDOR_SPECIFIC) OUI (0x00, 0x50, 0xF2, 0x01) +{ + data[ 2..249 ]; +} + +IE WMMInfoStation (EID_VENDOR_SPECIFIC) OUI(0x00, 0x50, 0xF2, 0x02, 0x00) +{ + // This IE contains the QoS Info field when sent from WMM Station + version, 1; + { + acvo_uapsd: 1; + acvi_uapsd: 1; + acbk_uapsd: 1; + acbe_uapsd: 1; + reserved1: 1; + max_sp_length: 2; + reserved2: 1; + } +} + +IE WMMInfoAp (EID_VENDOR_SPECIFIC) OUI(0x00, 0x50, 0xF2, 0x02, 0x00) +{ + // This IE contains the QoS Info field when sent from WMM AP + version, 1; + { + param_set_count: 4; + reserved: 3; + uapsd: 1; + } +} + + +IE WMMParams (EID_VENDOR_SPECIFIC) OUI(0x00, 0x50, 0xF2, 0x02, 0x01) +{ + version, 1 MUSTBE 1; + qosInfo, 1; // ToDo: This is actually a + // bitfield, but it's format + // varies depending on whether + // the sender is a STA or AP... + reserved2, 1; + { + acbe_aifsn: 4; + acbe_acm: 1; + acbe_aci: 2; + unused1: 1; + } + { + acbe_acwmin: 4; + acbe_acwmax: 4; + } + acbe_txoplimit, 2; + { + acbk_aifsn: 4; + acbk_acm: 1; + acbk_aci: 2; + unused2: 1; + } + { + acbk_acwmin: 4; + acbk_acwmax: 4; + } + acbk_txoplimit, 2; + { + acvi_aifsn: 4; + acvi_acm: 1; + acvi_aci: 2; + unused3: 1; + } + { + acvi_acwmin: 4; + acvi_acwmax: 4; + } + acvi_txoplimit, 2; + { + acvo_aifsn: 4; + acvo_acm: 1; + acvo_aci: 2; + unused4: 1; + } + { + acvo_acwmin: 4; + acvo_acwmax: 4; + } + acvo_txoplimit, 2; +} + +IE WMMTSPEC (EID_VENDOR_SPECIFIC) OUI (0x00, 0x50, 0xf2, 0x02, 0x02) +{ + version, 1 MUSTBE 1; + + // TS Info + { + traffic_type: 1; + tsid: 4; + direction: 2; + access_policy: 2; + aggregation: 1; + psb: 1; + user_priority: 3; + tsinfo_ack_pol: 2; + } + { + tsinfo_rsvd: 7; + burst_size_defn: 1; + } + + // Nominal MSDU Size + { + size: 15; + fixed: 1; + } + + max_msdu_size, 2; + min_service_int, 4; + max_service_int, 4; + inactivity_int, 4; + suspension_int, 4; + service_start_time, 4; + min_data_rate, 4; + mean_data_rate, 4; + peak_data_rate, 4; + burst_size, 4; + delay_bound, 4; + min_phy_rate, 4; + surplus_bw_allowance, 2; + medium_time, 2; + +} // End IE WMMTSpec. + +IE WMMCaps (EID_VENDOR_SPECIFIC) OUI (0x00, 0x50, 0xF2, 0x02, 0x05) +{ + version, 1 MUSTBE 1; + { + reserved: 4; + qack: 1; + queue_request: 1; + txop_request: 1; + more_ack: 1; + } +} + +IE WMMTCLAS (EID_VENDOR_SPECIFIC) OUI (0x00, 0x50, 0xF2, 0x02, 0x06) +{ + version, 1 MUSTBE 1; + + user_priority, 1; + classifier_type, 1; + classifier_mask, 1; + UNION info (DISCRIMINATOR classifier_type) + { + EthParams (classifier_type IS 0) + { + source[6]; + dest[6]; + type, 2; + } + IpParams (classifier_type IS 1) + { + version, 1; + UNION params (DISCRIMINATOR version) + { + IpV4Params (version IS 4) + { + source[4]; + dest[4]; + src_port, 2; + dest_port, 2; + DSCP, 1; + proto, 1; + reserved, 1; + } + IpV6Params (version IS 6) + { + source[16]; + dest[16]; + src_port, 2; + dest_port, 2; + flow_label[3]; + } + }; + } + Params8021dq (classifier_type IS 2) + { + tag_type, 2; + } + }; + +} + +IE WMMTCLASPROC (EID_VENDOR_SPECIFIC) OUI (0x00, 0x50, 0xF2, 0x02, 0x07) +{ + version, 1 MUSTBE 1; + processing, 1; +} + +IE WMMTSDelay (EID_VENDOR_SPECIFIC) OUI (0x00, 0x50, 0xF2, 0x02, 0x08) +{ + version, 1 MUSTBE 1; + delay, 4; +} + +IE WMMSchedule (EID_VENDOR_SPECIFIC) OUI (0x00, 0x50, 0xF2, 0x02, 0x09) +{ + version, 1 MUSTBE 1; + + { + aggregation: 1; + tsid: 4; + direction: 2; + reserved: 9; + } + + service_start_time, 4; + service_interval, 4; + max_service_dur, 2; + spec_interval, 2; +} + +IE ESERadMgmtCap (EID_VENDOR_SPECIFIC) OUI (0x00, 0x40, 0x96, 0x01) +{ + + mgmt_state, 1; + + { + mbssid_mask: 3; + reserved: 5; + } + +} + +IE BeaconReportStatus (EID_VENDOR_SPECIFIC) OUI (0x00, 0x00, 0x00F, 0x22) +{ + sub_type, 1; + version, 1; + length, 1; + reason_code, 1; +} + +IE Vendor1IE (EID_VENDOR_SPECIFIC) OUI (0x00, 0x10, 0x18) +{ +} + +IE Vendor3IE (EID_VENDOR_SPECIFIC) OUI (0x00, 0x16, 0x32) +{ +} + +IE hs20vendor_ie (EID_VENDOR_SPECIFIC) OUI (0x50, 0x6F, 0x9A, 0x10) +{ + /* hotspot_configurations */ + { + dgaf_dis: 1; + hs_id_present: 2; + reserved: 1; + release_num: 4; + } + OPTIONAL UNION hs_id (DISCRIMINATOR hs_id_present) + { + pps_mo (hs_id_present IS 1) + { + pps_mo_id, 2; + } + anqp_domain (hs_id_present IS 2) + { + anqp_domain_id, 2; + } + }; +} + +IE osen_ie (EID_VENDOR_SPECIFIC) OUI (0x50, 0x6F, 0x9A, 0x12) +{ + data[0..255]; +} + +IE roaming_consortium_sel (EID_VENDOR_SPECIFIC) OUI (0x50, 0x6F, 0x9A, 0x1d) +{ + data[0..255]; +} + +IE QComVendorIE (EID_VENDOR_SPECIFIC) OUI (0x00, 0xA0, 0xC6) +{ + type, 1; + channel, 1; +} + +IE ESETrafStrmMet (EID_VENDOR_SPECIFIC) OUI (0x00, 0x40, 0x96, 0x07) +{ + tsid, 1; + state, 1; + msmt_interval, 2; +} + +IE ESETrafStrmRateSet (EID_VENDOR_SPECIFIC) OUI (0x00, 0x40, 0x96, 0x08) +{ + tsid, 1; + tsrates[0..8]; +} + +IE ESEVersion (EID_VENDOR_SPECIFIC) OUI (0x00, 0x40, 0x96, 0x03) +{ + version, 1; +} + +IE ESETxmitPower (EID_ESE_SPECIFIC) OUI (0x00, 0x40, 0x96, 0x00) +{ + power_limit, 1; + reserved, 1; +} + +IE ESECckmOpaque (EID_ESE_CCKM_SPECIFIC) OUI (0x00, 0x40, 0x96, 0x00) +{ + data[ 6..20 ]; +} + +IE RRMEnabledCap (EID_RRM_ENABLED_CAPS) +{ + //Capability bitmap + { + LinkMeasurement: 1; + NeighborRpt: 1; + parallel: 1; + repeated: 1; + BeaconPassive: 1; + BeaconActive: 1; + BeaconTable: 1; + BeaconRepCond: 1; + } + { + FrameMeasurement: 1; + ChannelLoad: 1; + NoiseHistogram: 1; + statistics: 1; + LCIMeasurement: 1; + LCIAzimuth: 1; + TCMCapability: 1; + triggeredTCM: 1; + } + { + APChanReport: 1; + RRMMIBEnabled: 1; + operatingChanMax: 3; + nonOperatinChanMax: 3; + } + { + MeasurementPilot: 3; + MeasurementPilotEnabled: 1; + NeighborTSFOffset: 1; + RCPIMeasurement: 1; + RSNIMeasurement: 1; + BssAvgAccessDelay: 1; + } + { + BSSAvailAdmission: 1; + AntennaInformation: 1; + fine_time_meas_rpt: 1; + lci_capability: 1; + reserved: 4; + } +} + +IE MeasurementPilot (EID_RRM_MEAS_PILOT_TX_INFO) +{ + measurementPilot, 1; + vendorSpecific[0..255]; //Should be an IE. But currently only one level of nesting allowed. Can ignore for now. +} + +IE MultiBssid (EID_MULTIPLE_BSSID) +{ + maxBSSIDIndicator, 1; + vendorSpecific[0..255]; +} + +IE OBSSScanParameters (EID_OBSS_SCAN_PARAMETERS) +{ + obssScanPassiveDwell, 2; + obssScanActiveDwell, 2; + bssChannelWidthTriggerScanInterval, 2; + obssScanPassiveTotalPerChannel, 2; + obssScanActiveTotalPerChannel, 2; + bssWidthChannelTransitionDelayFactor, 2; + obssScanActivityThreshold, 2; +} + +IE ht2040_bss_coexistence (EID_20_40_BSS_COEXISTENCE) +{ + // 20/40 BSS Coexistence Information + { + info_request: 1; + forty_mhz_intolerant: 1; + twenty_mhz_bsswidth_req: 1; + obss_scan_exemption_req: 1; + obss_scan_exemption_grant: 1; + unused: 3; + } +} + +IE ht2040_bss_intolerant_report (EID_20_40_BSS_INTOLERANT_REPORT) +{ + operating_class, 1; + channel_list[0..50]; +} + +const EID_RRM_NBR_RPT_TSF = 1; +const EID_RRM_NBR_CD_COUNTRY = 2; + +IE TSFInfo (EID_RRM_NBR_RPT_TSF) +{ + TsfOffset, 2; + BeaconIntvl, 2; +} +IE CondensedCountryStr (EID_RRM_NBR_CD_COUNTRY) +{ + countryStr[2]; +} + +IE NeighborReport (EID_NEIGHBOR_REPORT) +{ + bssid[6]; + //Bssid Info + { + APReachability: 2; + Security: 1; + KeyScope: 1; + //Capabilities + SpecMgmtCap: 1; + QosCap: 1; + apsd: 1; + rrm: 1; + } + //Capabilities contd. + { + DelayedBA: 1; + ImmBA: 1; + //Capabilities end. + MobilityDomain: 1; + reserved: 5; + } + + reserved1, 2; //part of BSSID Info. + + regulatoryClass, 1; + channel, 1; + PhyType, 1; + OPTIE IE TSFInfo; + OPTIE IE CondensedCountryStr; + OPTIE IE MeasurementPilot; + OPTIE IE RRMEnabledCap; + OPTIE IE MultiBssid; + //Ignoring vendor specific. +} + +IE RCPIIE (EID_RCPI) +{ + rcpi, 1; +} + +IE RSNIIE (EID_RSNI) +{ + rsni, 1; +} + +IE WFATPC (EID_VENDOR_SPECIFIC) OUI (0x00, 0x50, 0xF2, 0x08, 0x00) +{ + txPower, 1; + linkMargin, 1; +} + +IE MobilityDomain (EID_FT_MOBILITY_DOMAIN) +{ + MDID, 2; + //FT Capability and policy + { + overDSCap: 1; + resourceReqCap: 1; + reserved: 6; + } +} +const SUB_EID_FT_R1KH_ID = 1; +const SUB_EID_FT_GTK = 2; +const SUB_EID_FT_R0KH_ID = 3; +const SUB_EID_FT_IGTK = 4; +IE FTInfo (EID_FT_INFO) +{ + // MicControl, 2; + { + reserved: 8; + IECount: 8; + } + MIC[16]; + Anonce[32]; + Snonce[32]; + + OPTIE IE R1KH_ID (SUB_EID_FT_R1KH_ID) + { + PMK_R1_ID[6]; + } + + OPTIE IE GTK (SUB_EID_FT_GTK) + { + //Key Info + { + keyId: 2; + reserved: 14; + } + keyLength, 1; + RSC[8]; + key[5..32]; + } + + OPTIE IE R0KH_ID (SUB_EID_FT_R0KH_ID) + { + PMK_R0_ID[1..48]; + } + + OPTIE IE IGTK (SUB_EID_FT_IGTK) + { + //Key Info + keyID[2]; + IPN[6]; + keyLength, 1; + key[24]; + } +} + +IE TimeoutInterval (EID_TIMEOUT_INTERVAL) +{ + timeoutType, 1; + timeoutValue, 4; +} + +//TODO: need to define this properly. +IE RICData (EID_FT_RIC_DATA) +{ + Identifier, 1; + resourceDescCount, 1; + statusCode, 2; +} + +IE RICDescriptor (EID_FT_RIC_DESCRIPTOR) +{ + resourceType, 1; + variableData[0..255]; //Block ack param set...TODO: +} + +IE WscIEOpaque (EID_VENDOR_SPECIFIC) OUI ( 0x00, 0x50, 0xF2, 0x04 ) +{ + data[ 2..249 ]; +} + +IE P2PIEOpaque (EID_VENDOR_SPECIFIC) OUI ( 0x50, 0x6F, 0x9A, 0x09 ) +{ + data[ 2..249 ]; +} + +IE WFDIEOpaque (EID_VENDOR_SPECIFIC) OUI ( 0x50, 0x6F, 0x9A, 0x0A ) +{ + data[ 2..249 ]; +} + +IE PTIControl (EID_PTI_CONTROL) // 7.3.2.65 +{ + tid, 1; + sequence_control, 2; +} + +IE PUBufferStatus (EID_PU_BUFFER_STATUS) // 7.3.2.66 +{ + { + ac_bk_traffic_aval: 1; + ac_be_traffic_aval: 1; + ac_vi_traffic_aval: 1; + ac_vo_traffic_aval: 1; + reserved: 4; + } +} + + + +IE VHTCaps (EID_VHT_CAPABILITIES) +{ + //VHT Capability Info + { + maxMPDULen: 2; + supportedChannelWidthSet: 2; + ldpcCodingCap: 1; + shortGI80MHz: 1; + shortGI160and80plus80MHz: 1; + txSTBC: 1; + rxSTBC: 3; + suBeamFormerCap: 1; + suBeamformeeCap: 1; + csnofBeamformerAntSup: 3; + numSoundingDim: 3; + muBeamformerCap: 1; + muBeamformeeCap: 1; + vhtTXOPPS: 1; + htcVHTCap: 1; + maxAMPDULenExp: 3; + vhtLinkAdaptCap: 2; + rxAntPattern: 1; + txAntPattern: 1; + extended_nss_bw_supp: 2; + } + rxMCSMap, 2; + { + rxHighSupDataRate: 13; + max_nsts_total: 3; + } + txMCSMap, 2; + { + txSupDataRate: 13; + vht_extended_nss_bw_cap: 1; + reserved: 2; + } +} + +IE VHTOperation (EID_VHT_OPERATION_ELEMENT) +{ + chanWidth, 1; + chan_center_freq_seg0, 1; + chan_center_freq_seg1, 1; + basicMCSSet, 2; +} + +IE VHTExtBssLoad (EID_VHT_EXT_BSS_LOAD) +{ + muMIMOCapStaCount, 1; + ssUnderUtil, 1; + FortyMHzUtil, 1; + EightyMHzUtil, 1; + OneSixtyMHzUtil, 1; +} + +IE AID (EID_AID) +{ + assocId, 2; +} + +IE WiderBWChanSwitchAnn (EID_WIDER_BW_CHANNEL_SWITCH_ANN) +{ + newChanWidth, 1; + newCenterChanFreq0, 1; + newCenterChanFreq1, 1; +} + +IE vht_transmit_power_env (VHT_TRANSMIT_POWER_ENVELOPE) +{ + bytes[2..5]; +} + +IE ChannelSwitchWrapper (EID_CHANNEL_SWITCH_WRAPPER) +{ + OPTIE IE WiderBWChanSwitchAnn; + OPTIE IE vht_transmit_power_env; +} +IE ExtCap (EID_EXT_CAP) +{ + bytes[1..15]; +} + +IE HTCaps (EID_HT_CAPABILITIES) +{ + // HT Capability Info + { + advCodingCap: 1; + supportedChannelWidthSet: 1; + mimoPowerSave: 2; + greenField: 1; + shortGI20MHz: 1; + shortGI40MHz: 1; + txSTBC: 1; + rxSTBC: 2; + delayedBA: 1; + maximalAMSDUsize: 1; + dsssCckMode40MHz: 1; + psmp: 1; + stbcControlFrame: 1; + lsigTXOPProtection: 1; + } + // HT Parameters Info; + { + maxRxAMPDUFactor: 2; + mpduDensity: 3; + reserved1: 3; + } + + supportedMCSSet[ HT_MAX_SUPPORTED_MCS_SET ]; + + // Extended HT Capability Info + { + pco: 1; + transitionTime: 2; + reserved2: 5; + mcsFeedback: 2; + reserved3: 6; + } + // TXBF Capability Info + { + txBF: 1; + rxStaggeredSounding: 1; + txStaggeredSounding: 1; + rxZLF: 1; + txZLF: 1; + implicitTxBF: 1; + calibration: 2; + explicitCSITxBF: 1; + explicitUncompressedSteeringMatrix: 1; + explicitBFCSIFeedback: 3; + explicitUncompressedSteeringMatrixFeedback: 3; + explicitCompressedSteeringMatrixFeedback: 3; + csiNumBFAntennae: 2; + uncompressedSteeringMatrixBFAntennae: 2; + compressedSteeringMatrixBFAntennae: 2; + reserved4: 7; + } + // AS Capability Info + { + antennaSelection: 1; + explicitCSIFeedbackTx: 1; + antennaIndicesFeedbackTx: 1; + explicitCSIFeedback: 1; + antennaIndicesFeedback: 1; + rxAS: 1; + txSoundingPPDUs: 1; + reserved5: 1; + } + //TODO: take it out when generic fix to remove extra bytes in IE is available. + //This is required to interop with Dlink AP which is sending 2 bytes extra in HTInfo IE. + rsvd[0..32]; + +} // End IE HTCaps. + +IE HTInfo (EID_HT_INFO) +{ + primaryChannel, 1; + + // ahtInfoField1 + { + secondaryChannelOffset: 2; + recommendedTxWidthSet: 1; + rifsMode: 1; + controlledAccessOnly: 1; + serviceIntervalGranularity: 3; + } + + // ahtInfoField2 + + + // ahtInfoField2 + { + opMode: 2; + nonGFDevicesPresent: 1; + transmitBurstLimit: 1; + obssNonHTStaPresent:1; + chan_center_freq_seg2:8; + reserved: 3; + } + + + // ahtInfoField3 + { + basicSTBCMCS: 7; + dualCTSProtection: 1; + secondaryBeacon: 1; + lsigTXOPProtectionFullSupport: 1; + pcoActive: 1; + pcoPhase: 1; + reserved2: 4; + } + + basicMCSSet[ HT_MAX_SUPPORTED_MCS_SET ]; + + //TODO: take it out when generic fix to remove extra bytes in IE is available. + //This is required to interop with Dlink AP which is sending 2 bytes extra in HTInfo IE. + rsvd[0..32]; + +} // End IE HTInfo. + + +IE OperatingMode (EID_OPERATING_MODE) +{ + { //Operating Mode field + chanWidth: 2; + vht_160_80p80_supp: 1; + no_ldpc: 1; + rxNSS: 3; + rxNSSType: 1; + } +} + +IE QosMapSet (EID_QOS_MAP_SET) +{ + dscp_exceptions[QOS_DSCP_RANGE_LEN..MAX_QOS_DSCP_DATA_LEN]; +} + +CONTAINERIE RICDataDesc +{ + MANDIE RICData; + OPTIE RICDescriptor; + OPTIE TSPEC; + OPTIE TCLAS[0..2]; + OPTIE TCLASSPROC; + OPTIE TSDelay; + OPTIE Schedule; + OPTIE WMMTSPEC; + OPTIE WMMTCLAS[0..2]; + OPTIE WMMTCLASPROC; + OPTIE WMMTSDelay; + OPTIE WMMSchedule; +} + +IE TimeAdvertisement (EID_TIME_ADVERTISEMENT) // 8.4.2.63 +{ + timing_capabilities, 1; + time_value[10]; + time_error[5]; +} + +const QTI_VERSION_ATTR_ID = 1; +const QTI_VHT_MCS_10_11_ATTR_ID = 2; + +IE qcn_ie (EID_VENDOR_SPECIFIC) OUI ( 0x8C, 0xFD, 0xF0, 0x01 ) +{ + OPTIE IE version_attr (QTI_VERSION_ATTR_ID) + { + version, 1; + sub_version, 1; + } + + OPTIE IE vht_mcs11_attr (QTI_VHT_MCS_10_11_ATTR_ID) + { + vht_mcs_10_11_supp, 1; + } +} + +IE esp_information (EID_EXTN_ID_ELEMENT) OUI ( 0x0B ) +{ + data[0..96]; +} +IE fils_indication (EID_FILS_INDICATION) +{ + // FILS Information element + { + public_key_identifiers_cnt : 3; + realm_identifiers_cnt : 3; + is_ip_config_supported : 1; + is_cache_id_present : 1; + is_hessid_present : 1; + is_fils_sk_auth_supported : 1; + is_fils_sk_auth_pfs_supported : 1; + is_pk_auth_supported : 1; + reserved : 4; + } + // other FILS elements + variable_data[2..255]; +} + +IE fils_assoc_delay_info (EID_EXTN_ID_ELEMENT) OUI ( 0x01 ) +{ + assoc_delay_info, 1; +} + +IE fils_key_confirmation (EID_EXTN_ID_ELEMENT) OUI ( 0x03 ) +{ + key_auth[0..255]; +} + +IE fils_session (EID_EXTN_ID_ELEMENT) OUI ( 0x04 ) +{ + session[8]; +} + +IE fils_hlp_container (EID_EXTN_ID_ELEMENT) OUI ( 0x05 ) +{ + dest_mac[6]; + src_mac[6]; + hlp_packet[0..255]; +} + +IE fils_kde (EID_EXTN_ID_ELEMENT) OUI ( 0x07 ) +{ + key_rsc[8]; + kde_list[0..255]; +} + +IE fils_wrapped_data (EID_EXTN_ID_ELEMENT) OUI ( 0x08 ) +{ + wrapped_data[0..255]; +} + +IE fils_public_key (EID_EXTN_ID_ELEMENT) OUI ( 0x0C ) +{ + key_type, 1; + public_key[0..255]; +} + +IE fils_nonce (EID_EXTN_ID_ELEMENT) OUI ( 0x0D ) +{ + nonce[16]; +} + +IE fragment_ie (EID_FRAGMENT_IE) +{ + data[0..255]; +} + +IE dh_parameter_element (EID_EXTN_ID_ELEMENT) OUI ( 0x20 ) +{ + group[2]; + public_key[0..255]; +} + +const EID_RRM_BEACON_REPORTING = 1; +const EID_RRM_BCN_REPORTING_DETAIL = 2; + +const SUB_EID_AZIMUTH_REQ = 1; +const SUB_EID_REQ_MAC_ADDR = 2; +const SUB_EID_TGT_MAC_ADDR = 3; +const SUB_EID_MAX_AGE = 4; +const SUB_EID_NEIGHBOR_RPT = 52; + +IE BeaconReporting (EID_RRM_BEACON_REPORTING) +{ + reportingCondition, 1; + threshold, 1; +} + +IE BcnReportingDetail (EID_RRM_BCN_REPORTING_DETAIL) +{ + reportingDetail, 1; +} + +IE APChannelReport (EID_AP_CHAN_REPORT) +{ + regulatoryClass, 1; + channelList[0..50]; +} + +IE azimuth_req (SUB_EID_AZIMUTH_REQ) +{ + request, 1; +} + +IE req_mac_addr (SUB_EID_REQ_MAC_ADDR) +{ + addr[6]; +} + +IE tgt_mac_addr (SUB_EID_TGT_MAC_ADDR) +{ + addr[6]; +} + +IE max_age (SUB_EID_MAX_AGE) +{ + max_age, 2; +} + +IE neighbor_rpt (SUB_EID_NEIGHBOR_RPT) +{ + bssid[6]; + //Bssid Info + { + APReachability: 2; + Security: 1; + KeyScope: 1; + //Capabilities + SpecMgmtCap: 1; + QosCap: 1; + apsd: 1; + rrm: 1; + } + //Capabilities contd. + { + DelayedBA: 1; + ImmBA: 1; + //Capabilities end. + MobilityDomain: 1; + reserved: 5; + } + reserved1, 2; //part of BSSID Info. + regulatoryClass, 1; + channel, 1; + PhyType, 1; + OPTIE IE TSFInfo; + OPTIE IE CondensedCountryStr; + OPTIE IE MeasurementPilot; + OPTIE IE RRMEnabledCap; + OPTIE IE MultiBssid; +} + +IE MeasurementRequest (EID_MEAS_REQUEST) // 7.3.2.21 +{ + measurement_token, 1; + + // Measurement Request Mode + { + parallel: 1; + enable: 1; + request: 1; + report: 1; + durationMandatory: 1; + unused: 3; + } + + measurement_type, 1; + UNION measurement_request (DISCRIMINATOR measurement_type) + { + Basic (measurement_type IS 0) + { + channel_no, 1; + meas_start_time[8]; + meas_duration, 2; + } + CCA (measurement_type IS 1) + { + channel_no, 1; + meas_start_time[8]; + meas_duration, 2; + } + RPIHistogram (measurement_type IS 2) + { + channel_no, 1; + meas_start_time[8]; + meas_duration, 2; + } + Beacon (measurement_type IS 5) + { + regClass, 1; + channel, 1; + randomization, 2; + meas_duration, 2; + meas_mode, 1; + BSSID[6]; + OPTIE SSID; + OPTIE BeaconReporting; + OPTIE BcnReportingDetail; + OPTIE RequestedInfo; + OPTIE APChannelReport[0..2]; + OPTIE last_beacon_report_indication; + //OPTIONAL vendor_specific[1..239]; + } + lci (measurement_type IS 8) + { + loc_subject, 1; + OPTIE azimuth_req; + OPTIE req_mac_addr; + OPTIE tgt_mac_addr; + OPTIE max_age; + } + ftmrr (measurement_type IS 16) + { + random_interval, 2; + min_ap_count, 1; + + OPTIE neighbor_rpt; + OPTIE max_age; + } + + }; +} + +IE he_cap (EID_EXTN_ID_ELEMENT) OUI (0x23) +{ + { + htc_he:1; + twt_request:1; + twt_responder:1; + fragmentation:2; + max_num_frag_msdu_amsdu_exp:3; + min_frag_size:2; + trigger_frm_mac_pad:2; + multi_tid_aggr_rx_supp:3; + he_link_adaptation:2; + all_ack:1; + trigd_rsp_sched:1; + a_bsr:1; + broadcast_twt:1; + ba_32bit_bitmap:1; + mu_cascade:1; + ack_enabled_multitid:1; + reserved:1; + omi_a_ctrl:1; + ofdma_ra:1; + max_ampdu_len_exp_ext:2; + amsdu_frag:1; + flex_twt_sched:1; + rx_ctrl_frame:1; + } + { + bsrp_ampdu_aggr:1; + qtp:1; + a_bqr:1; + spatial_reuse_param_rspder:1; + ndp_feedback_supp:1; + ops_supp:1; + amsdu_in_ampdu:1; + multi_tid_aggr_tx_supp:3; + he_sub_ch_sel_tx_supp:1; + ul_2x996_tone_ru_supp:1; + om_ctrl_ul_mu_data_dis_rx:1; + he_dynamic_smps:1; + punctured_sounding_supp:1; + ht_vht_trg_frm_rx_supp:1; + } + { + reserved2:1; + chan_width_0:1; + chan_width_1:1; + chan_width_2:1; + chan_width_3:1; + chan_width_4:1; + chan_width_5:1; + chan_width_6:1; + rx_pream_puncturing:4; + device_class:1; + ldpc_coding:1; + he_1x_ltf_800_gi_ppdu:1; + midamble_tx_rx_max_nsts:2; + he_4x_ltf_3200_gi_ndp:1; + tb_ppdu_tx_stbc_lt_80mhz:1; + rx_stbc_lt_80mhz:1; + doppler:2; + ul_mu:2; + dcm_enc_tx:3; + dcm_enc_rx:3; + ul_he_mu:1; + su_beamformer:1; + } + { + su_beamformee:1; + mu_beamformer:1; + bfee_sts_lt_80:3; + bfee_sts_gt_80:3; + num_sounding_lt_80:3; + num_sounding_gt_80:3; + su_feedback_tone16:1; + mu_feedback_tone16:1; + codebook_su:1; + codebook_mu:1; + beamforming_feedback:3; + he_er_su_ppdu:1; + dl_mu_mimo_part_bw:1; + ppet_present:1; + srp:1; + power_boost:1; + he_ltf_800_gi_4x:1; + max_nc:3; + tb_ppdu_tx_stbc_gt_80mhz:1; + rx_stbc_gt_80mhz:1; + } + { + er_he_ltf_800_gi_4x:1; + he_ppdu_20_in_40Mhz_2G:1; + he_ppdu_20_in_160_80p80Mhz:1; + he_ppdu_80_in_160_80p80Mhz:1; + er_1x_he_ltf_gi:1; + midamble_tx_rx_1x_he_ltf:1; + dcm_max_bw:2; + longer_than_16_he_sigb_ofdm_sym:1; + non_trig_cqi_feedback:1; + tx_1024_qam_lt_242_tone_ru:1; + rx_1024_qam_lt_242_tone_ru:1; + rx_full_bw_su_he_mu_compress_sigb:1; + rx_full_bw_su_he_mu_non_cmpr_sigb:1; + reserved3:2; + } + reserved4, 1; + rx_he_mcs_map_lt_80, 2; + tx_he_mcs_map_lt_80, 2; + rx_he_mcs_map_160[2][0..1] COUNTIS chan_width_2; + tx_he_mcs_map_160[2][0..1] COUNTIS chan_width_2; + rx_he_mcs_map_80_80[2][0..1] COUNTIS chan_width_3; + tx_he_mcs_map_80_80[2][0..1] COUNTIS chan_width_3; + OPTIONAL UNION ppet (DISCRIMINATOR ppet_present) + { + ppe_threshold (ppet_present IS 1) + { + ppe_th[1..25]; + } + }; +} + +IE he_op (EID_EXTN_ID_ELEMENT) OUI (0x24) +{ + { + default_pe: 3; + twt_required: 1; + txop_rts_threshold: 10; + vht_oper_present: 1; + co_located_bss: 1; + } + { + er_su_disable: 1; + oper_info_6g_present: 1; + reserved2: 6; + } + { + bss_color:6; + partial_bss_col:1; + bss_col_disabled:1; + } + basic_mcs_nss[2]; + OPTIONAL UNION vht_oper (DISCRIMINATOR vht_oper_present) + { + info (vht_oper_present IS 1) + { + chan_width, 1; + center_freq_seg0, 1; + center_freq_seg1, 1; + } + }; + OPTIONAL UNION maxbssid_ind (DISCRIMINATOR co_located_bss) + { + info (co_located_bss IS 1) + { + data, 1; + } + }; + OPTIONAL UNION oper_info_6g (DISCRIMINATOR oper_info_6g_present) + { + info (oper_info_6g_present IS 1) + { + primary_ch, 1; + { // control + ch_width: 2; + dup_bcon: 1; + reserved: 5; + } + center_freq_seg0, 1; + center_freq_seg1, 1; + min_rate, 1; + } + }; +} + +IE he_6ghz_band_cap (EID_EXTN_ID_ELEMENT) OUI (0x3B) +{ + { // capabilities_information + min_mpdu_start_spacing :3; + max_ampdu_len_exp: 3; + max_mpdu_len: 3; + sm_pow_save: 2; + rd_responder: 1; + rx_ant_pattern_consistency: 1; + tx_ant_pattern_consistency: 1; + reserved: 2; + } +} + +IE mu_edca_param_set (EID_EXTN_ID_ELEMENT) OUI (0x26) +{ + qos, 1; + { + acbe_aifsn: 4; + acbe_acm: 1; + acbe_aci: 2; + unused1: 1; + } + { + acbe_acwmin: 4; + acbe_acwmax: 4; + } + acbe_muedca_timer, 1; + { + acbk_aifsn: 4; + acbk_acm: 1; + acbk_aci: 2; + unused2: 1; + } + { + acbk_acwmin: 4; + acbk_acwmax: 4; + } + acbk_muedca_timer, 1; + { + acvi_aifsn: 4; + acvi_acm: 1; + acvi_aci: 2; + unused3: 1; + } + { + acvi_acwmin: 4; + acvi_acwmax: 4; + } + acvi_muedca_timer, 1; + { + acvo_aifsn: 4; + acvo_acm: 1; + acvo_aci: 2; + unused4: 1; + } + { + acvo_acwmin: 4; + acvo_acwmax: 4; + } + acvo_muedca_timer, 1; +} + +IE bss_color_change (EID_EXTN_ID_ELEMENT) OUI (0x2A) +{ + countdown, 1; + { + new_color: 6; + reserved: 2; + } +} + +///////////////////////////////////////////////////////////////////////////// +// MULTIIEs // +///////////////////////////////////////////////////////////////////////////// + +MULTIIE WSC ( EID_VENDOR_SPECIFIC ) OUI( 0x00, 0x50, 0xF2, 0x04 ) +{ + MANDATORYTLV Version; // Must be 0x10 + OPTIONALTLV WPSState; + OPTIONALTLV APSetupLocked; + OPTIONALTLV SelectedRegistrarConfigMethods; + OPTIONALTLV UUID_E; + OPTIONALTLV UUID_R; + OPTIONALTLV RFBands; + OPTIONALTLV SelectedRegistrar; + OPTIONALTLV ConfigMethods; + OPTIONALTLV AssociationState; + OPTIONALTLV ConfigurationError; + OPTIONALTLV Manufacturer; + OPTIONALTLV ModelName; + OPTIONALTLV ModelNumber; + OPTIONALTLV SerialNumber; + OPTIONALTLV DeviceName; + OPTIONALTLV DevicePasswordID; + OPTIONALTLV PrimaryDeviceType; + OPTIONALTLV RequestType; + OPTIONALTLV ResponseType; + OPTIONALTLV VendorExtension; + OPTIONALTLV RequestDeviceType; +} + +MULTIIE WscBeacon ( EID_VENDOR_SPECIFIC ) OUI( 0x00, 0x50, 0xF2, 0x04 ) +{ + MANDATORYTLV Version; // 0x10 = version 1.0, 0x11 + // = version 1.1, etc. + MANDATORYTLV WPSState; // 1 = unconfigured, 2 = + // configured + OPTIONALTLV APSetupLocked; // Must be included if value + // is TRUE + OPTIONALTLV SelectedRegistrar; // BOOL: indicates if the + // user has recently + // activated a Registrar to + // add an Enrollee. + OPTIONALTLV DevicePasswordID; // Device Password ID + // indicates the method or + // identifies the specific + // password that the + // selected Registrar + // intends to use. + OPTIONALTLV SelectedRegistrarConfigMethods; // This attribute contains + // the config methods active + // on the selected + // Registrar. + OPTIONALTLV UUID_E; // The AP's UUID is provided + // only when the AP is a + // dual-band AP in push + // button mode and + // indicating push button + // mode on both radios + OPTIONALTLV RFBands; // Indicates all RF bands + // available on the AP. A + // dual-band AP must provide + // this attribute. + // WSC 2.0 + OPTIONALTLV VendorExtension; // Version2 and AuthorizedMACs + +} // End Multi-IE WscBeacon. + +MULTIIE WscAssocReq ( EID_VENDOR_SPECIFIC ) OUI( 0x00, 0x50, 0xF2, 0x04 ) +{ + MANDATORYTLV Version; // 0x10 = version 1.0, 0x11 + // = version 1.1, etc. + MANDATORYTLV RequestType; // + // + // WSC 2.0 + OPTIONALTLV VendorExtension; // Version2 + +} // End Multi-IE WscAssocReq. + + +MULTIIE WscAssocRes ( EID_VENDOR_SPECIFIC ) OUI( 0x00, 0x50, 0xF2, 0x04 ) +{ + MANDATORYTLV Version; // 0x10 = version 1.0, 0x11 + // = version 1.1, etc. + MANDATORYTLV ResponseType; // + // + // WSC 2.0 + OPTIONALTLV VendorExtension; // Version2 + +} // End Multi-IE WscAssocRes. + +MULTIIE WscReassocRes ( 221 ) OUI( 0x00, 0x50, 0xF2, 0x04 ) +{ + MANDATORYTLV Version; // 0x10 = version 1.0, 0x11 + // = version 1.1, etc. + MANDATORYTLV ResponseType; // + // + // WSC 2.0 + OPTIONALTLV VendorExtension; // Version2 + +} // End Multi-IE WscReassocRes + +MULTIIE WscProbeReq ( EID_VENDOR_SPECIFIC ) OUI( 0x00, 0x50, 0xF2, 0x04 ) +{ + MANDATORYTLV Version; // 0x10 = version 1.0, 0x11 + // = version 1.1, etc. + MANDATORYTLV RequestType; // + // + MANDATORYTLV ConfigMethods; // Configuration methods the + // Enrollee or Registrar + // supports + MANDATORYTLV UUID_E; // unique GUID generated by + // the Enrollee. + MANDATORYTLV PrimaryDeviceType; + MANDATORYTLV RFBands; // Specific RF bands used + // for this message + MANDATORYTLV AssociationState; // Configuration and previous + // association state + MANDATORYTLV ConfigurationError; + MANDATORYTLV DevicePasswordID; + + // WSC 2.0 + OPTIONALTLV Manufacturer; // Must be included in ver 2.0 + // or higher. + OPTIONALTLV ModelName; // Must be included in ver 2.0 + // or higher. + OPTIONALTLV ModelNumber; // Must be included in ver 2.0 + // or higher. + OPTIONALTLV DeviceName; // Must be included in ver 2.0 + // or higher. + OPTIONALTLV VendorExtension; // Version2 and RequestToEntroll + + OPTIONALTLV RequestDeviceType; // When a device receives a Probe + // Request containing this type, + // It will only reponse if Primary + // or Secondary Device Type matches. + +} // End Multi-IE WscProbeReq. + +MULTIIE WscProbeRes ( EID_VENDOR_SPECIFIC ) OUI( 0x00, 0x50, 0xF2, 0x04 ) +{ + MANDATORYTLV Version; // 0x10 = version 1.0, 0x11 + // = version 1.1, etc. + MANDATORYTLV WPSState; // 1 = unconfigured, 2 = + // configured + OPTIONALTLV APSetupLocked; // Must be included if value + // is TRUE + OPTIONALTLV SelectedRegistrar; // BOOL: indicates if the + // user has recently + // activated a Registrar to + // add an Enrollee. + OPTIONALTLV DevicePasswordID; // Device Password ID + // indicates the method or + // identifies the specific + // password that the + // selected Registrar + // intends to use. + OPTIONALTLV SelectedRegistrarConfigMethods; // This attribute contains + // the config methods active + // on the selected + // Registrar. + MANDATORYTLV ResponseType; + MANDATORYTLV UUID_E; // unique identifier of AP + MANDATORYTLV Manufacturer; + MANDATORYTLV ModelName; + MANDATORYTLV ModelNumber; + MANDATORYTLV SerialNumber; + MANDATORYTLV PrimaryDeviceType; + MANDATORYTLV DeviceName; // User-friendly description + // of device + MANDATORYTLV ConfigMethods; // Config Methods corresponds + // to the methods the AP + // supports as an Enrollee + // for adding external + // Registrars. + OPTIONALTLV RFBands; // Indicates all RF bands + // available on the AP. A + // dual-band AP must provide + // this attribute. + // WSC 2.0 + OPTIONALTLV VendorExtension; // Version2 and AuthorizedMACs + +} // WscProbeRes. + +// This MULTIIE combines the fields from the WSC IEs as they appear in +// Beacons *and* in Probe Responses, with the difference that they're all +// optional. In our device drivers, we combine Probe Responses and Beacons +// into one list, and parse their IEs later (c.f. frame BeaconIEs). Because +// the WSC IE differs in those two frames, we'd often see warning messages +// about either unexpected fields showing up (if we thought we were parsing a +// Beacon, and we in fact had data from a Probe Response) or mandatory fields +// missing (if we thought we were parsing a Probe Response, and in fact had +// data from a Beacon). + +// I created this MULTIIE to stuff into the BeaconIEs frames to avoid this. +// It's intended to be used on unpack only, and to do so in a very forgiving +// way. + +MULTIIE WscBeaconProbeRes ( EID_VENDOR_SPECIFIC ) OUI( 0x00, 0x50, 0xF2, 0x04 ) +{ + OPTIONALTLV Version; // 0x10 = version 1.0, 0x11 + // = version 1.1, etc. + OPTIONALTLV WPSState; // 1 = unconfigured, 2 = + // configured + OPTIONALTLV APSetupLocked; // Must be included if value + // is TRUE + OPTIONALTLV SelectedRegistrar; // BOOL: indicates if the + // user has recently + // activated a Registrar to + // add an Enrollee. + OPTIONALTLV DevicePasswordID; // Device Password ID + // indicates the method or + // identifies the specific + // password that the + // selected Registrar + // intends to use. + OPTIONALTLV SelectedRegistrarConfigMethods; // This attribute contains + // the config methods active + // on the selected + // Registrar. + OPTIONALTLV ResponseType; + OPTIONALTLV UUID_E; // unique identifier of AP + OPTIONALTLV Manufacturer; + OPTIONALTLV ModelName; + OPTIONALTLV ModelNumber; + OPTIONALTLV SerialNumber; + OPTIONALTLV PrimaryDeviceType; + OPTIONALTLV DeviceName; // User-friendly description + // of device + OPTIONALTLV ConfigMethods; // Config Methods corresponds + // to the methods the AP + // supports as an Enrollee + // for adding external + // Registrars. + OPTIONALTLV RFBands; // Indicates all RF bands + // available on the AP. A + // dual-band AP must provide + // this attribute. + // WSC 2.0 + OPTIONALTLV VendorExtension; // Version2 and AuthorizedMACs + +} // WscProbeRes. +///////////////////////////////////////////////////////////////////////////// +// MULTIIEs // +///////////////////////////////////////////////////////////////////////////// + +MULTIIE P2PBeacon ( EID_VENDOR_SPECIFIC ) OUI( 0x50, 0x6F, 0x9A, 0x09 ) +{ + MANDATORYTLV P2PCapability; // Contains P2P Device + // and P2P Group Capability + MANDATORYTLV P2PDeviceId; // Contains P2P Device + // Address + OPTIONALTLV NoticeOfAbsence; // Indicates Notice of + // Absence schedule and + // CT Window + +} // End P2PBeacon + + +MULTIIE P2PAssocReq ( EID_VENDOR_SPECIFIC ) OUI( 0x50, 0x6F, 0x9A, 0x09 ) +{ + MANDATORYTLV P2PCapability; // Contains P2P Device + // and P2P Group Capability + OPTIONALTLV ExtendedListenTiming; + MANDATORYTLV P2PDeviceInfo; + +} // End P2PAssocReq + + +MULTIIE P2PAssocRes ( EID_VENDOR_SPECIFIC ) OUI( 0x50, 0x6F, 0x9A, 0x09 ) +{ + MANDATORYTLV P2PStatus; + OPTIONALTLV ExtendedListenTiming; + +} // End P2PAssocRes + + +MULTIIE P2PProbeReq ( EID_VENDOR_SPECIFIC ) OUI( 0x50, 0x6F, 0x9A, 0x09 ) +{ + MANDATORYTLV P2PCapability; + OPTIONALTLV P2PDeviceId; + MANDATORYTLV ListenChannel; + OPTIONALTLV ExtendedListenTiming; + OPTIONALTLV OperatingChannel; +} // End P2PProbeReq + + +MULTIIE P2PProbeRes ( EID_VENDOR_SPECIFIC ) OUI( 0x50, 0x6F, 0x9A, 0x09 ) +{ + MANDATORYTLV P2PCapability; + OPTIONALTLV ExtendedListenTiming; + OPTIONALTLV NoticeOfAbsence; + MANDATORYTLV P2PDeviceInfo; + OPTIONALTLV P2PGroupInfo; + +} // End P2PProbeRes + + +MULTIIE P2PBeaconProbeRes ( EID_VENDOR_SPECIFIC ) OUI( 0x50, 0x6F, 0x9A, 0x09 ) +{ + OPTIONALTLV P2PCapability; + OPTIONALTLV P2PDeviceId; + OPTIONALTLV ExtendedListenTiming; + OPTIONALTLV NoticeOfAbsence; + OPTIONALTLV P2PDeviceInfo; + OPTIONALTLV P2PGroupInfo; + +} // End P2PBeaconProbeRes + + +MULTIIE P2PDeAuth ( EID_VENDOR_SPECIFIC ) OUI( 0x50, 0x6F, 0x9A, 0x09 ) +{ + MANDATORYTLV MinorReasonCode; +} + + +MULTIIE P2PDisAssoc ( EID_VENDOR_SPECIFIC ) OUI( 0x50, 0x6F, 0x9A, 0x09 ) +{ + MANDATORYTLV MinorReasonCode; +} + +MULTIIE MBO_IE (EID_VENDOR_SPECIFIC) OUI ( 0x50, 0x6F, 0x9A, 0x16 ) +{ + OPTIONALTLV mbo_ap_cap; + OPTIONALTLV non_prefferd_chan_rep; + OPTIONALTLV cellular_data_cap ; + OPTIONALTLV assoc_disallowed; + OPTIONALTLV cellular_data_con_pref; + OPTIONALTLV transition_reason; + OPTIONALTLV transition_reject_reason; + OPTIONALTLV assoc_retry_delay; + OPTIONALTLV oce_cap; + OPTIONALTLV rssi_assoc_rej; + OPTIONALTLV reduced_wan_metrics; +} + +IE vendor_vht_ie (EID_VENDOR_SPECIFIC) OUI (0x00, 0x90, 0x4c, 0x04) +{ + sub_type, 1; + OPTIE IE VHTCaps; + OPTIE IE VHTOperation; +} + +///////////////////////////////////////////////////////////////////////////// +// Frames + +FRAME Beacon // C.f. Sec. 7.2.3.1 +{ + FF TimeStamp; + FF BeaconInterval; + FF Capabilities; + MANDIE SSID; + MANDIE SuppRates; + OPTIE FHParamSet; + OPTIE DSParams; + OPTIE CFParams; + OPTIE IBSSParams; + OPTIE TIM; + OPTIE Country; + OPTIE FHParams; + OPTIE FHPattTable; + OPTIE PowerConstraints; + OPTIE ChanSwitchAnn; + OPTIE ext_chan_switch_ann; + OPTIE SuppOperatingClasses; + OPTIE Quiet; + OPTIE TPCReport; + OPTIE ERPInfo; + OPTIE ExtSuppRates; + OPTIE RSN; + OPTIE QBSSLoad; + OPTIE EDCAParamSet; + OPTIE QOSCapsAp; + OPTIE APChannelReport; + OPTIE RRMEnabledCap; + OPTIE MobilityDomain; + OPTIE WPA; + OPTIE HTCaps; + OPTIE HTInfo; + OPTIE sec_chan_offset_ele; + OPTIE WMMInfoAp; + OPTIE WMMParams; + OPTIE WMMCaps; + OPTIE WAPI; + OPTIE ESERadMgmtCap; + OPTIE ESETrafStrmMet; + OPTIE ESETxmitPower; + + OPTIE WscBeacon; + OPTIE P2PBeacon; + OPTIE VHTCaps; + OPTIE VHTOperation; + OPTIE VHTExtBssLoad; + OPTIE ExtCap; + OPTIE OperatingMode; + OPTIE WiderBWChanSwitchAnn; + OPTIE OBSSScanParameters; + OPTIE fils_indication; + OPTIE Vendor1IE; + OPTIE vendor_vht_ie; + OPTIE Vendor3IE; + OPTIE hs20vendor_ie; + OPTIE ChannelSwitchWrapper; + OPTIE QComVendorIE; + OPTIE ESEVersion; + OPTIE MBO_IE; + OPTIE qcn_ie; + OPTIE he_cap; + OPTIE he_op; + OPTIE he_6ghz_band_cap; + OPTIE bss_color_change; + OPTIE mu_edca_param_set; + OPTIE esp_information; +} // End frame Beacon. + +// Ok, here's the story on Beacon1 & Beacon2. We presumably beacon a lot +// more than we change configuration. So it makes sense to keep the beacon +// we plan to send next in serialized format. We do this in struct schMisc. +// Whenever our config changes in a way that would affect our beacons, we +// just update our internal datastructures & re-generate the serialized +// beacon. + +// The problem is that there are *some* fields that need to be updated at +// send time, specifically the CF Param Set & the TIM. So, what we do is +// this: whenever our config changes, call schSetFixedBeaconFields. There, +// we serialize the following Beacon fields into gSchBeaconFrameBegin (after +// the power template & MAC header): TimeStamp, BeaconInterval, Capabilities, +// SSID, SuppRates, DSParams, & IBSSParams. It sets gSchBeaconOffsetBegin to +// the length of this buffer (incl. power template & MAC header). + +// Next, it serializes the following fields into gSchBeaconFrameEnd: Country, +// EDCAParamSet, PowerConstraints, TPCReport, ChannelSwitchAnn, Quiet, +// ERPInfo, HTCaps, HTInfo, ExtSuppRates, WPA, RSN, WMMInfo, +// WMMParams, WMMCaps. +// The length of *this* buffer is kept in gSchBeaconOffsetEnd. + +// Then, in 'schBeaconInterruptHandler', we write CFParams & TIM at the end +// of gSchBeaconFrameBegin, keeping track of the (new) size of this buffer in +// the local 'beaconSize'. + +// After that, we call 'specialBeaconProcessing'. Note that this may +// actually call schSetFixedBeaconFields repeatedly! The comments say they +// try to avoid this, but... + +// Finally, we call writeBeaconToTFP, where the first thing we do is copy the +// gSchBeaconFrameEnd buffer after the end of gSchBeaconFrameBegin. + +FRAME Beacon1 +{ + FF TimeStamp; + FF BeaconInterval; + FF Capabilities; + MANDIE SSID; + MANDIE SuppRates; + OPTIE DSParams; + OPTIE IBSSParams; +} + +FRAME Beacon2 +{ + OPTIE Country; + OPTIE PowerConstraints; + OPTIE ChanSwitchAnn; + OPTIE ext_chan_switch_ann; + OPTIE SuppOperatingClasses; + OPTIE Quiet; + OPTIE TPCReport; + OPTIE ERPInfo; + OPTIE ExtSuppRates; + OPTIE RSNOpaque; + OPTIE EDCAParamSet; + OPTIE APChannelReport; + OPTIE RRMEnabledCap; + OPTIE MobilityDomain; + OPTIE WPA; + OPTIE HTCaps; + OPTIE HTInfo; + OPTIE sec_chan_offset_ele; + OPTIE WMMInfoAp; + OPTIE WMMParams; + OPTIE WMMCaps; + OPTIE WscBeacon; + OPTIE WAPI; + OPTIE ESERadMgmtCap; + OPTIE ESETrafStrmMet; + OPTIE ESETxmitPower; + OPTIE P2PBeacon; + OPTIE VHTCaps; + OPTIE VHTOperation; + OPTIE vht_transmit_power_env; + OPTIE ChannelSwitchWrapper; + OPTIE VHTExtBssLoad; + OPTIE ExtCap; + OPTIE OperatingMode; + OPTIE WiderBWChanSwitchAnn; + OPTIE OBSSScanParameters; + OPTIE fils_indication; + OPTIE Vendor1IE; + OPTIE vendor_vht_ie; + OPTIE Vendor3IE; + OPTIE hs20vendor_ie; + OPTIE QComVendorIE; + OPTIE ESEVersion; + OPTIE qcn_ie; + OPTIE he_cap; + OPTIE he_op; + OPTIE he_6ghz_band_cap; + OPTIE bss_color_change; + OPTIE mu_edca_param_set; + OPTIE esp_information; +} + +// This frame is just Beacon with its Fixed Fields stripped out. It's handy +// for use with struct 'tSirBssDescription', which has members corresponding +// to some fixed fields, but keeps its IEs in un-parsed format. + +// Note that it also includes the IE 'WscBeaconProbeRes'. + +FRAME BeaconIEs +{ + + MANDIE SSID; + MANDIE SuppRates; + OPTIE FHParamSet; + OPTIE DSParams; + OPTIE CFParams; + OPTIE IBSSParams; + OPTIE TIM; + OPTIE Country; + OPTIE FHParams; + OPTIE FHPattTable; + OPTIE PowerConstraints; + OPTIE ChanSwitchAnn; + OPTIE ext_chan_switch_ann; + OPTIE SuppOperatingClasses; + OPTIE Quiet; + OPTIE TPCReport; + OPTIE ERPInfo; + OPTIE ExtSuppRates; + OPTIE RSN; + OPTIE QBSSLoad; + OPTIE EDCAParamSet; + OPTIE QOSCapsAp; + OPTIE APChannelReport; + OPTIE RRMEnabledCap; + OPTIE MobilityDomain; + OPTIE WPA; + OPTIE HTCaps; + OPTIE HTInfo; + OPTIE sec_chan_offset_ele; + OPTIE WMMInfoAp; + OPTIE WMMParams; + OPTIE WMMCaps; + OPTIE WAPI; + OPTIE ESEVersion; + OPTIE ESERadMgmtCap; + OPTIE ESETrafStrmMet; + OPTIE ESETxmitPower; + + OPTIE WscBeaconProbeRes; + OPTIE P2PBeaconProbeRes; + OPTIE VHTCaps; + OPTIE VHTOperation; + OPTIE VHTExtBssLoad; + OPTIE ExtCap; + OPTIE OperatingMode; + OPTIE WiderBWChanSwitchAnn; + OPTIE OBSSScanParameters; + OPTIE fils_indication; + OPTIE Vendor1IE; + OPTIE vendor_vht_ie; + OPTIE Vendor3IE; + OPTIE hs20vendor_ie; + OPTIE ChannelSwitchWrapper; + OPTIE QComVendorIE; + OPTIE MBO_IE; + OPTIE qcn_ie; + OPTIE he_cap; + OPTIE he_op; + OPTIE he_6ghz_band_cap; + OPTIE bss_color_change; + OPTIE mu_edca_param_set; + OPTIE esp_information; +} // End frame BeaconIEs. + +FRAME Disassociation // 7.3.3.3 +{ + FF Reason; + OPTIE P2PDisAssoc; +} + +FRAME AssocRequest // 7.2.3.4 +{ + FF Capabilities; + FF ListenInterval; + MANDIE SSID; + MANDIE SuppRates; + OPTIE OperatingMode; + OPTIE PowerCaps; + OPTIE SuppChannels; + OPTIE HTCaps; + OPTIE QOSCapsStation; + OPTIE RSNOpaque; + OPTIE ExtSuppRates; + OPTIE MobilityDomain; + OPTIE FTInfo; + OPTIE SuppOperatingClasses; + OPTIE WAPIOpaque; + OPTIE WAPI; + OPTIE RRMEnabledCap; + OPTIE QosMapSet; + OPTIE ExtCap; + OPTIE VHTCaps; + OPTIE fils_session; + OPTIE fils_public_key; + OPTIE fils_key_confirmation; + OPTIE fils_hlp_container; + OPTIE fragment_ie; + OPTIE dh_parameter_element; + OPTIE WPAOpaque; + OPTIE WMMCaps; + OPTIE WMMInfoStation; + OPTIE WscIEOpaque; + OPTIE ESERadMgmtCap; + OPTIE ESEVersion; + OPTIE P2PIEOpaque; + OPTIE WFDIEOpaque; + OPTIE vendor_vht_ie; + OPTIE hs20vendor_ie; + OPTIE qcn_ie; + OPTIE he_cap; + OPTIE he_6ghz_band_cap; + OPTIE osen_ie; + OPTIE roaming_consortium_sel; +} // End frame AssocRequest. + +FRAME AssocResponse // 7.2.3.5 +{ + FF Capabilities; + FF Status; + FF AID; + MANDIE SuppRates; + OPTIE ExtSuppRates; + OPTIE EDCAParamSet; + OPTIE RCPIIE; + OPTIE RSNIIE; + OPTIE RRMEnabledCap; + OPTIE MobilityDomain; + OPTIE FTInfo; + OPTIE RICDataDesc[2]; + OPTIE WPA; + OPTIE TimeoutInterval; + OPTIE HTCaps; + OPTIE HTInfo; + OPTIE WMMParams; + OPTIE WMMCaps; + OPTIE ESERadMgmtCap; + OPTIE ESETrafStrmMet; + OPTIE ESETxmitPower; + OPTIE WMMTSPEC[0..4]; + OPTIE WscAssocRes; + OPTIE P2PAssocRes; + OPTIE VHTCaps; + OPTIE VHTOperation; + OPTIE ExtCap; + OPTIE OBSSScanParameters; + OPTIE QosMapSet; + OPTIE fils_session; + OPTIE fils_public_key; + OPTIE fils_key_confirmation; + OPTIE fils_hlp_container; + OPTIE fragment_ie; + OPTIE fils_kde; + OPTIE vendor_vht_ie; + OPTIE qcn_ie; + OPTIE he_cap; + OPTIE he_op; + OPTIE he_6ghz_band_cap; + OPTIE bss_color_change; + OPTIE mu_edca_param_set; + OPTIE MBO_IE; +} // End frame AssocResponse. + +FRAME ReAssocRequest // 7.2.3.6 +{ + FF Capabilities; + FF ListenInterval; + FF CurrentAPAddress; + MANDIE SSID; + MANDIE SuppRates; + OPTIE ExtSuppRates; + OPTIE PowerCaps; + OPTIE SuppChannels; + OPTIE RSNOpaque; + OPTIE QOSCapsStation; + OPTIE RRMEnabledCap; + OPTIE MobilityDomain; + OPTIE FTInfo; + OPTIE RICDataDesc[2]; + OPTIE SuppOperatingClasses; + OPTIE WPAOpaque; + OPTIE HTCaps; + OPTIE WMMCaps; + OPTIE WMMInfoStation; + OPTIE WscIEOpaque; + OPTIE WAPIOpaque; + OPTIE WAPI; + OPTIE ESERadMgmtCap; + OPTIE ESEVersion; + OPTIE ESECckmOpaque; + OPTIE WMMTSPEC[0..4]; + OPTIE ESETrafStrmRateSet; + OPTIE P2PIEOpaque; + OPTIE WFDIEOpaque; + OPTIE VHTCaps; + OPTIE ExtCap; + OPTIE OperatingMode; + OPTIE QosMapSet; + OPTIE vendor_vht_ie; + OPTIE hs20vendor_ie; + OPTIE he_cap; + OPTIE he_6ghz_band_cap; +} // End frame ReAssocRequest. + +FRAME ReAssocResponse // 7.2.3.7 +{ + FF Capabilities; + FF Status; + FF AID; + MANDIE SuppRates; + OPTIE ExtSuppRates; + OPTIE EDCAParamSet; + OPTIE RCPIIE; + OPTIE RSNIIE; + OPTIE RRMEnabledCap; + OPTIE RSNOpaque; + OPTIE MobilityDomain; + OPTIE FTInfo; + OPTIE RICDataDesc[2]; + OPTIE WPA; + OPTIE TimeoutInterval; + OPTIE HTCaps; + OPTIE HTInfo; + OPTIE WMMParams; + OPTIE ESERadMgmtCap; + OPTIE ESETrafStrmMet; + OPTIE ESETxmitPower; + OPTIE WMMTSPEC[0..4]; + OPTIE ESETrafStrmRateSet; + OPTIE WscReassocRes; + OPTIE P2PAssocRes; + OPTIE VHTCaps; + OPTIE VHTOperation; + OPTIE ExtCap; + OPTIE OBSSScanParameters; + OPTIE QosMapSet; + OPTIE vendor_vht_ie; + OPTIE he_cap; + OPTIE he_op; + OPTIE he_6ghz_band_cap; + OPTIE bss_color_change; + OPTIE mu_edca_param_set; + OPTIE MBO_IE; +} // End frame ReAssocResponse. + +FRAME ProbeRequest // 7.2.3.8 +{ + MANDIE SSID; + MANDIE SuppRates; + OPTIE RequestedInfo; + OPTIE ExtSuppRates; + OPTIE DSParams; + OPTIE HTCaps; + OPTIE WscProbeReq; + OPTIE WFATPC; + OPTIE P2PProbeReq; + OPTIE VHTCaps; + OPTIE ExtCap; + OPTIE qcn_ie; + OPTIE he_cap; + OPTIE he_6ghz_band_cap; +} // End frame ProbeRequest. + +FRAME ProbeResponse // 7.2.3.9 +{ + FF TimeStamp; + FF BeaconInterval; + FF Capabilities; + MANDIE SSID; + MANDIE SuppRates; + OPTIE FHParamSet; + OPTIE DSParams; + OPTIE CFParams; + OPTIE IBSSParams; + OPTIE Country; + OPTIE FHParams; + OPTIE FHPattTable; + OPTIE PowerConstraints; + OPTIE ChanSwitchAnn; + OPTIE ext_chan_switch_ann; + OPTIE SuppOperatingClasses; + OPTIE Quiet; + OPTIE TPCReport; + OPTIE ERPInfo; + OPTIE ExtSuppRates; + OPTIE RSNOpaque; + OPTIE QBSSLoad; + OPTIE EDCAParamSet; + OPTIE RRMEnabledCap; + OPTIE APChannelReport; + OPTIE MobilityDomain; + OPTIE WPA; + OPTIE HTCaps; + OPTIE HTInfo; + OPTIE sec_chan_offset_ele; + OPTIE WMMInfoAp; + OPTIE WMMParams; + OPTIE WMMCaps; + OPTIE WAPI; + OPTIE ESERadMgmtCap; + OPTIE ESETrafStrmMet; + OPTIE ESETxmitPower; + + OPTIE WscProbeRes; + OPTIE P2PProbeRes; + + OPTIE VHTCaps; + OPTIE VHTOperation; + OPTIE VHTExtBssLoad; + OPTIE ExtCap; + OPTIE OBSSScanParameters; + OPTIE fils_indication; + OPTIE Vendor1IE; + OPTIE vendor_vht_ie; + OPTIE Vendor3IE; + OPTIE hs20vendor_ie; + OPTIE ChannelSwitchWrapper; + OPTIE QComVendorIE; + OPTIE ESEVersion; + OPTIE MBO_IE; + OPTIE qcn_ie; + OPTIE he_cap; + OPTIE he_op; + OPTIE he_6ghz_band_cap; + OPTIE bss_color_change; + OPTIE mu_edca_param_set; + OPTIE esp_information; +} // End frame ProbeResponse. + +FRAME Authentication // 7.2.3.10 +{ + FF AuthAlgo; + FF AuthSeqNo; + FF Status; + OPTIE ChallengeText; + OPTIE RSNOpaque; + OPTIE MobilityDomain; + OPTIE FTInfo; + OPTIE TimeoutInterval; + OPTIE RICDataDesc[2]; + OPTIE fils_nonce; + OPTIE fils_session; + OPTIE fils_wrapped_data; + OPTIE fils_assoc_delay_info; +} // End frame Auth. + +FRAME DeAuth // 7.2.3.11 +{ + FF Reason; + OPTIE P2PDeAuth; +} + +FRAME AddTSRequest // 7.4.2.1 +{ + + FF Category; + FF Action; + FF DialogToken; + MANDIE TSPEC; + OPTIE TCLAS[0..2]; + OPTIE TCLASSPROC; + + // These IEs aren't in the spec, but our extant code *will* parse them if + // they're present. I included them to preserve that capability + + OPTIE WMMTSPEC; + OPTIE WMMTCLAS[0..2]; + OPTIE WMMTCLASPROC; + OPTIE ESETrafStrmRateSet; + +} // End frame AddTSRequest. + +FRAME WMMAddTSRequest +{ + FF Category; + FF Action; + FF DialogToken; + FF StatusCode; + MANDIE WMMTSPEC; + OPTIE ESETrafStrmRateSet; +} // End Frame WMMAddTSRequest + +FRAME AddTSResponse // 7.4.2.2 +{ + + FF Category; + FF Action; + FF DialogToken; + FF Status; + MANDIE TSDelay; + MANDIE TSPEC; + OPTIE TCLAS[0..2]; + OPTIE TCLASSPROC; + OPTIE Schedule; + + // These IEs aren't in the spec, but our extant code *will* parse them if + // they're present. I included them to preserve that capability + OPTIE WMMTSDelay; + OPTIE WMMSchedule; + OPTIE WMMTSPEC; + OPTIE WMMTCLAS[0..2]; + OPTIE WMMTCLASPROC; + OPTIE ESETrafStrmMet; + +} // End frame AddTSResponse. + +FRAME WMMAddTSResponse +{ + + FF Category; + FF Action; + FF DialogToken; + FF StatusCode; + OPTIE WMMTSPEC; + OPTIE ESETrafStrmMet; + +} // End frame WMMAddTSResponse. + +FRAME DelTS // 7.4.2.3 +{ + FF Category; + FF Action; + FF TSInfo; + FF Reason; +} + +FRAME WMMDelTS +{ + FF Category; + FF Action; + FF DialogToken; + FF StatusCode; + MANDIE WMMTSPEC; +} + +FRAME TPCRequest +{ + FF Category; + FF Action; + FF DialogToken; + MANDIE TPCRequest; +} + +FRAME TPCReport +{ + FF Category; + FF Action; + FF DialogToken; + MANDIE TPCReport; +} + +FRAME ChannelSwitch +{ + FF Category; + FF Action; + MANDIE ChanSwitchAnn; + OPTIE sec_chan_offset_ele; + OPTIE WiderBWChanSwitchAnn; +} + +FRAME MeasurementRequest +{ + FF Category; + FF Action; + FF DialogToken; + MANDIE MeasurementRequest[1..4]; +} + +FRAME MeasurementReport +{ + FF Category; + FF Action; + FF DialogToken; + MANDIE MeasurementReport; +} + +FRAME SMPowerSave +{ + FF Category; + FF Action; + FF SMPowerModeSet; +} + +FRAME RadioMeasurementRequest +{ + FF Category; + FF Action; + FF DialogToken; + FF NumOfRepetitions; + //Measurement Request IE. + MANDIE MeasurementRequest[1..5]; +} + +FRAME RadioMeasurementReport +{ + FF Category; + FF Action; + FF DialogToken; + //Measurement Report elements. + MANDIE MeasurementReport[1..4]; + OPTIE BeaconReportStatus; +} + +FRAME LinkMeasurementRequest +{ + FF Category; + FF Action; + FF DialogToken; + FF TxPower; + FF MaxTxPower; + //Optional Sub Ies +} + +FRAME LinkMeasurementReport +{ + FF Category; + FF Action; + FF DialogToken; + FF TPCEleID; + FF TPCEleLen; + FF TxPower; + FF LinkMargin; + FF RxAntennaId; + FF TxAntennaId; + FF RCPI; + FF RSNI; + //Optional Vendor specific IEs ... ignoring +} + +FRAME NeighborReportRequest +{ + FF Category; + FF Action; + FF DialogToken; + OPTIE SSID; + //Optional vendor specific IE...ignoring. +} + +FRAME NeighborReportResponse +{ + FF Category; + FF Action; + FF DialogToken; + OPTIE NeighborReport[1..MAX_SUPPORTED_NEIGHBOR_RPT]; +} + +FRAME OperatingMode +{ + FF Category; + FF Action; + //Operating Mode field + FF OperatingMode; +} + +FRAME TDLSDisReq +{ + FF Category; + FF Action; + FF DialogToken; + MANDIE LinkIdentifier; +} + +FRAME TDLSDisRsp +{ + FF Category; + FF Action; + FF DialogToken; + FF Capabilities; + MANDIE SuppRates; + OPTIE ExtSuppRates; + OPTIE SuppChannels; + OPTIE SuppOperatingClasses; + OPTIE RSN; + OPTIE ExtCap; + OPTIE FTInfo; + OPTIE TimeoutInterval; + OPTIE RICData; + OPTIE HTCaps; + OPTIE ht2040_bss_coexistence; + MANDIE LinkIdentifier; + OPTIE VHTCaps; +} + +FRAME TDLSSetupReq +{ + FF Category; + FF Action; + FF DialogToken; + FF Capabilities; + MANDIE SuppRates; + OPTIE Country; + OPTIE ExtSuppRates; + OPTIE SuppChannels; + OPTIE RSN; + OPTIE ExtCap; + OPTIE SuppOperatingClasses; + OPTIE QOSCapsStation; + OPTIE FTInfo; + OPTIE TimeoutInterval; + OPTIE RICData; + OPTIE HTCaps; + OPTIE ht2040_bss_coexistence; + MANDIE LinkIdentifier; + OPTIE WMMInfoStation; + OPTIE AID; + OPTIE VHTCaps; +} + +FRAME TDLSSetupRsp +{ + FF Category; + FF Action; + FF Status; + FF DialogToken; + FF Capabilities ; + OPTIE SuppRates; + OPTIE Country; + OPTIE ExtSuppRates; + OPTIE SuppChannels; + OPTIE RSN; + OPTIE ExtCap; + OPTIE SuppOperatingClasses; + OPTIE QOSCapsStation; + OPTIE FTInfo; + OPTIE TimeoutInterval; + OPTIE RICData; + OPTIE HTCaps; + OPTIE ht2040_bss_coexistence; + OPTIE LinkIdentifier; + OPTIE WMMInfoStation; + OPTIE AID; + OPTIE VHTCaps; + OPTIE OperatingMode; +} + +FRAME TDLSSetupCnf +{ + FF Category; + FF Action; + FF Status; + FF DialogToken; + OPTIE RSN; + OPTIE EDCAParamSet; + OPTIE FTInfo; + OPTIE TimeoutInterval; + OPTIE HTInfo; + OPTIE LinkIdentifier; + OPTIE WMMParams; + OPTIE VHTOperation; + OPTIE OperatingMode; +} +FRAME TDLSTeardown +{ + FF Category; + FF Action; + FF Reason; + OPTIE FTInfo; + MANDIE LinkIdentifier; +} + +FRAME TDLSPeerTrafficInd +{ + FF Category; + FF Action; + FF DialogToken; + MANDIE LinkIdentifier; + OPTIE PTIControl; + MANDIE PUBufferStatus; +} + +FRAME TDLSPeerTrafficRsp +{ + FF Category; + FF Action; + FF DialogToken; + MANDIE LinkIdentifier; +} + +FRAME SaQueryReq +{ + FF Category; + FF Action; + FF TransactionId; +} + +FRAME SaQueryRsp +{ + FF Category; + FF Action; + FF TransactionId; +} + +FRAME QosMapConfigure +{ + FF Category; + FF Action; + MANDIE QosMapSet; +} + +FRAME VHTGidManagementActionFrame +{ + FF Category; + FF Action; + FF VhtMembershipStatusArray; + FF VhtUserPositionArray; +} + +FRAME ht2040_bss_coexistence_mgmt_action_frame +{ + FF Category; + FF Action; + MANDIE ht2040_bss_coexistence; + MANDIE ht2040_bss_intolerant_report; +} + +FRAME TimingAdvertisementFrame // 8.3.3.15 +{ + FF TimeStamp; + FF Capabilities; + OPTIE Country; + OPTIE PowerConstraints; + OPTIE TimeAdvertisement; + OPTIE ExtCap; + OPTIE Vendor1IE; + OPTIE Vendor3IE; +} + +FRAME ext_channel_switch_action_frame +{ + FF Category; + FF Action; + FF ext_chan_switch_ann_action; + OPTIE WiderBWChanSwitchAnn; +} + +FRAME p2p_oper_chan_change_confirm +{ + FF Category; + FF p2p_action_oui; + FF p2p_action_subtype; + FF DialogToken; + OPTIE HTCaps; + OPTIE VHTCaps; + OPTIE OperatingMode; +} + +FRAME addba_req +{ + FF Category; + FF Action; + FF DialogToken; + FF addba_param_set; + FF ba_timeout; + FF ba_start_seq_ctrl; + OPTIE addba_extn_element; +} + +FRAME addba_rsp +{ + FF Category; + FF Action; + FF DialogToken; + FF Status; + FF addba_param_set; + FF ba_timeout; + OPTIE addba_extn_element; +} + +FRAME delba_req +{ + FF Category; + FF Action; + FF delba_param_set; + FF Reason; +} + +FRAME vendor_action_frame +{ + FF Category; + FF vendor_oui; + FF vendor_action_subtype; +} + +// Local Variables: +// mode: c++ +// fill-column: 77 +// comment-column: 42 +// indent-tabs-mode: nil +// show-trailing-whitespace: t +// End: + +// parser.frms ends here. diff --git a/drivers/staging/qcacld-3.0/core/mac/src/dph/dph_hash_table.c b/drivers/staging/qcacld-3.0/core/mac/src/dph/dph_hash_table.c new file mode 100644 index 0000000000000000000000000000000000000000..326d7f9be14a67af18eaff682cbaa83da6fded72 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/dph/dph_hash_table.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file dph_hash_table.cc implements the member functions of + * DPH hash table class. + * + * Author: Sandesh Goel + * Date: 02/25/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#include "cds_api.h" +#include "sch_api.h" +#include "dph_global.h" +#include "lim_api.h" +#include "wma_if.h" +#include "wlan_mlme_api.h" + +void dph_hash_table_init(struct mac_context *mac, + struct dph_hash_table *hash_table) +{ + uint16_t i; + + for (i = 0; i < hash_table->size; i++) { + hash_table->pHashTable[i] = 0; + } + + for (i = 0; i < hash_table->size; i++) { + hash_table->pDphNodeArray[i].valid = 0; + hash_table->pDphNodeArray[i].added = 0; + hash_table->pDphNodeArray[i].assocId = i; + } + +} + +/* --------------------------------------------------------------------- */ +/** + * hash_function + * + * FUNCTION: + * Hashing function + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param staAddr MAC address of the station + * @return None + */ + +static uint16_t hash_function(struct mac_context *mac, uint8_t staAddr[], + uint16_t numSta) +{ + int i; + uint16_t sum = 0; + + for (i = 0; i < 6; i++) + sum += staAddr[i]; + + return sum % numSta; +} + +/* --------------------------------------------------------------------- */ +/** + * dph_lookup_hash_entry + * + * FUNCTION: + * Look up an entry in hash table + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param staAddr MAC address of the station + * @param pStaId pointer to the Station ID assigned to the station + * @return pointer to STA hash entry if lookup was a success \n + * NULL if lookup was a failure + */ + +tpDphHashNode dph_lookup_hash_entry(struct mac_context *mac, uint8_t staAddr[], + uint16_t *pAssocId, + struct dph_hash_table *hash_table) +{ + tpDphHashNode ptr = NULL; + uint16_t index = hash_function(mac, staAddr, hash_table->size); + + if (!hash_table->pHashTable) { + pe_err("pHashTable is NULL"); + return ptr; + } + + for (ptr = hash_table->pHashTable[index]; ptr; ptr = ptr->next) { + if (dph_compare_mac_addr(staAddr, ptr->staAddr)) { + *pAssocId = ptr->assocId; + break; + } + } + return ptr; +} + +/* --------------------------------------------------------------------- */ +/** + * dph_get_hash_entry + * + * FUNCTION: + * Get a pointer to the hash node + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param staId Station ID + * @return pointer to STA hash entry if lookup was a success \n + * NULL if lookup was a failure + */ + +tpDphHashNode dph_get_hash_entry(struct mac_context *mac, uint16_t peerIdx, + struct dph_hash_table *hash_table) +{ + if (peerIdx < hash_table->size) { + if (hash_table->pDphNodeArray[peerIdx].added) + return &hash_table->pDphNodeArray[peerIdx]; + else + return NULL; + } else + return NULL; + +} + +static inline tpDphHashNode get_node(struct mac_context *mac, uint8_t assocId, + struct dph_hash_table *hash_table) +{ + return &hash_table->pDphNodeArray[assocId]; +} + +/** ------------------------------------------------------------- + \fn dph_init_sta_state + \brief Initialize STA state. this function saves the staId from the current entry in the DPH table with given assocId + \ if validStaIdx flag is set. Otherwise it sets the staId to invalid. + \param struct mac_context * mac + \param tSirMacAddr staAddr + \param uint16_t assocId + \param uint8_t validStaIdx - true ==> the staId in the DPH entry with given assocId is valid and restore it back. + \ false ==> set the staId to invalid. + \return tpDphHashNode - DPH hash node if found. + -------------------------------------------------------------*/ + +tpDphHashNode dph_init_sta_state(struct mac_context *mac, tSirMacAddr staAddr, + uint16_t assocId, + struct dph_hash_table *hash_table) +{ + tpDphHashNode sta, pnext; + + if (assocId >= hash_table->size) { + pe_err("Invalid Assoc Id %d", assocId); + return NULL; + } + + sta = get_node(mac, (uint8_t) assocId, hash_table); + pnext = sta->next; + + /* Clear the STA node except for the next pointer */ + qdf_mem_zero((uint8_t *)sta, sizeof(tDphHashNode)); + sta->next = pnext; + + /* Initialize the assocId */ + sta->assocId = assocId; + + /* Initialize STA mac address */ + qdf_mem_copy(sta->staAddr, staAddr, sizeof(tSirMacAddr)); + + sta->added = 1; + sta->is_disassoc_deauth_in_progress = 0; + sta->sta_deletion_in_progress = false; + sta->valid = 1; + return sta; +} + +/* --------------------------------------------------------------------- */ +/** + * dph_add_hash_entry + * + * FUNCTION: + * Add entry to hash table + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param staAddr MAC address of the station + * @param staId Station ID assigned to the station + * @return Pointer to STA hash entry + */ + +tpDphHashNode dph_add_hash_entry(struct mac_context *mac, tSirMacAddr staAddr, + uint16_t assocId, + struct dph_hash_table *hash_table) +{ + tpDphHashNode ptr, node; + uint16_t index = hash_function(mac, staAddr, hash_table->size); + + pe_debug("assocId %d index %d STA addr", + assocId, index); + pe_debug(QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(staAddr)); + + if (assocId >= hash_table->size) { + pe_err("invalid STA id %d", assocId); + return NULL; + } + + if (hash_table->pDphNodeArray[assocId].added) { + pe_err("already added STA %d", assocId); + return NULL; + } + + for (ptr = hash_table->pHashTable[index]; ptr; ptr = ptr->next) { + if (ptr == ptr->next) { + pe_err("Infinite Loop"); + return NULL; + } + + if (dph_compare_mac_addr(staAddr, ptr->staAddr) + || ptr->assocId == assocId) + break; + } + + if (ptr) { + /* Duplicate entry */ + pe_err("assocId %d hashIndex %d entry exists", + assocId, index); + return NULL; + } else { + if (dph_init_sta_state + (mac, staAddr, assocId, hash_table) == NULL) { + pe_err("could not Init STA id: %d", assocId); + return NULL; + } + /* Add the node to the link list */ + hash_table->pDphNodeArray[assocId].next = + hash_table->pHashTable[index]; + hash_table->pHashTable[index] = + &hash_table->pDphNodeArray[assocId]; + + node = hash_table->pHashTable[index]; + return node; + } +} + +/* --------------------------------------------------------------------- */ +/** + * dph_delete_hash_entry + * + * FUNCTION: + * Delete entry from hash table + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param staAddr MAC address of the station + * @param staId Station ID assigned to the station + * @return QDF_STATUS_SUCCESS if successful, + * QDF_STATUS_E_FAILURE otherwise + */ + +QDF_STATUS dph_delete_hash_entry(struct mac_context *mac, tSirMacAddr staAddr, + uint16_t assocId, + struct dph_hash_table *hash_table) +{ + tpDphHashNode ptr, prev; + uint16_t index = hash_function(mac, staAddr, hash_table->size); + + pe_debug("assocId %d index %d STA addr", assocId, index); + pe_debug(QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(staAddr)); + + if (assocId >= hash_table->size) { + pe_err("invalid STA id %d", assocId); + return QDF_STATUS_E_FAILURE; + } + + if (hash_table->pDphNodeArray[assocId].added == 0) { + pe_err("STA %d never added", assocId); + return QDF_STATUS_E_FAILURE; + } + + for (prev = 0, ptr = hash_table->pHashTable[index]; + ptr; prev = ptr, ptr = ptr->next) { + if (dph_compare_mac_addr(staAddr, ptr->staAddr)) + break; + if (prev == ptr) { + pe_err("Infinite Loop"); + return QDF_STATUS_E_FAILURE; + } + } + + if (ptr) { + /* / Delete the entry after invalidating it */ + ptr->valid = 0; + memset(ptr->staAddr, 0, sizeof(ptr->staAddr)); + if (prev == 0) + hash_table->pHashTable[index] = ptr->next; + else + prev->next = ptr->next; + ptr->added = 0; + ptr->is_disassoc_deauth_in_progress = 0; + ptr->sta_deletion_in_progress = false; + ptr->next = 0; + } else { + pe_err("Entry not present STA addr"); + pe_err(QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(staAddr)); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + + diff --git a/drivers/staging/qcacld-3.0/core/mac/src/dph/dph_hash_table.h b/drivers/staging/qcacld-3.0/core/mac/src/dph/dph_hash_table.h new file mode 100644 index 0000000000000000000000000000000000000000..ef7583398427cd4416f58a0368ecf18a5627d739 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/dph/dph_hash_table.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011-2015, 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file dph_hash_table.h contains the definition of the scheduler class. + * + * Author: Sandesh Goel + * Date: 02/25/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#ifndef __DPH_HASH_TABLE_H__ +#define __DPH_HASH_TABLE_H__ + +#include "ani_global.h" +/* Compare MAC addresses, return true if same */ +static inline uint8_t dph_compare_mac_addr(uint8_t addr1[], uint8_t addr2[]) +{ + return (addr1[0] == addr2[0]) && + (addr1[1] == addr2[1]) && + (addr1[2] == addr2[2]) && + (addr1[3] == addr2[3]) && + (addr1[4] == addr2[4]) && (addr1[5] == addr2[5]); +} + +/** + * struct dph_hash_table - DPH hash table + * @pHashTable: The actual hash table + * @pDphNodeArray: The state array + * @size: The size of the hash table + */ +struct dph_hash_table { + tpDphHashNode *pHashTable; + tDphHashNode *pDphNodeArray; + uint16_t size; +}; + +tpDphHashNode dph_lookup_hash_entry(struct mac_context *mac, uint8_t staAddr[], + uint16_t *pStaId, + struct dph_hash_table *hash_table); + +/* Get a pointer to the hash node */ +tpDphHashNode dph_get_hash_entry(struct mac_context *mac, uint16_t staId, + struct dph_hash_table *hash_table); + +/* Add an entry to the hash table */ +tpDphHashNode dph_add_hash_entry(struct mac_context *mac, + tSirMacAddr staAddr, + uint16_t staId, + struct dph_hash_table *hash_table); + +/* Delete an entry from the hash table */ +QDF_STATUS dph_delete_hash_entry(struct mac_context *mac, + tSirMacAddr staAddr, uint16_t staId, + struct dph_hash_table *hash_table); + +/** + * dph_hash_table_init - Initialize a DPH Hash Table + * @mac: Global MAC Context + * @hash_table: Pointer to the Hash Table to initialize + */ +void dph_hash_table_init(struct mac_context *mac, + struct dph_hash_table *hash_table); + +/* Initialize STA state */ +tpDphHashNode dph_init_sta_state(struct mac_context *mac, + tSirMacAddr staAddr, + uint16_t staId, + struct dph_hash_table *hash_table); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/include/dot11f.h b/drivers/staging/qcacld-3.0/core/mac/src/include/dot11f.h new file mode 100644 index 0000000000000000000000000000000000000000..507f905e17b744bbc456f8bd0c5958d8eddbdf8a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/include/dot11f.h @@ -0,0 +1,11478 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DOT11F_H +#define DOT11F_H +/* + * \file dot11f.h + * + * \brief Structures, function prototypes & definitions + * for working with 802.11 Frames + * + * + * This file was automatically generated by 'framesc' + * Wed Sep 29 13:23:21 2021 from the following file(s): + * + * dot11f.frms + * + * PLEASE DON'T EDIT THIS FILE BY HAND! + * + * Instead, please update the input files & re-run + * 'framesc' For more information on 'framesc' & the + * frames language, run 'framesc --help'. + * + */ + +typedef uint32_t tDOT11F_U64[2]; + +#if defined (_MSC_VER) +#pragma warning (disable:4214) /* nonstandard extension used */ +#endif /* Microsoft C/C++ bit field types other than int */ + +#if !defined __must_check +#define __must_check +#endif + +#if !defined unlikely +#define unlikely(x) (x) +#endif + +/* + * Frames Return Codes: + * + * Success is indicated by a return value of zero. Failure is indicated + * by the presence of the high bit. Warnings encountered in the course + * of a successful parse are indicated by various bits in the lower 31 + * being turned on. + * + * For instance, a return value of 0x0000000a would indicate that the + * parse succeeded, but that a mandatory IE wasn't present, and some IE + * was found to be corrupt. + * + * + */ + +#define DOT11F_PARSE_SUCCESS (0x00000000) +#define DOT11F_UNKNOWN_IES (0x00000001) +#define DOT11F_MANDATORY_IE_MISSING (0x00000002) +#define DOT11F_INCOMPLETE_IE (0x00000004) +#define DOT11F_SKIPPED_BAD_IE (0x00000008) +#define DOT11F_LAST_IE_TOO_LONG (0x00000010) +#define DOT11F_DUPLICATE_IE (0x00000020) +#define DOT11F_BAD_FIXED_VALUE (0x00000040) +#define DOT11F_INCOMPLETE_TLV (0x00000080) +#define DOT11F_INVALID_TLV_LENGTH (0x00000100) +#define DOT11F_SKIPPED_BAD_TLV (0x00000200) +#define DOT11F_UNKNOWN_TLVS (0x00000400) +#define DOT11F_LAST_TLV_TOO_LONG (0x00000800) +#define DOT11F_MANDATORY_TLV_MISSING (0x00001000) +#define DOT11F_INTERNAL_ERROR (0x10000001) +#define DOT11F_MISSING_FIXED_FIELD (0x10000002) +#define DOT11F_BAD_INPUT_BUFFER (0x10000003) +#define DOT11F_BAD_OUTPUT_BUFFER (0x10000004) +#define DOT11F_BUFFER_OVERFLOW (0x10000005) +#define DOT11F_FAILED(code) ((code) & 0x10000000) +#define DOT11F_SUCCEEDED(code) ((code) == 0) +#define DOT11F_WARNED(code) (!DOT11F_SUCCEEDED(code) && !DOT11F_FAILED(code)) + +/********************************************************************* + * Fixed Fields * + ********************************************************************/ + +typedef struct sDot11fFfAID { + uint16_t associd; +} tDot11fFfAID; + +#define DOT11F_FF_AID_LEN (2) + +void dot11f_unpack_ff_AID(tpAniSirGlobal, uint8_t *, tDot11fFfAID *); + +void dot11f_pack_ff_aid(tpAniSirGlobal, tDot11fFfAID *, uint8_t *); + +typedef struct sDot11fFfAction { + uint8_t action; +} tDot11fFfAction; + +#define DOT11F_FF_ACTION_LEN (1) + +void dot11f_unpack_ff_action(tpAniSirGlobal, uint8_t *, tDot11fFfAction *); + +void dot11f_pack_ff_action(tpAniSirGlobal, tDot11fFfAction *, uint8_t *); + +typedef struct sDot11fFfAuthAlgo { + uint16_t algo; +} tDot11fFfAuthAlgo; + +#define DOT11F_FF_AUTHALGO_LEN (2) + +void dot11f_unpack_ff_AuthAlgo(tpAniSirGlobal, uint8_t *, + tDot11fFfAuthAlgo *); + +void dot11f_pack_ff_auth_algo(tpAniSirGlobal, tDot11fFfAuthAlgo *, uint8_t *); + +typedef struct sDot11fFfAuthSeqNo { + uint16_t no; +} tDot11fFfAuthSeqNo; + +#define DOT11F_FF_AUTHSEQNO_LEN (2) + +void dot11f_unpack_ff_AuthSeqNo(tpAniSirGlobal, uint8_t *, + tDot11fFfAuthSeqNo *); + +void dot11f_pack_ff_auth_seq_no(tpAniSirGlobal, tDot11fFfAuthSeqNo *, + uint8_t *); + +typedef struct sDot11fFfBeaconInterval { + uint16_t interval; +} tDot11fFfBeaconInterval; + +#define DOT11F_FF_BEACONINTERVAL_LEN (2) + +void dot11f_unpack_ff_BeaconInterval(tpAniSirGlobal, uint8_t *, + tDot11fFfBeaconInterval *); + +void dot11f_pack_ff_beacon_interval(tpAniSirGlobal, tDot11fFfBeaconInterval *, + uint8_t *); + +typedef struct sDot11fFfCapabilities { + uint16_t ess:1; + uint16_t ibss:1; + uint16_t cfPollable:1; + uint16_t cfPollReq:1; + uint16_t privacy:1; + uint16_t shortPreamble:1; + uint16_t pbcc:1; + uint16_t channelAgility:1; + uint16_t spectrumMgt:1; + uint16_t qos:1; + uint16_t shortSlotTime:1; + uint16_t apsd:1; + uint16_t rrm:1; + uint16_t dsssOfdm:1; + uint16_t delayedBA:1; + uint16_t immediateBA:1; +} tDot11fFfCapabilities; + +#define DOT11F_FF_CAPABILITIES_LEN (2) + +void dot11f_unpack_ff_capabilities(tpAniSirGlobal, uint8_t *, + tDot11fFfCapabilities *); + +void dot11f_pack_ff_capabilities(tpAniSirGlobal, tDot11fFfCapabilities *, + uint8_t *); + +#define CAPABILITIES_ESS_OFFSET 0 +#define CAPABILITIES_ESS_WIDTH 1 +#define CAPABILITIES_IBSS_OFFSET 1 +#define CAPABILITIES_IBSS_WIDTH 1 +#define CAPABILITIES_CFPOLLABLE_OFFSET 2 +#define CAPABILITIES_CFPOLLABLE_WIDTH 1 +#define CAPABILITIES_CFPOLLREQ_OFFSET 3 +#define CAPABILITIES_CFPOLLREQ_WIDTH 1 +#define CAPABILITIES_PRIVACY_OFFSET 4 +#define CAPABILITIES_PRIVACY_WIDTH 1 +#define CAPABILITIES_SHORTPREAMBLE_OFFSET 5 +#define CAPABILITIES_SHORTPREAMBLE_WIDTH 1 +#define CAPABILITIES_PBCC_OFFSET 6 +#define CAPABILITIES_PBCC_WIDTH 1 +#define CAPABILITIES_CHANNELAGILITY_OFFSET 7 +#define CAPABILITIES_CHANNELAGILITY_WIDTH 1 +#define CAPABILITIES_SPECTRUMMGT_OFFSET 8 +#define CAPABILITIES_SPECTRUMMGT_WIDTH 1 +#define CAPABILITIES_QOS_OFFSET 9 +#define CAPABILITIES_QOS_WIDTH 1 +#define CAPABILITIES_SHORTSLOTTIME_OFFSET 10 +#define CAPABILITIES_SHORTSLOTTIME_WIDTH 1 +#define CAPABILITIES_APSD_OFFSET 11 +#define CAPABILITIES_APSD_WIDTH 1 +#define CAPABILITIES_RRM_OFFSET 12 +#define CAPABILITIES_RRM_WIDTH 1 +#define CAPABILITIES_DSSSOFDM_OFFSET 13 +#define CAPABILITIES_DSSSOFDM_WIDTH 1 +#define CAPABILITIES_DELAYEDBA_OFFSET 14 +#define CAPABILITIES_DELAYEDBA_WIDTH 1 +#define CAPABILITIES_IMMEDIATEBA_OFFSET 15 +#define CAPABILITIES_IMMEDIATEBA_WIDTH 1 + +typedef struct sDot11fFfCategory { + uint8_t category; +} tDot11fFfCategory; + +#define DOT11F_FF_CATEGORY_LEN (1) + +void dot11f_unpack_ff_category(tpAniSirGlobal, uint8_t *, + tDot11fFfCategory *); + +void dot11f_pack_ff_category(tpAniSirGlobal, tDot11fFfCategory *, uint8_t *); + +typedef struct sDot11fFfCurrentAPAddress { + uint8_t mac[6]; +} tDot11fFfCurrentAPAddress; + +#define DOT11F_FF_CURRENTAPADDRESS_LEN (6) + +void dot11f_unpack_ff_current_ap_address(tpAniSirGlobal, uint8_t *, + tDot11fFfCurrentAPAddress *); + +void dot11f_pack_ff_current_ap_address(tpAniSirGlobal, + tDot11fFfCurrentAPAddress *, + uint8_t *); + + +typedef struct sDot11fFfDialogToken { + uint8_t token; +} tDot11fFfDialogToken; + +#define DOT11F_FF_DIALOGTOKEN_LEN (1) + +void dot11f_unpack_ff_dialog_token(tpAniSirGlobal, uint8_t *, + tDot11fFfDialogToken *); + +void dot11f_pack_ff_dialog_token(tpAniSirGlobal, tDot11fFfDialogToken *, + uint8_t *); + +typedef struct sDot11fFfLinkMargin { + uint8_t linkMargin; +} tDot11fFfLinkMargin; + +#define DOT11F_FF_LINKMARGIN_LEN (1) + +void dot11f_unpack_ff_link_margin(tpAniSirGlobal, uint8_t *, + tDot11fFfLinkMargin *); + +void dot11f_pack_ff_link_margin(tpAniSirGlobal, tDot11fFfLinkMargin *, + uint8_t *); + +typedef struct sDot11fFfListenInterval { + uint16_t interval; +} tDot11fFfListenInterval; + +#define DOT11F_FF_LISTENINTERVAL_LEN (2) + +void dot11f_unpack_ff_ListenInterval(tpAniSirGlobal, uint8_t *, + tDot11fFfListenInterval *); + +void dot11f_pack_ff_listen_interval(tpAniSirGlobal, tDot11fFfListenInterval *, + uint8_t *); + +typedef struct sDot11fFfMaxTxPower { + uint8_t maxTxPower; +} tDot11fFfMaxTxPower; + +#define DOT11F_FF_MAXTXPOWER_LEN (1) + +void dot11f_unpack_ff_max_tx_power(tpAniSirGlobal, uint8_t *, + tDot11fFfMaxTxPower *); + +void dot11f_pack_ff_max_tx_power(tpAniSirGlobal, tDot11fFfMaxTxPower *, + uint8_t *); + +typedef struct sDot11fFfNumOfRepetitions { + uint16_t repetitions; +} tDot11fFfNumOfRepetitions; + +#define DOT11F_FF_NUMOFREPETITIONS_LEN (2) + +void dot11f_unpack_ff_num_of_repetitions(tpAniSirGlobal, uint8_t *, + tDot11fFfNumOfRepetitions *); + +void dot11f_pack_ff_num_of_repetitions(tpAniSirGlobal, + tDot11fFfNumOfRepetitions *, + uint8_t *); + + +typedef struct sDot11fFfOperatingMode { + uint8_t chanWidth:2; + uint8_t reserved:2; + uint8_t rxNSS:3; + uint8_t rxNSSType:1; +} tDot11fFfOperatingMode; + +#define DOT11F_FF_OPERATINGMODE_LEN (1) + +void dot11f_unpack_ff_operating_mode(tpAniSirGlobal, uint8_t *, + tDot11fFfOperatingMode *); + +void dot11f_pack_ff_operating_mode(tpAniSirGlobal, tDot11fFfOperatingMode *, + uint8_t *); + +#define OPERATINGMODE_CHANWIDTH_OFFSET 0 +#define OPERATINGMODE_CHANWIDTH_WIDTH 2 +#define OPERATINGMODE_RESERVED_OFFSET 2 +#define OPERATINGMODE_RESERVED_WIDTH 2 +#define OPERATINGMODE_RXNSS_OFFSET 4 +#define OPERATINGMODE_RXNSS_WIDTH 3 +#define OPERATINGMODE_RXNSSTYPE_OFFSET 7 +#define OPERATINGMODE_RXNSSTYPE_WIDTH 1 + +typedef struct sDot11fFfRCPI { + uint8_t rcpi; +} tDot11fFfRCPI; + +#define DOT11F_FF_RCPI_LEN (1) + +void dot11f_unpack_ff_rcpi(tpAniSirGlobal, uint8_t *, tDot11fFfRCPI *); + +void dot11f_pack_ff_rcpi(tpAniSirGlobal, tDot11fFfRCPI *, uint8_t *); + +typedef struct sDot11fFfRSNI { + uint8_t rsni; +} tDot11fFfRSNI; + +#define DOT11F_FF_RSNI_LEN (1) + +void dot11f_unpack_ff_rsni(tpAniSirGlobal, uint8_t *, tDot11fFfRSNI *); + +void dot11f_pack_ff_rsni(tpAniSirGlobal, tDot11fFfRSNI *, uint8_t *); + +typedef struct sDot11fFfReason { + uint16_t code; +} tDot11fFfReason; + +#define DOT11F_FF_REASON_LEN (2) + +void dot11f_unpack_ff_Reason(tpAniSirGlobal, uint8_t *, tDot11fFfReason *); + +void dot11f_pack_ff_reason(tpAniSirGlobal, tDot11fFfReason *, uint8_t *); + +typedef struct sDot11fFfRxAntennaId { + uint8_t antennaId; +} tDot11fFfRxAntennaId; + +#define DOT11F_FF_RXANTENNAID_LEN (1) + +void dot11f_unpack_ff_rx_antenna_id(tpAniSirGlobal, uint8_t *, + tDot11fFfRxAntennaId *); + +void dot11f_pack_ff_rx_antenna_id(tpAniSirGlobal, tDot11fFfRxAntennaId *, + uint8_t *); + +typedef struct sDot11fFfSMPowerModeSet { + uint8_t PowerSave_En:1; + uint8_t Mode:1; + uint8_t reserved:6; +} tDot11fFfSMPowerModeSet; + +#define DOT11F_FF_SMPOWERMODESET_LEN (1) + +void dot11f_unpack_ff_sm_power_mode_set(tpAniSirGlobal, uint8_t *, + tDot11fFfSMPowerModeSet *); + +void dot11f_pack_ff_sm_power_mode_set(tpAniSirGlobal, tDot11fFfSMPowerModeSet *, + uint8_t *); + +#define SMPOWERMODESET_POWERSAVE_EN_OFFSET 0 +#define SMPOWERMODESET_POWERSAVE_EN_WIDTH 1 +#define SMPOWERMODESET_MODE_OFFSET 1 +#define SMPOWERMODESET_MODE_WIDTH 1 +#define SMPOWERMODESET_RESERVED_OFFSET 2 +#define SMPOWERMODESET_RESERVED_WIDTH 6 + +typedef struct sDot11fFfStatus { + uint16_t status; +} tDot11fFfStatus; + +#define DOT11F_FF_STATUS_LEN (2) + +void dot11f_unpack_ff_Status(tpAniSirGlobal, uint8_t *, tDot11fFfStatus *); + +void dot11f_pack_ff_status(tpAniSirGlobal, tDot11fFfStatus *, uint8_t *); + +typedef struct sDot11fFfStatusCode { + uint8_t statusCode; +} tDot11fFfStatusCode; + +#define DOT11F_FF_STATUSCODE_LEN (1) + +void dot11f_unpack_ff_status_code(tpAniSirGlobal, uint8_t *, + tDot11fFfStatusCode *); + +void dot11f_pack_ff_status_code(tpAniSirGlobal, tDot11fFfStatusCode *, + uint8_t *); + +typedef struct sDot11fFfTPCEleID { + uint8_t TPCId; +} tDot11fFfTPCEleID; + +#define DOT11F_FF_TPCELEID_LEN (1) + +void dot11f_unpack_ff_tpc_ele_id(tpAniSirGlobal, uint8_t *, + tDot11fFfTPCEleID *); + +void dot11f_pack_ff_tpc_ele_id(tpAniSirGlobal, tDot11fFfTPCEleID *, uint8_t *); + +typedef struct sDot11fFfTPCEleLen { + uint8_t TPCLen; +} tDot11fFfTPCEleLen; + +#define DOT11F_FF_TPCELELEN_LEN (1) + +void dot11f_unpack_ff_tpc_ele_len(tpAniSirGlobal, uint8_t *, + tDot11fFfTPCEleLen *); + +void dot11f_pack_ff_tpc_ele_len(tpAniSirGlobal, tDot11fFfTPCEleLen *, + uint8_t *); + +typedef struct sDot11fFfTSInfo { + uint32_t traffic_type:1; + uint32_t tsid:4; + uint32_t direction:2; + uint32_t access_policy:2; + uint32_t aggregation:1; + uint32_t psb:1; + uint32_t user_priority:3; + uint32_t tsinfo_ack_pol:2; + uint32_t schedule:1; + uint32_t unused:15; +} tDot11fFfTSInfo; + +#define DOT11F_FF_TSINFO_LEN (3) + +void dot11f_unpack_ff_ts_info(tpAniSirGlobal, uint8_t *, tDot11fFfTSInfo *); + +void dot11f_pack_ff_ts_info(tpAniSirGlobal, tDot11fFfTSInfo *, uint8_t *); + +#define TSINFO_TRAFFIC_TYPE_OFFSET 0 +#define TSINFO_TRAFFIC_TYPE_WIDTH 1 +#define TSINFO_TSID_OFFSET 1 +#define TSINFO_TSID_WIDTH 4 +#define TSINFO_DIRECTION_OFFSET 5 +#define TSINFO_DIRECTION_WIDTH 2 +#define TSINFO_ACCESS_POLICY_OFFSET 7 +#define TSINFO_ACCESS_POLICY_WIDTH 2 +#define TSINFO_AGGREGATION_OFFSET 9 +#define TSINFO_AGGREGATION_WIDTH 1 +#define TSINFO_PSB_OFFSET 10 +#define TSINFO_PSB_WIDTH 1 +#define TSINFO_USER_PRIORITY_OFFSET 11 +#define TSINFO_USER_PRIORITY_WIDTH 3 +#define TSINFO_TSINFO_ACK_POL_OFFSET 14 +#define TSINFO_TSINFO_ACK_POL_WIDTH 2 +#define TSINFO_SCHEDULE_OFFSET 16 +#define TSINFO_SCHEDULE_WIDTH 1 +#define TSINFO_UNUSED_OFFSET 17 +#define TSINFO_UNUSED_WIDTH 15 + +typedef struct sDot11fFfTimeStamp { + tDOT11F_U64 timestamp; +} tDot11fFfTimeStamp; + +#define DOT11F_FF_TIMESTAMP_LEN (8) + +void dot11f_unpack_ff_time_stamp(tpAniSirGlobal, uint8_t *, + tDot11fFfTimeStamp *); + +void dot11f_pack_ff_time_stamp(tpAniSirGlobal, tDot11fFfTimeStamp *, + uint8_t *); + +typedef struct sDot11fFfTransactionId { + uint8_t transId[2]; +} tDot11fFfTransactionId; + +#define DOT11F_FF_TRANSACTIONID_LEN (2) + +void dot11f_unpack_ff_transaction_id(tpAniSirGlobal, uint8_t *, + tDot11fFfTransactionId *); + +void dot11f_pack_ff_transaction_id(tpAniSirGlobal, tDot11fFfTransactionId *, + uint8_t *); + +typedef struct sDot11fFfTxAntennaId { + uint8_t antennaId; +} tDot11fFfTxAntennaId; + +#define DOT11F_FF_TXANTENNAID_LEN (1) + +void dot11f_unpack_ff_tx_antenna_id(tpAniSirGlobal, uint8_t *, + tDot11fFfTxAntennaId *); + +void dot11f_pack_ff_tx_antenna_id(tpAniSirGlobal, tDot11fFfTxAntennaId *, + uint8_t *); + +typedef struct sDot11fFfTxPower { + uint8_t txPower; +} tDot11fFfTxPower; + +#define DOT11F_FF_TXPOWER_LEN (1) + +void dot11f_unpack_ff_tx_power(tpAniSirGlobal, uint8_t *, + tDot11fFfTxPower *); + +void dot11f_pack_ff_tx_power(tpAniSirGlobal, tDot11fFfTxPower *, uint8_t *); + +typedef struct sDot11fFfVhtMembershipStatusArray { + uint8_t membershipStatusArray[8]; +} tDot11fFfVhtMembershipStatusArray; + +#define DOT11F_FF_VHTMEMBERSHIPSTATUSARRAY_LEN (8) + +void dot11f_unpack_ff_vht_membership_status_array(tpAniSirGlobal, uint8_t *, + tDot11fFfVhtMembershipStatusArray *); + +void dot11f_pack_ff_vht_membership_status_array(tpAniSirGlobal, + tDot11fFfVhtMembershipStatusArray *, + uint8_t *); + + +typedef struct sDot11fFfVhtUserPositionArray { + uint8_t userPositionArray[16]; +} tDot11fFfVhtUserPositionArray; + +#define DOT11F_FF_VHTUSERPOSITIONARRAY_LEN (16) + +void dot11f_unpack_ff_vht_user_position_array(tpAniSirGlobal, uint8_t *, + tDot11fFfVhtUserPositionArray *); + +void dot11f_pack_ff_vht_user_position_array(tpAniSirGlobal, + tDot11fFfVhtUserPositionArray *, + uint8_t *); + + +typedef struct sDot11fFfaddba_param_set { + uint16_t amsdu_supp:1; + uint16_t policy:1; + uint16_t tid:4; + uint16_t buff_size:10; +} tDot11fFfaddba_param_set; + +#define DOT11F_FF_ADDBA_PARAM_SET_LEN (2) + +void dot11f_unpack_ff_addba_param_set(tpAniSirGlobal, uint8_t *, + tDot11fFfaddba_param_set *); + +void dot11f_pack_ff_addba_param_set(tpAniSirGlobal, tDot11fFfaddba_param_set *, + uint8_t *); + +#define ADDBA_PARAM_SET_AMSDU_SUPP_OFFSET 0 +#define ADDBA_PARAM_SET_AMSDU_SUPP_WIDTH 1 +#define ADDBA_PARAM_SET_POLICY_OFFSET 1 +#define ADDBA_PARAM_SET_POLICY_WIDTH 1 +#define ADDBA_PARAM_SET_TID_OFFSET 2 +#define ADDBA_PARAM_SET_TID_WIDTH 4 +#define ADDBA_PARAM_SET_BUFF_SIZE_OFFSET 6 +#define ADDBA_PARAM_SET_BUFF_SIZE_WIDTH 10 + +typedef struct sDot11fFfba_start_seq_ctrl { + uint16_t frag_number:4; + uint16_t ssn:12; +} tDot11fFfba_start_seq_ctrl; + +#define DOT11F_FF_BA_START_SEQ_CTRL_LEN (2) + +void dot11f_unpack_ff_ba_start_seq_ctrl(tpAniSirGlobal, uint8_t *, + tDot11fFfba_start_seq_ctrl *); + +void dot11f_pack_ff_ba_start_seq_ctrl(tpAniSirGlobal, + tDot11fFfba_start_seq_ctrl *, + uint8_t *); + + +#define BA_START_SEQ_CTRL_FRAG_NUMBER_OFFSET 0 +#define BA_START_SEQ_CTRL_FRAG_NUMBER_WIDTH 4 +#define BA_START_SEQ_CTRL_SSN_OFFSET 4 +#define BA_START_SEQ_CTRL_SSN_WIDTH 12 + +typedef struct sDot11fFfba_timeout { + uint16_t timeout; +} tDot11fFfba_timeout; + +#define DOT11F_FF_BA_TIMEOUT_LEN (2) + +void dot11f_unpack_ff_ba_timeout(tpAniSirGlobal, uint8_t *, + tDot11fFfba_timeout *); + +void dot11f_pack_ff_ba_timeout(tpAniSirGlobal, tDot11fFfba_timeout *, + uint8_t *); + +typedef struct sDot11fFfdelba_param_set { + uint16_t reserved:11; + uint16_t initiator:1; + uint16_t tid:4; +} tDot11fFfdelba_param_set; + +#define DOT11F_FF_DELBA_PARAM_SET_LEN (2) + +void dot11f_unpack_ff_delba_param_set(tpAniSirGlobal, uint8_t *, + tDot11fFfdelba_param_set *); + +void dot11f_pack_ff_delba_param_set(tpAniSirGlobal, tDot11fFfdelba_param_set *, + uint8_t *); + +#define DELBA_PARAM_SET_RESERVED_OFFSET 0 +#define DELBA_PARAM_SET_RESERVED_WIDTH 11 +#define DELBA_PARAM_SET_INITIATOR_OFFSET 11 +#define DELBA_PARAM_SET_INITIATOR_WIDTH 1 +#define DELBA_PARAM_SET_TID_OFFSET 12 +#define DELBA_PARAM_SET_TID_WIDTH 4 + +typedef struct sDot11fFfext_chan_switch_ann_action { + uint32_t switch_mode:8; + uint32_t op_class:8; + uint32_t new_channel:8; + uint32_t switch_count:8; +} tDot11fFfext_chan_switch_ann_action; + +#define DOT11F_FF_EXT_CHAN_SWITCH_ANN_ACTION_LEN (4) + +void dot11f_unpack_ff_ext_chan_switch_ann_action(tpAniSirGlobal, uint8_t *, + tDot11fFfext_chan_switch_ann_action *); + +void dot11f_pack_ff_ext_chan_switch_ann_action(tpAniSirGlobal, + tDot11fFfext_chan_switch_ann_action *, + uint8_t *); + + +#define EXT_CHAN_SWITCH_ANN_ACTION_SWITCH_MODE_OFFSET 0 +#define EXT_CHAN_SWITCH_ANN_ACTION_SWITCH_MODE_WIDTH 8 +#define EXT_CHAN_SWITCH_ANN_ACTION_OP_CLASS_OFFSET 8 +#define EXT_CHAN_SWITCH_ANN_ACTION_OP_CLASS_WIDTH 8 +#define EXT_CHAN_SWITCH_ANN_ACTION_NEW_CHANNEL_OFFSET 16 +#define EXT_CHAN_SWITCH_ANN_ACTION_NEW_CHANNEL_WIDTH 8 +#define EXT_CHAN_SWITCH_ANN_ACTION_SWITCH_COUNT_OFFSET 24 +#define EXT_CHAN_SWITCH_ANN_ACTION_SWITCH_COUNT_WIDTH 8 + +typedef struct sDot11fFfp2p_action_oui { + uint8_t oui_data[4]; +} tDot11fFfp2p_action_oui; + +#define DOT11F_FF_P2P_ACTION_OUI_LEN (4) + +void dot11f_unpack_ff_p2p_action_oui(tpAniSirGlobal, uint8_t *, + tDot11fFfp2p_action_oui *); + +void dot11f_pack_ff_p2p_action_oui(tpAniSirGlobal, tDot11fFfp2p_action_oui *, + uint8_t *); + +typedef struct sDot11fFfp2p_action_subtype { + uint8_t subtype; +} tDot11fFfp2p_action_subtype; + +#define DOT11F_FF_P2P_ACTION_SUBTYPE_LEN (1) + +void dot11f_unpack_ff_p2p_action_subtype(tpAniSirGlobal, uint8_t *, + tDot11fFfp2p_action_subtype *); + +void dot11f_pack_ff_p2p_action_subtype(tpAniSirGlobal, + tDot11fFfp2p_action_subtype *, + uint8_t *); + + +typedef struct sDot11fFfvendor_action_subtype { + uint8_t subtype; +} tDot11fFfvendor_action_subtype; + +#define DOT11F_FF_VENDOR_ACTION_SUBTYPE_LEN (1) + +void dot11f_unpack_ff_vendor_action_subtype(tpAniSirGlobal, uint8_t *, + tDot11fFfvendor_action_subtype *); + +void dot11f_pack_ff_vendor_action_subtype(tpAniSirGlobal, + tDot11fFfvendor_action_subtype *, + uint8_t *); + + +typedef struct sDot11fFfvendor_oui { + uint8_t oui_data[3]; +} tDot11fFfvendor_oui; + +#define DOT11F_FF_VENDOR_OUI_LEN (3) + +void dot11f_unpack_ff_vendor_oui(tpAniSirGlobal, uint8_t *, + tDot11fFfvendor_oui *); + +void dot11f_pack_ff_vendor_oui(tpAniSirGlobal, tDot11fFfvendor_oui *, + uint8_t *); + +/********************************************************************* + * TLVs * + ********************************************************************/ + + +/* ID 1 (0x0001) */ +typedef struct sDot11fTLVAuthorizedMACs { + uint8_t present; + uint8_t mac[6]; +} tDot11fTLVAuthorizedMACs; + +#define DOT11F_TLV_AUTHORIZEDMACS (1) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_AUTHORIZEDMACS_MIN_LEN (6) + +#define DOT11F_TLV_AUTHORIZEDMACS_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_authorized_ma_cs( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVAuthorizedMACs*); + +uint32_t dot11f_pack_tlv_authorized_ma_cs( + tpAniSirGlobal, + tDot11fTLVAuthorizedMACs *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_AuthorizedMACs( + tpAniSirGlobal, + tDot11fTLVAuthorizedMACs *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 3 (0x0003) */ +typedef struct sDot11fTLVRequestToEnroll { + uint8_t present; + uint8_t req; +} tDot11fTLVRequestToEnroll; + +#define DOT11F_TLV_REQUESTTOENROLL (3) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_REQUESTTOENROLL_MIN_LEN (1) + +#define DOT11F_TLV_REQUESTTOENROLL_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_RequestToEnroll( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVRequestToEnroll*); + +uint32_t dot11f_pack_tlv_request_to_enroll( + tpAniSirGlobal, + tDot11fTLVRequestToEnroll *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_RequestToEnroll( + tpAniSirGlobal, + tDot11fTLVRequestToEnroll *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 0 (0x0000) */ +typedef struct sDot11fTLVVersion2 { + uint8_t present; + uint8_t minor:4; + uint8_t major:4; +} tDot11fTLVVersion2; + +#define DOT11F_TLV_VERSION2 (0) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_VERSION2_MIN_LEN (1) + +#define DOT11F_TLV_VERSION2_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_version2( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVVersion2*); + +uint32_t dot11f_pack_tlv_version2( + tpAniSirGlobal, + tDot11fTLVVersion2 *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_Version2( + tpAniSirGlobal, + tDot11fTLVVersion2 *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4183 (0x1057) */ +typedef struct sDot11fTLVAPSetupLocked { + uint8_t present; + uint8_t fLocked; +} tDot11fTLVAPSetupLocked; + +#define DOT11F_TLV_APSETUPLOCKED (4183) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_APSETUPLOCKED_MIN_LEN (3) + +#define DOT11F_TLV_APSETUPLOCKED_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_APSetupLocked( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVAPSetupLocked*); + +uint32_t dot11f_pack_tlv_ap_setup_locked( + tpAniSirGlobal, + tDot11fTLVAPSetupLocked *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_APSetupLocked( + tpAniSirGlobal, + tDot11fTLVAPSetupLocked *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4098 (0x1002) */ +typedef struct sDot11fTLVAssociationState { + uint8_t present; + uint16_t state; +} tDot11fTLVAssociationState; + +#define DOT11F_TLV_ASSOCIATIONSTATE (4098) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_ASSOCIATIONSTATE_MIN_LEN (4) + +#define DOT11F_TLV_ASSOCIATIONSTATE_MAX_LEN (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_AssociationState( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVAssociationState*); + +uint32_t dot11f_pack_tlv_association_state( + tpAniSirGlobal, + tDot11fTLVAssociationState *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_AssociationState( + tpAniSirGlobal, + tDot11fTLVAssociationState *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4104 (0x1008) */ +typedef struct sDot11fTLVConfigMethods { + uint8_t present; + uint16_t methods; +} tDot11fTLVConfigMethods; + +#define DOT11F_TLV_CONFIGMETHODS (4104) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_CONFIGMETHODS_MIN_LEN (4) + +#define DOT11F_TLV_CONFIGMETHODS_MAX_LEN (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_ConfigMethods( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVConfigMethods*); + +uint32_t dot11f_pack_tlv_config_methods( + tpAniSirGlobal, + tDot11fTLVConfigMethods *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_ConfigMethods( + tpAniSirGlobal, + tDot11fTLVConfigMethods *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4105 (0x1009) */ +typedef struct sDot11fTLVConfigurationError { + uint8_t present; + uint16_t error; +} tDot11fTLVConfigurationError; + +#define DOT11F_TLV_CONFIGURATIONERROR (4105) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_CONFIGURATIONERROR_MIN_LEN (4) + +#define DOT11F_TLV_CONFIGURATIONERROR_MAX_LEN (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_ConfigurationError( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVConfigurationError*); + +uint32_t dot11f_pack_tlv_configuration_error( + tpAniSirGlobal, + tDot11fTLVConfigurationError *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_ConfigurationError( + tpAniSirGlobal, + tDot11fTLVConfigurationError *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4113 (0x1011) */ +typedef struct sDot11fTLVDeviceName { + uint8_t present; + uint8_t num_text; + uint8_t text[32]; +} tDot11fTLVDeviceName; + +#define DOT11F_TLV_DEVICENAME (4113) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_DEVICENAME_MIN_LEN (2) + +#define DOT11F_TLV_DEVICENAME_MAX_LEN (34) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_device_name( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVDeviceName*); + +uint32_t dot11f_pack_tlv_device_name( + tpAniSirGlobal, + tDot11fTLVDeviceName *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_DeviceName( + tpAniSirGlobal, + tDot11fTLVDeviceName *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4114 (0x1012) */ +typedef struct sDot11fTLVDevicePasswordID { + uint8_t present; + uint16_t id; +} tDot11fTLVDevicePasswordID; + +#define DOT11F_TLV_DEVICEPASSWORDID (4114) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_DEVICEPASSWORDID_MIN_LEN (4) + +#define DOT11F_TLV_DEVICEPASSWORDID_MAX_LEN (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_DevicePasswordID( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVDevicePasswordID*); + +uint32_t dot11f_pack_tlv_device_password_id( + tpAniSirGlobal, + tDot11fTLVDevicePasswordID *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_DevicePasswordID( + tpAniSirGlobal, + tDot11fTLVDevicePasswordID *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 8 (0x0008) */ +typedef struct sDot11fTLVExtendedListenTiming { + uint8_t present; + uint16_t availibilityPeriod; + uint16_t availibilityInterval; +} tDot11fTLVExtendedListenTiming; + +#define DOT11F_TLV_EXTENDEDLISTENTIMING (8) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_EXTENDEDLISTENTIMING_MIN_LEN (5) + +#define DOT11F_TLV_EXTENDEDLISTENTIMING_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_extended_listen_timing( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVExtendedListenTiming*); + +uint32_t dot11f_pack_tlv_extended_listen_timing( + tpAniSirGlobal, + tDot11fTLVExtendedListenTiming *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_ExtendedListenTiming( + tpAniSirGlobal, + tDot11fTLVExtendedListenTiming *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 6 (0x0006) */ +typedef struct sDot11fTLVListenChannel { + uint8_t present; + uint8_t countryString[3]; + uint8_t regulatoryClass; + uint8_t channel; +} tDot11fTLVListenChannel; + +#define DOT11F_TLV_LISTENCHANNEL (6) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_LISTENCHANNEL_MIN_LEN (6) + +#define DOT11F_TLV_LISTENCHANNEL_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_listen_channel( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVListenChannel*); + +uint32_t dot11f_pack_tlv_listen_channel( + tpAniSirGlobal, + tDot11fTLVListenChannel *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_ListenChannel( + tpAniSirGlobal, + tDot11fTLVListenChannel *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4129 (0x1021) */ +typedef struct sDot11fTLVManufacturer { + uint8_t present; + uint8_t num_name; + uint8_t name[64]; +} tDot11fTLVManufacturer; + +#define DOT11F_TLV_MANUFACTURER (4129) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_MANUFACTURER_MIN_LEN (2) + +#define DOT11F_TLV_MANUFACTURER_MAX_LEN (66) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_manufacturer( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVManufacturer*); + +uint32_t dot11f_pack_tlv_manufacturer( + tpAniSirGlobal, + tDot11fTLVManufacturer *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_Manufacturer( + tpAniSirGlobal, + tDot11fTLVManufacturer *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 1 (0x0001) */ +typedef struct sDot11fTLVMinorReasonCode { + uint8_t present; + uint8_t minorReasonCode; +} tDot11fTLVMinorReasonCode; + +#define DOT11F_TLV_MINORREASONCODE (1) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_MINORREASONCODE_MIN_LEN (2) + +#define DOT11F_TLV_MINORREASONCODE_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_MinorReasonCode( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVMinorReasonCode*); + +uint32_t dot11f_pack_tlv_minor_reason_code( + tpAniSirGlobal, + tDot11fTLVMinorReasonCode *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_MinorReasonCode( + tpAniSirGlobal, + tDot11fTLVMinorReasonCode *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4131 (0x1023) */ +typedef struct sDot11fTLVModelName { + uint8_t present; + uint8_t num_text; + uint8_t text[32]; +} tDot11fTLVModelName; + +#define DOT11F_TLV_MODELNAME (4131) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_MODELNAME_MIN_LEN (2) + +#define DOT11F_TLV_MODELNAME_MAX_LEN (34) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_model_name( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVModelName*); + +uint32_t dot11f_pack_tlv_model_name( + tpAniSirGlobal, + tDot11fTLVModelName *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_ModelName( + tpAniSirGlobal, + tDot11fTLVModelName *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4132 (0x1024) */ +typedef struct sDot11fTLVModelNumber { + uint8_t present; + uint8_t num_text; + uint8_t text[32]; +} tDot11fTLVModelNumber; + +#define DOT11F_TLV_MODELNUMBER (4132) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_MODELNUMBER_MIN_LEN (2) + +#define DOT11F_TLV_MODELNUMBER_MAX_LEN (34) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_model_number( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVModelNumber*); + +uint32_t dot11f_pack_tlv_model_number( + tpAniSirGlobal, + tDot11fTLVModelNumber *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_ModelNumber( + tpAniSirGlobal, + tDot11fTLVModelNumber *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 12 (0x000c) */ +typedef struct sDot11fTLVNoticeOfAbsence { + uint8_t present; + uint8_t index; + uint8_t CTSWindowOppPS; + uint8_t num_NoADesc; + uint8_t NoADesc[36]; +} tDot11fTLVNoticeOfAbsence; + +#define DOT11F_TLV_NOTICEOFABSENCE (12) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_NOTICEOFABSENCE_MIN_LEN (3) + +#define DOT11F_TLV_NOTICEOFABSENCE_MAX_LEN (39) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_notice_of_absence( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVNoticeOfAbsence*); + +uint32_t dot11f_pack_tlv_notice_of_absence( + tpAniSirGlobal, + tDot11fTLVNoticeOfAbsence *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_NoticeOfAbsence( + tpAniSirGlobal, + tDot11fTLVNoticeOfAbsence *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 17 (0x0011) */ +typedef struct sDot11fTLVOperatingChannel { + uint8_t present; + uint8_t countryString[3]; + uint8_t regulatoryClass; + uint8_t channel; +} tDot11fTLVOperatingChannel; + +#define DOT11F_TLV_OPERATINGCHANNEL (17) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_OPERATINGCHANNEL_MIN_LEN (6) + +#define DOT11F_TLV_OPERATINGCHANNEL_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_operating_channel( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVOperatingChannel*); + +uint32_t dot11f_pack_tlv_operating_channel( + tpAniSirGlobal, + tDot11fTLVOperatingChannel *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_OperatingChannel( + tpAniSirGlobal, + tDot11fTLVOperatingChannel *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 2 (0x0002) */ +typedef struct sDot11fTLVP2PCapability { + uint8_t present; + uint8_t deviceCapability; + uint8_t groupCapability; +} tDot11fTLVP2PCapability; + +#define DOT11F_TLV_P2PCAPABILITY (2) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_P2PCAPABILITY_MIN_LEN (3) + +#define DOT11F_TLV_P2PCAPABILITY_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_p2_p_capability( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVP2PCapability*); + +uint32_t dot11f_pack_tlv_p2_p_capability( + tpAniSirGlobal, + tDot11fTLVP2PCapability *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_P2PCapability( + tpAniSirGlobal, + tDot11fTLVP2PCapability *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 3 (0x0003) */ +typedef struct sDot11fTLVP2PDeviceId { + uint8_t present; + uint8_t P2PDeviceAddress[6]; +} tDot11fTLVP2PDeviceId; + +#define DOT11F_TLV_P2PDEVICEID (3) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_P2PDEVICEID_MIN_LEN (7) + +#define DOT11F_TLV_P2PDEVICEID_MAX_LEN (7) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_p2_p_device_id( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVP2PDeviceId*); + +uint32_t dot11f_pack_tlv_p2_p_device_id( + tpAniSirGlobal, + tDot11fTLVP2PDeviceId *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_P2PDeviceId( + tpAniSirGlobal, + tDot11fTLVP2PDeviceId *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 13 (0x000d) */ +typedef struct sDot11fTLVP2PDeviceInfo { + uint8_t present; + uint8_t P2PDeviceAddress[6]; + uint16_t configMethod; + uint8_t primaryDeviceType[8]; + tDot11fTLVDeviceName DeviceName; +} tDot11fTLVP2PDeviceInfo; + +#define DOT11F_TLV_P2PDEVICEINFO (13) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_P2PDEVICEINFO_MIN_LEN (17) + +#define DOT11F_TLV_P2PDEVICEINFO_MAX_LEN (53) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_p2_p_device_info( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVP2PDeviceInfo*); + +uint32_t dot11f_pack_tlv_p2_p_device_info( + tpAniSirGlobal, + tDot11fTLVP2PDeviceInfo *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_P2PDeviceInfo( + tpAniSirGlobal, + tDot11fTLVP2PDeviceInfo *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 14 (0x000e) */ +typedef struct sDot11fTLVP2PGroupInfo { + uint8_t present; + uint8_t num_P2PClientInfoDesc; + uint8_t P2PClientInfoDesc[1024]; +} tDot11fTLVP2PGroupInfo; + +#define DOT11F_TLV_P2PGROUPINFO (14) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_P2PGROUPINFO_MIN_LEN (1) + +#define DOT11F_TLV_P2PGROUPINFO_MAX_LEN (1025) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_p2_p_group_info( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVP2PGroupInfo*); + +uint32_t dot11f_pack_tlv_p2_p_group_info( + tpAniSirGlobal, + tDot11fTLVP2PGroupInfo *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_P2PGroupInfo( + tpAniSirGlobal, + tDot11fTLVP2PGroupInfo *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 0 (0x0000) */ +typedef struct sDot11fTLVP2PStatus { + uint8_t present; + uint8_t status; +} tDot11fTLVP2PStatus; + +#define DOT11F_TLV_P2PSTATUS (0) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_P2PSTATUS_MIN_LEN (2) + +#define DOT11F_TLV_P2PSTATUS_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_P2PStatus( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVP2PStatus*); + +uint32_t dot11f_pack_tlv_p2_p_status( + tpAniSirGlobal, + tDot11fTLVP2PStatus *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_P2PStatus( + tpAniSirGlobal, + tDot11fTLVP2PStatus *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4180 (0x1054) */ +typedef struct sDot11fTLVPrimaryDeviceType { + uint8_t present; + uint16_t primary_category; + uint8_t oui[4]; + uint16_t sub_category; +} tDot11fTLVPrimaryDeviceType; + +#define DOT11F_TLV_PRIMARYDEVICETYPE (4180) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_PRIMARYDEVICETYPE_MIN_LEN (10) + +#define DOT11F_TLV_PRIMARYDEVICETYPE_MAX_LEN (10) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_primary_device_type( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVPrimaryDeviceType*); + +uint32_t dot11f_pack_tlv_primary_device_type( + tpAniSirGlobal, + tDot11fTLVPrimaryDeviceType *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_PrimaryDeviceType( + tpAniSirGlobal, + tDot11fTLVPrimaryDeviceType *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4156 (0x103c) */ +typedef struct sDot11fTLVRFBands { + uint8_t present; + uint8_t bands; +} tDot11fTLVRFBands; + +#define DOT11F_TLV_RFBANDS (4156) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_RFBANDS_MIN_LEN (3) + +#define DOT11F_TLV_RFBANDS_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_RFBands( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVRFBands*); + +uint32_t dot11f_pack_tlv_rf_bands( + tpAniSirGlobal, + tDot11fTLVRFBands *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_RFBands( + tpAniSirGlobal, + tDot11fTLVRFBands *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4202 (0x106a) */ +typedef struct sDot11fTLVRequestDeviceType { + uint8_t present; + uint16_t primary_category; + uint8_t oui[4]; + uint16_t sub_category; +} tDot11fTLVRequestDeviceType; + +#define DOT11F_TLV_REQUESTDEVICETYPE (4202) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_REQUESTDEVICETYPE_MIN_LEN (10) + +#define DOT11F_TLV_REQUESTDEVICETYPE_MAX_LEN (10) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_request_device_type( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVRequestDeviceType*); + +uint32_t dot11f_pack_tlv_request_device_type( + tpAniSirGlobal, + tDot11fTLVRequestDeviceType *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_RequestDeviceType( + tpAniSirGlobal, + tDot11fTLVRequestDeviceType *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4154 (0x103a) */ +typedef struct sDot11fTLVRequestType { + uint8_t present; + uint8_t reqType; +} tDot11fTLVRequestType; + +#define DOT11F_TLV_REQUESTTYPE (4154) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_REQUESTTYPE_MIN_LEN (3) + +#define DOT11F_TLV_REQUESTTYPE_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_RequestType( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVRequestType*); + +uint32_t dot11f_pack_tlv_request_type( + tpAniSirGlobal, + tDot11fTLVRequestType *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_RequestType( + tpAniSirGlobal, + tDot11fTLVRequestType *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4155 (0x103b) */ +typedef struct sDot11fTLVResponseType { + uint8_t present; + uint8_t resType; +} tDot11fTLVResponseType; + +#define DOT11F_TLV_RESPONSETYPE (4155) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_RESPONSETYPE_MIN_LEN (3) + +#define DOT11F_TLV_RESPONSETYPE_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_ResponseType( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVResponseType*); + +uint32_t dot11f_pack_tlv_response_type( + tpAniSirGlobal, + tDot11fTLVResponseType *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_ResponseType( + tpAniSirGlobal, + tDot11fTLVResponseType *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4161 (0x1041) */ +typedef struct sDot11fTLVSelectedRegistrar { + uint8_t present; + uint8_t selected; +} tDot11fTLVSelectedRegistrar; + +#define DOT11F_TLV_SELECTEDREGISTRAR (4161) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_SELECTEDREGISTRAR_MIN_LEN (3) + +#define DOT11F_TLV_SELECTEDREGISTRAR_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_SelectedRegistrar( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVSelectedRegistrar*); + +uint32_t dot11f_pack_tlv_selected_registrar( + tpAniSirGlobal, + tDot11fTLVSelectedRegistrar *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_SelectedRegistrar( + tpAniSirGlobal, + tDot11fTLVSelectedRegistrar *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4179 (0x1053) */ +typedef struct sDot11fTLVSelectedRegistrarConfigMethods { + uint8_t present; + uint16_t methods; +} tDot11fTLVSelectedRegistrarConfigMethods; + +#define DOT11F_TLV_SELECTEDREGISTRARCONFIGMETHODS (4179) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_SELECTEDREGISTRARCONFIGMETHODS_MIN_LEN (4) + +#define DOT11F_TLV_SELECTEDREGISTRARCONFIGMETHODS_MAX_LEN (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_SelectedRegistrarConfigMethods( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVSelectedRegistrarConfigMethods*); + +uint32_t dot11f_pack_tlv_selected_registrar_config_methods( + tpAniSirGlobal, + tDot11fTLVSelectedRegistrarConfigMethods *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_SelectedRegistrarConfigMethods( + tpAniSirGlobal, + tDot11fTLVSelectedRegistrarConfigMethods *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4162 (0x1042) */ +typedef struct sDot11fTLVSerialNumber { + uint8_t present; + uint8_t num_text; + uint8_t text[32]; +} tDot11fTLVSerialNumber; + +#define DOT11F_TLV_SERIALNUMBER (4162) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_SERIALNUMBER_MIN_LEN (2) + +#define DOT11F_TLV_SERIALNUMBER_MAX_LEN (34) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_serial_number( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVSerialNumber*); + +uint32_t dot11f_pack_tlv_serial_number( + tpAniSirGlobal, + tDot11fTLVSerialNumber *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_SerialNumber( + tpAniSirGlobal, + tDot11fTLVSerialNumber *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4167 (0x1047) */ +typedef struct sDot11fTLVUUID_E { + uint8_t present; + uint8_t uuid[16]; +} tDot11fTLVUUID_E; + +#define DOT11F_TLV_UUID_E (4167) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_UUID_E_MIN_LEN (18) + +#define DOT11F_TLV_UUID_E_MAX_LEN (18) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_uuid_e( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVUUID_E*); + +uint32_t dot11f_pack_tlv_uuid_e( + tpAniSirGlobal, + tDot11fTLVUUID_E *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_UUID_E( + tpAniSirGlobal, + tDot11fTLVUUID_E *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4168 (0x1048) */ +typedef struct sDot11fTLVUUID_R { + uint8_t present; + uint8_t uuid[16]; +} tDot11fTLVUUID_R; + +#define DOT11F_TLV_UUID_R (4168) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_UUID_R_MIN_LEN (18) + +#define DOT11F_TLV_UUID_R_MAX_LEN (18) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_uuid_r( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVUUID_R*); + +uint32_t dot11f_pack_tlv_uuid_r( + tpAniSirGlobal, + tDot11fTLVUUID_R *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_UUID_R( + tpAniSirGlobal, + tDot11fTLVUUID_R *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4169 (0x1049) */ +typedef struct sDot11fTLVVendorExtension { + uint8_t present; + uint8_t vendorId[3]; + tDot11fTLVVersion2 Version2; + tDot11fTLVAuthorizedMACs AuthorizedMACs; + tDot11fTLVRequestToEnroll RequestToEnroll; +} tDot11fTLVVendorExtension; + +#define DOT11F_TLV_VENDOREXTENSION (4169) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_VENDOREXTENSION_MIN_LEN (5) + +#define DOT11F_TLV_VENDOREXTENSION_MAX_LEN (19) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_vendor_extension( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVVendorExtension*); + +uint32_t dot11f_pack_tlv_vendor_extension( + tpAniSirGlobal, + tDot11fTLVVendorExtension *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_VendorExtension( + tpAniSirGlobal, + tDot11fTLVVendorExtension *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4170 (0x104a) */ +typedef struct sDot11fTLVVersion { + uint8_t present; + uint8_t minor:4; + uint8_t major:4; +} tDot11fTLVVersion; + +#define DOT11F_TLV_VERSION (4170) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_VERSION_MIN_LEN (3) + +#define DOT11F_TLV_VERSION_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_version( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVVersion*); + +uint32_t dot11f_pack_tlv_version( + tpAniSirGlobal, + tDot11fTLVVersion *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_Version( + tpAniSirGlobal, + tDot11fTLVVersion *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4164 (0x1044) */ +typedef struct sDot11fTLVWPSState { + uint8_t present; + uint8_t state; +} tDot11fTLVWPSState; + +#define DOT11F_TLV_WPSSTATE (4164) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_WPSSTATE_MIN_LEN (3) + +#define DOT11F_TLV_WPSSTATE_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_WPSState( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVWPSState*); + +uint32_t dot11f_pack_tlv_wps_state( + tpAniSirGlobal, + tDot11fTLVWPSState *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_WPSState( + tpAniSirGlobal, + tDot11fTLVWPSState *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 4 (0x0004) */ +typedef struct sDot11fTLVassoc_disallowed { + uint8_t present; + uint8_t reason_code; +} tDot11fTLVassoc_disallowed; + +#define DOT11F_TLV_ASSOC_DISALLOWED (4) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_ASSOC_DISALLOWED_MIN_LEN (1) + +#define DOT11F_TLV_ASSOC_DISALLOWED_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_assoc_disallowed( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVassoc_disallowed*); + +uint32_t dot11f_pack_tlv_assoc_disallowed( + tpAniSirGlobal, + tDot11fTLVassoc_disallowed *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_assoc_disallowed( + tpAniSirGlobal, + tDot11fTLVassoc_disallowed *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 8 (0x0008) */ +typedef struct sDot11fTLVassoc_retry_delay { + uint8_t present; + uint16_t delay; +} tDot11fTLVassoc_retry_delay; + +#define DOT11F_TLV_ASSOC_RETRY_DELAY (8) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_ASSOC_RETRY_DELAY_MIN_LEN (2) + +#define DOT11F_TLV_ASSOC_RETRY_DELAY_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_assoc_retry_delay( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVassoc_retry_delay*); + +uint32_t dot11f_pack_tlv_assoc_retry_delay( + tpAniSirGlobal, + tDot11fTLVassoc_retry_delay *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_assoc_retry_delay( + tpAniSirGlobal, + tDot11fTLVassoc_retry_delay *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 3 (0x0003) */ +typedef struct sDot11fTLVcellular_data_cap { + uint8_t present; + uint8_t cellular_connectivity; +} tDot11fTLVcellular_data_cap; + +#define DOT11F_TLV_CELLULAR_DATA_CAP (3) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_CELLULAR_DATA_CAP_MIN_LEN (1) + +#define DOT11F_TLV_CELLULAR_DATA_CAP_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_cellular_data_cap( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVcellular_data_cap*); + +uint32_t dot11f_pack_tlv_cellular_data_cap( + tpAniSirGlobal, + tDot11fTLVcellular_data_cap *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_cellular_data_cap( + tpAniSirGlobal, + tDot11fTLVcellular_data_cap *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 5 (0x0005) */ +typedef struct sDot11fTLVcellular_data_con_pref { + uint8_t present; + uint8_t cellular_preference; +} tDot11fTLVcellular_data_con_pref; + +#define DOT11F_TLV_CELLULAR_DATA_CON_PREF (5) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_CELLULAR_DATA_CON_PREF_MIN_LEN (1) + +#define DOT11F_TLV_CELLULAR_DATA_CON_PREF_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_cellular_data_con_pref( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVcellular_data_con_pref*); + +uint32_t dot11f_pack_tlv_cellular_data_con_pref( + tpAniSirGlobal, + tDot11fTLVcellular_data_con_pref *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_cellular_data_con_pref( + tpAniSirGlobal, + tDot11fTLVcellular_data_con_pref *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 1 (0x0001) */ +typedef struct sDot11fTLVmbo_ap_cap { + uint8_t present; + uint8_t mbo_cap_ind; +} tDot11fTLVmbo_ap_cap; + +#define DOT11F_TLV_MBO_AP_CAP (1) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_MBO_AP_CAP_MIN_LEN (1) + +#define DOT11F_TLV_MBO_AP_CAP_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_mbo_ap_cap( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVmbo_ap_cap*); + +uint32_t dot11f_pack_tlv_mbo_ap_cap( + tpAniSirGlobal, + tDot11fTLVmbo_ap_cap *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_mbo_ap_cap( + tpAniSirGlobal, + tDot11fTLVmbo_ap_cap *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 2 (0x0002) */ +typedef struct sDot11fTLVnon_prefferd_chan_rep { + uint8_t present; + uint8_t oper_class; + uint8_t num_channel_report; + uint8_t channel_report[254]; +} tDot11fTLVnon_prefferd_chan_rep; + +#define DOT11F_TLV_NON_PREFFERD_CHAN_REP (2) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_NON_PREFFERD_CHAN_REP_MIN_LEN (4) + +#define DOT11F_TLV_NON_PREFFERD_CHAN_REP_MAX_LEN (255) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_non_prefferd_chan_rep( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVnon_prefferd_chan_rep*); + +uint32_t dot11f_pack_tlv_non_prefferd_chan_rep( + tpAniSirGlobal, + tDot11fTLVnon_prefferd_chan_rep *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_non_prefferd_chan_rep( + tpAniSirGlobal, + tDot11fTLVnon_prefferd_chan_rep *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 101 (0x0065) */ +typedef struct sDot11fTLVoce_cap { + uint8_t present; + uint8_t oce_release:3; + uint8_t is_sta_cfon:1; + uint8_t non_oce_ap_present:1; + uint8_t reserved:3; +} tDot11fTLVoce_cap; + +#define DOT11F_TLV_OCE_CAP (101) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_OCE_CAP_MIN_LEN (1) + +#define DOT11F_TLV_OCE_CAP_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_oce_cap( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVoce_cap*); + +uint32_t dot11f_pack_tlv_oce_cap( + tpAniSirGlobal, + tDot11fTLVoce_cap *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_oce_cap( + tpAniSirGlobal, + tDot11fTLVoce_cap *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 103 (0x0067) */ +typedef struct sDot11fTLVreduced_wan_metrics { + uint8_t present; + uint8_t downlink_av_cap:4; + uint8_t uplink_av_cap:4; +} tDot11fTLVreduced_wan_metrics; + +#define DOT11F_TLV_REDUCED_WAN_METRICS (103) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_REDUCED_WAN_METRICS_MIN_LEN (1) + +#define DOT11F_TLV_REDUCED_WAN_METRICS_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_reduced_wan_metrics( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVreduced_wan_metrics*); + +uint32_t dot11f_pack_tlv_reduced_wan_metrics( + tpAniSirGlobal, + tDot11fTLVreduced_wan_metrics *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_reduced_wan_metrics( + tpAniSirGlobal, + tDot11fTLVreduced_wan_metrics *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 102 (0x0066) */ +typedef struct sDot11fTLVrssi_assoc_rej { + uint8_t present; + uint8_t delta_rssi; + uint8_t retry_delay; +} tDot11fTLVrssi_assoc_rej; + +#define DOT11F_TLV_RSSI_ASSOC_REJ (102) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_RSSI_ASSOC_REJ_MIN_LEN (2) + +#define DOT11F_TLV_RSSI_ASSOC_REJ_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_rssi_assoc_rej( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVrssi_assoc_rej*); + +uint32_t dot11f_pack_tlv_rssi_assoc_rej( + tpAniSirGlobal, + tDot11fTLVrssi_assoc_rej *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_rssi_assoc_rej( + tpAniSirGlobal, + tDot11fTLVrssi_assoc_rej *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 6 (0x0006) */ +typedef struct sDot11fTLVtransition_reason { + uint8_t present; + uint8_t transition_reason_code; +} tDot11fTLVtransition_reason; + +#define DOT11F_TLV_TRANSITION_REASON (6) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_TRANSITION_REASON_MIN_LEN (1) + +#define DOT11F_TLV_TRANSITION_REASON_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_transition_reason( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVtransition_reason*); + +uint32_t dot11f_pack_tlv_transition_reason( + tpAniSirGlobal, + tDot11fTLVtransition_reason *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_transition_reason( + tpAniSirGlobal, + tDot11fTLVtransition_reason *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 7 (0x0007) */ +typedef struct sDot11fTLVtransition_reject_reason { + uint8_t present; + uint8_t transition_reject_code; +} tDot11fTLVtransition_reject_reason; + +#define DOT11F_TLV_TRANSITION_REJECT_REASON (7) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_TRANSITION_REJECT_REASON_MIN_LEN (1) + +#define DOT11F_TLV_TRANSITION_REJECT_REASON_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_transition_reject_reason( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVtransition_reject_reason*); + +uint32_t dot11f_pack_tlv_transition_reject_reason( + tpAniSirGlobal, + tDot11fTLVtransition_reject_reason *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_transition_reject_reason( + tpAniSirGlobal, + tDot11fTLVtransition_reject_reason *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 16 (0x0010) */ +typedef struct sDot11fTLVP2PInterface { + uint8_t present; + uint8_t P2PDeviceAddress[6]; +} tDot11fTLVP2PInterface; + +#define DOT11F_TLV_P2PINTERFACE (16) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_P2PINTERFACE_MIN_LEN (7) + +#define DOT11F_TLV_P2PINTERFACE_MAX_LEN (7) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_p2_p_interface( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVP2PInterface*); + +uint32_t dot11f_pack_tlv_p2_p_interface( + tpAniSirGlobal, + tDot11fTLVP2PInterface *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_P2PInterface( + tpAniSirGlobal, + tDot11fTLVP2PInterface *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* ID 10 (0x000a) */ +typedef struct sDot11fTLVP2PManageability { + uint8_t present; + uint8_t manageability; +} tDot11fTLVP2PManageability; + +#define DOT11F_TLV_P2PMANAGEABILITY (10) + +/* N.B. These #defines do *not* include the ID & length */ +#define DOT11F_TLV_P2PMANAGEABILITY_MIN_LEN (2) + +#define DOT11F_TLV_P2PMANAGEABILITY_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +uint32_t dot11f_unpack_tlv_P2PManageability( + tpAniSirGlobal, + uint8_t *, + uint16_t, + tDot11fTLVP2PManageability*); + +uint32_t dot11f_pack_tlv_p2_p_manageability( + tpAniSirGlobal, + tDot11fTLVP2PManageability *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_tlv_P2PManageability( + tpAniSirGlobal, + tDot11fTLVP2PManageability *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ +/********************************************************************* + * Information Elements * + ********************************************************************/ + + +/* EID 2 (0x02) */ +typedef struct sDot11fIEGTK { + uint8_t present; + uint16_t keyId:2; + uint16_t reserved:14; + uint8_t keyLength; + uint8_t RSC[8]; + uint8_t num_key; + uint8_t key[32]; +} tDot11fIEGTK; + +#define DOT11F_EID_GTK (2) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_GTK_MIN_LEN (16) + +#define DOT11F_IE_GTK_MAX_LEN (43) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_gtk( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEGTK*, + bool); + +uint32_t dot11f_pack_ie_gtk( + tpAniSirGlobal, + tDot11fIEGTK *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_GTK( + tpAniSirGlobal, + tDot11fIEGTK *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 4 (0x04) */ +typedef struct sDot11fIEIGTK { + uint8_t present; + uint8_t keyID[2]; + uint8_t IPN[6]; + uint8_t keyLength; + uint8_t key[24]; +} tDot11fIEIGTK; + +#define DOT11F_EID_IGTK (4) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_IGTK_MIN_LEN (33) + +#define DOT11F_IE_IGTK_MAX_LEN (33) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_igtk( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEIGTK*, + bool); + +uint32_t dot11f_pack_ie_igtk( + tpAniSirGlobal, + tDot11fIEIGTK *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_IGTK( + tpAniSirGlobal, + tDot11fIEIGTK *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 3 (0x03) */ +typedef struct sDot11fIER0KH_ID { + uint8_t present; + uint8_t num_PMK_R0_ID; + uint8_t PMK_R0_ID[48]; +} tDot11fIER0KH_ID; + +#define DOT11F_EID_R0KH_ID (3) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_R0KH_ID_MIN_LEN (1) + +#define DOT11F_IE_R0KH_ID_MAX_LEN (48) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_r0_kh_id( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIER0KH_ID*, + bool); + +uint32_t dot11f_pack_ie_r0_kh_id( + tpAniSirGlobal, + tDot11fIER0KH_ID *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_R0KH_ID( + tpAniSirGlobal, + tDot11fIER0KH_ID *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 1 (0x01) */ +typedef struct sDot11fIER1KH_ID { + uint8_t present; + uint8_t PMK_R1_ID[6]; +} tDot11fIER1KH_ID; + +#define DOT11F_EID_R1KH_ID (1) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_R1KH_ID_MIN_LEN (6) + +#define DOT11F_IE_R1KH_ID_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_r1_kh_id( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIER1KH_ID*, + bool); + +uint32_t dot11f_pack_ie_r1_kh_id( + tpAniSirGlobal, + tDot11fIER1KH_ID *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_R1KH_ID( + tpAniSirGlobal, + tDot11fIER1KH_ID *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 1 (0x01) */ +typedef struct sDot11fIEversion_attr { + uint8_t present; + uint8_t version; + uint8_t sub_version; +} tDot11fIEversion_attr; + +#define DOT11F_EID_VERSION_ATTR (1) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_VERSION_ATTR_MIN_LEN (2) + +#define DOT11F_IE_VERSION_ATTR_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_version_attr( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEversion_attr*, + bool); + +uint32_t dot11f_pack_ie_version_attr( + tpAniSirGlobal, + tDot11fIEversion_attr *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_version_attr( + tpAniSirGlobal, + tDot11fIEversion_attr *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 2 (0x02) */ +typedef struct sDot11fIEvht_mcs11_attr { + uint8_t present; + uint8_t vht_mcs_10_11_supp; +} tDot11fIEvht_mcs11_attr; + +#define DOT11F_EID_VHT_MCS11_ATTR (2) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_VHT_MCS11_ATTR_MIN_LEN (1) + +#define DOT11F_IE_VHT_MCS11_ATTR_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_vht_mcs11_attr( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEvht_mcs11_attr*, + bool); + +uint32_t dot11f_pack_ie_vht_mcs11_attr( + tpAniSirGlobal, + tDot11fIEvht_mcs11_attr *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_vht_mcs11_attr( + tpAniSirGlobal, + tDot11fIEvht_mcs11_attr *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 51 (0x33) */ +typedef struct sDot11fIEAPChannelReport { + uint8_t present; + uint8_t regulatoryClass; + uint8_t num_channelList; + uint8_t channelList[50]; +} tDot11fIEAPChannelReport; + +#define DOT11F_EID_APCHANNELREPORT (51) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_APCHANNELREPORT_MIN_LEN (1) + +#define DOT11F_IE_APCHANNELREPORT_MAX_LEN (51) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ap_channel_report( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEAPChannelReport*, + bool); + +uint32_t dot11f_pack_ie_ap_channel_report( + tpAniSirGlobal, + tDot11fIEAPChannelReport *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_APChannelReport( + tpAniSirGlobal, + tDot11fIEAPChannelReport *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 2 (0x02) */ +typedef struct sDot11fIEBcnReportingDetail { + uint8_t present; + uint8_t reportingDetail; +} tDot11fIEBcnReportingDetail; + +#define DOT11F_EID_BCNREPORTINGDETAIL (2) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_BCNREPORTINGDETAIL_MIN_LEN (1) + +#define DOT11F_IE_BCNREPORTINGDETAIL_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_bcn_reporting_detail( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEBcnReportingDetail*, + bool); + +uint32_t dot11f_pack_ie_bcn_reporting_detail( + tpAniSirGlobal, + tDot11fIEBcnReportingDetail *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_BcnReportingDetail( + tpAniSirGlobal, + tDot11fIEBcnReportingDetail *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 1 (0x01) */ +typedef struct sDot11fIEBeaconReportFrmBody { + uint8_t present; + uint8_t num_reportedFields; + uint8_t reportedFields[224]; +} tDot11fIEBeaconReportFrmBody; + +#define DOT11F_EID_BEACONREPORTFRMBODY (1) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_BEACONREPORTFRMBODY_MIN_LEN (0) + +#define DOT11F_IE_BEACONREPORTFRMBODY_MAX_LEN (224) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_beacon_report_frm_body( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEBeaconReportFrmBody*, + bool); + +uint32_t dot11f_pack_ie_beacon_report_frm_body( + tpAniSirGlobal, + tDot11fIEBeaconReportFrmBody *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_BeaconReportFrmBody( + tpAniSirGlobal, + tDot11fIEBeaconReportFrmBody *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 1 (0x01) */ +typedef struct sDot11fIEBeaconReporting { + uint8_t present; + uint8_t reportingCondition; + uint8_t threshold; +} tDot11fIEBeaconReporting; + +#define DOT11F_EID_BEACONREPORTING (1) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_BEACONREPORTING_MIN_LEN (2) + +#define DOT11F_IE_BEACONREPORTING_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_beacon_reporting( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEBeaconReporting*, + bool); + +uint32_t dot11f_pack_ie_beacon_reporting( + tpAniSirGlobal, + tDot11fIEBeaconReporting *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_BeaconReporting( + tpAniSirGlobal, + tDot11fIEBeaconReporting *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 2 (0x02) */ +typedef struct sDot11fIECondensedCountryStr { + uint8_t present; + uint8_t countryStr[2]; +} tDot11fIECondensedCountryStr; + +#define DOT11F_EID_CONDENSEDCOUNTRYSTR (2) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_CONDENSEDCOUNTRYSTR_MIN_LEN (2) + +#define DOT11F_IE_CONDENSEDCOUNTRYSTR_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_condensed_country_str( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIECondensedCountryStr*, + bool); + +uint32_t dot11f_pack_ie_condensed_country_str( + tpAniSirGlobal, + tDot11fIECondensedCountryStr *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_CondensedCountryStr( + tpAniSirGlobal, + tDot11fIECondensedCountryStr *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 66 (0x42) */ +typedef struct sDot11fIEMeasurementPilot { + uint8_t present; + uint8_t measurementPilot; + uint8_t num_vendorSpecific; + uint8_t vendorSpecific[255]; +} tDot11fIEMeasurementPilot; + +#define DOT11F_EID_MEASUREMENTPILOT (66) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_MEASUREMENTPILOT_MIN_LEN (1) + +#define DOT11F_IE_MEASUREMENTPILOT_MAX_LEN (256) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_measurement_pilot( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEMeasurementPilot*, + bool); + +uint32_t dot11f_pack_ie_measurement_pilot( + tpAniSirGlobal, + tDot11fIEMeasurementPilot *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_MeasurementPilot( + tpAniSirGlobal, + tDot11fIEMeasurementPilot *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 71 (0x47) */ +typedef struct sDot11fIEMultiBssid { + uint8_t present; + uint8_t maxBSSIDIndicator; + uint8_t num_vendorSpecific; + uint8_t vendorSpecific[255]; +} tDot11fIEMultiBssid; + +#define DOT11F_EID_MULTIBSSID (71) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_MULTIBSSID_MIN_LEN (1) + +#define DOT11F_IE_MULTIBSSID_MAX_LEN (256) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_multi_bssid( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEMultiBssid*, + bool); + +uint32_t dot11f_pack_ie_multi_bssid( + tpAniSirGlobal, + tDot11fIEMultiBssid *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_MultiBssid( + tpAniSirGlobal, + tDot11fIEMultiBssid *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 57 (0x39) */ +typedef struct sDot11fIERICData { + uint8_t present; + uint8_t Identifier; + uint8_t resourceDescCount; + uint16_t statusCode; +} tDot11fIERICData; + +#define DOT11F_EID_RICDATA (57) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_RICDATA_MIN_LEN (4) + +#define DOT11F_IE_RICDATA_MAX_LEN (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ric_data( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIERICData*, + bool); + +uint32_t dot11f_pack_ie_ric_data( + tpAniSirGlobal, + tDot11fIERICData *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_RICData( + tpAniSirGlobal, + tDot11fIERICData *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 75 (0x4b) */ +typedef struct sDot11fIERICDescriptor { + uint8_t present; + uint8_t resourceType; + uint8_t num_variableData; + uint8_t variableData[255]; +} tDot11fIERICDescriptor; + +#define DOT11F_EID_RICDESCRIPTOR (75) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_RICDESCRIPTOR_MIN_LEN (1) + +#define DOT11F_IE_RICDESCRIPTOR_MAX_LEN (256) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ric_descriptor( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIERICDescriptor*, + bool); + +uint32_t dot11f_pack_ie_ric_descriptor( + tpAniSirGlobal, + tDot11fIERICDescriptor *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_RICDescriptor( + tpAniSirGlobal, + tDot11fIERICDescriptor *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 70 (0x46) */ +typedef struct sDot11fIERRMEnabledCap { + uint8_t present; + uint8_t LinkMeasurement:1; + uint8_t NeighborRpt:1; + uint8_t parallel:1; + uint8_t repeated:1; + uint8_t BeaconPassive:1; + uint8_t BeaconActive:1; + uint8_t BeaconTable:1; + uint8_t BeaconRepCond:1; + uint8_t FrameMeasurement:1; + uint8_t ChannelLoad:1; + uint8_t NoiseHistogram:1; + uint8_t statistics:1; + uint8_t LCIMeasurement:1; + uint8_t LCIAzimuth:1; + uint8_t TCMCapability:1; + uint8_t triggeredTCM:1; + uint8_t APChanReport:1; + uint8_t RRMMIBEnabled:1; + uint8_t operatingChanMax:3; + uint8_t nonOperatinChanMax:3; + uint8_t MeasurementPilot:3; + uint8_t MeasurementPilotEnabled:1; + uint8_t NeighborTSFOffset:1; + uint8_t RCPIMeasurement:1; + uint8_t RSNIMeasurement:1; + uint8_t BssAvgAccessDelay:1; + uint8_t BSSAvailAdmission:1; + uint8_t AntennaInformation:1; + uint8_t fine_time_meas_rpt:1; + uint8_t lci_capability:1; + uint8_t reserved:4; +} tDot11fIERRMEnabledCap; + +#define DOT11F_EID_RRMENABLEDCAP (70) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_RRMENABLEDCAP_MIN_LEN (5) + +#define DOT11F_IE_RRMENABLEDCAP_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_rrm_enabled_cap( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIERRMEnabledCap*, + bool); + +uint32_t dot11f_pack_ie_rrm_enabled_cap( + tpAniSirGlobal, + tDot11fIERRMEnabledCap *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_RRMEnabledCap( + tpAniSirGlobal, + tDot11fIERRMEnabledCap *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 10 (0x0a) */ +typedef struct sDot11fIERequestedInfo { + uint8_t present; + uint8_t num_requested_eids; + uint8_t requested_eids[255]; +} tDot11fIERequestedInfo; + +#define DOT11F_EID_REQUESTEDINFO (10) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_REQUESTEDINFO_MIN_LEN (0) + +#define DOT11F_IE_REQUESTEDINFO_MAX_LEN (255) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_requested_info( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIERequestedInfo*, + bool); + +uint32_t dot11f_pack_ie_requested_info( + tpAniSirGlobal, + tDot11fIERequestedInfo *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_RequestedInfo( + tpAniSirGlobal, + tDot11fIERequestedInfo *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 0 (0x00) */ +typedef struct sDot11fIESSID { + uint8_t present; + uint8_t num_ssid; + uint8_t ssid[32]; +} tDot11fIESSID; + +#define DOT11F_EID_SSID (0) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_SSID_MIN_LEN (0) + +#define DOT11F_IE_SSID_MAX_LEN (32) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ssid( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIESSID*, + bool); + +uint32_t dot11f_pack_ie_ssid( + tpAniSirGlobal, + tDot11fIESSID *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_SSID( + tpAniSirGlobal, + tDot11fIESSID *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 15 (0x0f) */ +typedef struct sDot11fIESchedule { + uint8_t present; + uint16_t aggregation:1; + uint16_t tsid:4; + uint16_t direction:2; + uint16_t reserved:9; + uint32_t service_start_time; + uint32_t service_interval; + uint16_t max_service_dur; + uint16_t spec_interval; +} tDot11fIESchedule; + +#define DOT11F_EID_SCHEDULE (15) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_SCHEDULE_MIN_LEN (14) + +#define DOT11F_IE_SCHEDULE_MAX_LEN (14) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_schedule( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIESchedule*, + bool); + +uint32_t dot11f_pack_ie_schedule( + tpAniSirGlobal, + tDot11fIESchedule *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_Schedule( + tpAniSirGlobal, + tDot11fIESchedule *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 14 (0x0e) */ +typedef struct sDot11fIETCLAS { + uint8_t present; + uint8_t user_priority; + uint8_t classifier_type; + uint8_t classifier_mask; + union { + struct { + uint8_t source[6]; + uint8_t dest[6]; + uint16_t type; + } EthParams; /* classifier_type = 0 */ + struct { + uint8_t version; + union { + struct { + uint8_t source[4]; + uint8_t dest[4]; + uint16_t src_port; + uint16_t dest_port; + uint8_t DSCP; + uint8_t proto; + uint8_t reserved; + } IpV4Params; /* version = 4 */ + struct { + uint8_t source[16]; + uint8_t dest[16]; + uint16_t src_port; + uint16_t dest_port; + uint8_t flow_label[3]; + } IpV6Params; /* version = 6 */ + } params; + } IpParams; /* classifier_type = 1 */ + struct { + uint16_t tag_type; + } Params8021dq; /* classifier_type = 2 */ + } info; +} tDot11fIETCLAS; + +#define DOT11F_EID_TCLAS (14) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TCLAS_MIN_LEN (5) + +#define DOT11F_IE_TCLAS_MAX_LEN (43) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_tclas( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETCLAS*, + bool); + +uint32_t dot11f_pack_ie_tclas( + tpAniSirGlobal, + tDot11fIETCLAS *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ietclas( + tpAniSirGlobal, + tDot11fIETCLAS *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 44 (0x2c) */ +typedef struct sDot11fIETCLASSPROC { + uint8_t present; + uint8_t processing; +} tDot11fIETCLASSPROC; + +#define DOT11F_EID_TCLASSPROC (44) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TCLASSPROC_MIN_LEN (1) + +#define DOT11F_IE_TCLASSPROC_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_tclasSPROC( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETCLASSPROC*, + bool); + +uint32_t dot11f_pack_ie_tclassproc( + tpAniSirGlobal, + tDot11fIETCLASSPROC *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ietclasSPROC( + tpAniSirGlobal, + tDot11fIETCLASSPROC *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 43 (0x2b) */ +typedef struct sDot11fIETSDelay { + uint8_t present; + uint32_t delay; +} tDot11fIETSDelay; + +#define DOT11F_EID_TSDELAY (43) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TSDELAY_MIN_LEN (4) + +#define DOT11F_IE_TSDELAY_MAX_LEN (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ts_delay( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETSDelay*, + bool); + +uint32_t dot11f_pack_ie_ts_delay( + tpAniSirGlobal, + tDot11fIETSDelay *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_TSDelay( + tpAniSirGlobal, + tDot11fIETSDelay *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 1 (0x01) */ +typedef struct sDot11fIETSFInfo { + uint8_t present; + uint16_t TsfOffset; + uint16_t BeaconIntvl; +} tDot11fIETSFInfo; + +#define DOT11F_EID_TSFINFO (1) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TSFINFO_MIN_LEN (4) + +#define DOT11F_IE_TSFINFO_MAX_LEN (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_tsf_info( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETSFInfo*, + bool); + +uint32_t dot11f_pack_ie_tsf_info( + tpAniSirGlobal, + tDot11fIETSFInfo *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_TSFInfo( + tpAniSirGlobal, + tDot11fIETSFInfo *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 13 (0x0d) */ +typedef struct sDot11fIETSPEC { + uint8_t present; + uint16_t traffic_type:1; + uint16_t tsid:4; + uint16_t direction:2; + uint16_t access_policy:2; + uint16_t aggregation:1; + uint16_t psb:1; + uint16_t user_priority:3; + uint16_t tsinfo_ack_pol:2; + uint8_t schedule:1; + uint8_t unused:7; + uint16_t size:15; + uint16_t fixed:1; + uint16_t max_msdu_size; + uint32_t min_service_int; + uint32_t max_service_int; + uint32_t inactivity_int; + uint32_t suspension_int; + uint32_t service_start_time; + uint32_t min_data_rate; + uint32_t mean_data_rate; + uint32_t peak_data_rate; + uint32_t burst_size; + uint32_t delay_bound; + uint32_t min_phy_rate; + uint16_t surplus_bw_allowance; + uint16_t medium_time; +} tDot11fIETSPEC; + +#define DOT11F_EID_TSPEC (13) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TSPEC_MIN_LEN (55) + +#define DOT11F_IE_TSPEC_MAX_LEN (55) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_tspec( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETSPEC*, + bool); + +uint32_t dot11f_pack_ie_tspec( + tpAniSirGlobal, + tDot11fIETSPEC *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_TSPEC( + tpAniSirGlobal, + tDot11fIETSPEC *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 191 (0xbf) */ +typedef struct sDot11fIEVHTCaps { + uint8_t present; + uint32_t maxMPDULen:2; + uint32_t supportedChannelWidthSet:2; + uint32_t ldpcCodingCap:1; + uint32_t shortGI80MHz:1; + uint32_t shortGI160and80plus80MHz:1; + uint32_t txSTBC:1; + uint32_t rxSTBC:3; + uint32_t suBeamFormerCap:1; + uint32_t suBeamformeeCap:1; + uint32_t csnofBeamformerAntSup:3; + uint32_t numSoundingDim:3; + uint32_t muBeamformerCap:1; + uint32_t muBeamformeeCap:1; + uint32_t vhtTXOPPS:1; + uint32_t htcVHTCap:1; + uint32_t maxAMPDULenExp:3; + uint32_t vhtLinkAdaptCap:2; + uint32_t rxAntPattern:1; + uint32_t txAntPattern:1; + uint32_t extended_nss_bw_supp:2; + uint16_t rxMCSMap; + uint16_t rxHighSupDataRate:13; + uint16_t max_nsts_total:3; + uint16_t txMCSMap; + uint16_t txSupDataRate:13; + uint16_t vht_extended_nss_bw_cap:1; + uint16_t reserved:2; +} tDot11fIEVHTCaps; + +#define DOT11F_EID_VHTCAPS (191) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_VHTCAPS_MIN_LEN (12) + +#define DOT11F_IE_VHTCAPS_MAX_LEN (12) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_vht_caps( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEVHTCaps*, + bool); + +uint32_t dot11f_pack_ie_vht_caps( + tpAniSirGlobal, + tDot11fIEVHTCaps *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_VHTCaps( + tpAniSirGlobal, + tDot11fIEVHTCaps *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 192 (0xc0) */ +typedef struct sDot11fIEVHTOperation { + uint8_t present; + uint8_t chanWidth; + uint8_t chan_center_freq_seg0; + uint8_t chan_center_freq_seg1; + uint16_t basicMCSSet; +} tDot11fIEVHTOperation; + +#define DOT11F_EID_VHTOPERATION (192) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_VHTOPERATION_MIN_LEN (5) + +#define DOT11F_IE_VHTOPERATION_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_vht_operation( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEVHTOperation*, + bool); + +uint32_t dot11f_pack_ie_vht_operation( + tpAniSirGlobal, + tDot11fIEVHTOperation *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_VHTOperation( + tpAniSirGlobal, + tDot11fIEVHTOperation *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x09} */ +typedef struct sDot11fIEWMMSchedule { + uint8_t present; + uint8_t version /* Must be 1! */; + uint16_t aggregation:1; + uint16_t tsid:4; + uint16_t direction:2; + uint16_t reserved:9; + uint32_t service_start_time; + uint32_t service_interval; + uint16_t max_service_dur; + uint16_t spec_interval; +} tDot11fIEWMMSchedule; + +#define DOT11F_EID_WMMSCHEDULE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WMMSCHEDULE_MIN_LEN (20) + +#define DOT11F_IE_WMMSCHEDULE_MAX_LEN (20) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wmm_schedule( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWMMSchedule*, + bool); + +uint32_t dot11f_pack_ie_wmm_schedule( + tpAniSirGlobal, + tDot11fIEWMMSchedule *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WMMSchedule( + tpAniSirGlobal, + tDot11fIEWMMSchedule *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x06} */ +typedef struct sDot11fIEWMMTCLAS { + uint8_t present; + uint8_t version /* Must be 1! */; + uint8_t user_priority; + uint8_t classifier_type; + uint8_t classifier_mask; + union { + struct { + uint8_t source[6]; + uint8_t dest[6]; + uint16_t type; + } EthParams; /* classifier_type = 0 */ + struct { + uint8_t version; + union { + struct { + uint8_t source[4]; + uint8_t dest[4]; + uint16_t src_port; + uint16_t dest_port; + uint8_t DSCP; + uint8_t proto; + uint8_t reserved; + } IpV4Params; /* version = 4 */ + struct { + uint8_t source[16]; + uint8_t dest[16]; + uint16_t src_port; + uint16_t dest_port; + uint8_t flow_label[3]; + } IpV6Params; /* version = 6 */ + } params; + } IpParams; /* classifier_type = 1 */ + struct { + uint16_t tag_type; + } Params8021dq; /* classifier_type = 2 */ + } info; +} tDot11fIEWMMTCLAS; + +#define DOT11F_EID_WMMTCLAS (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WMMTCLAS_MIN_LEN (11) + +#define DOT11F_IE_WMMTCLAS_MAX_LEN (49) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wmmtclas( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWMMTCLAS*, + bool); + +uint32_t dot11f_pack_ie_wmmtclas( + tpAniSirGlobal, + tDot11fIEWMMTCLAS *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iewmmtclas( + tpAniSirGlobal, + tDot11fIEWMMTCLAS *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x07} */ +typedef struct sDot11fIEWMMTCLASPROC { + uint8_t present; + uint8_t version /* Must be 1! */; + uint8_t processing; +} tDot11fIEWMMTCLASPROC; + +#define DOT11F_EID_WMMTCLASPROC (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WMMTCLASPROC_MIN_LEN (7) + +#define DOT11F_IE_WMMTCLASPROC_MAX_LEN (7) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wmmtclasproc( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWMMTCLASPROC*, + bool); + +uint32_t dot11f_pack_ie_wmmtclasproc( + tpAniSirGlobal, + tDot11fIEWMMTCLASPROC *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iewmmtclasPROC( + tpAniSirGlobal, + tDot11fIEWMMTCLASPROC *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x08} */ +typedef struct sDot11fIEWMMTSDelay { + uint8_t present; + uint8_t version /* Must be 1! */; + uint32_t delay; +} tDot11fIEWMMTSDelay; + +#define DOT11F_EID_WMMTSDELAY (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WMMTSDELAY_MIN_LEN (10) + +#define DOT11F_IE_WMMTSDELAY_MAX_LEN (10) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wmmts_delay( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWMMTSDelay*, + bool); + +uint32_t dot11f_pack_ie_wmmts_delay( + tpAniSirGlobal, + tDot11fIEWMMTSDelay *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WMMTSDelay( + tpAniSirGlobal, + tDot11fIEWMMTSDelay *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x02} */ +typedef struct sDot11fIEWMMTSPEC { + uint8_t present; + uint8_t version /* Must be 1! */; + uint16_t traffic_type:1; + uint16_t tsid:4; + uint16_t direction:2; + uint16_t access_policy:2; + uint16_t aggregation:1; + uint16_t psb:1; + uint16_t user_priority:3; + uint16_t tsinfo_ack_pol:2; + uint8_t tsinfo_rsvd:7; + uint8_t burst_size_defn:1; + uint16_t size:15; + uint16_t fixed:1; + uint16_t max_msdu_size; + uint32_t min_service_int; + uint32_t max_service_int; + uint32_t inactivity_int; + uint32_t suspension_int; + uint32_t service_start_time; + uint32_t min_data_rate; + uint32_t mean_data_rate; + uint32_t peak_data_rate; + uint32_t burst_size; + uint32_t delay_bound; + uint32_t min_phy_rate; + uint16_t surplus_bw_allowance; + uint16_t medium_time; +} tDot11fIEWMMTSPEC; + +#define DOT11F_EID_WMMTSPEC (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WMMTSPEC_MIN_LEN (61) + +#define DOT11F_IE_WMMTSPEC_MAX_LEN (61) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wmmtspec( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWMMTSPEC*, + bool); + +uint32_t dot11f_pack_ie_wmmtspec( + tpAniSirGlobal, + tDot11fIEWMMTSPEC *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WMMTSPEC( + tpAniSirGlobal, + tDot11fIEWMMTSPEC *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 194 (0xc2) */ +typedef struct sDot11fIEWiderBWChanSwitchAnn { + uint8_t present; + uint8_t newChanWidth; + uint8_t newCenterChanFreq0; + uint8_t newCenterChanFreq1; +} tDot11fIEWiderBWChanSwitchAnn; + +#define DOT11F_EID_WIDERBWCHANSWITCHANN (194) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WIDERBWCHANSWITCHANN_MIN_LEN (3) + +#define DOT11F_IE_WIDERBWCHANSWITCHANN_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wider_bw_chan_switch_ann( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWiderBWChanSwitchAnn*, + bool); + +uint32_t dot11f_pack_ie_wider_bw_chan_switch_ann( + tpAniSirGlobal, + tDot11fIEWiderBWChanSwitchAnn *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WiderBWChanSwitchAnn( + tpAniSirGlobal, + tDot11fIEWiderBWChanSwitchAnn *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 1 (0x01) */ +typedef struct sDot11fIEazimuth_req { + uint8_t present; + uint8_t request; +} tDot11fIEazimuth_req; + +#define DOT11F_EID_AZIMUTH_REQ (1) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_AZIMUTH_REQ_MIN_LEN (1) + +#define DOT11F_IE_AZIMUTH_REQ_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_azimuth_req( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEazimuth_req*, + bool); + +uint32_t dot11f_pack_ie_azimuth_req( + tpAniSirGlobal, + tDot11fIEazimuth_req *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_azimuth_req( + tpAniSirGlobal, + tDot11fIEazimuth_req *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 2 (0x02) */ +typedef struct sDot11fIEbeacon_report_frm_body_fragment_id { + uint8_t present; + uint16_t beacon_report_id:8; + uint16_t fragment_id_number:7; + uint16_t more_fragments:1; +} tDot11fIEbeacon_report_frm_body_fragment_id; + +#define DOT11F_EID_BEACON_REPORT_FRM_BODY_FRAGMENT_ID (2) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_BEACON_REPORT_FRM_BODY_FRAGMENT_ID_MIN_LEN (2) + +#define DOT11F_IE_BEACON_REPORT_FRM_BODY_FRAGMENT_ID_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_beacon_report_frm_body_fragment_id( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEbeacon_report_frm_body_fragment_id*, + bool); + +uint32_t dot11f_pack_ie_beacon_report_frm_body_fragment_id( + tpAniSirGlobal, + tDot11fIEbeacon_report_frm_body_fragment_id *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_beacon_report_frm_body_fragment_id( + tpAniSirGlobal, + tDot11fIEbeacon_report_frm_body_fragment_id *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 164 (0xa4) */ +typedef struct sDot11fIElast_beacon_report_indication { + uint8_t present; + uint8_t last_fragment; +} tDot11fIElast_beacon_report_indication; + +#define DOT11F_EID_LAST_BEACON_REPORT_INDICATION (164) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_LAST_BEACON_REPORT_INDICATION_MIN_LEN (1) + +#define DOT11F_IE_LAST_BEACON_REPORT_INDICATION_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_last_beacon_report_indication( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIElast_beacon_report_indication*, + bool); + +uint32_t dot11f_pack_ie_last_beacon_report_indication( + tpAniSirGlobal, + tDot11fIElast_beacon_report_indication *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_last_beacon_report_indication( + tpAniSirGlobal, + tDot11fIElast_beacon_report_indication *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 4 (0x04) */ +typedef struct sDot11fIEmax_age { + uint8_t present; + uint16_t max_age; +} tDot11fIEmax_age; + +#define DOT11F_EID_MAX_AGE (4) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_MAX_AGE_MIN_LEN (2) + +#define DOT11F_IE_MAX_AGE_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_max_age( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEmax_age*, + bool); + +uint32_t dot11f_pack_ie_max_age( + tpAniSirGlobal, + tDot11fIEmax_age *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_max_age( + tpAniSirGlobal, + tDot11fIEmax_age *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 52 (0x34) */ +typedef struct sDot11fIEneighbor_rpt { + uint8_t present; + uint8_t bssid[6]; + uint8_t APReachability:2; + uint8_t Security:1; + uint8_t KeyScope:1; + uint8_t SpecMgmtCap:1; + uint8_t QosCap:1; + uint8_t apsd:1; + uint8_t rrm:1; + uint8_t DelayedBA:1; + uint8_t ImmBA:1; + uint8_t MobilityDomain:1; + uint8_t reserved:5; + uint16_t reserved1; + uint8_t regulatoryClass; + uint8_t channel; + uint8_t PhyType; + tDot11fIETSFInfo TSFInfo; + tDot11fIECondensedCountryStr CondensedCountryStr; + tDot11fIEMeasurementPilot MeasurementPilot; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIEMultiBssid MultiBssid; +} tDot11fIEneighbor_rpt; + +#define DOT11F_EID_NEIGHBOR_RPT (52) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_NEIGHBOR_RPT_MIN_LEN (13) + +#define DOT11F_IE_NEIGHBOR_RPT_MAX_LEN (546) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_neighbor_rpt( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEneighbor_rpt*, + bool); + +uint32_t dot11f_pack_ie_neighbor_rpt( + tpAniSirGlobal, + tDot11fIEneighbor_rpt *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_neighbor_rpt( + tpAniSirGlobal, + tDot11fIEneighbor_rpt *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 2 (0x02) */ +typedef struct sDot11fIEreq_mac_addr { + uint8_t present; + uint8_t addr[6]; +} tDot11fIEreq_mac_addr; + +#define DOT11F_EID_REQ_MAC_ADDR (2) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_REQ_MAC_ADDR_MIN_LEN (6) + +#define DOT11F_IE_REQ_MAC_ADDR_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_req_mac_addr( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEreq_mac_addr*, + bool); + +uint32_t dot11f_pack_ie_req_mac_addr( + tpAniSirGlobal, + tDot11fIEreq_mac_addr *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_req_mac_addr( + tpAniSirGlobal, + tDot11fIEreq_mac_addr *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 3 (0x03) */ +typedef struct sDot11fIEtgt_mac_addr { + uint8_t present; + uint8_t addr[6]; +} tDot11fIEtgt_mac_addr; + +#define DOT11F_EID_TGT_MAC_ADDR (3) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TGT_MAC_ADDR_MIN_LEN (6) + +#define DOT11F_IE_TGT_MAC_ADDR_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_tgt_mac_addr( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEtgt_mac_addr*, + bool); + +uint32_t dot11f_pack_ie_tgt_mac_addr( + tpAniSirGlobal, + tDot11fIEtgt_mac_addr *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_tgt_mac_addr( + tpAniSirGlobal, + tDot11fIEtgt_mac_addr *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 195 (0xc3) */ +typedef struct sDot11fIEvht_transmit_power_env { + uint8_t present; + uint8_t num_bytes; + uint8_t bytes[5]; +} tDot11fIEvht_transmit_power_env; + +#define DOT11F_EID_VHT_TRANSMIT_POWER_ENV (195) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_VHT_TRANSMIT_POWER_ENV_MIN_LEN (2) + +#define DOT11F_IE_VHT_TRANSMIT_POWER_ENV_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_vht_transmit_power_env( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEvht_transmit_power_env*, + bool); + +uint32_t dot11f_pack_ie_vht_transmit_power_env( + tpAniSirGlobal, + tDot11fIEvht_transmit_power_env *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_vht_transmit_power_env( + tpAniSirGlobal, + tDot11fIEvht_transmit_power_env *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 197 (0xc5) */ +typedef struct sDot11fIEAID { + uint8_t present; + uint16_t assocId; +} tDot11fIEAID; + +#define DOT11F_EID_AID (197) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_AID_MIN_LEN (2) + +#define DOT11F_IE_AID_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_aid( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEAID*, + bool); + +uint32_t dot11f_pack_ie_aid( + tpAniSirGlobal, + tDot11fIEAID *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_AID( + tpAniSirGlobal, + tDot11fIEAID *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x00, 0x0f, 0x22} */ +typedef struct sDot11fIEBeaconReportStatus { + uint8_t present; + uint8_t sub_type; + uint8_t version; + uint8_t length; + uint8_t reason_code; +} tDot11fIEBeaconReportStatus; + +#define DOT11F_EID_BEACONREPORTSTATUS (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_BEACONREPORTSTATUS_MIN_LEN (8) + +#define DOT11F_IE_BEACONREPORTSTATUS_MAX_LEN (8) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_BeaconReportStatus( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEBeaconReportStatus*, + bool); + +uint32_t dot11f_pack_ie_BeaconReportStatus( + tpAniSirGlobal, + tDot11fIEBeaconReportStatus *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_BeaconReportStatus( + tpAniSirGlobal, + tDot11fIEBeaconReportStatus *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 4 (0x04) */ +typedef struct sDot11fIECFParams { + uint8_t present; + uint8_t cfp_count; + uint8_t cfp_period; + uint16_t cfp_maxduration; + uint16_t cfp_durremaining; +} tDot11fIECFParams; + +#define DOT11F_EID_CFPARAMS (4) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_CFPARAMS_MIN_LEN (6) + +#define DOT11F_IE_CFPARAMS_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_cf_params( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIECFParams*, + bool); + +uint32_t dot11f_pack_ie_cf_params( + tpAniSirGlobal, + tDot11fIECFParams *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_CFParams( + tpAniSirGlobal, + tDot11fIECFParams *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 16 (0x10) */ +typedef struct sDot11fIEChallengeText { + uint8_t present; + uint8_t num_text; + uint8_t text[253]; +} tDot11fIEChallengeText; + +#define DOT11F_EID_CHALLENGETEXT (16) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_CHALLENGETEXT_MIN_LEN (1) + +#define DOT11F_IE_CHALLENGETEXT_MAX_LEN (253) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_challenge_text( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEChallengeText*, + bool); + +uint32_t dot11f_pack_ie_challenge_text( + tpAniSirGlobal, + tDot11fIEChallengeText *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ChallengeText( + tpAniSirGlobal, + tDot11fIEChallengeText *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 37 (0x25) */ +typedef struct sDot11fIEChanSwitchAnn { + uint8_t present; + uint8_t switchMode; + uint8_t newChannel; + uint8_t switchCount; +} tDot11fIEChanSwitchAnn; + +#define DOT11F_EID_CHANSWITCHANN (37) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_CHANSWITCHANN_MIN_LEN (3) + +#define DOT11F_IE_CHANSWITCHANN_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_chan_switch_ann( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEChanSwitchAnn*, + bool); + +uint32_t dot11f_pack_ie_chan_switch_ann( + tpAniSirGlobal, + tDot11fIEChanSwitchAnn *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ChanSwitchAnn( + tpAniSirGlobal, + tDot11fIEChanSwitchAnn *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 196 (0xc4) */ +typedef struct sDot11fIEChannelSwitchWrapper { + uint8_t present; + tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn; + tDot11fIEvht_transmit_power_env vht_transmit_power_env; +} tDot11fIEChannelSwitchWrapper; + +#define DOT11F_EID_CHANNELSWITCHWRAPPER (196) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_CHANNELSWITCHWRAPPER_MIN_LEN (0) + +#define DOT11F_IE_CHANNELSWITCHWRAPPER_MAX_LEN (12) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_channel_switch_wrapper( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEChannelSwitchWrapper*, + bool); + +uint32_t dot11f_pack_ie_channel_switch_wrapper( + tpAniSirGlobal, + tDot11fIEChannelSwitchWrapper *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_channel_switch_wrapper( + tpAniSirGlobal, + tDot11fIEChannelSwitchWrapper *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 7 (0x07) */ +typedef struct sDot11fIECountry { + uint8_t present; + uint8_t country[3]; + uint8_t num_triplets; + uint8_t triplets[84][3]; +} tDot11fIECountry; + +#define DOT11F_EID_COUNTRY (7) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_COUNTRY_MIN_LEN (3) + +#define DOT11F_IE_COUNTRY_MAX_LEN (255) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_country( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIECountry*, + bool); + +uint32_t dot11f_pack_ie_country( + tpAniSirGlobal, + tDot11fIECountry *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_country( + tpAniSirGlobal, + tDot11fIECountry *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 3 (0x03) */ +typedef struct sDot11fIEDSParams { + uint8_t present; + uint8_t curr_channel; +} tDot11fIEDSParams; + +#define DOT11F_EID_DSPARAMS (3) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_DSPARAMS_MIN_LEN (1) + +#define DOT11F_IE_DSPARAMS_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_DSParams( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEDSParams*, + bool); + +uint32_t dot11f_pack_ie_ds_params( + tpAniSirGlobal, + tDot11fIEDSParams *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_DSParams( + tpAniSirGlobal, + tDot11fIEDSParams *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 12 (0x0c) */ +typedef struct sDot11fIEEDCAParamSet { + uint8_t present; + uint8_t qos; + uint8_t reserved; + uint8_t acbe_aifsn:4; + uint8_t acbe_acm:1; + uint8_t acbe_aci:2; + uint8_t unused1:1; + uint8_t acbe_acwmin:4; + uint8_t acbe_acwmax:4; + uint16_t acbe_txoplimit; + uint8_t acbk_aifsn:4; + uint8_t acbk_acm:1; + uint8_t acbk_aci:2; + uint8_t unused2:1; + uint8_t acbk_acwmin:4; + uint8_t acbk_acwmax:4; + uint16_t acbk_txoplimit; + uint8_t acvi_aifsn:4; + uint8_t acvi_acm:1; + uint8_t acvi_aci:2; + uint8_t unused3:1; + uint8_t acvi_acwmin:4; + uint8_t acvi_acwmax:4; + uint16_t acvi_txoplimit; + uint8_t acvo_aifsn:4; + uint8_t acvo_acm:1; + uint8_t acvo_aci:2; + uint8_t unused4:1; + uint8_t acvo_acwmin:4; + uint8_t acvo_acwmax:4; + uint16_t acvo_txoplimit; +} tDot11fIEEDCAParamSet; + +#define DOT11F_EID_EDCAPARAMSET (12) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_EDCAPARAMSET_MIN_LEN (18) + +#define DOT11F_IE_EDCAPARAMSET_MAX_LEN (18) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_edca_param_set( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEEDCAParamSet*, + bool); + +uint32_t dot11f_pack_ie_edca_param_set( + tpAniSirGlobal, + tDot11fIEEDCAParamSet *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_EDCAParamSet( + tpAniSirGlobal, + tDot11fIEEDCAParamSet *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 42 (0x2a) */ +typedef struct sDot11fIEERPInfo { + uint8_t present; + uint8_t non_erp_present:1; + uint8_t use_prot:1; + uint8_t barker_preamble:1; + uint8_t unused:5; +} tDot11fIEERPInfo; + +#define DOT11F_EID_ERPINFO (42) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ERPINFO_MIN_LEN (1) + +#define DOT11F_IE_ERPINFO_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_erp_info( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEERPInfo*, + bool); + +uint32_t dot11f_pack_ie_erp_info( + tpAniSirGlobal, + tDot11fIEERPInfo *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ERPInfo( + tpAniSirGlobal, + tDot11fIEERPInfo *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 156 (0x9c) {OUI 0x00, 0x40, 0x96, 0x00} */ +typedef struct sDot11fIEESECckmOpaque { + uint8_t present; + uint8_t num_data; + uint8_t data[20]; +} tDot11fIEESECckmOpaque; + +#define DOT11F_EID_ESECCKMOPAQUE (156) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ESECCKMOPAQUE_MIN_LEN (10) + +#define DOT11F_IE_ESECCKMOPAQUE_MAX_LEN (24) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ese_cckm_opaque( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEESECckmOpaque*, + bool); + +uint32_t dot11f_pack_ie_ese_cckm_opaque( + tpAniSirGlobal, + tDot11fIEESECckmOpaque *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ESECckmOpaque( + tpAniSirGlobal, + tDot11fIEESECckmOpaque *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x40, 0x96, 0x01} */ +typedef struct sDot11fIEESERadMgmtCap { + uint8_t present; + uint8_t mgmt_state; + uint8_t mbssid_mask:3; + uint8_t reserved:5; +} tDot11fIEESERadMgmtCap; + +#define DOT11F_EID_ESERADMGMTCAP (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ESERADMGMTCAP_MIN_LEN (6) + +#define DOT11F_IE_ESERADMGMTCAP_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ese_rad_mgmt_cap( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEESERadMgmtCap*, + bool); + +uint32_t dot11f_pack_ie_ese_rad_mgmt_cap( + tpAniSirGlobal, + tDot11fIEESERadMgmtCap *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ESERadMgmtCap( + tpAniSirGlobal, + tDot11fIEESERadMgmtCap *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x40, 0x96, 0x07} */ +typedef struct sDot11fIEESETrafStrmMet { + uint8_t present; + uint8_t tsid; + uint8_t state; + uint16_t msmt_interval; +} tDot11fIEESETrafStrmMet; + +#define DOT11F_EID_ESETRAFSTRMMET (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ESETRAFSTRMMET_MIN_LEN (8) + +#define DOT11F_IE_ESETRAFSTRMMET_MAX_LEN (8) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ese_traf_strm_met( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEESETrafStrmMet*, + bool); + +uint32_t dot11f_pack_ie_ese_traf_strm_met( + tpAniSirGlobal, + tDot11fIEESETrafStrmMet *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ESETrafStrmMet( + tpAniSirGlobal, + tDot11fIEESETrafStrmMet *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x40, 0x96, 0x08} */ +typedef struct sDot11fIEESETrafStrmRateSet { + uint8_t present; + uint8_t tsid; + uint8_t num_tsrates; + uint8_t tsrates[8]; +} tDot11fIEESETrafStrmRateSet; + +#define DOT11F_EID_ESETRAFSTRMRATESET (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ESETRAFSTRMRATESET_MIN_LEN (5) + +#define DOT11F_IE_ESETRAFSTRMRATESET_MAX_LEN (13) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ese_traf_strm_rate_set( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEESETrafStrmRateSet*, + bool); + +uint32_t dot11f_pack_ie_ese_traf_strm_rate_set( + tpAniSirGlobal, + tDot11fIEESETrafStrmRateSet *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ESETrafStrmRateSet( + tpAniSirGlobal, + tDot11fIEESETrafStrmRateSet *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 150 (0x96) {OUI 0x00, 0x40, 0x96, 0x00} */ +typedef struct sDot11fIEESETxmitPower { + uint8_t present; + uint8_t power_limit; + uint8_t reserved; +} tDot11fIEESETxmitPower; + +#define DOT11F_EID_ESETXMITPOWER (150) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ESETXMITPOWER_MIN_LEN (6) + +#define DOT11F_IE_ESETXMITPOWER_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ese_txmit_power( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEESETxmitPower*, + bool); + +uint32_t dot11f_pack_ie_ese_txmit_power( + tpAniSirGlobal, + tDot11fIEESETxmitPower *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ESETxmitPower( + tpAniSirGlobal, + tDot11fIEESETxmitPower *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x40, 0x96, 0x03} */ +typedef struct sDot11fIEESEVersion { + uint8_t present; + uint8_t version; +} tDot11fIEESEVersion; + +#define DOT11F_EID_ESEVERSION (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ESEVERSION_MIN_LEN (5) + +#define DOT11F_IE_ESEVERSION_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ese_version( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEESEVersion*, + bool); + +uint32_t dot11f_pack_ie_ese_version( + tpAniSirGlobal, + tDot11fIEESEVersion *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ESEVersion( + tpAniSirGlobal, + tDot11fIEESEVersion *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 127 (0x7f) */ +typedef struct sDot11fIEExtCap { + uint8_t present; + uint8_t num_bytes; + uint8_t bytes[15]; +} tDot11fIEExtCap; + +#define DOT11F_EID_EXTCAP (127) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_EXTCAP_MIN_LEN (1) + +#define DOT11F_IE_EXTCAP_MAX_LEN (15) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ext_cap( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEExtCap*, + bool); + +uint32_t dot11f_pack_ie_ext_cap( + tpAniSirGlobal, + tDot11fIEExtCap *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ExtCap( + tpAniSirGlobal, + tDot11fIEExtCap *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 50 (0x32) */ +typedef struct sDot11fIEExtSuppRates { + uint8_t present; + uint8_t num_rates; + uint8_t rates[12]; +} tDot11fIEExtSuppRates; + +#define DOT11F_EID_EXTSUPPRATES (50) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_EXTSUPPRATES_MIN_LEN (1) + +#define DOT11F_IE_EXTSUPPRATES_MAX_LEN (12) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ext_supp_rates( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEExtSuppRates*, + bool); + +uint32_t dot11f_pack_ie_ext_supp_rates( + tpAniSirGlobal, + tDot11fIEExtSuppRates *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ExtSuppRates( + tpAniSirGlobal, + tDot11fIEExtSuppRates *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 2 (0x02) */ +typedef struct sDot11fIEFHParamSet { + uint8_t present; + uint16_t dwell_time; + uint8_t hop_set; + uint8_t hop_pattern; + uint8_t hop_index; +} tDot11fIEFHParamSet; + +#define DOT11F_EID_FHPARAMSET (2) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FHPARAMSET_MIN_LEN (5) + +#define DOT11F_IE_FHPARAMSET_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fh_param_set( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEFHParamSet*, + bool); + +uint32_t dot11f_pack_ie_fh_param_set( + tpAniSirGlobal, + tDot11fIEFHParamSet *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_FHParamSet( + tpAniSirGlobal, + tDot11fIEFHParamSet *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 8 (0x08) */ +typedef struct sDot11fIEFHParams { + uint8_t present; + uint8_t radix; + uint8_t nchannels; +} tDot11fIEFHParams; + +#define DOT11F_EID_FHPARAMS (8) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FHPARAMS_MIN_LEN (2) + +#define DOT11F_IE_FHPARAMS_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fh_params( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEFHParams*, + bool); + +uint32_t dot11f_pack_ie_fh_params( + tpAniSirGlobal, + tDot11fIEFHParams *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_FHParams( + tpAniSirGlobal, + tDot11fIEFHParams *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 9 (0x09) */ +typedef struct sDot11fIEFHPattTable { + uint8_t present; + uint8_t flag; + uint8_t nsets; + uint8_t modulus; + uint8_t offset; + uint8_t num_randtable; + uint8_t randtable[251]; +} tDot11fIEFHPattTable; + +#define DOT11F_EID_FHPATTTABLE (9) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FHPATTTABLE_MIN_LEN (4) + +#define DOT11F_IE_FHPATTTABLE_MAX_LEN (255) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fh_patt_table( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEFHPattTable*, + bool); + +uint32_t dot11f_pack_ie_fh_patt_table( + tpAniSirGlobal, + tDot11fIEFHPattTable *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_FHPattTable( + tpAniSirGlobal, + tDot11fIEFHPattTable *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 55 (0x37) */ +typedef struct sDot11fIEFTInfo { + uint8_t present; + uint16_t reserved:8; + uint16_t IECount:8; + uint8_t MIC[16]; + uint8_t Anonce[32]; + uint8_t Snonce[32]; + tDot11fIER1KH_ID R1KH_ID; + tDot11fIEGTK GTK; + tDot11fIER0KH_ID R0KH_ID; + tDot11fIEIGTK IGTK; +} tDot11fIEFTInfo; + +#define DOT11F_EID_FTINFO (55) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FTINFO_MIN_LEN (82) + +#define DOT11F_IE_FTINFO_MAX_LEN (220) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ft_info( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEFTInfo*, + bool); + +uint32_t dot11f_pack_ie_ft_info( + tpAniSirGlobal, + tDot11fIEFTInfo *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ieft_info( + tpAniSirGlobal, + tDot11fIEFTInfo *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 45 (0x2d) */ +typedef struct sDot11fIEHTCaps { + uint8_t present; + uint16_t advCodingCap:1; + uint16_t supportedChannelWidthSet:1; + uint16_t mimoPowerSave:2; + uint16_t greenField:1; + uint16_t shortGI20MHz:1; + uint16_t shortGI40MHz:1; + uint16_t txSTBC:1; + uint16_t rxSTBC:2; + uint16_t delayedBA:1; + uint16_t maximalAMSDUsize:1; + uint16_t dsssCckMode40MHz:1; + uint16_t psmp:1; + uint16_t stbcControlFrame:1; + uint16_t lsigTXOPProtection:1; + uint8_t maxRxAMPDUFactor:2; + uint8_t mpduDensity:3; + uint8_t reserved1:3; + uint8_t supportedMCSSet[16]; + uint16_t pco:1; + uint16_t transitionTime:2; + uint16_t reserved2:5; + uint16_t mcsFeedback:2; + uint16_t reserved3:6; + uint32_t txBF:1; + uint32_t rxStaggeredSounding:1; + uint32_t txStaggeredSounding:1; + uint32_t rxZLF:1; + uint32_t txZLF:1; + uint32_t implicitTxBF:1; + uint32_t calibration:2; + uint32_t explicitCSITxBF:1; + uint32_t explicitUncompressedSteeringMatrix:1; + uint32_t explicitBFCSIFeedback:3; + uint32_t explicitUncompressedSteeringMatrixFeedback:3; + uint32_t explicitCompressedSteeringMatrixFeedback:3; + uint32_t csiNumBFAntennae:2; + uint32_t uncompressedSteeringMatrixBFAntennae:2; + uint32_t compressedSteeringMatrixBFAntennae:2; + uint32_t reserved4:7; + uint8_t antennaSelection:1; + uint8_t explicitCSIFeedbackTx:1; + uint8_t antennaIndicesFeedbackTx:1; + uint8_t explicitCSIFeedback:1; + uint8_t antennaIndicesFeedback:1; + uint8_t rxAS:1; + uint8_t txSoundingPPDUs:1; + uint8_t reserved5:1; + uint8_t num_rsvd; + uint8_t rsvd[32]; +} tDot11fIEHTCaps; + +#define DOT11F_EID_HTCAPS (45) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_HTCAPS_MIN_LEN (26) + +#define DOT11F_IE_HTCAPS_MAX_LEN (58) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ht_caps( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEHTCaps*, + bool); + +uint32_t dot11f_pack_ie_ht_caps( + tpAniSirGlobal, + tDot11fIEHTCaps *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_HTCaps( + tpAniSirGlobal, + tDot11fIEHTCaps *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 61 (0x3d) */ +typedef struct sDot11fIEHTInfo { + uint8_t present; + uint8_t primaryChannel; + uint8_t secondaryChannelOffset:2; + uint8_t recommendedTxWidthSet:1; + uint8_t rifsMode:1; + uint8_t controlledAccessOnly:1; + uint8_t serviceIntervalGranularity:3; + uint16_t opMode:2; + uint16_t nonGFDevicesPresent:1; + uint16_t transmitBurstLimit:1; + uint16_t obssNonHTStaPresent:1; + uint16_t chan_center_freq_seg2:8; + uint16_t reserved:3; + uint16_t basicSTBCMCS:7; + uint16_t dualCTSProtection:1; + uint16_t secondaryBeacon:1; + uint16_t lsigTXOPProtectionFullSupport:1; + uint16_t pcoActive:1; + uint16_t pcoPhase:1; + uint16_t reserved2:4; + uint8_t basicMCSSet[16]; + uint8_t num_rsvd; + uint8_t rsvd[32]; +} tDot11fIEHTInfo; + +#define DOT11F_EID_HTINFO (61) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_HTINFO_MIN_LEN (22) + +#define DOT11F_IE_HTINFO_MAX_LEN (54) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ht_info( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEHTInfo*, + bool); + +uint32_t dot11f_pack_ie_ht_info( + tpAniSirGlobal, + tDot11fIEHTInfo *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_HTInfo( + tpAniSirGlobal, + tDot11fIEHTInfo *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 6 (0x06) */ +typedef struct sDot11fIEIBSSParams { + uint8_t present; + uint16_t atim; +} tDot11fIEIBSSParams; + +#define DOT11F_EID_IBSSPARAMS (6) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_IBSSPARAMS_MIN_LEN (2) + +#define DOT11F_IE_IBSSPARAMS_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ibss_params( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEIBSSParams*, + bool); + +uint32_t dot11f_pack_ie_ibss_params( + tpAniSirGlobal, + tDot11fIEIBSSParams *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_IBSSParams( + tpAniSirGlobal, + tDot11fIEIBSSParams *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 101 (0x65) */ +typedef struct sDot11fIELinkIdentifier { + uint8_t present; + uint8_t bssid[6]; + uint8_t InitStaAddr[6]; + uint8_t RespStaAddr[6]; +} tDot11fIELinkIdentifier; + +#define DOT11F_EID_LINKIDENTIFIER (101) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_LINKIDENTIFIER_MIN_LEN (18) + +#define DOT11F_IE_LINKIDENTIFIER_MAX_LEN (18) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_link_identifier( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIELinkIdentifier*, + bool); + +uint32_t dot11f_pack_ie_link_identifier( + tpAniSirGlobal, + tDot11fIELinkIdentifier *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_LinkIdentifier( + tpAniSirGlobal, + tDot11fIELinkIdentifier *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x16} (Multi-IE) */ +typedef struct sDot11fIEMBO_IE { + uint8_t present; + tDot11fTLVmbo_ap_cap mbo_ap_cap; + tDot11fTLVnon_prefferd_chan_rep non_prefferd_chan_rep; + tDot11fTLVcellular_data_cap cellular_data_cap; + tDot11fTLVassoc_disallowed assoc_disallowed; + tDot11fTLVcellular_data_con_pref cellular_data_con_pref; + tDot11fTLVtransition_reason transition_reason; + tDot11fTLVtransition_reject_reason transition_reject_reason; + tDot11fTLVassoc_retry_delay assoc_retry_delay; + tDot11fTLVoce_cap oce_cap; + tDot11fTLVrssi_assoc_rej rssi_assoc_rej; + tDot11fTLVreduced_wan_metrics reduced_wan_metrics; +} tDot11fIEMBO_IE; + +#define DOT11F_EID_MBO_IE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_MBO_IE_MIN_LEN (4) + +#define DOT11F_IE_MBO_IE_MAX_LEN (293) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_MBO_IE( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEMBO_IE*, + bool); + +uint32_t dot11f_pack_ie_MBO_IE( + tpAniSirGlobal, + tDot11fIEMBO_IE *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_MBO_IE( + tpAniSirGlobal, + tDot11fIEMBO_IE *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 39 (0x27) */ +typedef struct sDot11fIEMeasurementReport { + uint8_t present; + uint8_t token; + uint8_t late:1; + uint8_t incapable:1; + uint8_t refused:1; + uint8_t unused:5; + uint8_t type; + union { + struct { + uint8_t channel; + tDOT11F_U64 meas_start_time; + uint16_t meas_duration; + uint8_t bss:1; + uint8_t ofdm_preamble:1; + uint8_t unid_signal:1; + uint8_t rader:1; + uint8_t unmeasured:1; + uint8_t unused:3; + } Basic; /* type = 0 */ + struct { + uint8_t channel; + tDOT11F_U64 meas_start_time; + uint16_t meas_duration; + uint8_t cca_busy_fraction; + } CCA; /* type = 1 */ + struct { + uint8_t channel; + tDOT11F_U64 meas_start_time; + uint16_t meas_duration; + uint8_t rpi0_density; + uint8_t rpi1_density; + uint8_t rpi2_density; + uint8_t rpi3_density; + uint8_t rpi4_density; + uint8_t rpi5_density; + uint8_t rpi6_density; + uint8_t rpi7_density; + } RPIHistogram; /* type = 2 */ + struct { + uint8_t regClass; + uint8_t channel; + tDOT11F_U64 meas_start_time; + uint16_t meas_duration; + uint8_t condensed_PHY:7; + uint8_t reported_frame_type:1; + uint8_t RCPI; + uint8_t RSNI; + uint8_t BSSID[6]; + uint8_t antenna_id; + uint32_t parent_TSF; + tDot11fIEBeaconReportFrmBody BeaconReportFrmBody; + tDot11fIEbeacon_report_frm_body_fragment_id beacon_report_frm_body_fragment_id; + tDot11fIElast_beacon_report_indication last_beacon_report_indication; + } Beacon; /* type = 5 */ + } report; +} tDot11fIEMeasurementReport; + +#define DOT11F_EID_MEASUREMENTREPORT (39) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_MEASUREMENTREPORT_MIN_LEN (3) + +#define DOT11F_IE_MEASUREMENTREPORT_MAX_LEN (29) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_measurement_report( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEMeasurementReport*, + bool); + +uint32_t dot11f_pack_ie_measurement_report( + tpAniSirGlobal, + tDot11fIEMeasurementReport *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_measurement_report( + tpAniSirGlobal, + tDot11fIEMeasurementReport *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 38 (0x26) */ +typedef struct sDot11fIEMeasurementRequest { + uint8_t present; + uint8_t measurement_token; + uint8_t parallel:1; + uint8_t enable:1; + uint8_t request:1; + uint8_t report:1; + uint8_t durationMandatory:1; + uint8_t unused:3; + uint8_t measurement_type; + union { + struct { + uint8_t channel_no; + uint8_t meas_start_time[8]; + uint16_t meas_duration; + } Basic; /* measurement_type = 0 */ + struct { + uint8_t channel_no; + uint8_t meas_start_time[8]; + uint16_t meas_duration; + } CCA; /* measurement_type = 1 */ + struct { + uint8_t channel_no; + uint8_t meas_start_time[8]; + uint16_t meas_duration; + } RPIHistogram; /* measurement_type = 2 */ + struct { + uint8_t regClass; + uint8_t channel; + uint16_t randomization; + uint16_t meas_duration; + uint8_t meas_mode; + uint8_t BSSID[6]; + tDot11fIESSID SSID; + tDot11fIEBeaconReporting BeaconReporting; + tDot11fIEBcnReportingDetail BcnReportingDetail; + tDot11fIERequestedInfo RequestedInfo; + uint16_t num_APChannelReport; + tDot11fIEAPChannelReport APChannelReport[2]; + tDot11fIElast_beacon_report_indication last_beacon_report_indication; + } Beacon; /* measurement_type = 5 */ + struct { + uint8_t loc_subject; + tDot11fIEazimuth_req azimuth_req; + tDot11fIEreq_mac_addr req_mac_addr; + tDot11fIEtgt_mac_addr tgt_mac_addr; + tDot11fIEmax_age max_age; + } lci; /* measurement_type = 8 */ + struct { + uint16_t random_interval; + uint8_t min_ap_count; + tDot11fIEneighbor_rpt neighbor_rpt; + tDot11fIEmax_age max_age; + } ftmrr; /* measurement_type = 16 */ + } measurement_request; +} tDot11fIEMeasurementRequest; + +#define DOT11F_EID_MEASUREMENTREQUEST (38) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_MEASUREMENTREQUEST_MIN_LEN (4) + +#define DOT11F_IE_MEASUREMENTREQUEST_MAX_LEN (16) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_measurement_request( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEMeasurementRequest*, + bool); + +uint32_t dot11f_pack_ie_measurement_request( + tpAniSirGlobal, + tDot11fIEMeasurementRequest *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_measurement_request( + tpAniSirGlobal, + tDot11fIEMeasurementRequest *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 54 (0x36) */ +typedef struct sDot11fIEMobilityDomain { + uint8_t present; + uint16_t MDID; + uint8_t overDSCap:1; + uint8_t resourceReqCap:1; + uint8_t reserved:6; +} tDot11fIEMobilityDomain; + +#define DOT11F_EID_MOBILITYDOMAIN (54) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_MOBILITYDOMAIN_MIN_LEN (3) + +#define DOT11F_IE_MOBILITYDOMAIN_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_mobility_domain( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEMobilityDomain*, + bool); + +uint32_t dot11f_pack_ie_mobility_domain( + tpAniSirGlobal, + tDot11fIEMobilityDomain *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_MobilityDomain( + tpAniSirGlobal, + tDot11fIEMobilityDomain *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 52 (0x34) */ +typedef struct sDot11fIENeighborReport { + uint8_t present; + uint8_t bssid[6]; + uint8_t APReachability:2; + uint8_t Security:1; + uint8_t KeyScope:1; + uint8_t SpecMgmtCap:1; + uint8_t QosCap:1; + uint8_t apsd:1; + uint8_t rrm:1; + uint8_t DelayedBA:1; + uint8_t ImmBA:1; + uint8_t MobilityDomain:1; + uint8_t reserved:5; + uint16_t reserved1; + uint8_t regulatoryClass; + uint8_t channel; + uint8_t PhyType; + tDot11fIETSFInfo TSFInfo; + tDot11fIECondensedCountryStr CondensedCountryStr; + tDot11fIEMeasurementPilot MeasurementPilot; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIEMultiBssid MultiBssid; +} tDot11fIENeighborReport; + +#define DOT11F_EID_NEIGHBORREPORT (52) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_NEIGHBORREPORT_MIN_LEN (13) + +#define DOT11F_IE_NEIGHBORREPORT_MAX_LEN (546) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_neighbor_report( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIENeighborReport*, + bool); + +uint32_t dot11f_pack_ie_neighbor_report( + tpAniSirGlobal, + tDot11fIENeighborReport *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_neighbor_report( + tpAniSirGlobal, + tDot11fIENeighborReport *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 74 (0x4a) */ +typedef struct sDot11fIEOBSSScanParameters { + uint8_t present; + uint16_t obssScanPassiveDwell; + uint16_t obssScanActiveDwell; + uint16_t bssChannelWidthTriggerScanInterval; + uint16_t obssScanPassiveTotalPerChannel; + uint16_t obssScanActiveTotalPerChannel; + uint16_t bssWidthChannelTransitionDelayFactor; + uint16_t obssScanActivityThreshold; +} tDot11fIEOBSSScanParameters; + +#define DOT11F_EID_OBSSSCANPARAMETERS (74) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_OBSSSCANPARAMETERS_MIN_LEN (14) + +#define DOT11F_IE_OBSSSCANPARAMETERS_MAX_LEN (14) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_obss_scan_parameters( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEOBSSScanParameters*, + bool); + +uint32_t dot11f_pack_ie_obss_scan_parameters( + tpAniSirGlobal, + tDot11fIEOBSSScanParameters *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_OBSSScanParameters( + tpAniSirGlobal, + tDot11fIEOBSSScanParameters *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 199 (0xc7) */ +typedef struct sDot11fIEOperatingMode { + uint8_t present; + uint8_t chanWidth:2; + uint8_t vht_160_80p80_supp:1; + uint8_t no_ldpc:1; + uint8_t rxNSS:3; + uint8_t rxNSSType:1; +} tDot11fIEOperatingMode; + +#define DOT11F_EID_OPERATINGMODE (199) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_OPERATINGMODE_MIN_LEN (1) + +#define DOT11F_IE_OPERATINGMODE_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_operating_mode( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEOperatingMode*, + bool); + +uint32_t dot11f_pack_ie_operating_mode( + tpAniSirGlobal, + tDot11fIEOperatingMode *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_OperatingMode( + tpAniSirGlobal, + tDot11fIEOperatingMode *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x09} (Multi-IE) */ +typedef struct sDot11fIEP2PAssocReq { + uint8_t present; + tDot11fTLVP2PCapability P2PCapability; + tDot11fTLVExtendedListenTiming ExtendedListenTiming; + tDot11fTLVP2PDeviceInfo P2PDeviceInfo; +} tDot11fIEP2PAssocReq; + +#define DOT11F_EID_P2PASSOCREQ (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_P2PASSOCREQ_MIN_LEN (4) + +#define DOT11F_IE_P2PASSOCREQ_MAX_LEN (71) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_p2_p_assoc_req( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEP2PAssocReq*, + bool); + +uint32_t dot11f_pack_ie_p2_p_assoc_req( + tpAniSirGlobal, + tDot11fIEP2PAssocReq *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iep2_p_assoc_req( + tpAniSirGlobal, + tDot11fIEP2PAssocReq *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x09} (Multi-IE) */ +typedef struct sDot11fIEP2PAssocRes { + uint8_t present; + tDot11fTLVP2PStatus P2PStatus; + tDot11fTLVExtendedListenTiming ExtendedListenTiming; +} tDot11fIEP2PAssocRes; + +#define DOT11F_EID_P2PASSOCRES (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_P2PASSOCRES_MIN_LEN (4) + +#define DOT11F_IE_P2PASSOCRES_MAX_LEN (15) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_p2_p_assoc_res( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEP2PAssocRes*, + bool); + +uint32_t dot11f_pack_ie_p2_p_assoc_res( + tpAniSirGlobal, + tDot11fIEP2PAssocRes *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iep2_p_assoc_res( + tpAniSirGlobal, + tDot11fIEP2PAssocRes *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x09} (Multi-IE) */ +typedef struct sDot11fIEP2PBeacon { + uint8_t present; + tDot11fTLVP2PCapability P2PCapability; + tDot11fTLVP2PDeviceId P2PDeviceId; + tDot11fTLVNoticeOfAbsence NoticeOfAbsence; +} tDot11fIEP2PBeacon; + +#define DOT11F_EID_P2PBEACON (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_P2PBEACON_MIN_LEN (4) + +#define DOT11F_IE_P2PBEACON_MAX_LEN (59) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_p2_p_beacon( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEP2PBeacon*, + bool); + +uint32_t dot11f_pack_ie_p2_p_beacon( + tpAniSirGlobal, + tDot11fIEP2PBeacon *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iep2_p_beacon( + tpAniSirGlobal, + tDot11fIEP2PBeacon *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x09} (Multi-IE) */ +typedef struct sDot11fIEP2PBeaconProbeRes { + uint8_t present; + tDot11fTLVP2PCapability P2PCapability; + tDot11fTLVP2PDeviceId P2PDeviceId; + tDot11fTLVExtendedListenTiming ExtendedListenTiming; + tDot11fTLVNoticeOfAbsence NoticeOfAbsence; + tDot11fTLVP2PDeviceInfo P2PDeviceInfo; + tDot11fTLVP2PGroupInfo P2PGroupInfo; +} tDot11fIEP2PBeaconProbeRes; + +#define DOT11F_EID_P2PBEACONPROBERES (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_P2PBEACONPROBERES_MIN_LEN (4) + +#define DOT11F_IE_P2PBEACONPROBERES_MAX_LEN (1148) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_p2_p_beacon_probe_res( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEP2PBeaconProbeRes*, + bool); + +uint32_t dot11f_pack_ie_p2_p_beacon_probe_res( + tpAniSirGlobal, + tDot11fIEP2PBeaconProbeRes *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iep2_p_beacon_probe_res( + tpAniSirGlobal, + tDot11fIEP2PBeaconProbeRes *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x09} (Multi-IE) */ +typedef struct sDot11fIEP2PDeAuth { + uint8_t present; + tDot11fTLVMinorReasonCode MinorReasonCode; +} tDot11fIEP2PDeAuth; + +#define DOT11F_EID_P2PDEAUTH (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_P2PDEAUTH_MIN_LEN (4) + +#define DOT11F_IE_P2PDEAUTH_MAX_LEN (8) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_p2_p_de_auth( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEP2PDeAuth*, + bool); + +uint32_t dot11f_pack_ie_p2_p_de_auth( + tpAniSirGlobal, + tDot11fIEP2PDeAuth *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iep2_p_de_auth( + tpAniSirGlobal, + tDot11fIEP2PDeAuth *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x09} (Multi-IE) */ +typedef struct sDot11fIEP2PDisAssoc { + uint8_t present; + tDot11fTLVMinorReasonCode MinorReasonCode; +} tDot11fIEP2PDisAssoc; + +#define DOT11F_EID_P2PDISASSOC (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_P2PDISASSOC_MIN_LEN (4) + +#define DOT11F_IE_P2PDISASSOC_MAX_LEN (8) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_p2_p_dis_assoc( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEP2PDisAssoc*, + bool); + +uint32_t dot11f_pack_ie_p2_p_dis_assoc( + tpAniSirGlobal, + tDot11fIEP2PDisAssoc *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iep2_p_dis_assoc( + tpAniSirGlobal, + tDot11fIEP2PDisAssoc *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x09} */ +typedef struct sDot11fIEP2PIEOpaque { + uint8_t present; + uint8_t num_data; + uint8_t data[249]; +} tDot11fIEP2PIEOpaque; + +#define DOT11F_EID_P2PIEOPAQUE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_P2PIEOPAQUE_MIN_LEN (6) + +#define DOT11F_IE_P2PIEOPAQUE_MAX_LEN (253) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_p2_pie_opaque( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEP2PIEOpaque*, + bool); + +uint32_t dot11f_pack_ie_p2_pie_opaque( + tpAniSirGlobal, + tDot11fIEP2PIEOpaque *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_P2PIEOpaque( + tpAniSirGlobal, + tDot11fIEP2PIEOpaque *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x09} (Multi-IE) */ +typedef struct sDot11fIEP2PProbeReq { + uint8_t present; + tDot11fTLVP2PCapability P2PCapability; + tDot11fTLVP2PDeviceId P2PDeviceId; + tDot11fTLVListenChannel ListenChannel; + tDot11fTLVExtendedListenTiming ExtendedListenTiming; + tDot11fTLVOperatingChannel OperatingChannel; +} tDot11fIEP2PProbeReq; + +#define DOT11F_EID_P2PPROBEREQ (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_P2PPROBEREQ_MIN_LEN (4) + +#define DOT11F_IE_P2PPROBEREQ_MAX_LEN (41) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_p2_p_probe_req( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEP2PProbeReq*, + bool); + +uint32_t dot11f_pack_ie_p2_p_probe_req( + tpAniSirGlobal, + tDot11fIEP2PProbeReq *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iep2_p_probe_req( + tpAniSirGlobal, + tDot11fIEP2PProbeReq *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x09} (Multi-IE) */ +typedef struct sDot11fIEP2PProbeRes { + uint8_t present; + tDot11fTLVP2PCapability P2PCapability; + tDot11fTLVExtendedListenTiming ExtendedListenTiming; + tDot11fTLVNoticeOfAbsence NoticeOfAbsence; + tDot11fTLVP2PDeviceInfo P2PDeviceInfo; + tDot11fTLVP2PGroupInfo P2PGroupInfo; +} tDot11fIEP2PProbeRes; + +#define DOT11F_EID_P2PPROBERES (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_P2PPROBERES_MIN_LEN (4) + +#define DOT11F_IE_P2PPROBERES_MAX_LEN (1139) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_p2_p_probe_res( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEP2PProbeRes*, + bool); + +uint32_t dot11f_pack_ie_p2_p_probe_res( + tpAniSirGlobal, + tDot11fIEP2PProbeRes *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iep2_p_probe_res( + tpAniSirGlobal, + tDot11fIEP2PProbeRes *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 105 (0x69) */ +typedef struct sDot11fIEPTIControl { + uint8_t present; + uint8_t tid; + uint16_t sequence_control; +} tDot11fIEPTIControl; + +#define DOT11F_EID_PTICONTROL (105) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_PTICONTROL_MIN_LEN (3) + +#define DOT11F_IE_PTICONTROL_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_pti_control( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEPTIControl*, + bool); + +uint32_t dot11f_pack_ie_pti_control( + tpAniSirGlobal, + tDot11fIEPTIControl *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_PTIControl( + tpAniSirGlobal, + tDot11fIEPTIControl *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 106 (0x6a) */ +typedef struct sDot11fIEPUBufferStatus { + uint8_t present; + uint8_t ac_bk_traffic_aval:1; + uint8_t ac_be_traffic_aval:1; + uint8_t ac_vi_traffic_aval:1; + uint8_t ac_vo_traffic_aval:1; + uint8_t reserved:4; +} tDot11fIEPUBufferStatus; + +#define DOT11F_EID_PUBUFFERSTATUS (106) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_PUBUFFERSTATUS_MIN_LEN (1) + +#define DOT11F_IE_PUBUFFERSTATUS_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_pu_buffer_status( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEPUBufferStatus*, + bool); + +uint32_t dot11f_pack_ie_pu_buffer_status( + tpAniSirGlobal, + tDot11fIEPUBufferStatus *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_PUBufferStatus( + tpAniSirGlobal, + tDot11fIEPUBufferStatus *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 33 (0x21) */ +typedef struct sDot11fIEPowerCaps { + uint8_t present; + uint8_t minTxPower; + uint8_t maxTxPower; +} tDot11fIEPowerCaps; + +#define DOT11F_EID_POWERCAPS (33) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_POWERCAPS_MIN_LEN (2) + +#define DOT11F_IE_POWERCAPS_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_power_caps( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEPowerCaps*, + bool); + +uint32_t dot11f_pack_ie_power_caps( + tpAniSirGlobal, + tDot11fIEPowerCaps *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_PowerCaps( + tpAniSirGlobal, + tDot11fIEPowerCaps *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 32 (0x20) */ +typedef struct sDot11fIEPowerConstraints { + uint8_t present; + uint8_t localPowerConstraints; +} tDot11fIEPowerConstraints; + +#define DOT11F_EID_POWERCONSTRAINTS (32) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_POWERCONSTRAINTS_MIN_LEN (1) + +#define DOT11F_IE_POWERCONSTRAINTS_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_power_constraints( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEPowerConstraints*, + bool); + +uint32_t dot11f_pack_ie_power_constraints( + tpAniSirGlobal, + tDot11fIEPowerConstraints *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_PowerConstraints( + tpAniSirGlobal, + tDot11fIEPowerConstraints *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 11 (0x0b) */ +typedef struct sDot11fIEQBSSLoad { + uint8_t present; + uint16_t stacount; + uint8_t chautil; + uint16_t avail; +} tDot11fIEQBSSLoad; + +#define DOT11F_EID_QBSSLOAD (11) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_QBSSLOAD_MIN_LEN (5) + +#define DOT11F_IE_QBSSLOAD_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_qbss_load( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEQBSSLoad*, + bool); + +uint32_t dot11f_pack_ie_qbss_load( + tpAniSirGlobal, + tDot11fIEQBSSLoad *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_QBSSLoad( + tpAniSirGlobal, + tDot11fIEQBSSLoad *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0xa0, 0xc6} */ +typedef struct sDot11fIEQComVendorIE { + uint8_t present; + uint8_t type; + uint8_t channel; +} tDot11fIEQComVendorIE; + +#define DOT11F_EID_QCOMVENDORIE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_QCOMVENDORIE_MIN_LEN (5) + +#define DOT11F_IE_QCOMVENDORIE_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_QComVendorIE( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEQComVendorIE*, + bool); + +uint32_t dot11f_pack_ie_QComVendorIE( + tpAniSirGlobal, + tDot11fIEQComVendorIE *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_QComVendorIE( + tpAniSirGlobal, + tDot11fIEQComVendorIE *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 46 (0x2e) */ +typedef struct sDot11fIEQOSCapsAp { + uint8_t present; + uint8_t count:4; + uint8_t qack:1; + uint8_t qreq:1; + uint8_t txopreq:1; + uint8_t reserved:1; +} tDot11fIEQOSCapsAp; + +#define DOT11F_EID_QOSCAPSAP (46) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_QOSCAPSAP_MIN_LEN (1) + +#define DOT11F_IE_QOSCAPSAP_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_qos_caps_ap( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEQOSCapsAp*, + bool); + +uint32_t dot11f_pack_ie_qos_caps_ap( + tpAniSirGlobal, + tDot11fIEQOSCapsAp *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_QOSCapsAp( + tpAniSirGlobal, + tDot11fIEQOSCapsAp *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 46 (0x2e) */ +typedef struct sDot11fIEQOSCapsStation { + uint8_t present; + uint8_t acvo_uapsd:1; + uint8_t acvi_uapsd:1; + uint8_t acbk_uapsd:1; + uint8_t acbe_uapsd:1; + uint8_t qack:1; + uint8_t max_sp_length:2; + uint8_t more_data_ack:1; +} tDot11fIEQOSCapsStation; + +#define DOT11F_EID_QOSCAPSSTATION (46) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_QOSCAPSSTATION_MIN_LEN (1) + +#define DOT11F_IE_QOSCAPSSTATION_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_qos_caps_station( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEQOSCapsStation*, + bool); + +uint32_t dot11f_pack_ie_qos_caps_station( + tpAniSirGlobal, + tDot11fIEQOSCapsStation *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_QOSCapsStation( + tpAniSirGlobal, + tDot11fIEQOSCapsStation *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 110 (0x6e) */ +typedef struct sDot11fIEQosMapSet { + uint8_t present; + uint8_t num_dscp_exceptions; + uint8_t dscp_exceptions[58]; +} tDot11fIEQosMapSet; + +#define DOT11F_EID_QOSMAPSET (110) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_QOSMAPSET_MIN_LEN (16) + +#define DOT11F_IE_QOSMAPSET_MAX_LEN (58) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_qos_map_set( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEQosMapSet*, + bool); + +uint32_t dot11f_pack_ie_qos_map_set( + tpAniSirGlobal, + tDot11fIEQosMapSet *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_QosMapSet( + tpAniSirGlobal, + tDot11fIEQosMapSet *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 40 (0x28) */ +typedef struct sDot11fIEQuiet { + uint8_t present; + uint8_t count; + uint8_t period; + uint16_t duration; + uint16_t offset; +} tDot11fIEQuiet; + +#define DOT11F_EID_QUIET (40) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_QUIET_MIN_LEN (6) + +#define DOT11F_IE_QUIET_MAX_LEN (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_quiet( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEQuiet*, + bool); + +uint32_t dot11f_pack_ie_quiet( + tpAniSirGlobal, + tDot11fIEQuiet *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_Quiet( + tpAniSirGlobal, + tDot11fIEQuiet *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 53 (0x35) */ +typedef struct sDot11fIERCPIIE { + uint8_t present; + uint8_t rcpi; +} tDot11fIERCPIIE; + +#define DOT11F_EID_RCPIIE (53) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_RCPIIE_MIN_LEN (1) + +#define DOT11F_IE_RCPIIE_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_rcpiie( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIERCPIIE*, + bool); + +uint32_t dot11f_pack_ie_rcpiie( + tpAniSirGlobal, + tDot11fIERCPIIE *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_RCPIIE( + tpAniSirGlobal, + tDot11fIERCPIIE *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 57 (0x39) */ +typedef struct sDot11fIERICDataDesc { + uint8_t present; + tDot11fIERICData RICData; + tDot11fIERICDescriptor RICDescriptor; + tDot11fIETSPEC TSPEC; + uint16_t num_TCLAS; + tDot11fIETCLAS TCLAS[2]; + tDot11fIETCLASSPROC TCLASSPROC; + tDot11fIETSDelay TSDelay; + tDot11fIESchedule Schedule; + tDot11fIEWMMTSPEC WMMTSPEC; + uint16_t num_WMMTCLAS; + tDot11fIEWMMTCLAS WMMTCLAS[2]; + tDot11fIEWMMTCLASPROC WMMTCLASPROC; + tDot11fIEWMMTSDelay WMMTSDelay; + tDot11fIEWMMSchedule WMMSchedule; +} tDot11fIERICDataDesc; + +#define DOT11F_EID_RICDATADESC (57) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_RICDATADESC_MIN_LEN (0) + +#define DOT11F_IE_RICDATADESC_MAX_LEN (548) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ric_data_desc( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIERICDataDesc*, + bool); + +uint32_t dot11f_pack_ie_ric_data_desc( + tpAniSirGlobal, + tDot11fIERICDataDesc *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ieric_data_desc( + tpAniSirGlobal, + tDot11fIERICDataDesc *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 48 (0x30) */ +typedef struct sDot11fIERSN { + uint8_t present; + uint16_t version /* Must be 1! */; + uint8_t gp_cipher_suite_present; + uint8_t gp_cipher_suite[4]; + uint16_t pwise_cipher_suite_count; + uint8_t pwise_cipher_suites[6][4]; + uint16_t akm_suite_cnt; + uint8_t akm_suite[6][4]; + uint8_t RSN_Cap_present; + uint8_t RSN_Cap[2]; + uint16_t pmkid_count; + uint8_t pmkid[4][16]; + uint8_t gp_mgmt_cipher_suite_present; + uint8_t gp_mgmt_cipher_suite[4]; +} tDot11fIERSN; + +#define DOT11F_EID_RSN (48) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_RSN_MIN_LEN (2) + +#define DOT11F_IE_RSN_MAX_LEN (130) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_rsn( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIERSN*, + bool); + +uint32_t dot11f_pack_ie_rsn( + tpAniSirGlobal, + tDot11fIERSN *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iersn( + tpAniSirGlobal, + tDot11fIERSN *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 65 (0x41) */ +typedef struct sDot11fIERSNIIE { + uint8_t present; + uint8_t rsni; +} tDot11fIERSNIIE; + +#define DOT11F_EID_RSNIIE (65) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_RSNIIE_MIN_LEN (1) + +#define DOT11F_IE_RSNIIE_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_rsniie( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIERSNIIE*, + bool); + +uint32_t dot11f_pack_ie_rsniie( + tpAniSirGlobal, + tDot11fIERSNIIE *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iersnIIE( + tpAniSirGlobal, + tDot11fIERSNIIE *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 48 (0x30) */ +typedef struct sDot11fIERSNOpaque { + uint8_t present; + uint8_t num_data; + uint8_t data[253]; +} tDot11fIERSNOpaque; + +#define DOT11F_EID_RSNOPAQUE (48) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_RSNOPAQUE_MIN_LEN (0) + +#define DOT11F_IE_RSNOPAQUE_MAX_LEN (253) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_rsn_opaque( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIERSNOpaque*, + bool); + +uint32_t dot11f_pack_ie_rsn_opaque( + tpAniSirGlobal, + tDot11fIERSNOpaque *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iersnOpaque( + tpAniSirGlobal, + tDot11fIERSNOpaque *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 36 (0x24) */ +typedef struct sDot11fIESuppChannels { + uint8_t present; + uint8_t num_bands; + uint8_t bands[48][2]; +} tDot11fIESuppChannels; + +#define DOT11F_EID_SUPPCHANNELS (36) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_SUPPCHANNELS_MIN_LEN (0) + +#define DOT11F_IE_SUPPCHANNELS_MAX_LEN (96) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_supp_channels( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIESuppChannels*, + bool); + +uint32_t dot11f_pack_ie_supp_channels( + tpAniSirGlobal, + tDot11fIESuppChannels *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_SuppChannels( + tpAniSirGlobal, + tDot11fIESuppChannels *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 59 (0x3b) */ +typedef struct sDot11fIESuppOperatingClasses { + uint8_t present; + uint8_t num_classes; + uint8_t classes[32]; +} tDot11fIESuppOperatingClasses; + +#define DOT11F_EID_SUPPOPERATINGCLASSES (59) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_SUPPOPERATINGCLASSES_MIN_LEN (1) + +#define DOT11F_IE_SUPPOPERATINGCLASSES_MAX_LEN (32) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_supp_operating_classes( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIESuppOperatingClasses*, + bool); + +uint32_t dot11f_pack_ie_supp_operating_classes( + tpAniSirGlobal, + tDot11fIESuppOperatingClasses *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_SuppOperatingClasses( + tpAniSirGlobal, + tDot11fIESuppOperatingClasses *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 1 (0x01) */ +typedef struct sDot11fIESuppRates { + uint8_t present; + uint8_t num_rates; + uint8_t rates[12]; +} tDot11fIESuppRates; + +#define DOT11F_EID_SUPPRATES (1) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_SUPPRATES_MIN_LEN (0) + +#define DOT11F_IE_SUPPRATES_MAX_LEN (12) + +#define DOT11F_IS_BG_RATE(_x) (((_x) == 02) || \ + ((_x) == 04) || \ + ((_x) == 11) || \ + ((_x) == 22) || \ + ((_x) == 12) || \ + ((_x) == 18) || \ + ((_x) == 24) || \ + ((_x) == 36) || \ + ((_x) == 48) || \ + ((_x) == 72) || \ + ((_x) == 96) || \ + ((_x) == 108)) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_supp_rates( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIESuppRates*, + bool); + +uint32_t dot11f_pack_ie_supp_rates( + tpAniSirGlobal, + tDot11fIESuppRates *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_SuppRates( + tpAniSirGlobal, + tDot11fIESuppRates *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 5 (0x05) */ +typedef struct sDot11fIETIM { + uint8_t present; + uint8_t dtim_count; + uint8_t dtim_period; + uint8_t bmpctl; + uint8_t num_vbmp; + uint8_t vbmp[251]; +} tDot11fIETIM; + +#define DOT11F_EID_TIM (5) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TIM_MIN_LEN (4) + +#define DOT11F_IE_TIM_MAX_LEN (254) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_tim( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETIM*, + bool); + +uint32_t dot11f_pack_ie_tim( + tpAniSirGlobal, + tDot11fIETIM *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_TIM( + tpAniSirGlobal, + tDot11fIETIM *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 35 (0x23) */ +typedef struct sDot11fIETPCReport { + uint8_t present; + uint8_t tx_power; + uint8_t link_margin; +} tDot11fIETPCReport; + +#define DOT11F_EID_TPCREPORT (35) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TPCREPORT_MIN_LEN (2) + +#define DOT11F_IE_TPCREPORT_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_tpc_report( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETPCReport*, + bool); + +uint32_t dot11f_pack_ie_tpc_report( + tpAniSirGlobal, + tDot11fIETPCReport *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_TPCReport( + tpAniSirGlobal, + tDot11fIETPCReport *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 34 (0x22) */ +typedef struct sDot11fIETPCRequest { + uint8_t present; +} tDot11fIETPCRequest; + +#define DOT11F_EID_TPCREQUEST (34) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TPCREQUEST_MIN_LEN (0) + +#define DOT11F_IE_TPCREQUEST_MAX_LEN (0) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_tpc_request( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETPCRequest*, + bool); + +uint32_t dot11f_pack_ie_tpc_request( + tpAniSirGlobal, + tDot11fIETPCRequest *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_TPCRequest( + tpAniSirGlobal, + tDot11fIETPCRequest *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 69 (0x45) */ +typedef struct sDot11fIETimeAdvertisement { + uint8_t present; + uint8_t timing_capabilities; + uint8_t time_value[10]; + uint8_t time_error[5]; +} tDot11fIETimeAdvertisement; + +#define DOT11F_EID_TIMEADVERTISEMENT (69) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TIMEADVERTISEMENT_MIN_LEN (16) + +#define DOT11F_IE_TIMEADVERTISEMENT_MAX_LEN (16) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_time_advertisement( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETimeAdvertisement*, + bool); + +uint32_t dot11f_pack_ie_time_advertisement( + tpAniSirGlobal, + tDot11fIETimeAdvertisement *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_time_advertisement( + tpAniSirGlobal, + tDot11fIETimeAdvertisement *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 56 (0x38) */ +typedef struct sDot11fIETimeoutInterval { + uint8_t present; + uint8_t timeoutType; + uint32_t timeoutValue; +} tDot11fIETimeoutInterval; + +#define DOT11F_EID_TIMEOUTINTERVAL (56) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_TIMEOUTINTERVAL_MIN_LEN (5) + +#define DOT11F_IE_TIMEOUTINTERVAL_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_timeout_interval( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIETimeoutInterval*, + bool); + +uint32_t dot11f_pack_ie_timeout_interval( + tpAniSirGlobal, + tDot11fIETimeoutInterval *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_TimeoutInterval( + tpAniSirGlobal, + tDot11fIETimeoutInterval *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 193 (0xc1) */ +typedef struct sDot11fIEVHTExtBssLoad { + uint8_t present; + uint8_t muMIMOCapStaCount; + uint8_t ssUnderUtil; + uint8_t FortyMHzUtil; + uint8_t EightyMHzUtil; + uint8_t OneSixtyMHzUtil; +} tDot11fIEVHTExtBssLoad; + +#define DOT11F_EID_VHTEXTBSSLOAD (193) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_VHTEXTBSSLOAD_MIN_LEN (5) + +#define DOT11F_IE_VHTEXTBSSLOAD_MAX_LEN (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_vht_ext_bss_load( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEVHTExtBssLoad*, + bool); + +uint32_t dot11f_pack_ie_vht_ext_bss_load( + tpAniSirGlobal, + tDot11fIEVHTExtBssLoad *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_VHTExtBssLoad( + tpAniSirGlobal, + tDot11fIEVHTExtBssLoad *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x10, 0x18} */ +typedef struct sDot11fIEVendor1IE { + uint8_t present; +} tDot11fIEVendor1IE; + +#define DOT11F_EID_VENDOR1IE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_VENDOR1IE_MIN_LEN (3) + +#define DOT11F_IE_VENDOR1IE_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_vendor1_ie( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEVendor1IE*, + bool); + +uint32_t dot11f_pack_ie_vendor1_ie( + tpAniSirGlobal, + tDot11fIEVendor1IE *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_Vendor1IE( + tpAniSirGlobal, + tDot11fIEVendor1IE *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x16, 0x32} */ +typedef struct sDot11fIEVendor3IE { + uint8_t present; +} tDot11fIEVendor3IE; + +#define DOT11F_EID_VENDOR3IE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_VENDOR3IE_MIN_LEN (3) + +#define DOT11F_IE_VENDOR3IE_MAX_LEN (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_vendor3_ie( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEVendor3IE*, + bool); + +uint32_t dot11f_pack_ie_vendor3_ie( + tpAniSirGlobal, + tDot11fIEVendor3IE *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_Vendor3IE( + tpAniSirGlobal, + tDot11fIEVendor3IE *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 68 (0x44) */ +typedef struct sDot11fIEWAPI { + uint8_t present; + uint16_t version /* Must be 1! */; + uint16_t akm_suite_count; + uint8_t akm_suites[4][4]; + uint16_t unicast_cipher_suite_count; + uint8_t unicast_cipher_suites[4][4]; + uint8_t multicast_cipher_suite[4]; + uint16_t preauth:1; + uint16_t reserved:15; + uint16_t bkid_count; + uint8_t bkid[4][16]; +} tDot11fIEWAPI; + +#define DOT11F_EID_WAPI (68) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WAPI_MIN_LEN (12) + +#define DOT11F_IE_WAPI_MAX_LEN (110) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wapi( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWAPI*, + bool); + +uint32_t dot11f_pack_ie_wapi( + tpAniSirGlobal, + tDot11fIEWAPI *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iewapi( + tpAniSirGlobal, + tDot11fIEWAPI *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 68 (0x44) */ +typedef struct sDot11fIEWAPIOpaque { + uint8_t present; + uint8_t num_data; + uint8_t data[253]; +} tDot11fIEWAPIOpaque; + +#define DOT11F_EID_WAPIOPAQUE (68) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WAPIOPAQUE_MIN_LEN (6) + +#define DOT11F_IE_WAPIOPAQUE_MAX_LEN (253) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wapi_opaque( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWAPIOpaque*, + bool); + +uint32_t dot11f_pack_ie_wapi_opaque( + tpAniSirGlobal, + tDot11fIEWAPIOpaque *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iewapiOpaque( + tpAniSirGlobal, + tDot11fIEWAPIOpaque *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x08, 0x00} */ +typedef struct sDot11fIEWFATPC { + uint8_t present; + uint8_t txPower; + uint8_t linkMargin; +} tDot11fIEWFATPC; + +#define DOT11F_EID_WFATPC (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WFATPC_MIN_LEN (7) + +#define DOT11F_IE_WFATPC_MAX_LEN (7) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wfatpc( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWFATPC*, + bool); + +uint32_t dot11f_pack_ie_wfatpc( + tpAniSirGlobal, + tDot11fIEWFATPC *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WFATPC( + tpAniSirGlobal, + tDot11fIEWFATPC *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x0a} */ +typedef struct sDot11fIEWFDIEOpaque { + uint8_t present; + uint8_t num_data; + uint8_t data[249]; +} tDot11fIEWFDIEOpaque; + +#define DOT11F_EID_WFDIEOPAQUE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WFDIEOPAQUE_MIN_LEN (6) + +#define DOT11F_IE_WFDIEOPAQUE_MAX_LEN (253) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wfdie_opaque( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWFDIEOpaque*, + bool); + +uint32_t dot11f_pack_ie_wfdie_opaque( + tpAniSirGlobal, + tDot11fIEWFDIEOpaque *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WFDIEOpaque( + tpAniSirGlobal, + tDot11fIEWFDIEOpaque *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x05} */ +typedef struct sDot11fIEWMMCaps { + uint8_t present; + uint8_t version /* Must be 1! */; + uint8_t reserved:4; + uint8_t qack:1; + uint8_t queue_request:1; + uint8_t txop_request:1; + uint8_t more_ack:1; +} tDot11fIEWMMCaps; + +#define DOT11F_EID_WMMCAPS (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WMMCAPS_MIN_LEN (7) + +#define DOT11F_IE_WMMCAPS_MAX_LEN (7) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wmm_caps( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWMMCaps*, + bool); + +uint32_t dot11f_pack_ie_wmm_caps( + tpAniSirGlobal, + tDot11fIEWMMCaps *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WMMCaps( + tpAniSirGlobal, + tDot11fIEWMMCaps *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x00} */ +typedef struct sDot11fIEWMMInfoAp { + uint8_t present; + uint8_t version; + uint8_t param_set_count:4; + uint8_t reserved:3; + uint8_t uapsd:1; +} tDot11fIEWMMInfoAp; + +#define DOT11F_EID_WMMINFOAP (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WMMINFOAP_MIN_LEN (7) + +#define DOT11F_IE_WMMINFOAP_MAX_LEN (7) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wmm_info_ap( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWMMInfoAp*, + bool); + +uint32_t dot11f_pack_ie_wmm_info_ap( + tpAniSirGlobal, + tDot11fIEWMMInfoAp *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WMMInfoAp( + tpAniSirGlobal, + tDot11fIEWMMInfoAp *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x00} */ +typedef struct sDot11fIEWMMInfoStation { + uint8_t present; + uint8_t version; + uint8_t acvo_uapsd:1; + uint8_t acvi_uapsd:1; + uint8_t acbk_uapsd:1; + uint8_t acbe_uapsd:1; + uint8_t reserved1:1; + uint8_t max_sp_length:2; + uint8_t reserved2:1; +} tDot11fIEWMMInfoStation; + +#define DOT11F_EID_WMMINFOSTATION (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WMMINFOSTATION_MIN_LEN (7) + +#define DOT11F_IE_WMMINFOSTATION_MAX_LEN (7) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wmm_info_station( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWMMInfoStation*, + bool); + +uint32_t dot11f_pack_ie_wmm_info_station( + tpAniSirGlobal, + tDot11fIEWMMInfoStation *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WMMInfoStation( + tpAniSirGlobal, + tDot11fIEWMMInfoStation *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x01} */ +typedef struct sDot11fIEWMMParams { + uint8_t present; + uint8_t version /* Must be 1! */; + uint8_t qosInfo; + uint8_t reserved2; + uint8_t acbe_aifsn:4; + uint8_t acbe_acm:1; + uint8_t acbe_aci:2; + uint8_t unused1:1; + uint8_t acbe_acwmin:4; + uint8_t acbe_acwmax:4; + uint16_t acbe_txoplimit; + uint8_t acbk_aifsn:4; + uint8_t acbk_acm:1; + uint8_t acbk_aci:2; + uint8_t unused2:1; + uint8_t acbk_acwmin:4; + uint8_t acbk_acwmax:4; + uint16_t acbk_txoplimit; + uint8_t acvi_aifsn:4; + uint8_t acvi_acm:1; + uint8_t acvi_aci:2; + uint8_t unused3:1; + uint8_t acvi_acwmin:4; + uint8_t acvi_acwmax:4; + uint16_t acvi_txoplimit; + uint8_t acvo_aifsn:4; + uint8_t acvo_acm:1; + uint8_t acvo_aci:2; + uint8_t unused4:1; + uint8_t acvo_acwmin:4; + uint8_t acvo_acwmax:4; + uint16_t acvo_txoplimit; +} tDot11fIEWMMParams; + +#define DOT11F_EID_WMMPARAMS (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WMMPARAMS_MIN_LEN (24) + +#define DOT11F_IE_WMMPARAMS_MAX_LEN (24) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wmm_params( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWMMParams*, + bool); + +uint32_t dot11f_pack_ie_wmm_params( + tpAniSirGlobal, + tDot11fIEWMMParams *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WMMParams( + tpAniSirGlobal, + tDot11fIEWMMParams *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x01} */ +typedef struct sDot11fIEWPA { + uint8_t present; + uint16_t version /* Must be 1! */; + /* field added to fix the bug in dot11fPackIEWPA */ + uint8_t multicast_cipher_present; + uint8_t multicast_cipher[4]; + uint16_t unicast_cipher_count; + uint8_t unicast_ciphers[4][4]; + uint16_t auth_suite_count; + uint8_t auth_suites[4][4]; + uint16_t caps; +} tDot11fIEWPA; + +#define DOT11F_EID_WPA (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WPA_MIN_LEN (6) + +#define DOT11F_IE_WPA_MAX_LEN (48) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wpa( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWPA*, + bool); + +uint32_t dot11f_pack_ie_wpa( + tpAniSirGlobal, + tDot11fIEWPA *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iewpa( + tpAniSirGlobal, + tDot11fIEWPA *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x01} */ +typedef struct sDot11fIEWPAOpaque { + uint8_t present; + uint8_t num_data; + uint8_t data[249]; +} tDot11fIEWPAOpaque; + +#define DOT11F_EID_WPAOPAQUE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WPAOPAQUE_MIN_LEN (6) + +#define DOT11F_IE_WPAOPAQUE_MAX_LEN (253) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wpa_opaque( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWPAOpaque*, + bool); + +uint32_t dot11f_pack_ie_wpa_opaque( + tpAniSirGlobal, + tDot11fIEWPAOpaque *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iewpaOpaque( + tpAniSirGlobal, + tDot11fIEWPAOpaque *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x04} (Multi-IE) */ +typedef struct sDot11fIEWSC { + uint8_t present; + tDot11fTLVVersion Version; + tDot11fTLVWPSState WPSState; + tDot11fTLVAPSetupLocked APSetupLocked; + tDot11fTLVSelectedRegistrarConfigMethods SelectedRegistrarConfigMethods; + tDot11fTLVUUID_E UUID_E; + tDot11fTLVUUID_R UUID_R; + tDot11fTLVRFBands RFBands; + tDot11fTLVSelectedRegistrar SelectedRegistrar; + tDot11fTLVConfigMethods ConfigMethods; + tDot11fTLVAssociationState AssociationState; + tDot11fTLVConfigurationError ConfigurationError; + tDot11fTLVManufacturer Manufacturer; + tDot11fTLVModelName ModelName; + tDot11fTLVModelNumber ModelNumber; + tDot11fTLVSerialNumber SerialNumber; + tDot11fTLVDeviceName DeviceName; + tDot11fTLVDevicePasswordID DevicePasswordID; + tDot11fTLVPrimaryDeviceType PrimaryDeviceType; + tDot11fTLVRequestType RequestType; + tDot11fTLVResponseType ResponseType; + tDot11fTLVVendorExtension VendorExtension; + tDot11fTLVRequestDeviceType RequestDeviceType; +} tDot11fIEWSC; + +#define DOT11F_EID_WSC (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WSC_MIN_LEN (4) + +#define DOT11F_IE_WSC_MAX_LEN (366) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wsc( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWSC*, + bool); + +uint32_t dot11f_pack_ie_wsc( + tpAniSirGlobal, + tDot11fIEWSC *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_iewsc( + tpAniSirGlobal, + tDot11fIEWSC *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x04} (Multi-IE) */ +typedef struct sDot11fIEWscAssocReq { + uint8_t present; + tDot11fTLVVersion Version; + tDot11fTLVRequestType RequestType; + tDot11fTLVVendorExtension VendorExtension; +} tDot11fIEWscAssocReq; + +#define DOT11F_EID_WSCASSOCREQ (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WSCASSOCREQ_MIN_LEN (4) + +#define DOT11F_IE_WSCASSOCREQ_MAX_LEN (35) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wsc_assoc_req( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWscAssocReq*, + bool); + +uint32_t dot11f_pack_ie_wsc_assoc_req( + tpAniSirGlobal, + tDot11fIEWscAssocReq *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_wsc_assoc_req( + tpAniSirGlobal, + tDot11fIEWscAssocReq *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x04} (Multi-IE) */ +typedef struct sDot11fIEWscAssocRes { + uint8_t present; + tDot11fTLVVersion Version; + tDot11fTLVResponseType ResponseType; + tDot11fTLVVendorExtension VendorExtension; +} tDot11fIEWscAssocRes; + +#define DOT11F_EID_WSCASSOCRES (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WSCASSOCRES_MIN_LEN (4) + +#define DOT11F_IE_WSCASSOCRES_MAX_LEN (35) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wsc_assoc_res( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWscAssocRes*, + bool); + +uint32_t dot11f_pack_ie_wsc_assoc_res( + tpAniSirGlobal, + tDot11fIEWscAssocRes *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_wsc_assoc_res( + tpAniSirGlobal, + tDot11fIEWscAssocRes *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x04} (Multi-IE) */ +typedef struct sDot11fIEWscBeacon { + uint8_t present; + tDot11fTLVVersion Version; + tDot11fTLVWPSState WPSState; + tDot11fTLVAPSetupLocked APSetupLocked; + tDot11fTLVSelectedRegistrar SelectedRegistrar; + tDot11fTLVDevicePasswordID DevicePasswordID; + tDot11fTLVSelectedRegistrarConfigMethods SelectedRegistrarConfigMethods; + tDot11fTLVUUID_E UUID_E; + tDot11fTLVRFBands RFBands; + tDot11fTLVVendorExtension VendorExtension; +} tDot11fIEWscBeacon; + +#define DOT11F_EID_WSCBEACON (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WSCBEACON_MIN_LEN (4) + +#define DOT11F_IE_WSCBEACON_MAX_LEN (82) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wsc_beacon( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWscBeacon*, + bool); + +uint32_t dot11f_pack_ie_wsc_beacon( + tpAniSirGlobal, + tDot11fIEWscBeacon *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_wsc_beacon( + tpAniSirGlobal, + tDot11fIEWscBeacon *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x04} (Multi-IE) */ +typedef struct sDot11fIEWscBeaconProbeRes { + uint8_t present; + tDot11fTLVVersion Version; + tDot11fTLVWPSState WPSState; + tDot11fTLVAPSetupLocked APSetupLocked; + tDot11fTLVSelectedRegistrar SelectedRegistrar; + tDot11fTLVDevicePasswordID DevicePasswordID; + tDot11fTLVSelectedRegistrarConfigMethods SelectedRegistrarConfigMethods; + tDot11fTLVResponseType ResponseType; + tDot11fTLVUUID_E UUID_E; + tDot11fTLVManufacturer Manufacturer; + tDot11fTLVModelName ModelName; + tDot11fTLVModelNumber ModelNumber; + tDot11fTLVSerialNumber SerialNumber; + tDot11fTLVPrimaryDeviceType PrimaryDeviceType; + tDot11fTLVDeviceName DeviceName; + tDot11fTLVConfigMethods ConfigMethods; + tDot11fTLVRFBands RFBands; + tDot11fTLVVendorExtension VendorExtension; +} tDot11fIEWscBeaconProbeRes; + +#define DOT11F_EID_WSCBEACONPROBERES (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WSCBEACONPROBERES_MIN_LEN (4) + +#define DOT11F_IE_WSCBEACONPROBERES_MAX_LEN (317) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wsc_beacon_probe_res( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWscBeaconProbeRes*, + bool); + +uint32_t dot11f_pack_ie_wsc_beacon_probe_res( + tpAniSirGlobal, + tDot11fIEWscBeaconProbeRes *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_wsc_beacon_probe_res( + tpAniSirGlobal, + tDot11fIEWscBeaconProbeRes *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x04} */ +typedef struct sDot11fIEWscIEOpaque { + uint8_t present; + uint8_t num_data; + uint8_t data[249]; +} tDot11fIEWscIEOpaque; + +#define DOT11F_EID_WSCIEOPAQUE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WSCIEOPAQUE_MIN_LEN (6) + +#define DOT11F_IE_WSCIEOPAQUE_MAX_LEN (253) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wsc_ie_opaque( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWscIEOpaque*, + bool); + +uint32_t dot11f_pack_ie_wsc_ie_opaque( + tpAniSirGlobal, + tDot11fIEWscIEOpaque *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_WscIEOpaque( + tpAniSirGlobal, + tDot11fIEWscIEOpaque *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x04} (Multi-IE) */ +typedef struct sDot11fIEWscProbeReq { + uint8_t present; + tDot11fTLVVersion Version; + tDot11fTLVRequestType RequestType; + tDot11fTLVConfigMethods ConfigMethods; + tDot11fTLVUUID_E UUID_E; + tDot11fTLVPrimaryDeviceType PrimaryDeviceType; + tDot11fTLVRFBands RFBands; + tDot11fTLVAssociationState AssociationState; + tDot11fTLVConfigurationError ConfigurationError; + tDot11fTLVDevicePasswordID DevicePasswordID; + tDot11fTLVManufacturer Manufacturer; + tDot11fTLVModelName ModelName; + tDot11fTLVModelNumber ModelNumber; + tDot11fTLVDeviceName DeviceName; + tDot11fTLVVendorExtension VendorExtension; + tDot11fTLVRequestDeviceType RequestDeviceType; +} tDot11fIEWscProbeReq; + +#define DOT11F_EID_WSCPROBEREQ (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WSCPROBEREQ_MIN_LEN (4) + +#define DOT11F_IE_WSCPROBEREQ_MAX_LEN (284) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wsc_probe_req( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWscProbeReq*, + bool); + +uint32_t dot11f_pack_ie_wsc_probe_req( + tpAniSirGlobal, + tDot11fIEWscProbeReq *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_wsc_probe_req( + tpAniSirGlobal, + tDot11fIEWscProbeReq *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x04} (Multi-IE) */ +typedef struct sDot11fIEWscProbeRes { + uint8_t present; + tDot11fTLVVersion Version; + tDot11fTLVWPSState WPSState; + tDot11fTLVAPSetupLocked APSetupLocked; + tDot11fTLVSelectedRegistrar SelectedRegistrar; + tDot11fTLVDevicePasswordID DevicePasswordID; + tDot11fTLVSelectedRegistrarConfigMethods SelectedRegistrarConfigMethods; + tDot11fTLVResponseType ResponseType; + tDot11fTLVUUID_E UUID_E; + tDot11fTLVManufacturer Manufacturer; + tDot11fTLVModelName ModelName; + tDot11fTLVModelNumber ModelNumber; + tDot11fTLVSerialNumber SerialNumber; + tDot11fTLVPrimaryDeviceType PrimaryDeviceType; + tDot11fTLVDeviceName DeviceName; + tDot11fTLVConfigMethods ConfigMethods; + tDot11fTLVRFBands RFBands; + tDot11fTLVVendorExtension VendorExtension; +} tDot11fIEWscProbeRes; + +#define DOT11F_EID_WSCPROBERES (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WSCPROBERES_MIN_LEN (4) + +#define DOT11F_IE_WSCPROBERES_MAX_LEN (317) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wsc_probe_res( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWscProbeRes*, + bool); + +uint32_t dot11f_pack_ie_wsc_probe_res( + tpAniSirGlobal, + tDot11fIEWscProbeRes *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_wsc_probe_res( + tpAniSirGlobal, + tDot11fIEWscProbeRes *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x04} (Multi-IE) */ +typedef struct sDot11fIEWscReassocRes { + uint8_t present; + tDot11fTLVVersion Version; + tDot11fTLVResponseType ResponseType; + tDot11fTLVVendorExtension VendorExtension; +} tDot11fIEWscReassocRes; + +#define DOT11F_EID_WSCREASSOCRES (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_WSCREASSOCRES_MIN_LEN (4) + +#define DOT11F_IE_WSCREASSOCRES_MAX_LEN (35) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_wsc_reassoc_res( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEWscReassocRes*, + bool); + +uint32_t dot11f_pack_ie_wsc_reassoc_res( + tpAniSirGlobal, + tDot11fIEWscReassocRes *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_wsc_reassoc_res( + tpAniSirGlobal, + tDot11fIEWscReassocRes *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 159 (0x9f) */ +typedef struct sDot11fIEaddba_extn_element { + uint8_t present; + uint8_t no_fragmentation:1; + uint8_t he_frag_operation:2; + uint8_t reserved:5; +} tDot11fIEaddba_extn_element; + +#define DOT11F_EID_ADDBA_EXTN_ELEMENT (159) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ADDBA_EXTN_ELEMENT_MIN_LEN (1) + +#define DOT11F_IE_ADDBA_EXTN_ELEMENT_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_addba_extn_element( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEaddba_extn_element*, + bool); + +uint32_t dot11f_pack_ie_addba_extn_element( + tpAniSirGlobal, + tDot11fIEaddba_extn_element *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_addba_extn_element( + tpAniSirGlobal, + tDot11fIEaddba_extn_element *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 42 (0x2a) */ +typedef struct sDot11fIEbss_color_change { + uint8_t present; + uint8_t countdown; + uint8_t new_color:6; + uint8_t reserved:2; +} tDot11fIEbss_color_change; + +#define DOT11F_EID_BSS_COLOR_CHANGE (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_BSS_COLOR_CHANGE_MIN_LEN (2) + +#define DOT11F_IE_BSS_COLOR_CHANGE_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_bss_color_change( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEbss_color_change*, + bool); + +uint32_t dot11f_pack_ie_bss_color_change( + tpAniSirGlobal, + tDot11fIEbss_color_change *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_bss_color_change( + tpAniSirGlobal, + tDot11fIEbss_color_change *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 32 (0x20) */ +typedef struct sDot11fIEdh_parameter_element { + uint8_t present; + uint8_t group[2]; + uint8_t num_public_key; + uint8_t public_key[255]; +} tDot11fIEdh_parameter_element; + +#define DOT11F_EID_DH_PARAMETER_ELEMENT (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_DH_PARAMETER_ELEMENT_MIN_LEN (2) + +#define DOT11F_IE_DH_PARAMETER_ELEMENT_MAX_LEN (257) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_dh_parameter_element( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEdh_parameter_element*, + bool); + +uint32_t dot11f_pack_ie_dh_parameter_element( + tpAniSirGlobal, + tDot11fIEdh_parameter_element *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_dh_parameter_element( + tpAniSirGlobal, + tDot11fIEdh_parameter_element *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 11 (0x0b) */ +typedef struct sDot11fIEesp_information { + uint8_t present; + uint8_t num_data; + uint8_t data[96]; +} tDot11fIEesp_information; + +#define DOT11F_EID_ESP_INFORMATION (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ESP_INFORMATION_MIN_LEN (0) + +#define DOT11F_IE_ESP_INFORMATION_MAX_LEN (96) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_esp_information( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEesp_information*, + bool); + +uint32_t dot11f_pack_ie_esp_information( + tpAniSirGlobal, + tDot11fIEesp_information *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_esp_information( + tpAniSirGlobal, + tDot11fIEesp_information *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 60 (0x3c) */ +typedef struct sDot11fIEext_chan_switch_ann { + uint8_t present; + uint8_t switch_mode; + uint8_t new_reg_class; + uint8_t new_channel; + uint8_t switch_count; +} tDot11fIEext_chan_switch_ann; + +#define DOT11F_EID_EXT_CHAN_SWITCH_ANN (60) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_EXT_CHAN_SWITCH_ANN_MIN_LEN (4) + +#define DOT11F_IE_EXT_CHAN_SWITCH_ANN_MAX_LEN (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ext_chan_switch_ann( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEext_chan_switch_ann*, + bool); + +uint32_t dot11f_pack_ie_ext_chan_switch_ann( + tpAniSirGlobal, + tDot11fIEext_chan_switch_ann *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ext_chan_switch_ann( + tpAniSirGlobal, + tDot11fIEext_chan_switch_ann *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 1 (0x01) */ +typedef struct sDot11fIEfils_assoc_delay_info { + uint8_t present; + uint8_t assoc_delay_info; +} tDot11fIEfils_assoc_delay_info; + +#define DOT11F_EID_FILS_ASSOC_DELAY_INFO (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FILS_ASSOC_DELAY_INFO_MIN_LEN (1) + +#define DOT11F_IE_FILS_ASSOC_DELAY_INFO_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fils_assoc_delay_info( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfils_assoc_delay_info*, + bool); + +uint32_t dot11f_pack_ie_fils_assoc_delay_info( + tpAniSirGlobal, + tDot11fIEfils_assoc_delay_info *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fils_assoc_delay_info( + tpAniSirGlobal, + tDot11fIEfils_assoc_delay_info *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 5 (0x05) */ +typedef struct sDot11fIEfils_hlp_container { + uint8_t present; + uint8_t dest_mac[6]; + uint8_t src_mac[6]; + uint8_t num_hlp_packet; + uint8_t hlp_packet[255]; +} tDot11fIEfils_hlp_container; + +#define DOT11F_EID_FILS_HLP_CONTAINER (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FILS_HLP_CONTAINER_MIN_LEN (12) + +#define DOT11F_IE_FILS_HLP_CONTAINER_MAX_LEN (267) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fils_hlp_container( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfils_hlp_container*, + bool); + +uint32_t dot11f_pack_ie_fils_hlp_container( + tpAniSirGlobal, + tDot11fIEfils_hlp_container *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fils_hlp_container( + tpAniSirGlobal, + tDot11fIEfils_hlp_container *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 240 (0xf0) */ +typedef struct sDot11fIEfils_indication { + uint8_t present; + uint16_t public_key_identifiers_cnt:3; + uint16_t realm_identifiers_cnt:3; + uint16_t is_ip_config_supported:1; + uint16_t is_cache_id_present:1; + uint16_t is_hessid_present:1; + uint16_t is_fils_sk_auth_supported:1; + uint16_t is_fils_sk_auth_pfs_supported:1; + uint16_t is_pk_auth_supported:1; + uint16_t reserved:4; + uint8_t num_variable_data; + uint8_t variable_data[255]; +} tDot11fIEfils_indication; + +#define DOT11F_EID_FILS_INDICATION (240) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FILS_INDICATION_MIN_LEN (4) + +#define DOT11F_IE_FILS_INDICATION_MAX_LEN (257) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fils_indication( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfils_indication*, + bool); + +uint32_t dot11f_pack_ie_fils_indication( + tpAniSirGlobal, + tDot11fIEfils_indication *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fils_indication( + tpAniSirGlobal, + tDot11fIEfils_indication *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 7 (0x07) */ +typedef struct sDot11fIEfils_kde { + uint8_t present; + uint8_t key_rsc[8]; + uint8_t num_kde_list; + uint8_t kde_list[255]; +} tDot11fIEfils_kde; + +#define DOT11F_EID_FILS_KDE (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FILS_KDE_MIN_LEN (8) + +#define DOT11F_IE_FILS_KDE_MAX_LEN (263) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fils_kde( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfils_kde*, + bool); + +uint32_t dot11f_pack_ie_fils_kde( + tpAniSirGlobal, + tDot11fIEfils_kde *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fils_kde( + tpAniSirGlobal, + tDot11fIEfils_kde *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 3 (0x03) */ +typedef struct sDot11fIEfils_key_confirmation { + uint8_t present; + uint8_t num_key_auth; + uint8_t key_auth[255]; +} tDot11fIEfils_key_confirmation; + +#define DOT11F_EID_FILS_KEY_CONFIRMATION (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FILS_KEY_CONFIRMATION_MIN_LEN (0) + +#define DOT11F_IE_FILS_KEY_CONFIRMATION_MAX_LEN (255) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fils_key_confirmation( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfils_key_confirmation*, + bool); + +uint32_t dot11f_pack_ie_fils_key_confirmation( + tpAniSirGlobal, + tDot11fIEfils_key_confirmation *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fils_key_confirmation( + tpAniSirGlobal, + tDot11fIEfils_key_confirmation *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 13 (0x0d) */ +typedef struct sDot11fIEfils_nonce { + uint8_t present; + uint8_t nonce[16]; +} tDot11fIEfils_nonce; + +#define DOT11F_EID_FILS_NONCE (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FILS_NONCE_MIN_LEN (16) + +#define DOT11F_IE_FILS_NONCE_MAX_LEN (16) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fils_nonce( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfils_nonce*, + bool); + +uint32_t dot11f_pack_ie_fils_nonce( + tpAniSirGlobal, + tDot11fIEfils_nonce *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fils_nonce( + tpAniSirGlobal, + tDot11fIEfils_nonce *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 12 (0x0c) */ +typedef struct sDot11fIEfils_public_key { + uint8_t present; + uint8_t key_type; + uint8_t num_public_key; + uint8_t public_key[255]; +} tDot11fIEfils_public_key; + +#define DOT11F_EID_FILS_PUBLIC_KEY (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FILS_PUBLIC_KEY_MIN_LEN (1) + +#define DOT11F_IE_FILS_PUBLIC_KEY_MAX_LEN (256) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fils_public_key( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfils_public_key*, + bool); + +uint32_t dot11f_pack_ie_fils_public_key( + tpAniSirGlobal, + tDot11fIEfils_public_key *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fils_public_key( + tpAniSirGlobal, + tDot11fIEfils_public_key *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 4 (0x04) */ +typedef struct sDot11fIEfils_session { + uint8_t present; + uint8_t session[8]; +} tDot11fIEfils_session; + +#define DOT11F_EID_FILS_SESSION (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FILS_SESSION_MIN_LEN (8) + +#define DOT11F_IE_FILS_SESSION_MAX_LEN (8) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fils_session( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfils_session*, + bool); + +uint32_t dot11f_pack_ie_fils_session( + tpAniSirGlobal, + tDot11fIEfils_session *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fils_session( + tpAniSirGlobal, + tDot11fIEfils_session *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 8 (0x08) */ +typedef struct sDot11fIEfils_wrapped_data { + uint8_t present; + uint8_t num_wrapped_data; + uint8_t wrapped_data[255]; +} tDot11fIEfils_wrapped_data; + +#define DOT11F_EID_FILS_WRAPPED_DATA (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FILS_WRAPPED_DATA_MIN_LEN (0) + +#define DOT11F_IE_FILS_WRAPPED_DATA_MAX_LEN (255) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fils_wrapped_data( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfils_wrapped_data*, + bool); + +uint32_t dot11f_pack_ie_fils_wrapped_data( + tpAniSirGlobal, + tDot11fIEfils_wrapped_data *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fils_wrapped_data( + tpAniSirGlobal, + tDot11fIEfils_wrapped_data *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 242 (0xf2) */ +typedef struct sDot11fIEfragment_ie { + uint8_t present; + uint8_t num_data; + uint8_t data[255]; +} tDot11fIEfragment_ie; + +#define DOT11F_EID_FRAGMENT_IE (242) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_FRAGMENT_IE_MIN_LEN (0) + +#define DOT11F_IE_FRAGMENT_IE_MAX_LEN (255) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_fragment_ie( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEfragment_ie*, + bool); + +uint32_t dot11f_pack_ie_fragment_ie( + tpAniSirGlobal, + tDot11fIEfragment_ie *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_fragment_ie( + tpAniSirGlobal, + tDot11fIEfragment_ie *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 59 (0x3b) */ +typedef struct sDot11fIEhe_6ghz_band_cap { + uint8_t present; + uint16_t min_mpdu_start_spacing:3; + uint16_t max_ampdu_len_exp:3; + uint16_t max_mpdu_len:3; + uint16_t sm_pow_save:2; + uint16_t rd_responder:1; + uint16_t rx_ant_pattern_consistency:1; + uint16_t tx_ant_pattern_consistency:1; + uint16_t reserved:2; +} tDot11fIEhe_6ghz_band_cap; + +#define DOT11F_EID_HE_6GHZ_BAND_CAP (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_HE_6GHZ_BAND_CAP_MIN_LEN (2) + +#define DOT11F_IE_HE_6GHZ_BAND_CAP_MAX_LEN (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_he_6ghz_band_cap( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEhe_6ghz_band_cap*, + bool); + +uint32_t dot11f_pack_ie_he_6ghz_band_cap( + tpAniSirGlobal, + tDot11fIEhe_6ghz_band_cap *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_he_6ghz_band_cap( + tpAniSirGlobal, + tDot11fIEhe_6ghz_band_cap *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 35 (0x23) */ +typedef struct sDot11fIEhe_cap { + uint8_t present; + uint32_t htc_he:1; + uint32_t twt_request:1; + uint32_t twt_responder:1; + uint32_t fragmentation:2; + uint32_t max_num_frag_msdu_amsdu_exp:3; + uint32_t min_frag_size:2; + uint32_t trigger_frm_mac_pad:2; + uint32_t multi_tid_aggr_rx_supp:3; + uint32_t he_link_adaptation:2; + uint32_t all_ack:1; + uint32_t trigd_rsp_sched:1; + uint32_t a_bsr:1; + uint32_t broadcast_twt:1; + uint32_t ba_32bit_bitmap:1; + uint32_t mu_cascade:1; + uint32_t ack_enabled_multitid:1; + uint32_t reserved:1; + uint32_t omi_a_ctrl:1; + uint32_t ofdma_ra:1; + uint32_t max_ampdu_len_exp_ext:2; + uint32_t amsdu_frag:1; + uint32_t flex_twt_sched:1; + uint32_t rx_ctrl_frame:1; + uint16_t bsrp_ampdu_aggr:1; + uint16_t qtp:1; + uint16_t a_bqr:1; + uint16_t spatial_reuse_param_rspder:1; + uint16_t ndp_feedback_supp:1; + uint16_t ops_supp:1; + uint16_t amsdu_in_ampdu:1; + uint16_t multi_tid_aggr_tx_supp:3; + uint16_t he_sub_ch_sel_tx_supp:1; + uint16_t ul_2x996_tone_ru_supp:1; + uint16_t om_ctrl_ul_mu_data_dis_rx:1; + uint16_t he_dynamic_smps:1; + uint16_t punctured_sounding_supp:1; + uint16_t ht_vht_trg_frm_rx_supp:1; + uint32_t reserved2:1; + uint32_t chan_width_0:1; + uint32_t chan_width_1:1; + uint32_t chan_width_2:1; + uint32_t chan_width_3:1; + uint32_t chan_width_4:1; + uint32_t chan_width_5:1; + uint32_t chan_width_6:1; + uint32_t rx_pream_puncturing:4; + uint32_t device_class:1; + uint32_t ldpc_coding:1; + uint32_t he_1x_ltf_800_gi_ppdu:1; + uint32_t midamble_tx_rx_max_nsts:2; + uint32_t he_4x_ltf_3200_gi_ndp:1; + uint32_t tb_ppdu_tx_stbc_lt_80mhz:1; + uint32_t rx_stbc_lt_80mhz:1; + uint32_t doppler:2; + uint32_t ul_mu:2; + uint32_t dcm_enc_tx:3; + uint32_t dcm_enc_rx:3; + uint32_t ul_he_mu:1; + uint32_t su_beamformer:1; + uint32_t su_beamformee:1; + uint32_t mu_beamformer:1; + uint32_t bfee_sts_lt_80:3; + uint32_t bfee_sts_gt_80:3; + uint32_t num_sounding_lt_80:3; + uint32_t num_sounding_gt_80:3; + uint32_t su_feedback_tone16:1; + uint32_t mu_feedback_tone16:1; + uint32_t codebook_su:1; + uint32_t codebook_mu:1; + uint32_t beamforming_feedback:3; + uint32_t he_er_su_ppdu:1; + uint32_t dl_mu_mimo_part_bw:1; + uint32_t ppet_present:1; + uint32_t srp:1; + uint32_t power_boost:1; + uint32_t he_ltf_800_gi_4x:1; + uint32_t max_nc:3; + uint32_t tb_ppdu_tx_stbc_gt_80mhz:1; + uint32_t rx_stbc_gt_80mhz:1; + uint16_t er_he_ltf_800_gi_4x:1; + uint16_t he_ppdu_20_in_40Mhz_2G:1; + uint16_t he_ppdu_20_in_160_80p80Mhz:1; + uint16_t he_ppdu_80_in_160_80p80Mhz:1; + uint16_t er_1x_he_ltf_gi:1; + uint16_t midamble_tx_rx_1x_he_ltf:1; + uint16_t dcm_max_bw:2; + uint16_t longer_than_16_he_sigb_ofdm_sym:1; + uint16_t non_trig_cqi_feedback:1; + uint16_t tx_1024_qam_lt_242_tone_ru:1; + uint16_t rx_1024_qam_lt_242_tone_ru:1; + uint16_t rx_full_bw_su_he_mu_compress_sigb:1; + uint16_t rx_full_bw_su_he_mu_non_cmpr_sigb:1; + uint16_t reserved3:2; + uint8_t reserved4; + uint16_t rx_he_mcs_map_lt_80; + uint16_t tx_he_mcs_map_lt_80; + uint8_t rx_he_mcs_map_160[1][2]; + uint8_t tx_he_mcs_map_160[1][2]; + uint8_t rx_he_mcs_map_80_80[1][2]; + uint8_t tx_he_mcs_map_80_80[1][2]; + union { + struct { + uint8_t num_ppe_th; + uint8_t ppe_th[25]; + } ppe_threshold; /* ppet_present = 1 */ + } ppet; +} tDot11fIEhe_cap; + +#define DOT11F_EID_HE_CAP (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_HE_CAP_MIN_LEN (21) + +#define DOT11F_IE_HE_CAP_MAX_LEN (54) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_he_cap( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEhe_cap*, + bool); + +uint32_t dot11f_pack_ie_he_cap( + tpAniSirGlobal, + tDot11fIEhe_cap *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_he_cap( + tpAniSirGlobal, + tDot11fIEhe_cap *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 36 (0x24) */ +typedef struct sDot11fIEhe_op { + uint8_t present; + uint16_t default_pe:3; + uint16_t twt_required:1; + uint16_t txop_rts_threshold:10; + uint16_t vht_oper_present:1; + uint16_t co_located_bss:1; + uint8_t er_su_disable:1; + uint8_t oper_info_6g_present:1; + uint8_t reserved2:6; + uint8_t bss_color:6; + uint8_t partial_bss_col:1; + uint8_t bss_col_disabled:1; + uint8_t basic_mcs_nss[2]; + union { + struct { + uint8_t chan_width; + uint8_t center_freq_seg0; + uint8_t center_freq_seg1; + } info; /* vht_oper_present = 1 */ + } vht_oper; + union { + struct { + uint8_t data; + } info; /* co_located_bss = 1 */ + } maxbssid_ind; + union { + struct { + uint8_t primary_ch; + uint8_t ch_width:2; + uint8_t dup_bcon:1; + uint8_t reserved:5; + uint8_t center_freq_seg0; + uint8_t center_freq_seg1; + uint8_t min_rate; + } info; /* oper_info_6g_present = 1 */ + } oper_info_6g; +} tDot11fIEhe_op; + +#define DOT11F_EID_HE_OP (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_HE_OP_MIN_LEN (6) + +#define DOT11F_IE_HE_OP_MAX_LEN (15) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_he_op( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEhe_op*, + bool); + +uint32_t dot11f_pack_ie_he_op( + tpAniSirGlobal, + tDot11fIEhe_op *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_he_op( + tpAniSirGlobal, + tDot11fIEhe_op *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x10} */ +typedef struct sDot11fIEhs20vendor_ie { + uint8_t present; + uint8_t dgaf_dis:1; + uint8_t hs_id_present:2; + uint8_t reserved:1; + uint8_t release_num:4; + union { + struct { + uint16_t pps_mo_id; + } pps_mo; /* hs_id_present = 1 */ + struct { + uint16_t anqp_domain_id; + } anqp_domain; /* hs_id_present = 2 */ + } hs_id; +} tDot11fIEhs20vendor_ie; + +#define DOT11F_EID_HS20VENDOR_IE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_HS20VENDOR_IE_MIN_LEN (5) + +#define DOT11F_IE_HS20VENDOR_IE_MAX_LEN (7) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_hs20vendor_ie( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEhs20vendor_ie*, + bool); + +uint32_t dot11f_pack_ie_hs20vendor_ie( + tpAniSirGlobal, + tDot11fIEhs20vendor_ie *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_hs20vendor_ie( + tpAniSirGlobal, + tDot11fIEhs20vendor_ie *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 72 (0x48) */ +typedef struct sDot11fIEht2040_bss_coexistence { + uint8_t present; + uint8_t info_request:1; + uint8_t forty_mhz_intolerant:1; + uint8_t twenty_mhz_bsswidth_req:1; + uint8_t obss_scan_exemption_req:1; + uint8_t obss_scan_exemption_grant:1; + uint8_t unused:3; +} tDot11fIEht2040_bss_coexistence; + +#define DOT11F_EID_HT2040_BSS_COEXISTENCE (72) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_HT2040_BSS_COEXISTENCE_MIN_LEN (1) + +#define DOT11F_IE_HT2040_BSS_COEXISTENCE_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ht2040_bss_coexistence( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEht2040_bss_coexistence*, + bool); + +uint32_t dot11f_pack_ie_ht2040_bss_coexistence( + tpAniSirGlobal, + tDot11fIEht2040_bss_coexistence *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ht2040_bss_coexistence( + tpAniSirGlobal, + tDot11fIEht2040_bss_coexistence *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 73 (0x49) */ +typedef struct sDot11fIEht2040_bss_intolerant_report { + uint8_t present; + uint8_t operating_class; + uint8_t num_channel_list; + uint8_t channel_list[50]; +} tDot11fIEht2040_bss_intolerant_report; + +#define DOT11F_EID_HT2040_BSS_INTOLERANT_REPORT (73) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_HT2040_BSS_INTOLERANT_REPORT_MIN_LEN (1) + +#define DOT11F_IE_HT2040_BSS_INTOLERANT_REPORT_MAX_LEN (51) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_ht2040_bss_intolerant_report( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEht2040_bss_intolerant_report*, + bool); + +uint32_t dot11f_pack_ie_ht2040_bss_intolerant_report( + tpAniSirGlobal, + tDot11fIEht2040_bss_intolerant_report *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_ht2040_bss_intolerant_report( + tpAniSirGlobal, + tDot11fIEht2040_bss_intolerant_report *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 255 (0xff) Extended EID 38 (0x26) */ +typedef struct sDot11fIEmu_edca_param_set { + uint8_t present; + uint8_t qos; + uint8_t acbe_aifsn:4; + uint8_t acbe_acm:1; + uint8_t acbe_aci:2; + uint8_t unused1:1; + uint8_t acbe_acwmin:4; + uint8_t acbe_acwmax:4; + uint8_t acbe_muedca_timer; + uint8_t acbk_aifsn:4; + uint8_t acbk_acm:1; + uint8_t acbk_aci:2; + uint8_t unused2:1; + uint8_t acbk_acwmin:4; + uint8_t acbk_acwmax:4; + uint8_t acbk_muedca_timer; + uint8_t acvi_aifsn:4; + uint8_t acvi_acm:1; + uint8_t acvi_aci:2; + uint8_t unused3:1; + uint8_t acvi_acwmin:4; + uint8_t acvi_acwmax:4; + uint8_t acvi_muedca_timer; + uint8_t acvo_aifsn:4; + uint8_t acvo_acm:1; + uint8_t acvo_aci:2; + uint8_t unused4:1; + uint8_t acvo_acwmin:4; + uint8_t acvo_acwmax:4; + uint8_t acvo_muedca_timer; +} tDot11fIEmu_edca_param_set; + +#define DOT11F_EID_MU_EDCA_PARAM_SET (255) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_MU_EDCA_PARAM_SET_MIN_LEN (13) + +#define DOT11F_IE_MU_EDCA_PARAM_SET_MAX_LEN (13) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_mu_edca_param_set( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEmu_edca_param_set*, + bool); + +uint32_t dot11f_pack_ie_mu_edca_param_set( + tpAniSirGlobal, + tDot11fIEmu_edca_param_set *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_mu_edca_param_set( + tpAniSirGlobal, + tDot11fIEmu_edca_param_set *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x12} */ +typedef struct sDot11fIEosen_ie { + uint8_t present; + uint8_t num_data; + uint8_t data[255]; +} tDot11fIEosen_ie; + +#define DOT11F_EID_OSEN_IE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_OSEN_IE_MIN_LEN (4) + +#define DOT11F_IE_OSEN_IE_MAX_LEN (259) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_osen_ie( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEosen_ie*, + bool); + +uint32_t dot11f_pack_ie_osen_ie( + tpAniSirGlobal, + tDot11fIEosen_ie *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_osen_ie( + tpAniSirGlobal, + tDot11fIEosen_ie *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x8c, 0xfd, 0xf0, 0x01} */ +typedef struct sDot11fIEqcn_ie { + uint8_t present; + tDot11fIEversion_attr version_attr; + tDot11fIEvht_mcs11_attr vht_mcs11_attr; +} tDot11fIEqcn_ie; + +#define DOT11F_EID_QCN_IE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_QCN_IE_MIN_LEN (4) + +#define DOT11F_IE_QCN_IE_MAX_LEN (11) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_qcn_ie( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEqcn_ie*, + bool); + +uint32_t dot11f_pack_ie_qcn_ie( + tpAniSirGlobal, + tDot11fIEqcn_ie *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_qcn_ie( + tpAniSirGlobal, + tDot11fIEqcn_ie *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x50, 0x6f, 0x9a, 0x1d} */ +typedef struct sDot11fIEroaming_consortium_sel { + uint8_t present; + uint8_t num_data; + uint8_t data[255]; +} tDot11fIEroaming_consortium_sel; + +#define DOT11F_EID_ROAMING_CONSORTIUM_SEL (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_ROAMING_CONSORTIUM_SEL_MIN_LEN (4) + +#define DOT11F_IE_ROAMING_CONSORTIUM_SEL_MAX_LEN (259) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_roaming_consortium_sel( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEroaming_consortium_sel*, + bool); + +uint32_t dot11f_pack_ie_roaming_consortium_sel( + tpAniSirGlobal, + tDot11fIEroaming_consortium_sel *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_roaming_consortium_sel( + tpAniSirGlobal, + tDot11fIEroaming_consortium_sel *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 62 (0x3e) */ +typedef struct sDot11fIEsec_chan_offset_ele { + uint8_t present; + uint8_t secondaryChannelOffset; +} tDot11fIEsec_chan_offset_ele; + +#define DOT11F_EID_SEC_CHAN_OFFSET_ELE (62) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_SEC_CHAN_OFFSET_ELE_MIN_LEN (1) + +#define DOT11F_IE_SEC_CHAN_OFFSET_ELE_MAX_LEN (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_sec_chan_offset_ele( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEsec_chan_offset_ele*, + bool); + +uint32_t dot11f_pack_ie_sec_chan_offset_ele( + tpAniSirGlobal, + tDot11fIEsec_chan_offset_ele *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_sec_chan_offset_ele( + tpAniSirGlobal, + tDot11fIEsec_chan_offset_ele *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ + +/* EID 221 (0xdd) {OUI 0x00, 0x90, 0x4c, 0x04} */ +typedef struct sDot11fIEvendor_vht_ie { + uint8_t present; + uint8_t sub_type; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEVHTOperation VHTOperation; +} tDot11fIEvendor_vht_ie; + +#define DOT11F_EID_VENDOR_VHT_IE (221) + +/* N.B. These #defines do *not* include the EID & length */ +#define DOT11F_IE_VENDOR_VHT_IE_MIN_LEN (5) + +#define DOT11F_IE_VENDOR_VHT_IE_MAX_LEN (26) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ +__must_check uint32_t dot11f_unpack_ie_vendor_vht_ie( + tpAniSirGlobal, + uint8_t *, + uint8_t, + tDot11fIEvendor_vht_ie*, + bool); + +uint32_t dot11f_pack_ie_vendor_vht_ie( + tpAniSirGlobal, + tDot11fIEvendor_vht_ie *, + uint8_t *, + uint32_t, + uint32_t*); + +uint32_t dot11f_get_packed_ie_vendor_vht_ie( + tpAniSirGlobal, + tDot11fIEvendor_vht_ie *, + uint32_t*); + +#ifdef __cplusplus +}; /* End extern "C". */ +#endif /* C++ */ +/************************************************************************ + * Frames + **********************************************************************/ + +typedef struct sDot11fAddTSRequest{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fIETSPEC TSPEC; + uint16_t num_TCLAS; + tDot11fIETCLAS TCLAS[2]; + tDot11fIETCLASSPROC TCLASSPROC; + tDot11fIEWMMTSPEC WMMTSPEC; + uint16_t num_WMMTCLAS; + tDot11fIEWMMTCLAS WMMTCLAS[2]; + tDot11fIEWMMTCLASPROC WMMTCLASPROC; + tDot11fIEESETrafStrmRateSet ESETrafStrmRateSet; +} tDot11fAddTSRequest; + +#define DOT11F_ADDTSREQUEST (1) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_add_ts_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAddTSRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_add_ts_request(tpAniSirGlobal pCtx, + tDot11fAddTSRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_add_ts_request_size(tpAniSirGlobal pCtx, + tDot11fAddTSRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fAddTSResponse{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfStatus Status; + tDot11fIETSDelay TSDelay; + tDot11fIETSPEC TSPEC; + uint16_t num_TCLAS; + tDot11fIETCLAS TCLAS[2]; + tDot11fIETCLASSPROC TCLASSPROC; + tDot11fIESchedule Schedule; + tDot11fIEWMMTSDelay WMMTSDelay; + tDot11fIEWMMSchedule WMMSchedule; + tDot11fIEWMMTSPEC WMMTSPEC; + uint16_t num_WMMTCLAS; + tDot11fIEWMMTCLAS WMMTCLAS[2]; + tDot11fIEWMMTCLASPROC WMMTCLASPROC; + tDot11fIEESETrafStrmMet ESETrafStrmMet; +} tDot11fAddTSResponse; + +#define DOT11F_ADDTSRESPONSE (2) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_add_ts_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAddTSResponse * pFrm, bool append_ie); +uint32_t dot11f_pack_add_ts_response(tpAniSirGlobal pCtx, + tDot11fAddTSResponse *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_add_ts_response_size(tpAniSirGlobal pCtx, + tDot11fAddTSResponse *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fAssocRequest{ + tDot11fFfCapabilities Capabilities; + tDot11fFfListenInterval ListenInterval; + tDot11fIESSID SSID; + tDot11fIESuppRates SuppRates; + tDot11fIEOperatingMode OperatingMode; + tDot11fIEPowerCaps PowerCaps; + tDot11fIESuppChannels SuppChannels; + tDot11fIEHTCaps HTCaps; + tDot11fIEQOSCapsStation QOSCapsStation; + tDot11fIERSNOpaque RSNOpaque; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIEMobilityDomain MobilityDomain; + tDot11fIEFTInfo FTInfo; + tDot11fIESuppOperatingClasses SuppOperatingClasses; + tDot11fIEWAPIOpaque WAPIOpaque; + tDot11fIEWAPI WAPI; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIEQosMapSet QosMapSet; + tDot11fIEExtCap ExtCap; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEfils_session fils_session; + tDot11fIEfils_public_key fils_public_key; + tDot11fIEfils_key_confirmation fils_key_confirmation; + tDot11fIEfils_hlp_container fils_hlp_container; + tDot11fIEfragment_ie fragment_ie; + tDot11fIEdh_parameter_element dh_parameter_element; + tDot11fIEWPAOpaque WPAOpaque; + tDot11fIEWMMCaps WMMCaps; + tDot11fIEWMMInfoStation WMMInfoStation; + tDot11fIEWscIEOpaque WscIEOpaque; + tDot11fIEESERadMgmtCap ESERadMgmtCap; + tDot11fIEESEVersion ESEVersion; + tDot11fIEP2PIEOpaque P2PIEOpaque; + tDot11fIEWFDIEOpaque WFDIEOpaque; + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEhs20vendor_ie hs20vendor_ie; + tDot11fIEqcn_ie qcn_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; + tDot11fIEosen_ie osen_ie; + tDot11fIEroaming_consortium_sel roaming_consortium_sel; +} tDot11fAssocRequest; + +#define DOT11F_ASSOCREQUEST (3) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_assoc_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAssocRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_assoc_request(tpAniSirGlobal pCtx, + tDot11fAssocRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_assoc_request_size(tpAniSirGlobal pCtx, + tDot11fAssocRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fAssocResponse{ + tDot11fFfCapabilities Capabilities; + tDot11fFfStatus Status; + tDot11fFfAID AID; + tDot11fIESuppRates SuppRates; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIEEDCAParamSet EDCAParamSet; + tDot11fIERCPIIE RCPIIE; + tDot11fIERSNIIE RSNIIE; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIEMobilityDomain MobilityDomain; + tDot11fIEFTInfo FTInfo; + uint16_t num_RICDataDesc; + tDot11fIERICDataDesc RICDataDesc[2]; + tDot11fIEWPA WPA; + tDot11fIETimeoutInterval TimeoutInterval; + tDot11fIEHTCaps HTCaps; + tDot11fIEHTInfo HTInfo; + tDot11fIEWMMParams WMMParams; + tDot11fIEWMMCaps WMMCaps; + tDot11fIEESERadMgmtCap ESERadMgmtCap; + tDot11fIEESETrafStrmMet ESETrafStrmMet; + tDot11fIEESETxmitPower ESETxmitPower; + uint16_t num_WMMTSPEC; + tDot11fIEWMMTSPEC WMMTSPEC[4]; + tDot11fIEWscAssocRes WscAssocRes; + tDot11fIEP2PAssocRes P2PAssocRes; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEVHTOperation VHTOperation; + tDot11fIEExtCap ExtCap; + tDot11fIEOBSSScanParameters OBSSScanParameters; + tDot11fIEQosMapSet QosMapSet; + tDot11fIEfils_session fils_session; + tDot11fIEfils_public_key fils_public_key; + tDot11fIEfils_key_confirmation fils_key_confirmation; + tDot11fIEfils_hlp_container fils_hlp_container; + tDot11fIEfragment_ie fragment_ie; + tDot11fIEfils_kde fils_kde; + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEqcn_ie qcn_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_op he_op; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; + tDot11fIEbss_color_change bss_color_change; + tDot11fIEmu_edca_param_set mu_edca_param_set; + tDot11fIEMBO_IE MBO_IE; +} tDot11fAssocResponse; + +#define DOT11F_ASSOCRESPONSE (4) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_assoc_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAssocResponse * pFrm, bool append_ie); +uint32_t dot11f_pack_assoc_response(tpAniSirGlobal pCtx, + tDot11fAssocResponse *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_assoc_response_size(tpAniSirGlobal pCtx, + tDot11fAssocResponse *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fAuthentication{ + tDot11fFfAuthAlgo AuthAlgo; + tDot11fFfAuthSeqNo AuthSeqNo; + tDot11fFfStatus Status; + tDot11fIEChallengeText ChallengeText; + tDot11fIERSNOpaque RSNOpaque; + tDot11fIEMobilityDomain MobilityDomain; + tDot11fIEFTInfo FTInfo; + tDot11fIETimeoutInterval TimeoutInterval; + uint16_t num_RICDataDesc; + tDot11fIERICDataDesc RICDataDesc[2]; + tDot11fIEfils_nonce fils_nonce; + tDot11fIEfils_session fils_session; + tDot11fIEfils_wrapped_data fils_wrapped_data; + tDot11fIEfils_assoc_delay_info fils_assoc_delay_info; +} tDot11fAuthentication; + +#define DOT11F_AUTHENTICATION (5) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_authentication(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAuthentication * pFrm, bool append_ie); +uint32_t dot11f_pack_authentication(tpAniSirGlobal pCtx, + tDot11fAuthentication *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_authentication_size(tpAniSirGlobal pCtx, + tDot11fAuthentication *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fBeacon{ + tDot11fFfTimeStamp TimeStamp; + tDot11fFfBeaconInterval BeaconInterval; + tDot11fFfCapabilities Capabilities; + tDot11fIESSID SSID; + tDot11fIESuppRates SuppRates; + tDot11fIEFHParamSet FHParamSet; + tDot11fIEDSParams DSParams; + tDot11fIECFParams CFParams; + tDot11fIEIBSSParams IBSSParams; + tDot11fIETIM TIM; + tDot11fIECountry Country; + tDot11fIEFHParams FHParams; + tDot11fIEFHPattTable FHPattTable; + tDot11fIEPowerConstraints PowerConstraints; + tDot11fIEChanSwitchAnn ChanSwitchAnn; + tDot11fIEext_chan_switch_ann ext_chan_switch_ann; + tDot11fIESuppOperatingClasses SuppOperatingClasses; + tDot11fIEQuiet Quiet; + tDot11fIETPCReport TPCReport; + tDot11fIEERPInfo ERPInfo; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIERSN RSN; + tDot11fIEQBSSLoad QBSSLoad; + tDot11fIEEDCAParamSet EDCAParamSet; + tDot11fIEQOSCapsAp QOSCapsAp; + tDot11fIEAPChannelReport APChannelReport; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIEMobilityDomain MobilityDomain; + tDot11fIEWPA WPA; + tDot11fIEHTCaps HTCaps; + tDot11fIEHTInfo HTInfo; + tDot11fIEsec_chan_offset_ele sec_chan_offset_ele; + tDot11fIEWMMInfoAp WMMInfoAp; + tDot11fIEWMMParams WMMParams; + tDot11fIEWMMCaps WMMCaps; + tDot11fIEWAPI WAPI; + tDot11fIEESERadMgmtCap ESERadMgmtCap; + tDot11fIEESETrafStrmMet ESETrafStrmMet; + tDot11fIEESETxmitPower ESETxmitPower; + tDot11fIEWscBeacon WscBeacon; + tDot11fIEP2PBeacon P2PBeacon; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEVHTOperation VHTOperation; + tDot11fIEVHTExtBssLoad VHTExtBssLoad; + tDot11fIEExtCap ExtCap; + tDot11fIEOperatingMode OperatingMode; + tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn; + tDot11fIEOBSSScanParameters OBSSScanParameters; + tDot11fIEfils_indication fils_indication; + tDot11fIEVendor1IE Vendor1IE; + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEVendor3IE Vendor3IE; + tDot11fIEhs20vendor_ie hs20vendor_ie; + tDot11fIEChannelSwitchWrapper ChannelSwitchWrapper; + tDot11fIEQComVendorIE QComVendorIE; + tDot11fIEESEVersion ESEVersion; + tDot11fIEMBO_IE MBO_IE; + tDot11fIEqcn_ie qcn_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_op he_op; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; + tDot11fIEbss_color_change bss_color_change; + tDot11fIEmu_edca_param_set mu_edca_param_set; + tDot11fIEesp_information esp_information; +} tDot11fBeacon; + +#define DOT11F_BEACON (6) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_beacon(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fBeacon * pFrm, bool append_ie); +uint32_t dot11f_pack_beacon(tpAniSirGlobal pCtx, + tDot11fBeacon *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_beacon_size(tpAniSirGlobal pCtx, + tDot11fBeacon *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fBeacon1{ + tDot11fFfTimeStamp TimeStamp; + tDot11fFfBeaconInterval BeaconInterval; + tDot11fFfCapabilities Capabilities; + tDot11fIESSID SSID; + tDot11fIESuppRates SuppRates; + tDot11fIEDSParams DSParams; + tDot11fIEIBSSParams IBSSParams; +} tDot11fBeacon1; + +#define DOT11F_BEACON1 (7) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_beacon1(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fBeacon1 * pFrm, bool append_ie); +uint32_t dot11f_pack_beacon1(tpAniSirGlobal pCtx, + tDot11fBeacon1 *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_beacon1_size(tpAniSirGlobal pCtx, + tDot11fBeacon1 *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fBeacon2{ + tDot11fIECountry Country; + tDot11fIEPowerConstraints PowerConstraints; + tDot11fIEChanSwitchAnn ChanSwitchAnn; + tDot11fIEext_chan_switch_ann ext_chan_switch_ann; + tDot11fIESuppOperatingClasses SuppOperatingClasses; + tDot11fIEQuiet Quiet; + tDot11fIETPCReport TPCReport; + tDot11fIEERPInfo ERPInfo; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIERSNOpaque RSNOpaque; + tDot11fIEEDCAParamSet EDCAParamSet; + tDot11fIEAPChannelReport APChannelReport; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIEMobilityDomain MobilityDomain; + tDot11fIEWPA WPA; + tDot11fIEHTCaps HTCaps; + tDot11fIEHTInfo HTInfo; + tDot11fIEsec_chan_offset_ele sec_chan_offset_ele; + tDot11fIEWMMInfoAp WMMInfoAp; + tDot11fIEWMMParams WMMParams; + tDot11fIEWMMCaps WMMCaps; + tDot11fIEWscBeacon WscBeacon; + tDot11fIEWAPI WAPI; + tDot11fIEESERadMgmtCap ESERadMgmtCap; + tDot11fIEESETrafStrmMet ESETrafStrmMet; + tDot11fIEESETxmitPower ESETxmitPower; + tDot11fIEP2PBeacon P2PBeacon; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEVHTOperation VHTOperation; + tDot11fIEvht_transmit_power_env vht_transmit_power_env; + tDot11fIEChannelSwitchWrapper ChannelSwitchWrapper; + tDot11fIEVHTExtBssLoad VHTExtBssLoad; + tDot11fIEExtCap ExtCap; + tDot11fIEOperatingMode OperatingMode; + tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn; + tDot11fIEOBSSScanParameters OBSSScanParameters; + tDot11fIEfils_indication fils_indication; + tDot11fIEVendor1IE Vendor1IE; + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEVendor3IE Vendor3IE; + tDot11fIEhs20vendor_ie hs20vendor_ie; + tDot11fIEQComVendorIE QComVendorIE; + tDot11fIEESEVersion ESEVersion; + tDot11fIEqcn_ie qcn_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_op he_op; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; + tDot11fIEbss_color_change bss_color_change; + tDot11fIEmu_edca_param_set mu_edca_param_set; + tDot11fIEesp_information esp_information; +} tDot11fBeacon2; + +#define DOT11F_BEACON2 (8) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_beacon2(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fBeacon2 * pFrm, bool append_ie); +uint32_t dot11f_pack_beacon2(tpAniSirGlobal pCtx, + tDot11fBeacon2 *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_beacon2_size(tpAniSirGlobal pCtx, + tDot11fBeacon2 *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fBeaconIEs{ + tDot11fIESSID SSID; + tDot11fIESuppRates SuppRates; + tDot11fIEFHParamSet FHParamSet; + tDot11fIEDSParams DSParams; + tDot11fIECFParams CFParams; + tDot11fIEIBSSParams IBSSParams; + tDot11fIETIM TIM; + tDot11fIECountry Country; + tDot11fIEFHParams FHParams; + tDot11fIEFHPattTable FHPattTable; + tDot11fIEPowerConstraints PowerConstraints; + tDot11fIEChanSwitchAnn ChanSwitchAnn; + tDot11fIEext_chan_switch_ann ext_chan_switch_ann; + tDot11fIESuppOperatingClasses SuppOperatingClasses; + tDot11fIEQuiet Quiet; + tDot11fIETPCReport TPCReport; + tDot11fIEERPInfo ERPInfo; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIERSN RSN; + tDot11fIEQBSSLoad QBSSLoad; + tDot11fIEEDCAParamSet EDCAParamSet; + tDot11fIEQOSCapsAp QOSCapsAp; + tDot11fIEAPChannelReport APChannelReport; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIEMobilityDomain MobilityDomain; + tDot11fIEWPA WPA; + tDot11fIEHTCaps HTCaps; + tDot11fIEHTInfo HTInfo; + tDot11fIEsec_chan_offset_ele sec_chan_offset_ele; + tDot11fIEWMMInfoAp WMMInfoAp; + tDot11fIEWMMParams WMMParams; + tDot11fIEWMMCaps WMMCaps; + tDot11fIEWAPI WAPI; + tDot11fIEESEVersion ESEVersion; + tDot11fIEESERadMgmtCap ESERadMgmtCap; + tDot11fIEESETrafStrmMet ESETrafStrmMet; + tDot11fIEESETxmitPower ESETxmitPower; + tDot11fIEWscBeaconProbeRes WscBeaconProbeRes; + tDot11fIEP2PBeaconProbeRes P2PBeaconProbeRes; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEVHTOperation VHTOperation; + tDot11fIEVHTExtBssLoad VHTExtBssLoad; + tDot11fIEExtCap ExtCap; + tDot11fIEOperatingMode OperatingMode; + tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn; + tDot11fIEOBSSScanParameters OBSSScanParameters; + tDot11fIEfils_indication fils_indication; + tDot11fIEVendor1IE Vendor1IE; + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEVendor3IE Vendor3IE; + tDot11fIEhs20vendor_ie hs20vendor_ie; + tDot11fIEChannelSwitchWrapper ChannelSwitchWrapper; + tDot11fIEQComVendorIE QComVendorIE; + tDot11fIEMBO_IE MBO_IE; + tDot11fIEqcn_ie qcn_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_op he_op; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; + tDot11fIEbss_color_change bss_color_change; + tDot11fIEmu_edca_param_set mu_edca_param_set; + tDot11fIEesp_information esp_information; +} tDot11fBeaconIEs; + +#define DOT11F_BEACONIES (9) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_beacon_i_es(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fBeaconIEs * pFrm, bool append_ie); +uint32_t dot11f_pack_beacon_i_es(tpAniSirGlobal pCtx, + tDot11fBeaconIEs *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_beacon_i_es_size(tpAniSirGlobal pCtx, + tDot11fBeaconIEs *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fChannelSwitch{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fIEChanSwitchAnn ChanSwitchAnn; + tDot11fIEsec_chan_offset_ele sec_chan_offset_ele; + tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn; +} tDot11fChannelSwitch; + +#define DOT11F_CHANNELSWITCH (10) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_channel_switch(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fChannelSwitch * pFrm, bool append_ie); +uint32_t dot11f_pack_channel_switch(tpAniSirGlobal pCtx, + tDot11fChannelSwitch *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_channel_switch_size(tpAniSirGlobal pCtx, + tDot11fChannelSwitch *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fDeAuth{ + tDot11fFfReason Reason; + tDot11fIEP2PDeAuth P2PDeAuth; +} tDot11fDeAuth; + +#define DOT11F_DEAUTH (11) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_de_auth(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fDeAuth * pFrm, bool append_ie); +uint32_t dot11f_pack_de_auth(tpAniSirGlobal pCtx, + tDot11fDeAuth *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_de_auth_size(tpAniSirGlobal pCtx, + tDot11fDeAuth *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fDelTS{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfTSInfo TSInfo; + tDot11fFfReason Reason; +} tDot11fDelTS; + +#define DOT11F_DELTS (12) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_del_ts(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fDelTS * pFrm, bool append_ie); +uint32_t dot11f_pack_del_ts(tpAniSirGlobal pCtx, + tDot11fDelTS *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_del_ts_size(tpAniSirGlobal pCtx, + tDot11fDelTS *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fDisassociation{ + tDot11fFfReason Reason; + tDot11fIEP2PDisAssoc P2PDisAssoc; +} tDot11fDisassociation; + +#define DOT11F_DISASSOCIATION (13) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_disassociation(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fDisassociation * pFrm, bool append_ie); +uint32_t dot11f_pack_disassociation(tpAniSirGlobal pCtx, + tDot11fDisassociation *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_disassociation_size(tpAniSirGlobal pCtx, + tDot11fDisassociation *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fLinkMeasurementReport{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfTPCEleID TPCEleID; + tDot11fFfTPCEleLen TPCEleLen; + tDot11fFfTxPower TxPower; + tDot11fFfLinkMargin LinkMargin; + tDot11fFfRxAntennaId RxAntennaId; + tDot11fFfTxAntennaId TxAntennaId; + tDot11fFfRCPI RCPI; + tDot11fFfRSNI RSNI; +} tDot11fLinkMeasurementReport; + +#define DOT11F_LINKMEASUREMENTREPORT (14) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_link_measurement_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fLinkMeasurementReport * pFrm, bool append_ie); +uint32_t dot11f_pack_link_measurement_report(tpAniSirGlobal pCtx, + tDot11fLinkMeasurementReport *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_link_measurement_report_size(tpAniSirGlobal pCtx, + tDot11fLinkMeasurementReport *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fLinkMeasurementRequest{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfTxPower TxPower; + tDot11fFfMaxTxPower MaxTxPower; +} tDot11fLinkMeasurementRequest; + +#define DOT11F_LINKMEASUREMENTREQUEST (15) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_link_measurement_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fLinkMeasurementRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_link_measurement_request(tpAniSirGlobal pCtx, + tDot11fLinkMeasurementRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_link_measurement_request_size(tpAniSirGlobal pCtx, + tDot11fLinkMeasurementRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fMeasurementReport{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fIEMeasurementReport MeasurementReport; +} tDot11fMeasurementReport; + +#define DOT11F_MEASUREMENTREPORT (16) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_measurement_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fMeasurementReport * pFrm, bool append_ie); +uint32_t dot11f_pack_measurement_report(tpAniSirGlobal pCtx, + tDot11fMeasurementReport *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_measurement_report_size(tpAniSirGlobal pCtx, + tDot11fMeasurementReport *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fMeasurementRequest{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + uint16_t num_MeasurementRequest; + tDot11fIEMeasurementRequest MeasurementRequest[4]; +} tDot11fMeasurementRequest; + +#define DOT11F_MEASUREMENTREQUEST (17) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_measurement_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fMeasurementRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_measurement_request(tpAniSirGlobal pCtx, + tDot11fMeasurementRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_measurement_request_size(tpAniSirGlobal pCtx, + tDot11fMeasurementRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fNeighborReportRequest{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fIESSID SSID; +} tDot11fNeighborReportRequest; + +#define DOT11F_NEIGHBORREPORTREQUEST (18) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_neighbor_report_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fNeighborReportRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_neighbor_report_request(tpAniSirGlobal pCtx, + tDot11fNeighborReportRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_neighbor_report_request_size(tpAniSirGlobal pCtx, + tDot11fNeighborReportRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fNeighborReportResponse{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + uint16_t num_NeighborReport; + tDot11fIENeighborReport NeighborReport[15]; +} tDot11fNeighborReportResponse; + +#define DOT11F_NEIGHBORREPORTRESPONSE (19) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_neighbor_report_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fNeighborReportResponse * pFrm, bool append_ie); +uint32_t dot11f_pack_neighbor_report_response(tpAniSirGlobal pCtx, + tDot11fNeighborReportResponse *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_neighbor_report_response_size(tpAniSirGlobal pCtx, + tDot11fNeighborReportResponse *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fOperatingMode{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfOperatingMode OperatingMode; +} tDot11fOperatingMode; + +#define DOT11F_OPERATINGMODE (20) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_operating_mode(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fOperatingMode * pFrm, bool append_ie); +uint32_t dot11f_pack_operating_mode(tpAniSirGlobal pCtx, + tDot11fOperatingMode *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_operating_mode_size(tpAniSirGlobal pCtx, + tDot11fOperatingMode *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fProbeRequest{ + tDot11fIESSID SSID; + tDot11fIESuppRates SuppRates; + tDot11fIERequestedInfo RequestedInfo; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIEDSParams DSParams; + tDot11fIEHTCaps HTCaps; + tDot11fIEWscProbeReq WscProbeReq; + tDot11fIEWFATPC WFATPC; + tDot11fIEP2PProbeReq P2PProbeReq; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEExtCap ExtCap; + tDot11fIEqcn_ie qcn_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; +} tDot11fProbeRequest; + +#define DOT11F_PROBEREQUEST (21) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_probe_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fProbeRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_probe_request(tpAniSirGlobal pCtx, + tDot11fProbeRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_probe_request_size(tpAniSirGlobal pCtx, + tDot11fProbeRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fProbeResponse{ + tDot11fFfTimeStamp TimeStamp; + tDot11fFfBeaconInterval BeaconInterval; + tDot11fFfCapabilities Capabilities; + tDot11fIESSID SSID; + tDot11fIESuppRates SuppRates; + tDot11fIEFHParamSet FHParamSet; + tDot11fIEDSParams DSParams; + tDot11fIECFParams CFParams; + tDot11fIEIBSSParams IBSSParams; + tDot11fIECountry Country; + tDot11fIEFHParams FHParams; + tDot11fIEFHPattTable FHPattTable; + tDot11fIEPowerConstraints PowerConstraints; + tDot11fIEChanSwitchAnn ChanSwitchAnn; + tDot11fIEext_chan_switch_ann ext_chan_switch_ann; + tDot11fIESuppOperatingClasses SuppOperatingClasses; + tDot11fIEQuiet Quiet; + tDot11fIETPCReport TPCReport; + tDot11fIEERPInfo ERPInfo; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIERSNOpaque RSNOpaque; + tDot11fIEQBSSLoad QBSSLoad; + tDot11fIEEDCAParamSet EDCAParamSet; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIEAPChannelReport APChannelReport; + tDot11fIEMobilityDomain MobilityDomain; + tDot11fIEWPA WPA; + tDot11fIEHTCaps HTCaps; + tDot11fIEHTInfo HTInfo; + tDot11fIEsec_chan_offset_ele sec_chan_offset_ele; + tDot11fIEWMMInfoAp WMMInfoAp; + tDot11fIEWMMParams WMMParams; + tDot11fIEWMMCaps WMMCaps; + tDot11fIEWAPI WAPI; + tDot11fIEESERadMgmtCap ESERadMgmtCap; + tDot11fIEESETrafStrmMet ESETrafStrmMet; + tDot11fIEESETxmitPower ESETxmitPower; + tDot11fIEWscProbeRes WscProbeRes; + tDot11fIEP2PProbeRes P2PProbeRes; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEVHTOperation VHTOperation; + tDot11fIEVHTExtBssLoad VHTExtBssLoad; + tDot11fIEExtCap ExtCap; + tDot11fIEOBSSScanParameters OBSSScanParameters; + tDot11fIEfils_indication fils_indication; + tDot11fIEVendor1IE Vendor1IE; + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEVendor3IE Vendor3IE; + tDot11fIEhs20vendor_ie hs20vendor_ie; + tDot11fIEChannelSwitchWrapper ChannelSwitchWrapper; + tDot11fIEQComVendorIE QComVendorIE; + tDot11fIEESEVersion ESEVersion; + tDot11fIEMBO_IE MBO_IE; + tDot11fIEqcn_ie qcn_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_op he_op; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; + tDot11fIEbss_color_change bss_color_change; + tDot11fIEmu_edca_param_set mu_edca_param_set; + tDot11fIEesp_information esp_information; +} tDot11fProbeResponse; + +#define DOT11F_PROBERESPONSE (22) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_probe_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fProbeResponse * pFrm, bool append_ie); +uint32_t dot11f_pack_probe_response(tpAniSirGlobal pCtx, + tDot11fProbeResponse *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_probe_response_size(tpAniSirGlobal pCtx, + tDot11fProbeResponse *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fQosMapConfigure{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fIEQosMapSet QosMapSet; +} tDot11fQosMapConfigure; + +#define DOT11F_QOSMAPCONFIGURE (23) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_qos_map_configure(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fQosMapConfigure * pFrm, bool append_ie); +uint32_t dot11f_pack_qos_map_configure(tpAniSirGlobal pCtx, + tDot11fQosMapConfigure *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_qos_map_configure_size(tpAniSirGlobal pCtx, + tDot11fQosMapConfigure *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fRadioMeasurementReport{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + uint16_t num_MeasurementReport; + tDot11fIEMeasurementReport MeasurementReport[4]; + tDot11fIEBeaconReportStatus BeaconReportStatus; +} tDot11fRadioMeasurementReport; + +#define DOT11F_RADIOMEASUREMENTREPORT (24) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_radio_measurement_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fRadioMeasurementReport * pFrm, bool append_ie); +uint32_t dot11f_pack_radio_measurement_report(tpAniSirGlobal pCtx, + tDot11fRadioMeasurementReport *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_radio_measurement_report_size(tpAniSirGlobal pCtx, + tDot11fRadioMeasurementReport *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fRadioMeasurementRequest{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfNumOfRepetitions NumOfRepetitions; + uint16_t num_MeasurementRequest; + tDot11fIEMeasurementRequest MeasurementRequest[5]; +} tDot11fRadioMeasurementRequest; + +#define DOT11F_RADIOMEASUREMENTREQUEST (25) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_radio_measurement_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fRadioMeasurementRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_radio_measurement_request(tpAniSirGlobal pCtx, + tDot11fRadioMeasurementRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_radio_measurement_request_size(tpAniSirGlobal pCtx, + tDot11fRadioMeasurementRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fReAssocRequest{ + tDot11fFfCapabilities Capabilities; + tDot11fFfListenInterval ListenInterval; + tDot11fFfCurrentAPAddress CurrentAPAddress; + tDot11fIESSID SSID; + tDot11fIESuppRates SuppRates; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIEPowerCaps PowerCaps; + tDot11fIESuppChannels SuppChannels; + tDot11fIERSNOpaque RSNOpaque; + tDot11fIEQOSCapsStation QOSCapsStation; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIEMobilityDomain MobilityDomain; + tDot11fIEFTInfo FTInfo; + uint16_t num_RICDataDesc; + tDot11fIERICDataDesc RICDataDesc[2]; + tDot11fIESuppOperatingClasses SuppOperatingClasses; + tDot11fIEWPAOpaque WPAOpaque; + tDot11fIEHTCaps HTCaps; + tDot11fIEWMMCaps WMMCaps; + tDot11fIEWMMInfoStation WMMInfoStation; + tDot11fIEWscIEOpaque WscIEOpaque; + tDot11fIEWAPIOpaque WAPIOpaque; + tDot11fIEWAPI WAPI; + tDot11fIEESERadMgmtCap ESERadMgmtCap; + tDot11fIEESEVersion ESEVersion; + tDot11fIEESECckmOpaque ESECckmOpaque; + uint16_t num_WMMTSPEC; + tDot11fIEWMMTSPEC WMMTSPEC[4]; + tDot11fIEESETrafStrmRateSet ESETrafStrmRateSet; + tDot11fIEP2PIEOpaque P2PIEOpaque; + tDot11fIEWFDIEOpaque WFDIEOpaque; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEExtCap ExtCap; + tDot11fIEOperatingMode OperatingMode; + tDot11fIEQosMapSet QosMapSet; + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEhs20vendor_ie hs20vendor_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; +} tDot11fReAssocRequest; + +#define DOT11F_REASSOCREQUEST (26) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_re_assoc_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fReAssocRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_re_assoc_request(tpAniSirGlobal pCtx, + tDot11fReAssocRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_re_assoc_request_size(tpAniSirGlobal pCtx, + tDot11fReAssocRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fReAssocResponse{ + tDot11fFfCapabilities Capabilities; + tDot11fFfStatus Status; + tDot11fFfAID AID; + tDot11fIESuppRates SuppRates; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIEEDCAParamSet EDCAParamSet; + tDot11fIERCPIIE RCPIIE; + tDot11fIERSNIIE RSNIIE; + tDot11fIERRMEnabledCap RRMEnabledCap; + tDot11fIERSNOpaque RSNOpaque; + tDot11fIEMobilityDomain MobilityDomain; + tDot11fIEFTInfo FTInfo; + uint16_t num_RICDataDesc; + tDot11fIERICDataDesc RICDataDesc[2]; + tDot11fIEWPA WPA; + tDot11fIETimeoutInterval TimeoutInterval; + tDot11fIEHTCaps HTCaps; + tDot11fIEHTInfo HTInfo; + tDot11fIEWMMParams WMMParams; + tDot11fIEESERadMgmtCap ESERadMgmtCap; + tDot11fIEESETrafStrmMet ESETrafStrmMet; + tDot11fIEESETxmitPower ESETxmitPower; + uint16_t num_WMMTSPEC; + tDot11fIEWMMTSPEC WMMTSPEC[4]; + tDot11fIEESETrafStrmRateSet ESETrafStrmRateSet; + tDot11fIEWscReassocRes WscReassocRes; + tDot11fIEP2PAssocRes P2PAssocRes; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEVHTOperation VHTOperation; + tDot11fIEExtCap ExtCap; + tDot11fIEOBSSScanParameters OBSSScanParameters; + tDot11fIEQosMapSet QosMapSet; + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_op he_op; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; + tDot11fIEbss_color_change bss_color_change; + tDot11fIEmu_edca_param_set mu_edca_param_set; + tDot11fIEMBO_IE MBO_IE; +} tDot11fReAssocResponse; + +#define DOT11F_REASSOCRESPONSE (27) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_re_assoc_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fReAssocResponse * pFrm, bool append_ie); +uint32_t dot11f_pack_re_assoc_response(tpAniSirGlobal pCtx, + tDot11fReAssocResponse *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_re_assoc_response_size(tpAniSirGlobal pCtx, + tDot11fReAssocResponse *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fSMPowerSave{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfSMPowerModeSet SMPowerModeSet; +} tDot11fSMPowerSave; + +#define DOT11F_SMPOWERSAVE (28) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_sm_power_save(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fSMPowerSave * pFrm, bool append_ie); +uint32_t dot11f_pack_sm_power_save(tpAniSirGlobal pCtx, + tDot11fSMPowerSave *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_sm_power_save_size(tpAniSirGlobal pCtx, + tDot11fSMPowerSave *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fSaQueryReq{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfTransactionId TransactionId; +} tDot11fSaQueryReq; + +#define DOT11F_SAQUERYREQ (29) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_sa_query_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fSaQueryReq * pFrm, bool append_ie); +uint32_t dot11f_pack_sa_query_req(tpAniSirGlobal pCtx, + tDot11fSaQueryReq *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_sa_query_req_size(tpAniSirGlobal pCtx, + tDot11fSaQueryReq *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fSaQueryRsp{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfTransactionId TransactionId; +} tDot11fSaQueryRsp; + +#define DOT11F_SAQUERYRSP (30) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_sa_query_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fSaQueryRsp * pFrm, bool append_ie); +uint32_t dot11f_pack_sa_query_rsp(tpAniSirGlobal pCtx, + tDot11fSaQueryRsp *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_sa_query_rsp_size(tpAniSirGlobal pCtx, + tDot11fSaQueryRsp *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTDLSDisReq{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fIELinkIdentifier LinkIdentifier; +} tDot11fTDLSDisReq; + +#define DOT11F_TDLSDISREQ (31) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tdls_dis_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSDisReq * pFrm, bool append_ie); +uint32_t dot11f_pack_tdls_dis_req(tpAniSirGlobal pCtx, + tDot11fTDLSDisReq *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tdls_dis_req_size(tpAniSirGlobal pCtx, + tDot11fTDLSDisReq *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTDLSDisRsp{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfCapabilities Capabilities; + tDot11fIESuppRates SuppRates; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIESuppChannels SuppChannels; + tDot11fIESuppOperatingClasses SuppOperatingClasses; + tDot11fIERSN RSN; + tDot11fIEExtCap ExtCap; + tDot11fIEFTInfo FTInfo; + tDot11fIETimeoutInterval TimeoutInterval; + tDot11fIERICData RICData; + tDot11fIEHTCaps HTCaps; + tDot11fIEht2040_bss_coexistence ht2040_bss_coexistence; + tDot11fIELinkIdentifier LinkIdentifier; + tDot11fIEVHTCaps VHTCaps; +} tDot11fTDLSDisRsp; + +#define DOT11F_TDLSDISRSP (32) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tdls_dis_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSDisRsp * pFrm, bool append_ie); +uint32_t dot11f_pack_tdls_dis_rsp(tpAniSirGlobal pCtx, + tDot11fTDLSDisRsp *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tdls_dis_rsp_size(tpAniSirGlobal pCtx, + tDot11fTDLSDisRsp *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTDLSPeerTrafficInd{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fIELinkIdentifier LinkIdentifier; + tDot11fIEPTIControl PTIControl; + tDot11fIEPUBufferStatus PUBufferStatus; +} tDot11fTDLSPeerTrafficInd; + +#define DOT11F_TDLSPEERTRAFFICIND (33) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tdls_peer_traffic_ind(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSPeerTrafficInd * pFrm, bool append_ie); +uint32_t dot11f_pack_tdls_peer_traffic_ind(tpAniSirGlobal pCtx, + tDot11fTDLSPeerTrafficInd *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tdls_peer_traffic_ind_size(tpAniSirGlobal pCtx, + tDot11fTDLSPeerTrafficInd *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTDLSPeerTrafficRsp{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fIELinkIdentifier LinkIdentifier; +} tDot11fTDLSPeerTrafficRsp; + +#define DOT11F_TDLSPEERTRAFFICRSP (34) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tdls_peer_traffic_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSPeerTrafficRsp * pFrm, bool append_ie); +uint32_t dot11f_pack_tdls_peer_traffic_rsp(tpAniSirGlobal pCtx, + tDot11fTDLSPeerTrafficRsp *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tdls_peer_traffic_rsp_size(tpAniSirGlobal pCtx, + tDot11fTDLSPeerTrafficRsp *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTDLSSetupCnf{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfStatus Status; + tDot11fFfDialogToken DialogToken; + tDot11fIERSN RSN; + tDot11fIEEDCAParamSet EDCAParamSet; + tDot11fIEFTInfo FTInfo; + tDot11fIETimeoutInterval TimeoutInterval; + tDot11fIEHTInfo HTInfo; + tDot11fIELinkIdentifier LinkIdentifier; + tDot11fIEWMMParams WMMParams; + tDot11fIEVHTOperation VHTOperation; + tDot11fIEOperatingMode OperatingMode; +} tDot11fTDLSSetupCnf; + +#define DOT11F_TDLSSETUPCNF (35) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tdls_setup_cnf(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSSetupCnf * pFrm, bool append_ie); +uint32_t dot11f_pack_tdls_setup_cnf(tpAniSirGlobal pCtx, + tDot11fTDLSSetupCnf *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tdls_setup_cnf_size(tpAniSirGlobal pCtx, + tDot11fTDLSSetupCnf *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTDLSSetupReq{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfCapabilities Capabilities; + tDot11fIESuppRates SuppRates; + tDot11fIECountry Country; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIESuppChannels SuppChannels; + tDot11fIERSN RSN; + tDot11fIEExtCap ExtCap; + tDot11fIESuppOperatingClasses SuppOperatingClasses; + tDot11fIEQOSCapsStation QOSCapsStation; + tDot11fIEFTInfo FTInfo; + tDot11fIETimeoutInterval TimeoutInterval; + tDot11fIERICData RICData; + tDot11fIEHTCaps HTCaps; + tDot11fIEht2040_bss_coexistence ht2040_bss_coexistence; + tDot11fIELinkIdentifier LinkIdentifier; + tDot11fIEWMMInfoStation WMMInfoStation; + tDot11fIEAID AID; + tDot11fIEVHTCaps VHTCaps; +} tDot11fTDLSSetupReq; + +#define DOT11F_TDLSSETUPREQ (36) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tdls_setup_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSSetupReq * pFrm, bool append_ie); +uint32_t dot11f_pack_tdls_setup_req(tpAniSirGlobal pCtx, + tDot11fTDLSSetupReq *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tdls_setup_req_size(tpAniSirGlobal pCtx, + tDot11fTDLSSetupReq *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTDLSSetupRsp{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfStatus Status; + tDot11fFfDialogToken DialogToken; + tDot11fFfCapabilities Capabilities; + tDot11fIESuppRates SuppRates; + tDot11fIECountry Country; + tDot11fIEExtSuppRates ExtSuppRates; + tDot11fIESuppChannels SuppChannels; + tDot11fIERSN RSN; + tDot11fIEExtCap ExtCap; + tDot11fIESuppOperatingClasses SuppOperatingClasses; + tDot11fIEQOSCapsStation QOSCapsStation; + tDot11fIEFTInfo FTInfo; + tDot11fIETimeoutInterval TimeoutInterval; + tDot11fIERICData RICData; + tDot11fIEHTCaps HTCaps; + tDot11fIEht2040_bss_coexistence ht2040_bss_coexistence; + tDot11fIELinkIdentifier LinkIdentifier; + tDot11fIEWMMInfoStation WMMInfoStation; + tDot11fIEAID AID; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEOperatingMode OperatingMode; +} tDot11fTDLSSetupRsp; + +#define DOT11F_TDLSSETUPRSP (37) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tdls_setup_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSSetupRsp * pFrm, bool append_ie); +uint32_t dot11f_pack_tdls_setup_rsp(tpAniSirGlobal pCtx, + tDot11fTDLSSetupRsp *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tdls_setup_rsp_size(tpAniSirGlobal pCtx, + tDot11fTDLSSetupRsp *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTDLSTeardown{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfReason Reason; + tDot11fIEFTInfo FTInfo; + tDot11fIELinkIdentifier LinkIdentifier; +} tDot11fTDLSTeardown; + +#define DOT11F_TDLSTEARDOWN (38) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tdls_teardown(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSTeardown * pFrm, bool append_ie); +uint32_t dot11f_pack_tdls_teardown(tpAniSirGlobal pCtx, + tDot11fTDLSTeardown *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tdls_teardown_size(tpAniSirGlobal pCtx, + tDot11fTDLSTeardown *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTPCReport{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fIETPCReport TPCReport; +} tDot11fTPCReport; + +#define DOT11F_TPCREPORT (39) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tpc_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTPCReport * pFrm, bool append_ie); +uint32_t dot11f_pack_tpc_report(tpAniSirGlobal pCtx, + tDot11fTPCReport *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tpc_report_size(tpAniSirGlobal pCtx, + tDot11fTPCReport *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTPCRequest{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fIETPCRequest TPCRequest; +} tDot11fTPCRequest; + +#define DOT11F_TPCREQUEST (40) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_tpc_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTPCRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_tpc_request(tpAniSirGlobal pCtx, + tDot11fTPCRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_tpc_request_size(tpAniSirGlobal pCtx, + tDot11fTPCRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fTimingAdvertisementFrame{ + tDot11fFfTimeStamp TimeStamp; + tDot11fFfCapabilities Capabilities; + tDot11fIECountry Country; + tDot11fIEPowerConstraints PowerConstraints; + tDot11fIETimeAdvertisement TimeAdvertisement; + tDot11fIEExtCap ExtCap; + tDot11fIEVendor1IE Vendor1IE; + tDot11fIEVendor3IE Vendor3IE; +} tDot11fTimingAdvertisementFrame; + +#define DOT11F_TIMINGADVERTISEMENTFRAME (41) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_timing_advertisement_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTimingAdvertisementFrame * pFrm, bool append_ie); +uint32_t dot11f_pack_timing_advertisement_frame(tpAniSirGlobal pCtx, + tDot11fTimingAdvertisementFrame *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_timing_advertisement_frame_size(tpAniSirGlobal pCtx, + tDot11fTimingAdvertisementFrame *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fVHTGidManagementActionFrame{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfVhtMembershipStatusArray VhtMembershipStatusArray; + tDot11fFfVhtUserPositionArray VhtUserPositionArray; +} tDot11fVHTGidManagementActionFrame; + +#define DOT11F_VHTGIDMANAGEMENTACTIONFRAME (42) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_vht_gid_management_action_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fVHTGidManagementActionFrame * pFrm, bool append_ie); +uint32_t dot11f_pack_vht_gid_management_action_frame(tpAniSirGlobal pCtx, + tDot11fVHTGidManagementActionFrame *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_vht_gid_management_action_frame_size(tpAniSirGlobal pCtx, + tDot11fVHTGidManagementActionFrame *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fWMMAddTSRequest{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfStatusCode StatusCode; + tDot11fIEWMMTSPEC WMMTSPEC; + tDot11fIEESETrafStrmRateSet ESETrafStrmRateSet; +} tDot11fWMMAddTSRequest; + +#define DOT11F_WMMADDTSREQUEST (43) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_wmm_add_ts_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fWMMAddTSRequest * pFrm, bool append_ie); +uint32_t dot11f_pack_wmm_add_ts_request(tpAniSirGlobal pCtx, + tDot11fWMMAddTSRequest *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_wmm_add_ts_request_size(tpAniSirGlobal pCtx, + tDot11fWMMAddTSRequest *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fWMMAddTSResponse{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfStatusCode StatusCode; + tDot11fIEWMMTSPEC WMMTSPEC; + tDot11fIEESETrafStrmMet ESETrafStrmMet; +} tDot11fWMMAddTSResponse; + +#define DOT11F_WMMADDTSRESPONSE (44) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_wmm_add_ts_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fWMMAddTSResponse * pFrm, bool append_ie); +uint32_t dot11f_pack_wmm_add_ts_response(tpAniSirGlobal pCtx, + tDot11fWMMAddTSResponse *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_wmm_add_ts_response_size(tpAniSirGlobal pCtx, + tDot11fWMMAddTSResponse *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fWMMDelTS{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfStatusCode StatusCode; + tDot11fIEWMMTSPEC WMMTSPEC; +} tDot11fWMMDelTS; + +#define DOT11F_WMMDELTS (45) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_wmm_del_ts(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fWMMDelTS * pFrm, bool append_ie); +uint32_t dot11f_pack_wmm_del_ts(tpAniSirGlobal pCtx, + tDot11fWMMDelTS *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_wmm_del_ts_size(tpAniSirGlobal pCtx, + tDot11fWMMDelTS *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11faddba_req{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfaddba_param_set addba_param_set; + tDot11fFfba_timeout ba_timeout; + tDot11fFfba_start_seq_ctrl ba_start_seq_ctrl; + tDot11fIEaddba_extn_element addba_extn_element; +} tDot11faddba_req; + +#define DOT11F_ADDBA_REQ (46) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_addba_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11faddba_req * pFrm, bool append_ie); +uint32_t dot11f_pack_addba_req(tpAniSirGlobal pCtx, + tDot11faddba_req *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_addba_req_size(tpAniSirGlobal pCtx, + tDot11faddba_req *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11faddba_rsp{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfDialogToken DialogToken; + tDot11fFfStatus Status; + tDot11fFfaddba_param_set addba_param_set; + tDot11fFfba_timeout ba_timeout; + tDot11fIEaddba_extn_element addba_extn_element; +} tDot11faddba_rsp; + +#define DOT11F_ADDBA_RSP (47) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_addba_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11faddba_rsp * pFrm, bool append_ie); +uint32_t dot11f_pack_addba_rsp(tpAniSirGlobal pCtx, + tDot11faddba_rsp *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_addba_rsp_size(tpAniSirGlobal pCtx, + tDot11faddba_rsp *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fdelba_req{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfdelba_param_set delba_param_set; + tDot11fFfReason Reason; +} tDot11fdelba_req; + +#define DOT11F_DELBA_REQ (48) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_delba_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fdelba_req * pFrm, bool append_ie); +uint32_t dot11f_pack_delba_req(tpAniSirGlobal pCtx, + tDot11fdelba_req *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_delba_req_size(tpAniSirGlobal pCtx, + tDot11fdelba_req *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fext_channel_switch_action_frame{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fFfext_chan_switch_ann_action ext_chan_switch_ann_action; + tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn; +} tDot11fext_channel_switch_action_frame; + +#define DOT11F_EXT_CHANNEL_SWITCH_ACTION_FRAME (49) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_ext_channel_switch_action_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fext_channel_switch_action_frame * pFrm, bool append_ie); +uint32_t dot11f_pack_ext_channel_switch_action_frame(tpAniSirGlobal pCtx, + tDot11fext_channel_switch_action_frame *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_ext_channel_switch_action_frame_size(tpAniSirGlobal pCtx, + tDot11fext_channel_switch_action_frame *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fht2040_bss_coexistence_mgmt_action_frame{ + tDot11fFfCategory Category; + tDot11fFfAction Action; + tDot11fIEht2040_bss_coexistence ht2040_bss_coexistence; + tDot11fIEht2040_bss_intolerant_report ht2040_bss_intolerant_report; +} tDot11fht2040_bss_coexistence_mgmt_action_frame; + +#define DOT11F_HT2040_BSS_COEXISTENCE_MGMT_ACTION_FRAME (50) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_ht2040_bss_coexistence_mgmt_action_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fht2040_bss_coexistence_mgmt_action_frame * pFrm, bool append_ie); +uint32_t dot11f_pack_ht2040_bss_coexistence_mgmt_action_frame(tpAniSirGlobal pCtx, + tDot11fht2040_bss_coexistence_mgmt_action_frame *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_ht2040_bss_coexistence_mgmt_action_frameSize(tpAniSirGlobal pCtx, + tDot11fht2040_bss_coexistence_mgmt_action_frame *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fp2p_oper_chan_change_confirm{ + tDot11fFfCategory Category; + tDot11fFfp2p_action_oui p2p_action_oui; + tDot11fFfp2p_action_subtype p2p_action_subtype; + tDot11fFfDialogToken DialogToken; + tDot11fIEHTCaps HTCaps; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEOperatingMode OperatingMode; +} tDot11fp2p_oper_chan_change_confirm; + +#define DOT11F_P2P_OPER_CHAN_CHANGE_CONFIRM (51) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_p2p_oper_chan_change_confirm(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fp2p_oper_chan_change_confirm * pFrm, bool append_ie); +uint32_t dot11f_pack_p2p_oper_chan_change_confirm(tpAniSirGlobal pCtx, + tDot11fp2p_oper_chan_change_confirm *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_p2p_oper_chan_change_confirmSize(tpAniSirGlobal pCtx, + tDot11fp2p_oper_chan_change_confirm *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +typedef struct sDot11fvendor_action_frame{ + tDot11fFfCategory Category; + tDot11fFfvendor_oui vendor_oui; + tDot11fFfvendor_action_subtype vendor_action_subtype; +} tDot11fvendor_action_frame; + +#define DOT11F_VENDOR_ACTION_FRAME (52) + +#ifdef __cplusplus +extern "C" { +#endif /* C++ */ + +uint32_t dot11f_unpack_vendor_action_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fvendor_action_frame * pFrm, bool append_ie); +uint32_t dot11f_pack_vendor_action_frame(tpAniSirGlobal pCtx, + tDot11fvendor_action_frame *pFrm, uint8_t *pBuf, + uint32_t nBuf, uint32_t *pnConsumed); +uint32_t dot11f_get_packed_vendor_action_frameSize(tpAniSirGlobal pCtx, + tDot11fvendor_action_frame *pFrm, + uint32_t *pnNeeded); + +#ifdef __cplusplus +} /* End extern "C". */ +#endif /* C++ */ + +#endif /* DOT11F_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/include/dph_global.h b/drivers/staging/qcacld-3.0/core/mac/src/include/dph_global.h new file mode 100644 index 0000000000000000000000000000000000000000..6b8cd2b71a191e2340afce441805a55d9a166b86 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/include/dph_global.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + + * + + * Author: Sandesh Goel + + * Date: 02/25/02 + + * History:- + + * Date Modified by Modification Information + + * -------------------------------------------------------------------- + + * + + */ + +#ifndef __DPH_GLOBAL_H__ +#define __DPH_GLOBAL_H__ + +#include "lim_global.h" +#include "sir_mac_prot_def.h" +#include "sir_api.h" + +/* DPH Hash Index for BSS(STA's Peer) on station. */ +#define DPH_STA_HASH_INDEX_PEER 1 + +#ifdef WLAN_FEATURE_11W +/* DPH PMF SA Query state for station */ +#define DPH_SA_QUERY_NOT_IN_PROGRESS 1 +#define DPH_SA_QUERY_IN_PROGRESS 2 +#define DPH_SA_QUERY_TIMED_OUT 3 +#endif + +typedef struct sDphQosParams { + uint8_t addtsPresent; + tSirAddtsReqInfo addts; + tSirMacQosCapabilityStaIE capability; +} tDphQosParams; + +/** + * struct parsed_ies: Parsed IE's of BSS capability + * @ht_caps: HT caps IE + * @vht_caps: VHT caps IE + * @ht_operation: HT operation IE + * @vht_operation: VHT operation IE + * @hs20vendor_ie: HS2.0 vendor IE + * + * This structure holds the parsed IE of connected BSS + * and this is not the intersection of BSS and STA + * capability. For example, if BSS supports 80 MHz + * and STA connects to BSS in 20 MHz, this structure + * holds 80 MHz as peer capability. + */ +struct parsed_ies { + tDot11fIEHTCaps ht_caps; + tDot11fIEVHTCaps vht_caps; + tDot11fIEHTInfo ht_operation; + tDot11fIEVHTOperation vht_operation; + tDot11fIEhs20vendor_ie hs20vendor_ie; +#ifdef WLAN_FEATURE_11AX + tDot11fIEhe_op he_operation; +#endif +}; + +/* STA state node */ +typedef struct sDphHashNode { + /* + * This STA valid or not + */ + uint8_t valid:1; + uint8_t qosMode:1; + uint8_t erpEnabled:1; + /* This has been added to the dph hash table */ + uint8_t added:1; + uint8_t shortPreambleEnabled:1; + uint8_t shortSlotTimeEnabled:1; + /* set if both ap and sta are wme capable */ + uint8_t wmeEnabled:1; + /* set if both ap and sta are 11e capable */ + uint8_t lleEnabled:1; + /* set if both ap and sta are wsm capable */ + uint8_t wsmEnabled:1; + uint8_t fAniCount:1; + uint8_t rmfEnabled:1; + /* LIM state */ + struct lim_sta_context mlmStaContext; + /* qos parameter info */ + tDphQosParams qos; + /* + * All the legacy and airgo supported rates. + */ + struct supported_rates supportedRates; + /* MIMO Power Save */ + tSirMacHTMIMOPowerSaveState htMIMOPSState; + uint8_t htGreenfield:1; + uint8_t htShortGI40Mhz:1; + uint8_t htShortGI20Mhz:1; + /* DSSS/CCK at 40 MHz: Enabled 1 or Disabled */ + uint8_t htDsssCckRate40MHzSupport:1; + /* L-SIG TXOP Protection used only if peer support available */ + uint8_t htLsigTXOPProtection:1; + /* + * A-MPDU Density + * 000 - No restriction + * 001 - 1/8 usec + * 010 - 1/4 usec + * 011 - 1/2 usec + * 100 - 1 usec + * 101 - 2 usec + * 110 - 4 usec + * 111 - 8 usec + */ + uint8_t htAMpduDensity:3; + /* Set to 0 for 3839 octets */ + /* Set to 1 for 7935 octets */ + uint8_t htMaxAmsduLength; + /* */ + /* Maximum Rx A-MPDU factor */ + uint8_t htMaxRxAMpduFactor:3; + /* + * Recommended Tx Width Set + * 0 - use 20 MHz channel (control channel) + * 1 - use 40 Mhz channel + */ + uint8_t htSupportedChannelWidthSet:1; + uint8_t htSecondaryChannelOffset:2; + uint16_t assocId; /* Association ID */ + uint8_t staAddr[6]; + uint8_t staType; + + uint8_t vhtSupportedChannelWidthSet; + enum phy_ch_width ch_width; + uint8_t vhtSupportedRxNss; + uint8_t vhtBeamFormerCapable; + uint8_t vht_su_bfee_capable; + uint8_t vht_mcs_10_11_supp; + uint8_t vht_160mhz_nss; + uint8_t vht_80p80mhz_nss; + uint8_t vht_extended_nss_bw_cap; +#ifdef WLAN_FEATURE_11W + TX_TIMER pmfSaQueryTimer; + uint16_t pmfSaQueryCurrentTransId; + uint16_t pmfSaQueryStartTransId; + uint8_t pmfSaQueryState; + uint8_t pmfSaQueryRetryCount; +#endif + uint8_t htLdpcCapable; + uint8_t vhtLdpcCapable; +#ifdef FEATURE_WLAN_TDLS + uint16_t ht_caps; + uint32_t vht_caps; +#endif + uint8_t timingMeasCap; + /* key installed for this STA or not in the firmware */ + uint8_t is_key_installed; + uint8_t is_disassoc_deauth_in_progress; + + uint8_t nss; + int8_t del_sta_ctx_rssi; + bool sta_deletion_in_progress; + /* Flag indicating connected STA doesn't support ECSA */ + uint8_t non_ecsa_capable; + struct parsed_ies parsed_ies; + +#ifdef WLAN_FEATURE_11AX + tDot11fIEhe_cap he_config; +#endif + /* Peer operation class, extracted from ASSOC request frame*/ + tDot11fIESuppOperatingClasses supp_operating_classes; + /* + * When a station with already an existing dph entry tries to + * associate again, the old dph entry will be zeroed out except + * for the next pointer. The next pointer must be defined at the + * end of the structure. + */ + struct sDphHashNode *next; +} tDphHashNode, *tpDphHashNode; + +#include "dph_hash_table.h" + +/* ------------------------------------------------------------------- */ +typedef struct sAniSirDph { + /* The hash table object */ + struct dph_hash_table dphHashTable; +} tAniSirDph, *tpAniSirDph; + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/include/parser_api.h b/drivers/staging/qcacld-3.0/core/mac/src/include/parser_api.h new file mode 100644 index 0000000000000000000000000000000000000000..7c98e4213563f2801a0a5f8a1adb27279d287781 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/include/parser_api.h @@ -0,0 +1,1261 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file parser_api.h contains the definitions used + * for parsing received 802.11 frames + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#ifndef __PARSE_H__ +#define __PARSE_H__ + +#include +#include "sir_mac_prop_exts.h" +#include "dot11f.h" +#include "lim_ft_defs.h" +#include "lim_session.h" + +#define COUNTRY_STRING_LENGTH (3) +#define COUNTRY_INFO_MAX_CHANNEL (84) +#define MAX_SIZE_OF_TRIPLETS_IN_COUNTRY_IE (COUNTRY_STRING_LENGTH * \ + COUNTRY_INFO_MAX_CHANNEL) +#define HIGHEST_24GHZ_CHANNEL_NUM (14) + +#define IS_24G_CH(__chNum) ((__chNum > 0) && (__chNum < 15)) +#define IS_5G_CH(__chNum) ((__chNum >= 36) && (__chNum <= 165)) +#define IS_2X2_CHAIN(__chain) ((__chain & 0x3) == 0x3) +#define DISABLE_NSS2_MCS 0xC +#define VHT_1x1_MCS9_MAP 0x2 +#define VHT_2x2_MCS9_MAP 0xA +#define VHT_1x1_MCS8_VAL 0xFFFD +#define VHT_2x2_MCS8_VAL 0xFFF5 +#define VHT_1x1_MCS_MASK 0x3 +#define VHT_2x2_MCS_MASK 0xF +#define DISABLE_VHT_MCS_9(mcs, nss) \ + (mcs = (nss > 1) ? VHT_2x2_MCS8_VAL : VHT_1x1_MCS8_VAL) + +#define NSS_1x1_MODE 1 +#define NSS_2x2_MODE 2 +#define NSS_3x3_MODE 3 +#define NSS_4x4_MODE 4 +#define MBO_IE_ASSOC_DISALLOWED_SUBATTR_ID 0x04 + +/* QCN IE definitions */ +#define QCN_IE_HDR_LEN 6 + +#define QCN_IE_VERSION_SUBATTR_ID 1 +#define QCN_IE_VERSION_SUBATTR_DATA_LEN 2 +#define QCN_IE_VERSION_SUBATTR_LEN 4 +#define QCN_IE_VERSION_SUPPORTED 1 +#define QCN_IE_SUBVERSION_SUPPORTED 0 + +#define QCN_IE_ATTR_ID_VERSION 1 +#define QCN_IE_ATTR_ID_VHT_MCS11 2 +#define QCN_IE_ATTR_ID_ALL 0xFF + +#define SIZE_OF_FIXED_PARAM 12 +#define SIZE_OF_TAG_PARAM_NUM 1 +#define SIZE_OF_TAG_PARAM_LEN 1 +#define RSNIEID 0x30 +#define RSNIE_CAPABILITY_LEN 2 +#define DEFAULT_RSNIE_CAP_VAL 0x00 + +#define SIZE_MASK 0x7FFF +#define FIXED_MASK 0x8000 + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +#define QCOM_VENDOR_IE_MCC_AVOID_CH 0x01 + +struct sAvoidChannelIE { + /* following must be 0xDD (221) */ + uint8_t tag_number; + uint8_t length; + /* following must be 00-A0-C6 */ + uint8_t oui[3]; + /* following must be 0x01 */ + uint8_t type; + uint8_t channel; +}; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +typedef struct sSirCountryInformation { + uint8_t countryString[COUNTRY_STRING_LENGTH]; + uint8_t numIntervals; /* number of channel intervals */ + struct channelPowerLim { + uint8_t channelNumber; + uint8_t numChannel; + uint8_t maxTransmitPower; + } channelTransmitPower[COUNTRY_INFO_MAX_CHANNEL]; +} tSirCountryInformation, *tpSirCountryInformation; + +#ifdef WLAN_FEATURE_FILS_SK +#define SIR_MAX_IDENTIFIER_CNT 7 +#define SIR_CACHE_IDENTIFIER_LEN 2 +#define SIR_HESSID_LEN 6 +#define SIR_MAX_KEY_CNT 7 +#define SIR_MAX_KEY_LEN 48 +#define SIR_FILS_IND_ELEM_OFFSET 2 +/* + * struct public_key_identifier: structure for public key identifier + * present in fils indication element + * @is_present: if Key info is present + * @key_cnt: number of keys present + * @key_type: type of key used + * @length: length of key + * @key: key data + */ +struct public_key_identifier { + bool is_present; + uint8_t key_cnt; + uint8_t key_type; + uint8_t length; + uint8_t key[SIR_MAX_KEY_CNT][SIR_MAX_KEY_LEN]; +}; + +/* + * struct fils_cache_identifier: structure for fils cache identifier + * present in fils indication element + * @is_present: if cache identifier is present + * @identifier: cache identifier + */ +struct fils_cache_identifier { + bool is_present; + uint8_t identifier[SIR_CACHE_IDENTIFIER_LEN]; +}; + +/* + * struct fils_hessid: structure for fils hessid + * present in fils indication element + * @is_present: if hessid info is present + * @hessid: hessid data + */ +struct fils_hessid { + bool is_present; + uint8_t hessid[SIR_HESSID_LEN]; +}; + +/* + * struct fils_realm_identifier: structure for fils_realm_identifier + * present in fils indication element + * @is_present: if realm info is present + * @realm_cnt: realm count + * @realm: realm data + */ +struct fils_realm_identifier { + bool is_present; + uint8_t realm_cnt; + uint8_t realm[SIR_MAX_REALM_COUNT][SIR_REALM_LEN]; +}; + +/* + * struct sir_fils_indication: structure for fils indication element + * @is_present: if indication element is present + * @is_ip_config_supported: if IP config is supported + * @is_fils_sk_auth_supported: if fils sk suppprted + * @is_fils_sk_auth_pfs_supported: if fils sk with pfs supported + * @is_pk_auth_supported: if fils public key supported + * @cache_identifier: fils cache idenfier info + * @hessid: fils hessid info + * @realm_identifier: fils realm info + * @key_identifier: fils key identifier info + */ +struct sir_fils_indication { + bool is_present; + uint8_t is_ip_config_supported; + uint8_t is_fils_sk_auth_supported; + uint8_t is_fils_sk_auth_pfs_supported; + uint8_t is_pk_auth_supported; + struct fils_cache_identifier cache_identifier; + struct fils_hessid hessid; + struct fils_realm_identifier realm_identifier; + struct public_key_identifier key_identifier; +}; +#endif + +/* Structure common to Beacons & Probe Responses */ +typedef struct sSirProbeRespBeacon { + tSirMacTimeStamp timeStamp; + uint16_t beaconInterval; + tSirMacCapabilityInfo capabilityInfo; + + tSirMacSSid ssId; + tSirMacRateSet supportedRates; + tSirMacRateSet extendedRates; + uint32_t chan_freq; + tSirMacCfParamSet cfParamSet; + tSirMacTim tim; + tSirMacEdcaParamSetIE edcaParams; + tSirMacQosCapabilityIE qosCapability; + + tSirCountryInformation countryInfoParam; + tSirMacWpaInfo wpa; + tSirMacRsnInfo rsn; + + tSirMacErpInfo erpIEInfo; + + tDot11fIEPowerConstraints localPowerConstraint; + tDot11fIETPCReport tpcReport; + tDot11fIEChanSwitchAnn channelSwitchIE; + tDot11fIEsec_chan_offset_ele sec_chan_offset; + tDot11fIEext_chan_switch_ann ext_chan_switch; + tDot11fIESuppOperatingClasses supp_operating_classes; + tSirMacAddr bssid; + tDot11fIEQuiet quietIE; + tDot11fIEHTCaps HTCaps; + tDot11fIEHTInfo HTInfo; + tDot11fIEP2PProbeRes P2PProbeRes; + uint8_t mdie[SIR_MDIE_SIZE]; +#ifdef FEATURE_WLAN_ESE + tDot11fIEESETxmitPower eseTxPwr; + tDot11fIEQBSSLoad QBSSLoad; +#endif + uint8_t ssidPresent; + uint8_t suppRatesPresent; + uint8_t extendedRatesPresent; + uint8_t supp_operating_class_present; + uint8_t cfPresent; + uint8_t dsParamsPresent; + uint8_t timPresent; + + uint8_t edcaPresent; + uint8_t qosCapabilityPresent; + uint8_t wmeEdcaPresent; + uint8_t wmeInfoPresent; + uint8_t wsmCapablePresent; + + uint8_t countryInfoPresent; + uint8_t wpaPresent; + uint8_t rsnPresent; + uint8_t erpPresent; + uint8_t channelSwitchPresent; + uint8_t sec_chan_offset_present; + uint8_t ext_chan_switch_present; + uint8_t quietIEPresent; + uint8_t tpcReportPresent; + uint8_t powerConstraintPresent; + + uint8_t mdiePresent; + + tDot11fIEVHTCaps VHTCaps; + tDot11fIEVHTOperation VHTOperation; + tDot11fIEVHTExtBssLoad VHTExtBssLoad; + tDot11fIEExtCap ext_cap; + tDot11fIEOperatingMode OperatingMode; + uint8_t WiderBWChanSwitchAnnPresent; + tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn; + uint8_t Vendor1IEPresent; + tDot11fIEvendor_vht_ie vendor_vht_ie; + uint8_t Vendor3IEPresent; + tDot11fIEhs20vendor_ie hs20vendor_ie; + tDot11fIEIBSSParams IBSSParams; +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + tDot11fIEQComVendorIE AvoidChannelIE; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ +#ifdef FEATURE_WLAN_ESE + uint8_t is_ese_ver_ie_present; +#endif + tDot11fIEOBSSScanParameters obss_scanparams; + bool MBO_IE_present; + uint8_t MBO_capability; + bool assoc_disallowed; + uint8_t assoc_disallowed_reason; + tDot11fIEqcn_ie qcn_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_op he_op; +#ifdef WLAN_FEATURE_11AX_BSS_COLOR + tDot11fIEbss_color_change vendor_he_bss_color_change; +#endif +#ifdef WLAN_FEATURE_FILS_SK + struct sir_fils_indication fils_ind; +#endif +} tSirProbeRespBeacon, *tpSirProbeRespBeacon; + +/* probe Request structure */ +typedef struct sSirProbeReq { + tSirMacSSid ssId; + tSirMacRateSet supportedRates; + tSirMacRateSet extendedRates; + tDot11fIEWscProbeReq probeReqWscIeInfo; + tDot11fIEHTCaps HTCaps; + uint8_t ssidPresent; + uint8_t suppRatesPresent; + uint8_t extendedRatesPresent; + uint8_t wscIePresent; + uint8_t p2pIePresent; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEhe_cap he_cap; +} tSirProbeReq, *tpSirProbeReq; + +/* / Association Request structure (one day to be replaced by */ +/* / tDot11fAssocRequest) */ +typedef struct sSirAssocReq { + + tSirMacCapabilityInfo capabilityInfo; + uint16_t listenInterval; + tSirMacAddr currentApAddr; /* only in reassoc frames */ + tSirMacSSid ssId; + tSirMacRateSet supportedRates; + tSirMacRateSet extendedRates; + + tSirAddtsReqInfo addtsReq; + tSirMacQosCapabilityStaIE qosCapability; + + tSirMacWapiInfo wapi; + tSirMacWpaInfo wpa; + tSirMacRsnInfo rsn; + tSirAddie addIE; + + tSirMacPowerCapabilityIE powerCapability; + tSirMacSupportedChannelIE supportedChannels; + tDot11fIEHTCaps HTCaps; + tDot11fIEWMMInfoStation WMMInfoStation; + tDot11fIESuppOperatingClasses supp_operating_classes; + /* / This is set if the frame is a reassoc request: */ + uint8_t reassocRequest; + uint8_t ssidPresent; + uint8_t suppRatesPresent; + uint8_t extendedRatesPresent; + + uint8_t wmeInfoPresent; + uint8_t qosCapabilityPresent; + uint8_t addtsPresent; + uint8_t wsmCapablePresent; + + uint8_t wapiPresent; + uint8_t wpaPresent; + uint8_t rsnPresent; + uint8_t addIEPresent; + + uint8_t powerCapabilityPresent; + uint8_t supportedChannelsPresent; + /* keeping copy of association request received, this is + required for indicating the frame to upper layers */ + uint32_t assocReqFrameLength; + uint8_t *assocReqFrame; + tDot11fIEVHTCaps VHTCaps; + tDot11fIEOperatingMode operMode; + tDot11fIEExtCap ExtCap; + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEhs20vendor_ie hs20vendor_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; + tDot11fIEqcn_ie qcn_ie; + bool is_sae_authenticated; +} tSirAssocReq, *tpSirAssocReq; + +#define FTIE_SUBELEM_R1KH_ID 1 +#define FTIE_SUBELEM_GTK 2 +#define FTIE_SUBELEM_R0KH_ID 3 +#define FTIE_SUBELEM_IGTK 4 +#define FTIE_SUBELEM_OCI 5 + +#define FTIE_R1KH_LEN 6 +#define FTIE_R0KH_MAX_LEN 48 + +/** + * struct wlan_sha384_ftinfo_subelem - subelements of FTIE + * @r1kh_id: FT R1 Key holder ID + * @gtk: Ft group temporal key + * @gtk_len: GTK length + * @r0kh_id: FT R0 Key Holder ID + * @igtk: FT IGTK used for 11w + * @igtk_len: IGTK length + */ +struct wlan_sha384_ftinfo_subelem { + tDot11fIER1KH_ID r1kh_id; + uint8_t *gtk; + uint8_t gtk_len; + tDot11fIER0KH_ID r0kh_id; + uint8_t *igtk; + uint8_t igtk_len; +}; + +#define MIC_CONTROL_BYTES 2 +#define MIC_SHA384_BYTES 24 +#define NONCE_BYTES 32 + +/** + * struct wlan_sha384_ftinfo - FTE for sha384 based AKMs + * @mic_control: FTIE mic control field of 2 bytes + * @mic: MIC present in the FTIE assoc Response + * @anonce: Anonce sent by the AP + * @snonce: Snonce field in the FTIE + */ +struct wlan_sha384_ftinfo { + uint8_t mic_control[MIC_CONTROL_BYTES]; + uint8_t mic[MIC_SHA384_BYTES]; + uint8_t anonce[NONCE_BYTES]; + uint8_t snonce[NONCE_BYTES]; +}; + +/* / Association Response structure (one day to be replaced by */ +/* / tDot11fAssocRequest) */ +typedef struct sSirAssocRsp { + + tSirMacCapabilityInfo capabilityInfo; + uint16_t aid; + uint16_t status_code; + tSirMacRateSet supportedRates; + tSirMacRateSet extendedRates; + tSirMacEdcaParamSetIE edca; + tSirAddtsRspInfo addtsRsp; + tDot11fIEHTCaps HTCaps; + tDot11fIEHTInfo HTInfo; + tDot11fIEFTInfo FTInfo; + struct wlan_sha384_ftinfo sha384_ft_info; + struct wlan_sha384_ftinfo_subelem sha384_ft_subelem; + uint8_t mdie[SIR_MDIE_SIZE]; + uint8_t num_RICData; + tDot11fIERICDataDesc RICData[2]; + +#ifdef FEATURE_WLAN_ESE + uint8_t num_tspecs; + tDot11fIEWMMTSPEC TSPECInfo[SIR_ESE_MAX_TSPEC_IES]; + struct ese_tsm_ie tsmIE; +#endif + + uint8_t suppRatesPresent; + uint8_t extendedRatesPresent; + + uint8_t edcaPresent; + uint8_t wmeEdcaPresent; + uint8_t addtsPresent; + uint8_t wsmCapablePresent; + uint8_t ftinfoPresent; + uint8_t mdiePresent; + uint8_t ricPresent; +#ifdef FEATURE_WLAN_ESE + uint8_t tspecPresent; + uint8_t tsmPresent; +#endif + tDot11fIEVHTCaps VHTCaps; + tDot11fIEVHTOperation VHTOperation; + tDot11fIEExtCap ExtCap; + struct qos_map_set QosMapSet; +#ifdef WLAN_FEATURE_11W + tDot11fIETimeoutInterval TimeoutInterval; +#endif + tDot11fIEvendor_vht_ie vendor_vht_ie; + tDot11fIEOBSSScanParameters obss_scanparams; + tDot11fTLVrssi_assoc_rej rssi_assoc_rej; + tDot11fIEqcn_ie qcn_ie; + tDot11fIEhe_cap he_cap; + tDot11fIEhe_op he_op; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_cap; + bool mu_edca_present; + tSirMacEdcaParamSetIE mu_edca; +#ifdef WLAN_FEATURE_FILS_SK + tDot11fIEfils_session fils_session; + tDot11fIEfils_key_confirmation fils_key_auth; + tDot11fIEfils_kde fils_kde; + struct qdf_mac_addr dst_mac; + struct qdf_mac_addr src_mac; + uint16_t hlp_data_len; + uint8_t hlp_data[FILS_MAX_HLP_DATA_LEN]; +#endif +} tSirAssocRsp, *tpSirAssocRsp; + +#ifdef FEATURE_WLAN_ESE +/* Structure to hold ESE Beacon report mandatory IEs */ +typedef struct sSirEseBcnReportMandatoryIe { + tSirMacSSid ssId; + tSirMacRateSet supportedRates; + tSirMacFHParamSet fhParamSet; + tSirMacDsParamSetIE dsParamSet; + tSirMacCfParamSet cfParamSet; + tSirMacIBSSParams ibssParamSet; + tSirMacTim tim; + tSirMacRRMEnabledCap rmEnabledCapabilities; + + uint8_t ssidPresent; + uint8_t suppRatesPresent; + uint8_t fhParamPresent; + uint8_t dsParamsPresent; + uint8_t cfPresent; + uint8_t ibssParamPresent; + uint8_t timPresent; + uint8_t rrmPresent; +} tSirEseBcnReportMandatoryIe, *tpSirEseBcnReportMandatoryIe; +#endif /* FEATURE_WLAN_ESE */ + +/** + * struct s_ext_cap - holds bitfields of extended capability IE + * + * s_ext_cap holds bitfields of extended capability IE. In dot11f files + * extended capability IE information is stored as an array of bytes. + * This structure is used to encode/decode the byte array present in + * dot11f IE structure. + */ + +struct s_ext_cap { + uint8_t bss_coexist_mgmt_support:1; + uint8_t reserved1:1; + uint8_t ext_chan_switch:1; + uint8_t reserved2:1; + uint8_t psmp_cap:1; + uint8_t reserved3:1; + uint8_t spsmp_cap:1; + uint8_t event:1; + uint8_t diagnostics:1; + uint8_t multi_diagnostics:1; + uint8_t loc_tracking:1; + uint8_t fms:1; + uint8_t proxy_arp_service:1; + uint8_t co_loc_intf_reporting:1; + uint8_t civic_loc:1; + uint8_t geospatial_loc:1; + uint8_t tfs:1; + uint8_t wnm_sleep_mode:1; + uint8_t tim_broadcast:1; + uint8_t bss_transition:1; + uint8_t qos_traffic_cap:1; + uint8_t ac_sta_cnt:1; + uint8_t multi_bssid:1; + uint8_t timing_meas:1; + uint8_t chan_usage:1; + uint8_t ssid_list:1; + uint8_t dms:1; + uint8_t utctsf_offset:1; + uint8_t tdls_peer_uapsd_buffer_sta:1; + uint8_t tdls_peer_psm_supp:1; + uint8_t tdls_channel_switching:1; + uint8_t interworking_service:1; + uint8_t qos_map:1; + uint8_t ebr:1; + uint8_t sspn_interface:1; + uint8_t reserved4:1; + uint8_t msg_cf_cap:1; + uint8_t tdls_support:1; + uint8_t tdls_prohibited:1; + uint8_t tdls_chan_swit_prohibited:1; + uint8_t reject_unadmitted_traffic:1; + uint8_t service_interval_granularity:3; + uint8_t identifier_loc:1; + uint8_t uapsd_coexistence:1; + uint8_t wnm_notification:1; + uint8_t qa_bcapbility:1; + uint8_t utf8_ssid:1; + uint8_t qmf_activated:1; + uint8_t qm_frecon_act:1; + uint8_t robust_av_streaming:1; + uint8_t advanced_gcr:1; + uint8_t mesh_gcr:1; + uint8_t scs:1; + uint8_t q_load_report:1; + uint8_t alternate_edca:1; + uint8_t unprot_txo_pneg:1; + uint8_t prot_txo_pneg:1; + uint8_t reserved6:1; + uint8_t prot_q_load_report:1; + uint8_t tdls_wider_bw:1; + uint8_t oper_mode_notification:1; + uint8_t max_num_of_msdu_bit1:1; + uint8_t max_num_of_msdu_bit2:1; + uint8_t chan_sch_mgmt:1; + uint8_t geo_db_inband_en_signal:1; + uint8_t nw_chan_control:1; + uint8_t white_space_map:1; + uint8_t chan_avail_query:1; + uint8_t fine_time_meas_responder:1; + uint8_t fine_time_meas_initiator:1; + uint8_t fils_capability:1; + uint8_t ext_spectrum_management:1; + uint8_t future_channel_guidance:1; + uint8_t reserved7:2; + uint8_t twt_requestor_support:1; + uint8_t twt_responder_support:1; +}; + +void swap_bit_field16(uint16_t in, uint16_t *out); + +/* Currently implemented as "shims" between callers & the new framesc- */ +/* generated code: */ + +QDF_STATUS +sir_convert_probe_req_frame2_struct(struct mac_context *mac, + uint8_t *frame, uint32_t len, + tpSirProbeReq probe); + +QDF_STATUS +sir_convert_probe_frame2_struct(struct mac_context *mac, uint8_t *frame, + uint32_t len, tpSirProbeRespBeacon probe); + +QDF_STATUS +sir_convert_assoc_req_frame2_struct(struct mac_context *mac, + uint8_t *frame, uint32_t len, + tpSirAssocReq assoc); +/** + * wlan_parse_ftie_sha384() - Parse the FT IE if akm uses sha384 KDF + * @frame: Pointer to the association response frame + * @frame_len: Length of the assoc response frame + * @assoc_rsp: Destination assoc response structure in PE to which the FTIE + * needs to be parsed and copied + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_parse_ftie_sha384(uint8_t *frame, uint32_t frame_len, + struct sSirAssocRsp *assoc_rsp); + +QDF_STATUS +sir_convert_assoc_resp_frame2_struct(struct mac_context *mac, + struct pe_session *session_entry, + uint8_t *frame, uint32_t len, + tpSirAssocRsp assoc); + +QDF_STATUS +sir_convert_reassoc_req_frame2_struct(struct mac_context *mac, + uint8_t *frame, uint32_t len, + tpSirAssocReq assoc); + +QDF_STATUS +sir_parse_beacon_ie(struct mac_context *mac, + tpSirProbeRespBeacon pBeaconStruct, + uint8_t *pPayload, uint32_t payloadLength); + +QDF_STATUS +sir_convert_beacon_frame2_struct(struct mac_context *mac, + uint8_t *pBeaconFrame, + tpSirProbeRespBeacon pBeaconStruct); + +QDF_STATUS +sir_convert_auth_frame2_struct(struct mac_context *mac, + uint8_t *frame, uint32_t len, + tpSirMacAuthFrameBody auth); + +QDF_STATUS +sir_convert_addts_rsp2_struct(struct mac_context *mac, + uint8_t *frame, uint32_t len, + tSirAddtsRspInfo *addts); + +QDF_STATUS +sir_convert_delts_req2_struct(struct mac_context *mac, + uint8_t *frame, uint32_t len, + struct delts_req_info *delTs); +QDF_STATUS +sir_convert_qos_map_configure_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, uint32_t nFrame, + struct qos_map_set *pQosMapSet); + +#ifdef ANI_SUPPORT_11H +QDF_STATUS +sir_convert_tpc_req_frame2_struct(struct mac_context *, uint8_t *, + tpSirMacTpcReqActionFrame, uint32_t); + +QDF_STATUS +sir_convert_meas_req_frame2_struct(struct mac_context *, uint8_t *, + tpSirMacMeasReqActionFrame, uint32_t); +#endif + +/** + * \brief Populated a tDot11fFfCapabilities + * + * \param mac Pointer to the global MAC data structure + * + * \param pDot11f Address of a tDot11fFfCapabilities to be filled in + * + * + * \note If SIR_MAC_PROP_CAPABILITY_11EQOS is enabled, we'll clear the QOS + * bit in pDot11f + * + * + */ + +QDF_STATUS +populate_dot11f_capabilities(struct mac_context *mac, + tDot11fFfCapabilities *pDot11f, + struct pe_session *pe_session); + +/* / Populate a tDot11fIEChanSwitchAnn */ +void +populate_dot11f_chan_switch_ann(struct mac_context *mac, + tDot11fIEChanSwitchAnn *pDot11f, + struct pe_session *pe_session); + +void +populate_dot_11_f_ext_chann_switch_ann(struct mac_context *mac_ptr, + tDot11fIEext_chan_switch_ann *dot_11_ptr, + struct pe_session *session_entry); + +void +populate_dot11f_vht_tx_power_env(struct mac_context *mac, + tDot11fIEvht_transmit_power_env *pDot11f, + enum phy_ch_width ch_width, uint32_t chan_freq); +/* / Populate a tDot11fIEChannelSwitchWrapper */ +void +populate_dot11f_chan_switch_wrapper(struct mac_context *mac, + tDot11fIEChannelSwitchWrapper *pDot11f, + struct pe_session *pe_session); + +/* / Populate a tDot11fIECountry */ +QDF_STATUS +populate_dot11f_country(struct mac_context *mac, + tDot11fIECountry *pDot11f, struct pe_session *pe_session); + +/* Populated a populate_dot11f_ds_params */ +QDF_STATUS +populate_dot11f_ds_params(struct mac_context *mac, + tDot11fIEDSParams *pDot11f, uint8_t channel); + +/* / Populated a tDot11fIEEDCAParamSet */ +void +populate_dot11f_edca_param_set(struct mac_context *mac, + tDot11fIEEDCAParamSet *pDot11f, + struct pe_session *pe_session); + +QDF_STATUS +populate_dot11f_erp_info(struct mac_context *mac, + tDot11fIEERPInfo *pDot11f, struct pe_session *pe_session); + +QDF_STATUS +populate_dot11f_ext_supp_rates(struct mac_context *mac, + uint8_t nChannelNum, tDot11fIEExtSuppRates *pDot11f, + struct pe_session *pe_session); + +/** + * populate_dot11f_beacon_report() - Populate the Beacon Report IE + * @mac: Pointer to the global MAC context + * @pDot11f: Pointer to the measurement report structure + * @pBeaconReport: Pointer to the Beacon Report structure + * @is_last_frame: is the current report last or more reports to follow + * + * Return: Ret Status + */ +QDF_STATUS +populate_dot11f_beacon_report(struct mac_context *mac, + tDot11fIEMeasurementReport *pDot11f, + tSirMacBeaconReport *pBeaconReport, + bool is_last_frame); + +/** + * \brief Populate a tDot11fIEExtSuppRates + * + * + * \param mac Pointer to the global MAC data structure + * + * \param nChannelNum Channel on which the enclosing frame will be going out + * + * \param pDot11f Address of a tDot11fIEExtSuppRates struct to be filled in. + * + * + * This method is a NOP if the channel is greater than 14. + * + * + */ + +QDF_STATUS +populate_dot11f_ext_supp_rates1(struct mac_context *mac, + uint8_t nChannelNum, + tDot11fIEExtSuppRates *pDot11f); + +QDF_STATUS +populate_dot11f_ht_caps(struct mac_context *mac, + struct pe_session *pe_session, tDot11fIEHTCaps *pDot11f); + +QDF_STATUS +populate_dot11f_ht_info(struct mac_context *mac, + tDot11fIEHTInfo *pDot11f, struct pe_session *pe_session); + +void populate_dot11f_ibss_params(struct mac_context *mac, + tDot11fIEIBSSParams *pDot11f, + struct pe_session *pe_session); + +#ifdef ANI_SUPPORT_11H +QDF_STATUS +populate_dot11f_measurement_report0(struct mac_context *mac, + tpSirMacMeasReqActionFrame pReq, + tDot11fIEMeasurementReport *pDot11f); + +/* / Populate a tDot11fIEMeasurementReport when the report type is CCA */ +QDF_STATUS +populate_dot11f_measurement_report1(struct mac_context *mac, + tpSirMacMeasReqActionFrame pReq, + tDot11fIEMeasurementReport *pDot11f); + +/* / Populate a tDot11fIEMeasurementReport when the report type is RPI Hist */ +QDF_STATUS +populate_dot11f_measurement_report2(struct mac_context *mac, + tpSirMacMeasReqActionFrame pReq, + tDot11fIEMeasurementReport *pDot11f); +#endif /* ANI_SUPPORT_11H */ + +/* / Populate a tDot11fIEPowerCaps */ +void +populate_dot11f_power_caps(struct mac_context *mac, + tDot11fIEPowerCaps *pCaps, + uint8_t nAssocType, struct pe_session *pe_session); + +/* / Populate a tDot11fIEPowerConstraints */ +QDF_STATUS +populate_dot11f_power_constraints(struct mac_context *mac, + tDot11fIEPowerConstraints *pDot11f); + +void +populate_dot11f_qos_caps_station(struct mac_context *mac, struct pe_session *session, + tDot11fIEQOSCapsStation *pDot11f); + +QDF_STATUS +populate_dot11f_rsn(struct mac_context *mac, + tpSirRSNie pRsnIe, tDot11fIERSN *pDot11f); + +QDF_STATUS +populate_dot11f_rsn_opaque(struct mac_context *mac, + tpSirRSNie pRsnIe, tDot11fIERSNOpaque *pDot11f); + +#if defined(FEATURE_WLAN_WAPI) + +QDF_STATUS +populate_dot11f_wapi(struct mac_context *mac, + tpSirRSNie pRsnIe, tDot11fIEWAPI *pDot11f); + +QDF_STATUS populate_dot11f_wapi_opaque(struct mac_context *mac, + tpSirRSNie pRsnIe, + tDot11fIEWAPIOpaque *pDot11f); + +#endif /* defined(FEATURE_WLAN_WAPI) */ + +/* / Populate a tDot11fIESSID given a tSirMacSSid */ +void +populate_dot11f_ssid(struct mac_context *mac, + tSirMacSSid *pInternal, tDot11fIESSID *pDot11f); + +/* / Populate a tDot11fIESSID from CFG */ +QDF_STATUS populate_dot11f_ssid2(struct mac_context *mac, + tDot11fIESSID *pDot11f); + +/** + * \brief Populate a tDot11fIESchedule + * + * \sa populate_dot11f_wmm_schedule + * + * + * \param pSchedule Address of a tSirMacScheduleIE struct + * + * \param pDot11f Address of a tDot11fIESchedule to be filled in + * + * + */ + +void +populate_dot11f_schedule(tSirMacScheduleIE *pSchedule, + tDot11fIESchedule *pDot11f); + +void +populate_dot11f_supp_channels(struct mac_context *mac, + tDot11fIESuppChannels *pDot11f, + uint8_t nAssocType, struct pe_session *pe_session); + +/** + * \brief Populated a tDot11fIESuppRates + * + * + * \param mac Pointer to the global MAC data structure + * + * \param nChannelNum Channel the enclosing frame will be going out on; see + * below + * + * \param pDot11f Address of a tDot11fIESuppRates struct to be filled in. + * + * + * If nChannelNum is greater than 13, the supported rates will be + * WNI_CFG_SUPPORTED_RATES_11B. If it is less than or equal to 13, the + * supported rates will be WNI_CFG_SUPPORTED_RATES_11A. If nChannelNum is + * set to the sentinel value POPULATE_DOT11F_RATES_OPERATIONAL, the struct + * will be populated with WNI_CFG_OPERATIONAL_RATE_SET. + * + * + */ + +#define POPULATE_DOT11F_RATES_OPERATIONAL (0xff) + +QDF_STATUS +populate_dot11f_supp_rates(struct mac_context *mac, + uint8_t nChannelNum, + tDot11fIESuppRates *pDot11f, struct pe_session *); + +QDF_STATUS +populate_dot11f_rates_tdls(struct mac_context *p_mac, + tDot11fIESuppRates *p_supp_rates, + tDot11fIEExtSuppRates *p_ext_supp_rates, + uint8_t curr_oper_channel); + +QDF_STATUS populate_dot11f_tpc_report(struct mac_context *mac, + tDot11fIETPCReport *pDot11f, + struct pe_session *pe_session); + +/* / Populate a tDot11FfTSInfo */ +void populate_dot11f_ts_info(struct mac_ts_info *pInfo, + tDot11fFfTSInfo *pDot11f); + +void populate_dot11f_wmm(struct mac_context *mac, + tDot11fIEWMMInfoAp *pInfo, + tDot11fIEWMMParams *pParams, + tDot11fIEWMMCaps *pCaps, struct pe_session *pe_session); + +void populate_dot11f_wmm_caps(tDot11fIEWMMCaps *pCaps); + +#if defined(FEATURE_WLAN_ESE) +/* Fill the ESE version IE */ +void populate_dot11f_ese_version(tDot11fIEESEVersion *pESEVersion); +/* Fill the Radio Management Capability */ +void populate_dot11f_ese_rad_mgmt_cap(tDot11fIEESERadMgmtCap *pESERadMgmtCap); +/* Fill the CCKM IE */ +QDF_STATUS populate_dot11f_ese_cckm_opaque(struct mac_context *mac, + tpSirCCKMie pCCKMie, + tDot11fIEESECckmOpaque *pDot11f); + +void populate_dot11_tsrsie(struct mac_context *mac, + struct ese_tsrs_ie *pOld, + tDot11fIEESETrafStrmRateSet *pDot11f, + uint8_t rate_length); +void populate_dot11f_re_assoc_tspec(struct mac_context *mac, + tDot11fReAssocRequest *pReassoc, + struct pe_session *pe_session); +QDF_STATUS +sir_beacon_ie_ese_bcn_report(struct mac_context *mac, + uint8_t *pPayload, const uint32_t payloadLength, + uint8_t **outIeBuf, uint32_t *pOutIeLen); + +/** + * ese_populate_wmm_tspec() - Populates TSPEC info for + * reassoc + * @source: source structure + * @dest: destination structure + * + * This function copies TSPEC parameters from source + * structure to destination structure. + * + * Return: None + */ +void ese_populate_wmm_tspec(struct mac_tspec_ie *source, + ese_wmm_tspec_ie *dest); + +#endif + +void populate_dot11f_wmm_info_ap(struct mac_context *mac, + tDot11fIEWMMInfoAp *pInfo, + struct pe_session *pe_session); + +void populate_dot11f_wmm_info_station_per_session(struct mac_context *mac, + struct pe_session *pe_session, + tDot11fIEWMMInfoStation *pInfo); + +void populate_dot11f_wmm_params(struct mac_context *mac, + tDot11fIEWMMParams *pParams, + struct pe_session *pe_session); + +QDF_STATUS +populate_dot11f_wpa(struct mac_context *mac, + tpSirRSNie pRsnIe, tDot11fIEWPA *pDot11f); + +QDF_STATUS +populate_dot11f_wpa_opaque(struct mac_context *mac, + tpSirRSNie pRsnIe, tDot11fIEWPAOpaque *pDot11f); + +void populate_dot11f_tspec(struct mac_tspec_ie *pOld, tDot11fIETSPEC *pDot11f); + +void populate_dot11f_wmmtspec(struct mac_tspec_ie *pOld, + tDot11fIEWMMTSPEC *pDot11f); + +QDF_STATUS +populate_dot11f_tclas(struct mac_context *mac, + tSirTclasInfo *pOld, tDot11fIETCLAS *pDot11f); + +QDF_STATUS +populate_dot11f_wmmtclas(struct mac_context *mac, + tSirTclasInfo *pOld, tDot11fIEWMMTCLAS *pDot11f); + +QDF_STATUS populate_dot11f_wsc(struct mac_context *mac, + tDot11fIEWscBeacon *pDot11f); + +QDF_STATUS populate_dot11f_wsc_registrar_info(struct mac_context *mac, + tDot11fIEWscBeacon *pDot11f); + +QDF_STATUS de_populate_dot11f_wsc_registrar_info(struct mac_context *mac, + tDot11fIEWscBeacon *pDot11f); + +QDF_STATUS populate_dot11f_probe_res_wpsi_es(struct mac_context *mac, + tDot11fIEWscProbeRes *pDot11f, + struct pe_session *pe_session); +QDF_STATUS populate_dot11f_beacon_wpsi_es(struct mac_context *mac, + tDot11fIEWscBeacon *pDot11f, + struct pe_session *pe_session); + +QDF_STATUS populate_dot11f_wsc_in_probe_res(struct mac_context *mac, + tDot11fIEWscProbeRes *pDot11f); + +QDF_STATUS +populate_dot11f_wsc_registrar_info_in_probe_res(struct mac_context *mac, + tDot11fIEWscProbeRes *pDot11f); + +QDF_STATUS +de_populate_dot11f_wsc_registrar_info_in_probe_res(struct mac_context *mac, + tDot11fIEWscProbeRes *pDot11f); + +QDF_STATUS populate_dot11_assoc_res_p2p_ie(struct mac_context *mac, + tDot11fIEP2PAssocRes *pDot11f, + tpSirAssocReq pRcvdAssocReq); + +QDF_STATUS populate_dot11f_wfatpc(struct mac_context *mac, + tDot11fIEWFATPC *pDot11f, uint8_t txPower, + uint8_t linkMargin); + +QDF_STATUS populate_dot11f_rrm_ie(struct mac_context *mac, + tDot11fIERRMEnabledCap *pDot11f, + struct pe_session *pe_session); + +void populate_mdie(struct mac_context *mac, tDot11fIEMobilityDomain *pDot11f, + uint8_t mdie[]); + +#ifdef WLAN_FEATURE_FILS_SK +/** + * populate_fils_ft_info() - Populate FTIE into assoc request frame + * @mac: Global mac context + * @ft_info: pointer to assoc request frame FT IE buffer + * @pe_session: pointer to PE session + * + * Return: None + */ +void populate_fils_ft_info(struct mac_context *mac, tDot11fIEFTInfo *ft_info, + struct pe_session *pe_session); +#else +static inline +void populate_fils_ft_info(struct mac_context *mac, tDot11fIEFTInfo *ft_info, + struct pe_session *pe_session) +{} +#endif + +void populate_dot11f_assoc_rsp_rates(struct mac_context *mac, + tDot11fIESuppRates *pSupp, + tDot11fIEExtSuppRates *pExt, + uint16_t *_11bRates, uint16_t *_11aRates); + +int find_ie_location(struct mac_context *mac, tpSirRSNie pRsnIe, uint8_t EID); + +void lim_log_vht_cap(struct mac_context *mac, tDot11fIEVHTCaps *pDot11f); + +QDF_STATUS +populate_dot11f_vht_caps(struct mac_context *mac, struct pe_session *pe_session, + tDot11fIEVHTCaps *pDot11f); + +QDF_STATUS +populate_dot11f_vht_operation(struct mac_context *mac, + struct pe_session *pe_session, + tDot11fIEVHTOperation *pDot11f); + +QDF_STATUS +populate_dot11f_ext_cap(struct mac_context *mac, bool isVHTEnabled, + tDot11fIEExtCap *pDot11f, struct pe_session *pe_session); + +void populate_dot11f_qcn_ie(struct mac_context *mac, + tDot11fIEqcn_ie *qcn_ie, + uint8_t attr_id); + +#ifdef WLAN_FEATURE_FILS_SK +/** + * populate_dot11f_fils_params() - Populate FILS IE to frame + * @mac_ctx: global mac context + * @frm: Assoc request frame + * @pe_session: PE session + * + * This API is used to populate FILS IE to Association request + * + * Return: None + */ +void populate_dot11f_fils_params(struct mac_context *mac_ctx, + tDot11fAssocRequest * frm, + struct pe_session *pe_session); +#else +static inline void populate_dot11f_fils_params(struct mac_context *mac_ctx, + tDot11fAssocRequest *frm, + struct pe_session *pe_session) +{ } +#endif + +QDF_STATUS +populate_dot11f_operating_mode(struct mac_context *mac, + tDot11fIEOperatingMode *pDot11f, + struct pe_session *pe_session); + +void populate_dot11f_timeout_interval(struct mac_context *mac, + tDot11fIETimeoutInterval *pDot11f, + uint8_t type, uint32_t value); + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +/* Populate a tDot11fIEQComVendorIE */ +void +populate_dot11f_avoid_channel_ie(struct mac_context *mac_ctx, + tDot11fIEQComVendorIE *dot11f, + struct pe_session *session_entry); +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +QDF_STATUS populate_dot11f_timing_advert_frame(struct mac_context *mac, + tDot11fTimingAdvertisementFrame *frame); +void populate_dot11_supp_operating_classes(struct mac_context *mac_ptr, + tDot11fIESuppOperatingClasses *dot_11_ptr, struct pe_session *session_entry); + +QDF_STATUS +sir_validate_and_rectify_ies(struct mac_context *mac_ctx, + uint8_t *mgmt_frame, + uint32_t frame_bytes, + uint32_t *missing_rsn_bytes); +/** + * sir_copy_caps_info() - Copy Caps info from tDot11fFfCapabilities to + * beacon/probe response structure. + * @mac_ctx: MAC Context + * @caps: tDot11fFfCapabilities structure + * @pProbeResp: beacon/probe response structure + * + * Copy the caps info to beacon/probe response structure + * + * Return: None + */ +void sir_copy_caps_info(struct mac_context *mac_ctx, tDot11fFfCapabilities caps, + tpSirProbeRespBeacon pProbeResp); + +#ifdef WLAN_FEATURE_FILS_SK +/** + * update_fils_data: update fils params from beacon/probe response + * @fils_ind: pointer to sir_fils_indication + * @fils_indication: pointer to tDot11fIEfils_indication + * + * Return: None + */ +void update_fils_data(struct sir_fils_indication *fils_ind, + tDot11fIEfils_indication * fils_indication); +#endif +#ifdef WLAN_FEATURE_11AX +QDF_STATUS populate_dot11f_he_caps(struct mac_context *, struct pe_session *, + tDot11fIEhe_cap *); +QDF_STATUS populate_dot11f_he_operation(struct mac_context *, struct pe_session *, + tDot11fIEhe_op *); +/** + * populate_dot11f_he_6ghz_cap() - pouldate HE 6GHz caps IE + * @mac_ctx: Global MAC context + * @session: PE session + * @he_6g_cap: pointer to HE 6GHz IE + * + * Populdate the HE 6GHz IE based on the session. + */ +QDF_STATUS +populate_dot11f_he_6ghz_cap(struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fIEhe_6ghz_band_cap *he_6g_cap); +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +QDF_STATUS populate_dot11f_he_bss_color_change(struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fIEbss_color_change *bss_color); +#else +static inline QDF_STATUS populate_dot11f_he_bss_color_change( + struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fIEbss_color_change *bss_color) +{ + return QDF_STATUS_SUCCESS; +} +#endif +#else +static inline QDF_STATUS populate_dot11f_he_caps(struct mac_context *mac_ctx, + struct pe_session *session, tDot11fIEhe_cap *he_cap) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS populate_dot11f_he_operation(struct mac_context *mac_ctx, + struct pe_session *session, tDot11fIEhe_op *he_op) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +populate_dot11f_he_6ghz_cap(struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fIEhe_6ghz_band_cap *he_6g_cap) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS populate_dot11f_he_bss_color_change( + struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fIEbss_color_change *bss_color) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef WLAN_SUPPORT_TWT +/** + * populate_dot11f_twt_extended_caps() - populate TWT extended capabilities + * @mac_ctx: Global MAC context. + * @pe_session: Pointer to the PE session. + * @dot11f: Pointer to the extended capabilities of the session. + * + * Populate the TWT extended capabilities based on the target and INI support. + * + * Return: QDF_STATUS Success or Failure + */ +QDF_STATUS populate_dot11f_twt_extended_caps(struct mac_context *mac_ctx, + struct pe_session *pe_session, + tDot11fIEExtCap *dot11f); +#else +static inline +QDF_STATUS populate_dot11f_twt_extended_caps(struct mac_context *mac_ctx, + struct pe_session *pe_session, + tDot11fIEExtCap *dot11f) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * populate_dot11f_btm_caps() - populate btm extended capabilities + * @mac_ctx: Global MAC context. + * @pe_session: Pointer to the PE session. + * @dot11f: Pointer to the extended capabilities of the session. + * + * Disable btm for SAE types for Helium firmware limit + * + * Return: QDF_STATUS Success or Failure + */ +QDF_STATUS populate_dot11f_btm_caps(struct mac_context *mac_ctx, + struct pe_session *pe_session, + struct sDot11fIEExtCap *dot11f); + +/** + * lim_truncate_ppet: truncates ppet of trailling zeros + * @ppet: ppet to truncate + * max_len: max length of ppet + * + * Return: new length after truncation + */ +static inline uint32_t lim_truncate_ppet(uint8_t *ppet, uint32_t max_len) +{ + while (max_len) { + if (ppet[max_len - 1]) + break; + max_len--; + } + return max_len; +} +#endif /* __PARSE_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/include/sir_common.h b/drivers/staging/qcacld-3.0/core/mac/src/include/sir_common.h new file mode 100644 index 0000000000000000000000000000000000000000..66078b9bcea7a74e05d74238ab9a2e6b6ef9d884 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/include/sir_common.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011-2014 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file sir_common.h contains the common definitions used by all + * Firmware modules. + * + * Author: V. K. Kandarpa + * Date: 04/12/2002 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#ifndef __SIRCOMMON_H +#define __SIRCOMMON_H + +#include "sir_api.h" +#include "sir_params.h" +#include "sys_wrapper.h" + +/* ********************************************* * +* * +* SIRIUS SYSTEM EXTERNAL GLOBALS * +* * +* ********************************************* */ + +/* All the following are resource definitions */ + +#endif /* __SIRCOMMON_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/include/sir_debug.h b/drivers/staging/qcacld-3.0/core/mac/src/include/sir_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..68609756fc382d33134edd18f3656b556aaa5ff2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/include/sir_debug.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2011-2012, 2014-2015, 2017-2019, 2021 The Linux Foundation. All + * rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Author: Sandesh Goel + * Date: 02/25/02 + */ + +#ifndef __POL_DEBUG_H__ +#define __POL_DEBUG_H__ + +#define LOGOFF 0 +#define LOGP 1 +#define LOGE 2 +#define LOGW 3 +#define LOG1 4 +#define LOG2 5 +#define LOG3 6 +#define LOG4 7 +#define LOGD 8 + +#define pe_alert_rl(params...) QDF_TRACE_FATAL_RL(QDF_MODULE_ID_PE, params) +#define pe_err_rl(params...) QDF_TRACE_ERROR_RL(QDF_MODULE_ID_PE, params) +#define pe_warn_rl(params...) QDF_TRACE_WARN_RL(QDF_MODULE_ID_PE, params) +#define pe_info_rl(params...) QDF_TRACE_INFO_RL(QDF_MODULE_ID_PE, params) +#define pe_debug_rl(params...) QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_PE, params) + +#define pe_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_PE, params) +#define pe_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_PE, params) +#define pe_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_PE, params) +#define pe_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_PE, params) +#define pe_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_PE, params) + +#define pe_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_PE, params) +#define pe_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_PE, params) +#define pe_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_PE, params) +#define pe_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_PE, params) +#define pe_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_PE, params) +#define pe_nofl_rl_debug(params...) \ + QDF_TRACE_DEBUG_RL_NO_FL(QDF_MODULE_ID_PE, params) +#define pe_nofl_rl_info(params...) \ + QDF_TRACE_INFO_RL_NO_FL(QDF_MODULE_ID_PE, params) + +#define PE_ENTER() QDF_TRACE_ENTER(QDF_MODULE_ID_PE, "enter") +#define PE_EXIT() QDF_TRACE_EXIT(QDF_MODULE_ID_PE, "exit") +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/include/sir_params.h b/drivers/staging/qcacld-3.0/core/mac/src/include/sir_params.h new file mode 100644 index 0000000000000000000000000000000000000000..a6e30c3dfab193050aaced47819c40d671225a80 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/include/sir_params.h @@ -0,0 +1,759 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file sir_params.h contains the common parameter definitions, which + * are not dependent on threadX API. These can be used by all Firmware + * modules. + * + * Author: Sandesh Goel + * Date: 04/13/2002 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#ifndef __SIRPARAMS_H +#define __SIRPARAMS_H + +#include "sir_types.h" + +#define WAKELOCK_DURATION_RECOMMENDED 1000 +#define WAKELOCK_DURATION_MAX 3000 + + +#define SYSTEM_TIME_MSEC_TO_USEC 1000 +#define SYSTEM_TIME_SEC_TO_MSEC 1000 +#define SYSTEM_TIME_NSEC_TO_USEC 1000 + +/* + * Following time is used to program WOW_TIMER_PATTERN to FW so that FW will + * wake host up to do graceful disconnect in case PEER remains un-authorized + * for this long. + */ +#define SIR_INSTALL_KEY_TIMEOUT_SEC 70 +#define SIR_INSTALL_KEY_TIMEOUT_MS \ + (SIR_INSTALL_KEY_TIMEOUT_SEC * SYSTEM_TIME_SEC_TO_MSEC) + +/* defines for WPS config states */ +#define SAP_WPS_DISABLED 0 +#define SAP_WPS_ENABLED_UNCONFIGURED 1 +#define SAP_WPS_ENABLED_CONFIGURED 2 + + +/* Firmware wide constants */ + +#define SIR_MAX_PACKET_SIZE 512 +#define SIR_MAX_NUM_CHANNELS 64 +#define SIR_MAX_NUM_STA_IN_IBSS 16 +#define SIR_ESE_MAX_MEAS_IE_REQS 8 + +typedef enum { + PHY_SINGLE_CHANNEL_CENTERED = 0, /* 20MHz IF bandwidth centered on IF carrier */ + PHY_DOUBLE_CHANNEL_LOW_PRIMARY = 1, /* 40MHz IF bandwidth with lower 20MHz supporting the primary channel */ + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY = 3, /* 40MHz IF bandwidth with higher 20MHz supporting the primary channel */ + PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED = 4, /* 20/40MHZ offset LOW 40/80MHZ offset CENTERED */ + PHY_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED = 5, /* 20/40MHZ offset CENTERED 40/80MHZ offset CENTERED */ + PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED = 6, /* 20/40MHZ offset HIGH 40/80MHZ offset CENTERED */ + PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW = 7, /* 20/40MHZ offset LOW 40/80MHZ offset LOW */ + PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW = 8, /* 20/40MHZ offset HIGH 40/80MHZ offset LOW */ + PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH = 9, /* 20/40MHZ offset LOW 40/80MHZ offset HIGH */ + PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH = 10, /* 20/40MHZ offset-HIGH 40/80MHZ offset HIGH */ + PHY_CHANNEL_BONDING_STATE_MAX = 11 +} ePhyChanBondState; + +#define MAX_BONDED_CHANNELS 8 +/** + * enum cap_bitmap - bit field for FW capability + * MCC - indicate MCC + * P2P - indicate P2P + * DOT11AC - indicate 11AC + * SLM_SESSIONIZATION - indicate SLM_SESSIONIZATION + * DOT11AC_OPMODE - indicate 11ac opmode + * SAP32STA - indicate SAP32STA + * TDLS - indicate TDLS + * P2P_GO_NOA_DECOUPLE_INIT_SCAN - indicate P2P_GO_NOA_DECOUPLE_INIT_SCAN + * WLANACTIVE_OFFLOAD - indicate active offload + * EXTENDED_SCAN - indicate extended scan + * PNO - indicate PNO + * NAN - indicate NAN + * RTT - indicate RTT + * DOT11AX - indicate 11ax + * WOW - indicate WOW + * WLAN_ROAM_SCAN_OFFLOAD - indicate Roam scan offload + * IBSS_HEARTBEAT_OFFLOAD - indicate IBSS HB offload + * WLAN_PERIODIC_TX_PTRN - indicate WLAN_PERIODIC_TX_PTRN + * ADVANCE_TDLS - indicate advanced TDLS + * TDLS_OFF_CHANNEL - indicate TDLS off channel + * + * This definition is independent of any other modules. + * We can use any unused numbers. + */ +#define MAX_SUPPORTED_FEATURE 32 +enum cap_bitmap { + MCC = 0, + P2P = 1, + DOT11AC = 2, + SLM_SESSIONIZATION = 3, + DOT11AC_OPMODE = 4, + SAP32STA = 5, + TDLS = 6, + P2P_GO_NOA_DECOUPLE_INIT_SCAN = 7, + WLANACTIVE_OFFLOAD = 8, + EXTENDED_SCAN = 9, +#ifdef FEATURE_WLAN_SCAN_PNO + PNO = 10, +#endif +#ifdef WLAN_FEATURE_NAN + NAN = 11, +#endif + RTT = 12, + DOT11AX = 13, + WOW = 22, + WLAN_ROAM_SCAN_OFFLOAD = 23, + IBSS_HEARTBEAT_OFFLOAD = 26, + WLAN_PERIODIC_TX_PTRN = 28, +#ifdef FEATURE_WLAN_TDLS + ADVANCE_TDLS = 29, + TDLS_OFF_CHANNEL = 30, +#endif + VDEV_LATENCY_CONFIG = 31, + + /* MAX_FEATURE_SUPPORTED = 32 */ +}; + +/* / Mailbox Message Structure Define */ +typedef struct sSirMbMsg { + uint16_t type; + + /** + * This length includes 4 bytes of header, that is, + * 2 bytes type + 2 bytes msgLen + n*4 bytes of data. + * This field is byte length. + */ + uint16_t msgLen; + + /** + * This is the first data word in the mailbox message. + * It is followed by n words of data. + * NOTE: data[1] is not a place holder to store data + * instead to dereference the message body. + */ + uint32_t data[1]; +} tSirMbMsg, *tpSirMbMsg; + +/** + * struct sir_mgmt_msg - Structure used to send auth frame from CSR to LIM + * @type: Message type + * @msg_len: Message length + * @vdev_id: vdev id + * @data: Pointer to data tobe transmitted + */ +struct sir_mgmt_msg { + uint16_t type; + uint16_t msg_len; + uint8_t vdev_id; + uint8_t *data; +}; + +/** + * struct sir_cfg_action_frm_tb_ppdu - cfg to set action frame in he tb ppdu + * @type: Message type + * @vdev_id: vdev id + * @cfg: enable/disable cfg + */ +struct sir_cfg_action_frm_tb_ppdu { + uint16_t type; + uint8_t vdev_id; + uint8_t cfg; +}; + +/* ******************************************* * +* * +* SIRIUS MESSAGE TYPES * +* * +* ******************************************* */ + +/* + * The following message types have bounds defined for each module for + * inter thread/module communications. + * Each module will get 256 message types in total. + * Note that message type definitions for mailbox messages for + * communication with Host are in wni_api.h file. + * + * Any addition/deletion to this message list should also be + * reflected in the halUtil_getMsgString() routine. + */ + +/* HAL message types */ +#define SIR_HAL_MSG_TYPES_BEGIN (SIR_HAL_MODULE_ID << 8) +#define SIR_HAL_ITC_MSG_TYPES_BEGIN (SIR_HAL_MSG_TYPES_BEGIN+0x20) +#define SIR_HAL_RADAR_DETECTED_IND SIR_HAL_ITC_MSG_TYPES_BEGIN + +/* + * New Taurus related messages + */ +#define SIR_HAL_ADD_STA_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 1) +#define SIR_HAL_ADD_STA_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 2) +#define SIR_HAL_DELETE_STA_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 3) +#define SIR_HAL_DELETE_STA_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 4) +#define SIR_HAL_ADD_BSS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 5) +#define SIR_HAL_DELETE_BSS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 7) +#define SIR_HAL_DELETE_BSS_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 8) +/* + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 9) thru + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 16) are unused + */ +#define SIR_HAL_SEND_BEACON_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 17) + +#define SIR_HAL_SET_BSSKEY_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 19) +#define SIR_HAL_SET_STAKEY_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 21) +#define SIR_HAL_UPDATE_EDCA_PROFILE_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 22) + +#define SIR_HAL_UPDATE_BEACON_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 23) +#define SIR_HAL_CHNL_SWITCH_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 25) +#define SIR_HAL_ADD_TS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 26) +#define SIR_HAL_DEL_TS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 27) + +#define SIR_HAL_MISSED_BEACON_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 34) + +#define SIR_HAL_SWITCH_CHANNEL_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 35) +#define SIR_HAL_PWR_SAVE_CFG (SIR_HAL_ITC_MSG_TYPES_BEGIN + 36) + +#define SIR_HAL_IBSS_STA_ADD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 43) +#define SIR_HAL_TIMER_ADJUST_ADAPTIVE_THRESHOLD_IND \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 44) +#define SIR_HAL_SET_LINK_STATE (SIR_HAL_ITC_MSG_TYPES_BEGIN + 45) +#define SIR_HAL_DELETE_BSS_HO_FAIL_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 46) +#define SIR_HAL_DELETE_BSS_HO_FAIL_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 47) + +/* + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 48) to + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 57) are unused + */ + +#define SIR_HAL_SET_STA_BCASTKEY_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 59) +#define SIR_HAL_ADD_TS_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 60) +#define SIR_HAL_DPU_MIC_ERROR (SIR_HAL_ITC_MSG_TYPES_BEGIN + 61) +#define SIR_HAL_TIMER_CHIP_MONITOR_TIMEOUT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 63) +#define SIR_HAL_TIMER_TRAFFIC_ACTIVITY_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 64) +#define SIR_HAL_TIMER_ADC_RSSI_STATS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 65) +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 66) is unused */ +#define SIR_HAL_SET_MIMOPS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 67) +#define SIR_HAL_SET_MIMOPS_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 68) +#define SIR_HAL_SYS_READY_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 69) +#define SIR_HAL_SET_TX_POWER_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 70) +#define SIR_HAL_SET_TX_POWER_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 71) +#define SIR_HAL_GET_TX_POWER_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 72) +/* + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 73) thru + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 89) are unused + */ + +#define SIR_HAL_SET_KEY_DONE (SIR_HAL_ITC_MSG_TYPES_BEGIN + 90) + +/* / PE <-> HAL BTC messages */ +#define SIR_HAL_BTC_SET_CFG (SIR_HAL_ITC_MSG_TYPES_BEGIN + 91) +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 92) is unused */ +#define SIR_HAL_HANDLE_FW_MBOX_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 93) +#define SIR_HAL_SEND_PROBE_RSP_TMPL (SIR_HAL_ITC_MSG_TYPES_BEGIN + 94) + +/* PE <-> HAL addr2 mismatch message */ +#define SIR_LIM_ADDR2_MISS_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 95) + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 96) is unused */ +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 97) is unused */ + +#define SIR_HAL_SET_MAX_TX_POWER_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 98) +#define SIR_HAL_SET_MAX_TX_POWER_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 99) + +/* / PE <-> HAL Host Offload message */ +#define SIR_HAL_SET_HOST_OFFLOAD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 100) + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 101) is unused */ +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 102) is unused */ +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 103) is unused */ + +/* + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 104) thru + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 108) are unused + */ +#define SIR_HAL_AGGR_QOS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 109) +#define SIR_HAL_AGGR_QOS_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 110) + +/* P2P <-> HAL P2P msg */ +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 111) is unused */ +#define SIR_HAL_P2P_NOA_ATTR_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 112) +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 113) is unused */ + +#define SIR_HAL_WLAN_SUSPEND_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 115) +#define SIR_HAL_WLAN_RESUME_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 116) + +/* / PE <-> HAL Keep Alive message */ +#define SIR_HAL_SET_KEEP_ALIVE (SIR_HAL_ITC_MSG_TYPES_BEGIN + 117) + +#ifdef WLAN_NS_OFFLOAD +#define SIR_HAL_SET_NS_OFFLOAD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 118) +#endif /* WLAN_NS_OFFLOAD */ + +#define SIR_HAL_SOC_ANTENNA_MODE_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 120) +#define SIR_HAL_SOC_ANTENNA_MODE_RESP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 121) + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 122) is unused */ + +#define SIR_HAL_8023_MULTICAST_LIST_REQ \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 123) + +#ifdef WLAN_FEATURE_PACKET_FILTERING +#define SIR_HAL_RECEIVE_FILTER_SET_FILTER_REQ \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 124) +#define SIR_HAL_PACKET_COALESCING_FILTER_MATCH_COUNT_REQ \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 125) +#define SIR_HAL_PACKET_COALESCING_FILTER_MATCH_COUNT_RSP \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 126) +#define SIR_HAL_RECEIVE_FILTER_CLEAR_FILTER_REQ \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 127) +#endif /* WLAN_FEATURE_PACKET_FILTERING */ + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 128) is unused */ + +#ifdef WLAN_FEATURE_GTK_OFFLOAD +#define SIR_HAL_GTK_OFFLOAD_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 129) +#define SIR_HAL_GTK_OFFLOAD_GETINFO_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 130) +#define SIR_HAL_GTK_OFFLOAD_GETINFO_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 131) +#endif /* WLAN_FEATURE_GTK_OFFLOAD */ + +#ifdef FEATURE_WLAN_ESE +#define SIR_HAL_TSM_STATS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 132) +#define SIR_HAL_TSM_STATS_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 133) +#endif + +#define SIR_HAL_SET_TM_LEVEL_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 134) + +#define SIR_HAL_UPDATE_OP_MODE (SIR_HAL_ITC_MSG_TYPES_BEGIN + 135) + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 136) is unused */ +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 137) is unused */ + +#define SIR_HAL_ROAM_SCAN_OFFLOAD_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 138) +#define SIR_HAL_ROAM_PRE_AUTH_STATUS_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 139) + +#define SIR_HAL_TRAFFIC_STATS_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 141) + +#ifdef WLAN_FEATURE_11W +#define SIR_HAL_EXCLUDE_UNENCRYPTED_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 142) +#endif +#ifdef FEATURE_WLAN_TDLS +/* / PE <-> HAL TDLS messages */ +#define SIR_HAL_TDLS_LINK_ESTABLISH_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 143) +#define SIR_HAL_TDLS_LINK_ESTABLISH_REQ_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 144) +#endif +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 145) is unused */ + +#define SIR_HAL_STOP_SCAN_OFFLOAD_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 146) +#define SIR_HAL_RX_SCAN_EVENT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 147) +#define SIR_HAL_DHCP_START_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 148) +#define SIR_HAL_DHCP_STOP_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 149) +#define SIR_HAL_IBSS_PEER_INACTIVITY_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 150) + +#define SIR_HAL_LPHB_CONF_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 151) + +#define SIR_HAL_ADD_PERIODIC_TX_PTRN_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 152) +#define SIR_HAL_DEL_PERIODIC_TX_PTRN_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 153) + +/* Messages between 156 to 157 are not used */ +#define SIR_HAL_PDEV_DUAL_MAC_CFG_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 154) +#define SIR_HAL_PDEV_MAC_CFG_RESP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 155) + +/* For IBSS peer info related messages */ +#define SIR_HAL_IBSS_PEER_INFO_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 158) + +#define SIR_HAL_RATE_UPDATE_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 159) + +#define SIR_HAL_FLUSH_LOG_TO_FW (SIR_HAL_ITC_MSG_TYPES_BEGIN + 160) + +#define SIR_HAL_PDEV_SET_PCL_TO_FW (SIR_HAL_ITC_MSG_TYPES_BEGIN + 161) + +#ifdef WLAN_MWS_INFO_DEBUGFS +#define SIR_HAL_GET_MWS_COEX_INFO_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 162) +#endif /* WLAN_MWS_INFO_DEBUGFS */ + +#define SIR_HAL_CLI_SET_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 163) +#ifndef REMOVE_PKT_LOG +#define SIR_HAL_PKTLOG_ENABLE_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 164) +#endif +#define SIR_HAL_START_SCAN_OFFLOAD_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 166) +#define SIR_HAL_UPDATE_CHAN_LIST_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 167) +#define SIR_CSA_OFFLOAD_EVENT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 169) + +#define SIR_HAL_SET_MAX_TX_POWER_PER_BAND_REQ \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 170) + +#define SIR_HAL_TX_FAIL_MONITOR_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 171) + +#define SIR_HAL_UPDATE_MEMBERSHIP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 172) +#define SIR_HAL_UPDATE_USERPOS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 173) + +#ifdef FEATURE_WLAN_TDLS +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 174) is not used */ +#define SIR_HAL_UPDATE_TDLS_PEER_STATE (SIR_HAL_ITC_MSG_TYPES_BEGIN + 175) +#define SIR_HAL_TDLS_SHOULD_DISCOVER (SIR_HAL_ITC_MSG_TYPES_BEGIN + 176) +#define SIR_HAL_TDLS_SHOULD_TEARDOWN (SIR_HAL_ITC_MSG_TYPES_BEGIN + 177) +#define SIR_HAL_TDLS_PEER_DISCONNECTED (SIR_HAL_ITC_MSG_TYPES_BEGIN + 178) +#endif + +/* Handling of beacon tx indication from FW */ +#define SIR_HAL_BEACON_TX_SUCCESS_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 179) +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 180) is unused */ + +#define SIR_HAL_IBSS_CESIUM_ENABLE_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 181) + +#define SIR_HAL_RMC_ENABLE_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 182) +#define SIR_HAL_RMC_DISABLE_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 183) +#define SIR_HAL_RMC_ACTION_PERIOD_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 184) +#define SIR_HAL_INIT_THERMAL_INFO_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 185) +#define SIR_HAL_SET_THERMAL_LEVEL (SIR_HAL_ITC_MSG_TYPES_BEGIN + 186) + +#ifdef FEATURE_WLAN_ESE +#define SIR_HAL_SET_PLM_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 187) +#endif + +#define SIR_HAL_SET_TX_POWER_LIMIT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 188) +#define SIR_HAL_SET_SAP_INTRABSS_DIS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 189) + +#define SIR_HAL_MODEM_POWER_STATE_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 190) + +#define SIR_HAL_DISASSOC_TX_COMP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 191) +#define SIR_HAL_DEAUTH_TX_COMP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 192) + +#define SIR_HAL_UPDATE_RX_NSS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 193) + +#ifdef WLAN_FEATURE_STATS_EXT +#define SIR_HAL_STATS_EXT_REQUEST (SIR_HAL_ITC_MSG_TYPES_BEGIN + 194) +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 195) is unused */ +#endif /* WLAN_FEATURE_STATS_EXT */ +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 197) is unused */ + +#ifdef FEATURE_WLAN_EXTSCAN +#define SIR_HAL_EXTSCAN_GET_CAPABILITIES_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 198) +#define SIR_HAL_EXTSCAN_START_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 199) +#define SIR_HAL_EXTSCAN_STOP_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 200) +#define SIR_HAL_EXTSCAN_SET_BSS_HOTLIST_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 201) +#define SIR_HAL_EXTSCAN_RESET_BSS_HOTLIST_REQ \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 202) +#define SIR_HAL_EXTSCAN_SET_SIGNF_CHANGE_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 203) +#define SIR_HAL_EXTSCAN_RESET_SIGNF_CHANGE_REQ \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 204) +#define SIR_HAL_EXTSCAN_GET_CACHED_RESULTS_REQ \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 205) +#endif /* FEATURE_WLAN_EXTSCAN */ + +#ifdef FEATURE_WLAN_CH_AVOID +#define SIR_HAL_CH_AVOID_UPDATE_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 206) +#endif + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS +#define SIR_HAL_LL_STATS_CLEAR_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 207) +#define SIR_HAL_LL_STATS_SET_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 208) +#define SIR_HAL_LL_STATS_GET_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 209) +#define SIR_HAL_LL_STATS_RESULTS_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 210) +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 211) is unused */ + +#ifdef WLAN_FEATURE_NAN +#define SIR_HAL_NAN_REQUEST (SIR_HAL_ITC_MSG_TYPES_BEGIN + 212) +#endif /* WLAN_FEATURE_NAN */ + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +#define SIR_HAL_SET_AUTO_SHUTDOWN_TIMER_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 213) +#endif + +#define SIR_HAL_SET_BASE_MACADDR_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 214) + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 215) is unused */ + +#define SIR_HAL_LINK_STATUS_GET_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 216) + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +#define SIR_HAL_CONFIG_EXT_WOW (SIR_HAL_ITC_MSG_TYPES_BEGIN + 217) +#define SIR_HAL_CONFIG_APP_TYPE1_PARAMS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 218) +#define SIR_HAL_CONFIG_APP_TYPE2_PARAMS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 219) +#endif + +#define SIR_HAL_GET_TEMPERATURE_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 220) +#define SIR_HAL_SET_SCAN_MAC_OUI_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 221) +#ifdef DHCP_SERVER_OFFLOAD +#define SIR_HAL_SET_DHCP_SERVER_OFFLOAD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 222) +#endif /* DHCP_SERVER_OFFLOAD */ +#define SIR_HAL_LED_FLASHING_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 223) + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +#define SIR_HAL_ROAM_OFFLOAD_SYNCH_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 225) +#define SIR_HAL_ROAM_OFFLOAD_SYNCH_FAIL (SIR_HAL_ITC_MSG_TYPES_BEGIN + 226) +#define SIR_HAL_ROAM_INVOKE (SIR_HAL_ITC_MSG_TYPES_BEGIN + 227) +#endif + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 228) is unused */ + +#define SIR_HAL_SET_MAS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 229) +#define SIR_HAL_SET_MIRACAST (SIR_HAL_ITC_MSG_TYPES_BEGIN + 230) +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +#define SIR_HAL_UPDATE_Q2Q_IE_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 231) +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ +#define SIR_HAL_CONFIG_STATS_FACTOR (SIR_HAL_ITC_MSG_TYPES_BEGIN + 232) +#define SIR_HAL_CONFIG_GUARD_TIME (SIR_HAL_ITC_MSG_TYPES_BEGIN + 233) +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 234) is unused */ + +#define SIR_HAL_ENTER_PS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 235) +#define SIR_HAL_EXIT_PS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 236) +#define SIR_HAL_ENABLE_UAPSD_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 237) +#define SIR_HAL_DISABLE_UAPSD_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 238) +#define SIR_HAL_GATEWAY_PARAM_UPDATE_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 239) + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 308) is unused */ +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 309) is unused */ + +#define SIR_HAL_SET_EPNO_LIST_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 313) +#define SIR_HAL_SET_PASSPOINT_LIST_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 316) +#define SIR_HAL_RESET_PASSPOINT_LIST_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 317) +/* 318 unused */ + +#define SIR_HAL_OCB_SET_CONFIG_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 319) +#define SIR_HAL_OCB_SET_UTC_TIME_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 320) +#define SIR_HAL_OCB_START_TIMING_ADVERT_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 321) +#define SIR_HAL_OCB_STOP_TIMING_ADVERT_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 322) +#define SIR_HAL_OCB_GET_TSF_TIMER_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 323) +#define SIR_HAL_DCC_GET_STATS_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 324) +#define SIR_HAL_DCC_CLEAR_STATS_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 325) +#define SIR_HAL_DCC_UPDATE_NDL_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 326) + +/* FW Memory Dump feature is deprecated */ + +#define SIR_HAL_START_STOP_LOGGING (SIR_HAL_ITC_MSG_TYPES_BEGIN + 328) +#define SIR_HAL_PDEV_SET_HW_MODE (SIR_HAL_ITC_MSG_TYPES_BEGIN + 329) +#define SIR_HAL_PDEV_SET_HW_MODE_RESP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 330) +#define SIR_HAL_PDEV_HW_MODE_TRANS_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 331) + +#define SIR_HAL_BAD_PEER_TX_CTL_INI_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 332) +#define SIR_HAL_SET_RSSI_MONITOR_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 333) +#define SIR_HAL_SET_IE_INFO (SIR_HAL_ITC_MSG_TYPES_BEGIN + 334) + +#define SIR_HAL_LRO_CONFIG_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 335) + +#define SIR_HAL_HT40_OBSS_SCAN_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 337) + +#define SIR_HAL_TSF_GPIO_PIN_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 338) + +#define SIR_HAL_ADD_BCN_FILTER_CMDID (SIR_HAL_ITC_MSG_TYPES_BEGIN + 339) +#define SIR_HAL_REMOVE_BCN_FILTER_CMDID (SIR_HAL_ITC_MSG_TYPES_BEGIN + 340) + + +#define SIR_HAL_APF_GET_CAPABILITIES_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 341) +#define SIR_HAL_WMA_ROAM_SYNC_TIMEOUT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 342) + +#define SIR_HAL_SET_WISA_PARAMS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 343) +#define SIR_HAL_SET_ADAPT_DWELLTIME_PARAMS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 344) +#define SIR_HAL_SET_PDEV_IE_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 345) + +/* + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 346) thru + * (SIR_HAL_ITC_MSG_TYPES_BEGIN + 357) are unused + */ +#define SIR_HAL_UPDATE_WEP_DEFAULT_KEY (SIR_HAL_ITC_MSG_TYPES_BEGIN + 358) + +#define SIR_HAL_SEND_FREQ_RANGE_CONTROL_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 360) +#define SIR_HAL_POWER_DBG_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 362) +#define SIR_HAL_SET_DTIM_PERIOD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 363) +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 364) is unused */ +#define SIR_HAL_SHORT_RETRY_LIMIT_CNT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 365) +#define SIR_HAL_LONG_RETRY_LIMIT_CNT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 366) +#define SIR_HAL_UPDATE_TX_FAIL_CNT_TH (SIR_HAL_ITC_MSG_TYPES_BEGIN + 367) +#define SIR_HAL_POWER_DEBUG_STATS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 368) + +#define SIR_HAL_SET_WOW_PULSE_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 369) + +/* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 370) is unused */ + +#define SIR_HAL_SET_PER_ROAM_CONFIG_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 371) +#define SIR_HAL_RX_CHN_STATUS_EVENT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 372) + +#define SIR_HAL_GET_RCPI_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 373) + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS +#define SIR_HAL_LL_STATS_EXT_SET_THRESHOLD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 378) +#endif +#define SIR_HAL_SET_DBS_SCAN_SEL_PARAMS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 379) + +#define SIR_HAL_HIDDEN_SSID_RESTART_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 379) + +#define SIR_HAL_INIT_ROAM_OFFLOAD_PARAM (SIR_HAL_ITC_MSG_TYPES_BEGIN + 380) + +/* + * Unused SIR_HAL_ITC_MSG_TYPES_BEGIN + 381 to + * SIR_HAL_ITC_MSG_TYPES_BEGIN + 386 + */ +#define SIR_HAL_GET_PEER_INFO_EXT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 387) + +/* ARP Debug stats */ +#define SIR_HAL_SET_ARP_STATS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 388) +#define SIR_HAL_GET_ARP_STATS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 389) + +#define SIR_HAL_SET_LIMIT_OFF_CHAN (SIR_HAL_ITC_MSG_TYPES_BEGIN + 390) + +#define SIR_HAL_SET_DEL_PMKID_CACHE (SIR_HAL_ITC_MSG_TYPES_BEGIN + 391) +#define SIR_HAL_HLP_IE_INFO (SIR_HAL_ITC_MSG_TYPES_BEGIN + 392) +#define SIR_HAL_OBSS_DETECTION_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 393) +#define SIR_HAL_OBSS_DETECTION_INFO (SIR_HAL_ITC_MSG_TYPES_BEGIN + 394) +#define SIR_HAL_INVOKE_NEIGHBOR_REPORT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 395) +#define SIR_HAL_OBSS_COLOR_COLLISION_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 396) +#define SIR_HAL_OBSS_COLOR_COLLISION_INFO (SIR_HAL_ITC_MSG_TYPES_BEGIN + 397) + +#define SIR_HAL_SEND_ADDBA_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 398) +#define SIR_HAL_GET_ROAM_SCAN_STATS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 399) +#define SIR_HAL_SEND_AP_VDEV_UP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 400) +#define SIR_HAL_SEND_BCN_RSP (SIR_HAL_ITC_MSG_TYPES_BEGIN + 401) +#define SIR_HAL_CFG_VENDOR_ACTION_TB_PPDU (SIR_HAL_ITC_MSG_TYPES_BEGIN + 402) +#define SIR_HAL_BEACON_DEBUG_STATS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 403) +#define SIR_HAL_ROAM_BLACKLIST_MSG (SIR_HAL_ITC_MSG_TYPES_BEGIN + 404) + +#ifdef WLAN_FEATURE_MOTION_DETECTION +#define SIR_HAL_SET_MOTION_DET_CONFIG (SIR_HAL_ITC_MSG_TYPES_BEGIN + 405) +#define SIR_HAL_SET_MOTION_DET_ENABLE (SIR_HAL_ITC_MSG_TYPES_BEGIN + 406) +#define SIR_HAL_SET_MOTION_DET_BASE_LINE_CONFIG \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 407) +#define SIR_HAL_SET_MOTION_DET_BASE_LINE_ENABLE \ + (SIR_HAL_ITC_MSG_TYPES_BEGIN + 408) +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +#ifdef FW_THERMAL_THROTTLE_SUPPORT +#define SIR_HAL_SET_THERMAL_THROTTLE_CFG (SIR_HAL_ITC_MSG_TYPES_BEGIN + 409) +#define SIR_HAL_SET_THERMAL_MGMT (SIR_HAL_ITC_MSG_TYPES_BEGIN + 410) +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + +#define SIR_HAL_SEND_PEER_UNMAP_CONF (SIR_HAL_ITC_MSG_TYPES_BEGIN + 411) + +#define SIR_HAL_GET_ISOLATION (SIR_HAL_ITC_MSG_TYPES_BEGIN + 412) + +#define SIR_HAL_SET_ROAM_TRIGGERS (SIR_HAL_ITC_MSG_TYPES_BEGIN + 413) + +#define SIR_HAL_ROAM_SCAN_CH_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 414) + +#define SIR_HAL_REQ_SEND_DELBA_REQ_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 415) +#define SIR_HAL_SEND_MAX_TX_POWER (SIR_HAL_ITC_MSG_TYPES_BEGIN + 416) + +#define SIR_HAL_MSG_TYPES_END (SIR_HAL_MSG_TYPES_BEGIN + 0x1FF) + +/* LIM message types */ +#define SIR_LIM_MSG_TYPES_BEGIN (SIR_LIM_MODULE_ID << 8) +#define SIR_LIM_ITC_MSG_TYPES_BEGIN (SIR_LIM_MSG_TYPES_BEGIN+0xB0) +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 1) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 2) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 3) */ +/* Message from BB Transport */ +#define SIR_BB_XPORT_MGMT_MSG (SIR_LIM_ITC_MSG_TYPES_BEGIN + 4) +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 5) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 6) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 7) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 8) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 9) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0xA) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0xB) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0xC) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0xD) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0xE) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0xF) */ +/* UNUSED (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0x10) */ +/* Indication from HAL to delete Station context */ +#define SIR_LIM_DELETE_STA_CONTEXT_IND (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0x11) +/* Indication from HAL to delete BA */ +#define SIR_LIM_UPDATE_BEACON (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0x13) +/* Indication from HAL to handle RX invalid peer */ +#define SIR_LIM_RX_INVALID_PEER (SIR_LIM_ITC_MSG_TYPES_BEGIN + 0x15) + +/* LIM Timeout messages */ +#define SIR_LIM_TIMEOUT_MSG_START ((SIR_LIM_MODULE_ID << 8) + 0xD0) +#define SIR_LIM_JOIN_FAIL_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 2) +#define SIR_LIM_AUTH_FAIL_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 3) +#define SIR_LIM_AUTH_RSP_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 4) +#define SIR_LIM_ASSOC_FAIL_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 5) +#define SIR_LIM_REASSOC_FAIL_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 6) +#define SIR_LIM_HEART_BEAT_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 7) +/* currently unused SIR_LIM_TIMEOUT_MSG_START + 0x8 */ +/* Link Monitoring Messages */ +#define SIR_LIM_PROBE_HB_FAILURE_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0xB) +#define SIR_LIM_ADDTS_RSP_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0xC) +#define SIR_LIM_LINK_TEST_DURATION_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x13) +#define SIR_LIM_CNF_WAIT_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x17) +/* currently unused (SIR_LIM_TIMEOUT_MSG_START + 0x18) */ +#define SIR_LIM_UPDATE_OLBC_CACHEL_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x19) +#define SIR_LIM_CHANNEL_SWITCH_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x1A) + +#define SIR_LIM_WPS_OVERLAP_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x1D) +#define SIR_LIM_FT_PREAUTH_RSP_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x1E) + +#define SIR_LIM_BEACON_GEN_IND (SIR_LIM_TIMEOUT_MSG_START + 0x23) +/* currently unused (SIR_LIM_TIMEOUT_MSG_START + 0x24) */ +/* currently unused (SIR_LIM_TIMEOUT_MSG_START + 0x25) */ + +#define SIR_LIM_DISASSOC_ACK_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x26) +#define SIR_LIM_DEAUTH_ACK_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x27) +#define SIR_LIM_PERIODIC_JOIN_PROBE_REQ_TIMEOUT \ + (SIR_LIM_TIMEOUT_MSG_START + 0x28) + +#define SIR_LIM_AUTH_RETRY_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x2D) +#define SIR_LIM_AUTH_SAE_TIMEOUT (SIR_LIM_TIMEOUT_MSG_START + 0x2E) + +#define SIR_LIM_PROCESS_DEFERRED_QUEUE (SIR_LIM_TIMEOUT_MSG_START + 0x2F) + +#define SIR_LIM_MSG_TYPES_END (SIR_LIM_MSG_TYPES_BEGIN+0xFF) + +/* ****************************************** * +* * +* EVENT TYPE Definitions * +* * +* ****************************************** */ + +/* Param Change Bitmap sent to HAL */ +#define PARAM_BCN_INTERVAL_CHANGED (1 << 0) +#define PARAM_SHORT_PREAMBLE_CHANGED (1 << 1) +#define PARAM_SHORT_SLOT_TIME_CHANGED (1 << 2) +#define PARAM_llACOEXIST_CHANGED (1 << 3) +#define PARAM_llBCOEXIST_CHANGED (1 << 4) +#define PARAM_llGCOEXIST_CHANGED (1 << 5) +#define PARAM_HT20MHZCOEXIST_CHANGED (1<<6) +#define PARAM_NON_GF_DEVICES_PRESENT_CHANGED (1<<7) +#define PARAM_RIFS_MODE_CHANGED (1<<8) +#define PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED (1<<9) +#define PARAM_OBSS_MODE_CHANGED (1<<10) +#define PARAM_BSS_COLOR_CHANGED (1 << 11) +#define PARAM_BEACON_UPDATE_MASK (PARAM_BCN_INTERVAL_CHANGED | \ + PARAM_SHORT_PREAMBLE_CHANGED | \ + PARAM_SHORT_SLOT_TIME_CHANGED | \ + PARAM_llACOEXIST_CHANGED | \ + PARAM_llBCOEXIST_CHANGED | \ + PARAM_llGCOEXIST_CHANGED | \ + PARAM_HT20MHZCOEXIST_CHANGED | \ + PARAM_NON_GF_DEVICES_PRESENT_CHANGED | \ + PARAM_RIFS_MODE_CHANGED | \ + PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED | \ + PARAM_OBSS_MODE_CHANGED | \ + PARAM_BSS_COLOR_CHANGED) + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/include/sys_global.h b/drivers/staging/qcacld-3.0/core/mac/src/include/sys_global.h new file mode 100644 index 0000000000000000000000000000000000000000..98c6980775b87dd736d35d4650efed6821a1f334 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/include/sys_global.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __SYS_GLOBAL_H__ +#define __SYS_GLOBAL_H__ + +typedef struct sAniSirSys { + uint32_t gSysFrameCount[4][16]; + uint32_t gSysBbtReceived; + uint32_t sys_bbt_pending_mgmt_count; + uint32_t gSysBbtPostedToLim; + uint32_t gSysBbtDropped; + uint32_t gSysEnableLinkMonitorMode; + qdf_spinlock_t bbt_mgmt_lock; +} tAniSirSys, *tpAniSirSys; + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/include/utils_api.h b/drivers/staging/qcacld-3.0/core/mac/src/include/utils_api.h new file mode 100644 index 0000000000000000000000000000000000000000..e2f8ce26163b82705c4af091b171a9f7836a9804 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/include/utils_api.h @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __UTILSAPI_H +#define __UTILSAPI_H + +#include +#include +#include "ani_global.h" +#include "sys_wrapper.h" +#include "wlan_vdev_mlme_main.h" +#include "wlan_vdev_mlme_api.h" + +/** + * sir_swap_u16() + * + * FUNCTION: + * This function is called to swap two U8s of an uint16_t value + * + * LOGIC: + * + * ASSUMPTIONS: + * None. + * + * NOTE: + * + * @param val uint16_t value to be uint8_t swapped + * @return Swapped uint16_t value + */ + +static inline uint16_t sir_swap_u16(uint16_t val) +{ + return ((val & 0x00FF) << 8) | ((val & 0xFF00) >> 8); +} /*** end sir_swap_u16() ***/ + +/** + * sir_swap_u16if_needed() + * + * FUNCTION: + * This function is called to swap two U8s of an uint16_t value depending + * on endiannes of the target processor/compiler the software is + * running on + * + * LOGIC: + * + * ASSUMPTIONS: + * None. + * + * NOTE: + * + * @param val uint16_t value to be uint8_t swapped + * @return Swapped uint16_t value + */ + +static inline uint16_t sir_swap_u16if_needed(uint16_t val) +{ +#ifndef ANI_LITTLE_BYTE_ENDIAN + return sir_swap_u16(val); +#else + return val; +#endif +} /*** end sir_swap_u16if_needed() ***/ + +/** + * sir_swap_u32() + * + * FUNCTION: + * This function is called to swap four U8s of an uint32_t value + * + * LOGIC: + * + * ASSUMPTIONS: + * None. + * + * NOTE: + * + * @param val uint32_t value to be uint8_t swapped + * @return Swapped uint32_t value + */ + +static inline uint32_t sir_swap_u32(uint32_t val) +{ + return (val << 24) | + (val >> 24) | + ((val & 0x0000FF00) << 8) | ((val & 0x00FF0000) >> 8); +} /*** end sir_swap_u32() ***/ + +/** + * sir_swap_u32if_needed() + * + * FUNCTION: + * This function is called to swap U8s of an uint32_t value depending + * on endiannes of the target processor/compiler the software is + * running on + * + * LOGIC: + * + * ASSUMPTIONS: + * None. + * + * NOTE: + * + * @param val uint32_t value to be uint8_t swapped + * @return Swapped uint32_t value + */ + +static inline uint32_t sir_swap_u32if_needed(uint32_t val) +{ +#ifndef ANI_LITTLE_BYTE_ENDIAN + return sir_swap_u32(val); +#else + return val; +#endif +} /*** end sir_swap_u32if_needed() ***/ + +/** + * sir_swap_u32_buf + * + * FUNCTION: + * It swaps N dwords into the same buffer + * + * LOGIC: + * + * ASSUMPTIONS: + * None. + * + * NOTE: + * + * @param ptr address of uint32_t array + * @return void + * + */ + +/** + * sir_read_u32_n + * + * FUNCTION: + * It reads a 32 bit number from the byte array in network byte order + * i.e. the least significant byte first + * + * LOGIC: + * + * ASSUMPTIONS: + * None. + * + * NOTE: + * + * @param ptr address of byte array + * @return 32 bit value + */ + +static inline uint32_t sir_read_u32_n(uint8_t *ptr) +{ + return (*(ptr) << 24) | + (*(ptr + 1) << 16) | (*(ptr + 2) << 8) | (*(ptr + 3)); +} + +/** + * sir_read_u16 + * + * FUNCTION: + * It reads a 16 bit number from the byte array in NON-network byte order + * i.e. the least significant byte first + * + * LOGIC: + * + * ASSUMPTIONS: + * None. + * + * NOTE: + * + * @param ptr address of byte array + * @return 16 bit value + */ + +static inline uint16_t sir_read_u16(uint8_t *ptr) +{ + return (*ptr) | (*(ptr + 1) << 8); +} + +/** + * sir_read_u32 + * + * FUNCTION: + * It reads a 32 bit number from the byte array in NON-network byte order + * i.e. the least significant byte first + * + * LOGIC: + * + * ASSUMPTIONS: + * None. + * + * NOTE: + * + * @param ptr address of byte array + * @return 32 bit value + */ + +static inline uint32_t sir_read_u32(uint8_t *ptr) +{ + return (*(ptr)) | + (*(ptr + 1) << 8) | (*(ptr + 2) << 16) | (*(ptr + 3) << 24); +} + +/* / Copy a MAC address from 'from' to 'to' */ +static inline void sir_copy_mac_addr(uint8_t to[], uint8_t from[]) +{ +#if defined(_X86_) + uint32_t align = (0x3 & ((uint32_t) to | (uint32_t) from)); + + if (align == 0) { + *((uint16_t *) &(to[4])) = *((uint16_t *) &(from[4])); + *((uint32_t *) to) = *((uint32_t *) from); + } else if (align == 2) { + *((uint16_t *) &to[4]) = *((uint16_t *) &from[4]); + *((uint16_t *) &to[2]) = *((uint16_t *) &from[2]); + *((uint16_t *) &to[0]) = *((uint16_t *) &from[0]); + } else { + to[5] = from[5]; + to[4] = from[4]; + to[3] = from[3]; + to[2] = from[2]; + to[1] = from[1]; + to[0] = from[0]; + } +#else + to[0] = from[0]; + to[1] = from[1]; + to[2] = from[2]; + to[3] = from[3]; + to[4] = from[4]; + to[5] = from[5]; +#endif +} + +static inline uint8_t sir_compare_mac_addr(uint8_t addr1[], uint8_t addr2[]) +{ +#if defined(_X86_) + uint32_t align = (0x3 & ((uint32_t) addr1 | (uint32_t) addr2)); + + if (align == 0) { + return (*((uint16_t *) &(addr1[4])) == + *((uint16_t *) &(addr2[4]))) + && (*((uint32_t *) addr1) == *((uint32_t *) addr2)); + } else if (align == 2) { + return (*((uint16_t *) &addr1[4]) == + *((uint16_t *) &addr2[4])) + && (*((uint16_t *) &addr1[2]) == + *((uint16_t *) &addr2[2])) + && (*((uint16_t *) &addr1[0]) == + *((uint16_t *) &addr2[0])); + } else { + return (addr1[5] == addr2[5]) && + (addr1[4] == addr2[4]) && + (addr1[3] == addr2[3]) && + (addr1[2] == addr2[2]) && + (addr1[1] == addr2[1]) && (addr1[0] == addr2[0]); + } +#else + return (addr1[0] == addr2[0]) && + (addr1[1] == addr2[1]) && + (addr1[2] == addr2[2]) && + (addr1[3] == addr2[3]) && + (addr1[4] == addr2[4]) && (addr1[5] == addr2[5]); +#endif +} + +/* + * converts uint16_t CW value to 4 bit value to be inserted in IE + */ +static inline uint8_t convert_cw(uint16_t cw) +{ + uint8_t val = 0; + + while (cw > 0) { + val++; + cw >>= 1; + } + if (val > 15) + return 0xF; + return val; +} + +/* The user priority to AC mapping is such: + * UP(1, 2) ---> AC_BK(1) + * UP(0, 3) ---> AC_BE(0) + * UP(4, 5) ---> AC_VI(2) + * UP(6, 7) ---> AC_VO(3) + */ +#define WLAN_UP_TO_AC_MAP 0x33220110 +#define upToAc(up) ((WLAN_UP_TO_AC_MAP >> ((up) << 2)) & 0x03) + +#endif /* __UTILSAPI_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_admit_control.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_admit_control.h new file mode 100644 index 0000000000000000000000000000000000000000..3830f7dd4eadcce1105e86d351b87bae2e23ad7e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_admit_control.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011-2012, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * Author: Dinesh Upadhyay + * Date: 10/24/06 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#ifndef __LIM_ADMIT_CONTROL_H__ +#define __LIM_ADMIT_CONTROL_H__ + +#include "sir_common.h" +#include "sir_mac_prot_def.h" + +#include "ani_global.h" + +QDF_STATUS +lim_tspec_find_by_assoc_id(struct mac_context *, uint16_t, + struct mac_tspec_ie *, + tpLimTspecInfo, tpLimTspecInfo *); + +/* Add TSPEC in lim local table */ +QDF_STATUS lim_tspec_add(struct mac_context *mac, + uint8_t *pAddr, + uint16_t assocId, + struct mac_tspec_ie *pTspec, + uint32_t interval, tpLimTspecInfo *ppInfo); + +/* admit control interface */ +QDF_STATUS lim_admit_control_add_ts(struct mac_context *mac, + uint8_t *pAddr, tSirAddtsReqInfo *addts, + tSirMacQosCapabilityStaIE *qos, + uint16_t assocId, uint8_t alloc, + tSirMacScheduleIE *pSch, + /* index to the lim tspec table. */ + uint8_t *pTspecIdx, + struct pe_session *pe_session); + +static inline QDF_STATUS +lim_admit_control_add_sta(struct mac_context *mac, uint8_t *staAddr, uint8_t alloc) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +lim_admit_control_delete_sta(struct mac_context *mac, uint16_t assocId); + +QDF_STATUS +lim_admit_control_delete_ts(struct mac_context *mac, + uint16_t assocId, + struct mac_ts_info *tsinfo, + uint8_t *tsStatus, uint8_t *tspecIdx); + +QDF_STATUS lim_admit_control_init(struct mac_context *mac); +#ifdef FEATURE_WLAN_ESE +QDF_STATUS lim_send_hal_msg_add_ts(struct mac_context *mac, + uint8_t tspecIdx, + struct mac_tspec_ie tspecIE, + uint8_t sessionId, uint16_t tsm_interval); +#else +QDF_STATUS lim_send_hal_msg_add_ts(struct mac_context *mac, + uint8_t tspecIdx, + struct mac_tspec_ie tspecIE, + uint8_t sessionId); +#endif + +QDF_STATUS lim_send_hal_msg_del_ts(struct mac_context *mac, + uint8_t tspecIdx, + struct delts_req_info delts, + uint8_t sessionId, uint8_t *bssId); +void lim_process_hal_add_ts_rsp(struct mac_context *mac, + struct scheduler_msg *limMsg); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_api.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_api.h new file mode 100644 index 0000000000000000000000000000000000000000..893c56472b11a356f91f830b163dedaefb900dd8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_api.h @@ -0,0 +1,545 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_api.h contains the definitions exported by + * LIM module. + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#ifndef __LIM_API_H +#define __LIM_API_H +#include "wni_api.h" +#include "sir_api.h" +#include "ani_global.h" +#include "sir_mac_prot_def.h" +#include "sir_common.h" +#include "sir_debug.h" +#include "sch_global.h" +#include "utils_api.h" +#include "lim_global.h" +#include "wma_if.h" +#include "wma_types.h" +#include "scheduler_api.h" + +/* Macro to count heartbeat */ +#define limResetHBPktCount(pe_session) (pe_session->LimRxedBeaconCntDuringHB = 0) + +/* Useful macros for fetching various states in mac->lim */ +/* gLimSystemRole */ +#define GET_LIM_SYSTEM_ROLE(pe_session) (pe_session->limSystemRole) +#define LIM_IS_AP_ROLE(pe_session) (GET_LIM_SYSTEM_ROLE(pe_session) == eLIM_AP_ROLE) +#define LIM_IS_STA_ROLE(pe_session) (GET_LIM_SYSTEM_ROLE(pe_session) == eLIM_STA_ROLE) +#define LIM_IS_IBSS_ROLE(pe_session) (GET_LIM_SYSTEM_ROLE(pe_session) == eLIM_STA_IN_IBSS_ROLE) +#define LIM_IS_UNKNOWN_ROLE(pe_session) (GET_LIM_SYSTEM_ROLE(pe_session) == eLIM_UNKNOWN_ROLE) +#define LIM_IS_P2P_DEVICE_ROLE(pe_session) (GET_LIM_SYSTEM_ROLE(pe_session) == eLIM_P2P_DEVICE_ROLE) +#define LIM_IS_P2P_DEVICE_GO(pe_session) (GET_LIM_SYSTEM_ROLE(pe_session) == eLIM_P2P_DEVICE_GO) +#define LIM_IS_NDI_ROLE(pe_session) \ + (GET_LIM_SYSTEM_ROLE(pe_session) == eLIM_NDI_ROLE) +/* gLimSmeState */ +#define GET_LIM_SME_STATE(mac) (mac->lim.gLimSmeState) +#define SET_LIM_SME_STATE(mac, state) (mac->lim.gLimSmeState = state) +/* gLimMlmState */ +#define GET_LIM_MLM_STATE(mac) (mac->lim.gLimMlmState) +#define SET_LIM_MLM_STATE(mac, state) (mac->lim.gLimMlmState = state) +/*tpdphHashNode mlmStaContext*/ +#define GET_LIM_STA_CONTEXT_MLM_STATE(sta) (sta->mlmStaContext.mlmState) +#define SET_LIM_STA_CONTEXT_MLM_STATE(sta, state) (sta->mlmStaContext.mlmState = state) +#define LIM_IS_CONNECTION_ACTIVE(pe_session) (pe_session->LimRxedBeaconCntDuringHB) +/*mac->lim.gLimProcessDefdMsgs*/ +#define GET_LIM_PROCESS_DEFD_MESGS(mac) (mac->lim.gLimProcessDefdMsgs) + +/** + * lim_post_msg_api() - post normal priority PE message + * @mac: mac context + * @msg: message to be posted + * + * This function is called to post a message to the tail of the PE + * message queue to be processed in the MC Thread with normal + * priority. + * + * Return: QDF_STATUS_SUCCESS on success, other QDF_STATUS on error + */ +QDF_STATUS lim_post_msg_api(struct mac_context *mac, struct scheduler_msg *msg); + +static inline void +lim_post_msg_to_process_deferred_queue(struct mac_context *mac) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + if (!mac->lim.gLimProcessDefdMsgs || !mac->lim.gLimDeferredMsgQ.size) + return; + + msg.type = SIR_LIM_PROCESS_DEFERRED_QUEUE; + msg.bodyptr = NULL; + msg.bodyval = 0; + + status = lim_post_msg_api(mac, &msg); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("Failed to post lim msg:0x%x", msg.type); +} + +#define SET_LIM_PROCESS_DEFD_MESGS(mac, val) \ + mac->lim.gLimProcessDefdMsgs = val; \ + pe_debug("Defer LIM msg %d", val); \ + lim_post_msg_to_process_deferred_queue(mac); + +/* LIM exported function templates */ +#define LIM_MIN_BCN_PR_LENGTH 12 +#define LIM_BCN_PR_CAPABILITY_OFFSET 10 +#define LIM_ASSOC_REQ_IE_OFFSET 4 + +/** + * enum lim_vendor_ie_access_policy - vendor ie access policy + * @LIM_ACCESS_POLICY_NONE: access policy not valid + * @LIM_ACCESS_POLICY_RESPOND_IF_IE_IS_PRESENT: respond only if vendor ie + * is present in probe request and assoc request frames + * @LIM_ACCESS_POLICY_DONOT_RESPOND_IF_IE_IS_PRESENT: do not respond if vendor + * ie is present in probe request or assoc request frames + */ +enum lim_vendor_ie_access_policy { + LIM_ACCESS_POLICY_NONE, + LIM_ACCESS_POLICY_RESPOND_IF_IE_IS_PRESENT, + LIM_ACCESS_POLICY_DONOT_RESPOND_IF_IE_IS_PRESENT, +}; + +typedef enum eMgmtFrmDropReason { + eMGMT_DROP_NO_DROP, + eMGMT_DROP_NOT_LAST_IBSS_BCN, + eMGMT_DROP_INFRA_BCN_IN_IBSS, + eMGMT_DROP_SCAN_MODE_FRAME, + eMGMT_DROP_NON_SCAN_MODE_FRAME, + eMGMT_DROP_INVALID_SIZE, + eMGMT_DROP_SPURIOUS_FRAME, + eMGMT_DROP_DUPLICATE_AUTH_FRAME, + eMGMT_DROP_EXCESSIVE_MGMT_FRAME, +} tMgmtFrmDropReason; + +/** + * Function to initialize LIM state machines. + * This called upon LIM thread creation. + */ +QDF_STATUS lim_initialize(struct mac_context *); +QDF_STATUS pe_open(struct mac_context *mac, struct cds_config_info *cds_cfg); +QDF_STATUS pe_close(struct mac_context *mac); +QDF_STATUS lim_start(struct mac_context *mac); +QDF_STATUS pe_start(struct mac_context *mac); +void pe_stop(struct mac_context *mac); + +#ifdef WLAN_FEATURE_11W +/** + * lim_stop_pmfcomeback_timer() - stop pmf comeback timer + * @session: Pointer to PE session + * + * Return: None + */ +void lim_stop_pmfcomeback_timer(struct pe_session *session); +#else +static inline void lim_stop_pmfcomeback_timer(struct pe_session *session) +{ +} +#endif + +/** + * pe_register_mgmt_rx_frm_callback() - registers callback for receiving + * mgmt rx frames + * @mac_ctx: mac global ctx + * + * This function registers a PE function to mgmt txrx component and a WMA + * function to WMI layer as event handler for receiving mgmt frames. + * + * Return: None + */ +void pe_register_mgmt_rx_frm_callback(struct mac_context *mac_ctx); + +/** + * pe_deregister_mgmt_rx_frm_callback() - degisters callback for receiving + * mgmt rx frames + * @mac_ctx: mac global ctx + * + * This function deregisters the PE function registered to mgmt txrx component + * and the WMA function registered to WMI layer as event handler for receiving + * mgmt frames. + * + * Return: None + */ +void pe_deregister_mgmt_rx_frm_callback(struct mac_context *mac_ctx); + +/** + * pe_register_callbacks_with_wma() - register SME and PE callback functions to + * WMA. + * @mac: mac global ctx + * @ready_req: Ready request parameters, containing callback pointers + * + * Return: None + */ +void pe_register_callbacks_with_wma(struct mac_context *mac, + struct sme_ready_req *ready_req); + +/** + * Function to cleanup LIM state. + * This called upon reset/persona change etc + */ +void lim_cleanup(struct mac_context *); + +/** + * lim_post_msg_high_priority() - post high priority PE message + * @mac: mac context + * @msg: message to be posted + * + * This function is called to post a message to the head of the PE + * message queue to be processed in the MC Thread with expedited + * priority. + * + * Return: QDF_STATUS_SUCCESS on success, other QDF_STATUS on error + */ +QDF_STATUS lim_post_msg_high_priority(struct mac_context *mac, + struct scheduler_msg *msg); + +/** + * Function to process messages posted to LIM thread + * and dispatch to various sub modules within LIM module. + */ +void lim_message_processor(struct mac_context *, struct scheduler_msg *); + +#ifdef QCA_IBSS_SUPPORT +/** + * lim_handle_ibss_coalescing() - Function to handle IBSS coalescing. + * @param mac - Pointer to Global MAC structure + * @param pBeacon - Parsed Beacon Frame structure + * @param pRxPacketInfo - Pointer to RX packet info structure + * @pe_session - pointer to pe session + * + * This function is called upon receiving Beacon/Probe Response + * while operating in IBSS mode. + * + * @return Status whether to process or ignore received Beacon Frame + */ +QDF_STATUS +lim_handle_ibss_coalescing(struct mac_context *mac, + tpSchBeaconStruct pBeacon, + uint8_t *pRxPacketInfo, + struct pe_session *pe_session); +#else +/** + * lim_handle_ibss_coalescing() - Function to handle IBSS coalescing. + * @param mac - Pointer to Global MAC structure + * @param pBeacon - Parsed Beacon Frame structure + * @param pRxPacketInfo - Pointer to RX packet info structure + * @pe_session - pointer to pe session + * + * This function is dummy + * + * @return Status whether to process or ignore received Beacon Frame + */ +static inline QDF_STATUS +lim_handle_ibss_coalescing(struct mac_context *mac, + tpSchBeaconStruct pBeacon, + uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/* / Function used by other Sirius modules to read global SME state */ +static inline tLimSmeStates lim_get_sme_state(struct mac_context *mac) +{ + return mac->lim.gLimSmeState; +} + +/** + * lim_received_hb_handler() - This function is called by + * sch_beacon_process() upon receiving a Beacon on STA. This + * also gets called upon receiving Probe Response after heat + * beat failure is detected. + * + * @mac - global mac structure + * @chan_freq - channel frequency indicated in Beacon, Probe + * + * Response return - none + */ +void lim_received_hb_handler(struct mac_context *, uint32_t, + struct pe_session *); + +/* / Function that triggers STA context deletion */ +void lim_trigger_sta_deletion(struct mac_context *mac, tpDphHashNode sta, + struct pe_session *pe_session); + +#ifdef FEATURE_WLAN_TDLS +/* Function that sends TDLS Del Sta indication to SME */ +void lim_send_sme_tdls_del_sta_ind(struct mac_context *mac, tpDphHashNode sta, + struct pe_session *pe_session, + uint16_t reasonCode); + +/** + * lim_set_tdls_flags() - update tdls flags based on newer STA connection + * information + * @roam_sync_ind_ptr: pointer to roam offload structure + * @ft_session_ptr: pointer to PE session + * + * Set TDLS flags as per new STA connection capabilities. + * + * Return: None + */ +void lim_set_tdls_flags(struct roam_offload_synch_ind *roam_sync_ind_ptr, + struct pe_session *ft_session_ptr); +#else +static inline +void lim_set_tdls_flags(struct roam_offload_synch_ind *roam_sync_ind_ptr, + struct pe_session *ft_session_ptr) +{ +} +#endif + +/* / Function that checks for change in AP's capabilties on STA */ +void lim_detect_change_in_ap_capabilities(struct mac_context *, + tpSirProbeRespBeacon, + struct pe_session *); + +QDF_STATUS lim_update_short_slot(struct mac_context *mac, + tpSirProbeRespBeacon pBeacon, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *); + +/** + * lim_ps_offload_handle_missed_beacon_ind() - handle missed beacon indication + * @mac: global mac context + * @msg: message + * + * This function process the SIR_HAL_MISSED_BEACON_IND + * message from HAL, to do active AP probing. + * + * Return: void + */ +void lim_ps_offload_handle_missed_beacon_ind(struct mac_context *mac, + struct scheduler_msg *msg); + +void lim_send_heart_beat_timeout_ind(struct mac_context *mac, struct pe_session *pe_session); +tMgmtFrmDropReason lim_is_pkt_candidate_for_drop(struct mac_context *mac, + uint8_t *pRxPacketInfo, + uint32_t subType); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * pe_roam_synch_callback() - Callback registered at wma, gets invoked when + * ROAM SYNCH event is received from firmware + * @mac_ctx: global mac context + * @roam_sync_ind_ptr: Structure with roam synch parameters + * @bss_desc_ptr: bss_description pointer for new bss to which the firmware has + * started roaming + * @reason: Operation to be done by the callback + * + * This is a PE level callback called from WMA to complete the roam synch + * propagation at PE level and also fill the BSS descriptor which will be + * helpful further to complete the roam synch propagation. + * + * Return: QDF_STATUS + */ +QDF_STATUS +pe_roam_synch_callback(struct mac_context *mac_ctx, + struct roam_offload_synch_ind *roam_sync_ind_ptr, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason); + +void +lim_check_ft_initial_im_association(struct roam_offload_synch_ind *roam_synch, + struct pe_session *session_entry); + +/** + * pe_disconnect_callback() - Callback to handle deauth event is received + * from firmware + * @mac: pointer to global mac context + * @vdev_id: VDEV in which the event was received + * @deauth_disassoc_frame: Deauth/disassoc frame received from firmware + * @deauth_disassoc_frame_len: Length of @deauth_disassoc_frame + * @reason_code: Fw sent reason code if disassoc/deauth frame is not + * available + * + * Return: QDF_STATUS + */ +QDF_STATUS +pe_disconnect_callback(struct mac_context *mac, uint8_t vdev_id, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len, + uint16_t reason_code); + +#else +static inline QDF_STATUS +pe_roam_synch_callback(struct mac_context *mac_ctx, + struct roam_offload_synch_ind *roam_sync_ind_ptr, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline QDF_STATUS +pe_disconnect_callback(struct mac_context *mac, uint8_t vdev_id, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len, + uint16_t reason_code) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif + +/** + * lim_update_lost_link_info() - update lost link information to SME + * @mac: global MAC handle + * @session: PE session + * @rssi: rssi value from the received frame + * + * Return: None + */ +void lim_update_lost_link_info(struct mac_context *mac, struct pe_session *session, + int32_t rssi); + +/** + * lim_mon_init_session() - create PE session for monitor mode operation + * @mac_ptr: mac pointer + * @msg: Pointer to struct sir_create_session type. + * + * Return: NONE + */ +void lim_mon_init_session(struct mac_context *mac_ptr, + struct sir_create_session *msg); + +/** + * lim_mon_deinit_session() - delete PE session for monitor mode operation + * @mac_ptr: mac pointer + * @msg: Pointer to struct sir_delete_session type. + * + * Return: NONE + */ +void lim_mon_deinit_session(struct mac_context *mac_ptr, + struct sir_delete_session *msg); + +#define limGetQosMode(pe_session, pVal) (*(pVal) = (pe_session)->limQosEnabled) +#define limGetWmeMode(pe_session, pVal) (*(pVal) = (pe_session)->limWmeEnabled) +#define limGetWsmMode(pe_session, pVal) (*(pVal) = (pe_session)->limWsmEnabled) +/* ----------------------------------------------------------------------- */ +static inline void lim_get_phy_mode(struct mac_context *mac, uint32_t *phyMode, + struct pe_session *pe_session) +{ + *phyMode = + pe_session ? pe_session->gLimPhyMode : mac->lim.gLimPhyMode; +} + +/* ----------------------------------------------------------------------- */ +static inline void lim_get_rf_band_new(struct mac_context *mac, + enum reg_wifi_band *band, + struct pe_session *pe_session) +{ + *band = pe_session ? pe_session->limRFBand : REG_BAND_UNKNOWN; +} + +/** + * pe_mc_process_handler() - Message Processor for PE + * @msg: Pointer to the message structure + * + * Verifies the system is in a mode where messages are expected to be + * processed, and if so, routes the message to the appropriate handler + * based upon message type. + * + * Return: QDF_STATUS_SUCCESS if the message was handled, otherwise an + * appropriate QDF_STATUS error code + */ +QDF_STATUS pe_mc_process_handler(struct scheduler_msg *msg); + +/** ------------------------------------------------------------- + \fn pe_free_msg + \brief Called by CDS scheduler (function cds_sched_flush_mc_mqs) + \ to free a given PE message on the TX and MC thread. + \ This happens when there are messages pending in the PE + \ queue when system is being stopped and reset. + \param struct mac_context *mac + \param struct scheduler_msg pMsg + \return none + -----------------------------------------------------------------*/ +void pe_free_msg(struct mac_context *mac, struct scheduler_msg *pMsg); + +/** + * lim_process_abort_scan_ind() - abort the scan which is presently being run + * + * @mac_ctx: Pointer to Global MAC structure + * @vdev_id: vdev_id + * @scan_id: Scan ID from the scan request + * @scan_requesor_id: Entity requesting the scan + * + * @return: None + */ +void lim_process_abort_scan_ind(struct mac_context *mac, uint8_t vdev_id, + uint32_t scan_id, uint32_t scan_requestor_id); + +void __lim_process_sme_assoc_cnf_new(struct mac_context *, uint32_t, uint32_t *); + +/** + * lim_process_sme_addts_rsp_timeout(): Send addts rsp timeout to SME + * @mac: Pointer to Global MAC structure + * @param: Addts rsp timer count + * + * This function is used to reset the addts sent flag and + * send addts rsp timeout to SME + * + * Return: None + */ +void lim_process_sme_addts_rsp_timeout(struct mac_context *mac, uint32_t param); +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +void lim_fill_join_rsp_ht_caps(struct pe_session *session, + struct join_rsp *rsp); +#else +static inline +void lim_fill_join_rsp_ht_caps(struct pe_session *session, + struct join_rsp *rsp) +{} +#endif +QDF_STATUS lim_update_ext_cap_ie(struct mac_context *mac_ctx, uint8_t *ie_data, + uint8_t *local_ie_buf, uint16_t *local_ie_len, + struct pe_session *session); + +/** + * lim_handle_sap_beacon(): Handle the beacon received from scan module for SAP + * @pdev: pointer to the pdev object + * @scan_entry: pointer to the scan cache entry for the beacon + * + * Registered as callback to the scan module for handling beacon frames. + * This API filters the and allows beacons for SAP protection mechanisms + * if there are active SAP sessions and the received beacon's channel + * matches the SAP active channel + * + * Return: None + */ +void lim_handle_sap_beacon(struct wlan_objmgr_pdev *pdev, + struct scan_cache_entry *scan_entry); + +/** + * lim_translate_rsn_oui_to_akm_type() - translate RSN OUI to AKM type + * @auth_suite: auth suite + * + * Return: AKM type + */ +enum ani_akm_type lim_translate_rsn_oui_to_akm_type(uint8_t auth_suite[4]); + +/************************************************************/ +#endif /* __LIM_API_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_fils_defs.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_fils_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..277485cec55cf4ad9cd3b9c8f87287537744424f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_fils_defs.h @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#define FILS_EAP_TLV_MAX_DATA_LEN 255 +#define FILS_SHA256_128_AUTH_TAG 16 +#define FILS_SHA256_256_AUTH_TAG 32 + +/* RFC 6696 */ +#define RMSK_LABEL "Re-authentication Master Session Key@ietf.org" + +/* 12.12.2.5.3 80211-ai draft */ +#define PTK_KEY_LABEL "FILS PTK Derivation" +#define FT_PMK_R0_KEY_LABEL "FT-R0" +#define FT_PMK_R0_NAME_KEY_LABEL "FT-R0N" +#define FT_PMK_R1_NAME_KEY_LABEL "FT-R1N" + +#define PMKR0_SCATTER_LIST_ELEM 2 +#define PMKR1_SCATTER_LIST_ELEM 4 + +#define SCTR_LST_ELEM0 0 +#define SCTR_LST_ELEM1 1 +#define SCTR_LST_ELEM2 2 +#define SCTR_LST_ELEM3 3 + +/* Length of "FT-R1N" */ +#define SCTR_LST_R0_LABEL_LEN 6 +#define SCTR_LST_R1_LABEL_LEN 6 + +#define MAX_ICK_LEN 48 +#define MAX_KEK_LEN 64 +#define MAX_TK_LEN 32 +#define MAX_KEY_AUTH_DATA_LEN 48 +#define MAX_GTK_LEN 255 +#define MAX_IGTK_LEN 255 +#define SIR_FILS_SESSION_IE_LEN 11 +#define FILS_KEY_RSC_LEN 8 +#define FILS_MAX_KEY_AUTH_LEN (MAX_ICK_LEN + MAX_KEK_LEN + MAX_TK_LEN) + +#define IPN_LEN 6 +#define FILS_SESSION_LENGTH 8 +#define FILS_MAX_KDE_LIST_LEN 255 +#define FILS_MAX_HLP_DATA_LEN 2048 + +/* 12.12.2.5.3 80211-ai draft */ +#define FILS_SHA384_KEK_LEN 64 +#define FILS_SHA256_KEK_LEN 32 + +/* 12.12.2.5.3 80211-ai draft */ +#define FILS_SHA256_ICK_LEN 32 +#define FILS_SHA384_ICK_LEN 48 + +#define TK_LEN_TKIP 32 +#define TK_LEN_CCMP 16 +#define TK_LEN_AES_128_CMAC 32 + +#define FILS_SHA256_PMK_LEN 32 +#define FILS_SHA384_PMK_LEN 48 + +#define FILS_FT_SHA256_LEN 32 +#define FILS_FT_SHA384_LEN 48 + +#define FILS_FT_MAX_R0_KEY_DATA_LEN 64 + +/* 12.7.1.7.3 802.11ai */ +#define FILS_SHA256_Q_LEN 32 +#define FILS_SHA384_Q_LEN 48 + +#define MAX_PRF_INTERATIONS_COUNT 255 + +/* 9.4.2.180 FILS Session element */ +#define SIR_FILS_SESSION_LENGTH 8 +#define SIR_FILS_SESSION_EXT_EID 4 + +/* 9.4.2.184 FILS HLP Container Element */ +#define SIR_FILS_HLP_EXT_EID 5 + +/* 9.4.2.190 FILS Nonce element */ +#define SIR_FILS_NONCE_LENGTH 16 +#define SIR_FILS_NONCE_EXT_EID 13 + +/*9.4.2.188 FILS Wrapped Data element */ +#define SIR_FILS_WRAPPED_DATA_MAX_SIZE 255 +#define SIR_FILS_WRAPPED_DATA_EXT_EID 8 + +/* RFC 6696 5.3.1: EAP-Initiate/Re-auth-Start Packet */ +#define SIR_FILS_EAP_REAUTH_PACKET_TYPE 1 +#define SIR_FILS_EAP_INIT_PACKET_TYPE 2 + +#define FILS_AUTH_TAG_MAX_LENGTH 32 + +#define SIR_FILS_OPTIONAL_DATA_LEN 3 +/* RFC 6696 4.3: RiK deriavtion */ +#define SIR_FILS_RIK_LABEL "Re-authentication Integrity Key@ietf.org" + +/* RFC 6696 5.3.1: EAP-Initiate/Re-auth-Start Packet */ +#define SIR_FILS_EAP_TLV_KEYNAME_NAI 1 +#define SIR_FILS_EAP_TLV_R_RK_LIFETIME 2 +#define SIR_FILS_EAP_TLV_R_MSK_LIFETIME 3 +#define SIR_FILS_EAP_TLV_DOMAIN_NAME 4 +#define SIR_FILS_EAP_TLV_CRYPTO_LIST 5 +#define SIR_FILS_EAP_TLV_AUTH_INDICATION 6 + +#define DATA_TYPE_GTK 1 +#define DATA_TYPE_IGTK 9 +#define KEY_RSC_LEN 8 +#define KDE_IE_DATA_OFFSET 4 +#define KDE_DATA_TYPE_OFFSET 3 +#define GTK_OFFSET 2 +#define IPN_OFFSET 2 +#define IGTK_OFFSET 8 + +#define KDE_OUI_TYPE "\x00\x0F\xAC" +#define KDE_OUI_TYPE_SIZE 3 + +#define SINGLE_ELEMENT_HASH_CNT 1 + +/* + * struct eap_auth_reserved: this structure defines flags format in eap packets + * as defined in RFC 6696 5.3.1 + * flag_r: + * flag_b: + * flag_l: + */ +struct eap_auth_reserved { + uint8_t flag_r:1; + uint8_t flag_b:1; + uint8_t flag_l:1; + uint8_t reverved:5; +}; + +/* + * enum fils_erp_cryptosuite: this enum defines the cryptosuites used + * to calculate auth tag and auth tag length as defined by RFC 6696 5.3.1 + * @HMAC_SHA256_64: sha256 with auth tag len as 64 bits + * @HMAC_SHA256_128: sha256 with auth tag len as 128 bits + * @HMAC_SHA256_256: sha256 with auth tag len as 256 bits + */ +enum fils_erp_cryptosuite { + INVALID_CRYPTO = 0, /* reserved */ + HMAC_SHA256_64, + HMAC_SHA256_128, + HMAC_SHA256_256, +}; + +/* + * struct fils_eap_tlv: this structure defines the eap header + * for eap packet present in warpped data element IE + * @type: type of packet + * @length: length of packet + * @data: pointer to eap data + */ +struct fils_eap_tlv { + uint8_t type; + uint8_t length; + uint8_t data[FILS_EAP_TLV_MAX_DATA_LEN]; +}; + +/* struct fils_auth_rsp_info: this structure saves the info from + * fils auth response. + * @keyname: pointer to keyname nai + * @keylength: keyname nai length + * @domain_name: pointer to domain name + * @domain_len: domain length + * @r_rk_lifetime: rRk lifetime + * @r_msk_lifetime: RMSK lifetime + * @sequence: sequence number to be validated + * @fils_nonce: anonce + * @assoc_delay: time in ms, DUT needs to wait after association req + */ +struct fils_auth_rsp_info { + uint8_t *keyname; + uint8_t keylength; + uint8_t *domain_name; + uint8_t domain_len; + uint32_t r_rk_lifetime; + uint32_t r_msk_lifetime; + uint16_t sequence; + uint8_t fils_nonce[SIR_FILS_NONCE_LENGTH]; + uint8_t assoc_delay; +}; + +#define FT_R0KH_ID_MAX_LEN 48 +#define FT_R1KH_ID_LEN 6 +#define FT_NONCE_LEN 32 + +/* MIC Length Specified in Table 12-8- 802.11-2016 Spec */ +#define FT_MIC_LEN 16 +#define FT_GTK_RSC_LEN 8 +#define FT_GTK_KEY_LEN 32 +#define FT_IGTK_KEY_ID_LEN 2 +#define FT_IGTK_IPN_LEN 6 +#define FT_IGTK_KEY_LEN 24 + +/** + * struct mac_ft_gtk_ie - structure to parse the gtk ie + * @present: flag to indicate ie is present + * @key_id: Key-Id + * @reserved: reserved bits + * @key_length: gtk key length + * @rsc: denotes the last TSC or PN sent using the GTK + * @num_key: number of keys + * @key: actual keys + */ +struct mac_ft_gtk_ie { + uint8_t present; + uint16_t key_id:2; + uint16_t reserved:14; + uint8_t key_len; + uint8_t rsc[FT_GTK_RSC_LEN]; + uint8_t num_key; + uint8_t key[FT_GTK_KEY_LEN]; +}; + +/** + * struct mac_ft_gtk_ie - structure to parse the gtk ie + * @present: IE present or not present + * @key_id: 2Byte Key-ID + * @ipn: icorresponds to the last packet number used by broadcaster/multicaster + * @key_len: IGTK key length + * @key: IGTK Key + */ +struct mac_ft_igtk_ie { + uint8_t present; + uint8_t key_id[FT_IGTK_KEY_ID_LEN]; + uint8_t ipn[FT_IGTK_IPN_LEN]; + uint8_t key_len; + uint8_t key[FT_IGTK_KEY_LEN]; +}; + +/** + * struct mac_ft_ie - structure to parse the FT ie from auth frame + * @present: true if IE is present in Auth Frame + * @element_count: number of elements + * @mic: MIC. Will be zero in auth frame sent from AP. (Refer 13.2.4 802.11ai) + * @anonce: Authenticator NONCE. Will be zero in auth frame sent from AP. + * @snonce: Supplicant NONCE. Will be zero in auth frame + * @r1kh_id: R1KH ID. Length of R1KH ID is fixed(6 bytes). + * @r0kh_id_len: Length of R0KH ID + * @r0kh_id: R0KH id + * @gtk_ie: GTK subelement in FTIE + * @igtk_ie: IGTK subelement in FTIE + */ +struct mac_ft_ie { + bool present; + uint8_t element_count; + uint8_t mic[FT_MIC_LEN]; + uint8_t anonce[FT_NONCE_LEN]; + uint8_t snonce[FT_NONCE_LEN]; + uint8_t r1kh_id[FT_R1KH_ID_LEN]; + uint8_t r0kh_id_len; + uint8_t r0kh_id[FT_R0KH_ID_MAX_LEN]; + struct mac_ft_gtk_ie gtk_ie; + struct mac_ft_igtk_ie igtk_ie; +}; + +#define FILS_PMK_LEN 48 +#define FILS_PMK_NAME_LEN 16 +#define FILS_FT_MAX_LEN 48 +#define FILS_FT_PMK_R0_SALT_LEN 16 +#define FILS_MAX_KEY_DATA_LEN \ + (MAX_ICK_LEN + MAX_KEK_LEN + MAX_TK_LEN + FILS_FT_MAX_LEN) + +/* + * struct pe_fils_session: fils session info used in PE session + * @is_fils_connection: whether connection is fils or not + * @keyname_nai_data: keyname nai data + * @keyname_nai_length: keyname nai length + * @akm: akm type will be used + * @auth: authentication type + * @cipher: cipher type + * @fils_erp_reauth_pkt: pointer to fils reauth packet data + * @fils_erp_reauth_pkt_len: reauth packet length + * @fils_rrk: pointer to fils rRk + * @fils_rrk_len: fils rRk length + * @fils_rik: pointer to fils rIk + * @fils_rik_len: fils rIk length + * @sequence_number: sequence number needs to be used in eap packet + * @fils_session: fils session IE element + * @fils_nonce: fils snonce + * @rsn_ie: rsn ie used in auth request + * @rsn_ie_len: rsn ie length + * @group_mgmt_cipher_suite_present: Check if group management cipher suite + * is present in the FILS RSN IE + * @ft_ie: structure to store the parsed FTIE from auth response frame + * @pmkr0: PMKR0 + * @pmkr0_len: length of PMKR0 key + * @pmkr0_name: PMK_R0 name derived + * @pmkr1_name: PMKR1 Name derived + * @fils_eap_finish_pkt: pointer to eap finish packet + * @fils_eap_finish_pkt_len: eap finish packet length + * @fils_rmsk: rmsk data pointer + * @fils_rmsk_len: rmsk data length + * @fils_pmk: pointer to pmk data + * @fils_pmk_len: pmk length + * @fils_pmkid: pointer to pmkid derived + * @auth_info: data obtained from auth response + * @ick: pointer to ick + * @ick_len: ick length + * @kek: pointer to kek + * @kek_len: kek length + * @tk: pointer to tk + * @tk_len: tk length + * @key_auth: data needs to be sent in assoc req, will be validated by AP + * @key_auth_len: key auth data length + * @ap_key_auth_data: data needs to be validated in assoc rsp + * @ap_key_auth_len: ap key data length + * @gtk_len: gtk key length + * @gtk: pointer to gtk data + * @fils_ft: xx_key data + * @fils_ft_len: xx_key length + * @rsc: rsc value + * @igtk_len: igtk length + * @igtk: igtk data pointer + * @ipn: pointer to ipn data + * @dst_mac: HLP destination mac address + * @src_mac: HLP source mac address + * @hlp_data_len: HLP data length + * @hlp_data: pointer to HLP data + */ +struct pe_fils_session { + bool is_fils_connection; + uint8_t *keyname_nai_data; + uint8_t keyname_nai_length; + uint8_t akm; + uint8_t auth; + uint8_t cipher; + uint8_t *fils_erp_reauth_pkt; + uint32_t fils_erp_reauth_pkt_len; + uint8_t *fils_rrk; + uint8_t fils_rrk_len; + uint8_t *fils_rik; + uint32_t fils_rik_len; + uint16_t sequence_number; + uint8_t fils_session[SIR_FILS_SESSION_LENGTH]; + uint8_t fils_nonce[SIR_FILS_NONCE_LENGTH]; + uint8_t rsn_ie[WLAN_MAX_IE_LEN]; + uint8_t rsn_ie_len; + bool group_mgmt_cipher_present; + struct mac_ft_ie ft_ie; + uint8_t pmkr0[FILS_PMK_LEN]; + uint8_t pmkr0_len; + uint8_t pmkr0_name[FILS_PMK_NAME_LEN]; + uint8_t pmkr1_name[FILS_PMK_NAME_LEN]; + uint8_t *fils_eap_finish_pkt; + uint8_t fils_eap_finish_pkt_len; + uint8_t *fils_rmsk; + uint8_t fils_rmsk_len; + uint8_t *fils_pmk; + uint8_t fils_pmk_len; + uint8_t fils_pmkid[PMKID_LEN]; + struct fils_auth_rsp_info auth_info; + uint8_t ick[MAX_ICK_LEN]; + uint8_t ick_len; + uint8_t kek[MAX_KEK_LEN]; + uint8_t kek_len; + uint8_t tk[MAX_TK_LEN]; + uint8_t tk_len; + uint8_t fils_ft[FILS_FT_MAX_LEN]; + uint8_t fils_ft_len; + uint8_t key_auth[MAX_KEY_AUTH_DATA_LEN]; + uint8_t key_auth_len; + uint8_t ap_key_auth_data[MAX_KEY_AUTH_DATA_LEN]; + uint8_t ap_key_auth_len; + uint8_t gtk_len; + uint8_t gtk[MAX_GTK_LEN]; + uint8_t rsc; + uint8_t igtk_len; + uint8_t igtk[MAX_IGTK_LEN]; + uint8_t ipn[IPN_LEN]; + struct qdf_mac_addr dst_mac; + struct qdf_mac_addr src_mac; + uint16_t hlp_data_len; + uint8_t *hlp_data; +}; diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_ft.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_ft.h new file mode 100644 index 0000000000000000000000000000000000000000..f26d5c8c81734909010732665490ff9d2a5350f8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_ft.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + Macros and Function prototypes FT and 802.11R purposes + + ========================================================================*/ + +#ifndef __LIMFT_H__ +#define __LIMFT_H__ + +#include +#include +#include +#include + +/*------------------------------------------------------------------------- + Function declarations and documenation + ------------------------------------------------------------------------*/ +void lim_ft_open(struct mac_context *mac, struct pe_session *pe_session); +void lim_ft_cleanup(struct mac_context *mac, struct pe_session *pe_session); +#ifdef WLAN_FEATURE_HOST_ROAM +void lim_ft_cleanup_pre_auth_info(struct mac_context *mac, + struct pe_session *pe_session); +int lim_process_ft_pre_auth_req(struct mac_context *mac, + struct scheduler_msg *pMsg); +void lim_process_ft_preauth_rsp_timeout(struct mac_context *mac); + +/** + * lim_process_mlm_ft_reassoc_req() - Handle the Reassoc request + * @mac: Global MAC context + * @reassoc_req: reassoc req + * + * This function handles the Reassoc Req from SME + * + * Return: None + */ +void lim_process_mlm_ft_reassoc_req(struct mac_context *mac, + tLimMlmReassocReq *reassoc_req); +void lim_perform_ft_pre_auth(struct mac_context *mac, QDF_STATUS status, + uint32_t *data, struct pe_session *pe_session); +void lim_post_ft_pre_auth_rsp(struct mac_context *mac, QDF_STATUS status, + uint8_t *auth_rsp, uint16_t auth_rsp_length, + struct pe_session *pe_session); +void lim_handle_ft_pre_auth_rsp(struct mac_context *mac, QDF_STATUS status, + uint8_t *auth_rsp, uint16_t auth_rsp_len, + struct pe_session *pe_session); +QDF_STATUS lim_ft_setup_auth_session(struct mac_context *mac, + struct pe_session *pe_session); +void lim_process_mlm_reassoc_cnf(struct mac_context *mac_ctx, uint32_t *msg); +/** + * lim_process_sta_mlm_add_bss_rsp_ft() - Handle ft add bss response + * @mac: Global MAC context + * @add_bss_rsp: Bss params rsp data + * @pe_session: PE Session + * + * Function to handle fast roaming add bss response in FT reassoc state, + * send reassociation Request. + * + * Return: None + */ +void lim_process_sta_mlm_add_bss_rsp_ft(struct mac_context *mac, + struct add_bss_rsp *add_bss_rsp, + struct pe_session *pe_session); +void lim_process_mlm_reassoc_req(struct mac_context *mac_ctx, + tLimMlmReassocReq *reassoc_req); + +/** + * lim_preauth_scan_event_handler() - Process preauth scan events + * @mac_ctx:Pointer to global MAC structure + * @event: Scan event + * @vdev_id: vdev id + * @scan_id: scan id from WMA scan event. + * + * If scan event signifies failure or successful completion, operation + * is complete. + * If scan event signifies that STA is on foreign channel, send auth frame + * + * Return: void + */ +void lim_preauth_scan_event_handler(struct mac_context *mac_ctx, + enum sir_scan_event_type event, + uint8_t vdev_id, uint32_t scan_id); +QDF_STATUS lim_send_preauth_scan_offload(struct mac_context *mac_ctx, + struct pe_session *session_entry, tSirFTPreAuthReq *ft_preauth_req); +#else +static inline void lim_ft_cleanup_pre_auth_info(struct mac_context *mac, + struct pe_session *pe_session) +{} +static inline void lim_process_ft_preauth_rsp_timeout(struct mac_context *mac) +{} +static inline +void lim_process_mlm_ft_reassoc_req(struct mac_context *mac, + tLimMlmReassocReq *reassoc_req) +{} +static inline void lim_handle_ft_pre_auth_rsp(struct mac_context *mac, + QDF_STATUS status, uint8_t *auth_rsp, + uint16_t auth_rsp_len, struct pe_session *pe_session) +{} +static inline void lim_process_mlm_reassoc_cnf(struct mac_context *mac_ctx, + uint32_t *msg) +{} +static inline +void lim_process_sta_mlm_add_bss_rsp_ft(struct mac_context *mac, + struct add_bss_rsp *add_bss_rsp, + struct pe_session *pe_session) +{} +static inline void lim_process_mlm_reassoc_req(struct mac_context *mac_ctx, + tLimMlmReassocReq *reassoc_req) +{} +static inline void lim_preauth_scan_event_handler(struct mac_context *mac_ctx, + enum sir_scan_event_type event, + uint8_t vdev_id, uint32_t scan_id) +{} +static inline int lim_process_ft_pre_auth_req(struct mac_context *mac, + struct scheduler_msg *pMsg) +{ + return 0; +} +#endif + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +void lim_fill_ft_session(struct mac_context *mac, + struct bss_description *pbssDescription, + struct pe_session *ft_session, + struct pe_session *pe_session, + enum wlan_phymode bss_phymode); + +/** + * lim_ft_prepare_add_bss_req() - Create Add Bss Req to the new AP + * @mac: Global MAC context + * @add_bss_params: Bss params including rsp data + * @pe_session: PE Session + * + * This will be used when we are ready to FT to the new AP. + * The newly created ft Session entry is passed to this function + * + * Return: None + */ +void lim_ft_prepare_add_bss_req(struct mac_context *mac, + struct pe_session *ft_session, + struct bss_description *bssDescription); + +QDF_STATUS lim_send_preauth_scan_offload(struct mac_context *mac_ctx, + struct pe_session *session_entry, tSirFTPreAuthReq *ft_preauth_req); +#else +static inline void lim_fill_ft_session(struct mac_context *mac, + struct bss_description *pbssDescription, + struct pe_session *ft_session, + struct pe_session *pe_session, + enum wlan_phymode bss_phymode) +{} +static inline void lim_ft_prepare_add_bss_req(struct mac_context *mac, + struct pe_session *ft_session, + struct bss_description *bssDescription) +{} +#endif + +QDF_STATUS lim_process_ft_aggr_qos_req(struct mac_context *mac, + uint32_t *msg_buf); +void lim_process_ft_aggr_qos_rsp(struct mac_context *mac, + struct scheduler_msg *limMsg); +void lim_ft_cleanup_all_ft_sessions(struct mac_context *mac); +#endif /* __LIMFT_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_ft_defs.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_ft_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..b851c1a52613e3193c32a6d19de15b5fe89cc74e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_ft_defs.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + Macros and Function prototypes FT and 802.11R purposes + + ========================================================================*/ + +#ifndef __LIMFTDEFS_H__ +#define __LIMFTDEFS_H__ + +#include +#include "wma_if.h" + +/*-------------------------------------------------------------------------- + Preprocessor definitions and constants + ------------------------------------------------------------------------*/ +#define MAX_FTIE_SIZE 384 /* Max size limited to 384, on acct. of IW custom events */ + +/* Time to dwell on preauth channel during roaming, in milliseconds */ +#define LIM_FT_PREAUTH_SCAN_TIME 50 + +/*-------------------------------------------------------------------------- + Type declarations + ------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------- + FT Pre Auth Req SME<->PE + ------------------------------------------------------------------------*/ +typedef struct sSirFTPreAuthReq { + uint16_t messageType; /* eWNI_SME_FT_PRE_AUTH_REQ */ + uint16_t length; + uint32_t dot11mode; + /* + * Track if response is processed for this request + * We expect only one response per request. + */ + bool bPreAuthRspProcessed; + uint16_t pre_auth_channel_freq; + /* BSSID currently associated to suspend the link */ + tSirMacAddr currbssId; + tSirMacAddr preAuthbssId; /* BSSID to preauth to */ + tSirMacAddr self_mac_addr; + uint32_t scan_id; + uint16_t ft_ies_length; + uint8_t ft_ies[MAX_FTIE_SIZE]; + struct bss_description *pbssDescription; +} tSirFTPreAuthReq, *tpSirFTPreAuthReq; + +/*------------------------------------------------------------------------- + FT Pre Auth Rsp PE<->SME + ------------------------------------------------------------------------*/ +typedef struct sSirFTPreAuthRsp { + uint16_t messageType; /* eWNI_SME_FT_PRE_AUTH_RSP */ + uint16_t length; + uint8_t vdev_id; + tSirMacAddr preAuthbssId; /* BSSID to preauth to */ + QDF_STATUS status; + uint16_t ft_ies_length; + uint8_t ft_ies[MAX_FTIE_SIZE]; + uint16_t ric_ies_length; + uint8_t ric_ies[MAX_FTIE_SIZE]; +} tSirFTPreAuthRsp, *tpSirFTPreAuthRsp; + +/*-------------------------------------------------------------------------- + FT Pre Auth Rsp Key SME<->PE + ------------------------------------------------------------------------*/ +typedef struct sSirFTUpdateKeyInfo { + uint16_t messageType; + uint16_t length; + uint32_t vdev_id; + struct qdf_mac_addr bssid; + tSirKeyMaterial keyMaterial; +} tSirFTUpdateKeyInfo, *tpSirFTUpdateKeyInfo; + +/*-------------------------------------------------------------------------- + FT Pre Auth Rsp Key SME<->PE + ------------------------------------------------------------------------*/ +typedef struct sSirFTPreAuthKeyInfo { + uint8_t extSetStaKeyParamValid; /* Ext Bss Config Msg if set */ + /* SetStaKeyParams for ext bss msg */ + tLimMlmSetKeysReq extSetStaKeyParam; +} tSirFTPreAuthKeyInfo, *tpSirFTPreAuthKeyInfo; + +/*------------------------------------------------------------------------- + Global FT Information + ------------------------------------------------------------------------*/ +typedef struct sFTPEContext { + tpSirFTPreAuthReq pFTPreAuthReq; /* Saved FT Pre Auth Req */ + QDF_STATUS ftPreAuthStatus; + uint16_t saved_auth_rsp_length; + uint8_t saved_auth_rsp[MAX_FTIE_SIZE]; + tSirFTPreAuthKeyInfo PreAuthKeyInfo; + /* Items created for the new FT, session */ + void *pAddBssReq; /* Save add bss req */ + void *pAddStaReq; /*Save add sta req */ + uint32_t peSessionId; + uint32_t smeSessionId; + + /* This flag is required to indicate on which session the preauth + * has taken place, since the auth response for preauth will come + * for a new BSSID for which there is no session yet. This flag + * will be used to extract the session from the session preauth + * has been initiated + */ + bool ftPreAuthSession; +} tftPEContext, *tpftPEContext; + +#endif /* __LIMFTDEFS_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_global.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_global.h new file mode 100644 index 0000000000000000000000000000000000000000..6a71333aaae5051ce494cd16b38524fb782a0e05 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_global.h @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_global.h contains the definitions exported by + * LIM module. + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#ifndef __LIM_GLOBAL_H +#define __LIM_GLOBAL_H + +#include "wni_api.h" +#include "sir_api.h" +#include "sir_mac_prot_def.h" +#include "sir_mac_prop_exts.h" +#include "sir_common.h" +#include "sir_debug.h" +#include "wni_cfg.h" +#include "csr_api.h" +#include "sap_api.h" +#include "dot11f.h" +#include "wma_if.h" + +/* Deferred Message Queue Length */ +#define MAX_DEFERRED_QUEUE_LEN 80 + +#ifdef CHANNEL_HOPPING_ALL_BANDS +#define CHAN_HOP_ALL_BANDS_ENABLE 1 +#else +#define CHAN_HOP_ALL_BANDS_ENABLE 0 +#endif + +/* enums exported by LIM are as follows */ + +/*System role definition */ +typedef enum eLimSystemRole { + eLIM_UNKNOWN_ROLE, + eLIM_AP_ROLE, + eLIM_STA_IN_IBSS_ROLE, + eLIM_STA_ROLE, + eLIM_P2P_DEVICE_ROLE, + eLIM_P2P_DEVICE_GO, + eLIM_P2P_DEVICE_CLIENT, + eLIM_NDI_ROLE +} tLimSystemRole; + +/* + * SME state definition accessible across all Sirius modules. + * AP only states are LIM_SME_CHANNEL_SCAN_STATE & + * LIM_SME_NORMAL_CHANNEL_SCAN_STATE. + * Note that these states may also be present in STA + * side too when DFS support is present for a STA in IBSS mode. + */ +typedef enum eLimSmeStates { + eLIM_SME_OFFLINE_STATE, + eLIM_SME_IDLE_STATE, + eLIM_SME_SUSPEND_STATE, + eLIM_SME_WT_JOIN_STATE, + eLIM_SME_WT_AUTH_STATE, + eLIM_SME_WT_ASSOC_STATE, + eLIM_SME_WT_REASSOC_STATE, + eLIM_SME_JOIN_FAILURE_STATE, + eLIM_SME_ASSOCIATED_STATE, + eLIM_SME_REASSOCIATED_STATE, + eLIM_SME_LINK_EST_STATE, + eLIM_SME_WT_PRE_AUTH_STATE, + eLIM_SME_WT_DISASSOC_STATE, + eLIM_SME_WT_DEAUTH_STATE, + eLIM_SME_WT_START_BSS_STATE, + eLIM_SME_WT_STOP_BSS_STATE, + eLIM_SME_NORMAL_STATE, +} tLimSmeStates; + +/* + * MLM state definition. + * While these states are present on AP too when it is + * STA mode, per-STA MLM state exclusive to AP is: + * eLIM_MLM_WT_AUTH_FRAME3. + */ +typedef enum eLimMlmStates { + eLIM_MLM_OFFLINE_STATE, + eLIM_MLM_IDLE_STATE, + eLIM_MLM_WT_JOIN_BEACON_STATE, + eLIM_MLM_JOINED_STATE, + eLIM_MLM_BSS_STARTED_STATE, + eLIM_MLM_WT_AUTH_FRAME2_STATE, + eLIM_MLM_WT_AUTH_FRAME3_STATE, + eLIM_MLM_WT_AUTH_FRAME4_STATE, + eLIM_MLM_AUTH_RSP_TIMEOUT_STATE, + eLIM_MLM_AUTHENTICATED_STATE, + eLIM_MLM_WT_ASSOC_RSP_STATE, + eLIM_MLM_WT_REASSOC_RSP_STATE, + eLIM_MLM_ASSOCIATED_STATE, + eLIM_MLM_REASSOCIATED_STATE, + eLIM_MLM_LINK_ESTABLISHED_STATE, + eLIM_MLM_WT_ASSOC_CNF_STATE, + eLIM_MLM_WT_ADD_BSS_RSP_STATE, + eLIM_MLM_WT_DEL_BSS_RSP_STATE, + eLIM_MLM_WT_ADD_BSS_RSP_ASSOC_STATE, + eLIM_MLM_WT_ADD_BSS_RSP_REASSOC_STATE, + eLIM_MLM_WT_ADD_BSS_RSP_PREASSOC_STATE, + eLIM_MLM_WT_ADD_STA_RSP_STATE, + eLIM_MLM_WT_DEL_STA_RSP_STATE, + /* + * MLM goes to this state when LIM initiates DELETE_STA + * as processing of Assoc req because the entry already exists. + * LIM comes out of this state when DELETE_STA response from + * HAL is received. LIM needs to maintain this state so that ADD_STA + * can be issued while processing DELETE_STA response from HAL. + */ + eLIM_MLM_WT_ASSOC_DEL_STA_RSP_STATE, + eLIM_MLM_WT_SET_BSS_KEY_STATE, + eLIM_MLM_WT_SET_STA_KEY_STATE, + eLIM_MLM_WT_SET_STA_BCASTKEY_STATE, + eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE, + eLIM_MLM_WT_FT_REASSOC_RSP_STATE, + eLIM_MLM_WT_SAE_AUTH_STATE, +} tLimMlmStates; + +/* 11h channel switch states */ + +/* + * This enum indicates in which state the channel-swith + * is presently operating. + * eLIM_11H_CHANSW_INIT - Default state + * eLIM_11H_CHANSW_RUNNING - When channel switch is running + * eLIM_11H_CHANSW_END - After channel switch is complete + */ +typedef enum eLimDot11hChanSwStates { + eLIM_11H_CHANSW_INIT, + eLIM_11H_CHANSW_RUNNING, + eLIM_11H_CHANSW_END +} tLimDot11hChanSwStates; + +/* MLM Req/Cnf structure definitions */ +typedef struct sLimMlmAuthReq { + tSirMacAddr peerMacAddr; + tAniAuthType authType; + uint8_t sessionId; +} tLimMlmAuthReq, *tpLimMlmAuthReq; + +typedef struct sLimMlmJoinReq { + tSirMacRateSet operationalRateSet; + uint8_t sessionId; + struct bss_description bssDescription; + /* + * WARNING: Pls make bssDescription as last variable in struct + * tLimMlmJoinReq as it has ieFields followed after this bss + * description. Adding a variable after this corrupts the ieFields + */ +} tLimMlmJoinReq, *tpLimMlmJoinReq; + +/* Forward declarations */ +struct sSirAssocReq; +struct sDphHashNode; + +/* struct lim_assoc_data - Assoc data to be cached to defer association + * indication to SME + * @present: Indicates whether assoc data is present or not + * @sub_type: Indicates whether it is Association Request(=0) or Reassociation + * Request(=1) frame + * @hdr: MAC header + * @assoc_req: pointer to parsed ASSOC/REASSOC Request frame + * @pmf_connection: flag indicating pmf connection + * @assoc_req_copied: boolean to indicate if assoc req was copied to tmp above + * @dup_entry: flag indicating if duplicate entry found + * @sta_ds: station dph entry + */ +struct lim_assoc_data { + bool present; + uint8_t sub_type; + tSirMacMgmtHdr hdr; + struct sSirAssocReq *assoc_req; + bool pmf_connection; + bool assoc_req_copied; + bool dup_entry; + struct sDphHashNode *sta_ds; +}; + +/* Pre-authentication structure definition */ +typedef struct tLimPreAuthNode { + struct tLimPreAuthNode *next; + tSirMacAddr peerMacAddr; + tAniAuthType authType; + tLimMlmStates mlmState; + uint8_t authNodeIdx; + uint8_t challengeText[SIR_MAC_AUTH_CHALLENGE_LENGTH]; + uint8_t fTimerStarted:1; + uint8_t fSeen:1; + uint8_t fFree:1; + uint8_t rsvd:5; + TX_TIMER timer; + uint16_t seq_num; + unsigned long timestamp; + /* keeping copy of association request received, this is + * to defer the association request processing + */ + struct lim_assoc_data assoc_req; +} tLimPreAuthNode, *tpLimPreAuthNode; + +/* Pre-authentication table definition */ +typedef struct tLimPreAuthTable { + uint32_t numEntry; + tLimPreAuthNode **pTable; +} tLimPreAuthTable, *tpLimPreAuthTable; + +/** + * struct lim_sta_context - LIM per STA structure + * @mlmState: LIM State + * @authType: Authentication algorithm + * @akm_type: AKM of the connection + * @listenInterval: Listen interval + * @capabilityInfo: Capabilities + * @disassocReason: Disassociation reason code + * @resultCode: Result code + * @subType: Indicates association or reassociation + * @updateContext: Update context + * @schClean: Scheduler clean + * @htCapability: 802.11n HT capability + * @vhtCapability: 802.11ac VHT capability + * @cleanupTrigger: Cleanup trigger + * @protStatusCode: Protocol Status code + * @he_capable: 802.11ax HE capability + * @owe_ie: Pointer to OWE IE + * @owe_ie_len: Length of OWE IE + */ +struct lim_sta_context { + tLimMlmStates mlmState; + tAniAuthType authType; /* auth algo in auth frame */ + enum ani_akm_type akm_type; /* akm in rsn/wpa ie */ + uint16_t listenInterval; + tSirMacCapabilityInfo capabilityInfo; + tSirMacReasonCodes disassocReason; + + tSirResultCodes resultCode; + + uint8_t subType:1; /* Indicates ASSOC (0) or REASSOC (1) */ + uint8_t updateContext:1; + uint8_t schClean:1; + /* 802.11n HT Capability in Station: Enabled 1 or DIsabled 0 */ + uint8_t htCapability:1; + uint8_t vhtCapability:1; + uint16_t cleanupTrigger; + uint16_t protStatusCode; +#ifdef WLAN_FEATURE_11AX + bool he_capable; +#endif + bool force_1x1; + uint8_t *owe_ie; + uint32_t owe_ie_len; +}; + +/* Structure definition to hold deferred messages queue parameters */ +typedef struct sLimDeferredMsgQParams { + struct scheduler_msg deferredQueue[MAX_DEFERRED_QUEUE_LEN]; + uint16_t size; + uint16_t read; + uint16_t write; +} tLimDeferredMsgQParams, *tpLimDeferredMsgQParams; + +typedef struct sCfgProtection { + uint32_t overlapFromlla:1; + uint32_t overlapFromllb:1; + uint32_t overlapFromllg:1; + uint32_t overlapHt20:1; + uint32_t overlapNonGf:1; + uint32_t overlapLsigTxop:1; + uint32_t overlapRifs:1; + uint32_t overlapOBSS:1; /* added for obss */ + uint32_t fromlla:1; + uint32_t fromllb:1; + uint32_t fromllg:1; + uint32_t ht20:1; + uint32_t nonGf:1; + uint32_t lsigTxop:1; + uint32_t rifs:1; + uint32_t obss:1; /* added for Obss */ +} tCfgProtection, *tpCfgProtection; + +typedef enum eLimProtStaCacheType { + eLIM_PROT_STA_CACHE_TYPE_INVALID, + eLIM_PROT_STA_CACHE_TYPE_llB, + eLIM_PROT_STA_CACHE_TYPE_llG, + eLIM_PROT_STA_CACHE_TYPE_HT20 +} tLimProtStaCacheType; + +typedef struct sCacheParams { + uint8_t active; + tSirMacAddr addr; + tLimProtStaCacheType protStaCacheType; + +} tCacheParams, *tpCacheParams; + +#define LIM_PROT_STA_OVERLAP_CACHE_SIZE HAL_NUM_ASSOC_STA +#define LIM_PROT_STA_CACHE_SIZE HAL_NUM_ASSOC_STA + +typedef struct sLimProtStaParams { + uint8_t numSta; + uint8_t protectionEnabled; +} tLimProtStaParams, *tpLimProtStaParams; + +typedef struct sLimNoShortParams { + uint8_t numNonShortPreambleSta; + tCacheParams staNoShortCache[LIM_PROT_STA_CACHE_SIZE]; +} tLimNoShortParams, *tpLimNoShortParams; + +typedef struct sLimNoShortSlotParams { + uint8_t numNonShortSlotSta; + tCacheParams staNoShortSlotCache[LIM_PROT_STA_CACHE_SIZE]; +} tLimNoShortSlotParams, *tpLimNoShortSlotParams; + +typedef struct tLimIbssPeerNode tLimIbssPeerNode; +struct tLimIbssPeerNode { + tLimIbssPeerNode *next; + tSirMacAddr peerMacAddr; + uint8_t extendedRatesPresent:1; + uint8_t edcaPresent:1; + uint8_t wmeEdcaPresent:1; + uint8_t wmeInfoPresent:1; + uint8_t htCapable:1; + uint8_t vhtCapable:1; + uint8_t rsvd:2; + uint8_t htSecondaryChannelOffset; + tSirMacCapabilityInfo capabilityInfo; + tSirMacRateSet supportedRates; + tSirMacRateSet extendedRates; + uint8_t supportedMCSSet[SIZE_OF_SUPPORTED_MCS_SET]; + tSirMacEdcaParamSetIE edcaParams; + uint8_t erpIePresent; + + /* HT Capabilities of IBSS Peer */ + uint8_t htGreenfield; + uint8_t htShortGI40Mhz; + uint8_t htShortGI20Mhz; + + /* DSSS/CCK at 40 MHz: Enabled 1 or Disabled */ + uint8_t htDsssCckRate40MHzSupport; + + /* MIMO Power Save */ + tSirMacHTMIMOPowerSaveState htMIMOPSState; + + /* */ + /* A-MPDU Density */ + /* 000 - No restriction */ + /* 001 - 1/8 usec */ + /* 010 - 1/4 usec */ + /* 011 - 1/2 usec */ + /* 100 - 1 usec */ + /* 101 - 2 usec */ + /* 110 - 4 usec */ + /* 111 - 8 usec */ + /* */ + uint8_t htAMpduDensity; + + /* Maximum Rx A-MPDU factor */ + uint8_t htMaxRxAMpduFactor; + + /* Set to 0 for 3839 octets */ + /* Set to 1 for 7935 octets */ + uint8_t htMaxAmsduLength; + + /* */ + /* Recommended Tx Width Set */ + /* 0 - use 20 MHz channel (control channel) */ + /* 1 - use 40 Mhz channel */ + /* */ + uint8_t htSupportedChannelWidthSet; + + uint8_t htLdpcCapable; + + uint8_t beaconHBCount; + uint8_t heartbeatFailure; + + uint8_t *beacon; /* Hold beacon to be sent to HDD/CSR */ + uint16_t beaconLen; + + tDot11fIEVHTCaps VHTCaps; + uint8_t vhtSupportedChannelWidthSet; + uint8_t vhtBeamFormerCapable; + /* + * Peer Atim Info + */ + uint8_t atimIePresent; + uint32_t peerAtimWindowLength; +}; + +/* Enums used for channel switching. */ +typedef enum eLimChannelSwitchState { + eLIM_CHANNEL_SWITCH_IDLE, + eLIM_CHANNEL_SWITCH_PRIMARY_ONLY, + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY +} tLimChannelSwitchState; + +/* Channel Switch Info */ +typedef struct sLimChannelSwitchInfo { + tLimChannelSwitchState state; + uint32_t sw_target_freq; + uint8_t primaryChannel; + uint8_t ch_center_freq_seg0; + uint8_t ch_center_freq_seg1; + uint8_t sec_ch_offset; + enum phy_ch_width ch_width; + int8_t switchCount; + uint32_t switchTimeoutValue; + uint8_t switchMode; +} tLimChannelSwitchInfo, *tpLimChannelSwitchInfo; + +typedef struct sLimOperatingModeInfo { + uint8_t present; + uint8_t chanWidth:2; + uint8_t reserved:2; + uint8_t rxNSS:3; + uint8_t rxNSSType:1; +} tLimOperatingModeInfo, *tpLimOperatingModeInfo; + +typedef struct sLimWiderBWChannelSwitch { + uint8_t newChanWidth; + uint8_t newCenterChanFreq0; + uint8_t newCenterChanFreq1; +} tLimWiderBWChannelSwitchInfo, *tpLimWiderBWChannelSwitchInfo; + +typedef struct sLimTspecInfo { + /* 0==free, else used */ + uint8_t inuse; + /* index in list */ + uint8_t idx; + tSirMacAddr staAddr; + uint16_t assocId; + struct mac_tspec_ie tspec; + /* number of Tclas elements */ + uint8_t numTclas; + tSirTclasInfo tclasInfo[SIR_MAC_TCLASIE_MAXNUM]; + uint8_t tclasProc; + /* tclassProc is valid only if this is set to 1. */ + uint8_t tclasProcPresent:1; +} qdf_packed tLimTspecInfo, *tpLimTspecInfo; + +typedef struct sLimAdmitPolicyInfo { + /* admit control policy type */ + uint8_t type; + /* oversubscription factor : 0 means nothing is allowed */ + uint8_t bw_factor; + /* valid only when 'type' is set BW_FACTOR */ +} tLimAdmitPolicyInfo, *tpLimAdmitPolicyInfo; + +typedef enum eLimWscEnrollState { + eLIM_WSC_ENROLL_NOOP, + eLIM_WSC_ENROLL_BEGIN, + eLIM_WSC_ENROLL_IN_PROGRESS, + eLIM_WSC_ENROLL_END +} tLimWscEnrollState; + +#define WSC_PASSWD_ID_PUSH_BUTTON (0x0004) + +typedef struct sLimWscIeInfo { + bool apSetupLocked; + bool selectedRegistrar; + uint16_t selectedRegistrarConfigMethods; + tLimWscEnrollState wscEnrollmentState; + tLimWscEnrollState probeRespWscEnrollmentState; + uint8_t reqType; + uint8_t respType; +} tLimWscIeInfo, *tpLimWscIeInfo; + +/* maximum number of tspec's supported */ +#define LIM_NUM_TSPEC_MAX 15 + +/* structure to hold all 11h specific data */ +typedef struct sLimSpecMgmtInfo { + tLimDot11hChanSwStates dot11hChanSwState; +} tLimSpecMgmtInfo, *tpLimSpecMgmtInfo; + +/** + * struct lim_delba_req_info - Delba request struct + * @vdev_id: vdev id + * @peer_macaddr: peer mac address + * @tid: tid + * @reason_code: reason code + */ +struct lim_delba_req_info { + uint8_t vdev_id; + tSirMacAddr peer_macaddr; + uint8_t tid; + uint8_t reason_code; +}; +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_process_fils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_process_fils.h new file mode 100644 index 0000000000000000000000000000000000000000..cdf864ebe492f72c9b60e6b9da42197e8ec0c196 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_process_fils.h @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#ifdef WLAN_FEATURE_FILS_SK + +/** + * lim_process_fils_auth_frame2()- This API processes fils data from auth resp + * @mac_ctx: mac context + * @session: PE session + * @rx_auth_frm_body: pointer to auth frame + * + * Return: true if fils data needs to be processed else false + */ +bool lim_process_fils_auth_frame2(struct mac_context *mac_ctx, + struct pe_session *pe_session, + tSirMacAuthFrameBody * rx_auth_frm_body); + +/** + * lim_add_fils_data_to_auth_frame()- This API adds fils data to auth frame. + * Following will be added in this. + * 1. RSNIE + * 2. SNonce + * 3. Session + * 4. Wrapped data + * @session: PE session + * @body: pointer to auth frame where data needs to be added + * + * Return: None + */ +void lim_add_fils_data_to_auth_frame(struct pe_session *session, uint8_t *body); + +/** + * lim_is_valid_fils_auth_frame()- This API checks whether auth frame is a + * valid frame. + * @mac_ctx: mac context + * @pe_session: pe session pointer + * @rx_auth_frm_body: pointer to autherntication frame + * + * Return: true if frame is valid or fils is disable, false otherwise + */ +bool lim_is_valid_fils_auth_frame(struct mac_context *mac_ctx, + struct pe_session *pe_session, tSirMacAuthFrameBody *rx_auth_frm_body); + +/** + * lim_create_fils_rik()- This API create rik using rrk coming from + * supplicant. + * @rrk: input rrk + * @rrk_len: rrk length + * @rik: Created rik + * @rik_len: rik length to be filled + * + * rIK = KDF (K, S), where + * K = rRK and + * S = rIK Label + "\0" + cryptosuite + length + * The rIK Label is the 8-bit ASCII string: + * Re-authentication Integrity Key@ietf.org + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_create_fils_rik(uint8_t *rrk, uint8_t rrk_len, + uint8_t *rik, uint32_t *rik_len); + +/** + * lim_update_fils_config()- This API updates fils session info to csr config + * from join request. + * @mac_ctx: pointer to mac context + * @session: PE session + * @sme_join_req: pointer to join request + * + * Return: None + */ +void lim_update_fils_config(struct mac_context *mac_ctx, + struct pe_session *session, + struct join_req *sme_join_req); + +/** + * lim_create_fils_auth_data()- This API creates the fils auth data + * which needs to be sent in auth req. + * @mac_ctx: mac context + * @auth_frame: pointer to auth frame + * @session: PE session + * + * Return: length of fils data + */ +QDF_STATUS lim_create_fils_auth_data(struct mac_context *mac_ctx, + tpSirMacAuthFrameBody auth_frame, + struct pe_session *session, + uint32_t *frame_len); + +/** + * lim_increase_fils_sequence_number: this API increases fils sequence number in + * the event of resending auth packet + * @session_entry: pointer to PE session + * + * Return: None + */ +static inline void lim_increase_fils_sequence_number(struct pe_session *session_entry) +{ + if (!session_entry->fils_info) + return; + + if (session_entry->fils_info->is_fils_connection) + session_entry->fils_info->sequence_number++; +} + +/** + * populate_fils_connect_params() - Populate FILS connect params to join rsp + * @mac_ctx: Mac context + * @session: PE session + * @sme_join_rsp: SME join rsp + * + * This API copies the FILS connect params from PE session to SME join rsp + * + * Return: None + */ +void populate_fils_connect_params(struct mac_context *mac_ctx, + struct pe_session *session, + struct join_rsp *sme_join_rsp); + +/** + * lim_update_fils_hlp_data() - Update the hlp data from association + * response frame to PE session. + * @hlp_frm_src_mac: SRC mac address in HLP IE from assoc frame + * @hlp_frm_dst_mac: DST mac address in HLP IE from assoc frame + * @frm_hlp_len: HLP data length + * @frm_hlp_data: Pointer to hlp data + * @pe_session: Pointer to pe_session + * + * Return: None + */ +void lim_update_fils_hlp_data(struct qdf_mac_addr *hlp_frm_src_mac, + struct qdf_mac_addr *hlp_frm_dest_mac, + uint16_t frm_hlp_len, uint8_t *frm_hlp_data, + struct pe_session *pe_session); + +/** + * aead_encrypt_assoc_req() - Encrypt FILS IE's in assoc request + * @mac_ctx: mac context + * @pe_session: PE session + * @frame: packed frame buffer + * @payload: length of @frame + * + * This API is used to encrypt the all the IE present after FILS session IE + * in Association request frame + * + * Return: QDF_STATUS + */ +QDF_STATUS aead_encrypt_assoc_req(struct mac_context *mac_ctx, + struct pe_session *pe_session, + uint8_t *frame, uint32_t *payload); + +/** + * aead_decrypt_assoc_rsp() - API for AEAD decryption in FILS connection + * @mac_ctx: MAC context + * @session: PE session + * @ar: Assoc response frame structure + * @p_frame: frame buffer received + * @n_frame: length of @p_frame + * + * This API is used to decrypt the AEAD encrypted part of FILS assoc response + * and populate the decrypted FILS IE's to Assoc response frame structure(ar) + * + * Return: QDF_STATUS + */ +QDF_STATUS aead_decrypt_assoc_rsp(struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fAssocResponse *ar, + uint8_t *p_frame, uint32_t *n_frame); +/** + * lim_is_fils_connection() - Check if it is FILS connection + * @pe_session: PE session + * + * This API is used to check if current PE session is FILS connection + * + * Return: True if FILS connection, false if not + */ +static inline bool lim_is_fils_connection(struct pe_session *pe_session) +{ + if (pe_session->fils_info->is_fils_connection) + return true; + return false; +} + +/** + * lim_verify_fils_params_assoc_rsp() - Verify FILS params in assoc rsp + * @mac_ctx: Mac context + * @session_entry: PE session + * @assoc_rsp: Assoc response received + * @assoc_cnf: Assoc cnf msg to be sent to MLME + * + * This API is used to match FILS params received in Assoc response + * with Assoc params received/derived at the Authentication stage + * + * Return: True, if successfully matches. False, otherwise + */ +bool lim_verify_fils_params_assoc_rsp(struct mac_context *mac_ctx, + struct pe_session *session_entry, + tpSirAssocRsp assoc_rsp, + tLimMlmAssocCnf * assoc_cnf); + +/** + * lim_update_fils_rik() - API to update FILS RIK in RSO + * @pe_session: PE Session + * @req_buffer: Pointer to RSO request + * + * This API is used to calculate(if required) RIK and fill + * the same in RSO request to fw. + * + * Return: None + */ +void lim_update_fils_rik(struct pe_session *pe_session, + struct roam_offload_scan_req *req_buffer); +#else +static inline bool lim_process_fils_auth_frame2(struct mac_context *mac_ctx, + struct pe_session *pe_session, tSirMacAuthFrameBody *rx_auth_frm_body) +{ + return false; +} + +static inline void +lim_increase_fils_sequence_number(struct pe_session *session_entry) +{ } + +static inline void +lim_add_fils_data_to_auth_frame(struct pe_session *session, uint8_t *body) +{ +} + +static inline bool lim_is_valid_fils_auth_frame(struct mac_context *mac_ctx, + struct pe_session *pe_session, tSirMacAuthFrameBody *rx_auth_frm_body) +{ + return true; +} + +static inline +void lim_update_fils_config(struct mac_context *mac_ctx, + struct pe_session *session, + struct join_req *sme_join_req) +{ } + +static inline +QDF_STATUS lim_create_fils_auth_data(struct mac_context *mac_ctx, + tpSirMacAuthFrameBody auth_frame, + struct pe_session *session, + uint32_t *frame_len); +{ + return QDF_STATUS_SUCCESS; +} + +static inline bool lim_is_fils_connection(struct pe_session *pe_session) +{ + return false; +} + +static inline void populate_fils_connect_params(struct mac_context *mac_ctx, + struct pe_session *session, + struct join_rsp *sme_join_rsp) +{ } + +static inline +void lim_update_fils_hlp_data(struct qdf_mac_addr *hlp_frm_src_mac, + struct qdf_mac_addr *hlp_frm_dest_mac, + uint16_t frm_hlp_len, uint8_t *frm_hlp_data, + struct pe_session *pe_session) +{} + +static inline QDF_STATUS aead_encrypt_assoc_req(struct mac_context *mac_ctx, + struct pe_session *pe_session, + uint8_t *frame, + uint32_t *payload) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS aead_decrypt_assoc_rsp(struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fAssocResponse *ar, + uint8_t *p_frame, uint32_t *n_frame) +{ + return QDF_STATUS_SUCCESS; +} + +static inline bool lim_verify_fils_params_assoc_rsp(struct mac_context *mac_ctx, + struct pe_session *session_entry, + tpSirAssocRsp assoc_rsp, + tLimMlmAssocCnf *assoc_cnf) + +{ + return true; +} + +static inline void lim_update_fils_rik(struct pe_session *pe_session, + struct roam_offload_scan_req *req_buffer) +{ } +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_session.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_session.h new file mode 100644 index 0000000000000000000000000000000000000000..698b686bde86604d3947a7db8cf130f84863a83a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_session.h @@ -0,0 +1,788 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__LIM_SESSION_H) +#define __LIM_SESSION_H + +/**========================================================================= + + \file lim_session.h + + \brief prototype for lim Session related APIs + + \author Sunit Bhatia + + ========================================================================*/ + +/* Master Structure: This will be part of PE Session Entry */ +typedef struct sPowersaveoffloadInfo { + uint8_t bcnmiss; +} tPowersaveoffloadInfo, tpPowersaveoffloadInfo; + +#ifdef WLAN_FEATURE_11W +struct comeback_timer_info { + struct mac_context *mac; + uint8_t vdev_id; + uint8_t retried; + tLimMlmStates lim_prev_mlm_state; /* Previous MLM State */ + tLimMlmStates lim_mlm_state; /* MLM State */ +}; +#endif /* WLAN_FEATURE_11W */ +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------- + Preprocessor definitions and constants + ------------------------------------------------------------------------*/ +/* Maximum Number of WEP KEYS */ +#define MAX_WEP_KEYS 4 + +#define SCH_PROTECTION_RESET_TIME 4000 + +/*-------------------------------------------------------------------------- + Type declarations + ------------------------------------------------------------------------*/ +typedef struct { + tSirMacBeaconInterval beaconInterval; + uint8_t fShortPreamble; + uint8_t llaCoexist; + uint8_t llbCoexist; + uint8_t llgCoexist; + uint8_t ht20Coexist; + uint8_t llnNonGFCoexist; + uint8_t fRIFSMode; + uint8_t fLsigTXOPProtectionFullSupport; + uint8_t gHTObssMode; +} tBeaconParams, *tpBeaconParams; + +typedef struct join_params { + uint16_t prot_status_code; + uint16_t pe_session_id; + tSirResultCodes result_code; +} join_params; + +struct reassoc_params { + uint16_t prot_status_code; + tSirResultCodes result_code; + struct pe_session *session; +}; + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +#define MAX_BSS_COLOR_VALUE 63 +#define TIME_BEACON_NOT_UPDATED 30000 +#define BSS_COLOR_SWITCH_COUNTDOWN 5 +#define OBSS_COLOR_COLLISION_DETECTION_STA_PERIOD_MS 120000 +#define OBSS_COLOR_COLLISION_DETECTION_AP_PERIOD_MS 120000 +#define OBSS_COLOR_COLLISION_SCAN_PERIOD_MS 200 +#define OBSS_COLOR_COLLISION_FREE_SLOT_EXPIRY_MS 50000 +struct bss_color_info { + qdf_time_t timestamp; + uint64_t seen_count; +}; +#endif + +/** + * struct obss_detection_cfg - current obss detection cfg set to firmware + * @obss_11b_ap_detect_mode: detection mode for 11b access point. + * @obss_11b_sta_detect_mode: detection mode for 11b station. + * @obss_11g_ap_detect_mode: detection mode for 11g access point. + * @obss_11a_detect_mode: detection mode for 11a access point. + * @obss_ht_legacy_detect_mode: detection mode for ht ap with legacy mode. + * @obss_ht_mixed_detect_mode: detection mode for ht ap with mixed mode. + * @obss_ht_20mhz_detect_mode: detection mode for ht ap with 20mhz mode. + */ +struct obss_detection_cfg { + uint8_t obss_11b_ap_detect_mode; + uint8_t obss_11b_sta_detect_mode; + uint8_t obss_11g_ap_detect_mode; + uint8_t obss_11a_detect_mode; + uint8_t obss_ht_legacy_detect_mode; + uint8_t obss_ht_mixed_detect_mode; + uint8_t obss_ht_20mhz_detect_mode; +}; + +#define ADAPTIVE_11R_STA_IE_LEN 0x0B +#define ADAPTIVE_11R_STA_OUI "\x00\x00\x0f\x22" +#define ADAPTIVE_11R_OUI_LEN 0x04 +#define ADAPTIVE_11R_OUI_SUBTYPE 0x00 +#define ADAPTIVE_11R_OUI_VERSION 0x01 +#define ADAPTIVE_11R_DATA_LEN 0x04 +#define ADAPTIVE_11R_OUI_DATA "\x00\x00\x00\x01" + +/** + * struct pe_session - per-vdev PE context + * @available: true if the entry is available, false if it is in use + * @peSessionId: unique ID assigned to the entry + * @vdev_id: ID of the vdev for which this entry is applicable + * @vdev: the actual vdev for which this entry is applicable + * @connected_akm: AKM of current connection + * @is_adaptive_11R_connection: flag to check if we are connecting + * @ap_ecsa_wakelock: wakelock to complete CSA operation. + * @ap_ecsa_runtime_lock: runtime lock to complete SAP CSA operation. + * to Adaptive 11R network + * @prev_auth_seq_num: Sequence number of previously received auth frame to + * detect duplicate frames. + * @prev_auth_mac_addr: mac_addr of the sta correspond to @prev_auth_seq_num + */ +struct pe_session { + /* To check session table is in use or free */ + uint8_t available; + uint16_t peSessionId; + union { + uint8_t smeSessionId; + uint8_t vdev_id; + }; + struct wlan_objmgr_vdev *vdev; + + /* In AP role: BSSID and self_mac_addr will be the same. */ + /* In STA role: they will be different */ + tSirMacAddr bssId; + tSirMacAddr self_mac_addr; + tSirMacSSid ssId; + uint8_t valid; + tLimMlmStates limMlmState; /* MLM State */ + tLimMlmStates limPrevMlmState; /* Previous MLM State */ + tLimSmeStates limSmeState; /* SME State */ + tLimSmeStates limPrevSmeState; /* Previous SME State */ + tLimSystemRole limSystemRole; + enum bss_type bssType; + tSirNwType nwType; + struct start_bss_req *pLimStartBssReq; /* handle to start bss req */ + struct join_req *lim_join_req; /* handle to sme join req */ + struct join_req *pLimReAssocReq; /* handle to sme reassoc req */ + tpLimMlmJoinReq pLimMlmJoinReq; /* handle to MLM join Req */ + void *pLimMlmReassocRetryReq; /* keep reasoc req for retry */ + void *pLimMlmReassocReq; /* handle to MLM reassoc Req */ + uint16_t channelChangeReasonCode; + uint8_t dot11mode; + uint8_t htCapability; + enum ani_akm_type connected_akm; + + /* Supported Channel Width Set: 0-20MHz 1 - 40MHz */ + uint8_t htSupportedChannelWidthSet; + /* Recommended Tx Width Set + * 0 - use 20 MHz channel (control channel) + * 1 - use channel width enabled under Supported Channel Width Set + */ + uint8_t htRecommendedTxWidthSet; + /* Identifies the 40 MHz extension channel */ + ePhyChanBondState htSecondaryChannelOffset; + enum reg_wifi_band limRFBand; + uint8_t limIbssActive; /* TO SUPPORT CONCURRENCY */ + + /* These global varibales moved to session Table to support BT-AMP : Oct 9th review */ + tAniAuthType limCurrentAuthType; + uint16_t limCurrentBssCaps; + uint8_t limCurrentBssQosCaps; + uint8_t limSentCapsChangeNtf; + uint16_t limAID; + + /* Parameters For Reassociation */ + tSirMacAddr limReAssocbssId; + uint32_t lim_reassoc_chan_freq; + /* CB paramaters required/duplicated for Reassoc since re-assoc mantains its own params in lim */ + uint8_t reAssocHtSupportedChannelWidthSet; + uint8_t reAssocHtRecommendedTxWidthSet; + ePhyChanBondState reAssocHtSecondaryChannelOffset; + tSirMacSSid limReassocSSID; + uint16_t limReassocBssCaps; + uint8_t limReassocBssQosCaps; + + /* Assoc or ReAssoc Response Data/Frame */ + void *limAssocResponseData; + + /** BSS Table parameters **/ + + uint16_t statypeForBss; /* to know session is for PEER or SELF */ + uint8_t shortSlotTimeSupported; + uint8_t dtimPeriod; + tSirMacRateSet rateSet; + tSirMacRateSet extRateSet; + tSirMacHTOperatingMode htOperMode; + qdf_freq_t curr_op_freq; + uint32_t curr_req_chan_freq; + uint8_t LimRxedBeaconCntDuringHB; + + /* Time stamp of the last beacon received from the BSS to which STA is connected. */ + uint64_t lastBeaconTimeStamp; + /* RX Beacon count for the current BSS to which STA is connected. */ + uint32_t currentBssBeaconCnt; + uint8_t bcon_dtim_period; + + uint32_t bcnLen; + uint8_t *beacon; /* Used to store last beacon / probe response before assoc. */ + + uint32_t assocReqLen; + uint8_t *assoc_req; /* Used to store association request frame */ + + uint32_t assocRspLen; + uint8_t *assocRsp; /* Used to store association response received while associating */ + tAniSirDph dph; + void **parsedAssocReq; /* Used to store parsed assoc req from various requesting station */ + uint32_t RICDataLen; /* Used to store the Ric data received in the assoc response */ + uint8_t *ricData; +#ifdef FEATURE_WLAN_ESE + uint32_t tspecLen; /* Used to store the TSPEC IEs received in the assoc response */ + uint8_t *tspecIes; +#endif + uint32_t encryptType; + + bool bTkipCntrMeasActive; /* Used to keep record of TKIP counter measures start/stop */ + + uint8_t gLimProtectionControl; /* used for 11n protection */ + + uint8_t gHTNonGFDevicesPresent; + + /* protection related config cache */ + tCfgProtection cfgProtection; + + /* Number of legacy STAs associated */ + tLimProtStaParams gLim11bParams; + + /* Number of 11A STAs associated */ + tLimProtStaParams gLim11aParams; + + /* Number of non-ht non-legacy STAs associated */ + tLimProtStaParams gLim11gParams; + + /* Number of nonGf STA associated */ + tLimProtStaParams gLimNonGfParams; + + /* Number of HT 20 STAs associated */ + tLimProtStaParams gLimHt20Params; + + /* Number of Lsig Txop not supported STAs associated */ + tLimProtStaParams gLimLsigTxopParams; + + /* Number of STAs that do not support short preamble */ + tLimNoShortParams gLimNoShortParams; + + /* Number of STAs that do not support short slot time */ + tLimNoShortSlotParams gLimNoShortSlotParams; + + /* OLBC parameters */ + tLimProtStaParams gLimOlbcParams; + + /* OLBC parameters */ + tLimProtStaParams gLimOverlap11gParams; + + tLimProtStaParams gLimOverlap11aParams; + tLimProtStaParams gLimOverlapHt20Params; + tLimProtStaParams gLimOverlapNonGfParams; + + /* cache for each overlap */ + tCacheParams protStaCache[LIM_PROT_STA_CACHE_SIZE]; + + uint8_t privacy; + tAniAuthType authType; + tSirKeyMaterial WEPKeyMaterial[MAX_WEP_KEYS]; + + tDot11fIERSN gStartBssRSNIe; + tDot11fIEWPA gStartBssWPAIe; + tSirAPWPSIEs APWPSIEs; + uint8_t apUapsdEnable; + tSirWPSPBCSession *pAPWPSPBCSession; + uint32_t DefProbeRspIeBitmap[8]; + uint32_t proxyProbeRspEn; + tDot11fProbeResponse probeRespFrame; + uint8_t ssidHidden; + bool fwdWPSPBCProbeReq; + uint8_t wps_state; + bool wps_registration; + + uint8_t limQosEnabled:1; /* 11E */ + uint8_t limWmeEnabled:1; /* WME */ + uint8_t limWsmEnabled:1; /* WSM */ + uint8_t limHcfEnabled:1; +#ifdef WLAN_FEATURE_11W + uint8_t limRmfEnabled:1; /* 11W */ +#endif + uint32_t lim11hEnable; + + int8_t maxTxPower; /* MIN (Regulatory and local power constraint) */ + enum QDF_OPMODE opmode; + int8_t txMgmtPower; + bool is11Rconnection; + bool is_adaptive_11r_connection; + +#ifdef FEATURE_WLAN_ESE + bool isESEconnection; + tEsePEContext eseContext; +#endif + bool isFastTransitionEnabled; + bool isFastRoamIniFeatureEnabled; + tSirP2PNoaAttr p2pGoPsUpdate; + uint32_t defaultAuthFailureTimeout; + + /* EDCA QoS parameters + * gLimEdcaParams - These EDCA parameters are used locally on AP or STA. + * If STA, then these are values taken from the Assoc Rsp when associating, + * or Beacons/Probe Response after association. If AP, then these are + * values originally set locally on AP. + * + * gLimEdcaParamsBC - These EDCA parameters are use by AP to broadcast + * to other STATIONs in the BSS. + * + * gLimEdcaParamsActive: These EDCA parameters are what's actively being + * used on station. Specific AC values may be downgraded depending on + * admission control for that particular AC. + */ + tSirMacEdcaParamRecord gLimEdcaParams[QCA_WLAN_AC_ALL]; /* used locally */ + tSirMacEdcaParamRecord gLimEdcaParamsBC[QCA_WLAN_AC_ALL]; /* used for broadcast */ + tSirMacEdcaParamRecord gLimEdcaParamsActive[QCA_WLAN_AC_ALL]; + + uint8_t gLimEdcaParamSetCount; + + tBeaconParams beaconParams; + uint8_t vhtCapability; + tLimOperatingModeInfo gLimOperatingMode; + uint8_t vhtCapabilityPresentInBeacon; + uint8_t ch_center_freq_seg0; + enum phy_ch_width ch_width; + uint8_t ch_center_freq_seg1; + uint8_t enableVhtpAid; + uint8_t enableVhtGid; + tLimWiderBWChannelSwitchInfo gLimWiderBWChannelSwitch; + uint8_t enableAmpduPs; + uint8_t enableHtSmps; + uint8_t htSmpsvalue; + bool send_smps_action; + uint8_t spectrumMgtEnabled; + /* *********************11H related**************************** */ + tLimSpecMgmtInfo gLimSpecMgmt; + /* CB Primary/Secondary Channel Switch Info */ + tLimChannelSwitchInfo gLimChannelSwitch; + /* *********************End 11H related**************************** */ + + /*Flag to Track Status/Indicate HBFailure on this session */ + bool LimHBFailureStatus; + uint32_t gLimPhyMode; + uint8_t txLdpcIniFeatureEnabled; + /** + * Following is the place holder for free peer index pool. + * A non-zero value indicates that peer index is available + * for assignment. + */ + uint8_t *gpLimPeerIdxpool; + uint8_t freePeerIdxHead; + uint8_t freePeerIdxTail; + uint16_t gLimNumOfCurrentSTAs; +#ifdef FEATURE_WLAN_TDLS + /* TDLS parameters to check whether TDLS + * and TDLS channel switch is allowed in the + * AP network + */ + uint32_t peerAIDBitmap[2]; + bool tdls_prohibited; + bool tdls_chan_swit_prohibited; + bool tdls_send_set_state_disable; +#endif + bool fWaitForProbeRsp; + bool fIgnoreCapsChange; + bool fDeauthReceived; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + int8_t rssi; +#endif + uint8_t max_amsdu_num; + uint8_t isCoalesingInIBSSAllowed; + + struct ht_config ht_config; + struct sir_vht_config vht_config; + /* + * Place holder for StartBssReq message + * received by SME state machine + */ + uint8_t gLimCurrentBssUapsd; + + /* Used on STA, this is a static UAPSD mask setting + * derived from SME_JOIN_REQ and SME_REASSOC_REQ. If a + * particular AC bit is set, it means the AC is both + * trigger enabled and delivery enabled. + */ + uint8_t gUapsdPerAcBitmask; + + /* Used on STA, this is a dynamic UPASD mask setting + * derived from AddTS Rsp and DelTS frame. If a + * particular AC bit is set, it means AC is trigger + * enabled. + */ + uint8_t gUapsdPerAcTriggerEnableMask; + + /* Used on STA, dynamic UPASD mask setting + * derived from AddTS Rsp and DelTs frame. If + * a particular AC bit is set, it means AC is + * delivery enabled. + */ + uint8_t gUapsdPerAcDeliveryEnableMask; + + /* Flag to skip CSA IE processing when CSA + * offload is enabled. + */ + uint8_t csaOffloadEnable; + + /* Used on STA for AC downgrade. This is a dynamic mask + * setting which keep tracks of ACs being admitted. + * If bit is set to 0: That partiular AC is not admitted + * If bit is set to 1: That particular AC is admitted + */ + uint8_t gAcAdmitMask[SIR_MAC_DIRECTION_DIRECT]; + + /* Power Save Off load Parameters */ + tPowersaveoffloadInfo pmmOffloadInfo; + /* SMPS mode */ + uint8_t smpsMode; + + uint8_t chainMask; + + /* Flag to indicate Chan Sw announcement is required */ + uint8_t dfsIncludeChanSwIe; + + /* Flag to indicate Chan Wrapper Element is required */ + uint8_t dfsIncludeChanWrapperIe; + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + uint8_t cc_switch_mode; +#endif + + bool isCiscoVendorAP; + + struct add_ie_params add_ie_params; + + uint8_t *pSchProbeRspTemplate; + /* Beginning portion of the beacon frame to be written to TFP */ + uint8_t *pSchBeaconFrameBegin; + /* Trailing portion of the beacon frame to be written to TFP */ + uint8_t *pSchBeaconFrameEnd; + /* Size of the beginning portion */ + uint16_t schBeaconOffsetBegin; + /* Size of the trailing portion */ + uint16_t schBeaconOffsetEnd; + bool isOSENConnection; + /* DSCP to UP mapping for HS 2.0 */ + struct qos_map_set QosMapSet; + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + bool bRoamSynchInProgress; +#endif + + /* Fast Transition (FT) */ + tftPEContext ftPEContext; + bool isNonRoamReassoc; +#ifdef WLAN_FEATURE_11W + qdf_mc_timer_t pmf_retry_timer; + struct comeback_timer_info pmf_retry_timer_info; +#endif /* WLAN_FEATURE_11W */ + uint8_t is_key_installed; + /* timer for resetting protection fileds at regular intervals */ + qdf_mc_timer_t protection_fields_reset_timer; + /* timer to decrement CSA/ECSA count */ + qdf_mc_timer_t ap_ecsa_timer; + qdf_wake_lock_t ap_ecsa_wakelock; + qdf_runtime_lock_t ap_ecsa_runtime_lock; + struct mac_context *mac_ctx; + /* + * variable to store state of various protection struct like + * gLimOlbcParams, gLimOverlap11gParams, gLimOverlapHt20Params etc + */ + uint16_t old_protection_state; + tSirMacAddr prev_ap_bssid; +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + /* tells if Q2Q IE, from another MDM device in AP MCC mode was recvd */ + bool sap_advertise_avoid_ch_ie; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ +#ifdef FEATURE_WLAN_ESE + uint8_t is_ese_version_ie_present; +#endif + uint8_t sap_dot11mc; + bool is_vendor_specific_vhtcaps; + uint8_t vendor_specific_vht_ie_sub_type; + bool vendor_vht_sap; + /* HS 2.0 Indication */ + tDot11fIEhs20vendor_ie hs20vendor_ie; + /* flag to indicate country code in beacon */ + uint8_t country_info_present; + uint8_t nss; + bool nss_forced_1x1; + bool add_bss_failed; + /* To hold OBSS Scan IE Parameters */ + struct obss_scanparam obss_ht40_scanparam; + uint8_t vdev_nss; + /* Supported NSS is intersection of self and peer NSS */ + bool supported_nss_1x1; + bool is_ext_caps_present; + uint16_t beacon_tx_rate; + uint8_t *access_policy_vendor_ie; + uint8_t access_policy; + bool ignore_assoc_disallowed; + bool send_p2p_conf_frame; + bool process_ho_fail; + /* Number of STAs that do not support ECSA capability */ + uint8_t lim_non_ecsa_cap_num; +#ifdef WLAN_FEATURE_11AX + bool he_capable; + tDot11fIEhe_cap he_config; + tDot11fIEhe_op he_op; + uint32_t he_sta_obsspd; + bool he_6ghz_band; +#ifdef WLAN_FEATURE_11AX_BSS_COLOR + tDot11fIEbss_color_change he_bss_color_change; + struct bss_color_info bss_color_info[MAX_BSS_COLOR_VALUE]; + uint8_t bss_color_changing; +#endif +#endif + struct deauth_retry_params deauth_retry; + bool enable_bcast_probe_rsp; + uint8_t ht_client_cnt; + bool force_24ghz_in_ht20; + bool ch_switch_in_progress; + bool he_with_wep_tkip; +#ifdef WLAN_FEATURE_FILS_SK + struct pe_fils_session *fils_info; +#endif + /* previous auth frame's sequence number */ + uint16_t prev_auth_seq_num; + tSirMacAddr prev_auth_mac_addr; + struct obss_detection_cfg obss_offload_cfg; + struct obss_detection_cfg current_obss_detection; + bool is_session_obss_offload_enabled; + bool is_obss_reset_timer_initialized; + bool sae_pmk_cached; + bool fw_roaming_started; + bool recvd_deauth_while_roaming; + bool recvd_disassoc_while_roaming; + bool deauth_disassoc_rc; + enum wmi_obss_color_collision_evt_type obss_color_collision_dec_evt; + bool is_session_obss_color_collision_det_enabled; + tSirMacEdcaParamRecord ap_mu_edca_params[QCA_WLAN_AC_ALL]; + bool mu_edca_present; + int8_t def_max_tx_pwr; + bool active_ba_64_session; + bool is_mbssid_enabled; +#ifdef WLAN_SUPPORT_TWT + uint8_t peer_twt_requestor; + uint8_t peer_twt_responder; +#endif + bool enable_session_twt_support; + uint32_t cac_duration_ms; + tSirResultCodes stop_bss_reason; + uint16_t prot_status_code; + tSirResultCodes result_code; + uint32_t dfs_regdomain; +}; + +/*------------------------------------------------------------------------- + Function declarations and documenation + ------------------------------------------------------------------------*/ + +#ifdef WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY +/** + * pe_allocate_dph_node_array_buffer() - Allocate g_dph_node_array + * memory dynamically + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_NOMEM on failure + */ +QDF_STATUS pe_allocate_dph_node_array_buffer(void); + +/** + * pe_free_dph_node_array_buffer() - Free memory allocated dynamically + * + * Return: None + */ +void pe_free_dph_node_array_buffer(void); +#else /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ +static inline QDF_STATUS pe_allocate_dph_node_array_buffer(void) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void pe_free_dph_node_array_buffer(void) +{ +} +#endif /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ + +/** + * pe_create_session() - Creates a new PE session given the BSSID + * @mac: pointer to global adapter context + * @bssid: BSSID of the new session + * @sessionId: PE session ID is returned here, if PE session is created. + * @numSta: number of stations + * @bssType: bss type of new session to do conditional memory allocation. + * @vdev_id: vdev_id + * @opmode: operating mode + * + * This function returns the session context and the session ID if the session + * corresponding to the passed BSSID is found in the PE session table. + * + * Return: ptr to the session context or NULL if session can not be created. + */ +struct pe_session *pe_create_session(struct mac_context *mac, + uint8_t *bssid, uint8_t *sessionId, + uint16_t numSta, enum bss_type bssType, + uint8_t vdev_id, enum QDF_OPMODE opmode); + +/** + * pe_find_session_by_bssid() - looks up the PE session given the BSSID. + * + * @mac: pointer to global adapter context + * @bssid: BSSID of the new session + * @sessionId: session ID is returned here, if session is created. + * + * This function returns the session context and the session ID if the session + * corresponding to the given BSSID is found in the PE session table. + * + * Return: pointer to the session context or NULL if session is not found. + */ +struct pe_session *pe_find_session_by_bssid(struct mac_context *mac, uint8_t *bssid, + uint8_t *sessionId); + +/** + * pe_find_session_by_vdev_id() - looks up the PE session given the vdev_id. + * @mac: pointer to global adapter context + * @vdev_id: vdev id the session + * + * Return: pointer to the session context or NULL if session is not found. + */ +struct pe_session *pe_find_session_by_vdev_id(struct mac_context *mac, + uint8_t vdev_id); + +/** + * pe_find_session_by_vdev_id_and_state() - Find PE session by vdev_id and + * mlm state. + * @mac: pointer to global adapter context + * @vdev_id: vdev id the session + * @vdev_id: vdev id the session + * + * During LFR2 roaming, new pe session is created before old pe session + * deleted, the 2 pe sessions have different pe session id, but same vdev id, + * can't get correct pe session by vdev id at this time. + * + * Return: pointer to the session context or NULL if session is not found. + */ +struct pe_session +*pe_find_session_by_vdev_id_and_state(struct mac_context *mac, + uint8_t vdev_id, + enum eLimMlmStates lim_state); + +/** + * pe_find_session_by_peer_sta() - looks up the PE session given the Peer + * Station Address. + * + * @mac: pointer to global adapter context + * @sa: Peer STA Address of the session + * @sessionId: session ID is returned here, if session is found. + * + * This function returns the session context and the session ID if the session + * corresponding to the given destination address is found in the PE session + * table. + * + * Return: pointer to the session context or NULL if session is not found. + */ +struct pe_session *pe_find_session_by_peer_sta(struct mac_context *mac, uint8_t *sa, + uint8_t *sessionId); + +/** + * pe_find_session_by_session_id() - looks up the PE session given the session + * ID. + * + * @mac: pointer to global adapter context + * @sessionId: session ID for which session context needs to be looked up. + * + * This function returns the session context if the session corresponding to + * the given session ID is found in the PE session table. + * + * Return: pointer to the session context or NULL if session is not found. + */ +struct pe_session *pe_find_session_by_session_id(struct mac_context *mac, + uint8_t sessionId); + +/** + * pe_delete_session() - deletes the PE session given the session ID. + * + * @mac: pointer to global adapter context + * @sessionId: session ID to delete. + * + * Return: void + */ +void pe_delete_session(struct mac_context *mac, struct pe_session *pe_session); + +/** + * pe_find_session_by_scan_id() - looks up the PE session for given scan id + * @mac_ctx: pointer to global adapter context + * @scan_id: scan id + * + * looks up the PE session for given scan id + * + * Return: pe session entry for given scan id if found else NULL + */ +struct pe_session *pe_find_session_by_scan_id(struct mac_context *mac_ctx, + uint32_t scan_id); + +uint8_t pe_get_active_session_count(struct mac_context *mac_ctx); +#ifdef WLAN_FEATURE_FILS_SK +/** + * pe_delete_fils_info: API to delete fils session info + * @session: pe session + * + * Return: void + */ +void pe_delete_fils_info(struct pe_session *session); +#endif + +/** + * lim_set_bcn_probe_filter - set the beacon/probe filter in mac context + * + * @mac_ctx: pointer to global mac context + * @session: pointer to the PE session + * @ibss_ssid: SSID of the session for IBSS sessions + * @sap_channel: Operating Channel of the session for SAP sessions + * + * Sets the beacon/probe filter in the global mac context to filter + * and drop beacon/probe frames before posting it to PE queue + * + * Return: None + */ +void lim_set_bcn_probe_filter(struct mac_context *mac_ctx, + struct pe_session *session, + tSirMacSSid *ibss_ssid, + uint8_t sap_channel); + +/** + * lim_reset_bcn_probe_filter - clear the beacon/probe filter in mac context + * + * @mac_ctx: pointer to the global mac context + * @session: pointer to the PE session whose filter is to be cleared + * + * Return: None + */ +void lim_reset_bcn_probe_filter(struct mac_context *mac_ctx, struct pe_session *session); + +/** + * lim_update_bcn_probe_filter - Update the beacon/probe filter in mac context + * + * @mac_ctx: pointer to the global mac context + * @session: pointer to the PE session whose filter is to be cleared + * + * This API is applicable only for SAP sessions to update the SAP channel + * in the filter during a channel switch + * + * Return: None + */ +void lim_update_bcn_probe_filter(struct mac_context *mac_ctx, struct pe_session *session); + +#endif /* #if !defined( __LIM_SESSION_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_trace.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..80a656ac437678c0ea0439c44074fa6deeed1ba7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_trace.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013-2016, 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + * \file lim_trace.h + + * \brief definition for trace related APIs + + * \author Sunit Bhatia + + ========================================================================*/ + +#ifndef __LIM_TRACE_H +#define __LIM_TRACE_H + +#include "lim_global.h" +#include "mac_trace.h" +#include "qdf_trace.h" + +enum { + TRACE_CODE_MLM_STATE, + TRACE_CODE_SME_STATE, + TRACE_CODE_TX_MGMT, + TRACE_CODE_RX_MGMT, + TRACE_CODE_RX_MGMT_TSF, + TRACE_CODE_TX_COMPLETE, + TRACE_CODE_TX_SME_MSG, + TRACE_CODE_RX_SME_MSG, + TRACE_CODE_TX_WMA_MSG, + TRACE_CODE_RX_WMA_MSG, + TRACE_CODE_TX_LIM_MSG, + TRACE_CODE_RX_LIM_MSG, + TRACE_CODE_RX_MGMT_DROP, + + TRACE_CODE_TIMER_ACTIVATE, + TRACE_CODE_TIMER_DEACTIVATE, + TRACE_CODE_INFO_LOG +}; + +#ifdef LIM_TRACE_RECORD + +#define LIM_TRACE_GET_SSN(data) (((data) >> 16) & 0xff) +#define LIM_TRACE_GET_SUBTYPE(data) ((data) & 0xff) +#define LIM_TRACE_GET_DEFRD_OR_DROPPED(data) ((data) & 0xc0000000) + +#define LIM_MSG_PROCESSED 0 +#define LIM_MSG_DEFERRED 1 +#define LIM_MSG_DROPPED 2 + +#define LIM_TRACE_MAKE_RXMGMT(type, ssn) \ + (((ssn) << 16) | (type)) +#define LIM_TRACE_MAKE_RXMSG(msg, action) \ + ((msg) | ((action) << 30)) + +void lim_trace_init(struct mac_context *mac); +uint8_t *lim_trace_get_mlm_state_string(uint32_t mlmState); +uint8_t *lim_trace_get_sme_state_string(uint32_t smeState); +void lim_trace_dump(void *mac, tp_qdf_trace_record pRecord, + uint16_t recIndex); +void mac_trace_msg_tx(struct mac_context *mac, uint8_t session, uint32_t data); +void mac_trace_msg_rx(struct mac_context *mac, uint8_t session, uint32_t data); + +#endif /* endof LIM_TRACE_RECORD MACRO */ + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/rrm_api.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/rrm_api.h new file mode 100644 index 0000000000000000000000000000000000000000..c874bf378684731070b45dbfcec9709453b80825 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/rrm_api.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2011-2012, 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + \file rrm_api.h + + \brief RRM APIs + + ========================================================================*/ + +/* $Header$ */ + +#ifndef __RRM_API_H__ +#define __RRM_API_H__ + +#define RRM_BCN_RPT_NO_BSS_INFO 0 +#define RRM_BCN_RPT_MIN_RPT 1 +#define RRM_CH_BUF_LEN 45 + +QDF_STATUS rrm_initialize(struct mac_context *mac); + +/** + * rrm_cleanup - cleanup RRM measurement related data for the measurement + * index + * @mac: Pointer to mac context + * @idx: Measurement index + * + * Return: None + */ +void rrm_cleanup(struct mac_context *mac, uint8_t idx); + +QDF_STATUS rrm_process_link_measurement_request(struct mac_context *mac, + uint8_t *pRxPacketInfo, + tDot11fLinkMeasurementRequest + *pLinkReq, + struct pe_session * + pe_session); + +QDF_STATUS +rrm_process_radio_measurement_request(struct mac_context *mac_ctx, + tSirMacAddr peer, + tDot11fRadioMeasurementRequest *rrm_req, + struct pe_session *session_entry); + +QDF_STATUS rrm_process_neighbor_report_response(struct mac_context *mac, + tDot11fNeighborReportResponse + *pNeighborRep, + struct pe_session * + pe_session); + +QDF_STATUS rrm_send_set_max_tx_power_req(struct mac_context *mac, + int8_t txPower, + struct pe_session *pe_session); + +int8_t rrm_get_mgmt_tx_power(struct mac_context *mac, + struct pe_session *pe_session); + +void rrm_cache_mgmt_tx_power(struct mac_context *mac, + int8_t txPower, struct pe_session *pe_session); + +tpRRMCaps rrm_get_capabilities(struct mac_context *mac, + struct pe_session *pe_session); + +void rrm_get_start_tsf(struct mac_context *mac, uint32_t *pStartTSF); + +QDF_STATUS rrm_set_max_tx_power_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ); + +QDF_STATUS +rrm_process_neighbor_report_req(struct mac_context *mac, + tpSirNeighborReportReqInd pNeighborReq); + +QDF_STATUS +rrm_process_beacon_report_xmit(struct mac_context *mac_ctx, + tpSirBeaconReportXmitInd beacon_xmit_ind); + +/** + * rrm_reject_req - Reject rrm request + * @radiomes_report: radio measurement report + * @rrm_req: Array of Measurement request IEs + * @num_report: Num of report + * @index: Measurement index + * @measurement_type: Measurement Type + * + * Reject the Radio Resource Measurement request, if one is + * already in progress + * + * Return: QDF_STATUS + */ +QDF_STATUS rrm_reject_req(tpSirMacRadioMeasureReport *radiomes_report, + tDot11fRadioMeasurementRequest *rrm_req, + uint8_t *num_report, uint8_t index, + uint8_t measurement_type); + +void lim_update_rrm_capability(struct mac_context *mac_ctx, + struct join_req *join_req); +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/rrm_global.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/rrm_global.h new file mode 100644 index 0000000000000000000000000000000000000000..9e257e762d3cde17ecdbd127513bd795f456fc13 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/rrm_global.h @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2011-2012, 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__RRMGLOBAL_H) +#define __RRMGLOBAL_H + +/**========================================================================= + + \file rrm_global.h + + \brief Definitions for SME APIs + + ========================================================================*/ + +#define MAX_MEASUREMENT_REQUEST 5 +#define MAX_NUM_CHANNELS 255 + +#define DEFAULT_RRM_IDX 0 + +typedef enum eRrmRetStatus { + eRRM_SUCCESS, + eRRM_INCAPABLE, + eRRM_REFUSED, + eRRM_FAILURE +} tRrmRetStatus; + +typedef enum eRrmMsgReqSource { + eRRM_MSG_SOURCE_LEGACY_ESE = 1, /* legacy ese */ + eRRM_MSG_SOURCE_11K = 2, /* 11k */ + eRRM_MSG_SOURCE_ESE_UPLOAD = 3, /* ese upload approach */ +} tRrmMsgReqSource; + +struct sir_channel_info { + uint8_t reg_class; + uint8_t chan_num; + uint32_t chan_freq; +}; + +typedef struct sSirBeaconReportReqInd { + uint16_t messageType; /* eWNI_SME_BEACON_REPORT_REQ_IND */ + uint16_t length; + uint8_t measurement_idx; + tSirMacAddr bssId; + uint16_t measurementDuration[SIR_ESE_MAX_MEAS_IE_REQS]; /* ms */ + uint16_t randomizationInterval; /* ms */ + struct sir_channel_info channel_info; + /* 0: wildcard */ + tSirMacAddr macaddrBssid; + /* 0:Passive, 1: Active, 2: table mode */ + uint8_t fMeasurementtype[SIR_ESE_MAX_MEAS_IE_REQS]; + tAniSSID ssId; /* May be wilcard. */ + uint16_t uDialogToken; + struct report_channel_list channel_list; /* From AP channel report. */ + tRrmMsgReqSource msgSource; +} tSirBeaconReportReqInd, *tpSirBeaconReportReqInd; + +typedef struct sSirBeaconReportXmitInd { + uint16_t messageType; /* eWNI_SME_BEACON_REPORT_RESP_XMIT_IND */ + uint16_t length; + uint8_t measurement_idx; + tSirMacAddr bssId; + uint16_t uDialogToken; + uint8_t fMeasureDone; + uint16_t duration; + uint8_t regClass; + uint8_t numBssDesc; + struct bss_description *pBssDescription[SIR_BCN_REPORT_MAX_BSS_DESC]; +} tSirBeaconReportXmitInd, *tpSirBeaconReportXmitInd; + +typedef struct sSirNeighborReportReqInd { + /* eWNI_SME_NEIGHBOR_REPORT_REQ_IND */ + uint16_t messageType; + uint16_t length; + /* For the session. */ + tSirMacAddr bssId; + /* true - dont include SSID in the request. */ + uint16_t noSSID; + /* false include the SSID. It may be null (wildcard) */ + tSirMacSSid ucSSID; +} tSirNeighborReportReqInd, *tpSirNeighborReportReqInd; + +typedef struct sSirNeighborBssDescription { + uint16_t length; + tSirMacAddr bssId; + uint8_t regClass; + uint8_t channel; + uint8_t phyType; + union sSirNeighborBssidInfo { + struct _rrmInfo { + /* see IEEE 802.11k Table 7-43a */ + uint32_t fApPreauthReachable:2; + uint32_t fSameSecurityMode:1; + uint32_t fSameAuthenticator:1; + /* see IEEE 802.11k Table 7-95d */ + uint32_t fCapSpectrumMeasurement:1; + uint32_t fCapQos:1; + uint32_t fCapApsd:1; + uint32_t fCapRadioMeasurement:1; + uint32_t fCapDelayedBlockAck:1; + uint32_t fCapImmediateBlockAck:1; + uint32_t fMobilityDomain:1; + uint32_t reserved:21; + } rrmInfo; + struct _eseInfo { + uint32_t channelBand:8; + uint32_t minRecvSigPower:8; + uint32_t apTxPower:8; + uint32_t roamHysteresis:8; + uint32_t adaptScanThres:8; + + uint32_t transitionTime:8; + uint32_t tsfOffset:16; + + uint32_t beaconInterval:16; + uint32_t reserved:16; + } eseInfo; + } bssidInfo; + + /* Optional sub IEs....ignoring for now. */ +} tSirNeighborBssDescription, *tpSirNeighborBssDescripton; + +typedef struct sSirNeighborReportInd { + uint16_t messageType; /* eWNI_SME_NEIGHBOR_REPORT_IND */ + uint16_t length; + uint8_t sessionId; + uint8_t measurement_idx; + uint16_t numNeighborReports; + tSirMacAddr bssId; /* For the session. */ + tSirNeighborBssDescription sNeighborBssDescription[1]; +} tSirNeighborReportInd, *tpSirNeighborReportInd; + +typedef struct sRRMBeaconReportRequestedIes { + uint8_t num; + uint8_t *pElementIds; +} tRRMBeaconReportRequestedIes, *tpRRMBeaconReportRequestedIes; + +/* Reporting detail defines. */ +/* Reference - IEEE Std 802.11k-2008 section 7.3.2.21.6 Table 7-29h */ +#define BEACON_REPORTING_DETAIL_NO_FF_IE 0 +#define BEACON_REPORTING_DETAIL_ALL_FF_REQ_IE 1 +#define BEACON_REPORTING_DETAIL_ALL_FF_IE 2 + +typedef struct sRRMReq { + uint8_t measurement_idx; /* Index of the measurement report in frame */ + uint8_t dialog_token; /* In action frame; */ + uint8_t token; /* Within individual request; */ + uint8_t type; + union { + struct { + uint8_t reportingDetail; + uint8_t last_beacon_report_indication; + tRRMBeaconReportRequestedIes reqIes; + } Beacon; + } request; + uint8_t sendEmptyBcnRpt; +} tRRMReq, *tpRRMReq; + +typedef struct sRRMCaps { + uint8_t LinkMeasurement:1; + uint8_t NeighborRpt:1; + uint8_t parallel:1; + uint8_t repeated:1; + uint8_t BeaconPassive:1; + uint8_t BeaconActive:1; + uint8_t BeaconTable:1; + uint8_t BeaconRepCond:1; + uint8_t FrameMeasurement:1; + uint8_t ChannelLoad:1; + uint8_t NoiseHistogram:1; + uint8_t statistics:1; + uint8_t LCIMeasurement:1; + uint8_t LCIAzimuth:1; + uint8_t TCMCapability:1; + uint8_t triggeredTCM:1; + uint8_t APChanReport:1; + uint8_t RRMMIBEnabled:1; + uint8_t operatingChanMax:3; + uint8_t nonOperatingChanMax:3; + uint8_t MeasurementPilot:3; + uint8_t MeasurementPilotEnabled:1; + uint8_t NeighborTSFOffset:1; + uint8_t RCPIMeasurement:1; + uint8_t RSNIMeasurement:1; + uint8_t BssAvgAccessDelay:1; + uint8_t BSSAvailAdmission:1; + uint8_t AntennaInformation:1; + uint8_t fine_time_meas_rpt:1; + uint8_t lci_capability:1; + uint8_t reserved:4; +} tRRMCaps, *tpRRMCaps; + +typedef struct sRrmPEContext { + uint8_t rrmEnable; + /* + * Used during scan/measurement to store the start TSF. + * this is not used directly in beacon reports. + */ + uint32_t startTSF[2]; + /* + * This value is stored into bssdescription and beacon report + * gets it from bss decsription. + */ + tRRMCaps rrmEnabledCaps; + int8_t txMgmtPower; + /* Dialog token for the request initiated from station. */ + uint8_t DialogToken; + uint16_t prev_rrm_report_seq_num; + uint8_t num_active_request; + tpRRMReq pCurrentReq[MAX_MEASUREMENT_REQUEST]; + uint32_t beacon_rpt_chan_list[MAX_NUM_CHANNELS]; + uint8_t beacon_rpt_chan_num; +} tRrmPEContext, *tpRrmPEContext; + +/* 2008 11k spec reference: 18.4.8.5 RCPI Measurement */ +#define RCPI_LOW_RSSI_VALUE (-110) +#define RCPI_MAX_VALUE (220) +#define CALCULATE_RCPI(rssi) (((rssi) + 110) * 2) + +/* Bit mask are defined as per Draft P802.11REVmc_D4.2 */ + +/** + * enum mask_rm_capability_byte1 - mask for supported capability + * @RM_CAP_LINK_MEASUREMENT: Link Measurement capability + * @RM_CAP_NEIGHBOR_REPORT: Neighbor report capability + * @RM_CAP_PARALLEL_MEASUREMENT: Parallel Measurement capability + * @RM_CAP_REPEATED_MEASUREMENT: Repeated Measurement capability + * @RM_CAP_BCN_PASSIVE_MEASUREMENT: Beacon passive measurement capability + * @RM_CAP_BCN_ACTIVE_MEASUREMENT: Beacon active measurement capability + * @RM_CAP_BCN_TABLE_MEASUREMENT: Beacon table measurement capability + * @RM_CAP_BCN_MEAS_REPORTING_COND: Beacon measurement reporting conditions + */ +enum mask_rm_capability_byte1 { + RM_CAP_LINK_MEASUREMENT = (1 << (0)), + RM_CAP_NEIGHBOR_REPORT = (1 << (1)), + RM_CAP_PARALLEL_MEASUREMENT = (1 << (2)), + RM_CAP_REPEATED_MEASUREMENT = (1 << (3)), + RM_CAP_BCN_PASSIVE_MEASUREMENT = (1 << (4)), + RM_CAP_BCN_ACTIVE_MEASUREMENT = (1 << (5)), + RM_CAP_BCN_TABLE_MEASUREMENT = (1 << (6)), + RM_CAP_BCN_MEAS_REPORTING_COND = (1 << (7)), +}; + +/** + * enum mask_rm_capability_byte2 - mask for supported capability + * @RM_CAP_FRAME_MEASUREMENT: Frame Measurement capability + * @RM_CAP_CHAN_LOAD_MEASUREMENT: Channel load measurement capability + * @RM_CAP_NOISE_HIST_MEASUREMENT: Noise Histogram Measurement capability + * @RM_CAP_STATISTICS_MEASUREMENT: Statistics Measurement capability + * @RM_CAP_LCI_MEASUREMENT: LCI measurement capability + * @RM_CAP_LCI_AZIMUTH: LCI Azimuth capability + * @RM_CAP_TX_CATEGORY_MEASUREMENT: Transmit category measurement capability + * @RM_CAP_TRIG_TX_CATEGORY_MEASUREMENT: + * Triggered Transmit category measurement capability + */ +enum mask_rm_capability_byte2 { + RM_CAP_FRAME_MEASUREMENT = (1 << (0)), + RM_CAP_CHAN_LOAD_MEASUREMENT = (1 << (1)), + RM_CAP_NOISE_HIST_MEASUREMENT = (1 << (2)), + RM_CAP_STATISTICS_MEASUREMENT = (1 << (3)), + RM_CAP_LCI_MEASUREMENT = (1 << (4)), + RM_CAP_LCI_AZIMUTH = (1 << (5)), + RM_CAP_TX_CATEGORY_MEASUREMENT = (1 << (6)), + RM_CAP_TRIG_TX_CATEGORY_MEASUREMENT = (1 << (7)), +}; + +/** + * enum mask_rm_capability_byte3 - mask for supported capability + * @RM_CAP_AP_CHAN_REPORT: AP channel report capability + * @RM_CAP_RM_MIB: RM MIB capability + * @RM_CAP_OPER_CHAN_MAX_DURATION_1: OPER_CHAN_MAX_DURATION bit1 + * @RM_CAP_OPER_CHAN_MAX_DURATION_2: OPER_CHAN_MAX_DURATION bit2 + * @RM_CAP_OPER_CHAN_MAX_DURATION_3: OPER_CHAN_MAX_DURATION bit3 + * @RM_CAP_NONOPER_CHAN_MAX_DURATION_1: NONOPER_CHAN_MAX bit1 + * @RM_CAP_NONOPER_CHAN_MAX_DURATION_2: NONOPER_CHAN_MAX bit2 + * @RM_CAP_NONOPER_CHAN_MAX_DURATION_3: NONOPER_CHAN_MAX bit3 + * @RM_CAP_OPER_CHAN_MAX_DURATION: Operating Channel Max Measurement Duration + * @RM_CAP_NONOPER_CHAN_MAX_DURATION: + * Nonoperating Channel Max Measurement Duration + */ + +enum mask_rm_capability_byte3 { + RM_CAP_AP_CHAN_REPORT = (1 << (0)), + RM_CAP_RM_MIB = (1 << (1)), + RM_CAP_OPER_CHAN_MAX_DURATION_1 = (1 << (2)), + RM_CAP_OPER_CHAN_MAX_DURATION_2 = (1 << (3)), + RM_CAP_OPER_CHAN_MAX_DURATION_3 = (1 << (4)), + RM_CAP_NONOPER_CHAN_MAX_DURATION_1 = (1 << (5)), + RM_CAP_NONOPER_CHAN_MAX_DURATION_2 = (1 << (6)), + RM_CAP_NONOPER_CHAN_MAX_DURATION_3 = (1 << (7)), + RM_CAP_OPER_CHAN_MAX_DURATION = (RM_CAP_OPER_CHAN_MAX_DURATION_1 | + RM_CAP_OPER_CHAN_MAX_DURATION_2 | + RM_CAP_OPER_CHAN_MAX_DURATION_3), + RM_CAP_NONOPER_CHAN_MAX_DURATION = + (RM_CAP_NONOPER_CHAN_MAX_DURATION_1 | + RM_CAP_NONOPER_CHAN_MAX_DURATION_2 | + RM_CAP_NONOPER_CHAN_MAX_DURATION_3), +}; + +/** + * enum mask_rm_capability_byte4 - mask for supported capability + * @RM_CAP_MEASUREMENT_PILOT_1: MEASUREMENT_PILOT bit1 + * @RM_CAP_MEASUREMENT_PILOT_2: MEASUREMENT_PILOT bit2 + * @RM_CAP_MEASUREMENT_PILOT_3: MEASUREMENT_PILOT bit3 + * @RM_CAP_MEAS_PILOT_TX_INFO: Measurement Pilot Transmission Capability + * @RM_CAP_NEIGHBOR_RPT_TSF_OFFSET: Neighbor Report TSF Offset Capability + * @RM_CAP_RCPI_MEASUREMENT: RCPI Measurement Capability + * @RM_CAP_RSNI_MEASUREMENT: RSNI Measurement Capability + * @RM_CAP_BSS_AVG_ACCESS_DELAY: BSS Average Access Delay Capability + * @RM_CAP_MEASUREMENT_PILOT: Measurement pilot capability + */ + +enum mask_rm_capability_byte4 { + RM_CAP_MEASUREMENT_PILOT_1 = (1 << (0)), + RM_CAP_MEASUREMENT_PILOT_2 = (1 << (1)), + RM_CAP_MEASUREMENT_PILOT_3 = (1 << (2)), + RM_CAP_MEAS_PILOT_TX_INFO = (1 << (3)), + RM_CAP_NEIGHBOR_RPT_TSF_OFFSET = (1 << (4)), + RM_CAP_RCPI_MEASUREMENT1 = (1 << (5)), + RM_CAP_RSNI_MEASUREMENT = (1 << (6)), + RM_CAP_BSS_AVG_ACCESS_DELAY = (1 << (7)), + RM_CAP_MEASUREMENT_PILOT = (RM_CAP_MEASUREMENT_PILOT_1 | + RM_CAP_MEASUREMENT_PILOT_2 | + RM_CAP_MEASUREMENT_PILOT_3), +}; + +/** + * enum mask_rm_capability_byte5 - mask for supported capability + * @RM_CAP_BSS_AVAIL_ADMISSION: BSS Available Admission Capacity Capability + * @RM_CAP_ANTENNA: Antenna Capability + * @RM_CAP_FTM_RANGE_REPORT: FTM Range Report Capability + * @RM_CAP_CIVIC_LOC_MEASUREMENT: Civic Location Measurement capability + * + * 4 bits are reserved + */ +enum mask_rm_capability_byte5 { + RM_CAP_BSS_AVAIL_ADMISSION = (1 << (0)), + RM_CAP_ANTENNA = (1 << (1)), + RM_CAP_FTM_RANGE_REPORT = (1 << (2)), + RM_CAP_CIVIC_LOC_MEASUREMENT = (1 << (3)), +}; + +#endif /* #if defined __RRMGLOBAL_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/sch_api.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/sch_api.h new file mode 100644 index 0000000000000000000000000000000000000000..decd90d9aad1f560bfc74ecd996ab572eb08be28 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/sch_api.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2011-2015, 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * Author: Sandesh Goel + * Date: 02/25/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#ifndef __SCH_API_H__ +#define __SCH_API_H__ + +#include "sir_common.h" +#include "sir_mac_prot_def.h" + +#include "ani_global.h" + +/* update only the broadcast qos params */ +void sch_qos_update_broadcast(struct mac_context *mac, + struct pe_session *pe_session); + +/* fill in the default local edca parameter into gLimEdcaParams[] */ +void sch_set_default_edca_params(struct mac_context *mac, struct pe_session *pe_session); + +/* update only local qos params */ +void sch_qos_update_local(struct mac_context *mac, struct pe_session *pe_session); + +/* update the edca profile parameters */ +void sch_edca_profile_update(struct mac_context *mac, + struct pe_session *pe_session); + +/* / Set the fixed fields in a beacon frame */ +QDF_STATUS sch_set_fixed_beacon_fields(struct mac_context *mac, + struct pe_session *pe_session); + +/** + * sch_process_pre_beacon_ind() - Process the PreBeacon Indication from the Lim + * @mac: pointer to mac structure + * @msg: schedular msg + * @reason: beaon update reason + * + * return: success: QDF_STATUS_SUCCESS failure: QDF_STATUS_E_FAILURE + */ +QDF_STATUS sch_process_pre_beacon_ind(struct mac_context *mac, + struct scheduler_msg *msg, + enum sir_bcn_update_reason reason); + +void sch_beacon_process(struct mac_context *mac, uint8_t *pRxPacketInfo, + struct pe_session *pe_session); + +QDF_STATUS sch_beacon_edca_process(struct mac_context *mac, + tSirMacEdcaParamSetIE *edca, + struct pe_session *pe_session); + +void sch_generate_tim(struct mac_context *, uint8_t **, uint16_t *, uint8_t); + +void sch_set_beacon_interval(struct mac_context *mac, + struct pe_session *pe_session); + +/** + * sch_send_beacon_req() - send beacon update req to wma + * @mac_ctx: pointer to mac structure + * @bcn_payload: beacon payload + * @size: beacon size + * @session:pe session + * @reason: beaon update reason + * + * return: success: QDF_STATUS_SUCCESS failure: QDF_STATUS_E_FAILURE + */ +QDF_STATUS sch_send_beacon_req(struct mac_context *mac_ctx, uint8_t *bcn_payload, + uint16_t size, struct pe_session *session, + enum sir_bcn_update_reason reason); + + +QDF_STATUS lim_update_probe_rsp_template_ie_bitmap_beacon1(struct mac_context *, + tDot11fBeacon1 *, + struct pe_session * + pe_session); +void lim_update_probe_rsp_template_ie_bitmap_beacon2(struct mac_context *, + tDot11fBeacon2 *, + uint32_t *, + tDot11fProbeResponse *); +void set_probe_rsp_ie_bitmap(uint32_t *, uint32_t); +uint32_t lim_send_probe_rsp_template_to_hal(struct mac_context *, + struct pe_session *, + uint32_t *); + +int sch_gen_timing_advert_frame(struct mac_context *mac, tSirMacAddr self_addr, + uint8_t **buf, uint32_t *timestamp_offset, + uint32_t *time_value_offset); + +/* + * sch_beacon_process_for_ap() - process the beacon frame for AP sessions + * @mac_ctx: pointer to the global mac_ctx + * @rx_pkt_info: pointer to the frame Rx Meta + * @bcn: pointer to the beacon struct + * + * Process the beacon in the context of any existing AP or BTAP + * session. This takes cares of following two scenarios: + * - session = NULL: + * e.g. beacon received from a neighboring BSS, you want to apply the + * protection settings to BTAP/InfraAP beacons + * - session is non NULL: + * e.g. beacon received is from the INFRA AP to which you are connected + * on another concurrent link. In this case also, we want to apply the + * protection settings(as advertised by Infra AP) to BTAP beacons + * + * Return: None + */ +void sch_beacon_process_for_ap(struct mac_context *mac_ctx, + uint8_t session_id, + uint8_t *rx_pkt_info, + tSchBeaconStruct *bcn); + +/** + * sch_edca_profile_update_all() - update edca profile for all sessions + * @pmac: pointer to mac structure + * + * return: None + */ +void sch_edca_profile_update_all(struct mac_context *pmac); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/sch_global.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/sch_global.h new file mode 100644 index 0000000000000000000000000000000000000000..12c39adfddb6cf8df127bc6f6d5b6a46c68c1b45 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/sch_global.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013-2014, 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * + * Author: Sandesh Goel + * Date: 02/25/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#ifndef __SCH_GLOBAL_H__ +#define __SCH_GLOBAL_H__ + +#include "sir_mac_prop_exts.h" +#include "lim_global.h" + +#include "parser_api.h" + +#define TIM_IE_SIZE 0xB + +/* ----------------------- Beacon processing ------------------------ */ + +/* / Beacon structure */ +#define tSchBeaconStruct tSirProbeRespBeacon +#define tpSchBeaconStruct struct sSirProbeRespBeacon * + +/** + * struct sch_context - SCH global context + * @beacon_interval: global beacon interval + * @beacon_changed: flag to indicate that beacon template has been updated + * @p2p_ie_offset: P2P IE offset + * @csa_count_offset: CSA Switch Count Offset to be sent to FW + * @ecsa_count_offset: ECSA Switch Count Offset to be sent to FW + */ +struct sch_context { + uint16_t beacon_interval; + uint8_t beacon_changed; + uint16_t p2p_ie_offset; + uint32_t csa_count_offset; + uint32_t ecsa_count_offset; +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/wmm_apsd.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/wmm_apsd.h new file mode 100644 index 0000000000000000000000000000000000000000..86f6017fb087503e4769406c789c3a09b0108151 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/wmm_apsd.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WMMAPSD_H__ +#define __WMMAPSD_H__ + +#include "ani_global.h" + +/* UAPSD Flag for each AC (WMM spec 2.2.1) */ +#define LIM_UAPSD_BITOFFSET_ACVO 0 +#define LIM_UAPSD_BITOFFSET_ACVI 1 +#define LIM_UAPSD_BITOFFSET_ACBK 2 +#define LIM_UAPSD_BITOFFSET_ACBE 3 + +#define LIM_UAPSD_FLAG_ACVO (1 << LIM_UAPSD_BITOFFSET_ACVO) +#define LIM_UAPSD_FLAG_ACVI (1 << LIM_UAPSD_BITOFFSET_ACVI) +#define LIM_UAPSD_FLAG_ACBK (1 << LIM_UAPSD_BITOFFSET_ACBK) +#define LIM_UAPSD_FLAG_ACBE (1 << LIM_UAPSD_BITOFFSET_ACBE) + +#define LIM_UAPSD_GET(ac, mask) (((mask) & (LIM_UAPSD_FLAG_ ## ac)) >> LIM_UAPSD_BITOFFSET_ ## ac) + +/* Definition for setting/clearing Uapsd Mask */ +#define SET_UAPSD_MASK 1 +#define CLEAR_UAPSD_MASK 0 + +#endif /* __WMMAPSD_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_admit_control.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_admit_control.c new file mode 100644 index 0000000000000000000000000000000000000000..2ab5220c03203ea13ba2dc65138cb360582b2720 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_admit_control.c @@ -0,0 +1,1016 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file contains TSPEC and STA admit control related functions + * NOTE: applies only to AP builds + * + * Author: Sandesh Goel + * Date: 02/25/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#include "sys_def.h" +#include "lim_api.h" +#include "lim_trace.h" +#include "lim_send_sme_rsp_messages.h" +#include "lim_types.h" +#include "lim_admit_control.h" + +/* total available bandwidth in bps in each phy mode + * these should be defined in hal or dph - replace these later + */ +#define LIM_TOTAL_BW_11A 54000000 +#define LIM_MIN_BW_11A 6000000 +#define LIM_TOTAL_BW_11B 11000000 +#define LIM_MIN_BW_11B 1000000 +#define LIM_TOTAL_BW_11G LIM_TOTAL_BW_11A +#define LIM_MIN_BW_11G LIM_MIN_BW_11B + +/* conversion factors */ +#define LIM_CONVERT_SIZE_BITS(numBytes) ((numBytes) * 8) +#define LIM_CONVERT_RATE_MBPS(rate) ((rate)/1000000) + +/* ------------------------------------------------------------------------------ */ +/* local protos */ + +static void lim_get_available_bw(struct mac_context *, uint32_t *, uint32_t *, uint32_t, + uint32_t); + +/** ------------------------------------------------------------- + \fn lim_calculate_svc_int + \brief TSPEC validation and servcie interval determination + \param struct mac_context * mac + \param struct mac_tspec_ie *pTspec + \param uint32_t *pSvcInt + \return QDF_STATUS - status of the comparison + -------------------------------------------------------------*/ + +static QDF_STATUS +lim_calculate_svc_int(struct mac_context *mac, + struct mac_tspec_ie *pTspec, uint32_t *pSvcInt) +{ + uint32_t msduSz, dataRate; + *pSvcInt = 0; + + /* if a service interval is already specified, we are done */ + if ((pTspec->minSvcInterval != 0) || (pTspec->maxSvcInterval != 0)) { + *pSvcInt = (pTspec->maxSvcInterval != 0) + ? pTspec->maxSvcInterval : pTspec->minSvcInterval; + return QDF_STATUS_SUCCESS; + } + + /* Masking off the fixed bits according to definition of MSDU size + * in IEEE 802.11-2007 spec (section 7.3.2.30). Nominal MSDU size + * is defined as: Bit[0:14]=Size, Bit[15]=Fixed + */ + if (pTspec->nomMsduSz != 0) + msduSz = (pTspec->nomMsduSz & 0x7fff); + else if (pTspec->maxMsduSz != 0) + msduSz = pTspec->maxMsduSz; + else { + pe_err("MsduSize not specified"); + return QDF_STATUS_E_FAILURE; + } + + /* need to calculate a reasonable service interval + * this is simply the msduSz/meanDataRate + */ + if (pTspec->meanDataRate != 0) + dataRate = pTspec->meanDataRate; + else if (pTspec->peakDataRate != 0) + dataRate = pTspec->peakDataRate; + else if (pTspec->minDataRate != 0) + dataRate = pTspec->minDataRate; + else { + pe_err("DataRate not specified"); + return QDF_STATUS_E_FAILURE; + } + + *pSvcInt = + LIM_CONVERT_SIZE_BITS(msduSz) / LIM_CONVERT_RATE_MBPS(dataRate); + return QDF_STATUS_E_FAILURE; +} + +/** + * lim_validate_tspec_edca() - Validate the parameters + * @mac_ctx: Global MAC context + * @tspec: Pointer to the TSPEC + * @session_entry: Session Entry + * + * validate the parameters in the edca tspec + * mandatory fields are derived from 11e Annex I (Table I.1) + * + * Return: Status + **/ +static QDF_STATUS +lim_validate_tspec_edca(struct mac_context *mac_ctx, + struct mac_tspec_ie *tspec, + struct pe_session *session_entry) +{ + uint32_t max_phy_rate, min_phy_rate; + uint32_t phy_mode; + QDF_STATUS retval = QDF_STATUS_SUCCESS; + + lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); + + lim_get_available_bw(mac_ctx, &max_phy_rate, &min_phy_rate, phy_mode, + 1 /* bandwidth mult factor */); + /* mandatory fields are derived from 11e Annex I (Table I.1) */ + if ((tspec->nomMsduSz == 0) || + (tspec->meanDataRate == 0) || + (tspec->surplusBw == 0) || + (tspec->minPhyRate == 0) || + (tspec->minPhyRate > max_phy_rate)) { + pe_warn("Invalid EDCA Tspec: NomMsdu: %d meanDataRate: %d surplusBw: %d min_phy_rate: %d", + tspec->nomMsduSz, tspec->meanDataRate, + tspec->surplusBw, tspec->minPhyRate); + retval = QDF_STATUS_E_FAILURE; + } + + pe_debug("return status: %d", retval); + return retval; +} + +/** ------------------------------------------------------------- + \fn lim_validate_tspec + \brief validate the offered tspec + \param struct mac_context *mac + \param struct mac_tspec_ie *pTspec + \return QDF_STATUS - status + -------------------------------------------------------------*/ + +static QDF_STATUS +lim_validate_tspec(struct mac_context *mac, + struct mac_tspec_ie *pTspec, struct pe_session *pe_session) +{ + QDF_STATUS retval = QDF_STATUS_SUCCESS; + + switch (pTspec->tsinfo.traffic.accessPolicy) { + case SIR_MAC_ACCESSPOLICY_EDCA: + retval = lim_validate_tspec_edca(mac, pTspec, pe_session); + if (retval != QDF_STATUS_SUCCESS) + pe_warn("EDCA tspec invalid"); + break; + + case SIR_MAC_ACCESSPOLICY_HCCA: + case SIR_MAC_ACCESSPOLICY_BOTH: + /* TBD: should we support hybrid tspec as well?? for now, just fall through */ + default: + pe_warn("AccessType: %d not supported", + pTspec->tsinfo.traffic.accessPolicy); + retval = QDF_STATUS_E_FAILURE; + break; + } + return retval; +} + +/* ----------------------------------------------------------------------------- */ +/* Admit Control Policy */ + +/** ------------------------------------------------------------- + \fn lim_compute_mean_bw_used + \brief determime the used/allocated bandwidth + \param struct mac_context *mac + \param uint32_t *pBw + \param uint32_t phyMode + \param tpLimTspecInfo pTspecInfo + \return void + -------------------------------------------------------------*/ + +static void +lim_compute_mean_bw_used(struct mac_context *mac, + uint32_t *pBw, + uint32_t phyMode, + tpLimTspecInfo pTspecInfo, struct pe_session *pe_session) +{ + uint32_t ctspec; + *pBw = 0; + for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecInfo++) { + if (pTspecInfo->inuse) { + tpDphHashNode pSta = + dph_get_hash_entry(mac, pTspecInfo->assocId, + &pe_session->dph.dphHashTable); + if (!pSta) { + /* maybe we should delete the tspec?? */ + pe_err("Tspec: %d assocId: %d dphNode not found", + ctspec, pTspecInfo->assocId); + continue; + } + *pBw += pTspecInfo->tspec.meanDataRate; + } + } +} + +/** ------------------------------------------------------------- + \fn lim_get_available_bw + \brief based on the phy mode and the bw_factor, determine the total bandwidth that + can be supported + \param struct mac_context *mac + \param uint32_t *pMaxBw + \param uint32_t *pMinBw + \param uint32_t phyMode + \param uint32_t bw_factor + \return void + -------------------------------------------------------------*/ + +static void +lim_get_available_bw(struct mac_context *mac, + uint32_t *pMaxBw, + uint32_t *pMinBw, uint32_t phyMode, uint32_t bw_factor) +{ + switch (phyMode) { + case WNI_CFG_PHY_MODE_11B: + *pMaxBw = LIM_TOTAL_BW_11B; + *pMinBw = LIM_MIN_BW_11B; + break; + + case WNI_CFG_PHY_MODE_11A: + *pMaxBw = LIM_TOTAL_BW_11A; + *pMinBw = LIM_MIN_BW_11A; + break; + + case WNI_CFG_PHY_MODE_11G: + case WNI_CFG_PHY_MODE_NONE: + default: + *pMaxBw = LIM_TOTAL_BW_11G; + *pMinBw = LIM_MIN_BW_11G; + break; + } + *pMaxBw *= bw_factor; +} + +/** + * lim_admit_policy_oversubscription() - Admission control policy + * @mac_ctx: Global MAC Context + * @tspec: Pointer to the tspec + * @admit_policy: Admission policy + * @tspec_info: TSPEC information + * @session_entry: Session Entry + * + * simple admission control policy based on oversubscription + * if the total bandwidth of all admitted tspec's exceeds (factor * phy-bw) then + * reject the tspec, else admit it. The phy-bw is the peak available bw in the + * current phy mode. The 'factor' is the configured oversubscription factor. + * + * Return: Status + **/ +static QDF_STATUS +lim_admit_policy_oversubscription(struct mac_context *mac_ctx, + struct mac_tspec_ie *tspec, + tpLimAdmitPolicyInfo admit_policy, + tpLimTspecInfo tspec_info, + struct pe_session *session_entry) +{ + uint32_t totalbw, minbw, usedbw; + uint32_t phy_mode; + + /* determine total bandwidth used so far */ + lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); + + lim_compute_mean_bw_used(mac_ctx, &usedbw, phy_mode, + tspec_info, session_entry); + + /* determine how much bw is available based on the current phy mode */ + lim_get_available_bw(mac_ctx, &totalbw, &minbw, phy_mode, + admit_policy->bw_factor); + + if (usedbw > totalbw) /* this can't possibly happen */ + return QDF_STATUS_E_FAILURE; + + if ((totalbw - usedbw) < tspec->meanDataRate) { + pe_warn("Total BW: %d Used: %d Tspec request: %d not possible", + totalbw, usedbw, tspec->meanDataRate); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn lim_admit_policy + \brief determine the current admit control policy and apply it for the offered tspec + \param struct mac_context *mac + \param struct mac_tspec_ie *pTspec + \return QDF_STATUS - status + -------------------------------------------------------------*/ + +static QDF_STATUS lim_admit_policy(struct mac_context *mac, + struct mac_tspec_ie *pTspec, + struct pe_session *pe_session) +{ + QDF_STATUS retval = QDF_STATUS_E_FAILURE; + tpLimAdmitPolicyInfo pAdmitPolicy = &mac->lim.admitPolicyInfo; + + switch (pAdmitPolicy->type) { + case WNI_CFG_ADMIT_POLICY_ADMIT_ALL: + retval = QDF_STATUS_SUCCESS; + break; + + case WNI_CFG_ADMIT_POLICY_BW_FACTOR: + retval = lim_admit_policy_oversubscription(mac, pTspec, + &mac->lim. + admitPolicyInfo, + &mac->lim.tspecInfo[0], + pe_session); + if (retval != QDF_STATUS_SUCCESS) + pe_err("rejected by BWFactor policy"); + break; + + case WNI_CFG_ADMIT_POLICY_REJECT_ALL: + retval = QDF_STATUS_E_FAILURE; + break; + + default: + retval = QDF_STATUS_SUCCESS; + pe_warn("Admit Policy: %d unknown, admitting all traffic", + pAdmitPolicy->type); + break; + } + return retval; +} + +/** ------------------------------------------------------------- + \fn lim_tspec_delete + \brief delete the specified tspec + \param struct mac_context *mac + \param tpLimTspecInfo pInfo + \return void + -------------------------------------------------------------*/ + +/* ----------------------------------------------------------------------------- */ +/* delete the specified tspec */ +static void lim_tspec_delete(struct mac_context *mac, tpLimTspecInfo pInfo) +{ + if (!pInfo) + return; + /* pierre */ + pe_debug("tspec entry: %d delete tspec: %pK", pInfo->idx, pInfo); + pInfo->inuse = 0; + + return; +} + +/** ------------------------------------------------------------- + \fn lim_tspec_find_by_sta_addr + \brief Send halMsg_AddTs to HAL + \param struct mac_context *mac + \param \param uint8_t *pAddr + \param struct mac_tspec_ie *pTspecIE + \param tpLimTspecInfo pTspecList + \param tpLimTspecInfo *ppInfo + \return QDF_STATUS - status + -------------------------------------------------------------*/ + +/* find the specified tspec in the list */ +static QDF_STATUS +lim_tspec_find_by_sta_addr(struct mac_context *mac, + uint8_t *pAddr, + struct mac_tspec_ie *pTspecIE, + tpLimTspecInfo pTspecList, tpLimTspecInfo *ppInfo) +{ + int ctspec; + + *ppInfo = NULL; + + for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecList++) { + if ((pTspecList->inuse) && + (!qdf_mem_cmp(pAddr, pTspecList->staAddr, + sizeof(pTspecList->staAddr))) && + (!qdf_mem_cmp(pTspecIE, &pTspecList->tspec, + sizeof(*pTspecIE)))) { + *ppInfo = pTspecList; + return QDF_STATUS_SUCCESS; + } + } + return QDF_STATUS_E_FAILURE; +} + +/** ------------------------------------------------------------- + \fn lim_tspec_find_by_assoc_id + \brief find tspec with matchin staid and Tspec + \param struct mac_context *mac + \param uint32_t staid + \param struct mac_tspec_ie *pTspecIE + \param tpLimTspecInfo pTspecList + \param tpLimTspecInfo *ppInfo + \return QDF_STATUS - status + -------------------------------------------------------------*/ + +QDF_STATUS +lim_tspec_find_by_assoc_id(struct mac_context *mac, + uint16_t assocId, + struct mac_tspec_ie *pTspecIE, + tpLimTspecInfo pTspecList, tpLimTspecInfo *ppInfo) +{ + int ctspec; + + *ppInfo = NULL; + + pe_debug("Trying to find tspec entry for assocId: %d pTsInfo->traffic.direction: %d pTsInfo->traffic.tsid: %d", + assocId, pTspecIE->tsinfo.traffic.direction, + pTspecIE->tsinfo.traffic.tsid); + + for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecList++) { + if ((pTspecList->inuse) && + (assocId == pTspecList->assocId) && + (!qdf_mem_cmp(pTspecIE, &pTspecList->tspec, + sizeof(*pTspecIE)))) { + *ppInfo = pTspecList; + return QDF_STATUS_SUCCESS; + } + } + return QDF_STATUS_E_FAILURE; +} + +/** ------------------------------------------------------------- + \fn lim_find_tspec + \brief finding a TSPEC entry with assocId, tsinfo.direction and tsinfo.tsid + \param uint16_t assocId + \param struct mac_context * mac + \param struct mac_ts_info *pTsInfo + \param tpLimTspecInfo pTspecList + \param tpLimTspecInfo *ppInfo + \return QDF_STATUS - status of the comparison + -------------------------------------------------------------*/ + +static QDF_STATUS +lim_find_tspec(struct mac_context *mac, + uint16_t assocId, + struct mac_ts_info *pTsInfo, + tpLimTspecInfo pTspecList, tpLimTspecInfo *ppInfo) +{ + int ctspec; + + *ppInfo = NULL; + + pe_debug("Trying to find tspec entry for assocId: %d pTsInfo->traffic.direction: %d pTsInfo->traffic.tsid: %d", + assocId, pTsInfo->traffic.direction, pTsInfo->traffic.tsid); + + for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecList++) { + if ((pTspecList->inuse) + && (assocId == pTspecList->assocId) + && (pTsInfo->traffic.direction == + pTspecList->tspec.tsinfo.traffic.direction) + && (pTsInfo->traffic.tsid == + pTspecList->tspec.tsinfo.traffic.tsid)) { + *ppInfo = pTspecList; + return QDF_STATUS_SUCCESS; + } + } + return QDF_STATUS_E_FAILURE; +} + +/** ------------------------------------------------------------- + \fn lim_tspec_add + \brief add or update the specified tspec to the tspec list + \param struct mac_context * mac + \param uint8_t *pAddr + \param uint16_t assocId + \param struct mac_tspec_ie *pTspec + \param uint32_t interval + \param tpLimTspecInfo *ppInfo + + \return QDF_STATUS - status of the comparison + -------------------------------------------------------------*/ + +QDF_STATUS lim_tspec_add(struct mac_context *mac, + uint8_t *pAddr, + uint16_t assocId, + struct mac_tspec_ie *pTspec, + uint32_t interval, tpLimTspecInfo *ppInfo) +{ + tpLimTspecInfo pTspecList = &mac->lim.tspecInfo[0]; + *ppInfo = NULL; + + /* validate the assocId */ + if (assocId >= mac->lim.maxStation) { + pe_err("Invalid assocId 0x%x", assocId); + return QDF_STATUS_E_FAILURE; + } + /* decide whether to add/update */ + { + *ppInfo = NULL; + + if (QDF_STATUS_SUCCESS == + lim_find_tspec(mac, assocId, &pTspec->tsinfo, pTspecList, + ppInfo)) { + /* update this entry. */ + pe_debug("updating TSPEC table entry: %d", + (*ppInfo)->idx); + } else { + /* We didn't find one to update. So find a free slot in the + * LIM TSPEC list and add this new entry + */ + uint8_t ctspec = 0; + + for (ctspec = 0, pTspecList = &mac->lim.tspecInfo[0]; + ctspec < LIM_NUM_TSPEC_MAX; + ctspec++, pTspecList++) { + if (!pTspecList->inuse) { + pe_debug("Found free slot in TSPEC list. Add to TSPEC table entry: %d", + ctspec); + break; + } + } + + if (ctspec >= LIM_NUM_TSPEC_MAX) + return QDF_STATUS_E_FAILURE; + + /* Record the new index entry */ + pTspecList->idx = ctspec; + } + } + + /* update the tspec info */ + pTspecList->tspec = *pTspec; + pTspecList->assocId = assocId; + qdf_mem_copy(pTspecList->staAddr, pAddr, sizeof(pTspecList->staAddr)); + + /* for edca tspec's, we are all done */ + if (pTspec->tsinfo.traffic.accessPolicy == SIR_MAC_ACCESSPOLICY_EDCA) { + pTspecList->inuse = 1; + *ppInfo = pTspecList; + pe_debug("added entry for EDCA AccessPolicy"); + return QDF_STATUS_SUCCESS; + } + + /* + * for hcca tspec's, must set the parameterized bit in the queues + * the 'ts' bit in the queue data structure indicates that the queue is + * parameterized (hcca). When the schedule is written this bit is used + * in the tsid field (bit 3) and the other three bits (0-2) are simply + * filled in as the user priority (or qid). This applies only to uplink + * polls where the qos control field must contain the tsid specified in the + * tspec. + */ + pTspecList->inuse = 1; + *ppInfo = pTspecList; + pe_debug("added entry for HCCA AccessPolicy"); + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn lim_validate_access_policy + \brief Validates Access policy + \param struct mac_context *mac + \param uint8_t accessPolicy + \param uint16_t assocId + \return QDF_STATUS - status + -------------------------------------------------------------*/ + +static QDF_STATUS +lim_validate_access_policy(struct mac_context *mac, + uint8_t accessPolicy, + uint16_t assocId, struct pe_session *pe_session) +{ + QDF_STATUS retval = QDF_STATUS_E_FAILURE; + tpDphHashNode pSta = + dph_get_hash_entry(mac, assocId, &pe_session->dph.dphHashTable); + + if ((!pSta) || (!pSta->valid)) { + pe_err("invalid station address passed"); + return QDF_STATUS_E_FAILURE; + } + + switch (accessPolicy) { + case SIR_MAC_ACCESSPOLICY_EDCA: + if (pSta->wmeEnabled || pSta->lleEnabled) + retval = QDF_STATUS_SUCCESS; + break; + + case SIR_MAC_ACCESSPOLICY_HCCA: + case SIR_MAC_ACCESSPOLICY_BOTH: + default: + pe_err("Invalid accessPolicy: %d", + accessPolicy); + break; + } + + if (retval != QDF_STATUS_SUCCESS) + pe_warn("accPol: %d lle: %d wme: %d wsm: %d sta mac " + QDF_MAC_ADDR_FMT, accessPolicy, pSta->lleEnabled, + pSta->wmeEnabled, pSta->wsmEnabled, + QDF_MAC_ADDR_REF(pSta->staAddr)); + + return retval; +} + +/** + * lim_admit_control_add_ts() - Check if STA can be admitted + * @mac: Global MAC context + * @pAddr: Address + * @pAddts: ADD TS + * @pQos: QOS fields + * @assocId: Association ID + * @alloc: Allocate bandwidth for this tspec + * @pSch: Schedule IE + * @pTspecIdx: TSPEC index + * @pe_session: PE Session Entry + * + * Determine if STA with the specified TSPEC can be admitted. If it can, + * a schedule element is provided + * + * Return: status + **/ +QDF_STATUS lim_admit_control_add_ts(struct mac_context *mac, uint8_t *pAddr, + tSirAddtsReqInfo *pAddts, tSirMacQosCapabilityStaIE *pQos, + uint16_t assocId, uint8_t alloc, tSirMacScheduleIE *pSch, + uint8_t *pTspecIdx, struct pe_session *pe_session) +{ + tpLimTspecInfo pTspecInfo; + QDF_STATUS retval; + uint32_t svcInterval; + (void)pQos; + + /* TBD: modify tspec as needed */ + /* EDCA: need to fill in the medium time and the minimum phy rate */ + /* to be consistent with the desired traffic parameters. */ + + pe_debug("tsid: %d directn: %d start: %d intvl: %d accPolicy: %d up: %d", + pAddts->tspec.tsinfo.traffic.tsid, + pAddts->tspec.tsinfo.traffic.direction, + pAddts->tspec.svcStartTime, pAddts->tspec.minSvcInterval, + pAddts->tspec.tsinfo.traffic.accessPolicy, + pAddts->tspec.tsinfo.traffic.userPrio); + + /* check for duplicate tspec */ + retval = (alloc) + ? lim_tspec_find_by_assoc_id(mac, assocId, &pAddts->tspec, + &mac->lim.tspecInfo[0], &pTspecInfo) + : lim_tspec_find_by_sta_addr(mac, pAddr, &pAddts->tspec, + &mac->lim.tspecInfo[0], &pTspecInfo); + + if (retval == QDF_STATUS_SUCCESS) { + pe_err("duplicate tspec index: %d", pTspecInfo->idx); + return QDF_STATUS_E_FAILURE; + } + /* check that the tspec's are well formed and acceptable */ + if (lim_validate_tspec(mac, &pAddts->tspec, pe_session) != + QDF_STATUS_SUCCESS) { + pe_warn("tspec validation failed"); + return QDF_STATUS_E_FAILURE; + } + /* determine a service interval for the tspec */ + if (lim_calculate_svc_int(mac, &pAddts->tspec, &svcInterval) != + QDF_STATUS_SUCCESS) { + pe_warn("SvcInt calculate failed"); + return QDF_STATUS_E_FAILURE; + } + /* determine if the tspec can be admitted or not based on current policy */ + if (lim_admit_policy(mac, &pAddts->tspec, pe_session) != QDF_STATUS_SUCCESS) { + pe_warn("tspec rejected by admit control policy"); + return QDF_STATUS_E_FAILURE; + } + /* fill in a schedule if requested */ + if (pSch) { + qdf_mem_zero((uint8_t *) pSch, sizeof(*pSch)); + pSch->svcStartTime = pAddts->tspec.svcStartTime; + pSch->svcInterval = svcInterval; + pSch->maxSvcDuration = (uint16_t) pSch->svcInterval; /* use SP = SI */ + pSch->specInterval = 0x1000; /* fixed for now: TBD */ + + pSch->info.direction = pAddts->tspec.tsinfo.traffic.direction; + pSch->info.tsid = pAddts->tspec.tsinfo.traffic.tsid; + pSch->info.aggregation = 0; /* no support for aggregation for now: TBD */ + } + /* if no allocation is requested, done */ + if (!alloc) + return QDF_STATUS_SUCCESS; + + /* check that we are in the proper mode to deal with the tspec type */ + if (lim_validate_access_policy + (mac, (uint8_t) pAddts->tspec.tsinfo.traffic.accessPolicy, assocId, + pe_session) != QDF_STATUS_SUCCESS) { + pe_warn("AccessPolicy: %d is not valid in current mode", + pAddts->tspec.tsinfo.traffic.accessPolicy); + return QDF_STATUS_E_FAILURE; + } + /* add tspec to list */ + if (lim_tspec_add + (mac, pAddr, assocId, &pAddts->tspec, svcInterval, &pTspecInfo) + != QDF_STATUS_SUCCESS) { + pe_err("no space in tspec list"); + return QDF_STATUS_E_FAILURE; + } + /* passing lim tspec table index to the caller */ + *pTspecIdx = pTspecInfo->idx; + + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn lim_admit_control_delete_ts + \brief Delete the specified Tspec for the specified STA + \param struct mac_context *mac + \param uint16_t assocId + \param struct mac_ts_info *pTsInfo + \param uint8_t *pTsStatus + \param uint8_t *ptspecIdx + \return QDF_STATUS - status + -------------------------------------------------------------*/ + +QDF_STATUS +lim_admit_control_delete_ts(struct mac_context *mac, + uint16_t assocId, + struct mac_ts_info *pTsInfo, + uint8_t *pTsStatus, uint8_t *ptspecIdx) +{ + tpLimTspecInfo pTspecInfo = NULL; + + if (pTsStatus) + *pTsStatus = 0; + + if (lim_find_tspec + (mac, assocId, pTsInfo, &mac->lim.tspecInfo[0], + &pTspecInfo) == QDF_STATUS_SUCCESS) { + if (pTspecInfo) { + pe_debug("Tspec entry: %d found", pTspecInfo->idx); + + *ptspecIdx = pTspecInfo->idx; + lim_tspec_delete(mac, pTspecInfo); + return QDF_STATUS_SUCCESS; + } + } + return QDF_STATUS_E_FAILURE; +} + +/** ------------------------------------------------------------- + \fn lim_admit_control_delete_sta + \brief Delete all TSPEC for the specified STA + \param struct mac_context *mac + \param uint16_t assocId + \return QDF_STATUS - status + -------------------------------------------------------------*/ + +QDF_STATUS lim_admit_control_delete_sta(struct mac_context *mac, uint16_t assocId) +{ + tpLimTspecInfo pTspecInfo = &mac->lim.tspecInfo[0]; + int ctspec; + + for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecInfo++) { + if (assocId == pTspecInfo->assocId) { + lim_tspec_delete(mac, pTspecInfo); + pe_debug("Deleting TSPEC: %d for assocId: %d", ctspec, + assocId); + } + } + pe_debug("assocId: %d done", assocId); + + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn lim_admit_control_init + \brief init tspec table + \param struct mac_context *mac + \return QDF_STATUS - status + -------------------------------------------------------------*/ +QDF_STATUS lim_admit_control_init(struct mac_context *mac) +{ + qdf_mem_zero(mac->lim.tspecInfo, + LIM_NUM_TSPEC_MAX * sizeof(tLimTspecInfo)); + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn lim_send_hal_msg_add_ts + \brief Send halMsg_AddTs to HAL + \param struct mac_context *mac + \param uint8_t tspecIdx + \param struct mac_tspec_ie tspecIE + \param tSirTclasInfo *tclasInfo + \param uint8_t tclasProc + \param uint16_t tsm_interval + \return QDF_STATUS - status + -------------------------------------------------------------*/ +#ifdef FEATURE_WLAN_ESE +QDF_STATUS +lim_send_hal_msg_add_ts(struct mac_context *mac, + uint8_t tspecIdx, + struct mac_tspec_ie tspecIE, + uint8_t sessionId, uint16_t tsm_interval) +#else +QDF_STATUS +lim_send_hal_msg_add_ts(struct mac_context *mac, + uint8_t tspecIdx, + struct mac_tspec_ie tspecIE, + uint8_t sessionId) +#endif +{ + struct scheduler_msg msg = {0}; + struct add_ts_param *pAddTsParam; + + struct pe_session *pe_session = pe_find_session_by_session_id(mac, sessionId); + + if (!pe_session) { + pe_err("Unable to get Session for session Id: %d", + sessionId); + return QDF_STATUS_E_FAILURE; + } + + pAddTsParam = qdf_mem_malloc(sizeof(*pAddTsParam)); + if (!pAddTsParam) + return QDF_STATUS_E_NOMEM; + + pAddTsParam->tspec_idx = tspecIdx; + qdf_mem_copy(&pAddTsParam->tspec, &tspecIE, + sizeof(struct mac_tspec_ie)); + pAddTsParam->pe_session_id = sessionId; + pAddTsParam->vdev_id = pe_session->smeSessionId; + +#ifdef FEATURE_WLAN_ESE + pAddTsParam->tsm_interval = tsm_interval; +#endif +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (mac->mlme_cfg->lfr.lfr3_roaming_offload && + pe_session->is11Rconnection) + pAddTsParam->set_ric_params = true; +#endif + + msg.type = WMA_ADD_TS_REQ; + msg.bodyptr = pAddTsParam; + msg.bodyval = 0; + + /* We need to defer any incoming messages until we get a + * WMA_ADD_TS_RSP from HAL. + */ + SET_LIM_PROCESS_DEFD_MESGS(mac, false); + MTRACE(mac_trace_msg_tx(mac, sessionId, msg.type)); + + if (QDF_STATUS_SUCCESS != wma_post_ctrl_msg(mac, &msg)) { + pe_warn("wma_post_ctrl_msg() failed"); + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + qdf_mem_free(pAddTsParam); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn lim_send_hal_msg_del_ts + \brief Send halMsg_AddTs to HAL + \param struct mac_context *mac + \param uint8_t tspecIdx + \param tSirAddtsReqInfo addts + \return QDF_STATUS - status + -------------------------------------------------------------*/ + +QDF_STATUS +lim_send_hal_msg_del_ts(struct mac_context *mac, + uint8_t tspecIdx, + struct delts_req_info delts, + uint8_t sessionId, uint8_t *bssId) +{ + struct scheduler_msg msg = {0}; + struct del_ts_params *pDelTsParam; + struct pe_session *pe_session = NULL; + + pDelTsParam = qdf_mem_malloc(sizeof(*pDelTsParam)); + if (!pDelTsParam) + return QDF_STATUS_E_NOMEM; + + msg.type = WMA_DEL_TS_REQ; + msg.bodyptr = pDelTsParam; + msg.bodyval = 0; + + /* filling message parameters. */ + pDelTsParam->tspecIdx = tspecIdx; + qdf_mem_copy(&pDelTsParam->bssId, bssId, sizeof(tSirMacAddr)); + + pe_session = pe_find_session_by_session_id(mac, sessionId); + if (!pe_session) { + pe_err("Session does Not exist with given sessionId: %d", + sessionId); + goto err; + } + pDelTsParam->sessionId = pe_session->smeSessionId; + pDelTsParam->userPrio = delts.wmeTspecPresent ? + delts.tspec.tsinfo.traffic.userPrio : + delts.tsinfo.traffic.userPrio; + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (mac->mlme_cfg->lfr.lfr3_roaming_offload && + pe_session->is11Rconnection) { + qdf_mem_copy(&pDelTsParam->delTsInfo, &delts, + sizeof(struct delts_req_info)); + pDelTsParam->setRICparams = 1; + } +#endif + MTRACE(mac_trace_msg_tx(mac, sessionId, msg.type)); + + if (QDF_STATUS_SUCCESS != wma_post_ctrl_msg(mac, &msg)) { + pe_warn("wma_post_ctrl_msg() failed"); + goto err; + } + return QDF_STATUS_SUCCESS; + +err: + qdf_mem_free(pDelTsParam); + return QDF_STATUS_E_FAILURE; +} + +/** ------------------------------------------------------------- + \fn lim_process_hal_add_ts_rsp + \brief This function process the WMA_ADD_TS_RSP from HAL. + \ If response is successful, then send back SME_ADDTS_RSP. + \ Otherwise, send DELTS action frame to peer and then + \ then send back SME_ADDTS_RSP. + \ + \param struct mac_context * mac + \param struct scheduler_msg *limMsg + -------------------------------------------------------------*/ +void lim_process_hal_add_ts_rsp(struct mac_context *mac, + struct scheduler_msg *limMsg) +{ + struct add_ts_param *pAddTsRspMsg = NULL; + tpDphHashNode pSta = NULL; + uint16_t assocId = 0; + tSirMacAddr peerMacAddr; + uint8_t rspReqd = 1; + struct pe_session *pe_session = NULL; + + /* Need to process all the deferred messages enqueued + * since sending the WMA_ADD_TS_REQ. + */ + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + + if (!limMsg->bodyptr) { + pe_err("Received WMA_ADD_TS_RSP with NULL"); + goto end; + } + + pAddTsRspMsg = limMsg->bodyptr; + + /* 090803: Use pe_find_session_by_session_id() to obtain the PE session context */ + /* from the sessionId in the Rsp Msg from HAL */ + pe_session = pe_find_session_by_session_id(mac, + pAddTsRspMsg->pe_session_id); + + if (!pe_session) { + pe_err("Session does Not exist with given sessionId: %d", + pAddTsRspMsg->pe_session_id); + lim_send_sme_addts_rsp(mac, rspReqd, eSIR_SME_ADDTS_RSP_FAILED, + pe_session, pAddTsRspMsg->tspec, + mac->lim.gLimAddtsReq.sessionId); + goto end; + } + + if (pAddTsRspMsg->status == QDF_STATUS_SUCCESS) { + pe_debug("Received successful ADDTS response from HAL"); + /* Use the smesessionId and smetransactionId from the PE session context */ + lim_send_sme_addts_rsp(mac, rspReqd, eSIR_SME_SUCCESS, + pe_session, pAddTsRspMsg->tspec, + pe_session->smeSessionId); + goto end; + } else { + pe_debug("Received failure ADDTS response from HAL"); + /* Send DELTS action frame to AP */ + /* 090803: Get peer MAC addr from session */ + sir_copy_mac_addr(peerMacAddr, pe_session->bssId); + + /* 090803: Add the SME Session ID */ + lim_send_delts_req_action_frame(mac, peerMacAddr, rspReqd, + &pAddTsRspMsg->tspec.tsinfo, + &pAddTsRspMsg->tspec, pe_session); + + /* Delete TSPEC */ + /* 090803: Pull the hash table from the session */ + pSta = dph_lookup_hash_entry(mac, peerMacAddr, &assocId, + &pe_session->dph.dphHashTable); + if (pSta) + lim_admit_control_delete_ts(mac, assocId, + &pAddTsRspMsg->tspec.tsinfo, + NULL, + (uint8_t *) &pAddTsRspMsg-> + tspec_idx); + + /* Send SME_ADDTS_RSP */ + /* 090803: Use the smesessionId and smetransactionId from the PE session context */ + lim_send_sme_addts_rsp(mac, rspReqd, eSIR_SME_ADDTS_RSP_FAILED, + pe_session, pAddTsRspMsg->tspec, + pe_session->smeSessionId); + goto end; + } + +end: + if (pAddTsRspMsg) + qdf_mem_free(pAddTsRspMsg); + return; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_aid_mgmt.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_aid_mgmt.c new file mode 100644 index 0000000000000000000000000000000000000000..e1cde3be6a570e1372994d29dbb74e4fc10b57a7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_aid_mgmt.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2011-2016, 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_aid_mgmt.c contains the functions related to + * AID pool management like initialization, assignment etc. + * Author: Chandra Modumudi + * Date: 03/20/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#include "cds_api.h" +#include "wni_cfg.h" +#include "ani_global.h" +#include "sir_params.h" +#include "lim_utils.h" +#include "lim_timer_utils.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "lim_session_utils.h" + +#define LIM_START_PEER_IDX 1 + +/** + * lim_init_peer_idxpool() -- initializes peer index pool + * @mac: mac context + * @pe_session: session entry + * + * This function is called while starting a BSS at AP + * to initialize AID pool. This may also be called while + * starting/joining an IBSS if 'Association' is allowed + * in IBSS. + * + * Return: None + */ + +void lim_init_peer_idxpool(struct mac_context *mac, struct pe_session *pe_session) +{ + uint8_t i; + uint8_t maxAssocSta = mac->lim.maxStation; + + pe_session->gpLimPeerIdxpool[0] = 0; + +#ifdef FEATURE_WLAN_TDLS + /* + * In station role, DPH_STA_HASH_INDEX_PEER (index 1) is reserved + * for peer station index corresponding to AP. Avoid choosing that index + * and get index starting from (DPH_STA_HASH_INDEX_PEER + 1) + * (index 2) for TDLS stations; + */ + if (LIM_IS_STA_ROLE(pe_session)) { + pe_session->freePeerIdxHead = DPH_STA_HASH_INDEX_PEER + 1; + } else +#endif +#ifdef QCA_IBSS_SUPPORT + if (LIM_IS_IBSS_ROLE(pe_session)) { + pe_session->freePeerIdxHead = LIM_START_PEER_IDX; + } else +#endif + { + pe_session->freePeerIdxHead = LIM_START_PEER_IDX; + } + + for (i = pe_session->freePeerIdxHead; i < maxAssocSta; i++) { + pe_session->gpLimPeerIdxpool[i] = i + 1; + } + pe_session->gpLimPeerIdxpool[i] = 0; + + pe_session->freePeerIdxTail = i; + +} + +/** + * lim_assign_peer_idx() + * + ***FUNCTION: + * This function is called to get a peer station index. This index is + * used during Association/Reassociation + * frame handling to assign association ID (aid) to a STA. + * In case of TDLS, this is used to assign a index into the Dph hash entry. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @return peerIdx - assigned peer Station IDx for STA + */ + +uint16_t lim_assign_peer_idx(struct mac_context *mac, struct pe_session *pe_session) +{ + uint16_t peerId; + + /* make sure we haven't exceeded the configurable limit on associations */ + /* This count is global to ensure that it doesn't exceed the hardware limits. */ + if (pe_get_current_stas_count(mac) >= + mac->mlme_cfg->sap_cfg.assoc_sta_limit) { + /* too many associations already active */ + return 0; + } + + /* return head of free list */ + + if (pe_session->freePeerIdxHead) { + peerId = pe_session->freePeerIdxHead; + pe_session->freePeerIdxHead = + pe_session->gpLimPeerIdxpool[pe_session-> + freePeerIdxHead]; + if (pe_session->freePeerIdxHead == 0) + pe_session->freePeerIdxTail = 0; + pe_session->gLimNumOfCurrentSTAs++; + return peerId; + } + + return 0; /* no more free peer index */ +} + +/** + * lim_release_peer_idx() + * + ***FUNCTION: + * This function is called when a STA context is removed + * at AP (or at a STA in IBSS mode or TDLS) to return peer Index + * to free pool. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param peerIdx - peer station index that need to return to free pool + * + * @return None + */ + +void +lim_release_peer_idx(struct mac_context *mac, uint16_t peerIdx, + struct pe_session *pe_session) +{ + pe_session->gLimNumOfCurrentSTAs--; + + /* insert at tail of free list */ + if (pe_session->freePeerIdxTail) { + pe_session->gpLimPeerIdxpool[pe_session-> + freePeerIdxTail] = + (uint8_t) peerIdx; + pe_session->freePeerIdxTail = (uint8_t) peerIdx; + } else { + pe_session->freePeerIdxTail = + pe_session->freePeerIdxHead = (uint8_t) peerIdx; + } + pe_session->gpLimPeerIdxpool[(uint8_t) peerIdx] = 0; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_api.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_api.c new file mode 100644 index 0000000000000000000000000000000000000000..44175b47c204249e9f7eddd36cfb301b8bc452fa --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_api.c @@ -0,0 +1,3178 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_api.cc contains the functions that are + * exported by LIM to other modules. + * + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#include "cds_api.h" +#include "wni_cfg.h" +#include "wni_api.h" +#include "sir_common.h" +#include "sir_debug.h" + +#include "sch_api.h" +#include "utils_api.h" +#include "lim_api.h" +#include "lim_global.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_prop_exts_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_ibss_peer_mgmt.h" +#include "lim_admit_control.h" +#include "lim_send_sme_rsp_messages.h" +#include "lim_security_utils.h" +#include "wmm_apsd.h" +#include "lim_trace.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "wma_types.h" +#include "wlan_crypto_global_api.h" + +#include "rrm_api.h" + +#include +#include "qdf_types.h" +#include "cds_packet.h" +#include "cds_utils.h" +#include "sys_startup.h" +#include "cds_api.h" +#include "wlan_policy_mgr_api.h" +#include "nan_datapath.h" +#include "wma.h" +#include "wlan_mgmt_txrx_utils_api.h" +#include "wlan_objmgr_psoc_obj.h" +#include "os_if_nan.h" +#include +#include +#include +#include "wlan_utility.h" +#include +#include "cfg_ucfg_api.h" +#include "wlan_mlme_public_struct.h" +#include "wlan_scan_utils_api.h" +#include +#include + +struct pe_hang_event_fixed_param { + uint16_t tlv_header; + uint8_t vdev_id; + uint8_t limmlmstate; + uint8_t limprevmlmstate; + uint8_t limsmestate; + uint8_t limprevsmestate; +} qdf_packed; + +static void __lim_init_bss_vars(struct mac_context *mac) +{ + qdf_mem_zero((void *)mac->lim.gpSession, + sizeof(*mac->lim.gpSession) * mac->lim.maxBssId); + + /* This is for testing purposes only, be default should always be off */ + mac->lim.gpLimMlmSetKeysReq = NULL; +} + +static void __lim_init_stats_vars(struct mac_context *mac) +{ + mac->lim.gLimNumBeaconsRcvd = 0; + mac->lim.gLimNumBeaconsIgnored = 0; + + mac->lim.gLimNumDeferredMsgs = 0; + + /* / Variable to keep track of number of currently associated STAs */ + mac->lim.gLimNumOfAniSTAs = 0; /* count of ANI peers */ + + qdf_mem_zero(mac->lim.gLimHeartBeatApMac[0], + sizeof(tSirMacAddr)); + qdf_mem_zero(mac->lim.gLimHeartBeatApMac[1], + sizeof(tSirMacAddr)); + mac->lim.gLimHeartBeatApMacIndex = 0; + + /* Statistics to keep track of no. beacons rcvd in heart beat interval */ + qdf_mem_zero(mac->lim.gLimHeartBeatBeaconStats, + sizeof(mac->lim.gLimHeartBeatBeaconStats)); + +#ifdef WLAN_DEBUG + /* Debug counters */ + mac->lim.numTot = 0; + mac->lim.numBbt = 0; + mac->lim.numProtErr = 0; + mac->lim.numLearn = 0; + mac->lim.numLearnIgnore = 0; + mac->lim.numSme = 0; + qdf_mem_zero(mac->lim.numMAC, sizeof(mac->lim.numMAC)); + mac->lim.gLimNumAssocReqDropInvldState = 0; + mac->lim.gLimNumAssocReqDropACRejectTS = 0; + mac->lim.gLimNumAssocReqDropACRejectSta = 0; + mac->lim.gLimNumReassocReqDropInvldState = 0; + mac->lim.gLimNumHashMissIgnored = 0; + mac->lim.gLimUnexpBcnCnt = 0; + mac->lim.gLimBcnSSIDMismatchCnt = 0; + mac->lim.gLimNumLinkEsts = 0; + mac->lim.gLimNumRxCleanup = 0; + mac->lim.gLim11bStaAssocRejectCount = 0; +#endif +} + +static void __lim_init_states(struct mac_context *mac) +{ + /* Counts Heartbeat failures */ + mac->lim.gLimHBfailureCntInLinkEstState = 0; + mac->lim.gLimProbeFailureAfterHBfailedCnt = 0; + mac->lim.gLimHBfailureCntInOtherStates = 0; + mac->lim.gLimRspReqd = 0; + mac->lim.gLimPrevSmeState = eLIM_SME_OFFLINE_STATE; + + /* / MLM State visible across all Sirius modules */ + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, NO_SESSION, eLIM_MLM_IDLE_STATE)); + mac->lim.gLimMlmState = eLIM_MLM_IDLE_STATE; + + /* / Previous MLM State */ + mac->lim.gLimPrevMlmState = eLIM_MLM_OFFLINE_STATE; + + /** + * Initialize state to eLIM_SME_OFFLINE_STATE + */ + mac->lim.gLimSmeState = eLIM_SME_OFFLINE_STATE; + + /** + * By default assume 'unknown' role. This will be updated + * when SME_START_BSS_REQ is received. + */ + + qdf_mem_zero(&mac->lim.gLimNoShortParams, sizeof(tLimNoShortParams)); + qdf_mem_zero(&mac->lim.gLimNoShortSlotParams, + sizeof(tLimNoShortSlotParams)); + + mac->lim.gLimPhyMode = 0; +} + +static void __lim_init_vars(struct mac_context *mac) +{ + /* Place holder for Measurement Req/Rsp/Ind related info */ + + + /* Deferred Queue Parameters */ + qdf_mem_zero(&mac->lim.gLimDeferredMsgQ, sizeof(tSirAddtsReq)); + + /* addts request if any - only one can be outstanding at any time */ + qdf_mem_zero(&mac->lim.gLimAddtsReq, sizeof(tSirAddtsReq)); + mac->lim.gLimAddtsSent = 0; + mac->lim.gLimAddtsRspTimerCount = 0; + + /* protection related config cache */ + qdf_mem_zero(&mac->lim.cfgProtection, sizeof(tCfgProtection)); + mac->lim.gLimProtectionControl = 0; + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + + /* WMM Related Flag */ + mac->lim.gUapsdEnable = 0; + + /* QoS-AC Downgrade: Initially, no AC is admitted */ + mac->lim.gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] = 0; + mac->lim.gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK] = 0; + + /* dialogue token List head/tail for Action frames request sent. */ + mac->lim.pDialogueTokenHead = NULL; + mac->lim.pDialogueTokenTail = NULL; + + qdf_mem_zero(&mac->lim.tspecInfo, + sizeof(tLimTspecInfo) * LIM_NUM_TSPEC_MAX); + + /* admission control policy information */ + qdf_mem_zero(&mac->lim.admitPolicyInfo, sizeof(tLimAdmitPolicyInfo)); +} + +static void __lim_init_assoc_vars(struct mac_context *mac) +{ + mac->lim.gLimIbssStaLimit = 0; + /* Place holder for current authentication request */ + /* being handled */ + mac->lim.gpLimMlmAuthReq = NULL; + + /* / MAC level Pre-authentication related globals */ + mac->lim.gLimPreAuthChannelNumber = 0; + mac->lim.gLimPreAuthType = eSIR_OPEN_SYSTEM; + qdf_mem_zero(&mac->lim.gLimPreAuthPeerAddr, sizeof(tSirMacAddr)); + mac->lim.gLimNumPreAuthContexts = 0; + qdf_mem_zero(&mac->lim.gLimPreAuthTimerTable, sizeof(tLimPreAuthTable)); + + /* Place holder for Pre-authentication node list */ + mac->lim.pLimPreAuthList = NULL; + + /* One cache for each overlap and associated case. */ + qdf_mem_zero(mac->lim.protStaOverlapCache, + sizeof(tCacheParams) * LIM_PROT_STA_OVERLAP_CACHE_SIZE); + qdf_mem_zero(mac->lim.protStaCache, + sizeof(tCacheParams) * LIM_PROT_STA_CACHE_SIZE); + + mac->lim.pe_session = NULL; + mac->lim.reAssocRetryAttempt = 0; + +} + +static void __lim_init_ht_vars(struct mac_context *mac) +{ + mac->lim.htCapabilityPresentInBeacon = 0; + mac->lim.gHTGreenfield = 0; + mac->lim.gHTShortGI40Mhz = 0; + mac->lim.gHTShortGI20Mhz = 0; + mac->lim.gHTMaxAmsduLength = 0; + mac->lim.gHTDsssCckRate40MHzSupport = 0; + mac->lim.gHTPSMPSupport = 0; + mac->lim.gHTLsigTXOPProtection = 0; + mac->lim.gHTMIMOPSState = eSIR_HT_MIMO_PS_STATIC; + mac->lim.gHTAMpduDensity = 0; + + mac->lim.gMaxAmsduSizeEnabled = false; + mac->lim.gHTMaxRxAMpduFactor = 0; + mac->lim.gHTServiceIntervalGranularity = 0; + mac->lim.gHTControlledAccessOnly = 0; + mac->lim.gHTOperMode = eSIR_HT_OP_MODE_PURE; + mac->lim.gHTPCOActive = 0; + + mac->lim.gHTPCOPhase = 0; + mac->lim.gHTSecondaryBeacon = 0; + mac->lim.gHTDualCTSProtection = 0; + mac->lim.gHTSTBCBasicMCS = 0; +} + +static QDF_STATUS __lim_init_config(struct mac_context *mac) +{ + struct mlme_ht_capabilities_info *ht_cap_info; +#ifdef FEATURE_WLAN_TDLS + QDF_STATUS status; + uint32_t val1; + bool valb; +#endif + + /* Read all the CFGs here that were updated before pe_start is called */ + /* All these CFG READS/WRITES are only allowed in init, at start when there is no session + * and they will be used throughout when there is no session + */ + mac->lim.gLimIbssStaLimit = mac->mlme_cfg->sap_cfg.assoc_sta_limit; + ht_cap_info = &mac->mlme_cfg->ht_caps.ht_cap_info; + + /* channel bonding mode could be set to anything from 0 to 4(Titan had these */ + /* modes But for Taurus we have only two modes: enable(>0) or disable(=0) */ + ht_cap_info->supported_channel_width_set = + mac->mlme_cfg->feature_flags.channel_bonding_mode ? + WNI_CFG_CHANNEL_BONDING_MODE_ENABLE : + WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + + mac->mlme_cfg->ht_caps.info_field_1.recommended_tx_width_set = + ht_cap_info->supported_channel_width_set; + + if (!mac->mlme_cfg->timeouts.heart_beat_threshold) { + mac->sys.gSysEnableLinkMonitorMode = 0; + } else { + /* No need to activate the timer during init time. */ + mac->sys.gSysEnableLinkMonitorMode = 1; + } + + /* WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA - not needed */ + + /* This was initially done after resume notification from HAL. Now, DAL is + started before PE so this can be done here */ + handle_ht_capabilityand_ht_info(mac, NULL); +#ifdef FEATURE_WLAN_TDLS + status = cfg_tdls_get_buffer_sta_enable(mac->psoc, &valb); + if (QDF_STATUS_SUCCESS != status) { + pe_err("cfg get LimTDLSBufStaEnabled failed"); + return QDF_STATUS_E_FAILURE; + } + mac->lim.gLimTDLSBufStaEnabled = (uint8_t)valb; + + status = cfg_tdls_get_uapsd_mask(mac->psoc, &val1); + if (QDF_STATUS_SUCCESS != status) { + pe_err("cfg get LimTDLSUapsdMask failed"); + return QDF_STATUS_E_FAILURE; + } + mac->lim.gLimTDLSUapsdMask = (uint8_t)val1; + + status = cfg_tdls_get_off_channel_enable(mac->psoc, &valb); + if (QDF_STATUS_SUCCESS != status) { + pe_err("cfg get LimTDLSUapsdMask failed"); + return QDF_STATUS_E_FAILURE; + } + mac->lim.gLimTDLSOffChannelEnabled = (uint8_t)valb; + + status = cfg_tdls_get_wmm_mode_enable(mac->psoc, &valb); + if (QDF_STATUS_SUCCESS != status) { + pe_err("cfg get LimTDLSWmmMode failed"); + return QDF_STATUS_E_FAILURE; + } + mac->lim.gLimTDLSWmmMode = (uint8_t)valb; +#endif + + return QDF_STATUS_SUCCESS; +} + +/* + lim_start + This function is to replace the __lim_process_sme_start_req since there is no + eWNI_SME_START_REQ post to PE. + */ +QDF_STATUS lim_start(struct mac_context *mac) +{ + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + + pe_debug("enter"); + + if (mac->lim.gLimSmeState == eLIM_SME_OFFLINE_STATE) { + mac->lim.gLimSmeState = eLIM_SME_IDLE_STATE; + + MTRACE(mac_trace + (mac, TRACE_CODE_SME_STATE, NO_SESSION, + mac->lim.gLimSmeState)); + + /* Initialize MLM state machine */ + if (QDF_STATUS_SUCCESS != lim_init_mlm(mac)) { + pe_err("Init MLM failed"); + return QDF_STATUS_E_FAILURE; + } + } else { + /** + * Should not have received eWNI_SME_START_REQ in states + * other than OFFLINE. Return response to host and + * log error + */ + pe_warn("Invalid SME state: %X", + mac->lim.gLimSmeState); + retCode = QDF_STATUS_E_FAILURE; + } + + mac->lim.req_id = + ucfg_scan_register_requester(mac->psoc, + "LIM", + lim_process_rx_scan_handler, + mac); + return retCode; +} + +/** + * lim_initialize() + * + ***FUNCTION: + * This function is called from LIM thread entry function. + * LIM related global data structures are initialized in this function. + * + ***LOGIC: + * NA + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to global MAC structure + * @return None + */ + +QDF_STATUS lim_initialize(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + mac->lim.tdls_frm_session_id = NO_SESSION; + mac->lim.deferredMsgCnt = 0; + mac->lim.retry_packet_cnt = 0; + mac->lim.ibss_retry_cnt = 0; + mac->lim.deauthMsgCnt = 0; + mac->lim.disassocMsgCnt = 0; + + __lim_init_assoc_vars(mac); + __lim_init_vars(mac); + __lim_init_states(mac); + __lim_init_stats_vars(mac); + __lim_init_bss_vars(mac); + __lim_init_ht_vars(mac); + + /* Initializations for maintaining peers in IBSS */ + lim_ibss_init(mac); + + rrm_initialize(mac); + + if (QDF_IS_STATUS_ERROR(qdf_mutex_create( + &mac->lim.lim_frame_register_lock))) { + pe_err("lim lock init failed!"); + return QDF_STATUS_E_FAILURE; + } + + qdf_list_create(&mac->lim.gLimMgmtFrameRegistratinQueue, 0); + + /* initialize the TSPEC admission control table. */ + /* Note that this was initially done after resume notification from HAL. */ + /* Now, DAL is started before PE so this can be done here */ + lim_admit_control_init(mac); + return status; + +} /*** end lim_initialize() ***/ + +/** + * lim_cleanup() + * + ***FUNCTION: + * This function is called upon reset or persona change + * to cleanup LIM state + * + ***LOGIC: + * NA + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @return None + */ + +void lim_cleanup(struct mac_context *mac) +{ + uint8_t i; + qdf_list_node_t *lst_node; + + /* + * Before destroying the list making sure all the nodes have been + * deleted + */ + while (qdf_list_remove_front( + &mac->lim.gLimMgmtFrameRegistratinQueue, + &lst_node) == QDF_STATUS_SUCCESS) { + qdf_mem_free(lst_node); + } + qdf_list_destroy(&mac->lim.gLimMgmtFrameRegistratinQueue); + qdf_mutex_destroy(&mac->lim.lim_frame_register_lock); + + pe_deregister_mgmt_rx_frm_callback(mac); + + /* free up preAuth table */ + if (mac->lim.gLimPreAuthTimerTable.pTable) { + for (i = 0; i < mac->lim.gLimPreAuthTimerTable.numEntry; i++) + qdf_mem_free(mac->lim.gLimPreAuthTimerTable.pTable[i]); + qdf_mem_free(mac->lim.gLimPreAuthTimerTable.pTable); + mac->lim.gLimPreAuthTimerTable.pTable = NULL; + mac->lim.gLimPreAuthTimerTable.numEntry = 0; + } + + if (mac->lim.pDialogueTokenHead) { + lim_delete_dialogue_token_list(mac); + } + + if (mac->lim.pDialogueTokenTail) { + qdf_mem_free(mac->lim.pDialogueTokenTail); + mac->lim.pDialogueTokenTail = NULL; + } + + if (mac->lim.gpLimMlmSetKeysReq) { + qdf_mem_zero(mac->lim.gpLimMlmSetKeysReq, + sizeof(tLimMlmSetKeysReq)); + qdf_mem_free(mac->lim.gpLimMlmSetKeysReq); + mac->lim.gpLimMlmSetKeysReq = NULL; + } + + if (mac->lim.gpLimMlmAuthReq) { + qdf_mem_free(mac->lim.gpLimMlmAuthReq); + mac->lim.gpLimMlmAuthReq = NULL; + } + + if (mac->lim.limDisassocDeauthCnfReq.pMlmDisassocReq) { + qdf_mem_free(mac->lim.limDisassocDeauthCnfReq.pMlmDisassocReq); + mac->lim.limDisassocDeauthCnfReq.pMlmDisassocReq = NULL; + } + + if (mac->lim.limDisassocDeauthCnfReq.pMlmDeauthReq) { + qdf_mem_free(mac->lim.limDisassocDeauthCnfReq.pMlmDeauthReq); + mac->lim.limDisassocDeauthCnfReq.pMlmDeauthReq = NULL; + } + + /* Now, finally reset the deferred message queue pointers */ + lim_reset_deferred_msg_q(mac); + + for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) + rrm_cleanup(mac, i); + + lim_ft_cleanup_all_ft_sessions(mac); + + ucfg_scan_unregister_requester(mac->psoc, mac->lim.req_id); +} /*** end lim_cleanup() ***/ + +#ifdef WLAN_FEATURE_MEMDUMP_ENABLE +/** + * lim_state_info_dump() - print state information of lim layer + * @buf: buffer pointer + * @size: size of buffer to be filled + * + * This function is used to print state information of lim layer + * + * Return: None + */ +static void lim_state_info_dump(char **buf_ptr, uint16_t *size) +{ + struct mac_context *mac; + uint16_t len = 0; + char *buf = *buf_ptr; + + mac = cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + QDF_ASSERT(0); + return; + } + + pe_debug("size of buffer: %d", *size); + + len += qdf_scnprintf(buf + len, *size - len, + "\n SmeState: %d", mac->lim.gLimSmeState); + len += qdf_scnprintf(buf + len, *size - len, + "\n PrevSmeState: %d", mac->lim.gLimPrevSmeState); + len += qdf_scnprintf(buf + len, *size - len, + "\n MlmState: %d", mac->lim.gLimMlmState); + len += qdf_scnprintf(buf + len, *size - len, + "\n PrevMlmState: %d", mac->lim.gLimPrevMlmState); + len += qdf_scnprintf(buf + len, *size - len, + "\n ProcessDefdMsgs: %d", mac->lim.gLimProcessDefdMsgs); + + *size -= len; + *buf_ptr += len; +} + +/** + * lim_register_debug_callback() - registration function for lim layer + * to print lim state information + * + * Return: None + */ +static void lim_register_debug_callback(void) +{ + qdf_register_debug_callback(QDF_MODULE_ID_PE, &lim_state_info_dump); +} +#else /* WLAN_FEATURE_MEMDUMP_ENABLE */ +static void lim_register_debug_callback(void) +{ +} +#endif /* WLAN_FEATURE_MEMDUMP_ENABLE */ + +#ifdef WLAN_FEATURE_NAN +static void lim_nan_register_callbacks(struct mac_context *mac_ctx) +{ + struct nan_callbacks cb_obj = {0}; + + cb_obj.add_ndi_peer = lim_add_ndi_peer_converged; + cb_obj.ndp_delete_peers = lim_ndp_delete_peers_converged; + cb_obj.delete_peers_by_addr = lim_ndp_delete_peers_by_addr_converged; + + ucfg_nan_register_lim_callbacks(mac_ctx->psoc, &cb_obj); +} +#else +static inline void lim_nan_register_callbacks(struct mac_context *mac_ctx) +{ +} +#endif + +#ifdef WLAN_FEATURE_11W +void lim_stop_pmfcomeback_timer(struct pe_session *session) +{ + if (session->opmode != QDF_STA_MODE) + return; + + qdf_mc_timer_stop(&session->pmf_retry_timer); + session->pmf_retry_timer_info.retried = false; +} +#endif + +/* + * pe_shutdown_notifier_cb - Shutdown notifier callback + * @ctx: Pointer to Global MAC structure + * + * Return: None + */ +static void pe_shutdown_notifier_cb(void *ctx) +{ + struct mac_context *mac_ctx = (struct mac_context *)ctx; + struct pe_session *session; + uint8_t i; + + lim_deactivate_timers(mac_ctx); + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + session = &mac_ctx->lim.gpSession[i]; + if (session->valid == true) { + if (LIM_IS_AP_ROLE(session)) + qdf_mc_timer_stop(&session-> + protection_fields_reset_timer); + lim_stop_pmfcomeback_timer(session); + } + } +} + +#ifdef WLAN_FEATURE_11W +/** + * is_mgmt_protected - check RMF enabled for the peer + * @vdev_id: vdev id + * @peer_mac_addr: peer mac address + * + * The function check the mgmt frame protection enabled or not + * for station mode and AP mode + * + * Return: true, if the connection is RMF enabled. + */ +static bool is_mgmt_protected(uint32_t vdev_id, + const uint8_t *peer_mac_addr) +{ + uint16_t aid; + tpDphHashNode sta_ds; + struct pe_session *session; + bool protected = false; + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac_ctx) + return false; + + session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session) { + /* couldn't find session */ + pe_err("Session not found for vdev_id: %d", vdev_id); + return false; + } + + sta_ds = dph_lookup_hash_entry(mac_ctx, (uint8_t *)peer_mac_addr, &aid, + &session->dph.dphHashTable); + if (sta_ds) { + /* rmfenabled will be set at the time of addbss. + * but sometimes EAP auth fails and keys are not + * installed then if we send any management frame + * like deauth/disassoc with this bit set then + * firmware crashes. so check for keys are + * installed or not also before setting the bit + */ + if (sta_ds->rmfEnabled && sta_ds->is_key_installed) + protected = true; + } + + return protected; +} + +#else +/** + * is_mgmt_protected - check RMF enabled for the peer + * @vdev_id: vdev id + * @peer_mac_addr: peer mac address + * + * The function check the mgmt frame protection enabled or not + * for station mode and AP mode + * + * Return: true, if the connection is RMF enabled. + */ +static bool is_mgmt_protected(uint32_t vdev_id, + const uint8_t *peer_mac_addr) +{ + return false; +} +#endif + +static void p2p_register_callbacks(struct mac_context *mac_ctx) +{ + struct p2p_protocol_callbacks p2p_cb = {0}; + + p2p_cb.is_mgmt_protected = is_mgmt_protected; + ucfg_p2p_register_callbacks(mac_ctx->psoc, &p2p_cb); +} + +/* + * lim_register_sap_bcn_callback(): Register a callback with scan module for SAP + * @mac_ctx: pointer to the global mac context + * + * Registers the function lim_handle_sap_beacon as callback with the Scan + * module to handle beacon frames for SAP sessions + * + * Return: QDF Status + */ +static QDF_STATUS lim_register_sap_bcn_callback(struct mac_context *mac_ctx) +{ + QDF_STATUS status; + + status = ucfg_scan_register_bcn_cb(mac_ctx->psoc, + lim_handle_sap_beacon, + SCAN_CB_TYPE_UPDATE_BCN); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("failed with status code %08d [x%08x]", + status, status); + } + + return status; +} + +/* + * lim_unregister_sap_bcn_callback(): Unregister the callback with scan module + * @mac_ctx: pointer to the global mac context + * + * Unregisters the callback registered with the Scan + * module to handle beacon frames for SAP sessions + * + * Return: QDF Status + */ +static QDF_STATUS lim_unregister_sap_bcn_callback(struct mac_context *mac_ctx) +{ + QDF_STATUS status; + + status = ucfg_scan_register_bcn_cb(mac_ctx->psoc, + NULL, SCAN_CB_TYPE_UPDATE_BCN); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("failed with status code %08d [x%08x]", + status, status); + } + + return status; +} + +static int pe_hang_event_notifier_call(struct notifier_block *block, + unsigned long state, + void *data) +{ + qdf_notif_block *notif_block = qdf_container_of(block, qdf_notif_block, + notif_block); + struct mac_context *mac; + struct pe_session *session; + struct qdf_notifer_data *pe_hang_data = data; + uint8_t *pe_data; + uint8_t i; + struct pe_hang_event_fixed_param *cmd; + size_t size; + + if (!data) + return NOTIFY_STOP_MASK; + + mac = notif_block->priv_data; + if (!mac) + return NOTIFY_STOP_MASK; + + size = sizeof(*cmd); + for (i = 0; i < mac->lim.maxBssId; i++) { + session = &mac->lim.gpSession[i]; + if (!session->valid) + continue; + if (pe_hang_data->offset + size > QDF_WLAN_HANG_FW_OFFSET) + return NOTIFY_STOP_MASK; + + pe_data = (pe_hang_data->hang_data + pe_hang_data->offset); + cmd = (struct pe_hang_event_fixed_param *)pe_data; + QDF_HANG_EVT_SET_HDR(&cmd->tlv_header, HANG_EVT_TAG_LEGACY_MAC, + QDF_HANG_GET_STRUCT_TLVLEN(*cmd)); + cmd->vdev_id = session->vdev_id; + cmd->limmlmstate = session->limMlmState; + cmd->limprevmlmstate = session->limPrevMlmState; + cmd->limsmestate = session->limSmeState; + cmd->limprevsmestate = session->limPrevSmeState; + pe_hang_data->offset += size; + } + + return NOTIFY_OK; +} + +static qdf_notif_block pe_hang_event_notifier = { + .notif_block.notifier_call = pe_hang_event_notifier_call, +}; + +/** ------------------------------------------------------------- + \fn pe_open + \brief will be called in Open sequence from mac_open + \param struct mac_context *mac + \param tHalOpenParameters *pHalOpenParam + \return QDF_STATUS + -------------------------------------------------------------*/ + +QDF_STATUS pe_open(struct mac_context *mac, struct cds_config_info *cds_cfg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (QDF_DRIVER_TYPE_MFG == cds_cfg->driver_type) + return QDF_STATUS_SUCCESS; + + mac->lim.maxBssId = cds_cfg->max_bssid; + mac->lim.maxStation = cds_cfg->max_station; + qdf_spinlock_create(&mac->sys.bbt_mgmt_lock); + + if ((mac->lim.maxBssId == 0) || (mac->lim.maxStation == 0)) { + pe_err("max number of Bssid or Stations cannot be zero!"); + return QDF_STATUS_E_FAILURE; + } + + if (!QDF_IS_STATUS_SUCCESS(pe_allocate_dph_node_array_buffer())) { + pe_err("g_dph_node_array memory allocate failed!"); + return QDF_STATUS_E_NOMEM; + } + + mac->lim.lim_timers.gpLimCnfWaitTimer = + qdf_mem_malloc(sizeof(TX_TIMER) * (mac->lim.maxStation + 1)); + if (!mac->lim.lim_timers.gpLimCnfWaitTimer) { + status = QDF_STATUS_E_NOMEM; + goto pe_open_timer_fail; + } + + mac->lim.gpSession = + qdf_mem_malloc(sizeof(struct pe_session) * mac->lim.maxBssId); + if (!mac->lim.gpSession) { + status = QDF_STATUS_E_NOMEM; + goto pe_open_psession_fail; + } + + status = lim_initialize(mac); + if (QDF_STATUS_SUCCESS != status) { + pe_err("lim_initialize failed!"); + status = QDF_STATUS_E_FAILURE; + goto pe_open_lock_fail; + } + + /* + * pe_open is successful by now, so it is right time to initialize + * MTRACE for PE module. if LIM_TRACE_RECORD is not defined in build + * file then nothing will be logged for PE module. + */ +#ifdef LIM_TRACE_RECORD + MTRACE(lim_trace_init(mac)); +#endif + lim_register_debug_callback(); + lim_nan_register_callbacks(mac); + p2p_register_callbacks(mac); + lim_register_sap_bcn_callback(mac); + + if (!QDF_IS_STATUS_SUCCESS( + cds_shutdown_notifier_register(pe_shutdown_notifier_cb, mac))) { + pe_err("%s: Shutdown notifier register failed", __func__); + } + + pe_hang_event_notifier.priv_data = mac; + qdf_hang_event_register_notifier(&pe_hang_event_notifier); + + return status; /* status here will be QDF_STATUS_SUCCESS */ + +pe_open_lock_fail: + qdf_mem_free(mac->lim.gpSession); + mac->lim.gpSession = NULL; +pe_open_psession_fail: + qdf_mem_free(mac->lim.lim_timers.gpLimCnfWaitTimer); + mac->lim.lim_timers.gpLimCnfWaitTimer = NULL; +pe_open_timer_fail: + pe_free_dph_node_array_buffer(); + + return status; +} + +/** ------------------------------------------------------------- + \fn pe_close + \brief will be called in close sequence from mac_close + \param struct mac_context *mac + \return QDF_STATUS + -------------------------------------------------------------*/ + +QDF_STATUS pe_close(struct mac_context *mac) +{ + uint8_t i; + + if (ANI_DRIVER_TYPE(mac) == QDF_DRIVER_TYPE_MFG) + return QDF_STATUS_SUCCESS; + + qdf_hang_event_unregister_notifier(&pe_hang_event_notifier); + lim_cleanup(mac); + lim_unregister_sap_bcn_callback(mac); + + if (mac->lim.limDisassocDeauthCnfReq.pMlmDeauthReq) { + qdf_mem_free(mac->lim.limDisassocDeauthCnfReq.pMlmDeauthReq); + mac->lim.limDisassocDeauthCnfReq.pMlmDeauthReq = NULL; + } + + qdf_spinlock_destroy(&mac->sys.bbt_mgmt_lock); + for (i = 0; i < mac->lim.maxBssId; i++) { + if (mac->lim.gpSession[i].valid == true) + pe_delete_session(mac, &mac->lim.gpSession[i]); + } + qdf_mem_free(mac->lim.lim_timers.gpLimCnfWaitTimer); + mac->lim.lim_timers.gpLimCnfWaitTimer = NULL; + + qdf_mem_free(mac->lim.gpSession); + mac->lim.gpSession = NULL; + + pe_free_dph_node_array_buffer(); + + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn pe_start + \brief will be called in start sequence from mac_start + \param struct mac_context *mac + \return QDF_STATUS_SUCCESS on success, other QDF_STATUS on error + -------------------------------------------------------------*/ + +QDF_STATUS pe_start(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + status = lim_start(mac); + if (QDF_STATUS_SUCCESS != status) { + pe_err("lim_start failed!"); + return status; + } + /* Initialize the configurations needed by PE */ + if (QDF_STATUS_E_FAILURE == __lim_init_config(mac)) { + pe_err("lim init config failed!"); + /* We need to undo everything in lim_start */ + lim_cleanup_mlm(mac); + return QDF_STATUS_E_FAILURE; + } + + return status; +} + +/** ------------------------------------------------------------- + \fn pe_stop + \brief will be called in stop sequence from mac_stop + \param struct mac_context *mac + \return none + -------------------------------------------------------------*/ + +void pe_stop(struct mac_context *mac) +{ + lim_cleanup_mlm(mac); + pe_debug(" PE STOP: Set LIM state to eLIM_MLM_OFFLINE_STATE"); + SET_LIM_MLM_STATE(mac, eLIM_MLM_OFFLINE_STATE); + return; +} + +static void pe_free_nested_messages(struct scheduler_msg *msg) +{ + switch (msg->type) { + default: + break; + } +} + +/** ------------------------------------------------------------- + \fn pe_free_msg + \brief Called by CDS scheduler (function cds_sched_flush_mc_mqs) + \ to free a given PE message on the TX and MC thread. + \ This happens when there are messages pending in the PE + \ queue when system is being stopped and reset. + \param struct mac_context *mac + \param struct scheduler_msg pMsg + \return none + -----------------------------------------------------------------*/ +void pe_free_msg(struct mac_context *mac, struct scheduler_msg *pMsg) +{ + if (pMsg) { + if (pMsg->bodyptr) { + if (SIR_BB_XPORT_MGMT_MSG == pMsg->type) { + cds_pkt_return_packet((cds_pkt_t *) pMsg-> + bodyptr); + } else { + pe_free_nested_messages(pMsg); + qdf_mem_free((void *)pMsg->bodyptr); + } + } + pMsg->bodyptr = 0; + pMsg->bodyval = 0; + pMsg->type = 0; + } + return; +} + +QDF_STATUS lim_post_msg_api(struct mac_context *mac, struct scheduler_msg *msg) +{ + return scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, msg); +} + +QDF_STATUS lim_post_msg_high_priority(struct mac_context *mac, + struct scheduler_msg *msg) +{ + return scheduler_post_msg_by_priority(QDF_MODULE_ID_PE, + msg, true); +} + +QDF_STATUS pe_mc_process_handler(struct scheduler_msg *msg) +{ + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac_ctx) + return QDF_STATUS_E_FAILURE; + + if (ANI_DRIVER_TYPE(mac_ctx) == QDF_DRIVER_TYPE_MFG) + return QDF_STATUS_SUCCESS; + + lim_message_processor(mac_ctx, msg); + + return QDF_STATUS_SUCCESS; +} + +/** + * pe_drop_pending_rx_mgmt_frames: To drop pending RX mgmt frames + * @mac_ctx: Pointer to global MAC structure + * @hdr: Management header + * @cds_pkt: Packet + * + * This function is used to drop RX pending mgmt frames if pe mgmt queue + * reaches threshold + * + * Return: QDF_STATUS_SUCCESS on success or QDF_STATUS_E_FAILURE on failure + */ +static QDF_STATUS pe_drop_pending_rx_mgmt_frames(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, cds_pkt_t *cds_pkt) +{ + qdf_spin_lock(&mac_ctx->sys.bbt_mgmt_lock); + if (mac_ctx->sys.sys_bbt_pending_mgmt_count >= + MGMT_RX_PACKETS_THRESHOLD) { + qdf_spin_unlock(&mac_ctx->sys.bbt_mgmt_lock); + pe_debug("No.of pending RX management frames reaches to threshold, dropping management frames"); + cds_pkt_return_packet(cds_pkt); + cds_pkt = NULL; + mac_ctx->rx_packet_drop_counter++; + return QDF_STATUS_E_FAILURE; + } else if (mac_ctx->sys.sys_bbt_pending_mgmt_count > + (MGMT_RX_PACKETS_THRESHOLD / 2)) { + /* drop all probereq, proberesp and beacons */ + if (hdr->fc.subType == SIR_MAC_MGMT_BEACON || + hdr->fc.subType == SIR_MAC_MGMT_PROBE_REQ || + hdr->fc.subType == SIR_MAC_MGMT_PROBE_RSP) { + qdf_spin_unlock(&mac_ctx->sys.bbt_mgmt_lock); + if (!(mac_ctx->rx_packet_drop_counter % 100)) + pe_debug("No.of pending RX mgmt frames reaches 1/2 thresh, dropping frame subtype: %d rx_packet_drop_counter: %d", + hdr->fc.subType, + mac_ctx->rx_packet_drop_counter); + mac_ctx->rx_packet_drop_counter++; + cds_pkt_return_packet(cds_pkt); + cds_pkt = NULL; + return QDF_STATUS_E_FAILURE; + } + } + mac_ctx->sys.sys_bbt_pending_mgmt_count++; + qdf_spin_unlock(&mac_ctx->sys.bbt_mgmt_lock); + if (mac_ctx->sys.sys_bbt_pending_mgmt_count == + (MGMT_RX_PACKETS_THRESHOLD / 4)) { + if (!(mac_ctx->rx_packet_drop_counter % 100)) + pe_debug("No.of pending RX management frames reaches to 1/4th of threshold, rx_packet_drop_counter: %d", + mac_ctx->rx_packet_drop_counter); + mac_ctx->rx_packet_drop_counter++; + } + return QDF_STATUS_SUCCESS; +} + +/** + * pe_is_ext_scan_bcn_probe_rsp - Check if the beacon or probe response + * is from Ext or EPNO scan + * + * @hdr: pointer to the 802.11 header of the frame + * @rx_pkt_info: pointer to the rx packet meta + * + * Checks if the beacon or probe response is from Ext Scan or EPNO scan + * + * Return: true or false + */ +#ifdef FEATURE_WLAN_EXTSCAN +static inline bool pe_is_ext_scan_bcn_probe_rsp(tpSirMacMgmtHdr hdr, + uint8_t *rx_pkt_info) +{ + if ((hdr->fc.subType == SIR_MAC_MGMT_BEACON || + hdr->fc.subType == SIR_MAC_MGMT_PROBE_RSP) && + (WMA_IS_EXTSCAN_SCAN_SRC(rx_pkt_info) || + WMA_IS_EPNO_SCAN_SRC(rx_pkt_info))) + return true; + + return false; +} +#else +static inline bool pe_is_ext_scan_bcn_probe_rsp(tpSirMacMgmtHdr hdr, + uint8_t *rx_pkt_info) +{ + return false; +} +#endif + +/** + * pe_filter_drop_bcn_probe_frame - Apply filter on the received frame + * + * @mac_ctx: pointer to the global mac context + * @hdr: pointer to the 802.11 header of the frame + * @rx_pkt_info: pointer to the rx packet meta + * + * Applies the filter from global mac context on the received beacon/ + * probe response frame before posting it to the PE queue + * + * Return: true if frame is allowed, false if frame is to be dropped. + */ +static bool pe_filter_bcn_probe_frame(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, + uint8_t *rx_pkt_info) +{ + uint8_t session_id; + uint8_t *body; + const uint8_t *ssid_ie; + uint16_t frame_len; + struct mgmt_beacon_probe_filter *filter; + tpSirMacCapabilityInfo bcn_caps; + tSirMacSSid bcn_ssid; + + if (pe_is_ext_scan_bcn_probe_rsp(hdr, rx_pkt_info)) + return true; + + filter = &mac_ctx->bcn_filter; + + /* + * If any STA session exists and beacon source matches any of the + * STA BSSIDs, allow the frame + */ + if (filter->num_sta_sessions) { + for (session_id = 0; session_id < WLAN_MAX_VDEVS; + session_id++) { + if (sir_compare_mac_addr(filter->sta_bssid[session_id], + hdr->bssId)) { + return true; + } + } + } + + /* + * If any IBSS session exists and beacon is has IBSS capability set + * and SSID matches the IBSS SSID, allow the frame + */ + if (filter->num_ibss_sessions) { + body = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + if (frame_len < SIR_MAC_B_PR_SSID_OFFSET) + return false; + + bcn_caps = (tpSirMacCapabilityInfo) + (body + SIR_MAC_B_PR_CAPAB_OFFSET); + if (!bcn_caps->ibss) + return false; + + ssid_ie = wlan_get_ie_ptr_from_eid(WLAN_ELEMID_SSID, + body + SIR_MAC_B_PR_SSID_OFFSET, + frame_len - SIR_MAC_B_PR_SSID_OFFSET); + + if (!ssid_ie) + return false; + + bcn_ssid.length = ssid_ie[1]; + if (bcn_ssid.length > WLAN_SSID_MAX_LEN) + return false; + + qdf_mem_copy(&bcn_ssid.ssId, + &ssid_ie[2], + bcn_ssid.length); + + for (session_id = 0; session_id < WLAN_MAX_VDEVS; + session_id++) { + if (filter->ibss_ssid[session_id].length == + bcn_ssid.length && + (!qdf_mem_cmp(filter->ibss_ssid[session_id].ssId, + bcn_ssid.ssId, bcn_ssid.length))) { + return true; + } + } + } + + return false; +} + +static QDF_STATUS pe_handle_probe_req_frames(struct mac_context *mac_ctx, + cds_pkt_t *pkt) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0}; + uint32_t scan_queue_size = 0; + + /* Check if the probe request frame can be posted in the scan queue */ + status = scheduler_get_queue_size(QDF_MODULE_ID_SCAN, &scan_queue_size); + if (!QDF_IS_STATUS_SUCCESS(status) || + scan_queue_size > MAX_BCN_PROBE_IN_SCAN_QUEUE) { + pe_debug_rl("Dropping probe req frame, queue size %d", + scan_queue_size); + return QDF_STATUS_E_FAILURE; + } + + /* Forward to MAC via mesg = SIR_BB_XPORT_MGMT_MSG */ + msg.type = SIR_BB_XPORT_MGMT_MSG; + msg.bodyptr = pkt; + msg.bodyval = 0; + msg.callback = pe_mc_process_handler; + + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_SCAN, &msg); + + return status; +} + +/* --------------------------------------------------------------------------- */ +/** + * pe_handle_mgmt_frame() - Process the Management frames from TXRX + * @psoc: psoc context + * @peer: peer + * @buf: buffer + * @mgmt_rx_params; rx event params + * @frm_type: frame type + * + * This function handles the mgmt rx frame from mgmt txrx component and forms + * a cds packet and schedule it in controller thread for further processing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS pe_handle_mgmt_frame(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type) +{ + struct mac_context *mac; + tpSirMacMgmtHdr mHdr; + struct scheduler_msg msg = {0}; + cds_pkt_t *pVosPkt; + QDF_STATUS qdf_status; + uint8_t *pRxPacketInfo; + int ret; + + mac = cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + /* cannot log a failure without a valid mac */ + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + pVosPkt = qdf_mem_malloc_atomic(sizeof(*pVosPkt)); + if (!pVosPkt) { + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + ret = wma_form_rx_packet(buf, mgmt_rx_params, pVosPkt); + if (ret) { + pe_err_rl("Failed to fill cds packet from event buffer"); + return QDF_STATUS_E_FAILURE; + } + + qdf_status = + wma_ds_peek_rx_packet_info(pVosPkt, (void *)&pRxPacketInfo, false); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_pkt_return_packet(pVosPkt); + pVosPkt = NULL; + return QDF_STATUS_E_FAILURE; + } + + /* + * The MPDU header is now present at a certain "offset" in + * the BD and is specified in the BD itself + */ + + mHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + + /* + * Filter the beacon/probe response frames before posting it + * on the PE queue + */ + if ((mHdr->fc.subType == SIR_MAC_MGMT_BEACON || + mHdr->fc.subType == SIR_MAC_MGMT_PROBE_RSP) && + !pe_filter_bcn_probe_frame(mac, mHdr, pRxPacketInfo)) { + cds_pkt_return_packet(pVosPkt); + pVosPkt = NULL; + return QDF_STATUS_SUCCESS; + } + + /* + * Post Probe Req frames to Scan queue and return + */ + if (mHdr->fc.subType == SIR_MAC_MGMT_PROBE_REQ) { + qdf_status = pe_handle_probe_req_frames(mac, pVosPkt); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_pkt_return_packet(pVosPkt); + pVosPkt = NULL; + } + return qdf_status; + } + + if (QDF_STATUS_SUCCESS != + pe_drop_pending_rx_mgmt_frames(mac, mHdr, pVosPkt)) + return QDF_STATUS_E_FAILURE; + + /* Forward to MAC via mesg = SIR_BB_XPORT_MGMT_MSG */ + msg.type = SIR_BB_XPORT_MGMT_MSG; + msg.bodyptr = pVosPkt; + msg.bodyval = 0; + + if (QDF_STATUS_SUCCESS != sys_bbt_process_message_core(mac, + &msg, + mHdr->fc.type, + mHdr->fc.subType)) { + cds_pkt_return_packet(pVosPkt); + pVosPkt = NULL; + /* + * Decrement sys_bbt_pending_mgmt_count if packet + * is dropped before posting to LIM + */ + lim_decrement_pending_mgmt_count(mac); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +void pe_register_mgmt_rx_frm_callback(struct mac_context *mac_ctx) +{ + QDF_STATUS status; + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info; + + frm_cb_info.frm_type = MGMT_FRAME_TYPE_ALL; + frm_cb_info.mgmt_rx_cb = pe_handle_mgmt_frame; + + status = wlan_mgmt_txrx_register_rx_cb(mac_ctx->psoc, + WLAN_UMAC_COMP_MLME, &frm_cb_info, 1); + if (status != QDF_STATUS_SUCCESS) + pe_err("Registering the PE Handle with MGMT TXRX layer has failed"); + + wma_register_mgmt_frm_client(); +} + +void pe_deregister_mgmt_rx_frm_callback(struct mac_context *mac_ctx) +{ + QDF_STATUS status; + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info; + + frm_cb_info.frm_type = MGMT_FRAME_TYPE_ALL; + frm_cb_info.mgmt_rx_cb = pe_handle_mgmt_frame; + + status = wlan_mgmt_txrx_deregister_rx_cb(mac_ctx->psoc, + WLAN_UMAC_COMP_MLME, &frm_cb_info, 1); + if (status != QDF_STATUS_SUCCESS) + pe_err("Deregistering the PE Handle with MGMT TXRX layer has failed"); + + wma_de_register_mgmt_frm_client(); +} + + +/** + * pe_register_callbacks_with_wma() - register SME and PE callback functions to + * WMA. + * (function documentation in lim_api.h) + */ +void pe_register_callbacks_with_wma(struct mac_context *mac, + struct sme_ready_req *ready_req) +{ + QDF_STATUS status; + + status = wma_register_roaming_callbacks( + ready_req->csr_roam_synch_cb, + ready_req->csr_roam_auth_event_handle_cb, + ready_req->pe_roam_synch_cb, + ready_req->pe_disconnect_cb, + ready_req->csr_roam_pmkid_req_cb); + if (status != QDF_STATUS_SUCCESS) + pe_err("Registering roaming callbacks with WMA failed"); +} + +void +lim_received_hb_handler(struct mac_context *mac, uint32_t chan_freq, + struct pe_session *pe_session) +{ + if (chan_freq == 0 || chan_freq == pe_session->curr_op_freq) + pe_session->LimRxedBeaconCntDuringHB++; + + pe_session->pmmOffloadInfo.bcnmiss = false; +} /*** lim_init_wds_info_params() ***/ + +/** ------------------------------------------------------------- + \fn lim_update_overlap_sta_param + \brief Updates overlap cache and param data structure + \param struct mac_context * mac + \param tSirMacAddr bssId + \param tpLimProtStaParams pStaParams + \return None + -------------------------------------------------------------*/ +void +lim_update_overlap_sta_param(struct mac_context *mac, tSirMacAddr bssId, + tpLimProtStaParams pStaParams) +{ + int i; + + if (!pStaParams->numSta) { + qdf_mem_copy(mac->lim.protStaOverlapCache[0].addr, + bssId, sizeof(tSirMacAddr)); + mac->lim.protStaOverlapCache[0].active = true; + + pStaParams->numSta = 1; + + return; + } + + for (i = 0; i < LIM_PROT_STA_OVERLAP_CACHE_SIZE; i++) { + if (mac->lim.protStaOverlapCache[i].active) { + if (!qdf_mem_cmp + (mac->lim.protStaOverlapCache[i].addr, bssId, + sizeof(tSirMacAddr))) { + return; + } + } else + break; + } + + if (i == LIM_PROT_STA_OVERLAP_CACHE_SIZE) { + pe_debug("Overlap cache is full"); + } else { + qdf_mem_copy(mac->lim.protStaOverlapCache[i].addr, + bssId, sizeof(tSirMacAddr)); + mac->lim.protStaOverlapCache[i].active = true; + + pStaParams->numSta++; + } +} + +#ifdef QCA_IBSS_SUPPORT +/** + * lim_ibss_enc_type_matched() - API to check enc type match + * @param pBeacon - Parsed Beacon Frame structure + * @param pSession - Pointer to the PE session + * + * This function compares the encryption type of the peer with self + * while operating in IBSS mode and detects mismatch. + * + * @return true if encryption type is matched; false otherwise + */ +static bool lim_ibss_enc_type_matched(tpSchBeaconStruct pBeacon, + struct pe_session *pSession) +{ + if (!pBeacon || !pSession) + return false; + + /* Open case */ + if (pBeacon->capabilityInfo.privacy == 0 + && pSession->encryptType == eSIR_ED_NONE) + return true; + + /* WEP case */ + if (pBeacon->capabilityInfo.privacy == 1 && pBeacon->wpaPresent == 0 + && pBeacon->rsnPresent == 0 + && (pSession->encryptType == eSIR_ED_WEP40 + || pSession->encryptType == eSIR_ED_WEP104)) + return true; + + /* WPA-None case */ + if (pBeacon->capabilityInfo.privacy == 1 && pBeacon->wpaPresent == 1 + && pBeacon->rsnPresent == 0 + && ((pSession->encryptType == eSIR_ED_CCMP) || + (pSession->encryptType == eSIR_ED_GCMP) || + (pSession->encryptType == eSIR_ED_GCMP_256) || + (pSession->encryptType == eSIR_ED_TKIP))) + return true; + + return false; +} + +QDF_STATUS +lim_handle_ibss_coalescing(struct mac_context *mac, + tpSchBeaconStruct pBeacon, + uint8_t *pRxPacketInfo, struct pe_session *pe_session) +{ + tpSirMacMgmtHdr pHdr; + QDF_STATUS retCode; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + + /* Ignore the beacon when any of the conditions below is met: + 1. The beacon claims no IBSS network + 2. SSID in the beacon does not match SSID of self station + 3. Operational channel in the beacon does not match self station + 4. Encyption type in the beacon does not match with self station + */ + if ((!pBeacon->capabilityInfo.ibss) || + lim_cmp_ssid(&pBeacon->ssId, pe_session) || + (pe_session->curr_op_freq != pBeacon->chan_freq)) + retCode = QDF_STATUS_E_INVAL; + else if (lim_ibss_enc_type_matched(pBeacon, pe_session) != true) { + pe_debug("peer privacy: %d peer wpa: %d peer rsn: %d self encType: %d", + pBeacon->capabilityInfo.privacy, + pBeacon->wpaPresent, pBeacon->rsnPresent, + pe_session->encryptType); + retCode = QDF_STATUS_E_INVAL; + } else { + uint32_t ieLen; + uint16_t tsfLater; + uint8_t *pIEs; + + ieLen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + tsfLater = WMA_GET_RX_TSF_LATER(pRxPacketInfo); + pIEs = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + pe_debug("BEFORE Coalescing tsfLater val: %d", tsfLater); + retCode = + lim_ibss_coalesce(mac, pHdr, pBeacon, pIEs, ieLen, tsfLater, + pe_session); + } + return retCode; +} /*** end lim_handle_ibs_scoalescing() ***/ +#endif + +/** + * lim_enc_type_matched() - matches security type of incoming beracon with + * current + * @mac_ctx Pointer to Global MAC structure + * @bcn Pointer to parsed Beacon structure + * @session PE session entry + * + * This function matches security type of incoming beracon with current + * + * @return true if matched, false otherwise + */ +static bool +lim_enc_type_matched(struct mac_context *mac_ctx, + tpSchBeaconStruct bcn, + struct pe_session *session) +{ + if (!bcn || !session) + return false; + + /* + * This is handled by sending probe req due to IOT issues so + * return TRUE + */ + if ((bcn->capabilityInfo.privacy) != + SIR_MAC_GET_PRIVACY(session->limCurrentBssCaps)) { + pe_warn("Privacy bit miss match"); + return true; + } + + /* Open */ + if ((bcn->capabilityInfo.privacy == 0) && + (session->encryptType == eSIR_ED_NONE)) + return true; + + /* WEP */ + if ((bcn->capabilityInfo.privacy == 1) && + (bcn->wpaPresent == 0) && (bcn->rsnPresent == 0) && + ((session->encryptType == eSIR_ED_WEP40) || + (session->encryptType == eSIR_ED_WEP104) +#ifdef FEATURE_WLAN_WAPI + || (session->encryptType == eSIR_ED_WPI) +#endif + )) + return true; + + /* WPA OR RSN*/ + if ((bcn->capabilityInfo.privacy == 1) && + ((bcn->wpaPresent == 1) || (bcn->rsnPresent == 1)) && + ((session->encryptType == eSIR_ED_TKIP) || + (session->encryptType == eSIR_ED_CCMP) || + (session->encryptType == eSIR_ED_GCMP) || + (session->encryptType == eSIR_ED_GCMP_256) || + (session->encryptType == eSIR_ED_AES_128_CMAC))) + return true; + + /* + * For HS2.0, RSN ie is not present + * in beacon. Therefore no need to + * check for security type in case + * OSEN session. + * For WPS registration session no need to detect + * detect security mismatch as it wont match and + * driver may end up sending probe request without + * WPS IE during WPS registration process. + */ + if (session->isOSENConnection || + session->wps_registration) + return true; + + pe_debug("AP:: Privacy %d WPA %d RSN %d, SELF:: Privacy %d Enc %d OSEN %d WPS %d", + bcn->capabilityInfo.privacy, bcn->wpaPresent, bcn->rsnPresent, + SIR_MAC_GET_PRIVACY(session->limCurrentBssCaps), + session->encryptType, session->isOSENConnection, + session->wps_registration); + + return false; +} + +/** + * lim_detect_change_in_ap_capabilities() + * + ***FUNCTION: + * This function is called while SCH is processing + * received Beacon from AP on STA to detect any + * change in AP's capabilities. If there any change + * is detected, Roaming is informed of such change + * so that it can trigger reassociation. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * Notification is enabled for STA product only since + * it is not a requirement on BP side. + * + * @param mac Pointer to Global MAC structure + * @param pBeacon Pointer to parsed Beacon structure + * @return None + */ + +void +lim_detect_change_in_ap_capabilities(struct mac_context *mac, + tpSirProbeRespBeacon pBeacon, + struct pe_session *pe_session) +{ + uint8_t len; + struct ap_new_caps apNewCaps; + uint32_t new_chan_freq; + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool security_caps_matched = true; + + apNewCaps.capabilityInfo = + lim_get_u16((uint8_t *) &pBeacon->capabilityInfo); + new_chan_freq = pBeacon->chan_freq; + + security_caps_matched = lim_enc_type_matched(mac, pBeacon, + pe_session); + if ((false == pe_session->limSentCapsChangeNtf) && + (((!lim_is_null_ssid(&pBeacon->ssId)) && + lim_cmp_ssid(&pBeacon->ssId, pe_session)) || + ((SIR_MAC_GET_ESS(apNewCaps.capabilityInfo) != + SIR_MAC_GET_ESS(pe_session->limCurrentBssCaps)) || + (SIR_MAC_GET_PRIVACY(apNewCaps.capabilityInfo) != + SIR_MAC_GET_PRIVACY(pe_session->limCurrentBssCaps)) || + (SIR_MAC_GET_QOS(apNewCaps.capabilityInfo) != + SIR_MAC_GET_QOS(pe_session->limCurrentBssCaps)) || + ((new_chan_freq != pe_session->curr_op_freq) && + (new_chan_freq != 0)) || + (false == security_caps_matched) + ))) { + if (false == pe_session->fWaitForProbeRsp) { + /* If Beacon capabilities is not matching with the current capability, + * then send unicast probe request to AP and take decision after + * receiving probe response */ + if (true == pe_session->fIgnoreCapsChange) { + pe_debug("Ignoring the Capability change as it is false alarm"); + return; + } + pe_session->fWaitForProbeRsp = true; + pe_warn("AP capabilities are not matching, sending directed probe request"); + status = + lim_send_probe_req_mgmt_frame( + mac, &pe_session->ssId, + pe_session->bssId, + pe_session->curr_op_freq, + pe_session->self_mac_addr, + pe_session->dot11mode, + NULL, NULL); + + if (QDF_STATUS_SUCCESS != status) { + pe_err("send ProbeReq failed"); + pe_session->fWaitForProbeRsp = false; + } + return; + } + /** + * BSS capabilities have changed. + * Inform Roaming. + */ + len = sizeof(tSirMacCapabilityInfo) + sizeof(tSirMacAddr) + sizeof(uint8_t) + 3 * sizeof(uint8_t) + /* reserved fields */ + pBeacon->ssId.length + 1; + + qdf_mem_copy(apNewCaps.bssId.bytes, + pe_session->bssId, QDF_MAC_ADDR_SIZE); + if (new_chan_freq != pe_session->curr_op_freq) { + pe_err("Channel freq Change from %d --> %d Ignoring beacon!", + pe_session->curr_op_freq, new_chan_freq); + return; + } + + /** + * When Cisco 1262 Enterprise APs are configured with WPA2-PSK with + * AES+TKIP Pairwise ciphers and WEP-40 Group cipher, they do not set + * the privacy bit in Beacons (wpa/rsnie is still present in beacons), + * the privacy bit is set in Probe and association responses. + * Due to this anomaly, we detect a change in + * AP capabilities when we receive a beacon after association and + * disconnect from the AP. The following check makes sure that we can + * connect to such APs + */ + else if ((SIR_MAC_GET_PRIVACY(apNewCaps.capabilityInfo) == 0) && + (pBeacon->rsnPresent || pBeacon->wpaPresent)) { + pe_err("BSS Caps (Privacy) bit 0 in beacon, but WPA or RSN IE present, Ignore Beacon!"); + return; + } + qdf_mem_copy((uint8_t *) &apNewCaps.ssId, + (uint8_t *) &pBeacon->ssId, + pBeacon->ssId.length + 1); + + pe_session->fIgnoreCapsChange = false; + pe_session->fWaitForProbeRsp = false; + pe_session->limSentCapsChangeNtf = true; + lim_send_sme_wm_status_change_ntf(mac, eSIR_SME_AP_CAPS_CHANGED, + (uint32_t *) &apNewCaps, + len, pe_session->smeSessionId); + } else if (true == pe_session->fWaitForProbeRsp) { + /* Only for probe response frames and matching capabilities the control + * will come here. If beacon is with broadcast ssid then fWaitForProbeRsp + * will be false, the control will not come here*/ + + pe_debug("capabilities in probe response are" + "matching with the current setting," + "Ignoring subsequent capability" + "mismatch"); + pe_session->fIgnoreCapsChange = true; + pe_session->fWaitForProbeRsp = false; + } + +} /*** lim_detect_change_in_ap_capabilities() ***/ + +/* --------------------------------------------------------------------- */ +/** + * lim_update_short_slot + * + * FUNCTION: + * Enable/Disable short slot + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param enable Flag to enable/disable short slot + * @return None + */ + +QDF_STATUS lim_update_short_slot(struct mac_context *mac, + tpSirProbeRespBeacon pBeacon, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + + struct ap_new_caps apNewCaps; + uint32_t nShortSlot; + uint32_t phyMode; + + /* Check Admin mode first. If it is disabled just return */ + if (!mac->mlme_cfg->feature_flags.enable_short_slot_time_11g) + return QDF_STATUS_SUCCESS; + + /* Check for 11a mode or 11b mode. In both cases return since slot time is constant and cannot/should not change in beacon */ + lim_get_phy_mode(mac, &phyMode, pe_session); + if ((phyMode == WNI_CFG_PHY_MODE_11A) + || (phyMode == WNI_CFG_PHY_MODE_11B)) + return QDF_STATUS_SUCCESS; + + apNewCaps.capabilityInfo = + lim_get_u16((uint8_t *) &pBeacon->capabilityInfo); + + /* Earlier implementation: determine the appropriate short slot mode based on AP advertised modes */ + /* when erp is present, apply short slot always unless, prot=on && shortSlot=off */ + /* if no erp present, use short slot based on current ap caps */ + + /* Issue with earlier implementation : Cisco 1231 BG has shortSlot = 0, erpIEPresent and useProtection = 0 (Case4); */ + + /* Resolution : always use the shortSlot setting the capability info to decide slot time. */ + /* The difference between the earlier implementation and the new one is only Case4. */ + /* + ERP IE Present | useProtection | shortSlot = QC STA Short Slot + Case1 1 1 1 1 //AP should not advertise this combination. + Case2 1 1 0 0 + Case3 1 0 1 1 + Case4 1 0 0 0 + Case5 0 1 1 1 + Case6 0 1 0 0 + Case7 0 0 1 1 + Case8 0 0 0 0 + */ + nShortSlot = SIR_MAC_GET_SHORT_SLOT_TIME(apNewCaps.capabilityInfo); + + if (nShortSlot != pe_session->shortSlotTimeSupported) { + /* Short slot time capability of AP has changed. Adopt to it. */ + pe_debug("Shortslot capability of AP changed: %d", + nShortSlot); + ((tpSirMacCapabilityInfo) & pe_session-> + limCurrentBssCaps)->shortSlotTime = (uint16_t) nShortSlot; + pe_session->shortSlotTimeSupported = nShortSlot; + pBeaconParams->fShortSlotTime = (uint8_t) nShortSlot; + pBeaconParams->paramChangeBitmap |= + PARAM_SHORT_SLOT_TIME_CHANGED; + } + return QDF_STATUS_SUCCESS; +} + + +void lim_send_heart_beat_timeout_ind(struct mac_context *mac, + struct pe_session *pe_session) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0}; + + /* Prepare and post message to LIM Message Queue */ + msg.type = (uint16_t) SIR_LIM_HEART_BEAT_TIMEOUT; + msg.bodyptr = pe_session; + msg.bodyval = 0; + pe_err("Heartbeat failure from Fw"); + + status = lim_post_msg_api(mac, &msg); + + if (status != QDF_STATUS_SUCCESS) { + pe_err("posting message: %X to LIM failed, reason: %d", + msg.type, status); + } +} + +void lim_ps_offload_handle_missed_beacon_ind(struct mac_context *mac, + struct scheduler_msg *msg) +{ + struct missed_beacon_ind *missed_beacon_ind = msg->bodyptr; + struct pe_session *pe_session = + pe_find_session_by_vdev_id(mac, missed_beacon_ind->bss_idx); + + if (!pe_session) { + pe_err("session does not exist for vdev_id %d", + missed_beacon_ind->bss_idx); + return; + } + + /* Set Beacon Miss in Powersave Offload */ + pe_session->pmmOffloadInfo.bcnmiss = true; + pe_err("Received Heart Beat Failure"); + + /* Do AP probing immediately */ + lim_send_heart_beat_timeout_ind(mac, pe_session); +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * lim_fill_join_rsp_ht_caps() - Fill the HT caps in join response + * @session: PE Session + * @join_rsp: Join response buffer to be filled up. + * + * Return: None + */ +void lim_fill_join_rsp_ht_caps(struct pe_session *session, + struct join_rsp *join_rsp) +{ + struct ht_profile *ht_profile; + + if (!session) { + pe_err("Invalid Session"); + return; + } + if (!join_rsp) { + pe_err("Invalid Join Response"); + return; + } + + if (session->cc_switch_mode == QDF_MCC_TO_SCC_SWITCH_DISABLE) + return; + + ht_profile = &join_rsp->ht_profile; + ht_profile->htSupportedChannelWidthSet = + session->htSupportedChannelWidthSet; + ht_profile->htRecommendedTxWidthSet = + session->htRecommendedTxWidthSet; + ht_profile->htSecondaryChannelOffset = + session->htSecondaryChannelOffset; + ht_profile->dot11mode = session->dot11mode; + ht_profile->htCapability = session->htCapability; + ht_profile->vhtCapability = session->vhtCapability; + ht_profile->apCenterChan = session->ch_center_freq_seg0; + ht_profile->apChanWidth = session->ch_width; +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +#ifdef WLAN_FEATURE_11W +static void pe_set_rmf_caps(struct mac_context *mac_ctx, + struct pe_session *ft_session, + struct roam_offload_synch_ind *roam_synch) +{ + uint8_t *assoc_body; + uint16_t len; + tDot11fReAssocRequest *assoc_req; + uint32_t status; + tSirMacRsnInfo rsn_ie; + + assoc_body = (uint8_t *)roam_synch + roam_synch->reassoc_req_offset + + sizeof(tSirMacMgmtHdr); + len = roam_synch->reassoc_req_length - sizeof(tSirMacMgmtHdr); + + assoc_req = qdf_mem_malloc(sizeof(*assoc_req)); + if (!assoc_req) + return; + + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_re_assoc_request(mac_ctx, assoc_body, len, + assoc_req, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse a Re-association Request (0x%08x, %d bytes):", + status, len); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_INFO, + assoc_body, len); + qdf_mem_free(assoc_req); + return; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking a Re-association Request (0x%08x, %d bytes):", + status, len); + } + ft_session->limRmfEnabled = false; + if (!assoc_req->RSNOpaque.present) { + qdf_mem_free(assoc_req); + return; + } + rsn_ie.info[0] = WLAN_ELEMID_RSN; + rsn_ie.info[1] = assoc_req->RSNOpaque.num_data; + + rsn_ie.length = assoc_req->RSNOpaque.num_data + 2; + qdf_mem_copy(&rsn_ie.info[2], assoc_req->RSNOpaque.data, + assoc_req->RSNOpaque.num_data); + qdf_mem_free(assoc_req); + wlan_set_vdev_crypto_prarams_from_ie(ft_session->vdev, rsn_ie.info, + rsn_ie.length); + + ft_session->limRmfEnabled = + lim_get_vdev_rmf_capable(mac_ctx, ft_session); +} +#else +static inline void pe_set_rmf_caps(struct mac_context *mac_ctx, + struct pe_session *ft_session, + struct roam_offload_synch_ind *roam_synch) +{ +} +#endif + +/** + * sir_parse_bcn_fixed_fields() - Parse fixed fields in Beacon IE's + * + * @mac_ctx: MAC Context + * @beacon_struct: Beacon/Probe Response structure + * @buf: Fixed Fields buffer + */ +static void sir_parse_bcn_fixed_fields(struct mac_context *mac_ctx, + tpSirProbeRespBeacon beacon_struct, + uint8_t *buf) +{ + tDot11fFfCapabilities dst; + + beacon_struct->timeStamp[0] = lim_get_u32(buf); + beacon_struct->timeStamp[1] = lim_get_u32(buf + 4); + buf += 8; + + beacon_struct->beaconInterval = lim_get_u16(buf); + buf += 2; + + dot11f_unpack_ff_capabilities(mac_ctx, buf, &dst); + + sir_copy_caps_info(mac_ctx, dst, beacon_struct); +} + +static QDF_STATUS +lim_roam_gen_mbssid_beacon(struct mac_context *mac, + struct roam_offload_synch_ind *roam_ind, + tpSirProbeRespBeacon parsed_frm, + uint8_t **ie, uint32_t *ie_len) +{ + qdf_list_t *scan_list; + struct mgmt_rx_event_params rx_param; + uint8_t list_count = 0, i; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + qdf_list_node_t *next_node = NULL, *cur_node = NULL; + struct scan_cache_node *scan_node; + struct scan_cache_entry *scan_entry; + uint8_t *bcn_prb_ptr; + uint32_t nontx_bcn_prbrsp_len = 0, offset, length; + uint8_t *nontx_bcn_prbrsp = NULL; + uint8_t ie_offset; + + ie_offset = SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_SSID_OFFSET; + bcn_prb_ptr = (uint8_t *)roam_ind + + roam_ind->beaconProbeRespOffset; + + rx_param.chan_freq = roam_ind->chan_freq; + rx_param.pdev_id = wlan_objmgr_pdev_get_pdev_id(mac->pdev); + rx_param.rssi = roam_ind->rssi; + + scan_list = util_scan_unpack_beacon_frame(mac->pdev, bcn_prb_ptr, + roam_ind->beaconProbeRespLength, + MGMT_SUBTYPE_BEACON, &rx_param); + if (!scan_list) { + pe_err("failed to parse"); + return QDF_STATUS_E_FAILURE; + } + + list_count = qdf_list_size(scan_list); + status = qdf_list_peek_front(scan_list, &cur_node); + if (QDF_IS_STATUS_ERROR(status) || !cur_node) { + pe_debug("list peek front failure. list size %d", list_count); + goto error; + } + + for (i = 1; i < list_count; i++) { + scan_node = qdf_container_of(cur_node, + struct scan_cache_node, node); + scan_entry = scan_node->entry; + if (qdf_is_macaddr_equal(&roam_ind->bssid, + &scan_entry->bssid)) { + pe_debug("matched BSSID "QDF_MAC_ADDR_FMT" bcn len %d profiles %d", + QDF_MAC_ADDR_REF(scan_entry->bssid.bytes), + scan_entry->raw_frame.len, + list_count); + nontx_bcn_prbrsp = scan_entry->raw_frame.ptr; + nontx_bcn_prbrsp_len = scan_entry->raw_frame.len; + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_DEBUG, + scan_entry->raw_frame.ptr, + nontx_bcn_prbrsp_len); + break; + } + status = qdf_list_peek_next(scan_list, cur_node, &next_node); + if (QDF_IS_STATUS_ERROR(status) || !next_node) { + pe_debug("list remove failure i:%d, lsize:%d", + i, list_count); + goto error; + } + cur_node = next_node; + } + + if (!nontx_bcn_prbrsp_len) { + pe_debug("failed to generate/find MBSSID beacon"); + goto error; + } + + if (roam_ind->isBeacon) { + offset = SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_SSID_OFFSET; + length = nontx_bcn_prbrsp_len - SIR_MAC_HDR_LEN_3A; + if (sir_parse_beacon_ie(mac, parsed_frm, + &nontx_bcn_prbrsp[offset], + length) != QDF_STATUS_SUCCESS || + !parsed_frm->ssidPresent) { + pe_err("Parse error Beacon, length: %d", + roam_ind->beaconProbeRespLength); + status = QDF_STATUS_E_FAILURE; + goto error; + } + } else { + offset = SIR_MAC_HDR_LEN_3A; + length = nontx_bcn_prbrsp_len - SIR_MAC_HDR_LEN_3A; + if (sir_convert_probe_frame2_struct(mac, + &nontx_bcn_prbrsp[offset], + length, + parsed_frm) != QDF_STATUS_SUCCESS || + !parsed_frm->ssidPresent) { + pe_err("Parse error ProbeResponse, length: %d", + roam_ind->beaconProbeRespLength); + status = QDF_STATUS_E_FAILURE; + goto error; + } + } + + *ie_len = nontx_bcn_prbrsp_len - ie_offset; + if (*ie_len) { + *ie = qdf_mem_malloc(*ie_len); + if (!*ie) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy(*ie, nontx_bcn_prbrsp + ie_offset, *ie_len); + pe_debug("beacon/probe Ie length: %d", *ie_len); + } +error: + for (i = 0; i < list_count; i++) { + status = qdf_list_remove_front(scan_list, &next_node); + if (QDF_IS_STATUS_ERROR(status) || !next_node) { + pe_debug("list remove failure i:%d, lsize:%d", + i, list_count); + break; + } + scan_node = qdf_container_of(next_node, + struct scan_cache_node, node); + util_scan_free_cache_entry(scan_node->entry); + qdf_mem_free(scan_node); + } + qdf_mem_free(scan_list); + + return status; +} + +static QDF_STATUS +lim_roam_gen_beacon_descr(struct mac_context *mac, + struct roam_offload_synch_ind *roam_ind, + tpSirProbeRespBeacon parsed_frm, + uint8_t **ie, uint32_t *ie_len) +{ + QDF_STATUS status; + uint8_t *bcn_prb_ptr; + tpSirMacMgmtHdr mac_hdr; + uint8_t ie_offset; + + bcn_prb_ptr = (uint8_t *)roam_ind + + roam_ind->beaconProbeRespOffset; + mac_hdr = (tpSirMacMgmtHdr)bcn_prb_ptr; + ie_offset = SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_SSID_OFFSET; + + if (qdf_is_macaddr_zero((struct qdf_mac_addr *)mac_hdr->bssId)) { + pe_debug("bssid is 0 in beacon/probe update it with bssId "QDF_MAC_ADDR_FMT" in sync ind", + QDF_MAC_ADDR_REF(roam_ind->bssid.bytes)); + qdf_mem_copy(mac_hdr->bssId, roam_ind->bssid.bytes, + sizeof(tSirMacAddr)); + } + + if (qdf_mem_cmp(&roam_ind->bssid.bytes, + &mac_hdr->bssId, QDF_MAC_ADDR_SIZE) != 0) { + pe_debug("LFR3:MBSSID Beacon/Prb Rsp: %d bssid "QDF_MAC_ADDR_FMT, + roam_ind->isBeacon, + QDF_MAC_ADDR_REF(mac_hdr->bssId)); + /* + * Its a MBSSID non-tx BSS roaming scenario. + * Generate non tx BSS beacon/probe response + */ + status = lim_roam_gen_mbssid_beacon(mac, + roam_ind, + parsed_frm, + ie, ie_len); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("failed to gen mbssid beacon"); + return QDF_STATUS_E_FAILURE; + } + } else { + if (roam_ind->isBeacon) { + if (sir_parse_beacon_ie(mac, parsed_frm, + &bcn_prb_ptr[SIR_MAC_HDR_LEN_3A + + SIR_MAC_B_PR_SSID_OFFSET], + roam_ind->beaconProbeRespLength - + SIR_MAC_HDR_LEN_3A) != QDF_STATUS_SUCCESS || + !parsed_frm->ssidPresent) { + pe_err("Parse error Beacon, length: %d", + roam_ind->beaconProbeRespLength); + return QDF_STATUS_E_FAILURE; + } + } else { + if (sir_convert_probe_frame2_struct(mac, + &bcn_prb_ptr[SIR_MAC_HDR_LEN_3A], + roam_ind->beaconProbeRespLength - + SIR_MAC_HDR_LEN_3A, parsed_frm) != + QDF_STATUS_SUCCESS || + !parsed_frm->ssidPresent) { + pe_err("Parse error ProbeResponse, length: %d", + roam_ind->beaconProbeRespLength); + return QDF_STATUS_E_FAILURE; + } + } + /* 24 byte MAC header and 12 byte to ssid IE */ + if (roam_ind->beaconProbeRespLength > ie_offset) { + *ie_len = roam_ind->beaconProbeRespLength - ie_offset; + *ie = qdf_mem_malloc(*ie_len); + if (!*ie) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy(*ie, bcn_prb_ptr + ie_offset, *ie_len); + pe_debug("beacon/probe Ie length: %d", *ie_len); + } + } + /* + * For probe response, unpack core parses beacon interval, capabilities, + * timestamp. For beacon IEs, these fields are not parsed. + */ + if (roam_ind->isBeacon) + sir_parse_bcn_fixed_fields(mac, parsed_frm, + &bcn_prb_ptr[SIR_MAC_HDR_LEN_3A]); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +lim_roam_fill_bss_descr(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_ind_ptr, + struct bss_description *bss_desc_ptr) +{ + uint32_t ie_len = 0; + tpSirProbeRespBeacon parsed_frm_ptr; + tpSirMacMgmtHdr mac_hdr; + uint8_t *bcn_proberesp_ptr; + QDF_STATUS status; + uint8_t *ie = NULL; + + bcn_proberesp_ptr = (uint8_t *)roam_synch_ind_ptr + + roam_synch_ind_ptr->beaconProbeRespOffset; + mac_hdr = (tpSirMacMgmtHdr)bcn_proberesp_ptr; + parsed_frm_ptr = qdf_mem_malloc(sizeof(tSirProbeRespBeacon)); + if (!parsed_frm_ptr) + return QDF_STATUS_E_NOMEM; + + if (roam_synch_ind_ptr->beaconProbeRespLength <= + SIR_MAC_HDR_LEN_3A) { + pe_err("very few bytes in synchInd beacon / probe resp frame! length: %d", + roam_synch_ind_ptr->beaconProbeRespLength); + qdf_mem_free(parsed_frm_ptr); + return QDF_STATUS_E_FAILURE; + } + pe_debug("LFR3:Beacon/Prb Rsp: %d bssid "QDF_MAC_ADDR_FMT" beacon "QDF_MAC_ADDR_FMT, + roam_synch_ind_ptr->isBeacon, + QDF_MAC_ADDR_REF(roam_synch_ind_ptr->bssid.bytes), + QDF_MAC_ADDR_REF(mac_hdr->bssId)); + + status = lim_roam_gen_beacon_descr(mac, + roam_synch_ind_ptr, + parsed_frm_ptr, + &ie, &ie_len); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Failed to parse beacon"); + qdf_mem_free(parsed_frm_ptr); + return QDF_STATUS_E_FAILURE; + } + + /* + * Length of BSS desription is without length of + * length itself and length of pointer + * that holds ieFields + * + * struct bss_description + * +--------+---------------------------------+---------------+ + * | length | other fields | pointer to IEs| + * +--------+---------------------------------+---------------+ + * ^ + * ieFields + */ + bss_desc_ptr->length = (uint16_t) (offsetof(struct bss_description, + ieFields[0]) - + sizeof(bss_desc_ptr->length) + ie_len); + + bss_desc_ptr->fProbeRsp = !roam_synch_ind_ptr->isBeacon; + bss_desc_ptr->rssi = roam_synch_ind_ptr->rssi; + /* Copy Timestamp */ + bss_desc_ptr->scansystimensec = qdf_get_monotonic_boottime_ns(); + if (parsed_frm_ptr->he_op.oper_info_6g_present) { + bss_desc_ptr->chan_freq = wlan_reg_chan_band_to_freq(mac->pdev, + parsed_frm_ptr->he_op.oper_info_6g.info.primary_ch, + BIT(REG_BAND_6G)); + } else if (parsed_frm_ptr->dsParamsPresent) { + bss_desc_ptr->chan_freq = parsed_frm_ptr->chan_freq; + } else if (parsed_frm_ptr->HTInfo.present) { + bss_desc_ptr->chan_freq = + wlan_reg_chan_to_freq(mac->pdev, + parsed_frm_ptr->HTInfo. + primaryChannel); + } else { + /* + * If DS Params or HTIE is not present in the probe resp or + * beacon, then use the channel frequency provided by firmware + * to fill the channel in the BSS descriptor.*/ + bss_desc_ptr->chan_freq = roam_synch_ind_ptr->chan_freq; + } + + bss_desc_ptr->nwType = lim_get_nw_type( + mac, + bss_desc_ptr->chan_freq, + SIR_MAC_MGMT_FRAME, + parsed_frm_ptr); + + bss_desc_ptr->sinr = 0; + bss_desc_ptr->beaconInterval = parsed_frm_ptr->beaconInterval; + bss_desc_ptr->timeStamp[0] = parsed_frm_ptr->timeStamp[0]; + bss_desc_ptr->timeStamp[1] = parsed_frm_ptr->timeStamp[1]; + qdf_mem_copy(&bss_desc_ptr->capabilityInfo, + &bcn_proberesp_ptr[SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_CAPAB_OFFSET], 2); + + qdf_mem_copy((uint8_t *) &bss_desc_ptr->bssId, + (uint8_t *)roam_synch_ind_ptr->bssid.bytes, + sizeof(tSirMacAddr)); + + qdf_mem_copy((uint8_t *)&bss_desc_ptr->seq_ctrl, + (uint8_t *)&mac_hdr->seqControl, + sizeof(tSirMacSeqCtl)); + + bss_desc_ptr->received_time = + (uint64_t)qdf_mc_timer_get_system_time(); + if (parsed_frm_ptr->mdiePresent) { + bss_desc_ptr->mdiePresent = parsed_frm_ptr->mdiePresent; + qdf_mem_copy((uint8_t *)bss_desc_ptr->mdie, + (uint8_t *)parsed_frm_ptr->mdie, + SIR_MDIE_SIZE); + } + pe_debug("chan: %d rssi: %d ie_len %d", + bss_desc_ptr->chan_freq, + bss_desc_ptr->rssi, ie_len); + if (ie_len) { + qdf_mem_copy(&bss_desc_ptr->ieFields, + ie, ie_len); + qdf_mem_free(ie); + } + qdf_mem_free(parsed_frm_ptr); + return QDF_STATUS_SUCCESS; +} + +#if defined(WLAN_FEATURE_FILS_SK) +/** + * lim_copy_and_free_hlp_data_from_session - Copy HLP info + * @session_ptr: PE session + * @roam_sync_ind_ptr: Roam Synch Indication pointer + * + * This API is used to copy the parsed HLP info from PE session + * to roam synch indication data. THe HLP info is expected to be + * parsed/stored in PE session already from assoc IE's received + * from fw as part of Roam Synch Indication. + * + * Return: None + */ +static void +lim_copy_and_free_hlp_data_from_session(struct pe_session *session_ptr, + struct roam_offload_synch_ind + *roam_sync_ind_ptr) +{ + if (session_ptr->fils_info->hlp_data && + session_ptr->fils_info->hlp_data_len) { + cds_copy_hlp_info(&session_ptr->fils_info->dst_mac, + &session_ptr->fils_info->src_mac, + session_ptr->fils_info->hlp_data_len, + session_ptr->fils_info->hlp_data, + &roam_sync_ind_ptr->dst_mac, + &roam_sync_ind_ptr->src_mac, + &roam_sync_ind_ptr->hlp_data_len, + roam_sync_ind_ptr->hlp_data); + + qdf_mem_free(session_ptr->fils_info->hlp_data); + session_ptr->fils_info->hlp_data = NULL; + session_ptr->fils_info->hlp_data_len = 0; + } +} +#else +static inline void +lim_copy_and_free_hlp_data_from_session(struct pe_session *session_ptr, + struct roam_offload_synch_ind + *roam_sync_ind_ptr) +{} +#endif + +static +uint8_t *lim_process_rmf_disconnect_frame(struct mac_context *mac, + struct pe_session *session, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len, + uint16_t *extracted_length) +{ + struct wlan_frame_hdr *mac_hdr; + uint8_t mic_len, hdr_len, pdev_id; + uint8_t *orig_ptr, *efrm; + int32_t mgmtcipherset; + uint32_t mmie_len; + QDF_STATUS status; + + mac_hdr = (struct wlan_frame_hdr *)deauth_disassoc_frame; + orig_ptr = (uint8_t *)mac_hdr; + + if (mac_hdr->i_fc[1] & IEEE80211_FC1_WEP) { + if (QDF_IS_ADDR_BROADCAST(mac_hdr->i_addr1) || + IEEE80211_IS_MULTICAST(mac_hdr->i_addr1)) { + pe_err("Encrypted BC/MC frame dropping the frame"); + *extracted_length = 0; + return NULL; + } + + pdev_id = wlan_objmgr_pdev_get_pdev_id(mac->pdev); + status = mlme_get_peer_mic_len(mac->psoc, pdev_id, + mac_hdr->i_addr2, &mic_len, + &hdr_len); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Failed to get mic hdr and length"); + *extracted_length = 0; + return NULL; + } + + if (deauth_disassoc_frame_len < + (sizeof(*mac_hdr) + hdr_len + mic_len)) { + pe_err("Frame len less than expected %d", + deauth_disassoc_frame_len); + *extracted_length = 0; + return NULL; + } + + /* + * Strip the privacy headers and trailer + * for the received deauth/disassoc frame + */ + qdf_mem_move(orig_ptr + hdr_len, mac_hdr, + sizeof(*mac_hdr)); + *extracted_length = deauth_disassoc_frame_len - + (hdr_len + mic_len); + return orig_ptr + hdr_len; + } + + if (!(QDF_IS_ADDR_BROADCAST(mac_hdr->i_addr1) || + IEEE80211_IS_MULTICAST(mac_hdr->i_addr1))) { + pe_err("Rx unprotected unicast mgmt frame"); + *extracted_length = 0; + return NULL; + } + + mgmtcipherset = wlan_crypto_get_param(session->vdev, + WLAN_CRYPTO_PARAM_MGMT_CIPHER); + if (mgmtcipherset < 0) { + pe_err("Invalid mgmt cipher"); + *extracted_length = 0; + return NULL; + } + + mmie_len = (mgmtcipherset & (1 << WLAN_CRYPTO_CIPHER_AES_CMAC) ? + cds_get_mmie_size() : cds_get_gmac_mmie_size()); + + efrm = orig_ptr + deauth_disassoc_frame_len; + if (!mac->pmf_offload && + !wlan_crypto_is_mmie_valid(session->vdev, orig_ptr, efrm)) { + pe_err("Invalid MMIE"); + *extracted_length = 0; + return NULL; + } + + *extracted_length = deauth_disassoc_frame_len - mmie_len; + + return deauth_disassoc_frame; +} + +QDF_STATUS +pe_disconnect_callback(struct mac_context *mac, uint8_t vdev_id, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len, + uint16_t reason_code) +{ + struct pe_session *session; + uint8_t *extracted_frm = NULL; + uint16_t extracted_frm_len; + bool is_pmf_connection; + + session = pe_find_session_by_vdev_id(mac, vdev_id); + if (!session) { + pe_err("LFR3: Vdev %d doesn't exist", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + if (!((session->limMlmState == eLIM_MLM_LINK_ESTABLISHED_STATE) && + (session->limSmeState != eLIM_SME_WT_DISASSOC_STATE) && + (session->limSmeState != eLIM_SME_WT_DEAUTH_STATE))) { + pe_info("Cannot handle in mlmstate %d sme state %d as vdev_id:%d is not in connected state", + session->limMlmState, session->limSmeState, vdev_id); + return QDF_STATUS_SUCCESS; + } + + if (!(deauth_disassoc_frame || + deauth_disassoc_frame_len > SIR_MAC_MIN_IE_LEN)) + goto end; + + /* + * Use vdev pmf status instead of peer pmf capability as + * the firmware might roam to new AP in powersave case and + * roam synch can come before emergency deauth event. + * In that case, get peer will fail and reason code received + * from the WMI_ROAM_EVENTID will be sent to upper layers. + */ + is_pmf_connection = lim_get_vdev_rmf_capable(mac, session); + if (is_pmf_connection) { + extracted_frm = lim_process_rmf_disconnect_frame( + mac, session, + deauth_disassoc_frame, + deauth_disassoc_frame_len, + &extracted_frm_len); + if (!extracted_frm) { + pe_err("PMF frame validation failed"); + goto end; + } + } else { + extracted_frm = deauth_disassoc_frame; + extracted_frm_len = deauth_disassoc_frame_len; + } + + lim_extract_ies_from_deauth_disassoc(session, extracted_frm, + extracted_frm_len); + + reason_code = sir_read_u16(extracted_frm + + sizeof(struct wlan_frame_hdr)); +end: + lim_tear_down_link_with_ap(mac, session->peSessionId, + reason_code, + eLIM_PEER_ENTITY_DEAUTH); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_FILS_SK +static void +lim_fill_fils_ft(struct pe_session *src_session, + struct pe_session *dst_session) +{ + if (src_session->fils_info && + src_session->fils_info->fils_ft_len) { + dst_session->fils_info->fils_ft_len = + src_session->fils_info->fils_ft_len; + qdf_mem_copy(dst_session->fils_info->fils_ft, + src_session->fils_info->fils_ft, + src_session->fils_info->fils_ft_len); + } +} +#else +static inline void +lim_fill_fils_ft(struct pe_session *src_session, + struct pe_session *dst_session) +{} +#endif + +/** + * lim_check_ft_initial_im_association() - To check FT initial mobility(im) + * association + * @roam_synch: A pointer to roam sync ind structure + * @session_entry: pe session + * + * This function is to check ft_initial_im_association. + * + * Return: None + */ +void +lim_check_ft_initial_im_association(struct roam_offload_synch_ind *roam_synch, + struct pe_session *session_entry) +{ + tpSirMacMgmtHdr hdr; + uint8_t *assoc_req_ptr; + + assoc_req_ptr = (uint8_t *) roam_synch + roam_synch->reassoc_req_offset; + hdr = (tpSirMacMgmtHdr) assoc_req_ptr; + + if (hdr->fc.type == SIR_MAC_MGMT_FRAME && + hdr->fc.subType == SIR_MAC_MGMT_ASSOC_REQ && + session_entry->is11Rconnection) { + pe_debug("Frame subtype: %d and connection is %d", + hdr->fc.subType, session_entry->is11Rconnection); + roam_synch->is_ft_im_roam = true; + } +} + +QDF_STATUS +pe_roam_synch_callback(struct mac_context *mac_ctx, + struct roam_offload_synch_ind *roam_sync_ind_ptr, + struct bss_description *bss_desc, + enum sir_roam_op_code reason) +{ + struct pe_session *session_ptr; + struct pe_session *ft_session_ptr; + uint8_t session_id; + tpDphHashNode curr_sta_ds; + uint16_t aid; + struct bss_params *add_bss_params; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint16_t join_rsp_len; + + if (!roam_sync_ind_ptr) { + pe_err("LFR3:roam_sync_ind_ptr is NULL"); + return status; + } + session_ptr = pe_find_session_by_vdev_id(mac_ctx, + roam_sync_ind_ptr->roamed_vdev_id); + if (!session_ptr) { + pe_err("LFR3:Unable to find session"); + return status; + } + + if (!LIM_IS_STA_ROLE(session_ptr)) { + pe_err("LFR3:session is not in STA mode"); + return status; + } + + pe_debug("LFR3: PE callback reason: %d", reason); + switch (reason) { + case SIR_ROAMING_START: + session_ptr->fw_roaming_started = true; + return QDF_STATUS_SUCCESS; + case SIR_ROAMING_ABORT: + session_ptr->fw_roaming_started = false; + /* + * If there was a disassoc or deauth that was received + * during roaming and it was not honored, then we have + * to internally initiate a disconnect because with + * ROAM_ABORT we come back to original AP. + */ + if (session_ptr->recvd_deauth_while_roaming) + lim_perform_deauth(mac_ctx, session_ptr, + session_ptr->deauth_disassoc_rc, + session_ptr->bssId, 0); + if (session_ptr->recvd_disassoc_while_roaming) { + lim_disassoc_tdls_peers(mac_ctx, session_ptr, + session_ptr->bssId); + lim_perform_disassoc(mac_ctx, 0, + session_ptr->deauth_disassoc_rc, + session_ptr, session_ptr->bssId); + } + return QDF_STATUS_SUCCESS; + case SIR_ROAM_SYNCH_PROPAGATION: + session_ptr->fw_roaming_started = false; + break; + default: + return status; + } + + pe_debug("LFR3:Received ROAM_OFFLOAD_SYNCH_IND bssid "QDF_MAC_ADDR_FMT" auth: %d vdevId: %d", + QDF_MAC_ADDR_REF(roam_sync_ind_ptr->bssid.bytes), + roam_sync_ind_ptr->authStatus, + roam_sync_ind_ptr->roamed_vdev_id); + + /* + * If deauth from AP already in progress, ignore Roam Synch Indication + * from firmware. + */ + if (session_ptr->limSmeState != eLIM_SME_LINK_EST_STATE) { + pe_err("LFR3: Not in Link est state"); + return status; + } + status = lim_roam_fill_bss_descr(mac_ctx, roam_sync_ind_ptr, bss_desc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("LFR3:Failed to fill Bss Descr"); + return status; + } + status = QDF_STATUS_E_FAILURE; + ft_session_ptr = pe_create_session(mac_ctx, bss_desc->bssId, + &session_id, mac_ctx->lim.maxStation, + session_ptr->bssType, + session_ptr->vdev_id, + session_ptr->opmode); + if (!ft_session_ptr) { + pe_err("LFR3:Cannot create PE Session"); + lim_print_mac_addr(mac_ctx, bss_desc->bssId, LOGE); + return status; + } + /* Update the beacon/probe filter in mac_ctx */ + lim_set_bcn_probe_filter(mac_ctx, ft_session_ptr, NULL, 0); + + sir_copy_mac_addr(ft_session_ptr->self_mac_addr, + session_ptr->self_mac_addr); + sir_copy_mac_addr(roam_sync_ind_ptr->self_mac.bytes, + session_ptr->self_mac_addr); + sir_copy_mac_addr(ft_session_ptr->limReAssocbssId, bss_desc->bssId); + session_ptr->bRoamSynchInProgress = true; + ft_session_ptr->bRoamSynchInProgress = true; + ft_session_ptr->limSystemRole = eLIM_STA_ROLE; + sir_copy_mac_addr(session_ptr->limReAssocbssId, bss_desc->bssId); + ft_session_ptr->csaOffloadEnable = session_ptr->csaOffloadEnable; + + /* Next routine will update nss and vdev_nss with AP's capabilities */ + lim_fill_ft_session(mac_ctx, bss_desc, ft_session_ptr, + session_ptr, roam_sync_ind_ptr->phy_mode); + pe_set_rmf_caps(mac_ctx, ft_session_ptr, roam_sync_ind_ptr); + /* Next routine may update nss based on dot11Mode */ + lim_ft_prepare_add_bss_req(mac_ctx, ft_session_ptr, bss_desc); + if (session_ptr->is11Rconnection) + lim_fill_fils_ft(session_ptr, ft_session_ptr); + + roam_sync_ind_ptr->add_bss_params = + (struct bss_params *) ft_session_ptr->ftPEContext.pAddBssReq; + add_bss_params = ft_session_ptr->ftPEContext.pAddBssReq; + lim_delete_tdls_peers(mac_ctx, session_ptr); + curr_sta_ds = dph_lookup_hash_entry(mac_ctx, session_ptr->bssId, &aid, + &session_ptr->dph.dphHashTable); + if (!curr_sta_ds) { + pe_err("LFR3:failed to lookup hash entry"); + ft_session_ptr->bRoamSynchInProgress = false; + return status; + } + session_ptr->limSmeState = eLIM_SME_IDLE_STATE; + lim_cleanup_rx_path(mac_ctx, curr_sta_ds, session_ptr); + lim_delete_dph_hash_entry(mac_ctx, curr_sta_ds->staAddr, aid, + session_ptr); + pe_delete_session(mac_ctx, session_ptr); + session_ptr = NULL; + curr_sta_ds = dph_add_hash_entry(mac_ctx, + roam_sync_ind_ptr->bssid.bytes, + DPH_STA_HASH_INDEX_PEER, + &ft_session_ptr->dph.dphHashTable); + if (!curr_sta_ds) { + pe_err("LFR3:failed to add hash entry for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(add_bss_params->staContext.staMac)); + ft_session_ptr->bRoamSynchInProgress = false; + return status; + } + + mac_ctx->roam.reassocRespLen = roam_sync_ind_ptr->reassocRespLength; + mac_ctx->roam.pReassocResp = + qdf_mem_malloc(mac_ctx->roam.reassocRespLen); + if (!mac_ctx->roam.pReassocResp) { + ft_session_ptr->bRoamSynchInProgress = false; + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(mac_ctx->roam.pReassocResp, + (uint8_t *)roam_sync_ind_ptr + + roam_sync_ind_ptr->reassocRespOffset, + mac_ctx->roam.reassocRespLen); + + ft_session_ptr->bRoamSynchInProgress = true; + + if (roam_sync_ind_ptr->authStatus == + CSR_ROAM_AUTH_STATUS_AUTHENTICATED) { + ft_session_ptr->is_key_installed = true; + curr_sta_ds->is_key_installed = true; + } + + lim_process_assoc_rsp_frame(mac_ctx, mac_ctx->roam.pReassocResp, + LIM_REASSOC, ft_session_ptr); + + lim_check_ft_initial_im_association(roam_sync_ind_ptr, ft_session_ptr); + + lim_copy_and_free_hlp_data_from_session(ft_session_ptr, + roam_sync_ind_ptr); + + roam_sync_ind_ptr->aid = ft_session_ptr->limAID; + curr_sta_ds->mlmStaContext.mlmState = eLIM_MLM_LINK_ESTABLISHED_STATE; + curr_sta_ds->nss = ft_session_ptr->nss; + roam_sync_ind_ptr->nss = ft_session_ptr->nss; + ft_session_ptr->limMlmState = eLIM_MLM_LINK_ESTABLISHED_STATE; + ft_session_ptr->limPrevMlmState = ft_session_ptr->limMlmState; + lim_init_tdls_data(mac_ctx, ft_session_ptr); + join_rsp_len = ft_session_ptr->RICDataLen + + sizeof(struct join_rsp) - sizeof(uint8_t); + +#ifdef FEATURE_WLAN_ESE + join_rsp_len += ft_session_ptr->tspecLen; + pe_debug("LFR3: tspecLen: %d", ft_session_ptr->tspecLen); +#endif + + roam_sync_ind_ptr->join_rsp = qdf_mem_malloc(join_rsp_len); + if (!roam_sync_ind_ptr->join_rsp) { + ft_session_ptr->bRoamSynchInProgress = false; + if (mac_ctx->roam.pReassocResp) + qdf_mem_free(mac_ctx->roam.pReassocResp); + mac_ctx->roam.pReassocResp = NULL; + return QDF_STATUS_E_NOMEM; + } + + pe_debug("LFR3: Session RicLength: %d", ft_session_ptr->RICDataLen); + if (ft_session_ptr->ricData) { + roam_sync_ind_ptr->join_rsp->parsedRicRspLen = + ft_session_ptr->RICDataLen; + qdf_mem_copy(roam_sync_ind_ptr->join_rsp->frames, + ft_session_ptr->ricData, + roam_sync_ind_ptr->join_rsp->parsedRicRspLen); + qdf_mem_free(ft_session_ptr->ricData); + ft_session_ptr->ricData = NULL; + ft_session_ptr->RICDataLen = 0; + } + +#ifdef FEATURE_WLAN_ESE + if (ft_session_ptr->tspecIes) { + roam_sync_ind_ptr->join_rsp->tspecIeLen = + ft_session_ptr->tspecLen; + qdf_mem_copy(roam_sync_ind_ptr->join_rsp->frames + + roam_sync_ind_ptr->join_rsp->parsedRicRspLen, + ft_session_ptr->tspecIes, + roam_sync_ind_ptr->join_rsp->tspecIeLen); + qdf_mem_free(ft_session_ptr->tspecIes); + ft_session_ptr->tspecIes = NULL; + ft_session_ptr->tspecLen = 0; + } +#endif + + roam_sync_ind_ptr->join_rsp->vht_channel_width = + ft_session_ptr->ch_width; + roam_sync_ind_ptr->join_rsp->timingMeasCap = curr_sta_ds->timingMeasCap; + roam_sync_ind_ptr->join_rsp->nss = curr_sta_ds->nss; + roam_sync_ind_ptr->join_rsp->max_rate_flags = + lim_get_max_rate_flags(mac_ctx, curr_sta_ds); + lim_set_tdls_flags(roam_sync_ind_ptr, ft_session_ptr); + roam_sync_ind_ptr->join_rsp->aid = ft_session_ptr->limAID; + lim_fill_join_rsp_ht_caps(ft_session_ptr, roam_sync_ind_ptr->join_rsp); + ft_session_ptr->limSmeState = eLIM_SME_LINK_EST_STATE; + ft_session_ptr->limPrevSmeState = ft_session_ptr->limSmeState; + ft_session_ptr->bRoamSynchInProgress = false; + if (mac_ctx->roam.pReassocResp) + qdf_mem_free(mac_ctx->roam.pReassocResp); + mac_ctx->roam.pReassocResp = NULL; + + + return QDF_STATUS_SUCCESS; +} +#endif + +static bool lim_is_beacon_miss_scenario(struct mac_context *mac, + uint8_t *pRxPacketInfo) +{ + tpSirMacMgmtHdr pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + uint8_t sessionId; + struct pe_session *pe_session = + pe_find_session_by_bssid(mac, pHdr->bssId, &sessionId); + + if (pe_session && pe_session->pmmOffloadInfo.bcnmiss) + return true; + return false; +} + +/** ----------------------------------------------------------------- + \brief lim_is_pkt_candidate_for_drop() - decides whether to drop the frame or not + + This function is called before enqueuing the frame to PE queue for further processing. + This prevents unnecessary frames getting into PE Queue and drops them right away. + Frames will be droped in the following scenarios: + + - In Scan State, drop the frames which are not marked as scan frames + - In non-Scan state, drop the frames which are marked as scan frames. + - Drop INFRA Beacons and Probe Responses in IBSS Mode + - Drop the Probe Request in IBSS mode, if STA did not send out the last beacon + + \param mac - global mac structure + \return - none + \sa + ----------------------------------------------------------------- */ + +tMgmtFrmDropReason lim_is_pkt_candidate_for_drop(struct mac_context *mac, + uint8_t *pRxPacketInfo, + uint32_t subType) +{ + uint32_t framelen; + uint8_t *pBody; + tSirMacCapabilityInfo capabilityInfo; + tpSirMacMgmtHdr pHdr = NULL; + struct pe_session *pe_session = NULL; + uint8_t sessionId; + + /* + * + * In scan mode, drop only Beacon/Probe Response which are NOT marked as scan-frames. + * In non-scan mode, drop only Beacon/Probe Response which are marked as scan frames. + * Allow other mgmt frames, they must be from our own AP, as we don't allow + * other than beacons or probe responses in scan state. + */ + if ((subType == SIR_MAC_MGMT_BEACON) || + (subType == SIR_MAC_MGMT_PROBE_RSP)) { + if (lim_is_beacon_miss_scenario(mac, pRxPacketInfo)) { + MTRACE(mac_trace(mac, TRACE_CODE_INFO_LOG, 0, + eLOG_NODROP_MISSED_BEACON_SCENARIO)); + return eMGMT_DROP_NO_DROP; + } + + framelen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + /* drop the frame if length is less than 12 */ + if (framelen < LIM_MIN_BCN_PR_LENGTH) + return eMGMT_DROP_INVALID_SIZE; + + *((uint16_t *) &capabilityInfo) = + sir_read_u16(pBody + LIM_BCN_PR_CAPABILITY_OFFSET); + + /* Note sure if this is sufficient, basically this condition allows all probe responses and + * beacons from an infrastructure network + */ + if (!capabilityInfo.ibss) + return eMGMT_DROP_NO_DROP; + + /* Drop INFRA Beacons and Probe Responses in IBSS Mode */ + /* This can be enhanced to even check the SSID before deciding to enque the frame. */ + if (capabilityInfo.ess) + return eMGMT_DROP_INFRA_BCN_IN_IBSS; + + } else if ((subType == SIR_MAC_MGMT_PROBE_REQ) && + (!WMA_GET_RX_BEACON_SENT(pRxPacketInfo))) { + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pe_session = pe_find_session_by_bssid(mac, + pHdr->bssId, + &sessionId); + if ((pe_session && !LIM_IS_IBSS_ROLE(pe_session)) || + (!pe_session)) + return eMGMT_DROP_NO_DROP; + + /* Drop the Probe Request in IBSS mode, if STA did not send out the last beacon */ + /* In IBSS, the node which sends out the beacon, is supposed to respond to ProbeReq */ + return eMGMT_DROP_NOT_LAST_IBSS_BCN; + } else if (subType == SIR_MAC_MGMT_AUTH) { + uint16_t curr_seq_num = 0; + struct tLimPreAuthNode *auth_node; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pe_session = pe_find_session_by_bssid(mac, pHdr->bssId, + &sessionId); + if (!pe_session) + return eMGMT_DROP_NO_DROP; + + curr_seq_num = ((pHdr->seqControl.seqNumHi << 4) | + (pHdr->seqControl.seqNumLo)); + auth_node = lim_search_pre_auth_list(mac, pHdr->sa); + if (auth_node && pHdr->fc.retry && + (auth_node->seq_num == curr_seq_num)) { + pe_err_rl("auth frame, seq num: %d is already processed, drop it", + curr_seq_num); + return eMGMT_DROP_DUPLICATE_AUTH_FRAME; + } + } else if ((subType == SIR_MAC_MGMT_ASSOC_REQ) || + (subType == SIR_MAC_MGMT_DISASSOC) || + (subType == SIR_MAC_MGMT_DEAUTH)) { + struct peer_mlme_priv_obj *peer_priv; + struct wlan_objmgr_peer *peer; + qdf_time_t *timestamp; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pe_session = pe_find_session_by_bssid(mac, pHdr->bssId, + &sessionId); + if (!pe_session) + return eMGMT_DROP_SPURIOUS_FRAME; + + peer = wlan_objmgr_get_peer_by_mac(mac->psoc, + pHdr->sa, + WLAN_LEGACY_MAC_ID); + if (!peer) { + if (subType == SIR_MAC_MGMT_ASSOC_REQ) + return eMGMT_DROP_NO_DROP; + + return eMGMT_DROP_SPURIOUS_FRAME; + } + + peer_priv = wlan_objmgr_peer_get_comp_private_obj(peer, + WLAN_UMAC_COMP_MLME); + if (!peer_priv) { + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID); + if (subType == SIR_MAC_MGMT_ASSOC_REQ) + return eMGMT_DROP_NO_DROP; + + return eMGMT_DROP_SPURIOUS_FRAME; + } + + if (subType == SIR_MAC_MGMT_ASSOC_REQ) + timestamp = + &peer_priv->last_assoc_received_time; + else + timestamp = + &peer_priv->last_disassoc_deauth_received_time; + + if (*timestamp > 0 && + qdf_system_time_before(qdf_get_system_timestamp(), + *timestamp + + LIM_DOS_PROTECTION_TIME)) { + pe_debug_rl(FL("Dropping subtype 0x%x frame. %s %d ms %s %d ms"), + subType, "It is received after", + (int)(qdf_get_system_timestamp() - *timestamp), + "of last frame. Allow it only after", + LIM_DOS_PROTECTION_TIME); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID); + return eMGMT_DROP_EXCESSIVE_MGMT_FRAME; + } + + *timestamp = qdf_get_system_timestamp(); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID); + + } + + return eMGMT_DROP_NO_DROP; +} + +void lim_update_lost_link_info(struct mac_context *mac, struct pe_session *session, + int32_t rssi) +{ + struct sir_lost_link_info *lost_link_info; + struct scheduler_msg mmh_msg = {0}; + + if ((!mac) || (!session)) { + pe_err("parameter NULL"); + return; + } + if (!LIM_IS_STA_ROLE(session)) + return; + + lost_link_info = qdf_mem_malloc(sizeof(*lost_link_info)); + if (!lost_link_info) + return; + + lost_link_info->vdev_id = session->smeSessionId; + lost_link_info->rssi = rssi; + mmh_msg.type = eWNI_SME_LOST_LINK_INFO_IND; + mmh_msg.bodyptr = lost_link_info; + mmh_msg.bodyval = 0; + pe_debug("post eWNI_SME_LOST_LINK_INFO_IND, bss_idx: %d rssi: %d", + lost_link_info->vdev_id, lost_link_info->rssi); + + lim_sys_process_mmh_msg_api(mac, &mmh_msg); +} + +/** + * lim_mon_init_session() - create PE session for monitor mode operation + * @mac_ptr: mac pointer + * @msg: Pointer to struct sir_create_session type. + * + * Return: NONE + */ +void lim_mon_init_session(struct mac_context *mac_ptr, + struct sir_create_session *msg) +{ + struct pe_session *psession_entry; + uint8_t session_id; + + psession_entry = pe_create_session(mac_ptr, msg->bss_id.bytes, + &session_id, + mac_ptr->lim.maxStation, + eSIR_MONITOR_MODE, + msg->vdev_id, + QDF_MONITOR_MODE); + if (!psession_entry) { + pe_err("Monitor mode: Session Can not be created"); + lim_print_mac_addr(mac_ptr, msg->bss_id.bytes, LOGE); + return; + } + psession_entry->vhtCapability = 1; +} + +void lim_mon_deinit_session(struct mac_context *mac_ptr, + struct sir_delete_session *msg) +{ + struct pe_session *session; + + session = pe_find_session_by_session_id(mac_ptr, msg->vdev_id); + + if (session && session->bssType == eSIR_MONITOR_MODE) + pe_delete_session(mac_ptr, session); +} + +/** + * lim_update_ext_cap_ie() - Update Extended capabilities IE(if present) + * with capabilities of Fine Time measurements(FTM) if set in driver + * + * @mac_ctx: Pointer to Global MAC structure + * @ie_data: Default Scan IE data + * @local_ie_buf: Local Scan IE data + * @local_ie_len: Pointer to length of @ie_data + * @session: Pointer to pe session + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_update_ext_cap_ie(struct mac_context *mac_ctx, uint8_t *ie_data, + uint8_t *local_ie_buf, uint16_t *local_ie_len, + struct pe_session *session) +{ + uint32_t dot11mode; + bool vht_enabled = false; + tDot11fIEExtCap default_scan_ext_cap = {0}, driver_ext_cap = {0}; + QDF_STATUS status; + + status = lim_strip_extcap_update_struct(mac_ctx, ie_data, + local_ie_len, &default_scan_ext_cap); + if (QDF_STATUS_SUCCESS != status) { + pe_err("Strip ext cap fails %d", status); + return QDF_STATUS_E_FAILURE; + } + + if ((*local_ie_len) > (MAX_DEFAULT_SCAN_IE_LEN - EXT_CAP_IE_HDR_LEN)) { + pe_err("Invalid Scan IE length"); + return QDF_STATUS_E_FAILURE; + } + /* copy ie prior to ext cap to local buffer */ + qdf_mem_copy(local_ie_buf, ie_data, (*local_ie_len)); + + /* from here ext cap ie starts, set EID */ + local_ie_buf[*local_ie_len] = DOT11F_EID_EXTCAP; + + dot11mode = mac_ctx->mlme_cfg->dot11_mode.dot11_mode; + if (IS_DOT11_MODE_VHT(dot11mode)) + vht_enabled = true; + + status = populate_dot11f_ext_cap(mac_ctx, vht_enabled, + &driver_ext_cap, NULL); + if (QDF_STATUS_SUCCESS != status) { + pe_err("Failed %d to create ext cap IE. Use default value instead", + status); + local_ie_buf[*local_ie_len + 1] = DOT11F_IE_EXTCAP_MAX_LEN; + + if ((*local_ie_len) > (MAX_DEFAULT_SCAN_IE_LEN - + (DOT11F_IE_EXTCAP_MAX_LEN + EXT_CAP_IE_HDR_LEN))) { + pe_err("Invalid Scan IE length"); + return QDF_STATUS_E_FAILURE; + } + (*local_ie_len) += EXT_CAP_IE_HDR_LEN; + qdf_mem_copy(local_ie_buf + (*local_ie_len), + default_scan_ext_cap.bytes, + DOT11F_IE_EXTCAP_MAX_LEN); + (*local_ie_len) += DOT11F_IE_EXTCAP_MAX_LEN; + return QDF_STATUS_SUCCESS; + } + lim_merge_extcap_struct(&driver_ext_cap, &default_scan_ext_cap, true); + + if (session) + populate_dot11f_twt_extended_caps(mac_ctx, session, + &driver_ext_cap); + else + pe_debug("Session NULL, cannot set TWT caps"); + + local_ie_buf[*local_ie_len + 1] = driver_ext_cap.num_bytes; + + if ((*local_ie_len) > (MAX_DEFAULT_SCAN_IE_LEN - + (EXT_CAP_IE_HDR_LEN + driver_ext_cap.num_bytes))) { + pe_err("Invalid Scan IE length"); + return QDF_STATUS_E_FAILURE; + } + (*local_ie_len) += EXT_CAP_IE_HDR_LEN; + qdf_mem_copy(local_ie_buf + (*local_ie_len), + driver_ext_cap.bytes, driver_ext_cap.num_bytes); + (*local_ie_len) += driver_ext_cap.num_bytes; + return QDF_STATUS_SUCCESS; +} + +#define LIM_RSN_OUI_SIZE 4 + +struct rsn_oui_akm_type_map { + enum ani_akm_type akm_type; + uint8_t rsn_oui[LIM_RSN_OUI_SIZE]; +}; + +static const struct rsn_oui_akm_type_map rsn_oui_akm_type_mapping_table[] = { + {ANI_AKM_TYPE_RSN, {0x00, 0x0F, 0xAC, 0x01} }, + {ANI_AKM_TYPE_RSN_PSK, {0x00, 0x0F, 0xAC, 0x02} }, + {ANI_AKM_TYPE_FT_RSN, {0x00, 0x0F, 0xAC, 0x03} }, + {ANI_AKM_TYPE_FT_RSN_PSK, {0x00, 0x0F, 0xAC, 0x04} }, + {ANI_AKM_TYPE_RSN_8021X_SHA256, {0x00, 0x0F, 0xAC, 0x05} }, + {ANI_AKM_TYPE_RSN_PSK_SHA256, {0x00, 0x0F, 0xAC, 0x06} }, +#ifdef WLAN_FEATURE_SAE + {ANI_AKM_TYPE_SAE, {0x00, 0x0F, 0xAC, 0x08} }, + {ANI_AKM_TYPE_FT_SAE, {0x00, 0x0F, 0xAC, 0x09} }, +#endif + {ANI_AKM_TYPE_SUITEB_EAP_SHA256, {0x00, 0x0F, 0xAC, 0x0B} }, + {ANI_AKM_TYPE_SUITEB_EAP_SHA384, {0x00, 0x0F, 0xAC, 0x0C} }, + {ANI_AKM_TYPE_FT_SUITEB_EAP_SHA384, {0x00, 0x0F, 0xAC, 0x0D} }, + {ANI_AKM_TYPE_FILS_SHA256, {0x00, 0x0F, 0xAC, 0x0E} }, + {ANI_AKM_TYPE_FILS_SHA384, {0x00, 0x0F, 0xAC, 0x0F} }, + {ANI_AKM_TYPE_FT_FILS_SHA256, {0x00, 0x0F, 0xAC, 0x10} }, + {ANI_AKM_TYPE_FT_FILS_SHA384, {0x00, 0x0F, 0xAC, 0x11} }, + {ANI_AKM_TYPE_OWE, {0x00, 0x0F, 0xAC, 0x12} }, +#ifdef FEATURE_WLAN_ESE + {ANI_AKM_TYPE_CCKM, {0x00, 0x40, 0x96, 0x00} }, +#endif + {ANI_AKM_TYPE_OSEN, {0x50, 0x6F, 0x9A, 0x01} }, + {ANI_AKM_TYPE_DPP_RSN, {0x50, 0x6F, 0x9A, 0x02} }, + {ANI_AKM_TYPE_WPA, {0x00, 0x50, 0xF2, 0x01} }, + {ANI_AKM_TYPE_WPA_PSK, {0x00, 0x50, 0xF2, 0x02} }, + /* Add akm type above here */ + {ANI_AKM_TYPE_UNKNOWN, {0} }, +}; + +enum ani_akm_type lim_translate_rsn_oui_to_akm_type(uint8_t auth_suite[4]) +{ + const struct rsn_oui_akm_type_map *map; + enum ani_akm_type akm_type; + + map = rsn_oui_akm_type_mapping_table; + while (true) { + akm_type = map->akm_type; + if ((akm_type == ANI_AKM_TYPE_UNKNOWN) || + (qdf_mem_cmp(auth_suite, map->rsn_oui, 4) == 0)) + break; + map++; + } + + pe_debug("akm_type: %d", akm_type); + + return akm_type; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_assoc_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_assoc_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..9dbb4a89fd62e2e97e8a72a8e8803ed5c0ad8e62 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_assoc_utils.c @@ -0,0 +1,4385 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_assoc_utils.cc contains the utility functions + * LIM uses while processing (Re) Association messages. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * 05/26/10 js WPA handling in (Re)Assoc frames + * + */ + +#include "cds_api.h" +#include "ani_global.h" +#include "wni_api.h" +#include "sir_common.h" + +#include "wni_cfg.h" +#include "cfg_ucfg_api.h" + +#include "sch_api.h" +#include "utils_api.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_admit_control.h" +#include "lim_send_messages.h" +#include "lim_ibss_peer_mgmt.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "lim_process_fils.h" + +#include "qdf_types.h" +#include "wma_types.h" +#include "lim_types.h" +#include "wlan_utility.h" +#include "wlan_mlme_api.h" +#include "wma.h" + +#ifdef FEATURE_WLAN_TDLS +#define IS_TDLS_PEER(type) ((type) == STA_ENTRY_TDLS_PEER) +#else +#define IS_TDLS_PEER(type) 0 +#endif + +/** + * lim_cmp_ssid() - utility function to compare SSIDs + * @rx_ssid: Received SSID + * @session_entry: Session entry + * + * This function is called in various places within LIM code + * to determine whether received SSID is same as SSID in use. + * + * Return: zero if SSID matched, non-zero otherwise. + */ +uint32_t lim_cmp_ssid(tSirMacSSid *rx_ssid, struct pe_session *session_entry) +{ + return qdf_mem_cmp(rx_ssid, &session_entry->ssId, + session_entry->ssId.length); +} + +/** + * lim_compare_capabilities() + * + ***FUNCTION: + * This function is called during Association/Reassociation + * frame handling to determine whether received capabilities + * match with local capabilities or not. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param pAssocReq - Pointer to received Assoc Req frame + * @param pLocalCapabs - Pointer to local capabilities + * + * @return status - true for Capabilitity match else false. + */ + +uint8_t +lim_compare_capabilities(struct mac_context *mac, + tSirAssocReq *pAssocReq, + tSirMacCapabilityInfo *pLocalCapabs, + struct pe_session *pe_session) +{ + if (LIM_IS_AP_ROLE(pe_session) && + (pAssocReq->capabilityInfo.ibss)) { + /* Requesting STA asserting IBSS capability. */ + pe_debug("Requesting STA asserting IBSS capability"); + return false; + } + /* Compare CF capabilities */ + if (pAssocReq->capabilityInfo.cfPollable || + pAssocReq->capabilityInfo.cfPollReq) { + /* AP does not support PCF functionality */ + pe_debug(" AP does not support PCF functionality"); + return false; + } + /* Compare short preamble capability */ + if (pAssocReq->capabilityInfo.shortPreamble && + (pAssocReq->capabilityInfo.shortPreamble != + pLocalCapabs->shortPreamble)) { + /* Allowing a STA requesting short preamble while */ + /* AP does not support it */ + } + + pe_debug("QoS in AssocReq: %d, local capabs qos: %d", + pAssocReq->capabilityInfo.qos, pLocalCapabs->qos); + + /* Compare QoS capability */ + if (pAssocReq->capabilityInfo.qos && + (pAssocReq->capabilityInfo.qos != pLocalCapabs->qos)) + pe_debug("Received unmatched QOS but cfg to suppress - continuing"); + + /* + * If AP supports shortSlot and if apple user has + * enforced association only from shortSlot station, + * then AP must reject any station that does not support + * shortSlot + */ + if (LIM_IS_AP_ROLE(pe_session) && + (pLocalCapabs->shortSlotTime == 1)) { + if (mac->mlme_cfg->feature_flags.accept_short_slot_assoc) { + if (pAssocReq->capabilityInfo.shortSlotTime != + pLocalCapabs->shortSlotTime) { + pe_err("AP rejects association as station doesn't support shortslot time"); + return false; + } + return false; + } + } + + return true; +} /****** end lim_compare_capabilities() ******/ + +/** + * lim_check_rx_basic_rates() + * + ***FUNCTION: + * This function is called during Association/Reassociation + * frame handling to determine whether received rates in + * Assoc/Reassoc request frames include all BSS basic rates + * or not. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param rxRateSet - pointer to SSID structure + * + * @return status - true if ALL BSS basic rates are present in the + * received rateset else false. + */ + +uint8_t +lim_check_rx_basic_rates(struct mac_context *mac, tSirMacRateSet rxRateSet, + struct pe_session *pe_session) +{ + tSirMacRateSet *pRateSet, basicRate; + uint8_t i, j, k, match; + + pRateSet = qdf_mem_malloc(sizeof(tSirMacRateSet)); + if (!pRateSet) + return false; + + /* Copy operational rate set from session Entry */ + qdf_mem_copy(pRateSet->rate, (pe_session->rateSet.rate), + pe_session->rateSet.numRates); + + pRateSet->numRates = pe_session->rateSet.numRates; + + /* Extract BSS basic rateset from operational rateset */ + for (i = 0, j = 0; + ((i < pRateSet->numRates) && (i < WLAN_SUPPORTED_RATES_IE_MAX_LEN)); i++) { + if ((pRateSet->rate[i] & 0x80) == 0x80) { + /* msb is set, so this is a basic rate */ + basicRate.rate[j++] = pRateSet->rate[i]; + } + } + + /* + * For each BSS basic rate, find if it is present in the + * received rateset. + */ + for (k = 0; k < j; k++) { + match = 0; + for (i = 0; + ((i < rxRateSet.numRates) + && (i < WLAN_SUPPORTED_RATES_IE_MAX_LEN)); i++) { + if ((rxRateSet.rate[i] | 0x80) == basicRate.rate[k]) + match = 1; + } + + if (!match) { + /* Free up memory allocated for rateset */ + qdf_mem_free((uint8_t *) pRateSet); + + return false; + } + } + + /* Free up memory allocated for rateset */ + qdf_mem_free((uint8_t *) pRateSet); + + return true; +} /****** end lim_check_rx_basic_rates() ******/ + +/** + * lim_check_mcs_set() + * + ***FUNCTION: + * This function is called during Association/Reassociation + * frame handling to determine whether received MCS rates in + * Assoc/Reassoc request frames includes all Basic MCS Rate Set or not. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param supportedMCSSet - pointer to Supported MCS Rate Set + * + * @return status - true if ALL MCS Basic Rate Set rates are present in the + * received rateset else false. + */ + +uint8_t lim_check_mcs_set(struct mac_context *mac, uint8_t *supportedMCSSet) +{ + uint8_t basicMCSSet[SIZE_OF_BASIC_MCS_SET] = { 0 }; + qdf_size_t cfg_len = 0; + uint8_t i; + uint8_t validBytes; + uint8_t lastByteMCSMask = 0x1f; + + cfg_len = mac->mlme_cfg->rates.basic_mcs_set.len; + if (wlan_mlme_get_cfg_str((uint8_t *)basicMCSSet, + &mac->mlme_cfg->rates.basic_mcs_set, + &cfg_len) != QDF_STATUS_SUCCESS) { + /* / Could not get Basic MCS rateset from CFG. Log error. */ + pe_err("could not retrieve Basic MCS rateset"); + return false; + } + + validBytes = VALID_MCS_SIZE / 8; + + /* check if all the Basic MCS Bits are set in supported MCS bitmap */ + for (i = 0; i < validBytes; i++) { + if ((basicMCSSet[i] & supportedMCSSet[i]) != basicMCSSet[i]) { + pe_warn("One of Basic MCS Set Rates is not supported by the Station"); + return false; + } + } + + /* check the last 5 bits of the valid MCS bitmap */ + if (((basicMCSSet[i] & lastByteMCSMask) & + (supportedMCSSet[i] & lastByteMCSMask)) != + (basicMCSSet[i] & lastByteMCSMask)) { + pe_warn("One of Basic MCS Set Rates is not supported by the Station"); + return false; + } + + return true; +} + +#define SECURITY_SUITE_TYPE_MASK 0xFF +#define SECURITY_SUITE_TYPE_WEP40 0x1 +#define SECURITY_SUITE_TYPE_TKIP 0x2 +#define SECURITY_SUITE_TYPE_CCMP 0x4 +#define SECURITY_SUITE_TYPE_WEP104 0x4 +#define SECURITY_SUITE_TYPE_GCMP 0x8 +#define SECURITY_SUITE_TYPE_GCMP_256 0x9 + +/** + * lim_cleanup_rx_path() + * + ***FUNCTION: + * This function is called to cleanup STA state at SP & RFP. + * + ***LOGIC: + * To circumvent RFP's handling of dummy packet when it does not + * have an incomplete packet for the STA to be deleted, a packet + * with 'more framgents' bit set will be queued to RFP's WQ before + * queuing 'dummy packet'. + * A 'dummy' BD is pushed into RFP's WQ with type=00, subtype=1010 + * (Disassociation frame) and routing flags in BD set to eCPU's + * Low Priority WQ. + * RFP cleans up its local context for the STA id mentioned in the + * BD and then pushes BD to eCPU's low priority WQ. + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac Pointer to Global MAC structure + * @param sta Pointer to the per STA data structure + * initialized by LIM and maintained at DPH + * + * @return None + */ + +QDF_STATUS +lim_cleanup_rx_path(struct mac_context *mac, tpDphHashNode sta, + struct pe_session *pe_session) +{ + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + + pe_debug("Cleanup Rx Path for AID: %d" + "pe_session->limSmeState: %d, mlmState: %d", + sta->assocId, pe_session->limSmeState, + sta->mlmStaContext.mlmState); + + pe_session->isCiscoVendorAP = false; + + if (mac->lim.gLimAddtsSent) { + MTRACE(mac_trace + (mac, TRACE_CODE_TIMER_DEACTIVATE, + pe_session->peSessionId, eLIM_ADDTS_RSP_TIMER)); + tx_timer_deactivate(&mac->lim.lim_timers.gLimAddtsRspTimer); + pe_debug("Reset gLimAddtsSent flag and send addts timeout to SME"); + lim_process_sme_addts_rsp_timeout(mac, + mac->lim.gLimAddtsRspTimerCount); + } + + if (sta->mlmStaContext.mlmState == eLIM_MLM_WT_ASSOC_CNF_STATE) { + lim_deactivate_and_change_per_sta_id_timer(mac, eLIM_CNF_WAIT_TIMER, + sta->assocId); + + if (!sta->mlmStaContext.updateContext) { + /** + * There is no context at Polaris to delete. + * Release our assigned AID back to the free pool + */ + if (LIM_IS_AP_ROLE(pe_session)) { + lim_del_sta(mac, sta, true, pe_session); + return retCode; + } + lim_delete_dph_hash_entry(mac, sta->staAddr, + sta->assocId, pe_session); + + return retCode; + } + } + /* delete all tspecs associated with this sta. */ + lim_admit_control_delete_sta(mac, sta->assocId); + + /** + * Make STA hash entry invalid at eCPU so that DPH + * does not process any more data packets and + * releases those BDs + */ + sta->valid = 0; + lim_send_sme_tsm_ie_ind(mac, pe_session, 0, 0, 0); + /* Any roaming related changes should be above this line */ + if (lim_is_roam_synch_in_progress(pe_session)) + return QDF_STATUS_SUCCESS; + sta->mlmStaContext.mlmState = eLIM_MLM_WT_DEL_STA_RSP_STATE; + + if (LIM_IS_STA_ROLE(pe_session)) { + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + eLIM_MLM_WT_DEL_STA_RSP_STATE)); + pe_session->limMlmState = eLIM_MLM_WT_DEL_STA_RSP_STATE; + /* Deactivating probe after heart beat timer */ + lim_deactivate_and_change_timer(mac, eLIM_PROBE_AFTER_HB_TIMER); + lim_deactivate_and_change_timer(mac, eLIM_JOIN_FAIL_TIMER); + } +#ifdef WLAN_DEBUG + /* increment a debug count */ + mac->lim.gLimNumRxCleanup++; +#endif + /* Do DEL BSS or DEL STA only if ADD BSS was success */ + if (!pe_session->add_bss_failed) { + if (pe_session->limSmeState == eLIM_SME_JOIN_FAILURE_STATE) { + retCode = + lim_del_bss(mac, sta, pe_session->vdev_id, + pe_session); + } else + retCode = lim_del_sta(mac, + sta, true, pe_session); + } + + return retCode; + +} /*** end lim_cleanup_rx_path() ***/ + +/** + * lim_send_del_sta_cnf() - Send Del sta confirmation + * @mac: Pointer to Global MAC structure + * @sta_dsaddr: sta ds address + * @staDsAssocId: sta ds association id + * @mlmStaContext: MLM station context + * @status_code: Status code + * @pe_session: Session entry + * + * This function is called to send appropriate CNF message to SME. + * + * Return: None + */ +void +lim_send_del_sta_cnf(struct mac_context *mac, struct qdf_mac_addr sta_dsaddr, + uint16_t staDsAssocId, + struct lim_sta_context mlmStaContext, + tSirResultCodes status_code, struct pe_session *pe_session) +{ + tLimMlmDisassocCnf mlmDisassocCnf; + tLimMlmDeauthCnf mlmDeauthCnf; + tLimMlmPurgeStaInd mlmPurgeStaInd; + + pe_debug("Sessionid: %d staDsAssocId: %d Trigger: %d status_code: %d sta_dsaddr: "QDF_MAC_ADDR_FMT, + pe_session->peSessionId, staDsAssocId, + mlmStaContext.cleanupTrigger, status_code, + QDF_MAC_ADDR_REF(sta_dsaddr.bytes)); + + if (LIM_IS_STA_ROLE(pe_session)) { + /* Set BSSID at CFG to null */ + tSirMacAddr nullAddr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + sir_copy_mac_addr(nullAddr, pe_session->bssId); + + /* Free up buffer allocated for JoinReq held by */ + /* MLM state machine */ + if (pe_session->pLimMlmJoinReq) { + qdf_mem_free(pe_session->pLimMlmJoinReq); + pe_session->pLimMlmJoinReq = NULL; + } + + pe_session->limAID = 0; + } + + if ((mlmStaContext.cleanupTrigger == + eLIM_HOST_DISASSOC) || + (mlmStaContext.cleanupTrigger == + eLIM_LINK_MONITORING_DISASSOC) || + (mlmStaContext.cleanupTrigger == + eLIM_PROMISCUOUS_MODE_DISASSOC)) { + qdf_mem_copy((uint8_t *) &mlmDisassocCnf.peerMacAddr, + (uint8_t *) sta_dsaddr.bytes, QDF_MAC_ADDR_SIZE); + mlmDisassocCnf.resultCode = status_code; + mlmDisassocCnf.disassocTrigger = mlmStaContext.cleanupTrigger; + /* Update PE session Id */ + mlmDisassocCnf.sessionId = pe_session->peSessionId; + + lim_post_sme_message(mac, + LIM_MLM_DISASSOC_CNF, + (uint32_t *) &mlmDisassocCnf); + } else if ((mlmStaContext.cleanupTrigger == + eLIM_HOST_DEAUTH) || + (mlmStaContext.cleanupTrigger == + eLIM_LINK_MONITORING_DEAUTH)) { + qdf_copy_macaddr(&mlmDeauthCnf.peer_macaddr, &sta_dsaddr); + mlmDeauthCnf.resultCode = status_code; + mlmDeauthCnf.deauthTrigger = mlmStaContext.cleanupTrigger; + /* PE session Id */ + mlmDeauthCnf.sessionId = pe_session->peSessionId; + + lim_post_sme_message(mac, + LIM_MLM_DEAUTH_CNF, + (uint32_t *) &mlmDeauthCnf); + } else if ((mlmStaContext.cleanupTrigger == + eLIM_PEER_ENTITY_DISASSOC) || + (mlmStaContext.cleanupTrigger == eLIM_PEER_ENTITY_DEAUTH)) { + qdf_mem_copy((uint8_t *) &mlmPurgeStaInd.peerMacAddr, + (uint8_t *) sta_dsaddr.bytes, QDF_MAC_ADDR_SIZE); + mlmPurgeStaInd.reasonCode = + (uint8_t) mlmStaContext.disassocReason; + mlmPurgeStaInd.aid = staDsAssocId; + mlmPurgeStaInd.purgeTrigger = mlmStaContext.cleanupTrigger; + mlmPurgeStaInd.sessionId = pe_session->peSessionId; + + lim_post_sme_message(mac, + LIM_MLM_PURGE_STA_IND, + (uint32_t *) &mlmPurgeStaInd); + } else if (mlmStaContext.cleanupTrigger == eLIM_JOIN_FAILURE) { + /* PE setup the peer entry in HW upfront, right after join is completed. */ + /* If there is a failure during rest of the assoc sequence, this context needs to be cleaned up. */ + uint8_t smesessionId; + + smesessionId = pe_session->smeSessionId; + pe_session->limSmeState = eLIM_SME_JOIN_FAILURE_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_SME_STATE, pe_session->peSessionId, + pe_session->limSmeState)); + + /* if it is a reassoc failure to join new AP */ + if ((mlmStaContext.resultCode == + eSIR_SME_FT_REASSOC_TIMEOUT_FAILURE) + || (mlmStaContext.resultCode == eSIR_SME_FT_REASSOC_FAILURE) + || (mlmStaContext.resultCode == + eSIR_SME_REASSOC_TIMEOUT_RESULT_CODE)) { + pe_debug("Lim Posting eWNI_SME_REASSOC_RSP to SME" + "resultCode: %d, status_code: %d," + "sessionId: %d", + mlmStaContext.resultCode, + mlmStaContext.protStatusCode, + pe_session->peSessionId); + + lim_send_sme_join_reassoc_rsp(mac, eWNI_SME_REASSOC_RSP, + mlmStaContext.resultCode, + mlmStaContext.protStatusCode, + pe_session, smesessionId); + if (mlmStaContext.resultCode != eSIR_SME_SUCCESS) { + pe_delete_session(mac, pe_session); + pe_session = NULL; + } + } else { + qdf_mem_free(pe_session->lim_join_req); + pe_session->lim_join_req = NULL; + + pe_debug("Lim Posting eWNI_SME_JOIN_RSP to SME." + "resultCode: %d,status_code: %d," + "sessionId: %d", + mlmStaContext.resultCode, + mlmStaContext.protStatusCode, + pe_session->peSessionId); + + lim_send_sme_join_reassoc_rsp(mac, eWNI_SME_JOIN_RSP, + mlmStaContext.resultCode, + mlmStaContext.protStatusCode, + pe_session, smesessionId); + + if (mlmStaContext.resultCode != eSIR_SME_SUCCESS) { + pe_delete_session(mac, pe_session); + pe_session = NULL; + } + } + + } else if (mlmStaContext.cleanupTrigger == eLIM_DUPLICATE_ENTRY) { + + qdf_mem_copy((uint8_t *) &mlmDisassocCnf.peerMacAddr, + (uint8_t *) sta_dsaddr.bytes, QDF_MAC_ADDR_SIZE); + mlmDisassocCnf.resultCode = status_code; + mlmDisassocCnf.disassocTrigger = eLIM_DUPLICATE_ENTRY; + /* Update PE session Id */ + mlmDisassocCnf.sessionId = pe_session->peSessionId; + + lim_post_sme_message(mac, + LIM_MLM_DISASSOC_CNF, + (uint32_t *) &mlmDisassocCnf); + } + + if (pe_session && !LIM_IS_AP_ROLE(pe_session)) { + pe_delete_session(mac, pe_session); + pe_session = NULL; + } +} + +/** + * lim_reject_association() - function to reject Re/Association Request + * + * @mac_ctx: pointer to global mac structure + * @peer_addr: mac address of the peer + * @sub_type: Indicates whether it is Association Request (=0) or + * Reassociation Request (=1) frame + * @add_pre_auth_context:Indicates whether pre-auth context + * to be added for this STA + * @auth_type: Indicates auth type to be added + * @sta_id: Indicates staId of the STA being rejected + * association + * @delete_sta: Indicates whether to delete STA context + * at Polaris + * @result_code: Indicates what reasonCode to be sent in + * Re/Assoc response to STA + * @session_entry: pointer to PE session + * + * This function is called whenever Re/Association Request need + * to be rejected due to failure in assigning an AID or failure + * in adding STA context at Polaris or reject by applications. + * Resources allocated if any are freedup and (Re) Association + * Response frame is sent to requesting STA. Pre-Auth context + * will be added for this STA if it does not exist already + * + * Return: none + */ + +void +lim_reject_association(struct mac_context *mac_ctx, tSirMacAddr peer_addr, + uint8_t sub_type, uint8_t add_pre_auth_context, + tAniAuthType auth_type, uint16_t sta_id, + uint8_t delete_sta, enum mac_status_code result_code, + struct pe_session *session_entry) +{ + tpDphHashNode sta_ds; + + pe_debug("Sessionid: %d auth_type: %d sub_type: %d add_pre_auth_context: %d sta_id: %d delete_sta: %d result_code : %d peer_addr: " QDF_MAC_ADDR_FMT, + session_entry->peSessionId, auth_type, sub_type, + add_pre_auth_context, sta_id, delete_sta, result_code, + QDF_MAC_ADDR_REF(peer_addr)); + + if (add_pre_auth_context) { + /* Create entry for this STA in pre-auth list */ + struct tLimPreAuthNode *auth_node; + + auth_node = lim_acquire_free_pre_auth_node(mac_ctx, + &mac_ctx->lim.gLimPreAuthTimerTable); + + if (auth_node) { + qdf_mem_copy((uint8_t *) auth_node->peerMacAddr, + peer_addr, sizeof(tSirMacAddr)); + auth_node->fTimerStarted = 0; + auth_node->mlmState = eLIM_MLM_AUTHENTICATED_STATE; + auth_node->authType = (tAniAuthType) auth_type; + auth_node->timestamp = qdf_mc_timer_get_system_ticks(); + lim_add_pre_auth_node(mac_ctx, auth_node); + } + } + + if (delete_sta == false) { + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, + eSIR_MAC_MAX_ASSOC_STA_REACHED_STATUS, + 1, peer_addr, sub_type, 0, session_entry, + false); + pe_warn("received Re/Assoc req when max associated STAs reached from"); + lim_print_mac_addr(mac_ctx, peer_addr, LOGW); + lim_send_sme_max_assoc_exceeded_ntf(mac_ctx, peer_addr, + session_entry->smeSessionId); + return; + } + + sta_ds = dph_get_hash_entry(mac_ctx, sta_id, + &session_entry->dph.dphHashTable); + + if (!sta_ds) { + pe_err("No STA context, yet rejecting Association"); + return; + } + + /* + * Polaris has state for this STA. + * Trigger cleanup. + */ + sta_ds->mlmStaContext.cleanupTrigger = eLIM_REASSOC_REJECT; + + /* Receive path cleanup */ + lim_cleanup_rx_path(mac_ctx, sta_ds, session_entry); + + /* + * Send Re/Association Response with + * status code to requesting STA. + */ + lim_send_assoc_rsp_mgmt_frame(mac_ctx, result_code, 0, peer_addr, + sub_type, 0, session_entry, false); + + if (session_entry->parsedAssocReq[sta_ds->assocId]) { + uint8_t *assoc_req_frame; + + assoc_req_frame = (uint8_t *)((tpSirAssocReq) (session_entry-> + parsedAssocReq[sta_ds->assocId]))->assocReqFrame; + /* + *Assoction confirmation is complete, + *free the copy of association request frame. + */ + if (assoc_req_frame) { + qdf_mem_free(assoc_req_frame); + assoc_req_frame = NULL; + } + + qdf_mem_free(session_entry->parsedAssocReq[sta_ds->assocId]); + session_entry->parsedAssocReq[sta_ds->assocId] = NULL; + } +} + +/** + * lim_decide_ap_protection_on_ht20_delete() - function to update protection + * parameters. + * @mac_ctx: pointer to global mac structure + * @sta_ds: station node + * @beacon_params: ap beacon parameters + * @session_entry: pe session entry + * + * protection related function while HT20 station is getting deleted. + * + * Return: none + */ +static void +lim_decide_ap_protection_on_ht20_delete(struct mac_context *mac_ctx, + tpDphHashNode sta_ds, + tpUpdateBeaconParams beacon_params, + struct pe_session *session_entry) +{ + uint32_t i = 0; + + pe_debug("(%d) A HT 20 STA is disassociated. Addr is "QDF_MAC_ADDR_FMT, + session_entry->gLimHt20Params.numSta, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + + if (session_entry->gLimHt20Params.numSta > 0) { + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (!session_entry->protStaCache[i].active) + continue; + + if (!qdf_mem_cmp(session_entry->protStaCache[i].addr, + sta_ds->staAddr, sizeof(tSirMacAddr))) { + session_entry->gLimHt20Params.numSta--; + session_entry->protStaCache[i].active = + false; + break; + } + } + } + + if (session_entry->gLimHt20Params.numSta == 0) { + /* disable protection */ + pe_debug("No 11B STA exists, PESessionID %d", + session_entry->peSessionId); + lim_enable_ht20_protection(mac_ctx, false, false, beacon_params, + session_entry); + } +} + +/** + * lim_decide_ap_protection_on_delete() - update SAP protection on station + * deletion. + * @mac_ctx: pointer to global mac structure + * @sta_ds: station node + * @beacon_params: ap beacon parameters + * @session_entry: pe session entry + * + * Decides about protection related settings when a station is getting deleted. + * + * Return: none + */ +void +lim_decide_ap_protection_on_delete(struct mac_context *mac_ctx, + tpDphHashNode sta_ds, + tpUpdateBeaconParams beacon_params, + struct pe_session *session_entry) +{ + uint32_t phy_mode; + tHalBitVal erp_enabled = eHAL_CLEAR; + enum reg_wifi_band rf_band = REG_BAND_UNKNOWN; + uint32_t i; + + if (!sta_ds) + return; + + lim_get_rf_band_new(mac_ctx, &rf_band, session_entry); + lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); + erp_enabled = sta_ds->erpEnabled; + + if ((REG_BAND_5G == rf_band) && + (true == session_entry->htCapability) && + (session_entry->beaconParams.llaCoexist) && + (false == sta_ds->mlmStaContext.htCapability)) { + /* + * we are HT. if we are 11A, then protection is not required or + * we are HT and 11A station is leaving. + * protection consideration required. + * HT station leaving ==> this case is commonly handled + * between both the bands below. + */ + pe_debug("(%d) A 11A STA is disassociated. Addr is "QDF_MAC_ADDR_FMT, + session_entry->gLim11aParams.numSta, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (session_entry->protStaCache[i].active && + (!qdf_mem_cmp( + session_entry->protStaCache[i].addr, + sta_ds->staAddr, + sizeof(tSirMacAddr)))) { + session_entry->protStaCache[i].active = false; + break; + } + } + + if (session_entry->gLim11aParams.numSta == 0) { + /* disable protection */ + lim_update_11a_protection(mac_ctx, false, false, + beacon_params, session_entry); + } + } + + /* we are HT or 11G and 11B station is getting deleted */ + if ((REG_BAND_2G == rf_band) && + (phy_mode == WNI_CFG_PHY_MODE_11G || + session_entry->htCapability) && + (erp_enabled == eHAL_CLEAR)) { + pe_debug("(%d) A legacy STA is disassociated. Addr is "QDF_MAC_ADDR_FMT, + session_entry->gLim11bParams.numSta, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (session_entry->protStaCache[i].active && + (!qdf_mem_cmp( + session_entry->protStaCache[i].addr, + sta_ds->staAddr, + sizeof(tSirMacAddr)))) { + session_entry->gLim11bParams.numSta--; + session_entry->protStaCache[i].active = + false; + break; + } + } + + if (session_entry->gLim11bParams.numSta == 0) { + /* disable protection */ + lim_enable11g_protection(mac_ctx, false, false, + beacon_params, session_entry); + } + } + + /* + * we are HT AP and non-11B station is leaving. + * 11g station is leaving + */ + if ((REG_BAND_2G == rf_band) && + session_entry->htCapability && + !sta_ds->mlmStaContext.htCapability) { + pe_debug("(%d) A 11g STA is disassociated. Addr is "QDF_MAC_ADDR_FMT, + session_entry->gLim11bParams.numSta, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (session_entry->protStaCache[i].active && + (!qdf_mem_cmp( + session_entry->protStaCache[i].addr, + sta_ds->staAddr, + sizeof(tSirMacAddr)))) { + session_entry->gLim11gParams.numSta--; + session_entry->protStaCache[i].active = false; + break; + } + } + + if (session_entry->gLim11gParams.numSta == 0) { + /* disable protection */ + lim_enable_ht_protection_from11g(mac_ctx, false, false, + beacon_params, + session_entry); + } + } + + if (!((true == session_entry->htCapability) && + (true == sta_ds->mlmStaContext.htCapability))) + return; + + /* + * Applies to 2.4 as well as 5 GHZ. + * HT non-GF leaving + */ + if (!sta_ds->htGreenfield) { + pe_debug("(%d) A non-GF STA is disassociated. Addr is "QDF_MAC_ADDR_FMT, + session_entry->gLimNonGfParams.numSta, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (session_entry->protStaCache[i].active && + (!qdf_mem_cmp( + session_entry->protStaCache[i].addr, + sta_ds->staAddr, + sizeof(tSirMacAddr)))) { + session_entry->protStaCache[i].active = false; + break; + } + } + + if (session_entry->gLimNonGfParams.numSta == 0) { + /* disable protection */ + lim_enable_ht_non_gf_protection(mac_ctx, false, false, + beacon_params, session_entry); + } + } + + /* + * Applies to 2.4 as well as 5 GHZ. + * HT 20Mhz station leaving + */ + if (session_entry->beaconParams.ht20Coexist && + (eHT_CHANNEL_WIDTH_20MHZ == + sta_ds->htSupportedChannelWidthSet)) { + lim_decide_ap_protection_on_ht20_delete(mac_ctx, sta_ds, + beacon_params, session_entry); + } + + /* + * Applies to 2.4 as well as 5 GHZ. + * LSIG TXOP not supporting staiton leaving + */ + if ((false == session_entry->beaconParams. + fLsigTXOPProtectionFullSupport) && + (false == sta_ds->htLsigTXOPProtection)) { + pe_debug("(%d) A HT LSIG not supporting STA is disassociated. Addr is "QDF_MAC_ADDR_FMT, + session_entry->gLimLsigTxopParams.numSta, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (session_entry->protStaCache[i].active && + (!qdf_mem_cmp( + session_entry->protStaCache[i].addr, + sta_ds->staAddr, + sizeof(tSirMacAddr)))) { + session_entry->protStaCache[i].active = false; + break; + } + } + + if (session_entry->gLimLsigTxopParams.numSta == 0) { + /* disable protection */ + lim_enable_ht_lsig_txop_protection(mac_ctx, true, + false, beacon_params, session_entry); + } + } +} + +/** + * lim_decide_short_preamble() - update short preamble parameters + * @mac_ctx: pointer to global mac structure + * @sta_ds: station node + * @beacon_params: ap beacon parameters + * @session_entry: pe session entry + * + * Decides about any short preamble related change because of new station + * joining. + * + * Return: None + */ +static void lim_decide_short_preamble(struct mac_context *mac_ctx, + tpDphHashNode sta_ds, + tpUpdateBeaconParams beacon_params, + struct pe_session *session_entry) +{ + uint32_t i; + + if (sta_ds->shortPreambleEnabled == eHAL_CLEAR) { + pe_debug("(%d) A non-short preamble STA is disassociated. Addr is "QDF_MAC_ADDR_FMT, + session_entry->gLimNoShortParams.numNonShortPreambleSta, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (session_entry->gLimNoShortParams. + staNoShortCache[i].active && + (!qdf_mem_cmp(session_entry-> + gLimNoShortParams. + staNoShortCache[i].addr, + sta_ds->staAddr, + sizeof(tSirMacAddr)))) { + session_entry->gLimNoShortParams. + numNonShortPreambleSta--; + session_entry->gLimNoShortParams. + staNoShortCache[i].active = false; + break; + } + } + + if (session_entry->gLimNoShortParams.numNonShortPreambleSta) + return; + + /* + * enable short preamble + * reset the cache + */ + qdf_mem_zero((uint8_t *) &session_entry->gLimNoShortParams, + sizeof(tLimNoShortParams)); + if (lim_enable_short_preamble(mac_ctx, true, + beacon_params, session_entry) != QDF_STATUS_SUCCESS) + pe_err("Cannot enable short preamble"); + } +} + +/** + * lim_decide_short_slot() - update short slot time related parameters + * @mac_ctx: pointer to global mac structure + * @sta_ds: station node + * @beacon_params: ap beacon parameters + * @session_entry: pe session entry + * + * Decides about any short slot time related change because of station leaving + * the BSS. + * Return: None + */ +static void +lim_decide_short_slot(struct mac_context *mac_ctx, tpDphHashNode sta_ds, + tpUpdateBeaconParams beacon_params, + struct pe_session *session_entry) +{ + uint32_t i, val, non_short_slot_sta_count; + + if (sta_ds->shortSlotTimeEnabled != eHAL_CLEAR) + return; + + pe_debug("(%d) A non-short slottime STA is disassociated. Addr is "QDF_MAC_ADDR_FMT, + mac_ctx->lim.gLimNoShortSlotParams.numNonShortSlotSta, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + + val = mac_ctx->mlme_cfg->feature_flags.enable_short_slot_time_11g; + + if (LIM_IS_AP_ROLE(session_entry)) { + non_short_slot_sta_count = + session_entry->gLimNoShortSlotParams.numNonShortSlotSta; + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (session_entry->gLimNoShortSlotParams. + staNoShortSlotCache[i].active && + (!qdf_mem_cmp(session_entry-> + gLimNoShortSlotParams. + staNoShortSlotCache[i].addr, + sta_ds->staAddr, + sizeof(tSirMacAddr)))) { + non_short_slot_sta_count--; + session_entry->gLimNoShortSlotParams. + staNoShortSlotCache[i].active = false; + break; + } + } + + if (non_short_slot_sta_count == 0 && val) { + /* + * enable short slot time + * reset the cache + */ + qdf_mem_zero((uint8_t *) &session_entry-> + gLimNoShortSlotParams, + sizeof(tLimNoShortSlotParams)); + beacon_params->fShortSlotTime = true; + beacon_params->paramChangeBitmap |= + PARAM_SHORT_SLOT_TIME_CHANGED; + session_entry->shortSlotTimeSupported = true; + } + session_entry->gLimNoShortSlotParams.numNonShortSlotSta = + non_short_slot_sta_count; + } else { + non_short_slot_sta_count = + mac_ctx->lim.gLimNoShortSlotParams.numNonShortSlotSta; + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (mac_ctx->lim.gLimNoShortSlotParams. + staNoShortSlotCache[i].active && + (!qdf_mem_cmp( + mac_ctx->lim.gLimNoShortSlotParams. + staNoShortSlotCache[i].addr, + sta_ds->staAddr, + sizeof(tSirMacAddr)))) { + non_short_slot_sta_count--; + mac_ctx->lim.gLimNoShortSlotParams. + staNoShortSlotCache[i].active = false; + break; + } + } + + if (val && !non_short_slot_sta_count) { + /* + * enable short slot time + * reset the cache + */ + qdf_mem_zero( + (uint8_t *) &mac_ctx->lim.gLimNoShortSlotParams, + sizeof(tLimNoShortSlotParams)); + /*in case of AP set SHORT_SLOT_TIME to enable*/ + if (LIM_IS_AP_ROLE(session_entry)) { + beacon_params->fShortSlotTime = true; + beacon_params->paramChangeBitmap |= + PARAM_SHORT_SLOT_TIME_CHANGED; + session_entry->shortSlotTimeSupported = true; + } + } + mac_ctx->lim.gLimNoShortSlotParams.numNonShortSlotSta = + non_short_slot_sta_count; + } +} + +static uint8_t lim_get_nss_from_vht_mcs_map(uint16_t mcs_map) +{ + uint8_t nss = 0; + uint16_t mcs_mask = 0x3; + + for (nss = 0; nss < VHT_MAX_NSS; nss++) { + if ((mcs_map & mcs_mask) == mcs_mask) + return nss; + + mcs_mask = (mcs_mask << 2); + } + + return nss; +} + +static void lim_get_vht_gt80_nss(struct mac_context *mac_ctx, + struct sDphHashNode *sta_ds, + tDot11fIEVHTCaps *vht_caps, + struct pe_session *session) +{ + uint8_t nss; + + if (!vht_caps->vht_extended_nss_bw_cap) { + sta_ds->vht_160mhz_nss = 0; + sta_ds->vht_80p80mhz_nss = 0; + pe_debug("peer does not support vht extnd nss bw"); + + return; + } + + nss = lim_get_nss_from_vht_mcs_map(vht_caps->rxMCSMap); + + if (!nss) { + pe_debug("Invalid peer VHT MCS map %0X", vht_caps->rxMCSMap); + nss = 1; + } + + switch (vht_caps->supportedChannelWidthSet) { + case VHT_CAP_NO_160M_SUPP: + if (vht_caps->extended_nss_bw_supp == + VHT_EXTD_NSS_80_HALF_NSS_160) { + sta_ds->vht_160mhz_nss = nss / 2; + sta_ds->vht_80p80mhz_nss = 0; + } else if (vht_caps->extended_nss_bw_supp == + VHT_EXTD_NSS_80_HALF_NSS_80P80) { + sta_ds->vht_160mhz_nss = nss / 2; + sta_ds->vht_80p80mhz_nss = nss / 2; + } else if (vht_caps->extended_nss_bw_supp == + VHT_EXTD_NSS_80_3QUART_NSS_80P80) { + sta_ds->vht_160mhz_nss = (nss * 3) / 4; + sta_ds->vht_80p80mhz_nss = (nss * 3) / 4; + } else { + sta_ds->vht_160mhz_nss = 0; + sta_ds->vht_80p80mhz_nss = 0; + } + break; + case VHT_CAP_160_SUPP: + sta_ds->vht_160mhz_nss = nss; + if (vht_caps->extended_nss_bw_supp == + VHT_EXTD_NSS_160_HALF_NSS_80P80) { + sta_ds->vht_80p80mhz_nss = nss / 2; + } else if (vht_caps->extended_nss_bw_supp == + VHT_EXTD_NSS_160_3QUART_NSS_80P80) { + sta_ds->vht_80p80mhz_nss = (nss * 3) / 4; + } else if (vht_caps->extended_nss_bw_supp == + VHT_EXTD_NSS_2X_NSS_160_1X_NSS_80P80) { + if (nss > (VHT_MAX_NSS / 2)) { + pe_debug("Invalid extnd nss bw support val"); + sta_ds->vht_80p80mhz_nss = nss / 2; + break; + } + sta_ds->vht_160mhz_nss = nss * 2; + if (session->nss == MAX_VDEV_NSS) + break; + if (!mac_ctx->mlme_cfg->vht_caps.vht_cap_info.enable2x2) + break; + session->nss *= 2; + } else { + sta_ds->vht_80p80mhz_nss = 0; + } + break; + case VHT_CAP_160_AND_80P80_SUPP: + if (vht_caps->extended_nss_bw_supp == + VHT_EXTD_NSS_2X_NSS_80_1X_NSS_80P80) { + if (nss > (VHT_MAX_NSS / 2)) { + pe_debug("Invalid extnd nss bw support val"); + break; + } + if (session->nss == MAX_VDEV_NSS) + break; + if (!mac_ctx->mlme_cfg->vht_caps.vht_cap_info.enable2x2) + break; + session->nss *= 2; + } else { + sta_ds->vht_160mhz_nss = nss; + sta_ds->vht_80p80mhz_nss = nss; + } + break; + default: + sta_ds->vht_160mhz_nss = 0; + sta_ds->vht_80p80mhz_nss = 0; + } + pe_debug("AP Nss config: 160MHz: %d, 80P80MHz %d", + sta_ds->vht_160mhz_nss, sta_ds->vht_80p80mhz_nss); + sta_ds->vht_160mhz_nss = QDF_MIN(sta_ds->vht_160mhz_nss, session->nss); + sta_ds->vht_80p80mhz_nss = QDF_MIN(sta_ds->vht_80p80mhz_nss, + session->nss); + pe_debug("Session Nss config: 160MHz: %d, 80P80MHz %d, session Nss %d", + sta_ds->vht_160mhz_nss, sta_ds->vht_80p80mhz_nss, + session->nss); +} + +QDF_STATUS lim_populate_vht_mcs_set(struct mac_context *mac_ctx, + struct supported_rates *rates, + tDot11fIEVHTCaps *peer_vht_caps, + struct pe_session *session_entry, + uint8_t nss, + struct sDphHashNode *sta_ds) +{ + uint32_t self_sta_dot11mode = 0; + uint16_t mcs_map_mask = MCSMAPMASK1x1; + uint16_t mcs_map_mask2x2 = 0; + struct mlme_vht_capabilities_info *vht_cap_info; + + self_sta_dot11mode = mac_ctx->mlme_cfg->dot11_mode.dot11_mode; + + if (!IS_DOT11_MODE_VHT(self_sta_dot11mode)) + return QDF_STATUS_SUCCESS; + + if (!peer_vht_caps || !peer_vht_caps->present) + return QDF_STATUS_SUCCESS; + + vht_cap_info = &mac_ctx->mlme_cfg->vht_caps.vht_cap_info; + + rates->vhtRxMCSMap = (uint16_t)vht_cap_info->rx_mcs_map; + rates->vhtTxMCSMap = (uint16_t)vht_cap_info->tx_mcs_map; + rates->vhtRxHighestDataRate = + (uint16_t)vht_cap_info->rx_supp_data_rate; + rates->vhtTxHighestDataRate = + (uint16_t)vht_cap_info->tx_supp_data_rate; + + if (NSS_1x1_MODE == nss) { + rates->vhtRxMCSMap |= VHT_MCS_1x1; + rates->vhtTxMCSMap |= VHT_MCS_1x1; + rates->vhtTxHighestDataRate = + VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + rates->vhtRxHighestDataRate = + VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + if (session_entry && !session_entry->ch_width && + !vht_cap_info->enable_vht20_mcs9 && + ((rates->vhtRxMCSMap & VHT_1x1_MCS_MASK) == + VHT_1x1_MCS9_MAP)) { + DISABLE_VHT_MCS_9(rates->vhtRxMCSMap, + NSS_1x1_MODE); + DISABLE_VHT_MCS_9(rates->vhtTxMCSMap, + NSS_1x1_MODE); + } + } else { + if (session_entry && !session_entry->ch_width && + !vht_cap_info->enable_vht20_mcs9 && + ((rates->vhtRxMCSMap & VHT_2x2_MCS_MASK) == + VHT_2x2_MCS9_MAP)) { + DISABLE_VHT_MCS_9(rates->vhtRxMCSMap, + NSS_2x2_MODE); + DISABLE_VHT_MCS_9(rates->vhtTxMCSMap, + NSS_2x2_MODE); + } + } + + rates->vhtTxHighestDataRate = + QDF_MIN(rates->vhtTxHighestDataRate, + peer_vht_caps->txSupDataRate); + rates->vhtRxHighestDataRate = + QDF_MIN(rates->vhtRxHighestDataRate, + peer_vht_caps->rxHighSupDataRate); + + if (session_entry && session_entry->nss == NSS_2x2_MODE) + mcs_map_mask2x2 = MCSMAPMASK2x2; + + if ((peer_vht_caps->txMCSMap & mcs_map_mask) < + (rates->vhtRxMCSMap & mcs_map_mask)) { + rates->vhtRxMCSMap &= ~(mcs_map_mask); + rates->vhtRxMCSMap |= (peer_vht_caps->txMCSMap & mcs_map_mask); + } + if ((peer_vht_caps->rxMCSMap & mcs_map_mask) < + (rates->vhtTxMCSMap & mcs_map_mask)) { + rates->vhtTxMCSMap &= ~(mcs_map_mask); + rates->vhtTxMCSMap |= (peer_vht_caps->rxMCSMap & mcs_map_mask); + } + + if (mcs_map_mask2x2) { + uint16_t peer_mcs_map, self_mcs_map; + + peer_mcs_map = peer_vht_caps->txMCSMap & mcs_map_mask2x2; + self_mcs_map = rates->vhtRxMCSMap & mcs_map_mask2x2; + + if ((self_mcs_map != mcs_map_mask2x2) && + ((peer_mcs_map == mcs_map_mask2x2) || + (peer_mcs_map < self_mcs_map))) { + rates->vhtRxMCSMap &= ~mcs_map_mask2x2; + rates->vhtRxMCSMap |= peer_mcs_map; + } + + peer_mcs_map = (peer_vht_caps->rxMCSMap & mcs_map_mask2x2); + self_mcs_map = (rates->vhtTxMCSMap & mcs_map_mask2x2); + + if ((self_mcs_map != mcs_map_mask2x2) && + ((peer_mcs_map == mcs_map_mask2x2) || + (peer_mcs_map < self_mcs_map))) { + rates->vhtTxMCSMap &= ~mcs_map_mask2x2; + rates->vhtTxMCSMap |= peer_mcs_map; + } + } + + pe_debug("RxMCSMap %x TxMCSMap %x", rates->vhtRxMCSMap, + rates->vhtTxMCSMap); + + if (!session_entry) + return QDF_STATUS_SUCCESS; + + session_entry->supported_nss_1x1 = + ((rates->vhtTxMCSMap & VHT_MCS_1x1) == VHT_MCS_1x1) ? + true : false; + + if (!sta_ds || CH_WIDTH_80MHZ >= session_entry->ch_width) + return QDF_STATUS_SUCCESS; + + sta_ds->vht_extended_nss_bw_cap = + peer_vht_caps->vht_extended_nss_bw_cap; + lim_get_vht_gt80_nss(mac_ctx, sta_ds, peer_vht_caps, session_entry); + + return QDF_STATUS_SUCCESS; +} + +static void lim_dump_ht_mcs_mask(uint8_t *self_mcs, uint8_t *peer_mcs) +{ + uint32_t len = 0; + uint8_t idx; + uint8_t *buff; + uint32_t buff_len; + + /* + * Buffer of (SIR_MAC_MAX_SUPPORTED_MCS_SET * 5) + 1 to consider the 4 + * char MCS eg 0xff and 1 space after it and 1 to end the string with + * NULL. + */ + buff_len = (SIR_MAC_MAX_SUPPORTED_MCS_SET * 5) + 1; + buff = qdf_mem_malloc(buff_len); + if (!buff) + return; + + if (self_mcs) { + for (idx = 0; idx < SIR_MAC_MAX_SUPPORTED_MCS_SET; idx++) + len += qdf_scnprintf(buff + len, buff_len - len, + "0x%x ", self_mcs[idx]); + + pe_nofl_debug("SELF HT MCS: %s", buff); + } + + if (peer_mcs) { + len = 0; + for (idx = 0; idx < SIR_MAC_MAX_SUPPORTED_MCS_SET; idx++) + len += qdf_scnprintf(buff + len, buff_len - len, + "0x%x ", peer_mcs[idx]); + + pe_nofl_debug("PEER HT MCS: %s", buff); + } + + qdf_mem_free(buff); +} + +QDF_STATUS lim_populate_own_rate_set(struct mac_context *mac_ctx, + struct supported_rates *rates, + uint8_t *supported_mcs_set, + uint8_t basic_only, + struct pe_session *session_entry, + struct sDot11fIEVHTCaps *vht_caps, + struct sDot11fIEhe_cap *he_caps) +{ + tSirMacRateSet temp_rate_set; + tSirMacRateSet temp_rate_set2; + uint32_t i, j, val, min, is_arate; + uint32_t phy_mode = 0; + uint32_t self_sta_dot11mode = 0; + uint8_t a_rate_index = 0; + uint8_t b_rate_index = 0; + qdf_size_t val_len; + + is_arate = 0; + + self_sta_dot11mode = mac_ctx->mlme_cfg->dot11_mode.dot11_mode; + lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); + + /* + * Include 11b rates only when the device configured in + * auto, 11a/b/g or 11b_only + */ + if ((self_sta_dot11mode == MLME_DOT11_MODE_ALL) || + (self_sta_dot11mode == MLME_DOT11_MODE_11A) || + (self_sta_dot11mode == MLME_DOT11_MODE_11AC) || + (self_sta_dot11mode == MLME_DOT11_MODE_11N) || + (self_sta_dot11mode == MLME_DOT11_MODE_11G) || + (self_sta_dot11mode == MLME_DOT11_MODE_11B) || + (self_sta_dot11mode == MLME_DOT11_MODE_11AX)) { + val_len = mac_ctx->mlme_cfg->rates.supported_11b.len; + wlan_mlme_get_cfg_str((uint8_t *)&temp_rate_set.rate, + &mac_ctx->mlme_cfg->rates.supported_11b, + &val_len); + temp_rate_set.numRates = (uint8_t)val_len; + } else { + temp_rate_set.numRates = 0; + } + + /* Include 11a rates when the device configured in non-11b mode */ + if (!IS_DOT11_MODE_11B(self_sta_dot11mode)) { + val_len = mac_ctx->mlme_cfg->rates.supported_11a.len; + wlan_mlme_get_cfg_str((uint8_t *)&temp_rate_set2.rate, + &mac_ctx->mlme_cfg->rates.supported_11a, + &val_len); + temp_rate_set2.numRates = (uint8_t)val_len; + } else { + temp_rate_set2.numRates = 0; + } + + if ((temp_rate_set.numRates + temp_rate_set2.numRates) > 12) { + pe_err("more than 12 rates in CFG"); + return QDF_STATUS_E_FAILURE; + } + /* copy all rates in temp_rate_set, there are 12 rates max */ + for (i = 0; i < temp_rate_set2.numRates; i++) + temp_rate_set.rate[i + temp_rate_set.numRates] = + temp_rate_set2.rate[i]; + + temp_rate_set.numRates += temp_rate_set2.numRates; + + /** + * Sort rates in temp_rate_set (they are likely to be already sorted) + * put the result in pSupportedRates + */ + + qdf_mem_zero(rates, sizeof(*rates)); + for (i = 0; i < temp_rate_set.numRates; i++) { + min = 0; + val = 0xff; + is_arate = 0; + + for (j = 0; (j < temp_rate_set.numRates) && + (j < WLAN_SUPPORTED_RATES_IE_MAX_LEN); j++) { + if ((uint32_t) (temp_rate_set.rate[j] & 0x7f) < + val) { + val = temp_rate_set.rate[j] & 0x7f; + min = j; + } + } + + if (sirIsArate(temp_rate_set.rate[min] & 0x7f)) + is_arate = 1; + + if (is_arate) + rates->llaRates[a_rate_index++] = + temp_rate_set.rate[min]; + else + rates->llbRates[b_rate_index++] = + temp_rate_set.rate[min]; + temp_rate_set.rate[min] = 0xff; + } + + if (IS_DOT11_MODE_HT(self_sta_dot11mode)) { + val_len = SIZE_OF_SUPPORTED_MCS_SET; + if (wlan_mlme_get_cfg_str( + rates->supportedMCSSet, + &mac_ctx->mlme_cfg->rates.supported_mcs_set, + &val_len) != QDF_STATUS_SUCCESS) { + pe_err("could not retrieve supportedMCSSet"); + return QDF_STATUS_E_FAILURE; + } + + if (session_entry->nss == NSS_1x1_MODE) + rates->supportedMCSSet[1] = 0; + /* + * if supported MCS Set of the peer is passed in, + * then do the intersection + * else use the MCS set from local CFG. + */ + + if (supported_mcs_set) { + for (i = 0; i < SIR_MAC_MAX_SUPPORTED_MCS_SET; i++) + rates->supportedMCSSet[i] &= + supported_mcs_set[i]; + } + + lim_dump_ht_mcs_mask(rates->supportedMCSSet, NULL); + } + lim_populate_vht_mcs_set(mac_ctx, rates, vht_caps, session_entry, + session_entry->nss, NULL); + lim_populate_he_mcs_set(mac_ctx, rates, he_caps, + session_entry, session_entry->nss); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_11AX +/** + * lim_calculate_he_nss() - function to calculate new nss from he rates + * @rates: supported rtes struct object + * @session: pe session entry + * This function calculates nss from rx_he_mcs_map_lt_80 within rates struct + * object and assigns new value to nss within pe_session + * + * Return: None + */ +static void lim_calculate_he_nss(struct supported_rates *rates, + struct pe_session *session) +{ + HE_GET_NSS(rates->rx_he_mcs_map_lt_80, session->nss); +} + +static bool lim_check_valid_mcs_for_nss(struct pe_session *session, + tDot11fIEhe_cap *he_caps) +{ + uint16_t mcs_map; + uint8_t mcs_count = 2, i; + + if (!session->he_capable || !he_caps || !he_caps->present) + return true; + + mcs_map = he_caps->rx_he_mcs_map_lt_80; + + do { + for (i = 0; i < session->nss; i++) { + if (((mcs_map >> (i * 2)) & 0x3) == 0x3) + return false; + } + + mcs_map = he_caps->tx_he_mcs_map_lt_80; + mcs_count--; + } while (mcs_count); + + return true; + +} +#else +static void lim_calculate_he_nss(struct supported_rates *rates, + struct pe_session *session) +{ +} + +static bool lim_check_valid_mcs_for_nss(struct pe_session *session, + tDot11fIEhe_cap *he_caps) +{ + return true; +} +#endif + +/** + * lim_remove_membership_selectors() - remove elements from rate set + * + * @rate_set: pointer to rate set + * + * Removes the BSS membership selector elements from the rate set, and keep + * only the rates + * + * Return: none + */ +static void lim_remove_membership_selectors(tSirMacRateSet *rate_set) +{ + int i, selector_count = 0; + + for (i = 0; i < rate_set->numRates; i++) { + if ((rate_set->rate[i] == (WLAN_BASIC_RATE_MASK | + WLAN_BSS_MEMBERSHIP_SELECTOR_HT_PHY)) || + (rate_set->rate[i] == (WLAN_BASIC_RATE_MASK | + WLAN_BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) || + (rate_set->rate[i] == (WLAN_BASIC_RATE_MASK | + WLAN_BSS_MEMBERSHIP_SELECTOR_GLK)) || + (rate_set->rate[i] == (WLAN_BASIC_RATE_MASK | + WLAN_BSS_MEMBERSHIP_SELECTOR_EPD)) || + (rate_set->rate[i] == (WLAN_BASIC_RATE_MASK | + WLAN_BSS_MEMBERSHIP_SELECTOR_SAE_H2E)) || + (rate_set->rate[i] == (WLAN_BASIC_RATE_MASK | + WLAN_BSS_MEMBERSHIP_SELECTOR_HE_PHY))) + selector_count++; + + if (i + selector_count < rate_set->numRates) + rate_set->rate[i] = rate_set->rate[i + selector_count]; + } + rate_set->numRates -= selector_count; +} + +QDF_STATUS lim_populate_peer_rate_set(struct mac_context *mac, + struct supported_rates *pRates, + uint8_t *pSupportedMCSSet, + uint8_t basicOnly, + struct pe_session *pe_session, + tDot11fIEVHTCaps *pVHTCaps, + tDot11fIEhe_cap *he_caps, + struct sDphHashNode *sta_ds, + struct bss_description *bss_desc) +{ + tSirMacRateSet tempRateSet; + tSirMacRateSet tempRateSet2; + uint32_t i, j, val, min; + qdf_size_t val_len; + uint8_t aRateIndex = 0; + uint8_t bRateIndex = 0; + tDot11fIEhe_cap *peer_he_caps; + tSchBeaconStruct *pBeaconStruct = NULL; + + /* copy operational rate set from pe_session */ + if (pe_session->rateSet.numRates <= WLAN_SUPPORTED_RATES_IE_MAX_LEN) { + qdf_mem_copy((uint8_t *) tempRateSet.rate, + (uint8_t *) (pe_session->rateSet.rate), + pe_session->rateSet.numRates); + tempRateSet.numRates = pe_session->rateSet.numRates; + } else { + pe_err("more than WLAN_SUPPORTED_RATES_IE_MAX_LEN rates"); + return QDF_STATUS_E_FAILURE; + } + if ((pe_session->dot11mode == MLME_DOT11_MODE_11G) || + (pe_session->dot11mode == MLME_DOT11_MODE_11A) || + (pe_session->dot11mode == MLME_DOT11_MODE_11AC) || + (pe_session->dot11mode == MLME_DOT11_MODE_11N) || + (pe_session->dot11mode == MLME_DOT11_MODE_11AX)) { + if (pe_session->extRateSet.numRates <= + WLAN_SUPPORTED_RATES_IE_MAX_LEN) { + qdf_mem_copy((uint8_t *) tempRateSet2.rate, + (uint8_t *) (pe_session->extRateSet. + rate), + pe_session->extRateSet.numRates); + tempRateSet2.numRates = + pe_session->extRateSet.numRates; + } else { + pe_err("pe_session->extRateSet.numRates more than WLAN_SUPPORTED_RATES_IE_MAX_LEN rates"); + return QDF_STATUS_E_FAILURE; + } + } else + tempRateSet2.numRates = 0; + + lim_remove_membership_selectors(&tempRateSet); + lim_remove_membership_selectors(&tempRateSet2); + + if ((tempRateSet.numRates + tempRateSet2.numRates) > + WLAN_SUPPORTED_RATES_IE_MAX_LEN) { + pe_err("more than 12 rates in CFG"); + return QDF_STATUS_E_FAILURE; + } + + /* copy all rates in tempRateSet, there are 12 rates max */ + for (i = 0; i < tempRateSet2.numRates; i++) + tempRateSet.rate[i + tempRateSet.numRates] = + tempRateSet2.rate[i]; + tempRateSet.numRates += tempRateSet2.numRates; + /** + * Sort rates in tempRateSet (they are likely to be already sorted) + * put the result in pSupportedRates + */ + + qdf_mem_zero(pRates, sizeof(*pRates)); + for (i = 0; i < tempRateSet.numRates; i++) { + min = 0; + val = 0xff; + for (j = 0; (j < tempRateSet.numRates) && + (j < WLAN_SUPPORTED_RATES_IE_MAX_LEN); j++) { + if ((uint32_t)(tempRateSet.rate[j] & 0x7f) < + val) { + val = tempRateSet.rate[j] & 0x7f; + min = j; + } + } + /* + * HAL needs to know whether the rate is basic rate or not, + * as it needs to update the response rate table accordingly. + * e.g. if one of the 11a rates is basic rate, then that rate + * can be used for sending control frames. HAL updates the + * response rate table whenever basic rate set is changed. + */ + if (basicOnly && !(tempRateSet.rate[min] & 0x80)) { + pe_debug("Invalid basic rate"); + } else if (sirIsArate(tempRateSet.rate[min] & 0x7f)) { + if (aRateIndex >= SIR_NUM_11A_RATES) { + pe_debug("OOB, aRateIndex: %d", aRateIndex); + } else if (aRateIndex >= 1 && (tempRateSet.rate[min] == + pRates->llaRates[aRateIndex - 1])) { + pe_debug("Duplicate 11a rate: %d", + tempRateSet.rate[min]); + } else { + pRates->llaRates[aRateIndex++] = + tempRateSet.rate[min]; + } + } else if (sirIsBrate(tempRateSet.rate[min] & 0x7f)) { + if (bRateIndex >= SIR_NUM_11B_RATES) { + pe_debug("OOB, bRateIndex: %d", bRateIndex); + } else if (bRateIndex >= 1 && (tempRateSet.rate[min] == + pRates->llbRates[bRateIndex - 1])) { + pe_debug("Duplicate 11b rate: %d", + tempRateSet.rate[min]); + } else { + pRates->llbRates[bRateIndex++] = + tempRateSet.rate[min]; + } + } else { + pe_debug("%d is neither 11a nor 11b rate", + tempRateSet.rate[min]); + } + tempRateSet.rate[min] = 0xff; + } + + if (IS_DOT11_MODE_HT(pe_session->dot11mode) && + !lim_is_he_6ghz_band(pe_session)) { + val_len = SIZE_OF_SUPPORTED_MCS_SET; + if (wlan_mlme_get_cfg_str( + pRates->supportedMCSSet, + &mac->mlme_cfg->rates.supported_mcs_set, + &val_len) != QDF_STATUS_SUCCESS) { + pe_err("could not retrieve supportedMCSSet"); + return QDF_STATUS_E_FAILURE; + } + if (pe_session->nss == NSS_1x1_MODE) + pRates->supportedMCSSet[1] = 0; + + /* if supported MCS Set of the peer is passed in, then do the + * intersection, else use the MCS set from local CFG. + */ + if (pSupportedMCSSet) { + for (i = 0; i < SIR_MAC_MAX_SUPPORTED_MCS_SET; i++) + pRates->supportedMCSSet[i] &= + pSupportedMCSSet[i]; + } + + lim_dump_ht_mcs_mask(NULL, pRates->supportedMCSSet); + + if (pRates->supportedMCSSet[0] == 0) { + pe_debug("Incorrect MCS 0 - 7. They must be supported"); + pRates->supportedMCSSet[0] = 0xFF; + } + + pe_session->supported_nss_1x1 = + ((pRates->supportedMCSSet[1] != 0) ? false : true); + } + lim_populate_vht_mcs_set(mac, pRates, pVHTCaps, pe_session, + pe_session->nss, sta_ds); + + if (lim_check_valid_mcs_for_nss(pe_session, he_caps)) { + peer_he_caps = he_caps; + } else { + if (!bss_desc) { + pe_err("bssDescription is NULL"); + return QDF_STATUS_E_INVAL; + } + pBeaconStruct = qdf_mem_malloc(sizeof(tSchBeaconStruct)); + if (!pBeaconStruct) + return QDF_STATUS_E_NOMEM; + + lim_extract_ap_capabilities( + mac, (uint8_t *)bss_desc->ieFields, + lim_get_ielen_from_bss_description(bss_desc), + pBeaconStruct); + peer_he_caps = &pBeaconStruct->he_cap; + } + + lim_populate_he_mcs_set(mac, pRates, peer_he_caps, + pe_session, pe_session->nss); + + if (IS_DOT11_MODE_HE(pe_session->dot11mode) && he_caps) { + lim_calculate_he_nss(pRates, pe_session); + } else if (pe_session->vhtCapability) { + if ((pRates->vhtRxMCSMap & MCSMAPMASK2x2) == MCSMAPMASK2x2) + pe_session->nss = NSS_1x1_MODE; + } else if (pRates->supportedMCSSet[1] == 0) { + pe_session->nss = NSS_1x1_MODE; + } + pe_debug("nss 1x1 %d nss %d", pe_session->supported_nss_1x1, + pe_session->nss); + + if (pBeaconStruct) + qdf_mem_free(pBeaconStruct); + + return QDF_STATUS_SUCCESS; +} /*** lim_populate_peer_rate_set() ***/ + +/** + * lim_populate_matching_rate_set() -process the CFG rate sets and + * the rate sets received in the Assoc request on AP. + * @mac_ctx: pointer to global mac structure + * @sta_ds: station node + * @oper_rate_set: pointer to operating rate set + * @ext_rate_set: pointer to extended rate set + * @supported_mcs_set: pointer to supported rate set + * @session_entry: pointer to pe session entry + * @vht_caps: pointer to vht capabilities + * + * This is called at the time of Association Request + * processing on AP and while adding peer's context + * in IBSS role to process the CFG rate sets and + * the rate sets received in the Assoc request on AP + * or Beacon/Probe Response from peer in IBSS. + * + * 1. It makes the intersection between our own rate set + * and extended rate set and the ones received in the + * association request. + * 2. It creates a combined rate set of 12 rates max which + * comprised the basic and extended rates + * 3. It sorts the combined rate Set and copy it in the + * rate array of the pSTA descriptor + * + * The parser has already ensured unicity of the rates in the + * association request structure + * + * Return: QDF_STATUS_SUCCESS on success else QDF_STATUS_E_FAILURE + */ +QDF_STATUS lim_populate_matching_rate_set(struct mac_context *mac_ctx, + tpDphHashNode sta_ds, + tSirMacRateSet *oper_rate_set, + tSirMacRateSet *ext_rate_set, + uint8_t *supported_mcs_set, + struct pe_session *session_entry, + tDot11fIEVHTCaps *vht_caps, + tDot11fIEhe_cap *he_caps) +{ + tSirMacRateSet temp_rate_set; + tSirMacRateSet temp_rate_set2; + uint32_t i, j, val, min, is_arate; + uint32_t phy_mode; + uint8_t mcs_set[SIZE_OF_SUPPORTED_MCS_SET]; + struct supported_rates *rates; + uint8_t a_rate_index = 0; + uint8_t b_rate_index = 0; + qdf_size_t val_len; + + is_arate = 0; + + lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); + + /* copy operational rate set from session_entry */ + qdf_mem_copy((temp_rate_set.rate), (session_entry->rateSet.rate), + session_entry->rateSet.numRates); + temp_rate_set.numRates = (uint8_t) session_entry->rateSet.numRates; + + if (phy_mode == WNI_CFG_PHY_MODE_11G) { + qdf_mem_copy((temp_rate_set2.rate), + (session_entry->extRateSet.rate), + session_entry->extRateSet.numRates); + temp_rate_set2.numRates = + (uint8_t) session_entry->extRateSet.numRates; + } else { + temp_rate_set2.numRates = 0; + } + + lim_remove_membership_selectors(&temp_rate_set); + lim_remove_membership_selectors(&temp_rate_set2); + + /* + * absolute sum of both num_rates should be less than 12. following + * 16-bit sum avoids false condition where 8-bit arithmetic overflow + * might have caused total sum to be less than 12 + */ + if (((uint16_t)temp_rate_set.numRates + + (uint16_t)temp_rate_set2.numRates) > SIR_MAC_MAX_NUMBER_OF_RATES) { + pe_err("more than 12 rates in CFG"); + return QDF_STATUS_E_FAILURE; + } + + /* + * Handling of the rate set IEs is the following: + * - keep only rates that we support and that the station supports + * - sort and the rates into the pSta->rate array + */ + + /* Copy all rates in temp_rate_set, there are 12 rates max */ + for (i = 0; i < temp_rate_set2.numRates; i++) + temp_rate_set.rate[i + temp_rate_set.numRates] = + temp_rate_set2.rate[i]; + + temp_rate_set.numRates += temp_rate_set2.numRates; + + /* + * Sort rates in temp_rate_set (they are likely to be already sorted) + * put the result in temp_rate_set2 + */ + temp_rate_set2.numRates = 0; + + for (i = 0; i < temp_rate_set.numRates; i++) { + min = 0; + val = 0xff; + + for (j = 0; j < temp_rate_set.numRates; j++) + if ((uint32_t) (temp_rate_set.rate[j] & 0x7f) < val) { + val = temp_rate_set.rate[j] & 0x7f; + min = j; + } + + temp_rate_set2.rate[temp_rate_set2.numRates++] = + temp_rate_set.rate[min]; + temp_rate_set.rate[min] = 0xff; + } + + /* + * Copy received rates in temp_rate_set, the parser has ensured + * unicity of the rates so there cannot be more than 12 + */ + for (i = 0; (i < oper_rate_set->numRates && + i < WLAN_SUPPORTED_RATES_IE_MAX_LEN); i++) + temp_rate_set.rate[i] = oper_rate_set->rate[i]; + + temp_rate_set.numRates = oper_rate_set->numRates; + + pe_debug("Sum of SUPPORTED and EXTENDED Rate Set (%1d)", + temp_rate_set.numRates + ext_rate_set->numRates); + + if (ext_rate_set->numRates && + ((temp_rate_set.numRates + ext_rate_set->numRates) > 12) && + temp_rate_set.numRates < 12) { + int found = 0; + int tail = temp_rate_set.numRates; + + for (i = 0; (i < ext_rate_set->numRates && + i < WLAN_SUPPORTED_RATES_IE_MAX_LEN); i++) { + found = 0; + for (j = 0; j < (uint32_t) tail; j++) { + if ((temp_rate_set.rate[j] & 0x7F) == + (ext_rate_set->rate[i] & 0x7F)) { + found = 1; + break; + } + } + + if (!found) { + temp_rate_set.rate[temp_rate_set.numRates++] = + ext_rate_set->rate[i]; + if (temp_rate_set.numRates >= 12) + break; + } + } + } else if (ext_rate_set->numRates && + ((temp_rate_set.numRates + ext_rate_set->numRates) <= 12)) { + for (j = 0; ((j < ext_rate_set->numRates) && + (j < WLAN_SUPPORTED_RATES_IE_MAX_LEN) && + ((i + j) < WLAN_SUPPORTED_RATES_IE_MAX_LEN)); j++) + temp_rate_set.rate[i + j] = ext_rate_set->rate[j]; + + temp_rate_set.numRates += ext_rate_set->numRates; + } else if (ext_rate_set->numRates) { + pe_debug("Relying only on the SUPPORTED Rate Set IE"); + } + + rates = &sta_ds->supportedRates; + qdf_mem_zero(rates, sizeof(*rates)); + for (i = 0; (i < temp_rate_set2.numRates && + i < WLAN_SUPPORTED_RATES_IE_MAX_LEN); i++) { + for (j = 0; (j < temp_rate_set.numRates && + j < WLAN_SUPPORTED_RATES_IE_MAX_LEN); j++) { + if ((temp_rate_set2.rate[i] & 0x7F) != + (temp_rate_set.rate[j] & 0x7F)) + continue; + + if (sirIsArate(temp_rate_set2.rate[i] & 0x7f) && + a_rate_index < SIR_NUM_11A_RATES) { + is_arate = 1; + rates->llaRates[a_rate_index++] = + temp_rate_set2.rate[i]; + } else if ((b_rate_index < SIR_NUM_11B_RATES) && + !(sirIsArate(temp_rate_set2.rate[i] & 0x7f))) { + rates->llbRates[b_rate_index++] = + temp_rate_set2.rate[i]; + } + break; + } + } + + /* + * Now add the Polaris rates only when Proprietary rates are enabled. + * compute the matching MCS rate set, if peer is 11n capable and self + * mode is 11n + */ +#ifdef FEATURE_WLAN_TDLS + if (sta_ds->mlmStaContext.htCapability) +#else + if (IS_DOT11_MODE_HT(session_entry->dot11mode) && + (sta_ds->mlmStaContext.htCapability)) +#endif + { + val_len = SIZE_OF_SUPPORTED_MCS_SET; + if (wlan_mlme_get_cfg_str( + mcs_set, + &mac_ctx->mlme_cfg->rates.supported_mcs_set, + &val_len) != QDF_STATUS_SUCCESS) { + pe_err("could not retrieve supportedMCSet"); + return QDF_STATUS_E_FAILURE; + } + + if (session_entry->nss == NSS_1x1_MODE) + mcs_set[1] = 0; + + for (i = 0; i < val_len; i++) + sta_ds->supportedRates.supportedMCSSet[i] = + mcs_set[i] & supported_mcs_set[i]; + + lim_dump_ht_mcs_mask(mcs_set, + sta_ds->supportedRates.supportedMCSSet); + } + lim_populate_vht_mcs_set(mac_ctx, &sta_ds->supportedRates, vht_caps, + session_entry, session_entry->nss, sta_ds); + lim_populate_he_mcs_set(mac_ctx, &sta_ds->supportedRates, he_caps, + session_entry, session_entry->nss); + /* + * Set the erpEnabled bit if the phy is in G mode and at least + * one A rate is supported + */ + if ((phy_mode == WNI_CFG_PHY_MODE_11G) && is_arate) + sta_ds->erpEnabled = eHAL_SET; + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_populate_vht_caps() - populates vht capabilities based on input + * capabilities + * @input_caps: input capabilities based on which we format the vht + * capabilities + * + * function to populate the supported vht capabilities. + * + * Return: vht capabilities derived based on input parameters. + */ +static uint32_t lim_populate_vht_caps(tDot11fIEVHTCaps input_caps) +{ + uint32_t vht_caps; + + vht_caps = ((input_caps.maxMPDULen << SIR_MAC_VHT_CAP_MAX_MPDU_LEN) | + (input_caps.supportedChannelWidthSet << + SIR_MAC_VHT_CAP_SUPP_CH_WIDTH_SET) | + (input_caps.ldpcCodingCap << + SIR_MAC_VHT_CAP_LDPC_CODING_CAP) | + (input_caps.shortGI80MHz << + SIR_MAC_VHT_CAP_SHORTGI_80MHZ) | + (input_caps.shortGI160and80plus80MHz << + SIR_MAC_VHT_CAP_SHORTGI_160_80_80MHZ) | + (input_caps.txSTBC << SIR_MAC_VHT_CAP_TXSTBC) | + (input_caps.rxSTBC << SIR_MAC_VHT_CAP_RXSTBC) | + (input_caps.suBeamFormerCap << + SIR_MAC_VHT_CAP_SU_BEAMFORMER_CAP) | + (input_caps.suBeamformeeCap << + SIR_MAC_VHT_CAP_SU_BEAMFORMEE_CAP) | + (input_caps.csnofBeamformerAntSup << + SIR_MAC_VHT_CAP_CSN_BEAMORMER_ANT_SUP) | + (input_caps.numSoundingDim << + SIR_MAC_VHT_CAP_NUM_SOUNDING_DIM) | + (input_caps.muBeamformerCap << + SIR_MAC_VHT_CAP_NUM_BEAM_FORMER_CAP) | + (input_caps.muBeamformeeCap << + SIR_MAC_VHT_CAP_NUM_BEAM_FORMEE_CAP) | + (input_caps.vhtTXOPPS << + SIR_MAC_VHT_CAP_TXOPPS) | + (input_caps.htcVHTCap << + SIR_MAC_VHT_CAP_HTC_CAP) | + (input_caps.maxAMPDULenExp << + SIR_MAC_VHT_CAP_MAX_AMDU_LEN_EXPO) | + (input_caps.vhtLinkAdaptCap << + SIR_MAC_VHT_CAP_LINK_ADAPT_CAP) | + (input_caps.rxAntPattern << + SIR_MAC_VHT_CAP_RX_ANTENNA_PATTERN) | + (input_caps.txAntPattern << + SIR_MAC_VHT_CAP_TX_ANTENNA_PATTERN) | + (input_caps.extended_nss_bw_supp << + SIR_MAC_VHT_CAP_EXTD_NSS_BW)); + + return vht_caps; +} + +/** + * lim_update_he_stbc_capable() - Update stbc capable flag based on + * HE capability + * @add_sta_params: add sta related parameters + * + * Update stbc cpable flag based on HE capability + * + * Return: None + */ +#ifdef WLAN_FEATURE_11AX +static void lim_update_he_stbc_capable(tpAddStaParams add_sta_params) +{ + if (add_sta_params && + add_sta_params->he_capable && + add_sta_params->stbc_capable) + add_sta_params->stbc_capable = + add_sta_params->he_config.rx_stbc_lt_80mhz; +} +#else +static void lim_update_he_stbc_capable(tpAddStaParams add_sta_params) +{} +#endif + +/** + * lim_add_sta()- called to add an STA context at hardware + * @mac_ctx: pointer to global mac structure + * @sta_ds: station node + * @update_entry: set to true for updating the entry + * @session_entry: pe session entry + * + * This function is called to add an STA context at hardware + * whenever a STA is (Re) Associated. + * + * Return: QDF_STATUS_SUCCESS on success else QDF_STATUS failure codes + */ + +QDF_STATUS +lim_add_sta(struct mac_context *mac_ctx, + tpDphHashNode sta_ds, uint8_t update_entry, struct pe_session *session_entry) +{ + tpAddStaParams add_sta_params = NULL; + struct scheduler_msg msg_q = {0}; + QDF_STATUS ret_code = QDF_STATUS_SUCCESS; + tSirMacAddr sta_mac, *sta_Addr; + tpSirAssocReq assoc_req; + uint8_t i, nw_type_11b = 0; + tLimIbssPeerNode *peer_node; /* for IBSS mode */ + const uint8_t *p2p_ie = NULL; + tDot11fIEVHTCaps vht_caps; + struct mlme_vht_capabilities_info *vht_cap_info; + + vht_cap_info = &mac_ctx->mlme_cfg->vht_caps.vht_cap_info; + + sir_copy_mac_addr(sta_mac, session_entry->self_mac_addr); + + pe_debug("sessionid: %d update_entry = %d limsystemrole = %d", + session_entry->smeSessionId, update_entry, + GET_LIM_SYSTEM_ROLE(session_entry)); + + add_sta_params = qdf_mem_malloc(sizeof(tAddStaParams)); + if (!add_sta_params) + return QDF_STATUS_E_NOMEM; + + if (LIM_IS_AP_ROLE(session_entry) || LIM_IS_IBSS_ROLE(session_entry) || + LIM_IS_NDI_ROLE(session_entry)) + sta_Addr = &sta_ds->staAddr; +#ifdef FEATURE_WLAN_TDLS + /* SystemRole shouldn't be matter if staType is TDLS peer */ + else if (STA_ENTRY_TDLS_PEER == sta_ds->staType) + sta_Addr = &sta_ds->staAddr; +#endif + else + sta_Addr = &sta_mac; + + pe_debug(QDF_MAC_ADDR_FMT ": Subtype(Assoc/Reassoc): %d", + QDF_MAC_ADDR_REF(*sta_Addr), sta_ds->mlmStaContext.subType); + + qdf_mem_copy((uint8_t *) add_sta_params->staMac, + (uint8_t *) *sta_Addr, sizeof(tSirMacAddr)); + qdf_mem_copy((uint8_t *) add_sta_params->bssId, + session_entry->bssId, sizeof(tSirMacAddr)); + qdf_mem_copy(&add_sta_params->capab_info, + &sta_ds->mlmStaContext.capabilityInfo, + sizeof(add_sta_params->capab_info)); + + /* Copy legacy rates */ + qdf_mem_copy(&add_sta_params->supportedRates, + &sta_ds->supportedRates, + sizeof(sta_ds->supportedRates)); + + add_sta_params->assocId = sta_ds->assocId; + + add_sta_params->wmmEnabled = sta_ds->qosMode; + add_sta_params->listenInterval = sta_ds->mlmStaContext.listenInterval; + if (LIM_IS_AP_ROLE(session_entry) && + (sta_ds->mlmStaContext.subType == LIM_REASSOC)) { + /* + * TBD - need to remove this REASSOC check + * after fixinf rmmod issue + */ + add_sta_params->updateSta = sta_ds->mlmStaContext.updateContext; + } + sta_ds->valid = 0; + sta_ds->mlmStaContext.mlmState = eLIM_MLM_WT_ADD_STA_RSP_STATE; + + pe_debug("Assoc ID: %d wmmEnabled: %d listenInterval: %d", + add_sta_params->assocId, add_sta_params->wmmEnabled, + add_sta_params->listenInterval); + add_sta_params->staType = sta_ds->staType; + + add_sta_params->updateSta = update_entry; + + add_sta_params->status = QDF_STATUS_SUCCESS; + + /* Update VHT/HT Capability */ + if (LIM_IS_AP_ROLE(session_entry) || + LIM_IS_IBSS_ROLE(session_entry)) { + add_sta_params->htCapable = + sta_ds->mlmStaContext.htCapability && + session_entry->htCapability;; + add_sta_params->vhtCapable = + sta_ds->mlmStaContext.vhtCapability && + session_entry->vhtCapability; + } +#ifdef FEATURE_WLAN_TDLS + /* SystemRole shouldn't be matter if staType is TDLS peer */ + else if (STA_ENTRY_TDLS_PEER == sta_ds->staType) { + add_sta_params->htCapable = sta_ds->mlmStaContext.htCapability; + add_sta_params->vhtCapable = + sta_ds->mlmStaContext.vhtCapability; + } +#endif + else { + add_sta_params->htCapable = session_entry->htCapability; + add_sta_params->vhtCapable = session_entry->vhtCapability; + } + + pe_debug("updateSta: %d htcapable: %d vhtCapable: %d sta mac" + QDF_MAC_ADDR_FMT, add_sta_params->updateSta, + add_sta_params->htCapable, add_sta_params->vhtCapable, + QDF_MAC_ADDR_REF(add_sta_params->staMac)); + + /* + * If HT client is connected to SAP DUT and self cap is NSS = 2 then + * disable ASYNC DBS scan by sending WMI_VDEV_PARAM_SMPS_INTOLERANT + * to FW, because HT client's can't drop down chain using SMPS frames. + */ + if (!policy_mgr_is_hw_dbs_2x2_capable(mac_ctx->psoc) && + LIM_IS_AP_ROLE(session_entry) && + (STA_ENTRY_PEER == sta_ds->staType) && + !add_sta_params->vhtCapable && + (session_entry->nss == 2)) { + session_entry->ht_client_cnt++; + if (session_entry->ht_client_cnt == 1) { + pe_debug("setting SMPS intolrent vdev_param"); + wma_cli_set_command(session_entry->smeSessionId, + (int)WMI_VDEV_PARAM_SMPS_INTOLERANT, + 1, VDEV_CMD); + } + } + + lim_update_sta_he_capable(mac_ctx, add_sta_params, sta_ds, + session_entry); + + add_sta_params->maxAmpduDensity = sta_ds->htAMpduDensity; + add_sta_params->maxAmpduSize = sta_ds->htMaxRxAMpduFactor; + add_sta_params->fShortGI20Mhz = sta_ds->htShortGI20Mhz; + add_sta_params->fShortGI40Mhz = sta_ds->htShortGI40Mhz; + add_sta_params->ch_width = sta_ds->ch_width; + add_sta_params->mimoPS = sta_ds->htMIMOPSState; + + pe_debug("maxAmpduDensity: %d maxAmpduDensity: %d", + add_sta_params->maxAmpduDensity, add_sta_params->maxAmpduSize); + + pe_debug("fShortGI20Mhz: %d fShortGI40Mhz: %d", + add_sta_params->fShortGI20Mhz, add_sta_params->fShortGI40Mhz); + + pe_debug("txChannelWidth: %d mimoPS: %d", add_sta_params->ch_width, + add_sta_params->mimoPS); + + if (add_sta_params->vhtCapable) { + if (sta_ds->vhtSupportedChannelWidthSet) + add_sta_params->ch_width = + sta_ds->vhtSupportedChannelWidthSet + 1; + + add_sta_params->vhtSupportedRxNss = sta_ds->vhtSupportedRxNss; + if (LIM_IS_AP_ROLE(session_entry) || + LIM_IS_P2P_DEVICE_GO(session_entry)) + add_sta_params->vhtSupportedRxNss = QDF_MIN( + add_sta_params->vhtSupportedRxNss, + session_entry->nss); + add_sta_params->vhtTxBFCapable = +#ifdef FEATURE_WLAN_TDLS + ((STA_ENTRY_PEER == sta_ds->staType) + || (STA_ENTRY_TDLS_PEER == sta_ds->staType)) ? + sta_ds->vhtBeamFormerCapable : + session_entry->vht_config.su_beam_formee; +#else + (STA_ENTRY_PEER == sta_ds->staType) ? + sta_ds->vhtBeamFormerCapable : + session_entry->vht_config.su_beam_formee; +#endif + add_sta_params->enable_su_tx_bformer = + sta_ds->vht_su_bfee_capable; + add_sta_params->vht_mcs_10_11_supp = + sta_ds->vht_mcs_10_11_supp; + } + + pe_debug("TxChWidth %d vhtTxBFCap %d, su_bfer %d, vht_mcs11 %d", + add_sta_params->ch_width, add_sta_params->vhtTxBFCapable, + add_sta_params->enable_su_tx_bformer, + add_sta_params->vht_mcs_10_11_supp); +#ifdef FEATURE_WLAN_TDLS + if ((STA_ENTRY_PEER == sta_ds->staType) || + (STA_ENTRY_TDLS_PEER == sta_ds->staType)) +#else + if (STA_ENTRY_PEER == sta_ds->staType) +#endif + { + /* + * peer STA get the LDPC capability from sta_ds, + * which populated from + * HT/VHT capability + */ + if (add_sta_params->vhtTxBFCapable + && vht_cap_info->disable_ldpc_with_txbf_ap) { + add_sta_params->htLdpcCapable = 0; + add_sta_params->vhtLdpcCapable = 0; + } else { + if (session_entry->txLdpcIniFeatureEnabled & 0x1) + add_sta_params->htLdpcCapable = + sta_ds->htLdpcCapable; + else + add_sta_params->htLdpcCapable = 0; + + if (session_entry->txLdpcIniFeatureEnabled & 0x2) + add_sta_params->vhtLdpcCapable = + sta_ds->vhtLdpcCapable; + else + add_sta_params->vhtLdpcCapable = 0; + } + } else if (STA_ENTRY_SELF == sta_ds->staType) { + /* For Self STA get the LDPC capability from config.ini */ + add_sta_params->htLdpcCapable = + (session_entry->txLdpcIniFeatureEnabled & 0x01); + add_sta_params->vhtLdpcCapable = + ((session_entry->txLdpcIniFeatureEnabled >> 1) & 0x01); + } + + /* Update PE session ID */ + add_sta_params->sessionId = session_entry->peSessionId; + + /* Update SME session ID */ + add_sta_params->smesessionId = session_entry->smeSessionId; + + add_sta_params->maxTxPower = session_entry->maxTxPower; + + if (session_entry->parsedAssocReq) { + uint16_t aid = sta_ds->assocId; + /* Get a copy of the already parsed Assoc Request */ + assoc_req = + (tpSirAssocReq) session_entry->parsedAssocReq[aid]; + if (assoc_req && assoc_req->addIEPresent + && assoc_req->addIE.length) { + p2p_ie = limGetP2pIEPtr(mac_ctx, + assoc_req->addIE.addIEdata, + assoc_req->addIE.length); + } + + add_sta_params->p2pCapableSta = (p2p_ie != NULL); + if (assoc_req && add_sta_params->htCapable) { + qdf_mem_copy(&add_sta_params->ht_caps, + ((uint8_t *) &assoc_req->HTCaps) + 1, + sizeof(add_sta_params->ht_caps)); + } + + if (assoc_req && add_sta_params->vhtCapable) { + if (assoc_req->vendor_vht_ie.VHTCaps.present) + vht_caps = assoc_req->vendor_vht_ie.VHTCaps; + else + vht_caps = assoc_req->VHTCaps; + add_sta_params->vht_caps = + lim_populate_vht_caps(vht_caps); + } + + lim_add_he_cap(mac_ctx, session_entry, + add_sta_params, assoc_req); + + } else if (LIM_IS_IBSS_ROLE(session_entry)) { + + /* + * in IBSS mode, use peer node as the source of ht_caps + * and vht_caps + */ + peer_node = lim_ibss_peer_find(mac_ctx, *sta_Addr); + if (!peer_node) { + pe_err("Can't find IBSS peer node for ADD_STA"); + return QDF_STATUS_E_NOENT; + } + + if (peer_node->atimIePresent) { + add_sta_params->atimIePresent = + peer_node->atimIePresent; + add_sta_params->peerAtimWindowLength = + peer_node->peerAtimWindowLength; + } + + add_sta_params->ht_caps = + (peer_node->htSupportedChannelWidthSet << + SIR_MAC_HT_CAP_CHWIDTH40_S) | + (peer_node->htGreenfield << + SIR_MAC_HT_CAP_GREENFIELD_S) | + (peer_node->htShortGI20Mhz << + SIR_MAC_HT_CAP_SHORTGI20MHZ_S) | + (peer_node->htShortGI40Mhz << + SIR_MAC_HT_CAP_SHORTGI40MHZ_S) | + (SIR_MAC_TXSTBC << + SIR_MAC_HT_CAP_TXSTBC_S) | + (SIR_MAC_RXSTBC << + SIR_MAC_HT_CAP_RXSTBC_S) | + (peer_node->htMaxAmsduLength << + SIR_MAC_HT_CAP_MAXAMSDUSIZE_S) | + (peer_node->htDsssCckRate40MHzSupport << + SIR_MAC_HT_CAP_DSSSCCK40_S); + + add_sta_params->vht_caps = + lim_populate_vht_caps(peer_node->VHTCaps); + } +#ifdef FEATURE_WLAN_TDLS + if (STA_ENTRY_TDLS_PEER == sta_ds->staType) { + add_sta_params->ht_caps = sta_ds->ht_caps; + add_sta_params->vht_caps = sta_ds->vht_caps; + if (add_sta_params->vhtCapable) { + add_sta_params->maxAmpduSize = + SIR_MAC_GET_VHT_MAX_AMPDU_EXPO( + sta_ds->vht_caps); + } + pe_debug("Sta type is TDLS_PEER, ht_caps: 0x%x, vht_caps: 0x%x", + add_sta_params->ht_caps, + add_sta_params->vht_caps); + } +#endif + +#ifdef FEATURE_WLAN_TDLS + if (sta_ds->wmeEnabled && + (LIM_IS_AP_ROLE(session_entry) || + (STA_ENTRY_TDLS_PEER == sta_ds->staType))) +#else + if (sta_ds->wmeEnabled && LIM_IS_AP_ROLE(session_entry)) +#endif + { + add_sta_params->uAPSD = 0; + /* + * update UAPSD and send it to LIM to add STA + * bitmap MSB <- LSB MSB 4 bits are for + * trigger enabled AC setting and LSB 4 bits + * are for delivery enabled AC setting + * 7 6 5 4 3 2 1 0 + * BE BK VI VO BE BK VI VO + */ + add_sta_params->uAPSD |= + sta_ds->qos.capability.qosInfo.acvo_uapsd; + add_sta_params->uAPSD |= + (sta_ds->qos.capability.qosInfo.acvi_uapsd << 1); + add_sta_params->uAPSD |= + (sta_ds->qos.capability.qosInfo.acbk_uapsd << 2); + add_sta_params->uAPSD |= + (sta_ds->qos.capability.qosInfo.acbe_uapsd << 3); + /* + * making delivery enabled and + * trigger enabled setting the same. + */ + add_sta_params->uAPSD |= add_sta_params->uAPSD << 4; + + add_sta_params->maxSPLen = + sta_ds->qos.capability.qosInfo.maxSpLen; + pe_debug("uAPSD = 0x%x, maxSpLen = %d", + add_sta_params->uAPSD, add_sta_params->maxSPLen); + } +#ifdef WLAN_FEATURE_11W + add_sta_params->rmfEnabled = sta_ds->rmfEnabled; + pe_debug("PMF enabled %d", add_sta_params->rmfEnabled); +#endif + + pe_debug("htLdpcCapable: %d vhtLdpcCapable: %d " + "p2pCapableSta: %d", + add_sta_params->htLdpcCapable, add_sta_params->vhtLdpcCapable, + add_sta_params->p2pCapableSta); + + if (!add_sta_params->htLdpcCapable) + add_sta_params->ht_caps &= ~(1 << SIR_MAC_HT_CAP_ADVCODING_S); + if (!add_sta_params->vhtLdpcCapable) + add_sta_params->vht_caps &= + ~(1 << SIR_MAC_VHT_CAP_LDPC_CODING_CAP); + + /* + * we need to defer the message until we get the + * response back from HAL. + */ + SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, false); + + add_sta_params->nwType = session_entry->nwType; + + if (!(add_sta_params->htCapable || add_sta_params->vhtCapable)) { + nw_type_11b = 1; + for (i = 0; i < SIR_NUM_11A_RATES; i++) { + if (sirIsArate(sta_ds->supportedRates.llaRates[i] & + 0x7F)) { + nw_type_11b = 0; + break; + } + } + if (nw_type_11b) + add_sta_params->nwType = eSIR_11B_NW_TYPE; + } + + if (add_sta_params->htCapable && session_entry->ht_config.ht_tx_stbc) { + struct sDot11fIEHTCaps *ht_caps = (struct sDot11fIEHTCaps *) + &add_sta_params->ht_caps; + if (ht_caps->rxSTBC) + add_sta_params->stbc_capable = 1; + else + add_sta_params->stbc_capable = 0; + } + + if (add_sta_params->vhtCapable && add_sta_params->stbc_capable) { + struct sDot11fIEVHTCaps *vht_caps = (struct sDot11fIEVHTCaps *) + &add_sta_params->vht_caps; + if (vht_caps->rxSTBC) + add_sta_params->stbc_capable = 1; + else + add_sta_params->stbc_capable = 0; + } + + if (session_entry->opmode == QDF_SAP_MODE || + session_entry->opmode == QDF_P2P_GO_MODE) { + if (session_entry->parsedAssocReq) { + uint16_t aid = sta_ds->assocId; + /* Get a copy of the already parsed Assoc Request */ + assoc_req = + (tpSirAssocReq) session_entry->parsedAssocReq[aid]; + + if (assoc_req) { + add_sta_params->wpa_rsn = assoc_req->rsnPresent; + add_sta_params->wpa_rsn |= + (assoc_req->wpaPresent << 1); + } + } + } + + lim_update_he_stbc_capable(add_sta_params); + + msg_q.type = WMA_ADD_STA_REQ; + msg_q.reserved = 0; + msg_q.bodyptr = add_sta_params; + msg_q.bodyval = 0; + + pe_debug("Sending WMA_ADD_STA_REQ for assocId %d", sta_ds->assocId); + MTRACE(mac_trace_msg_tx(mac_ctx, session_entry->peSessionId, + msg_q.type)); + + ret_code = wma_post_ctrl_msg(mac_ctx, &msg_q); + if (QDF_STATUS_SUCCESS != ret_code) { + SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true); + pe_err("ADD_STA_REQ for aId %d failed (reason %X)", + sta_ds->assocId, ret_code); + qdf_mem_free(add_sta_params); + } + + return ret_code; +} + +/** + * lim_del_sta() + * + ***FUNCTION: + * This function is called to delete an STA context at hardware + * whenever a STA is disassociated + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param sta - Pointer to the STA datastructure created by + * LIM and maintained by DPH + * @param fRespReqd - flag to indicate whether the delete is synchronous (true) + * or not (false) + * @return retCode - Indicates success or failure return code + */ + +QDF_STATUS +lim_del_sta(struct mac_context *mac, + tpDphHashNode sta, bool fRespReqd, struct pe_session *pe_session) +{ + tpDeleteStaParams pDelStaParams = NULL; + struct scheduler_msg msgQ = {0}; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + + pDelStaParams = qdf_mem_malloc(sizeof(tDeleteStaParams)); + if (!pDelStaParams) + return QDF_STATUS_E_NOMEM; + + /* + * 2G-AS platform: SAP associates with HT (11n)clients as 2x1 in 2G and + * 2X2 in 5G + * Non-2G-AS platform: SAP associates with HT (11n) clients as 2X2 in 2G + * and 5G; and enable async dbs scan when all HT clients are gone + * 5G-AS: Don't care + */ + if (!policy_mgr_is_hw_dbs_2x2_capable(mac->psoc) && + LIM_IS_AP_ROLE(pe_session) && + (sta->staType == STA_ENTRY_PEER) && + !sta->mlmStaContext.vhtCapability && + (pe_session->nss == 2)) { + pe_session->ht_client_cnt--; + if (pe_session->ht_client_cnt == 0) { + pe_debug("clearing SMPS intolrent vdev_param"); + wma_cli_set_command(pe_session->smeSessionId, + (int)WMI_VDEV_PARAM_SMPS_INTOLERANT, + 0, VDEV_CMD); + } + } + + pDelStaParams->assocId = sta->assocId; + sta->valid = 0; + + if (!fRespReqd) + pDelStaParams->respReqd = 0; + else { + if (!(IS_TDLS_PEER(sta->staType))) { + /* when lim_del_sta is called from processSmeAssocCnf + * then mlmState is already set properly. */ + if (eLIM_MLM_WT_ASSOC_DEL_STA_RSP_STATE != + GET_LIM_STA_CONTEXT_MLM_STATE(sta)) { + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, + pe_session->peSessionId, + eLIM_MLM_WT_DEL_STA_RSP_STATE)); + SET_LIM_STA_CONTEXT_MLM_STATE(sta, + eLIM_MLM_WT_DEL_STA_RSP_STATE); + } + if (LIM_IS_STA_ROLE(pe_session)) { + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, + pe_session->peSessionId, + eLIM_MLM_WT_DEL_STA_RSP_STATE)); + + pe_session->limMlmState = + eLIM_MLM_WT_DEL_STA_RSP_STATE; + + } + } + + /* we need to defer the message until we get the + * response back from HAL. */ + SET_LIM_PROCESS_DEFD_MESGS(mac, false); + + pDelStaParams->respReqd = 1; + } + + /* Update PE session ID */ + pDelStaParams->sessionId = pe_session->peSessionId; + pDelStaParams->smesessionId = pe_session->smeSessionId; + + pDelStaParams->staType = sta->staType; + qdf_mem_copy((uint8_t *) pDelStaParams->staMac, + (uint8_t *) sta->staAddr, sizeof(tSirMacAddr)); + + pDelStaParams->status = QDF_STATUS_SUCCESS; + msgQ.type = WMA_DELETE_STA_REQ; + msgQ.reserved = 0; + msgQ.bodyptr = pDelStaParams; + msgQ.bodyval = 0; + + pe_debug("Sessionid %d :Sending SIR_HAL_DELETE_STA_REQ " + "for mac_addr "QDF_MAC_ADDR_FMT" and AssocID: %d MAC : " + QDF_MAC_ADDR_FMT, pDelStaParams->sessionId, + QDF_MAC_ADDR_REF(pDelStaParams->staMac), + pDelStaParams->assocId, + QDF_MAC_ADDR_REF(sta->staAddr)); + + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + if (fRespReqd) + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + pe_err("Posting DELETE_STA_REQ to HAL failed, reason=%X", + retCode); + qdf_mem_free(pDelStaParams); + } + + return retCode; +} + +/** + * lim_set_mbssid_info() - Save mbssid info + * @pe_session: pe session entry + * + * Return: None + */ +static void lim_set_mbssid_info(struct pe_session *pe_session) +{ + struct scan_mbssid_info *mbssid_info; + + mbssid_info = &pe_session->lim_join_req->bssDescription.mbssid_info; + mlme_set_mbssid_info(pe_session->vdev, mbssid_info); +} + +/** + * lim_add_sta_self() + * + ***FUNCTION: + * This function is called to add an STA context at hardware + * whenever a STA is (Re) Associated. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param sta - Pointer to the STA datastructure created by + * LIM and maintained by DPH + * @return retCode - Indicates success or failure return code + */ + +QDF_STATUS +lim_add_sta_self(struct mac_context *mac, uint8_t updateSta, + struct pe_session *pe_session) +{ + tpAddStaParams pAddStaParams = NULL; + struct scheduler_msg msgQ = {0}; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + tSirMacAddr staMac; + uint32_t listenInterval = MLME_CFG_LISTEN_INTERVAL; + /*This self Sta dot 11 mode comes from the cfg and the expectation here is + * that cfg carries the systemwide capability that device under + * consideration can support. This capability gets plumbed into the cfg + * cache at system initialization time via the .dat and .ini file override + * mechanisms and will not change. If it does change, it is the + * responsibility of SME to evict the selfSta and reissue a new AddStaSelf + * command.*/ + uint32_t selfStaDot11Mode = 0; + + selfStaDot11Mode = mac->mlme_cfg->dot11_mode.dot11_mode; + + sir_copy_mac_addr(staMac, pe_session->self_mac_addr); + pAddStaParams = qdf_mem_malloc(sizeof(tAddStaParams)); + if (!pAddStaParams) + return QDF_STATUS_E_NOMEM; + + /* / Add STA context at MAC HW (BMU, RHP & TFP) */ + qdf_mem_copy((uint8_t *) pAddStaParams->staMac, + (uint8_t *) staMac, sizeof(tSirMacAddr)); + + qdf_mem_copy((uint8_t *) pAddStaParams->bssId, + pe_session->bssId, sizeof(tSirMacAddr)); + + pAddStaParams->assocId = pe_session->limAID; + pAddStaParams->staType = STA_ENTRY_SELF; + pAddStaParams->status = QDF_STATUS_SUCCESS; + + /* Update PE session ID */ + pAddStaParams->sessionId = pe_session->peSessionId; + + /* Update SME session ID */ + pAddStaParams->smesessionId = pe_session->smeSessionId; + + pAddStaParams->maxTxPower = pe_session->maxTxPower; + + pAddStaParams->updateSta = updateSta; + + lim_set_mbssid_info(pe_session); + + lim_populate_own_rate_set(mac, &pAddStaParams->supportedRates, + NULL, false, + pe_session, NULL, NULL); + if (IS_DOT11_MODE_HT(selfStaDot11Mode)) { + pAddStaParams->htCapable = true; + + pAddStaParams->ch_width = + mac->roam.configParam.channelBondingMode5GHz; + pAddStaParams->mimoPS = + lim_get_ht_capability(mac, eHT_MIMO_POWER_SAVE, + pe_session); + pAddStaParams->maxAmpduDensity = + lim_get_ht_capability(mac, eHT_MPDU_DENSITY, + pe_session); + pAddStaParams->maxAmpduSize = + lim_get_ht_capability(mac, eHT_MAX_RX_AMPDU_FACTOR, + pe_session); + pAddStaParams->fShortGI20Mhz = pe_session->ht_config.ht_sgi20; + pAddStaParams->fShortGI40Mhz = pe_session->ht_config.ht_sgi40; + } + pAddStaParams->vhtCapable = pe_session->vhtCapability; + if (pAddStaParams->vhtCapable) + pAddStaParams->ch_width = + pe_session->ch_width; + + pAddStaParams->vhtTxBFCapable = + pe_session->vht_config.su_beam_formee; + pAddStaParams->enable_su_tx_bformer = + pe_session->vht_config.su_beam_former; + + /* In 11ac mode, the hardware is capable of supporting 128K AMPDU size */ + if (pe_session->vhtCapability) + pAddStaParams->maxAmpduSize = + mac->mlme_cfg->vht_caps.vht_cap_info.ampdu_len_exponent; + + pAddStaParams->vhtTxMUBformeeCapable = + pe_session->vht_config.mu_beam_formee; + pAddStaParams->enableVhtpAid = pe_session->enableVhtpAid; + pAddStaParams->enableAmpduPs = pe_session->enableAmpduPs; + pAddStaParams->enableHtSmps = (pe_session->enableHtSmps && + (!pe_session->supported_nss_1x1)); + pAddStaParams->htSmpsconfig = pe_session->htSmpsvalue; + pAddStaParams->send_smps_action = + pe_session->send_smps_action; + + /* For Self STA get the LDPC capability from session i.e config.ini */ + pAddStaParams->htLdpcCapable = + (pe_session->txLdpcIniFeatureEnabled & 0x01); + pAddStaParams->vhtLdpcCapable = + ((pe_session->txLdpcIniFeatureEnabled >> 1) & 0x01); + + listenInterval = mac->mlme_cfg->sap_cfg.listen_interval; + pAddStaParams->listenInterval = (uint16_t) listenInterval; + + if (QDF_P2P_CLIENT_MODE == pe_session->opmode) + pAddStaParams->p2pCapableSta = 1; + + if (pe_session->isNonRoamReassoc) { + pAddStaParams->nonRoamReassoc = 1; + pe_session->isNonRoamReassoc = 0; + } + + if (IS_DOT11_MODE_HE(selfStaDot11Mode)) + lim_add_self_he_cap(pAddStaParams, pe_session); + + if (lim_is_fils_connection(pe_session)) + pAddStaParams->no_ptk_4_way = true; + + msgQ.type = WMA_ADD_STA_REQ; + msgQ.reserved = 0; + msgQ.bodyptr = pAddStaParams; + msgQ.bodyval = 0; + + pe_debug(QDF_MAC_ADDR_FMT ": vdev %d Sending WMA_ADD_STA_REQ.LI %d", + QDF_MAC_ADDR_REF(pAddStaParams->staMac), + pe_session->vdev_id, pAddStaParams->listenInterval); + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, msgQ.type)); + + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + pe_err("Posting WMA_ADD_STA_REQ to HAL failed, reason=%X", + retCode); + qdf_mem_free(pAddStaParams); + } + return retCode; +} + +/** + * lim_handle_cnf_wait_timeout() + * + ***FUNCTION: + * This function is called by limProcessMessageQueue to handle + * various confirmation failure cases. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param sta - Pointer to a sta descriptor + * @return None + */ + +void lim_handle_cnf_wait_timeout(struct mac_context *mac, uint16_t staId) +{ + tpDphHashNode sta; + struct pe_session *pe_session = NULL; + + pe_session = pe_find_session_by_session_id(mac, + mac->lim.lim_timers.gpLimCnfWaitTimer[staId].sessionId); + if (!pe_session) { + pe_err("Session Does not exist for given sessionID"); + return; + } + sta = dph_get_hash_entry(mac, staId, &pe_session->dph.dphHashTable); + + if (!sta) { + pe_err("No STA context in SIR_LIM_CNF_WAIT_TIMEOUT"); + return; + } + + switch (sta->mlmStaContext.mlmState) { + case eLIM_MLM_WT_ASSOC_CNF_STATE: + pe_debug("Did not receive Assoc Cnf in eLIM_MLM_WT_ASSOC_CNF_STATE sta Assoc id %d", + sta->assocId); + lim_print_mac_addr(mac, sta->staAddr, LOGD); + + if (LIM_IS_AP_ROLE(pe_session)) { + lim_reject_association(mac, sta->staAddr, + sta->mlmStaContext.subType, + true, + sta->mlmStaContext.authType, + sta->assocId, true, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + pe_session); + } + break; + + default: + pe_warn("Received CNF_WAIT_TIMEOUT in state %d", + sta->mlmStaContext.mlmState); + } +} + +/** + * lim_delete_dph_hash_entry()- function to delete dph hash entry + * @mac_ctx: pointer to global mac structure + * @sta_addr: peer station address + * @sta_id: id assigned to peer station + * @session_entry: pe session entry + * + * This function is called whenever we need to delete + * the dph hash entry + * + * Return: none + */ + +void +lim_delete_dph_hash_entry(struct mac_context *mac_ctx, tSirMacAddr sta_addr, + uint16_t sta_id, struct pe_session *session_entry) +{ + uint16_t aid; + tpDphHashNode sta_ds; + tUpdateBeaconParams beacon_params; + + qdf_mem_zero(&beacon_params, sizeof(tUpdateBeaconParams)); + beacon_params.paramChangeBitmap = 0; + lim_deactivate_and_change_per_sta_id_timer(mac_ctx, eLIM_CNF_WAIT_TIMER, + sta_id); + if (!session_entry) { + pe_err("NULL session_entry"); + return; + } + + beacon_params.bss_idx = session_entry->vdev_id; + sta_ds = dph_lookup_hash_entry(mac_ctx, sta_addr, &aid, + &session_entry->dph.dphHashTable); + + if (!sta_ds) { + pe_err("sta_ds is NULL"); + return; + } + + pe_debug("Deleting DPH Hash entry sta mac " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sta_addr)); + /* + * update the station count and perform associated actions + * do this before deleting the dph hash entry + */ + lim_util_count_sta_del(mac_ctx, sta_ds, session_entry); + + if (LIM_IS_AP_ROLE(session_entry) || LIM_IS_IBSS_ROLE(session_entry)) { + if (LIM_IS_AP_ROLE(session_entry)) { + if (session_entry->gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) + lim_decide_ap_protection_on_delete(mac_ctx, + sta_ds, &beacon_params, session_entry); + } + + if (sta_ds->non_ecsa_capable) { + if (session_entry->lim_non_ecsa_cap_num == 0) { + pe_debug("NonECSA sta 0, id %d is ecsa", + sta_id); + } else { + session_entry->lim_non_ecsa_cap_num--; + pe_debug("reducing the non ECSA num to %d", + session_entry->lim_non_ecsa_cap_num); + } + } + + if (LIM_IS_IBSS_ROLE(session_entry)) + lim_ibss_decide_protection_on_delete(mac_ctx, sta_ds, + &beacon_params, session_entry); + + lim_decide_short_preamble(mac_ctx, sta_ds, &beacon_params, + session_entry); + lim_decide_short_slot(mac_ctx, sta_ds, &beacon_params, + session_entry); + + /* Send message to HAL about beacon parameter change. */ + pe_debug("param bitmap: %d", beacon_params.paramChangeBitmap); + if (beacon_params.paramChangeBitmap && + (false == + mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running)) { + sch_set_fixed_beacon_fields(mac_ctx, session_entry); + lim_send_beacon_params(mac_ctx, &beacon_params, + session_entry); + } + + lim_obss_send_detection_cfg(mac_ctx, session_entry, false); + +#ifdef WLAN_FEATURE_11W + if (sta_ds->rmfEnabled) { + pe_debug("delete pmf timer assoc-id:%d sta mac " + QDF_MAC_ADDR_FMT, sta_ds->assocId, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + tx_timer_delete(&sta_ds->pmfSaQueryTimer); + } +#endif + } + + if (dph_delete_hash_entry(mac_ctx, sta_addr, sta_id, + &session_entry->dph.dphHashTable) != QDF_STATUS_SUCCESS) + pe_err("error deleting hash entry"); + lim_ap_check_6g_compatible_peer(mac_ctx, session_entry); +} + +/** + * lim_check_and_announce_join_success()- function to check if the received + * Beacon/Probe Response is from the BSS that we're attempting to join. + * @mac: pointer to global mac structure + * @beacon_probe_rsp: pointer to reveived beacon/probe response frame + * @header: pointer to received management frame header + * @session_entry: pe session entry + * + * This function is called upon receiving Beacon/Probe Response + * frame in WT_JOIN_BEACON_STATE to check if the received + * Beacon/Probe Response is from the BSS that we're attempting + * to join. + * If the Beacon/Probe Response is indeed from the BSS we're + * attempting to join, join success is sent to SME. + * + * Return: none + */ + +void +lim_check_and_announce_join_success(struct mac_context *mac_ctx, + tSirProbeRespBeacon *beacon_probe_rsp, tpSirMacMgmtHdr header, + struct pe_session *session_entry) +{ + tSirMacSSid current_ssid; + tLimMlmJoinCnf mlm_join_cnf; + uint32_t val; + uint32_t *noa_duration_from_beacon = NULL; + uint32_t *noa2_duration_from_beacon = NULL; + uint32_t noa; + uint32_t total_num_noa_desc = 0; + + qdf_mem_copy(current_ssid.ssId, + session_entry->ssId.ssId, session_entry->ssId.length); + + current_ssid.length = (uint8_t) session_entry->ssId.length; + + /* + * Check for SSID only in probe response. Beacons may not carry + * SSID information in hidden SSID case + */ + if (((SIR_MAC_MGMT_FRAME == header->fc.type) && + (SIR_MAC_MGMT_PROBE_RSP == header->fc.subType)) && + current_ssid.length && + (qdf_mem_cmp((uint8_t *) &beacon_probe_rsp->ssId, + (uint8_t *) ¤t_ssid, + (uint8_t) (1 + current_ssid.length)))) { + /* + * Received SSID does not match with the one we've. + * Ignore received Beacon frame + */ + pe_debug("SSID received in Beacon does not match"); +#ifdef WLAN_DEBUG + mac_ctx->lim.gLimBcnSSIDMismatchCnt++; +#endif + return; + } + + if (!LIM_IS_STA_ROLE(session_entry)) + return; + + pe_debug("Received Beacon/PR with BSSID:"QDF_MAC_ADDR_FMT" pe session %d vdev %d", + QDF_MAC_ADDR_REF(session_entry->bssId), + session_entry->peSessionId, + session_entry->vdev_id); + + /* Deactivate Join Failure timer */ + lim_deactivate_and_change_timer(mac_ctx, eLIM_JOIN_FAIL_TIMER); + /* Deactivate Periodic Join timer */ + lim_deactivate_and_change_timer(mac_ctx, + eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER); + + if (QDF_P2P_CLIENT_MODE == session_entry->opmode && + beacon_probe_rsp->P2PProbeRes.NoticeOfAbsence.present) { + + noa_duration_from_beacon = (uint32_t *) + (beacon_probe_rsp->P2PProbeRes.NoticeOfAbsence.NoADesc + 1); + + if (beacon_probe_rsp->P2PProbeRes.NoticeOfAbsence.num_NoADesc) + total_num_noa_desc = + beacon_probe_rsp->P2PProbeRes.NoticeOfAbsence. + num_NoADesc / SIZE_OF_NOA_DESCRIPTOR; + + noa = *noa_duration_from_beacon; + + if (total_num_noa_desc > 1) { + noa2_duration_from_beacon = (uint32_t *) + (beacon_probe_rsp->P2PProbeRes.NoticeOfAbsence.NoADesc + + SIZE_OF_NOA_DESCRIPTOR + 1); + noa += *noa2_duration_from_beacon; + } + + /* + * If MAX Noa exceeds 3 secs we will consider only 3 secs to + * avoid arbitrary values in noa duration field + */ + noa = noa > MAX_NOA_PERIOD_IN_MICROSECS ? + MAX_NOA_PERIOD_IN_MICROSECS : noa; + noa = noa / 1000; /* Convert to ms */ + + session_entry->defaultAuthFailureTimeout = + mac_ctx->mlme_cfg->timeouts.auth_failure_timeout; + val = mac_ctx->mlme_cfg->timeouts.auth_failure_timeout + noa; + if (cfg_in_range(CFG_AUTH_FAILURE_TIMEOUT, val)) + mac_ctx->mlme_cfg->timeouts.auth_failure_timeout = val; + else + mac_ctx->mlme_cfg->timeouts.auth_failure_timeout = + cfg_default(CFG_AUTH_FAILURE_TIMEOUT); + } else { + session_entry->defaultAuthFailureTimeout = 0; + } + + + /* + * Check if MBO Association disallowed subattr is present and post + * failure status to LIM if present + */ + if (!session_entry->ignore_assoc_disallowed && + beacon_probe_rsp->assoc_disallowed) { + pe_err("Connection fails due to assoc disallowed reason(%d):"QDF_MAC_ADDR_FMT" PESessionID %d", + beacon_probe_rsp->assoc_disallowed_reason, + QDF_MAC_ADDR_REF(session_entry->bssId), + session_entry->peSessionId); + mlm_join_cnf.resultCode = eSIR_SME_ASSOC_REFUSED; + mlm_join_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + session_entry->limMlmState = eLIM_MLM_IDLE_STATE; + mlm_join_cnf.sessionId = session_entry->peSessionId; + if (session_entry->pLimMlmJoinReq) { + qdf_mem_free(session_entry->pLimMlmJoinReq); + session_entry->pLimMlmJoinReq = NULL; + } + lim_post_sme_message(mac_ctx, LIM_MLM_JOIN_CNF, + (uint32_t *) &mlm_join_cnf); + return; + } + + /* Update Beacon Interval at CFG database */ + + if (beacon_probe_rsp->HTCaps.present) + lim_update_sta_run_time_ht_capability(mac_ctx, + &beacon_probe_rsp->HTCaps); + if (beacon_probe_rsp->HTInfo.present) + lim_update_sta_run_time_ht_info(mac_ctx, + &beacon_probe_rsp->HTInfo, session_entry); + session_entry->limMlmState = eLIM_MLM_JOINED_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, eLIM_MLM_JOINED_STATE)); + + /* + * update the capability info based on recently received beacon/probe + * response frame + */ + session_entry->limCurrentBssCaps = + lim_get_u16((uint8_t *)&beacon_probe_rsp->capabilityInfo); + + /* + * Announce join success by sending + * Join confirm to SME. + */ + mlm_join_cnf.resultCode = eSIR_SME_SUCCESS; + mlm_join_cnf.protStatusCode = eSIR_MAC_SUCCESS_STATUS; + /* Update PE sessionId */ + mlm_join_cnf.sessionId = session_entry->peSessionId; + lim_post_sme_message(mac_ctx, LIM_MLM_JOIN_CNF, + (uint32_t *) &mlm_join_cnf); + + if (session_entry->vhtCapability && + beacon_probe_rsp->vendor_vht_ie.VHTCaps.present) { + session_entry->is_vendor_specific_vhtcaps = true; + session_entry->vendor_specific_vht_ie_sub_type = + beacon_probe_rsp->vendor_vht_ie.sub_type; + pe_debug("VHT caps are present in vendor specific IE"); + } + + /* Update HS 2.0 Information Element */ + if (beacon_probe_rsp->hs20vendor_ie.present) { + pe_debug("HS20 Indication Element Present, rel#:%u, id:%u", + beacon_probe_rsp->hs20vendor_ie.release_num, + beacon_probe_rsp->hs20vendor_ie.hs_id_present); + qdf_mem_copy(&session_entry->hs20vendor_ie, + &beacon_probe_rsp->hs20vendor_ie, + sizeof(tDot11fIEhs20vendor_ie) - + sizeof(beacon_probe_rsp->hs20vendor_ie.hs_id)); + if (beacon_probe_rsp->hs20vendor_ie.hs_id_present) + qdf_mem_copy(&session_entry->hs20vendor_ie.hs_id, + &beacon_probe_rsp->hs20vendor_ie.hs_id, + sizeof(beacon_probe_rsp->hs20vendor_ie.hs_id)); + } +} + +/** + * lim_extract_ap_capabilities() + * + ***FUNCTION: + * This function is called to extract all of the AP's capabilities + * from the IEs received from it in Beacon/Probe Response frames + * + ***LOGIC: + * This routine mimics the lim_extract_ap_capability() API. The difference here + * is that this API returns the entire tSirProbeRespBeacon info as is. It is + * left to the caller of this API to use this info as required + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param pIE Pointer to starting IE in Beacon/Probe Response + * @param ieLen Length of all IEs combined + * @param beaconStruct A pointer to tSirProbeRespBeacon that needs to be + * populated + * @return status A status reporting QDF_STATUS_SUCCESS or QDF_STATUS_E_FAILURE + */ +QDF_STATUS lim_extract_ap_capabilities(struct mac_context *mac, + uint8_t *pIE, + uint16_t ieLen, + tpSirProbeRespBeacon beaconStruct) +{ + qdf_mem_zero((uint8_t *) beaconStruct, sizeof(tSirProbeRespBeacon)); + + /* Parse the Beacon IE's, Don't try to parse if we dont have anything in IE */ + if (ieLen > 0) { + if (QDF_STATUS_SUCCESS != + sir_parse_beacon_ie(mac, beaconStruct, pIE, + (uint32_t) ieLen)) { + pe_err("APCapExtract: Beacon parsing error!"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_del_bss() + * + ***FUNCTION: + * This function is called to delete BSS context at hardware + * whenever a STA is disassociated + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param sta - Pointer to the STA datastructure created by + * LIM and maintained by DPH + * @return retCode - Indicates success or failure return code + */ + +QDF_STATUS +lim_del_bss(struct mac_context *mac, tpDphHashNode sta, uint16_t bss_idx, + struct pe_session *pe_session) +{ + struct scheduler_msg msgQ = {0}; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + + /* DPH was storing the AssocID in staID field, */ + /* staID is actually assigned by HAL when AddSTA message is sent. */ + if (sta) { + sta->valid = 0; + sta->mlmStaContext.mlmState = eLIM_MLM_WT_DEL_BSS_RSP_STATE; + } + pe_session->limMlmState = eLIM_MLM_WT_DEL_BSS_RSP_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + eLIM_MLM_WT_DEL_BSS_RSP_STATE)); + + if ((pe_session->peSessionId == + mac->lim.lim_timers.gLimJoinFailureTimer.sessionId) + && (true == + tx_timer_running(&mac->lim.lim_timers.gLimJoinFailureTimer))) { + lim_deactivate_and_change_timer(mac, eLIM_JOIN_FAIL_TIMER); + } + + /* we need to defer the message until we get the response back from HAL. */ + SET_LIM_PROCESS_DEFD_MESGS(mac, false); + + if (pe_session->process_ho_fail) + msgQ.type = WMA_DELETE_BSS_HO_FAIL_REQ; + else + msgQ.type = WMA_DELETE_BSS_REQ; + msgQ.reserved = 0; + msgQ.bodyptr = NULL; + msgQ.bodyval = pe_session->smeSessionId; + + pe_debug("Sessionid %d : Sending HAL_DELETE_BSS_REQ BSSID:" QDF_MAC_ADDR_FMT, + pe_session->peSessionId, + QDF_MAC_ADDR_REF(pe_session->bssId)); + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, msgQ.type)); + + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + pe_err("Posting DELETE_BSS_REQ to HAL failed, reason=%X", + retCode); + } + + return retCode; +} + +/** + * lim_update_vhtcaps_assoc_resp : Update VHT caps in assoc response. + * @mac_ctx Pointer to Global MAC structure + * @pAddBssParams: parameters required for add bss params. + * @vht_caps: VHT capabilities. + * @pe_session : session entry. + * + * Return : void + */ +void lim_update_vhtcaps_assoc_resp(struct mac_context *mac_ctx, + struct bss_params *pAddBssParams, + tDot11fIEVHTCaps *vht_caps, + struct pe_session *pe_session) +{ + pAddBssParams->staContext.vht_caps = + ((vht_caps->maxMPDULen << + SIR_MAC_VHT_CAP_MAX_MPDU_LEN) | + (vht_caps->supportedChannelWidthSet << + SIR_MAC_VHT_CAP_SUPP_CH_WIDTH_SET) | + (vht_caps->ldpcCodingCap << + SIR_MAC_VHT_CAP_LDPC_CODING_CAP) | + (vht_caps->shortGI80MHz << + SIR_MAC_VHT_CAP_SHORTGI_80MHZ) | + (vht_caps->shortGI160and80plus80MHz << + SIR_MAC_VHT_CAP_SHORTGI_160_80_80MHZ) | + (vht_caps->txSTBC << + SIR_MAC_VHT_CAP_TXSTBC) | + (vht_caps->rxSTBC << + SIR_MAC_VHT_CAP_RXSTBC) | + (vht_caps->suBeamFormerCap << + SIR_MAC_VHT_CAP_SU_BEAMFORMER_CAP) | + (vht_caps->suBeamformeeCap << + SIR_MAC_VHT_CAP_SU_BEAMFORMEE_CAP) | + (vht_caps->csnofBeamformerAntSup << + SIR_MAC_VHT_CAP_CSN_BEAMORMER_ANT_SUP) | + (vht_caps->numSoundingDim << + SIR_MAC_VHT_CAP_NUM_SOUNDING_DIM) | + (vht_caps->muBeamformerCap << + SIR_MAC_VHT_CAP_NUM_BEAM_FORMER_CAP) | + (vht_caps->muBeamformeeCap << + SIR_MAC_VHT_CAP_NUM_BEAM_FORMEE_CAP) | + (vht_caps->vhtTXOPPS << + SIR_MAC_VHT_CAP_TXOPPS) | + (vht_caps->htcVHTCap << + SIR_MAC_VHT_CAP_HTC_CAP) | + (vht_caps->maxAMPDULenExp << + SIR_MAC_VHT_CAP_MAX_AMDU_LEN_EXPO) | + (vht_caps->vhtLinkAdaptCap << + SIR_MAC_VHT_CAP_LINK_ADAPT_CAP) | + (vht_caps->rxAntPattern << + SIR_MAC_VHT_CAP_RX_ANTENNA_PATTERN) | + (vht_caps->txAntPattern << + SIR_MAC_VHT_CAP_TX_ANTENNA_PATTERN) | + (vht_caps->extended_nss_bw_supp << + SIR_MAC_VHT_CAP_EXTD_NSS_BW)); + + pAddBssParams->staContext.maxAmpduSize = + SIR_MAC_GET_VHT_MAX_AMPDU_EXPO( + pAddBssParams->staContext.vht_caps); +} + +/** + * lim_update_vht_oper_assoc_resp : Update VHT Operations in assoc response. + * @mac_ctx Pointer to Global MAC structure + * @pAddBssParams: parameters required for add bss params. + * @vht_oper: VHT Operations to update. + * @pe_session : session entry. + * + * Return : void + */ +static void lim_update_vht_oper_assoc_resp(struct mac_context *mac_ctx, + struct bss_params *pAddBssParams, + tDot11fIEVHTOperation *vht_oper, struct pe_session *pe_session) +{ + int16_t ccfs0 = vht_oper->chan_center_freq_seg0; + int16_t ccfs1 = vht_oper->chan_center_freq_seg1; + int16_t offset = abs((ccfs0 - ccfs1)); + uint8_t ch_width; + + ch_width = pAddBssParams->ch_width; + if (vht_oper->chanWidth && pe_session->ch_width) { + ch_width = CH_WIDTH_80MHZ; + if (ccfs1 && offset == 8) + ch_width = CH_WIDTH_160MHZ; + else if (ccfs1 && offset > 16) + ch_width = CH_WIDTH_80P80MHZ; + } + if (ch_width > pe_session->ch_width) + ch_width = pe_session->ch_width; + pAddBssParams->ch_width = ch_width; + pAddBssParams->staContext.ch_width = ch_width; +} + +#ifdef WLAN_SUPPORT_TWT +/** + * lim_set_sta_ctx_twt() - Save the TWT settings in STA context + * @sta_ctx: Pointer to Station Context + * @session: Pointer to PE session + * + * Return: None + */ +static void lim_set_sta_ctx_twt(tAddStaParams *sta_ctx, struct pe_session *session) +{ + sta_ctx->twt_requestor = session->peer_twt_requestor; + sta_ctx->twt_responder = session->peer_twt_responder; +} +#else +static inline void lim_set_sta_ctx_twt(tAddStaParams *sta_ctx, + struct pe_session *session) +{ +} +#endif + +void lim_sta_add_bss_update_ht_parameter(uint32_t bss_chan_freq, + tDot11fIEHTCaps* ht_cap, + tDot11fIEHTInfo* ht_inf, + bool chan_width_support, + struct bss_params *add_bss) +{ + if (!ht_cap->present) + return; + + add_bss->htCapable = ht_cap->present; + + if (!ht_inf->present) + return; + + if (chan_width_support && ht_cap->supportedChannelWidthSet) + add_bss->ch_width = ht_inf->recommendedTxWidthSet; + else + add_bss->ch_width = CH_WIDTH_20MHZ; +} + +QDF_STATUS lim_sta_send_add_bss(struct mac_context *mac, tpSirAssocRsp pAssocRsp, + tpSchBeaconStruct pBeaconStruct, + struct bss_description *bssDescription, + uint8_t updateEntry, struct pe_session *pe_session) +{ + struct bss_params *pAddBssParams = NULL; + uint32_t retCode; + tpDphHashNode sta = NULL; + bool chan_width_support = false; + bool is_vht_cap_in_vendor_ie = false; + tDot11fIEVHTCaps *vht_caps = NULL; + tDot11fIEVHTOperation *vht_oper = NULL; + tAddStaParams *sta_context; + uint32_t listen_interval = MLME_CFG_LISTEN_INTERVAL; + struct mlme_vht_capabilities_info *vht_cap_info; + + vht_cap_info = &mac->mlme_cfg->vht_caps.vht_cap_info; + + /* Package SIR_HAL_ADD_BSS_REQ message parameters */ + pAddBssParams = qdf_mem_malloc(sizeof(struct bss_params)); + if (!pAddBssParams) { + retCode = QDF_STATUS_E_NOMEM; + goto returnFailure; + } + + qdf_mem_copy(pAddBssParams->bssId, bssDescription->bssId, + sizeof(tSirMacAddr)); + + pAddBssParams->beaconInterval = bssDescription->beaconInterval; + + pAddBssParams->dtimPeriod = pBeaconStruct->tim.dtimPeriod; + pAddBssParams->updateBss = updateEntry; + + if (IS_DOT11_MODE_11B(pe_session->dot11mode) && + bssDescription->nwType != eSIR_11B_NW_TYPE) { + pAddBssParams->nwType = eSIR_11B_NW_TYPE; + } else { + pAddBssParams->nwType = bssDescription->nwType; + } + + pAddBssParams->shortSlotTimeSupported = + (uint8_t) pAssocRsp->capabilityInfo.shortSlotTime; + pAddBssParams->llbCoexist = + (uint8_t) pe_session->beaconParams.llbCoexist; + + /* Use the advertised capabilities from the received beacon/PR */ + if (IS_DOT11_MODE_HT(pe_session->dot11mode)) { + chan_width_support = + lim_get_ht_capability(mac, + eHT_SUPPORTED_CHANNEL_WIDTH_SET, + pe_session); + lim_sta_add_bss_update_ht_parameter(bssDescription->chan_freq, + &pAssocRsp->HTCaps, + &pAssocRsp->HTInfo, + chan_width_support, + pAddBssParams); + } + + if (pe_session->vhtCapability && (pAssocRsp->VHTCaps.present)) { + pAddBssParams->vhtCapable = pAssocRsp->VHTCaps.present; + vht_caps = &pAssocRsp->VHTCaps; + vht_oper = &pAssocRsp->VHTOperation; + } else if (pe_session->vhtCapability && + pAssocRsp->vendor_vht_ie.VHTCaps.present){ + pAddBssParams->vhtCapable = + pAssocRsp->vendor_vht_ie.VHTCaps.present; + pe_debug("VHT Caps and Operation are present in vendor Specific IE"); + vht_caps = &pAssocRsp->vendor_vht_ie.VHTCaps; + vht_oper = &pAssocRsp->vendor_vht_ie.VHTOperation; + } else { + pAddBssParams->vhtCapable = 0; + } + if (pAddBssParams->vhtCapable) { + if (vht_oper) + lim_update_vht_oper_assoc_resp(mac, pAddBssParams, + vht_oper, pe_session); + if (vht_caps) + lim_update_vhtcaps_assoc_resp(mac, pAddBssParams, + vht_caps, pe_session); + } + + if (lim_is_session_he_capable(pe_session) && + (pAssocRsp->he_cap.present)) { + lim_add_bss_he_cap(pAddBssParams, pAssocRsp); + lim_add_bss_he_cfg(pAddBssParams, pe_session); + } + /* + * Populate the STA-related parameters here + * Note that the STA here refers to the AP + * staType = PEER + */ + sta_context = &pAddBssParams->staContext; + /* Identifying AP as an STA */ + pAddBssParams->staContext.staType = STA_ENTRY_OTHER; + + qdf_mem_copy(pAddBssParams->staContext.bssId, + bssDescription->bssId, sizeof(tSirMacAddr)); + + listen_interval = mac->mlme_cfg->sap_cfg.listen_interval; + pAddBssParams->staContext.listenInterval = listen_interval; + + /* Fill Assoc id from the dph table */ + sta = dph_lookup_hash_entry(mac, pAddBssParams->staContext.bssId, + &pAddBssParams->staContext.assocId, + &pe_session->dph.dphHashTable); + if (!sta) { + pe_err("Couldn't get assoc id for " "MAC ADDR: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF( + pAddBssParams->staContext.staMac)); + return QDF_STATUS_E_FAILURE; + } + + pAddBssParams->staContext.uAPSD = + pe_session->gUapsdPerAcBitmask; + + pAddBssParams->staContext.maxSPLen = 0; + pAddBssParams->staContext.updateSta = updateEntry; + + if (IS_DOT11_MODE_HT(pe_session->dot11mode) + && pBeaconStruct->HTCaps.present) { + pAddBssParams->staContext.htCapable = 1; + if (pe_session->ht_config.ht_tx_stbc) + pAddBssParams->staContext.stbc_capable = + pAssocRsp->HTCaps.rxSTBC; + + if (pe_session->vhtCapability && + (IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps) || + IS_BSS_VHT_CAPABLE(pBeaconStruct-> + vendor_vht_ie.VHTCaps))) { + pAddBssParams->staContext.vhtCapable = 1; + pAddBssParams->staContext.vht_mcs_10_11_supp = + sta->vht_mcs_10_11_supp; + + pAddBssParams->staContext.vhtSupportedRxNss = + sta->vhtSupportedRxNss; + if (pAssocRsp->VHTCaps.present) + vht_caps = &pAssocRsp->VHTCaps; + else if (pAssocRsp->vendor_vht_ie.VHTCaps.present) { + vht_caps = &pAssocRsp->vendor_vht_ie.VHTCaps; + pe_debug("VHT Caps are in vendor Specific IE"); + is_vht_cap_in_vendor_ie = true; + } + + if ((vht_caps) && (vht_caps->suBeamFormerCap || + vht_caps->muBeamformerCap) && + pe_session->vht_config.su_beam_formee) + sta_context->vhtTxBFCapable = 1; + + if ((vht_caps) && vht_caps->muBeamformerCap && + pe_session->vht_config.mu_beam_formee) + sta_context->vhtTxMUBformeeCapable = 1; + + if ((vht_caps) && vht_caps->suBeamformeeCap && + pe_session->vht_config.su_beam_former) + sta_context->enable_su_tx_bformer = 1; + + if (vht_caps && pAddBssParams->staContext.stbc_capable) + pAddBssParams->staContext.stbc_capable = + vht_caps->rxSTBC; + if (pe_session->ch_width == CH_WIDTH_160MHZ || + pe_session->ch_width == CH_WIDTH_80P80MHZ) { + sta_context->vht_160mhz_nss = + sta->vht_160mhz_nss; + sta_context->vht_80p80mhz_nss = + sta->vht_80p80mhz_nss; + sta_context->vht_extended_nss_bw_cap = + sta->vht_extended_nss_bw_cap; + } else { + sta_context->vht_160mhz_nss = 0; + sta_context->vht_80p80mhz_nss = 0; + sta_context->vht_extended_nss_bw_cap = 0; + } + } + if (lim_is_session_he_capable(pe_session) && + pAssocRsp->he_cap.present) { + lim_intersect_ap_he_caps(pe_session, + pAddBssParams, + NULL, + pAssocRsp); + lim_update_he_stbc_capable(&pAddBssParams->staContext); + } + + /* + * in limExtractApCapability function intersection of FW + * advertised channel width and AP advertised channel + * width has been taken into account for calculating + * pe_session->ch_width + */ + if (chan_width_support && + ((pAssocRsp->HTCaps.supportedChannelWidthSet) || + (pBeaconStruct->HTCaps.supportedChannelWidthSet))) { + pAddBssParams->ch_width = + pe_session->ch_width; + pAddBssParams->staContext.ch_width = + pe_session->ch_width; + } else { + pAddBssParams->ch_width = CH_WIDTH_20MHZ; + sta_context->ch_width = CH_WIDTH_20MHZ; + if (!vht_cap_info->enable_txbf_20mhz) + sta_context->vhtTxBFCapable = 0; + } + + pAddBssParams->staContext.mimoPS = + (tSirMacHTMIMOPowerSaveState) + pAssocRsp->HTCaps.mimoPowerSave; + pAddBssParams->staContext.maxAmpduDensity = + pAssocRsp->HTCaps.mpduDensity; + /* + * We will check gShortGI20Mhz and gShortGI40Mhz from + * session entry if they are set then we will use what ever + * Assoc response coming from AP supports. If these + * values are set as 0 in session entry then we will + * hardcode this values to 0. + */ + if (pe_session->ht_config.ht_sgi20) { + pAddBssParams->staContext.fShortGI20Mhz = + (uint8_t)pAssocRsp->HTCaps.shortGI20MHz; + } else { + pAddBssParams->staContext.fShortGI20Mhz = false; + } + + if (pe_session->ht_config.ht_sgi40) { + pAddBssParams->staContext.fShortGI40Mhz = + (uint8_t) pAssocRsp->HTCaps.shortGI40MHz; + } else { + pAddBssParams->staContext.fShortGI40Mhz = false; + } + + if (!pAddBssParams->staContext.vhtCapable) + /* Use max ampd factor advertised in + * HTCAP for non-vht connection */ + { + pAddBssParams->staContext.maxAmpduSize = + pAssocRsp->HTCaps.maxRxAMPDUFactor; + } else if (pAddBssParams->staContext.maxAmpduSize < + pAssocRsp->HTCaps.maxRxAMPDUFactor) { + pAddBssParams->staContext.maxAmpduSize = + pAssocRsp->HTCaps.maxRxAMPDUFactor; + } + if (pAddBssParams->staContext.vhtTxBFCapable + && vht_cap_info->disable_ldpc_with_txbf_ap) { + pAddBssParams->staContext.htLdpcCapable = 0; + pAddBssParams->staContext.vhtLdpcCapable = 0; + } else { + if (pe_session->txLdpcIniFeatureEnabled & 0x1) + pAddBssParams->staContext.htLdpcCapable = + (uint8_t) pAssocRsp->HTCaps.advCodingCap; + else + pAddBssParams->staContext.htLdpcCapable = 0; + + if (pAssocRsp->VHTCaps.present) + vht_caps = &pAssocRsp->VHTCaps; + else if (pAssocRsp->vendor_vht_ie.VHTCaps.present) { + vht_caps = &pAssocRsp->vendor_vht_ie.VHTCaps; + pe_debug("VHT Caps is in vendor Specific IE"); + } + if (vht_caps && + (pe_session->txLdpcIniFeatureEnabled & 0x2)) { + if (!is_vht_cap_in_vendor_ie) + pAddBssParams->staContext.vhtLdpcCapable = + (uint8_t) pAssocRsp->VHTCaps.ldpcCodingCap; + else + pAddBssParams->staContext.vhtLdpcCapable = + (uint8_t) vht_caps->ldpcCodingCap; + } else { + pAddBssParams->staContext.vhtLdpcCapable = 0; + } + } + + } + if (lim_is_he_6ghz_band(pe_session)) { + if (lim_is_session_he_capable(pe_session) && + pAssocRsp->he_cap.present) { + lim_intersect_ap_he_caps(pe_session, + pAddBssParams, + NULL, + pAssocRsp); + lim_update_he_stbc_capable(&pAddBssParams->staContext); + lim_update_he_6gop_assoc_resp(pAddBssParams, + &pAssocRsp->he_op, + pe_session); + lim_update_he_6ghz_band_caps(mac, + &pAssocRsp->he_6ghz_band_cap, + &pAddBssParams->staContext); + } + } + pAddBssParams->staContext.smesessionId = + pe_session->smeSessionId; + pAddBssParams->staContext.wpa_rsn = pBeaconStruct->rsnPresent; + pAddBssParams->staContext.wpa_rsn |= + (pBeaconStruct->wpaPresent << 1); + /* For OSEN Connection AP does not advertise RSN or WPA IE + * so from the IEs we get from supplicant we get this info + * so for FW to transmit EAPOL message 4 we shall set + * wpa_rsn + */ + if ((!pAddBssParams->staContext.wpa_rsn) + && (pe_session->isOSENConnection)) + pAddBssParams->staContext.wpa_rsn = 1; + qdf_mem_copy(&pAddBssParams->staContext.capab_info, + &pAssocRsp->capabilityInfo, + sizeof(pAddBssParams->staContext.capab_info)); + qdf_mem_copy(&pAddBssParams->staContext.ht_caps, + (uint8_t *) &pAssocRsp->HTCaps + sizeof(uint8_t), + sizeof(pAddBssParams->staContext.ht_caps)); + + /* If WMM IE or 802.11E IE is present then enable WMM */ + if ((pe_session->limWmeEnabled && pAssocRsp->wmeEdcaPresent) || + (pe_session->limQosEnabled && pAssocRsp->edcaPresent)) + pAddBssParams->staContext.wmmEnabled = 1; + else + pAddBssParams->staContext.wmmEnabled = 0; + + /* Update the rates */ + sta = dph_get_hash_entry(mac, DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + if (sta) { + qdf_mem_copy(&pAddBssParams->staContext.supportedRates, + &sta->supportedRates, + sizeof(sta->supportedRates)); + } else + pe_err("could not Update the supported rates"); + pAddBssParams->staContext.encryptType = pe_session->encryptType; + + pAddBssParams->maxTxPower = pe_session->maxTxPower; + + if (QDF_P2P_CLIENT_MODE == pe_session->opmode) + pAddBssParams->staContext.p2pCapableSta = 1; + + pAddBssParams->extSetStaKeyParamValid = 0; + +#ifdef WLAN_FEATURE_11W + if (pe_session->limRmfEnabled) { + pAddBssParams->rmfEnabled = 1; + pAddBssParams->staContext.rmfEnabled = 1; + } +#endif + + /* Set a new state for MLME */ + if (eLIM_MLM_WT_ASSOC_RSP_STATE == pe_session->limMlmState) + pe_session->limMlmState = + eLIM_MLM_WT_ADD_BSS_RSP_ASSOC_STATE; + else + pe_session->limMlmState = + eLIM_MLM_WT_ADD_BSS_RSP_REASSOC_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + pe_session->limMlmState)); + + if (!pAddBssParams->staContext.htLdpcCapable) + pAddBssParams->staContext.ht_caps &= + ~(1 << SIR_MAC_HT_CAP_ADVCODING_S); + if (!pAddBssParams->staContext.vhtLdpcCapable) + pAddBssParams->staContext.vht_caps &= + ~(1 << SIR_MAC_VHT_CAP_LDPC_CODING_CAP); + + if (pe_session->isNonRoamReassoc) + pAddBssParams->nonRoamReassoc = 1; + + pe_debug("update %d MxAmpduDen %d mimoPS %d vht_mcs11 %d shortSlot %d BI %d DTIM %d enc type %d p2p cab STA %d", + updateEntry, + pAddBssParams->staContext.maxAmpduDensity, + pAddBssParams->staContext.mimoPS, + pAddBssParams->staContext.vht_mcs_10_11_supp, + pAddBssParams->shortSlotTimeSupported, + pAddBssParams->beaconInterval, pAddBssParams->dtimPeriod, + pAddBssParams->staContext.encryptType, + pAddBssParams->staContext.p2pCapableSta); + if (cds_is_5_mhz_enabled()) { + pAddBssParams->ch_width = CH_WIDTH_5MHZ; + pAddBssParams->staContext.ch_width = CH_WIDTH_5MHZ; + } else if (cds_is_10_mhz_enabled()) { + pAddBssParams->ch_width = CH_WIDTH_10MHZ; + pAddBssParams->staContext.ch_width = CH_WIDTH_10MHZ; + } + lim_set_sta_ctx_twt(&pAddBssParams->staContext, pe_session); + + if (lim_is_fils_connection(pe_session)) + pAddBssParams->no_ptk_4_way = true; + + /* we need to defer the message until we get the response back */ + SET_LIM_PROCESS_DEFD_MESGS(mac, false); + + retCode = wma_send_peer_assoc_req(pAddBssParams); + if (QDF_IS_STATUS_ERROR(retCode)) { + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + pe_err("wma_send_peer_assoc_req failed=%X", + retCode); + } + qdf_mem_free(pAddBssParams); + +returnFailure: + /* Clean-up will be done by the caller... */ + return retCode; +} + +QDF_STATUS lim_sta_send_add_bss_pre_assoc(struct mac_context *mac, + struct pe_session *pe_session) +{ + struct bss_params *pAddBssParams = NULL; + uint32_t retCode; + tSchBeaconStruct *pBeaconStruct; + bool chan_width_support = false; + tDot11fIEVHTOperation *vht_oper = NULL; + tDot11fIEVHTCaps *vht_caps = NULL; + uint32_t listen_interval = MLME_CFG_LISTEN_INTERVAL; + struct bss_description *bssDescription = NULL; + struct mlme_vht_capabilities_info *vht_cap_info; + + if (!pe_session->lim_join_req) { + pe_err("Lim Join request is NULL"); + return QDF_STATUS_E_FAILURE; + } + + bssDescription = &pe_session->lim_join_req->bssDescription; + vht_cap_info = &mac->mlme_cfg->vht_caps.vht_cap_info; + + pBeaconStruct = qdf_mem_malloc(sizeof(tSchBeaconStruct)); + if (!pBeaconStruct) + return QDF_STATUS_E_NOMEM; + + /* Package SIR_HAL_ADD_BSS_REQ message parameters */ + pAddBssParams = qdf_mem_malloc(sizeof(struct bss_params)); + if (!pAddBssParams) { + retCode = QDF_STATUS_E_NOMEM; + goto returnFailure; + } + + lim_extract_ap_capabilities(mac, (uint8_t *) bssDescription->ieFields, + lim_get_ielen_from_bss_description(bssDescription), + pBeaconStruct); + + if (mac->lim.gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) + lim_decide_sta_protection_on_assoc(mac, pBeaconStruct, + pe_session); + qdf_mem_copy(pAddBssParams->bssId, bssDescription->bssId, + sizeof(tSirMacAddr)); + + pAddBssParams->beaconInterval = bssDescription->beaconInterval; + + pAddBssParams->dtimPeriod = pBeaconStruct->tim.dtimPeriod; + pAddBssParams->updateBss = false; + + pAddBssParams->nwType = bssDescription->nwType; + + pAddBssParams->shortSlotTimeSupported = + (uint8_t) pBeaconStruct->capabilityInfo.shortSlotTime; + pAddBssParams->llbCoexist = + (uint8_t) pe_session->beaconParams.llbCoexist; + + /* Use the advertised capabilities from the received beacon/PR */ + if (IS_DOT11_MODE_HT(pe_session->dot11mode)) { + chan_width_support = + lim_get_ht_capability(mac, + eHT_SUPPORTED_CHANNEL_WIDTH_SET, + pe_session); + lim_sta_add_bss_update_ht_parameter(bssDescription->chan_freq, + &pBeaconStruct->HTCaps, + &pBeaconStruct->HTInfo, + chan_width_support, + pAddBssParams); + } + + if (pe_session->vhtCapability && + (IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps) || + IS_BSS_VHT_CAPABLE(pBeaconStruct->vendor_vht_ie.VHTCaps))) { + + pAddBssParams->vhtCapable = 1; + if (pBeaconStruct->VHTOperation.present) + vht_oper = &pBeaconStruct->VHTOperation; + else if (pBeaconStruct->vendor_vht_ie.VHTOperation.present) { + vht_oper = &pBeaconStruct->vendor_vht_ie.VHTOperation; + pe_debug("VHT Operation is present in vendor Specific IE"); + } + + /* + * in limExtractApCapability function intersection of FW + * advertised channel width and AP advertised channel width has + * been taken into account for calculating + * pe_session->ch_width + */ + pAddBssParams->ch_width = + pe_session->ch_width; + pAddBssParams->staContext.maxAmpduSize = + SIR_MAC_GET_VHT_MAX_AMPDU_EXPO( + pAddBssParams->staContext.vht_caps); + } else { + pAddBssParams->vhtCapable = 0; + } + + if (lim_is_session_he_capable(pe_session) && + pBeaconStruct->he_cap.present) { + lim_update_bss_he_capable(mac, pAddBssParams); + lim_add_bss_he_cfg(pAddBssParams, pe_session); + } + + /* + * Populate the STA-related parameters here + * Note that the STA here refers to the AP + */ + /* Identifying AP as an STA */ + pAddBssParams->staContext.staType = STA_ENTRY_OTHER; + + qdf_mem_copy(pAddBssParams->staContext.bssId, + bssDescription->bssId, sizeof(tSirMacAddr)); + + listen_interval = mac->mlme_cfg->sap_cfg.listen_interval; + pAddBssParams->staContext.listenInterval = listen_interval; + pAddBssParams->staContext.assocId = 0; + pAddBssParams->staContext.uAPSD = 0; + pAddBssParams->staContext.maxSPLen = 0; + pAddBssParams->staContext.updateSta = false; + + if (IS_DOT11_MODE_HT(pe_session->dot11mode) + && (pBeaconStruct->HTCaps.present)) { + pAddBssParams->staContext.htCapable = 1; + if (pe_session->vhtCapability && + (IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps) || + IS_BSS_VHT_CAPABLE( + pBeaconStruct->vendor_vht_ie.VHTCaps))) { + pAddBssParams->staContext.vhtCapable = 1; + if (pBeaconStruct->VHTCaps.present) + vht_caps = &pBeaconStruct->VHTCaps; + else if (pBeaconStruct->vendor_vht_ie.VHTCaps.present) + vht_caps = &pBeaconStruct-> + vendor_vht_ie.VHTCaps; + + if ((vht_caps) && (vht_caps->suBeamFormerCap || + vht_caps->muBeamformerCap) && + pe_session->vht_config.su_beam_formee) + pAddBssParams->staContext.vhtTxBFCapable = 1; + + if ((vht_caps) && vht_caps->muBeamformerCap && + pe_session->vht_config.mu_beam_formee) + pAddBssParams->staContext.vhtTxMUBformeeCapable + = 1; + + if ((vht_caps) && vht_caps->suBeamformeeCap && + pe_session->vht_config.su_beam_former) + pAddBssParams->staContext.enable_su_tx_bformer + = 1; + } + if (lim_is_session_he_capable(pe_session) && + pBeaconStruct->he_cap.present) + lim_intersect_ap_he_caps(pe_session, pAddBssParams, + pBeaconStruct, NULL); + + if (pBeaconStruct->HTCaps.supportedChannelWidthSet && + chan_width_support) { + pAddBssParams->staContext.ch_width = + (uint8_t) pBeaconStruct->HTInfo. + recommendedTxWidthSet; + if ((vht_oper) && + pAddBssParams->staContext.vhtCapable && + vht_oper->chanWidth) + pAddBssParams->staContext.ch_width = + vht_oper->chanWidth + 1; + } else { + pAddBssParams->staContext.ch_width = + CH_WIDTH_20MHZ; + } + pAddBssParams->staContext.mimoPS = + (tSirMacHTMIMOPowerSaveState) pBeaconStruct->HTCaps. + mimoPowerSave; + pAddBssParams->staContext.maxAmpduDensity = + pBeaconStruct->HTCaps.mpduDensity; + /* + * We will check gShortGI20Mhz and gShortGI40Mhz from ini file. + * if they are set then we will use what ever Beacon coming + * from AP supports. If these values are set as 0 in ini file + * then we will hardcode this values to 0. + */ + if (pe_session->ht_config.ht_sgi20) + pAddBssParams->staContext.fShortGI20Mhz = + (uint8_t)pBeaconStruct->HTCaps.shortGI20MHz; + else + pAddBssParams->staContext.fShortGI20Mhz = false; + + if (pe_session->ht_config.ht_sgi40) + pAddBssParams->staContext.fShortGI40Mhz = + (uint8_t) pBeaconStruct->HTCaps.shortGI40MHz; + else + pAddBssParams->staContext.fShortGI40Mhz = false; + + pAddBssParams->staContext.maxAmpduSize = + pBeaconStruct->HTCaps.maxRxAMPDUFactor; + if (pAddBssParams->staContext.vhtTxBFCapable + && vht_cap_info->disable_ldpc_with_txbf_ap) { + pAddBssParams->staContext.htLdpcCapable = 0; + pAddBssParams->staContext.vhtLdpcCapable = 0; + } else { + if (pe_session->txLdpcIniFeatureEnabled & 0x1) + pAddBssParams->staContext.htLdpcCapable = + (uint8_t) pBeaconStruct->HTCaps. + advCodingCap; + else + pAddBssParams->staContext.htLdpcCapable = 0; + + if (pBeaconStruct->VHTCaps.present) + vht_caps = &pBeaconStruct->VHTCaps; + else if (pBeaconStruct->vendor_vht_ie.VHTCaps.present) { + vht_caps = + &pBeaconStruct->vendor_vht_ie.VHTCaps; + } + if (vht_caps && + (pe_session->txLdpcIniFeatureEnabled & 0x2)) + pAddBssParams->staContext.vhtLdpcCapable = + (uint8_t) vht_caps->ldpcCodingCap; + else + pAddBssParams->staContext.vhtLdpcCapable = 0; + } + } + /* + * If WMM IE or 802.11E IE is not present + * and AP is HT AP then enable WMM + */ + if ((pe_session->limWmeEnabled && (pBeaconStruct->wmeEdcaPresent || + pAddBssParams->staContext.htCapable)) || + (pe_session->limQosEnabled && + (pBeaconStruct->edcaPresent || + pAddBssParams->staContext.htCapable))) + pAddBssParams->staContext.wmmEnabled = 1; + else + pAddBssParams->staContext.wmmEnabled = 0; + + /* Update the rates */ + lim_populate_peer_rate_set(mac, + &pAddBssParams->staContext. + supportedRates, + pBeaconStruct->HTCaps.supportedMCSSet, + false, pe_session, + &pBeaconStruct->VHTCaps, + &pBeaconStruct->he_cap, NULL, + bssDescription); + + pAddBssParams->staContext.encryptType = pe_session->encryptType; + + pAddBssParams->maxTxPower = pe_session->maxTxPower; + + pAddBssParams->staContext.smesessionId = pe_session->smeSessionId; + pAddBssParams->staContext.sessionId = pe_session->peSessionId; + pAddBssParams->extSetStaKeyParamValid = 0; + +#ifdef WLAN_FEATURE_11W + if (pe_session->limRmfEnabled) { + pAddBssParams->rmfEnabled = 1; + pAddBssParams->staContext.rmfEnabled = 1; + } +#endif + /* Set a new state for MLME */ + pe_session->limMlmState = eLIM_MLM_WT_ADD_BSS_RSP_PREASSOC_STATE; + + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + pe_session->limMlmState)); + if (cds_is_5_mhz_enabled()) { + pAddBssParams->ch_width = CH_WIDTH_5MHZ; + pAddBssParams->staContext.ch_width = CH_WIDTH_5MHZ; + } else if (cds_is_10_mhz_enabled()) { + pAddBssParams->ch_width = CH_WIDTH_10MHZ; + pAddBssParams->staContext.ch_width = CH_WIDTH_10MHZ; + } + + if (lim_is_fils_connection(pe_session)) + pAddBssParams->no_ptk_4_way = true; + + retCode = wma_pre_assoc_req(pAddBssParams); + lim_process_sta_add_bss_rsp_pre_assoc(mac, pAddBssParams, + pe_session, retCode); + qdf_mem_free(pAddBssParams); + /* + * Set retCode sucess as lim_process_sta_add_bss_rsp_pre_assoc take + * care of failure + */ + retCode = QDF_STATUS_SUCCESS; + +returnFailure: + /* Clean-up will be done by the caller... */ + qdf_mem_free(pBeaconStruct); + return retCode; +} + +/** + * lim_prepare_and_send_del_sta_cnf() - prepares and send del sta cnf + * + * @mac: mac global context + * @sta: sta dph node + * @status_code: status code + * @pe_session: session context + * + * deletes DPH entry, changes the MLM mode for station, calls + * lim_send_del_sta_cnf + * + * Return: void + */ +void +lim_prepare_and_send_del_sta_cnf(struct mac_context *mac, tpDphHashNode sta, + tSirResultCodes status_code, + struct pe_session *pe_session) +{ + uint16_t staDsAssocId = 0; + struct qdf_mac_addr sta_dsaddr; + struct lim_sta_context mlmStaContext; + + if (!sta) { + pe_err("sta is NULL"); + return; + } + staDsAssocId = sta->assocId; + qdf_mem_copy((uint8_t *) sta_dsaddr.bytes, + sta->staAddr, QDF_MAC_ADDR_SIZE); + + mlmStaContext = sta->mlmStaContext; + if (LIM_IS_AP_ROLE(pe_session)) + lim_release_peer_idx(mac, sta->assocId, pe_session); + + lim_delete_dph_hash_entry(mac, sta->staAddr, sta->assocId, + pe_session); + + if (LIM_IS_STA_ROLE(pe_session)) { + pe_session->limMlmState = eLIM_MLM_IDLE_STATE; + MTRACE(mac_trace(mac, TRACE_CODE_MLM_STATE, + pe_session->peSessionId, + pe_session->limMlmState)); + } + lim_send_del_sta_cnf(mac, sta_dsaddr, staDsAssocId, mlmStaContext, + status_code, pe_session); +} + +/** ------------------------------------------------------------- + \fn lim_init_pre_auth_timer_table + \brief Initialize the Pre Auth Tanle and creates the timer for + each node for the timeout value got from cfg. + \param struct mac_context * mac + \param tpLimPreAuthTable pPreAuthTimerTable + \return none + -------------------------------------------------------------*/ +void lim_init_pre_auth_timer_table(struct mac_context *mac, + tpLimPreAuthTable pPreAuthTimerTable) +{ + uint32_t cfgValue; + uint32_t authNodeIdx; + + tLimPreAuthNode **pAuthNode = pPreAuthTimerTable->pTable; + + /* Get AUTH_RSP Timers value */ + cfgValue = SYS_MS_TO_TICKS(mac->mlme_cfg->timeouts.auth_rsp_timeout); + for (authNodeIdx = 0; authNodeIdx < pPreAuthTimerTable->numEntry; + authNodeIdx++) { + if (tx_timer_create(mac, &(pAuthNode[authNodeIdx]->timer), + "AUTH RESPONSE TIMEOUT", + lim_auth_response_timer_handler, authNodeIdx, + cfgValue, 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("Cannot create Auth Rsp timer of Index: %d", + authNodeIdx); + return; + } + pAuthNode[authNodeIdx]->authNodeIdx = (uint8_t) authNodeIdx; + pAuthNode[authNodeIdx]->fFree = 1; + } +} + +/** ------------------------------------------------------------- + \fn lim_acquire_free_pre_auth_node + \brief Retrives a free Pre Auth node from Pre Auth Table. + \param struct mac_context * mac + \param tpLimPreAuthTable pPreAuthTimerTable + \return none + -------------------------------------------------------------*/ +tLimPreAuthNode *lim_acquire_free_pre_auth_node(struct mac_context *mac, + tpLimPreAuthTable pPreAuthTimerTable) +{ + uint32_t i; + tLimPreAuthNode **pTempNode = pPreAuthTimerTable->pTable; + + for (i = 0; i < pPreAuthTimerTable->numEntry; i++) { + if (pTempNode[i]->fFree == 1) { + pTempNode[i]->fFree = 0; + return pTempNode[i]; + } + } + + return NULL; +} + +/** ------------------------------------------------------------- + \fn lim_get_pre_auth_node_from_index + \brief Depending on the Index this retrieves the pre auth node. + \param struct mac_context * mac + \param tpLimPreAuthTable pAuthTable + \param uint32_t authNodeIdx + \return none + -------------------------------------------------------------*/ +tLimPreAuthNode *lim_get_pre_auth_node_from_index(struct mac_context *mac, + tpLimPreAuthTable pAuthTable, + uint32_t authNodeIdx) +{ + if ((authNodeIdx >= pAuthTable->numEntry) + || (!pAuthTable->pTable)) { + pe_err("Invalid Auth Timer Index: %d NumEntry: %d", + authNodeIdx, pAuthTable->numEntry); + return NULL; + } + + return pAuthTable->pTable[authNodeIdx]; +} + +/* Util API to check if the channels supported by STA is within range */ +QDF_STATUS lim_is_dot11h_supported_channels_valid(struct mac_context *mac, + tSirAssocReq *assoc) +{ + /* + * Allow all the stations to join with us. + * 802.11h-2003 11.6.1 => An AP may use the supported channels list for associated STAs + * as an input into an algorithm used to select a new channel for the BSS. + * The specification of the algorithm is beyond the scope of this amendment. + */ + + return QDF_STATUS_SUCCESS; +} + +/* Util API to check if the txpower supported by STA is within range */ +QDF_STATUS lim_is_dot11h_power_capabilities_in_range(struct mac_context *mac, + tSirAssocReq *assoc, + struct pe_session *pe_session) +{ + int8_t localMaxTxPower; + uint8_t local_pwr_constraint; + + localMaxTxPower = wlan_reg_get_channel_reg_power_for_freq( + mac->pdev, pe_session->curr_op_freq); + + local_pwr_constraint = mac->mlme_cfg->power.local_power_constraint; + localMaxTxPower -= (int8_t)local_pwr_constraint; + + /** + * The min Tx Power of the associating station should not be greater than (regulatory + * max tx power - local power constraint configured on AP). + */ + if (assoc->powerCapability.minTxPower > localMaxTxPower) { + pe_warn("minTxPower (STA): %d, localMaxTxPower (AP): %d", + assoc->powerCapability.minTxPower, localMaxTxPower); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +void lim_fill_rx_highest_supported_rate(struct mac_context *mac, + uint16_t *rxHighestRate, + uint8_t *pSupportedMCSSet) +{ + tSirMacRxHighestSupportRate *pRxHighestRate; + uint8_t *pBuf; + uint16_t rate = 0; + + pBuf = pSupportedMCSSet + MCS_RX_HIGHEST_SUPPORTED_RATE_BYTE_OFFSET; + rate = lim_get_u16(pBuf); + + pRxHighestRate = (tSirMacRxHighestSupportRate *) &rate; + *rxHighestRate = pRxHighestRate->rate; + + return; +} + +#ifdef WLAN_FEATURE_11W +/** ------------------------------------------------------------- + \fn lim_send_sme_unprotected_mgmt_frame_ind + \brief Forwards the unprotected management frame to SME. + \param struct mac_context * mac + \param frameType - 802.11 frame type + \param frame - frame buffer + \param sessionId - id for the current session + \param pe_session - PE session context + \return none + -------------------------------------------------------------*/ +void lim_send_sme_unprotected_mgmt_frame_ind(struct mac_context *mac, uint8_t frameType, + uint8_t *frame, uint32_t frameLen, + uint16_t sessionId, + struct pe_session *pe_session) +{ + struct scheduler_msg mmhMsg = {0}; + tSirSmeUnprotMgmtFrameInd *pSirSmeMgmtFrame = NULL; + uint16_t length; + + length = sizeof(tSirSmeUnprotMgmtFrameInd) + frameLen; + + pSirSmeMgmtFrame = qdf_mem_malloc(length); + if (!pSirSmeMgmtFrame) + return; + + pSirSmeMgmtFrame->sessionId = sessionId; + pSirSmeMgmtFrame->frameType = frameType; + + qdf_mem_copy(pSirSmeMgmtFrame->frameBuf, frame, frameLen); + pSirSmeMgmtFrame->frameLen = frameLen; + + mmhMsg.type = eWNI_SME_UNPROT_MGMT_FRM_IND; + mmhMsg.bodyptr = pSirSmeMgmtFrame; + mmhMsg.bodyval = 0; + + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + return; +} +#endif + +#ifdef FEATURE_WLAN_ESE +void lim_send_sme_tsm_ie_ind(struct mac_context *mac, + struct pe_session *pe_session, + uint8_t tid, uint8_t state, + uint16_t measurement_interval) +{ + struct scheduler_msg msg = {0}; + struct tsm_ie_ind *tsm_ie_ind; + + if (!mac || !pe_session) + return; + + tsm_ie_ind = qdf_mem_malloc(sizeof(*tsm_ie_ind)); + if (!tsm_ie_ind) + return; + + tsm_ie_ind->sessionId = pe_session->smeSessionId; + tsm_ie_ind->tsm_ie.tsid = tid; + tsm_ie_ind->tsm_ie.state = state; + tsm_ie_ind->tsm_ie.msmt_interval = measurement_interval; + + msg.type = eWNI_SME_TSM_IE_IND; + msg.bodyptr = tsm_ie_ind; + msg.bodyval = 0; + + lim_sys_process_mmh_msg_api(mac, &msg); +} +#endif /* FEATURE_WLAN_ESE */ + +void lim_extract_ies_from_deauth_disassoc(struct pe_session *session, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len) +{ + uint16_t reason_code, ie_offset; + struct wlan_ies ie; + + if (!session) { + pe_err("NULL session"); + return; + } + + /* Get the offset of IEs */ + ie_offset = sizeof(struct wlan_frame_hdr) + sizeof(reason_code); + + if (!deauth_disassoc_frame || deauth_disassoc_frame_len <= ie_offset) + return; + + ie.data = deauth_disassoc_frame + ie_offset; + ie.len = deauth_disassoc_frame_len - ie_offset; + + mlme_set_peer_disconnect_ies(session->vdev, &ie); +} + diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_assoc_utils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_assoc_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..f0f669e90cfd2f933c580ce653a74766b134d091 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_assoc_utils.h @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_assoc_utils.h contains the utility definitions + * LIM uses while processing Re/Association messages. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * 05/26/10 js WPA handling in (Re)Assoc frames + * + */ +#ifndef __LIM_ASSOC_UTILS_H +#define __LIM_ASSOC_UTILS_H + +#include "sir_api.h" +#include "sir_debug.h" + +#include "lim_types.h" + +#define SIZE_OF_NOA_DESCRIPTOR 13 +#define MAX_NOA_PERIOD_IN_MICROSECS 3000000 + +uint32_t lim_cmp_ssid(tSirMacSSid *, struct pe_session *); +uint8_t lim_compare_capabilities(struct mac_context *, + tSirAssocReq *, + tSirMacCapabilityInfo *, struct pe_session *); +uint8_t lim_check_rx_basic_rates(struct mac_context *, tSirMacRateSet, struct pe_session *); +uint8_t lim_check_mcs_set(struct mac_context *mac, uint8_t *supportedMCSSet); +QDF_STATUS lim_cleanup_rx_path(struct mac_context *, tpDphHashNode, + struct pe_session *); +void lim_reject_association(struct mac_context *, tSirMacAddr, uint8_t, + uint8_t, tAniAuthType, uint16_t, uint8_t, + enum mac_status_code, struct pe_session *); + +QDF_STATUS lim_populate_peer_rate_set(struct mac_context *mac, + struct supported_rates *pRates, + uint8_t *pSupportedMCSSet, + uint8_t basicOnly, + struct pe_session *pe_session, + tDot11fIEVHTCaps *pVHTCaps, + tDot11fIEhe_cap *he_caps, + struct sDphHashNode *sta_ds, + struct bss_description *bss_desc); + +/** + * lim_populate_own_rate_set() - comprises the basic and extended rates read + * from CFG + * @mac_ctx: pointer to global mac structure + * @rates: pointer to supported rates + * @supported_mcs_set: pointer to supported mcs rates + * @basic_only: update only basic rates if set true + * @session_entry: pe session entry + * @vht_caps: pointer to vht capability + * @he_caps: pointer to HE capability + * + * This function is called by limProcessAssocRsp() or + * lim_add_staInIBSS() + * - It creates a combined rate set of 12 rates max which + * comprises the basic and extended rates read from CFG + * - It sorts the combined rate Set and copy it in the + * rate array of the pSTA descriptor + * - It sets the erpEnabled bit of the STA descriptor + * ERP bit is set iff the dph PHY mode is 11G and there is at least + * an A rate in the supported or extended rate sets + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_E_FAILURE. + */ +QDF_STATUS lim_populate_own_rate_set(struct mac_context *mac_ctx, + struct supported_rates *rates, + uint8_t *supported_mcs_set, + uint8_t basic_only, + struct pe_session *session_entry, + struct sDot11fIEVHTCaps *vht_caps, + struct sDot11fIEhe_cap *he_caps); + +QDF_STATUS lim_populate_matching_rate_set(struct mac_context *mac_ctx, + tpDphHashNode sta_ds, + tSirMacRateSet *oper_rate_set, + tSirMacRateSet *ext_rate_set, + uint8_t *supported_mcs_set, + struct pe_session *session_entry, + tDot11fIEVHTCaps *vht_caps, + tDot11fIEhe_cap *he_caps); + +QDF_STATUS lim_add_sta(struct mac_context *, tpDphHashNode, uint8_t, struct pe_session *); +QDF_STATUS lim_del_bss(struct mac_context *, tpDphHashNode, uint16_t, struct pe_session *); +QDF_STATUS lim_del_sta(struct mac_context *, tpDphHashNode, bool, struct pe_session *); +QDF_STATUS lim_add_sta_self(struct mac_context *, uint8_t, struct pe_session *); + +#ifdef WLAN_FEATURE_HOST_ROAM +void lim_restore_pre_reassoc_state(struct mac_context *, + tSirResultCodes, uint16_t, struct pe_session *); +void lim_post_reassoc_failure(struct mac_context *, + tSirResultCodes, uint16_t, struct pe_session *); +bool lim_is_reassoc_in_progress(struct mac_context *, struct pe_session *); + +void lim_handle_add_bss_in_re_assoc_context(struct mac_context *mac, + tpDphHashNode sta, struct pe_session *pe_session); +void lim_handle_del_bss_in_re_assoc_context(struct mac_context *mac, + tpDphHashNode sta, struct pe_session *pe_session); +void lim_send_retry_reassoc_req_frame(struct mac_context *mac, + tLimMlmReassocReq *pMlmReassocReq, struct pe_session *pe_session); +QDF_STATUS lim_add_ft_sta_self(struct mac_context *mac, uint16_t assocId, + struct pe_session *pe_session); +#else +static inline void lim_restore_pre_reassoc_state(struct mac_context *mac_ctx, + tSirResultCodes res_code, uint16_t prot_status, + struct pe_session *pe_session) +{} +static inline void lim_post_reassoc_failure(struct mac_context *mac_ctx, + tSirResultCodes res_code, uint16_t prot_status, + struct pe_session *pe_session) +{} +static inline void lim_handle_add_bss_in_re_assoc_context(struct mac_context *mac, + tpDphHashNode sta, struct pe_session *pe_session) +{} +static inline void lim_handle_del_bss_in_re_assoc_context(struct mac_context *mac, + tpDphHashNode sta, struct pe_session *pe_session) +{} +static inline void lim_send_retry_reassoc_req_frame(struct mac_context *mac, + tLimMlmReassocReq *pMlmReassocReq, struct pe_session *pe_session) +{} +static inline bool lim_is_reassoc_in_progress(struct mac_context *mac_ctx, + struct pe_session *pe_session) +{ + return false; +} +static inline QDF_STATUS lim_add_ft_sta_self(struct mac_context *mac, + uint16_t assocId, struct pe_session *pe_session) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static inline bool lim_is_roam_synch_in_progress(struct pe_session *pe_session) +{ + return pe_session->bRoamSynchInProgress; +} +#else +static inline bool lim_is_roam_synch_in_progress(struct pe_session *pe_session) +{ + return false; +} +#endif + +void +lim_send_del_sta_cnf(struct mac_context *mac, struct qdf_mac_addr sta_dsaddr, + uint16_t staDsAssocId, + struct lim_sta_context mlmStaContext, + tSirResultCodes status_code, + struct pe_session *pe_session); + +void lim_handle_cnf_wait_timeout(struct mac_context *mac, uint16_t staId); +void lim_delete_dph_hash_entry(struct mac_context *, tSirMacAddr, uint16_t, struct pe_session *); +void lim_check_and_announce_join_success(struct mac_context *, + tSirProbeRespBeacon *, + tpSirMacMgmtHdr, struct pe_session *); +void lim_update_re_assoc_globals(struct mac_context *mac, + tpSirAssocRsp pAssocRsp, + struct pe_session *pe_session); + +void lim_update_assoc_sta_datas(struct mac_context *mac, + tpDphHashNode sta, tpSirAssocRsp pAssocRsp, + struct pe_session *pe_session, + tSchBeaconStruct *beacon); + +/** + * lim_sta_add_bss_update_ht_parameter() - function to update ht related + * parameters when add bss request + * @bss_chan_freq: operating frequency of bss + * @ht_cap: ht capability extract from beacon/assoc response + * @ht_inf: ht information extract from beacon/assoc response + * @chan_width_support: local wide bandwith support capability + * @add_bss: add bss request struct to be updated + * + * Return: none + */ +void lim_sta_add_bss_update_ht_parameter(uint32_t bss_chan_freq, + tDot11fIEHTCaps* ht_cap, + tDot11fIEHTInfo* ht_inf, + bool chan_width_support, + struct bss_params *add_bss); + +/** + * lim_sta_send_add_bss() - add bss and send peer assoc after receive assoc + * rsp in sta mode + *.@mac: pointer to Global MAC structure + * @pAssocRsp: contains the structured assoc/reassoc Response got from AP + * @beaconstruct: the ProbeRsp/Beacon structured details + * @bssDescription: bss description passed to PE from the SME + * @updateEntry: bool flag of whether update bss and sta + * @pe_session: pointer to pe session + * + * Return: none + */ +QDF_STATUS lim_sta_send_add_bss(struct mac_context *mac, + tpSirAssocRsp pAssocRsp, + tpSchBeaconStruct pBeaconStruct, + struct bss_description *bssDescription, + uint8_t updateEntry, + struct pe_session *pe_session); + +/** + * lim_sta_send_add_bss_pre_assoc() - add bss after channel switch and before + * associate req in sta mode + *.@mac: pointer to Global MAC structure + * @pe_session: pointer to pe session + * + * Return: none + */ +QDF_STATUS lim_sta_send_add_bss_pre_assoc(struct mac_context *mac, + struct pe_session *pe_session); + +void lim_prepare_and_send_del_sta_cnf(struct mac_context *mac, + tpDphHashNode sta, + tSirResultCodes status_code, + struct pe_session *pe_session); + +QDF_STATUS lim_extract_ap_capabilities(struct mac_context *mac, uint8_t *pIE, + uint16_t ieLen, + tpSirProbeRespBeacon beaconStruct); +void lim_init_pre_auth_timer_table(struct mac_context *mac, + tpLimPreAuthTable pPreAuthTimerTable); +tpLimPreAuthNode lim_acquire_free_pre_auth_node(struct mac_context *mac, + tpLimPreAuthTable + pPreAuthTimerTable); +tpLimPreAuthNode lim_get_pre_auth_node_from_index(struct mac_context *mac, + tpLimPreAuthTable pAuthTable, + uint32_t authNodeIdx); + +/* Util API to check if the channels supported by STA is within range */ +QDF_STATUS lim_is_dot11h_supported_channels_valid(struct mac_context *mac, + tSirAssocReq *assoc); + +/* Util API to check if the txpower supported by STA is within range */ +QDF_STATUS lim_is_dot11h_power_capabilities_in_range(struct mac_context *mac, + tSirAssocReq *assoc, + struct pe_session *); +/** + * lim_fill_rx_highest_supported_rate() - Fill highest rx rate + * @mac: Global MAC context + * @rxHighestRate: location to store the highest rate + * @pSupportedMCSSet: location of the 'supported MCS set' field in HT + * capability element + * + * Fills in the Rx Highest Supported Data Rate field from + * the 'supported MCS set' field in HT capability element. + * + * Return: void + */ +void lim_fill_rx_highest_supported_rate(struct mac_context *mac, + uint16_t *rxHighestRate, + uint8_t *pSupportedMCSSet); +#ifdef WLAN_FEATURE_11W +void lim_send_sme_unprotected_mgmt_frame_ind(struct mac_context *mac, uint8_t frameType, + uint8_t *frame, uint32_t frameLen, + uint16_t sessionId, + struct pe_session *pe_session); +#endif + +/** + * lim_send_sme_tsm_ie_ind() - Send TSM IE information to SME + * @mac: Global MAC context + * @pe_session: PE session context + * @tid: traffic id + * @state: tsm state (enabled/disabled) + * @measurement_interval: measurement interval + * + * Return: void + */ +#ifdef FEATURE_WLAN_ESE +void lim_send_sme_tsm_ie_ind(struct mac_context *mac, + struct pe_session *pe_session, + uint8_t tid, uint8_t state, + uint16_t measurement_interval); +#else +static inline +void lim_send_sme_tsm_ie_ind(struct mac_context *mac, + struct pe_session *pe_session, + uint8_t tid, uint8_t state, + uint16_t measurement_interval) +{} +#endif /* FEATURE_WLAN_ESE */ + +/** + * lim_populate_vht_mcs_set - function to populate vht mcs rate set + * @mac_ctx: pointer to global mac structure + * @rates: pointer to supported rate set + * @peer_vht_caps: pointer to peer vht capabilities + * @session_entry: pe session entry + * @nss: number of spatial streams + * @sta_ds: pointer to peer sta data structure + * + * Populates vht mcs rate set based on peer and self capabilities + * + * Return: QDF_STATUS_SUCCESS on success else QDF_STATUS_E_FAILURE + */ +QDF_STATUS lim_populate_vht_mcs_set(struct mac_context *mac_ctx, + struct supported_rates *rates, + tDot11fIEVHTCaps *peer_vht_caps, + struct pe_session *session_entry, + uint8_t nss, + struct sDphHashNode *sta_ds); + +/** + * lim_extract_ies_from_deauth_disassoc() - Extract IEs from deauth/disassoc + * + * @session: PE session entry + * @deauth_disassoc_frame: A pointer to the deauth/disconnect frame buffer + * received from WMA. + * @deauth_disassoc_frame_leni: Length of the deauth/disconnect frame. + * + * This function receives deauth/disassoc frame from header. It extracts + * the IEs(tagged params) from the frame and caches in vdev object. + * + * Return: None + */ +void +lim_extract_ies_from_deauth_disassoc(struct pe_session *session, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len); + +/** + * lim_update_vhtcaps_assoc_resp : Update VHT caps in assoc response. + * @mac_ctx Pointer to Global MAC structure + * @pAddBssParams: parameters required for add bss params. + * @vht_caps: VHT capabilities. + * @pe_session : session entry. + * + * Return : void + */ +void lim_update_vhtcaps_assoc_resp(struct mac_context *mac_ctx, + struct bss_params *pAddBssParams, + tDot11fIEVHTCaps *vht_caps, + struct pe_session *pe_session); +#endif /* __LIM_ASSOC_UTILS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ft.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ft.c new file mode 100644 index 0000000000000000000000000000000000000000..c6f17ee065e471cb515efe1f786516881faed1ee --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ft.c @@ -0,0 +1,1031 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + \brief implementation for PE 11r VoWiFi FT Protocol + + ========================================================================*/ + +/* $Header$ */ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wmm_apsd.h" +#include "wma.h" + +/*-------------------------------------------------------------------------- + Initialize the FT variables. + ------------------------------------------------------------------------*/ +void lim_ft_open(struct mac_context *mac, struct pe_session *pe_session) +{ + if (pe_session) + qdf_mem_zero(&pe_session->ftPEContext, sizeof(tftPEContext)); +} + +void lim_ft_cleanup_all_ft_sessions(struct mac_context *mac) +{ + /* Wrapper function to cleanup all FT sessions */ + int i; + + for (i = 0; i < mac->lim.maxBssId; i++) { + if (true == mac->lim.gpSession[i].valid) { + /* The session is valid, may have FT data */ + lim_ft_cleanup(mac, &mac->lim.gpSession[i]); + } + } +} + +void lim_ft_cleanup(struct mac_context *mac, struct pe_session *pe_session) +{ + if (!pe_session) { + pe_err("pe_session is NULL"); + return; + } + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_debug("pe_session is not in STA mode"); + return; + } + + if (pe_session->ftPEContext.pFTPreAuthReq) { + pe_debug("Freeing pFTPreAuthReq: %pK", + pe_session->ftPEContext.pFTPreAuthReq); + if (NULL != + pe_session->ftPEContext.pFTPreAuthReq-> + pbssDescription) { + qdf_mem_free(pe_session->ftPEContext.pFTPreAuthReq-> + pbssDescription); + pe_session->ftPEContext.pFTPreAuthReq-> + pbssDescription = NULL; + } + qdf_mem_free(pe_session->ftPEContext.pFTPreAuthReq); + pe_session->ftPEContext.pFTPreAuthReq = NULL; + } + + if (pe_session->ftPEContext.pAddBssReq) { + qdf_mem_free(pe_session->ftPEContext.pAddBssReq); + pe_session->ftPEContext.pAddBssReq = NULL; + } + + if (pe_session->ftPEContext.pAddStaReq) { + qdf_mem_free(pe_session->ftPEContext.pAddStaReq); + pe_session->ftPEContext.pAddStaReq = NULL; + } + + /* The session is being deleted, cleanup the contents */ + qdf_mem_zero(&pe_session->ftPEContext, sizeof(tftPEContext)); +} + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +/*------------------------------------------------------------------ + * + * Create the new Add Bss Req to the new AP. + * This will be used when we are ready to FT to the new AP. + * The newly created ft Session entry is passed to this function + * + *------------------------------------------------------------------*/ +void lim_ft_prepare_add_bss_req(struct mac_context *mac, + struct pe_session *ft_session, + struct bss_description *bssDescription) +{ + struct bss_params *pAddBssParams = NULL; + tAddStaParams *sta_ctx; + bool chan_width_support = false; + tSchBeaconStruct *pBeaconStruct; + tDot11fIEVHTCaps *vht_caps = NULL; + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(ft_session)) { + pe_err("pe_session is not in STA mode"); + return; + } + + pBeaconStruct = qdf_mem_malloc(sizeof(tSchBeaconStruct)); + if (!pBeaconStruct) + return; + + /* Package SIR_HAL_ADD_BSS_REQ message parameters */ + pAddBssParams = qdf_mem_malloc(sizeof(struct bss_params)); + if (!pAddBssParams) { + qdf_mem_free(pBeaconStruct); + return; + } + + lim_extract_ap_capabilities(mac, (uint8_t *) bssDescription->ieFields, + lim_get_ielen_from_bss_description(bssDescription), + pBeaconStruct); + + if (mac->lim.gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) + lim_decide_sta_protection_on_assoc(mac, pBeaconStruct, + ft_session); + + qdf_mem_copy(pAddBssParams->bssId, bssDescription->bssId, + sizeof(tSirMacAddr)); + pAddBssParams->beaconInterval = bssDescription->beaconInterval; + + pAddBssParams->dtimPeriod = pBeaconStruct->tim.dtimPeriod; + pAddBssParams->updateBss = false; + + pAddBssParams->nwType = bssDescription->nwType; + + pAddBssParams->shortSlotTimeSupported = + (uint8_t) pBeaconStruct->capabilityInfo.shortSlotTime; + pAddBssParams->llbCoexist = + (uint8_t) ft_session->beaconParams.llbCoexist; +#ifdef WLAN_FEATURE_11W + pAddBssParams->rmfEnabled = ft_session->limRmfEnabled; +#endif + /* Use the advertised capabilities from the received beacon/PR */ + if (IS_DOT11_MODE_HT(ft_session->dot11mode) && + (pBeaconStruct->HTCaps.present)) { + chan_width_support = + lim_get_ht_capability(mac, + eHT_SUPPORTED_CHANNEL_WIDTH_SET, + ft_session); + lim_sta_add_bss_update_ht_parameter(bssDescription->chan_freq, + &pBeaconStruct->HTCaps, + &pBeaconStruct->HTInfo, + chan_width_support, + pAddBssParams); + qdf_mem_copy(&pAddBssParams->staContext.capab_info, + &pBeaconStruct->capabilityInfo, + sizeof(pAddBssParams->staContext.capab_info)); + qdf_mem_copy(&pAddBssParams->staContext.ht_caps, + (uint8_t *) &pBeaconStruct->HTCaps + + sizeof(uint8_t), + sizeof(pAddBssParams->staContext.ht_caps)); + } + + ft_session->htSecondaryChannelOffset = + pBeaconStruct->HTInfo.secondaryChannelOffset; + sta_ctx = &pAddBssParams->staContext; + + if (ft_session->vhtCapability && + ft_session->vhtCapabilityPresentInBeacon) { + pAddBssParams->vhtCapable = pBeaconStruct->VHTCaps.present; + if (pBeaconStruct->VHTOperation.chanWidth && chan_width_support) + pAddBssParams->ch_width = + pBeaconStruct->VHTOperation.chanWidth + 1; + vht_caps = &pBeaconStruct->VHTCaps; + lim_update_vhtcaps_assoc_resp(mac, pAddBssParams, + vht_caps, ft_session); + } else if (ft_session->vhtCapability && + pBeaconStruct->vendor_vht_ie.VHTCaps.present) { + pe_debug("VHT caps are present in vendor specific IE"); + pAddBssParams->vhtCapable = + pBeaconStruct->vendor_vht_ie.VHTCaps.present; + if (pBeaconStruct->vendor_vht_ie.VHTOperation.chanWidth && + chan_width_support) + pAddBssParams->ch_width = + pBeaconStruct->vendor_vht_ie.VHTOperation.chanWidth + 1; + vht_caps = &pBeaconStruct->vendor_vht_ie.VHTCaps; + lim_update_vhtcaps_assoc_resp(mac, pAddBssParams, + vht_caps, ft_session); + } else { + pAddBssParams->vhtCapable = 0; + } + + if (lim_is_session_he_capable(ft_session) && + pBeaconStruct->he_cap.present) { + lim_update_bss_he_capable(mac, pAddBssParams); + lim_add_bss_he_cfg(pAddBssParams, ft_session); + } + + pe_debug("SIR_HAL_ADD_BSS_REQ with frequency: %d", + bssDescription->chan_freq); + + /* Populate the STA-related parameters here */ + /* Note that the STA here refers to the AP */ + { + pAddBssParams->staContext.staType = STA_ENTRY_OTHER; + + qdf_mem_copy(pAddBssParams->staContext.bssId, + bssDescription->bssId, sizeof(tSirMacAddr)); + pAddBssParams->staContext.listenInterval = + bssDescription->beaconInterval; + + pAddBssParams->staContext.assocId = 0; + pAddBssParams->staContext.uAPSD = 0; + pAddBssParams->staContext.maxSPLen = 0; + pAddBssParams->staContext.updateSta = false; + pAddBssParams->staContext.encryptType = + ft_session->encryptType; +#ifdef WLAN_FEATURE_11W + pAddBssParams->staContext.rmfEnabled = + ft_session->limRmfEnabled; +#endif + + if (IS_DOT11_MODE_HT(ft_session->dot11mode) && + (pBeaconStruct->HTCaps.present)) { + pAddBssParams->staContext.htCapable = 1; + if (pBeaconStruct->HTCaps.supportedChannelWidthSet && + chan_width_support) { + pAddBssParams->staContext.ch_width = (uint8_t) + pBeaconStruct->HTInfo.recommendedTxWidthSet; + } else { + pAddBssParams->staContext.ch_width = + CH_WIDTH_20MHZ; + } + if (ft_session->vhtCapability && + IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps)) { + pAddBssParams->staContext.vhtCapable = 1; + if ((pBeaconStruct->VHTCaps.suBeamFormerCap || + pBeaconStruct->VHTCaps.muBeamformerCap) && + ft_session->vht_config.su_beam_formee) + sta_ctx->vhtTxBFCapable + = 1; + if (pBeaconStruct->VHTCaps.suBeamformeeCap && + ft_session->vht_config.su_beam_former) + sta_ctx->enable_su_tx_bformer = 1; + } + if (lim_is_session_he_capable(ft_session) && + pBeaconStruct->he_cap.present) + lim_intersect_ap_he_caps(ft_session, + pAddBssParams, pBeaconStruct, NULL); + + if (pBeaconStruct->HTCaps.supportedChannelWidthSet && + chan_width_support) { + sta_ctx->ch_width = (uint8_t) + pBeaconStruct->HTInfo.recommendedTxWidthSet; + if (pAddBssParams->staContext.vhtCapable && + pBeaconStruct->VHTOperation.chanWidth) + sta_ctx->ch_width = + pBeaconStruct->VHTOperation.chanWidth + + 1; + } else { + pAddBssParams->staContext.ch_width = + CH_WIDTH_20MHZ; + } + pAddBssParams->staContext.mimoPS = + (tSirMacHTMIMOPowerSaveState) pBeaconStruct->HTCaps. + mimoPowerSave; + pAddBssParams->staContext.maxAmpduDensity = + pBeaconStruct->HTCaps.mpduDensity; + pAddBssParams->staContext.fShortGI20Mhz = + (uint8_t) pBeaconStruct->HTCaps.shortGI20MHz; + pAddBssParams->staContext.fShortGI40Mhz = + (uint8_t) pBeaconStruct->HTCaps.shortGI40MHz; + pAddBssParams->staContext.maxAmpduSize = + pBeaconStruct->HTCaps.maxRxAMPDUFactor; + } + + if ((ft_session->limWmeEnabled + && pBeaconStruct->wmeEdcaPresent) + || (ft_session->limQosEnabled + && pBeaconStruct->edcaPresent)) + pAddBssParams->staContext.wmmEnabled = 1; + else + pAddBssParams->staContext.wmmEnabled = 0; + + pAddBssParams->staContext.wpa_rsn = pBeaconStruct->rsnPresent; + /* For OSEN Connection AP does not advertise RSN or WPA IE + * so from the IEs we get from supplicant we get this info + * so for FW to transmit EAPOL message 4 we shall set + * wpa_rsn + */ + pAddBssParams->staContext.wpa_rsn |= + (pBeaconStruct->wpaPresent << 1); + if ((!pAddBssParams->staContext.wpa_rsn) + && (ft_session->isOSENConnection)) + pAddBssParams->staContext.wpa_rsn = 1; + /* Update the rates */ + lim_populate_peer_rate_set(mac, + &pAddBssParams->staContext. + supportedRates, + pBeaconStruct->HTCaps.supportedMCSSet, + false, ft_session, + &pBeaconStruct->VHTCaps, + &pBeaconStruct->he_cap, NULL, + bssDescription); + } + + pAddBssParams->maxTxPower = ft_session->maxTxPower; + +#ifdef WLAN_FEATURE_11W + if (ft_session->limRmfEnabled) { + pAddBssParams->rmfEnabled = 1; + pAddBssParams->staContext.rmfEnabled = 1; + } +#endif + pAddBssParams->staContext.sessionId = ft_session->peSessionId; + pAddBssParams->staContext.smesessionId = ft_session->smeSessionId; + + /* Set a new state for MLME */ + if (!lim_is_roam_synch_in_progress(ft_session)) { + ft_session->limMlmState = + eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, + ft_session->peSessionId, + eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE)); + } + ft_session->ftPEContext.pAddBssReq = pAddBssParams; + + pe_debug("Saving SIR_HAL_ADD_BSS_REQ for pre-auth ap"); + + qdf_mem_free(pBeaconStruct); + return; +} +#endif + +#if defined(WLAN_FEATURE_ROAM_OFFLOAD) + +/** + * lim_convert_phymode_to_dot11mode() - get dot11 mode from phymode + * @phymode: phymode + * + * The function is to convert the phymode to corresponding dot11 mode + * + * Return: dot11mode. + */ +static uint8_t lim_convert_phymode_to_dot11mode(enum wlan_phymode phymode) +{ + + if (IS_WLAN_PHYMODE_HE(phymode)) + return MLME_DOT11_MODE_11AX; + + if (IS_WLAN_PHYMODE_VHT(phymode)) + return MLME_DOT11_MODE_11AC; + + if (IS_WLAN_PHYMODE_HT(phymode)) + return MLME_DOT11_MODE_11N; + + if (phymode == WLAN_PHYMODE_11G) + return MLME_DOT11_MODE_11G; + + if (phymode == WLAN_PHYMODE_11G_ONLY) + return MLME_DOT11_MODE_11G_ONLY; + + if (phymode == WLAN_PHYMODE_11A) + return MLME_DOT11_MODE_11A; + + if (phymode == WLAN_PHYMODE_11B) + return MLME_DOT11_MODE_11B; + + return MLME_DOT11_MODE_ALL; +} + +/** + * lim_calculate_dot11_mode() - calculate dot11 mode. + * @mac_context: mac context + * @bcn: beacon structure + * @band: reg_wifi_band + * + * The function is to calculate dot11 mode in case fw doen't send phy mode. + * + * Return: dot11mode. + */ +static uint8_t lim_calculate_dot11_mode(struct mac_context *mac_ctx, + tSchBeaconStruct *bcn, + enum reg_wifi_band band) +{ + enum mlme_dot11_mode self_dot11_mode; + enum mlme_dot11_mode new_dot11_mode; + + self_dot11_mode = mac_ctx->mlme_cfg->dot11_mode.dot11_mode; + + if (band == REG_BAND_2G) + new_dot11_mode = MLME_DOT11_MODE_11G; + else + new_dot11_mode = MLME_DOT11_MODE_11A; + + switch (self_dot11_mode) { + case MLME_DOT11_MODE_11AX: + case MLME_DOT11_MODE_11AX_ONLY: + case MLME_DOT11_MODE_ALL: + if (bcn->he_cap.present) + return MLME_DOT11_MODE_11AX; + else if (bcn->VHTCaps.present || + bcn->vendor_vht_ie.present) + return MLME_DOT11_MODE_11AC; + else if (bcn->HTCaps.present) + return MLME_DOT11_MODE_11N; + case MLME_DOT11_MODE_11AC: + case MLME_DOT11_MODE_11AC_ONLY: + if (bcn->VHTCaps.present || + bcn->vendor_vht_ie.present) + return MLME_DOT11_MODE_11AC; + else if (bcn->HTCaps.present) + return MLME_DOT11_MODE_11N; + case MLME_DOT11_MODE_11N: + case MLME_DOT11_MODE_11N_ONLY: + if (bcn->HTCaps.present) + return MLME_DOT11_MODE_11N; + default: + return new_dot11_mode; + } + +} + +/** + * lim_fill_dot11mode() - to fill 802.11 mode in FT session + * @mac_ctx: pointer to mac ctx + * @ft_session: FT session + * @pe_session: PE session + * @bcn: AP beacon pointer + * @bss_phymode: bss phy mode + * + * This API fills FT session's dot11mode either from pe session or + * from CFG depending on the condition. + * + * Return: none + */ +static void lim_fill_dot11mode(struct mac_context *mac_ctx, + struct pe_session *ft_session, + struct pe_session *pe_session, + tSchBeaconStruct *bcn, + enum wlan_phymode bss_phymode) +{ + if (pe_session->ftPEContext.pFTPreAuthReq && + !csr_is_roam_offload_enabled(mac_ctx)) { + ft_session->dot11mode = + pe_session->ftPEContext.pFTPreAuthReq->dot11mode; + return; + } + + if (bss_phymode == WLAN_PHYMODE_AUTO) + ft_session->dot11mode = lim_calculate_dot11_mode( + mac_ctx, bcn, + ft_session->limRFBand); + + else + ft_session->dot11mode = + lim_convert_phymode_to_dot11mode(bss_phymode); +} +#elif defined(WLAN_FEATURE_HOST_ROAM) +/** + * lim_fill_dot11mode() - to fill 802.11 mode in FT session + * @mac_ctx: pointer to mac ctx + * @ft_session: FT session + * @pe_session: PE session + * @bcn: AP beacon pointer + * @bss_phymode: bss phy mode + * + * This API fills FT session's dot11mode either from pe session. + * + * Return: none + */ +static void lim_fill_dot11mode(struct mac_context *mac_ctx, + struct pe_session *ft_session, + struct pe_session *pe_session, + tSchBeaconStruct *bcn, + enum wlan_phymode bss_phymode) +{ + ft_session->dot11mode = + pe_session->ftPEContext.pFTPreAuthReq->dot11mode; +} +#endif + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +/*------------------------------------------------------------------ + * + * Setup the new session for the pre-auth AP. + * Return the newly created session entry. + * + *------------------------------------------------------------------*/ +void lim_fill_ft_session(struct mac_context *mac, + struct bss_description *pbssDescription, + struct pe_session *ft_session, + struct pe_session *pe_session, + enum wlan_phymode bss_phymode) +{ + uint8_t currentBssUapsd; + uint8_t bss_chan_id; + int8_t localPowerConstraint; + int8_t regMax; + tSchBeaconStruct *pBeaconStruct; + ePhyChanBondState cbEnabledMode; + struct lim_max_tx_pwr_attr tx_pwr_attr = {0}; + + pBeaconStruct = qdf_mem_malloc(sizeof(tSchBeaconStruct)); + if (!pBeaconStruct) + return; + + /* Retrieve the session that was already created and update the entry */ + ft_session->limWmeEnabled = pe_session->limWmeEnabled; + ft_session->limQosEnabled = pe_session->limQosEnabled; + ft_session->limWsmEnabled = pe_session->limWsmEnabled; + ft_session->lim11hEnable = pe_session->lim11hEnable; + ft_session->isOSENConnection = pe_session->isOSENConnection; + ft_session->connected_akm = pe_session->connected_akm; + + /* Fields to be filled later */ + ft_session->lim_join_req = NULL; + ft_session->smeSessionId = pe_session->smeSessionId; + + lim_extract_ap_capabilities(mac, (uint8_t *) pbssDescription->ieFields, + lim_get_ielen_from_bss_description(pbssDescription), + pBeaconStruct); + + ft_session->rateSet.numRates = + pBeaconStruct->supportedRates.numRates; + qdf_mem_copy(ft_session->rateSet.rate, + pBeaconStruct->supportedRates.rate, + pBeaconStruct->supportedRates.numRates); + + ft_session->extRateSet.numRates = + pBeaconStruct->extendedRates.numRates; + qdf_mem_copy(ft_session->extRateSet.rate, + pBeaconStruct->extendedRates.rate, + ft_session->extRateSet.numRates); + + ft_session->ssId.length = pBeaconStruct->ssId.length; + qdf_mem_copy(ft_session->ssId.ssId, pBeaconStruct->ssId.ssId, + ft_session->ssId.length); + /* Copy The channel Id to the session Table */ + bss_chan_id = + wlan_reg_freq_to_chan(mac->pdev, pbssDescription->chan_freq); + ft_session->lim_reassoc_chan_freq = pbssDescription->chan_freq; + ft_session->curr_op_freq = pbssDescription->chan_freq; + ft_session->limRFBand = lim_get_rf_band(ft_session->curr_op_freq); + + lim_fill_dot11mode(mac, ft_session, pe_session, pBeaconStruct, + bss_phymode); + pe_debug("dot11mode: %d bss_phymode %d", ft_session->dot11mode, + bss_phymode); + + ft_session->vhtCapability = + (IS_DOT11_MODE_VHT(ft_session->dot11mode) && + (IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps) || + IS_BSS_VHT_CAPABLE(pBeaconStruct->vendor_vht_ie.VHTCaps))); + ft_session->htCapability = + (IS_DOT11_MODE_HT(ft_session->dot11mode) + && pBeaconStruct->HTCaps.present); + + if (IS_DOT11_MODE_HE(ft_session->dot11mode) && + pBeaconStruct->he_cap.present) + lim_update_session_he_capable(mac, ft_session); + + /* Assign default configured nss value in the new session */ + if (wlan_reg_is_5ghz_ch_freq(ft_session->curr_op_freq)) + ft_session->vdev_nss = mac->vdev_type_nss_5g.sta; + else + ft_session->vdev_nss = mac->vdev_type_nss_2g.sta; + + ft_session->nss = ft_session ->vdev_nss; + + if (ft_session->limRFBand == REG_BAND_2G) { + cbEnabledMode = mac->roam.configParam.channelBondingMode24GHz; + } else { + cbEnabledMode = mac->roam.configParam.channelBondingMode5GHz; + } + ft_session->htSupportedChannelWidthSet = + (pBeaconStruct->HTInfo.present) ? + (cbEnabledMode && pBeaconStruct->HTInfo.recommendedTxWidthSet) : 0; + ft_session->htRecommendedTxWidthSet = + ft_session->htSupportedChannelWidthSet; + + if (IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps) && + pBeaconStruct->VHTOperation.present && + ft_session->vhtCapability) { + ft_session->vhtCapabilityPresentInBeacon = 1; + } else if (IS_BSS_VHT_CAPABLE(pBeaconStruct->vendor_vht_ie.VHTCaps) && + pBeaconStruct->vendor_vht_ie.VHTOperation.present && + ft_session->vhtCapability){ + ft_session->vhtCapabilityPresentInBeacon = 1; + } else { + ft_session->vhtCapabilityPresentInBeacon = 0; + } + + if (ft_session->htRecommendedTxWidthSet) { + ft_session->ch_width = CH_WIDTH_40MHZ; + if (ft_session->vhtCapabilityPresentInBeacon && + pBeaconStruct->VHTOperation.chanWidth) { + ft_session->ch_width = + pBeaconStruct->VHTOperation.chanWidth + 1; + ft_session->ch_center_freq_seg0 = + pBeaconStruct->VHTOperation.chan_center_freq_seg0; + ft_session->ch_center_freq_seg1 = + pBeaconStruct->VHTOperation.chan_center_freq_seg1; + } else if (ft_session->vhtCapabilityPresentInBeacon && + pBeaconStruct->vendor_vht_ie.VHTOperation.chanWidth){ + ft_session->ch_width = + pBeaconStruct->vendor_vht_ie.VHTOperation.chanWidth + 1; + ft_session->ch_center_freq_seg0 = + pBeaconStruct->vendor_vht_ie.VHTOperation.chan_center_freq_seg0; + ft_session->ch_center_freq_seg1 = + pBeaconStruct->vendor_vht_ie.VHTOperation.chan_center_freq_seg1; + + } else { + if (pBeaconStruct->HTInfo.secondaryChannelOffset == + PHY_DOUBLE_CHANNEL_LOW_PRIMARY) + ft_session->ch_center_freq_seg0 = + bss_chan_id + 2; + else if (pBeaconStruct->HTInfo.secondaryChannelOffset == + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY) + ft_session->ch_center_freq_seg0 = + bss_chan_id - 2; + else { + pe_warn("Invalid sec ch offset"); + ft_session->ch_width = CH_WIDTH_20MHZ; + ft_session->ch_center_freq_seg0 = 0; + ft_session->ch_center_freq_seg1 = 0; + } + } + } else { + ft_session->ch_width = CH_WIDTH_20MHZ; + ft_session->ch_center_freq_seg0 = 0; + ft_session->ch_center_freq_seg1 = 0; + } + + sir_copy_mac_addr(ft_session->self_mac_addr, + pe_session->self_mac_addr); + sir_copy_mac_addr(ft_session->limReAssocbssId, + pbssDescription->bssId); + sir_copy_mac_addr(ft_session->prev_ap_bssid, pe_session->bssId); + + /* Store beaconInterval */ + ft_session->beaconParams.beaconInterval = + pbssDescription->beaconInterval; + ft_session->bssType = pe_session->bssType; + + ft_session->statypeForBss = STA_ENTRY_PEER; + ft_session->nwType = pbssDescription->nwType; + + + if (ft_session->bssType == eSIR_INFRASTRUCTURE_MODE) { + ft_session->limSystemRole = eLIM_STA_ROLE; + } else { + /* Throw an error & return & make sure to delete the session */ + pe_warn("Invalid bss type"); + } + + ft_session->limCurrentBssCaps = pbssDescription->capabilityInfo; + ft_session->limReassocBssCaps = pbssDescription->capabilityInfo; + if (mac->mlme_cfg->ht_caps.short_slot_time_enabled && + SIR_MAC_GET_SHORT_SLOT_TIME(ft_session->limReassocBssCaps)) { + ft_session->shortSlotTimeSupported = true; + } + + regMax = wlan_reg_get_channel_reg_power_for_freq( + mac->pdev, ft_session->curr_op_freq); + localPowerConstraint = regMax; + lim_extract_ap_capability(mac, (uint8_t *) pbssDescription->ieFields, + + lim_get_ielen_from_bss_description(pbssDescription), + &ft_session->limCurrentBssQosCaps, + ¤tBssUapsd, + &localPowerConstraint, ft_session); + + ft_session->limReassocBssQosCaps = + ft_session->limCurrentBssQosCaps; + + ft_session->is11Rconnection = pe_session->is11Rconnection; +#ifdef FEATURE_WLAN_ESE + ft_session->isESEconnection = pe_session->isESEconnection; + ft_session->is_ese_version_ie_present = + pBeaconStruct->is_ese_ver_ie_present; +#endif + ft_session->isFastTransitionEnabled = + pe_session->isFastTransitionEnabled; + + ft_session->isFastRoamIniFeatureEnabled = + pe_session->isFastRoamIniFeatureEnabled; + + tx_pwr_attr.reg_max = regMax; + tx_pwr_attr.ap_tx_power = localPowerConstraint; + tx_pwr_attr.ini_tx_power = mac->mlme_cfg->power.max_tx_power; + tx_pwr_attr.frequency = ft_session->curr_op_freq; + +#ifdef FEATURE_WLAN_ESE + ft_session->maxTxPower = lim_get_max_tx_power(mac, &tx_pwr_attr); +#else + ft_session->maxTxPower = QDF_MIN(regMax, (localPowerConstraint)); +#endif + + pe_debug("Reg max: %d local pwr: %d, ini tx pwr: %d max tx pwr: %d", + regMax, localPowerConstraint, + mac->mlme_cfg->power.max_tx_power, + ft_session->maxTxPower); + if (!lim_is_roam_synch_in_progress(pe_session)) { + ft_session->limPrevSmeState = ft_session->limSmeState; + ft_session->limSmeState = eLIM_SME_WT_REASSOC_STATE; + MTRACE(mac_trace(mac, + TRACE_CODE_SME_STATE, + ft_session->peSessionId, + ft_session->limSmeState)); + } + ft_session->encryptType = pe_session->encryptType; +#ifdef WLAN_FEATURE_11W + ft_session->limRmfEnabled = pe_session->limRmfEnabled; +#endif + if ((ft_session->limRFBand == REG_BAND_2G) && + (ft_session->htSupportedChannelWidthSet == + eHT_CHANNEL_WIDTH_40MHZ)) + lim_init_obss_params(mac, ft_session); + + ft_session->enableHtSmps = pe_session->enableHtSmps; + ft_session->htSmpsvalue = pe_session->htSmpsvalue; + /* + * By default supported NSS 1x1 is set to true + * and later on updated while determining session + * supported rates which is the intersection of + * self and peer rates + */ + ft_session->supported_nss_1x1 = true; + pe_debug("FT enable smps: %d mode: %d supported nss 1x1: %d", + ft_session->enableHtSmps, + ft_session->htSmpsvalue, + ft_session->supported_nss_1x1); + + qdf_mem_free(pBeaconStruct); +} +#endif + +static void +lim_ft_send_aggr_qos_rsp(struct mac_context *mac, uint8_t rspReqd, + struct aggr_add_ts_param *aggrQosRsp, + uint8_t smesessionId) +{ + tpSirAggrQosRsp rsp; + int i = 0; + + if (!rspReqd) { + return; + } + rsp = qdf_mem_malloc(sizeof(tSirAggrQosRsp)); + if (!rsp) + return; + + rsp->messageType = eWNI_SME_FT_AGGR_QOS_RSP; + rsp->sessionId = smesessionId; + rsp->length = sizeof(*rsp); + rsp->aggrInfo.tspecIdx = aggrQosRsp->tspecIdx; + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + if ((1 << i) & aggrQosRsp->tspecIdx) { + if (QDF_IS_STATUS_SUCCESS(aggrQosRsp->status[i])) + rsp->aggrInfo.aggrRsp[i].status = + eSIR_MAC_SUCCESS_STATUS; + else + rsp->aggrInfo.aggrRsp[i].status = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + rsp->aggrInfo.aggrRsp[i].tspec = aggrQosRsp->tspec[i]; + } + } + lim_send_sme_aggr_qos_rsp(mac, rsp, smesessionId); + return; +} + +void lim_process_ft_aggr_qos_rsp(struct mac_context *mac, + struct scheduler_msg *limMsg) +{ + struct aggr_add_ts_param *pAggrQosRspMsg; + struct add_ts_param addTsParam = { 0 }; + tpDphHashNode pSta = NULL; + uint16_t assocId = 0; + tSirMacAddr peerMacAddr; + uint8_t rspReqd = 1; + struct pe_session *pe_session = NULL; + int i = 0; + + pe_debug(" Received AGGR_QOS_RSP from HAL"); + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + pAggrQosRspMsg = limMsg->bodyptr; + if (!pAggrQosRspMsg) { + pe_err("NULL pAggrQosRspMsg"); + return; + } + pe_session = + pe_find_session_by_session_id(mac, pAggrQosRspMsg->sessionId); + if (!pe_session) { + pe_err("Cant find session entry for %s", __func__); + if (pAggrQosRspMsg) { + qdf_mem_free(pAggrQosRspMsg); + } + return; + } + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("pe_session is not in STA mode"); + return; + } + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + if ((((1 << i) & pAggrQosRspMsg->tspecIdx)) && + (pAggrQosRspMsg->status[i] != QDF_STATUS_SUCCESS)) { + sir_copy_mac_addr(peerMacAddr, pe_session->bssId); + addTsParam.pe_session_id = pAggrQosRspMsg->sessionId; + addTsParam.tspec = pAggrQosRspMsg->tspec[i]; + addTsParam.tspec_idx = pAggrQosRspMsg->tspecIdx; + lim_send_delts_req_action_frame(mac, peerMacAddr, + rspReqd, + &addTsParam.tspec.tsinfo, + &addTsParam.tspec, + pe_session); + pSta = + dph_lookup_hash_entry(mac, peerMacAddr, + &assocId, + &pe_session-> + dph.dphHashTable); + + if (pSta) { + lim_admit_control_delete_ts(mac, assocId, + &addTsParam.tspec. + tsinfo, NULL, + (uint8_t *) & + addTsParam.tspec_idx); + } + } + } + lim_ft_send_aggr_qos_rsp(mac, rspReqd, pAggrQosRspMsg, + pe_session->smeSessionId); + if (pAggrQosRspMsg) { + qdf_mem_free(pAggrQosRspMsg); + } + return; +} + +QDF_STATUS lim_process_ft_aggr_qos_req(struct mac_context *mac, + uint32_t *msg_buf) +{ + struct scheduler_msg msg = {0}; + tSirAggrQosReq *aggrQosReq = (tSirAggrQosReq *) msg_buf; + struct aggr_add_ts_param *pAggrAddTsParam; + struct pe_session *pe_session = NULL; + tpLimTspecInfo tspecInfo; + uint8_t ac; + tpDphHashNode pSta; + uint16_t aid; + uint8_t sessionId; + int i; + + pAggrAddTsParam = qdf_mem_malloc(sizeof(*pAggrAddTsParam)); + if (!pAggrAddTsParam) + return QDF_STATUS_E_NOMEM; + + pe_session = pe_find_session_by_bssid(mac, aggrQosReq->bssid.bytes, + &sessionId); + + if (!pe_session) { + pe_err("psession Entry Null for sessionId: %d", + aggrQosReq->sessionId); + qdf_mem_free(pAggrAddTsParam); + return QDF_STATUS_E_FAILURE; + } + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("pe_session is not in STA mode"); + qdf_mem_free(pAggrAddTsParam); + return QDF_STATUS_E_FAILURE; + } + + pSta = dph_lookup_hash_entry(mac, aggrQosReq->bssid.bytes, &aid, + &pe_session->dph.dphHashTable); + if (!pSta) { + pe_err("Station context not found - ignoring AddTsRsp"); + qdf_mem_free(pAggrAddTsParam); + return QDF_STATUS_E_FAILURE; + } + + /* Fill in the sessionId specific to PE */ + pAggrAddTsParam->sessionId = sessionId; + pAggrAddTsParam->tspecIdx = aggrQosReq->aggrInfo.tspecIdx; + pAggrAddTsParam->vdev_id = pe_session->smeSessionId; + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + if (aggrQosReq->aggrInfo.tspecIdx & (1 << i)) { + struct mac_tspec_ie *pTspec = + &aggrQosReq->aggrInfo.aggrAddTsInfo[i].tspec; + /* Since AddTS response was successful, check for the PSB flag + * and directional flag inside the TS Info field. + * An AC is trigger enabled AC if the PSB subfield is set to 1 + * in the uplink direction. + * An AC is delivery enabled AC if the PSB subfield is set to 1 + * in the downlink direction. + * An AC is trigger and delivery enabled AC if the PSB subfield + * is set to 1 in the bi-direction field. + */ + if (pTspec->tsinfo.traffic.psb == 1) { + lim_set_tspec_uapsd_mask_per_session(mac, + pe_session, + &pTspec-> + tsinfo, + SET_UAPSD_MASK); + } else { + lim_set_tspec_uapsd_mask_per_session(mac, + pe_session, + &pTspec-> + tsinfo, + CLEAR_UAPSD_MASK); + } + /* + * ADDTS success, so AC is now admitted. + * We shall now use the default + * EDCA parameters as advertised by AP and + * send the updated EDCA params + * to HAL. + */ + ac = upToAc(pTspec->tsinfo.traffic.userPrio); + if (pTspec->tsinfo.traffic.direction == + SIR_MAC_DIRECTION_UPLINK) { + pe_session-> + gAcAdmitMask + [SIR_MAC_DIRECTION_UPLINK] |= + (1 << ac); + } else if (pTspec->tsinfo.traffic.direction == + SIR_MAC_DIRECTION_DNLINK) { + pe_session-> + gAcAdmitMask + [SIR_MAC_DIRECTION_DNLINK] |= + (1 << ac); + } else if (pTspec->tsinfo.traffic.direction == + SIR_MAC_DIRECTION_BIDIR) { + pe_session-> + gAcAdmitMask + [SIR_MAC_DIRECTION_UPLINK] |= + (1 << ac); + pe_session-> + gAcAdmitMask + [SIR_MAC_DIRECTION_DNLINK] |= + (1 << ac); + } + lim_set_active_edca_params(mac, + pe_session->gLimEdcaParams, + pe_session); + + lim_send_edca_params(mac, + pe_session->gLimEdcaParamsActive, + pe_session->vdev_id, false); + + if (QDF_STATUS_SUCCESS != + lim_tspec_add(mac, pSta->staAddr, pSta->assocId, + pTspec, 0, &tspecInfo)) { + pe_err("Adding entry in lim Tspec Table failed"); + mac->lim.gLimAddtsSent = false; + qdf_mem_free(pAggrAddTsParam); + return QDF_STATUS_E_FAILURE; + } + + pAggrAddTsParam->tspec[i] = + aggrQosReq->aggrInfo.aggrAddTsInfo[i].tspec; + } + } + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (!mac->mlme_cfg->lfr.lfr3_roaming_offload || + (mac->mlme_cfg->lfr.lfr3_roaming_offload && + !pe_session->is11Rconnection)) +#endif + { + msg.type = WMA_AGGR_QOS_REQ; + msg.bodyptr = pAggrAddTsParam; + msg.bodyval = 0; + + /* We need to defer any incoming messages until we get a + * WMA_AGGR_QOS_RSP from HAL. + */ + SET_LIM_PROCESS_DEFD_MESGS(mac, false); + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, msg.type)); + + if (QDF_STATUS_SUCCESS != wma_post_ctrl_msg(mac, &msg)) { + pe_warn("wma_post_ctrl_msg() failed"); + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + qdf_mem_free(pAggrAddTsParam); + return QDF_STATUS_E_FAILURE; + } + } +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + else { + /* Implies it is a LFR3.0 based 11r connection + * so donot send add ts request to firmware since it + * already has the RIC IEs */ + + /* Send the Aggr QoS response to SME */ + lim_ft_send_aggr_qos_rsp(mac, true, pAggrAddTsParam, + pe_session->smeSessionId); + if (pAggrAddTsParam) { + qdf_mem_free(pAggrAddTsParam); + } + } +#endif + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ft_preauth.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ft_preauth.c new file mode 100644 index 0000000000000000000000000000000000000000..0a77b623dd9ff2d667297ee38d6a7913820b6f7e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ft_preauth.c @@ -0,0 +1,790 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: lim_ft_preauth.c + * + * Pre-Authentication implementation for host based roaming + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wma.h" + +/** + * lim_ft_cleanup_pre_auth_info() - Cleanup preauth related information + * @mac: Global MAC Context + * @pe_session: PE Session + * + * This routine is called to free the FT context, session and other + * information used during preauth operation. + * + * Return: None + */ +void lim_ft_cleanup_pre_auth_info(struct mac_context *mac, + struct pe_session *pe_session) +{ + struct pe_session *pReAssocSessionEntry = NULL; + uint8_t sessionId = 0; + + if (!pe_session) { + pe_err("pe_session is NULL"); + return; + } + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("pe_session is not in STA mode"); + return; + } + + if (pe_session->ftPEContext.pFTPreAuthReq) { + pReAssocSessionEntry = + pe_find_session_by_bssid(mac, + pe_session->ftPEContext. + pFTPreAuthReq->preAuthbssId, + &sessionId); + + if (pe_session->ftPEContext.pFTPreAuthReq-> + pbssDescription) { + qdf_mem_free(pe_session->ftPEContext.pFTPreAuthReq-> + pbssDescription); + pe_session->ftPEContext.pFTPreAuthReq-> + pbssDescription = NULL; + } + qdf_mem_free(pe_session->ftPEContext.pFTPreAuthReq); + pe_session->ftPEContext.pFTPreAuthReq = NULL; + } + + if (pe_session->ftPEContext.pAddBssReq) { + qdf_mem_free(pe_session->ftPEContext.pAddBssReq); + pe_session->ftPEContext.pAddBssReq = NULL; + } + + if (pe_session->ftPEContext.pAddStaReq) { + qdf_mem_free(pe_session->ftPEContext.pAddStaReq); + pe_session->ftPEContext.pAddStaReq = NULL; + } + + /* The session is being deleted, cleanup the contents */ + qdf_mem_zero(&pe_session->ftPEContext, sizeof(tftPEContext)); + + /* Delete the session created while handling pre-auth response */ + if (pReAssocSessionEntry) { + /* If we have successful pre-auth response, then we would have + * created a session on which reassoc request will be sent + */ + if (pReAssocSessionEntry->valid && + pReAssocSessionEntry->limSmeState == + eLIM_SME_WT_REASSOC_STATE) { + QDF_TRACE(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_DEBUG, + FL("Deleting Preauth session(%d)"), + pReAssocSessionEntry->peSessionId); + pe_delete_session(mac, pReAssocSessionEntry); + } + } +} + +/* + * lim_process_ft_pre_auth_req() - process ft pre auth req + * + * @mac_ctx: global mac ctx + * @msg: pointer to message + * + * In this function, we process the FT Pre Auth Req: + * We receive Pre-Auth, suspend link, register a call back. In the call back, + * we will need to accept frames from the new bssid. Send out the auth req to + * new AP. Start timer and when the timer is done or if we receive the Auth + * response. We change channel. Resume link + * + * Return: value to indicate if buffer was consumed + */ +int lim_process_ft_pre_auth_req(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + int buf_consumed = false; + struct pe_session *session; + uint8_t session_id; + tpSirFTPreAuthReq ft_pre_auth_req = (tSirFTPreAuthReq *) msg->bodyptr; + + if (!ft_pre_auth_req) { + pe_err("tSirFTPreAuthReq is NULL"); + return buf_consumed; + } + + /* Get the current session entry */ + session = pe_find_session_by_bssid(mac_ctx, + ft_pre_auth_req->currbssId, + &session_id); + if (!session) { + pe_err("Unable to find session for the bssid" + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(ft_pre_auth_req->currbssId)); + /* Post the FT Pre Auth Response to SME */ + lim_post_ft_pre_auth_rsp(mac_ctx, QDF_STATUS_E_FAILURE, NULL, 0, + session); + buf_consumed = true; + return buf_consumed; + } + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(session)) { + pe_err("session is not in STA mode"); + buf_consumed = true; + return buf_consumed; + } + + /* Can set it only after sending auth */ + session->ftPEContext.ftPreAuthStatus = QDF_STATUS_E_FAILURE; + session->ftPEContext.ftPreAuthSession = true; + + /* Indicate that this is the session on which preauth is being done */ + if (session->ftPEContext.pFTPreAuthReq) { + if (session->ftPEContext.pFTPreAuthReq->pbssDescription) { + qdf_mem_free( + session->ftPEContext.pFTPreAuthReq->pbssDescription); + session->ftPEContext.pFTPreAuthReq->pbssDescription = + NULL; + } + qdf_mem_free(session->ftPEContext.pFTPreAuthReq); + session->ftPEContext.pFTPreAuthReq = NULL; + } + + /* We need information from the Pre-Auth Req. Lets save that */ + session->ftPEContext.pFTPreAuthReq = ft_pre_auth_req; + + pe_debug("PRE Auth ft_ies_length=%02x%02x%02x", + session->ftPEContext.pFTPreAuthReq->ft_ies[0], + session->ftPEContext.pFTPreAuthReq->ft_ies[1], + session->ftPEContext.pFTPreAuthReq->ft_ies[2]); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_PRE_AUTH_REQ_EVENT, + session, 0, 0); +#endif + + /* + * Dont need to suspend if APs are in same channel and DUT + * is not in MCC state + */ + if ((session->curr_op_freq != + session->ftPEContext.pFTPreAuthReq->pre_auth_channel_freq) + || lim_is_in_mcc(mac_ctx)) { + /* Need to suspend link only if the channels are different */ + pe_debug("Performing pre-auth on diff channel(session %pK)", + session); + lim_send_preauth_scan_offload(mac_ctx, session, + session->ftPEContext.pFTPreAuthReq); + } else { + pe_debug("Performing pre-auth on same channel (session %pK)", + session); + /* We are in the same channel. Perform pre-auth */ + lim_perform_ft_pre_auth(mac_ctx, QDF_STATUS_SUCCESS, NULL, + session); + } + + return buf_consumed; +} + +/** + * lim_perform_ft_pre_auth() - Perform preauthentication + * @mac: Global MAC Context + * @status: Status Code + * @data: pre-auth data + * @pe_session: PE Session + * + * This routine will trigger the sending of authentication frame + * to the peer. + * + * Return: None + */ +void lim_perform_ft_pre_auth(struct mac_context *mac, QDF_STATUS status, + uint32_t *data, struct pe_session *pe_session) +{ + tSirMacAuthFrameBody authFrame; + unsigned int session_id; + enum csr_akm_type auth_type; + + if (!pe_session) { + pe_err("pe_session is NULL"); + return; + } + session_id = pe_session->smeSessionId; + auth_type = + mac->roam.roamSession[session_id].connectedProfile.AuthType; + + if (pe_session->is11Rconnection && + pe_session->ftPEContext.pFTPreAuthReq) { + /* Only 11r assoc has FT IEs */ + if ((auth_type != eCSR_AUTH_TYPE_OPEN_SYSTEM) && + (pe_session->ftPEContext.pFTPreAuthReq->ft_ies_length + == 0)) { + pe_err("FTIEs for Auth Req Seq 1 is absent"); + goto preauth_fail; + } + } + + if (status != QDF_STATUS_SUCCESS) { + pe_err("Change channel not successful for FT pre-auth"); + goto preauth_fail; + } + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("pe_session is not in STA mode"); + return; + } + pe_debug("Entered wait auth2 state for FT (old session %pK)", + pe_session); + if (pe_session->is11Rconnection) { + /* Now we are on the right channel and need to send out Auth1 + * and receive Auth2 + */ + authFrame.authAlgoNumber = eSIR_FT_AUTH; + } else { + /* Will need to make isESEconnection a enum may be for further + * improvements to this to match this algorithm number + */ + authFrame.authAlgoNumber = eSIR_OPEN_SYSTEM; + } + authFrame.authTransactionSeqNumber = SIR_MAC_AUTH_FRAME_1; + authFrame.authStatusCode = 0; + + mac->lim.lim_timers.g_lim_periodic_auth_retry_timer.sessionId = + pe_session->peSessionId; + + /* Start timer here to come back to operating channel */ + mac->lim.lim_timers.gLimFTPreAuthRspTimer.sessionId = + pe_session->peSessionId; + if (TX_SUCCESS != + tx_timer_activate(&mac->lim.lim_timers.gLimFTPreAuthRspTimer)) { + pe_err("FT Auth Rsp Timer Start Failed"); + goto preauth_fail; + } + MTRACE(mac_trace(mac, TRACE_CODE_TIMER_ACTIVATE, + pe_session->peSessionId, eLIM_FT_PREAUTH_RSP_TIMER)); + + pe_debug("FT Auth Rsp Timer Started"); +#ifdef FEATURE_WLAN_DIAG_SUPPORT + lim_diag_event_report(mac, WLAN_PE_DIAG_ROAM_AUTH_START_EVENT, + mac->lim.pe_session, QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); +#endif + if (pe_session->ftPEContext.pFTPreAuthReq) + lim_send_auth_mgmt_frame(mac, &authFrame, + pe_session->ftPEContext.pFTPreAuthReq->preAuthbssId, + LIM_NO_WEP_IN_FC, pe_session); + + return; + +preauth_fail: + lim_handle_ft_pre_auth_rsp(mac, QDF_STATUS_E_FAILURE, NULL, 0, pe_session); + return; +} + +/** + * lim_ft_setup_auth_session() - Fill the FT Session + * @mac: Global MAC Context + * @pe_session: PE Session + * + * Setup the session and the add bss req for the pre-auth AP. + * + * Return: Success or Failure Status + */ +QDF_STATUS lim_ft_setup_auth_session(struct mac_context *mac, + struct pe_session *pe_session) +{ + struct pe_session *ft_session = NULL; + uint8_t sessionId = 0; + struct sSirFTPreAuthReq *req; + + ft_session = + pe_find_session_by_bssid(mac, pe_session->limReAssocbssId, + &sessionId); + if (!ft_session) { + pe_err("No session found for bssid"); + lim_print_mac_addr(mac, pe_session->limReAssocbssId, LOGE); + return QDF_STATUS_E_FAILURE; + } + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("pe_session is not in STA mode"); + return QDF_STATUS_E_FAILURE; + } + + req = pe_session->ftPEContext.pFTPreAuthReq; + if (req && req->pbssDescription) { + lim_fill_ft_session(mac, + req->pbssDescription, ft_session, + pe_session, WLAN_PHYMODE_AUTO); + lim_ft_prepare_add_bss_req(mac, ft_session, + req->pbssDescription); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_ft_process_pre_auth_result() - Process the Auth frame + * @mac: Global MAC context + * @pe_session: PE Session + * + * Return: None + */ +static void lim_ft_process_pre_auth_result(struct mac_context *mac, + struct pe_session *pe_session) +{ + if (!pe_session || + !pe_session->ftPEContext.pFTPreAuthReq) + return; + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("pe_session is not in STA mode"); + return; + } + + if (pe_session->ftPEContext.ftPreAuthStatus == QDF_STATUS_SUCCESS) { + pe_session->ftPEContext.ftPreAuthStatus = + lim_ft_setup_auth_session(mac, pe_session); + } + /* Post the FT Pre Auth Response to SME */ + lim_post_ft_pre_auth_rsp(mac, + pe_session->ftPEContext.ftPreAuthStatus, + pe_session->ftPEContext.saved_auth_rsp, + pe_session->ftPEContext.saved_auth_rsp_length, + pe_session); +} + +/** + * lim_handle_ft_pre_auth_rsp() - Handle the Auth response + * @mac: Global MAC Context + * @status: Status Code + * @auth_rsp: Auth Response + * @auth_rsp_length: Auth response length + * @pe_session: PE Session + * + * Send the FT Pre Auth Response to SME whenever we have a status + * ready to be sent to SME + * + * SME will be the one to send it up to the supplicant to receive + * FTIEs which will be required for Reassoc Req. + * + * @Return: None + */ +void lim_handle_ft_pre_auth_rsp(struct mac_context *mac, QDF_STATUS status, + uint8_t *auth_rsp, uint16_t auth_rsp_length, + struct pe_session *pe_session) +{ + struct pe_session *ft_session = NULL; + uint8_t sessionId = 0; + struct bss_description *pbssDescription = NULL; +#ifdef FEATURE_WLAN_DIAG_SUPPORT + lim_diag_event_report(mac, WLAN_PE_DIAG_PRE_AUTH_RSP_EVENT, + pe_session, (uint16_t) status, 0); +#endif + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("pe_session is not in STA mode"); + return; + } + + /* Save the status of pre-auth */ + pe_session->ftPEContext.ftPreAuthStatus = status; + + /* Save the auth rsp, so we can send it to + * SME once we resume link + */ + pe_session->ftPEContext.saved_auth_rsp_length = 0; + if ((auth_rsp) && (auth_rsp_length < MAX_FTIE_SIZE)) { + qdf_mem_copy(pe_session->ftPEContext.saved_auth_rsp, + auth_rsp, auth_rsp_length); + pe_session->ftPEContext.saved_auth_rsp_length = + auth_rsp_length; + } + + if (!pe_session->ftPEContext.pFTPreAuthReq || + !pe_session->ftPEContext.pFTPreAuthReq->pbssDescription) { + pe_err("pFTPreAuthReq or pbssDescription is NULL"); + return; + } + + /* Create FT session for the re-association at this point */ + if (pe_session->ftPEContext.ftPreAuthStatus == QDF_STATUS_SUCCESS) { + pbssDescription = + pe_session->ftPEContext.pFTPreAuthReq->pbssDescription; + ft_session = + pe_create_session(mac, pbssDescription->bssId, + &sessionId, mac->lim.maxStation, + pe_session->bssType, + pe_session->vdev_id, + pe_session->opmode); + if (!ft_session) { + pe_err("Session not created for pre-auth 11R AP"); + status = QDF_STATUS_E_FAILURE; + pe_session->ftPEContext.ftPreAuthStatus = status; + goto send_rsp; + } + + sir_copy_mac_addr(ft_session->self_mac_addr, + pe_session->self_mac_addr); + sir_copy_mac_addr(ft_session->limReAssocbssId, + pbssDescription->bssId); + + /* Update the beacon/probe filter in mac_ctx */ + lim_set_bcn_probe_filter(mac, + ft_session, + NULL, 0); + + if (ft_session->bssType == eSIR_INFRASTRUCTURE_MODE) + ft_session->limSystemRole = eLIM_STA_ROLE; + else + pe_err("Invalid bss type"); + + ft_session->limPrevSmeState = ft_session->limSmeState; + ft_session->ht_config = pe_session->ht_config; + ft_session->limSmeState = eLIM_SME_WT_REASSOC_STATE; + + if (wlan_reg_is_24ghz_ch_freq(pe_session->ftPEContext. + pFTPreAuthReq->pre_auth_channel_freq)) + ft_session->vdev_nss = mac->vdev_type_nss_2g.sta; + else + ft_session->vdev_nss = mac->vdev_type_nss_5g.sta; + + pe_debug("created session (%pK) with id = %d", + ft_session, ft_session->peSessionId); + + /* Update the ReAssoc BSSID of the current session */ + sir_copy_mac_addr(pe_session->limReAssocbssId, + pbssDescription->bssId); + lim_print_mac_addr(mac, pe_session->limReAssocbssId, LOGD); + } +send_rsp: + if ((pe_session->curr_op_freq != + pe_session->ftPEContext.pFTPreAuthReq->pre_auth_channel_freq) || + lim_is_in_mcc(mac)) { + /* Need to move to the original AP channel */ + lim_process_abort_scan_ind(mac, pe_session->smeSessionId, + pe_session->ftPEContext.pFTPreAuthReq->scan_id, + mac->lim.req_id | PREAUTH_REQUESTOR_ID); + } else { + pe_debug("Pre auth on same freq as connected AP freq %d and no mcc pe sessions exist", + pe_session->ftPEContext.pFTPreAuthReq-> + pre_auth_channel_freq); + lim_ft_process_pre_auth_result(mac, pe_session); + } +} + +/* + * lim_process_ft_preauth_rsp_timeout() - process ft preauth rsp timeout + * + * @mac_ctx: global mac ctx + * + * This function is called if preauth response is not received from the AP + * within this timeout while FT in progress + * + * Return: void + */ +void lim_process_ft_preauth_rsp_timeout(struct mac_context *mac_ctx) +{ + struct pe_session *session; + + /* + * We have failed pre auth. We need to resume link and get back on + * home channel + */ + pe_err("FT Pre-Auth Time Out!!!!"); + session = pe_find_session_by_session_id(mac_ctx, + mac_ctx->lim.lim_timers.gLimFTPreAuthRspTimer.sessionId); + if (!session) { + pe_err("Session Does not exist for given sessionID"); + return; + } + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(session)) { + pe_err("session is not in STA mode"); + return; + } + + /* Reset the flag to indicate preauth request session */ + session->ftPEContext.ftPreAuthSession = false; + + if (!session->ftPEContext.pFTPreAuthReq) { + /* Auth Rsp might already be posted to SME and ftcleanup done */ + pe_err("pFTPreAuthReq is NULL sessionId: %d", + mac_ctx->lim.lim_timers.gLimFTPreAuthRspTimer.sessionId); + return; + } + + /* + * To handle the race condition where we receive preauth rsp after + * timer has expired. + */ + if (true == + session->ftPEContext.pFTPreAuthReq->bPreAuthRspProcessed) { + pe_err("Auth rsp already posted to SME (session %pK)", + session); + return; + } else { + /* + * Here we are sending preauth rsp with failure state + * and which is forwarded to SME. Now, if we receive an preauth + * resp from AP with success it would create a FT pesession, but + * will be dropped in SME leaving behind the pesession. Mark + * Preauth rsp processed so that any rsp from AP is dropped in + * lim_process_auth_frame_no_session. + */ + pe_debug("Auth rsp not yet posted to SME (session %pK)", + session); + session->ftPEContext.pFTPreAuthReq->bPreAuthRspProcessed = true; + } + + /* + * Attempted at Pre-Auth and failed. If we are off channel. We need + * to get back to home channel + */ + lim_handle_ft_pre_auth_rsp(mac_ctx, QDF_STATUS_E_FAILURE, NULL, 0, session); +} + +/* + * lim_post_ft_pre_auth_rsp() - post ft pre auth response to SME. + * + * @mac_ctx: global mac ctx + * @status: status code to post in auth rsp + * @auth_rsp: pointer to auth rsp FT ie + * @auth_rsp_length: len of the IE field + * @session: pe session + * + * post pre auth response to SME. + * + * Return: void + */ +void lim_post_ft_pre_auth_rsp(struct mac_context *mac_ctx, + QDF_STATUS status, + uint8_t *auth_rsp, + uint16_t auth_rsp_length, + struct pe_session *session) +{ + tpSirFTPreAuthRsp ft_pre_auth_rsp; + struct scheduler_msg mmh_msg = {0}; + uint16_t rsp_len = sizeof(tSirFTPreAuthRsp); + + ft_pre_auth_rsp = qdf_mem_malloc(rsp_len); + if (!ft_pre_auth_rsp) { + QDF_ASSERT(ft_pre_auth_rsp); + return; + } + + pe_debug("Auth Rsp = %pK", ft_pre_auth_rsp); + if (session) { + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(session)) { + pe_err("session is not in STA mode"); + qdf_mem_free(ft_pre_auth_rsp); + return; + } + ft_pre_auth_rsp->vdev_id = session->vdev_id; + /* The bssid of the AP we are sending Auth1 to. */ + if (session->ftPEContext.pFTPreAuthReq) + sir_copy_mac_addr(ft_pre_auth_rsp->preAuthbssId, + session->ftPEContext.pFTPreAuthReq->preAuthbssId); + } + + ft_pre_auth_rsp->messageType = eWNI_SME_FT_PRE_AUTH_RSP; + ft_pre_auth_rsp->length = (uint16_t) rsp_len; + ft_pre_auth_rsp->status = status; + + /* Attach the auth response now back to SME */ + ft_pre_auth_rsp->ft_ies_length = 0; + if ((auth_rsp) && (auth_rsp_length < MAX_FTIE_SIZE)) { + /* Only 11r assoc has FT IEs */ + qdf_mem_copy(ft_pre_auth_rsp->ft_ies, + auth_rsp, auth_rsp_length); + ft_pre_auth_rsp->ft_ies_length = auth_rsp_length; + } + + if (status != QDF_STATUS_SUCCESS) { + /* + * Ensure that on Pre-Auth failure the cached Pre-Auth Req and + * other allocated memory is freed up before returning. + */ + pe_debug("Pre-Auth Failed, Cleanup!"); + lim_ft_cleanup(mac_ctx, session); + } + + mmh_msg.type = ft_pre_auth_rsp->messageType; + mmh_msg.bodyptr = ft_pre_auth_rsp; + mmh_msg.bodyval = 0; + + pe_debug("Posted Auth Rsp to SME with status of 0x%x", status); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + if (status == QDF_STATUS_SUCCESS) + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_PREAUTH_DONE, + session, status, 0); +#endif + lim_sys_process_mmh_msg_api(mac_ctx, &mmh_msg); +} + +/** + * lim_send_preauth_scan_offload() - Send scan command to handle preauth. + * + * @mac_ctx: Pointer to Global MAC structure + * @session_entry: pe session + * @ft_preauth_req: Preauth request with parameters + * + * Builds a single channel scan request and sends it to scan module. + * Scan dwell time is the time allocated to go to preauth candidate + * channel for auth frame exchange. + * + * Return: Status of sending message to scan module. + */ +QDF_STATUS lim_send_preauth_scan_offload(struct mac_context *mac_ctx, + struct pe_session *session_entry, + tSirFTPreAuthReq *ft_preauth_req) +{ + struct scan_start_request *req; + struct wlan_objmgr_vdev *vdev; + uint8_t session_id; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!session_entry) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + FL("Session entry is NULL")); + return QDF_STATUS_E_FAILURE; + } + + session_id = session_entry->smeSessionId; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return QDF_STATUS_E_NOMEM; + + qdf_mem_zero(req, sizeof(*req)); + + vdev = wlan_objmgr_get_vdev_by_id_from_pdev(mac_ctx->pdev, + session_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + FL("vdev object is NULL")); + qdf_mem_free(req); + return QDF_STATUS_E_FAILURE; + } + + ucfg_scan_init_default_params(vdev, req); + + qdf_mem_copy(req->scan_req.bssid_list, + (uint8_t *)ft_preauth_req->currbssId, + QDF_MAC_ADDR_SIZE); + + req->scan_req.scan_id = ucfg_scan_get_scan_id(mac_ctx->psoc); + if (!req->scan_req.scan_id) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + qdf_mem_free(req); + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + FL("Invalid scan ID")); + return QDF_STATUS_E_FAILURE; + } + ft_preauth_req->scan_id = req->scan_req.scan_id; + req->scan_req.vdev_id = session_id; + req->scan_req.scan_req_id = mac_ctx->lim.req_id | PREAUTH_REQUESTOR_ID; + req->scan_req.scan_priority = SCAN_PRIORITY_VERY_HIGH; + req->scan_req.scan_f_passive = true; + + req->scan_req.chan_list.num_chan = 1; + req->scan_req.chan_list.chan[0].freq = + ft_preauth_req->pre_auth_channel_freq; + + req->scan_req.dwell_time_active = LIM_FT_PREAUTH_SCAN_TIME; + req->scan_req.dwell_time_passive = LIM_FT_PREAUTH_SCAN_TIME; + + status = ucfg_scan_start(req); + if (status != QDF_STATUS_SUCCESS) + /* Don't free req here, ucfg_scan_start will do free */ + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_INFO_HIGH, + FL("Issue scan req failed")); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + return status; +} + +void lim_preauth_scan_event_handler(struct mac_context *mac_ctx, + enum sir_scan_event_type event, + uint8_t vdev_id, uint32_t scan_id) +{ + struct pe_session *session_entry; + + session_entry = pe_find_session_by_scan_id(mac_ctx, scan_id); + /* Pre-auth request is sent */ + if (session_entry) { + if ((event == SIR_SCAN_EVENT_FOREIGN_CHANNEL) && + (session_entry->ftPEContext.ftPreAuthStatus + == QDF_STATUS_SUCCESS)) { + pe_err("Pre-auth is done, skip sending pre-auth req"); + return; + } + } else { + /* For the first pre-auth request + * need to get it by sme session id (vdev id) + */ + session_entry = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + } + + if (!session_entry) { + pe_err("vdev_id :%d PeSessionId:%d does not exist", vdev_id, + mac_ctx->lim.lim_timers.gLimFTPreAuthRspTimer.sessionId); + return; + } + + switch (event) { + case SIR_SCAN_EVENT_START_FAILED: + /* Scan command is rejected by firmware */ + pe_err("Failed to start preauth scan"); + lim_post_ft_pre_auth_rsp(mac_ctx, QDF_STATUS_E_FAILURE, NULL, 0, + session_entry); + return; + + case SIR_SCAN_EVENT_COMPLETED: + /* + * Scan either completed successfully or or got terminated + * after successful auth, or timed out. Either way, STA + * is back to home channel. Data traffic can continue. + */ + lim_ft_process_pre_auth_result(mac_ctx, session_entry); + break; + + case SIR_SCAN_EVENT_FOREIGN_CHANNEL: + /* Sta is on candidate channel. Send auth */ + lim_perform_ft_pre_auth(mac_ctx, QDF_STATUS_SUCCESS, NULL, + session_entry); + break; + default: + /* Don't print message for scan events that are ignored */ + break; + } +} + diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ibss_peer_mgmt.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ibss_peer_mgmt.c new file mode 100644 index 0000000000000000000000000000000000000000..46bb6ca3fc0ddb08a495d25c86992c8515ac96b2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ibss_peer_mgmt.c @@ -0,0 +1,1683 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cds_api.h" +#include "ani_global.h" +#include "sir_common.h" +#include "wni_cfg.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "sch_api.h" /* sch_set_fixed_beacon_fields for IBSS coalesce */ +#include "lim_security_utils.h" +#include "lim_send_messages.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "lim_ibss_peer_mgmt.h" +#include "lim_types.h" +#include "wlan_mlme_api.h" +#include "cfg_ucfg_api.h" + +/** + * ibss_peer_find + * + ***FUNCTION: + * This function is called while adding a context at + * DPH & Polaris for a peer in IBSS. + * If peer is found in the list, capabilities from the + * returned BSS description are used at DPH node & Polaris. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param macAddr - MAC address of the peer + * + * @return Pointer to peer node if found, else NULL + */ + +static tLimIbssPeerNode *ibss_peer_find(struct mac_context *mac, + tSirMacAddr macAddr) +{ + tLimIbssPeerNode *pTempNode = mac->lim.gLimIbssPeerList; + + while (pTempNode) { + if (!qdf_mem_cmp((uint8_t *) macAddr, + (uint8_t *) &pTempNode->peerMacAddr, + sizeof(tSirMacAddr))) + break; + pTempNode = pTempNode->next; + } + return pTempNode; +} /*** end ibss_peer_find() ***/ + +/** + * ibss_peer_add + * + ***FUNCTION: + * This is called on a STA in IBSS upon receiving Beacon/ + * Probe Response from a peer. + * + ***LOGIC: + * Node is always added to the front of the list + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param pPeerNode - Pointer to peer node to be added to the list. + * + * @return None + */ + +static QDF_STATUS +ibss_peer_add(struct mac_context *mac, tLimIbssPeerNode *pPeerNode) +{ +#ifdef ANI_SIR_IBSS_PEER_CACHING + uint32_t numIbssPeers = (2 * mac->lim.maxStation); + + if (mac->lim.gLimNumIbssPeers >= numIbssPeers) { + /** + * Reached max number of peers to be maintained. + * Delete last entry & add new entry at the beginning. + */ + tLimIbssPeerNode *pTemp, *pPrev; + + pTemp = pPrev = mac->lim.gLimIbssPeerList; + while (pTemp->next) { + pPrev = pTemp; + pTemp = pTemp->next; + } + if (pTemp->beacon) { + qdf_mem_free(pTemp->beacon); + } + + qdf_mem_free(pTemp); + pPrev->next = NULL; + } else +#endif + mac->lim.gLimNumIbssPeers++; + + pPeerNode->next = mac->lim.gLimIbssPeerList; + mac->lim.gLimIbssPeerList = pPeerNode; + + return QDF_STATUS_SUCCESS; + +} /*** end limAddIbssPeerToList() ***/ + +/** + * ibss_peer_collect + * + ***FUNCTION: + * This is called to collect IBSS peer information + * from received Beacon/Probe Response frame from it. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param pBeacon - Parsed Beacon Frame structure + * @param pBD - Pointer to received BD + * @param peer - Pointer to IBSS peer node + * + * @return None + */ + +static void +ibss_peer_collect(struct mac_context *mac, + tpSchBeaconStruct pBeacon, + tpSirMacMgmtHdr pHdr, + tLimIbssPeerNode *peer, struct pe_session *pe_session) +{ + qdf_mem_copy(peer->peerMacAddr, pHdr->sa, sizeof(tSirMacAddr)); + + peer->capabilityInfo = pBeacon->capabilityInfo; + peer->extendedRatesPresent = pBeacon->extendedRatesPresent; + peer->edcaPresent = pBeacon->edcaPresent; + peer->wmeEdcaPresent = pBeacon->wmeEdcaPresent; + peer->wmeInfoPresent = pBeacon->wmeInfoPresent; + + if (pBeacon->IBSSParams.present) { + peer->atimIePresent = pBeacon->IBSSParams.present; + peer->peerAtimWindowLength = pBeacon->IBSSParams.atim; + } + + if (IS_DOT11_MODE_HT(pe_session->dot11mode) && + (pBeacon->HTCaps.present)) { + peer->htCapable = pBeacon->HTCaps.present; + qdf_mem_copy((uint8_t *) peer->supportedMCSSet, + (uint8_t *) pBeacon->HTCaps.supportedMCSSet, + sizeof(peer->supportedMCSSet)); + peer->htGreenfield = (uint8_t) pBeacon->HTCaps.greenField; + peer->htSupportedChannelWidthSet = + (uint8_t) pBeacon->HTCaps.supportedChannelWidthSet; + peer->htMIMOPSState = + (tSirMacHTMIMOPowerSaveState) pBeacon->HTCaps.mimoPowerSave; + peer->htMaxAmsduLength = + (uint8_t) pBeacon->HTCaps.maximalAMSDUsize; + peer->htAMpduDensity = pBeacon->HTCaps.mpduDensity; + peer->htDsssCckRate40MHzSupport = + (uint8_t) pBeacon->HTCaps.dsssCckMode40MHz; + peer->htShortGI20Mhz = (uint8_t) pBeacon->HTCaps.shortGI20MHz; + peer->htShortGI40Mhz = (uint8_t) pBeacon->HTCaps.shortGI40MHz; + peer->htMaxRxAMpduFactor = pBeacon->HTCaps.maxRxAMPDUFactor; + peer->htSecondaryChannelOffset = + pBeacon->HTInfo.secondaryChannelOffset; + peer->htLdpcCapable = (uint8_t) pBeacon->HTCaps.advCodingCap; + } + + /* Collect peer VHT capabilities based on the received beacon from the peer */ + if (pBeacon->VHTCaps.present) { + peer->vhtSupportedChannelWidthSet = + pBeacon->VHTOperation.chanWidth; + peer->vhtCapable = pBeacon->VHTCaps.present; + + /* Collect VHT capabilities from beacon */ + qdf_mem_copy((uint8_t *) &peer->VHTCaps, + (uint8_t *) &pBeacon->VHTCaps, + sizeof(tDot11fIEVHTCaps)); + } + peer->erpIePresent = pBeacon->erpPresent; + + qdf_mem_copy((uint8_t *) &peer->supportedRates, + (uint8_t *) &pBeacon->supportedRates, + pBeacon->supportedRates.numRates + 1); + if (peer->extendedRatesPresent) + qdf_mem_copy((uint8_t *) &peer->extendedRates, + (uint8_t *) &pBeacon->extendedRates, + pBeacon->extendedRates.numRates + 1); + else + peer->extendedRates.numRates = 0; + + peer->next = NULL; +} /*** end ibss_peer_collect() ***/ + +/* handle change in peer qos/wme capabilities */ +static void +ibss_sta_caps_update(struct mac_context *mac, + tLimIbssPeerNode *pPeerNode, struct pe_session *pe_session) +{ + uint16_t peerIdx; + tpDphHashNode sta; + + pPeerNode->beaconHBCount++; /* Update beacon count. */ + + /* if the peer node exists, update its qos capabilities */ + sta = dph_lookup_hash_entry(mac, pPeerNode->peerMacAddr, &peerIdx, + &pe_session->dph.dphHashTable); + if (!sta) + return; + + /* Update HT Capabilities */ + if (IS_DOT11_MODE_HT(pe_session->dot11mode)) { + sta->mlmStaContext.htCapability = pPeerNode->htCapable; + if (pPeerNode->htCapable) { + sta->htGreenfield = pPeerNode->htGreenfield; + sta->htSupportedChannelWidthSet = + pPeerNode->htSupportedChannelWidthSet; + sta->htSecondaryChannelOffset = + pPeerNode->htSecondaryChannelOffset; + sta->htMIMOPSState = pPeerNode->htMIMOPSState; + sta->htMaxAmsduLength = pPeerNode->htMaxAmsduLength; + sta->htAMpduDensity = pPeerNode->htAMpduDensity; + sta->htDsssCckRate40MHzSupport = + pPeerNode->htDsssCckRate40MHzSupport; + sta->htShortGI20Mhz = pPeerNode->htShortGI20Mhz; + sta->htShortGI40Mhz = pPeerNode->htShortGI40Mhz; + sta->htMaxRxAMpduFactor = + pPeerNode->htMaxRxAMpduFactor; + sta->htLdpcCapable = pPeerNode->htLdpcCapable; + } + } + if (IS_DOT11_MODE_VHT(pe_session->dot11mode)) { + sta->mlmStaContext.vhtCapability = pPeerNode->vhtCapable; + if (pPeerNode->vhtCapable) { + sta->vhtSupportedChannelWidthSet = + pPeerNode->vhtSupportedChannelWidthSet; + + /* If in 11AC mode and if session requires 11AC mode, consider peer's */ + /* max AMPDU length factor */ + sta->htMaxRxAMpduFactor = + pPeerNode->VHTCaps.maxAMPDULenExp; + sta->vhtLdpcCapable = + (uint8_t) pPeerNode->VHTCaps.ldpcCodingCap; + } + } + /* peer is 11e capable but is not 11e enabled yet */ + /* some STA's when joining Airgo IBSS, assert qos capability even when */ + /* they don't support qos. however, they do not include the edca parameter */ + /* set. so let's check for edcaParam in addition to the qos capability */ + if (pPeerNode->capabilityInfo.qos && (pe_session->limQosEnabled) + && pPeerNode->edcaPresent) { + sta->qosMode = 1; + sta->wmeEnabled = 0; + if (!sta->lleEnabled) { + sta->lleEnabled = 1; + /* dphSetACM(mac, sta); */ + } + return; + } + /* peer is not 11e capable now but was 11e enabled earlier */ + else if (sta->lleEnabled) { + sta->qosMode = 0; + sta->lleEnabled = 0; + } + /* peer is wme capable but is not wme enabled yet */ + if (pPeerNode->wmeInfoPresent && pe_session->limWmeEnabled) { + sta->qosMode = 1; + sta->lleEnabled = 0; + if (!sta->wmeEnabled) { + sta->wmeEnabled = 1; + } + return; + } + /* When the peer device supports EDCA parameters, then we were not + considering. Added this code when we saw that one of the Peer Device + was advertising WMM param where we were not honouring that. CR# 210756 + */ + if (pPeerNode->wmeEdcaPresent && pe_session->limWmeEnabled) { + sta->qosMode = 1; + sta->lleEnabled = 0; + if (!sta->wmeEnabled) { + sta->wmeEnabled = 1; + } + return; + } + /* peer is not wme capable now but was wme enabled earlier */ + else if (sta->wmeEnabled) { + sta->qosMode = 0; + sta->wmeEnabled = 0; + } + +} + +static void +ibss_sta_rates_update(struct mac_context *mac, + tpDphHashNode sta, + tLimIbssPeerNode *peer, struct pe_session *pe_session) +{ + lim_populate_matching_rate_set(mac, sta, &peer->supportedRates, + &peer->extendedRates, + peer->supportedMCSSet, pe_session, + &peer->VHTCaps, NULL); + sta->mlmStaContext.capabilityInfo = peer->capabilityInfo; +} /*** end ibss_sta_info_update() ***/ + +/** + * ibss_sta_info_update + * + ***FUNCTION: + * This is called to program both SW & Polaris context + * for peer in IBSS. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param sta - Pointer to DPH node + * @param peer - Pointer to IBSS peer node + * + * @return None + */ + +static void +ibss_sta_info_update(struct mac_context *mac, + tpDphHashNode sta, + tLimIbssPeerNode *peer, struct pe_session *pe_session) +{ + sta->staType = STA_ENTRY_PEER; + ibss_sta_caps_update(mac, peer, pe_session); + ibss_sta_rates_update(mac, sta, peer, pe_session); +} /*** end ibss_sta_info_update() ***/ + +static void ibss_coalesce_free(struct mac_context *mac) +{ + qdf_mem_free(mac->lim.ibss_info.mac_hdr); + mac->lim.ibss_info.mac_hdr = NULL; + qdf_mem_free(mac->lim.ibss_info.beacon); + mac->lim.ibss_info.beacon = NULL; +} + +/* + * save the beacon params for use when adding the bss + */ +static void +ibss_coalesce_save(struct mac_context *mac, + tpSirMacMgmtHdr pHdr, tpSchBeaconStruct pBeacon) +{ + /* get rid of any saved info */ + ibss_coalesce_free(mac); + + mac->lim.ibss_info.mac_hdr = qdf_mem_malloc(sizeof(*pHdr)); + if (!mac->lim.ibss_info.mac_hdr) + return; + + mac->lim.ibss_info.beacon = qdf_mem_malloc(sizeof(*pBeacon)); + if (!mac->lim.ibss_info.beacon) { + ibss_coalesce_free(mac); + return; + } + + qdf_mem_copy(mac->lim.ibss_info.mac_hdr, pHdr, sizeof(*pHdr)); + qdf_mem_copy(mac->lim.ibss_info.beacon, pBeacon, sizeof(*pBeacon)); +} + +static QDF_STATUS lim_ibss_add_bss( + struct mac_context *mac, + struct pe_session *session, + tLimMlmStartReq mlmStartReq) +{ + return wlan_vdev_mlme_sm_deliver_evt( + session->vdev, + WLAN_VDEV_SM_EV_START, + sizeof(mlmStartReq), &mlmStartReq); +} + +void lim_ibss_delete(struct mac_context *mac, struct pe_session *session) +{ + ibss_coalesce_free(mac); +} + +/* + * tries to add a new entry to dph hash node + * if necessary, an existing entry is eliminated + */ +static QDF_STATUS +ibss_dph_entry_add(struct mac_context *mac, + tSirMacAddr peerAddr, + tpDphHashNode *ppSta, struct pe_session *pe_session) +{ + uint16_t peerIdx; + tpDphHashNode sta; + + *ppSta = NULL; + + sta = + dph_lookup_hash_entry(mac, peerAddr, &peerIdx, + &pe_session->dph.dphHashTable); + if (sta) { + /* Trying to add context for already existing STA in IBSS */ + pe_err("STA exists already"); + lim_print_mac_addr(mac, peerAddr, LOGE); + return QDF_STATUS_E_FAILURE; + } + + /** + * Assign an AID, delete context existing with that + * AID and then add an entry to hash table maintained + * by DPH module. + */ + peerIdx = lim_assign_peer_idx(mac, pe_session); + + sta = + dph_get_hash_entry(mac, peerIdx, &pe_session->dph.dphHashTable); + if (sta) { + (void)lim_del_sta(mac, sta, false /*asynchronous */, + pe_session); + lim_delete_dph_hash_entry(mac, sta->staAddr, peerIdx, + pe_session); + } + + sta = + dph_add_hash_entry(mac, peerAddr, peerIdx, + &pe_session->dph.dphHashTable); + if (!sta) { + /* Could not add hash table entry */ + pe_err("could not add hash entry at DPH for peerIdx/aid: %d MACaddr:", + peerIdx); + lim_print_mac_addr(mac, peerAddr, LOGE); + return QDF_STATUS_E_FAILURE; + } + + *ppSta = sta; + return QDF_STATUS_SUCCESS; +} + +/* send a status change notification */ +static void +ibss_status_chg_notify(struct mac_context *mac, tSirMacAddr peerAddr, + uint16_t status, uint8_t sessionId) +{ + + tLimIbssPeerNode *peerNode; + uint8_t *beacon = NULL; + uint16_t bcnLen = 0; + + peerNode = ibss_peer_find(mac, peerAddr); + if (peerNode) { + if (!peerNode->beacon) + peerNode->beaconLen = 0; + beacon = peerNode->beacon; + bcnLen = peerNode->beaconLen; + peerNode->beacon = NULL; + peerNode->beaconLen = 0; + } + + lim_send_sme_ibss_peer_ind(mac, peerAddr, + beacon, bcnLen, status, sessionId); + + if (beacon) { + qdf_mem_free(beacon); + } +} + +void ibss_bss_add(struct mac_context *mac, struct pe_session *pe_session) +{ + tLimMlmStartReq mlmStartReq; + uint32_t cfg; + tpSirMacMgmtHdr pHdr = mac->lim.ibss_info.mac_hdr; + tpSchBeaconStruct pBeacon = mac->lim.ibss_info.beacon; + qdf_size_t num_ext_rates = 0; + QDF_STATUS status; + + if ((!pHdr) || (!pBeacon)) { + pe_err("Unable to add BSS (no cached BSS info)"); + return; + } + + qdf_mem_copy(pe_session->bssId, pHdr->bssId, sizeof(tSirMacAddr)); + + sir_copy_mac_addr(pHdr->bssId, pe_session->bssId); + + /* Copy beacon interval from sessionTable */ + cfg = pe_session->beaconParams.beaconInterval; + if (cfg != pBeacon->beaconInterval) + pe_session->beaconParams.beaconInterval = + pBeacon->beaconInterval; + + /* This function ibss_bss_add (and hence the below code) is only called during ibss coalescing. We need to + * adapt to peer's capability with respect to short slot time. Changes have been made to lim_apply_configuration() + * so that the IBSS doesn't blindly start with short slot = 1. If IBSS start is part of coalescing then it will adapt + * to peer's short slot using code below. + */ + /* If cfg is already set to current peer's capability then no need to set it again */ + if (pe_session->shortSlotTimeSupported != + pBeacon->capabilityInfo.shortSlotTime) { + pe_session->shortSlotTimeSupported = + pBeacon->capabilityInfo.shortSlotTime; + } + qdf_mem_copy((uint8_t *) &pe_session->pLimStartBssReq-> + operationalRateSet, (uint8_t *) &pBeacon->supportedRates, + pBeacon->supportedRates.numRates); + + /** + * WNI_CFG_EXTENDED_OPERATIONAL_RATE_SET CFG needs to be reset, when + * there is no extended rate IE present in beacon. This is especially important when + * supportedRateSet IE contains all the extended rates as well and STA decides to coalesce. + * In this IBSS coalescing scenario LIM will tear down the BSS and Add a new one. So LIM needs to + * reset this CFG, just in case CSR originally had set this CFG when IBSS was started from the local profile. + * If IBSS was started by CSR from the BssDescription, then it would reset this CFG before StartBss is issued. + * The idea is that the count of OpRateSet and ExtendedOpRateSet rates should not be more than 12. + */ + + if (pBeacon->extendedRatesPresent) + num_ext_rates = pBeacon->extendedRates.numRates; + if (wlan_mlme_set_cfg_str( + (uint8_t *)&pBeacon->extendedRates.rate, + &mac->mlme_cfg->rates.ext_opr_rate_set, + num_ext_rates) != QDF_STATUS_SUCCESS) { + pe_err("could not update ExtendedOperRateset at CFG"); + return; + } + + /* + * Each IBSS node will advertise its own HT Capabilities instead of adapting to the Peer's capabilities + * If we don't do this then IBSS may not go back to full capabilities when the STA with lower capabilities + * leaves the IBSS. e.g. when non-CB STA joins an IBSS and then leaves, the IBSS will be stuck at non-CB mode + * even though all the nodes are capable of doing CB. + * so it is decided to leave the self HT capabilties intact. This may change if some issues are found in interop. + */ + qdf_mem_zero((void *)&mlmStartReq, sizeof(mlmStartReq)); + + qdf_mem_copy(mlmStartReq.bssId, pHdr->bssId, sizeof(tSirMacAddr)); + mlmStartReq.rateSet.numRates = + pe_session->pLimStartBssReq->operationalRateSet.numRates; + qdf_mem_copy(&mlmStartReq.rateSet.rate[0], + &pe_session->pLimStartBssReq->operationalRateSet. + rate[0], mlmStartReq.rateSet.numRates); + mlmStartReq.bssType = eSIR_IBSS_MODE; + mlmStartReq.beaconPeriod = pBeacon->beaconInterval; + mlmStartReq.nwType = pe_session->pLimStartBssReq->nwType; /* pe_session->nwType is also OK???? */ + mlmStartReq.htCapable = pe_session->htCapability; + mlmStartReq.htOperMode = mac->lim.gHTOperMode; + mlmStartReq.dualCTSProtection = mac->lim.gHTDualCTSProtection; + mlmStartReq.txChannelWidthSet = pe_session->htRecommendedTxWidthSet; + + /* reading the channel num from session Table */ + mlmStartReq.oper_ch_freq = pe_session->curr_op_freq; + + mlmStartReq.cbMode = pe_session->pLimStartBssReq->cbMode; + + /* Copy the SSID for RxP filtering based on SSID. */ + qdf_mem_copy((uint8_t *) &mlmStartReq.ssId, + (uint8_t *) &pe_session->pLimStartBssReq->ssId, + pe_session->pLimStartBssReq->ssId.length + 1); + + pe_debug("invoking ADD_BSS as part of coalescing!"); + + status = lim_ibss_add_bss(mac, pe_session, mlmStartReq); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("AddBss failure"); + return; + } + /* Update fields in Beacon */ + if (sch_set_fixed_beacon_fields(mac, pe_session) != QDF_STATUS_SUCCESS) { + pe_err("Unable to set fixed Beacon fields"); + return; + } + +} + +void ibss_bss_delete(struct mac_context *mac_ctx, struct pe_session *session) +{ + QDF_STATUS status; + + pe_debug("Initiating IBSS Delete BSS"); + if (session->limMlmState != eLIM_MLM_BSS_STARTED_STATE) { + pe_warn("Incorrect LIM MLM state for delBss: %d", + session->limMlmState); + return; + } + status = lim_del_bss(mac_ctx, NULL, session->vdev_id, session); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("delBss failed for bss: %d", session->vdev_id); +} + +/** + * lim_ibss_init + * + ***FUNCTION: + * This function is called while starting an IBSS + * to initialize list used to maintain IBSS peers. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @return None + */ + +void lim_ibss_init(struct mac_context *mac) +{ + mac->lim.gLimIbssCoalescingHappened = 0; + mac->lim.gLimIbssPeerList = NULL; + mac->lim.gLimNumIbssPeers = 0; + + /* ibss info - params for which ibss to join while coalescing */ + qdf_mem_zero(&mac->lim.ibss_info, sizeof(mac->lim.ibss_info)); +} /*** end lim_ibss_init() ***/ + +/** + * lim_ibss_delete_all_peers + * + ***FUNCTION: + * This function is called to delete all peers. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @return None + */ +void lim_ibss_delete_all_peers(struct mac_context *mac, + struct pe_session *pe_session) +{ + tLimIbssPeerNode *pCurrNode, *pTempNode; + tpDphHashNode sta; + uint16_t peerIdx; + + pCurrNode = pTempNode = mac->lim.gLimIbssPeerList; + + while (pCurrNode) { + if (!mac->lim.gLimNumIbssPeers) { + pe_err("Number of peers in the list is zero and node present"); + return; + } + /* Delete the dph entry for the station + * Since it is called to remove all peers, just delete from dph, + * no need to do any beacon related params i.e., dont call lim_delete_dph_hash_entry + */ + sta = + dph_lookup_hash_entry(mac, pCurrNode->peerMacAddr, &peerIdx, + &pe_session->dph.dphHashTable); + if (sta) { + + ibss_status_chg_notify(mac, pCurrNode->peerMacAddr, + eWNI_SME_IBSS_PEER_DEPARTED_IND, + pe_session->smeSessionId); + lim_del_sta(mac, sta, false, pe_session); + lim_release_peer_idx(mac, peerIdx, pe_session); + dph_delete_hash_entry(mac, sta->staAddr, peerIdx, + &pe_session->dph.dphHashTable); + } + + pTempNode = pCurrNode->next; + + /* TODO :Sessionize this code */ + /* Fix CR 227642: PeerList should point to the next node since the current node is being + * freed in the next line. In ibss_peerfind in ibss_status_chg_notify above, we use this + * peer list to find the next peer. So this list needs to be updated with the no of peers left + * after each iteration in this while loop since one by one peers are deleted (freed) in this + * loop causing the lim.gLimIbssPeerList to point to some freed memory. + */ + mac->lim.gLimIbssPeerList = pTempNode; + + if (pCurrNode->beacon) { + qdf_mem_free(pCurrNode->beacon); + } + qdf_mem_free(pCurrNode); + if (mac->lim.gLimNumIbssPeers > 0) /* be paranoid */ + mac->lim.gLimNumIbssPeers--; + pCurrNode = pTempNode; + } + + if (mac->lim.gLimNumIbssPeers) + pe_err("Number of peers: %d in the list is non-zero", + mac->lim.gLimNumIbssPeers); + + mac->lim.gLimNumIbssPeers = 0; + mac->lim.gLimIbssPeerList = NULL; + lim_disconnect_complete(pe_session, false); +} + +/** ------------------------------------------------------------- + \fn lim_ibss_set_protection + \brief Decides all the protection related information. + \ + \param struct mac_context * mac + \param tSirMacAddr peerMacAddr + \param tpUpdateBeaconParams pBeaconParams + \return None + -------------------------------------------------------------*/ +static void +lim_ibss_set_protection(struct mac_context *mac, uint8_t enable, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + + if (!mac->lim.cfgProtection.fromllb) { + pe_err("protection from 11b is disabled"); + return; + } + + if (enable) { + pe_session->gLim11bParams.protectionEnabled = true; + if (false == + pe_session->beaconParams. + llbCoexist /*mac->lim.llbCoexist */) { + pe_debug("=> IBSS: Enable Protection"); + pBeaconParams->llbCoexist = + pe_session->beaconParams.llbCoexist = true; + pBeaconParams->paramChangeBitmap |= + PARAM_llBCOEXIST_CHANGED; + } + } else if (true == + pe_session->beaconParams. + llbCoexist /*mac->lim.llbCoexist */) { + pe_session->gLim11bParams.protectionEnabled = false; + pe_debug("===> IBSS: Disable protection"); + pBeaconParams->llbCoexist = + pe_session->beaconParams.llbCoexist = false; + pBeaconParams->paramChangeBitmap |= PARAM_llBCOEXIST_CHANGED; + } + return; +} + +/** ------------------------------------------------------------- + \fn lim_ibss_update_protection_params + \brief Decides all the protection related information. + \ + \param struct mac_context * mac + \param tSirMacAddr peerMacAddr + \param tpUpdateBeaconParams pBeaconParams + \return None + -------------------------------------------------------------*/ +static void +lim_ibss_update_protection_params(struct mac_context *mac, + tSirMacAddr peerMacAddr, + tLimProtStaCacheType protStaCacheType, + struct pe_session *pe_session) +{ + uint32_t i; + + pe_debug("STA is associated Addr :"); + lim_print_mac_addr(mac, peerMacAddr, LOGD); + + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (mac->lim.protStaCache[i].active) { + pe_debug("Addr:"); + lim_print_mac_addr + (mac, mac->lim.protStaCache[i].addr, LOGD); + + if (!qdf_mem_cmp(mac->lim.protStaCache[i].addr, + peerMacAddr, + sizeof(tSirMacAddr))) { + pe_debug("matching cache entry at: %d already active", + i); + return; + } + } + } + + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (!mac->lim.protStaCache[i].active) + break; + } + + if (i >= LIM_PROT_STA_CACHE_SIZE) { + pe_err("No space in ProtStaCache"); + return; + } + + qdf_mem_copy(mac->lim.protStaCache[i].addr, + peerMacAddr, sizeof(tSirMacAddr)); + + mac->lim.protStaCache[i].protStaCacheType = protStaCacheType; + mac->lim.protStaCache[i].active = true; + if (eLIM_PROT_STA_CACHE_TYPE_llB == protStaCacheType) { + pe_session->gLim11bParams.numSta++; + } else if (eLIM_PROT_STA_CACHE_TYPE_llG == protStaCacheType) { + pe_session->gLim11gParams.numSta++; + } +} + +/** ------------------------------------------------------------- + \fn lim_ibss_decide_protection + \brief Decides all the protection related information. + \ + \param struct mac_context * mac + \param tSirMacAddr peerMacAddr + \param tpUpdateBeaconParams pBeaconParams + \return None + -------------------------------------------------------------*/ +static void +lim_ibss_decide_protection(struct mac_context *mac, tpDphHashNode sta, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + enum reg_wifi_band rfBand = REG_BAND_UNKNOWN; + uint32_t phyMode; + tLimProtStaCacheType protStaCacheType = + eLIM_PROT_STA_CACHE_TYPE_INVALID; + + pBeaconParams->paramChangeBitmap = 0; + + if (!sta) { + pe_err("sta is NULL"); + return; + } + + lim_get_rf_band_new(mac, &rfBand, pe_session); + if (REG_BAND_2G == rfBand) { + lim_get_phy_mode(mac, &phyMode, pe_session); + + /* We are 11G or 11n. Check if we need protection from 11b Stations. */ + if ((phyMode == WNI_CFG_PHY_MODE_11G) + || (pe_session->htCapability)) { + /* As we found in the past, it is possible that a 11n STA sends + * Beacon with HT IE but not ERP IE. So the absence of ERP IE + * in the Beacon is not enough to conclude that STA is 11b. + */ + if ((sta->erpEnabled == eHAL_CLEAR) && + (!sta->mlmStaContext.htCapability)) { + protStaCacheType = eLIM_PROT_STA_CACHE_TYPE_llB; + pe_err("Enable protection from 11B"); + lim_ibss_set_protection(mac, true, + pBeaconParams, + pe_session); + } + } + } + lim_ibss_update_protection_params(mac, sta->staAddr, protStaCacheType, + pe_session); + return; +} + +tLimIbssPeerNode *lim_ibss_peer_find(struct mac_context *mac, tSirMacAddr macAddr) +{ + return ibss_peer_find(mac, macAddr); +} + +QDF_STATUS +lim_ibss_sta_add(struct mac_context *mac, void *pBody, struct pe_session *pe_session) +{ + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + tpDphHashNode sta; + tLimIbssPeerNode *pPeerNode; + tLimMlmStates prevState; + tSirMacAddr *pPeerAddr = (tSirMacAddr *) pBody; + tUpdateBeaconParams beaconParams; + + qdf_mem_zero((uint8_t *) &beaconParams, sizeof(tUpdateBeaconParams)); + + if (pBody == 0) { + pe_err("Invalid IBSS AddSta"); + return QDF_STATUS_E_FAILURE; + } + + pe_debug("Rx Add-Ibss-Sta for MAC:"); + lim_print_mac_addr(mac, *pPeerAddr, LOGD); + + pPeerNode = ibss_peer_find(mac, *pPeerAddr); + if (pPeerNode) { + retCode = + ibss_dph_entry_add(mac, *pPeerAddr, &sta, + pe_session); + if (QDF_STATUS_SUCCESS == retCode) { + prevState = sta->mlmStaContext.mlmState; + sta->erpEnabled = pPeerNode->erpIePresent; + + ibss_sta_info_update(mac, sta, pPeerNode, + pe_session); + pe_debug("initiating ADD STA for the IBSS peer"); + retCode = + lim_add_sta(mac, sta, false, pe_session); + if (retCode != QDF_STATUS_SUCCESS) { + pe_err("ibss-sta-add failed (reason %x)", + retCode); + lim_print_mac_addr(mac, *pPeerAddr, LOGE); + sta->mlmStaContext.mlmState = prevState; + dph_delete_hash_entry(mac, sta->staAddr, + sta->assocId, + &pe_session->dph. + dphHashTable); + } else { + if (mac->lim.gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) + lim_ibss_decide_protection(mac, sta, + &beaconParams, + pe_session); + + if (beaconParams.paramChangeBitmap) { + pe_debug("---> Update Beacon Params"); + sch_set_fixed_beacon_fields(mac, + pe_session); + beaconParams.bss_idx = + pe_session->vdev_id; + lim_send_beacon_params(mac, &beaconParams, + pe_session); + } + } + } else { + pe_err("hashTblAdd failed reason: %x", retCode); + lim_print_mac_addr(mac, *pPeerAddr, LOGE); + } + } else { + retCode = QDF_STATUS_E_FAILURE; + } + + return retCode; +} + +/** + * lim_ibss_search_and_delete_peer()- to cleanup the IBSS + * peer from lim ibss peer list + * + * @mac_ptr: Pointer to Global MAC structure + * @session_entry: Session entry + * @mac_addr: Mac Address of the IBSS peer + * + * This function is called to cleanup the IBSS peer from + * lim ibss peer list + * + * Return: None + * + */ +static void +lim_ibss_search_and_delete_peer(struct mac_context *mac_ctx, + struct pe_session *session_entry, tSirMacAddr mac_addr) +{ + tLimIbssPeerNode *temp_node, *prev_node; + tLimIbssPeerNode *temp_next_node = NULL; + + prev_node = temp_node = mac_ctx->lim.gLimIbssPeerList; + + pe_debug(" PEER ADDR :" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_addr)); + + /** Compare Peer */ + while (temp_node) { + temp_next_node = temp_node->next; + + /* Delete the STA with MAC address */ + if (!qdf_mem_cmp((uint8_t *) mac_addr, + (uint8_t *) &temp_node->peerMacAddr, + sizeof(tSirMacAddr))) { + if (temp_node == + mac_ctx->lim.gLimIbssPeerList) { + mac_ctx->lim.gLimIbssPeerList = + temp_node->next; + prev_node = + mac_ctx->lim.gLimIbssPeerList; + } else + prev_node->next = temp_node->next; + if (temp_node->beacon) + qdf_mem_free(temp_node->beacon); + + qdf_mem_free(temp_node); + mac_ctx->lim.gLimNumIbssPeers--; + + temp_node = temp_next_node; + break; + } + prev_node = temp_node; + temp_node = temp_next_node; + } + /* + * if it is the last peer walking out, we better + * we set IBSS state to inactive. + */ + if (0 == mac_ctx->lim.gLimNumIbssPeers) { + pe_debug("Last STA from IBSS walked out"); + session_entry->limIbssActive = false; + } +} + +/** + * lim_ibss_delete_peer()- to delete IBSS peer + * + * @mac_ptr: Pointer to Global MAC structure + * @session_entry: Session entry + * @mac_addr: Mac Address of the IBSS peer + * @del_sta: del sta sent to firmware if true + * + * This function is called delete IBSS peer. + * + * Return: None + * + */ +static void +lim_ibss_delete_peer(struct mac_context *mac_ctx, + struct pe_session *session_entry, tSirMacAddr mac_addr, + bool del_sta) +{ + tpDphHashNode sta = NULL; + uint16_t peer_idx = 0; + + pe_debug("Delete peer :" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_addr)); + + sta = dph_lookup_hash_entry(mac_ctx, mac_addr, + &peer_idx, + &session_entry->dph. + dphHashTable); + + if (!sta) { + pe_err("DPH Entry for STA "QDF_MAC_ADDR_FMT" is missing", + QDF_MAC_ADDR_REF(mac_addr)); + return; + } + + if (del_sta) { + lim_del_sta(mac_ctx, sta, + true, session_entry); + } else { + /* + * This mean ADD STA failed, thus remove the sta from + * from database and no need to send del sta to firmware + * and peer departed indication to upper layer. + */ + lim_delete_dph_hash_entry(mac_ctx, sta->staAddr, + peer_idx, session_entry); + lim_release_peer_idx(mac_ctx, + peer_idx, session_entry); + lim_ibss_search_and_delete_peer(mac_ctx, + session_entry, mac_addr); + } + +} + +void lim_process_ibss_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg, + struct pe_session *pe_session) +{ + tpDphHashNode sta_ds = NULL; + tpDeleteStaParams del_sta_params = (tpDeleteStaParams) lim_msg->bodyptr; + tSirResultCodes status = eSIR_SME_SUCCESS; + + if (!del_sta_params) { + pe_err("del_sta_params is NULL"); + return; + } + if (!LIM_IS_IBSS_ROLE(pe_session)) { + pe_err("Session %d is not IBSS role", del_sta_params->assocId); + status = eSIR_SME_REFUSED; + goto skip_event; + } + + sta_ds = dph_get_hash_entry(mac_ctx, del_sta_params->assocId, + &pe_session->dph.dphHashTable); + if (!sta_ds) { + pe_err("DPH Entry for STA %X is missing", + del_sta_params->assocId); + status = eSIR_SME_REFUSED; + goto skip_event; + } + + if (QDF_STATUS_SUCCESS != del_sta_params->status) { + pe_err("DEL STA failed!"); + status = eSIR_SME_REFUSED; + goto skip_event; + } + pe_debug("Deleted STA associd %d MAC " QDF_MAC_ADDR_FMT, + sta_ds->assocId, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + + lim_delete_dph_hash_entry(mac_ctx, sta_ds->staAddr, + del_sta_params->assocId, pe_session); + lim_release_peer_idx(mac_ctx, + del_sta_params->assocId, pe_session); + + ibss_status_chg_notify(mac_ctx, + del_sta_params->staMac, + eWNI_SME_IBSS_PEER_DEPARTED_IND, + pe_session->smeSessionId); + + lim_ibss_search_and_delete_peer(mac_ctx, + pe_session, del_sta_params->staMac); + +skip_event: + qdf_mem_free(del_sta_params); + lim_msg->bodyptr = NULL; +} + +/* handle the response from HAL for an ADD STA request */ +QDF_STATUS +lim_ibss_add_sta_rsp(struct mac_context *mac, void *msg, struct pe_session *pe_session) +{ + tpDphHashNode sta; + uint16_t peerIdx; + tpAddStaParams pAddStaParams = (tpAddStaParams) msg; + + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + if (!pAddStaParams) { + pe_err("IBSS: ADD_STA_RSP with no body!"); + return QDF_STATUS_E_FAILURE; + } + + sta = + dph_lookup_hash_entry(mac, pAddStaParams->staMac, &peerIdx, + &pe_session->dph.dphHashTable); + if (!sta) { + pe_err("IBSS: ADD_STA_RSP for unknown MAC addr: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pAddStaParams->staMac)); + qdf_mem_free(pAddStaParams); + return QDF_STATUS_E_FAILURE; + } + + if (pAddStaParams->status != QDF_STATUS_SUCCESS) { + pe_err("IBSS: ADD_STA_RSP error: %x for MAC:"QDF_MAC_ADDR_FMT, + pAddStaParams->status, + QDF_MAC_ADDR_REF(pAddStaParams->staMac)); + lim_ibss_delete_peer(mac, + pe_session, pAddStaParams->staMac, false); + qdf_mem_free(pAddStaParams); + return QDF_STATUS_E_FAILURE; + } + + sta->valid = 1; + sta->mlmStaContext.mlmState = eLIM_MLM_LINK_ESTABLISHED_STATE; + + pe_debug("IBSS: sending IBSS_NEW_PEER msg to SME!"); + + ibss_status_chg_notify(mac, pAddStaParams->staMac, + eWNI_SME_IBSS_NEW_PEER_IND, + pe_session->smeSessionId); + + qdf_mem_free(pAddStaParams); + + return QDF_STATUS_SUCCESS; +} + +void lim_ibss_del_bss_rsp_when_coalescing(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session) +{ + pe_debug("IBSS: DEL_BSS_RSP Rcvd during coalescing!"); + + if (!vdev_stop_rsp) { + pe_err("IBSS: DEL_BSS_RSP(coalesce) with no body!"); + return; + } + + if (vdev_stop_rsp->status != QDF_STATUS_SUCCESS) { + pe_err("IBSS: DEL_BSS_RSP(coalesce) error: %x", + vdev_stop_rsp->status); + return; + } + + /* Delete peer entries. */ + /* add the new bss */ + ibss_bss_add(mac, pe_session); +} + +void lim_ibss_add_bss_rsp_when_coalescing(struct mac_context *mac, + uint32_t op_chan_freq, + struct pe_session *pe_session) +{ + uint8_t infoLen; + struct new_bss_info newBssInfo; + tpSirMacMgmtHdr pHdr = mac->lim.ibss_info.mac_hdr; + tpSchBeaconStruct pBeacon = mac->lim.ibss_info.beacon; + + if ((!pHdr) || (!pBeacon)) { + pe_err("Unable to handle AddBssRspWhenCoalescing (no cached BSS info)"); + goto end; + } + /* Inform Host of IBSS coalescing */ + infoLen = sizeof(tSirMacAddr) + sizeof(tSirMacChanNum) + + sizeof(uint8_t) + pBeacon->ssId.length + 1; + + qdf_mem_zero((void *)&newBssInfo, sizeof(newBssInfo)); + qdf_mem_copy(newBssInfo.bssId.bytes, pHdr->bssId, QDF_MAC_ADDR_SIZE); + newBssInfo.freq = op_chan_freq; + qdf_mem_copy((uint8_t *) &newBssInfo.ssId, + (uint8_t *) &pBeacon->ssId, pBeacon->ssId.length + 1); + + pe_debug("Sending JOINED_NEW_BSS notification to SME"); + + lim_send_sme_wm_status_change_ntf(mac, eSIR_SME_JOINED_NEW_BSS, + (uint32_t *) &newBssInfo, + infoLen, pe_session->smeSessionId); + { + /* Configure beacon and send beacons to HAL */ + lim_send_beacon(mac, pe_session); + } + +end: + ibss_coalesce_free(mac); +} + +void lim_ibss_del_bss_rsp(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session) +{ + tSirResultCodes rc = eSIR_SME_SUCCESS; + + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + if (!vdev_stop_rsp) { + pe_err("IBSS: DEL_BSS_RSP with no body!"); + rc = eSIR_SME_REFUSED; + goto end; + } + + pe_session = pe_find_session_by_vdev_id(mac, vdev_stop_rsp->vdev_id); + if (!pe_session) { + pe_err("Session Does not exist for given sessionID"); + goto end; + } + + /* + * If delBss was issued as part of IBSS Coalescing, gLimIbssCoalescingHappened flag will be true. + * BSS has to be added again in this scenario, so this case needs to be handled separately. + * If delBss was issued as a result of trigger from SME_STOP_BSS Request, then limSme state changes to + * 'IDLE' and gLimIbssCoalescingHappened flag will be false. In this case STOP BSS RSP has to be sent to SME. + */ + if (true == mac->lim.gLimIbssCoalescingHappened) { + lim_ibss_del_bss_rsp_when_coalescing(mac, vdev_stop_rsp, + pe_session); + return; + } + + if (vdev_stop_rsp->status != QDF_STATUS_SUCCESS) { + pe_err("IBSS: DEL_BSS_RSP error: %x", vdev_stop_rsp->status); + rc = eSIR_SME_STOP_BSS_FAILURE; + goto end; + } + + lim_ibss_delete(mac, pe_session); + + dph_hash_table_init(mac, &pe_session->dph.dphHashTable); + lim_delete_pre_auth_list(mac); + + pe_session->limMlmState = eLIM_MLM_IDLE_STATE; + + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + pe_session->limMlmState)); + + pe_session->limSystemRole = eLIM_STA_ROLE; + + /* Change the short slot operating mode to Default (which is 1 for now) so that when IBSS starts next time with Libra + * as originator, it picks up the default. This enables us to remove hard coding of short slot = 1 from lim_apply_configuration + */ + pe_session->shortSlotTimeSupported = + cfg_default(CFG_SHORT_SLOT_TIME_ENABLED); + +end: + /* Delete PE session once BSS is deleted */ + if (pe_session) { + lim_send_sme_rsp(mac, eWNI_SME_STOP_BSS_RSP, rc, + pe_session->smeSessionId); + pe_delete_session(mac, pe_session); + pe_session = NULL; + } +} + +static void lim_ibss_bss_delete(struct mac_context *mac, + struct pe_session *pe_session) +{ + QDF_STATUS status; + + status = wlan_vdev_mlme_sm_deliver_evt( + pe_session->vdev, + WLAN_VDEV_SM_EV_DOWN, + sizeof(*pe_session), + pe_session); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("Deliver WLAN_VDEV_SM_EV_DOWN failed"); +} + +QDF_STATUS +lim_ibss_coalesce(struct mac_context *mac, + tpSirMacMgmtHdr pHdr, + tpSchBeaconStruct pBeacon, + uint8_t *pIEs, + uint32_t ieLen, uint16_t fTsfLater, struct pe_session *pe_session) +{ + uint16_t peerIdx; + tSirMacAddr currentBssId; + tLimIbssPeerNode *pPeerNode; + tpDphHashNode sta; + tUpdateBeaconParams beaconParams; + + qdf_mem_zero((uint8_t *) &beaconParams, sizeof(tUpdateBeaconParams)); + + sir_copy_mac_addr(currentBssId, pe_session->bssId); + + pe_debug("Current BSSID :" QDF_MAC_ADDR_FMT " Received BSSID :" + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(currentBssId), + QDF_MAC_ADDR_REF(pHdr->bssId)); + + /* Check for IBSS Coalescing only if Beacon is from different BSS */ + if (qdf_mem_cmp(currentBssId, pHdr->bssId, sizeof(tSirMacAddr)) + && pe_session->isCoalesingInIBSSAllowed) { + /* + * If STA entry is already available in the LIM hash table, then it is + * possible that the peer may have left and rejoined within the heartbeat + * timeout. In the offloaded case with 32 peers, the HB timeout is whopping + * 128 seconds. In that case, the FW will not let any frames come in until + * atleast the last sequence number is received before the peer is left + * Hence, if the coalescing peer is already there in the peer list and if + * the BSSID matches then, invoke delSta() to cleanup the entries. We will + * let the peer coalesce when we receive next beacon from the peer + */ + pPeerNode = ibss_peer_find(mac, pHdr->sa); + if (pPeerNode) { + lim_ibss_delete_peer(mac, pe_session, + pHdr->sa, true); + pe_warn("Peer attempting to reconnect before HB timeout, deleted"); + return QDF_STATUS_E_INVAL; + } + + if (!fTsfLater) { /* No Coalescing happened. */ + pe_warn("No Coalescing happened"); + return QDF_STATUS_E_INVAL; + } + /* + * IBSS Coalescing happened. + * save the received beacon, and delete the current BSS. The rest of the + * processing will be done in the delBss response processing + */ + mac->lim.gLimIbssCoalescingHappened = true; + ibss_coalesce_save(mac, pHdr, pBeacon); + pe_debug("IBSS Coalescing happened Delete BSSID :" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(currentBssId)); + lim_ibss_bss_delete(mac, pe_session); + + return QDF_STATUS_SUCCESS; + } else { + if (qdf_mem_cmp + (currentBssId, pHdr->bssId, sizeof(tSirMacAddr))) + return QDF_STATUS_E_INVAL; + } + + /* STA in IBSS mode and SSID matches with ours */ + pPeerNode = ibss_peer_find(mac, pHdr->sa); + if (!pPeerNode) { + /* Peer not in the list - Collect BSS description & add to the list */ + uint32_t frameLen; + QDF_STATUS retCode; + + /* + * Limit the Max number of IBSS Peers allowed as the max + * number of STA's allowed + * mac->lim.gLimNumIbssPeers will be increamented after exiting + * this function. so we will add additional 1 to compare against + * mac->lim.gLimIbssStaLimit + */ + if ((mac->lim.gLimNumIbssPeers + 1) >= + mac->lim.gLimIbssStaLimit) { + /*Print every 100th time */ + if (mac->lim.ibss_retry_cnt % 100 == 0) { + pe_debug("**** MAX STA LIMIT HAS REACHED ****"); + } + mac->lim.ibss_retry_cnt++; + return QDF_STATUS_E_NOSPC; + } + pe_debug("IBSS Peer node does not exist, adding it"); + frameLen = + sizeof(tLimIbssPeerNode) + ieLen - sizeof(uint32_t); + + pPeerNode = qdf_mem_malloc((uint16_t) frameLen); + if (!pPeerNode) + return QDF_STATUS_E_NOMEM; + + pPeerNode->beacon = NULL; + pPeerNode->beaconLen = 0; + + ibss_peer_collect(mac, pBeacon, pHdr, pPeerNode, + pe_session); + pPeerNode->beacon = qdf_mem_malloc(ieLen); + if (pPeerNode->beacon) { + qdf_mem_copy(pPeerNode->beacon, pIEs, ieLen); + pPeerNode->beaconLen = (uint16_t) ieLen; + } + ibss_peer_add(mac, pPeerNode); + + sta = + dph_lookup_hash_entry(mac, pPeerNode->peerMacAddr, &peerIdx, + &pe_session->dph.dphHashTable); + if (sta) { + /* / DPH node already exists for the peer */ + pe_warn("DPH Node present for just learned peer"); + lim_print_mac_addr(mac, pPeerNode->peerMacAddr, LOGD); + ibss_sta_info_update(mac, sta, pPeerNode, + pe_session); + return QDF_STATUS_SUCCESS; + } + retCode = + lim_ibss_sta_add(mac, pPeerNode->peerMacAddr, pe_session); + if (retCode != QDF_STATUS_SUCCESS) { + pe_err("lim-ibss-sta-add failed reason: %x", retCode); + lim_print_mac_addr(mac, pPeerNode->peerMacAddr, LOGE); + return retCode; + } + /* Decide protection mode */ + sta = + dph_lookup_hash_entry(mac, pPeerNode->peerMacAddr, &peerIdx, + &pe_session->dph.dphHashTable); + if (mac->lim.gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) + lim_ibss_decide_protection(mac, sta, &beaconParams, + pe_session); + + if (beaconParams.paramChangeBitmap) { + pe_err("beaconParams.paramChangeBitmap=1 ---> Update Beacon Params"); + sch_set_fixed_beacon_fields(mac, pe_session); + beaconParams.bss_idx = pe_session->vdev_id; + lim_send_beacon_params(mac, &beaconParams, pe_session); + } + } else + ibss_sta_caps_update(mac, pPeerNode, pe_session); + + if (pe_session->limSmeState != eLIM_SME_NORMAL_STATE) + return QDF_STATUS_SUCCESS; + + /* Received Beacon from same IBSS we're */ + /* currently part of. Inform Roaming algorithm */ + /* if not already that IBSS is active. */ + if (pe_session->limIbssActive == false) { + limResetHBPktCount(pe_session); + pe_warn("Partner joined our IBSS, Sending IBSS_ACTIVE Notification to SME"); + pe_session->limIbssActive = true; + lim_send_sme_wm_status_change_ntf(mac, eSIR_SME_IBSS_ACTIVE, NULL, 0, + pe_session->smeSessionId); + } + + return QDF_STATUS_SUCCESS; +} /*** end lim_handle_ibs_scoalescing() ***/ + +void lim_ibss_heart_beat_handle(struct mac_context *mac_ctx, struct pe_session *session) +{ + tLimIbssPeerNode *tempnode, *prevnode; + tLimIbssPeerNode *temp_next = NULL; + uint16_t peer_idx = 0; + tpDphHashNode stads = 0; + uint32_t threshold = 0; + + /* + * MLM BSS is started and if PE in scanmode then MLM state will be + * waiting for probe resp. If Heart beat timeout triggers during this + * corner case then we need to reactivate HeartBeat timer. + */ + if (session->limMlmState != eLIM_MLM_BSS_STARTED_STATE) + return; + + /* If LinkMonitor is Disabled */ + if (!mac_ctx->sys.gSysEnableLinkMonitorMode) + return; + + prevnode = tempnode = mac_ctx->lim.gLimIbssPeerList; + threshold = (mac_ctx->lim.gLimNumIbssPeers / 4) + 1; + + /* Monitor the HeartBeat with the Individual PEERS in the IBSS */ + while (tempnode) { + temp_next = tempnode->next; + if (tempnode->beaconHBCount) { + /* There was a beacon for this peer during heart beat */ + tempnode->beaconHBCount = 0; + tempnode->heartbeatFailure = 0; + prevnode = tempnode; + tempnode = temp_next; + continue; + } + + /* There wasnt any beacon received during heartbeat timer. */ + tempnode->heartbeatFailure++; + pe_err("Heartbeat fail: %d thres: %d", + tempnode->heartbeatFailure, mac_ctx->lim.gLimNumIbssPeers); + if (tempnode->heartbeatFailure >= threshold) { + /* Remove this entry from the list. */ + stads = dph_lookup_hash_entry(mac_ctx, + tempnode->peerMacAddr, &peer_idx, + &session->dph.dphHashTable); + if (stads) { + (void)lim_del_sta(mac_ctx, stads, false, + session); + lim_delete_dph_hash_entry(mac_ctx, + stads->staAddr, peer_idx, session); + lim_release_peer_idx(mac_ctx, peer_idx, + session); + /* Send indication. */ + ibss_status_chg_notify(mac_ctx, + tempnode->peerMacAddr, + eWNI_SME_IBSS_PEER_DEPARTED_IND, + session->smeSessionId); + } + if (tempnode == mac_ctx->lim.gLimIbssPeerList) { + mac_ctx->lim.gLimIbssPeerList = tempnode->next; + prevnode = mac_ctx->lim.gLimIbssPeerList; + } else { + prevnode->next = tempnode->next; + } + + if (tempnode->beacon) + qdf_mem_free(tempnode->beacon); + qdf_mem_free(tempnode); + mac_ctx->lim.gLimNumIbssPeers--; + + /* we deleted current node, so prevNode remains same. */ + tempnode = temp_next; + continue; + } + prevnode = tempnode; + tempnode = temp_next; + } + + /* + * General IBSS Activity Monitor, + * check if in IBSS Mode we are received any Beacons + */ + if (mac_ctx->lim.gLimNumIbssPeers) { + if (session->LimRxedBeaconCntDuringHB < + MAX_NO_BEACONS_PER_HEART_BEAT_INTERVAL) + mac_ctx->lim.gLimHeartBeatBeaconStats[ + session->LimRxedBeaconCntDuringHB]++; + else + mac_ctx->lim.gLimHeartBeatBeaconStats[0]++; + + /* Reset number of beacons received */ + limResetHBPktCount(session); + return; + } else { + pe_warn("Heartbeat Failure"); + mac_ctx->lim.gLimHBfailureCntInLinkEstState++; + + if (session->limIbssActive == true) { + /* + * We don't receive Beacon frames from any + * other STA in IBSS. Announce IBSS inactive + * to Roaming algorithm + */ + pe_warn("Alone in IBSS"); + session->limIbssActive = false; + + lim_send_sme_wm_status_change_ntf(mac_ctx, + eSIR_SME_IBSS_INACTIVE, NULL, 0, + session->smeSessionId); + } + } +} + +void lim_ibss_decide_protection_on_delete(struct mac_context *mac_ctx, + tpDphHashNode stads, + tpUpdateBeaconParams bcn_param, + struct pe_session *session) +{ + uint32_t phymode; + tHalBitVal erpenabled = eHAL_CLEAR; + enum reg_wifi_band rfband = REG_BAND_UNKNOWN; + uint32_t i; + + if (!stads) + return; + + lim_get_rf_band_new(mac_ctx, &rfband, session); + if (REG_BAND_2G != rfband) + return; + + lim_get_phy_mode(mac_ctx, &phymode, session); + erpenabled = stads->erpEnabled; + /* we are HT or 11G and 11B station is getting deleted. */ + if (((phymode == WNI_CFG_PHY_MODE_11G) || + session->htCapability) && (erpenabled == eHAL_CLEAR)) { + pe_err("%d A legacy STA is disassociated Addr is", + session->gLim11bParams.numSta); + lim_print_mac_addr(mac_ctx, stads->staAddr, LOGE); + if (session->gLim11bParams.numSta == 0) { + pe_err("No 11B STA exists. Disable protection"); + lim_ibss_set_protection(mac_ctx, false, + bcn_param, session); + } + + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (!mac_ctx->lim.protStaCache[i].active) + continue; + if (!qdf_mem_cmp(mac_ctx->lim.protStaCache[i].addr, + stads->staAddr, sizeof(tSirMacAddr))) { + session->gLim11bParams.numSta--; + mac_ctx->lim.protStaCache[i].active = false; + break; + } + } + + } +} + +/** + * __lim_ibss_peer_inactivity_handler() - Handle inactive IBSS peer + * @mac: Global MAC context + * @pe_session: PE session + * @ind: IBSS peer inactivity indication + * + * Internal function. Deletes FW indicated peer which is inactive + * + * Return: None + */ +static void +__lim_ibss_peer_inactivity_handler(struct mac_context *mac, + struct pe_session *pe_session, + struct ibss_peer_inactivity_ind *ind) +{ + if (pe_session->limMlmState != eLIM_MLM_BSS_STARTED_STATE) { + return; + } + + /* delete the peer for which heartbeat is observed */ + lim_ibss_delete_peer(mac, pe_session, ind->peer_addr.bytes, true); +} + +/** ------------------------------------------------------------- + \fn lim_process_ibss_peer_inactivity + \brief Peer inactivity message handler + \ + \param struct mac_context * mac + \param void* buf + \return None + -------------------------------------------------------------*/ +void lim_process_ibss_peer_inactivity(struct mac_context *mac, void *buf) +{ + /* + * --------------- HEARTBEAT OFFLOAD CASE ------------------ + * This message handler is executed when the firmware identifies + * inactivity from one or more peer devices. We will come here + * for every inactive peer device + */ + uint8_t i; + + struct ibss_peer_inactivity_ind *peerInactivityInd = buf; + + /* + * If IBSS is not started or heartbeat offload is not enabled + * we should not handle this request + */ + if (eLIM_STA_IN_IBSS_ROLE != mac->lim.gLimSystemRole && + !IS_IBSS_HEARTBEAT_OFFLOAD_FEATURE_ENABLE) { + return; + } + + /** If LinkMonitor is Disabled */ + if (!mac->sys.gSysEnableLinkMonitorMode) { + return; + } + + for (i = 0; i < mac->lim.maxBssId; i++) { + if (true == mac->lim.gpSession[i].valid && + eSIR_IBSS_MODE == mac->lim.gpSession[i].bssType) { + __lim_ibss_peer_inactivity_handler(mac, + &mac->lim.gpSession[i], + peerInactivityInd); + break; + } + } +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ibss_peer_mgmt.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ibss_peer_mgmt.h new file mode 100644 index 0000000000000000000000000000000000000000..ec092b2c40b479b47da74f967df891df99610815 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ibss_peer_mgmt.h @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2011-2012, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_ibss_peer_mgmt.h contains prototypes for + * the utility functions LIM uses to maintain peers in IBSS. + * Author: Chandra Modumudi + * Date: 03/12/04 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#include "sir_common.h" +#include "lim_utils.h" + +#ifdef QCA_IBSS_SUPPORT +/** + * ibss_bss_add()- add the ibss + * @mac_ctx: Pointer to Global MAC structure + * @session: pe session + * + * Return: None + */ +void ibss_bss_add(struct mac_context *mac_ctx, struct pe_session *session); + +/** + * ibss_bss_delete()- delete the current BSS + * @mac_ctx: Pointer to Global MAC structure + * @session: pe session + * + * Return: None + */ +void ibss_bss_delete(struct mac_context *mac_ctx, struct pe_session *session); + +/** + * lim_ibss_delete_all_peers() - delete all IBSS peers. + * @mac_ctx: Pointer to Global MAC structure + * @session: pe session + * + * Return: None + */ +void lim_ibss_delete_all_peers(struct mac_context *mac_ctx, struct pe_session *session); + +/** + * lim_ibss_init() - API to init IBSS + * @mac_ctx: Pointer to Global MAC structure + * + * Return: None + */ +void lim_ibss_init(struct mac_context *); + +/** + * lim_ibss_delete() - Delete ibss while tearing down an IBSS + * + * @mac: Pointer to Global MAC structure + * @session: Pointer to session entry + * + * Return: none + */ +void lim_ibss_delete(struct mac_context *, struct pe_session *pe_session); + +/** + * lim_ibss_coalesce() - API to process IBSS Beacon/Probe Response + * @param mac - Pointer to Global MAC structure + * @param pBeacon - Parsed Beacon Frame structure + * @param pBD - Pointer to received BD + * + * This function is called upon receiving Beacon/Probe Response + * while operating in IBSS mode. + * + * @return Status whether to process or ignore received Beacon Frame + */ +QDF_STATUS lim_ibss_coalesce(struct mac_context *, tpSirMacMgmtHdr, + tpSchBeaconStruct, uint8_t *, uint32_t, uint16_t, + struct pe_session *); + +/** + * lim_ibss_sta_add() - API to add an STA context in IBSS role + * @param mac Pointer to Global MAC structure + * @param peerAdddr MAC address of the peer being added + * + * This function is called to add an STA context in IBSS role + * whenever a data frame is received from/for a STA that failed + * hash lookup at DPH. + * + * @return retCode Indicates success or failure return code + */ +QDF_STATUS lim_ibss_sta_add(struct mac_context *, void *, struct pe_session *); + +/** + * lim_ibss_add_sta_rsp() - API to process STA add response in IBSS role + * @param mac Pointer to Global MAC structure + * @param peerAdddr MAC address of the peer being added + * + * This function is called to add an STA context in IBSS role + * whenever a data frame is received from/for a STA that failed + * hash lookup at DPH. + * + * @return retCode Indicates success or failure return code + */ +QDF_STATUS lim_ibss_add_sta_rsp(struct mac_context *, void *, struct pe_session *); + +/** + * lim_process_ibss_del_sta_rsp()- Handle ibss delete + * peer resp from firmware + * @mac_ptr: Pointer to Global MAC structure + * @lim_msg: delete sta response + * @pe_session: pe session + * + * Return: None + * + */ +void lim_process_ibss_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg, + struct pe_session *pe_session); + +/** + * lim_ibss_peer_find() - API to find IBSS peer + * @mac: pointer to mac context + * @param macAddr - MAC address of the peer + * + * This function is called while adding a context at + * DPH & Polaris for a peer in IBSS. + * If peer is found in the list, capabilities from the + * returned BSS description are used at DPH node & Polaris. + * + * @return Pointer to peer node if found, else NULL + */ +tLimIbssPeerNode *lim_ibss_peer_find(struct mac_context *mac, tSirMacAddr macAddr); + +/** + * lim_ibss_del_bss_rsp() - Handle ibss delete + * response + * @mac: Pointer to Global MAC structure + * @vdev_stop_rsp: pointer to del bss response + * @pe_session: pointer to pe session + * + * Return: None + * + */ +void lim_ibss_del_bss_rsp(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session); + +/** + * lim_ibss_del_bss_rsp_when_coalescing() - Handle ibss delete + * response when coalescing + * @mac: Pointer to Global MAC structure + * @vdev_stop_rsp: pointer to del bss response + * @pe_session: pointer to pe session + * + * Return: None + * + */ +void lim_ibss_del_bss_rsp_when_coalescing(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session); + +/** + * lim_ibss_add_bss_rsp_when_coalescing()- Handle ADD BSS rsp of IBSS coalescing + * @mac_ptr: Pointer to Global MAC structure + * @op_chan_freq: operating chan freq + * @pe_session: PE session pointer + * + * Return: None + */ +void lim_ibss_add_bss_rsp_when_coalescing(struct mac_context *mac, + uint32_t op_chan_freq, + struct pe_session *pe_session); + +/** + * lim_ibss_decide_protection_on_delete() - decides protection related info. + * @mac_ctx: global mac context + * @stads: station hash node + * @bcn_param: beacon parameters + * @session: PE session entry + * + * Decides all the protection related information. + * + * Return: None + */ +void lim_ibss_decide_protection_on_delete(struct mac_context *mac, tpDphHashNode sta, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session); + +/** + * lim_ibss_heart_beat_handle() - handle IBSS hearbeat failure + * @mac_ctx: global mac context + * @session: PE session entry + * + * Hanlde IBSS hearbeat failure. + * + * Return: None. + */ +void lim_ibss_heart_beat_handle(struct mac_context *mac, struct pe_session *pe_session); + +/** + * lim_process_ibss_peer_inactivity() - Handle inactive IBSS peer + * @mac: Global MAC context + * @buf: pointer to buffer + * + * Internal function. Deletes FW indicated peer which is inactive + * + * Return: None + */ +void lim_process_ibss_peer_inactivity(struct mac_context *mac, void *buf); +#else +/** + * ibss_bss_add()- api to add ibss + * @mac_ctx: Pointer to Global MAC structure + * @session: pe session + * + * Return: None + */ +static inline +void ibss_bss_add(struct mac_context *mac_ctx, struct pe_session *session) +{ +} + +/** + * ibss_bss_delete()- delete the current BSS + * @mac_ctx: Pointer to Global MAC structure + * @session: pe session + * + * Return: None + */ +static inline +void ibss_bss_delete(struct mac_context *mac_ctx, struct pe_session *session) +{ +} + +/** + * lim_ibss_delete_all_peers: delete all IBSS peers. + * @mac_ctx: Pointer to Global MAC structure + * @session: pe session + * + * Return: None + */ +static inline +void lim_ibss_delete_all_peers(struct mac_context *mac_ctx, + struct pe_session *session) +{ +} + +/** + * lim_ibss_init() - API to init IBSS + * @mac_ctx: Pointer to Global MAC structure + * + * This function is dummy. + * + * Return: None + */ +static inline +void lim_ibss_init(struct mac_context *mac) +{ +} + +/** + * lim_ibss_delete() - Delete ibss while tearing down an IBSS + * @mac: Pointer to Global MAC structure + * @session: Pointer to session entry + * + * Return: none + */ +static inline +void lim_ibss_delete(struct mac_context *mac, struct pe_session *session) +{ +} + +/** + * lim_ibss_coalesce() - API to process IBSS Beacon/Probe Response + * @param mac - Pointer to Global MAC structure + * @param pBeacon - Parsed Beacon Frame structure + * @param pBD - Pointer to received BD + * + * This function is dummy. + * + * @return Status whether to process or ignore received Beacon Frame + */ +static inline +QDF_STATUS lim_ibss_coalesce(struct mac_context *mac, + tpSirMacMgmtHdr pHdr, + tpSchBeaconStruct pBeacon, + uint8_t *pIEs, + uint32_t ieLen, uint16_t fTsfLater, + struct pe_session *pe_session) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * lim_ibss_sta_add() - API to add an STA context in IBSS role + * @param mac Pointer to Global MAC structure + * @param peerAdddr MAC address of the peer being added + * + * This function is dummy. + * + * @return retCode Indicates success or failure return code + */ +static inline +QDF_STATUS lim_ibss_sta_add(struct mac_context *mac, void *pBody, + struct pe_session *pe_session) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * lim_ibss_add_sta_rsp() - API to process STA add response in IBSS role + * @param mac Pointer to Global MAC structure + * @param peerAdddr MAC address of the peer being added + * + * This function is funny. + * + * @return retCode Indicates success or failure return code + */ +static inline +QDF_STATUS lim_ibss_add_sta_rsp(struct mac_context *mac, + void *msg, struct pe_session *pe_session) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * lim_process_ibss_del_sta_rsp()- Handle ibss delete + * peer resp from firmware + * @mac_ptr: Pointer to Global MAC structure + * @lim_msg: delete sta response + * @pe_session: pe session + * + * This function is dymmy. + * + * Return: None + */ +static inline +void lim_process_ibss_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg, + struct pe_session *pe_session) +{ +} + +/** + * lim_ibss_peer_find() - API to find IBSS peer + * @mac: pointer to mac context + * @param macAddr - MAC address of the peer + * + * This function is dymmy. + * + * @return Pointer to peer node if found, else NULL + */ +static inline +tLimIbssPeerNode *lim_ibss_peer_find(struct mac_context *mac, + tSirMacAddr macAddr) +{ + return NULL; +} + +/** + * lim_ibss_del_bss_rsp() - Handle ibss delete + * response + * @mac: Pointer to Global MAC structure + * @vdev_stop_rsp: pointer to del bss response + * @pe_session: pointer to pe session + * + * This function is dymmy. + * + * Return: None + */ +static inline +void lim_ibss_del_bss_rsp(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session) +{ +} + +/** + * lim_ibss_del_bss_rsp_when_coalescing() - Handle ibss delete + * response when coalescing + * @mac: Pointer to Global MAC structure + * @vdev_stop_rsp: pointer to del bss response + * @pe_session: pointer to pe session + * + * Return: None + * + */ +static inline +void lim_ibss_del_bss_rsp_when_coalescing(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session) +{ +} + +/** + * lim_ibss_add_bss_rsp_when_coalescing()- Handle ADD BSS rsp of IBSS coalescing + * @mac_ptr: Pointer to Global MAC structure + * @op_chan_freq: operating chan freq + * @pe_session: PE session pointer + * + * Return: None + */ +static inline +void lim_ibss_add_bss_rsp_when_coalescing(struct mac_context *mac, + uint32_t op_chan_freq, + struct pe_session *pe_session) +{ +} + +/** + * lim_ibss_decide_protection_on_delete() - decides protection related info. + * @mac_ctx: global mac context + * @stads: station hash node + * @bcn_param: beacon parameters + * @session: PE session entry + * + * This function is dummy. + * + * Return: None + */ +static inline +void lim_ibss_decide_protection_on_delete(struct mac_context *mac, + tpDphHashNode sta, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ +} + +/** + * lim_ibss_heart_beat_handle() - handle IBSS hearbeat failure + * @mac_ctx: global mac context + * @session: PE session entry + * + * This function is dummy. + * + * Return: None. + */ +static inline +void lim_ibss_heart_beat_handle(struct mac_context *mac, + struct pe_session *pe_session) +{ +} + +/** + * lim_process_ibss_peer_inactivity() - Handle inactive IBSS peer + * @mac: Global MAC context + * @buf: pointer to buffer + * + * This function is dummy. + * + * Return: None + */ +static inline +void lim_process_ibss_peer_inactivity(struct mac_context *mac, void *buf) +{ +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_link_monitoring_algo.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_link_monitoring_algo.c new file mode 100644 index 0000000000000000000000000000000000000000..e118b35ad8222ee80e38f8b438dbd568183a0b4b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_link_monitoring_algo.c @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_link_monitoring_algo.cc contains the code for + * Link monitoring algorithm on AP and heart beat failure + * handling on STA. + * Author: Chandra Modumudi + * Date: 03/01/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "ani_global.h" +#include "wni_cfg.h" + +#include "sch_api.h" +#include "utils_api.h" +#include "lim_assoc_utils.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_prop_exts_utils.h" + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ +#include "host_diag_core_log.h" +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "lim_ser_des_utils.h" +#include "wlan_blm_api.h" + +/** + * lim_delete_sta_util - utility function for deleting station context + * + * @mac_ctx: global MAC context + * @msg: pointer to delte station context + * @session_entry: PE session entry + * + * utility function called to clear up station context. + * + * Return: None. + */ +static void lim_delete_sta_util(struct mac_context *mac_ctx, tpDeleteStaContext msg, + struct pe_session *session_entry) +{ + tpDphHashNode stads; + + pe_debug("Deleting station: reasonCode: %d", msg->reasonCode); + + if (LIM_IS_IBSS_ROLE(session_entry)) { + return; + } + + stads = dph_lookup_hash_entry(mac_ctx, msg->addr2, &msg->assocId, + &session_entry->dph.dphHashTable); + + if (!stads) { + pe_err("Invalid STA limSystemRole: %d", + GET_LIM_SYSTEM_ROLE(session_entry)); + return; + } + stads->del_sta_ctx_rssi = msg->rssi; + + if (LIM_IS_AP_ROLE(session_entry)) { + pe_debug("Delete Station assocId: %d", msg->assocId); + /* + * Check if Deauth/Disassoc is triggered from Host. + * If mlmState is in some transient state then + * don't trigger STA deletion to avoid the race + * condition. + */ + if ((stads && + ((stads->mlmStaContext.mlmState != + eLIM_MLM_LINK_ESTABLISHED_STATE) && + (stads->mlmStaContext.mlmState != + eLIM_MLM_WT_ASSOC_CNF_STATE) && + (stads->mlmStaContext.mlmState != + eLIM_MLM_ASSOCIATED_STATE)))) { + pe_err("Inv Del STA assocId: %d", msg->assocId); + return; + } else { + lim_send_disassoc_mgmt_frame(mac_ctx, + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON, + stads->staAddr, session_entry, false); + lim_trigger_sta_deletion(mac_ctx, stads, session_entry); + } + } else { +#ifdef FEATURE_WLAN_TDLS + if (LIM_IS_STA_ROLE(session_entry) && + STA_ENTRY_TDLS_PEER == stads->staType) { + /* + * TeardownLink with PEER reason code + * HAL_DEL_STA_REASON_CODE_KEEP_ALIVE means + * eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE + */ + lim_send_sme_tdls_del_sta_ind(mac_ctx, stads, + session_entry, + eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE); + } else { +#endif + /* TearDownLink with AP */ + tLimMlmDeauthInd mlm_deauth_ind; + + pe_debug("Delete Station (assocId: %d)", msg->assocId); + + if ((stads && + ((stads->mlmStaContext.mlmState != + eLIM_MLM_LINK_ESTABLISHED_STATE) && + (stads->mlmStaContext.mlmState != + eLIM_MLM_WT_ASSOC_CNF_STATE) && + (stads->mlmStaContext.mlmState != + eLIM_MLM_ASSOCIATED_STATE)))) { + + /* + * Received SIR_LIM_DELETE_STA_CONTEXT_IND for STA that + * does not have context or in some transit state. + * Log error + */ + pe_debug("Received SIR_LIM_DELETE_STA_CONTEXT_IND for " + "STA that either has no context or " + "in some transit state, Addr = " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(msg->bssId)); + return; + } + + stads->mlmStaContext.disassocReason = + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON; + stads->mlmStaContext.cleanupTrigger = + eLIM_LINK_MONITORING_DEAUTH; + + /* Issue Deauth Indication to SME. */ + qdf_mem_copy((uint8_t *) &mlm_deauth_ind.peerMacAddr, + stads->staAddr, sizeof(tSirMacAddr)); + mlm_deauth_ind.reasonCode = + (uint8_t) stads->mlmStaContext.disassocReason; + mlm_deauth_ind.deauthTrigger = + stads->mlmStaContext.cleanupTrigger; + +#ifdef FEATURE_WLAN_TDLS + /* Delete all TDLS peers connected before leaving BSS */ + lim_delete_tdls_peers(mac_ctx, session_entry); +#endif + if (LIM_IS_STA_ROLE(session_entry)) + lim_post_sme_message(mac_ctx, LIM_MLM_DEAUTH_IND, + (uint32_t *) &mlm_deauth_ind); + + lim_send_sme_deauth_ind(mac_ctx, stads, session_entry); +#ifdef FEATURE_WLAN_TDLS + } +#endif + } +} + +/** + * lim_delete_sta_context() - delete sta context. + * + * @mac_ctx: global mac_ctx context + * @lim_msg: lim message. + * + * This function handles the message from HAL: WMA_DELETE_STA_CONTEXT_IND. + * This function validates that the given station id exist, and if so, + * deletes the station by calling lim_trigger_sta_deletion. + * + * Return: none + */ +void lim_delete_sta_context(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg) +{ + tpDeleteStaContext msg = (tpDeleteStaContext) lim_msg->bodyptr; + struct pe_session *session_entry; + tpDphHashNode sta_ds; + enum eSirMacReasonCodes reason_code; + struct reject_ap_info ap_info; + + if (!msg) { + pe_err("Invalid body pointer in message"); + return; + } + session_entry = pe_find_session_by_vdev_id(mac_ctx, msg->vdev_id); + if (!session_entry) { + pe_err("session not found for given sme session"); + qdf_mem_free(msg); + return; + } + + switch (msg->reasonCode) { + case HAL_DEL_STA_REASON_CODE_KEEP_ALIVE: + case HAL_DEL_STA_REASON_CODE_SA_QUERY_TIMEOUT: + case HAL_DEL_STA_REASON_CODE_XRETRY: + if (LIM_IS_STA_ROLE(session_entry) && !msg->is_tdls) { + if (!((session_entry->limMlmState == + eLIM_MLM_LINK_ESTABLISHED_STATE) && + (session_entry->limSmeState != + eLIM_SME_WT_DISASSOC_STATE) && + (session_entry->limSmeState != + eLIM_SME_WT_DEAUTH_STATE))) { + pe_err("Do not process in limMlmState %s(%x) limSmeState %s(%x)", + lim_mlm_state_str(session_entry->limMlmState), + session_entry->limMlmState, + lim_sme_state_str(session_entry->limSmeState), + session_entry->limSmeState); + qdf_mem_free(msg); + return; + } + sta_ds = dph_get_hash_entry(mac_ctx, + DPH_STA_HASH_INDEX_PEER, + &session_entry->dph.dphHashTable); + if (!sta_ds) { + pe_err("Dph entry not found"); + qdf_mem_free(msg); + return; + } + lim_send_deauth_mgmt_frame(mac_ctx, + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON, + msg->addr2, session_entry, false); + if (msg->reasonCode == + HAL_DEL_STA_REASON_CODE_SA_QUERY_TIMEOUT) + reason_code = eSIR_MAC_SA_QUERY_TIMEOUT; + else if (msg->reasonCode == + HAL_DEL_STA_REASON_CODE_XRETRY) + reason_code = eSIR_MAC_PEER_XRETRY_FAIL; + else + reason_code = eSIR_MAC_PEER_INACTIVITY; + lim_tear_down_link_with_ap(mac_ctx, + session_entry->peSessionId, + reason_code, + eLIM_LINK_MONITORING_DEAUTH); + qdf_mem_copy(&ap_info.bssid, msg->addr2, + QDF_MAC_ADDR_SIZE); + ap_info.reject_ap_type = DRIVER_AVOID_TYPE; + ap_info.reject_reason = REASON_STA_KICKOUT; + ap_info.source = ADDED_BY_DRIVER; + wlan_blm_add_bssid_to_reject_list(mac_ctx->pdev, + &ap_info); + + /* only break for STA role (non TDLS) */ + break; + } + lim_delete_sta_util(mac_ctx, msg, session_entry); + break; + + case HAL_DEL_STA_REASON_CODE_UNKNOWN_A2: + pe_err("Deleting Unknown station"); + lim_print_mac_addr(mac_ctx, msg->addr2, LOGE); + lim_send_deauth_mgmt_frame(mac_ctx, + eSIR_MAC_CLASS3_FRAME_FROM_NON_ASSOC_STA_REASON, + msg->addr2, session_entry, false); + break; + + case HAL_DEL_STA_REASON_CODE_BTM_DISASSOC_IMMINENT: + if (session_entry->limMlmState != + eLIM_MLM_LINK_ESTABLISHED_STATE) { + pe_err("BTM request received in state %s", + lim_mlm_state_str(session_entry->limMlmState)); + qdf_mem_free(msg); + lim_msg->bodyptr = NULL; + return; + } + lim_send_deauth_mgmt_frame(mac_ctx, + eSIR_MAC_BSS_TRANSITION_DISASSOC, + session_entry->bssId, session_entry, false); + lim_tear_down_link_with_ap(mac_ctx, session_entry->peSessionId, + eSIR_MAC_BSS_TRANSITION_DISASSOC, + eLIM_LINK_MONITORING_DEAUTH); + break; + + default: + pe_err("Unknown reason code"); + break; + } + qdf_mem_free(msg); + lim_msg->bodyptr = NULL; + return; +} + +/** + * lim_trigger_sta_deletion() - + * This function is called to trigger STA context deletion. + * + * @param mac_ctx - Pointer to global MAC structure + * @param sta_ds - Pointer to internal STA Datastructure + * @session_entry: PE session entry + + * @return None + */ +void +lim_trigger_sta_deletion(struct mac_context *mac_ctx, tpDphHashNode sta_ds, + struct pe_session *session_entry) +{ + tLimMlmDisassocInd mlm_disassoc_ind; + + if (!sta_ds) { + pe_warn("Skip STA deletion (invalid STA)"); + return; + } + + if ((sta_ds->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_STA_RSP_STATE) || + (sta_ds->mlmStaContext.mlmState == + eLIM_MLM_WT_DEL_BSS_RSP_STATE) || + sta_ds->sta_deletion_in_progress) { + /* Already in the process of deleting context for the peer */ + pe_debug("Deletion is in progress (%d) for peer:%pK in mlmState %d", + sta_ds->sta_deletion_in_progress, sta_ds->staAddr, + sta_ds->mlmStaContext.mlmState); + return; + } + sta_ds->sta_deletion_in_progress = true; + + sta_ds->mlmStaContext.disassocReason = + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON; + sta_ds->mlmStaContext.cleanupTrigger = eLIM_LINK_MONITORING_DISASSOC; + qdf_mem_copy(&mlm_disassoc_ind.peerMacAddr, sta_ds->staAddr, + sizeof(tSirMacAddr)); + mlm_disassoc_ind.reasonCode = + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON; + mlm_disassoc_ind.disassocTrigger = eLIM_LINK_MONITORING_DISASSOC; + + /* Update PE session Id */ + mlm_disassoc_ind.sessionId = session_entry->peSessionId; + lim_post_sme_message(mac_ctx, LIM_MLM_DISASSOC_IND, + (uint32_t *) &mlm_disassoc_ind); + if (mac_ctx->mlme_cfg->gen.fatal_event_trigger) + cds_flush_logs(WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_REASON_HB_FAILURE, + false, false); + /* Issue Disassoc Indication to SME */ + lim_send_sme_disassoc_ind(mac_ctx, sta_ds, session_entry); +} /*** end lim_trigger_st_adeletion() ***/ + +void +lim_tear_down_link_with_ap(struct mac_context *mac, uint8_t sessionId, + tSirMacReasonCodes reasonCode, + enum eLimDisassocTrigger trigger) +{ + tpDphHashNode sta = NULL; + + /* tear down the following pe_session */ + struct pe_session *pe_session; + + pe_session = pe_find_session_by_session_id(mac, sessionId); + if (!pe_session) { + pe_err("Session Does not exist for given sessionID"); + return; + } + /** + * Heart beat failed for upto threshold value + * and AP did not respond for Probe request. + * Trigger link tear down. + */ + pe_session->pmmOffloadInfo.bcnmiss = false; + + pe_info("Session %d Vdev %d reason code %d trigger %d", + pe_session->peSessionId, pe_session->vdev_id, reasonCode, + trigger); + + /* Announce loss of link to Roaming algorithm */ + /* and cleanup by sending SME_DISASSOC_REQ to SME */ + + sta = + dph_get_hash_entry(mac, DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + + if (sta) { + tLimMlmDeauthInd mlmDeauthInd; + + if ((sta->mlmStaContext.disassocReason == + eSIR_MAC_DEAUTH_LEAVING_BSS_REASON) || + (sta->mlmStaContext.cleanupTrigger == + eLIM_HOST_DEAUTH)) { + pe_err("Host already issued deauth, do nothing"); + return; + } + +#ifdef FEATURE_WLAN_TDLS + /* Delete all TDLS peers connected before leaving BSS */ + lim_delete_tdls_peers(mac, pe_session); +#endif + + sta->mlmStaContext.disassocReason = reasonCode; + sta->mlmStaContext.cleanupTrigger = trigger; + /* / Issue Deauth Indication to SME. */ + qdf_mem_copy((uint8_t *) &mlmDeauthInd.peerMacAddr, + sta->staAddr, sizeof(tSirMacAddr)); + + /* + * if deauth_before_connection is enabled and reasoncode is + * Beacon Missed Store the MAC of AP in the flip flop + * buffer. This MAC will be used to send Deauth before + * connection, if we connect to same AP after HB failure. + */ + if (mac->mlme_cfg->sta.deauth_before_connection && + eSIR_MAC_BEACON_MISSED == reasonCode) { + int apCount = mac->lim.gLimHeartBeatApMacIndex; + + if (mac->lim.gLimHeartBeatApMacIndex) + mac->lim.gLimHeartBeatApMacIndex = 0; + else + mac->lim.gLimHeartBeatApMacIndex = 1; + + pe_debug("HB Failure on MAC " + QDF_MAC_ADDR_FMT" Store it on Index %d", + QDF_MAC_ADDR_REF(sta->staAddr), apCount); + + sir_copy_mac_addr(mac->lim.gLimHeartBeatApMac[apCount], + sta->staAddr); + } + + mlmDeauthInd.reasonCode = + (uint8_t) sta->mlmStaContext.disassocReason; + mlmDeauthInd.deauthTrigger = + sta->mlmStaContext.cleanupTrigger; + + if (LIM_IS_STA_ROLE(pe_session)) + lim_post_sme_message(mac, LIM_MLM_DEAUTH_IND, + (uint32_t *) &mlmDeauthInd); + if (mac->mlme_cfg->gen.fatal_event_trigger) + cds_flush_logs(WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_REASON_HB_FAILURE, + false, false); + + lim_send_sme_deauth_ind(mac, sta, pe_session); + } +} /*** lim_tear_down_link_with_ap() ***/ + +/** + * lim_handle_heart_beat_failure() - handle hear beat failure in STA + * + * @mac_ctx: global MAC context + * @session: PE session entry + * + * This function is called when heartbeat (beacon reception) + * fails on STA + * + * Return: None + */ + +void lim_handle_heart_beat_failure(struct mac_context *mac_ctx, + struct pe_session *session) +{ + uint8_t curr_chan; + tpSirAddie scan_ie = NULL; + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + host_log_beacon_update_pkt_type *log_ptr = NULL; +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + WLAN_HOST_DIAG_LOG_ALLOC(log_ptr, host_log_beacon_update_pkt_type, + LOG_WLAN_BEACON_UPDATE_C); + if (log_ptr) + log_ptr->bcn_rx_cnt = session->LimRxedBeaconCntDuringHB; + WLAN_HOST_DIAG_LOG_REPORT(log_ptr); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + /* Ensure HB Status for the session has been reseted */ + session->LimHBFailureStatus = false; + + if (LIM_IS_STA_ROLE(session) && + (session->limMlmState == eLIM_MLM_LINK_ESTABLISHED_STATE) && + (session->limSmeState != eLIM_SME_WT_DISASSOC_STATE) && + (session->limSmeState != eLIM_SME_WT_DEAUTH_STATE)) { + if (!mac_ctx->sys.gSysEnableLinkMonitorMode) { + goto hb_handler_fail; + } + + /* Ignore HB if channel switch is in progress */ + if (session->gLimSpecMgmt.dot11hChanSwState == + eLIM_11H_CHANSW_RUNNING) { + pe_debug("Ignore Heartbeat failure as Channel switch is in progress"); + session->pmmOffloadInfo.bcnmiss = false; + goto hb_handler_fail; + } + /* Beacon frame not received within heartbeat timeout. */ + pe_warn("Heartbeat Failure"); + mac_ctx->lim.gLimHBfailureCntInLinkEstState++; + + /* + * Check if connected on the DFS channel, if not connected on + * DFS channel then only send the probe request otherwise tear + * down the link + */ + curr_chan = wlan_reg_freq_to_chan( + mac_ctx->pdev, session->curr_op_freq); + if (!lim_isconnected_on_dfs_channel(mac_ctx, curr_chan)) { + /* Detected continuous Beacon Misses */ + session->LimHBFailureStatus = true; + + /*Reset the HB packet count before sending probe*/ + limResetHBPktCount(session); + /** + * Send Probe Request frame to AP to see if + * it is still around. Wait until certain + * timeout for Probe Response from AP. + */ + pe_debug("HB missed from AP. Sending Probe Req"); + /* for searching AP, we don't include any more IE */ + if (session->lim_join_req) { + scan_ie = &session->lim_join_req->addIEScan; + lim_send_probe_req_mgmt_frame(mac_ctx, + &session->ssId, + session->bssId, session->curr_op_freq, + session->self_mac_addr, + session->dot11mode, + &scan_ie->length, scan_ie->addIEdata); + } else { + lim_send_probe_req_mgmt_frame(mac_ctx, + &session->ssId, + session->bssId, session->curr_op_freq, + session->self_mac_addr, + session->dot11mode, NULL, NULL); + } + } else { + /* + * Connected on DFS channel so should not send the + * probe request tear down the link directly + */ + lim_tear_down_link_with_ap(mac_ctx, + session->peSessionId, + eSIR_MAC_BEACON_MISSED, + eLIM_LINK_MONITORING_DEAUTH); + } + } else { + /** + * Heartbeat timer may have timed out + * while we're doing background scanning/learning + * or in states other than link-established state. + * Log error. + */ + pe_debug("received heartbeat timeout in state %X", + session->limMlmState); + lim_print_mlm_state(mac_ctx, LOG1, session->limMlmState); + mac_ctx->lim.gLimHBfailureCntInOtherStates++; + } + +hb_handler_fail: + if (mac_ctx->sme.tx_queue_cb) + mac_ctx->sme.tx_queue_cb(mac_ctx->hdd_handle, + session->smeSessionId, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); +} + +void lim_rx_invalid_peer_process(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg) +{ + struct ol_rx_inv_peer_params *msg = + (struct ol_rx_inv_peer_params *)lim_msg->bodyptr; + struct pe_session *session_entry; + uint16_t reason_code = + eSIR_MAC_CLASS3_FRAME_FROM_NON_ASSOC_STA_REASON; + + if (!msg) { + pe_err("Invalid body pointer in message"); + return; + } + + session_entry = pe_find_session_by_vdev_id(mac_ctx, msg->vdev_id); + if (!session_entry) { + pe_err_rl("session not found for given sme session"); + qdf_mem_free(msg); + return; + } + + /* only if SAP mode */ + if (session_entry->bssType == eSIR_INFRA_AP_MODE) { + pe_debug("send deauth frame to non-assoc STA"); + lim_send_deauth_mgmt_frame(mac_ctx, + reason_code, + msg->ta, + session_entry, + false); + } + + qdf_mem_free(msg); + lim_msg->bodyptr = NULL; +} + +void lim_req_send_delba_ind_process(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg) +{ + struct lim_delba_req_info *req = + (struct lim_delba_req_info *)lim_msg->bodyptr; + QDF_STATUS status; + void *dp_soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!req) { + pe_err("Invalid body pointer in message"); + return; + } + + status = lim_send_delba_action_frame(mac_ctx, req->vdev_id, + req->peer_macaddr, + req->tid, req->reason_code); + if (status != QDF_STATUS_SUCCESS) + cdp_delba_tx_completion(dp_soc, req->peer_macaddr, + req->vdev_id, req->tid, + WMI_MGMT_TX_COMP_TYPE_DISCARD); + qdf_mem_free(req); + lim_msg->bodyptr = NULL; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_action_frame.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_action_frame.c new file mode 100644 index 0000000000000000000000000000000000000000..60abe3eed6c404cc6c7be550f5badce9dda251db --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_action_frame.c @@ -0,0 +1,2314 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_process_action_frame.cc contains the code + * for processing Action Frame. + * Author: Michael Lui + * Date: 05/23/03 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#include "cds_api.h" +#include "wni_api.h" +#include "sir_api.h" +#include "ani_global.h" +#include "wni_cfg.h" +#include "sch_api.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_send_sme_rsp_messages.h" +#include "parser_api.h" +#include "lim_admit_control.h" +#include "wmm_apsd.h" +#include "lim_send_messages.h" +#include "rrm_api.h" +#include "lim_session_utils.h" +#include "wlan_policy_mgr_api.h" +#include "wma_types.h" +#include "wma.h" +#include +#include +#include "dot11f.h" +#include "wlan_p2p_cfg_api.h" + +static last_processed_msg rrm_link_action_frm; + +/**----------------------------------------------------------------- + \fn lim_stop_tx_and_switch_channel + \brief Stops the transmission if channel switch mode is silent and + starts the channel switch timer. + + \param mac + \return NONE + -----------------------------------------------------------------*/ +void lim_stop_tx_and_switch_channel(struct mac_context *mac, uint8_t sessionId) +{ + struct pe_session *pe_session; + QDF_STATUS status; + + pe_session = pe_find_session_by_session_id(mac, sessionId); + + if (!pe_session) { + pe_err("Session: %d not active", sessionId); + return; + } + + if (pe_session->ftPEContext.pFTPreAuthReq) { + pe_debug("Avoid Switch Channel req during pre auth"); + return; + } + + mac->lim.lim_timers.gLimChannelSwitchTimer.sessionId = sessionId; + status = policy_mgr_check_and_set_hw_mode_for_channel_switch(mac->psoc, + pe_session->smeSessionId, + pe_session->gLimChannelSwitch.sw_target_freq, + POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_STA); + + /* + * If status is QDF_STATUS_E_FAILURE, mean HW mode change was required + * but driver failed to set HW mode so ignore CSA for the channel. + * If status is QDF_STATUS_SUCCESS mean HW mode change was required + * and was sucessfully changed so the channel switch will continue after + * HW mode change completion. + * If status is QDF_STATUS_E_NOSUPPORT or QDF_STATUS_E_ALREADY, mean + * DBS is not supported or required HW mode is already set, so + * So contunue with CSA from here. + */ + if (status == QDF_STATUS_E_FAILURE) { + pe_err("Failed to set required HW mode for channel %d freq %d, ignore CSA", + pe_session->gLimChannelSwitch.primaryChannel, + pe_session->gLimChannelSwitch.sw_target_freq); + return; + } + + if (QDF_IS_STATUS_SUCCESS(status)) { + pe_info("Channel change will continue after HW mode change"); + return; + } + /* change the channel immediately only if + * the channel switch count is 0 + */ + if (pe_session->gLimChannelSwitch.switchCount == 0) { + lim_process_channel_switch_timeout(mac); + return; + } + MTRACE(mac_trace + (mac, TRACE_CODE_TIMER_ACTIVATE, sessionId, + eLIM_CHANNEL_SWITCH_TIMER)); + + if (tx_timer_activate(&mac->lim.lim_timers.gLimChannelSwitchTimer) != + TX_SUCCESS) { + pe_err("tx_timer_activate failed"); + } + return; +} + +/**------------------------------------------------------------ + \fn lim_start_channel_switch + \brief Switches the channel if switch count == 0, otherwise + starts the timer for channel switch and stops BG scan + and heartbeat timer tempororily. + + \param mac + \param pe_session + \return NONE + ------------------------------------------------------------*/ +QDF_STATUS lim_start_channel_switch(struct mac_context *mac, + struct pe_session *pe_session) +{ + pe_debug("Starting the channel switch"); + + /*If channel switch is already running and it is on a different session, just return */ + /*This need to be removed for MCC */ + if ((lim_is_chan_switch_running(mac) && + pe_session->gLimSpecMgmt.dot11hChanSwState != + eLIM_11H_CHANSW_RUNNING) || pe_session->csaOffloadEnable) { + pe_warn("Ignoring channel switch on session: %d", + pe_session->peSessionId); + return QDF_STATUS_SUCCESS; + } + + /* Deactivate and change reconfigure the timeout value */ + /* lim_deactivate_and_change_timer(mac, eLIM_CHANNEL_SWITCH_TIMER); */ + MTRACE(mac_trace + (mac, TRACE_CODE_TIMER_DEACTIVATE, pe_session->peSessionId, + eLIM_CHANNEL_SWITCH_TIMER)); + if (tx_timer_deactivate(&mac->lim.lim_timers.gLimChannelSwitchTimer) != + QDF_STATUS_SUCCESS) { + pe_err("tx_timer_deactivate failed!"); + return QDF_STATUS_E_FAILURE; + } + + if (tx_timer_change(&mac->lim.lim_timers.gLimChannelSwitchTimer, + pe_session->gLimChannelSwitch.switchTimeoutValue, + 0) != TX_SUCCESS) { + pe_err("tx_timer_change failed"); + return QDF_STATUS_E_FAILURE; + } + + /* Prepare for 11h channel switch */ + lim_prepare_for11h_channel_switch(mac, pe_session); + + /** Dont add any more statements here as we posted finish scan request + * to HAL, wait till we get the response + */ + return QDF_STATUS_SUCCESS; +} + +/** + * __lim_process_channel_switch_action_frame() - to process channel switch + * @mac_ctx: Pointer to Global MAC structure + * @rx_pkt_info: A pointer to packet info structure + * + * This routine will be called to process channel switch action frame + * + * Return: None + */ + +static void __lim_process_channel_switch_action_frame(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, struct pe_session *session) +{ + tpSirMacMgmtHdr mac_hdr; + uint8_t *body_ptr; + tDot11fChannelSwitch *chnl_switch_frame; + uint16_t bcn_period; + uint32_t val, frame_len, status; + tLimChannelSwitchInfo *ch_switch_params; + struct sDot11fIEWiderBWChanSwitchAnn *wbw_chnlswitch_ie = NULL; + struct sLimWiderBWChannelSwitch *lim_wbw_chnlswitch_info = NULL; + struct sDot11fIEsec_chan_offset_ele *sec_chnl_offset = NULL; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + pe_debug("Received Channel switch action frame"); + if (!session->lim11hEnable) + return; + + chnl_switch_frame = qdf_mem_malloc(sizeof(*chnl_switch_frame)); + if (!chnl_switch_frame) + return; + + /* Unpack channel switch frame */ + status = dot11f_unpack_channel_switch(mac_ctx, body_ptr, frame_len, + chnl_switch_frame, false); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to unpack and parse (0x%08x, %d bytes)", + status, frame_len); + qdf_mem_free(chnl_switch_frame); + return; + } else if (DOT11F_WARNED(status)) { + pe_warn("warning: unpack 11h-CHANSW Req(0x%08x, %d bytes)", + status, frame_len); + } + + if (qdf_mem_cmp((uint8_t *) &session->bssId, + (uint8_t *) &mac_hdr->sa, sizeof(tSirMacAddr))) { + pe_warn("Rcvd action frame not from our BSS, dropping"); + qdf_mem_free(chnl_switch_frame); + return; + } + /* copy the beacon interval from session */ + val = session->beaconParams.beaconInterval; + ch_switch_params = &session->gLimChannelSwitch; + bcn_period = (uint16_t)val; + ch_switch_params->primaryChannel = + chnl_switch_frame->ChanSwitchAnn.newChannel; + ch_switch_params->sw_target_freq = wlan_reg_legacy_chan_to_freq + (mac_ctx->pdev, + chnl_switch_frame->ChanSwitchAnn.newChannel); + ch_switch_params->switchCount = + chnl_switch_frame->ChanSwitchAnn.switchCount; + ch_switch_params->switchTimeoutValue = + SYS_MS_TO_TICKS(bcn_period) * + session->gLimChannelSwitch.switchCount; + ch_switch_params->switchMode = + chnl_switch_frame->ChanSwitchAnn.switchMode; + + /* Only primary channel switch element is present */ + ch_switch_params->state = eLIM_CHANNEL_SWITCH_PRIMARY_ONLY; + ch_switch_params->ch_width = CH_WIDTH_20MHZ; + + if (chnl_switch_frame->WiderBWChanSwitchAnn.present + && session->vhtCapability) { + wbw_chnlswitch_ie = &chnl_switch_frame->WiderBWChanSwitchAnn; + session->gLimWiderBWChannelSwitch.newChanWidth = + wbw_chnlswitch_ie->newChanWidth; + session->gLimWiderBWChannelSwitch.newCenterChanFreq0 = + wbw_chnlswitch_ie->newCenterChanFreq0; + session->gLimWiderBWChannelSwitch.newCenterChanFreq1 = + wbw_chnlswitch_ie->newCenterChanFreq1; + } + pe_debug("Rcv Chnl Swtch Frame: Timeout in %d ticks", + session->gLimChannelSwitch.switchTimeoutValue); + if (session->htSupportedChannelWidthSet) { + sec_chnl_offset = &chnl_switch_frame->sec_chan_offset_ele; + if (sec_chnl_offset->secondaryChannelOffset == + PHY_DOUBLE_CHANNEL_LOW_PRIMARY) { + ch_switch_params->state = + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY; + ch_switch_params->ch_width = CH_WIDTH_40MHZ; + ch_switch_params->ch_center_freq_seg0 = + ch_switch_params->primaryChannel + 2; + } else if (sec_chnl_offset->secondaryChannelOffset == + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY) { + ch_switch_params->state = + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY; + ch_switch_params->ch_width = CH_WIDTH_40MHZ; + ch_switch_params->ch_center_freq_seg0 = + ch_switch_params->primaryChannel - 2; + + } + if (session->vhtCapability && + chnl_switch_frame->WiderBWChanSwitchAnn.present) { + wbw_chnlswitch_ie = + &chnl_switch_frame->WiderBWChanSwitchAnn; + ch_switch_params->ch_width = + wbw_chnlswitch_ie->newChanWidth + 1; + lim_wbw_chnlswitch_info = + &session->gLimWiderBWChannelSwitch; + ch_switch_params->ch_center_freq_seg0 = + lim_wbw_chnlswitch_info->newCenterChanFreq0; + ch_switch_params->ch_center_freq_seg1 = + lim_wbw_chnlswitch_info->newCenterChanFreq1; + + } + } + + if (CH_WIDTH_20MHZ == ch_switch_params->ch_width) { + session->htSupportedChannelWidthSet = + WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + session->htRecommendedTxWidthSet = + session->htSupportedChannelWidthSet; + } + + if (QDF_STATUS_SUCCESS != lim_start_channel_switch(mac_ctx, session)) + pe_err("Could not start channel switch"); + + qdf_mem_free(chnl_switch_frame); + return; +} + +/** + * lim_process_ext_channel_switch_action_frame()- Process ECSA Action + * Frames. + * @mac_ctx: pointer to global mac structure + * @rx_packet_info: rx packet meta information + * @session_entry: Session entry. + * + * This function is called when ECSA action frame is received. + * + * Return: void + */ +static void +lim_process_ext_channel_switch_action_frame(struct mac_context *mac_ctx, + uint8_t *rx_packet_info, struct pe_session *session_entry) +{ + + tpSirMacMgmtHdr hdr; + uint8_t *body; + tDot11fext_channel_switch_action_frame *ext_channel_switch_frame; + uint32_t frame_len; + uint32_t status; + uint32_t target_freq; + + hdr = WMA_GET_RX_MAC_HEADER(rx_packet_info); + body = WMA_GET_RX_MPDU_DATA(rx_packet_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_packet_info); + + pe_debug("Received EXT Channel switch action frame"); + + ext_channel_switch_frame = + qdf_mem_malloc(sizeof(*ext_channel_switch_frame)); + if (!ext_channel_switch_frame) + return; + + /* Unpack channel switch frame */ + status = dot11f_unpack_ext_channel_switch_action_frame(mac_ctx, + body, frame_len, ext_channel_switch_frame, false); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse CHANSW action frame (0x%08x, len %d):", + status, frame_len); + qdf_mem_free(ext_channel_switch_frame); + return; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking CHANSW Request (0x%08x, %d bytes):", + status, frame_len); + } + + if (!wlan_reg_is_6ghz_supported(mac_ctx->psoc) && + (wlan_reg_is_6ghz_op_class(mac_ctx->pdev, + ext_channel_switch_frame-> + ext_chan_switch_ann_action.op_class))) { + pe_err("channel belongs to 6 ghz spectrum, abort"); + qdf_mem_free(ext_channel_switch_frame); + return; + } + + target_freq = + wlan_reg_chan_opclass_to_freq(ext_channel_switch_frame->ext_chan_switch_ann_action.new_channel, + ext_channel_switch_frame->ext_chan_switch_ann_action.op_class, + false); + + /* Free ext_channel_switch_frame here as its no longer needed */ + qdf_mem_free(ext_channel_switch_frame); + /* + * Now, validate if channel change is required for the passed + * channel and if is valid in the current regulatory domain, + * and no concurrent session is running. + */ + if (!(session_entry->curr_op_freq != target_freq && + ((wlan_reg_get_channel_state_for_freq(mac_ctx->pdev, target_freq) == + CHANNEL_STATE_ENABLE) || + (wlan_reg_get_channel_state_for_freq(mac_ctx->pdev, target_freq) == + CHANNEL_STATE_DFS && + !policy_mgr_concurrent_open_sessions_running( + mac_ctx->psoc))))) { + pe_err("Channel freq: %d is not valid", target_freq); + return; + } + + if (session_entry->opmode == QDF_P2P_GO_MODE) { + + struct sir_sme_ext_cng_chan_ind *ext_cng_chan_ind; + struct scheduler_msg mmh_msg = {0}; + + ext_cng_chan_ind = qdf_mem_malloc(sizeof(*ext_cng_chan_ind)); + if (!ext_cng_chan_ind) + return; + + ext_cng_chan_ind->session_id = + session_entry->smeSessionId; + + /* No need to extract op mode as BW will be decided in + * in SAP FSM depending on previous BW. + */ + ext_cng_chan_ind->new_chan_freq = target_freq; + + mmh_msg.type = eWNI_SME_EXT_CHANGE_CHANNEL_IND; + mmh_msg.bodyptr = ext_cng_chan_ind; + mmh_msg.bodyval = 0; + lim_sys_process_mmh_msg_api(mac_ctx, &mmh_msg); + } + return; +} /*** end lim_process_ext_channel_switch_action_frame() ***/ + +/** + * __lim_process_operating_mode_action_frame() - To process op mode frames + * @mac_ctx: pointer to mac context + * @rx_pkt_info: pointer to received packet info + * @session: pointer to session + * + * This routine is called to process operating mode action frames + * + * Return: None + */ +static void __lim_process_operating_mode_action_frame(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, struct pe_session *session) +{ + + tpSirMacMgmtHdr mac_hdr; + uint8_t *body_ptr; + tDot11fOperatingMode *operating_mode_frm; + uint32_t frame_len; + uint32_t status; + tpDphHashNode sta_ptr; + uint16_t aid; + uint8_t oper_mode; + uint8_t cb_mode; + uint8_t ch_bw = 0; + uint8_t skip_opmode_update = false; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + pe_debug("Received Operating Mode action frame"); + + /* + * Ignore opmode change during channel change The opmode will be updated + * with the beacons on new channel once the AP move to new channel. + */ + if (session->ch_switch_in_progress) { + pe_debug("Ignore opmode change as channel switch is in progress"); + return; + } + operating_mode_frm = qdf_mem_malloc(sizeof(*operating_mode_frm)); + if (!operating_mode_frm) + return; + + /* Unpack channel switch frame */ + status = dot11f_unpack_operating_mode(mac_ctx, body_ptr, frame_len, + operating_mode_frm, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to unpack and parse (0x%08x, %d bytes)", + status, frame_len); + qdf_mem_free(operating_mode_frm); + return; + } else if (DOT11F_WARNED(status)) { + pe_warn("warnings while unpacking (0x%08x, %d bytes):", + status, frame_len); + } + sta_ptr = dph_lookup_hash_entry(mac_ctx, mac_hdr->sa, &aid, + &session->dph.dphHashTable); + + if (!sta_ptr) { + pe_err("Station context not found"); + goto end; + } + + if (wlan_reg_is_24ghz_ch_freq(session->curr_op_freq)) + cb_mode = mac_ctx->roam.configParam.channelBondingMode24GHz; + else + cb_mode = mac_ctx->roam.configParam.channelBondingMode5GHz; + /* + * Do not update the channel bonding mode if channel bonding + * mode is disabled in INI. + */ + if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE == cb_mode) { + pe_debug("channel bonding disabled"); + goto update_nss; + } + + if (sta_ptr->htSupportedChannelWidthSet) { + if (WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ < + sta_ptr->vhtSupportedChannelWidthSet) + oper_mode = eHT_CHANNEL_WIDTH_160MHZ; + else + oper_mode = sta_ptr->vhtSupportedChannelWidthSet + 1; + } else { + oper_mode = eHT_CHANNEL_WIDTH_20MHZ; + } + + if ((oper_mode == eHT_CHANNEL_WIDTH_80MHZ) && + (operating_mode_frm->OperatingMode.chanWidth > + eHT_CHANNEL_WIDTH_80MHZ)) + skip_opmode_update = true; + + if (!skip_opmode_update && (oper_mode != + operating_mode_frm->OperatingMode.chanWidth)) { + uint32_t fw_vht_ch_wd = wma_get_vht_ch_width(); + + pe_debug("received Chanwidth: %d", + operating_mode_frm->OperatingMode.chanWidth); + + pe_debug(" MAC: %0x:%0x:%0x:%0x:%0x:%0x", + mac_hdr->sa[0], mac_hdr->sa[1], mac_hdr->sa[2], + mac_hdr->sa[3], mac_hdr->sa[4], mac_hdr->sa[5]); + + if (operating_mode_frm->OperatingMode.chanWidth >= + eHT_CHANNEL_WIDTH_160MHZ + && (fw_vht_ch_wd >= eHT_CHANNEL_WIDTH_160MHZ)) { + sta_ptr->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ; + sta_ptr->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + ch_bw = eHT_CHANNEL_WIDTH_160MHZ; + } else if (operating_mode_frm->OperatingMode.chanWidth >= + eHT_CHANNEL_WIDTH_80MHZ) { + sta_ptr->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ; + sta_ptr->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + ch_bw = eHT_CHANNEL_WIDTH_80MHZ; + } else if (operating_mode_frm->OperatingMode.chanWidth == + eHT_CHANNEL_WIDTH_40MHZ) { + sta_ptr->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ; + sta_ptr->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + ch_bw = eHT_CHANNEL_WIDTH_40MHZ; + } else if (operating_mode_frm->OperatingMode.chanWidth == + eHT_CHANNEL_WIDTH_20MHZ) { + sta_ptr->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ; + sta_ptr->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_20MHZ; + ch_bw = eHT_CHANNEL_WIDTH_20MHZ; + } + lim_check_vht_op_mode_change(mac_ctx, session, ch_bw, + mac_hdr->sa); + } + +update_nss: + if (sta_ptr->vhtSupportedRxNss != + (operating_mode_frm->OperatingMode.rxNSS + 1)) { + sta_ptr->vhtSupportedRxNss = + operating_mode_frm->OperatingMode.rxNSS + 1; + lim_set_nss_change(mac_ctx, session, sta_ptr->vhtSupportedRxNss, + mac_hdr->sa); + } + +end: + qdf_mem_free(operating_mode_frm); + return; +} + +/** + * __lim_process_gid_management_action_frame() - To process group-id mgmt frames + * @mac_ctx: Pointer to mac context + * @rx_pkt_info: Rx packet info + * @session: pointer to session + * + * This routine will be called to process group id management frames + * + * Return: none + */ +static void +__lim_process_gid_management_action_frame(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, + struct pe_session *session) +{ + uint8_t *body_ptr; + uint16_t aid; + uint32_t frame_len, status, membership = 0, usr_position = 0; + uint32_t *mem_lower, *mem_upper, *mem_cur; + tpSirMacMgmtHdr mac_hdr; + tDot11fVHTGidManagementActionFrame *gid_mgmt_frame; + tpDphHashNode sta_ptr; + struct sDot11fFfVhtMembershipStatusArray *vht_member_status = NULL; + struct sDot11fFfVhtUserPositionArray *vht_user_position = NULL; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + pe_debug("Received GID Management action frame"); + gid_mgmt_frame = qdf_mem_malloc(sizeof(*gid_mgmt_frame)); + if (!gid_mgmt_frame) + return; + + /* Unpack Gid Management Action frame */ + status = dot11f_unpack_vht_gid_management_action_frame(mac_ctx, + body_ptr, frame_len, gid_mgmt_frame, false); + if (DOT11F_FAILED(status)) { + pe_err("Fail to parse an Grp id frame (0x%08x, %d bytes):", + status, frame_len); + qdf_mem_free(gid_mgmt_frame); + return; + } else if (DOT11F_WARNED(status)) { + pe_warn("warnings while unpacking Grp id frm (0x%08x, %d bytes):", + status, frame_len); + } + sta_ptr = dph_lookup_hash_entry(mac_ctx, mac_hdr->sa, &aid, + &session->dph.dphHashTable); + if (!sta_ptr) { + pe_err("Failed to get STA entry from hash table"); + goto out; + } + + pe_debug(" MAC: %0x:%0x:%0x:%0x:%0x:%0x", + mac_hdr->sa[0], mac_hdr->sa[1], mac_hdr->sa[2], + mac_hdr->sa[3], mac_hdr->sa[4], mac_hdr->sa[5]); + vht_member_status = &gid_mgmt_frame->VhtMembershipStatusArray; + mem_lower = (uint32_t *) vht_member_status->membershipStatusArray; + mem_upper = (uint32_t *) &vht_member_status->membershipStatusArray[4]; + + if (*mem_lower && *mem_upper) { + pe_err("rcved frame with mult group ID set"); + goto out; + } + if (*mem_lower) { + mem_cur = mem_lower; + } else if (*mem_upper) { + mem_cur = mem_upper; + membership += sizeof(uint32_t); + } else { + pe_err("rcved Gid frame with no group ID set"); + goto out; + } + while (!(*mem_cur & 1)) { + *mem_cur >>= 1; + ++membership; + } + if (*mem_cur) { + pe_err("rcved frame with mult group ID set"); + goto out; + } + + /*Just read the last two bits */ + vht_user_position = &gid_mgmt_frame->VhtUserPositionArray; + usr_position = vht_user_position->userPositionArray[membership] & 0x3; + lim_check_membership_user_position(mac_ctx, session, membership, + usr_position); +out: + qdf_mem_free(gid_mgmt_frame); + return; +} + +static void +__lim_process_add_ts_req(struct mac_context *mac, uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ +} + +/** + * __lim_process_add_ts_rsp() - To process add ts response frame + * @mac_ctx: pointer to mac context + * @rx_pkt_info: Received packet info + * @session: pointer to session + * + * This routine is to handle add ts response frame + * + * Return: none + */ +static void __lim_process_add_ts_rsp(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, struct pe_session *session) +{ + tSirAddtsRspInfo addts; + QDF_STATUS retval; + tpSirMacMgmtHdr mac_hdr; + tpDphHashNode sta_ptr; + uint16_t aid; + uint32_t frameLen; + uint8_t *body_ptr; + tpLimTspecInfo tspec_info; + uint8_t ac; + tpDphHashNode sta_ds_ptr = NULL; + uint8_t rsp_reqd = 1; + uint32_t cfg_len; + tSirMacAddr peer_macaddr; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frameLen = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + pe_warn("Recv AddTs Response"); + if (LIM_IS_AP_ROLE(session)) { + pe_warn("AddTsRsp recvd at AP: ignoring"); + return; + } + + sta_ptr = dph_lookup_hash_entry(mac_ctx, mac_hdr->sa, &aid, + &session->dph.dphHashTable); + if (!sta_ptr) { + pe_err("Station context not found - ignoring AddTsRsp"); + return; + } + + retval = sir_convert_addts_rsp2_struct(mac_ctx, body_ptr, + frameLen, &addts); + if (retval != QDF_STATUS_SUCCESS) { + pe_err("AddTsRsp parsing failed %d", retval); + return; + } + /* + * don't have to check for qos/wme capabilities since we wouldn't have + * this flag set otherwise + */ + if (!mac_ctx->lim.gLimAddtsSent) { + /* we never sent an addts request! */ + pe_warn("rx AddTsRsp but no req was ever sent-ignoring"); + return; + } + + if (mac_ctx->lim.gLimAddtsReq.req.dialogToken != addts.dialogToken) { + pe_warn("token mismatch got: %d exp: %d - ignoring", + addts.dialogToken, + mac_ctx->lim.gLimAddtsReq.req.dialogToken); + return; + } + + /* + * for successful addts response, try to add the classifier. + * if this fails for any reason, we should send a delts request to the + * ap for now, its ok not to send a delts since we are going to add + * support for multiple tclas soon and until then we won't send any + * addts requests with multiple tclas elements anyway. + * In case of addClassifier failure, we just let the addts timer run out + */ + if (((addts.tspec.tsinfo.traffic.accessPolicy == + SIR_MAC_ACCESSPOLICY_HCCA) || + (addts.tspec.tsinfo.traffic.accessPolicy == + SIR_MAC_ACCESSPOLICY_BOTH)) && + (addts.status == eSIR_MAC_SUCCESS_STATUS)) { + /* add the classifier - this should always succeed */ + if (addts.numTclas > 1) { + /* currently no support for multiple tclas elements */ + pe_err("Sta: %d Too many Tclas: %d 1 supported", + aid, addts.numTclas); + return; + } else if (addts.numTclas == 1) { + pe_debug("Response from STA: %d tsid: %d UP: %d OK!", + aid, addts.tspec.tsinfo.traffic.tsid, + addts.tspec.tsinfo.traffic.userPrio); + } + } + pe_debug("Recv AddTsRsp: tsid: %d UP: %d status: %d", + addts.tspec.tsinfo.traffic.tsid, + addts.tspec.tsinfo.traffic.userPrio, addts.status); + + /* deactivate the response timer */ + lim_deactivate_and_change_timer(mac_ctx, eLIM_ADDTS_RSP_TIMER); + + if (addts.status != eSIR_MAC_SUCCESS_STATUS) { + pe_debug("Recv AddTsRsp: tsid: %d UP: %d status: %d", + addts.tspec.tsinfo.traffic.tsid, + addts.tspec.tsinfo.traffic.userPrio, addts.status); + lim_send_sme_addts_rsp(mac_ctx, true, addts.status, session, + addts.tspec, session->smeSessionId); + + /* clear the addts flag */ + mac_ctx->lim.gLimAddtsSent = false; + + return; + } +#ifdef FEATURE_WLAN_ESE + if (addts.tsmPresent) { + pe_debug("TSM IE Present"); + session->eseContext.tsm.tid = + addts.tspec.tsinfo.traffic.userPrio; + qdf_mem_copy(&session->eseContext.tsm.tsmInfo, + &addts.tsmIE, sizeof(struct ese_tsm_ie)); + lim_send_sme_tsm_ie_ind(mac_ctx, session, addts.tsmIE.tsid, + addts.tsmIE.state, + addts.tsmIE.msmt_interval); + } +#endif + /* + * Since AddTS response was successful, check for the PSB flag + * and directional flag inside the TS Info field. + * An AC is trigger enabled AC if the PSB subfield is set to 1 + * in the uplink direction. + * An AC is delivery enabled AC if the PSB subfield is set to 1 + * in the downlink direction. + * An AC is trigger and delivery enabled AC if the PSB subfield + * is set to 1 in the bi-direction field. + */ + if (addts.tspec.tsinfo.traffic.psb == 1) + lim_set_tspec_uapsd_mask_per_session(mac_ctx, session, + &addts.tspec.tsinfo, + SET_UAPSD_MASK); + else + lim_set_tspec_uapsd_mask_per_session(mac_ctx, session, + &addts.tspec.tsinfo, + CLEAR_UAPSD_MASK); + + /* + * ADDTS success, so AC is now admitted. We shall now use the default + * EDCA parameters as advertised by AP and send the updated EDCA params + * to HAL. + */ + ac = upToAc(addts.tspec.tsinfo.traffic.userPrio); + if (addts.tspec.tsinfo.traffic.direction == + SIR_MAC_DIRECTION_UPLINK) { + session->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] |= + (1 << ac); + } else if (addts.tspec.tsinfo.traffic.direction == + SIR_MAC_DIRECTION_DNLINK) { + session->gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK] |= + (1 << ac); + } else if (addts.tspec.tsinfo.traffic.direction == + SIR_MAC_DIRECTION_BIDIR) { + session->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] |= + (1 << ac); + session->gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK] |= + (1 << ac); + } + lim_set_active_edca_params(mac_ctx, session->gLimEdcaParams, + session); + sta_ds_ptr = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session->dph.dphHashTable); + if (sta_ds_ptr) + lim_send_edca_params(mac_ctx, session->gLimEdcaParamsActive, + session->vdev_id, false); + else + pe_err("Self entry missing in Hash Table"); + sir_copy_mac_addr(peer_macaddr, session->bssId); + /* if schedule is not present then add TSPEC with svcInterval as 0. */ + if (!addts.schedulePresent) + addts.schedule.svcInterval = 0; + if (QDF_STATUS_SUCCESS != + lim_tspec_add(mac_ctx, sta_ptr->staAddr, sta_ptr->assocId, + &addts.tspec, addts.schedule.svcInterval, &tspec_info)) { + pe_err("Adding entry in lim Tspec Table failed"); + lim_send_delts_req_action_frame(mac_ctx, peer_macaddr, rsp_reqd, + &addts.tspec.tsinfo, + &addts.tspec, session); + mac_ctx->lim.gLimAddtsSent = false; + return; + /* + * Error handling. send the response with error status. + * need to send DelTS to tear down the TSPEC status. + */ + } + if ((addts.tspec.tsinfo.traffic.accessPolicy != + SIR_MAC_ACCESSPOLICY_EDCA) || + ((upToAc(addts.tspec.tsinfo.traffic.userPrio) < QCA_WLAN_AC_ALL))) { +#ifdef FEATURE_WLAN_ESE + retval = lim_send_hal_msg_add_ts(mac_ctx, + tspec_info->idx, + addts.tspec, session->peSessionId, + addts.tsmIE.msmt_interval); +#else + retval = lim_send_hal_msg_add_ts(mac_ctx, + tspec_info->idx, + addts.tspec, session->peSessionId); +#endif + if (QDF_STATUS_SUCCESS != retval) { + lim_admit_control_delete_ts(mac_ctx, sta_ptr->assocId, + &addts.tspec.tsinfo, NULL, &tspec_info->idx); + + /* Send DELTS action frame to AP */ + cfg_len = sizeof(tSirMacAddr); + lim_send_delts_req_action_frame(mac_ctx, peer_macaddr, + rsp_reqd, &addts.tspec.tsinfo, + &addts.tspec, session); + lim_send_sme_addts_rsp(mac_ctx, true, retval, + session, addts.tspec, + session->smeSessionId); + mac_ctx->lim.gLimAddtsSent = false; + return; + } + pe_debug("AddTsRsp received successfully UP: %d TSID: %d", + addts.tspec.tsinfo.traffic.userPrio, + addts.tspec.tsinfo.traffic.tsid); + } else { + pe_debug("AddTsRsp received successfully UP: %d TSID: %d", + addts.tspec.tsinfo.traffic.userPrio, + addts.tspec.tsinfo.traffic.tsid); + pe_debug("no ACM: Bypass sending WMA_ADD_TS_REQ to HAL"); + lim_send_sme_addts_rsp(mac_ctx, true, eSIR_SME_SUCCESS, + session, addts.tspec, + session->smeSessionId); + } + /* clear the addts flag */ + mac_ctx->lim.gLimAddtsSent = false; + return; +} + +/** + * __lim_process_del_ts_req() - To process del ts response frame + * @mac_ctx: pointer to mac context + * @rx_pkt_info: Received packet info + * @session: pointer to session + * + * This routine is to handle del ts request frame + * + * Return: none + */ +static void __lim_process_del_ts_req(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, struct pe_session *session) +{ + QDF_STATUS retval; + struct delts_req_info delts; + tpSirMacMgmtHdr mac_hdr; + tpDphHashNode sta_ptr; + uint32_t frame_len; + uint16_t aid; + uint8_t *body_ptr; + uint8_t ts_status; + struct mac_ts_info *tsinfo; + uint8_t tspec_idx; + uint8_t ac; + tpDphHashNode sta_ds_ptr = NULL; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + sta_ptr = dph_lookup_hash_entry(mac_ctx, mac_hdr->sa, &aid, + &session->dph.dphHashTable); + if (!sta_ptr) { + pe_err("Station context not found - ignoring DelTs"); + return; + } + /* parse the delts request */ + retval = sir_convert_delts_req2_struct(mac_ctx, body_ptr, + frame_len, &delts); + if (retval != QDF_STATUS_SUCCESS) { + pe_err("DelTs parsing failed %d", retval); + return; + } + + if (delts.wmeTspecPresent) { + if ((!session->limWmeEnabled) || (!sta_ptr->wmeEnabled)) { + pe_warn("Ignore delts req: wme not enabled"); + return; + } + pe_debug("WME Delts received"); + } else if ((session->limQosEnabled) && sta_ptr->lleEnabled) { + pe_debug("11e QoS Delts received"); + } else if ((session->limWsmEnabled) && sta_ptr->wsmEnabled) { + pe_debug("WSM Delts received"); + } else { + pe_warn("Ignoring delts request: qos not enabled/capable"); + return; + } + + tsinfo = delts.wmeTspecPresent ? &delts.tspec.tsinfo : &delts.tsinfo; + + /* if no Admit Control, ignore the request */ + if (tsinfo->traffic.accessPolicy == SIR_MAC_ACCESSPOLICY_EDCA) { + if (upToAc(tsinfo->traffic.userPrio) >= QCA_WLAN_AC_ALL) { + pe_warn("DelTs with UP: %d has no AC - ignoring req", + tsinfo->traffic.userPrio); + return; + } + } + + if (!LIM_IS_AP_ROLE(session)) + lim_send_sme_delts_ind(mac_ctx, &delts, aid, session); + + /* try to delete the TS */ + if (QDF_STATUS_SUCCESS != + lim_admit_control_delete_ts(mac_ctx, sta_ptr->assocId, tsinfo, + &ts_status, &tspec_idx)) { + pe_warn("Unable to Delete TS"); + return; + } else if (!((tsinfo->traffic.accessPolicy == SIR_MAC_ACCESSPOLICY_HCCA) + || (tsinfo->traffic.accessPolicy == + SIR_MAC_ACCESSPOLICY_BOTH))){ + /* send message to HAL to delete TS */ + if (QDF_STATUS_SUCCESS != lim_send_hal_msg_del_ts(mac_ctx, + tspec_idx, + delts, session->peSessionId, + session->bssId)) { + pe_warn("DelTs with UP: %d failed ignoring request", + tsinfo->traffic.userPrio); + return; + } + } + /* + * We successfully deleted the TSPEC. Update the dynamic UAPSD Mask. + * The AC for this TSPEC is no longer trigger enabled if this Tspec + * was set-up in uplink direction only. + * The AC for this TSPEC is no longer delivery enabled if this Tspec + * was set-up in downlink direction only. + * The AC for this TSPEC is no longer triiger enabled and delivery + * enabled if this Tspec was a bidirectional TSPEC. + */ + lim_set_tspec_uapsd_mask_per_session(mac_ctx, session, + tsinfo, CLEAR_UAPSD_MASK); + /* + * We're deleting the TSPEC. + * The AC for this TSPEC is no longer admitted in uplink/downlink + * direction if this TSPEC was set-up in uplink/downlink direction only. + * The AC for this TSPEC is no longer admitted in both uplink and + * downlink directions if this TSPEC was a bi-directional TSPEC. + * If ACM is set for this AC and this AC is admitted only in downlink + * direction, PE needs to downgrade the EDCA parameter + * (for the AC for which TS is being deleted) to the + * next best AC for which ACM is not enabled, and send the + * updated values to HAL. + */ + ac = upToAc(tsinfo->traffic.userPrio); + if (tsinfo->traffic.direction == SIR_MAC_DIRECTION_UPLINK) { + session->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] &= + ~(1 << ac); + } else if (tsinfo->traffic.direction == + SIR_MAC_DIRECTION_DNLINK) { + session->gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK] &= + ~(1 << ac); + } else if (tsinfo->traffic.direction == SIR_MAC_DIRECTION_BIDIR) { + session->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] &= + ~(1 << ac); + session->gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK] &= + ~(1 << ac); + } + lim_set_active_edca_params(mac_ctx, session->gLimEdcaParams, + session); + sta_ds_ptr = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session->dph.dphHashTable); + if (sta_ds_ptr) + lim_send_edca_params(mac_ctx, session->gLimEdcaParamsActive, + session->vdev_id, false); + else + pe_err("Self entry missing in Hash Table"); + + pe_debug("DeleteTS succeeded"); +#ifdef FEATURE_WLAN_ESE + lim_send_sme_tsm_ie_ind(mac_ctx, session, 0, 0, 0); +#endif +} + +/** + * __lim_process_qos_map_configure_frame() - to process QoS map configure frame + * @mac_ctx: pointer to mac context + * @rx_pkt_info: pointer to received packet info + * @session: pointer to session + * + * This routine will called to process qos map configure frame + * + * Return: none + */ +static void __lim_process_qos_map_configure_frame(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, struct pe_session *session) +{ + tpSirMacMgmtHdr mac_hdr; + uint32_t frame_len; + uint8_t *body_ptr; + QDF_STATUS retval; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + retval = sir_convert_qos_map_configure_frame2_struct(mac_ctx, + body_ptr, frame_len, &session->QosMapSet); + if (retval != QDF_STATUS_SUCCESS) { + pe_err("QosMapConfigure frame parsing fail %d", retval); + return; + } + lim_send_sme_mgmt_frame_ind(mac_ctx, mac_hdr->fc.subType, + (uint8_t *)mac_hdr, + frame_len + sizeof(tSirMacMgmtHdr), 0, + WMA_GET_RX_FREQ(rx_pkt_info), session, + WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info), + RXMGMT_FLAG_NONE); +} + +#ifdef ANI_SUPPORT_11H +static void +__lim_process_basic_meas_req(struct mac_context *mac, + tpSirMacMeasReqActionFrame pMeasReqFrame, + tSirMacAddr peerMacAddr, struct pe_session *pe_session) +{ + if (lim_send_meas_report_frame(mac, pMeasReqFrame, + peerMacAddr, pe_session) != + QDF_STATUS_SUCCESS) { + pe_err("fail to send Basic Meas report"); + return; + } +} +static void +__lim_process_cca_meas_req(struct mac_context *mac, + tpSirMacMeasReqActionFrame pMeasReqFrame, + tSirMacAddr peerMacAddr, struct pe_session *pe_session) +{ + if (lim_send_meas_report_frame(mac, pMeasReqFrame, + peerMacAddr, pe_session) != + QDF_STATUS_SUCCESS) { + pe_err("fail to send CCA Meas report"); + return; + } +} +static void +__lim_process_rpi_meas_req(struct mac_context *mac, + tpSirMacMeasReqActionFrame pMeasReqFrame, + tSirMacAddr peerMacAddr, struct pe_session *pe_session) +{ + if (lim_send_meas_report_frame(mac, pMeasReqFrame, + peerMacAddr, pe_session) != + QDF_STATUS_SUCCESS) { + pe_err("fail to send RPI Meas report"); + return; + } +} +static void +__lim_process_measurement_request_frame(struct mac_context *mac, + uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + tpSirMacMgmtHdr pHdr; + uint8_t *pBody; + tpSirMacMeasReqActionFrame pMeasReqFrame; + uint32_t frameLen; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + frameLen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + + pMeasReqFrame = qdf_mem_malloc(sizeof(tSirMacMeasReqActionFrame)); + if (!pMeasReqFrame) + return; + + if (sir_convert_meas_req_frame2_struct(mac, pBody, pMeasReqFrame, frameLen) + != QDF_STATUS_SUCCESS) { + pe_warn("Rcv invalid Measurement Request Action Frame"); + return; + } + switch (pMeasReqFrame->measReqIE.measType) { + case SIR_MAC_BASIC_MEASUREMENT_TYPE: + __lim_process_basic_meas_req(mac, pMeasReqFrame, pHdr->sa, + pe_session); + break; + case SIR_MAC_CCA_MEASUREMENT_TYPE: + __lim_process_cca_meas_req(mac, pMeasReqFrame, pHdr->sa, + pe_session); + break; + case SIR_MAC_RPI_MEASUREMENT_TYPE: + __lim_process_rpi_meas_req(mac, pMeasReqFrame, pHdr->sa, + pe_session); + break; + default: + pe_warn("Unknown Measurement Type: %d", + pMeasReqFrame->measReqIE.measType); + break; + } +} /*** end limProcessMeasurementRequestFrame ***/ +static void +__lim_process_tpc_request_frame(struct mac_context *mac, uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + tpSirMacMgmtHdr pHdr; + uint8_t *pBody; + tpSirMacTpcReqActionFrame pTpcReqFrame; + uint32_t frameLen; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + frameLen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + pe_debug("****LIM: Processing TPC Request from peer ****"); + + pTpcReqFrame = qdf_mem_malloc(sizeof(tSirMacTpcReqActionFrame)); + if (!pTpcReqFrame) + return; + + if (sir_convert_tpc_req_frame2_struct(mac, pBody, pTpcReqFrame, frameLen) != + QDF_STATUS_SUCCESS) { + pe_warn("Rcv invalid TPC Req Action Frame"); + return; + } + if (lim_send_tpc_report_frame(mac, + pTpcReqFrame, + pHdr->sa, pe_session) != QDF_STATUS_SUCCESS) { + pe_err("fail to send TPC Report Frame"); + return; + } +} +#endif + +static void +__lim_process_sm_power_save_update(struct mac_context *mac, uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + + tpSirMacMgmtHdr pHdr; + tDot11fSMPowerSave frmSMPower; + tSirMacHTMIMOPowerSaveState state; + tpDphHashNode pSta; + uint16_t aid; + uint32_t frameLen, nStatus; + uint8_t *pBody; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + frameLen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + + pSta = + dph_lookup_hash_entry(mac, pHdr->sa, &aid, + &pe_session->dph.dphHashTable); + if (!pSta) { + pe_err("STA context not found - ignoring UpdateSM PSave Mode from"); + lim_print_mac_addr(mac, pHdr->sa, LOGE); + return; + } + + /**Unpack the received frame */ + nStatus = dot11f_unpack_sm_power_save(mac, pBody, frameLen, + &frmSMPower, false); + + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to unpack and parse a Update SM Power (0x%08x, %d bytes):", + nStatus, frameLen); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pBody, frameLen); + return; + } else if (DOT11F_WARNED(nStatus)) { + pe_debug("There were warnings while unpacking a SMPower Save update (0x%08x, %d bytes):", + nStatus, frameLen); + } + + pe_debug("Received SM Power save Mode update Frame with PS_Enable: %d" + "PS Mode: %d", frmSMPower.SMPowerModeSet.PowerSave_En, + frmSMPower.SMPowerModeSet.Mode); + + /** Update in the DPH Table about the Update in the SM Power Save mode*/ + if (frmSMPower.SMPowerModeSet.PowerSave_En + && frmSMPower.SMPowerModeSet.Mode) + state = eSIR_HT_MIMO_PS_DYNAMIC; + else if ((frmSMPower.SMPowerModeSet.PowerSave_En) + && (frmSMPower.SMPowerModeSet.Mode == 0)) + state = eSIR_HT_MIMO_PS_STATIC; + else if ((frmSMPower.SMPowerModeSet.PowerSave_En == 0) + && (frmSMPower.SMPowerModeSet.Mode == 0)) + state = eSIR_HT_MIMO_PS_NO_LIMIT; + else { + pe_warn("Received SM Power save Mode update Frame with invalid mode"); + return; + } + + if (state == pSta->htMIMOPSState) { + pe_err("The PEER is already set in the same mode"); + return; + } + + /** Update in the HAL Station Table for the Update of the Protection Mode */ + pSta->htMIMOPSState = state; + lim_post_sm_state_update(mac, pSta->htMIMOPSState, + pSta->staAddr, pe_session->smeSessionId); +} + + +static void +__lim_process_radio_measure_request(struct mac_context *mac, uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + tpSirMacMgmtHdr pHdr; + tDot11fRadioMeasurementRequest *frm; + uint32_t frameLen, nStatus; + uint8_t *pBody; + uint16_t curr_seq_num; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + frameLen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + + if (!pe_session) { + return; + } + + curr_seq_num = ((pHdr->seqControl.seqNumHi << + HIGH_SEQ_NUM_OFFSET) | + pHdr->seqControl.seqNumLo); + if (curr_seq_num == mac->rrm.rrmPEContext.prev_rrm_report_seq_num && + (mac->rrm.rrmPEContext.pCurrentReq[DEFAULT_RRM_IDX] || + mac->rrm.rrmPEContext.pCurrentReq[DEFAULT_RRM_IDX + 1])) { + pe_err("rrm report req frame, seq num: %d is already in progress, drop it", + curr_seq_num); + return; + } + /* Save seq no of currently processing rrm report req frame */ + mac->rrm.rrmPEContext.prev_rrm_report_seq_num = curr_seq_num; + lim_send_sme_mgmt_frame_ind(mac, pHdr->fc.subType, (uint8_t *)pHdr, + frameLen + sizeof(tSirMacMgmtHdr), 0, + WMA_GET_RX_FREQ(pRxPacketInfo), pe_session, + WMA_GET_RX_RSSI_NORMALIZED(pRxPacketInfo), + RXMGMT_FLAG_NONE); + + frm = qdf_mem_malloc(sizeof(*frm)); + if (!frm) + return; + + /**Unpack the received frame */ + nStatus = dot11f_unpack_radio_measurement_request(mac, pBody, + frameLen, frm, false); + + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to unpack and parse a Radio Measure request (0x%08x, %d bytes):", + nStatus, frameLen); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pBody, frameLen); + goto err; + } else if (DOT11F_WARNED(nStatus)) { + pe_debug("Warnings while unpacking a Radio Measure request (0x%08x, %d bytes):", + nStatus, frameLen); + } + /* Call rrm function to handle the request. */ + + rrm_process_radio_measurement_request(mac, pHdr->sa, frm, + pe_session); +err: + qdf_mem_free(frm); +} + +static QDF_STATUS +__lim_process_link_measurement_req(struct mac_context *mac, uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + tpSirMacMgmtHdr pHdr; + tDot11fLinkMeasurementRequest frm; + uint32_t frameLen, nStatus; + uint8_t *pBody; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + frameLen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + + if (!pe_session) { + return QDF_STATUS_E_FAILURE; + } + + /**Unpack the received frame */ + nStatus = + dot11f_unpack_link_measurement_request(mac, pBody, frameLen, + &frm, false); + + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to unpack and parse a Link Measure request (0x%08x, %d bytes):", + nStatus, frameLen); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pBody, frameLen); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(nStatus)) { + pe_debug("There were warnings while unpacking a Link Measure request (0x%08x, %d bytes):", + nStatus, frameLen); + } + /* Call rrm function to handle the request. */ + + return rrm_process_link_measurement_request(mac, pRxPacketInfo, &frm, + pe_session); + +} + +static void +__lim_process_neighbor_report(struct mac_context *mac, uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + tpSirMacMgmtHdr pHdr; + tDot11fNeighborReportResponse *pFrm; + uint32_t frameLen, nStatus; + uint8_t *pBody; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + frameLen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + + pFrm = qdf_mem_malloc(sizeof(tDot11fNeighborReportResponse)); + if (!pFrm) + return; + + if (!pe_session) { + qdf_mem_free(pFrm); + return; + } + + /**Unpack the received frame */ + nStatus = + dot11f_unpack_neighbor_report_response(mac, pBody, + frameLen, pFrm, false); + + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to unpack and parse a Neighbor report response (0x%08x, %d bytes):", + nStatus, frameLen); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pBody, frameLen); + qdf_mem_free(pFrm); + return; + } else if (DOT11F_WARNED(nStatus)) { + pe_debug("There were warnings while unpacking a Neighbor report response (0x%08x, %d bytes):", + nStatus, frameLen); + } + + /* Call rrm function to handle the request. */ + rrm_process_neighbor_report_response(mac, pFrm, pe_session); + + qdf_mem_free(pFrm); +} + + +#ifdef WLAN_FEATURE_11W +/** + * limProcessSAQueryRequestActionFrame + * + ***FUNCTION: + * This function is called by lim_process_action_frame() upon + * SA query request Action frame reception. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param *pRxPacketInfo - Handle to the Rx packet info + * @param pe_session - PE session entry + * + * @return None + */ +static void __lim_process_sa_query_request_action_frame(struct mac_context *mac, + uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + tpSirMacMgmtHdr pHdr; + uint8_t *pBody; + uint32_t frame_len; + uint8_t transId[2]; + + /* Prima --- Below Macro not available in prima + pHdr = SIR_MAC_BD_TO_MPDUHEADER(pBd); + pBody = SIR_MAC_BD_TO_MPDUDATA(pBd); */ + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + frame_len = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + + if (frame_len < sizeof(struct sDot11fSaQueryReq)) { + pe_err("Invalid frame length"); + return; + } + /* If this is an unprotected SA Query Request, then ignore it. */ + if (pHdr->fc.wep == 0) + return; + + /* 11w offload is enabled then firmware should not fwd this frame */ + if (LIM_IS_STA_ROLE(pe_session) && mac->pmf_offload) { + pe_err("11w offload enabled, SA Query req isn't expected"); + return; + } + + /*Extract 11w trsansId from SA query request action frame + In SA query response action frame we will send same transId + In SA query request action frame: + Category : 1 byte + Action : 1 byte + Transaction ID : 2 bytes */ + qdf_mem_copy(&transId[0], &pBody[2], 2); + + /* Send 11w SA query response action frame */ + if (lim_send_sa_query_response_frame(mac, + transId, + pHdr->sa, + pe_session) != QDF_STATUS_SUCCESS) { + pe_err("fail to send SA query response action frame"); + return; + } +} + +/** + * __lim_process_sa_query_response_action_frame + * + ***FUNCTION: + * This function is called by lim_process_action_frame() upon + * SA query response Action frame reception. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param *pRxPacketInfo - Handle to the Rx packet info + * @param pe_session - PE session entry + * @return None + */ +static void __lim_process_sa_query_response_action_frame(struct mac_context *mac, + uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + tpSirMacMgmtHdr pHdr; + uint32_t frame_len; + uint8_t *pBody; + tpDphHashNode pSta; + uint16_t aid; + uint16_t transId; + uint8_t retryNum; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + frame_len = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + pe_debug("SA Query Response received"); + + if (frame_len < sizeof(struct sDot11fSaQueryRsp)) { + pe_err("Invalid frame length"); + return; + } + /* When a station, supplicant handles SA Query Response. + * Forward to SME to HDD to wpa_supplicant. + */ + if (LIM_IS_STA_ROLE(pe_session)) { + lim_send_sme_mgmt_frame_ind(mac, pHdr->fc.subType, + (uint8_t *)pHdr, + frame_len + sizeof(tSirMacMgmtHdr), + 0, + WMA_GET_RX_FREQ(pRxPacketInfo), + pe_session, + WMA_GET_RX_RSSI_NORMALIZED( + pRxPacketInfo), RXMGMT_FLAG_NONE); + return; + } + + /* If this is an unprotected SA Query Response, then ignore it. */ + if (pHdr->fc.wep == 0) + return; + + pSta = + dph_lookup_hash_entry(mac, pHdr->sa, &aid, + &pe_session->dph.dphHashTable); + if (!pSta) + return; + + pe_debug("SA Query Response source addr: %0x:%0x:%0x:%0x:%0x:%0x", + pHdr->sa[0], pHdr->sa[1], pHdr->sa[2], pHdr->sa[3], + pHdr->sa[4], pHdr->sa[5]); + pe_debug("SA Query state for station: %d", pSta->pmfSaQueryState); + + if (DPH_SA_QUERY_IN_PROGRESS != pSta->pmfSaQueryState) + return; + + /* Extract 11w trsansId from SA query response action frame + In SA query response action frame: + Category : 1 byte + Action : 1 byte + Transaction ID : 2 bytes */ + qdf_mem_copy(&transId, &pBody[2], 2); + + /* If SA Query is in progress with the station and the station + responds then the association request that triggered the SA + query is from a rogue station, just go back to initial state. */ + for (retryNum = 0; retryNum <= pSta->pmfSaQueryRetryCount; retryNum++) + if (transId == pSta->pmfSaQueryStartTransId + retryNum) { + pe_debug("Found matching SA Query Request - transaction ID: %d", + transId); + tx_timer_deactivate(&pSta->pmfSaQueryTimer); + pSta->pmfSaQueryState = DPH_SA_QUERY_NOT_IN_PROGRESS; + break; + } +} +#endif + +#ifdef WLAN_FEATURE_11W +/** + * lim_drop_unprotected_action_frame + * + ***FUNCTION: + * This function checks if an Action frame should be dropped since it is + * a Robust Management Frame, it is unprotected, and it is received on a + * connection where PMF is enabled. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Global MAC structure + * @param pe_session - PE session entry + * @param pHdr - Frame header + * @param category - Action frame category + * @return true if frame should be dropped + */ + +static bool +lim_drop_unprotected_action_frame(struct mac_context *mac, struct pe_session *pe_session, + tpSirMacMgmtHdr pHdr, uint8_t category) +{ + uint16_t aid; + tpDphHashNode sta; + bool rmfConnection = false; + + sta = dph_lookup_hash_entry(mac, pHdr->sa, &aid, + &pe_session->dph.dphHashTable); + if (sta && sta->rmfEnabled) + rmfConnection = true; + + if (rmfConnection && (pHdr->fc.wep == 0)) { + pe_err("Dropping unprotected Action category: %d frame since RMF is enabled", + category); + return true; + } + + return false; +} +#endif + +/** + * lim_process_addba_req() - process ADDBA Request + * @mac_ctx: Pointer to Global MAC structure + * @rx_pkt_info: A pointer to packet info structure + * @session: PE session pointer + * + * This routine will be called to process ADDBA request action frame + * + * Return: None + */ +static void lim_process_addba_req(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + struct pe_session *session) +{ + tpSirMacMgmtHdr mac_hdr; + uint8_t *body_ptr; + tDot11faddba_req *addba_req; + uint32_t frame_len, status; + QDF_STATUS qdf_status; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + tpDphHashNode sta_ds; + uint16_t aid, buff_size; + bool he_cap = false; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + QDF_TRACE_HEX_DUMP_DEBUG_RL(QDF_MODULE_ID_PE, body_ptr, frame_len); + + addba_req = qdf_mem_malloc(sizeof(*addba_req)); + if (!addba_req) + return; + + /* Unpack ADDBA request frame */ + status = dot11f_unpack_addba_req(mac_ctx, body_ptr, frame_len, + addba_req, false); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to unpack and parse (0x%08x, %d bytes)", + status, frame_len); + goto error; + } else if (DOT11F_WARNED(status)) { + pe_warn("warning: unpack addba Req(0x%08x, %d bytes)", + status, frame_len); + } + + sta_ds = dph_lookup_hash_entry(mac_ctx, mac_hdr->sa, &aid, + &session->dph.dphHashTable); + if (sta_ds && lim_is_session_he_capable(session)) + he_cap = lim_is_sta_he_capable(sta_ds); + + if (he_cap) + buff_size = MAX_BA_BUFF_SIZE; + else + buff_size = SIR_MAC_BA_DEFAULT_BUFF_SIZE; + + if (mac_ctx->usr_cfg_ba_buff_size) + buff_size = mac_ctx->usr_cfg_ba_buff_size; + + if (addba_req->addba_param_set.buff_size) + buff_size = QDF_MIN(buff_size, + addba_req->addba_param_set.buff_size); + + pe_debug("token %d tid %d timeout %d buff_size in frame %d buf_size calculated %d ssn %d", + addba_req->DialogToken.token, addba_req->addba_param_set.tid, + addba_req->ba_timeout.timeout, + addba_req->addba_param_set.buff_size, buff_size, + addba_req->ba_start_seq_ctrl.ssn); + + qdf_status = cdp_addba_requestprocess( + soc, mac_hdr->sa, + session->vdev_id, + addba_req->DialogToken.token, + addba_req->addba_param_set.tid, + addba_req->ba_timeout.timeout, + buff_size, + addba_req->ba_start_seq_ctrl.ssn); + + if (QDF_STATUS_SUCCESS == qdf_status) { + qdf_status = lim_send_addba_response_frame(mac_ctx, + mac_hdr->sa, + addba_req->addba_param_set.tid, + session, + addba_req->addba_extn_element.present, + addba_req->addba_param_set.amsdu_supp, + mac_hdr->fc.wep); + if (qdf_status != QDF_STATUS_SUCCESS) { + pe_err("Failed to send addba response frame"); + cdp_addba_resp_tx_completion( + soc, mac_hdr->sa, session->vdev_id, + addba_req->addba_param_set.tid, + WMI_MGMT_TX_COMP_TYPE_DISCARD); + } + } else { + pe_err_rl("Failed to process addba request"); + } + +error: + qdf_mem_free(addba_req); + return; +} + +/** + * lim_process_delba_req() - process DELBA Request + * @mac_ctx: Pointer to Global MAC structure + * @rx_pkt_info: A pointer to packet info structure + * @session: PE session pointer + * + * This routine will be called to process ADDBA request action frame + * + * Return: None + */ +static void lim_process_delba_req(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + struct pe_session *session) +{ + tpSirMacMgmtHdr mac_hdr; + uint8_t *body_ptr; + tDot11fdelba_req *delba_req; + uint32_t frame_len, status; + QDF_STATUS qdf_status; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_INFO, + body_ptr, frame_len); + + delba_req = qdf_mem_malloc(sizeof(*delba_req)); + if (!delba_req) + return; + + /* Unpack DELBA request frame */ + status = dot11f_unpack_delba_req(mac_ctx, body_ptr, frame_len, + delba_req, false); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to unpack and parse (0x%08x, %d bytes)", + status, frame_len); + goto error; + } else if (DOT11F_WARNED(status)) { + pe_warn("warning: unpack addba Req(0x%08x, %d bytes)", + status, frame_len); + } + + qdf_status = cdp_delba_process(soc, mac_hdr->sa, session->vdev_id, + delba_req->delba_param_set.tid, delba_req->Reason.code); + + if (QDF_STATUS_SUCCESS != qdf_status) + pe_err("Failed to process delba request"); + +error: + qdf_mem_free(delba_req); + return; +} + +/** + * lim_process_action_frame() - to process action frames + * @mac_ctx: Pointer to Global MAC structure + * @rx_pkt_info: A pointer to packet info structure + * + * This function is called by limProcessMessageQueue() upon + * Action frame reception. + * + * Return: none + */ + +void lim_process_action_frame(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, struct pe_session *session) +{ + uint8_t *body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + tpSirMacActionFrameHdr action_hdr = (tpSirMacActionFrameHdr) body_ptr; +#ifdef WLAN_FEATURE_11W + tpSirMacMgmtHdr mac_hdr_11w = WMA_GET_RX_MAC_HEADER(rx_pkt_info); +#endif + tpSirMacMgmtHdr mac_hdr = NULL; + int8_t rssi; + uint32_t frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + tpSirMacVendorSpecificFrameHdr vendor_specific; + uint8_t oui[] = { 0x00, 0x00, 0xf0 }; + uint8_t dpp_oui[] = { 0x50, 0x6F, 0x9A, 0x1A }; + tpSirMacVendorSpecificPublicActionFrameHdr pub_action; + + if (frame_len < sizeof(*action_hdr)) { + pe_debug("frame_len %d less than Action Frame Hdr size", + frame_len); + return; + } + +#ifdef WLAN_FEATURE_11W + if (lim_is_robust_mgmt_action_frame(action_hdr->category) && + lim_drop_unprotected_action_frame(mac_ctx, session, + mac_hdr_11w, action_hdr->category)) + return; +#endif + + switch (action_hdr->category) { + case ACTION_CATEGORY_QOS: + if ((session->limQosEnabled) || + (action_hdr->actionID == QOS_MAP_CONFIGURE)) { + switch (action_hdr->actionID) { + case QOS_ADD_TS_REQ: + __lim_process_add_ts_req(mac_ctx, + (uint8_t *) rx_pkt_info, + session); + break; + + case QOS_ADD_TS_RSP: + __lim_process_add_ts_rsp(mac_ctx, + (uint8_t *) rx_pkt_info, + session); + break; + + case QOS_DEL_TS_REQ: + __lim_process_del_ts_req(mac_ctx, + (uint8_t *) rx_pkt_info, + session); + break; + + case QOS_MAP_CONFIGURE: + __lim_process_qos_map_configure_frame(mac_ctx, + (uint8_t *)rx_pkt_info, + session); + break; + default: + pe_warn("Qos action: %d not handled", + action_hdr->actionID); + break; + } + break; + } + break; + + case ACTION_CATEGORY_SPECTRUM_MGMT: + switch (action_hdr->actionID) { +#ifdef ANI_SUPPORT_11H + case ACTION_SPCT_MSR_REQ: + if (session->lim11hEnable) + __lim_process_measurement_request_frame(mac_ctx, + rx_pkt_info, + session); + break; + case ACTION_SPCT_TPC_REQ: + if ((LIM_IS_STA_ROLE(session) || + LIM_IS_AP_ROLE(session)) && + session->lim11hEnable) + __lim_process_tpc_request_frame(mac_ctx, + rx_pkt_info, session); + break; +#endif + case ACTION_SPCT_CHL_SWITCH: + if (LIM_IS_STA_ROLE(session)) + __lim_process_channel_switch_action_frame( + mac_ctx, rx_pkt_info, session); + break; + default: + pe_warn("Spectrum mgmt action id: %d not handled", + action_hdr->actionID); + break; + } + break; + + case ACTION_CATEGORY_WMM: + if (!session->limWmeEnabled) { + pe_warn("WME mode disabled - dropping frame: %d", + action_hdr->actionID); + break; + } + switch (action_hdr->actionID) { + case QOS_ADD_TS_REQ: + __lim_process_add_ts_req(mac_ctx, + (uint8_t *) rx_pkt_info, session); + break; + + case QOS_ADD_TS_RSP: + __lim_process_add_ts_rsp(mac_ctx, + (uint8_t *) rx_pkt_info, session); + break; + + case QOS_DEL_TS_REQ: + __lim_process_del_ts_req(mac_ctx, + (uint8_t *) rx_pkt_info, session); + break; + + case QOS_MAP_CONFIGURE: + __lim_process_qos_map_configure_frame(mac_ctx, + (uint8_t *)rx_pkt_info, session); + break; + + default: + pe_warn("WME action: %d not handled", + action_hdr->actionID); + break; + } + break; + + case ACTION_CATEGORY_HT: + /** Type of HT Action to be performed*/ + switch (action_hdr->actionID) { + case SIR_MAC_SM_POWER_SAVE: + if (LIM_IS_AP_ROLE(session)) + __lim_process_sm_power_save_update(mac_ctx, + (uint8_t *)rx_pkt_info, + session); + break; + default: + pe_warn("Action ID: %d not handled in HT category", + action_hdr->actionID); + break; + } + break; + + case ACTION_CATEGORY_WNM: + pe_debug("WNM Action category: %d action: %d", + action_hdr->category, action_hdr->actionID); + switch (action_hdr->actionID) { + case WNM_BSS_TM_QUERY: + case WNM_BSS_TM_REQUEST: + case WNM_BSS_TM_RESPONSE: + if (cfg_p2p_is_roam_config_disabled(mac_ctx->psoc) && + session && LIM_IS_STA_ROLE(session) && + (policy_mgr_mode_specific_connection_count( + mac_ctx->psoc, PM_P2P_CLIENT_MODE, NULL) || + policy_mgr_mode_specific_connection_count( + mac_ctx->psoc, PM_P2P_GO_MODE, NULL))) { + pe_debug("p2p session active drop BTM frame"); + break; + } + case WNM_NOTIF_REQUEST: + case WNM_NOTIF_RESPONSE: + rssi = WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info); + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + /* Forward to the SME to HDD to wpa_supplicant */ + lim_send_sme_mgmt_frame_ind(mac_ctx, + mac_hdr->fc.subType, + (uint8_t *) mac_hdr, + frame_len + sizeof(tSirMacMgmtHdr), + session->smeSessionId, + WMA_GET_RX_FREQ(rx_pkt_info), + session, rssi, RXMGMT_FLAG_NONE); + break; + default: + pe_debug("Action ID: %d not handled in WNM category", + action_hdr->actionID); + break; + } + break; + + case ACTION_CATEGORY_RRM: + /* Ignore RRM measurement request until DHCP is set */ + if (mac_ctx->rrm.rrmPEContext.rrmEnable && + mac_ctx->roam.roamSession + [session->smeSessionId].dhcp_done) { + switch (action_hdr->actionID) { + case RRM_RADIO_MEASURE_REQ: + __lim_process_radio_measure_request(mac_ctx, + (uint8_t *)rx_pkt_info, + session); + break; + case RRM_LINK_MEASUREMENT_REQ: + if (!lim_is_valid_frame( + &rrm_link_action_frm, + rx_pkt_info)) + break; + + if (__lim_process_link_measurement_req( + mac_ctx, + (uint8_t *)rx_pkt_info, + session) == QDF_STATUS_SUCCESS) + lim_update_last_processed_frame( + &rrm_link_action_frm, + rx_pkt_info); + + break; + case RRM_NEIGHBOR_RPT: + __lim_process_neighbor_report(mac_ctx, + (uint8_t *)rx_pkt_info, + session); + break; + default: + pe_warn("Action ID: %d not handled in RRM", + action_hdr->actionID); + break; + + } + } else { + /* Else we will just ignore the RRM messages. */ + pe_debug("RRM frm ignored, it is disabled in cfg: %d or DHCP not completed: %d", + mac_ctx->rrm.rrmPEContext.rrmEnable, + mac_ctx->roam.roamSession + [session->smeSessionId].dhcp_done); + } + break; + + case SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY: + vendor_specific = (tpSirMacVendorSpecificFrameHdr) action_hdr; + mac_hdr = NULL; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + + if (frame_len < sizeof(*vendor_specific)) { + pe_debug("frame len %d less than Vendor Specific Hdr len", + frame_len); + return; + } + + /* Check if it is a vendor specific action frame. */ + if (LIM_IS_STA_ROLE(session) && + (!qdf_mem_cmp(session->self_mac_addr, + &mac_hdr->da[0], sizeof(tSirMacAddr))) + && IS_WES_MODE_ENABLED(mac_ctx) + && !qdf_mem_cmp(vendor_specific->Oui, oui, 3)) { + pe_debug("Rcvd Vendor specific frame OUI: %x %x %x", + vendor_specific->Oui[0], + vendor_specific->Oui[1], + vendor_specific->Oui[2]); + /* + * Forward to the SME to HDD to wpa_supplicant + * type is ACTION + */ + lim_send_sme_mgmt_frame_ind(mac_ctx, + mac_hdr->fc.subType, + (uint8_t *) mac_hdr, + frame_len + + sizeof(tSirMacMgmtHdr), + session->smeSessionId, + WMA_GET_RX_FREQ(rx_pkt_info), + session, + WMA_GET_RX_RSSI_NORMALIZED( + rx_pkt_info), RXMGMT_FLAG_NONE); + } else { + pe_debug("Dropping the vendor specific action frame" + "beacause of (WES Mode not enabled " + "(WESMODE: %d) or OUI mismatch " + "(%02x %02x %02x) or not received with" + "SelfSta address) system role: %d", + IS_WES_MODE_ENABLED(mac_ctx), + vendor_specific->Oui[0], + vendor_specific->Oui[1], + vendor_specific->Oui[2], + GET_LIM_SYSTEM_ROLE(session)); + } + break; + case ACTION_CATEGORY_PUBLIC: + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + + switch (action_hdr->actionID) { + case SIR_MAC_ACTION_EXT_CHANNEL_SWITCH_ID: + lim_process_ext_channel_switch_action_frame(mac_ctx, + rx_pkt_info, session); + break; + case SIR_MAC_ACTION_VENDOR_SPECIFIC: + pub_action = + (tpSirMacVendorSpecificPublicActionFrameHdr) + action_hdr; + if (frame_len < sizeof(*pub_action)) { + pe_debug("Received vendor specific public action frame of invalid len %d", + frame_len); + return; + } + /* + * Check if it is a DPP public action frame and fall + * thru, else drop the frame. + */ + if (qdf_mem_cmp(pub_action->Oui, dpp_oui, 4)) { + pe_debug("Unhandled public action frame (Vendor specific) OUI: %x %x %x %x", + pub_action->Oui[0], pub_action->Oui[1], + pub_action->Oui[2], pub_action->Oui[3]); + break; + } + /* Fall through to send the frame to supplicant */ + case SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY: + case SIR_MAC_ACTION_2040_BSS_COEXISTENCE: + case SIR_MAC_ACTION_GAS_INITIAL_REQUEST: + case SIR_MAC_ACTION_GAS_INITIAL_RESPONSE: + case SIR_MAC_ACTION_GAS_COMEBACK_REQUEST: + case SIR_MAC_ACTION_GAS_COMEBACK_RESPONSE: + /* + * Forward to the SME to HDD to wpa_supplicant + * type is ACTION + */ + lim_send_sme_mgmt_frame_ind(mac_ctx, + mac_hdr->fc.subType, + (uint8_t *) mac_hdr, + frame_len + sizeof(tSirMacMgmtHdr), + session->smeSessionId, + WMA_GET_RX_FREQ(rx_pkt_info), session, + WMA_GET_RX_RSSI_NORMALIZED( + rx_pkt_info), RXMGMT_FLAG_NONE); + break; + default: + pe_debug("Unhandled public action frame: %d", + action_hdr->actionID); + break; + } + break; + +#ifdef WLAN_FEATURE_11W + case ACTION_CATEGORY_SA_QUERY: + pe_debug("SA Query Action category: %d action: %d", + action_hdr->category, action_hdr->actionID); + switch (action_hdr->actionID) { + case SA_QUERY_REQUEST: + /**11w SA query request action frame received**/ + /* Respond directly to the incoming request in LIM */ + __lim_process_sa_query_request_action_frame(mac_ctx, + (uint8_t *)rx_pkt_info, + session); + break; + case SA_QUERY_RESPONSE: + /**11w SA query response action frame received**/ + /* Handle based on the current SA Query state */ + __lim_process_sa_query_response_action_frame(mac_ctx, + (uint8_t *)rx_pkt_info, + session); + break; + default: + break; + } + break; +#endif + case ACTION_CATEGORY_VHT: + if (!session->vhtCapability) + break; + switch (action_hdr->actionID) { + case SIR_MAC_VHT_OPMODE_NOTIFICATION: + __lim_process_operating_mode_action_frame(mac_ctx, + rx_pkt_info, session); + break; + case SIR_MAC_VHT_GID_NOTIFICATION: + /* Only if ini supports it */ + if (session->enableVhtGid) + __lim_process_gid_management_action_frame( + mac_ctx, rx_pkt_info, session); + break; + default: + break; + } + break; + case ACTION_CATEGORY_FST: { + tpSirMacMgmtHdr hdr; + + hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + + pe_debug("Received FST MGMT action frame"); + /* Forward to the SME to HDD */ + lim_send_sme_mgmt_frame_ind(mac_ctx, hdr->fc.subType, + (uint8_t *)hdr, + frame_len + sizeof(tSirMacMgmtHdr), + session->smeSessionId, + WMA_GET_RX_FREQ(rx_pkt_info), + session, + WMA_GET_RX_RSSI_NORMALIZED( + rx_pkt_info), RXMGMT_FLAG_NONE); + break; + } + case ACTION_CATEGORY_PROTECTED_DUAL_OF_PUBLIC_ACTION: + pe_debug("Rcvd Protected Dual of Public Action: %d", + action_hdr->actionID); + switch (action_hdr->actionID) { + case SIR_MAC_PDPA_GAS_INIT_REQ: + case SIR_MAC_PDPA_GAS_INIT_RSP: + case SIR_MAC_PDPA_GAS_COMEBACK_REQ: + case SIR_MAC_PDPA_GAS_COMEBACK_RSP: + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + rssi = WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info); + lim_send_sme_mgmt_frame_ind(mac_ctx, + mac_hdr->fc.subType, (uint8_t *) mac_hdr, + frame_len + sizeof(tSirMacMgmtHdr), + session->smeSessionId, + WMA_GET_RX_FREQ(rx_pkt_info), session, rssi, + RXMGMT_FLAG_NONE); + break; + default: + pe_debug("Unhandled - Protected Dual Public Action"); + break; + } + break; + case ACTION_CATEGORY_BACK: + pe_debug("Rcvd Block Ack for "QDF_MAC_ADDR_FMT"; action: %d", + QDF_MAC_ADDR_REF(session->self_mac_addr), + action_hdr->actionID); + switch (action_hdr->actionID) { + case ADDBA_REQUEST: + lim_process_addba_req(mac_ctx, rx_pkt_info, session); + break; + case DELBA: + lim_process_delba_req(mac_ctx, rx_pkt_info, session); + break; + default: + pe_err("Unhandle BA action frame"); + break; + } + break; + default: + pe_warn("Action category: %d not handled", + action_hdr->category); + break; + } +} + +/** + * lim_process_action_frame_no_session + * + ***FUNCTION: + * This function is called by limProcessMessageQueue() upon + * Action frame reception and no session. + * Currently only public action frames can be received from + * a non-associated station. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param *pBd - A pointer to Buffer descriptor + associated PDUs + * @return None + */ +void lim_process_action_frame_no_session(struct mac_context *mac, uint8_t *pBd) +{ + tpSirMacMgmtHdr mac_hdr = WMA_GET_RX_MAC_HEADER(pBd); + uint32_t frame_len = WMA_GET_RX_PAYLOAD_LEN(pBd); + uint8_t *pBody = WMA_GET_RX_MPDU_DATA(pBd); + uint8_t dpp_oui[] = { 0x50, 0x6F, 0x9A, 0x1A }; + tpSirMacActionFrameHdr action_hdr = (tpSirMacActionFrameHdr) pBody; + tpSirMacVendorSpecificPublicActionFrameHdr vendor_specific; + + pe_debug("Received an Action frame -- no session"); + + if (frame_len < sizeof(*action_hdr)) { + pe_debug("frame_len %d less than action frame header len", + frame_len); + return; + } + + switch (action_hdr->category) { + case ACTION_CATEGORY_PUBLIC: + switch (action_hdr->actionID) { + case SIR_MAC_ACTION_VENDOR_SPECIFIC: + vendor_specific = + (tpSirMacVendorSpecificPublicActionFrameHdr) + action_hdr; + + if (frame_len < sizeof(*vendor_specific)) { + pe_debug("Received vendor specific public action frame of invalid len %d", + frame_len); + return; + } + /* + * Check if it is a DPP public action frame and fall + * thru, else drop the frame. + */ + if (qdf_mem_cmp(vendor_specific->Oui, dpp_oui, 4)) { + pe_debug("Unhandled public action frame (Vendor specific) OUI: %x %x %x %x", + vendor_specific->Oui[0], + vendor_specific->Oui[1], + vendor_specific->Oui[2], + vendor_specific->Oui[3]); + break; + } + /* Fall through to send the frame to supplicant */ + case SIR_MAC_ACTION_GAS_INITIAL_REQUEST: + case SIR_MAC_ACTION_GAS_INITIAL_RESPONSE: + case SIR_MAC_ACTION_GAS_COMEBACK_REQUEST: + case SIR_MAC_ACTION_GAS_COMEBACK_RESPONSE: + /* + * Forward the GAS frames to wpa_supplicant + * type is ACTION + */ + lim_send_sme_mgmt_frame_ind(mac, + mac_hdr->fc.subType, + (uint8_t *) mac_hdr, + frame_len + sizeof(tSirMacMgmtHdr), 0, + WMA_GET_RX_FREQ(pBd), NULL, + WMA_GET_RX_RSSI_NORMALIZED(pBd), + RXMGMT_FLAG_NONE); + break; + default: + pe_warn("Unhandled public action frame: %x", + action_hdr->actionID); + break; + } + break; + default: + pe_warn("Unhandled action frame without session: %x", + action_hdr->category); + break; + + } +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_assoc_req_frame.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_assoc_req_frame.c new file mode 100644 index 0000000000000000000000000000000000000000..111f095243c5d5aad50a5259a525de23eb8a55a1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_assoc_req_frame.c @@ -0,0 +1,3021 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_process_assoc_req_frame.c contains the code + * for processing Re/Association Request Frame. + */ +#include "cds_api.h" +#include "ani_global.h" +#include "wni_cfg.h" +#include "sir_api.h" +#include "cfg_ucfg_api.h" + +#include "sch_api.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_admit_control.h" +#include "cds_packet.h" +#include "lim_session_utils.h" +#include "utils_parser.h" +#include "wlan_p2p_api.h" + +#include "qdf_types.h" +#include "cds_utils.h" +#include "wlan_utility.h" +#include "wlan_crypto_global_api.h" + +/** + * lim_convert_supported_channels - Parses channel support IE + * @mac_ctx: A pointer to Global MAC structure + * @assoc_ind: A pointer to SME ASSOC/REASSOC IND + * @assoc_req: A pointer to ASSOC/REASSOC Request frame + * + * This function is called by lim_process_assoc_req_frame() to + * parse the channel support IE in the Assoc/Reassoc Request + * frame, and send relevant information in the SME_ASSOC_IND + * + * Return: None + */ +static void lim_convert_supported_channels(struct mac_context *mac_ctx, + tpLimMlmAssocInd assoc_ind, + tSirAssocReq *assoc_req) +{ + uint16_t i, j, index = 0; + uint8_t first_ch_no; + uint8_t chn_count; + uint8_t next_ch_no; + uint8_t channel_offset = 0; + uint32_t chan_freq; + + if (assoc_req->supportedChannels.length >= + SIR_MAX_SUPPORTED_CHANNEL_LIST) { + pe_err("Number of supported channels: %d is more than MAX", + assoc_req->supportedChannels.length); + assoc_ind->supportedChannels.numChnl = 0; + return; + } + + for (i = 0; i < (assoc_req->supportedChannels.length); i++) { + /* Get First Channel Number */ + first_ch_no = assoc_req->supportedChannels.supportedChannels[i]; + assoc_ind->supportedChannels.channelList[index] = first_ch_no; + i++; + index++; + + /* Get Number of Channels in a Subband */ + chn_count = assoc_req->supportedChannels.supportedChannels[i]; + pe_debug("Rcv assoc_req: chnl: %d numOfChnl: %d", + first_ch_no, chn_count); + if (index >= SIR_MAX_SUPPORTED_CHANNEL_LIST) { + pe_warn("Ch count > max supported: %d", chn_count); + assoc_ind->supportedChannels.numChnl = 0; + return; + } + if (chn_count <= 1) + continue; + next_ch_no = first_ch_no; + chan_freq = wlan_reg_legacy_chan_to_freq(mac_ctx->pdev, + first_ch_no); + + if (REG_BAND_5G == lim_get_rf_band(chan_freq)) + channel_offset = SIR_11A_FREQUENCY_OFFSET; + else if (REG_BAND_2G == lim_get_rf_band(chan_freq)) + channel_offset = SIR_11B_FREQUENCY_OFFSET; + else + continue; + + for (j = 1; j < chn_count; j++) { + next_ch_no += channel_offset; + assoc_ind->supportedChannels.channelList[index] + = next_ch_no; + index++; + if (index >= SIR_MAX_SUPPORTED_CHANNEL_LIST) { + pe_warn("Ch count > supported: %d", chn_count); + assoc_ind->supportedChannels.numChnl = 0; + return; + } + } + } + + assoc_ind->supportedChannels.numChnl = (uint8_t) index; + pe_debug("Send AssocInd to WSM: minPwr: %d maxPwr: %d numChnl: %d", + assoc_ind->powerCap.minTxPower, + assoc_ind->powerCap.maxTxPower, + assoc_ind->supportedChannels.numChnl); +} + +/** + * lim_check_sta_in_pe_entries() - checks if sta exists in any dph tables. + * @mac_ctx: Pointer to Global MAC structure + * @hdr: A pointer to the MAC header + * @sessionid - session id for which session is initiated + * @dup_entry: pointer for duplicate entry found + * + * This function is called by lim_process_assoc_req_frame() to check if STA + * entry already exists in any of the PE entries of the AP. If it exists, deauth + * will be sent on that session and the STA deletion will happen. After this, + * the ASSOC request will be processed. If the STA is already in deleting phase + * this will return failure so that assoc req will be rejected till STA is + * deleted. + * + * Return: QDF_STATUS. + */ +static QDF_STATUS lim_check_sta_in_pe_entries(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, + uint16_t sessionid, + bool *dup_entry) +{ + uint8_t i; + uint16_t assoc_id = 0; + tpDphHashNode sta_ds = NULL; + struct pe_session *session; + + *dup_entry = false; + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + session = &mac_ctx->lim.gpSession[i]; + if (session->valid && + (session->opmode == QDF_SAP_MODE)) { + sta_ds = dph_lookup_hash_entry(mac_ctx, hdr->sa, + &assoc_id, &session->dph.dphHashTable); + if (sta_ds +#ifdef WLAN_FEATURE_11W + && (!sta_ds->rmfEnabled || + (sessionid != session->peSessionId)) +#endif + ) { + if (sta_ds->mlmStaContext.mlmState == + eLIM_MLM_WT_DEL_STA_RSP_STATE || + sta_ds->mlmStaContext.mlmState == + eLIM_MLM_WT_DEL_BSS_RSP_STATE || + sta_ds->sta_deletion_in_progress) { + pe_debug( + "Deletion is in progress (%d) for peer:"QDF_MAC_ADDR_FMT" in mlmState %d", + sta_ds->sta_deletion_in_progress, + QDF_MAC_ADDR_REF(sta_ds->staAddr), + sta_ds->mlmStaContext.mlmState); + *dup_entry = true; + return QDF_STATUS_E_AGAIN; + } + sta_ds->sta_deletion_in_progress = true; + pe_err("Sending Disassoc and Deleting existing STA entry:" + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF( + session->self_mac_addr)); + lim_send_disassoc_mgmt_frame(mac_ctx, + eSIR_MAC_UNSPEC_FAILURE_REASON, + (uint8_t *) hdr->sa, session, false); + /* + * Cleanup Rx path posts eWNI_SME_DISASSOC_RSP + * msg to SME after delete sta which will update + * the userspace with disconnect + */ + sta_ds->mlmStaContext.cleanupTrigger = + eLIM_DUPLICATE_ENTRY; + sta_ds->mlmStaContext.disassocReason = + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON; + lim_send_sme_disassoc_ind(mac_ctx, sta_ds, + session); + *dup_entry = true; + break; + } + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_chk_sa_da() - checks source addr to destination addr of assoc req frame + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks source addr to destination addr of assoc req frame + * + * Return: true if source and destination address are different + */ +static bool lim_chk_sa_da(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, uint8_t sub_type) +{ + if (qdf_mem_cmp((uint8_t *) hdr->sa, + (uint8_t *) hdr->da, + (uint8_t) (sizeof(tSirMacAddr)))) + return true; + + pe_err("Assoc Req rejected: wlan.sa = wlan.da"); + lim_send_assoc_rsp_mgmt_frame(mac_ctx, eSIR_MAC_UNSPEC_FAILURE_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + return false; +} + +/** + * lim_chk_tkip() - checks TKIP counter measure is active + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks TKIP counter measure is active + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_tkip(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, uint8_t sub_type) +{ + /* + * If TKIP counter measures active send Assoc Rsp frame to station + * with eSIR_MAC_MIC_FAILURE_REASON + */ + if (!(session->bTkipCntrMeasActive && LIM_IS_AP_ROLE(session))) + return true; + + pe_err("Assoc Req rejected: TKIP counter measure is active"); + lim_send_assoc_rsp_mgmt_frame(mac_ctx, eSIR_MAC_MIC_FAILURE_REASON, 1, + hdr->sa, sub_type, 0, session, false); + return false; +} + +/** + * lim_chk_assoc_req_parse_error() - checks for error in frame parsing + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @frm_body: frame body + * @frame_len: frame len + * + * Checks for error in frame parsing + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_assoc_req_parse_error(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, + struct pe_session *session, + tpSirAssocReq assoc_req, + uint8_t sub_type, uint8_t *frm_body, + uint32_t frame_len) +{ + QDF_STATUS status; + + if (sub_type == LIM_ASSOC) + status = sir_convert_assoc_req_frame2_struct(mac_ctx, frm_body, + frame_len, + assoc_req); + else + status = sir_convert_reassoc_req_frame2_struct(mac_ctx, + frm_body, frame_len, assoc_req); + + if (status == QDF_STATUS_SUCCESS) + return true; + + pe_warn("Assoc Req rejected: frame parsing error. source addr:" + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(hdr->sa)); + lim_send_assoc_rsp_mgmt_frame(mac_ctx, eSIR_MAC_UNSPEC_FAILURE_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + return false; +} + +/** + * lim_chk_capab() - checks for capab match + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @local_cap: local capabilities of SAP + * + * Checks for capab match + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_capab(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type, tSirMacCapabilityInfo *local_cap) +{ + uint16_t temp; + + if (lim_get_capability_info(mac_ctx, &temp, session) != + QDF_STATUS_SUCCESS) { + pe_err("could not retrieve Capabilities"); + return false; + } + + lim_copy_u16((uint8_t *) local_cap, temp); + + if (lim_compare_capabilities(mac_ctx, assoc_req, + local_cap, session) == false) { + pe_warn("Rcvd %s Req with unsupported capab from" + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? "Assoc" : "ReAssoc", + QDF_MAC_ADDR_REF(hdr->sa)); + /* + * Capabilities of requesting STA does not match with + * local capabilities. Respond with 'unsupported capabilities' + * status code. + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_CAPABILITIES_NOT_SUPPORTED_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + return false; + } + return true; +} + +/** + * lim_chk_ssid() - checks for SSID match + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks for SSID match + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_ssid(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + if (lim_cmp_ssid(&assoc_req->ssId, session) != true) + return true; + + pe_err("%s Req with ssid wrong(Rcvd: %.*s self: %.*s) from " + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? "Assoc" : "ReAssoc", + assoc_req->ssId.length, assoc_req->ssId.ssId, + session->ssId.length, session->ssId.ssId, + QDF_MAC_ADDR_REF(hdr->sa)); + + /* + * Received Re/Association Request with either Broadcast SSID OR with + * SSID that does not match with local one. Respond with unspecified + * status code. + */ + lim_send_assoc_rsp_mgmt_frame(mac_ctx, eSIR_MAC_UNSPEC_FAILURE_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + return false; +} + +/** + * lim_chk_rates() - checks for supported rates + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks for supported rates + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_rates(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + uint8_t i = 0, j = 0; + tSirMacRateSet basic_rates; + /* + * Verify if the requested rates are available in supported rate + * set or Extended rate set. Some APs are adding basic rates in + * Extended rateset IE + */ + basic_rates.numRates = 0; + + for (i = 0; i < assoc_req->supportedRates.numRates + && (i < WLAN_SUPPORTED_RATES_IE_MAX_LEN); i++) { + basic_rates.rate[i] = assoc_req->supportedRates.rate[i]; + basic_rates.numRates++; + } + + for (j = 0; (j < assoc_req->extendedRates.numRates) + && (i < WLAN_SUPPORTED_RATES_IE_MAX_LEN); i++, j++) { + basic_rates.rate[i] = assoc_req->extendedRates.rate[j]; + basic_rates.numRates++; + } + + if (lim_check_rx_basic_rates(mac_ctx, basic_rates, session) == true) + return true; + + pe_warn("Assoc Req rejected: unsupported rates, soruce addr: %s" + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? "Assoc" : "ReAssoc", + QDF_MAC_ADDR_REF(hdr->sa)); + /* + * Requesting STA does not support ALL BSS basic rates. Respond with + * 'basic rates not supported' status code. + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_BASIC_RATES_NOT_SUPPORTED_STATUS, 1, + hdr->sa, sub_type, 0, session, false); + return false; +} + +/** + * lim_chk_11g_only() - checks for non 11g STA + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks for non 11g STA + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_11g_only(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + if (LIM_IS_AP_ROLE(session) && + (session->dot11mode == MLME_DOT11_MODE_11G_ONLY) && + (assoc_req->HTCaps.present)) { + pe_err("SOFTAP was in 11G only mode, rejecting legacy STA: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_CAPABILITIES_NOT_SUPPORTED_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + return false; + } + return true; +} + +/** + * lim_chk_11n_only() - checks for non 11n STA + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks for non 11n STA + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_11n_only(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + if (LIM_IS_AP_ROLE(session) && + (session->dot11mode == MLME_DOT11_MODE_11N_ONLY) && + (!assoc_req->HTCaps.present)) { + pe_err("SOFTAP was in 11N only mode, rejecting legacy STA: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_CAPABILITIES_NOT_SUPPORTED_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + return false; + } + return true; +} + +/** + * lim_chk_11ac_only() - checks for non 11ac STA + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks for non 11ac STA + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_11ac_only(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + tDot11fIEVHTCaps *vht_caps; + + if (assoc_req->VHTCaps.present) + vht_caps = &assoc_req->VHTCaps; + else if (assoc_req->vendor_vht_ie.VHTCaps.present && + session->vendor_vht_sap) + vht_caps = &assoc_req->vendor_vht_ie.VHTCaps; + else + vht_caps = NULL; + + if (LIM_IS_AP_ROLE(session) && + (session->dot11mode == MLME_DOT11_MODE_11AC_ONLY) && + ((!vht_caps) || ((vht_caps) && (!vht_caps->present)))) { + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_CAPABILITIES_NOT_SUPPORTED_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + pe_err("SOFTAP was in 11AC only mode, reject"); + return false; + } + return true; +} + +/** + * lim_chk_11ax_only() - checks for non 11ax STA + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks for non 11ax STA + * + * Return: true of no error, false otherwise + */ +#ifdef WLAN_FEATURE_11AX +static bool lim_chk_11ax_only(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + if (LIM_IS_AP_ROLE(session) && + (session->dot11mode == MLME_DOT11_MODE_11AX_ONLY) && + !assoc_req->he_cap.present) { + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_CAPABILITIES_NOT_SUPPORTED_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + pe_err("SOFTAP was in 11AX only mode, reject"); + return false; + } + return true; +} + +/** + * lim_check_11ax_basic_mcs() - checks for 11ax basic MCS rates + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks for non 11ax STA + * + * Return: true of no error, false otherwise + */ +static bool lim_check_11ax_basic_mcs(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, + struct pe_session *session, + tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + uint16_t basic_mcs, sta_mcs, rx_mcs, tx_mcs, final_mcs; + + if (LIM_IS_AP_ROLE(session) && + assoc_req->he_cap.present) { + rx_mcs = assoc_req->he_cap.rx_he_mcs_map_lt_80; + tx_mcs = assoc_req->he_cap.tx_he_mcs_map_lt_80; + sta_mcs = HE_INTERSECT_MCS(rx_mcs, tx_mcs); + basic_mcs = + (uint16_t)mac_ctx->mlme_cfg->he_caps.he_ops_basic_mcs_nss; + final_mcs = HE_INTERSECT_MCS(sta_mcs, basic_mcs); + if (final_mcs != basic_mcs) { + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, + eSIR_MAC_CAPABILITIES_NOT_SUPPORTED_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + pe_err("STA did not support basic MCS required by SAP"); + return false; + } + } + return true; +} + +#else +static bool lim_chk_11ax_only(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + return true; +} + +static bool lim_check_11ax_basic_mcs(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, + struct pe_session *session, + tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + return true; +} +#endif + +/** + * lim_process_for_spectrum_mgmt() - process assoc req for spectrum mgmt + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @local_cap: local capabilities of SAP + * + * Checks for SSID match + * + * process assoc req for spectrum mgmt + */ +static void +lim_process_for_spectrum_mgmt(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type, tSirMacCapabilityInfo local_cap) +{ + if (local_cap.spectrumMgt) { + QDF_STATUS status = QDF_STATUS_SUCCESS; + /* + * If station is 11h capable, then it SHOULD send all mandatory + * IEs in assoc request frame. Let us verify that + */ + if (assoc_req->capabilityInfo.spectrumMgt) { + if (!((assoc_req->powerCapabilityPresent) + && (assoc_req->supportedChannelsPresent))) { + /* + * One or more required information elements are + * missing, log the peers error + */ + if (!assoc_req->powerCapabilityPresent) { + pe_warn("LIM Info: Missing Power capability IE in %s Req from " + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? + "Assoc" : "ReAssoc", + QDF_MAC_ADDR_REF(hdr->sa)); + } + if (!assoc_req->supportedChannelsPresent) { + pe_warn("LIM Info: Missing Supported channel IE in %s Req from " + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? + "Assoc" : "ReAssoc", + QDF_MAC_ADDR_REF(hdr->sa)); + } + } else { + /* Assoc request has mandatory fields */ + status = + lim_is_dot11h_power_capabilities_in_range( + mac_ctx, assoc_req, session); + if (QDF_STATUS_SUCCESS != status) { + pe_warn("LIM Info: MinTxPower(STA) > MaxTxPower(AP) in %s Req from " + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? + "Assoc" : "ReAssoc", + QDF_MAC_ADDR_REF(hdr->sa)); + } + status = lim_is_dot11h_supported_channels_valid( + mac_ctx, assoc_req); + if (QDF_STATUS_SUCCESS != status) { + pe_warn("LIM Info: wrong supported channels (STA) in %s Req from " + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? + "Assoc" : "ReAssoc", + QDF_MAC_ADDR_REF(hdr->sa)); + } + /* IEs are valid, use them if needed */ + } + } /* if(assoc.capabilityInfo.spectrumMgt) */ + else { + /* + * As per the capabiities, the spectrum management is + * not enabled on the station. The AP may allow the + * associations to happen even if spectrum management + * is not allowed, if the transmit power of station is + * below the regulatory maximum + */ + + /* + * TODO: presently, this is not handled. In the current + * implementation, the AP would allow the station to + * associate even if it doesn't support spectrum + * management. + */ + } + } /* end of spectrum management related processing */ +} + +/** + * lim_chk_mcs() - checks for supported MCS + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * + * Checks for supported MCS + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_mcs(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type) +{ + if ((assoc_req->HTCaps.present) && (lim_check_mcs_set(mac_ctx, + assoc_req->HTCaps.supportedMCSSet) == false)) { + pe_warn("rcvd %s req with unsupported MCS Rate Set from " + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? "Assoc" : "ReAssoc", + QDF_MAC_ADDR_REF(hdr->sa)); + /* + * Requesting STA does not support ALL BSS MCS basic Rate set + * rates. Spec does not define any status code for this + * scenario. + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_OUTSIDE_SCOPE_OF_SPEC_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + return false; + } + return true; +} + +/** + * lim_chk_is_11b_sta_supported() - checks if STA is 11b + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @phy_mode: phy mode + * + * Checks if STA is 11b + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_is_11b_sta_supported(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, + struct pe_session *session, + tpSirAssocReq assoc_req, + uint8_t sub_type, uint32_t phy_mode) +{ + uint32_t cfg_11g_only; + + if (phy_mode == WNI_CFG_PHY_MODE_11G) { + cfg_11g_only = mac_ctx->mlme_cfg->sap_cfg.sap_11g_policy; + if (!assoc_req->extendedRatesPresent && cfg_11g_only) { + /* + * Received Re/Association Request from 11b STA when 11g + * only policy option is set. Reject with unspecified + * status code. + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, + eSIR_MAC_BASIC_RATES_NOT_SUPPORTED_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + + pe_warn("Rejecting Re/Assoc req from 11b STA:"); + lim_print_mac_addr(mac_ctx, hdr->sa, LOGW); + +#ifdef WLAN_DEBUG + mac_ctx->lim.gLim11bStaAssocRejectCount++; +#endif + return false; + } + } + return true; +} + +/** + * lim_print_ht_cap() - prints HT caps + * @mac_ctx: pointer to Global MAC structure + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * + * Prints HT caps + * + * Return: void + */ +static void lim_print_ht_cap(struct mac_context *mac_ctx, struct pe_session *session, + tpSirAssocReq assoc_req) +{ + if (!session->htCapability) + return; + + if (assoc_req->HTCaps.present) { + /* The station *does* support 802.11n HT capability... */ + pe_debug("AdvCodingCap:%d ChaWidthSet:%d PowerSave:%d greenField:%d shortGI20:%d shortGI40:%d txSTBC:%d rxSTBC:%d delayBA:%d maxAMSDUsize:%d DSSS/CCK:%d PSMP:%d stbcCntl:%d lsigTXProt:%d", + assoc_req->HTCaps.advCodingCap, + assoc_req->HTCaps.supportedChannelWidthSet, + assoc_req->HTCaps.mimoPowerSave, + assoc_req->HTCaps.greenField, + assoc_req->HTCaps.shortGI20MHz, + assoc_req->HTCaps.shortGI40MHz, + assoc_req->HTCaps.txSTBC, + assoc_req->HTCaps.rxSTBC, + assoc_req->HTCaps.delayedBA, + assoc_req->HTCaps.maximalAMSDUsize, + assoc_req->HTCaps.dsssCckMode40MHz, + assoc_req->HTCaps.psmp, + assoc_req->HTCaps.stbcControlFrame, + assoc_req->HTCaps.lsigTXOPProtection); + /* + * Make sure the STA's caps are compatible with our own: + * 11.15.2 Support of DSSS/CCK in 40 MHz the AP shall refuse + * association requests from an HT STA that has the DSSS/CCK + * Mode in 40 MHz subfield set to 1; + */ + } +} + +static enum mac_status_code +lim_check_crypto_param(tpSirAssocReq assoc_req, + struct wlan_crypto_params *peer_crypto_params) +{ + /* TKIP/WEP is not allowed in HT/VHT mode*/ + if (assoc_req->HTCaps.present) { + if ((peer_crypto_params->ucastcipherset & + (1 << WLAN_CRYPTO_CIPHER_TKIP)) || + (peer_crypto_params->ucastcipherset & + (1 << WLAN_CRYPTO_CIPHER_WEP))) { + pe_info("TKIP/WEP cipher with HT supported client, reject assoc"); + return eSIR_MAC_INVALID_IE_STATUS; + } + } + return eSIR_MAC_SUCCESS_STATUS; +} + +static +enum mac_status_code lim_check_rsn_ie(struct pe_session *session, + struct mac_context *mac_ctx, + tpSirAssocReq assoc_req, + bool *pmf_connection) +{ + struct wlan_objmgr_vdev *vdev; + tSirMacRsnInfo rsn_ie; + struct wlan_crypto_params peer_crypto_params; + + rsn_ie.info[0] = WLAN_ELEMID_RSN; + rsn_ie.info[1] = assoc_req->rsn.length; + + rsn_ie.length = assoc_req->rsn.length + 2; + qdf_mem_copy(&rsn_ie.info[2], assoc_req->rsn.info, + assoc_req->rsn.length); + if (wlan_crypto_check_rsn_match(mac_ctx->psoc, session->smeSessionId, + &rsn_ie.info[0], rsn_ie.length, + &peer_crypto_params)) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + session->smeSessionId, + WLAN_LEGACY_MAC_ID); + if (!vdev) { + pe_err("vdev is NULL"); + return eSIR_MAC_UNSPEC_FAILURE_STATUS; + } + if ((peer_crypto_params.rsn_caps & + WLAN_CRYPTO_RSN_CAP_MFP_ENABLED) && + wlan_crypto_vdev_is_pmf_enabled(vdev)) + *pmf_connection = true; + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + return lim_check_crypto_param(assoc_req, &peer_crypto_params); + + } else { + return eSIR_MAC_INVALID_IE_STATUS; + } + + return eSIR_MAC_SUCCESS_STATUS; +} + +static enum mac_status_code lim_check_wpa_ie(struct pe_session *session, + struct mac_context *mac_ctx, + tpSirAssocReq assoc_req, + tDot11fIEWPA *wpa) +{ + uint8_t buffer[WLAN_MAX_IE_LEN]; + uint32_t dot11f_status, written = 0, nbuffer = WLAN_MAX_IE_LEN; + tSirMacRsnInfo wpa_ie = {0}; + struct wlan_crypto_params peer_crypto_params; + + dot11f_status = dot11f_pack_ie_wpa(mac_ctx, wpa, buffer, + nbuffer, &written); + if (DOT11F_FAILED(dot11f_status)) { + pe_err("Failed to re-pack the RSN IE (0x%0x8)", dot11f_status); + return eSIR_MAC_INVALID_IE_STATUS; + } + + wpa_ie.length = (uint8_t) written; + qdf_mem_copy(&wpa_ie.info[0], buffer, wpa_ie.length); + if (wlan_crypto_check_wpa_match(mac_ctx->psoc, session->smeSessionId, + &wpa_ie.info[0], wpa_ie.length, + &peer_crypto_params)) { + return lim_check_crypto_param(assoc_req, &peer_crypto_params); + } + + return eSIR_MAC_INVALID_IE_STATUS; +} + +/** + * lim_check_sae_pmf_cap() - check pmf capability for SAE STA + * @session: pointer to pe session entry + * @rsn: pointer to RSN + * @akm_type: AKM type + * + * This function checks if SAE STA is pmf capable when SAE SAP is pmf + * capable. Reject with eSIR_MAC_ROBUST_MGMT_FRAMES_POLICY_VIOLATION + * if SAE STA is pmf disable. + * + * Return: mac_status_code + */ +#if defined(WLAN_FEATURE_SAE) && defined(WLAN_FEATURE_11W) +static enum mac_status_code lim_check_sae_pmf_cap(struct pe_session *session, + tDot11fIERSN *rsn, + enum ani_akm_type akm_type) +{ + enum mac_status_code status = eSIR_MAC_SUCCESS_STATUS; + + if (session->pLimStartBssReq->pmfCapable && + (rsn->RSN_Cap[0] & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED) == 0 && + akm_type == ANI_AKM_TYPE_SAE) + status = eSIR_MAC_ROBUST_MGMT_FRAMES_POLICY_VIOLATION_STATUS; + + return status; +} +#else +static enum mac_status_code lim_check_sae_pmf_cap(struct pe_session *session, + tDot11fIERSN *rsn, + enum ani_akm_type akm_type) +{ + return eSIR_MAC_SUCCESS_STATUS; +} +#endif + +/** + * lim_check_wpa_rsn_ie() - wpa and rsn ie related checks + * @session: pointer to pe session entry + * @mac_ctx: pointer to Global MAC structure + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @hdr: pointer to the MAC head + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @pmf_connection: flag indicating pmf connection + * @akm_type: AKM type + * + * This function checks if wpa/rsn IE is present and validates + * ie version, length and mismatch. + * + * Return: true if no error, false otherwise + */ +static bool lim_check_wpa_rsn_ie(struct pe_session *session, + struct mac_context *mac_ctx, + uint8_t sub_type, tpSirMacMgmtHdr hdr, + tpSirAssocReq assoc_req, bool *pmf_connection, + enum ani_akm_type *akm_type) +{ + uint32_t ret; + tDot11fIEWPA dot11f_ie_wpa = {0}; + tDot11fIERSN dot11f_ie_rsn = {0}; + enum mac_status_code status = eSIR_MAC_SUCCESS_STATUS; + + /* + * Clear the buffers so that frame parser knows that there isn't a + * previously decoded IE in these buffers + */ + qdf_mem_zero((uint8_t *) &dot11f_ie_rsn, sizeof(dot11f_ie_rsn)); + qdf_mem_zero((uint8_t *) &dot11f_ie_wpa, sizeof(dot11f_ie_wpa)); + pe_debug("RSN enabled auth, Re/Assoc req from STA: " + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(hdr->sa)); + + if (assoc_req->rsnPresent) { + if (!(assoc_req->rsn.length)) { + pe_warn("Re/Assoc rejected from: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + /* + * rcvd Assoc req frame with RSN IE but + * length is zero + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_INVALID_IE_STATUS, 1, + hdr->sa, sub_type, 0, session, false); + return false; + } + + /* Unpack the RSN IE */ + ret = dot11f_unpack_ie_rsn(mac_ctx, + &assoc_req->rsn.info[0], + assoc_req->rsn.length, + &dot11f_ie_rsn, false); + if (!DOT11F_SUCCEEDED(ret)) { + pe_err("Invalid RSN IE"); + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_INVALID_IE_STATUS, 1, + hdr->sa, sub_type, 0, session, false); + return false; + } + + /* Check if the RSN version is supported */ + if (SIR_MAC_OUI_VERSION_1 == dot11f_ie_rsn.version) { + /* check the groupwise and pairwise cipher suites */ + status = lim_check_rsn_ie(session, mac_ctx, assoc_req, + pmf_connection); + if (eSIR_MAC_SUCCESS_STATUS != status) { + pe_warn("Re/Assoc rejected from: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, status, 1, hdr->sa, sub_type, + 0, session, false); + return false; + } + } else { + pe_warn("Re/Assoc rejected from: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + /* + * rcvd Assoc req frame with RSN IE but + * IE version is wrong + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, + eSIR_MAC_UNSUPPORTED_RSN_IE_VERSION_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + return false; + } + *akm_type = lim_translate_rsn_oui_to_akm_type( + dot11f_ie_rsn.akm_suite[0]); + + status = lim_check_sae_pmf_cap(session, &dot11f_ie_rsn, + *akm_type); + if (eSIR_MAC_SUCCESS_STATUS != status) { + /* Reject pmf disable SAE STA */ + pe_warn("Re/Assoc rejected from: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + lim_send_assoc_rsp_mgmt_frame(mac_ctx, status, + 1, hdr->sa, sub_type, + 0, session, false); + return false; + } + + } else if (assoc_req->wpaPresent) { + if (!(assoc_req->wpa.length)) { + pe_warn("Re/Assoc rejected from: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + + /* rcvd Assoc req frame with invalid WPA IE length */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_INVALID_IE_STATUS, 1, + hdr->sa, sub_type, 0, session, false); + return false; + } + /* Unpack the WPA IE */ + ret = dot11f_unpack_ie_wpa(mac_ctx, + &assoc_req->wpa.info[4], + (assoc_req->wpa.length - 4), + &dot11f_ie_wpa, false); + if (!DOT11F_SUCCEEDED(ret)) { + pe_err("Invalid WPA IE"); + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_INVALID_IE_STATUS, 1, + hdr->sa, sub_type, 0, session, false); + return false; + } + + /* check the groupwise and pairwise cipher suites*/ + status = lim_check_wpa_ie(session, mac_ctx, assoc_req, + &dot11f_ie_wpa); + if (eSIR_MAC_SUCCESS_STATUS != status) { + pe_warn("Re/Assoc rejected from: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + /* + * rcvd Assoc req frame with WPA IE + * but there is mismatch + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, status, 1, + hdr->sa, sub_type, 0, session, false); + return false; + } + *akm_type = lim_translate_rsn_oui_to_akm_type( + dot11f_ie_wpa.auth_suites[0]); + } + + return true; +} + +/** + * lim_chk_n_process_wpa_rsn_ie() - wpa ie related checks + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @pmf_connection: flag indicating pmf connection + * @akm_type: AKM type + * + * wpa ie related checks + * + * Return: true if no error, false otherwise + */ +static bool lim_chk_n_process_wpa_rsn_ie(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, + struct pe_session *session, + tpSirAssocReq assoc_req, + uint8_t sub_type, + bool *pmf_connection, + enum ani_akm_type *akm_type) +{ + const uint8_t *wps_ie = NULL; + + /* if additional IE is present, check if it has WscIE */ + if (assoc_req->addIEPresent && assoc_req->addIE.length) + wps_ie = limGetWscIEPtr(mac_ctx, assoc_req->addIE.addIEdata, + assoc_req->addIE.length); + else + pe_debug("Assoc req addIEPresent: %d addIE length: %d", + assoc_req->addIEPresent, assoc_req->addIE.length); + + /* when wps_ie is present, RSN/WPA IE is ignored */ + if (!wps_ie) { + /* check whether RSN IE is present */ + if (LIM_IS_AP_ROLE(session) && + session->pLimStartBssReq->privacy && + session->pLimStartBssReq->rsnIE.length) + return lim_check_wpa_rsn_ie(session, mac_ctx, sub_type, + hdr, assoc_req, pmf_connection, + akm_type); + } else { + pe_debug("Assoc req WSE IE is present"); + } + return true; +} + +/** + * lim_process_assoc_req_no_sta_ctx() - process assoc req for no sta ctx present + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @sta_pre_auth_ctx: sta pre auth context + * @sta_ds: station dph entry + * @auth_type: indicates security type + * + * Process assoc req for no sta ctx present + * + * Return: true of no error, false otherwise + */ +static bool lim_process_assoc_req_no_sta_ctx(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, struct pe_session *session, + tpSirAssocReq assoc_req, uint8_t sub_type, + struct tLimPreAuthNode *sta_pre_auth_ctx, + tpDphHashNode sta_ds, tAniAuthType *auth_type) +{ + /* Requesting STA is not currently associated */ + if (pe_get_current_stas_count(mac_ctx) == + mac_ctx->mlme_cfg->sap_cfg.assoc_sta_limit) { + /* + * Maximum number of STAs that AP can handle reached. + * Send Association response to peer MAC entity + */ + pe_err("Max Sta count reached : %d", + mac_ctx->lim.maxStation); + lim_reject_association(mac_ctx, hdr->sa, sub_type, false, + (tAniAuthType) 0, 0, false, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session); + return false; + } + /* Check if STA is pre-authenticated. */ + if ((!sta_pre_auth_ctx) || (sta_pre_auth_ctx && + (sta_pre_auth_ctx->mlmState != eLIM_MLM_AUTHENTICATED_STATE))) { + /* + * STA is not pre-authenticated yet requesting Re/Association + * before Authentication. OR STA is in the process of getting + * authenticated and sent Re/Association request. Send + * Deauthentication frame with 'prior authentication required' + * reason code. + */ + lim_send_deauth_mgmt_frame(mac_ctx, + eSIR_MAC_STA_NOT_PRE_AUTHENTICATED_REASON, + hdr->sa, session, false); + + pe_warn("rcvd %s req, sessionid: %d, without pre-auth ctx" + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? "Assoc" : "ReAssoc", + session->peSessionId, QDF_MAC_ADDR_REF(hdr->sa)); + return false; + } + /* Delete 'pre-auth' context of STA */ + *auth_type = sta_pre_auth_ctx->authType; + if (sta_pre_auth_ctx->authType == eSIR_AUTH_TYPE_SAE) + assoc_req->is_sae_authenticated = true; + lim_delete_pre_auth_node(mac_ctx, hdr->sa); + /* All is well. Assign AID (after else part) */ + return true; +} + +#ifdef WLAN_DEBUG +static inline void +lim_update_assoc_drop_count(struct mac_context *mac_ctx, uint8_t sub_type) +{ + if (sub_type == LIM_ASSOC) + mac_ctx->lim.gLimNumAssocReqDropInvldState++; + else + mac_ctx->lim.gLimNumReassocReqDropInvldState++; +} +#else +static inline void +lim_update_assoc_drop_count(struct mac_context *mac_ctx, uint8_t sub_type) {} +#endif + +#ifdef WLAN_FEATURE_11W +static inline void +lim_delete_pmf_query_timer(tpDphHashNode sta_ds) +{ + if (!sta_ds->rmfEnabled) + return; + + if (tx_timer_running(&sta_ds->pmfSaQueryTimer)) + tx_timer_deactivate(&sta_ds->pmfSaQueryTimer); + tx_timer_delete(&sta_ds->pmfSaQueryTimer); +} +#else +static inline void +lim_delete_pmf_query_timer(tpDphHashNode sta_ds) {} +#endif + +/** + * lim_process_assoc_req_sta_ctx() - process assoc req for sta context present + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @sta_pre_auth_ctx: sta pre auth context + * @sta_ds: station dph entry + * @peer_idx: peer index + * @auth_type: indicates security type + * @update_ctx: indicates if STA context already exist + * + * Process assoc req for sta context present + * + * Return: true of no error, false otherwise + */ +static bool lim_process_assoc_req_sta_ctx(struct mac_context *mac_ctx, + tpSirMacMgmtHdr hdr, struct pe_session *session, + tpSirAssocReq assoc_req, uint8_t sub_type, + struct tLimPreAuthNode *sta_pre_auth_ctx, + tpDphHashNode sta_ds, uint16_t peer_idx, + tAniAuthType *auth_type, uint8_t *update_ctx) +{ + /* Drop if STA deletion is in progress or not in established state */ + if (sta_ds->sta_deletion_in_progress || + (sta_ds->mlmStaContext.mlmState != + eLIM_MLM_LINK_ESTABLISHED_STATE)) { + pe_debug("%s: peer:"QDF_MAC_ADDR_FMT" in mlmState %d (%s) and sta del %d", + (sub_type == LIM_ASSOC) ? "Assoc" : "ReAssoc", + QDF_MAC_ADDR_REF(sta_ds->staAddr), + sta_ds->mlmStaContext.mlmState, + lim_mlm_state_str(sta_ds->mlmStaContext.mlmState), + sta_ds->sta_deletion_in_progress); + lim_update_assoc_drop_count(mac_ctx, sub_type); + return false; + } + + /* STA sent assoc req frame while already in 'associated' state */ + +#ifdef WLAN_FEATURE_11W + pe_debug("Re/Assoc request from station that is already associated"); + pe_debug("PMF enabled: %d, SA Query state: %d", + sta_ds->rmfEnabled, sta_ds->pmfSaQueryState); + if (sta_ds->rmfEnabled) { + switch (sta_ds->pmfSaQueryState) { + /* + * start SA Query procedure, respond to Association Request with + * try again later + */ + case DPH_SA_QUERY_NOT_IN_PROGRESS: + /* + * We should reset the retry counter before we start + * the SA query procedure, otherwise in next set of SA + * query procedure we will end up using the stale value. + */ + sta_ds->pmfSaQueryRetryCount = 0; + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_TRY_AGAIN_LATER, 1, hdr->sa, + sub_type, sta_ds, session, false); + lim_send_sa_query_request_frame(mac_ctx, + (uint8_t *) &(sta_ds->pmfSaQueryCurrentTransId), + hdr->sa, session); + sta_ds->pmfSaQueryStartTransId = + sta_ds->pmfSaQueryCurrentTransId; + sta_ds->pmfSaQueryCurrentTransId++; + + /* start timer for SA Query retry */ + if (tx_timer_activate(&sta_ds->pmfSaQueryTimer) + != TX_SUCCESS) { + pe_err("PMF SA Query timer start failed!"); + return false; + } + sta_ds->pmfSaQueryState = DPH_SA_QUERY_IN_PROGRESS; + return false; + /* + * SA Query procedure still going, respond to Association + * Request with try again later + */ + case DPH_SA_QUERY_IN_PROGRESS: + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_TRY_AGAIN_LATER, 1, + hdr->sa, sub_type, 0, session, false); + return false; + + /* + * SA Query procedure timed out, accept Association + * Request normally + */ + case DPH_SA_QUERY_TIMED_OUT: + sta_ds->pmfSaQueryState = DPH_SA_QUERY_NOT_IN_PROGRESS; + break; + } + } +#endif + + /* no change in the capability so drop the frame */ + if ((sub_type == LIM_ASSOC) && + (!qdf_mem_cmp(&sta_ds->mlmStaContext.capabilityInfo, + &assoc_req->capabilityInfo, + sizeof(tSirMacCapabilityInfo)))) { + pe_err("Received Assoc req in state: %X STAid: %d", + sta_ds->mlmStaContext.mlmState, peer_idx); + return false; + } + + /* + * STA sent Re/association Request frame while already in + * 'associated' state. Update STA capabilities and send + * Association response frame with same AID + */ + pe_debug("Rcvd Assoc req from STA already connected"); + sta_ds->mlmStaContext.capabilityInfo = + assoc_req->capabilityInfo; + if (sta_pre_auth_ctx && (sta_pre_auth_ctx->mlmState == + eLIM_MLM_AUTHENTICATED_STATE)) { + /* STA has triggered pre-auth again */ + *auth_type = sta_pre_auth_ctx->authType; + lim_delete_pre_auth_node(mac_ctx, hdr->sa); + } else { + *auth_type = sta_ds->mlmStaContext.authType; + } + + *update_ctx = true; + /* Free pmf query timer before resetting the sta_ds */ + lim_delete_pmf_query_timer(sta_ds); + if (dph_init_sta_state(mac_ctx, hdr->sa, peer_idx, + &session->dph.dphHashTable) == NULL) { + pe_err("could not Init STAid: %d", peer_idx); + return false; + } + + return true; +} + +/** + * lim_chk_wmm() - wmm related checks + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @qos_mode: qos mode + * + * wmm related checks + * + * Return: true of no error, false otherwise + */ +static bool lim_chk_wmm(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, tpSirAssocReq assoc_req, + uint8_t sub_type, tHalBitVal qos_mode) +{ + tHalBitVal wme_mode; + + limGetWmeMode(session, &wme_mode); + if ((qos_mode == eHAL_SET) || (wme_mode == eHAL_SET)) { + /* + * for a qsta, check if the requested Traffic spec is admissible + * for a non-qsta check if the sta can be admitted + */ + if (assoc_req->addtsPresent) { + uint8_t tspecIdx = 0; + + if (lim_admit_control_add_ts(mac_ctx, hdr->sa, + &(assoc_req->addtsReq), + &(assoc_req->qosCapability), + 0, false, NULL, &tspecIdx, session) != + QDF_STATUS_SUCCESS) { + pe_warn("AdmitControl: TSPEC rejected"); + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, + eSIR_MAC_QAP_NO_BANDWIDTH_REASON, + 1, hdr->sa, sub_type, 0, session, + false); +#ifdef WLAN_DEBUG + mac_ctx->lim.gLimNumAssocReqDropACRejectTS++; +#endif + return false; + } + } else if (lim_admit_control_add_sta(mac_ctx, hdr->sa, false) + != QDF_STATUS_SUCCESS) { + pe_warn("AdmitControl: Sta rejected"); + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_QAP_NO_BANDWIDTH_REASON, 1, + hdr->sa, sub_type, 0, session, false); +#ifdef WLAN_DEBUG + mac_ctx->lim.gLimNumAssocReqDropACRejectSta++; +#endif + return false; + } + /* else all ok */ + pe_debug("AdmitControl: Sta OK!"); + } + return true; +} + +static void lim_update_sta_ds_op_classes(tpSirAssocReq assoc_req, + tpDphHashNode sta_ds) +{ + qdf_mem_copy(&sta_ds->supp_operating_classes, + &assoc_req->supp_operating_classes, + sizeof(tDot11fIESuppOperatingClasses)); +} + +/** + * lim_update_sta_ds() - updates ds dph entry + * @mac_ctx: pointer to Global MAC structure + * @hdr: pointer to the MAC head + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame pointer + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @sta_ds: station dph entry + * @auth_type: indicates security type + * @akm_type: indicates security type in akm + * @assoc_req_copied: boolean to indicate if assoc req was copied to tmp above + * @peer_idx: peer index + * @qos_mode: qos mode + * @pmf_connection: flag indicating pmf connection + * @force_1x1: Flag to check if the HT capable STA needs to be downgraded to 1x1 + * nss. + * + * Updates ds dph entry + * + * Return: true of no error, false otherwise + */ +static bool lim_update_sta_ds(struct mac_context *mac_ctx, tpSirMacMgmtHdr hdr, + struct pe_session *session, + tpSirAssocReq assoc_req, + uint8_t sub_type, tpDphHashNode sta_ds, + tAniAuthType auth_type, + enum ani_akm_type akm_type, + bool *assoc_req_copied, uint16_t peer_idx, + tHalBitVal qos_mode, bool pmf_connection, + bool force_1x1) +{ + tHalBitVal wme_mode, wsm_mode; + uint8_t *ht_cap_ie = NULL; +#ifdef WLAN_FEATURE_11W + tPmfSaQueryTimerId timer_id; + uint16_t retry_interval; +#endif + tDot11fIEVHTCaps *vht_caps; + tpSirAssocReq tmp_assoc_req; + + if (assoc_req->VHTCaps.present) + vht_caps = &assoc_req->VHTCaps; + else if (assoc_req->vendor_vht_ie.VHTCaps.present && + session->vendor_vht_sap) + vht_caps = &assoc_req->vendor_vht_ie.VHTCaps; + else + vht_caps = NULL; + + /* + * check here if the parsedAssocReq already pointing to the assoc_req + * and free it before assigning this new assoc_req + */ + if (session->parsedAssocReq) { + tmp_assoc_req = session->parsedAssocReq[sta_ds->assocId]; + if (tmp_assoc_req) { + if (tmp_assoc_req->assocReqFrame) { + qdf_mem_free(tmp_assoc_req->assocReqFrame); + tmp_assoc_req->assocReqFrame = NULL; + tmp_assoc_req->assocReqFrameLength = 0; + } + qdf_mem_free(tmp_assoc_req); + tmp_assoc_req = NULL; + } + + session->parsedAssocReq[sta_ds->assocId] = assoc_req; + *assoc_req_copied = true; + } + + if (!assoc_req->wmeInfoPresent) { + sta_ds->mlmStaContext.htCapability = 0; + sta_ds->mlmStaContext.vhtCapability = 0; + } else { + sta_ds->mlmStaContext.htCapability = assoc_req->HTCaps.present; + if ((vht_caps) && vht_caps->present) + sta_ds->mlmStaContext.vhtCapability = vht_caps->present; + else + sta_ds->mlmStaContext.vhtCapability = false; + } + + lim_update_stads_he_capable(sta_ds, assoc_req); + sta_ds->qos.addtsPresent = + (assoc_req->addtsPresent == 0) ? false : true; + sta_ds->qos.addts = assoc_req->addtsReq; + sta_ds->qos.capability = assoc_req->qosCapability; + /* + * short slot and short preamble should be updated before doing + * limaddsta + */ + sta_ds->shortPreambleEnabled = + (uint8_t) assoc_req->capabilityInfo.shortPreamble; + sta_ds->shortSlotTimeEnabled = + (uint8_t) assoc_req->capabilityInfo.shortSlotTime; + + sta_ds->valid = 0; + sta_ds->mlmStaContext.authType = auth_type; + sta_ds->mlmStaContext.akm_type = akm_type; + sta_ds->staType = STA_ENTRY_PEER; + sta_ds->mlmStaContext.force_1x1 = force_1x1; + + pe_debug("auth_type = %d, akm_type = %d", auth_type, akm_type); + + /* + * TODO: If listen interval is more than certain limit, reject the + * association. Need to check customer requirements and then implement. + */ + sta_ds->mlmStaContext.listenInterval = assoc_req->listenInterval; + sta_ds->mlmStaContext.capabilityInfo = assoc_req->capabilityInfo; + + if (IS_DOT11_MODE_HT(session->dot11mode) && + sta_ds->mlmStaContext.htCapability) { + sta_ds->htGreenfield = (uint8_t) assoc_req->HTCaps.greenField; + sta_ds->htAMpduDensity = assoc_req->HTCaps.mpduDensity; + sta_ds->htDsssCckRate40MHzSupport = + (uint8_t) assoc_req->HTCaps.dsssCckMode40MHz; + sta_ds->htLsigTXOPProtection = + (uint8_t) assoc_req->HTCaps.lsigTXOPProtection; + sta_ds->htMaxAmsduLength = + (uint8_t) assoc_req->HTCaps.maximalAMSDUsize; + sta_ds->htMaxRxAMpduFactor = assoc_req->HTCaps.maxRxAMPDUFactor; + sta_ds->htMIMOPSState = assoc_req->HTCaps.mimoPowerSave; + + /* assoc_req will be copied to session->parsedAssocReq later */ + ht_cap_ie = ((uint8_t *) &assoc_req->HTCaps) + 1; + + if (session->ht_config.ht_sgi20) { + sta_ds->htShortGI20Mhz = + (uint8_t)assoc_req->HTCaps.shortGI20MHz; + } else { + /* Unset htShortGI20Mhz in ht_caps*/ + *ht_cap_ie &= ~(1 << SIR_MAC_HT_CAP_SHORTGI20MHZ_S); + sta_ds->htShortGI20Mhz = 0; + } + + if (session->ht_config.ht_sgi40) { + sta_ds->htShortGI40Mhz = + (uint8_t)assoc_req->HTCaps.shortGI40MHz; + } else { + /* Unset htShortGI40Mhz in ht_caps */ + *ht_cap_ie &= ~(1 << SIR_MAC_HT_CAP_SHORTGI40MHZ_S); + sta_ds->htShortGI40Mhz = 0; + } + + sta_ds->htSupportedChannelWidthSet = + (uint8_t) assoc_req->HTCaps.supportedChannelWidthSet; + if (session->ch_width > CH_WIDTH_20MHZ && + session->ch_width <= CH_WIDTH_80P80MHZ && + sta_ds->htSupportedChannelWidthSet) { + /* + * peer just follows AP; so when we are softAP/GO, + * we just store our session entry's secondary channel + * offset here in peer INFRA STA. However, if peer's + * 40MHz channel width support is disabled then + * secondary channel will be zero + */ + sta_ds->htSecondaryChannelOffset = + session->htSecondaryChannelOffset; + sta_ds->ch_width = CH_WIDTH_40MHZ; + if (sta_ds->mlmStaContext.vhtCapability) { + if (assoc_req->operMode.present) { + sta_ds->ch_width = + assoc_req->operMode.chanWidth; + } else if (vht_caps->supportedChannelWidthSet == + VHT_CAP_160_AND_80P80_SUPP) { + sta_ds->ch_width = CH_WIDTH_80P80MHZ; + } else if (vht_caps->supportedChannelWidthSet == + VHT_CAP_160_SUPP) { + if (vht_caps->vht_extended_nss_bw_cap && + vht_caps->extended_nss_bw_supp) + sta_ds->ch_width = + CH_WIDTH_80P80MHZ; + else + sta_ds->ch_width = + CH_WIDTH_160MHZ; + } else if (vht_caps->vht_extended_nss_bw_cap) { + if (vht_caps->extended_nss_bw_supp == + VHT_EXTD_NSS_80_HALF_NSS_160) + sta_ds->ch_width = + CH_WIDTH_160MHZ; + else if (vht_caps->extended_nss_bw_supp > + VHT_EXTD_NSS_80_HALF_NSS_160) + sta_ds->ch_width = + CH_WIDTH_80P80MHZ; + else + sta_ds->ch_width = + CH_WIDTH_80MHZ; + } else { + sta_ds->ch_width = CH_WIDTH_80MHZ; + } + + sta_ds->ch_width = QDF_MIN(sta_ds->ch_width, + session->ch_width); + } + } else { + sta_ds->htSupportedChannelWidthSet = 0; + sta_ds->htSecondaryChannelOffset = 0; + sta_ds->ch_width = CH_WIDTH_20MHZ; + } + sta_ds->htLdpcCapable = + (uint8_t) assoc_req->HTCaps.advCodingCap; + } + + if (assoc_req->ExtCap.present) + sta_ds->non_ecsa_capable = + !((struct s_ext_cap *)assoc_req->ExtCap.bytes)-> + ext_chan_switch; + else + sta_ds->non_ecsa_capable = 1; + + if (sta_ds->mlmStaContext.vhtCapability && + session->vhtCapability) { + sta_ds->htMaxRxAMpduFactor = + vht_caps->maxAMPDULenExp; + sta_ds->vhtLdpcCapable = + (uint8_t)vht_caps->ldpcCodingCap; + if (session->vht_config.su_beam_formee && + vht_caps->suBeamFormerCap) + sta_ds->vhtBeamFormerCapable = 1; + else + sta_ds->vhtBeamFormerCapable = 0; + if (session->vht_config.su_beam_former && + vht_caps->suBeamformeeCap) + sta_ds->vht_su_bfee_capable = 1; + else + sta_ds->vht_su_bfee_capable = 0; + + pe_debug("peer_caps: suBformer: %d, suBformee: %d", + vht_caps->suBeamFormerCap, + vht_caps->suBeamformeeCap); + pe_debug("self_cap: suBformer: %d, suBformee: %d", + session->vht_config.su_beam_former, + session->vht_config.su_beam_formee); + pe_debug("connection's final cap: suBformer: %d, suBformee: %d", + sta_ds->vhtBeamFormerCapable, + sta_ds->vht_su_bfee_capable); + } + + sta_ds->vht_mcs_10_11_supp = 0; + if (IS_DOT11_MODE_HT(session->dot11mode) && + sta_ds->mlmStaContext.vhtCapability) { + if (mac_ctx->mlme_cfg->vht_caps.vht_cap_info. + vht_mcs_10_11_supp && + assoc_req->qcn_ie.present && + assoc_req->qcn_ie.vht_mcs11_attr.present) + sta_ds->vht_mcs_10_11_supp = + assoc_req->qcn_ie.vht_mcs11_attr. + vht_mcs_10_11_supp; + } + lim_intersect_sta_he_caps(assoc_req, session, sta_ds); + + if (lim_populate_matching_rate_set(mac_ctx, sta_ds, + &(assoc_req->supportedRates), + &(assoc_req->extendedRates), + assoc_req->HTCaps.supportedMCSSet, + session, vht_caps, + &assoc_req->he_cap) != QDF_STATUS_SUCCESS) { + /* Could not update hash table entry at DPH with rateset */ + pe_err("Couldn't update hash entry for aid: %d MacAddr: " + QDF_MAC_ADDR_FMT, + peer_idx, QDF_MAC_ADDR_REF(hdr->sa)); + + /* Release AID */ + lim_release_peer_idx(mac_ctx, peer_idx, session); + + lim_reject_association(mac_ctx, hdr->sa, + sub_type, true, auth_type, peer_idx, false, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session); + pe_err("Delete dph hash entry"); + if (dph_delete_hash_entry(mac_ctx, hdr->sa, sta_ds->assocId, + &session->dph.dphHashTable) != QDF_STATUS_SUCCESS) + pe_err("error deleting hash entry"); + return false; + } + if (assoc_req->operMode.present) { + sta_ds->vhtSupportedRxNss = assoc_req->operMode.rxNSS + 1; + } else { + sta_ds->vhtSupportedRxNss = + ((sta_ds->supportedRates.vhtRxMCSMap & MCSMAPMASK2x2) + == MCSMAPMASK2x2) ? 1 : 2; + } + lim_update_stads_he_6ghz_op(session, sta_ds); + lim_update_sta_ds_op_classes(assoc_req, sta_ds); + /* Add STA context at MAC HW (BMU, RHP & TFP) */ + sta_ds->qosMode = false; + sta_ds->lleEnabled = false; + if (assoc_req->capabilityInfo.qos && (qos_mode == eHAL_SET)) { + sta_ds->lleEnabled = true; + sta_ds->qosMode = true; + } + + sta_ds->wmeEnabled = false; + sta_ds->wsmEnabled = false; + limGetWmeMode(session, &wme_mode); + if ((!sta_ds->lleEnabled) && assoc_req->wmeInfoPresent + && (wme_mode == eHAL_SET)) { + sta_ds->wmeEnabled = true; + sta_ds->qosMode = true; + limGetWsmMode(session, &wsm_mode); + /* + * WMM_APSD - WMM_SA related processing should be separate; + * WMM_SA and WMM_APSD can coexist + */ + if (assoc_req->WMMInfoStation.present) { + /* check whether AP supports or not */ + if (LIM_IS_AP_ROLE(session) && + (session->apUapsdEnable == 0) && + (assoc_req->WMMInfoStation.acbe_uapsd || + assoc_req->WMMInfoStation.acbk_uapsd || + assoc_req->WMMInfoStation.acvo_uapsd || + assoc_req->WMMInfoStation.acvi_uapsd)) { + /* + * Rcvd Re/Assoc Req from STA when UPASD is + * not supported. + */ + pe_err("UAPSD not supported, reply accordingly"); + /* update UAPSD and send it to LIM to add STA */ + sta_ds->qos.capability.qosInfo.acbe_uapsd = 0; + sta_ds->qos.capability.qosInfo.acbk_uapsd = 0; + sta_ds->qos.capability.qosInfo.acvo_uapsd = 0; + sta_ds->qos.capability.qosInfo.acvi_uapsd = 0; + sta_ds->qos.capability.qosInfo.maxSpLen = 0; + } else { + /* update UAPSD and send it to LIM to add STA */ + sta_ds->qos.capability.qosInfo.acbe_uapsd = + assoc_req->WMMInfoStation.acbe_uapsd; + sta_ds->qos.capability.qosInfo.acbk_uapsd = + assoc_req->WMMInfoStation.acbk_uapsd; + sta_ds->qos.capability.qosInfo.acvo_uapsd = + assoc_req->WMMInfoStation.acvo_uapsd; + sta_ds->qos.capability.qosInfo.acvi_uapsd = + assoc_req->WMMInfoStation.acvi_uapsd; + sta_ds->qos.capability.qosInfo.maxSpLen = + assoc_req->WMMInfoStation.max_sp_length; + } + } + if (assoc_req->wsmCapablePresent && (wsm_mode == eHAL_SET)) + sta_ds->wsmEnabled = true; + } + /* Re/Assoc Response frame to requesting STA */ + sta_ds->mlmStaContext.subType = sub_type; + +#ifdef WLAN_FEATURE_11W + sta_ds->rmfEnabled = (pmf_connection) ? 1 : 0; + sta_ds->pmfSaQueryState = DPH_SA_QUERY_NOT_IN_PROGRESS; + timer_id.fields.sessionId = session->peSessionId; + timer_id.fields.peerIdx = peer_idx; + retry_interval = mac_ctx->mlme_cfg->gen.pmf_sa_query_retry_interval; + if (cfg_min(CFG_PMF_SA_QUERY_RETRY_INTERVAL) > retry_interval) { + retry_interval = cfg_default(CFG_PMF_SA_QUERY_RETRY_INTERVAL); + } + if (sta_ds->rmfEnabled) { + /* Try to delete it before, creating.*/ + lim_delete_pmf_query_timer(sta_ds); + if (tx_timer_create(mac_ctx, &sta_ds->pmfSaQueryTimer, + "PMF SA Query timer", lim_pmf_sa_query_timer_handler, + timer_id.value, + SYS_MS_TO_TICKS((retry_interval * 1024) / 1000), + 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("could not create PMF SA Query timer"); + lim_reject_association(mac_ctx, hdr->sa, sub_type, + true, auth_type, peer_idx, false, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session); + return false; + } + pe_debug("Created pmf timer assoc-id:%d sta mac" QDF_MAC_ADDR_FMT, + sta_ds->assocId, QDF_MAC_ADDR_REF(sta_ds->staAddr)); + } +#endif + + if (assoc_req->ExtCap.present) { + lim_set_stads_rtt_cap(sta_ds, + (struct s_ext_cap *) assoc_req->ExtCap.bytes, mac_ctx); + } else { + sta_ds->timingMeasCap = 0; + pe_debug("ExtCap not present"); + } + lim_ap_check_6g_compatible_peer(mac_ctx, session); + return true; +} + +/** + * lim_update_sta_ctx() - add/del sta depending on connection state machine + * @mac_ctx: pointer to Global MAC structure + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sub_type: Assoc(=0) or Reassoc(=1) Requestframe + * @sta_ds: station dph entry + * @update_ctx: indicates if STA context already exist + * + * Checks for SSID match + * + * Return: true of no error, false otherwise + */ +static bool lim_update_sta_ctx(struct mac_context *mac_ctx, struct pe_session *session, + tpSirAssocReq assoc_req, uint8_t sub_type, + tpDphHashNode sta_ds, uint8_t update_ctx) +{ + tLimMlmStates mlm_prev_state; + /* + * BTAMP: If STA context already exist (ie. update_ctx = 1) for this STA + * then we should delete the old one, and add the new STA. This is taken + * care of in the lim_del_sta() routine. + * + * Prior to BTAMP, we were setting this flag so that when PE receives + * SME_ASSOC_CNF, and if this flag is set, then PE shall delete the old + * station and then add. But now in BTAMP, we're directly adding station + * before waiting for SME_ASSOC_CNF, so we can do this now. + */ + if (!(update_ctx)) { + sta_ds->mlmStaContext.updateContext = 0; + + /* + * BTAMP: Add STA context at HW - issue WMA_ADD_STA_REQ to HAL + */ + if (lim_add_sta(mac_ctx, sta_ds, false, session) != + QDF_STATUS_SUCCESS) { + pe_err("could not Add STA with assocId: %d", + sta_ds->assocId); + lim_reject_association(mac_ctx, sta_ds->staAddr, + sta_ds->mlmStaContext.subType, true, + sta_ds->mlmStaContext.authType, + sta_ds->assocId, true, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session); + + if (session->parsedAssocReq) + assoc_req = + session->parsedAssocReq[sta_ds->assocId]; + return false; + } + } else { + sta_ds->mlmStaContext.updateContext = 1; + mlm_prev_state = sta_ds->mlmStaContext.mlmState; + + /* + * As per the HAL/FW needs the reassoc req need not be calling + * lim_del_sta + */ + if (sub_type != LIM_REASSOC) { + /* + * we need to set the mlmState here in order + * differentiate in lim_del_sta. + */ + sta_ds->mlmStaContext.mlmState = + eLIM_MLM_WT_ASSOC_DEL_STA_RSP_STATE; + if (lim_del_sta(mac_ctx, sta_ds, true, session) + != QDF_STATUS_SUCCESS) { + pe_err("Couldn't DEL STA, assocId: %d sta mac" + QDF_MAC_ADDR_FMT, sta_ds->assocId, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + lim_reject_association(mac_ctx, sta_ds->staAddr, + sta_ds->mlmStaContext.subType, true, + sta_ds->mlmStaContext.authType, + sta_ds->assocId, true, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session); + + /* Restoring the state back. */ + sta_ds->mlmStaContext.mlmState = mlm_prev_state; + if (session->parsedAssocReq) + assoc_req = session->parsedAssocReq[ + sta_ds->assocId]; + return false; + } + } else { + /* + * mlmState is changed in lim_add_sta context use the + * same AID, already allocated + */ + if (lim_add_sta(mac_ctx, sta_ds, false, session) + != QDF_STATUS_SUCCESS) { + pe_err("UPASD not supported, REASSOC Failed"); + lim_reject_association(mac_ctx, sta_ds->staAddr, + sta_ds->mlmStaContext.subType, true, + sta_ds->mlmStaContext.authType, + sta_ds->assocId, true, + eSIR_MAC_WME_REFUSED_STATUS, + session); + + /* Restoring the state back. */ + sta_ds->mlmStaContext.mlmState = mlm_prev_state; + if (session->parsedAssocReq) + assoc_req = session->parsedAssocReq[ + sta_ds->assocId]; + return false; + } + } + } + return true; +} + +void lim_process_assoc_cleanup(struct mac_context *mac_ctx, + struct pe_session *session, + tpSirAssocReq assoc_req, + tpDphHashNode sta_ds, + bool assoc_req_copied) +{ + tpSirAssocReq tmp_assoc_req; + + if (assoc_req) { + if (assoc_req->assocReqFrame) { + qdf_mem_free(assoc_req->assocReqFrame); + assoc_req->assocReqFrame = NULL; + assoc_req->assocReqFrameLength = 0; + } + + qdf_mem_free(assoc_req); + /* to avoid double free */ + if (assoc_req_copied && session->parsedAssocReq && sta_ds) + session->parsedAssocReq[sta_ds->assocId] = NULL; + } + + /* If it is not duplicate Assoc request then only make to Null */ + if ((sta_ds) && + (sta_ds->mlmStaContext.mlmState != eLIM_MLM_WT_ADD_STA_RSP_STATE)) { + if (session->parsedAssocReq) { + tmp_assoc_req = + session->parsedAssocReq[sta_ds->assocId]; + if (tmp_assoc_req) { + if (tmp_assoc_req->assocReqFrame) { + qdf_mem_free( + tmp_assoc_req->assocReqFrame); + tmp_assoc_req->assocReqFrame = NULL; + tmp_assoc_req->assocReqFrameLength = 0; + } + qdf_mem_free(tmp_assoc_req); + session->parsedAssocReq[sta_ds->assocId] = NULL; + } + } + } +} + +/** + * lim_defer_sme_indication() - Defer assoc indication to SME + * @mac_ctx: Pointer to Global MAC structure + * @session: pe session entry + * @sub_type: Indicates whether it is Association Request(=0) or Reassociation + * Request(=1) frame + * @hdr: A pointer to the MAC header + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @pmf_connection: flag indicating pmf connection + * @assoc_req_copied: boolean to indicate if assoc req was copied to tmp above + * @dup_entry: flag indicating if duplicate entry found + * + * Defer Initialization of PE data structures and wait for an external event. + * lim_send_assoc_ind_to_sme() will be called to initialize PE data structures + * when the expected event is received. + * + * Return: void + */ +static void lim_defer_sme_indication(struct mac_context *mac_ctx, + struct pe_session *session, + uint8_t sub_type, + tpSirMacMgmtHdr hdr, + struct sSirAssocReq *assoc_req, + bool pmf_connection, + bool assoc_req_copied, + bool dup_entry, + struct sDphHashNode *sta_ds) +{ + struct tLimPreAuthNode *sta_pre_auth_ctx; + struct lim_assoc_data *cached_req; + + /* Extract pre-auth context for the STA, if any. */ + sta_pre_auth_ctx = lim_search_pre_auth_list(mac_ctx, hdr->sa); + if (sta_pre_auth_ctx->assoc_req.present) { + pe_debug("Free the cached assoc req as a new one is received"); + cached_req = &sta_pre_auth_ctx->assoc_req; + lim_process_assoc_cleanup(mac_ctx, session, + cached_req->assoc_req, + cached_req->sta_ds, + cached_req->assoc_req_copied); + } + + sta_pre_auth_ctx->assoc_req.present = true; + sta_pre_auth_ctx->assoc_req.sub_type = sub_type; + qdf_mem_copy(&sta_pre_auth_ctx->assoc_req.hdr, hdr, + sizeof(tSirMacMgmtHdr)); + sta_pre_auth_ctx->assoc_req.assoc_req = assoc_req; + sta_pre_auth_ctx->assoc_req.pmf_connection = pmf_connection; + sta_pre_auth_ctx->assoc_req.assoc_req_copied = assoc_req_copied; + sta_pre_auth_ctx->assoc_req.dup_entry = dup_entry; + sta_pre_auth_ctx->assoc_req.sta_ds = sta_ds; +} + +bool lim_send_assoc_ind_to_sme(struct mac_context *mac_ctx, + struct pe_session *session, + uint8_t sub_type, tpSirMacMgmtHdr hdr, + tpSirAssocReq assoc_req, + enum ani_akm_type akm_type, + bool pmf_connection, bool *assoc_req_copied, + bool dup_entry, bool force_1x1) +{ + uint16_t peer_idx; + struct tLimPreAuthNode *sta_pre_auth_ctx; + tpDphHashNode sta_ds = NULL; + tHalBitVal qos_mode; + tAniAuthType auth_type; + uint8_t update_ctx = false; + + limGetQosMode(session, &qos_mode); + /* Extract 'associated' context for STA, if any. */ + sta_ds = dph_lookup_hash_entry(mac_ctx, hdr->sa, &peer_idx, + &session->dph.dphHashTable); + + /* Extract pre-auth context for the STA, if any. */ + sta_pre_auth_ctx = lim_search_pre_auth_list(mac_ctx, hdr->sa); + + if (!sta_ds) { + if (!lim_process_assoc_req_no_sta_ctx(mac_ctx, hdr, session, + assoc_req, sub_type, + sta_pre_auth_ctx, sta_ds, + &auth_type)) + return false; + } else { + if (!lim_process_assoc_req_sta_ctx(mac_ctx, hdr, session, + assoc_req, sub_type, + sta_pre_auth_ctx, sta_ds, + peer_idx, &auth_type, + &update_ctx)) + return false; + goto send_ind_to_sme; + } + + /* check if sta is allowed per QoS AC rules */ + if (!lim_chk_wmm(mac_ctx, hdr, session, assoc_req, sub_type, qos_mode)) + return false; + + /* STA is Associated ! */ + pe_debug("Received: %s Req successful from " QDF_MAC_ADDR_FMT, + (sub_type == LIM_ASSOC) ? "Assoc" : "ReAssoc", + QDF_MAC_ADDR_REF(hdr->sa)); + + /* + * AID for this association will be same as the peer Index used in DPH + * table. Assign unused/least recently used peer Index from perStaDs. + * NOTE: lim_assign_peer_idx() assigns AID values ranging between + * 1 - cfg_item(WNI_CFG_ASSOC_STA_LIMIT) + */ + + peer_idx = lim_assign_peer_idx(mac_ctx, session); + + if (!peer_idx) { + /* Could not assign AID. Reject association */ + pe_err("PeerIdx not avaialble. Reject associaton"); + lim_reject_association(mac_ctx, hdr->sa, sub_type, + true, auth_type, peer_idx, false, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session); + return false; + } + + /* Add an entry to hash table maintained by DPH module */ + + sta_ds = dph_add_hash_entry(mac_ctx, hdr->sa, peer_idx, + &session->dph.dphHashTable); + + if (!sta_ds) { + /* Could not add hash table entry at DPH */ + pe_err("couldn't add hash entry at DPH for aid: %d MacAddr:" + QDF_MAC_ADDR_FMT, peer_idx, QDF_MAC_ADDR_REF(hdr->sa)); + + /* Release AID */ + lim_release_peer_idx(mac_ctx, peer_idx, session); + + lim_reject_association(mac_ctx, hdr->sa, sub_type, + true, auth_type, peer_idx, false, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session); + return false; + } + + if (LIM_IS_AP_ROLE(session)) { + if ((assoc_req->wpaPresent || assoc_req->rsnPresent) && + !session->privacy) { + lim_reject_association(mac_ctx, hdr->sa, sub_type, + true, auth_type, peer_idx, + true, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session); + return false; + } + } + +send_ind_to_sme: + if (!lim_update_sta_ds(mac_ctx, hdr, session, assoc_req, + sub_type, sta_ds, auth_type, akm_type, + assoc_req_copied, peer_idx, qos_mode, + pmf_connection, force_1x1)) + return false; + + /* BTAMP: Storing the parsed assoc request in the session array */ + if (session->parsedAssocReq) + session->parsedAssocReq[sta_ds->assocId] = assoc_req; + *assoc_req_copied = true; + + /* If it is duplicate entry wait till the peer is deleted */ + if (!dup_entry) { + if (!lim_update_sta_ctx(mac_ctx, session, assoc_req, + sub_type, sta_ds, update_ctx)) + return false; + } + + /* AddSta is success here */ + if (LIM_IS_AP_ROLE(session) && IS_DOT11_MODE_HT(session->dot11mode) && + assoc_req->HTCaps.present && assoc_req->wmeInfoPresent) { + /* + * Update in the HAL Sta Table for the Update of the Protection + * Mode + */ + lim_post_sm_state_update(mac_ctx, + sta_ds->htMIMOPSState, sta_ds->staAddr, + session->smeSessionId); + } + + return true; +} + +/** + * lim_peer_present_on_any_sta() - Check if Same MAC is connected with STA, i.e. + * duplicate mac detection. + * @mac_ctx: Pointer to Global MAC structure + * @peer_addr: peer address to check + * + * This function will return true if a peer STA and AP are using same mac + * address. + * + * @Return: bool + */ +static bool +lim_peer_present_on_any_sta(struct mac_context *mac_ctx, uint8_t *peer_addr) +{ + struct wlan_objmgr_peer *peer; + bool sta_peer_present = false; + enum QDF_OPMODE mode; + uint8_t peer_vdev_id; + + peer = wlan_objmgr_get_peer_by_mac(mac_ctx->psoc, peer_addr, + WLAN_LEGACY_MAC_ID); + if (!peer) + return sta_peer_present; + + peer_vdev_id = wlan_vdev_get_id(wlan_peer_get_vdev(peer)); + mode = wlan_vdev_mlme_get_opmode(wlan_peer_get_vdev(peer)); + if (mode == QDF_STA_MODE || mode == QDF_P2P_CLIENT_MODE) { + pe_debug("duplicate mac detected!!! Peer " QDF_MAC_ADDR_FMT " present on STA vdev %d", + QDF_MAC_ADDR_REF(peer_addr), peer_vdev_id); + sta_peer_present = true; + } + + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID); + + return sta_peer_present; +} + +/** + * lim_process_assoc_req_frame() - Process RE/ASSOC Request frame. + * @mac_ctx: Pointer to Global MAC structure + * @rx_pkt_info: A pointer to Buffer descriptor + associated PDUs + * @sub_type: Indicates whether it is Association Request(=0) or Reassociation + * Request(=1) frame + * @session: pe session entry + * + * This function is called to process RE/ASSOC Request frame. + * + * @Return: void + */ +void lim_process_assoc_req_frame(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + uint8_t sub_type, struct pe_session *session) +{ + bool pmf_connection = false, assoc_req_copied = false; + uint8_t *frm_body; + uint16_t assoc_id = 0; + uint32_t frame_len; + uint32_t phy_mode; + tHalBitVal qos_mode; + tpSirMacMgmtHdr hdr; + struct tLimPreAuthNode *sta_pre_auth_ctx; + enum ani_akm_type akm_type = ANI_AKM_TYPE_NONE; + tSirMacCapabilityInfo local_cap; + tpDphHashNode sta_ds = NULL; + tpSirAssocReq assoc_req; + bool dup_entry = false, force_1x1 = false; + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + lim_get_phy_mode(mac_ctx, &phy_mode, session); + + limGetQosMode(session, &qos_mode); + + hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + pe_nofl_rl_debug("Assoc req RX: subtype %d vdev %d sys role %d lim state %d rssi %d from " QDF_MAC_ADDR_FMT, + sub_type, session->vdev_id, + GET_LIM_SYSTEM_ROLE(session), session->limMlmState, + WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info), + QDF_MAC_ADDR_REF(hdr->sa)); + + if (LIM_IS_STA_ROLE(session)) { + pe_err("Rcvd unexpected ASSOC REQ, sessionid: %d sys sub_type: %d for role: %d from: " + QDF_MAC_ADDR_FMT, + session->peSessionId, sub_type, + GET_LIM_SYSTEM_ROLE(session), + QDF_MAC_ADDR_REF(hdr->sa)); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + WMA_GET_RX_MPDU_DATA(rx_pkt_info), + frame_len); + return; + } + if (session->limMlmState == eLIM_MLM_WT_DEL_BSS_RSP_STATE) { + pe_err("drop ASSOC REQ on sessionid: %d " + "role: %d from: "QDF_MAC_ADDR_FMT" in limMlmState: %d", + session->peSessionId, + GET_LIM_SYSTEM_ROLE(session), + QDF_MAC_ADDR_REF(hdr->sa), + eLIM_MLM_WT_DEL_BSS_RSP_STATE); + return; + } + vdev = session->vdev; + if (!vdev) { + pe_err("vdev is NULL"); + return; + } + + if (wlan_vdev_mlme_get_state(vdev) != WLAN_VDEV_S_UP) { + pe_err("SAP is not up, drop ASSOC REQ on sessionid: %d", + session->peSessionId); + + return; + } + + if (lim_peer_present_on_any_sta(mac_ctx, hdr->sa)) + /* + * This mean a AP and STA have same mac address and device STA + * is already connected to the AP, and STA is now trying to + * connect to device SAP. So ignore association. + */ + return; + + /* + * If a STA is already present in DPH and it is initiating a Assoc + * re-transmit, do not process it. This can happen when first Assoc Req + * frame is received but ACK lost at STA side. The ACK for this dropped + * Assoc Req frame should be sent by HW. Host simply does not process it + * once the entry for the STA is already present in DPH. + */ + sta_ds = dph_lookup_hash_entry(mac_ctx, hdr->sa, &assoc_id, + &session->dph.dphHashTable); + if (sta_ds) { + if (hdr->fc.retry > 0) { + pe_err("STA is initiating Assoc Req after ACK lost. Do not process sessionid: %d sys sub_type=%d for role=%d from: " + QDF_MAC_ADDR_FMT, session->peSessionId, + sub_type, GET_LIM_SYSTEM_ROLE(session), + QDF_MAC_ADDR_REF(hdr->sa)); + return; + } else if (!sta_ds->rmfEnabled && (sub_type == LIM_REASSOC)) { + /* + * SAP should send reassoc response with reject code + * to avoid IOT issues. as per the specification SAP + * should do 4-way handshake after reassoc response and + * some STA doesn't like 4way handshake after reassoc + * where some STA does expect 4-way handshake. + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_OUTSIDE_SCOPE_OF_SPEC_STATUS, + sta_ds->assocId, sta_ds->staAddr, + sub_type, sta_ds, session, false); + pe_err("Rejecting reassoc req from STA"); + return; + } else if (!sta_ds->rmfEnabled) { + /* + * Do this only for non PMF case. + * STA might have missed the assoc response, so it is + * sending assoc request frame again. + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, QDF_STATUS_SUCCESS, + sta_ds->assocId, sta_ds->staAddr, + sub_type, + sta_ds, session, false); + pe_err("DUT already received an assoc request frame and STA is sending another assoc req.So, do not Process sessionid: %d sys sub_type: %d for role: %d from: " + QDF_MAC_ADDR_FMT, + session->peSessionId, sub_type, + session->limSystemRole, + QDF_MAC_ADDR_REF(hdr->sa)); + return; + } + } + + status = lim_check_sta_in_pe_entries(mac_ctx, hdr, session->peSessionId, + &dup_entry); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Reject assoc as duplicate entry is present and is already being deleted, assoc will be accepted once deletion is completed"); + /* + * This mean that the duplicate entry is present on other vdev + * and is already being deleted, so reject the assoc and lets + * peer try again to connect, once peer is deleted from + * other vdev. + */ + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + 1, hdr->sa, + sub_type, 0, session, false); + return; + } + + /* Get pointer to Re/Association Request frame body */ + frm_body = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + + if (IEEE80211_IS_MULTICAST(hdr->sa)) { + /* + * Rcvd Re/Assoc Req frame from BC/MC address Log error and + * ignore it + */ + pe_err("Rcvd: %s Req, sessionid: %d from a BC/MC address" + QDF_MAC_ADDR_FMT, + (LIM_ASSOC == sub_type) ? "Assoc" : "ReAssoc", + session->peSessionId, QDF_MAC_ADDR_REF(hdr->sa)); + return; + } + + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + (uint8_t *) frm_body, frame_len); + + if (false == lim_chk_sa_da(mac_ctx, hdr, session, sub_type)) + return; + + if (false == lim_chk_tkip(mac_ctx, hdr, session, sub_type)) + return; + + /* check for the presence of vendor IE */ + if ((session->access_policy_vendor_ie) && + (session->access_policy == + LIM_ACCESS_POLICY_RESPOND_IF_IE_IS_PRESENT)) { + if (frame_len <= LIM_ASSOC_REQ_IE_OFFSET) { + pe_debug("Received action frame of invalid len %d", + frame_len); + return; + } + if (!wlan_get_vendor_ie_ptr_from_oui( + &session->access_policy_vendor_ie[2], + 3, frm_body + LIM_ASSOC_REQ_IE_OFFSET, + frame_len - LIM_ASSOC_REQ_IE_OFFSET)) { + pe_err("Vendor ie not present and access policy is %x, Rejected association", + session->access_policy); + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, eSIR_MAC_UNSPEC_FAILURE_STATUS, + 1, hdr->sa, sub_type, 0, session, false); + return; + } + } + /* Allocate memory for the Assoc Request frame */ + assoc_req = qdf_mem_malloc(sizeof(*assoc_req)); + if (!assoc_req) + return; + + /* Parse Assoc Request frame */ + if (false == lim_chk_assoc_req_parse_error(mac_ctx, hdr, session, + assoc_req, sub_type, frm_body, frame_len)) + goto error; + + assoc_req->assocReqFrame = qdf_mem_malloc(frame_len); + if (!assoc_req->assocReqFrame) + goto error; + + qdf_mem_copy((uint8_t *) assoc_req->assocReqFrame, + (uint8_t *) frm_body, frame_len); + assoc_req->assocReqFrameLength = frame_len; + + if (false == lim_chk_capab(mac_ctx, hdr, session, assoc_req, + sub_type, &local_cap)) + goto error; + + if (false == lim_chk_ssid(mac_ctx, hdr, session, assoc_req, sub_type)) + goto error; + + if (false == lim_chk_rates(mac_ctx, hdr, session, assoc_req, sub_type)) + goto error; + + if (false == lim_chk_11g_only(mac_ctx, hdr, session, assoc_req, + sub_type)) + goto error; + + if (false == lim_chk_11n_only(mac_ctx, hdr, session, assoc_req, + sub_type)) + goto error; + + if (false == lim_chk_11ac_only(mac_ctx, hdr, session, assoc_req, + sub_type)) + goto error; + + if (false == lim_chk_11ax_only(mac_ctx, hdr, session, assoc_req, + sub_type)) + goto error; + + if (false == lim_check_11ax_basic_mcs(mac_ctx, hdr, session, assoc_req, + sub_type)) + goto error; + + /* Spectrum Management (11h) specific checks */ + lim_process_for_spectrum_mgmt(mac_ctx, hdr, session, + assoc_req, sub_type, local_cap); + + if (false == lim_chk_mcs(mac_ctx, hdr, session, assoc_req, sub_type)) + goto error; + + if (false == lim_chk_is_11b_sta_supported(mac_ctx, hdr, session, + assoc_req, sub_type, phy_mode)) + goto error; + + /* + * Check for 802.11n HT caps compatibility; are HT Capabilities + * turned on in lim? + */ + lim_print_ht_cap(mac_ctx, session, assoc_req); + + if (false == lim_chk_n_process_wpa_rsn_ie(mac_ctx, hdr, session, + assoc_req, sub_type, + &pmf_connection, + &akm_type)) + goto error; + + /* Extract pre-auth context for the STA, if any. */ + sta_pre_auth_ctx = lim_search_pre_auth_list(mac_ctx, hdr->sa); + + /* SAE authentication is offloaded to hostapd. Hostapd sends + * authentication status to driver after completing SAE + * authentication (after sending out 4/4 SAE auth frame). + * There is a possible race condition where driver gets + * assoc request from SAE station before getting authentication + * status from hostapd. Don't reject the association in such + * cases and defer the processing of assoc request frame by caching + * the frame and process it when the auth status is received. + */ + if (sta_pre_auth_ctx && + sta_pre_auth_ctx->authType == eSIR_AUTH_TYPE_SAE && + sta_pre_auth_ctx->mlmState == eLIM_MLM_WT_SAE_AUTH_STATE) { + pe_debug("Received assoc request frame while SAE authentication is in progress; Defer association request handling till SAE auth status is received"); + lim_defer_sme_indication(mac_ctx, session, sub_type, hdr, + assoc_req, pmf_connection, + assoc_req_copied, dup_entry, sta_ds); + return; + } + + if (session->opmode == QDF_P2P_GO_MODE) { + /* + * WAR: In P2P GO mode, if the P2P client device + * is only HT capable and not VHT capable, but the P2P + * GO device is VHT capable and advertises 2x2 NSS with + * HT capablity client device, which results in IOT + * issues. + * When GO is operating in DBS mode, GO beacons + * advertise 2x2 capability but include OMN IE to + * indicate current operating mode of 1x1. But here + * peer device is only HT capable and will not + * understand OMN IE. + */ + force_1x1 = wlan_p2p_check_oui_and_force_1x1( + frm_body + LIM_ASSOC_REQ_IE_OFFSET, + frame_len - LIM_ASSOC_REQ_IE_OFFSET); + } + + /* Send assoc indication to SME */ + if (!lim_send_assoc_ind_to_sme(mac_ctx, session, sub_type, hdr, + assoc_req, akm_type, pmf_connection, + &assoc_req_copied, dup_entry, force_1x1)) + goto error; + + return; + +error: + lim_process_assoc_cleanup(mac_ctx, session, assoc_req, sta_ds, + assoc_req_copied); + return; +} + +#ifdef FEATURE_WLAN_WAPI +/** + * lim_fill_assoc_ind_wapi_info()- Updates WAPI data in assoc indication + * @mac_ctx: Global Mac context + * @assoc_req: pointer to association request + * @assoc_ind: Pointer to association indication + * @wpsie: WPS IE + * + * This function updates WAPI meta data in association indication message + * sent to SME. + * + * Return: None + */ +static void lim_fill_assoc_ind_wapi_info(struct mac_context *mac_ctx, + tpSirAssocReq assoc_req, tpLimMlmAssocInd assoc_ind, + const uint8_t *wpsie) +{ + if (assoc_req->wapiPresent && (!wpsie)) { + pe_debug("Received WAPI IE length in Assoc Req is %d", + assoc_req->wapi.length); + assoc_ind->wapiIE.wapiIEdata[0] = WLAN_ELEMID_WAPI; + assoc_ind->wapiIE.wapiIEdata[1] = assoc_req->wapi.length; + qdf_mem_copy(&assoc_ind->wapiIE.wapiIEdata[2], + assoc_req->wapi.info, assoc_req->wapi.length); + assoc_ind->wapiIE.length = + 2 + assoc_req->wapi.length; + } + return; +} +#else +static void lim_fill_assoc_ind_wapi_info( + struct mac_context *mac_ctx, + tpSirAssocReq assoc_req, tpLimMlmAssocInd assoc_ind, + const uint8_t *wpsie) +{ +} +#endif + +/** + * lim_fill_assoc_ind_vht_info() - Updates VHT information in assoc indication + * @mac_ctx: Global Mac context + * @assoc_req: pointer to association request + * @session_entry: PE session entry + * @assoc_ind: Pointer to association indication + * + * This function updates VHT information in association indication message + * sent to SME. + * + * Return: None + */ +static void lim_fill_assoc_ind_vht_info(struct mac_context *mac_ctx, + struct pe_session *session_entry, + tpSirAssocReq assoc_req, + tpLimMlmAssocInd assoc_ind, + tpDphHashNode sta_ds) +{ + uint8_t chan; + uint8_t i; + bool nw_type_11b = true; + + if (session_entry->limRFBand == REG_BAND_2G) { + if (session_entry->vhtCapability && assoc_req->VHTCaps.present) + assoc_ind->chan_info.info = MODE_11AC_VHT20_2G; + else if (session_entry->htCapability + && assoc_req->HTCaps.present) + assoc_ind->chan_info.info = MODE_11NG_HT20; + else { + for (i = 0; i < SIR_NUM_11A_RATES; i++) { + if (sirIsArate(sta_ds-> + supportedRates.llaRates[i] + & 0x7F)) { + assoc_ind->chan_info.info = MODE_11G; + nw_type_11b = false; + break; + } + } + if (nw_type_11b) + assoc_ind->chan_info.info = MODE_11B; + } + return; + } + + if (session_entry->vhtCapability && assoc_req->VHTCaps.present) { + if ((session_entry->ch_width > CH_WIDTH_40MHZ) + && assoc_req->HTCaps.supportedChannelWidthSet) { + chan = session_entry->ch_center_freq_seg0; + assoc_ind->chan_info.band_center_freq1 = + cds_chan_to_freq(chan); + assoc_ind->chan_info.info = MODE_11AC_VHT80; + return; + } + + if ((session_entry->ch_width == CH_WIDTH_40MHZ) + && assoc_req->HTCaps.supportedChannelWidthSet) { + assoc_ind->chan_info.info = MODE_11AC_VHT40; + if (session_entry->htSecondaryChannelOffset == + PHY_DOUBLE_CHANNEL_LOW_PRIMARY) + assoc_ind->chan_info.band_center_freq1 += 10; + else + assoc_ind->chan_info.band_center_freq1 -= 10; + return; + } + + assoc_ind->chan_info.info = MODE_11AC_VHT20; + return; + } + + if (session_entry->htCapability && assoc_req->HTCaps.present) { + if ((session_entry->ch_width == CH_WIDTH_40MHZ) + && assoc_req->HTCaps.supportedChannelWidthSet) { + assoc_ind->chan_info.info = MODE_11NA_HT40; + if (session_entry->htSecondaryChannelOffset == + PHY_DOUBLE_CHANNEL_LOW_PRIMARY) + assoc_ind->chan_info.band_center_freq1 += 10; + else + assoc_ind->chan_info.band_center_freq1 -= 10; + return; + } + + assoc_ind->chan_info.info = MODE_11NA_HT20; + return; + } + + assoc_ind->chan_info.info = MODE_11A; + return; +} + +static uint8_t lim_get_max_rate_idx(tSirMacRateSet *rateset) +{ + uint8_t maxidx; + int i; + + maxidx = rateset->rate[0] & 0x7f; + for (i = 1; i < rateset->numRates; i++) { + if ((rateset->rate[i] & 0x7f) > maxidx) + maxidx = rateset->rate[i] & 0x7f; + } + + return maxidx; +} + +static void fill_mlm_assoc_ind_vht(tpSirAssocReq assocreq, + tpDphHashNode stads, + tpLimMlmAssocInd assocind) +{ + if (stads->mlmStaContext.vhtCapability) { + /* ampdu */ + assocind->ampdu = true; + + /* sgi */ + if (assocreq->VHTCaps.shortGI80MHz || + assocreq->VHTCaps.shortGI160and80plus80MHz) + assocind->sgi_enable = true; + + /* stbc */ + assocind->tx_stbc = assocreq->VHTCaps.txSTBC; + assocind->rx_stbc = assocreq->VHTCaps.rxSTBC; + + /* ch width */ + assocind->ch_width = stads->vhtSupportedChannelWidthSet ? + eHT_CHANNEL_WIDTH_80MHZ : + stads->htSupportedChannelWidthSet ? + eHT_CHANNEL_WIDTH_40MHZ : eHT_CHANNEL_WIDTH_20MHZ; + + /* mode */ + assocind->mode = SIR_SME_PHY_MODE_VHT; + assocind->rx_mcs_map = assocreq->VHTCaps.rxMCSMap & 0xff; + assocind->tx_mcs_map = assocreq->VHTCaps.txMCSMap & 0xff; + } +} + +/** + *lim_convert_channel_width_enum() - map between two channel width enums + *@ch_width: channel width of enum type phy_ch_width + * + *Return: channel width of enum type tSirMacHTChannelWidth + */ +static tSirMacHTChannelWidth +lim_convert_channel_width_enum(enum phy_ch_width ch_width) +{ + switch (ch_width) { + case CH_WIDTH_20MHZ: + return eHT_CHANNEL_WIDTH_20MHZ; + case CH_WIDTH_40MHZ: + return eHT_CHANNEL_WIDTH_40MHZ; + case CH_WIDTH_80MHZ: + return eHT_CHANNEL_WIDTH_80MHZ; + case CH_WIDTH_160MHZ: + return eHT_CHANNEL_WIDTH_160MHZ; + case CH_WIDTH_80P80MHZ: + return eHT_CHANNEL_WIDTH_80P80MHZ; + case CH_WIDTH_MAX: + return eHT_MAX_CHANNEL_WIDTH; + case CH_WIDTH_5MHZ: + break; + case CH_WIDTH_10MHZ: + break; + case CH_WIDTH_INVALID: + break; + } + pe_debug("invalid enum: %d", ch_width); + return eHT_CHANNEL_WIDTH_20MHZ; +} + +bool lim_fill_lim_assoc_ind_params( + tpLimMlmAssocInd assoc_ind, + struct mac_context *mac_ctx, + tpDphHashNode sta_ds, + struct pe_session *session_entry) +{ + tpSirAssocReq assoc_req; + uint16_t rsn_len; + uint32_t phy_mode; + const uint8_t *wpsie = NULL; + uint8_t maxidx, i; + bool wme_enable; + + if (!session_entry->parsedAssocReq) { + pe_err(" Parsed Assoc req is NULL"); + return false; + } + + /* Get a copy of the already parsed Assoc Request */ + assoc_req = + (tpSirAssocReq)session_entry->parsedAssocReq[sta_ds->assocId]; + + if (!assoc_req) { + pe_err("assoc req for assoc_id:%d is NULL", sta_ds->assocId); + return false; + } + + /* Get the phy_mode */ + lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); + + qdf_mem_copy((uint8_t *)assoc_ind->peerMacAddr, + (uint8_t *)sta_ds->staAddr, sizeof(tSirMacAddr)); + assoc_ind->aid = sta_ds->assocId; + qdf_mem_copy((uint8_t *)&assoc_ind->ssId, + (uint8_t *)&assoc_req->ssId, + assoc_req->ssId.length + 1); + assoc_ind->sessionId = session_entry->peSessionId; + assoc_ind->authType = sta_ds->mlmStaContext.authType; + assoc_ind->akm_type = sta_ds->mlmStaContext.akm_type; + assoc_ind->capabilityInfo = assoc_req->capabilityInfo; + + /* Fill in RSN IE information */ + assoc_ind->rsnIE.length = 0; + /* if WPS IE is present, ignore RSN IE */ + if (assoc_req->addIEPresent && assoc_req->addIE.length) { + wpsie = limGetWscIEPtr( + mac_ctx, + assoc_req->addIE.addIEdata, + assoc_req->addIE.length); + } + if (assoc_req->rsnPresent && !wpsie) { + pe_debug("Assoc Req RSN IE len: %d", + assoc_req->rsn.length); + assoc_ind->rsnIE.length = 2 + assoc_req->rsn.length; + assoc_ind->rsnIE.rsnIEdata[0] = WLAN_ELEMID_RSN; + assoc_ind->rsnIE.rsnIEdata[1] = + assoc_req->rsn.length; + qdf_mem_copy( + &assoc_ind->rsnIE.rsnIEdata[2], + assoc_req->rsn.info, + assoc_req->rsn.length); + } + /* Fill in 802.11h related info */ + if (assoc_req->powerCapabilityPresent && + assoc_req->supportedChannelsPresent) { + assoc_ind->spectrumMgtIndicator = true; + assoc_ind->powerCap.minTxPower = + assoc_req->powerCapability.minTxPower; + assoc_ind->powerCap.maxTxPower = + assoc_req->powerCapability.maxTxPower; + lim_convert_supported_channels( + mac_ctx, assoc_ind, + assoc_req); + } else { + assoc_ind->spectrumMgtIndicator = false; + } + + /* This check is to avoid extra Sec IEs present incase of WPS */ + if (assoc_req->wpaPresent && !wpsie) { + rsn_len = assoc_ind->rsnIE.length; + if ((rsn_len + assoc_req->wpa.length) + >= WLAN_MAX_IE_LEN) { + pe_err("rsnIEdata index out of bounds: %d", + rsn_len); + return false; + } + assoc_ind->rsnIE.rsnIEdata[rsn_len] = + SIR_MAC_WPA_EID; + assoc_ind->rsnIE.rsnIEdata[rsn_len + 1] + = assoc_req->wpa.length; + qdf_mem_copy( + &assoc_ind->rsnIE.rsnIEdata[rsn_len + 2], + assoc_req->wpa.info, assoc_req->wpa.length); + assoc_ind->rsnIE.length += 2 + assoc_req->wpa.length; + } + lim_fill_assoc_ind_wapi_info(mac_ctx, assoc_req, assoc_ind, wpsie); + + assoc_ind->addIE.length = 0; + if (assoc_req->addIEPresent) { + qdf_mem_copy( + &assoc_ind->addIE.addIEdata, + assoc_req->addIE.addIEdata, + assoc_req->addIE.length); + assoc_ind->addIE.length = assoc_req->addIE.length; + } + /* + * Add HT Capabilities into addIE for OBSS + * processing in hostapd + */ + if (assoc_req->HTCaps.present) { + qdf_mem_copy(&assoc_ind->ht_caps, &assoc_req->HTCaps, + sizeof(tDot11fIEHTCaps)); + rsn_len = assoc_ind->addIE.length; + if (assoc_ind->addIE.length + DOT11F_IE_HTCAPS_MIN_LEN + + 2 < WLAN_MAX_IE_LEN) { + assoc_ind->addIE.addIEdata[rsn_len] = + WLAN_ELEMID_HTCAP_ANA; + assoc_ind->addIE.addIEdata[rsn_len + 1] = + DOT11F_IE_HTCAPS_MIN_LEN; + qdf_mem_copy( + &assoc_ind->addIE.addIEdata[rsn_len + 2], + ((uint8_t *)&assoc_req->HTCaps) + 1, + DOT11F_IE_HTCAPS_MIN_LEN); + assoc_ind->addIE.length += + 2 + DOT11F_IE_HTCAPS_MIN_LEN; + } else { + pe_err("Fail:HT capabilities IE to addIE"); + } + } + + if (assoc_req->wmeInfoPresent) { + /* Set whether AP is enabled with WMM or not */ + wme_enable = mac_ctx->mlme_cfg->wmm_params.wme_enabled; + assoc_ind->WmmStaInfoPresent = wme_enable; + /* + * Note: we are not rejecting association here + * because IOT will fail + */ + } + /* Required for indicating the frames to upper layer */ + assoc_ind->assocReqLength = assoc_req->assocReqFrameLength; + assoc_ind->assocReqPtr = assoc_req->assocReqFrame; + assoc_ind->beaconPtr = session_entry->beacon; + assoc_ind->beaconLength = session_entry->bcnLen; + + assoc_ind->chan_info.mhz = session_entry->curr_op_freq; + assoc_ind->chan_info.band_center_freq1 = + session_entry->curr_op_freq; + assoc_ind->chan_info.band_center_freq2 = 0; + assoc_ind->chan_info.reg_info_1 = + (session_entry->maxTxPower << 16); + assoc_ind->chan_info.reg_info_2 = + (session_entry->maxTxPower << 8); + assoc_ind->chan_info.nss = sta_ds->nss; + assoc_ind->chan_info.rate_flags = + lim_get_max_rate_flags(mac_ctx, sta_ds); + assoc_ind->ampdu = false; + assoc_ind->sgi_enable = false; + assoc_ind->tx_stbc = false; + assoc_ind->rx_stbc = false; + assoc_ind->ch_width = eHT_CHANNEL_WIDTH_20MHZ; + assoc_ind->mode = SIR_SME_PHY_MODE_LEGACY; + assoc_ind->max_supp_idx = 0xff; + assoc_ind->max_ext_idx = 0xff; + assoc_ind->max_mcs_idx = 0xff; + assoc_ind->rx_mcs_map = 0xff; + assoc_ind->tx_mcs_map = 0xff; + + if (assoc_req->supportedRates.numRates) + assoc_ind->max_supp_idx = + lim_get_max_rate_idx(&assoc_req->supportedRates); + if (assoc_req->extendedRates.numRates) + assoc_ind->max_ext_idx = + lim_get_max_rate_idx(&assoc_req->extendedRates); + + if (sta_ds->mlmStaContext.htCapability) { + /* ampdu */ + assoc_ind->ampdu = true; + + /* sgi */ + if (sta_ds->htShortGI20Mhz || sta_ds->htShortGI40Mhz) + assoc_ind->sgi_enable = true; + + /* stbc */ + assoc_ind->tx_stbc = assoc_req->HTCaps.txSTBC; + assoc_ind->rx_stbc = assoc_req->HTCaps.rxSTBC; + + /* ch width */ + assoc_ind->ch_width = + sta_ds->htSupportedChannelWidthSet ? + eHT_CHANNEL_WIDTH_40MHZ : + eHT_CHANNEL_WIDTH_20MHZ; + /* mode */ + assoc_ind->mode = SIR_SME_PHY_MODE_HT; + maxidx = 0; + for (i = 0; i < 8; i++) { + if (assoc_req->HTCaps.supportedMCSSet[0] & + (1 << i)) + maxidx = i; + } + assoc_ind->max_mcs_idx = maxidx; + } + fill_mlm_assoc_ind_vht(assoc_req, sta_ds, assoc_ind); + if (assoc_req->ExtCap.present) + assoc_ind->ecsa_capable = + ((struct s_ext_cap *)assoc_req->ExtCap.bytes)->ext_chan_switch; + /* updates VHT information in assoc indication */ + if (assoc_req->VHTCaps.present) + qdf_mem_copy(&assoc_ind->vht_caps, &assoc_req->VHTCaps, + sizeof(tDot11fIEVHTCaps)); + else if (assoc_req->vendor_vht_ie.VHTCaps.present) + qdf_mem_copy(&assoc_ind->vht_caps, + &assoc_req->vendor_vht_ie.VHTCaps, + sizeof(tDot11fIEVHTCaps)); + + lim_fill_assoc_ind_vht_info(mac_ctx, session_entry, assoc_req, + assoc_ind, sta_ds); + assoc_ind->he_caps_present = assoc_req->he_cap.present; + assoc_ind->is_sae_authenticated = + assoc_req->is_sae_authenticated; + /* updates HE bandwidth in assoc indication */ + if (wlan_reg_is_6ghz_chan_freq(session_entry->curr_op_freq)) + assoc_ind->ch_width = + lim_convert_channel_width_enum(sta_ds->ch_width); + return true; +} + +void lim_send_mlm_assoc_ind(struct mac_context *mac_ctx, + tpDphHashNode sta_ds, + struct pe_session *session_entry) +{ + tpLimMlmAssocInd assoc_ind; + tpSirAssocReq assoc_req; + uint16_t temp; + uint32_t phy_mode; + uint8_t sub_type; + + if (!session_entry->parsedAssocReq) { + pe_err(" Parsed Assoc req is NULL"); + return; + } + + /* Get a copy of the already parsed Assoc Request */ + assoc_req = + (tpSirAssocReq) session_entry->parsedAssocReq[sta_ds->assocId]; + + if (!assoc_req) { + pe_err("assoc req for assoc_id:%d is NULL", sta_ds->assocId); + return; + } + + /* Get the phy_mode */ + lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); + + /* Determine if its Assoc or ReAssoc Request */ + if (assoc_req->reassocRequest == 1) + sub_type = LIM_REASSOC; + else + sub_type = LIM_ASSOC; + + pe_debug("Sessionid: %d ssid: %s sub_type: %d Associd: %d staAddr: " + QDF_MAC_ADDR_FMT, session_entry->peSessionId, + assoc_req->ssId.ssId, sub_type, sta_ds->assocId, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + + if (sub_type == LIM_ASSOC || sub_type == LIM_REASSOC) { + temp = sizeof(tLimMlmAssocInd); + + assoc_ind = qdf_mem_malloc(temp); + if (!assoc_ind) { + lim_release_peer_idx(mac_ctx, sta_ds->assocId, + session_entry); + return; + } + if (!lim_fill_lim_assoc_ind_params(assoc_ind, mac_ctx, + sta_ds, session_entry)) { + qdf_mem_free(assoc_ind); + return; + } + lim_post_sme_message(mac_ctx, LIM_MLM_ASSOC_IND, + (uint32_t *)assoc_ind); + qdf_mem_free(assoc_ind); + } + + return; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c new file mode 100644 index 0000000000000000000000000000000000000000..0c02b3eb2b4e835c5bd605c7c6a1d1d36953bcbc --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c @@ -0,0 +1,1166 @@ +/* + * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_process_assoc_rsp_frame.cc contains the code + * for processing Re/Association Response Frame. + * Author: Chandra Modumudi + * Date: 03/18/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "wni_api.h" +#include "wni_cfg.h" +#include "ani_global.h" +#include "sch_api.h" + +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_send_messages.h" +#include "lim_process_fils.h" +#include "wlan_blm_api.h" + +/** + * lim_update_stads_htcap() - Updates station Descriptor HT capability + * @mac_ctx: Pointer to Global MAC structure + * @sta_ds: Station Descriptor in DPH + * @assoc_rsp: Pointer to Association Response Structure + * @session_entry : PE session Entry + * + * This function is called to Update the HT capabilities in + * Station Descriptor (dph) Details from + * Association / ReAssociation Response Frame + * + * Return: None + */ +static void lim_update_stads_htcap(struct mac_context *mac_ctx, + tpDphHashNode sta_ds, tpSirAssocRsp assoc_rsp, + struct pe_session *session_entry) +{ + uint16_t highest_rxrate = 0; + tDot11fIEHTCaps *ht_caps; + + ht_caps = &assoc_rsp->HTCaps; + sta_ds->mlmStaContext.htCapability = assoc_rsp->HTCaps.present; + if (assoc_rsp->HTCaps.present) { + sta_ds->htGreenfield = + (uint8_t) ht_caps->greenField; + if (session_entry->htSupportedChannelWidthSet) { + sta_ds->htSupportedChannelWidthSet = + (uint8_t) (ht_caps->supportedChannelWidthSet ? + assoc_rsp->HTInfo.recommendedTxWidthSet : + ht_caps->supportedChannelWidthSet); + } else + sta_ds->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_20MHZ; + sta_ds->htLsigTXOPProtection = + (uint8_t) ht_caps->lsigTXOPProtection; + sta_ds->htMIMOPSState = + (tSirMacHTMIMOPowerSaveState)ht_caps->mimoPowerSave; + sta_ds->htMaxAmsduLength = + (uint8_t) ht_caps->maximalAMSDUsize; + sta_ds->htAMpduDensity = ht_caps->mpduDensity; + sta_ds->htDsssCckRate40MHzSupport = + (uint8_t) ht_caps->dsssCckMode40MHz; + sta_ds->htMaxRxAMpduFactor = + ht_caps->maxRxAMPDUFactor; + lim_fill_rx_highest_supported_rate(mac_ctx, &highest_rxrate, + ht_caps->supportedMCSSet); + sta_ds->supportedRates.rxHighestDataRate = + highest_rxrate; + /* + * This is for AP as peer STA and we are INFRA STA + *.We will put APs offset in dph node which is peer STA + */ + sta_ds->htSecondaryChannelOffset = + (uint8_t) assoc_rsp->HTInfo.secondaryChannelOffset; + + /* Check if we have support for gShortGI20Mhz and + * gShortGI40Mhz from ini file + */ + if (session_entry->ht_config.ht_sgi20) + sta_ds->htShortGI20Mhz = + (uint8_t)assoc_rsp->HTCaps.shortGI20MHz; + else + sta_ds->htShortGI20Mhz = false; + + if (session_entry->ht_config.ht_sgi40) + sta_ds->htShortGI40Mhz = + (uint8_t)assoc_rsp->HTCaps.shortGI40MHz; + else + sta_ds->htShortGI40Mhz = false; + } +} + +/** + * lim_update_assoc_sta_datas() - Updates station Descriptor + * mac_ctx: Pointer to Global MAC structure + * sta_ds: Station Descriptor in DPH + * assoc_rsp: Pointer to Association Response Structure + * session_entry : PE session Entry + * + * This function is called to Update the Station Descriptor (dph) Details from + * Association / ReAssociation Response Frame + * + * Return: None + */ +void lim_update_assoc_sta_datas(struct mac_context *mac_ctx, + tpDphHashNode sta_ds, tpSirAssocRsp assoc_rsp, + struct pe_session *session_entry, tSchBeaconStruct *beacon) +{ + uint32_t phy_mode; + bool qos_mode; + tDot11fIEVHTCaps *vht_caps = NULL; + tDot11fIEhe_cap *he_cap = NULL; + struct bss_description *bss_desc = NULL; + + lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); + sta_ds->staType = STA_ENTRY_SELF; + limGetQosMode(session_entry, &qos_mode); + sta_ds->mlmStaContext.authType = session_entry->limCurrentAuthType; + + /* Add capabilities information, rates and AID */ + sta_ds->mlmStaContext.capabilityInfo = assoc_rsp->capabilityInfo; + sta_ds->shortPreambleEnabled = + (uint8_t) assoc_rsp->capabilityInfo.shortPreamble; + + /* Update HT Capabilities only when the self mode supports HT */ + if (IS_DOT11_MODE_HT(session_entry->dot11mode)) + lim_update_stads_htcap(mac_ctx, sta_ds, assoc_rsp, + session_entry); + + if (assoc_rsp->VHTCaps.present) + vht_caps = &assoc_rsp->VHTCaps; + else if (assoc_rsp->vendor_vht_ie.VHTCaps.present) + vht_caps = &assoc_rsp->vendor_vht_ie.VHTCaps; + + if (session_entry->vhtCapability && (vht_caps && vht_caps->present)) { + sta_ds->mlmStaContext.vhtCapability = + vht_caps->present; + /* + * If 11ac is supported and if the peer is + * sending VHT capabilities, + * then htMaxRxAMpduFactor should be + * overloaded with VHT maxAMPDULenExp + */ + sta_ds->htMaxRxAMpduFactor = vht_caps->maxAMPDULenExp; + if (session_entry->htSupportedChannelWidthSet) { + if (assoc_rsp->VHTOperation.present) + sta_ds->vhtSupportedChannelWidthSet = + assoc_rsp->VHTOperation.chanWidth; + else + sta_ds->vhtSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + } + sta_ds->vht_mcs_10_11_supp = 0; + if (mac_ctx->mlme_cfg->vht_caps.vht_cap_info. + vht_mcs_10_11_supp && + assoc_rsp->qcn_ie.present && + assoc_rsp->qcn_ie.vht_mcs11_attr.present) + sta_ds->vht_mcs_10_11_supp = + assoc_rsp->qcn_ie.vht_mcs11_attr. + vht_mcs_10_11_supp; + } + + if (IS_DOT11_MODE_HE(session_entry->dot11mode)) + lim_update_stads_he_caps(sta_ds, assoc_rsp, + session_entry, beacon); + + if (lim_is_sta_he_capable(sta_ds)) + he_cap = &assoc_rsp->he_cap; + + if (session_entry->lim_join_req) + bss_desc = &session_entry->lim_join_req->bssDescription; + + if (lim_populate_peer_rate_set(mac_ctx, &sta_ds->supportedRates, + assoc_rsp->HTCaps.supportedMCSSet, + false, session_entry, + vht_caps, he_cap, sta_ds, + bss_desc) != + QDF_STATUS_SUCCESS) { + pe_err("could not get rateset and extended rate set"); + return; + } + sta_ds->vhtSupportedRxNss = + ((sta_ds->supportedRates.vhtRxMCSMap & MCSMAPMASK2x2) + == MCSMAPMASK2x2) ? 1 : 2; + + /* If one of the rates is 11g rates, set the ERP mode. */ + if ((phy_mode == WNI_CFG_PHY_MODE_11G) && + sirIsArate(sta_ds->supportedRates.llaRates[0] & 0x7f)) + sta_ds->erpEnabled = eHAL_SET; + + /* Could not get prop rateset from CFG. Log error. */ + sta_ds->qosMode = 0; + sta_ds->lleEnabled = 0; + + /* update TSID to UP mapping */ + if (qos_mode) { + if (assoc_rsp->edcaPresent) { + QDF_STATUS status; + + status = + sch_beacon_edca_process(mac_ctx, + &assoc_rsp->edca, session_entry); + pe_debug("Edca set update based on AssocRsp: status %d", + status); + if (status != QDF_STATUS_SUCCESS) { + pe_err("Edca error in AssocResp"); + } else { + /* update default tidmap based on ACM */ + sta_ds->qosMode = 1; + sta_ds->lleEnabled = 1; + } + } + } + + sta_ds->wmeEnabled = 0; + sta_ds->wsmEnabled = 0; + if (session_entry->limWmeEnabled && assoc_rsp->wmeEdcaPresent) { + QDF_STATUS status; + + status = sch_beacon_edca_process(mac_ctx, &assoc_rsp->edca, + session_entry); + pe_debug("WME Edca set update based on AssocRsp: status %d", + status); + + if (status != QDF_STATUS_SUCCESS) + pe_err("WME Edca error in AssocResp - ignoring"); + + else { + /* update default tidmap based on HashACM */ + sta_ds->qosMode = 1; + sta_ds->wmeEnabled = 1; + } + } else { + /* + * We received assoc rsp from a legacy AP. + * So fill in the default local EDCA params. + * This is needed (refer to bug #14989) as we'll + * be passing the gLimEdcaParams to HAL in + * lim_process_sta_mlm_add_bss_rsp(). + */ + sch_set_default_edca_params(mac_ctx, session_entry); + } + + if (qos_mode && (!sta_ds->qosMode) && + sta_ds->mlmStaContext.htCapability) { + /* + * Enable QOS for all HT AP's even though WMM + * or 802.11E IE is not present + */ + sta_ds->qosMode = 1; + sta_ds->wmeEnabled = 1; + } +#ifdef WLAN_FEATURE_11W + if (session_entry->limRmfEnabled) + sta_ds->rmfEnabled = 1; +#endif +} + +/** + * lim_update_ric_data() - update session with ric data + * @mac_ctx: Pointer to Global MAC structure + * @session_entry: PE session handle + * @assoc_rsp: pointer to assoc response + * + * This function is called by lim_process_assoc_rsp_frame() to + * update PE session context with RIC data. + * + * Return: None + */ +static void lim_update_ric_data(struct mac_context *mac_ctx, + struct pe_session *session_entry, tpSirAssocRsp assoc_rsp) +{ + if (session_entry->ricData) { + qdf_mem_free(session_entry->ricData); + session_entry->ricData = NULL; + session_entry->RICDataLen = 0; + } + if (assoc_rsp->ricPresent) { + session_entry->RICDataLen = + assoc_rsp->num_RICData * sizeof(tDot11fIERICDataDesc); + if (session_entry->RICDataLen) { + session_entry->ricData = + qdf_mem_malloc(session_entry->RICDataLen); + if (!session_entry->ricData) + session_entry->RICDataLen = 0; + else + qdf_mem_copy(session_entry->ricData, + &assoc_rsp->RICData[0], + session_entry->RICDataLen); + } else { + pe_err("RIC data not present"); + } + } else { + session_entry->RICDataLen = 0; + session_entry->ricData = NULL; + } + return; +} + +#ifdef FEATURE_WLAN_ESE +/** + * lim_update_ese_tspec() - update session with Tspec info. + * @mac_ctx: Pointer to Global MAC structure + * @session_entry: PE session handle + * @assoc_rsp: pointer to assoc response + * + * This function is called by lim_process_assoc_rsp_frame() to + * update PE session context with Tspec data. + * + * Return: None + */ +static void lim_update_ese_tspec(struct mac_context *mac_ctx, + struct pe_session *session_entry, tpSirAssocRsp assoc_rsp) +{ + if (session_entry->tspecIes) { + qdf_mem_free(session_entry->tspecIes); + session_entry->tspecIes = NULL; + session_entry->tspecLen = 0; + } + if (assoc_rsp->tspecPresent) { + pe_debug("Tspec EID present in assoc rsp"); + session_entry->tspecLen = + assoc_rsp->num_tspecs * sizeof(tDot11fIEWMMTSPEC); + if (session_entry->tspecLen) { + session_entry->tspecIes = + qdf_mem_malloc(session_entry->tspecLen); + if (!session_entry->tspecIes) + session_entry->tspecLen = 0; + else + qdf_mem_copy(session_entry->tspecIes, + &assoc_rsp->TSPECInfo[0], + session_entry->tspecLen); + } else { + pe_err("TSPEC has Zero length"); + } + } else { + session_entry->tspecLen = 0; + session_entry->tspecIes = NULL; + } + return; +} + +/** + * lim_update_ese_tsm() - update session with TSM info. + * @mac_ctx: Pointer to Global MAC structure + * @session_entry: PE session handle + * @assoc_rsp: pointer to assoc response + * + * This function is called by lim_process_assoc_rsp_frame() to + * update PE session context with TSM IE data and send + * eWNI_TSM_IE_IND to SME. + * + * Return: None + */ +static void lim_update_ese_tsm(struct mac_context *mac_ctx, + struct pe_session *session_entry, tpSirAssocRsp assoc_rsp) +{ + uint8_t cnt = 0; + tpEseTSMContext tsm_ctx; + + pe_debug("TSM IE Present in Reassoc Rsp"); + /* + * Start the TSM timer only if the TSPEC + * Ie is present in the reassoc rsp + */ + if (!assoc_rsp->tspecPresent) { + pe_debug("TSM present but TSPEC IE not present"); + return; + } + tsm_ctx = &session_entry->eseContext.tsm; + /* Find the TSPEC IE with VO user priority */ + for (cnt = 0; cnt < assoc_rsp->num_tspecs; cnt++) { + if (upToAc(assoc_rsp->TSPECInfo[cnt].user_priority) == + QCA_WLAN_AC_VO) { + tsm_ctx->tid = + assoc_rsp->TSPECInfo[cnt].user_priority; + qdf_mem_copy(&tsm_ctx->tsmInfo, + &assoc_rsp->tsmIE, sizeof(struct ese_tsm_ie)); + lim_send_sme_tsm_ie_ind(mac_ctx, + session_entry, assoc_rsp->tsmIE.tsid, + assoc_rsp->tsmIE.state, + assoc_rsp->tsmIE.msmt_interval); + if (tsm_ctx->tsmInfo.state) + tsm_ctx->tsmMetrics.RoamingCount++; + break; + } + } +} +#endif + +/** + * lim_update_stads_ext_cap() - update sta ds with ext cap + * @mac_ctx: Pointer to Global MAC structure + * @session_entry: PE session handle + * @assoc_rsp: pointer to assoc response + * + * This function is called by lim_process_assoc_rsp_frame() to + * update STA DS with ext capablities. + * + * Return: None + */ +static void lim_update_stads_ext_cap(struct mac_context *mac_ctx, + struct pe_session *session_entry, tpSirAssocRsp assoc_rsp, + tpDphHashNode sta_ds) +{ + struct s_ext_cap *ext_cap; + + if (!assoc_rsp->ExtCap.present) { + sta_ds->timingMeasCap = 0; +#ifdef FEATURE_WLAN_TDLS + session_entry->tdls_prohibited = false; + session_entry->tdls_chan_swit_prohibited = false; +#endif + pe_debug("ExtCap not present"); + return; + } + + ext_cap = (struct s_ext_cap *)assoc_rsp->ExtCap.bytes; + lim_set_stads_rtt_cap(sta_ds, ext_cap, mac_ctx); +#ifdef FEATURE_WLAN_TDLS + session_entry->tdls_prohibited = ext_cap->tdls_prohibited; + session_entry->tdls_chan_swit_prohibited = + ext_cap->tdls_chan_swit_prohibited; + pe_debug("ExtCap: tdls_prohibited: %d tdls_chan_swit_prohibited: %d", + ext_cap->tdls_prohibited, + ext_cap->tdls_chan_swit_prohibited); +#endif + lim_set_peer_twt_cap(session_entry, ext_cap); +} + +/** + * lim_stop_reassoc_retry_timer() - Cleanup after reassoc response is received + * @mac_ctx: Global MAC context + * + * Stop the reassoc retry timer and release the stored reassoc request. + * + * Return: None + */ +static void lim_stop_reassoc_retry_timer(struct mac_context *mac_ctx) +{ + mac_ctx->lim.reAssocRetryAttempt = 0; + if ((mac_ctx->lim.pe_session) + && (NULL != + mac_ctx->lim.pe_session->pLimMlmReassocRetryReq)) { + qdf_mem_free( + mac_ctx->lim.pe_session->pLimMlmReassocRetryReq); + mac_ctx->lim.pe_session->pLimMlmReassocRetryReq = NULL; + } + lim_deactivate_and_change_timer(mac_ctx, eLIM_REASSOC_FAIL_TIMER); +} + +/** + * lim_get_nss_supported_by_ap() - finds out nss from AP's beacons + * @vht_caps: VHT capabilities + * @ht_caps: HT capabilities + * + * Return: nss advertised by AP in beacon + */ +static uint8_t lim_get_nss_supported_by_ap(tDot11fIEVHTCaps *vht_caps, + tDot11fIEHTCaps *ht_caps, + tDot11fIEhe_cap *he_caps) +{ + + if (he_caps->present) { + if ((he_caps->rx_he_mcs_map_lt_80 & 0xC0) != 0xC0) + return NSS_4x4_MODE; + + if ((he_caps->rx_he_mcs_map_lt_80 & 0x30) != 0x30) + return NSS_3x3_MODE; + + if ((he_caps->rx_he_mcs_map_lt_80 & 0x0C) != 0x0C) + return NSS_2x2_MODE; + } else if (vht_caps->present) { + if ((vht_caps->rxMCSMap & 0xC0) != 0xC0) + return NSS_4x4_MODE; + + if ((vht_caps->rxMCSMap & 0x30) != 0x30) + return NSS_3x3_MODE; + + if ((vht_caps->rxMCSMap & 0x0C) != 0x0C) + return NSS_2x2_MODE; + } else if (ht_caps->present) { + if (ht_caps->supportedMCSSet[3]) + return NSS_4x4_MODE; + + if (ht_caps->supportedMCSSet[2]) + return NSS_3x3_MODE; + + if (ht_caps->supportedMCSSet[1]) + return NSS_2x2_MODE; + } + + return NSS_1x1_MODE; +} + +#ifdef WLAN_FEATURE_11AX +static void lim_process_he_info(tpSirProbeRespBeacon beacon, + tpDphHashNode sta_ds) +{ + if (beacon->he_op.present) + sta_ds->parsed_ies.he_operation = beacon->he_op; +} +#else +static inline void lim_process_he_info(tpSirProbeRespBeacon beacon, + tpDphHashNode sta_ds) +{ +} +#endif + +#ifdef WLAN_FEATURE_11W + +#define MAX_RETRY_TIMER 1500 +static QDF_STATUS +lim_handle_pmfcomeback_timer(struct pe_session *session_entry, + tpSirAssocRsp assoc_rsp) +{ + uint16_t timeout_value; + + if (session_entry->opmode != QDF_STA_MODE) + return QDF_STATUS_E_FAILURE; + + if (session_entry->limRmfEnabled && + session_entry->pmf_retry_timer_info.retried && + assoc_rsp->status_code == eSIR_MAC_TRY_AGAIN_LATER) { + pe_debug("Already retry in progress"); + return QDF_STATUS_SUCCESS; + } + + /* + * Handle association Response for sta mode with RMF enabled and TRY + * again later with timeout interval and Assoc comeback type + */ + if (!session_entry->limRmfEnabled || assoc_rsp->status_code != + eSIR_MAC_TRY_AGAIN_LATER || !assoc_rsp->TimeoutInterval.present || + assoc_rsp->TimeoutInterval.timeoutType != + SIR_MAC_TI_TYPE_ASSOC_COMEBACK || + session_entry->pmf_retry_timer_info.retried) + return QDF_STATUS_E_FAILURE; + + timeout_value = assoc_rsp->TimeoutInterval.timeoutValue; + if (timeout_value < 10) { + /* + * if this value is less than 10 then our timer + * will fail to start and due to this we will + * never re-attempt. Better modify the timer + * value here. + */ + timeout_value = 10; + } + timeout_value = QDF_MIN(MAX_RETRY_TIMER, timeout_value); + pe_debug("ASSOC res with eSIR_MAC_TRY_AGAIN_LATER recvd.Starting timer to wait timeout: %d", + timeout_value); + if (QDF_STATUS_SUCCESS != + qdf_mc_timer_start(&session_entry->pmf_retry_timer, + timeout_value)) { + pe_err("Failed to start comeback timer"); + return QDF_STATUS_E_FAILURE; + } + session_entry->pmf_retry_timer_info.retried = true; + + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS +lim_handle_pmfcomeback_timer(struct pe_session *session_entry, + tpSirAssocRsp assoc_rsp) +{ + return QDF_STATUS_E_FAILURE; +} +#endif + +/** + * lim_process_assoc_rsp_frame() - Processes assoc response + * @mac_ctx: Pointer to Global MAC structure + * @rx_packet_info - A pointer to Rx packet info structure + * @sub_type - Indicates whether it is Association Response (=0) or + * Reassociation Response (=1) frame + * + * This function is called by limProcessMessageQueue() upon + * Re/Association Response frame reception. + * + * Return: None + */ + +void +lim_process_assoc_rsp_frame(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, uint8_t subtype, struct pe_session *session_entry) +{ + uint8_t *body; + uint16_t caps, ie_len; + uint32_t frame_len; + tSirMacAddr current_bssid; + tpSirMacMgmtHdr hdr = NULL; + tSirMacCapabilityInfo mac_capab; + tpDphHashNode sta_ds; + tpSirAssocRsp assoc_rsp; + tLimMlmAssocCnf assoc_cnf; + tSchBeaconStruct *beacon; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + uint8_t vdev_id = 0; + struct csr_roam_session *roam_session; +#endif + uint8_t ap_nss; + int8_t rssi; + QDF_STATUS status; + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + vdev_id = session_entry->vdev_id; +#endif + assoc_cnf.resultCode = eSIR_SME_SUCCESS; + /* Update PE session Id */ + assoc_cnf.sessionId = session_entry->peSessionId; + + if (LIM_IS_AP_ROLE(session_entry)) { + /* + * Should not have received Re/Association + * Response frame on AP. Log error + */ + pe_err("Should not received Re/Assoc Response in role: %d", + GET_LIM_SYSTEM_ROLE(session_entry)); + return; + } + + if (lim_is_roam_synch_in_progress(session_entry)) { + hdr = (tpSirMacMgmtHdr) mac_ctx->roam.pReassocResp; + frame_len = mac_ctx->roam.reassocRespLen - SIR_MAC_HDR_LEN_3A; + rssi = 0; + } else { + hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + rssi = WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info); + } + + if (!hdr) { + pe_err("LFR3: Reassoc response packet header is NULL"); + return; + } + + pe_nofl_rl_info("Assoc rsp RX: subtype %d vdev %d sys role %d lim state %d rssi %d from " QDF_MAC_ADDR_FMT, + subtype, session_entry->vdev_id, + GET_LIM_SYSTEM_ROLE(session_entry), + session_entry->limMlmState, rssi, + QDF_MAC_ADDR_REF(hdr->sa)); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + (uint8_t *)hdr, frame_len + SIR_MAC_HDR_LEN_3A); + + beacon = qdf_mem_malloc(sizeof(tSchBeaconStruct)); + if (!beacon) + return; + + if (((subtype == LIM_ASSOC) && + (session_entry->limMlmState != eLIM_MLM_WT_ASSOC_RSP_STATE)) || + ((subtype == LIM_REASSOC) && + !lim_is_roam_synch_in_progress(session_entry) && + ((session_entry->limMlmState != eLIM_MLM_WT_REASSOC_RSP_STATE) + && (session_entry->limMlmState != + eLIM_MLM_WT_FT_REASSOC_RSP_STATE) + ))) { + /* Received unexpected Re/Association Response frame */ + pe_debug("Received Re/Assoc rsp in unexpected state: %d on session: %d", + session_entry->limMlmState, session_entry->peSessionId); + if (!hdr->fc.retry) { + if (!(mac_ctx->lim.retry_packet_cnt & 0xf)) { + pe_err("recvd Re/Assoc rsp:not a retry frame"); + lim_print_mlm_state(mac_ctx, LOGE, + session_entry->limMlmState); + } else { + mac_ctx->lim.retry_packet_cnt++; + } + } + qdf_mem_free(beacon); + return; + } + sir_copy_mac_addr(current_bssid, session_entry->bssId); + if (subtype == LIM_ASSOC) { + if (qdf_mem_cmp + (hdr->sa, current_bssid, sizeof(tSirMacAddr))) { + /* + * Received Association Response frame from an entity + * other than one to which request was initiated. + * Ignore this and wait until Assoc Failure Timeout + */ + pe_warn("received AssocRsp from unexpected peer "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + qdf_mem_free(beacon); + return; + } + } else { + if (qdf_mem_cmp + (hdr->sa, session_entry->limReAssocbssId, + sizeof(tSirMacAddr))) { + /* + * Received Reassociation Response frame from an entity + * other than one to which request was initiated. + * Ignore this and wait until Reassoc Failure Timeout. + */ + pe_warn("received ReassocRsp from unexpected peer "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); + qdf_mem_free(beacon); + return; + } + } + + assoc_rsp = qdf_mem_malloc(sizeof(*assoc_rsp)); + if (!assoc_rsp) { + qdf_mem_free(beacon); + return; + } + /* Get pointer to Re/Association Response frame body */ + if (lim_is_roam_synch_in_progress(session_entry)) + body = mac_ctx->roam.pReassocResp + SIR_MAC_HDR_LEN_3A; + else + body = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + /* parse Re/Association Response frame. */ + if (sir_convert_assoc_resp_frame2_struct(mac_ctx, session_entry, body, + frame_len, assoc_rsp) == QDF_STATUS_E_FAILURE) { + qdf_mem_free(assoc_rsp); + pe_err("Parse error Assoc resp subtype: %d" "length: %d", + frame_len, subtype); + qdf_mem_free(beacon); + return; + } + + if (!assoc_rsp->suppRatesPresent) { + pe_debug("assoc response does not have supported rate set"); + qdf_mem_copy(&assoc_rsp->supportedRates, + &session_entry->rateSet, + sizeof(tSirMacRateSet)); + } + + assoc_cnf.protStatusCode = assoc_rsp->status_code; + if (session_entry->assocRsp) { + pe_warn("session_entry->assocRsp is not NULL freeing it and setting NULL"); + qdf_mem_free(session_entry->assocRsp); + session_entry->assocRsp = NULL; + session_entry->assocRspLen = 0; + } + + if (frame_len) { + session_entry->assocRsp = qdf_mem_malloc(frame_len); + if (session_entry->assocRsp) { + /* + * Store the Assoc response. This is sent + * to csr/hdd in join cnf response. + */ + qdf_mem_copy(session_entry->assocRsp, body, frame_len); + session_entry->assocRspLen = frame_len; + } + } + + lim_update_ric_data(mac_ctx, session_entry, assoc_rsp); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + roam_session = + &mac_ctx->roam.roamSession[vdev_id]; + if (assoc_rsp->sha384_ft_subelem.r0kh_id.present) { + roam_session->ftSmeContext.r0kh_id_len = + assoc_rsp->sha384_ft_subelem.r0kh_id.num_PMK_R0_ID; + qdf_mem_copy(roam_session->ftSmeContext.r0kh_id, + assoc_rsp->sha384_ft_subelem.r0kh_id.PMK_R0_ID, + roam_session->ftSmeContext.r0kh_id_len); + } else if (assoc_rsp->FTInfo.R0KH_ID.present) { + roam_session->ftSmeContext.r0kh_id_len = + assoc_rsp->FTInfo.R0KH_ID.num_PMK_R0_ID; + qdf_mem_copy(roam_session->ftSmeContext.r0kh_id, + assoc_rsp->FTInfo.R0KH_ID.PMK_R0_ID, + roam_session->ftSmeContext.r0kh_id_len); + } else { + roam_session->ftSmeContext.r0kh_id_len = 0; + qdf_mem_zero(roam_session->ftSmeContext.r0kh_id, + SIR_ROAM_R0KH_ID_MAX_LEN); + } +#endif + +#ifdef FEATURE_WLAN_ESE + lim_update_ese_tspec(mac_ctx, session_entry, assoc_rsp); +#endif + + if (lim_get_capability_info(mac_ctx, &caps, session_entry) + != QDF_STATUS_SUCCESS) { + qdf_mem_free(assoc_rsp); + qdf_mem_free(beacon); + pe_err("could not retrieve Capabilities"); + return; + } + lim_copy_u16((uint8_t *) &mac_capab, caps); + + if (eSIR_MAC_XS_FRAME_LOSS_POOR_CHANNEL_RSSI_STATUS == + assoc_rsp->status_code && + assoc_rsp->rssi_assoc_rej.present) { + struct sir_rssi_disallow_lst ap_info = {{0}}; + + if (!assoc_rsp->rssi_assoc_rej.retry_delay) + ap_info.expected_rssi = assoc_rsp->rssi_assoc_rej.delta_rssi + + WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info) + + wlan_blm_get_rssi_blacklist_threshold(mac_ctx->pdev); + else + ap_info.expected_rssi = assoc_rsp->rssi_assoc_rej.delta_rssi + + WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info); + + ap_info.retry_delay = assoc_rsp->rssi_assoc_rej.retry_delay * + QDF_MC_TIMER_TO_MS_UNIT; + qdf_mem_copy(ap_info.bssid.bytes, hdr->sa, QDF_MAC_ADDR_SIZE); + ap_info.reject_reason = REASON_ASSOC_REJECT_OCE; + ap_info.source = ADDED_BY_DRIVER; + ap_info.original_timeout = ap_info.retry_delay; + ap_info.received_time = qdf_mc_timer_get_system_time(); + lim_add_bssid_to_reject_list(mac_ctx->pdev, &ap_info); + } + + status = lim_handle_pmfcomeback_timer(session_entry, assoc_rsp); + /* return if retry again timer is started and ignore this assoc resp */ + if (QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(beacon); + qdf_mem_free(assoc_rsp); + return; + } + + /* Stop Association failure timer */ + if (subtype == LIM_ASSOC) + lim_deactivate_and_change_timer(mac_ctx, eLIM_ASSOC_FAIL_TIMER); + else + lim_stop_reassoc_retry_timer(mac_ctx); + + if (assoc_rsp->status_code != eSIR_MAC_SUCCESS_STATUS) { + /* + *Re/Association response was received + * either with failure code. + */ + pe_err("received Re/AssocRsp frame failure code: %d", + assoc_rsp->status_code); + /* + * Need to update 'association failure' error counter + * along with STATUS CODE + * Return Assoc confirm to SME with received failure code + */ + assoc_cnf.resultCode = eSIR_SME_ASSOC_REFUSED; + /* Delete Pre-auth context for the associated BSS */ + if (lim_search_pre_auth_list(mac_ctx, hdr->sa)) + lim_delete_pre_auth_node(mac_ctx, hdr->sa); + goto assocReject; + } else if ((assoc_rsp->aid & 0x3FFF) > 2007) { + /* + * Re/Association response was received + * with invalid AID value + */ + pe_err("received Re/AssocRsp frame with invalid aid: %X", + assoc_rsp->aid); + assoc_cnf.resultCode = eSIR_SME_INVALID_ASSOC_RSP_RXED; + assoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + /* Send advisory Disassociation frame to AP */ + lim_send_disassoc_mgmt_frame(mac_ctx, + eSIR_MAC_UNSPEC_FAILURE_REASON, + hdr->sa, session_entry, false); + goto assocReject; + } + + /* + * If it is FILS connection, check is FILS params are matching + * with Authentication stage. + */ + if (!lim_verify_fils_params_assoc_rsp(mac_ctx, session_entry, + assoc_rsp, &assoc_cnf)) { + pe_err("FILS params doesnot match"); + assoc_cnf.resultCode = eSIR_SME_INVALID_ASSOC_RSP_RXED; + assoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + /* Send advisory Disassociation frame to AP */ + lim_send_disassoc_mgmt_frame(mac_ctx, + eSIR_MAC_UNSPEC_FAILURE_REASON, + hdr->sa, session_entry, false); + goto assocReject; + } + + if (assoc_rsp->QosMapSet.present) + qdf_mem_copy(&session_entry->QosMapSet, + &assoc_rsp->QosMapSet, + sizeof(struct qos_map_set)); + else + qdf_mem_zero(&session_entry->QosMapSet, + sizeof(struct qos_map_set)); + + if (assoc_rsp->obss_scanparams.present) + lim_update_obss_scanparams(session_entry, + &assoc_rsp->obss_scanparams); + + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_ROAM_ASSOC_COMP_EVENT, + session_entry, + (assoc_rsp->status_code ? QDF_STATUS_E_FAILURE : + QDF_STATUS_SUCCESS), assoc_rsp->status_code); + + ap_nss = lim_get_nss_supported_by_ap(&assoc_rsp->VHTCaps, + &assoc_rsp->HTCaps, + &assoc_rsp->he_cap); + + if (subtype == LIM_REASSOC) { + pe_debug("Successfully Reassociated with BSS"); +#ifdef FEATURE_WLAN_ESE + if (assoc_rsp->tsmPresent) + lim_update_ese_tsm(mac_ctx, session_entry, assoc_rsp); +#endif + if (session_entry->pLimMlmJoinReq) { + qdf_mem_free(session_entry->pLimMlmJoinReq); + session_entry->pLimMlmJoinReq = NULL; + } + + session_entry->limAssocResponseData = (void *)assoc_rsp; + /* + * Store the ReAssocRsp Frame in DphTable + * to be used during processing DelSta and + * DelBss to send AddBss again + */ + sta_ds = + dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session_entry->dph.dphHashTable); + + if (!sta_ds) { + pe_err("could not get hash entry at DPH for"); + lim_print_mac_addr(mac_ctx, hdr->sa, LOGE); + assoc_cnf.resultCode = + eSIR_SME_INVALID_ASSOC_RSP_RXED; + assoc_cnf.protStatusCode = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + + /* Send advisory Disassociation frame to AP */ + lim_send_disassoc_mgmt_frame(mac_ctx, + eSIR_MAC_UNSPEC_FAILURE_REASON, hdr->sa, + session_entry, false); + goto assocReject; + } + + if (ap_nss < session_entry->nss) + session_entry->nss = ap_nss; + + lim_objmgr_update_vdev_nss(mac_ctx->psoc, + session_entry->smeSessionId, + session_entry->nss); + + if ((session_entry->limMlmState == + eLIM_MLM_WT_FT_REASSOC_RSP_STATE) || + lim_is_roam_synch_in_progress(session_entry)) { + pe_debug("Sending self sta"); + lim_update_assoc_sta_datas(mac_ctx, sta_ds, assoc_rsp, + session_entry, NULL); + lim_update_stads_ext_cap(mac_ctx, session_entry, + assoc_rsp, sta_ds); + /* Store assigned AID for TIM processing */ + session_entry->limAID = assoc_rsp->aid & 0x3FFF; + /* Downgrade the EDCA parameters if needed */ + lim_set_active_edca_params(mac_ctx, + session_entry->gLimEdcaParams, + session_entry); + /* Send the active EDCA parameters to HAL */ + if (!lim_is_roam_synch_in_progress(session_entry)) { + lim_send_edca_params(mac_ctx, + session_entry->gLimEdcaParamsActive, + session_entry->vdev_id, false); + lim_add_ft_sta_self(mac_ctx, + (assoc_rsp->aid & 0x3FFF), + session_entry); + } + qdf_mem_free(beacon); + return; + } + + /* + * If we're re-associating to the same BSS, + * we don't want to invoke delete STA, delete + * BSS, as that would remove the already + * established TSPEC. Just go ahead and re-add + * the BSS, STA with new capability information. + * However, if we're re-associating to a different + * BSS, then follow thru with del STA, del BSS, + * add BSS, add STA. + */ + if (sir_compare_mac_addr(session_entry->bssId, + session_entry->limReAssocbssId)) + lim_handle_add_bss_in_re_assoc_context(mac_ctx, sta_ds, + session_entry); + else { + /* + * reset the uapsd mask settings since + * we're re-associating to new AP + */ + session_entry->gUapsdPerAcDeliveryEnableMask = 0; + session_entry->gUapsdPerAcTriggerEnableMask = 0; + + if (lim_cleanup_rx_path(mac_ctx, sta_ds, session_entry) + != QDF_STATUS_SUCCESS) { + pe_err("Could not cleanup the rx path"); + goto assocReject; + } + } + qdf_mem_free(beacon); + return; + } + pe_debug("Successfully Associated with BSS " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(hdr->sa)); +#ifdef FEATURE_WLAN_ESE + if (session_entry->eseContext.tsm.tsmInfo.state) + session_entry->eseContext.tsm.tsmMetrics.RoamingCount = 0; +#endif + /* Store assigned AID for TIM processing */ + session_entry->limAID = assoc_rsp->aid & 0x3FFF; + + /* STA entry was created during pre-assoc state. */ + sta_ds = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session_entry->dph.dphHashTable); + if (!sta_ds) { + /* Could not add hash table entry */ + pe_err("could not get hash entry at DPH"); + lim_print_mac_addr(mac_ctx, hdr->sa, LOGE); + assoc_cnf.resultCode = eSIR_SME_RESOURCES_UNAVAILABLE; + assoc_cnf.protStatusCode = eSIR_SME_SUCCESS; + lim_post_sme_message(mac_ctx, LIM_MLM_ASSOC_CNF, + (uint32_t *) &assoc_cnf); + qdf_mem_free(assoc_rsp); + qdf_mem_free(beacon); + return; + } + /* Delete Pre-auth context for the associated BSS */ + if (lim_search_pre_auth_list(mac_ctx, hdr->sa)) + lim_delete_pre_auth_node(mac_ctx, hdr->sa); + + if (ap_nss < session_entry->nss) + session_entry->nss = ap_nss; + + lim_objmgr_update_vdev_nss(mac_ctx->psoc, + session_entry->smeSessionId, + session_entry->nss); + + /* + * Extract the AP capabilities from the beacon that + * was received earlier + */ + ie_len = lim_get_ielen_from_bss_description( + &session_entry->lim_join_req->bssDescription); + lim_extract_ap_capabilities(mac_ctx, + (uint8_t *)session_entry->lim_join_req->bssDescription.ieFields, + ie_len, beacon); + + lim_update_assoc_sta_datas(mac_ctx, sta_ds, assoc_rsp, + session_entry, beacon); + if (lim_is_session_he_capable(session_entry)) { + session_entry->mu_edca_present = assoc_rsp->mu_edca_present; + if (session_entry->mu_edca_present) { + pe_debug("Save MU EDCA params to session"); + session_entry->ap_mu_edca_params[QCA_WLAN_AC_BE] = + assoc_rsp->mu_edca.acbe; + session_entry->ap_mu_edca_params[QCA_WLAN_AC_BK] = + assoc_rsp->mu_edca.acbk; + session_entry->ap_mu_edca_params[QCA_WLAN_AC_VI] = + assoc_rsp->mu_edca.acvi; + session_entry->ap_mu_edca_params[QCA_WLAN_AC_VO] = + assoc_rsp->mu_edca.acvo; + } + + } + + if (beacon->VHTCaps.present) + sta_ds->parsed_ies.vht_caps = beacon->VHTCaps; + if (beacon->HTCaps.present) + sta_ds->parsed_ies.ht_caps = beacon->HTCaps; + if (beacon->hs20vendor_ie.present) + sta_ds->parsed_ies.hs20vendor_ie = beacon->hs20vendor_ie; + if (beacon->HTInfo.present) + sta_ds->parsed_ies.ht_operation = beacon->HTInfo; + if (beacon->VHTOperation.present) + sta_ds->parsed_ies.vht_operation = beacon->VHTOperation; + + lim_process_he_info(beacon, sta_ds); + + if (mac_ctx->lim.gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) + lim_decide_sta_protection_on_assoc(mac_ctx, beacon, + session_entry); + + if (beacon->erpPresent) { + if (beacon->erpIEInfo.barkerPreambleMode) + session_entry->beaconParams.fShortPreamble = false; + else + session_entry->beaconParams.fShortPreamble = true; + } + +#ifdef FEATURE_WLAN_DIAG_SUPPORT + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_CONNECTED, session_entry, + QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); +#endif + lim_update_stads_ext_cap(mac_ctx, session_entry, assoc_rsp, sta_ds); + /* Update the BSS Entry, this entry was added during preassoc. */ + if (QDF_STATUS_SUCCESS == lim_sta_send_add_bss(mac_ctx, assoc_rsp, + beacon, + &session_entry->lim_join_req->bssDescription, true, + session_entry)) { + qdf_mem_free(assoc_rsp); + qdf_mem_free(beacon); + return; + } else { + pe_err("could not update the bss entry"); + assoc_cnf.resultCode = eSIR_SME_RESOURCES_UNAVAILABLE; + assoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + } + +assocReject: + if ((subtype == LIM_ASSOC) + || ((subtype == LIM_REASSOC) + && (session_entry->limMlmState == + eLIM_MLM_WT_FT_REASSOC_RSP_STATE))) { + pe_err("Assoc Rejected by the peer mlmestate: %d sessionid: %d Reason: %d MACADDR:" + QDF_MAC_ADDR_FMT, + session_entry->limMlmState, + session_entry->peSessionId, + assoc_cnf.resultCode, QDF_MAC_ADDR_REF(hdr->sa)); + session_entry->limMlmState = eLIM_MLM_IDLE_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, + session_entry->limMlmState)); + if (session_entry->pLimMlmJoinReq) { + qdf_mem_free(session_entry->pLimMlmJoinReq); + session_entry->pLimMlmJoinReq = NULL; + } + if (subtype == LIM_ASSOC) { + lim_post_sme_message(mac_ctx, LIM_MLM_ASSOC_CNF, + (uint32_t *) &assoc_cnf); + } else { + assoc_cnf.resultCode = eSIR_SME_FT_REASSOC_FAILURE; + lim_post_sme_message(mac_ctx, LIM_MLM_REASSOC_CNF, + (uint32_t *)&assoc_cnf); + } + } else { + lim_restore_pre_reassoc_state(mac_ctx, + eSIR_SME_REASSOC_REFUSED, + assoc_cnf.protStatusCode, + session_entry); + } + + qdf_mem_free(beacon); + qdf_mem_free(assoc_rsp->sha384_ft_subelem.gtk); + qdf_mem_free(assoc_rsp->sha384_ft_subelem.igtk); + qdf_mem_free(assoc_rsp); + return; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_auth_frame.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_auth_frame.c new file mode 100644 index 0000000000000000000000000000000000000000..4805c6d7ca75bf8a6f1d76d3ac0ca43c55d95fb2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_auth_frame.c @@ -0,0 +1,1877 @@ +/* + * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_process_auth_frame.cc contains the code + * for processing received Authentication Frame. + * Author: Chandra Modumudi + * Date: 03/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * 05/12/2010 js To support Shared key authentication at AP side + * + */ + +#include "wni_api.h" +#include "wni_cfg.h" +#include "ani_global.h" +#include "cfg_ucfg_api.h" +#include "utils_api.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_ft.h" +#include "cds_utils.h" +#include "lim_send_messages.h" +#include "lim_process_fils.h" +#include "wlan_mlme_api.h" + +/** + * is_auth_valid + * + ***FUNCTION: + * This function is called by lim_process_auth_frame() upon Authentication + * frame reception. + * + ***LOGIC: + * This function is used to test validity of auth frame: + * - AUTH1 and AUTH3 must be received in AP mode + * - AUTH2 and AUTH4 must be received in STA mode + * - AUTH3 and AUTH4 must have challenge text IE, that is,'type' field has been set to + * WLAN_ELEMID_CHALLENGE by parser + * - + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param *auth - Pointer to extracted auth frame body + * + * @return 0 or 1 (Valid) + */ + +static inline unsigned int is_auth_valid(struct mac_context *mac, + tpSirMacAuthFrameBody auth, + struct pe_session *pe_session) +{ + unsigned int valid = 1; + + if (((auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_1) || + (auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_3)) && + (LIM_IS_STA_ROLE(pe_session))) + valid = 0; + + if (((auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_2) || + (auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_4)) && + (LIM_IS_AP_ROLE(pe_session))) + valid = 0; + + if (((auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_3) || + (auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_4)) && + (auth->type != WLAN_ELEMID_CHALLENGE) && + (auth->authAlgoNumber != eSIR_SHARED_KEY)) + valid = 0; + + return valid; +} + +/** + * lim_get_wep_key_sap() - get sap's wep key for shared wep auth + * @pe_session: pointer to pe session + * @wep_params: pointer to wlan_mlme_wep_cfg + * @key_id: key id + * @default_key: output of the key + * @key_len: output of ket length + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_get_wep_key_sap(struct pe_session *pe_session, + struct wlan_mlme_wep_cfg *wep_params, + uint8_t key_id, + uint8_t *default_key, + qdf_size_t *key_len) +{ + return mlme_get_wep_key(pe_session->vdev, + wep_params, + (MLME_WEP_DEFAULT_KEY_1 + key_id), + default_key, + key_len); +} + +static void lim_process_auth_shared_system_algo(struct mac_context *mac_ctx, + tpSirMacMgmtHdr mac_hdr, + tSirMacAuthFrameBody *rx_auth_frm_body, + tSirMacAuthFrameBody *auth_frame, + struct pe_session *pe_session) +{ + uint32_t val; + uint8_t cfg_privacy_opt_imp; + struct tLimPreAuthNode *auth_node; + uint8_t challenge_txt_arr[SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH] = {0}; + + pe_debug("=======> eSIR_SHARED_KEY"); + if (LIM_IS_AP_ROLE(pe_session)) + val = pe_session->privacy; + else + val = mac_ctx->mlme_cfg->wep_params.is_privacy_enabled; + + cfg_privacy_opt_imp = (uint8_t) val; + if (!cfg_privacy_opt_imp) { + pe_err("rx Auth frame for unsupported auth algorithm %d " + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authAlgoNumber, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + + /* + * Authenticator does not have WEP + * implemented. + * Reject by sending Authentication frame + * with Auth algorithm not supported status + * code. + */ + auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = + eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS; + + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + return; + } else { + /* Create entry for this STA in pre-auth list */ + auth_node = lim_acquire_free_pre_auth_node(mac_ctx, + &mac_ctx->lim.gLimPreAuthTimerTable); + if (!auth_node) { + pe_warn("Max preauth-nodes reached"); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW); + return; + } + + qdf_mem_copy((uint8_t *) auth_node->peerMacAddr, mac_hdr->sa, + sizeof(tSirMacAddr)); + auth_node->mlmState = eLIM_MLM_WT_AUTH_FRAME3_STATE; + auth_node->authType = + (tAniAuthType) rx_auth_frm_body->authAlgoNumber; + auth_node->fSeen = 0; + auth_node->fTimerStarted = 0; + auth_node->seq_num = ((mac_hdr->seqControl.seqNumHi << 4) | + (mac_hdr->seqControl.seqNumLo)); + auth_node->timestamp = qdf_mc_timer_get_system_ticks(); + lim_add_pre_auth_node(mac_ctx, auth_node); + + pe_debug("Alloc new data: %pK id: %d peer ", + auth_node, auth_node->authNodeIdx); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD); + /* / Create and activate Auth Response timer */ + if (tx_timer_change_context(&auth_node->timer, + auth_node->authNodeIdx) != TX_SUCCESS) { + /* Could not start Auth response timer. Log error */ + pe_warn("Unable to chg context auth response timer for peer"); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW); + + /* + * Send Auth frame with unspecified failure status code. + */ + + auth_frame->authAlgoNumber = + rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa); + return; + } + + /* + * get random bytes and use as challenge text. + */ + get_random_bytes(challenge_txt_arr, + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH); + qdf_mem_zero(auth_node->challengeText, + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH); + if (!qdf_mem_cmp(challenge_txt_arr, + auth_node->challengeText, + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH)) { + pe_err("Challenge text preparation failed"); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW); + auth_frame->authAlgoNumber = + rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = eSIR_MAC_TRY_AGAIN_LATER; + lim_send_auth_mgmt_frame(mac_ctx, + auth_frame, + mac_hdr->sa, + LIM_NO_WEP_IN_FC, + pe_session); + lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa); + return; + } + + lim_activate_auth_rsp_timer(mac_ctx, auth_node); + auth_node->fTimerStarted = 1; + + qdf_mem_copy(auth_node->challengeText, + challenge_txt_arr, + sizeof(challenge_txt_arr)); + /* + * Sending Authenticaton frame with challenge. + */ + auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = eSIR_MAC_SUCCESS_STATUS; + auth_frame->type = WLAN_ELEMID_CHALLENGE; + auth_frame->length = SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH; + qdf_mem_copy(auth_frame->challengeText, + auth_node->challengeText, + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH); + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + } +} + +static void lim_process_auth_open_system_algo(struct mac_context *mac_ctx, + tpSirMacMgmtHdr mac_hdr, + tSirMacAuthFrameBody *rx_auth_frm_body, + tSirMacAuthFrameBody *auth_frame, + struct pe_session *pe_session) +{ + struct tLimPreAuthNode *auth_node; + + pe_debug("=======> eSIR_OPEN_SYSTEM"); + /* Create entry for this STA in pre-auth list */ + auth_node = lim_acquire_free_pre_auth_node(mac_ctx, + &mac_ctx->lim.gLimPreAuthTimerTable); + if (!auth_node) { + pe_warn("Max pre-auth nodes reached "); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW); + return; + } + pe_debug("Alloc new data: %pK peer", auth_node); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD); + qdf_mem_copy((uint8_t *) auth_node->peerMacAddr, + mac_hdr->sa, sizeof(tSirMacAddr)); + auth_node->mlmState = eLIM_MLM_AUTHENTICATED_STATE; + auth_node->authType = (tAniAuthType) rx_auth_frm_body->authAlgoNumber; + auth_node->fSeen = 0; + auth_node->fTimerStarted = 0; + auth_node->seq_num = ((mac_hdr->seqControl.seqNumHi << 4) | + (mac_hdr->seqControl.seqNumLo)); + auth_node->timestamp = qdf_mc_timer_get_system_ticks(); + lim_add_pre_auth_node(mac_ctx, auth_node); + /* + * Send Authenticaton frame with Success + * status code. + */ + auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = eSIR_MAC_SUCCESS_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, mac_hdr->sa, + LIM_NO_WEP_IN_FC, + pe_session); +} + +#ifdef WLAN_FEATURE_SAE + +/** + * lim_external_auth_add_pre_auth_node()- Add preauth node for the peer + * performing external authentication + * @mac_ctx: MAC context + * @mac_hdr: Mac header of the packet + * @mlm_state: MLM state to be marked to track SAE authentication + * + * Return: None + */ +static void lim_external_auth_add_pre_auth_node(struct mac_context *mac_ctx, + tpSirMacMgmtHdr mac_hdr, + tLimMlmStates mlm_state) +{ + struct tLimPreAuthNode *auth_node; + tpLimPreAuthTable preauth_table = &mac_ctx->lim.gLimPreAuthTimerTable; + + pe_debug("=======> eSIR_AUTH_TYPE_SAE"); + /* Create entry for this STA in pre-auth list */ + auth_node = lim_acquire_free_pre_auth_node(mac_ctx, preauth_table); + if (!auth_node) { + pe_debug("Max pre-auth nodes reached " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + pe_debug("Creating preauth node for SAE peer " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + qdf_mem_copy((uint8_t *)auth_node->peerMacAddr, + mac_hdr->sa, sizeof(tSirMacAddr)); + auth_node->mlmState = mlm_state; + auth_node->authType = eSIR_AUTH_TYPE_SAE; + auth_node->timestamp = qdf_mc_timer_get_system_ticks(); + auth_node->seq_num = ((mac_hdr->seqControl.seqNumHi << 4) | + (mac_hdr->seqControl.seqNumLo)); + auth_node->assoc_req.present = false; + lim_add_pre_auth_node(mac_ctx, auth_node); +} + +void lim_sae_auth_cleanup_retry(struct mac_context *mac_ctx, + uint8_t vdev_id) +{ + struct pe_session *pe_session; + + pe_session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!pe_session) { + pe_err("session not found for given vdev_id %d", vdev_id); + return; + } + + pe_debug("sae auth cleanup for vdev_id %d", vdev_id); + lim_deactivate_and_change_timer(mac_ctx, eLIM_AUTH_RETRY_TIMER); + mlme_free_sae_auth_retry(pe_session->vdev); +} + +#define SAE_AUTH_ALGO_BYTES 2 +#define SAE_AUTH_SEQ_NUM_BYTES 2 +#define SAE_AUTH_SEQ_OFFSET 1 + +/** + * lim_is_sae_auth_algo_match()- Match SAE auth seq in queued SAE auth and + * SAE auth rx frame + * @queued_frame: Pointer to queued SAE auth retry frame + * @q_len: length of queued sae auth retry frame + * @rx_pkt_info: Rx packet + * + * Return: True if SAE auth seq is mached else false + */ +static bool lim_is_sae_auth_algo_match(uint8_t *queued_frame, uint16_t q_len, + uint8_t *rx_pkt_info) +{ + tpSirMacMgmtHdr qmac_hdr = (tpSirMacMgmtHdr)queued_frame; + uint16_t *rxbody_ptr, *qbody_ptr, rxframe_len, min_len; + + min_len = sizeof(tSirMacMgmtHdr) + SAE_AUTH_ALGO_BYTES + + SAE_AUTH_SEQ_NUM_BYTES; + + rxframe_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + if (rxframe_len < min_len || q_len < min_len) { + pe_debug("rxframe_len %d, queued_frame_len %d, min_len %d", + rxframe_len, q_len, min_len); + return false; + } + + rxbody_ptr = (uint16_t *)WMA_GET_RX_MPDU_DATA(rx_pkt_info); + qbody_ptr = (uint16_t *)((uint8_t *)qmac_hdr + sizeof(tSirMacMgmtHdr)); + + pe_debug("sae_auth : rx pkt auth seq %d queued pkt auth seq %d", + rxbody_ptr[SAE_AUTH_SEQ_OFFSET], + qbody_ptr[SAE_AUTH_SEQ_OFFSET]); + if (rxbody_ptr[SAE_AUTH_SEQ_OFFSET] == + qbody_ptr[SAE_AUTH_SEQ_OFFSET]) + return true; + + return false; +} + +/** + * lim_process_sae_auth_frame()-Process SAE authentication frame + * @mac_ctx: MAC context + * @rx_pkt_info: Rx packet + * @pe_session: PE session + * + * Return: None + */ +static void lim_process_sae_auth_frame(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, struct pe_session *pe_session) +{ + tpSirMacMgmtHdr mac_hdr; + uint32_t frame_len; + uint8_t *body_ptr; + enum rxmgmt_flags rx_flags = RXMGMT_FLAG_NONE; + struct sae_auth_retry *sae_retry; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + pe_nofl_rl_info("SAE Auth RX type %d subtype %d from " QDF_MAC_ADDR_FMT, + mac_hdr->fc.type, mac_hdr->fc.subType, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + + if (LIM_IS_STA_ROLE(pe_session) && + pe_session->limMlmState != eLIM_MLM_WT_SAE_AUTH_STATE) + pe_err("SAE auth response for STA in unexpected state %x", + pe_session->limMlmState); + + if (LIM_IS_AP_ROLE(pe_session)) { + struct tLimPreAuthNode *sta_pre_auth_ctx; + + rx_flags = RXMGMT_FLAG_EXTERNAL_AUTH; + /* Add preauth node when the first SAE authentication frame + * is received and mark state as authenticating. + * It's not good to track SAE authentication frames with + * authTransactionSeqNumber as it's subjected to + * SAE protocol optimizations. + */ + /* Extract pre-auth context for the STA, if any. */ + sta_pre_auth_ctx = lim_search_pre_auth_list(mac_ctx, + mac_hdr->sa); + if (!sta_pre_auth_ctx || + (sta_pre_auth_ctx->mlmState != eLIM_MLM_WT_SAE_AUTH_STATE && + sta_pre_auth_ctx->mlmState != + eLIM_MLM_AUTHENTICATED_STATE)) { + lim_external_auth_add_pre_auth_node(mac_ctx, mac_hdr, + eLIM_MLM_WT_SAE_AUTH_STATE); + } + } + + sae_retry = mlme_get_sae_auth_retry(pe_session->vdev); + if (LIM_IS_STA_ROLE(pe_session) && sae_retry && + sae_retry->sae_auth.data) { + if (lim_is_sae_auth_algo_match( + sae_retry->sae_auth.data, sae_retry->sae_auth.len, + rx_pkt_info)) + lim_sae_auth_cleanup_retry(mac_ctx, + pe_session->vdev_id); + } + lim_send_sme_mgmt_frame_ind(mac_ctx, mac_hdr->fc.subType, + (uint8_t *)mac_hdr, + frame_len + sizeof(tSirMacMgmtHdr), + pe_session->smeSessionId, + WMA_GET_RX_FREQ(rx_pkt_info), pe_session, + WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info), + rx_flags); +} +#else +static inline void lim_process_sae_auth_frame(struct mac_context *mac_ctx, + uint8_t *rx_pkt_info, struct pe_session *pe_session) +{} +#endif + +static void lim_process_auth_frame_type1(struct mac_context *mac_ctx, + tpSirMacMgmtHdr mac_hdr, + tSirMacAuthFrameBody *rx_auth_frm_body, + uint8_t *rx_pkt_info, uint16_t curr_seq_num, + tSirMacAuthFrameBody *auth_frame, struct pe_session *pe_session) +{ + tpDphHashNode sta_ds_ptr = NULL; + struct tLimPreAuthNode *auth_node; + uint32_t maxnum_preauth; + uint16_t associd = 0; + + /* AuthFrame 1 */ + sta_ds_ptr = dph_lookup_hash_entry(mac_ctx, mac_hdr->sa, + &associd, &pe_session->dph.dphHashTable); + if (sta_ds_ptr) { + tLimMlmDisassocReq *pMlmDisassocReq = NULL; + tLimMlmDeauthReq *pMlmDeauthReq = NULL; + bool is_connected = true; + + pMlmDisassocReq = + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq; + if (pMlmDisassocReq && + (!qdf_mem_cmp((uint8_t *) mac_hdr->sa, (uint8_t *) + &pMlmDisassocReq->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE))) { + pe_debug("TODO:Ack for disassoc frame is pending Issue delsta for " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF( + pMlmDisassocReq->peer_macaddr.bytes)); + lim_process_disassoc_ack_timeout(mac_ctx); + is_connected = false; + } + pMlmDeauthReq = + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq; + if (pMlmDeauthReq && + (!qdf_mem_cmp((uint8_t *) mac_hdr->sa, (uint8_t *) + &pMlmDeauthReq->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE))) { + pe_debug("TODO:Ack for deauth frame is pending Issue delsta for " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF( + pMlmDeauthReq->peer_macaddr.bytes)); + lim_process_deauth_ack_timeout(mac_ctx); + is_connected = false; + } + + /* + * pStaDS != NULL and is_connected = 1 means the STA is already + * connected, But SAP received the Auth from that station. + * For non PMF connection send Deauth frame as STA will retry + * to connect back. The reason for above logic is captured in + * CR620403. If we silently drop the auth, the subsequent EAPOL + * exchange will fail & peer STA will keep trying until DUT + * SAP/GO gets a kickout event from FW & cleans up. + * + * For PMF connection the AP should not tear down or otherwise + * modify the state of the existing association until the + * SA-Query procedure determines that the original SA is + * invalid. + */ + if (is_connected +#ifdef WLAN_FEATURE_11W + && !sta_ds_ptr->rmfEnabled +#endif + ) { + pe_err("STA is already connected but received auth frame" + "Send the Deauth and lim Delete Station Context" + "(associd: %d) sta mac" QDF_MAC_ADDR_FMT, + associd, QDF_MAC_ADDR_REF(mac_hdr->sa)); + lim_send_deauth_mgmt_frame(mac_ctx, + eSIR_MAC_UNSPEC_FAILURE_REASON, + (uint8_t *) mac_hdr->sa, + pe_session, false); + lim_trigger_sta_deletion(mac_ctx, sta_ds_ptr, + pe_session); + return; + } + } + /* Check if there exists pre-auth context for this STA */ + auth_node = lim_search_pre_auth_list(mac_ctx, mac_hdr->sa); + if (auth_node) { + /* Pre-auth context exists for the STA */ + if (!(mac_hdr->fc.retry == 0 || + auth_node->seq_num != curr_seq_num)) { + /* + * This can happen when first authentication frame is + * received but ACK lost at STA side, in this case 2nd + * auth frame is already in transmission queue + */ + pe_warn("STA is initiating Auth after ACK lost"); + return; + } + /* + * STA is initiating brand-new Authentication + * sequence after local Auth Response timeout Or STA + * retrying to transmit First Auth frame due to packet + * drop OTA Delete Pre-auth node and fall through. + */ + if (auth_node->fTimerStarted) + lim_deactivate_and_change_per_sta_id_timer( + mac_ctx, eLIM_AUTH_RSP_TIMER, + auth_node->authNodeIdx); + pe_debug("STA is initiating brand-new Auth"); + lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa); + /* + * SAP Mode:Disassociate the station and + * delete its entry if we have its entry + * already and received "auth" from the + * same station. + * SAP dphHashTable.size = 8 + */ + for (associd = 0; associd < pe_session->dph.dphHashTable.size; + associd++) { + sta_ds_ptr = dph_get_hash_entry(mac_ctx, associd, + &pe_session->dph.dphHashTable); + if (!sta_ds_ptr) + continue; + if (sta_ds_ptr->valid && (!qdf_mem_cmp( + (uint8_t *)&sta_ds_ptr->staAddr, + (uint8_t *) &(mac_hdr->sa), + (uint8_t) sizeof(tSirMacAddr)))) + break; + sta_ds_ptr = NULL; + } + + if (sta_ds_ptr +#ifdef WLAN_FEATURE_11W + && !sta_ds_ptr->rmfEnabled +#endif + ) { + pe_debug("lim Del Sta Ctx associd: %d sta mac" + QDF_MAC_ADDR_FMT, associd, + QDF_MAC_ADDR_REF(sta_ds_ptr->staAddr)); + lim_send_deauth_mgmt_frame(mac_ctx, + eSIR_MAC_UNSPEC_FAILURE_REASON, + (uint8_t *)auth_node->peerMacAddr, + pe_session, false); + lim_trigger_sta_deletion(mac_ctx, sta_ds_ptr, + pe_session); + return; + } + } + maxnum_preauth = mac_ctx->mlme_cfg->lfr.max_num_pre_auth; + if (mac_ctx->lim.gLimNumPreAuthContexts == maxnum_preauth && + !lim_delete_open_auth_pre_auth_node(mac_ctx)) { + pe_err("Max no of preauth context reached"); + /* + * Maximum number of pre-auth contexts reached. + * Send Authentication frame with unspecified failure + */ + auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + return; + } + /* No Pre-auth context exists for the STA. */ + if (lim_is_auth_algo_supported(mac_ctx, + (tAniAuthType) rx_auth_frm_body->authAlgoNumber, + pe_session)) { + struct wlan_objmgr_vdev *vdev; + + vdev = + wlan_objmgr_get_vdev_by_macaddr_from_pdev(mac_ctx->pdev, + mac_hdr->sa, + WLAN_LEGACY_MAC_ID); + /* SA is same as any of the device vdev, return failure */ + if (vdev) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + auth_frame->authAlgoNumber = + rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = + eSIR_MAC_WME_INVALID_PARAMS_STATUS; + + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + return; + } + + switch (rx_auth_frm_body->authAlgoNumber) { + case eSIR_OPEN_SYSTEM: + lim_process_auth_open_system_algo(mac_ctx, mac_hdr, + rx_auth_frm_body, auth_frame, pe_session); + break; + + case eSIR_SHARED_KEY: + lim_process_auth_shared_system_algo(mac_ctx, mac_hdr, + rx_auth_frm_body, auth_frame, pe_session); + break; + default: + pe_err("rx Auth frm for unsupported auth algo %d " + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authAlgoNumber, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + + /* + * Responding party does not support the + * authentication algorithm requested by + * sending party. + * Reject by sending Authentication frame + * with auth algorithm not supported status code + */ + auth_frame->authAlgoNumber = + rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = + eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + return; + } + } else { + pe_err("received Authentication frame for unsupported auth algorithm %d " + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authAlgoNumber, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + + /* + * Responding party does not support the + * authentication algorithm requested by sending party. + * Reject Authentication with StatusCode=13. + */ + auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = + eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS; + + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, + LIM_NO_WEP_IN_FC, + pe_session); + return; + } +} + +static void lim_process_auth_frame_type2(struct mac_context *mac_ctx, + tpSirMacMgmtHdr mac_hdr, + tSirMacAuthFrameBody *rx_auth_frm_body, + tSirMacAuthFrameBody *auth_frame, + uint8_t *plainbody, + uint8_t *body_ptr, uint16_t frame_len, + struct pe_session *pe_session) +{ + uint8_t key_id, cfg_privacy_opt_imp; + uint32_t key_length = 8; + qdf_size_t val; + uint8_t defaultkey[SIR_MAC_KEY_LENGTH]; + struct tLimPreAuthNode *auth_node; + uint8_t *encr_auth_frame; + struct wlan_mlme_wep_cfg *wep_params = &mac_ctx->mlme_cfg->wep_params; + QDF_STATUS qdf_status; + + /* AuthFrame 2 */ + if (pe_session->limMlmState != eLIM_MLM_WT_AUTH_FRAME2_STATE) { + /** + * Check if a Reassociation is in progress and this is a + * Pre-Auth frame + */ + if (LIM_IS_STA_ROLE(pe_session) && + (pe_session->limSmeState == eLIM_SME_WT_REASSOC_STATE) && + (rx_auth_frm_body->authStatusCode == + eSIR_MAC_SUCCESS_STATUS) && + (pe_session->ftPEContext.pFTPreAuthReq) && + (!qdf_mem_cmp( + pe_session->ftPEContext.pFTPreAuthReq->preAuthbssId, + mac_hdr->sa, sizeof(tSirMacAddr)))) { + + /* Update the FTIEs in the saved auth response */ + pe_warn("rx PreAuth frm2 in smestate: %d from: "QDF_MAC_ADDR_FMT, + pe_session->limSmeState, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + pe_session->ftPEContext.saved_auth_rsp_length = 0; + + if ((body_ptr) && (frame_len < MAX_FTIE_SIZE)) { + qdf_mem_copy( + pe_session->ftPEContext.saved_auth_rsp, + body_ptr, frame_len); + pe_session->ftPEContext.saved_auth_rsp_length = + frame_len; + } + } else { + /* + * Received Auth frame2 in an unexpected state. + * Log error and ignore the frame. + */ + pe_debug("rx Auth frm2 from peer in state: %d addr", + pe_session->limMlmState); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD); + } + return; + } + + if (qdf_mem_cmp((uint8_t *) mac_hdr->sa, + (uint8_t *) &mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr, + sizeof(tSirMacAddr))) { + /* + * Received Authentication frame from an entity + * other than one request was initiated. + * Wait until Authentication Failure Timeout. + */ + + pe_warn("received Auth frame2 from unexpected peer" + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + + if (rx_auth_frm_body->authStatusCode == + eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS) { + /* + * Interoperability workaround: Linksys WAP4400N is returning + * wrong authType in OpenAuth response in case of + * SharedKey AP configuration. Pretend we don't see that, + * so upper layer can fallback to SharedKey authType, + * and successfully connect to the AP. + */ + if (rx_auth_frm_body->authAlgoNumber != + mac_ctx->lim.gpLimMlmAuthReq->authType) { + rx_auth_frm_body->authAlgoNumber = + mac_ctx->lim.gpLimMlmAuthReq->authType; + } + } + + if (rx_auth_frm_body->authAlgoNumber != + mac_ctx->lim.gpLimMlmAuthReq->authType) { + /* + * Auth algo is open in rx auth frame when auth type is SAE and + * PMK is cached as driver sent auth algo as open in tx frame + * as well. + */ + if ((mac_ctx->lim.gpLimMlmAuthReq->authType == + eSIR_AUTH_TYPE_SAE) && pe_session->sae_pmk_cached) { + pe_debug("rx Auth frame2 auth algo %d in SAE PMK case", + rx_auth_frm_body->authAlgoNumber); + } else { + /* + * Received Authentication frame with an auth + * algorithm other than one requested. + * Wait until Authentication Failure Timeout. + */ + + pe_warn("rx Auth frame2 for unexpected auth algo %d" + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authAlgoNumber, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + } + + if (rx_auth_frm_body->authStatusCode != eSIR_MAC_SUCCESS_STATUS) { + /* + * Authentication failure. + * Return Auth confirm with received failure code to SME + */ + pe_err("rx Auth frame from peer with failure code %d " + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authStatusCode, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + lim_restore_from_auth_state(mac_ctx, eSIR_SME_AUTH_REFUSED, + rx_auth_frm_body->authStatusCode, + pe_session); + return; + } + + if (lim_process_fils_auth_frame2(mac_ctx, pe_session, + rx_auth_frm_body)) { + lim_restore_from_auth_state(mac_ctx, eSIR_SME_SUCCESS, + rx_auth_frm_body->authStatusCode, pe_session); + return; + } + + if (rx_auth_frm_body->authAlgoNumber == eSIR_OPEN_SYSTEM) { + pe_session->limCurrentAuthType = eSIR_OPEN_SYSTEM; + auth_node = lim_acquire_free_pre_auth_node(mac_ctx, + &mac_ctx->lim.gLimPreAuthTimerTable); + if (!auth_node) { + pe_warn("Max pre-auth nodes reached"); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW); + return; + } + + pe_debug("add new auth node: for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + qdf_mem_copy((uint8_t *) auth_node->peerMacAddr, + mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr, + sizeof(tSirMacAddr)); + auth_node->fTimerStarted = 0; + auth_node->authType = + mac_ctx->lim.gpLimMlmAuthReq->authType; + auth_node->seq_num = + ((mac_hdr->seqControl.seqNumHi << 4) | + (mac_hdr->seqControl.seqNumLo)); + auth_node->timestamp = qdf_mc_timer_get_system_ticks(); + lim_add_pre_auth_node(mac_ctx, auth_node); + lim_restore_from_auth_state(mac_ctx, eSIR_SME_SUCCESS, + rx_auth_frm_body->authStatusCode, pe_session); + } else { + /* Shared key authentication */ + if (LIM_IS_AP_ROLE(pe_session)) + cfg_privacy_opt_imp = pe_session->privacy; + else + cfg_privacy_opt_imp = wep_params->is_privacy_enabled; + + if (!cfg_privacy_opt_imp) { + /* + * Requesting STA does not have WEP implemented. + * Reject with unsupported authentication algo + * Status code & wait until auth failure timeout + */ + + pe_err("rx Auth frm from peer for unsupported auth algo %d " + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authAlgoNumber, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + + auth_frame->authAlgoNumber = + rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + auth_frame->authStatusCode = + eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + return; + } + if (rx_auth_frm_body->type != WLAN_ELEMID_CHALLENGE) { + pe_err("rx auth frm with invalid challenge txtie"); + return; + } + + key_id = mac_ctx->mlme_cfg->wep_params.wep_default_key_id; + val = SIR_MAC_KEY_LENGTH; + if (LIM_IS_AP_ROLE(pe_session)) { + qdf_status = lim_get_wep_key_sap(pe_session, + wep_params, + key_id, + defaultkey, + &val); + } else { + qdf_status = mlme_get_wep_key(pe_session->vdev, + wep_params, + (MLME_WEP_DEFAULT_KEY_1 + + key_id), defaultkey, + &val); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + pe_warn("cant retrieve Defaultkey"); + + auth_frame->authAlgoNumber = + rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + rx_auth_frm_body->authTransactionSeqNumber + 1; + + auth_frame->authStatusCode = + eSIR_MAC_CHALLENGE_FAILURE_STATUS; + + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, + LIM_NO_WEP_IN_FC, + pe_session); + lim_restore_from_auth_state(mac_ctx, + eSIR_SME_INVALID_WEP_DEFAULT_KEY, + eSIR_MAC_UNSPEC_FAILURE_REASON, + pe_session); + return; + } + } + key_length = val; + ((tpSirMacAuthFrameBody)plainbody)->authAlgoNumber = + sir_swap_u16if_needed(rx_auth_frm_body->authAlgoNumber); + ((tpSirMacAuthFrameBody)plainbody)->authTransactionSeqNumber = + sir_swap_u16if_needed((uint16_t)( + rx_auth_frm_body->authTransactionSeqNumber + + 1)); + ((tpSirMacAuthFrameBody)plainbody)->authStatusCode = + eSIR_MAC_SUCCESS_STATUS; + ((tpSirMacAuthFrameBody)plainbody)->type = + WLAN_ELEMID_CHALLENGE; + ((tpSirMacAuthFrameBody)plainbody)->length = + rx_auth_frm_body->length; + qdf_mem_copy((uint8_t *) ( + (tpSirMacAuthFrameBody)plainbody)->challengeText, + rx_auth_frm_body->challengeText, + rx_auth_frm_body->length); + encr_auth_frame = qdf_mem_malloc(rx_auth_frm_body->length + + LIM_ENCR_AUTH_INFO_LEN); + if (!encr_auth_frame) + return; + lim_encrypt_auth_frame(mac_ctx, key_id, + defaultkey, plainbody, + encr_auth_frame, key_length); + pe_session->limMlmState = eLIM_MLM_WT_AUTH_FRAME4_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + pe_session->peSessionId, + pe_session->limMlmState)); + lim_send_auth_mgmt_frame(mac_ctx, + (tpSirMacAuthFrameBody)encr_auth_frame, + mac_hdr->sa, rx_auth_frm_body->length, + pe_session); + qdf_mem_free(encr_auth_frame); + return; + } +} + +static void lim_process_auth_frame_type3(struct mac_context *mac_ctx, + tpSirMacMgmtHdr mac_hdr, + tSirMacAuthFrameBody *rx_auth_frm_body, + tSirMacAuthFrameBody *auth_frame, + struct pe_session *pe_session) +{ + struct tLimPreAuthNode *auth_node; + + /* AuthFrame 3 */ + if (rx_auth_frm_body->authAlgoNumber != eSIR_SHARED_KEY) { + pe_err("rx Auth frame3 from peer with auth algo number %d " + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authAlgoNumber, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + /* + * Received Authentication frame3 with algorithm other than + * Shared Key authentication type. Reject with Auth frame4 + * with 'out of sequence' status code. + */ + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_AUTH_FRAME_OUT_OF_SEQ_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + return; + } + + if (LIM_IS_AP_ROLE(pe_session) || + LIM_IS_IBSS_ROLE(pe_session)) { + /* + * Check if wep bit was set in FC. If not set, + * reject with Authentication frame4 with + * 'challenge failure' status code. + */ + if (!mac_hdr->fc.wep) { + pe_err("received Auth frame3 from peer with no WEP bit set " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + /* WEP bit is not set in FC of Auth Frame3 */ + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_CHALLENGE_FAILURE_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, + LIM_NO_WEP_IN_FC, + pe_session); + return; + } + + auth_node = lim_search_pre_auth_list(mac_ctx, mac_hdr->sa); + if (!auth_node) { + pe_warn("received AuthFrame3 from peer that has no preauth context " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + /* + * No 'pre-auth' context exists for this STA that sent + * an Authentication frame3. Send Auth frame4 with + * 'out of sequence' status code. + */ + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_AUTH_FRAME_OUT_OF_SEQ_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + return; + } + + if (auth_node->mlmState == eLIM_MLM_AUTH_RSP_TIMEOUT_STATE) { + pe_warn("auth response timer timedout for peer " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + /* + * Received Auth Frame3 after Auth Response timeout. + * Reject by sending Auth Frame4 with + * Auth respone timeout Status Code. + */ + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_AUTH_RSP_TIMEOUT_STATUS; + + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + /* Delete pre-auth context of STA */ + lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa); + return; + } + if (rx_auth_frm_body->authStatusCode != + eSIR_MAC_SUCCESS_STATUS) { + /* + * Received Authenetication Frame 3 with status code + * other than success. Wait until Auth response timeout + * to delete STA context. + */ + pe_err("rx Auth frm3 from peer with status code %d " + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authStatusCode, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + /* + * Check if received challenge text is same as one sent in + * Authentication frame3 + */ + if (!qdf_mem_cmp(rx_auth_frm_body->challengeText, + auth_node->challengeText, + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH)) { + /* + * Challenge match. STA is autheticated + * Delete Authentication response timer if running + */ + lim_deactivate_and_change_per_sta_id_timer(mac_ctx, + eLIM_AUTH_RSP_TIMER, auth_node->authNodeIdx); + + auth_node->fTimerStarted = 0; + auth_node->mlmState = eLIM_MLM_AUTHENTICATED_STATE; + + /* + * Send Auth Frame4 with 'success' Status Code. + */ + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_SUCCESS_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + return; + } else { + pe_warn("Challenge failure for peer "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + /* + * Challenge Failure. + * Send Authentication frame4 with 'challenge failure' + * status code and wait until Auth response timeout to + * delete STA context. + */ + auth_frame->authAlgoNumber = + rx_auth_frm_body->authAlgoNumber; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_CHALLENGE_FAILURE_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + return; + } + } +} + +static void lim_process_auth_frame_type4(struct mac_context *mac_ctx, + tpSirMacMgmtHdr mac_hdr, + tSirMacAuthFrameBody *rx_auth_frm_body, + struct pe_session *pe_session) +{ + struct tLimPreAuthNode *auth_node; + + if (pe_session->limMlmState != eLIM_MLM_WT_AUTH_FRAME4_STATE) { + /* + * Received Authentication frame4 in an unexpected state. + * Log error and ignore the frame. + */ + pe_warn("received unexpected Auth frame4 from peer in state %d, addr " + QDF_MAC_ADDR_FMT, + pe_session->limMlmState, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + + if (rx_auth_frm_body->authAlgoNumber != eSIR_SHARED_KEY) { + /* + * Received Authentication frame4 with algorithm other than + * Shared Key authentication type. + * Wait until Auth failure timeout to report authentication + * failure to SME. + */ + pe_err("received Auth frame4 from peer with invalid auth algo %d" + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authAlgoNumber, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + + if (qdf_mem_cmp((uint8_t *) mac_hdr->sa, + (uint8_t *) &mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr, + sizeof(tSirMacAddr))) { + /* + * Received Authentication frame from an entity + * other than one to which request was initiated. + * Wait until Authentication Failure Timeout. + */ + + pe_warn("received Auth frame4 from unexpected peer "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + + if (rx_auth_frm_body->authAlgoNumber != + mac_ctx->lim.gpLimMlmAuthReq->authType) { + /* + * Received Authentication frame with an auth algorithm + * other than one requested. + * Wait until Authentication Failure Timeout. + */ + + pe_err("received Authentication frame from peer with invalid auth seq number %d " + QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authTransactionSeqNumber, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + + if (rx_auth_frm_body->authStatusCode == eSIR_MAC_SUCCESS_STATUS) { + /* + * Authentication Success, Inform SME of same. + */ + pe_session->limCurrentAuthType = eSIR_SHARED_KEY; + auth_node = lim_acquire_free_pre_auth_node(mac_ctx, + &mac_ctx->lim.gLimPreAuthTimerTable); + if (!auth_node) { + pe_warn("Max pre-auth nodes reached"); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW); + return; + } + pe_debug("Alloc new data: %pK peer", auth_node); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD); + qdf_mem_copy((uint8_t *) auth_node->peerMacAddr, + mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr, + sizeof(tSirMacAddr)); + auth_node->fTimerStarted = 0; + auth_node->authType = mac_ctx->lim.gpLimMlmAuthReq->authType; + auth_node->seq_num = ((mac_hdr->seqControl.seqNumHi << 4) | + (mac_hdr->seqControl.seqNumLo)); + auth_node->timestamp = qdf_mc_timer_get_system_ticks(); + lim_add_pre_auth_node(mac_ctx, auth_node); + lim_restore_from_auth_state(mac_ctx, eSIR_SME_SUCCESS, + rx_auth_frm_body->authStatusCode, pe_session); + } else { + /* + * Authentication failure. + * Return Auth confirm with received failure code to SME + */ + pe_err("Authentication failure from peer "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + lim_restore_from_auth_state(mac_ctx, eSIR_SME_AUTH_REFUSED, + rx_auth_frm_body->authStatusCode, + pe_session); + } +} + +/** + * lim_process_auth_frame() - to process auth frame + * @mac_ctx - Pointer to Global MAC structure + * @rx_pkt_info - A pointer to Rx packet info structure + * @session - A pointer to session + * + * This function is called by limProcessMessageQueue() upon Authentication + * frame reception. + * + * LOGIC: + * This function processes received Authentication frame and responds + * with either next Authentication frame in sequence to peer MAC entity + * or LIM_MLM_AUTH_IND on AP or LIM_MLM_AUTH_CNF on STA. + * + * NOTE: + * 1. Authentication failures are reported to SME with same status code + * received from the peer MAC entity. + * 2. Authentication frame2/4 received with alogirthm number other than + * one requested in frame1/3 are logged with an error and auth confirm + * will be sent to SME only after auth failure timeout. + * 3. Inconsistency in the spec: + * On receiving Auth frame2, specs says that if WEP key mapping key + * or default key is NULL, Auth frame3 with a status code 15 (challenge + * failure to be returned to peer entity. However, section 7.2.3.10, + * table 14 says that status code field is 'reserved' for frame3 ! + * In the current implementation, Auth frame3 is returned with status + * code 15 overriding section 7.2.3.10. + * 4. If number pre-authentications reach configrable max limit, + * Authentication frame with 'unspecified failure' status code is + * returned to requesting entity. + * + * Return: None + */ +void +lim_process_auth_frame(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + struct pe_session *pe_session) +{ + uint8_t *body_ptr, key_id, cfg_privacy_opt_imp; + uint8_t defaultkey[SIR_MAC_KEY_LENGTH]; + uint8_t *plainbody = NULL; + uint8_t decrypt_result; + uint16_t frame_len, curr_seq_num = 0, auth_alg; + uint32_t key_length = 8; + qdf_size_t val; + tSirMacAuthFrameBody *rx_auth_frm_body, *rx_auth_frame, *auth_frame; + tpSirMacMgmtHdr mac_hdr; + struct tLimPreAuthNode *auth_node; + struct wlan_mlme_wep_cfg *wep_params = &mac_ctx->mlme_cfg->wep_params; + QDF_STATUS qdf_status; + + /* Get pointer to Authentication frame header and body */ + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + if (!frame_len) { + /* Log error */ + pe_err("received Auth frame with no body from: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + + if (IEEE80211_IS_MULTICAST(mac_hdr->sa)) { + /* + * Received Auth frame from a BC/MC address + * Log error and ignore it + */ + pe_err("received Auth frame from a BC/MC addr: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + curr_seq_num = (mac_hdr->seqControl.seqNumHi << 4) | + (mac_hdr->seqControl.seqNumLo); + + if (pe_session->prev_auth_seq_num == curr_seq_num && + !qdf_mem_cmp(pe_session->prev_auth_mac_addr, &mac_hdr->sa, + ETH_ALEN) && + mac_hdr->fc.retry) { + pe_debug("auth frame, seq num: %d is already processed, drop it", + curr_seq_num); + return; + } + + /* save seq number and mac_addr in pe_session */ + pe_session->prev_auth_seq_num = curr_seq_num; + qdf_mem_copy(pe_session->prev_auth_mac_addr, mac_hdr->sa, ETH_ALEN); + + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + + if (frame_len < 2) { + pe_err("invalid frame len: %d", frame_len); + return; + } + auth_alg = *(uint16_t *) body_ptr; + + pe_nofl_rl_info("Auth RX: vdev %d sys role %d lim_state %d from " QDF_MAC_ADDR_FMT " rssi %d auth_alg %d seq %d", + pe_session->vdev_id, GET_LIM_SYSTEM_ROLE(pe_session), + pe_session->limMlmState, + QDF_MAC_ADDR_REF(mac_hdr->sa), + WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info), + auth_alg, curr_seq_num); + + /* Restore default failure timeout */ + if (QDF_P2P_CLIENT_MODE == pe_session->opmode && + pe_session->defaultAuthFailureTimeout) { + pe_debug("Restore default failure timeout"); + if (cfg_in_range(CFG_AUTH_FAILURE_TIMEOUT, + pe_session->defaultAuthFailureTimeout)) { + mac_ctx->mlme_cfg->timeouts.auth_failure_timeout = + pe_session->defaultAuthFailureTimeout; + } else { + mac_ctx->mlme_cfg->timeouts.auth_failure_timeout = + cfg_default(CFG_AUTH_FAILURE_TIMEOUT); + pe_session->defaultAuthFailureTimeout = + cfg_default(CFG_AUTH_FAILURE_TIMEOUT); + } + } + + rx_auth_frame = qdf_mem_malloc(sizeof(tSirMacAuthFrameBody)); + if (!rx_auth_frame) + return; + + auth_frame = qdf_mem_malloc(sizeof(tSirMacAuthFrameBody)); + if (!auth_frame) + goto free; + + plainbody = qdf_mem_malloc(LIM_ENCR_AUTH_BODY_LEN); + if (!plainbody) + goto free; + + qdf_mem_zero(plainbody, LIM_ENCR_AUTH_BODY_LEN); + + /* + * Determine if WEP bit is set in the FC or received MAC header + * Note: WEP bit is set in FC of MAC header. + */ + if (mac_hdr->fc.wep) { + /* + * If TKIP counter measures enabled then issue Deauth + * frame to station + */ + if (pe_session->bTkipCntrMeasActive && + LIM_IS_AP_ROLE(pe_session)) { + pe_err("Tkip counter enabled, send deauth to: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + lim_send_deauth_mgmt_frame(mac_ctx, + eSIR_MAC_MIC_FAILURE_REASON, + mac_hdr->sa, pe_session, false); + goto free; + } + if (frame_len < 4) { + pe_err("invalid frame len: %d", frame_len); + goto free; + } + /* Extract key ID from IV (most 2 bits of 4th byte of IV) */ + key_id = (*(body_ptr + 3)) >> 6; + + /* + * On STA in infrastructure BSS, Authentication frames received + * with WEP bit set in the FC must be rejected with challenge + * failure status code (weird thing in the spec - this should've + * been rejected with unspecified failure/unexpected assertion + * of wep bit (this status code does not exist though) or + * Out-of-sequence-Authentication-Frame status code. + */ + if (LIM_IS_STA_ROLE(pe_session)) { + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_CHALLENGE_FAILURE_STATUS; + /* Log error */ + pe_err("rx Auth frm with wep bit set role: %d "QDF_MAC_ADDR_FMT, + GET_LIM_SYSTEM_ROLE(pe_session), + QDF_MAC_ADDR_REF(mac_hdr->sa)); + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + goto free; + } + + if ((frame_len < LIM_ENCR_AUTH_BODY_LEN_SAP) || + (frame_len > LIM_ENCR_AUTH_BODY_LEN)) { + /* Log error */ + pe_err("Not enough size: %d to decry rx Auth frm", + frame_len); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGE); + goto free; + } + + /* + * Accept Authentication frame only if Privacy is + * implemented + */ + if (LIM_IS_AP_ROLE(pe_session)) + cfg_privacy_opt_imp = pe_session->privacy; + else + cfg_privacy_opt_imp = wep_params->is_privacy_enabled; + + if (!cfg_privacy_opt_imp) { + pe_err("received Authentication frame3 from peer that while privacy option is turned OFF " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + /* + * Privacy option is not implemented. + * So reject Authentication frame received with + * WEP bit set by sending Authentication frame + * with 'challenge failure' status code. This is + * another strange thing in the spec. Status code + * should have been 'unsupported algorithm' status code. + */ + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_CHALLENGE_FAILURE_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + goto free; + } + + /* + * Privacy option is implemented. Check if the received frame is + * Authentication frame3 and there is a context for requesting + * STA. If not, reject with unspecified failure status code + */ + auth_node = lim_search_pre_auth_list(mac_ctx, mac_hdr->sa); + if (!auth_node) { + pe_err("rx Auth frame with no preauth ctx with WEP bit set " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + /* + * No 'pre-auth' context exists for this STA + * that sent an Authentication frame with FC + * bit set. Send Auth frame4 with + * 'out of sequence' status code. + */ + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_AUTH_FRAME_OUT_OF_SEQ_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + goto free; + } + /* Change the auth-response timeout */ + lim_deactivate_and_change_per_sta_id_timer(mac_ctx, + eLIM_AUTH_RSP_TIMER, auth_node->authNodeIdx); + + /* 'Pre-auth' status exists for STA */ + if ((auth_node->mlmState != eLIM_MLM_WT_AUTH_FRAME3_STATE) && + (auth_node->mlmState != + eLIM_MLM_AUTH_RSP_TIMEOUT_STATE)) { + pe_err("received Authentication frame from peer that is in state %d " + QDF_MAC_ADDR_FMT, + auth_node->mlmState, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + /* + * Should not have received Authentication frame + * with WEP bit set in FC in other states. + * Reject by sending Authenticaton frame with + * out of sequence Auth frame status code. + */ + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_AUTH_FRAME_OUT_OF_SEQ_STATUS; + + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + goto free; + } + + val = SIR_MAC_KEY_LENGTH; + + if (LIM_IS_AP_ROLE(pe_session)) { + qdf_status = lim_get_wep_key_sap(pe_session, + wep_params, + key_id, + defaultkey, + &val); + } else { + qdf_status = mlme_get_wep_key(pe_session->vdev, + wep_params, + (MLME_WEP_DEFAULT_KEY_1 + + key_id), defaultkey, + &val); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + pe_warn("could not retrieve Default key"); + + /* + * Send Authentication frame + * with challenge failure status code + */ + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_CHALLENGE_FAILURE_STATUS; + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + goto free; + } + } + + key_length = val; + decrypt_result = lim_decrypt_auth_frame(mac_ctx, defaultkey, + body_ptr, plainbody, key_length, + (uint16_t) (frame_len - + SIR_MAC_WEP_IV_LENGTH)); + if (decrypt_result == LIM_DECRYPT_ICV_FAIL) { + pe_err("received Authentication frame from peer that failed decryption: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + /* ICV failure */ + lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa); + auth_frame->authAlgoNumber = eSIR_SHARED_KEY; + auth_frame->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + auth_frame->authStatusCode = + eSIR_MAC_CHALLENGE_FAILURE_STATUS; + + lim_send_auth_mgmt_frame(mac_ctx, auth_frame, + mac_hdr->sa, LIM_NO_WEP_IN_FC, + pe_session); + goto free; + } + if ((sir_convert_auth_frame2_struct(mac_ctx, plainbody, + frame_len - 8, rx_auth_frame) != QDF_STATUS_SUCCESS) + || (!is_auth_valid(mac_ctx, rx_auth_frame, + pe_session))) { + pe_err("failed to convert Auth Frame to structure or Auth is not valid"); + goto free; + } + } else if (auth_alg == eSIR_AUTH_TYPE_SAE) { + if (LIM_IS_STA_ROLE(pe_session) || + (LIM_IS_AP_ROLE(pe_session) && + mac_ctx->mlme_cfg->sap_cfg.sap_sae_enabled)) + lim_process_sae_auth_frame(mac_ctx, rx_pkt_info, + pe_session); + goto free; + } else if ((sir_convert_auth_frame2_struct(mac_ctx, body_ptr, + frame_len, rx_auth_frame) != QDF_STATUS_SUCCESS) + || (!is_auth_valid(mac_ctx, rx_auth_frame, + pe_session))) { + pe_err("failed to convert Auth Frame to structure or Auth is not valid"); + goto free; + } + + rx_auth_frm_body = rx_auth_frame; + + if (!lim_is_valid_fils_auth_frame(mac_ctx, pe_session, + rx_auth_frm_body)) { + pe_err("Received invalid FILS auth packet"); + goto free; + } + + /* + * IOT Workaround: with invalid WEP key, some APs reply + * AuthFrame 4 with invalid seqNumber. This AuthFrame + * will be dropped by driver, thus driver sends the + * generic status code instead of protocol status code. + * As a workaround, override AuthFrame 4's seqNumber. + */ + if ((pe_session->limMlmState == + eLIM_MLM_WT_AUTH_FRAME4_STATE) && + (rx_auth_frm_body->authTransactionSeqNumber != + SIR_MAC_AUTH_FRAME_1) && + (rx_auth_frm_body->authTransactionSeqNumber != + SIR_MAC_AUTH_FRAME_2) && + (rx_auth_frm_body->authTransactionSeqNumber != + SIR_MAC_AUTH_FRAME_3)) { + pe_warn("Override AuthFrame 4's seqNumber to 4"); + rx_auth_frm_body->authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_4; + } + + + switch (rx_auth_frm_body->authTransactionSeqNumber) { + case SIR_MAC_AUTH_FRAME_1: + lim_process_auth_frame_type1(mac_ctx, + mac_hdr, rx_auth_frm_body, rx_pkt_info, + curr_seq_num, auth_frame, pe_session); + break; + case SIR_MAC_AUTH_FRAME_2: + lim_process_auth_frame_type2(mac_ctx, + mac_hdr, rx_auth_frm_body, auth_frame, plainbody, + body_ptr, frame_len, pe_session); + break; + case SIR_MAC_AUTH_FRAME_3: + lim_process_auth_frame_type3(mac_ctx, + mac_hdr, rx_auth_frm_body, auth_frame, pe_session); + break; + case SIR_MAC_AUTH_FRAME_4: + lim_process_auth_frame_type4(mac_ctx, + mac_hdr, rx_auth_frm_body, pe_session); + break; + default: + /* Invalid Authentication Frame received. Ignore it. */ + pe_warn("rx auth frm with invalid authseq no: %d from: "QDF_MAC_ADDR_FMT, + rx_auth_frm_body->authTransactionSeqNumber, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + break; + } +free: + if (auth_frame) + qdf_mem_free(auth_frame); + if (rx_auth_frame) + qdf_mem_free(rx_auth_frame); + if (plainbody) + qdf_mem_free(plainbody); +} + +/** + * lim_process_sae_preauth_frame() - Send the WPA3 preauth SAE frame received + * to the user space. + * @mac: Global mac context + * @rx_pkt: Received auth packet + * + * SAE auth frame will be received with no session if its SAE preauth during + * roaming offloaded to the host. Forward this frame to the wpa supplicant. + * + * Return: True if auth algo is SAE else false + */ +static +bool lim_process_sae_preauth_frame(struct mac_context *mac, uint8_t *rx_pkt) +{ + tpSirMacMgmtHdr dot11_hdr; + uint16_t auth_alg, frm_len; + uint8_t *frm_body, pdev_id; + struct wlan_objmgr_vdev *vdev; + + dot11_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt); + frm_body = WMA_GET_RX_MPDU_DATA(rx_pkt); + frm_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt); + + if (frm_len < 2) { + pe_debug("LFR3: Invalid auth frame len:%d", frm_len); + return false; + } + + auth_alg = *(uint16_t *)frm_body; + if (auth_alg != eSIR_AUTH_TYPE_SAE) + return false; + + pe_debug("LFR3: SAE auth frame: seq_ctrl:0x%X auth_transaction_num:%d", + ((dot11_hdr->seqControl.seqNumHi << 8) | + (dot11_hdr->seqControl.seqNumLo << 4) | + (dot11_hdr->seqControl.fragNum)), *(uint16_t *)(frm_body + 2)); + pdev_id = wlan_objmgr_pdev_get_pdev_id(mac->pdev); + vdev = wlan_objmgr_get_vdev_by_macaddr_from_psoc( + mac->psoc, pdev_id, dot11_hdr->da, WLAN_LEGACY_SME_ID); + if (vdev) { + lim_sae_auth_cleanup_retry(mac, vdev->vdev_objmgr.vdev_id); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + } + + lim_send_sme_mgmt_frame_ind(mac, dot11_hdr->fc.subType, + (uint8_t *)dot11_hdr, + frm_len + sizeof(tSirMacMgmtHdr), + SME_SESSION_ID_ANY, + WMA_GET_RX_FREQ(rx_pkt), NULL, + WMA_GET_RX_RSSI_NORMALIZED(rx_pkt), + RXMGMT_FLAG_NONE); + return true; +} + +/** + * + * Pass the received Auth frame. This is possibly the pre-auth from the + * neighbor AP, in the same mobility domain. + * This will be used in case of 11r FT. + * + ***---------------------------------------------------------------------- + */ +QDF_STATUS lim_process_auth_frame_no_session(struct mac_context *mac, uint8_t *pBd, + void *body) +{ + tpSirMacMgmtHdr pHdr; + struct pe_session *pe_session = NULL; + uint8_t *pBody; + uint16_t frameLen; + tSirMacAuthFrameBody rxAuthFrame; + tSirMacAuthFrameBody *pRxAuthFrameBody = NULL; + QDF_STATUS ret_status = QDF_STATUS_E_FAILURE; + int i; + bool sae_auth_frame; + + pHdr = WMA_GET_RX_MAC_HEADER(pBd); + pBody = WMA_GET_RX_MPDU_DATA(pBd); + frameLen = WMA_GET_RX_PAYLOAD_LEN(pBd); + + pe_debug("Auth Frame Received: BSSID " QDF_MAC_ADDR_FMT " (RSSI %d)", + QDF_MAC_ADDR_REF(pHdr->bssId), + (uint) abs((int8_t) WMA_GET_RX_RSSI_NORMALIZED(pBd))); + + if (frameLen == 0) { + pe_err("Frame len = 0"); + return QDF_STATUS_E_FAILURE; + } + + sae_auth_frame = lim_process_sae_preauth_frame(mac, pBd); + if (sae_auth_frame) + return QDF_STATUS_SUCCESS; + + /* Auth frame has come on a new BSS, however, we need to find the session + * from where the auth-req was sent to the new AP + */ + for (i = 0; i < mac->lim.maxBssId; i++) { + /* Find first free room in session table */ + if (mac->lim.gpSession[i].valid == true && + mac->lim.gpSession[i].ftPEContext.ftPreAuthSession == + true) { + /* Found the session */ + pe_session = &mac->lim.gpSession[i]; + mac->lim.gpSession[i].ftPEContext.ftPreAuthSession = + false; + } + } + + if (!pe_session) { + pe_debug("cannot find session id in FT pre-auth phase"); + return QDF_STATUS_E_FAILURE; + } + + if (!pe_session->ftPEContext.pFTPreAuthReq) { + pe_err("Error: No FT"); + /* No FT in progress. */ + return QDF_STATUS_E_FAILURE; + } + + lim_print_mac_addr(mac, pHdr->bssId, LOGD); + lim_print_mac_addr(mac, + pe_session->ftPEContext.pFTPreAuthReq->preAuthbssId, + LOGD); + pe_debug("seqControl: 0x%X", + ((pHdr->seqControl.seqNumHi << 8) | + (pHdr->seqControl.seqNumLo << 4) | + (pHdr->seqControl.fragNum))); + + /* Check that its the same bssId we have for preAuth */ + if (qdf_mem_cmp + (pe_session->ftPEContext.pFTPreAuthReq->preAuthbssId, + pHdr->bssId, sizeof(tSirMacAddr))) { + pe_err("Error: Same bssid as preauth BSSID"); + /* In this case SME if indeed has triggered a */ + /* pre auth it will time out. */ + return QDF_STATUS_E_FAILURE; + } + + if (true == + pe_session->ftPEContext.pFTPreAuthReq->bPreAuthRspProcessed) { + /* + * This is likely a duplicate for the same pre-auth request. + * PE/LIM already posted a response to SME. Hence, drop it. + * TBD: + * 1) How did we even receive multiple auth responses? + * 2) Do we need to delete pre-auth session? Suppose we + * previously received an auth resp with failure which + * would not have created the session and forwarded to SME. + * And, we subsequently received an auth resp with success + * which would have created the session. This will now be + * dropped without being forwarded to SME! However, it is + * very unlikely to receive auth responses from the same + * AP with different reason codes. + * NOTE: return QDF_STATUS_SUCCESS so that the packet is dropped + * as this was indeed a response from the BSSID we tried to + * pre-auth. + */ + pe_debug("Auth rsp already posted to SME" + " (session %pK, FT session %pK)", pe_session, + pe_session); + return QDF_STATUS_SUCCESS; + } else { + pe_warn("Auth rsp not yet posted to SME" + " (session %pK, FT session %pK)", pe_session, + pe_session); + pe_session->ftPEContext.pFTPreAuthReq->bPreAuthRspProcessed = + true; + } + + /* Stopping timer now, that we have our unicast from the AP */ + /* of our choice. */ + lim_deactivate_and_change_timer(mac, eLIM_FT_PREAUTH_RSP_TIMER); + + /* Save off the auth resp. */ + if ((sir_convert_auth_frame2_struct(mac, pBody, frameLen, &rxAuthFrame) != + QDF_STATUS_SUCCESS)) { + pe_err("failed to convert Auth frame to struct"); + lim_handle_ft_pre_auth_rsp(mac, QDF_STATUS_E_FAILURE, NULL, 0, + pe_session); + return QDF_STATUS_E_FAILURE; + } + pRxAuthFrameBody = &rxAuthFrame; + + pe_debug("Received Auth frame with type: %d seqnum: %d status: %d %d", + (uint32_t) pRxAuthFrameBody->authAlgoNumber, + (uint32_t) pRxAuthFrameBody->authTransactionSeqNumber, + (uint32_t) pRxAuthFrameBody->authStatusCode, + (uint32_t) mac->lim.gLimNumPreAuthContexts); + switch (pRxAuthFrameBody->authTransactionSeqNumber) { + case SIR_MAC_AUTH_FRAME_2: + if (pRxAuthFrameBody->authStatusCode != eSIR_MAC_SUCCESS_STATUS) { + pe_err("Auth status code received is %d", + (uint32_t) pRxAuthFrameBody->authStatusCode); + if (eSIR_MAC_MAX_ASSOC_STA_REACHED_STATUS == + pRxAuthFrameBody->authStatusCode) + ret_status = QDF_STATUS_E_NOSPC; + } else { + ret_status = QDF_STATUS_SUCCESS; + } + break; + + default: + pe_warn("Seq. no incorrect expected 2 received %d", + (uint32_t) pRxAuthFrameBody->authTransactionSeqNumber); + break; + } + + /* Send the Auth response to SME */ + lim_handle_ft_pre_auth_rsp(mac, ret_status, pBody, frameLen, pe_session); + + return ret_status; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_beacon_frame.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_beacon_frame.c new file mode 100644 index 0000000000000000000000000000000000000000..b14b6376f130985ac1a3e4ff7d116c6885bb270f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_beacon_frame.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_process_beacon_frame.cc contains the code + * for processing Received Beacon Frame. + * Author: Chandra Modumudi + * Date: 03/01/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "wni_cfg.h" +#include "ani_global.h" +#include "sch_api.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_prop_exts_utils.h" +#include "lim_ser_des_utils.h" + +/** + * lim_process_beacon_frame() - to process beacon frames + * @mac_ctx: Pointer to Global MAC structure + * @rx_pkt_info: A pointer to RX packet info structure + * @session: A pointer to session + * + * This function is called by limProcessMessageQueue() upon Beacon + * frame reception. + * Note: + * 1. Beacons received in 'normal' state in IBSS are handled by + * Beacon Processing module. + * + * Return: none + */ + +void +lim_process_beacon_frame(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + struct pe_session *session) +{ + tpSirMacMgmtHdr mac_hdr; + tSchBeaconStruct *bcn_ptr; + uint8_t *frame; + const uint8_t *owe_transition_ie; + uint16_t frame_len; + + mac_ctx->lim.gLimNumBeaconsRcvd++; + + /* + * here is it required to increment session specific heartBeat + * beacon counter + */ + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + frame = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + pe_debug("Beacon (len %d): " QDF_MAC_ADDR_FMT " RSSI %d", + WMA_GET_RX_MPDU_LEN(rx_pkt_info), + QDF_MAC_ADDR_REF(mac_hdr->sa), + (uint)abs((int8_t) + WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info))); + + /* Expect Beacon in any state as Scan is independent of LIM state */ + bcn_ptr = qdf_mem_malloc(sizeof(*bcn_ptr)); + if (!bcn_ptr) + return; + + /* Parse received Beacon */ + if (sir_convert_beacon_frame2_struct(mac_ctx, + rx_pkt_info, bcn_ptr) != + QDF_STATUS_SUCCESS) { + /* + * Received wrongly formatted/invalid Beacon. + * Ignore it and move on. + */ + pe_warn("Received invalid Beacon in state: %X", + session->limMlmState); + lim_print_mlm_state(mac_ctx, LOGW, + session->limMlmState); + qdf_mem_free(bcn_ptr); + return; + } + + /* + * during scanning, when any session is active, and + * beacon/Pr belongs to one of the session, fill up the + * following, TBD - HB counter + */ + if (sir_compare_mac_addr(session->bssId, + bcn_ptr->bssid)) { + qdf_mem_copy((uint8_t *)&session->lastBeaconTimeStamp, + (uint8_t *) bcn_ptr->timeStamp, + sizeof(uint64_t)); + session->currentBssBeaconCnt++; + } + MTRACE(mac_trace(mac_ctx, + TRACE_CODE_RX_MGMT_TSF, 0, bcn_ptr->timeStamp[0])); + MTRACE(mac_trace(mac_ctx, TRACE_CODE_RX_MGMT_TSF, 0, + bcn_ptr->timeStamp[1])); + + if (session->limMlmState == + eLIM_MLM_WT_JOIN_BEACON_STATE) { + owe_transition_ie = wlan_get_vendor_ie_ptr_from_oui( + OWE_TRANSITION_OUI_TYPE, + OWE_TRANSITION_OUI_SIZE, + frame + SIR_MAC_B_PR_SSID_OFFSET, + frame_len - SIR_MAC_B_PR_SSID_OFFSET); + if (session->connected_akm == ANI_AKM_TYPE_OWE && + owe_transition_ie) { + pe_debug("vdev:%d Drop OWE rx beacon. Wait for probe for join success", + session->vdev_id); + qdf_mem_free(bcn_ptr); + return; + } + + if (session->beacon) { + qdf_mem_free(session->beacon); + session->beacon = NULL; + session->bcnLen = 0; + } + session->bcnLen = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + session->beacon = qdf_mem_malloc(session->bcnLen); + if (session->beacon) + /* + * Store the Beacon/ProbeRsp. This is sent to + * csr/hdd in join cnf response. + */ + qdf_mem_copy(session->beacon, + WMA_GET_RX_MPDU_DATA(rx_pkt_info), + session->bcnLen); + + lim_check_and_announce_join_success(mac_ctx, bcn_ptr, + mac_hdr, session); + } + qdf_mem_free(bcn_ptr); + return; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_cfg_updates.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_cfg_updates.c new file mode 100644 index 0000000000000000000000000000000000000000..31f1be6d43b6ae1a86a88d52b0bc6431cff4cd1a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_cfg_updates.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_process_cfg_updates.cc contains the utility functions + * to handle various CFG parameter update events + * Author: Chandra Modumudi + * Date: 01/20/03 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#include "ani_global.h" + +#include "wni_cfg.h" +#include "sir_mac_prot_def.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_prop_exts_utils.h" +#include "sch_api.h" +#include "rrm_api.h" + +static void lim_update_config(struct mac_context *mac, struct pe_session *pe_session); + +void lim_set_cfg_protection(struct mac_context *mac, struct pe_session *pesessionEntry) +{ + uint32_t val = 0; + struct wlan_mlme_cfg *mlme_cfg = mac->mlme_cfg; + + if (pesessionEntry && LIM_IS_AP_ROLE(pesessionEntry)) { + if (pesessionEntry->gLimProtectionControl == + MLME_FORCE_POLICY_PROTECTION_DISABLE) + qdf_mem_zero((void *)&pesessionEntry->cfgProtection, + sizeof(tCfgProtection)); + else { + pe_debug("frm11a = %d, from11b = %d, frm11g = %d, " + "ht20 = %d, nongf = %d, lsigTxop = %d, " + "rifs = %d, obss = %d", + pesessionEntry->cfgProtection.fromlla, + pesessionEntry->cfgProtection.fromllb, + pesessionEntry->cfgProtection.fromllg, + pesessionEntry->cfgProtection.ht20, + pesessionEntry->cfgProtection.nonGf, + pesessionEntry->cfgProtection.lsigTxop, + pesessionEntry->cfgProtection.rifs, + pesessionEntry->cfgProtection.obss); + } + } else { + mac->lim.gLimProtectionControl = + mlme_cfg->sap_protection_cfg.protection_force_policy; + + + if (mac->lim.gLimProtectionControl == + MLME_FORCE_POLICY_PROTECTION_DISABLE) + qdf_mem_zero((void *)&mac->lim.cfgProtection, + sizeof(tCfgProtection)); + else { + val = mlme_cfg->sap_protection_cfg.protection_enabled; + + mac->lim.cfgProtection.fromlla = + (val >> MLME_PROTECTION_ENABLED_FROM_llA) & 1; + mac->lim.cfgProtection.fromllb = + (val >> MLME_PROTECTION_ENABLED_FROM_llB) & 1; + mac->lim.cfgProtection.fromllg = + (val >> MLME_PROTECTION_ENABLED_FROM_llG) & 1; + mac->lim.cfgProtection.ht20 = + (val >> MLME_PROTECTION_ENABLED_HT_20) & 1; + mac->lim.cfgProtection.nonGf = + (val >> MLME_PROTECTION_ENABLED_NON_GF) & 1; + mac->lim.cfgProtection.lsigTxop = + (val >> MLME_PROTECTION_ENABLED_LSIG_TXOP) & 1; + mac->lim.cfgProtection.rifs = + (val >> MLME_PROTECTION_ENABLED_RIFS) & 1; + mac->lim.cfgProtection.obss = + (val >> MLME_PROTECTION_ENABLED_OBSS) & 1; + + } + } +} + +/** + * lim_handle_param_update() + * + ***FUNCTION: + * This function is use to post a message whenever need indicate + * there is update of config parameter. + * + ***PARAMS: + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param cfgId - ID of CFG parameter that got updated + * @return None + */ +void lim_handle_param_update(struct mac_context *mac, eUpdateIEsType cfgId) +{ + struct scheduler_msg msg = { 0 }; + QDF_STATUS status; + + pe_debug("Handling CFG parameter id %X update", cfgId); + + switch (cfgId) { + case eUPDATE_IE_PROBE_BCN: + { + msg.type = SIR_LIM_UPDATE_BEACON; + status = lim_post_msg_api(mac, &msg); + + if (status != QDF_STATUS_SUCCESS) + pe_err("Failed lim_post_msg_api %u", status); + break; + } + default: + break; + } +} + +/** + * lim_apply_configuration() + * + ***FUNCTION: + * This function is called to apply the configured parameters + * before joining or reassociating with a BSS or starting a BSS. + * + ***PARAMS: + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @return None + */ + +void lim_apply_configuration(struct mac_context *mac, struct pe_session *pe_session) +{ + uint32_t phyMode; + + pe_session->limSentCapsChangeNtf = false; + + lim_get_phy_mode(mac, &phyMode, pe_session); + + lim_update_config(mac, pe_session); + + lim_get_short_slot_from_phy_mode(mac, pe_session, phyMode, + &pe_session->shortSlotTimeSupported); + + lim_set_cfg_protection(mac, pe_session); + + /* Added for BT - AMP Support */ + if (LIM_IS_AP_ROLE(pe_session) || + LIM_IS_IBSS_ROLE(pe_session)) { + /* This check is required to ensure the beacon generation is not done + as a part of join request for a BT-AMP station */ + + if (pe_session->statypeForBss == STA_ENTRY_SELF) { + sch_set_beacon_interval(mac, pe_session); + sch_set_fixed_beacon_fields(mac, pe_session); + } + } +} /*** end lim_apply_configuration() ***/ + +/** + * lim_update_config + * + * FUNCTION: + * Update the local state from CFG database + * (This used to be dphUpdateConfig) + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param None + * @return None + */ + +static void lim_update_config(struct mac_context *mac, struct pe_session *pe_session) +{ + bool enabled; + + pe_session->beaconParams.fShortPreamble = + mac->mlme_cfg->ht_caps.short_preamble; + + /* In STA case this parameter is filled during the join request */ + if (LIM_IS_AP_ROLE(pe_session) || + LIM_IS_IBSS_ROLE(pe_session)) { + enabled = mac->mlme_cfg->wmm_params.wme_enabled; + pe_session->limWmeEnabled = enabled; + } + enabled = mac->mlme_cfg->wmm_params.wsm_enabled; + pe_session->limWsmEnabled = enabled; + + if ((!pe_session->limWmeEnabled) && (pe_session->limWsmEnabled)) { + pe_err("Can't enable WSM without WME"); + pe_session->limWsmEnabled = 0; + } + /* In STA , this parameter is filled during the join request */ + if (LIM_IS_AP_ROLE(pe_session) || LIM_IS_IBSS_ROLE(pe_session)) { + enabled = mac->mlme_cfg->wmm_params.qos_enabled; + pe_session->limQosEnabled = enabled; + } + pe_session->limHcfEnabled = mac->mlme_cfg->feature_flags.enable_hcf; + + /* AP: WSM should enable HCF as well, for STA enable WSM only after */ + /* association response is received */ + if (pe_session->limWsmEnabled && LIM_IS_AP_ROLE(pe_session)) + pe_session->limHcfEnabled = 1; + + pe_debug("Updated Lim shadow state based on CFG"); +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_deauth_frame.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_deauth_frame.c new file mode 100644 index 0000000000000000000000000000000000000000..b1ab6715309209e9838a82c9641824cc07fc19a8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_deauth_frame.c @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_process_deauth_frame.cc contains the code + * for processing Deauthentication Frame. + * Author: Chandra Modumudi + * Date: 03/24/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#include "cds_api.h" +#include "ani_global.h" + +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "sch_api.h" +#include "lim_send_messages.h" + +/** + * lim_process_deauth_frame + * + ***FUNCTION: + * This function is called by limProcessMessageQueue() upon + * Deauthentication frame reception. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param *pRxPacketInfo - A pointer to Buffer descriptor + associated PDUs + * @return None + */ + +void +lim_process_deauth_frame(struct mac_context *mac, uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + uint8_t *pBody; + uint16_t reasonCode; + tpSirMacMgmtHdr pHdr; + struct pe_session *pRoamSessionEntry = NULL; + uint8_t roamSessionId; +#ifdef WLAN_FEATURE_11W + uint32_t frameLen; +#endif + int32_t frame_rssi; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + frame_rssi = (int32_t)WMA_GET_RX_RSSI_NORMALIZED(pRxPacketInfo); +#ifdef WLAN_FEATURE_11W + frameLen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + if (frameLen < sizeof(reasonCode)) { + pe_err("Deauth Frame length invalid %d", frameLen); + return ; + } +#endif + + if (LIM_IS_STA_ROLE(pe_session) && + ((eLIM_SME_WT_DISASSOC_STATE == pe_session->limSmeState) || + (eLIM_SME_WT_DEAUTH_STATE == pe_session->limSmeState))) { + /*Every 15th deauth frame will be logged in kmsg */ + if (!(mac->lim.deauthMsgCnt & 0xF)) { + pe_debug("received Deauth frame in DEAUTH_WT_STATE" + "(already processing previously received DEAUTH frame)" + "Dropping this.. Deauth Failed %d", + ++mac->lim.deauthMsgCnt); + } else { + mac->lim.deauthMsgCnt++; + } + return; + } + + if (IEEE80211_IS_MULTICAST(pHdr->sa)) { + /* Received Deauth frame from a BC/MC address */ + /* Log error and ignore it */ + pe_debug("received Deauth frame from a BC/MC address"); + return; + } + + if (IEEE80211_IS_MULTICAST(pHdr->da) && + !QDF_IS_ADDR_BROADCAST(pHdr->da)) { + /* Received Deauth frame for a MC address */ + /* Log error and ignore it */ + pe_debug("received Deauth frame for a MC address"); + return; + } + if (!lim_validate_received_frame_a1_addr(mac, + pHdr->da, pe_session)) { + pe_err("rx frame doesn't have valid a1 address, drop it"); + return; + } +#ifdef WLAN_FEATURE_11W + /* PMF: If this session is a PMF session, then ensure that this frame was protected */ + if (pe_session->limRmfEnabled + && (WMA_GET_RX_DPU_FEEDBACK(pRxPacketInfo) & + DPU_FEEDBACK_UNPROTECTED_ERROR)) { + pe_debug("received an unprotected deauth from AP"); + /* + * When 11w offload is enabled then + * firmware should not fwd this frame + */ + if (LIM_IS_STA_ROLE(pe_session) && mac->pmf_offload) { + pe_err("11w offload is enable,unprotected deauth is not expected"); + return; + } + + /* If the frame received is unprotected, forward it to the supplicant to initiate */ + /* an SA query */ + + /* send the unprotected frame indication to SME */ + lim_send_sme_unprotected_mgmt_frame_ind(mac, pHdr->fc.subType, + (uint8_t *) pHdr, + (frameLen + + sizeof(tSirMacMgmtHdr)), + pe_session->smeSessionId, + pe_session); + return; + } +#endif + + /* Get reasonCode from Deauthentication frame body */ + reasonCode = sir_read_u16(pBody); + + pe_nofl_rl_info("Deauth RX: vdev %d from " QDF_MAC_ADDR_FMT " for " QDF_MAC_ADDR_FMT " RSSI = %d reason %d mlm state = %d, sme state = %d systemrole = %d ", + pe_session->vdev_id, QDF_MAC_ADDR_REF(pHdr->sa), + QDF_MAC_ADDR_REF(pHdr->da), frame_rssi, + reasonCode, pe_session->limMlmState, + pe_session->limSmeState, + GET_LIM_SYSTEM_ROLE(pe_session)); + + lim_diag_event_report(mac, WLAN_PE_DIAG_DEAUTH_FRAME_EVENT, + pe_session, 0, reasonCode); + + if (lim_check_disassoc_deauth_ack_pending(mac, (uint8_t *) pHdr->sa)) { + pe_debug("Ignore the Deauth received, while waiting for ack of " + "disassoc/deauth"); + lim_clean_up_disassoc_deauth_req(mac, (uint8_t *) pHdr->sa, 1); + return; + } + + if (LIM_IS_AP_ROLE(pe_session)) { + switch (reasonCode) { + case eSIR_MAC_UNSPEC_FAILURE_REASON: + case eSIR_MAC_DEAUTH_LEAVING_BSS_REASON: + /* Valid reasonCode in received Deauthentication frame */ + break; + + default: + /* Invalid reasonCode in received Deauthentication frame */ + /* Log error and ignore the frame */ + pe_err("received Deauth frame with invalid reasonCode %d from " + QDF_MAC_ADDR_FMT, reasonCode, + QDF_MAC_ADDR_REF(pHdr->sa)); + + break; + } + } else if (LIM_IS_STA_ROLE(pe_session)) { + switch (reasonCode) { + case eSIR_MAC_UNSPEC_FAILURE_REASON: + case eSIR_MAC_PREV_AUTH_NOT_VALID_REASON: + case eSIR_MAC_DEAUTH_LEAVING_BSS_REASON: + case eSIR_MAC_CLASS2_FRAME_FROM_NON_AUTH_STA_REASON: + case eSIR_MAC_CLASS3_FRAME_FROM_NON_ASSOC_STA_REASON: + case eSIR_MAC_STA_NOT_PRE_AUTHENTICATED_REASON: + /* Valid reasonCode in received Deauth frame */ + break; + + default: + /* Invalid reasonCode in received Deauth frame */ + /* Log error and ignore the frame */ + pe_err("received Deauth frame with invalid reasonCode %d from " + QDF_MAC_ADDR_FMT, reasonCode, + QDF_MAC_ADDR_REF(pHdr->sa)); + + break; + } + } else { + /* Received Deauth frame in either IBSS */ + /* or un-known role. Log and ignore it */ + pe_err("received Deauth frame with reasonCode %d in role %d from " + QDF_MAC_ADDR_FMT, reasonCode, + GET_LIM_SYSTEM_ROLE(pe_session), + QDF_MAC_ADDR_REF(pHdr->sa)); + + return; + } + + /** If we are in the middle of ReAssoc, a few things could happen: + * - STA is reassociating to current AP, and receives deauth from: + * a) current AP + * b) other AP + * - STA is reassociating to a new AP, and receives deauth from: + * c) current AP + * d) reassoc AP + * e) other AP + * + * The logic is: + * 1) If rcv deauth from an AP other than the one we're trying to + * reassociate with, then drop the deauth frame (case b, c, e) + * 2) If rcv deauth from the "new" reassoc AP (case d), then restore + * context with previous AP and send SME_REASSOC_RSP failure. + * 3) If rcv deauth from the reassoc AP, which is also the same + * AP we're currently associated with (case a), then proceed + * with normal deauth processing. + */ + pRoamSessionEntry = + pe_find_session_by_bssid(mac, pe_session->limReAssocbssId, + &roamSessionId); + + if (lim_is_reassoc_in_progress(mac, pe_session) || + lim_is_reassoc_in_progress(mac, pRoamSessionEntry) || + pe_session->fw_roaming_started) { + /* + * For LFR3, the roaming bssid is not known during ROAM_START, + * so check if the deauth is received from current AP when + * roaming is being done in the firmware + */ + if (pe_session->fw_roaming_started && + IS_CURRENT_BSSID(mac, pHdr->sa, pe_session)) { + pe_debug("LFR3: Drop deauth frame from connected AP"); + /* + * recvd_deauth_while_roaming will be stored in the + * current AP session amd if roaming has been aborted + * for some reason and come back to same AP, then issue + * a disconnect internally if this flag is true. There + * is no need to reset this flag to false, because if + * roaming succeeds, then this session gets deleted and + * new session is created. + */ + pe_session->recvd_deauth_while_roaming = true; + pe_session->deauth_disassoc_rc = reasonCode; + return; + } + if (!IS_REASSOC_BSSID(mac, pHdr->sa, pe_session)) { + pe_debug("Rcv Deauth from unknown/different " + "AP while ReAssoc. Ignore " QDF_MAC_ADDR_FMT + "limReAssocbssId : " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pHdr->sa), + QDF_MAC_ADDR_REF(pe_session->limReAssocbssId)); + return; + } + + /** Received deauth from the new AP to which we tried to ReAssociate. + * Drop ReAssoc and Restore the Previous context( current connected AP). + */ + if (!IS_CURRENT_BSSID(mac, pHdr->sa, pe_session)) { + pe_debug("received DeAuth from the New AP to " + "which ReAssoc is sent " QDF_MAC_ADDR_FMT + "pe_session->bssId: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pHdr->sa), + QDF_MAC_ADDR_REF(pe_session->bssId)); + + lim_restore_pre_reassoc_state(mac, + eSIR_SME_REASSOC_REFUSED, + reasonCode, + pe_session); + return; + } + } + + /* If received DeAuth from AP other than the one we're trying to join with + * nor associated with, then ignore deauth and delete Pre-auth entry. + */ + if (!LIM_IS_AP_ROLE(pe_session)) { + if (!IS_CURRENT_BSSID(mac, pHdr->bssId, pe_session)) { + pe_err("received DeAuth from an AP other " + "than we're trying to join. Ignore. " + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(pHdr->sa)); + + if (lim_search_pre_auth_list(mac, pHdr->sa)) { + pe_debug("Preauth entry exist. Deleting"); + lim_delete_pre_auth_node(mac, pHdr->sa); + } + return; + } + } + + lim_extract_ies_from_deauth_disassoc(pe_session, (uint8_t *)pHdr, + WMA_GET_RX_MPDU_LEN(pRxPacketInfo)); + lim_perform_deauth(mac, pe_session, reasonCode, pHdr->sa, + frame_rssi); + + if (mac->mlme_cfg->gen.fatal_event_trigger && + (reasonCode != eSIR_MAC_UNSPEC_FAILURE_REASON && + reasonCode != eSIR_MAC_DEAUTH_LEAVING_BSS_REASON && + reasonCode != eSIR_MAC_DISASSOC_LEAVING_BSS_REASON)) { + cds_flush_logs(WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_REASON_DISCONNECT, + false, false); + } + +} /*** end lim_process_deauth_frame() ***/ + +void lim_perform_deauth(struct mac_context *mac_ctx, struct pe_session *pe_session, + uint16_t rc, tSirMacAddr addr, int32_t frame_rssi) +{ + tLimMlmDeauthInd mlmDeauthInd; + tLimMlmAssocCnf mlmAssocCnf; + uint16_t aid; + tpDphHashNode sta_ds; + + sta_ds = dph_lookup_hash_entry(mac_ctx, addr, &aid, + &pe_session->dph.dphHashTable); + if (!sta_ds) { + pe_debug("Hash entry not found"); + return; + } + /* Check for pre-assoc states */ + switch (GET_LIM_SYSTEM_ROLE(pe_session)) { + case eLIM_STA_ROLE: + switch (pe_session->limMlmState) { + case eLIM_MLM_WT_AUTH_FRAME2_STATE: + /** + * AP sent Deauth frame while waiting + * for Auth frame2. Report Auth failure + * to SME. + */ + + pe_debug("received Deauth frame state %X with failure " + "code %d from " QDF_MAC_ADDR_FMT, + pe_session->limMlmState, rc, + QDF_MAC_ADDR_REF(addr)); + + lim_restore_from_auth_state(mac_ctx, + eSIR_SME_DEAUTH_WHILE_JOIN, + rc, pe_session); + + return; + + case eLIM_MLM_AUTHENTICATED_STATE: + pe_debug("received Deauth frame state %X with " + "reasonCode=%d from " QDF_MAC_ADDR_FMT, + pe_session->limMlmState, rc, + QDF_MAC_ADDR_REF(addr)); + /* / Issue Deauth Indication to SME. */ + qdf_mem_copy((uint8_t *) &mlmDeauthInd.peerMacAddr, + addr, sizeof(tSirMacAddr)); + mlmDeauthInd.reasonCode = rc; + + pe_session->limMlmState = eLIM_MLM_IDLE_STATE; + MTRACE(mac_trace + (mac_ctx, TRACE_CODE_MLM_STATE, + pe_session->peSessionId, + pe_session->limMlmState)); + + lim_post_sme_message(mac_ctx, + LIM_MLM_DEAUTH_IND, + (uint32_t *) &mlmDeauthInd); + return; + + case eLIM_MLM_WT_ASSOC_RSP_STATE: + /** + * AP may have 'aged-out' our Pre-auth + * context. Delete local pre-auth context + * if any and issue ASSOC_CNF to SME. + */ + pe_debug("received Deauth frame state %X with " + "reasonCode=%d from " QDF_MAC_ADDR_FMT, + pe_session->limMlmState, rc, + QDF_MAC_ADDR_REF(addr)); + if (lim_search_pre_auth_list(mac_ctx, addr)) + lim_delete_pre_auth_node(mac_ctx, addr); + + if (pe_session->pLimMlmJoinReq) { + qdf_mem_free(pe_session->pLimMlmJoinReq); + pe_session->pLimMlmJoinReq = NULL; + } + + mlmAssocCnf.resultCode = eSIR_SME_DEAUTH_WHILE_JOIN; + mlmAssocCnf.protStatusCode = rc; + + /* PE session Id */ + mlmAssocCnf.sessionId = pe_session->peSessionId; + + pe_session->limMlmState = + pe_session->limPrevMlmState; + MTRACE(mac_trace + (mac_ctx, TRACE_CODE_MLM_STATE, + pe_session->peSessionId, + pe_session->limMlmState)); + + /* Deactive Association response timeout */ + lim_deactivate_and_change_timer(mac_ctx, + eLIM_ASSOC_FAIL_TIMER); + + lim_post_sme_message(mac_ctx, + LIM_MLM_ASSOC_CNF, + (uint32_t *) &mlmAssocCnf); + + return; + + case eLIM_MLM_WT_ADD_STA_RSP_STATE: + pe_session->fDeauthReceived = true; + pe_debug("Received Deauth frame in state %X with Reason " + "Code %d from Peer" QDF_MAC_ADDR_FMT, + pe_session->limMlmState, rc, + QDF_MAC_ADDR_REF(addr)); + return; + + case eLIM_MLM_IDLE_STATE: + case eLIM_MLM_LINK_ESTABLISHED_STATE: +#ifdef FEATURE_WLAN_TDLS + if ((sta_ds) + && (STA_ENTRY_TDLS_PEER == sta_ds->staType)) { + pe_err("received Deauth frame in state %X with " + "reason code %d from Tdls peer" + QDF_MAC_ADDR_FMT, + pe_session->limMlmState, rc, + QDF_MAC_ADDR_REF(addr)); + lim_send_sme_tdls_del_sta_ind(mac_ctx, sta_ds, + pe_session, + rc); + return; + } else { + + /* + * Delete all the TDLS peers only if Deauth + * is received from the AP + */ + if (IS_CURRENT_BSSID(mac_ctx, addr, pe_session)) + lim_delete_tdls_peers(mac_ctx, pe_session); +#endif + /** + * This could be Deauthentication frame from + * a BSS with which pre-authentication was + * performed. Delete Pre-auth entry if found. + */ + if (lim_search_pre_auth_list(mac_ctx, addr)) + lim_delete_pre_auth_node(mac_ctx, addr); +#ifdef FEATURE_WLAN_TDLS + } +#endif + break; + + case eLIM_MLM_WT_REASSOC_RSP_STATE: + pe_err("received Deauth frame state %X with " + "reasonCode=%d from " QDF_MAC_ADDR_FMT, + pe_session->limMlmState, rc, + QDF_MAC_ADDR_REF(addr)); + break; + + case eLIM_MLM_WT_FT_REASSOC_RSP_STATE: + pe_err("received Deauth frame in FT state %X with " + "reasonCode=%d from " QDF_MAC_ADDR_FMT, + pe_session->limMlmState, rc, + QDF_MAC_ADDR_REF(addr)); + break; + + default: + pe_err("received Deauth frame in state %X with " + "reasonCode=%d from " QDF_MAC_ADDR_FMT, + pe_session->limMlmState, rc, + QDF_MAC_ADDR_REF(addr)); + return; + } + break; + + case eLIM_STA_IN_IBSS_ROLE: + break; + + case eLIM_AP_ROLE: + break; + + default: + return; + } /* end switch (mac->lim.gLimSystemRole) */ + + /** + * Extract 'associated' context for STA, if any. + * This is maintained by DPH and created by LIM. + */ + if (!sta_ds) { + pe_err("sta_ds is NULL"); + return; + } + + if ((sta_ds->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_STA_RSP_STATE) || + (sta_ds->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_BSS_RSP_STATE) || + sta_ds->sta_deletion_in_progress) { + /** + * Already in the process of deleting context for the peer + * and received Deauthentication frame. Log and Ignore. + */ + pe_debug("Deletion is in progress (%d) for peer:"QDF_MAC_ADDR_FMT" in mlmState %d", + sta_ds->sta_deletion_in_progress, + QDF_MAC_ADDR_REF(addr), + sta_ds->mlmStaContext.mlmState); + return; + } + sta_ds->mlmStaContext.disassocReason = (tSirMacReasonCodes) rc; + sta_ds->mlmStaContext.cleanupTrigger = eLIM_PEER_ENTITY_DEAUTH; + sta_ds->sta_deletion_in_progress = true; + + /* / Issue Deauth Indication to SME. */ + qdf_mem_copy((uint8_t *) &mlmDeauthInd.peerMacAddr, + sta_ds->staAddr, sizeof(tSirMacAddr)); + mlmDeauthInd.reasonCode = + (uint8_t) sta_ds->mlmStaContext.disassocReason; + mlmDeauthInd.deauthTrigger = eLIM_PEER_ENTITY_DEAUTH; + + /* + * If we're in the middle of ReAssoc and received deauth from + * the ReAssoc AP, then notify SME by sending REASSOC_RSP with + * failure result code. SME will post the disconnect to the + * supplicant and the latter would start a fresh assoc. + */ + if (lim_is_reassoc_in_progress(mac_ctx, pe_session)) { + /** + * AP may have 'aged-out' our Pre-auth + * context. Delete local pre-auth context + * if any and issue REASSOC_CNF to SME. + */ + if (lim_search_pre_auth_list(mac_ctx, addr)) + lim_delete_pre_auth_node(mac_ctx, addr); + + if (pe_session->limAssocResponseData) { + qdf_mem_free(pe_session->limAssocResponseData); + pe_session->limAssocResponseData = NULL; + } + + pe_debug("Rcv Deauth from ReAssoc AP Issue REASSOC_CNF"); + /* + * TODO: Instead of overloading eSIR_SME_FT_REASSOC_TIMEOUT_FAILURE + * it would have been good to define/use a different failure type. + * Using eSIR_SME_FT_REASSOC_FAILURE does not seem to clean-up + * properly and we end up seeing "transmit queue timeout". + */ + lim_post_reassoc_failure(mac_ctx, + eSIR_SME_FT_REASSOC_TIMEOUT_FAILURE, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + pe_session); + return; + } + /* reset the deauthMsgCnt here since we are able to Process + * the deauth frame and sending up the indication as well */ + if (mac_ctx->lim.deauthMsgCnt != 0) { + mac_ctx->lim.deauthMsgCnt = 0; + } + if (LIM_IS_STA_ROLE(pe_session)) + wma_tx_abort(pe_session->smeSessionId); + + lim_update_lost_link_info(mac_ctx, pe_session, frame_rssi); + + /* / Deauthentication from peer MAC entity */ + if (LIM_IS_STA_ROLE(pe_session)) + lim_post_sme_message(mac_ctx, LIM_MLM_DEAUTH_IND, + (uint32_t *) &mlmDeauthInd); + + /* send eWNI_SME_DEAUTH_IND to SME */ + lim_send_sme_deauth_ind(mac_ctx, sta_ds, pe_session); + return; + +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_disassoc_frame.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_disassoc_frame.c new file mode 100644 index 0000000000000000000000000000000000000000..c7c7e3d213668cce56f7bfb04989fcf34aa34fcc --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_disassoc_frame.c @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_process_disassoc_frame.cc contains the code + * for processing Disassocation Frame. + * Author: Chandra Modumudi + * Date: 03/24/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#include "cds_api.h" +#include "wni_api.h" +#include "sir_api.h" +#include "ani_global.h" +#include "wni_cfg.h" + +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_send_messages.h" +#include "sch_api.h" +#include "wlan_blm_api.h" + +/** + * lim_process_disassoc_frame + * + ***FUNCTION: + * This function is called by limProcessMessageQueue() upon + * Disassociation frame reception. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * DPH drops packets for STA with 'valid' bit in sta set to '0'. + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param *pRxPacketInfo - A pointer to Rx packet info structure + * @return None + */ +void +lim_process_disassoc_frame(struct mac_context *mac, uint8_t *pRxPacketInfo, + struct pe_session *pe_session) +{ + uint8_t *pBody; + uint16_t aid, reasonCode; + tpSirMacMgmtHdr pHdr; + tpDphHashNode sta; + uint32_t frame_len; + int32_t frame_rssi; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + frame_len = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + + frame_rssi = (int32_t)WMA_GET_RX_RSSI_NORMALIZED(pRxPacketInfo); + + if (IEEE80211_IS_MULTICAST(pHdr->sa)) { + /* Received Disassoc frame from a BC/MC address */ + /* Log error and ignore it */ + pe_err("received Disassoc frame from a BC/MC address"); + return; + } + + if (IEEE80211_IS_MULTICAST(pHdr->da) && + !QDF_IS_ADDR_BROADCAST(pHdr->da)) { + /* Received Disassoc frame for a MC address */ + /* Log error and ignore it */ + pe_err("received Disassoc frame for a MC address"); + return; + } + if (!lim_validate_received_frame_a1_addr(mac, + pHdr->da, pe_session)) { + pe_err("rx frame doesn't have valid a1 address, drop it"); + return; + } + + if (LIM_IS_STA_ROLE(pe_session) && + ((eLIM_SME_WT_DISASSOC_STATE == pe_session->limSmeState) || + (eLIM_SME_WT_DEAUTH_STATE == pe_session->limSmeState))) { + if (!(mac->lim.disassocMsgCnt & 0xF)) { + pe_debug("received Disassoc frame in %s" + "already processing previously received Disassoc frame, dropping this %d", + lim_sme_state_str(pe_session->limSmeState), + ++mac->lim.disassocMsgCnt); + } else { + mac->lim.disassocMsgCnt++; + } + return; + } +#ifdef WLAN_FEATURE_11W + /* PMF: If this session is a PMF session, then ensure that this frame was protected */ + if (pe_session->limRmfEnabled + && (WMA_GET_RX_DPU_FEEDBACK(pRxPacketInfo) & + DPU_FEEDBACK_UNPROTECTED_ERROR)) { + pe_err("received an unprotected disassoc from AP"); + /* + * When 11w offload is enabled then + * firmware should not fwd this frame + */ + if (LIM_IS_STA_ROLE(pe_session) && mac->pmf_offload) { + pe_err("11w offload is enable,unprotected disassoc is not expected"); + return; + } + + /* If the frame received is unprotected, forward it to the supplicant to initiate */ + /* an SA query */ + /* send the unprotected frame indication to SME */ + lim_send_sme_unprotected_mgmt_frame_ind(mac, pHdr->fc.subType, + (uint8_t *) pHdr, + (frame_len + + sizeof(tSirMacMgmtHdr)), + pe_session->smeSessionId, + pe_session); + return; + } +#endif + + if (frame_len < 2) { + pe_err("frame len less than 2"); + return; + } + + /* Get reasonCode from Disassociation frame body */ + reasonCode = sir_read_u16(pBody); + + pe_nofl_rl_info("Disassoc RX: vdev %d from " QDF_MAC_ADDR_FMT " for " QDF_MAC_ADDR_FMT " RSSI = %d reason %d mlm state = %d, sme state = %d systemrole = %d ", + pe_session->vdev_id, QDF_MAC_ADDR_REF(pHdr->sa), + QDF_MAC_ADDR_REF(pHdr->da), frame_rssi, + reasonCode, pe_session->limMlmState, + pe_session->limSmeState, + GET_LIM_SYSTEM_ROLE(pe_session)); + lim_diag_event_report(mac, WLAN_PE_DIAG_DISASSOC_FRAME_EVENT, + pe_session, 0, reasonCode); + + /** + * Extract 'associated' context for STA, if any. + * This is maintained by DPH and created by LIM. + */ + sta = + dph_lookup_hash_entry(mac, pHdr->sa, &aid, + &pe_session->dph.dphHashTable); + + if (!sta) { + /** + * Disassociating STA is not associated. + * Log error. + */ + pe_err("received Disassoc frame from STA that does not have context" + "reasonCode=%d, addr " QDF_MAC_ADDR_FMT, + reasonCode, QDF_MAC_ADDR_REF(pHdr->sa)); + return; + } + + if (lim_check_disassoc_deauth_ack_pending(mac, (uint8_t *) pHdr->sa)) { + pe_err("Ignore the DisAssoc received, while waiting for ack of disassoc/deauth"); + lim_clean_up_disassoc_deauth_req(mac, (uint8_t *) pHdr->sa, 1); + return; + } + + if (mac->lim.disassocMsgCnt != 0) { + mac->lim.disassocMsgCnt = 0; + } + + /** If we are in the Wait for ReAssoc Rsp state */ + if (lim_is_reassoc_in_progress(mac, pe_session)) { + /* + * For LFR3, the roaming bssid is not known during ROAM_START, + * so check if the disassoc is received from current AP when + * roaming is being done in the firmware + */ + if (pe_session->fw_roaming_started && + IS_CURRENT_BSSID(mac, pHdr->sa, pe_session)) { + pe_debug("Dropping disassoc frame from connected AP"); + pe_session->recvd_disassoc_while_roaming = true; + pe_session->deauth_disassoc_rc = reasonCode; + return; + } + /** If we had received the DisAssoc from, + * a. the Current AP during ReAssociate to different AP in same ESS + * b. Unknown AP + * drop/ignore the DisAssoc received + */ + if (!IS_REASSOC_BSSID(mac, pHdr->sa, pe_session)) { + pe_err("Ignore DisAssoc while Processing ReAssoc"); + return; + } + /** If the Disassoc is received from the new AP to which we tried to ReAssociate + * Drop ReAssoc and Restore the Previous context( current connected AP). + */ + if (!IS_CURRENT_BSSID(mac, pHdr->sa, pe_session)) { + pe_debug("received Disassoc from the New AP to which ReAssoc is sent"); + lim_restore_pre_reassoc_state(mac, + eSIR_SME_REASSOC_REFUSED, + reasonCode, + pe_session); + return; + } + } + + if (LIM_IS_AP_ROLE(pe_session)) { + switch (reasonCode) { + case eSIR_MAC_UNSPEC_FAILURE_REASON: + case eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON: + case eSIR_MAC_DISASSOC_LEAVING_BSS_REASON: + case eSIR_MAC_MIC_FAILURE_REASON: + case eSIR_MAC_4WAY_HANDSHAKE_TIMEOUT_REASON: + case eSIR_MAC_GR_KEY_UPDATE_TIMEOUT_REASON: + case eSIR_MAC_RSN_IE_MISMATCH_REASON: + case eSIR_MAC_1X_AUTH_FAILURE_REASON: + /* Valid reasonCode in received Disassociation frame */ + break; + + default: + /* Invalid reasonCode in received Disassociation frame */ + pe_warn("received Disassoc frame with invalid reasonCode: %d from " QDF_MAC_ADDR_FMT, + reasonCode, QDF_MAC_ADDR_REF(pHdr->sa)); + break; + } + } else if (LIM_IS_STA_ROLE(pe_session) && + ((pe_session->limSmeState != eLIM_SME_WT_JOIN_STATE) && + (pe_session->limSmeState != eLIM_SME_WT_AUTH_STATE) && + (pe_session->limSmeState != eLIM_SME_WT_ASSOC_STATE) && + (pe_session->limSmeState != eLIM_SME_WT_REASSOC_STATE))) { + switch (reasonCode) { + case eSIR_MAC_DEAUTH_LEAVING_BSS_REASON: + case eSIR_MAC_DISASSOC_LEAVING_BSS_REASON: + case eSIR_MAC_POOR_RSSI_CONDITIONS: + /* Valid reasonCode in received Disassociation frame */ + /* as long as we're not about to channel switch */ + if (pe_session->gLimChannelSwitch.state != + eLIM_CHANNEL_SWITCH_IDLE) { + pe_err("Ignoring disassoc frame due to upcoming channel switch, from "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pHdr->sa)); + return; + } + break; + + default: + break; + } + } else { + /* Received Disassociation frame in either IBSS */ + /* or un-known role. Log and ignore it */ + pe_err("received Disassoc frame with invalid reasonCode: %d in role:" + "%d in sme state: %d from " QDF_MAC_ADDR_FMT, reasonCode, + GET_LIM_SYSTEM_ROLE(pe_session), pe_session->limSmeState, + QDF_MAC_ADDR_REF(pHdr->sa)); + + return; + } + + if ((sta->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_STA_RSP_STATE) || + (sta->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_BSS_RSP_STATE) || + sta->sta_deletion_in_progress) { + /** + * Already in the process of deleting context for the peer + * and received Disassociation frame. Log and Ignore. + */ + pe_debug("Deletion is in progress (%d) for peer:"QDF_MAC_ADDR_FMT" in mlmState %d", + sta->sta_deletion_in_progress, + QDF_MAC_ADDR_REF(pHdr->sa), + sta->mlmStaContext.mlmState); + return; + } + sta->sta_deletion_in_progress = true; + lim_disassoc_tdls_peers(mac, pe_session, pHdr->sa); + if (sta->mlmStaContext.mlmState != eLIM_MLM_LINK_ESTABLISHED_STATE) { + /** + * Requesting STA is in some 'transient' state? + * Log error. + */ + if (sta->mlmStaContext.mlmState == + eLIM_MLM_WT_ASSOC_CNF_STATE) + sta->mlmStaContext.updateContext = 1; + + pe_err("received Disassoc frame from peer that is in state: %X, addr "QDF_MAC_ADDR_FMT, + sta->mlmStaContext.mlmState, + QDF_MAC_ADDR_REF(pHdr->sa)); + + } /* if (sta->mlmStaContext.mlmState != eLIM_MLM_LINK_ESTABLISHED_STATE) */ + + if (reasonCode == eSIR_MAC_POOR_RSSI_CONDITIONS) { + struct sir_rssi_disallow_lst ap_info = {{0}}; + + ap_info.retry_delay = 0; + ap_info.expected_rssi = frame_rssi + + wlan_blm_get_rssi_blacklist_threshold(mac->pdev); + qdf_mem_copy(ap_info.bssid.bytes, pHdr->sa, QDF_MAC_ADDR_SIZE); + ap_info.reject_reason = REASON_ASSOC_REJECT_POOR_RSSI; + ap_info.source = ADDED_BY_DRIVER; + ap_info.original_timeout = ap_info.retry_delay; + ap_info.received_time = qdf_mc_timer_get_system_time(); + lim_add_bssid_to_reject_list(mac->pdev, &ap_info); + } + lim_extract_ies_from_deauth_disassoc(pe_session, (uint8_t *)pHdr, + WMA_GET_RX_MPDU_LEN(pRxPacketInfo)); + lim_perform_disassoc(mac, frame_rssi, reasonCode, + pe_session, pHdr->sa); + + if (mac->mlme_cfg->gen.fatal_event_trigger && + (reasonCode != eSIR_MAC_UNSPEC_FAILURE_REASON && + reasonCode != eSIR_MAC_DEAUTH_LEAVING_BSS_REASON && + reasonCode != eSIR_MAC_DISASSOC_LEAVING_BSS_REASON)) { + cds_flush_logs(WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_REASON_DISCONNECT, + false, false); + } +} /*** end lim_process_disassoc_frame() ***/ + +#ifdef FEATURE_WLAN_TDLS +void lim_disassoc_tdls_peers(struct mac_context *mac_ctx, + struct pe_session *pe_session, tSirMacAddr addr) +{ + tpDphHashNode sta_ds; + uint16_t aid; + + sta_ds = dph_lookup_hash_entry(mac_ctx, addr, &aid, + &pe_session->dph.dphHashTable); + if (!sta_ds) { + pe_debug("Hash entry not found"); + return; + } + /** + * Delete all the TDLS peers only if Disassoc is received + * from the AP + */ + if ((LIM_IS_STA_ROLE(pe_session)) && + ((sta_ds->mlmStaContext.mlmState == + eLIM_MLM_LINK_ESTABLISHED_STATE) || + (sta_ds->mlmStaContext.mlmState == + eLIM_MLM_IDLE_STATE)) && + (IS_CURRENT_BSSID(mac_ctx, addr, pe_session))) + lim_delete_tdls_peers(mac_ctx, pe_session); +} +#endif + +void lim_perform_disassoc(struct mac_context *mac_ctx, int32_t frame_rssi, + uint16_t rc, struct pe_session *pe_session, tSirMacAddr addr) +{ + tLimMlmDisassocInd mlmDisassocInd; + uint16_t aid; + tpDphHashNode sta_ds; + + sta_ds = dph_lookup_hash_entry(mac_ctx, addr, &aid, + &pe_session->dph.dphHashTable); + if (!sta_ds) { + pe_debug("Hash entry not found"); + return; + } + sta_ds->mlmStaContext.cleanupTrigger = eLIM_PEER_ENTITY_DISASSOC; + sta_ds->mlmStaContext.disassocReason = (tSirMacReasonCodes) rc; + + /* Issue Disassoc Indication to SME. */ + qdf_mem_copy((uint8_t *) &mlmDisassocInd.peerMacAddr, + (uint8_t *) sta_ds->staAddr, sizeof(tSirMacAddr)); + mlmDisassocInd.reasonCode = + (uint8_t) sta_ds->mlmStaContext.disassocReason; + mlmDisassocInd.disassocTrigger = eLIM_PEER_ENTITY_DISASSOC; + + /* Update PE session Id */ + mlmDisassocInd.sessionId = pe_session->peSessionId; + + if (lim_is_reassoc_in_progress(mac_ctx, pe_session)) { + + /* If we're in the middle of ReAssoc and received disassoc from + * the ReAssoc AP, then notify SME by sending REASSOC_RSP with + * failure result code. By design, SME will then issue "Disassoc" + * and cleanup will happen at that time. + */ + pe_debug("received Disassoc from AP while waiting for Reassoc Rsp"); + + if (pe_session->limAssocResponseData) { + qdf_mem_free(pe_session->limAssocResponseData); + pe_session->limAssocResponseData = NULL; + } + + lim_restore_pre_reassoc_state(mac_ctx, eSIR_SME_REASSOC_REFUSED, + rc, pe_session); + return; + } + + lim_update_lost_link_info(mac_ctx, pe_session, frame_rssi); + lim_post_sme_message(mac_ctx, LIM_MLM_DISASSOC_IND, + (uint32_t *) &mlmDisassocInd); + + /* send eWNI_SME_DISASSOC_IND to SME */ + lim_send_sme_disassoc_ind(mac_ctx, sta_ds, pe_session); + + return; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_fils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_fils.c new file mode 100644 index 0000000000000000000000000000000000000000..c69ef138e4362b961565d1dea68c254e7cd5b031 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_fils.c @@ -0,0 +1,2333 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lim_process_fils.h" +#include +#include +#include +#include +#include +#include +#include +#include "qdf_util.h" + +#ifdef WLAN_FEATURE_FILS_SK + +#ifdef WLAN_FILS_DEBUG +/** + * lim_fils_data_dump()- dump fils data + * @type: Data name + * @data: pointer to data buffer + * @len: data len + * + * Return: None + */ +static void lim_fils_data_dump(char *type, uint8_t *data, uint32_t len) +{ + + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + ("%s : length %d"), type, len); + qdf_trace_hex_dump(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, data, len); +} +#else +static void lim_fils_data_dump(char *type, uint8_t *data, uint32_t len) +{ } +#endif + +/** + * lim_get_crypto_digest_len()- Returns hash length based on crypto type + * @type: Crypto type + * + * Return: hash length + */ +static int lim_get_crypto_digest_len(uint8_t *type) +{ + if (!strcmp(type, HMAC_SHA386_CRYPTO_TYPE)) + return SHA384_DIGEST_SIZE; + else if (!strcmp(type, HMAC_SHA256_CRYPTO_TYPE)) + return SHA256_DIGEST_SIZE; + + return 0; +} + +/** + * lim_get_auth_tag_len()- This API returns auth tag len based on crypto suit + * used to encrypt erp packet. + * @crypto_suite: Crtpto suit + * + * Return: tag length + */ +static uint8_t lim_get_auth_tag_len(enum fils_erp_cryptosuite crypto_suite) +{ + switch (crypto_suite) { + case HMAC_SHA256_64: + return -EINVAL; + case HMAC_SHA256_128: + return FILS_SHA256_128_AUTH_TAG; + case HMAC_SHA256_256: + return FILS_SHA256_256_AUTH_TAG; + default: + return -EINVAL; + } +} + + +/** + * lim_get_crypto_type()- This API returns crypto type based on akm suite used + * @akm: akm used for authentication + * + * Return: Crypto type + */ +static uint8_t *lim_get_hash_crypto_type(uint8_t akm) +{ + switch (akm) { + case eCSR_AUTH_TYPE_FILS_SHA384: + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return SHA386_CRYPTO_TYPE; + case eCSR_AUTH_TYPE_FILS_SHA256: + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + default: + return SHA256_CRYPTO_TYPE; + } +} + +/** + * lim_get_hmac_crypto_type()- This API returns crypto type based on akm suite + * used. + * @akm: akm used for authentication + * + * This API is used to get the crypto type when HMAC-hash() needs to + * be generated. + * Eg: PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ]) + * Here HMAC-Hash will be either hmac(sha256) or hmac(sha384) + * + * Return: Crypto type + */ +static uint8_t *lim_get_hmac_crypto_type(uint8_t akm) +{ + switch (akm) { + case eCSR_AUTH_TYPE_FILS_SHA384: + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return HMAC_SHA386_CRYPTO_TYPE; + case eCSR_AUTH_TYPE_FILS_SHA256: + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + default: + return HMAC_SHA256_CRYPTO_TYPE; + } +} + +/** + * lim_get_Q_length()- This API returns pmk length based on akm used + * @akm: akm used for authentication + * + * [IEEE 802.11ai - 12.7.1.7.3 PMK-R0] + * PMK-R0 = L(R0-Key-Data, 0, Q) + * where Q is 32 if AKM negotiated is 00-0F-AC:16 + * Q is 48 if AKM negotiated is 00-0F-AC:17 + * + * PMK-R0 Derivation is for FT protocol akm only. + * So Value of Q is not applicable for non-FT akm. + * + * Return: Q length + */ +static uint8_t lim_get_Q_length(int akm_type) +{ + switch (akm_type) { + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + return FILS_SHA256_Q_LEN; + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return FILS_SHA384_Q_LEN; + default: + return 0; + } +} + +/** + * lim_get_pmk_length()- This API returns pmk length based on akm used + * @akm: akm used for authentication + * + * Return: PMK length + */ +static uint8_t lim_get_pmk_length(int akm_type) +{ + switch (akm_type) { + case eCSR_AUTH_TYPE_FILS_SHA256: + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + return FILS_SHA256_PMK_LEN; + case eCSR_AUTH_TYPE_FILS_SHA384: + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return FILS_SHA384_PMK_LEN; + default: + return FILS_SHA256_PMK_LEN; + } +} + +/** + * lim_get_fils_ft_length()- This API returns fils_ft length based on akm used + * @akm: akm used for authentication + * + * FILS_FT is the xx key used in derivation of the PMKR0. + * + * Return: PMK length + */ +static uint8_t lim_get_fils_ft_length(int akm_type) +{ + switch (akm_type) { + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + return FILS_FT_SHA256_LEN; + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return FILS_FT_SHA384_LEN; + default: + return 0; + } +} + +/** + * lim_get_kek_len()- This API returns kek length based on akm used + * @akm: akm used for authentication + * + * Return: KEK length + */ +static uint8_t lim_get_kek_len(uint8_t akm) +{ + switch (akm) { + case eCSR_AUTH_TYPE_FILS_SHA384: + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return FILS_SHA384_KEK_LEN; + case eCSR_AUTH_TYPE_FILS_SHA256: + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + return FILS_SHA256_KEK_LEN; + default: + return FILS_SHA256_KEK_LEN; + } +} + +/** + * lim_get_tk_len()- This API returns tk length based on cypher used + * @akm: cypher used + * + * Return: TK length + */ +static uint8_t lim_get_tk_len(int cypher_suite) +{ + switch (cypher_suite) { + case eSIR_ED_TKIP: + return TK_LEN_TKIP; + case eSIR_ED_CCMP: + return TK_LEN_CCMP; + case eSIR_ED_AES_128_CMAC: + return TK_LEN_AES_128_CMAC; + default: + return 0; + } +} + +/** + * lim_get_ick_len()- This API returns ick length based on akm used + * @akm: akm used for authentication + * + * Return: ICK length + */ +static int lim_get_ick_len(uint8_t akm) +{ + switch (akm) { + case eCSR_AUTH_TYPE_FILS_SHA384: + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return FILS_SHA384_ICK_LEN; + case eCSR_AUTH_TYPE_FILS_SHA256: + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + default: + return FILS_SHA256_ICK_LEN; + } +} + +/** + * lim_get_key_from_prf()- This API returns key data using PRF-X as defined in + * 11.6.1.7.2 ieee-80211-2012. + * @type: crypto type needs to be used + * @secret: key which needs to be used in crypto + * @secret_len: key_len of secret + * @label: PRF label + * @optional_data: Data used for hash + * @optional_data_len: data length + * @key: key data output + * @keylen: key data length + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_get_key_from_prf(uint8_t *type, uint8_t *secret, + uint32_t secret_len, uint8_t *label, uint8_t *optional_data, + uint32_t optional_data_len, uint8_t *key, uint32_t keylen) +{ + uint8_t count[2]; + uint8_t *addr[4]; + uint32_t len[4]; + uint16_t key_bit_length = keylen * 8; + uint8_t key_length[2]; + uint32_t i = 0, remain_len; + uint16_t interation; + uint8_t crypto_digest_len = lim_get_crypto_digest_len(type); + uint8_t tmp_hash[SHA384_DIGEST_SIZE] = {0}; + + if (!crypto_digest_len) { + pe_err("Incorrect crypto length"); + return QDF_STATUS_E_FAILURE; + } + + addr[0] = count; + len[0] = sizeof(count); + + addr[1] = label; + len[1] = strlen(label); + + addr[2] = optional_data; + len[2] = optional_data_len; + + qdf_mem_copy(key_length, &key_bit_length, sizeof(key_bit_length)); + addr[3] = key_length; + len[3] = sizeof(key_length); + + for (interation = 1; i < keylen; interation++) { + remain_len = keylen - i; + qdf_mem_copy(count, &interation, sizeof(interation)); + + if (remain_len >= crypto_digest_len) + remain_len = crypto_digest_len; + + if (qdf_get_hmac_hash(type, secret, secret_len, 4, + addr, len, tmp_hash) < 0) { + pe_err("qdf_get_hmac_hash failed"); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(&key[i], tmp_hash, remain_len); + i += crypto_digest_len; + } + return QDF_STATUS_SUCCESS; +} + +/** + * lim_default_hmac_sha256_kdf()- This API calculates key data using default kdf + * defined in RFC4306. + * @secret: key which needs to be used in crypto + * @secret_len: key_len of secret + * @label: PRF label + * @optional_data: Data used for hash + * @optional_data_len: data length + * @key: key data output + * @keylen: key data length + * + * This API creates default KDF as defined in RFC4306 + * PRF+ (K,S) = T1 | T2 | T3 | T4 | ... + * T1 = PRF (K, S | 0x01) + * T2 = PRF (K, T1 | S | 0x02) + * T3 = PRF (K, T2 | S | 0x03) + * T4 = PRF (K, T3 | S | 0x04) + * + * for every iteration its creates 32 bit of hash + * + * Return: QDF_STATUS + */ +static QDF_STATUS +lim_default_hmac_sha256_kdf(uint8_t *secret, uint32_t secret_len, + uint8_t *label, uint8_t *optional_data, + uint32_t optional_data_len, uint8_t *key, uint32_t keylen) +{ + uint8_t tmp_hash[SHA256_DIGEST_SIZE] = {0}; + uint8_t count = 1; + uint8_t *addr[4]; + uint32_t len[4]; + uint32_t current_position = 0, remaining_data = SHA256_DIGEST_SIZE; + + addr[0] = tmp_hash; + len[0] = SHA256_DIGEST_SIZE; + addr[1] = label; + len[1] = strlen(label) + 1; + addr[2] = optional_data; + len[2] = optional_data_len; + addr[3] = &count; + len[3] = 1; + + if (keylen == 0 || + (keylen > (MAX_PRF_INTERATIONS_COUNT * SHA256_DIGEST_SIZE))) { + pe_err("invalid key length %d", keylen); + return QDF_STATUS_E_FAILURE; + } + + /* Create T1 */ + if (qdf_get_hmac_hash(HMAC_SHA256_CRYPTO_TYPE, secret, secret_len, 3, + &addr[1], &len[1], tmp_hash) < 0) { + pe_err("failed to get hmac hash"); + return QDF_STATUS_E_FAILURE; + } + + /* Update hash from tmp_hash */ + qdf_mem_copy(key + current_position, tmp_hash, remaining_data); + current_position += remaining_data; + + for (count = 2; current_position < keylen; count++) { + remaining_data = keylen - current_position; + if (remaining_data > SHA256_DIGEST_SIZE) + remaining_data = SHA256_DIGEST_SIZE; + + /* Create T-n */ + if (qdf_get_hmac_hash(HMAC_SHA256_CRYPTO_TYPE, secret, + secret_len, 4, addr, len, tmp_hash) < 0) { + pe_err("failed to get hmac hash"); + return QDF_STATUS_E_FAILURE; + } + /* Update hash from tmp_hash */ + qdf_mem_copy(key + current_position, tmp_hash, remaining_data); + current_position += remaining_data; + } + return QDF_STATUS_SUCCESS; +} + +/** + * lim_process_fils_eap_tlv()- This API process eap tlv available in auth resp + * and returns remaining length. + * @pe_session: PE session + * @wrapped_data: wrapped data + * @data_len: length of tlv + * + * Return: remaining length + */ +static uint32_t lim_process_fils_eap_tlv(struct pe_session *pe_session, + uint8_t *wrapped_data, uint32_t data_len) +{ + struct fils_eap_tlv *tlv; + struct fils_auth_rsp_info *auth_info; + uint8_t auth_tag_len; + + auth_info = &pe_session->fils_info->auth_info; + /* Minimum */ + auth_tag_len = lim_get_auth_tag_len(HMAC_SHA256_128); + + while (data_len > (auth_tag_len + 1)) { + tlv = (struct fils_eap_tlv *) wrapped_data; + + pe_debug("tlv type %x len %u total %u", + tlv->type, tlv->length, data_len); + + if (tlv->length > (data_len - 2)) { + pe_err("tlv len %d greater data_len %d", + tlv->length, data_len); + return 0; + } + + switch (tlv->type) { + case SIR_FILS_EAP_TLV_KEYNAME_NAI: + auth_info->keyname = qdf_mem_malloc(tlv->length); + if (!auth_info->keyname) + return 0; + + qdf_mem_copy(auth_info->keyname, + tlv->data, tlv->length); + auth_info->keylength = tlv->length; + data_len -= (tlv->length + 2); + wrapped_data += (tlv->length + 2); + break; + case SIR_FILS_EAP_TLV_R_RK_LIFETIME: + /* TODO check this */ + auth_info->r_rk_lifetime = lim_get_u32(tlv->data); + data_len -= (tlv->length + 2); + wrapped_data += (tlv->length + 2); + break; + case SIR_FILS_EAP_TLV_R_MSK_LIFETIME: + /* TODO check this */ + auth_info->r_msk_lifetime = lim_get_u32(tlv->data); + data_len -= (tlv->length + 2); + wrapped_data += (tlv->length + 2); + break; + case SIR_FILS_EAP_TLV_DOMAIN_NAME: + auth_info->domain_name = qdf_mem_malloc(tlv->length); + if (!auth_info->domain_name) + return 0; + + qdf_mem_copy(auth_info->domain_name, + tlv->data, tlv->length); + auth_info->domain_len = tlv->length; + data_len -= (tlv->length + 2); + wrapped_data += (tlv->length + 2); + break; + /* TODO process these now */ + case SIR_FILS_EAP_TLV_CRYPTO_LIST: + case SIR_FILS_EAP_TLV_AUTH_INDICATION: + data_len -= (tlv->length + 2); + wrapped_data += (tlv->length + 2); + break; + default: + pe_debug("Unknown type"); + return data_len; + } + } + return data_len; +} + +/** + * lim_generate_key_data()- This API generates key data using prf + * FILS-Key-Data = KDF-X(PMK, "FILS PTK Derivation", SPA||AA||SNonce||ANonce) + * @fils_info: fils session info + * @key_label: label used + * @data: data buffer + * @data_len: data buffer len + * @key_data: hash data needs to be generated + * @key_data_len: hash data len + * + * Return: QDF_STATUS + */ +static QDF_STATUS +lim_generate_key_data(struct pe_fils_session *fils_info, + uint8_t *key_label, uint8_t *data, uint32_t data_len, + uint8_t *key_data, uint32_t key_data_len) +{ + QDF_STATUS status; + + if (!fils_info) + return QDF_STATUS_E_FAILURE; + + status = lim_get_key_from_prf(lim_get_hmac_crypto_type(fils_info->akm), + fils_info->fils_pmk, + fils_info->fils_pmk_len, key_label, data, + data_len, key_data, key_data_len); + if (status != QDF_STATUS_SUCCESS) + pe_err("failed to generate keydata"); + + return status; +} + +/** + * lim_generate_ap_key_auth()- This API generates ap auth data which needs to be + * verified in assoc response. + * @pe_session: pe session pointer + * + * Return: None + */ +static void lim_generate_ap_key_auth(struct pe_session *pe_session) +{ + uint8_t *buf, *addr[1]; + uint32_t len; + struct pe_fils_session *fils_info = pe_session->fils_info; + uint8_t data[SIR_FILS_NONCE_LENGTH + SIR_FILS_NONCE_LENGTH + + QDF_MAC_ADDR_SIZE + QDF_MAC_ADDR_SIZE] = {0}; + + if (!fils_info) + return; + + len = SIR_FILS_NONCE_LENGTH + SIR_FILS_NONCE_LENGTH + + QDF_MAC_ADDR_SIZE + QDF_MAC_ADDR_SIZE; + addr[0] = data; + buf = data; + qdf_mem_copy(buf, fils_info->auth_info.fils_nonce, + SIR_FILS_NONCE_LENGTH); + buf += SIR_FILS_NONCE_LENGTH; + qdf_mem_copy(buf, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH); + buf += SIR_FILS_NONCE_LENGTH; + qdf_mem_copy(buf, pe_session->bssId, QDF_MAC_ADDR_SIZE); + buf += QDF_MAC_ADDR_SIZE; + qdf_mem_copy(buf, pe_session->self_mac_addr, QDF_MAC_ADDR_SIZE); + buf += QDF_MAC_ADDR_SIZE; + + if (qdf_get_hmac_hash(lim_get_hmac_crypto_type(fils_info->akm), + fils_info->ick, fils_info->ick_len, 1, &addr[0], + &len, fils_info->ap_key_auth_data) < 0) + pe_err("failed to generate PMK id"); + fils_info->ap_key_auth_len = lim_get_crypto_digest_len( + lim_get_hmac_crypto_type(fils_info->akm)); + lim_fils_data_dump("AP Key Auth", fils_info->ap_key_auth_data, + fils_info->ap_key_auth_len); +} + +/** + * lim_generate_key_auth()- This API generates sta auth data which needs to be + * send to AP in assoc request, AP will generate the same the verify it. + * @pe_session: pe session pointer + * + * Return: None + */ +static void lim_generate_key_auth(struct pe_session *pe_session) +{ + uint8_t *buf, *addr[1]; + uint32_t len; + struct pe_fils_session *fils_info = pe_session->fils_info; + uint8_t data[SIR_FILS_NONCE_LENGTH + SIR_FILS_NONCE_LENGTH + + QDF_MAC_ADDR_SIZE + QDF_MAC_ADDR_SIZE] = {0}; + + if (!fils_info) + return; + + len = SIR_FILS_NONCE_LENGTH + SIR_FILS_NONCE_LENGTH + + QDF_MAC_ADDR_SIZE + QDF_MAC_ADDR_SIZE; + + addr[0] = data; + buf = data; + qdf_mem_copy(buf, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH); + buf += SIR_FILS_NONCE_LENGTH; + qdf_mem_copy(buf, fils_info->auth_info.fils_nonce, + SIR_FILS_NONCE_LENGTH); + buf += SIR_FILS_NONCE_LENGTH; + qdf_mem_copy(buf, pe_session->self_mac_addr, QDF_MAC_ADDR_SIZE); + buf += QDF_MAC_ADDR_SIZE; + qdf_mem_copy(buf, pe_session->bssId, QDF_MAC_ADDR_SIZE); + buf += QDF_MAC_ADDR_SIZE; + + if (qdf_get_hmac_hash(lim_get_hmac_crypto_type(fils_info->akm), + fils_info->ick, fils_info->ick_len, 1, + &addr[0], &len, fils_info->key_auth) < 0) + pe_err("failed to generate key auth"); + fils_info->key_auth_len = lim_get_crypto_digest_len( + lim_get_hmac_crypto_type(fils_info->akm)); + lim_fils_data_dump("STA Key Auth", + fils_info->key_auth, fils_info->key_auth_len); +} + +/** + * lim_get_keys()- This API generates keys keydata which is generated after + * parsing of auth response. + * KCK = L(FILS-Key-Data, 0, KCK_bits) + * KEK = L(FILS-Key-Data, KCK_bits, KEK_bits) + * TK = L(FILS-Key-Data, KCK_bits + KEK_bits, TK_bits) + * FILS-FT = L(FILS-Key-Data, KCK_bits + KEK_bits + TK_bits, FILS-FT_bits) + * @pe_session: pe session pointer + * + * Return: None + */ +static void lim_get_keys(struct pe_session *pe_session) +{ + uint8_t key_label[] = PTK_KEY_LABEL; + uint8_t *data; + uint8_t data_len; + struct pe_fils_session *fils_info = pe_session->fils_info; + uint8_t key_data[FILS_MAX_KEY_DATA_LEN] = {0}; + uint8_t key_data_len; + uint8_t ick_len; + uint8_t kek_len; + uint8_t fils_ft_len = 0; + uint8_t tk_len = lim_get_tk_len(pe_session->encryptType); + uint8_t *buf; + + if (!fils_info) + return; + + ick_len = lim_get_ick_len(fils_info->akm); + kek_len = lim_get_kek_len(fils_info->akm); + + if (pe_session->is11Rconnection) + fils_ft_len = lim_get_fils_ft_length(fils_info->akm); + + /* + * [IEEE 802.11ai - 12.12.2.5.3] + * FILS-Key-Data = PRF-X(PMK, “FILS PTK Derivation”, SPA || AA || + * SNonce || ANonce) + * ICK = L(FILS-Key-Data, 0, ICK_bits) + * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits) + * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits) + * When doing FT initial mobility domain association using + * FILS authentication, + * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + + * TK_bits, FILS-FT_bits) + */ + key_data_len = ick_len + kek_len + tk_len + fils_ft_len; + + data_len = 2 * SIR_FILS_NONCE_LENGTH + 2 * QDF_MAC_ADDR_SIZE; + data = qdf_mem_malloc(data_len); + if (!data) + return; + + /* data is SPA || AA ||SNonce || ANonce */ + buf = data; + qdf_mem_copy(buf, pe_session->self_mac_addr, QDF_MAC_ADDR_SIZE); + buf += QDF_MAC_ADDR_SIZE; + + qdf_mem_copy(buf, pe_session->bssId, QDF_MAC_ADDR_SIZE); + buf += QDF_MAC_ADDR_SIZE; + + qdf_mem_copy(buf, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH); + buf += SIR_FILS_NONCE_LENGTH; + + qdf_mem_copy(buf, fils_info->auth_info.fils_nonce, + SIR_FILS_NONCE_LENGTH); + + /* Derive FILS-Key-Data */ + lim_generate_key_data(fils_info, key_label, data, data_len, + key_data, key_data_len); + buf = key_data; + + qdf_mem_copy(fils_info->ick, buf, ick_len); + fils_info->ick_len = ick_len; + buf += ick_len; + + qdf_mem_copy(fils_info->kek, buf, kek_len); + fils_info->kek_len = kek_len; + buf += kek_len; + + qdf_mem_copy(fils_info->tk, buf, tk_len); + fils_info->tk_len = tk_len; + buf += tk_len; + + /* + * Derive FILS-FT: + * FILS-FT = + * L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits, FILS-FT_bits) + */ + if (pe_session->is11Rconnection && fils_ft_len) { + qdf_mem_copy(fils_info->fils_ft, buf, fils_ft_len); + fils_info->fils_ft_len = fils_ft_len; + } + qdf_mem_zero(data, data_len); + qdf_mem_free(data); +} + +/** + * lim_generate_pmkid()- This API generates PMKID using hash of erp auth packet + * parsing of auth response. + * PMKID = Truncate-128(Hash(EAP-Initiate/Reauth)) + * @pe_session: pe session pointer + * + * Return: None + */ +static void lim_generate_pmkid(struct pe_session *pe_session) +{ + uint8_t hash[SHA384_DIGEST_SIZE]; + struct pe_fils_session *fils_info = pe_session->fils_info; + + if (!fils_info) + return; + + qdf_get_hash(lim_get_hash_crypto_type(fils_info->akm), 1, + &fils_info->fils_erp_reauth_pkt, + &fils_info->fils_erp_reauth_pkt_len, hash); + qdf_mem_copy(fils_info->fils_pmkid, hash, PMKID_LEN); + lim_fils_data_dump("PMKID", fils_info->fils_pmkid, PMKID_LEN); +} + +/** + * lim_generate_pmk()- This API generates PMK using hmac hash of rmsk data + * anonce, snonce will be used as key for this + * PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ]) + * @pe_session: pe session pointer + * + * Return: None + */ +static void lim_generate_pmk(struct pe_session *pe_session) +{ + uint8_t nounce[2 * SIR_FILS_NONCE_LENGTH] = {0}; + uint8_t nounce_len = 2 * SIR_FILS_NONCE_LENGTH; + uint8_t *addr[1]; + uint32_t len[1]; + struct pe_fils_session *fils_info = pe_session->fils_info; + + if (!fils_info) + return; + + /* Snounce */ + qdf_mem_copy(nounce, pe_session->fils_info->fils_nonce, + SIR_FILS_NONCE_LENGTH); + /* anounce */ + qdf_mem_copy(nounce + SIR_FILS_NONCE_LENGTH, + pe_session->fils_info->auth_info.fils_nonce, + SIR_FILS_NONCE_LENGTH); + fils_info->fils_pmk_len = lim_get_pmk_length(fils_info->akm); + + if (fils_info->fils_pmk) + qdf_mem_free(fils_info->fils_pmk); + + fils_info->fils_pmk = qdf_mem_malloc(fils_info->fils_pmk_len); + if (!fils_info->fils_pmk) + return; + + addr[0] = fils_info->fils_rmsk; + len[0] = fils_info->fils_rmsk_len; + lim_fils_data_dump("Nonce", nounce, nounce_len); + if (qdf_get_hmac_hash(lim_get_hmac_crypto_type(fils_info->akm), nounce, + nounce_len, 1, &addr[0], &len[0], + fils_info->fils_pmk) < 0) + pe_err("failed to generate PMK"); +} + +/** + * lim_generate_rmsk_data()- This API generates RMSK data using + * default kdf as defined in RFC4306. + * @pe_session: pe session pointer + * + * Return: None + */ +static void lim_generate_rmsk_data(struct pe_session *pe_session) +{ + uint8_t optional_data[4] = {0}; + uint8_t rmsk_label[] = RMSK_LABEL; + struct pe_fils_session *fils_info = pe_session->fils_info; + struct fils_auth_rsp_info *auth_info; + + if (!fils_info) + return; + + auth_info = &(pe_session->fils_info->auth_info); + fils_info->fils_rmsk_len = fils_info->fils_rrk_len; + fils_info->fils_rmsk = qdf_mem_malloc(fils_info->fils_rrk_len); + if (!fils_info->fils_rmsk) + return; + + /* + * Sequence number sent in EAP-INIT packet, + * it should be in network byte order + */ + lim_copy_u16_be(&optional_data[0], fils_info->sequence_number); + lim_copy_u16_be(&optional_data[2], fils_info->fils_rrk_len); + lim_default_hmac_sha256_kdf(fils_info->fils_rrk, + fils_info->fils_rrk_len, rmsk_label, + optional_data, sizeof(optional_data), + fils_info->fils_rmsk, fils_info->fils_rmsk_len); +} + +/** + * lim_process_auth_wrapped_data()- This API process wrapped data element + * of auth response. + * @pe_session: pe session pointer + * @wrapped_data: wrapped data pointer + * @data_len: wrapped data len + * + * Return: None + */ +static QDF_STATUS lim_process_auth_wrapped_data(struct pe_session *pe_session, + uint8_t *wrapped_data, uint32_t data_len) +{ + uint8_t code; + uint8_t identifier; + uint16_t length; + uint8_t type; + unsigned long flags; + struct pe_fils_session *fils_info; + uint8_t hash[32] = {0}, crypto; + uint32_t remaining_len = data_len, new_len; + uint8_t *input_data[1]; + uint32_t input_len[1]; + uint8_t auth_tag_len; + + fils_info = pe_session->fils_info; + input_data[0] = wrapped_data; + input_len[0] = data_len; + + if (!fils_info) + return QDF_STATUS_E_FAILURE; + + pe_debug("trying to process the wrappped data"); + + code = *wrapped_data; + wrapped_data++; + remaining_len--; + identifier = *wrapped_data; + wrapped_data++; + remaining_len--; + + length = lim_get_u16(wrapped_data); + wrapped_data += sizeof(uint16_t); + remaining_len -= sizeof(uint16_t); + + type = *wrapped_data; /* val should be 2 here */ + wrapped_data++; + remaining_len--; + + flags = *wrapped_data; + if (test_bit(7, &flags)) { + pe_err("R bit is set in flag, error"); + return QDF_STATUS_E_FAILURE; + } + wrapped_data++; + remaining_len--; + + fils_info->auth_info.sequence = lim_get_u16_be(wrapped_data); + wrapped_data += sizeof(uint16_t); + remaining_len -= sizeof(uint16_t); + /* Validate Auth sequence number */ + if (fils_info->auth_info.sequence < fils_info->sequence_number) { + pe_err("sequence EAP-finish:%d is less than EAP-init:%d", + fils_info->auth_info.sequence, + fils_info->sequence_number); + return QDF_STATUS_E_FAILURE; + } + + /* Parse attached TLVs */ + new_len = lim_process_fils_eap_tlv(pe_session, + wrapped_data, remaining_len); + + wrapped_data += remaining_len - new_len; + remaining_len = new_len; + /* Remove cryptosuite */ + crypto = *wrapped_data; + wrapped_data++; + remaining_len--; + + auth_tag_len = lim_get_auth_tag_len(crypto); + input_len[0] -= auth_tag_len; + /* if we have auth tag remaining */ + if (remaining_len == auth_tag_len) { + qdf_get_hmac_hash(HMAC_SHA256_CRYPTO_TYPE, + fils_info->fils_rik, + fils_info->fils_rik_len, + SINGLE_ELEMENT_HASH_CNT, + input_data, input_len, hash); + } else { + pe_err("invalid remaining len %d", + remaining_len); + } + if (qdf_mem_cmp(wrapped_data, hash, auth_tag_len)) { + pe_err("integratity check failed for auth, crypto %d", + crypto); + return QDF_STATUS_E_FAILURE; + } + + lim_generate_rmsk_data(pe_session); + lim_generate_pmk(pe_session); + lim_generate_pmkid(pe_session); + return QDF_STATUS_SUCCESS; +} + +/** + * lim_is_valid_fils_auth_frame()- This API check whether auth frame is a + * valid frame. + * @mac_ctx: mac context + * @pe_session: pe session pointer + * @rx_auth_frm_body: pointer to autherntication frame + * + * Return: true if frame is valid or fils is disable, false otherwise + */ +bool lim_is_valid_fils_auth_frame(struct mac_context *mac_ctx, + struct pe_session *pe_session, + tSirMacAuthFrameBody *rx_auth_frm_body) +{ + if (!pe_session->fils_info) + return true; + + if (pe_session->fils_info->is_fils_connection == false) + return true; + + if (qdf_mem_cmp(rx_auth_frm_body->session, + pe_session->fils_info->fils_session, + SIR_FILS_SESSION_LENGTH)) { + lim_fils_data_dump("Current FILS session", + pe_session->fils_info->fils_session, + SIR_FILS_SESSION_LENGTH); + lim_fils_data_dump("FILS Session in pkt", + rx_auth_frm_body->session, + SIR_FILS_SESSION_LENGTH); + return false; + } + qdf_mem_copy(pe_session->fils_info->auth_info.fils_nonce, + rx_auth_frm_body->nonce, SIR_FILS_NONCE_LENGTH); + pe_session->fils_info->auth_info.assoc_delay = + rx_auth_frm_body->assoc_delay_info; + return true; +} + +QDF_STATUS lim_create_fils_rik(uint8_t *rrk, uint8_t rrk_len, + uint8_t *rik, uint32_t *rik_len) +{ + uint8_t optional_data[SIR_FILS_OPTIONAL_DATA_LEN]; + uint8_t label[] = SIR_FILS_RIK_LABEL; + + if (!rrk || !rik) { + pe_err("FILS rrk/rik NULL"); + return QDF_STATUS_E_FAILURE; + } + + optional_data[0] = HMAC_SHA256_128; + /* basic validation */ + if (rrk_len <= 0) { + pe_err("invalid r_rk length %d", rrk_len); + return QDF_STATUS_E_FAILURE; + } + lim_copy_u16_be(&optional_data[1], rrk_len); + if (lim_default_hmac_sha256_kdf(rrk, rrk_len, label, + optional_data, sizeof(optional_data), + rik, rrk_len) + != QDF_STATUS_SUCCESS) { + pe_err("failed to create rik"); + return QDF_STATUS_E_FAILURE; + } + *rik_len = rrk_len; + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_create_fils_wrapper_data()- This API create warpped data which will be + * sent in auth request. + * @fils_info: fils session info + * + * Return: length of the created wrapped data + */ +static int lim_create_fils_wrapper_data(struct pe_fils_session *fils_info) +{ + uint8_t *buf; + uint8_t auth_tag[FILS_AUTH_TAG_MAX_LENGTH] = {0}; + uint32_t length = 0; + QDF_STATUS status; + int buf_len; + + if (!fils_info) + return 0; + + if (!fils_info->keyname_nai_length || !fils_info->fils_rrk_len) { + pe_debug("FILS_PMKSA: NO keyname nai/RRK configured. Use PMKSA caching"); + return 0; + } + + buf_len = + /* code + identifier */ + sizeof(uint8_t) * 2 + + /* length */ + sizeof(uint16_t) + + /* type + flags */ + sizeof(uint8_t) * 2 + + /* sequence */ + sizeof(uint16_t) + + /* tlv : type, length, data */ + sizeof(uint8_t) * 2 + fils_info->keyname_nai_length + + /* cryptosuite + auth_tag */ + sizeof(uint8_t) + lim_get_auth_tag_len(HMAC_SHA256_128); + + fils_info->fils_erp_reauth_pkt = qdf_mem_malloc(buf_len); + if (!fils_info->fils_erp_reauth_pkt) + return -EINVAL; + + buf = fils_info->fils_erp_reauth_pkt; + *buf = 5; + buf++; + /* Identifier */ + *buf = 0; + buf++; + /* Length */ + lim_copy_u16_be(buf, buf_len); + buf += sizeof(uint16_t); + /* type */ + *buf = SIR_FILS_EAP_INIT_PACKET_TYPE; + buf++; + + /** + * flag + * 0 1 2 <-- 5 --> + * ---------------- + * |R|B|L| Reserved| + * ----------------- + */ + *buf = 0x20; /* l=1, b=0, r=0 */ + buf++; + /* sequence */ + lim_copy_u16_be(buf, fils_info->sequence_number); + buf += sizeof(uint16_t); + + /* tlv */ + /* Type */ + *buf = SIR_FILS_EAP_TLV_KEYNAME_NAI; + buf++; + /* NAI Length */ + *buf = fils_info->keyname_nai_length; + buf++; + /* NAI Data */ + qdf_mem_copy(buf, fils_info->keyname_nai_data, + fils_info->keyname_nai_length); + buf += fils_info->keyname_nai_length; + + /* cryptosuite */ + *buf = HMAC_SHA256_128; + buf++; + + /* + * This should be moved to just after sending probe to save time + * lim_process_switch_channel_join_req ?? + */ + fils_info->fils_rik = qdf_mem_malloc(fils_info->fils_rrk_len); + if (!fils_info->fils_rik) { + qdf_mem_free(fils_info->fils_erp_reauth_pkt); + fils_info->fils_erp_reauth_pkt = NULL; + return -EINVAL; + } + status = lim_create_fils_rik(fils_info->fils_rrk, + fils_info->fils_rrk_len, + fils_info->fils_rik, + &fils_info->fils_rik_len); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("RIK create fails"); + qdf_mem_free(fils_info->fils_erp_reauth_pkt); + qdf_mem_free(fils_info->fils_rik); + fils_info->fils_erp_reauth_pkt = NULL; + fils_info->fils_rik = NULL; + return -EINVAL; + } + + fils_info->fils_erp_reauth_pkt_len = buf_len; + length = fils_info->fils_erp_reauth_pkt_len - + lim_get_auth_tag_len(HMAC_SHA256_128); + qdf_get_hmac_hash(HMAC_SHA256_CRYPTO_TYPE, + fils_info->fils_rik, fils_info->fils_rik_len, 1, + &fils_info->fils_erp_reauth_pkt, &length, auth_tag); + + lim_fils_data_dump("Auth tag", auth_tag, + lim_get_auth_tag_len(HMAC_SHA256_128)); + lim_fils_data_dump("EAP init pkt", fils_info->fils_erp_reauth_pkt, + fils_info->fils_erp_reauth_pkt_len); + + qdf_mem_copy(buf, auth_tag, lim_get_auth_tag_len(HMAC_SHA256_128)); + buf += lim_get_auth_tag_len(HMAC_SHA256_128); + + return buf_len; +} + +/** + * lim_add_fils_data_to_auth_frame()- This API add fils data to auth frame. + * Following will be added in this. + * RSNIE + * SNonce + * Session + * Wrapped data + * @session: PE session + * @body: pointer to auth frame where data needs to be added + * + * Return: None + */ +void lim_add_fils_data_to_auth_frame(struct pe_session *session, + uint8_t *body) +{ + struct pe_fils_session *fils_info; + + fils_info = session->fils_info; + + if (!fils_info) + return; + + /* RSN IE */ + qdf_mem_copy(body, fils_info->rsn_ie, fils_info->rsn_ie_len); + body += fils_info->rsn_ie_len; + lim_fils_data_dump("FILS RSN", fils_info->rsn_ie, + fils_info->rsn_ie_len); + + /* + * FT-FILS IEEE-802.11ai specification mandates + * MDIE to be sent in auth frame during initial + * mobility domain association + */ + if (session->lim_join_req->is11Rconnection) { + struct bss_description *bss_desc; + + bss_desc = &session->lim_join_req->bssDescription; + + if (bss_desc->mdiePresent) { + /* Populate MDIE received from AP */ + *body = WLAN_ELEMID_MOBILITY_DOMAIN; + body++; + *body = SIR_MDIE_SIZE; + body++; + qdf_mem_copy(body, &bss_desc->mdie[0], + SIR_MDIE_SIZE); + pe_debug("FILS: mdie = %02x %02x %02x", + bss_desc->mdie[0], bss_desc->mdie[1], + bss_desc->mdie[2]); + body += SIR_MDIE_SIZE; + } else { + pe_err("FT-FILS: MDIE not advertised by AP"); + } + } + + /* ***Nounce*** */ + /* Add element id */ + *body = SIR_MAX_ELEMENT_ID; + body++; + /* Add nounce length + 1 for ext element id */ + *body = SIR_FILS_NONCE_LENGTH + 1; + body++; + /* Add ext element */ + *body = SIR_FILS_NONCE_EXT_EID; + body++; + /* Add data */ + qdf_get_random_bytes(fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH); + qdf_mem_copy(body, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH); + body = body + SIR_FILS_NONCE_LENGTH; + /* Dump data */ + lim_fils_data_dump("fils anonce", fils_info->fils_nonce, + SIR_FILS_NONCE_LENGTH); + + /* *** Session *** */ + /* Add element id */ + *body = SIR_MAX_ELEMENT_ID; + body++; + /* Add nounce length + 1 for ext element id */ + *body = SIR_FILS_SESSION_LENGTH + 1; + body++; + /* Add ext element */ + *body = SIR_FILS_SESSION_EXT_EID; + body++; + /* Add data */ + qdf_get_random_bytes(fils_info->fils_session, SIR_FILS_SESSION_LENGTH); + qdf_mem_copy(body, fils_info->fils_session, SIR_FILS_SESSION_LENGTH); + body = body + SIR_FILS_SESSION_LENGTH; + /* dump data */ + lim_fils_data_dump("Fils Session", + fils_info->fils_session, SIR_FILS_SESSION_LENGTH); + + if (!fils_info->fils_erp_reauth_pkt || + !fils_info->fils_erp_reauth_pkt_len) { + pe_debug("FILS: No ERP data. Dont add auth wrapped data"); + return; + } + + /* ERP Packet */ + /* Add element id */ + *body = SIR_MAX_ELEMENT_ID; + body++; + /* Add packet length + 1 for ext element id */ + *body = fils_info->fils_erp_reauth_pkt_len + 1; + body++; + /* Add ext element */ + *body = SIR_FILS_WRAPPED_DATA_EXT_EID; + body++; + /* Copy data */ + qdf_mem_copy(body, fils_info->fils_erp_reauth_pkt, + fils_info->fils_erp_reauth_pkt_len); + lim_fils_data_dump("Fils ERP reauth Pkt", + fils_info->fils_erp_reauth_pkt, + fils_info->fils_erp_reauth_pkt_len); + body = body + fils_info->fils_erp_reauth_pkt_len; +} + +/** + * lim_generate_fils_pmkr0() - Derive PMKR0 and PMKR0-Name from FT-FILS + * key data + * @pe_session: pointer to pe_session + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_generate_fils_pmkr0(struct pe_session *pe_session) +{ + uint8_t *key_data, *data_buf; + uint8_t key_label[] = FT_PMK_R0_KEY_LABEL; + uint8_t key_data_len, ssid_length, r0kh_len, mdid_len; + uint8_t *r0_key_data; + uint8_t *hash; + uint8_t r0_key_data_len; + uint8_t pmkr0_len; + uint8_t *buf, *pmkr0_name_salt; + uint8_t *scatter_list[PMKR0_SCATTER_LIST_ELEM]; + uint32_t len[PMKR0_SCATTER_LIST_ELEM]; + uint16_t data_buf_len; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct pe_fils_session *fils_info = pe_session->fils_info; + struct bss_description *bss_desc = + &pe_session->lim_join_req->bssDescription; + + if (!fils_info) + return QDF_STATUS_E_FAILURE; + + ssid_length = pe_session->ssId.length; + r0kh_len = fils_info->ft_ie.r0kh_id_len; + mdid_len = (SIR_MDIE_SIZE - 1); + pmkr0_len = lim_get_Q_length(fils_info->akm); + r0_key_data_len = pmkr0_len + FILS_FT_PMK_R0_SALT_LEN; + + /* + * [IEEE 802.11ai 12.7.1.7.3] + * R0-Key-Data = KDF-Hash-Length(XXKey, "FT-R0", SSIDlength || SSID || + * MDID || R0KHlength || R0KH-ID || S0KH-ID) + * PMK-R0 = L(R0-Key-Data, 0, Q) + * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128) + * Length = Q + 128 + */ + key_data_len = (1 + ssid_length + mdid_len + 1 + r0kh_len + + QDF_MAC_ADDR_SIZE); + pe_debug("FT-FILS: ssid_length:%d MDID len:%d R0KH len:%d key_data len:%d", + ssid_length, mdid_len, r0kh_len, key_data_len); + + data_buf_len = (key_data_len + FILS_FT_MAX_R0_KEY_DATA_LEN + + SHA384_DIGEST_SIZE); + data_buf = qdf_mem_malloc(data_buf_len); + if (!data_buf) + return QDF_STATUS_E_NOMEM; + + key_data = &data_buf[0]; + r0_key_data = &data_buf[key_data_len]; + hash = &data_buf[key_data_len + FILS_FT_MAX_R0_KEY_DATA_LEN]; + + /* + * key_data is (SSIDlength || SSID || MDID || R0KHlength || R0KH-ID || + * S0KH-ID) + */ + buf = key_data; + + *key_data = pe_session->ssId.length; + key_data += 1; + + qdf_mem_copy(key_data, pe_session->ssId.ssId, ssid_length); + key_data += ssid_length; + + qdf_mem_copy(key_data, bss_desc->mdie, mdid_len); + key_data += mdid_len; + + *key_data = r0kh_len; + key_data += 1; + + qdf_mem_copy(key_data, fils_info->ft_ie.r0kh_id, r0kh_len); + key_data += r0kh_len; + + qdf_mem_copy(key_data, pe_session->self_mac_addr, QDF_MAC_ADDR_SIZE); + + pe_debug("FT-FILS: Derive R0-Key-Data"); + status = lim_get_key_from_prf(lim_get_hmac_crypto_type(fils_info->akm), + fils_info->fils_ft, + fils_info->fils_ft_len, key_label, + buf, key_data_len, r0_key_data, + r0_key_data_len); + if (QDF_IS_STATUS_ERROR(status)) + goto free_buf; + + /* PMK-R0 is the first Q bytes of R0-Key-Data */ + qdf_mem_copy(fils_info->pmkr0, r0_key_data, pmkr0_len); + fils_info->pmkr0_len = pmkr0_len; + + /* PMK-R0Name-Salt = L(R0-Key-Data, Q, 128) */ + pmkr0_name_salt = r0_key_data + pmkr0_len; + + /* + * [IEEE 802.11-2016 12.7.1.7.3] + * PMKR0Name = Truncate-128(Hash("FT-R0N" || PMK-R0Name-Salt) + * The Hash function requires the crypto type, number of scatterlist + * parameters, scatterlist, lengths of scatterlist arguments, pointer + * to output hash. + * + * The scatterlist has two arguments - label "FT-R0N" and + * PMK-R0Name-Salt + * + */ + scatter_list[SCTR_LST_ELEM0] = FT_PMK_R0_NAME_KEY_LABEL; + len[SCTR_LST_ELEM0] = SCTR_LST_R0_LABEL_LEN; + scatter_list[SCTR_LST_ELEM1] = pmkr0_name_salt; + len[SCTR_LST_ELEM1] = FILS_FT_PMK_R0_SALT_LEN; + + pe_debug("FT-FILS: Derive PMK-R0 Name"); + if (qdf_get_hash(lim_get_hash_crypto_type(fils_info->akm), + PMKR0_SCATTER_LIST_ELEM, scatter_list, len, + hash) < 0) { + pe_err("FT-FILS: PMK-R0Name derivation failed"); + status = QDF_STATUS_E_FAILURE; + goto free_buf; + } + qdf_mem_copy(fils_info->pmkr0_name, hash, FILS_PMK_NAME_LEN); + +free_buf: + qdf_mem_zero(data_buf, data_buf_len); + qdf_mem_free(data_buf); + + return status; +} + +/** + * lim_generate_fils_pmkr1_name() - Derive PMKR1 and PMKR1-Name from FT-FILS + * key data + * @pe_session: pointer to pe_session + * + * Return: None + */ +static QDF_STATUS lim_generate_fils_pmkr1_name(struct pe_session *pe_session) +{ + uint8_t *hash; + uint8_t *scatter_list[PMKR1_SCATTER_LIST_ELEM]; + uint32_t len[PMKR1_SCATTER_LIST_ELEM]; + uint8_t *buf; + uint8_t gp_mgmt_cipher_suite[4]; + struct pe_fils_session *fils_info = pe_session->fils_info; + + if (!fils_info) + return QDF_STATUS_E_FAILURE; + + hash = qdf_mem_malloc(SHA384_DIGEST_SIZE); + if (!hash) + return QDF_STATUS_E_NOMEM; + + /* + * [IEEE 802.11-2016 12.7.1.7.4] + * PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name || + * R1KH-ID || S1KH-ID)) + */ + scatter_list[SCTR_LST_ELEM0] = FT_PMK_R1_NAME_KEY_LABEL; + len[SCTR_LST_ELEM0] = SCTR_LST_R1_LABEL_LEN; + scatter_list[SCTR_LST_ELEM1] = fils_info->pmkr0_name; + len[SCTR_LST_ELEM1] = FILS_PMK_NAME_LEN; + scatter_list[SCTR_LST_ELEM2] = fils_info->ft_ie.r1kh_id; + len[SCTR_LST_ELEM2] = FT_R1KH_ID_LEN; + scatter_list[SCTR_LST_ELEM3] = pe_session->self_mac_addr; + len[SCTR_LST_ELEM3] = QDF_MAC_ADDR_SIZE; + + if (qdf_get_hash(lim_get_hash_crypto_type(fils_info->akm), + PMKR1_SCATTER_LIST_ELEM, scatter_list, len, + hash) < 0) { + qdf_mem_zero(hash, SHA384_DIGEST_SIZE); + qdf_mem_free(hash); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(fils_info->pmkr1_name, hash, FILS_PMK_NAME_LEN); + + if (fils_info->rsn_ie_len) { + if (fils_info->group_mgmt_cipher_present) { + /* + * If 802.11w is enabled, group management cipher + * suite is added at the end of RSN IE after + * PMKID. Since the driver has opaque RSN IE + * saved in fils_session, strip the last 4 bytes + * of the RSN IE to get group mgmt cipher suite. + * Then copy the PMKID followed by the grp mgmt cipher + * suite. + */ + buf = fils_info->rsn_ie + fils_info->rsn_ie_len - 4; + qdf_mem_copy(gp_mgmt_cipher_suite, buf, 4); + buf -= 2; + } else { + buf = fils_info->rsn_ie + fils_info->rsn_ie_len; + } + + /* + * Add PMKID count as 1. PMKID count field is 2 bytes long. + * Copy the PMKR1-Name in the PMKID list at the end of the + * RSN IE. + */ + *buf = 1; + buf += 2; + qdf_mem_copy(buf, fils_info->pmkr1_name, FILS_PMK_NAME_LEN); + if (fils_info->group_mgmt_cipher_present) { + fils_info->rsn_ie_len += FILS_PMK_NAME_LEN; + fils_info->rsn_ie[1] += (FILS_PMK_NAME_LEN); + } else { + fils_info->rsn_ie_len += (2 + FILS_PMK_NAME_LEN); + fils_info->rsn_ie[1] += (2 + FILS_PMK_NAME_LEN); + } + + if (fils_info->group_mgmt_cipher_present) { + buf += FILS_PMK_NAME_LEN; + qdf_mem_copy(buf, gp_mgmt_cipher_suite, 4); + } + } else { + pe_err("FT-FILS: RSN IE not present"); + qdf_mem_zero(hash, SHA384_DIGEST_SIZE); + qdf_mem_free(hash); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_zero(hash, SHA384_DIGEST_SIZE); + qdf_mem_free(hash); + return QDF_STATUS_SUCCESS; +} + +/** + * lim_process_fils_auth_frame2()- This API process fils data from auth response + * @mac_ctx: mac context + * @session: PE session + * @rx_auth_frm_body: pointer to auth frame + * + * Return: true if fils data needs to be processed else false + */ +bool lim_process_fils_auth_frame2(struct mac_context *mac_ctx, + struct pe_session *pe_session, + tSirMacAuthFrameBody *rx_auth_frm_body) +{ + int i; + uint32_t ret; + bool pmkid_found = false; + tDot11fIERSN dot11f_ie_rsn = {0}; + QDF_STATUS status; + + if (!pe_session->fils_info) + return false; + + if (rx_auth_frm_body->authAlgoNumber != SIR_FILS_SK_WITHOUT_PFS) + return false; + + ret = dot11f_unpack_ie_rsn(mac_ctx, &rx_auth_frm_body->rsn_ie.info[0], + rx_auth_frm_body->rsn_ie.length, + &dot11f_ie_rsn, 0); + if (!DOT11F_SUCCEEDED(ret)) { + pe_err("unpack failed, ret: %d", ret); + return false; + } + + /* + * copy FTIE to fils_info and send it over assoc response frame + * for FT-FILS connection + */ + if (pe_session->is11Rconnection && pe_session->fils_info) { + pe_session->fils_info->ft_ie = rx_auth_frm_body->ft_ie; + if (!pe_session->fils_info->ft_ie.present) { + pe_err("FT-FILS: NO FTIE in auth response"); + } + } + + for (i = 0; i < dot11f_ie_rsn.pmkid_count; i++) { + if (qdf_mem_cmp(dot11f_ie_rsn.pmkid[i], + pe_session->fils_info->fils_pmkid, + PMKID_LEN) == 0) { + pmkid_found = true; + pe_debug("pmkid match in rsn ie total_count %d", + dot11f_ie_rsn.pmkid_count); + break; + } + } + if (!pmkid_found) { + if (QDF_STATUS_SUCCESS != + lim_process_auth_wrapped_data(pe_session, + rx_auth_frm_body->wrapped_data, + rx_auth_frm_body->wrapped_data_len)) + return false; + } + lim_get_keys(pe_session); + if (pe_session->is11Rconnection) { + status = lim_generate_fils_pmkr0(pe_session); + if (QDF_IS_STATUS_ERROR(status)) + return false; + + status = lim_generate_fils_pmkr1_name(pe_session); + if (QDF_IS_STATUS_ERROR(status)) + return false; + } + lim_generate_key_auth(pe_session); + lim_generate_ap_key_auth(pe_session); + return true; +} + +void lim_update_fils_config(struct mac_context *mac_ctx, + struct pe_session *session, + struct join_req *sme_join_req) +{ + struct pe_fils_session *pe_fils_info; + struct cds_fils_connection_info *fils_config_info; + tDot11fIERSN dot11f_ie_rsn = {0}; + uint32_t ret; + + fils_config_info = &sme_join_req->fils_con_info; + pe_fils_info = session->fils_info; + + if (!pe_fils_info) + return; + + if (fils_config_info->is_fils_connection == false) + return; + + pe_fils_info->is_fils_connection = + fils_config_info->is_fils_connection; + pe_fils_info->keyname_nai_length = + fils_config_info->key_nai_length; + pe_fils_info->fils_rrk_len = + fils_config_info->r_rk_length; + pe_fils_info->akm = fils_config_info->akm_type; + pe_fils_info->auth = fils_config_info->auth_type; + pe_fils_info->sequence_number = fils_config_info->sequence_number; + if (fils_config_info->key_nai_length > FILS_MAX_KEYNAME_NAI_LENGTH) { + pe_err("Restricting the key_nai_length of %d to max %d", + fils_config_info->key_nai_length, + FILS_MAX_KEYNAME_NAI_LENGTH); + fils_config_info->key_nai_length = FILS_MAX_KEYNAME_NAI_LENGTH; + } + + if (fils_config_info->key_nai_length) { + pe_fils_info->keyname_nai_data = + qdf_mem_malloc(fils_config_info->key_nai_length); + if (!pe_fils_info->keyname_nai_data) + return; + + qdf_mem_copy(pe_fils_info->keyname_nai_data, + fils_config_info->keyname_nai, + fils_config_info->key_nai_length); + } + + if (fils_config_info->r_rk_length) { + pe_fils_info->fils_rrk = + qdf_mem_malloc(fils_config_info->r_rk_length); + if (!pe_fils_info->fils_rrk) { + qdf_mem_free(pe_fils_info->keyname_nai_data); + return; + } + + if (fils_config_info->r_rk_length <= FILS_MAX_RRK_LENGTH) + qdf_mem_copy(pe_fils_info->fils_rrk, + fils_config_info->r_rk, + fils_config_info->r_rk_length); + } + + qdf_mem_copy(pe_fils_info->fils_pmkid, fils_config_info->pmkid, + PMKID_LEN); + pe_fils_info->rsn_ie_len = sme_join_req->rsnIE.length; + qdf_mem_copy(pe_fils_info->rsn_ie, + sme_join_req->rsnIE.rsnIEdata, + sme_join_req->rsnIE.length); + /* + * When AP is MFP capable and STA is also MFP capable, + * the supplicant fills the RSN IE with PMKID count as 0 + * and PMKID as 0, then appends the group management cipher + * suite. This opaque RSN IE is copied into fils_info in pe + * session. For FT-FILS association, STA has to fill the + * PMKR0 derived after authentication response is received from + * the AP. So unpack the RSN IE to find if group management cipher + * suite is present and based on this RSN IE will be constructed in + * lim_generate_fils_pmkr1_name() for FT-FILS connection. + */ + ret = dot11f_unpack_ie_rsn(mac_ctx, pe_fils_info->rsn_ie + 2, + pe_fils_info->rsn_ie_len - 2, + &dot11f_ie_rsn, 0); + if (DOT11F_SUCCEEDED(ret)) + pe_fils_info->group_mgmt_cipher_present = + dot11f_ie_rsn.gp_mgmt_cipher_suite_present; + else + pe_err("FT-FILS: Invalid RSN IE"); + + pe_fils_info->fils_pmk_len = fils_config_info->pmk_len; + if (fils_config_info->pmk_len) { + pe_fils_info->fils_pmk = + qdf_mem_malloc(fils_config_info->pmk_len); + if (!pe_fils_info->fils_pmk) { + qdf_mem_free(pe_fils_info->keyname_nai_data); + qdf_mem_free(pe_fils_info->fils_rrk); + return; + } + qdf_mem_copy(pe_fils_info->fils_pmk, fils_config_info->pmk, + fils_config_info->pmk_len); + } + pe_debug("fils=%d nai-len=%d rrk_len=%d akm=%d auth=%d pmk_len=%d", + fils_config_info->is_fils_connection, + fils_config_info->key_nai_length, + fils_config_info->r_rk_length, + fils_config_info->akm_type, + fils_config_info->auth_type, + fils_config_info->pmk_len); +} + +#define EXTENDED_IE_HEADER_LEN 3 +/** + * lim_create_fils_auth_data()- This API creates the fils auth data + * which needs to be sent in auth req. + * @mac_ctx: mac context + * @auth_frame: pointer to auth frame + * @session: PE session + * + * Return: length of fils data + */ +QDF_STATUS lim_create_fils_auth_data(struct mac_context *mac_ctx, + tpSirMacAuthFrameBody auth_frame, + struct pe_session *session, + uint32_t *frame_len) +{ + uint16_t frm_len = 0; + int32_t wrapped_data_len; + + if (!session->fils_info) + return QDF_STATUS_SUCCESS; + + /* These memory may already been allocated if auth retry */ + if (session->fils_info->fils_rik) { + qdf_mem_free(session->fils_info->fils_rik); + session->fils_info->fils_rik = NULL; + } + + if (session->fils_info->fils_erp_reauth_pkt) { + qdf_mem_free(session->fils_info->fils_erp_reauth_pkt); + session->fils_info->fils_erp_reauth_pkt = NULL; + } + + if (auth_frame->authAlgoNumber == SIR_FILS_SK_WITHOUT_PFS) { + frm_len += session->fils_info->rsn_ie_len; + /* FILS nounce */ + frm_len += SIR_FILS_NONCE_LENGTH + EXTENDED_IE_HEADER_LEN; + /* FILS Session */ + frm_len += SIR_FILS_SESSION_LENGTH + EXTENDED_IE_HEADER_LEN; + /* Calculate data/length for FILS Wrapped Data */ + wrapped_data_len = + lim_create_fils_wrapper_data(session->fils_info); + if (wrapped_data_len < 0) { + pe_err("failed to allocate wrapped data"); + return QDF_STATUS_E_FAILURE; + } + + if (wrapped_data_len) + frm_len += wrapped_data_len + EXTENDED_IE_HEADER_LEN; + } + + *frame_len += frm_len; + + return QDF_STATUS_SUCCESS; +} + +void populate_fils_connect_params(struct mac_context *mac_ctx, + struct pe_session *session, + struct join_rsp *sme_join_rsp) +{ + struct fils_join_rsp_params *fils_join_rsp; + struct pe_fils_session *fils_info = session->fils_info; + + if (!lim_is_fils_connection(session)) + return; + + if (!fils_info->fils_pmk_len || + !fils_info->tk_len || !fils_info->gtk_len || + !fils_info->fils_pmk || !fils_info->kek_len) { + pe_err("Invalid FILS info pmk len %d kek len %d tk len %d gtk len %d", + fils_info->fils_pmk_len, + fils_info->kek_len, + fils_info->tk_len, + fils_info->gtk_len); + return; + } + + sme_join_rsp->fils_join_rsp = qdf_mem_malloc(sizeof(*fils_join_rsp)); + if (!sme_join_rsp->fils_join_rsp) { + pe_delete_fils_info(session); + return; + } + + fils_join_rsp = sme_join_rsp->fils_join_rsp; + fils_join_rsp->fils_pmk = qdf_mem_malloc(fils_info->fils_pmk_len); + if (!fils_join_rsp->fils_pmk) { + qdf_mem_free(fils_join_rsp); + pe_delete_fils_info(session); + return; + } + + fils_join_rsp->fils_pmk_len = fils_info->fils_pmk_len; + qdf_mem_copy(fils_join_rsp->fils_pmk, fils_info->fils_pmk, + fils_info->fils_pmk_len); + + qdf_mem_copy(fils_join_rsp->fils_pmkid, fils_info->fils_pmkid, + PMKID_LEN); + + fils_join_rsp->kek_len = fils_info->kek_len; + qdf_mem_copy(fils_join_rsp->kek, fils_info->kek, fils_info->kek_len); + + fils_join_rsp->tk_len = fils_info->tk_len; + qdf_mem_copy(fils_join_rsp->tk, fils_info->tk, fils_info->tk_len); + + fils_join_rsp->gtk_len = fils_info->gtk_len; + qdf_mem_copy(fils_join_rsp->gtk, fils_info->gtk, fils_info->gtk_len); + + cds_copy_hlp_info(&fils_info->dst_mac, &fils_info->src_mac, + fils_info->hlp_data_len, fils_info->hlp_data, + &fils_join_rsp->dst_mac, &fils_join_rsp->src_mac, + &fils_join_rsp->hlp_data_len, + fils_join_rsp->hlp_data); + + pe_debug("FILS connect params copied lim"); +} + +/** + * lim_parse_kde_elements() - Parse Key Delivery Elements + * @mac_ctx: mac context + * @fils_info: FILS info + * @kde_list: KDE list buffer + * @kde_list_len: Length of @kde_list + * + * This API is used to parse the Key Delivery Elements from buffer + * and populate them in PE FILS session struct i.e @fils_info + * + * Key Delivery Element[KDE] format + * +----------+--------+-----------+------------+----------+ + * | ID(0xDD) | length | KDE OUI | data type | IE data | + * |----------|--------|-----------|------------|----------| + * | 1 byte | 1 byte | 3 bytes | 1 byte | variable | + * +----------+--------+-----------+------------+----------+ + * + * there can be multiple KDE present inside KDE list. + * the IE data could be GTK, IGTK etc based on the data type + * + * Return: QDF_STATUS_SUCCESS if we parse GTK successfully, + * QDF_STATUS_E_FAILURE otherwise + */ +static QDF_STATUS lim_parse_kde_elements(struct mac_context *mac_ctx, + struct pe_fils_session *fils_info, + uint8_t *kde_list, + uint8_t kde_list_len) +{ + uint8_t rem_len = kde_list_len; + uint8_t *temp_ie = kde_list; + uint8_t elem_id, data_type, data_len, *ie_data = NULL, *current_ie; + uint16_t elem_len; + + if (!kde_list_len || !kde_list) { + pe_err("kde_list NULL or kde_list_len %d", kde_list_len); + return QDF_STATUS_E_FAILURE; + } + + while (rem_len >= 2) { + current_ie = temp_ie; + elem_id = *temp_ie++; + elem_len = *temp_ie++; + rem_len -= 2; + + if (rem_len < elem_len || elem_len > kde_list_len) { + pe_err("Invalid elem_len %d rem_len %d list_len %d", + elem_len, rem_len, kde_list_len); + return QDF_STATUS_E_FAILURE; + } + + if (elem_len < KDE_IE_DATA_OFFSET) { + pe_err("Not enough len to parse elem_len %d", + elem_len); + return QDF_STATUS_E_FAILURE; + } + + if (lim_check_if_vendor_oui_match(mac_ctx, KDE_OUI_TYPE, + KDE_OUI_TYPE_SIZE, current_ie, elem_len)) { + + data_type = *(temp_ie + KDE_DATA_TYPE_OFFSET); + ie_data = (temp_ie + KDE_IE_DATA_OFFSET); + data_len = (elem_len - KDE_IE_DATA_OFFSET); + + switch (data_type) { + case DATA_TYPE_GTK: + if (data_len < GTK_OFFSET) { + pe_err("Invalid KDE data_len %d", + data_len); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(fils_info->gtk, (ie_data + + GTK_OFFSET), (data_len - + GTK_OFFSET)); + fils_info->gtk_len = (data_len - GTK_OFFSET); + break; + + case DATA_TYPE_IGTK: + if (data_len < IGTK_OFFSET) { + pe_err("Invalid KDE data_len %d", + data_len); + return QDF_STATUS_E_FAILURE; + } + fils_info->igtk_len = (data_len - IGTK_OFFSET); + qdf_mem_copy(fils_info->igtk, (ie_data + + IGTK_OFFSET), (data_len - + IGTK_OFFSET)); + qdf_mem_copy(fils_info->ipn, (ie_data + + IPN_OFFSET), IPN_LEN); + break; + default: + pe_err("Unknown KDE data type %x", data_type); + break; + } + } + + temp_ie += elem_len; + rem_len -= elem_len; + ie_data = NULL; + } + + /* Expecting GTK in KDE */ + if (!fils_info->gtk_len) { + pe_err("GTK not found in KDE"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +void lim_update_fils_hlp_data(struct qdf_mac_addr *hlp_frm_src_mac, + struct qdf_mac_addr *hlp_frm_dest_mac, + uint16_t frm_hlp_len, uint8_t *frm_hlp_data, + struct pe_session *pe_session) +{ + struct pe_fils_session *pe_fils_info = pe_session->fils_info; + + if (!pe_fils_info) { + pe_err("Not a fils connection"); + return; + } + + if (frm_hlp_data && frm_hlp_len) { + qdf_mem_free(pe_fils_info->hlp_data); + pe_fils_info->hlp_data = qdf_mem_malloc(frm_hlp_len); + if (!pe_fils_info->hlp_data) + return; + + pe_debug("FILS: hlp_data_len:%d", frm_hlp_len); + cds_copy_hlp_info(hlp_frm_dest_mac, hlp_frm_src_mac, + frm_hlp_len, frm_hlp_data, + &pe_fils_info->dst_mac, + &pe_fils_info->src_mac, + &pe_fils_info->hlp_data_len, + pe_fils_info->hlp_data); + } +} + +bool lim_verify_fils_params_assoc_rsp(struct mac_context *mac_ctx, + struct pe_session *session_entry, + tpSirAssocRsp assoc_rsp, + tLimMlmAssocCnf *assoc_cnf) +{ + struct pe_fils_session *fils_info = session_entry->fils_info; + tDot11fIEfils_session fils_session = assoc_rsp->fils_session; + tDot11fIEfils_key_confirmation fils_key_auth = assoc_rsp->fils_key_auth; + tDot11fIEfils_kde fils_kde = assoc_rsp->fils_kde; + QDF_STATUS status; + + if (!lim_is_fils_connection(session_entry)) + return true; + + if (!fils_info) { + pe_err("FILS Info not present"); + goto verify_fils_params_fails; + } + + if (!assoc_rsp->fils_session.present) { + pe_err("FILS IE not present"); + goto verify_fils_params_fails; + } + + /* Compare FILS session */ + if (qdf_mem_cmp(fils_info->fils_session, + fils_session.session, DOT11F_IE_FILS_SESSION_MAX_LEN)) { + pe_err("FILS session mismatch"); + goto verify_fils_params_fails; + } + + /* Compare FILS key auth */ + if ((fils_key_auth.num_key_auth != fils_info->key_auth_len) || + qdf_mem_cmp(fils_info->ap_key_auth_data, fils_key_auth.key_auth, + fils_info->ap_key_auth_len)) { + lim_fils_data_dump("session keyauth", + fils_info->ap_key_auth_data, + fils_info->ap_key_auth_len); + lim_fils_data_dump("Pkt keyauth", + fils_key_auth.key_auth, + fils_key_auth.num_key_auth); + goto verify_fils_params_fails; + } + + /* Verify the Key Delivery Element presence */ + if (!fils_kde.num_kde_list) { + pe_err("FILS KDE list absent"); + goto verify_fils_params_fails; + } + + /* Derive KDE elements */ + status = lim_parse_kde_elements(mac_ctx, fils_info, fils_kde.kde_list, + fils_kde.num_kde_list); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("KDE parsing fails"); + goto verify_fils_params_fails; + } + + lim_update_fils_hlp_data(&assoc_rsp->dst_mac, + &assoc_rsp->src_mac, + assoc_rsp->hlp_data_len, + assoc_rsp->hlp_data, + session_entry); + return true; + +verify_fils_params_fails: + assoc_cnf->resultCode = eSIR_SME_ASSOC_REFUSED; + assoc_cnf->protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + return false; +} + +/** + * find_ie_data_after_fils_session_ie() - Find IE pointer after FILS Session IE + * @mac_ctx: MAC context + * @buf: IE buffer + * @buf_len: Length of @buf + * @ie: Pointer to update the found IE pointer after FILS session IE + * @ie_len: length of the IE data after FILS session IE + * + * This API is used to find the IE data ptr and length after FILS session IE + * + * Return: QDF_STATUS_SUCCESS if found, else QDF_STATUS_E_FAILURE + */ +static QDF_STATUS find_ie_data_after_fils_session_ie(struct mac_context *mac_ctx, + uint8_t *buf, + uint32_t buf_len, + uint8_t **ie, + uint32_t *ie_len) +{ + uint32_t left = buf_len; + uint8_t *ptr = buf; + uint8_t elem_id, elem_len; + + if (!buf || 0 == buf_len) + return QDF_STATUS_E_FAILURE; + + while (left >= 2) { + elem_id = ptr[0]; + elem_len = ptr[1]; + left -= 2; + if (elem_len > left) + return QDF_STATUS_E_FAILURE; + + if (elem_id == WLAN_REQUEST_IE_MAX_LEN && + ptr[2] == SIR_FILS_SESSION_EXT_EID) { + (*ie) = ((&ptr[1]) + ptr[1] + 1); + (*ie_len) = (left - elem_len); + return QDF_STATUS_SUCCESS; + } + left -= elem_len; + ptr += (elem_len + 2); + } + return QDF_STATUS_E_FAILURE; +} + +/** + * fils_aead_encrypt() - API to do FILS AEAD encryption + * + * @kek: Pointer to KEK + * @kek_len: KEK length + * @own_mac: Pointer to own MAC address + * @bssid: Bssid + * @snonce: Supplicant Nonce + * @anonce: Authenticator Nonce + * @data: Pointer to data after MAC header + * @data_len: length of @data + * @plain_text: Pointer to data after FILS Session IE + * @plain_text_len: length of @plain_text + * @out: Pointer to the encrypted data + * + * length of AEAD encryption @out is @plain_text_len + AES_BLOCK_SIZE[16 bytes] + * + * Return: zero on success, error otherwise + */ +static int fils_aead_encrypt(const uint8_t *kek, unsigned int kek_len, + const uint8_t *own_mac, const uint8_t *bssid, + const uint8_t *snonce, const uint8_t *anonce, + const uint8_t *data, size_t data_len, + uint8_t *plain_text, size_t plain_text_len, + uint8_t *out) +{ + uint8_t v[AES_BLOCK_SIZE]; + const uint8_t *aad[6]; + size_t aad_len[6]; + uint8_t *buf; + int ret; + + /* SIV Encrypt/Decrypt takes input key of length 256, 384 or 512 bits */ + if (kek_len != 32 && kek_len != 48 && kek_len != 64) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Invalid key length: %u"), kek_len); + return -EINVAL; + } + + if (!own_mac || !bssid || !snonce || + !anonce || data_len == 0 || plain_text_len == 0 || + !out) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Error missing params mac:%pK bssid:%pK snonce:%pK anonce:%pK data_len:%zu plain_text_len:%zu out:%pK"), + own_mac, bssid, snonce, anonce, data_len, + plain_text_len, out); + return -EINVAL; + } + + if (plain_text == out) { + buf = qdf_mem_malloc(plain_text_len); + if (!buf) + return -ENOMEM; + qdf_mem_copy(buf, plain_text, plain_text_len); + } else { + buf = plain_text; + } + + aad[0] = own_mac; + aad_len[0] = QDF_MAC_ADDR_SIZE; + aad[1] = bssid; + aad_len[1] = QDF_MAC_ADDR_SIZE; + aad[2] = snonce; + aad_len[2] = SIR_FILS_NONCE_LENGTH; + aad[3] = anonce; + aad_len[3] = SIR_FILS_NONCE_LENGTH; + aad[4] = data; + aad_len[4] = data_len; + /* Plain text, P, is Sn in AES-SIV */ + aad[5] = buf; + aad_len[5] = plain_text_len; + + /* AES-SIV S2V */ + /* K1 = leftmost(K, len(K)/2) */ + ret = qdf_aes_s2v(kek, kek_len/2, aad, aad_len, 6, v); + if (ret) + goto error; + + /* out = SIV || C (Synthetic Initialization Vector || Ciphered text) */ + qdf_mem_copy(out, v, AES_BLOCK_SIZE); + + /* AES-SIV CTR */ + /* K2 = rightmost(K, len(K)/2) */ + /* Clear 31st and 63rd bits in counter synthetic iv */ + v[12] &= 0x7F; + v[8] &= 0x7F; + + ret = qdf_aes_ctr(kek + kek_len/2, kek_len/2, v, buf, plain_text_len, + out + AES_BLOCK_SIZE, true); + +error: + if (plain_text == out) + qdf_mem_free(buf); + return ret; +} + +QDF_STATUS aead_encrypt_assoc_req(struct mac_context *mac_ctx, + struct pe_session *pe_session, + uint8_t *frm, uint32_t *frm_len) +{ + uint8_t *plain_text = NULL, *data; + uint32_t plain_text_len = 0, data_len; + QDF_STATUS status; + struct pe_fils_session *fils_info = pe_session->fils_info; + + /* + * data is the packet data after MAC header till + * FILS session IE(inclusive) + */ + data = frm + sizeof(tSirMacMgmtHdr); + + /* + * plain_text is the packet data after FILS session IE + * which needs to be encrypted. Get plain_text ptr and + * plain_text_len values using find_ptr_aft_fils_session_ie() + */ + status = find_ie_data_after_fils_session_ie(mac_ctx, data + + FIXED_PARAM_OFFSET_ASSOC_REQ, + (*frm_len - + FIXED_PARAM_OFFSET_ASSOC_REQ), + &plain_text, &plain_text_len); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Could not find FILS session IE"); + return QDF_STATUS_E_FAILURE; + } + data_len = ((*frm_len) - plain_text_len); + + lim_fils_data_dump("Plain text: ", plain_text, plain_text_len); + + /* Overwrite the AEAD encrypted output @ plain_text */ + if (fils_aead_encrypt(fils_info->kek, fils_info->kek_len, + pe_session->self_mac_addr, pe_session->bssId, + fils_info->fils_nonce, + fils_info->auth_info.fils_nonce, + data, data_len, plain_text, plain_text_len, + plain_text)) { + pe_err("AEAD Encryption fails!"); + return QDF_STATUS_E_FAILURE; + } + + /* + * AEAD encrypted output(cipher_text) will have length equals to + * plain_text_len + AES_BLOCK_SIZE(AEAD encryption header info). + * Add this to frm_len + */ + (*frm_len) += (AES_BLOCK_SIZE); + + return QDF_STATUS_SUCCESS; +} + +/** + * fils_aead_decrypt() - API to do AEAD decryption + * + * @kek: Pointer to KEK + * @kek_len: KEK length + * @own_mac: Pointer to own MAC address + * @bssid: Bssid + * @snonce: Supplicant Nonce + * @anonce: Authenticator Nonce + * @data: Pointer to data after MAC header + * @data_len: length of @data + * @plain_text: Pointer to data after FILS Session IE + * @plain_text_len: length of @plain_text + * @out: Pointer to the encrypted data + * + * Return: zero on success, error otherwise + */ +static int fils_aead_decrypt(const uint8_t *kek, unsigned int kek_len, + const uint8_t *own_mac, const uint8_t *bssid, + const uint8_t *snonce, const uint8_t *anonce, + const uint8_t *data, size_t data_len, + uint8_t *ciphered_text, size_t ciphered_text_len, + uint8_t *plain_text) +{ + const uint8_t *aad[6]; + size_t aad_len[6]; + uint8_t *buf; + size_t buf_len; + uint8_t v[AES_BLOCK_SIZE]; + uint8_t siv[AES_BLOCK_SIZE]; + int ret; + + /* SIV Encrypt/Decrypt takes input key of length 256, 384 or 512 bits */ + if (kek_len != 32 && kek_len != 48 && kek_len != 64) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Invalid key length: %u"), kek_len); + return -EINVAL; + } + + if (!own_mac || !bssid || !snonce || + !anonce || data_len == 0 || ciphered_text_len == 0 || + !plain_text) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Error missing params mac:%pK bssid:%pK snonce:%pK anonce:%pK data_len:%zu ciphered_text_len:%zu plain_text:%pK"), + own_mac, bssid, snonce, anonce, data_len, + ciphered_text_len, plain_text); + return -EINVAL; + } + + qdf_mem_copy(v, ciphered_text, AES_BLOCK_SIZE); + qdf_mem_copy(siv, ciphered_text, AES_BLOCK_SIZE); + v[12] &= 0x7F; + v[8] &= 0x7F; + + buf_len = ciphered_text_len - AES_BLOCK_SIZE; + if (ciphered_text == plain_text) { + /* in place decryption */ + buf = qdf_mem_malloc(buf_len); + if (!buf) + return -ENOMEM; + qdf_mem_copy(buf, ciphered_text + AES_BLOCK_SIZE, buf_len); + } else { + buf = ciphered_text + AES_BLOCK_SIZE; + } + + /* AES-SIV CTR */ + /* K2 = rightmost(K, len(K)/2) */ + ret = qdf_aes_ctr(kek + kek_len/2, kek_len/2, v, buf, buf_len, + plain_text, false); + if (ret) + goto error; + + aad[0] = bssid; + aad_len[0] = QDF_MAC_ADDR_SIZE; + aad[1] = own_mac; + aad_len[1] = QDF_MAC_ADDR_SIZE; + aad[2] = anonce; + aad_len[2] = SIR_FILS_NONCE_LENGTH; + aad[3] = snonce; + aad_len[3] = SIR_FILS_NONCE_LENGTH; + aad[4] = data; + aad_len[4] = data_len; + aad[5] = plain_text; + aad_len[5] = buf_len; + + /* AES-SIV S2V */ + /* K1 = leftmost(K, len(K)/2) */ + ret = qdf_aes_s2v(kek, kek_len/2, aad, aad_len, 6, v); + if (ret) + goto error; + + /* compare the iv generated against the one sent by AP */ + if (memcmp(v, siv, AES_BLOCK_SIZE) != 0) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("siv not same as frame siv")); + ret = -EINVAL; + } + +error: + if (ciphered_text == plain_text) + qdf_mem_free(buf); + return ret; +} + +QDF_STATUS aead_decrypt_assoc_rsp(struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fAssocResponse *ar, + uint8_t *p_frame, uint32_t *n_frame) +{ + QDF_STATUS status; + uint32_t data_len, fils_ies_len; + uint8_t *fils_ies; + struct pe_fils_session *fils_info = session->fils_info; + + if (*n_frame < FIXED_PARAM_OFFSET_ASSOC_RSP) { + pe_debug("payload len is less than ASSOC RES offset"); + return QDF_STATUS_E_FAILURE; + } + + status = find_ie_data_after_fils_session_ie(mac_ctx, p_frame + + FIXED_PARAM_OFFSET_ASSOC_RSP, + ((*n_frame) - + FIXED_PARAM_OFFSET_ASSOC_RSP), + &fils_ies, &fils_ies_len); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("FILS session IE not present"); + return status; + } + + data_len = (*n_frame) - fils_ies_len; + + if (fils_aead_decrypt(fils_info->kek, fils_info->kek_len, + session->self_mac_addr, session->bssId, + fils_info->fils_nonce, + fils_info->auth_info.fils_nonce, + p_frame, data_len, + fils_ies, fils_ies_len, fils_ies)){ + pe_err("AEAD decryption fails"); + return QDF_STATUS_E_FAILURE; + } + + /* Dump the output of AEAD decrypt */ + lim_fils_data_dump("Plain text: ", fils_ies, + fils_ies_len - AES_BLOCK_SIZE); + + (*n_frame) -= AES_BLOCK_SIZE; + return status; +} + +void lim_update_fils_rik(struct pe_session *pe_session, + struct roam_offload_scan_req *req_buffer) +{ + struct pe_fils_session *pe_fils_info = pe_session->fils_info; + struct roam_fils_params *roam_fils_params = + &req_buffer->roam_fils_params; + + /* + * If it is first connection, LIM session entries will not be + * set with FILS. However in RSO, CSR filled the RRK, realm + * info and is_fils_connection to true in req_buffer, RIK + * can be created with RRK and send all the FILS info to fw + */ + if ((!lim_is_fils_connection(pe_session) || + !pe_fils_info) && (req_buffer->is_fils_connection)) { + if (roam_fils_params->rrk_length > FILS_MAX_RRK_LENGTH) { + if (lim_is_fils_connection(pe_session)) + pe_debug("FILS rrk len(%d) max (%d)", + roam_fils_params->rrk_length, + FILS_MAX_RRK_LENGTH); + return; + } + + lim_create_fils_rik(roam_fils_params->rrk, + roam_fils_params->rrk_length, + roam_fils_params->rik, + &roam_fils_params->rik_length); + pe_debug("Fils created rik len %d", + roam_fils_params->rik_length); + return; + } + + if (!pe_fils_info) { + pe_debug("No FILS info available in the session"); + return; + } + if ((pe_fils_info->fils_rik_len > FILS_MAX_RIK_LENGTH) || + !pe_fils_info->fils_rik) { + if (pe_fils_info->fils_rik) + pe_debug("Fils rik len(%d) max %d", + pe_fils_info->fils_rik_len, + FILS_MAX_RIK_LENGTH); + return; + } + + roam_fils_params->rik_length = pe_fils_info->fils_rik_len; + qdf_mem_copy(roam_fils_params->rik, pe_fils_info->fils_rik, + roam_fils_params->rik_length); + qdf_mem_copy(roam_fils_params->fils_ft, pe_fils_info->fils_ft, + pe_fils_info->fils_ft_len); + roam_fils_params->fils_ft_len = pe_fils_info->fils_ft_len; + pe_debug("fils rik len %d ft-len:%d", roam_fils_params->rik_length, + pe_fils_info->fils_ft_len); +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_message_queue.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_message_queue.c new file mode 100644 index 0000000000000000000000000000000000000000..196f3322dc00da196aff2c551ebca322a6214000 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_message_queue.c @@ -0,0 +1,2372 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim ProcessMessageQueue.cc contains the code + * for processing LIM message Queue. + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#include "cds_api.h" +#include "wni_api.h" +#include "wma_types.h" + +#include "wni_cfg.h" +#include "sir_common.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_prop_exts_utils.h" + +#include "lim_admit_control.h" +#include "lim_ibss_peer_mgmt.h" +#include "sch_api.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "lim_send_messages.h" + +#include "rrm_api.h" + +#include "lim_ft.h" + +#include "qdf_types.h" +#include "cds_packet.h" +#include "qdf_mem.h" +#include "wlan_policy_mgr_api.h" +#include "nan_datapath.h" +#include "wlan_reg_services_api.h" +#include "lim_security_utils.h" +#include "cds_ieee80211_common.h" +#include +#include "wlan_mlme_public_struct.h" +#include "wma.h" +#include "wma_internal.h" +#include "../../core/src/vdev_mgr_ops.h" +#include "wlan_p2p_cfg_api.h" + +void lim_log_session_states(struct mac_context *mac); +static void lim_process_normal_hdd_msg(struct mac_context *mac_ctx, + struct scheduler_msg *msg, uint8_t rsp_reqd); + +#ifdef WLAN_FEATURE_SAE + +/** + * lim_process_sae_msg_sta() - Process SAE message for STA + * @mac: Global MAC pointer + * @session: Pointer to the PE session entry + * @sae_msg: SAE message buffer pointer + * + * Return: None + */ +static void lim_process_sae_msg_sta(struct mac_context *mac, + struct pe_session *session, + struct sir_sae_msg *sae_msg) +{ + switch (session->limMlmState) { + case eLIM_MLM_WT_SAE_AUTH_STATE: + /* SAE authentication is completed. + * Restore from auth state + */ + if (tx_timer_running(&mac->lim.lim_timers.sae_auth_timer)) + lim_deactivate_and_change_timer(mac, + eLIM_AUTH_SAE_TIMER); + lim_sae_auth_cleanup_retry(mac, session->vdev_id); + /* success */ + if (sae_msg->sae_status == IEEE80211_STATUS_SUCCESS) + lim_restore_from_auth_state(mac, + eSIR_SME_SUCCESS, + eSIR_MAC_SUCCESS_STATUS, + session); + else + lim_restore_from_auth_state(mac, eSIR_SME_AUTH_REFUSED, + eSIR_MAC_UNSPEC_FAILURE_STATUS, session); + break; + default: + /* SAE msg is received in unexpected state */ + pe_err("received SAE msg in state %X", session->limMlmState); + lim_print_mlm_state(mac, LOGE, session->limMlmState); + break; + } +} + +/** + * lim_process_sae_msg_ap() - Process SAE message + * @mac: Global MAC pointer + * @session: Pointer to the PE session entry + * @sae_msg: SAE message buffer pointer + * + * Return: None + */ +static void lim_process_sae_msg_ap(struct mac_context *mac, + struct pe_session *session, + struct sir_sae_msg *sae_msg) +{ + struct tLimPreAuthNode *sta_pre_auth_ctx; + struct lim_assoc_data *assoc_req; + bool assoc_ind_sent; + + /* Extract pre-auth context for the STA and move limMlmState + * of preauth node to eLIM_MLM_AUTHENTICATED_STATE + */ + sta_pre_auth_ctx = lim_search_pre_auth_list(mac, + sae_msg->peer_mac_addr); + + if (!sta_pre_auth_ctx) { + pe_debug("No preauth node created for " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sae_msg->peer_mac_addr)); + return; + } + + assoc_req = &sta_pre_auth_ctx->assoc_req; + + if (sae_msg->sae_status != IEEE80211_STATUS_SUCCESS) { + pe_debug("SAE authentication failed for " + QDF_MAC_ADDR_FMT " status: %u", + QDF_MAC_ADDR_REF(sae_msg->peer_mac_addr), + sae_msg->sae_status); + if (assoc_req->present) { + pe_debug("Assoc req cached; clean it up"); + lim_process_assoc_cleanup(mac, session, + assoc_req->assoc_req, + assoc_req->sta_ds, + assoc_req->assoc_req_copied); + assoc_req->present = false; + } + lim_delete_pre_auth_node(mac, sae_msg->peer_mac_addr); + return; + } + sta_pre_auth_ctx->mlmState = eLIM_MLM_AUTHENTICATED_STATE; + /* Send assoc indication to SME if any assoc request is cached*/ + if (assoc_req->present) { + /* Assoc request is present in preauth context. Get the assoc + * request and make it invalid in preauth context. It'll be + * freed later in the legacy path. + */ + bool assoc_req_copied; + + assoc_req->present = false; + pe_debug("Assoc req cached; handle it"); + assoc_ind_sent = + lim_send_assoc_ind_to_sme(mac, session, + assoc_req->sub_type, + &assoc_req->hdr, + assoc_req->assoc_req, + ANI_AKM_TYPE_SAE, + assoc_req->pmf_connection, + &assoc_req_copied, + assoc_req->dup_entry, false); + if (!assoc_ind_sent) + lim_process_assoc_cleanup(mac, session, + assoc_req->assoc_req, + assoc_req->sta_ds, + assoc_req_copied); + } +} + +/** + * lim_process_sae_msg() - Process SAE message + * @mac: Global MAC pointer + * @body: Buffer pointer + * + * Return: None + */ +static void lim_process_sae_msg(struct mac_context *mac, struct sir_sae_msg *body) +{ + struct sir_sae_msg *sae_msg = body; + struct pe_session *session; + + if (!sae_msg) { + pe_err("SAE msg is NULL"); + return; + } + + session = pe_find_session_by_vdev_id(mac, sae_msg->vdev_id); + if (!session) { + pe_err("SAE:Unable to find session"); + return; + } + + if (session->opmode != QDF_STA_MODE && + session->opmode != QDF_SAP_MODE) { + pe_err("SAE:Not supported in this mode %d", + session->opmode); + return; + } + + pe_debug("SAE:status %d limMlmState %d opmode %d peer: " + QDF_MAC_ADDR_FMT, sae_msg->sae_status, + session->limMlmState, session->opmode, + QDF_MAC_ADDR_REF(sae_msg->peer_mac_addr)); + if (LIM_IS_STA_ROLE(session)) + lim_process_sae_msg_sta(mac, session, sae_msg); + else if (LIM_IS_AP_ROLE(session)) + lim_process_sae_msg_ap(mac, session, sae_msg); + else + pe_debug("SAE message on unsupported interface"); +} +#else +static inline void lim_process_sae_msg(struct mac_context *mac, void *body) +{} +#endif + +/** + * lim_process_dual_mac_cfg_resp() - Process set dual mac config response + * @mac: Global MAC pointer + * @body: Set dual mac config response in sir_dual_mac_config_resp format + * + * Process the set dual mac config response and post the message + * to SME to process this further and release the active + * command list + * + * Return: None + */ +static void lim_process_dual_mac_cfg_resp(struct mac_context *mac, void *body) +{ + struct sir_dual_mac_config_resp *resp, *param; + uint32_t len, fail_resp = 0; + struct scheduler_msg msg = {0}; + + resp = (struct sir_dual_mac_config_resp *)body; + if (!resp) { + pe_err("Set dual mac cfg param is NULL"); + fail_resp = 1; + /* Not returning here. If possible, let us proceed + * and send fail response to SME + */ + } + + len = sizeof(*param); + + param = qdf_mem_malloc(len); + if (!param) + return; + + if (fail_resp) { + pe_err("Send fail status to SME"); + param->status = SET_HW_MODE_STATUS_ECANCELED; + } else { + param->status = resp->status; + /* + * TODO: Update this HW mode info in any UMAC params, if needed + */ + } + + msg.type = eWNI_SME_SET_DUAL_MAC_CFG_RESP; + msg.bodyptr = param; + msg.bodyval = 0; + pe_debug("Send eWNI_SME_SET_DUAL_MAC_CFG_RESP to SME"); + lim_sys_process_mmh_msg_api(mac, &msg); + return; +} + +/** + * lim_process_set_hw_mode_resp() - Process set HW mode response + * @mac: Global MAC pointer + * @body: Set HW mode response in sir_set_hw_mode_resp format + * + * Process the set HW mode response and post the message + * to SME to process this further and release the active + * command list + * + * Return: None + */ +static void lim_process_set_hw_mode_resp(struct mac_context *mac, void *body) +{ + struct sir_set_hw_mode_resp *resp, *param; + uint32_t len, i, fail_resp = 0; + struct scheduler_msg msg = {0}; + + resp = (struct sir_set_hw_mode_resp *)body; + if (!resp) { + pe_err("Set HW mode param is NULL"); + fail_resp = 1; + /* Not returning here. If possible, let us proceed + * and send fail response to SME */ + } + + len = sizeof(*param); + + param = qdf_mem_malloc(len); + if (!param) + return; + + if (fail_resp) { + pe_err("Send fail status to SME"); + param->status = SET_HW_MODE_STATUS_ECANCELED; + param->cfgd_hw_mode_index = 0; + param->num_vdev_mac_entries = 0; + } else { + param->status = resp->status; + param->cfgd_hw_mode_index = resp->cfgd_hw_mode_index; + param->num_vdev_mac_entries = resp->num_vdev_mac_entries; + + for (i = 0; i < resp->num_vdev_mac_entries; i++) { + param->vdev_mac_map[i].vdev_id = + resp->vdev_mac_map[i].vdev_id; + param->vdev_mac_map[i].mac_id = + resp->vdev_mac_map[i].mac_id; + } + /* + * TODO: Update this HW mode info in any UMAC params, if needed + */ + } + + msg.type = eWNI_SME_SET_HW_MODE_RESP; + msg.bodyptr = param; + msg.bodyval = 0; + pe_debug("Send eWNI_SME_SET_HW_MODE_RESP to SME"); + lim_sys_process_mmh_msg_api(mac, &msg); + return; +} + +/** + * lim_process_antenna_mode_resp() - Process set antenna mode + * response + * @mac: Global MAC pointer + * @body: Set antenna mode response in sir_antenna_mode_resp + * format + * + * Process the set antenna mode response and post the message + * to SME to process this further and release the active + * command list + * + * Return: None + */ +static void lim_process_set_antenna_resp(struct mac_context *mac, void *body) +{ + struct sir_antenna_mode_resp *resp, *param; + bool fail_resp = false; + struct scheduler_msg msg = {0}; + + resp = (struct sir_antenna_mode_resp *)body; + if (!resp) { + pe_err("Set antenna mode resp is NULL"); + fail_resp = true; + /* Not returning here. If possible, let us proceed + * and send fail response to SME + */ + } + + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return; + + if (fail_resp) { + pe_err("Send fail status to SME"); + param->status = SET_ANTENNA_MODE_STATUS_ECANCELED; + } else { + param->status = resp->status; + } + + msg.type = eWNI_SME_SET_ANTENNA_MODE_RESP; + msg.bodyptr = param; + msg.bodyval = 0; + pe_debug("Send eWNI_SME_SET_ANTENNA_MODE_RESP to SME"); + lim_sys_process_mmh_msg_api(mac, &msg); + return; +} + +/** + * lim_process_set_default_scan_ie_request() - Process the Set default + * Scan IE request from HDD. + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: Pointer to incoming data + * + * This function receives the default scan IEs and updates the ext cap IE + * (if present) with FTM capabilities and pass the Scan IEs to WMA. + * + * Return: None + */ +static void lim_process_set_default_scan_ie_request(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct hdd_default_scan_ie *set_ie_params; + struct vdev_ie_info *wma_ie_params; + uint8_t *local_ie_buf; + uint16_t local_ie_len; + struct scheduler_msg msg_q = {0}; + QDF_STATUS ret_code; + struct pe_session *pe_session; + + if (!msg_buf) { + pe_err("msg_buf is NULL"); + return; + } + + set_ie_params = (struct hdd_default_scan_ie *) msg_buf; + local_ie_len = set_ie_params->ie_len; + + local_ie_buf = qdf_mem_malloc(MAX_DEFAULT_SCAN_IE_LEN); + if (!local_ie_buf) + return; + + pe_session = pe_find_session_by_vdev_id(mac_ctx, + set_ie_params->vdev_id); + if (lim_update_ext_cap_ie(mac_ctx, + (uint8_t *)set_ie_params->ie_data, + local_ie_buf, &local_ie_len, pe_session)) { + pe_err("Update ext cap IEs fails"); + goto scan_ie_send_fail; + } + + wma_ie_params = qdf_mem_malloc(sizeof(*wma_ie_params) + local_ie_len); + if (!wma_ie_params) + goto scan_ie_send_fail; + + wma_ie_params->vdev_id = set_ie_params->vdev_id; + wma_ie_params->ie_id = DEFAULT_SCAN_IE_ID; + wma_ie_params->length = local_ie_len; + wma_ie_params->data = (uint8_t *)(wma_ie_params) + + sizeof(*wma_ie_params); + qdf_mem_copy(wma_ie_params->data, local_ie_buf, local_ie_len); + + msg_q.type = WMA_SET_IE_INFO; + msg_q.bodyptr = wma_ie_params; + msg_q.bodyval = 0; + ret_code = wma_post_ctrl_msg(mac_ctx, &msg_q); + if (QDF_STATUS_SUCCESS != ret_code) { + pe_err("fail to send WMA_SET_IE_INFO"); + qdf_mem_free(wma_ie_params); + } +scan_ie_send_fail: + qdf_mem_free(local_ie_buf); +} + +/** + * lim_process_hw_mode_trans_ind() - Process set HW mode transition indication + * @mac: Global MAC pointer + * @body: Set HW mode response in sir_hw_mode_trans_ind format + * + * Process the set HW mode transition indication and post the message + * to SME to invoke the HDD callback + * command list + * + * Return: None + */ +static void lim_process_hw_mode_trans_ind(struct mac_context *mac, void *body) +{ + struct sir_hw_mode_trans_ind *ind, *param; + uint32_t len, i; + struct scheduler_msg msg = {0}; + + ind = (struct sir_hw_mode_trans_ind *)body; + if (!ind) { + pe_err("Set HW mode trans ind param is NULL"); + return; + } + + len = sizeof(*param); + + param = qdf_mem_malloc(len); + if (!param) + return; + + param->old_hw_mode_index = ind->old_hw_mode_index; + param->new_hw_mode_index = ind->new_hw_mode_index; + param->num_vdev_mac_entries = ind->num_vdev_mac_entries; + + for (i = 0; i < ind->num_vdev_mac_entries; i++) { + param->vdev_mac_map[i].vdev_id = + ind->vdev_mac_map[i].vdev_id; + param->vdev_mac_map[i].mac_id = + ind->vdev_mac_map[i].mac_id; + } + + /* TODO: Update this HW mode info in any UMAC params, if needed */ + + msg.type = eWNI_SME_HW_MODE_TRANS_IND; + msg.bodyptr = param; + msg.bodyval = 0; + pe_err("Send eWNI_SME_HW_MODE_TRANS_IND to SME"); + lim_sys_process_mmh_msg_api(mac, &msg); + return; +} + +/** + * def_msg_decision() - Should a message be deferred? + * @mac_ctx: The global MAC context + * @lim_msg: The message to check for potential deferral + * + * This function decides whether to defer a message or not in + * lim_message_processor() function + * + * Return: true if the message can be deferred, false otherwise + */ +static bool def_msg_decision(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg) +{ + uint8_t type, subtype; + QDF_STATUS status; + bool mgmt_pkt_defer = true; + + /* this function should not changed */ + if (mac_ctx->lim.gLimSmeState == eLIM_SME_OFFLINE_STATE) { + /* Defer processing this message */ + if (lim_defer_msg(mac_ctx, lim_msg) != TX_SUCCESS) { + pe_err_rl("Unable to Defer Msg"); + lim_log_session_states(mac_ctx); + lim_handle_defer_msg_error(mac_ctx, lim_msg); + } + + return true; + } + + /* + * When defer is requested then defer all the messages except + * HAL responses. + */ + if (!GET_LIM_PROCESS_DEFD_MESGS(mac_ctx)) { + if (lim_msg->type == SIR_BB_XPORT_MGMT_MSG) { + /* + * Dont defer beacon and probe response + * because it will fill the differ queue quickly + */ + status = lim_util_get_type_subtype(lim_msg->bodyptr, + &type, &subtype); + if (QDF_IS_STATUS_SUCCESS(status) && + (type == SIR_MAC_MGMT_FRAME) && + ((subtype == SIR_MAC_MGMT_BEACON) || + (subtype == SIR_MAC_MGMT_PROBE_RSP))) + mgmt_pkt_defer = false; + } + + if ((lim_msg->type != WMA_DELETE_BSS_RSP) && + (lim_msg->type != WMA_DELETE_BSS_HO_FAIL_RSP) && + (lim_msg->type != WMA_ADD_STA_RSP) && + (lim_msg->type != WMA_DELETE_STA_RSP) && + (lim_msg->type != WMA_SET_BSSKEY_RSP) && + (lim_msg->type != WMA_SET_STAKEY_RSP) && + (lim_msg->type != WMA_SET_STA_BCASTKEY_RSP) && + (lim_msg->type != WMA_AGGR_QOS_RSP) && + (lim_msg->type != WMA_SET_MIMOPS_RSP) && + (lim_msg->type != WMA_SWITCH_CHANNEL_RSP) && + (lim_msg->type != WMA_P2P_NOA_ATTR_IND) && + (lim_msg->type != WMA_ADD_TS_RSP) && + /* + * LIM won't process any defer queue commands if gLimAddtsSent is + * set to TRUE. gLimAddtsSent will be set TRUE to while sending + * ADDTS REQ. Say, when deferring is enabled, if + * SIR_LIM_ADDTS_RSP_TIMEOUT is posted (because of not receiving ADDTS + * RSP) then this command will be added to defer queue and as + * gLimAddtsSent is set TRUE LIM will never process any commands from + * defer queue, including SIR_LIM_ADDTS_RSP_TIMEOUT. Hence allowing + * SIR_LIM_ADDTS_RSP_TIMEOUT command to be processed with deferring + * enabled, so that this will be processed immediately and sets + * gLimAddtsSent to FALSE. + */ + (lim_msg->type != SIR_LIM_ADDTS_RSP_TIMEOUT) && + /* Allow processing of RX frames while awaiting reception + * of ADD TS response over the air. This logic particularly + * handles the case when host sends ADD BA request to FW + * after ADD TS request is sent over the air and + * ADD TS response received over the air */ + !(lim_msg->type == SIR_BB_XPORT_MGMT_MSG && + mac_ctx->lim.gLimAddtsSent) && + (mgmt_pkt_defer)) { + pe_debug("Defer the current message %s , gLimProcessDefdMsgs is false and system is not in scan/learn mode", + lim_msg_str(lim_msg->type)); + /* Defer processing this message */ + if (lim_defer_msg(mac_ctx, lim_msg) != TX_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_PE, LOGE, + FL("Unable to Defer Msg")); + lim_log_session_states(mac_ctx); + lim_handle_defer_msg_error(mac_ctx, lim_msg); + } + return true; + } + } + return false; +} + +#ifdef FEATURE_WLAN_EXTSCAN +static void +__lim_pno_match_fwd_bcn_probepsp(struct mac_context *pmac, uint8_t *rx_pkt_info, + tSirProbeRespBeacon *frame, uint32_t ie_len, + uint32_t msg_type) +{ + struct pno_match_found *result; + uint8_t *body; + struct scheduler_msg mmh_msg = {0}; + tpSirMacMgmtHdr hdr; + uint32_t num_results = 1, len, i; + + /* Upon receiving every matched beacon, bss info is forwarded to the + * the upper layer, hence num_results is set to 1 */ + len = sizeof(*result) + (num_results * sizeof(tSirWifiScanResult)) + + ie_len; + + result = qdf_mem_malloc(len); + if (!result) + return; + + hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + + /* Received frame does not have request id, hence set 0 */ + result->request_id = 0; + result->more_data = 0; + result->num_results = num_results; + + for (i = 0; i < result->num_results; i++) { + result->ap[i].ts = qdf_mc_timer_get_system_time(); + result->ap[i].beaconPeriod = frame->beaconInterval; + result->ap[i].capability = + lim_get_u16((uint8_t *) &frame->capabilityInfo); + result->ap[i].channel = wlan_reg_freq_to_chan( + pmac->pdev, + WMA_GET_RX_FREQ(rx_pkt_info)); + result->ap[i].rssi = WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info); + result->ap[i].rtt = 0; + result->ap[i].rtt_sd = 0; + result->ap[i].ieLength = ie_len; + qdf_mem_copy((uint8_t *) &result->ap[i].ssid[0], + (uint8_t *) frame->ssId.ssId, frame->ssId.length); + result->ap[i].ssid[frame->ssId.length] = '\0'; + qdf_mem_copy((uint8_t *) &result->ap[i].bssid, + (uint8_t *) hdr->bssId, + sizeof(tSirMacAddr)); + /* Copy IE fields */ + qdf_mem_copy((uint8_t *) &result->ap[i].ieData, + body + SIR_MAC_B_PR_SSID_OFFSET, ie_len); + } + + mmh_msg.type = msg_type; + mmh_msg.bodyptr = result; + mmh_msg.bodyval = 0; + lim_sys_process_mmh_msg_api(pmac, &mmh_msg); +} + + +static void +__lim_ext_scan_forward_bcn_probe_rsp(struct mac_context *pmac, uint8_t *rx_pkt_info, + tSirProbeRespBeacon *frame, + uint32_t ie_len, + uint32_t msg_type) +{ + tpSirWifiFullScanResultEvent result; + uint8_t *body; + struct scheduler_msg mmh_msg = {0}; + tpSirMacMgmtHdr hdr; + uint32_t frame_len; + struct bss_description *bssdescr; + + result = qdf_mem_malloc(sizeof(*result) + ie_len); + if (!result) + return; + + hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + body = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + + /* Received frame does not have request id, hence set 0 */ + result->requestId = 0; + + result->moreData = 0; + result->ap.ts = qdf_mc_timer_get_system_time(); + result->ap.beaconPeriod = frame->beaconInterval; + result->ap.capability = + lim_get_u16((uint8_t *) &frame->capabilityInfo); + result->ap.channel = wlan_reg_freq_to_chan( + pmac->pdev, + WMA_GET_RX_FREQ(rx_pkt_info)); + result->ap.rssi = WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info); + result->ap.rtt = 0; + result->ap.rtt_sd = 0; + result->ap.ieLength = ie_len; + + qdf_mem_copy((uint8_t *) &result->ap.ssid[0], + (uint8_t *) frame->ssId.ssId, frame->ssId.length); + result->ap.ssid[frame->ssId.length] = '\0'; + qdf_mem_copy((uint8_t *) &result->ap.bssid.bytes, + (uint8_t *) hdr->bssId, + QDF_MAC_ADDR_SIZE); + /* Copy IE fields */ + qdf_mem_copy((uint8_t *) &result->ap.ieData, + body + SIR_MAC_B_PR_SSID_OFFSET, ie_len); + + frame_len = sizeof(*bssdescr) + ie_len - sizeof(bssdescr->ieFields[1]); + bssdescr = qdf_mem_malloc(frame_len); + if (!bssdescr) { + qdf_mem_free(result); + return; + } + + qdf_mem_zero(bssdescr, frame_len); + + lim_collect_bss_description(pmac, bssdescr, frame, rx_pkt_info, false); + + qdf_mem_free(bssdescr); + + mmh_msg.type = msg_type; + mmh_msg.bodyptr = result; + mmh_msg.bodyval = 0; + lim_sys_process_mmh_msg_api(pmac, &mmh_msg); +} + +static void +__lim_process_ext_scan_beacon_probe_rsp(struct mac_context *pmac, + uint8_t *rx_pkt_info, + uint8_t sub_type) +{ + tSirProbeRespBeacon *frame; + uint8_t *body; + uint32_t frm_len; + QDF_STATUS status; + + frm_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + if (frm_len <= SIR_MAC_B_PR_SSID_OFFSET) { + pe_err("RX packet has invalid length %d", frm_len); + return; + } + + frame = qdf_mem_malloc(sizeof(*frame)); + if (!frame) + return; + + if (sub_type == SIR_MAC_MGMT_BEACON) { + pe_debug("Beacon due to ExtScan/epno"); + status = sir_convert_beacon_frame2_struct(pmac, + (uint8_t *)rx_pkt_info, + frame); + } else if (sub_type == SIR_MAC_MGMT_PROBE_RSP) { + pe_debug("Probe Rsp due to ExtScan/epno"); + body = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + status = sir_convert_probe_frame2_struct(pmac, body, + frm_len, frame); + } else { + qdf_mem_free(frame); + return; + } + + if (status != QDF_STATUS_SUCCESS) { + pe_err("Frame parsing failed"); + qdf_mem_free(frame); + return; + } + + if (WMA_IS_EXTSCAN_SCAN_SRC(rx_pkt_info)) + __lim_ext_scan_forward_bcn_probe_rsp(pmac, rx_pkt_info, frame, + (frm_len - SIR_MAC_B_PR_SSID_OFFSET), + eWNI_SME_EXTSCAN_FULL_SCAN_RESULT_IND); + + if (WMA_IS_EPNO_SCAN_SRC(rx_pkt_info)) + __lim_pno_match_fwd_bcn_probepsp(pmac, rx_pkt_info, frame, + (frm_len - SIR_MAC_B_PR_SSID_OFFSET), + eWNI_SME_EPNO_NETWORK_FOUND_IND); + + qdf_mem_free(frame); +} +#endif + +/* + * Beacon Handling Cases: + * during scanning, when no session is active: + * handled by lim_handle_frames_in_scan_state before __lim_handle_beacon call is invoked. + * during scanning, when any session is active, but beacon/Pr does not belong to that session, pe_session will be null. + * handled by lim_handle_frames_in_scan_state before __lim_handle_beacon call is invoked. + * during scanning, when any session is active, and beacon/Pr belongs to one of the session, pe_session will not be null. + * handled by lim_handle_frames_in_scan_state before __lim_handle_beacon call is invoked. + * Not scanning, no session: + * there should not be any beacon coming, if coming, should be dropped. + * Not Scanning, + */ +static void +__lim_handle_beacon(struct mac_context *mac, struct scheduler_msg *pMsg, + struct pe_session *pe_session) +{ + uint8_t *pRxPacketInfo; + + lim_get_b_dfrom_rx_packet(mac, pMsg->bodyptr, + (uint32_t **) &pRxPacketInfo); + + /* This function should not be called if beacon is received in scan state. */ + /* So not doing any checks for the global state. */ + + if (!pe_session) { + sch_beacon_process(mac, pRxPacketInfo, NULL); + } else if ((pe_session->limSmeState == eLIM_SME_LINK_EST_STATE) || + (pe_session->limSmeState == eLIM_SME_NORMAL_STATE)) { + sch_beacon_process(mac, pRxPacketInfo, pe_session); + } else + lim_process_beacon_frame(mac, pRxPacketInfo, pe_session); + + return; +} + +/* + * lim_fill_sap_bcn_pkt_meta(): Fills essential fields in Rx Pkt Meta + * @scan_entry: pointer to the scan cache entry of the beacon + * @rx_pkt: pointer to the cds pkt allocated + * + * This API fills only the essential parameters in the Rx Pkt Meta which are + * required while converting the beacon frame to struct and while handling + * the beacon for implementation of SAP protection mechanisms. + * + * Return: None + */ +static void lim_fill_sap_bcn_pkt_meta(struct scan_cache_entry *scan_entry, + cds_pkt_t *rx_pkt) +{ + rx_pkt->pkt_meta.frequency = scan_entry->channel.chan_freq; + rx_pkt->pkt_meta.mpdu_hdr_len = sizeof(struct ieee80211_frame); + rx_pkt->pkt_meta.mpdu_len = scan_entry->raw_frame.len; + rx_pkt->pkt_meta.mpdu_data_len = rx_pkt->pkt_meta.mpdu_len - + rx_pkt->pkt_meta.mpdu_hdr_len; + + rx_pkt->pkt_meta.mpdu_hdr_ptr = scan_entry->raw_frame.ptr; + rx_pkt->pkt_meta.mpdu_data_ptr = rx_pkt->pkt_meta.mpdu_hdr_ptr + + rx_pkt->pkt_meta.mpdu_hdr_len; + + /* + * The scan_entry->raw_frame contains the qdf_nbuf->data from the SKB + * of the beacon. We set the rx_pkt->pkt_meta.mpdu_hdr_ptr to point + * to this memory directly. However we do not have the pointer to + * the SKB itself here which is usually is pointed by rx_pkt->pkt_buf. + * Also, we always get the pkt data using WMA_GET_RX_MPDU_DATA and + * dont actually use the pkt_buf. So setting this to NULL. + */ + rx_pkt->pkt_buf = NULL; +} + +/* + * lim_allocate_and_get_bcn() - Allocate and get the bcn frame pkt and structure + * @mac_ctx: pointer to global mac_ctx + * @pkt: pointer to the pkt to be allocated + * @rx_pkt_info: pointer to the allocated pkt meta + * @bcn: pointer to the beacon struct + * @scan_entry: pointer to the scan cache entry from scan module + * + * Allocates a cds_pkt for beacon frame in scan cache entry, + * fills the essential pkt_meta elements and converts the + * pkt to beacon strcut. + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_allocate_and_get_bcn( + struct mac_context *mac_ctx, + cds_pkt_t **pkt, + uint8_t **rx_pkt_info, + tSchBeaconStruct **bcn, + struct scan_cache_entry *scan_entry) +{ + QDF_STATUS status; + uint8_t *rx_pkt_info_l = NULL; + tSchBeaconStruct *bcn_l = NULL; + cds_pkt_t *pkt_l = NULL; + + pkt_l = qdf_mem_malloc(sizeof(cds_pkt_t)); + if (!pkt_l) + return QDF_STATUS_E_FAILURE; + + status = wma_ds_peek_rx_packet_info( + pkt_l, (void *)&rx_pkt_info_l, false); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("Failed to get Rx Pkt meta"); + goto free; + } + + bcn_l = qdf_mem_malloc(sizeof(tSchBeaconStruct)); + if (!bcn_l) + goto free; + + lim_fill_sap_bcn_pkt_meta(scan_entry, pkt_l); + + /* Convert the beacon frame into a structure */ + if (sir_convert_beacon_frame2_struct(mac_ctx, + (uint8_t *)rx_pkt_info_l, + bcn_l) != QDF_STATUS_SUCCESS) { + pe_err("beacon parsing failed"); + goto free; + } + + *pkt = pkt_l; + *bcn = bcn_l; + *rx_pkt_info = rx_pkt_info_l; + + return QDF_STATUS_SUCCESS; + +free: + if (pkt_l) { + qdf_mem_free(pkt_l); + pkt_l = NULL; + } + + if (bcn_l) { + qdf_mem_free(bcn_l); + bcn_l = NULL; + } + + return QDF_STATUS_E_FAILURE; +} + +void lim_handle_sap_beacon(struct wlan_objmgr_pdev *pdev, + struct scan_cache_entry *scan_entry) +{ + struct mac_context *mac_ctx; + cds_pkt_t *pkt = NULL; + tSchBeaconStruct *bcn = NULL; + struct mgmt_beacon_probe_filter *filter; + QDF_STATUS status; + uint8_t *rx_pkt_info = NULL; + int session_id; + + if (!scan_entry) { + pe_err("scan_entry is NULL"); + return; + } + + if (scan_entry->frm_subtype != MGMT_SUBTYPE_BEACON) + return; + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + pe_err("Failed to get mac_ctx"); + return; + } + + filter = &mac_ctx->bcn_filter; + + if (!filter->num_sap_sessions) { + return; + } + for (session_id = 0; session_id < mac_ctx->lim.maxBssId; session_id++) { + if (filter->sap_channel[session_id] && + (filter->sap_channel[session_id] == + wlan_reg_freq_to_chan(pdev, + scan_entry->channel.chan_freq))) { + if (!pkt) { + status = lim_allocate_and_get_bcn( + mac_ctx, &pkt, &rx_pkt_info, + &bcn, scan_entry); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_debug("lim_allocate_and_get_bcn fail!"); + return; + } + } + sch_beacon_process_for_ap(mac_ctx, session_id, + rx_pkt_info, bcn); + } + } + + /* + * Free only the pkt memory we allocated and not the pkt->pkt_buf. + * The actual SKB buffer is freed in the scan module from where + * this API is invoked via callback + */ + if (bcn) + qdf_mem_free(bcn); + if (pkt) + qdf_mem_free(pkt); +} + +/** + * lim_defer_msg() + * + ***FUNCTION: + * This function is called to defer the messages received + * during Learn mode + * + ***LOGIC: + * NA + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param pMsg of type struct scheduler_msg - Pointer to the message structure + * @return None + */ + +uint32_t lim_defer_msg(struct mac_context *mac, struct scheduler_msg *pMsg) +{ + uint32_t retCode = TX_SUCCESS; + + retCode = lim_write_deferred_msg_q(mac, pMsg); + + if (retCode == TX_SUCCESS) { + MTRACE(mac_trace_msg_rx + (mac, NO_SESSION, + LIM_TRACE_MAKE_RXMSG(pMsg->type, LIM_MSG_DEFERRED))); + } else { + pe_err_rl("Dropped lim message (0x%X) Message %s", pMsg->type, + lim_msg_str(pMsg->type)); + MTRACE(mac_trace_msg_rx + (mac, NO_SESSION, + LIM_TRACE_MAKE_RXMSG(pMsg->type, LIM_MSG_DROPPED))); + } + + return retCode; +} /*** end lim_defer_msg() ***/ + +/** + * lim_handle_unknown_a2_index_frames() - This function handles Unknown Unicast + * (A2 Index) packets + * @mac_ctx: Pointer to the Global Mac Context. + * @rx_pkt_buffer: Pointer to the packet Buffer descriptor. + * @session_entry: Pointer to the PE Session Entry. + * + * This routine will handle public action frames. + * + * Return: None. + */ +static void lim_handle_unknown_a2_index_frames(struct mac_context *mac_ctx, + void *rx_pkt_buffer, struct pe_session *session_entry) +{ +#ifdef FEATURE_WLAN_TDLS + tpSirMacDataHdr3a mac_hdr; +#endif + if (LIM_IS_P2P_DEVICE_ROLE(session_entry)) + lim_process_action_frame_no_session(mac_ctx, + (uint8_t *) rx_pkt_buffer); +#ifdef FEATURE_WLAN_TDLS + mac_hdr = WMA_GET_RX_MPDUHEADER3A(rx_pkt_buffer); + + if (IEEE80211_IS_MULTICAST(mac_hdr->addr2)) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + FL("Ignoring A2 Invalid Packet received for MC/BC:")); + lim_print_mac_addr(mac_ctx, mac_hdr->addr2, LOGD); + return; + } + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + FL("type=0x%x, subtype=0x%x"), + mac_hdr->fc.type, mac_hdr->fc.subType); + /* Currently only following type and subtype are handled. + * If there are more combinations, then add switch-case + * statements. + */ + if (LIM_IS_STA_ROLE(session_entry) && + (mac_hdr->fc.type == SIR_MAC_MGMT_FRAME) && + (mac_hdr->fc.subType == SIR_MAC_MGMT_ACTION)) + lim_process_action_frame(mac_ctx, rx_pkt_buffer, session_entry); +#endif + return; +} + +/** + * lim_check_mgmt_registered_frames() - This function handles registered + * management frames. + * + * @mac_ctx: Pointer to the Global Mac Context. + * @buff_desc: Pointer to the packet Buffer descriptor. + * @session_entry: Pointer to the PE Session Entry. + * + * This function is called to process to check if received frame match with + * any of the registered frame from HDD. If yes pass this frame to SME. + * + * Return: True or False for Match or Mismatch respectively. + */ +static bool +lim_check_mgmt_registered_frames(struct mac_context *mac_ctx, uint8_t *buff_desc, + struct pe_session *session_entry) +{ + tSirMacFrameCtl fc; + tpSirMacMgmtHdr hdr; + uint8_t *body; + struct mgmt_frm_reg_info *mgmt_frame = NULL; + struct mgmt_frm_reg_info *next_frm = NULL; + uint16_t frm_type; + uint16_t frm_len; + uint8_t type, sub_type; + bool match = false; + tpSirMacActionFrameHdr action_hdr; + uint8_t actionID, category; + QDF_STATUS qdf_status; + + hdr = WMA_GET_RX_MAC_HEADER(buff_desc); + fc = hdr->fc; + frm_type = (fc.type << 2) | (fc.subType << 4); + body = WMA_GET_RX_MPDU_DATA(buff_desc); + action_hdr = (tpSirMacActionFrameHdr)body; + frm_len = WMA_GET_RX_PAYLOAD_LEN(buff_desc); + + qdf_mutex_acquire(&mac_ctx->lim.lim_frame_register_lock); + qdf_list_peek_front(&mac_ctx->lim.gLimMgmtFrameRegistratinQueue, + (qdf_list_node_t **) &mgmt_frame); + qdf_mutex_release(&mac_ctx->lim.lim_frame_register_lock); + + while (mgmt_frame) { + type = (mgmt_frame->frameType >> 2) & 0x03; + sub_type = (mgmt_frame->frameType >> 4) & 0x0f; + if ((type == SIR_MAC_MGMT_FRAME) + && (fc.type == SIR_MAC_MGMT_FRAME) + && (sub_type == SIR_MAC_MGMT_RESERVED15)) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + FL + ("rcvd frm match for SIR_MAC_MGMT_RESERVED15")); + match = true; + break; + } + if (mgmt_frame->frameType == frm_type) { + if (mgmt_frame->matchLen <= 0) { + match = true; + break; + } + if (mgmt_frame->matchLen <= frm_len && + (!qdf_mem_cmp(mgmt_frame->matchData, body, + mgmt_frame->matchLen))) { + /* found match! */ + match = true; + break; + } + } + + qdf_mutex_acquire(&mac_ctx->lim.lim_frame_register_lock); + qdf_status = + qdf_list_peek_next( + &mac_ctx->lim.gLimMgmtFrameRegistratinQueue, + (qdf_list_node_t *) mgmt_frame, + (qdf_list_node_t **) &next_frm); + qdf_mutex_release(&mac_ctx->lim.lim_frame_register_lock); + mgmt_frame = next_frm; + next_frm = NULL; + } + if (match) { + pe_debug("rcvd frame match with registered frame params"); + + /* + * Drop BTM frame received on STA interface if concurrent + * P2P connection is active and p2p_disable_roam ini is + * enabled. This will help to avoid scan triggered by + * userspace after processing the BTM frame from AP so the + * audio glitches are not seen in P2P connection. + */ + if (cfg_p2p_is_roam_config_disabled(mac_ctx->psoc) && + session_entry && LIM_IS_STA_ROLE(session_entry) && + (policy_mgr_mode_specific_connection_count(mac_ctx->psoc, + PM_P2P_CLIENT_MODE, NULL) || + policy_mgr_mode_specific_connection_count(mac_ctx->psoc, + PM_P2P_GO_MODE, NULL))) { + if (frm_len >= sizeof(*action_hdr) && action_hdr && + fc.type == SIR_MAC_MGMT_FRAME && + fc.subType == SIR_MAC_MGMT_ACTION) { + actionID = action_hdr->actionID; + category = action_hdr->category; + if (category == ACTION_CATEGORY_WNM && + (actionID == WNM_BSS_TM_QUERY || + actionID == WNM_BSS_TM_REQUEST || + actionID == WNM_BSS_TM_RESPONSE)) { + pe_debug("p2p session active drop BTM frame"); + return match; + } + } + } + /* Indicate this to SME */ + lim_send_sme_mgmt_frame_ind(mac_ctx, hdr->fc.subType, + (uint8_t *) hdr, + WMA_GET_RX_PAYLOAD_LEN(buff_desc) + + sizeof(tSirMacMgmtHdr), mgmt_frame->sessionId, + WMA_GET_RX_FREQ(buff_desc), session_entry, + WMA_GET_RX_RSSI_NORMALIZED(buff_desc), + RXMGMT_FLAG_NONE); + + if ((type == SIR_MAC_MGMT_FRAME) + && (fc.type == SIR_MAC_MGMT_FRAME) + && (sub_type == SIR_MAC_MGMT_RESERVED15)) + /* These packets needs to be processed by PE/SME + * as well as HDD.If it returns true here, + * the packet is forwarded to HDD only. + */ + match = false; + } + + return match; +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +/** + * lim_is_mgmt_frame_loggable() - to log non-excessive mgmt frames + * @type: type of frames i.e. mgmt, control, data + * @subtype: subtype of frames i.e. beacon, probe rsp, probe req and etc + * + * This API tells if given mgmt frame is expected to come excessive in + * amount or not. + * + * Return: true if mgmt is expected to come not that often, so makes it + * loggable. false if mgmt is expected to come too often, so makes + * it not loggable + */ +static bool +lim_is_mgmt_frame_loggable(uint8_t type, uint8_t subtype) +{ + if (type != SIR_MAC_MGMT_FRAME) + return false; + + switch (subtype) { + case SIR_MAC_MGMT_BEACON: + case SIR_MAC_MGMT_PROBE_REQ: + case SIR_MAC_MGMT_PROBE_RSP: + return false; + default: + return true; + } +} +#else +static bool +lim_is_mgmt_frame_loggable(uint8_t type, uint8_t subtype) +{ + return false; +} +#endif + +/** + * lim_handle80211_frames() + * + ***FUNCTION: + * This function is called to process 802.11 frames + * received by LIM. + * + ***LOGIC: + * NA + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param pMsg of type struct scheduler_msg - Pointer to the message structure + * @return None + */ + +static void +lim_handle80211_frames(struct mac_context *mac, struct scheduler_msg *limMsg, + uint8_t *pDeferMsg) +{ + uint8_t *pRxPacketInfo = NULL; + tSirMacFrameCtl fc; + tpSirMacMgmtHdr pHdr = NULL; + struct pe_session *pe_session = NULL; + uint8_t sessionId; + bool isFrmFt = false; + uint32_t frequency; + bool is_hw_sbs_capable = false; + + *pDeferMsg = false; + lim_get_b_dfrom_rx_packet(mac, limMsg->bodyptr, + (uint32_t **) &pRxPacketInfo); + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + isFrmFt = WMA_GET_RX_FT_DONE(pRxPacketInfo); + frequency = WMA_GET_RX_FREQ(pRxPacketInfo); + fc = pHdr->fc; + + is_hw_sbs_capable = + policy_mgr_is_hw_sbs_capable(mac->psoc); + if (WLAN_REG_IS_5GHZ_CH_FREQ(frequency) && + (!is_hw_sbs_capable || + (is_hw_sbs_capable && + wlan_reg_is_dfs_for_freq(mac->pdev, frequency))) && + mac->sap.SapDfsInfo.is_dfs_cac_timer_running) { + pe_session = pe_find_session_by_bssid(mac, + pHdr->bssId, &sessionId); + if (pe_session && + (QDF_SAP_MODE == pe_session->opmode)) { + pe_debug("CAC timer running - drop the frame"); + goto end; + } + } + +#ifdef WLAN_DUMP_MGMTFRAMES + pe_debug("ProtVersion %d, Type %d, Subtype %d rateIndex=%d", + fc.protVer, fc.type, fc.subType, + WMA_GET_RX_MAC_RATE_IDX(pRxPacketInfo)); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, pHdr, + WMA_GET_RX_MPDU_HEADER_LEN(pRxPacketInfo)); +#endif + if (mac->mlme_cfg->gen.debug_packet_log & 0x1) { + if ((fc.type == SIR_MAC_MGMT_FRAME) && + (fc.subType != SIR_MAC_MGMT_PROBE_REQ) && + (fc.subType != SIR_MAC_MGMT_PROBE_RSP) && + (fc.subType != SIR_MAC_MGMT_BEACON)) { + pe_debug("RX MGMT - Type %hu, SubType %hu, seq num[%d]", + fc.type, + fc.subType, + ((pHdr->seqControl.seqNumHi << + HIGH_SEQ_NUM_OFFSET) | + pHdr->seqControl.seqNumLo)); + } + } +#ifdef FEATURE_WLAN_EXTSCAN + if (WMA_IS_EXTSCAN_SCAN_SRC(pRxPacketInfo) || + WMA_IS_EPNO_SCAN_SRC(pRxPacketInfo)) { + if (fc.subType == SIR_MAC_MGMT_BEACON || + fc.subType == SIR_MAC_MGMT_PROBE_RSP) { + __lim_process_ext_scan_beacon_probe_rsp(mac, + pRxPacketInfo, + fc.subType); + } else { + pe_err("Wrong frameType %d, Subtype %d for %d", + fc.type, fc.subType, + WMA_GET_SCAN_SRC(pRxPacketInfo)); + } + goto end; + } +#endif + /* Added For BT-AMP Support */ + pe_session = pe_find_session_by_bssid(mac, pHdr->bssId, + &sessionId); + if (!pe_session) { + if (fc.subType == SIR_MAC_MGMT_AUTH) { + pe_debug("ProtVersion %d, Type %d, Subtype %d rateIndex=%d", + fc.protVer, fc.type, fc.subType, + WMA_GET_RX_MAC_RATE_IDX(pRxPacketInfo)); + lim_print_mac_addr(mac, pHdr->bssId, LOGD); + if (lim_process_auth_frame_no_session + (mac, pRxPacketInfo, + limMsg->bodyptr) == QDF_STATUS_SUCCESS) { + goto end; + } + } + /* Public action frame can be received from non-assoc stations*/ + if ((fc.subType != SIR_MAC_MGMT_PROBE_RSP) && + (fc.subType != SIR_MAC_MGMT_BEACON) && + (fc.subType != SIR_MAC_MGMT_PROBE_REQ) + && (fc.subType != SIR_MAC_MGMT_ACTION)) { + + pe_session = pe_find_session_by_peer_sta(mac, + pHdr->sa, &sessionId); + if (!pe_session) { + pe_debug("session does not exist for bssId"); + lim_print_mac_addr(mac, pHdr->sa, LOGD); + goto end; + } else { + pe_debug("SessionId:%d exists for given Bssid", + pe_session->peSessionId); + } + } + /* For p2p resp frames search for valid session with DA as */ + /* BSSID will be SA and session will be present with DA only */ + if (fc.subType == SIR_MAC_MGMT_ACTION) { + pe_session = + pe_find_session_by_bssid(mac, pHdr->da, &sessionId); + } + } + + /* Check if frame is registered by HDD */ + if (lim_check_mgmt_registered_frames(mac, pRxPacketInfo, pe_session)) { + pe_debug("Received frame is passed to SME"); + goto end; + } + + if (fc.protVer != SIR_MAC_PROTOCOL_VERSION) { /* Received Frame with non-zero Protocol Version */ + pe_err("Unexpected frame with protVersion %d received", + fc.protVer); + lim_pkt_free(mac, TXRX_FRM_802_11_MGMT, pRxPacketInfo, + (void *)limMsg->bodyptr); +#ifdef WLAN_DEBUG + mac->lim.numProtErr++; +#endif + goto end; + } + +/* Chance of crashing : to be done BT-AMP ........happens when broadcast probe req is received */ + +#ifdef WLAN_DEBUG + mac->lim.numMAC[fc.type][fc.subType]++; +#endif + + switch (fc.type) { + case SIR_MAC_MGMT_FRAME: + { + /* Received Management frame */ + switch (fc.subType) { + case SIR_MAC_MGMT_ASSOC_REQ: + /* Make sure the role supports Association */ + if (LIM_IS_AP_ROLE(pe_session)) + lim_process_assoc_req_frame(mac, + pRxPacketInfo, + LIM_ASSOC, + pe_session); + else { + pe_err("unexpected message received %X", + limMsg->type); + lim_print_msg_name(mac, LOGE, + limMsg->type); + } + break; + + case SIR_MAC_MGMT_ASSOC_RSP: + lim_process_assoc_rsp_frame(mac, pRxPacketInfo, + LIM_ASSOC, + pe_session); + break; + + case SIR_MAC_MGMT_REASSOC_REQ: + /* Make sure the role supports Reassociation */ + if (LIM_IS_AP_ROLE(pe_session)) { + lim_process_assoc_req_frame(mac, + pRxPacketInfo, + LIM_REASSOC, + pe_session); + } else { + pe_err("unexpected message received %X", + limMsg->type); + lim_print_msg_name(mac, LOGE, + limMsg->type); + } + break; + + case SIR_MAC_MGMT_REASSOC_RSP: + lim_process_assoc_rsp_frame(mac, pRxPacketInfo, + LIM_REASSOC, + pe_session); + break; + + case SIR_MAC_MGMT_PROBE_REQ: + lim_process_probe_req_frame_multiple_bss(mac, + pRxPacketInfo, + pe_session); + break; + + case SIR_MAC_MGMT_PROBE_RSP: + if (pe_session) + lim_process_probe_rsp_frame(mac, + pRxPacketInfo, + pe_session); + break; + + case SIR_MAC_MGMT_BEACON: + __lim_handle_beacon(mac, limMsg, pe_session); + break; + + case SIR_MAC_MGMT_DISASSOC: + lim_process_disassoc_frame(mac, pRxPacketInfo, + pe_session); + break; + + case SIR_MAC_MGMT_AUTH: + lim_process_auth_frame(mac, pRxPacketInfo, + pe_session); + break; + + case SIR_MAC_MGMT_DEAUTH: + lim_process_deauth_frame(mac, pRxPacketInfo, + pe_session); + break; + + case SIR_MAC_MGMT_ACTION: + if (!pe_session) + lim_process_action_frame_no_session(mac, + pRxPacketInfo); + else { + if (WMA_GET_RX_UNKNOWN_UCAST + (pRxPacketInfo)) + lim_handle_unknown_a2_index_frames + (mac, pRxPacketInfo, + pe_session); + else + lim_process_action_frame(mac, + pRxPacketInfo, + pe_session); + } + break; + default: + /* Received Management frame of 'reserved' subtype */ + break; + } /* switch (fc.subType) */ + + } + break; + case SIR_MAC_DATA_FRAME: + { + } + break; + default: + /* Received frame of type 'reserved' */ + break; + + } /* switch (fc.type) */ + if (lim_is_mgmt_frame_loggable(fc.type, fc.subType)) + lim_diag_mgmt_rx_event_report(mac, pHdr, + pe_session, + QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); +end: + lim_pkt_free(mac, TXRX_FRM_802_11_MGMT, pRxPacketInfo, + (void *)limMsg->bodyptr); + return; +} /*** end lim_handle80211_frames() ***/ + +void lim_process_abort_scan_ind(struct mac_context *mac_ctx, + uint8_t vdev_id, uint32_t scan_id, uint32_t scan_requestor_id) +{ + QDF_STATUS status; + struct scan_cancel_request *req; + struct wlan_objmgr_vdev *vdev; + + pe_debug("scan_id %d, scan_requestor_id 0x%x", + scan_id, scan_requestor_id); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + mac_ctx->psoc, vdev_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) { + pe_debug("vdev is NULL"); + return; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + goto fail; + + req->vdev = vdev; + req->cancel_req.requester = scan_requestor_id; + req->cancel_req.scan_id = scan_id; + req->cancel_req.vdev_id = vdev_id; + req->cancel_req.req_type = WLAN_SCAN_CANCEL_SINGLE; + + status = ucfg_scan_cancel(req); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("Cancel scan request failed"); + +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); +} + +static void lim_process_sme_obss_scan_ind(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + struct pe_session *session; + uint8_t session_id; + struct sme_obss_ht40_scanind_msg *ht40_scanind; + + ht40_scanind = (struct sme_obss_ht40_scanind_msg *)msg->bodyptr; + session = pe_find_session_by_bssid(mac_ctx, + ht40_scanind->mac_addr.bytes, &session_id); + if (!session) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + "OBSS Scan not started: session id is NULL"); + return; + } + if (session->htSupportedChannelWidthSet == + WNI_CFG_CHANNEL_BONDING_MODE_ENABLE) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + "OBSS Scan Start Req: session id %d" + "htSupportedChannelWidthSet %d", + session->peSessionId, + session->htSupportedChannelWidthSet); + lim_send_ht40_obss_scanind(mac_ctx, session); + } else { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + "OBSS Scan not started: channel width - %d session %d", + session->htSupportedChannelWidthSet, + session->peSessionId); + } + return; +} + +static void +lim_process_vdev_delete(struct mac_context *mac_ctx, + struct del_vdev_params *vdev_param) +{ + tp_wma_handle wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + WMA_LOGE("%s: WMA context is invalid", __func__); + return; + } + wma_vdev_detach(wma_handle, vdev_param); +} + +/** + * lim_process_messages() - Process messages from upper layers. + * + * @mac_ctx: Pointer to the Global Mac Context. + * @msg: Received message. + * + * Return: None. + */ +static void lim_process_messages(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + uint8_t vdev_id = 0; + tUpdateBeaconParams beacon_params; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + uint8_t i; + struct pe_session *session_entry = NULL; + uint8_t defer_msg = false; + uint16_t pkt_len = 0; + cds_pkt_t *body_ptr = NULL; + QDF_STATUS qdf_status; + struct scheduler_msg new_msg = {0}; + + if (!msg) { + pe_err("Message pointer is Null"); + QDF_ASSERT(0); + return; + } + + if (ANI_DRIVER_TYPE(mac_ctx) == QDF_DRIVER_TYPE_MFG) { + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + return; + } + +#ifdef WLAN_DEBUG + mac_ctx->lim.numTot++; +#endif + /* + * MTRACE logs not captured for events received from SME + * SME enums (eWNI_SME_START_REQ) starts with 0x16xx. + * Compare received SME events with SIR_SME_MODULE_ID + */ + if ((SIR_SME_MODULE_ID == + (uint8_t)MAC_TRACE_GET_MODULE_ID(msg->type)) && + (msg->type != eWNI_SME_REGISTER_MGMT_FRAME_REQ)) { + MTRACE(mac_trace(mac_ctx, TRACE_CODE_RX_SME_MSG, + NO_SESSION, msg->type)); + } else { + /* + * Omitting below message types as these are too frequent + * and when crash happens we loose critical trace logs + * if these are also logged + */ + if (msg->type != SIR_BB_XPORT_MGMT_MSG && + msg->type != WMA_RX_SCAN_EVENT) + MTRACE(mac_trace_msg_rx(mac_ctx, NO_SESSION, + LIM_TRACE_MAKE_RXMSG(msg->type, + LIM_MSG_PROCESSED))); + } + + switch (msg->type) { + + case SIR_LIM_UPDATE_BEACON: + lim_update_beacon(mac_ctx); + break; +#ifdef ANI_SIR_IBSS_PEER_CACHING + case WMA_IBSS_STA_ADD: + lim_ibss_sta_add(mac_ctx, msg->bodyptr); + break; +#endif + case SIR_BB_XPORT_MGMT_MSG: + /* These messages are from Peer MAC entity. */ +#ifdef WLAN_DEBUG + mac_ctx->lim.numBbt++; +#endif + /* The original msg which we were deferring have the + * bodyPointer point to 'BD' instead of 'cds pkt'. If we + * don't make a copy of msg, then overwrite the + * msg->bodyPointer and next time when we try to + * process the msg, we will try to use 'BD' as + * 'cds Pkt' which will cause a crash + */ + if (!msg->bodyptr) { + pe_err("Message bodyptr is Null"); + QDF_ASSERT(0); + break; + } + qdf_mem_copy((uint8_t *) &new_msg, + (uint8_t *) msg, sizeof(struct scheduler_msg)); + body_ptr = (cds_pkt_t *) new_msg.bodyptr; + cds_pkt_get_packet_length(body_ptr, &pkt_len); + + qdf_status = wma_ds_peek_rx_packet_info(body_ptr, + (void **) &new_msg.bodyptr, false); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + lim_decrement_pending_mgmt_count(mac_ctx); + cds_pkt_return_packet(body_ptr); + break; + } + if (WMA_GET_ROAMCANDIDATEIND(new_msg.bodyptr)) + pe_debug("roamCandidateInd: %d", + WMA_GET_ROAMCANDIDATEIND(new_msg.bodyptr)); + if (WMA_GET_OFFLOADSCANLEARN(new_msg.bodyptr)) + pe_debug("offloadScanLearn: %d", + WMA_GET_OFFLOADSCANLEARN(new_msg.bodyptr)); + + lim_handle80211_frames(mac_ctx, &new_msg, &defer_msg); + + if (defer_msg == true) { + QDF_TRACE(QDF_MODULE_ID_PE, LOGD, + FL("Defer Msg type=%x"), msg->type); + if (lim_defer_msg(mac_ctx, msg) != TX_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_PE, LOGE, + FL("Unable to Defer Msg")); + lim_log_session_states(mac_ctx); + lim_decrement_pending_mgmt_count(mac_ctx); + cds_pkt_return_packet(body_ptr); + } + } else { + /* PE is not deferring this 802.11 frame so we need to + * call cds_pkt_return. Asumption here is when Rx mgmt + * frame processing is done, cds packet could be + * freed here. + */ + lim_decrement_pending_mgmt_count(mac_ctx); + cds_pkt_return_packet(body_ptr); + } + break; + case eWNI_SME_DISASSOC_REQ: + case eWNI_SME_DEAUTH_REQ: +#ifdef FEATURE_WLAN_TDLS + case eWNI_SME_TDLS_SEND_MGMT_REQ: + case eWNI_SME_TDLS_ADD_STA_REQ: + case eWNI_SME_TDLS_DEL_STA_REQ: + case eWNI_SME_TDLS_LINK_ESTABLISH_REQ: +#endif + case eWNI_SME_RESET_AP_CAPS_CHANGED: + case eWNI_SME_SET_HW_MODE_REQ: + case eWNI_SME_SET_DUAL_MAC_CFG_REQ: + case eWNI_SME_SET_ANTENNA_MODE_REQ: + case eWNI_SME_UPDATE_ACCESS_POLICY_VENDOR_IE: + case eWNI_SME_UPDATE_CONFIG: + /* These messages are from HDD. Need to respond to HDD */ + lim_process_normal_hdd_msg(mac_ctx, msg, true); + break; + case eWNI_SME_SEND_DISASSOC_FRAME: + /* Need to response to hdd */ + lim_process_normal_hdd_msg(mac_ctx, msg, true); + break; + case eWNI_SME_PDEV_SET_HT_VHT_IE: + case eWNI_SME_SET_VDEV_IES_PER_BAND: + case eWNI_SME_SYS_READY_IND: + case eWNI_SME_JOIN_REQ: + case eWNI_SME_REASSOC_REQ: + case eWNI_SME_START_BSS_REQ: + case eWNI_SME_STOP_BSS_REQ: + case eWNI_SME_SWITCH_CHL_IND: + case eWNI_SME_DISASSOC_CNF: + case eWNI_SME_DEAUTH_CNF: + case eWNI_SME_ASSOC_CNF: + case eWNI_SME_ADDTS_REQ: + case eWNI_SME_DELTS_REQ: + case eWNI_SME_SESSION_UPDATE_PARAM: + case eWNI_SME_CHNG_MCC_BEACON_INTERVAL: + case eWNI_SME_NEIGHBOR_REPORT_REQ_IND: + case eWNI_SME_BEACON_REPORT_RESP_XMIT_IND: +#if defined FEATURE_WLAN_ESE + case eWNI_SME_ESE_ADJACENT_AP_REPORT: +#endif + case eWNI_SME_FT_PRE_AUTH_REQ: + case eWNI_SME_FT_AGGR_QOS_REQ: + case eWNI_SME_REGISTER_MGMT_FRAME_REQ: +#ifdef FEATURE_WLAN_ESE + case eWNI_SME_GET_TSM_STATS_REQ: +#endif /* FEATURE_WLAN_ESE */ + case eWNI_SME_REGISTER_MGMT_FRAME_CB: + case eWNI_SME_EXT_CHANGE_CHANNEL: + case eWNI_SME_ROAM_INVOKE: + /* fall through */ + case eWNI_SME_ROAM_SCAN_OFFLOAD_REQ: + case eWNI_SME_ROAM_INIT_PARAM: + case eWNI_SME_ROAM_SEND_PER_REQ: + case eWNI_SME_SET_ADDBA_ACCEPT: + case eWNI_SME_UPDATE_EDCA_PROFILE: + case WNI_SME_UPDATE_MU_EDCA_PARAMS: + case eWNI_SME_UPDATE_SESSION_EDCA_TXQ_PARAMS: + case WNI_SME_CFG_ACTION_FRM_HE_TB_PPDU: + case WNI_SME_REGISTER_BCN_REPORT_SEND_CB: + /* These messages are from HDD.No need to respond to HDD */ + lim_process_normal_hdd_msg(mac_ctx, msg, false); + break; + case eWNI_SME_SEND_MGMT_FRAME_TX: + lim_send_mgmt_frame_tx(mac_ctx, msg); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_MON_INIT_SESSION: + lim_mon_init_session(mac_ctx, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_MON_DEINIT_SESSION: + lim_mon_deinit_session(mac_ctx, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + break; + case SIR_HAL_P2P_NOA_ATTR_IND: + session_entry = &mac_ctx->lim.gpSession[0]; + pe_debug("Received message Noa_ATTR %x", + msg->type); + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + session_entry = &mac_ctx->lim.gpSession[i]; + if ((session_entry) && (session_entry->valid) && + (session_entry->opmode == QDF_P2P_GO_MODE)) { + /* Save P2P attr for Go */ + qdf_mem_copy(&session_entry->p2pGoPsUpdate, + msg->bodyptr, + sizeof(tSirP2PNoaAttr)); + pe_debug("bssId" + QDF_MAC_ADDR_FMT + " ctWin=%d oppPsFlag=%d", + QDF_MAC_ADDR_REF(session_entry->bssId), + session_entry->p2pGoPsUpdate.ctWin, + session_entry->p2pGoPsUpdate.oppPsFlag); + pe_debug("uNoa1IntervalCnt=%d uNoa1Duration=%d uNoa1Interval=%d uNoa1StartTime=%d", + session_entry->p2pGoPsUpdate.uNoa1IntervalCnt, + session_entry->p2pGoPsUpdate.uNoa1Duration, + session_entry->p2pGoPsUpdate.uNoa1Interval, + session_entry->p2pGoPsUpdate.uNoa1StartTime); + break; + } + } + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + break; + case WMA_MISSED_BEACON_IND: + lim_ps_offload_handle_missed_beacon_ind(mac_ctx, msg); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + break; + case SIR_LIM_ADDTS_RSP_TIMEOUT: + lim_process_sme_req_messages(mac_ctx, msg); + break; +#ifdef FEATURE_WLAN_ESE + case WMA_TSM_STATS_RSP: + lim_send_sme_pe_ese_tsm_rsp(mac_ctx, + (tAniGetTsmStatsRsp *) msg->bodyptr); + break; +#endif + case WMA_ADD_TS_RSP: + lim_process_hal_add_ts_rsp(mac_ctx, msg); + break; + case SIR_LIM_BEACON_GEN_IND: + if (mac_ctx->lim.gLimSystemRole != eLIM_AP_ROLE) + sch_process_pre_beacon_ind(mac_ctx, + msg, REASON_DEFAULT); + break; + case SIR_LIM_DELETE_STA_CONTEXT_IND: + lim_delete_sta_context(mac_ctx, msg); + break; + case SIR_LIM_RX_INVALID_PEER: + lim_rx_invalid_peer_process(mac_ctx, msg); + break; + case SIR_HAL_REQ_SEND_DELBA_REQ_IND: + lim_req_send_delba_ind_process(mac_ctx, msg); + break; + case SIR_LIM_JOIN_FAIL_TIMEOUT: + case SIR_LIM_PERIODIC_JOIN_PROBE_REQ_TIMEOUT: + case SIR_LIM_AUTH_FAIL_TIMEOUT: + case SIR_LIM_AUTH_RSP_TIMEOUT: + case SIR_LIM_ASSOC_FAIL_TIMEOUT: + case SIR_LIM_REASSOC_FAIL_TIMEOUT: + case SIR_LIM_FT_PREAUTH_RSP_TIMEOUT: + case SIR_LIM_DISASSOC_ACK_TIMEOUT: + case SIR_LIM_DEAUTH_ACK_TIMEOUT: + case SIR_LIM_AUTH_RETRY_TIMEOUT: + case SIR_LIM_AUTH_SAE_TIMEOUT: + /* These timeout messages are handled by MLM sub module */ + lim_process_mlm_req_messages(mac_ctx, msg); + break; + case SIR_LIM_HEART_BEAT_TIMEOUT: + /** check if heart beat failed, even if one Beacon + * is rcvd within the Heart Beat interval continue + * normal processing + */ + if (!msg->bodyptr) + pe_err("Can't Process HB TO - bodyptr is Null"); + else { + session_entry = (struct pe_session *) msg->bodyptr; + pe_err("SIR_LIM_HEART_BEAT_TIMEOUT, Session %d", + ((struct pe_session *) msg->bodyptr)->peSessionId); + limResetHBPktCount(session_entry); + lim_handle_heart_beat_timeout_for_session(mac_ctx, + session_entry); + } + break; + case SIR_LIM_PROBE_HB_FAILURE_TIMEOUT: + lim_handle_heart_beat_failure_timeout(mac_ctx); + break; + case SIR_LIM_CNF_WAIT_TIMEOUT: + /* Does not receive CNF or dummy packet */ + lim_handle_cnf_wait_timeout(mac_ctx, (uint16_t) msg->bodyval); + break; + case SIR_LIM_CHANNEL_SWITCH_TIMEOUT: + lim_process_channel_switch_timeout(mac_ctx); + break; + case SIR_LIM_UPDATE_OLBC_CACHEL_TIMEOUT: + lim_handle_update_olbc_cache(mac_ctx); + break; + case WMA_ADD_STA_RSP: + lim_process_add_sta_rsp(mac_ctx, msg); + break; + case WMA_DELETE_STA_RSP: + lim_process_mlm_del_sta_rsp(mac_ctx, msg); + break; + case WMA_DELETE_BSS_RSP: + case WMA_DELETE_BSS_HO_FAIL_RSP: + lim_handle_delete_bss_rsp(mac_ctx, msg->bodyptr); + break; + case WMA_CSA_OFFLOAD_EVENT: + lim_handle_csa_offload_msg(mac_ctx, msg); + break; + case WMA_SET_BSSKEY_RSP: + case WMA_SET_STA_BCASTKEY_RSP: + lim_process_mlm_set_bss_key_rsp(mac_ctx, msg); + break; + case WMA_SET_STAKEY_RSP: + lim_process_mlm_set_sta_key_rsp(mac_ctx, msg); + break; + case WMA_SET_MIMOPS_RSP: + case WMA_SET_TX_POWER_RSP: + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case WMA_SET_MAX_TX_POWER_RSP: + rrm_set_max_tx_power_rsp(mac_ctx, msg); + if (msg->bodyptr) { + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + } + break; + case SIR_LIM_ADDR2_MISS_IND: + pe_err( + FL("Addr2 mismatch interrupt received %X"), msg->type); + /* message from HAL indicating addr2 mismatch interrupt occurred + * msg->bodyptr contains only pointer to 48-bit addr2 field + */ + qdf_mem_free((void *)(msg->bodyptr)); + msg->bodyptr = NULL; + break; + case WMA_AGGR_QOS_RSP: + lim_process_ft_aggr_qos_rsp(mac_ctx, msg); + break; + case WMA_RX_CHN_STATUS_EVENT: + lim_process_rx_channel_status_event(mac_ctx, msg->bodyptr); + break; + case WMA_IBSS_PEER_INACTIVITY_IND: + lim_process_ibss_peer_inactivity(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)(msg->bodyptr)); + msg->bodyptr = NULL; + break; + case WMA_DFS_BEACON_TX_SUCCESS_IND: + lim_process_beacon_tx_success_ind(mac_ctx, msg->type, + (void *)msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case WMA_DISASSOC_TX_COMP: + lim_disassoc_tx_complete_cnf(mac_ctx, msg->bodyval, + msg->bodyptr); + break; + case WMA_DEAUTH_TX_COMP: + lim_deauth_tx_complete_cnf(mac_ctx, msg->bodyval, + msg->bodyptr); + break; +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + case WMA_UPDATE_Q2Q_IE_IND: + qdf_mem_zero(&beacon_params, sizeof(tUpdateBeaconParams)); + beacon_params.paramChangeBitmap = 0; + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + vdev_id = ((uint8_t *)msg->bodyptr)[i]; + session_entry = pe_find_session_by_vdev_id(mac_ctx, + vdev_id); + if (!session_entry) + continue; + session_entry->sap_advertise_avoid_ch_ie = + (uint8_t)msg->bodyval; + /* + * if message comes for DFS channel, no need to update: + * 1) We wont have MCC with DFS channels. so no need to + * add Q2Q IE + * 2) We cannot end up in DFS channel SCC by channel + * switch from non DFS MCC scenario, so no need to + * remove Q2Q IE + * 3) There is however a case where device start MCC and + * then user modifies hostapd.conf and does SAP + * restart, in such a case, beacon params will be + * reset and thus will not contain Q2Q IE, by default + */ + if (wlan_reg_get_channel_state( + mac_ctx->pdev, + wlan_reg_freq_to_chan( + mac_ctx->pdev, session_entry->curr_op_freq)) + != CHANNEL_STATE_DFS) { + beacon_params.bss_idx = session_entry->vdev_id; + beacon_params.beaconInterval = + session_entry->beaconParams.beaconInterval; + beacon_params.paramChangeBitmap |= + PARAM_BCN_INTERVAL_CHANGED; + sch_set_fixed_beacon_fields(mac_ctx, + session_entry); + lim_send_beacon_params(mac_ctx, &beacon_params, + session_entry); + } + } + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + break; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + case eWNI_SME_NSS_UPDATE_REQ: + case eWNI_SME_DFS_BEACON_CHAN_SW_IE_REQ: + lim_process_sme_req_messages(mac_ctx, msg); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_CHANNEL_CHANGE_REQ: + lim_process_sme_req_messages(mac_ctx, msg); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_START_BEACON_REQ: + lim_process_sme_req_messages(mac_ctx, msg); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_UPDATE_ADDITIONAL_IES: + lim_process_sme_req_messages(mac_ctx, msg); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_MODIFY_ADDITIONAL_IES: + lim_process_sme_req_messages(mac_ctx, msg); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; +#ifdef QCA_HT_2040_COEX + case eWNI_SME_SET_HT_2040_MODE: + lim_process_sme_req_messages(mac_ctx, msg); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; +#endif + case SIR_HAL_PDEV_SET_HW_MODE_RESP: + lim_process_set_hw_mode_resp(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case SIR_HAL_PDEV_HW_MODE_TRANS_IND: + lim_process_hw_mode_trans_ind(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case SIR_HAL_PDEV_MAC_CFG_RESP: + lim_process_dual_mac_cfg_resp(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_SET_IE_REQ: + lim_process_sme_req_messages(mac_ctx, msg); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_HT40_OBSS_SCAN_IND: + lim_process_sme_obss_scan_ind(mac_ctx, msg); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_SOC_ANTENNA_MODE_RESP: + lim_process_set_antenna_resp(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_DEFAULT_SCAN_IE: + lim_process_set_default_scan_ie_request(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_SET_HE_BSS_COLOR: + lim_process_set_he_bss_color(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_DEL_ALL_TDLS_PEERS: + lim_process_sme_del_all_tdls_peers(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case WMA_OBSS_DETECTION_INFO: + lim_process_obss_detection_ind(mac_ctx, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_SEND_SAE_MSG: + lim_process_sae_msg(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case WMA_OBSS_COLOR_COLLISION_INFO: + lim_process_obss_color_collision_info(mac_ctx, msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_CSA_RESTART_REQ: + lim_send_csa_restart_req(mac_ctx, msg->bodyval); + break; + case eWNI_SME_STA_CSA_CONTINUE_REQ: + lim_continue_sta_csa_req(mac_ctx, msg->bodyval); + break; + case WMA_SEND_BCN_RSP: + lim_send_bcn_rsp(mac_ctx, (tpSendbeaconParams)msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case WMA_ROAM_BLACKLIST_MSG: + lim_add_roam_blacklist_ap(mac_ctx, + (struct roam_blacklist_event *) + msg->bodyptr); + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + break; + case eWNI_SME_VDEV_DELETE_REQ: + lim_process_vdev_delete(mac_ctx, msg->bodyptr); + /* Do not free msg->bodyptr, same memory used to send resp */ + msg->bodyptr = NULL; + break; + case SIR_LIM_PROCESS_DEFERRED_QUEUE: + break; + case eWNI_SME_ABORT_CONN_TIMER: + lim_deactivate_timers_for_vdev(mac_ctx, msg->bodyval); + break; + default: + qdf_mem_free((void *)msg->bodyptr); + msg->bodyptr = NULL; + pe_debug("Discarding unexpected message received %X", + msg->type); + lim_print_msg_name(mac_ctx, LOGE, msg->type); + break; + + } /* switch (msg->type) */ +} /*** end lim_process_messages() ***/ + +/** + * lim_process_deferred_message_queue() + * + * This function is called by LIM while exiting from Learn + * mode. This function fetches messages posted to the LIM + * deferred message queue limDeferredMsgQ. + * + * @mac: Pointer to Global MAC structure + * @return None + */ + +static void lim_process_deferred_message_queue(struct mac_context *mac) +{ + struct scheduler_msg limMsg = {0}; + struct scheduler_msg *readMsg; + uint16_t size; + + /* + ** check any deferred messages need to be processed + **/ + size = mac->lim.gLimDeferredMsgQ.size; + if (size > 0) { + while ((readMsg = lim_read_deferred_msg_q(mac)) != NULL) { + qdf_mem_copy((uint8_t *) &limMsg, + (uint8_t *) readMsg, + sizeof(struct scheduler_msg)); + size--; + lim_process_messages(mac, &limMsg); + + if (!GET_LIM_PROCESS_DEFD_MESGS(mac) || + mac->lim.gLimAddtsSent) + break; + } + } +} /*** end lim_process_deferred_message_queue() ***/ + +/** + * lim_message_processor() - Process messages from LIM. + * @mac_ctx: Pointer to the Global Mac Context. + * @msg: Received LIM message. + * + * Wrapper function for lim_process_messages when handling messages received by + * LIM. Could either defer messages or process them. + * + * Return: None. + */ +void lim_message_processor(struct mac_context *mac_ctx, struct scheduler_msg *msg) +{ + if (eLIM_MLM_OFFLINE_STATE == mac_ctx->lim.gLimMlmState) { + pe_free_msg(mac_ctx, msg); + return; + } + + if (!def_msg_decision(mac_ctx, msg)) { + lim_process_messages(mac_ctx, msg); + /* process deferred message queue if allowed */ + if ((!(mac_ctx->lim.gLimAddtsSent)) && + GET_LIM_PROCESS_DEFD_MESGS(mac_ctx)) + lim_process_deferred_message_queue(mac_ctx); + } +} + +/** + * lim_process_normal_hdd_msg() - Process the message and defer if needed + * @mac_ctx : Pointer to Global MAC structure + * @msg : The message need to be processed + * @rsp_reqd: whether return result to hdd + * + * This function checks the current lim state and decide whether the message + * passed will be deferred or not. + * + * Return: None + */ +static void lim_process_normal_hdd_msg(struct mac_context *mac_ctx, + struct scheduler_msg *msg, + uint8_t rsp_reqd) +{ + bool defer_msg = true; + + /* Added For BT-AMP Support */ + if ((mac_ctx->lim.gLimSystemRole == eLIM_AP_ROLE) + || (mac_ctx->lim.gLimSystemRole == eLIM_UNKNOWN_ROLE)) { + /* + * This check is required only for the AP and in 2 cases. + * 1. If we are in learn mode and we receive any of these + * messages, you have to come out of scan and process the + * message, hence dont defer the message here. In handler, + * these message could be defered till we actually come out of + * scan mode. + * 2. If radar is detected, you might have to defer all of + * these messages except Stop BSS request/ Switch channel + * request. This decision is also made inside its handler. + * + * Please be careful while using the flag defer_msg. Possibly + * you might end up in an infinite loop. + */ + if ((msg->type == eWNI_SME_START_BSS_REQ) || + (msg->type == eWNI_SME_STOP_BSS_REQ) || + (msg->type == eWNI_SME_SWITCH_CHL_IND)) + defer_msg = false; + } + + if (mac_ctx->lim.gLimAddtsSent && defer_msg) { + /* + * System is in DFS (Learn) mode or awaiting addts response or + * if radar is detected, Defer processing this message + */ + if (lim_defer_msg(mac_ctx, msg) != TX_SUCCESS) { +#ifdef WLAN_DEBUG + mac_ctx->lim.numSme++; +#endif + lim_log_session_states(mac_ctx); + /* Release body */ + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + } + } else { + /* + * These messages are from HDD.Since these requests may also be + * generated internally within LIM module, need to distinquish + * and send response to host + */ + if (rsp_reqd) + mac_ctx->lim.gLimRspReqd = true; +#ifdef WLAN_DEBUG + mac_ctx->lim.numSme++; +#endif + if (lim_process_sme_req_messages(mac_ctx, msg)) { + /* + * Release body. limProcessSmeReqMessage consumed the + * buffer. We can free it. + */ + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + } + } +} + +void +handle_ht_capabilityand_ht_info(struct mac_context *mac, + struct pe_session *pe_session) +{ + struct mlme_ht_capabilities_info *ht_cap_info; + + ht_cap_info = &mac->mlme_cfg->ht_caps.ht_cap_info; + mac->lim.gHTLsigTXOPProtection = + (uint8_t)ht_cap_info->l_sig_tx_op_protection; + mac->lim.gHTMIMOPSState = + (tSirMacHTMIMOPowerSaveState)ht_cap_info->mimo_power_save; + mac->lim.gHTGreenfield = (uint8_t)ht_cap_info->green_field; + mac->lim.gHTMaxAmsduLength = + (uint8_t)ht_cap_info->maximal_amsdu_size; + mac->lim.gHTShortGI20Mhz = (uint8_t)ht_cap_info->short_gi_20_mhz; + mac->lim.gHTShortGI40Mhz = (uint8_t)ht_cap_info->short_gi_40_mhz; + mac->lim.gHTPSMPSupport = (uint8_t)ht_cap_info->psmp; + mac->lim.gHTDsssCckRate40MHzSupport = + (uint8_t)ht_cap_info->dsss_cck_mode_40_mhz; + + mac->lim.gHTAMpduDensity = + (uint8_t)mac->mlme_cfg->ht_caps.ampdu_params.mpdu_density; + mac->lim.gHTMaxRxAMpduFactor = + (uint8_t)mac->mlme_cfg->ht_caps.ampdu_params. + max_rx_ampdu_factor; + + /* Get HT IE Info */ + mac->lim.gHTServiceIntervalGranularity = + (uint8_t)mac->mlme_cfg->ht_caps.info_field_1. + service_interval_granularity; + mac->lim.gHTControlledAccessOnly = + (uint8_t)mac->mlme_cfg->ht_caps.info_field_1. + controlled_access_only; + + mac->lim.gHTOperMode = (tSirMacHTOperatingMode)mac->mlme_cfg->ht_caps. + info_field_2.op_mode; + + mac->lim.gHTPCOActive = (uint8_t)mac->mlme_cfg->ht_caps.info_field_3. + pco_active; + mac->lim.gHTPCOPhase = (uint8_t)mac->mlme_cfg->ht_caps.info_field_3. + pco_phase; + mac->lim.gHTSecondaryBeacon = + (uint8_t)mac->mlme_cfg->ht_caps.info_field_3.secondary_beacon; + mac->lim.gHTDualCTSProtection = (uint8_t)mac->mlme_cfg->ht_caps. + info_field_3.dual_cts_protection; + mac->lim.gHTSTBCBasicMCS = (uint8_t)mac->mlme_cfg->ht_caps. + info_field_3.basic_stbc_mcs; + + /* The lim globals for channelwidth and secondary chnl have been removed and should not be used during no session; + * instead direct cfg is read and used when no session for transmission of mgmt frames (same as old); + * For now, we might come here during init and join with pe_session = NULL; in that case just fill the globals which exist + * Sessionized entries values will be filled in join or add bss req. The ones which are missed in join are filled below + */ + if (pe_session) { + pe_session->htCapability = + IS_DOT11_MODE_HT(pe_session->dot11mode); + pe_session->beaconParams.fLsigTXOPProtectionFullSupport = + (uint8_t)mac->mlme_cfg->ht_caps.info_field_3. + lsig_txop_protection_full_support; + lim_init_obss_params(mac, pe_session); + } +} + +void lim_log_session_states(struct mac_context *mac_ctx) +{ +#ifdef WLAN_DEBUG + int i; + + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + if (mac_ctx->lim.gpSession[i].valid) { + QDF_TRACE(QDF_MODULE_ID_PE, LOGD, + FL("sysRole(%d) Session (%d)"), + mac_ctx->lim.gLimSystemRole, i); + QDF_TRACE(QDF_MODULE_ID_PE, LOGD, + FL("SME: Curr %s,Prev %s,MLM: Curr %s,Prev %s"), + lim_sme_state_str( + mac_ctx->lim.gpSession[i].limSmeState), + lim_sme_state_str( + mac_ctx->lim.gpSession[i].limPrevSmeState), + lim_mlm_state_str( + mac_ctx->lim.gpSession[i].limMlmState), + lim_mlm_state_str( + mac_ctx->lim.gpSession[i].limPrevMlmState)); + } + } +#endif +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_mlm_host_roam.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_mlm_host_roam.c new file mode 100644 index 0000000000000000000000000000000000000000..e7ab7994f0166652344f3d91e41c4968a55cd87f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_mlm_host_roam.c @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: lim_process_mlm_host_roam.c + * + * Host based roaming MLM implementation + */ +#include "cds_api.h" +#include "wni_cfg.h" +#include "ani_global.h" +#include "sir_api.h" +#include "sir_params.h" + +#include "sch_api.h" +#include "utils_api.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_prop_exts_utils.h" +#include "lim_security_utils.h" +#include "lim_send_messages.h" +#include "lim_send_messages.h" +#include "lim_session_utils.h" +#include +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM +#include "host_diag_core_log.h" +#endif +#include "wma_if.h" +#include "rrm_api.h" +#include "wma.h" + +static void lim_handle_sme_reaasoc_result(struct mac_context *, tSirResultCodes, + uint16_t, struct pe_session *); +/** + * lim_process_mlm_reassoc_req() - process mlm reassoc request. + * + * @mac_ctx: pointer to Global MAC structure + * @msg: pointer to the MLM message buffer + * + * This function is called to process MLM_REASSOC_REQ message + * from SME + * + * Return: None + */ +void lim_process_mlm_reassoc_req(struct mac_context *mac_ctx, + tLimMlmReassocReq *reassoc_req) +{ + struct tLimPreAuthNode *auth_node; + tLimMlmReassocCnf reassoc_cnf; + struct pe_session *session; + + session = pe_find_session_by_session_id(mac_ctx, + reassoc_req->sessionId); + if (!session) { + pe_err("Session Does not exist for given sessionId: %d", + reassoc_req->sessionId); + qdf_mem_free(reassoc_req); + return; + } + + pe_debug("ReAssoc Req on session: %d role: %d mlm: %d " QDF_MAC_ADDR_FMT, + reassoc_req->sessionId, GET_LIM_SYSTEM_ROLE(session), + session->limMlmState, + QDF_MAC_ADDR_REF(reassoc_req->peerMacAddr)); + + if (LIM_IS_AP_ROLE(session) || + (session->limMlmState != + eLIM_MLM_LINK_ESTABLISHED_STATE)) { + /* + * Received Reassoc request in invalid state or + * in AP role.Return Reassoc confirm with Invalid + * parameters code. + */ + + pe_warn("unexpect msg state: %X role: %d MAC" QDF_MAC_ADDR_FMT, + session->limMlmState, GET_LIM_SYSTEM_ROLE(session), + QDF_MAC_ADDR_REF(reassoc_req->peerMacAddr)); + lim_print_mlm_state(mac_ctx, LOGW, session->limMlmState); + reassoc_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + reassoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + goto end; + } + + if (session->pLimMlmReassocReq) + qdf_mem_free(session->pLimMlmReassocReq); + + /* + * Hold Re-Assoc request as part of Session, knock-out mac_ctx + * Hold onto Reassoc request parameters + */ + session->pLimMlmReassocReq = reassoc_req; + + /* See if we have pre-auth context with new AP */ + auth_node = lim_search_pre_auth_list(mac_ctx, session->limReAssocbssId); + + if (!auth_node && (qdf_mem_cmp(reassoc_req->peerMacAddr, + session->bssId, + sizeof(tSirMacAddr)))) { + /* + * Either pre-auth context does not exist AND + * we are not reassociating with currently + * associated AP. + * Return Reassoc confirm with not authenticated + */ + reassoc_cnf.resultCode = eSIR_SME_STA_NOT_AUTHENTICATED; + reassoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + + goto end; + } + /* assign the sessionId to the timer object */ + mac_ctx->lim.lim_timers.gLimReassocFailureTimer.sessionId = + reassoc_req->sessionId; + session->limPrevMlmState = session->limMlmState; + session->limMlmState = eLIM_MLM_WT_REASSOC_RSP_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, session->peSessionId, + session->limMlmState)); + + /* Apply previously set configuration at HW */ + lim_apply_configuration(mac_ctx, session); + + /* store the channel switch reason code in the lim global var */ + session->channelChangeReasonCode = LIM_SWITCH_CHANNEL_REASSOC; + mlme_set_chan_switch_in_progress(session->vdev, true); + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_FW_VDEV_RESTART, + sizeof(struct pe_session), session); + return; +end: + reassoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + /* Update PE sessio Id */ + reassoc_cnf.sessionId = reassoc_req->sessionId; + /* Free up buffer allocated for reassocReq */ + qdf_mem_free(reassoc_req); + session->pLimReAssocReq = NULL; + lim_post_sme_message(mac_ctx, LIM_MLM_REASSOC_CNF, + (uint32_t *) &reassoc_cnf); +} + +/** + * lim_handle_sme_reaasoc_result() - Handle the reassoc result + * @mac: Global MAC Context + * @resultCode: Result code + * @protStatusCode: Protocol Status Code + * @pe_session: PE Session + * + * This function is called to process reassoc failures + * upon receiving REASSOC_CNF with a failure code or + * MLM_REASSOC_CNF with a success code in case of STA role + * + * Return: None + */ +static void lim_handle_sme_reaasoc_result(struct mac_context *mac, + tSirResultCodes resultCode, uint16_t protStatusCode, + struct pe_session *pe_session) +{ + tpDphHashNode sta = NULL; + uint8_t smesessionId; + + if (!pe_session) { + pe_err("pe_session is NULL"); + return; + } + smesessionId = pe_session->smeSessionId; + if (resultCode != eSIR_SME_SUCCESS) { + sta = + dph_get_hash_entry(mac, DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + if (sta) { + sta->mlmStaContext.disassocReason = + eSIR_MAC_UNSPEC_FAILURE_REASON; + sta->mlmStaContext.cleanupTrigger = + eLIM_JOIN_FAILURE; + sta->mlmStaContext.resultCode = resultCode; + sta->mlmStaContext.protStatusCode = protStatusCode; + lim_cleanup_rx_path(mac, sta, pe_session); + /* Cleanup if add bss failed */ + if (pe_session->add_bss_failed) { + dph_delete_hash_entry(mac, + sta->staAddr, sta->assocId, + &pe_session->dph.dphHashTable); + goto error; + } + return; + } + } +error: + /* Delete the session if REASSOC failure occurred. */ + if (resultCode != eSIR_SME_SUCCESS) { + if (pe_session) { + pe_delete_session(mac, pe_session); + pe_session = NULL; + } + } + lim_send_sme_join_reassoc_rsp(mac, eWNI_SME_REASSOC_RSP, resultCode, + protStatusCode, pe_session, smesessionId); +} + +/** + * lim_process_mlm_reassoc_cnf() - process mlm reassoc cnf msg + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: A pointer to the MLM message buffer + * + * This function is called to process MLM_REASSOC_CNF message from MLM State + * machine. + * + * @Return: void + */ +void lim_process_mlm_reassoc_cnf(struct mac_context *mac_ctx, uint32_t *msg_buf) +{ + struct pe_session *session; + tLimMlmReassocCnf *lim_mlm_reassoc_cnf; + struct reassoc_params param; + QDF_STATUS status; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + lim_mlm_reassoc_cnf = (tLimMlmReassocCnf *)msg_buf; + session = pe_find_session_by_session_id( + mac_ctx, + lim_mlm_reassoc_cnf->sessionId); + if (!session) { + pe_err("session Does not exist for given session Id"); + return; + } + if (session->limSmeState != eLIM_SME_WT_REASSOC_STATE || + LIM_IS_AP_ROLE(session)) { + /* + * Should not have received Reassocication confirm + * from MLM in other states OR on AP. + */ + pe_err("Rcv unexpected MLM_REASSOC_CNF role: %d sme 0x%X", + GET_LIM_SYSTEM_ROLE(session), session->limSmeState); + return; + } + + /* + * Upon Reassoc success or failure, freeup the cached preauth request, + * to ensure that channel switch is now allowed following any change in + * HT params. + */ + if (session->ftPEContext.pFTPreAuthReq) { + pe_debug("Freeing pFTPreAuthReq: %pK", + session->ftPEContext.pFTPreAuthReq); + if (session->ftPEContext.pFTPreAuthReq->pbssDescription) { + qdf_mem_free( + session->ftPEContext.pFTPreAuthReq->pbssDescription); + session->ftPEContext.pFTPreAuthReq->pbssDescription = + NULL; + } + qdf_mem_free(session->ftPEContext.pFTPreAuthReq); + session->ftPEContext.pFTPreAuthReq = NULL; + session->ftPEContext.ftPreAuthSession = false; + } + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (session->bRoamSynchInProgress) { + pe_debug("LFR3:Re-set the LIM Ctxt Roam Synch In Progress"); + session->bRoamSynchInProgress = false; + } +#endif + + pe_debug("Rcv MLM_REASSOC_CNF with result code: %d", + lim_mlm_reassoc_cnf->resultCode); + if (lim_mlm_reassoc_cnf->resultCode == eSIR_SME_SUCCESS) { + /* Successful Reassociation */ + pe_debug("*** Reassociated with new BSS ***"); + + session->limSmeState = eLIM_SME_LINK_EST_STATE; + MTRACE(mac_trace( + mac_ctx, TRACE_CODE_SME_STATE, + session->peSessionId, session->limSmeState)); + + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START_SUCCESS, + 0, NULL); + + /* Need to send Reassoc rsp with Reassoc success to Host. */ + lim_send_sme_join_reassoc_rsp( + mac_ctx, eWNI_SME_REASSOC_RSP, + lim_mlm_reassoc_cnf->resultCode, + lim_mlm_reassoc_cnf->protStatusCode, + session, session->smeSessionId); + } else { + param.result_code = lim_mlm_reassoc_cnf->resultCode; + param.prot_status_code = lim_mlm_reassoc_cnf->protStatusCode; + param.session = session; + + mlme_set_connection_fail(session->vdev, true); + + if (wlan_vdev_mlme_get_substate(session->vdev) == + WLAN_VDEV_SS_START_START_PROGRESS) + status = wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START_REQ_FAIL, + sizeof(param), ¶m); + else + status = wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_CONNECTION_FAIL, + sizeof(param), ¶m); + } + + if (session->pLimReAssocReq) { + qdf_mem_free(session->pLimReAssocReq); + session->pLimReAssocReq = NULL; + } +} + +QDF_STATUS lim_sta_reassoc_error_handler(struct reassoc_params *param) +{ + struct pe_session *session; + struct mac_context *mac_ctx; + + if (!param) { + pe_err("param is NULL"); + return QDF_STATUS_E_INVAL; + } + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + pe_err("mac_ctx is NULL"); + return QDF_STATUS_E_INVAL; + } + + session = param->session; + if (param->result_code + == eSIR_SME_REASSOC_REFUSED) { + /* + * Reassociation failure With the New AP but we still have the + * link with the Older AP + */ + session->limSmeState = eLIM_SME_LINK_EST_STATE; + MTRACE(mac_trace( + mac_ctx, TRACE_CODE_SME_STATE, + session->peSessionId, session->limSmeState)); + + /* Need to send Reassoc rsp with Assoc failure to Host. */ + lim_send_sme_join_reassoc_rsp( + mac_ctx, eWNI_SME_REASSOC_RSP, + param->result_code, + param->prot_status_code, + session, session->smeSessionId); + } else { + /* Reassociation failure */ + session->limSmeState = eLIM_SME_JOIN_FAILURE_STATE; + MTRACE(mac_trace( + mac_ctx, TRACE_CODE_SME_STATE, + session->peSessionId, session->limSmeState)); + /* Need to send Reassoc rsp with Assoc failure to Host. */ + lim_handle_sme_reaasoc_result( + mac_ctx, + param->result_code, + param->prot_status_code, + session); + } + return QDF_STATUS_SUCCESS; +} + +void lim_process_sta_mlm_add_bss_rsp_ft(struct mac_context *mac, + struct add_bss_rsp *add_bss_rsp, + struct pe_session *pe_session) +{ + tLimMlmReassocCnf mlmReassocCnf; /* keep sme */ + tpDphHashNode sta = NULL; + tpAddStaParams pAddStaParams = NULL; + uint32_t listenInterval = MLME_CFG_LISTEN_INTERVAL; + uint32_t selfStaDot11Mode = 0; + struct bss_description *bss_desc = NULL; + + /* Sanity Checks */ + + if (!add_bss_rsp) { + pe_err("add_bss_rsp is NULL"); + goto end; + } + if (eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE != + pe_session->limMlmState) { + goto end; + } + + sta = dph_add_hash_entry(mac, pe_session->bssId, + DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + if (!sta) { + /* Could not add hash table entry */ + pe_err("could not add hash entry at DPH for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pe_session->bssId)); + goto end; + } + /* Prepare and send Reassociation request frame */ + /* start reassoc timer. */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (pe_session->bRoamSynchInProgress != true) { +#endif + mac->lim.lim_timers.gLimReassocFailureTimer.sessionId = + pe_session->peSessionId; + /* / Start reassociation failure timer */ + MTRACE(mac_trace + (mac, TRACE_CODE_TIMER_ACTIVATE, + pe_session->peSessionId, eLIM_REASSOC_FAIL_TIMER)); + if (tx_timer_activate + (&mac->lim.lim_timers.gLimReassocFailureTimer) + != TX_SUCCESS) { + /* / Could not start reassoc failure timer. */ + /* Log error */ + pe_err("could not start Reassoc failure timer"); + /* Return Reassoc confirm with */ + /* Resources Unavailable */ + mlmReassocCnf.resultCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + mlmReassocCnf.protStatusCode = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + goto end; + } + mac->lim.pe_session = pe_session; + if (!mac->lim.pe_session->pLimMlmReassocRetryReq) { + /* Take a copy of reassoc request for retrying */ + mac->lim.pe_session->pLimMlmReassocRetryReq = + qdf_mem_malloc(sizeof(tLimMlmReassocReq)); + if (!mac->lim.pe_session->pLimMlmReassocRetryReq) + goto end; + qdf_mem_copy(mac->lim.pe_session-> + pLimMlmReassocRetryReq, + pe_session->pLimMlmReassocReq, + sizeof(tLimMlmReassocReq)); + } + mac->lim.reAssocRetryAttempt = 0; + lim_send_reassoc_req_with_ft_ies_mgmt_frame(mac, + pe_session-> + pLimMlmReassocReq, + pe_session); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + } else { + pe_debug("LFR3:Do not activate timer and dont send the reassoc"); + } +#endif + pe_session->limPrevMlmState = pe_session->limMlmState; + pe_session->limMlmState = eLIM_MLM_WT_FT_REASSOC_RSP_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + eLIM_MLM_WT_FT_REASSOC_RSP_STATE)); + pe_debug("Set the mlm state: %d session: %d", + pe_session->limMlmState, pe_session->peSessionId); + + /* Success, handle below */ + + pAddStaParams = qdf_mem_malloc(sizeof(tAddStaParams)); + if (!pAddStaParams) + goto end; + + /* / Add STA context at MAC HW (BMU, RHP & TFP) */ + qdf_mem_copy((uint8_t *)pAddStaParams->staMac, + (uint8_t *)pe_session->self_mac_addr, + sizeof(tSirMacAddr)); + + qdf_mem_copy((uint8_t *)pAddStaParams->bssId, + pe_session->bssId, sizeof(tSirMacAddr)); + + pAddStaParams->staType = STA_ENTRY_SELF; + pAddStaParams->status = QDF_STATUS_SUCCESS; + + /* Update PE session ID */ + pAddStaParams->sessionId = pe_session->peSessionId; + pAddStaParams->smesessionId = pe_session->smeSessionId; + + pAddStaParams->updateSta = false; + + if (pe_session->lim_join_req) + bss_desc = &pe_session->lim_join_req->bssDescription; + + lim_populate_peer_rate_set(mac, &pAddStaParams->supportedRates, NULL, + false, pe_session, NULL, NULL, NULL, + bss_desc); + + if (pe_session->htCapability) { + pAddStaParams->htCapable = pe_session->htCapability; + pAddStaParams->vhtCapable = pe_session->vhtCapability; + pAddStaParams->ch_width = pe_session->ch_width; + + pAddStaParams->mimoPS = + lim_get_ht_capability(mac, eHT_MIMO_POWER_SAVE, + pe_session); + pAddStaParams->maxAmpduDensity = + lim_get_ht_capability(mac, eHT_MPDU_DENSITY, + pe_session); + pAddStaParams->maxAmpduSize = + lim_get_ht_capability(mac, eHT_MAX_RX_AMPDU_FACTOR, + pe_session); + pAddStaParams->fShortGI20Mhz = + lim_get_ht_capability(mac, eHT_SHORT_GI_20MHZ, + pe_session); + pAddStaParams->fShortGI40Mhz = + lim_get_ht_capability(mac, eHT_SHORT_GI_40MHZ, + pe_session); + } + + listenInterval = mac->mlme_cfg->sap_cfg.listen_interval; + pAddStaParams->listenInterval = (uint16_t) listenInterval; + + selfStaDot11Mode = mac->mlme_cfg->dot11_mode.dot11_mode; + pAddStaParams->encryptType = pe_session->encryptType; + pAddStaParams->maxTxPower = pe_session->maxTxPower; + + /* Lets save this for when we receive the Reassoc Rsp */ + pe_session->ftPEContext.pAddStaReq = pAddStaParams; + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (pe_session->bRoamSynchInProgress) { + pe_debug("LFR3:Prep and save AddStaReq for post-assoc-rsp"); + lim_process_assoc_rsp_frame(mac, mac->roam.pReassocResp, + LIM_REASSOC, pe_session); + } +#endif + return; + +end: + /* Free up buffer allocated for reassocReq */ + if (pe_session) + if (pe_session->pLimMlmReassocReq) { + qdf_mem_free(pe_session->pLimMlmReassocReq); + pe_session->pLimMlmReassocReq = NULL; + } + + mlmReassocCnf.resultCode = eSIR_SME_FT_REASSOC_FAILURE; + mlmReassocCnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + /* Update PE session Id */ + if (pe_session) + mlmReassocCnf.sessionId = pe_session->peSessionId; + else + mlmReassocCnf.sessionId = 0; + + lim_post_sme_message(mac, LIM_MLM_REASSOC_CNF, + (uint32_t *) &mlmReassocCnf); +} + +void lim_process_mlm_ft_reassoc_req(struct mac_context *mac, + tLimMlmReassocReq *reassoc_req) +{ + struct pe_session *session; + uint16_t caps; + uint32_t val; + QDF_STATUS status; + uint32_t teleBcnEn = 0; + struct wlan_objmgr_vdev *vdev; + struct vdev_mlme_obj *mlme_obj; + + if (!reassoc_req) { + pe_err("reassoc_req is NULL"); + return; + } + + session = pe_find_session_by_session_id(mac, reassoc_req->sessionId); + if (!session) { + pe_err("session Does not exist for given session Id"); + qdf_mem_free(reassoc_req); + return; + } + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_REASSOCIATING, + session, 0, 0); +#endif + + /* Nothing to be done if the session is not in STA mode */ + if (!LIM_IS_STA_ROLE(session)) { + pe_err("pe_session is not in STA mode"); + qdf_mem_free(reassoc_req); + return; + } + + if (!session->ftPEContext.pAddBssReq) { + pe_err("pAddBssReq is NULL"); + return; + } + + qdf_mem_copy(reassoc_req->peerMacAddr, + session->bssId, sizeof(tSirMacAddr)); + + if (lim_get_capability_info(mac, &caps, session) != + QDF_STATUS_SUCCESS) { + /** + * Could not get Capabilities value + * from CFG. Log error. + */ + pe_err("could not get Capabilities value"); + qdf_mem_free(reassoc_req); + return; + } + + lim_update_caps_info_for_bss(mac, &caps, + session->pLimReAssocReq->bssDescription.capabilityInfo); + pe_debug("Capabilities info FT Reassoc: 0x%X", caps); + + reassoc_req->capabilityInfo = caps; + + /* If telescopic beaconing is enabled, set listen interval + to CFG_TELE_BCN_MAX_LI + */ + teleBcnEn = mac->mlme_cfg->sap_cfg.tele_bcn_wakeup_en; + if (teleBcnEn) + val = mac->mlme_cfg->sap_cfg.tele_bcn_max_li; + else + val = mac->mlme_cfg->sap_cfg.listen_interval; + + status = wma_add_bss_peer_sta(session->self_mac_addr, session->bssId, + false); + + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(reassoc_req); + return; + } + + reassoc_req->listenInterval = (uint16_t) val; + + vdev = session->vdev; + if (!vdev) { + pe_err("vdev is NULL"); + qdf_mem_free(reassoc_req); + return; + } + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + pe_err("vdev component object is NULL"); + return; + } + + status = lim_pre_vdev_start(mac, mlme_obj, session); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(reassoc_req); + return; + } + qdf_mem_copy(mlme_obj->mgmt.generic.bssid, session->bssId, + QDF_MAC_ADDR_SIZE); + + session->pLimMlmReassocReq = reassoc_req; + /* we need to defer the message until we get response back from HAL */ + SET_LIM_PROCESS_DEFD_MESGS(mac, false); + status = wma_add_bss_lfr2_vdev_start(session->vdev, + session->ftPEContext.pAddBssReq); + if (QDF_IS_STATUS_ERROR(status)) { + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + pe_err("wma_add_bss_lfr2_vdev_start, reason: %X", status); + session->pLimMlmReassocReq = NULL; + qdf_mem_free(reassoc_req); + } + qdf_mem_free(session->ftPEContext.pAddBssReq); + + session->ftPEContext.pAddBssReq = NULL; + return; +} + diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_mlm_req_messages.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_mlm_req_messages.c new file mode 100644 index 0000000000000000000000000000000000000000..71814103b5814a9937f87297f9190e8fe255202b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_mlm_req_messages.c @@ -0,0 +1,2013 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cds_api.h" +#include "wni_cfg.h" +#include "ani_global.h" +#include "sir_api.h" +#include "sir_params.h" +#include "cfg_ucfg_api.h" +#include "sch_api.h" +#include "utils_api.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_prop_exts_utils.h" +#include "lim_security_utils.h" +#include "lim_send_messages.h" +#include "lim_send_messages.h" +#include "lim_session_utils.h" +#include +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM +#include "host_diag_core_log.h" +#endif +#include "wma_if.h" +#include "wma.h" +#include "wlan_reg_services_api.h" +#include "lim_process_fils.h" +#include "wlan_mlme_public_struct.h" +#include "../../core/src/vdev_mgr_ops.h" +#include "wlan_pmo_ucfg_api.h" +#include "wlan_objmgr_vdev_obj.h" + +static void lim_process_mlm_auth_req(struct mac_context *, uint32_t *); +static void lim_process_mlm_assoc_req(struct mac_context *, uint32_t *); +static void lim_process_mlm_disassoc_req(struct mac_context *, uint32_t *); + +/* MLM Timeout event handler templates */ +static void lim_process_auth_rsp_timeout(struct mac_context *, uint32_t); +static void lim_process_periodic_join_probe_req_timer(struct mac_context *); +static void lim_process_auth_retry_timer(struct mac_context *); + +static void lim_fill_status_code(uint8_t frame_type, + enum tx_ack_status ack_status, + enum wlan_status_code *proto_status_code) +{ + if (frame_type == SIR_MAC_MGMT_AUTH) { + switch (ack_status) { + case LIM_TX_FAILED: + *proto_status_code = STATUS_AUTH_TX_FAIL; + break; + case LIM_ACK_RCD_FAILURE: + *proto_status_code = STATUS_AUTH_NO_ACK_RECEIVED; + break; + case LIM_ACK_RCD_SUCCESS: + *proto_status_code = STATUS_AUTH_NO_RESP_RECEIVED; + break; + default: + *proto_status_code = STATUS_UNSPECIFIED_FAILURE; + } + } else if (frame_type == SIR_MAC_MGMT_ASSOC_RSP) { + switch (ack_status) { + case LIM_TX_FAILED: + *proto_status_code = STATUS_ASSOC_TX_FAIL; + break; + case LIM_ACK_RCD_FAILURE: + *proto_status_code = STATUS_ASSOC_NO_ACK_RECEIVED; + break; + case LIM_ACK_RCD_SUCCESS: + *proto_status_code = STATUS_ASSOC_NO_RESP_RECEIVED; + break; + default: + *proto_status_code = STATUS_UNSPECIFIED_FAILURE; + } + } +} + +void lim_process_sae_auth_timeout(struct mac_context *mac_ctx) +{ + struct pe_session *session; + enum wlan_status_code proto_status_code; + + session = pe_find_session_by_session_id(mac_ctx, + mac_ctx->lim.lim_timers.sae_auth_timer.sessionId); + if (!session) { + pe_err("Session does not exist for given session id"); + return; + } + + pe_warn("SAE auth timeout sessionid %d mlmstate %X SmeState %X", + session->peSessionId, session->limMlmState, + session->limSmeState); + + switch (session->limMlmState) { + case eLIM_MLM_WT_SAE_AUTH_STATE: + lim_fill_status_code(SIR_MAC_MGMT_AUTH, + mac_ctx->auth_ack_status, + &proto_status_code); + /* + * SAE authentication is not completed. Restore from + * auth state. + */ + if (session->opmode == QDF_STA_MODE) + lim_restore_from_auth_state(mac_ctx, + eSIR_SME_AUTH_TIMEOUT_RESULT_CODE, + proto_status_code, session); + break; + default: + /* SAE authentication is timed out in unexpected state */ + pe_err("received unexpected SAE auth timeout in state %X", + session->limMlmState); + lim_print_mlm_state(mac_ctx, LOGE, session->limMlmState); + break; + } +} + +/** + * lim_process_mlm_req_messages() - process mlm request messages + * @mac_ctx: global MAC context + * @msg: mlm request message + * + * This function is called by lim_post_mlm_message(). This + * function handles MLM primitives invoked by SME. + * Depending on the message type, corresponding function will be + * called. + * ASSUMPTIONS: + * 1. Upon receiving Beacon in WT_JOIN_STATE, MLM module invokes + * APIs exposed by Beacon Processing module for setting parameters + * at MAC hardware. + * 2. If attempt to Reassociate with an AP fails, link with current + * AP is restored back. + * + * Return: None + */ +void lim_process_mlm_req_messages(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + switch (msg->type) { + case LIM_MLM_AUTH_REQ: + lim_process_mlm_auth_req(mac_ctx, msg->bodyptr); + break; + case LIM_MLM_ASSOC_REQ: + lim_process_mlm_assoc_req(mac_ctx, msg->bodyptr); + break; + case LIM_MLM_DISASSOC_REQ: + lim_process_mlm_disassoc_req(mac_ctx, msg->bodyptr); + break; + case SIR_LIM_JOIN_FAIL_TIMEOUT: + lim_process_join_failure_timeout(mac_ctx); + break; + case SIR_LIM_PERIODIC_JOIN_PROBE_REQ_TIMEOUT: + lim_process_periodic_join_probe_req_timer(mac_ctx); + break; + case SIR_LIM_AUTH_FAIL_TIMEOUT: + lim_process_auth_failure_timeout(mac_ctx); + break; + case SIR_LIM_AUTH_RSP_TIMEOUT: + lim_process_auth_rsp_timeout(mac_ctx, msg->bodyval); + break; + case SIR_LIM_ASSOC_FAIL_TIMEOUT: + lim_process_assoc_failure_timeout(mac_ctx, msg->bodyval); + break; + case SIR_LIM_FT_PREAUTH_RSP_TIMEOUT: + lim_process_ft_preauth_rsp_timeout(mac_ctx); + break; + case SIR_LIM_DISASSOC_ACK_TIMEOUT: + lim_process_disassoc_ack_timeout(mac_ctx); + break; + case SIR_LIM_DEAUTH_ACK_TIMEOUT: + lim_process_deauth_ack_timeout(mac_ctx); + break; + case SIR_LIM_AUTH_RETRY_TIMEOUT: + lim_process_auth_retry_timer(mac_ctx); + break; + case SIR_LIM_AUTH_SAE_TIMEOUT: + lim_process_sae_auth_timeout(mac_ctx); + break; + case LIM_MLM_TSPEC_REQ: + default: + break; + } /* switch (msg->type) */ +} + +#ifdef WLAN_FEATURE_11W +static void update_rmfEnabled(struct bss_params *addbss_param, + struct pe_session *session) +{ + addbss_param->rmfEnabled = session->limRmfEnabled; +} +#else +static void update_rmfEnabled(struct bss_params *addbss_param, + struct pe_session *session) +{ +} +#endif + +/** + * lim_mlm_add_bss() - HAL interface for WMA_ADD_BSS_REQ + * @mac_ctx: global MAC context + * @mlm_start_req: MLM start request + * @session: PE session entry + * + * Package WMA_ADD_BSS_REQ to HAL, in order to start a BSS + * + * Return: eSIR_SME_SUCCESS on success, other error codes otherwise + */ +tSirResultCodes +lim_mlm_add_bss(struct mac_context *mac_ctx, + tLimMlmStartReq *mlm_start_req, struct pe_session *session) +{ + struct vdev_mlme_obj *mlme_obj; + struct wlan_objmgr_vdev *vdev = session->vdev; + uint8_t vdev_id = session->vdev_id; + QDF_STATUS status = QDF_STATUS_SUCCESS; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct bss_params *addbss_param = NULL; + + if (!wma) { + pe_err("Invalid wma handle"); + return eSIR_SME_INVALID_PARAMETERS; + } + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + pe_err("vdev component object is NULL"); + return eSIR_SME_INVALID_PARAMETERS; + } + + qdf_mem_copy(mlme_obj->mgmt.generic.bssid, mlm_start_req->bssId, + QDF_MAC_ADDR_SIZE); + if (lim_is_session_he_capable(session)) { + lim_decide_he_op(mac_ctx, &mlme_obj->proto.he_ops_info.he_ops, + session); + lim_update_usr_he_cap(mac_ctx, session); + } + + /* Set a new state for MLME */ + session->limMlmState = eLIM_MLM_WT_ADD_BSS_RSP_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, session->peSessionId, + session->limMlmState)); + + status = lim_pre_vdev_start(mac_ctx, mlme_obj, session); + if (QDF_IS_STATUS_ERROR(status)) + goto send_fail_resp; + + addbss_param = qdf_mem_malloc(sizeof(struct bss_params)); + if (!addbss_param) + goto send_fail_resp; + + addbss_param->vhtCapable = mlm_start_req->htCapable; + addbss_param->htCapable = session->vhtCapability; + addbss_param->ch_width = session->ch_width; + update_rmfEnabled(addbss_param, session); + addbss_param->staContext.fShortGI20Mhz = + lim_get_ht_capability(mac_ctx, eHT_SHORT_GI_20MHZ, session); + addbss_param->staContext.fShortGI40Mhz = + lim_get_ht_capability(mac_ctx, eHT_SHORT_GI_40MHZ, session); + status = wma_pre_vdev_start_setup(vdev_id, addbss_param); + qdf_mem_free(addbss_param); + if (QDF_IS_STATUS_ERROR(status)) + goto send_fail_resp; + + if (session->wps_state == SAP_WPS_DISABLED) + ucfg_pmo_disable_wakeup_event(wma->psoc, vdev_id, + WOW_PROBE_REQ_WPS_IE_EVENT); + status = wma_vdev_pre_start(vdev_id, false); + if (QDF_IS_STATUS_ERROR(status)) + goto peer_cleanup; + status = vdev_mgr_start_send(mlme_obj, false); + if (QDF_IS_STATUS_ERROR(status)) + goto peer_cleanup; + wma_post_vdev_start_setup(vdev_id); + + return eSIR_SME_SUCCESS; + +peer_cleanup: + wma_remove_bss_peer_on_vdev_start_failure(wma, vdev_id); +send_fail_resp: + wma_send_add_bss_resp(wma, vdev_id, QDF_STATUS_E_FAILURE); + + return eSIR_SME_SUCCESS; +} + +void lim_process_mlm_start_req(struct mac_context *mac_ctx, + tLimMlmStartReq *mlm_start_req) +{ + tLimMlmStartCnf mlm_start_cnf; + struct pe_session *session = NULL; + + if (!mlm_start_req) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + session = pe_find_session_by_session_id(mac_ctx, + mlm_start_req->sessionId); + if (!session) { + pe_err("Session Does not exist for given sessionID"); + mlm_start_cnf.resultCode = eSIR_SME_REFUSED; + goto end; + } + + if (session->limMlmState != eLIM_MLM_IDLE_STATE) { + /* + * Should not have received Start req in states other than idle. + * Return Start confirm with failure code. + */ + pe_err("received unexpected MLM_START_REQ in state %X", + session->limMlmState); + lim_print_mlm_state(mac_ctx, LOGE, session->limMlmState); + mlm_start_cnf.resultCode = + eSIR_SME_BSS_ALREADY_STARTED_OR_JOINED; + goto end; + } + + mlm_start_cnf.resultCode = + lim_mlm_add_bss(mac_ctx, mlm_start_req, session); + +end: + /* Update PE session Id */ + mlm_start_cnf.sessionId = mlm_start_req->sessionId; + + /* + * Respond immediately to LIM, only if MLME has not been + * successfully able to send WMA_ADD_BSS_REQ to HAL. + * Else, LIM_MLM_START_CNF will be sent after receiving + * WMA_ADD_BSS_RSP from HAL + */ + if (eSIR_SME_SUCCESS != mlm_start_cnf.resultCode) + lim_send_start_bss_confirm(mac_ctx, &mlm_start_cnf); +} + +/** + * lim_post_join_set_link_state_callback()- registered callback to perform post + * peer creation operations + * + * @mac: pointer to global mac structure + * @callback_arg: registered callback argument + * @status: peer creation status + * + * this is registered callback function during association to perform + * post peer creation operation based on the peer creation status + * + * Return: none + */ +static void lim_post_join_set_link_state_callback( + struct mac_context *mac, + struct pe_session *session_entry, QDF_STATUS status) +{ + tLimMlmJoinCnf mlm_join_cnf; + + if (!session_entry) { + pe_err("sessionId is NULL"); + return; + } + + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("failed to find pe session for session id:%d", + session_entry->peSessionId); + goto failure; + } + + /* + * store the channel switch session_entry in the lim + * global variable + */ + session_entry->channelChangeReasonCode = + LIM_SWITCH_CHANNEL_JOIN; + session_entry->pLimMlmReassocRetryReq = NULL; + lim_send_switch_chnl_params(mac, session_entry); + + return; + +failure: + MTRACE(mac_trace(mac, TRACE_CODE_MLM_STATE, session_entry->peSessionId, + session_entry->limMlmState)); + session_entry->limMlmState = eLIM_MLM_IDLE_STATE; + mlm_join_cnf.resultCode = eSIR_SME_PEER_CREATE_FAILED; + mlm_join_cnf.sessionId = session_entry->peSessionId; + mlm_join_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + lim_post_sme_message(mac, LIM_MLM_JOIN_CNF, (uint32_t *) &mlm_join_cnf); +} + +/** + * lim_process_mlm_post_join_suspend_link() - This function is called after the + * suspend link while joining off channel. + * @mac_ctx: Pointer to Global MAC structure + * @session: session + * + * This function does following: + * Check for suspend state. + * If success, proceed with setting link state to receive the + * probe response/beacon from intended AP. + * Switch to the APs channel. + * On an error case, send the MLM_JOIN_CNF with error status. + * + * @Return None + */ +static void +lim_process_mlm_post_join_suspend_link(struct mac_context *mac_ctx, + struct pe_session *session) +{ + QDF_STATUS status; + + lim_deactivate_and_change_timer(mac_ctx, eLIM_JOIN_FAIL_TIMER); + + /* assign appropriate sessionId to the timer object */ + mac_ctx->lim.lim_timers.gLimJoinFailureTimer.sessionId = + session->peSessionId; + + status = wma_add_bss_peer_sta(session->self_mac_addr, session->bssId, + false); + lim_post_join_set_link_state_callback(mac_ctx, session, status); + + return; +} + +/** + * lim_process_mlm_join_req() - process mlm join request. + * + * @mac_ctx: Pointer to Global MAC structure + * @mlm_join_req: Pointer to the mlme join request + * + * This function is called to process MLM_JOIN_REQ message + * from SME. It does following: + * 1) Initialize LIM, HAL, DPH + * 2) Configure the BSS for which the JOIN REQ was received + * a) Send WMA_ADD_BSS_REQ to HAL - + * This will identify the BSS that we are interested in + * --AND-- + * Add a STA entry for the AP (in a STA context) + * b) Wait for WMA_ADD_BSS_RSP + * c) Send WMA_ADD_STA_REQ to HAL + * This will add the "local STA" entry to the STA table + * 3) Continue as before, i.e, + * a) Send a PROBE REQ + * b) Wait for PROBE RSP/BEACON containing the SSID that + * we are interested in + * c) Then start an AUTH seq + * d) Followed by the ASSOC seq + * + * @Return: None + */ +void lim_process_mlm_join_req(struct mac_context *mac_ctx, + tLimMlmJoinReq *mlm_join_req) +{ + tLimMlmJoinCnf mlmjoin_cnf; + uint8_t sessionid; + struct pe_session *session; + + sessionid = mlm_join_req->sessionId; + + session = pe_find_session_by_session_id(mac_ctx, sessionid); + if (!session) { + pe_err("SessionId:%d does not exist", sessionid); + goto error; + } + + if (!LIM_IS_AP_ROLE(session) && + ((session->limMlmState == eLIM_MLM_IDLE_STATE) || + (session->limMlmState == eLIM_MLM_JOINED_STATE)) && + (SIR_MAC_GET_ESS + (mlm_join_req->bssDescription.capabilityInfo) != + SIR_MAC_GET_IBSS(mlm_join_req->bssDescription. + capabilityInfo))) { + session->pLimMlmJoinReq = mlm_join_req; + lim_process_mlm_post_join_suspend_link(mac_ctx, session); + return; + } + + /** + * Should not have received JOIN req in states other than + * Idle state or on AP. + * Return join confirm with invalid parameters code. + */ + pe_err("Session:%d Unexpected Join req, role %d state %X", + session->peSessionId, GET_LIM_SYSTEM_ROLE(session), + session->limMlmState); + lim_print_mlm_state(mac_ctx, LOGE, session->limMlmState); + +error: + qdf_mem_free(mlm_join_req); + if (session) + session->pLimMlmJoinReq = NULL; + mlmjoin_cnf.resultCode = eSIR_SME_PEER_CREATE_FAILED; + mlmjoin_cnf.sessionId = sessionid; + mlmjoin_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + lim_post_sme_message(mac_ctx, LIM_MLM_JOIN_CNF, + (uint32_t *)&mlmjoin_cnf); + +} + +/** + * lim_is_auth_req_expected() - check if auth request is expected + * + * @mac_ctx: global MAC context + * @session: PE session entry + * + * This function is called by lim_process_mlm_auth_req to check + * if auth request is expected. + * + * Return: true if expected and false otherwise + */ +static bool lim_is_auth_req_expected(struct mac_context *mac_ctx, + struct pe_session *session) +{ + bool flag = false; + + /* + * Expect Auth request only when: + * 1. STA joined/associated with a BSS or + * 2. STA is in IBSS mode + * and STA is going to authenticate with a unicast + * address and requested authentication algorithm is + * supported. + */ + + flag = (((LIM_IS_STA_ROLE(session) && + ((session->limMlmState == eLIM_MLM_JOINED_STATE) || + (session->limMlmState == + eLIM_MLM_LINK_ESTABLISHED_STATE))) || + (LIM_IS_IBSS_ROLE(session) && + (session->limMlmState == + eLIM_MLM_BSS_STARTED_STATE))) && + (!IEEE80211_IS_MULTICAST( + mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr)) && + lim_is_auth_algo_supported(mac_ctx, + mac_ctx->lim.gpLimMlmAuthReq->authType, session)); + + return flag; +} + +/** + * lim_is_preauth_ctx_exisits() - check if preauth context exists + * + * @mac_ctx: global MAC context + * @session: PE session entry + * @preauth_node_ptr: pointer to preauth node pointer + * + * This function is called by lim_process_mlm_auth_req to check + * if preauth context already exists + * + * Return: true if exists and false otherwise + */ +static bool lim_is_preauth_ctx_exists(struct mac_context *mac_ctx, + struct pe_session *session, + struct tLimPreAuthNode **preauth_node_ptr) +{ + bool fl = false; + struct tLimPreAuthNode *preauth_node; + tpDphHashNode stads; + tSirMacAddr curr_bssid; + + preauth_node = *preauth_node_ptr; + sir_copy_mac_addr(curr_bssid, session->bssId); + stads = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session->dph.dphHashTable); + preauth_node = lim_search_pre_auth_list(mac_ctx, + mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr); + + fl = (((LIM_IS_STA_ROLE(session)) && + (session->limMlmState == eLIM_MLM_LINK_ESTABLISHED_STATE) && + ((stads) && + (mac_ctx->lim.gpLimMlmAuthReq->authType == + stads->mlmStaContext.authType)) && + (!qdf_mem_cmp(mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr, + curr_bssid, sizeof(tSirMacAddr)))) || + ((preauth_node) && + (preauth_node->authType == + mac_ctx->lim.gpLimMlmAuthReq->authType))); + + return fl; +} + +#ifdef WLAN_FEATURE_SAE +/** + * lim_process_mlm_auth_req_sae() - Handle SAE authentication + * @mac_ctx: global MAC context + * @session: PE session entry + * + * This function is called by lim_process_mlm_auth_req to handle SAE + * authentication. + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_process_mlm_auth_req_sae(struct mac_context *mac_ctx, + struct pe_session *session) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct sir_sae_info *sae_info; + struct scheduler_msg msg = {0}; + + sae_info = qdf_mem_malloc(sizeof(*sae_info)); + if (!sae_info) + return QDF_STATUS_E_FAILURE; + + sae_info->msg_type = eWNI_SME_TRIGGER_SAE; + sae_info->msg_len = sizeof(*sae_info); + sae_info->vdev_id = session->smeSessionId; + + qdf_mem_copy(sae_info->peer_mac_addr.bytes, + session->bssId, + QDF_MAC_ADDR_SIZE); + + sae_info->ssid.length = session->ssId.length; + qdf_mem_copy(sae_info->ssid.ssId, + session->ssId.ssId, + session->ssId.length); + + pe_debug("vdev_id %d ssid %.*s "QDF_MAC_ADDR_FMT, + sae_info->vdev_id, + sae_info->ssid.length, + sae_info->ssid.ssId, + QDF_MAC_ADDR_REF(sae_info->peer_mac_addr.bytes)); + + msg.type = eWNI_SME_TRIGGER_SAE; + msg.bodyptr = sae_info; + msg.bodyval = 0; + + qdf_status = mac_ctx->lim.sme_msg_callback(mac_ctx, &msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("SAE failed for AUTH frame"); + qdf_mem_free(sae_info); + return qdf_status; + } + session->limMlmState = eLIM_MLM_WT_SAE_AUTH_STATE; + + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, session->peSessionId, + session->limMlmState)); + + mac_ctx->lim.lim_timers.sae_auth_timer.sessionId = + session->peSessionId; + + /* Activate SAE auth timer */ + MTRACE(mac_trace(mac_ctx, TRACE_CODE_TIMER_ACTIVATE, + session->peSessionId, eLIM_AUTH_SAE_TIMER)); + if (tx_timer_activate(&mac_ctx->lim.lim_timers.sae_auth_timer) + != TX_SUCCESS) { + pe_err("could not start Auth SAE timer"); + } + + return qdf_status; +} +#else +static QDF_STATUS lim_process_mlm_auth_req_sae(struct mac_context *mac_ctx, + struct pe_session *session) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif + + +/** + * lim_process_mlm_auth_req() - process lim auth request + * + * @mac_ctx: global MAC context + * @msg: MLM auth request message + * + * This function is called to process MLM_AUTH_REQ message from SME + * + * @Return: None + */ +static void lim_process_mlm_auth_req(struct mac_context *mac_ctx, uint32_t *msg) +{ + uint32_t num_preauth_ctx; + tSirMacAddr curr_bssid; + tSirMacAuthFrameBody auth_frame_body; + tLimMlmAuthCnf mlm_auth_cnf; + struct tLimPreAuthNode *preauth_node = NULL; + uint8_t session_id; + struct pe_session *session; + + if (!msg) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + mac_ctx->lim.gpLimMlmAuthReq = (tLimMlmAuthReq *) msg; + session_id = mac_ctx->lim.gpLimMlmAuthReq->sessionId; + session = pe_find_session_by_session_id(mac_ctx, session_id); + if (!session) { + pe_err("SessionId:%d does not exist", session_id); + qdf_mem_free(msg); + mac_ctx->lim.gpLimMlmAuthReq = NULL; + return; + } + + pe_debug("vdev %d Systemrole %d mlmstate %d from: " QDF_MAC_ADDR_FMT "with authtype %d", + session->vdev_id, GET_LIM_SYSTEM_ROLE(session), + session->limMlmState, + QDF_MAC_ADDR_REF(mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr), + mac_ctx->lim.gpLimMlmAuthReq->authType); + + sir_copy_mac_addr(curr_bssid, session->bssId); + + if (!lim_is_auth_req_expected(mac_ctx, session)) { + /* + * Unexpected auth request. + * Return Auth confirm with Invalid parameters code. + */ + mlm_auth_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + /* + * This is a request for pre-authentication. Check if there exists + * context already for the requested peer OR + * if this request is for the AP we're currently associated with. + * If yes, return auth confirm immediately when + * requested auth type is same as the one used before. + */ + if (lim_is_preauth_ctx_exists(mac_ctx, session, &preauth_node)) { + pe_debug("Already have pre-auth context with peer: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr)); + mlm_auth_cnf.resultCode = (tSirResultCodes) + eSIR_MAC_SUCCESS_STATUS; + goto end; + } else { + num_preauth_ctx = mac_ctx->mlme_cfg->lfr.max_num_pre_auth; + if (mac_ctx->lim.gLimNumPreAuthContexts == num_preauth_ctx) { + pe_warn("Number of pre-auth reached max limit"); + /* Return Auth confirm with reject code */ + mlm_auth_cnf.resultCode = + eSIR_SME_MAX_NUM_OF_PRE_AUTH_REACHED; + goto end; + } + } + + /* Delete pre-auth node if exists */ + if (preauth_node) + lim_delete_pre_auth_node(mac_ctx, + mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr); + + session->limPrevMlmState = session->limMlmState; + + if ((mac_ctx->lim.gpLimMlmAuthReq->authType == eSIR_AUTH_TYPE_SAE) && + !session->sae_pmk_cached) { + if (lim_process_mlm_auth_req_sae(mac_ctx, session) != + QDF_STATUS_SUCCESS) { + mlm_auth_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + goto end; + } else { + pe_debug("lim_process_mlm_auth_req_sae is successful"); + auth_frame_body.authAlgoNumber = eSIR_AUTH_TYPE_SAE; + auth_frame_body.authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_1; + auth_frame_body.authStatusCode = 0; + host_log_wlan_auth_info(auth_frame_body.authAlgoNumber, + auth_frame_body.authTransactionSeqNumber, + auth_frame_body.authStatusCode); + + return; + } + } else + session->limMlmState = eLIM_MLM_WT_AUTH_FRAME2_STATE; + + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, session->peSessionId, + session->limMlmState)); + + /* Mark auth algo as open when auth type is SAE and PMK is cached */ + if ((mac_ctx->lim.gpLimMlmAuthReq->authType == eSIR_AUTH_TYPE_SAE) && + session->sae_pmk_cached) { + auth_frame_body.authAlgoNumber = eSIR_OPEN_SYSTEM; + } else { + auth_frame_body.authAlgoNumber = + (uint8_t) mac_ctx->lim.gpLimMlmAuthReq->authType; + } + + /* Prepare & send Authentication frame */ + auth_frame_body.authTransactionSeqNumber = SIR_MAC_AUTH_FRAME_1; + auth_frame_body.authStatusCode = 0; + host_log_wlan_auth_info(auth_frame_body.authAlgoNumber, + auth_frame_body.authTransactionSeqNumber, + auth_frame_body.authStatusCode); + mac_ctx->auth_ack_status = LIM_ACK_NOT_RCD; + lim_send_auth_mgmt_frame(mac_ctx, + &auth_frame_body, mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr, + LIM_NO_WEP_IN_FC, session); + + /* assign appropriate session_id to the timer object */ + mac_ctx->lim.lim_timers.gLimAuthFailureTimer.sessionId = session_id; + + /* assign appropriate sessionId to the timer object */ + mac_ctx->lim.lim_timers.g_lim_periodic_auth_retry_timer.sessionId = + session_id; + lim_deactivate_and_change_timer(mac_ctx, eLIM_AUTH_RETRY_TIMER); + /* Activate Auth failure timer */ + MTRACE(mac_trace(mac_ctx, TRACE_CODE_TIMER_ACTIVATE, + session->peSessionId, eLIM_AUTH_FAIL_TIMER)); + lim_deactivate_and_change_timer(mac_ctx, eLIM_AUTH_FAIL_TIMER); + if (tx_timer_activate(&mac_ctx->lim.lim_timers.gLimAuthFailureTimer) + != TX_SUCCESS) { + pe_err("could not start Auth failure timer"); + /* Cleanup as if auth timer expired */ + lim_process_auth_failure_timeout(mac_ctx); + } else { + MTRACE(mac_trace(mac_ctx, TRACE_CODE_TIMER_ACTIVATE, + session->peSessionId, eLIM_AUTH_RETRY_TIMER)); + /* Activate Auth Retry timer */ + if (tx_timer_activate + (&mac_ctx->lim.lim_timers.g_lim_periodic_auth_retry_timer) + != TX_SUCCESS) + pe_err("could not activate Auth Retry timer"); + } + + return; +end: + qdf_mem_copy((uint8_t *) &mlm_auth_cnf.peerMacAddr, + (uint8_t *) &mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr, + sizeof(tSirMacAddr)); + + mlm_auth_cnf.authType = mac_ctx->lim.gpLimMlmAuthReq->authType; + mlm_auth_cnf.sessionId = session_id; + + qdf_mem_free(mac_ctx->lim.gpLimMlmAuthReq); + mac_ctx->lim.gpLimMlmAuthReq = NULL; + pe_debug("SessionId:%d LimPostSme LIM_MLM_AUTH_CNF", + session_id); + lim_post_sme_message(mac_ctx, LIM_MLM_AUTH_CNF, + (uint32_t *) &mlm_auth_cnf); +} + +#ifdef WLAN_FEATURE_11W +static void lim_store_pmfcomeback_timerinfo(struct pe_session *session_entry) +{ + if (session_entry->opmode != QDF_STA_MODE || + !session_entry->limRmfEnabled) + return; + /* + * Store current MLM state in case ASSOC response returns with + * TRY_AGAIN_LATER return code. + */ + session_entry->pmf_retry_timer_info.lim_prev_mlm_state = + session_entry->limPrevMlmState; + session_entry->pmf_retry_timer_info.lim_mlm_state = + session_entry->limMlmState; +} +#else +static void lim_store_pmfcomeback_timerinfo(struct pe_session *session_entry) +{ +} +#endif /* WLAN_FEATURE_11W */ + +/** + * lim_process_mlm_assoc_req() - This function is called to process + * MLM_ASSOC_REQ message from SME + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: A pointer to the MLM message buffer + * + * This function is called to process MLM_ASSOC_REQ message from SME + * + * @Return None + */ + +static void lim_process_mlm_assoc_req(struct mac_context *mac_ctx, uint32_t *msg_buf) +{ + tSirMacAddr curr_bssId; + tLimMlmAssocReq *mlm_assoc_req; + tLimMlmAssocCnf mlm_assoc_cnf; + struct pe_session *session_entry; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + mlm_assoc_req = (tLimMlmAssocReq *) msg_buf; + session_entry = pe_find_session_by_session_id(mac_ctx, + mlm_assoc_req->sessionId); + if (!session_entry) { + pe_err("SessionId:%d Session Does not exist", + mlm_assoc_req->sessionId); + qdf_mem_free(mlm_assoc_req); + return; + } + + sir_copy_mac_addr(curr_bssId, session_entry->bssId); + + if (!(!LIM_IS_AP_ROLE(session_entry) && + (session_entry->limMlmState == eLIM_MLM_AUTHENTICATED_STATE || + session_entry->limMlmState == eLIM_MLM_JOINED_STATE) && + (!qdf_mem_cmp(mlm_assoc_req->peerMacAddr, + curr_bssId, sizeof(tSirMacAddr))))) { + /* + * Received Association request either in invalid state + * or to a peer MAC entity whose address is different + * from one that STA is currently joined with or on AP. + * Return Assoc confirm with Invalid parameters code. + */ + pe_warn("received unexpected MLM_ASSOC_CNF in state %X for role=%d, MAC addr= " + QDF_MAC_ADDR_FMT, session_entry->limMlmState, + GET_LIM_SYSTEM_ROLE(session_entry), + QDF_MAC_ADDR_REF(mlm_assoc_req->peerMacAddr)); + lim_print_mlm_state(mac_ctx, LOGW, session_entry->limMlmState); + mlm_assoc_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + mlm_assoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + goto end; + } + + /* map the session entry pointer to the AssocFailureTimer */ + mac_ctx->lim.lim_timers.gLimAssocFailureTimer.sessionId = + mlm_assoc_req->sessionId; + lim_store_pmfcomeback_timerinfo(session_entry); + session_entry->limPrevMlmState = session_entry->limMlmState; + session_entry->limMlmState = eLIM_MLM_WT_ASSOC_RSP_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, + session_entry->limMlmState)); + pe_debug("vdev %d Sending Assoc_Req Frame", session_entry->vdev_id); + + /* Prepare and send Association request frame */ + lim_send_assoc_req_mgmt_frame(mac_ctx, mlm_assoc_req, session_entry); + + /* Start association failure timer */ + MTRACE(mac_trace(mac_ctx, TRACE_CODE_TIMER_ACTIVATE, + session_entry->peSessionId, eLIM_ASSOC_FAIL_TIMER)); + if (tx_timer_activate(&mac_ctx->lim.lim_timers.gLimAssocFailureTimer) + != TX_SUCCESS) { + pe_warn("SessionId:%d couldn't start Assoc failure timer", + session_entry->peSessionId); + /* Cleanup as if assoc timer expired */ + lim_process_assoc_failure_timeout(mac_ctx, LIM_ASSOC); + } + + return; +end: + /* Update PE session Id */ + mlm_assoc_cnf.sessionId = mlm_assoc_req->sessionId; + /* Free up buffer allocated for assoc_req */ + qdf_mem_free(mlm_assoc_req); + lim_post_sme_message(mac_ctx, LIM_MLM_ASSOC_CNF, + (uint32_t *) &mlm_assoc_cnf); +} + +/** + * lim_process_mlm_disassoc_req_ntf() - process disassoc request notification + * + * @mac_ctx: global MAC context + * @suspend_status: suspend status + * @msg: mlm message buffer + * + * This function is used to process MLM disassoc notification + * + * Return: None + */ +static void +lim_process_mlm_disassoc_req_ntf(struct mac_context *mac_ctx, + QDF_STATUS suspend_status, uint32_t *msg) +{ + uint16_t aid; + struct qdf_mac_addr curr_bssid; + tpDphHashNode stads; + tLimMlmDisassocReq *mlm_disassocreq; + tLimMlmDisassocCnf mlm_disassoccnf; + struct pe_session *session; + extern bool send_disassoc_frame; + tLimMlmStates mlm_state; + struct disassoc_rsp *sme_disassoc_rsp; + + if (QDF_STATUS_SUCCESS != suspend_status) + pe_err("Suspend Status is not success %X", + suspend_status); + + mlm_disassocreq = (tLimMlmDisassocReq *) msg; + + session = pe_find_session_by_session_id(mac_ctx, + mlm_disassocreq->sessionId); + if (!session) { + pe_err("session does not exist for given sessionId %d", + mlm_disassocreq->sessionId); + mlm_disassoccnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + qdf_mem_copy(curr_bssid.bytes, session->bssId, QDF_MAC_ADDR_SIZE); + + switch (GET_LIM_SYSTEM_ROLE(session)) { + case eLIM_STA_ROLE: + if (!qdf_is_macaddr_equal(&mlm_disassocreq->peer_macaddr, + &curr_bssid)) { + pe_warn("received MLM_DISASSOC_REQ with invalid BSS id"); + lim_print_mac_addr(mac_ctx, + mlm_disassocreq->peer_macaddr.bytes, LOGW); + + /* + * Disassociation response due to host triggered + * disassociation + */ + sme_disassoc_rsp = + qdf_mem_malloc(sizeof(*sme_disassoc_rsp)); + if (!sme_disassoc_rsp) { + qdf_mem_free(mlm_disassocreq); + return; + } + + pe_debug("send disassoc rsp with ret code %d for" QDF_MAC_ADDR_FMT, + eSIR_SME_DEAUTH_STATUS, + QDF_MAC_ADDR_REF( + mlm_disassocreq->peer_macaddr.bytes)); + + sme_disassoc_rsp->messageType = eWNI_SME_DISASSOC_RSP; + sme_disassoc_rsp->length = sizeof(*sme_disassoc_rsp); + sme_disassoc_rsp->sessionId = + mlm_disassocreq->sessionId; + sme_disassoc_rsp->status_code = eSIR_SME_DEAUTH_STATUS; + + qdf_copy_macaddr(&sme_disassoc_rsp->peer_macaddr, + &mlm_disassocreq->peer_macaddr); + msg = (uint32_t *)sme_disassoc_rsp; + + lim_send_sme_disassoc_deauth_ntf(mac_ctx, + QDF_STATUS_SUCCESS, msg); + qdf_mem_free(mlm_disassocreq); + return; + + } + break; + default: + break; + } /* end switch (GET_LIM_SYSTEM_ROLE(session)) */ + + /* + * Check if there exists a context for the peer entity + * to be disassociated with. + */ + stads = dph_lookup_hash_entry(mac_ctx, + mlm_disassocreq->peer_macaddr.bytes, + &aid, &session->dph.dphHashTable); + if (stads) + mlm_state = stads->mlmStaContext.mlmState; + + if ((!stads) || + (stads && + ((mlm_state != eLIM_MLM_LINK_ESTABLISHED_STATE) && + (mlm_state != eLIM_MLM_WT_ASSOC_CNF_STATE) && + (mlm_state != eLIM_MLM_ASSOCIATED_STATE)))) { + /* + * Received LIM_MLM_DISASSOC_REQ for STA that does not + * have context or in some transit state. + */ + pe_warn("Invalid MLM_DISASSOC_REQ, Addr= " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mlm_disassocreq->peer_macaddr.bytes)); + if (stads) + pe_err("Sta MlmState: %d", stads->mlmStaContext.mlmState); + + /* Prepare and Send LIM_MLM_DISASSOC_CNF */ + mlm_disassoccnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + stads->mlmStaContext.disassocReason = (tSirMacReasonCodes) + mlm_disassocreq->reasonCode; + stads->mlmStaContext.cleanupTrigger = mlm_disassocreq->disassocTrigger; + + /* + * Set state to mlm State to eLIM_MLM_WT_DEL_STA_RSP_STATE + * This is to address the issue of race condition between + * disconnect request from the HDD and deauth from AP + */ + + stads->mlmStaContext.mlmState = eLIM_MLM_WT_DEL_STA_RSP_STATE; + + /* Send Disassociate frame to peer entity */ + if (send_disassoc_frame && (mlm_disassocreq->reasonCode != + eSIR_MAC_DISASSOC_DUE_TO_FTHANDOFF_REASON)) { + if (mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq) { + pe_err("pMlmDisassocReq is not NULL, freeing"); + qdf_mem_free(mac_ctx->lim.limDisassocDeauthCnfReq. + pMlmDisassocReq); + } + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq = + mlm_disassocreq; + /* + * Set state to mlm State to eLIM_MLM_WT_DEL_STA_RSP_STATE + * This is to address the issue of race condition between + * disconnect request from the HDD and deauth from AP + */ + stads->mlmStaContext.mlmState = eLIM_MLM_WT_DEL_STA_RSP_STATE; + + lim_send_disassoc_mgmt_frame(mac_ctx, + mlm_disassocreq->reasonCode, + mlm_disassocreq->peer_macaddr.bytes, session, true); + /* + * Abort Tx so that data frames won't be sent to the AP + * after sending Disassoc. + */ + if (LIM_IS_STA_ROLE(session)) + wma_tx_abort(session->smeSessionId); + } else { + /* Disassoc frame is not sent OTA */ + send_disassoc_frame = 1; + /* Receive path cleanup with dummy packet */ + if (QDF_STATUS_SUCCESS != + lim_cleanup_rx_path(mac_ctx, stads, session)) { + mlm_disassoccnf.resultCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + goto end; + } + /* Free up buffer allocated for mlmDisassocReq */ + qdf_mem_free(mlm_disassocreq); + } + + return; + +end: + qdf_mem_copy((uint8_t *) &mlm_disassoccnf.peerMacAddr, + (uint8_t *) mlm_disassocreq->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE); + mlm_disassoccnf.aid = mlm_disassocreq->aid; + mlm_disassoccnf.disassocTrigger = mlm_disassocreq->disassocTrigger; + + /* Update PE session ID */ + mlm_disassoccnf.sessionId = mlm_disassocreq->sessionId; + + /* Free up buffer allocated for mlmDisassocReq */ + qdf_mem_free(mlm_disassocreq); + + lim_post_sme_message(mac_ctx, LIM_MLM_DISASSOC_CNF, + (uint32_t *) &mlm_disassoccnf); +} + +/** + * lim_check_disassoc_deauth_ack_pending() - check if deauth is pending + * + * @mac_ctx - global MAC context + * @sta_mac - station MAC + * + * This function checks if diassociation or deauthentication is pending for + * given station MAC address. + * + * Return: true if pending and false otherwise. + */ +bool lim_check_disassoc_deauth_ack_pending(struct mac_context *mac_ctx, + uint8_t *sta_mac) +{ + tLimMlmDisassocReq *disassoc_req; + tLimMlmDeauthReq *deauth_req; + + disassoc_req = mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq; + deauth_req = mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq; + if ((disassoc_req && (!qdf_mem_cmp((uint8_t *) sta_mac, + (uint8_t *) &disassoc_req->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE))) || + (deauth_req && (!qdf_mem_cmp((uint8_t *) sta_mac, + (uint8_t *) &deauth_req->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE)))) { + pe_debug("Disassoc/Deauth ack pending"); + return true; + } else { + pe_debug("Disassoc/Deauth Ack not pending"); + return false; + } +} + +/* + * lim_clean_up_disassoc_deauth_req() - cleans up pending disassoc or deauth req + * + * @mac_ctx: mac_ctx + * @sta_mac: sta mac address + * @clean_rx_path: flag to indicate whether to cleanup rx path or not + * + * This function cleans up pending disassoc or deauth req + * + * Return: void + */ +void lim_clean_up_disassoc_deauth_req(struct mac_context *mac_ctx, + uint8_t *sta_mac, bool clean_rx_path) +{ + tLimMlmDisassocReq *mlm_disassoc_req; + tLimMlmDeauthReq *mlm_deauth_req; + + mlm_disassoc_req = mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq; + if (mlm_disassoc_req && + (!qdf_mem_cmp((uint8_t *) sta_mac, + (uint8_t *) &mlm_disassoc_req->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE))) { + if (clean_rx_path) { + lim_process_disassoc_ack_timeout(mac_ctx); + } else { + if (tx_timer_running( + &mac_ctx->lim.lim_timers.gLimDisassocAckTimer)) { + lim_deactivate_and_change_timer(mac_ctx, + eLIM_DISASSOC_ACK_TIMER); + } + qdf_mem_free(mlm_disassoc_req); + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq = + NULL; + } + } + + mlm_deauth_req = mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq; + if (mlm_deauth_req && + (!qdf_mem_cmp((uint8_t *) sta_mac, + (uint8_t *) &mlm_deauth_req->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE))) { + if (clean_rx_path) { + lim_process_deauth_ack_timeout(mac_ctx); + } else { + if (tx_timer_running( + &mac_ctx->lim.lim_timers.gLimDeauthAckTimer)) { + lim_deactivate_and_change_timer(mac_ctx, + eLIM_DEAUTH_ACK_TIMER); + } + qdf_mem_free(mlm_deauth_req); + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq = + NULL; + } + } +} + +/* + * lim_process_disassoc_ack_timeout() - wrapper function around + * lim_send_disassoc_cnf + * + * @mac_ctx: mac_ctx + * + * wrapper function around lim_send_disassoc_cnf + * + * Return: void + */ +void lim_process_disassoc_ack_timeout(struct mac_context *mac_ctx) +{ + lim_send_disassoc_cnf(mac_ctx); +} + +/** + * lim_process_mlm_disassoc_req() - This function is called to process + * MLM_DISASSOC_REQ message from SME + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: A pointer to the MLM message buffer + * + * This function is called to process MLM_DISASSOC_REQ message from SME + * + * @Return: None + */ +static void +lim_process_mlm_disassoc_req(struct mac_context *mac_ctx, uint32_t *msg_buf) +{ + tLimMlmDisassocReq *mlm_disassoc_req; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + mlm_disassoc_req = (tLimMlmDisassocReq *) msg_buf; + lim_process_mlm_disassoc_req_ntf(mac_ctx, QDF_STATUS_SUCCESS, + (uint32_t *) msg_buf); +} + +/** + * lim_process_mlm_deauth_req_ntf() - This function is process mlm deauth req + * notification + * + * @mac_ctx: Pointer to Global MAC structure + * @suspend_status: suspend status + * @msg_buf: A pointer to the MLM message buffer + * + * This function is process mlm deauth req notification + * + * @Return: None + */ +static void +lim_process_mlm_deauth_req_ntf(struct mac_context *mac_ctx, + QDF_STATUS suspend_status, uint32_t *msg_buf) +{ + uint16_t aid; + tSirMacAddr curr_bssId; + tpDphHashNode sta_ds; + struct tLimPreAuthNode *auth_node; + tLimMlmDeauthReq *mlm_deauth_req; + tLimMlmDeauthCnf mlm_deauth_cnf; + struct pe_session *session; + struct deauth_rsp *sme_deauth_rsp; + + if (QDF_STATUS_SUCCESS != suspend_status) + pe_err("Suspend Status is not success %X", + suspend_status); + + mlm_deauth_req = (tLimMlmDeauthReq *) msg_buf; + session = pe_find_session_by_session_id(mac_ctx, + mlm_deauth_req->sessionId); + if (!session) { + pe_err("session does not exist for given sessionId %d", + mlm_deauth_req->sessionId); + qdf_mem_free(mlm_deauth_req); + return; + } + sir_copy_mac_addr(curr_bssId, session->bssId); + + switch (GET_LIM_SYSTEM_ROLE(session)) { + case eLIM_STA_ROLE: + switch (session->limMlmState) { + case eLIM_MLM_IDLE_STATE: + /* + * Attempting to Deauthenticate with a pre-authenticated + * peer. Deauthetiate with peer if there exists a + * pre-auth context below. + */ + break; + case eLIM_MLM_AUTHENTICATED_STATE: + case eLIM_MLM_WT_ASSOC_RSP_STATE: + case eLIM_MLM_LINK_ESTABLISHED_STATE: + if (qdf_mem_cmp(mlm_deauth_req->peer_macaddr.bytes, + curr_bssId, QDF_MAC_ADDR_SIZE)) { + pe_err("received MLM_DEAUTH_REQ with invalid BSS id " + "Peer MAC: "QDF_MAC_ADDR_FMT + " CFG BSSID Addr : "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF( + mlm_deauth_req->peer_macaddr.bytes), + QDF_MAC_ADDR_REF(curr_bssId)); + /* + * Deauthentication response to host triggered + * deauthentication + */ + sme_deauth_rsp = + qdf_mem_malloc(sizeof(*sme_deauth_rsp)); + if (!sme_deauth_rsp) { + qdf_mem_free(mlm_deauth_req); + return; + } + + sme_deauth_rsp->messageType = + eWNI_SME_DEAUTH_RSP; + sme_deauth_rsp->length = + sizeof(*sme_deauth_rsp); + sme_deauth_rsp->status_code = + eSIR_SME_DEAUTH_STATUS; + sme_deauth_rsp->sessionId = + mlm_deauth_req->sessionId; + + qdf_mem_copy(sme_deauth_rsp->peer_macaddr.bytes, + mlm_deauth_req->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE); + + msg_buf = (uint32_t *)sme_deauth_rsp; + + lim_send_sme_disassoc_deauth_ntf(mac_ctx, + QDF_STATUS_SUCCESS, msg_buf); + qdf_mem_free(mlm_deauth_req); + return; + } + + if ((session->limMlmState == + eLIM_MLM_AUTHENTICATED_STATE) || + (session->limMlmState == + eLIM_MLM_WT_ASSOC_RSP_STATE)) { + /* Send deauth frame to peer entity */ + lim_send_deauth_mgmt_frame(mac_ctx, + mlm_deauth_req->reasonCode, + mlm_deauth_req->peer_macaddr.bytes, + session, false); + /* Prepare and Send LIM_MLM_DEAUTH_CNF */ + mlm_deauth_cnf.resultCode = eSIR_SME_SUCCESS; + session->limMlmState = eLIM_MLM_IDLE_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session->peSessionId, + session->limMlmState)); + goto end; + } + break; + default: + pe_warn("received MLM_DEAUTH_REQ with in state %d for peer " + QDF_MAC_ADDR_FMT, + session->limMlmState, + QDF_MAC_ADDR_REF( + mlm_deauth_req->peer_macaddr.bytes)); + lim_print_mlm_state(mac_ctx, LOGW, + session->limMlmState); + /* Prepare and Send LIM_MLM_DEAUTH_CNF */ + mlm_deauth_cnf.resultCode = + eSIR_SME_STA_NOT_AUTHENTICATED; + + goto end; + } + break; + case eLIM_STA_IN_IBSS_ROLE: + pe_err("received MLM_DEAUTH_REQ IBSS Mode"); + mlm_deauth_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + goto end; + default: + break; + } /* end switch (GET_LIM_SYSTEM_ROLE(session)) */ + + /* + * Check if there exists a context for the peer entity + * to be deauthenticated with. + */ + sta_ds = dph_lookup_hash_entry(mac_ctx, + mlm_deauth_req->peer_macaddr.bytes, + &aid, &session->dph.dphHashTable); + + if (!sta_ds) { + /* Check if there exists pre-auth context for this STA */ + auth_node = lim_search_pre_auth_list(mac_ctx, + mlm_deauth_req->peer_macaddr.bytes); + if (!auth_node) { + /* + * Received DEAUTH REQ for a STA that is neither + * Associated nor Pre-authenticated. Log error, + * Prepare and Send LIM_MLM_DEAUTH_CNF + */ + pe_warn("received MLM_DEAUTH_REQ in mlme state %d for STA that " + "does not have context, Addr=" + QDF_MAC_ADDR_FMT, + session->limMlmState, + QDF_MAC_ADDR_REF( + mlm_deauth_req->peer_macaddr.bytes)); + mlm_deauth_cnf.resultCode = + eSIR_SME_STA_NOT_AUTHENTICATED; + } else { + mlm_deauth_cnf.resultCode = eSIR_SME_SUCCESS; + /* Delete STA from pre-auth STA list */ + lim_delete_pre_auth_node(mac_ctx, + mlm_deauth_req->peer_macaddr.bytes); + /* Send Deauthentication frame to peer entity */ + lim_send_deauth_mgmt_frame(mac_ctx, + mlm_deauth_req->reasonCode, + mlm_deauth_req->peer_macaddr.bytes, + session, false); + } + goto end; + } else if ((sta_ds->mlmStaContext.mlmState != + eLIM_MLM_LINK_ESTABLISHED_STATE) && + (sta_ds->mlmStaContext.mlmState != + eLIM_MLM_WT_ASSOC_CNF_STATE)) { + /* + * received MLM_DEAUTH_REQ for STA that either has no context or + * in some transit state + */ + pe_warn("Invalid MLM_DEAUTH_REQ, Addr="QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mlm_deauth_req->peer_macaddr.bytes)); + /* Prepare and Send LIM_MLM_DEAUTH_CNF */ + mlm_deauth_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + /* sta_ds->mlmStaContext.rxPurgeReq = 1; */ + sta_ds->mlmStaContext.disassocReason = (tSirMacReasonCodes) + mlm_deauth_req->reasonCode; + sta_ds->mlmStaContext.cleanupTrigger = mlm_deauth_req->deauthTrigger; + + if (mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq) { + pe_err("pMlmDeauthReq is not NULL, freeing"); + qdf_mem_free(mac_ctx->lim.limDisassocDeauthCnfReq. + pMlmDeauthReq); + } + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq = mlm_deauth_req; + + /* + * Set state to mlm State to eLIM_MLM_WT_DEL_STA_RSP_STATE + * This is to address the issue of race condition between + * disconnect request from the HDD and disassoc from + * inactivity timer. This will make sure that we will not + * process disassoc if deauth is in progress for the station + * and thus mlmStaContext.cleanupTrigger will not be overwritten. + */ + sta_ds->mlmStaContext.mlmState = eLIM_MLM_WT_DEL_STA_RSP_STATE; + /* Send Deauthentication frame to peer entity */ + lim_send_deauth_mgmt_frame(mac_ctx, mlm_deauth_req->reasonCode, + mlm_deauth_req->peer_macaddr.bytes, + session, true); + return; +end: + qdf_copy_macaddr(&mlm_deauth_cnf.peer_macaddr, + &mlm_deauth_req->peer_macaddr); + mlm_deauth_cnf.deauthTrigger = mlm_deauth_req->deauthTrigger; + mlm_deauth_cnf.aid = mlm_deauth_req->aid; + mlm_deauth_cnf.sessionId = mlm_deauth_req->sessionId; + + /* Free up buffer allocated for mlmDeauthReq */ + qdf_mem_free(mlm_deauth_req); + lim_post_sme_message(mac_ctx, + LIM_MLM_DEAUTH_CNF, (uint32_t *) &mlm_deauth_cnf); +} + +/* + * lim_process_deauth_ack_timeout() - wrapper function around + * lim_send_deauth_cnf + * + * @mac_ctx: mac_ctx + * + * wrapper function around lim_send_deauth_cnf + * + * Return: void + */ +void lim_process_deauth_ack_timeout(struct mac_context *mac_ctx) +{ + lim_send_deauth_cnf(mac_ctx); +} + +/* + * lim_process_mlm_deauth_req() - This function is called to process + * MLM_DEAUTH_REQ message from SME + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: A pointer to the MLM message buffer + * + * This function is called to process MLM_DEAUTH_REQ message from SME + * + * @Return: None + */ +void lim_process_mlm_deauth_req(struct mac_context *mac_ctx, uint32_t *msg_buf) +{ + tLimMlmDeauthReq *mlm_deauth_req; + struct pe_session *session; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + mlm_deauth_req = (tLimMlmDeauthReq *) msg_buf; + session = pe_find_session_by_session_id(mac_ctx, + mlm_deauth_req->sessionId); + if (!session) { + pe_err("session does not exist for given sessionId %d", + mlm_deauth_req->sessionId); + qdf_mem_free(mlm_deauth_req); + return; + } + lim_process_mlm_deauth_req_ntf(mac_ctx, QDF_STATUS_SUCCESS, + (uint32_t *) msg_buf); +} + +void lim_process_join_failure_timeout(struct mac_context *mac_ctx) +{ + tLimMlmJoinCnf mlm_join_cnf; + uint32_t len; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + host_log_rssi_pkt_type *rssi_log = NULL; +#endif + struct pe_session *session; + + session = pe_find_session_by_session_id(mac_ctx, + mac_ctx->lim.lim_timers.gLimJoinFailureTimer.sessionId); + if (!session) { + pe_err("Session Does not exist for given sessionID"); + return; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + WLAN_HOST_DIAG_LOG_ALLOC(rssi_log, + host_log_rssi_pkt_type, LOG_WLAN_RSSI_UPDATE_C); + if (rssi_log) + rssi_log->rssi = session->rssi; + WLAN_HOST_DIAG_LOG_REPORT(rssi_log); +#endif + + if (session->limMlmState == eLIM_MLM_WT_JOIN_BEACON_STATE) { + len = sizeof(tSirMacAddr); + /* Change timer for future activations */ + lim_deactivate_and_change_timer(mac_ctx, eLIM_JOIN_FAIL_TIMER); + /* Change Periodic probe req timer for future activation */ + lim_deactivate_and_change_timer(mac_ctx, + eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER); + /* Issue MLM join confirm with timeout reason code */ + pe_err("Join Failure Timeout, In eLIM_MLM_WT_JOIN_BEACON_STATE session:%d " + QDF_MAC_ADDR_FMT, + session->peSessionId, + QDF_MAC_ADDR_REF(session->bssId)); + + mlm_join_cnf.resultCode = eSIR_SME_JOIN_TIMEOUT_RESULT_CODE; + mlm_join_cnf.protStatusCode = STATUS_NO_NETWORK_FOUND; + session->limMlmState = eLIM_MLM_IDLE_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session->peSessionId, session->limMlmState)); + /* Update PE session Id */ + mlm_join_cnf.sessionId = session->peSessionId; + /* Freeup buffer allocated to join request */ + if (session->pLimMlmJoinReq) { + qdf_mem_free(session->pLimMlmJoinReq); + session->pLimMlmJoinReq = NULL; + } + lim_post_sme_message(mac_ctx, LIM_MLM_JOIN_CNF, + (uint32_t *) &mlm_join_cnf); + return; + } else { + pe_warn("received unexpected JOIN failure timeout in state %X", + session->limMlmState); + lim_print_mlm_state(mac_ctx, LOGW, session->limMlmState); + } +} + +/** + * lim_process_periodic_join_probe_req_timer() - This function is called to + * process periodic probe request send during joining process. + * + * @mac_ctx: Pointer to Global MAC structure + * + * This function is called to process periodic probe request send during + * joining process. + * + * @Return None + */ +static void lim_process_periodic_join_probe_req_timer(struct mac_context *mac_ctx) +{ + struct pe_session *session; + tSirMacSSid ssid; + + session = pe_find_session_by_session_id(mac_ctx, + mac_ctx->lim.lim_timers.gLimPeriodicJoinProbeReqTimer.sessionId); + if (!session) { + pe_err("session does not exist for given SessionId: %d", + mac_ctx->lim.lim_timers.gLimPeriodicJoinProbeReqTimer. + sessionId); + return; + } + + if ((true == + tx_timer_running(&mac_ctx->lim.lim_timers.gLimJoinFailureTimer)) + && (session->limMlmState == eLIM_MLM_WT_JOIN_BEACON_STATE)) { + qdf_mem_copy(ssid.ssId, session->ssId.ssId, + session->ssId.length); + ssid.length = session->ssId.length; + lim_send_probe_req_mgmt_frame(mac_ctx, &ssid, + session->pLimMlmJoinReq->bssDescription.bssId, + session->curr_op_freq, + session->self_mac_addr, session->dot11mode, + &session->lim_join_req->addIEScan.length, + session->lim_join_req->addIEScan.addIEdata); + lim_deactivate_and_change_timer(mac_ctx, + eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER); + /* Activate Join Periodic Probe Req timer */ + if (tx_timer_activate( + &mac_ctx->lim.lim_timers.gLimPeriodicJoinProbeReqTimer) != + TX_SUCCESS) { + pe_warn("could not activate Periodic Join req failure timer"); + return; + } + } +} + +static void lim_handle_sae_auth_timeout(struct mac_context *mac_ctx, + struct pe_session *session_entry) +{ + struct sae_auth_retry *sae_retry; + + sae_retry = mlme_get_sae_auth_retry(session_entry->vdev); + if (!(sae_retry && sae_retry->sae_auth.data)) { + pe_debug("sae auth frame is not buffered vdev id %d", + session_entry->vdev_id); + return; + } + + pe_debug("retry sae auth for seq num %d vdev id %d", + mac_ctx->mgmtSeqNum, session_entry->vdev_id); + lim_send_frame(mac_ctx, session_entry->vdev_id, + sae_retry->sae_auth.data, sae_retry->sae_auth.len); + + sae_retry->sae_auth_max_retry--; + /* Activate Auth Retry timer if max_retries are not done */ + if (!sae_retry->sae_auth_max_retry || (tx_timer_activate( + &mac_ctx->lim.lim_timers.g_lim_periodic_auth_retry_timer) != + TX_SUCCESS)) + goto free_and_deactivate_timer; + + return; + +free_and_deactivate_timer: + lim_sae_auth_cleanup_retry(mac_ctx, session_entry->vdev_id); +} + +/** + * lim_process_auth_retry_timer()- function to Retry Auth when auth timeout + * occurs + * @mac_ctx:pointer to global mac + * + * Return: void + */ +static void lim_process_auth_retry_timer(struct mac_context *mac_ctx) +{ + struct pe_session *session_entry; + tAniAuthType auth_type; + tLimTimers *lim_timers = &mac_ctx->lim.lim_timers; + uint16_t pe_session_id = + lim_timers->g_lim_periodic_auth_retry_timer.sessionId; + + session_entry = pe_find_session_by_session_id(mac_ctx, pe_session_id); + if (!session_entry) { + pe_err("session does not exist for pe_session_id: %d", + pe_session_id); + return; + } + + /** For WPA3 SAE gLimAuthFailureTimer is not running hence + * we don't enter in below "if" block in case of wpa3 sae + */ + if (tx_timer_running(&mac_ctx->lim.lim_timers.gLimAuthFailureTimer) && + (session_entry->limMlmState == eLIM_MLM_WT_AUTH_FRAME2_STATE) && + (LIM_ACK_RCD_SUCCESS != mac_ctx->auth_ack_status)) { + tSirMacAuthFrameBody auth_frame; + + /* + * Send the auth retry only in case we have received ack failure + * else just restart the retry timer. + */ + if (((mac_ctx->auth_ack_status == LIM_ACK_RCD_FAILURE) || + (mac_ctx->auth_ack_status == LIM_TX_FAILED)) && + mac_ctx->lim.gpLimMlmAuthReq) { + auth_type = mac_ctx->lim.gpLimMlmAuthReq->authType; + + /* Prepare & send Authentication frame */ + if (session_entry->sae_pmk_cached && + auth_type == eSIR_AUTH_TYPE_SAE) + auth_frame.authAlgoNumber = eSIR_OPEN_SYSTEM; + else + auth_frame.authAlgoNumber = (uint8_t)auth_type; + + auth_frame.authTransactionSeqNumber = + SIR_MAC_AUTH_FRAME_1; + auth_frame.authStatusCode = 0; + pe_debug("Retry Auth"); + mac_ctx->auth_ack_status = LIM_ACK_NOT_RCD; + lim_increase_fils_sequence_number(session_entry); + lim_send_auth_mgmt_frame(mac_ctx, &auth_frame, + mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr, + LIM_NO_WEP_IN_FC, session_entry); + } + + lim_deactivate_and_change_timer(mac_ctx, eLIM_AUTH_RETRY_TIMER); + + /* Activate Auth Retry timer */ + if (tx_timer_activate + (&mac_ctx->lim.lim_timers.g_lim_periodic_auth_retry_timer) + != TX_SUCCESS) + pe_err("could not activate Auth Retry failure timer"); + + return; + } + + /* Auth retry time out for wpa3 sae */ + lim_handle_sae_auth_timeout(mac_ctx, session_entry); +} /*** lim_process_auth_retry_timer() ***/ + +void lim_process_auth_failure_timeout(struct mac_context *mac_ctx) +{ + /* fetch the pe_session based on the sessionId */ + struct pe_session *session; + uint32_t val; + enum wlan_status_code proto_status_code; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + host_log_rssi_pkt_type *rssi_log = NULL; +#endif + + session = pe_find_session_by_session_id(mac_ctx, + mac_ctx->lim.lim_timers.gLimAuthFailureTimer.sessionId); + if (!session) { + pe_err("Session Does not exist for given sessionID"); + return; + } + + pe_warn("received AUTH failure timeout in sessionid %d " + "limMlmstate %X limSmeState %X", + session->peSessionId, session->limMlmState, + session->limSmeState); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_AUTH_TIMEOUT, session, + 0, AUTH_FAILURE_TIMEOUT); + + WLAN_HOST_DIAG_LOG_ALLOC(rssi_log, host_log_rssi_pkt_type, + LOG_WLAN_RSSI_UPDATE_C); + if (rssi_log) + rssi_log->rssi = session->rssi; + WLAN_HOST_DIAG_LOG_REPORT(rssi_log); +#endif + + switch (session->limMlmState) { + case eLIM_MLM_WT_AUTH_FRAME2_STATE: + case eLIM_MLM_WT_AUTH_FRAME4_STATE: + /* + * Requesting STA did not receive next auth frame before Auth + * Failure timeout. Issue MLM auth confirm with timeout reason + * code. Restore default failure timeout + */ + if (QDF_P2P_CLIENT_MODE == session->opmode && + session->defaultAuthFailureTimeout) { + if (cfg_in_range(CFG_AUTH_FAILURE_TIMEOUT, + session->defaultAuthFailureTimeout)) { + val = session->defaultAuthFailureTimeout; + } else { + val = cfg_default(CFG_AUTH_FAILURE_TIMEOUT); + session->defaultAuthFailureTimeout = val; + } + mac_ctx->mlme_cfg->timeouts.auth_failure_timeout = val; + } + lim_fill_status_code(SIR_MAC_MGMT_AUTH, + mac_ctx->auth_ack_status, + &proto_status_code); + + lim_restore_from_auth_state(mac_ctx, + eSIR_SME_AUTH_TIMEOUT_RESULT_CODE, + proto_status_code, session); + break; + default: + /* + * Auth failure timer should not have timed out + * in states other than wt_auth_frame2/4 + */ + pe_err("received unexpected AUTH failure timeout in state %X", + session->limMlmState); + lim_print_mlm_state(mac_ctx, LOGE, session->limMlmState); + break; + } +} + +/** + * lim_process_auth_rsp_timeout() - This function is called to process Min + * Channel Timeout during channel scan. + * + * @mac_ctx: Pointer to Global MAC structure + * + * This function is called to process Min Channel Timeout during channel scan. + * + * @Return: None + */ +static void +lim_process_auth_rsp_timeout(struct mac_context *mac_ctx, uint32_t auth_idx) +{ + struct tLimPreAuthNode *auth_node; + struct pe_session *session; + uint8_t session_id; + + auth_node = lim_get_pre_auth_node_from_index(mac_ctx, + &mac_ctx->lim.gLimPreAuthTimerTable, auth_idx); + if (!auth_node) { + pe_warn("Invalid auth node"); + return; + } + + session = pe_find_session_by_bssid(mac_ctx, auth_node->peerMacAddr, + &session_id); + if (!session) { + pe_warn("session does not exist for given BSSID"); + return; + } + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_AUTH_TIMEOUT, + session, 0, AUTH_RESPONSE_TIMEOUT); +#endif + + if (LIM_IS_AP_ROLE(session) || LIM_IS_IBSS_ROLE(session)) { + if (auth_node->mlmState != eLIM_MLM_WT_AUTH_FRAME3_STATE) { + pe_err("received AUTH rsp timeout in unexpected " + "state for MAC address: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(auth_node->peerMacAddr)); + } else { + auth_node->mlmState = eLIM_MLM_AUTH_RSP_TIMEOUT_STATE; + auth_node->fTimerStarted = 0; + pe_debug("AUTH rsp timedout for MAC address " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(auth_node->peerMacAddr)); + /* Change timer to reactivate it in future */ + lim_deactivate_and_change_per_sta_id_timer(mac_ctx, + eLIM_AUTH_RSP_TIMER, auth_node->authNodeIdx); + lim_delete_pre_auth_node(mac_ctx, + auth_node->peerMacAddr); + } + } +} + +void lim_process_assoc_failure_timeout(struct mac_context *mac_ctx, + uint32_t msg_type) +{ + tLimMlmAssocCnf mlm_assoc_cnf; + struct pe_session *session; + enum wlan_status_code proto_status_code; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + host_log_rssi_pkt_type *rssi_log = NULL; +#endif + /* + * to fetch the lim/mlm state based on the session_id, use the + * below pe_session + */ + uint8_t session_id; + + if (msg_type == LIM_ASSOC) + session_id = + mac_ctx->lim.lim_timers.gLimAssocFailureTimer.sessionId; + else + session_id = + mac_ctx->lim.lim_timers.gLimReassocFailureTimer.sessionId; + + session = pe_find_session_by_session_id(mac_ctx, session_id); + if (!session) { + pe_err("Session Does not exist for given sessionID"); + return; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_ASSOC_TIMEOUT, + session, 0, 0); + + WLAN_HOST_DIAG_LOG_ALLOC(rssi_log, + host_log_rssi_pkt_type, + LOG_WLAN_RSSI_UPDATE_C); + if (rssi_log) + rssi_log->rssi = session->rssi; + WLAN_HOST_DIAG_LOG_REPORT(rssi_log); +#endif + + pe_debug("Re/Association Response not received before timeout"); + + /* + * Send Deauth to handle the scenareo where association timeout happened + * when device has missed the assoc resp sent by peer. + * By sending deauth try to clear the session created on peer device. + */ + if (msg_type == LIM_ASSOC && + mlme_get_reconn_after_assoc_timeout_flag(mac_ctx->psoc, + session->vdev_id)) { + pe_debug("vdev: %d skip sending deauth on channel freq %d to BSSID: " + QDF_MAC_ADDR_FMT, session->vdev_id, + session->curr_op_freq, + QDF_MAC_ADDR_REF(session->bssId)); + } else { + pe_debug("vdev: %d try sending deauth on channel freq %d to BSSID: " + QDF_MAC_ADDR_FMT, session->vdev_id, + session->curr_op_freq, + QDF_MAC_ADDR_REF(session->bssId)); + lim_send_deauth_mgmt_frame(mac_ctx, + eSIR_MAC_UNSPEC_FAILURE_REASON, + session->bssId, session, false); + } + if ((LIM_IS_AP_ROLE(session)) || + ((session->limMlmState != eLIM_MLM_WT_ASSOC_RSP_STATE) && + (session->limMlmState != eLIM_MLM_WT_REASSOC_RSP_STATE) && + (session->limMlmState != eLIM_MLM_WT_FT_REASSOC_RSP_STATE))) { + /* + * Re/Assoc failure timer should not have timedout on AP + * or in a state other than wt_re/assoc_response. + */ + pe_warn("received unexpected REASSOC failure timeout in state %X for role %d", + session->limMlmState, + GET_LIM_SYSTEM_ROLE(session)); + lim_print_mlm_state(mac_ctx, LOGW, session->limMlmState); + return; + } + + if ((msg_type == LIM_ASSOC) || ((msg_type == LIM_REASSOC) + && (session->limMlmState == eLIM_MLM_WT_FT_REASSOC_RSP_STATE))) { + pe_err("(Re)Assoc Failure Timeout occurred"); + session->limMlmState = eLIM_MLM_IDLE_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session->peSessionId, session->limMlmState)); + /* Change timer for future activations */ + lim_deactivate_and_change_timer(mac_ctx, eLIM_ASSOC_FAIL_TIMER); + lim_stop_pmfcomeback_timer(session); + /* + * Free up buffer allocated for JoinReq held by + * MLM state machine + */ + if (session->pLimMlmJoinReq) { + qdf_mem_free(session->pLimMlmJoinReq); + session->pLimMlmJoinReq = NULL; + } + /* To remove the preauth node in case of fail to associate */ + if (lim_search_pre_auth_list(mac_ctx, session->bssId)) { + pe_debug("delete pre auth node for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(session->bssId)); + lim_delete_pre_auth_node(mac_ctx, + session->bssId); + } + lim_fill_status_code(SIR_MAC_MGMT_ASSOC_RSP, + mac_ctx->assoc_ack_status, + &proto_status_code); + + mlm_assoc_cnf.resultCode = eSIR_SME_ASSOC_TIMEOUT_RESULT_CODE; + mlm_assoc_cnf.protStatusCode = proto_status_code; + /* Update PE session Id */ + mlm_assoc_cnf.sessionId = session->peSessionId; + if (msg_type == LIM_ASSOC) { + lim_post_sme_message(mac_ctx, LIM_MLM_ASSOC_CNF, + (uint32_t *) &mlm_assoc_cnf); + } else { + /* + * Will come here only in case of 11r, Ese FT + * when reassoc rsp is not received and we + * receive a reassoc - timesout + */ + mlm_assoc_cnf.resultCode = + eSIR_SME_FT_REASSOC_TIMEOUT_FAILURE; + lim_post_sme_message(mac_ctx, LIM_MLM_REASSOC_CNF, + (uint32_t *) &mlm_assoc_cnf); + } + } else { + /* + * Restore pre-reassoc req state. + * Set BSSID to currently associated AP address. + */ + session->limMlmState = session->limPrevMlmState; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session->peSessionId, session->limMlmState)); + lim_restore_pre_reassoc_state(mac_ctx, + eSIR_SME_REASSOC_TIMEOUT_RESULT_CODE, + eSIR_MAC_UNSPEC_FAILURE_STATUS, session); + } +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c new file mode 100644 index 0000000000000000000000000000000000000000..6909b63d42eeacea7a8da6c369c38c42f9384a76 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c @@ -0,0 +1,3022 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "wni_api.h" +#include "wni_cfg.h" +#include "sir_api.h" +#include "sch_api.h" +#include "utils_api.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_timer_utils.h" +#include "lim_send_messages.h" +#include "lim_admit_control.h" +#include "lim_send_messages.h" +#include "lim_ibss_peer_mgmt.h" +#include "lim_ft.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "lim_session_utils.h" +#include "rrm_api.h" +#include "wma_types.h" +#include "cds_utils.h" +#include "lim_types.h" +#include "wlan_policy_mgr_api.h" +#include "nan_datapath.h" +#include "wlan_reg_services_api.h" +#include "wma.h" + +#define MAX_SUPPORTED_PEERS_WEP 16 + +void lim_process_mlm_join_cnf(struct mac_context *, uint32_t *); +void lim_process_mlm_auth_cnf(struct mac_context *, uint32_t *); +void lim_process_mlm_assoc_ind(struct mac_context *, uint32_t *); +void lim_process_mlm_assoc_cnf(struct mac_context *, uint32_t *); +void lim_process_mlm_set_keys_cnf(struct mac_context *, uint32_t *); +void lim_process_mlm_disassoc_ind(struct mac_context *, uint32_t *); +void lim_process_mlm_disassoc_cnf(struct mac_context *, uint32_t *); +static void lim_process_mlm_deauth_ind(struct mac_context *, tLimMlmDeauthInd *); +void lim_process_mlm_deauth_cnf(struct mac_context *, uint32_t *); +void lim_process_mlm_purge_sta_ind(struct mac_context *, uint32_t *); + +/** + * lim_process_mlm_rsp_messages() + * + ***FUNCTION: + * This function is called to processes various MLM response (CNF/IND + * messages from MLM State machine. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param msgType Indicates the MLM message type + * @param *msg_buf A pointer to the MLM message buffer + * + * @return None + */ +void +lim_process_mlm_rsp_messages(struct mac_context *mac, uint32_t msgType, + uint32_t *msg_buf) +{ + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + MTRACE(mac_trace(mac, TRACE_CODE_TX_LIM_MSG, 0, msgType)); + switch (msgType) { + case LIM_MLM_AUTH_CNF: + lim_process_mlm_auth_cnf(mac, msg_buf); + break; + case LIM_MLM_ASSOC_CNF: + lim_process_mlm_assoc_cnf(mac, msg_buf); + break; + case LIM_MLM_START_CNF: + lim_process_mlm_start_cnf(mac, msg_buf); + break; + case LIM_MLM_JOIN_CNF: + lim_process_mlm_join_cnf(mac, msg_buf); + break; + case LIM_MLM_ASSOC_IND: + lim_process_mlm_assoc_ind(mac, msg_buf); + break; + case LIM_MLM_REASSOC_CNF: + lim_process_mlm_reassoc_cnf(mac, msg_buf); + break; + case LIM_MLM_DISASSOC_CNF: + lim_process_mlm_disassoc_cnf(mac, msg_buf); + break; + case LIM_MLM_DISASSOC_IND: + lim_process_mlm_disassoc_ind(mac, msg_buf); + break; + case LIM_MLM_PURGE_STA_IND: + lim_process_mlm_purge_sta_ind(mac, msg_buf); + break; + case LIM_MLM_DEAUTH_CNF: + lim_process_mlm_deauth_cnf(mac, msg_buf); + break; + case LIM_MLM_DEAUTH_IND: + lim_process_mlm_deauth_ind(mac, (tLimMlmDeauthInd *)msg_buf); + break; + case LIM_MLM_SETKEYS_CNF: + lim_process_mlm_set_keys_cnf(mac, msg_buf); + break; + case LIM_MLM_TSPEC_CNF: + break; + default: + break; + } /* switch (msgType) */ + return; +} /*** end lim_process_mlm_rsp_messages() ***/ + +/** + * lim_process_mlm_start_cnf() + * + ***FUNCTION: + * This function is called to processes MLM_START_CNF + * message from MLM State machine. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param msg_buf A pointer to the MLM message buffer + * + * @return None + */ +void lim_process_mlm_start_cnf(struct mac_context *mac, uint32_t *msg_buf) +{ + struct pe_session *pe_session = NULL; + tLimMlmStartCnf *pLimMlmStartCnf; + uint8_t smesessionId; + uint32_t chan_freq, ch_cfreq1 = 0; + uint8_t send_bcon_ind = false; + enum reg_wifi_band band; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + pLimMlmStartCnf = (tLimMlmStartCnf *)msg_buf; + pe_session = pe_find_session_by_session_id(mac, + pLimMlmStartCnf->sessionId); + if (!pe_session) { + pe_err("Session does Not exist with given sessionId"); + return; + } + smesessionId = pe_session->smeSessionId; + + if (pe_session->limSmeState != eLIM_SME_WT_START_BSS_STATE) { + /* + * Should not have received Start confirm from MLM + * in other states. Log error. + */ + pe_err("received unexpected MLM_START_CNF in state %X", + pe_session->limSmeState); + return; + } + if (((tLimMlmStartCnf *)msg_buf)->resultCode == eSIR_SME_SUCCESS) { + + /* + * Update global SME state so that Beacon Generation + * module starts writing Beacon frames into TFP's + * Beacon file register. + */ + pe_session->limSmeState = eLIM_SME_NORMAL_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_SME_STATE, pe_session->peSessionId, + pe_session->limSmeState)); + if (pe_session->bssType == eSIR_INFRA_AP_MODE) + pe_debug("*** Started BSS in INFRA AP SIDE***"); + else if (pe_session->bssType == eSIR_NDI_MODE) + pe_debug("*** Started BSS in NDI mode ***"); + else + pe_debug("*** Started BSS ***"); + } else { + /* Start BSS is a failure */ + pe_delete_session(mac, pe_session); + pe_session = NULL; + pe_err("Start BSS Failed"); + } + /* Send response to Host */ + lim_send_sme_start_bss_rsp(mac, eWNI_SME_START_BSS_RSP, + ((tLimMlmStartCnf *)msg_buf)->resultCode, + pe_session, smesessionId); + if (pe_session && + (((tLimMlmStartCnf *)msg_buf)->resultCode == eSIR_SME_SUCCESS)) { + lim_ndi_mlme_vdev_up_transition(pe_session); + + /* We should start beacon transmission only if the channel + * on which we are operating is non-DFS until the channel + * availability check is done. The PE will receive an explicit + * request from upper layers to start the beacon transmission + */ + chan_freq = pe_session->curr_op_freq; + band = wlan_reg_freq_to_band(pe_session->curr_op_freq); + if (pe_session->ch_center_freq_seg1) + ch_cfreq1 = wlan_reg_chan_band_to_freq( + mac->pdev, + pe_session->ch_center_freq_seg1, + BIT(band)); + + if (!(LIM_IS_IBSS_ROLE(pe_session) || + (LIM_IS_AP_ROLE(pe_session)))) + return; + if (pe_session->ch_width == CH_WIDTH_160MHZ) { + send_bcon_ind = false; + } else if (pe_session->ch_width == CH_WIDTH_80P80MHZ) { + if ((wlan_reg_get_channel_state_for_freq( + mac->pdev, chan_freq) != + CHANNEL_STATE_DFS) && + (wlan_reg_get_channel_state_for_freq( + mac->pdev, ch_cfreq1) != + CHANNEL_STATE_DFS)) + send_bcon_ind = true; + } else { + if (wlan_reg_get_channel_state_for_freq(mac->pdev, + chan_freq) + != CHANNEL_STATE_DFS) + send_bcon_ind = true; + } + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(pe_session->curr_op_freq)) + send_bcon_ind = true; + + if (send_bcon_ind) { + /* Configure beacon and send beacons to HAL */ + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + "Start Beacon with ssid %s Ch freq %d", + pe_session->ssId.ssId, + pe_session->curr_op_freq); + lim_send_beacon(mac, pe_session); + lim_enable_obss_detection_config(mac, pe_session); + lim_send_obss_color_collision_cfg(mac, pe_session, + OBSS_COLOR_COLLISION_DETECTION); + } else { + lim_sap_move_to_cac_wait_state(pe_session); + } + } +} + +/*** end lim_process_mlm_start_cnf() ***/ + +/** + * lim_process_mlm_join_cnf() - Processes join confirmation + * @mac_ctx: Pointer to Global MAC structure + * @msg: A pointer to the MLM message buffer + * + * This Function handles the join confirmation message + * LIM_MLM_JOIN_CNF. + * + * Return: None + */ +void lim_process_mlm_join_cnf(struct mac_context *mac_ctx, + uint32_t *msg) +{ + tSirResultCodes result_code; + tLimMlmJoinCnf *join_cnf; + struct pe_session *session_entry; + + join_cnf = (tLimMlmJoinCnf *) msg; + session_entry = pe_find_session_by_session_id(mac_ctx, + join_cnf->sessionId); + if (!session_entry) { + pe_err("SessionId:%d does not exist", join_cnf->sessionId); + return; + } + + if (session_entry->limSmeState != eLIM_SME_WT_JOIN_STATE) { + pe_err("received unexpected MLM_JOIN_CNF in state %X", + session_entry->limSmeState); + return; + } + + result_code = ((tLimMlmJoinCnf *) msg)->resultCode; + /* Process Join confirm from MLM */ + if (result_code == eSIR_SME_SUCCESS) { + /* Setup hardware upfront */ + if (lim_sta_send_add_bss_pre_assoc(mac_ctx, + session_entry) == + QDF_STATUS_SUCCESS) + return; + else + result_code = eSIR_SME_REFUSED; + } + + /* Join failure */ + session_entry->limSmeState = eLIM_SME_JOIN_FAILURE_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + /* Send Join response to Host */ + lim_handle_sme_join_result(mac_ctx, result_code, + ((tLimMlmJoinCnf *) msg)->protStatusCode, session_entry); + return; +} + +/** + * lim_send_mlm_assoc_req() - Association request will be processed + * mac_ctx: Pointer to Global MAC structure + * session_entry: Pointer to session etnry + * + * This function is sends ASSOC request MLM message to MLM State machine. + * ASSOC request packet would be by picking parameters from pe_session + * automatically based on the current state of MLM state machine. + * ASSUMPTIONS: + * this function is called in middle of connection state machine and is + * expected to be called after auth cnf has been received or after ASSOC rsp + * with TRY_AGAIN_LATER was received and required time has elapsed after that. + * + * Return: None + */ + +static void lim_send_mlm_assoc_req(struct mac_context *mac_ctx, + struct pe_session *session_entry) +{ + tLimMlmAssocReq *assoc_req; + uint32_t val; + uint16_t caps; + uint32_t tele_bcn = 0; + tpSirMacCapabilityInfo cap_info; + + if (!session_entry->lim_join_req) { + pe_err("Join Request is NULL"); + /* No need to Assert. JOIN timeout will handle this error */ + return; + } + + assoc_req = qdf_mem_malloc(sizeof(tLimMlmAssocReq)); + if (!assoc_req) { + pe_err("call to AllocateMemory failed for mlmAssocReq"); + return; + } + val = sizeof(tSirMacAddr); + sir_copy_mac_addr(assoc_req->peerMacAddr, session_entry->bssId); + + if (lim_get_capability_info(mac_ctx, &caps, session_entry) + != QDF_STATUS_SUCCESS) + /* Could not get Capabilities value from CFG.*/ + pe_err("could not retrieve Capabilities value"); + + /* Clear spectrum management bit if AP doesn't support it */ + if (!(session_entry->lim_join_req->bssDescription.capabilityInfo & + LIM_SPECTRUM_MANAGEMENT_BIT_MASK)) + /* + * AP doesn't support spectrum management + * clear spectrum management bit + */ + caps &= (~LIM_SPECTRUM_MANAGEMENT_BIT_MASK); + + /* Clear rrm bit if AP doesn't support it */ + if (!(session_entry->lim_join_req->bssDescription.capabilityInfo & + LIM_RRM_BIT_MASK)) + caps &= (~LIM_RRM_BIT_MASK); + + /* Clear short preamble bit if AP does not support it */ + if (!(session_entry->lim_join_req->bssDescription.capabilityInfo & + (LIM_SHORT_PREAMBLE_BIT_MASK))) { + caps &= (~LIM_SHORT_PREAMBLE_BIT_MASK); + pe_debug("Clearing short preamble:no AP support"); + } + + /* Clear immediate block ack bit if AP does not support it */ + if (!(session_entry->lim_join_req->bssDescription.capabilityInfo & + (LIM_IMMEDIATE_BLOCK_ACK_MASK))) { + caps &= (~LIM_IMMEDIATE_BLOCK_ACK_MASK); + pe_debug("Clearing Immed Blk Ack:no AP support"); + } + + assoc_req->capabilityInfo = caps; + cap_info = ((tpSirMacCapabilityInfo) &assoc_req->capabilityInfo); + + /* + * If telescopic beaconing is enabled, set listen interval to + * CFG_TELE_BCN_MAX_LI + */ + tele_bcn = mac_ctx->mlme_cfg->sap_cfg.tele_bcn_wakeup_en; + if (tele_bcn) + val = mac_ctx->mlme_cfg->sap_cfg.tele_bcn_max_li; + else + val = mac_ctx->mlme_cfg->sap_cfg.listen_interval; + +#ifdef FEATURE_WLAN_DIAG_SUPPORT + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_ASSOC_REQ_EVENT, + session_entry, QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); +#endif + assoc_req->listenInterval = (uint16_t) val; + /* Update PE session ID */ + assoc_req->sessionId = session_entry->peSessionId; + session_entry->limPrevSmeState = session_entry->limSmeState; + session_entry->limSmeState = eLIM_SME_WT_ASSOC_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, session_entry->limSmeState)); + lim_post_mlm_message(mac_ctx, LIM_MLM_ASSOC_REQ, + (uint32_t *) assoc_req); +} + +#ifdef WLAN_FEATURE_11W +/** + * lim_pmf_comeback_timer_callback() -PMF callback handler + * @context: Timer context + * + * This function is called to processes the PMF comeback + * callback + * + * Return: None + */ +void lim_pmf_comeback_timer_callback(void *context) +{ + struct comeback_timer_info *info = + (struct comeback_timer_info *)context; + struct mac_context *mac_ctx = info->mac; + struct pe_session *session; + + session = pe_find_session_by_vdev_id(mac_ctx, info->vdev_id); + if (!session) { + pe_err("no session found for vdev %d", info->vdev_id); + return; + } + + pe_info("comeback later timer expired. sending MLM ASSOC req for vdev %d", + session->vdev_id); + /* set MLM state such that ASSOC REQ packet will be sent out */ + session->limPrevMlmState = info->lim_prev_mlm_state; + session->limMlmState = info->lim_mlm_state; + lim_send_mlm_assoc_req(mac_ctx, session); +} +#endif /* WLAN_FEATURE_11W */ + +/** + * lim_process_mlm_auth_cnf()-Process Auth confirmation + * @mac_ctx: Pointer to Global MAC structure + * @msg: A pointer to the MLM message buffer + * + * This function is called to processes MLM_AUTH_CNF + * message from MLM State machine. + * + * Return: None + */ +void lim_process_mlm_auth_cnf(struct mac_context *mac_ctx, uint32_t *msg) +{ + tAniAuthType auth_type, auth_mode; + tLimMlmAuthReq *auth_req; + tLimMlmAuthCnf *auth_cnf; + struct pe_session *session_entry; + + if (!msg) { + pe_err("Buffer is Pointing to NULL"); + return; + } + auth_cnf = (tLimMlmAuthCnf *) msg; + session_entry = pe_find_session_by_session_id(mac_ctx, + auth_cnf->sessionId); + if (!session_entry) { + pe_err("SessionId:%d session doesn't exist", + auth_cnf->sessionId); + return; + } + + if ((session_entry->limSmeState != eLIM_SME_WT_AUTH_STATE && + session_entry->limSmeState != eLIM_SME_WT_PRE_AUTH_STATE) || + LIM_IS_AP_ROLE(session_entry)) { + /** + * Should not have received AUTH confirm + * from MLM in other states or on AP. + * Log error + */ + pe_err("SessionId:%d received MLM_AUTH_CNF in state %X", + session_entry->peSessionId, session_entry->limSmeState); + return; + } + + if (auth_cnf->resultCode == eSIR_SME_SUCCESS) { + if (session_entry->limSmeState == eLIM_SME_WT_AUTH_STATE) { + lim_send_mlm_assoc_req(mac_ctx, session_entry); + } else { + /* + * Successful Pre-authentication. Send + * Pre-auth response to host + */ + session_entry->limSmeState = + session_entry->limPrevSmeState; + MTRACE(mac_trace + (mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + } + /* Return for success case */ + return; + } + /* + * Failure case handle: + * Process AUTH confirm from MLM + */ + if (session_entry->limSmeState == eLIM_SME_WT_AUTH_STATE) + auth_type = mac_ctx->mlme_cfg->wep_params.auth_type; + else + auth_type = mac_ctx->lim.gLimPreAuthType; + + if ((auth_type == eSIR_AUTO_SWITCH) && + (auth_cnf->authType == eSIR_SHARED_KEY) && + ((eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS == + auth_cnf->protStatusCode) || + (auth_cnf->resultCode == eSIR_SME_AUTH_TIMEOUT_RESULT_CODE))) { + /* + * When shared authentication fails with reason + * code "13" and authType set to 'auto switch', + * Try with open Authentication + */ + auth_mode = eSIR_OPEN_SYSTEM; + /* Trigger MAC based Authentication */ + auth_req = qdf_mem_malloc(sizeof(tLimMlmAuthReq)); + if (!auth_req) { + pe_err("mlmAuthReq :Memory alloc failed"); + return; + } + if (session_entry->limSmeState == + eLIM_SME_WT_AUTH_STATE) { + sir_copy_mac_addr(auth_req->peerMacAddr, + session_entry->bssId); + } else { + qdf_mem_copy((uint8_t *)&auth_req->peerMacAddr, + (uint8_t *)&mac_ctx->lim.gLimPreAuthPeerAddr, + sizeof(tSirMacAddr)); + } + auth_req->authType = auth_mode; + /* Update PE session Id */ + auth_req->sessionId = auth_cnf->sessionId; + lim_post_mlm_message(mac_ctx, LIM_MLM_AUTH_REQ, + (uint32_t *) auth_req); + return; + } else { + /* MAC based authentication failure */ + if (session_entry->limSmeState == + eLIM_SME_WT_AUTH_STATE) { + pe_err("Auth Failure occurred"); + session_entry->limSmeState = + eLIM_SME_JOIN_FAILURE_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + session_entry->limMlmState = + eLIM_MLM_IDLE_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, + session_entry->limMlmState)); + + /* WAR for some IOT issue on specific AP: + * STA failed to connect to SAE AP due to incorrect + * password is used. Then AP will still reject the + * authentication even correct password is used unless + * STA send deauth to AP upon authentication failure. + */ + if (auth_type == eSIR_AUTH_TYPE_SAE) { + pe_debug("Send deauth for SAE auth failure"); + lim_send_deauth_mgmt_frame(mac_ctx, + auth_cnf->protStatusCode, + auth_cnf->peerMacAddr, + session_entry, false); + } + + /* + * Need to send Join response with + * auth failure to Host. + */ + lim_handle_sme_join_result(mac_ctx, + auth_cnf->resultCode, + auth_cnf->protStatusCode, + session_entry); + } else { + /* + * Pre-authentication failure. + * Send Pre-auth failure response to host + */ + session_entry->limSmeState = + session_entry->limPrevSmeState; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + } + } +} + +/** + * lim_process_mlm_assoc_cnf() - Process association confirmation + * @mac_ctx: Pointer to Global MAC structure + * @msg: A pointer to the MLM message buffer + * + * This function is called to processes MLM_ASSOC_CNF + * message from MLM State machine. + * + * Return: None + */ +void lim_process_mlm_assoc_cnf(struct mac_context *mac_ctx, + uint32_t *msg) +{ + struct pe_session *session_entry; + tLimMlmAssocCnf *assoc_cnf; + + if (!msg) { + pe_err("Buffer is Pointing to NULL"); + return; + } + assoc_cnf = (tLimMlmAssocCnf *) msg; + session_entry = pe_find_session_by_session_id(mac_ctx, + assoc_cnf->sessionId); + if (!session_entry) { + pe_err("SessionId:%d Session does not exist", + assoc_cnf->sessionId); + return; + } + if (session_entry->limSmeState != eLIM_SME_WT_ASSOC_STATE || + LIM_IS_AP_ROLE(session_entry)) { + /* + * Should not have received Assocication confirm + * from MLM in other states OR on AP. + * Log error + */ + pe_err("SessionId:%d Received MLM_ASSOC_CNF in state %X", + session_entry->peSessionId, session_entry->limSmeState); + return; + } + if (((tLimMlmAssocCnf *) msg)->resultCode != eSIR_SME_SUCCESS) { + /* Association failure */ + pe_err("SessionId:%d Association failure resultCode: %d limSmeState:%d", + session_entry->peSessionId, + ((tLimMlmAssocCnf *) msg)->resultCode, + session_entry->limSmeState); + + /* If driver gets deauth when its waiting for ADD_STA_RSP then + * we need to do DEL_STA followed by DEL_BSS. So based on below + * reason-code here we decide whether to do only DEL_BSS or + * DEL_STA + DEL_BSS. + */ + if (((tLimMlmAssocCnf *) msg)->resultCode != + eSIR_SME_JOIN_DEAUTH_FROM_AP_DURING_ADD_STA) + session_entry->limSmeState = + eLIM_SME_JOIN_FAILURE_STATE; + + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, mac_ctx->lim.gLimSmeState)); + /* + * Need to send Join response with + * Association failure to Host. + */ + lim_handle_sme_join_result(mac_ctx, + ((tLimMlmAssocCnf *) msg)->resultCode, + ((tLimMlmAssocCnf *) msg)->protStatusCode, + session_entry); + } else { + /* Successful Association */ + pe_debug("SessionId:%d Associated with BSS", + session_entry->peSessionId); + session_entry->limSmeState = eLIM_SME_LINK_EST_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + /** + * Need to send Join response with + * Association success to Host. + */ + lim_handle_sme_join_result(mac_ctx, + ((tLimMlmAssocCnf *) msg)->resultCode, + ((tLimMlmAssocCnf *) msg)->protStatusCode, + session_entry); + } +} + +void +lim_fill_sme_assoc_ind_params( + struct mac_context *mac_ctx, + tpLimMlmAssocInd assoc_ind, struct assoc_ind *sme_assoc_ind, + struct pe_session *session_entry, bool assoc_req_alloc) +{ + sme_assoc_ind->length = sizeof(struct assoc_ind); + sme_assoc_ind->sessionId = session_entry->smeSessionId; + + /* Required for indicating the frames to upper layer */ + sme_assoc_ind->assocReqLength = assoc_ind->assocReqLength; + if (assoc_req_alloc && assoc_ind->assocReqLength) { + sme_assoc_ind->assocReqPtr = qdf_mem_malloc( + assoc_ind->assocReqLength); + qdf_mem_copy(sme_assoc_ind->assocReqPtr, assoc_ind->assocReqPtr, + assoc_ind->assocReqLength); + } else { + sme_assoc_ind->assocReqPtr = assoc_ind->assocReqPtr; + } + + sme_assoc_ind->beaconPtr = session_entry->beacon; + sme_assoc_ind->beaconLength = session_entry->bcnLen; + + /* Fill in peerMacAddr */ + qdf_mem_copy(sme_assoc_ind->peerMacAddr, assoc_ind->peerMacAddr, + sizeof(tSirMacAddr)); + + /* Fill in aid */ + sme_assoc_ind->aid = assoc_ind->aid; + /* Fill in bssId */ + qdf_mem_copy(sme_assoc_ind->bssId, session_entry->bssId, + sizeof(tSirMacAddr)); + /* Fill in authType */ + sme_assoc_ind->authType = assoc_ind->authType; + /* Fill in rsn_akm_type */ + sme_assoc_ind->akm_type = assoc_ind->akm_type; + /* Fill in ssId */ + qdf_mem_copy((uint8_t *) &sme_assoc_ind->ssId, + (uint8_t *) &(assoc_ind->ssId), assoc_ind->ssId.length + 1); + sme_assoc_ind->rsnIE.length = assoc_ind->rsnIE.length; + qdf_mem_copy((uint8_t *) &sme_assoc_ind->rsnIE.rsnIEdata, + (uint8_t *) &(assoc_ind->rsnIE.rsnIEdata), + assoc_ind->rsnIE.length); + +#ifdef FEATURE_WLAN_WAPI + sme_assoc_ind->wapiIE.length = assoc_ind->wapiIE.length; + qdf_mem_copy((uint8_t *) &sme_assoc_ind->wapiIE.wapiIEdata, + (uint8_t *) &(assoc_ind->wapiIE.wapiIEdata), + assoc_ind->wapiIE.length); +#endif + sme_assoc_ind->addIE.length = assoc_ind->addIE.length; + qdf_mem_copy((uint8_t *) &sme_assoc_ind->addIE.addIEdata, + (uint8_t *) &(assoc_ind->addIE.addIEdata), + assoc_ind->addIE.length); + + /* Copy the new TITAN capabilities */ + sme_assoc_ind->spectrumMgtIndicator = assoc_ind->spectrumMgtIndicator; + if (assoc_ind->spectrumMgtIndicator == true) { + sme_assoc_ind->powerCap.minTxPower = + assoc_ind->powerCap.minTxPower; + sme_assoc_ind->powerCap.maxTxPower = + assoc_ind->powerCap.maxTxPower; + sme_assoc_ind->supportedChannels.numChnl = + assoc_ind->supportedChannels.numChnl; + qdf_mem_copy((uint8_t *) &sme_assoc_ind->supportedChannels. + channelList, + (uint8_t *) &(assoc_ind->supportedChannels.channelList), + assoc_ind->supportedChannels.numChnl); + } + qdf_mem_copy(&sme_assoc_ind->chan_info, &assoc_ind->chan_info, + sizeof(struct oem_channel_info)); + /* Fill in WmmInfo */ + sme_assoc_ind->wmmEnabledSta = assoc_ind->WmmStaInfoPresent; + sme_assoc_ind->ampdu = assoc_ind->ampdu; + sme_assoc_ind->sgi_enable = assoc_ind->sgi_enable; + sme_assoc_ind->tx_stbc = assoc_ind->tx_stbc; + sme_assoc_ind->rx_stbc = assoc_ind->rx_stbc; + sme_assoc_ind->ch_width = assoc_ind->ch_width; + sme_assoc_ind->mode = assoc_ind->mode; + sme_assoc_ind->max_supp_idx = assoc_ind->max_supp_idx; + sme_assoc_ind->max_ext_idx = assoc_ind->max_ext_idx; + sme_assoc_ind->max_mcs_idx = assoc_ind->max_mcs_idx; + sme_assoc_ind->rx_mcs_map = assoc_ind->rx_mcs_map; + sme_assoc_ind->tx_mcs_map = assoc_ind->tx_mcs_map; + sme_assoc_ind->ecsa_capable = assoc_ind->ecsa_capable; + + if (assoc_ind->ht_caps.present) + sme_assoc_ind->HTCaps = assoc_ind->ht_caps; + if (assoc_ind->vht_caps.present) + sme_assoc_ind->VHTCaps = assoc_ind->vht_caps; + sme_assoc_ind->capability_info = assoc_ind->capabilityInfo; + sme_assoc_ind->he_caps_present = assoc_ind->he_caps_present; + sme_assoc_ind->is_sae_authenticated = assoc_ind->is_sae_authenticated; +} + +/** + * lim_process_mlm_assoc_ind() - This function is called to processes MLM_ASSOC_IND + * message from MLM State machine. + * @mac Pointer to Global MAC structure + * @msg_buf A pointer to the MLM message buffer + * + * Return: None + */ +void lim_process_mlm_assoc_ind(struct mac_context *mac, uint32_t *msg_buf) +{ + uint32_t len; + struct scheduler_msg msg = {0}; + struct assoc_ind *pSirSmeAssocInd; + tpDphHashNode sta = 0; + struct pe_session *pe_session; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + pe_session = pe_find_session_by_session_id(mac, + ((tpLimMlmAssocInd) msg_buf)->sessionId); + if (!pe_session) { + pe_err("Session Does not exist for given sessionId"); + return; + } + /* / Inform Host of STA association */ + len = sizeof(struct assoc_ind); + pSirSmeAssocInd = qdf_mem_malloc(len); + if (!pSirSmeAssocInd) { + pe_err("call to AllocateMemory failed for eWNI_SME_ASSOC_IND"); + return; + } + + pSirSmeAssocInd->messageType = eWNI_SME_ASSOC_IND; + lim_fill_sme_assoc_ind_params(mac, (tpLimMlmAssocInd)msg_buf, + pSirSmeAssocInd, + pe_session, false); + msg.type = eWNI_SME_ASSOC_IND; + msg.bodyptr = pSirSmeAssocInd; + msg.bodyval = 0; + sta = dph_get_hash_entry(mac, + ((tpLimMlmAssocInd) msg_buf)->aid, + &pe_session->dph.dphHashTable); + if (!sta) { + pe_err("MLM AssocInd: Station context no longer valid (aid %d)", + ((tpLimMlmAssocInd) msg_buf)->aid); + qdf_mem_free(pSirSmeAssocInd); + + return; + } + + pSirSmeAssocInd->reassocReq = sta->mlmStaContext.subType; + pSirSmeAssocInd->timingMeasCap = sta->timingMeasCap; + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, msg.type)); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_ASSOC_IND_EVENT, pe_session, 0, + 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + pe_debug("Create CNF_WAIT_TIMER after received LIM_MLM_ASSOC_IND"); + /* + ** turn on a timer to detect the loss of ASSOC CNF + **/ + lim_activate_cnf_timer(mac, + (uint16_t) ((tpLimMlmAssocInd)msg_buf)->aid, + pe_session); + + mac->lim.sme_msg_callback(mac, &msg); +} /*** end lim_process_mlm_assoc_ind() ***/ + +/** + * lim_process_mlm_disassoc_ind() - This function is called to processes + * MLM_DISASSOC_IND message from MLM State machine. + * @mac: Pointer to Global MAC structure + * @msg_buf: A pointer to the MLM message buffer + * + * Return None + */ +void lim_process_mlm_disassoc_ind(struct mac_context *mac, uint32_t *msg_buf) +{ + tLimMlmDisassocInd *pMlmDisassocInd; + struct pe_session *pe_session; + + pMlmDisassocInd = (tLimMlmDisassocInd *)msg_buf; + pe_session = pe_find_session_by_session_id(mac, + pMlmDisassocInd->sessionId); + if (!pe_session) { + pe_err("Session Does not exist for given sessionID"); + return; + } + switch (GET_LIM_SYSTEM_ROLE(pe_session)) { + case eLIM_STA_IN_IBSS_ROLE: + break; + case eLIM_STA_ROLE: + pe_session->limSmeState = eLIM_SME_WT_DISASSOC_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_SME_STATE, pe_session->peSessionId, + pe_session->limSmeState)); + break; + default: /* eLIM_AP_ROLE */ + pe_debug("*** Peer staId=%d Disassociated ***", + pMlmDisassocInd->aid); + /* Send SME_DISASOC_IND after Polaris cleanup */ + /* (after receiving LIM_MLM_PURGE_STA_IND) */ + break; + } /* end switch (GET_LIM_SYSTEM_ROLE(pe_session)) */ +} /*** end lim_process_mlm_disassoc_ind() ***/ + +/** + * lim_process_mlm_disassoc_cnf() - Processes disassociation + * @mac_ctx: Pointer to Global MAC structure + * @msg: A pointer to the MLM message buffer + * + * This function is called to processes MLM_DISASSOC_CNF + * message from MLM State machine. + * + * Return: None + */ +void lim_process_mlm_disassoc_cnf(struct mac_context *mac_ctx, + uint32_t *msg) +{ + tSirResultCodes result_code; + tLimMlmDisassocCnf *disassoc_cnf; + struct pe_session *session_entry; + + disassoc_cnf = (tLimMlmDisassocCnf *) msg; + + session_entry = + pe_find_session_by_session_id(mac_ctx, disassoc_cnf->sessionId); + if (!session_entry) { + pe_err("session Does not exist for given session Id"); + return; + } + result_code = (tSirResultCodes)(disassoc_cnf->disassocTrigger == + eLIM_LINK_MONITORING_DISASSOC) ? + eSIR_SME_LOST_LINK_WITH_PEER_RESULT_CODE : + disassoc_cnf->resultCode; + if (LIM_IS_STA_ROLE(session_entry)) { + /* Disassociate Confirm from MLM */ + if ((session_entry->limSmeState != eLIM_SME_WT_DISASSOC_STATE) + && (session_entry->limSmeState != + eLIM_SME_WT_DEAUTH_STATE)) { + /* + * Should not have received + * Disassocate confirm + * from MLM in other states.Log error + */ + pe_err("received MLM_DISASSOC_CNF in state %X", + session_entry->limSmeState); + return; + } + if (mac_ctx->lim.gLimRspReqd) + mac_ctx->lim.gLimRspReqd = false; + if (disassoc_cnf->disassocTrigger == + eLIM_PROMISCUOUS_MODE_DISASSOC) { + if (disassoc_cnf->resultCode != eSIR_SME_SUCCESS) + session_entry->limSmeState = + session_entry->limPrevSmeState; + else + session_entry->limSmeState = + eLIM_SME_OFFLINE_STATE; + MTRACE(mac_trace + (mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + } else { + if (disassoc_cnf->resultCode != eSIR_SME_SUCCESS) + session_entry->limSmeState = + session_entry->limPrevSmeState; + else + session_entry->limSmeState = + eLIM_SME_IDLE_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + lim_send_sme_disassoc_ntf(mac_ctx, + disassoc_cnf->peerMacAddr, result_code, + disassoc_cnf->disassocTrigger, + disassoc_cnf->aid, session_entry->smeSessionId, + session_entry); + } + } else if (LIM_IS_AP_ROLE(session_entry)) { + lim_send_sme_disassoc_ntf(mac_ctx, disassoc_cnf->peerMacAddr, + result_code, disassoc_cnf->disassocTrigger, + disassoc_cnf->aid, session_entry->smeSessionId, + session_entry); + } +} + +/** + * lim_process_mlm_deauth_ind() - processes MLM_DEAUTH_IND + * @mac_ctx: global mac structure + * @deauth_ind: deauth indication + * + * This function is called to processes MLM_DEAUTH_IND + * message from MLM State machine. + * + * Return: None + */ +static void lim_process_mlm_deauth_ind(struct mac_context *mac_ctx, + tLimMlmDeauthInd *deauth_ind) +{ + struct pe_session *session; + uint8_t session_id; + enum eLimSystemRole role; + + if (!deauth_ind) { + pe_err("deauth_ind is null"); + return; + } + session = pe_find_session_by_bssid(mac_ctx, + deauth_ind->peerMacAddr, + &session_id); + if (!session) { + pe_err("session does not exist for Addr:" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(deauth_ind->peerMacAddr)); + return; + } + role = GET_LIM_SYSTEM_ROLE(session); + if (role == eLIM_STA_ROLE) { + session->limSmeState = eLIM_SME_WT_DEAUTH_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session->peSessionId, session->limSmeState)); + } +} + +/** + * lim_process_mlm_deauth_cnf() + * + ***FUNCTION: + * This function is called to processes MLM_DEAUTH_CNF + * message from MLM State machine. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param msg_buf A pointer to the MLM message buffer + * + * @return None + */ +void lim_process_mlm_deauth_cnf(struct mac_context *mac, uint32_t *msg_buf) +{ + uint16_t aid; + tSirResultCodes resultCode; + tLimMlmDeauthCnf *pMlmDeauthCnf; + struct pe_session *pe_session; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + pMlmDeauthCnf = (tLimMlmDeauthCnf *)msg_buf; + pe_session = pe_find_session_by_session_id(mac, + pMlmDeauthCnf->sessionId); + if (!pe_session) { + pe_err("session does not exist for given session Id"); + return; + } + + resultCode = (tSirResultCodes) + (pMlmDeauthCnf->deauthTrigger == + eLIM_LINK_MONITORING_DEAUTH) ? + eSIR_SME_LOST_LINK_WITH_PEER_RESULT_CODE : + pMlmDeauthCnf->resultCode; + aid = LIM_IS_AP_ROLE(pe_session) ? pMlmDeauthCnf->aid : 1; + if (LIM_IS_STA_ROLE(pe_session)) { + /* Deauth Confirm from MLM */ + if ((pe_session->limSmeState != eLIM_SME_WT_DISASSOC_STATE) + && pe_session->limSmeState != + eLIM_SME_WT_DEAUTH_STATE) { + /** + * Should not have received Deauth confirm + * from MLM in other states. + * Log error + */ + pe_err("received unexpected MLM_DEAUTH_CNF in state %X", + pe_session->limSmeState); + return; + } + if (pMlmDeauthCnf->resultCode == eSIR_SME_SUCCESS) { + pe_session->limSmeState = eLIM_SME_IDLE_STATE; + } else + pe_session->limSmeState = + pe_session->limPrevSmeState; + MTRACE(mac_trace + (mac, TRACE_CODE_SME_STATE, pe_session->peSessionId, + pe_session->limSmeState)); + + if (mac->lim.gLimRspReqd) + mac->lim.gLimRspReqd = false; + } + /* On STA or on BASIC AP, send SME_DEAUTH_RSP to host */ + lim_send_sme_deauth_ntf(mac, pMlmDeauthCnf->peer_macaddr.bytes, + resultCode, + pMlmDeauthCnf->deauthTrigger, + aid, pe_session->smeSessionId); +} /*** end lim_process_mlm_deauth_cnf() ***/ + +/** + * lim_process_mlm_purge_sta_ind() + * + ***FUNCTION: + * This function is called to processes MLM_PURGE_STA_IND + * message from MLM State machine. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param msg_buf A pointer to the MLM message buffer + * + * @return None + */ +void lim_process_mlm_purge_sta_ind(struct mac_context *mac, uint32_t *msg_buf) +{ + tSirResultCodes resultCode; + tpLimMlmPurgeStaInd pMlmPurgeStaInd; + struct pe_session *pe_session; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + pMlmPurgeStaInd = (tpLimMlmPurgeStaInd)msg_buf; + pe_session = pe_find_session_by_session_id(mac, + pMlmPurgeStaInd->sessionId); + if (!pe_session) { + pe_err("session does not exist for given bssId"); + return; + } + /* Purge STA indication from MLM */ + resultCode = (tSirResultCodes) pMlmPurgeStaInd->reasonCode; + switch (GET_LIM_SYSTEM_ROLE(pe_session)) { + case eLIM_STA_IN_IBSS_ROLE: + break; + case eLIM_STA_ROLE: + default: /* eLIM_AP_ROLE */ + if (LIM_IS_STA_ROLE(pe_session) && + (pe_session->limSmeState != + eLIM_SME_WT_DISASSOC_STATE) && + (pe_session->limSmeState != eLIM_SME_WT_DEAUTH_STATE)) { + /** + * Should not have received + * Purge STA indication + * from MLM in other states. + * Log error + */ + pe_err("received unexpected MLM_PURGE_STA_IND in state %X", + pe_session->limSmeState); + break; + } + pe_debug("*** Cleanup completed for staId=%d ***", + pMlmPurgeStaInd->aid); + if (LIM_IS_STA_ROLE(pe_session)) { + pe_session->limSmeState = eLIM_SME_IDLE_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_SME_STATE, + pe_session->peSessionId, + pe_session->limSmeState)); + + } + if (pMlmPurgeStaInd->purgeTrigger == eLIM_PEER_ENTITY_DEAUTH) { + lim_send_sme_deauth_ntf(mac, + pMlmPurgeStaInd->peerMacAddr, + resultCode, + pMlmPurgeStaInd->purgeTrigger, + pMlmPurgeStaInd->aid, + pe_session->smeSessionId); + } else + lim_send_sme_disassoc_ntf(mac, + pMlmPurgeStaInd->peerMacAddr, + resultCode, + pMlmPurgeStaInd->purgeTrigger, + pMlmPurgeStaInd->aid, + pe_session->smeSessionId, + pe_session); + } /* end switch (GET_LIM_SYSTEM_ROLE(pe_session)) */ +} /*** end lim_process_mlm_purge_sta_ind() ***/ + +/** + * lim_process_mlm_set_keys_cnf() + * + ***FUNCTION: + * This function is called to processes MLM_SETKEYS_CNF + * message from MLM State machine. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param msg_buf A pointer to the MLM message buffer + * + * @return None + */ +void lim_process_mlm_set_keys_cnf(struct mac_context *mac, uint32_t *msg_buf) +{ + /* Prepare and send SME_SETCONTEXT_RSP message */ + tLimMlmSetKeysCnf *pMlmSetKeysCnf; + struct pe_session *pe_session; + uint16_t aid; + tpDphHashNode sta_ds; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + pMlmSetKeysCnf = (tLimMlmSetKeysCnf *)msg_buf; + pe_session = pe_find_session_by_session_id(mac, + pMlmSetKeysCnf->sessionId); + if (!pe_session) { + pe_err("session does not exist for given sessionId"); + return; + } + pe_session->is_key_installed = 0; + pe_debug("Received MLM_SETKEYS_CNF with resultCode = %d", + pMlmSetKeysCnf->resultCode); + /* if the status is success keys are installed in the + * Firmware so we can set the protection bit + */ + if (eSIR_SME_SUCCESS == pMlmSetKeysCnf->resultCode) { + if (pMlmSetKeysCnf->key_len_nonzero) + pe_session->is_key_installed = 1; + sta_ds = dph_lookup_hash_entry(mac, + pMlmSetKeysCnf->peer_macaddr.bytes, + &aid, &pe_session->dph.dphHashTable); + if (sta_ds && pMlmSetKeysCnf->key_len_nonzero) + sta_ds->is_key_installed = 1; + } + pe_debug("is_key_installed = %d", pe_session->is_key_installed); + + lim_send_sme_set_context_rsp(mac, + pMlmSetKeysCnf->peer_macaddr, + 1, + (tSirResultCodes) pMlmSetKeysCnf->resultCode, + pe_session, pe_session->smeSessionId); +} /*** end lim_process_mlm_set_keys_cnf() ***/ + +void lim_join_result_callback(struct mac_context *mac, + uint8_t vdev_id) +{ + struct pe_session *session; + + session = pe_find_session_by_vdev_id(mac, vdev_id); + if (!session) { + return; + } + lim_send_sme_join_reassoc_rsp(mac, eWNI_SME_JOIN_RSP, + session->result_code, + session->prot_status_code, + session, vdev_id); + pe_delete_session(mac, session); +} + +QDF_STATUS lim_sta_handle_connect_fail(join_params *param) +{ + struct pe_session *session; + struct mac_context *mac_ctx; + tpDphHashNode sta_ds = NULL; + + if (!param) { + pe_err("param is NULL"); + return QDF_STATUS_E_INVAL; + } + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + pe_err("Mac context is NULL"); + return QDF_STATUS_E_INVAL; + } + session = pe_find_session_by_session_id(mac_ctx, param->pe_session_id); + if (!session) { + pe_err("session is NULL"); + return QDF_STATUS_E_INVAL; + } + + sta_ds = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session->dph.dphHashTable); + if (sta_ds) { + sta_ds->mlmStaContext.disassocReason = + eSIR_MAC_UNSPEC_FAILURE_REASON; + sta_ds->mlmStaContext.cleanupTrigger = + eLIM_JOIN_FAILURE; + sta_ds->mlmStaContext.resultCode = param->result_code; + sta_ds->mlmStaContext.protStatusCode = param->prot_status_code; + /* + * FIX_ME: at the end of lim_cleanup_rx_path, + * make sure PE is sending eWNI_SME_JOIN_RSP + * to SME + */ + lim_cleanup_rx_path(mac_ctx, sta_ds, session); + qdf_mem_free(session->lim_join_req); + session->lim_join_req = NULL; + /* Cleanup if add bss failed */ + if (session->add_bss_failed) { + dph_delete_hash_entry(mac_ctx, + sta_ds->staAddr, sta_ds->assocId, + &session->dph.dphHashTable); + goto error; + } + return QDF_STATUS_SUCCESS; + } + qdf_mem_free(session->lim_join_req); + session->lim_join_req = NULL; + +error: + /* + * Delete the session if JOIN failure occurred. + * if the peer is not created, then there is no + * need to send down the set link state which will + * try to delete the peer. Instead a join response + * failure should be sent to the upper layers. + */ + if (param->result_code != eSIR_SME_PEER_CREATE_FAILED) { + QDF_STATUS status; + + session->prot_status_code = param->prot_status_code; + session->result_code = param->result_code; + + status = wma_send_vdev_stop(session->smeSessionId); + if (QDF_IS_STATUS_ERROR(status)) { + lim_join_result_callback(mac_ctx, + session->smeSessionId); + } + + return status; + } + + + lim_send_sme_join_reassoc_rsp(mac_ctx, eWNI_SME_JOIN_RSP, + param->result_code, + param->prot_status_code, + session, session->smeSessionId); + if (param->result_code == eSIR_SME_PEER_CREATE_FAILED) + pe_delete_session(mac_ctx, session); + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_handle_sme_join_result() - Handles sme join result + * @mac_ctx: Pointer to Global MAC structure + * @result_code: Failure code to be sent + * @prot_status_code : Protocol status code + * @session_entry: PE session handle + * + * This function is called to process join/auth/assoc failures + * upon receiving MLM_JOIN/AUTH/ASSOC_CNF with a failure code or + * MLM_ASSOC_CNF with a success code in case of STA role and + * MLM_JOIN_CNF with success in case of STA in IBSS role. + * + * Return: None + */ +void lim_handle_sme_join_result(struct mac_context *mac_ctx, + tSirResultCodes result_code, uint16_t prot_status_code, + struct pe_session *session) +{ + join_params param; + QDF_STATUS status; + + if (!session) { + pe_err("session is NULL"); + return; + } + + if (result_code == eSIR_SME_SUCCESS) { + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START_SUCCESS, + 0, NULL); + return lim_send_sme_join_reassoc_rsp(mac_ctx, eWNI_SME_JOIN_RSP, + result_code, + prot_status_code, session, + session->smeSessionId); + } + + param.result_code = result_code; + param.prot_status_code = prot_status_code; + param.pe_session_id = session->peSessionId; + + mlme_set_connection_fail(session->vdev, true); + if (wlan_vdev_mlme_get_substate(session->vdev) == + WLAN_VDEV_SS_START_START_PROGRESS) + status = wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START_REQ_FAIL, + sizeof(param), ¶m); + else + status = wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_CONNECTION_FAIL, + sizeof(param), ¶m); + + return; +} + + +/** + * lim_process_mlm_add_sta_rsp() + * + ***FUNCTION: + * This function is called to process a WMA_ADD_STA_RSP from HAL. + * Upon receipt of this message from HAL, MLME - + * > Determines the "state" in which this message was received + * > Forwards it to the appropriate callback + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param struct scheduler_msg The MsgQ header, which contains the + * response buffer + * + * @return None + */ +void lim_process_mlm_add_sta_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ, + struct pe_session *pe_session) +{ + /* we need to process the deferred message since the initiating req. there might be nested request. */ + /* in the case of nested request the new request initiated from the response will take care of resetting */ + /* the deffered flag. */ + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + if (LIM_IS_AP_ROLE(pe_session)) { + lim_process_ap_mlm_add_sta_rsp(mac, limMsgQ, pe_session); + return; + } + lim_process_sta_mlm_add_sta_rsp(mac, limMsgQ, pe_session); +} + +/** + * lim_process_sta_mlm_add_sta_rsp () - Process add sta response + * @mac_ctx: Pointer to mac context + * @msg: struct scheduler_msg *an Message structure + * @session_entry: PE session entry + * + * Process ADD STA response sent from WMA and posts results + * to SME. + * + * Return: Null + */ + +void lim_process_sta_mlm_add_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *msg, struct pe_session *session_entry) +{ + tLimMlmAssocCnf mlm_assoc_cnf; + tpDphHashNode sta_ds; + uint32_t msg_type = LIM_MLM_ASSOC_CNF; + tpAddStaParams add_sta_params = (tpAddStaParams) msg->bodyptr; + + if (!add_sta_params) { + pe_err("Encountered NULL Pointer"); + return; + } + + if (session_entry->limSmeState == eLIM_SME_WT_REASSOC_STATE) + msg_type = LIM_MLM_REASSOC_CNF; + + if (true == session_entry->fDeauthReceived) { + pe_err("Received Deauth frame in ADD_STA_RESP state"); + if (QDF_STATUS_SUCCESS == add_sta_params->status) { + pe_err("ADD_STA success, send update result code with eSIR_SME_JOIN_DEAUTH_FROM_AP_DURING_ADD_STA limMlmState: %d bssid "QDF_MAC_ADDR_FMT, + session_entry->limMlmState, + QDF_MAC_ADDR_REF(add_sta_params->staMac)); + + if (session_entry->limSmeState == + eLIM_SME_WT_REASSOC_STATE) + msg_type = LIM_MLM_REASSOC_CNF; + /* + * We are sending result code + * eSIR_SME_JOIN_DEAUTH_FROM_AP_DURING_ADD_STA which + * will trigger proper cleanup (DEL_STA/DEL_BSS both + * required) in either assoc cnf or reassoc cnf handler. + */ + mlm_assoc_cnf.resultCode = + eSIR_SME_JOIN_DEAUTH_FROM_AP_DURING_ADD_STA; + mlm_assoc_cnf.protStatusCode = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + goto end; + } + } + + if (QDF_STATUS_SUCCESS == add_sta_params->status) { + if (eLIM_MLM_WT_ADD_STA_RSP_STATE != + session_entry->limMlmState) { + pe_err("Received WMA_ADD_STA_RSP in state %X", + session_entry->limMlmState); + mlm_assoc_cnf.resultCode = + (tSirResultCodes) eSIR_SME_REFUSED; + goto end; + } + /* + * Update the DPH Hash Entry for this STA + * with proper state info + */ + sta_ds = + dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session_entry->dph.dphHashTable); + if (sta_ds) { + sta_ds->mlmStaContext.mlmState = + eLIM_MLM_LINK_ESTABLISHED_STATE; + sta_ds->nss = add_sta_params->nss; + } else + pe_warn("Fail to get DPH Hash Entry for AID - %d", + DPH_STA_HASH_INDEX_PEER); + session_entry->limMlmState = eLIM_MLM_LINK_ESTABLISHED_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, + session_entry->limMlmState)); + +#ifdef WLAN_DEBUG + mac_ctx->lim.gLimNumLinkEsts++; +#endif +#ifdef FEATURE_WLAN_TDLS + /* initialize TDLS peer related data */ + lim_init_tdls_data(mac_ctx, session_entry); +#endif + /* + * Return Assoc confirm to SME with success + * FIXME - Need the correct ASSOC RSP code to + * be passed in here + */ + mlm_assoc_cnf.resultCode = (tSirResultCodes) eSIR_SME_SUCCESS; + lim_send_obss_color_collision_cfg(mac_ctx, session_entry, + OBSS_COLOR_COLLISION_DETECTION); + if (lim_is_session_he_capable(session_entry) && sta_ds) { + if (mac_ctx->usr_cfg_mu_edca_params) { + pe_debug("Send user cfg MU EDCA params to FW"); + lim_send_edca_params(mac_ctx, + mac_ctx->usr_mu_edca_params, + session_entry->vdev_id, true); + } else if (session_entry->mu_edca_present) { + pe_debug("Send MU EDCA params to FW"); + lim_send_edca_params(mac_ctx, + session_entry->ap_mu_edca_params, + session_entry->vdev_id, true); + } + } + } else { + pe_err("ADD_STA failed!"); + if (session_entry->limSmeState == eLIM_SME_WT_REASSOC_STATE) + mlm_assoc_cnf.resultCode = + (tSirResultCodes) eSIR_SME_FT_REASSOC_FAILURE; + else + mlm_assoc_cnf.resultCode = + (tSirResultCodes) eSIR_SME_REFUSED; + mlm_assoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + } +end: + if (msg->bodyptr) { + qdf_mem_free(add_sta_params); + msg->bodyptr = NULL; + } + /* Updating PE session Id */ + mlm_assoc_cnf.sessionId = session_entry->peSessionId; + lim_post_sme_message(mac_ctx, msg_type, (uint32_t *) &mlm_assoc_cnf); + if (true == session_entry->fDeauthReceived) + session_entry->fDeauthReceived = false; + return; +} + +void lim_process_mlm_del_bss_rsp(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session) +{ + /* we need to process the deferred message since the initiating req. there might be nested request. */ + /* in the case of nested request the new request initiated from the response will take care of resetting */ + /* the deffered flag. */ + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + mac->sys.gSysFrameCount[SIR_MAC_MGMT_FRAME][SIR_MAC_MGMT_DEAUTH] = 0; + + if (LIM_IS_AP_ROLE(pe_session) && + (pe_session->statypeForBss == STA_ENTRY_SELF)) { + lim_process_ap_mlm_del_bss_rsp(mac, vdev_stop_rsp, pe_session); + return; + } + lim_process_sta_mlm_del_bss_rsp(mac, vdev_stop_rsp, pe_session); + +#ifdef WLAN_FEATURE_11W + if (pe_session->limRmfEnabled) { + if (QDF_STATUS_SUCCESS != + lim_send_exclude_unencrypt_ind(mac, true, pe_session)) { + pe_err("Could not send down Exclude Unencrypted Indication!"); + } + } +#endif +} + +void lim_process_sta_mlm_del_bss_rsp(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session) +{ + tpDphHashNode sta = + dph_get_hash_entry(mac, DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + tSirResultCodes status_code = eSIR_SME_SUCCESS; + + if (!vdev_stop_rsp) { + pe_err("Invalid body pointer in message"); + goto end; + } + if (vdev_stop_rsp->status == QDF_STATUS_SUCCESS) { + if (!sta) { + pe_err("DPH Entry for STA 1 missing"); + status_code = eSIR_SME_REFUSED; + goto end; + } + if (eLIM_MLM_WT_DEL_BSS_RSP_STATE != + sta->mlmStaContext.mlmState) { + pe_err("Received unexpected WMA_DEL_BSS_RSP in state %X", + sta->mlmStaContext.mlmState); + status_code = eSIR_SME_REFUSED; + goto end; + } + pe_debug("STA AssocID %d MAC "QDF_MAC_ADDR_FMT, sta->assocId, + QDF_MAC_ADDR_REF(sta->staAddr)); + } else { + pe_err("DEL BSS failed!"); + status_code = eSIR_SME_STOP_BSS_FAILURE; + } +end: + if (!sta) + return; + if ((LIM_IS_STA_ROLE(pe_session)) && + (pe_session->limSmeState != + eLIM_SME_WT_DISASSOC_STATE && + pe_session->limSmeState != + eLIM_SME_WT_DEAUTH_STATE) && + sta->mlmStaContext.cleanupTrigger != + eLIM_JOIN_FAILURE) { + /** The Case where the DelBss is invoked from + * context of other than normal DisAssoc / Deauth OR + * as part of Join Failure. + */ + lim_handle_del_bss_in_re_assoc_context(mac, sta, pe_session); + return; + } + lim_prepare_and_send_del_sta_cnf(mac, sta, status_code, pe_session); + return; +} + +void lim_process_ap_mlm_del_bss_rsp(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session) +{ + tSirResultCodes rc = eSIR_SME_SUCCESS; + + if (!pe_session) { + pe_err("Session entry passed is NULL"); + if (vdev_stop_rsp) + qdf_mem_free(vdev_stop_rsp); + return; + } + + if (!vdev_stop_rsp) { + pe_err("BSS: DEL_BSS_RSP with no body!"); + rc = eSIR_SME_REFUSED; + goto end; + } + mac->lim.gLimMlmState = eLIM_MLM_IDLE_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, NO_SESSION, + mac->lim.gLimMlmState)); + + if (eLIM_MLM_WT_DEL_BSS_RSP_STATE != pe_session->limMlmState) { + pe_err("Received unexpected WMA_DEL_BSS_RSP in state %X", + pe_session->limMlmState); + rc = eSIR_SME_REFUSED; + goto end; + } + if (vdev_stop_rsp->status != QDF_STATUS_SUCCESS) { + pe_err("BSS: DEL_BSS_RSP error (%x)", vdev_stop_rsp->status); + rc = eSIR_SME_STOP_BSS_FAILURE; + goto end; + } + /** Softmac may send all the buffered packets right after resuming the transmission hence + * to occupy the medium during non channel occupancy period. So resume the transmission after + * HAL gives back the response. + */ + dph_hash_table_init(mac, &pe_session->dph.dphHashTable); + lim_delete_pre_auth_list(mac); + /* Initialize number of associated stations during cleanup */ + pe_session->gLimNumOfCurrentSTAs = 0; +end: + lim_send_sme_rsp(mac, eWNI_SME_STOP_BSS_RSP, rc, + pe_session->smeSessionId); + pe_delete_session(mac, pe_session); +} + +/** + * lim_process_mlm_del_sta_rsp() - Process DEL STA response + * @mac_ctx: Pointer to Global MAC structure + * @msg: The MsgQ header, which contains the response buffer + * + * This function is called to process a WMA_DEL_STA_RSP from + * WMA Upon receipt of this message from FW. + * + * Return: None + */ +void lim_process_mlm_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + /* + * we need to process the deferred message since the + * initiating req. there might be nested request + * in the case of nested request the new request + * initiated from the response will take care of resetting + * the deffered flag. + */ + struct pe_session *session_entry; + tpDeleteStaParams del_sta_params; + + del_sta_params = (tpDeleteStaParams) msg->bodyptr; + if (!del_sta_params) { + pe_err("null pointer del_sta_params msg"); + return; + } + SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true); + + session_entry = pe_find_session_by_session_id(mac_ctx, + del_sta_params->sessionId); + if (!session_entry) { + pe_err("Session Doesn't exist: %d", + del_sta_params->sessionId); + qdf_mem_free(del_sta_params); + msg->bodyptr = NULL; + return; + } + + if (LIM_IS_AP_ROLE(session_entry)) { + lim_process_ap_mlm_del_sta_rsp(mac_ctx, msg, + session_entry); + return; + } + if (LIM_IS_IBSS_ROLE(session_entry)) { + lim_process_ibss_del_sta_rsp(mac_ctx, msg, + session_entry); + return; + } + if (LIM_IS_NDI_ROLE(session_entry)) { + lim_process_ndi_del_sta_rsp(mac_ctx, msg, session_entry); + return; + } + lim_process_sta_mlm_del_sta_rsp(mac_ctx, msg, session_entry); +} + +/** + * lim_process_ap_mlm_del_sta_rsp() - Process WMA_DEL_STA_RSP + * @mac_ctx: Global pointer to MAC context + * @msg: Received message + * @session_entry: Session entry + * + * Process WMA_DEL_STA_RSP for AP role + * + * Retunrn: None + */ +void lim_process_ap_mlm_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *msg, + struct pe_session *session_entry) +{ + tpDeleteStaParams del_sta_params = (tpDeleteStaParams) msg->bodyptr; + tpDphHashNode sta_ds; + tSirResultCodes status_code = eSIR_SME_SUCCESS; + + if (!msg->bodyptr) { + pe_err("msg->bodyptr NULL"); + return; + } + + sta_ds = dph_get_hash_entry(mac_ctx, del_sta_params->assocId, + &session_entry->dph.dphHashTable); + if (!sta_ds) { + pe_err("DPH Entry for STA %X missing", + del_sta_params->assocId); + status_code = eSIR_SME_REFUSED; + qdf_mem_free(del_sta_params); + msg->bodyptr = NULL; + return; + } + pe_debug("Received del Sta Rsp in StaD MlmState: %d", + sta_ds->mlmStaContext.mlmState); + if (QDF_STATUS_SUCCESS != del_sta_params->status) { + pe_warn("DEL STA failed!"); + status_code = eSIR_SME_REFUSED; + goto end; + } + + pe_debug("AP received the DEL_STA_RSP for assocID: %X sta mac " + QDF_MAC_ADDR_FMT, del_sta_params->assocId, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + if ((eLIM_MLM_WT_DEL_STA_RSP_STATE != sta_ds->mlmStaContext.mlmState) && + (eLIM_MLM_WT_ASSOC_DEL_STA_RSP_STATE != + sta_ds->mlmStaContext.mlmState)) { + pe_err("Received unexpected WMA_DEL_STA_RSP in state %s for assocId %d", + lim_mlm_state_str(sta_ds->mlmStaContext.mlmState), + sta_ds->assocId); + status_code = eSIR_SME_REFUSED; + goto end; + } + + pe_debug("Deleted STA AssocID %d", sta_ds->assocId); + lim_print_mac_addr(mac_ctx, sta_ds->staAddr, LOGD); + if (eLIM_MLM_WT_ASSOC_DEL_STA_RSP_STATE == + sta_ds->mlmStaContext.mlmState) { + qdf_mem_free(del_sta_params); + msg->bodyptr = NULL; + if (lim_add_sta(mac_ctx, sta_ds, false, session_entry) != + QDF_STATUS_SUCCESS) { + pe_err("could not Add STA with assocId: %d", + sta_ds->assocId); + /* + * delete the TS if it has already been added. + * send the response with error status. + */ + if (sta_ds->qos.addtsPresent) { + tpLimTspecInfo pTspecInfo; + + if (QDF_STATUS_SUCCESS == + lim_tspec_find_by_assoc_id(mac_ctx, + sta_ds->assocId, + &sta_ds->qos.addts.tspec, + &mac_ctx->lim.tspecInfo[0], + &pTspecInfo)) { + lim_admit_control_delete_ts(mac_ctx, + sta_ds->assocId, + &sta_ds->qos.addts.tspec.tsinfo, + NULL, + &pTspecInfo->idx); + } + } + lim_reject_association(mac_ctx, sta_ds->staAddr, + sta_ds->mlmStaContext.subType, true, + sta_ds->mlmStaContext.authType, sta_ds->assocId, + true, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session_entry); + } + return; + } +end: + qdf_mem_free(del_sta_params); + msg->bodyptr = NULL; + if (eLIM_MLM_WT_ASSOC_DEL_STA_RSP_STATE != + sta_ds->mlmStaContext.mlmState) { + lim_prepare_and_send_del_sta_cnf(mac_ctx, sta_ds, status_code, + session_entry); + } + return; +} + +void lim_process_sta_mlm_del_sta_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ, + struct pe_session *pe_session) +{ + tSirResultCodes status_code = eSIR_SME_SUCCESS; + tpDeleteStaParams pDelStaParams = (tpDeleteStaParams) limMsgQ->bodyptr; + + if (!pDelStaParams) { + pe_err("Encountered NULL Pointer"); + goto end; + } + pe_debug("Del STA RSP received. Status: %d AssocID: %d", + pDelStaParams->status, pDelStaParams->assocId); + +#ifdef FEATURE_WLAN_TDLS + if (pDelStaParams->staType == STA_ENTRY_TDLS_PEER) { + pe_debug("TDLS Del STA RSP received"); + lim_process_tdls_del_sta_rsp(mac, limMsgQ, pe_session); + return; + } +#endif + if (QDF_STATUS_SUCCESS != pDelStaParams->status) + pe_err("Del STA failed! Status:%d, proceeding with Del BSS", + pDelStaParams->status); + + if (eLIM_MLM_WT_DEL_STA_RSP_STATE != pe_session->limMlmState) { + pe_err("Received unexpected WDA_DELETE_STA_RSP in state %s", + lim_mlm_state_str(pe_session->limMlmState)); + status_code = eSIR_SME_REFUSED; + goto end; + } + /* + * we must complete all cleanup related to delSta before + * calling limDelBSS. + */ + if (0 != limMsgQ->bodyptr) { + qdf_mem_free(pDelStaParams); + limMsgQ->bodyptr = NULL; + } + + lim_disconnect_complete(pe_session, true); + + return; +end: + if (0 != limMsgQ->bodyptr) { + qdf_mem_free(pDelStaParams); + limMsgQ->bodyptr = NULL; + } + return; +} + +void lim_process_ap_mlm_add_sta_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ, + struct pe_session *pe_session) +{ + tpAddStaParams pAddStaParams = (tpAddStaParams) limMsgQ->bodyptr; + tpDphHashNode sta = NULL; + + if (!pAddStaParams) { + pe_err("Invalid body pointer in message"); + goto end; + } + + sta = + dph_get_hash_entry(mac, pAddStaParams->assocId, + &pe_session->dph.dphHashTable); + if (!sta) { + pe_err("DPH Entry for STA %X missing", pAddStaParams->assocId); + goto end; + } + + if (eLIM_MLM_WT_ADD_STA_RSP_STATE != sta->mlmStaContext.mlmState) { + pe_err("Received unexpected WMA_ADD_STA_RSP in state %X", + sta->mlmStaContext.mlmState); + goto end; + } + if (QDF_STATUS_SUCCESS != pAddStaParams->status) { + pe_err("Error! rcvd delSta rsp from HAL with status %d", + pAddStaParams->status); + lim_reject_association(mac, sta->staAddr, + sta->mlmStaContext.subType, + true, sta->mlmStaContext.authType, + sta->assocId, true, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + pe_session); + goto end; + } + sta->nss = pAddStaParams->nss; + /* if the AssocRsp frame is not acknowledged, then keep alive timer will take care of the state */ + sta->valid = 1; + sta->mlmStaContext.mlmState = eLIM_MLM_WT_ASSOC_CNF_STATE; + pe_debug("AddStaRsp Success.STA AssocID %d sta mac" QDF_MAC_ADDR_FMT, + sta->assocId, QDF_MAC_ADDR_REF(sta->staAddr)); + lim_print_mac_addr(mac, sta->staAddr, LOGD); + + /* For BTAMP-AP, the flow sequence shall be: + * 1) PE sends eWNI_SME_ASSOC_IND to SME + * 2) PE receives eWNI_SME_ASSOC_CNF from SME + * 3) BTAMP-AP sends Re/Association Response to BTAMP-STA + */ + lim_send_mlm_assoc_ind(mac, sta, pe_session); + /* fall though to reclaim the original Add STA Response message */ +end: + if (0 != limMsgQ->bodyptr) { + qdf_mem_free(pAddStaParams); + limMsgQ->bodyptr = NULL; + } + return; +} + +static void lim_process_ap_mlm_add_bss_rsp(struct mac_context *mac, + struct add_bss_rsp *add_bss_rsp) +{ + tLimMlmStartCnf mlmStartCnf; + struct pe_session *pe_session; + uint8_t isWepEnabled = false; + + if (!add_bss_rsp) { + pe_err("Encountered NULL Pointer"); + return; + } + /* TBD: free the memory before returning, do it for all places where lookup fails. */ + pe_session = pe_find_session_by_vdev_id(mac, add_bss_rsp->vdev_id); + if (!pe_session) { + pe_err("session does not exist for vdev_id %d", + add_bss_rsp->vdev_id); + return; + } + /* Update PE session Id */ + mlmStartCnf.sessionId = pe_session->peSessionId; + if (QDF_IS_STATUS_SUCCESS(add_bss_rsp->status)) { + pe_debug("WMA_ADD_BSS_RSP returned with QDF_STATUS_SUCCESS"); + /* Set MLME state */ + pe_session->limMlmState = eLIM_MLM_BSS_STARTED_STATE; + pe_session->chainMask = add_bss_rsp->chain_mask; + pe_session->smpsMode = add_bss_rsp->smps_mode; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + pe_session->limMlmState)); + if (eSIR_IBSS_MODE == pe_session->bssType) { + /** IBSS is 'active' when we receive + * Beacon frames from other STAs that are part of same IBSS. + * Mark internal state as inactive until then. + */ + pe_session->limIbssActive = false; + pe_session->statypeForBss = STA_ENTRY_PEER; /* to know session created for self/peer */ + limResetHBPktCount(pe_session); + } + + pe_session->limSystemRole = eLIM_STA_IN_IBSS_ROLE; + + if (eSIR_INFRA_AP_MODE == pe_session->bssType) + pe_session->limSystemRole = eLIM_AP_ROLE; + else + pe_session->limSystemRole = eLIM_STA_IN_IBSS_ROLE; + sch_edca_profile_update(mac, pe_session); + lim_init_pre_auth_list(mac); + /* Check the SAP security configuration.If configured to + * WEP then max clients supported is 16 + */ + if (pe_session->privacy) { + if ((pe_session->gStartBssRSNIe.present) + || (pe_session->gStartBssWPAIe.present)) + pe_debug("WPA/WPA2 SAP configuration"); + else { + if (mac->mlme_cfg->sap_cfg.assoc_sta_limit > + MAX_SUPPORTED_PEERS_WEP) { + pe_debug("WEP SAP Configuration"); + mac->mlme_cfg->sap_cfg.assoc_sta_limit + = MAX_SUPPORTED_PEERS_WEP; + isWepEnabled = true; + } + } + } + lim_init_peer_idxpool(mac, pe_session); + + /* Start OLBC timer */ + if (tx_timer_activate + (&mac->lim.lim_timers.gLimUpdateOlbcCacheTimer) != + TX_SUCCESS) { + pe_err("tx_timer_activate failed"); + } + + /* Apply previously set configuration at HW */ + lim_apply_configuration(mac, pe_session); + + /* In lim_apply_configuration gLimAssocStaLimit is assigned from cfg. + * So update the value to 16 in case SoftAP is configured in WEP. + */ + if ((mac->mlme_cfg->sap_cfg.assoc_sta_limit > + MAX_SUPPORTED_PEERS_WEP) + && (isWepEnabled)) + mac->mlme_cfg->sap_cfg.assoc_sta_limit = + MAX_SUPPORTED_PEERS_WEP; + mlmStartCnf.resultCode = eSIR_SME_SUCCESS; + } else { + pe_err("WMA_ADD_BSS_REQ failed with status %d", + add_bss_rsp->status); + mlmStartCnf.resultCode = eSIR_SME_HAL_SEND_MESSAGE_FAIL; + } + + lim_send_start_bss_confirm(mac, &mlmStartCnf); +} + +#ifdef QCA_IBSS_SUPPORT +/* + * lim_process_ibss_mlm_add_bss_rsp: API to process add bss response + * in IBSS role + * @session_entry: pe session entry + * @auth_mode: auth mode needs to be updated + * + * Return: None + */ +static void +lim_process_ibss_mlm_add_bss_rsp(struct mac_context *mac, + struct add_bss_rsp *add_bss_rsp, + struct pe_session *pe_session) +{ + tLimMlmStartCnf mlmStartCnf; + + if (!add_bss_rsp) { + pe_err("add_bss_rsp is NULL"); + return; + } + if (QDF_IS_STATUS_SUCCESS(add_bss_rsp->status)) { + pe_debug("WMA_ADD_BSS_RSP returned with QDF_STATUS_SUCCESS"); + + /* Set MLME state */ + pe_session->limMlmState = eLIM_MLM_BSS_STARTED_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + pe_session->limMlmState)); + /** IBSS is 'active' when we receive + * Beacon frames from other STAs that are part of same IBSS. + * Mark internal state as inactive until then. + */ + pe_session->limIbssActive = false; + limResetHBPktCount(pe_session); + pe_session->limSystemRole = eLIM_STA_IN_IBSS_ROLE; + pe_session->statypeForBss = STA_ENTRY_SELF; + sch_edca_profile_update(mac, pe_session); + if (0 == pe_session->freePeerIdxHead) + lim_init_peer_idxpool(mac, pe_session); + + /* Apply previously set configuration at HW */ + lim_apply_configuration(mac, pe_session); + mlmStartCnf.resultCode = eSIR_SME_SUCCESS; + /* If ADD BSS was issued as part of IBSS coalescing, don't send the message to SME, as that is internal to LIM */ + if (true == mac->lim.gLimIbssCoalescingHappened) { + lim_ibss_add_bss_rsp_when_coalescing(mac, + pe_session->curr_op_freq, + pe_session); + return; + } + } else { + pe_err("WMA_ADD_BSS_REQ failed with status %d", + add_bss_rsp->status); + mlmStartCnf.resultCode = eSIR_SME_HAL_SEND_MESSAGE_FAIL; + } + /* Send this message to SME, when ADD_BSS is initiated by SME */ + /* If ADD_BSS is done as part of coalescing, this won't happen. */ + /* Update PE session Id */ + mlmStartCnf.sessionId = pe_session->peSessionId; + lim_send_start_bss_confirm(mac, &mlmStartCnf); +} +#else +static inline void +lim_process_ibss_mlm_add_bss_rsp(struct mac_context *mac, + struct add_bss_rsp *add_bss_rsp, + struct pe_session *pe_session) +{ +} +#endif + +#ifdef WLAN_FEATURE_FILS_SK +/* + * lim_update_fils_auth_mode: API to update Auth mode in case of fils session + * @session_entry: pe session entry + * @auth_mode: auth mode needs to be updated + * + * Return: None + */ +static void lim_update_fils_auth_mode(struct pe_session *session_entry, + tAniAuthType *auth_mode) +{ + if (!session_entry->fils_info) + return; + + if (session_entry->fils_info->is_fils_connection) + *auth_mode = session_entry->fils_info->auth; +} +#else +static void lim_update_fils_auth_mode(struct pe_session *session_entry, + tAniAuthType *auth_mode) +{ } +#endif + +void lim_process_sta_add_bss_rsp_pre_assoc(struct mac_context *mac_ctx, + struct bss_params *add_bss_params, + struct pe_session *session_entry, + QDF_STATUS status) +{ + tAniAuthType cfgAuthType, authMode; + tLimMlmAuthReq *pMlmAuthReq; + tpDphHashNode sta = NULL; + + if (!add_bss_params) { + pe_err("Invalid body pointer in message"); + goto joinFailure; + } + if (QDF_IS_STATUS_SUCCESS(status)) { + sta = dph_add_hash_entry(mac_ctx, + add_bss_params->staContext.staMac, + DPH_STA_HASH_INDEX_PEER, + &session_entry->dph.dphHashTable); + if (!sta) { + /* Could not add hash table entry */ + pe_err("could not add hash entry at DPH for"); + lim_print_mac_addr(mac_ctx, + add_bss_params->staContext.staMac, LOGE); + goto joinFailure; + } + /* Success, handle below */ + /* Trigger Authentication with AP */ + cfgAuthType = mac_ctx->mlme_cfg->wep_params.auth_type; + + /* Try shared Authentication first */ + if (cfgAuthType == eSIR_AUTO_SWITCH) + authMode = eSIR_SHARED_KEY; + else + authMode = cfgAuthType; + + lim_update_fils_auth_mode(session_entry, &authMode); + /* Trigger MAC based Authentication */ + pMlmAuthReq = qdf_mem_malloc(sizeof(tLimMlmAuthReq)); + if (!pMlmAuthReq) { + pe_err("Allocate Memory failed for mlmAuthReq"); + return; + } + sir_copy_mac_addr(pMlmAuthReq->peerMacAddr, + session_entry->bssId); + + pMlmAuthReq->authType = authMode; + session_entry->limMlmState = eLIM_MLM_JOINED_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, eLIM_MLM_JOINED_STATE)); + pMlmAuthReq->sessionId = session_entry->peSessionId; + session_entry->limPrevSmeState = session_entry->limSmeState; + session_entry->limSmeState = eLIM_SME_WT_AUTH_STATE; + + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + lim_post_mlm_message(mac_ctx, LIM_MLM_AUTH_REQ, + (uint32_t *) pMlmAuthReq); + return; + } + +joinFailure: + { + session_entry->limSmeState = eLIM_SME_JOIN_FAILURE_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + + /* Send Join response to Host */ + lim_handle_sme_join_result(mac_ctx, eSIR_SME_REFUSED, + eSIR_MAC_UNSPEC_FAILURE_STATUS, session_entry); + } + +} + +static void lim_process_sta_mlm_add_bss_rsp(struct mac_context *mac_ctx, + struct add_bss_rsp *add_bss_rsp, + struct pe_session *session_entry) +{ + tLimMlmAssocCnf mlm_assoc_cnf; + uint32_t msg_type = LIM_MLM_ASSOC_CNF; + uint32_t sub_type = LIM_ASSOC; + tpDphHashNode sta_ds = NULL; + uint8_t update_sta = false; + + mlm_assoc_cnf.resultCode = eSIR_SME_SUCCESS; + if (eLIM_MLM_WT_ADD_BSS_RSP_REASSOC_STATE == session_entry->limMlmState + || (eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE == + session_entry->limMlmState)) { + msg_type = LIM_MLM_REASSOC_CNF; + sub_type = LIM_REASSOC; + /* + * If Reassoc is happening for the same BSS, then + * use the existing StaId and indicate to HAL to update + * the existing STA entry. + * If Reassoc is happening for the new BSS, then + * old BSS and STA entry would have been already deleted + * before PE tries to add BSS for the new BSS, so set the + * updateSta to false and pass INVALID STA Index. + */ + if (sir_compare_mac_addr(session_entry->bssId, + session_entry->limReAssocbssId)) { + update_sta = true; + } + } + + if (QDF_IS_STATUS_SUCCESS(add_bss_rsp->status)) { + if (eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE == + session_entry->limMlmState) { + pe_debug("Mlm=%d %d", session_entry->limMlmState, + eLIM_MLM_WT_ADD_BSS_RSP_REASSOC_STATE); + lim_process_sta_mlm_add_bss_rsp_ft(mac_ctx, + add_bss_rsp, + session_entry); + return; + } + + /* Set MLME state */ + session_entry->limMlmState = eLIM_MLM_WT_ADD_STA_RSP_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, + session_entry->limMlmState)); + /* to know the session started for self or for peer */ + session_entry->statypeForBss = STA_ENTRY_PEER; + sta_ds = + dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session_entry->dph.dphHashTable); + if (!sta_ds) { + pe_err("Session:%d Fail to add Self Entry for STA", + session_entry->peSessionId); + mlm_assoc_cnf.resultCode = + (tSirResultCodes) eSIR_SME_REFUSED; + } else { + /* Success, handle below */ + /* Downgrade the EDCA parameters if needed */ + lim_set_active_edca_params(mac_ctx, + session_entry->gLimEdcaParams, session_entry); + lim_send_edca_params(mac_ctx, + session_entry->gLimEdcaParamsActive, + session_entry->vdev_id, false); + if (lim_add_sta_self(mac_ctx, update_sta, + session_entry) != QDF_STATUS_SUCCESS) { + /* Add STA context at HW */ + pe_err("Session:%d could not Add Self" + "Entry for the station", + session_entry->peSessionId); + mlm_assoc_cnf.resultCode = + (tSirResultCodes) eSIR_SME_REFUSED; + } + } + } else { + pe_err("SessionId: %d ADD_BSS failed!", + session_entry->peSessionId); + mlm_assoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + /* Return Assoc confirm to SME with failure */ + if (eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE == + session_entry->limMlmState) + mlm_assoc_cnf.resultCode = + (tSirResultCodes) eSIR_SME_FT_REASSOC_FAILURE; + else + mlm_assoc_cnf.resultCode = + (tSirResultCodes) eSIR_SME_REFUSED; + session_entry->add_bss_failed = true; + } + + if (mlm_assoc_cnf.resultCode != eSIR_SME_SUCCESS) { + session_entry->limMlmState = eLIM_MLM_IDLE_STATE; + /* Update PE session Id */ + mlm_assoc_cnf.sessionId = session_entry->peSessionId; + lim_post_sme_message(mac_ctx, msg_type, + (uint32_t *) &mlm_assoc_cnf); + } +} + +void lim_handle_add_bss_rsp(struct mac_context *mac_ctx, + struct add_bss_rsp *add_bss_rsp) +{ + tLimMlmStartCnf mlm_start_cnf; + struct pe_session *session_entry; + enum bss_type bss_type; + + if (!add_bss_rsp) { + pe_err("add_bss_rspis NULL"); + return; + } + + /* + * we need to process the deferred message since the + * initiating req.there might be nested request. + * in the case of nested request the new request initiated + * from the response will take care of resetting the deffered + * flag. + */ + SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true); + /* Validate SME/LIM/MLME state */ + session_entry = pe_find_session_by_vdev_id(mac_ctx, + add_bss_rsp->vdev_id); + if (!session_entry) { + pe_err("vdev id:%d Session Doesn't exist", + add_bss_rsp->vdev_id); + goto err; + } + + bss_type = session_entry->bssType; + /* update PE session Id */ + mlm_start_cnf.sessionId = session_entry->peSessionId; + if (eSIR_IBSS_MODE == bss_type) { + lim_process_ibss_mlm_add_bss_rsp(mac_ctx, add_bss_rsp, + session_entry); + } else if (eSIR_NDI_MODE == session_entry->bssType) { + lim_process_ndi_mlm_add_bss_rsp(mac_ctx, add_bss_rsp, + session_entry); + } else { + if (eLIM_SME_WT_START_BSS_STATE == session_entry->limSmeState) { + if (eLIM_MLM_WT_ADD_BSS_RSP_STATE != + session_entry->limMlmState) { + pe_err("SessionId:%d Received " + " WMA_ADD_BSS_RSP in state %X", + session_entry->peSessionId, + session_entry->limMlmState); + mlm_start_cnf.resultCode = + eSIR_SME_BSS_ALREADY_STARTED_OR_JOINED; + + lim_send_start_bss_confirm(mac_ctx, &mlm_start_cnf); + } + lim_process_ap_mlm_add_bss_rsp(mac_ctx, + add_bss_rsp); + } else { + /* Called while processing assoc response */ + lim_process_sta_mlm_add_bss_rsp(mac_ctx, add_bss_rsp, + session_entry); + } + } + +#ifdef WLAN_FEATURE_11W + if (session_entry->limRmfEnabled) { + if (QDF_STATUS_SUCCESS != + lim_send_exclude_unencrypt_ind(mac_ctx, false, + session_entry)) { + pe_err("Failed to send Exclude Unencrypted Ind"); + } + } +#endif +err: + qdf_mem_free(add_bss_rsp); +} + +void lim_process_mlm_update_hidden_ssid_rsp(struct mac_context *mac_ctx, + uint8_t vdev_id) +{ + struct pe_session *session_entry; + struct scheduler_msg message = {0}; + QDF_STATUS status; + + pe_debug("hidden ssid resp for vdev_id:%d ", vdev_id); + + session_entry = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session_entry) { + pe_err("vdev_id:%d Session Doesn't exist", + vdev_id); + return; + } + /* Update beacon */ + sch_set_fixed_beacon_fields(mac_ctx, session_entry); + lim_send_beacon(mac_ctx, session_entry); + + message.type = eWNI_SME_HIDDEN_SSID_RESTART_RSP; + message.bodyval = vdev_id; + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &message); + + if (status != QDF_STATUS_SUCCESS) + pe_err("Failed to post message %u", status); +} + +/** + * lim_process_mlm_set_sta_key_rsp() - Process STA key response + * + * @mac_ctx: Pointer to Global MAC structure + * @msg: The MsgQ header, which contains the response buffer + * + * This function is called to process the following two + * messages from HAL: + * 1) WMA_SET_BSSKEY_RSP + * 2) WMA_SET_STAKEY_RSP + * 3) WMA_SET_STA_BCASTKEY_RSP + * Upon receipt of this message from HAL, + * MLME - + * > Determines the "state" in which this message was received + * > Forwards it to the appropriate callback + * LOGIC: + * WMA_SET_BSSKEY_RSP/WMA_SET_STAKEY_RSP can be + * received by MLME while in the following state: + * MLME state = eLIM_MLM_WT_SET_BSS_KEY_STATE --OR-- + * MLME state = eLIM_MLM_WT_SET_STA_KEY_STATE --OR-- + * MLME state = eLIM_MLM_WT_SET_STA_BCASTKEY_STATE + * Based on this state, this API will determine where to + * route the message to + * Assumption: + * ONLY the MLME state is being taken into account for now. + * This is because, it appears that the handling of the + * SETKEYS REQ is handled symmetrically on both the AP & STA + * + * Return: None + */ +void lim_process_mlm_set_sta_key_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + uint8_t resp_reqd = 1; + struct sLimMlmSetKeysCnf mlm_set_key_cnf; + uint8_t session_id = 0; + uint8_t vdev_id; + struct pe_session *session_entry; + uint16_t key_len; + uint16_t result_status; + tSetStaKeyParams *set_key_params; + + SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true); + qdf_mem_zero((void *)&mlm_set_key_cnf, sizeof(tLimMlmSetKeysCnf)); + if (!msg->bodyptr) { + pe_err("msg bodyptr is NULL"); + return; + } + set_key_params = msg->bodyptr; + vdev_id = set_key_params->vdev_id; + session_entry = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session_entry) { + pe_err("session does not exist for given vdev_id %d", vdev_id); + qdf_mem_zero(msg->bodyptr, sizeof(*set_key_params)); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + lim_send_sme_set_context_rsp(mac_ctx, + mlm_set_key_cnf.peer_macaddr, + 0, eSIR_SME_INVALID_SESSION, NULL, + vdev_id); + return; + } + + session_id = session_entry->peSessionId; + pe_debug("PE session ID %d, vdev_id %d", session_id, vdev_id); + result_status = set_key_params->status; + key_len = set_key_params->key[0].keyLength; + + if (result_status == eSIR_SME_SUCCESS && key_len) + mlm_set_key_cnf.key_len_nonzero = true; + else + mlm_set_key_cnf.key_len_nonzero = false; + + + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, session_entry->limMlmState)); + if (resp_reqd) { + tpLimMlmSetKeysReq lpLimMlmSetKeysReq = + (tpLimMlmSetKeysReq) mac_ctx->lim.gpLimMlmSetKeysReq; + /* Prepare and Send LIM_MLM_SETKEYS_CNF */ + if (lpLimMlmSetKeysReq) { + qdf_copy_macaddr(&mlm_set_key_cnf.peer_macaddr, + &lpLimMlmSetKeysReq->peer_macaddr); + /* + * Free the buffer cached for the global + * mac_ctx->lim.gpLimMlmSetKeysReq + */ + qdf_mem_zero(mac_ctx->lim.gpLimMlmSetKeysReq, + sizeof(tpLimMlmSetKeysReq)); + qdf_mem_free(mac_ctx->lim.gpLimMlmSetKeysReq); + mac_ctx->lim.gpLimMlmSetKeysReq = NULL; + } else { + qdf_copy_macaddr(&mlm_set_key_cnf.peer_macaddr, + &set_key_params->macaddr); + } + mlm_set_key_cnf.sessionId = session_id; + lim_post_sme_message(mac_ctx, LIM_MLM_SETKEYS_CNF, + (uint32_t *) &mlm_set_key_cnf); + } + qdf_mem_zero(msg->bodyptr, sizeof(*set_key_params)); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; +} + +/** + * lim_process_mlm_set_bss_key_rsp() - handles BSS key + * + * @mac_ctx: A pointer to Global MAC structure + * @msg: Message from SME + * + * This function processes BSS key response and updates + * PE status accordingly. + * + * Return: NULL + */ +void lim_process_mlm_set_bss_key_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + struct sLimMlmSetKeysCnf set_key_cnf; + uint16_t result_status; + uint8_t session_id = 0; + uint8_t vdev_id; + struct pe_session *session_entry; + tpLimMlmSetKeysReq set_key_req; + uint16_t key_len; + + SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true); + qdf_mem_zero((void *)&set_key_cnf, sizeof(tLimMlmSetKeysCnf)); + if (!msg->bodyptr) { + pe_err("msg bodyptr is null"); + return; + } + vdev_id = ((tpSetBssKeyParams) msg->bodyptr)->vdev_id; + session_entry = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session_entry) { + pe_err("session does not exist for vdev_id %d", vdev_id); + qdf_mem_zero(msg->bodyptr, sizeof(tSetBssKeyParams)); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + lim_send_sme_set_context_rsp(mac_ctx, set_key_cnf.peer_macaddr, + 0, eSIR_SME_INVALID_SESSION, NULL, + vdev_id); + return; + } + + session_id = session_entry->peSessionId; + pe_debug("PE session ID %d, vdev_id %d", session_id, vdev_id); + if (eLIM_MLM_WT_SET_BSS_KEY_STATE == session_entry->limMlmState) { + result_status = + (uint16_t)(((tpSetBssKeyParams)msg->bodyptr)->status); + key_len = ((tpSetBssKeyParams)msg->bodyptr)->key[0].keyLength; + } else { + result_status = + (uint16_t)(((tpSetBssKeyParams)msg->bodyptr)->status); + key_len = ((tpSetBssKeyParams)msg->bodyptr)->key[0].keyLength; + } + + pe_debug("limMlmState %d status %d key_len %d", + session_entry->limMlmState, result_status, key_len); + + if (result_status == eSIR_SME_SUCCESS && key_len) + set_key_cnf.key_len_nonzero = true; + else + set_key_cnf.key_len_nonzero = false; + + MTRACE(mac_trace + (mac_ctx, TRACE_CODE_MLM_STATE, session_entry->peSessionId, + session_entry->limMlmState)); + set_key_req = + (tpLimMlmSetKeysReq) mac_ctx->lim.gpLimMlmSetKeysReq; + set_key_cnf.sessionId = session_id; + + /* Prepare and Send LIM_MLM_SETKEYS_CNF */ + if (set_key_req) { + qdf_copy_macaddr(&set_key_cnf.peer_macaddr, + &set_key_req->peer_macaddr); + /* + * Free the buffer cached for the + * global mac_ctx->lim.gpLimMlmSetKeysReq + */ + qdf_mem_zero(mac_ctx->lim.gpLimMlmSetKeysReq, + sizeof(*set_key_req)); + qdf_mem_free(mac_ctx->lim.gpLimMlmSetKeysReq); + mac_ctx->lim.gpLimMlmSetKeysReq = NULL; + } else { + qdf_copy_macaddr(&set_key_cnf.peer_macaddr, + &((tpSetBssKeyParams)msg->bodyptr)->macaddr); + } + qdf_mem_zero(msg->bodyptr, sizeof(tSetBssKeyParams)); + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + + lim_post_sme_message(mac_ctx, LIM_MLM_SETKEYS_CNF, + (uint32_t *) &set_key_cnf); +} + +/** + * lim_process_switch_channel_re_assoc_req() + * + ***FUNCTION: + * This function is called to send the reassoc req mgmt frame after the + * switchChannelRsp message is received from HAL. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure. + * @param pe_session - session related information. + * @param status - channel switch success/failure. + * + * @return None + */ +static void lim_process_switch_channel_re_assoc_req(struct mac_context *mac, + struct pe_session *pe_session, + QDF_STATUS status) +{ + tLimMlmReassocCnf mlmReassocCnf; + tLimMlmReassocReq *pMlmReassocReq; + + pMlmReassocReq = + (tLimMlmReassocReq *) (pe_session->pLimMlmReassocReq); + if (!pMlmReassocReq) { + pe_err("pLimMlmReassocReq does not exist for given switchChanSession"); + mlmReassocCnf.resultCode = eSIR_SME_RESOURCES_UNAVAILABLE; + goto end; + } + + if (status != QDF_STATUS_SUCCESS) { + pe_err("Change channel failed!!"); + mlmReassocCnf.resultCode = eSIR_SME_CHANNEL_SWITCH_FAIL; + goto end; + } + /* / Start reassociation failure timer */ + MTRACE(mac_trace + (mac, TRACE_CODE_TIMER_ACTIVATE, pe_session->peSessionId, + eLIM_REASSOC_FAIL_TIMER)); + if (tx_timer_activate(&mac->lim.lim_timers.gLimReassocFailureTimer) + != TX_SUCCESS) { + pe_err("could not start Reassociation failure timer"); + /* Return Reassoc confirm with */ + /* Resources Unavailable */ + mlmReassocCnf.resultCode = eSIR_SME_RESOURCES_UNAVAILABLE; + goto end; + } + /* / Prepare and send Reassociation request frame */ + lim_send_reassoc_req_mgmt_frame(mac, pMlmReassocReq, pe_session); + return; +end: + /* Free up buffer allocated for reassocReq */ + if (pMlmReassocReq) { + /* Update PE session Id */ + mlmReassocCnf.sessionId = pMlmReassocReq->sessionId; + qdf_mem_free(pMlmReassocReq); + pe_session->pLimMlmReassocReq = NULL; + } else { + mlmReassocCnf.sessionId = 0; + } + + mlmReassocCnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + /* Update PE sessio Id */ + mlmReassocCnf.sessionId = pe_session->peSessionId; + + lim_post_sme_message(mac, LIM_MLM_REASSOC_CNF, + (uint32_t *) &mlmReassocCnf); +} + + +/** + * lim_process_switch_channel_join_req() -Initiates probe request + * + * @mac_ctx - A pointer to Global MAC structure + * @pe_session - session related information. + * @status - channel switch success/failure + * + * This function is called to send the probe req mgmt frame + * after the switchChannelRsp message is received from HAL. + * + * Return None + */ +static void lim_process_switch_channel_join_req( + struct mac_context *mac_ctx, struct pe_session *session_entry, + QDF_STATUS status) +{ + tSirMacSSid ssId; + tLimMlmJoinCnf join_cnf; + uint8_t nontx_bss_id = 0; + struct bss_description *bss; + + if (status != QDF_STATUS_SUCCESS) { + pe_err("Change channel failed!!"); + goto error; + } + + if ((!session_entry) || (!session_entry->pLimMlmJoinReq) || + (!session_entry->lim_join_req)) { + pe_err("invalid pointer!!"); + goto error; + } + + bss = &session_entry->lim_join_req->bssDescription; + nontx_bss_id = bss->mbssid_info.profile_num; + + session_entry->limPrevMlmState = session_entry->limMlmState; + session_entry->limMlmState = eLIM_MLM_WT_JOIN_BEACON_STATE; + + /* Apply previously set configuration at HW */ + lim_apply_configuration(mac_ctx, session_entry); + + /* + * If deauth_before_connection is enabled, Send Deauth first to AP if + * last disconnection was caused by HB failure. + */ + if (mac_ctx->mlme_cfg->sta.deauth_before_connection) { + int apCount; + + for (apCount = 0; apCount < 2; apCount++) { + + if (!qdf_mem_cmp(session_entry->pLimMlmJoinReq->bssDescription.bssId, + mac_ctx->lim.gLimHeartBeatApMac[apCount], sizeof(tSirMacAddr))) { + + pe_err("Index %d Sessionid: %d Send deauth on " + "channel freq %d to BSSID: " QDF_MAC_ADDR_FMT, + apCount, + session_entry->peSessionId, + session_entry->curr_op_freq, + QDF_MAC_ADDR_REF( + session_entry->pLimMlmJoinReq->bssDescription.bssId)); + + lim_send_deauth_mgmt_frame(mac_ctx, eSIR_MAC_UNSPEC_FAILURE_REASON, + session_entry->pLimMlmJoinReq->bssDescription.bssId, + session_entry, false); + + qdf_mem_zero(mac_ctx->lim.gLimHeartBeatApMac[apCount], + sizeof(tSirMacAddr)); + break; + } + } + } + + /* + * MBSSID: Non Tx BSS may or may not respond to unicast + * probe request.So dont send unicast probe request + * and wait for the probe response/ beacon to post JOIN CNF + */ + if (nontx_bss_id) { + session_entry->limMlmState = eLIM_MLM_JOINED_STATE; + join_cnf.sessionId = session_entry->peSessionId; + join_cnf.resultCode = eSIR_SME_SUCCESS; + join_cnf.protStatusCode = eSIR_MAC_SUCCESS_STATUS; + lim_post_sme_message(mac_ctx, LIM_MLM_JOIN_CNF, + (uint32_t *)&join_cnf); + return; + } + + /* Wait for Beacon to announce join success */ + qdf_mem_copy(ssId.ssId, + session_entry->ssId.ssId, session_entry->ssId.length); + ssId.length = session_entry->ssId.length; + + lim_deactivate_and_change_timer(mac_ctx, + eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER); + + /* assign appropriate sessionId to the timer object */ + mac_ctx->lim.lim_timers.gLimPeriodicJoinProbeReqTimer.sessionId = + session_entry->peSessionId; + pe_debug("vdev %d Send Probe req on freq %d %.*s " QDF_MAC_ADDR_FMT, session_entry->vdev_id, + session_entry->curr_op_freq, ssId.length, ssId.ssId, + QDF_MAC_ADDR_REF( + session_entry->pLimMlmJoinReq->bssDescription.bssId)); + + /* + * We need to wait for probe response, so start join + * timeout timer.This timer will be deactivated once + * we receive probe response. + */ + MTRACE(mac_trace(mac_ctx, TRACE_CODE_TIMER_ACTIVATE, + session_entry->peSessionId, eLIM_JOIN_FAIL_TIMER)); + if (tx_timer_activate(&mac_ctx->lim.lim_timers.gLimJoinFailureTimer) != + TX_SUCCESS) { + pe_err("couldn't activate Join failure timer"); + session_entry->limMlmState = session_entry->limPrevMlmState; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, + mac_ctx->lim.gLimMlmState)); + goto error; + } + + /* include additional IE if there is */ + lim_send_probe_req_mgmt_frame(mac_ctx, &ssId, + session_entry->pLimMlmJoinReq->bssDescription.bssId, + session_entry->curr_op_freq, + session_entry->self_mac_addr, + session_entry->dot11mode, + &session_entry->lim_join_req->addIEScan.length, + session_entry->lim_join_req->addIEScan.addIEdata); + + /* Activate Join Periodic Probe Req timer */ + if (tx_timer_activate + (&mac_ctx->lim.lim_timers.gLimPeriodicJoinProbeReqTimer) + != TX_SUCCESS) { + pe_err("Periodic JoinReq timer activate failed"); + goto error; + } + + return; +error: + if (session_entry) { + if (session_entry->pLimMlmJoinReq) { + qdf_mem_free(session_entry->pLimMlmJoinReq); + session_entry->pLimMlmJoinReq = NULL; + } + if (session_entry->lim_join_req) { + qdf_mem_free(session_entry->lim_join_req); + session_entry->lim_join_req = NULL; + } + join_cnf.sessionId = session_entry->peSessionId; + } else { + join_cnf.sessionId = 0; + } + join_cnf.resultCode = eSIR_SME_RESOURCES_UNAVAILABLE; + join_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + lim_post_sme_message(mac_ctx, LIM_MLM_JOIN_CNF, (uint32_t *)&join_cnf); +} + +static void lim_handle_mon_switch_channel_rsp(struct pe_session *session, + QDF_STATUS status) +{ + struct scheduler_msg message = {0}; + + if (session->bssType != eSIR_MONITOR_MODE) + return; + + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Set channel failed for monitor mode"); + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START_REQ_FAIL, + 0, NULL); + return; + } + + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START_SUCCESS, 0, NULL); + + message.type = eWNI_SME_MONITOR_MODE_VDEV_UP; + message.bodyval = session->vdev_id; + pe_debug("vdev id %d ", session->vdev_id); + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &message)) { + pe_err("Failed to post message montior mode vdev up"); + } +} + +/** + * lim_process_switch_channel_rsp() + * + ***FUNCTION: + * This function is called to process switchChannelRsp message from HAL. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param body - message body. + * + * @return None + */ +void lim_process_switch_channel_rsp(struct mac_context *mac, + struct vdev_start_response *rsp) +{ + QDF_STATUS status; + uint16_t channelChangeReasonCode; + struct pe_session *pe_session; + /* we need to process the deferred message since the initiating req. there might be nested request. */ + /* in the case of nested request the new request initiated from the response will take care of resetting */ + /* the deffered flag. */ + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + status = rsp->status; + + pe_session = pe_find_session_by_vdev_id(mac, rsp->vdev_id); + if (!pe_session) { + pe_err("session does not exist for given sessionId"); + return; + } + pe_session->ch_switch_in_progress = false; + channelChangeReasonCode = pe_session->channelChangeReasonCode; + /* initialize it back to invalid id */ + pe_session->chainMask = rsp->chain_mask; + pe_session->smpsMode = rsp->smps_mode; + pe_session->channelChangeReasonCode = 0xBAD; + switch (channelChangeReasonCode) { + case LIM_SWITCH_CHANNEL_REASSOC: + lim_process_switch_channel_re_assoc_req(mac, pe_session, status); + break; + case LIM_SWITCH_CHANNEL_JOIN: + lim_process_switch_channel_join_req(mac, pe_session, status); + break; + + case LIM_SWITCH_CHANNEL_OPERATION: + case LIM_SWITCH_CHANNEL_HT_WIDTH: + /* + * The above code should also use the callback. + * mechanism below, there is scope for cleanup here. + * THat way all this response handler does is call the call back + * We can get rid of the reason code here. + */ + if (mac->lim.gpchangeChannelCallback) + mac->lim.gpchangeChannelCallback(mac, status, + mac->lim. + gpchangeChannelData, + pe_session); + + /* If MCC upgrade/DBS downgrade happended during channel switch, + * the policy manager connection table needs to be updated. + */ + policy_mgr_update_connection_info(mac->psoc, + pe_session->smeSessionId); + if (pe_session->opmode == QDF_P2P_CLIENT_MODE) { + pe_debug("Send p2p operating channel change conf action frame once first beacon is received on new channel"); + pe_session->send_p2p_conf_frame = true; + } + break; + case LIM_SWITCH_CHANNEL_SAP_DFS: + /* Note: This event code specific to SAP mode + * When SAP session issues channel change as performing + * DFS, we will come here. Other sessions, for e.g. P2P + * will have to define their own event code and channel + * switch handler. This is required since the SME may + * require completely different information for P2P unlike + * SAP. + */ + lim_send_sme_ap_channel_switch_resp(mac, pe_session, rsp); + /* If MCC upgrade/DBS downgrade happended during channel switch, + * the policy manager connection table needs to be updated. + */ + policy_mgr_update_connection_info(mac->psoc, + pe_session->smeSessionId); + break; + case LIM_SWITCH_CHANNEL_MONITOR: + lim_handle_mon_switch_channel_rsp(pe_session, status); + /* + * If MCC upgrade/DBS downgrade happended during channel switch, + * the policy manager connection table needs to be updated. + */ + policy_mgr_update_connection_info(mac->psoc, + pe_session->smeSessionId); + break; + default: + break; + } +} + +QDF_STATUS lim_send_beacon_ind(struct mac_context *mac, + struct pe_session *pe_session, + enum sir_bcn_update_reason reason) +{ + struct beacon_gen_params *params; + struct scheduler_msg msg = {0}; + + if (!pe_session) { + pe_err("Error:Unable to get the PESessionEntry"); + return QDF_STATUS_E_INVAL; + } + params = qdf_mem_malloc(sizeof(*params)); + if (!params) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy(params->bssid, pe_session->bssId, QDF_MAC_ADDR_SIZE); + msg.bodyptr = params; + + return sch_process_pre_beacon_ind(mac, &msg, reason); +} + +void lim_process_rx_channel_status_event(struct mac_context *mac_ctx, void *buf) +{ + struct lim_channel_status *chan_status = buf; + + if (!chan_status) { + QDF_TRACE(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_ERROR, + "%s: ACS evt report buf NULL", __func__); + return; + } + + if (mac_ctx->sap.acs_with_more_param) + lim_add_channel_status_info(mac_ctx, chan_status, + chan_status->channel_id); + else + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_WARN, + "%s: Error evt report", __func__); + + qdf_mem_free(buf); + + return; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_probe_req_frame.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_probe_req_frame.c new file mode 100644 index 0000000000000000000000000000000000000000..57897920cbc8fd1afd357d735dad46860ef1e76f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_probe_req_frame.c @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_process_probe_req_frame.cc contains the code + * for processing Probe Request Frame. + * Author: Chandra Modumudi + * Date: 02/28/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "wni_cfg.h" +#include "ani_global.h" + +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_ser_des_utils.h" +#include "parser_api.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "wlan_utility.h" + +void + +lim_send_sme_probe_req_ind(struct mac_context *mac, + tSirMacAddr peerMacAddr, + uint8_t *pProbeReqIE, + uint32_t ProbeReqIELen, struct pe_session *pe_session); + +/** + * lim_remove_timeout_pbc_sessions() - remove pbc probe req entries. + * @mac - Pointer to Global MAC structure + * @pbc - The beginning entry in WPS PBC probe request link list + * + * This function is called to remove the WPS PBC probe request entries from + * specific entry to end. + * + * Return - None + */ +static void lim_remove_timeout_pbc_sessions(struct mac_context *mac, + tSirWPSPBCSession *pbc) +{ + tSirWPSPBCSession *prev; + + while (pbc) { + prev = pbc; + pbc = pbc->next; + pe_debug("WPS PBC sessions remove"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + prev->addr.bytes, QDF_MAC_ADDR_SIZE); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + prev->uuid_e, SIR_WPS_UUID_LEN); + + qdf_mem_free(prev); + } +} + +/** + * lim_update_pbc_session_entry + * + ***FUNCTION: + * This function is called when probe request with WPS PBC IE is received + * + ***LOGIC: + * This function add the WPS PBC probe request in the WPS PBC probe request link list + * The link list is in decreased time order of probe request that is received. + * The entry that is more than 120 second is removed. + * + ***ASSUMPTIONS: + * + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param addr A pointer to probe request source MAC address + * @param uuid_e A pointer to UUIDE element of WPS IE + * @param pe_session A pointer to station PE session + * + * @return None + */ + +static void lim_update_pbc_session_entry(struct mac_context *mac, + uint8_t *addr, uint8_t *uuid_e, + struct pe_session *pe_session) +{ + tSirWPSPBCSession *pbc, *prev = NULL; + + uint32_t curTime; + + curTime = + (uint32_t) (qdf_mc_timer_get_system_ticks() / + QDF_TICKS_PER_SECOND); + + pe_debug("Receive WPS probe reques curTime: %d", curTime); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + addr, QDF_MAC_ADDR_SIZE); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + uuid_e, SIR_WPS_UUID_LEN); + + pbc = pe_session->pAPWPSPBCSession; + + while (pbc) { + if ((!qdf_mem_cmp + ((uint8_t *) pbc->addr.bytes, (uint8_t *) addr, + QDF_MAC_ADDR_SIZE)) + && (!qdf_mem_cmp((uint8_t *) pbc->uuid_e, + (uint8_t *) uuid_e, SIR_WPS_UUID_LEN))) { + if (prev) + prev->next = pbc->next; + else + pe_session->pAPWPSPBCSession = pbc->next; + break; + } + prev = pbc; + pbc = pbc->next; + } + + if (!pbc) { + pbc = qdf_mem_malloc(sizeof(tSirWPSPBCSession)); + if (!pbc) + return; + qdf_mem_copy((uint8_t *) pbc->addr.bytes, (uint8_t *) addr, + QDF_MAC_ADDR_SIZE); + + if (uuid_e) + qdf_mem_copy((uint8_t *) pbc->uuid_e, + (uint8_t *) uuid_e, SIR_WPS_UUID_LEN); + } + + pbc->next = pe_session->pAPWPSPBCSession; + pe_session->pAPWPSPBCSession = pbc; + pbc->timestamp = curTime; + + /* remove entries that have timed out */ + prev = pbc; + pbc = pbc->next; + + while (pbc) { + if (curTime > pbc->timestamp + SIR_WPS_PBC_WALK_TIME) { + prev->next = NULL; + lim_remove_timeout_pbc_sessions(mac, pbc); + break; + } + prev = pbc; + pbc = pbc->next; + } +} + +/** + * lim_wpspbc_close + * + ***FUNCTION: + * This function is called when BSS is closed + * + ***LOGIC: + * This function remove all the WPS PBC entries + * + ***ASSUMPTIONS: + * + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param pe_session A pointer to station PE session + * + * @return None + */ + +void lim_wpspbc_close(struct mac_context *mac, struct pe_session *pe_session) +{ + + lim_remove_timeout_pbc_sessions(mac, pe_session->pAPWPSPBCSession); + +} + +/** + * lim_check11b_rates + * + ***FUNCTION: + * This function is called by lim_process_probe_req_frame() upon + * Probe Request frame reception. + * + ***LOGIC: + * This function check 11b rates in supportedRates and extendedRates rates + * + ***NOTE: + * + * @param rate + * + * @return BOOLEAN + */ + +static bool lim_check11b_rates(uint8_t rate) +{ + if ((0x02 == (rate)) + || (0x04 == (rate)) + || (0x0b == (rate)) + || (0x16 == (rate)) + ) { + return true; + } + return false; +} + +/** + * lim_process_probe_req_frame: to process probe req frame + * @mac_ctx: Pointer to Global MAC structure + * @rx_pkt_info: A pointer to Buffer descriptor + associated PDUs + * @session: a ponter to session entry + * + * This function is called by limProcessMessageQueue() upon + * Probe Request frame reception. This function processes received + * Probe Request frame and responds with Probe Response. + * Only AP or STA in IBSS mode that sent last Beacon will respond to + * Probe Request. + * ASSUMPTIONS: + * 1. AP or STA in IBSS mode that sent last Beacon will always respond + * to Probe Request received with broadcast SSID. + * NOTE: + * 1. Dunno what to do with Rates received in Probe Request frame + * 2. Frames with out-of-order fields/IEs are dropped. + * + * + * Return: none + */ + +void +lim_process_probe_req_frame(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + struct pe_session *session) +{ + uint8_t *body_ptr; + tpSirMacMgmtHdr mac_hdr; + uint32_t frame_len; + tSirProbeReq probe_req; + tAniSSID ssid; + + mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + if (LIM_IS_AP_ROLE(session) || + (LIM_IS_IBSS_ROLE(session) && + (WMA_GET_RX_BEACON_SENT(rx_pkt_info)))) { + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info); + + pe_debug("Received Probe Request: %d bytes from", + frame_len); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD); + /* Get pointer to Probe Request frame body */ + body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info); + + /* check for vendor IE presence */ + if ((session->access_policy_vendor_ie) && + (session->access_policy == + LIM_ACCESS_POLICY_RESPOND_IF_IE_IS_PRESENT)) { + if (!wlan_get_vendor_ie_ptr_from_oui( + &session->access_policy_vendor_ie[2], + 3, body_ptr, frame_len)) { + pe_warn("Vendor IE is not present and access policy is: %x dropping probe request", + session->access_policy); + return; + } + } + + /* Parse Probe Request frame */ + if (sir_convert_probe_req_frame2_struct(mac_ctx, body_ptr, + frame_len, &probe_req) == QDF_STATUS_E_FAILURE) { + pe_err("Parse error ProbeReq, length: %d, SA is: " + QDF_MAC_ADDR_FMT, frame_len, + QDF_MAC_ADDR_REF(mac_hdr->sa)); + return; + } + if (session->opmode == QDF_P2P_GO_MODE) { + uint8_t i = 0, rate_11b = 0, other_rates = 0; + /* Check 11b rates in supported rates */ + for (i = 0; i < probe_req.supportedRates.numRates; + i++) { + if (lim_check11b_rates( + probe_req.supportedRates.rate[i] & + 0x7f)) + rate_11b++; + else + other_rates++; + } + + /* Check 11b rates in extended rates */ + for (i = 0; i < probe_req.extendedRates.numRates; i++) { + if (lim_check11b_rates( + probe_req.extendedRates.rate[i] & 0x7f)) + rate_11b++; + else + other_rates++; + } + + if ((rate_11b > 0) && (other_rates == 0)) { + pe_debug("Received a probe req frame with only 11b rates, SA is: "); + lim_print_mac_addr(mac_ctx, + mac_hdr->sa, LOGD); + return; + } + } + if (LIM_IS_AP_ROLE(session) && + ((session->APWPSIEs.SirWPSProbeRspIE.FieldPresent + & SIR_WPS_PROBRSP_VER_PRESENT) + && (probe_req.wscIePresent == 1) + && (probe_req.probeReqWscIeInfo.DevicePasswordID.id == + WSC_PASSWD_ID_PUSH_BUTTON) + && (probe_req.probeReqWscIeInfo.UUID_E.present == 1))) { + if (session->fwdWPSPBCProbeReq) { + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_DEBUG, + mac_hdr->sa, + QDF_MAC_ADDR_SIZE); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_DEBUG, + body_ptr, frame_len); + lim_send_sme_probe_req_ind(mac_ctx, mac_hdr->sa, + body_ptr, frame_len, session); + } else { + lim_update_pbc_session_entry(mac_ctx, + mac_hdr->sa, + probe_req.probeReqWscIeInfo.UUID_E.uuid, + session); + } + } + ssid.length = session->ssId.length; + /* Copy the SSID from sessio entry to local variable */ + qdf_mem_copy(ssid.ssId, session->ssId.ssId, + session->ssId.length); + + /* + * Compare received SSID with current SSID. If they match, + * reply with Probe Response + */ + if (probe_req.ssId.length) { + if (!ssid.length) + goto multipleSSIDcheck; + + if (!qdf_mem_cmp((uint8_t *) &ssid, + (uint8_t *) &(probe_req.ssId), + (uint8_t) (ssid.length + 1))) { + lim_send_probe_rsp_mgmt_frame(mac_ctx, + mac_hdr->sa, &ssid, + session, + probe_req.p2pIePresent); + return; + } else if (session->opmode == + QDF_P2P_GO_MODE) { + uint8_t direct_ssid[7] = "DIRECT-"; + uint8_t direct_ssid_len = 7; + + if (!qdf_mem_cmp((uint8_t *) &direct_ssid, + (uint8_t *) &(probe_req.ssId.ssId), + (uint8_t) (direct_ssid_len))) { + lim_send_probe_rsp_mgmt_frame(mac_ctx, + mac_hdr->sa, + &ssid, + session, + probe_req.p2pIePresent); + return; + } + } else { + pe_debug("Ignore ProbeReq frm with unmatch SSID received from"); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, + LOGD); + } + } else { + /* + * Broadcast SSID in the Probe Request. + * Reply with SSID we're configured with. + * Turn off the SSID length to 0 if hidden SSID feature + * is present + */ + if (session->ssidHidden) + /* + * We are returning from here as probe request + * contains the broadcast SSID. So no need to + * send the probe resp + */ + return; + lim_send_probe_rsp_mgmt_frame(mac_ctx, mac_hdr->sa, + &ssid, + session, + probe_req.p2pIePresent); + return; + } +multipleSSIDcheck: + pe_debug("Ignore ProbeReq frm with unmatch SSID rcved from"); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD); + } else { + /* Ignore received Probe Request frame */ + pe_debug("Ignoring Probe Request frame received from"); + lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD); + } + return; +} + +/** + * lim_indicate_probe_req_to_hdd + * + ***FUNCTION: + * This function is called by lim_process_probe_req_frame_multiple_bss() upon + * Probe Request frame reception. + * + ***LOGIC: + * This function processes received Probe Request frame and Pass + * Probe Request Frame to HDD. + * + * @param mac Pointer to Global MAC structure + * @param *pBd A pointer to Buffer descriptor + associated PDUs + * @param pe_session A pointer to PE session + * + * @return None + */ + +static void +lim_indicate_probe_req_to_hdd(struct mac_context *mac, uint8_t *pBd, + struct pe_session *pe_session) +{ + tpSirMacMgmtHdr pHdr; + uint32_t frameLen; + + pe_debug("Received a probe request frame"); + + pHdr = WMA_GET_RX_MAC_HEADER(pBd); + frameLen = WMA_GET_RX_PAYLOAD_LEN(pBd); + + /* send the probe req to SME. */ + lim_send_sme_mgmt_frame_ind(mac, pHdr->fc.subType, + (uint8_t *) pHdr, + (frameLen + sizeof(tSirMacMgmtHdr)), + pe_session->smeSessionId, + WMA_GET_RX_FREQ(pBd), + pe_session, + WMA_GET_RX_RSSI_NORMALIZED(pBd), + RXMGMT_FLAG_NONE); +} /*** end lim_indicate_probe_req_to_hdd() ***/ + +/** + * lim_process_probe_req_frame_multiple_bss() - to process probe req + * @mac_ctx: Pointer to Global MAC structure + * @buf_descr: A pointer to Buffer descriptor + associated PDUs + * @session: A pointer to PE session + * + * This function is called by limProcessMessageQueue() upon + * Probe Request frame reception. This function call + * lim_indicate_probe_req_to_hdd function to indicate + * Probe Request frame to HDD. It also call lim_process_probe_req_frame + * function which process received Probe Request frame and responds + * with Probe Response. + * + * @return None + */ +void +lim_process_probe_req_frame_multiple_bss(struct mac_context *mac_ctx, + uint8_t *buf_descr, struct pe_session *session) +{ + uint8_t i; + + if (session) { + if (LIM_IS_AP_ROLE(session)) { + lim_indicate_probe_req_to_hdd(mac_ctx, + buf_descr, session); + } + lim_process_probe_req_frame(mac_ctx, buf_descr, session); + return; + } + + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + session = pe_find_session_by_session_id(mac_ctx, i); + if (!session) + continue; + if (LIM_IS_AP_ROLE(session)) + lim_indicate_probe_req_to_hdd(mac_ctx, + buf_descr, session); + if (LIM_IS_AP_ROLE(session) || + LIM_IS_IBSS_ROLE(session)) + lim_process_probe_req_frame(mac_ctx, + buf_descr, session); + } +} + +/** + * lim_send_sme_probe_req_ind() + * + ***FUNCTION: + * This function is to send + * eWNI_SME_WPS_PBC_PROBE_REQ_IND message to host + * + ***PARAMS: + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * This function is used for sending eWNI_SME_WPS_PBC_PROBE_REQ_IND + * to host. + * + * @param peerMacAddr Indicates the peer MAC addr that the probe request + * is generated. + * @param pProbeReqIE pointer to RAW probe request IE + * @param ProbeReqIELen The length of probe request IE. + * @param pe_session A pointer to PE session + * + * @return None + */ +void +lim_send_sme_probe_req_ind(struct mac_context *mac, + tSirMacAddr peerMacAddr, + uint8_t *pProbeReqIE, + uint32_t ProbeReqIELen, struct pe_session *pe_session) +{ + tSirSmeProbeReqInd *pSirSmeProbeReqInd; + struct scheduler_msg msgQ = {0}; + + pSirSmeProbeReqInd = qdf_mem_malloc(sizeof(tSirSmeProbeReqInd)); + if (!pSirSmeProbeReqInd) + return; + + msgQ.type = eWNI_SME_WPS_PBC_PROBE_REQ_IND; + msgQ.bodyval = 0; + msgQ.bodyptr = pSirSmeProbeReqInd; + + pSirSmeProbeReqInd->messageType = eWNI_SME_WPS_PBC_PROBE_REQ_IND; + pSirSmeProbeReqInd->length = sizeof(*pSirSmeProbeReqInd); + pSirSmeProbeReqInd->sessionId = pe_session->smeSessionId; + + qdf_mem_copy(pSirSmeProbeReqInd->bssid.bytes, pe_session->bssId, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(pSirSmeProbeReqInd->WPSPBCProbeReq.peer_macaddr.bytes, + peerMacAddr, QDF_MAC_ADDR_SIZE); + + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, msgQ.type)); + + if (ProbeReqIELen > sizeof(pSirSmeProbeReqInd->WPSPBCProbeReq. + probeReqIE)) { + ProbeReqIELen = sizeof(pSirSmeProbeReqInd->WPSPBCProbeReq. + probeReqIE); + } + + pSirSmeProbeReqInd->WPSPBCProbeReq.probeReqIELen = + (uint16_t) ProbeReqIELen; + qdf_mem_copy(pSirSmeProbeReqInd->WPSPBCProbeReq.probeReqIE, pProbeReqIE, + ProbeReqIELen); + + lim_sys_process_mmh_msg_api(mac, &msgQ); + +} /*** end lim_send_sme_probe_req_ind() ***/ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_probe_rsp_frame.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_probe_rsp_frame.c new file mode 100644 index 0000000000000000000000000000000000000000..17d19332bf13b9d443670d150774edff1632c38d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_probe_rsp_frame.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_process_probe_rsp_frame.cc contains the code + * for processing Probe Response Frame. + * Author: Chandra Modumudi + * Date: 03/01/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "wni_api.h" +#include "wni_cfg.h" +#include "ani_global.h" +#include "sch_api.h" +#include "utils_api.h" +#include "lim_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_prop_exts_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_send_messages.h" + +#include "parser_api.h" + +/** + * lim_validate_ie_information_in_probe_rsp_frame () - validates ie + * information in probe response. + * @mac_ctx: mac context + * @pRxPacketInfo: Rx packet info + * + * Return: 0 on success, one on failure + */ +static QDF_STATUS +lim_validate_ie_information_in_probe_rsp_frame(struct mac_context *mac_ctx, + uint8_t *pRxPacketInfo) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t *pframe; + uint32_t nframe; + uint32_t missing_rsn_bytes; + + /* + * Validate a Probe response frame for malformed frame. + * If the frame is malformed then do not consider as it + * may cause problem fetching wrong IE values + */ + + if (WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo) < + (SIR_MAC_B_PR_SSID_OFFSET + SIR_MAC_MIN_IE_LEN)) + return QDF_STATUS_E_FAILURE; + + pframe = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + nframe = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); + missing_rsn_bytes = 0; + + status = sir_validate_and_rectify_ies(mac_ctx, + pframe, nframe, &missing_rsn_bytes); + + if (status == QDF_STATUS_SUCCESS) + WMA_GET_RX_MPDU_LEN(pRxPacketInfo) += missing_rsn_bytes; + + return status; +} + +/** + * lim_process_probe_rsp_frame() - processes received Probe Response frame + * @mac_ctx: Pointer to Global MAC structure + * @rx_Packet_info: A pointer to Buffer descriptor + associated PDUs + * @session_entry: Handle to the session. + * + * This function processes received Probe Response frame. + * Frames with out-of-order IEs are dropped. + * In case of IBSS, join 'success' makes MLM state machine + * transition into 'BSS started' state. This may have to change + * depending on supporting what kinda Authentication in IBSS. + * + * Return: None + */ +void +lim_process_probe_rsp_frame(struct mac_context *mac_ctx, uint8_t *rx_Packet_info, + struct pe_session *session_entry) +{ + uint8_t *body; + uint32_t frame_len = 0; + tSirMacAddr current_bssid; + tpSirMacMgmtHdr header; + tSirProbeRespBeacon *probe_rsp; + uint8_t qos_enabled = false; + uint8_t wme_enabled = false; + uint32_t chan_freq = 0; + + if (!session_entry) { + pe_err("session_entry is NULL"); + return; + } + + probe_rsp = qdf_mem_malloc(sizeof(tSirProbeRespBeacon)); + if (!probe_rsp) { + pe_err("Unable to allocate memory"); + return; + } + + probe_rsp->ssId.length = 0; + probe_rsp->wpa.length = 0; + + header = WMA_GET_RX_MAC_HEADER(rx_Packet_info); + + /* Validate IE information before processing Probe Response Frame */ + if (lim_validate_ie_information_in_probe_rsp_frame(mac_ctx, + rx_Packet_info) != + QDF_STATUS_SUCCESS) { + pe_err("Parse error ProbeResponse, length=%d", frame_len); + qdf_mem_free(probe_rsp); + return; + } + + frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_Packet_info); + pe_debug("Probe Resp(len %d): " QDF_MAC_ADDR_FMT " RSSI %d", + WMA_GET_RX_MPDU_LEN(rx_Packet_info), + QDF_MAC_ADDR_REF(header->bssId), + (uint)abs((int8_t) + WMA_GET_RX_RSSI_NORMALIZED(rx_Packet_info))); + /* Get pointer to Probe Response frame body */ + body = WMA_GET_RX_MPDU_DATA(rx_Packet_info); + /* Enforce Mandatory IEs */ + if ((sir_convert_probe_frame2_struct(mac_ctx, + body, frame_len, probe_rsp) == QDF_STATUS_E_FAILURE) || + !probe_rsp->ssidPresent) { + pe_err("Parse error ProbeResponse, length=%d", frame_len); + qdf_mem_free(probe_rsp); + return; + } + + if (session_entry->limMlmState == + eLIM_MLM_WT_JOIN_BEACON_STATE) { + /* + * Either Beacon/probe response is required. + * Hence store it in same buffer. + */ + if (session_entry->beacon) { + qdf_mem_free(session_entry->beacon); + session_entry->beacon = NULL; + session_entry->bcnLen = 0; + } + session_entry->bcnLen = + WMA_GET_RX_PAYLOAD_LEN(rx_Packet_info); + session_entry->beacon = + qdf_mem_malloc(session_entry->bcnLen); + if (!session_entry->beacon) { + pe_err("No Memory to store beacon"); + } else { + /* + * Store the Beacon/ProbeRsp. + * This is sent to csr/hdd in join cnf response. + */ + qdf_mem_copy(session_entry->beacon, + WMA_GET_RX_MPDU_DATA + (rx_Packet_info), + session_entry->bcnLen); + } + /* STA in WT_JOIN_BEACON_STATE */ + lim_check_and_announce_join_success(mac_ctx, probe_rsp, + header, + session_entry); + } else if (session_entry->limMlmState == + eLIM_MLM_LINK_ESTABLISHED_STATE) { + tpDphHashNode sta_ds = NULL; + /* + * Check if this Probe Response is for + * our Probe Request sent upon reaching + * heart beat threshold + */ + sir_copy_mac_addr(current_bssid, session_entry->bssId); + if (qdf_mem_cmp(current_bssid, header->bssId, + sizeof(tSirMacAddr))) { + qdf_mem_free(probe_rsp); + return; + } + if (!LIM_IS_CONNECTION_ACTIVE(session_entry)) { + pe_warn("Recved Probe Resp from AP,AP-alive"); + if (probe_rsp->HTInfo.present) { + chan_freq = + wlan_reg_legacy_chan_to_freq(mac_ctx->pdev, + probe_rsp->HTInfo.primaryChannel); + lim_received_hb_handler(mac_ctx, chan_freq, + session_entry); + } else + lim_received_hb_handler(mac_ctx, + probe_rsp->chan_freq, + session_entry); + } + if (LIM_IS_STA_ROLE(session_entry) && + !wma_is_csa_offload_enabled()) { + if (probe_rsp->channelSwitchPresent) { + /* + * on receiving channel switch announcement + * from AP, delete all TDLS peers before + * leaving BSS and proceed for channel switch + */ + lim_update_tdls_set_state_for_fw(session_entry, + false); + lim_delete_tdls_peers(mac_ctx, session_entry); + + lim_update_channel_switch(mac_ctx, + probe_rsp, + session_entry); + } else if (session_entry->gLimSpecMgmt.dot11hChanSwState + == eLIM_11H_CHANSW_RUNNING) { + lim_cancel_dot11h_channel_switch( + mac_ctx, session_entry); + } + } + /* + * Now Process EDCA Parameters, if EDCAParamSet + * count is different. + * -- While processing beacons in link established + * state if it is determined that + * QoS Info IE has a different count for EDCA Params, + * and EDCA IE is not present in beacon, + * then probe req is sent out to get the EDCA params. + */ + sta_ds = dph_get_hash_entry(mac_ctx, + DPH_STA_HASH_INDEX_PEER, + &session_entry->dph.dphHashTable); + limGetQosMode(session_entry, &qos_enabled); + limGetWmeMode(session_entry, &wme_enabled); + pe_debug("wmeEdcaPresent: %d wme_enabled: %d" + "edcaPresent: %d, qos_enabled: %d" + "edcaParams.qosInfo.count: %d" + "schObject.gLimEdcaParamSetCount: %d", + probe_rsp->wmeEdcaPresent, wme_enabled, + probe_rsp->edcaPresent, qos_enabled, + probe_rsp->edcaParams.qosInfo.count, + session_entry->gLimEdcaParamSetCount); + + if (((probe_rsp->wmeEdcaPresent && wme_enabled) || + (probe_rsp->edcaPresent && qos_enabled)) && + (probe_rsp->edcaParams.qosInfo.count != + session_entry->gLimEdcaParamSetCount)) { + if (sch_beacon_edca_process(mac_ctx, + &probe_rsp->edcaParams, + session_entry) != QDF_STATUS_SUCCESS) { + pe_err("EDCA param process error"); + } else if (sta_ds) { + /* + * If needed, downgrade the + * EDCA parameters + */ + lim_set_active_edca_params(mac_ctx, + session_entry-> + gLimEdcaParams, + session_entry); + lim_send_edca_params(mac_ctx, + session_entry->gLimEdcaParamsActive, + session_entry->vdev_id, false); + } else { + pe_err("SelfEntry missing in Hash"); + } + } + if (session_entry->fWaitForProbeRsp == true) { + pe_warn("Check probe resp for caps change"); + lim_detect_change_in_ap_capabilities( + mac_ctx, probe_rsp, session_entry); + } + } else { + if (LIM_IS_IBSS_ROLE(session_entry) && + (session_entry->limMlmState == + eLIM_MLM_BSS_STARTED_STATE)) + lim_handle_ibss_coalescing(mac_ctx, probe_rsp, + rx_Packet_info, session_entry); + } + qdf_mem_free(probe_rsp); + + /* Ignore Probe Response frame in all other states */ + return; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_sme_req_messages.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_sme_req_messages.c new file mode 100644 index 0000000000000000000000000000000000000000..d10581d1779c507bc7a3de391fb2cec980408a25 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_sme_req_messages.c @@ -0,0 +1,6274 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_process_sme_req_messages.cc contains the code + * for processing SME request messages. + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "cds_api.h" +#include "wni_api.h" +#include "wni_cfg.h" +#include "sir_api.h" +#include "sch_api.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_sme_req_utils.h" +#include "lim_ibss_peer_mgmt.h" +#include "lim_admit_control.h" +#include "dph_hash_table.h" +#include "lim_send_messages.h" +#include "lim_api.h" +#include "wmm_apsd.h" +#include "sir_mac_prot_def.h" +#include "rrm_api.h" +#include "nan_datapath.h" +#include "sap_api.h" +#include +#include "cds_regdomain.h" +#include +#include "lim_process_fils.h" +#include "wlan_utility.h" +#include +#include "../../core/src/vdev_mgr_ops.h" +#include "wma.h" + +/* SME REQ processing function templates */ +static bool __lim_process_sme_sys_ready_ind(struct mac_context *, uint32_t *); +static bool __lim_process_sme_start_bss_req(struct mac_context *, + struct scheduler_msg *pMsg); +static void __lim_process_sme_disassoc_req(struct mac_context *, uint32_t *); +static void __lim_process_sme_disassoc_cnf(struct mac_context *, uint32_t *); +static void __lim_process_sme_deauth_req(struct mac_context *, uint32_t *); +static bool __lim_process_sme_stop_bss_req(struct mac_context *, + struct scheduler_msg *pMsg); +static void lim_process_sme_channel_change_request(struct mac_context *mac, + uint32_t *pMsg); +static void lim_process_sme_start_beacon_req(struct mac_context *mac, uint32_t *pMsg); +static void lim_process_sme_dfs_csa_ie_request(struct mac_context *mac, uint32_t *pMsg); +static void lim_process_nss_update_request(struct mac_context *mac, uint32_t *pMsg); +static void lim_process_set_ie_req(struct mac_context *mac, uint32_t *pMsg); + +static void lim_start_bss_update_add_ie_buffer(struct mac_context *mac, + uint8_t **pDstData_buff, + uint16_t *pDstDataLen, + uint8_t *pSrcData_buff, + uint16_t srcDataLen); + +static void lim_update_add_ie_buffer(struct mac_context *mac, + uint8_t **pDstData_buff, + uint16_t *pDstDataLen, + uint8_t *pSrcData_buff, uint16_t srcDataLen); +static bool lim_update_ibss_prop_add_ies(struct mac_context *mac, + uint8_t **pDstData_buff, + uint16_t *pDstDataLen, + tSirModifyIE *pModifyIE); +static void lim_process_modify_add_ies(struct mac_context *mac, uint32_t *pMsg); + +static void lim_process_update_add_ies(struct mac_context *mac, uint32_t *pMsg); + +static void lim_process_ext_change_channel(struct mac_context *mac_ctx, + uint32_t *msg); + +/** + * lim_process_set_hw_mode() - Send set HW mode command to WMA + * @mac: Globacl MAC pointer + * @msg: Message containing the hw mode index + * + * Send the set HW mode command to WMA + * + * Return: QDF_STATUS_SUCCESS if message posting is successful + */ +static QDF_STATUS lim_process_set_hw_mode(struct mac_context *mac, uint32_t *msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg message = {0}; + struct policy_mgr_hw_mode *req_msg; + uint32_t len; + struct s_sir_set_hw_mode *buf; + struct scheduler_msg resp_msg = {0}; + struct sir_set_hw_mode_resp *param; + + buf = (struct s_sir_set_hw_mode *) msg; + if (!buf) { + pe_err("Set HW mode param is NULL"); + status = QDF_STATUS_E_INVAL; + /* To free the active command list */ + goto fail; + } + + len = sizeof(*req_msg); + + req_msg = qdf_mem_malloc(len); + if (!req_msg) { + pe_debug("failed to allocate memory"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + + req_msg->hw_mode_index = buf->set_hw.hw_mode_index; + req_msg->reason = buf->set_hw.reason; + /* Other parameters are not needed for WMA */ + + message.bodyptr = req_msg; + message.type = SIR_HAL_PDEV_SET_HW_MODE; + + pe_debug("Posting SIR_HAL_SOC_SET_HW_MOD to WMA"); + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("scheduler_post_msg failed!(err=%d)", + status); + qdf_mem_free(req_msg); + goto fail; + } + return status; +fail: + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return QDF_STATUS_E_FAILURE; + param->status = SET_HW_MODE_STATUS_ECANCELED; + param->cfgd_hw_mode_index = 0; + param->num_vdev_mac_entries = 0; + resp_msg.type = eWNI_SME_SET_HW_MODE_RESP; + resp_msg.bodyptr = param; + resp_msg.bodyval = 0; + lim_sys_process_mmh_msg_api(mac, &resp_msg); + return status; +} + +/** + * lim_process_set_dual_mac_cfg_req() - Set dual mac config command to WMA + * @mac: Global MAC pointer + * @msg: Message containing the dual mac config parameter + * + * Send the set dual mac config command to WMA + * + * Return: QDF_STATUS_SUCCESS if message posting is successful + */ +static QDF_STATUS lim_process_set_dual_mac_cfg_req(struct mac_context *mac, + uint32_t *msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg message = {0}; + struct policy_mgr_dual_mac_config *req_msg; + uint32_t len; + struct sir_set_dual_mac_cfg *buf; + struct scheduler_msg resp_msg = {0}; + struct sir_dual_mac_config_resp *param; + + buf = (struct sir_set_dual_mac_cfg *) msg; + if (!buf) { + pe_err("Set Dual mac config is NULL"); + status = QDF_STATUS_E_INVAL; + /* To free the active command list */ + goto fail; + } + + len = sizeof(*req_msg); + + req_msg = qdf_mem_malloc(len); + if (!req_msg) { + pe_debug("failed to allocate memory"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + + req_msg->scan_config = buf->set_dual_mac.scan_config; + req_msg->fw_mode_config = buf->set_dual_mac.fw_mode_config; + /* Other parameters are not needed for WMA */ + + message.bodyptr = req_msg; + message.type = SIR_HAL_PDEV_DUAL_MAC_CFG_REQ; + + pe_debug("Post SIR_HAL_PDEV_DUAL_MAC_CFG_REQ to WMA: %x %x", + req_msg->scan_config, req_msg->fw_mode_config); + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("scheduler_post_msg failed!(err=%d)", + status); + qdf_mem_free(req_msg); + goto fail; + } + return status; +fail: + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return QDF_STATUS_E_FAILURE; + param->status = SET_HW_MODE_STATUS_ECANCELED; + resp_msg.type = eWNI_SME_SET_DUAL_MAC_CFG_RESP; + resp_msg.bodyptr = param; + resp_msg.bodyval = 0; + lim_sys_process_mmh_msg_api(mac, &resp_msg); + return status; +} + +/** + * lim_process_set_antenna_mode_req() - Set antenna mode command + * to WMA + * @mac: Global MAC pointer + * @msg: Message containing the antenna mode parameter + * + * Send the set antenna mode command to WMA + * + * Return: QDF_STATUS_SUCCESS if message posting is successful + */ +static QDF_STATUS lim_process_set_antenna_mode_req(struct mac_context *mac, + uint32_t *msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg message = {0}; + struct sir_antenna_mode_param *req_msg; + struct sir_set_antenna_mode *buf; + struct scheduler_msg resp_msg = {0}; + struct sir_antenna_mode_resp *param; + + buf = (struct sir_set_antenna_mode *) msg; + if (!buf) { + pe_err("Set antenna mode is NULL"); + status = QDF_STATUS_E_INVAL; + /* To free the active command list */ + goto fail; + } + + req_msg = qdf_mem_malloc(sizeof(*req_msg)); + if (!req_msg) { + pe_debug("failed to allocate memory"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + + req_msg->num_rx_chains = buf->set_antenna_mode.num_rx_chains; + req_msg->num_tx_chains = buf->set_antenna_mode.num_tx_chains; + + message.bodyptr = req_msg; + message.type = SIR_HAL_SOC_ANTENNA_MODE_REQ; + + pe_debug("Post SIR_HAL_SOC_ANTENNA_MODE_REQ to WMA: %d %d", + req_msg->num_rx_chains, + req_msg->num_tx_chains); + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("scheduler_post_msg failed!(err=%d)", + status); + qdf_mem_free(req_msg); + goto fail; + } + return status; +fail: + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return QDF_STATUS_E_NOMEM; + param->status = SET_ANTENNA_MODE_STATUS_ECANCELED; + resp_msg.type = eWNI_SME_SET_ANTENNA_MODE_RESP; + resp_msg.bodyptr = param; + resp_msg.bodyval = 0; + lim_sys_process_mmh_msg_api(mac, &resp_msg); + return status; +} + +/** + * __lim_is_sme_assoc_cnf_valid() -- Validate ASSOC_CNF message + * @assoc_cnf: Pointer to Received ASSOC_CNF message + * + * This function is called by __lim_process_sme_assoc_cnf_new() upon + * receiving SME_ASSOC_CNF. + * + * Return: true when received SME_ASSOC_CNF is formatted correctly + * false otherwise + */ +static bool __lim_is_sme_assoc_cnf_valid(struct assoc_cnf *assoc_cnf) +{ + if (qdf_is_macaddr_group(&assoc_cnf->peer_macaddr)) + return false; + + return true; +} + +/** + * __lim_is_defered_msg_for_radar() - Defers the message if radar is detected + * @mac_ctx: Pointer to Global MAC structure + * @message: Pointer to message posted from SME to LIM. + * + * Has role only if 11h is enabled. Not used on STA side. + * Defers the message if radar is detected. + * + * Return: true, if defered otherwise return false. + */ +static bool +__lim_is_defered_msg_for_radar(struct mac_context *mac_ctx, + struct scheduler_msg *message) +{ + /* + * fRadarDetCurOperChan will be set only if we + * detect radar in current operating channel and + * System Role == AP ROLE + * + * TODO: Need to take care radar detection. + * + * if (LIM_IS_RADAR_DETECTED(mac_ctx)) + */ + if (0) { + if (lim_defer_msg(mac_ctx, message) != TX_SUCCESS) { + pe_err("Could not defer Msg: %d", message->type); + return false; + } + pe_debug("Defer the message, in learn mode type: %d", + message->type); + return true; + } + return false; +} + +/** + * __lim_process_sme_sys_ready_ind () - Process ready indication from WMA + * @mac: Global MAC context + * @msg_buf: Message from WMA + * + * handles the notification from HDD. PE just forwards this message to HAL. + * + * Return: true-Posting to HAL failed, so PE will consume the buffer. + * false-Posting to HAL successful, so HAL will consume the buffer. + */ + +static bool __lim_process_sme_sys_ready_ind(struct mac_context *mac, + uint32_t *msg_buf) +{ + struct scheduler_msg msg = {0}; + struct sme_ready_req *ready_req = (struct sme_ready_req *)msg_buf; + + msg.type = WMA_SYS_READY_IND; + msg.reserved = 0; + msg.bodyptr = msg_buf; + msg.bodyval = 0; + + if (ANI_DRIVER_TYPE(mac) != QDF_DRIVER_TYPE_MFG) { + ready_req->pe_roam_synch_cb = pe_roam_synch_callback; + ready_req->pe_disconnect_cb = pe_disconnect_callback; + pe_register_mgmt_rx_frm_callback(mac); + pe_register_callbacks_with_wma(mac, ready_req); + mac->lim.sme_msg_callback = ready_req->sme_msg_cb; + mac->lim.stop_roaming_callback = ready_req->stop_roaming_cb; + } + + pe_debug("sending WMA_SYS_READY_IND msg to HAL"); + MTRACE(mac_trace_msg_tx(mac, NO_SESSION, msg.type)); + + if (QDF_STATUS_SUCCESS != wma_post_ctrl_msg(mac, &msg)) { + pe_err("wma_post_ctrl_msg failed"); + return true; + } + return false; +} + +#ifdef WLAN_BCN_RECV_FEATURE +/** + * lim_register_bcn_report_send_cb() - Register bcn receive start + * indication handler callback + * @mac: Pointer to Global MAC structure + * @msg: A pointer to the SME message buffer + * + * Once driver gets QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING vendor + * command with attribute for start only. LIM layer register a sme + * callback through this function. + * + * Return: None. + */ +static void lim_register_bcn_report_send_cb(struct mac_context *mac, + struct scheduler_msg *msg) +{ + if (!msg) { + pe_err("Invalid message"); + return; + } + + mac->lim.sme_bcn_rcv_callback = msg->callback; +} +#else +static inline +void lim_register_bcn_report_send_cb(struct mac_context *mac, + struct scheduler_msg *msg) +{ +} +#endif +/** + *lim_configure_ap_start_bss_session() - Configure the AP Start BSS in session. + *@mac_ctx: Pointer to Global MAC structure + *@session: A pointer to session entry + *@sme_start_bss_req: Start BSS Request from upper layers. + * + * This function is used to configure the start bss parameters + * in to the session. + * + * Return: None. + */ +static void +lim_configure_ap_start_bss_session(struct mac_context *mac_ctx, + struct pe_session *session, + struct start_bss_req *sme_start_bss_req) +{ + session->limSystemRole = eLIM_AP_ROLE; + session->privacy = sme_start_bss_req->privacy; + session->fwdWPSPBCProbeReq = sme_start_bss_req->fwdWPSPBCProbeReq; + session->authType = sme_start_bss_req->authType; + /* Store the DTIM period */ + session->dtimPeriod = (uint8_t) sme_start_bss_req->dtimPeriod; + /* Enable/disable UAPSD */ + session->apUapsdEnable = sme_start_bss_req->apUapsdEnable; + if (session->opmode == QDF_P2P_GO_MODE) { + session->proxyProbeRspEn = 0; + } else { + /* + * To detect PBC overlap in SAP WPS mode, + * Host handles Probe Requests. + */ + if (SAP_WPS_DISABLED == sme_start_bss_req->wps_state) + session->proxyProbeRspEn = 1; + else + session->proxyProbeRspEn = 0; + } + session->ssidHidden = sme_start_bss_req->ssidHidden; + session->wps_state = sme_start_bss_req->wps_state; + session->sap_dot11mc = sme_start_bss_req->sap_dot11mc; + session->vendor_vht_sap = + sme_start_bss_req->vendor_vht_sap; + lim_get_short_slot_from_phy_mode(mac_ctx, session, session->gLimPhyMode, + &session->shortSlotTimeSupported); + session->isCoalesingInIBSSAllowed = + sme_start_bss_req->isCoalesingInIBSSAllowed; + + session->beacon_tx_rate = sme_start_bss_req->beacon_tx_rate; + +} + +/** + * lim_send_start_vdev_req() - send vdev start request + *@session: pe session + *@mlm_start_req: vdev start req + * + * Return: QDF_STATUS + */ +static QDF_STATUS +lim_send_start_vdev_req(struct pe_session *session, tLimMlmStartReq *mlm_start_req) +{ + return wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START, + sizeof(*mlm_start_req), + mlm_start_req); +} + +#ifdef WLAN_FEATURE_11AX +static void lim_strip_he_ies_from_add_ies(struct mac_context *mac_ctx, + struct pe_session *session) +{ + struct add_ie_params *add_ie = &session->add_ie_params; + QDF_STATUS status; + uint8_t he_cap_buff[DOT11F_IE_HE_CAP_MAX_LEN + 2]; + uint8_t he_op_buff[DOT11F_IE_HE_OP_MAX_LEN + 2]; + + qdf_mem_zero(he_cap_buff, sizeof(he_cap_buff)); + qdf_mem_zero(he_op_buff, sizeof(he_op_buff)); + + status = lim_strip_ie(mac_ctx, add_ie->probeRespBCNData_buff, + &add_ie->probeRespBCNDataLen, + DOT11F_EID_HE_CAP, ONE_BYTE, + HE_CAP_OUI_TYPE, (uint8_t)HE_CAP_OUI_SIZE, + he_cap_buff, DOT11F_IE_HE_CAP_MAX_LEN); + if (status != QDF_STATUS_SUCCESS) + pe_debug("Failed to strip HE cap IE status: %d", status); + + + status = lim_strip_ie(mac_ctx, add_ie->probeRespBCNData_buff, + &add_ie->probeRespBCNDataLen, + DOT11F_EID_HE_OP, ONE_BYTE, + HE_OP_OUI_TYPE, (uint8_t)HE_OP_OUI_SIZE, + he_op_buff, DOT11F_IE_HE_OP_MAX_LEN); + if (status != QDF_STATUS_SUCCESS) + pe_debug("Failed to strip HE op IE status: %d", status); +} +#else +static inline void lim_strip_he_ies_from_add_ies(struct mac_context *mac_ctx, + struct pe_session *session) +{ +} +#endif + +/** + * __lim_handle_sme_start_bss_request() - process SME_START_BSS_REQ message + *@mac_ctx: Pointer to Global MAC structure + *@msg_buf: A pointer to the SME message buffer + * + * This function is called to process SME_START_BSS_REQ message + * from HDD or upper layer application. + * + * Return: None + */ +static void +__lim_handle_sme_start_bss_request(struct mac_context *mac_ctx, uint32_t *msg_buf) +{ + uint16_t size; + uint32_t val = 0; + tSirMacChanNum channel_number; + tLimMlmStartReq *mlm_start_req = NULL; + struct start_bss_req *sme_start_bss_req = NULL; + tSirResultCodes ret_code = eSIR_SME_SUCCESS; + /* Flag Used in case of IBSS to Auto generate BSSID. */ + uint32_t auto_gen_bssid = false; + uint8_t session_id; + struct pe_session *session = NULL; + uint8_t vdev_id = 0xFF; + uint32_t chanwidth; + struct vdev_type_nss *vdev_type_nss; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + +/* FEATURE_WLAN_DIAG_SUPPORT */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + /* + * Since the session is not created yet, sending NULL. + * The response should have the correct state. + */ + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_START_BSS_REQ_EVENT, + NULL, 0, 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + size = sizeof(*sme_start_bss_req); + sme_start_bss_req = qdf_mem_malloc(size); + if (!sme_start_bss_req) { + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto free; + } + qdf_mem_copy(sme_start_bss_req, msg_buf, size); + vdev_id = sme_start_bss_req->vdev_id; + + if ((mac_ctx->lim.gLimSmeState == eLIM_SME_OFFLINE_STATE) || + (mac_ctx->lim.gLimSmeState == eLIM_SME_IDLE_STATE)) { + if (!lim_is_sme_start_bss_req_valid(mac_ctx, + sme_start_bss_req)) { + pe_warn("Received invalid eWNI_SME_START_BSS_REQ"); + ret_code = eSIR_SME_INVALID_PARAMETERS; + goto free; + } + channel_number = wlan_reg_freq_to_chan(mac_ctx->pdev, + sme_start_bss_req->oper_ch_freq); + /* + * This is the place where PE is going to create a session. + * If session is not existed, then create a new session + */ + session = pe_find_session_by_bssid(mac_ctx, + sme_start_bss_req->bssid.bytes, &session_id); + if (session) { + pe_warn("Session Already exists for given BSSID"); + ret_code = eSIR_SME_BSS_ALREADY_STARTED_OR_JOINED; + session = NULL; + goto free; + } else { + session = pe_create_session(mac_ctx, + sme_start_bss_req->bssid.bytes, + &session_id, mac_ctx->lim.maxStation, + sme_start_bss_req->bssType, + sme_start_bss_req->vdev_id, + sme_start_bss_req->bssPersona); + if (!session) { + pe_warn("Session Can not be created"); + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto free; + } + + /* Update the beacon/probe filter in mac_ctx */ + lim_set_bcn_probe_filter(mac_ctx, session, + &sme_start_bss_req->ssId, + channel_number); + } + + if (QDF_NDI_MODE != sme_start_bss_req->bssPersona) { + /* Probe resp add ie */ + lim_start_bss_update_add_ie_buffer(mac_ctx, + &session->add_ie_params.probeRespData_buff, + &session->add_ie_params.probeRespDataLen, + sme_start_bss_req->add_ie_params. + probeRespData_buff, + sme_start_bss_req->add_ie_params. + probeRespDataLen); + + /* Probe Beacon add ie */ + lim_start_bss_update_add_ie_buffer(mac_ctx, + &session->add_ie_params.probeRespBCNData_buff, + &session->add_ie_params.probeRespBCNDataLen, + sme_start_bss_req->add_ie_params. + probeRespBCNData_buff, + sme_start_bss_req->add_ie_params. + probeRespBCNDataLen); + + /* Assoc resp IE */ + lim_start_bss_update_add_ie_buffer(mac_ctx, + &session->add_ie_params.assocRespData_buff, + &session->add_ie_params.assocRespDataLen, + sme_start_bss_req->add_ie_params. + assocRespData_buff, + sme_start_bss_req->add_ie_params. + assocRespDataLen); + } + /* Store the session related params in newly created session */ + session->pLimStartBssReq = sme_start_bss_req; + session->ht_config = sme_start_bss_req->ht_config; + session->vht_config = sme_start_bss_req->vht_config; + + sir_copy_mac_addr(session->self_mac_addr, + sme_start_bss_req->self_macaddr.bytes); + + /* Copy SSID to session table */ + qdf_mem_copy((uint8_t *) &session->ssId, + (uint8_t *) &sme_start_bss_req->ssId, + (sme_start_bss_req->ssId.length + 1)); + + session->nwType = sme_start_bss_req->nwType; + + session->beaconParams.beaconInterval = + sme_start_bss_req->beaconInterval; + + /* Store the oper freq in session Table */ + session->curr_op_freq = sme_start_bss_req->oper_ch_freq; + + /* Update the phymode */ + session->gLimPhyMode = sme_start_bss_req->nwType; + + session->maxTxPower = wlan_reg_get_channel_reg_power_for_freq( + mac_ctx->pdev, session->curr_op_freq); + /* Store the dot 11 mode in to the session Table */ + session->dot11mode = sme_start_bss_req->dot11mode; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + session->cc_switch_mode = + sme_start_bss_req->cc_switch_mode; +#endif + session->htCapability = + IS_DOT11_MODE_HT(session->dot11mode); + session->vhtCapability = + IS_DOT11_MODE_VHT(session->dot11mode); + + if (IS_DOT11_MODE_HE(session->dot11mode)) { + lim_update_session_he_capable(mac_ctx, session); + lim_copy_bss_he_cap(session, sme_start_bss_req); + } else if (wlan_reg_is_6ghz_chan_freq(session->curr_op_freq)) { + pe_err("Invalid oper_ch_freq %d for dot11mode %d", + session->curr_op_freq, session->dot11mode); + ret_code = eSIR_SME_INVALID_PARAMETERS; + goto free; + } else { + lim_strip_he_ies_from_add_ies(mac_ctx, session); + } + + session->txLdpcIniFeatureEnabled = + sme_start_bss_req->txLdpcIniFeatureEnabled; +#ifdef WLAN_FEATURE_11W + session->limRmfEnabled = sme_start_bss_req->pmfCapable ? 1 : 0; + pe_debug("RMF enabled: %d", session->limRmfEnabled); +#endif + + qdf_mem_copy((void *)&session->rateSet, + (void *)&sme_start_bss_req->operationalRateSet, + sizeof(tSirMacRateSet)); + qdf_mem_copy((void *)&session->extRateSet, + (void *)&sme_start_bss_req->extendedRateSet, + sizeof(tSirMacRateSet)); + + if (wlan_reg_is_5ghz_ch_freq(session->curr_op_freq)) + vdev_type_nss = &mac_ctx->vdev_type_nss_5g; + else + vdev_type_nss = &mac_ctx->vdev_type_nss_2g; + + switch (sme_start_bss_req->bssType) { + case eSIR_INFRA_AP_MODE: + lim_configure_ap_start_bss_session(mac_ctx, session, + sme_start_bss_req); + if (session->opmode == QDF_SAP_MODE) + session->vdev_nss = vdev_type_nss->sap; + else + session->vdev_nss = vdev_type_nss->p2p_go; + break; + case eSIR_IBSS_MODE: + session->limSystemRole = eLIM_STA_IN_IBSS_ROLE; + lim_get_short_slot_from_phy_mode(mac_ctx, session, + session->gLimPhyMode, + &session->shortSlotTimeSupported); + + /* + * initialize to "OPEN". + * will be updated upon key installation + */ + session->encryptType = eSIR_ED_NONE; + session->vdev_nss = vdev_type_nss->ibss; + + break; + case eSIR_NDI_MODE: + session->vdev_nss = vdev_type_nss->ndi; + session->limSystemRole = eLIM_NDI_ROLE; + break; + + + /* + * There is one more mode called auto mode. + * which is used no where + */ + + /* FORBUILD -TEMPFIX.. HOW TO use AUTO MODE????? */ + + default: + /* not used anywhere...used in scan function */ + break; + } + + session->nss = session->vdev_nss; + if (!mac_ctx->mlme_cfg->vht_caps.vht_cap_info.enable2x2) + session->nss = 1; + /* + * Allocate memory for the array of + * parsed (Re)Assoc request structure + */ + if (sme_start_bss_req->bssType == eSIR_INFRA_AP_MODE) { + session->parsedAssocReq = + qdf_mem_malloc(session->dph.dphHashTable. + size * sizeof(tpSirAssocReq)); + if (!session->parsedAssocReq) { + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto free; + } + } + + if (!sme_start_bss_req->oper_ch_freq && + sme_start_bss_req->bssType != eSIR_NDI_MODE) { + pe_err("Received invalid eWNI_SME_START_BSS_REQ"); + ret_code = eSIR_SME_INVALID_PARAMETERS; + goto free; + } +#ifdef QCA_HT_2040_COEX + if (sme_start_bss_req->obssEnabled) + session->htSupportedChannelWidthSet = + session->htCapability; + else +#endif + session->htSupportedChannelWidthSet = + (sme_start_bss_req->sec_ch_offset) ? 1 : 0; + session->htSecondaryChannelOffset = + sme_start_bss_req->sec_ch_offset; + session->htRecommendedTxWidthSet = + (session->htSecondaryChannelOffset) ? 1 : 0; + if (lim_is_session_he_capable(session) || + session->vhtCapability || session->htCapability) { + chanwidth = sme_start_bss_req->vht_channel_width; + session->ch_width = chanwidth; + session->ch_center_freq_seg0 = + sme_start_bss_req->center_freq_seg0; + session->ch_center_freq_seg1 = + sme_start_bss_req->center_freq_seg1; + lim_update_he_bw_cap_mcs(session, NULL); + } + + /* Delete pre-auth list if any */ + lim_delete_pre_auth_list(mac_ctx); + + /* + * keep the RSN/WPA IE information in PE Session Entry + * later will be using this to check when received (Re)Assoc req + */ + lim_set_rs_nie_wp_aiefrom_sme_start_bss_req_message(mac_ctx, + &sme_start_bss_req->rsnIE, session); + + if (LIM_IS_AP_ROLE(session) || + LIM_IS_IBSS_ROLE(session) || + LIM_IS_NDI_ROLE(session)) { + session->gLimProtectionControl = + sme_start_bss_req->protEnabled; + /* + * each byte will have the following info + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * reserved reserved RIFS Lsig n-GF ht20 11g 11b + */ + qdf_mem_copy((void *)&session->cfgProtection, + (void *)&sme_start_bss_req->ht_capab, + sizeof(uint16_t)); + /* Initialize WPS PBC session link list */ + session->pAPWPSPBCSession = NULL; + } + /* Prepare and Issue LIM_MLM_START_REQ to MLM */ + mlm_start_req = qdf_mem_malloc(sizeof(tLimMlmStartReq)); + if (!mlm_start_req) { + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto free; + } + + /* Copy SSID to the MLM start structure */ + qdf_mem_copy((uint8_t *) &mlm_start_req->ssId, + (uint8_t *) &sme_start_bss_req->ssId, + sme_start_bss_req->ssId.length + 1); + mlm_start_req->ssidHidden = sme_start_bss_req->ssidHidden; + mlm_start_req->obssProtEnabled = + sme_start_bss_req->obssProtEnabled; + + mlm_start_req->bssType = session->bssType; + + /* Fill PE session Id from the session Table */ + mlm_start_req->sessionId = session->peSessionId; + + if (mlm_start_req->bssType == eSIR_INFRA_AP_MODE || + mlm_start_req->bssType == eSIR_NDI_MODE) { + /* + * Copy the BSSId from sessionTable to + * mlmStartReq struct + */ + sir_copy_mac_addr(mlm_start_req->bssId, session->bssId); + } else { + /* ibss mode */ + mac_ctx->lim.gLimIbssCoalescingHappened = false; + auto_gen_bssid = mac_ctx->mlme_cfg->ibss.auto_bssid; + + if (!auto_gen_bssid) { + /* + * We're not auto generating BSSID. + * Instead, get it from session entry + */ + sir_copy_mac_addr(mlm_start_req->bssId, + session->bssId); + /* + * Start IBSS group BSSID + * Auto Generating BSSID. + */ + auto_gen_bssid = ((mlm_start_req->bssId[0] & + 0x01) ? true : false); + } + + if (auto_gen_bssid) { + /* + * if BSSID is not any uc id. + * then use locally generated BSSID. + * Autogenerate the BSSID + */ + lim_get_random_bssid(mac_ctx, + mlm_start_req->bssId); + mlm_start_req->bssId[0] = 0x02; + + /* + * Copy randomly generated BSSID + * to the session Table + */ + sir_copy_mac_addr(session->bssId, + mlm_start_req->bssId); + } + } + /* store the channel num in mlmstart req structure */ + mlm_start_req->oper_ch_freq = session->curr_op_freq; + mlm_start_req->cbMode = sme_start_bss_req->cbMode; + mlm_start_req->beaconPeriod = + session->beaconParams.beaconInterval; + mlm_start_req->cac_duration_ms = + sme_start_bss_req->cac_duration_ms; + mlm_start_req->dfs_regdomain = + sme_start_bss_req->dfs_regdomain; + if (LIM_IS_AP_ROLE(session)) { + mlm_start_req->dtimPeriod = session->dtimPeriod; + mlm_start_req->wps_state = session->wps_state; + session->cac_duration_ms = + mlm_start_req->cac_duration_ms; + session->dfs_regdomain = mlm_start_req->dfs_regdomain; + } else { + val = mac_ctx->mlme_cfg->sap_cfg.dtim_interval; + mlm_start_req->dtimPeriod = (uint8_t) val; + } + + mlm_start_req->cfParamSet.cfpPeriod = + mac_ctx->mlme_cfg->rates.cfp_period; + mlm_start_req->cfParamSet.cfpMaxDuration = + mac_ctx->mlme_cfg->rates.cfp_max_duration; + + /* + * this may not be needed anymore now, + * as rateSet is now included in the + * session entry and MLM has session context. + */ + qdf_mem_copy((void *)&mlm_start_req->rateSet, + (void *)&session->rateSet, + sizeof(tSirMacRateSet)); + + /* Now populate the 11n related parameters */ + mlm_start_req->nwType = session->nwType; + mlm_start_req->htCapable = session->htCapability; + + mlm_start_req->htOperMode = mac_ctx->lim.gHTOperMode; + /* Unused */ + mlm_start_req->dualCTSProtection = + mac_ctx->lim.gHTDualCTSProtection; + mlm_start_req->txChannelWidthSet = + session->htRecommendedTxWidthSet; + + session->limRFBand = lim_get_rf_band( + sme_start_bss_req->oper_ch_freq); + + /* Initialize 11h Enable Flag */ + session->lim11hEnable = 0; + if (mlm_start_req->bssType != eSIR_IBSS_MODE && + (CHAN_HOP_ALL_BANDS_ENABLE || + REG_BAND_5G == session->limRFBand)) { + session->lim11hEnable = + mac_ctx->mlme_cfg->gen.enabled_11h; + + if (session->lim11hEnable && + (eSIR_INFRA_AP_MODE == + mlm_start_req->bssType)) { + session->lim11hEnable = + mac_ctx->mlme_cfg-> + dfs_cfg.dfs_master_capable; + } + } + + if (!session->lim11hEnable) + mac_ctx->mlme_cfg->power.local_power_constraint = 0; + + mlm_start_req->beacon_tx_rate = session->beacon_tx_rate; + + session->limPrevSmeState = session->limSmeState; + session->limSmeState = eLIM_SME_WT_START_BSS_STATE; + + pe_debug("Freq %d width %d freq0 %d freq1 %d, dot11mode %d nss %d vendor vht %d", + session->curr_op_freq, session->ch_width, + session->ch_center_freq_seg0, + session->ch_center_freq_seg1, + session->dot11mode, session->vdev_nss, + session->vendor_vht_sap); + MTRACE(mac_trace + (mac_ctx, TRACE_CODE_SME_STATE, + session->peSessionId, + session->limSmeState)); + + qdf_status = lim_send_start_vdev_req(session, mlm_start_req); + if (QDF_IS_STATUS_ERROR(qdf_status)) + goto free; + qdf_mem_free(mlm_start_req); + return; + } else { + + pe_err("Received unexpected START_BSS_REQ, in state %X", + mac_ctx->lim.gLimSmeState); + ret_code = eSIR_SME_BSS_ALREADY_STARTED_OR_JOINED; + goto free; + } /* if (mac_ctx->lim.gLimSmeState == eLIM_SME_OFFLINE_STATE) */ + +free: + if ((session) && + (session->pLimStartBssReq == sme_start_bss_req)) { + session->pLimStartBssReq = NULL; + } + if (sme_start_bss_req) + qdf_mem_free(sme_start_bss_req); + if (mlm_start_req) + qdf_mem_free(mlm_start_req); + if (session) { + pe_delete_session(mac_ctx, session); + session = NULL; + } + lim_send_sme_start_bss_rsp(mac_ctx, eWNI_SME_START_BSS_RSP, ret_code, + session, vdev_id); +} + +/** + * __lim_process_sme_start_bss_req() - Call handler to start BSS + * + * @mac: Global MAC context + * @pMsg: Message pointer + * + * Wrapper for the function __lim_handle_sme_start_bss_request + * This message will be defered until softmac come out of + * scan mode or if we have detected radar on the current + * operating channel. + * + * return true - If we consumed the buffer + * false - If have defered the message. + */ +static bool __lim_process_sme_start_bss_req(struct mac_context *mac, + struct scheduler_msg *pMsg) +{ + if (__lim_is_defered_msg_for_radar(mac, pMsg)) { + /** + * If message defered, buffer is not consumed yet. + * So return false + */ + return false; + } + + __lim_handle_sme_start_bss_request(mac, (uint32_t *) pMsg->bodyptr); + return true; +} + +/** + * lim_get_random_bssid() + * + * FUNCTION:This function is called to process generate the random number for bssid + * This function is called to process SME_SCAN_REQ message + * from HDD or upper layer application. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * 1. geneartes the unique random number for bssid in ibss + * + * @param mac Pointer to Global MAC structure + * @param *data Pointer to bssid buffer + * @return None + */ +void lim_get_random_bssid(struct mac_context *mac, uint8_t *data) +{ + uint32_t random[2]; + + random[0] = qdf_mc_timer_get_system_ticks(); + random[0] |= (random[0] << 15); + random[1] = random[0] >> 1; + qdf_mem_copy(data, random, sizeof(tSirMacAddr)); +} + +#ifdef WLAN_FEATURE_SAE + +/** + * lim_update_sae_config()- This API update SAE session info to csr config + * from join request. + * @session: PE session + * @sme_join_req: pointer to join request + * + * Return: None + */ +static void lim_update_sae_config(struct pe_session *session, + struct join_req *sme_join_req) +{ + session->sae_pmk_cached = sme_join_req->sae_pmk_cached; + + pe_debug("pmk_cached %d for BSSID=" QDF_MAC_ADDR_FMT, + session->sae_pmk_cached, + QDF_MAC_ADDR_REF(sme_join_req->bssDescription.bssId)); +} +#else +static inline void lim_update_sae_config(struct pe_session *session, + struct join_req *sme_join_req) +{} +#endif + +/** + * lim_send_join_req() - send vdev start request for assoc + *@session: pe session + *@mlm_join_req: join req + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_send_join_req(struct pe_session *session, + tLimMlmJoinReq *mlm_join_req) +{ + QDF_STATUS status; + + status = mlme_set_assoc_type(session->vdev, VDEV_ASSOC); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + return wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START, + sizeof(*mlm_join_req), + mlm_join_req); +} + +/** + * lim_send_reassoc_req() - send vdev start request for reassoc + *@session: pe session + *@mlm_join_req: join req + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_send_reassoc_req(struct pe_session *session, + tLimMlmReassocReq *reassoc_req) +{ + QDF_STATUS status; + + status = mlme_set_assoc_type(session->vdev, VDEV_REASSOC); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + if (wlan_vdev_mlme_get_state(session->vdev) != WLAN_VDEV_S_UP) { + pe_err("Reassoc req in unexpected vdev SM state:%d", + wlan_vdev_mlme_get_state(session->vdev)); + return QDF_STATUS_E_FAILURE; + } + + lim_process_mlm_reassoc_req(session->mac_ctx, reassoc_req); + return QDF_STATUS_SUCCESS; +} + +/** + * lim_send_ft_reassoc_req() - send vdev start request for ft_reassoc + *@session: pe session + *@mlm_join_req: join req + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_send_ft_reassoc_req(struct pe_session *session, + tLimMlmReassocReq *reassoc_req) +{ + QDF_STATUS status; + + status = mlme_set_assoc_type(session->vdev, VDEV_FT_REASSOC); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + if (wlan_vdev_mlme_get_state(session->vdev) == WLAN_VDEV_S_UP) { + pe_err("ft_reassoc req in unexpected vdev SM state:%d", + wlan_vdev_mlme_get_state(session->vdev)); + return QDF_STATUS_E_FAILURE; + } + + return wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START, + sizeof(*reassoc_req), + reassoc_req); +} + +#ifdef WLAN_FEATURE_11W +bool +lim_get_vdev_rmf_capable(struct mac_context *mac, struct pe_session *session) +{ + struct wlan_objmgr_vdev *vdev; + uint16_t rsn_caps; + bool peer_rmf_capable = false; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, + session->vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + pe_err("Invalid vdev"); + return false; + } + rsn_caps = (uint16_t)wlan_crypto_get_param(vdev, + WLAN_CRYPTO_PARAM_RSN_CAP); + if (wlan_crypto_vdev_has_mgmtcipher( + vdev, + (1 << WLAN_CRYPTO_CIPHER_AES_GMAC) | + (1 << WLAN_CRYPTO_CIPHER_AES_GMAC_256) | + (1 << WLAN_CRYPTO_CIPHER_AES_CMAC)) && + (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)) + peer_rmf_capable = true; + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + pe_debug("vdev %d peer_rmf_capable %d rsn_caps 0x%x", + session->vdev_id, peer_rmf_capable, + rsn_caps); + + return peer_rmf_capable; +} +#endif + +/** + * __lim_process_sme_join_req() - process SME_JOIN_REQ message + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: A pointer to the SME message buffer + * + * This function is called to process SME_JOIN_REQ message + * from HDD or upper layer application. + * + * Return: None + */ +static void +__lim_process_sme_join_req(struct mac_context *mac_ctx, void *msg_buf) +{ + struct join_req *in_req = msg_buf; + struct join_req *sme_join_req = NULL; + tLimMlmJoinReq *mlm_join_req; + tSirResultCodes ret_code = eSIR_SME_SUCCESS; + uint32_t val = 0; + uint8_t session_id; + uint8_t bss_chan_id; + struct pe_session *session = NULL; + uint8_t vdev_id = 0; + int8_t local_power_constraint = 0, reg_max = 0; + uint16_t ie_len; + const uint8_t *vendor_ie; + struct bss_description *bss_desc; + QDF_STATUS status; + struct lim_max_tx_pwr_attr tx_pwr_attr = {0}; + + if (!mac_ctx || !msg_buf) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + FL("JOIN REQ with invalid data")); + return; + } + +/* FEATURE_WLAN_DIAG_SUPPORT */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + /* + * Not sending any session, since it is not created yet. + * The response whould have correct state. + */ + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_JOIN_REQ_EVENT, NULL, 0, 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + /* + * Expect Join request in idle state. + * Reassociate request is expected in link established state. + */ + + /* Global SME and LIM states are not defined yet for BT-AMP Support */ + if (mac_ctx->lim.gLimSmeState == eLIM_SME_IDLE_STATE) { + sme_join_req = qdf_mem_malloc(in_req->length); + if (!sme_join_req) { + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto end; + } + qdf_mem_copy(sme_join_req, in_req, in_req->length); + + if (!lim_is_sme_join_req_valid(mac_ctx, sme_join_req)) { + /* Received invalid eWNI_SME_JOIN_REQ */ + /* Log the event */ + pe_warn("vdev_id:%d JOIN REQ with invalid data", + sme_join_req->vdev_id); + ret_code = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + /* + * Update the capability here itself as this is used in + * lim_extract_ap_capability() below. If not updated issues + * like not honoring power constraint on 1st association after + * driver loading might occur. + */ + lim_update_rrm_capability(mac_ctx, sme_join_req); + + bss_desc = &sme_join_req->bssDescription; + /* check for the existence of start BSS session */ + session = pe_find_session_by_bssid(mac_ctx, bss_desc->bssId, + &session_id); + bss_chan_id = wlan_reg_freq_to_chan(mac_ctx->pdev, + bss_desc->chan_freq); + + if (session) { + pe_err("Session(%d) Already exists for BSSID: " + QDF_MAC_ADDR_FMT " in limSmeState = %X", + session_id, + QDF_MAC_ADDR_REF(bss_desc->bssId), + session->limSmeState); + + if (session->limSmeState == eLIM_SME_LINK_EST_STATE && + session->smeSessionId == sme_join_req->vdev_id) { + /* + * Received eWNI_SME_JOIN_REQ for same + * BSS as currently associated. + * Log the event and send success + */ + pe_warn("SessionId: %d", session_id); + pe_warn("JOIN_REQ for current joined BSS"); + /* Send Join success response to host */ + ret_code = eSIR_SME_ALREADY_JOINED_A_BSS; + session = NULL; + goto end; + } else { + pe_err("JOIN_REQ not for current joined BSS"); + ret_code = eSIR_SME_REFUSED; + session = NULL; + goto end; + } + } else { + /* + * Session Entry does not exist for given BSSId + * Try to Create a new session + */ + session = pe_create_session(mac_ctx, bss_desc->bssId, + &session_id, mac_ctx->lim.maxStation, + eSIR_INFRASTRUCTURE_MODE, + sme_join_req->vdev_id, + sme_join_req->staPersona); + if (!session) { + pe_err("Session Can not be created"); + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto end; + } + /* Update the beacon/probe filter in mac_ctx */ + lim_set_bcn_probe_filter(mac_ctx, session, + &sme_join_req->ssId, + bss_chan_id); + } + session->max_amsdu_num = sme_join_req->max_amsdu_num; + session->enable_session_twt_support = + sme_join_req->enable_session_twt_support; + /* + * Store Session related parameters + */ + + /* store the smejoin req handle in session table */ + session->lim_join_req = sme_join_req; + + /* Store beaconInterval */ + session->beaconParams.beaconInterval = + bss_desc->beaconInterval; + + session->ht_config = sme_join_req->ht_config; + session->vht_config = sme_join_req->vht_config; + + /* Copying of bssId is already done, while creating session */ + sir_copy_mac_addr(session->self_mac_addr, + sme_join_req->self_mac_addr); + + session->statypeForBss = STA_ENTRY_PEER; + session->limWmeEnabled = sme_join_req->isWMEenabled; + session->limQosEnabled = sme_join_req->isQosEnabled; + session->wps_registration = sme_join_req->wps_registration; + session->he_with_wep_tkip = sme_join_req->he_with_wep_tkip; + + session->enable_bcast_probe_rsp = + sme_join_req->enable_bcast_probe_rsp; + + /* Store vendor specific IE for CISCO AP */ + ie_len = (bss_desc->length + sizeof(bss_desc->length) - + GET_FIELD_OFFSET(struct bss_description, ieFields)); + + vendor_ie = wlan_get_vendor_ie_ptr_from_oui( + SIR_MAC_CISCO_OUI, SIR_MAC_CISCO_OUI_SIZE, + ((uint8_t *)&bss_desc->ieFields), ie_len); + + if (vendor_ie) + session->isCiscoVendorAP = true; + else + session->isCiscoVendorAP = false; + + /* Copy the dot 11 mode in to the session table */ + + session->dot11mode = sme_join_req->dot11mode; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + session->cc_switch_mode = sme_join_req->cc_switch_mode; +#endif + session->nwType = bss_desc->nwType; + session->enableAmpduPs = sme_join_req->enableAmpduPs; + session->enableHtSmps = sme_join_req->enableHtSmps; + session->htSmpsvalue = sme_join_req->htSmps; + session->send_smps_action = + sme_join_req->send_smps_action; + /* + * By default supported NSS 1x1 is set to true + * and later on updated while determining session + * supported rates which is the intersection of + * self and peer rates + */ + session->supported_nss_1x1 = true; + + /* Copy oper freq to the session Table */ + session->curr_op_freq = bss_desc->chan_freq; + session->vhtCapability = + IS_DOT11_MODE_VHT(session->dot11mode); + if (session->vhtCapability) { + if (session->opmode == QDF_STA_MODE) { + session->vht_config.su_beam_formee = + sme_join_req->vht_config.su_beam_formee; + } else { + session->vht_config.su_beam_formee = 0; + } + session->enableVhtpAid = + sme_join_req->enableVhtpAid; + session->enableVhtGid = + sme_join_req->enableVhtGid; + } + + /*Phy mode */ + session->gLimPhyMode = bss_desc->nwType; + handle_ht_capabilityand_ht_info(mac_ctx, session); + session->force_24ghz_in_ht20 = + sme_join_req->force_24ghz_in_ht20; + /* cbMode is already merged value of peer and self - + * done by csr in csr_get_cb_mode_from_ies */ + session->htSupportedChannelWidthSet = + (sme_join_req->cbMode) ? 1 : 0; + session->htRecommendedTxWidthSet = + session->htSupportedChannelWidthSet; + session->htSecondaryChannelOffset = sme_join_req->cbMode; + + if (PHY_DOUBLE_CHANNEL_HIGH_PRIMARY == sme_join_req->cbMode) { + session->ch_center_freq_seg0 = + wlan_reg_freq_to_chan( + mac_ctx->pdev, session->curr_op_freq) - 2; + session->ch_width = CH_WIDTH_40MHZ; + } else if (PHY_DOUBLE_CHANNEL_LOW_PRIMARY == + sme_join_req->cbMode) { + session->ch_center_freq_seg0 = + wlan_reg_freq_to_chan( + mac_ctx->pdev, session->curr_op_freq) + 2; + session->ch_width = CH_WIDTH_40MHZ; + } else { + session->ch_center_freq_seg0 = 0; + session->ch_width = CH_WIDTH_20MHZ; + } + + if (IS_DOT11_MODE_HE(session->dot11mode)) { + lim_update_session_he_capable(mac_ctx, session); + lim_copy_join_req_he_cap(session, sme_join_req); + } + + + /* Record if management frames need to be protected */ + session->limRmfEnabled = + lim_get_vdev_rmf_capable(mac_ctx, session); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + session->rssi = bss_desc->rssi; +#endif + + /* Copy the SSID from smejoinreq to session entry */ + session->ssId.length = sme_join_req->ssId.length; + qdf_mem_copy(session->ssId.ssId, sme_join_req->ssId.ssId, + session->ssId.length); + + /* + * Determin 11r or ESE connection based on input from SME + * which inturn is dependent on the profile the user wants + * to connect to, So input is coming from supplicant + */ + session->is11Rconnection = sme_join_req->is11Rconnection; + session->connected_akm = sme_join_req->akm; + session->is_adaptive_11r_connection = + sme_join_req->is_adaptive_11r_connection; +#ifdef FEATURE_WLAN_ESE + session->isESEconnection = sme_join_req->isESEconnection; +#endif + session->isFastTransitionEnabled = + sme_join_req->isFastTransitionEnabled; + + session->isFastRoamIniFeatureEnabled = + sme_join_req->isFastRoamIniFeatureEnabled; + session->txLdpcIniFeatureEnabled = + sme_join_req->txLdpcIniFeatureEnabled; + + lim_update_fils_config(mac_ctx, session, sme_join_req); + lim_update_sae_config(session, sme_join_req); + if (session->bssType == eSIR_INFRASTRUCTURE_MODE) { + session->limSystemRole = eLIM_STA_ROLE; + } else { + /* + * Throw an error and return and make + * sure to delete the session. + */ + pe_err("recvd JOIN_REQ with invalid bss type %d", + session->bssType); + ret_code = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + if (sme_join_req->addIEScan.length) + qdf_mem_copy(&session->lim_join_req->addIEScan, + &sme_join_req->addIEScan, + sizeof(tSirAddie)); + + if (sme_join_req->addIEAssoc.length) + qdf_mem_copy(&session->lim_join_req->addIEAssoc, + &sme_join_req->addIEAssoc, + sizeof(tSirAddie)); + + val = sizeof(tLimMlmJoinReq) + + session->lim_join_req->bssDescription.length + 2; + mlm_join_req = qdf_mem_malloc(val); + if (!mlm_join_req) { + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto end; + } + + /* PE SessionId is stored as a part of JoinReq */ + mlm_join_req->sessionId = session->peSessionId; + + /* copy operational rate from session */ + qdf_mem_copy((void *)&session->rateSet, + (void *)&sme_join_req->operationalRateSet, + sizeof(tSirMacRateSet)); + qdf_mem_copy((void *)&session->extRateSet, + (void *)&sme_join_req->extendedRateSet, + sizeof(tSirMacRateSet)); + /* + * this may not be needed anymore now, as rateSet is now + * included in the session entry and MLM has session context. + */ + qdf_mem_copy((void *)&mlm_join_req->operationalRateSet, + (void *)&session->rateSet, + sizeof(tSirMacRateSet)); + + session->encryptType = sme_join_req->UCEncryptionType; + + session->supported_nss_1x1 = sme_join_req->supported_nss_1x1; + session->vdev_nss = sme_join_req->vdev_nss; + session->nss = sme_join_req->nss; + session->nss_forced_1x1 = sme_join_req->nss_forced_1x1; + + mlm_join_req->bssDescription.length = + session->lim_join_req->bssDescription.length; + + qdf_mem_copy((uint8_t *) &mlm_join_req->bssDescription.bssId, + (uint8_t *) + &session->lim_join_req->bssDescription.bssId, + session->lim_join_req->bssDescription.length + 2); + + session->limCurrentBssCaps = + session->lim_join_req->bssDescription.capabilityInfo; + + reg_max = wlan_reg_get_channel_reg_power_for_freq( + mac_ctx->pdev, session->curr_op_freq); + local_power_constraint = reg_max; + + lim_extract_ap_capability(mac_ctx, + (uint8_t *) + session->lim_join_req->bssDescription.ieFields, + lim_get_ielen_from_bss_description( + &session->lim_join_req->bssDescription), + &session->limCurrentBssQosCaps, + &session->gLimCurrentBssUapsd, + &local_power_constraint, session); + + tx_pwr_attr.reg_max = reg_max; + tx_pwr_attr.ap_tx_power = local_power_constraint; + tx_pwr_attr.ini_tx_power = + mac_ctx->mlme_cfg->power.max_tx_power; + tx_pwr_attr.frequency = session->curr_op_freq; + + session->maxTxPower = lim_get_max_tx_power(mac_ctx, + &tx_pwr_attr); + session->def_max_tx_pwr = session->maxTxPower; + + pe_debug("Reg %d local %d session %d join req %d", + reg_max, local_power_constraint, session->maxTxPower, + sme_join_req->powerCap.maxTxPower); + + if (sme_join_req->powerCap.maxTxPower > session->maxTxPower) + sme_join_req->powerCap.maxTxPower = session->maxTxPower; + + if (session->gLimCurrentBssUapsd) { + session->gUapsdPerAcBitmask = + session->lim_join_req->uapsdPerAcBitmask; + pe_debug("UAPSD flag for all AC - 0x%2x", + session->gUapsdPerAcBitmask); + + /* resetting the dynamic uapsd mask */ + session->gUapsdPerAcDeliveryEnableMask = 0; + session->gUapsdPerAcTriggerEnableMask = 0; + } + + session->limRFBand = lim_get_rf_band(session->curr_op_freq); + + /* Initialize 11h Enable Flag */ + if (session->limRFBand == REG_BAND_5G) + session->lim11hEnable = + mac_ctx->mlme_cfg->gen.enabled_11h; + else + session->lim11hEnable = 0; + /* + * To care of the scenario when STA transitions from + * IBSS to Infrastructure mode. + */ + mac_ctx->lim.gLimIbssCoalescingHappened = false; + + session->limPrevSmeState = session->limSmeState; + session->limSmeState = eLIM_SME_WT_JOIN_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session->peSessionId, + session->limSmeState)); + + /* Indicate whether spectrum management is enabled */ + session->spectrumMgtEnabled = + sme_join_req->spectrumMgtIndicator; + /* Enable MBSSID only for station */ + session->is_mbssid_enabled = wma_is_mbssid_enabled(); + + /* Enable the spectrum management if this is a DFS channel */ + if (session->country_info_present && + lim_isconnected_on_dfs_channel( + mac_ctx, + wlan_reg_freq_to_chan( + mac_ctx->pdev, session->curr_op_freq))) + session->spectrumMgtEnabled = true; + + session->isOSENConnection = sme_join_req->isOSENConnection; + pe_debug("Freq %d width %d freq0 %d freq1 %d, Smps %d: mode %d action %d, nss 1x1 %d vdev_nss %d nss %d cbMode %d dot11mode %d subfer %d subfee %d csn %d is_cisco %d", + session->curr_op_freq, session->ch_width, + session->ch_center_freq_seg0, + session->ch_center_freq_seg1, + session->enableHtSmps, session->htSmpsvalue, + session->send_smps_action, session->supported_nss_1x1, + session->vdev_nss, session->nss, + sme_join_req->cbMode, session->dot11mode, + session->vht_config.su_beam_former, + session->vht_config.su_beam_formee, + session->vht_config.csnof_beamformer_antSup, + session->isCiscoVendorAP); + + /* Issue LIM_MLM_JOIN_REQ to MLM */ + status = lim_send_join_req(session, mlm_join_req); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(mlm_join_req); + ret_code = eSIR_SME_REFUSED; + goto end; + } + return; + + } else { + /* Received eWNI_SME_JOIN_REQ un expected state */ + pe_err("received unexpected SME_JOIN_REQ in state %X", + mac_ctx->lim.gLimSmeState); + lim_print_sme_state(mac_ctx, LOGE, mac_ctx->lim.gLimSmeState); + ret_code = eSIR_SME_UNEXPECTED_REQ_RESULT_CODE; + session = NULL; + goto end; + } + +end: + vdev_id = in_req->vdev_id; + + if (sme_join_req) { + qdf_mem_free(sme_join_req); + sme_join_req = NULL; + if (session) + session->lim_join_req = NULL; + } + if (ret_code != eSIR_SME_SUCCESS) { + if (session) { + pe_delete_session(mac_ctx, session); + session = NULL; + } + } + pe_debug("Send failure status on vdev_id: %d with ret_code: %d", + vdev_id, ret_code); + lim_send_sme_join_reassoc_rsp(mac_ctx, eWNI_SME_JOIN_RSP, ret_code, + eSIR_MAC_UNSPEC_FAILURE_STATUS, session, vdev_id); +} + +uint8_t lim_get_max_tx_power(struct mac_context *mac, + struct lim_max_tx_pwr_attr *attr) +{ + uint8_t max_tx_power = 0; + uint8_t tx_power; + + if (!attr) + return 0; + + if (wlan_reg_get_fcc_constraint(mac->pdev, attr->frequency)) + return attr->reg_max; + + tx_power = QDF_MIN(attr->reg_max, attr->ap_tx_power); + tx_power = QDF_MIN(tx_power, attr->ini_tx_power); + + if (tx_power >= MIN_TX_PWR_CAP && tx_power <= MAX_TX_PWR_CAP) + max_tx_power = tx_power; + else if (tx_power < MIN_TX_PWR_CAP) + max_tx_power = MIN_TX_PWR_CAP; + else + max_tx_power = MAX_TX_PWR_CAP; + + return max_tx_power; +} + +/** + * __lim_process_sme_reassoc_req() - process reassoc req + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: pointer to the SME message buffer + * + * This function is called to process SME_REASSOC_REQ message + * from HDD or upper layer application. + * + * Return: None + */ + +static void __lim_process_sme_reassoc_req(struct mac_context *mac_ctx, + void *msg_buf) +{ + uint16_t caps; + uint32_t val; + struct join_req *in_req = msg_buf; + struct join_req *reassoc_req; + tLimMlmReassocReq *mlm_reassoc_req; + tSirResultCodes ret_code = eSIR_SME_SUCCESS; + struct pe_session *session_entry = NULL; + uint8_t session_id; + uint8_t vdev_id; + int8_t local_pwr_constraint = 0, reg_max = 0; + uint32_t tele_bcn_en = 0; + QDF_STATUS status; + + vdev_id = in_req->vdev_id; + + reassoc_req = qdf_mem_malloc(in_req->length); + if (!reassoc_req) { + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto end; + } + qdf_mem_copy(reassoc_req, in_req, in_req->length); + + if (!lim_is_sme_join_req_valid(mac_ctx, reassoc_req)) { + /* + * Received invalid eWNI_SME_REASSOC_REQ + */ + pe_warn("received SME_REASSOC_REQ with invalid data"); + + ret_code = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + session_entry = pe_find_session_by_bssid(mac_ctx, + reassoc_req->bssDescription.bssId, + &session_id); + if (!session_entry) { + pe_err("Session does not exist for given bssId"); + lim_print_mac_addr(mac_ctx, reassoc_req->bssDescription.bssId, + LOGE); + ret_code = eSIR_SME_INVALID_PARAMETERS; + session_entry = + pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (session_entry) + lim_handle_sme_join_result(mac_ctx, + eSIR_SME_INVALID_PARAMETERS, + eSIR_MAC_UNSPEC_FAILURE_STATUS, + session_entry); + goto end; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_REASSOC_REQ_EVENT, + session_entry, QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + /* mac_ctx->lim.gpLimReassocReq = reassoc_req;//TO SUPPORT BT-AMP */ + + /* Store the reassoc handle in the session Table */ + session_entry->pLimReAssocReq = reassoc_req; + + session_entry->dot11mode = reassoc_req->dot11mode; + session_entry->vhtCapability = + IS_DOT11_MODE_VHT(reassoc_req->dot11mode); + + if (session_entry->vhtCapability) { + if (session_entry->opmode == QDF_STA_MODE) { + session_entry->vht_config.su_beam_formee = + reassoc_req->vht_config.su_beam_formee; + } else { + reassoc_req->vht_config.su_beam_formee = 0; + } + session_entry->enableVhtpAid = + reassoc_req->enableVhtpAid; + session_entry->enableVhtGid = + reassoc_req->enableVhtGid; + pe_debug("vht su bformer [%d]", session_entry->vht_config.su_beam_former); + } + + session_entry->supported_nss_1x1 = reassoc_req->supported_nss_1x1; + session_entry->vdev_nss = reassoc_req->vdev_nss; + session_entry->nss = reassoc_req->nss; + session_entry->nss_forced_1x1 = reassoc_req->nss_forced_1x1; + + pe_debug("vhtCapability: %d su_beam_formee: %d su_tx_bformer %d", + session_entry->vhtCapability, + session_entry->vht_config.su_beam_formee, + session_entry->vht_config.su_beam_former); + + session_entry->enableHtSmps = reassoc_req->enableHtSmps; + session_entry->htSmpsvalue = reassoc_req->htSmps; + session_entry->send_smps_action = + reassoc_req->send_smps_action; + pe_debug("enableHtSmps: %d htSmps: %d send action: %d supported nss 1x1: %d", + session_entry->enableHtSmps, + session_entry->htSmpsvalue, + session_entry->send_smps_action, + session_entry->supported_nss_1x1); + /* + * Reassociate request is expected + * in link established state only. + */ + + if (session_entry->limSmeState != eLIM_SME_LINK_EST_STATE) { + if (session_entry->limSmeState == eLIM_SME_WT_REASSOC_STATE) { + /* + * May be from 11r FT pre-auth. So lets check it + * before we bail out + */ + pe_debug("Session in reassoc state is %d", + session_entry->peSessionId); + + /* Make sure its our preauth bssid */ + if (qdf_mem_cmp(reassoc_req->bssDescription.bssId, + session_entry->limReAssocbssId, + 6)) { + lim_print_mac_addr(mac_ctx, + reassoc_req->bssDescription. + bssId, LOGE); + pe_err("Unknown bssId in reassoc state"); + ret_code = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + session_entry->vdev_id = vdev_id; + mlm_reassoc_req = + qdf_mem_malloc(sizeof(*mlm_reassoc_req)); + if (!mlm_reassoc_req) { + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto end; + } + + /* Update PE sessionId */ + mlm_reassoc_req->sessionId = session_entry->peSessionId; + status = lim_send_ft_reassoc_req(session_entry, + mlm_reassoc_req); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(mlm_reassoc_req); + ret_code = eSIR_SME_REFUSED; + goto end; + } + return; + } + /* + * Should not have received eWNI_SME_REASSOC_REQ + */ + pe_err("received unexpected SME_REASSOC_REQ in state %X", + session_entry->limSmeState); + lim_print_sme_state(mac_ctx, LOGE, session_entry->limSmeState); + + ret_code = eSIR_SME_UNEXPECTED_REQ_RESULT_CODE; + goto end; + } + + qdf_mem_copy(session_entry->limReAssocbssId, + session_entry->pLimReAssocReq->bssDescription.bssId, + sizeof(tSirMacAddr)); + + session_entry->lim_reassoc_chan_freq = + session_entry->pLimReAssocReq->bssDescription.chan_freq; + session_entry->reAssocHtSupportedChannelWidthSet = + (session_entry->pLimReAssocReq->cbMode) ? 1 : 0; + session_entry->reAssocHtRecommendedTxWidthSet = + session_entry->reAssocHtSupportedChannelWidthSet; + session_entry->reAssocHtSecondaryChannelOffset = + session_entry->pLimReAssocReq->cbMode; + + session_entry->limReassocBssCaps = + session_entry->pLimReAssocReq->bssDescription.capabilityInfo; + reg_max = wlan_reg_get_channel_reg_power_for_freq( + mac_ctx->pdev, session_entry->curr_op_freq); + local_pwr_constraint = reg_max; + + lim_extract_ap_capability(mac_ctx, + (uint8_t *)session_entry->pLimReAssocReq->bssDescription.ieFields, + lim_get_ielen_from_bss_description( + &session_entry->pLimReAssocReq->bssDescription), + &session_entry->limReassocBssQosCaps, + &session_entry->gLimCurrentBssUapsd, + &local_pwr_constraint, session_entry); + session_entry->maxTxPower = QDF_MIN(reg_max, (local_pwr_constraint)); + pe_err("Reg max = %d, local pwr constraint = %d, max tx = %d", + reg_max, local_pwr_constraint, session_entry->maxTxPower); + /* Copy the SSID from session entry to local variable */ + session_entry->limReassocSSID.length = reassoc_req->ssId.length; + qdf_mem_copy(session_entry->limReassocSSID.ssId, + reassoc_req->ssId.ssId, + session_entry->limReassocSSID.length); + if (session_entry->gLimCurrentBssUapsd) { + session_entry->gUapsdPerAcBitmask = + session_entry->pLimReAssocReq->uapsdPerAcBitmask; + pe_debug("UAPSD flag for all AC - 0x%2x", + session_entry->gUapsdPerAcBitmask); + } + + mlm_reassoc_req = qdf_mem_malloc(sizeof(tLimMlmReassocReq)); + if (!mlm_reassoc_req) { + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + goto end; + } + + qdf_mem_copy(mlm_reassoc_req->peerMacAddr, + session_entry->limReAssocbssId, sizeof(tSirMacAddr)); + + if (lim_get_capability_info(mac_ctx, &caps, session_entry) != + QDF_STATUS_SUCCESS) + pe_err("could not retrieve Capabilities value"); + + lim_update_caps_info_for_bss(mac_ctx, &caps, + reassoc_req->bssDescription.capabilityInfo); + pe_debug("Capabilities info Reassoc: 0x%X", caps); + + mlm_reassoc_req->capabilityInfo = caps; + + /* Update PE session_id */ + mlm_reassoc_req->sessionId = session_id; + + /* + * If telescopic beaconing is enabled, set listen interval to + * CFG_TELE_BCN_MAX_LI + */ + + tele_bcn_en = mac_ctx->mlme_cfg->sap_cfg.tele_bcn_wakeup_en; + + if (tele_bcn_en) + val = mac_ctx->mlme_cfg->sap_cfg.tele_bcn_max_li; + else + val = mac_ctx->mlme_cfg->sap_cfg.listen_interval; + + mlm_reassoc_req->listenInterval = (uint16_t) val; + + /* Indicate whether spectrum management is enabled */ + session_entry->spectrumMgtEnabled = reassoc_req->spectrumMgtIndicator; + + /* Enable the spectrum management if this is a DFS channel */ + if (session_entry->country_info_present && + lim_isconnected_on_dfs_channel( + mac_ctx, wlan_reg_freq_to_chan( + mac_ctx->pdev, session_entry->curr_op_freq))) + session_entry->spectrumMgtEnabled = true; + + session_entry->limPrevSmeState = session_entry->limSmeState; + session_entry->limSmeState = eLIM_SME_WT_REASSOC_STATE; + + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + + status = lim_send_reassoc_req(session_entry, mlm_reassoc_req); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(mlm_reassoc_req); + ret_code = eSIR_SME_REFUSED; + goto end; + } + + return; +end: + if (reassoc_req) { + qdf_mem_free(reassoc_req); + if (session_entry) + session_entry->pLimReAssocReq = NULL; + } + + if (session_entry) + /* + * error occurred after we determined the session so + * extract session id from there, otherwise we'll use + * the value already extracted from the message + */ + vdev_id = session_entry->vdev_id; + + /* + * Send Reassoc failure response to host + * (note session_entry may be NULL, but that's OK) + */ + lim_send_sme_join_reassoc_rsp(mac_ctx, eWNI_SME_REASSOC_RSP, + ret_code, eSIR_MAC_UNSPEC_FAILURE_STATUS, + session_entry, vdev_id); +} + +bool send_disassoc_frame = 1; +/** + * __lim_process_sme_disassoc_req() + * + ***FUNCTION: + * This function is called to process SME_DISASSOC_REQ message + * from HDD or upper layer application. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param *msg_buf A pointer to the SME message buffer + * @return None + */ + +static void __lim_process_sme_disassoc_req(struct mac_context *mac, + uint32_t *msg_buf) +{ + uint16_t disassocTrigger, reasonCode; + tLimMlmDisassocReq *pMlmDisassocReq; + tSirResultCodes retCode = eSIR_SME_SUCCESS; + struct disassoc_req smeDisassocReq; + struct pe_session *pe_session = NULL; + uint8_t sessionId; + uint8_t smesessionId; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + qdf_mem_copy(&smeDisassocReq, msg_buf, sizeof(struct disassoc_req)); + smesessionId = smeDisassocReq.sessionId; + if (!lim_is_sme_disassoc_req_valid(mac, + &smeDisassocReq, + pe_session)) { + pe_err("received invalid SME_DISASSOC_REQ message"); + if (mac->lim.gLimRspReqd) { + mac->lim.gLimRspReqd = false; + + retCode = eSIR_SME_INVALID_PARAMETERS; + disassocTrigger = eLIM_HOST_DISASSOC; + goto sendDisassoc; + } + + return; + } + + pe_session = pe_find_session_by_bssid(mac, + smeDisassocReq.bssid.bytes, + &sessionId); + if (!pe_session) { + pe_err("session does not exist for given bssId " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(smeDisassocReq.bssid.bytes)); + retCode = eSIR_SME_INVALID_PARAMETERS; + disassocTrigger = eLIM_HOST_DISASSOC; + goto sendDisassoc; + } + pe_debug("vdev %d (session %d) Systemrole %d Reason: %u SmeState: %d limMlmState %d ho fail %d send OTA %d from: " + QDF_MAC_ADDR_FMT, pe_session->vdev_id, pe_session->peSessionId, + GET_LIM_SYSTEM_ROLE(pe_session), smeDisassocReq.reasonCode, + pe_session->limSmeState, pe_session->limMlmState, + smeDisassocReq.process_ho_fail, + smeDisassocReq.doNotSendOverTheAir, + QDF_MAC_ADDR_REF(smeDisassocReq.peer_macaddr.bytes)); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_DISASSOC_REQ_EVENT, pe_session, + 0, smeDisassocReq.reasonCode); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + pe_session->smeSessionId = smesessionId; + pe_session->process_ho_fail = smeDisassocReq.process_ho_fail; + + switch (GET_LIM_SYSTEM_ROLE(pe_session)) { + case eLIM_STA_ROLE: + switch (pe_session->limSmeState) { + case eLIM_SME_ASSOCIATED_STATE: + case eLIM_SME_LINK_EST_STATE: + pe_session->limPrevSmeState = + pe_session->limSmeState; + pe_session->limSmeState = eLIM_SME_WT_DISASSOC_STATE; + /* Delete all TDLS peers connected before leaving BSS */ + lim_delete_tdls_peers(mac, pe_session); + MTRACE(mac_trace(mac, TRACE_CODE_SME_STATE, + pe_session->peSessionId, + pe_session->limSmeState)); + break; + + case eLIM_SME_WT_DEAUTH_STATE: + /* PE shall still process the DISASSOC_REQ and proceed with + * link tear down even if it had already sent a DEAUTH_IND to + * to SME. mac->lim.gLimPrevSmeState shall remain the same as + * its been set when PE entered WT_DEAUTH_STATE. + */ + pe_session->limSmeState = eLIM_SME_WT_DISASSOC_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_SME_STATE, + pe_session->peSessionId, + pe_session->limSmeState)); + break; + + case eLIM_SME_WT_DISASSOC_STATE: + /* PE Received a Disassoc frame. Normally it gets DISASSOC_CNF but it + * received DISASSOC_REQ. Which means host is also trying to disconnect. + * PE can continue processing DISASSOC_REQ and send the response instead + * of failing the request. SME will anyway ignore DEAUTH_IND that was sent + * for disassoc frame. + * + * It will send a disassoc, which is ok. However, we can use the global flag + * sendDisassoc to not send disassoc frame. + */ + break; + + case eLIM_SME_JOIN_FAILURE_STATE: { + /* Already in Disconnected State, return success */ + if (mac->lim.gLimRspReqd) { + retCode = eSIR_SME_SUCCESS; + disassocTrigger = eLIM_HOST_DISASSOC; + goto sendDisassoc; + } + } + break; + default: + /** + * STA is not currently associated. + * Log error and send response to host + */ + pe_err("received unexpected SME_DISASSOC_REQ in state %X", + pe_session->limSmeState); + lim_print_sme_state(mac, LOGE, + pe_session->limSmeState); + + if (mac->lim.gLimRspReqd) { + if (pe_session->limSmeState != + eLIM_SME_WT_ASSOC_STATE) + mac->lim.gLimRspReqd = false; + + retCode = eSIR_SME_UNEXPECTED_REQ_RESULT_CODE; + disassocTrigger = eLIM_HOST_DISASSOC; + goto sendDisassoc; + } + + return; + } + + break; + + case eLIM_AP_ROLE: + /* Fall through */ + break; + + case eLIM_STA_IN_IBSS_ROLE: + default: + /* eLIM_UNKNOWN_ROLE */ + pe_err("received unexpected SME_DISASSOC_REQ for role %d", + GET_LIM_SYSTEM_ROLE(pe_session)); + + retCode = eSIR_SME_UNEXPECTED_REQ_RESULT_CODE; + disassocTrigger = eLIM_HOST_DISASSOC; + goto sendDisassoc; + } /* end switch (mac->lim.gLimSystemRole) */ + + disassocTrigger = eLIM_HOST_DISASSOC; + reasonCode = smeDisassocReq.reasonCode; + + if (smeDisassocReq.doNotSendOverTheAir) + send_disassoc_frame = 0; + + pMlmDisassocReq = qdf_mem_malloc(sizeof(tLimMlmDisassocReq)); + if (!pMlmDisassocReq) + return; + + qdf_copy_macaddr(&pMlmDisassocReq->peer_macaddr, + &smeDisassocReq.peer_macaddr); + + pMlmDisassocReq->reasonCode = reasonCode; + pMlmDisassocReq->disassocTrigger = disassocTrigger; + + /* Update PE session ID */ + pMlmDisassocReq->sessionId = sessionId; + + lim_post_mlm_message(mac, + LIM_MLM_DISASSOC_REQ, (uint32_t *) pMlmDisassocReq); + return; + +sendDisassoc: + if (pe_session) + lim_send_sme_disassoc_ntf(mac, + smeDisassocReq.peer_macaddr.bytes, + retCode, + disassocTrigger, + 1, smesessionId, + pe_session); + else + lim_send_sme_disassoc_ntf(mac, + smeDisassocReq.peer_macaddr.bytes, + retCode, disassocTrigger, 1, + smesessionId, NULL); + +} /*** end __lim_process_sme_disassoc_req() ***/ + +/** ----------------------------------------------------------------- + \brief __lim_process_sme_disassoc_cnf() - Process SME_DISASSOC_CNF + + This function is called to process SME_DISASSOC_CNF message + from HDD or upper layer application. + + \param mac - global mac structure + \param sta - station dph hash node + \return none + \sa + ----------------------------------------------------------------- */ +void __lim_process_sme_disassoc_cnf(struct mac_context *mac, uint32_t *msg_buf) +{ + struct disassoc_cnf smeDisassocCnf; + uint16_t aid; + tpDphHashNode sta; + struct pe_session *pe_session; + uint8_t sessionId; + uint32_t *msg = NULL; + QDF_STATUS status; + + qdf_mem_copy(&smeDisassocCnf, msg_buf, sizeof(smeDisassocCnf)); + + pe_session = pe_find_session_by_bssid(mac, + smeDisassocCnf.bssid.bytes, + &sessionId); + if (!pe_session) { + pe_err("session does not exist for given bssId"); + status = lim_prepare_disconnect_done_ind(mac, &msg, + smeDisassocCnf.vdev_id, + eSIR_SME_INVALID_SESSION, + NULL); + if (QDF_IS_STATUS_SUCCESS(status)) + lim_send_sme_disassoc_deauth_ntf(mac, + QDF_STATUS_SUCCESS, + (uint32_t *)msg); + return; + } + + if (!lim_is_sme_disassoc_cnf_valid(mac, &smeDisassocCnf, pe_session)) { + pe_err("received invalid SME_DISASSOC_CNF message"); + status = lim_prepare_disconnect_done_ind(mac, &msg, + pe_session->smeSessionId, + eSIR_SME_INVALID_PARAMETERS, + &smeDisassocCnf.bssid.bytes[0]); + if (QDF_IS_STATUS_SUCCESS(status)) + lim_send_sme_disassoc_deauth_ntf(mac, + QDF_STATUS_SUCCESS, + (uint32_t *)msg); + return; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + if (smeDisassocCnf.messageType == eWNI_SME_DISASSOC_CNF) + lim_diag_event_report(mac, WLAN_PE_DIAG_DISASSOC_CNF_EVENT, + pe_session, + (uint16_t)smeDisassocCnf.status_code, 0); + else if (smeDisassocCnf.messageType == eWNI_SME_DEAUTH_CNF) + lim_diag_event_report(mac, WLAN_PE_DIAG_DEAUTH_CNF_EVENT, + pe_session, + (uint16_t)smeDisassocCnf.status_code, 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + switch (GET_LIM_SYSTEM_ROLE(pe_session)) { + case eLIM_STA_ROLE: + if ((pe_session->limSmeState != eLIM_SME_IDLE_STATE) && + (pe_session->limSmeState != eLIM_SME_WT_DISASSOC_STATE) + && (pe_session->limSmeState != + eLIM_SME_WT_DEAUTH_STATE)) { + pe_err("received unexp SME_DISASSOC_CNF in state %X", + pe_session->limSmeState); + lim_print_sme_state(mac, LOGE, + pe_session->limSmeState); + status = lim_prepare_disconnect_done_ind(mac, &msg, + pe_session->smeSessionId, + eSIR_SME_INVALID_STATE, + &smeDisassocCnf.bssid.bytes[0]); + if (QDF_IS_STATUS_SUCCESS(status)) + lim_send_sme_disassoc_deauth_ntf(mac, + QDF_STATUS_SUCCESS, + (uint32_t *)msg); + return; + } + break; + + case eLIM_AP_ROLE: + /* Fall through */ + break; + + case eLIM_STA_IN_IBSS_ROLE: + default: /* eLIM_UNKNOWN_ROLE */ + pe_err("received unexpected SME_DISASSOC_CNF role %d", + GET_LIM_SYSTEM_ROLE(pe_session)); + status = lim_prepare_disconnect_done_ind(mac, &msg, + pe_session->smeSessionId, + eSIR_SME_INVALID_STATE, + &smeDisassocCnf.bssid.bytes[0]); + if (QDF_IS_STATUS_SUCCESS(status)) + lim_send_sme_disassoc_deauth_ntf(mac, + QDF_STATUS_SUCCESS, + (uint32_t *)msg); + return; + } + + if ((pe_session->limSmeState == eLIM_SME_WT_DISASSOC_STATE) || + (pe_session->limSmeState == eLIM_SME_WT_DEAUTH_STATE) || + LIM_IS_AP_ROLE(pe_session)) { + sta = dph_lookup_hash_entry(mac, + smeDisassocCnf.peer_macaddr.bytes, &aid, + &pe_session->dph.dphHashTable); + if (!sta) { + pe_err("DISASSOC_CNF for a STA with no context, addr= " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(smeDisassocCnf.peer_macaddr.bytes)); + status = lim_prepare_disconnect_done_ind(mac, &msg, + pe_session->smeSessionId, + eSIR_SME_INVALID_PARAMETERS, + &smeDisassocCnf.bssid.bytes[0]); + if (QDF_IS_STATUS_SUCCESS(status)) + lim_send_sme_disassoc_deauth_ntf(mac, + QDF_STATUS_SUCCESS, + (uint32_t *)msg); + return; + } + + if ((sta->mlmStaContext.mlmState == + eLIM_MLM_WT_DEL_STA_RSP_STATE) || + (sta->mlmStaContext.mlmState == + eLIM_MLM_WT_DEL_BSS_RSP_STATE)) { + pe_err("No need of cleanup for addr:" QDF_MAC_ADDR_FMT "as MLM state is %d", + QDF_MAC_ADDR_REF(smeDisassocCnf.peer_macaddr.bytes), + sta->mlmStaContext.mlmState); + status = lim_prepare_disconnect_done_ind(mac, &msg, + pe_session->smeSessionId, + eSIR_SME_SUCCESS, + NULL); + if (QDF_IS_STATUS_SUCCESS(status)) + lim_send_sme_disassoc_deauth_ntf(mac, + QDF_STATUS_SUCCESS, + (uint32_t *)msg); + return; + } + + /* Delete FT session if there exists one */ + lim_ft_cleanup_pre_auth_info(mac, pe_session); + lim_cleanup_rx_path(mac, sta, pe_session); + + lim_clean_up_disassoc_deauth_req(mac, + (char *)&smeDisassocCnf.peer_macaddr, 0); + } + + return; +} + +/** + * __lim_process_sme_deauth_req() - process sme deauth req + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: pointer to the SME message buffer + * + * This function is called to process SME_DEAUTH_REQ message + * from HDD or upper layer application. + * + * Return: None + */ + +static void __lim_process_sme_deauth_req(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + uint16_t deauth_trigger, reason_code; + tLimMlmDeauthReq *mlm_deauth_req; + struct deauth_req sme_deauth_req; + tSirResultCodes ret_code = eSIR_SME_SUCCESS; + struct pe_session *session_entry; + uint8_t session_id; /* PE sessionId */ + uint8_t vdev_id; + + qdf_mem_copy(&sme_deauth_req, msg_buf, sizeof(sme_deauth_req)); + vdev_id = sme_deauth_req.vdev_id; + + /* + * We need to get a session first but we don't even know + * if the message is correct. + */ + session_entry = pe_find_session_by_bssid(mac_ctx, + sme_deauth_req.bssid.bytes, + &session_id); + if (!session_entry) { + pe_err("session does not exist for given bssId"); + ret_code = eSIR_SME_INVALID_PARAMETERS; + deauth_trigger = eLIM_HOST_DEAUTH; + goto send_deauth; + } + + if (!lim_is_sme_deauth_req_valid(mac_ctx, &sme_deauth_req, + session_entry)) { + pe_err("received invalid SME_DEAUTH_REQ message"); + mac_ctx->lim.gLimRspReqd = false; + + ret_code = eSIR_SME_INVALID_PARAMETERS; + deauth_trigger = eLIM_HOST_DEAUTH; + goto send_deauth; + } + pe_debug("vdev %d (session %d) Systemrole %d reasoncode %u limSmestate %d limMlmState %d from " + QDF_MAC_ADDR_FMT, vdev_id, session_entry->peSessionId, + GET_LIM_SYSTEM_ROLE(session_entry), sme_deauth_req.reasonCode, + session_entry->limSmeState, session_entry->limMlmState, + QDF_MAC_ADDR_REF(sme_deauth_req.peer_macaddr.bytes)); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_DEAUTH_REQ_EVENT, + session_entry, 0, sme_deauth_req.reasonCode); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + session_entry->vdev_id = vdev_id; + + switch (GET_LIM_SYSTEM_ROLE(session_entry)) { + case eLIM_STA_ROLE: + switch (session_entry->limSmeState) { + case eLIM_SME_ASSOCIATED_STATE: + case eLIM_SME_LINK_EST_STATE: + /* Delete all TDLS peers connected before leaving BSS */ + lim_delete_tdls_peers(mac_ctx, session_entry); + /* fallthrough */ + case eLIM_SME_WT_ASSOC_STATE: + case eLIM_SME_JOIN_FAILURE_STATE: + case eLIM_SME_IDLE_STATE: + session_entry->limPrevSmeState = + session_entry->limSmeState; + session_entry->limSmeState = eLIM_SME_WT_DEAUTH_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, + session_entry->peSessionId, + session_entry->limSmeState)); + /* Send Deauthentication request to MLM below */ + break; + case eLIM_SME_WT_DEAUTH_STATE: + case eLIM_SME_WT_DISASSOC_STATE: + /* + * PE Received a Deauth/Disassoc frame. Normally it get + * DEAUTH_CNF/DISASSOC_CNF but it received DEAUTH_REQ. + * Which means host is also trying to disconnect. + * PE can continue processing DEAUTH_REQ and send + * the response instead of failing the request. + * SME will anyway ignore DEAUTH_IND/DISASSOC_IND that + * was sent for deauth/disassoc frame. + */ + session_entry->limSmeState = eLIM_SME_WT_DEAUTH_STATE; + break; + default: + /* + * STA is not in a state to deauthenticate with + * peer. Log error and send response to host. + */ + pe_err("received unexp SME_DEAUTH_REQ in state %X", + session_entry->limSmeState); + lim_print_sme_state(mac_ctx, LOGE, + session_entry->limSmeState); + + if (mac_ctx->lim.gLimRspReqd) { + mac_ctx->lim.gLimRspReqd = false; + + ret_code = eSIR_SME_STA_NOT_AUTHENTICATED; + deauth_trigger = eLIM_HOST_DEAUTH; + + /* + * here we received deauth request from AP so + * sme state is eLIM_SME_WT_DEAUTH_STATE.if we + * have ISSUED delSta then mlm state should be + * eLIM_MLM_WT_DEL_STA_RSP_STATE and ifwe got + * delBSS rsp then mlm state should be + * eLIM_MLM_IDLE_STATE so the below condition + * captures the state where delSta not done + * and firmware still in connected state. + */ + if (session_entry->limSmeState == + eLIM_SME_WT_DEAUTH_STATE && + session_entry->limMlmState != + eLIM_MLM_IDLE_STATE && + session_entry->limMlmState != + eLIM_MLM_WT_DEL_STA_RSP_STATE) + ret_code = eSIR_SME_DEAUTH_STATUS; + goto send_deauth; + } + return; + } + break; + + case eLIM_STA_IN_IBSS_ROLE: + pe_err("Deauth not allowed in IBSS"); + if (mac_ctx->lim.gLimRspReqd) { + mac_ctx->lim.gLimRspReqd = false; + ret_code = eSIR_SME_INVALID_PARAMETERS; + deauth_trigger = eLIM_HOST_DEAUTH; + goto send_deauth; + } + return; + case eLIM_AP_ROLE: + break; + default: + pe_err("received unexpected SME_DEAUTH_REQ for role %X", + GET_LIM_SYSTEM_ROLE(session_entry)); + if (mac_ctx->lim.gLimRspReqd) { + mac_ctx->lim.gLimRspReqd = false; + ret_code = eSIR_SME_INVALID_PARAMETERS; + deauth_trigger = eLIM_HOST_DEAUTH; + goto send_deauth; + } + return; + } /* end switch (mac_ctx->lim.gLimSystemRole) */ + + if (sme_deauth_req.reasonCode == eLIM_LINK_MONITORING_DEAUTH && + session_entry->limSystemRole == eLIM_STA_ROLE) { + /* Deauthentication is triggered by Link Monitoring */ + pe_debug("** Lost link with AP **"); + deauth_trigger = eLIM_LINK_MONITORING_DEAUTH; + reason_code = eSIR_MAC_UNSPEC_FAILURE_REASON; + } else { + deauth_trigger = eLIM_HOST_DEAUTH; + reason_code = sme_deauth_req.reasonCode; + } + + /* Trigger Deauthentication frame to peer MAC entity */ + mlm_deauth_req = qdf_mem_malloc(sizeof(tLimMlmDeauthReq)); + if (!mlm_deauth_req) { + if (mac_ctx->lim.gLimRspReqd) { + mac_ctx->lim.gLimRspReqd = false; + ret_code = eSIR_SME_RESOURCES_UNAVAILABLE; + deauth_trigger = eLIM_HOST_DEAUTH; + goto send_deauth; + } + return; + } + + qdf_copy_macaddr(&mlm_deauth_req->peer_macaddr, + &sme_deauth_req.peer_macaddr); + + mlm_deauth_req->reasonCode = reason_code; + mlm_deauth_req->deauthTrigger = deauth_trigger; + + /* Update PE session Id */ + mlm_deauth_req->sessionId = session_id; + lim_process_mlm_deauth_req(mac_ctx, (uint32_t *)mlm_deauth_req); + + return; + +send_deauth: + lim_send_sme_deauth_ntf(mac_ctx, sme_deauth_req.peer_macaddr.bytes, + ret_code, deauth_trigger, 1, vdev_id); +} + +/** + * __lim_counter_measures() + * + * FUNCTION: + * This function is called to "implement" MIC counter measure + * and is *temporary* only + * + * LOGIC: on AP, disassoc all STA associated thru TKIP, + * we don't do the proper STA disassoc sequence since the + * BSS will be stopped anyway + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @return None + */ + +static void __lim_counter_measures(struct mac_context *mac, struct pe_session *pe_session) +{ + tSirMacAddr mac_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + if (LIM_IS_AP_ROLE(pe_session)) + lim_send_disassoc_mgmt_frame(mac, eSIR_MAC_MIC_FAILURE_REASON, + mac_addr, pe_session, false); +}; + +void lim_send_stop_bss_failure_resp(struct mac_context *mac_ctx, + struct pe_session *session) +{ + session->limSmeState = session->limPrevSmeState; + + MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE, session->peSessionId, + session->limSmeState)); + + lim_send_sme_rsp(mac_ctx, eWNI_SME_STOP_BSS_RSP, + eSIR_SME_STOP_BSS_FAILURE, session->smeSessionId); +} + +void lim_delete_all_peers(struct pe_session *session) +{ + uint8_t i = 0; + struct mac_context *mac_ctx = session->mac_ctx; + tpDphHashNode sta_ds = NULL; + QDF_STATUS status; + tSirMacAddr bc_addr = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + /* IBSS and NDI doesn't send Disassoc frame */ + if (!LIM_IS_IBSS_ROLE(session) && + !LIM_IS_NDI_ROLE(session)) { + pe_debug("stop_bss_reason: %d", session->stop_bss_reason); + if (session->stop_bss_reason == eSIR_SME_MIC_COUNTER_MEASURES) + __lim_counter_measures(mac_ctx, session); + else + lim_send_disassoc_mgmt_frame(mac_ctx, + eSIR_MAC_DEAUTH_LEAVING_BSS_REASON, + bc_addr, session, false); + } + + for (i = 1; i < session->dph.dphHashTable.size; i++) { + sta_ds = dph_get_hash_entry(mac_ctx, i, + &session->dph.dphHashTable); + if (!sta_ds) + continue; + status = lim_del_sta(mac_ctx, sta_ds, true, session); + if (QDF_STATUS_SUCCESS == status) { + lim_delete_dph_hash_entry(mac_ctx, sta_ds->staAddr, + sta_ds->assocId, session); + lim_release_peer_idx(mac_ctx, sta_ds->assocId, session); + } else { + pe_err("lim_del_sta failed with Status: %d", status); + QDF_ASSERT(0); + } + } + lim_disconnect_complete(session, false); + if (mac_ctx->del_peers_ind_cb) + mac_ctx->del_peers_ind_cb(mac_ctx->psoc, session->vdev_id); +} + +QDF_STATUS lim_sta_send_del_bss(struct pe_session *session) +{ + struct mac_context *mac_ctx = session->mac_ctx; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + tpDphHashNode sta_ds = NULL; + + sta_ds = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session->dph.dphHashTable); + if (!sta_ds) { + pe_err("DPH Entry for STA is missing, failed to send delbss"); + goto end; + } + + status = lim_del_bss(mac_ctx, sta_ds, 0, session); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("delBss failed for bss %d", session->vdev_id); + +end: + return status; +} + +QDF_STATUS lim_send_vdev_stop(struct pe_session *session) +{ + struct mac_context *mac_ctx = session->mac_ctx; + QDF_STATUS status; + + status = lim_del_bss(mac_ctx, NULL, session->vdev_id, session); + + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("delBss failed for bss %d", session->vdev_id); + lim_send_stop_bss_failure_resp(mac_ctx, session); + } + + return status; +} + +/** + * lim_delete_peers_and_send_vdev_stop() -delete peers and send vdev stop + * @session: session pointer + * + * Return None + */ +static void lim_delete_peers_and_send_vdev_stop(struct pe_session *session) +{ + struct mac_context *mac_ctx = session->mac_ctx; + QDF_STATUS status; + + if (QDF_IS_STATUS_SUCCESS( + wlan_vdev_is_restart_progress(session->vdev))) + status = + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_RESTART_REQ_FAIL, + sizeof(*session), session); + else + status = wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_DOWN, + sizeof(*session), + session); + if (QDF_IS_STATUS_ERROR(status)) + lim_send_stop_bss_failure_resp(mac_ctx, session); +} + +static void +__lim_handle_sme_stop_bss_request(struct mac_context *mac, uint32_t *msg_buf) +{ + struct stop_bss_req stop_bss_req; + tLimSmeStates prevState; + struct pe_session *pe_session; + uint8_t smesessionId; + uint8_t sessionId; + + qdf_mem_copy(&stop_bss_req, msg_buf, sizeof(stop_bss_req)); + smesessionId = stop_bss_req.sessionId; + + if (!lim_is_sme_stop_bss_req_valid(msg_buf)) { + pe_warn("received invalid SME_STOP_BSS_REQ message"); + /* Send Stop BSS response to host */ + lim_send_sme_rsp(mac, eWNI_SME_STOP_BSS_RSP, + eSIR_SME_INVALID_PARAMETERS, smesessionId); + return; + } + + pe_session = pe_find_session_by_bssid(mac, + stop_bss_req.bssid.bytes, + &sessionId); + if (!pe_session) { + pe_err("session does not exist for given BSSID"); + lim_send_sme_rsp(mac, eWNI_SME_STOP_BSS_RSP, + eSIR_SME_INVALID_PARAMETERS, smesessionId); + return; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_STOP_BSS_REQ_EVENT, pe_session, + 0, 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + if (pe_session->limSmeState != eLIM_SME_NORMAL_STATE || /* Added For BT -AMP Support */ + LIM_IS_STA_ROLE(pe_session)) { + /** + * Should not have received STOP_BSS_REQ in states + * other than 'normal' state or on STA in Infrastructure + * mode. Log error and return response to host. + */ + pe_err("received unexpected SME_STOP_BSS_REQ in state %X, for role %d", + pe_session->limSmeState, + GET_LIM_SYSTEM_ROLE(pe_session)); + lim_print_sme_state(mac, LOGE, pe_session->limSmeState); + /* / Send Stop BSS response to host */ + lim_send_sme_rsp(mac, eWNI_SME_STOP_BSS_RSP, + eSIR_SME_UNEXPECTED_REQ_RESULT_CODE, + smesessionId); + return; + } + + if (LIM_IS_AP_ROLE(pe_session)) + lim_wpspbc_close(mac, pe_session); + + pe_debug("RECEIVED STOP_BSS_REQ with reason code=%d", + stop_bss_req.reasonCode); + + prevState = pe_session->limSmeState; + pe_session->limPrevSmeState = prevState; + + pe_session->limSmeState = eLIM_SME_IDLE_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_SME_STATE, pe_session->peSessionId, + pe_session->limSmeState)); + + pe_session->smeSessionId = smesessionId; + pe_session->stop_bss_reason = stop_bss_req.reasonCode; + + if (!LIM_IS_NDI_ROLE(pe_session)) { + /* Free the buffer allocated in START_BSS_REQ */ + qdf_mem_free(pe_session->add_ie_params.probeRespData_buff); + pe_session->add_ie_params.probeRespDataLen = 0; + pe_session->add_ie_params.probeRespData_buff = NULL; + + qdf_mem_free(pe_session->add_ie_params.assocRespData_buff); + pe_session->add_ie_params.assocRespDataLen = 0; + pe_session->add_ie_params.assocRespData_buff = NULL; + + qdf_mem_free(pe_session->add_ie_params.probeRespBCNData_buff); + pe_session->add_ie_params.probeRespBCNDataLen = 0; + pe_session->add_ie_params.probeRespBCNData_buff = NULL; + + /* + * lim_del_bss is also called as part of coalescing, + * when we send DEL BSS followed by Add Bss msg. + */ + mac->lim.gLimIbssCoalescingHappened = false; + } + + lim_delete_peers_and_send_vdev_stop(pe_session); + +} + +/** + * __lim_process_sme_stop_bss_req() - Process STOP_BSS from SME + * @mac: Global MAC context + * @pMsg: Message from SME + * + * Wrapper for the function __lim_handle_sme_stop_bss_request + * This message will be defered until softmac come out of + * scan mode. Message should be handled even if we have + * detected radar in the current operating channel. + * + * Return: true - If we consumed the buffer + * false - If have defered the message. + */ + +static bool __lim_process_sme_stop_bss_req(struct mac_context *mac, + struct scheduler_msg *pMsg) +{ + __lim_handle_sme_stop_bss_request(mac, (uint32_t *) pMsg->bodyptr); + return true; +} /*** end __lim_process_sme_stop_bss_req() ***/ + +void lim_process_sme_del_bss_rsp(struct mac_context *mac, + struct pe_session *pe_session) +{ + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + lim_ibss_delete(mac, pe_session); + dph_hash_table_init(mac, &pe_session->dph.dphHashTable); + lim_delete_pre_auth_list(mac); + lim_send_sme_rsp(mac, eWNI_SME_STOP_BSS_RSP, eSIR_SME_SUCCESS, + pe_session->smeSessionId); + return; +} + +/** + * __lim_process_sme_assoc_cnf_new() - process sme assoc/reassoc cnf + * + * @mac_ctx: pointer to mac context + * @msg_type: message type + * @msg_buf: pointer to the SME message buffer + * + * This function handles SME_ASSOC_CNF/SME_REASSOC_CNF + * in BTAMP AP. + * + * Return: None + */ + +void __lim_process_sme_assoc_cnf_new(struct mac_context *mac_ctx, uint32_t msg_type, + uint32_t *msg_buf) +{ + struct assoc_cnf assoc_cnf; + tpDphHashNode sta_ds = NULL; + struct pe_session *session_entry = NULL; + uint8_t session_id; + tpSirAssocReq assoc_req; + + if (!msg_buf) { + pe_err("msg_buf is NULL"); + return; + } + + qdf_mem_copy(&assoc_cnf, msg_buf, sizeof(assoc_cnf)); + if (!__lim_is_sme_assoc_cnf_valid(&assoc_cnf)) { + pe_err("Received invalid SME_RE(ASSOC)_CNF message"); + goto end; + } + + session_entry = pe_find_session_by_bssid(mac_ctx, assoc_cnf.bssid.bytes, + &session_id); + if (!session_entry) { + pe_err("session does not exist for given bssId"); + goto end; + } + + if ((!LIM_IS_AP_ROLE(session_entry)) || + (session_entry->limSmeState != eLIM_SME_NORMAL_STATE)) { + pe_err("Rcvd unexpected msg %X in state %X, in role %X", + msg_type, session_entry->limSmeState, + GET_LIM_SYSTEM_ROLE(session_entry)); + goto end; + } + sta_ds = dph_get_hash_entry(mac_ctx, assoc_cnf.aid, + &session_entry->dph.dphHashTable); + if (!sta_ds) { + pe_err("Rcvd invalid msg %X due to no STA ctx, aid %d, peer", + msg_type, assoc_cnf.aid); + lim_print_mac_addr(mac_ctx, assoc_cnf.peer_macaddr.bytes, LOGE); + + /* + * send a DISASSOC_IND message to WSM to make sure + * the state in WSM and LIM is the same + */ + lim_send_sme_disassoc_ntf(mac_ctx, assoc_cnf.peer_macaddr.bytes, + eSIR_SME_STA_NOT_ASSOCIATED, + eLIM_PEER_ENTITY_DISASSOC, assoc_cnf.aid, + session_entry->smeSessionId, + session_entry); + goto end; + } + if (qdf_mem_cmp((uint8_t *)sta_ds->staAddr, + (uint8_t *) assoc_cnf.peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE)) { + pe_debug("peerMacAddr mismatched for aid %d, peer ", + assoc_cnf.aid); + lim_print_mac_addr(mac_ctx, assoc_cnf.peer_macaddr.bytes, LOGD); + goto end; + } + + if ((sta_ds->mlmStaContext.mlmState != eLIM_MLM_WT_ASSOC_CNF_STATE) || + ((sta_ds->mlmStaContext.subType == LIM_ASSOC) && + (msg_type != eWNI_SME_ASSOC_CNF)) || + ((sta_ds->mlmStaContext.subType == LIM_REASSOC) && + (msg_type != eWNI_SME_ASSOC_CNF))) { + pe_debug("not in MLM_WT_ASSOC_CNF_STATE, for aid %d, peer" + "StaD mlmState: %d", + assoc_cnf.aid, sta_ds->mlmStaContext.mlmState); + lim_print_mac_addr(mac_ctx, assoc_cnf.peer_macaddr.bytes, LOGD); + goto end; + } + /* + * Deactivate/delet CNF_WAIT timer since ASSOC_CNF + * has been received + */ + pe_debug("Received SME_ASSOC_CNF. Delete Timer"); + lim_deactivate_and_change_per_sta_id_timer(mac_ctx, + eLIM_CNF_WAIT_TIMER, sta_ds->assocId); + + if (assoc_cnf.status_code == eSIR_SME_SUCCESS) { + /* + * In BTAMP-AP, PE already finished the WMA_ADD_STA sequence + * when it had received Assoc Request frame. Now, PE just needs + * to send association rsp frame to the requesting BTAMP-STA. + */ + sta_ds->mlmStaContext.mlmState = + eLIM_MLM_LINK_ESTABLISHED_STATE; + sta_ds->mlmStaContext.owe_ie = assoc_cnf.owe_ie; + sta_ds->mlmStaContext.owe_ie_len = assoc_cnf.owe_ie_len; + pe_debug("sending Assoc Rsp frame to STA assoc id=%d, tx cb %d", + sta_ds->assocId, assoc_cnf.need_assoc_rsp_tx_cb); + lim_send_assoc_rsp_mgmt_frame( + mac_ctx, QDF_STATUS_SUCCESS, + sta_ds->assocId, sta_ds->staAddr, + sta_ds->mlmStaContext.subType, sta_ds, + session_entry, + assoc_cnf.need_assoc_rsp_tx_cb); + sta_ds->mlmStaContext.owe_ie = NULL; + sta_ds->mlmStaContext.owe_ie_len = 0; + goto end; + } else { + uint8_t add_pre_auth_context = true; + /* + * SME_ASSOC_CNF status is non-success, so STA is not allowed + * to be associated since the HAL sta entry is created for + * denied STA we need to remove this HAL entry. + * So to do that set updateContext to 1 + */ + enum mac_status_code mac_status_code = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + + if (!sta_ds->mlmStaContext.updateContext) + sta_ds->mlmStaContext.updateContext = 1; + pe_debug("Recv Assoc Cnf, status Code : %d(assoc id=%d) Reason code: %d", + assoc_cnf.status_code, sta_ds->assocId, + assoc_cnf.mac_status_code); + if (assoc_cnf.mac_status_code) + mac_status_code = assoc_cnf.mac_status_code; + if (assoc_cnf.mac_status_code == eSIR_MAC_INVALID_PMKID || + assoc_cnf.mac_status_code == + eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS) + add_pre_auth_context = false; + + lim_reject_association(mac_ctx, sta_ds->staAddr, + sta_ds->mlmStaContext.subType, + add_pre_auth_context, + sta_ds->mlmStaContext.authType, + sta_ds->assocId, true, + mac_status_code, + session_entry); + } +end: + if (((session_entry) && (sta_ds)) && + (session_entry->parsedAssocReq[sta_ds->assocId]) && + !assoc_cnf.need_assoc_rsp_tx_cb) { + assoc_req = (tpSirAssocReq) + session_entry->parsedAssocReq[sta_ds->assocId]; + if (assoc_req->assocReqFrame) { + qdf_mem_free(assoc_req->assocReqFrame); + assoc_req->assocReqFrame = NULL; + } + qdf_mem_free(session_entry->parsedAssocReq[sta_ds->assocId]); + session_entry->parsedAssocReq[sta_ds->assocId] = NULL; + } + qdf_mem_free(assoc_cnf.owe_ie); +} + +static void +__lim_process_sme_addts_req(struct mac_context *mac, uint32_t *msg_buf) +{ + tpDphHashNode sta; + tSirMacAddr peerMac; + tpSirAddtsReq pSirAddts; + uint32_t timeout; + struct pe_session *pe_session; + uint8_t sessionId; /* PE sessionId */ + uint8_t smesessionId; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + pSirAddts = (tpSirAddtsReq) msg_buf; + smesessionId = pSirAddts->sessionId; + pe_session = pe_find_session_by_bssid(mac, pSirAddts->bssid.bytes, + &sessionId); + if (!pe_session) { + pe_err("Session Does not exist for given bssId"); + lim_send_sme_addts_rsp(mac, pSirAddts->rspReqd, + QDF_STATUS_E_FAILURE, + NULL, pSirAddts->req.tspec, + smesessionId); + return; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_ADDTS_REQ_EVENT, pe_session, 0, + 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + /* if sta + * - verify assoc state + * - send addts request to ap + * - wait for addts response from ap + * if ap, just ignore with error log + */ + pe_debug("Received SME_ADDTS_REQ (TSid %d, UP %d)", + pSirAddts->req.tspec.tsinfo.traffic.tsid, + pSirAddts->req.tspec.tsinfo.traffic.userPrio); + + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("AddTs received on AP - ignoring"); + goto send_failure_addts_rsp; + } + + sta = + dph_get_hash_entry(mac, DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + + if (!sta) { + pe_err("Cannot find AP context for addts req"); + goto send_failure_addts_rsp; + } + + if ((!sta->valid) || (sta->mlmStaContext.mlmState != + eLIM_MLM_LINK_ESTABLISHED_STATE)) { + pe_err("AddTs received in invalid MLM state"); + goto send_failure_addts_rsp; + } + + pSirAddts->req.wsmTspecPresent = 0; + pSirAddts->req.wmeTspecPresent = 0; + pSirAddts->req.lleTspecPresent = 0; + + if ((sta->wsmEnabled) && + (pSirAddts->req.tspec.tsinfo.traffic.accessPolicy != + SIR_MAC_ACCESSPOLICY_EDCA)) + pSirAddts->req.wsmTspecPresent = 1; + else if (sta->wmeEnabled) + pSirAddts->req.wmeTspecPresent = 1; + else if (sta->lleEnabled) + pSirAddts->req.lleTspecPresent = 1; + else { + pe_warn("ADDTS_REQ ignore - qos is disabled"); + goto send_failure_addts_rsp; + } + + if ((pe_session->limSmeState != eLIM_SME_ASSOCIATED_STATE) && + (pe_session->limSmeState != eLIM_SME_LINK_EST_STATE)) { + pe_err("AddTs received in invalid LIMsme state (%d)", + pe_session->limSmeState); + goto send_failure_addts_rsp; + } + + if (mac->lim.gLimAddtsSent) { + pe_err("Addts (token %d, tsid %d, up %d) is still pending", + mac->lim.gLimAddtsReq.req.dialogToken, + mac->lim.gLimAddtsReq.req.tspec.tsinfo.traffic.tsid, + mac->lim.gLimAddtsReq.req.tspec.tsinfo.traffic. + userPrio); + goto send_failure_addts_rsp; + } + + sir_copy_mac_addr(peerMac, pe_session->bssId); + + /* save the addts request */ + mac->lim.gLimAddtsSent = true; + qdf_mem_copy((uint8_t *) &mac->lim.gLimAddtsReq, + (uint8_t *) pSirAddts, sizeof(tSirAddtsReq)); + + /* ship out the message now */ + lim_send_addts_req_action_frame(mac, peerMac, &pSirAddts->req, + pe_session); + pe_err("Sent ADDTS request"); + /* start a timer to wait for the response */ + if (pSirAddts->timeout) + timeout = pSirAddts->timeout; + else + timeout = mac->mlme_cfg->timeouts.addts_rsp_timeout; + + timeout = SYS_MS_TO_TICKS(timeout); + if (tx_timer_change(&mac->lim.lim_timers.gLimAddtsRspTimer, timeout, 0) + != TX_SUCCESS) { + pe_err("AddtsRsp timer change failed!"); + goto send_failure_addts_rsp; + } + mac->lim.gLimAddtsRspTimerCount++; + if (tx_timer_change_context(&mac->lim.lim_timers.gLimAddtsRspTimer, + mac->lim.gLimAddtsRspTimerCount) != + TX_SUCCESS) { + pe_err("AddtsRsp timer change failed!"); + goto send_failure_addts_rsp; + } + MTRACE(mac_trace + (mac, TRACE_CODE_TIMER_ACTIVATE, pe_session->peSessionId, + eLIM_ADDTS_RSP_TIMER)); + + /* add the sessionId to the timer object */ + mac->lim.lim_timers.gLimAddtsRspTimer.sessionId = sessionId; + if (tx_timer_activate(&mac->lim.lim_timers.gLimAddtsRspTimer) != + TX_SUCCESS) { + pe_err("AddtsRsp timer activation failed!"); + goto send_failure_addts_rsp; + } + return; + +send_failure_addts_rsp: + lim_send_sme_addts_rsp(mac, pSirAddts->rspReqd, QDF_STATUS_E_FAILURE, + pe_session, pSirAddts->req.tspec, + smesessionId); +} + +static void +__lim_process_sme_delts_req(struct mac_context *mac, uint32_t *msg_buf) +{ + tSirMacAddr peerMacAddr; + uint8_t ac; + struct mac_ts_info *pTsinfo; + tpSirDeltsReq pDeltsReq = (tpSirDeltsReq) msg_buf; + tpDphHashNode sta = NULL; + struct pe_session *pe_session; + uint8_t sessionId; + uint32_t status = QDF_STATUS_SUCCESS; + uint8_t smesessionId; + + smesessionId = pDeltsReq->sessionId; + + pe_session = pe_find_session_by_bssid(mac, + pDeltsReq->bssid.bytes, + &sessionId); + if (!pe_session) { + pe_err("Session Does not exist for given bssId"); + status = QDF_STATUS_E_FAILURE; + goto end; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_DELTS_REQ_EVENT, pe_session, 0, + 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + if (QDF_STATUS_SUCCESS != + lim_validate_delts_req(mac, pDeltsReq, peerMacAddr, pe_session)) { + pe_err("lim_validate_delts_req failed"); + status = QDF_STATUS_E_FAILURE; + lim_send_sme_delts_rsp(mac, pDeltsReq, QDF_STATUS_E_FAILURE, + pe_session, smesessionId); + return; + } + + pe_debug("Sent DELTS request to station with assocId = %d MacAddr = " + QDF_MAC_ADDR_FMT, + pDeltsReq->aid, QDF_MAC_ADDR_REF(peerMacAddr)); + + lim_send_delts_req_action_frame(mac, peerMacAddr, + pDeltsReq->req.wmeTspecPresent, + &pDeltsReq->req.tsinfo, + &pDeltsReq->req.tspec, pe_session); + + pTsinfo = + pDeltsReq->req.wmeTspecPresent ? &pDeltsReq->req.tspec. + tsinfo : &pDeltsReq->req.tsinfo; + + /* We've successfully send DELTS frame to AP. Update the + * dynamic UAPSD mask. The AC for this TSPEC to be deleted + * is no longer trigger enabled or delivery enabled + */ + lim_set_tspec_uapsd_mask_per_session(mac, pe_session, + pTsinfo, CLEAR_UAPSD_MASK); + + /* We're deleting the TSPEC, so this particular AC is no longer + * admitted. PE needs to downgrade the EDCA + * parameters(for the AC for which TS is being deleted) to the + * next best AC for which ACM is not enabled, and send the + * updated values to HAL. + */ + ac = upToAc(pTsinfo->traffic.userPrio); + + if (pTsinfo->traffic.direction == SIR_MAC_DIRECTION_UPLINK) { + pe_session->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] &= + ~(1 << ac); + } else if (pTsinfo->traffic.direction == + SIR_MAC_DIRECTION_DNLINK) { + pe_session->gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK] &= + ~(1 << ac); + } else if (pTsinfo->traffic.direction == + SIR_MAC_DIRECTION_BIDIR) { + pe_session->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] &= + ~(1 << ac); + pe_session->gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK] &= + ~(1 << ac); + } + + lim_set_active_edca_params(mac, pe_session->gLimEdcaParams, + pe_session); + + sta = + dph_get_hash_entry(mac, DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + if (sta) { + lim_send_edca_params(mac, pe_session->gLimEdcaParamsActive, + pe_session->vdev_id, false); + status = QDF_STATUS_SUCCESS; + } else { + pe_err("Self entry missing in Hash Table"); + status = QDF_STATUS_E_FAILURE; + } +#ifdef FEATURE_WLAN_ESE + lim_send_sme_tsm_ie_ind(mac, pe_session, 0, 0, 0); +#endif + + /* send an sme response back */ +end: + lim_send_sme_delts_rsp(mac, pDeltsReq, QDF_STATUS_SUCCESS, pe_session, + smesessionId); +} + +void lim_process_sme_addts_rsp_timeout(struct mac_context *mac, uint32_t param) +{ + /* fetch the pe_session based on the sessionId */ + struct pe_session *pe_session; + + pe_session = pe_find_session_by_session_id(mac, + mac->lim.lim_timers.gLimAddtsRspTimer. + sessionId); + if (!pe_session) { + pe_err("Session Does not exist for given sessionID"); + return; + } + + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_warn("AddtsRspTimeout in non-Sta role (%d)", + GET_LIM_SYSTEM_ROLE(pe_session)); + mac->lim.gLimAddtsSent = false; + return; + } + + if (!mac->lim.gLimAddtsSent) { + pe_warn("AddtsRspTimeout but no AddtsSent"); + return; + } + + if (param != mac->lim.gLimAddtsRspTimerCount) { + pe_err("Invalid AddtsRsp Timer count %d (exp %d)", param, + mac->lim.gLimAddtsRspTimerCount); + return; + } + /* this a real response timeout */ + mac->lim.gLimAddtsSent = false; + mac->lim.gLimAddtsRspTimerCount++; + + lim_send_sme_addts_rsp(mac, true, eSIR_SME_ADDTS_RSP_TIMEOUT, + pe_session, mac->lim.gLimAddtsReq.req.tspec, + pe_session->smeSessionId); +} + +#ifdef FEATURE_WLAN_ESE +/** + * __lim_process_sme_get_tsm_stats_request() - get tsm stats request + * + * @mac: Pointer to Global MAC structure + * @msg_buf: A pointer to the SME message buffer + * + * Return: None + */ +static void __lim_process_sme_get_tsm_stats_request(struct mac_context *mac, + uint32_t *msg_buf) +{ + struct scheduler_msg msgQ = {0}; + + msgQ.type = WMA_TSM_STATS_REQ; + msgQ.reserved = 0; + msgQ.bodyptr = msg_buf; + msgQ.bodyval = 0; + MTRACE(mac_trace_msg_tx(mac, NO_SESSION, msgQ.type)); + + if (QDF_STATUS_SUCCESS != (wma_post_ctrl_msg(mac, &msgQ))) { + qdf_mem_free(msg_buf); + msg_buf = NULL; + pe_err("Unable to forward request"); + return; + } +} +#endif /* FEATURE_WLAN_ESE */ + +static void lim_process_sme_set_addba_accept(struct mac_context *mac_ctx, + struct sme_addba_accept *msg) +{ + if (!msg) { + pe_err("Msg Buffer is NULL"); + return; + } + if (!msg->addba_accept) + mac_ctx->reject_addba_req = 1; + else + mac_ctx->reject_addba_req = 0; +} + +static void lim_process_sme_update_edca_params(struct mac_context *mac_ctx, + uint32_t vdev_id) +{ + struct pe_session *pe_session; + tpDphHashNode sta_ds_ptr; + + pe_session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!pe_session) { + pe_err("Session does not exist: vdev_id %d", vdev_id); + return; + } + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_BE].no_ack = + mac_ctx->no_ack_policy_cfg[QCA_WLAN_AC_BE]; + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_BK].no_ack = + mac_ctx->no_ack_policy_cfg[QCA_WLAN_AC_BK]; + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_VI].no_ack = + mac_ctx->no_ack_policy_cfg[QCA_WLAN_AC_VI]; + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_VO].no_ack = + mac_ctx->no_ack_policy_cfg[QCA_WLAN_AC_VO]; + sta_ds_ptr = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + if (sta_ds_ptr) + lim_send_edca_params(mac_ctx, + pe_session->gLimEdcaParamsActive, + pe_session->vdev_id, false); + else + pe_err("Self entry missing in Hash Table"); +} + +/** + * lim_process_sme_update_session_edca_txq_params() + * Update the edca tx queue parameters for the vdev + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: Pointer to SME message buffer + * + * Return: None + */ +static void +lim_process_sme_update_session_edca_txq_params(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct sir_update_session_txq_edca_param *msg; + struct pe_session *pe_session; + uint8_t ac; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + msg = (struct sir_update_session_txq_edca_param *)msg_buf; + + pe_session = pe_find_session_by_vdev_id(mac_ctx, msg->vdev_id); + if (!pe_session) { + pe_warn("Session does not exist for given vdev_id %d", + msg->vdev_id); + return; + } + + ac = msg->txq_edca_params.aci.aci; + pe_debug("received SME Session tx queue update for vdev %d queue %d", + msg->vdev_id, ac); + + if ((!LIM_IS_AP_ROLE(pe_session)) || + (pe_session->limSmeState != eLIM_SME_NORMAL_STATE)) { + pe_err("Rcvd edca update req in state %X, in role %X", + pe_session->limSmeState, + GET_LIM_SYSTEM_ROLE(pe_session)); + return; + } + + pe_session->gLimEdcaParams[ac].cw.min = + msg->txq_edca_params.cw.min; + pe_session->gLimEdcaParams[ac].cw.max = + msg->txq_edca_params.cw.max; + pe_session->gLimEdcaParams[ac].aci.aci = + msg->txq_edca_params.aci.aci; + pe_session->gLimEdcaParams[ac].aci.aifsn = + msg->txq_edca_params.aci.aifsn; + pe_session->gLimEdcaParams[ac].txoplimit = + msg->txq_edca_params.txoplimit; + + lim_send_edca_params(mac_ctx, + pe_session->gLimEdcaParams, + pe_session->vdev_id, false); +} + +static void lim_process_sme_update_mu_edca_params(struct mac_context *mac_ctx, + uint32_t vdev_id) +{ + struct pe_session *pe_session; + tpDphHashNode sta_ds_ptr; + + pe_session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!pe_session) { + pe_err("Session does not exist: vdev_id %d", vdev_id); + return; + } + sta_ds_ptr = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + if (sta_ds_ptr) + lim_send_edca_params(mac_ctx, mac_ctx->usr_mu_edca_params, + pe_session->vdev_id, true); + else + pe_err("Self entry missing in Hash Table"); +} + +static void +lim_process_sme_cfg_action_frm_in_tb_ppdu(struct mac_context *mac_ctx, + struct sir_cfg_action_frm_tb_ppdu + *msg) +{ + if (!msg) { + pe_err("Buffer is NULL"); + return; + } + + lim_send_action_frm_tb_ppdu_cfg(mac_ctx, msg->vdev_id, msg->cfg); +} + +static void lim_process_sme_update_config(struct mac_context *mac_ctx, + struct update_config *msg) +{ + struct pe_session *pe_session; + + pe_debug("received eWNI_SME_UPDATE_HT_CONFIG message"); + if (!msg) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + pe_session = pe_find_session_by_vdev_id(mac_ctx, msg->vdev_id); + if (!pe_session) { + pe_warn("Session does not exist for given BSSID"); + return; + } + + switch (msg->capab) { + case WNI_CFG_HT_CAP_INFO_ADVANCE_CODING: + pe_session->ht_config.ht_rx_ldpc = msg->value; + break; + case WNI_CFG_HT_CAP_INFO_TX_STBC: + pe_session->ht_config.ht_tx_stbc = msg->value; + break; + case WNI_CFG_HT_CAP_INFO_RX_STBC: + pe_session->ht_config.ht_rx_stbc = msg->value; + break; + case WNI_CFG_HT_CAP_INFO_SHORT_GI_20MHZ: + pe_session->ht_config.ht_sgi20 = msg->value; + break; + case WNI_CFG_HT_CAP_INFO_SHORT_GI_40MHZ: + pe_session->ht_config.ht_sgi40 = msg->value; + break; + } + + if (LIM_IS_AP_ROLE(pe_session)) { + sch_set_fixed_beacon_fields(mac_ctx, pe_session); + lim_send_beacon_ind(mac_ctx, pe_session, REASON_CONFIG_UPDATE); + } +} + +void +lim_send_vdev_restart(struct mac_context *mac, + struct pe_session *pe_session, uint8_t sessionId) +{ + struct vdev_mlme_obj *mlme_obj; + + if (!pe_session) { + pe_err("Invalid parameters"); + return; + } + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(pe_session->vdev); + if (!mlme_obj) { + pe_err("vdev component object is NULL"); + return; + } + + mlme_obj->mgmt.ap.hidden_ssid = pe_session->ssidHidden ? true : false; + + vdev_mgr_start_send(mlme_obj, true); +} + +/** + * __lim_process_roam_scan_offload_req() - Process Roam scan offload from csr + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: Pointer to SME message buffer + * + * Return: None + */ +static void __lim_process_roam_scan_offload_req(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct pe_session *pe_session; + struct scheduler_msg wma_msg = {0}; + QDF_STATUS status; + struct roam_offload_scan_req *req_buffer; + uint16_t local_ie_len; + uint8_t *local_ie_buf; + + req_buffer = (struct roam_offload_scan_req *)msg_buf; + pe_session = pe_find_session_by_vdev_id(mac_ctx, + req_buffer->sessionId); + + local_ie_buf = qdf_mem_malloc(MAX_DEFAULT_SCAN_IE_LEN); + if (!local_ie_buf) { + qdf_mem_zero(req_buffer, sizeof(*req_buffer)); + qdf_mem_free(req_buffer); + return; + } + + local_ie_len = req_buffer->assoc_ie.length; + /* Update ext cap IE if present */ + if (local_ie_len && + !lim_update_ext_cap_ie(mac_ctx, req_buffer->assoc_ie.addIEdata, + local_ie_buf, &local_ie_len, pe_session)) { + if (local_ie_len < + QDF_ARRAY_SIZE(req_buffer->assoc_ie.addIEdata)) { + req_buffer->assoc_ie.length = local_ie_len; + qdf_mem_copy(req_buffer->assoc_ie.addIEdata, + local_ie_buf, local_ie_len); + } + } + qdf_mem_free(local_ie_buf); + + if (pe_session) + lim_update_fils_rik(pe_session, req_buffer); + + wma_msg.type = WMA_ROAM_SCAN_OFFLOAD_REQ; + wma_msg.bodyptr = req_buffer; + + status = wma_post_ctrl_msg(mac_ctx, &wma_msg); + if (QDF_STATUS_SUCCESS != status) { + pe_err("Posting WMA_ROAM_SCAN_OFFLOAD_REQ failed"); + qdf_mem_zero(req_buffer, sizeof(*req_buffer)); + qdf_mem_free(req_buffer); + } +} + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * lim_send_roam_offload_init() - Process Roam offload flag from csr + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: Pointer to SME message buffer + * + * Return: None + */ +static void lim_send_roam_offload_init(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct scheduler_msg wma_msg = {0}; + QDF_STATUS status; + + wma_msg.type = WMA_ROAM_INIT_PARAM; + wma_msg.bodyptr = msg_buf; + + status = wma_post_ctrl_msg(mac_ctx, &wma_msg); + if (QDF_STATUS_SUCCESS != status) { + pe_err("Posting WMA_ROAM_INIT_PARAM failed"); + qdf_mem_free(msg_buf); + } +} + +/** + * lim_send_roam_per_command() - Process roam send PER command from csr + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: Pointer to SME message buffer + * + * Return: None + */ +static void lim_send_roam_per_command(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct scheduler_msg wma_msg = {0}; + QDF_STATUS status; + + wma_msg.type = WMA_SET_PER_ROAM_CONFIG_CMD; + wma_msg.bodyptr = msg_buf; + + status = wma_post_ctrl_msg(mac_ctx, &wma_msg); + if (QDF_STATUS_SUCCESS != status) { + pe_err("Posting WMA_ROAM_INIT_PARAM failed"); + qdf_mem_free(msg_buf); + } +} +#else +static void lim_send_roam_offload_init(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + qdf_mem_free(msg_buf); +} + +static void lim_send_roam_per_command(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + qdf_mem_free(msg_buf); +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * lim_process_roam_invoke() - process the Roam Invoke req + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: Pointer to the SME message buffer + * + * This function is called by limProcessMessageQueue(). This function sends the + * ROAM_INVOKE command to WMA. + * + * Return: None + */ +static void lim_process_roam_invoke(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + msg.type = SIR_HAL_ROAM_INVOKE; + msg.bodyptr = msg_buf; + msg.reserved = 0; + + status = wma_post_ctrl_msg(mac_ctx, &msg); + if (QDF_STATUS_SUCCESS != status) + pe_err("Not able to post SIR_HAL_ROAM_INVOKE to WMA"); +} +#else +static void lim_process_roam_invoke(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + qdf_mem_free(msg_buf); +} +#endif + +static void lim_handle_update_ssid_hidden(struct mac_context *mac_ctx, + struct pe_session *session, uint8_t ssid_hidden) +{ + pe_debug("rcvd HIDE_SSID message old HIDE_SSID: %d new HIDE_SSID: %d", + session->ssidHidden, ssid_hidden); + + if (ssid_hidden != session->ssidHidden) { + session->ssidHidden = ssid_hidden; + } else { + pe_debug("Dont process HIDE_SSID msg with existing setting"); + return; + } + + ap_mlme_set_hidden_ssid_restart_in_progress(session->vdev, true); + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_FW_VDEV_RESTART, + sizeof(*session), session); +} + +/** + * __lim_process_sme_session_update - process SME session update msg + * + * @mac_ctx: Pointer to global mac context + * @msg_buf: Pointer to the received message buffer + * + * Return: None + */ +static void __lim_process_sme_session_update(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct sir_update_session_param *msg; + struct pe_session *session; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + msg = (struct sir_update_session_param *) msg_buf; + + session = pe_find_session_by_vdev_id(mac_ctx, msg->vdev_id); + if (!session) { + pe_warn("Session does not exist for given vdev_id %d", + msg->vdev_id); + return; + } + + pe_debug("received SME Session update for %d val %d", + msg->param_type, msg->param_val); + switch (msg->param_type) { + case SIR_PARAM_SSID_HIDDEN: + lim_handle_update_ssid_hidden(mac_ctx, session, msg->param_val); + break; + case SIR_PARAM_IGNORE_ASSOC_DISALLOWED: + session->ignore_assoc_disallowed = msg->param_val; + break; + default: + pe_err("Unknown session param"); + break; + } +} + +/* + Update the beacon Interval dynamically if beaconInterval is different in MCC + */ +static void __lim_process_sme_change_bi(struct mac_context *mac, + uint32_t *msg_buf) +{ + struct change_bi_params *pChangeBIParams; + struct pe_session *pe_session; + uint8_t sessionId = 0; + tUpdateBeaconParams beaconParams; + + pe_debug("received Update Beacon Interval message"); + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + qdf_mem_zero(&beaconParams, sizeof(tUpdateBeaconParams)); + pChangeBIParams = (struct change_bi_params *)msg_buf; + + pe_session = pe_find_session_by_bssid(mac, + pChangeBIParams->bssid.bytes, + &sessionId); + if (!pe_session) { + pe_err("Session does not exist for given BSSID"); + return; + } + + /*Update pe_session Beacon Interval */ + if (pe_session->beaconParams.beaconInterval != + pChangeBIParams->beaconInterval) { + pe_session->beaconParams.beaconInterval = + pChangeBIParams->beaconInterval; + } + + /*Update sch beaconInterval */ + if (mac->sch.beacon_interval != + pChangeBIParams->beaconInterval) { + mac->sch.beacon_interval = + pChangeBIParams->beaconInterval; + + pe_debug("LIM send update BeaconInterval Indication: %d", + pChangeBIParams->beaconInterval); + + if (false == mac->sap.SapDfsInfo.is_dfs_cac_timer_running) { + /* Update beacon */ + sch_set_fixed_beacon_fields(mac, pe_session); + + beaconParams.bss_idx = pe_session->vdev_id; + /* Set change in beacon Interval */ + beaconParams.beaconInterval = + pChangeBIParams->beaconInterval; + beaconParams.paramChangeBitmap = + PARAM_BCN_INTERVAL_CHANGED; + lim_send_beacon_params(mac, &beaconParams, pe_session); + } + } + + return; +} /*** end __lim_process_sme_change_bi() ***/ + +#ifdef QCA_HT_2040_COEX +static void __lim_process_sme_set_ht2040_mode(struct mac_context *mac, + uint32_t *msg_buf) +{ + struct set_ht2040_mode *pSetHT2040Mode; + struct pe_session *pe_session; + uint8_t sessionId = 0; + struct scheduler_msg msg = {0}; + tUpdateVHTOpMode *pHtOpMode = NULL; + uint16_t staId = 0; + tpDphHashNode sta = NULL; + + pe_debug("received Set HT 20/40 mode message"); + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + pSetHT2040Mode = (struct set_ht2040_mode *)msg_buf; + + pe_session = pe_find_session_by_bssid(mac, + pSetHT2040Mode->bssid.bytes, + &sessionId); + if (!pe_session) { + pe_debug("Session does not exist for given BSSID"); + lim_print_mac_addr(mac, pSetHT2040Mode->bssid.bytes, LOGD); + return; + } + + pe_debug("Update session entry for cbMod=%d", + pSetHT2040Mode->cbMode); + /*Update pe_session HT related fields */ + switch (pSetHT2040Mode->cbMode) { + case PHY_SINGLE_CHANNEL_CENTERED: + pe_session->htSecondaryChannelOffset = + PHY_SINGLE_CHANNEL_CENTERED; + pe_session->htRecommendedTxWidthSet = 0; + if (pSetHT2040Mode->obssEnabled) + pe_session->htSupportedChannelWidthSet + = eHT_CHANNEL_WIDTH_40MHZ; + else + pe_session->htSupportedChannelWidthSet + = eHT_CHANNEL_WIDTH_20MHZ; + break; + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + pe_session->htSecondaryChannelOffset = + PHY_DOUBLE_CHANNEL_LOW_PRIMARY; + pe_session->htRecommendedTxWidthSet = 1; + break; + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + pe_session->htSecondaryChannelOffset = + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY; + pe_session->htRecommendedTxWidthSet = 1; + break; + default: + pe_err("Invalid cbMode"); + return; + } + + /* Update beacon */ + sch_set_fixed_beacon_fields(mac, pe_session); + lim_send_beacon_ind(mac, pe_session, REASON_SET_HT2040); + + /* update OP Mode for each associated peer */ + for (staId = 0; staId < pe_session->dph.dphHashTable.size; staId++) { + sta = dph_get_hash_entry(mac, staId, + &pe_session->dph.dphHashTable); + if (!sta) + continue; + + if (sta->valid && sta->htSupportedChannelWidthSet) { + pHtOpMode = qdf_mem_malloc(sizeof(tUpdateVHTOpMode)); + if (!pHtOpMode) + return; + pHtOpMode->opMode = + (pe_session->htSecondaryChannelOffset == + PHY_SINGLE_CHANNEL_CENTERED) ? + eHT_CHANNEL_WIDTH_20MHZ : eHT_CHANNEL_WIDTH_40MHZ; + qdf_mem_copy(pHtOpMode->peer_mac, &sta->staAddr, + sizeof(tSirMacAddr)); + pHtOpMode->smesessionId = sessionId; + + msg.type = WMA_UPDATE_OP_MODE; + msg.reserved = 0; + msg.bodyptr = pHtOpMode; + if (!QDF_IS_STATUS_SUCCESS + (scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg))) { + pe_err("Not able to post WMA_UPDATE_OP_MODE message to WMA"); + qdf_mem_free(pHtOpMode); + return; + } + pe_debug("Notified FW about OP mode: %d", + pHtOpMode->opMode); + + } else + pe_debug("station does not support HT40"); + } + + return; +} +#endif + +/* -------------------------------------------------------------------- */ +/** + * __lim_process_report_message + * + * FUNCTION: Processes the next received Radio Resource Management message + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param None + * @return None + */ + +static void __lim_process_report_message(struct mac_context *mac, + struct scheduler_msg *pMsg) +{ + switch (pMsg->type) { + case eWNI_SME_NEIGHBOR_REPORT_REQ_IND: + rrm_process_neighbor_report_req(mac, pMsg->bodyptr); + break; + case eWNI_SME_BEACON_REPORT_RESP_XMIT_IND: + rrm_process_beacon_report_xmit(mac, pMsg->bodyptr); + break; + default: + pe_err("Invalid msg type: %d", pMsg->type); + } +} + +/* -------------------------------------------------------------------- */ +/** + * lim_send_set_max_tx_power_req + * + * FUNCTION: Send SIR_HAL_SET_MAX_TX_POWER_REQ message to change the max tx power. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param txPower txPower to be set. + * @param pe_session session entry. + * @return None + */ +QDF_STATUS +lim_send_set_max_tx_power_req(struct mac_context *mac, int8_t txPower, + struct pe_session *pe_session) +{ + tpMaxTxPowerParams pMaxTxParams = NULL; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + + if (!pe_session) { + pe_err("Invalid parameters"); + return QDF_STATUS_E_FAILURE; + } + + pMaxTxParams = qdf_mem_malloc(sizeof(tMaxTxPowerParams)); + if (!pMaxTxParams) + return QDF_STATUS_E_NOMEM; + pMaxTxParams->power = txPower; + qdf_mem_copy(pMaxTxParams->bssId.bytes, pe_session->bssId, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(pMaxTxParams->selfStaMacAddr.bytes, + pe_session->self_mac_addr, + QDF_MAC_ADDR_SIZE); + + msgQ.type = WMA_SET_MAX_TX_POWER_REQ; + msgQ.bodyptr = pMaxTxParams; + msgQ.bodyval = 0; + pe_debug("Post WMA_SET_MAX_TX_POWER_REQ to WMA"); + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + pe_err("wma_post_ctrl_msg() failed"); + qdf_mem_free(pMaxTxParams); + } + return retCode; +} + +/** + * __lim_process_sme_register_mgmt_frame_req() - process sme reg mgmt frame req + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: pointer to the SME message buffer + * + * This function is called to process eWNI_SME_REGISTER_MGMT_FRAME_REQ message + * from SME. It Register this information within PE. + * + * Return: None + */ +static void __lim_process_sme_register_mgmt_frame_req(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + QDF_STATUS qdf_status; + struct register_mgmt_frame *sme_req = + (struct register_mgmt_frame *)msg_buf; + struct mgmt_frm_reg_info *lim_mgmt_regn = NULL; + struct mgmt_frm_reg_info *next = NULL; + bool match = false; + + pe_nofl_debug("Register Frame: register %d, type %d, match length %d", + sme_req->registerFrame, sme_req->frameType, + sme_req->matchLen); + /* First check whether entry exists already */ + qdf_mutex_acquire(&mac_ctx->lim.lim_frame_register_lock); + qdf_list_peek_front(&mac_ctx->lim.gLimMgmtFrameRegistratinQueue, + (qdf_list_node_t **) &lim_mgmt_regn); + qdf_mutex_release(&mac_ctx->lim.lim_frame_register_lock); + + while (lim_mgmt_regn) { + if (lim_mgmt_regn->frameType != sme_req->frameType) + goto skip_match; + if (sme_req->matchLen) { + if ((lim_mgmt_regn->matchLen == sme_req->matchLen) && + (!qdf_mem_cmp(lim_mgmt_regn->matchData, + sme_req->matchData, + lim_mgmt_regn->matchLen))) { + /* found match! */ + match = true; + break; + } + } else { + /* found match! */ + match = true; + break; + } +skip_match: + qdf_mutex_acquire(&mac_ctx->lim.lim_frame_register_lock); + qdf_status = qdf_list_peek_next( + &mac_ctx->lim.gLimMgmtFrameRegistratinQueue, + (qdf_list_node_t *)lim_mgmt_regn, + (qdf_list_node_t **)&next); + qdf_mutex_release(&mac_ctx->lim.lim_frame_register_lock); + lim_mgmt_regn = next; + next = NULL; + } + if (match) { + qdf_mutex_acquire(&mac_ctx->lim.lim_frame_register_lock); + if (QDF_STATUS_SUCCESS == + qdf_list_remove_node( + &mac_ctx->lim.gLimMgmtFrameRegistratinQueue, + (qdf_list_node_t *)lim_mgmt_regn)) + qdf_mem_free(lim_mgmt_regn); + qdf_mutex_release(&mac_ctx->lim.lim_frame_register_lock); + } + + if (sme_req->registerFrame) { + lim_mgmt_regn = + qdf_mem_malloc(sizeof(struct mgmt_frm_reg_info) + + sme_req->matchLen); + if (lim_mgmt_regn) { + lim_mgmt_regn->frameType = sme_req->frameType; + lim_mgmt_regn->matchLen = sme_req->matchLen; + lim_mgmt_regn->sessionId = sme_req->sessionId; + if (sme_req->matchLen) { + qdf_mem_copy(lim_mgmt_regn->matchData, + sme_req->matchData, + sme_req->matchLen); + } + qdf_mutex_acquire( + &mac_ctx->lim.lim_frame_register_lock); + qdf_list_insert_front(&mac_ctx->lim. + gLimMgmtFrameRegistratinQueue, + &lim_mgmt_regn->node); + qdf_mutex_release( + &mac_ctx->lim.lim_frame_register_lock); + } + } + return; +} + +static void __lim_process_sme_reset_ap_caps_change(struct mac_context *mac, + uint32_t *msg_buf) +{ + tpSirResetAPCapsChange pResetCapsChange; + struct pe_session *pe_session; + uint8_t sessionId = 0; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + pResetCapsChange = (tpSirResetAPCapsChange)msg_buf; + pe_session = + pe_find_session_by_bssid(mac, pResetCapsChange->bssId.bytes, + &sessionId); + if (!pe_session) { + pe_err("Session does not exist for given BSSID"); + return; + } + + pe_session->limSentCapsChangeNtf = false; + return; +} + +/** + * lim_register_mgmt_frame_ind_cb() - Save the Management frame + * indication callback in PE. + * @mac_ptr: Mac pointer + * @msg_buf: Msg pointer containing the callback + * + * This function is used save the Management frame + * indication callback in PE. + * + * Return: None + */ +static void lim_register_mgmt_frame_ind_cb(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct sir_sme_mgmt_frame_cb_req *sme_req = + (struct sir_sme_mgmt_frame_cb_req *)msg_buf; + + if (!msg_buf) { + pe_err("msg_buf is null"); + return; + } + if (sme_req->callback) + mac_ctx->mgmt_frame_ind_cb = + (sir_mgmt_frame_ind_callback)sme_req->callback; + else + pe_err("sme_req->callback is null"); +} + +/** + *__lim_process_send_disassoc_frame() - processes send disassoc frame request + * @mac_ctx: pointer to mac context + * @msg_buf: request message buffer + * + * Process a request from SME to send a disassoc frame + * + * Return: none + */ +static void __lim_process_send_disassoc_frame(struct mac_context *mac_ctx, + void *msg_buf) +{ + struct sme_send_disassoc_frm_req *req = msg_buf; + struct pe_session *session_entry; + + if (!req) { + pe_err("NULL req"); + return; + } + + if ((IEEE80211_IS_MULTICAST(req->peer_mac) && + !QDF_IS_ADDR_BROADCAST(req->peer_mac))) { + pe_err("received invalid SME_DISASSOC_REQ message"); + return; + } + + session_entry = pe_find_session_by_vdev_id(mac_ctx, req->vdev_id); + if (!session_entry) { + pe_err("session does not exist for given bssId " + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(req->peer_mac)); + return; + } + + pe_debug("msg_type %d len %d vdev_id %d mac: " QDF_MAC_ADDR_FMT " reason %d wait_for_ack %d", + req->msg_type, req->length, req->vdev_id, + QDF_MAC_ADDR_REF(req->peer_mac), req->reason, req->wait_for_ack); + + lim_send_disassoc_mgmt_frame(mac_ctx, req->reason, req->peer_mac, + session_entry, req->wait_for_ack); +} + +/** + * lim_set_pdev_ht_ie() - sends the set HT IE req to FW + * @mac_ctx: Pointer to Global MAC structure + * @pdev_id: pdev id to set the IE. + * @nss: Nss values to prepare the HT IE. + * + * Prepares the HT IE with self capabilities for different + * Nss values and sends the set HT IE req to FW. + * + * Return: None + */ +static void lim_set_pdev_ht_ie(struct mac_context *mac_ctx, uint8_t pdev_id, + uint8_t nss) +{ + struct set_ie_param *ie_params; + struct scheduler_msg msg = {0}; + QDF_STATUS rc = QDF_STATUS_SUCCESS; + const uint8_t *p_ie = NULL; + tHtCaps *p_ht_cap; + int i; + + for (i = 1; i <= nss; i++) { + ie_params = qdf_mem_malloc(sizeof(*ie_params)); + if (!ie_params) + return; + ie_params->nss = i; + ie_params->pdev_id = pdev_id; + ie_params->ie_type = DOT11_HT_IE; + /* 2 for IE len and EID */ + ie_params->ie_len = 2 + sizeof(tHtCaps); + ie_params->ie_ptr = qdf_mem_malloc(ie_params->ie_len); + if (!ie_params->ie_ptr) { + qdf_mem_free(ie_params); + return; + } + *ie_params->ie_ptr = WLAN_ELEMID_HTCAP_ANA; + *(ie_params->ie_ptr + 1) = ie_params->ie_len - 2; + lim_set_ht_caps(mac_ctx, NULL, ie_params->ie_ptr, + ie_params->ie_len); + + if (NSS_1x1_MODE == i) { + p_ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_HTCAPS, + ie_params->ie_ptr, ie_params->ie_len); + if (!p_ie) { + qdf_mem_free(ie_params->ie_ptr); + qdf_mem_free(ie_params); + pe_err("failed to get IE ptr"); + return; + } + p_ht_cap = (tHtCaps *)&p_ie[2]; + p_ht_cap->supportedMCSSet[1] = 0; + p_ht_cap->txSTBC = 0; + } + + msg.type = WMA_SET_PDEV_IE_REQ; + msg.bodyptr = ie_params; + msg.bodyval = 0; + + rc = wma_post_ctrl_msg(mac_ctx, &msg); + if (rc != QDF_STATUS_SUCCESS) { + pe_err("wma_post_ctrl_msg() return failure"); + qdf_mem_free(ie_params->ie_ptr); + qdf_mem_free(ie_params); + return; + } + } +} + +/** + * lim_set_pdev_vht_ie() - sends the set VHT IE to req FW + * @mac_ctx: Pointer to Global MAC structure + * @pdev_id: pdev id to set the IE. + * @nss: Nss values to prepare the VHT IE. + * + * Prepares the VHT IE with self capabilities for different + * Nss values and sends the set VHT IE req to FW. + * + * Return: None + */ +static void lim_set_pdev_vht_ie(struct mac_context *mac_ctx, uint8_t pdev_id, + uint8_t nss) +{ + struct set_ie_param *ie_params; + struct scheduler_msg msg = {0}; + QDF_STATUS rc = QDF_STATUS_SUCCESS; + const uint8_t *p_ie = NULL; + tSirMacVHTCapabilityInfo *vht_cap; + int i; + tSirVhtMcsInfo *vht_mcs; + + for (i = 1; i <= nss; i++) { + ie_params = qdf_mem_malloc(sizeof(*ie_params)); + if (!ie_params) + return; + ie_params->nss = i; + ie_params->pdev_id = pdev_id; + ie_params->ie_type = DOT11_VHT_IE; + /* 2 for IE len and EID */ + ie_params->ie_len = 2 + sizeof(tSirMacVHTCapabilityInfo) + + sizeof(tSirVhtMcsInfo); + ie_params->ie_ptr = qdf_mem_malloc(ie_params->ie_len); + if (!ie_params->ie_ptr) { + qdf_mem_free(ie_params); + return; + } + *ie_params->ie_ptr = WLAN_ELEMID_VHTCAP; + *(ie_params->ie_ptr + 1) = ie_params->ie_len - 2; + lim_set_vht_caps(mac_ctx, NULL, ie_params->ie_ptr, + ie_params->ie_len); + + if (NSS_1x1_MODE == i) { + p_ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_VHTCAPS, + ie_params->ie_ptr, ie_params->ie_len); + if (!p_ie) { + qdf_mem_free(ie_params->ie_ptr); + qdf_mem_free(ie_params); + pe_err("failed to get IE ptr"); + return; + } + vht_cap = (tSirMacVHTCapabilityInfo *)&p_ie[2]; + vht_cap->txSTBC = 0; + vht_mcs = + (tSirVhtMcsInfo *)&p_ie[2 + + sizeof(tSirMacVHTCapabilityInfo)]; + vht_mcs->rxMcsMap |= DISABLE_NSS2_MCS; + vht_mcs->rxHighest = + VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + vht_mcs->txMcsMap |= DISABLE_NSS2_MCS; + vht_mcs->txHighest = + VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + } + msg.type = WMA_SET_PDEV_IE_REQ; + msg.bodyptr = ie_params; + msg.bodyval = 0; + + rc = wma_post_ctrl_msg(mac_ctx, &msg); + if (rc != QDF_STATUS_SUCCESS) { + pe_err("wma_post_ctrl_msg failure"); + qdf_mem_free(ie_params->ie_ptr); + qdf_mem_free(ie_params); + return; + } + } +} + +/** + * lim_process_set_vdev_ies_per_band() - process the set vdev IE req + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: Pointer to the SME message buffer + * + * This function is called by limProcessMessageQueue(). This function sets the + * VDEV IEs to the FW. + * + * Return: None + */ +static void lim_process_set_vdev_ies_per_band(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct sir_set_vdev_ies_per_band *p_msg = + (struct sir_set_vdev_ies_per_band *)msg_buf; + + if (!p_msg) { + pe_err("NULL p_msg"); + return; + } + + pe_debug("rcvd set vdev ie per band req vdev_id = %d", + p_msg->vdev_id); + /* intentionally using NULL here so that self capabilty are sent */ + if (lim_send_ies_per_band(mac_ctx, NULL, p_msg->vdev_id, + p_msg->dot11_mode, p_msg->device_mode) != + QDF_STATUS_SUCCESS) + pe_err("Unable to send HT/VHT Cap to FW"); +} + +/** + * lim_process_set_pdev_IEs() - process the set pdev IE req + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: Pointer to the SME message buffer + * + * This function is called by limProcessMessageQueue(). This + * function sets the PDEV IEs to the FW. + * + * Return: None + */ +static void lim_process_set_pdev_IEs(struct mac_context *mac_ctx, uint32_t *msg_buf) +{ + struct sir_set_ht_vht_cfg *ht_vht_cfg; + + ht_vht_cfg = (struct sir_set_ht_vht_cfg *)msg_buf; + + if (!ht_vht_cfg) { + pe_err("NULL ht_vht_cfg"); + return; + } + + pe_debug("rcvd set pdev ht vht ie req with nss = %d", + ht_vht_cfg->nss); + lim_set_pdev_ht_ie(mac_ctx, ht_vht_cfg->pdev_id, ht_vht_cfg->nss); + + if (IS_DOT11_MODE_VHT(ht_vht_cfg->dot11mode)) + lim_set_pdev_vht_ie(mac_ctx, ht_vht_cfg->pdev_id, + ht_vht_cfg->nss); +} + +/** + * lim_process_sme_update_access_policy_vendor_ie: function updates vendor IE + * + * access policy + * @mac_ctx: pointer to mac context + * @msg: message buffer + * + * function processes vendor IE and access policy from SME and updates PE + * + * session entry + * + * return: none +*/ +static void lim_process_sme_update_access_policy_vendor_ie( + struct mac_context *mac_ctx, + uint32_t *msg) +{ + struct sme_update_access_policy_vendor_ie *update_vendor_ie; + struct pe_session *pe_session_entry; + uint16_t num_bytes; + + if (!msg) { + pe_err("Buffer is Pointing to NULL"); + return; + } + update_vendor_ie = (struct sme_update_access_policy_vendor_ie *) msg; + pe_session_entry = pe_find_session_by_vdev_id(mac_ctx, + update_vendor_ie->vdev_id); + + if (!pe_session_entry) { + pe_err("Session does not exist for given vdev_id %d", + update_vendor_ie->vdev_id); + return; + } + if (pe_session_entry->access_policy_vendor_ie) + qdf_mem_free(pe_session_entry->access_policy_vendor_ie); + + num_bytes = update_vendor_ie->ie[1] + 2; + pe_session_entry->access_policy_vendor_ie = qdf_mem_malloc(num_bytes); + if (!pe_session_entry->access_policy_vendor_ie) + return; + qdf_mem_copy(pe_session_entry->access_policy_vendor_ie, + &update_vendor_ie->ie[0], num_bytes); + + pe_session_entry->access_policy = update_vendor_ie->access_policy; +} + +QDF_STATUS lim_sta_mlme_vdev_disconnect_bss(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct mac_context *mac_ctx; + struct scheduler_msg *msg = (struct scheduler_msg *)data; + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + pe_err("mac_ctx is NULL"); + if (data) + qdf_mem_free(data); + return QDF_STATUS_E_INVAL; + } + pe_debug("VDEV Manager disconnect bss callback type:(%d)", msg->type); + + switch (msg->type) { + case eWNI_SME_DEAUTH_REQ: + __lim_process_sme_deauth_req(mac_ctx, + (uint32_t *)msg->bodyptr); + break; + case eWNI_SME_DISASSOC_CNF: + case eWNI_SME_DEAUTH_CNF: + __lim_process_sme_disassoc_cnf(mac_ctx, + (uint32_t *)msg->bodyptr); + break; + case eWNI_SME_DISASSOC_REQ: + __lim_process_sme_disassoc_req(mac_ctx, + (uint32_t *)msg->bodyptr); + break; + default: + pe_debug("Wrong message type received %d", msg->type); + } + return QDF_STATUS_SUCCESS; +} + +static void lim_process_disconnect_sta(struct pe_session *session, + struct scheduler_msg *msg) +{ + if (QDF_IS_STATUS_SUCCESS( + wlan_vdev_is_restart_progress(session->vdev))) + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_RESTART_REQ_FAIL, + sizeof(*msg), msg); + else + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_DOWN, + sizeof(*msg), msg); +} + +static void lim_process_sme_disassoc_cnf(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + struct disassoc_cnf sme_disassoc_cnf; + struct pe_session *session; + uint8_t session_id; + uint32_t *err_msg = NULL; + QDF_STATUS status; + + qdf_mem_copy(&sme_disassoc_cnf, msg->bodyptr, sizeof(sme_disassoc_cnf)); + + session = pe_find_session_by_bssid(mac_ctx, + sme_disassoc_cnf.bssid.bytes, + &session_id); + if (!session) { + pe_err("session not found for bssid:"QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sme_disassoc_cnf.bssid.bytes)); + status = lim_prepare_disconnect_done_ind + (mac_ctx, &err_msg, + sme_disassoc_cnf.vdev_id, + eSIR_SME_INVALID_SESSION, + NULL); + + if (QDF_IS_STATUS_SUCCESS(status)) + lim_send_sme_disassoc_deauth_ntf(mac_ctx, + QDF_STATUS_SUCCESS, + err_msg); + return; + } + + if (LIM_IS_STA_ROLE(session)) + lim_process_disconnect_sta(session, msg); + else + __lim_process_sme_disassoc_cnf(mac_ctx, + (uint32_t *)msg->bodyptr); +} + +static void lim_process_sme_disassoc_req(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + struct disassoc_req disassoc_req; + struct pe_session *session; + uint8_t session_id; + + qdf_mem_copy(&disassoc_req, msg->bodyptr, sizeof(struct disassoc_req)); + + session = pe_find_session_by_bssid(mac_ctx, + disassoc_req.bssid.bytes, + &session_id); + if (!session) { + pe_err("session not found for bssid:"QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(disassoc_req.bssid.bytes)); + lim_send_sme_disassoc_ntf(mac_ctx, + disassoc_req.peer_macaddr.bytes, + eSIR_SME_INVALID_PARAMETERS, + eLIM_HOST_DISASSOC, 1, + disassoc_req.sessionId, NULL); + + return; + } + + /* In LFR2.0 roaming scenario, if association is not completed with + * new AP, there is possibality of trying to send disassoc in + * failure handling. So, if vdev is in INIT state send + * disassoc failure and cleanup session. + */ + if (QDF_IS_STATUS_SUCCESS( + wlan_vdev_mlme_is_init_state(session->vdev))) { + pe_err("vdev is in INIT state. Send failure."); + lim_send_sme_disassoc_ntf(mac_ctx, + disassoc_req.peer_macaddr.bytes, + eSIR_SME_INVALID_PARAMETERS, + eLIM_HOST_DISASSOC, 1, + disassoc_req.sessionId, session); + + return; + } + + if (LIM_IS_STA_ROLE(session)) + lim_process_disconnect_sta(session, msg); + else + __lim_process_sme_disassoc_req(mac_ctx, + (uint32_t *)msg->bodyptr); +} + +static void lim_process_sme_deauth_req(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + struct deauth_req sme_deauth_req; + struct pe_session *session; + uint8_t session_id; + + qdf_mem_copy(&sme_deauth_req, msg->bodyptr, sizeof(sme_deauth_req)); + + session = pe_find_session_by_bssid(mac_ctx, + sme_deauth_req.bssid.bytes, + &session_id); + if (!session) { + pe_err("session not found for bssid:"QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sme_deauth_req.bssid.bytes)); + lim_send_sme_deauth_ntf(mac_ctx, + sme_deauth_req.peer_macaddr.bytes, + eSIR_SME_INVALID_PARAMETERS, + eLIM_HOST_DEAUTH, 1, + sme_deauth_req.vdev_id); + + return; + } + + if (QDF_IS_STATUS_SUCCESS( + wlan_vdev_mlme_is_init_state(session->vdev))) { + pe_err("vdev is in INIT state. Send failure."); + lim_send_sme_deauth_ntf(mac_ctx, + sme_deauth_req.peer_macaddr.bytes, + eSIR_SME_INVALID_PARAMETERS, + eLIM_HOST_DEAUTH, 1, + sme_deauth_req.vdev_id); + return; + } + + if (LIM_IS_STA_ROLE(session)) + lim_process_disconnect_sta(session, msg); + else + __lim_process_sme_deauth_req(mac_ctx, + (uint32_t *)msg->bodyptr); +} + +/** + * lim_process_sme_req_messages() + * + ***FUNCTION: + * This function is called by limProcessMessageQueue(). This + * function processes SME request messages from HDD or upper layer + * application. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param msgType Indicates the SME message type + * @param *msg_buf A pointer to the SME message buffer + * @return Boolean - true - if msg_buf is consumed and can be freed. + * false - if msg_buf is not to be freed. + */ + +bool lim_process_sme_req_messages(struct mac_context *mac, + struct scheduler_msg *pMsg) +{ + /* + * Set this flag to false within case block of any following message, + * that doesn't want msg_buf to be freed. + */ + bool bufConsumed = true; + uint32_t *msg_buf = pMsg->bodyptr; + + pe_nofl_debug("LIM handle SME Msg %s(%d)", + lim_msg_str(pMsg->type), pMsg->type); + + /* If no insert NOA required then execute the code below */ + + switch (pMsg->type) { + case eWNI_SME_SYS_READY_IND: + bufConsumed = __lim_process_sme_sys_ready_ind(mac, msg_buf); + break; + + case eWNI_SME_START_BSS_REQ: + bufConsumed = __lim_process_sme_start_bss_req(mac, pMsg); + break; + + case eWNI_SME_JOIN_REQ: + __lim_process_sme_join_req(mac, msg_buf); + break; + + case eWNI_SME_REASSOC_REQ: + __lim_process_sme_reassoc_req(mac, msg_buf); + break; + + case eWNI_SME_DISASSOC_REQ: + lim_process_sme_disassoc_req(mac, pMsg); + break; + + case eWNI_SME_DISASSOC_CNF: + case eWNI_SME_DEAUTH_CNF: + lim_process_sme_disassoc_cnf(mac, pMsg); + break; + + case eWNI_SME_DEAUTH_REQ: + lim_process_sme_deauth_req(mac, pMsg); + break; + + case eWNI_SME_SEND_DISASSOC_FRAME: + __lim_process_send_disassoc_frame(mac, msg_buf); + break; + + case eWNI_SME_STOP_BSS_REQ: + bufConsumed = __lim_process_sme_stop_bss_req(mac, pMsg); + break; + + case eWNI_SME_ASSOC_CNF: + if (pMsg->type == eWNI_SME_ASSOC_CNF) + pe_debug("Received ASSOC_CNF message"); + __lim_process_sme_assoc_cnf_new(mac, pMsg->type, + msg_buf); + break; + + case eWNI_SME_ADDTS_REQ: + pe_debug("Received ADDTS_REQ message"); + __lim_process_sme_addts_req(mac, msg_buf); + break; + + case eWNI_SME_DELTS_REQ: + pe_debug("Received DELTS_REQ message"); + __lim_process_sme_delts_req(mac, msg_buf); + break; + + case SIR_LIM_ADDTS_RSP_TIMEOUT: + pe_debug("Received SIR_LIM_ADDTS_RSP_TIMEOUT message"); + lim_process_sme_addts_rsp_timeout(mac, pMsg->bodyval); + break; + +#ifdef FEATURE_WLAN_ESE + case eWNI_SME_GET_TSM_STATS_REQ: + __lim_process_sme_get_tsm_stats_request(mac, msg_buf); + bufConsumed = false; + break; +#endif /* FEATURE_WLAN_ESE */ + case eWNI_SME_SESSION_UPDATE_PARAM: + __lim_process_sme_session_update(mac, msg_buf); + break; + case eWNI_SME_ROAM_SCAN_OFFLOAD_REQ: + __lim_process_roam_scan_offload_req(mac, msg_buf); + bufConsumed = false; + break; + case eWNI_SME_ROAM_INIT_PARAM: + lim_send_roam_offload_init(mac, msg_buf); + bufConsumed = false; + break; + case eWNI_SME_ROAM_SEND_PER_REQ: + lim_send_roam_per_command(mac, msg_buf); + bufConsumed = false; + break; + case eWNI_SME_ROAM_INVOKE: + lim_process_roam_invoke(mac, msg_buf); + bufConsumed = false; + break; + case eWNI_SME_CHNG_MCC_BEACON_INTERVAL: + /* Update the beaconInterval */ + __lim_process_sme_change_bi(mac, msg_buf); + break; + +#ifdef QCA_HT_2040_COEX + case eWNI_SME_SET_HT_2040_MODE: + __lim_process_sme_set_ht2040_mode(mac, msg_buf); + break; +#endif + + case eWNI_SME_NEIGHBOR_REPORT_REQ_IND: + case eWNI_SME_BEACON_REPORT_RESP_XMIT_IND: + __lim_process_report_message(mac, pMsg); + break; + + case eWNI_SME_FT_PRE_AUTH_REQ: + bufConsumed = (bool) lim_process_ft_pre_auth_req(mac, pMsg); + break; + + case eWNI_SME_FT_AGGR_QOS_REQ: + lim_process_ft_aggr_qos_req(mac, msg_buf); + break; + + case eWNI_SME_REGISTER_MGMT_FRAME_REQ: + __lim_process_sme_register_mgmt_frame_req(mac, msg_buf); + break; +#ifdef FEATURE_WLAN_TDLS + case eWNI_SME_TDLS_SEND_MGMT_REQ: + lim_process_sme_tdls_mgmt_send_req(mac, msg_buf); + break; + case eWNI_SME_TDLS_ADD_STA_REQ: + lim_process_sme_tdls_add_sta_req(mac, msg_buf); + break; + case eWNI_SME_TDLS_DEL_STA_REQ: + lim_process_sme_tdls_del_sta_req(mac, msg_buf); + break; +#endif + case eWNI_SME_RESET_AP_CAPS_CHANGED: + __lim_process_sme_reset_ap_caps_change(mac, msg_buf); + break; + + case eWNI_SME_CHANNEL_CHANGE_REQ: + lim_process_sme_channel_change_request(mac, msg_buf); + break; + + case eWNI_SME_START_BEACON_REQ: + lim_process_sme_start_beacon_req(mac, msg_buf); + break; + + case eWNI_SME_DFS_BEACON_CHAN_SW_IE_REQ: + lim_process_sme_dfs_csa_ie_request(mac, msg_buf); + break; + + case eWNI_SME_UPDATE_ADDITIONAL_IES: + lim_process_update_add_ies(mac, msg_buf); + break; + + case eWNI_SME_MODIFY_ADDITIONAL_IES: + lim_process_modify_add_ies(mac, msg_buf); + break; + case eWNI_SME_SET_HW_MODE_REQ: + lim_process_set_hw_mode(mac, msg_buf); + break; + case eWNI_SME_NSS_UPDATE_REQ: + lim_process_nss_update_request(mac, msg_buf); + break; + case eWNI_SME_SET_DUAL_MAC_CFG_REQ: + lim_process_set_dual_mac_cfg_req(mac, msg_buf); + break; + case eWNI_SME_SET_IE_REQ: + lim_process_set_ie_req(mac, msg_buf); + break; + case eWNI_SME_REGISTER_MGMT_FRAME_CB: + lim_register_mgmt_frame_ind_cb(mac, msg_buf); + break; + case eWNI_SME_EXT_CHANGE_CHANNEL: + lim_process_ext_change_channel(mac, msg_buf); + break; + case eWNI_SME_SET_ANTENNA_MODE_REQ: + lim_process_set_antenna_mode_req(mac, msg_buf); + break; + case eWNI_SME_PDEV_SET_HT_VHT_IE: + lim_process_set_pdev_IEs(mac, msg_buf); + break; + case eWNI_SME_SET_VDEV_IES_PER_BAND: + lim_process_set_vdev_ies_per_band(mac, msg_buf); + break; + case eWNI_SME_UPDATE_ACCESS_POLICY_VENDOR_IE: + lim_process_sme_update_access_policy_vendor_ie(mac, msg_buf); + break; + case eWNI_SME_UPDATE_CONFIG: + lim_process_sme_update_config(mac, + (struct update_config *)msg_buf); + break; + case eWNI_SME_SET_ADDBA_ACCEPT: + lim_process_sme_set_addba_accept(mac, + (struct sme_addba_accept *)msg_buf); + break; + case eWNI_SME_UPDATE_EDCA_PROFILE: + lim_process_sme_update_edca_params(mac, pMsg->bodyval); + break; + case WNI_SME_UPDATE_MU_EDCA_PARAMS: + lim_process_sme_update_mu_edca_params(mac, pMsg->bodyval); + break; + case eWNI_SME_UPDATE_SESSION_EDCA_TXQ_PARAMS: + lim_process_sme_update_session_edca_txq_params(mac, msg_buf); + break; + case WNI_SME_CFG_ACTION_FRM_HE_TB_PPDU: + lim_process_sme_cfg_action_frm_in_tb_ppdu(mac, + (struct sir_cfg_action_frm_tb_ppdu *)msg_buf); + break; + case WNI_SME_REGISTER_BCN_REPORT_SEND_CB: + lim_register_bcn_report_send_cb(mac, pMsg); + break; + default: + qdf_mem_free((void *)pMsg->bodyptr); + pMsg->bodyptr = NULL; + break; + } /* switch (msgType) */ + + return bufConsumed; +} /*** end lim_process_sme_req_messages() ***/ + +/** + * lim_process_sme_start_beacon_req() + * + ***FUNCTION: + * This function is called by limProcessMessageQueue(). This + * function processes SME request messages from HDD or upper layer + * application. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param msgType Indicates the SME message type + * @param *msg_buf A pointer to the SME message buffer + * @return Boolean - true - if msg_buf is consumed and can be freed. + * false - if msg_buf is not to be freed. + */ +static void lim_process_sme_start_beacon_req(struct mac_context *mac, uint32_t *pMsg) +{ + tpSirStartBeaconIndication pBeaconStartInd; + struct pe_session *pe_session; + uint8_t sessionId; /* PE sessionID */ + + if (!pMsg) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + pBeaconStartInd = (tpSirStartBeaconIndication) pMsg; + pe_session = pe_find_session_by_bssid(mac, + pBeaconStartInd->bssid, + &sessionId); + if (!pe_session) { + lim_print_mac_addr(mac, pBeaconStartInd->bssid, LOGE); + pe_err("Session does not exist for given bssId"); + return; + } + + if (pBeaconStartInd->beaconStartStatus == true) { + /* + * Currently this Indication comes from SAP + * to start Beacon Tx on a DFS channel + * since beaconing has to be done on DFS + * channel only after CAC WAIT is completed. + * On a DFS Channel LIM does not start beacon + * Tx right after the WMA_ADD_BSS_RSP. + */ + lim_apply_configuration(mac, pe_session); + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + FL("Start Beacon with ssid %s Ch freq %d"), + pe_session->ssId.ssId, + pe_session->curr_op_freq); + lim_send_beacon(mac, pe_session); + lim_enable_obss_detection_config(mac, pe_session); + lim_send_obss_color_collision_cfg(mac, pe_session, + OBSS_COLOR_COLLISION_DETECTION); + } else { + pe_err("Invalid Beacon Start Indication"); + return; + } +} + +static void lim_mon_change_channel( + struct mac_context *mac_ctx, + struct pe_session *session_entry) +{ + if (wlan_vdev_mlme_get_state(session_entry->vdev) == WLAN_VDEV_S_INIT) + wlan_vdev_mlme_sm_deliver_evt(session_entry->vdev, + WLAN_VDEV_SM_EV_START, + sizeof(*session_entry), + session_entry); + else if (wlan_vdev_mlme_get_state(session_entry->vdev) == + WLAN_VDEV_S_UP) { + mlme_set_chan_switch_in_progress(session_entry->vdev, true); + wlan_vdev_mlme_sm_deliver_evt(session_entry->vdev, + WLAN_VDEV_SM_EV_FW_VDEV_RESTART, + sizeof(*session_entry), + session_entry); + } else { + pe_err("Invalid vdev state to change channel"); + } +} + +static void lim_change_channel( + struct mac_context *mac_ctx, + struct pe_session *session_entry) +{ + if (session_entry->bssType == eSIR_MONITOR_MODE) + return lim_mon_change_channel(mac_ctx, session_entry); + + mlme_set_chan_switch_in_progress(session_entry->vdev, true); + + if (wlan_vdev_mlme_get_state(session_entry->vdev) == + WLAN_VDEV_S_DFS_CAC_WAIT) + wlan_vdev_mlme_sm_deliver_evt(session_entry->vdev, + WLAN_VDEV_SM_EV_RADAR_DETECTED, + sizeof(*session_entry), + session_entry); + else + wlan_vdev_mlme_sm_deliver_evt(session_entry->vdev, + WLAN_VDEV_SM_EV_CSA_COMPLETE, + sizeof(*session_entry), + session_entry); +} + +/** + * lim_process_sme_channel_change_request() - process sme ch change req + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: pointer to the SME message buffer + * + * This function is called to process SME_CHANNEL_CHANGE_REQ message + * + * Return: None + */ +static void lim_process_sme_channel_change_request(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + tpSirChanChangeRequest ch_change_req; + struct pe_session *session_entry; + uint8_t session_id; /* PE session_id */ + int8_t max_tx_pwr; + uint32_t target_freq; + + if (!msg_buf) { + pe_err("msg_buf is NULL"); + return; + } + ch_change_req = (tpSirChanChangeRequest)msg_buf; + + target_freq = ch_change_req->target_chan_freq; + + max_tx_pwr = wlan_reg_get_channel_reg_power_for_freq( + mac_ctx->pdev, target_freq); + + if ((ch_change_req->messageType != eWNI_SME_CHANNEL_CHANGE_REQ) || + (max_tx_pwr == WMA_MAX_TXPOWER_INVALID)) { + pe_err("Invalid Request/max_tx_pwr"); + return; + } + + session_entry = pe_find_session_by_bssid(mac_ctx, + ch_change_req->bssid, &session_id); + if (!session_entry) { + lim_print_mac_addr(mac_ctx, ch_change_req->bssid, LOGE); + pe_err("Session does not exist for given bssId"); + return; + } + + if (session_entry->curr_op_freq == target_freq && + session_entry->ch_width == ch_change_req->ch_width) { + pe_err("Target channel and mode is same as current channel and mode channel freq %d and mode %d", + session_entry->curr_op_freq, session_entry->ch_width); + return; + } + + if (LIM_IS_AP_ROLE(session_entry)) + session_entry->channelChangeReasonCode = + LIM_SWITCH_CHANNEL_SAP_DFS; + else + session_entry->channelChangeReasonCode = + LIM_SWITCH_CHANNEL_MONITOR; + + pe_nofl_debug("SAP CSA: %d ---> %d, ch_bw %d, nw_type %d, dot11mode %d", + session_entry->curr_op_freq, target_freq, + ch_change_req->ch_width, ch_change_req->nw_type, + ch_change_req->dot11mode); + + if (IS_DOT11_MODE_HE(ch_change_req->dot11mode) && + ((QDF_MONITOR_MODE == session_entry->opmode) || + lim_is_session_he_capable(session_entry))) { + lim_update_session_he_capable_chan_switch + (mac_ctx, session_entry, target_freq); + } else if (wlan_reg_is_6ghz_chan_freq(target_freq)) { + pe_debug("Invalid target_freq %d for dot11mode %d cur HE %d", + target_freq, ch_change_req->dot11mode, + lim_is_session_he_capable(session_entry)); + return; + } + + /* Store the New Channel Params in session_entry */ + session_entry->ch_width = ch_change_req->ch_width; + session_entry->ch_center_freq_seg0 = + ch_change_req->center_freq_seg_0; + session_entry->ch_center_freq_seg1 = + ch_change_req->center_freq_seg_1; + session_entry->htSecondaryChannelOffset = ch_change_req->sec_ch_offset; + session_entry->htSupportedChannelWidthSet = + (ch_change_req->ch_width ? 1 : 0); + session_entry->htRecommendedTxWidthSet = + session_entry->htSupportedChannelWidthSet; + session_entry->curr_op_freq = target_freq; + session_entry->limRFBand = lim_get_rf_band( + session_entry->curr_op_freq); + session_entry->cac_duration_ms = ch_change_req->cac_duration_ms; + session_entry->dfs_regdomain = ch_change_req->dfs_regdomain; + session_entry->maxTxPower = max_tx_pwr; + + /* Update the global beacon filter */ + lim_update_bcn_probe_filter(mac_ctx, session_entry); + + /* Initialize 11h Enable Flag */ + if (CHAN_HOP_ALL_BANDS_ENABLE || + session_entry->limRFBand == REG_BAND_5G) + session_entry->lim11hEnable = + mac_ctx->mlme_cfg->gen.enabled_11h; + else + session_entry->lim11hEnable = 0; + + session_entry->dot11mode = ch_change_req->dot11mode; + session_entry->nwType = ch_change_req->nw_type; + qdf_mem_copy(&session_entry->rateSet, + &ch_change_req->operational_rateset, + sizeof(session_entry->rateSet)); + qdf_mem_copy(&session_entry->extRateSet, + &ch_change_req->extended_rateset, + sizeof(session_entry->extRateSet)); + + lim_change_channel(mac_ctx, session_entry); +} + +/****************************************************************************** +* lim_start_bss_update_add_ie_buffer() +* +***FUNCTION: +* This function checks the src buffer and its length and then malloc for +* dst buffer update the same +* +***LOGIC: +* +***ASSUMPTIONS: +* +***NOTE: +* +* @param mac Pointer to Global MAC structure +* @param **pDstData_buff A pointer to pointer of uint8_t dst buffer +* @param *pDstDataLen A pointer to pointer of uint16_t dst buffer length +* @param *pSrcData_buff A pointer of uint8_t src buffer +* @param srcDataLen src buffer length +******************************************************************************/ + +static void +lim_start_bss_update_add_ie_buffer(struct mac_context *mac, + uint8_t **pDstData_buff, + uint16_t *pDstDataLen, + uint8_t *pSrcData_buff, uint16_t srcDataLen) +{ + + if (srcDataLen > 0 && pSrcData_buff) { + *pDstDataLen = srcDataLen; + + *pDstData_buff = qdf_mem_malloc(*pDstDataLen); + if (!*pDstData_buff) + return; + qdf_mem_copy(*pDstData_buff, pSrcData_buff, *pDstDataLen); + } else { + *pDstData_buff = NULL; + *pDstDataLen = 0; + } +} + +/****************************************************************************** +* lim_update_add_ie_buffer() +* +***FUNCTION: +* This function checks the src buffer and length if src buffer length more +* than dst buffer length then free the dst buffer and malloc for the new src +* length, and update the dst buffer and length. But if dst buffer is bigger +* than src buffer length then it just update the dst buffer and length +* +***LOGIC: +* +***ASSUMPTIONS: +* +***NOTE: +* +* @param mac Pointer to Global MAC structure +* @param **pDstData_buff A pointer to pointer of uint8_t dst buffer +* @param *pDstDataLen A pointer to pointer of uint16_t dst buffer length +* @param *pSrcData_buff A pointer of uint8_t src buffer +* @param srcDataLen src buffer length +******************************************************************************/ + +static void +lim_update_add_ie_buffer(struct mac_context *mac, + uint8_t **pDstData_buff, + uint16_t *pDstDataLen, + uint8_t *pSrcData_buff, uint16_t srcDataLen) +{ + + if (!pSrcData_buff) { + pe_err("src buffer is null"); + return; + } + + if (srcDataLen > *pDstDataLen) { + *pDstDataLen = srcDataLen; + /* free old buffer */ + qdf_mem_free(*pDstData_buff); + /* allocate a new */ + *pDstData_buff = qdf_mem_malloc(*pDstDataLen); + if (!*pDstData_buff) { + *pDstDataLen = 0; + return; + } + } + + /* copy the content of buffer into dst buffer + */ + *pDstDataLen = srcDataLen; + qdf_mem_copy(*pDstData_buff, pSrcData_buff, *pDstDataLen); + +} + +#ifdef QCA_IBSS_SUPPORT +/** + * lim_update_ibss_prop_add_ies() - update IBSS prop IE + * @mac : Pointer to Global MAC structure + * @pDstData_buff : A pointer to pointer of dst buffer + * @pDstDataLen : A pointer to pointer of dst buffer length + * @pModifyIE : A pointer to tSirModifyIE + * + * This function replaces previous ibss prop_ie with new ibss prop_ie. + * + * Return: + * True or false depending upon whether IE is updated or not + */ +static bool +lim_update_ibss_prop_add_ies(struct mac_context *mac, uint8_t **pDstData_buff, + uint16_t *pDstDataLen, tSirModifyIE *pModifyIE) +{ + int32_t oui_length; + uint8_t *ibss_ie = NULL; + uint8_t *vendor_ie; +#define MAC_VENDOR_OUI "\x00\x16\x32" +#define MAC_VENDOR_SIZE 3 + + ibss_ie = pModifyIE->pIEBuffer; + oui_length = pModifyIE->oui_length; + + if ((0 == oui_length) || (!ibss_ie)) { + pe_err("Invalid set IBSS vendor IE command length %d", + oui_length); + return false; + } + + /* + * Why replace only beacon OUI data here: + * 1. other ie (such as wpa) shall not be overwritten here. + * 2. per spec, beacon oui ie might be set twice and original one + * shall be updated. + */ + vendor_ie = (uint8_t *)wlan_get_vendor_ie_ptr_from_oui(MAC_VENDOR_OUI, + MAC_VENDOR_SIZE, *pDstData_buff, *pDstDataLen); + if (vendor_ie) { + QDF_ASSERT((vendor_ie[1] + 2) == pModifyIE->ieBufferlength); + qdf_mem_copy(vendor_ie, pModifyIE->pIEBuffer, + pModifyIE->ieBufferlength); + } else { + uint16_t new_length; + uint8_t *new_ptr; + + /* + * check for uint16 overflow before using sum of two numbers as + * length of size to malloc + */ + if (USHRT_MAX - pModifyIE->ieBufferlength < *pDstDataLen) { + pe_err("U16 overflow due to %d + %d", + pModifyIE->ieBufferlength, *pDstDataLen); + return false; + } + + new_length = pModifyIE->ieBufferlength + *pDstDataLen; + new_ptr = qdf_mem_malloc(new_length); + if (!new_ptr) + return false; + qdf_mem_copy(new_ptr, *pDstData_buff, *pDstDataLen); + qdf_mem_copy(&new_ptr[*pDstDataLen], pModifyIE->pIEBuffer, + pModifyIE->ieBufferlength); + qdf_mem_free(*pDstData_buff); + *pDstDataLen = new_length; + *pDstData_buff = new_ptr; + } + return true; +} +#else +static bool +lim_update_ibss_prop_add_ies(struct mac_context *mac, uint8_t **pDstData_buff, + uint16_t *pDstDataLen, tSirModifyIE *pModifyIE) +{ + return false; +} +#endif +/* +* lim_process_modify_add_ies() - process modify additional IE req. +* +* @mac_ctx: Pointer to Global MAC structure +* @msg_buf: pointer to the SME message buffer +* +* This function update the PE buffers for additional IEs. +* +* Return: None +*/ +static void lim_process_modify_add_ies(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + tpSirModifyIEsInd modify_add_ies; + struct pe_session *session_entry; + uint8_t session_id; + bool ret = false; + struct add_ie_params *add_ie_params; + + if (!msg_buf) { + pe_err("msg_buf is NULL"); + return; + } + + modify_add_ies = (tpSirModifyIEsInd)msg_buf; + /* Incoming message has smeSession, use BSSID to find PE session */ + session_entry = pe_find_session_by_bssid(mac_ctx, + modify_add_ies->modifyIE.bssid.bytes, &session_id); + + if (!session_entry) { + pe_err("Session not found for given bssid" + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(modify_add_ies->modifyIE.bssid.bytes)); + goto end; + } + if ((0 == modify_add_ies->modifyIE.ieBufferlength) || + (0 == modify_add_ies->modifyIE.ieIDLen) || + (!modify_add_ies->modifyIE.pIEBuffer)) { + pe_err("Invalid request pIEBuffer %pK ieBufferlength %d ieIDLen %d ieID %d. update Type %d", + modify_add_ies->modifyIE.pIEBuffer, + modify_add_ies->modifyIE.ieBufferlength, + modify_add_ies->modifyIE.ieID, + modify_add_ies->modifyIE.ieIDLen, + modify_add_ies->updateType); + goto end; + } + add_ie_params = &session_entry->add_ie_params; + switch (modify_add_ies->updateType) { + case eUPDATE_IE_PROBE_RESP: + /* Probe resp */ + if (LIM_IS_IBSS_ROLE(session_entry)) { + lim_update_ibss_prop_add_ies(mac_ctx, + &add_ie_params->probeRespData_buff, + &add_ie_params->probeRespDataLen, + &modify_add_ies->modifyIE); + } + break; + case eUPDATE_IE_ASSOC_RESP: + /* assoc resp IE */ + if (add_ie_params->assocRespDataLen == 0) { + QDF_TRACE(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_ERROR, FL( + "assoc resp add ie not present %d"), + add_ie_params->assocRespDataLen); + } + /* search through the buffer and modify the IE */ + break; + case eUPDATE_IE_PROBE_BCN: + /*probe beacon IE */ + if (LIM_IS_IBSS_ROLE(session_entry)) { + ret = lim_update_ibss_prop_add_ies(mac_ctx, + &add_ie_params->probeRespBCNData_buff, + &add_ie_params->probeRespBCNDataLen, + &modify_add_ies->modifyIE); + } + if (ret == true && modify_add_ies->modifyIE.notify) { + lim_handle_param_update(mac_ctx, + modify_add_ies->updateType); + } + break; + default: + pe_err("unhandled buffer type %d", + modify_add_ies->updateType); + break; + } +end: + qdf_mem_free(modify_add_ies->modifyIE.pIEBuffer); + modify_add_ies->modifyIE.pIEBuffer = NULL; +} + +/* +* lim_process_update_add_ies() - process additional IE update req +* +* @mac_ctx: Pointer to Global MAC structure +* @msg_buf: pointer to the SME message buffer +* +* This function update the PE buffers for additional IEs. +* +* Return: None +*/ +static void lim_process_update_add_ies(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + tpSirUpdateIEsInd update_add_ies = (tpSirUpdateIEsInd)msg_buf; + uint8_t session_id; + struct pe_session *session_entry; + struct add_ie_params *addn_ie; + uint16_t new_length = 0; + uint8_t *new_ptr = NULL; + tSirUpdateIE *update_ie; + + if (!msg_buf) { + pe_err("msg_buf is NULL"); + return; + } + update_ie = &update_add_ies->updateIE; + /* incoming message has smeSession, use BSSID to find PE session */ + session_entry = pe_find_session_by_bssid(mac_ctx, + update_ie->bssid.bytes, &session_id); + + if (!session_entry) { + pe_debug("Session not found for given bssid" + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(update_ie->bssid.bytes)); + goto end; + } + addn_ie = &session_entry->add_ie_params; + /* if len is 0, upper layer requested freeing of buffer */ + if (0 == update_ie->ieBufferlength) { + switch (update_add_ies->updateType) { + case eUPDATE_IE_PROBE_RESP: + qdf_mem_free(addn_ie->probeRespData_buff); + addn_ie->probeRespData_buff = NULL; + addn_ie->probeRespDataLen = 0; + break; + case eUPDATE_IE_ASSOC_RESP: + qdf_mem_free(addn_ie->assocRespData_buff); + addn_ie->assocRespData_buff = NULL; + addn_ie->assocRespDataLen = 0; + break; + case eUPDATE_IE_PROBE_BCN: + qdf_mem_free(addn_ie->probeRespBCNData_buff); + addn_ie->probeRespBCNData_buff = NULL; + addn_ie->probeRespBCNDataLen = 0; + + if (update_ie->notify) + lim_handle_param_update(mac_ctx, + update_add_ies->updateType); + break; + default: + break; + } + return; + } + switch (update_add_ies->updateType) { + case eUPDATE_IE_PROBE_RESP: + if (update_ie->append) { + /* + * In case of append, allocate new memory + * with combined length. + * Multiple back to back append commands + * can lead to a huge length.So, check + * for the validity of the length. + */ + if (addn_ie->probeRespDataLen > + (USHRT_MAX - update_ie->ieBufferlength)) { + pe_err("IE Length overflow, curr:%d, new:%d", + addn_ie->probeRespDataLen, + update_ie->ieBufferlength); + goto end; + } + new_length = update_ie->ieBufferlength + + addn_ie->probeRespDataLen; + new_ptr = qdf_mem_malloc(new_length); + if (!new_ptr) + goto end; + /* append buffer to end of local buffers */ + qdf_mem_copy(new_ptr, addn_ie->probeRespData_buff, + addn_ie->probeRespDataLen); + qdf_mem_copy(&new_ptr[addn_ie->probeRespDataLen], + update_ie->pAdditionIEBuffer, + update_ie->ieBufferlength); + /* free old memory */ + qdf_mem_free(addn_ie->probeRespData_buff); + /* adjust length accordingly */ + addn_ie->probeRespDataLen = new_length; + /* save refernece of local buffer in PE session */ + addn_ie->probeRespData_buff = new_ptr; + goto end; + } + lim_update_add_ie_buffer(mac_ctx, &addn_ie->probeRespData_buff, + &addn_ie->probeRespDataLen, + update_ie->pAdditionIEBuffer, + update_ie->ieBufferlength); + break; + case eUPDATE_IE_ASSOC_RESP: + /* assoc resp IE */ + lim_update_add_ie_buffer(mac_ctx, &addn_ie->assocRespData_buff, + &addn_ie->assocRespDataLen, + update_ie->pAdditionIEBuffer, + update_ie->ieBufferlength); + break; + case eUPDATE_IE_PROBE_BCN: + /* probe resp Bcn IE */ + lim_update_add_ie_buffer(mac_ctx, + &addn_ie->probeRespBCNData_buff, + &addn_ie->probeRespBCNDataLen, + update_ie->pAdditionIEBuffer, + update_ie->ieBufferlength); + if (update_ie->notify) + lim_handle_param_update(mac_ctx, + update_add_ies->updateType); + break; + default: + pe_err("unhandled buffer type %d", update_add_ies->updateType); + break; + } +end: + qdf_mem_free(update_ie->pAdditionIEBuffer); + update_ie->pAdditionIEBuffer = NULL; +} + +void send_extended_chan_switch_action_frame(struct mac_context *mac_ctx, + uint16_t new_channel_freq, + enum phy_ch_width ch_bandwidth, + struct pe_session *session_entry) +{ + uint8_t op_class = 0; + uint8_t switch_mode = 0, i; + tpDphHashNode psta; + uint8_t switch_count; + uint8_t new_channel = 0; + + op_class = + lim_op_class_from_bandwidth(mac_ctx, new_channel_freq, + ch_bandwidth, + session_entry->gLimChannelSwitch.sec_ch_offset); + new_channel = wlan_reg_freq_to_chan(mac_ctx->pdev, new_channel_freq); + if (LIM_IS_AP_ROLE(session_entry) && + (mac_ctx->sap.SapDfsInfo.disable_dfs_ch_switch == false)) + switch_mode = session_entry->gLimChannelSwitch.switchMode; + + switch_count = session_entry->gLimChannelSwitch.switchCount; + + if (LIM_IS_AP_ROLE(session_entry)) { + for (i = 0; i <= mac_ctx->lim.maxStation; i++) { + psta = + session_entry->dph.dphHashTable.pDphNodeArray + i; + if (psta && psta->added) + lim_send_extended_chan_switch_action_frame( + mac_ctx, + psta->staAddr, + switch_mode, op_class, new_channel, + switch_count, session_entry); + } + } else if (LIM_IS_STA_ROLE(session_entry)) { + lim_send_extended_chan_switch_action_frame(mac_ctx, + session_entry->bssId, + switch_mode, op_class, new_channel, + switch_count, session_entry); + } + +} + +void lim_send_chan_switch_action_frame(struct mac_context *mac_ctx, + uint16_t new_channel_freq, + enum phy_ch_width ch_bandwidth, + struct pe_session *session_entry) +{ + uint8_t op_class = 0, new_channel; + uint8_t switch_mode = 0, i; + uint8_t switch_count; + tpDphHashNode psta; + tpDphHashNode dph_node_array_ptr; + + dph_node_array_ptr = session_entry->dph.dphHashTable.pDphNodeArray; + op_class = + lim_op_class_from_bandwidth(mac_ctx, new_channel_freq, + ch_bandwidth, + session_entry->gLimChannelSwitch.sec_ch_offset); + new_channel = wlan_reg_freq_to_chan(mac_ctx->pdev, new_channel_freq); + + if (LIM_IS_AP_ROLE(session_entry) && + (false == mac_ctx->sap.SapDfsInfo.disable_dfs_ch_switch)) + switch_mode = session_entry->gLimChannelSwitch.switchMode; + + switch_count = session_entry->gLimChannelSwitch.switchCount; + + if (LIM_IS_AP_ROLE(session_entry)) { + for (i = 0; i < mac_ctx->lim.maxStation; i++) { + psta = dph_node_array_ptr + i; + if (!(psta && psta->added)) + continue; + if (session_entry->lim_non_ecsa_cap_num == 0) + lim_send_extended_chan_switch_action_frame + (mac_ctx, psta->staAddr, switch_mode, + op_class, new_channel, switch_count, + session_entry); + else + lim_send_channel_switch_mgmt_frame + (mac_ctx, psta->staAddr, switch_mode, + new_channel, switch_count, + session_entry); + } + } else if (LIM_IS_STA_ROLE(session_entry)) { + lim_send_extended_chan_switch_action_frame + (mac_ctx, session_entry->bssId, switch_mode, op_class, + new_channel, switch_count, session_entry); + } +} + +/** + * lim_process_sme_dfs_csa_ie_request() - process sme dfs csa ie req + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: pointer to the SME message buffer + * + * This function processes SME request messages from HDD or upper layer + * application. + * + * Return: None + */ +static void lim_process_sme_dfs_csa_ie_request(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + tpSirDfsCsaIeRequest dfs_csa_ie_req; + struct pe_session *session_entry = NULL; + uint8_t session_id; + tLimWiderBWChannelSwitchInfo *wider_bw_ch_switch; + QDF_STATUS status; + enum phy_ch_width ch_width; + uint32_t target_ch_freq; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + dfs_csa_ie_req = (tSirDfsCsaIeRequest *)msg_buf; + session_entry = pe_find_session_by_bssid(mac_ctx, + dfs_csa_ie_req->bssid, &session_id); + if (!session_entry) { + pe_err("Session not found for given BSSID" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(dfs_csa_ie_req->bssid)); + return; + } + + if (session_entry->valid && !LIM_IS_AP_ROLE(session_entry)) { + pe_err("Invalid SystemRole %d", + GET_LIM_SYSTEM_ROLE(session_entry)); + return; + } + + /* target channel */ + session_entry->gLimChannelSwitch.primaryChannel = + wlan_reg_freq_to_chan(mac_ctx->pdev, + dfs_csa_ie_req->target_chan_freq); + session_entry->gLimChannelSwitch.sw_target_freq = + dfs_csa_ie_req->target_chan_freq; + target_ch_freq = dfs_csa_ie_req->target_chan_freq; + /* Channel switch announcement needs to be included in beacon */ + session_entry->dfsIncludeChanSwIe = true; + session_entry->gLimChannelSwitch.switchCount = + dfs_csa_ie_req->ch_switch_beacon_cnt; + ch_width = dfs_csa_ie_req->ch_params.ch_width; + if (ch_width >= CH_WIDTH_160MHZ && + wma_get_vht_ch_width() < WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) { + ch_width = CH_WIDTH_80MHZ; + } + session_entry->gLimChannelSwitch.ch_width = ch_width; + session_entry->gLimChannelSwitch.sec_ch_offset = + dfs_csa_ie_req->ch_params.sec_ch_offset; + if (mac_ctx->sap.SapDfsInfo.disable_dfs_ch_switch == false) + session_entry->gLimChannelSwitch.switchMode = + dfs_csa_ie_req->ch_switch_mode; + + /* + * Validate if SAP is operating HT or VHT/HE mode and set the Channel + * Switch Wrapper element with the Wide Band Switch subelement. + */ + if (!(session_entry->vhtCapability || + lim_is_session_he_capable(session_entry))) + goto skip_vht; + + /* Now encode the Wider Ch BW element depending on the ch width */ + wider_bw_ch_switch = &session_entry->gLimWiderBWChannelSwitch; + switch (ch_width) { + case CH_WIDTH_20MHZ: + /* + * Wide channel BW sublement in channel wrapper element is not + * required in case of 20 Mhz operation. Currently It is set + * only set in case of 40/80 Mhz Operation. + */ + session_entry->dfsIncludeChanWrapperIe = false; + wider_bw_ch_switch->newChanWidth = + WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ; + break; + case CH_WIDTH_40MHZ: + session_entry->dfsIncludeChanWrapperIe = false; + wider_bw_ch_switch->newChanWidth = + WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ; + break; + case CH_WIDTH_80MHZ: + session_entry->dfsIncludeChanWrapperIe = true; + wider_bw_ch_switch->newChanWidth = + WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ; + break; + case CH_WIDTH_160MHZ: + session_entry->dfsIncludeChanWrapperIe = true; + wider_bw_ch_switch->newChanWidth = + WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ; + break; + case CH_WIDTH_80P80MHZ: + session_entry->dfsIncludeChanWrapperIe = true; + wider_bw_ch_switch->newChanWidth = + WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ; + /* + * This is not applicable for 20/40/80 Mhz. + * Only used when we support 80+80 Mhz operation. + * In case of 80+80 Mhz, this parameter indicates + * center channel frequency index of 80 Mhz channel of + * frequency segment 1. + */ + wider_bw_ch_switch->newCenterChanFreq1 = + dfs_csa_ie_req->ch_params.center_freq_seg1; + break; + default: + session_entry->dfsIncludeChanWrapperIe = false; + /* + * Need to handle 80+80 Mhz Scenario. When 80+80 is supported + * set the gLimWiderBWChannelSwitch.newChanWidth to 3 + */ + pe_err("Invalid Channel Width"); + break; + } + /* Fetch the center channel based on the channel width */ + wider_bw_ch_switch->newCenterChanFreq0 = + dfs_csa_ie_req->ch_params.center_freq_seg0; +skip_vht: + + /* Take a wakelock for CSA for 5 seconds and release in vdev start */ + + qdf_wake_lock_timeout_acquire(&session_entry->ap_ecsa_wakelock, + MAX_WAKELOCK_FOR_CSA); + qdf_runtime_pm_prevent_suspend(&session_entry->ap_ecsa_runtime_lock); + + /* Send CSA IE request from here */ + lim_send_dfs_chan_sw_ie_update(mac_ctx, session_entry); + + /* + * Wait for MAX_WAIT_FOR_BCN_TX_COMPLETE ms for tx complete for beacon. + * If tx complete for beacon is received before this timer expire, + * stop this timer and then this will be restarted for every beacon + * interval until switchCount become 0 and bcn template with new + * switchCount will be sent to firmware. + * OR + * If no tx complete for beacon is recived till this timer expire + * this will be restarted for every beacon interval until switchCount + * become 0 and bcn template with new switchCount will be sent to + * firmware. + */ + status = qdf_mc_timer_start(&session_entry->ap_ecsa_timer, + MAX_WAIT_FOR_BCN_TX_COMPLETE); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("cannot start ap_ecsa_timer"); + + pe_debug("IE count:%d chan:%d freq %d width:%d wrapper:%d ch_offset:%d", + session_entry->gLimChannelSwitch.switchCount, + session_entry->gLimChannelSwitch.primaryChannel, + session_entry->gLimChannelSwitch.sw_target_freq, + session_entry->gLimChannelSwitch.ch_width, + session_entry->dfsIncludeChanWrapperIe, + session_entry->gLimChannelSwitch.sec_ch_offset); + + /* Send ECSA/CSA Action frame after updating the beacon */ + if (CHAN_HOP_ALL_BANDS_ENABLE && + !WLAN_REG_IS_6GHZ_CHAN_FREQ(target_ch_freq)) + lim_send_chan_switch_action_frame + (mac_ctx, + session_entry->gLimChannelSwitch.primaryChannel, + ch_width, session_entry); + else + send_extended_chan_switch_action_frame + (mac_ctx, target_ch_freq, ch_width, + session_entry); +} + +/** + * lim_process_ext_change_channel()- function to send ECSA + * action frame for STA/CLI . + * @mac_ctx: pointer to global mac structure + * @msg: params from sme for new channel. + * + * This function is called to send ECSA frame for STA/CLI. + * + * Return: void + */ + +static void lim_process_ext_change_channel(struct mac_context *mac_ctx, + uint32_t *msg) +{ + struct sir_sme_ext_cng_chan_req *ext_chng_channel = + (struct sir_sme_ext_cng_chan_req *) msg; + struct pe_session *session_entry = NULL; + uint32_t new_ext_chan_freq; + + if (!msg) { + pe_err("Buffer is Pointing to NULL"); + return; + } + session_entry = + pe_find_session_by_vdev_id(mac_ctx, ext_chng_channel->vdev_id); + if (!session_entry) { + pe_err("Session not found for given vdev_id %d", + ext_chng_channel->vdev_id); + return; + } + if (LIM_IS_AP_ROLE(session_entry)) { + pe_err("not an STA/CLI session"); + return; + } + new_ext_chan_freq = + wlan_reg_legacy_chan_to_freq(mac_ctx->pdev, + ext_chng_channel->new_channel); + session_entry->gLimChannelSwitch.sec_ch_offset = 0; + send_extended_chan_switch_action_frame(mac_ctx, new_ext_chan_freq, 0, + session_entry); +} + +/** + * lim_nss_update_rsp() - send NSS update response to SME + * @mac_ctx Pointer to Global MAC structure + * @vdev_id: vdev id + * @status: nss update status + * + * Return: None + */ +static void lim_nss_update_rsp(struct mac_context *mac_ctx, + uint8_t vdev_id, QDF_STATUS status) +{ + struct scheduler_msg msg = {0}; + struct sir_bcn_update_rsp *nss_rsp; + QDF_STATUS qdf_status; + + nss_rsp = qdf_mem_malloc(sizeof(*nss_rsp)); + if (!nss_rsp) { + pe_err("AllocateMemory failed for nss_rsp"); + return; + } + + nss_rsp->vdev_id = vdev_id; + nss_rsp->status = status; + nss_rsp->reason = REASON_NSS_UPDATE; + + msg.type = eWNI_SME_NSS_UPDATE_RSP; + msg.bodyptr = nss_rsp; + msg.bodyval = 0; + qdf_status = scheduler_post_message(QDF_MODULE_ID_PE, QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &msg); + if (QDF_IS_STATUS_ERROR(qdf_status)) + qdf_mem_free(nss_rsp); +} + +void lim_send_bcn_rsp(struct mac_context *mac_ctx, tpSendbeaconParams rsp) +{ + if (!rsp) { + pe_err("rsp is NULL"); + return; + } + + pe_debug("Send beacon resp status %d for reason %d", + rsp->status, rsp->reason); + + if (rsp->reason == REASON_NSS_UPDATE) + lim_nss_update_rsp(mac_ctx, rsp->vdev_id, rsp->status); +} + +/** + * lim_process_nss_update_request() - process sme nss update req + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: pointer to the SME message buffer + * + * This function processes SME request messages from HDD or upper layer + * application. + * + * Return: None + */ +static void lim_process_nss_update_request(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct sir_nss_update_request *nss_update_req_ptr; + struct pe_session *session_entry = NULL; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint8_t vdev_id; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + nss_update_req_ptr = (struct sir_nss_update_request *)msg_buf; + vdev_id = nss_update_req_ptr->vdev_id; + session_entry = pe_find_session_by_vdev_id(mac_ctx, + nss_update_req_ptr->vdev_id); + if (!session_entry) { + pe_err("Session not found for given session_id %d", + nss_update_req_ptr->vdev_id); + goto end; + } + + if (session_entry->valid && !LIM_IS_AP_ROLE(session_entry)) { + pe_err("Invalid SystemRole %d", + GET_LIM_SYSTEM_ROLE(session_entry)); + goto end; + } + + /* populate nss field in the beacon */ + session_entry->gLimOperatingMode.present = 1; + session_entry->gLimOperatingMode.rxNSS = nss_update_req_ptr->new_nss; + session_entry->gLimOperatingMode.chanWidth = session_entry->ch_width; + + if ((nss_update_req_ptr->new_nss == NSS_1x1_MODE) && + (session_entry->ch_width > CH_WIDTH_80MHZ)) + session_entry->gLimOperatingMode.chanWidth = CH_WIDTH_80MHZ; + if (session_entry->gLimOperatingMode.chanWidth <= CH_WIDTH_160MHZ && + nss_update_req_ptr->ch_width < + session_entry->gLimOperatingMode.chanWidth) + session_entry->gLimOperatingMode.chanWidth = + nss_update_req_ptr->ch_width; + + pe_debug("ch width %d Rx NSS %d", + session_entry->gLimOperatingMode.chanWidth, + session_entry->gLimOperatingMode.rxNSS); + + /* Send nss update request from here */ + status = sch_set_fixed_beacon_fields(mac_ctx, session_entry); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Unable to set op mode IE in beacon"); + goto end; + } + + status = lim_send_beacon_ind(mac_ctx, session_entry, REASON_NSS_UPDATE); + if (QDF_IS_STATUS_SUCCESS(status)) + return; + + pe_err("Unable to send beacon"); +end: + /* + * send resp only in case of failure, + * success case response will be from wma. + */ + lim_nss_update_rsp(mac_ctx, vdev_id, status); +} + +/** + * lim_process_set_ie_req() - process sme set IE request + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: pointer to the SME message buffer + * + * This function processes SME request messages from HDD or upper layer + * application. + * + * Return: None + */ +static void lim_process_set_ie_req(struct mac_context *mac_ctx, uint32_t *msg_buf) +{ + struct send_extcap_ie *msg; + QDF_STATUS status; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + msg = (struct send_extcap_ie *)msg_buf; + status = lim_send_ext_cap_ie(mac_ctx, msg->session_id, NULL, false); + if (QDF_STATUS_SUCCESS != status) + pe_err("Unable to send ExtCap to FW"); + +} + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR + +/** + * obss_color_collision_process_color_disable() - Disable bss color + * @mac_ctx: Pointer to Global MAC structure + * @session: pointer to session + * + * This function will disbale bss color. + * + * Return: None + */ +static void obss_color_collision_process_color_disable(struct mac_context *mac_ctx, + struct pe_session *session) +{ + tUpdateBeaconParams beacon_params; + + if (!session) { + pe_err("Invalid session"); + return; + } + + if (session->valid && !LIM_IS_AP_ROLE(session)) { + pe_err("Invalid SystemRole %d", + GET_LIM_SYSTEM_ROLE(session)); + return; + } + + if (session->bss_color_changing == 1) { + pe_warn("%d: color change in progress", session->smeSessionId); + /* Continue color collision detection */ + lim_send_obss_color_collision_cfg(mac_ctx, session, + OBSS_COLOR_COLLISION_DETECTION); + return; + } + + if (session->he_op.bss_col_disabled == 1) { + pe_warn("%d: bss color already disabled", + session->smeSessionId); + /* Continue free color detection */ + lim_send_obss_color_collision_cfg(mac_ctx, session, + OBSS_COLOR_FREE_SLOT_AVAILABLE); + return; + } + + qdf_mem_zero(&beacon_params, sizeof(beacon_params)); + beacon_params.paramChangeBitmap |= PARAM_BSS_COLOR_CHANGED; + session->he_op.bss_col_disabled = 1; + beacon_params.bss_color_disabled = 1; + beacon_params.bss_color = session->he_op.bss_color; + + if (sch_set_fixed_beacon_fields(mac_ctx, session) != + QDF_STATUS_SUCCESS) { + pe_err("Unable to set op mode IE in beacon"); + return; + } + + lim_send_beacon_params(mac_ctx, &beacon_params, session); + lim_send_obss_color_collision_cfg(mac_ctx, session, + OBSS_COLOR_FREE_SLOT_AVAILABLE); +} + +/** + * obss_color_collision_process_color_change() - Process bss color change + * @mac_ctx: Pointer to Global MAC structure + * @session: pointer to session + * @obss_color_info: obss color collision/free slot indication info + * + * This function selects new color ib case of bss color collision. + * + * Return: None + */ +static void obss_color_collision_process_color_change(struct mac_context *mac_ctx, + struct pe_session *session, + struct wmi_obss_color_collision_info *obss_color_info) +{ + int i, num_bss_color = 0; + uint32_t bss_color_bitmap; + uint8_t bss_color_index_array[MAX_BSS_COLOR_VALUE]; + uint32_t rand_byte = 0; + struct sir_set_he_bss_color he_bss_color; + bool is_color_collision = false; + + + if (session->bss_color_changing == 1) { + pe_err("%d: color change in progress", session->smeSessionId); + return; + } + + if (!session->he_op.bss_col_disabled) { + if (session->he_op.bss_color < 32) + is_color_collision = (obss_color_info-> + obss_color_bitmap_bit0to31 >> + session->he_op.bss_color) & 0x01; + else + is_color_collision = (obss_color_info-> + obss_color_bitmap_bit32to63 >> + (session->he_op.bss_color - + 32)) & 0x01; + if (!is_color_collision) { + pe_err("%d: color collision not found, curr_color: %d", + session->smeSessionId, + session->he_op.bss_color); + return; + } + } + + bss_color_bitmap = obss_color_info->obss_color_bitmap_bit0to31; + + /* Skip color zero */ + bss_color_bitmap = bss_color_bitmap >> 1; + for (i = 0; (i < 31) && (num_bss_color < MAX_BSS_COLOR_VALUE); i++) { + if (!(bss_color_bitmap & 0x01)) { + bss_color_index_array[num_bss_color] = i + 1; + num_bss_color++; + } + bss_color_bitmap = bss_color_bitmap >> 1; + } + + bss_color_bitmap = obss_color_info->obss_color_bitmap_bit32to63; + for (i = 0; (i < 32) && (num_bss_color < MAX_BSS_COLOR_VALUE); i++) { + if (!(bss_color_bitmap & 0x01)) { + bss_color_index_array[num_bss_color] = i + 32; + num_bss_color++; + } + bss_color_bitmap = bss_color_bitmap >> 1; + } + + if (num_bss_color) { + qdf_get_random_bytes((void *) &rand_byte, 1); + i = (rand_byte + qdf_mc_timer_get_system_ticks()) % + num_bss_color; + pe_debug("New bss color = %d", bss_color_index_array[i]); + he_bss_color.vdev_id = obss_color_info->vdev_id; + he_bss_color.bss_color = bss_color_index_array[i]; + lim_process_set_he_bss_color(mac_ctx, + (uint32_t *)&he_bss_color); + } else { + pe_err("Unable to find bss color from bitmasp"); + if (obss_color_info->evt_type == + OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY && + session->obss_color_collision_dec_evt == + OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY) + /* In dot11BSSColorCollisionAPPeriod and + * timer expired, time to disable bss color. + */ + obss_color_collision_process_color_disable(mac_ctx, + session); + else + /* + * Enter dot11BSSColorCollisionAPPeriod period. + */ + lim_send_obss_color_collision_cfg(mac_ctx, session, + OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY); + } +} + +void lim_process_set_he_bss_color(struct mac_context *mac_ctx, uint32_t *msg_buf) +{ + struct sir_set_he_bss_color *bss_color; + struct pe_session *session_entry = NULL; + tUpdateBeaconParams beacon_params; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + bss_color = (struct sir_set_he_bss_color *)msg_buf; + session_entry = pe_find_session_by_vdev_id(mac_ctx, bss_color->vdev_id); + if (!session_entry) { + pe_err("Session not found for given vdev_id %d", + bss_color->vdev_id); + return; + } + + if (session_entry->valid && !LIM_IS_AP_ROLE(session_entry)) { + pe_err("Invalid SystemRole %d", + GET_LIM_SYSTEM_ROLE(session_entry)); + return; + } + + if (bss_color->bss_color == session_entry->he_op.bss_color) { + pe_err("No change in BSS color, current BSS color %d", + bss_color->bss_color); + return; + } + qdf_mem_zero(&beacon_params, sizeof(beacon_params)); + beacon_params.paramChangeBitmap |= PARAM_BSS_COLOR_CHANGED; + session_entry->he_op.bss_col_disabled = 1; + session_entry->he_bss_color_change.countdown = + BSS_COLOR_SWITCH_COUNTDOWN; + session_entry->he_bss_color_change.new_color = bss_color->bss_color; + beacon_params.bss_color_disabled = 1; + beacon_params.bss_color = session_entry->he_op.bss_color; + session_entry->bss_color_changing = 1; + + if (sch_set_fixed_beacon_fields(mac_ctx, session_entry) != + QDF_STATUS_SUCCESS) { + pe_err("Unable to set op mode IE in beacon"); + return; + } + + lim_send_beacon_params(mac_ctx, &beacon_params, session_entry); + lim_send_obss_color_collision_cfg(mac_ctx, session_entry, + OBSS_COLOR_COLLISION_DETECTION_DISABLE); +} + +void lim_send_obss_color_collision_cfg(struct mac_context *mac_ctx, + struct pe_session *session, + enum wmi_obss_color_collision_evt_type + event_type) +{ + struct wmi_obss_color_collision_cfg_param *cfg_param; + struct scheduler_msg msg = {0}; + + if (!session) { + pe_err("Invalid session"); + return; + } + + if (!session->he_capable || + !session->is_session_obss_color_collision_det_enabled) { + pe_debug("%d: obss color det not enabled, he_cap:%d, sup:%d:%d", + session->smeSessionId, session->he_capable, + session->is_session_obss_color_collision_det_enabled, + mac_ctx->mlme_cfg->obss_ht40. + obss_color_collision_offload_enabled); + return; + } + + cfg_param = qdf_mem_malloc(sizeof(*cfg_param)); + if (!cfg_param) + return; + + pe_debug("%d: sending event:%d", session->smeSessionId, event_type); + qdf_mem_zero(cfg_param, sizeof(*cfg_param)); + cfg_param->vdev_id = session->smeSessionId; + cfg_param->evt_type = event_type; + if (LIM_IS_AP_ROLE(session)) + cfg_param->detection_period_ms = + OBSS_COLOR_COLLISION_DETECTION_AP_PERIOD_MS; + else + cfg_param->detection_period_ms = + OBSS_COLOR_COLLISION_DETECTION_STA_PERIOD_MS; + + cfg_param->scan_period_ms = OBSS_COLOR_COLLISION_SCAN_PERIOD_MS; + if (event_type == OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY) + cfg_param->free_slot_expiry_time_ms = + OBSS_COLOR_COLLISION_FREE_SLOT_EXPIRY_MS; + + msg.type = WMA_OBSS_COLOR_COLLISION_REQ; + msg.bodyptr = cfg_param; + msg.reserved = 0; + + if (QDF_IS_STATUS_ERROR(scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg))) { + qdf_mem_free(cfg_param); + } else { + session->obss_color_collision_dec_evt = event_type; + } +} + +void lim_process_obss_color_collision_info(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{ + struct wmi_obss_color_collision_info *obss_color_info; + struct pe_session *session; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + obss_color_info = (struct wmi_obss_color_collision_info *)msg_buf; + session = pe_find_session_by_vdev_id(mac_ctx, obss_color_info->vdev_id); + if (!session) { + pe_err("Session not found for given session_id %d", + obss_color_info->vdev_id); + return; + } + + pe_debug("vdev_id:%d, evt:%d:%d, 0to31:0x%x, 32to63:0x%x, cap:%d:%d:%d", + obss_color_info->vdev_id, + obss_color_info->evt_type, + session->obss_color_collision_dec_evt, + obss_color_info->obss_color_bitmap_bit0to31, + obss_color_info->obss_color_bitmap_bit32to63, + session->he_capable, + session->is_session_obss_color_collision_det_enabled, + mac_ctx->mlme_cfg->obss_ht40. + obss_color_collision_offload_enabled); + + if (!session->he_capable || + !session->is_session_obss_color_collision_det_enabled) { + return; + } + + switch (obss_color_info->evt_type) { + case OBSS_COLOR_COLLISION_DETECTION_DISABLE: + pe_err("%d: FW disabled obss color det. he_cap:%d, sup:%d:%d", + session->smeSessionId, session->he_capable, + session->is_session_obss_color_collision_det_enabled, + mac_ctx->mlme_cfg->obss_ht40. + obss_color_collision_offload_enabled); + session->is_session_obss_color_collision_det_enabled = false; + return; + case OBSS_COLOR_FREE_SLOT_AVAILABLE: + case OBSS_COLOR_COLLISION_DETECTION: + case OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY: + if (session->valid && !LIM_IS_AP_ROLE(session)) { + pe_debug("Invalid System Role %d", + GET_LIM_SYSTEM_ROLE(session)); + return; + } + + if (session->obss_color_collision_dec_evt != + obss_color_info->evt_type) { + pe_debug("%d: Wrong event: %d, skiping", + obss_color_info->vdev_id, + obss_color_info->evt_type); + return; + } + obss_color_collision_process_color_change(mac_ctx, session, + obss_color_info); + break; + default: + pe_err("%d: Invalid event type %d", + obss_color_info->vdev_id, obss_color_info->evt_type); + return; + } +} +#endif + +void lim_send_csa_restart_req(struct mac_context *mac_ctx, uint8_t vdev_id) +{ + struct pe_session *session; + + session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session) { + pe_err("session not found for vdev id %d", vdev_id); + return; + } + + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_CSA_RESTART, + sizeof(*session), session); +} + +void lim_continue_sta_csa_req(struct mac_context *mac_ctx, uint8_t vdev_id) +{ + pe_info("Continue CSA for STA vdev id %d", vdev_id); + lim_process_channel_switch_timeout(mac_ctx); +} + +void lim_add_roam_blacklist_ap(struct mac_context *mac_ctx, + struct roam_blacklist_event *src_lst) +{ + uint32_t i; + struct sir_rssi_disallow_lst entry; + struct roam_blacklist_timeout *blacklist; + + pe_debug("Received Blacklist event from FW num entries %d", + src_lst->num_entries); + blacklist = &src_lst->roam_blacklist[0]; + for (i = 0; i < src_lst->num_entries; i++) { + + entry.bssid = blacklist->bssid; + entry.time_during_rejection = blacklist->received_time; + entry.reject_reason = blacklist->reject_reason; + entry.source = blacklist->source ? blacklist->source : + ADDED_BY_TARGET; + entry.original_timeout = blacklist->original_timeout; + entry.received_time = blacklist->received_time; + /* If timeout = 0 and rssi = 0 ignore the entry */ + if (!blacklist->timeout && !blacklist->rssi) { + continue; + } else if (blacklist->timeout) { + entry.retry_delay = blacklist->timeout; + /* set 0dbm as expected rssi */ + entry.expected_rssi = LIM_MIN_RSSI; + } else { + /* blacklist timeout as 0 */ + entry.retry_delay = blacklist->timeout; + entry.expected_rssi = blacklist->rssi; + } + + /* Add this bssid to the rssi reject ap type in blacklist mgr */ + lim_add_bssid_to_reject_list(mac_ctx->pdev, &entry); + blacklist++; + } +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_tdls.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_tdls.c new file mode 100644 index 0000000000000000000000000000000000000000..84b44b217c06798f61d4be86b92a834ef2aee330 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_tdls.c @@ -0,0 +1,3218 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*=========================================================================== + * lim_process_tdls.c + * OVERVIEW: + * + * DEPENDENCIES: + * + * Are listed for each API below. + * ===========================================================================*/ + +/*=========================================================================== + + * EDIT HISTORY FOR FILE + + * This section contains comments describing changes made to the module. + * Notice that changes are listed in reverse chronological order. + + * $Header$$DateTime$$Author$ + + * when who what, where, why + * ---------- --- ------------------------------------------------------ + * 05/05/2010 Ashwani Initial Creation, added TDLS action frame + * functionality,TDLS message exchange with SME..etc.. + + ===========================================================================*/ + +/** + * \file lim_process_tdls.c + * + * \brief Code for preparing,processing and sending 802.11z action frames + * + */ + +#ifdef FEATURE_WLAN_TDLS + +#include "sir_api.h" +#include "ani_global.h" +#include "sir_mac_prot_def.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_security_utils.h" +#include "dot11f.h" +#include "sch_api.h" +#include "lim_send_messages.h" +#include "utils_parser.h" +#include "lim_assoc_utils.h" +#include "lim_prop_exts_utils.h" +#include "dph_hash_table.h" +#include "wma_types.h" +#include "cds_regdomain.h" +#include "cds_utils.h" +#include "wlan_reg_services_api.h" +#include "wlan_tdls_tgt_api.h" +#include "wlan_mlme_public_struct.h" +#include "wlan_mlme_api.h" +#include "wlan_tdls_public_structs.h" + +/* define NO_PAD_TDLS_MIN_8023_SIZE to NOT padding: See CR#447630 + There was IOT issue with cisco 1252 open mode, where it pads + discovery req/teardown frame with some junk value up to min size. + To avoid this issue, we pad QCOM_VENDOR_IE. + If there is other IOT issue because of this bandage, define NO_PAD... + */ +#ifndef NO_PAD_TDLS_MIN_8023_SIZE +#define MIN_IEEE_8023_SIZE 46 +#define MIN_VENDOR_SPECIFIC_IE_SIZE 5 +#endif + +/* + * TDLS data frames will go out/come in as non-qos data. + * so, eth_890d_header will be aligned access.. + */ +static const uint8_t eth_890d_header[] = { + 0xaa, 0xaa, 0x03, 0x00, + 0x00, 0x00, 0x89, 0x0d, +}; + +/* + * type of links used in TDLS + */ +enum tdlsLinks { + TDLS_LINK_AP, + TDLS_LINK_DIRECT +} e_tdls_link; + +enum tdlsReqType { + TDLS_INITIATOR, + TDLS_RESPONDER +} e_tdls_req_type; + +typedef enum tdlsLinkSetupStatus { + TDLS_SETUP_STATUS_SUCCESS = 0, + TDLS_SETUP_STATUS_FAILURE = 37 +} etdlsLinkSetupStatus; + +/* These maps to Kernel TDLS peer capability + * flags and should get changed as and when necessary + */ +enum tdls_peer_capability { + TDLS_PEER_HT_CAP = 0, + TDLS_PEER_VHT_CAP = 1, + TDLS_PEER_WMM_CAP = 2 +} e_tdls_peer_capability; + +#define LINK_IDEN_ADDR_OFFSET(x) (&x.LinkIdentifier) + +/* TODO, Move this parameters to configuration */ +#define PEER_PSM_SUPPORT (0) +#define TDLS_SUPPORT (1) +#define TDLS_PROHIBITED (0) +#define TDLS_CH_SWITCH_PROHIBITED (1) +/** @brief Set bit manipulation macro */ +#define SET_BIT(value, mask) ((value) |= (1 << (mask))) +/** @brief Clear bit manipulation macro */ +#define CLEAR_BIT(value, mask) ((value) &= ~(1 << (mask))) +/** @brief Check bit manipulation macro */ +#define CHECK_BIT(value, mask) ((value) & (1 << (mask))) + +#define SET_PEER_AID_BITMAP(peer_bitmap, aid) \ + do { \ + if ((aid) < (sizeof(uint32_t) << 3)) \ + SET_BIT(peer_bitmap[0], (aid)); \ + else if ((aid) < (sizeof(uint32_t) << 4)) \ + SET_BIT(peer_bitmap[1], ((aid) - (sizeof(uint32_t) << 3)));\ + } while (0); + +#define CLEAR_PEER_AID_BITMAP(peer_bitmap, aid) \ + do { \ + if ((aid) < (sizeof(uint32_t) << 3)) \ + CLEAR_BIT(peer_bitmap[0], (aid)); \ + else if ((aid) < (sizeof(uint32_t) << 4)) \ + CLEAR_BIT(peer_bitmap[1], ((aid) - (sizeof(uint32_t) << 3)));\ + } while (0); + +#define IS_QOS_ENABLED(pe_session) ((((pe_session)->limQosEnabled) && \ + SIR_MAC_GET_QOS((pe_session)->limCurrentBssCaps)) || \ + (((pe_session)->limWmeEnabled) && \ + LIM_BSS_CAPS_GET(WME, (pe_session)->limCurrentBssQosCaps))) + +#define TID_AC_VI 4 +#define TID_AC_BK 1 + +static const uint8_t *lim_trace_tdls_action_string(uint8_t tdlsActionCode) +{ + switch (tdlsActionCode) { + CASE_RETURN_STRING(TDLS_SETUP_REQUEST); + CASE_RETURN_STRING(TDLS_SETUP_RESPONSE); + CASE_RETURN_STRING(TDLS_SETUP_CONFIRM); + CASE_RETURN_STRING(TDLS_TEARDOWN); + CASE_RETURN_STRING(TDLS_PEER_TRAFFIC_INDICATION); + CASE_RETURN_STRING(TDLS_CHANNEL_SWITCH_REQUEST); + CASE_RETURN_STRING(TDLS_CHANNEL_SWITCH_RESPONSE); + CASE_RETURN_STRING(TDLS_PEER_TRAFFIC_RESPONSE); + CASE_RETURN_STRING(TDLS_DISCOVERY_REQUEST); + CASE_RETURN_STRING(TDLS_DISCOVERY_RESPONSE); + } + return (const uint8_t *)"UNKNOWN"; +} + +/* + * initialize TDLS setup list and related data structures. + */ +void lim_init_tdls_data(struct mac_context *mac, struct pe_session *pe_session) +{ + lim_init_peer_idxpool(mac, pe_session); + + return; +} + +static void populate_dot11f_tdls_offchannel_params( + struct mac_context *mac, + struct pe_session *pe_session, + tDot11fIESuppChannels *suppChannels, + tDot11fIESuppOperatingClasses *suppOperClasses) +{ + uint32_t numChans = CFG_VALID_CHANNEL_LIST_LEN; + uint8_t validChan[CFG_VALID_CHANNEL_LIST_LEN]; + uint8_t i; + uint8_t valid_count = 0; + uint8_t chanOffset; + uint8_t op_class; + uint8_t numClasses; + uint8_t classes[REG_MAX_SUPP_OPER_CLASSES]; + uint32_t band; + uint8_t nss_2g; + uint8_t nss_5g; + qdf_freq_t ch_freq; + + numChans = mac->mlme_cfg->reg.valid_channel_list_num; + + for (i = 0; i < mac->mlme_cfg->reg.valid_channel_list_num; i++) { + validChan[i] = wlan_reg_freq_to_chan(mac->pdev, + mac->mlme_cfg->reg.valid_channel_freq_list[i]); + } + + if (wlan_reg_is_5ghz_ch_freq(pe_session->curr_op_freq)) + band = BAND_5G; + else + band = BAND_2G; + + nss_5g = QDF_MIN(mac->vdev_type_nss_5g.tdls, + mac->user_configured_nss); + nss_2g = QDF_MIN(mac->vdev_type_nss_2g.tdls, + mac->user_configured_nss); + + /* validating the channel list for DFS and 2G channels */ + for (i = 0U; i < numChans; i++) { + ch_freq = wlan_reg_legacy_chan_to_freq(mac->pdev, validChan[i]); + if ((band == BAND_5G) && + (NSS_2x2_MODE == nss_5g) && + (NSS_1x1_MODE == nss_2g) && + (wlan_reg_is_dfs_for_freq(mac->pdev, ch_freq))) { + pe_debug("skipping channel: %d, nss_5g: %d, nss_2g: %d", + validChan[i], nss_5g, nss_2g); + continue; + } else { + if (wlan_reg_is_dsrc_chan(mac->pdev, validChan[i])) { + pe_debug("skipping channel: %d from the valid channel list", + validChan[i]); + continue; + } + } + + if (valid_count >= ARRAY_SIZE(suppChannels->bands)) + break; + suppChannels->bands[valid_count][0] = validChan[i]; + suppChannels->bands[valid_count][1] = 1; + valid_count++; + } + + suppChannels->num_bands = valid_count; + suppChannels->present = 1; + + /* find channel offset and get op class for current operating channel */ + switch (pe_session->htSecondaryChannelOffset) { + case PHY_SINGLE_CHANNEL_CENTERED: + chanOffset = BW20; + break; + + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + chanOffset = BW40_LOW_PRIMARY; + break; + + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + chanOffset = BW40_HIGH_PRIMARY; + break; + + default: + chanOffset = BWALL; + break; + } + + op_class = wlan_reg_dmn_get_opclass_from_channel( + mac->scan.countryCodeCurrent, + wlan_reg_freq_to_chan(mac->pdev, pe_session->curr_op_freq), + chanOffset); + + pe_debug("countryCodeCurrent: %s, curr_op_freq: %d, htSecondaryChannelOffset: %d, chanOffset: %d op class: %d", + mac->scan.countryCodeCurrent, + pe_session->curr_op_freq, + pe_session->htSecondaryChannelOffset, + chanOffset, op_class); + suppOperClasses->present = 1; + suppOperClasses->classes[0] = op_class; + + wlan_reg_dmn_get_curr_opclasses(&numClasses, &classes[0]); + + for (i = 0; i < numClasses; i++) + suppOperClasses->classes[i + 1] = classes[i]; + + /* add one for present operating class, added in the beginning */ + suppOperClasses->num_classes = numClasses + 1; + + return; +} + +/* + * FUNCTION: Populate Link Identifier element IE + * + */ + +static void populate_dot11f_link_iden(struct mac_context *mac, + struct pe_session *pe_session, + tDot11fIELinkIdentifier *linkIden, + struct qdf_mac_addr peer_mac, + uint8_t reqType) +{ + uint8_t *initStaAddr = NULL; + uint8_t *respStaAddr = NULL; + + (reqType == TDLS_INITIATOR) ? ((initStaAddr = linkIden->InitStaAddr), + (respStaAddr = linkIden->RespStaAddr)) + : ((respStaAddr = linkIden->InitStaAddr), + (initStaAddr = linkIden->RespStaAddr)); + qdf_mem_copy((uint8_t *) linkIden->bssid, + (uint8_t *) pe_session->bssId, QDF_MAC_ADDR_SIZE); + + qdf_mem_copy((uint8_t *) initStaAddr, + pe_session->self_mac_addr, QDF_MAC_ADDR_SIZE); + + qdf_mem_copy((uint8_t *) respStaAddr, (uint8_t *) peer_mac.bytes, + QDF_MAC_ADDR_SIZE); + + linkIden->present = 1; + return; + +} + +static void populate_dot11f_tdls_ext_capability(struct mac_context *mac, + struct pe_session *pe_session, + tDot11fIEExtCap *extCapability) +{ + struct s_ext_cap *p_ext_cap = (struct s_ext_cap *)extCapability->bytes; + + p_ext_cap->tdls_peer_psm_supp = PEER_PSM_SUPPORT; + p_ext_cap->tdls_peer_uapsd_buffer_sta = mac->lim.gLimTDLSBufStaEnabled; + + /* + * Set TDLS channel switching bits only if offchannel is enabled + * and TDLS Channel Switching is not prohibited by AP in ExtCap + * IE in assoc/re-assoc response. + */ + if ((1 == mac->lim.gLimTDLSOffChannelEnabled) && + (!pe_session->tdls_chan_swit_prohibited)) { + p_ext_cap->tdls_channel_switching = 1; + p_ext_cap->tdls_chan_swit_prohibited = 0; + } else { + p_ext_cap->tdls_channel_switching = 0; + p_ext_cap->tdls_chan_swit_prohibited = TDLS_CH_SWITCH_PROHIBITED; + } + p_ext_cap->tdls_support = TDLS_SUPPORT; + p_ext_cap->tdls_prohibited = TDLS_PROHIBITED; + + extCapability->present = 1; + extCapability->num_bytes = lim_compute_ext_cap_ie_length(extCapability); + + return; +} + +/* + * prepare TDLS frame header, it includes + * | | | | + * |802.11 header|RFC1042 header|TDLS_PYLOAD_TYPE|PAYLOAD + * | | | | + */ +static uint32_t lim_prepare_tdls_frame_header(struct mac_context *mac, uint8_t *pFrame, + tDot11fIELinkIdentifier *link_iden, + uint8_t tdlsLinkType, uint8_t reqType, + uint8_t tid, + struct pe_session *pe_session) +{ + tpSirMacDataHdr3a pMacHdr; + uint32_t header_offset = 0; + uint8_t *addr1 = NULL; + uint8_t *addr3 = NULL; + uint8_t toDs = (tdlsLinkType == TDLS_LINK_AP) + ? ANI_TXDIR_TODS : ANI_TXDIR_IBSS; + uint8_t *peerMac = (reqType == TDLS_INITIATOR) + ? link_iden->RespStaAddr : link_iden->InitStaAddr; + uint8_t *staMac = (reqType == TDLS_INITIATOR) + ? link_iden->InitStaAddr : link_iden->RespStaAddr; + tpDphHashNode sta_ds; + uint16_t aid = 0; + uint8_t qos_mode = 0; + + pMacHdr = (tpSirMacDataHdr3a) (pFrame); + + /* + * if TDLS frame goes through the AP link, it follows normal address + * pattern, if TDLS frame goes thorugh the direct link, then + * A1--> Peer STA addr, A2-->Self STA address, A3--> BSSID + */ + (tdlsLinkType == TDLS_LINK_AP) ? ((addr1 = (link_iden->bssid)), + (addr3 = (peerMac))) + : ((addr1 = (peerMac)), (addr3 = (link_iden->bssid))); + /* + * prepare 802.11 header + */ + pMacHdr->fc.protVer = SIR_MAC_PROTOCOL_VERSION; + pMacHdr->fc.type = SIR_MAC_DATA_FRAME; + + sta_ds = dph_lookup_hash_entry(mac, peerMac, &aid, + &pe_session->dph.dphHashTable); + if (sta_ds) + qos_mode = sta_ds->qosMode; + + pMacHdr->fc.subType = + ((IS_QOS_ENABLED(pe_session) && + (tdlsLinkType == TDLS_LINK_AP)) || + ((tdlsLinkType == TDLS_LINK_DIRECT) && qos_mode)) + ? SIR_MAC_DATA_QOS_DATA : SIR_MAC_DATA_DATA; + + /* + * TL is not setting up below fields, so we are doing it here + */ + pMacHdr->fc.toDS = toDs; + pMacHdr->fc.powerMgmt = 0; + pMacHdr->fc.wep = (pe_session->encryptType == eSIR_ED_NONE) ? 0 : 1; + + qdf_mem_copy((uint8_t *) pMacHdr->addr1, + (uint8_t *) addr1, sizeof(tSirMacAddr)); + qdf_mem_copy((uint8_t *) pMacHdr->addr2, + (uint8_t *) staMac, sizeof(tSirMacAddr)); + + qdf_mem_copy((uint8_t *) pMacHdr->addr3, + (uint8_t *) (addr3), sizeof(tSirMacAddr)); + + pe_debug("Preparing TDLS frame header to %s A1:" + QDF_MAC_ADDR_FMT", A2:"QDF_MAC_ADDR_FMT", A3:" + QDF_MAC_ADDR_FMT, + (tdlsLinkType == TDLS_LINK_AP) ? "AP" : "DIRECT", + QDF_MAC_ADDR_REF(pMacHdr->addr1), + QDF_MAC_ADDR_REF(pMacHdr->addr2), + QDF_MAC_ADDR_REF(pMacHdr->addr3)); + + if (pMacHdr->fc.subType == SIR_MAC_DATA_QOS_DATA) { + pMacHdr->qosControl.tid = tid; + header_offset += sizeof(tSirMacDataHdr3a); + } else + header_offset += sizeof(tSirMacMgmtHdr); + + /* + * Now form RFC1042 header + */ + qdf_mem_copy((uint8_t *) (pFrame + header_offset), + (uint8_t *) eth_890d_header, sizeof(eth_890d_header)); + + header_offset += sizeof(eth_890d_header); + + /* add payload type as TDLS */ + *(pFrame + header_offset) = PAYLOAD_TYPE_TDLS; + header_offset += PAYLOAD_TYPE_TDLS_SIZE; + return header_offset; +} + +/** + * lim_mgmt_tdls_tx_complete - callback to indicate Tx completion + * @context: pointer to mac structure + * @buf: buffer + * @tx_complete: indicates tx success/failure + * @params: tx completion params + * + * function will be invoked on receiving tx completion indication + * + * return: success: eHAL_STATUS_SUCCESS failure: eHAL_STATUS_FAILURE + */ +static QDF_STATUS lim_mgmt_tdls_tx_complete(void *context, + qdf_nbuf_t buf, + uint32_t tx_complete, + void *params) +{ + struct mac_context *mac_ctx = (struct mac_context *)context; + + pe_debug("tdls_frm_session_id: %x tx_complete: %x", + mac_ctx->lim.tdls_frm_session_id, tx_complete); + + if (NO_SESSION != mac_ctx->lim.tdls_frm_session_id) { + lim_send_sme_mgmt_tx_completion(mac_ctx, + mac_ctx->lim.tdls_frm_session_id, + tx_complete); + mac_ctx->lim.tdls_frm_session_id = NO_SESSION; + } + + if (buf) + qdf_nbuf_free(buf); + + return QDF_STATUS_SUCCESS; +} + +/* + * This function can be used for bacst or unicast discovery request + * We are not differentiating it here, it will all depnds on peer MAC address, + */ +static QDF_STATUS lim_send_tdls_dis_req_frame(struct mac_context *mac, + struct qdf_mac_addr peer_mac, + uint8_t dialog, + struct pe_session *pe_session, + enum wifi_traffic_ac ac) +{ + tDot11fTDLSDisReq tdlsDisReq; + uint32_t status = 0; + uint32_t nPayload = 0; + uint32_t size = 0; + uint32_t nBytes = 0; + uint32_t header_offset = 0; + uint8_t *pFrame; + void *pPacket; + QDF_STATUS qdf_status; +#ifndef NO_PAD_TDLS_MIN_8023_SIZE + uint32_t padLen = 0; +#endif + uint8_t smeSessionId = 0; + + if (!pe_session) { + pe_err("pe_session is NULL"); + return QDF_STATUS_E_FAILURE; + } + smeSessionId = pe_session->smeSessionId; + /* + * The scheme here is to fill out a 'tDot11fProbeRequest' structure + * and then hand it off to 'dot11f_pack_probe_request' (for + * serialization). We start by zero-initializing the structure: + */ + qdf_mem_zero((uint8_t *) &tdlsDisReq, sizeof(tDot11fTDLSDisReq)); + + /* + * setup Fixed fields, + */ + tdlsDisReq.Category.category = ACTION_CATEGORY_TDLS; + tdlsDisReq.Action.action = TDLS_DISCOVERY_REQUEST; + tdlsDisReq.DialogToken.token = dialog; + + size = sizeof(tSirMacAddr); + + populate_dot11f_link_iden(mac, pe_session, &tdlsDisReq.LinkIdentifier, + peer_mac, TDLS_INITIATOR); + + /* + * now we pack it. First, how much space are we going to need? + */ + status = dot11f_get_packed_tdls_dis_req_size(mac, &tdlsDisReq, &nPayload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to calculate the packed size for a discovery Request (0x%08x)", + status); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fTDLSDisReq); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size for a discovery Request (0x%08x)", + status); + } + + /* + * This frame is going out from PE as data frames with special ethertype + * 89-0d. + * 8 bytes of RFC 1042 header + */ + + nBytes = nPayload + ((IS_QOS_ENABLED(pe_session)) + ? sizeof(tSirMacDataHdr3a) : + sizeof(tSirMacMgmtHdr)) + + sizeof(eth_890d_header) + + PAYLOAD_TYPE_TDLS_SIZE; + +#ifndef NO_PAD_TDLS_MIN_8023_SIZE + /* IOT issue with some AP : some AP doesn't like the data packet size < minimum 802.3 frame length (64) + Hence AP itself padding some bytes, which caused teardown packet is dropped at + receiver side. To avoid such IOT issue, we added some extra bytes to meet data frame size >= 64 + */ + if (nPayload + PAYLOAD_TYPE_TDLS_SIZE < MIN_IEEE_8023_SIZE) { + padLen = + MIN_IEEE_8023_SIZE - (nPayload + PAYLOAD_TYPE_TDLS_SIZE); + + /* if padLen is less than minimum vendorSpecific (5), pad up to 5 */ + if (padLen < MIN_VENDOR_SPECIFIC_IE_SIZE) + padLen = MIN_VENDOR_SPECIFIC_IE_SIZE; + + nBytes += padLen; + } +#endif + + /* Ok-- try to allocate memory from MGMT PKT pool */ + + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate: %d bytes for a TDLS Discovery Request", + nBytes); + return QDF_STATUS_E_NOMEM; + } + + /* zero out the memory */ + qdf_mem_zero(pFrame, nBytes); + + /* + * IE formation, memory allocation is completed, Now form TDLS discovery + * request frame + */ + + /* fill out the buffer descriptor */ + + header_offset = lim_prepare_tdls_frame_header(mac, pFrame, + LINK_IDEN_ADDR_OFFSET(tdlsDisReq), TDLS_LINK_AP, + TDLS_INITIATOR, + (ac == WIFI_AC_VI) ? TID_AC_VI : TID_AC_BK, + pe_session); + + status = dot11f_pack_tdls_dis_req(mac, &tdlsDisReq, pFrame + + header_offset, nPayload, &nPayload); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a TDLS discovery req (0x%08x)", + status); + cds_packet_free((void *)pPacket); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing TDLS Discovery Request (0x%08x)", + status); + } + +#ifndef NO_PAD_TDLS_MIN_8023_SIZE + if (padLen != 0) { + /* QCOM VENDOR OUI = { 0x00, 0xA0, 0xC6, type = 0x0000 }; */ + uint8_t *padVendorSpecific = pFrame + header_offset + nPayload; + /* make QCOM_VENDOR_OUI, and type = 0x0000, and all the payload to be zero */ + padVendorSpecific[0] = 221; + padVendorSpecific[1] = padLen - 2; + padVendorSpecific[2] = 0x00; + padVendorSpecific[3] = 0xA0; + padVendorSpecific[4] = 0xC6; + + pe_debug("Padding Vendor Specific Ie Len: %d", padLen); + + /* padding zero if more than 5 bytes are required */ + if (padLen > MIN_VENDOR_SPECIFIC_IE_SIZE) + qdf_mem_zero(pFrame + header_offset + nPayload + + MIN_VENDOR_SPECIFIC_IE_SIZE, + padLen - MIN_VENDOR_SPECIFIC_IE_SIZE); + } +#endif + + pe_debug("[TDLS] action: %d (%s) -AP-> OTA peer="QDF_MAC_ADDR_FMT, + TDLS_DISCOVERY_REQUEST, + lim_trace_tdls_action_string(TDLS_DISCOVERY_REQUEST), + QDF_MAC_ADDR_REF(peer_mac.bytes)); + + mac->lim.tdls_frm_session_id = pe_session->smeSessionId; + lim_diag_mgmt_tx_event_report(mac, (tpSirMacMgmtHdr) pFrame, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + qdf_status = wma_tx_frameWithTxComplete(mac, pPacket, + (uint16_t) nBytes, + TXRX_FRM_802_11_DATA, + ANI_TXDIR_TODS, + TID_AC_VI, + lim_tx_complete, pFrame, + lim_mgmt_tdls_tx_complete, + HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME | + HAL_USE_PEER_STA_REQUESTED_MASK, + smeSessionId, false, 0, RATEID_DEFAULT); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + mac->lim.tdls_frm_session_id = NO_SESSION; + pe_err("could not send TDLS Discovery Request frame"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; + +} + +/* + * This static function is consistent with any kind of TDLS management + * frames we are sending. Currently it is being used by lim_send_tdls_dis_rsp_frame, + * lim_send_tdls_link_setup_req_frame and lim_send_tdls_setup_rsp_frame + */ +static void populate_dot11f_tdls_ht_vht_cap(struct mac_context *mac, + uint32_t selfDot11Mode, + tDot11fIEHTCaps *htCap, + tDot11fIEVHTCaps *vhtCap, + struct pe_session *pe_session) +{ + uint8_t nss; + qdf_size_t val_len; + struct mlme_vht_capabilities_info *vht_cap_info; + + vht_cap_info = &mac->mlme_cfg->vht_caps.vht_cap_info; + + if (wlan_reg_is_5ghz_ch_freq(pe_session->curr_op_freq)) + nss = mac->vdev_type_nss_5g.tdls; + else + nss = mac->vdev_type_nss_2g.tdls; + + nss = QDF_MIN(nss, mac->user_configured_nss); + if (IS_DOT11_MODE_HT(selfDot11Mode)) { + /* Include HT Capability IE */ + populate_dot11f_ht_caps(mac, pe_session, htCap); + val_len = SIZE_OF_SUPPORTED_MCS_SET; + wlan_mlme_get_cfg_str(&htCap->supportedMCSSet[0], + &mac->mlme_cfg->rates.supported_mcs_set, + &val_len); + if (NSS_1x1_MODE == nss) + htCap->supportedMCSSet[1] = 0; + /* + * Advertise ht capability and max supported channel bandwidth + * when populating HT IE in TDLS Setup Request/Setup Response/ + * Setup Confirmation frames. + * 11.21.6.2 Setting up a 40 MHz direct link: A 40 MHz + * off-channel direct link may be started if both TDLS peer STAs + * indicated 40 MHz support in the Supported Channel Width Set + * field of the HT Capabilities element (which is included in + * the TDLS Setup Request frame and the TDLS Setup Response + * frame). Switching to a 40 MHz off-channel direct link is + * achieved by including the following information in the TDLS + * Channel Switch Request + * 11.21.1 General: The channel width of the TDLS direct link on + * the base channel shall not exceed the channel width of the + * BSS to which the TDLS peer STAs are associated. + * Select supportedChannelWidthSet based on channel bonding + * settings for each band + */ + } else { + htCap->present = 0; + } + pe_debug("HT present: %hu, Chan Width: %hu", + htCap->present, htCap->supportedChannelWidthSet); + + if ((WLAN_REG_IS_24GHZ_CH_FREQ(pe_session->curr_op_freq) && + vht_cap_info->b24ghz_band) || + WLAN_REG_IS_5GHZ_CH_FREQ(pe_session->curr_op_freq)) { + if (IS_DOT11_MODE_VHT(selfDot11Mode) && + IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) { + /* Include VHT Capability IE */ + populate_dot11f_vht_caps(mac, pe_session, vhtCap); + + /* + * Set to 0 if the TDLS STA does not support either 160 + * or 80+80 MHz. + * Set to 1 if the TDLS STA supports 160 MHz. + * Set to 2 if the TDLS STA supports 160 MHz and + * 80+80 MHz. + * The value 3 is reserved + */ + vhtCap->supportedChannelWidthSet = 0; + + vhtCap->suBeamformeeCap = 0; + vhtCap->suBeamFormerCap = 0; + vhtCap->muBeamformeeCap = 0; + vhtCap->muBeamformerCap = 0; + + vhtCap->rxMCSMap = vht_cap_info->rx_mcs_map; + + vhtCap->rxHighSupDataRate = + vht_cap_info->rx_supp_data_rate; + vhtCap->txMCSMap = vht_cap_info->tx_mcs_map; + vhtCap->txSupDataRate = vht_cap_info->tx_supp_data_rate; + if (nss == NSS_1x1_MODE) { + vhtCap->txMCSMap |= DISABLE_NSS2_MCS; + vhtCap->rxMCSMap |= DISABLE_NSS2_MCS; + vhtCap->txSupDataRate = + VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + vhtCap->rxHighSupDataRate = + VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + } + } else { + vhtCap->present = 0; + } + } else { + /* Vht Disable from ini in 2.4 GHz */ + vhtCap->present = 0; + } + pe_debug("VHT present: %hu, Chan Width: %hu", + vhtCap->present, vhtCap->supportedChannelWidthSet); +} + +/* + * Send TDLS discovery response frame on direct link. + */ + +static QDF_STATUS lim_send_tdls_dis_rsp_frame(struct mac_context *mac, + struct qdf_mac_addr peer_mac, + uint8_t dialog, + struct pe_session *pe_session, + uint8_t *addIe, + uint16_t addIeLen) +{ + tDot11fTDLSDisRsp tdlsDisRsp; + uint16_t caps = 0; + uint32_t status = 0; + uint32_t nPayload = 0; + uint32_t nBytes = 0; + uint8_t *pFrame; + void *pPacket; + QDF_STATUS qdf_status; + uint32_t selfDot11Mode; +/* Placeholder to support different channel bonding mode of TDLS than AP. */ +/* Today, WNI_CFG_CHANNEL_BONDING_MODE will be overwritten when connecting to AP */ +/* To support this feature, we need to introduce WNI_CFG_TDLS_CHANNEL_BONDING_MODE */ +/* As of now, we hardcoded to max channel bonding of dot11Mode (i.e HT80 for 11ac/HT40 for 11n) */ +/* uint32_t tdlsChannelBondingMode; */ + uint8_t smeSessionId = 0; + + if (!pe_session) { + pe_err("pe_session is NULL"); + return QDF_STATUS_E_FAILURE; + } + smeSessionId = pe_session->smeSessionId; + + /* + * The scheme here is to fill out a 'tDot11fProbeRequest' structure + * and then hand it off to 'dot11f_pack_probe_request' (for + * serialization). We start by zero-initializing the structure: + */ + qdf_mem_zero((uint8_t *) &tdlsDisRsp, sizeof(tDot11fTDLSDisRsp)); + + /* + * setup Fixed fields, + */ + tdlsDisRsp.Category.category = ACTION_CATEGORY_PUBLIC; + tdlsDisRsp.Action.action = TDLS_DISCOVERY_RESPONSE; + tdlsDisRsp.DialogToken.token = dialog; + + populate_dot11f_link_iden(mac, pe_session, + &tdlsDisRsp.LinkIdentifier, + peer_mac, TDLS_RESPONDER); + + if (lim_get_capability_info(mac, &caps, pe_session) != + QDF_STATUS_SUCCESS) { + /* + * Could not get Capabilities value + * from CFG. Log error. + */ + pe_err("could not retrieve Capabilities value"); + } + swap_bit_field16(caps, (uint16_t *) &tdlsDisRsp.Capabilities); + + /* populate supported rate and ext supported rate IE */ + if (QDF_STATUS_E_FAILURE == populate_dot11f_rates_tdls(mac, + &tdlsDisRsp.SuppRates, + &tdlsDisRsp.ExtSuppRates, + wlan_reg_freq_to_chan( + mac->pdev, pe_session->curr_op_freq))) + pe_err("could not populate supported data rates"); + + /* populate extended capability IE */ + populate_dot11f_tdls_ext_capability(mac, + pe_session, + &tdlsDisRsp.ExtCap); + + selfDot11Mode = mac->mlme_cfg->dot11_mode.dot11_mode; + + /* Populate HT/VHT Capabilities */ + populate_dot11f_tdls_ht_vht_cap(mac, selfDot11Mode, &tdlsDisRsp.HTCaps, + &tdlsDisRsp.VHTCaps, pe_session); + + /* Populate TDLS offchannel param only if offchannel is enabled + * and TDLS Channel Switching is not prohibited by AP in ExtCap + * IE in assoc/re-assoc response. + */ + if ((1 == mac->lim.gLimTDLSOffChannelEnabled) && + (!pe_session->tdls_chan_swit_prohibited)) { + populate_dot11f_tdls_offchannel_params(mac, pe_session, + &tdlsDisRsp.SuppChannels, + &tdlsDisRsp. + SuppOperatingClasses); + if (mac->mlme_cfg->gen.band_capability != BIT(REG_BAND_2G)) { + tdlsDisRsp.ht2040_bss_coexistence.present = 1; + tdlsDisRsp.ht2040_bss_coexistence.info_request = 1; + } + } else { + pe_debug("TDLS offchan not enabled, or channel switch prohibited by AP, gLimTDLSOffChannelEnabled: %d tdls_chan_swit_prohibited: %d", + mac->lim.gLimTDLSOffChannelEnabled, + pe_session->tdls_chan_swit_prohibited); + } + /* + * now we pack it. First, how much space are we going to need? + */ + status = dot11f_get_packed_tdls_dis_rsp_size(mac, &tdlsDisRsp, &nPayload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to calculate the packed size for a Discovery Response (0x%08x)", + status); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fProbeRequest); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size for a Discovery Response (0x%08x)", + status); + } + + /* + * This frame is going out from PE as data frames with special ethertype + * 89-0d. + * 8 bytes of RFC 1042 header + */ + + nBytes = nPayload + sizeof(tSirMacMgmtHdr) + addIeLen; + + /* Ok-- try to allocate memory from MGMT PKT pool */ + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a TDLS Discovery Request", + nBytes); + return QDF_STATUS_E_NOMEM; + } + + /* zero out the memory */ + qdf_mem_zero(pFrame, nBytes); + + /* + * IE formation, memory allocation is completed, Now form TDLS discovery + * response frame + */ + + /* Make public Action Frame */ + + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer_mac.bytes, + pe_session->self_mac_addr); + + { + tpSirMacMgmtHdr pMacHdr; + + pMacHdr = (tpSirMacMgmtHdr) pFrame; + pMacHdr->fc.toDS = ANI_TXDIR_IBSS; + pMacHdr->fc.powerMgmt = 0; + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + } + + status = dot11f_pack_tdls_dis_rsp(mac, &tdlsDisRsp, pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a TDLS discovery response (0x%08x)", + status); + cds_packet_free((void *)pPacket); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing TDLS Discovery Response (0x%08x)", + status); + } + + if (0 != addIeLen) { + pe_debug("Copy Additional Ie Len: %d", addIeLen); + qdf_mem_copy(pFrame + sizeof(tSirMacMgmtHdr) + nPayload, addIe, + addIeLen); + } + pe_debug("[TDLS] action: %d (%s) -DIRECT-> OTA peer="QDF_MAC_ADDR_FMT, + TDLS_DISCOVERY_RESPONSE, + lim_trace_tdls_action_string(TDLS_DISCOVERY_RESPONSE), + QDF_MAC_ADDR_REF(peer_mac.bytes)); + + mac->lim.tdls_frm_session_id = pe_session->smeSessionId; + lim_diag_mgmt_tx_event_report(mac, (tpSirMacMgmtHdr) pFrame, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + /* + * Transmit Discovery response and watch if this is delivered to + * peer STA. + */ + /* In CLD 2.0, pass Discovery Response as mgmt frame so that + * wma does not do header conversion to 802.3 before calling tx/rx + * routine and subsequenly target also sends frame as is OTA + */ + qdf_status = wma_tx_frameWithTxComplete(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_IBSS, + 0, + lim_tx_complete, pFrame, + lim_mgmt_tdls_tx_complete, + HAL_USE_SELF_STA_REQUESTED_MASK, + smeSessionId, false, 0, + RATEID_DEFAULT); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + mac->lim.tdls_frm_session_id = NO_SESSION; + pe_err("could not send TDLS Discovery Response frame!"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; + +} + +/* + * This static function is currently used by lim_send_tdls_link_setup_req_frame and + * lim_send_tdls_setup_rsp_frame to populate the AID if device is 11ac capable. + */ +static void populate_dotf_tdls_vht_aid(struct mac_context *mac, uint32_t selfDot11Mode, + struct qdf_mac_addr peerMac, + tDot11fIEAID *Aid, + struct pe_session *pe_session) +{ + if (((wlan_reg_freq_to_chan(mac->pdev, pe_session->curr_op_freq) <= + SIR_11B_CHANNEL_END) && + mac->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band) || + (wlan_reg_freq_to_chan(mac->pdev, pe_session->curr_op_freq) >= + SIR_11B_CHANNEL_END)) { + if (IS_DOT11_MODE_VHT(selfDot11Mode) && + IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) { + + uint16_t aid; + tpDphHashNode sta; + + sta = + dph_lookup_hash_entry(mac, peerMac.bytes, &aid, + &pe_session->dph. + dphHashTable); + if (sta) { + Aid->present = 1; + Aid->assocId = aid | LIM_AID_MASK; /* set bit 14 and 15 1's */ + } else { + Aid->present = 0; + pe_err("sta is NULL for " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peerMac.bytes)); + } + } + } else { + Aid->present = 0; + pe_warn("Vht not enable from ini for 2.4GHz"); + } +} + +#ifdef CONFIG_HL_SUPPORT + +/** + * wma_tx_frame_with_tx_complete_send() - Send tx frames on Direct link or AP link + * depend on reason code + * @mac: pointer to MAC Sirius parameter structure + * @pPacket: pointer to mgmt packet + * @nBytes: number of bytes to send + * @tid:tid value for AC + * @pFrame: pointer to tdls frame + * @smeSessionId:session id + * @flag: tdls flag + * + * Send TDLS Teardown frame on Direct link or AP link, depends on reason code. + * + * Return: None + */ +static inline QDF_STATUS +wma_tx_frame_with_tx_complete_send(struct mac_context *mac, void *pPacket, + uint16_t nBytes, + uint8_t tid, + uint8_t *pFrame, + uint8_t smeSessionId, bool flag) +{ + return wma_tx_frameWithTxComplete(mac, pPacket, + (uint16_t) nBytes, + TXRX_FRM_802_11_DATA, + ANI_TXDIR_TODS, + tid, + lim_tx_complete, pFrame, + lim_mgmt_tdls_tx_complete, + HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME + | HAL_USE_PEER_STA_REQUESTED_MASK, + smeSessionId, flag, 0, + RATEID_DEFAULT); +} +#else + +static inline QDF_STATUS +wma_tx_frame_with_tx_complete_send(struct mac_context *mac, void *pPacket, + uint16_t nBytes, + uint8_t tid, + uint8_t *pFrame, + uint8_t smeSessionId, bool flag) +{ + return wma_tx_frameWithTxComplete(mac, pPacket, + (uint16_t) nBytes, + TXRX_FRM_802_11_DATA, + ANI_TXDIR_TODS, + tid, + lim_tx_complete, pFrame, + lim_mgmt_tdls_tx_complete, + HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME + | HAL_USE_PEER_STA_REQUESTED_MASK, + smeSessionId, false, 0, + RATEID_DEFAULT); +} +#endif + +void lim_set_tdls_flags(struct roam_offload_synch_ind *roam_sync_ind_ptr, + struct pe_session *ft_session_ptr) +{ + roam_sync_ind_ptr->join_rsp->tdls_prohibited = + ft_session_ptr->tdls_prohibited; + roam_sync_ind_ptr->join_rsp->tdls_chan_swit_prohibited = + ft_session_ptr->tdls_chan_swit_prohibited; +} + +/* + * TDLS setup Request frame on AP link + */ +static +QDF_STATUS lim_send_tdls_link_setup_req_frame(struct mac_context *mac, + struct qdf_mac_addr peer_mac, + uint8_t dialog, + struct pe_session *pe_session, + uint8_t *addIe, + uint16_t addIeLen, + enum wifi_traffic_ac ac) +{ + tDot11fTDLSSetupReq tdlsSetupReq; + uint16_t caps = 0; + uint32_t status = 0; + uint32_t nPayload = 0; + uint32_t nBytes = 0; + uint32_t header_offset = 0; + uint8_t *pFrame; + void *pPacket; + QDF_STATUS qdf_status; + uint32_t selfDot11Mode; + uint8_t smeSessionId = 0; + uint8_t sp_length = 0; + +/* Placeholder to support different channel bonding mode of TDLS than AP. */ +/* Today, WNI_CFG_CHANNEL_BONDING_MODE will be overwritten when connecting to AP */ +/* To support this feature, we need to introduce WNI_CFG_TDLS_CHANNEL_BONDING_MODE */ +/* As of now, we hardcoded to max channel bonding of dot11Mode (i.e HT80 for 11ac/HT40 for 11n) */ +/* uint32_t tdlsChannelBondingMode; */ + + /* + * The scheme here is to fill out a 'tDot11fProbeRequest' structure + * and then hand it off to 'dot11f_pack_probe_request' (for + * serialization). We start by zero-initializing the structure: + */ + smeSessionId = pe_session->smeSessionId; + + qdf_mem_zero((uint8_t *) &tdlsSetupReq, sizeof(tDot11fTDLSSetupReq)); + tdlsSetupReq.Category.category = ACTION_CATEGORY_TDLS; + tdlsSetupReq.Action.action = TDLS_SETUP_REQUEST; + tdlsSetupReq.DialogToken.token = dialog; + + populate_dot11f_link_iden(mac, pe_session, + &tdlsSetupReq.LinkIdentifier, peer_mac, + TDLS_INITIATOR); + + if (lim_get_capability_info(mac, &caps, pe_session) != + QDF_STATUS_SUCCESS) { + /* + * Could not get Capabilities value + * from CFG. Log error. + */ + pe_err("could not retrieve Capabilities value"); + } + swap_bit_field16(caps, (uint16_t *) &tdlsSetupReq.Capabilities); + + /* populate supported rate and ext supported rate IE */ + if (QDF_STATUS_E_FAILURE == populate_dot11f_rates_tdls(mac, + &tdlsSetupReq.SuppRates, + &tdlsSetupReq.ExtSuppRates, + wlan_reg_freq_to_chan( + mac->pdev, pe_session->curr_op_freq))) + pe_err("could not populate supported data rates"); + + /* Populate extended capability IE */ + populate_dot11f_tdls_ext_capability(mac, + pe_session, + &tdlsSetupReq.ExtCap); + + if (1 == mac->lim.gLimTDLSWmmMode) { + + pe_debug("populate WMM IE in Setup Request Frame"); + sp_length = mac->mlme_cfg->wmm_params.max_sp_length; + /* include WMM IE */ + tdlsSetupReq.WMMInfoStation.version = SIR_MAC_OUI_VERSION_1; + tdlsSetupReq.WMMInfoStation.acvo_uapsd = + (mac->lim.gLimTDLSUapsdMask & 0x01); + tdlsSetupReq.WMMInfoStation.acvi_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x02) >> 1); + tdlsSetupReq.WMMInfoStation.acbk_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x04) >> 2); + tdlsSetupReq.WMMInfoStation.acbe_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x08) >> 3); + tdlsSetupReq.WMMInfoStation.max_sp_length = sp_length; + tdlsSetupReq.WMMInfoStation.present = 1; + } else { + /* + * TODO: we need to see if we have to support conditions where + * we have EDCA parameter info element is needed a) if we need + * different QOS parameters for off channel operations or QOS + * is not supported on AP link and we wanted to QOS on direct + * link. + */ + + /* Populate QOS info, needed for Peer U-APSD session */ + + /* + * TODO: Now hardcoded, since populate_dot11f_qos_caps_station() + * depends on AP's capability, and TDLS doesn't want to depend + * on AP's capability + */ + + pe_debug("populate QOS IE in Setup Request Frame"); + tdlsSetupReq.QOSCapsStation.present = 1; + tdlsSetupReq.QOSCapsStation.max_sp_length = 0; + tdlsSetupReq.QOSCapsStation.qack = 0; + tdlsSetupReq.QOSCapsStation.acbe_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x08) >> 3); + tdlsSetupReq.QOSCapsStation.acbk_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x04) >> 2); + tdlsSetupReq.QOSCapsStation.acvi_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x02) >> 1); + tdlsSetupReq.QOSCapsStation.acvo_uapsd = + (mac->lim.gLimTDLSUapsdMask & 0x01); + } + + /* + * we will always try to init TDLS link with 11n capabilities + * let TDLS setup response to come, and we will set our caps based + * of peer caps + */ + + selfDot11Mode = mac->mlme_cfg->dot11_mode.dot11_mode; + + /* Populate HT/VHT Capabilities */ + populate_dot11f_tdls_ht_vht_cap(mac, selfDot11Mode, &tdlsSetupReq.HTCaps, + &tdlsSetupReq.VHTCaps, pe_session); + + /* Populate AID */ + populate_dotf_tdls_vht_aid(mac, selfDot11Mode, peer_mac, + &tdlsSetupReq.AID, pe_session); + + /* Populate TDLS offchannel param only if offchannel is enabled + * and TDLS Channel Switching is not prohibited by AP in ExtCap + * IE in assoc/re-assoc response. + */ + if ((1 == mac->lim.gLimTDLSOffChannelEnabled) && + (!pe_session->tdls_chan_swit_prohibited)) { + populate_dot11f_tdls_offchannel_params(mac, pe_session, + &tdlsSetupReq.SuppChannels, + &tdlsSetupReq. + SuppOperatingClasses); + if (mac->mlme_cfg->gen.band_capability != BIT(REG_BAND_2G)) { + tdlsSetupReq.ht2040_bss_coexistence.present = 1; + tdlsSetupReq.ht2040_bss_coexistence.info_request = 1; + } + } else { + pe_debug("TDLS offchan not enabled, or channel switch prohibited by AP, gLimTDLSOffChannelEnabled: %d tdls_chan_swit_prohibited: %d", + mac->lim.gLimTDLSOffChannelEnabled, + pe_session->tdls_chan_swit_prohibited); + } + /* + * now we pack it. First, how much space are we going to need? + */ + status = dot11f_get_packed_tdls_setup_req_size(mac, &tdlsSetupReq, + &nPayload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to calculate the packed size for a Setup Request (0x%08x)", + status); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fProbeRequest); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size for a Setup Request (0x%08x)", + status); + } + + /* + * This frame is going out from PE as data frames with special ethertype + * 89-0d. + * 8 bytes of RFC 1042 header + */ + + nBytes = nPayload + ((IS_QOS_ENABLED(pe_session)) + ? sizeof(tSirMacDataHdr3a) : + sizeof(tSirMacMgmtHdr)) + + sizeof(eth_890d_header) + + PAYLOAD_TYPE_TDLS_SIZE + addIeLen; + + /* Ok-- try to allocate memory from MGMT PKT pool */ + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a TDLS Setup Request", + nBytes); + return QDF_STATUS_E_NOMEM; + } + + /* zero out the memory */ + qdf_mem_zero(pFrame, nBytes); + + /* + * IE formation, memory allocation is completed, Now form TDLS discovery + * request frame + */ + + /* fill out the buffer descriptor */ + + header_offset = lim_prepare_tdls_frame_header(mac, pFrame, + LINK_IDEN_ADDR_OFFSET(tdlsSetupReq), + TDLS_LINK_AP, TDLS_INITIATOR, + (ac == WIFI_AC_VI) ? TID_AC_VI : TID_AC_BK, + pe_session); + + pe_debug("SupportedChnlWidth: %x rxMCSMap: %x rxMCSMap: %x txSupDataRate: %x", + tdlsSetupReq.VHTCaps.supportedChannelWidthSet, + tdlsSetupReq.VHTCaps.rxMCSMap, + tdlsSetupReq.VHTCaps.txMCSMap, + tdlsSetupReq.VHTCaps.txSupDataRate); + + status = dot11f_pack_tdls_setup_req(mac, &tdlsSetupReq, pFrame + + header_offset, nPayload, &nPayload); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a TDLS Setup request (0x%08x)", + status); + cds_packet_free((void *)pPacket); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing TDLS Setup Request (0x%08x)", + status); + } + + /* Copy the additional IE. */ + /* TODO : addIe is added at the end of the frame. This means it doesn't */ + /* follow the order. This should be ok, but we should consider changing this */ + /* if there is any IOT issue. */ + if (addIeLen != 0) { + pe_debug("Copy Additional Ie Len = %d", addIeLen); + qdf_mem_copy(pFrame + header_offset + nPayload, addIe, + addIeLen); + } + + pe_debug("[TDLS] action: %d (%s) -AP-> OTA peer="QDF_MAC_ADDR_FMT, + TDLS_SETUP_REQUEST, + lim_trace_tdls_action_string(TDLS_SETUP_REQUEST), + QDF_MAC_ADDR_REF(peer_mac.bytes)); + + mac->lim.tdls_frm_session_id = pe_session->smeSessionId; + lim_diag_mgmt_tx_event_report(mac, (tpSirMacMgmtHdr) pFrame, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + + qdf_status = wma_tx_frame_with_tx_complete_send(mac, pPacket, + (uint16_t) nBytes, + TID_AC_VI, + pFrame, + smeSessionId, true); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + mac->lim.tdls_frm_session_id = NO_SESSION; + pe_err("could not send TDLS Setup Request frame!"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; + +} + +/* + * Send TDLS Teardown frame on Direct link or AP link, depends on reason code. + */ +static +QDF_STATUS lim_send_tdls_teardown_frame(struct mac_context *mac, + struct qdf_mac_addr peer_mac, + uint16_t reason, + uint8_t responder, + struct pe_session *pe_session, + uint8_t *addIe, uint16_t addIeLen, + enum wifi_traffic_ac ac) +{ + tDot11fTDLSTeardown teardown; + uint32_t status = 0; + uint32_t nPayload = 0; + uint32_t nBytes = 0; + uint32_t header_offset = 0; + uint8_t *pFrame; + void *pPacket; + QDF_STATUS qdf_status; +#ifndef NO_PAD_TDLS_MIN_8023_SIZE + uint32_t padLen = 0; +#endif + uint8_t smeSessionId = 0; + tpDphHashNode sta_ds; + uint16_t aid = 0; + uint8_t qos_mode = 0; + uint8_t tdls_link_type; + + if (!pe_session) { + pe_err("pe_session is NULL"); + return QDF_STATUS_E_FAILURE; + } + smeSessionId = pe_session->smeSessionId; + /* + * The scheme here is to fill out a 'tDot11fProbeRequest' structure + * and then hand it off to 'dot11f_pack_probe_request' (for + * serialization). We start by zero-initializing the structure: + */ + qdf_mem_zero((uint8_t *) &teardown, sizeof(tDot11fTDLSTeardown)); + teardown.Category.category = ACTION_CATEGORY_TDLS; + teardown.Action.action = TDLS_TEARDOWN; + teardown.Reason.code = reason; + + populate_dot11f_link_iden(mac, pe_session, &teardown.LinkIdentifier, + peer_mac, + (responder == + true) ? TDLS_RESPONDER : TDLS_INITIATOR); + + /* + * now we pack it. First, how much space are we going to need? + */ + status = dot11f_get_packed_tdls_teardown_size(mac, &teardown, &nPayload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to calculate the packed size for a discovery Request (0x%08x)", + status); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fProbeRequest); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size for a discovery Request (0x%08x)", + status); + } + + /* + * This frame is going out from PE as data frames with special ethertype + * 89-0d. + * 8 bytes of RFC 1042 header + */ + sta_ds = dph_lookup_hash_entry(mac, pe_session->bssId, &aid, + &pe_session->dph.dphHashTable); + if (sta_ds) + qos_mode = sta_ds->qosMode; + tdls_link_type = (reason == eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE) + ? TDLS_LINK_AP : TDLS_LINK_DIRECT; + nBytes = nPayload + (((IS_QOS_ENABLED(pe_session) && + (tdls_link_type == TDLS_LINK_AP)) || + ((tdls_link_type == TDLS_LINK_DIRECT) && qos_mode)) + ? sizeof(tSirMacDataHdr3a) : + sizeof(tSirMacMgmtHdr)) + + sizeof(eth_890d_header) + + PAYLOAD_TYPE_TDLS_SIZE + addIeLen; + +#ifndef NO_PAD_TDLS_MIN_8023_SIZE + /* IOT issue with some AP : some AP doesn't like the data packet size < minimum 802.3 frame length (64) + Hence AP itself padding some bytes, which caused teardown packet is dropped at + receiver side. To avoid such IOT issue, we added some extra bytes to meet data frame size >= 64 + */ + if (nPayload + PAYLOAD_TYPE_TDLS_SIZE < MIN_IEEE_8023_SIZE) { + padLen = + MIN_IEEE_8023_SIZE - (nPayload + PAYLOAD_TYPE_TDLS_SIZE); + + /* if padLen is less than minimum vendorSpecific (5), pad up to 5 */ + if (padLen < MIN_VENDOR_SPECIFIC_IE_SIZE) + padLen = MIN_VENDOR_SPECIFIC_IE_SIZE; + + nBytes += padLen; + } +#endif + + /* Ok-- try to allocate memory from MGMT PKT pool */ + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a TDLS Teardown Frame.", + nBytes); + return QDF_STATUS_E_NOMEM; + } + + /* zero out the memory */ + qdf_mem_zero(pFrame, nBytes); + + /* + * IE formation, memory allocation is completed, Now form TDLS discovery + * request frame + */ + + /* fill out the buffer descriptor */ + pe_debug("Reason of TDLS Teardown: %d", reason); + header_offset = lim_prepare_tdls_frame_header(mac, pFrame, + LINK_IDEN_ADDR_OFFSET(teardown), + (reason == eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE) ? + TDLS_LINK_AP : TDLS_LINK_DIRECT, + (responder == true) ? TDLS_RESPONDER : TDLS_INITIATOR, + (ac == WIFI_AC_VI) ? TID_AC_VI : TID_AC_BK, + pe_session); + + status = dot11f_pack_tdls_teardown(mac, &teardown, pFrame + + header_offset, nPayload, &nPayload); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a TDLS Teardown frame (0x%08x)", + status); + cds_packet_free((void *)pPacket); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing TDLS Teardown frame (0x%08x)", + status); + } + + if (addIeLen != 0) { + pe_debug("Copy Additional Ie Len = %d", addIeLen); + qdf_mem_copy(pFrame + header_offset + nPayload, addIe, + addIeLen); + } +#ifndef NO_PAD_TDLS_MIN_8023_SIZE + if (padLen != 0) { + /* QCOM VENDOR OUI = { 0x00, 0xA0, 0xC6, type = 0x0000 }; */ + uint8_t *padVendorSpecific = + pFrame + header_offset + nPayload + addIeLen; + /* make QCOM_VENDOR_OUI, and type = 0x0000, and all the payload to be zero */ + padVendorSpecific[0] = 221; + padVendorSpecific[1] = padLen - 2; + padVendorSpecific[2] = 0x00; + padVendorSpecific[3] = 0xA0; + padVendorSpecific[4] = 0xC6; + + pe_debug("Padding Vendor Specific Ie Len = %d", padLen); + + /* padding zero if more than 5 bytes are required */ + if (padLen > MIN_VENDOR_SPECIFIC_IE_SIZE) + qdf_mem_zero(pFrame + header_offset + nPayload + + addIeLen + MIN_VENDOR_SPECIFIC_IE_SIZE, + padLen - MIN_VENDOR_SPECIFIC_IE_SIZE); + } +#endif + pe_debug("[TDLS] action: %d (%s) -%s-> OTA peer="QDF_MAC_ADDR_FMT, + TDLS_TEARDOWN, + lim_trace_tdls_action_string(TDLS_TEARDOWN), + ((reason == eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE) ? "AP" : + "DIRECT"), + QDF_MAC_ADDR_REF(peer_mac.bytes)); + + mac->lim.tdls_frm_session_id = pe_session->smeSessionId; + lim_diag_mgmt_tx_event_report(mac, (tpSirMacMgmtHdr) pFrame, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + + qdf_status = wma_tx_frame_with_tx_complete_send(mac, pPacket, + (uint16_t) nBytes, + TID_AC_VI, + pFrame, + smeSessionId, + (reason == eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE) + ? true : false); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + mac->lim.tdls_frm_session_id = NO_SESSION; + pe_err("could not send TDLS Teardown frame"); + return QDF_STATUS_E_FAILURE; + + } + + return QDF_STATUS_SUCCESS; +} + +/* + * Send Setup RSP frame on AP link. + */ +static QDF_STATUS lim_send_tdls_setup_rsp_frame(struct mac_context *mac, + struct qdf_mac_addr peer_mac, + uint8_t dialog, + struct pe_session *pe_session, + etdlsLinkSetupStatus setupStatus, + uint8_t *addIe, + uint16_t addIeLen, + enum wifi_traffic_ac ac) +{ + tDot11fTDLSSetupRsp tdlsSetupRsp; + uint32_t status = 0; + uint16_t caps = 0; + uint32_t nPayload = 0; + uint32_t header_offset = 0; + uint32_t nBytes = 0; + uint8_t *pFrame; + void *pPacket; + QDF_STATUS qdf_status; + uint32_t selfDot11Mode; + uint8_t max_sp_length = 0; +/* Placeholder to support different channel bonding mode of TDLS than AP. */ +/* Today, WNI_CFG_CHANNEL_BONDING_MODE will be overwritten when connecting to AP */ +/* To support this feature, we need to introduce WNI_CFG_TDLS_CHANNEL_BONDING_MODE */ +/* As of now, we hardcoded to max channel bonding of dot11Mode (i.e HT80 for 11ac/HT40 for 11n) */ +/* uint32_t tdlsChannelBondingMode; */ + uint8_t smeSessionId = 0; + + if (!pe_session) { + pe_err("pe_session is NULL"); + return QDF_STATUS_E_FAILURE; + } + smeSessionId = pe_session->smeSessionId; + + /* + * The scheme here is to fill out a 'tDot11fProbeRequest' structure + * and then hand it off to 'dot11f_pack_probe_request' (for + * serialization). We start by zero-initializing the structure: + */ + qdf_mem_zero((uint8_t *) &tdlsSetupRsp, sizeof(tDot11fTDLSSetupRsp)); + + /* + * setup Fixed fields, + */ + tdlsSetupRsp.Category.category = ACTION_CATEGORY_TDLS; + tdlsSetupRsp.Action.action = TDLS_SETUP_RESPONSE; + tdlsSetupRsp.DialogToken.token = dialog; + + populate_dot11f_link_iden(mac, pe_session, + &tdlsSetupRsp.LinkIdentifier, peer_mac, + TDLS_RESPONDER); + + if (lim_get_capability_info(mac, &caps, pe_session) != + QDF_STATUS_SUCCESS) { + /* + * Could not get Capabilities value + * from CFG. Log error. + */ + pe_err("could not retrieve Capabilities value"); + } + swap_bit_field16(caps, (uint16_t *) &tdlsSetupRsp.Capabilities); + + /* populate supported rate and ext supported rate IE */ + if (QDF_STATUS_E_FAILURE == populate_dot11f_rates_tdls(mac, + &tdlsSetupRsp.SuppRates, + &tdlsSetupRsp.ExtSuppRates, + wlan_reg_freq_to_chan( + mac->pdev, pe_session->curr_op_freq))) + pe_err("could not populate supported data rates"); + + /* Populate extended capability IE */ + populate_dot11f_tdls_ext_capability(mac, + pe_session, + &tdlsSetupRsp.ExtCap); + + if (1 == mac->lim.gLimTDLSWmmMode) { + + pe_debug("populate WMM IE in Setup Response frame"); + max_sp_length = mac->mlme_cfg->wmm_params.max_sp_length; + /* include WMM IE */ + tdlsSetupRsp.WMMInfoStation.version = SIR_MAC_OUI_VERSION_1; + tdlsSetupRsp.WMMInfoStation.acvo_uapsd = + (mac->lim.gLimTDLSUapsdMask & 0x01); + tdlsSetupRsp.WMMInfoStation.acvi_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x02) >> 1); + tdlsSetupRsp.WMMInfoStation.acbk_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x04) >> 2); + tdlsSetupRsp.WMMInfoStation.acbe_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x08) >> 3); + tdlsSetupRsp.WMMInfoStation.max_sp_length = max_sp_length; + tdlsSetupRsp.WMMInfoStation.present = 1; + } else { + /* + * TODO: we need to see if we have to support conditions where + * we have EDCA parameter info element is needed a) if we need + * different QOS parameters for off channel operations or QOS + * is not supported on AP link and we wanted to QOS on direct + * link. + */ + /* Populate QOS info, needed for Peer U-APSD session */ + /* + * TODO: Now hardcoded, because + * populate_dot11f_qos_caps_station() depends on AP's + * capability, and TDLS doesn't want to depend on AP's + * capability + */ + pe_debug("populate QOS IE in Setup Response frame"); + tdlsSetupRsp.QOSCapsStation.present = 1; + tdlsSetupRsp.QOSCapsStation.max_sp_length = 0; + tdlsSetupRsp.QOSCapsStation.qack = 0; + tdlsSetupRsp.QOSCapsStation.acbe_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x08) >> 3); + tdlsSetupRsp.QOSCapsStation.acbk_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x04) >> 2); + tdlsSetupRsp.QOSCapsStation.acvi_uapsd = + ((mac->lim.gLimTDLSUapsdMask & 0x02) >> 1); + tdlsSetupRsp.QOSCapsStation.acvo_uapsd = + (mac->lim.gLimTDLSUapsdMask & 0x01); + } + + selfDot11Mode = mac->mlme_cfg->dot11_mode.dot11_mode; + + /* Populate HT/VHT Capabilities */ + populate_dot11f_tdls_ht_vht_cap(mac, selfDot11Mode, &tdlsSetupRsp.HTCaps, + &tdlsSetupRsp.VHTCaps, pe_session); + + /* Populate AID */ + populate_dotf_tdls_vht_aid(mac, selfDot11Mode, peer_mac, + &tdlsSetupRsp.AID, pe_session); + + /* Populate TDLS offchannel param only if offchannel is enabled + * and TDLS Channel Switching is not prohibited by AP in ExtCap + * IE in assoc/re-assoc response. + */ + if ((1 == mac->lim.gLimTDLSOffChannelEnabled) && + (!pe_session->tdls_chan_swit_prohibited)) { + populate_dot11f_tdls_offchannel_params(mac, pe_session, + &tdlsSetupRsp.SuppChannels, + &tdlsSetupRsp. + SuppOperatingClasses); + if (mac->mlme_cfg->gen.band_capability != BIT(REG_BAND_2G)) { + tdlsSetupRsp.ht2040_bss_coexistence.present = 1; + tdlsSetupRsp.ht2040_bss_coexistence.info_request = 1; + } + } else { + pe_debug("TDLS offchan not enabled, or channel switch prohibited by AP, gLimTDLSOffChannelEnabled: %d tdls_chan_swit_prohibited: %d", + mac->lim.gLimTDLSOffChannelEnabled, + pe_session->tdls_chan_swit_prohibited); + } + tdlsSetupRsp.Status.status = setupStatus; + /* + * now we pack it. First, how much space are we going to need? + */ + status = dot11f_get_packed_tdls_setup_rsp_size(mac, &tdlsSetupRsp, + &nPayload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to calculate the packed size for a Setup Response (0x%08x)", + status); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fProbeRequest); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size for Setup Response (0x%08x)", + status); + } + + /* + * This frame is going out from PE as data frames with special ethertype + * 89-0d. + * 8 bytes of RFC 1042 header + */ + + nBytes = nPayload + ((IS_QOS_ENABLED(pe_session)) + ? sizeof(tSirMacDataHdr3a) : + sizeof(tSirMacMgmtHdr)) + + sizeof(eth_890d_header) + + PAYLOAD_TYPE_TDLS_SIZE + addIeLen; + + /* Ok-- try to allocate memory from MGMT PKT pool */ + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a TDLS Setup Response", + nBytes); + return QDF_STATUS_E_NOMEM; + } + + /* zero out the memory */ + qdf_mem_zero(pFrame, nBytes); + + /* + * IE formation, memory allocation is completed, Now form TDLS discovery + * request frame + */ + + /* fill out the buffer descriptor */ + + header_offset = lim_prepare_tdls_frame_header(mac, pFrame, + LINK_IDEN_ADDR_OFFSET(tdlsSetupRsp), TDLS_LINK_AP, + TDLS_RESPONDER, + (ac == WIFI_AC_VI) ? TID_AC_VI : TID_AC_BK, + pe_session); + + pe_debug("SupportedChnlWidth: %x rxMCSMap: %x rxMCSMap: %x txSupDataRate: %x", + tdlsSetupRsp.VHTCaps.supportedChannelWidthSet, + tdlsSetupRsp.VHTCaps.rxMCSMap, + tdlsSetupRsp.VHTCaps.txMCSMap, + tdlsSetupRsp.VHTCaps.txSupDataRate); + status = dot11f_pack_tdls_setup_rsp(mac, &tdlsSetupRsp, + pFrame + header_offset, + nPayload, &nPayload); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a TDLS Setup Response (0x%08x)", + status); + cds_packet_free((void *)pPacket); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing TDLS Setup Response (0x%08x)", + status); + } + + /* Copy the additional IE. */ + /* TODO : addIe is added at the end of the frame. This means it doesn't */ + /* follow the order. This should be ok, but we should consider changing this */ + /* if there is any IOT issue. */ + if (addIeLen != 0) { + qdf_mem_copy(pFrame + header_offset + nPayload, addIe, + addIeLen); + } + + pe_debug("[TDLS] action: %d (%s) -AP-> OTA peer="QDF_MAC_ADDR_FMT, + TDLS_SETUP_RESPONSE, + lim_trace_tdls_action_string(TDLS_SETUP_RESPONSE), + QDF_MAC_ADDR_REF(peer_mac.bytes)); + + mac->lim.tdls_frm_session_id = pe_session->smeSessionId; + lim_diag_mgmt_tx_event_report(mac, (tpSirMacMgmtHdr) pFrame, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + + qdf_status = wma_tx_frame_with_tx_complete_send(mac, pPacket, + (uint16_t) nBytes, + TID_AC_VI, + pFrame, + smeSessionId, true); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + mac->lim.tdls_frm_session_id = NO_SESSION; + pe_err("could not send TDLS Dis Request frame!"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/* + * Send TDLS setup CNF frame on AP link + */ +static +QDF_STATUS lim_send_tdls_link_setup_cnf_frame(struct mac_context *mac, + struct qdf_mac_addr peer_mac, + uint8_t dialog, + uint32_t peerCapability, + struct pe_session *pe_session, + uint8_t *addIe, + uint16_t addIeLen, + enum wifi_traffic_ac ac) +{ + tDot11fTDLSSetupCnf tdlsSetupCnf; + uint32_t status = 0; + uint32_t nPayload = 0; + uint32_t nBytes = 0; + uint32_t header_offset = 0; + uint8_t *pFrame; + void *pPacket; + QDF_STATUS qdf_status; +#ifndef NO_PAD_TDLS_MIN_8023_SIZE + uint32_t padLen = 0; +#endif + uint8_t smeSessionId = 0; + + /* + * The scheme here is to fill out a 'tDot11fProbeRequest' structure + * and then hand it off to 'dot11f_pack_probe_request' (for + * serialization). We start by zero-initializing the structure: + */ + smeSessionId = pe_session->smeSessionId; + + qdf_mem_zero((uint8_t *) &tdlsSetupCnf, sizeof(tDot11fTDLSSetupCnf)); + + /* + * setup Fixed fields, + */ + tdlsSetupCnf.Category.category = ACTION_CATEGORY_TDLS; + tdlsSetupCnf.Action.action = TDLS_SETUP_CONFIRM; + tdlsSetupCnf.DialogToken.token = dialog; + + populate_dot11f_link_iden(mac, pe_session, + &tdlsSetupCnf.LinkIdentifier, peer_mac, + TDLS_INITIATOR); + /* + * TODO: we need to see if we have to support conditions where we have + * EDCA parameter info element is needed a) if we need different QOS + * parameters for off channel operations or QOS is not supported on + * AP link and we wanted to QOS on direct link. + */ + + /* Check self and peer WMM capable */ + if ((1 == mac->lim.gLimTDLSWmmMode) && + (CHECK_BIT(peerCapability, TDLS_PEER_WMM_CAP))) { + pe_debug("populate WMM praram in Setup Confirm"); + populate_dot11f_wmm_params(mac, &tdlsSetupCnf.WMMParams, + pe_session); + } + + /* Check peer is VHT capable */ + if (CHECK_BIT(peerCapability, TDLS_PEER_VHT_CAP)) { + populate_dot11f_vht_operation(mac, + pe_session, + &tdlsSetupCnf.VHTOperation); + populate_dot11f_ht_info(mac, &tdlsSetupCnf.HTInfo, pe_session); + } else if (CHECK_BIT(peerCapability, TDLS_PEER_HT_CAP)) { /* Check peer is HT capable */ + populate_dot11f_ht_info(mac, &tdlsSetupCnf.HTInfo, pe_session); + } + + /* + * now we pack it. First, how much space are we going to need? + */ + status = dot11f_get_packed_tdls_setup_cnf_size(mac, &tdlsSetupCnf, + &nPayload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to calculate the packed size for a Setup Confirm (0x%08x)", + status); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fProbeRequest); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size for Setup Confirm (0x%08x)", + status); + } + + /* + * This frame is going out from PE as data frames with special ethertype + * 89-0d. + * 8 bytes of RFC 1042 header + */ + + nBytes = nPayload + ((IS_QOS_ENABLED(pe_session)) + ? sizeof(tSirMacDataHdr3a) : + sizeof(tSirMacMgmtHdr)) + + sizeof(eth_890d_header) + + PAYLOAD_TYPE_TDLS_SIZE + addIeLen; + +#ifndef NO_PAD_TDLS_MIN_8023_SIZE + /* IOT issue with some AP : some AP doesn't like the data packet size < minimum 802.3 frame length (64) + Hence AP itself padding some bytes, which caused teardown packet is dropped at + receiver side. To avoid such IOT issue, we added some extra bytes to meet data frame size >= 64 + */ + if (nPayload + PAYLOAD_TYPE_TDLS_SIZE < MIN_IEEE_8023_SIZE) { + padLen = + MIN_IEEE_8023_SIZE - (nPayload + PAYLOAD_TYPE_TDLS_SIZE); + + /* if padLen is less than minimum vendorSpecific (5), pad up to 5 */ + if (padLen < MIN_VENDOR_SPECIFIC_IE_SIZE) + padLen = MIN_VENDOR_SPECIFIC_IE_SIZE; + + nBytes += padLen; + } +#endif + + /* Ok-- try to allocate memory from MGMT PKT pool */ + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a TDLS Setup Confirm", + nBytes); + return QDF_STATUS_E_NOMEM; + } + + /* zero out the memory */ + qdf_mem_zero(pFrame, nBytes); + + /* + * IE formation, memory allocation is completed, Now form TDLS discovery + * request frame + */ + + /* fill out the buffer descriptor */ + + header_offset = lim_prepare_tdls_frame_header(mac, pFrame, + LINK_IDEN_ADDR_OFFSET(tdlsSetupCnf), + TDLS_LINK_AP, + TDLS_INITIATOR, + (ac == WIFI_AC_VI) ? TID_AC_VI : TID_AC_BK, + pe_session); + + status = dot11f_pack_tdls_setup_cnf(mac, &tdlsSetupCnf, pFrame + + header_offset, nPayload, &nPayload); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a TDLS discovery req (0x%08x)", status); + cds_packet_free((void *)pPacket); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing TDLS Discovery Request (0x%08x)", + status); + } + + /* Copy the additional IE. */ + /* TODO : addIe is added at the end of the frame. This means it doesn't */ + /* follow the order. This should be ok, but we should consider changing this */ + /* if there is any IOT issue. */ + if (addIeLen != 0) { + qdf_mem_copy(pFrame + header_offset + nPayload, addIe, + addIeLen); + } +#ifndef NO_PAD_TDLS_MIN_8023_SIZE + if (padLen != 0) { + /* QCOM VENDOR OUI = { 0x00, 0xA0, 0xC6, type = 0x0000 }; */ + uint8_t *padVendorSpecific = + pFrame + header_offset + nPayload + addIeLen; + /* make QCOM_VENDOR_OUI, and type = 0x0000, and all the payload to be zero */ + padVendorSpecific[0] = 221; + padVendorSpecific[1] = padLen - 2; + padVendorSpecific[2] = 0x00; + padVendorSpecific[3] = 0xA0; + padVendorSpecific[4] = 0xC6; + + pe_debug("Padding Vendor Specific Ie Len: %d", padLen); + + /* padding zero if more than 5 bytes are required */ + if (padLen > MIN_VENDOR_SPECIFIC_IE_SIZE) + qdf_mem_zero(pFrame + header_offset + nPayload + + addIeLen + MIN_VENDOR_SPECIFIC_IE_SIZE, + padLen - MIN_VENDOR_SPECIFIC_IE_SIZE); + } +#endif + + pe_debug("[TDLS] action: %d (%s) -AP-> OTA peer="QDF_MAC_ADDR_FMT, + TDLS_SETUP_CONFIRM, + lim_trace_tdls_action_string(TDLS_SETUP_CONFIRM), + QDF_MAC_ADDR_REF(peer_mac.bytes)); + + mac->lim.tdls_frm_session_id = pe_session->smeSessionId; + lim_diag_mgmt_tx_event_report(mac, (tpSirMacMgmtHdr) pFrame, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + + qdf_status = wma_tx_frame_with_tx_complete_send(mac, pPacket, + (uint16_t) nBytes, + TID_AC_VI, + pFrame, + smeSessionId, true); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + mac->lim.tdls_frm_session_id = NO_SESSION; + pe_err("could not send TDLS Setup Confirm frame"); + return QDF_STATUS_E_FAILURE; + + } + + return QDF_STATUS_SUCCESS; +} + +/* This Function is similar to populate_dot11f_ht_caps, except that + * the HT Capabilities are considered from the AddStaReq rather from + * the cfg.dat as in populate_dot11f_ht_caps + */ +static QDF_STATUS +lim_tdls_populate_dot11f_ht_caps(struct mac_context *mac, + struct pe_session *pe_session, + struct tdls_add_sta_req *add_sta_req, + tDot11fIEHTCaps *pDot11f) +{ + uint32_t nCfgValue; + uint8_t nCfgValue8; + tSirMacHTParametersInfo *pHTParametersInfo; + union { + uint16_t nCfgValue16; + struct mlme_ht_capabilities_info ht_cap_info; + tSirMacExtendedHTCapabilityInfo extHtCapInfo; + } uHTCapabilityInfo; + + tSirMacTxBFCapabilityInfo *pTxBFCapabilityInfo; + tSirMacASCapabilityInfo *pASCapabilityInfo; + + nCfgValue = add_sta_req->ht_cap.hc_cap; + + uHTCapabilityInfo.nCfgValue16 = nCfgValue & 0xFFFF; + + pDot11f->advCodingCap = uHTCapabilityInfo.ht_cap_info.adv_coding_cap; + pDot11f->mimoPowerSave = uHTCapabilityInfo.ht_cap_info.mimo_power_save; + pDot11f->greenField = uHTCapabilityInfo.ht_cap_info.green_field; + pDot11f->shortGI20MHz = uHTCapabilityInfo.ht_cap_info.short_gi_20_mhz; + pDot11f->shortGI40MHz = uHTCapabilityInfo.ht_cap_info.short_gi_40_mhz; + pDot11f->txSTBC = uHTCapabilityInfo.ht_cap_info.tx_stbc; + pDot11f->rxSTBC = uHTCapabilityInfo.ht_cap_info.rx_stbc; + pDot11f->delayedBA = uHTCapabilityInfo.ht_cap_info.delayed_ba; + pDot11f->maximalAMSDUsize = + uHTCapabilityInfo.ht_cap_info.maximal_amsdu_size; + pDot11f->dsssCckMode40MHz = + uHTCapabilityInfo.ht_cap_info.dsss_cck_mode_40_mhz; + pDot11f->psmp = uHTCapabilityInfo.ht_cap_info.psmp; + pDot11f->stbcControlFrame = + uHTCapabilityInfo.ht_cap_info.stbc_control_frame; + pDot11f->lsigTXOPProtection = + uHTCapabilityInfo.ht_cap_info.l_sig_tx_op_protection; + + /* + * All sessionized entries will need the check below + * Only in case of NO session + */ + if (!pe_session) { + pDot11f->supportedChannelWidthSet = + uHTCapabilityInfo.ht_cap_info. + supported_channel_width_set; + } else { + pDot11f->supportedChannelWidthSet = + pe_session->htSupportedChannelWidthSet; + } + + /* Ensure that shortGI40MHz is Disabled if supportedChannelWidthSet is + eHT_CHANNEL_WIDTH_20MHZ */ + if (pDot11f->supportedChannelWidthSet == eHT_CHANNEL_WIDTH_20MHZ) { + pDot11f->shortGI40MHz = 0; + } + + pe_debug("SupportedChnlWidth: %d, mimoPS: %d, GF: %d, shortGI20:%d, shortGI40: %d, dsssCck: %d", + pDot11f->supportedChannelWidthSet, + pDot11f->mimoPowerSave, + pDot11f->greenField, + pDot11f->shortGI20MHz, + pDot11f->shortGI40MHz, + pDot11f->dsssCckMode40MHz); + + nCfgValue = add_sta_req->ht_cap.ampdu_param; + + nCfgValue8 = (uint8_t) nCfgValue; + pHTParametersInfo = (tSirMacHTParametersInfo *) &nCfgValue8; + + pDot11f->maxRxAMPDUFactor = pHTParametersInfo->maxRxAMPDUFactor; + pDot11f->mpduDensity = pHTParametersInfo->mpduDensity; + pDot11f->reserved1 = pHTParametersInfo->reserved; + + pe_debug("AMPDU Param: %x", nCfgValue); + qdf_mem_copy(pDot11f->supportedMCSSet, add_sta_req->ht_cap.mcsset, + SIZE_OF_SUPPORTED_MCS_SET); + + nCfgValue = add_sta_req->ht_cap.extcap; + + uHTCapabilityInfo.nCfgValue16 = nCfgValue & 0xFFFF; + + pDot11f->pco = uHTCapabilityInfo.extHtCapInfo.pco; + pDot11f->transitionTime = uHTCapabilityInfo.extHtCapInfo.transitionTime; + pDot11f->mcsFeedback = uHTCapabilityInfo.extHtCapInfo.mcsFeedback; + + nCfgValue = add_sta_req->ht_cap.txbf_cap; + + pTxBFCapabilityInfo = (tSirMacTxBFCapabilityInfo *) &nCfgValue; + pDot11f->txBF = pTxBFCapabilityInfo->txBF; + pDot11f->rxStaggeredSounding = pTxBFCapabilityInfo->rxStaggeredSounding; + pDot11f->txStaggeredSounding = pTxBFCapabilityInfo->txStaggeredSounding; + pDot11f->rxZLF = pTxBFCapabilityInfo->rxZLF; + pDot11f->txZLF = pTxBFCapabilityInfo->txZLF; + pDot11f->implicitTxBF = pTxBFCapabilityInfo->implicitTxBF; + pDot11f->calibration = pTxBFCapabilityInfo->calibration; + pDot11f->explicitCSITxBF = pTxBFCapabilityInfo->explicitCSITxBF; + pDot11f->explicitUncompressedSteeringMatrix = + pTxBFCapabilityInfo->explicitUncompressedSteeringMatrix; + pDot11f->explicitBFCSIFeedback = + pTxBFCapabilityInfo->explicitBFCSIFeedback; + pDot11f->explicitUncompressedSteeringMatrixFeedback = + pTxBFCapabilityInfo->explicitUncompressedSteeringMatrixFeedback; + pDot11f->explicitCompressedSteeringMatrixFeedback = + pTxBFCapabilityInfo->explicitCompressedSteeringMatrixFeedback; + pDot11f->csiNumBFAntennae = pTxBFCapabilityInfo->csiNumBFAntennae; + pDot11f->uncompressedSteeringMatrixBFAntennae = + pTxBFCapabilityInfo->uncompressedSteeringMatrixBFAntennae; + pDot11f->compressedSteeringMatrixBFAntennae = + pTxBFCapabilityInfo->compressedSteeringMatrixBFAntennae; + + nCfgValue = add_sta_req->ht_cap.antenna; + + nCfgValue8 = (uint8_t) nCfgValue; + + pASCapabilityInfo = (tSirMacASCapabilityInfo *) &nCfgValue8; + pDot11f->antennaSelection = pASCapabilityInfo->antennaSelection; + pDot11f->explicitCSIFeedbackTx = + pASCapabilityInfo->explicitCSIFeedbackTx; + pDot11f->antennaIndicesFeedbackTx = + pASCapabilityInfo->antennaIndicesFeedbackTx; + pDot11f->explicitCSIFeedback = pASCapabilityInfo->explicitCSIFeedback; + pDot11f->antennaIndicesFeedback = + pASCapabilityInfo->antennaIndicesFeedback; + pDot11f->rxAS = pASCapabilityInfo->rxAS; + pDot11f->txSoundingPPDUs = pASCapabilityInfo->txSoundingPPDUs; + + pDot11f->present = add_sta_req->htcap_present; + + return QDF_STATUS_SUCCESS; + +} + +static QDF_STATUS +lim_tdls_populate_dot11f_vht_caps(struct mac_context *mac, + struct tdls_add_sta_req *add_sta_req, + tDot11fIEVHTCaps *pDot11f) +{ + uint32_t nCfgValue = 0; + union { + uint32_t nCfgValue32; + tSirMacVHTCapabilityInfo vhtCapInfo; + } uVHTCapabilityInfo; + union { + uint16_t nCfgValue16; + tSirMacVHTTxSupDataRateInfo vhtTxSupDataRateInfo; + tSirMacVHTRxSupDataRateInfo vhtRxsupDataRateInfo; + } uVHTSupDataRateInfo; + + pDot11f->present = add_sta_req->vhtcap_present; + + nCfgValue = add_sta_req->vht_cap.vht_capinfo; + uVHTCapabilityInfo.nCfgValue32 = nCfgValue; + + pDot11f->maxMPDULen = uVHTCapabilityInfo.vhtCapInfo.maxMPDULen; + pDot11f->supportedChannelWidthSet = + uVHTCapabilityInfo.vhtCapInfo.supportedChannelWidthSet; + pDot11f->ldpcCodingCap = uVHTCapabilityInfo.vhtCapInfo.ldpcCodingCap; + pDot11f->shortGI80MHz = uVHTCapabilityInfo.vhtCapInfo.shortGI80MHz; + pDot11f->shortGI160and80plus80MHz = + uVHTCapabilityInfo.vhtCapInfo.shortGI160and80plus80MHz; + pDot11f->txSTBC = uVHTCapabilityInfo.vhtCapInfo.txSTBC; + pDot11f->rxSTBC = uVHTCapabilityInfo.vhtCapInfo.rxSTBC; + pDot11f->suBeamFormerCap = 0; + pDot11f->suBeamformeeCap = 0; + pDot11f->csnofBeamformerAntSup = + uVHTCapabilityInfo.vhtCapInfo.csnofBeamformerAntSup; + pDot11f->numSoundingDim = uVHTCapabilityInfo.vhtCapInfo.numSoundingDim; + pDot11f->muBeamformerCap = 0; + pDot11f->muBeamformeeCap = 0; + pDot11f->vhtTXOPPS = uVHTCapabilityInfo.vhtCapInfo.vhtTXOPPS; + pDot11f->htcVHTCap = uVHTCapabilityInfo.vhtCapInfo.htcVHTCap; + pDot11f->maxAMPDULenExp = uVHTCapabilityInfo.vhtCapInfo.maxAMPDULenExp; + pDot11f->vhtLinkAdaptCap = + uVHTCapabilityInfo.vhtCapInfo.vhtLinkAdaptCap; + pDot11f->rxAntPattern = uVHTCapabilityInfo.vhtCapInfo.rxAntPattern; + pDot11f->txAntPattern = uVHTCapabilityInfo.vhtCapInfo.txAntPattern; + pDot11f->extended_nss_bw_supp = + uVHTCapabilityInfo.vhtCapInfo.extended_nss_bw_supp; + + pDot11f->rxMCSMap = add_sta_req->vht_cap.supp_mcs.rx_mcs_map; + + nCfgValue = add_sta_req->vht_cap.supp_mcs.rx_highest; + uVHTSupDataRateInfo.nCfgValue16 = nCfgValue & 0xffff; + pDot11f->rxHighSupDataRate = + uVHTSupDataRateInfo.vhtRxsupDataRateInfo.rxSupDataRate; + pDot11f->max_nsts_total = + uVHTSupDataRateInfo.vhtRxsupDataRateInfo.max_nsts_total; + + pDot11f->txMCSMap = add_sta_req->vht_cap.supp_mcs.tx_mcs_map; + + nCfgValue = add_sta_req->vht_cap.supp_mcs.tx_highest; + uVHTSupDataRateInfo.nCfgValue16 = nCfgValue & 0xffff; + pDot11f->txSupDataRate = + uVHTSupDataRateInfo.vhtTxSupDataRateInfo.txSupDataRate; + + pDot11f->vht_extended_nss_bw_cap = + uVHTSupDataRateInfo.vhtTxSupDataRateInfo.vht_extended_nss_bw_cap; + + lim_log_vht_cap(mac, pDot11f); + + return QDF_STATUS_SUCCESS; + +} + +/** + * lim_tdls_populate_matching_rate_set() - populate matching rate set + * + * @mac_ctx - global MAC context + * @stads - station hash entry + * @supp_rate_set - pointer to supported rate set + * @supp_rates_len - length of the supported rates + * @supp_mcs_set - pointer to supported MSC set + * @session_entry - pointer to PE session entry + * @vht_caps - pointer to VHT capability + * + * + * This function gets set of available rates from the config and compare them + * against the set of received supported rates. After the comparison station + * entry's rates is populated with 11A rates and 11B rates. + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE on failure. + */ +static QDF_STATUS +lim_tdls_populate_matching_rate_set(struct mac_context *mac_ctx, + tpDphHashNode stads, + uint8_t *supp_rate_set, + uint8_t supp_rates_len, + uint8_t *supp_mcs_set, + struct pe_session *session_entry, + tDot11fIEVHTCaps *vht_caps) +{ + tSirMacRateSet temp_rate_set; + uint32_t i, j, is_a_rate; + uint32_t phymode; + uint8_t mcsSet[SIZE_OF_SUPPORTED_MCS_SET]; + struct supported_rates *rates; + uint8_t a_rateindex = 0; + uint8_t b_rateindex = 0; + uint8_t nss; + qdf_size_t val_len; + + is_a_rate = 0; + + lim_get_phy_mode(mac_ctx, &phymode, NULL); + + /** + * Copy received rates in temp_rate_set, the parser has ensured + * unicity of the rates so there cannot be more than 12 . + */ + if (supp_rates_len > WLAN_SUPPORTED_RATES_IE_MAX_LEN) { + pe_warn("Supported rates length: %d more than the Max limit, reset to Max", + supp_rates_len); + supp_rates_len = WLAN_SUPPORTED_RATES_IE_MAX_LEN; + } + + for (i = 0; i < supp_rates_len; i++) + temp_rate_set.rate[i] = supp_rate_set[i]; + + temp_rate_set.numRates = supp_rates_len; + + rates = &stads->supportedRates; + qdf_mem_zero(rates, sizeof(*rates)); + + for (j = 0; j < temp_rate_set.numRates; j++) { + if ((b_rateindex > SIR_NUM_11B_RATES) || + (a_rateindex > SIR_NUM_11A_RATES)) { + pe_warn("Invalid number of rates (11b->%d, 11a->%d)", + b_rateindex, a_rateindex); + return QDF_STATUS_E_FAILURE; + } + if (sirIsArate(temp_rate_set.rate[j] & 0x7f)) { + is_a_rate = 1; + if (a_rateindex < SIR_NUM_11A_RATES) + rates->llaRates[a_rateindex++] = + temp_rate_set.rate[j]; + } else { + if (b_rateindex < SIR_NUM_11B_RATES) + rates->llbRates[b_rateindex++] = + temp_rate_set.rate[j]; + } + } + + if (wlan_reg_is_5ghz_ch_freq(session_entry->curr_op_freq)) + nss = mac_ctx->vdev_type_nss_5g.tdls; + else + nss = mac_ctx->vdev_type_nss_2g.tdls; + + nss = QDF_MIN(nss, mac_ctx->user_configured_nss); + + /* compute the matching MCS rate set, if peer is 11n capable and self mode is 11n */ +#ifdef FEATURE_WLAN_TDLS + if (stads->mlmStaContext.htCapability) +#else + if (IS_DOT11_MODE_HT(session_entry->dot11mode) && + (stads->mlmStaContext.htCapability)) +#endif + { + val_len = SIZE_OF_SUPPORTED_MCS_SET; + if (wlan_mlme_get_cfg_str( + mcsSet, + &mac_ctx->mlme_cfg->rates.supported_mcs_set, + &val_len) != QDF_STATUS_SUCCESS) { + /* Could not get rateset from CFG. Log error. */ + pe_err("could not retrieve supportedMCSSet"); + return QDF_STATUS_E_FAILURE; + } + + if (NSS_1x1_MODE == nss) + mcsSet[1] = 0; + for (i = 0; i < val_len; i++) + stads->supportedRates.supportedMCSSet[i] = + mcsSet[i] & supp_mcs_set[i]; + + pe_debug("MCS Rate Set Bitmap from CFG and DPH"); + for (i = 0; i < SIR_MAC_MAX_SUPPORTED_MCS_SET; i++) { + pe_debug("%x %x", mcsSet[i], + stads->supportedRates.supportedMCSSet[i]); + } + } + lim_populate_vht_mcs_set(mac_ctx, &stads->supportedRates, vht_caps, + session_entry, nss, NULL); + /** + * Set the erpEnabled bit if the phy is in G mode and at least + * one A rate is supported + */ + if ((phymode == WNI_CFG_PHY_MODE_11G) && is_a_rate) + stads->erpEnabled = eHAL_SET; + + return QDF_STATUS_SUCCESS; +} + +/* + * update HASH node entry info + */ +static void lim_tdls_update_hash_node_info(struct mac_context *mac, + tDphHashNode *sta, + struct tdls_add_sta_req *add_sta_req, + struct pe_session *pe_session) +{ + tDot11fIEHTCaps htCap = {0,}; + tDot11fIEHTCaps *htCaps; + tDot11fIEVHTCaps *pVhtCaps = NULL; + tDot11fIEVHTCaps *pVhtCaps_txbf = NULL; + tDot11fIEVHTCaps vhtCap; + uint8_t cbMode; + + if (add_sta_req->tdls_oper == TDLS_OPER_ADD) { + populate_dot11f_ht_caps(mac, pe_session, &htCap); + } else if (add_sta_req->tdls_oper == TDLS_OPER_UPDATE) { + lim_tdls_populate_dot11f_ht_caps(mac, NULL, + add_sta_req, &htCap); + sta->rmfEnabled = add_sta_req->is_pmf; + } + htCaps = &htCap; + if (htCaps->present) { + sta->mlmStaContext.htCapability = 1; + sta->htGreenfield = htCaps->greenField; + /* + * sta->htSupportedChannelWidthSet should have the base + * channel capability. The htSupportedChannelWidthSet of the + * TDLS link on base channel should be less than or equal to + * channel width of STA-AP link. So take this setting from the + * pe_session. + */ + pe_debug("supportedChannelWidthSet: 0x%x htSupportedChannelWidthSet: 0x%x", + htCaps->supportedChannelWidthSet, + pe_session->htSupportedChannelWidthSet); + sta->htSupportedChannelWidthSet = + (htCaps->supportedChannelWidthSet < + pe_session->htSupportedChannelWidthSet) ? + htCaps->supportedChannelWidthSet : + pe_session->htSupportedChannelWidthSet; + pe_debug("sta->htSupportedChannelWidthSet: 0x%x", + sta->htSupportedChannelWidthSet); + + sta->ch_width = sta->htSupportedChannelWidthSet; + sta->htMIMOPSState = htCaps->mimoPowerSave; + sta->htMaxAmsduLength = htCaps->maximalAMSDUsize; + sta->htAMpduDensity = htCaps->mpduDensity; + sta->htDsssCckRate40MHzSupport = htCaps->dsssCckMode40MHz; + sta->htShortGI20Mhz = htCaps->shortGI20MHz; + sta->htShortGI40Mhz = htCaps->shortGI40MHz; + sta->htMaxRxAMpduFactor = htCaps->maxRxAMPDUFactor; + lim_fill_rx_highest_supported_rate(mac, + &sta->supportedRates. + rxHighestDataRate, + htCaps->supportedMCSSet); + sta->ht_caps = add_sta_req->ht_cap.hc_cap; + } else { + sta->mlmStaContext.htCapability = 0; + } + lim_tdls_populate_dot11f_vht_caps(mac, add_sta_req, &vhtCap); + pVhtCaps = &vhtCap; + if (pVhtCaps->present) { + sta->mlmStaContext.vhtCapability = 1; + + /* + * 11.21.1 General: The channel width of the TDLS direct + * link on the base channel shall not exceed the channel + * width of the BSS to which the TDLS peer STAs are + * associated. + */ + if (pe_session->ch_width) + sta->vhtSupportedChannelWidthSet = + pe_session->ch_width - 1; + else + sta->vhtSupportedChannelWidthSet = + pe_session->ch_width; + + pe_debug("vhtSupportedChannelWidthSet: %hu htSupportedChannelWidthSet: %hu", + sta->vhtSupportedChannelWidthSet, + sta->htSupportedChannelWidthSet); + + sta->vhtLdpcCapable = pVhtCaps->ldpcCodingCap; + sta->vhtBeamFormerCapable = 0; + pVhtCaps_txbf = (tDot11fIEVHTCaps *) (&add_sta_req->vht_cap); + pVhtCaps_txbf->suBeamformeeCap = 0; + pVhtCaps_txbf->suBeamFormerCap = 0; + pVhtCaps_txbf->muBeamformerCap = 0; + pVhtCaps_txbf->muBeamformeeCap = 0; + sta->vht_caps = add_sta_req->vht_cap.vht_capinfo; + } else { + sta->mlmStaContext.vhtCapability = 0; + sta->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ; + } + + /* + * Calculate the Secondary Coannel Offset if our + * own channel bonding state is enabled + */ + if (pe_session->htSupportedChannelWidthSet) { + cbMode = lim_select_cb_mode(sta, pe_session, + wlan_reg_freq_to_chan( + mac->pdev, pe_session->curr_op_freq), + sta->vhtSupportedChannelWidthSet); + + if (sta->mlmStaContext.vhtCapability) + sta->htSecondaryChannelOffset = + lim_get_htcb_state(cbMode); + else + sta->htSecondaryChannelOffset = cbMode; + } + /* Lets enable QOS parameter */ + sta->qosMode = (add_sta_req->capability & CAPABILITIES_QOS_OFFSET) + || add_sta_req->htcap_present; + sta->wmeEnabled = 1; + sta->lleEnabled = 0; + /* TDLS Dummy AddSTA does not have qosInfo , is it OK ?? + */ + sta->qos.capability.qosInfo = + (*(tSirMacQosInfoStation *) &add_sta_req->uapsd_queues); + + /* populate matching rate set */ + + /* TDLS Dummy AddSTA does not have HTCap,VHTCap,Rates info , is it OK ?? + */ + lim_tdls_populate_matching_rate_set(mac, sta, + add_sta_req->supported_rates, + add_sta_req->supported_rates_length, + add_sta_req->ht_cap.mcsset, + pe_session, pVhtCaps); + + /* TDLS Dummy AddSTA does not have right capability , is it OK ?? + */ + sta->mlmStaContext.capabilityInfo = + (*(tSirMacCapabilityInfo *) &add_sta_req->capability); + + return; +} + +/* + * Add STA for TDLS setup procedure + */ +static QDF_STATUS lim_tdls_setup_add_sta(struct mac_context *mac, + struct tdls_add_sta_req *pAddStaReq, + struct pe_session *pe_session) +{ + tpDphHashNode sta = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint16_t aid = 0; + + sta = dph_lookup_hash_entry(mac, pAddStaReq->peermac.bytes, &aid, + &pe_session->dph.dphHashTable); + if (!sta && pAddStaReq->tdls_oper == TDLS_OPER_UPDATE) { + pe_err("TDLS update peer is given without peer creation"); + return QDF_STATUS_E_FAILURE; + } + if (sta && pAddStaReq->tdls_oper == TDLS_OPER_ADD) { + pe_err("TDLS entry for peer: "QDF_MAC_ADDR_FMT " already exist, cannot add new entry", + QDF_MAC_ADDR_REF(pAddStaReq->peermac.bytes)); + return QDF_STATUS_E_FAILURE; + } + + if (sta && sta->staType != STA_ENTRY_TDLS_PEER) { + pe_err("Non TDLS entry for peer: "QDF_MAC_ADDR_FMT " already exist", + QDF_MAC_ADDR_REF(pAddStaReq->peermac.bytes)); + return QDF_STATUS_E_FAILURE; + } + + if (!sta) { + aid = lim_assign_peer_idx(mac, pe_session); + + if (!aid) { + pe_err("No more free AID for peer: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pAddStaReq->peermac.bytes)); + return QDF_STATUS_E_FAILURE; + } + + /* Set the aid in peerAIDBitmap as it has been assigned to TDLS peer */ + SET_PEER_AID_BITMAP(pe_session->peerAIDBitmap, aid); + + pe_debug("Aid: %d, for peer: " QDF_MAC_ADDR_FMT, + aid, QDF_MAC_ADDR_REF(pAddStaReq->peermac.bytes)); + sta = + dph_get_hash_entry(mac, aid, + &pe_session->dph.dphHashTable); + + if (sta) { + (void)lim_del_sta(mac, sta, false /*asynchronous */, + pe_session); + lim_delete_dph_hash_entry(mac, sta->staAddr, aid, + pe_session); + } + + sta = dph_add_hash_entry(mac, pAddStaReq->peermac.bytes, + aid, &pe_session->dph.dphHashTable); + + if (!sta) { + pe_err("add hash entry failed"); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + } + + lim_tdls_update_hash_node_info(mac, sta, pAddStaReq, pe_session); + + sta->staType = STA_ENTRY_TDLS_PEER; + + status = + lim_add_sta(mac, sta, + (pAddStaReq->tdls_oper == + TDLS_OPER_UPDATE) ? true : false, pe_session); + + if (QDF_STATUS_SUCCESS != status) { + /* should not fail */ + QDF_ASSERT(0); + } + return status; +} + +/* + * Del STA, after Link is teardown or discovery response sent on direct link + */ +static QDF_STATUS lim_tdls_del_sta(struct mac_context *mac, + struct qdf_mac_addr peerMac, + struct pe_session *pe_session, + bool resp_reqd) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint16_t peerIdx = 0; + tpDphHashNode sta; + + sta = dph_lookup_hash_entry(mac, peerMac.bytes, &peerIdx, + &pe_session->dph.dphHashTable); + + if (sta && sta->staType == STA_ENTRY_TDLS_PEER) + status = lim_del_sta(mac, sta, resp_reqd, pe_session); + else + pe_debug("TDLS peer "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(peerMac.bytes)); + + return status; +} + +/* + * Once Link is setup with PEER, send Add STA ind to SME + */ +static QDF_STATUS lim_send_sme_tdls_add_sta_rsp(struct mac_context *mac, + uint8_t sessionId, + tSirMacAddr peerMac, + uint8_t updateSta, + tDphHashNode *sta, uint8_t status) +{ + struct scheduler_msg msg = { 0 }; + struct tdls_add_sta_rsp *add_sta_rsp; + QDF_STATUS ret; + + msg.type = eWNI_SME_TDLS_ADD_STA_RSP; + + add_sta_rsp = qdf_mem_malloc(sizeof(*add_sta_rsp)); + if (!add_sta_rsp) + return QDF_STATUS_E_NOMEM; + + add_sta_rsp->session_id = sessionId; + add_sta_rsp->status_code = status; + + if (peerMac) { + qdf_mem_copy(add_sta_rsp->peermac.bytes, + (uint8_t *) peerMac, QDF_MAC_ADDR_SIZE); + } + if (updateSta) + add_sta_rsp->tdls_oper = TDLS_OPER_UPDATE; + else + add_sta_rsp->tdls_oper = TDLS_OPER_ADD; + + add_sta_rsp->psoc = mac->psoc; + msg.bodyptr = add_sta_rsp; + msg.callback = tgt_tdls_add_peer_rsp; + + ret = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(ret)) { + pe_err("post msg fail, %d", ret); + qdf_mem_free(add_sta_rsp); + } + + return ret; +} + +/* + * STA RSP received from HAL + */ +QDF_STATUS lim_process_tdls_add_sta_rsp(struct mac_context *mac, void *msg, + struct pe_session *pe_session) +{ + tAddStaParams *pAddStaParams = (tAddStaParams *) msg; + uint8_t status = QDF_STATUS_SUCCESS; + tDphHashNode *sta = NULL; + uint16_t aid = 0; + + SET_LIM_PROCESS_DEFD_MESGS(mac, true); + pe_debug("staMac: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pAddStaParams->staMac)); + + if (pAddStaParams->status != QDF_STATUS_SUCCESS) { + QDF_ASSERT(0); + pe_err("Add sta failed "); + status = QDF_STATUS_E_FAILURE; + goto add_sta_error; + } + + sta = dph_lookup_hash_entry(mac, pAddStaParams->staMac, &aid, + &pe_session->dph.dphHashTable); + if (!sta) { + pe_err("sta is NULL "); + status = QDF_STATUS_E_FAILURE; + goto add_sta_error; + } + + sta->mlmStaContext.mlmState = eLIM_MLM_LINK_ESTABLISHED_STATE; + sta->valid = 1; +add_sta_error: + status = lim_send_sme_tdls_add_sta_rsp(mac, pe_session->smeSessionId, + pAddStaParams->staMac, + pAddStaParams->updateSta, sta, + status); + qdf_mem_free(pAddStaParams); + return status; +} + +/** + * lim_send_tdls_comp_mgmt_rsp() - Send Response to upper layers + * @mac_ctx: Pointer to Global MAC structure + * @msg_type: Indicates message type + * @result_code: Indicates the result of previously issued + * eWNI_SME_msg_type_REQ message + * @vdev_id: vdev id + * + * This function is called by lim_process_sme_req_messages() to send + * eWNI_SME_START_RSP, eWNI_SME_STOP_BSS_RSP + * or eWNI_SME_SWITCH_CHL_RSP messages to applications above MAC + * Software. + * + * Return: None + */ + +static void +lim_send_tdls_comp_mgmt_rsp(struct mac_context *mac_ctx, uint16_t msg_type, + tSirResultCodes result_code, uint8_t vdev_id) +{ + struct scheduler_msg msg = {0}; + struct tdls_send_mgmt_rsp *sme_rsp; + QDF_STATUS status; + + pe_debug("Sending message %s with reasonCode %s", + lim_msg_str(msg_type), lim_result_code_str(result_code)); + + sme_rsp = qdf_mem_malloc(sizeof(*sme_rsp)); + if (!sme_rsp) + return; + + sme_rsp->status_code = (enum legacy_result_code)result_code; + sme_rsp->vdev_id = vdev_id; + sme_rsp->psoc = mac_ctx->psoc; + + msg.type = msg_type; + msg.bodyptr = sme_rsp; + msg.callback = tgt_tdls_send_mgmt_rsp; + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("post msg fail, %d", status); + qdf_mem_free(sme_rsp); + } +} + +QDF_STATUS lim_process_sme_tdls_mgmt_send_req(struct mac_context *mac_ctx, + void *msg) +{ + struct tdls_send_mgmt_request *send_req = msg; + struct pe_session *session_entry; + uint8_t session_id; + uint16_t ie_len; + tSirResultCodes result_code = eSIR_SME_INVALID_PARAMETERS; + + pe_debug("Send Mgmt Received"); + session_entry = pe_find_session_by_bssid(mac_ctx, + send_req->bssid.bytes, + &session_id); + if (!session_entry) { + pe_err("PE Session does not exist for given sme session_id %d", + send_req->session_id); + goto lim_tdls_send_mgmt_error; + } + + /* check if we are in proper state to work as TDLS client */ + if (!LIM_IS_STA_ROLE(session_entry)) { + pe_err("send mgmt received in wrong system Role: %d", + GET_LIM_SYSTEM_ROLE(session_entry)); + goto lim_tdls_send_mgmt_error; + } + + if (lim_is_roam_synch_in_progress(session_entry)) { + pe_err("roaming in progress, reject mgmt! for session %d", + send_req->session_id); + result_code = eSIR_SME_REFUSED; + goto lim_tdls_send_mgmt_error; + } + + /* + * if we are still good, go ahead and check if we are in proper state to + * do TDLS discovery req/rsp/....frames. + */ + if ((session_entry->limSmeState != eLIM_SME_ASSOCIATED_STATE) && + (session_entry->limSmeState != eLIM_SME_LINK_EST_STATE)) { + pe_err("send mgmt received in invalid LIMsme state: %d", + session_entry->limSmeState); + goto lim_tdls_send_mgmt_error; + } + + cds_tdls_tx_rx_mgmt_event(ACTION_CATEGORY_TDLS, + SIR_MAC_ACTION_TX, SIR_MAC_MGMT_ACTION, + send_req->req_type, send_req->peer_mac.bytes); + + ie_len = send_req->length - sizeof(*send_req); + + switch (send_req->req_type) { + case TDLS_DISCOVERY_REQUEST: + pe_debug("Transmit Discovery Request Frame"); + /* format TDLS discovery request frame and transmit it */ + lim_send_tdls_dis_req_frame(mac_ctx, send_req->peer_mac, + send_req->dialog, session_entry, + send_req->ac); + result_code = eSIR_SME_SUCCESS; + break; + case TDLS_DISCOVERY_RESPONSE: + pe_debug("Transmit Discovery Response Frame"); + /* Send a response mgmt action frame */ + lim_send_tdls_dis_rsp_frame(mac_ctx, send_req->peer_mac, + send_req->dialog, session_entry, + send_req->add_ie, ie_len); + result_code = eSIR_SME_SUCCESS; + break; + case TDLS_SETUP_REQUEST: + pe_debug("Transmit Setup Request Frame"); + lim_send_tdls_link_setup_req_frame(mac_ctx, + send_req->peer_mac, + send_req->dialog, + session_entry, + send_req->add_ie, ie_len, + send_req->ac); + result_code = eSIR_SME_SUCCESS; + break; + case TDLS_SETUP_RESPONSE: + pe_debug("Transmit Setup Response Frame"); + lim_send_tdls_setup_rsp_frame(mac_ctx, + send_req->peer_mac, + send_req->dialog, session_entry, + send_req->status_code, + send_req->add_ie, ie_len, + send_req->ac); + result_code = eSIR_SME_SUCCESS; + break; + case TDLS_SETUP_CONFIRM: + pe_debug("Transmit Setup Confirm Frame"); + lim_send_tdls_link_setup_cnf_frame(mac_ctx, + send_req->peer_mac, + send_req->dialog, + send_req->peer_capability, + session_entry, + send_req->add_ie, ie_len, + send_req->ac); + result_code = eSIR_SME_SUCCESS; + break; + case TDLS_TEARDOWN: + pe_debug("Transmit Teardown Frame"); + lim_send_tdls_teardown_frame(mac_ctx, + send_req->peer_mac, + send_req->status_code, + send_req->responder, + session_entry, + send_req->add_ie, ie_len, + send_req->ac); + result_code = eSIR_SME_SUCCESS; + break; + case TDLS_PEER_TRAFFIC_INDICATION: + break; + case TDLS_CHANNEL_SWITCH_REQUEST: + break; + case TDLS_CHANNEL_SWITCH_RESPONSE: + break; + case TDLS_PEER_TRAFFIC_RESPONSE: + break; + default: + break; + } + +lim_tdls_send_mgmt_error: + lim_send_tdls_comp_mgmt_rsp(mac_ctx, eWNI_SME_TDLS_SEND_MGMT_RSP, + result_code, send_req->session_id); + + return QDF_STATUS_SUCCESS; +} + +/* + * Once link is teardown, send Del Peer Ind to SME + */ +static QDF_STATUS lim_send_sme_tdls_del_sta_rsp(struct mac_context *mac, + uint8_t sessionId, + struct qdf_mac_addr peerMac, + tDphHashNode *sta, uint8_t status) +{ + struct scheduler_msg msg = { 0 }; + struct tdls_del_sta_rsp *del_sta_rsp; + QDF_STATUS ret; + + msg.type = eWNI_SME_TDLS_DEL_STA_RSP; + + del_sta_rsp = qdf_mem_malloc(sizeof(*del_sta_rsp)); + if (!del_sta_rsp) + return QDF_STATUS_E_NOMEM; + + del_sta_rsp->session_id = sessionId; + del_sta_rsp->status_code = status; + + qdf_copy_macaddr(&del_sta_rsp->peermac, &peerMac); + + del_sta_rsp->psoc = mac->psoc; + msg.bodyptr = del_sta_rsp; + msg.callback = tgt_tdls_del_peer_rsp; + ret = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(ret)) { + pe_err("post msg fail, %d", ret); + qdf_mem_free(del_sta_rsp); + } + + return ret; +} + +QDF_STATUS lim_process_sme_tdls_add_sta_req(struct mac_context *mac, + void *msg) +{ + struct tdls_add_sta_req *add_sta_req = msg; + struct pe_session *pe_session; + uint8_t session_id; + + pe_debug("TDLS Add STA Request Received"); + pe_session = + pe_find_session_by_bssid(mac, add_sta_req->bssid.bytes, + &session_id); + if (!pe_session) { + pe_err("PE Session does not exist for given sme sessionId: %d", + add_sta_req->session_id); + goto lim_tdls_add_sta_error; + } + + /* check if we are in proper state to work as TDLS client */ + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("send mgmt received in wrong system Role: %d", + GET_LIM_SYSTEM_ROLE(pe_session)); + goto lim_tdls_add_sta_error; + } + + if (lim_is_roam_synch_in_progress(pe_session)) { + pe_err("roaming in progress, reject add sta! for session %d", + add_sta_req->session_id); + goto lim_tdls_add_sta_error; + } + + /* + * if we are still good, go ahead and check if we are in proper state to + * do TDLS discovery req/rsp/....frames. + */ + if ((pe_session->limSmeState != eLIM_SME_ASSOCIATED_STATE) && + (pe_session->limSmeState != eLIM_SME_LINK_EST_STATE)) { + pe_err("send mgmt received in invalid LIMsme state: %d", + pe_session->limSmeState); + goto lim_tdls_add_sta_error; + } + + + /* To start with, send add STA request to HAL */ + if (QDF_STATUS_E_FAILURE == lim_tdls_setup_add_sta(mac, add_sta_req, pe_session)) { + pe_err("Add TDLS Station request failed"); + goto lim_tdls_add_sta_error; + } + return QDF_STATUS_SUCCESS; +lim_tdls_add_sta_error: + lim_send_sme_tdls_add_sta_rsp(mac, + add_sta_req->session_id, + add_sta_req->peermac.bytes, + (add_sta_req->tdls_oper == TDLS_OPER_UPDATE), + NULL, QDF_STATUS_E_FAILURE); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_process_sme_tdls_del_sta_req(struct mac_context *mac, + void *msg) +{ + struct tdls_del_sta_req *del_sta_req = msg; + struct pe_session *pe_session; + uint8_t session_id; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + pe_debug("TDLS Delete STA Request Received"); + pe_session = + pe_find_session_by_bssid(mac, del_sta_req->bssid.bytes, + &session_id); + if (!pe_session) { + pe_err("PE Session does not exist for given vdev id: %d", + del_sta_req->session_id); + lim_send_sme_tdls_del_sta_rsp(mac, del_sta_req->session_id, + del_sta_req->peermac, NULL, + QDF_STATUS_E_FAILURE); + return QDF_STATUS_E_FAILURE; + } + + /* check if we are in proper state to work as TDLS client */ + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_err("Del sta received in wrong system Role %d", + GET_LIM_SYSTEM_ROLE(pe_session)); + goto lim_tdls_del_sta_error; + } + + if (lim_is_roam_synch_in_progress(pe_session)) { + pe_err("roaming in progress, reject del sta! for session %d", + del_sta_req->session_id); + lim_send_sme_tdls_del_sta_rsp(mac, del_sta_req->session_id, + del_sta_req->peermac, NULL, + QDF_STATUS_E_FAILURE); + return QDF_STATUS_E_FAILURE; + } + + /* + * if we are still good, go ahead and check if we are in proper state to + * do TDLS discovery req/rsp/....frames. + */ + if ((pe_session->limSmeState != eLIM_SME_ASSOCIATED_STATE) && + (pe_session->limSmeState != eLIM_SME_LINK_EST_STATE)) { + + pe_err("Del Sta received in invalid LIMsme state: %d", + pe_session->limSmeState); + goto lim_tdls_del_sta_error; + } + + status = lim_tdls_del_sta(mac, del_sta_req->peermac, + pe_session, true); + if (status == QDF_STATUS_SUCCESS) + return status; + +lim_tdls_del_sta_error: + lim_send_sme_tdls_del_sta_rsp(mac, pe_session->smeSessionId, + del_sta_req->peermac, NULL, QDF_STATUS_E_FAILURE); + + return status; +} + +/** + * lim_check_aid_and_delete_peer() - Function to check aid and delete peer + * @p_mac: pointer to mac context + * @session_entry: pointer to PE session + * + * This function verifies aid and delete's peer with that aid from hash table + * + * Return: None + */ +static void lim_check_aid_and_delete_peer(struct mac_context *p_mac, + struct pe_session *session_entry) +{ + tpDphHashNode stads = NULL; + int i, aid; + size_t aid_bitmap_size = sizeof(session_entry->peerAIDBitmap); + struct qdf_mac_addr mac_addr; + QDF_STATUS status; + /* + * Check all the set bit in peerAIDBitmap and delete the peer + * (with that aid) entry from the hash table and add the aid + * in free pool + */ + for (i = 0; i < aid_bitmap_size / sizeof(uint32_t); i++) { + for (aid = 0; aid < (sizeof(uint32_t) << 3); aid++) { + if (!CHECK_BIT(session_entry->peerAIDBitmap[i], aid)) + continue; + stads = dph_get_hash_entry(p_mac, + (aid + i * (sizeof(uint32_t) << 3)), + &session_entry->dph.dphHashTable); + + if (!stads) + goto skip; + + pe_debug("Deleting "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(stads->staAddr)); + + if (!lim_is_roam_synch_in_progress(session_entry)) { + lim_send_deauth_mgmt_frame(p_mac, + eSIR_MAC_DEAUTH_LEAVING_BSS_REASON, + stads->staAddr, session_entry, false); + } + /* Delete TDLS peer */ + qdf_mem_copy(mac_addr.bytes, stads->staAddr, + QDF_MAC_ADDR_SIZE); + + status = lim_tdls_del_sta(p_mac, mac_addr, + session_entry, false); + + dph_delete_hash_entry(p_mac, + stads->staAddr, stads->assocId, + &session_entry->dph.dphHashTable); +skip: + lim_release_peer_idx(p_mac, + (aid + i * (sizeof(uint32_t) << 3)), + session_entry); + CLEAR_BIT(session_entry->peerAIDBitmap[i], aid); + } + } +} + +void lim_update_tdls_set_state_for_fw(struct pe_session *session_entry, + bool value) +{ + session_entry->tdls_send_set_state_disable = value; +} + +/** + * lim_delete_tdls_peers() - delete tdls peers + * + * @mac_ctx - global MAC context + * @session_entry - PE session entry + * + * Delete all the TDLS peer connected before leaving the BSS + * + * Return: QDF_STATUS_SUCCESS on success, error code otherwise + */ +QDF_STATUS lim_delete_tdls_peers(struct mac_context *mac_ctx, + struct pe_session *session_entry) +{ + + if (!session_entry) { + pe_err("NULL session_entry"); + return QDF_STATUS_E_FAILURE; + } + + lim_check_aid_and_delete_peer(mac_ctx, session_entry); + + tgt_tdls_delete_all_peers_indication(mac_ctx->psoc, + session_entry->smeSessionId); + + if (lim_is_roam_synch_in_progress(session_entry)) + return QDF_STATUS_SUCCESS; + /* In case of CSA, Only peers in lim and TDLS component + * needs to be removed and set state disable command + * should not be sent to fw as there is no way to enable + * TDLS in FW after vdev restart. + */ + if (session_entry->tdls_send_set_state_disable) { + tgt_tdls_peers_deleted_notification(mac_ctx->psoc, + session_entry-> + smeSessionId); + } + + /* reset the set_state_disable flag */ + session_entry->tdls_send_set_state_disable = true; + return QDF_STATUS_SUCCESS; +} + +/** + * lim_process_sme_del_all_tdls_peers(): process delete tdls peers + * @p_mac: pointer to mac context + * @msg_buf: message buffer + * + * This function processes request to delete tdls peers + * + * Return: Success: QDF_STATUS_SUCCESS Failure: Error value + */ +QDF_STATUS lim_process_sme_del_all_tdls_peers(struct mac_context *p_mac, + uint32_t *msg_buf) +{ + struct tdls_del_all_tdls_peers *msg; + struct pe_session *session_entry; + uint8_t session_id; + + msg = (struct tdls_del_all_tdls_peers *)msg_buf; + if (!msg) { + pe_err("NULL msg"); + return QDF_STATUS_E_FAILURE; + } + + session_entry = pe_find_session_by_bssid(p_mac, + msg->bssid.bytes, &session_id); + if (!session_entry) { + pe_debug("NULL pe_session"); + return QDF_STATUS_E_FAILURE; + } + + lim_check_aid_and_delete_peer(p_mac, session_entry); + + tgt_tdls_peers_deleted_notification(p_mac->psoc, + session_entry->smeSessionId); + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_process_tdls_del_sta_rsp() - Handle WDA_DELETE_STA_RSP for TDLS + * @mac_ctx: Global MAC context + * @lim_msg: LIM message + * @pe_session: PE session + * + * Return: None + */ +void lim_process_tdls_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg, + struct pe_session *session_entry) +{ + tpDeleteStaParams del_sta_params = (tpDeleteStaParams) lim_msg->bodyptr; + tpDphHashNode sta_ds; + uint16_t peer_idx = 0; + struct qdf_mac_addr peer_mac; + + if (!del_sta_params) { + pe_err("del_sta_params is NULL"); + return; + } + + qdf_mem_copy(peer_mac.bytes, + del_sta_params->staMac, QDF_MAC_ADDR_SIZE); + + sta_ds = dph_lookup_hash_entry(mac_ctx, del_sta_params->staMac, + &peer_idx, &session_entry->dph.dphHashTable); + if (!sta_ds) { + pe_err("DPH Entry for STA: %X is missing release the serialization command", + DPH_STA_HASH_INDEX_PEER); + lim_send_sme_tdls_del_sta_rsp(mac_ctx, + session_entry->smeSessionId, + peer_mac, NULL, + QDF_STATUS_SUCCESS); + goto skip_event; + } + + if (QDF_STATUS_SUCCESS != del_sta_params->status) { + pe_err("DEL STA failed!"); + lim_send_sme_tdls_del_sta_rsp(mac_ctx, + session_entry->smeSessionId, + peer_mac, NULL, QDF_STATUS_E_FAILURE); + goto skip_event; + } + + pe_debug("DEL STA success"); + + /* now send indication to SME-->HDD->TL to remove STA from TL */ + + lim_send_sme_tdls_del_sta_rsp(mac_ctx, session_entry->smeSessionId, + peer_mac, sta_ds, + QDF_STATUS_SUCCESS); + lim_release_peer_idx(mac_ctx, sta_ds->assocId, session_entry); + + /* Clear the aid in peerAIDBitmap as this aid is now in freepool */ + CLEAR_PEER_AID_BITMAP(session_entry->peerAIDBitmap, + sta_ds->assocId); + lim_delete_dph_hash_entry(mac_ctx, sta_ds->staAddr, sta_ds->assocId, + session_entry); + +skip_event: + qdf_mem_free(del_sta_params); + lim_msg->bodyptr = NULL; +} + + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_prop_exts_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_prop_exts_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..e55cf3f88f5ed028b8550c50caa99cab2a3075f6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_prop_exts_utils.c @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_prop_exts_utils.cc contains the utility functions + * to populate, parse proprietary extensions required to + * support ANI feature set. + * + * Author: Chandra Modumudi + * Date: 11/27/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#include "ani_global.h" +#include "wni_cfg.h" +#include "sir_common.h" +#include "sir_debug.h" +#include "utils_api.h" +#include "lim_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_prop_exts_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_trace.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "wma.h" +#include "wlan_utility.h" + +#ifdef FEATURE_WLAN_ESE +/** + * get_local_power_constraint_probe_response() - extracts local constraint + * from probe response + * @beacon_struct: beacon structure + * @local_constraint: local constraint pointer + * @session: A pointer to session entry. + * + * Return: None + */ +static void get_local_power_constraint_probe_response( + tpSirProbeRespBeacon beacon_struct, + int8_t *local_constraint, + struct pe_session *session) +{ + if (beacon_struct->eseTxPwr.present) + *local_constraint = + beacon_struct->eseTxPwr.power_limit; +} + +/** + * get_ese_version_ie_probe_response() - extracts ESE version IE + * from probe response + * @beacon_struct: beacon structure + * @session: A pointer to session entry. + * + * Return: None + */ +static void get_ese_version_ie_probe_response(struct mac_context *mac_ctx, + tpSirProbeRespBeacon beacon_struct, + struct pe_session *session) +{ + if (mac_ctx->mlme_cfg->lfr.ese_enabled) + session->is_ese_version_ie_present = + beacon_struct->is_ese_ver_ie_present; +} +#else +static void get_local_power_constraint_probe_response( + tpSirProbeRespBeacon beacon_struct, + int8_t *local_constraint, + struct pe_session *session) +{ + +} + +static inline void get_ese_version_ie_probe_response(struct mac_context *mac_ctx, + tpSirProbeRespBeacon beacon_struct, + struct pe_session *session) +{ +} +#endif + +#ifdef WLAN_FEATURE_11AX +static void lim_extract_he_op(struct pe_session *session, + tSirProbeRespBeacon *beacon_struct) +{ + uint8_t fw_vht_ch_wd; + uint8_t ap_bcon_ch_width; + uint8_t center_freq_diff; + + if (!session->he_capable) + return; + if (!beacon_struct->he_op.present) { + pe_debug("HE op not present in beacon"); + return; + } + qdf_mem_copy(&session->he_op, &beacon_struct->he_op, + sizeof(session->he_op)); + pe_debug("he_op.bss_color %d", session->he_op.bss_color); + pe_debug("he_op.default_pe %d", session->he_op.default_pe); + if (!session->he_6ghz_band) + return; + if (!session->he_op.oper_info_6g_present) { + pe_debug("6GHz op not present in 6G beacon"); + return; + } + session->ch_width = session->he_op.oper_info_6g.info.ch_width; + session->ch_center_freq_seg0 = + session->he_op.oper_info_6g.info.center_freq_seg0; + session->ch_center_freq_seg1 = + session->he_op.oper_info_6g.info.center_freq_seg1; + + pe_debug("6G op info: ch_wd %d cntr_freq_seg0 %d cntr_freq_seg1 %d", + session->ch_width, session->ch_center_freq_seg0, + session->ch_center_freq_seg1); + + if (!session->ch_center_freq_seg1) + return; + + fw_vht_ch_wd = wma_get_vht_ch_width(); + if (fw_vht_ch_wd <= WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) { + session->ch_width = CH_WIDTH_80MHZ; + session->ch_center_freq_seg1 = 0; + return; + } + center_freq_diff = abs(session->ch_center_freq_seg1 - + session->ch_center_freq_seg0); + if (center_freq_diff == 8) { + ap_bcon_ch_width = CH_WIDTH_160MHZ; + } else if (center_freq_diff > 16) { + ap_bcon_ch_width = CH_WIDTH_80P80MHZ; + } else { + session->ch_width = CH_WIDTH_80MHZ; + session->ch_center_freq_seg1 = 0; + return; + } + + if ((ap_bcon_ch_width == CH_WIDTH_80P80MHZ) && + (fw_vht_ch_wd != WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ)) { + session->ch_width = CH_WIDTH_80MHZ; + session->ch_center_freq_seg1 = 0; + } +} + +static void lim_check_is_he_mcs_valid(struct pe_session *session, + tSirProbeRespBeacon *beacon_struct) +{ + uint8_t i; + uint16_t mcs_map; + + if (!session->he_capable || !beacon_struct->he_cap.present) + return; + + mcs_map = beacon_struct->he_cap.rx_he_mcs_map_lt_80; + for (i = 0; i < session->nss; i++) { + if (((mcs_map >> (i * 2)) & 0x3) != 0x3) + return; + } + session->he_capable = false; + pe_err("AP does not have valid MCS map"); + if (session->vhtCapability) { + session->dot11mode = MLME_DOT11_MODE_11AC; + pe_debug("Update dot11mode to 11ac"); + } else { + session->dot11mode = MLME_DOT11_MODE_11N; + pe_debug("Update dot11mode to 11N"); + } +} + +void lim_update_he_bw_cap_mcs(struct pe_session *session, + tSirProbeRespBeacon *beacon) +{ + uint8_t is_80mhz; + + if (!session->he_capable) + return; + + if ((session->opmode == QDF_STA_MODE || + session->opmode == QDF_P2P_CLIENT_MODE) && + beacon && beacon->he_cap.present) { + if (!beacon->he_cap.chan_width_2) + is_80mhz = 1; + else if (beacon->he_cap.chan_width_2 && + (*(uint16_t *)beacon->he_cap.rx_he_mcs_map_160 == + HE_MCS_ALL_DISABLED)) + is_80mhz = 1; + else + is_80mhz = 0; + } else { + is_80mhz = 1; + } + + if (session->ch_width <= CH_WIDTH_80MHZ && is_80mhz) { + session->he_config.chan_width_2 = 0; + session->he_config.chan_width_3 = 0; + } else if (session->ch_width == CH_WIDTH_160MHZ) { + session->he_config.chan_width_3 = 0; + } + /* Reset the > 20MHz caps for 20MHz connection */ + if (session->ch_width == CH_WIDTH_20MHZ) { + session->he_config.chan_width_0 = 0; + session->he_config.chan_width_1 = 0; + session->he_config.chan_width_2 = 0; + session->he_config.chan_width_3 = 0; + session->he_config.chan_width_4 = 0; + session->he_config.chan_width_5 = 0; + session->he_config.chan_width_6 = 0; + session->he_config.he_ppdu_20_in_40Mhz_2G = 0; + session->he_config.he_ppdu_20_in_160_80p80Mhz = 0; + session->he_config.he_ppdu_80_in_160_80p80Mhz = 0; + } + if (WLAN_REG_IS_24GHZ_CH_FREQ(session->curr_op_freq)) { + session->he_config.chan_width_1 = 0; + session->he_config.chan_width_2 = 0; + session->he_config.chan_width_3 = 0; + session->he_config.chan_width_5 = 0; + session->he_config.chan_width_6 = 0; + } else { + session->he_config.chan_width_0 = 0; + session->he_config.chan_width_4 = 0; + session->he_config.chan_width_6 = 0; + } + if (!session->he_config.chan_width_2) { + session->he_config.bfee_sts_gt_80 = 0; + session->he_config.num_sounding_gt_80 = 0; + session->he_config.he_ppdu_20_in_160_80p80Mhz = 0; + session->he_config.he_ppdu_80_in_160_80p80Mhz = 0; + *(uint16_t *)session->he_config.rx_he_mcs_map_160 = + HE_MCS_ALL_DISABLED; + *(uint16_t *)session->he_config.tx_he_mcs_map_160 = + HE_MCS_ALL_DISABLED; + } + if (!session->he_config.chan_width_3) { + *(uint16_t *)session->he_config.rx_he_mcs_map_80_80 = + HE_MCS_ALL_DISABLED; + *(uint16_t *)session->he_config.tx_he_mcs_map_80_80 = + HE_MCS_ALL_DISABLED; + } +} +#else +static inline void lim_extract_he_op(struct pe_session *session, + tSirProbeRespBeacon *beacon_struct) +{} +static void lim_check_is_he_mcs_valid(struct pe_session *session, + tSirProbeRespBeacon *beacon_struct) +{ +} +#endif + +void lim_objmgr_update_vdev_nss(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t nss) +{ + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) { + pe_err("vdev not found for id: %d", vdev_id); + return; + } + wlan_vdev_obj_lock(vdev); + wlan_vdev_mlme_set_nss(vdev, nss); + wlan_vdev_obj_unlock(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); +} + +#ifdef WLAN_ADAPTIVE_11R +/** + * lim_extract_adaptive_11r_cap() - check if the AP has adaptive 11r + * IE + * @ie: Pointer to the IE + * @ie_len: ie Length + * + * Return: True if adaptive 11r IE is present + */ +static bool lim_extract_adaptive_11r_cap(uint8_t *ie, uint16_t ie_len) +{ + const uint8_t *adaptive_ie; + uint8_t data; + bool adaptive_11r; + + adaptive_ie = wlan_get_vendor_ie_ptr_from_oui(LIM_ADAPTIVE_11R_OUI, + LIM_ADAPTIVE_11R_OUI_SIZE, + ie, ie_len); + if (!adaptive_ie) + return false; + + if ((adaptive_ie[1] < (OUI_LENGTH + 1)) || + (adaptive_ie[1] > MAX_ADAPTIVE_11R_IE_LEN)) + return false; + + data = *(adaptive_ie + OUI_LENGTH + 2); + adaptive_11r = (data & 0x1) ? true : false; + + return adaptive_11r; +} + +#else +static inline bool lim_extract_adaptive_11r_cap(uint8_t *ie, uint16_t ie_len) +{ + return false; +} +#endif + +static +void lim_update_ch_width_for_p2p_client(struct mac_context *mac, + struct pe_session *session, + uint32_t ch_freq) +{ + struct ch_params ch_params = {0}; + + /* + * Some IOT AP's/P2P-GO's (e.g. make: Wireless-AC 9560160MHz as P2P GO), + * send beacon with 20mhz and assoc resp with 80mhz and + * after assoc resp, next beacon also has 80mhz. + * Connection is expected to happen in better possible + * bandwidth(80MHz in this case). + * Start the vdev with max supported ch_width in order to support this. + * It'll be downgraded to appropriate ch_width or the same would be + * continued based on assoc resp. + * Restricting this check for p2p client and 5G only and this may be + * extended to STA based on wider testing results with multiple AP's. + * Limit it to 80MHz as 80+80 is channel specific and 160MHz is not + * supported in p2p. + */ + ch_params.ch_width = CH_WIDTH_80MHZ; + + wlan_reg_set_channel_params_for_freq(mac->pdev, ch_freq, 0, &ch_params); + if (ch_params.ch_width == CH_WIDTH_20MHZ) + ch_params.sec_ch_offset = PHY_SINGLE_CHANNEL_CENTERED; + + session->htSupportedChannelWidthSet = ch_params.sec_ch_offset ? 1 : 0; + session->htRecommendedTxWidthSet = session->htSupportedChannelWidthSet; + session->htSecondaryChannelOffset = ch_params.sec_ch_offset; + session->ch_width = ch_params.ch_width; + session->ch_center_freq_seg0 = ch_params.center_freq_seg0; + session->ch_center_freq_seg1 = ch_params.center_freq_seg1; + pe_debug("Start P2P_CLI in ch freq %d max supported ch_width: %u cbmode: %u seg0: %u, seg1: %u", + ch_freq, ch_params.ch_width, ch_params.sec_ch_offset, + session->ch_center_freq_seg0, session->ch_center_freq_seg1); +} + +void lim_extract_ap_capability(struct mac_context *mac_ctx, uint8_t *p_ie, + uint16_t ie_len, uint8_t *qos_cap, + uint8_t *uapsd, int8_t *local_constraint, + struct pe_session *session) +{ + tSirProbeRespBeacon *beacon_struct; + uint8_t ap_bcon_ch_width; + bool new_ch_width_dfn = false; + tDot11fIEVHTOperation *vht_op; + uint8_t fw_vht_ch_wd; + uint8_t vht_ch_wd; + uint8_t center_freq_diff; + struct s_ext_cap *ext_cap; + uint8_t chan_center_freq_seg1; + tDot11fIEVHTCaps *vht_caps; + uint8_t channel = 0; + struct mlme_vht_capabilities_info *mlme_vht_cap; + + beacon_struct = qdf_mem_malloc(sizeof(tSirProbeRespBeacon)); + if (!beacon_struct) + return; + + *qos_cap = 0; + *uapsd = 0; + pe_debug("The IE's being received:"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + p_ie, ie_len); + if (sir_parse_beacon_ie(mac_ctx, beacon_struct, p_ie, + (uint32_t) ie_len) != QDF_STATUS_SUCCESS) { + pe_err("sir_parse_beacon_ie failed to parse beacon"); + qdf_mem_free(beacon_struct); + return; + } + + mlme_vht_cap = &mac_ctx->mlme_cfg->vht_caps.vht_cap_info; + if (beacon_struct->wmeInfoPresent || + beacon_struct->wmeEdcaPresent || + beacon_struct->HTCaps.present) + LIM_BSS_CAPS_SET(WME, *qos_cap); + if (LIM_BSS_CAPS_GET(WME, *qos_cap) + && beacon_struct->wsmCapablePresent) + LIM_BSS_CAPS_SET(WSM, *qos_cap); + if (beacon_struct->HTCaps.present) + mac_ctx->lim.htCapabilityPresentInBeacon = 1; + else + mac_ctx->lim.htCapabilityPresentInBeacon = 0; + + vht_op = &beacon_struct->VHTOperation; + vht_caps = &beacon_struct->VHTCaps; + if (IS_BSS_VHT_CAPABLE(beacon_struct->VHTCaps) && + vht_op->present && + session->vhtCapability) { + session->vhtCapabilityPresentInBeacon = 1; + if (((beacon_struct->Vendor1IEPresent && + beacon_struct->vendor_vht_ie.present && + beacon_struct->Vendor3IEPresent)) && + (((beacon_struct->VHTCaps.txMCSMap & VHT_MCS_3x3_MASK) == + VHT_MCS_3x3_MASK) && + ((beacon_struct->VHTCaps.txMCSMap & VHT_MCS_2x2_MASK) != + VHT_MCS_2x2_MASK))) + session->vht_config.su_beam_formee = 0; + } else { + session->vhtCapabilityPresentInBeacon = 0; + } + + if (session->vhtCapabilityPresentInBeacon == 1 && + !session->htSupportedChannelWidthSet) { + if (!mac_ctx->mlme_cfg->vht_caps.vht_cap_info.enable_txbf_20mhz) + session->vht_config.su_beam_formee = 0; + + if (session->opmode == QDF_P2P_CLIENT_MODE && + !wlan_reg_is_24ghz_ch_freq(beacon_struct->chan_freq)) + lim_update_ch_width_for_p2p_client( + mac_ctx, session, + beacon_struct->chan_freq); + + } else if (session->vhtCapabilityPresentInBeacon && + vht_op->chanWidth) { + /* If VHT is supported min 80 MHz support is must */ + ap_bcon_ch_width = vht_op->chanWidth; + if (vht_caps->vht_extended_nss_bw_cap) { + if (!vht_caps->extended_nss_bw_supp) + chan_center_freq_seg1 = + vht_op->chan_center_freq_seg1; + else + chan_center_freq_seg1 = + beacon_struct->HTInfo.chan_center_freq_seg2; + } else { + chan_center_freq_seg1 = vht_op->chan_center_freq_seg1; + } + if (chan_center_freq_seg1 && + (ap_bcon_ch_width == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ)) { + new_ch_width_dfn = true; + if (chan_center_freq_seg1 > + vht_op->chan_center_freq_seg0) + center_freq_diff = chan_center_freq_seg1 - + vht_op->chan_center_freq_seg0; + else + center_freq_diff = vht_op->chan_center_freq_seg0 - + chan_center_freq_seg1; + if (center_freq_diff == 8) + ap_bcon_ch_width = + WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ; + else if (center_freq_diff > 16) + ap_bcon_ch_width = + WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ; + else + ap_bcon_ch_width = + WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ; + } + + fw_vht_ch_wd = wma_get_vht_ch_width(); + vht_ch_wd = QDF_MIN(fw_vht_ch_wd, ap_bcon_ch_width); + + if ((vht_ch_wd > WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) && + (ap_bcon_ch_width == + WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ) && + mlme_vht_cap->restricted_80p80_bw_supp) { + if ((chan_center_freq_seg1 == 138 && + vht_op->chan_center_freq_seg0 == 155) || + (vht_op->chan_center_freq_seg0 == 138 && + chan_center_freq_seg1 == 155)) + vht_ch_wd = + WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ; + else + vht_ch_wd = + WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ; + } + /* + * If the supported channel width is greater than 80MHz and + * AP supports Nss > 1 in 160MHz mode then connect the STA + * in 2x2 80MHz mode instead of connecting in 160MHz mode. + */ + if ((vht_ch_wd > WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) && + mac_ctx->mlme_cfg->sta.sta_prefer_80mhz_over_160mhz) { + if (!(IS_VHT_NSS_1x1(beacon_struct->VHTCaps.txMCSMap)) + && + (!IS_VHT_NSS_1x1(beacon_struct->VHTCaps.rxMCSMap))) + vht_ch_wd = WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ; + } + /* + * VHT OP IE old definition: + * vht_op->chan_center_freq_seg0: center freq of 80MHz/160MHz/ + * primary 80 in 80+80MHz. + * + * vht_op->chan_center_freq_seg1: center freq of secondary 80 + * in 80+80MHz. + * + * VHT OP IE NEW definition: + * vht_op->chan_center_freq_seg0: center freq of 80MHz/primary + * 80 in 80+80MHz/center freq of the 80 MHz channel segment + * that contains the primary channel in 160MHz mode. + * + * vht_op->chan_center_freq_seg1: center freq of secondary 80 + * in 80+80MHz/center freq of 160MHz. + */ + session->ch_center_freq_seg0 = vht_op->chan_center_freq_seg0; + session->ch_center_freq_seg1 = chan_center_freq_seg1; + channel = wlan_reg_freq_to_chan(mac_ctx->pdev, + beacon_struct->chan_freq); + if (vht_ch_wd == WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) { + /* DUT or AP supports only 160MHz */ + if (ap_bcon_ch_width == + WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) { + /* AP is in 160MHz mode */ + if (!new_ch_width_dfn) { + session->ch_center_freq_seg1 = + vht_op->chan_center_freq_seg0; + session->ch_center_freq_seg0 = + lim_get_80Mhz_center_channel(channel); + } + } else { + /* DUT supports only 160MHz and AP is + * in 80+80 mode + */ + vht_ch_wd = WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ; + session->ch_center_freq_seg1 = 0; + session->ch_center_freq_seg0 = + lim_get_80Mhz_center_channel(channel); + } + } else if (vht_ch_wd == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) { + /* DUT or AP supports only 80MHz */ + session->ch_center_freq_seg1 = 0; + session->ch_center_freq_seg0 = + lim_get_80Mhz_center_channel(channel); + } + session->ch_width = vht_ch_wd + 1; + } + + if (session->vhtCapability && + session->vhtCapabilityPresentInBeacon && + beacon_struct->ext_cap.present) { + ext_cap = (struct s_ext_cap *)beacon_struct->ext_cap.bytes; + session->gLimOperatingMode.present = + ext_cap->oper_mode_notification; + if (ext_cap->oper_mode_notification) { + if (CH_WIDTH_160MHZ > session->ch_width) + session->gLimOperatingMode.chanWidth = + session->ch_width; + else + session->gLimOperatingMode.chanWidth = + CH_WIDTH_160MHZ; + session->gLimOperatingMode.rxNSS = session->nss - 1; + } else { + pe_err("AP does not support op_mode rx"); + } + } + lim_check_is_he_mcs_valid(session, beacon_struct); + lim_extract_he_op(session, beacon_struct); + lim_update_he_bw_cap_mcs(session, beacon_struct); + /* Extract the UAPSD flag from WMM Parameter element */ + if (beacon_struct->wmeEdcaPresent) + *uapsd = beacon_struct->edcaParams.qosInfo.uapsd; + + if (mac_ctx->mlme_cfg->sta.allow_tpc_from_ap) { + if (beacon_struct->powerConstraintPresent) { + *local_constraint -= + beacon_struct->localPowerConstraint. + localPowerConstraints; + } else { + get_local_power_constraint_probe_response( + beacon_struct, local_constraint, session); + } + } + + get_ese_version_ie_probe_response(mac_ctx, beacon_struct, session); + + session->country_info_present = false; + /* Initializing before first use */ + if (beacon_struct->countryInfoPresent) + session->country_info_present = true; + /* Check if Extended caps are present in probe resp or not */ + if (beacon_struct->ext_cap.present) + session->is_ext_caps_present = true; + /* Update HS 2.0 Information Element */ + if (beacon_struct->hs20vendor_ie.present) { + pe_debug("HS20 Indication Element Present, rel#: %u id: %u", + beacon_struct->hs20vendor_ie.release_num, + beacon_struct->hs20vendor_ie.hs_id_present); + qdf_mem_copy(&session->hs20vendor_ie, + &beacon_struct->hs20vendor_ie, + sizeof(tDot11fIEhs20vendor_ie) - + sizeof(beacon_struct->hs20vendor_ie.hs_id)); + if (beacon_struct->hs20vendor_ie.hs_id_present) + qdf_mem_copy(&session->hs20vendor_ie.hs_id, + &beacon_struct->hs20vendor_ie.hs_id, + sizeof(beacon_struct->hs20vendor_ie.hs_id)); + } + + lim_objmgr_update_vdev_nss(mac_ctx->psoc, session->smeSessionId, + session->nss); + + session->is_adaptive_11r_connection = + lim_extract_adaptive_11r_cap(p_ie, ie_len); + qdf_mem_free(beacon_struct); + return; +} /****** end lim_extract_ap_capability() ******/ + +/** + * lim_get_htcb_state + * + ***FUNCTION: + * This routing provides the translation of Airgo Enum to HT enum for determining + * secondary channel offset. + * Airgo Enum is required for backward compatibility purposes. + * + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @return The corresponding HT enumeration + */ +ePhyChanBondState lim_get_htcb_state(ePhyChanBondState aniCBMode) +{ + switch (aniCBMode) { + case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW: + case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED: + case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH: + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + return PHY_DOUBLE_CHANNEL_HIGH_PRIMARY; + case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW: + case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED: + case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH: + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + return PHY_DOUBLE_CHANNEL_LOW_PRIMARY; + case PHY_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED: + return PHY_SINGLE_CHANNEL_CENTERED; + default: + return PHY_SINGLE_CHANNEL_CENTERED; + } +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_prop_exts_utils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_prop_exts_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..d07af6f454c206518d7ce6005f9b18635ba86fa3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_prop_exts_utils.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011-2014, 2016, 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_prop_exts_utils.h contains the definitions + * used by all LIM modules to support proprietary features. + * Author: Chandra Modumudi + * Date: 12/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#ifndef __LIM_PROP_EXTS_UTILS_H +#define __LIM_PROP_EXTS_UTILS_H + +#define LIM_ADAPTIVE_11R_OUI "\x00\x40\x96\x2C" +#define LIM_ADAPTIVE_11R_OUI_SIZE 4 + +/** + * lim_extract_ap_capability() - extract AP's HCF/WME/WSM capability + * @mac_ctx: Pointer to Global MAC structure + * @p_ie: Pointer to starting IE in Beacon/Probe Response + * @ie_len: Length of all IEs combined + * @qos_cap: Bits are set according to capabilities + * @uapsd: pointer to uapsd + * @local_constraint: Pointer to local power constraint. + * @session: A pointer to session entry. + * + * This function is called to extract AP's HCF/WME/WSM capability + * from the IEs received from it in Beacon/Probe Response frames + * + * Return: None + */ +void +lim_extract_ap_capability(struct mac_context *mac_ctx, uint8_t *p_ie, + uint16_t ie_len, uint8_t *qos_cap, uint8_t *uapsd, + int8_t *local_constraint, struct pe_session *session); + +ePhyChanBondState lim_get_htcb_state(ePhyChanBondState aniCBMode); + +/** + * lim_objmgr_update_vdev_nss() - update nss in vdev object + * @psoc: Pointer to Global MAC structure + * @vdev_id: vdev id + * @nss: nss + * + * Return: None + */ +void lim_objmgr_update_vdev_nss(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t nss); + +#endif /* __LIM_PROP_EXTS_UTILS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_reassoc_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_reassoc_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..f5a20f05629dd095378f73b3b21f7bf6da85169f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_reassoc_utils.c @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: lim_reassoc_utils.c + * + * Host based roaming re-association utilities + */ + +#include "cds_api.h" +#include "ani_global.h" +#include "wni_api.h" +#include "sir_common.h" + +#include "wni_cfg.h" + +#include "sch_api.h" +#include "utils_api.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_admit_control.h" +#include "lim_send_messages.h" +#include "lim_ibss_peer_mgmt.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "lim_process_fils.h" + +#include "qdf_types.h" +#include "wma_types.h" +#include "lim_types.h" + +/** + * lim_update_re_assoc_globals() - Update reassoc global data + * @mac: Global MAC context + * @pAssocRsp: Reassociation response data + * @pe_session: PE Session + * + * This function is called to Update the Globals (LIM) during ReAssoc. + * + * Return: None + */ + +void lim_update_re_assoc_globals(struct mac_context *mac, tpSirAssocRsp pAssocRsp, + struct pe_session *pe_session) +{ + /* Update the current Bss Information */ + qdf_mem_copy(pe_session->bssId, + pe_session->limReAssocbssId, sizeof(tSirMacAddr)); + pe_session->curr_op_freq = pe_session->lim_reassoc_chan_freq; + pe_session->htSecondaryChannelOffset = + pe_session->reAssocHtSupportedChannelWidthSet; + pe_session->htRecommendedTxWidthSet = + pe_session->reAssocHtRecommendedTxWidthSet; + pe_session->htSecondaryChannelOffset = + pe_session->reAssocHtSecondaryChannelOffset; + pe_session->limCurrentBssCaps = pe_session->limReassocBssCaps; + pe_session->limCurrentBssQosCaps = + pe_session->limReassocBssQosCaps; + + qdf_mem_copy((uint8_t *) &pe_session->ssId, + (uint8_t *) &pe_session->limReassocSSID, + pe_session->limReassocSSID.length + 1); + + /* Store assigned AID for TIM processing */ + pe_session->limAID = pAssocRsp->aid & 0x3FFF; + /** Set the State Back to ReAssoc Rsp*/ + pe_session->limMlmState = eLIM_MLM_WT_REASSOC_RSP_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + pe_session->limMlmState)); + +} + +/** + * @lim_handle_del_bss_in_re_assoc_context() - DEL BSS during reassociation + * @mac: Global MAC Context + * @sta: Station Hash entry + * @pe_session: PE Session + * + * While Processing the ReAssociation Response Frame in STA, + * a.immediately after receiving the Reassoc Response the RxCleanUp is + * being issued and the end of DelBSS the new BSS is being added. + * + * b. If an AP rejects the ReAssociation (Disassoc/Deauth) with some context + * change, We need to update CSR with ReAssocCNF Response with the + * ReAssoc Fail and the reason Code, that is also being handled in the + * DELBSS context only + * + * Return: None + */ +void lim_handle_del_bss_in_re_assoc_context(struct mac_context *mac, + tpDphHashNode sta, struct pe_session *pe_session) +{ + tLimMlmReassocCnf mlmReassocCnf; + struct bss_description *bss_desc; + /* + * Skipped the DeleteDPH Hash Entry as we need it for the new BSS + * Set the MlmState to IDLE + */ + pe_session->limMlmState = eLIM_MLM_IDLE_STATE; + /* Update PE session Id */ + mlmReassocCnf.sessionId = pe_session->peSessionId; + switch (pe_session->limSmeState) { + case eLIM_SME_WT_REASSOC_STATE: + { + tpSirAssocRsp assocRsp; + tpDphHashNode sta; + QDF_STATUS retStatus = QDF_STATUS_SUCCESS; + tpSchBeaconStruct beacon_struct; + + beacon_struct = qdf_mem_malloc(sizeof(tSchBeaconStruct)); + if (!beacon_struct) { + mlmReassocCnf.resultCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + mlmReassocCnf.protStatusCode = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + lim_delete_dph_hash_entry(mac, pe_session->bssId, + DPH_STA_HASH_INDEX_PEER, pe_session); + goto error; + } + /* Delete the older STA Table entry */ + lim_delete_dph_hash_entry(mac, pe_session->bssId, + DPH_STA_HASH_INDEX_PEER, pe_session); + /* + * Add an entry for AP to hash table + * maintained by DPH module + */ + sta = dph_add_hash_entry(mac, + pe_session->limReAssocbssId, + DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + if (!sta) { + /* Could not add hash table entry */ + pe_err("could not add hash entry at DPH for"); + lim_print_mac_addr(mac, + pe_session->limReAssocbssId, LOGE); + mlmReassocCnf.resultCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + mlmReassocCnf.protStatusCode = eSIR_SME_SUCCESS; + qdf_mem_free(beacon_struct); + goto error; + } + /* + * While Processing the ReAssoc Response Frame the Rsp Frame + * is being stored to be used here for sending ADDBSS + */ + assocRsp = + (tpSirAssocRsp) pe_session->limAssocResponseData; + bss_desc = &pe_session->pLimReAssocReq->bssDescription; + lim_extract_ap_capabilities(mac, + (uint8_t *)bss_desc->ieFields, + lim_get_ielen_from_bss_description(bss_desc), + beacon_struct); + lim_update_assoc_sta_datas(mac, sta, assocRsp, + pe_session, beacon_struct); + lim_update_re_assoc_globals(mac, assocRsp, pe_session); + + if (mac->lim.gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) + lim_decide_sta_protection_on_assoc(mac, + beacon_struct, + pe_session); + if (beacon_struct->erpPresent) { + if (beacon_struct->erpIEInfo.barkerPreambleMode) + pe_session->beaconParams.fShortPreamble = 0; + else + pe_session->beaconParams.fShortPreamble = 1; + } + /* + * updateBss flag is false, as in this case, PE is first + * deleting the existing BSS and then adding a new one + */ + if (QDF_STATUS_SUCCESS != + lim_sta_send_add_bss(mac, assocRsp, beacon_struct, + bss_desc, + false, pe_session)) { + pe_err("Posting ADDBSS in the ReAssocCtx Failed"); + retStatus = QDF_STATUS_E_FAILURE; + } + if (retStatus != QDF_STATUS_SUCCESS) { + mlmReassocCnf.resultCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + mlmReassocCnf.protStatusCode = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + qdf_mem_free(assocRsp); + mac->lim.gLimAssocResponseData = NULL; + qdf_mem_free(beacon_struct); + goto error; + } + qdf_mem_free(assocRsp); + qdf_mem_free(beacon_struct); + pe_session->limAssocResponseData = NULL; + } + break; + default: + pe_err("DelBss in wrong system Role and SME State"); + mlmReassocCnf.resultCode = eSIR_SME_REFUSED; + mlmReassocCnf.protStatusCode = + eSIR_SME_UNEXPECTED_REQ_RESULT_CODE; + goto error; + } + return; +error: + lim_post_sme_message(mac, LIM_MLM_REASSOC_CNF, + (uint32_t *) &mlmReassocCnf); +} + +/** + * @lim_handle_add_bss_in_re_assoc_context() - ADD BSS during reassociation + * @mac: Global MAC Context + * @sta: Station Hash entry + * @pe_session: PE Session + * + * While Processing the ReAssociation Response Frame in STA, + * a. immediately after receiving the Reassoc Response the RxCleanUp is + * being issued and the end of DelBSS the new BSS is being added. + * + * b. If an AP rejects the ReAssociation (Disassoc/Deauth) with some context + * change, We need to update CSR with ReAssocCNF Response with the + * ReAssoc Fail and the reason Code, that is also being handled in the + * DELBSS context only + * + * Return: None + */ +void lim_handle_add_bss_in_re_assoc_context(struct mac_context *mac, + tpDphHashNode sta, struct pe_session *pe_session) +{ + tLimMlmReassocCnf mlmReassocCnf; + /** Skipped the DeleteDPH Hash Entry as we need it for the new BSS*/ + /** Set the MlmState to IDLE*/ + pe_session->limMlmState = eLIM_MLM_IDLE_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + pe_session->limMlmState)); + switch (pe_session->limSmeState) { + case eLIM_SME_WT_REASSOC_STATE: { + tpSirAssocRsp assocRsp; + tpDphHashNode sta; + QDF_STATUS retStatus = QDF_STATUS_SUCCESS; + tSchBeaconStruct *pBeaconStruct; + + pBeaconStruct = + qdf_mem_malloc(sizeof(tSchBeaconStruct)); + if (!pBeaconStruct) { + mlmReassocCnf.resultCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + mlmReassocCnf.protStatusCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + goto Error; + } + /* Get the AP entry from DPH hash table */ + sta = + dph_get_hash_entry(mac, DPH_STA_HASH_INDEX_PEER, + &pe_session->dph.dphHashTable); + if (!sta) { + pe_err("Fail to get STA PEER entry from hash"); + mlmReassocCnf.resultCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + mlmReassocCnf.protStatusCode = eSIR_SME_SUCCESS; + qdf_mem_free(pBeaconStruct); + goto Error; + } + /* + * While Processing the ReAssoc Response Frame the Rsp Frame + * is being stored to be used here for sending ADDBSS + */ + assocRsp = + (tpSirAssocRsp) pe_session->limAssocResponseData; + lim_extract_ap_capabilities(mac, + (uint8_t *) pe_session-> + pLimReAssocReq->bssDescription. + ieFields, + lim_get_ielen_from_bss_description + (&pe_session-> + pLimReAssocReq-> + bssDescription), + pBeaconStruct); + lim_update_assoc_sta_datas(mac, sta, assocRsp, + pe_session, pBeaconStruct); + lim_update_re_assoc_globals(mac, assocRsp, pe_session); + if (mac->lim.gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) + lim_decide_sta_protection_on_assoc(mac, + pBeaconStruct, + pe_session); + + if (pBeaconStruct->erpPresent) { + if (pBeaconStruct->erpIEInfo.barkerPreambleMode) + pe_session->beaconParams. + fShortPreamble = 0; + else + pe_session->beaconParams. + fShortPreamble = 1; + } + + pe_session->isNonRoamReassoc = 1; + if (QDF_STATUS_SUCCESS != + lim_sta_send_add_bss(mac, assocRsp, pBeaconStruct, + &pe_session->pLimReAssocReq-> + bssDescription, true, + pe_session)) { + pe_err("Post ADDBSS in the ReAssocCtxt Failed"); + retStatus = QDF_STATUS_E_FAILURE; + } + if (retStatus != QDF_STATUS_SUCCESS) { + mlmReassocCnf.resultCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + mlmReassocCnf.protStatusCode = + eSIR_MAC_UNSPEC_FAILURE_STATUS; + qdf_mem_free(assocRsp); + mac->lim.gLimAssocResponseData = NULL; + qdf_mem_free(pBeaconStruct); + goto Error; + } + qdf_mem_free(assocRsp); + pe_session->limAssocResponseData = NULL; + qdf_mem_free(pBeaconStruct); + } + break; + default: + pe_err("DelBss in the wrong system Role and SME State"); + mlmReassocCnf.resultCode = eSIR_SME_REFUSED; + mlmReassocCnf.protStatusCode = + eSIR_SME_UNEXPECTED_REQ_RESULT_CODE; + goto Error; + } + return; +Error: + lim_post_sme_message(mac, LIM_MLM_REASSOC_CNF, + (uint32_t *) &mlmReassocCnf); +} + +/** + * lim_is_reassoc_in_progress() - Check if reassoiciation is in progress + * @mac: Global MAC Context + * @pe_session: PE Session + * + * Return: true When STA is waiting for Reassoc response from AP + * else false + */ +bool lim_is_reassoc_in_progress(struct mac_context *mac, struct pe_session *pe_session) +{ + if (!pe_session) + return false; + + if (LIM_IS_STA_ROLE(pe_session) && + (pe_session->limSmeState == eLIM_SME_WT_REASSOC_STATE)) + return true; + + return false; +} + +/** + * lim_add_ft_sta_self()- function to add STA once we have connected with a + * new AP + * @mac_ctx: pointer to global mac structure + * @assoc_id: association id for the station connection + * @session_entry: pe session entr + * + * This function is called to add a STA once we have connected with a new + * AP, that we have performed an FT to. + * + * The Add STA Response is created and now after the ADD Bss Is Successful + * we add the self sta. We update with the association id from the reassoc + * response from the AP. + * + * Return: QDF_STATUS_SUCCESS on success else QDF_STATUS failure codes + */ +QDF_STATUS lim_add_ft_sta_self(struct mac_context *mac_ctx, uint16_t assoc_id, + struct pe_session *session_entry) +{ + tpAddStaParams add_sta_params = NULL; + QDF_STATUS ret_code = QDF_STATUS_SUCCESS; + struct scheduler_msg msg_q = {0}; + tpDphHashNode sta_ds; + + sta_ds = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session_entry->dph.dphHashTable); + + if (!sta_ds) { + pe_err("Could not get hash entry at DPH"); + return QDF_STATUS_E_FAILURE; + } + + add_sta_params = session_entry->ftPEContext.pAddStaReq; + add_sta_params->assocId = assoc_id; + add_sta_params->smesessionId = session_entry->smeSessionId; + + qdf_mem_copy(add_sta_params->supportedRates.supportedMCSSet, + sta_ds->supportedRates.supportedMCSSet, + SIR_MAC_MAX_SUPPORTED_MCS_SET); + + if (lim_is_fils_connection(session_entry)) + add_sta_params->no_ptk_4_way = true; + + msg_q.type = WMA_ADD_STA_REQ; + msg_q.reserved = 0; + msg_q.bodyptr = add_sta_params; + msg_q.bodyval = 0; + + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + "Sending WMA_ADD_STA_REQ (aid %d)", + add_sta_params->assocId); + MTRACE(mac_trace_msg_tx(mac_ctx, session_entry->peSessionId, + msg_q.type)); + + session_entry->limPrevMlmState = session_entry->limMlmState; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, eLIM_MLM_WT_ADD_STA_RSP_STATE)); + session_entry->limMlmState = eLIM_MLM_WT_ADD_STA_RSP_STATE; + ret_code = wma_post_ctrl_msg(mac_ctx, &msg_q); + if (QDF_STATUS_SUCCESS != ret_code) { + pe_err("Posting WMA_ADD_STA_REQ to HAL failed, reason=%X", + ret_code); + qdf_mem_free(add_sta_params); + } + + session_entry->ftPEContext.pAddStaReq = NULL; + return ret_code; +} + +/** + * lim_restore_pre_reassoc_state() - Restore the pre-association context + * @mac: Global MAC Context + * @resultCode: Assoc response result + * @protStatusCode: Internal protocol status code + * @pe_session: PE Session + * + * This function is called on STA role whenever Reasociation + * Response with a reject code is received from AP. + * Reassociation failure timer is stopped, Old (or current) AP's + * context is restored both at Polaris & software + * + * Return: None + */ + +void +lim_restore_pre_reassoc_state(struct mac_context *mac, + tSirResultCodes resultCode, uint16_t protStatusCode, + struct pe_session *pe_session) +{ + tLimMlmReassocCnf mlmReassocCnf; + + pe_debug("sessionid: %d protStatusCode: %d resultCode: %d", + pe_session->smeSessionId, protStatusCode, resultCode); + + pe_session->limMlmState = eLIM_MLM_LINK_ESTABLISHED_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + eLIM_MLM_LINK_ESTABLISHED_STATE)); + + /* 'Change' timer for future activations */ + lim_deactivate_and_change_timer(mac, eLIM_REASSOC_FAIL_TIMER); + + lim_send_switch_chnl_params(mac, pe_session); + + /* @ToDo:Need to Integrate the STOP the Dataxfer to AP from 11H code */ + + mlmReassocCnf.resultCode = resultCode; + mlmReassocCnf.protStatusCode = protStatusCode; + mlmReassocCnf.sessionId = pe_session->peSessionId; + lim_post_sme_message(mac, + LIM_MLM_REASSOC_CNF, (uint32_t *) &mlmReassocCnf); +} + +/** + * lim_post_reassoc_failure() - Post failure message to SME + * @mac: Global MAC Context + * @resultCode: Result Code + * @protStatusCode: Protocol Status Code + * @pe_session: PE Session + * + * Return: None + */ +void lim_post_reassoc_failure(struct mac_context *mac, + tSirResultCodes resultCode, uint16_t protStatusCode, + struct pe_session *pe_session) +{ + tLimMlmReassocCnf mlmReassocCnf; + + pe_session->limMlmState = eLIM_MLM_LINK_ESTABLISHED_STATE; + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + eLIM_MLM_LINK_ESTABLISHED_STATE)); + + lim_deactivate_and_change_timer(mac, eLIM_REASSOC_FAIL_TIMER); + + mlmReassocCnf.resultCode = resultCode; + mlmReassocCnf.protStatusCode = protStatusCode; + mlmReassocCnf.sessionId = pe_session->peSessionId; + lim_post_sme_message(mac, + LIM_MLM_REASSOC_CNF, (uint32_t *) &mlmReassocCnf); +} + diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_roam_timer_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_roam_timer_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..4f0d67471fbd7ba48ae3f088a88663a255c4caf6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_roam_timer_utils.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: lim_roam_timer_utils.c + * + * Host based roaming timers implementation + */ + +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" + +/** + * lim_create_timers_host_roam() - Create timers used in host based roaming + * @mac_ctx: Global MAC context + * + * Create reassoc and preauth timers + * + * Return: TX_SUCCESS or TX_TIMER_ERROR + */ +uint32_t lim_create_timers_host_roam(struct mac_context *mac_ctx) +{ + uint32_t cfg_value; + + cfg_value = SYS_MS_TO_TICKS( + mac_ctx->mlme_cfg->timeouts.reassoc_failure_timeout); + /* Create Association failure timer and activate it later */ + if (tx_timer_create(mac_ctx, + &mac_ctx->lim.lim_timers.gLimReassocFailureTimer, + "REASSOC FAILURE TIMEOUT", lim_assoc_failure_timer_handler, + LIM_REASSOC, cfg_value, 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("failed to create Reassoc timer"); + return TX_TIMER_ERROR; + } + cfg_value = 1000; + cfg_value = SYS_MS_TO_TICKS(cfg_value); + if (tx_timer_create(mac_ctx, + &mac_ctx->lim.lim_timers.gLimFTPreAuthRspTimer, + "FT PREAUTH RSP TIMEOUT", + lim_timer_handler, SIR_LIM_FT_PREAUTH_RSP_TIMEOUT, + cfg_value, 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("failed to create Join fail timer"); + goto err_roam_timer; + } + return TX_SUCCESS; + +err_roam_timer: + tx_timer_delete(&mac_ctx->lim.lim_timers.gLimReassocFailureTimer); + return TX_TIMER_ERROR; +} + +void lim_delete_timers_host_roam(struct mac_context *mac_ctx) +{ + tLimTimers *lim_timer = &mac_ctx->lim.lim_timers; + + /* Delete Reassociation failure timer. */ + tx_timer_delete(&lim_timer->gLimReassocFailureTimer); + /* Delete FT Preauth response timer */ + tx_timer_delete(&lim_timer->gLimFTPreAuthRspTimer); +} + +void lim_deactivate_timers_host_roam(struct mac_context *mac_ctx) +{ + tLimTimers *lim_timer = &mac_ctx->lim.lim_timers; + + /* Deactivate Reassociation failure timer. */ + tx_timer_deactivate(&lim_timer->gLimReassocFailureTimer); + /* Deactivate FT Preauth response timer */ + tx_timer_deactivate(&lim_timer->gLimFTPreAuthRspTimer); +} + +/** + * lim_deactivate_and_change_timer_host_roam() - Change timers in host roaming + * @mac_ctx: Pointer to Global MAC structure + * @timer_id: enum of timer to be deactivated and changed + * + * This function is called to deactivate and change a timer for future + * re-activation for host roaming timers. + * + * Return: None + */ +void lim_deactivate_and_change_timer_host_roam(struct mac_context *mac_ctx, + uint32_t timer_id) +{ + uint32_t val = 0; + + switch (timer_id) { + case eLIM_REASSOC_FAIL_TIMER: + if (tx_timer_deactivate + (&mac_ctx->lim.lim_timers.gLimReassocFailureTimer) != + TX_SUCCESS) + pe_warn("unable to deactivate Reassoc fail timer"); + + val = SYS_MS_TO_TICKS( + mac_ctx->mlme_cfg->timeouts.reassoc_failure_timeout); + if (tx_timer_change + (&mac_ctx->lim.lim_timers.gLimReassocFailureTimer, val, + 0) != TX_SUCCESS) + pe_warn("unable to change Reassoc fail timer"); + break; + + case eLIM_FT_PREAUTH_RSP_TIMER: + if (tx_timer_deactivate + (&mac_ctx->lim.lim_timers.gLimFTPreAuthRspTimer) != + TX_SUCCESS) { + pe_err("Unable to deactivate Preauth Fail timer"); + return; + } + val = 1000; + val = SYS_MS_TO_TICKS(val); + if (tx_timer_change( + &mac_ctx->lim.lim_timers.gLimFTPreAuthRspTimer, + val, 0) != TX_SUCCESS) { + pe_err("Unable to change Join Failure timer"); + return; + } + break; + } +} + diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_scan_result_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_scan_result_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..fc764388c4f596e0dd51fb7cabd587b3eb4369b2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_scan_result_utils.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_scan_result_utils.cc contains the utility functions + * LIM uses for maintaining and accessing scan results on STA. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_api.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "rrm_api.h" +#include "cds_utils.h" + +/** + * lim_collect_bss_description() + * + ***FUNCTION: + * This function is called during scan upon receiving + * Beacon/Probe Response frame to check if the received + * frame matches scan criteria, collect BSS description + * and add it to cached scan results. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param pBPR - Pointer to parsed Beacon/Probe Response structure + * @param pRxPacketInfo - Pointer to Received frame's BD + * @param fScanning - flag to indicate if it is during scan. + * --------------------------------------------- + * + * @return None + */ +void +lim_collect_bss_description(struct mac_context *mac, + struct bss_description *pBssDescr, + tpSirProbeRespBeacon pBPR, + uint8_t *pRxPacketInfo, uint8_t fScanning) +{ + uint8_t *pBody; + uint32_t ieLen = 0; + tpSirMacMgmtHdr pHdr; + uint32_t chan_freq; + uint8_t rfBand = 0; + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + + if (SIR_MAC_B_PR_SSID_OFFSET > WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo)) { + QDF_ASSERT(WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo) >= + SIR_MAC_B_PR_SSID_OFFSET); + return; + } + ieLen = + WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo) - SIR_MAC_B_PR_SSID_OFFSET; + pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); + rfBand = WMA_GET_RX_RFBAND(pRxPacketInfo); + + /** + * Length of BSS desription is without length of + * length itself and length of pointer that holds ieFields. + * + * struct bss_description + * +--------+---------------------------------+---------------+ + * | length | other fields | pointer to IEs| + * +--------+---------------------------------+---------------+ + * ^ + * ieFields + */ + pBssDescr->length = + (uint16_t)(offsetof(struct bss_description, ieFields[0]) - + sizeof(pBssDescr->length) + ieLen); + + /* Copy BSS Id */ + qdf_mem_copy((uint8_t *) &pBssDescr->bssId, + (uint8_t *) pHdr->bssId, sizeof(tSirMacAddr)); + + /* Copy Timestamp, Beacon Interval and Capability Info */ + pBssDescr->scansystimensec = qdf_get_bootbased_boottime_ns(); + + pBssDescr->timeStamp[0] = pBPR->timeStamp[0]; + pBssDescr->timeStamp[1] = pBPR->timeStamp[1]; + pBssDescr->beaconInterval = pBPR->beaconInterval; + pBssDescr->capabilityInfo = + lim_get_u16((uint8_t *) &pBPR->capabilityInfo); + + if (!pBssDescr->beaconInterval) { + pe_warn("Beacon Interval is ZERO, making it to default 100 " + QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(pHdr->bssId)); + pBssDescr->beaconInterval = 100; + } + /* + * There is a narrow window after Channel Switch msg is sent to HAL and before the AGC is shut + * down and beacons/Probe Rsps can trickle in and we may report the incorrect channel in 5Ghz + * band, so not relying on the 'last Scanned Channel' stored in LIM. + * Instead use the value returned by RXP in BD. This the the same value which HAL programs into + * RXP before every channel switch. + * Right now there is a problem in 5Ghz, where we are receiving beacons from a channel different from + * the currently scanned channel. so incorrect channel is reported to CSR and association does not happen. + * So for now we keep on looking for the channel info in the beacon (DSParamSet IE OR HT Info IE), and only if it + * is not present in the beacon, we go for the channel info present in RXP. + * This fix will work for 5Ghz 11n devices, but for 11a devices, we have to rely on RXP routing flag to get the correct channel. + * So The problem of incorrect channel reporting in 5Ghz will still remain for 11a devices. + */ + chan_freq = lim_get_channel_from_beacon(mac, pBPR); + pBssDescr->chan_freq = chan_freq; + + /* set the network type in bss description */ + pBssDescr->nwType = + lim_get_nw_type(mac, chan_freq, SIR_MAC_MGMT_FRAME, pBPR); + + /* Copy RSSI & SINR from BD */ + pBssDescr->rssi = (int8_t) WMA_GET_RX_RSSI_NORMALIZED(pRxPacketInfo); + pBssDescr->rssi_raw = (int8_t) WMA_GET_RX_RSSI_RAW(pRxPacketInfo); + + /* SINR no longer reported by HW */ + pBssDescr->sinr = 0; + pe_debug(QDF_MAC_ADDR_FMT " rssi: normalized: %d, absolute: %d", + QDF_MAC_ADDR_REF(pHdr->bssId), pBssDescr->rssi, + pBssDescr->rssi_raw); + + pBssDescr->received_time = (uint64_t)qdf_mc_timer_get_system_time(); + pBssDescr->tsf_delta = WMA_GET_RX_TSF_DELTA(pRxPacketInfo); + pBssDescr->seq_ctrl = pHdr->seqControl; + + pe_debug("Received %s from BSSID: "QDF_MAC_ADDR_FMT" tsf_delta = %u Seq Num: %x ssid:%.*s, rssi: %d", + pBssDescr->fProbeRsp ? "Probe Rsp" : "Beacon", + QDF_MAC_ADDR_REF(pHdr->bssId), + pBssDescr->tsf_delta, ((pHdr->seqControl.seqNumHi << + HIGH_SEQ_NUM_OFFSET) | pHdr->seqControl.seqNumLo), + pBPR->ssId.length, pBPR->ssId.ssId, pBssDescr->rssi_raw); + + if (fScanning) { + rrm_get_start_tsf(mac, pBssDescr->startTSF); + pBssDescr->parentTSF = WMA_GET_RX_TIMESTAMP(pRxPacketInfo); + } + + /* MobilityDomain */ + pBssDescr->mdie[0] = 0; + pBssDescr->mdie[1] = 0; + pBssDescr->mdie[2] = 0; + pBssDescr->mdiePresent = false; + /* If mdie is present in the probe resp we */ + /* fill it in the bss description */ + if (pBPR->mdiePresent) { + pBssDescr->mdiePresent = true; + pBssDescr->mdie[0] = pBPR->mdie[0]; + pBssDescr->mdie[1] = pBPR->mdie[1]; + pBssDescr->mdie[2] = pBPR->mdie[2]; + } + +#ifdef FEATURE_WLAN_ESE + pBssDescr->QBSSLoad_present = false; + pBssDescr->QBSSLoad_avail = 0; + if (pBPR->QBSSLoad.present) { + pBssDescr->QBSSLoad_present = true; + pBssDescr->QBSSLoad_avail = pBPR->QBSSLoad.avail; + } +#endif + /* Copy IE fields */ + qdf_mem_copy((uint8_t *) &pBssDescr->ieFields, + pBody + SIR_MAC_B_PR_SSID_OFFSET, ieLen); + + /*set channel number in beacon in case it is not present */ + pBPR->chan_freq = chan_freq; + mac->lim.beacon_probe_rsp_cnt_per_scan++; + + return; +} /*** end lim_collect_bss_description() ***/ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_scan_result_utils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_scan_result_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..a483856cf4358cfbd7620905b6b49209837d8863 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_scan_result_utils.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_scan_result_utils.h contains the utility definitions + * LIM uses for maintaining and accessing scan results on STA. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ +#ifndef __LIM_SCAN_UTILS_H +#define __LIM_SCAN_UTILS_H + +#include "parser_api.h" +#include "lim_types.h" + +/* Scan result hash related functions */ +void lim_collect_bss_description(struct mac_context *mac, + struct bss_description *pBssDescr, + tpSirProbeRespBeacon pBPR, + uint8_t *pRxPacketInfo, uint8_t fScanning); +#endif /* __LIM_SCAN_UTILS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_security_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_security_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..d1e0cbd650573b8514b1096f2794f0681018e2d2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_security_utils.c @@ -0,0 +1,763 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_utils.cc contains the utility functions + * LIM uses. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#include "ani_global.h" +#include "wni_api.h" + +#include "sir_common.h" +#include "wni_cfg.h" + +#include "utils_api.h" +#include "lim_utils.h" +#include "lim_security_utils.h" +#include "lim_ft_defs.h" +#include "lim_session.h" + +#define LIM_SEED_LENGTH 16 +/* + * preauth node timeout value in interval of 10msec + */ +#define LIM_OPENAUTH_TIMEOUT 500 + +/** + * lim_is_auth_algo_supported() + * + ***FUNCTION: + * This function is called in various places within LIM code + * to determine whether passed authentication algorithm is enabled + * or not + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param authType Indicates MAC based authentication type + * (eSIR_OPEN_SYSTEM or eSIR_SHARED_KEY) + * If Shared Key authentication to be used, + * 'Privacy Option Implemented' flag is also + * checked. + * + * @return true if passed authType is enabled else false + */ +uint8_t +lim_is_auth_algo_supported(struct mac_context *mac, tAniAuthType authType, + struct pe_session *pe_session) +{ + bool algoEnable, privacyOptImp; + struct wlan_mlme_wep_cfg *wep_params = &mac->mlme_cfg->wep_params; + + if (authType == eSIR_OPEN_SYSTEM) { + + if (LIM_IS_AP_ROLE(pe_session)) { + if ((pe_session->authType == eSIR_OPEN_SYSTEM) + || (pe_session->authType == eSIR_AUTO_SWITCH)) + return true; + else + return false; + } + + algoEnable = wep_params->is_auth_open_system; + return algoEnable > 0 ? true : false; + + } else { + + if (LIM_IS_AP_ROLE(pe_session)) { + if ((pe_session->authType == eSIR_SHARED_KEY) + || (pe_session->authType == eSIR_AUTO_SWITCH)) + algoEnable = true; + else + algoEnable = false; + + } else { + algoEnable = wep_params->is_shared_key_auth; + } + + if (LIM_IS_AP_ROLE(pe_session)) + privacyOptImp = pe_session->privacy; + else + privacyOptImp = wep_params->is_privacy_enabled; + + return algoEnable && privacyOptImp; + } +} /****** end lim_is_auth_algo_supported() ******/ + +/** + * lim_init_pre_auth_list + * + ***FUNCTION: + * This function is called while starting a BSS at AP + * to initialize MAC authenticated STA list. This may also be called + * while joining/starting an IBSS if MAC authentication is allowed + * in IBSS mode. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @return None + */ + +void lim_init_pre_auth_list(struct mac_context *mac) +{ + mac->lim.pLimPreAuthList = NULL; + +} /*** end lim_init_pre_auth_list() ***/ + +/** + * lim_delete_pre_auth_list + * + ***FUNCTION: + * This function is called cleanup Pre-auth list either on + * AP or on STA when moving from one persona to other. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @return None + */ + +void lim_delete_pre_auth_list(struct mac_context *mac) +{ + struct tLimPreAuthNode *pCurrNode, *pTempNode; + + pCurrNode = pTempNode = mac->lim.pLimPreAuthList; + while (pCurrNode) { + pTempNode = pCurrNode->next; + lim_release_pre_auth_node(mac, pCurrNode); + + pCurrNode = pTempNode; + } + mac->lim.pLimPreAuthList = NULL; +} /*** end lim_delete_pre_auth_list() ***/ + +/** + * lim_search_pre_auth_list + * + ***FUNCTION: + * This function is called when Authentication frame is received + * by AP (or at a STA in IBSS supporting MAC based authentication) + * to search if a STA is in the middle of MAC Authentication + * transaction sequence. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param macAddr - MAC address of the STA that sent + * Authentication frame. + * + * @return Pointer to pre-auth node if found, else NULL + */ + +struct tLimPreAuthNode *lim_search_pre_auth_list(struct mac_context *mac, + tSirMacAddr macAddr) +{ + struct tLimPreAuthNode *pTempNode = mac->lim.pLimPreAuthList; + + while (pTempNode) { + if (!qdf_mem_cmp((uint8_t *) macAddr, + (uint8_t *) &pTempNode->peerMacAddr, + sizeof(tSirMacAddr))) + break; + + pTempNode = pTempNode->next; + } + + return pTempNode; +} /*** end lim_search_pre_auth_list() ***/ + +/** + * lim_delete_open_auth_pre_auth_node() - delete any stale preauth nodes + * @mac_ctx: Pointer to Global MAC structure + * + * This function is called to delete any stale preauth nodes on + * receiving authentication frame and existing preauth nodes + * reached the maximum allowed limit. + * + * Return: return true if any preauthnode deleted else false + */ +uint8_t +lim_delete_open_auth_pre_auth_node(struct mac_context *mac_ctx) +{ + struct tLimPreAuthNode *prev_node, *temp_node, *found_node; + uint8_t auth_node_freed = false; + + temp_node = prev_node = mac_ctx->lim.pLimPreAuthList; + + if (!temp_node) + return auth_node_freed; + + while (temp_node) { + if (temp_node->mlmState == eLIM_MLM_AUTHENTICATED_STATE && + temp_node->authType == eSIR_OPEN_SYSTEM && + (qdf_mc_timer_get_system_ticks() > + (LIM_OPENAUTH_TIMEOUT + temp_node->timestamp) || + qdf_mc_timer_get_system_ticks() < temp_node->timestamp)) { + /* Found node to be deleted */ + auth_node_freed = true; + found_node = temp_node; + if (mac_ctx->lim.pLimPreAuthList == temp_node) { + prev_node = mac_ctx->lim.pLimPreAuthList = + temp_node = found_node->next; + } else { + prev_node->next = temp_node->next; + temp_node = prev_node->next; + } + + lim_release_pre_auth_node(mac_ctx, found_node); + } else { + prev_node = temp_node; + temp_node = prev_node->next; + } + } + + return auth_node_freed; +} + +/** + * lim_add_pre_auth_node + * + ***FUNCTION: + * This function is called at AP while sending Authentication + * frame2. + * This may also be called on a STA in IBSS if MAC authentication is + * allowed in IBSS mode. + * + ***LOGIC: + * Node is always added to the front of the list + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param pAuthNode - Pointer to pre-auth node to be added to the list. + * + * @return None + */ + +void lim_add_pre_auth_node(struct mac_context *mac, struct tLimPreAuthNode *pAuthNode) +{ + mac->lim.gLimNumPreAuthContexts++; + + pAuthNode->next = mac->lim.pLimPreAuthList; + + mac->lim.pLimPreAuthList = pAuthNode; +} /*** end lim_add_pre_auth_node() ***/ + +/** + * lim_release_pre_auth_node + * + ***FUNCTION: + * This function is called to realease the acquired + * pre auth node from list. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param pAuthNode - Pointer to Pre Auth node to be released + * @return None + */ + +void lim_release_pre_auth_node(struct mac_context *mac, + tpLimPreAuthNode pAuthNode) +{ + pAuthNode->fFree = 1; + if (pAuthNode->authType == eSIR_AUTH_TYPE_SAE && + pAuthNode->assoc_req.present) { + tpSirAssocReq assoc = + (tpSirAssocReq)pAuthNode->assoc_req.assoc_req; + + if (assoc->assocReqFrameLength) + qdf_mem_free(assoc->assocReqFrame); + qdf_mem_free(assoc); + pAuthNode->assoc_req.assoc_req = NULL; + pAuthNode->assoc_req.present = false; + } + MTRACE(mac_trace + (mac, TRACE_CODE_TIMER_DEACTIVATE, NO_SESSION, + eLIM_PRE_AUTH_CLEANUP_TIMER)); + tx_timer_deactivate(&pAuthNode->timer); + mac->lim.gLimNumPreAuthContexts--; +} /*** end lim_release_pre_auth_node() ***/ + +/** + * lim_delete_pre_auth_node + * + ***FUNCTION: + * This function is called at AP when a pre-authenticated STA is + * Associated/Reassociated or when AuthFrame4 is received after + * Auth Response timeout. + * This may also be called on a STA in IBSS if MAC authentication and + * Association/Reassociation is allowed in IBSS mode. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param peerMacAddr - MAC address of the STA that need to be deleted + * from pre-auth node list. + * + * @return None + */ + +void lim_delete_pre_auth_node(struct mac_context *mac, tSirMacAddr macAddr) +{ + struct tLimPreAuthNode *pPrevNode, *pTempNode; + + pTempNode = pPrevNode = mac->lim.pLimPreAuthList; + + if (!pTempNode) + return; + + if (!qdf_mem_cmp((uint8_t *) macAddr, + (uint8_t *) &pTempNode->peerMacAddr, + sizeof(tSirMacAddr))) { + /* First node to be deleted */ + + mac->lim.pLimPreAuthList = pTempNode->next; + + pe_debug("fRelease data for %d peer "QDF_MAC_ADDR_FMT, + pTempNode->authNodeIdx, + QDF_MAC_ADDR_REF(macAddr)); + lim_release_pre_auth_node(mac, pTempNode); + + return; + } + + pTempNode = pTempNode->next; + + while (pTempNode) { + if (!qdf_mem_cmp((uint8_t *) macAddr, + (uint8_t *) &pTempNode->peerMacAddr, + sizeof(tSirMacAddr))) { + /* Found node to be deleted */ + + pPrevNode->next = pTempNode->next; + + pe_debug("subsequent node to delete, Release data entry: %pK id %d peer", + pTempNode, pTempNode->authNodeIdx); + lim_print_mac_addr(mac, macAddr, LOG1); + lim_release_pre_auth_node(mac, pTempNode); + + return; + } + + pPrevNode = pTempNode; + pTempNode = pTempNode->next; + } + + pe_err("peer not found in pre-auth list, addr= "); + lim_print_mac_addr(mac, macAddr, LOGE); + +} /*** end lim_delete_pre_auth_node() ***/ + +/** + * limRestoreFromPreAuthState + * + ***FUNCTION: + * This function is called on STA whenever an Authentication + * sequence is complete and state prior to auth need to be + * restored. + * + ***LOGIC: + * MLM_AUTH_CNF is prepared and sent to SME state machine. + * In case of restoring from pre-auth: + * - Channel Id is programmed at LO/RF synthesizer + * - BSSID is programmed at RHP + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * @param resultCode - result of authentication attempt + * @return None + */ + +void +lim_restore_from_auth_state(struct mac_context *mac, tSirResultCodes resultCode, + uint16_t protStatusCode, struct pe_session *pe_session) +{ + tSirMacAddr currentBssId; + tLimMlmAuthCnf mlmAuthCnf; + +#ifdef FEATURE_WLAN_DIAG_SUPPORT + lim_diag_event_report(mac, WLAN_PE_DIAG_AUTH_COMP_EVENT, pe_session, + resultCode, protStatusCode); +#endif + + qdf_mem_copy((uint8_t *) &mlmAuthCnf.peerMacAddr, + (uint8_t *) &mac->lim.gpLimMlmAuthReq->peerMacAddr, + sizeof(tSirMacAddr)); + mlmAuthCnf.authType = mac->lim.gpLimMlmAuthReq->authType; + mlmAuthCnf.resultCode = resultCode; + mlmAuthCnf.protStatusCode = protStatusCode; + + /* Update PE session ID */ + mlmAuthCnf.sessionId = pe_session->peSessionId; + + /* / Free up buffer allocated */ + /* / for mac->lim.gLimMlmAuthReq */ + qdf_mem_free(mac->lim.gpLimMlmAuthReq); + mac->lim.gpLimMlmAuthReq = NULL; + + pe_session->limMlmState = pe_session->limPrevMlmState; + + MTRACE(mac_trace + (mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId, + pe_session->limMlmState)); + + /* + * Set the auth_ack_status status flag as success as + * host have received the auth rsp and no longer auth + * retry is needed also cancel the auth rety timer + */ + mac->auth_ack_status = LIM_ACK_RCD_SUCCESS; + + /* Auth retry and AUth failure timers are not started for SAE */ + /* 'Change' timer for future activations */ + if (tx_timer_running(&mac->lim.lim_timers. + g_lim_periodic_auth_retry_timer)) + lim_deactivate_and_change_timer(mac, + eLIM_AUTH_RETRY_TIMER); + /* 'Change' timer for future activations */ + if (tx_timer_running(&mac->lim.lim_timers.gLimAuthFailureTimer)) + lim_deactivate_and_change_timer(mac, + eLIM_AUTH_FAIL_TIMER); + + sir_copy_mac_addr(currentBssId, pe_session->bssId); + + if (pe_session->limSmeState == eLIM_SME_WT_PRE_AUTH_STATE) { + mac->lim.gLimPreAuthChannelNumber = 0; + } + + lim_post_sme_message(mac, LIM_MLM_AUTH_CNF, (uint32_t *) &mlmAuthCnf); +} /*** end lim_restore_from_auth_state() ***/ + +/** + * lim_encrypt_auth_frame() + * + ***FUNCTION: + * This function is called in lim_process_auth_frame() function + * to encrypt Authentication frame3 body. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac Pointer to Global MAC structure + * @param keyId key id to used + * @param pKey Pointer to the key to be used for encryption + * @param pPlainText Pointer to the body to be encrypted + * @param pEncrBody Pointer to the encrypted auth frame body + * @param keyLength 8 (WEP40) or 16 (WEP104) + * @return None + */ + +void +lim_encrypt_auth_frame(struct mac_context *mac, uint8_t keyId, uint8_t *pKey, + uint8_t *pPlainText, uint8_t *pEncrBody, + uint32_t keyLength) +{ + uint8_t seed[LIM_SEED_LENGTH], icv[SIR_MAC_WEP_ICV_LENGTH]; + uint16_t frame_len; + + frame_len = ((tpSirMacAuthFrameBody)pPlainText)->length + + SIR_MAC_AUTH_FRAME_INFO_LEN + SIR_MAC_CHALLENGE_ID_LEN; + keyLength += 3; + + /* + * Make sure that IV is non-zero, because few IOT APs fails to decrypt + * auth sequence 3 encrypted frames if initialization vector value is 0 + */ + qdf_get_random_bytes(seed, SIR_MAC_WEP_IV_LENGTH); + while (!(*(uint32_t *)seed)) + qdf_get_random_bytes(seed, SIR_MAC_WEP_IV_LENGTH); + + /* Bytes 3-7 of seed is key */ + qdf_mem_copy((uint8_t *) &seed[3], pKey, keyLength - 3); + + /* Compute CRC-32 and place them in last 4 bytes of plain text */ + lim_compute_crc32(icv, pPlainText, frame_len); + + qdf_mem_copy(pPlainText + frame_len, + icv, SIR_MAC_WEP_ICV_LENGTH); + + /* Run RC4 on plain text with the seed */ + lim_rc4(pEncrBody + SIR_MAC_WEP_IV_LENGTH, + (uint8_t *) pPlainText, seed, keyLength, + frame_len + SIR_MAC_WEP_ICV_LENGTH); + + /* Prepare IV */ + pEncrBody[0] = seed[0]; + pEncrBody[1] = seed[1]; + pEncrBody[2] = seed[2]; + pEncrBody[3] = keyId << 6; +} /****** end lim_encrypt_auth_frame() ******/ + +/** + * lim_compute_crc32() + * + ***FUNCTION: + * This function is called to compute CRC-32 on a given source. + * Used while encrypting/decrypting Authentication frame 3. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param pDest Destination location for computed CRC + * @param pSrc Source location to be CRC computed + * @param len Length over which CRC to be computed + * @return None + */ + +void lim_compute_crc32(uint8_t *pDest, uint8_t *pSrc, uint16_t len) +{ + uint32_t crc; + int i; + + crc = 0; + crc = ~crc; + + while (len-- > 0) + crc = lim_crc_update(crc, *pSrc++); + + crc = ~crc; + + for (i = 0; i < SIR_MAC_WEP_IV_LENGTH; i++) { + pDest[i] = (uint8_t) crc; + crc >>= 8; + } +} /****** end lim_compute_crc32() ******/ + +/** + * lim_rc4() + * + ***FUNCTION: + * This function is called to run RC4 algorithm. Called while + * encrypting/decrypting Authentication frame 3. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param pDest Destination location for encrypted text + * @param pSrc Source location to be encrypted + * @param seed Contains seed (IV + key) for PRNG + * @param keyLength 8 (WEP40) or 16 (WEP104) + * @param frameLen Length of the frame + * + * @return None + */ + +void +lim_rc4(uint8_t *pDest, uint8_t *pSrc, uint8_t *seed, uint32_t keyLength, + uint16_t frameLen) +{ + typedef struct { + uint8_t i, j; + uint8_t sbox[256]; + } tRC4Context; + + tRC4Context ctx; + + { + uint16_t i, j, k; + + /* */ + /* Initialize sbox using seed */ + /* */ + + ctx.i = ctx.j = 0; + for (i = 0; i < 256; i++) + ctx.sbox[i] = (uint8_t) i; + + j = 0; + k = 0; + for (i = 0; i < 256; i++) { + uint8_t temp; + + if (k < LIM_SEED_LENGTH) + j = (uint8_t) (j + ctx.sbox[i] + seed[k]); + temp = ctx.sbox[i]; + ctx.sbox[i] = ctx.sbox[j]; + ctx.sbox[j] = temp; + + if (++k >= keyLength) + k = 0; + } + } + + { + uint8_t i = ctx.i; + uint8_t j = ctx.j; + uint16_t len = frameLen; + + while (len-- > 0) { + uint8_t temp1, temp2; + + i = (uint8_t) (i + 1); + temp1 = ctx.sbox[i]; + j = (uint8_t) (j + temp1); + + ctx.sbox[i] = temp2 = ctx.sbox[j]; + ctx.sbox[j] = temp1; + + temp1 = (uint8_t) (temp1 + temp2); + temp1 = ctx.sbox[temp1]; + temp2 = (uint8_t) (pSrc ? *pSrc++ : 0); + + *pDest++ = (uint8_t) (temp1 ^ temp2); + } + + ctx.i = i; + ctx.j = j; + } +} /****** end lim_rc4() ******/ + +/** + * lim_decrypt_auth_frame() + * + ***FUNCTION: + * This function is called in lim_process_auth_frame() function + * to decrypt received Authentication frame3 body. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac Pointer to Global MAC structure + * @param pKey Pointer to the key to be used for decryption + * @param pEncrBody Pointer to the body to be decrypted + * @param pPlainBody Pointer to the decrypted body + * @param keyLength 8 (WEP40) or 16 (WEP104) + * + * @return Decrypt result - QDF_STATUS_SUCCESS for success and + * LIM_DECRYPT_ICV_FAIL for ICV mismatch. + * If decryption is a success, pBody will + * have decrypted auth frame body. + */ + +uint8_t +lim_decrypt_auth_frame(struct mac_context *mac, uint8_t *pKey, uint8_t *pEncrBody, + uint8_t *pPlainBody, uint32_t keyLength, uint16_t frameLen) +{ + uint8_t seed[LIM_SEED_LENGTH], icv[SIR_MAC_WEP_ICV_LENGTH]; + int i; + + keyLength += 3; + + /* Bytes 0-2 of seed is received IV */ + qdf_mem_copy((uint8_t *) seed, pEncrBody, SIR_MAC_WEP_IV_LENGTH - 1); + + /* Bytes 3-7 of seed is key */ + qdf_mem_copy((uint8_t *) &seed[3], pKey, keyLength - 3); + + /* Run RC4 on encrypted text with the seed */ + lim_rc4(pPlainBody, + pEncrBody + SIR_MAC_WEP_IV_LENGTH, seed, keyLength, frameLen); + + /* Compute CRC-32 and place them in last 4 bytes of encrypted body */ + lim_compute_crc32(icv, + (uint8_t *) pPlainBody, + (frameLen - SIR_MAC_WEP_ICV_LENGTH)); + + /* Compare RX_ICV with computed ICV */ + for (i = 0; i < SIR_MAC_WEP_ICV_LENGTH; i++) { + pe_debug("computed ICV%d[%x], rxed ICV%d[%x]", + i, icv[i], i, + pPlainBody[frameLen - SIR_MAC_WEP_ICV_LENGTH + i]); + + if (icv[i] != + pPlainBody[frameLen - SIR_MAC_WEP_ICV_LENGTH + i]) + return LIM_DECRYPT_ICV_FAIL; + } + + return QDF_STATUS_SUCCESS; +} /****** end lim_decrypt_auth_frame() ******/ + +/** + * lim_post_sme_set_keys_cnf + * + * A utility API to send MLM_SETKEYS_CNF to SME + */ +void lim_post_sme_set_keys_cnf(struct mac_context *mac, + tLimMlmSetKeysReq *pMlmSetKeysReq, + tLimMlmSetKeysCnf *mlmSetKeysCnf) +{ + /* Prepare and Send LIM_MLM_SETKEYS_CNF */ + qdf_copy_macaddr(&mlmSetKeysCnf->peer_macaddr, + &pMlmSetKeysReq->peer_macaddr); + + /* Free up buffer allocated for mlmSetKeysReq */ + qdf_mem_zero(pMlmSetKeysReq, sizeof(tLimMlmSetKeysReq)); + qdf_mem_free(pMlmSetKeysReq); + mac->lim.gpLimMlmSetKeysReq = NULL; + + lim_post_sme_message(mac, + LIM_MLM_SETKEYS_CNF, (uint32_t *) mlmSetKeysCnf); +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_security_utils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_security_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..620f3ecc5ba4990627516df30a651b51b065557b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_security_utils.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2011-2015, 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_security_utils.h contains the utility definitions + * related to WEP encryption/decryption etc. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ +#ifndef __LIM_SECURITY_UTILS_H +#define __LIM_SECURITY_UTILS_H +#include "sir_mac_prot_def.h" /* for tSirMacAuthFrameBody */ + +#define LIM_ENCR_AUTH_BODY_LEN (SIR_MAC_AUTH_FRAME_INFO_LEN + \ + SIR_MAC_AUTH_CHALLENGE_BODY_LEN + \ + SIR_MAC_WEP_IV_LENGTH + \ + SIR_MAC_WEP_ICV_LENGTH) + +#define LIM_ENCR_AUTH_BODY_LEN_SAP (SIR_MAC_AUTH_FRAME_INFO_LEN + \ + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH + \ + SIR_MAC_CHALLENGE_ID_LEN + \ + SIR_MAC_WEP_IV_LENGTH + \ + SIR_MAC_WEP_ICV_LENGTH) + +#define LIM_ENCR_AUTH_INFO_LEN (SIR_MAC_AUTH_FRAME_INFO_LEN +\ + SIR_MAC_WEP_IV_LENGTH + \ + SIR_MAC_WEP_ICV_LENGTH + \ + SIR_MAC_CHALLENGE_ID_LEN) + + +struct tLimPreAuthNode; + +uint8_t lim_is_auth_algo_supported(struct mac_context *, tAniAuthType, + struct pe_session *); + +/* MAC based authentication related functions */ +void lim_init_pre_auth_list(struct mac_context *); +void lim_delete_pre_auth_list(struct mac_context *); +struct tLimPreAuthNode *lim_search_pre_auth_list(struct mac_context *, + tSirMacAddr); +void lim_add_pre_auth_node(struct mac_context *, struct tLimPreAuthNode *); +void lim_delete_pre_auth_node(struct mac_context *, tSirMacAddr); +void lim_release_pre_auth_node(struct mac_context *mac, + tpLimPreAuthNode pAuthNode); +void lim_restore_from_auth_state(struct mac_context *, + tSirResultCodes, uint16_t, struct pe_session *); +uint8_t lim_delete_open_auth_pre_auth_node(struct mac_context *mac_ctx); + +/* Encryption/Decryption related functions */ +void lim_compute_crc32(uint8_t *, uint8_t *, uint16_t); +void lim_rc4(uint8_t *, uint8_t *, uint8_t *, uint32_t, uint16_t); +void lim_encrypt_auth_frame(struct mac_context *, uint8_t, uint8_t *, uint8_t *, + uint8_t *, uint32_t); +uint8_t lim_decrypt_auth_frame(struct mac_context *, uint8_t *, uint8_t *, + uint8_t *, uint32_t, uint16_t); + +void lim_post_sme_set_keys_cnf(struct mac_context *, tLimMlmSetKeysReq *, + tLimMlmSetKeysCnf *); + +#define PTAPS 0xedb88320 + +static inline uint32_t lim_crc_update(uint32_t crc, uint8_t x) +{ + + /* Update CRC computation for 8 bits contained in x */ + /* */ + uint32_t z; + uint32_t fb; + int i; + + z = crc ^ x; + for (i = 0; i < 8; i++) { + fb = z & 1; + z >>= 1; + if (fb) + z ^= PTAPS; + } + return z; +} + +#endif /* __LIM_SECURITY_UTILS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_frames_host_roam.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_frames_host_roam.c new file mode 100644 index 0000000000000000000000000000000000000000..a70245d746262897e5a16a7dee69e18246b4733c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_frames_host_roam.c @@ -0,0 +1,851 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: lim_send_frames_host_roam.c + * + * Send management frames for host based roaming + */ +#include "sir_api.h" +#include "ani_global.h" +#include "sir_mac_prot_def.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_security_utils.h" +#include "lim_prop_exts_utils.h" +#include "dot11f.h" +#include "sch_api.h" +#include "lim_send_messages.h" +#include "lim_assoc_utils.h" +#include "lim_ft.h" +#ifdef WLAN_FEATURE_11W +#include "wni_cfg.h" +#endif + +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "cds_utils.h" +#include "sme_trace.h" +#include "rrm_api.h" + +#include "wma_types.h" +#include "wlan_utility.h" + +/** + * lim_send_reassoc_req_with_ft_ies_mgmt_frame() - Send Reassoc Req with FTIEs. + * + * @mac_ctx: Handle to mac context + * @mlm_reassoc_req: Original reassoc request + * @pe_session: PE session information + * + * It builds a reassoc request with FT IEs and sends it to AP through WMA. + * Then it creates assoc request and stores it for sending after join + * confirmation. + * + * Return: None + */ +void lim_send_reassoc_req_with_ft_ies_mgmt_frame(struct mac_context *mac_ctx, + tLimMlmReassocReq *mlm_reassoc_req, + struct pe_session *pe_session) +{ + tDot11fReAssocRequest *frm; + uint16_t caps; + uint8_t *frame; + uint32_t bytes, payload, status; + uint8_t qos_enabled, wme_enabled, wsm_enabled; + void *packet; + QDF_STATUS qdf_status; + uint8_t power_caps_populated = false; + uint16_t ft_ies_length = 0; + uint8_t *body; + uint16_t add_ie_len; + uint8_t *add_ie; + const uint8_t *wps_ie = NULL; + uint8_t tx_flag = 0; + uint8_t vdev_id = 0; + bool vht_enabled = false; + tpSirMacMgmtHdr mac_hdr; + tftSMEContext *ft_sme_context; + + if (!pe_session) + return; + + vdev_id = pe_session->vdev_id; + + /* check this early to avoid unncessary operation */ + if (!pe_session->pLimReAssocReq) + return; + + frm = qdf_mem_malloc(sizeof(*frm)); + if (!frm) + goto err; + + add_ie_len = pe_session->pLimReAssocReq->addIEAssoc.length; + add_ie = pe_session->pLimReAssocReq->addIEAssoc.addIEdata; + pe_debug("called in state: %d", pe_session->limMlmState); + + qdf_mem_zero((uint8_t *) frm, sizeof(*frm)); + + caps = mlm_reassoc_req->capabilityInfo; +#if defined(FEATURE_WLAN_WAPI) + /* + * According to WAPI standard: + * 7.3.1.4 Capability Information field + * In WAPI, non-AP STAs within an ESS set the Privacy subfield + * to 0 in transmitted Association or Reassociation management + * frames. APs ignore the Privacy subfield within received + * Association and Reassociation management frames. + */ + if (pe_session->encryptType == eSIR_ED_WPI) + ((tSirMacCapabilityInfo *) &caps)->privacy = 0; +#endif + swap_bit_field16(caps, (uint16_t *) &frm->Capabilities); + + frm->ListenInterval.interval = mlm_reassoc_req->listenInterval; + + /* + * Get the old bssid of the older AP. + * The previous ap bssid is stored in the FT Session + * while creating the PE FT Session for reassociation. + */ + qdf_mem_copy((uint8_t *)frm->CurrentAPAddress.mac, + pe_session->prev_ap_bssid, sizeof(tSirMacAddr)); + + populate_dot11f_ssid2(mac_ctx, &frm->SSID); + populate_dot11f_supp_rates(mac_ctx, POPULATE_DOT11F_RATES_OPERATIONAL, + &frm->SuppRates, pe_session); + + qos_enabled = (pe_session->limQosEnabled) && + SIR_MAC_GET_QOS(pe_session->limReassocBssCaps); + + wme_enabled = (pe_session->limWmeEnabled) && + LIM_BSS_CAPS_GET(WME, pe_session->limReassocBssQosCaps); + + wsm_enabled = (pe_session->limWsmEnabled) && wme_enabled && + LIM_BSS_CAPS_GET(WSM, pe_session->limReassocBssQosCaps); + + if (pe_session->lim11hEnable && + pe_session->pLimReAssocReq->spectrumMgtIndicator == true) { + power_caps_populated = true; + + populate_dot11f_power_caps(mac_ctx, &frm->PowerCaps, + LIM_REASSOC, pe_session); + populate_dot11f_supp_channels(mac_ctx, &frm->SuppChannels, + LIM_REASSOC, pe_session); + } + if (mac_ctx->rrm.rrmPEContext.rrmEnable && + SIR_MAC_GET_RRM(pe_session->limCurrentBssCaps)) { + if (power_caps_populated == false) { + power_caps_populated = true; + populate_dot11f_power_caps(mac_ctx, &frm->PowerCaps, + LIM_REASSOC, pe_session); + } + } + + if (qos_enabled) + populate_dot11f_qos_caps_station(mac_ctx, pe_session, + &frm->QOSCapsStation); + + populate_dot11f_ext_supp_rates(mac_ctx, + POPULATE_DOT11F_RATES_OPERATIONAL, &frm->ExtSuppRates, + pe_session); + + if (mac_ctx->rrm.rrmPEContext.rrmEnable && + SIR_MAC_GET_RRM(pe_session->limCurrentBssCaps)) + populate_dot11f_rrm_ie(mac_ctx, &frm->RRMEnabledCap, pe_session); + + /* + * Ideally this should be enabled for 11r also. But 11r does + * not follow the usual norm of using the Opaque object + * for rsnie and fties. Instead we just add the rsnie and fties + * at the end of the pack routine for 11r. + * This should ideally! be fixed. + */ + /* + * The join request *should* contain zero or one of the WPA and RSN + * IEs. The payload send along with the request is a + * 'struct join_req'; the IE portion is held inside a 'tSirRSNie': + * + * typedef struct sSirRSNie + * { + * uint16_t length; + * uint8_t rsnIEdata[WLAN_MAX_IE_LEN+2]; + * } tSirRSNie, *tpSirRSNie; + * + * So, we should be able to make the following two calls harmlessly, + * since they do nothing if they don't find the given IE in the + * bytestream with which they're provided. + * + * The net effect of this will be to faithfully transmit whatever + * security IE is in the join request. + + * However, if we're associating for the purpose of WPS + * enrollment, and we've been configured to indicate that by + * eliding the WPA or RSN IE, we just skip this: + */ + if (!pe_session->is11Rconnection) { + if (add_ie_len && add_ie) + wps_ie = limGetWscIEPtr(mac_ctx, add_ie, add_ie_len); + if (!wps_ie) { + populate_dot11f_rsn_opaque(mac_ctx, + &(pe_session->pLimReAssocReq->rsnIE), + &frm->RSNOpaque); + populate_dot11f_wpa_opaque(mac_ctx, + &(pe_session->pLimReAssocReq->rsnIE), + &frm->WPAOpaque); + } +#ifdef FEATURE_WLAN_ESE + if (pe_session->pLimReAssocReq->cckmIE.length) { + populate_dot11f_ese_cckm_opaque(mac_ctx, + &(pe_session->pLimReAssocReq->cckmIE), + &frm->ESECckmOpaque); + } +#endif + } +#ifdef FEATURE_WLAN_ESE + /* + * ESE Version IE will be included in re-association request + * when ESE is enabled on DUT through ini and it is also + * advertised by the peer AP to which we are trying to + * associate to. + */ + if (pe_session->is_ese_version_ie_present && + mac_ctx->mlme_cfg->lfr.ese_enabled) + populate_dot11f_ese_version(&frm->ESEVersion); + /* For ESE Associations fill the ESE IEs */ + if (pe_session->isESEconnection && + pe_session->pLimReAssocReq->isESEFeatureIniEnabled) { +#ifndef FEATURE_DISABLE_RM + populate_dot11f_ese_rad_mgmt_cap(&frm->ESERadMgmtCap); +#endif + } +#endif /* FEATURE_WLAN_ESE */ + + /* include WME EDCA IE as well */ + if (wme_enabled) { + populate_dot11f_wmm_info_station_per_session(mac_ctx, + pe_session, &frm->WMMInfoStation); + if (wsm_enabled) + populate_dot11f_wmm_caps(&frm->WMMCaps); +#ifdef FEATURE_WLAN_ESE + if (pe_session->isESEconnection) { + uint32_t phymode; + uint8_t rate; + + populate_dot11f_re_assoc_tspec(mac_ctx, frm, + pe_session); + + /* + * Populate the TSRS IE if TSPEC is included in + * the reassoc request + */ + lim_get_phy_mode(mac_ctx, &phymode, pe_session); + if (phymode == WNI_CFG_PHY_MODE_11G || + phymode == WNI_CFG_PHY_MODE_11A) + rate = TSRS_11AG_RATE_6MBPS; + else + rate = TSRS_11B_RATE_5_5MBPS; + + if (pe_session->pLimReAssocReq->eseTspecInfo. + numTspecs) { + struct ese_tsrs_ie tsrs_ie; + + tsrs_ie.tsid = 0; + tsrs_ie.rates[0] = rate; + populate_dot11_tsrsie(mac_ctx, &tsrs_ie, + &frm->ESETrafStrmRateSet, + sizeof(uint8_t)); + } + } +#endif + } + + ft_sme_context = &mac_ctx->roam.roamSession[vdev_id].ftSmeContext; + if (pe_session->htCapability && + mac_ctx->lim.htCapabilityPresentInBeacon) { + populate_dot11f_ht_caps(mac_ctx, pe_session, &frm->HTCaps); + } + if (pe_session->pLimReAssocReq->bssDescription.mdiePresent && + (ft_sme_context->addMDIE == true) +#if defined FEATURE_WLAN_ESE + && !pe_session->isESEconnection +#endif + ) { + populate_mdie(mac_ctx, &frm->MobilityDomain, + pe_session->pLimReAssocReq->bssDescription.mdie); + } + if (pe_session->vhtCapability && + pe_session->vhtCapabilityPresentInBeacon) { + pe_debug("Populate VHT IEs in Re-Assoc Request"); + populate_dot11f_vht_caps(mac_ctx, pe_session, &frm->VHTCaps); + vht_enabled = true; + populate_dot11f_ext_cap(mac_ctx, vht_enabled, &frm->ExtCap, + pe_session); + } + if (!vht_enabled && + pe_session->is_vendor_specific_vhtcaps) { + pe_debug("Populate Vendor VHT IEs in Re-Assoc Request"); + frm->vendor_vht_ie.present = 1; + frm->vendor_vht_ie.sub_type = + pe_session->vendor_specific_vht_ie_sub_type; + frm->vendor_vht_ie.VHTCaps.present = 1; + populate_dot11f_vht_caps(mac_ctx, pe_session, + &frm->vendor_vht_ie.VHTCaps); + vht_enabled = true; + } + + if (lim_is_session_he_capable(pe_session)) { + pe_debug("Populate HE IEs"); + populate_dot11f_he_caps(mac_ctx, pe_session, + &frm->he_cap); + populate_dot11f_he_6ghz_cap(mac_ctx, pe_session, + &frm->he_6ghz_band_cap); + } + + status = dot11f_get_packed_re_assoc_request_size(mac_ctx, frm, + &payload); + if (DOT11F_FAILED(status)) { + pe_err("Failure in size calculation (0x%08x)", status); + /* We'll fall back on the worst case scenario: */ + payload = sizeof(tDot11fReAssocRequest); + } else if (DOT11F_WARNED(status)) { + pe_warn("Warnings in size calculation (0x%08x)", status); + } + + bytes = payload + sizeof(tSirMacMgmtHdr) + add_ie_len; + + pe_debug("FT IE Reassoc Req %d", + ft_sme_context->reassoc_ft_ies_length); + + if (pe_session->is11Rconnection) + ft_ies_length = ft_sme_context->reassoc_ft_ies_length; + + qdf_status = cds_packet_alloc((uint16_t) bytes + ft_ies_length, + (void **)&frame, (void **)&packet); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_session->limMlmState = pe_session->limPrevMlmState; + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_MLM_STATE, + pe_session->peSessionId, + pe_session->limMlmState)); + pe_err("Failed to alloc memory %d", bytes); + goto end; + } + /* Paranoia: */ + qdf_mem_zero(frame, bytes + ft_ies_length); + + lim_print_mac_addr(mac_ctx, pe_session->limReAssocbssId, LOGD); + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac_ctx, frame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_REASSOC_REQ, pe_session->limReAssocbssId, + pe_session->self_mac_addr); + mac_hdr = (tpSirMacMgmtHdr) frame; + /* That done, pack the ReAssoc Request: */ + status = dot11f_pack_re_assoc_request(mac_ctx, frm, frame + + sizeof(tSirMacMgmtHdr), + payload, &payload); + if (DOT11F_FAILED(status)) { + pe_err("Failure in pack (0x%08x)", status); + cds_packet_free((void *)packet); + goto end; + } else if (DOT11F_WARNED(status)) { + pe_warn("Warnings in pack (0x%08x)", status); + } + + pe_debug("*** Sending Re-Assoc Request length: %d %d to", + bytes, payload); + + if (pe_session->assoc_req) { + qdf_mem_free(pe_session->assoc_req); + pe_session->assoc_req = NULL; + pe_session->assocReqLen = 0; + } + + if (add_ie_len) { + qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload, + add_ie, add_ie_len); + payload += add_ie_len; + } + + pe_session->assoc_req = qdf_mem_malloc(payload); + if (pe_session->assoc_req) { + /* + * Store the Assoc request. This is sent to csr/hdd in + * join cnf response. + */ + qdf_mem_copy(pe_session->assoc_req, + frame + sizeof(tSirMacMgmtHdr), payload); + pe_session->assocReqLen = payload; + } + + if (pe_session->is11Rconnection && ft_sme_context->reassoc_ft_ies) { + int i = 0; + + body = frame + bytes; + for (i = 0; i < ft_ies_length; i++) { + *body = ft_sme_context->reassoc_ft_ies[i]; + body++; + } + } + pe_debug("Re-assoc Req Frame is:"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + (uint8_t *) frame, (bytes + ft_ies_length)); + + if ((pe_session->ftPEContext.pFTPreAuthReq) && + (!wlan_reg_is_24ghz_ch_freq( + pe_session->ftPEContext.pFTPreAuthReq->pre_auth_channel_freq))) + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + else if (wlan_reg_is_5ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + if (pe_session->assoc_req) { + qdf_mem_free(pe_session->assoc_req); + pe_session->assoc_req = NULL; + pe_session->assocReqLen = 0; + } + if (ft_ies_length) { + pe_session->assoc_req = qdf_mem_malloc(ft_ies_length); + if (!pe_session->assoc_req) { + pe_session->assocReqLen = 0; + } else { + /* + * Store the FT IEs. This is sent to csr/hdd in + * join cnf response. + */ + qdf_mem_copy(pe_session->assoc_req, + ft_sme_context->reassoc_ft_ies, + ft_ies_length); + pe_session->assocReqLen = ft_ies_length; + } + } else { + pe_debug("FT IEs not present"); + pe_session->assocReqLen = 0; + } + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, mac_hdr->fc.subType)); + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_REASSOC_START_EVENT, + pe_session, QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); + lim_diag_mgmt_tx_event_report(mac_ctx, mac_hdr, + pe_session, QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); + qdf_status = wma_tx_frame(mac_ctx, packet, + (uint16_t) (bytes + ft_ies_length), + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, 7, + lim_tx_complete, frame, tx_flag, vdev_id, + 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send Re-Assoc Request: %X!", qdf_status); + } + +end: + qdf_mem_free(frm); +err: + /* Free up buffer allocated for mlmAssocReq */ + qdf_mem_free(mlm_reassoc_req); + pe_session->pLimMlmReassocReq = NULL; + +} + +/** + * lim_send_retry_reassoc_req_frame() - Retry for reassociation + * @mac: Global MAC Context + * @pMlmReassocReq: Request buffer to be sent + * @pe_session: PE Session + * + * Return: None + */ +void lim_send_retry_reassoc_req_frame(struct mac_context *mac, + tLimMlmReassocReq *pMlmReassocReq, + struct pe_session *pe_session) +{ + tLimMlmReassocCnf mlmReassocCnf; /* keep sme */ + tLimMlmReassocReq *pTmpMlmReassocReq = NULL; + + if (!pTmpMlmReassocReq) { + pTmpMlmReassocReq = qdf_mem_malloc(sizeof(tLimMlmReassocReq)); + if (!pTmpMlmReassocReq) + goto end; + qdf_mem_copy(pTmpMlmReassocReq, pMlmReassocReq, + sizeof(tLimMlmReassocReq)); + } + /* Prepare and send Reassociation request frame */ + /* start reassoc timer. */ + mac->lim.lim_timers.gLimReassocFailureTimer.sessionId = + pe_session->peSessionId; + /* Start reassociation failure timer */ + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TIMER_ACTIVATE, + pe_session->peSessionId, eLIM_REASSOC_FAIL_TIMER)); + if (tx_timer_activate(&mac->lim.lim_timers.gLimReassocFailureTimer) + != TX_SUCCESS) { + /* Could not start reassoc failure timer. */ + /* Log error */ + pe_err("could not start Reassociation failure timer"); + /* Return Reassoc confirm with */ + /* Resources Unavailable */ + mlmReassocCnf.resultCode = eSIR_SME_RESOURCES_UNAVAILABLE; + mlmReassocCnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + goto end; + } + + lim_send_reassoc_req_with_ft_ies_mgmt_frame(mac, pTmpMlmReassocReq, + pe_session); + return; + +end: + /* Free up buffer allocated for reassocReq */ + if (pMlmReassocReq) { + qdf_mem_free(pMlmReassocReq); + pMlmReassocReq = NULL; + } + if (pTmpMlmReassocReq) { + qdf_mem_free(pTmpMlmReassocReq); + pTmpMlmReassocReq = NULL; + } + mlmReassocCnf.resultCode = eSIR_SME_FT_REASSOC_FAILURE; + mlmReassocCnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS; + /* Update PE sessio Id */ + mlmReassocCnf.sessionId = pe_session->peSessionId; + + lim_post_sme_message(mac, LIM_MLM_REASSOC_CNF, + (uint32_t *) &mlmReassocCnf); +} + +/** + * lim_send_reassoc_req_mgmt_frame() - Send the reassociation frame + * @mac: Global MAC Context + * @pMlmReassocReq: Reassociation request buffer to be sent + * @pe_session: PE Session + * + * Return: None + */ +void lim_send_reassoc_req_mgmt_frame(struct mac_context *mac, + tLimMlmReassocReq *pMlmReassocReq, + struct pe_session *pe_session) +{ + tDot11fReAssocRequest *frm; + uint16_t caps; + uint8_t *pFrame; + uint32_t nBytes, nPayload, nStatus; + uint8_t fQosEnabled, fWmeEnabled, fWsmEnabled; + void *pPacket; + QDF_STATUS qdf_status; + uint16_t nAddIELen; + uint8_t *pAddIE; + const uint8_t *wpsIe = NULL; + uint8_t txFlag = 0; + uint8_t PowerCapsPopulated = false; + uint8_t smeSessionId = 0; + bool isVHTEnabled = false; + tpSirMacMgmtHdr pMacHdr; + + if (!pe_session) + return; + + smeSessionId = pe_session->smeSessionId; + if (!pe_session->pLimReAssocReq) + return; + + frm = qdf_mem_malloc(sizeof(*frm)); + if (!frm) + goto err; + nAddIELen = pe_session->pLimReAssocReq->addIEAssoc.length; + pAddIE = pe_session->pLimReAssocReq->addIEAssoc.addIEdata; + + qdf_mem_zero((uint8_t *) frm, sizeof(*frm)); + + caps = pMlmReassocReq->capabilityInfo; +#if defined(FEATURE_WLAN_WAPI) + /* + * CR: 262463 : + * According to WAPI standard: + * 7.3.1.4 Capability Information field + * In WAPI, non-AP STAs within an ESS set the Privacy subfield to 0 in + * transmitted. Association or Reassociation management frames. APs + * ignore the Privacy subfield within received Association and + * Reassociation management frames. + */ + if (pe_session->encryptType == eSIR_ED_WPI) + ((tSirMacCapabilityInfo *) &caps)->privacy = 0; +#endif + swap_bit_field16(caps, (uint16_t *) &frm->Capabilities); + + frm->ListenInterval.interval = pMlmReassocReq->listenInterval; + + qdf_mem_copy((uint8_t *) frm->CurrentAPAddress.mac, + (uint8_t *) pe_session->bssId, 6); + + populate_dot11f_ssid2(mac, &frm->SSID); + populate_dot11f_supp_rates(mac, POPULATE_DOT11F_RATES_OPERATIONAL, + &frm->SuppRates, pe_session); + + fQosEnabled = (pe_session->limQosEnabled) && + SIR_MAC_GET_QOS(pe_session->limReassocBssCaps); + + fWmeEnabled = (pe_session->limWmeEnabled) && + LIM_BSS_CAPS_GET(WME, pe_session->limReassocBssQosCaps); + + fWsmEnabled = (pe_session->limWsmEnabled) && fWmeEnabled && + LIM_BSS_CAPS_GET(WSM, pe_session->limReassocBssQosCaps); + + if (pe_session->lim11hEnable && + pe_session->pLimReAssocReq->spectrumMgtIndicator == true) { + PowerCapsPopulated = true; + populate_dot11f_power_caps(mac, &frm->PowerCaps, LIM_REASSOC, + pe_session); + populate_dot11f_supp_channels(mac, &frm->SuppChannels, + LIM_REASSOC, pe_session); + } + if (mac->rrm.rrmPEContext.rrmEnable && + SIR_MAC_GET_RRM(pe_session->limCurrentBssCaps)) { + if (PowerCapsPopulated == false) { + PowerCapsPopulated = true; + populate_dot11f_power_caps(mac, &frm->PowerCaps, + LIM_REASSOC, pe_session); + } + } + + if (fQosEnabled) + populate_dot11f_qos_caps_station(mac, pe_session, + &frm->QOSCapsStation); + + populate_dot11f_ext_supp_rates(mac, POPULATE_DOT11F_RATES_OPERATIONAL, + &frm->ExtSuppRates, pe_session); + + if (mac->rrm.rrmPEContext.rrmEnable && + SIR_MAC_GET_RRM(pe_session->limCurrentBssCaps)) + populate_dot11f_rrm_ie(mac, &frm->RRMEnabledCap, pe_session); + /* The join request *should* contain zero or one of the WPA and RSN */ + /* IEs. The payload send along with the request is a */ + /* 'struct join_req'; the IE portion is held inside a 'tSirRSNie': */ + + /* typedef struct sSirRSNie */ + /* { */ + /* uint16_t length; */ + /* uint8_t rsnIEdata[WLAN_MAX_IE_LEN+2]; */ + /* } tSirRSNie, *tpSirRSNie; */ + + /* So, we should be able to make the following two calls harmlessly, */ + /* since they do nothing if they don't find the given IE in the */ + /* bytestream with which they're provided. */ + + /* The net effect of this will be to faithfully transmit whatever */ + /* security IE is in the join request. */ + + /**However*, if we're associating for the purpose of WPS */ + /* enrollment, and we've been configured to indicate that by */ + /* eliding the WPA or RSN IE, we just skip this: */ + if (nAddIELen && pAddIE) + wpsIe = limGetWscIEPtr(mac, pAddIE, nAddIELen); + if (!wpsIe) { + populate_dot11f_rsn_opaque(mac, + &(pe_session->pLimReAssocReq->rsnIE), + &frm->RSNOpaque); + populate_dot11f_wpa_opaque(mac, + &(pe_session->pLimReAssocReq->rsnIE), + &frm->WPAOpaque); +#if defined(FEATURE_WLAN_WAPI) + populate_dot11f_wapi_opaque(mac, + &(pe_session->pLimReAssocReq-> + rsnIE), &frm->WAPIOpaque); +#endif /* defined(FEATURE_WLAN_WAPI) */ + } + /* include WME EDCA IE as well */ + if (fWmeEnabled) { + populate_dot11f_wmm_info_station_per_session(mac, + pe_session, &frm->WMMInfoStation); + + if (fWsmEnabled) + populate_dot11f_wmm_caps(&frm->WMMCaps); + } + + if (pe_session->htCapability && + mac->lim.htCapabilityPresentInBeacon) { + populate_dot11f_ht_caps(mac, pe_session, &frm->HTCaps); + } + if (pe_session->vhtCapability && + pe_session->vhtCapabilityPresentInBeacon) { + pe_warn("Populate VHT IEs in Re-Assoc Request"); + populate_dot11f_vht_caps(mac, pe_session, &frm->VHTCaps); + isVHTEnabled = true; + } + populate_dot11f_ext_cap(mac, isVHTEnabled, &frm->ExtCap, pe_session); + + if (lim_is_session_he_capable(pe_session)) { + pe_debug("Populate HE IEs"); + populate_dot11f_he_caps(mac, pe_session, + &frm->he_cap); + populate_dot11f_he_6ghz_cap(mac, pe_session, + &frm->he_6ghz_band_cap); + } + + nStatus = + dot11f_get_packed_re_assoc_request_size(mac, frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Fail to get size:ReassocReq: (0x%08x)", nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fReAssocRequest); + } else if (DOT11F_WARNED(nStatus)) { + pe_err("warning for size:ReAssoc Req: (0x%08x)", nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr) + nAddIELen; + + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_session->limMlmState = pe_session->limPrevMlmState; + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_MLM_STATE, + pe_session->peSessionId, + pe_session->limMlmState)); + pe_err("Failed to alloc %d bytes for a ReAssociation Req", + nBytes); + goto end; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_REASSOC_REQ, pe_session->limReAssocbssId, + pe_session->self_mac_addr); + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + /* That done, pack the Probe Request: */ + nStatus = dot11f_pack_re_assoc_request(mac, frm, pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Fail to pack a Re-Assoc Req: (0x%08x)", nStatus); + cds_packet_free((void *)pPacket); + goto end; + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("warning packing a Re-AssocReq: (0x%08x)", nStatus); + } + + pe_debug("*** Sending Re-Association Request length: %d" "to", nBytes); + + if (pe_session->assoc_req) { + qdf_mem_free(pe_session->assoc_req); + pe_session->assoc_req = NULL; + pe_session->assocReqLen = 0; + } + + if (nAddIELen) { + qdf_mem_copy(pFrame + sizeof(tSirMacMgmtHdr) + nPayload, + pAddIE, nAddIELen); + nPayload += nAddIELen; + } + + pe_session->assoc_req = qdf_mem_malloc(nPayload); + if (pe_session->assoc_req) { + /* Store the Assocrequest. It is sent to csr in joincnfrsp */ + qdf_mem_copy(pe_session->assoc_req, + pFrame + sizeof(tSirMacMgmtHdr), nPayload); + pe_session->assocReqLen = nPayload; + } + + if (wlan_reg_is_5ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + if (pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_STA_MODE) + txFlag |= HAL_USE_PEER_STA_REQUESTED_MASK; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, pMacHdr->fc.subType)); + lim_diag_event_report(mac, WLAN_PE_DIAG_REASSOC_START_EVENT, + pe_session, QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); + lim_diag_mgmt_tx_event_report(mac, pMacHdr, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + qdf_status = + wma_tx_frame(mac, pPacket, + (uint16_t) (sizeof(tSirMacMgmtHdr) + nPayload), + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, 7, + lim_tx_complete, pFrame, txFlag, smeSessionId, 0, + RATEID_DEFAULT); + MTRACE(qdf_trace + (QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send Re-Association Request: %X!", + qdf_status); + /* Pkt will be freed up by the callback */ + } + +end: + qdf_mem_free(frm); +err: + /* Free up buffer allocated for mlmAssocReq */ + qdf_mem_free(pMlmReassocReq); + pe_session->pLimMlmReassocReq = NULL; + +} + +void lim_process_rx_scan_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, + void *arg) +{ + struct mac_context *mac_ctx; + enum sir_scan_event_type event_type; + + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + "event: %u, id: 0x%x, requestor: 0x%x, freq: %u, reason: %u", + event->type, event->scan_id, event->requester, + event->chan_freq, event->reason); + + mac_ctx = (struct mac_context *)arg; + event_type = 0x1 << event->type; + + qdf_mtrace(QDF_MODULE_ID_SCAN, QDF_MODULE_ID_PE, event->type, + event->vdev_id, event->scan_id); + + switch (event_type) { + case SIR_SCAN_EVENT_STARTED: + break; + case SIR_SCAN_EVENT_COMPLETED: + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + "No.of beacons and probe response received per scan %d", + mac_ctx->lim.beacon_probe_rsp_cnt_per_scan); + /* Fall through */ + case SIR_SCAN_EVENT_FOREIGN_CHANNEL: + case SIR_SCAN_EVENT_START_FAILED: + if ((mac_ctx->lim.req_id | PREAUTH_REQUESTOR_ID) == + event->requester) + lim_preauth_scan_event_handler(mac_ctx, + event_type, + event->vdev_id, + event->scan_id); + break; + case SIR_SCAN_EVENT_BSS_CHANNEL: + case SIR_SCAN_EVENT_DEQUEUED: + case SIR_SCAN_EVENT_PREEMPTED: + default: + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + "Received unhandled scan event %u", + event_type); + } +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_management_frames.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_management_frames.c new file mode 100644 index 0000000000000000000000000000000000000000..7f325e838073b27b1ee773e4651253206c02cc83 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_management_frames.c @@ -0,0 +1,5566 @@ +/* + * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file lim_send_management_frames.c + * + * \brief Code for preparing and sending 802.11 Management frames + * + * + */ + +#include "sir_api.h" +#include "ani_global.h" +#include "sir_mac_prot_def.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_security_utils.h" +#include "lim_prop_exts_utils.h" +#include "dot11f.h" +#include "sch_api.h" +#include "lim_send_messages.h" +#include "lim_assoc_utils.h" +#include "lim_ft.h" +#ifdef WLAN_FEATURE_11W +#include "wni_cfg.h" +#endif + +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "cds_utils.h" +#include "sme_trace.h" +#include "rrm_api.h" +#include "qdf_crypto.h" +#include "parser_api.h" + +#include "wma_types.h" +#include +#include +#include "lim_process_fils.h" +#include "wlan_utility.h" +#include + +/** + * + * \brief This function is called to add the sequence number to the + * management frames + * + * \param mac Pointer to Global MAC structure + * + * \param pMacHdr Pointer to MAC management header + * + * The pMacHdr argument points to the MAC management header. The + * sequence number stored in the mac structure will be incremented + * and updated to the MAC management header. The start sequence + * number is WLAN_HOST_SEQ_NUM_MIN and the end value is + * WLAN_HOST_SEQ_NUM_MAX. After reaching the MAX value, the sequence + * number will roll over. + * + */ +static void lim_add_mgmt_seq_num(struct mac_context *mac, tpSirMacMgmtHdr pMacHdr) +{ + if (mac->mgmtSeqNum >= WLAN_HOST_SEQ_NUM_MAX) { + mac->mgmtSeqNum = WLAN_HOST_SEQ_NUM_MIN - 1; + } + + mac->mgmtSeqNum++; + + pMacHdr->seqControl.seqNumLo = (mac->mgmtSeqNum & LOW_SEQ_NUM_MASK); + pMacHdr->seqControl.seqNumHi = + ((mac->mgmtSeqNum & HIGH_SEQ_NUM_MASK) >> HIGH_SEQ_NUM_OFFSET); +} + +/** + * lim_populate_mac_header() - Fill in 802.11 header of frame + * + * @mac_ctx: Pointer to Global MAC structure + * @buf: Pointer to the frame buffer that needs to be populate + * @type: 802.11 Type of the frame + * @sub_type: 802.11 Subtype of the frame + * @peer_addr: dst address + * @self_mac_addr: local mac address + * + * This function is called by various LIM modules to prepare the + * 802.11 frame MAC header + * + * The buf argument points to the beginning of the frame buffer to + * which - a) The 802.11 MAC header is set b) Following this MAC header + * will be the MGMT frame payload The payload itself is populated by the + * caller API + * + * Return: None + */ + +void lim_populate_mac_header(struct mac_context *mac_ctx, uint8_t *buf, + uint8_t type, uint8_t sub_type, tSirMacAddr peer_addr, + tSirMacAddr self_mac_addr) +{ + tpSirMacMgmtHdr mac_hdr; + + /* Prepare MAC management header */ + mac_hdr = (tpSirMacMgmtHdr) (buf); + + /* Prepare FC */ + mac_hdr->fc.protVer = SIR_MAC_PROTOCOL_VERSION; + mac_hdr->fc.type = type; + mac_hdr->fc.subType = sub_type; + + /* Prepare Address 1 */ + qdf_mem_copy((uint8_t *) mac_hdr->da, + (uint8_t *) peer_addr, sizeof(tSirMacAddr)); + + /* Prepare Address 2 */ + sir_copy_mac_addr(mac_hdr->sa, self_mac_addr); + + /* Prepare Address 3 */ + qdf_mem_copy((uint8_t *) mac_hdr->bssId, + (uint8_t *) peer_addr, sizeof(tSirMacAddr)); + + /* Prepare sequence number */ + lim_add_mgmt_seq_num(mac_ctx, mac_hdr); + pe_debug("seqNumLo=%d, seqNumHi=%d, mgmtSeqNum=%d", + mac_hdr->seqControl.seqNumLo, + mac_hdr->seqControl.seqNumHi, mac_ctx->mgmtSeqNum); +} + +/** + * lim_send_probe_req_mgmt_frame() - send probe request management frame + * @mac_ctx: Pointer to Global MAC structure + * @ssid: SSID to be sent in Probe Request frame + * @bssid: BSSID to be sent in Probe Request frame + * @chan_freq: Channel frequency on which the Probe Request is going out + * @self_macaddr: self MAC address + * @dot11mode: self dotllmode + * @additional_ielen: if non-zero, include additional_ie in the Probe Request + * frame + * @additional_ie: if additional_ielen is non zero, include this field in the + * Probe Request frame + * + * This function is called by various LIM modules to send Probe Request frame + * during active scan/learn phase. + * Probe request is sent out in the following scenarios: + * --heartbeat failure: session needed + * --join req: session needed + * --foreground scan: no session + * --background scan: no session + * --sch_beacon_processing: to get EDCA parameters: session needed + * + * Return: QDF_STATUS (QDF_STATUS_SUCCESS on success and error codes otherwise) + */ +QDF_STATUS +lim_send_probe_req_mgmt_frame(struct mac_context *mac_ctx, + tSirMacSSid *ssid, + tSirMacAddr bssid, + qdf_freq_t chan_freq, + tSirMacAddr self_macaddr, + uint32_t dot11mode, + uint16_t *additional_ielen, + uint8_t *additional_ie) +{ + tDot11fProbeRequest pr; + uint32_t status, bytes, payload; + uint8_t *frame; + void *packet; + QDF_STATUS qdf_status; + struct pe_session *pesession; + uint8_t sessionid; + const uint8_t *p2pie = NULL; + uint8_t txflag = 0; + uint8_t vdev_id = 0; + bool is_vht_enabled = false; + uint8_t txPower; + uint16_t addn_ielen = 0; + bool extracted_ext_cap_flag = false; + tDot11fIEExtCap extracted_ext_cap; + QDF_STATUS sir_status; + const uint8_t *qcn_ie = NULL; + uint8_t channel; + + if (additional_ielen) + addn_ielen = *additional_ielen; + + channel = wlan_reg_freq_to_chan(mac_ctx->pdev, chan_freq); + /* + * The probe req should not send 11ac capabilities if band is + * 2.4GHz, unless gEnableVhtFor24GHzBand is enabled in INI. So + * if gEnableVhtFor24GHzBand is false and dot11mode is 11ac + * set it to 11n. + */ + if (wlan_reg_is_24ghz_ch_freq(chan_freq) && + !mac_ctx->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band && + (MLME_DOT11_MODE_11AC == dot11mode || + MLME_DOT11_MODE_11AC_ONLY == dot11mode)) + dot11mode = MLME_DOT11_MODE_11N; + /* + * session context may or may not be present, when probe request needs + * to be sent out. Following cases exist: + * --heartbeat failure: session needed + * --join req: session needed + * --foreground scan: no session + * --background scan: no session + * --sch_beacon_processing: to get EDCA parameters: session needed + * If session context does not exist, some IEs will be populated from + * CFGs, e.g. Supported and Extended rate set IEs + */ + pesession = pe_find_session_by_bssid(mac_ctx, bssid, &sessionid); + + if (pesession) + vdev_id = pesession->vdev_id; + + /* The scheme here is to fill out a 'tDot11fProbeRequest' structure */ + /* and then hand it off to 'dot11f_pack_probe_request' (for */ + /* serialization). We start by zero-initializing the structure: */ + qdf_mem_zero((uint8_t *) &pr, sizeof(pr)); + + /* & delegating to assorted helpers: */ + populate_dot11f_ssid(mac_ctx, ssid, &pr.SSID); + + if (addn_ielen && additional_ie) + p2pie = limGetP2pIEPtr(mac_ctx, additional_ie, addn_ielen); + + /* + * Don't include 11b rate if it is a P2P serach or probe request is + * sent by P2P Client + */ + if ((MLME_DOT11_MODE_11B != dot11mode) && (p2pie) && + ((pesession) && (QDF_P2P_CLIENT_MODE == pesession->opmode))) { + /* + * In the below API pass channel number > 14, do that it fills + * only 11a rates in supported rates + */ + populate_dot11f_supp_rates(mac_ctx, 15, &pr.SuppRates, + pesession); + } else { + populate_dot11f_supp_rates(mac_ctx, channel, + &pr.SuppRates, pesession); + + if (MLME_DOT11_MODE_11B != dot11mode) { + populate_dot11f_ext_supp_rates1(mac_ctx, channel, + &pr.ExtSuppRates); + } + } + + /* + * Table 7-14 in IEEE Std. 802.11k-2008 says + * DS params "can" be present in RRM is disabled and "is" present if + * RRM is enabled. It should be ok even if we add it into probe req when + * RRM is not enabled. + */ + populate_dot11f_ds_params(mac_ctx, &pr.DSParams, channel); + /* Call RRM module to get the tx power for management used. */ + txPower = (uint8_t) rrm_get_mgmt_tx_power(mac_ctx, pesession); + populate_dot11f_wfatpc(mac_ctx, &pr.WFATPC, txPower, 0); + + if (pesession) { + /* Include HT Capability IE */ + if (pesession->htCapability && + !(WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_freq))) + populate_dot11f_ht_caps(mac_ctx, pesession, &pr.HTCaps); + } else { /* !pesession */ + if (IS_DOT11_MODE_HT(dot11mode) && + !(WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_freq))) + populate_dot11f_ht_caps(mac_ctx, NULL, &pr.HTCaps); + } + + /* + * Set channelbonding information as "disabled" when tunned to a + * 2.4 GHz channel + */ + if (wlan_reg_is_24ghz_ch_freq(chan_freq)) { + if (mac_ctx->roam.configParam.channelBondingMode24GHz + == PHY_SINGLE_CHANNEL_CENTERED) { + pr.HTCaps.supportedChannelWidthSet = + eHT_CHANNEL_WIDTH_20MHZ; + pr.HTCaps.shortGI40MHz = 0; + } else { + pr.HTCaps.supportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + } + } + if (pesession) { + /* Include VHT Capability IE */ + if (pesession->vhtCapability && + !(WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_freq))) { + populate_dot11f_vht_caps(mac_ctx, pesession, + &pr.VHTCaps); + is_vht_enabled = true; + } + } else { + if (IS_DOT11_MODE_VHT(dot11mode) && + !(WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_freq))) { + populate_dot11f_vht_caps(mac_ctx, pesession, + &pr.VHTCaps); + is_vht_enabled = true; + } + } + if (pesession) + populate_dot11f_ext_cap(mac_ctx, is_vht_enabled, &pr.ExtCap, + pesession); + + if (IS_DOT11_MODE_HE(dot11mode) && pesession) + lim_update_session_he_capable(mac_ctx, pesession); + + populate_dot11f_he_caps(mac_ctx, pesession, &pr.he_cap); + populate_dot11f_he_6ghz_cap(mac_ctx, pesession, + &pr.he_6ghz_band_cap); + + if (addn_ielen && additional_ie) { + qdf_mem_zero((uint8_t *)&extracted_ext_cap, + sizeof(tDot11fIEExtCap)); + sir_status = lim_strip_extcap_update_struct(mac_ctx, + additional_ie, + &addn_ielen, + &extracted_ext_cap); + if (QDF_STATUS_SUCCESS != sir_status) { + pe_debug("Unable to Stripoff ExtCap IE from Probe Req"); + } else { + struct s_ext_cap *p_ext_cap = + (struct s_ext_cap *) + extracted_ext_cap.bytes; + if (p_ext_cap->interworking_service) + p_ext_cap->qos_map = 1; + extracted_ext_cap.num_bytes = + lim_compute_ext_cap_ie_length + (&extracted_ext_cap); + extracted_ext_cap_flag = + (extracted_ext_cap.num_bytes > 0); + if (additional_ielen) + *additional_ielen = addn_ielen; + } + qcn_ie = wlan_get_vendor_ie_ptr_from_oui(SIR_MAC_QCN_OUI_TYPE, + SIR_MAC_QCN_OUI_TYPE_SIZE, + additional_ie, addn_ielen); + } + /* Add qcn_ie only if qcn ie is not present in additional_ie */ + if (!qcn_ie) + populate_dot11f_qcn_ie(mac_ctx, &pr.qcn_ie, QCN_IE_ATTR_ID_ALL); + else + populate_dot11f_qcn_ie(mac_ctx, &pr.qcn_ie, + QCN_IE_ATTR_ID_VHT_MCS11); + + /* + * Extcap IE now support variable length, merge Extcap IE from addn_ie + * may change the frame size. Therefore, MUST merge ExtCap IE before + * dot11f get packed payload size. + */ + if (extracted_ext_cap_flag) + lim_merge_extcap_struct(&pr.ExtCap, &extracted_ext_cap, true); + + /* That's it-- now we pack it. First, how much space are we going to */ + status = dot11f_get_packed_probe_request_size(mac_ctx, &pr, &payload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to calculate the packed size for a Probe Request (0x%08x)", + status); + /* We'll fall back on the worst case scenario: */ + payload = sizeof(tDot11fProbeRequest); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size for a Probe Request (0x%08x)", + status); + } + + bytes = payload + sizeof(tSirMacMgmtHdr) + addn_ielen; + + /* Ok-- try to allocate some memory: */ + qdf_status = cds_packet_alloc((uint16_t) bytes, (void **)&frame, + (void **)&packet); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a Probe Request", bytes); + return QDF_STATUS_E_NOMEM; + } + /* Paranoia: */ + qdf_mem_zero(frame, bytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac_ctx, frame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_PROBE_REQ, bssid, self_macaddr); + + /* That done, pack the Probe Request: */ + status = dot11f_pack_probe_request(mac_ctx, &pr, frame + + sizeof(tSirMacMgmtHdr), + payload, &payload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a Probe Request (0x%08x)", status); + cds_packet_free((void *)packet); + return QDF_STATUS_E_FAILURE; /* allocated! */ + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing a Probe Request (0x%08x)", status); + } + /* Append any AddIE if present. */ + if (addn_ielen) { + qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload, + additional_ie, addn_ielen); + payload += addn_ielen; + } + pe_nofl_debug("Probe req TX: vdev %d seq num %d to " QDF_MAC_ADDR_FMT " len %d", + vdev_id, mac_ctx->mgmtSeqNum, + QDF_MAC_ADDR_REF(bssid), + (int)sizeof(tSirMacMgmtHdr) + payload); + + /* If this probe request is sent during P2P Search State, then we need + * to send it at OFDM rate. + */ + if ((REG_BAND_5G == lim_get_rf_band(chan_freq)) || + /* + * For unicast probe req mgmt from Join function we don't set + * above variables. So we need to add one more check whether it + * is opmode is P2P_CLIENT or not + */ + ((pesession) && (QDF_P2P_CLIENT_MODE == pesession->opmode))) + txflag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + qdf_status = + wma_tx_frame(mac_ctx, packet, + (uint16_t) sizeof(tSirMacMgmtHdr) + payload, + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, 7, + lim_tx_complete, frame, txflag, vdev_id, + 0, RATEID_DEFAULT); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("could not send Probe Request frame!"); + /* Pkt will be freed up by the callback */ + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} /* End lim_send_probe_req_mgmt_frame. */ + +static QDF_STATUS lim_get_addn_ie_for_probe_resp(struct mac_context *mac, + uint8_t *addIE, uint16_t *addnIELen, + uint8_t probeReqP2pIe) +{ + /* If Probe request doesn't have P2P IE, then take out P2P IE + from additional IE */ + if (!probeReqP2pIe) { + uint8_t *tempbuf = NULL; + uint16_t tempLen = 0; + int left = *addnIELen; + uint8_t *ptr = addIE; + uint8_t elem_id, elem_len; + + if (!addIE) { + pe_err("NULL addIE pointer"); + return QDF_STATUS_E_FAILURE; + } + + tempbuf = qdf_mem_malloc(left); + if (!tempbuf) + return QDF_STATUS_E_NOMEM; + + while (left >= 2) { + elem_id = ptr[0]; + elem_len = ptr[1]; + left -= 2; + if (elem_len > left) { + pe_err("Invalid IEs eid: %d elem_len: %d left: %d", + elem_id, elem_len, left); + qdf_mem_free(tempbuf); + return QDF_STATUS_E_FAILURE; + } + if (!((WLAN_ELEMID_VENDOR == elem_id) && + (memcmp + (&ptr[2], SIR_MAC_P2P_OUI, + SIR_MAC_P2P_OUI_SIZE) == 0))) { + qdf_mem_copy(tempbuf + tempLen, &ptr[0], + elem_len + 2); + tempLen += (elem_len + 2); + } + left -= elem_len; + ptr += (elem_len + 2); + } + qdf_mem_copy(addIE, tempbuf, tempLen); + *addnIELen = tempLen; + qdf_mem_free(tempbuf); + } + return QDF_STATUS_SUCCESS; +} + +/** + * lim_add_additional_ie() - Add additional IE to management frame + * @frame: pointer to frame + * @frame_offset: current offset of frame + * @add_ie: pointer to addtional ie + * @add_ie_len: length of addtional ie + * @p2p_ie: pointer to p2p ie + * @noa_ie: pointer to noa ie, this is seperate p2p ie + * @noa_ie_len: length of noa ie + * @noa_stream: pointer to noa stream, this is noa attribute only + * @noa_stream_len: length of noa stream + * + * This function adds additional IE to management frame. + * + * Return: None + */ +static void lim_add_additional_ie(uint8_t *frame, uint32_t frame_offset, + uint8_t *add_ie, uint32_t add_ie_len, + uint8_t *p2p_ie, uint8_t *noa_ie, + uint32_t noa_ie_len, uint8_t *noa_stream, + uint32_t noa_stream_len) { + uint16_t p2p_ie_offset; + + if (!add_ie_len || !add_ie) { + pe_debug("no valid addtional ie"); + return; + } + + if (!noa_stream_len) { + qdf_mem_copy(frame + frame_offset, &add_ie[0], add_ie_len); + return; + } + + if (noa_ie_len > (SIR_MAX_NOA_ATTR_LEN + SIR_P2P_IE_HEADER_LEN)) { + pe_err("Not able to insert NoA, len=%d", noa_ie_len); + return; + } else if (noa_ie_len > 0) { + pe_debug("new p2p ie for noa attr"); + qdf_mem_copy(frame + frame_offset, &add_ie[0], add_ie_len); + frame_offset += add_ie_len; + qdf_mem_copy(frame + frame_offset, &noa_ie[0], noa_ie_len); + } else { + if (!p2p_ie || (p2p_ie < add_ie) || + (p2p_ie > (add_ie + add_ie_len))) { + pe_err("invalid p2p ie"); + return; + } + p2p_ie_offset = p2p_ie - add_ie + p2p_ie[1] + 2; + if (p2p_ie_offset > add_ie_len) { + pe_err("Invalid p2p ie"); + return; + } + pe_debug("insert noa attr to existed p2p ie"); + p2p_ie[1] = p2p_ie[1] + noa_stream_len; + qdf_mem_copy(frame + frame_offset, &add_ie[0], p2p_ie_offset); + frame_offset += p2p_ie_offset; + qdf_mem_copy(frame + frame_offset, &noa_stream[0], + noa_stream_len); + if (p2p_ie_offset < add_ie_len) { + frame_offset += noa_stream_len; + qdf_mem_copy(frame + frame_offset, + &add_ie[p2p_ie_offset], + add_ie_len - p2p_ie_offset); + } + } +} + +void +lim_send_probe_rsp_mgmt_frame(struct mac_context *mac_ctx, + tSirMacAddr peer_macaddr, + tpAniSSID ssid, + struct pe_session *pe_session, + uint8_t preq_p2pie) +{ + tDot11fProbeResponse *frm; + QDF_STATUS sir_status; + uint32_t cfg, payload, bytes = 0, status; + tpSirMacMgmtHdr mac_hdr; + uint8_t *frame; + void *packet = NULL; + QDF_STATUS qdf_status; + uint32_t addn_ie_present = false; + + uint16_t addn_ie_len = 0; + bool wps_ap = 0; + uint8_t tx_flag = 0; + uint8_t *add_ie = NULL; + uint8_t *p2p_ie = NULL; + uint8_t noalen = 0; + uint8_t total_noalen = 0; + uint8_t noa_stream[SIR_MAX_NOA_ATTR_LEN + SIR_P2P_IE_HEADER_LEN]; + uint8_t noa_ie[SIR_MAX_NOA_ATTR_LEN + SIR_P2P_IE_HEADER_LEN]; + uint8_t vdev_id = 0; + bool is_vht_enabled = false; + tDot11fIEExtCap extracted_ext_cap = {0}; + bool extracted_ext_cap_flag = false; + + /* We don't answer requests in this case*/ + if (ANI_DRIVER_TYPE(mac_ctx) == QDF_DRIVER_TYPE_MFG) + return; + + if (!pe_session) + return; + + /* + * In case when cac timer is running for this SAP session then + * avoid sending probe rsp out. It is violation of dfs specification. + */ + if (((pe_session->opmode == QDF_SAP_MODE) || + (pe_session->opmode == QDF_P2P_GO_MODE)) && + (true == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO, + FL("CAC timer is running, probe response dropped")); + return; + } + vdev_id = pe_session->vdev_id; + frm = qdf_mem_malloc(sizeof(tDot11fProbeResponse)); + if (!frm) + return; + + /* + * Fill out 'frm', after which we'll just hand the struct off to + * 'dot11f_pack_probe_response'. + */ + qdf_mem_zero((uint8_t *) frm, sizeof(tDot11fProbeResponse)); + + /* + * Timestamp to be updated by TFP, below. + * + * Beacon Interval: + */ + if (LIM_IS_AP_ROLE(pe_session)) { + frm->BeaconInterval.interval = + mac_ctx->sch.beacon_interval; + } else { + cfg = mac_ctx->mlme_cfg->sap_cfg.beacon_interval; + frm->BeaconInterval.interval = (uint16_t) cfg; + } + + populate_dot11f_capabilities(mac_ctx, &frm->Capabilities, pe_session); + populate_dot11f_ssid(mac_ctx, (tSirMacSSid *) ssid, &frm->SSID); + populate_dot11f_supp_rates(mac_ctx, POPULATE_DOT11F_RATES_OPERATIONAL, + &frm->SuppRates, pe_session); + populate_dot11f_tpc_report(mac_ctx, &frm->TPCReport, pe_session); + + populate_dot11f_ds_params( + mac_ctx, &frm->DSParams, + wlan_reg_freq_to_chan(mac_ctx->pdev, + pe_session->curr_op_freq)); + populate_dot11f_ibss_params(mac_ctx, &frm->IBSSParams, pe_session); + + if (LIM_IS_AP_ROLE(pe_session)) { + if (pe_session->wps_state != SAP_WPS_DISABLED) + populate_dot11f_probe_res_wpsi_es(mac_ctx, + &frm->WscProbeRes, + pe_session); + } else { + wps_ap = mac_ctx->mlme_cfg->wps_params.enable_wps & + WNI_CFG_WPS_ENABLE_AP; + if (wps_ap) + populate_dot11f_wsc_in_probe_res(mac_ctx, + &frm->WscProbeRes); + + if (mac_ctx->lim.wscIeInfo.probeRespWscEnrollmentState == + eLIM_WSC_ENROLL_BEGIN) { + populate_dot11f_wsc_registrar_info_in_probe_res(mac_ctx, + &frm->WscProbeRes); + mac_ctx->lim.wscIeInfo.probeRespWscEnrollmentState = + eLIM_WSC_ENROLL_IN_PROGRESS; + } + + if (mac_ctx->lim.wscIeInfo.wscEnrollmentState == + eLIM_WSC_ENROLL_END) { + de_populate_dot11f_wsc_registrar_info_in_probe_res( + mac_ctx, &frm->WscProbeRes); + mac_ctx->lim.wscIeInfo.probeRespWscEnrollmentState = + eLIM_WSC_ENROLL_NOOP; + } + } + + populate_dot11f_country(mac_ctx, &frm->Country, pe_session); + populate_dot11f_edca_param_set(mac_ctx, &frm->EDCAParamSet, pe_session); + + if (pe_session->dot11mode != MLME_DOT11_MODE_11B) + populate_dot11f_erp_info(mac_ctx, &frm->ERPInfo, pe_session); + + populate_dot11f_ext_supp_rates(mac_ctx, + POPULATE_DOT11F_RATES_OPERATIONAL, + &frm->ExtSuppRates, pe_session); + + /* Populate HT IEs, when operating in 11n */ + if (pe_session->htCapability) { + populate_dot11f_ht_caps(mac_ctx, pe_session, &frm->HTCaps); + populate_dot11f_ht_info(mac_ctx, &frm->HTInfo, pe_session); + } + if (pe_session->vhtCapability) { + pe_debug("Populate VHT IE in Probe Response"); + populate_dot11f_vht_caps(mac_ctx, pe_session, &frm->VHTCaps); + populate_dot11f_vht_operation(mac_ctx, pe_session, + &frm->VHTOperation); + /* + * we do not support multi users yet. + * populate_dot11f_vht_ext_bss_load( mac_ctx, + * &frm.VHTExtBssLoad ); + */ + is_vht_enabled = true; + } + + if (lim_is_session_he_capable(pe_session)) { + pe_debug("Populate HE IEs"); + populate_dot11f_he_caps(mac_ctx, pe_session, + &frm->he_cap); + populate_dot11f_he_operation(mac_ctx, pe_session, + &frm->he_op); + populate_dot11f_he_6ghz_cap(mac_ctx, pe_session, + &frm->he_6ghz_band_cap); + } + + populate_dot11f_ext_cap(mac_ctx, is_vht_enabled, &frm->ExtCap, + pe_session); + + if (pe_session->pLimStartBssReq) { + populate_dot11f_wpa(mac_ctx, + &(pe_session->pLimStartBssReq->rsnIE), + &frm->WPA); + populate_dot11f_rsn_opaque(mac_ctx, + &(pe_session->pLimStartBssReq->rsnIE), + &frm->RSNOpaque); + } + + populate_dot11f_wmm(mac_ctx, &frm->WMMInfoAp, &frm->WMMParams, + &frm->WMMCaps, pe_session); + +#if defined(FEATURE_WLAN_WAPI) + if (pe_session->pLimStartBssReq) + populate_dot11f_wapi(mac_ctx, + &(pe_session->pLimStartBssReq->rsnIE), + &frm->WAPI); +#endif /* defined(FEATURE_WLAN_WAPI) */ + + /* + * Only use CFG for non-listen mode. This CFG is not working for + * concurrency. In listening mode, probe rsp IEs is passed in + * the message from SME to PE. + */ + addn_ie_present = + (pe_session->add_ie_params.probeRespDataLen != 0); + + if (addn_ie_present) { + + add_ie = qdf_mem_malloc( + pe_session->add_ie_params.probeRespDataLen); + if (!add_ie) + goto err_ret; + + qdf_mem_copy(add_ie, + pe_session->add_ie_params.probeRespData_buff, + pe_session->add_ie_params.probeRespDataLen); + addn_ie_len = pe_session->add_ie_params.probeRespDataLen; + + if (QDF_STATUS_SUCCESS != lim_get_addn_ie_for_probe_resp(mac_ctx, + add_ie, &addn_ie_len, preq_p2pie)) { + pe_err("Unable to get addn_ie"); + goto err_ret; + } + + sir_status = lim_strip_extcap_update_struct(mac_ctx, + add_ie, &addn_ie_len, + &extracted_ext_cap); + if (QDF_STATUS_SUCCESS != sir_status) { + pe_debug("Unable to strip off ExtCap IE"); + } else { + extracted_ext_cap_flag = true; + } + + bytes = bytes + addn_ie_len; + + if (preq_p2pie) + p2p_ie = (uint8_t *)limGetP2pIEPtr(mac_ctx, &add_ie[0], + addn_ie_len); + + if (p2p_ie) { + /* get NoA attribute stream P2P IE */ + noalen = lim_get_noa_attr_stream(mac_ctx, + noa_stream, pe_session); + if (noalen) { + if ((p2p_ie[1] + noalen) > + WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN) { + total_noalen = lim_build_p2p_ie( + mac_ctx, + &noa_ie[0], + &noa_stream[0], + noalen); + bytes = bytes + total_noalen; + } else { + bytes = bytes + noalen; + } + } + } + } + + /* + * Extcap IE now support variable length, merge Extcap IE from addn_ie + * may change the frame size. Therefore, MUST merge ExtCap IE before + * dot11f get packed payload size. + */ + if (extracted_ext_cap_flag) + lim_merge_extcap_struct(&frm->ExtCap, &extracted_ext_cap, + true); + + status = dot11f_get_packed_probe_response_size(mac_ctx, frm, &payload); + if (DOT11F_FAILED(status)) { + pe_err("Probe Response size error (0x%08x)", + status); + /* We'll fall back on the worst case scenario: */ + payload = sizeof(tDot11fProbeResponse); + } else if (DOT11F_WARNED(status)) { + pe_warn("Probe Response size warning (0x%08x)", + status); + } + + bytes += payload + sizeof(tSirMacMgmtHdr); + + qdf_status = cds_packet_alloc((uint16_t) bytes, (void **)&frame, + (void **)&packet); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Probe Response allocation failed"); + goto err_ret; + } + /* Paranoia: */ + qdf_mem_zero(frame, bytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac_ctx, frame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_PROBE_RSP, peer_macaddr, + pe_session->self_mac_addr); + + mac_hdr = (tpSirMacMgmtHdr) frame; + + sir_copy_mac_addr(mac_hdr->bssId, pe_session->bssId); + + /* That done, pack the Probe Response: */ + status = + dot11f_pack_probe_response(mac_ctx, frm, + frame + sizeof(tSirMacMgmtHdr), + payload, &payload); + if (DOT11F_FAILED(status)) { + pe_err("Probe Response pack failure (0x%08x)", + status); + goto err_ret; + } else if (DOT11F_WARNED(status)) { + pe_warn("Probe Response pack warning (0x%08x)", status); + } + + pe_debug("Sending Probe Response frame to"); + lim_print_mac_addr(mac_ctx, peer_macaddr, LOGD); + + lim_add_additional_ie(frame, sizeof(tSirMacMgmtHdr) + payload, add_ie, + addn_ie_len, p2p_ie, noa_ie, total_noalen, + noa_stream, noalen); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + /* Queue Probe Response frame in high priority WQ */ + qdf_status = wma_tx_frame(mac_ctx, packet, + (uint16_t)bytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, frame, tx_flag, + vdev_id, 0, RATEID_DEFAULT); + + /* Pkt will be freed up by the callback */ + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + pe_err("Could not send Probe Response"); + + if (add_ie) + qdf_mem_free(add_ie); + + qdf_mem_free(frm); + return; + +err_ret: + if (add_ie) + qdf_mem_free(add_ie); + if (frm) + qdf_mem_free(frm); + if (packet) + cds_packet_free((void *)packet); + return; + +} /* End lim_send_probe_rsp_mgmt_frame. */ + +void +lim_send_addts_req_action_frame(struct mac_context *mac, + tSirMacAddr peerMacAddr, + tSirAddtsReqInfo *pAddTS, struct pe_session *pe_session) +{ + uint16_t i; + uint8_t *pFrame; + tDot11fAddTSRequest AddTSReq; + tDot11fWMMAddTSRequest WMMAddTSReq; + uint32_t nPayload, nBytes, nStatus; + tpSirMacMgmtHdr pMacHdr; + void *pPacket; +#ifdef FEATURE_WLAN_ESE + uint32_t phyMode; +#endif + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + uint8_t smeSessionId = 0; + + if (!pe_session) { + return; + } + + smeSessionId = pe_session->smeSessionId; + + if (!pAddTS->wmeTspecPresent) { + qdf_mem_zero((uint8_t *) &AddTSReq, sizeof(AddTSReq)); + + AddTSReq.Action.action = QOS_ADD_TS_REQ; + AddTSReq.DialogToken.token = pAddTS->dialogToken; + AddTSReq.Category.category = ACTION_CATEGORY_QOS; + if (pAddTS->lleTspecPresent) { + populate_dot11f_tspec(&pAddTS->tspec, &AddTSReq.TSPEC); + } else { + populate_dot11f_wmmtspec(&pAddTS->tspec, + &AddTSReq.WMMTSPEC); + } + + if (pAddTS->lleTspecPresent) { + AddTSReq.num_WMMTCLAS = 0; + AddTSReq.num_TCLAS = pAddTS->numTclas; + for (i = 0; i < pAddTS->numTclas; ++i) { + populate_dot11f_tclas(mac, &pAddTS->tclasInfo[i], + &AddTSReq.TCLAS[i]); + } + } else { + AddTSReq.num_TCLAS = 0; + AddTSReq.num_WMMTCLAS = pAddTS->numTclas; + for (i = 0; i < pAddTS->numTclas; ++i) { + populate_dot11f_wmmtclas(mac, + &pAddTS->tclasInfo[i], + &AddTSReq.WMMTCLAS[i]); + } + } + + if (pAddTS->tclasProcPresent) { + if (pAddTS->lleTspecPresent) { + AddTSReq.TCLASSPROC.processing = + pAddTS->tclasProc; + AddTSReq.TCLASSPROC.present = 1; + } else { + AddTSReq.WMMTCLASPROC.version = 1; + AddTSReq.WMMTCLASPROC.processing = + pAddTS->tclasProc; + AddTSReq.WMMTCLASPROC.present = 1; + } + } + + nStatus = + dot11f_get_packed_add_ts_request_size(mac, &AddTSReq, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for an Add TS Request (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fAddTSRequest); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for an Add TS Request (0x%08x)", + nStatus); + } + } else { + qdf_mem_zero((uint8_t *) &WMMAddTSReq, sizeof(WMMAddTSReq)); + + WMMAddTSReq.Action.action = QOS_ADD_TS_REQ; + WMMAddTSReq.DialogToken.token = pAddTS->dialogToken; + WMMAddTSReq.Category.category = ACTION_CATEGORY_WMM; + + /* WMM spec 2.2.10 - status code is only filled in for ADDTS response */ + WMMAddTSReq.StatusCode.statusCode = 0; + + populate_dot11f_wmmtspec(&pAddTS->tspec, &WMMAddTSReq.WMMTSPEC); +#ifdef FEATURE_WLAN_ESE + lim_get_phy_mode(mac, &phyMode, pe_session); + + if (phyMode == WNI_CFG_PHY_MODE_11G + || phyMode == WNI_CFG_PHY_MODE_11A) { + pAddTS->tsrsIE.rates[0] = TSRS_11AG_RATE_6MBPS; + } else { + pAddTS->tsrsIE.rates[0] = TSRS_11B_RATE_5_5MBPS; + } + populate_dot11_tsrsie(mac, &pAddTS->tsrsIE, + &WMMAddTSReq.ESETrafStrmRateSet, + sizeof(uint8_t)); +#endif + /* fillWmeTspecIE */ + + nStatus = + dot11f_get_packed_wmm_add_ts_request_size(mac, &WMMAddTSReq, + &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a WMM Add TS Request (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fAddTSRequest); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a WMM Add TS Request (0x%08x)", + nStatus); + } + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for an Add TS Request", + nBytes); + return; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peerMacAddr, pe_session->self_mac_addr); + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + lim_set_protected_bit(mac, pe_session, peerMacAddr, pMacHdr); + + /* That done, pack the struct: */ + if (!pAddTS->wmeTspecPresent) { + nStatus = dot11f_pack_add_ts_request(mac, &AddTSReq, + pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack an Add TS Request " + "(0x%08x)", nStatus); + cds_packet_free((void *)pPacket); + return; /* allocated! */ + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing an Add TS Request (0x%08x)", + nStatus); + } + } else { + nStatus = dot11f_pack_wmm_add_ts_request(mac, &WMMAddTSReq, + pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack a WMM Add TS Request (0x%08x)", + nStatus); + cds_packet_free((void *)pPacket); + return; /* allocated! */ + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing a WMM Add TS Request (0x%08x)", + nStatus); + } + } + + pe_debug("Sending an Add TS Request frame to"); + lim_print_mac_addr(mac, peerMacAddr, LOGD); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, pMacHdr->fc.subType)); + lim_diag_mgmt_tx_event_report(mac, pMacHdr, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + + /* Queue Addts Response frame in high priority WQ */ + qdf_status = wma_tx_frame(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + pe_err("Could not send an Add TS Request (%X", + qdf_status); +} /* End lim_send_addts_req_action_frame. */ + +/** + * lim_assoc_rsp_tx_complete() - Confirmation for assoc rsp OTA + * @context: pointer to global mac + * @buf: buffer which is nothing but entire assoc rsp frame + * @tx_complete : Sent status + * @params; tx completion params + * + * Return: This returns QDF_STATUS + */ +static QDF_STATUS lim_assoc_rsp_tx_complete( + void *context, + qdf_nbuf_t buf, + uint32_t tx_complete, + void *params) +{ + struct mac_context *mac_ctx = (struct mac_context *)context; + tSirMacMgmtHdr *mac_hdr; + struct pe_session *session_entry; + uint8_t session_id; + tpLimMlmAssocInd lim_assoc_ind; + tpDphHashNode sta_ds; + uint16_t aid; + uint8_t *data; + struct assoc_ind *sme_assoc_ind; + struct scheduler_msg msg; + tpSirAssocReq assoc_req; + + if (!buf) { + pe_err("Assoc rsp frame buffer is NULL"); + goto null_buf; + } + + data = qdf_nbuf_data(buf); + + if (!data) { + pe_err("Assoc rsp frame is NULL"); + goto end; + } + + mac_hdr = (tSirMacMgmtHdr *)data; + + session_entry = pe_find_session_by_bssid( + mac_ctx, mac_hdr->sa, + &session_id); + if (!session_entry) { + pe_err("session entry is NULL"); + goto end; + } + + sta_ds = dph_lookup_hash_entry(mac_ctx, + (uint8_t *)mac_hdr->da, &aid, + &session_entry->dph.dphHashTable); + if (!sta_ds) { + pe_err("sta_ds is NULL"); + goto end; + } + + /* Get a copy of the already parsed Assoc Request */ + assoc_req = + (tpSirAssocReq)session_entry->parsedAssocReq[sta_ds->assocId]; + + if (!assoc_req) { + pe_err("assoc req for assoc_id:%d is NULL", sta_ds->assocId); + goto end; + } + + if (tx_complete != WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK) { + lim_send_disassoc_mgmt_frame(mac_ctx, + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON, + sta_ds->staAddr, + session_entry, false); + lim_trigger_sta_deletion(mac_ctx, sta_ds, session_entry); + goto free_buffers; + } + + lim_assoc_ind = qdf_mem_malloc(sizeof(tLimMlmAssocInd)); + if (!lim_assoc_ind) { + pe_err("lim assoc ind is NULL"); + goto free_assoc_req; + } + if (!lim_fill_lim_assoc_ind_params(lim_assoc_ind, mac_ctx, + sta_ds, session_entry)) { + pe_err("lim assoc ind fill error"); + goto lim_assoc_ind; + } + + sme_assoc_ind = qdf_mem_malloc(sizeof(struct assoc_ind)); + if (!sme_assoc_ind) { + pe_err("sme assoc ind is NULL"); + goto lim_assoc_ind; + } + sme_assoc_ind->messageType = eWNI_SME_ASSOC_IND_UPPER_LAYER; + lim_fill_sme_assoc_ind_params( + mac_ctx, lim_assoc_ind, + sme_assoc_ind, + session_entry, true); + + qdf_mem_zero(&msg, sizeof(struct scheduler_msg)); + msg.type = eWNI_SME_ASSOC_IND_UPPER_LAYER; + msg.bodyptr = sme_assoc_ind; + msg.bodyval = 0; + sme_assoc_ind->reassocReq = sta_ds->mlmStaContext.subType; + sme_assoc_ind->timingMeasCap = sta_ds->timingMeasCap; + MTRACE(mac_trace_msg_tx(mac_ctx, session_entry->peSessionId, msg.type)); + lim_sys_process_mmh_msg_api(mac_ctx, &msg); + + qdf_mem_free(lim_assoc_ind); + +free_buffers: + if (assoc_req->assocReqFrame) { + qdf_mem_free(assoc_req->assocReqFrame); + assoc_req->assocReqFrame = NULL; + } + qdf_mem_free(session_entry->parsedAssocReq[sta_ds->assocId]); + session_entry->parsedAssocReq[sta_ds->assocId] = NULL; + qdf_nbuf_free(buf); + + return QDF_STATUS_SUCCESS; + +lim_assoc_ind: + qdf_mem_free(lim_assoc_ind); +free_assoc_req: + if (assoc_req->assocReqFrame) { + qdf_mem_free(assoc_req->assocReqFrame); + assoc_req->assocReqFrame = NULL; + } + qdf_mem_free(session_entry->parsedAssocReq[sta_ds->assocId]); + session_entry->parsedAssocReq[sta_ds->assocId] = NULL; +end: + qdf_nbuf_free(buf); +null_buf: + return QDF_STATUS_E_FAILURE; +} + +void +lim_send_assoc_rsp_mgmt_frame( + struct mac_context *mac_ctx, + uint16_t status_code, uint16_t aid, tSirMacAddr peer_addr, + uint8_t subtype, tpDphHashNode sta, struct pe_session *pe_session, + bool tx_complete) +{ + static tDot11fAssocResponse frm; + uint8_t *frame; + tpSirMacMgmtHdr mac_hdr; + QDF_STATUS sir_status; + uint8_t lle_mode = 0, addts; + tHalBitVal qos_mode, wme_mode; + uint32_t payload, bytes = 0, status; + void *packet; + QDF_STATUS qdf_status; + tUpdateBeaconParams beacon_params; + uint8_t tx_flag = 0; + uint32_t addn_ie_len = 0; + uint8_t add_ie[WNI_CFG_ASSOC_RSP_ADDNIE_DATA_LEN]; + tpSirAssocReq assoc_req = NULL; + uint8_t sme_session = 0; + bool is_vht = false; + uint16_t stripoff_len = 0; + tDot11fIEExtCap extracted_ext_cap; + bool extracted_flag = false; +#ifdef WLAN_FEATURE_11W + uint8_t retry_int; + uint16_t max_retries; +#endif + + if (!pe_session) { + pe_err("pe_session is NULL"); + return; + } + + sme_session = pe_session->smeSessionId; + + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + + limGetQosMode(pe_session, &qos_mode); + limGetWmeMode(pe_session, &wme_mode); + + /* + * An Add TS IE is added only if the AP supports it and + * the requesting STA sent a traffic spec. + */ + addts = (qos_mode && sta && sta->qos.addtsPresent) ? 1 : 0; + + frm.Status.status = status_code; + + frm.AID.associd = aid | LIM_AID_MASK; + + if (!sta) { + populate_dot11f_supp_rates(mac_ctx, + POPULATE_DOT11F_RATES_OPERATIONAL, + &frm.SuppRates, pe_session); + populate_dot11f_ext_supp_rates(mac_ctx, + POPULATE_DOT11F_RATES_OPERATIONAL, + &frm.ExtSuppRates, pe_session); + } else { + populate_dot11f_assoc_rsp_rates(mac_ctx, &frm.SuppRates, + &frm.ExtSuppRates, + sta->supportedRates.llbRates, + sta->supportedRates.llaRates); + } + + if (LIM_IS_AP_ROLE(pe_session) && sta && + QDF_STATUS_SUCCESS == status_code) { + assoc_req = (tpSirAssocReq) + pe_session->parsedAssocReq[sta->assocId]; + /* + * populate P2P IE in AssocRsp when assoc_req from the peer + * includes P2P IE + */ + if (assoc_req && assoc_req->addIEPresent) + populate_dot11_assoc_res_p2p_ie(mac_ctx, + &frm.P2PAssocRes, + assoc_req); + } + + if (sta) { + if (eHAL_SET == qos_mode) { + if (sta->lleEnabled) { + lle_mode = 1; + populate_dot11f_edca_param_set(mac_ctx, + &frm.EDCAParamSet, pe_session); + } + } + + if ((!lle_mode) && (eHAL_SET == wme_mode) && sta->wmeEnabled) { + populate_dot11f_wmm_params(mac_ctx, &frm.WMMParams, + pe_session); + + if (sta->wsmEnabled) + populate_dot11f_wmm_caps(&frm.WMMCaps); + } + + if (sta->mlmStaContext.htCapability && + pe_session->htCapability) { + pe_debug("Populate HT IEs in Assoc Response"); + populate_dot11f_ht_caps(mac_ctx, pe_session, + &frm.HTCaps); + /* + * Check the STA capability and + * update the HTCaps accordingly + */ + frm.HTCaps.supportedChannelWidthSet = ( + sta->htSupportedChannelWidthSet < + pe_session->htSupportedChannelWidthSet) ? + sta->htSupportedChannelWidthSet : + pe_session->htSupportedChannelWidthSet; + if (!frm.HTCaps.supportedChannelWidthSet) + frm.HTCaps.shortGI40MHz = 0; + + populate_dot11f_ht_info(mac_ctx, &frm.HTInfo, + pe_session); + } + pe_debug("SupportedChnlWidth: %d, mimoPS: %d, GF: %d, short GI20:%d, shortGI40: %d, dsssCck: %d, AMPDU Param: %x", + frm.HTCaps.supportedChannelWidthSet, + frm.HTCaps.mimoPowerSave, + frm.HTCaps.greenField, frm.HTCaps.shortGI20MHz, + frm.HTCaps.shortGI40MHz, + frm.HTCaps.dsssCckMode40MHz, + frm.HTCaps.maxRxAMPDUFactor); + + if (sta->mlmStaContext.vhtCapability && + pe_session->vhtCapability) { + pe_debug("Populate VHT IEs in Assoc Response"); + populate_dot11f_vht_caps(mac_ctx, pe_session, + &frm.VHTCaps); + populate_dot11f_vht_operation(mac_ctx, pe_session, + &frm.VHTOperation); + is_vht = true; + } else if (sta->mlmStaContext.force_1x1 && + frm.HTCaps.present) { + /* + * WAR: In P2P GO mode, if the P2P client device + * is only HT capable and not VHT capable, but the P2P + * GO device is VHT capable and advertises 2x2 NSS with + * HT capablity client device, which results in IOT + * issues. + * When GO is operating in DBS mode, GO beacons + * advertise 2x2 capability but include OMN IE to + * indicate current operating mode of 1x1. But here + * peer device is only HT capable and will not + * understand OMN IE. + */ + frm.HTInfo.basicMCSSet[1] = 0; + frm.HTCaps.supportedMCSSet[1] = 0; + } + + if (pe_session->vhtCapability && + pe_session->vendor_vht_sap && + (assoc_req) && + assoc_req->vendor_vht_ie.VHTCaps.present) { + pe_debug("Populate Vendor VHT IEs in Assoc Rsponse"); + frm.vendor_vht_ie.present = 1; + frm.vendor_vht_ie.sub_type = + pe_session->vendor_specific_vht_ie_sub_type; + frm.vendor_vht_ie.VHTCaps.present = 1; + populate_dot11f_vht_caps(mac_ctx, pe_session, + &frm.vendor_vht_ie.VHTCaps); + populate_dot11f_vht_operation(mac_ctx, pe_session, + &frm.vendor_vht_ie.VHTOperation); + is_vht = true; + populate_dot11f_qcn_ie(mac_ctx, &frm.qcn_ie, + QCN_IE_ATTR_ID_ALL); + } + populate_dot11f_ext_cap(mac_ctx, is_vht, &frm.ExtCap, + pe_session); + + if (lim_is_sta_he_capable(sta) && + lim_is_session_he_capable(pe_session)) { + pe_debug("Populate HE IEs"); + populate_dot11f_he_caps(mac_ctx, pe_session, + &frm.he_cap); + populate_dot11f_he_operation(mac_ctx, pe_session, + &frm.he_op); + populate_dot11f_he_6ghz_cap(mac_ctx, pe_session, + &frm.he_6ghz_band_cap); + } +#ifdef WLAN_FEATURE_11W + if (eSIR_MAC_TRY_AGAIN_LATER == status_code) { + max_retries = + mac_ctx->mlme_cfg->gen.pmf_sa_query_max_retries; + retry_int = + mac_ctx->mlme_cfg->gen.pmf_sa_query_retry_interval; + populate_dot11f_timeout_interval(mac_ctx, + &frm.TimeoutInterval, + SIR_MAC_TI_TYPE_ASSOC_COMEBACK, + (max_retries - + sta->pmfSaQueryRetryCount) + * retry_int); + } +#endif + + if (LIM_IS_AP_ROLE(pe_session) && sta->non_ecsa_capable) + pe_session->lim_non_ecsa_cap_num++; + } + + qdf_mem_zero((uint8_t *) &beacon_params, sizeof(beacon_params)); + + if (LIM_IS_AP_ROLE(pe_session) && + (pe_session->gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE)) + lim_decide_ap_protection(mac_ctx, peer_addr, &beacon_params, + pe_session); + + lim_update_short_preamble(mac_ctx, peer_addr, &beacon_params, + pe_session); + lim_update_short_slot_time(mac_ctx, peer_addr, &beacon_params, + pe_session); + + /* + * Populate Do11capabilities after updating session with + * Assos req details + */ + populate_dot11f_capabilities(mac_ctx, &frm.Capabilities, pe_session); + + beacon_params.bss_idx = pe_session->vdev_id; + + /* Send message to HAL about beacon parameter change. */ + if ((false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running) + && beacon_params.paramChangeBitmap) { + sch_set_fixed_beacon_fields(mac_ctx, pe_session); + lim_send_beacon_params(mac_ctx, &beacon_params, pe_session); + } + + lim_obss_send_detection_cfg(mac_ctx, pe_session, false); + + if (assoc_req) { + addn_ie_len = pe_session->add_ie_params.assocRespDataLen; + + /* Nonzero length indicates Assoc rsp IE available */ + if (addn_ie_len > 0 && + addn_ie_len <= WNI_CFG_ASSOC_RSP_ADDNIE_DATA_LEN && + (bytes + addn_ie_len) <= SIR_MAX_PACKET_SIZE) { + qdf_mem_copy(add_ie, + pe_session->add_ie_params.assocRespData_buff, + pe_session->add_ie_params.assocRespDataLen); + + qdf_mem_zero((uint8_t *) &extracted_ext_cap, + sizeof(extracted_ext_cap)); + + stripoff_len = addn_ie_len; + sir_status = + lim_strip_extcap_update_struct + (mac_ctx, &add_ie[0], &stripoff_len, + &extracted_ext_cap); + if (QDF_STATUS_SUCCESS != sir_status) { + pe_debug("strip off extcap IE failed"); + } else { + addn_ie_len = stripoff_len; + extracted_flag = true; + } + bytes = bytes + addn_ie_len; + } + pe_debug("addn_ie_len: %d for Assoc Resp: %d", + addn_ie_len, assoc_req->addIEPresent); + } + + /* + * Extcap IE now support variable length, merge Extcap IE from addn_ie + * may change the frame size. Therefore, MUST merge ExtCap IE before + * dot11f get packed payload size. + */ + if (extracted_flag) + lim_merge_extcap_struct(&(frm.ExtCap), &extracted_ext_cap, + true); + + /* Allocate a buffer for this frame: */ + status = dot11f_get_packed_assoc_response_size(mac_ctx, &frm, &payload); + if (DOT11F_FAILED(status)) { + pe_err("get Association Response size failure (0x%08x)", + status); + return; + } else if (DOT11F_WARNED(status)) { + pe_warn("get Association Response size warning (0x%08x)", + status); + } + + bytes += sizeof(tSirMacMgmtHdr) + payload; + + if (sta) + bytes += sta->mlmStaContext.owe_ie_len; + + qdf_status = cds_packet_alloc((uint16_t) bytes, (void **)&frame, + (void **)&packet); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("cds_packet_alloc failed"); + return; + } + /* Paranoia: */ + qdf_mem_zero(frame, bytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac_ctx, frame, SIR_MAC_MGMT_FRAME, + (LIM_ASSOC == subtype) ? + SIR_MAC_MGMT_ASSOC_RSP : SIR_MAC_MGMT_REASSOC_RSP, + peer_addr, + pe_session->self_mac_addr); + mac_hdr = (tpSirMacMgmtHdr) frame; + + sir_copy_mac_addr(mac_hdr->bssId, pe_session->bssId); + + status = dot11f_pack_assoc_response(mac_ctx, &frm, + frame + sizeof(tSirMacMgmtHdr), + payload, &payload); + if (DOT11F_FAILED(status)) { + pe_err("Association Response pack failure(0x%08x)", + status); + cds_packet_free((void *)packet); + return; + } else if (DOT11F_WARNED(status)) { + pe_warn("Association Response pack warning (0x%08x)", + status); + } + + pe_nofl_debug("Assoc rsp TX: vdev %d subtype %d to "QDF_MAC_ADDR_FMT" seq num %d status %d aid %d", + pe_session->vdev_id, subtype, + QDF_MAC_ADDR_REF(mac_hdr->da), + mac_ctx->mgmtSeqNum, status_code, aid); + + if (addn_ie_len && addn_ie_len <= WNI_CFG_ASSOC_RSP_ADDNIE_DATA_LEN) + qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload, + &add_ie[0], addn_ie_len); + + if (sta && sta->mlmStaContext.owe_ie_len) + qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload + + addn_ie_len, + sta->mlmStaContext.owe_ie, + sta->mlmStaContext.owe_ie_len); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, mac_hdr->fc.subType)); + lim_diag_mgmt_tx_event_report(mac_ctx, mac_hdr, + pe_session, QDF_STATUS_SUCCESS, status_code); + /* Queue Association Response frame in high priority WQ */ + if (tx_complete) + qdf_status = wma_tx_frameWithTxComplete( + mac_ctx, packet, (uint16_t)bytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, frame, + lim_assoc_rsp_tx_complete, tx_flag, + sme_session, false, 0, RATEID_DEFAULT); + else + qdf_status = wma_tx_frame( + mac_ctx, packet, (uint16_t)bytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, frame, tx_flag, + sme_session, 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + + /* Pkt will be freed up by the callback */ + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + pe_err("Could not Send Re/AssocRsp, retCode=%X", + qdf_status); + + /* + * update the ANI peer station count. + * FIXME_PROTECTION : take care of different type of station + * counter inside this function. + */ + lim_util_count_sta_add(mac_ctx, sta, pe_session); + +} + +void +lim_send_delts_req_action_frame(struct mac_context *mac, + tSirMacAddr peer, + uint8_t wmmTspecPresent, + struct mac_ts_info *pTsinfo, + struct mac_tspec_ie *pTspecIe, + struct pe_session *pe_session) +{ + uint8_t *pFrame; + tpSirMacMgmtHdr pMacHdr; + tDot11fDelTS DelTS; + tDot11fWMMDelTS WMMDelTS; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + uint8_t smeSessionId = 0; + + if (!pe_session) + return; + + smeSessionId = pe_session->smeSessionId; + + if (!wmmTspecPresent) { + qdf_mem_zero((uint8_t *) &DelTS, sizeof(DelTS)); + + DelTS.Category.category = ACTION_CATEGORY_QOS; + DelTS.Action.action = QOS_DEL_TS_REQ; + populate_dot11f_ts_info(pTsinfo, &DelTS.TSInfo); + + nStatus = dot11f_get_packed_del_ts_size(mac, &DelTS, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a Del TS (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fDelTS); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a Del TS (0x%08x)", + nStatus); + } + } else { + qdf_mem_zero((uint8_t *) &WMMDelTS, sizeof(WMMDelTS)); + + WMMDelTS.Category.category = ACTION_CATEGORY_WMM; + WMMDelTS.Action.action = QOS_DEL_TS_REQ; + WMMDelTS.DialogToken.token = 0; + WMMDelTS.StatusCode.statusCode = 0; + populate_dot11f_wmmtspec(pTspecIe, &WMMDelTS.WMMTSPEC); + nStatus = + dot11f_get_packed_wmm_del_ts_size(mac, &WMMDelTS, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a WMM Del TS (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fDelTS); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a WMM Del TS (0x%08x)", + nStatus); + } + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + + qdf_status = + cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for an Add TS Response", + nBytes); + return; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer, pe_session->self_mac_addr); + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + /* That done, pack the struct: */ + if (!wmmTspecPresent) { + nStatus = dot11f_pack_del_ts(mac, &DelTS, + pFrame + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack a Del TS frame (0x%08x)", + nStatus); + cds_packet_free((void *)pPacket); + return; /* allocated! */ + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing a Del TS frame (0x%08x)", + nStatus); + } + } else { + nStatus = dot11f_pack_wmm_del_ts(mac, &WMMDelTS, + pFrame + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack a WMM Del TS frame (0x%08x)", + nStatus); + cds_packet_free((void *)pPacket); + return; /* allocated! */ + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing a WMM Del TS frame (0x%08x)", + nStatus); + } + } + + pe_debug("Sending DELTS REQ (size %d) to ", nBytes); + lim_print_mac_addr(mac, pMacHdr->da, LOGD); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, pMacHdr->fc.subType)); + lim_diag_mgmt_tx_event_report(mac, pMacHdr, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + qdf_status = wma_tx_frame(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + /* Pkt will be freed up by the callback */ + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + pe_err("Failed to send Del TS (%X)!", qdf_status); + +} /* End lim_send_delts_req_action_frame. */ + +/** + * lim_assoc_tx_complete_cnf()- Confirmation for assoc sent over the air + * @context: pointer to global mac + * @buf: buffer + * @tx_complete : Sent status + * @params; tx completion params + * + * Return: This returns QDF_STATUS + */ + +static QDF_STATUS lim_assoc_tx_complete_cnf(void *context, + qdf_nbuf_t buf, + uint32_t tx_complete, + void *params) +{ + uint16_t assoc_ack_status; + uint16_t reason_code; + struct mac_context *mac_ctx = (struct mac_context *)context; + + pe_nofl_info("Assoc req TX: %s", + (tx_complete == WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK) ? + "success" : "fail"); + + if (tx_complete == WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK) { + assoc_ack_status = ACKED; + reason_code = QDF_STATUS_SUCCESS; + mac_ctx->assoc_ack_status = LIM_ACK_RCD_SUCCESS; + } else if (tx_complete == WMI_MGMT_TX_COMP_TYPE_COMPLETE_NO_ACK) { + assoc_ack_status = NOT_ACKED; + reason_code = QDF_STATUS_E_FAILURE; + mac_ctx->assoc_ack_status = LIM_ACK_RCD_FAILURE; + } else { + assoc_ack_status = SENT_FAIL; + reason_code = QDF_STATUS_E_FAILURE; + mac_ctx->assoc_ack_status = LIM_TX_FAILED; + } + + if (buf) + qdf_nbuf_free(buf); + + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_ASSOC_ACK_EVENT, + NULL, assoc_ack_status, reason_code); + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_ADAPTIVE_11R +/** + * lim_fill_adaptive_11r_ie() - Populate the Vendor secific adaptive 11r + * IE to association request frame + * @pe_session: pointer to PE session + * @ie_buf: buffer to which Adaptive 11r IE will be copied + * @ie_len: length of the Adaptive 11r Vendor specific IE + * + * Return QDF_STATUS + */ +static QDF_STATUS lim_fill_adaptive_11r_ie(struct pe_session *pe_session, + uint8_t **ie_buf, uint8_t *ie_len) +{ + uint8_t *buf = NULL, *adaptive_11r_ie = NULL; + + if (!pe_session->is_adaptive_11r_connection) + return QDF_STATUS_SUCCESS; + + /* + * Vendor specific Adaptive 11r IE to be advertised in Assoc + * req: + * Type 0xDD + * Length 0x0B + * OUI 0x00 0x00 0x0F + * Type 0x22 + * subtype 0x00 + * Version 0x01 + * Length 0x04 + * Data 0x00 00 00 01(0th bit is 1 means adaptive 11r is + * supported) + */ + adaptive_11r_ie = qdf_mem_malloc(ADAPTIVE_11R_STA_IE_LEN + 2); + if (!adaptive_11r_ie) + return QDF_STATUS_E_FAILURE; + + /* Fill the Vendor IE Type (0xDD) */ + buf = adaptive_11r_ie; + *buf = WLAN_ELEMID_VENDOR; + buf++; + + /* Fill the Vendor IE length (0x0B) */ + *buf = ADAPTIVE_11R_STA_IE_LEN; + buf++; + + /* + * Fill the Adaptive 11r Vendor specific OUI(0x00 0x00 0x0F 0x22) + */ + qdf_mem_copy(buf, ADAPTIVE_11R_STA_OUI, ADAPTIVE_11R_OUI_LEN); + buf += ADAPTIVE_11R_OUI_LEN; + + /* Fill Adaptive 11r Vendor specific Subtype (0x00) */ + *buf = ADAPTIVE_11R_OUI_SUBTYPE; + buf++; + + /* Fill Adaptive 11r Version (0x01) */ + *buf = ADAPTIVE_11R_OUI_VERSION; + buf++; + + /* Fill Adaptive 11r IE Data length (0x04) */ + *buf = ADAPTIVE_11R_DATA_LEN; + buf++; + + /* Fill Adaptive 11r IE Data (0x00 0x00 0x00 0x01) */ + qdf_mem_copy(buf, ADAPTIVE_11R_OUI_DATA, ADAPTIVE_11R_DATA_LEN); + + *ie_len = ADAPTIVE_11R_STA_IE_LEN + 2; + *ie_buf = adaptive_11r_ie; + + return QDF_STATUS_SUCCESS; +} + +#else +static inline +QDF_STATUS lim_fill_adaptive_11r_ie(struct pe_session *pe_session, + uint8_t **ie_buf, uint8_t *ie_len) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * lim_send_assoc_req_mgmt_frame() - Send association request + * @mac_ctx: Handle to MAC context + * @mlm_assoc_req: Association request information + * @pe_session: PE session information + * + * Builds and transmits association request frame to AP. + * + * Return: Void + */ +void +lim_send_assoc_req_mgmt_frame(struct mac_context *mac_ctx, + tLimMlmAssocReq *mlm_assoc_req, + struct pe_session *pe_session) +{ + int ret; + tDot11fAssocRequest *frm; + uint16_t caps; + uint8_t *frame, *rsnx_ie = NULL; + QDF_STATUS sir_status; + tLimMlmAssocCnf assoc_cnf; + uint32_t bytes = 0, payload, status; + uint8_t qos_enabled, wme_enabled, wsm_enabled; + void *packet; + QDF_STATUS qdf_status; + uint16_t add_ie_len, current_len = 0, vendor_ie_len = 0; + uint8_t *add_ie; + const uint8_t *wps_ie = NULL; + uint8_t power_caps = false; + uint8_t tx_flag = 0; + uint8_t vdev_id = 0; + bool vht_enabled = false; + tDot11fIEExtCap extr_ext_cap; + bool extr_ext_flag = true, is_open_auth = false; + tpSirMacMgmtHdr mac_hdr; + uint32_t ie_offset = 0; + uint8_t *p_ext_cap = NULL; + tDot11fIEExtCap bcn_ext_cap; + uint8_t *bcn_ie = NULL; + uint32_t bcn_ie_len = 0; + uint32_t aes_block_size_len = 0; + enum rateid min_rid = RATEID_DEFAULT; + uint8_t *mbo_ie = NULL, *adaptive_11r_ie = NULL, *vendor_ies = NULL; + uint8_t mbo_ie_len = 0, adaptive_11r_ie_len = 0, rsnx_ie_len = 0; + bool bss_mfp_capable; + + if (!pe_session) { + pe_err("pe_session is NULL"); + qdf_mem_free(mlm_assoc_req); + return; + } + + vdev_id = pe_session->vdev_id; + + /* check this early to avoid unncessary operation */ + if (!pe_session->lim_join_req) { + pe_err("pe_session->lim_join_req is NULL"); + qdf_mem_free(mlm_assoc_req); + return; + } + add_ie_len = pe_session->lim_join_req->addIEAssoc.length; + if (add_ie_len) { + add_ie = qdf_mem_malloc(add_ie_len); + if (!add_ie) { + qdf_mem_free(mlm_assoc_req); + return; + } else { + /* + * copy the additional ie to local, as this func modify + * the IE, these IE will be required in assoc/re-assoc + * retry. So do not modify the original IE. + */ + qdf_mem_copy(add_ie, pe_session->lim_join_req->addIEAssoc.addIEdata, + add_ie_len); + } + } else { + add_ie = NULL; + } + + frm = qdf_mem_malloc(sizeof(tDot11fAssocRequest)); + if (!frm) { + qdf_mem_free(mlm_assoc_req); + qdf_mem_free(add_ie); + return; + } + qdf_mem_zero((uint8_t *) frm, sizeof(tDot11fAssocRequest)); + + if (add_ie_len && pe_session->is_ext_caps_present) { + qdf_mem_zero((uint8_t *) &extr_ext_cap, + sizeof(tDot11fIEExtCap)); + sir_status = lim_strip_extcap_update_struct(mac_ctx, + add_ie, &add_ie_len, &extr_ext_cap); + if (QDF_STATUS_SUCCESS != sir_status) { + extr_ext_flag = false; + pe_debug("Unable to Stripoff ExtCap IE from Assoc Req"); + } else { + struct s_ext_cap *p_ext_cap = (struct s_ext_cap *) + extr_ext_cap.bytes; + + if (p_ext_cap->interworking_service) + p_ext_cap->qos_map = 1; + extr_ext_cap.num_bytes = + lim_compute_ext_cap_ie_length(&extr_ext_cap); + extr_ext_flag = (extr_ext_cap.num_bytes > 0); + } + } else { + pe_debug("No addn IE or peer doesn't support addnIE for Assoc Req"); + extr_ext_flag = false; + } + + caps = mlm_assoc_req->capabilityInfo; +#if defined(FEATURE_WLAN_WAPI) + /* + * According to WAPI standard: + * 7.3.1.4 Capability Information field + * In WAPI, non-AP STAs within an ESS set the Privacy subfield to 0 + * in transmitted Association or Reassociation management frames. + * APs ignore the Privacy subfield within received Association and + * Reassociation management frames. + */ + if (pe_session->encryptType == eSIR_ED_WPI) + ((tSirMacCapabilityInfo *) &caps)->privacy = 0; +#endif + swap_bit_field16(caps, (uint16_t *) &frm->Capabilities); + + frm->ListenInterval.interval = mlm_assoc_req->listenInterval; + populate_dot11f_ssid2(mac_ctx, &frm->SSID); + populate_dot11f_supp_rates(mac_ctx, POPULATE_DOT11F_RATES_OPERATIONAL, + &frm->SuppRates, pe_session); + + qos_enabled = (pe_session->limQosEnabled) && + SIR_MAC_GET_QOS(pe_session->limCurrentBssCaps); + + wme_enabled = (pe_session->limWmeEnabled) && + LIM_BSS_CAPS_GET(WME, pe_session->limCurrentBssQosCaps); + + /* We prefer .11e asociations: */ + if (qos_enabled) + wme_enabled = false; + + wsm_enabled = (pe_session->limWsmEnabled) && wme_enabled && + LIM_BSS_CAPS_GET(WSM, pe_session->limCurrentBssQosCaps); + + if (pe_session->lim11hEnable && + pe_session->lim_join_req->spectrumMgtIndicator == true) { + power_caps = true; + + populate_dot11f_power_caps(mac_ctx, &frm->PowerCaps, + LIM_ASSOC, pe_session); + populate_dot11f_supp_channels(mac_ctx, &frm->SuppChannels, + LIM_ASSOC, pe_session); + + } + if (mac_ctx->rrm.rrmPEContext.rrmEnable && + SIR_MAC_GET_RRM(pe_session->limCurrentBssCaps)) { + if (power_caps == false) { + power_caps = true; + populate_dot11f_power_caps(mac_ctx, &frm->PowerCaps, + LIM_ASSOC, pe_session); + } + } + if (qos_enabled) + populate_dot11f_qos_caps_station(mac_ctx, pe_session, + &frm->QOSCapsStation); + + populate_dot11f_ext_supp_rates(mac_ctx, + POPULATE_DOT11F_RATES_OPERATIONAL, &frm->ExtSuppRates, + pe_session); + + if (mac_ctx->rrm.rrmPEContext.rrmEnable && + SIR_MAC_GET_RRM(pe_session->limCurrentBssCaps)) + populate_dot11f_rrm_ie(mac_ctx, &frm->RRMEnabledCap, + pe_session); + + /* + * The join request *should* contain zero or one of the WPA and RSN + * IEs. The payload send along with the request is a + * 'struct join_req'; the IE portion is held inside a 'tSirRSNie': + * typedef struct sSirRSNie + * { + * uint16_t length; + * uint8_t rsnIEdata[WLAN_MAX_IE_LEN+2]; + * } tSirRSNie, *tpSirRSNie; + * So, we should be able to make the following two calls harmlessly, + * since they do nothing if they don't find the given IE in the + * bytestream with which they're provided. + * The net effect of this will be to faithfully transmit whatever + * security IE is in the join request. + * However, if we're associating for the purpose of WPS + * enrollment, and we've been configured to indicate that by + * eliding the WPA or RSN IE, we just skip this: + */ + if (add_ie_len && add_ie) + wps_ie = limGetWscIEPtr(mac_ctx, add_ie, add_ie_len); + + if (!wps_ie) { + populate_dot11f_rsn_opaque(mac_ctx, + &pe_session->lim_join_req->rsnIE, + &frm->RSNOpaque); + populate_dot11f_wpa_opaque(mac_ctx, + &pe_session->lim_join_req->rsnIE, + &frm->WPAOpaque); +#if defined(FEATURE_WLAN_WAPI) + populate_dot11f_wapi_opaque(mac_ctx, + &(pe_session->lim_join_req->rsnIE), + &frm->WAPIOpaque); +#endif /* defined(FEATURE_WLAN_WAPI) */ + } + /* include WME EDCA IE as well */ + if (wme_enabled) { + populate_dot11f_wmm_info_station_per_session(mac_ctx, + pe_session, &frm->WMMInfoStation); + + if (wsm_enabled) + populate_dot11f_wmm_caps(&frm->WMMCaps); + } + + /* + * Populate HT IEs, when operating in 11n and + * when AP is also operating in 11n mode + */ + if (pe_session->htCapability && + mac_ctx->lim.htCapabilityPresentInBeacon) { + pe_debug("Populate HT Caps in Assoc Request"); + populate_dot11f_ht_caps(mac_ctx, pe_session, &frm->HTCaps); + } else if (pe_session->he_with_wep_tkip) { + pe_debug("Populate HT Caps in Assoc Request with WEP/TKIP"); + populate_dot11f_ht_caps(mac_ctx, NULL, &frm->HTCaps); + } + + if (pe_session->vhtCapability && + pe_session->vhtCapabilityPresentInBeacon) { + pe_debug("Populate VHT IEs in Assoc Request"); + populate_dot11f_vht_caps(mac_ctx, pe_session, &frm->VHTCaps); + vht_enabled = true; + if (pe_session->gLimOperatingMode.present) { + pe_debug("VHT OP mode IE in Assoc Req"); + populate_dot11f_operating_mode(mac_ctx, + &frm->OperatingMode, pe_session); + } + } else if (pe_session->he_with_wep_tkip) { + pe_debug("Populate VHT IEs in Assoc Request with WEP/TKIP"); + populate_dot11f_vht_caps(mac_ctx, NULL, &frm->VHTCaps); + } + + if (!vht_enabled && + pe_session->is_vendor_specific_vhtcaps) { + pe_debug("Populate Vendor VHT IEs in Assoc Request"); + frm->vendor_vht_ie.present = 1; + frm->vendor_vht_ie.sub_type = + pe_session->vendor_specific_vht_ie_sub_type; + frm->vendor_vht_ie.VHTCaps.present = 1; + if (!mac_ctx->mlme_cfg->vht_caps.vht_cap_info.vendor_vhtie && + pe_session->vht_config.su_beam_formee) { + pe_debug("Disable SU beamformee for vendor IE"); + pe_session->vht_config.su_beam_formee = 0; + } + populate_dot11f_vht_caps(mac_ctx, pe_session, + &frm->vendor_vht_ie.VHTCaps); + vht_enabled = true; + } + if (pe_session->is_ext_caps_present) + populate_dot11f_ext_cap(mac_ctx, vht_enabled, + &frm->ExtCap, pe_session); + + populate_dot11f_qcn_ie(mac_ctx, &frm->qcn_ie, QCN_IE_ATTR_ID_ALL); + + if (lim_is_session_he_capable(pe_session)) { + pe_debug("Populate HE IEs"); + populate_dot11f_he_caps(mac_ctx, pe_session, + &frm->he_cap); + populate_dot11f_he_6ghz_cap(mac_ctx, pe_session, + &frm->he_6ghz_band_cap); + } else if (pe_session->he_with_wep_tkip) { + pe_debug("Populate HE IEs in Assoc Request with WEP/TKIP"); + populate_dot11f_he_caps(mac_ctx, NULL, &frm->he_cap); + populate_dot11f_he_6ghz_cap(mac_ctx, pe_session, + &frm->he_6ghz_band_cap); + } + + if (pe_session->lim_join_req->is11Rconnection) { + struct bss_description *bssdescr; + + bssdescr = &pe_session->lim_join_req->bssDescription; + pe_debug("mdie = %02x %02x %02x", + (unsigned int) bssdescr->mdie[0], + (unsigned int) bssdescr->mdie[1], + (unsigned int) bssdescr->mdie[2]); + populate_mdie(mac_ctx, &frm->MobilityDomain, + pe_session->lim_join_req->bssDescription.mdie); + + /* + * IEEE80211-ai [13.2.4 FT initial mobility domain association + * over FILS in an RSN] + * Populate FT IE in association request. This FT IE should be + * same as the FT IE received in auth response frame during the + * FT-FILS authentication. + */ + if (lim_is_fils_connection(pe_session)) + populate_fils_ft_info(mac_ctx, &frm->FTInfo, + pe_session); + } + +#ifdef FEATURE_WLAN_ESE + /* + * ESE Version IE will be included in association request + * when ESE is enabled on DUT through ini and it is also + * advertised by the peer AP to which we are trying to + * associate to. + */ + if (pe_session->is_ese_version_ie_present && + mac_ctx->mlme_cfg->lfr.ese_enabled) + populate_dot11f_ese_version(&frm->ESEVersion); + /* For ESE Associations fill the ESE IEs */ + if (pe_session->isESEconnection && + pe_session->lim_join_req->isESEFeatureIniEnabled) { +#ifndef FEATURE_DISABLE_RM + populate_dot11f_ese_rad_mgmt_cap(&frm->ESERadMgmtCap); +#endif + } +#endif + + /* + * Extcap IE now support variable length, merge Extcap IE from addn_ie + * may change the frame size. Therefore, MUST merge ExtCap IE before + * dot11f get packed payload size. + */ + if (extr_ext_flag) + lim_merge_extcap_struct(&frm->ExtCap, &extr_ext_cap, true); + + /* Clear the bits in EXTCAP IE if AP not advertise it in beacon */ + if (frm->ExtCap.present && pe_session->is_ext_caps_present) { + ie_offset = DOT11F_FF_TIMESTAMP_LEN + + DOT11F_FF_BEACONINTERVAL_LEN + + DOT11F_FF_CAPABILITIES_LEN; + + qdf_mem_zero((uint8_t *)&bcn_ext_cap, sizeof(tDot11fIEExtCap)); + if (pe_session->beacon && pe_session->bcnLen > ie_offset) { + bcn_ie = pe_session->beacon + ie_offset; + bcn_ie_len = pe_session->bcnLen - ie_offset; + p_ext_cap = (uint8_t *)wlan_get_ie_ptr_from_eid( + DOT11F_EID_EXTCAP, + bcn_ie, bcn_ie_len); + lim_update_extcap_struct(mac_ctx, p_ext_cap, + &bcn_ext_cap); + lim_merge_extcap_struct(&frm->ExtCap, &bcn_ext_cap, + false); + } + + populate_dot11f_btm_caps(mac_ctx, pe_session, &frm->ExtCap); + /* + * TWT extended capabilities should be populated after the + * intersection of beacon caps and self caps is done because + * the bits for TWT are unique to STA and AP and cannot be + * intersected. + */ + populate_dot11f_twt_extended_caps(mac_ctx, pe_session, + &frm->ExtCap); + } + + if (QDF_STATUS_SUCCESS != lim_strip_supp_op_class_update_struct(mac_ctx, + add_ie, &add_ie_len, &frm->SuppOperatingClasses)) + pe_debug("Unable to Stripoff supp op classes IE from Assoc Req"); + + if (lim_is_fils_connection(pe_session)) { + populate_dot11f_fils_params(mac_ctx, frm, pe_session); + aes_block_size_len = AES_BLOCK_SIZE; + } + + /* RSNX IE for SAE PWE derivation based on H2E */ + if (wlan_get_ie_ptr_from_eid(WLAN_ELEMID_RSNXE, add_ie, add_ie_len)) { + rsnx_ie = qdf_mem_malloc(WLAN_MAX_IE_LEN + 2); + if (!rsnx_ie) + goto end; + + qdf_status = lim_strip_ie(mac_ctx, add_ie, &add_ie_len, + WLAN_ELEMID_RSNXE, ONE_BYTE, + NULL, 0, rsnx_ie, WLAN_MAX_IE_LEN); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + pe_err("Failed to strip Vendor IEs"); + goto end; + } + rsnx_ie_len = rsnx_ie[1] + 2; + } + + /* + * MBO IE needs to be appendded at the end of the assoc request + * frame and is not parsed and unpacked by the frame parser + * as the supplicant can send multiple TLVs with same Attribute + * in the MBO IE and the frame parser does not support multiple + * TLVs with same attribute in a single IE. + * Strip off the MBO IE from add_ie and append it at the end. + */ + if (wlan_get_vendor_ie_ptr_from_oui(SIR_MAC_MBO_OUI, + SIR_MAC_MBO_OUI_SIZE, add_ie, add_ie_len)) { + mbo_ie = qdf_mem_malloc(DOT11F_IE_MBO_IE_MAX_LEN + 2); + if (!mbo_ie) + goto end; + + qdf_status = lim_strip_ie(mac_ctx, add_ie, &add_ie_len, + WLAN_ELEMID_VENDOR, ONE_BYTE, + SIR_MAC_MBO_OUI, + SIR_MAC_MBO_OUI_SIZE, + mbo_ie, DOT11F_IE_MBO_IE_MAX_LEN); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + pe_err("Failed to strip MBO IE"); + goto end; + } + + /* Include the EID and length fields */ + mbo_ie_len = mbo_ie[1] + 2; + + if (pe_session->connected_akm == ANI_AKM_TYPE_NONE) + is_open_auth = true; + + pe_debug("Stripped MBO IE of length %d is_open_auth:%d", + mbo_ie_len, is_open_auth); + + if (!is_open_auth) { + bss_mfp_capable = + lim_get_vdev_rmf_capable(mac_ctx, pe_session); + if (!bss_mfp_capable) { + pe_debug("Peer doesn't support PMF, Don't add MBO IE"); + qdf_mem_free(mbo_ie); + mbo_ie = NULL; + mbo_ie_len = 0; + } + } + } + + /* + * Strip rest of the vendor IEs and append to the assoc request frame. + * Append the IEs just before MBO IEs as MBO IEs have to be at the + * end of the frame. + */ + if (wlan_get_ie_ptr_from_eid(WLAN_ELEMID_VENDOR, add_ie, add_ie_len)) { + vendor_ies = qdf_mem_malloc(MAX_VENDOR_IES_LEN + 2); + if (vendor_ies) { + current_len = add_ie_len; + qdf_status = lim_strip_ie(mac_ctx, add_ie, &add_ie_len, + WLAN_ELEMID_VENDOR, ONE_BYTE, + NULL, + 0, + vendor_ies, + MAX_VENDOR_IES_LEN); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + pe_err("Failed to strip Vendor IEs"); + goto end; + } + + vendor_ie_len = current_len - add_ie_len; + pe_debug("Stripped vendor IEs of size: %u", + current_len); + } + } + + qdf_status = lim_fill_adaptive_11r_ie(pe_session, &adaptive_11r_ie, + &adaptive_11r_ie_len); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + pe_err("Failed to fill adaptive 11r IE"); + goto end; + } + + /* + * Do unpack to populate the add_ie buffer to frm structure + * before packing the frm structure. In this way, the IE ordering + * which the latest 802.11 spec mandates is maintained. + */ + if (add_ie_len) { + ret = dot11f_unpack_assoc_request(mac_ctx, add_ie, + add_ie_len, frm, true); + if (DOT11F_FAILED(ret)) { + pe_err("unpack failed, ret: 0x%x", ret); + goto end; + } + } + + status = dot11f_get_packed_assoc_request_size(mac_ctx, frm, &payload); + if (DOT11F_FAILED(status)) { + pe_err("Association Request packet size failure(0x%08x)", + status); + /* We'll fall back on the worst case scenario: */ + payload = sizeof(tDot11fAssocRequest); + } else if (DOT11F_WARNED(status)) { + pe_warn("Association request packet size warning (0x%08x)", + status); + } + + bytes = payload + sizeof(tSirMacMgmtHdr) + aes_block_size_len + + rsnx_ie_len + mbo_ie_len + adaptive_11r_ie_len + vendor_ie_len; + + qdf_status = cds_packet_alloc((uint16_t) bytes, (void **)&frame, + (void **)&packet); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes", bytes); + + pe_session->limMlmState = pe_session->limPrevMlmState; + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_MLM_STATE, + pe_session->peSessionId, + pe_session->limMlmState)); + + /* Update PE session id */ + assoc_cnf.sessionId = pe_session->peSessionId; + + assoc_cnf.resultCode = eSIR_SME_RESOURCES_UNAVAILABLE; + + lim_post_sme_message(mac_ctx, LIM_MLM_ASSOC_CNF, + (uint32_t *) &assoc_cnf); + + goto end; + } + /* Paranoia: */ + qdf_mem_zero(frame, bytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac_ctx, frame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ASSOC_REQ, pe_session->bssId, + pe_session->self_mac_addr); + /* That done, pack the Assoc Request: */ + status = dot11f_pack_assoc_request(mac_ctx, frm, + frame + sizeof(tSirMacMgmtHdr), payload, &payload); + if (DOT11F_FAILED(status)) { + pe_err("Assoc request pack failure (0x%08x)", status); + cds_packet_free((void *)packet); + goto end; + } else if (DOT11F_WARNED(status)) { + pe_warn("Assoc request pack warning (0x%08x)", status); + } + + if (rsnx_ie && rsnx_ie_len) { + qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload, + rsnx_ie, rsnx_ie_len); + payload = payload + rsnx_ie_len; + } + + /* Copy the vendor IEs to the end of the frame */ + qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload, + vendor_ies, vendor_ie_len); + payload = payload + vendor_ie_len; + + /* Copy the MBO IE to the end of the frame */ + qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload, + mbo_ie, mbo_ie_len); + payload = payload + mbo_ie_len; + + /* + * Copy the Vendor specific Adaptive 11r IE to end of the + * assoc request frame + */ + qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload, + adaptive_11r_ie, adaptive_11r_ie_len); + payload = payload + adaptive_11r_ie_len; + + if (pe_session->assoc_req) { + qdf_mem_free(pe_session->assoc_req); + pe_session->assoc_req = NULL; + pe_session->assocReqLen = 0; + } + + if (lim_is_fils_connection(pe_session)) { + qdf_status = aead_encrypt_assoc_req(mac_ctx, pe_session, + frame, &payload); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + cds_packet_free((void *)packet); + goto end; + } + } + + pe_session->assoc_req = qdf_mem_malloc(payload); + if (pe_session->assoc_req) { + /* + * Store the Assoc request. This is sent to csr/hdd in + * join cnf response. + */ + qdf_mem_copy(pe_session->assoc_req, + frame + sizeof(tSirMacMgmtHdr), payload); + pe_session->assocReqLen = payload; + } + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + if (pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_STA_MODE) + tx_flag |= HAL_USE_PEER_STA_REQUESTED_MASK; + + mac_hdr = (tpSirMacMgmtHdr) frame; + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, mac_hdr->fc.subType)); + + pe_nofl_rl_info("Assoc req TX: vdev %d to "QDF_MAC_ADDR_FMT" seq num %d", pe_session->vdev_id, + QDF_MAC_ADDR_REF(pe_session->bssId), + mac_ctx->mgmtSeqNum); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + frame, (uint16_t)(sizeof(tSirMacMgmtHdr) + payload)); + + min_rid = lim_get_min_session_txrate(pe_session); + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_ASSOC_START_EVENT, + pe_session, QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); + lim_diag_mgmt_tx_event_report(mac_ctx, mac_hdr, + pe_session, QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); + qdf_status = + wma_tx_frameWithTxComplete(mac_ctx, packet, + (uint16_t) (sizeof(tSirMacMgmtHdr) + payload), + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, 7, + lim_tx_complete, frame, lim_assoc_tx_complete_cnf, + tx_flag, vdev_id, false, 0, min_rid); + MTRACE(qdf_trace + (QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send Association Request (%X)!", + qdf_status); + mac_ctx->assoc_ack_status = LIM_TX_FAILED; + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_ASSOC_ACK_EVENT, + pe_session, SENT_FAIL, QDF_STATUS_E_FAILURE); + /* Pkt will be freed up by the callback */ + } + +end: + qdf_mem_free(rsnx_ie); + qdf_mem_free(vendor_ies); + qdf_mem_free(mbo_ie); + + /* Free up buffer allocated for mlm_assoc_req */ + qdf_mem_free(adaptive_11r_ie); + qdf_mem_free(mlm_assoc_req); + qdf_mem_free(add_ie); + mlm_assoc_req = NULL; + qdf_mem_free(frm); + return; +} + +/** + * lim_addba_rsp_tx_complete_cnf() - Confirmation for add BA response OTA + * @context: pointer to global mac + * @buf: buffer which is nothing but entire ADD BA frame + * @tx_complete : Sent status + * @params; tx completion params + * + * Return: This returns QDF_STATUS + */ +static QDF_STATUS lim_addba_rsp_tx_complete_cnf(void *context, + qdf_nbuf_t buf, + uint32_t tx_complete, + void *params) +{ + struct mac_context *mac_ctx = (struct mac_context *)context; + tSirMacMgmtHdr *mac_hdr; + tDot11faddba_rsp rsp; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint32_t frame_len; + QDF_STATUS status; + uint8_t *data; + struct wmi_mgmt_params *mgmt_params = (struct wmi_mgmt_params *)params; + + if (tx_complete == WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK) + pe_debug("Add ba response successfully sent"); + else + pe_debug("Fail to send add ba response"); + + if (!buf) { + pe_err("Addba response frame buffer is NULL"); + return QDF_STATUS_E_FAILURE; + } + + data = qdf_nbuf_data(buf); + + if (!data) { + pe_err("Addba response frame is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mac_hdr = (tSirMacMgmtHdr *)data; + qdf_mem_zero((void *)&rsp, sizeof(tDot11faddba_rsp)); + frame_len = sizeof(rsp); + status = dot11f_unpack_addba_rsp(mac_ctx, (uint8_t *)data + + sizeof(*mac_hdr), frame_len, + &rsp, false); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to unpack and parse (0x%08x, %d bytes)", + status, frame_len); + goto error; + } + + cdp_addba_resp_tx_completion(soc, mac_hdr->da, mgmt_params->vdev_id, + rsp.addba_param_set.tid, tx_complete); +error: + if (buf) + qdf_nbuf_free(buf); + + return QDF_STATUS_SUCCESS; +} + +#define SAE_AUTH_ALGO_LEN 2 +#define SAE_AUTH_ALGO_OFFSET 0 +static bool lim_is_ack_for_sae_auth(qdf_nbuf_t buf) +{ + tpSirMacMgmtHdr mac_hdr; + uint16_t *sae_auth, min_len; + + if (!buf) { + pe_debug("buf is NULL"); + return false; + } + + min_len = sizeof(tSirMacMgmtHdr) + SAE_AUTH_ALGO_LEN; + if (qdf_nbuf_len(buf) < min_len) { + pe_debug("buf_len %d less than min_len %d", + (uint32_t)qdf_nbuf_len(buf), min_len); + return false; + } + + mac_hdr = (tpSirMacMgmtHdr)(qdf_nbuf_data(buf)); + if (mac_hdr->fc.subType == SIR_MAC_MGMT_AUTH) { + sae_auth = (uint16_t *)((uint8_t *)mac_hdr + + sizeof(tSirMacMgmtHdr)); + if (sae_auth[SAE_AUTH_ALGO_OFFSET] == + eSIR_AUTH_TYPE_SAE) + return true; + } + + return false; +} + +/** + * lim_auth_tx_complete_cnf()- Confirmation for auth sent over the air + * @context: pointer to global mac + * @buf: buffer + * @tx_complete : Sent status + * @params; tx completion params + * + * Return: This returns QDF_STATUS + */ +static QDF_STATUS lim_auth_tx_complete_cnf(void *context, + qdf_nbuf_t buf, + uint32_t tx_complete, + void *params) +{ + struct mac_context *mac_ctx = (struct mac_context *)context; + uint16_t auth_ack_status; + uint16_t reason_code; + bool sae_auth_acked; + + pe_nofl_rl_info("Auth TX: %s", + (tx_complete == WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK) ? + "success" : "fail"); + if (tx_complete == WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK) { + mac_ctx->auth_ack_status = LIM_ACK_RCD_SUCCESS; + auth_ack_status = ACKED; + reason_code = QDF_STATUS_SUCCESS; + sae_auth_acked = lim_is_ack_for_sae_auth(buf); + /* + * 'Change' timer for future activations only if ack + * received is not for WPA SAE auth frames. + */ + if (!sae_auth_acked) + lim_deactivate_and_change_timer(mac_ctx, + eLIM_AUTH_RETRY_TIMER); + } else if (tx_complete == WMI_MGMT_TX_COMP_TYPE_COMPLETE_NO_ACK) { + mac_ctx->auth_ack_status = LIM_ACK_RCD_FAILURE; + auth_ack_status = NOT_ACKED; + reason_code = QDF_STATUS_E_FAILURE; + } else { + mac_ctx->auth_ack_status = LIM_TX_FAILED; + auth_ack_status = SENT_FAIL; + reason_code = QDF_STATUS_E_FAILURE; + } + + if (buf) + qdf_nbuf_free(buf); + + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_AUTH_ACK_EVENT, + NULL, auth_ack_status, reason_code); + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_send_auth_mgmt_frame() - Send an Authentication frame + * + * @mac_ctx: Pointer to Global MAC structure + * @auth_frame: Pointer to Authentication frame structure + * @peer_addr: MAC address of destination peer + * @wep_bit: wep bit in frame control for Authentication frame3 + * @session: PE session information + * + * This function is called by lim_process_mlm_messages(). Authentication frame + * is formatted and sent when this function is called. + * + * Return: void + */ +void +lim_send_auth_mgmt_frame(struct mac_context *mac_ctx, + tpSirMacAuthFrameBody auth_frame, + tSirMacAddr peer_addr, + uint8_t wep_challenge_len, + struct pe_session *session) +{ + uint8_t *frame, *body; + uint32_t frame_len = 0, body_len = 0; + tpSirMacMgmtHdr mac_hdr; + void *packet; + QDF_STATUS qdf_status, status; + uint8_t tx_flag = 0; + uint8_t vdev_id = 0; + uint16_t ft_ies_length = 0; + bool challenge_req = false; + enum rateid min_rid = RATEID_DEFAULT; + uint16_t ch_freq_tx_frame = 0; + + if (!session) { + pe_err("Error: psession Entry is NULL"); + return; + } + + vdev_id = session->vdev_id; + + if (wep_challenge_len) { + /* + * Auth frame3 to be sent with encrypted framebody + * + * Allocate buffer for Authenticaton frame of size + * equal to management frame header length plus 2 bytes + * each for auth algorithm number, transaction number, + * status code, 128 bytes for challenge text and + * 4 bytes each for IV & ICV. + */ + body_len = wep_challenge_len + LIM_ENCR_AUTH_INFO_LEN; + frame_len = sizeof(tSirMacMgmtHdr) + body_len; + + goto alloc_packet; + } + + switch (auth_frame->authTransactionSeqNumber) { + case SIR_MAC_AUTH_FRAME_1: + /* + * Allocate buffer for Authenticaton frame of size + * equal to management frame header length plus 2 bytes + * each for auth algorithm number, transaction number + * and status code. + */ + + body_len = SIR_MAC_AUTH_FRAME_INFO_LEN; + frame_len = sizeof(tSirMacMgmtHdr) + body_len; + + status = lim_create_fils_auth_data(mac_ctx, auth_frame, + session, &frame_len); + if (QDF_IS_STATUS_ERROR(status)) + return; + + if (auth_frame->authAlgoNumber == eSIR_FT_AUTH) { + if (session->ftPEContext.pFTPreAuthReq && + 0 != session->ftPEContext.pFTPreAuthReq-> + ft_ies_length) { + ft_ies_length = session->ftPEContext. + pFTPreAuthReq->ft_ies_length; + frame_len += ft_ies_length; + pe_debug("Auth frame, FTIES length added=%d", + ft_ies_length); + } else { + pe_debug("Auth frame, Does not contain FTIES!"); + frame_len += (2 + SIR_MDIE_SIZE); + } + } + + /* include MDIE in FILS authentication frame */ + if (session->lim_join_req && + session->lim_join_req->is11Rconnection && + auth_frame->authAlgoNumber == SIR_FILS_SK_WITHOUT_PFS && + session->lim_join_req->bssDescription.mdiePresent) + frame_len += (2 + SIR_MDIE_SIZE); + break; + + case SIR_MAC_AUTH_FRAME_2: + if ((auth_frame->authAlgoNumber == eSIR_OPEN_SYSTEM) || + ((auth_frame->authAlgoNumber == eSIR_SHARED_KEY) && + (auth_frame->authStatusCode != + eSIR_MAC_SUCCESS_STATUS))) { + /* + * Allocate buffer for Authenticaton frame of size + * equal to management frame header length plus + * 2 bytes each for auth algorithm number, + * transaction number and status code. + */ + + body_len = SIR_MAC_AUTH_FRAME_INFO_LEN; + frame_len = sizeof(tSirMacMgmtHdr) + body_len; + } else { + /* + * Shared Key algorithm with challenge text + * to be sent. + * + * Allocate buffer for Authenticaton frame of size + * equal to management frame header length plus + * 2 bytes each for auth algorithm number, + * transaction number, status code and 128 bytes + * for challenge text. + */ + + challenge_req = true; + body_len = SIR_MAC_AUTH_FRAME_INFO_LEN + + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH + + SIR_MAC_CHALLENGE_ID_LEN; + frame_len = sizeof(tSirMacMgmtHdr) + body_len; + } + break; + + case SIR_MAC_AUTH_FRAME_3: + /* + * Auth frame3 to be sent without encrypted framebody + * + * Allocate buffer for Authenticaton frame of size equal + * to management frame header length plus 2 bytes each + * for auth algorithm number, transaction number and + * status code. + */ + + body_len = SIR_MAC_AUTH_FRAME_INFO_LEN; + frame_len = sizeof(tSirMacMgmtHdr) + body_len; + break; + + case SIR_MAC_AUTH_FRAME_4: + /* + * Allocate buffer for Authenticaton frame of size equal + * to management frame header length plus 2 bytes each + * for auth algorithm number, transaction number and + * status code. + */ + + body_len = SIR_MAC_AUTH_FRAME_INFO_LEN; + frame_len = sizeof(tSirMacMgmtHdr) + body_len; + + break; + default: + pe_err("Invalid auth transaction seq num"); + return; + } /* switch (auth_frame->authTransactionSeqNumber) */ + +alloc_packet: + qdf_status = cds_packet_alloc((uint16_t) frame_len, (void **)&frame, + (void **)&packet); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("call to bufAlloc failed for AUTH frame"); + return; + } + + qdf_mem_zero(frame, frame_len); + + /* Prepare BD */ + lim_populate_mac_header(mac_ctx, frame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_AUTH, peer_addr, session->self_mac_addr); + mac_hdr = (tpSirMacMgmtHdr) frame; + if (wep_challenge_len) + mac_hdr->fc.wep = LIM_WEP_IN_FC; + else + mac_hdr->fc.wep = LIM_NO_WEP_IN_FC; + + /* Prepare BSSId */ + if (LIM_IS_AP_ROLE(session)) + qdf_mem_copy((uint8_t *) mac_hdr->bssId, + (uint8_t *) session->bssId, + sizeof(tSirMacAddr)); + + /* Prepare Authentication frame body */ + body = frame + sizeof(tSirMacMgmtHdr); + + if (wep_challenge_len) { + qdf_mem_copy(body, (uint8_t *) auth_frame, body_len); + } else { + *((uint16_t *) (body)) = + sir_swap_u16if_needed(auth_frame->authAlgoNumber); + body += sizeof(uint16_t); + body_len -= sizeof(uint16_t); + + *((uint16_t *) (body)) = + sir_swap_u16if_needed( + auth_frame->authTransactionSeqNumber); + body += sizeof(uint16_t); + body_len -= sizeof(uint16_t); + + *((uint16_t *) (body)) = + sir_swap_u16if_needed(auth_frame->authStatusCode); + body += sizeof(uint16_t); + body_len -= sizeof(uint16_t); + + if (challenge_req) { + if (body_len < SIR_MAC_AUTH_CHALLENGE_BODY_LEN) { + /* copy challenge IE id, len, challenge text */ + *body = auth_frame->type; + body++; + body_len -= sizeof(uint8_t); + *body = auth_frame->length; + body++; + body_len -= sizeof(uint8_t); + qdf_mem_copy(body, auth_frame->challengeText, + body_len); + pe_err("Incomplete challenge info: length: %d, expected: %d", + body_len, + SIR_MAC_AUTH_CHALLENGE_BODY_LEN); + body += body_len; + body_len = 0; + } else { + /* copy challenge IE id, len, challenge text */ + *body = auth_frame->type; + body++; + *body = auth_frame->length; + body++; + qdf_mem_copy(body, auth_frame->challengeText, + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH); + body += SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH; + body_len -= SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH + + SIR_MAC_CHALLENGE_ID_LEN; + } + } + + if ((auth_frame->authAlgoNumber == eSIR_FT_AUTH) && + (auth_frame->authTransactionSeqNumber == + SIR_MAC_AUTH_FRAME_1) && + (session->ftPEContext.pFTPreAuthReq)) { + + if (ft_ies_length > 0) { + qdf_mem_copy(body, + session->ftPEContext. + pFTPreAuthReq->ft_ies, + ft_ies_length); + pe_debug("Auth1 Frame FTIE is: "); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_DEBUG, + (uint8_t *) body, + ft_ies_length); + } else if (session->ftPEContext. + pFTPreAuthReq->pbssDescription) { + /* MDID attr is 54 */ + *body = WLAN_ELEMID_MOBILITY_DOMAIN; + body++; + *body = SIR_MDIE_SIZE; + body++; + qdf_mem_copy(body, + &session->ftPEContext.pFTPreAuthReq-> + pbssDescription->mdie[0], + SIR_MDIE_SIZE); + } + } else if ((auth_frame->authAlgoNumber == + SIR_FILS_SK_WITHOUT_PFS) && + (auth_frame->authTransactionSeqNumber == + SIR_MAC_AUTH_FRAME_1)) { + pe_debug("FILS: appending fils Auth data"); + lim_add_fils_data_to_auth_frame(session, body); + } + } + + pe_nofl_info("Auth TX: seq %d seq num %d status %d WEP %d to " QDF_MAC_ADDR_FMT, + auth_frame->authTransactionSeqNumber, mac_ctx->mgmtSeqNum, + auth_frame->authStatusCode, mac_hdr->fc.wep, + QDF_MAC_ADDR_REF(mac_hdr->da)); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + frame, frame_len); + + if ((session->ftPEContext.pFTPreAuthReq) && + (!wlan_reg_is_24ghz_ch_freq( + session->ftPEContext.pFTPreAuthReq->pre_auth_channel_freq))) + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + else if (!wlan_reg_is_24ghz_ch_freq(session->curr_op_freq) || + session->opmode == QDF_P2P_CLIENT_MODE || + session->opmode == QDF_P2P_GO_MODE) + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + if (session->opmode == QDF_P2P_CLIENT_MODE || + session->opmode == QDF_STA_MODE) + tx_flag |= HAL_USE_PEER_STA_REQUESTED_MASK; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + session->peSessionId, mac_hdr->fc.subType)); + + mac_ctx->auth_ack_status = LIM_ACK_NOT_RCD; + min_rid = lim_get_min_session_txrate(session); + lim_diag_mgmt_tx_event_report(mac_ctx, mac_hdr, + session, QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); + + if (session->ftPEContext.pFTPreAuthReq) + ch_freq_tx_frame = session->ftPEContext. + pFTPreAuthReq->pre_auth_channel_freq; + + qdf_status = wma_tx_frameWithTxComplete(mac_ctx, packet, + (uint16_t)frame_len, + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, + 7, lim_tx_complete, frame, + lim_auth_tx_complete_cnf, + tx_flag, vdev_id, false, + ch_freq_tx_frame, min_rid); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + session->peSessionId, qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("*** Could not send Auth frame, retCode=%X ***", + qdf_status); + mac_ctx->auth_ack_status = LIM_TX_FAILED; + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_AUTH_ACK_EVENT, + session, SENT_FAIL, QDF_STATUS_E_FAILURE); + /* Pkt will be freed up by the callback */ + } + return; +} + +QDF_STATUS lim_send_deauth_cnf(struct mac_context *mac_ctx) +{ + uint16_t aid; + tpDphHashNode sta_ds; + tLimMlmDeauthReq *deauth_req; + tLimMlmDeauthCnf deauth_cnf; + struct pe_session *session_entry; + + deauth_req = mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq; + if (deauth_req) { + if (tx_timer_running( + &mac_ctx->lim.lim_timers.gLimDeauthAckTimer)) + lim_deactivate_and_change_timer(mac_ctx, + eLIM_DEAUTH_ACK_TIMER); + + session_entry = pe_find_session_by_session_id(mac_ctx, + deauth_req->sessionId); + if (!session_entry) { + pe_err("session does not exist for given sessionId"); + deauth_cnf.resultCode = + eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + sta_ds = + dph_lookup_hash_entry(mac_ctx, + deauth_req->peer_macaddr.bytes, + &aid, + &session_entry->dph.dphHashTable); + if (!sta_ds) { + deauth_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + /* / Receive path cleanup with dummy packet */ + lim_ft_cleanup_pre_auth_info(mac_ctx, session_entry); + lim_cleanup_rx_path(mac_ctx, sta_ds, session_entry); + if ((session_entry->limSystemRole == eLIM_STA_ROLE) && + ( +#ifdef FEATURE_WLAN_ESE + (session_entry->isESEconnection) || +#endif + (session_entry->isFastRoamIniFeatureEnabled) || + (session_entry->is11Rconnection))) { + pe_debug("FT Preauth (%pK,%d) Deauth rc %d src = %d", + session_entry, + session_entry->peSessionId, + deauth_req->reasonCode, + deauth_req->deauthTrigger); + lim_ft_cleanup(mac_ctx, session_entry); + } else { +#ifdef FEATURE_WLAN_ESE + pe_debug("No FT Preauth Session Cleanup in role %d" + " isESE %d" + " isLFR %d" + " is11r %d, Deauth reason %d Trigger = %d", + session_entry->limSystemRole, + session_entry->isESEconnection, + session_entry->isFastRoamIniFeatureEnabled, + session_entry->is11Rconnection, + deauth_req->reasonCode, + deauth_req->deauthTrigger); +#else + pe_debug("No FT Preauth Session Cleanup in role %d" + " isLFR %d" + " is11r %d, Deauth reason %d Trigger = %d", + session_entry->limSystemRole, + session_entry->isFastRoamIniFeatureEnabled, + session_entry->is11Rconnection, + deauth_req->reasonCode, + deauth_req->deauthTrigger); +#endif + } + /* Free up buffer allocated for mlmDeauthReq */ + qdf_mem_free(deauth_req); + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq = NULL; + } + return QDF_STATUS_SUCCESS; +end: + qdf_copy_macaddr(&deauth_cnf.peer_macaddr, + &deauth_req->peer_macaddr); + deauth_cnf.deauthTrigger = deauth_req->deauthTrigger; + deauth_cnf.aid = deauth_req->aid; + deauth_cnf.sessionId = deauth_req->sessionId; + + /* Free up buffer allocated */ + /* for mlmDeauthReq */ + qdf_mem_free(deauth_req); + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq = NULL; + + lim_post_sme_message(mac_ctx, + LIM_MLM_DEAUTH_CNF, (uint32_t *) &deauth_cnf); + return QDF_STATUS_SUCCESS; +} + +/** + * lim_send_disassoc_cnf() - Send disassoc confirmation to SME + * + * @mac_ctx: Handle to MAC context + * + * Sends disassoc confirmation to SME. Removes disassoc request stored + * in lim. + * + * Return: QDF_STATUS_SUCCESS + */ + +QDF_STATUS lim_send_disassoc_cnf(struct mac_context *mac_ctx) +{ + uint16_t aid; + tpDphHashNode sta_ds; + tLimMlmDisassocCnf disassoc_cnf; + struct pe_session *pe_session; + tLimMlmDisassocReq *disassoc_req; + + disassoc_req = mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq; + if (disassoc_req) { + if (tx_timer_running( + &mac_ctx->lim.lim_timers.gLimDisassocAckTimer)) + lim_deactivate_and_change_timer(mac_ctx, + eLIM_DISASSOC_ACK_TIMER); + + pe_session = pe_find_session_by_session_id( + mac_ctx, disassoc_req->sessionId); + if (!pe_session) { + pe_err("No session for given sessionId"); + disassoc_cnf.resultCode = + eSIR_SME_INVALID_PARAMETERS; + goto end; + } + + sta_ds = dph_lookup_hash_entry(mac_ctx, + disassoc_req->peer_macaddr.bytes, &aid, + &pe_session->dph.dphHashTable); + if (!sta_ds) { + pe_err("StaDs Null"); + disassoc_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS; + goto end; + } + /* Receive path cleanup with dummy packet */ + if (QDF_STATUS_SUCCESS != + lim_cleanup_rx_path(mac_ctx, sta_ds, pe_session)) { + disassoc_cnf.resultCode = + eSIR_SME_RESOURCES_UNAVAILABLE; + pe_err("cleanup_rx_path error"); + goto end; + } + if (LIM_IS_STA_ROLE(pe_session) && + (disassoc_req->reasonCode != + eSIR_MAC_DISASSOC_DUE_TO_FTHANDOFF_REASON)) { + pe_debug("FT Preauth Session (%pK %d) Clean up", + pe_session, pe_session->peSessionId); + + /* Delete FT session if there exists one */ + lim_ft_cleanup_pre_auth_info(mac_ctx, pe_session); + } + /* Free up buffer allocated for mlmDisassocReq */ + qdf_mem_free(disassoc_req); + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq = NULL; + return QDF_STATUS_SUCCESS; + } else { + return QDF_STATUS_SUCCESS; + } +end: + qdf_mem_copy((uint8_t *) &disassoc_cnf.peerMacAddr, + (uint8_t *) disassoc_req->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE); + disassoc_cnf.aid = disassoc_req->aid; + disassoc_cnf.disassocTrigger = disassoc_req->disassocTrigger; + + /* Update PE session ID */ + disassoc_cnf.sessionId = disassoc_req->sessionId; + + if (disassoc_req) { + /* / Free up buffer allocated for mlmDisassocReq */ + qdf_mem_free(disassoc_req); + mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq = NULL; + } + + lim_post_sme_message(mac_ctx, LIM_MLM_DISASSOC_CNF, + (uint32_t *) &disassoc_cnf); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_disassoc_tx_complete_cnf(void *context, + uint32_t tx_success, + void *params) +{ + struct mac_context *max_ctx = (struct mac_context *)context; + + pe_debug("tx_success: %d", tx_success); + + return lim_send_disassoc_cnf(max_ctx); +} + +static QDF_STATUS lim_disassoc_tx_complete_cnf_handler(void *context, + qdf_nbuf_t buf, + uint32_t tx_success, + void *params) +{ + struct mac_context *max_ctx = (struct mac_context *)context; + QDF_STATUS status_code; + struct scheduler_msg msg = {0}; + + pe_debug("tx_success: %d", tx_success); + + if (buf) + qdf_nbuf_free(buf); + msg.type = (uint16_t) WMA_DISASSOC_TX_COMP; + msg.bodyptr = params; + msg.bodyval = tx_success; + + status_code = lim_post_msg_high_priority(max_ctx, &msg); + if (status_code != QDF_STATUS_SUCCESS) + pe_err("posting message: %X to LIM failed, reason: %d", + msg.type, status_code); + return status_code; +} + +QDF_STATUS lim_deauth_tx_complete_cnf(void *context, + uint32_t tx_success, + void *params) +{ + struct mac_context *mac_ctx = (struct mac_context *)context; + + pe_debug("tx_success: %d", tx_success); + + return lim_send_deauth_cnf(mac_ctx); +} + +static QDF_STATUS lim_deauth_tx_complete_cnf_handler(void *context, + qdf_nbuf_t buf, + uint32_t tx_success, + void *params) +{ + struct mac_context *mac_ctx = (struct mac_context *)context; + QDF_STATUS status_code; + struct scheduler_msg msg = {0}; + tLimMlmDeauthReq *deauth_req; + struct pe_session *session = NULL; + + deauth_req = mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq; + + pe_debug("tx_complete = %s tx_success = %d", + (tx_success == WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK) ? + "success" : "fail", tx_success); + + if (buf) + qdf_nbuf_free(buf); + if (deauth_req) + session = pe_find_session_by_session_id(mac_ctx, + deauth_req->sessionId); + if (tx_success != WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK && session && + session->deauth_retry.retry_cnt) { + if (tx_timer_running( + &mac_ctx->lim.lim_timers.gLimDeauthAckTimer)) + lim_deactivate_and_change_timer(mac_ctx, + eLIM_DEAUTH_ACK_TIMER); + lim_send_deauth_mgmt_frame(mac_ctx, + session->deauth_retry.reason_code, + session->deauth_retry.peer_macaddr.bytes, + session, true); + session->deauth_retry.retry_cnt--; + return QDF_STATUS_SUCCESS; + } + msg.type = (uint16_t) WMA_DEAUTH_TX_COMP; + msg.bodyptr = params; + msg.bodyval = tx_success; + + status_code = lim_post_msg_high_priority(mac_ctx, &msg); + if (status_code != QDF_STATUS_SUCCESS) + pe_err("posting message: %X to LIM failed, reason: %d", + msg.type, status_code); + return status_code; +} + +/** + * lim_append_ies_to_frame() - Append IEs to the frame + * + * @frame: Pointer to the frame buffer that needs to be populated + * @frame_len: Pointer for current frame length + * @ie: pointer for disconnect IEs + * + * This function is called by lim_send_disassoc_mgmt_frame and + * lim_send_deauth_mgmt_frame APIs as part of disconnection. + * Append IEs and update frame length. + * + * Return: None + */ +static void +lim_append_ies_to_frame(uint8_t *frame, uint32_t *frame_len, + struct wlan_ies *ie) +{ + if (!ie || !ie->len || !ie->data) + return; + qdf_mem_copy(frame, ie->data, ie->len); + *frame_len += ie->len; + pe_debug("Appended IEs len: %u", ie->len); +} + +/** + * \brief This function is called to send Disassociate frame. + * + * + * \param mac Pointer to Global MAC structure + * + * \param nReason Indicates the reason that need to be sent in + * Disassociation frame + * + * \param peerMacAddr MAC address of the STA to which Disassociation frame is + * sent + * + * + */ + +void +lim_send_disassoc_mgmt_frame(struct mac_context *mac, + uint16_t nReason, + tSirMacAddr peer, + struct pe_session *pe_session, bool waitForAck) +{ + tDot11fDisassociation frm; + uint8_t *pFrame; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + uint32_t val = 0; + uint8_t smeSessionId = 0; + struct wlan_ies *discon_ie; + + if (!pe_session) { + return; + } + + /* + * In case when cac timer is running for this SAP session then + * avoid sending disassoc out. It is violation of dfs specification. + */ + if (((pe_session->opmode == QDF_SAP_MODE) || + (pe_session->opmode == QDF_P2P_GO_MODE)) && + (true == mac->sap.SapDfsInfo.is_dfs_cac_timer_running)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO, + FL("CAC timer is running, drop disassoc from going out")); + if (waitForAck) + lim_send_disassoc_cnf(mac); + return; + } + smeSessionId = pe_session->smeSessionId; + + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + + frm.Reason.code = nReason; + + nStatus = dot11f_get_packed_disassociation_size(mac, &frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a Disassociation (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fDisassociation); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a Disassociation (0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + + discon_ie = mlme_get_self_disconnect_ies(pe_session->vdev); + if (discon_ie && discon_ie->len) + nBytes += discon_ie->len; + + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a Disassociation", + nBytes); + if (waitForAck) + lim_send_disassoc_cnf(mac); + return; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_DISASSOC, peer, pe_session->self_mac_addr); + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + /* Prepare the BSSID */ + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + nStatus = dot11f_pack_disassociation(mac, &frm, pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack a Disassociation (0x%08x)", + nStatus); + cds_packet_free((void *)pPacket); + if (waitForAck) + lim_send_disassoc_cnf(mac); + return; + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing a Disassociation (0x%08x)", + nStatus); + } + + /* Copy disconnect IEs to the end of the frame */ + lim_append_ies_to_frame(pFrame + sizeof(tSirMacMgmtHdr) + nPayload, + &nPayload, discon_ie); + mlme_free_self_disconnect_ies(pe_session->vdev); + + pe_nofl_info("Disassoc TX: vdev %d seq %d reason %u and waitForAck %d to " QDF_MAC_ADDR_FMT " From " QDF_MAC_ADDR_FMT, + pe_session->vdev_id, mac->mgmtSeqNum, nReason, waitForAck, + QDF_MAC_ADDR_REF(pMacHdr->da), + QDF_MAC_ADDR_REF(pe_session->self_mac_addr)); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + txFlag |= HAL_USE_PEER_STA_REQUESTED_MASK; + + if (waitForAck) { + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, + pMacHdr->fc.subType)); + lim_diag_mgmt_tx_event_report(mac, pMacHdr, + pe_session, QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + /* Queue Disassociation frame in high priority WQ */ + /* get the duration from the request */ + qdf_status = + wma_tx_frameWithTxComplete(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, 7, lim_tx_complete, + pFrame, lim_disassoc_tx_complete_cnf_handler, + txFlag, smeSessionId, false, 0, + RATEID_DEFAULT); + MTRACE(qdf_trace + (QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + + if (QDF_IS_STATUS_ERROR(qdf_status)) { + pe_err("Failed to send disassoc frame"); + lim_send_disassoc_cnf(mac); + return; + } + + val = SYS_MS_TO_TICKS(LIM_DISASSOC_DEAUTH_ACK_TIMEOUT); + + if (tx_timer_change + (&mac->lim.lim_timers.gLimDisassocAckTimer, val, 0) + != TX_SUCCESS) { + pe_err("Unable to change Disassoc ack Timer val"); + return; + } else if (TX_SUCCESS != + tx_timer_activate(&mac->lim.lim_timers. + gLimDisassocAckTimer)) { + pe_err("Unable to activate Disassoc ack Timer"); + lim_deactivate_and_change_timer(mac, + eLIM_DISASSOC_ACK_TIMER); + return; + } + } else { + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, + pMacHdr->fc.subType)); + lim_diag_mgmt_tx_event_report(mac, pMacHdr, + pe_session, + QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); + /* Queue Disassociation frame in high priority WQ */ + qdf_status = wma_tx_frame(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, + lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); + MTRACE(qdf_trace + (QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send Disassociation (%X)!", + qdf_status); + /* Pkt will be freed up by the callback */ + } + } +} /* End lim_send_disassoc_mgmt_frame. */ + +/** + * \brief This function is called to send a Deauthenticate frame + * + * + * \param mac Pointer to global MAC structure + * + * \param nReason Indicates the reason that need to be sent in the + * Deauthenticate frame + * + * \param peeer address of the STA to which the frame is to be sent + * + * + */ + +void +lim_send_deauth_mgmt_frame(struct mac_context *mac, + uint16_t nReason, + tSirMacAddr peer, + struct pe_session *pe_session, bool waitForAck) +{ + tDot11fDeAuth frm; + uint8_t *pFrame; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + uint32_t val = 0; +#ifdef FEATURE_WLAN_TDLS + uint16_t aid; + tpDphHashNode sta; +#endif + uint8_t smeSessionId = 0; + struct wlan_ies *discon_ie; + + if (!pe_session) { + return; + } + + /* + * In case when cac timer is running for this SAP session then + * avoid deauth frame out. It is violation of dfs specification. + */ + if (((pe_session->opmode == QDF_SAP_MODE) || + (pe_session->opmode == QDF_P2P_GO_MODE)) && + (true == mac->sap.SapDfsInfo.is_dfs_cac_timer_running)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO, + FL + ("CAC timer is running, drop the deauth from going out")); + if (waitForAck) + lim_send_deauth_cnf(mac); + return; + } + smeSessionId = pe_session->smeSessionId; + + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + + frm.Reason.code = nReason; + + nStatus = dot11f_get_packed_de_auth_size(mac, &frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a De-Authentication (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fDeAuth); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a De-Authentication (0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + discon_ie = mlme_get_self_disconnect_ies(pe_session->vdev); + if (discon_ie && discon_ie->len) + nBytes += discon_ie->len; + + qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a De-Authentication", + nBytes); + if (waitForAck) + lim_send_deauth_cnf(mac); + return; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_DEAUTH, peer, pe_session->self_mac_addr); + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + /* Prepare the BSSID */ + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + nStatus = dot11f_pack_de_auth(mac, &frm, pFrame + + sizeof(tSirMacMgmtHdr), nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack a DeAuthentication (0x%08x)", + nStatus); + cds_packet_free((void *)pPacket); + if (waitForAck) + lim_send_deauth_cnf(mac); + return; + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing a De-Authentication (0x%08x)", + nStatus); + } + + /* Copy disconnect IEs to the end of the frame */ + lim_append_ies_to_frame(pFrame + sizeof(tSirMacMgmtHdr) + nPayload, + &nPayload, discon_ie); + mlme_free_self_disconnect_ies(pe_session->vdev); + + pe_nofl_rl_info("Deauth TX: vdev %d seq_num %d reason %u waitForAck %d to " QDF_MAC_ADDR_FMT " from " QDF_MAC_ADDR_FMT, + pe_session->vdev_id, mac->mgmtSeqNum, nReason, + waitForAck, QDF_MAC_ADDR_REF(pMacHdr->da), + QDF_MAC_ADDR_REF(pe_session->self_mac_addr)); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + txFlag |= HAL_USE_PEER_STA_REQUESTED_MASK; +#ifdef FEATURE_WLAN_TDLS + sta = + dph_lookup_hash_entry(mac, peer, &aid, + &pe_session->dph.dphHashTable); +#endif + + if (waitForAck) { + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, + pMacHdr->fc.subType)); + lim_diag_mgmt_tx_event_report(mac, pMacHdr, + pe_session, + QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); + if (pe_session->opmode == QDF_STA_MODE && + mac->mlme_cfg->sta.deauth_retry_cnt && + !pe_session->deauth_retry.retry_cnt) { + pe_session->deauth_retry.retry_cnt = + mac->mlme_cfg->sta.deauth_retry_cnt; + pe_session->deauth_retry.reason_code = nReason; + qdf_mem_copy(pe_session->deauth_retry.peer_macaddr.bytes, + peer, QDF_MAC_ADDR_SIZE); + } + /* Queue Disassociation frame in high priority WQ */ + qdf_status = + wma_tx_frameWithTxComplete(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, 7, lim_tx_complete, + pFrame, lim_deauth_tx_complete_cnf_handler, + txFlag, smeSessionId, false, 0, + RATEID_DEFAULT); + MTRACE(qdf_trace + (QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + /* Pkt will be freed up by the callback lim_tx_complete */ + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send De-Authentication (%X)!", + qdf_status); + + /* Call lim_process_deauth_ack_timeout which will send + * DeauthCnf for this frame + */ + lim_process_deauth_ack_timeout(mac); + return; + } + + val = SYS_MS_TO_TICKS(LIM_DISASSOC_DEAUTH_ACK_TIMEOUT); + + if (tx_timer_change + (&mac->lim.lim_timers.gLimDeauthAckTimer, val, 0) + != TX_SUCCESS) { + pe_err("Unable to change Deauth ack Timer val"); + return; + } else if (TX_SUCCESS != + tx_timer_activate(&mac->lim.lim_timers. + gLimDeauthAckTimer)) { + pe_err("Unable to activate Deauth ack Timer"); + lim_deactivate_and_change_timer(mac, + eLIM_DEAUTH_ACK_TIMER); + return; + } + } else { + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, + pMacHdr->fc.subType)); +#ifdef FEATURE_WLAN_TDLS + if ((sta) + && (STA_ENTRY_TDLS_PEER == sta->staType)) { + /* Queue Disassociation frame in high priority WQ */ + lim_diag_mgmt_tx_event_report(mac, pMacHdr, + pe_session, + QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + qdf_status = + wma_tx_frame(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, ANI_TXDIR_IBSS, + 7, lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); + } else { +#endif + lim_diag_mgmt_tx_event_report(mac, pMacHdr, + pe_session, + QDF_STATUS_SUCCESS, + QDF_STATUS_SUCCESS); + /* Queue Disassociation frame in high priority WQ */ + qdf_status = + wma_tx_frame(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, + 7, lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); +#ifdef FEATURE_WLAN_TDLS + } +#endif + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send De-Authentication (%X)!", + qdf_status); + /* Pkt will be freed up by the callback */ + } + } + +} /* End lim_send_deauth_mgmt_frame. */ + +#ifdef ANI_SUPPORT_11H +/** + * \brief Send a Measurement Report Action frame + * + * + * \param mac Pointer to the global MAC structure + * + * \param pMeasReqFrame Address of a tSirMacMeasReqActionFrame + * + * \return QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE else + * + * + */ + +QDF_STATUS +lim_send_meas_report_frame(struct mac_context *mac, + tpSirMacMeasReqActionFrame pMeasReqFrame, + tSirMacAddr peer, struct pe_session *pe_session) +{ + tDot11fMeasurementReport frm; + uint8_t *pFrame; + QDF_STATUS nSirStatus; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + + frm.Category.category = ACTION_CATEGORY_SPECTRUM_MGMT; + frm.Action.action = ACTION_SPCT_MSR_RPRT; + frm.DialogToken.token = pMeasReqFrame->actionHeader.dialogToken; + + switch (pMeasReqFrame->measReqIE.measType) { + case SIR_MAC_BASIC_MEASUREMENT_TYPE: + nSirStatus = + populate_dot11f_measurement_report0(mac, pMeasReqFrame, + &frm.MeasurementReport); + break; + case SIR_MAC_CCA_MEASUREMENT_TYPE: + nSirStatus = + populate_dot11f_measurement_report1(mac, pMeasReqFrame, + &frm.MeasurementReport); + break; + case SIR_MAC_RPI_MEASUREMENT_TYPE: + nSirStatus = + populate_dot11f_measurement_report2(mac, pMeasReqFrame, + &frm.MeasurementReport); + break; + default: + pe_err("Unknown measurement type %d in limSendMeasReportFrame", + pMeasReqFrame->measReqIE.measType); + return QDF_STATUS_E_FAILURE; + } + + if (QDF_STATUS_SUCCESS != nSirStatus) + return QDF_STATUS_E_FAILURE; + + nStatus = dot11f_get_packed_measurement_report_size(mac, &frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a Measurement Report (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fMeasurementReport); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a Measurement Report (0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + + qdf_status = + cds_packet_alloc(mac->hdd_handle, TXRX_FRM_802_11_MGMT, + (uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a De-Authentication", + nBytes); + return QDF_STATUS_E_FAILURE; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer); + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + qdf_mem_copy(pMacHdr->bssId, pe_session->bssId, sizeof(tSirMacAddr)); + + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + nStatus = dot11f_pack_measurement_report(mac, &frm, pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack a Measurement Report (0x%08x)", + nStatus); + cds_packet_free(mac->hdd_handle, TXRX_FRM_802_11_MGMT, + (void *)pFrame, (void *)pPacket); + return QDF_STATUS_E_FAILURE; /* allocated! */ + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing a Measurement Report (0x%08x)", + nStatus); + } + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + ((pe_session) ? pe_session-> + peSessionId : NO_SESSION), pMacHdr->fc.subType)); + qdf_status = + wma_tx_frame(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, 7, + lim_tx_complete, pFrame, 0, 0, RATEID_DEFAULT); + MTRACE(qdf_trace + (QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + ((pe_session) ? pe_session->peSessionId : NO_SESSION), + qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send a Measurement Report (%X)!", + qdf_status); + /* Pkt will be freed up by the callback */ + return QDF_STATUS_E_FAILURE; /* just allocated... */ + } + + return QDF_STATUS_SUCCESS; + +} /* End lim_send_meas_report_frame. */ + +/** + * \brief Send a TPC Report Action frame + * + * + * \param mac Pointer to the global MAC datastructure + * + * \param pTpcReqFrame Pointer to the received TPC Request + * + * \return QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE else + * + * + */ + +QDF_STATUS +lim_send_tpc_report_frame(struct mac_context *mac, + tpSirMacTpcReqActionFrame pTpcReqFrame, + tSirMacAddr peer, struct pe_session *pe_session) +{ + tDot11fTPCReport frm; + uint8_t *pFrame; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + struct vdev_mlme_obj *mlme_obj; + + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + + frm.Category.category = ACTION_CATEGORY_SPECTRUM_MGMT; + frm.Action.action = ACTION_SPCT_TPC_RPRT; + frm.DialogToken.token = pTpcReqFrame->actionHeader.dialogToken; + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(pe_session->vdev); + if (mlme_obj) + frm.TPCReport.tx_power = mlme_obj->mgmt.generic.tx_pwrlimit; + + frm.TPCReport.link_margin = 0; + frm.TPCReport.present = 1; + + nStatus = dot11f_get_packed_tpc_report_size(mac, &frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a TPC Report (0x%08x)", nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fTPCReport); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a TPC Report (0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + + qdf_status = + cds_packet_alloc(mac->hdd_handle, TXRX_FRM_802_11_MGMT, + (uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a TPC" + " Report", nBytes); + return QDF_STATUS_E_FAILURE; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer); + + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + qdf_mem_copy(pMacHdr->bssId, pe_session->bssId, sizeof(tSirMacAddr)); + + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + nStatus = dot11f_pack_tpc_report(mac, &frm, pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack a TPC Report (0x%08x)", + nStatus); + cds_packet_free(mac->hdd_handle, TXRX_FRM_802_11_MGMT, + (void *)pFrame, (void *)pPacket); + return QDF_STATUS_E_FAILURE; /* allocated! */ + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing a TPC Report (0x%08x)", + nStatus); + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + ((pe_session) ? pe_session-> + peSessionId : NO_SESSION), pMacHdr->fc.subType)); + qdf_status = + wma_tx_frame(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, 7, + lim_tx_complete, pFrame, 0, 0, RATEID_DEFAULT); + MTRACE(qdf_trace + (QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + ((pe_session) ? pe_session->peSessionId : NO_SESSION), + qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send a TPC Report (%X)!", + qdf_status); + /* Pkt will be freed up by the callback */ + return QDF_STATUS_E_FAILURE; /* just allocated... */ + } + + return QDF_STATUS_SUCCESS; + +} /* End lim_send_tpc_report_frame. */ +#endif /* ANI_SUPPORT_11H */ + +/** + * \brief Send a Channel Switch Announcement + * + * + * \param mac Pointer to the global MAC datastructure + * + * \param peer MAC address to which this frame will be sent + * + * \param nMode + * + * \param nNewChannel + * + * \param nCount + * + * \return QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE else + * + * + */ + +QDF_STATUS +lim_send_channel_switch_mgmt_frame(struct mac_context *mac, + tSirMacAddr peer, + uint8_t nMode, + uint8_t nNewChannel, + uint8_t nCount, struct pe_session *pe_session) +{ + tDot11fChannelSwitch frm; + uint8_t *pFrame; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; /* , nCfg; */ + void *pPacket; + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + + uint8_t smeSessionId = 0; + + if (!pe_session) { + pe_err("Session entry is NULL!!!"); + return QDF_STATUS_E_FAILURE; + } + smeSessionId = pe_session->smeSessionId; + + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + + frm.Category.category = ACTION_CATEGORY_SPECTRUM_MGMT; + frm.Action.action = ACTION_SPCT_CHL_SWITCH; + frm.ChanSwitchAnn.switchMode = nMode; + frm.ChanSwitchAnn.newChannel = nNewChannel; + frm.ChanSwitchAnn.switchCount = nCount; + frm.ChanSwitchAnn.present = 1; + + nStatus = dot11f_get_packed_channel_switch_size(mac, &frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a Channel Switch (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fChannelSwitch); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a Channel Switch (0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + + qdf_status = + cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a TPC Report", nBytes); + return QDF_STATUS_E_FAILURE; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer, + pe_session->self_mac_addr); + pMacHdr = (tpSirMacMgmtHdr) pFrame; + qdf_mem_copy((uint8_t *) pMacHdr->bssId, + (uint8_t *) pe_session->bssId, sizeof(tSirMacAddr)); + + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + nStatus = dot11f_pack_channel_switch(mac, &frm, pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack a Channel Switch (0x%08x)", + nStatus); + cds_packet_free((void *)pPacket); + return QDF_STATUS_E_FAILURE; /* allocated! */ + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing a Channel Switch (0x%08x)", + nStatus); + } + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, pMacHdr->fc.subType)); + qdf_status = wma_tx_frame(mac, pPacket, (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send a Channel Switch (%X)!", + qdf_status); + /* Pkt will be freed up by the callback */ + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; + +} /* End lim_send_channel_switch_mgmt_frame. */ + +/** + * lim_send_extended_chan_switch_action_frame()- function to send ECSA + * action frame over the air . + * @mac_ctx: pointer to global mac structure + * @peer: Destination mac. + * @mode: channel switch mode + * @new_op_class: new op class + * @new_channel: new channel to switch + * @count: channel switch count + * + * This function is called to send ECSA frame. + * + * Return: success if frame is sent else return failure + */ + +QDF_STATUS +lim_send_extended_chan_switch_action_frame(struct mac_context *mac_ctx, + tSirMacAddr peer, uint8_t mode, uint8_t new_op_class, + uint8_t new_channel, uint8_t count, struct pe_session *session_entry) +{ + tDot11fext_channel_switch_action_frame frm; + uint8_t *frame; + tpSirMacMgmtHdr mac_hdr; + uint32_t num_bytes, n_payload, status; + void *packet; + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + uint8_t vdev_id = 0; + uint8_t ch_spacing; + tLimWiderBWChannelSwitchInfo *wide_bw_ie; + + if (!session_entry) { + pe_err("Session entry is NULL!!!"); + return QDF_STATUS_E_FAILURE; + } + + vdev_id = session_entry->smeSessionId; + + qdf_mem_zero(&frm, sizeof(frm)); + + frm.Category.category = ACTION_CATEGORY_PUBLIC; + frm.Action.action = SIR_MAC_ACTION_EXT_CHANNEL_SWITCH_ID; + + frm.ext_chan_switch_ann_action.switch_mode = mode; + frm.ext_chan_switch_ann_action.op_class = new_op_class; + frm.ext_chan_switch_ann_action.new_channel = new_channel; + frm.ext_chan_switch_ann_action.switch_count = count; + + ch_spacing = wlan_reg_dmn_get_chanwidth_from_opclass( + mac_ctx->scan.countryCodeCurrent, new_channel, + new_op_class); + + if ((ch_spacing == 80) || (ch_spacing == 160)) { + wide_bw_ie = &session_entry->gLimWiderBWChannelSwitch; + frm.WiderBWChanSwitchAnn.newChanWidth = + wide_bw_ie->newChanWidth; + frm.WiderBWChanSwitchAnn.newCenterChanFreq0 = + wide_bw_ie->newCenterChanFreq0; + frm.WiderBWChanSwitchAnn.newCenterChanFreq1 = + wide_bw_ie->newCenterChanFreq1; + frm.WiderBWChanSwitchAnn.present = 1; + pe_debug("wrapper: width:%d f0:%d f1:%d", + frm.WiderBWChanSwitchAnn.newChanWidth, + frm.WiderBWChanSwitchAnn.newCenterChanFreq0, + frm.WiderBWChanSwitchAnn.newCenterChanFreq1); + } + + status = dot11f_get_packed_ext_channel_switch_action_frame_size(mac_ctx, + &frm, &n_payload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to get packed size for Channel Switch 0x%08x", + status); + /* We'll fall back on the worst case scenario*/ + n_payload = sizeof(tDot11fext_channel_switch_action_frame); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size for a Ext Channel Switch (0x%08x)", + status); + } + + num_bytes = n_payload + sizeof(tSirMacMgmtHdr); + + qdf_status = cds_packet_alloc((uint16_t)num_bytes, + (void **) &frame, (void **) &packet); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a Ext Channel Switch", + num_bytes); + return QDF_STATUS_E_FAILURE; + } + + /* Paranoia*/ + qdf_mem_zero(frame, num_bytes); + + /* Next, we fill out the buffer descriptor */ + lim_populate_mac_header(mac_ctx, frame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer, session_entry->self_mac_addr); + mac_hdr = (tpSirMacMgmtHdr) frame; + qdf_mem_copy((uint8_t *) mac_hdr->bssId, + (uint8_t *) session_entry->bssId, + sizeof(tSirMacAddr)); + + lim_set_protected_bit(mac_ctx, session_entry, peer, mac_hdr); + + status = dot11f_pack_ext_channel_switch_action_frame(mac_ctx, &frm, + frame + sizeof(tSirMacMgmtHdr), n_payload, &n_payload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a Channel Switch 0x%08x", status); + cds_packet_free((void *)packet); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing a Channel Switch 0x%08x", + status); + } + + if (!wlan_reg_is_24ghz_ch_freq(session_entry->curr_op_freq) || + session_entry->opmode == QDF_P2P_CLIENT_MODE || + session_entry->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + pe_debug("ECSA frame to :"QDF_MAC_ADDR_FMT" count %d mode %d chan %d op class %d", + QDF_MAC_ADDR_REF(mac_hdr->da), + frm.ext_chan_switch_ann_action.switch_count, + frm.ext_chan_switch_ann_action.switch_mode, + frm.ext_chan_switch_ann_action.new_channel, + frm.ext_chan_switch_ann_action.op_class); + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + session_entry->peSessionId, mac_hdr->fc.subType)); + qdf_status = wma_tx_frame(mac_ctx, packet, (uint16_t) num_bytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, + lim_tx_complete, frame, + txFlag, vdev_id, 0, + RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + session_entry->peSessionId, qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send a Ext Channel Switch %X!", + qdf_status); + /* Pkt will be freed up by the callback */ + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} /* End lim_send_extended_chan_switch_action_frame */ + + +/** + * lim_oper_chan_change_confirm_tx_complete_cnf()- Confirmation for oper_chan_change_confirm + * sent over the air + * + * @context: pointer to global mac + * @buf: buffer + * @tx_complete : Sent status + * @params: tx completion params + * + * Return: This returns QDF_STATUS + */ + +static QDF_STATUS lim_oper_chan_change_confirm_tx_complete_cnf( + void *context, + qdf_nbuf_t buf, + uint32_t tx_complete, + void *params) +{ + pe_debug("tx_complete: %d", tx_complete); + if (buf) + qdf_nbuf_free(buf); + return QDF_STATUS_SUCCESS; +} + +/** + * lim_p2p_oper_chan_change_confirm_action_frame()- function to send + * p2p oper chan change confirm action frame + * @mac_ctx: pointer to global mac structure + * @peer: Destination mac. + * @session_entry: session entry + * + * This function is called to send p2p oper chan change confirm action frame. + * + * Return: success if frame is sent else return failure + */ + +QDF_STATUS +lim_p2p_oper_chan_change_confirm_action_frame(struct mac_context *mac_ctx, + tSirMacAddr peer, struct pe_session *session_entry) +{ + tDot11fp2p_oper_chan_change_confirm frm; + uint8_t *frame; + tpSirMacMgmtHdr mac_hdr; + uint32_t num_bytes, n_payload, status; + void *packet; + QDF_STATUS qdf_status; + uint8_t tx_flag = 0; + uint8_t vdev_id = 0; + + if (!session_entry) { + pe_err("Session entry is NULL!!!"); + return QDF_STATUS_E_FAILURE; + } + + vdev_id = session_entry->smeSessionId; + + qdf_mem_zero(&frm, sizeof(frm)); + + frm.Category.category = SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY; + + qdf_mem_copy(frm.p2p_action_oui.oui_data, + SIR_MAC_P2P_OUI, SIR_MAC_P2P_OUI_SIZE); + frm.p2p_action_subtype.subtype = 0x04; + frm.DialogToken.token = 0x0; + + if (session_entry->htCapability) { + pe_debug("Populate HT Caps in Assoc Request"); + populate_dot11f_ht_caps(mac_ctx, session_entry, &frm.HTCaps); + } + + if (session_entry->vhtCapability) { + pe_debug("Populate VHT Caps in Assoc Request"); + populate_dot11f_vht_caps(mac_ctx, session_entry, &frm.VHTCaps); + populate_dot11f_operating_mode(mac_ctx, + &frm.OperatingMode, session_entry); + } + + status = dot11f_get_packed_p2p_oper_chan_change_confirmSize(mac_ctx, + &frm, &n_payload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to get packed size 0x%08x", status); + /* We'll fall back on the worst case scenario*/ + n_payload = sizeof(tDot11fp2p_oper_chan_change_confirm); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size (0x%08x)", + status); + } + + num_bytes = n_payload + sizeof(tSirMacMgmtHdr); + + qdf_status = cds_packet_alloc((uint16_t)num_bytes, + (void **) &frame, (void **) &packet); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes", num_bytes); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_zero(frame, num_bytes); + + /* Next, fill out the buffer descriptor */ + lim_populate_mac_header(mac_ctx, frame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer, session_entry->self_mac_addr); + mac_hdr = (tpSirMacMgmtHdr) frame; + qdf_mem_copy((uint8_t *) mac_hdr->bssId, + (uint8_t *) session_entry->bssId, + sizeof(tSirMacAddr)); + + status = dot11f_pack_p2p_oper_chan_change_confirm(mac_ctx, &frm, + frame + sizeof(tSirMacMgmtHdr), n_payload, &n_payload); + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack 0x%08x", status); + cds_packet_free((void *)packet); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing 0x%08x", + status); + } + + if (!wlan_reg_is_24ghz_ch_freq(session_entry->curr_op_freq) || + session_entry->opmode == QDF_P2P_CLIENT_MODE || + session_entry->opmode == QDF_P2P_GO_MODE) { + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + } + pe_debug("Send frame on channel freq %d to mac " + QDF_MAC_ADDR_FMT, session_entry->curr_op_freq, + QDF_MAC_ADDR_REF(peer)); + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + session_entry->peSessionId, mac_hdr->fc.subType)); + + qdf_status = wma_tx_frameWithTxComplete(mac_ctx, packet, + (uint16_t)num_bytes, + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, + 7, lim_tx_complete, frame, + lim_oper_chan_change_confirm_tx_complete_cnf, + tx_flag, vdev_id, false, 0, RATEID_DEFAULT); + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + session_entry->peSessionId, qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to send status %X!", qdf_status); + /* Pkt will be freed up by the callback */ + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + + +/** + * \brief Send a Neighbor Report Request Action frame + * + * + * \param mac Pointer to the global MAC structure + * + * \param pNeighborReq Address of a tSirMacNeighborReportReq + * + * \param peer mac address of peer station. + * + * \param pe_session address of session entry. + * + * \return QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE else + * + * + */ + +QDF_STATUS +lim_send_neighbor_report_request_frame(struct mac_context *mac, + tpSirMacNeighborReportReq pNeighborReq, + tSirMacAddr peer, struct pe_session *pe_session) +{ + QDF_STATUS status_code = QDF_STATUS_SUCCESS; + tDot11fNeighborReportRequest frm; + uint8_t *pFrame; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + uint8_t smeSessionId = 0; + + if (!pe_session) { + pe_err("(!psession) in Request to send Neighbor Report request action frame"); + return QDF_STATUS_E_FAILURE; + } + smeSessionId = pe_session->smeSessionId; + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + + frm.Category.category = ACTION_CATEGORY_RRM; + frm.Action.action = RRM_NEIGHBOR_REQ; + frm.DialogToken.token = pNeighborReq->dialogToken; + + if (pNeighborReq->ssid_present) { + populate_dot11f_ssid(mac, &pNeighborReq->ssid, &frm.SSID); + } + + nStatus = + dot11f_get_packed_neighbor_report_request_size(mac, &frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a Neighbor Report Request(0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fNeighborReportRequest); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a Neighbor Report Request(0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + + qdf_status = + cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a Neighbor " + "Report Request", nBytes); + return QDF_STATUS_E_FAILURE; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Copy necessary info to BD */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer, pe_session->self_mac_addr); + + /* Update A3 with the BSSID */ + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + /* Now, we're ready to "pack" the frames */ + nStatus = dot11f_pack_neighbor_report_request(mac, + &frm, + pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack an Neighbor Report Request (0x%08x)", + nStatus); + + /* FIXME - Need to convert to QDF_STATUS */ + status_code = QDF_STATUS_E_FAILURE; + goto returnAfterError; + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing Neighbor Report Request (0x%08x)", + nStatus); + } + + pe_debug("Sending a Neighbor Report Request to"); + lim_print_mac_addr(mac, peer, LOGD); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, pMacHdr->fc.subType)); + qdf_status = wma_tx_frame(mac, + pPacket, + (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + if (QDF_STATUS_SUCCESS != qdf_status) { + pe_err("wma_tx_frame FAILED! Status [%d]", qdf_status); + status_code = QDF_STATUS_E_FAILURE; + /* Pkt will be freed up by the callback */ + return status_code; + } else + return QDF_STATUS_SUCCESS; + +returnAfterError: + cds_packet_free((void *)pPacket); + + return status_code; +} /* End lim_send_neighbor_report_request_frame. */ + +QDF_STATUS +lim_send_link_report_action_frame(struct mac_context *mac, + tpSirMacLinkReport pLinkReport, + tSirMacAddr peer, struct pe_session *pe_session) +{ + QDF_STATUS status_code = QDF_STATUS_SUCCESS; + tDot11fLinkMeasurementReport frm; + uint8_t *pFrame; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + uint8_t vdev_id = 0; + + if (!pe_session) { + pe_err("RRM: Send link report: NULL PE session"); + return QDF_STATUS_E_FAILURE; + } + + vdev_id = pe_session->vdev_id; + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + + frm.Category.category = ACTION_CATEGORY_RRM; + frm.Action.action = RRM_LINK_MEASUREMENT_RPT; + frm.DialogToken.token = pLinkReport->dialogToken; + + /* IEEE Std. 802.11 7.3.2.18. for the report element. */ + /* Even though TPC report an IE, it is represented using fixed fields since it is positioned */ + /* in the middle of other fixed fields in the link report frame(IEEE Std. 802.11k section7.4.6.4 */ + /* and frame parser always expects IEs to come after all fixed fields. It is easier to handle */ + /* such case this way than changing the frame parser. */ + frm.TPCEleID.TPCId = WLAN_ELEMID_TPCREP; + frm.TPCEleLen.TPCLen = 2; + frm.TxPower.txPower = pLinkReport->txPower; + frm.LinkMargin.linkMargin = 0; + + frm.RxAntennaId.antennaId = pLinkReport->rxAntenna; + frm.TxAntennaId.antennaId = pLinkReport->txAntenna; + frm.RCPI.rcpi = pLinkReport->rcpi; + frm.RSNI.rsni = pLinkReport->rsni; + + nStatus = + dot11f_get_packed_link_measurement_report_size(mac, &frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a Link Report (0x%08x)", nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fLinkMeasurementReport); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for a Link Report (0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + + qdf_status = + cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a Link " + "Report", nBytes); + return QDF_STATUS_E_FAILURE; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Copy necessary info to BD */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer, pe_session->self_mac_addr); + + /* Update A3 with the BSSID */ + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + /* Now, we're ready to "pack" the frames */ + nStatus = dot11f_pack_link_measurement_report(mac, + &frm, + pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack an Link Report (0x%08x)", nStatus); + + /* FIXME - Need to convert to QDF_STATUS */ + status_code = QDF_STATUS_E_FAILURE; + goto returnAfterError; + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing Link Report (0x%08x)", + nStatus); + } + + pe_warn("RRM: Sending Link Report to "QDF_MAC_ADDR_FMT" on vdev[%d]", + QDF_MAC_ADDR_REF(peer), vdev_id); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, pMacHdr->fc.subType)); + qdf_status = wma_tx_frame(mac, + pPacket, + (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, pFrame, txFlag, + vdev_id, 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + if (QDF_STATUS_SUCCESS != qdf_status) { + pe_err("wma_tx_frame FAILED! Status [%d]", qdf_status); + status_code = QDF_STATUS_E_FAILURE; + /* Pkt will be freed up by the callback */ + return status_code; + } else + return QDF_STATUS_SUCCESS; + +returnAfterError: + cds_packet_free((void *)pPacket); + + return status_code; +} /* End lim_send_link_report_action_frame. */ + +QDF_STATUS +lim_send_radio_measure_report_action_frame(struct mac_context *mac, + uint8_t dialog_token, + uint8_t num_report, + bool is_last_frame, + tpSirMacRadioMeasureReport pRRMReport, + tSirMacAddr peer, + struct pe_session *pe_session) +{ + QDF_STATUS status_code = QDF_STATUS_SUCCESS; + uint8_t *pFrame; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + uint8_t i; + uint8_t txFlag = 0; + uint8_t smeSessionId = 0; + bool is_last_report = false; + + tDot11fRadioMeasurementReport *frm = + qdf_mem_malloc(sizeof(tDot11fRadioMeasurementReport)); + if (!frm) + return QDF_STATUS_E_NOMEM; + + if (!pe_session) { + pe_err("session not found"); + qdf_mem_free(frm); + return QDF_STATUS_E_FAILURE; + } + + smeSessionId = pe_session->smeSessionId; + + + frm->Category.category = ACTION_CATEGORY_RRM; + frm->Action.action = RRM_RADIO_MEASURE_RPT; + frm->DialogToken.token = dialog_token; + + frm->num_MeasurementReport = + (num_report > + RADIO_REPORTS_MAX_IN_A_FRAME) ? RADIO_REPORTS_MAX_IN_A_FRAME : + num_report; + + for (i = 0; i < frm->num_MeasurementReport; i++) { + frm->MeasurementReport[i].type = pRRMReport[i].type; + frm->MeasurementReport[i].token = pRRMReport[i].token; + frm->MeasurementReport[i].late = 0; /* IEEE 802.11k section 7.3.22. (always zero in rrm) */ + switch (pRRMReport[i].type) { + case SIR_MAC_RRM_BEACON_TYPE: + /* + * Last beacon report indication needs to be set to 1 + * only for the last report in the last frame + */ + if (is_last_frame && + (i == (frm->num_MeasurementReport - 1))) + is_last_report = true; + + populate_dot11f_beacon_report(mac, + &frm->MeasurementReport[i], + &pRRMReport[i].report. + beaconReport, + is_last_report); + frm->MeasurementReport[i].incapable = + pRRMReport[i].incapable; + frm->MeasurementReport[i].refused = + pRRMReport[i].refused; + frm->MeasurementReport[i].present = 1; + break; + default: + frm->MeasurementReport[i].incapable = + pRRMReport[i].incapable; + frm->MeasurementReport[i].refused = + pRRMReport[i].refused; + frm->MeasurementReport[i].present = 1; + break; + } + } + + + nStatus = + dot11f_get_packed_radio_measurement_report_size(mac, frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_nofl_err("TX: [802.11 RRM] Failed to get packed size for RM Report (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fLinkMeasurementReport); + qdf_mem_free(frm); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("Warnings while calculating the size for Radio Measure Report (0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + + qdf_status = + cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame, + (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_nofl_err("TX: [802.11 RRM] Allocation of %d bytes failed for RM" + "Report", nBytes); + qdf_mem_free(frm); + return QDF_STATUS_E_FAILURE; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Copy necessary info to BD */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer, pe_session->self_mac_addr); + + /* Update A3 with the BSSID */ + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + /* Now, we're ready to "pack" the frames */ + nStatus = dot11f_pack_radio_measurement_report(mac, + frm, + pFrame + + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + + if (DOT11F_FAILED(nStatus)) { + pe_nofl_err("Failed to pack an Radio Measure Report (0x%08x)", + nStatus); + + /* FIXME - Need to convert to QDF_STATUS */ + status_code = QDF_STATUS_E_FAILURE; + goto returnAfterError; + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("Warnings while packing Radio Measure Report (0x%08x)", + nStatus); + } + + pe_nofl_info("TX: %s seq_no:%d dialog_token:%d no. of APs:%d is_last_rpt:%d num_report: %d peer:"QDF_MAC_ADDR_FMT, + frm->MeasurementReport[0].type == SIR_MAC_RRM_BEACON_TYPE ? + "[802.11 BCN_RPT]" : "[802.11 RRM]", + (pMacHdr->seqControl.seqNumHi << HIGH_SEQ_NUM_OFFSET | + pMacHdr->seqControl.seqNumLo), + dialog_token, frm->num_MeasurementReport, + is_last_report, num_report, + QDF_MAC_ADDR_REF(peer)); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, pMacHdr->fc.subType)); + qdf_status = wma_tx_frame(mac, pPacket, (uint16_t)nBytes, + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, + 7, lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + if (QDF_STATUS_SUCCESS != qdf_status) { + pe_nofl_err("TX: [802.11 RRM] Send FAILED! err_status [%d]", + qdf_status); + status_code = QDF_STATUS_E_FAILURE; + /* Pkt will be freed up by the callback */ + } + + qdf_mem_free(frm); + return status_code; + +returnAfterError: + qdf_mem_free(frm); + cds_packet_free((void *)pPacket); + return status_code; +} + +#ifdef WLAN_FEATURE_11W +/** + * \brief Send SA query request action frame to peer + * + * \sa lim_send_sa_query_request_frame + * + * + * \param mac The global struct mac_context *object + * + * \param transId Transaction identifier + * + * \param peer The Mac address of the station to which this action frame is addressed + * + * \param pe_session The PE session entry + * + * \return QDF_STATUS_SUCCESS if setup completes successfully + * QDF_STATUS_E_FAILURE is some problem is encountered + */ + +QDF_STATUS lim_send_sa_query_request_frame(struct mac_context *mac, uint8_t *transId, + tSirMacAddr peer, + struct pe_session *pe_session) +{ + + tDot11fSaQueryReq frm; /* SA query request action frame */ + uint8_t *pFrame; + QDF_STATUS nSirStatus; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + uint8_t smeSessionId = 0; + + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + frm.Category.category = ACTION_CATEGORY_SA_QUERY; + /* 11w action field is : + action: 0 --> SA Query Request action frame + action: 1 --> SA Query Response action frame */ + frm.Action.action = SA_QUERY_REQUEST; + /* 11w SA Query Request transId */ + qdf_mem_copy(&frm.TransactionId.transId[0], &transId[0], 2); + + nStatus = dot11f_get_packed_sa_query_req_size(mac, &frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for an SA Query Request (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fSaQueryReq); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for an SA Query Request (0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + qdf_status = + cds_packet_alloc(nBytes, (void **)&pFrame, (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a SA Query Request " + "action frame", nBytes); + return QDF_STATUS_E_FAILURE; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Copy necessary info to BD */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer, pe_session->self_mac_addr); + + /* Update A3 with the BSSID */ + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + /* Since this is a SA Query Request, set the "protect" (aka WEP) bit */ + /* in the FC */ + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + /* Pack 11w SA Query Request frame */ + nStatus = dot11f_pack_sa_query_req(mac, + &frm, + pFrame + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack an SA Query Request (0x%08x)", + nStatus); + /* FIXME - Need to convert to QDF_STATUS */ + nSirStatus = QDF_STATUS_E_FAILURE; + goto returnAfterError; + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing SA Query Request (0x%08x)", + nStatus); + } + + pe_debug("Sending an SA Query Request to"); + lim_print_mac_addr(mac, peer, LOGD); + pe_debug("Sending an SA Query Request from "); + lim_print_mac_addr(mac, pe_session->self_mac_addr, LOGD); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + smeSessionId = pe_session->smeSessionId; + + qdf_status = wma_tx_frame(mac, + pPacket, + (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); + if (QDF_STATUS_SUCCESS != qdf_status) { + pe_err("wma_tx_frame FAILED! Status [%d]", qdf_status); + nSirStatus = QDF_STATUS_E_FAILURE; + /* Pkt will be freed up by the callback */ + return nSirStatus; + } else { + return QDF_STATUS_SUCCESS; + } + +returnAfterError: + cds_packet_free((void *)pPacket); + return nSirStatus; +} /* End lim_send_sa_query_request_frame */ + +/** + * \brief Send SA query response action frame to peer + * + * \sa lim_send_sa_query_response_frame + * + * + * \param mac The global struct mac_context *object + * + * \param transId Transaction identifier received in SA query request action frame + * + * \param peer The Mac address of the AP to which this action frame is addressed + * + * \param pe_session The PE session entry + * + * \return QDF_STATUS_SUCCESS if setup completes successfully + * QDF_STATUS_E_FAILURE is some problem is encountered + */ + +QDF_STATUS lim_send_sa_query_response_frame(struct mac_context *mac, + uint8_t *transId, tSirMacAddr peer, + struct pe_session *pe_session) +{ + + tDot11fSaQueryRsp frm; /* SA query response action frame */ + uint8_t *pFrame; + QDF_STATUS nSirStatus; + tpSirMacMgmtHdr pMacHdr; + uint32_t nBytes, nPayload, nStatus; + void *pPacket; + QDF_STATUS qdf_status; + uint8_t txFlag = 0; + uint8_t smeSessionId = 0; + + smeSessionId = pe_session->smeSessionId; + + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + frm.Category.category = ACTION_CATEGORY_SA_QUERY; + /*11w action field is : + action: 0 --> SA query request action frame + action: 1 --> SA query response action frame */ + frm.Action.action = SA_QUERY_RESPONSE; + /*11w SA query response transId is same as + SA query request transId */ + qdf_mem_copy(&frm.TransactionId.transId[0], &transId[0], 2); + + nStatus = dot11f_get_packed_sa_query_rsp_size(mac, &frm, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a SA Query Response (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fSaQueryRsp); + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while calculating the packed size for an SA Query Response (0x%08x)", + nStatus); + } + + nBytes = nPayload + sizeof(tSirMacMgmtHdr); + qdf_status = + cds_packet_alloc(nBytes, (void **)&pFrame, (void **)&pPacket); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("Failed to allocate %d bytes for a SA query response" + " action frame", nBytes); + return QDF_STATUS_E_FAILURE; + } + /* Paranoia: */ + qdf_mem_zero(pFrame, nBytes); + + /* Copy necessary info to BD */ + lim_populate_mac_header(mac, pFrame, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer, pe_session->self_mac_addr); + + /* Update A3 with the BSSID */ + pMacHdr = (tpSirMacMgmtHdr) pFrame; + + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + /* Since this is a SA Query Response, set the "protect" (aka WEP) bit */ + /* in the FC */ + lim_set_protected_bit(mac, pe_session, peer, pMacHdr); + + /* Pack 11w SA query response frame */ + nStatus = dot11f_pack_sa_query_rsp(mac, + &frm, + pFrame + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack an SA Query Response (0x%08x)", + nStatus); + /* FIXME - Need to convert to QDF_STATUS */ + nSirStatus = QDF_STATUS_E_FAILURE; + goto returnAfterError; + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing SA Query Response (0x%08x)", + nStatus); + } + + pe_debug("Sending a SA Query Response to"); + lim_print_mac_addr(mac, peer, LOGD); + + if (!wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) || + pe_session->opmode == QDF_P2P_CLIENT_MODE || + pe_session->opmode == QDF_P2P_GO_MODE) + txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + pe_session->peSessionId, pMacHdr->fc.subType)); + qdf_status = wma_tx_frame(mac, + pPacket, + (uint16_t) nBytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, + 7, lim_tx_complete, pFrame, txFlag, + smeSessionId, 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + pe_session->peSessionId, qdf_status)); + if (QDF_STATUS_SUCCESS != qdf_status) { + pe_err("wma_tx_frame FAILED! Status [%d]", qdf_status); + nSirStatus = QDF_STATUS_E_FAILURE; + /* Pkt will be freed up by the callback */ + return nSirStatus; + } else { + return QDF_STATUS_SUCCESS; + } + +returnAfterError: + cds_packet_free((void *)pPacket); + return nSirStatus; +} /* End lim_send_sa_query_response_frame */ +#endif + +#if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ + defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) +#ifdef WLAN_FEATURE_11AX +#define IS_PE_SESSION_HE_MODE(_session) ((_session)->he_capable) +#else +#define IS_PE_SESSION_HE_MODE(_session) false +#endif /* WLAN_FEATURE_11AX */ +#else +#define IS_PE_SESSION_HE_MODE(_session) false +#endif + +QDF_STATUS lim_send_addba_response_frame(struct mac_context *mac_ctx, + tSirMacAddr peer_mac, uint16_t tid, + struct pe_session *session, uint8_t addba_extn_present, + uint8_t amsdu_support, uint8_t is_wep) +{ + + tDot11faddba_rsp frm; + uint8_t *frame_ptr; + tpSirMacMgmtHdr mgmt_hdr; + uint32_t num_bytes, payload_size, status; + void *pkt_ptr = NULL; + QDF_STATUS qdf_status; + uint8_t tx_flag = 0; + uint8_t vdev_id = 0; + uint16_t buff_size, status_code, batimeout; + uint8_t dialog_token; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint8_t he_frag = 0; + tpDphHashNode sta_ds; + uint16_t aid; + bool he_cap = false; + + vdev_id = session->vdev_id; + + cdp_addba_responsesetup(soc, peer_mac, vdev_id, tid, + &dialog_token, &status_code, &buff_size, + &batimeout); + + qdf_mem_zero((uint8_t *) &frm, sizeof(frm)); + frm.Category.category = ACTION_CATEGORY_BACK; + frm.Action.action = ADDBA_RESPONSE; + + frm.DialogToken.token = dialog_token; + frm.Status.status = status_code; + if (mac_ctx->reject_addba_req) { + frm.Status.status = eSIR_MAC_REQ_DECLINED_STATUS; + pe_err("refused addba req"); + } + frm.addba_param_set.tid = tid; + + sta_ds = dph_lookup_hash_entry(mac_ctx, peer_mac, &aid, + &session->dph.dphHashTable); + if (sta_ds && lim_is_session_he_capable(session)) + he_cap = lim_is_sta_he_capable(sta_ds); + + if (he_cap) + frm.addba_param_set.buff_size = MAX_BA_BUFF_SIZE; + else + frm.addba_param_set.buff_size = SIR_MAC_BA_DEFAULT_BUFF_SIZE; + + if (mac_ctx->usr_cfg_ba_buff_size) + frm.addba_param_set.buff_size = mac_ctx->usr_cfg_ba_buff_size; + + if (frm.addba_param_set.buff_size > MAX_BA_BUFF_SIZE) + frm.addba_param_set.buff_size = MAX_BA_BUFF_SIZE; + + if (frm.addba_param_set.buff_size > SIR_MAC_BA_DEFAULT_BUFF_SIZE) { + if (session->active_ba_64_session) { + frm.addba_param_set.buff_size = + SIR_MAC_BA_DEFAULT_BUFF_SIZE; + } + } else if (!session->active_ba_64_session) { + session->active_ba_64_session = true; + } + if (buff_size && (frm.addba_param_set.buff_size > buff_size)) { + pe_debug("buff size: %d larger than peer's capability: %d", + frm.addba_param_set.buff_size, buff_size); + frm.addba_param_set.buff_size = buff_size; + } + + /* Enable RX AMSDU only in HE mode if supported */ + if (mac_ctx->is_usr_cfg_amsdu_enabled && + ((IS_PE_SESSION_HE_MODE(session) && + WLAN_REG_IS_24GHZ_CH_FREQ(session->curr_op_freq)) || + !WLAN_REG_IS_24GHZ_CH_FREQ(session->curr_op_freq))) + frm.addba_param_set.amsdu_supp = amsdu_support; + else + frm.addba_param_set.amsdu_supp = 0; + + frm.addba_param_set.policy = SIR_MAC_BA_POLICY_IMMEDIATE; + frm.ba_timeout.timeout = batimeout; + if (addba_extn_present) { + frm.addba_extn_element.present = 1; + frm.addba_extn_element.no_fragmentation = 1; + if (lim_is_session_he_capable(session)) { + he_frag = lim_get_session_he_frag_cap(session); + if (he_frag != 0) { + frm.addba_extn_element.no_fragmentation = 0; + frm.addba_extn_element.he_frag_operation = + he_frag; + } + } + } + + pe_debug("Sending a ADDBA Response from "QDF_MAC_ADDR_FMT" to "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(session->self_mac_addr), + QDF_MAC_ADDR_REF(peer_mac)); + pe_debug("tid %d dialog_token %d status %d buff_size %d amsdu_supp %d", + tid, frm.DialogToken.token, frm.Status.status, + frm.addba_param_set.buff_size, + frm.addba_param_set.amsdu_supp); + pe_debug("addba_extn %d he_capable %d no_frag %d he_frag %d", + addba_extn_present, + lim_is_session_he_capable(session), + frm.addba_extn_element.no_fragmentation, + frm.addba_extn_element.he_frag_operation); + + status = dot11f_get_packed_addba_rsp_size(mac_ctx, &frm, &payload_size); + if (DOT11F_FAILED(status)) { + pe_err("Failed to calculate the packed size for a ADDBA Response (0x%08x).", + status); + /* We'll fall back on the worst case scenario: */ + payload_size = sizeof(tDot11faddba_rsp); + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while calculating the packed size for a ADDBA Response (0x%08x).", status); + } + + num_bytes = payload_size + sizeof(*mgmt_hdr); + qdf_status = cds_packet_alloc(num_bytes, (void **)&frame_ptr, + (void **)&pkt_ptr); + if (!QDF_IS_STATUS_SUCCESS(qdf_status) || (!pkt_ptr)) { + pe_err("Failed to allocate %d bytes for a ADDBA response action frame", + num_bytes); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_zero(frame_ptr, num_bytes); + + lim_populate_mac_header(mac_ctx, frame_ptr, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer_mac, session->self_mac_addr); + + /* Update A3 with the BSSID */ + mgmt_hdr = (tpSirMacMgmtHdr) frame_ptr; + sir_copy_mac_addr(mgmt_hdr->bssId, session->bssId); + + /* ADDBA Response is a robust mgmt action frame, + * set the "protect" (aka WEP) bit in the FC + */ + lim_set_protected_bit(mac_ctx, session, peer_mac, mgmt_hdr); + + if (is_wep && sta_ds && sta_ds->staType == STA_ENTRY_NDI_PEER) { + mgmt_hdr->fc.wep = 1; + tx_flag |= HAL_USE_PMF; + } + + status = dot11f_pack_addba_rsp(mac_ctx, &frm, + frame_ptr + sizeof(tSirMacMgmtHdr), payload_size, + &payload_size); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a ADDBA Response (0x%08x)", + status); + qdf_status = QDF_STATUS_E_FAILURE; + goto error_addba_rsp; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing ADDBA Response (0x%08x)", + status); + } + + + if (!wlan_reg_is_24ghz_ch_freq(session->curr_op_freq) || + session->opmode == QDF_P2P_CLIENT_MODE || + session->opmode == QDF_P2P_GO_MODE) + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + session->peSessionId, mgmt_hdr->fc.subType)); + qdf_status = wma_tx_frameWithTxComplete(mac_ctx, pkt_ptr, + (uint16_t)num_bytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, 7, + NULL, frame_ptr, + lim_addba_rsp_tx_complete_cnf, + tx_flag, vdev_id, + false, 0, RATEID_DEFAULT); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + session->peSessionId, qdf_status)); + if (QDF_STATUS_SUCCESS != qdf_status) { + pe_err("wma_tx_frame FAILED! Status [%d]", + qdf_status); + return QDF_STATUS_E_FAILURE; + } else { + return QDF_STATUS_SUCCESS; + } + +error_addba_rsp: + cds_packet_free((void *)pkt_ptr); + return qdf_status; +} + +/** + * lim_delba_tx_complete_cnf() - Confirmation for Delba OTA + * @context: pointer to global mac + * @buf: netbuf of Del BA frame + * @tx_complete : Sent status + * @params; tx completion params + * + * Return: This returns QDF_STATUS + */ +static QDF_STATUS lim_delba_tx_complete_cnf(void *context, + qdf_nbuf_t buf, + uint32_t tx_complete, + void *params) +{ + struct mac_context *mac_ctx = (struct mac_context *)context; + tSirMacMgmtHdr *mac_hdr; + struct sDot11fdelba_req frm; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint32_t frame_len; + QDF_STATUS status; + uint8_t *data; + struct wmi_mgmt_params *mgmt_params = (struct wmi_mgmt_params *)params; + + if (!mgmt_params || !mac_ctx || !buf || !soc) { + pe_err("delba tx cnf invalid parameters"); + goto error; + } + data = qdf_nbuf_data(buf); + if (!data) { + pe_err("Delba frame is NULL"); + goto error; + } + + mac_hdr = (tSirMacMgmtHdr *)data; + qdf_mem_zero((void *)&frm, sizeof(struct sDot11fdelba_req)); + frame_len = sizeof(frm); + status = dot11f_unpack_delba_req(mac_ctx, (uint8_t *)data + + sizeof(*mac_hdr), frame_len, + &frm, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to unpack and parse delba (0x%08x, %d bytes)", + status, frame_len); + goto error; + } + pe_debug("delba ota done vdev %d "QDF_MAC_ADDR_FMT" tid %d desc_id %d status %d", + mgmt_params->vdev_id, + QDF_MAC_ADDR_REF(mac_hdr->da), frm.delba_param_set.tid, + mgmt_params->desc_id, tx_complete); + cdp_delba_tx_completion(soc, mac_hdr->da, mgmt_params->vdev_id, + frm.delba_param_set.tid, tx_complete); + +error: + if (buf) + qdf_nbuf_free(buf); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_send_delba_action_frame(struct mac_context *mac_ctx, + uint8_t vdev_id, uint8_t *peer_macaddr, + uint8_t tid, uint8_t reason_code) +{ + struct pe_session *session; + struct sDot11fdelba_req frm; + QDF_STATUS qdf_status; + tpSirMacMgmtHdr mgmt_hdr; + uint32_t num_bytes, payload_size = 0; + uint32_t status; + void *pkt_ptr = NULL; + uint8_t *frame_ptr; + uint8_t tx_flag = 0; + + session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session) { + pe_debug("delba invalid vdev id %d ", vdev_id); + return QDF_STATUS_E_INVAL; + } + pe_debug("send delba vdev %d "QDF_MAC_ADDR_FMT" tid %d reason %d", vdev_id, + QDF_MAC_ADDR_REF(peer_macaddr), tid, reason_code); + qdf_mem_zero((uint8_t *)&frm, sizeof(frm)); + frm.Category.category = ACTION_CATEGORY_BACK; + frm.Action.action = DELBA; + frm.delba_param_set.initiator = 0; + frm.delba_param_set.tid = tid; + frm.Reason.code = reason_code; + + status = dot11f_get_packed_delba_req_size(mac_ctx, &frm, &payload_size); + if (DOT11F_FAILED(status)) { + pe_err("Failed to calculate the packed size for a DELBA(0x%08x).", + status); + /* We'll fall back on the worst case scenario: */ + payload_size = sizeof(struct sDot11fdelba_req); + } else if (DOT11F_WARNED(status)) { + pe_warn("Warnings in calculating the packed size for a DELBA (0x%08x).", + status); + } + num_bytes = payload_size + sizeof(*mgmt_hdr); + qdf_status = cds_packet_alloc(num_bytes, (void **)&frame_ptr, + (void **)&pkt_ptr); + if (!QDF_IS_STATUS_SUCCESS(qdf_status) || !pkt_ptr) { + pe_err("Failed to allocate %d bytes for a DELBA", + num_bytes); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_zero(frame_ptr, num_bytes); + + lim_populate_mac_header(mac_ctx, frame_ptr, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_ACTION, peer_macaddr, + session->self_mac_addr); + + /* Update A3 with the BSSID */ + mgmt_hdr = (tpSirMacMgmtHdr)frame_ptr; + sir_copy_mac_addr(mgmt_hdr->bssId, session->bssId); + + /* DEL is a robust mgmt action frame, + * set the "protect" (aka WEP) bit in the FC + */ + lim_set_protected_bit(mac_ctx, session, peer_macaddr, mgmt_hdr); + + status = dot11f_pack_delba_req(mac_ctx, &frm, + frame_ptr + sizeof(tSirMacMgmtHdr), + payload_size, &payload_size); + if (DOT11F_FAILED(status)) { + pe_err("Failed to pack a DELBA (0x%08x)", + status); + qdf_status = QDF_STATUS_E_FAILURE; + goto error_delba; + } else if (DOT11F_WARNED(status)) { + pe_warn("There were warnings while packing DELBA (0x%08x)", + status); + } + if (wlan_reg_is_5ghz_ch_freq(session->curr_op_freq) || + wlan_reg_is_6ghz_chan_freq(session->curr_op_freq) || + session->opmode == QDF_P2P_CLIENT_MODE || + session->opmode == QDF_P2P_GO_MODE) + tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME; + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT, + session->peSessionId, mgmt_hdr->fc.subType)); + qdf_status = wma_tx_frameWithTxComplete(mac_ctx, pkt_ptr, + (uint16_t)num_bytes, + TXRX_FRM_802_11_MGMT, + ANI_TXDIR_TODS, 7, + NULL, frame_ptr, + lim_delba_tx_complete_cnf, + tx_flag, vdev_id, + false, 0, RATEID_DEFAULT); + if (qdf_status != QDF_STATUS_SUCCESS) { + pe_err("delba wma_tx_frame FAILED! Status [%d]", qdf_status); + return qdf_status; + } else { + return QDF_STATUS_SUCCESS; + } + +error_delba: + if (pkt_ptr) + cds_packet_free((void *)pkt_ptr); + + return qdf_status; +} + +#define WLAN_SAE_AUTH_TIMEOUT 1000 + +/** + * lim_tx_mgmt_frame() - Transmits Auth mgmt frame + * @mac_ctx Pointer to Global MAC structure + * @vdev_id: vdev id + * @msg_len: Received message length + * @packet: Packet to be transmitted + * @frame: Received frame + * + * Return: None + */ +static void lim_tx_mgmt_frame(struct mac_context *mac_ctx, uint8_t vdev_id, + uint32_t msg_len, void *packet, uint8_t *frame) +{ + tpSirMacFrameCtl fc = (tpSirMacFrameCtl)frame; + QDF_STATUS qdf_status; + struct pe_session *session; + uint16_t auth_ack_status; + enum rateid min_rid = RATEID_DEFAULT; + + session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session) { + cds_packet_free((void *)packet); + pe_err("session not found for given vdev_id %d", + vdev_id); + return; + } + + qdf_mtrace(QDF_MODULE_ID_PE, QDF_MODULE_ID_WMA, TRACE_CODE_TX_MGMT, + session->peSessionId, 0); + + mac_ctx->auth_ack_status = LIM_ACK_NOT_RCD; + min_rid = lim_get_min_session_txrate(session); + + qdf_status = wma_tx_frameWithTxComplete(mac_ctx, packet, + (uint16_t)msg_len, + TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, + 7, lim_tx_complete, frame, + lim_auth_tx_complete_cnf, + 0, vdev_id, false, 0, min_rid); + MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE, + session->peSessionId, qdf_status)); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("*** Could not send Auth frame (subType: %d), retCode=%X ***", + fc->subType, qdf_status); + mac_ctx->auth_ack_status = LIM_TX_FAILED; + auth_ack_status = SENT_FAIL; + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_AUTH_ACK_EVENT, + session, auth_ack_status, QDF_STATUS_E_FAILURE); + /* Pkt will be freed up by the callback */ + } +} + +static void +lim_handle_sae_auth_retry(struct mac_context *mac_ctx, uint8_t vdev_id, + uint8_t *frame, uint32_t frame_len) +{ + struct pe_session *session; + struct sae_auth_retry *sae_retry; + uint8_t retry_count = 0; + + session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session) { + pe_err("session not found for given vdev_id %d", + vdev_id); + return; + } + if (session->opmode != QDF_STA_MODE) + return; + + if (session->limMlmState == eLIM_MLM_WT_SAE_AUTH_STATE) + wlan_mlme_get_sae_auth_retry_count(mac_ctx->psoc, &retry_count); + else + wlan_mlme_get_sae_roam_auth_retry_count(mac_ctx->psoc, + &retry_count); + if (!retry_count) { + pe_debug("vdev %d: SAE Auth retry disabled", vdev_id); + return; + } + + sae_retry = mlme_get_sae_auth_retry(session->vdev); + if (!sae_retry) { + pe_err("sae retry pointer is NULL for vdev_id %d", + vdev_id); + return; + } + + if (sae_retry->sae_auth.data) + lim_sae_auth_cleanup_retry(mac_ctx, vdev_id); + + sae_retry->sae_auth.data = qdf_mem_malloc(frame_len); + if (!sae_retry->sae_auth.data) { + pe_err("failed to alloc memory for sae auth"); + return; + } + + pe_debug("SAE auth frame queued vdev_id %d seq_num %d", + vdev_id, mac_ctx->mgmtSeqNum); + qdf_mem_copy(sae_retry->sae_auth.data, frame, frame_len); + mac_ctx->lim.lim_timers.g_lim_periodic_auth_retry_timer.sessionId = + session->peSessionId; + sae_retry->sae_auth.len = frame_len; + sae_retry->sae_auth_max_retry = retry_count; + + tx_timer_change( + &mac_ctx->lim.lim_timers.g_lim_periodic_auth_retry_timer, + SYS_MS_TO_TICKS(WLAN_SAE_AUTH_TIMEOUT), 0); + /* Activate Auth Retry timer */ + if (tx_timer_activate( + &mac_ctx->lim.lim_timers.g_lim_periodic_auth_retry_timer) != + TX_SUCCESS) { + pe_err("failed to start periodic auth retry timer"); + lim_sae_auth_cleanup_retry(mac_ctx, vdev_id); + } +} + +void lim_send_frame(struct mac_context *mac_ctx, uint8_t vdev_id, uint8_t *buf, + uint16_t buf_len) +{ + QDF_STATUS qdf_status; + uint8_t *frame; + void *packet; + tpSirMacFrameCtl fc = (tpSirMacFrameCtl)buf; + tpSirMacMgmtHdr mac_hdr = (tpSirMacMgmtHdr)buf; + + pe_debug("sending fc->type: %d fc->subType: %d", + fc->type, fc->subType); + + lim_add_mgmt_seq_num(mac_ctx, mac_hdr); + qdf_status = cds_packet_alloc(buf_len, (void **)&frame, + (void **)&packet); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + pe_err("call to bufAlloc failed for AUTH frame"); + return; + } + + qdf_mem_copy(frame, buf, buf_len); + lim_tx_mgmt_frame(mac_ctx, vdev_id, buf_len, packet, frame); +} + +void lim_send_mgmt_frame_tx(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + struct sir_mgmt_msg *mb_msg = (struct sir_mgmt_msg *)msg->bodyptr; + uint32_t msg_len; + tpSirMacFrameCtl fc = (tpSirMacFrameCtl) mb_msg->data; + uint8_t vdev_id; + uint16_t auth_algo; + + msg_len = mb_msg->msg_len - sizeof(*mb_msg); + vdev_id = mb_msg->vdev_id; + + if (fc->subType == SIR_MAC_MGMT_AUTH) { + auth_algo = *(uint16_t *)(mb_msg->data + + sizeof(tSirMacMgmtHdr)); + if (auth_algo == eSIR_AUTH_TYPE_SAE) + lim_handle_sae_auth_retry(mac_ctx, vdev_id, + mb_msg->data, msg_len); + } + + lim_send_frame(mac_ctx, vdev_id, mb_msg->data, msg_len); +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_messages.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_messages.c new file mode 100644 index 0000000000000000000000000000000000000000..5b7c3a96832a583189c073e32abbeb830ebb13e3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_messages.c @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * lim_send_messages.c: Provides functions to send messages or Indications to HAL. + * Author: Sunit Bhatia + * Date: 09/21/2006 + * History:- + * Date Modified by Modification Information + * + * -------------------------------------------------------------------------- + * + */ +#include "lim_send_messages.h" +#include "lim_trace.h" +#include "wlan_reg_services_api.h" +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ +#include "host_diag_core_log.h" +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ +#include "lim_utils.h" +#include "wma.h" +#include "../../core/src/vdev_mgr_ops.h" + +/** + * lim_send_beacon_params() - updates bcn params to WMA + * + * @mac : pointer to Global Mac structure. + * @tpUpdateBeaconParams : pointer to the structure, which contains the beacon + * parameters which are changed. + * + * This function is called to send beacon interval, short preamble or other + * parameters to WMA, which are changed and indication is received in beacon. + * + * @return success if message send is ok, else false. + */ +QDF_STATUS lim_send_beacon_params(struct mac_context *mac, + tpUpdateBeaconParams pUpdatedBcnParams, + struct pe_session *pe_session) +{ + tpUpdateBeaconParams pBcnParams = NULL; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + + pBcnParams = qdf_mem_malloc(sizeof(*pBcnParams)); + if (!pBcnParams) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy((uint8_t *) pBcnParams, pUpdatedBcnParams, + sizeof(*pBcnParams)); + msgQ.type = WMA_UPDATE_BEACON_IND; + msgQ.reserved = 0; + msgQ.bodyptr = pBcnParams; + msgQ.bodyval = 0; + pe_debug("Sending WMA_UPDATE_BEACON_IND, paramChangeBitmap in hex: %x", + pUpdatedBcnParams->paramChangeBitmap); + if (!pe_session) { + qdf_mem_free(pBcnParams); + MTRACE(mac_trace_msg_tx(mac, NO_SESSION, msgQ.type)); + return QDF_STATUS_E_FAILURE; + } else { + MTRACE(mac_trace_msg_tx(mac, + pe_session->peSessionId, + msgQ.type)); + } + pBcnParams->vdev_id = pe_session->vdev_id; + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + qdf_mem_free(pBcnParams); + pe_err("Posting WMA_UPDATE_BEACON_IND, reason=%X", + retCode); + } + lim_send_beacon_ind(mac, pe_session, REASON_DEFAULT); + return retCode; +} + +QDF_STATUS lim_send_switch_chnl_params(struct mac_context *mac, + struct pe_session *session) +{ + struct vdev_mlme_obj *mlme_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct vdev_start_response rsp = {0}; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + pe_err("Invalid wma handle"); + return QDF_STATUS_E_FAILURE; + } + if (!session) { + pe_err("session is NULL"); + return QDF_STATUS_E_FAILURE; + } + if (!session->vdev) { + pe_err("vdev is NULL"); + return QDF_STATUS_E_FAILURE; + } + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(session->vdev); + if (!mlme_obj) { + pe_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + status = lim_pre_vdev_start(mac, mlme_obj, session); + if (QDF_IS_STATUS_ERROR(status)) + goto send_resp; + + session->ch_switch_in_progress = true; + + /* we need to defer the message until we + * get the response back from WMA + */ + SET_LIM_PROCESS_DEFD_MESGS(mac, false); + + status = wma_pre_chan_switch_setup(session->vdev_id); + if (status != QDF_STATUS_SUCCESS) { + pe_err("failed status = %d", status); + goto send_resp; + } + status = vdev_mgr_start_send(mlme_obj, + mlme_is_chan_switch_in_progress(session->vdev)); + if (status != QDF_STATUS_SUCCESS) { + pe_err("failed status = %d", status); + goto send_resp; + } + wma_post_chan_switch_setup(session->vdev_id); + + return QDF_STATUS_SUCCESS; +send_resp: + rsp.status = status; + rsp.vdev_id = session->vdev_id; + + wma_handle_channel_switch_resp(wma, &rsp); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_send_edca_params(struct mac_context *mac, + tSirMacEdcaParamRecord *pUpdatedEdcaParams, + uint16_t vdev_id, bool mu_edca) +{ + tEdcaParams *pEdcaParams = NULL; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + + pEdcaParams = qdf_mem_malloc(sizeof(tEdcaParams)); + if (!pEdcaParams) + return QDF_STATUS_E_NOMEM; + pEdcaParams->vdev_id = vdev_id; + pEdcaParams->acbe = pUpdatedEdcaParams[QCA_WLAN_AC_BE]; + pEdcaParams->acbk = pUpdatedEdcaParams[QCA_WLAN_AC_BK]; + pEdcaParams->acvi = pUpdatedEdcaParams[QCA_WLAN_AC_VI]; + pEdcaParams->acvo = pUpdatedEdcaParams[QCA_WLAN_AC_VO]; + pEdcaParams->mu_edca_params = mu_edca; + msgQ.type = WMA_UPDATE_EDCA_PROFILE_IND; + msgQ.reserved = 0; + msgQ.bodyptr = pEdcaParams; + msgQ.bodyval = 0; + pe_debug("Sending WMA_UPDATE_EDCA_PROFILE_IND"); + MTRACE(mac_trace_msg_tx(mac, NO_SESSION, msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + qdf_mem_free(pEdcaParams); + pe_err("Posting WMA_UPDATE_EDCA_PROFILE_IND failed, reason=%X", + retCode); + } + return retCode; +} + +void lim_set_active_edca_params(struct mac_context *mac_ctx, + tSirMacEdcaParamRecord *edca_params, + struct pe_session *pe_session) +{ + uint8_t ac, new_ac, i; + uint8_t ac_admitted; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + host_log_qos_edca_pkt_type *log_ptr = NULL; +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + /* Initialize gLimEdcaParamsActive[] to be same as localEdcaParams */ + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_BE] = edca_params[QCA_WLAN_AC_BE]; + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_BK] = edca_params[QCA_WLAN_AC_BK]; + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_VI] = edca_params[QCA_WLAN_AC_VI]; + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_VO] = edca_params[QCA_WLAN_AC_VO]; + + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_BE].no_ack = + mac_ctx->no_ack_policy_cfg[QCA_WLAN_AC_BE]; + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_BK].no_ack = + mac_ctx->no_ack_policy_cfg[QCA_WLAN_AC_BK]; + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_VI].no_ack = + mac_ctx->no_ack_policy_cfg[QCA_WLAN_AC_VI]; + pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_VO].no_ack = + mac_ctx->no_ack_policy_cfg[QCA_WLAN_AC_VO]; + + /* An AC requires downgrade if the ACM bit is set, and the AC has not + * yet been admitted in uplink or bi-directions. + * If an AC requires downgrade, it will downgrade to the next beset AC + * for which ACM is not enabled. + * + * - There's no need to downgrade AC_BE since it IS the lowest AC. Hence + * start the for loop with AC_BK. + * - If ACM bit is set for an AC, initially downgrade it to AC_BE. Then + * traverse thru the AC list. If we do find the next best AC which is + * better than AC_BE, then use that one. For example, if ACM bits are set + * such that: BE_ACM=1, BK_ACM=1, VI_ACM=1, VO_ACM=0 + * then all AC will be downgraded to AC_BE. + */ + pe_debug("adAdmitMask[UPLINK] = 0x%x ", + pe_session->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK]); + pe_debug("adAdmitMask[DOWNLINK] = 0x%x ", + pe_session->gAcAdmitMask[SIR_MAC_DIRECTION_DNLINK]); + for (ac = QCA_WLAN_AC_BK; ac <= QCA_WLAN_AC_VO; ac++) { + ac_admitted = + ((pe_session->gAcAdmitMask[SIR_MAC_DIRECTION_UPLINK] & + (1 << ac)) >> ac); + + pe_debug("For AC[%d]: acm=%d, ac_admitted=%d ", + ac, edca_params[ac].aci.acm, ac_admitted); + if ((edca_params[ac].aci.acm == 1) && (ac_admitted == 0)) { + pe_debug("We need to downgrade AC %d!!", ac); + /* Loop backwards through AC values until it finds + * acm == 0 or reaches QCA_WLAN_AC_BE. + * Note that for block has no executable statements. + */ + for (i = ac - 1; + (i > QCA_WLAN_AC_BE && + (edca_params[i].aci.acm != 0)); + i--) + ; + new_ac = i; + pe_debug("Downgrading AC %d ---> AC %d ", ac, new_ac); + pe_session->gLimEdcaParamsActive[ac] = + edca_params[new_ac]; + } + } +/* log: LOG_WLAN_QOS_EDCA_C */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + WLAN_HOST_DIAG_LOG_ALLOC(log_ptr, host_log_qos_edca_pkt_type, + LOG_WLAN_QOS_EDCA_C); + if (log_ptr) { + tSirMacEdcaParamRecord *rec; + + rec = &pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_BE]; + log_ptr->aci_be = rec->aci.aci; + log_ptr->cw_be = rec->cw.max << 4 | rec->cw.min; + log_ptr->txoplimit_be = rec->txoplimit; + + rec = &pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_BK]; + log_ptr->aci_bk = rec->aci.aci; + log_ptr->cw_bk = rec->cw.max << 4 | rec->cw.min; + log_ptr->txoplimit_bk = rec->txoplimit; + + rec = &pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_VI]; + log_ptr->aci_vi = rec->aci.aci; + log_ptr->cw_vi = rec->cw.max << 4 | rec->cw.min; + log_ptr->txoplimit_vi = rec->txoplimit; + + rec = &pe_session->gLimEdcaParamsActive[QCA_WLAN_AC_VO]; + log_ptr->aci_vo = rec->aci.aci; + log_ptr->cw_vo = rec->cw.max << 4 | rec->cw.min; + log_ptr->txoplimit_vo = rec->txoplimit; + } + WLAN_HOST_DIAG_LOG_REPORT(log_ptr); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + return; +} + +QDF_STATUS lim_send_mode_update(struct mac_context *mac, + tUpdateVHTOpMode *pTempParam, + struct pe_session *pe_session) +{ + tUpdateVHTOpMode *pVhtOpMode = NULL; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + + pVhtOpMode = qdf_mem_malloc(sizeof(tUpdateVHTOpMode)); + if (!pVhtOpMode) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy((uint8_t *) pVhtOpMode, pTempParam, + sizeof(tUpdateVHTOpMode)); + msgQ.type = WMA_UPDATE_OP_MODE; + msgQ.reserved = 0; + msgQ.bodyptr = pVhtOpMode; + msgQ.bodyval = 0; + pe_debug("Sending WMA_UPDATE_OP_MODE, op_mode %d", + pVhtOpMode->opMode); + if (!pe_session) + MTRACE(mac_trace_msg_tx(mac, NO_SESSION, msgQ.type)); + else + MTRACE(mac_trace_msg_tx(mac, + pe_session->peSessionId, + msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + qdf_mem_free(pVhtOpMode); + pe_err("Posting WMA_UPDATE_OP_MODE failed, reason=%X", + retCode); + } + + return retCode; +} + +QDF_STATUS lim_send_rx_nss_update(struct mac_context *mac, + tUpdateRxNss *pTempParam, + struct pe_session *pe_session) +{ + tUpdateRxNss *pRxNss = NULL; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + + pRxNss = qdf_mem_malloc(sizeof(tUpdateRxNss)); + if (!pRxNss) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy((uint8_t *) pRxNss, pTempParam, sizeof(tUpdateRxNss)); + msgQ.type = WMA_UPDATE_RX_NSS; + msgQ.reserved = 0; + msgQ.bodyptr = pRxNss; + msgQ.bodyval = 0; + pe_debug("Sending WMA_UPDATE_RX_NSS"); + if (!pe_session) + MTRACE(mac_trace_msg_tx(mac, NO_SESSION, msgQ.type)); + else + MTRACE(mac_trace_msg_tx(mac, + pe_session->peSessionId, + msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + qdf_mem_free(pRxNss); + pe_err("Posting WMA_UPDATE_RX_NSS failed, reason=%X", + retCode); + } + + return retCode; +} + +QDF_STATUS lim_set_membership(struct mac_context *mac, + tUpdateMembership *pTempParam, + struct pe_session *pe_session) +{ + tUpdateMembership *pMembership = NULL; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + + pMembership = qdf_mem_malloc(sizeof(tUpdateMembership)); + if (!pMembership) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy((uint8_t *) pMembership, pTempParam, + sizeof(tUpdateMembership)); + + msgQ.type = WMA_UPDATE_MEMBERSHIP; + msgQ.reserved = 0; + msgQ.bodyptr = pMembership; + msgQ.bodyval = 0; + pe_debug("Sending WMA_UPDATE_MEMBERSHIP"); + if (!pe_session) + MTRACE(mac_trace_msg_tx(mac, NO_SESSION, msgQ.type)); + else + MTRACE(mac_trace_msg_tx(mac, + pe_session->peSessionId, + msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + qdf_mem_free(pMembership); + pe_err("Posting WMA_UPDATE_MEMBERSHIP failed, reason=%X", + retCode); + } + + return retCode; +} + +QDF_STATUS lim_set_user_pos(struct mac_context *mac, + tUpdateUserPos *pTempParam, + struct pe_session *pe_session) +{ + tUpdateUserPos *pUserPos = NULL; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + + pUserPos = qdf_mem_malloc(sizeof(tUpdateUserPos)); + if (!pUserPos) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy((uint8_t *) pUserPos, pTempParam, sizeof(tUpdateUserPos)); + + msgQ.type = WMA_UPDATE_USERPOS; + msgQ.reserved = 0; + msgQ.bodyptr = pUserPos; + msgQ.bodyval = 0; + pe_debug("Sending WMA_UPDATE_USERPOS"); + if (!pe_session) + MTRACE(mac_trace_msg_tx(mac, NO_SESSION, msgQ.type)); + else + MTRACE(mac_trace_msg_tx(mac, + pe_session->peSessionId, + msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + qdf_mem_free(pUserPos); + pe_err("Posting WMA_UPDATE_USERPOS failed, reason=%X", + retCode); + } + + return retCode; +} + +#ifdef WLAN_FEATURE_11W +/** + * lim_send_exclude_unencrypt_ind() - sends WMA_EXCLUDE_UNENCRYPTED_IND to HAL + * @mac: mac global context + * @excludeUnenc: true: ignore, false: indicate + * @pe_session: session context + * + * LIM sends a message to HAL to indicate whether to ignore or indicate the + * unprotected packet error. + * + * Return: status of operation + */ +QDF_STATUS lim_send_exclude_unencrypt_ind(struct mac_context *mac, + bool excludeUnenc, + struct pe_session *pe_session) +{ + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + tSirWlanExcludeUnencryptParam *pExcludeUnencryptParam; + + pExcludeUnencryptParam = + qdf_mem_malloc(sizeof(tSirWlanExcludeUnencryptParam)); + if (!pExcludeUnencryptParam) + return QDF_STATUS_E_NOMEM; + + pExcludeUnencryptParam->excludeUnencrypt = excludeUnenc; + qdf_mem_copy(pExcludeUnencryptParam->bssid.bytes, pe_session->bssId, + QDF_MAC_ADDR_SIZE); + + msgQ.type = WMA_EXCLUDE_UNENCRYPTED_IND; + msgQ.reserved = 0; + msgQ.bodyptr = pExcludeUnencryptParam; + msgQ.bodyval = 0; + pe_debug("Sending WMA_EXCLUDE_UNENCRYPTED_IND"); + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + qdf_mem_free(pExcludeUnencryptParam); + pe_err("Posting WMA_EXCLUDE_UNENCRYPTED_IND failed, reason=%X", + retCode); + } + + return retCode; +} +#endif + +/** + * lim_send_ht40_obss_scanind() - send ht40 obss start scan request + * mac: mac context + * session PE session handle + * + * LIM sends a HT40 start scan message to WMA + * + * Return: status of operation + */ +QDF_STATUS lim_send_ht40_obss_scanind(struct mac_context *mac_ctx, + struct pe_session *session) +{ + QDF_STATUS ret = QDF_STATUS_SUCCESS; + struct obss_ht40_scanind *ht40_obss_scanind; + uint32_t channelnum, chan_freq; + struct scheduler_msg msg = {0}; + uint8_t channel24gnum, count; + + ht40_obss_scanind = qdf_mem_malloc(sizeof(struct obss_ht40_scanind)); + if (!ht40_obss_scanind) + return QDF_STATUS_E_FAILURE; + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + "OBSS Scan Indication bssid " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(session->bssId)); + + ht40_obss_scanind->cmd = HT40_OBSS_SCAN_PARAM_START; + ht40_obss_scanind->scan_type = eSIR_ACTIVE_SCAN; + ht40_obss_scanind->obss_passive_dwelltime = + session->obss_ht40_scanparam.obss_passive_dwelltime; + ht40_obss_scanind->obss_active_dwelltime = + session->obss_ht40_scanparam.obss_active_dwelltime; + ht40_obss_scanind->obss_width_trigger_interval = + session->obss_ht40_scanparam.obss_width_trigger_interval; + ht40_obss_scanind->obss_passive_total_per_channel = + session->obss_ht40_scanparam.obss_passive_total_per_channel; + ht40_obss_scanind->obss_active_total_per_channel = + session->obss_ht40_scanparam.obss_active_total_per_channel; + ht40_obss_scanind->bsswidth_ch_trans_delay = + session->obss_ht40_scanparam.bsswidth_ch_trans_delay; + ht40_obss_scanind->obss_activity_threshold = + session->obss_ht40_scanparam.obss_activity_threshold; + ht40_obss_scanind->current_operatingclass = + wlan_reg_dmn_get_opclass_from_channel( + mac_ctx->scan.countryCodeCurrent, + wlan_reg_freq_to_chan( + mac_ctx->pdev, session->curr_op_freq), + session->ch_width); + channelnum = mac_ctx->mlme_cfg->reg.valid_channel_list_num; + + /* Extract 24G channel list */ + channel24gnum = 0; + for (count = 0; count < channelnum && + (channel24gnum < SIR_ROAM_MAX_CHANNELS); count++) { + chan_freq = + mac_ctx->mlme_cfg->reg.valid_channel_freq_list[count]; + if (wlan_reg_is_24ghz_ch_freq(chan_freq)) { + ht40_obss_scanind->chan_freq_list[channel24gnum] = + chan_freq; + channel24gnum++; + } + } + ht40_obss_scanind->channel_count = channel24gnum; + /* FW API requests BSS IDX */ + ht40_obss_scanind->bss_id = session->vdev_id; + ht40_obss_scanind->fortymhz_intolerent = 0; + ht40_obss_scanind->iefield_len = 0; + msg.type = WMA_HT40_OBSS_SCAN_IND; + msg.reserved = 0; + msg.bodyptr = (void *)ht40_obss_scanind; + msg.bodyval = 0; + pe_debug("Sending WDA_HT40_OBSS_SCAN_IND to WDA" + "Obss Scan trigger width: %d, delay factor: %d", + ht40_obss_scanind->obss_width_trigger_interval, + ht40_obss_scanind->bsswidth_ch_trans_delay); + ret = wma_post_ctrl_msg(mac_ctx, &msg); + if (QDF_STATUS_SUCCESS != ret) { + pe_err("WDA_HT40_OBSS_SCAN_IND msg failed, reason=%X", + ret); + qdf_mem_free(ht40_obss_scanind); + } + return ret; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_messages.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_messages.h new file mode 100644 index 0000000000000000000000000000000000000000..f114208e1ade22e857397d042417abffc36f7135 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_messages.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * lim_send_messages.h: Provides functions to send messages or Indications to HAL. + * Author: Sunit Bhatia + * Date: 09/21/2006 + * History:- + * Date Modified by Modification Information + * + * -------------------------------------------------------------------------- + * + */ +#ifndef __LIM_SEND_MESSAGES_H +#define __LIM_SEND_MESSAGES_H + +#include "ani_global.h" +#include "lim_types.h" +#include "wma_if.h" +#include "sir_params.h" +QDF_STATUS lim_send_beacon_params(struct mac_context *mac, + tpUpdateBeaconParams pUpdatedBcnParams, + struct pe_session *pe_session); +/* QDF_STATUS lim_send_beacon_params(struct mac_context *mac, tpUpdateBeaconParams pUpdatedBcnParams); */ +QDF_STATUS lim_send_mode_update(struct mac_context *mac, + tUpdateVHTOpMode *tempParam, + struct pe_session *pe_session); +QDF_STATUS lim_send_rx_nss_update(struct mac_context *mac, + tUpdateRxNss *tempParam, + struct pe_session *pe_session); + +QDF_STATUS lim_set_membership(struct mac_context *mac, + tUpdateMembership *pTempParam, + struct pe_session *pe_session); + +QDF_STATUS lim_set_user_pos(struct mac_context *mac, + tUpdateUserPos *pTempParam, + struct pe_session *pe_session); + +/** + * lim_send_switch_chnl_params() - change channel + * @mac: pointer to Global MAC structure + * @session: pe session + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_send_switch_chnl_params(struct mac_context *mac, + struct pe_session *session); + +/** + * lim_send_edca_params() - Send edsa params to firmware + * @mac: pointer to Global MAC structure + * @pUpdatedEdcaParams: updated edca params + * @vdev_id: vdev id + * @mu_edca: MU edca + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_send_edca_params(struct mac_context *mac, + tSirMacEdcaParamRecord *pUpdatedEdcaParams, + uint16_t vdev_id, bool mu_edca); +/** + * lim_set_active_edca_params() - Choose best EDCA parameters + * @mac_ctx: pointer to Global Mac structure. + * @edca_params: pointer to the local EDCA parameters + * @pe_session: point to the session entry + * + * This function is called to set the most up-to-date EDCA parameters + * given the default local EDCA parameters. The rules are as following: + * - If ACM bit is set for all ACs, then downgrade everything to Best Effort. + * - If ACM is not set for any AC, then PE will use the default EDCA + * parameters as advertised by AP. + * - If ACM is set in any of the ACs, PE will use the EDCA parameters + * from the next best AC for which ACM is not enabled. + * + * Return: none + */ +void lim_set_active_edca_params(struct mac_context *mac_ctx, + tSirMacEdcaParamRecord *edca_params, + struct pe_session *pe_session); + +#define CAPABILITY_FILTER_MASK 0x73CF +#define ERP_FILTER_MASK 0xF8 +#define EDCA_FILTER_MASK 0xF0 +#define QOS_FILTER_MASK 0xF0 +#define HT_BYTE0_FILTER_MASK 0x0 +#define HT_BYTE2_FILTER_MASK 0xEB +#define HT_BYTE5_FILTER_MASK 0xFD +#define DS_PARAM_CHANNEL_MASK 0x0 +#define VHTOP_CHWIDTH_MASK 0xFC + +#ifdef WLAN_FEATURE_11W +QDF_STATUS lim_send_exclude_unencrypt_ind(struct mac_context *mac, + bool excludeUnenc, + struct pe_session *pe_session); +#endif +QDF_STATUS lim_send_ht40_obss_scanind(struct mac_context *mac_ctx, + struct pe_session *session); +void lim_handle_sme_join_result(struct mac_context *, + tSirResultCodes, uint16_t, struct pe_session *); +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c new file mode 100644 index 0000000000000000000000000000000000000000..4f10d98bd2f6d706c66132ba06a22f826bd0737e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c @@ -0,0 +1,2161 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_send_sme_rspMessages.cc contains the functions + * for sending SME response/notification messages to applications + * above MAC software. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#include "qdf_types.h" +#include "wni_api.h" +#include "sir_common.h" +#include "ani_global.h" + +#include "wni_cfg.h" +#include "sys_def.h" + +#include "sch_api.h" +#include "utils_api.h" +#include "lim_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_send_sme_rsp_messages.h" +#include "lim_ibss_peer_mgmt.h" +#include "lim_session_utils.h" +#include "lim_types.h" +#include "sir_api.h" +#include "cds_regdomain.h" +#include "lim_send_messages.h" +#include "nan_datapath.h" +#include "lim_assoc_utils.h" +#include "wlan_reg_services_api.h" +#include "wlan_utility.h" + +#include "wlan_tdls_tgt_api.h" +#include "lim_process_fils.h" +#include "wma.h" + +void lim_send_sme_rsp(struct mac_context *mac_ctx, uint16_t msg_type, + tSirResultCodes result_code, uint8_t vdev_id) +{ + struct scheduler_msg msg = {0}; + tSirSmeRsp *sme_rsp; + + pe_debug("Sending message: %s with reasonCode: %s", + lim_msg_str(msg_type), lim_result_code_str(result_code)); + + sme_rsp = qdf_mem_malloc(sizeof(tSirSmeRsp)); + if (!sme_rsp) + return; + + sme_rsp->messageType = msg_type; + sme_rsp->length = sizeof(tSirSmeRsp); + sme_rsp->status_code = result_code; + sme_rsp->vdev_id = vdev_id; + + msg.type = msg_type; + msg.bodyptr = sme_rsp; + msg.bodyval = 0; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_TX_SME_MSG, vdev_id, msg.type)); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + switch (msg_type) { + case eWNI_SME_STOP_BSS_RSP: + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_STOP_BSS_RSP_EVENT, + NULL, (uint16_t) result_code, 0); + break; + } +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_sys_process_mmh_msg_api(mac_ctx, &msg); +} + +/** + * lim_get_max_rate_flags() - Get rate flags + * @mac_ctx: Pointer to global MAC structure + * @sta_ds: Pointer to station ds structure + * + * This function is called to get the rate flags for a connection + * from the station ds structure depending on the ht and the vht + * channel width supported. + * + * Return: Returns the populated rate_flags + */ +uint32_t lim_get_max_rate_flags(struct mac_context *mac_ctx, tpDphHashNode sta_ds) +{ + uint32_t rate_flags = 0; + + if (!sta_ds) { + pe_err("sta_ds is NULL"); + return rate_flags; + } + + if (!sta_ds->mlmStaContext.htCapability && + !sta_ds->mlmStaContext.vhtCapability) { + rate_flags |= TX_RATE_LEGACY; + } else { + if (sta_ds->mlmStaContext.vhtCapability) { + if (WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ == + sta_ds->vhtSupportedChannelWidthSet) { + rate_flags |= TX_RATE_VHT80; + } else if (WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ == + sta_ds->vhtSupportedChannelWidthSet) { + if (sta_ds->htSupportedChannelWidthSet) + rate_flags |= TX_RATE_VHT40; + else + rate_flags |= TX_RATE_VHT20; + } + } else if (sta_ds->mlmStaContext.htCapability) { + if (sta_ds->htSupportedChannelWidthSet) + rate_flags |= TX_RATE_HT40; + else + rate_flags |= TX_RATE_HT20; + } + } + + if (sta_ds->htShortGI20Mhz || sta_ds->htShortGI40Mhz) + rate_flags |= TX_RATE_SGI; + + return rate_flags; +} + +/** + * lim_send_sme_join_reassoc_rsp_after_resume() - Send Response to SME + * @mac_ctx: Pointer to Global MAC structure + * @status: Resume link status + * @sme_join_rsp: Join response to be sent + * + * This function is called to send Join/Reassoc rsp + * message to SME after the resume link. + * + * Return: None + */ +static void +lim_send_sme_join_reassoc_rsp_after_resume(struct mac_context *mac_ctx, + QDF_STATUS status, + struct join_rsp *sme_join_rsp) +{ + struct scheduler_msg msg = {0}; + + msg.type = sme_join_rsp->messageType; + msg.bodyptr = sme_join_rsp; + msg.bodyval = 0; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_TX_SME_MSG, NO_SESSION, msg.type)); + lim_sys_process_mmh_msg_api(mac_ctx, &msg); +} + +/** + * lim_handle_join_rsp_status() - Handle the response. + * @mac_ctx: Pointer to Global MAC structure + * @session_entry: PE Session Info + * @result_code: Indicates the result of previously issued + * eWNI_SME_msgType_REQ message + * @sme_join_rsp The received response. + * + * This function will handle both the success and failure status + * of the received response. + * + * Return: None + */ +static void lim_handle_join_rsp_status(struct mac_context *mac_ctx, + struct pe_session *session_entry, + tSirResultCodes result_code, + struct join_rsp *sme_join_rsp) +{ + uint16_t bss_ie_len; + void *bss_ies; + bool is_vendor_ap_1_present; + struct join_req *join_reassoc_req = NULL; + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + struct ht_profile *ht_profile; +#endif + if (session_entry->beacon) { + sme_join_rsp->beaconLength = session_entry->bcnLen; + qdf_mem_copy(sme_join_rsp->frames, + session_entry->beacon, + sme_join_rsp->beaconLength); + qdf_mem_free(session_entry->beacon); + session_entry->beacon = NULL; + session_entry->bcnLen = 0; + } + + if (session_entry->assoc_req) { + sme_join_rsp->assocReqLength = + session_entry->assocReqLen; + qdf_mem_copy(sme_join_rsp->frames + + sme_join_rsp->beaconLength, + session_entry->assoc_req, + sme_join_rsp->assocReqLength); + qdf_mem_free(session_entry->assoc_req); + session_entry->assoc_req = NULL; + session_entry->assocReqLen = 0; + } + if (session_entry->assocRsp) { + sme_join_rsp->assocRspLength = + session_entry->assocRspLen; + qdf_mem_copy(sme_join_rsp->frames + + sme_join_rsp->beaconLength + + sme_join_rsp->assocReqLength, + session_entry->assocRsp, + sme_join_rsp->assocRspLength); + qdf_mem_free(session_entry->assocRsp); + session_entry->assocRsp = NULL; + session_entry->assocRspLen = 0; + } + + pe_debug("Beacon len %d Assoc Req len %d Assoc Rsp len %d", + sme_join_rsp->beaconLength, sme_join_rsp->assocReqLength, + sme_join_rsp->assocRspLength); + + if (result_code == eSIR_SME_SUCCESS) { + if (session_entry->ricData) { + sme_join_rsp->parsedRicRspLen = + session_entry->RICDataLen; + qdf_mem_copy(sme_join_rsp->frames + + sme_join_rsp->beaconLength + + sme_join_rsp->assocReqLength + + sme_join_rsp->assocRspLength, + session_entry->ricData, + sme_join_rsp->parsedRicRspLen); + qdf_mem_free(session_entry->ricData); + session_entry->ricData = NULL; + session_entry->RICDataLen = 0; + pe_debug("RicLength: %d", + sme_join_rsp->parsedRicRspLen); + } +#ifdef FEATURE_WLAN_ESE + if (session_entry->tspecIes) { + sme_join_rsp->tspecIeLen = + session_entry->tspecLen; + qdf_mem_copy(sme_join_rsp->frames + + sme_join_rsp->beaconLength + + sme_join_rsp->assocReqLength + + sme_join_rsp->assocRspLength + + sme_join_rsp->parsedRicRspLen, + session_entry->tspecIes, + sme_join_rsp->tspecIeLen); + qdf_mem_free(session_entry->tspecIes); + session_entry->tspecIes = NULL; + session_entry->tspecLen = 0; + pe_debug("ESE-TspecLen: %d", + sme_join_rsp->tspecIeLen); + } +#endif + sme_join_rsp->aid = session_entry->limAID; + sme_join_rsp->vht_channel_width = + session_entry->ch_width; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + if (session_entry->cc_switch_mode != + QDF_MCC_TO_SCC_SWITCH_DISABLE) { + ht_profile = &sme_join_rsp->ht_profile; + ht_profile->htSupportedChannelWidthSet = + session_entry->htSupportedChannelWidthSet; + ht_profile->htRecommendedTxWidthSet = + session_entry->htRecommendedTxWidthSet; + ht_profile->htSecondaryChannelOffset = + session_entry->htSecondaryChannelOffset; + ht_profile->dot11mode = session_entry->dot11mode; + ht_profile->htCapability = session_entry->htCapability; + ht_profile->vhtCapability = + session_entry->vhtCapability; + ht_profile->apCenterChan = session_entry->ch_center_freq_seg0; + ht_profile->apChanWidth = session_entry->ch_width; + } +#endif + pe_debug("lim_join_req:%pK, pLimReAssocReq:%pK", + session_entry->lim_join_req, + session_entry->pLimReAssocReq); + + if (session_entry->lim_join_req) + join_reassoc_req = session_entry->lim_join_req; + + if (session_entry->pLimReAssocReq) + join_reassoc_req = session_entry->pLimReAssocReq; + + if (!join_reassoc_req) { + pe_err("both lim_join_req and pLimReAssocReq NULL"); + return; + } + + bss_ie_len = lim_get_ielen_from_bss_description( + &join_reassoc_req->bssDescription); + bss_ies = &join_reassoc_req->bssDescription.ieFields; + is_vendor_ap_1_present = (wlan_get_vendor_ie_ptr_from_oui( + SIR_MAC_VENDOR_AP_1_OUI, SIR_MAC_VENDOR_AP_1_OUI_LEN, + bss_ies, bss_ie_len) != NULL); + + if (mac_ctx->roam.configParam.is_force_1x1 && + is_vendor_ap_1_present && (session_entry->nss == 2) && + (mac_ctx->lteCoexAntShare == 0 || + wlan_reg_is_5ghz_ch_freq(session_entry->curr_op_freq))) { + /* SET vdev param */ + pe_debug("sending SMPS intolrent vdev_param"); + wma_cli_set_command(session_entry->smeSessionId, + (int)WMI_VDEV_PARAM_SMPS_INTOLERANT, + 1, VDEV_CMD); + + } + } else { + if (session_entry->beacon) { + qdf_mem_free(session_entry->beacon); + session_entry->beacon = NULL; + session_entry->bcnLen = 0; + } + if (session_entry->assoc_req) { + qdf_mem_free(session_entry->assoc_req); + session_entry->assoc_req = NULL; + session_entry->assocReqLen = 0; + } + if (session_entry->assocRsp) { + qdf_mem_free(session_entry->assocRsp); + session_entry->assocRsp = NULL; + session_entry->assocRspLen = 0; + } + if (session_entry->ricData) { + qdf_mem_free(session_entry->ricData); + session_entry->ricData = NULL; + session_entry->RICDataLen = 0; + } +#ifdef FEATURE_WLAN_ESE + if (session_entry->tspecIes) { + qdf_mem_free(session_entry->tspecIes); + session_entry->tspecIes = NULL; + session_entry->tspecLen = 0; + } +#endif + } +} + +#ifdef WLAN_FEATURE_11AX +static void lim_add_he_info(struct parsed_ies *parsed_ies, + struct join_rsp *sme_join_rsp) +{ + if (parsed_ies->he_operation.present) + sme_join_rsp->he_operation = parsed_ies->he_operation; +} +#else +static inline void lim_add_he_info(struct parsed_ies *parsed_ies, + struct join_rsp *sme_join_rsp) +{ +} +#endif + +/** + * lim_add_bss_info() - copy data from session entry to join rsp + * @sta_ds: Station dph entry + * @sme_join_rsp: Join response buffer to be filled up + * + * Return: None + */ +static void lim_add_bss_info(tpDphHashNode sta_ds, + struct join_rsp *sme_join_rsp) +{ + struct parsed_ies *parsed_ies = &sta_ds->parsed_ies; + + if (parsed_ies->hs20vendor_ie.present) + sme_join_rsp->hs20vendor_ie = parsed_ies->hs20vendor_ie; + if (parsed_ies->vht_caps.present) + sme_join_rsp->vht_caps = parsed_ies->vht_caps; + if (parsed_ies->ht_caps.present) + sme_join_rsp->ht_caps = parsed_ies->ht_caps; + if (parsed_ies->ht_operation.present) + sme_join_rsp->ht_operation = parsed_ies->ht_operation; + if (parsed_ies->vht_operation.present) + sme_join_rsp->vht_operation = parsed_ies->vht_operation; + lim_add_he_info(parsed_ies, sme_join_rsp); +} + +#ifdef WLAN_FEATURE_FILS_SK +static void lim_update_fils_seq_num(struct join_rsp *sme_join_rsp, + struct pe_session *session_entry) +{ + sme_join_rsp->fils_seq_num = + session_entry->fils_info->sequence_number; +} +#else +static inline void lim_update_fils_seq_num(struct join_rsp *sme_join_rsp, + struct pe_session *session_entry) +{} +#endif + +void lim_send_sme_join_reassoc_rsp(struct mac_context *mac_ctx, + uint16_t msg_type, + tSirResultCodes result_code, + uint16_t prot_status_code, + struct pe_session *session_entry, + uint8_t vdev_id) +{ + struct join_rsp *sme_join_rsp; + uint32_t rsp_len; + tpDphHashNode sta_ds = NULL; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + if (msg_type == eWNI_SME_REASSOC_RSP) + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_REASSOC_RSP_EVENT, + session_entry, (uint16_t) result_code, 0); + else + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_JOIN_RSP_EVENT, + session_entry, (uint16_t) result_code, 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + pe_debug("Sending message: %s with reasonCode: %s", + lim_msg_str(msg_type), lim_result_code_str(result_code)); + + if (!session_entry) { + rsp_len = sizeof(*sme_join_rsp); + sme_join_rsp = qdf_mem_malloc(rsp_len); + if (!sme_join_rsp) + return; + + sme_join_rsp->beaconLength = 0; + sme_join_rsp->assocReqLength = 0; + sme_join_rsp->assocRspLength = 0; + } else { + rsp_len = session_entry->assocReqLen + + session_entry->assocRspLen + session_entry->bcnLen + + session_entry->RICDataLen + +#ifdef FEATURE_WLAN_ESE + session_entry->tspecLen + +#endif + sizeof(*sme_join_rsp) - sizeof(uint8_t); + sme_join_rsp = qdf_mem_malloc(rsp_len); + if (!sme_join_rsp) + return; + + if (lim_is_fils_connection(session_entry)) { + sme_join_rsp->is_fils_connection = true; + lim_update_fils_seq_num(sme_join_rsp, + session_entry); + } + + if (result_code == eSIR_SME_SUCCESS) { + sta_ds = dph_get_hash_entry(mac_ctx, + DPH_STA_HASH_INDEX_PEER, + &session_entry->dph.dphHashTable); + if (!sta_ds) { + pe_err("Get Self Sta Entry fail"); + } else { + sme_join_rsp->timingMeasCap = + sta_ds->timingMeasCap; +#ifdef FEATURE_WLAN_TDLS + sme_join_rsp->tdls_prohibited = + session_entry->tdls_prohibited; + sme_join_rsp->tdls_chan_swit_prohibited = + session_entry->tdls_chan_swit_prohibited; +#endif + sme_join_rsp->nss = sta_ds->nss; + sme_join_rsp->max_rate_flags = + lim_get_max_rate_flags(mac_ctx, sta_ds); + lim_add_bss_info(sta_ds, sme_join_rsp); + + /* Copy FILS params only for Successful join */ + populate_fils_connect_params(mac_ctx, + session_entry, sme_join_rsp); + } + } + + sme_join_rsp->beaconLength = 0; + sme_join_rsp->assocReqLength = 0; + sme_join_rsp->assocRspLength = 0; + sme_join_rsp->parsedRicRspLen = 0; +#ifdef FEATURE_WLAN_ESE + sme_join_rsp->tspecIeLen = 0; +#endif + lim_handle_join_rsp_status(mac_ctx, session_entry, result_code, + sme_join_rsp); + sme_join_rsp->uapsd_mask = session_entry->gUapsdPerAcBitmask; + /* Send supported NSS 1x1 to SME */ + sme_join_rsp->supported_nss_1x1 = + session_entry->supported_nss_1x1; + pe_debug("SME Join Rsp is supported NSS 1X1: %d", + sme_join_rsp->supported_nss_1x1); + } + + sme_join_rsp->messageType = msg_type; + sme_join_rsp->length = (uint16_t) rsp_len; + sme_join_rsp->status_code = result_code; + sme_join_rsp->protStatusCode = prot_status_code; + + sme_join_rsp->vdev_id = vdev_id; + + lim_send_sme_join_reassoc_rsp_after_resume(mac_ctx, QDF_STATUS_SUCCESS, + sme_join_rsp); +} + +void lim_send_sme_start_bss_rsp(struct mac_context *mac, + uint16_t msgType, + tSirResultCodes resultCode, + struct pe_session *pe_session, + uint8_t smesessionId) +{ + + uint16_t size = 0; + struct scheduler_msg mmhMsg = {0}; + struct start_bss_rsp *pSirSmeRsp; + uint16_t ieLen; + uint16_t ieOffset, curLen; + + pe_debug("Sending message: %s with reasonCode: %s", + lim_msg_str(msgType), lim_result_code_str(resultCode)); + + size = sizeof(struct start_bss_rsp); + + if (!pe_session) { + pSirSmeRsp = qdf_mem_malloc(size); + if (!pSirSmeRsp) + return; + } else { + /* subtract size of beaconLength + Mac Hdr + Fixed Fields before SSID */ + ieOffset = sizeof(tAniBeaconStruct) + SIR_MAC_B_PR_SSID_OFFSET; + ieLen = pe_session->schBeaconOffsetBegin + + pe_session->schBeaconOffsetEnd - ieOffset; + /* calculate the memory size to allocate */ + size += ieLen; + + pSirSmeRsp = qdf_mem_malloc(size); + if (!pSirSmeRsp) + return; + size = sizeof(struct start_bss_rsp); + if (resultCode == eSIR_SME_SUCCESS) { + + sir_copy_mac_addr(pSirSmeRsp->bssDescription.bssId, + pe_session->bssId); + + /* Read beacon interval from session */ + pSirSmeRsp->bssDescription.beaconInterval = + (uint16_t) pe_session->beaconParams. + beaconInterval; + pSirSmeRsp->bssType = pe_session->bssType; + + if (lim_get_capability_info + (mac, &pSirSmeRsp->bssDescription.capabilityInfo, + pe_session) + != QDF_STATUS_SUCCESS) + pe_err("could not retrieve Capabilities value"); + + lim_get_phy_mode(mac, + (uint32_t *) &pSirSmeRsp->bssDescription. + nwType, pe_session); + + pSirSmeRsp->bssDescription.chan_freq = + pe_session->curr_op_freq; + + if (!LIM_IS_NDI_ROLE(pe_session)) { + curLen = pe_session->schBeaconOffsetBegin - ieOffset; + qdf_mem_copy((uint8_t *) &pSirSmeRsp->bssDescription. + ieFields, + pe_session->pSchBeaconFrameBegin + + ieOffset, (uint32_t) curLen); + + qdf_mem_copy(((uint8_t *) &pSirSmeRsp->bssDescription. + ieFields) + curLen, + pe_session->pSchBeaconFrameEnd, + (uint32_t) pe_session-> + schBeaconOffsetEnd); + + pSirSmeRsp->bssDescription.length = (uint16_t) + (offsetof(struct bss_description, ieFields[0]) + - sizeof(pSirSmeRsp->bssDescription.length) + + ieLen); + /* This is the size of the message, subtracting the size of the pointer to ieFields */ + size += ieLen - sizeof(uint32_t); + } +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + if (pe_session->cc_switch_mode + != QDF_MCC_TO_SCC_SWITCH_DISABLE) { + pSirSmeRsp->ht_profile. + htSupportedChannelWidthSet = + pe_session->htSupportedChannelWidthSet; + pSirSmeRsp->ht_profile.htRecommendedTxWidthSet = + pe_session->htRecommendedTxWidthSet; + pSirSmeRsp->ht_profile.htSecondaryChannelOffset = + pe_session->htSecondaryChannelOffset; + pSirSmeRsp->ht_profile.dot11mode = + pe_session->dot11mode; + pSirSmeRsp->ht_profile.htCapability = + pe_session->htCapability; + pSirSmeRsp->ht_profile.vhtCapability = + pe_session->vhtCapability; + pSirSmeRsp->ht_profile.apCenterChan = + pe_session->ch_center_freq_seg0; + pSirSmeRsp->ht_profile.apChanWidth = + pe_session->ch_width; + } +#endif + } + } + pSirSmeRsp->messageType = msgType; + pSirSmeRsp->length = size; + pSirSmeRsp->sessionId = smesessionId; + pSirSmeRsp->status_code = resultCode; + + mmhMsg.type = msgType; + mmhMsg.bodyptr = pSirSmeRsp; + mmhMsg.bodyval = 0; + if (!pe_session) { + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + NO_SESSION, mmhMsg.type)); + } else { + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, mmhMsg.type)); + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_START_BSS_RSP_EVENT, + pe_session, (uint16_t) resultCode, 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + lim_sys_process_mmh_msg_api(mac, &mmhMsg); +} /*** end lim_send_sme_start_bss_rsp() ***/ + +void lim_send_sme_disassoc_deauth_ntf(struct mac_context *mac, + QDF_STATUS status, uint32_t *pCtx) +{ + struct scheduler_msg mmhMsg = {0}; + struct scheduler_msg *pMsg = (struct scheduler_msg *) pCtx; + + mmhMsg.type = pMsg->type; + mmhMsg.bodyptr = pMsg; + mmhMsg.bodyval = 0; + + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, NO_SESSION, mmhMsg.type)); + + lim_sys_process_mmh_msg_api(mac, &mmhMsg); +} + +void lim_send_sme_disassoc_ntf(struct mac_context *mac, + tSirMacAddr peerMacAddr, + tSirResultCodes reasonCode, + uint16_t disassocTrigger, + uint16_t aid, + uint8_t smesessionId, + struct pe_session *pe_session) +{ + struct disassoc_rsp *pSirSmeDisassocRsp; + struct disassoc_ind *pSirSmeDisassocInd; + uint32_t *pMsg = NULL; + bool failure = false; + struct pe_session *session = NULL; + uint16_t i, assoc_id; + tpDphHashNode sta_ds = NULL; + QDF_STATUS status; + + pe_debug("Disassoc Ntf with trigger : %d reasonCode: %d", + disassocTrigger, reasonCode); + + switch (disassocTrigger) { + case eLIM_DUPLICATE_ENTRY: + /* + * Duplicate entry is removed at LIM. + * Initiate new entry for other session + */ + pe_debug("Rcvd eLIM_DUPLICATE_ENTRY for " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peerMacAddr)); + + for (i = 0; i < mac->lim.maxBssId; i++) { + session = &mac->lim.gpSession[i]; + if (session->valid && + (session->opmode == QDF_SAP_MODE)) { + /* Find the sta ds entry in another session */ + sta_ds = dph_lookup_hash_entry(mac, + peerMacAddr, &assoc_id, + &session->dph.dphHashTable); + if (sta_ds) + break; + } + } + if (sta_ds) { + if (lim_add_sta(mac, sta_ds, false, session) != + QDF_STATUS_SUCCESS) + pe_err("could not Add STA with assocId: %d", + sta_ds->assocId); + } + status = lim_prepare_disconnect_done_ind(mac, &pMsg, + smesessionId, + reasonCode, + &peerMacAddr[0]); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("Failed to prepare message"); + return; + } + break; + + case eLIM_HOST_DISASSOC: + /** + * Disassociation response due to + * host triggered disassociation + */ + + pSirSmeDisassocRsp = qdf_mem_malloc(sizeof(struct disassoc_rsp)); + if (!pSirSmeDisassocRsp) { + failure = true; + goto error; + } + pe_debug("send eWNI_SME_DISASSOC_RSP with retCode: %d for " + QDF_MAC_ADDR_FMT, + reasonCode, QDF_MAC_ADDR_REF(peerMacAddr)); + pSirSmeDisassocRsp->messageType = eWNI_SME_DISASSOC_RSP; + pSirSmeDisassocRsp->length = sizeof(struct disassoc_rsp); + pSirSmeDisassocRsp->sessionId = smesessionId; + pSirSmeDisassocRsp->status_code = reasonCode; + qdf_mem_copy(pSirSmeDisassocRsp->peer_macaddr.bytes, + peerMacAddr, sizeof(tSirMacAddr)); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + + lim_diag_event_report(mac, WLAN_PE_DIAG_DISASSOC_RSP_EVENT, + pe_session, (uint16_t) reasonCode, 0); +#endif + pMsg = (uint32_t *) pSirSmeDisassocRsp; + break; + + case eLIM_PEER_ENTITY_DISASSOC: + case eLIM_LINK_MONITORING_DISASSOC: + status = lim_prepare_disconnect_done_ind(mac, &pMsg, + smesessionId, + reasonCode, &peerMacAddr[0]); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("Failed to prepare message"); + return; + } + break; + + default: + /** + * Disassociation indication due to Disassociation + * frame reception from peer entity or due to + * loss of link with peer entity. + */ + pSirSmeDisassocInd = + qdf_mem_malloc(sizeof(*pSirSmeDisassocInd)); + if (!pSirSmeDisassocInd) { + failure = true; + goto error; + } + pe_debug("send eWNI_SME_DISASSOC_IND with retCode: %d for " + QDF_MAC_ADDR_FMT, + reasonCode, QDF_MAC_ADDR_REF(peerMacAddr)); + pSirSmeDisassocInd->messageType = eWNI_SME_DISASSOC_IND; + pSirSmeDisassocInd->length = sizeof(*pSirSmeDisassocInd); + pSirSmeDisassocInd->vdev_id = smesessionId; + pSirSmeDisassocInd->reasonCode = reasonCode; + pSirSmeDisassocInd->status_code = reasonCode; + qdf_mem_copy(pSirSmeDisassocInd->bssid.bytes, + pe_session->bssId, sizeof(tSirMacAddr)); + qdf_mem_copy(pSirSmeDisassocInd->peer_macaddr.bytes, + peerMacAddr, sizeof(tSirMacAddr)); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_DISASSOC_IND_EVENT, + pe_session, (uint16_t) reasonCode, 0); +#endif + pMsg = (uint32_t *) pSirSmeDisassocInd; + + break; + } + +error: + /* Delete the PE session Created */ + if ((pe_session) && LIM_IS_STA_ROLE(pe_session)) + pe_delete_session(mac, pe_session); + + if (false == failure) + lim_send_sme_disassoc_deauth_ntf(mac, QDF_STATUS_SUCCESS, + (uint32_t *) pMsg); +} /*** end lim_send_sme_disassoc_ntf() ***/ + +static bool lim_is_disconnect_from_ap(enum eLimDisassocTrigger trigger) +{ + if (trigger == eLIM_PEER_ENTITY_DEAUTH || + trigger == eLIM_PEER_ENTITY_DISASSOC) + return true; + + return false; +} +/** ----------------------------------------------------------------- + \brief lim_send_sme_disassoc_ind() - sends SME_DISASSOC_IND + + After receiving disassociation frame from peer entity, this + function sends a eWNI_SME_DISASSOC_IND to SME with a specific + reason code. + + \param mac - global mac structure + \param sta - station dph hash node + \return none + \sa + ----------------------------------------------------------------- */ +void +lim_send_sme_disassoc_ind(struct mac_context *mac, tpDphHashNode sta, + struct pe_session *pe_session) +{ + struct scheduler_msg mmhMsg = {0}; + struct disassoc_ind *pSirSmeDisassocInd; + + pSirSmeDisassocInd = qdf_mem_malloc(sizeof(*pSirSmeDisassocInd)); + if (!pSirSmeDisassocInd) + return; + + pSirSmeDisassocInd->messageType = eWNI_SME_DISASSOC_IND; + pSirSmeDisassocInd->length = sizeof(*pSirSmeDisassocInd); + + pSirSmeDisassocInd->vdev_id = pe_session->smeSessionId; + pSirSmeDisassocInd->status_code = eSIR_SME_DEAUTH_STATUS; + pSirSmeDisassocInd->reasonCode = sta->mlmStaContext.disassocReason; + + qdf_mem_copy(pSirSmeDisassocInd->bssid.bytes, pe_session->bssId, + QDF_MAC_ADDR_SIZE); + + qdf_mem_copy(pSirSmeDisassocInd->peer_macaddr.bytes, sta->staAddr, + QDF_MAC_ADDR_SIZE); + + if (LIM_IS_STA_ROLE(pe_session)) + pSirSmeDisassocInd->from_ap = + lim_is_disconnect_from_ap(sta->mlmStaContext.cleanupTrigger); + + mmhMsg.type = eWNI_SME_DISASSOC_IND; + mmhMsg.bodyptr = pSirSmeDisassocInd; + mmhMsg.bodyval = 0; + + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, mmhMsg.type)); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_DISASSOC_IND_EVENT, pe_session, + 0, (uint16_t) sta->mlmStaContext.disassocReason); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + +} /*** end lim_send_sme_disassoc_ind() ***/ + +/** ----------------------------------------------------------------- + \brief lim_send_sme_deauth_ind() - sends SME_DEAUTH_IND + + After receiving deauthentication frame from peer entity, this + function sends a eWNI_SME_DEAUTH_IND to SME with a specific + reason code. + + \param mac - global mac structure + \param sta - station dph hash node + \return none + \sa + ----------------------------------------------------------------- */ +void +lim_send_sme_deauth_ind(struct mac_context *mac, tpDphHashNode sta, + struct pe_session *pe_session) +{ + struct scheduler_msg mmhMsg = {0}; + struct deauth_ind *pSirSmeDeauthInd; + + pSirSmeDeauthInd = qdf_mem_malloc(sizeof(*pSirSmeDeauthInd)); + if (!pSirSmeDeauthInd) + return; + + pSirSmeDeauthInd->messageType = eWNI_SME_DEAUTH_IND; + pSirSmeDeauthInd->length = sizeof(*pSirSmeDeauthInd); + + pSirSmeDeauthInd->vdev_id = pe_session->smeSessionId; + if (eSIR_INFRA_AP_MODE == pe_session->bssType) { + pSirSmeDeauthInd->status_code = + (tSirResultCodes) sta->mlmStaContext.cleanupTrigger; + } else { + /* Need to indicate the reason code over the air */ + pSirSmeDeauthInd->status_code = + (tSirResultCodes) sta->mlmStaContext.disassocReason; + } + /* BSSID */ + qdf_mem_copy(pSirSmeDeauthInd->bssid.bytes, pe_session->bssId, + QDF_MAC_ADDR_SIZE); + /* peerMacAddr */ + qdf_mem_copy(pSirSmeDeauthInd->peer_macaddr.bytes, sta->staAddr, + QDF_MAC_ADDR_SIZE); + pSirSmeDeauthInd->reasonCode = sta->mlmStaContext.disassocReason; + + if (eSIR_MAC_PEER_STA_REQ_LEAVING_BSS_REASON == + sta->mlmStaContext.disassocReason) + pSirSmeDeauthInd->rssi = sta->del_sta_ctx_rssi; + + if (LIM_IS_STA_ROLE(pe_session)) + pSirSmeDeauthInd->from_ap = + lim_is_disconnect_from_ap(sta->mlmStaContext.cleanupTrigger); + + mmhMsg.type = eWNI_SME_DEAUTH_IND; + mmhMsg.bodyptr = pSirSmeDeauthInd; + mmhMsg.bodyval = 0; + + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, mmhMsg.type)); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_DEAUTH_IND_EVENT, pe_session, + 0, sta->mlmStaContext.cleanupTrigger); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + return; +} /*** end lim_send_sme_deauth_ind() ***/ + +#ifdef FEATURE_WLAN_TDLS +/** + * lim_send_sme_tdls_del_sta_ind() + * + ***FUNCTION: + * This function is called to send the TDLS STA context deletion to SME. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * NA + * + * @param mac - Pointer to global MAC structure + * @param sta - Pointer to internal STA Datastructure + * @param pe_session - Pointer to the session entry + * @param reasonCode - Reason for TDLS sta deletion + * @return None + */ +void +lim_send_sme_tdls_del_sta_ind(struct mac_context *mac, tpDphHashNode sta, + struct pe_session *pe_session, uint16_t reasonCode) +{ + struct tdls_event_info info; + + pe_debug("Delete TDLS Peer "QDF_MAC_ADDR_FMT "with reason code: %d", + QDF_MAC_ADDR_REF(sta->staAddr), reasonCode); + info.vdev_id = pe_session->smeSessionId; + qdf_mem_copy(info.peermac.bytes, sta->staAddr, QDF_MAC_ADDR_SIZE); + info.message_type = TDLS_PEER_DISCONNECTED; + info.peer_reason = TDLS_DISCONNECTED_PEER_DELETE; + + tgt_tdls_event_handler(mac->psoc, &info); + + return; +} /*** end lim_send_sme_tdls_del_sta_ind() ***/ + +/** + * lim_send_sme_mgmt_tx_completion() + * + ***FUNCTION: + * This function is called to send the eWNI_SME_MGMT_FRM_TX_COMPLETION_IND + * message to SME. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * NA + * + * @param mac - Pointer to global MAC structure + * @param pe_session - Pointer to the session entry + * @param txCompleteStatus - TX Complete Status of Mgmt Frames + * @return None + */ +void +lim_send_sme_mgmt_tx_completion(struct mac_context *mac, + uint32_t vdev_id, + uint32_t txCompleteStatus) +{ + struct scheduler_msg msg = {0}; + struct tdls_mgmt_tx_completion_ind *mgmt_tx_completion_ind; + QDF_STATUS status; + + mgmt_tx_completion_ind = + qdf_mem_malloc(sizeof(*mgmt_tx_completion_ind)); + if (!mgmt_tx_completion_ind) + return; + + /* sessionId */ + mgmt_tx_completion_ind->vdev_id = vdev_id; + + mgmt_tx_completion_ind->tx_complete_status = txCompleteStatus; + + msg.type = eWNI_SME_MGMT_FRM_TX_COMPLETION_IND; + msg.bodyptr = mgmt_tx_completion_ind; + msg.bodyval = 0; + + mgmt_tx_completion_ind->psoc = mac->psoc; + msg.callback = tgt_tdls_send_mgmt_tx_completion; + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("post msg fail, %d", status); + qdf_mem_free(mgmt_tx_completion_ind); + } +} /*** end lim_send_sme_tdls_delete_all_peer_ind() ***/ + +#endif /* FEATURE_WLAN_TDLS */ + +QDF_STATUS lim_prepare_disconnect_done_ind(struct mac_context *mac_ctx, + uint32_t **msg, + uint8_t session_id, + tSirResultCodes reason_code, + uint8_t *peer_mac_addr) +{ + struct sir_sme_discon_done_ind *sir_sme_dis_ind; + + sir_sme_dis_ind = qdf_mem_malloc(sizeof(*sir_sme_dis_ind)); + if (!sir_sme_dis_ind) + return QDF_STATUS_E_FAILURE; + + pe_debug("Prepare eWNI_SME_DISCONNECT_DONE_IND withretCode: %d", + reason_code); + + sir_sme_dis_ind->message_type = eWNI_SME_DISCONNECT_DONE_IND; + sir_sme_dis_ind->length = sizeof(*sir_sme_dis_ind); + sir_sme_dis_ind->session_id = session_id; + if (peer_mac_addr) + qdf_mem_copy(sir_sme_dis_ind->peer_mac, + peer_mac_addr, ETH_ALEN); + + /* + * Instead of sending deauth reason code as 505 which is + * internal value(eSIR_SME_LOST_LINK_WITH_PEER_RESULT_CODE) + * Send reason code as zero to Supplicant + */ + if (reason_code == eSIR_SME_LOST_LINK_WITH_PEER_RESULT_CODE) + sir_sme_dis_ind->reason_code = 0; + else + sir_sme_dis_ind->reason_code = reason_code; + + *msg = (uint32_t *)sir_sme_dis_ind; + + return QDF_STATUS_SUCCESS; +} + +void lim_send_sme_deauth_ntf(struct mac_context *mac, tSirMacAddr peerMacAddr, + tSirResultCodes reasonCode, uint16_t deauthTrigger, + uint16_t aid, uint8_t smesessionId) +{ + uint8_t *pBuf; + struct deauth_rsp *pSirSmeDeauthRsp; + struct deauth_ind *pSirSmeDeauthInd; + struct pe_session *pe_session; + uint8_t sessionId; + uint32_t *pMsg = NULL; + QDF_STATUS status; + + pe_session = pe_find_session_by_bssid(mac, peerMacAddr, &sessionId); + switch (deauthTrigger) { + case eLIM_HOST_DEAUTH: + /** + * Deauthentication response to host triggered + * deauthentication. + */ + pSirSmeDeauthRsp = qdf_mem_malloc(sizeof(*pSirSmeDeauthRsp)); + if (!pSirSmeDeauthRsp) + return; + pe_debug("send eWNI_SME_DEAUTH_RSP with retCode: %d for " + QDF_MAC_ADDR_FMT, + reasonCode, QDF_MAC_ADDR_REF(peerMacAddr)); + pSirSmeDeauthRsp->messageType = eWNI_SME_DEAUTH_RSP; + pSirSmeDeauthRsp->length = sizeof(*pSirSmeDeauthRsp); + pSirSmeDeauthRsp->status_code = reasonCode; + pSirSmeDeauthRsp->sessionId = smesessionId; + + pBuf = (uint8_t *) pSirSmeDeauthRsp->peer_macaddr.bytes; + qdf_mem_copy(pBuf, peerMacAddr, sizeof(tSirMacAddr)); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_DEAUTH_RSP_EVENT, + pe_session, 0, (uint16_t) reasonCode); +#endif + pMsg = (uint32_t *) pSirSmeDeauthRsp; + + break; + + case eLIM_PEER_ENTITY_DEAUTH: + case eLIM_LINK_MONITORING_DEAUTH: + status = lim_prepare_disconnect_done_ind(mac, &pMsg, + smesessionId, reasonCode, + &peerMacAddr[0]); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("Failed to prepare message"); + return; + } + break; + default: + /** + * Deauthentication indication due to Deauthentication + * frame reception from peer entity or due to + * loss of link with peer entity. + */ + pSirSmeDeauthInd = qdf_mem_malloc(sizeof(*pSirSmeDeauthInd)); + if (!pSirSmeDeauthInd) + return; + pe_debug("send eWNI_SME_DEAUTH_IND with retCode: %d for " + QDF_MAC_ADDR_FMT, + reasonCode, QDF_MAC_ADDR_REF(peerMacAddr)); + pSirSmeDeauthInd->messageType = eWNI_SME_DEAUTH_IND; + pSirSmeDeauthInd->length = sizeof(*pSirSmeDeauthInd); + pSirSmeDeauthInd->reasonCode = eSIR_MAC_UNSPEC_FAILURE_REASON; + pSirSmeDeauthInd->vdev_id = smesessionId; + pSirSmeDeauthInd->status_code = reasonCode; + qdf_mem_copy(pSirSmeDeauthInd->bssid.bytes, pe_session->bssId, + sizeof(tSirMacAddr)); + qdf_mem_copy(pSirSmeDeauthInd->peer_macaddr.bytes, peerMacAddr, + QDF_MAC_ADDR_SIZE); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_DEAUTH_IND_EVENT, + pe_session, 0, (uint16_t) reasonCode); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + pMsg = (uint32_t *) pSirSmeDeauthInd; + + break; + } + + /*Delete the PE session created */ + if (pe_session && LIM_IS_STA_ROLE(pe_session)) + pe_delete_session(mac, pe_session); + + lim_send_sme_disassoc_deauth_ntf(mac, QDF_STATUS_SUCCESS, + (uint32_t *) pMsg); + +} /*** end lim_send_sme_deauth_ntf() ***/ + +/** + * lim_send_sme_wm_status_change_ntf() - Send Notification + * @mac_ctx: Global MAC Context + * @status_change_code: Indicates the change in the wireless medium. + * @status_change_info: Indicates the information associated with + * change in the wireless medium. + * @info_len: Indicates the length of status change information + * being sent. + * @session_id SessionID + * + * This function is called by limProcessSmeMessages() to send + * eWNI_SME_WM_STATUS_CHANGE_NTF message to host. + * + * Return: None + */ +void +lim_send_sme_wm_status_change_ntf(struct mac_context *mac_ctx, + tSirSmeStatusChangeCode status_change_code, + uint32_t *status_change_info, uint16_t info_len, uint8_t session_id) +{ + struct scheduler_msg msg = {0}; + struct wm_status_change_ntf *wm_status_change_ntf; + uint32_t max_info_len; + + wm_status_change_ntf = qdf_mem_malloc(sizeof(*wm_status_change_ntf)); + if (!wm_status_change_ntf) + return; + + msg.type = eWNI_SME_WM_STATUS_CHANGE_NTF; + msg.bodyval = 0; + msg.bodyptr = wm_status_change_ntf; + + switch (status_change_code) { + case eSIR_SME_AP_CAPS_CHANGED: + max_info_len = sizeof(struct ap_new_caps); + break; + case eSIR_SME_JOINED_NEW_BSS: + max_info_len = sizeof(struct new_bss_info); + break; + default: + max_info_len = sizeof(wm_status_change_ntf->statusChangeInfo); + break; + } + + switch (status_change_code) { + case eSIR_SME_RADAR_DETECTED: + break; + default: + wm_status_change_ntf->messageType = + eWNI_SME_WM_STATUS_CHANGE_NTF; + wm_status_change_ntf->statusChangeCode = status_change_code; + wm_status_change_ntf->length = sizeof(*wm_status_change_ntf); + wm_status_change_ntf->sessionId = session_id; + if (info_len <= max_info_len && status_change_info) { + qdf_mem_copy( + (uint8_t *) &wm_status_change_ntf->statusChangeInfo, + (uint8_t *) status_change_info, info_len); + } + pe_debug("StatusChg code: 0x%x length: %d", + status_change_code, info_len); + break; + } + + MTRACE(mac_trace(mac_ctx, TRACE_CODE_TX_SME_MSG, session_id, msg.type)); + lim_sys_process_mmh_msg_api(mac_ctx, &msg); + +} /*** end lim_send_sme_wm_status_change_ntf() ***/ + +void lim_send_sme_set_context_rsp(struct mac_context *mac, + struct qdf_mac_addr peer_macaddr, + uint16_t aid, + tSirResultCodes resultCode, + struct pe_session *pe_session, + uint8_t smesessionId) +{ + struct scheduler_msg mmhMsg = {0}; + struct set_context_rsp *set_context_rsp; + + set_context_rsp = qdf_mem_malloc(sizeof(*set_context_rsp)); + if (!set_context_rsp) + return; + + set_context_rsp->messageType = eWNI_SME_SETCONTEXT_RSP; + set_context_rsp->length = sizeof(*set_context_rsp); + set_context_rsp->status_code = resultCode; + + qdf_copy_macaddr(&set_context_rsp->peer_macaddr, &peer_macaddr); + + set_context_rsp->sessionId = smesessionId; + + mmhMsg.type = eWNI_SME_SETCONTEXT_RSP; + mmhMsg.bodyptr = set_context_rsp; + mmhMsg.bodyval = 0; + if (!pe_session) { + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + NO_SESSION, mmhMsg.type)); + } else { + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, mmhMsg.type)); + } + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_SETCONTEXT_RSP_EVENT, + pe_session, (uint16_t) resultCode, 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + mac->lim.sme_msg_callback(mac, &mmhMsg); +} /*** end lim_send_sme_set_context_rsp() ***/ + +void lim_send_sme_addts_rsp(struct mac_context *mac, + uint8_t rspReqd, uint32_t status, + struct pe_session *pe_session, + struct mac_tspec_ie tspec, + uint8_t smesessionId) +{ + tpSirAddtsRsp rsp; + struct scheduler_msg mmhMsg = {0}; + + if (!rspReqd) + return; + + rsp = qdf_mem_malloc(sizeof(tSirAddtsRsp)); + if (!rsp) + return; + + rsp->messageType = eWNI_SME_ADDTS_RSP; + rsp->rc = status; + rsp->rsp.status = (enum mac_status_code)status; + rsp->rsp.tspec = tspec; + rsp->sessionId = smesessionId; + + mmhMsg.type = eWNI_SME_ADDTS_RSP; + mmhMsg.bodyptr = rsp; + mmhMsg.bodyval = 0; + if (!pe_session) { + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + NO_SESSION, mmhMsg.type)); + } else { + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, mmhMsg.type)); + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_ADDTS_RSP_EVENT, pe_session, 0, + 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + return; +} + +void lim_send_sme_delts_rsp(struct mac_context *mac, tpSirDeltsReq delts, + uint32_t status, struct pe_session *pe_session, + uint8_t smesessionId) +{ + tpSirDeltsRsp rsp; + struct scheduler_msg mmhMsg = {0}; + + pe_debug("SendSmeDeltsRsp aid: %d tsid: %d up: %d status: %d", + delts->aid, + delts->req.tsinfo.traffic.tsid, + delts->req.tsinfo.traffic.userPrio, status); + if (!delts->rspReqd) + return; + + rsp = qdf_mem_malloc(sizeof(tSirDeltsRsp)); + if (!rsp) + return; + + if (pe_session) { + + rsp->aid = delts->aid; + qdf_copy_macaddr(&rsp->macaddr, &delts->macaddr); + qdf_mem_copy((uint8_t *) &rsp->rsp, (uint8_t *) &delts->req, + sizeof(struct delts_req_info)); + } + + rsp->messageType = eWNI_SME_DELTS_RSP; + rsp->rc = status; + rsp->sessionId = smesessionId; + + mmhMsg.type = eWNI_SME_DELTS_RSP; + mmhMsg.bodyptr = rsp; + mmhMsg.bodyval = 0; + if (!pe_session) { + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + NO_SESSION, mmhMsg.type)); + } else { + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, mmhMsg.type)); + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_DELTS_RSP_EVENT, pe_session, + (uint16_t) status, 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + lim_sys_process_mmh_msg_api(mac, &mmhMsg); +} + +void +lim_send_sme_delts_ind(struct mac_context *mac, struct delts_req_info *delts, + uint16_t aid, struct pe_session *pe_session) +{ + tpSirDeltsRsp rsp; + struct scheduler_msg mmhMsg = {0}; + + pe_debug("SendSmeDeltsInd aid: %d tsid: %d up: %d", + aid, delts->tsinfo.traffic.tsid, delts->tsinfo.traffic.userPrio); + + rsp = qdf_mem_malloc(sizeof(tSirDeltsRsp)); + if (!rsp) + return; + + rsp->messageType = eWNI_SME_DELTS_IND; + rsp->rc = QDF_STATUS_SUCCESS; + rsp->aid = aid; + qdf_mem_copy((uint8_t *) &rsp->rsp, (uint8_t *) delts, sizeof(*delts)); + rsp->sessionId = pe_session->smeSessionId; + + mmhMsg.type = eWNI_SME_DELTS_IND; + mmhMsg.bodyptr = rsp; + mmhMsg.bodyval = 0; + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, mmhMsg.type)); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */ + lim_diag_event_report(mac, WLAN_PE_DIAG_DELTS_IND_EVENT, pe_session, 0, + 0); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + lim_sys_process_mmh_msg_api(mac, &mmhMsg); +} + +#ifdef FEATURE_WLAN_ESE +/** + * lim_send_sme_pe_ese_tsm_rsp() - send tsm response + * @mac: Pointer to global mac structure + * @pStats: Pointer to TSM Stats + * + * This function is called to send tsm stats response to HDD. + * This function posts the result back to HDD. This is a response to + * HDD's request to get tsm stats. + * + * Return: None + */ +void lim_send_sme_pe_ese_tsm_rsp(struct mac_context *mac, + tAniGetTsmStatsRsp *pStats) +{ + struct scheduler_msg mmhMsg = {0}; + uint8_t sessionId; + tAniGetTsmStatsRsp *pPeStats = (tAniGetTsmStatsRsp *) pStats; + struct pe_session *pPeSessionEntry = NULL; + + /* Get the Session Id based on Sta Id */ + pPeSessionEntry = pe_find_session_by_bssid(mac, pPeStats->bssid.bytes, + &sessionId); + /* Fill the Session Id */ + if (pPeSessionEntry) { + /* Fill the Session Id */ + pPeStats->sessionId = pPeSessionEntry->smeSessionId; + } else { + pe_err("Session not found for the Sta peer:" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pPeStats->bssid.bytes)); + qdf_mem_free(pPeStats->tsmStatsReq); + qdf_mem_free(pPeStats); + return; + } + + pPeStats->msgType = eWNI_SME_GET_TSM_STATS_RSP; + pPeStats->tsmMetrics.RoamingCount + = pPeSessionEntry->eseContext.tsm.tsmMetrics.RoamingCount; + pPeStats->tsmMetrics.RoamingDly + = pPeSessionEntry->eseContext.tsm.tsmMetrics.RoamingDly; + + mmhMsg.type = eWNI_SME_GET_TSM_STATS_RSP; + mmhMsg.bodyptr = pStats; + mmhMsg.bodyval = 0; + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, sessionId, mmhMsg.type)); + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + + return; +} /*** end lim_send_sme_pe_ese_tsm_rsp() ***/ + +#endif /* FEATURE_WLAN_ESE */ + +#ifdef QCA_IBSS_SUPPORT +void +lim_send_sme_ibss_peer_ind(struct mac_context *mac, + tSirMacAddr peerMacAddr, + uint8_t *beacon, + uint16_t beaconLen, uint16_t msgType, uint8_t sessionId) +{ + struct scheduler_msg mmhMsg = {0}; + tSmeIbssPeerInd *pNewPeerInd; + + pNewPeerInd = qdf_mem_malloc(sizeof(tSmeIbssPeerInd) + beaconLen); + if (!pNewPeerInd) + return; + + qdf_mem_copy((uint8_t *) pNewPeerInd->peer_addr.bytes, + peerMacAddr, QDF_MAC_ADDR_SIZE); + pNewPeerInd->mesgLen = sizeof(tSmeIbssPeerInd) + beaconLen; + pNewPeerInd->mesgType = msgType; + pNewPeerInd->sessionId = sessionId; + + if (beacon) { + qdf_mem_copy((void *)((uint8_t *) pNewPeerInd + + sizeof(tSmeIbssPeerInd)), (void *)beacon, + beaconLen); + } + + mmhMsg.type = msgType; + mmhMsg.bodyptr = pNewPeerInd; + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, sessionId, mmhMsg.type)); + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + +} +#endif + +/** + * lim_process_csa_wbw_ie() - Process CSA Wide BW IE + * @mac_ctx: pointer to global adapter context + * @csa_params: pointer to CSA parameters + * @chnl_switch_info:pointer to channel switch parameters + * @session_entry: session pointer + * + * Return: None + */ +static QDF_STATUS lim_process_csa_wbw_ie(struct mac_context *mac_ctx, + struct csa_offload_params *csa_params, + tLimWiderBWChannelSwitchInfo *chnl_switch_info, + struct pe_session *session_entry) +{ + struct ch_params ch_params = {0}; + uint8_t ap_new_ch_width; + bool new_ch_width_dfn = false; + uint8_t center_freq_diff; + uint32_t fw_vht_ch_wd = wma_get_vht_ch_width() + 1; + + ap_new_ch_width = csa_params->new_ch_width + 1; + + if ((ap_new_ch_width != CH_WIDTH_80MHZ) && + (ap_new_ch_width != CH_WIDTH_160MHZ) && + (ap_new_ch_width != CH_WIDTH_80P80MHZ)) { + pe_err("CSA wide BW IE has wrong ch_width %d", + csa_params->new_ch_width); + return QDF_STATUS_E_INVAL; + } + + if (!csa_params->new_ch_freq_seg1 && !csa_params->new_ch_freq_seg2) { + pe_err("CSA wide BW IE has invalid center freq"); + return QDF_STATUS_E_INVAL; + } + if ((ap_new_ch_width == CH_WIDTH_80MHZ) && + csa_params->new_ch_freq_seg2) { + new_ch_width_dfn = true; + if (csa_params->new_ch_freq_seg2 > + csa_params->new_ch_freq_seg1) + center_freq_diff = csa_params->new_ch_freq_seg2 - + csa_params->new_ch_freq_seg1; + else + center_freq_diff = csa_params->new_ch_freq_seg1 - + csa_params->new_ch_freq_seg2; + if (center_freq_diff == CENTER_FREQ_DIFF_160MHz) + ap_new_ch_width = CH_WIDTH_160MHZ; + else if (center_freq_diff > CENTER_FREQ_DIFF_80P80MHz) + ap_new_ch_width = CH_WIDTH_80P80MHZ; + else + ap_new_ch_width = CH_WIDTH_80MHZ; + } + session_entry->gLimChannelSwitch.state = + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY; + if ((ap_new_ch_width == CH_WIDTH_160MHZ) && + !new_ch_width_dfn) { + if (csa_params->new_ch_freq_seg1 != csa_params->channel + + CH_TO_CNTR_FREQ_DIFF_160MHz) { + pe_err("CSA wide BW IE has invalid center freq"); + return QDF_STATUS_E_INVAL; + } + + if (ap_new_ch_width > fw_vht_ch_wd) { + pe_debug("New BW is not supported, setting BW to %d", + fw_vht_ch_wd); + ap_new_ch_width = fw_vht_ch_wd; + } + ch_params.ch_width = ap_new_ch_width ; + wlan_reg_set_channel_params(mac_ctx->pdev, + csa_params->channel, 0, &ch_params); + ap_new_ch_width = ch_params.ch_width; + csa_params->new_ch_freq_seg1 = ch_params.center_freq_seg0; + csa_params->new_ch_freq_seg2 = ch_params.center_freq_seg1; + } else if (!new_ch_width_dfn) { + if (ap_new_ch_width > fw_vht_ch_wd) { + pe_debug("New BW is not supported, setting BW to %d", + fw_vht_ch_wd); + ap_new_ch_width = fw_vht_ch_wd; + } + if (csa_params->new_ch_freq_seg1 != csa_params->channel + + CH_TO_CNTR_FREQ_DIFF_80MHz) { + pe_err("CSA wide BW IE has invalid center freq"); + return QDF_STATUS_E_INVAL; + } + csa_params->new_ch_freq_seg2 = 0; + } + if (new_ch_width_dfn) { + if (csa_params->new_ch_freq_seg1 != csa_params->channel + + CH_TO_CNTR_FREQ_DIFF_80MHz) { + pe_err("CSA wide BW IE has invalid center freq"); + return QDF_STATUS_E_INVAL; + } + if (ap_new_ch_width > fw_vht_ch_wd) { + pe_debug("New width is not supported, setting BW to %d", + fw_vht_ch_wd); + ap_new_ch_width = fw_vht_ch_wd; + } + if ((ap_new_ch_width == CH_WIDTH_160MHZ) && + (csa_params->new_ch_freq_seg1 != + csa_params->channel + + CH_TO_CNTR_FREQ_DIFF_160MHz)) { + pe_err("wide BW IE has invalid 160M center freq"); + csa_params->new_ch_freq_seg2 = 0; + ap_new_ch_width = CH_WIDTH_80MHZ; + } + } + chnl_switch_info->newChanWidth = ap_new_ch_width; + chnl_switch_info->newCenterChanFreq0 = csa_params->new_ch_freq_seg1; + chnl_switch_info->newCenterChanFreq1 = csa_params->new_ch_freq_seg2; + + if (session_entry->ch_width == ap_new_ch_width) + goto prnt_log; + + if (session_entry->ch_width == CH_WIDTH_80MHZ) { + chnl_switch_info->newChanWidth = CH_WIDTH_80MHZ; + chnl_switch_info->newCenterChanFreq1 = 0; + } else { + session_entry->ch_width = ap_new_ch_width; + chnl_switch_info->newChanWidth = ap_new_ch_width; + } +prnt_log: + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_handle_csa_offload_msg() - Handle CSA offload message + * @mac_ctx: pointer to global adapter context + * @msg: Message pointer. + * + * Return: None + */ +void lim_handle_csa_offload_msg(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + struct pe_session *session_entry; + struct csa_offload_params *csa_params = + (struct csa_offload_params *) (msg->bodyptr); + tpDphHashNode sta_ds = NULL; + uint8_t session_id; + uint16_t aid = 0; + uint16_t chan_space = 0; + struct ch_params ch_params = {0}; + uint32_t channel_bonding_mode; + + tLimWiderBWChannelSwitchInfo *chnl_switch_info = NULL; + tLimChannelSwitchInfo *lim_ch_switch = NULL; + + if (!csa_params) { + pe_err("limMsgQ body ptr is NULL"); + return; + } + + session_entry = + pe_find_session_by_bssid(mac_ctx, + csa_params->bssId, &session_id); + if (!session_entry) { + pe_err("Session does not exists for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(csa_params->bssId)); + goto err; + } + + sta_ds = dph_lookup_hash_entry(mac_ctx, session_entry->bssId, &aid, + &session_entry->dph.dphHashTable); + + if (!sta_ds) { + pe_err("sta_ds does not exist"); + goto err; + } + + if (!LIM_IS_STA_ROLE(session_entry)) { + pe_debug("Invalid role to handle CSA"); + goto err; + } + /* + * on receiving channel switch announcement from AP, delete all + * TDLS peers before leaving BSS and proceed for channel switch + */ + + lim_update_tdls_set_state_for_fw(session_entry, false); + lim_delete_tdls_peers(mac_ctx, session_entry); + + lim_ch_switch = &session_entry->gLimChannelSwitch; + session_entry->gLimChannelSwitch.switchMode = + csa_params->switch_mode; + /* timer already started by firmware, switch immediately */ + session_entry->gLimChannelSwitch.switchCount = 0; + session_entry->gLimChannelSwitch.primaryChannel = + csa_params->channel; + session_entry->gLimChannelSwitch.sw_target_freq = + csa_params->csa_chan_freq; + session_entry->gLimChannelSwitch.state = + eLIM_CHANNEL_SWITCH_PRIMARY_ONLY; + session_entry->gLimChannelSwitch.ch_width = CH_WIDTH_20MHZ; + lim_ch_switch->sec_ch_offset = + session_entry->htSecondaryChannelOffset; + session_entry->gLimChannelSwitch.ch_center_freq_seg0 = 0; + session_entry->gLimChannelSwitch.ch_center_freq_seg1 = 0; + chnl_switch_info = + &session_entry->gLimWiderBWChannelSwitch; + + if (WLAN_REG_IS_24GHZ_CH_FREQ(csa_params->csa_chan_freq)) { + channel_bonding_mode = + mac_ctx->roam.configParam.channelBondingMode24GHz; + } else { + channel_bonding_mode = + mac_ctx->roam.configParam.channelBondingMode5GHz; + } + + pe_debug("Session %d vdev %d: vht: %d ht: %d he %d cbmode %d", + session_entry->peSessionId, session_entry->vdev_id, + session_entry->vhtCapability, + session_entry->htSupportedChannelWidthSet, + lim_is_session_he_capable(session_entry), + channel_bonding_mode); + + session_entry->htSupportedChannelWidthSet = false; + + if (channel_bonding_mode && + ((session_entry->vhtCapability && session_entry->htCapability) || + lim_is_session_he_capable(session_entry))) { + if ((csa_params->ies_present_flag & lim_wbw_ie_present) && + (QDF_STATUS_SUCCESS == lim_process_csa_wbw_ie(mac_ctx, + csa_params, chnl_switch_info, + session_entry))) { + lim_ch_switch->sec_ch_offset = + PHY_SINGLE_CHANNEL_CENTERED; + if (chnl_switch_info->newChanWidth) { + if (csa_params->channel < + csa_params->new_ch_freq_seg1) + lim_ch_switch->sec_ch_offset = + PHY_DOUBLE_CHANNEL_LOW_PRIMARY; + else + lim_ch_switch->sec_ch_offset = + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY; + session_entry->htSupportedChannelWidthSet = + true; + } + } else if (csa_params->ies_present_flag + & lim_xcsa_ie_present) { + uint32_t fw_vht_ch_wd = wma_get_vht_ch_width(); + + if (wlan_reg_is_6ghz_op_class + (mac_ctx->pdev, csa_params->new_op_class)) { + chan_space = wlan_reg_get_op_class_width + (mac_ctx->pdev, + csa_params->new_op_class, true); + } else { + chan_space = + wlan_reg_dmn_get_chanwidth_from_opclass( + mac_ctx->scan.countryCodeCurrent, + csa_params->channel, + csa_params->new_op_class); + } + if (chan_space >= 160 && fw_vht_ch_wd < + WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) + chan_space = 80; + session_entry->gLimChannelSwitch.state = + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY; + if (chan_space == 160) { + chnl_switch_info->newChanWidth = + CH_WIDTH_160MHZ; + } else if (chan_space == 80) { + chnl_switch_info->newChanWidth = + CH_WIDTH_80MHZ; + session_entry->htSupportedChannelWidthSet = + true; + } else if (chan_space == 40) { + chnl_switch_info->newChanWidth = + CH_WIDTH_40MHZ; + session_entry->htSupportedChannelWidthSet = + true; + } else { + chnl_switch_info->newChanWidth = + CH_WIDTH_20MHZ; + lim_ch_switch->state = + eLIM_CHANNEL_SWITCH_PRIMARY_ONLY; + } + + ch_params.ch_width = + chnl_switch_info->newChanWidth; + wlan_reg_set_channel_params(mac_ctx->pdev, + csa_params->channel, 0, &ch_params); + chnl_switch_info->newCenterChanFreq0 = + ch_params.center_freq_seg0; + /* + * This is not applicable for 20/40/80 MHz. + * Only used when we support 80+80 MHz operation. + * In case of 80+80 MHz, this parameter indicates + * center channel frequency index of 80 MHz + * channel offrequency segment 1. + */ + chnl_switch_info->newCenterChanFreq1 = + ch_params.center_freq_seg1; + lim_ch_switch->sec_ch_offset = + ch_params.sec_ch_offset; + + } else { + lim_ch_switch->state = + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY; + ch_params.ch_width = CH_WIDTH_40MHZ; + wlan_reg_set_channel_params(mac_ctx->pdev, + csa_params->channel, 0, &ch_params); + lim_ch_switch->sec_ch_offset = + ch_params.sec_ch_offset; + chnl_switch_info->newChanWidth = CH_WIDTH_40MHZ; + chnl_switch_info->newCenterChanFreq0 = + ch_params.center_freq_seg0; + chnl_switch_info->newCenterChanFreq1 = 0; + session_entry->htSupportedChannelWidthSet = true; + } + session_entry->gLimChannelSwitch.ch_center_freq_seg0 = + chnl_switch_info->newCenterChanFreq0; + session_entry->gLimChannelSwitch.ch_center_freq_seg1 = + chnl_switch_info->newCenterChanFreq1; + session_entry->gLimChannelSwitch.ch_width = + chnl_switch_info->newChanWidth; + + } else if (channel_bonding_mode && session_entry->htCapability) { + if (csa_params->ies_present_flag + & lim_xcsa_ie_present) { + chan_space = + wlan_reg_dmn_get_chanwidth_from_opclass( + mac_ctx->scan.countryCodeCurrent, + csa_params->channel, + csa_params->new_op_class); + lim_ch_switch->state = + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY; + if (chan_space == 40) { + lim_ch_switch->ch_width = + CH_WIDTH_40MHZ; + chnl_switch_info->newChanWidth = + CH_WIDTH_40MHZ; + ch_params.ch_width = + chnl_switch_info->newChanWidth; + wlan_reg_set_channel_params(mac_ctx->pdev, + csa_params->channel, + 0, &ch_params); + lim_ch_switch->ch_center_freq_seg0 = + ch_params.center_freq_seg0; + lim_ch_switch->sec_ch_offset = + ch_params.sec_ch_offset; + session_entry->htSupportedChannelWidthSet = + true; + } else { + lim_ch_switch->ch_width = + CH_WIDTH_20MHZ; + chnl_switch_info->newChanWidth = + CH_WIDTH_20MHZ; + lim_ch_switch->state = + eLIM_CHANNEL_SWITCH_PRIMARY_ONLY; + lim_ch_switch->sec_ch_offset = + PHY_SINGLE_CHANNEL_CENTERED; + } + } else { + lim_ch_switch->ch_width = + CH_WIDTH_40MHZ; + lim_ch_switch->state = + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY; + ch_params.ch_width = CH_WIDTH_40MHZ; + wlan_reg_set_channel_params(mac_ctx->pdev, + csa_params->channel, 0, &ch_params); + lim_ch_switch->ch_center_freq_seg0 = + ch_params.center_freq_seg0; + lim_ch_switch->sec_ch_offset = + ch_params.sec_ch_offset; + session_entry->htSupportedChannelWidthSet = true; + } + } + pe_debug("new ch %d freq %d width: %d freq0 %d freq1 %d ht width %d", + session_entry->gLimChannelSwitch.primaryChannel, + session_entry->gLimChannelSwitch.sw_target_freq, + session_entry->gLimChannelSwitch.ch_width, + session_entry->gLimChannelSwitch.ch_center_freq_seg0, + session_entry->gLimChannelSwitch.ch_center_freq_seg1, + session_entry->gLimChannelSwitch.sec_ch_offset); + + if (session_entry->curr_op_freq == csa_params->csa_chan_freq && + session_entry->ch_width == + session_entry->gLimChannelSwitch.ch_width) { + pe_debug("Ignore CSA, no change in ch and bw"); + goto err; + } + + if (WLAN_REG_IS_24GHZ_CH_FREQ(csa_params->csa_chan_freq) && + session_entry->dot11mode == MLME_DOT11_MODE_11A) + session_entry->dot11mode = MLME_DOT11_MODE_11G; + else if (WLAN_REG_IS_5GHZ_CH_FREQ(csa_params->csa_chan_freq) && + ((session_entry->dot11mode == MLME_DOT11_MODE_11G) || + (session_entry->dot11mode == MLME_DOT11_MODE_11G_ONLY))) + session_entry->dot11mode = MLME_DOT11_MODE_11A; + + /* Send RSO Stop to FW before triggering the vdev restart for CSA */ + if (mac_ctx->lim.stop_roaming_callback) + mac_ctx->lim.stop_roaming_callback(MAC_HANDLE(mac_ctx), + session_entry->smeSessionId, + REASON_DRIVER_DISABLED, + RSO_CHANNEL_SWITCH); + + lim_prepare_for11h_channel_switch(mac_ctx, session_entry); + + lim_flush_bssid(mac_ctx, session_entry->bssId); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT + lim_diag_event_report(mac_ctx, + WLAN_PE_DIAG_SWITCH_CHL_IND_EVENT, session_entry, + QDF_STATUS_SUCCESS, QDF_STATUS_SUCCESS); +#endif + +err: + qdf_mem_free(csa_params); +} + +/*-------------------------------------------------------------------------- + \brief pe_delete_session() - Handle the Delete BSS Response from HAL. + + \param mac - pointer to global adapter context + \param sessionId - Message pointer. + + \sa + --------------------------------------------------------------------------*/ + +void lim_handle_delete_bss_rsp(struct mac_context *mac, + struct del_bss_resp *del_bss_rsp) +{ + struct pe_session *pe_session; + + pe_session = + pe_find_session_by_vdev_id_and_state(mac, + del_bss_rsp->vdev_id, + eLIM_MLM_WT_DEL_BSS_RSP_STATE); + if (!pe_session) { + qdf_mem_free(del_bss_rsp); + return; + } + + /* + * During DEL BSS handling, the PE Session will be deleted, but it is + * better to clear this flag if the session is hanging around due + * to some error conditions so that the next DEL_BSS request does + * not take the HO_FAIL path + */ + pe_session->process_ho_fail = false; + if (LIM_IS_IBSS_ROLE(pe_session)) + lim_ibss_del_bss_rsp(mac, del_bss_rsp, pe_session); + else if (LIM_IS_UNKNOWN_ROLE(pe_session)) + lim_process_sme_del_bss_rsp(mac, pe_session); + else if (LIM_IS_NDI_ROLE(pe_session)) + lim_ndi_del_bss_rsp(mac, del_bss_rsp, pe_session); + else + lim_process_mlm_del_bss_rsp(mac, del_bss_rsp, pe_session); + + qdf_mem_free(del_bss_rsp); +} + +/** ----------------------------------------------------------------- + \brief lim_send_sme_aggr_qos_rsp() - sends SME FT AGGR QOS RSP + \ This function sends a eWNI_SME_FT_AGGR_QOS_RSP to SME. + \ SME only looks at rc and tspec field. + \param mac - global mac structure + \param rspReqd - is SmeAddTsRsp required + \param status - status code of eWNI_SME_FT_AGGR_QOS_RSP + \return tspec + \sa + ----------------------------------------------------------------- */ +void +lim_send_sme_aggr_qos_rsp(struct mac_context *mac, tpSirAggrQosRsp aggrQosRsp, + uint8_t smesessionId) +{ + struct scheduler_msg mmhMsg = {0}; + + mmhMsg.type = eWNI_SME_FT_AGGR_QOS_RSP; + mmhMsg.bodyptr = aggrQosRsp; + mmhMsg.bodyval = 0; + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + smesessionId, mmhMsg.type)); + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + + return; +} + +void lim_send_sme_max_assoc_exceeded_ntf(struct mac_context *mac, tSirMacAddr peerMacAddr, + uint8_t smesessionId) +{ + struct scheduler_msg mmhMsg = {0}; + tSmeMaxAssocInd *pSmeMaxAssocInd; + + pSmeMaxAssocInd = qdf_mem_malloc(sizeof(tSmeMaxAssocInd)); + if (!pSmeMaxAssocInd) + return; + qdf_mem_copy((uint8_t *) pSmeMaxAssocInd->peer_mac.bytes, + (uint8_t *) peerMacAddr, QDF_MAC_ADDR_SIZE); + pSmeMaxAssocInd->mesgType = eWNI_SME_MAX_ASSOC_EXCEEDED; + pSmeMaxAssocInd->mesgLen = sizeof(tSmeMaxAssocInd); + pSmeMaxAssocInd->sessionId = smesessionId; + mmhMsg.type = pSmeMaxAssocInd->mesgType; + mmhMsg.bodyptr = pSmeMaxAssocInd; + pe_debug("msgType: %s peerMacAddr "QDF_MAC_ADDR_FMT "sme session id %d", + "eWNI_SME_MAX_ASSOC_EXCEEDED", QDF_MAC_ADDR_REF(peerMacAddr), + pSmeMaxAssocInd->sessionId); + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + smesessionId, mmhMsg.type)); + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + + return; +} + +/** ----------------------------------------------------------------- + \brief lim_send_sme_ap_channel_switch_resp() - sends + eWNI_SME_CHANNEL_CHANGE_RSP + After receiving WMA_SWITCH_CHANNEL_RSP indication this + function sends a eWNI_SME_CHANNEL_CHANGE_RSP to SME to notify + that the Channel change has been done to the specified target + channel in the Channel change request + \param mac - global mac structure + \param pe_session - session info + \param pChnlParams - Channel switch params + --------------------------------------------------------------------*/ +void +lim_send_sme_ap_channel_switch_resp(struct mac_context *mac, + struct pe_session *pe_session, + struct vdev_start_response *rsp) +{ + struct scheduler_msg mmhMsg = {0}; + struct sSirChanChangeResponse *chan_change_rsp; + bool is_ch_dfs = false; + enum phy_ch_width ch_width; + uint32_t ch_cfreq1 = 0; + enum reg_wifi_band band; + + qdf_runtime_pm_allow_suspend(&pe_session->ap_ecsa_runtime_lock); + qdf_wake_lock_release(&pe_session->ap_ecsa_wakelock, 0); + + chan_change_rsp = + qdf_mem_malloc(sizeof(struct sSirChanChangeResponse)); + if (!chan_change_rsp) + return; + + chan_change_rsp->new_op_freq = pe_session->curr_op_freq; + chan_change_rsp->channelChangeStatus = rsp->status; + /* + * Pass the sme sessionID to SME instead + * PE session ID. + */ + chan_change_rsp->sessionId = rsp->vdev_id; + + mmhMsg.type = eWNI_SME_CHANNEL_CHANGE_RSP; + mmhMsg.bodyptr = (void *)chan_change_rsp; + mmhMsg.bodyval = 0; + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + + if (QDF_IS_STATUS_ERROR(rsp->status)) { + pe_err("failed to change sap freq to %u", + pe_session->curr_op_freq); + return; + } + + /* + * We should start beacon transmission only if the new + * channel after channel change is Non-DFS. For a DFS + * channel, PE will receive an explicit request from + * upper layers to start the beacon transmission . + */ + ch_width = pe_session->ch_width; + band = wlan_reg_freq_to_band(pe_session->curr_op_freq); + if (pe_session->ch_center_freq_seg1) + ch_cfreq1 = wlan_reg_chan_band_to_freq( + mac->pdev, + pe_session->ch_center_freq_seg1, + BIT(band)); + + if (ch_width == CH_WIDTH_160MHZ) { + is_ch_dfs = true; + } else if (ch_width == CH_WIDTH_80P80MHZ) { + if (wlan_reg_get_channel_state_for_freq( + mac->pdev, + pe_session->curr_op_freq) == + CHANNEL_STATE_DFS || + wlan_reg_get_channel_state_for_freq( + mac->pdev, + ch_cfreq1) == + CHANNEL_STATE_DFS) + is_ch_dfs = true; + } else { + if (wlan_reg_get_channel_state_for_freq( + mac->pdev, + pe_session->curr_op_freq) == + CHANNEL_STATE_DFS) + is_ch_dfs = true; + } + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(pe_session->curr_op_freq)) + is_ch_dfs = false; + + if (is_ch_dfs) { + lim_sap_move_to_cac_wait_state(pe_session); + + } else { + lim_apply_configuration(mac, pe_session); + lim_send_beacon(mac, pe_session); + lim_obss_send_detection_cfg(mac, pe_session, true); + } + return; +} + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +/** + * lim_send_bss_color_change_ie_update() - update bss color change IE in + * beacon template + * + * @mac_ctx: pointer to global adapter context + * @session: session pointer + * + * Return: none + */ +static void +lim_send_bss_color_change_ie_update(struct mac_context *mac_ctx, + struct pe_session *session) +{ + /* Update the beacon template and send to FW */ + if (sch_set_fixed_beacon_fields(mac_ctx, session) != QDF_STATUS_SUCCESS) { + pe_err("Unable to set BSS color change IE in beacon"); + return; + } + + /* Send update beacon template message */ + lim_send_beacon_ind(mac_ctx, session, REASON_COLOR_CHANGE); + pe_debug("Updated BSS color change countdown = %d", + session->he_bss_color_change.countdown); +} + +static void +lim_handle_bss_color_change_ie(struct mac_context *mac_ctx, + struct pe_session *session) +{ + tUpdateBeaconParams beacon_params; + + /* handle bss color change IE */ + if (LIM_IS_AP_ROLE(session) && + session->he_op.bss_col_disabled && + session->he_bss_color_change.new_color) { + pe_debug("countdown: %d, new_color: %d", + session->he_bss_color_change.countdown, + session->he_bss_color_change.new_color); + if (session->he_bss_color_change.countdown > 0) { + session->he_bss_color_change.countdown--; + } else { + session->bss_color_changing = 0; + qdf_mem_zero(&beacon_params, sizeof(beacon_params)); + session->he_op.bss_col_disabled = 0; + session->he_op.bss_color = + session->he_bss_color_change.new_color; + session->he_bss_color_change.new_color = 0; + beacon_params.paramChangeBitmap |= + PARAM_BSS_COLOR_CHANGED; + beacon_params.bss_color_disabled = 0; + beacon_params.bss_color = session->he_op.bss_color; + lim_send_beacon_params(mac_ctx, + &beacon_params, + session); + lim_send_obss_color_collision_cfg( + mac_ctx, session, + OBSS_COLOR_COLLISION_DETECTION); + } + lim_send_bss_color_change_ie_update(mac_ctx, session); + } +} + +#else +static void +lim_handle_bss_color_change_ie(struct mac_context *mac_ctx, + struct pe_session *session) +{ +} +#endif + +void +lim_process_beacon_tx_success_ind(struct mac_context *mac_ctx, uint16_t msgType, + void *event) +{ + struct pe_session *session; + tpSirFirstBeaconTxCompleteInd bcn_ind = + (tSirFirstBeaconTxCompleteInd *) event; + + session = pe_find_session_by_vdev_id(mac_ctx, bcn_ind->bss_idx); + if (!session) { + pe_err("Session Does not exist for given session id"); + return; + } + + pe_debug("role: %d swIe: %d opIe: %d switch cnt:%d", + GET_LIM_SYSTEM_ROLE(session), + session->dfsIncludeChanSwIe, + session->gLimOperatingMode.present, + session->gLimChannelSwitch.switchCount); + + if (!LIM_IS_AP_ROLE(session)) + return; + + if (session->dfsIncludeChanSwIe && + (session->gLimChannelSwitch.switchCount == + mac_ctx->sap.SapDfsInfo.sap_ch_switch_beacon_cnt)) + lim_process_ap_ecsa_timeout(session); + + + if (session->gLimOperatingMode.present) + /* Done with nss update */ + session->gLimOperatingMode.present = 0; + + lim_handle_bss_color_change_ie(mac_ctx, session); + + return; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.h new file mode 100644 index 0000000000000000000000000000000000000000..0c7255483394e3947878dea6e92b11c39bd48763 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_send_sme_rsp_messages.h contains the definitions for + * sending SME response/notification messages to applications above + * MAC software. + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#ifndef __LIM_SEND_SME_RSP_H +#define __LIM_SEND_SME_RSP_H + +#include "sir_common.h" +#include "sir_api.h" +#include "sir_mac_prot_def.h" + +/* Functions for sending responses up the stack */ + +/** + * lim_send_sme_rsp() - Send Generic Response to upper layers + * @mac_ctx: Pointer to Global MAC structure + * @msg_type: Indicates message type + * @result_code: Indicates the result of previously issued + * eWNI_SME_msg_type_REQ message + * @vdev_id: vdev_id + * + * This function is called by lim_process_sme_req_messages() to send + * eWNI_SME_START_RSP, eWNI_SME_STOP_BSS_RSP + * or eWNI_SME_SWITCH_CHL_RSP messages to applications above MAC + * Software. + * + * Return: None + */ +void lim_send_sme_rsp(struct mac_context *mac_ctx, uint16_t msg_type, + tSirResultCodes result_code, uint8_t vdev_id); + +/** + * lim_send_sme_start_bss_rsp() - Send Start BSS response + * @mac: Pointer to Global MAC structure + * @msgType: Indicates message type + * @resultCode: Indicates the result of previously issued request + * @pe_session: PE session associated with the BSS + * @smesessionId: ID of the SME session associated with the BSS + * + * This function is called to send eWNI_SME_START_BSS_RSP + * message to applications above MAC Software. + */ +void lim_send_sme_start_bss_rsp(struct mac_context *mac, + uint16_t msgType, + tSirResultCodes resultCode, + struct pe_session *pe_session, + uint8_t smesessionId); + +/** + * lim_send_sme_join_reassoc_rsp() - Send Response to Upper Layers + * @mac_ctx: Pointer to Global MAC structure + * @msg_type: Indicates message type + * @result_code: Indicates the result of previously issued request + * @prot_status_code: Protocol Status Code + * @session_entry: PE Session Info + * @vdev_id: vdev_id + * + * This function is called by lim_process_sme_req_messages() to send + * eWNI_SME_JOIN_RSP or eWNI_SME_REASSOC_RSP messages to applications + * above MAC Software. + * + * Return: None + */ +void lim_send_sme_join_reassoc_rsp(struct mac_context *mac_ctx, + uint16_t msg_type, + tSirResultCodes result_code, + uint16_t prot_status_code, + struct pe_session *session_entry, + uint8_t vdev_id); + +/** + * lim_prepare_disconnect_done_ind() - Prepares the disconnect done ind message + * @mac_ctx: Global mac_ctx + * @session_id: PE session id + * @reason_code: Disconnect indication reason code + * @peer_mac_addr: MAC address of the peer + * + * Prepares the disconnect done indication message to be sent to the upper layer + * + * Return: QDF Status + */ +QDF_STATUS lim_prepare_disconnect_done_ind(struct mac_context *mac_ctx, + uint32_t **msg, + uint8_t session_id, + tSirResultCodes reason_code, + uint8_t *peer_mac_addr); + +/** + * lim_send_sme_disassoc_ntf() - Send disassoc notification to upper layer + * @mac: Global MAC context + * @peerMacAddr: The peer MAC addr to which disassociate was initiated + * @reasonCode: The reason for Disassociation + * @disassocTrigger: The trigger for Disassociation + * @aid: The STAID. This parameter is present only on AP + * @smesessionId: ID of the SME session associated with the event + * @pe_session: The PE session associated with the event + * + * This function is used for sending eWNI_SME_DISASSOC_RSP, + * or eWNI_SME_DISASSOC_IND to upper layer depending on + * disassociation trigger. + * + * Return: None + */ +void lim_send_sme_disassoc_ntf(struct mac_context *mac, + tSirMacAddr peerMacAddr, + tSirResultCodes reasonCode, + uint16_t disassocTrigger, + uint16_t aid, + uint8_t smesessionId, + struct pe_session *pe_session); + +/** + * lim_send_sme_deauth_ntf() - send deauth notice to upper layer + * @peerMacAddr: peer MAC addr to which deauthentication was initiated + * @reasonCode: the reason for Deauthetication + * @deauthTrigger: the trigger for Deauthetication + * @aid: the STAID. This parameter is present only on AP. + * @vdev_id: vdev id + * + * This function is used for sending eWNI_SME_DEAUTH_RSP or + * eWNI_SME_DEAUTH_IND to upper layers depending on deauthentication + * trigger. + * + * Return: None + */ +void lim_send_sme_deauth_ntf(struct mac_context *mac, tSirMacAddr peerMacAddr, + tSirResultCodes reasonCode, uint16_t deauthTrigger, + uint16_t aid, uint8_t vdev_id); + +void lim_send_sme_disassoc_ind(struct mac_context *, tpDphHashNode, struct pe_session *); +void lim_send_sme_deauth_ind(struct mac_context *, tpDphHashNode, + struct pe_session *pe_session); +void lim_send_sme_wm_status_change_ntf(struct mac_context *, tSirSmeStatusChangeCode, + uint32_t *, uint16_t, uint8_t); + +/** + * lim_send_sme_set_context_rsp() - Send set context response to upper layer + * @mac: Pointer to Global MAC structure + * @peer_macaddr: the peer MAC addr to which setContext was performed + * @aid: the aid corresponding to the peer MAC address + * @resultCode: the result of previously issued Set Context Req message + * @pe_session: The PE session associated with the peer + * @smesessionId: ID of the SME session associated with the peer + * + * This function is called to send eWNI_SME_SETCONTEXT_RSP message to + * upper layer + * + * Return: None + */ +void lim_send_sme_set_context_rsp(struct mac_context *mac, + struct qdf_mac_addr peer_macaddr, + uint16_t aid, + tSirResultCodes resultCode, + struct pe_session *pe_session, + uint8_t smesessionId); + +/** + * lim_handle_delete_bss_rsp () - API to handle delete bss response + * @mac: global mac structure + * @del_bss_rsp: pointer to del bss response + * + * Return: None + */ +void lim_handle_delete_bss_rsp(struct mac_context *mac, + struct del_bss_resp *del_bss_rsp); + +void lim_handle_csa_offload_msg(struct mac_context *mac_ctx, + struct scheduler_msg *msg); + +void +lim_send_sme_aggr_qos_rsp(struct mac_context *mac, tpSirAggrQosRsp aggrQosRsp, + uint8_t smesessionId); + +/** + * lim_send_sme_addts_rsp() - sends SME ADDTS RSP + * @mac: global mac structure + * @rspReqd: is SmeAddTsRsp required + * @status: status code of SME_ADD_TS_RSP + * @pe_session: The PE session associated with the connection + * @tspec: The TSpec that was added + * @smesessionId: ID of the SME session associated with the connection + * + * This function sends a eWNI_SME_ADDTS_RSP to upper layer + * + * Return: None + */ +void lim_send_sme_addts_rsp(struct mac_context *mac, + uint8_t rspReqd, uint32_t status, + struct pe_session *pe_session, + struct mac_tspec_ie tspec, + uint8_t smesessionId); + +void lim_send_sme_delts_rsp(struct mac_context *mac, tpSirDeltsReq delts, + uint32_t status, struct pe_session *pe_session, + uint8_t smesessionId); +void lim_send_sme_delts_ind(struct mac_context *mac, + struct delts_req_info *delts, + uint16_t aid, struct pe_session *); + +#ifdef FEATURE_WLAN_ESE +void lim_send_sme_pe_ese_tsm_rsp(struct mac_context *mac, tAniGetTsmStatsRsp *pStats); +#endif + +#ifdef QCA_IBSS_SUPPORT +/* + * lim_send_sme_ibss_peer_ind() - API to send ibss peer ind to sme + * @mac_ctx: Global mac_ctx + * @peerMacAddr: peer mac address + * @staIndex: sta index + * @beacon: pionter to beacon + * @beaconLen: length of beacon buffer + * @msg_type: msg_type + * @sessionId: session id + * + * + * Return: none + */ +void lim_send_sme_ibss_peer_ind(struct mac_context *mac, tSirMacAddr peerMacAddr, + uint8_t *beacon, + uint16_t beaconLen, uint16_t msgType, + uint8_t sessionId); +#else +static inline void +lim_send_sme_ibss_peer_ind(struct mac_context *mac, + tSirMacAddr peerMacAddr, + uint16_t staIndex, + uint8_t *beacon, + uint16_t beaconLen, uint16_t msgType, + uint8_t sessionId) +{ +} +#endif + +void lim_send_sme_max_assoc_exceeded_ntf(struct mac_context *mac, tSirMacAddr peerMacAddr, + uint8_t smesessionId); + +void lim_send_sme_ap_channel_switch_resp(struct mac_context *mac, + struct pe_session *pe_session, + struct vdev_start_response *rsp); +/* + * lim_process_beacon_tx_success_ind() - handle successful beacon transmission + * indication from the FW This is a generic event generated by the FW afer the + * first beacon is sent out after the beacon template update by the host. + * + * @mac_ctx: Global mac_ctx + * @msg_type: msg_type + */ +void +lim_process_beacon_tx_success_ind(struct mac_context *mac, uint16_t msgType, + void *event); + +typedef enum { + lim_csa_ie_present = 0x00000001, + lim_xcsa_ie_present = 0x00000002, + lim_wbw_ie_present = 0x00000004, + lim_cswarp_ie_present = 0x00000008, +} lim_csa_event_ies_present_flag; + +#endif /* __LIM_SEND_SME_RSP_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ser_des_utils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ser_des_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..488529a81bbc69897a42d67fa539920df5a63bfb --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_ser_des_utils.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_ser_des_utils.h contains the utility definitions + * LIM uses while processing messages from upper layer software + * modules + * Author: Chandra Modumudi + * Date: 10/20/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ +#ifndef __LIM_SERDES_UTILS_H +#define __LIM_SERDES_UTILS_H + +#include "sir_api.h" +#include "ani_system_defs.h" +#include "sir_mac_prot_def.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_prop_exts_utils.h" + +/* Byte String <--> uint16_t/uint32_t copy functions */ +static inline void lim_copy_u16(uint8_t *ptr, uint16_t u16Val) +{ +#if ((defined(ANI_OS_TYPE_QNX) && defined(ANI_LITTLE_BYTE_ENDIAN)) || \ + (defined(ANI_OS_TYPE_ANDROID) && defined(ANI_LITTLE_BYTE_ENDIAN))) + *ptr++ = (uint8_t) (u16Val & 0xff); + *ptr = (uint8_t) ((u16Val >> 8) & 0xff); +#else +#error "Unknown combination of OS Type and endianness" +#endif +} + +static inline uint16_t lim_get_u16(uint8_t *ptr) +{ +#if ((defined(ANI_OS_TYPE_QNX) && defined(ANI_LITTLE_BYTE_ENDIAN)) || \ + (defined(ANI_OS_TYPE_ANDROID) && defined(ANI_LITTLE_BYTE_ENDIAN))) + return ((uint16_t) (*(ptr + 1) << 8)) | ((uint16_t) (*ptr)); +#else +#error "Unknown combination of OS Type and endianness" +#endif +} + +static inline void lim_copy_u32(uint8_t *ptr, uint32_t u32Val) +{ +#if ((defined(ANI_OS_TYPE_QNX) && defined(ANI_LITTLE_BYTE_ENDIAN)) || \ + (defined(ANI_OS_TYPE_ANDROID) && defined(ANI_LITTLE_BYTE_ENDIAN))) + *ptr++ = (uint8_t) (u32Val & 0xff); + *ptr++ = (uint8_t) ((u32Val >> 8) & 0xff); + *ptr++ = (uint8_t) ((u32Val >> 16) & 0xff); + *ptr = (uint8_t) ((u32Val >> 24) & 0xff); +#else +#error "Unknown combination of OS Type and endianness" +#endif +} + +static inline uint32_t lim_get_u32(uint8_t *ptr) +{ +#if ((defined(ANI_OS_TYPE_QNX) && defined(ANI_LITTLE_BYTE_ENDIAN)) || \ + (defined(ANI_OS_TYPE_ANDROID) && defined(ANI_LITTLE_BYTE_ENDIAN))) + return ((*(ptr + 3) << 24) | + (*(ptr + 2) << 16) | (*(ptr + 1) << 8) | (*(ptr))); +#else +#error "Unknown combination of OS Type and endianness" +#endif +} + +/** + * lim_copy_u16_be()- This API copies a u16 value in buffer + * to network byte order + * @ptr: pointer to buffer + * @u16_val: value needs to be copied + * + * Return: None + */ +static inline void lim_copy_u16_be(uint8_t *ptr, uint16_t u16_val) +{ + ptr[0] = u16_val >> 8; + ptr[1] = u16_val & 0xff; +} + +/** + * lim_copy_u16_be()- This API reads u16 value from network byte order buffer + * @ptr: pointer to buffer + * + * Return: 16bit value + */ +static inline uint16_t lim_get_u16_be(uint8_t *buf) +{ + return (buf[0] << 8) | buf[1]; +} +#endif /* __LIM_SERDES_UTILS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_session.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_session.c new file mode 100644 index 0000000000000000000000000000000000000000..eacb32ec1f93d43a6115037baabab688b3943ae0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_session.c @@ -0,0 +1,1148 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + \file lim_session.c + + \brief implementation for lim Session related APIs + + \author Sunit Bhatia + + ========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "ani_global.h" +#include "lim_ft_defs.h" +#include "lim_ft.h" +#include "lim_session.h" +#include "lim_utils.h" + +#include "sch_api.h" +#include "lim_send_messages.h" +#include "cfg_ucfg_api.h" + +#ifdef WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY +static struct sDphHashNode *g_dph_node_array; + +QDF_STATUS pe_allocate_dph_node_array_buffer(void) +{ + uint32_t buf_size; + + buf_size = WLAN_MAX_VDEVS * (SIR_SAP_MAX_NUM_PEERS + 1) * + sizeof(struct sDphHashNode); + g_dph_node_array = qdf_mem_malloc(buf_size); + if (!g_dph_node_array) + return QDF_STATUS_E_NOMEM; + + return QDF_STATUS_SUCCESS; +} + +void pe_free_dph_node_array_buffer(void) +{ + qdf_mem_free(g_dph_node_array); + g_dph_node_array = NULL; +} + +static inline +struct sDphHashNode *pe_get_session_dph_node_array(uint8_t session_id) +{ + return &g_dph_node_array[session_id * (SIR_SAP_MAX_NUM_PEERS + 1)]; +} + +#else /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ +static struct sDphHashNode + g_dph_node_array[WLAN_MAX_VDEVS][SIR_SAP_MAX_NUM_PEERS + 1]; + +static inline +struct sDphHashNode *pe_get_session_dph_node_array(uint8_t session_id) +{ + return g_dph_node_array[session_id]; +} +#endif /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ + +/*-------------------------------------------------------------------------- + + \brief pe_init_beacon_params() - Initialize the beaconParams structure + + \param struct pe_session * - pointer to the session context or NULL if session can not be created. + \return void + \sa + + --------------------------------------------------------------------------*/ + +static void pe_init_beacon_params(struct mac_context *mac, + struct pe_session *pe_session) +{ + pe_session->beaconParams.beaconInterval = 0; + pe_session->beaconParams.fShortPreamble = 0; + pe_session->beaconParams.llaCoexist = 0; + pe_session->beaconParams.llbCoexist = 0; + pe_session->beaconParams.llgCoexist = 0; + pe_session->beaconParams.ht20Coexist = 0; + pe_session->beaconParams.llnNonGFCoexist = 0; + pe_session->beaconParams.fRIFSMode = 0; + pe_session->beaconParams.fLsigTXOPProtectionFullSupport = 0; + pe_session->beaconParams.gHTObssMode = 0; + + /* Number of legacy STAs associated */ + qdf_mem_zero((void *)&pe_session->gLim11bParams, + sizeof(tLimProtStaParams)); + qdf_mem_zero((void *)&pe_session->gLim11aParams, + sizeof(tLimProtStaParams)); + qdf_mem_zero((void *)&pe_session->gLim11gParams, + sizeof(tLimProtStaParams)); + qdf_mem_zero((void *)&pe_session->gLimNonGfParams, + sizeof(tLimProtStaParams)); + qdf_mem_zero((void *)&pe_session->gLimHt20Params, + sizeof(tLimProtStaParams)); + qdf_mem_zero((void *)&pe_session->gLimLsigTxopParams, + sizeof(tLimProtStaParams)); + qdf_mem_zero((void *)&pe_session->gLimOlbcParams, + sizeof(tLimProtStaParams)); +} + +/* + * pe_reset_protection_callback() - resets protection structs so that when an AP + * causing use of protection goes away, corresponding protection bit can be + * reset + * @ptr: pointer to pe_session + * + * This function resets protection structs so that when an AP causing use of + * protection goes away, corresponding protection bit can be reset. This allowes + * protection bits to be reset once legacy overlapping APs are gone. + * + * Return: void + */ +static void pe_reset_protection_callback(void *ptr) +{ + struct pe_session *pe_session_entry = (struct pe_session *)ptr; + struct mac_context *mac_ctx = pe_session_entry->mac_ctx; + int8_t i = 0; + tUpdateBeaconParams beacon_params; + uint16_t current_protection_state = 0; + tpDphHashNode station_hash_node = NULL; + tSirMacHTOperatingMode old_op_mode; + bool bcn_prms_changed = false; + + if (pe_session_entry->valid == false) { + pe_err("session already deleted. exiting timer callback"); + return; + } + + /* + * During CAC period, if the callback is triggered, the beacon + * template may get updated. Subsequently if the vdev is not up, the + * vdev would be made up -- which should not happen during the CAC + * period. To avoid this, ignore the protection callback if the session + * is not yet up. + */ + if (!wma_is_vdev_up(pe_session_entry->smeSessionId)) { + pe_err("session is not up yet. exiting timer callback"); + return; + } + + /* + * If dfsIncludeChanSwIe is set restrat timer as we are going to change + * channel and no point in checking protection mode for this channel. + */ + if (pe_session_entry->dfsIncludeChanSwIe) { + pe_err("CSA going on restart timer"); + goto restart_timer; + } + current_protection_state |= + pe_session_entry->gLimOverlap11gParams.protectionEnabled | + pe_session_entry->gLimOverlap11aParams.protectionEnabled << 1 | + pe_session_entry->gLimOverlapHt20Params.protectionEnabled << 2 | + pe_session_entry->gLimOverlapNonGfParams.protectionEnabled << 3 | + pe_session_entry->gLimOlbcParams.protectionEnabled << 4; + + pe_debug("old protection state: 0x%04X, new protection state: 0x%04X", + pe_session_entry->old_protection_state, + current_protection_state); + + qdf_mem_zero(&pe_session_entry->gLimOverlap11gParams, + sizeof(pe_session_entry->gLimOverlap11gParams)); + qdf_mem_zero(&pe_session_entry->gLimOverlap11aParams, + sizeof(pe_session_entry->gLimOverlap11aParams)); + qdf_mem_zero(&pe_session_entry->gLimOverlapHt20Params, + sizeof(pe_session_entry->gLimOverlapHt20Params)); + qdf_mem_zero(&pe_session_entry->gLimOverlapNonGfParams, + sizeof(pe_session_entry->gLimOverlapNonGfParams)); + + qdf_mem_zero(&pe_session_entry->gLimOlbcParams, + sizeof(pe_session_entry->gLimOlbcParams)); + + /* + * Do not reset fShortPreamble and beaconInterval, as they + * are not updated. + */ + pe_session_entry->beaconParams.llaCoexist = 0; + pe_session_entry->beaconParams.llbCoexist = 0; + pe_session_entry->beaconParams.llgCoexist = 0; + pe_session_entry->beaconParams.ht20Coexist = 0; + pe_session_entry->beaconParams.llnNonGFCoexist = 0; + pe_session_entry->beaconParams.fRIFSMode = 0; + pe_session_entry->beaconParams.fLsigTXOPProtectionFullSupport = 0; + pe_session_entry->beaconParams.gHTObssMode = 0; + + + old_op_mode = pe_session_entry->htOperMode; + pe_session_entry->htOperMode = eSIR_HT_OP_MODE_PURE; + mac_ctx->lim.gHTOperMode = eSIR_HT_OP_MODE_PURE; + + qdf_mem_zero(&beacon_params, sizeof(tUpdateBeaconParams)); + /* index 0, is self node, peers start from 1 */ + for (i = 1 ; i <= mac_ctx->mlme_cfg->sap_cfg.assoc_sta_limit ; i++) { + station_hash_node = dph_get_hash_entry(mac_ctx, i, + &pe_session_entry->dph.dphHashTable); + if (!station_hash_node) + continue; + lim_decide_ap_protection(mac_ctx, station_hash_node->staAddr, + &beacon_params, pe_session_entry); + } + + if (pe_session_entry->htOperMode != old_op_mode) + bcn_prms_changed = true; + + if ((current_protection_state != + pe_session_entry->old_protection_state) && + (false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running)) { + pe_debug("protection changed, update beacon template"); + /* update beacon fix params and send update to FW */ + qdf_mem_zero(&beacon_params, sizeof(tUpdateBeaconParams)); + beacon_params.bss_idx = pe_session_entry->vdev_id; + beacon_params.fShortPreamble = + pe_session_entry->beaconParams.fShortPreamble; + beacon_params.beaconInterval = + pe_session_entry->beaconParams.beaconInterval; + beacon_params.llaCoexist = + pe_session_entry->beaconParams.llaCoexist; + beacon_params.llbCoexist = + pe_session_entry->beaconParams.llbCoexist; + beacon_params.llgCoexist = + pe_session_entry->beaconParams.llgCoexist; + beacon_params.ht20MhzCoexist = + pe_session_entry->beaconParams.ht20Coexist; + beacon_params.llnNonGFCoexist = + pe_session_entry->beaconParams.llnNonGFCoexist; + beacon_params.fLsigTXOPProtectionFullSupport = + pe_session_entry->beaconParams. + fLsigTXOPProtectionFullSupport; + beacon_params.fRIFSMode = + pe_session_entry->beaconParams.fRIFSMode; + beacon_params.vdev_id = + pe_session_entry->vdev_id; + beacon_params.paramChangeBitmap |= PARAM_llBCOEXIST_CHANGED; + bcn_prms_changed = true; + } + + if (bcn_prms_changed) { + sch_set_fixed_beacon_fields(mac_ctx, pe_session_entry); + lim_send_beacon_params(mac_ctx, &beacon_params, pe_session_entry); + } + + pe_session_entry->old_protection_state = current_protection_state; +restart_timer: + if (qdf_mc_timer_start(&pe_session_entry-> + protection_fields_reset_timer, + SCH_PROTECTION_RESET_TIME) + != QDF_STATUS_SUCCESS) { + pe_err("cannot create or start protectionFieldsResetTimer"); + } +} + +#ifdef WLAN_FEATURE_11W +/** + * pe_init_pmf_comeback_timer: init PMF comeback timer + * @mac_ctx: pointer to global adapter context + * @session: pe session + * + * Return: void + */ +static void +pe_init_pmf_comeback_timer(tpAniSirGlobal mac_ctx, struct pe_session *session) +{ + QDF_STATUS status; + + if (session->opmode != QDF_STA_MODE) + return; + + pe_debug("init pmf comeback timer for vdev %d", session->vdev_id); + session->pmf_retry_timer_info.mac = mac_ctx; + session->pmf_retry_timer_info.vdev_id = session->vdev_id; + session->pmf_retry_timer_info.retried = false; + status = qdf_mc_timer_init( + &session->pmf_retry_timer, QDF_TIMER_TYPE_SW, + lim_pmf_comeback_timer_callback, + (void *)&session->pmf_retry_timer_info); + if (!QDF_IS_STATUS_SUCCESS(status)) + pe_err("cannot init pmf comeback timer"); +} +#else +static inline void +pe_init_pmf_comeback_timer(tpAniSirGlobal mac_ctx, struct pe_session *session, + uint8_t vdev_id) +{ +} +#endif + +#ifdef WLAN_FEATURE_FILS_SK +/** + * pe_delete_fils_info: API to delete fils session info + * @session: pe session + * + * Return: void + */ +void pe_delete_fils_info(struct pe_session *session) +{ + struct pe_fils_session *fils_info; + + if (!session || (session && !session->valid)) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + FL("session is not valid")); + return; + } + fils_info = session->fils_info; + if (!fils_info) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + FL("fils info not found")); + return; + } + if (fils_info->keyname_nai_data) + qdf_mem_free(fils_info->keyname_nai_data); + if (fils_info->fils_erp_reauth_pkt) + qdf_mem_free(fils_info->fils_erp_reauth_pkt); + if (fils_info->fils_rrk) + qdf_mem_free(fils_info->fils_rrk); + if (fils_info->fils_rik) + qdf_mem_free(fils_info->fils_rik); + if (fils_info->fils_eap_finish_pkt) + qdf_mem_free(fils_info->fils_eap_finish_pkt); + if (fils_info->fils_rmsk) + qdf_mem_free(fils_info->fils_rmsk); + if (fils_info->fils_pmk) + qdf_mem_free(fils_info->fils_pmk); + if (fils_info->auth_info.keyname) + qdf_mem_free(fils_info->auth_info.keyname); + if (fils_info->auth_info.domain_name) + qdf_mem_free(fils_info->auth_info.domain_name); + if (fils_info->hlp_data) + qdf_mem_free(fils_info->hlp_data); + qdf_mem_free(fils_info); + session->fils_info = NULL; +} +/** + * pe_init_fils_info: API to initialize fils session info elements to null + * @session: pe session + * + * Return: void + */ +static void pe_init_fils_info(struct pe_session *session) +{ + struct pe_fils_session *fils_info; + + if (!session || (session && !session->valid)) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + FL("session is not valid")); + return; + } + session->fils_info = qdf_mem_malloc(sizeof(struct pe_fils_session)); + fils_info = session->fils_info; + if (!fils_info) + return; + fils_info->keyname_nai_data = NULL; + fils_info->fils_erp_reauth_pkt = NULL; + fils_info->fils_rrk = NULL; + fils_info->fils_rik = NULL; + fils_info->fils_eap_finish_pkt = NULL; + fils_info->fils_rmsk = NULL; + fils_info->fils_pmk = NULL; + fils_info->auth_info.keyname = NULL; + fils_info->auth_info.domain_name = NULL; +} +#else +static void pe_delete_fils_info(struct pe_session *session) { } +static void pe_init_fils_info(struct pe_session *session) { } +#endif + +/** + * lim_get_peer_idxpool_size: get number of peer idx pool size + * @num_sta: Max number of STA + * @bss_type: BSS type + * + * The peer index start from 1 and thus index 0 is not used, so + * add 1 to the max sta. For STA if TDLS is enabled add 2 as + * index 1 is reserved for peer BSS. + * + * Return: number of peer idx pool size + */ +#ifdef FEATURE_WLAN_TDLS +static inline uint8_t +lim_get_peer_idxpool_size(uint16_t num_sta, enum bss_type bss_type) +{ + /* + * In station role, index 1 is reserved for peer + * corresponding to AP. For TDLS the index should + * start from 2 + */ + if (bss_type == eSIR_INFRASTRUCTURE_MODE) + return num_sta + 2; + else + return num_sta + 1; + +} +#else +static inline uint8_t +lim_get_peer_idxpool_size(uint16_t num_sta, enum bss_type bss_type) +{ + return num_sta + 1; +} +#endif + +void lim_set_bcn_probe_filter(struct mac_context *mac_ctx, + struct pe_session *session, + tSirMacSSid *ibss_ssid, + uint8_t sap_channel) +{ + struct mgmt_beacon_probe_filter *filter; + enum bss_type bss_type; + uint8_t session_id; + tSirMacAddr *bssid; + + if (!session) { + pe_err("Invalid session pointer"); + return; + } + + bss_type = session->bssType; + session_id = session->peSessionId; + bssid = &session->bssId; + + if (session_id >= WLAN_MAX_VDEVS) { + pe_err("Invalid session_id %d of type %d", + session_id, bss_type); + return; + } + + filter = &mac_ctx->bcn_filter; + + if (eSIR_INFRASTRUCTURE_MODE == bss_type) { + filter->num_sta_sessions++; + sir_copy_mac_addr(filter->sta_bssid[session_id], *bssid); + pe_debug("Set filter for STA Session %d bssid "QDF_MAC_ADDR_FMT, + session_id, QDF_MAC_ADDR_REF(*bssid)); + } else if (eSIR_IBSS_MODE == bss_type) { + if (!ibss_ssid) { + pe_err("IBSS Type with NULL SSID"); + goto done; + } + filter->num_ibss_sessions++; + filter->ibss_ssid[session_id].length = ibss_ssid->length; + qdf_mem_copy(&filter->ibss_ssid[session_id].ssId, + ibss_ssid->ssId, + ibss_ssid->length); + pe_debug("Set filter for IBSS session %d ssid %s", + session_id, ibss_ssid->ssId); + } else if (eSIR_INFRA_AP_MODE == bss_type) { + if (!sap_channel) { + pe_err("SAP Type with invalid channel"); + goto done; + } + filter->num_sap_sessions++; + filter->sap_channel[session_id] = sap_channel; + pe_debug("Set filter for SAP session %d channel %d", + session_id, sap_channel); + } + +done: + pe_debug("sta %d ibss %d sap %d", + filter->num_sta_sessions, filter->num_ibss_sessions, + filter->num_sap_sessions); +} + +void lim_reset_bcn_probe_filter(struct mac_context *mac_ctx, + struct pe_session *session) +{ + struct mgmt_beacon_probe_filter *filter; + enum bss_type bss_type; + uint8_t session_id; + + if (!session) { + pe_err("Invalid session pointer"); + return; + } + + bss_type = session->bssType; + session_id = session->peSessionId; + + if (session_id >= WLAN_MAX_VDEVS) { + pe_err("Invalid session_id %d of type %d", + session_id, bss_type); + return; + } + + filter = &mac_ctx->bcn_filter; + + if (eSIR_INFRASTRUCTURE_MODE == bss_type) { + if (filter->num_sta_sessions) + filter->num_sta_sessions--; + qdf_mem_zero(&filter->sta_bssid[session_id], + sizeof(tSirMacAddr)); + pe_debug("Cleared STA Filter for session %d", session_id); + } else if (eSIR_IBSS_MODE == bss_type) { + if (filter->num_ibss_sessions) + filter->num_ibss_sessions--; + filter->ibss_ssid[session_id].length = 0; + qdf_mem_zero(&filter->ibss_ssid[session_id].ssId, + WLAN_SSID_MAX_LEN); + pe_debug("Cleared IBSS Filter for session %d", session_id); + } else if (eSIR_INFRA_AP_MODE == bss_type) { + if (filter->num_sap_sessions) + filter->num_sap_sessions--; + filter->sap_channel[session_id] = 0; + pe_debug("Cleared SAP Filter for session %d", session_id); + } + + pe_debug("sta %d ibss %d sap %d", + filter->num_sta_sessions, filter->num_ibss_sessions, + filter->num_sap_sessions); +} + +void lim_update_bcn_probe_filter(struct mac_context *mac_ctx, + struct pe_session *session) +{ + struct mgmt_beacon_probe_filter *filter; + enum bss_type bss_type; + uint8_t session_id; + + if (!session) { + pe_err("Invalid session pointer"); + return; + } + + bss_type = session->bssType; + session_id = session->peSessionId; + + if (session_id >= WLAN_MAX_VDEVS) { + pe_err("Invalid session_id %d of type %d", + session_id, bss_type); + return; + } + + filter = &mac_ctx->bcn_filter; + + if (eSIR_INFRA_AP_MODE == bss_type) { + filter->sap_channel[session_id] = wlan_reg_freq_to_chan( + mac_ctx->pdev, session->curr_op_freq); + pe_debug("Updated SAP Filter for session %d channel %d", + session_id, filter->sap_channel[session_id]); + } else { + pe_debug("Invalid session type %d session id %d", + bss_type, session_id); + } + + pe_debug("sta %d ibss %d sap %d", + filter->num_sta_sessions, filter->num_ibss_sessions, + filter->num_sap_sessions); +} + +struct pe_session *pe_create_session(struct mac_context *mac, + uint8_t *bssid, uint8_t *sessionId, + uint16_t numSta, enum bss_type bssType, + uint8_t vdev_id, enum QDF_OPMODE opmode) +{ + QDF_STATUS status; + uint8_t i; + struct pe_session *session_ptr; + struct wlan_objmgr_vdev *vdev; + + for (i = 0; i < mac->lim.maxBssId; i++) { + /* Find first free room in session table */ + if (mac->lim.gpSession[i].valid == true) + continue; + break; + } + + if (i == mac->lim.maxBssId) { + pe_err("Session can't be created. Reached max sessions"); + return NULL; + } + + session_ptr = &mac->lim.gpSession[i]; + qdf_mem_zero((void *)session_ptr, sizeof(struct pe_session)); + /* Allocate space for Station Table for this session. */ + session_ptr->dph.dphHashTable.pHashTable = + qdf_mem_malloc(sizeof(tpDphHashNode) * (numSta + 1)); + if (!session_ptr->dph.dphHashTable.pHashTable) + return NULL; + + session_ptr->dph.dphHashTable.pDphNodeArray = + pe_get_session_dph_node_array(i); + session_ptr->dph.dphHashTable.size = numSta + 1; + dph_hash_table_init(mac, &session_ptr->dph.dphHashTable); + session_ptr->gpLimPeerIdxpool = qdf_mem_malloc( + sizeof(*(session_ptr->gpLimPeerIdxpool)) * + lim_get_peer_idxpool_size(numSta, bssType)); + if (!session_ptr->gpLimPeerIdxpool) + goto free_dp_hash_table; + + session_ptr->freePeerIdxHead = 0; + session_ptr->freePeerIdxTail = 0; + session_ptr->gLimNumOfCurrentSTAs = 0; + /* Copy the BSSID to the session table */ + sir_copy_mac_addr(session_ptr->bssId, bssid); + if (bssType == eSIR_MONITOR_MODE) + sir_copy_mac_addr(mac->lim.gpSession[i].self_mac_addr, bssid); + session_ptr->valid = true; + /* Initialize the SME and MLM states to IDLE */ + session_ptr->limMlmState = eLIM_MLM_IDLE_STATE; + session_ptr->limSmeState = eLIM_SME_IDLE_STATE; + session_ptr->limCurrentAuthType = eSIR_OPEN_SYSTEM; + pe_init_beacon_params(mac, &mac->lim.gpSession[i]); + session_ptr->is11Rconnection = false; +#ifdef FEATURE_WLAN_ESE + session_ptr->isESEconnection = false; +#endif + session_ptr->isFastTransitionEnabled = false; + session_ptr->isFastRoamIniFeatureEnabled = false; + *sessionId = i; + session_ptr->peSessionId = i; + session_ptr->bssType = bssType; + session_ptr->opmode = opmode; + session_ptr->gLimPhyMode = WNI_CFG_PHY_MODE_11G; + /* Initialize CB mode variables when session is created */ + session_ptr->htSupportedChannelWidthSet = 0; + session_ptr->htRecommendedTxWidthSet = 0; + session_ptr->htSecondaryChannelOffset = 0; +#ifdef FEATURE_WLAN_TDLS + qdf_mem_zero(session_ptr->peerAIDBitmap, + sizeof(session_ptr->peerAIDBitmap)); + session_ptr->tdls_prohibited = false; + session_ptr->tdls_chan_swit_prohibited = false; +#endif + lim_update_tdls_set_state_for_fw(session_ptr, true); + session_ptr->fWaitForProbeRsp = 0; + session_ptr->fIgnoreCapsChange = 0; + session_ptr->ignore_assoc_disallowed = mac->ignore_assoc_disallowed; + session_ptr->is_session_obss_color_collision_det_enabled = + mac->mlme_cfg->obss_ht40.obss_color_collision_offload_enabled; + + pe_debug("Create PE session: %d opmode %d vdev_id %d BSSID: "QDF_MAC_ADDR_FMT" Max No of STA: %d", + *sessionId, opmode, vdev_id, QDF_MAC_ADDR_REF(bssid), + numSta); + + if (eSIR_INFRA_AP_MODE == bssType || eSIR_IBSS_MODE == bssType) { + session_ptr->pSchProbeRspTemplate = + qdf_mem_malloc(SIR_MAX_PROBE_RESP_SIZE); + session_ptr->pSchBeaconFrameBegin = + qdf_mem_malloc(SIR_MAX_BEACON_SIZE); + session_ptr->pSchBeaconFrameEnd = + qdf_mem_malloc(SIR_MAX_BEACON_SIZE); + if ((!session_ptr->pSchProbeRspTemplate) + || (!session_ptr->pSchBeaconFrameBegin) + || (!session_ptr->pSchBeaconFrameEnd)) { + goto free_session_attrs; + } + } + + /* + * Get vdev object from soc which automatically increments + * reference count. + */ + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, + vdev_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) { + pe_err("vdev is NULL for vdev_id: %u", vdev_id); + goto free_session_attrs; + } + session_ptr->vdev = vdev; + session_ptr->vdev_id = vdev_id; + session_ptr->mac_ctx = mac; + + if (eSIR_INFRASTRUCTURE_MODE == bssType) + lim_ft_open(mac, &mac->lim.gpSession[i]); + + if (eSIR_MONITOR_MODE == bssType) + lim_ft_open(mac, &mac->lim.gpSession[i]); + + if (eSIR_INFRA_AP_MODE == bssType) { + session_ptr->old_protection_state = 0; + session_ptr->is_session_obss_offload_enabled = false; + session_ptr->is_obss_reset_timer_initialized = false; + + status = qdf_mc_timer_init(&session_ptr-> + protection_fields_reset_timer, + QDF_TIMER_TYPE_SW, + pe_reset_protection_callback, + (void *)&mac->lim.gpSession[i]); + + if (QDF_IS_STATUS_ERROR(status)) + pe_err("cannot create protection fields reset timer"); + else + session_ptr->is_obss_reset_timer_initialized = true; + + qdf_wake_lock_create(&session_ptr->ap_ecsa_wakelock, + "ap_ecsa_wakelock"); + qdf_runtime_lock_init(&session_ptr->ap_ecsa_runtime_lock); + status = qdf_mc_timer_init(&session_ptr->ap_ecsa_timer, + QDF_TIMER_TYPE_WAKE_APPS, + lim_process_ap_ecsa_timeout, + (void *)&mac->lim.gpSession[i]); + if (status != QDF_STATUS_SUCCESS) + pe_err("cannot create ap_ecsa_timer"); + } + if (session_ptr->opmode == QDF_STA_MODE) + session_ptr->is_session_obss_color_collision_det_enabled = + mac->mlme_cfg->obss_ht40.bss_color_collision_det_sta; + pe_init_fils_info(session_ptr); + pe_init_pmf_comeback_timer(mac, session_ptr); + session_ptr->ht_client_cnt = 0; + /* following is invalid value since seq number is 12 bit */ + session_ptr->prev_auth_seq_num = 0xFFFF; + + return &mac->lim.gpSession[i]; + +free_session_attrs: + qdf_mem_free(session_ptr->gpLimPeerIdxpool); + qdf_mem_free(session_ptr->pSchProbeRspTemplate); + qdf_mem_free(session_ptr->pSchBeaconFrameBegin); + qdf_mem_free(session_ptr->pSchBeaconFrameEnd); + + session_ptr->gpLimPeerIdxpool = NULL; + session_ptr->pSchProbeRspTemplate = NULL; + session_ptr->pSchBeaconFrameBegin = NULL; + session_ptr->pSchBeaconFrameEnd = NULL; + +free_dp_hash_table: + qdf_mem_free(session_ptr->dph.dphHashTable.pHashTable); + qdf_mem_zero(session_ptr->dph.dphHashTable.pDphNodeArray, + sizeof(struct sDphHashNode) * (SIR_SAP_MAX_NUM_PEERS + 1)); + + session_ptr->dph.dphHashTable.pHashTable = NULL; + session_ptr->dph.dphHashTable.pDphNodeArray = NULL; + session_ptr->valid = false; + + return NULL; +} + +/*-------------------------------------------------------------------------- + \brief pe_find_session_by_bssid() - looks up the PE session given the BSSID. + + This function returns the session context and the session ID if the session + corresponding to the given BSSID is found in the PE session table. + + \param mac - pointer to global adapter context + \param bssid - BSSID of the session + \param sessionId -session ID is returned here, if session is found. + + \return struct pe_session * - pointer to the session context or NULL if session is not found. + + \sa + --------------------------------------------------------------------------*/ +struct pe_session *pe_find_session_by_bssid(struct mac_context *mac, uint8_t *bssid, + uint8_t *sessionId) +{ + uint8_t i; + + for (i = 0; i < mac->lim.maxBssId; i++) { + /* If BSSID matches return corresponding tables address */ + if ((mac->lim.gpSession[i].valid) + && (sir_compare_mac_addr(mac->lim.gpSession[i].bssId, + bssid))) { + *sessionId = i; + return &mac->lim.gpSession[i]; + } + } + + return NULL; + +} + +struct pe_session *pe_find_session_by_vdev_id(struct mac_context *mac, + uint8_t vdev_id) +{ + uint8_t i; + + for (i = 0; i < mac->lim.maxBssId; i++) { + /* If BSSID matches return corresponding tables address */ + if ((mac->lim.gpSession[i].valid) && + (mac->lim.gpSession[i].vdev_id == vdev_id)) + return &mac->lim.gpSession[i]; + } + pe_debug("Session lookup fails for vdev_id: %d", vdev_id); + + return NULL; +} + +struct pe_session +*pe_find_session_by_vdev_id_and_state(struct mac_context *mac, + uint8_t vdev_id, + enum eLimMlmStates lim_state) +{ + uint8_t i; + + for (i = 0; i < mac->lim.maxBssId; i++) { + if (mac->lim.gpSession[i].valid && + mac->lim.gpSession[i].vdev_id == vdev_id && + mac->lim.gpSession[i].limMlmState == lim_state) + return &mac->lim.gpSession[i]; + } + pe_debug("Session lookup fails for vdev_id: %d, mlm state: %d", + vdev_id, lim_state); + + return NULL; +} + +/*-------------------------------------------------------------------------- + \brief pe_find_session_by_session_id() - looks up the PE session given the session ID. + + This function returns the session context if the session + corresponding to the given session ID is found in the PE session table. + + \param mac - pointer to global adapter context + \param sessionId -session ID for which session context needs to be looked up. + + \return struct pe_session * - pointer to the session context or NULL if session is not found. + + \sa + --------------------------------------------------------------------------*/ +struct pe_session *pe_find_session_by_session_id(struct mac_context *mac, + uint8_t sessionId) +{ + if (sessionId >= mac->lim.maxBssId) { + pe_err("Invalid sessionId: %d", sessionId); + return NULL; + } + + if (mac->lim.gpSession[sessionId].valid) + return &mac->lim.gpSession[sessionId]; + + return NULL; +} + +#ifdef WLAN_FEATURE_11W +static void lim_clear_pmfcomeback_timer(struct pe_session *session) +{ + if (session->opmode != QDF_STA_MODE) + return; + + pe_debug("deinit pmf comeback timer for vdev %d", session->vdev_id); + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&session->pmf_retry_timer)) + qdf_mc_timer_stop(&session->pmf_retry_timer); + qdf_mc_timer_destroy(&session->pmf_retry_timer); + session->pmf_retry_timer_info.retried = false; +} +#else +static void lim_clear_pmfcomeback_timer(struct pe_session *session) +{ +} +#endif + +/** + * pe_delete_session() - deletes the PE session given the session ID. + * @mac_ctx: pointer to global adapter context + * @session: session to be deleted. + * + * Deletes the given PE session + * + * Return: void + */ +void pe_delete_session(struct mac_context *mac_ctx, struct pe_session *session) +{ + uint16_t i = 0; + uint16_t n; + TX_TIMER *timer_ptr; + struct wlan_objmgr_vdev *vdev; + + if (!session || (session && !session->valid)) { + pe_debug("session already deleted or not valid"); + return; + } + + pe_debug("Delete PE session: %d opmode: %d vdev_id: %d BSSID: "QDF_MAC_ADDR_FMT, + session->peSessionId, session->opmode, session->vdev_id, + QDF_MAC_ADDR_REF(session->bssId)); + + lim_reset_bcn_probe_filter(mac_ctx, session); + lim_sae_auth_cleanup_retry(mac_ctx, session->vdev_id); + + /* Restore default failure timeout */ + if (session->defaultAuthFailureTimeout) { + pe_debug("Restore default failure timeout"); + if (cfg_in_range(CFG_AUTH_FAILURE_TIMEOUT, + session->defaultAuthFailureTimeout)) + mac_ctx->mlme_cfg->timeouts.auth_failure_timeout = + session->defaultAuthFailureTimeout; + else + mac_ctx->mlme_cfg->timeouts.auth_failure_timeout = + cfg_default(CFG_AUTH_FAILURE_TIMEOUT); + } + + for (n = 0; n < (mac_ctx->lim.maxStation + 1); n++) { + timer_ptr = &mac_ctx->lim.lim_timers.gpLimCnfWaitTimer[n]; + if (session->peSessionId == timer_ptr->sessionId) + if (true == tx_timer_running(timer_ptr)) + tx_timer_deactivate(timer_ptr); + } + + if (LIM_IS_AP_ROLE(session)) { + qdf_runtime_lock_deinit(&session->ap_ecsa_runtime_lock); + qdf_wake_lock_destroy(&session->ap_ecsa_wakelock); + qdf_mc_timer_stop(&session->protection_fields_reset_timer); + qdf_mc_timer_destroy(&session->protection_fields_reset_timer); + session->dfsIncludeChanSwIe = 0; + qdf_mc_timer_stop(&session->ap_ecsa_timer); + qdf_mc_timer_destroy(&session->ap_ecsa_timer); + lim_del_pmf_sa_query_timer(mac_ctx, session); + } + + /* Delete FT related information */ + lim_ft_cleanup(mac_ctx, session); + if (session->pLimStartBssReq) { + qdf_mem_free(session->pLimStartBssReq); + session->pLimStartBssReq = NULL; + } + + if (session->lim_join_req) { + qdf_mem_free(session->lim_join_req); + session->lim_join_req = NULL; + } + + if (session->pLimReAssocReq) { + qdf_mem_free(session->pLimReAssocReq); + session->pLimReAssocReq = NULL; + } + + if (session->pLimMlmJoinReq) { + qdf_mem_free(session->pLimMlmJoinReq); + session->pLimMlmJoinReq = NULL; + } + + if (session->dph.dphHashTable.pHashTable) { + qdf_mem_free(session->dph.dphHashTable.pHashTable); + session->dph.dphHashTable.pHashTable = NULL; + } + + if (session->dph.dphHashTable.pDphNodeArray) { + qdf_mem_zero(session->dph.dphHashTable.pDphNodeArray, + sizeof(struct sDphHashNode) * + (SIR_SAP_MAX_NUM_PEERS + 1)); + session->dph.dphHashTable.pDphNodeArray = NULL; + } + + if (session->gpLimPeerIdxpool) { + qdf_mem_free(session->gpLimPeerIdxpool); + session->gpLimPeerIdxpool = NULL; + } + + if (session->beacon) { + qdf_mem_free(session->beacon); + session->beacon = NULL; + session->bcnLen = 0; + } + + if (session->assoc_req) { + qdf_mem_free(session->assoc_req); + session->assoc_req = NULL; + session->assocReqLen = 0; + } + + if (session->assocRsp) { + qdf_mem_free(session->assocRsp); + session->assocRsp = NULL; + session->assocRspLen = 0; + } + + if (session->parsedAssocReq) { + tpSirAssocReq tmp_ptr = NULL; + /* Cleanup the individual allocation first */ + for (i = 0; i < session->dph.dphHashTable.size; i++) { + if (!session->parsedAssocReq[i]) + continue; + tmp_ptr = ((tpSirAssocReq) + (session->parsedAssocReq[i])); + if (tmp_ptr->assocReqFrame) { + qdf_mem_free(tmp_ptr->assocReqFrame); + tmp_ptr->assocReqFrame = NULL; + tmp_ptr->assocReqFrameLength = 0; + } + qdf_mem_free(session->parsedAssocReq[i]); + session->parsedAssocReq[i] = NULL; + } + /* Cleanup the whole block */ + qdf_mem_free(session->parsedAssocReq); + session->parsedAssocReq = NULL; + } + if (session->limAssocResponseData) { + qdf_mem_free(session->limAssocResponseData); + session->limAssocResponseData = NULL; + } + if (session->pLimMlmReassocRetryReq) { + qdf_mem_free(session->pLimMlmReassocRetryReq); + session->pLimMlmReassocRetryReq = NULL; + } + if (session->pLimMlmReassocReq) { + qdf_mem_free(session->pLimMlmReassocReq); + session->pLimMlmReassocReq = NULL; + } + + if (session->pSchProbeRspTemplate) { + qdf_mem_free(session->pSchProbeRspTemplate); + session->pSchProbeRspTemplate = NULL; + } + + if (session->pSchBeaconFrameBegin) { + qdf_mem_free(session->pSchBeaconFrameBegin); + session->pSchBeaconFrameBegin = NULL; + } + + if (session->pSchBeaconFrameEnd) { + qdf_mem_free(session->pSchBeaconFrameEnd); + session->pSchBeaconFrameEnd = NULL; + } + + /* Must free the buffer before peSession invalid */ + if (session->add_ie_params.probeRespData_buff) { + qdf_mem_free(session->add_ie_params.probeRespData_buff); + session->add_ie_params.probeRespData_buff = NULL; + session->add_ie_params.probeRespDataLen = 0; + } + if (session->add_ie_params.assocRespData_buff) { + qdf_mem_free(session->add_ie_params.assocRespData_buff); + session->add_ie_params.assocRespData_buff = NULL; + session->add_ie_params.assocRespDataLen = 0; + } + if (session->add_ie_params.probeRespBCNData_buff) { + qdf_mem_free(session->add_ie_params.probeRespBCNData_buff); + session->add_ie_params.probeRespBCNData_buff = NULL; + session->add_ie_params.probeRespBCNDataLen = 0; + } + pe_delete_fils_info(session); + lim_clear_pmfcomeback_timer(session); + session->valid = false; + + session->mac_ctx = NULL; + + qdf_mem_zero(session->WEPKeyMaterial, + sizeof(session->WEPKeyMaterial)); + + if (session->access_policy_vendor_ie) + qdf_mem_free(session->access_policy_vendor_ie); + + session->access_policy_vendor_ie = NULL; + + if (LIM_IS_AP_ROLE(session)) + lim_check_and_reset_protection_params(mac_ctx); + + vdev = session->vdev; + session->vdev = NULL; + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); +} + +/*-------------------------------------------------------------------------- + \brief pe_find_session_by_peer_sta() - looks up the PE session given the Station Address. + + This function returns the session context and the session ID if the session + corresponding to the given station address is found in the PE session table. + + \param mac - pointer to global adapter context + \param sa - Peer STA Address of the session + \param sessionId -session ID is returned here, if session is found. + + \return struct pe_session * - pointer to the session context or NULL if session is not found. + + \sa + --------------------------------------------------------------------------*/ + +struct pe_session *pe_find_session_by_peer_sta(struct mac_context *mac, uint8_t *sa, + uint8_t *sessionId) +{ + uint8_t i; + tpDphHashNode pSta; + uint16_t aid; + + for (i = 0; i < mac->lim.maxBssId; i++) { + if ((mac->lim.gpSession[i].valid)) { + pSta = + dph_lookup_hash_entry(mac, sa, &aid, + &mac->lim.gpSession[i].dph. + dphHashTable); + if (pSta) { + *sessionId = i; + return &mac->lim.gpSession[i]; + } + } + } + + pe_debug("Session lookup fails for Peer StaId: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sa)); + return NULL; +} + +/** + * pe_find_session_by_scan_id() - looks up the PE session for given scan id + * @mac_ctx: pointer to global adapter context + * @scan_id: scan id + * + * looks up the PE session for given scan id + * + * Return: pe session entry for given scan id if found else NULL + */ +struct pe_session *pe_find_session_by_scan_id(struct mac_context *mac_ctx, + uint32_t scan_id) +{ + uint8_t i; + + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + if ((mac_ctx->lim.gpSession[i].valid) && + (mac_ctx->lim.gpSession[i].ftPEContext.pFTPreAuthReq) && + (mac_ctx->lim.gpSession[i].ftPEContext.pFTPreAuthReq + ->scan_id == scan_id)) { + return &mac_ctx->lim.gpSession[i]; + } + } + return NULL; +} + +/** + * pe_get_active_session_count() - function to return active pe session count + * + * @mac_ctx: pointer to global mac structure + * + * returns number of active pe session count + * + * Return: 0 if there are no active sessions else return number of active + * sessions + */ +uint8_t pe_get_active_session_count(struct mac_context *mac_ctx) +{ + uint8_t i, active_session_count = 0; + + for (i = 0; i < mac_ctx->lim.maxBssId; i++) + if (mac_ctx->lim.gpSession[i].valid) + active_session_count++; + + return active_session_count; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_session_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_session_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..64d99d21237bb1efc92db9211d4b92e3fef710e7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_session_utils.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + \file lim_session_utils.c + \brief implementation for lim Session Utility APIs + \author Sunit Bhatia + ========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "ani_global.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "lim_session_utils.h" +#include "lim_utils.h" + +/** + * lim_is_chan_switch_running() - check if channel switch is happening + * @mac_ctx: Global MAC context. + * + * Return: 1 - if channel switch is happening on any session. + * 0 - if channel switch is not happening. + **/ +uint8_t lim_is_chan_switch_running(struct mac_context *mac_ctx) +{ + uint8_t i; + + for (i = 0; i < mac_ctx->lim.maxBssId; i++) + if (mac_ctx->lim.gpSession[i].valid && + mac_ctx->lim.gpSession[i].gLimSpecMgmt.dot11hChanSwState + == eLIM_11H_CHANSW_RUNNING) + return 1; + return 0; +} + +/** + * lim_is_in_mcc() - check if device is in MCC + * @mac_ctx: Global MAC context. + * + * Return: true - if in MCC. + * false - Not in MCC + **/ +uint8_t lim_is_in_mcc(struct mac_context *mac_ctx) +{ + uint8_t i; + uint32_t freq = 0; + uint32_t curr_oper_freq = 0; + + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + /* + * if another session is valid and it is on different channel + * it is an off channel operation. + */ + if ((mac_ctx->lim.gpSession[i].valid)) { + curr_oper_freq = + mac_ctx->lim.gpSession[i].curr_op_freq; + if (curr_oper_freq == 0) + continue; + if (freq == 0) + freq = curr_oper_freq; + else if (freq != curr_oper_freq) + return true; + } + } + return false; +} + +/** + * pe_get_current_stas_count() - Total stations associated on all sessions. + * @mac_ctx: Global MAC context. + * + * Return: true - Number of stations active on all sessions. + **/ +uint8_t pe_get_current_stas_count(struct mac_context *mac_ctx) +{ + uint8_t i; + uint8_t stacount = 0; + + for (i = 0; i < mac_ctx->lim.maxBssId; i++) + if (mac_ctx->lim.gpSession[i].valid == true) + stacount += + mac_ctx->lim.gpSession[i].gLimNumOfCurrentSTAs; + return stacount; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_session_utils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_session_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..3079f655db2919aaf475542fc7a59ac4179586fa --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_session_utils.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2012-2015, 2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__LIM_SESSION_UTILS_H) +#define __LIM_SESSION_UTILS_H + +uint8_t lim_is_chan_switch_running(struct mac_context *mac); +uint8_t lim_is_in_mcc(struct mac_context *mac); +uint8_t pe_get_current_stas_count(struct mac_context *mac); + +#endif /* #if !defined( __LIM_SESSION_UTILS_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_sme_req_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_sme_req_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..c682624cf7accecdf7f2e6e65c4b74c668f80246 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_sme_req_utils.c @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_sme_req_utils.cc contains the utility functions + * for processing SME request messages. + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * 05/26/10 js WPA handling in (Re)Assoc frames + * + */ + +#include "wni_api.h" +#include "wni_cfg.h" +#include "sir_api.h" +#include "sch_api.h" +#include "utils_api.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "lim_ser_des_utils.h" +#include "lim_sme_req_utils.h" + +/** + * lim_is_rsn_ie_valid_in_sme_req_message() + * + * @mac_ctx Pointer to Global MAC structure + * @rsn_ie Pointer to received RSN IE + * + * This function is called to verify if the RSN IE received in various SME_REQ + * messages is valid or not + * + * Return: true when RSN IE is valid, false otherwise + * + */ +static uint8_t +lim_is_rsn_ie_valid_in_sme_req_message(struct mac_context *mac_ctx, + tpSirRSNie rsn_ie) +{ + uint8_t start = 0, privacy; + uint32_t val; + int len; + + privacy = mac_ctx->mlme_cfg->wep_params.is_privacy_enabled; + + val = mac_ctx->mlme_cfg->feature_flags.enable_rsn; + if (rsn_ie->length && (!privacy || !val)) { + /* Privacy & RSN not enabled in CFG. + * In order to allow mixed mode for Guest access + * allow BSS creation/join with no Privacy capability + * yet advertising WPA IE + */ + pe_debug("RSN ie len: %d PRIVACY: %d RSN: %d", + rsn_ie->length, privacy, val); + } + + if (!rsn_ie->length) + return true; + + if ((rsn_ie->rsnIEdata[0] != DOT11F_EID_RSN) +#ifdef FEATURE_WLAN_WAPI + && (rsn_ie->rsnIEdata[0] != DOT11F_EID_WAPI) +#endif + && (rsn_ie->rsnIEdata[0] != DOT11F_EID_WPA)) { + pe_err("RSN/WPA/WAPI EID: %d not [%d || %d]", + rsn_ie->rsnIEdata[0], DOT11F_EID_RSN, + DOT11F_EID_WPA); + return false; + } + + len = rsn_ie->length; + start = 0; + while (len > 0) { + switch (rsn_ie->rsnIEdata[start]) { + case DOT11F_EID_RSN: + /* Check validity of RSN IE */ + if ((rsn_ie->rsnIEdata[start + 1] > + DOT11F_IE_RSN_MAX_LEN) + || (rsn_ie->rsnIEdata[start + 1] < + DOT11F_IE_RSN_MIN_LEN)) { + pe_err("RSN IE len: %d not [%d,%d]", + rsn_ie->rsnIEdata[start + 1], + DOT11F_IE_RSN_MIN_LEN, + DOT11F_IE_RSN_MAX_LEN); + return false; + } + break; + case DOT11F_EID_WPA: + /* Check validity of WPA IE */ + if (WLAN_MAX_IE_LEN <= start) + break; + + if (start <= (WLAN_MAX_IE_LEN - sizeof(uint32_t))) + val = sir_read_u32((uint8_t *) & + rsn_ie->rsnIEdata[start + 2]); + + if ((rsn_ie->rsnIEdata[start + 1] < + DOT11F_IE_WPA_MIN_LEN) + || (rsn_ie->rsnIEdata[start + 1] > + DOT11F_IE_WPA_MAX_LEN) + || (SIR_MAC_WPA_OUI != val)) { + pe_err("WPA IE len: %d not [%d,%d] OR data 0x%x not 0x%x", + rsn_ie->rsnIEdata[start + 1], + DOT11F_IE_WPA_MIN_LEN, + DOT11F_IE_WPA_MAX_LEN, + val, SIR_MAC_WPA_OUI); + return false; + } + break; +#ifdef FEATURE_WLAN_WAPI + case DOT11F_EID_WAPI: + if ((rsn_ie->rsnIEdata[start + 1] > + DOT11F_IE_WAPI_MAX_LEN) + || (rsn_ie->rsnIEdata[start + 1] < + DOT11F_IE_WAPI_MIN_LEN)) { + pe_err("WAPI IE len: %d not [%d,%d]", + rsn_ie->rsnIEdata[start + 1], + DOT11F_IE_WAPI_MIN_LEN, + DOT11F_IE_WAPI_MAX_LEN); + return false; + } + break; +#endif + default: + /* we will never be here, simply for completeness */ + return false; + } /* end of switch */ + /* EID + length field + length */ + start += 2 + rsn_ie->rsnIEdata[start + 1]; + len -= start; + } /* end while loop */ + return true; +} /*** end lim_is_rs_nie_valid_in_sme_req_message() ***/ + +/** + * lim_is_addie_valid_in_sme_req_message() + * + ***FUNCTION: + * This function is called to verify if the Add IE + * received in various SME_REQ messages is valid or not + * + ***LOGIC: + * Add IE validity checks are performed on only length + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param pWSCie Pointer to received WSC IE + * @return true when WSC IE is valid, false otherwise + */ + +static uint8_t +lim_is_addie_valid_in_sme_req_message(struct mac_context *mac, tpSirAddie pAddie) +{ + int left = pAddie->length; + uint8_t *ptr = pAddie->addIEdata; + uint8_t elem_id, elem_len; + + if (left == 0) + return true; + + while (left >= 2) { + elem_id = ptr[0]; + elem_len = ptr[1]; + left -= 2; + if (elem_len > left) { + pe_err("Invalid Add IEs eid: %d elem_len: %d left: %d", + elem_id, elem_len, left); + return false; + } + + left -= elem_len; + ptr += (elem_len + 2); + } + /* there shouldn't be any left byte */ + + return true; +} /*** end lim_is_addie_valid_in_sme_req_message() ***/ + +/** + * lim_set_rs_nie_wp_aiefrom_sme_start_bss_req_message() - to set rsnie/wpaie + * + * @mac_ctx : Pointer to Global MAC structure + * @rsn_ie : Pointer to received RSN IE + * @session : Pointer to pe session + * + * This function is called to verify if the RSN IE received in various + * SME_REQ messages is valid or not. RSN IE validity checks are performed in + * this function + * + * Return: true when RSN IE is valid, false otherwise + */ +uint8_t +lim_set_rs_nie_wp_aiefrom_sme_start_bss_req_message(struct mac_context *mac_ctx, + tpSirRSNie rsn_ie, + struct pe_session *session) +{ + uint32_t ret; + uint8_t wpa_idx = 0; + uint32_t val; + bool privacy; + + privacy = mac_ctx->mlme_cfg->wep_params.is_privacy_enabled; + + val = mac_ctx->mlme_cfg->feature_flags.enable_rsn; + if (rsn_ie->length && (!privacy || !val)) { + /* + * Privacy & RSN not enabled in CFG. + * In order to allow mixed mode for Guest access + * allow BSS creation/join with no Privacy capability + * yet advertising WPA IE + */ + pe_debug("RSN ie len: %d but PRIVACY: %d RSN: %d", + rsn_ie->length, privacy, val); + } + + if (!rsn_ie->length) + return true; + + if ((rsn_ie->rsnIEdata[0] != WLAN_ELEMID_RSN) && + (rsn_ie->rsnIEdata[0] != SIR_MAC_WPA_EID)) { + pe_err("RSN/WPA EID: %d not [%d || %d]", + rsn_ie->rsnIEdata[0], WLAN_ELEMID_RSN, + SIR_MAC_WPA_EID); + return false; + } + /* Check validity of RSN IE */ + if ((rsn_ie->rsnIEdata[0] == WLAN_ELEMID_RSN) && + (rsn_ie->rsnIEdata[1] < SIR_MAC_RSN_IE_MIN_LENGTH)) { + pe_err("RSN IE len: %d not [%d,%d]", + rsn_ie->rsnIEdata[1], SIR_MAC_RSN_IE_MIN_LENGTH, + WLAN_MAX_IE_LEN); + return false; + } + + if (rsn_ie->length > rsn_ie->rsnIEdata[1] + 2) { + if (rsn_ie->rsnIEdata[0] != WLAN_ELEMID_RSN) { + pe_err("First byte: %d in rsnIEdata isn't RSN_EID", + rsn_ie->rsnIEdata[1]); + return false; + } + pe_debug("WPA IE is present along with WPA2 IE"); + wpa_idx = 2 + rsn_ie->rsnIEdata[1]; + } else if ((rsn_ie->length == rsn_ie->rsnIEdata[1] + 2) && + (rsn_ie->rsnIEdata[0] == WLAN_ELEMID_RSN)) { + pe_debug("Only RSN IE is present"); + ret = dot11f_unpack_ie_rsn(mac_ctx, &rsn_ie->rsnIEdata[2], + rsn_ie->rsnIEdata[1], + &session->gStartBssRSNIe, false); + if (!DOT11F_SUCCEEDED(ret)) { + pe_err("unpack failed, ret: %d", ret); + return false; + } + return true; + } else if ((rsn_ie->length == rsn_ie->rsnIEdata[1] + 2) + && (rsn_ie->rsnIEdata[0] == SIR_MAC_WPA_EID)) { + pe_debug("Only WPA IE is present"); + ret = dot11f_unpack_ie_wpa(mac_ctx, &rsn_ie->rsnIEdata[6], + rsn_ie->rsnIEdata[1] - 4, + &session->gStartBssWPAIe, false); + if (!DOT11F_SUCCEEDED(ret)) { + pe_err("unpack failed, ret: %d", ret); + return false; + } + return true; + } + /* Check validity of WPA IE */ + if (wpa_idx + 6 >= WLAN_MAX_IE_LEN) + return false; + + val = sir_read_u32((uint8_t *)&rsn_ie->rsnIEdata[wpa_idx + 2]); + if ((rsn_ie->rsnIEdata[wpa_idx] == SIR_MAC_WPA_EID) + && ((rsn_ie->rsnIEdata[wpa_idx + 1] < SIR_MAC_WPA_IE_MIN_LENGTH) + || (SIR_MAC_WPA_OUI != val))) { + pe_err("WPA IE len: %d not [%d,%d] OR data 0x%x not 0x%x", + rsn_ie->rsnIEdata[1], + SIR_MAC_RSN_IE_MIN_LENGTH, + WLAN_MAX_IE_LEN, val, + SIR_MAC_WPA_OUI); + return false; + } else { + /* Both RSN and WPA IEs are present */ + ret = dot11f_unpack_ie_rsn(mac_ctx, &rsn_ie->rsnIEdata[2], + rsn_ie->rsnIEdata[1], + &session->gStartBssRSNIe, false); + if (!DOT11F_SUCCEEDED(ret)) { + pe_err("unpack failed, ret: %d", ret); + return false; + } + ret = dot11f_unpack_ie_wpa(mac_ctx, + &rsn_ie->rsnIEdata[wpa_idx + 6], + rsn_ie->rsnIEdata[wpa_idx + 1] - 4, + &session->gStartBssWPAIe, false); + if (!DOT11F_SUCCEEDED(ret)) { + pe_err("unpack failed, ret: %d", ret); + return false; + } + } + return true; +} + +/** + * lim_is_bss_descr_valid_in_sme_req_message() + * + ***FUNCTION: + * This function is called to verify if the BSS Descr + * received in various SME_REQ messages is valid or not + * + ***LOGIC: + * BSS Descritipion validity checks are performed in this function + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param pBssDescr Pointer to received Bss Descritipion + * @return true when BSS description is valid, false otherwise + */ + +static uint8_t +lim_is_bss_descr_valid_in_sme_req_message(struct mac_context *mac, + struct bss_description *pBssDescr) +{ + uint8_t valid = true; + + if (QDF_IS_ADDR_BROADCAST(pBssDescr->bssId) || !pBssDescr->chan_freq) + valid = false; + + return valid; +} /*** end lim_is_bss_descr_valid_in_sme_req_message() ***/ + +bool lim_is_sme_start_bss_req_valid(struct mac_context *mac_ctx, + struct start_bss_req *start_bss_req) +{ + uint8_t i = 0; + tSirMacRateSet *opr_rates = &start_bss_req->operationalRateSet; + + switch (start_bss_req->bssType) { + case eSIR_INFRASTRUCTURE_MODE: + /** + * Should not have received start BSS req with bssType + * Infrastructure on STA. + */ + pe_warn("Invalid bssType: %d in eWNI_SME_START_BSS_REQ", + start_bss_req->bssType); + return false; + break; + case eSIR_IBSS_MODE: + break; + case eSIR_INFRA_AP_MODE: + break; + case eSIR_NDI_MODE: + break; + default: + /** + * Should not have received start BSS req with bssType + * other than Infrastructure/IBSS. + */ + pe_warn("Invalid bssType: %d in eWNI_SME_START_BSS_REQ", + start_bss_req->bssType); + return false; + } + + if (start_bss_req->bssType == eSIR_IBSS_MODE + && (!start_bss_req->ssId.length + || start_bss_req->ssId.length > WLAN_SSID_MAX_LEN)) { + pe_warn("Invalid SSID length in eWNI_SME_START_BSS_REQ"); + return false; + } + + if (!lim_is_rsn_ie_valid_in_sme_req_message(mac_ctx, + &start_bss_req->rsnIE)) + return false; + + if (start_bss_req->nwType != eSIR_11A_NW_TYPE + && start_bss_req->nwType != eSIR_11B_NW_TYPE + && start_bss_req->nwType != eSIR_11G_NW_TYPE) + return false; + + if (start_bss_req->nwType == eSIR_11A_NW_TYPE) { + for (i = 0; i < opr_rates->numRates; i++) { + if (sirIsArate(opr_rates->rate[i] & 0x7F)) + continue; + + pe_warn("Invalid operational 11A rates"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_WARN, + opr_rates->rate, + opr_rates->numRates); + return false; + } + return true; + } + /* check if all the rates in the opr rate set are legal 11G rates */ + if (start_bss_req->nwType == eSIR_11G_NW_TYPE) { + for (i = 0; i < opr_rates->numRates; i++) { + if (sirIsGrate(opr_rates->rate[i] & 0x7F)) + continue; + + pe_warn("Invalid operational 11G rates"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_WARN, + opr_rates->rate, + opr_rates->numRates); + return false; + } + return true; + } + + for (i = 0; i < opr_rates->numRates; i++) { + if (sirIsBrate(opr_rates->rate[i] & 0x7F)) + continue; + + pe_warn("Invalid operational 11B rates"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_WARN, + opr_rates->rate, + opr_rates->numRates); + return false; + } + return true; +} + +uint8_t lim_is_sme_join_req_valid(struct mac_context *mac, + struct join_req *pJoinReq) +{ + uint8_t valid = true; + + /* + * If force_rsne_override is enabled that mean User has provided the + * test RSNIE which need to be send as it is in assoc req and thus RSNIE + * validity is not required. + */ + if (!pJoinReq->force_rsne_override && + !lim_is_rsn_ie_valid_in_sme_req_message(mac, &pJoinReq->rsnIE)) { + pe_err("received SME_JOIN_REQ with invalid RSNIE"); + valid = false; + goto end; + } + + if (!lim_is_addie_valid_in_sme_req_message(mac, &pJoinReq->addIEScan)) { + pe_err("received SME_JOIN_REQ with invalid additional IE for scan"); + valid = false; + goto end; + } + + if (!lim_is_addie_valid_in_sme_req_message(mac, &pJoinReq->addIEAssoc)) { + pe_err("received SME_JOIN_REQ with invalid additional IE for assoc"); + valid = false; + goto end; + } + + if (!lim_is_bss_descr_valid_in_sme_req_message(mac, &pJoinReq->bssDescription)) { + /* / Received eWNI_SME_JOIN_REQ with invalid BSS Info */ + /* Log the event */ + pe_err("received SME_JOIN_REQ with invalid bssInfo"); + + valid = false; + goto end; + } + + /* + Reject Join Req if the Self Mac Address and + the Ap's Mac Address is same + */ + if (!qdf_mem_cmp((uint8_t *)pJoinReq->self_mac_addr, + (uint8_t *)pJoinReq->bssDescription.bssId, + (uint8_t) (sizeof(tSirMacAddr)))) { + /* Log the event */ + pe_err("received SME_JOIN_REQ with Self Mac and BSSID Same"); + + valid = false; + goto end; + } + +end: + return valid; +} /*** end lim_is_sme_join_req_valid() ***/ + +bool lim_is_sme_disassoc_req_valid(struct mac_context *mac, + struct disassoc_req *disassoc_req, + struct pe_session *pe_session) +{ + if (qdf_is_macaddr_group(&disassoc_req->peer_macaddr) && + !qdf_is_macaddr_broadcast(&disassoc_req->peer_macaddr)) + return false; + + return true; +} /*** end lim_is_sme_disassoc_req_valid() ***/ + +bool lim_is_sme_disassoc_cnf_valid(struct mac_context *mac, + struct disassoc_cnf *disassoc_cnf, + struct pe_session *pe_session) +{ + if (qdf_is_macaddr_group(&disassoc_cnf->peer_macaddr)) + return false; + + return true; +} /*** end lim_is_sme_disassoc_cnf_valid() ***/ + +bool lim_is_sme_deauth_req_valid(struct mac_context *mac, + struct deauth_req *deauth_req, + struct pe_session *pe_session) +{ + if (qdf_is_macaddr_group(&deauth_req->peer_macaddr) && + !qdf_is_macaddr_broadcast(&deauth_req->peer_macaddr)) + return false; + + return true; +} /*** end lim_is_sme_deauth_req_valid() ***/ + +bool lim_is_sme_set_context_req_valid(struct mac_context *mac, + struct set_context_req *set_context_req) +{ + uint8_t i = 0; + uint8_t valid = true; + tpSirKeys key = set_context_req->keyMaterial.key; + + if ((set_context_req->keyMaterial.edType != eSIR_ED_WEP40) && + (set_context_req->keyMaterial.edType != eSIR_ED_WEP104) && + (set_context_req->keyMaterial.edType != eSIR_ED_NONE) && +#ifdef FEATURE_WLAN_WAPI + (set_context_req->keyMaterial.edType != eSIR_ED_WPI) && +#endif + !set_context_req->keyMaterial.numKeys) { + /** + * No keys present in case of TKIP or CCMP + * Log error. + */ + pe_warn("No keys present in SME_SETCONTEXT_REQ for edType: %d", + set_context_req->keyMaterial.edType); + + valid = false; + goto end; + } + + if (set_context_req->keyMaterial.numKeys && + (set_context_req->keyMaterial.edType == eSIR_ED_NONE)) { + /** + * Keys present in case of no ED policy + * Log error. + */ + pe_warn("Keys present in SME_SETCONTEXT_REQ for edType: %d", + set_context_req->keyMaterial.edType); + + valid = false; + goto end; + } + + if (set_context_req->keyMaterial.edType >= eSIR_ED_NOT_IMPLEMENTED) { + /** + * Invalid edType in the message + * Log error. + */ + pe_warn("Invalid edType: %d in SME_SETCONTEXT_REQ", + set_context_req->keyMaterial.edType); + + valid = false; + goto end; + } else if (set_context_req->keyMaterial.edType > eSIR_ED_NONE) { + bool privacy; + + privacy = mac->mlme_cfg->wep_params.is_privacy_enabled; + + if (!privacy) { + /** + * Privacy is not enabled + * In order to allow mixed mode for Guest access + * allow BSS creation/join with no Privacy capability + * yet advertising WPA IE + */ + pe_debug("Privacy is not enabled, yet non-None EDtype: %d in SME_SETCONTEXT_REQ", + set_context_req->keyMaterial.edType); + } + } + + for (i = 0; i < set_context_req->keyMaterial.numKeys; i++) { + if (((set_context_req->keyMaterial.edType == eSIR_ED_WEP40) && + (key->keyLength != 5)) || + ((set_context_req->keyMaterial.edType == eSIR_ED_WEP104) && + (key->keyLength != 13)) || + ((set_context_req->keyMaterial.edType == eSIR_ED_TKIP) && + (key->keyLength != 32)) || +#ifdef FEATURE_WLAN_WAPI + ((set_context_req->keyMaterial.edType == eSIR_ED_WPI) && + (key->keyLength != 32)) || +#endif + ((set_context_req->keyMaterial.edType == eSIR_ED_GCMP) && + (key->keyLength != 16)) || + ((set_context_req->keyMaterial.edType == eSIR_ED_GCMP_256) && + (key->keyLength != 32)) || + ((set_context_req->keyMaterial.edType == eSIR_ED_CCMP) && + (key->keyLength != 16))) { + /** + * Invalid key length for a given ED type + * Log error. + */ + pe_warn("Invalid keyLength: %d for edType: %d in SME_SETCONTEXT_REQ", + key->keyLength, + set_context_req->keyMaterial.edType); + + valid = false; + goto end; + } + key++; + } + +end: + return valid; +} /*** end lim_is_sme_set_context_req_valid() ***/ + +/** + * lim_is_sme_stop_bss_req_valid() + * + ***FUNCTION: + * This function is called by lim_process_sme_req_messages() upon + * receiving SME_STOP_BSS_REQ message from application. + * + ***LOGIC: + * Message validity checks are performed in this function + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param pMsg - Pointer to received SME_STOP_BSS_REQ message + * @return true when received SME_STOP_BSS_REQ is formatted correctly + * false otherwise + */ + +uint8_t lim_is_sme_stop_bss_req_valid(uint32_t *pMsg) +{ + uint8_t valid = true; + + return valid; +} /*** end lim_is_sme_stop_bss_req_valid() ***/ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_sme_req_utils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_sme_req_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..6b1117ba543f04490ff5fbd574a2a11bbb8b2656 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_sme_req_utils.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2011-2012,2014-2015,2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_sme_req_utils.h contains the utility definitions + * LIM uses while processing SME request messages. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ +#ifndef __LIM_SME_REQ_UTILS_H +#define __LIM_SME_REQ_UTILS_H + +#include "sir_api.h" +#include "lim_types.h" + +/* LIM SME request messages related utility functions */ + +/** + * lim_is_sme_start_bss_req_valid() - To validate sme start bss request + * @mac_ctx: Pointer to Global MAC structure + * @start_bss_req: Pointer to received SME_START_BSS_REQ message + * + * This function is called by lim_process_sme_req_messages() upon + * receiving SME_START_BSS_REQ message from application. + * + * Return: true when received SME_START_BSS_REQ is formatted correctly false + * otherwise + */ +bool lim_is_sme_start_bss_req_valid(struct mac_context *mac_ctx, + struct start_bss_req *start_bss_req); + +uint8_t lim_set_rs_nie_wp_aiefrom_sme_start_bss_req_message(struct mac_context *, + tpSirRSNie, struct pe_session *); + +/** + * lim_is_sme_join_req_valid() - Verify join request message is valid + * @mac: Pointer to Global MAC structure + * @pJoinReq: Pointer to received SME_JOIN_REQ message + * + * This function is called by lim_process_sme_req_messages() upon + * receiving SME_JOIN_REQ message from application. + * + * Return: true when received SME_JOIN_REQ is formatted correctly + * false otherwise + */ +uint8_t lim_is_sme_join_req_valid(struct mac_context *mac, + struct join_req *pJoinReq); + +/** + * lim_is_sme_disassoc_req_valid() - Validate disassoc req message + * @mac: Pointer to Global MAC structure + * @disassoc_req: Pointer to received SME_DISASSOC_REQ message + * @pe_session: Pointer to the PE session + * + * This function is called by lim_process_sme_req_messages() upon + * receiving SME_DISASSOC_REQ message from application. + * + * Return: true When received SME_DISASSOC_REQ is formatted correctly + * false otherwise + */ +bool lim_is_sme_disassoc_req_valid(struct mac_context *mac, + struct disassoc_req *disassoc_req, + struct pe_session *pe_session); + +/** + * lim_is_sme_deauth_req_valid() - Validate deauth req message + * @mac: Pointer to Global MAC structure + * @disassoc_req: Pointer to received SME_DEAUTH_REQ message + * @pe_session: Pointer to the PE session + * + * This function is called by lim_process_sme_req_messages() upon + * receiving SME_DEAUTH_REQ message from application. + * + * Return: true When received SME_DEAUTH_REQ is formatted correctly + * false otherwise + */ +bool lim_is_sme_deauth_req_valid(struct mac_context *mac, + struct deauth_req *deauth_req, + struct pe_session *pe_session); + +/** + * lim_is_sme_set_context_req_valid() - Validate set context req message + * @mac: Pointer to Global MAC structure + * @dset_context_req: Pointer to received SME_SET_CONTEXT_REQ message + * + * This function is called by lim_process_sme_req_messages() upon + * receiving SME_SET_CONTEXT_REQ message from application. + * + * Return: true when received SME_SET_CONTEXT_REQ is formatted correctly + * false otherwise + */ +bool lim_is_sme_set_context_req_valid(struct mac_context *, + struct set_context_req *set_context_req); + +uint8_t lim_is_sme_stop_bss_req_valid(uint32_t *); + +/** + * lim_is_sme_disassoc_cnf_valid() - Validate disassoc cnf message + * @mac: Pointer to Global MAC structure + * @disassoc_cnf: Pointer to received SME_DISASSOC_CNF message + * @pe_session: Pointer to the PE session + * + * This function is called by lim_process_sme_req_messages() upon + * receiving SME_DISASSOC_CNF message from application. + * + * Return: true When received SME_DISASSOC_CNF is formatted correctly + * false otherwise + */ +bool lim_is_sme_disassoc_cnf_valid(struct mac_context *mac, + struct disassoc_cnf *disassoc_cnf, + struct pe_session *pe_session); + +#endif /* __LIM_SME_REQ_UTILS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_timer_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_timer_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..17e5cf5c524a114f203c25c0ba5e7f674782ffd1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_timer_utils.c @@ -0,0 +1,938 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_timer_utils.cc contains the utility functions + * LIM uses for handling various timers. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_assoc_utils.h" +#include "lim_security_utils.h" +#include "wlan_mlme_public_struct.h" +#include + +/* channel Switch Timer in ticks */ +#define LIM_CHANNEL_SWITCH_TIMER_TICKS 1 +/* Lim Quite timer in ticks */ +#define LIM_QUIET_TIMER_TICKS 100 +/* Lim Quite BSS timer interval in ticks */ +#define LIM_QUIET_BSS_TIMER_TICK 100 +/* Lim KeepAlive timer default (3000)ms */ +#define LIM_KEEPALIVE_TIMER_MS 3000 +/* Lim JoinProbeRequest Retry timer default (200)ms */ +#define LIM_JOIN_PROBE_REQ_TIMER_MS 200 +/* Lim Periodic Auth Retry timer default 60 ms */ +#define LIM_AUTH_RETRY_TIMER_MS 60 + +/* + * SAE auth timer of 5secs. This is required for duration of entire SAE + * authentication. + */ +#define LIM_AUTH_SAE_TIMER_MS 5000 + +static bool lim_create_non_ap_timers(struct mac_context *mac) +{ + uint32_t cfgValue; + /* Create Channel Switch Timer */ + if (tx_timer_create(mac, &mac->lim.lim_timers.gLimChannelSwitchTimer, + "CHANNEL SWITCH TIMER", + lim_channel_switch_timer_handler, 0, + LIM_CHANNEL_SWITCH_TIMER_TICKS, + 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("failed to create Ch Switch timer"); + return false; + } + + cfgValue = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.join_failure_timeout); + /* Create Join failure timer and activate it later */ + if (tx_timer_create(mac, &mac->lim.lim_timers.gLimJoinFailureTimer, + "JOIN FAILURE TIMEOUT", + lim_timer_handler, SIR_LIM_JOIN_FAIL_TIMEOUT, + cfgValue, 0, + TX_NO_ACTIVATE) != TX_SUCCESS) { + /* / Could not create Join failure timer. */ + /* Log error */ + pe_err("could not create Join failure timer"); + return false; + } + /* Send unicast probe req frame every 200 ms */ + if (tx_timer_create(mac, + &mac->lim.lim_timers.gLimPeriodicJoinProbeReqTimer, + "Periodic Join Probe Request Timer", + lim_timer_handler, + SIR_LIM_PERIODIC_JOIN_PROBE_REQ_TIMEOUT, + SYS_MS_TO_TICKS(LIM_JOIN_PROBE_REQ_TIMER_MS), 0, + TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("could not create Periodic Join Probe Request tmr"); + return false; + } + + /* Send Auth frame every 60 ms */ + if ((tx_timer_create(mac, + &mac->lim.lim_timers.g_lim_periodic_auth_retry_timer, + "Periodic AUTH Timer", + lim_timer_handler, SIR_LIM_AUTH_RETRY_TIMEOUT, + SYS_MS_TO_TICKS(LIM_AUTH_RETRY_TIMER_MS), 0, + TX_NO_ACTIVATE)) != TX_SUCCESS) { + pe_err("could not create Periodic AUTH Timer"); + return false; + } + + cfgValue = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.assoc_failure_timeout); + /* Create Association failure timer and activate it later */ + if (tx_timer_create(mac, &mac->lim.lim_timers.gLimAssocFailureTimer, + "ASSOC FAILURE TIMEOUT", + lim_assoc_failure_timer_handler, LIM_ASSOC, + cfgValue, 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("could not create Association failure timer"); + return false; + } + + cfgValue = SYS_MS_TO_TICKS(mac->mlme_cfg->timeouts.addts_rsp_timeout); + + /* Create Addts response timer and activate it later */ + if (tx_timer_create(mac, &mac->lim.lim_timers.gLimAddtsRspTimer, + "ADDTS RSP TIMEOUT", + lim_addts_response_timer_handler, + SIR_LIM_ADDTS_RSP_TIMEOUT, + cfgValue, 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("could not create Addts response timer"); + return false; + } + + cfgValue = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.auth_failure_timeout); + /* Create Auth failure timer and activate it later */ + if (tx_timer_create(mac, &mac->lim.lim_timers.gLimAuthFailureTimer, + "AUTH FAILURE TIMEOUT", + lim_timer_handler, + SIR_LIM_AUTH_FAIL_TIMEOUT, + cfgValue, 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("could not create Auth failure timer"); + return false; + } + + /* Change timer to reactivate it in future */ + cfgValue = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.probe_after_hb_fail_timeout); + if (tx_timer_create(mac, &mac->lim.lim_timers.gLimProbeAfterHBTimer, + "Probe after Heartbeat TIMEOUT", + lim_timer_handler, + SIR_LIM_PROBE_HB_FAILURE_TIMEOUT, + cfgValue, 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("unable to create ProbeAfterHBTimer"); + return false; + } + + /* + * SAE auth timer of 5secs. This is required for duration of entire SAE + * authentication. + */ + if ((tx_timer_create(mac, + &mac->lim.lim_timers.sae_auth_timer, + "SAE AUTH Timer", + lim_timer_handler, SIR_LIM_AUTH_SAE_TIMEOUT, + SYS_MS_TO_TICKS(LIM_AUTH_SAE_TIMER_MS), 0, + TX_NO_ACTIVATE)) != TX_SUCCESS) { + pe_err("could not create SAE AUTH Timer"); + return false; + } + + return true; +} +/** + * lim_create_timers() + * + * @mac : Pointer to Global MAC structure + * + * This function is called upon receiving + * 1. SME_START_REQ for STA in ESS role + * 2. SME_START_BSS_REQ for AP role & STA in IBSS role + * + * @return : status of operation + */ + +uint32_t lim_create_timers(struct mac_context *mac) +{ + uint32_t cfgValue, i = 0; + + pe_debug("Creating Timers used by LIM module in Role: %d", + mac->lim.gLimSystemRole); + /* Create timers required for host roaming feature */ + if (TX_SUCCESS != lim_create_timers_host_roam(mac)) + return TX_TIMER_ERROR; + + if (mac->lim.gLimSystemRole != eLIM_AP_ROLE) + if (false == lim_create_non_ap_timers(mac)) + goto err_timer; + + cfgValue = mac->mlme_cfg->sta.wait_cnf_timeout; + cfgValue = SYS_MS_TO_TICKS(cfgValue); + for (i = 0; i < (mac->lim.maxStation + 1); i++) { + if (tx_timer_create(mac, + &mac->lim.lim_timers.gpLimCnfWaitTimer[i], + "CNF_MISS_TIMEOUT", + lim_cnf_wait_tmer_handler, + (uint32_t) i, cfgValue, + 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("Cannot create CNF wait timer"); + goto err_timer; + } + } + + /* Alloc and init table for the preAuth timer list */ + cfgValue = mac->mlme_cfg->lfr.max_num_pre_auth; + mac->lim.gLimPreAuthTimerTable.numEntry = cfgValue; + mac->lim.gLimPreAuthTimerTable.pTable = + qdf_mem_malloc(cfgValue * sizeof(tLimPreAuthNode *)); + + if (!mac->lim.gLimPreAuthTimerTable.pTable) + goto err_timer; + + for (i = 0; i < cfgValue; i++) { + mac->lim.gLimPreAuthTimerTable.pTable[i] = + qdf_mem_malloc(sizeof(tLimPreAuthNode)); + if (!mac->lim.gLimPreAuthTimerTable.pTable[i]) { + mac->lim.gLimPreAuthTimerTable.numEntry = 0; + goto err_timer; + } + } + + lim_init_pre_auth_timer_table(mac, &mac->lim.gLimPreAuthTimerTable); + pe_debug("alloc and init table for preAuth timers"); + + cfgValue = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.olbc_detect_timeout); + if (tx_timer_create(mac, &mac->lim.lim_timers.gLimUpdateOlbcCacheTimer, + "OLBC UPDATE CACHE TIMEOUT", + lim_update_olbc_cache_timer_handler, + SIR_LIM_UPDATE_OLBC_CACHEL_TIMEOUT, cfgValue, + cfgValue, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("Cannot create update OLBC cache tmr"); + goto err_timer; + } + + cfgValue = 1000; + cfgValue = SYS_MS_TO_TICKS(cfgValue); + if (tx_timer_create(mac, &mac->lim.lim_timers.gLimDisassocAckTimer, + "DISASSOC ACK TIMEOUT", + lim_timer_handler, SIR_LIM_DISASSOC_ACK_TIMEOUT, + cfgValue, 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("could not DISASSOC ACK TIMEOUT timer"); + goto err_timer; + } + + cfgValue = 1000; + cfgValue = SYS_MS_TO_TICKS(cfgValue); + if (tx_timer_create(mac, &mac->lim.lim_timers.gLimDeauthAckTimer, + "DISASSOC ACK TIMEOUT", + lim_timer_handler, SIR_LIM_DEAUTH_ACK_TIMEOUT, + cfgValue, 0, TX_NO_ACTIVATE) != TX_SUCCESS) { + pe_err("could not create DEAUTH ACK TIMEOUT timer"); + goto err_timer; + } + + return TX_SUCCESS; + +err_timer: + lim_delete_timers_host_roam(mac); + tx_timer_delete(&mac->lim.lim_timers.gLimDeauthAckTimer); + tx_timer_delete(&mac->lim.lim_timers.gLimDisassocAckTimer); + tx_timer_delete(&mac->lim.lim_timers.gLimUpdateOlbcCacheTimer); + while (((int32_t)-- i) >= 0) { + tx_timer_delete(&mac->lim.lim_timers.gpLimCnfWaitTimer[i]); + } + tx_timer_delete(&mac->lim.lim_timers.gLimProbeAfterHBTimer); + tx_timer_delete(&mac->lim.lim_timers.gLimAuthFailureTimer); + tx_timer_delete(&mac->lim.lim_timers.gLimAddtsRspTimer); + tx_timer_delete(&mac->lim.lim_timers.gLimAssocFailureTimer); + tx_timer_delete(&mac->lim.lim_timers.gLimJoinFailureTimer); + tx_timer_delete(&mac->lim.lim_timers.gLimPeriodicJoinProbeReqTimer); + tx_timer_delete(&mac->lim.lim_timers.g_lim_periodic_auth_retry_timer); + tx_timer_delete(&mac->lim.lim_timers.gLimChannelSwitchTimer); + tx_timer_delete(&mac->lim.lim_timers.sae_auth_timer); + + if (mac->lim.gLimPreAuthTimerTable.pTable) { + for (i = 0; i < mac->lim.gLimPreAuthTimerTable.numEntry; i++) + qdf_mem_free(mac->lim.gLimPreAuthTimerTable.pTable[i]); + qdf_mem_free(mac->lim.gLimPreAuthTimerTable.pTable); + mac->lim.gLimPreAuthTimerTable.pTable = NULL; + } + return TX_TIMER_ERROR; +} /****** end lim_create_timers() ******/ + +/** + * lim_timer_handler() + * + ***FUNCTION: + * This function is called upon + * 1. MIN_CHANNEL, MAX_CHANNEL timer expiration during scanning + * 2. JOIN_FAILURE timer expiration while joining a BSS + * 3. AUTH_FAILURE timer expiration while authenticating with a peer + * 4. Heartbeat timer expiration on STA + * 5. Background scan timer expiration on STA + * 6. AID release, Pre-auth cleanup and Link monitoring timer + * expiration on AP + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param param - Message corresponding to the timer that expired + * + * @return None + */ + +void lim_timer_handler(void *pMacGlobal, uint32_t param) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0}; + struct mac_context *mac = (struct mac_context *) pMacGlobal; + + /* Prepare and post message to LIM Message Queue */ + + msg.type = (uint16_t) param; + msg.bodyptr = NULL; + msg.bodyval = 0; + + status = lim_post_msg_high_priority(mac, &msg); + if (status != QDF_STATUS_SUCCESS) + pe_err("posting message: %X to LIM failed, reason: %d", + msg.type, status); +} /****** end lim_timer_handler() ******/ + +/** + * lim_addts_response_timer_handler() + * + ***FUNCTION: + * This function is called upon Addts response timer expiration on sta + * + ***LOGIC: + * Message SIR_LIM_ADDTS_RSP_TIMEOUT is posted to gSirLimMsgQ + * when this function is executed. + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param param - pointer to pre-auth node + * + * @return None + */ + +void lim_addts_response_timer_handler(void *pMacGlobal, uint32_t param) +{ + struct scheduler_msg msg = {0}; + struct mac_context *mac = (struct mac_context *) pMacGlobal; + + /* Prepare and post message to LIM Message Queue */ + + msg.type = SIR_LIM_ADDTS_RSP_TIMEOUT; + msg.bodyval = param; + msg.bodyptr = NULL; + + lim_post_msg_api(mac, &msg); +} /****** end lim_auth_response_timer_handler() ******/ + +/** + * lim_auth_response_timer_handler() + * + ***FUNCTION: + * This function is called upon Auth response timer expiration on AP + * + ***LOGIC: + * Message SIR_LIM_AUTH_RSP_TIMEOUT is posted to gSirLimMsgQ + * when this function is executed. + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param param - pointer to pre-auth node + * + * @return None + */ + +void lim_auth_response_timer_handler(void *pMacGlobal, uint32_t param) +{ + struct scheduler_msg msg = {0}; + struct mac_context *mac = (struct mac_context *) pMacGlobal; + + /* Prepare and post message to LIM Message Queue */ + + msg.type = SIR_LIM_AUTH_RSP_TIMEOUT; + msg.bodyptr = NULL; + msg.bodyval = (uint32_t) param; + + lim_post_msg_api(mac, &msg); +} /****** end lim_auth_response_timer_handler() ******/ + +/** + * lim_assoc_failure_timer_handler() + * + * @mac_global : Pointer to Global MAC structure + * @param : Indicates whether this is assoc or reassoc failure timeout + * + * This function is called upon Re/Assoc failure timer expiration on STA. + * Message SIR_LIM_ASSOC_FAIL_TIMEOUT is posted to gSirLimMsgQ when this + * function is executed. + * + * Return void + */ +void lim_assoc_failure_timer_handler(void *mac_global, uint32_t param) +{ + struct scheduler_msg msg = {0}; + struct mac_context *mac_ctx = (struct mac_context *) mac_global; + struct pe_session *session = NULL; + + session = mac_ctx->lim.pe_session; + if (LIM_REASSOC == param && session + && session->limMlmState == eLIM_MLM_WT_FT_REASSOC_RSP_STATE) { + pe_err("Reassoc timeout happened"); + if (mac_ctx->lim.reAssocRetryAttempt < + LIM_MAX_REASSOC_RETRY_LIMIT) { + lim_send_retry_reassoc_req_frame(mac_ctx, + session->pLimMlmReassocRetryReq, session); + mac_ctx->lim.reAssocRetryAttempt++; + pe_warn("Reassoc request retry is sent %d times", + mac_ctx->lim.reAssocRetryAttempt); + return; + } else { + pe_warn("Reassoc request retry MAX: %d reached", + LIM_MAX_REASSOC_RETRY_LIMIT); + if (session->pLimMlmReassocRetryReq) { + qdf_mem_free(session->pLimMlmReassocRetryReq); + session->pLimMlmReassocRetryReq = NULL; + } + } + } + /* Prepare and post message to LIM Message Queue */ + msg.type = SIR_LIM_ASSOC_FAIL_TIMEOUT; + msg.bodyval = (uint32_t) param; + msg.bodyptr = NULL; + lim_post_msg_api(mac_ctx, &msg); +} /****** end lim_assoc_failure_timer_handler() ******/ + +/** + * lim_update_olbc_cache_timer_handler() + * + ***FUNCTION: + * This function is called upon update olbc cache timer expiration + * on STA + * + ***LOGIC: + * Message SIR_LIM_UPDATE_OLBC_CACHEL_TIMEOUT is posted to gSirLimMsgQ + * when this function is executed. + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param + * + * @return None + */ +void lim_update_olbc_cache_timer_handler(void *pMacGlobal, uint32_t param) +{ + struct scheduler_msg msg = {0}; + struct mac_context *mac = (struct mac_context *) pMacGlobal; + + /* Prepare and post message to LIM Message Queue */ + + msg.type = SIR_LIM_UPDATE_OLBC_CACHEL_TIMEOUT; + msg.bodyval = 0; + msg.bodyptr = NULL; + + lim_post_msg_api(mac, &msg); +} /****** end lim_update_olbc_cache_timer_handler() ******/ + +/** + * lim_deactivate_and_change_timer() + * + ***FUNCTION: + * This function is called to deactivate and change a timer + * for future re-activation + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param timerId - enum of timer to be deactivated and changed + * This enum is defined in lim_utils.h file + * + * @return None + */ + +void lim_deactivate_and_change_timer(struct mac_context *mac, uint32_t timerId) +{ + uint32_t val = 0; + struct pe_session * session_entry; + + switch (timerId) { + case eLIM_REASSOC_FAIL_TIMER: + case eLIM_FT_PREAUTH_RSP_TIMER: + lim_deactivate_and_change_timer_host_roam(mac, timerId); + break; + + case eLIM_ADDTS_RSP_TIMER: + mac->lim.gLimAddtsRspTimerCount++; + if (tx_timer_deactivate(&mac->lim.lim_timers.gLimAddtsRspTimer) + != TX_SUCCESS) { + /* Could not deactivate AddtsRsp Timer */ + /* Log error */ + pe_err("Unable to deactivate AddtsRsp timer"); + } + break; + + case eLIM_JOIN_FAIL_TIMER: + if (tx_timer_deactivate + (&mac->lim.lim_timers.gLimJoinFailureTimer) + != TX_SUCCESS) { + /** + * Could not deactivate Join Failure + * timer. Log error. + */ + pe_err("Unable to deactivate Join Failure timer"); + } + + val = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.join_failure_timeout); + + if (tx_timer_change(&mac->lim.lim_timers.gLimJoinFailureTimer, + val, 0) != TX_SUCCESS) { + /** + * Could not change Join Failure + * timer. Log error. + */ + pe_err("Unable to change Join Failure timer"); + } + + break; + + case eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER: + if (tx_timer_deactivate + (&mac->lim.lim_timers.gLimPeriodicJoinProbeReqTimer) + != TX_SUCCESS) { + /* Could not deactivate periodic join req Times. */ + pe_err("Unable to deactivate periodic join request timer"); + } + + val = SYS_MS_TO_TICKS(LIM_JOIN_PROBE_REQ_TIMER_MS); + if (tx_timer_change + (&mac->lim.lim_timers.gLimPeriodicJoinProbeReqTimer, + val, 0) != TX_SUCCESS) { + /* Could not change periodic join req times. */ + /* Log error */ + pe_err("Unable to change periodic join request timer"); + } + + break; + + case eLIM_AUTH_FAIL_TIMER: + if (tx_timer_deactivate + (&mac->lim.lim_timers.gLimAuthFailureTimer) + != TX_SUCCESS) { + /* Could not deactivate Auth failure timer. */ + /* Log error */ + pe_err("Unable to deactivate auth failure timer"); + } + /* Change timer to reactivate it in future */ + val = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.auth_failure_timeout); + + if (tx_timer_change(&mac->lim.lim_timers.gLimAuthFailureTimer, + val, 0) != TX_SUCCESS) { + /* Could not change Authentication failure timer. */ + /* Log error */ + pe_err("unable to change Auth failure timer"); + } + + break; + + case eLIM_AUTH_RETRY_TIMER: + + if (tx_timer_deactivate + (&mac->lim.lim_timers.g_lim_periodic_auth_retry_timer) + != TX_SUCCESS) { + /* Could not deactivate Auth Retry Timer. */ + pe_err("Unable to deactivate Auth Retry timer"); + } + session_entry = pe_find_session_by_session_id(mac, + mac->lim.lim_timers. + g_lim_periodic_auth_retry_timer.sessionId); + if (!session_entry) { + pe_debug("session does not exist for given SessionId : %d", + mac->lim.lim_timers. + g_lim_periodic_auth_retry_timer.sessionId); + break; + } + /* 3/5 of the beacon interval */ + val = (session_entry->beaconParams.beaconInterval * 3) / 5; + val = SYS_MS_TO_TICKS(val); + if (tx_timer_change + (&mac->lim.lim_timers.g_lim_periodic_auth_retry_timer, + val, 0) != TX_SUCCESS) { + /* Could not change Auth Retry timer. */ + pe_err("Unable to change Auth Retry timer"); + } + break; + + case eLIM_ASSOC_FAIL_TIMER: + if (tx_timer_deactivate + (&mac->lim.lim_timers.gLimAssocFailureTimer) != + TX_SUCCESS) { + /* Could not deactivate Association failure timer. */ + /* Log error */ + pe_err("unable to deactivate Association failure timer"); + } + /* Change timer to reactivate it in future */ + val = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.assoc_failure_timeout); + + if (tx_timer_change(&mac->lim.lim_timers.gLimAssocFailureTimer, + val, 0) != TX_SUCCESS) { + /* Could not change Association failure timer. */ + /* Log error */ + pe_err("unable to change Assoc failure timer"); + } + + break; + + case eLIM_PROBE_AFTER_HB_TIMER: + if (tx_timer_deactivate + (&mac->lim.lim_timers.gLimProbeAfterHBTimer) != + TX_SUCCESS) { + /* Could not deactivate Heartbeat timer. */ + /* Log error */ + pe_err("unable to deactivate probeAfterHBTimer"); + } else { + pe_debug("Deactivated probe after hb timer"); + } + + /* Change timer to reactivate it in future */ + val = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.probe_after_hb_fail_timeout); + + if (tx_timer_change(&mac->lim.lim_timers.gLimProbeAfterHBTimer, + val, 0) != TX_SUCCESS) { + /* Could not change HeartBeat timer. */ + /* Log error */ + pe_err("unable to change ProbeAfterHBTimer"); + } else { + pe_debug("Probe after HB timer value is changed: %u", + val); + } + + break; + + case eLIM_DISASSOC_ACK_TIMER: + if (tx_timer_deactivate( + &mac->lim.lim_timers.gLimDisassocAckTimer) != + TX_SUCCESS) { + /** + ** Could not deactivate Join Failure + ** timer. Log error. + **/ + pe_err("Unable to deactivate Disassoc ack timer"); + return; + } + val = 1000; + val = SYS_MS_TO_TICKS(val); + if (tx_timer_change(&mac->lim.lim_timers.gLimDisassocAckTimer, + val, 0) != TX_SUCCESS) { + /** + * Could not change Join Failure + * timer. Log error. + */ + pe_err("Unable to change timer"); + return; + } + break; + + case eLIM_DEAUTH_ACK_TIMER: + if (tx_timer_deactivate(&mac->lim.lim_timers.gLimDeauthAckTimer) + != TX_SUCCESS) { + /** + ** Could not deactivate Join Failure + ** timer. Log error. + **/ + pe_err("Unable to deactivate Deauth ack timer"); + return; + } + val = 1000; + val = SYS_MS_TO_TICKS(val); + if (tx_timer_change(&mac->lim.lim_timers.gLimDeauthAckTimer, + val, 0) != TX_SUCCESS) { + /** + * Could not change Join Failure + * timer. Log error. + */ + pe_err("Unable to change timer"); + return; + } + break; + + case eLIM_AUTH_SAE_TIMER: + if (tx_timer_deactivate + (&mac->lim.lim_timers.sae_auth_timer) + != TX_SUCCESS) + pe_err("Unable to deactivate SAE auth timer"); + + /* Change timer to reactivate it in future */ + val = SYS_MS_TO_TICKS(LIM_AUTH_SAE_TIMER_MS); + + if (tx_timer_change(&mac->lim.lim_timers.sae_auth_timer, + val, 0) != TX_SUCCESS) + pe_err("unable to change SAE auth timer"); + + break; + + default: + /* Invalid timerId. Log error */ + break; + } +} /****** end lim_deactivate_and_change_timer() ******/ + +/** + * lim_deactivate_and_change_per_sta_id_timer() + * + * + * @brief: This function is called to deactivate and change a per STA timer + * for future re-activation + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + * @note staId for eLIM_AUTH_RSP_TIMER is auth Node Index. + * + * @param mac - Pointer to Global MAC structure + * @param timerId - enum of timer to be deactivated and changed + * This enum is defined in lim_utils.h file + * @param staId - staId + * + * @return None + */ + +void +lim_deactivate_and_change_per_sta_id_timer(struct mac_context *mac, uint32_t timerId, + uint16_t staId) +{ + uint32_t val; + + switch (timerId) { + case eLIM_CNF_WAIT_TIMER: + + if (staId >= (mac->lim.maxStation + 1)) { + pe_err("Invalid staId = %d ", staId); + return; + } + + if (tx_timer_deactivate(&mac->lim.lim_timers.gpLimCnfWaitTimer[staId]) + != TX_SUCCESS) { + pe_err("unable to deactivate CNF wait timer"); + } + + /* Change timer to reactivate it in future */ + val = mac->mlme_cfg->sta.wait_cnf_timeout; + val = SYS_MS_TO_TICKS(val); + + if (tx_timer_change + (&mac->lim.lim_timers.gpLimCnfWaitTimer[staId], val, + val) != TX_SUCCESS) { + /* Could not change cnf timer. */ + /* Log error */ + pe_err("unable to change cnf wait timer"); + } + + break; + + case eLIM_AUTH_RSP_TIMER: + { + tLimPreAuthNode *pAuthNode; + + pAuthNode = + lim_get_pre_auth_node_from_index(mac, + &mac->lim. + gLimPreAuthTimerTable, + staId); + + if (!pAuthNode) { + pe_err("Invalid Pre Auth Index passed :%d", + staId); + break; + } + + if (tx_timer_deactivate(&pAuthNode->timer) != + TX_SUCCESS) { + /* Could not deactivate auth response timer. */ + /* Log error */ + pe_err("unable to deactivate auth response timer"); + } + /* Change timer to reactivate it in future */ + val = SYS_MS_TO_TICKS( + mac->mlme_cfg->timeouts.auth_rsp_timeout); + + if (tx_timer_change(&pAuthNode->timer, val, 0) != + TX_SUCCESS) { + /* Could not change auth rsp timer. */ + /* Log error */ + pe_err("unable to change auth rsp timer"); + } + } + break; + + default: + /* Invalid timerId. Log error */ + break; + + } +} + +/** + * lim_activate_cnf_timer() + * + ***FUNCTION: + * This function is called to activate a per STA timer + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param StaId - staId + * + * @return None + */ + +void lim_activate_cnf_timer(struct mac_context *mac, uint16_t staId, + struct pe_session *pe_session) +{ + mac->lim.lim_timers.gpLimCnfWaitTimer[staId].sessionId = + pe_session->peSessionId; + if (tx_timer_activate(&mac->lim.lim_timers.gpLimCnfWaitTimer[staId]) + != TX_SUCCESS) { + pe_err("could not activate cnf wait timer"); + } +} + +/** + * lim_activate_auth_rsp_timer() + * + ***FUNCTION: + * This function is called to activate a per STA timer + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param id - id + * + * @return None + */ + +void lim_activate_auth_rsp_timer(struct mac_context *mac, tLimPreAuthNode *pAuthNode) +{ + if (tx_timer_activate(&pAuthNode->timer) != TX_SUCCESS) { + /* / Could not activate auth rsp timer. */ + /* Log error */ + pe_err("could not activate auth rsp timer"); + } +} + +/** + * limAssocCnfWaitTmerHandler() + * + ***FUNCTION: + * This function post a message to send a disassociate frame out. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * NA + * + * @param + * + * @return None + */ + +void lim_cnf_wait_tmer_handler(void *pMacGlobal, uint32_t param) +{ + struct scheduler_msg msg = {0}; + uint32_t status_code; + struct mac_context *mac = (struct mac_context *) pMacGlobal; + + msg.type = SIR_LIM_CNF_WAIT_TIMEOUT; + msg.bodyval = (uint32_t) param; + msg.bodyptr = NULL; + + status_code = lim_post_msg_api(mac, &msg); + if (status_code != QDF_STATUS_SUCCESS) + pe_err("posting to LIM failed, reason: %d", status_code); + +} + +void lim_channel_switch_timer_handler(void *pMacGlobal, uint32_t param) +{ + struct scheduler_msg msg = {0}; + struct mac_context *mac = (struct mac_context *) pMacGlobal; + + pe_debug("ChannelSwitch Timer expired. Posting msg to LIM"); + + msg.type = SIR_LIM_CHANNEL_SWITCH_TIMEOUT; + msg.bodyval = (uint32_t) param; + msg.bodyptr = NULL; + + lim_post_msg_api(mac, &msg); +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_timer_utils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_timer_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..cf49ff253bd426d0f320d37b2d99688f2f34c564 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_timer_utils.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011-2014, 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file lim_timer_utils.h contains the utility definitions + * LIM uses for timer handling. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ +#ifndef __LIM_TIMER_UTILS_H +#define __LIM_TIMER_UTILS_H + +#include "lim_types.h" + +/* Timer related functions */ +enum { + eLIM_MIN_CHANNEL_TIMER, + eLIM_MAX_CHANNEL_TIMER, + eLIM_JOIN_FAIL_TIMER, + eLIM_AUTH_FAIL_TIMER, + eLIM_AUTH_RESP_TIMER, + eLIM_ASSOC_FAIL_TIMER, + eLIM_REASSOC_FAIL_TIMER, + eLIM_PRE_AUTH_CLEANUP_TIMER, + eLIM_CNF_WAIT_TIMER, + eLIM_AUTH_RSP_TIMER, + eLIM_UPDATE_OLBC_CACHE_TIMER, + eLIM_PROBE_AFTER_HB_TIMER, + eLIM_ADDTS_RSP_TIMER, + eLIM_CHANNEL_SWITCH_TIMER, + eLIM_WPS_OVERLAP_TIMER, + eLIM_FT_PREAUTH_RSP_TIMER, + eLIM_REMAIN_CHN_TIMER, + eLIM_DISASSOC_ACK_TIMER, + eLIM_DEAUTH_ACK_TIMER, + eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER, + eLIM_INSERT_SINGLESHOT_NOA_TIMER, + eLIM_AUTH_RETRY_TIMER, + eLIM_AUTH_SAE_TIMER +}; + +#define LIM_DISASSOC_DEAUTH_ACK_TIMEOUT 500 + +/* Timer Handler functions */ +uint32_t lim_create_timers(struct mac_context *); +void lim_timer_handler(void *, uint32_t); +void lim_auth_response_timer_handler(void *, uint32_t); +void lim_assoc_failure_timer_handler(void *, uint32_t); + +void lim_deactivate_and_change_timer(struct mac_context *, uint32_t); +void lim_cnf_wait_tmer_handler(void *, uint32_t); +void lim_deactivate_and_change_per_sta_id_timer(struct mac_context *, uint32_t, uint16_t); +void lim_activate_cnf_timer(struct mac_context *, uint16_t, struct pe_session *); +void lim_activate_auth_rsp_timer(struct mac_context *, tLimPreAuthNode *); +void lim_update_olbc_cache_timer_handler(void *, uint32_t); +void lim_addts_response_timer_handler(void *, uint32_t); +void lim_channel_switch_timer_handler(void *, uint32_t); +#endif /* __LIM_TIMER_UTILS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_trace.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_trace.c new file mode 100644 index 0000000000000000000000000000000000000000..3077b816a5bcd0d7a4ed5a6039dea58714c82012 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_trace.c @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + \file lim_trace.c + + \brief implementation for trace related APIs + + \author Sunit Bhatia + + ========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ + +#include "ani_global.h" /* for struct mac_context **/ + +#include "lim_trace.h" +#include "lim_timer_utils.h" +#include "qdf_trace.h" + +#ifdef LIM_TRACE_RECORD + +#define LIM_TRACE_MAX_SUBTYPES 14 + +static uint8_t *__lim_trace_get_timer_string(uint16_t timerId) +{ + switch (timerId) { + CASE_RETURN_STRING(eLIM_MIN_CHANNEL_TIMER); + CASE_RETURN_STRING(eLIM_MAX_CHANNEL_TIMER); + CASE_RETURN_STRING(eLIM_JOIN_FAIL_TIMER); + CASE_RETURN_STRING(eLIM_AUTH_FAIL_TIMER); + CASE_RETURN_STRING(eLIM_AUTH_RESP_TIMER); + CASE_RETURN_STRING(eLIM_ASSOC_FAIL_TIMER); + CASE_RETURN_STRING(eLIM_REASSOC_FAIL_TIMER); + CASE_RETURN_STRING(eLIM_PRE_AUTH_CLEANUP_TIMER); + CASE_RETURN_STRING(eLIM_CNF_WAIT_TIMER); + CASE_RETURN_STRING(eLIM_AUTH_RSP_TIMER); + CASE_RETURN_STRING(eLIM_UPDATE_OLBC_CACHE_TIMER); + CASE_RETURN_STRING(eLIM_PROBE_AFTER_HB_TIMER); + CASE_RETURN_STRING(eLIM_ADDTS_RSP_TIMER); + CASE_RETURN_STRING(eLIM_CHANNEL_SWITCH_TIMER); + CASE_RETURN_STRING(eLIM_WPS_OVERLAP_TIMER); + CASE_RETURN_STRING(eLIM_FT_PREAUTH_RSP_TIMER); + CASE_RETURN_STRING(eLIM_REMAIN_CHN_TIMER); + CASE_RETURN_STRING(eLIM_DISASSOC_ACK_TIMER); + CASE_RETURN_STRING(eLIM_DEAUTH_ACK_TIMER); + CASE_RETURN_STRING(eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER); + CASE_RETURN_STRING(eLIM_AUTH_RETRY_TIMER); + default: + return "UNKNOWN"; + break; + } +} + +static uint8_t *__lim_trace_get_mgmt_drop_reason_string(uint16_t dropReason) +{ + + switch (dropReason) { + CASE_RETURN_STRING(eMGMT_DROP_INFRA_BCN_IN_IBSS); + CASE_RETURN_STRING(eMGMT_DROP_INVALID_SIZE); + CASE_RETURN_STRING(eMGMT_DROP_NON_SCAN_MODE_FRAME); + CASE_RETURN_STRING(eMGMT_DROP_NOT_LAST_IBSS_BCN); + CASE_RETURN_STRING(eMGMT_DROP_NO_DROP); + CASE_RETURN_STRING(eMGMT_DROP_SCAN_MODE_FRAME); + CASE_RETURN_STRING(eMGMT_DROP_SPURIOUS_FRAME); + + default: + return "UNKNOWN"; + break; + } +} + +void lim_trace_init(struct mac_context *mac) +{ + qdf_trace_register(QDF_MODULE_ID_PE, &lim_trace_dump); +} + +void lim_trace_dump(void *mac, tp_qdf_trace_record pRecord, + uint16_t recIndex) +{ + static char *frameSubtypeStr[LIM_TRACE_MAX_SUBTYPES] = { + "Association request", + "Association response", + "Reassociation request", + "Reassociation response", + "Probe request", + "Probe response", + NULL, + NULL, + "Beacon", + "ATIM", + "Disassociation", + "Authentication", + "Deauthentication", + "Action" + }; + + switch (pRecord->code) { + case TRACE_CODE_MLM_STATE: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + "MLM State:", + lim_trace_get_mlm_state_string( + (uint16_t)pRecord->data), + pRecord->data); + break; + case TRACE_CODE_SME_STATE: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + "SME State:", + lim_trace_get_sme_state_string( + (uint16_t)pRecord->data), + pRecord->data); + break; + case TRACE_CODE_TX_MGMT: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + "TX Mgmt:", frameSubtypeStr[pRecord->data], + pRecord->data); + break; + + case TRACE_CODE_RX_MGMT: + if (LIM_TRACE_MAX_SUBTYPES <= + LIM_TRACE_GET_SUBTYPE(pRecord->data)) + pe_nofl_debug("Wrong Subtype - %d", + LIM_TRACE_GET_SUBTYPE(pRecord->data)); + else + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(%d) SN: %d", + recIndex, pRecord->qtime, pRecord->time, + pRecord->session, "RX Mgmt:", + frameSubtypeStr[LIM_TRACE_GET_SUBTYPE + (pRecord->data)], + LIM_TRACE_GET_SUBTYPE(pRecord->data), + LIM_TRACE_GET_SSN(pRecord->data)); + break; + case TRACE_CODE_RX_MGMT_DROP: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(%d)", + recIndex, pRecord->qtime, pRecord->time, + pRecord->session, "Drop RX Mgmt:", + __lim_trace_get_mgmt_drop_reason_string( + (uint16_t)pRecord->data), + pRecord->data); + break; + + case TRACE_CODE_RX_MGMT_TSF: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s0x%x(%d)", + recIndex, pRecord->qtime, pRecord->time, + pRecord->session, "RX Mgmt TSF:", " ", + pRecord->data, pRecord->data); + break; + + case TRACE_CODE_TX_COMPLETE: + pe_nofl_debug("%04d %012llu %s S%d %-14s %d", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + "TX Complete", pRecord->data); + break; + + case TRACE_CODE_TX_SME_MSG: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + "TX SME Msg:", + mac_trace_get_sme_msg_string((uint16_t)pRecord->data), + pRecord->data); + break; + case TRACE_CODE_RX_SME_MSG: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + LIM_TRACE_GET_DEFRD_OR_DROPPED( + pRecord->data) ? "Def/Drp LIM Msg:" : "RX Sme Msg:", + mac_trace_get_sme_msg_string((uint16_t)pRecord->data), + pRecord->data); + break; + + case TRACE_CODE_TX_WMA_MSG: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + "TX WMA Msg:", + mac_trace_get_wma_msg_string((uint16_t)pRecord->data), + pRecord->data); + break; + + case TRACE_CODE_RX_WMA_MSG: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + LIM_TRACE_GET_DEFRD_OR_DROPPED( + pRecord->data) ? "Def/Drp LIM Msg:" : "RX WMA Msg:", + mac_trace_get_wma_msg_string((uint16_t)pRecord->data), + pRecord->data); + break; + + case TRACE_CODE_TX_LIM_MSG: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + "TX LIM Msg:", + mac_trace_get_lim_msg_string((uint16_t)pRecord->data), + pRecord->data); + break; + case TRACE_CODE_RX_LIM_MSG: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + LIM_TRACE_GET_DEFRD_OR_DROPPED( + pRecord->data) ? "Def/Drp LIM Msg:" : "RX LIM Msg", + mac_trace_get_lim_msg_string((uint16_t)pRecord->data), + pRecord->data); + break; + case TRACE_CODE_TIMER_ACTIVATE: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + "Timer Actvtd", + __lim_trace_get_timer_string((uint16_t)pRecord->data), + pRecord->data); + break; + case TRACE_CODE_TIMER_DEACTIVATE: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", recIndex, + pRecord->qtime, pRecord->time, pRecord->session, + "Timer DeActvtd", + __lim_trace_get_timer_string((uint16_t)pRecord->data), + pRecord->data); + break; + + case TRACE_CODE_INFO_LOG: + pe_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", + recIndex, pRecord->qtime, pRecord->time, + pRecord->session, "INFORMATION_LOG", + mac_trace_get_info_log_string((uint16_t)pRecord->data), + pRecord->data); + break; + default: + pe_nofl_debug("%04d %012llu %s S%d %-14s(%d) (0x%x)", + recIndex, pRecord->qtime, pRecord->time, + pRecord->session, "Unknown Code", + pRecord->code, pRecord->data); + break; + } +} + +void mac_trace_msg_tx(struct mac_context *mac, uint8_t session, uint32_t data) +{ + + uint16_t msgId = (uint16_t) MAC_TRACE_GET_MSG_ID(data); + uint8_t module_id = (uint8_t) MAC_TRACE_GET_MODULE_ID(data); + + switch (module_id) { + case SIR_LIM_MODULE_ID: + if (msgId >= SIR_LIM_ITC_MSG_TYPES_BEGIN) + mac_trace(mac, TRACE_CODE_TX_LIM_MSG, session, data); + else + mac_trace(mac, TRACE_CODE_TX_SME_MSG, session, data); + break; + case SIR_WMA_MODULE_ID: + mac_trace(mac, TRACE_CODE_TX_WMA_MSG, session, data); + break; + default: + mac_trace(mac, module_id, session, data); + break; + } +} + +/* + * bit31: Rx message deferred or not + * bit 0-15: message ID: + */ +void mac_trace_msg_rx(struct mac_context *mac, uint8_t session, uint32_t data) +{ + uint16_t msgId = (uint16_t) MAC_TRACE_GET_MSG_ID(data); + uint8_t module_id = (uint8_t) MAC_TRACE_GET_MODULE_ID(data); + + switch (module_id) { + case SIR_LIM_MODULE_ID: + if (msgId >= SIR_LIM_ITC_MSG_TYPES_BEGIN) + mac_trace(mac, TRACE_CODE_RX_LIM_MSG, session, data); + else + mac_trace(mac, TRACE_CODE_RX_SME_MSG, session, data); + break; + case SIR_WMA_MODULE_ID: + mac_trace(mac, TRACE_CODE_RX_WMA_MSG, session, data); + break; + default: + mac_trace(mac, module_id, session, data); + break; + } +} + +uint8_t *lim_trace_get_mlm_state_string(uint32_t mlmState) +{ + switch (mlmState) { + CASE_RETURN_STRING(eLIM_MLM_OFFLINE_STATE); + CASE_RETURN_STRING(eLIM_MLM_IDLE_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_JOIN_BEACON_STATE); + CASE_RETURN_STRING(eLIM_MLM_JOINED_STATE); + CASE_RETURN_STRING(eLIM_MLM_BSS_STARTED_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_AUTH_FRAME2_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_AUTH_FRAME3_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_AUTH_FRAME4_STATE); + CASE_RETURN_STRING(eLIM_MLM_AUTH_RSP_TIMEOUT_STATE); + CASE_RETURN_STRING(eLIM_MLM_AUTHENTICATED_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ASSOC_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_REASSOC_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_ASSOCIATED_STATE); + CASE_RETURN_STRING(eLIM_MLM_REASSOCIATED_STATE); + CASE_RETURN_STRING(eLIM_MLM_LINK_ESTABLISHED_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ASSOC_CNF_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_BSS_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_DEL_BSS_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_BSS_RSP_ASSOC_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_BSS_RSP_PREASSOC_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_BSS_RSP_REASSOC_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_STA_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_DEL_STA_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ASSOC_DEL_STA_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_SET_BSS_KEY_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_SET_STA_KEY_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_SET_STA_BCASTKEY_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_FT_REASSOC_RSP_STATE); + default: + return "UNKNOWN"; + break; + } +} + +uint8_t *lim_trace_get_sme_state_string(uint32_t smeState) +{ + switch (smeState) { + + CASE_RETURN_STRING(eLIM_SME_OFFLINE_STATE); + CASE_RETURN_STRING(eLIM_SME_IDLE_STATE); + CASE_RETURN_STRING(eLIM_SME_SUSPEND_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_JOIN_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_AUTH_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_ASSOC_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_REASSOC_STATE); + CASE_RETURN_STRING(eLIM_SME_JOIN_FAILURE_STATE); + CASE_RETURN_STRING(eLIM_SME_ASSOCIATED_STATE); + CASE_RETURN_STRING(eLIM_SME_REASSOCIATED_STATE); + CASE_RETURN_STRING(eLIM_SME_LINK_EST_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_PRE_AUTH_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_DISASSOC_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_DEAUTH_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_START_BSS_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_STOP_BSS_STATE); + CASE_RETURN_STRING(eLIM_SME_NORMAL_STATE); + default: + return "UNKNOWN"; + break; + } +} + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_types.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_types.h new file mode 100644 index 0000000000000000000000000000000000000000..7c9fe0ade3e384cb5b7ded5feeb146db03cbc009 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_types.h @@ -0,0 +1,1424 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_types.h contains the definitions used by all + * all LIM modules. + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#ifndef __LIM_TYPES_H +#define __LIM_TYPES_H + +#include "wni_api.h" +#include "sir_api.h" +#include "sir_common.h" +#include "sir_mac_prot_def.h" +#include "utils_api.h" + +#include "lim_api.h" +#include "lim_trace.h" +#include "lim_send_sme_rsp_messages.h" +#include "sys_global.h" +#include "dph_global.h" +#include "parser_api.h" +#include "wma_if.h" + +#define LINK_TEST_DEFER 1 + +#define TRACE_EVENT_CNF_TIMER_DEACT 0x6600 +#define TRACE_EVENT_CNF_TIMER_ACT 0x6601 +#define TRACE_EVENT_AUTH_RSP_TIMER_DEACT 0x6602 +#define TRACE_EVENT_AUTH_RSP_TIMER_ACT 0x6603 + +/* MLM message types */ +#define LIM_MLM_MSG_START 1000 +#define LIM_MLM_SCAN_REQ LIM_MLM_MSG_START +#define LIM_MLM_SCAN_CNF (LIM_MLM_MSG_START + 1) +#define LIM_MLM_START_CNF (LIM_MLM_MSG_START + 3) +#define LIM_MLM_JOIN_REQ (LIM_MLM_MSG_START + 4) +#define LIM_MLM_JOIN_CNF (LIM_MLM_MSG_START + 5) +#define LIM_MLM_AUTH_REQ (LIM_MLM_MSG_START + 6) +#define LIM_MLM_AUTH_CNF (LIM_MLM_MSG_START + 7) +#define LIM_MLM_AUTH_IND (LIM_MLM_MSG_START + 8) +#define LIM_MLM_ASSOC_REQ (LIM_MLM_MSG_START + 9) +#define LIM_MLM_ASSOC_CNF (LIM_MLM_MSG_START + 10) +#define LIM_MLM_ASSOC_IND (LIM_MLM_MSG_START + 11) +#define LIM_MLM_DISASSOC_REQ (LIM_MLM_MSG_START + 12) +#define LIM_MLM_DISASSOC_CNF (LIM_MLM_MSG_START + 13) +#define LIM_MLM_DISASSOC_IND (LIM_MLM_MSG_START + 14) +#define LIM_MLM_REASSOC_CNF (LIM_MLM_MSG_START + 15) +#define LIM_MLM_REASSOC_IND (LIM_MLM_MSG_START + 16) +#define LIM_MLM_DEAUTH_REQ (LIM_MLM_MSG_START + 17) +#define LIM_MLM_DEAUTH_CNF (LIM_MLM_MSG_START + 18) +#define LIM_MLM_DEAUTH_IND (LIM_MLM_MSG_START + 19) +#define LIM_MLM_TSPEC_REQ (LIM_MLM_MSG_START + 20) +#define LIM_MLM_TSPEC_CNF (LIM_MLM_MSG_START + 21) +#define LIM_MLM_TSPEC_IND (LIM_MLM_MSG_START + 22) +#define LIM_MLM_SETKEYS_CNF (LIM_MLM_MSG_START + 24) +#define LIM_MLM_LINK_TEST_STOP_REQ (LIM_MLM_MSG_START + 30) +#define LIM_MLM_PURGE_STA_IND (LIM_MLM_MSG_START + 31) +/* + * Values (LIM_MLM_MSG_START + 32) through + * (LIM_MLM_MSG_START + 40) are unused. + */ + +#define LIM_WEP_IN_FC 1 +#define LIM_NO_WEP_IN_FC 0 + +#define LIM_DECRYPT_ICV_FAIL 1 + +/* / Definitions to distinquish between Association/Reassociaton */ +#define LIM_ASSOC 0 +#define LIM_REASSOC 1 + +/* / Verifies whether given mac addr matches the CURRENT Bssid */ +#define IS_CURRENT_BSSID(mac, addr, pe_session) (!qdf_mem_cmp(addr, \ + pe_session->bssId, \ + sizeof(pe_session->bssId))) +/* / Verifies whether given addr matches the REASSOC Bssid */ +#define IS_REASSOC_BSSID(mac, addr, pe_session) (!qdf_mem_cmp(addr, \ + pe_session->limReAssocbssId, \ + sizeof(pe_session->limReAssocbssId))) + +#define REQ_TYPE_REGISTRAR (0x2) +#define REQ_TYPE_WLAN_MANAGER_REGISTRAR (0x3) + +#define RESP_TYPE_ENROLLEE_INFO_ONLY (0x0) +#define RESP_TYPE_ENROLLEE_OPEN_8021X (0x1) +#define RESP_TYPE_AP (0x3) + + +#define HAL_USE_SELF_STA_REQUESTED_MASK 0x2 /* bit 1 for STA overwrite with selfSta Requested. */ + +#define HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME 0x40 /* Bit 6 will be used to control BD rate for Management frames */ +#define HAL_USE_PEER_STA_REQUESTED_MASK 0x80 /* bit 7 will be used to control frames for p2p interface */ +#define HAL_USE_PMF 0x20 + +#define LIM_DOS_PROTECTION_TIME 1000 //1000ms +#define LIM_MIN_RSSI 0 /* 0dbm */ +/* enums used by LIM are as follows */ + +enum eLimDisassocTrigger { + eLIM_HOST_DISASSOC, + eLIM_PEER_ENTITY_DISASSOC, + eLIM_LINK_MONITORING_DISASSOC, + eLIM_PROMISCUOUS_MODE_DISASSOC, + eLIM_HOST_DEAUTH, + eLIM_PEER_ENTITY_DEAUTH, + eLIM_LINK_MONITORING_DEAUTH, + eLIM_JOIN_FAILURE, + eLIM_REASSOC_REJECT, + eLIM_DUPLICATE_ENTRY +}; + +/** + * enum eChannelChangeReasonCodes - Reason code to determine the channel change + * reason + * @LIM_SWITCH_CHANNEL_REASSOC: channel switch to reassoc + * @LIM_SWITCH_CHANNEL_JOIN: switch for connect req + * @LIM_SWITCH_CHANNEL_OPERATION: Generic change channel for STA + * @LIM_SWITCH_CHANNEL_SAP_DFS: SAP channel change req + * @LIM_SWITCH_CHANNEL_HT_WIDTH: HT channel width change reg + * @LIM_SWITCH_CHANNEL_MONITOR: Monitor mode channel change req + */ +enum eChannelChangeReasonCodes { + LIM_SWITCH_CHANNEL_REASSOC, + LIM_SWITCH_CHANNEL_JOIN, + LIM_SWITCH_CHANNEL_OPERATION, + LIM_SWITCH_CHANNEL_SAP_DFS, + LIM_SWITCH_CHANNEL_HT_WIDTH, + LIM_SWITCH_CHANNEL_MONITOR, +}; + +typedef struct sLimMlmStartReq { + tSirMacSSid ssId; + enum bss_type bssType; + tSirMacAddr bssId; + tSirMacBeaconInterval beaconPeriod; + uint8_t dtimPeriod; + tSirMacCfParamSet cfParamSet; + uint32_t oper_ch_freq; + ePhyChanBondState cbMode; + tSirMacRateSet rateSet; + uint8_t sessionId; /* Added For BT-AMP Support */ + + /* Parameters reqd for new HAL (message) interface */ + tSirNwType nwType; + uint8_t htCapable; + tSirMacHTOperatingMode htOperMode; + uint8_t dualCTSProtection; + uint8_t txChannelWidthSet; + uint8_t ssidHidden; + uint8_t wps_state; + uint8_t obssProtEnabled; + uint16_t beacon_tx_rate; + uint32_t cac_duration_ms; + uint32_t dfs_regdomain; +} tLimMlmStartReq, *tpLimMlmStartReq; + +typedef struct sLimMlmStartCnf { + tSirResultCodes resultCode; + uint8_t sessionId; +} tLimMlmStartCnf, *tpLimMlmStartCnf; + +typedef struct sLimMlmJoinCnf { + tSirResultCodes resultCode; + uint16_t protStatusCode; + uint8_t sessionId; +} tLimMlmJoinCnf, *tpLimMlmJoinCnf; + +typedef struct sLimMlmAssocReq { + tSirMacAddr peerMacAddr; + uint16_t capabilityInfo; + tSirMacListenInterval listenInterval; + uint8_t sessionId; +} tLimMlmAssocReq, *tpLimMlmAssocReq; + +typedef struct sLimMlmAssocCnf { + tSirResultCodes resultCode; /* Internal status code. */ + uint16_t protStatusCode; /* Protocol Status code. */ + uint8_t sessionId; +} tLimMlmAssocCnf, *tpLimMlmAssocCnf; + +typedef struct sLimMlmAssocInd { + tSirMacAddr peerMacAddr; + uint16_t aid; + tAniAuthType authType; + enum ani_akm_type akm_type; + tAniSSID ssId; + tSirRSNie rsnIE; + tSirWAPIie wapiIE; + tSirAddie addIE; /* additional IE received from the peer, which possibly includes WSC IE and/or P2P IE. */ + tSirMacCapabilityInfo capabilityInfo; + bool spectrumMgtIndicator; + struct power_cap_info powerCap; + struct supported_channels supportedChannels; + uint8_t sessionId; + + bool WmmStaInfoPresent; + + /* Required for indicating the frames to upper layer */ + uint32_t beaconLength; + uint8_t *beaconPtr; + uint32_t assocReqLength; + uint8_t *assocReqPtr; + struct oem_channel_info chan_info; + bool ampdu; + bool sgi_enable; + bool tx_stbc; + bool rx_stbc; + tSirMacHTChannelWidth ch_width; + enum sir_sme_phy_mode mode; + uint8_t max_supp_idx; + uint8_t max_ext_idx; + uint8_t max_mcs_idx; + uint8_t rx_mcs_map; + uint8_t tx_mcs_map; + uint8_t ecsa_capable; + + tDot11fIEHTCaps ht_caps; + tDot11fIEVHTCaps vht_caps; + bool he_caps_present; + bool is_sae_authenticated; +} tLimMlmAssocInd, *tpLimMlmAssocInd; + +typedef struct sLimMlmReassocReq { + tSirMacAddr peerMacAddr; + uint16_t capabilityInfo; + tSirMacListenInterval listenInterval; + uint8_t sessionId; +} tLimMlmReassocReq, *tpLimMlmReassocReq; + +typedef struct sLimMlmReassocCnf { + tSirResultCodes resultCode; + uint16_t protStatusCode; /* Protocol Status code. */ + uint8_t sessionId; +} tLimMlmReassocCnf, *tpLimMlmReassocCnf; + +typedef struct sLimMlmAuthCnf { + tSirMacAddr peerMacAddr; + tAniAuthType authType; + tSirResultCodes resultCode; + uint16_t protStatusCode; + uint8_t sessionId; +} tLimMlmAuthCnf, *tpLimMlmAuthCnf; + +typedef struct sLimMlmDeauthReq { + struct qdf_mac_addr peer_macaddr; + uint16_t reasonCode; + uint16_t deauthTrigger; + uint16_t aid; + uint8_t sessionId; /* Added for BT-AMP SUPPORT */ + +} tLimMlmDeauthReq, *tpLimMlmDeauthReq; + +typedef struct sLimMlmDeauthCnf { + struct qdf_mac_addr peer_macaddr; + tSirResultCodes resultCode; + uint16_t deauthTrigger; + uint16_t aid; + uint8_t sessionId; +} tLimMlmDeauthCnf, *tpLimMLmDeauthCnf; + +typedef struct sLimMlmDeauthInd { + tSirMacAddr peerMacAddr; + uint16_t reasonCode; + uint16_t deauthTrigger; + uint16_t aid; +} tLimMlmDeauthInd, *tpLimMlmDeauthInd; + +typedef struct sLimMlmDisassocReq { + struct qdf_mac_addr peer_macaddr; + uint16_t reasonCode; + uint16_t disassocTrigger; + uint16_t aid; + uint8_t sessionId; +} tLimMlmDisassocReq, *tpLimMlmDisassocReq; + +typedef struct sLimMlmDisassocCnf { + tSirMacAddr peerMacAddr; + tSirResultCodes resultCode; + uint16_t disassocTrigger; + uint16_t aid; + uint8_t sessionId; +} tLimMlmDisassocCnf, *tpLimMlmDisassocCnf; + +typedef struct sLimMlmDisassocInd { + tSirMacAddr peerMacAddr; + uint16_t reasonCode; + uint16_t disassocTrigger; + uint16_t aid; + uint8_t sessionId; +} tLimMlmDisassocInd, *tpLimMlmDisassocInd; + +typedef struct sLimMlmPurgeStaInd { + tSirMacAddr peerMacAddr; + uint16_t reasonCode; + uint16_t purgeTrigger; + uint16_t aid; + uint8_t sessionId; +} tLimMlmPurgeStaInd, *tpLimMlmPurgeStaInd; + +/** + * struct sLimMlmSetKeysCnf - set key confirmation parameters + * @peer_macaddr: peer mac address + * @resultCode: Result of set key operation + * @aid: association id + * @sessionId: PE session id + * @key_len_nonzero: Keys are non-zero length + */ +typedef struct sLimMlmSetKeysCnf { + struct qdf_mac_addr peer_macaddr; + uint16_t resultCode; + uint16_t aid; + uint8_t sessionId; + bool key_len_nonzero; +} tLimMlmSetKeysCnf, *tpLimMlmSetKeysCnf; + +/* Function templates */ + +bool lim_process_sme_req_messages(struct mac_context *, struct scheduler_msg *); +void lim_process_mlm_req_messages(struct mac_context *, struct scheduler_msg *); +void lim_process_mlm_rsp_messages(struct mac_context *, uint32_t, uint32_t *); +void lim_process_sme_del_bss_rsp(struct mac_context *mac, + struct pe_session *pe_session); + +/** + * lim_process_mlm_start_cnf(): called to processes MLM_START_CNF message from + * MLM State machine. + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: A pointer to the MLM message buffer + * + * Return: None + */ +void lim_process_mlm_start_cnf(struct mac_context *mac_ctx, uint32_t *msg_buf); + +void lim_get_random_bssid(struct mac_context *mac, uint8_t *data); + +/* Function to handle HT and HT IE CFG parameter intializations */ +void handle_ht_capabilityand_ht_info(struct mac_context *mac, + struct pe_session *pe_session); + +void lim_handle_param_update(struct mac_context *mac, eUpdateIEsType cfgId); + +/* Function to apply CFG parameters before join/reassoc/start BSS */ +void lim_apply_configuration(struct mac_context *, struct pe_session *); + +/** + * lim_set_cfg_protection() - sets lim global cfg cache from the config + * @mac: global mac context + * @pesessionEntry: PE session + * + * Return none + */ +void lim_set_cfg_protection(struct mac_context *mac, struct pe_session *pesessionEntry); + +/* Function to Initialize MLM state machine on STA */ +QDF_STATUS lim_init_mlm(struct mac_context *); + +/* Function to cleanup MLM state machine */ +void lim_cleanup_mlm(struct mac_context *); + +/* Management frame handling functions */ +void lim_process_beacon_frame(struct mac_context *, uint8_t *, struct pe_session *); +void lim_process_probe_req_frame(struct mac_context *, uint8_t *, struct pe_session *); +void lim_process_probe_rsp_frame(struct mac_context *, uint8_t *, struct pe_session *); +void lim_process_probe_req_frame_multiple_bss(struct mac_context *, uint8_t *, + struct pe_session *); + +/* Process Auth frame when we have a session in progress. */ +void lim_process_auth_frame(struct mac_context *, uint8_t *, struct pe_session *); + +/** + * lim_process_auth_frame_no_session() - Process auth frame received from AP to + * which we are not connected currently. + * @mac: Pointer to global mac context + * @bd: Pointer to rx auth frame + * @body: Pointer to lim_msg->body_ptr + * + * This is possibly the pre-auth from the neighbor AP, in the same mobility + * domain or pre-authentication reply for WPA3 SAE roaming. + * This will be used in case of 11r FT. + */ +QDF_STATUS lim_process_auth_frame_no_session(struct mac_context *mac, + uint8_t *bd, void *body); + +void lim_process_assoc_req_frame(struct mac_context *, uint8_t *, uint8_t, struct pe_session *); + +/** + * lim_fill_lim_assoc_ind_params() - Initialize lim association indication + * @assoc_ind: PE association indication structure + * @mac_ctx: Pointer to Global MAC structure + * @sta_ds: station dph entry + * @session_entry: PE session entry + * + * Return: true if lim assoc ind filled successfully + */ +bool lim_fill_lim_assoc_ind_params( + tpLimMlmAssocInd assoc_ind, + struct mac_context *mac_ctx, + tpDphHashNode sta_ds, + struct pe_session *session_entry); + +/** + * lim_sae_auth_cleanup_retry() - API to cleanup sae auth frmae stored + * and deactivate the timer + * @mac_ctx: Pointer to mac context + * @vdev_id: vdev id + * + * Return: none + */ +void lim_sae_auth_cleanup_retry(struct mac_context *mac_ctx, + uint8_t vdev_id); + +/** + * lim_fill_sme_assoc_ind_params() - Initialize association indication + * @mac_ctx: Pointer to Global MAC structure + * @assoc_ind: PE association indication structure + * @sme_assoc_ind: SME association indication + * @session_entry: PE session entry + * @assoc_req_alloc: malloc memory for assoc_req or not + * + * Return: None + */ +void +lim_fill_sme_assoc_ind_params( + struct mac_context *mac_ctx, + tpLimMlmAssocInd assoc_ind, struct assoc_ind *sme_assoc_ind, + struct pe_session *session_entry, bool assoc_req_alloc); + +/** + * lim_send_mlm_assoc_ind() - Sends assoc indication to SME + * @mac_ctx: Global Mac context + * @sta_ds: Station DPH hash entry + * @session_entry: PE session entry + * + * This function sends either LIM_MLM_ASSOC_IND + * or LIM_MLM_REASSOC_IND to SME. + * + * Return: None + */ +void lim_send_mlm_assoc_ind(struct mac_context *mac, tpDphHashNode sta, + struct pe_session *pe_session); +void lim_process_assoc_rsp_frame(struct mac_context *, uint8_t *, uint8_t, struct pe_session *); +void lim_process_disassoc_frame(struct mac_context *, uint8_t *, struct pe_session *); +/* + * lim_perform_disassoc() - Actual action taken after receiving disassoc + * @mac_ctx: Global MAC context + * @frame_rssi: RSSI of the frame + * @rc: Reason code of the deauth + * @pe_session: PE session entry pointer + * @addr: BSSID from which the disassoc is received + * + * Return: None + */ +void lim_perform_disassoc(struct mac_context *mac_ctx, int32_t frame_rssi, + uint16_t rc, struct pe_session *pe_session, + tSirMacAddr addr); +/* + * lim_disassoc_tdls_peers() - Disassoc action for tdls peers + * @mac_ctx: Global MAC context + * @pe_session: PE session entry pointer + * @addr: BSSID from which the disassoc is received + * + * Return: None + */ +#ifdef FEATURE_WLAN_TDLS +void lim_disassoc_tdls_peers(struct mac_context *mac_ctx, + struct pe_session *pe_session, tSirMacAddr addr); +#else +static inline void lim_disassoc_tdls_peers(struct mac_context *mac_ctx, + struct pe_session *pe_session, tSirMacAddr addr) +{ +} +#endif +void lim_process_deauth_frame(struct mac_context *, uint8_t *, struct pe_session *); +/* + * lim_perform_deauth() - Actual action taken after receiving deauth + * @mac_ctx: Global MAC context + * @pe_session: PE session entry pointer + * @rc: Reason code of the deauth + * @addr: BSSID from which the deauth is received + * @frame_rssi: RSSI of the frame + * + * Return: None + */ +void lim_perform_deauth(struct mac_context *mac_ctx, struct pe_session *pe_session, + uint16_t rc, tSirMacAddr addr, int32_t frame_rssi); +void lim_process_action_frame(struct mac_context *, uint8_t *, struct pe_session *); +void lim_process_action_frame_no_session(struct mac_context *mac, uint8_t *pRxMetaInfo); + +void lim_populate_mac_header(struct mac_context *, uint8_t *, uint8_t, uint8_t, + tSirMacAddr, tSirMacAddr); +QDF_STATUS lim_send_probe_req_mgmt_frame(struct mac_context *, tSirMacSSid *, + tSirMacAddr, qdf_freq_t, tSirMacAddr, + uint32_t, uint16_t *, uint8_t *); + +/** + * lim_send_probe_rsp_mgmt_frame() - Send probe response + * @mac_ctx: Handle for mac context + * @peer_macaddr: Mac address of requesting peer + * @ssid: SSID for response + * @pe_session: PE session id + * @preq_p2pie: P2P IE in incoming probe request + * + * Builds and sends probe response frame to the requesting peer + * + * Return: void + */ +void +lim_send_probe_rsp_mgmt_frame(struct mac_context *mac_ctx, + tSirMacAddr peer_macaddr, + tpAniSSID ssid, + struct pe_session *pe_session, + uint8_t preq_p2pie); + +void lim_send_auth_mgmt_frame(struct mac_context *, tSirMacAuthFrameBody *, tSirMacAddr, + uint8_t, struct pe_session *); +void lim_send_assoc_req_mgmt_frame(struct mac_context *, tLimMlmAssocReq *, struct pe_session *); +#ifdef WLAN_FEATURE_HOST_ROAM +void lim_send_reassoc_req_with_ft_ies_mgmt_frame(struct mac_context *mac, + tLimMlmReassocReq *pMlmReassocReq, struct pe_session *pe_session); +void lim_send_reassoc_req_mgmt_frame(struct mac_context *, tLimMlmReassocReq *, + struct pe_session *); +/** + * lim_process_rx_scan_handler() - + * process the event for scan which is issued by LIM + * @vdev: wlan objmgr vdev pointer + * @event: scan event + * @arg: global mac context pointer + * + * Return: void + */ +void lim_process_rx_scan_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg); +#else +static inline void lim_send_reassoc_req_with_ft_ies_mgmt_frame( + struct mac_context *mac, tLimMlmReassocReq *pMlmReassocReq, + struct pe_session *pe_session) +{} +static inline void lim_send_reassoc_req_mgmt_frame(struct mac_context *mac_ctx, + tLimMlmReassocReq *reassoc_req, struct pe_session *pe_session) +{} +static inline void lim_process_rx_scan_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{} +#endif +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +/** + * lim_process_set_he_bss_color() - process the set he bss color request + * + * @mac_ctx: global mac context pointer + * @msg_buf: message buffer pointer + * + * Return: void + */ +void lim_process_set_he_bss_color(struct mac_context *mac_ctx, uint32_t *msg_buf); + +/** + * lim_process_obss_color_collision_info() - Process the obss color collision + * request. + * @mac_ctx: global mac context pointer + * @msg_buf: message buffer pointer + * + * Return: void + */ +void lim_process_obss_color_collision_info(struct mac_context *mac_ctx, + uint32_t *msg_buf); + +/** + * lim_send_obss_color_collision_cfg() - Send obss color collision cfg. + * @mac_ctx: global mac context pointer + * @session: Pointer to session + * @event_type: obss color collision detection type + * + * Return: void + */ +void lim_send_obss_color_collision_cfg(struct mac_context *mac_ctx, + struct pe_session *session, + enum wmi_obss_color_collision_evt_type + event_type); +#else +static inline void lim_process_set_he_bss_color(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{} +static inline void lim_process_obss_color_collision_info(struct mac_context *mac_ctx, + uint32_t *msg_buf) +{} +static inline void lim_send_obss_color_collision_cfg(struct mac_context *mac_ctx, + struct pe_session *session, + enum wmi_obss_color_collision_evt_type event_type) +{} +#endif +void lim_send_delts_req_action_frame(struct mac_context *mac, tSirMacAddr peer, + uint8_t wmmTspecPresent, + struct mac_ts_info * pTsinfo, + struct mac_tspec_ie * pTspecIe, + struct pe_session *pe_session); +void lim_send_addts_req_action_frame(struct mac_context *mac, tSirMacAddr peerMacAddr, + tSirAddtsReqInfo *addts, struct pe_session *); + +/** + * lim_send_assoc_rsp_mgmt_frame() - Send assoc response + * @mac_ctx: Handle for mac context + * @status_code: Status code for assoc response frame + * @aid: Association ID + * @peer_addr: Mac address of requesting peer + * @subtype: Assoc/Reassoc + * @sta: Pointer to station node + * @pe_session: PE session id. + * @tx_complete: Need tx complete callback or not + * + * Builds and sends association response frame to the requesting peer. + * + * Return: void + */ +void +lim_send_assoc_rsp_mgmt_frame( + struct mac_context *mac_ctx, + uint16_t status_code, uint16_t aid, tSirMacAddr peer_addr, + uint8_t subtype, tpDphHashNode sta, struct pe_session *pe_session, + bool tx_complete); + +void lim_send_disassoc_mgmt_frame(struct mac_context *, uint16_t, tSirMacAddr, + struct pe_session *, bool waitForAck); +void lim_send_deauth_mgmt_frame(struct mac_context *, uint16_t, tSirMacAddr, struct pe_session *, + bool waitForAck); + +/** + * lim_process_mlm_update_hidden_ssid_rsp() - process hidden ssid response + * @mac_ctx: global mac context + * @vdev_id: vdev id + * + * Return: None + */ +void lim_process_mlm_update_hidden_ssid_rsp(struct mac_context *mac_ctx, + uint8_t vdev_id); + +tSirResultCodes lim_mlm_add_bss(struct mac_context *, tLimMlmStartReq *, + struct pe_session *pe_session); + +QDF_STATUS lim_send_channel_switch_mgmt_frame(struct mac_context *, tSirMacAddr, + uint8_t, uint8_t, uint8_t, + struct pe_session *); + +QDF_STATUS lim_send_extended_chan_switch_action_frame(struct mac_context *mac_ctx, + tSirMacAddr peer, uint8_t mode, uint8_t new_op_class, + uint8_t new_channel, uint8_t count, struct pe_session *session_entry); +QDF_STATUS lim_p2p_oper_chan_change_confirm_action_frame( + struct mac_context *mac_ctx, tSirMacAddr peer, + struct pe_session *session_entry); + +QDF_STATUS lim_send_neighbor_report_request_frame(struct mac_context *, + tpSirMacNeighborReportReq, + tSirMacAddr, struct pe_session *); + +/** + * lim_send_link_report_action_frame() - Send link measurement report action + * frame in response for a link measurement request received. + * @mac: Pointer to Mac context + * @link_report: Pointer to the sSirMacLinkReport struct + * @peer: BSSID of the peer + * @pe_session: Pointer to the pe_session + * + * Return: QDF_STATUS + * + */ +QDF_STATUS +lim_send_link_report_action_frame(struct mac_context *mac, + tpSirMacLinkReport link_report, + tSirMacAddr peer, + struct pe_session *pe_session); + +/** + * lim_send_radio_measure_report_action_frame - Send RRM report action frame + * @mac: pointer to global MAC context + * @dialog_token: Dialog token to be used in the action frame + * @num_report: number of reports in pRRMReport + * @is_last_frame: is the current report last or more reports to follow + * @pRRMReport: Pointer to the RRM report structure + * @peer: MAC address of the peer + * @pe_session: Pointer to the PE session entry + * + * Return: Ret Status + */ +QDF_STATUS +lim_send_radio_measure_report_action_frame(struct mac_context *mac, + uint8_t dialog_token, + uint8_t num_report, + bool is_last_frame, + tpSirMacRadioMeasureReport pRRMReport, + tSirMacAddr peer, + struct pe_session *pe_session); + +#ifdef FEATURE_WLAN_TDLS +void lim_init_tdls_data(struct mac_context *, struct pe_session *); + +/** + * lim_process_sme_tdls_mgmt_send_req() - send out tdls management frames + * @mac_ctx: global mac context + * @msg: message buffer received from SME. + * + * Process Send Mgmt Request from SME and transmit to AP. + * + * Return: QDF_STATUS_SUCCESS on success, error code otherwise + */ +QDF_STATUS lim_process_sme_tdls_mgmt_send_req(struct mac_context *mac_ctx, + void *msg); + +/** + * lim_process_sme_tdls_add_sta_req() - process TDLS Add STA + * @mac_ctx: global mac context + * @msg: message buffer received from SME. + * + * Process TDLS Add Station request + * + * Return: QDF_STATUS_SUCCESS on success, error code otherwise + */ +QDF_STATUS lim_process_sme_tdls_add_sta_req(struct mac_context *mac, + void *msg); + +/** + * lim_process_sme_tdls_del_sta_req() - process TDLS Del STA + * @mac_ctx: global mac context + * @msg: message buffer received from SME. + * + * Process TDLS Delete Station request + * + * Return: QDF_STATUS_SUCCESS on success, error code otherwise + */ +QDF_STATUS lim_process_sme_tdls_del_sta_req(struct mac_context *mac, + void *msg); + +void lim_send_sme_mgmt_tx_completion(struct mac_context *mac, uint32_t vdev_id, + uint32_t txCompleteStatus); +QDF_STATUS lim_delete_tdls_peers(struct mac_context *mac_ctx, + struct pe_session *session_entry); +QDF_STATUS lim_process_tdls_add_sta_rsp(struct mac_context *mac, void *msg, struct pe_session *); +void lim_process_tdls_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg, + struct pe_session *session_entry); + +/** + * lim_update_tdls_state_in_fw() - Update TDLS state in FW + * + * @session_entry - PE sessions + * @value -value to be updated + * + * + * Return: void + */ +void lim_update_tdls_set_state_for_fw(struct pe_session *session_entry, + bool value); +#else +static inline QDF_STATUS lim_delete_tdls_peers(struct mac_context *mac_ctx, + struct pe_session *session_entry) +{ + return QDF_STATUS_SUCCESS; +} +static inline void lim_init_tdls_data(struct mac_context *mac, + struct pe_session *pe_session) +{ + +} + +static inline void lim_update_tdls_set_state_for_fw(struct pe_session + *session_entry, bool value) +{ +} +#endif + +/* Algorithms & Link Monitoring related functions */ +/* / Function that handles heartbeat failure */ +void lim_handle_heart_beat_failure(struct mac_context *, struct pe_session *); + +/** + * lim_tear_down_link_with_ap() - Tear down link with AP + * @mac: mac context + * @session_id: PE session id + * @reason_code: Disconnect reason code as per emun eSirMacReasonCodes + * @trigger: Disconnect trigger as per enum eLimDisassocTrigger + * + * Function that triggers link tear down with AP upon HB failure + * + * Return: None + */ +void lim_tear_down_link_with_ap(struct mac_context *mac, + uint8_t session_id, + tSirMacReasonCodes reason_code, + enum eLimDisassocTrigger trigger); + +/* / Function that defers the messages received */ +uint32_t lim_defer_msg(struct mac_context *, struct scheduler_msg *); + +#ifdef ANI_SUPPORT_11H +/* / Function that sends Measurement Report action frame */ +QDF_STATUS lim_send_meas_report_frame(struct mac_context *, tpSirMacMeasReqActionFrame, + tSirMacAddr, struct pe_session *pe_session); + +/* / Function that sends TPC Report action frame */ +QDF_STATUS lim_send_tpc_report_frame(struct mac_context *, tpSirMacTpcReqActionFrame, + tSirMacAddr, struct pe_session *pe_session); +#endif + +/** + * lim_handle_add_bss_rsp() - Handle add bss response + * @mac_ctx: mac context + * @add_bss_rsp: add bss rsp + * + * This function is called to handle all types of add bss rsp + * It will free memory of add_bss_rsp in the end after rsp is handled. + * + * Return: None + */ +void lim_handle_add_bss_rsp(struct mac_context *mac_ctx, + struct add_bss_rsp *add_bss_rsp); + +void lim_process_mlm_add_sta_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQt, + struct pe_session *pe_session); +void lim_process_mlm_del_sta_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ); + +/** + * lim_process_mlm_del_bss_rsp () - API to process delete bss response + * @mac: Pointer to Global MAC structure + * @vdev_stop_rsp: pointer to vdev stop response + * @pe_session: pointer to pe_session + * + * Return: None + */ +void lim_process_mlm_del_bss_rsp(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session); + +void lim_process_sta_mlm_add_sta_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ, + struct pe_session *pe_session); +void lim_process_sta_mlm_del_sta_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ, + struct pe_session *pe_session); + +/** + * lim_process_sta_mlm_del_bss_rsp() - handle del bss response of STA + * @mac: Pointer to Global MAC structure + * @vdev_stop_rsp: pointer to vdev stop response + * @pe_session: pointer to pe_session + * + * Return: none + */ +void lim_process_sta_mlm_del_bss_rsp(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session); + +void lim_process_mlm_set_sta_key_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ); +void lim_process_mlm_set_bss_key_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ); + +/* Function to process WMA_SWITCH_CHANNEL_RSP message */ +void lim_process_switch_channel_rsp(struct mac_context *mac, + struct vdev_start_response *rsp); + +/** + * lim_sta_handle_connect_fail() - handle connect failure of STA + * @param - join params + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_sta_handle_connect_fail(join_params *param); + +/** + * lim_join_result_callback() - Callback to handle join rsp + * @mac: Pointer to Global MAC structure + * @vdev_id: vdev id + * + * This callback function is used to delete PE session + * entry and send join response to sme. + * + * Return: None + */ +void lim_join_result_callback(struct mac_context *mac, + uint8_t vdev_id); + +#ifdef WLAN_FEATURE_HOST_ROAM +QDF_STATUS lim_sta_reassoc_error_handler(struct reassoc_params *param); +#else +static inline +QDF_STATUS lim_sta_reassoc_error_handler(struct reassoc_params *param) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif + +#ifdef WLAN_FEATURE_11W +/* 11w send SA query request action frame */ +QDF_STATUS lim_send_sa_query_request_frame(struct mac_context *mac, uint8_t *transId, + tSirMacAddr peer, + struct pe_session *pe_session); +/* 11w SA query request action frame handler */ +QDF_STATUS lim_send_sa_query_response_frame(struct mac_context *mac, + uint8_t *transId, tSirMacAddr peer, + struct pe_session *pe_session); +#endif + +/* Inline functions */ + +/** + * lim_post_sme_message() + * + ***FUNCTION: + * This function is called by limProcessMlmMessages(). In this + * function MLM sub-module invokes MLM ind/cnf primitives. + * + ***LOGIC: + * Initially MLM makes an SME function call to invoke MLM ind/cnf + * primitive. In future this can be enhanced to 'post' messages to SME. + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac Pointer to Global MAC structure + * @param msgType Indicates the MLM primitive message type + * @param *msg_buf A pointer to the MLM message buffer + * + * @return None + */ +static inline void +lim_post_sme_message(struct mac_context *mac, uint32_t msgType, + uint32_t *msg_buf) +{ + struct scheduler_msg msg = {0}; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + + msg.type = (uint16_t) msgType; + msg.bodyptr = msg_buf; + msg.bodyval = 0; + if (msgType > eWNI_SME_MSG_TYPES_BEGIN) { + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, NO_SESSION, + msg.type)); + lim_process_sme_req_messages(mac, &msg); + } else { + lim_process_mlm_rsp_messages(mac, msgType, msg_buf); + } +} /*** end lim_post_sme_message() ***/ + +/** + * lim_post_mlm_message() + * + ***FUNCTION: + * This function is called by limProcessSmeMessages(). In this + * function SME invokes MLME primitives. + * + ***PARAMS: + * + ***LOGIC: + * Initially SME makes an MLM function call to invoke MLM primitive. + * In future this can be enhanced to 'post' messages to MLM. + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param mac Pointer to Global MAC structure + * @param msgType Indicates the MLM primitive message type + * @param *msg_buf A pointer to the MLM message buffer + * + * @return None + */ +static inline void +lim_post_mlm_message(struct mac_context *mac, uint32_t msgType, + uint32_t *msg_buf) +{ + struct scheduler_msg msg = {0}; + + if (!msg_buf) { + pe_err("Buffer is Pointing to NULL"); + return; + } + msg.type = (uint16_t) msgType; + msg.bodyptr = msg_buf; + msg.bodyval = 0; + MTRACE(mac_trace_msg_rx(mac, NO_SESSION, msg.type)); + lim_process_mlm_req_messages(mac, &msg); +} /*** end lim_post_mlm_message() ***/ + +/** + * lim_get_ielen_from_bss_description() + * + ***FUNCTION: + * This function is called in various places to get IE length + * from struct bss_description structure + * number being scanned. + * + ***PARAMS: + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param pBssDescr + * @return Total IE length + */ + +static inline uint16_t +lim_get_ielen_from_bss_description(struct bss_description *pBssDescr) +{ + uint16_t ielen; + + if (!pBssDescr) + return 0; + + /* + * Length of BSS desription is without length of + * length itself and length of pointer + * that holds ieFields + * + * <------------sizeof(struct bss_description)--------------------> + * +--------+---------------------------------+---------------+ + * | length | other fields | pointer to IEs| + * +--------+---------------------------------+---------------+ + * ^ + * ieFields + */ + + ielen = (uint16_t)(pBssDescr->length + sizeof(pBssDescr->length) - + GET_FIELD_OFFSET(struct bss_description, ieFields)); + + return ielen; +} /*** end lim_get_ielen_from_bss_description() ***/ + +/** + * lim_send_beacon_ind() - send the beacon indication + * @mac_ctx: pointer to mac structure + * @session: pe session + * @reason: beacon update reason + * + * return: success: QDF_STATUS_SUCCESS failure: QDF_STATUS_E_FAILURE + */ +QDF_STATUS lim_send_beacon_ind(struct mac_context *mac_ctx, struct pe_session *session, + enum sir_bcn_update_reason reason); + +void +lim_send_vdev_restart(struct mac_context *mac, struct pe_session *pe_session, + uint8_t sessionId); + +void lim_wpspbc_close(struct mac_context *mac, struct pe_session *pe_session); + +#define LIM_WPS_OVERLAP_TIMER_MS 10000 + +void lim_process_disassoc_ack_timeout(struct mac_context *mac); +void lim_process_deauth_ack_timeout(struct mac_context *mac); +QDF_STATUS lim_send_disassoc_cnf(struct mac_context *mac); +QDF_STATUS lim_send_deauth_cnf(struct mac_context *mac); + +/** + * lim_disassoc_tx_complete_cnf() - callback to indicate Tx completion + * @context: pointer to mac structure + * @txCompleteSuccess: indicates tx success/failure + * @params: tx completion params + * + * function will be invoked on receiving tx completion indication + * + * return: success: QDF_STATUS_SUCCESS failure: QDF_STATUS_E_FAILURE + */ +QDF_STATUS lim_disassoc_tx_complete_cnf(void *context, + uint32_t txCompleteSuccess, + void *params); + +/** + * lim_deauth_tx_complete_cnf() - callback to indicate Tx completion + * @context: pointer to mac structure + * @txCompleteSuccess: indicates tx success/failure + * @params: tx completion params + * + * function will be invoked on receiving tx completion indication + * + * return: success: QDF_STATUS_SUCCESS failure: QDF_STATUS_E_FAILURE + */ +QDF_STATUS lim_deauth_tx_complete_cnf(void *context, + uint32_t txCompleteSuccess, + void *params); + +void lim_send_sme_disassoc_deauth_ntf(struct mac_context *mac_ctx, + QDF_STATUS status, uint32_t *ctx); + +#ifdef FEATURE_WLAN_TDLS +QDF_STATUS lim_process_sme_del_all_tdls_peers(struct mac_context *p_mac, + uint32_t *msg_buf); +#else +static inline +QDF_STATUS lim_process_sme_del_all_tdls_peers(struct mac_context *p_mac, + uint32_t *msg_buf) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * lim_send_bcn_rsp() - handle beacon send response + * @mac_ctx Pointer to Global MAC structure + * @rsp: beacon send response + * + * Return: None + */ +void lim_send_bcn_rsp(struct mac_context *mac_ctx, tpSendbeaconParams rsp); + +/** + * lim_add_roam_blacklist_ap() - handle the blacklist bssid list received from + * firmware + * @mac_ctx: Pointer to Global MAC structure + * @list: roam blacklist ap list + * + * Return: None + */ +void lim_add_roam_blacklist_ap(struct mac_context *mac_ctx, + struct roam_blacklist_event *src_lst); + +/** + * lim_process_rx_channel_status_event() - processes + * event WDA_RX_CHN_STATUS_EVENT + * @mac_ctx Pointer to Global MAC structure + * @buf: Received message info + * + * Return: None + */ +void lim_process_rx_channel_status_event(struct mac_context *mac_ctx, void *buf); + +/* / Bit value data structure */ +typedef enum sHalBitVal /* For Bit operations */ +{ + eHAL_CLEAR, + eHAL_SET +} tHalBitVal; + +/** + * lim_send_addba_response_frame(): Send ADDBA response action frame to peer + * @mac_ctx: mac context + * @peer_mac: Peer MAC address + * @tid: TID for which addba response is being sent + * @session: PE session entry + * @addba_extn_present: ADDBA extension present flag + * @amsdu_support: amsdu in ampdu support + * @is_wep: protected bit in fc + * + * This function is called when ADDBA request is successful. ADDBA response is + * setup by calling addba_response_setup API and frame is then sent out OTA. + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_send_addba_response_frame(struct mac_context *mac_ctx, + tSirMacAddr peer_mac, uint16_t tid, + struct pe_session *session, + uint8_t addba_extn_present, + uint8_t amsdu_support, uint8_t is_wep); + +/** + * lim_send_delba_action_frame() - Send delba to peer + * @mac_ctx: mac context + * @vdev_id: vdev id + * @peer_macaddr: Peer mac addr + * @tid: Tid number + * @reason_code: reason code + * + * Return: 0 for success, non-zero for failure + */ +QDF_STATUS lim_send_delba_action_frame(struct mac_context *mac_ctx, + uint8_t vdev_id, + uint8_t *peer_macaddr, uint8_t tid, + uint8_t reason_code); + +/** + * lim_process_join_failure_timeout() - This function is called to process + * JoinFailureTimeout + * + * @mac_ctx: Pointer to Global MAC structure + * + * This function is called to process JoinFailureTimeout + * + * @Return None + */ +void lim_process_join_failure_timeout(struct mac_context *mac_ctx); + +/** + * lim_process_auth_failure_timeout() - This function is called to process Min + * Channel Timeout during channel scan. + * + * @mac_ctx: Pointer to Global MAC structure + * + * This function is called to process Min Channel Timeout during channel scan. + * + * @Return: None + */ +void lim_process_auth_failure_timeout(struct mac_context *mac_ctx); + +/** + * lim_process_assoc_failure_timeout() - This function is called to process Min + * Channel Timeout during channel scan. + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_type: Assoc or reassoc + * + * This function is called to process Min Channel Timeout during channel scan. + * + * @Return: None + */ +void lim_process_assoc_failure_timeout(struct mac_context *mac_ctx, + uint32_t msg_type); + +/** + * lim_process_sae_auth_timeout() - This function is called to process sae + * auth timeout + * @mac_ctx: Pointer to Global MAC structure + * + * @Return: None + */ +void lim_process_sae_auth_timeout(struct mac_context *mac_ctx); + +/** + * lim_send_frame() - API to send frame + * @mac_ctx Pointer to Global MAC structure + * @vdev_id: vdev id + * @buf: Pointer to SAE auth retry frame + * @buf_len: length of frame + * + * Return: None + */ +void lim_send_frame(struct mac_context *mac_ctx, uint8_t vdev_id, uint8_t *buf, + uint16_t buf_len); + +/** + * lim_send_mgmt_frame_tx() - Sends mgmt frame + * @mac_ctx Pointer to Global MAC structure + * @msg: Received message info + * + * Return: None + */ +void lim_send_mgmt_frame_tx(struct mac_context *mac_ctx, + struct scheduler_msg *msg); + +/** + * lim_send_csa_restart_req() - send csa restart req + * @mac_ctx Pointer to Global MAC structure + * @vdev_id: vdev id + * + * Return: None + */ +void lim_send_csa_restart_req(struct mac_context *mac_ctx, uint8_t vdev_id); + +/** + * lim_continue_sta_csa_req() - continue with CSA req after HW mode change + * @mac_ctx Pointer to Global MAC structure + * @vdev_id: vdev id + * + * Return: None + */ +void lim_continue_sta_csa_req(struct mac_context *mac_ctx, uint8_t vdev_id); + +/** + * lim_process_mlm_start_req() - process MLM_START_REQ message + * + * @mac_ctx: global MAC context + * @mlm_start_req: Pointer to start req + * + * This function is called to process MLM_START_REQ message + * from SME. MLME now waits for HAL to send WMA_ADD_BSS_RSP. + * + * Return: None + */ +void lim_process_mlm_start_req(struct mac_context *mac_ctx, + tLimMlmStartReq *mlm_start_req); + +/** + * lim_process_mlm_join_req() - process mlm join request. + * + * @mac_ctx: Pointer to Global MAC structure + * @msg: Pointer to the MLM message buffer + * + * This function is called to process MLM_JOIN_REQ message + * from SME. It does following: + * 1) Initialize LIM, HAL, DPH + * 2) Configure the BSS for which the JOIN REQ was received + * a) Send WMA_ADD_BSS_REQ to HAL - + * This will identify the BSS that we are interested in + * --AND-- + * Add a STA entry for the AP (in a STA context) + * b) Wait for WMA_ADD_BSS_RSP + * c) Send WMA_ADD_STA_REQ to HAL + * This will add the "local STA" entry to the STA table + * 3) Continue as before, i.e, + * a) Send a PROBE REQ + * b) Wait for PROBE RSP/BEACON containing the SSID that + * we are interested in + * c) Then start an AUTH seq + * d) Followed by the ASSOC seq + * + * @Return: None + */ +void lim_process_mlm_join_req(struct mac_context *mac_ctx, + tLimMlmJoinReq *mlm_join_req); + +/* + * lim_process_mlm_deauth_req() - This function is called to process + * MLM_DEAUTH_REQ message from SME + * + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: A pointer to the MLM message buffer + * + * This function is called to process MLM_DEAUTH_REQ message from SME + * + * @Return: None + */ +void lim_process_mlm_deauth_req(struct mac_context *mac_ctx, uint32_t *msg_buf); + +/** + * lim_sta_mlme_vdev_disconnect_bss() - Disconnect from BSS + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes BSS disconnection + * + * Return: SUCCESS on successful completion of disconnection + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_sta_mlme_vdev_disconnect_bss(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_process_assoc_cleanup() - frees up resources used in function + * lim_process_assoc_req_frame() + * @mac_ctx: pointer to Global MAC structure + * @session: pointer to pe session entry + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @sta_ds: station dph entry + * @assoc_req_copied: boolean to indicate if assoc req was copied to tmp above + * + * Frees up resources used in function lim_process_assoc_req_frame + * + * Return: void + */ +void lim_process_assoc_cleanup(struct mac_context *mac_ctx, + struct pe_session *session, + tpSirAssocReq assoc_req, + tpDphHashNode sta_ds, + bool assoc_req_copied); + +/** + * lim_send_assoc_ind_to_sme() - Initialize PE data structures and send assoc + * indication to SME. + * @mac_ctx: Pointer to Global MAC structure + * @session: pe session entry + * @sub_type: Indicates whether it is Association Request(=0) or Reassociation + * Request(=1) frame + * @hdr: A pointer to the MAC header + * @assoc_req: pointer to ASSOC/REASSOC Request frame + * @akm_type: AKM type + * @pmf_connection: flag indicating pmf connection + * @assoc_req_copied: boolean to indicate if assoc req was copied to tmp above + * @dup_entry: flag indicating if duplicate entry found + * @force_1x1: flag to indicate if the STA nss needs to be downgraded to 1x1 + * + * Return: void + */ +bool lim_send_assoc_ind_to_sme(struct mac_context *mac_ctx, + struct pe_session *session, + uint8_t sub_type, + tpSirMacMgmtHdr hdr, + tpSirAssocReq assoc_req, + enum ani_akm_type akm_type, + bool pmf_connection, + bool *assoc_req_copied, + bool dup_entry, bool force_1x1); + +/** + * lim_process_sta_add_bss_rsp_pre_assoc - Processes handoff request + * @mac_ctx: Pointer to mac context + * @pAddBssParams: Bss params including rsp data + * @session_entry: PE session handle + * @status: Qdf status + * + * This function is called to process a WMA_ADD_BSS_RSP from HAL. + * Upon receipt of this message from HAL if the state is pre assoc. + * + * Return: Null + */ +void lim_process_sta_add_bss_rsp_pre_assoc(struct mac_context *mac_ctx, + struct bss_params *add_bss_params, + struct pe_session *session_entry, + QDF_STATUS status); +#endif /* __LIM_TYPES_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_utils.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..f4ad67174fdd2bbdda221f8a3eec3319e2d3ffb0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_utils.c @@ -0,0 +1,9020 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_utils.cc contains the utility functions + * LIM uses. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#include "sch_api.h" +#include "lim_utils.h" +#include "lim_types.h" +#include "lim_security_utils.h" +#include "lim_prop_exts_utils.h" +#include "lim_send_messages.h" +#include "lim_ser_des_utils.h" +#include "lim_admit_control.h" +#include "dot11f.h" +#include "dot11fdefs.h" +#include "wmm_apsd.h" +#include "lim_trace.h" + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +#include "host_diag_core_event.h" +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ +#include "lim_ibss_peer_mgmt.h" +#include "lim_session_utils.h" +#include "lim_ft_defs.h" +#include "lim_session.h" +#include "cds_reg_service.h" +#include "nan_datapath.h" +#include "wma.h" +#include "wlan_reg_services_api.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_mlme_public_struct.h" +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +#include "wma_he.h" +#endif +#include "wlan_utility.h" + +#ifdef WLAN_FEATURE_11W +#include "wni_cfg.h" +#endif +#include "cfg_mlme_obss_ht40.h" +#include "cfg_ucfg_api.h" +#include "lim_ft.h" +#include "wlan_mlme_main.h" +#include "qdf_util.h" +#include "wlan_qct_sys.h" +#include +#include +#include +#include "wlan_mlme_ucfg_api.h" + +#define ASCII_SPACE_CHARACTER 0x20 + +/** ------------------------------------------------------------- + \fn lim_delete_dialogue_token_list + \brief deletes the complete lim dialogue token linked list. + \param struct mac_context * mac + \return None + -------------------------------------------------------------*/ +void lim_delete_dialogue_token_list(struct mac_context *mac) +{ + tpDialogueToken pCurrNode = mac->lim.pDialogueTokenHead; + + while (mac->lim.pDialogueTokenHead) { + pCurrNode = mac->lim.pDialogueTokenHead; + mac->lim.pDialogueTokenHead = + mac->lim.pDialogueTokenHead->next; + qdf_mem_free(pCurrNode); + pCurrNode = NULL; + } + mac->lim.pDialogueTokenTail = NULL; +} + +char *lim_dot11_reason_str(uint16_t reasonCode) +{ + switch (reasonCode) { + case 0: + return " "; + CASE_RETURN_STRING(eSIR_MAC_UNSPEC_FAILURE_REASON); + CASE_RETURN_STRING(eSIR_MAC_PREV_AUTH_NOT_VALID_REASON); + CASE_RETURN_STRING(eSIR_MAC_DEAUTH_LEAVING_BSS_REASON); + CASE_RETURN_STRING(eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON); + CASE_RETURN_STRING(eSIR_MAC_DISASSOC_DUE_TO_DISABILITY_REASON); + CASE_RETURN_STRING + (eSIR_MAC_CLASS2_FRAME_FROM_NON_AUTH_STA_REASON); + CASE_RETURN_STRING + (eSIR_MAC_CLASS3_FRAME_FROM_NON_ASSOC_STA_REASON); + CASE_RETURN_STRING(eSIR_MAC_DISASSOC_LEAVING_BSS_REASON); + CASE_RETURN_STRING(eSIR_MAC_STA_NOT_PRE_AUTHENTICATED_REASON); + CASE_RETURN_STRING(eSIR_MAC_PWR_CAPABILITY_BAD_REASON); + CASE_RETURN_STRING(eSIR_MAC_SPRTD_CHANNELS_BAD_REASON); + + CASE_RETURN_STRING(eSIR_MAC_INVALID_IE_REASON); + CASE_RETURN_STRING(eSIR_MAC_MIC_FAILURE_REASON); + CASE_RETURN_STRING(eSIR_MAC_4WAY_HANDSHAKE_TIMEOUT_REASON); + CASE_RETURN_STRING(eSIR_MAC_GR_KEY_UPDATE_TIMEOUT_REASON); + CASE_RETURN_STRING(eSIR_MAC_RSN_IE_MISMATCH_REASON); + + CASE_RETURN_STRING(eSIR_MAC_INVALID_MC_CIPHER_REASON); + CASE_RETURN_STRING(eSIR_MAC_INVALID_UC_CIPHER_REASON); + CASE_RETURN_STRING(eSIR_MAC_INVALID_AKMP_REASON); + CASE_RETURN_STRING(eSIR_MAC_UNSUPPORTED_RSN_IE_VER_REASON); + CASE_RETURN_STRING(eSIR_MAC_INVALID_RSN_CAPABILITIES_REASON); + CASE_RETURN_STRING(eSIR_MAC_1X_AUTH_FAILURE_REASON); + CASE_RETURN_STRING(eSIR_MAC_CIPHER_SUITE_REJECTED_REASON); +#ifdef FEATURE_WLAN_TDLS + CASE_RETURN_STRING(eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE); + CASE_RETURN_STRING(eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON); +#endif + /* Reserved 27 - 30 */ +#ifdef WLAN_FEATURE_11W + CASE_RETURN_STRING + (eSIR_MAC_ROBUST_MGMT_FRAMES_POLICY_VIOLATION); +#endif + CASE_RETURN_STRING(eSIR_MAC_QOS_UNSPECIFIED_REASON); + CASE_RETURN_STRING(eSIR_MAC_QAP_NO_BANDWIDTH_REASON); + CASE_RETURN_STRING(eSIR_MAC_XS_UNACKED_FRAMES_REASON); + CASE_RETURN_STRING(eSIR_MAC_BAD_TXOP_USE_REASON); + CASE_RETURN_STRING(eSIR_MAC_PEER_STA_REQ_LEAVING_BSS_REASON); + CASE_RETURN_STRING(eSIR_MAC_PEER_REJECT_MECHANISIM_REASON); + CASE_RETURN_STRING(eSIR_MAC_MECHANISM_NOT_SETUP_REASON); + + CASE_RETURN_STRING(eSIR_MAC_PEER_TIMEDOUT_REASON); + CASE_RETURN_STRING(eSIR_MAC_CIPHER_NOT_SUPPORTED_REASON); + CASE_RETURN_STRING(eSIR_MAC_DISASSOC_DUE_TO_FTHANDOFF_REASON); + /* Reserved 47 - 65535 */ + default: + return "Unknown"; + } +} + +char *lim_mlm_state_str(tLimMlmStates state) +{ + switch (state) { + case eLIM_MLM_OFFLINE_STATE: + return "eLIM_MLM_OFFLINE_STATE"; + case eLIM_MLM_IDLE_STATE: + return "eLIM_MLM_IDLE_STATE"; + case eLIM_MLM_WT_JOIN_BEACON_STATE: + return "eLIM_MLM_WT_JOIN_BEACON_STATE"; + case eLIM_MLM_JOINED_STATE: + return "eLIM_MLM_JOINED_STATE"; + case eLIM_MLM_BSS_STARTED_STATE: + return "eLIM_MLM_BSS_STARTED_STATE"; + case eLIM_MLM_WT_AUTH_FRAME2_STATE: + return "eLIM_MLM_WT_AUTH_FRAME2_STATE"; + case eLIM_MLM_WT_AUTH_FRAME3_STATE: + return "eLIM_MLM_WT_AUTH_FRAME3_STATE"; + case eLIM_MLM_WT_AUTH_FRAME4_STATE: + return "eLIM_MLM_WT_AUTH_FRAME4_STATE"; + case eLIM_MLM_AUTH_RSP_TIMEOUT_STATE: + return "eLIM_MLM_AUTH_RSP_TIMEOUT_STATE"; + case eLIM_MLM_AUTHENTICATED_STATE: + return "eLIM_MLM_AUTHENTICATED_STATE"; + case eLIM_MLM_WT_ASSOC_RSP_STATE: + return "eLIM_MLM_WT_ASSOC_RSP_STATE"; + case eLIM_MLM_WT_REASSOC_RSP_STATE: + return "eLIM_MLM_WT_REASSOC_RSP_STATE"; + case eLIM_MLM_WT_FT_REASSOC_RSP_STATE: + return "eLIM_MLM_WT_FT_REASSOC_RSP_STATE"; + case eLIM_MLM_WT_DEL_STA_RSP_STATE: + return "eLIM_MLM_WT_DEL_STA_RSP_STATE"; + case eLIM_MLM_WT_DEL_BSS_RSP_STATE: + return "eLIM_MLM_WT_DEL_BSS_RSP_STATE"; + case eLIM_MLM_WT_ADD_STA_RSP_STATE: + return "eLIM_MLM_WT_ADD_STA_RSP_STATE"; + case eLIM_MLM_WT_ADD_BSS_RSP_STATE: + return "eLIM_MLM_WT_ADD_BSS_RSP_STATE"; + case eLIM_MLM_REASSOCIATED_STATE: + return "eLIM_MLM_REASSOCIATED_STATE"; + case eLIM_MLM_LINK_ESTABLISHED_STATE: + return "eLIM_MLM_LINK_ESTABLISHED_STATE"; + case eLIM_MLM_WT_ASSOC_CNF_STATE: + return "eLIM_MLM_WT_ASSOC_CNF_STATE"; + case eLIM_MLM_WT_ADD_BSS_RSP_ASSOC_STATE: + return "eLIM_MLM_WT_ADD_BSS_RSP_ASSOC_STATE"; + case eLIM_MLM_WT_ADD_BSS_RSP_REASSOC_STATE: + return "eLIM_MLM_WT_ADD_BSS_RSP_REASSOC_STATE"; + case eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE: + return "eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE"; + case eLIM_MLM_WT_ASSOC_DEL_STA_RSP_STATE: + return "eLIM_MLM_WT_ASSOC_DEL_STA_RSP_STATE"; + case eLIM_MLM_WT_SET_BSS_KEY_STATE: + return "eLIM_MLM_WT_SET_BSS_KEY_STATE"; + case eLIM_MLM_WT_SET_STA_KEY_STATE: + return "eLIM_MLM_WT_SET_STA_KEY_STATE"; + default: + return "INVALID MLM state"; + } +} + +void +lim_print_mlm_state(struct mac_context *mac, uint16_t logLevel, tLimMlmStates state) +{ + pe_debug("Mlm state: %s", lim_mlm_state_str(state)); +} + +char *lim_sme_state_str(tLimSmeStates state) +{ + switch (state) { + case eLIM_SME_OFFLINE_STATE: + return "eLIM_SME_OFFLINE_STATE"; + case eLIM_SME_IDLE_STATE: + return "eLIM_SME_OFFLINE_STATE"; + case eLIM_SME_SUSPEND_STATE: + return "eLIM_SME_SUSPEND_STATE"; + case eLIM_SME_WT_JOIN_STATE: + return "eLIM_SME_WT_JOIN_STATE"; + case eLIM_SME_WT_AUTH_STATE: + return "eLIM_SME_WT_AUTH_STATE"; + case eLIM_SME_WT_ASSOC_STATE: + return "eLIM_SME_WT_ASSOC_STATE"; + case eLIM_SME_WT_REASSOC_STATE: + return "eLIM_SME_WT_REASSOC_STATE"; + case eLIM_SME_JOIN_FAILURE_STATE: + return "eLIM_SME_JOIN_FAILURE_STATE"; + case eLIM_SME_ASSOCIATED_STATE: + return "eLIM_SME_ASSOCIATED_STATE"; + case eLIM_SME_REASSOCIATED_STATE: + return "eLIM_SME_REASSOCIATED_STATE"; + case eLIM_SME_LINK_EST_STATE: + return "eLIM_SME_LINK_EST_STATE"; + case eLIM_SME_WT_PRE_AUTH_STATE: + return "eLIM_SME_WT_PRE_AUTH_STATE"; + case eLIM_SME_WT_DISASSOC_STATE: + return "eLIM_SME_WT_DISASSOC_STATE"; + case eLIM_SME_WT_DEAUTH_STATE: + return "eLIM_SME_WT_DEAUTH_STATE"; + case eLIM_SME_WT_START_BSS_STATE: + return "eLIM_SME_WT_START_BSS_STATE"; + case eLIM_SME_WT_STOP_BSS_STATE: + return "eLIM_SME_WT_STOP_BSS_STATE"; + case eLIM_SME_NORMAL_STATE: + return "eLIM_SME_NORMAL_STATE"; + default: + return "INVALID SME STATE"; + } +} + +void +lim_print_sme_state(struct mac_context *mac, uint16_t logLevel, tLimSmeStates state) +{ + pe_debug("SME state: %s", lim_sme_state_str(state)); +} + +char *lim_msg_str(uint32_t msgType) +{ + switch (msgType) { + case eWNI_SME_SYS_READY_IND: + return "eWNI_SME_SYS_READY_IND"; + case eWNI_SME_JOIN_REQ: + return "eWNI_SME_JOIN_REQ"; + case eWNI_SME_JOIN_RSP: + return "eWNI_SME_JOIN_RSP"; + case eWNI_SME_SETCONTEXT_RSP: + return "eWNI_SME_SETCONTEXT_RSP"; + case eWNI_SME_REASSOC_REQ: + return "eWNI_SME_REASSOC_REQ"; + case eWNI_SME_REASSOC_RSP: + return "eWNI_SME_REASSOC_RSP"; + case eWNI_SME_DISASSOC_REQ: + return "eWNI_SME_DISASSOC_REQ"; + case eWNI_SME_DISASSOC_RSP: + return "eWNI_SME_DISASSOC_RSP"; + case eWNI_SME_DISASSOC_IND: + return "eWNI_SME_DISASSOC_IND"; + case eWNI_SME_DISASSOC_CNF: + return "eWNI_SME_DISASSOC_CNF"; + case eWNI_SME_DEAUTH_REQ: + return "eWNI_SME_DEAUTH_REQ"; + case eWNI_SME_DEAUTH_RSP: + return "eWNI_SME_DEAUTH_RSP"; + case eWNI_SME_DEAUTH_IND: + return "eWNI_SME_DEAUTH_IND"; + case eWNI_SME_WM_STATUS_CHANGE_NTF: + return "eWNI_SME_WM_STATUS_CHANGE_NTF"; + case eWNI_SME_START_BSS_REQ: + return "eWNI_SME_START_BSS_REQ"; + case eWNI_SME_START_BSS_RSP: + return "eWNI_SME_START_BSS_RSP"; + case eWNI_SME_ASSOC_IND: + return "eWNI_SME_ASSOC_IND"; + case eWNI_SME_ASSOC_IND_UPPER_LAYER: + return "eWNI_SME_ASSOC_IND_UPPER_LAYER"; + case eWNI_SME_ASSOC_CNF: + return "eWNI_SME_ASSOC_CNF"; + case eWNI_SME_SWITCH_CHL_IND: + return "eWNI_SME_SWITCH_CHL_IND"; + case eWNI_SME_STOP_BSS_REQ: + return "eWNI_SME_STOP_BSS_REQ"; + case eWNI_SME_STOP_BSS_RSP: + return "eWNI_SME_STOP_BSS_RSP"; + case eWNI_SME_DEAUTH_CNF: + return "eWNI_SME_DEAUTH_CNF"; + case eWNI_SME_ADDTS_REQ: + return "eWNI_SME_ADDTS_REQ"; + case eWNI_SME_ADDTS_RSP: + return "eWNI_SME_ADDTS_RSP"; + case eWNI_SME_DELTS_REQ: + return "eWNI_SME_DELTS_REQ"; + case eWNI_SME_DELTS_RSP: + return "eWNI_SME_DELTS_RSP"; + case eWNI_SME_DELTS_IND: + return "eWNI_SME_DELTS_IND"; + case SIR_BB_XPORT_MGMT_MSG: + return "SIR_BB_XPORT_MGMT_MSG"; + case SIR_LIM_JOIN_FAIL_TIMEOUT: + return "SIR_LIM_JOIN_FAIL_TIMEOUT"; + case SIR_LIM_AUTH_FAIL_TIMEOUT: + return "SIR_LIM_AUTH_FAIL_TIMEOUT"; + case SIR_LIM_AUTH_RSP_TIMEOUT: + return "SIR_LIM_AUTH_RSP_TIMEOUT"; + case SIR_LIM_ASSOC_FAIL_TIMEOUT: + return "SIR_LIM_ASSOC_FAIL_TIMEOUT"; + case SIR_LIM_REASSOC_FAIL_TIMEOUT: + return "SIR_LIM_REASSOC_FAIL_TIMEOUT"; + case SIR_LIM_HEART_BEAT_TIMEOUT: + return "SIR_LIM_HEART_BEAT_TIMEOUT"; + case SIR_LIM_ADDTS_RSP_TIMEOUT: + return "SIR_LIM_ADDTS_RSP_TIMEOUT"; + case SIR_LIM_LINK_TEST_DURATION_TIMEOUT: + return "SIR_LIM_LINK_TEST_DURATION_TIMEOUT"; + case SIR_LIM_UPDATE_OLBC_CACHEL_TIMEOUT: + return "SIR_LIM_UPDATE_OLBC_CACHEL_TIMEOUT"; + case SIR_LIM_CNF_WAIT_TIMEOUT: + return "SIR_LIM_CNF_WAIT_TIMEOUT"; + case SIR_LIM_FT_PREAUTH_RSP_TIMEOUT: + return "SIR_LIM_FT_PREAUTH_RSP_TIMEOUT"; +#ifdef FEATURE_WLAN_ESE + case eWNI_SME_GET_TSM_STATS_REQ: + return "eWNI_SME_GET_TSM_STATS_REQ"; + case eWNI_SME_GET_TSM_STATS_RSP: + return "eWNI_SME_GET_TSM_STATS_RSP"; +#endif /* FEATURE_WLAN_ESE */ + case eWNI_SME_SET_HW_MODE_REQ: + return "eWNI_SME_SET_HW_MODE_REQ"; + case eWNI_SME_SET_HW_MODE_RESP: + return "eWNI_SME_SET_HW_MODE_RESP"; + case eWNI_SME_HW_MODE_TRANS_IND: + return "eWNI_SME_HW_MODE_TRANS_IND"; + case SIR_LIM_PROCESS_DEFERRED_QUEUE: + return "SIR_LIM_PROCESS_DEFERRED_QUEUE"; + default: + return "Unknown"; + } +} + +char *lim_result_code_str(tSirResultCodes resultCode) +{ + switch (resultCode) { + case eSIR_SME_SUCCESS: + return "eSIR_SME_SUCCESS"; + case eSIR_LOGE_EXCEPTION: + return "eSIR_LOGE_EXCEPTION"; + case eSIR_SME_INVALID_PARAMETERS: + return "eSIR_SME_INVALID_PARAMETERS"; + case eSIR_SME_UNEXPECTED_REQ_RESULT_CODE: + return "eSIR_SME_UNEXPECTED_REQ_RESULT_CODE"; + case eSIR_SME_RESOURCES_UNAVAILABLE: + return "eSIR_SME_RESOURCES_UNAVAILABLE"; + case eSIR_SME_SCAN_FAILED: + return "eSIR_SME_SCAN_FAILED"; + case eSIR_SME_BSS_ALREADY_STARTED_OR_JOINED: + return "eSIR_SME_BSS_ALREADY_STARTED_OR_JOINED"; + case eSIR_SME_LOST_LINK_WITH_PEER_RESULT_CODE: + return "eSIR_SME_LOST_LINK_WITH_PEER_RESULT_CODE"; + case eSIR_SME_REFUSED: + return "eSIR_SME_REFUSED"; + case eSIR_SME_JOIN_TIMEOUT_RESULT_CODE: + return "eSIR_SME_JOIN_TIMEOUT_RESULT_CODE"; + case eSIR_SME_AUTH_TIMEOUT_RESULT_CODE: + return "eSIR_SME_AUTH_TIMEOUT_RESULT_CODE"; + case eSIR_SME_ASSOC_TIMEOUT_RESULT_CODE: + return "eSIR_SME_ASSOC_TIMEOUT_RESULT_CODE"; + case eSIR_SME_REASSOC_TIMEOUT_RESULT_CODE: + return "eSIR_SME_REASSOC_TIMEOUT_RESULT_CODE"; + case eSIR_SME_MAX_NUM_OF_PRE_AUTH_REACHED: + return "eSIR_SME_MAX_NUM_OF_PRE_AUTH_REACHED"; + case eSIR_SME_AUTH_REFUSED: + return "eSIR_SME_AUTH_REFUSED"; + case eSIR_SME_INVALID_WEP_DEFAULT_KEY: + return "eSIR_SME_INVALID_WEP_DEFAULT_KEY"; + case eSIR_SME_ASSOC_REFUSED: + return "eSIR_SME_ASSOC_REFUSED"; + case eSIR_SME_REASSOC_REFUSED: + return "eSIR_SME_REASSOC_REFUSED"; + case eSIR_SME_STA_NOT_AUTHENTICATED: + return "eSIR_SME_STA_NOT_AUTHENTICATED"; + case eSIR_SME_STA_NOT_ASSOCIATED: + return "eSIR_SME_STA_NOT_ASSOCIATED"; + case eSIR_SME_ALREADY_JOINED_A_BSS: + return "eSIR_SME_ALREADY_JOINED_A_BSS"; + case eSIR_SME_MORE_SCAN_RESULTS_FOLLOW: + return "eSIR_SME_MORE_SCAN_RESULTS_FOLLOW"; + case eSIR_SME_INVALID_ASSOC_RSP_RXED: + return "eSIR_SME_INVALID_ASSOC_RSP_RXED"; + case eSIR_SME_MIC_COUNTER_MEASURES: + return "eSIR_SME_MIC_COUNTER_MEASURES"; + case eSIR_SME_ADDTS_RSP_TIMEOUT: + return "eSIR_SME_ADDTS_RSP_TIMEOUT"; + case eSIR_SME_CHANNEL_SWITCH_FAIL: + return "eSIR_SME_CHANNEL_SWITCH_FAIL"; + case eSIR_SME_HAL_SCAN_INIT_FAILED: + return "eSIR_SME_HAL_SCAN_INIT_FAILED"; + case eSIR_SME_HAL_SCAN_END_FAILED: + return "eSIR_SME_HAL_SCAN_END_FAILED"; + case eSIR_SME_HAL_SCAN_FINISH_FAILED: + return "eSIR_SME_HAL_SCAN_FINISH_FAILED"; + case eSIR_SME_HAL_SEND_MESSAGE_FAIL: + return "eSIR_SME_HAL_SEND_MESSAGE_FAIL"; + + default: + return "Unknown resultCode"; + } +} + +void lim_print_msg_name(struct mac_context *mac, uint16_t logLevel, uint32_t msgType) +{ + pe_debug("Msg: %s", lim_msg_str(msgType)); +} + +/** + * lim_init_mlm() - This function is called by limProcessSmeMessages() to + * initialize MLM state machine on STA + * @mac: Pointer to Global MAC structure + * + * @Return: Status of operation + */ +QDF_STATUS lim_init_mlm(struct mac_context *mac) +{ + uint32_t retVal; + + mac->lim.gLimTimersCreated = 0; + + MTRACE(mac_trace(mac, TRACE_CODE_MLM_STATE, NO_SESSION, + mac->lim.gLimMlmState)); + + /* Initialize number of pre-auth contexts */ + mac->lim.gLimNumPreAuthContexts = 0; + + /* Initialize MAC based Authentication STA list */ + lim_init_pre_auth_list(mac); + + /* Create timers used by LIM */ + retVal = lim_create_timers(mac); + if (retVal != TX_SUCCESS) { + pe_err("lim_create_timers Failed"); + return QDF_STATUS_SUCCESS; + } + + mac->lim.gLimTimersCreated = 1; + return QDF_STATUS_SUCCESS; +} /*** end lim_init_mlm() ***/ + +void lim_deactivate_timers(struct mac_context *mac_ctx) +{ + uint32_t n; + tLimTimers *lim_timer = &mac_ctx->lim.lim_timers; + + lim_deactivate_timers_host_roam(mac_ctx); + + /* Deactivate channel switch timer. */ + tx_timer_deactivate(&lim_timer->gLimChannelSwitchTimer); + + /* Deactivate addts response timer. */ + tx_timer_deactivate(&lim_timer->gLimAddtsRspTimer); + + if (tx_timer_running(&lim_timer->gLimJoinFailureTimer)) { + pe_err("Join failure timer running call the timeout API"); + /* Cleanup as if join timer expired */ + lim_timer_handler(mac_ctx, SIR_LIM_JOIN_FAIL_TIMEOUT); + } + /* Deactivate Join failure timer. */ + tx_timer_deactivate(&lim_timer->gLimJoinFailureTimer); + + /* Deactivate Periodic Join Probe Request timer. */ + tx_timer_deactivate(&lim_timer->gLimPeriodicJoinProbeReqTimer); + + /* Deactivate Auth Retry timer. */ + tx_timer_deactivate + (&lim_timer->g_lim_periodic_auth_retry_timer); + + if (tx_timer_running(&lim_timer->gLimAssocFailureTimer)) { + pe_err("Assoc failure timer running call the timeout API"); + /* Cleanup as if assoc timer expired */ + lim_assoc_failure_timer_handler(mac_ctx, LIM_ASSOC); + } + /* Deactivate Association failure timer. */ + tx_timer_deactivate(&lim_timer->gLimAssocFailureTimer); + + if (tx_timer_running(&mac_ctx->lim.lim_timers.gLimAuthFailureTimer)) { + pe_err("Auth failure timer running call the timeout API"); + /* Cleanup as if auth timer expired */ + lim_timer_handler(mac_ctx, SIR_LIM_AUTH_FAIL_TIMEOUT); + } + /* Deactivate Authentication failure timer. */ + tx_timer_deactivate(&lim_timer->gLimAuthFailureTimer); + + /* Deactivate wait-for-probe-after-Heartbeat timer. */ + tx_timer_deactivate(&lim_timer->gLimProbeAfterHBTimer); + + /* Deactivate cnf wait timer */ + for (n = 0; n < (mac_ctx->lim.maxStation + 1); n++) { + tx_timer_deactivate(&lim_timer->gpLimCnfWaitTimer[n]); + } + + /* Deactivate any Authentication response timers */ + lim_delete_pre_auth_list(mac_ctx); + + tx_timer_deactivate(&lim_timer->gLimUpdateOlbcCacheTimer); + tx_timer_deactivate(&lim_timer->gLimPreAuthClnupTimer); + + if (tx_timer_running(&lim_timer->gLimDisassocAckTimer)) { + pe_err("Disassoc timer running call the timeout API"); + lim_timer_handler(mac_ctx, SIR_LIM_DISASSOC_ACK_TIMEOUT); + } + tx_timer_deactivate(&lim_timer->gLimDisassocAckTimer); + + if (tx_timer_running(&lim_timer->gLimDeauthAckTimer)) { + pe_err("Deauth timer running call the timeout API"); + lim_timer_handler(mac_ctx, SIR_LIM_DEAUTH_ACK_TIMEOUT); + } + tx_timer_deactivate(&lim_timer->gLimDeauthAckTimer); + + if (tx_timer_running(&lim_timer->sae_auth_timer)) { + pe_err("SAE Auth failure timer running call the timeout API"); + /* Cleanup as if SAE auth timer expired */ + lim_timer_handler(mac_ctx, SIR_LIM_AUTH_SAE_TIMEOUT); + } + + tx_timer_deactivate(&lim_timer->sae_auth_timer); +} + +void lim_deactivate_timers_for_vdev(struct mac_context *mac_ctx, + uint8_t vdev_id) +{ + tLimTimers *lim_timer = &mac_ctx->lim.lim_timers; + struct pe_session *pe_session; + + pe_session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!pe_session) { + pe_err("pe session invalid for vdev %d", vdev_id); + return; + } + pe_debug("pe limMlmState %s vdev %d", + lim_mlm_state_str(pe_session->limMlmState), + vdev_id); + switch (pe_session->limMlmState) { + case eLIM_MLM_WT_JOIN_BEACON_STATE: + if (tx_timer_running( + &lim_timer->gLimJoinFailureTimer)) { + pe_debug("Trigger Join failure timeout for vdev %d", + vdev_id); + tx_timer_deactivate( + &lim_timer->gLimJoinFailureTimer); + lim_process_join_failure_timeout(mac_ctx); + } + break; + case eLIM_MLM_WT_AUTH_FRAME2_STATE: + case eLIM_MLM_WT_AUTH_FRAME4_STATE: + if (tx_timer_running( + &lim_timer->gLimAuthFailureTimer)) { + pe_debug("Trigger Auth failure timeout for vdev %d", + vdev_id); + tx_timer_deactivate( + &lim_timer->gLimAuthFailureTimer); + lim_process_auth_failure_timeout(mac_ctx); + } + break; + case eLIM_MLM_WT_ASSOC_RSP_STATE: + if (tx_timer_running( + &lim_timer->gLimAssocFailureTimer)) { + pe_debug("Trigger Assoc failure timeout for vdev %d", + vdev_id); + tx_timer_deactivate( + &lim_timer->gLimAssocFailureTimer); + lim_process_assoc_failure_timeout(mac_ctx, + LIM_ASSOC); + } + break; + case eLIM_MLM_WT_SAE_AUTH_STATE: + if (tx_timer_running(&lim_timer->sae_auth_timer)) { + pe_debug("Trigger SAE Auth failure timeout for vdev %d", + vdev_id); + tx_timer_deactivate( + &lim_timer->sae_auth_timer); + lim_process_sae_auth_timeout(mac_ctx); + } + break; + default: + return; + } +} + + +/** + * lim_cleanup_mlm() - This function is called to cleanup + * @mac_ctx: Pointer to Global MAC structure + * + * Function is called to cleanup any resources allocated by the MLM + * state machine. + * + * Return: none + */ +void lim_cleanup_mlm(struct mac_context *mac_ctx) +{ + uint32_t n; + + tLimPreAuthNode **pAuthNode; + tLimTimers *lim_timer = NULL; + + if (mac_ctx->lim.gLimTimersCreated == 1) { + lim_timer = &mac_ctx->lim.lim_timers; + + lim_deactivate_timers(mac_ctx); + + lim_delete_timers_host_roam(mac_ctx); + /* Delete channel switch timer. */ + tx_timer_delete(&lim_timer->gLimChannelSwitchTimer); + + /* Delete addts response timer. */ + tx_timer_delete(&lim_timer->gLimAddtsRspTimer); + + /* Delete Join failure timer. */ + tx_timer_delete(&lim_timer->gLimJoinFailureTimer); + + /* Delete Periodic Join Probe Request timer. */ + tx_timer_delete(&lim_timer->gLimPeriodicJoinProbeReqTimer); + + /* Delete Auth Retry timer. */ + tx_timer_delete(&lim_timer->g_lim_periodic_auth_retry_timer); + + /* Delete Association failure timer. */ + tx_timer_delete(&lim_timer->gLimAssocFailureTimer); + + /* Delete Authentication failure timer. */ + tx_timer_delete(&lim_timer->gLimAuthFailureTimer); + + /* Delete wait-for-probe-after-Heartbeat timer. */ + tx_timer_delete(&lim_timer->gLimProbeAfterHBTimer); + + /* Delete cnf wait timer */ + for (n = 0; n < (mac_ctx->lim.maxStation + 1); n++) { + tx_timer_delete(&lim_timer->gpLimCnfWaitTimer[n]); + } + + pAuthNode = mac_ctx->lim.gLimPreAuthTimerTable.pTable; + + /* Delete any Auth rsp timers, which might have been started */ + for (n = 0; n < mac_ctx->lim.gLimPreAuthTimerTable.numEntry; + n++) + tx_timer_delete(&pAuthNode[n]->timer); + + tx_timer_delete(&lim_timer->gLimUpdateOlbcCacheTimer); + tx_timer_delete(&lim_timer->gLimPreAuthClnupTimer); + + tx_timer_delete(&lim_timer->gLimDisassocAckTimer); + + tx_timer_delete(&lim_timer->gLimDeauthAckTimer); + + tx_timer_delete(&lim_timer->sae_auth_timer); + + mac_ctx->lim.gLimTimersCreated = 0; + } +} /*** end lim_cleanup_mlm() ***/ + +/** + * lim_print_mac_addr() + * + ***FUNCTION: + * This function is called to print passed MAC address + * in : format. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * @param macAddr - MacAddr to be printed + * @param logLevel - Loglevel to be used + * + * @return None. + */ + +void lim_print_mac_addr(struct mac_context *mac, tSirMacAddr macAddr, uint8_t logLevel) +{ + pe_debug(QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(macAddr)); +} /****** end lim_print_mac_addr() ******/ + +/* + * lim_reset_deferred_msg_q() + * + ***FUNCTION: + * This function resets the deferred message queue parameters. + * + ***PARAMS: + * @param mac - Pointer to Global MAC structure + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + ***RETURNS: + * None + */ + +void lim_reset_deferred_msg_q(struct mac_context *mac) +{ + struct scheduler_msg *read_msg = {0}; + + if (mac->lim.gLimDeferredMsgQ.size > 0) { + while ((read_msg = lim_read_deferred_msg_q(mac)) != NULL) { + pe_free_msg(mac, read_msg); + } + } + + mac->lim.gLimDeferredMsgQ.size = + mac->lim.gLimDeferredMsgQ.write = + mac->lim.gLimDeferredMsgQ.read = 0; + +} + +#define LIM_DEFERRED_Q_CHECK_THRESHOLD (MAX_DEFERRED_QUEUE_LEN/2) +#define LIM_MAX_NUM_MGMT_FRAME_DEFERRED (MAX_DEFERRED_QUEUE_LEN/2) + +/** + * lim_write_deferred_msg_q() - This function queues up a deferred message + * + * @mac_ctx: Pointer to Global MAC structure + * @lim_msg: a LIM message + * + * Function queues up a deferred message for later processing on the + * STA side. + * + * Return: none + */ + +uint8_t lim_write_deferred_msg_q(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg) +{ + uint8_t type = 0, subtype = 0; + + pe_debug("Queue a deferred message size: %d write: %d - type: 0x%x", + mac_ctx->lim.gLimDeferredMsgQ.size, + mac_ctx->lim.gLimDeferredMsgQ.write, + lim_msg->type); + + /* check if the deferred message queue is full */ + if (mac_ctx->lim.gLimDeferredMsgQ.size >= MAX_DEFERRED_QUEUE_LEN) { + if (!(mac_ctx->lim.deferredMsgCnt & 0xF)) { + pe_err("queue->MsgQ full Msg: %d Msgs Failed: %d", + lim_msg->type, + ++mac_ctx->lim.deferredMsgCnt); + cds_flush_logs(WLAN_LOG_TYPE_NON_FATAL, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_REASON_QUEUE_FULL, + false, false); + } else { + mac_ctx->lim.deferredMsgCnt++; + } + return TX_QUEUE_FULL; + } + + /* + * In the application, there should not be more than 1 message get + * queued up. If happens, flags a warning. In the future, this can + * happen. + */ + if (mac_ctx->lim.gLimDeferredMsgQ.size > 0) + pe_debug("%d Deferred Msg type: 0x%x global sme: %d global mlme: %d addts: %d", + mac_ctx->lim.gLimDeferredMsgQ.size, + lim_msg->type, + mac_ctx->lim.gLimSmeState, + mac_ctx->lim.gLimMlmState, + mac_ctx->lim.gLimAddtsSent); + + if (SIR_BB_XPORT_MGMT_MSG == lim_msg->type) { + lim_util_get_type_subtype(lim_msg->bodyptr, + &type, &subtype); + pe_debug(" Deferred management type %d subtype %d ", + type, subtype); + } + + /* + * To prevent the deferred Q is full of management frames, only give + * them certain space + */ + if ((SIR_BB_XPORT_MGMT_MSG == lim_msg->type) && + (LIM_DEFERRED_Q_CHECK_THRESHOLD < + mac_ctx->lim.gLimDeferredMsgQ.size)) { + uint16_t idx, count = 0; + + for (idx = 0; idx < mac_ctx->lim.gLimDeferredMsgQ.size; + idx++) { + if (SIR_BB_XPORT_MGMT_MSG == + mac_ctx->lim.gLimDeferredMsgQ. + deferredQueue[idx].type) { + count++; + } + } + if (LIM_MAX_NUM_MGMT_FRAME_DEFERRED < count) { + /* + * We reach the quota for management frames, + * drop this one + */ + pe_warn_rl("Too many queue->MsgQ Msg: %d count: %d", + lim_msg->type, count); + /* Return error, caller knows what to do */ + return TX_QUEUE_FULL; + } + } + + ++mac_ctx->lim.gLimDeferredMsgQ.size; + + /* reset the count here since we are able to defer the message */ + if (mac_ctx->lim.deferredMsgCnt != 0) + mac_ctx->lim.deferredMsgCnt = 0; + + /* if the write pointer hits the end of the queue, rewind it */ + if (mac_ctx->lim.gLimDeferredMsgQ.write >= MAX_DEFERRED_QUEUE_LEN) + mac_ctx->lim.gLimDeferredMsgQ.write = 0; + + /* save the message to the queue and advanced the write pointer */ + qdf_mem_copy((uint8_t *) &mac_ctx->lim.gLimDeferredMsgQ. + deferredQueue[mac_ctx->lim.gLimDeferredMsgQ.write++], + (uint8_t *) lim_msg, + sizeof(struct scheduler_msg)); + return TX_SUCCESS; + +} + +/* + * lim_read_deferred_msg_q() + * + ***FUNCTION: + * This function dequeues a deferred message for processing on the + * STA side. + * + ***PARAMS: + * @param mac - Pointer to Global MAC structure + * + ***LOGIC: + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * + * + ***RETURNS: + * Returns the message at the head of the deferred message queue + */ + +struct scheduler_msg *lim_read_deferred_msg_q(struct mac_context *mac) +{ + struct scheduler_msg *msg = {0}; + + /* + ** check any messages left. If no, return + **/ + if (mac->lim.gLimDeferredMsgQ.size <= 0) + return NULL; + + /* + ** decrement the queue size + **/ + mac->lim.gLimDeferredMsgQ.size--; + + /* + ** retrieve the message from the head of the queue + **/ + msg = + &mac->lim.gLimDeferredMsgQ.deferredQueue[mac->lim. + gLimDeferredMsgQ.read]; + + /* + ** advance the read pointer + **/ + mac->lim.gLimDeferredMsgQ.read++; + + /* + ** if the read pointer hits the end of the queue, rewind it + **/ + if (mac->lim.gLimDeferredMsgQ.read >= MAX_DEFERRED_QUEUE_LEN) + mac->lim.gLimDeferredMsgQ.read = 0; + + pe_debug("DeQueue a deferred message size: %d read: %d - type: 0x%x", + mac->lim.gLimDeferredMsgQ.size, + mac->lim.gLimDeferredMsgQ.read, msg->type); + + pe_debug("DQ msg -- global sme: %d global mlme: %d addts: %d", + mac->lim.gLimSmeState, mac->lim.gLimMlmState, + mac->lim.gLimAddtsSent); + + return msg; +} + +/* + * lim_handle_update_olbc_cache() - This function update olbc cache + * + * @mac_ctx: Pointer to Global MAC structure + * + * Function updates olbc cache + * + * Return: none + */ +void lim_handle_update_olbc_cache(struct mac_context *mac_ctx) +{ + int i; + static int enable; + tUpdateBeaconParams beaconParams; + + struct pe_session *pe_session = lim_is_ap_session_active(mac_ctx); + + if (!pe_session) { + pe_debug(" Session not found"); + return; + } + + if (pe_session->is_session_obss_offload_enabled) { + pe_debug("protection offloaded"); + return; + } + qdf_mem_zero((uint8_t *) &beaconParams, sizeof(tUpdateBeaconParams)); + beaconParams.bss_idx = pe_session->vdev_id; + + beaconParams.paramChangeBitmap = 0; + /* + * This is doing a 2 pass check. The first pass is to invalidate + * all the cache entries. The second pass is to decide whether to + * disable protection. + */ + if (!enable) { + pe_debug("Resetting OLBC cache"); + pe_session->gLimOlbcParams.numSta = 0; + pe_session->gLimOverlap11gParams.numSta = 0; + pe_session->gLimOverlapHt20Params.numSta = 0; + pe_session->gLimNonGfParams.numSta = 0; + pe_session->gLimLsigTxopParams.numSta = 0; + + for (i = 0; i < LIM_PROT_STA_OVERLAP_CACHE_SIZE; i++) + mac_ctx->lim.protStaOverlapCache[i].active = false; + + enable = 1; + } else { + if ((!pe_session->gLimOlbcParams.numSta) && + (pe_session->gLimOlbcParams.protectionEnabled) && + (!pe_session->gLim11bParams.protectionEnabled)) { + pe_debug("Overlap cache clear and no 11B STA set"); + lim_enable11g_protection(mac_ctx, false, true, + &beaconParams, + pe_session); + } + + if ((!pe_session->gLimOverlap11gParams.numSta) && + (pe_session->gLimOverlap11gParams.protectionEnabled) + && (!pe_session->gLim11gParams.protectionEnabled)) { + pe_debug("Overlap cache clear and no 11G STA set"); + lim_enable_ht_protection_from11g(mac_ctx, false, true, + &beaconParams, + pe_session); + } + + if ((!pe_session->gLimOverlapHt20Params.numSta) && + (pe_session->gLimOverlapHt20Params.protectionEnabled) + && (!pe_session->gLimHt20Params.protectionEnabled)) { + pe_debug("Overlap cache clear and no HT20 STA set"); + lim_enable11g_protection(mac_ctx, false, true, + &beaconParams, + pe_session); + } + + enable = 0; + } + + if ((false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running) + && beaconParams.paramChangeBitmap) { + sch_set_fixed_beacon_fields(mac_ctx, pe_session); + lim_send_beacon_params(mac_ctx, &beaconParams, pe_session); + } + /* Start OLBC timer */ + if (tx_timer_activate(&mac_ctx->lim.lim_timers.gLimUpdateOlbcCacheTimer) + != TX_SUCCESS) + pe_err("tx_timer_activate failed"); +} + +/** + * lim_is_null_ssid() - This function checks if ssid supplied is Null SSID + * @ssid: pointer to tSirMacSSid + * + * Function checks if ssid supplied is Null SSID + * + * Return: none + */ + +uint8_t lim_is_null_ssid(tSirMacSSid *ssid) +{ + uint8_t fnull_ssid = false; + uint32_t ssid_len; + uint8_t *ssid_str; + + if (0 == ssid->length) { + fnull_ssid = true; + return fnull_ssid; + } + /* If the first charactes is space, then check if all + * characters in SSID are spaces to consider it as NULL SSID + */ + if ((ASCII_SPACE_CHARACTER == ssid->ssId[0]) && + (ssid->length == 1)) { + fnull_ssid = true; + return fnull_ssid; + } else { + /* check if all the charactes in SSID are NULL */ + ssid_len = ssid->length; + ssid_str = ssid->ssId; + + while (ssid_len) { + if (*ssid_str) + return fnull_ssid; + + ssid_str++; + ssid_len--; + } + + if (0 == ssid_len) { + fnull_ssid = true; + return fnull_ssid; + } + } + + return fnull_ssid; +} + +/** ------------------------------------------------------------- + \fn lim_update_prot_sta_params + \brief updates protection related counters. + \param struct mac_context * mac + \param tSirMacAddr peerMacAddr + \param tLimProtStaCacheType protStaCacheType + \param tHalBitVal gfSupported + \param tHalBitVal lsigTxopSupported + \return None + -------------------------------------------------------------*/ +static void +lim_update_prot_sta_params(struct mac_context *mac, + tSirMacAddr peerMacAddr, + tLimProtStaCacheType protStaCacheType, + tHalBitVal gfSupported, tHalBitVal lsigTxopSupported, + struct pe_session *pe_session) +{ + uint32_t i; + + pe_debug("Associated STA addr is:"); + lim_print_mac_addr(mac, peerMacAddr, LOGD); + + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (pe_session->protStaCache[i].active) { + pe_debug("Addr:"); + lim_print_mac_addr + (mac, pe_session->protStaCache[i].addr, + LOGD); + + if (!qdf_mem_cmp + (pe_session->protStaCache[i].addr, + peerMacAddr, sizeof(tSirMacAddr))) { + pe_debug("matching cache entry at: %d already active", + i); + return; + } + } + } + + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (!pe_session->protStaCache[i].active) + break; + } + + if (i >= LIM_PROT_STA_CACHE_SIZE) { + pe_err("No space in ProtStaCache"); + return; + } + + qdf_mem_copy(pe_session->protStaCache[i].addr, + peerMacAddr, sizeof(tSirMacAddr)); + + pe_session->protStaCache[i].protStaCacheType = protStaCacheType; + pe_session->protStaCache[i].active = true; + if (eLIM_PROT_STA_CACHE_TYPE_llB == protStaCacheType) { + pe_session->gLim11bParams.numSta++; + pe_debug("11B,"); + } else if (eLIM_PROT_STA_CACHE_TYPE_llG == protStaCacheType) { + pe_session->gLim11gParams.numSta++; + pe_debug("11G,"); + } else if (eLIM_PROT_STA_CACHE_TYPE_HT20 == protStaCacheType) { + pe_session->gLimHt20Params.numSta++; + pe_debug("HT20,"); + } + + if (!gfSupported) { + pe_session->gLimNonGfParams.numSta++; + pe_debug("NonGf,"); + } + if (!lsigTxopSupported) { + pe_session->gLimLsigTxopParams.numSta++; + pe_debug("!lsigTxopSupported"); + } +} /* --------------------------------------------------------------------- */ + +/** ------------------------------------------------------------- + \fn lim_decide_ap_protection + \brief Decides all the protection related staiton coexistence and also sets + \ short preamble and short slot appropriately. This function will be called + \ when AP is ready to send assocRsp tp the station joining right now. + \param struct mac_context * mac + \param tSirMacAddr peerMacAddr + \return None + -------------------------------------------------------------*/ +void +lim_decide_ap_protection(struct mac_context *mac, tSirMacAddr peerMacAddr, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + uint16_t tmpAid; + tpDphHashNode sta; + enum reg_wifi_band rfBand = REG_BAND_UNKNOWN; + uint32_t phyMode; + tLimProtStaCacheType protStaCacheType = + eLIM_PROT_STA_CACHE_TYPE_INVALID; + tHalBitVal gfSupported = eHAL_SET, lsigTxopSupported = eHAL_SET; + + pBeaconParams->paramChangeBitmap = 0; + /* check whether to enable protection or not */ + sta = + dph_lookup_hash_entry(mac, peerMacAddr, &tmpAid, + &pe_session->dph.dphHashTable); + if (!sta) { + pe_err("sta is NULL"); + return; + } + lim_get_rf_band_new(mac, &rfBand, pe_session); + /* if we are in 5 GHZ band */ + if (REG_BAND_5G == rfBand) { + /* We are 11N. we need to protect from 11A and Ht20. we don't need any other protection in 5 GHZ. */ + /* HT20 case is common between both the bands and handled down as common code. */ + if (true == pe_session->htCapability) { + /* we are 11N and 11A station is joining. */ + /* protection from 11A required. */ + if (false == sta->mlmStaContext.htCapability) { + lim_update_11a_protection(mac, true, false, + pBeaconParams, + pe_session); + return; + } + } + } else if (REG_BAND_2G == rfBand) { + lim_get_phy_mode(mac, &phyMode, pe_session); + + /* We are 11G. Check if we need protection from 11b Stations. */ + if ((phyMode == WNI_CFG_PHY_MODE_11G) && + (false == pe_session->htCapability)) { + + if (sta->erpEnabled == eHAL_CLEAR) { + protStaCacheType = eLIM_PROT_STA_CACHE_TYPE_llB; + /* enable protection */ + pe_debug("Enabling protection from 11B"); + lim_enable11g_protection(mac, true, false, + pBeaconParams, + pe_session); + } + } + /* HT station. */ + if (true == pe_session->htCapability) { + /* check if we need protection from 11b station */ + if ((sta->erpEnabled == eHAL_CLEAR) && + (!sta->mlmStaContext.htCapability)) { + protStaCacheType = eLIM_PROT_STA_CACHE_TYPE_llB; + /* enable protection */ + pe_debug("Enabling protection from 11B"); + lim_enable11g_protection(mac, true, false, + pBeaconParams, + pe_session); + } + /* station being joined is non-11b and non-ht ==> 11g device */ + else if (!sta->mlmStaContext.htCapability) { + protStaCacheType = eLIM_PROT_STA_CACHE_TYPE_llG; + /* enable protection */ + lim_enable_ht_protection_from11g(mac, true, false, + pBeaconParams, + pe_session); + } + /* ERP mode is enabled for the latest station joined */ + /* latest station joined is HT capable */ + /* This case is being handled in common code (commn between both the bands) below. */ + } + } + /* we are HT and HT station is joining. This code is common for both the bands. */ + if ((true == pe_session->htCapability) && + (true == sta->mlmStaContext.htCapability)) { + if (!sta->htGreenfield) { + lim_enable_ht_non_gf_protection(mac, true, false, + pBeaconParams, + pe_session); + gfSupported = eHAL_CLEAR; + } + /* Station joining is HT 20Mhz */ + if ((eHT_CHANNEL_WIDTH_20MHZ == + sta->htSupportedChannelWidthSet) && + (eHT_CHANNEL_WIDTH_20MHZ != + pe_session->htSupportedChannelWidthSet)){ + protStaCacheType = eLIM_PROT_STA_CACHE_TYPE_HT20; + lim_enable_ht20_protection(mac, true, false, + pBeaconParams, pe_session); + } + /* Station joining does not support LSIG TXOP Protection */ + if (!sta->htLsigTXOPProtection) { + lim_enable_ht_lsig_txop_protection(mac, false, false, + pBeaconParams, + pe_session); + lsigTxopSupported = eHAL_CLEAR; + } + } + + lim_update_prot_sta_params(mac, peerMacAddr, protStaCacheType, + gfSupported, lsigTxopSupported, pe_session); + + return; +} + +/** ------------------------------------------------------------- + \fn lim_enable_overlap11g_protection + \brief wrapper function for setting overlap 11g protection. + \param struct mac_context * mac + \param tpUpdateBeaconParams pBeaconParams + \param tpSirMacMgmtHdr pMh + \return None + -------------------------------------------------------------*/ +void +lim_enable_overlap11g_protection(struct mac_context *mac, + tpUpdateBeaconParams pBeaconParams, + tpSirMacMgmtHdr pMh, struct pe_session *pe_session) +{ + lim_update_overlap_sta_param(mac, pMh->bssId, + &(pe_session->gLimOlbcParams)); + + if (pe_session->gLimOlbcParams.numSta && + !pe_session->gLimOlbcParams.protectionEnabled) { + /* enable protection */ + pe_debug("OLBC happens!!!"); + lim_enable11g_protection(mac, true, true, pBeaconParams, + pe_session); + } +} + +/** + * lim_update_short_preamble() - This function Updates short preamble + * @mac_ctx: pointer to Global MAC structure + * @peer_mac_addr: pointer to tSirMacAddr + * @pbeaconparams: pointer to tpUpdateBeaconParams + * @psession_entry: pointer to struct pe_session * + * + * Function Updates short preamble if needed when a new station joins + * + * Return: none + */ +void +lim_update_short_preamble(struct mac_context *mac_ctx, tSirMacAddr peer_mac_addr, + tpUpdateBeaconParams beaconparams, + struct pe_session *psession_entry) +{ + uint16_t aid; + tpDphHashNode sta_ds; + uint32_t phy_mode; + uint16_t i; + + /* check whether to enable protection or not */ + sta_ds = + dph_lookup_hash_entry(mac_ctx, peer_mac_addr, &aid, + &psession_entry->dph.dphHashTable); + + lim_get_phy_mode(mac_ctx, &phy_mode, psession_entry); + + if (!sta_ds || phy_mode != WNI_CFG_PHY_MODE_11G) + return; + + if (sta_ds->shortPreambleEnabled != eHAL_CLEAR) + return; + + pe_debug("Short Preamble is not enabled in Assoc Req from"); + + lim_print_mac_addr(mac_ctx, peer_mac_addr, LOGD); + + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (LIM_IS_AP_ROLE(psession_entry) && + (psession_entry->gLimNoShortParams. + staNoShortCache[i].active) && + (!qdf_mem_cmp + (psession_entry->gLimNoShortParams. + staNoShortCache[i].addr, + peer_mac_addr, sizeof(tSirMacAddr)))) + return; + else if (!LIM_IS_AP_ROLE(psession_entry) && + (mac_ctx->lim.gLimNoShortParams. + staNoShortCache[i].active) && + (!qdf_mem_cmp(mac_ctx->lim.gLimNoShortParams. + staNoShortCache[i].addr, + peer_mac_addr, + sizeof(tSirMacAddr)))) + return; + } + + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (LIM_IS_AP_ROLE(psession_entry) && + !psession_entry->gLimNoShortParams. + staNoShortCache[i].active) + break; + else if (!mac_ctx->lim.gLimNoShortParams. + staNoShortCache[i].active) + break; + } + + if (i >= LIM_PROT_STA_CACHE_SIZE) { + tLimNoShortParams *lim_params = + &psession_entry->gLimNoShortParams; + if (LIM_IS_AP_ROLE(psession_entry)) { + pe_err("No space in Short cache active: %d sta: %d for sta", + i, lim_params->numNonShortPreambleSta); + lim_print_mac_addr(mac_ctx, peer_mac_addr, LOGE); + return; + } else { + pe_err("No space in Short cache active: %d sta: %d for sta", + i, lim_params->numNonShortPreambleSta); + lim_print_mac_addr(mac_ctx, peer_mac_addr, LOGE); + return; + } + + } + + if (LIM_IS_AP_ROLE(psession_entry)) { + qdf_mem_copy(psession_entry->gLimNoShortParams. + staNoShortCache[i].addr, + peer_mac_addr, sizeof(tSirMacAddr)); + psession_entry->gLimNoShortParams.staNoShortCache[i]. + active = true; + psession_entry->gLimNoShortParams.numNonShortPreambleSta++; + } else { + qdf_mem_copy(mac_ctx->lim.gLimNoShortParams. + staNoShortCache[i].addr, + peer_mac_addr, sizeof(tSirMacAddr)); + mac_ctx->lim.gLimNoShortParams.staNoShortCache[i].active = true; + mac_ctx->lim.gLimNoShortParams.numNonShortPreambleSta++; + } + + /* enable long preamble */ + pe_debug("Disabling short preamble"); + + if (lim_enable_short_preamble(mac_ctx, false, beaconparams, + psession_entry) != QDF_STATUS_SUCCESS) + pe_err("Cannot enable long preamble"); +} + +/** + * lim_update_short_slot_time() - This function Updates short slot time + * @mac_ctx: pointer to Global MAC structure + * @peer_mac_addr: pointer to tSirMacAddr + * @beacon_params: pointer to tpUpdateBeaconParams + * @psession_entry: pointer to struct pe_session * + * + * Function Updates short slot time if needed when a new station joins + * + * Return: None + */ +void +lim_update_short_slot_time(struct mac_context *mac_ctx, tSirMacAddr peer_mac_addr, + tpUpdateBeaconParams beacon_params, + struct pe_session *session_entry) +{ + uint16_t aid; + tpDphHashNode sta_ds; + uint32_t phy_mode; + uint32_t val; + uint16_t i; + + /* check whether to enable protection or not */ + sta_ds = dph_lookup_hash_entry(mac_ctx, peer_mac_addr, &aid, + &session_entry->dph.dphHashTable); + lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); + + if (!sta_ds || phy_mode != WNI_CFG_PHY_MODE_11G) + return; + + /* + * Only in case of softap in 11g mode, slot time might change + * depending on the STA being added. In 11a case, it should + * be always 1 and in 11b case, it should be always 0. + * Only when the new STA has short slot time disabled, we need to + * change softap's overall slot time settings else the default for + * softap is always short slot enabled. When the last long slot STA + * leaves softAP, we take care of it in lim_decide_short_slot + */ + if (sta_ds->shortSlotTimeEnabled != eHAL_CLEAR) + return; + + pe_debug("Short Slot Time is not enabled in Assoc Req from"); + lim_print_mac_addr(mac_ctx, peer_mac_addr, LOGD); + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (LIM_IS_AP_ROLE(session_entry) && + session_entry->gLimNoShortSlotParams. + staNoShortSlotCache[i].active) { + if (!qdf_mem_cmp(session_entry-> + gLimNoShortSlotParams.staNoShortSlotCache[i].addr, + peer_mac_addr, sizeof(tSirMacAddr))) + return; + } else if (!LIM_IS_AP_ROLE(session_entry)) { + if (mac_ctx->lim.gLimNoShortSlotParams. + staNoShortSlotCache[i].active) { + if (!qdf_mem_cmp(mac_ctx-> + lim.gLimNoShortSlotParams. + staNoShortSlotCache[i].addr, + peer_mac_addr, sizeof(tSirMacAddr))) + return; + } + } + } + for (i = 0; i < LIM_PROT_STA_CACHE_SIZE; i++) { + if (LIM_IS_AP_ROLE(session_entry) && + !session_entry->gLimNoShortSlotParams. + staNoShortSlotCache[i].active) + break; + else + if (!mac_ctx->lim.gLimNoShortSlotParams. + staNoShortSlotCache[i].active) + break; + } + + if (i >= LIM_PROT_STA_CACHE_SIZE) { + if (LIM_IS_AP_ROLE(session_entry)) { + pe_err("No space in ShortSlot cache active: %d sta: %d for sta", + i, session_entry->gLimNoShortSlotParams. + numNonShortSlotSta); + lim_print_mac_addr(mac_ctx, peer_mac_addr, LOGE); + return; + } else { + pe_err("No space in ShortSlot cache active: %d sta: %d for sta", + i, mac_ctx->lim.gLimNoShortSlotParams. + numNonShortSlotSta); + lim_print_mac_addr(mac_ctx, peer_mac_addr, LOGE); + return; + } + } + + if (LIM_IS_AP_ROLE(session_entry)) { + qdf_mem_copy(session_entry->gLimNoShortSlotParams. + staNoShortSlotCache[i].addr, + peer_mac_addr, sizeof(tSirMacAddr)); + session_entry->gLimNoShortSlotParams. + staNoShortSlotCache[i].active = true; + session_entry->gLimNoShortSlotParams.numNonShortSlotSta++; + } else { + qdf_mem_copy(mac_ctx->lim.gLimNoShortSlotParams. + staNoShortSlotCache[i].addr, + peer_mac_addr, sizeof(tSirMacAddr)); + mac_ctx->lim.gLimNoShortSlotParams. + staNoShortSlotCache[i].active = true; + mac_ctx->lim.gLimNoShortSlotParams. + numNonShortSlotSta++; + } + val = mac_ctx->mlme_cfg->feature_flags.enable_short_slot_time_11g; + /* + * Here we check if we are AP role and short slot enabled + * (both admin and oper modes) but we have atleast one STA + * connected with only long slot enabled, we need to change + * our beacon/pb rsp to broadcast short slot disabled + */ + if ((LIM_IS_AP_ROLE(session_entry)) && (val && + session_entry->gLimNoShortSlotParams.numNonShortSlotSta + && session_entry->shortSlotTimeSupported)) { + /* enable long slot time */ + beacon_params->fShortSlotTime = false; + beacon_params->paramChangeBitmap |= + PARAM_SHORT_SLOT_TIME_CHANGED; + pe_debug("Disable short slot time. Enable long slot time"); + session_entry->shortSlotTimeSupported = false; + } else if (!LIM_IS_AP_ROLE(session_entry) && + (val && mac_ctx->lim.gLimNoShortSlotParams. + numNonShortSlotSta && + session_entry->shortSlotTimeSupported)) { + /* enable long slot time */ + beacon_params->fShortSlotTime = false; + beacon_params->paramChangeBitmap |= + PARAM_SHORT_SLOT_TIME_CHANGED; + pe_debug("Disable short slot time. Enable long slot time"); + session_entry->shortSlotTimeSupported = false; + } +} + +/** ------------------------------------------------------------- + \fn lim_decide_sta_protection_on_assoc + \brief Decide protection related settings on Sta while association. + \param struct mac_context * mac + \param tpSchBeaconStruct pBeaconStruct + \return None + -------------------------------------------------------------*/ +void +lim_decide_sta_protection_on_assoc(struct mac_context *mac, + tpSchBeaconStruct pBeaconStruct, + struct pe_session *pe_session) +{ + enum reg_wifi_band rfBand = REG_BAND_UNKNOWN; + uint32_t phyMode = WNI_CFG_PHY_MODE_NONE; + + lim_get_rf_band_new(mac, &rfBand, pe_session); + lim_get_phy_mode(mac, &phyMode, pe_session); + + if (REG_BAND_5G == rfBand) { + if ((eSIR_HT_OP_MODE_MIXED == pBeaconStruct->HTInfo.opMode) || + (eSIR_HT_OP_MODE_OVERLAP_LEGACY == + pBeaconStruct->HTInfo.opMode)) { + if (mac->lim.cfgProtection.fromlla) + pe_session->beaconParams.llaCoexist = true; + } else if (eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT == + pBeaconStruct->HTInfo.opMode) { + if (mac->lim.cfgProtection.ht20) + pe_session->beaconParams.ht20Coexist = true; + } + + } else if (REG_BAND_2G == rfBand) { + /* spec 7.3.2.13 */ + /* UseProtection will be set when nonERP STA is associated. */ + /* NonERPPresent bit will be set when: */ + /* --nonERP Sta is associated OR */ + /* --nonERP Sta exists in overlapping BSS */ + /* when useProtection is not set then protection from nonERP stations is optional. */ + + /* CFG protection from 11b is enabled and */ + /* 11B device in the BSS */ + /* TODO, This is not sessionized */ + if (phyMode != WNI_CFG_PHY_MODE_11B) { + if (mac->lim.cfgProtection.fromllb && + pBeaconStruct->erpPresent && + (pBeaconStruct->erpIEInfo.useProtection || + pBeaconStruct->erpIEInfo.nonErpPresent)) { + pe_session->beaconParams.llbCoexist = true; + } + /* AP has no 11b station associated. */ + else { + pe_session->beaconParams.llbCoexist = false; + } + } + /* following code block is only for HT station. */ + if ((pe_session->htCapability) && + (pBeaconStruct->HTInfo.present)) { + tDot11fIEHTInfo htInfo = pBeaconStruct->HTInfo; + + /* Obss Non HT STA present mode */ + pe_session->beaconParams.gHTObssMode = + (uint8_t) htInfo.obssNonHTStaPresent; + + /* CFG protection from 11G is enabled and */ + /* our AP has at least one 11G station associated. */ + if (mac->lim.cfgProtection.fromllg && + ((eSIR_HT_OP_MODE_MIXED == htInfo.opMode) || + (eSIR_HT_OP_MODE_OVERLAP_LEGACY == htInfo.opMode)) + && (!pe_session->beaconParams.llbCoexist)) { + if (mac->lim.cfgProtection.fromllg) + pe_session->beaconParams.llgCoexist = + true; + } + /* AP has only HT stations associated and at least one station is HT 20 */ + /* disable protection from any non-HT devices. */ + /* decision for disabling protection from 11b has already been taken above. */ + if (eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT == htInfo.opMode) { + /* Disable protection from 11G station. */ + pe_session->beaconParams.llgCoexist = false; + /* CFG protection from HT 20 is enabled. */ + if (mac->lim.cfgProtection.ht20) + pe_session->beaconParams. + ht20Coexist = true; + } + /* Disable protection from non-HT and HT20 devices. */ + /* decision for disabling protection from 11b has already been taken above. */ + if (eSIR_HT_OP_MODE_PURE == htInfo.opMode) { + pe_session->beaconParams.llgCoexist = false; + pe_session->beaconParams.ht20Coexist = false; + } + + } + } + /* protection related factors other than HT operating mode. Applies to 2.4 GHZ as well as 5 GHZ. */ + if ((pe_session->htCapability) && (pBeaconStruct->HTInfo.present)) { + tDot11fIEHTInfo htInfo = pBeaconStruct->HTInfo; + + pe_session->beaconParams.fRIFSMode = + (uint8_t) htInfo.rifsMode; + pe_session->beaconParams.llnNonGFCoexist = + (uint8_t) htInfo.nonGFDevicesPresent; + pe_session->beaconParams.fLsigTXOPProtectionFullSupport = + (uint8_t) htInfo.lsigTXOPProtectionFullSupport; + } +} + + +/** + * lim_decide_sta_11bg_protection() - decides protection related settings on sta + * @mac_ctx: pointer to global mac structure + * @beacon_struct: pointer to tpschbeaconstruct + * @beaconparams: pointer to tpupdatebeaconparams + * @psession_entry: pointer to tppesession + * @phy_mode: phy mode index + * + * decides 11bg protection related settings on sta while processing beacon + * + * Return: none + */ +static void +lim_decide_sta_11bg_protection(struct mac_context *mac_ctx, + tpSchBeaconStruct beacon_struct, + tpUpdateBeaconParams beaconparams, + struct pe_session *psession_entry, + uint32_t phy_mode) +{ + + tDot11fIEHTInfo htInfo; + + /* + * spec 7.3.2.13 + * UseProtection will be set when nonERP STA is associated. + * NonERPPresent bit will be set when: + * --nonERP Sta is associated OR + * --nonERP Sta exists in overlapping BSS + * when useProtection is not set then protection from + * nonERP stations is optional. + */ + if (phy_mode != WNI_CFG_PHY_MODE_11B) { + if (beacon_struct->erpPresent && + (beacon_struct->erpIEInfo.useProtection || + beacon_struct->erpIEInfo.nonErpPresent)) { + lim_enable11g_protection(mac_ctx, true, false, + beaconparams, + psession_entry); + } + /* AP has no 11b station associated. */ + else { + /* disable protection from 11b station */ + lim_enable11g_protection(mac_ctx, false, false, + beaconparams, + psession_entry); + } + } + + if (!(psession_entry->htCapability) || + !(beacon_struct->HTInfo.present)) + return; + + /* following code is only for HT station. */ + + htInfo = beacon_struct->HTInfo; + /* AP has at least one 11G station associated. */ + if (((eSIR_HT_OP_MODE_MIXED == htInfo.opMode) || + (eSIR_HT_OP_MODE_OVERLAP_LEGACY == htInfo.opMode)) && + (!psession_entry->beaconParams.llbCoexist)) { + lim_enable_ht_protection_from11g(mac_ctx, true, false, + beaconparams, psession_entry); + + } + /* + * no HT operating mode change ==> no change in + * protection settings except for MIXED_MODE/Legacy + * Mode. + */ + /* + * in Mixed mode/legacy Mode even if there is no + * change in HT operating mode, there might be + * change in 11bCoexist or 11gCoexist. Hence this + * check is being done after mixed/legacy mode + * check. + */ + if (mac_ctx->lim.gHTOperMode != + (tSirMacHTOperatingMode)htInfo.opMode) { + mac_ctx->lim.gHTOperMode = + (tSirMacHTOperatingMode) htInfo.opMode; + /* + * AP has only HT stations associated and + * at least one station is HT 20 + */ + + /* disable protection from any non-HT devices. */ + + /* + * decision for disabling protection from + * 11b has already been taken above. + */ + if (eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT == + htInfo.opMode) { + /* Disable protection from 11G station. */ + lim_enable_ht_protection_from11g(mac_ctx, false, + false, beaconparams, + psession_entry); + + lim_enable_ht20_protection(mac_ctx, true, false, + beaconparams, + psession_entry); + } + /* + * Disable protection from non-HT and + * HT20 devices. + */ + /* + * decision for disabling protection from + * 11b has already been taken above. + */ + else if (eSIR_HT_OP_MODE_PURE == htInfo.opMode) { + lim_enable_ht_protection_from11g(mac_ctx, false, + false, beaconparams, + psession_entry); + + lim_enable_ht20_protection(mac_ctx, false, + false, beaconparams, + psession_entry); + + } + } + +} + +/** + * lim_decide_sta_protection() - decides protection related settings on sta + * @mac_ctx: pointer to global mac structure + * @beacon_struct: pointer to tpschbeaconstruct + * @beaconparams: pointer to tpupdatebeaconparams + * @psession_entry: pointer to tppesession + * + * decides protection related settings on sta while processing beacon + * + * Return: none + */ +void +lim_decide_sta_protection(struct mac_context *mac_ctx, + tpSchBeaconStruct beacon_struct, + tpUpdateBeaconParams beaconparams, + struct pe_session *psession_entry) +{ + + enum reg_wifi_band rfband = REG_BAND_UNKNOWN; + uint32_t phy_mode = WNI_CFG_PHY_MODE_NONE; + + lim_get_rf_band_new(mac_ctx, &rfband, psession_entry); + lim_get_phy_mode(mac_ctx, &phy_mode, psession_entry); + + if ((REG_BAND_5G == rfband) && + /* we are HT capable. */ + (true == psession_entry->htCapability) && + (beacon_struct->HTInfo.present)) { + /* + * we are HT capable, AP's HT OPMode is + * mixed / overlap legacy ==> need protection + * from 11A. + */ + if ((eSIR_HT_OP_MODE_MIXED == + beacon_struct->HTInfo.opMode) || + (eSIR_HT_OP_MODE_OVERLAP_LEGACY == + beacon_struct->HTInfo.opMode)) { + lim_update_11a_protection(mac_ctx, true, false, + beaconparams, psession_entry); + } + /* + * we are HT capable, AP's HT OPMode is + * HT20 ==> disable protection from 11A if + * enabled. + */ + /* protection from HT20 if needed. */ + else if (eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT == + beacon_struct->HTInfo.opMode) { + lim_update_11a_protection(mac_ctx, false, false, + beaconparams, psession_entry); + lim_enable_ht20_protection(mac_ctx, true, false, + beaconparams, psession_entry); + } else if (eSIR_HT_OP_MODE_PURE == + beacon_struct->HTInfo.opMode) { + lim_update_11a_protection(mac_ctx, false, false, + beaconparams, psession_entry); + lim_enable_ht20_protection(mac_ctx, false, + false, beaconparams, + psession_entry); + } + } else if (REG_BAND_2G == rfband) { + lim_decide_sta_11bg_protection(mac_ctx, beacon_struct, + beaconparams, psession_entry, phy_mode); + } + /* + * following code block is only for HT station. + * (2.4 GHZ as well as 5 GHZ) + */ + if ((psession_entry->htCapability) && (beacon_struct->HTInfo.present)) { + tDot11fIEHTInfo htInfo = beacon_struct->HTInfo; + /* + * Check for changes in protection related factors other + * than HT operating mode. + */ + /* + * Check for changes in RIFS mode, nonGFDevicesPresent, + * lsigTXOPProtectionFullSupport. + */ + if (psession_entry->beaconParams.fRIFSMode != + (uint8_t) htInfo.rifsMode) { + beaconparams->fRIFSMode = + psession_entry->beaconParams.fRIFSMode = + (uint8_t) htInfo.rifsMode; + beaconparams->paramChangeBitmap |= + PARAM_RIFS_MODE_CHANGED; + } + + if (psession_entry->beaconParams.llnNonGFCoexist != + htInfo.nonGFDevicesPresent) { + beaconparams->llnNonGFCoexist = + psession_entry->beaconParams.llnNonGFCoexist = + (uint8_t) htInfo.nonGFDevicesPresent; + beaconparams->paramChangeBitmap |= + PARAM_NON_GF_DEVICES_PRESENT_CHANGED; + } + + if (psession_entry->beaconParams. + fLsigTXOPProtectionFullSupport != + (uint8_t) htInfo.lsigTXOPProtectionFullSupport) { + beaconparams->fLsigTXOPProtectionFullSupport = + psession_entry->beaconParams. + fLsigTXOPProtectionFullSupport = + (uint8_t) htInfo. + lsigTXOPProtectionFullSupport; + beaconparams->paramChangeBitmap |= + PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED; + } + /* + * For Station just update the global lim variable, + * no need to send message to HAL since Station already + * taking care of HT OPR Mode=01, + * meaning AP is seeing legacy + */ + /* stations in overlapping BSS. */ + if (psession_entry->beaconParams.gHTObssMode != + (uint8_t) htInfo.obssNonHTStaPresent) + psession_entry->beaconParams.gHTObssMode = + (uint8_t) htInfo.obssNonHTStaPresent; + + } +} + +/** + * __lim_process_channel_switch_timeout() + * + ***FUNCTION: + * This function is invoked when Channel Switch Timer expires at + * the STA. Now, STA must stop traffic, and then change/disable + * primary or secondary channel. + * + * + ***NOTE: + * @param pe_session - Pointer to pe session + * @return None + */ +static void __lim_process_channel_switch_timeout(struct pe_session *pe_session) +{ + struct mac_context *mac; + uint32_t channel_freq; + + if (!pe_session) { + pe_err("Invalid pe session"); + return; + } + mac = pe_session->mac_ctx; + if (!mac) { + pe_err("Invalid mac context"); + return; + } + + if (!LIM_IS_STA_ROLE(pe_session)) { + pe_warn("Channel switch can be done only in STA role, Current Role: %d", + GET_LIM_SYSTEM_ROLE(pe_session)); + return; + } + + if (pe_session->gLimSpecMgmt.dot11hChanSwState != + eLIM_11H_CHANSW_RUNNING) { + pe_warn("Channel switch timer should not have been running in state: %d", + pe_session->gLimSpecMgmt.dot11hChanSwState); + return; + } + + channel_freq = pe_session->gLimChannelSwitch.sw_target_freq; + /* Restore Channel Switch parameters to default */ + pe_session->gLimChannelSwitch.switchTimeoutValue = 0; + + /* Channel-switch timeout has occurred. reset the state */ + pe_session->gLimSpecMgmt.dot11hChanSwState = eLIM_11H_CHANSW_END; + + /* Check if the AP is switching to a channel that we support. + * Else, just don't bother to switch. Indicate HDD to look for a + * better AP to associate + */ + if (!lim_is_channel_valid_for_channel_switch(mac, channel_freq)) { + /* We need to restore pre-channelSwitch state on the STA */ + if (lim_restore_pre_channel_switch_state(mac, pe_session) != + QDF_STATUS_SUCCESS) { + pe_err("Could not restore pre-channelSwitch (11h) state, resetting the system"); + return; + } + + /* + * The channel switch request received from AP is carrying + * invalid channel. It's ok to ignore this channel switch + * request as it might be from spoof AP. If it's from genuine + * AP, it may lead to heart beat failure and result in + * disconnection. DUT can go ahead and reconnect to it/any + * other AP once it disconnects. + */ + pe_err("Invalid channel freq %u Ignore CSA request", + channel_freq); + return; + } + switch (pe_session->gLimChannelSwitch.state) { + case eLIM_CHANNEL_SWITCH_PRIMARY_ONLY: + lim_switch_primary_channel(mac, + pe_session->gLimChannelSwitch. + sw_target_freq, pe_session); + pe_session->gLimChannelSwitch.state = + eLIM_CHANNEL_SWITCH_IDLE; + break; + case eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY: + lim_switch_primary_secondary_channel(mac, pe_session, + pe_session->gLimChannelSwitch.sw_target_freq, + pe_session->gLimChannelSwitch.ch_center_freq_seg0, + pe_session->gLimChannelSwitch.ch_center_freq_seg1, + pe_session->gLimChannelSwitch.ch_width); + pe_session->gLimChannelSwitch.state = + eLIM_CHANNEL_SWITCH_IDLE; + break; + + case eLIM_CHANNEL_SWITCH_IDLE: + default: + pe_err("incorrect state"); + if (lim_restore_pre_channel_switch_state(mac, pe_session) != + QDF_STATUS_SUCCESS) { + pe_err("Could not restore pre-channelSwitch (11h) state, resetting the system"); + } + return; /* Please note, this is 'return' and not 'break' */ + } +} + +void lim_disconnect_complete(struct pe_session *session, bool del_bss) +{ + QDF_STATUS status; + struct mac_context *mac = session->mac_ctx; + + if (wlan_vdev_mlme_get_substate(session->vdev) == + WLAN_VDEV_SS_STOP_STOP_PROGRESS) { + status = wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_STOP_REQ, + sizeof(*session), + session); + return; + } + status = + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_DISCONNECT_COMPLETE, + sizeof(*session), session); + if (!mac->lim.gLimIbssCoalescingHappened && + QDF_IS_STATUS_ERROR(status)) { + lim_send_stop_bss_failure_resp(mac, session); + } +} + +void lim_process_channel_switch_timeout(struct mac_context *mac_ctx) +{ + struct pe_session *session_entry; + QDF_STATUS status; + + session_entry = pe_find_session_by_session_id( + mac_ctx, + mac_ctx->lim.lim_timers.gLimChannelSwitchTimer.sessionId); + if (!session_entry) { + pe_err("Session does not exist for given sessionID"); + return; + } + + session_entry->channelChangeReasonCode = LIM_SWITCH_CHANNEL_OPERATION; + mlme_set_chan_switch_in_progress(session_entry->vdev, true); + status = wlan_vdev_mlme_sm_deliver_evt( + session_entry->vdev, + WLAN_VDEV_SM_EV_FW_VDEV_RESTART, + sizeof(*session_entry), + session_entry); + if (QDF_IS_STATUS_ERROR(status)) + mlme_set_chan_switch_in_progress(session_entry->vdev, false); +} + +/** + * lim_update_channel_switch() - This Function updates channel switch + * @mac_ctx: pointer to Global MAC structure + * @beacon: pointer to tpSirProbeRespBeacon + * @psessionentry: pointer to struct pe_session * + * + * This function is invoked whenever Station receives + * either 802.11h channel switch IE or airgo proprietary + * channel switch IE. + * + * Return: none + */ +void +lim_update_channel_switch(struct mac_context *mac_ctx, + tpSirProbeRespBeacon beacon, + struct pe_session *psession_entry) +{ + uint16_t beacon_period; + tDot11fIEChanSwitchAnn *chnl_switch; + tLimChannelSwitchInfo *ch_switch_params; + tDot11fIEWiderBWChanSwitchAnn *widerchnl_switch; + + beacon_period = psession_entry->beaconParams.beaconInterval; + + /* 802.11h standard channel switch IE */ + chnl_switch = &(beacon->channelSwitchIE); + ch_switch_params = &psession_entry->gLimChannelSwitch; + ch_switch_params->primaryChannel = + chnl_switch->newChannel; + ch_switch_params->sw_target_freq = + wlan_reg_legacy_chan_to_freq(mac_ctx->pdev, + chnl_switch->newChannel); + ch_switch_params->switchCount = chnl_switch->switchCount; + ch_switch_params->switchTimeoutValue = + SYS_MS_TO_TICKS(beacon_period) * (chnl_switch->switchCount); + ch_switch_params->switchMode = chnl_switch->switchMode; + widerchnl_switch = &(beacon->WiderBWChanSwitchAnn); + if (beacon->WiderBWChanSwitchAnnPresent) { + psession_entry->gLimWiderBWChannelSwitch.newChanWidth = + widerchnl_switch->newChanWidth; + psession_entry->gLimWiderBWChannelSwitch.newCenterChanFreq0 = + widerchnl_switch->newCenterChanFreq0; + psession_entry->gLimWiderBWChannelSwitch.newCenterChanFreq1 = + widerchnl_switch->newCenterChanFreq1; + } + /* Only primary channel switch element is present */ + ch_switch_params->state = + eLIM_CHANNEL_SWITCH_PRIMARY_ONLY; + ch_switch_params->ch_width = CH_WIDTH_20MHZ; + + /* + * Do not bother to look and operate on extended channel switch element + * if our own channel-bonding state is not enabled + */ + if (psession_entry->htSupportedChannelWidthSet && + beacon->sec_chan_offset_present) { + if (beacon->sec_chan_offset.secondaryChannelOffset == + PHY_DOUBLE_CHANNEL_LOW_PRIMARY) { + ch_switch_params->state = + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY; + ch_switch_params->ch_width = CH_WIDTH_40MHZ; + ch_switch_params->ch_center_freq_seg0 = + ch_switch_params->primaryChannel + 2; + } else if (beacon->sec_chan_offset.secondaryChannelOffset == + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY) { + ch_switch_params->state = + eLIM_CHANNEL_SWITCH_PRIMARY_AND_SECONDARY; + ch_switch_params->ch_width = CH_WIDTH_40MHZ; + ch_switch_params->ch_center_freq_seg0 = + ch_switch_params->primaryChannel - 2; + } + if (psession_entry->vhtCapability && + beacon->WiderBWChanSwitchAnnPresent) { + ch_switch_params->ch_width = + widerchnl_switch->newChanWidth + 1; + ch_switch_params->ch_center_freq_seg0 = + psession_entry->gLimWiderBWChannelSwitch. + newCenterChanFreq0; + ch_switch_params->ch_center_freq_seg1 = + psession_entry->gLimWiderBWChannelSwitch. + newCenterChanFreq1; + } + } + if (QDF_STATUS_SUCCESS != lim_start_channel_switch(mac_ctx, psession_entry)) + pe_warn("Could not start Channel Switch"); + + pe_debug("session: %d primary chl: %d freq %d ch_width: %d count: %d (%d ticks)", + psession_entry->peSessionId, + psession_entry->gLimChannelSwitch.primaryChannel, + psession_entry->gLimChannelSwitch.sw_target_freq, + psession_entry->gLimChannelSwitch.ch_width, + psession_entry->gLimChannelSwitch.switchCount, + psession_entry->gLimChannelSwitch.switchTimeoutValue); + return; +} + +/** + * lim_cancel_dot11h_channel_switch + * + ***FUNCTION: + * This function is called when STA does not send updated channel-swith IE + * after indicating channel-switch start. This will cancel the channel-swith + * timer which is already running. + * + ***LOGIC: + * + ***ASSUMPTIONS: + * + ***NOTE: + * + * @param mac - Pointer to Global MAC structure + * + * @return None + */ +void lim_cancel_dot11h_channel_switch(struct mac_context *mac, + struct pe_session *pe_session) +{ + if (!LIM_IS_STA_ROLE(pe_session)) + return; + + pe_debug("Received a beacon without channel switch IE"); + + MTRACE(mac_trace + (mac, TRACE_CODE_TIMER_DEACTIVATE, + pe_session->peSessionId, eLIM_CHANNEL_SWITCH_TIMER)); + + if (tx_timer_deactivate(&mac->lim.lim_timers.gLimChannelSwitchTimer) != + QDF_STATUS_SUCCESS) { + pe_err("tx_timer_deactivate failed!"); + } + + /* We need to restore pre-channelSwitch state on the STA */ + if (lim_restore_pre_channel_switch_state(mac, pe_session) != + QDF_STATUS_SUCCESS) { + pe_err("LIM: Could not restore pre-channelSwitch (11h) state, resetting the system"); + } +} + +/** ------------------------------------------------------------------------ **/ +/** + * keep track of the number of ANI peers associated in the BSS + * For the first and last ANI peer, we have to update EDCA params as needed + * + * When the first ANI peer joins the BSS, we notify SCH + * When the last ANI peer leaves the BSS, we notfiy SCH + */ +void +lim_util_count_sta_add(struct mac_context *mac, + tpDphHashNode pSta, struct pe_session *pe_session) +{ + + if ((!pSta) || (!pSta->valid) || (pSta->fAniCount)) + return; + + pSta->fAniCount = 1; + + if (mac->lim.gLimNumOfAniSTAs++ != 0) + return; + + /* get here only if this is the first ANI peer in the BSS */ + sch_edca_profile_update(mac, pe_session); +} + +void +lim_util_count_sta_del(struct mac_context *mac, + tpDphHashNode pSta, struct pe_session *pe_session) +{ + + if ((!pSta) || (!pSta->fAniCount)) + return; + + /* Only if sta is invalid and the validInDummyState bit is set to 1, + * then go ahead and update the count and profiles. This ensures + * that the "number of ani station" count is properly incremented/decremented. + */ + if (pSta->valid == 1) + return; + + pSta->fAniCount = 0; + + if (mac->lim.gLimNumOfAniSTAs <= 0) { + pe_err("CountStaDel: ignoring Delete Req when AniPeer count: %d", + mac->lim.gLimNumOfAniSTAs); + return; + } + + mac->lim.gLimNumOfAniSTAs--; + + if (mac->lim.gLimNumOfAniSTAs != 0) + return; + + /* get here only if this is the last ANI peer in the BSS */ + sch_edca_profile_update(mac, pe_session); +} + +/** + * lim_switch_channel_vdev_started() - Send vdev started when switch channel + * + * @pe_session: PE session entry + * + * This function is called to deliver WLAN_VDEV_SM_EV_START_SUCCESS to VDEV SM + * + * Return: None + */ +static void lim_switch_channel_vdev_started(struct pe_session *pe_session) +{ + QDF_STATUS status; + + status = wlan_vdev_mlme_sm_deliver_evt( + pe_session->vdev, + WLAN_VDEV_SM_EV_START_SUCCESS, + sizeof(*pe_session), pe_session); +} + +/** + * lim_switch_channel_cback() + * + ***FUNCTION: + * This is the callback function registered while requesting to switch channel + * after AP indicates a channel switch for spectrum management (11h). + * + ***NOTE: + * @param mac Pointer to Global MAC structure + * @param status Status of channel switch request + * @param data User data + * @param pe_session Session information + * @return NONE + */ +void lim_switch_channel_cback(struct mac_context *mac, QDF_STATUS status, + uint32_t *data, struct pe_session *pe_session) +{ + struct scheduler_msg mmhMsg = { 0 }; + struct switch_channel_ind *pSirSmeSwitchChInd; + struct wlan_channel *des_chan; + struct vdev_mlme_obj *mlme_obj; + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(pe_session->vdev); + if (!mlme_obj) { + pe_err("vdev component object is NULL"); + return; + } + + des_chan = mlme_obj->vdev->vdev_mlme.des_chan; + if (!des_chan) { + pe_err("des_chan is NULL"); + return; + } + pe_session->curr_op_freq = pe_session->curr_req_chan_freq; + /* We need to restore pre-channelSwitch state on the STA */ + if (lim_restore_pre_channel_switch_state(mac, pe_session) != + QDF_STATUS_SUCCESS) { + pe_err("Could not restore pre-channelSwitch (11h) state, resetting the system"); + return; + } + + mmhMsg.type = eWNI_SME_SWITCH_CHL_IND; + pSirSmeSwitchChInd = qdf_mem_malloc(sizeof(*pSirSmeSwitchChInd)); + if (!pSirSmeSwitchChInd) + return; + + pSirSmeSwitchChInd->messageType = eWNI_SME_SWITCH_CHL_IND; + pSirSmeSwitchChInd->length = sizeof(*pSirSmeSwitchChInd); + pSirSmeSwitchChInd->freq = des_chan->ch_freq; + pSirSmeSwitchChInd->sessionId = pe_session->smeSessionId; + pSirSmeSwitchChInd->chan_params.ch_width = des_chan->ch_width; + if (des_chan->ch_width > CH_WIDTH_20MHZ) { + pSirSmeSwitchChInd->chan_params.sec_ch_offset = + pe_session->gLimChannelSwitch.sec_ch_offset; + pSirSmeSwitchChInd->chan_params.center_freq_seg0 = + des_chan->ch_freq_seg1; + pSirSmeSwitchChInd->chan_params.mhz_freq_seg0 = + des_chan->ch_cfreq1; + pSirSmeSwitchChInd->chan_params.center_freq_seg1 = + des_chan->ch_freq_seg2; + pSirSmeSwitchChInd->chan_params.mhz_freq_seg1 = + des_chan->ch_cfreq2; + } + + pSirSmeSwitchChInd->status = status; + qdf_mem_copy(pSirSmeSwitchChInd->bssid.bytes, pe_session->bssId, + QDF_MAC_ADDR_SIZE); + mmhMsg.bodyptr = pSirSmeSwitchChInd; + mmhMsg.bodyval = 0; + + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, mmhMsg.type)); + + sys_process_mmh_msg(mac, &mmhMsg); + + if (QDF_IS_STATUS_SUCCESS(status)) + lim_switch_channel_vdev_started(pe_session); +} + +void lim_switch_primary_channel(struct mac_context *mac, + uint32_t new_channel_freq, + struct pe_session *pe_session) +{ + pe_debug("freq: %d --> freq: %d", pe_session->curr_op_freq, + new_channel_freq); + + pe_session->curr_req_chan_freq = new_channel_freq; + pe_session->curr_op_freq = pe_session->curr_req_chan_freq; + pe_session->ch_center_freq_seg0 = 0; + pe_session->ch_center_freq_seg1 = 0; + pe_session->ch_width = CH_WIDTH_20MHZ; + pe_session->limRFBand = lim_get_rf_band(pe_session->curr_req_chan_freq); + + pe_session->channelChangeReasonCode = LIM_SWITCH_CHANNEL_OPERATION; + + mac->lim.gpchangeChannelCallback = lim_switch_channel_cback; + mac->lim.gpchangeChannelData = NULL; + + lim_send_switch_chnl_params(mac, pe_session); + return; +} + +void lim_switch_primary_secondary_channel(struct mac_context *mac, + struct pe_session *pe_session, + uint32_t new_channel_freq, + uint8_t ch_center_freq_seg0, + uint8_t ch_center_freq_seg1, + enum phy_ch_width ch_width) +{ + + /* Assign the callback to resume TX once channel is changed. */ + pe_session->curr_req_chan_freq = new_channel_freq; + pe_session->limRFBand = lim_get_rf_band(pe_session->curr_req_chan_freq); + pe_session->channelChangeReasonCode = LIM_SWITCH_CHANNEL_OPERATION; + mac->lim.gpchangeChannelCallback = lim_switch_channel_cback; + mac->lim.gpchangeChannelData = NULL; + + /* Store the new primary and secondary channel in session entries if different */ + if (pe_session->curr_op_freq != new_channel_freq) { + pe_warn("freq: %d --> freq: %d", pe_session->curr_op_freq, + new_channel_freq); + pe_session->curr_op_freq = new_channel_freq; + } + if (pe_session->htSecondaryChannelOffset != + pe_session->gLimChannelSwitch.sec_ch_offset) { + pe_warn("HT sec chnl: %d --> HT sec chnl: %d", + pe_session->htSecondaryChannelOffset, + pe_session->gLimChannelSwitch.sec_ch_offset); + pe_session->htSecondaryChannelOffset = + pe_session->gLimChannelSwitch.sec_ch_offset; + if (pe_session->htSecondaryChannelOffset == + PHY_SINGLE_CHANNEL_CENTERED) { + pe_session->htSupportedChannelWidthSet = + WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + } else { + pe_session->htSupportedChannelWidthSet = + WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; + } + pe_session->htRecommendedTxWidthSet = + pe_session->htSupportedChannelWidthSet; + } + + pe_session->ch_center_freq_seg0 = ch_center_freq_seg0; + pe_session->ch_center_freq_seg1 = ch_center_freq_seg1; + pe_session->ch_width = ch_width; + + lim_send_switch_chnl_params(mac, pe_session); + + return; +} + +/** + * lim_get_ht_capability() + * + ***FUNCTION: + * A utility function that returns the "current HT capability state" for the HT + * capability of interest (as requested in the API) + * + ***LOGIC: + * This routine will return with the "current" setting of a requested HT + * capability. This state info could be retrieved from - + * a) CFG (for static entries) + * b) Run time info + * - Dynamic state maintained by LIM + * - Configured at radio init time by SME + * + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * + * @param mac Pointer to Global MAC structure + * @param htCap The HT capability being queried + * @return uint8_t The current state of the requested HT capability is returned in a + * uint8_t variable + */ + +uint8_t lim_get_ht_capability(struct mac_context *mac, + uint32_t htCap, struct pe_session *pe_session) +{ + uint8_t retVal = 0; + uint8_t *ptr; + tSirMacTxBFCapabilityInfo macTxBFCapabilityInfo = { 0 }; + tSirMacASCapabilityInfo macASCapabilityInfo = { 0 }; + struct mlme_vht_capabilities_info *vht_cap_info; + + vht_cap_info = &mac->mlme_cfg->vht_caps.vht_cap_info; + + /* */ + /* Determine which CFG to read from. Not ALL of the HT */ + /* related CFG's need to be read each time this API is */ + /* accessed */ + /* */ + if (htCap >= eHT_ANTENNA_SELECTION && htCap < eHT_SI_GRANULARITY) { + /* Get Antenna Seletion HT Capabilities */ + ptr = (uint8_t *) &macASCapabilityInfo; + *((uint8_t *)ptr) = (uint8_t)(vht_cap_info->as_cap & 0xff); + } else if (htCap >= eHT_TX_BEAMFORMING && + htCap < eHT_ANTENNA_SELECTION) { + /* Get Transmit Beam Forming HT Capabilities */ + ptr = (uint8_t *)&macTxBFCapabilityInfo; + *((uint32_t *)ptr) = (uint32_t)(vht_cap_info->tx_bf_cap); + } + + switch (htCap) { + case eHT_LSIG_TXOP_PROTECTION: + retVal = mac->lim.gHTLsigTXOPProtection; + break; + + case eHT_STBC_CONTROL_FRAME: + retVal = (uint8_t)mac->mlme_cfg->ht_caps.ht_cap_info. + stbc_control_frame; + break; + + case eHT_PSMP: + retVal = mac->lim.gHTPSMPSupport; + break; + + case eHT_DSSS_CCK_MODE_40MHZ: + retVal = mac->lim.gHTDsssCckRate40MHzSupport; + break; + + case eHT_MAX_AMSDU_LENGTH: + retVal = (uint8_t)mac->mlme_cfg->ht_caps.ht_cap_info. + maximal_amsdu_size; + break; + + case eHT_MAX_AMSDU_NUM: + retVal = (uint8_t) pe_session->max_amsdu_num; + break; + + case eHT_RX_STBC: + retVal = (uint8_t) pe_session->ht_config.ht_rx_stbc; + break; + + case eHT_TX_STBC: + retVal = (uint8_t) pe_session->ht_config.ht_tx_stbc; + break; + + case eHT_SHORT_GI_40MHZ: + retVal = (uint8_t)(pe_session->ht_config.ht_sgi40) ? + mac->mlme_cfg->ht_caps.ht_cap_info.short_gi_40_mhz : 0; + break; + + case eHT_SHORT_GI_20MHZ: + retVal = (uint8_t)(pe_session->ht_config.ht_sgi20) ? + mac->mlme_cfg->ht_caps.ht_cap_info.short_gi_20_mhz : 0; + break; + + case eHT_GREENFIELD: + retVal = (uint8_t)mac->mlme_cfg->ht_caps.ht_cap_info. + green_field; + break; + + case eHT_MIMO_POWER_SAVE: + retVal = (uint8_t) mac->lim.gHTMIMOPSState; + break; + + case eHT_SUPPORTED_CHANNEL_WIDTH_SET: + retVal = (uint8_t) pe_session->htSupportedChannelWidthSet; + break; + + case eHT_ADVANCED_CODING: + retVal = (uint8_t) pe_session->ht_config.ht_rx_ldpc; + break; + + case eHT_MAX_RX_AMPDU_FACTOR: + retVal = mac->lim.gHTMaxRxAMpduFactor; + break; + + case eHT_MPDU_DENSITY: + retVal = mac->lim.gHTAMpduDensity; + break; + + case eHT_PCO: + retVal = (uint8_t)mac->mlme_cfg->ht_caps.ext_cap_info.pco; + break; + + case eHT_TRANSITION_TIME: + retVal = (uint8_t)mac->mlme_cfg->ht_caps.ext_cap_info. + transition_time; + break; + + case eHT_MCS_FEEDBACK: + retVal = (uint8_t)mac->mlme_cfg->ht_caps.ext_cap_info. + mcs_feedback; + break; + + case eHT_TX_BEAMFORMING: + retVal = (uint8_t) macTxBFCapabilityInfo.txBF; + break; + + case eHT_ANTENNA_SELECTION: + retVal = (uint8_t) macASCapabilityInfo.antennaSelection; + break; + + case eHT_SI_GRANULARITY: + retVal = mac->lim.gHTServiceIntervalGranularity; + break; + + case eHT_CONTROLLED_ACCESS: + retVal = mac->lim.gHTControlledAccessOnly; + break; + + case eHT_RIFS_MODE: + retVal = pe_session->beaconParams.fRIFSMode; + break; + + case eHT_RECOMMENDED_TX_WIDTH_SET: + retVal = pe_session->htRecommendedTxWidthSet; + break; + + case eHT_EXTENSION_CHANNEL_OFFSET: + retVal = pe_session->htSecondaryChannelOffset; + break; + + case eHT_OP_MODE: + if (LIM_IS_AP_ROLE(pe_session)) + retVal = pe_session->htOperMode; + else + retVal = mac->lim.gHTOperMode; + break; + + case eHT_BASIC_STBC_MCS: + retVal = mac->lim.gHTSTBCBasicMCS; + break; + + case eHT_DUAL_CTS_PROTECTION: + retVal = mac->lim.gHTDualCTSProtection; + break; + + case eHT_LSIG_TXOP_PROTECTION_FULL_SUPPORT: + retVal = + pe_session->beaconParams.fLsigTXOPProtectionFullSupport; + break; + + case eHT_PCO_ACTIVE: + retVal = mac->lim.gHTPCOActive; + break; + + case eHT_PCO_PHASE: + retVal = mac->lim.gHTPCOPhase; + break; + + default: + break; + } + + return retVal; +} + +/** + * lim_enable_11a_protection() - updates protection params for enable 11a + * protection request + * @mac_ctx: pointer to Global MAC structure + * @overlap: 1=> called from overlap context, 0 => called from assoc context. + * @bcn_prms: beacon parameters + * @pe_session: pe session entry + * + * This function updates protection params for enable 11a protection request + * + * @Return: void + */ +static void +lim_enable_11a_protection(struct mac_context *mac_ctx, + uint8_t overlap, + tpUpdateBeaconParams bcn_prms, + struct pe_session *pe_session) +{ + /* + * If we are AP and HT capable, we need to set the HT OP mode + * appropriately. + */ + if (LIM_IS_AP_ROLE(pe_session) && (true == pe_session->htCapability)) { + if (overlap) { + pe_session->gLimOverlap11aParams.protectionEnabled = + true; + if ((eSIR_HT_OP_MODE_OVERLAP_LEGACY != + mac_ctx->lim.gHTOperMode) + && (eSIR_HT_OP_MODE_MIXED != + mac_ctx->lim.gHTOperMode)) { + mac_ctx->lim.gHTOperMode = + eSIR_HT_OP_MODE_OVERLAP_LEGACY; + pe_session->htOperMode = + eSIR_HT_OP_MODE_OVERLAP_LEGACY; + lim_enable_ht_rifs_protection(mac_ctx, true, + overlap, bcn_prms, pe_session); + lim_enable_ht_obss_protection(mac_ctx, true, + overlap, bcn_prms, pe_session); + } + } else { + pe_session->gLim11aParams.protectionEnabled = true; + if (eSIR_HT_OP_MODE_MIXED != pe_session->htOperMode) { + mac_ctx->lim.gHTOperMode = + eSIR_HT_OP_MODE_MIXED; + pe_session->htOperMode = eSIR_HT_OP_MODE_MIXED; + lim_enable_ht_rifs_protection(mac_ctx, true, + overlap, bcn_prms, pe_session); + lim_enable_ht_obss_protection(mac_ctx, true, + overlap, bcn_prms, pe_session); + } + } + } + /* This part is common for station as well. */ + if (false == pe_session->beaconParams.llaCoexist) { + pe_debug(" => protection from 11A Enabled"); + bcn_prms->llaCoexist = true; + pe_session->beaconParams.llaCoexist = true; + bcn_prms->paramChangeBitmap |= PARAM_llACOEXIST_CHANGED; + } +} + +/** + * lim_disable_11a_protection() - updates protection params for disable 11a + * protection request + * @mac_ctx: pointer to Global MAC structure + * @overlap: 1=> called from overlap context, 0 => called from assoc context. + * @bcn_prms: beacon parameters + * @pe_session: pe session entry + * + * This function updates protection params for disable 11a protection request + * + * @Return: void + */ +static void +lim_disable_11a_protection(struct mac_context *mac_ctx, + uint8_t overlap, + tpUpdateBeaconParams bcn_prms, + struct pe_session *pe_session) +{ + if (false == pe_session->beaconParams.llaCoexist) + return; + + /* for station role */ + if (!LIM_IS_AP_ROLE(pe_session)) { + pe_debug("===> Protection from 11A Disabled"); + bcn_prms->llaCoexist = false; + pe_session->beaconParams.llaCoexist = false; + bcn_prms->paramChangeBitmap |= PARAM_llACOEXIST_CHANGED; + return; + } + /* + * for AP role. + * we need to take care of HT OP mode change if needed. + * We need to take care of Overlap cases. + */ + if (overlap) { + /* Overlap Legacy protection disabled. */ + pe_session->gLimOverlap11aParams.protectionEnabled = false; + + /* + * We need to take care of HT OP mode iff we are HT AP. + * OR no HT op-mode change is needed if any of the overlap + * protection enabled. + */ + if (!pe_session->htCapability || + (pe_session->gLimOverlap11aParams.protectionEnabled + || pe_session->gLimOverlapHt20Params.protectionEnabled + || pe_session->gLimOverlapNonGfParams.protectionEnabled)) + goto disable_11a_end; + + /* Check if there is a need to change HT OP mode. */ + if (eSIR_HT_OP_MODE_OVERLAP_LEGACY == + mac_ctx->lim.gHTOperMode) { + lim_enable_ht_rifs_protection(mac_ctx, false, overlap, + bcn_prms, pe_session); + lim_enable_ht_obss_protection(mac_ctx, false, overlap, + bcn_prms, pe_session); + + if (pe_session->gLimHt20Params.protectionEnabled) + mac_ctx->lim.gHTOperMode = + eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT; + else + mac_ctx->lim.gHTOperMode = eSIR_HT_OP_MODE_PURE; + } + } else { + /* Disable protection from 11A stations. */ + pe_session->gLim11aParams.protectionEnabled = false; + lim_enable_ht_obss_protection(mac_ctx, false, overlap, + bcn_prms, pe_session); + + /* + * Check if any other non-HT protection enabled. Right now we + * are in HT OP Mixed mode. Change HT op mode appropriately. + */ + + /* Change HT OP mode to 01 if any overlap protection enabled */ + if (pe_session->gLimOverlap11aParams.protectionEnabled + || pe_session->gLimOverlapHt20Params.protectionEnabled + || pe_session->gLimOverlapNonGfParams.protectionEnabled) { + mac_ctx->lim.gHTOperMode = + eSIR_HT_OP_MODE_OVERLAP_LEGACY; + pe_session->htOperMode = eSIR_HT_OP_MODE_OVERLAP_LEGACY; + lim_enable_ht_rifs_protection(mac_ctx, true, overlap, + bcn_prms, pe_session); + } else if (pe_session->gLimHt20Params.protectionEnabled) { + mac_ctx->lim.gHTOperMode = + eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT; + pe_session->htOperMode = + eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT; + lim_enable_ht_rifs_protection(mac_ctx, false, overlap, + bcn_prms, pe_session); + } else { + mac_ctx->lim.gHTOperMode = eSIR_HT_OP_MODE_PURE; + pe_session->htOperMode = eSIR_HT_OP_MODE_PURE; + lim_enable_ht_rifs_protection(mac_ctx, false, overlap, + bcn_prms, pe_session); + } + } + +disable_11a_end: + if (!pe_session->gLimOverlap11aParams.protectionEnabled && + !pe_session->gLim11aParams.protectionEnabled) { + pe_warn("===> Protection from 11A Disabled"); + bcn_prms->llaCoexist = false; + pe_session->beaconParams.llaCoexist = false; + bcn_prms->paramChangeBitmap |= PARAM_llACOEXIST_CHANGED; + } +} + +/** + * lim_update_11a_protection() - based on config setting enables\disables 11a + * protection. + * @mac_ctx: pointer to Global MAC structure + * @enable: 1=> enable protection, 0=> disable protection. + * @overlap: 1=> called from overlap context, 0 => called from assoc context. + * @bcn_prms: beacon parameters + * @session: pe session entry + * + * This based on config setting enables\disables 11a protection. + * + * @Return: success of failure of operation + */ +QDF_STATUS +lim_update_11a_protection(struct mac_context *mac_ctx, uint8_t enable, + uint8_t overlap, tpUpdateBeaconParams bcn_prms, + struct pe_session *session) +{ + if (!session) { + pe_err("session is NULL"); + return QDF_STATUS_E_FAILURE; + } + /* overlapping protection configuration check. */ + if (!overlap) { + /* normal protection config check */ + if ((LIM_IS_AP_ROLE(session)) && + (!session->cfgProtection.fromlla)) { + /* protection disabled. */ + pe_warn("protection from 11a is disabled"); + return QDF_STATUS_SUCCESS; + } + } + + if (enable) + lim_enable_11a_protection(mac_ctx, overlap, bcn_prms, session); + else + lim_disable_11a_protection(mac_ctx, overlap, bcn_prms, session); + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_handle_enable11g_protection_enabled() - handle 11g protection enabled + * @mac_ctx: pointer to Globale Mac structure + * @beaconparams: pointer to tpUpdateBeaconParams + * @overlap: 1=> called from overlap context, 0 => called from assoc context. + * @session_entry: pointer to struct pe_session * + * + * Function handles 11g protection enaled case + * + * Return: none + */ +static void +lim_handle_enable11g_protection_enabled(struct mac_context *mac_ctx, + tpUpdateBeaconParams beaconparams, + uint8_t overlap, struct pe_session *session_entry) +{ + /* + * If we are AP and HT capable, we need to set the HT OP mode + * appropriately. + */ + if (LIM_IS_AP_ROLE(session_entry) && overlap) { + session_entry->gLimOlbcParams.protectionEnabled = true; + + pe_debug("protection from olbc is enabled"); + + if (true == session_entry->htCapability) { + if ((eSIR_HT_OP_MODE_OVERLAP_LEGACY != + session_entry->htOperMode) && + (eSIR_HT_OP_MODE_MIXED != + session_entry->htOperMode)) { + session_entry->htOperMode = + eSIR_HT_OP_MODE_OVERLAP_LEGACY; + } + /* + * CR-263021: OBSS bit is not switching back to 0 after + * disabling the overlapping legacy BSS + */ + /* + * This fixes issue of OBSS bit not set after 11b, 11g + * station leaves + */ + lim_enable_ht_rifs_protection(mac_ctx, true, + overlap, beaconparams, session_entry); + /* + * Not processing OBSS bit from other APs, as we are + * already taking care of Protection from overlapping + * BSS based on erp IE or useProtection bit + */ + lim_enable_ht_obss_protection(mac_ctx, true, + overlap, beaconparams, session_entry); + } + } else if (LIM_IS_AP_ROLE(session_entry) && !overlap) { + session_entry->gLim11bParams.protectionEnabled = true; + pe_debug("protection from 11b is enabled"); + if (true == session_entry->htCapability) { + if (eSIR_HT_OP_MODE_MIXED != + session_entry->htOperMode) { + session_entry->htOperMode = + eSIR_HT_OP_MODE_MIXED; + lim_enable_ht_rifs_protection(mac_ctx, + true, overlap, beaconparams, + session_entry); + lim_enable_ht_obss_protection(mac_ctx, + true, overlap, beaconparams, + session_entry); + } + } + } + + /* This part is common for staiton as well. */ + if (false == session_entry->beaconParams.llbCoexist) { + pe_debug("=> 11G Protection Enabled"); + beaconparams->llbCoexist = + session_entry->beaconParams.llbCoexist = true; + beaconparams->paramChangeBitmap |= + PARAM_llBCOEXIST_CHANGED; + } +} + +/** + * lim_handle_11g_protection_for_11bcoexist() - 11g protection for 11b co-ex + * @mac_ctx: pointer to Globale Mac structure + * @beaconparams: pointer to tpUpdateBeaconParams + * @overlap: 1=> called from overlap context, 0 => called from assoc context. + * @session_entry: pointer to struct pe_session * + * + * Function handles 11g protection for 11b co-exist + * + * Return: none + */ +static void +lim_handle_11g_protection_for_11bcoexist(struct mac_context *mac_ctx, + tpUpdateBeaconParams beaconparams, + uint8_t overlap, struct pe_session *session_entry) +{ + /* + * For AP role: + * we need to take care of HT OP mode change if needed. + * We need to take care of Overlap cases. + */ + if (LIM_IS_AP_ROLE(session_entry) && overlap) { + /* Overlap Legacy protection disabled. */ + session_entry->gLimOlbcParams.protectionEnabled = false; + + /* We need to take care of HT OP mode if we are HT AP. */ + if (session_entry->htCapability) { + /* + * no HT op mode change if any of the overlap + * protection enabled. + */ + if (!(session_entry->gLimOverlap11gParams. + protectionEnabled || + session_entry->gLimOverlapHt20Params. + protectionEnabled || + session_entry->gLimOverlapNonGfParams. + protectionEnabled) && + /* + * Check if there is a need to change HT + * OP mode. + */ + (eSIR_HT_OP_MODE_OVERLAP_LEGACY == + session_entry->htOperMode)) { + lim_enable_ht_rifs_protection(mac_ctx, false, + overlap, beaconparams, session_entry); + lim_enable_ht_obss_protection(mac_ctx, false, + overlap, beaconparams, session_entry); + if (session_entry->gLimHt20Params. + protectionEnabled) { + if (eHT_CHANNEL_WIDTH_20MHZ == + session_entry->htSupportedChannelWidthSet) + session_entry->htOperMode = + eSIR_HT_OP_MODE_PURE; + else + session_entry->htOperMode = + eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT; + } else + session_entry->htOperMode = + eSIR_HT_OP_MODE_PURE; + } + } + } else if (LIM_IS_AP_ROLE(session_entry) && !overlap) { + /* Disable protection from 11B stations. */ + session_entry->gLim11bParams.protectionEnabled = false; + pe_debug("===> 11B Protection Disabled"); + /* Check if any other non-HT protection enabled. */ + if (!session_entry->gLim11gParams.protectionEnabled) { + /* Right now we are in HT OP Mixed mode. */ + /* Change HT op mode appropriately. */ + lim_enable_ht_obss_protection(mac_ctx, false, overlap, + beaconparams, session_entry); + /* + * Change HT OP mode to 01 if any overlap protection + * enabled + */ + if (session_entry->gLimOlbcParams.protectionEnabled || + session_entry->gLimOverlap11gParams. + protectionEnabled || + session_entry->gLimOverlapHt20Params. + protectionEnabled || + session_entry->gLimOverlapNonGfParams. + protectionEnabled) { + session_entry->htOperMode = + eSIR_HT_OP_MODE_OVERLAP_LEGACY; + pe_debug("===> 11G Protection Disabled"); + lim_enable_ht_rifs_protection(mac_ctx, true, + overlap, beaconparams, + session_entry); + } else if (session_entry->gLimHt20Params. + protectionEnabled) { + /* Commenting because of CR 258588 WFA cert */ + /* session_entry->htOperMode = + eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT; */ + session_entry->htOperMode = + eSIR_HT_OP_MODE_PURE; + pe_debug("===> 11G Protection Disabled"); + lim_enable_ht_rifs_protection(mac_ctx, false, + overlap, beaconparams, + session_entry); + } else { + session_entry->htOperMode = + eSIR_HT_OP_MODE_PURE; + lim_enable_ht_rifs_protection(mac_ctx, false, + overlap, beaconparams, + session_entry); + } + } + } + if (LIM_IS_AP_ROLE(session_entry)) { + if (!session_entry->gLimOlbcParams.protectionEnabled && + !session_entry->gLim11bParams.protectionEnabled) { + pe_debug("===> 11G Protection Disabled"); + beaconparams->llbCoexist = + session_entry->beaconParams.llbCoexist = + false; + beaconparams->paramChangeBitmap |= + PARAM_llBCOEXIST_CHANGED; + } + } + /* For station role */ + if (!LIM_IS_AP_ROLE(session_entry)) { + pe_debug("===> 11G Protection Disabled"); + beaconparams->llbCoexist = + session_entry->beaconParams.llbCoexist = false; + beaconparams->paramChangeBitmap |= + PARAM_llBCOEXIST_CHANGED; + } +} + +/** + * lim_enable11g_protection() - Function to enable 11g protection + * @mac_ctx: pointer to Global Mac structure + * @enable: 1=> enable protection, 0=> disable protection. + * @overlap: 1=> called from overlap context, 0 => called from assoc context. + * @beaconparams: pointer to tpUpdateBeaconParams + * @session_entry: pointer to struct pe_session * + * + * based on config setting enables\disables 11g protection. + * + * Return: Success - QDF_STATUS_SUCCESS - Success, Error number - Failure + */ +QDF_STATUS +lim_enable11g_protection(struct mac_context *mac_ctx, uint8_t enable, + uint8_t overlap, tpUpdateBeaconParams beaconparams, + struct pe_session *session_entry) +{ + + /* overlapping protection configuration check. */ + if (!overlap) { + /* normal protection config check */ + if ((LIM_IS_AP_ROLE(session_entry)) && + !session_entry->cfgProtection.fromllb) { + /* protection disabled. */ + pe_debug("protection from 11b is disabled"); + return QDF_STATUS_SUCCESS; + } else if (!LIM_IS_AP_ROLE(session_entry)) { + if (!mac_ctx->lim.cfgProtection.fromllb) { + /* protection disabled. */ + pe_debug("protection from 11b is disabled"); + return QDF_STATUS_SUCCESS; + } + } + } + + if (enable) { + lim_handle_enable11g_protection_enabled(mac_ctx, beaconparams, + overlap, session_entry); + } else if (true == session_entry->beaconParams.llbCoexist) { + lim_handle_11g_protection_for_11bcoexist(mac_ctx, beaconparams, + overlap, session_entry); + } + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn lim_enable_ht_protection_from11g + \brief based on cofig enables\disables protection from 11g. + \param uint8_t enable : 1=> enable protection, 0=> disable protection. + \param uint8_t overlap: 1=> called from overlap context, 0 => called from assoc context. + \param tpUpdateBeaconParams pBeaconParams + \return None + -------------------------------------------------------------*/ +QDF_STATUS +lim_enable_ht_protection_from11g(struct mac_context *mac, uint8_t enable, + uint8_t overlap, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + if (!pe_session->htCapability) + return QDF_STATUS_SUCCESS; /* protection from 11g is only for HT stations. */ + + /* overlapping protection configuration check. */ + if (overlap) { + if ((LIM_IS_AP_ROLE(pe_session)) + && (!pe_session->cfgProtection.overlapFromllg)) { + /* protection disabled. */ + pe_debug("overlap protection from 11g is disabled"); + return QDF_STATUS_SUCCESS; + } + } else { + /* normal protection config check */ + if (LIM_IS_AP_ROLE(pe_session) && + !pe_session->cfgProtection.fromllg) { + /* protection disabled. */ + pe_debug("protection from 11g is disabled"); + return QDF_STATUS_SUCCESS; + } else if (!LIM_IS_AP_ROLE(pe_session)) { + if (!mac->lim.cfgProtection.fromllg) { + /* protection disabled. */ + pe_debug("protection from 11g is disabled"); + return QDF_STATUS_SUCCESS; + } + } + } + if (enable) { + /* If we are AP and HT capable, we need to set the HT OP mode */ + /* appropriately. */ + + if (LIM_IS_AP_ROLE(pe_session)) { + if (overlap) { + pe_session->gLimOverlap11gParams. + protectionEnabled = true; + /* 11g exists in overlap BSS. */ + /* need not to change the operating mode to overlap_legacy */ + /* if higher or same protection operating mode is enabled right now. */ + if ((eSIR_HT_OP_MODE_OVERLAP_LEGACY != + pe_session->htOperMode) + && (eSIR_HT_OP_MODE_MIXED != + pe_session->htOperMode)) { + pe_session->htOperMode = + eSIR_HT_OP_MODE_OVERLAP_LEGACY; + } + lim_enable_ht_rifs_protection(mac, true, overlap, + pBeaconParams, + pe_session); + lim_enable_ht_obss_protection(mac, true, overlap, + pBeaconParams, + pe_session); + } else { + /* 11g is associated to an AP operating in 11n mode. */ + /* Change the HT operating mode to 'mixed mode'. */ + pe_session->gLim11gParams.protectionEnabled = + true; + if (eSIR_HT_OP_MODE_MIXED != + pe_session->htOperMode) { + pe_session->htOperMode = + eSIR_HT_OP_MODE_MIXED; + lim_enable_ht_rifs_protection(mac, true, + overlap, + pBeaconParams, + pe_session); + lim_enable_ht_obss_protection(mac, true, + overlap, + pBeaconParams, + pe_session); + } + } + } + /* This part is common for staiton as well. */ + if (false == pe_session->beaconParams.llgCoexist) { + pBeaconParams->llgCoexist = + pe_session->beaconParams.llgCoexist = true; + pBeaconParams->paramChangeBitmap |= + PARAM_llGCOEXIST_CHANGED; + } else if (true == + pe_session->gLimOverlap11gParams. + protectionEnabled) { + /* As operating mode changed after G station assoc some way to update beacon */ + /* This addresses the issue of mode not changing to - 11 in beacon when OBSS overlap is enabled */ + /* mac->sch.beacon_changed = 1; */ + pBeaconParams->paramChangeBitmap |= + PARAM_llGCOEXIST_CHANGED; + } + } else if (true == pe_session->beaconParams.llgCoexist) { + /* for AP role. */ + /* we need to take care of HT OP mode change if needed. */ + /* We need to take care of Overlap cases. */ + + if (LIM_IS_AP_ROLE(pe_session)) { + if (overlap) { + /* Overlap Legacy protection disabled. */ + if (pe_session->gLim11gParams.numSta == 0) + pe_session->gLimOverlap11gParams. + protectionEnabled = false; + + /* no HT op mode change if any of the overlap protection enabled. */ + if (! + (pe_session->gLimOlbcParams. + protectionEnabled + || pe_session->gLimOverlapHt20Params. + protectionEnabled + || pe_session->gLimOverlapNonGfParams. + protectionEnabled)) { + /* Check if there is a need to change HT OP mode. */ + if (eSIR_HT_OP_MODE_OVERLAP_LEGACY == + pe_session->htOperMode) { + lim_enable_ht_rifs_protection(mac, + false, + overlap, + pBeaconParams, + pe_session); + lim_enable_ht_obss_protection(mac, + false, + overlap, + pBeaconParams, + pe_session); + + if (pe_session->gLimHt20Params.protectionEnabled) { + if (eHT_CHANNEL_WIDTH_20MHZ == + pe_session->htSupportedChannelWidthSet) + pe_session->htOperMode = + eSIR_HT_OP_MODE_PURE; + else + pe_session->htOperMode = + eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT; + } else + pe_session->htOperMode = + eSIR_HT_OP_MODE_PURE; + } + } + } else { + /* Disable protection from 11G stations. */ + pe_session->gLim11gParams.protectionEnabled = + false; + /* Check if any other non-HT protection enabled. */ + if (!pe_session->gLim11bParams. + protectionEnabled) { + + /* Right now we are in HT OP Mixed mode. */ + /* Change HT op mode appropriately. */ + lim_enable_ht_obss_protection(mac, false, + overlap, + pBeaconParams, + pe_session); + + /* Change HT OP mode to 01 if any overlap protection enabled */ + if (pe_session->gLimOlbcParams. + protectionEnabled + || pe_session-> + gLimOverlap11gParams. + protectionEnabled + || pe_session-> + gLimOverlapHt20Params. + protectionEnabled + || pe_session-> + gLimOverlapNonGfParams. + protectionEnabled) { + pe_session->htOperMode = + eSIR_HT_OP_MODE_OVERLAP_LEGACY; + lim_enable_ht_rifs_protection(mac, + true, + overlap, + pBeaconParams, + pe_session); + } else if (pe_session-> + gLimHt20Params. + protectionEnabled) { + /* Commenting because of CR 258588 WFA cert */ + /* pe_session->htOperMode = eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT; */ + pe_session->htOperMode = + eSIR_HT_OP_MODE_PURE; + lim_enable_ht_rifs_protection(mac, + false, + overlap, + pBeaconParams, + pe_session); + } else { + pe_session->htOperMode = + eSIR_HT_OP_MODE_PURE; + lim_enable_ht_rifs_protection(mac, + false, + overlap, + pBeaconParams, + pe_session); + } + } + } + if (!pe_session->gLimOverlap11gParams. + protectionEnabled + && !pe_session->gLim11gParams. + protectionEnabled) { + pe_debug("===> Protection from 11G Disabled"); + pBeaconParams->llgCoexist = + pe_session->beaconParams.llgCoexist = + false; + pBeaconParams->paramChangeBitmap |= + PARAM_llGCOEXIST_CHANGED; + } + } + /* for station role */ + else { + pe_debug("===> Protection from 11G Disabled"); + pBeaconParams->llgCoexist = + pe_session->beaconParams.llgCoexist = false; + pBeaconParams->paramChangeBitmap |= + PARAM_llGCOEXIST_CHANGED; + } + } + return QDF_STATUS_SUCCESS; +} + +/* FIXME_PROTECTION : need to check for no APSD whenever we want to enable this protection. */ +/* This check will be done at the caller. */ + +/** ------------------------------------------------------------- + \fn limEnableHtObssProtection + \brief based on cofig enables\disables obss protection. + \param uint8_t enable : 1=> enable protection, 0=> disable protection. + \param uint8_t overlap: 1=> called from overlap context, 0 => called from assoc context. + \param tpUpdateBeaconParams pBeaconParams + \return None + -------------------------------------------------------------*/ +QDF_STATUS +lim_enable_ht_obss_protection(struct mac_context *mac, uint8_t enable, + uint8_t overlap, tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + + if (!pe_session->htCapability) + return QDF_STATUS_SUCCESS; /* this protection is only for HT stations. */ + + /* overlapping protection configuration check. */ + if (overlap) { + /* overlapping protection configuration check. */ + } else { + /* normal protection config check */ + if ((LIM_IS_AP_ROLE(pe_session)) && + !pe_session->cfgProtection.obss) { /* ToDo Update this field */ + /* protection disabled. */ + pe_debug("protection from Obss is disabled"); + return QDF_STATUS_SUCCESS; + } else if (!LIM_IS_AP_ROLE(pe_session)) { + if (!mac->lim.cfgProtection.obss) { /* ToDo Update this field */ + /* protection disabled. */ + pe_debug("protection from Obss is disabled"); + return QDF_STATUS_SUCCESS; + } + } + } + + if (LIM_IS_AP_ROLE(pe_session)) { + if ((enable) + && (false == pe_session->beaconParams.gHTObssMode)) { + pe_debug("=>obss protection enabled"); + pe_session->beaconParams.gHTObssMode = true; + pBeaconParams->paramChangeBitmap |= PARAM_OBSS_MODE_CHANGED; /* UPDATE AN ENUM FOR OBSS MODE */ + + } else if (!enable + && (true == + pe_session->beaconParams.gHTObssMode)) { + pe_debug("===> obss Protection disabled"); + pe_session->beaconParams.gHTObssMode = false; + pBeaconParams->paramChangeBitmap |= + PARAM_OBSS_MODE_CHANGED; + + } +/* CR-263021: OBSS bit is not switching back to 0 after disabling the overlapping legacy BSS */ + if (!enable && !overlap) { + pe_session->gLimOverlap11gParams.protectionEnabled = + false; + } + } else { + if ((enable) + && (false == pe_session->beaconParams.gHTObssMode)) { + pe_debug("=>obss protection enabled"); + pe_session->beaconParams.gHTObssMode = true; + pBeaconParams->paramChangeBitmap |= PARAM_OBSS_MODE_CHANGED; /* UPDATE AN ENUM FOR OBSS MODE */ + + } else if (!enable + && (true == + pe_session->beaconParams.gHTObssMode)) { + pe_debug("===> obss Protection disabled"); + pe_session->beaconParams.gHTObssMode = false; + pBeaconParams->paramChangeBitmap |= + PARAM_OBSS_MODE_CHANGED; + + } + } + return QDF_STATUS_SUCCESS; +} + +/** + * lim_handle_ht20protection_enabled() - Handle ht20 protection enabled + * @mac_ctx: pointer to Gloal Mac Structure + * @overlap: variable for overlap detection + * @beaconparams: pointer to tpUpdateBeaconParams + * @session_entry: pointer to struct pe_session * + * + * Function handles ht20 protection enabled + * + * Return: none + */ +static void lim_handle_ht20protection_enabled(struct mac_context *mac_ctx, + uint8_t overlap, tpUpdateBeaconParams beaconparams, + struct pe_session *session_entry) +{ + /* + * If we are AP and HT capable, we need to set the HT OP mode + * appropriately. + */ + if (LIM_IS_AP_ROLE(session_entry) && overlap) { + session_entry->gLimOverlapHt20Params.protectionEnabled = true; + if ((eSIR_HT_OP_MODE_OVERLAP_LEGACY != + session_entry->htOperMode) && + (eSIR_HT_OP_MODE_MIXED != + session_entry->htOperMode)) { + session_entry->htOperMode = + eSIR_HT_OP_MODE_OVERLAP_LEGACY; + lim_enable_ht_rifs_protection(mac_ctx, true, + overlap, beaconparams, session_entry); + } + } else if (LIM_IS_AP_ROLE(session_entry) && !overlap) { + session_entry->gLimHt20Params.protectionEnabled = true; + if (eSIR_HT_OP_MODE_PURE == session_entry->htOperMode) { + if (session_entry->htSupportedChannelWidthSet != + eHT_CHANNEL_WIDTH_20MHZ) + session_entry->htOperMode = + eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT; + lim_enable_ht_rifs_protection(mac_ctx, false, + overlap, beaconparams, session_entry); + lim_enable_ht_obss_protection(mac_ctx, false, + overlap, beaconparams, session_entry); + } + } + /* This part is common for staiton as well. */ + if (false == session_entry->beaconParams.ht20Coexist) { + pe_debug("=> Protection from HT20 Enabled"); + beaconparams->ht20MhzCoexist = + session_entry->beaconParams.ht20Coexist = true; + beaconparams->paramChangeBitmap |= + PARAM_HT20MHZCOEXIST_CHANGED; + } +} + +/** + * lim_handle_ht20coexist_ht20protection() - ht20 protection for ht20 coexist + * @mac_ctx: pointer to Gloal Mac Structure + * @beaconparams: pointer to tpUpdateBeaconParams + * @session_entry: pointer to struct pe_session * + * @overlap: variable for overlap detection + * + * Function handles ht20 protection for ht20 coexist + * + * Return: none + */ +static void lim_handle_ht20coexist_ht20protection(struct mac_context *mac_ctx, + tpUpdateBeaconParams beaconparams, + struct pe_session *session_entry, uint8_t overlap) +{ + /* + * For AP role: + * we need to take care of HT OP mode change if needed. + * We need to take care of Overlap cases. + */ + if (LIM_IS_AP_ROLE(session_entry) && overlap) { + /* Overlap Legacy protection disabled. */ + session_entry->gLimOverlapHt20Params.protectionEnabled = + false; + /* + * no HT op mode change if any of the overlap + * protection enabled. + */ + if (!(session_entry->gLimOlbcParams.protectionEnabled || + session_entry->gLimOverlap11gParams.protectionEnabled || + session_entry->gLimOverlapHt20Params.protectionEnabled + || session_entry->gLimOverlapNonGfParams. + protectionEnabled) && + /* + * Check if there is a need to change HT + * OP mode. + */ + (eSIR_HT_OP_MODE_OVERLAP_LEGACY == + session_entry->htOperMode)) { + if (session_entry->gLimHt20Params. + protectionEnabled) { + if (eHT_CHANNEL_WIDTH_20MHZ == + session_entry-> + htSupportedChannelWidthSet) + session_entry->htOperMode = + eSIR_HT_OP_MODE_PURE; + else + session_entry->htOperMode = + eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT; + + lim_enable_ht_rifs_protection(mac_ctx, + false, overlap, beaconparams, + session_entry); + lim_enable_ht_obss_protection(mac_ctx, + false, overlap, beaconparams, + session_entry); + } else { + session_entry->htOperMode = + eSIR_HT_OP_MODE_PURE; + } + } + } else if (LIM_IS_AP_ROLE(session_entry) && !overlap) { + /* Disable protection from 11G stations. */ + session_entry->gLimHt20Params.protectionEnabled = false; + /* Change HT op mode appropriately. */ + if (eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT == + session_entry->htOperMode) { + session_entry->htOperMode = + eSIR_HT_OP_MODE_PURE; + lim_enable_ht_rifs_protection(mac_ctx, false, + overlap, beaconparams, session_entry); + lim_enable_ht_obss_protection(mac_ctx, false, + overlap, beaconparams, session_entry); + } + } + if (LIM_IS_AP_ROLE(session_entry)) { + pe_debug("===> Protection from HT 20 Disabled"); + beaconparams->ht20MhzCoexist = + session_entry->beaconParams.ht20Coexist = false; + beaconparams->paramChangeBitmap |= + PARAM_HT20MHZCOEXIST_CHANGED; + } + if (!LIM_IS_AP_ROLE(session_entry)) { + /* For station role */ + pe_debug("===> Protection from HT20 Disabled"); + beaconparams->ht20MhzCoexist = + session_entry->beaconParams.ht20Coexist = false; + beaconparams->paramChangeBitmap |= + PARAM_HT20MHZCOEXIST_CHANGED; + } +} + +/** + * lim_enable_ht20_protection() - Function to enable ht20 protection + * @mac_ctx: pointer to Global Mac structure + * @enable: 1=> enable protection, 0=> disable protection. + * @overlap: 1=> called from overlap context, 0 => called from assoc context. + * @beaconparams: pointer to tpUpdateBeaconParams + * @session_entry: pointer to struct pe_session * + * + * based on cofig enables\disables protection from Ht20 + * + * Return: 0 - success + */ +QDF_STATUS lim_enable_ht20_protection(struct mac_context *mac_ctx, uint8_t enable, + uint8_t overlap, tpUpdateBeaconParams beaconparams, + struct pe_session *session_entry) +{ + /* This protection is only for HT stations. */ + if (!session_entry->htCapability) + return QDF_STATUS_SUCCESS; + + /* overlapping protection configuration check. */ + if (!overlap) { + /* normal protection config check */ + if ((LIM_IS_AP_ROLE(session_entry)) && + !session_entry->cfgProtection.ht20) { + /* protection disabled. */ + pe_debug("protection from HT20 is disabled"); + return QDF_STATUS_SUCCESS; + } else if (!LIM_IS_AP_ROLE(session_entry)) { + if (!mac_ctx->lim.cfgProtection.ht20) { + /* protection disabled. */ + pe_debug("protection from HT20 is disabled"); + return QDF_STATUS_SUCCESS; + } + } + } + + if (enable) + lim_handle_ht20protection_enabled(mac_ctx, overlap, + beaconparams, session_entry); + else if (true == session_entry->beaconParams.ht20Coexist) + lim_handle_ht20coexist_ht20protection(mac_ctx, beaconparams, + session_entry, overlap); + + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn lim_enable_ht_non_gf_protection + \brief based on cofig enables\disables protection from NonGf. + \param uint8_t enable : 1=> enable protection, 0=> disable protection. + \param uint8_t overlap: 1=> called from overlap context, 0 => called from assoc context. + \param tpUpdateBeaconParams pBeaconParams + \return None + -------------------------------------------------------------*/ +QDF_STATUS +lim_enable_ht_non_gf_protection(struct mac_context *mac, uint8_t enable, + uint8_t overlap, tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + if (!pe_session->htCapability) + return QDF_STATUS_SUCCESS; /* this protection is only for HT stations. */ + + /* overlapping protection configuration check. */ + if (overlap) { + } else { + /* normal protection config check */ + if (LIM_IS_AP_ROLE(pe_session) && + !pe_session->cfgProtection.nonGf) { + /* protection disabled. */ + pe_debug("protection from NonGf is disabled"); + return QDF_STATUS_SUCCESS; + } else if (!LIM_IS_AP_ROLE(pe_session)) { + /* normal protection config check */ + if (!mac->lim.cfgProtection.nonGf) { + /* protection disabled. */ + pe_debug("protection from NonGf is disabled"); + return QDF_STATUS_SUCCESS; + } + } + } + if (LIM_IS_AP_ROLE(pe_session)) { + if ((enable) + && (false == pe_session->beaconParams.llnNonGFCoexist)) { + pe_debug(" => Protection from non GF Enabled"); + pBeaconParams->llnNonGFCoexist = + pe_session->beaconParams.llnNonGFCoexist = true; + pBeaconParams->paramChangeBitmap |= + PARAM_NON_GF_DEVICES_PRESENT_CHANGED; + } else if (!enable + && (true == + pe_session->beaconParams.llnNonGFCoexist)) { + pe_debug("===> Protection from Non GF Disabled"); + pBeaconParams->llnNonGFCoexist = + pe_session->beaconParams.llnNonGFCoexist = false; + pBeaconParams->paramChangeBitmap |= + PARAM_NON_GF_DEVICES_PRESENT_CHANGED; + } + } else { + if ((enable) + && (false == pe_session->beaconParams.llnNonGFCoexist)) { + pe_debug(" => Protection from non GF Enabled"); + pBeaconParams->llnNonGFCoexist = + pe_session->beaconParams.llnNonGFCoexist = true; + pBeaconParams->paramChangeBitmap |= + PARAM_NON_GF_DEVICES_PRESENT_CHANGED; + } else if (!enable + && (true == + pe_session->beaconParams.llnNonGFCoexist)) { + pe_debug("===> Protection from Non GF Disabled"); + pBeaconParams->llnNonGFCoexist = + pe_session->beaconParams.llnNonGFCoexist = false; + pBeaconParams->paramChangeBitmap |= + PARAM_NON_GF_DEVICES_PRESENT_CHANGED; + } + } + + return QDF_STATUS_SUCCESS; +} + +/** ------------------------------------------------------------- + \fn lim_enable_ht_lsig_txop_protection + \brief based on cofig enables\disables LsigTxop protection. + \param uint8_t enable : 1=> enable protection, 0=> disable protection. + \param uint8_t overlap: 1=> called from overlap context, 0 => called from assoc context. + \param tpUpdateBeaconParams pBeaconParams + \return None + -------------------------------------------------------------*/ +QDF_STATUS +lim_enable_ht_lsig_txop_protection(struct mac_context *mac, uint8_t enable, + uint8_t overlap, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + if (!pe_session->htCapability) + return QDF_STATUS_SUCCESS; /* this protection is only for HT stations. */ + + /* overlapping protection configuration check. */ + if (overlap) { + } else { + /* normal protection config check */ + if (LIM_IS_AP_ROLE(pe_session) && + !pe_session->cfgProtection.lsigTxop) { + /* protection disabled. */ + pe_debug("protection from LsigTxop not supported is disabled"); + return QDF_STATUS_SUCCESS; + } else if (!LIM_IS_AP_ROLE(pe_session)) { + /* normal protection config check */ + if (!mac->lim.cfgProtection.lsigTxop) { + /* protection disabled. */ + pe_debug("protection from LsigTxop not supported is disabled"); + return QDF_STATUS_SUCCESS; + } + } + } + + if (LIM_IS_AP_ROLE(pe_session)) { + if ((enable) + && (false == + pe_session->beaconParams. + fLsigTXOPProtectionFullSupport)) { + pe_debug(" => Protection from LsigTxop Enabled"); + pBeaconParams->fLsigTXOPProtectionFullSupport = + pe_session->beaconParams. + fLsigTXOPProtectionFullSupport = true; + pBeaconParams->paramChangeBitmap |= + PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED; + } else if (!enable + && (true == + pe_session->beaconParams. + fLsigTXOPProtectionFullSupport)) { + pe_debug("===> Protection from LsigTxop Disabled"); + pBeaconParams->fLsigTXOPProtectionFullSupport = + pe_session->beaconParams. + fLsigTXOPProtectionFullSupport = false; + pBeaconParams->paramChangeBitmap |= + PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED; + } + } else { + if ((enable) + && (false == + pe_session->beaconParams. + fLsigTXOPProtectionFullSupport)) { + pe_debug(" => Protection from LsigTxop Enabled"); + pBeaconParams->fLsigTXOPProtectionFullSupport = + pe_session->beaconParams. + fLsigTXOPProtectionFullSupport = true; + pBeaconParams->paramChangeBitmap |= + PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED; + } else if (!enable + && (true == + pe_session->beaconParams. + fLsigTXOPProtectionFullSupport)) { + pe_debug("===> Protection from LsigTxop Disabled"); + pBeaconParams->fLsigTXOPProtectionFullSupport = + pe_session->beaconParams. + fLsigTXOPProtectionFullSupport = false; + pBeaconParams->paramChangeBitmap |= + PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED; + } + } + return QDF_STATUS_SUCCESS; +} + +/* FIXME_PROTECTION : need to check for no APSD whenever we want to enable this protection. */ +/* This check will be done at the caller. */ +/** ------------------------------------------------------------- + \fn lim_enable_ht_rifs_protection + \brief based on cofig enables\disables Rifs protection. + \param uint8_t enable : 1=> enable protection, 0=> disable protection. + \param uint8_t overlap: 1=> called from overlap context, 0 => called from assoc context. + \param tpUpdateBeaconParams pBeaconParams + \return None + -------------------------------------------------------------*/ +QDF_STATUS +lim_enable_ht_rifs_protection(struct mac_context *mac, uint8_t enable, + uint8_t overlap, tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + if (!pe_session->htCapability) + return QDF_STATUS_SUCCESS; /* this protection is only for HT stations. */ + + /* overlapping protection configuration check. */ + if (overlap) { + } else { + /* normal protection config check */ + if (LIM_IS_AP_ROLE(pe_session) && + !pe_session->cfgProtection.rifs) { + /* protection disabled. */ + pe_debug("protection from Rifs is disabled"); + return QDF_STATUS_SUCCESS; + } else if (!LIM_IS_AP_ROLE(pe_session)) { + /* normal protection config check */ + if (!mac->lim.cfgProtection.rifs) { + /* protection disabled. */ + pe_debug("protection from Rifs is disabled"); + return QDF_STATUS_SUCCESS; + } + } + } + + if (LIM_IS_AP_ROLE(pe_session)) { + /* Disabling the RIFS Protection means Enable the RIFS mode of operation in the BSS */ + if ((!enable) + && (false == pe_session->beaconParams.fRIFSMode)) { + pe_debug(" => Rifs protection Disabled"); + pBeaconParams->fRIFSMode = + pe_session->beaconParams.fRIFSMode = true; + pBeaconParams->paramChangeBitmap |= + PARAM_RIFS_MODE_CHANGED; + } + /* Enabling the RIFS Protection means Disable the RIFS mode of operation in the BSS */ + else if (enable + && (true == pe_session->beaconParams.fRIFSMode)) { + pe_debug("===> Rifs Protection Enabled"); + pBeaconParams->fRIFSMode = + pe_session->beaconParams.fRIFSMode = false; + pBeaconParams->paramChangeBitmap |= + PARAM_RIFS_MODE_CHANGED; + } + } else { + /* Disabling the RIFS Protection means Enable the RIFS mode of operation in the BSS */ + if ((!enable) + && (false == pe_session->beaconParams.fRIFSMode)) { + pe_debug(" => Rifs protection Disabled"); + pBeaconParams->fRIFSMode = + pe_session->beaconParams.fRIFSMode = true; + pBeaconParams->paramChangeBitmap |= + PARAM_RIFS_MODE_CHANGED; + } + /* Enabling the RIFS Protection means Disable the RIFS mode of operation in the BSS */ + else if (enable + && (true == pe_session->beaconParams.fRIFSMode)) { + pe_debug("===> Rifs Protection Enabled"); + pBeaconParams->fRIFSMode = + pe_session->beaconParams.fRIFSMode = false; + pBeaconParams->paramChangeBitmap |= + PARAM_RIFS_MODE_CHANGED; + } + } + return QDF_STATUS_SUCCESS; +} + +/* --------------------------------------------------------------------- */ +/** + * lim_enable_short_preamble + * + * FUNCTION: + * Enable/Disable short preamble + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param enable Flag to enable/disable short preamble + * @return None + */ + +QDF_STATUS +lim_enable_short_preamble(struct mac_context *mac, uint8_t enable, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session) +{ + if (!mac->mlme_cfg->ht_caps.short_preamble) + return QDF_STATUS_SUCCESS; + + /* 11G short preamble switching is disabled. */ + if (!mac->mlme_cfg->feature_flags.enable_short_preamble_11g) + return QDF_STATUS_SUCCESS; + + if (LIM_IS_AP_ROLE(pe_session)) { + if (enable && (pe_session->beaconParams.fShortPreamble == 0)) { + pe_debug("===> Short Preamble Enabled"); + pe_session->beaconParams.fShortPreamble = true; + pBeaconParams->fShortPreamble = + (uint8_t) pe_session->beaconParams. + fShortPreamble; + pBeaconParams->paramChangeBitmap |= + PARAM_SHORT_PREAMBLE_CHANGED; + } else if (!enable + && (pe_session->beaconParams.fShortPreamble == + 1)) { + pe_debug("===> Short Preamble Disabled"); + pe_session->beaconParams.fShortPreamble = false; + pBeaconParams->fShortPreamble = + (uint8_t) pe_session->beaconParams. + fShortPreamble; + pBeaconParams->paramChangeBitmap |= + PARAM_SHORT_PREAMBLE_CHANGED; + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_tx_complete + * + * Function: + * This is LIM's very own "TX MGMT frame complete" completion routine. + * + * Logic: + * LIM wants to send a MGMT frame (broadcast or unicast) + * LIM allocates memory using cds_packet_alloc( ..., **pData, **pPacket ) + * LIM transmits the MGMT frame using the API: + * wma_tx_frame( ... pPacket, ..., (void *) lim_tx_complete, pData ) + * HDD, via wma_tx_frame/DXE, "transfers" the packet over to BMU + * HDD, if it determines that a TX completion routine (in this case + * lim_tx_complete) has been provided, will invoke this callback + * LIM will try to free the TX MGMT packet that was earlier allocated, in order + * to send this MGMT frame, using the PAL API cds_packet_free( ... pData, pPacket ) + * + * Assumptions: + * Presently, this is ONLY being used for MGMT frames/packets + * TODO: + * Would it do good for LIM to have some sort of "signature" validation to + * ensure that the pData argument passed in was a buffer that was actually + * allocated by LIM and/or is not corrupted? + * + * Note: FIXME and TODO + * Looks like cds_packet_free() is interested in pPacket. But, when this completion + * routine is called, only pData is made available to LIM!! + * + * @param void A pointer to pData. Shouldn't it be pPacket?! + * + * @return QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS lim_tx_complete(void *context, qdf_nbuf_t buf, bool free) +{ + if (free) + cds_packet_free((void *)buf); + + return QDF_STATUS_SUCCESS; +} + +static void lim_ht_switch_chnl_params(struct pe_session *pe_session) +{ + uint8_t center_freq = 0; + enum phy_ch_width ch_width = CH_WIDTH_20MHZ; + struct mac_context *mac; + uint8_t primary_channel; + + mac = pe_session->mac_ctx; + if (!mac) { + pe_err("Invalid mac_ctx"); + return; + } + + primary_channel = wlan_reg_freq_to_chan(mac->pdev, + pe_session->curr_op_freq); + if (eHT_CHANNEL_WIDTH_40MHZ == + pe_session->htRecommendedTxWidthSet) { + ch_width = CH_WIDTH_40MHZ; + if (PHY_DOUBLE_CHANNEL_LOW_PRIMARY == + pe_session->htSecondaryChannelOffset) + center_freq = primary_channel + 2; + else if (PHY_DOUBLE_CHANNEL_HIGH_PRIMARY == + pe_session->htSecondaryChannelOffset) + center_freq = primary_channel - 2; + else + ch_width = CH_WIDTH_20MHZ; + } + pe_session->gLimChannelSwitch.primaryChannel = primary_channel; + pe_session->curr_req_chan_freq = pe_session->curr_op_freq; + pe_session->ch_center_freq_seg0 = center_freq; + pe_session->gLimChannelSwitch.ch_center_freq_seg0 = center_freq; + pe_session->gLimChannelSwitch.sw_target_freq = + pe_session->curr_op_freq; + pe_session->ch_width = ch_width; + pe_session->gLimChannelSwitch.ch_width = ch_width; + pe_session->gLimChannelSwitch.sec_ch_offset = + pe_session->htSecondaryChannelOffset; + pe_session->gLimChannelSwitch.ch_center_freq_seg1 = 0; + + pe_debug("HT IE changed: Primary Channel: %d center chan: %d Channel Width: %d cur op freq %d", + primary_channel, center_freq, + pe_session->htRecommendedTxWidthSet, + pe_session->gLimChannelSwitch.sw_target_freq); + pe_session->channelChangeReasonCode = + LIM_SWITCH_CHANNEL_HT_WIDTH; + mac->lim.gpchangeChannelCallback = lim_switch_channel_cback; + mac->lim.gpchangeChannelData = NULL; + + lim_send_switch_chnl_params(mac, pe_session); +} + +static void lim_ht_switch_chnl_req(struct pe_session *session) +{ + struct mac_context *mac; + QDF_STATUS status; + + mac = session->mac_ctx; + if (!mac) { + pe_err("Invalid mac context"); + return; + } + + session->channelChangeReasonCode = + LIM_SWITCH_CHANNEL_HT_WIDTH; + mlme_set_chan_switch_in_progress(session->vdev, true); + status = wlan_vdev_mlme_sm_deliver_evt( + session->vdev, + WLAN_VDEV_SM_EV_FW_VDEV_RESTART, + sizeof(*session), + session); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Failed to post WLAN_VDEV_SM_EV_FW_VDEV_RESTART for vdevid %d", + session->smeSessionId); + mlme_set_chan_switch_in_progress(session->vdev, false); + } +} + +void lim_update_sta_run_time_ht_switch_chnl_params(struct mac_context *mac, + tDot11fIEHTInfo *pHTInfo, + struct pe_session *pe_session) +{ + /* If self capability is set to '20Mhz only', then do not change the CB mode. */ + if (!lim_get_ht_capability + (mac, eHT_SUPPORTED_CHANNEL_WIDTH_SET, pe_session)) + return; + + if (wlan_reg_is_24ghz_ch_freq(pe_session->curr_op_freq) && + pe_session->force_24ghz_in_ht20) { + pe_debug("force_24ghz_in_ht20 is set and channel is 2.4 Ghz"); + return; + } + + if (pe_session->ftPEContext.ftPreAuthSession) { + pe_err("FT PREAUTH channel change is in progress"); + return; + } + + if (pe_session->ch_switch_in_progress == true) { + pe_debug("ch switch is in progress, ignore HT IE BW update"); + return; + } + + if (wlan_reg_get_chan_enum(pHTInfo->primaryChannel) == + INVALID_CHANNEL) { + pe_debug("Ignore Invalid channel in HT info"); + return; + } + + /* If channel mismatch the CSA will take care of this change */ + if (pHTInfo->primaryChannel != wlan_reg_freq_to_chan( + mac->pdev, pe_session->curr_op_freq)) { + pe_debug("Current channel doesnt match HT info ignore"); + return; + } + + if (lim_is_roam_synch_in_progress(pe_session)) { + pe_debug("Roaming in progress, ignore HT IE BW update"); + return; + } + + if (pe_session->htSecondaryChannelOffset != + (uint8_t) pHTInfo->secondaryChannelOffset + || pe_session->htRecommendedTxWidthSet != + (uint8_t) pHTInfo->recommendedTxWidthSet) { + pe_session->htSecondaryChannelOffset = + (ePhyChanBondState) pHTInfo->secondaryChannelOffset; + pe_session->htRecommendedTxWidthSet = + (uint8_t) pHTInfo->recommendedTxWidthSet; + + /* Before restarting vdev, delete the tdls peers */ + lim_update_tdls_set_state_for_fw(pe_session, false); + lim_delete_tdls_peers(mac, pe_session); + + lim_ht_switch_chnl_req(pe_session); + + /* In case of IBSS, if STA should update HT Info IE in its beacons. */ + if (LIM_IS_IBSS_ROLE(pe_session)) + sch_set_fixed_beacon_fields(mac, pe_session); + } + +} /* End limUpdateStaRunTimeHTParams. */ + +/** + * \brief This function updates the lim global structure, if any of the + * HT Capabilities have changed. + * + * + * \param mac Pointer to Global MAC structure + * + * \param pHTCapability Pointer to HT Capability Information Element + * obtained from a Beacon or Probe Response + * + * + * + */ + +void lim_update_sta_run_time_ht_capability(struct mac_context *mac, + tDot11fIEHTCaps *pHTCaps) +{ + + if (mac->lim.gHTLsigTXOPProtection != + (uint8_t) pHTCaps->lsigTXOPProtection) { + mac->lim.gHTLsigTXOPProtection = + (uint8_t) pHTCaps->lsigTXOPProtection; + /* Send change notification to HAL */ + } + + if (mac->lim.gHTAMpduDensity != (uint8_t) pHTCaps->mpduDensity) { + mac->lim.gHTAMpduDensity = (uint8_t) pHTCaps->mpduDensity; + /* Send change notification to HAL */ + } + + if (mac->lim.gHTMaxRxAMpduFactor != + (uint8_t) pHTCaps->maxRxAMPDUFactor) { + mac->lim.gHTMaxRxAMpduFactor = + (uint8_t) pHTCaps->maxRxAMPDUFactor; + /* Send change notification to HAL */ + } + +} /* End lim_update_sta_run_time_ht_capability. */ + +/** + * \brief This function updates lim global structure, if any of the HT + * Info Parameters have changed. + * + * + * \param mac Pointer to the global MAC structure + * + * \param pHTInfo Pointer to the HT Info IE obtained from a Beacon or + * Probe Response + * + * + */ + +void lim_update_sta_run_time_ht_info(struct mac_context *mac, + tDot11fIEHTInfo *pHTInfo, + struct pe_session *pe_session) +{ + if (pe_session->htRecommendedTxWidthSet != + (uint8_t) pHTInfo->recommendedTxWidthSet) { + pe_session->htRecommendedTxWidthSet = + (uint8_t) pHTInfo->recommendedTxWidthSet; + /* Send change notification to HAL */ + } + + if (pe_session->beaconParams.fRIFSMode != + (uint8_t) pHTInfo->rifsMode) { + pe_session->beaconParams.fRIFSMode = + (uint8_t) pHTInfo->rifsMode; + /* Send change notification to HAL */ + } + + if (mac->lim.gHTServiceIntervalGranularity != + (uint8_t) pHTInfo->serviceIntervalGranularity) { + mac->lim.gHTServiceIntervalGranularity = + (uint8_t) pHTInfo->serviceIntervalGranularity; + /* Send change notification to HAL */ + } + + if (mac->lim.gHTOperMode != (tSirMacHTOperatingMode) pHTInfo->opMode) { + mac->lim.gHTOperMode = + (tSirMacHTOperatingMode) pHTInfo->opMode; + /* Send change notification to HAL */ + } + + if (pe_session->beaconParams.llnNonGFCoexist != + pHTInfo->nonGFDevicesPresent) { + pe_session->beaconParams.llnNonGFCoexist = + (uint8_t) pHTInfo->nonGFDevicesPresent; + } + + if (mac->lim.gHTSTBCBasicMCS != (uint8_t) pHTInfo->basicSTBCMCS) { + mac->lim.gHTSTBCBasicMCS = (uint8_t) pHTInfo->basicSTBCMCS; + /* Send change notification to HAL */ + } + + if (mac->lim.gHTDualCTSProtection != + (uint8_t) pHTInfo->dualCTSProtection) { + mac->lim.gHTDualCTSProtection = + (uint8_t) pHTInfo->dualCTSProtection; + /* Send change notification to HAL */ + } + + if (mac->lim.gHTSecondaryBeacon != (uint8_t) pHTInfo->secondaryBeacon) { + mac->lim.gHTSecondaryBeacon = + (uint8_t) pHTInfo->secondaryBeacon; + /* Send change notification to HAL */ + } + + if (pe_session->beaconParams.fLsigTXOPProtectionFullSupport != + (uint8_t) pHTInfo->lsigTXOPProtectionFullSupport) { + pe_session->beaconParams.fLsigTXOPProtectionFullSupport = + (uint8_t) pHTInfo->lsigTXOPProtectionFullSupport; + /* Send change notification to HAL */ + } + + if (mac->lim.gHTPCOActive != (uint8_t) pHTInfo->pcoActive) { + mac->lim.gHTPCOActive = (uint8_t) pHTInfo->pcoActive; + /* Send change notification to HAL */ + } + + if (mac->lim.gHTPCOPhase != (uint8_t) pHTInfo->pcoPhase) { + mac->lim.gHTPCOPhase = (uint8_t) pHTInfo->pcoPhase; + /* Send change notification to HAL */ + } + +} /* End lim_update_sta_run_time_ht_info. */ + +/** + * lim_validate_delts_req() - This function validates DelTs req + * @mac_ctx: pointer to Global Mac structure + * @delts_req: pointer to delete traffic stream structure + * @peer_mac_addr: variable for peer mac address + * @session: pe session entry + * + * Function validates DelTs req originated by SME or by HAL and also + * sends halMsg_DelTs to HAL + * + * Return: QDF_STATUS_SUCCESS - Success, QDF_STATUS_E_FAILURE - Failure + */ + +QDF_STATUS +lim_validate_delts_req(struct mac_context *mac_ctx, tpSirDeltsReq delts_req, + tSirMacAddr peer_mac_addr, + struct pe_session *session) +{ + tpDphHashNode sta; + uint8_t ts_status; + struct mac_ts_info *tsinfo; + uint32_t i; + uint8_t tspec_idx; + + /* + * if sta + * - verify assoc state + * - del tspec locally + * if ap + * - verify sta is in assoc state + * - del sta tspec locally + */ + if (!delts_req) { + pe_err("Delete TS request pointer is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (LIM_IS_STA_ROLE(session)) { + uint32_t val; + + /* station always talks to the AP */ + sta = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session->dph.dphHashTable); + + val = sizeof(tSirMacAddr); + sir_copy_mac_addr(peer_mac_addr, session->bssId); + + } else { + uint16_t associd; + uint8_t *macaddr = (uint8_t *) peer_mac_addr; + + associd = delts_req->aid; + if (associd != 0) + sta = dph_get_hash_entry(mac_ctx, associd, + &session->dph.dphHashTable); + else + sta = dph_lookup_hash_entry(mac_ctx, + delts_req->macaddr.bytes, + &associd, + &session->dph. + dphHashTable); + + if (sta) + /* TBD: check sta assoc state as well */ + for (i = 0; i < sizeof(tSirMacAddr); i++) + macaddr[i] = sta->staAddr[i]; + } + + if (!sta) { + pe_err("Cannot find station context for delts req"); + return QDF_STATUS_E_FAILURE; + } + + if ((!sta->valid) || + (sta->mlmStaContext.mlmState != + eLIM_MLM_LINK_ESTABLISHED_STATE)) { + pe_err("Invalid Sta (or state) for DelTsReq"); + return QDF_STATUS_E_FAILURE; + } + + delts_req->req.wsmTspecPresent = 0; + delts_req->req.wmeTspecPresent = 0; + delts_req->req.lleTspecPresent = 0; + + if ((sta->wsmEnabled) && + (delts_req->req.tspec.tsinfo.traffic.accessPolicy != + SIR_MAC_ACCESSPOLICY_EDCA)) + delts_req->req.wsmTspecPresent = 1; + else if (sta->wmeEnabled) + delts_req->req.wmeTspecPresent = 1; + else if (sta->lleEnabled) + delts_req->req.lleTspecPresent = 1; + else { + pe_warn("DELTS_REQ ignore - qos is disabled"); + return QDF_STATUS_E_FAILURE; + } + + tsinfo = delts_req->req.wmeTspecPresent ? &delts_req->req.tspec.tsinfo + : &delts_req->req.tsinfo; + pe_debug("received DELTS_REQ message wmeTspecPresent: %d lleTspecPresent: %d wsmTspecPresent: %d tsid: %d up: %d direction: %d", + delts_req->req.wmeTspecPresent, + delts_req->req.lleTspecPresent, + delts_req->req.wsmTspecPresent, tsinfo->traffic.tsid, + tsinfo->traffic.userPrio, tsinfo->traffic.direction); + + /* if no Access Control, ignore the request */ + if (lim_admit_control_delete_ts(mac_ctx, sta->assocId, tsinfo, + &ts_status, &tspec_idx) != QDF_STATUS_SUCCESS) { + pe_err("DELTS request for sta assocId: %d tsid: %d up: %d", + sta->assocId, tsinfo->traffic.tsid, + tsinfo->traffic.userPrio); + return QDF_STATUS_E_FAILURE; + } else if ((tsinfo->traffic.accessPolicy == SIR_MAC_ACCESSPOLICY_HCCA) + || (tsinfo->traffic.accessPolicy == + SIR_MAC_ACCESSPOLICY_BOTH)) { + /* edca only now. */ + } else if (tsinfo->traffic.accessPolicy == SIR_MAC_ACCESSPOLICY_EDCA) { + /* send message to HAL to delete TS */ + if (QDF_STATUS_SUCCESS != + lim_send_hal_msg_del_ts(mac_ctx, + tspec_idx, delts_req->req, + session->peSessionId, + session->bssId)) { + pe_warn("DelTs with UP: %d failed in lim_send_hal_msg_del_ts - ignoring request", + tsinfo->traffic.userPrio); + return QDF_STATUS_E_FAILURE; + } + } + return QDF_STATUS_SUCCESS; +} + +/** + * @function : lim_post_sm_state_update() + * + * @brief : This function Updates the HAL and Softmac about the change in the STA's SMPS state. + * + * LOGIC: + * + * ASSUMPTIONS: + * NA + * + * NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param limMsg - Lim Message structure object with the MimoPSparam in body + * @return None + */ +QDF_STATUS +lim_post_sm_state_update(struct mac_context *mac, + tSirMacHTMIMOPowerSaveState state, + uint8_t *pPeerStaMac, uint8_t sessionId) +{ + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + tpSetMIMOPS pMIMO_PSParams; + + msgQ.reserved = 0; + msgQ.type = WMA_SET_MIMOPS_REQ; + + /* Allocate for WMA_SET_MIMOPS_REQ */ + pMIMO_PSParams = qdf_mem_malloc(sizeof(tSetMIMOPS)); + if (!pMIMO_PSParams) + return QDF_STATUS_E_NOMEM; + + pMIMO_PSParams->htMIMOPSState = state; + pMIMO_PSParams->fsendRsp = true; + pMIMO_PSParams->sessionId = sessionId; + qdf_mem_copy(pMIMO_PSParams->peerMac, pPeerStaMac, sizeof(tSirMacAddr)); + + msgQ.bodyptr = pMIMO_PSParams; + msgQ.bodyval = 0; + + pe_debug("Sending WMA_SET_MIMOPS_REQ"); + + MTRACE(mac_trace_msg_tx(mac, NO_SESSION, msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + pe_err("Posting WMA_SET_MIMOPS_REQ to HAL failed! Reason: %d", + retCode); + qdf_mem_free(pMIMO_PSParams); + return retCode; + } + + return retCode; +} + +void lim_pkt_free(struct mac_context *mac, + eFrameType frmType, uint8_t *pRxPacketInfo, void *pBody) +{ + (void)mac; + (void)frmType; + (void)pRxPacketInfo; + (void)pBody; +} + +/** + * lim_get_b_dfrom_rx_packet() + * + ***FUNCTION: + * This function is called to get pointer to Polaris + * Buffer Descriptor containing MAC header & other control + * info from the body of the message posted to LIM. + * + ***LOGIC: + * NA + * + ***ASSUMPTIONS: + * NA + * + ***NOTE: + * NA + * + * @param body - Received message body + * @param pRxPacketInfo - Pointer to received BD + * @return None + */ + +void +lim_get_b_dfrom_rx_packet(struct mac_context *mac, void *body, uint32_t **pRxPacketInfo) +{ + *pRxPacketInfo = (uint32_t *) body; +} /*** end lim_get_b_dfrom_rx_packet() ***/ + +void lim_add_channel_status_info(struct mac_context *p_mac, + struct lim_channel_status *channel_stat, + uint8_t channel_id) +{ + uint8_t i; + bool found = false; + struct lim_scan_channel_status *channel_info = + &p_mac->lim.scan_channel_status; + struct lim_channel_status *channel_status_list = + channel_info->channel_status_list; + uint8_t total_channel = channel_info->total_channel; + + if (!p_mac->sap.acs_with_more_param) + return; + + for (i = 0; i < total_channel; i++) { + if (channel_status_list[i].channel_id == channel_id) { + if (channel_stat->cmd_flags == + WMI_CHAN_InFO_END_RESP && + channel_status_list[i].cmd_flags == + WMI_CHAN_InFO_START_RESP) { + /* adjust to delta value for counts */ + channel_stat->rx_clear_count -= + channel_status_list[i].rx_clear_count; + channel_stat->cycle_count -= + channel_status_list[i].cycle_count; + channel_stat->rx_frame_count -= + channel_status_list[i].rx_frame_count; + channel_stat->tx_frame_count -= + channel_status_list[i].tx_frame_count; + channel_stat->bss_rx_cycle_count -= + channel_status_list[i].bss_rx_cycle_count; + } + qdf_mem_copy(&channel_status_list[i], channel_stat, + sizeof(*channel_status_list)); + found = true; + break; + } + } + + if (!found) { + if (total_channel < SIR_MAX_SUPPORTED_CHANNEL_LIST) { + qdf_mem_copy(&channel_status_list[total_channel++], + channel_stat, + sizeof(*channel_status_list)); + channel_info->total_channel = total_channel; + } else { + pe_warn("Chan cnt exceed, channel_id=%d", channel_id); + } + } + + return; +} + +bool lim_is_channel_valid_for_channel_switch(struct mac_context *mac, + uint32_t channel_freq) +{ + uint8_t index; + bool ok = false; + + if (policy_mgr_is_chan_ok_for_dnbs(mac->psoc, channel_freq, + &ok)) { + pe_err("policy_mgr_is_chan_ok_for_dnbs() returned error"); + return false; + } + + if (!ok) { + pe_debug("channel not ok for DNBS"); + return false; + } + + for (index = 0; index < mac->mlme_cfg->reg.valid_channel_list_num; index++) { + if (mac->mlme_cfg->reg.valid_channel_freq_list[index] != + channel_freq) + continue; + + ok = policy_mgr_is_valid_for_channel_switch( + mac->psoc, channel_freq); + return ok; + } + + /* channel does not belong to list of valid channels */ + return false; +} + +/** + * @function : lim_restore_pre_channel_switch_state() + * + * @brief : This API is called by the user to undo any + * specific changes done on the device during + * channel switch. + * LOGIC: + * + * ASSUMPTIONS: + * NA + * + * NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @return None + */ + +QDF_STATUS +lim_restore_pre_channel_switch_state(struct mac_context *mac, struct pe_session *pe_session) +{ + + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + + if (!LIM_IS_STA_ROLE(pe_session)) + return retCode; + + /* Channel switch should be ready for the next time */ + pe_session->gLimSpecMgmt.dot11hChanSwState = eLIM_11H_CHANSW_INIT; + + return retCode; +} + +/** + * @function: lim_prepare_for11h_channel_switch() + * + * @brief : This API is called by the user to prepare for + * 11h channel switch. As of now, the API does + * very minimal work. User can add more into the + * same API if needed. + * LOGIC: + * + * ASSUMPTIONS: + * NA + * + * NOTE: + * NA + * + * @param mac - Pointer to Global MAC structure + * @param pe_session + * @return None + */ +void +lim_prepare_for11h_channel_switch(struct mac_context *mac, struct pe_session *pe_session) +{ + if (!LIM_IS_STA_ROLE(pe_session)) + return; + + /* Flag to indicate 11h channel switch in progress */ + pe_session->gLimSpecMgmt.dot11hChanSwState = eLIM_11H_CHANSW_RUNNING; + + /** We are safe to switch channel at this point */ + lim_stop_tx_and_switch_channel(mac, pe_session->peSessionId); +} + +tSirNwType lim_get_nw_type(struct mac_context *mac, uint32_t chan_freq, uint32_t type, + tpSchBeaconStruct pBeacon) +{ + tSirNwType nwType = eSIR_11B_NW_TYPE; + + /* Logic to be cleaned up for 11AC & 11AX */ + if (type == SIR_MAC_DATA_FRAME) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq)) { + nwType = eSIR_11G_NW_TYPE; + } else { + nwType = eSIR_11A_NW_TYPE; + } + } else { + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq)) { + int i; + /* 11b or 11g packet */ + /* 11g iff extended Rate IE is present or */ + /* if there is an A rate in suppRate IE */ + for (i = 0; i < pBeacon->supportedRates.numRates; i++) { + if (sirIsArate + (pBeacon->supportedRates.rate[i] & 0x7f)) { + nwType = eSIR_11G_NW_TYPE; + break; + } + } + if (pBeacon->extendedRatesPresent) { + nwType = eSIR_11G_NW_TYPE; + } else if (pBeacon->HTInfo.present || + IS_BSS_VHT_CAPABLE(pBeacon->VHTCaps)) { + nwType = eSIR_11G_NW_TYPE; + } + } else { + /* 11a packet */ + nwType = eSIR_11A_NW_TYPE; + } + } + return nwType; +} + +uint32_t lim_get_channel_from_beacon(struct mac_context *mac, tpSchBeaconStruct pBeacon) +{ + uint8_t chan_freq = 0; + + if (pBeacon->he_op.oper_info_6g_present) + chan_freq = wlan_reg_chan_band_to_freq(mac->pdev, + pBeacon->he_op.oper_info_6g.info.primary_ch, + BIT(REG_BAND_6G)); + else if (pBeacon->dsParamsPresent) + chan_freq = pBeacon->chan_freq; + else if (pBeacon->HTInfo.present) + chan_freq = wlan_reg_legacy_chan_to_freq(mac->pdev, + pBeacon->HTInfo.primaryChannel); + else + chan_freq = pBeacon->chan_freq; + + return chan_freq; +} + +void lim_set_tspec_uapsd_mask_per_session(struct mac_context *mac, + struct pe_session *pe_session, + struct mac_ts_info *pTsInfo, + uint32_t action) +{ + uint8_t userPrio = (uint8_t) pTsInfo->traffic.userPrio; + uint16_t direction = pTsInfo->traffic.direction; + uint8_t ac = upToAc(userPrio); + + pe_debug("Set UAPSD mask for AC: %d dir: %d action: %d" + , ac, direction, action); + + /* Converting AC to appropriate Uapsd Bit Mask + * AC_BE(0) --> UAPSD_BITOFFSET_ACVO(3) + * AC_BK(1) --> UAPSD_BITOFFSET_ACVO(2) + * AC_VI(2) --> UAPSD_BITOFFSET_ACVO(1) + * AC_VO(3) --> UAPSD_BITOFFSET_ACVO(0) + */ + ac = ((~ac) & 0x3); + + if (action == CLEAR_UAPSD_MASK) { + if (direction == SIR_MAC_DIRECTION_UPLINK) + pe_session->gUapsdPerAcTriggerEnableMask &= + ~(1 << ac); + else if (direction == SIR_MAC_DIRECTION_DNLINK) + pe_session->gUapsdPerAcDeliveryEnableMask &= + ~(1 << ac); + else if (direction == SIR_MAC_DIRECTION_BIDIR) { + pe_session->gUapsdPerAcTriggerEnableMask &= + ~(1 << ac); + pe_session->gUapsdPerAcDeliveryEnableMask &= + ~(1 << ac); + } + } else if (action == SET_UAPSD_MASK) { + if (direction == SIR_MAC_DIRECTION_UPLINK) + pe_session->gUapsdPerAcTriggerEnableMask |= + (1 << ac); + else if (direction == SIR_MAC_DIRECTION_DNLINK) + pe_session->gUapsdPerAcDeliveryEnableMask |= + (1 << ac); + else if (direction == SIR_MAC_DIRECTION_BIDIR) { + pe_session->gUapsdPerAcTriggerEnableMask |= + (1 << ac); + pe_session->gUapsdPerAcDeliveryEnableMask |= + (1 << ac); + } + } + + pe_debug("New pe_session->gUapsdPerAcTriggerEnableMask 0x%x pe_session->gUapsdPerAcDeliveryEnableMask 0x%x", + pe_session->gUapsdPerAcTriggerEnableMask, + pe_session->gUapsdPerAcDeliveryEnableMask); + + return; +} + +/** + * lim_handle_heart_beat_timeout_for_session() - Handle heart beat time out + * @mac_ctx: pointer to Global Mac Structure + * @psession_entry: pointer to struct pe_session * + * + * Function handles heart beat time out for session + * + * Return: none + */ +void lim_handle_heart_beat_timeout_for_session(struct mac_context *mac_ctx, + struct pe_session *psession_entry) +{ + if (psession_entry->valid == true) { + if (psession_entry->bssType == eSIR_IBSS_MODE) + lim_ibss_heart_beat_handle(mac_ctx, psession_entry); + + if ((psession_entry->bssType == eSIR_INFRASTRUCTURE_MODE) && + (LIM_IS_STA_ROLE(psession_entry))) + lim_handle_heart_beat_failure(mac_ctx, psession_entry); + } + /* + * In the function lim_handle_heart_beat_failure things can change + * so check for the session entry valid and the other things + * again + */ + if ((psession_entry->valid == true) && + (psession_entry->bssType == eSIR_INFRASTRUCTURE_MODE) && + (LIM_IS_STA_ROLE(psession_entry)) && + (psession_entry->LimHBFailureStatus == true)) { + tLimTimers *lim_timer = &mac_ctx->lim.lim_timers; + /* + * Activate Probe After HeartBeat Timer incase HB + * Failure detected + */ + pe_debug("Sending Probe for Session: %d", + psession_entry->vdev_id); + lim_deactivate_and_change_timer(mac_ctx, + eLIM_PROBE_AFTER_HB_TIMER); + MTRACE(mac_trace(mac_ctx, TRACE_CODE_TIMER_ACTIVATE, 0, + eLIM_PROBE_AFTER_HB_TIMER)); + if (tx_timer_activate(&lim_timer->gLimProbeAfterHBTimer) + != TX_SUCCESS) + pe_err("Fail to re-activate Probe-after-hb timer"); + } +} + +void lim_process_add_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + struct pe_session *session; + tpAddStaParams add_sta_params; + + add_sta_params = (tpAddStaParams) msg->bodyptr; + + session = pe_find_session_by_session_id(mac_ctx, + add_sta_params->sessionId); + if (!session) { + pe_err("Session Does not exist for given sessionID"); + qdf_mem_free(add_sta_params); + return; + } + session->csaOffloadEnable = add_sta_params->csaOffloadEnable; + if (LIM_IS_IBSS_ROLE(session)) + (void)lim_ibss_add_sta_rsp(mac_ctx, msg->bodyptr, session); + else if (LIM_IS_NDI_ROLE(session)) + lim_ndp_add_sta_rsp(mac_ctx, session, msg->bodyptr); +#ifdef FEATURE_WLAN_TDLS + else if (add_sta_params->staType == STA_ENTRY_TDLS_PEER) + lim_process_tdls_add_sta_rsp(mac_ctx, msg->bodyptr, session); +#endif + else + lim_process_mlm_add_sta_rsp(mac_ctx, msg, session); + +} + +/** + * lim_update_beacon() - This function updates beacon + * @mac_ctx: pointer to Global Mac Structure + * + * This Function is invoked to update the beacon + * + * Return: none + */ +void lim_update_beacon(struct mac_context *mac_ctx) +{ + uint8_t i; + + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + if (mac_ctx->lim.gpSession[i].valid != true) + continue; + if (((mac_ctx->lim.gpSession[i].limSystemRole == eLIM_AP_ROLE) + || (mac_ctx->lim.gpSession[i].limSystemRole == + eLIM_STA_IN_IBSS_ROLE)) + && (eLIM_SME_NORMAL_STATE == + mac_ctx->lim.gpSession[i].limSmeState)) { + + sch_set_fixed_beacon_fields(mac_ctx, + &mac_ctx->lim.gpSession[i]); + + if (false == mac_ctx->sap.SapDfsInfo. + is_dfs_cac_timer_running) + lim_send_beacon_ind(mac_ctx, + &mac_ctx->lim.gpSession[i], + REASON_DEFAULT); + } + } +} + +/** + * lim_handle_heart_beat_failure_timeout - handle heart beat failure + * @mac_ctx: pointer to Global Mac Structure + * + * Function handle heart beat failure timeout + * + * Return: none + */ +void lim_handle_heart_beat_failure_timeout(struct mac_context *mac_ctx) +{ + uint8_t i; + struct pe_session *psession_entry; + /* + * Probe response is not received after HB failure. + * This is handled by LMM sub module. + */ + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + if (mac_ctx->lim.gpSession[i].valid != true) + continue; + psession_entry = &mac_ctx->lim.gpSession[i]; + if (psession_entry->LimHBFailureStatus != true) + continue; + pe_debug("SME: %d MLME: %d HB-Count: %d", + psession_entry->limSmeState, + psession_entry->limMlmState, + psession_entry->LimRxedBeaconCntDuringHB); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM + lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_HB_FAILURE_TIMEOUT, + psession_entry, 0, 0); +#endif + if ((psession_entry->limMlmState == + eLIM_MLM_LINK_ESTABLISHED_STATE) && + (psession_entry->limSmeState != + eLIM_SME_WT_DISASSOC_STATE) && + (psession_entry->limSmeState != + eLIM_SME_WT_DEAUTH_STATE) && + ((!LIM_IS_CONNECTION_ACTIVE(psession_entry)) || + /* + * Disconnect even if we have not received a single + * beacon after connection. + */ + (psession_entry->currentBssBeaconCnt == 0))) { + pe_nofl_info("HB fail vdev %d",psession_entry->vdev_id); + lim_send_deauth_mgmt_frame(mac_ctx, + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON, + psession_entry->bssId, psession_entry, false); + + /* + * AP did not respond to Probe Request. + * Tear down link with it. + */ + lim_tear_down_link_with_ap(mac_ctx, + psession_entry->peSessionId, + eSIR_MAC_BEACON_MISSED, + eLIM_LINK_MONITORING_DISASSOC); + mac_ctx->lim.gLimProbeFailureAfterHBfailedCnt++; + } else { + pe_err("Unexpected wt-probe-timeout in state"); + lim_print_mlm_state(mac_ctx, LOGE, + psession_entry->limMlmState); + if (mac_ctx->sme.tx_queue_cb) + mac_ctx->sme.tx_queue_cb(mac_ctx->hdd_handle, + psession_entry->smeSessionId, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + } + } + /* + * Deactivate Timer ProbeAfterHB Timer -> As its a oneshot timer, + * need not deactivate the timer + * tx_timer_deactivate(&mac->lim.lim_timers.gLimProbeAfterHBTimer); + */ +} + +#ifdef QCA_IBSS_SUPPORT +/* + * This function assumes there will not be more than one IBSS session active at any time. + */ +struct pe_session *lim_is_ibss_session_active(struct mac_context *mac) +{ + uint8_t i; + + for (i = 0; i < mac->lim.maxBssId; i++) { + if ((mac->lim.gpSession[i].valid) && + (mac->lim.gpSession[i].limSystemRole == + eLIM_STA_IN_IBSS_ROLE)) + return &mac->lim.gpSession[i]; + } + + return NULL; +} +#endif + +struct pe_session *lim_is_ap_session_active(struct mac_context *mac) +{ + uint8_t i; + + for (i = 0; i < mac->lim.maxBssId; i++) { + if (mac->lim.gpSession[i].valid && + (mac->lim.gpSession[i].limSystemRole == eLIM_AP_ROLE)) + return &mac->lim.gpSession[i]; + } + + return NULL; +} + +/**--------------------------------------------------------- + \fn lim_handle_defer_msg_error + \brief handles error scenario, when the msg can not be deferred. + \param mac + \param pLimMsg LIM msg, which could not be deferred. + \return void + -----------------------------------------------------------*/ + +void lim_handle_defer_msg_error(struct mac_context *mac, + struct scheduler_msg *pLimMsg) +{ + if (SIR_BB_XPORT_MGMT_MSG == pLimMsg->type) { + lim_decrement_pending_mgmt_count(mac); + cds_pkt_return_packet((cds_pkt_t *) pLimMsg->bodyptr); + pLimMsg->bodyptr = NULL; + } else if (pLimMsg->bodyptr) { + qdf_mem_free(pLimMsg->bodyptr); + pLimMsg->bodyptr = NULL; + } + +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +/**--------------------------------------------------------- + \fn lim_diag_event_report + \brief This function reports Diag event + \param mac + \param eventType + \param bssid + \param status + \param reasonCode + \return void + -----------------------------------------------------------*/ +void lim_diag_event_report(struct mac_context *mac, uint16_t eventType, + struct pe_session *pe_session, uint16_t status, + uint16_t reasonCode) +{ + tSirMacAddr nullBssid = { 0, 0, 0, 0, 0, 0 }; + + WLAN_HOST_DIAG_EVENT_DEF(peEvent, host_event_wlan_pe_payload_type); + + qdf_mem_zero(&peEvent, sizeof(host_event_wlan_pe_payload_type)); + + if (!pe_session) { + qdf_mem_copy(peEvent.bssid, nullBssid, sizeof(tSirMacAddr)); + peEvent.sme_state = (uint16_t) mac->lim.gLimSmeState; + peEvent.mlm_state = (uint16_t) mac->lim.gLimMlmState; + + } else { + qdf_mem_copy(peEvent.bssid, pe_session->bssId, + sizeof(tSirMacAddr)); + peEvent.sme_state = (uint16_t) pe_session->limSmeState; + peEvent.mlm_state = (uint16_t) pe_session->limMlmState; + } + peEvent.event_type = eventType; + peEvent.status = status; + peEvent.reason_code = reasonCode; + + WLAN_HOST_DIAG_EVENT_REPORT(&peEvent, EVENT_WLAN_PE); + return; +} + +static void lim_diag_fill_mgmt_event_report(struct mac_context *mac_ctx, + tpSirMacMgmtHdr mac_hdr, + struct pe_session *session, uint16_t result_code, + uint16_t reason_code, + struct host_event_wlan_mgmt_payload_type *mgmt_event) +{ + uint8_t length; + + qdf_mem_zero(mgmt_event, sizeof(*mgmt_event)); + mgmt_event->mgmt_type = mac_hdr->fc.type; + mgmt_event->mgmt_subtype = mac_hdr->fc.subType; + qdf_mem_copy(mgmt_event->self_mac_addr, session->self_mac_addr, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(mgmt_event->bssid, session->bssId, + QDF_MAC_ADDR_SIZE); + length = session->ssId.length; + if (length > WLAN_SSID_MAX_LEN) + length = WLAN_SSID_MAX_LEN; + qdf_mem_copy(mgmt_event->ssid, session->ssId.ssId, length); + mgmt_event->ssid_len = length; + mgmt_event->operating_channel = wlan_reg_freq_to_chan( + mac_ctx->pdev, session->curr_op_freq); + mgmt_event->result_code = result_code; + mgmt_event->reason_code = reason_code; +} + +void lim_diag_mgmt_tx_event_report(struct mac_context *mac_ctx, void *mgmt_hdr, + struct pe_session *session, uint16_t result_code, + uint16_t reason_code) +{ + tpSirMacMgmtHdr mac_hdr = mgmt_hdr; + + WLAN_HOST_DIAG_EVENT_DEF(mgmt_event, + struct host_event_wlan_mgmt_payload_type); + if (!session || !mac_hdr) { + pe_err("not valid input"); + return; + } + lim_diag_fill_mgmt_event_report(mac_ctx, mac_hdr, session, + result_code, reason_code, &mgmt_event); + + pe_debug("TX frame: type:%d sub_type:%d seq_num:%d ssid:%.*s selfmacaddr:"QDF_MAC_ADDR_FMT" bssid:"QDF_MAC_ADDR_FMT" channel:%d", + mgmt_event.mgmt_type, mgmt_event.mgmt_subtype, + ((mac_hdr->seqControl.seqNumHi << 4) | + mac_hdr->seqControl.seqNumLo), + mgmt_event.ssid_len, mgmt_event.ssid, + QDF_MAC_ADDR_REF(mgmt_event.self_mac_addr), + QDF_MAC_ADDR_REF(mgmt_event.bssid), + mgmt_event.operating_channel); + WLAN_HOST_DIAG_EVENT_REPORT(&mgmt_event, EVENT_WLAN_HOST_MGMT_TX_V2); +} + +void lim_diag_mgmt_rx_event_report(struct mac_context *mac_ctx, void *mgmt_hdr, + struct pe_session *session, uint16_t result_code, + uint16_t reason_code) +{ + tpSirMacMgmtHdr mac_hdr = mgmt_hdr; + + WLAN_HOST_DIAG_EVENT_DEF(mgmt_event, + struct host_event_wlan_mgmt_payload_type); + if (!session || !mac_hdr) { + pe_debug("not valid input"); + return; + } + lim_diag_fill_mgmt_event_report(mac_ctx, mac_hdr, session, + result_code, reason_code, &mgmt_event); + pe_debug("RX frame: type:%d sub_type:%d seq_num:%d ssid:%.*s selfmacaddr:"QDF_MAC_ADDR_FMT" bssid:"QDF_MAC_ADDR_FMT" channel:%d", + mgmt_event.mgmt_type, mgmt_event.mgmt_subtype, + ((mac_hdr->seqControl.seqNumHi << 4) | + mac_hdr->seqControl.seqNumLo), + mgmt_event.ssid_len, mgmt_event.ssid, + QDF_MAC_ADDR_REF(mgmt_event.self_mac_addr), + QDF_MAC_ADDR_REF(mgmt_event.bssid), + mgmt_event.operating_channel); + WLAN_HOST_DIAG_EVENT_REPORT(&mgmt_event, EVENT_WLAN_HOST_MGMT_RX_V2); +} +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +/* Returns length of P2P stream and Pointer ie passed to this function is filled with noa stream */ + +uint8_t lim_build_p2p_ie(struct mac_context *mac, uint8_t *ie, uint8_t *data, + uint8_t ie_len) +{ + int length = 0; + uint8_t *ptr = ie; + + ptr[length++] = WLAN_ELEMID_VENDOR; + ptr[length++] = ie_len + SIR_MAC_P2P_OUI_SIZE; + qdf_mem_copy(&ptr[length], SIR_MAC_P2P_OUI, SIR_MAC_P2P_OUI_SIZE); + qdf_mem_copy(&ptr[length + SIR_MAC_P2P_OUI_SIZE], data, ie_len); + return ie_len + SIR_P2P_IE_HEADER_LEN; +} + +/* Returns length of NoA stream and Pointer pNoaStream passed to this function is filled with noa stream */ +uint8_t lim_get_noa_attr_stream(struct mac_context *mac, uint8_t *pNoaStream, + struct pe_session *pe_session) +{ + uint8_t len = 0; + + uint8_t *pBody = pNoaStream; + + if ((pe_session) && (pe_session->valid) && + (pe_session->opmode == QDF_P2P_GO_MODE)) { + if ((!(pe_session->p2pGoPsUpdate.uNoa1Duration)) + && (!(pe_session->p2pGoPsUpdate.uNoa2Duration)) + && (!pe_session->p2pGoPsUpdate.oppPsFlag) + ) + return 0; /* No NoA Descriptor then return 0 */ + + pBody[0] = SIR_P2P_NOA_ATTR; + + pBody[3] = pe_session->p2pGoPsUpdate.index; + pBody[4] = + pe_session->p2pGoPsUpdate.ctWin | (pe_session-> + p2pGoPsUpdate. + oppPsFlag << 7); + len = 5; + pBody += len; + + if (pe_session->p2pGoPsUpdate.uNoa1Duration) { + *pBody = pe_session->p2pGoPsUpdate.uNoa1IntervalCnt; + pBody += 1; + len += 1; + + *((uint32_t *) (pBody)) = + sir_swap_u32if_needed(pe_session->p2pGoPsUpdate. + uNoa1Duration); + pBody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pBody)) = + sir_swap_u32if_needed(pe_session->p2pGoPsUpdate. + uNoa1Interval); + pBody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pBody)) = + sir_swap_u32if_needed(pe_session->p2pGoPsUpdate. + uNoa1StartTime); + pBody += sizeof(uint32_t); + len += 4; + + } + + if (pe_session->p2pGoPsUpdate.uNoa2Duration) { + *pBody = pe_session->p2pGoPsUpdate.uNoa2IntervalCnt; + pBody += 1; + len += 1; + + *((uint32_t *) (pBody)) = + sir_swap_u32if_needed(pe_session->p2pGoPsUpdate. + uNoa2Duration); + pBody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pBody)) = + sir_swap_u32if_needed(pe_session->p2pGoPsUpdate. + uNoa2Interval); + pBody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pBody)) = + sir_swap_u32if_needed(pe_session->p2pGoPsUpdate. + uNoa2StartTime); + pBody += sizeof(uint32_t); + len += 4; + + } + + pBody = pNoaStream + 1; + *((uint16_t *) (pBody)) = sir_swap_u16if_needed(len - 3); /*one byte for Attr and 2 bytes for length */ + + return len; + + } + return 0; + +} + +void pe_set_resume_channel(struct mac_context *mac, uint16_t channel, + ePhyChanBondState phyCbState) +{ + + mac->lim.gResumeChannel = channel; + mac->lim.gResumePhyCbState = phyCbState; +} + +bool lim_isconnected_on_dfs_channel(struct mac_context *mac_ctx, + uint8_t currentChannel) +{ + if (CHANNEL_STATE_DFS == + wlan_reg_get_channel_state(mac_ctx->pdev, currentChannel)) { + return true; + } else { + return false; + } +} + +#ifdef WLAN_FEATURE_11W +void lim_pmf_sa_query_timer_handler(void *pMacGlobal, uint32_t param) +{ + struct mac_context *mac = (struct mac_context *) pMacGlobal; + tPmfSaQueryTimerId timerId; + struct pe_session *pe_session; + tpDphHashNode pSta; + uint8_t maxretries; + + pe_debug("SA Query timer fires"); + timerId.value = param; + + /* Check that SA Query is in progress */ + pe_session = pe_find_session_by_session_id(mac, + timerId.fields.sessionId); + if (!pe_session) { + pe_err("Session does not exist for given session ID: %d", + timerId.fields.sessionId); + return; + } + pSta = dph_get_hash_entry(mac, timerId.fields.peerIdx, + &pe_session->dph.dphHashTable); + if (!pSta) { + pe_err("Entry does not exist for given peer index: %d", + timerId.fields.peerIdx); + return; + } + if (DPH_SA_QUERY_IN_PROGRESS != pSta->pmfSaQueryState) + return; + + /* Increment the retry count, check if reached maximum */ + maxretries = mac->mlme_cfg->gen.pmf_sa_query_max_retries; + pSta->pmfSaQueryRetryCount++; + if (pSta->pmfSaQueryRetryCount >= maxretries) { + pe_err("SA Query timed out,Deleting STA"); + lim_print_mac_addr(mac, pSta->staAddr, LOGE); + lim_send_disassoc_mgmt_frame(mac, + eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON, + pSta->staAddr, pe_session, false); + lim_trigger_sta_deletion(mac, pSta, pe_session); + pSta->pmfSaQueryState = DPH_SA_QUERY_TIMED_OUT; + return; + } + /* Retry SA Query */ + lim_send_sa_query_request_frame(mac, + (uint8_t *) &(pSta-> + pmfSaQueryCurrentTransId), + pSta->staAddr, pe_session); + pSta->pmfSaQueryCurrentTransId++; + pe_debug("Starting SA Query retry: %d", pSta->pmfSaQueryRetryCount); + if (tx_timer_activate(&pSta->pmfSaQueryTimer) != TX_SUCCESS) { + pe_err("PMF SA Query timer activation failed!"); + pSta->pmfSaQueryState = DPH_SA_QUERY_NOT_IN_PROGRESS; + } +} +#endif + +bool lim_check_vht_op_mode_change(struct mac_context *mac, + struct pe_session *pe_session, + uint8_t chanWidth, uint8_t *peerMac) +{ + tUpdateVHTOpMode tempParam; + + tempParam.opMode = chanWidth; + tempParam.smesessionId = pe_session->smeSessionId; + qdf_mem_copy(tempParam.peer_mac, peerMac, sizeof(tSirMacAddr)); + + lim_send_mode_update(mac, &tempParam, pe_session); + + return true; +} + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +bool lim_send_he_ie_update(struct mac_context *mac_ctx, struct pe_session *pe_session) +{ + QDF_STATUS status; + + status = wma_update_he_ops_ie(cds_get_context(QDF_MODULE_ID_WMA), + pe_session->smeSessionId, + &pe_session->he_op); + if (QDF_IS_STATUS_ERROR(status)) + return false; + + return true; +} +#endif + +bool lim_set_nss_change(struct mac_context *mac, struct pe_session *pe_session, + uint8_t rxNss, uint8_t *peerMac) +{ + tUpdateRxNss tempParam; + + if (!rxNss) { + pe_err("Invalid rxNss value: %u", rxNss); + cds_trigger_recovery(QDF_REASON_UNSPECIFIED); + } + + tempParam.rxNss = rxNss; + tempParam.smesessionId = pe_session->smeSessionId; + qdf_mem_copy(tempParam.peer_mac, peerMac, sizeof(tSirMacAddr)); + + lim_send_rx_nss_update(mac, &tempParam, pe_session); + + return true; +} + +bool lim_check_membership_user_position(struct mac_context *mac, + struct pe_session *pe_session, + uint32_t membership, + uint32_t userPosition) +{ + tUpdateMembership tempParamMembership; + tUpdateUserPos tempParamUserPosition; + + tempParamMembership.membership = membership; + tempParamMembership.smesessionId = pe_session->smeSessionId; + qdf_mem_copy(tempParamMembership.peer_mac, pe_session->bssId, + sizeof(tSirMacAddr)); + + lim_set_membership(mac, &tempParamMembership, pe_session); + + tempParamUserPosition.userPos = userPosition; + tempParamUserPosition.smesessionId = pe_session->smeSessionId; + qdf_mem_copy(tempParamUserPosition.peer_mac, pe_session->bssId, + sizeof(tSirMacAddr)); + + lim_set_user_pos(mac, &tempParamUserPosition, pe_session); + + return true; +} + +void lim_get_short_slot_from_phy_mode(struct mac_context *mac, struct pe_session *pe_session, + uint32_t phyMode, uint8_t *pShortSlotEnabled) +{ + uint8_t val = 0; + + /* only 2.4G band should have short slot enable, rest it should be default */ + if (phyMode == WNI_CFG_PHY_MODE_11G) { + /* short slot is default in all other modes */ + if ((pe_session->opmode == QDF_SAP_MODE) || + (pe_session->opmode == QDF_IBSS_MODE) || + (pe_session->opmode == QDF_P2P_GO_MODE)) { + val = true; + } + /* Program Polaris based on AP capability */ + if (pe_session->limMlmState == eLIM_MLM_WT_JOIN_BEACON_STATE) { + /* Joining BSS. */ + val = + SIR_MAC_GET_SHORT_SLOT_TIME(pe_session-> + limCurrentBssCaps); + } else if (pe_session->limMlmState == + eLIM_MLM_WT_REASSOC_RSP_STATE) { + /* Reassociating with AP. */ + val = + SIR_MAC_GET_SHORT_SLOT_TIME(pe_session-> + limReassocBssCaps); + } + } else { + /* + * 11B does not short slot and short slot is default + * for 11A mode. Hence, not need to set this bit + */ + val = false; + } + + pe_debug("phyMode: %u shortslotsupported: %u", phyMode, val); + *pShortSlotEnabled = val; +} + +#ifdef WLAN_FEATURE_11W +/** + * + * \brief This function is called by various LIM modules to correctly set + * the Protected bit in the Frame Control Field of the 802.11 frame MAC header + * + * + * \param mac Pointer to Global MAC structure + * + * \param pe_session Pointer to session corresponding to the connection + * + * \param peer Peer address of the STA to which the frame is to be sent + * + * \param pMacHdr Pointer to the frame MAC header + * + * \return nothing + * + * + */ +void +lim_set_protected_bit(struct mac_context *mac, + struct pe_session *pe_session, + tSirMacAddr peer, tpSirMacMgmtHdr pMacHdr) +{ + uint16_t aid; + tpDphHashNode sta; + + sta = dph_lookup_hash_entry(mac, peer, &aid, + &pe_session->dph.dphHashTable); + if (sta) { + /* rmfenabled will be set at the time of addbss. + * but sometimes EAP auth fails and keys are not + * installed then if we send any management frame + * like deauth/disassoc with this bit set then + * firmware crashes. so check for keys are + * installed or not also before setting the bit + */ + if (sta->rmfEnabled && sta->is_key_installed) + pMacHdr->fc.wep = 1; + + pe_debug("wep:%d rmf:%d is_key_set:%d", pMacHdr->fc.wep, + sta->rmfEnabled, sta->is_key_installed); + } +} /*** end lim_set_protected_bit() ***/ +#endif + +void lim_set_ht_caps(struct mac_context *p_mac, struct pe_session *p_session_entry, + uint8_t *p_ie_start, uint32_t num_bytes) +{ + const uint8_t *p_ie = NULL; + tDot11fIEHTCaps dot11_ht_cap = {0,}; + + populate_dot11f_ht_caps(p_mac, p_session_entry, &dot11_ht_cap); + p_ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_HTCAPS, + p_ie_start, num_bytes); + pe_debug("p_ie: %pK dot11_ht_cap.supportedMCSSet[0]: 0x%x", + p_ie, dot11_ht_cap.supportedMCSSet[0]); + if (p_ie) { + /* convert from unpacked to packed structure */ + tHtCaps *p_ht_cap = (tHtCaps *) &p_ie[2]; + + p_ht_cap->advCodingCap = dot11_ht_cap.advCodingCap; + p_ht_cap->supportedChannelWidthSet = + dot11_ht_cap.supportedChannelWidthSet; + p_ht_cap->mimoPowerSave = dot11_ht_cap.mimoPowerSave; + p_ht_cap->greenField = dot11_ht_cap.greenField; + p_ht_cap->shortGI20MHz = dot11_ht_cap.shortGI20MHz; + p_ht_cap->shortGI40MHz = dot11_ht_cap.shortGI40MHz; + p_ht_cap->txSTBC = dot11_ht_cap.txSTBC; + p_ht_cap->rxSTBC = dot11_ht_cap.rxSTBC; + p_ht_cap->delayedBA = dot11_ht_cap.delayedBA; + p_ht_cap->maximalAMSDUsize = dot11_ht_cap.maximalAMSDUsize; + p_ht_cap->dsssCckMode40MHz = dot11_ht_cap.dsssCckMode40MHz; + p_ht_cap->psmp = dot11_ht_cap.psmp; + p_ht_cap->stbcControlFrame = dot11_ht_cap.stbcControlFrame; + p_ht_cap->lsigTXOPProtection = dot11_ht_cap.lsigTXOPProtection; + p_ht_cap->maxRxAMPDUFactor = dot11_ht_cap.maxRxAMPDUFactor; + p_ht_cap->mpduDensity = dot11_ht_cap.mpduDensity; + qdf_mem_copy((void *)p_ht_cap->supportedMCSSet, + (void *)(dot11_ht_cap.supportedMCSSet), + sizeof(p_ht_cap->supportedMCSSet)); + p_ht_cap->pco = dot11_ht_cap.pco; + p_ht_cap->transitionTime = dot11_ht_cap.transitionTime; + p_ht_cap->mcsFeedback = dot11_ht_cap.mcsFeedback; + p_ht_cap->txBF = dot11_ht_cap.txBF; + p_ht_cap->rxStaggeredSounding = + dot11_ht_cap.rxStaggeredSounding; + p_ht_cap->txStaggeredSounding = + dot11_ht_cap.txStaggeredSounding; + p_ht_cap->rxZLF = dot11_ht_cap.rxZLF; + p_ht_cap->txZLF = dot11_ht_cap.txZLF; + p_ht_cap->implicitTxBF = dot11_ht_cap.implicitTxBF; + p_ht_cap->calibration = dot11_ht_cap.calibration; + p_ht_cap->explicitCSITxBF = dot11_ht_cap.explicitCSITxBF; + p_ht_cap->explicitUncompressedSteeringMatrix = + dot11_ht_cap.explicitUncompressedSteeringMatrix; + p_ht_cap->explicitBFCSIFeedback = + dot11_ht_cap.explicitBFCSIFeedback; + p_ht_cap->explicitUncompressedSteeringMatrixFeedback = + dot11_ht_cap.explicitUncompressedSteeringMatrixFeedback; + p_ht_cap->explicitCompressedSteeringMatrixFeedback = + dot11_ht_cap.explicitCompressedSteeringMatrixFeedback; + p_ht_cap->csiNumBFAntennae = dot11_ht_cap.csiNumBFAntennae; + p_ht_cap->uncompressedSteeringMatrixBFAntennae = + dot11_ht_cap.uncompressedSteeringMatrixBFAntennae; + p_ht_cap->compressedSteeringMatrixBFAntennae = + dot11_ht_cap.compressedSteeringMatrixBFAntennae; + p_ht_cap->antennaSelection = dot11_ht_cap.antennaSelection; + p_ht_cap->explicitCSIFeedbackTx = + dot11_ht_cap.explicitCSIFeedbackTx; + p_ht_cap->antennaIndicesFeedbackTx = + dot11_ht_cap.antennaIndicesFeedbackTx; + p_ht_cap->explicitCSIFeedback = + dot11_ht_cap.explicitCSIFeedback; + p_ht_cap->antennaIndicesFeedback = + dot11_ht_cap.antennaIndicesFeedback; + p_ht_cap->rxAS = dot11_ht_cap.rxAS; + p_ht_cap->txSoundingPPDUs = dot11_ht_cap.txSoundingPPDUs; + } +} + +void lim_set_vht_caps(struct mac_context *p_mac, struct pe_session *p_session_entry, + uint8_t *p_ie_start, uint32_t num_bytes) +{ + const uint8_t *p_ie = NULL; + tDot11fIEVHTCaps dot11_vht_cap; + + populate_dot11f_vht_caps(p_mac, p_session_entry, &dot11_vht_cap); + p_ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_VHTCAPS, p_ie_start, + num_bytes); + if (p_ie) { + tSirMacVHTCapabilityInfo *vht_cap = + (tSirMacVHTCapabilityInfo *) &p_ie[2]; + tSirVhtMcsInfo *vht_mcs = (tSirVhtMcsInfo *) &p_ie[2 + + sizeof(tSirMacVHTCapabilityInfo)]; + + union { + uint16_t u_value; + tSirMacVHTRxSupDataRateInfo vht_rx_supp_rate; + tSirMacVHTTxSupDataRateInfo vht_tx_supp_rate; + } u_vht_data_rate_info; + + vht_cap->maxMPDULen = dot11_vht_cap.maxMPDULen; + vht_cap->supportedChannelWidthSet = + dot11_vht_cap.supportedChannelWidthSet; + vht_cap->ldpcCodingCap = dot11_vht_cap.ldpcCodingCap; + vht_cap->shortGI80MHz = dot11_vht_cap.shortGI80MHz; + vht_cap->shortGI160and80plus80MHz = + dot11_vht_cap.shortGI160and80plus80MHz; + vht_cap->txSTBC = dot11_vht_cap.txSTBC; + vht_cap->rxSTBC = dot11_vht_cap.rxSTBC; + vht_cap->suBeamFormerCap = dot11_vht_cap.suBeamFormerCap; + vht_cap->suBeamformeeCap = dot11_vht_cap.suBeamformeeCap; + vht_cap->csnofBeamformerAntSup = + dot11_vht_cap.csnofBeamformerAntSup; + vht_cap->numSoundingDim = dot11_vht_cap.numSoundingDim; + vht_cap->muBeamformerCap = dot11_vht_cap.muBeamformerCap; + vht_cap->muBeamformeeCap = dot11_vht_cap.muBeamformeeCap; + vht_cap->vhtTXOPPS = dot11_vht_cap.vhtTXOPPS; + vht_cap->htcVHTCap = dot11_vht_cap.htcVHTCap; + vht_cap->maxAMPDULenExp = dot11_vht_cap.maxAMPDULenExp; + vht_cap->vhtLinkAdaptCap = dot11_vht_cap.vhtLinkAdaptCap; + vht_cap->rxAntPattern = dot11_vht_cap.rxAntPattern; + vht_cap->txAntPattern = dot11_vht_cap.txAntPattern; + vht_cap->extended_nss_bw_supp = + dot11_vht_cap.extended_nss_bw_supp; + + /* Populate VHT MCS Information */ + vht_mcs->rxMcsMap = dot11_vht_cap.rxMCSMap; + u_vht_data_rate_info.vht_rx_supp_rate.rxSupDataRate = + dot11_vht_cap.rxHighSupDataRate; + u_vht_data_rate_info.vht_rx_supp_rate.max_nsts_total = + dot11_vht_cap.max_nsts_total; + vht_mcs->rxHighest = u_vht_data_rate_info.u_value; + + vht_mcs->txMcsMap = dot11_vht_cap.txMCSMap; + u_vht_data_rate_info.vht_tx_supp_rate.txSupDataRate = + dot11_vht_cap.txSupDataRate; + u_vht_data_rate_info.vht_tx_supp_rate.vht_extended_nss_bw_cap = + dot11_vht_cap.vht_extended_nss_bw_cap; + vht_mcs->txHighest = u_vht_data_rate_info.u_value; + } +} + +/** + * lim_validate_received_frame_a1_addr() - To validate received frame's A1 addr + * @mac_ctx: pointer to mac context + * @a1: received frame's a1 address which is nothing but our self address + * @session: PE session pointer + * + * This routine will validate, A1 address of the received frame + * + * Return: true or false + */ +bool lim_validate_received_frame_a1_addr(struct mac_context *mac_ctx, + tSirMacAddr a1, struct pe_session *session) +{ + if (!mac_ctx || !session) { + pe_err("mac or session context is null"); + /* let main routine handle it */ + return true; + } + if (IEEE80211_IS_MULTICAST(a1) || QDF_IS_ADDR_BROADCAST(a1)) { + /* just for fail safe, don't handle MC/BC a1 in this routine */ + return true; + } + if (qdf_mem_cmp(a1, session->self_mac_addr, 6)) { + pe_err("Invalid A1 address in received frame"); + return false; + } + return true; +} + +/** + * lim_check_and_reset_protection_params() - reset protection related parameters + * + * @mac_ctx: pointer to global mac structure + * + * resets protection related global parameters if the pe active session count + * is zero. + * + * Return: None + */ +void lim_check_and_reset_protection_params(struct mac_context *mac_ctx) +{ + if (!pe_get_active_session_count(mac_ctx)) { + mac_ctx->lim.gHTOperMode = eSIR_HT_OP_MODE_PURE; + } +} + +/** + * lim_set_stads_rtt_cap() - update station node RTT capability + * @sta_ds: Station hash node + * @ext_cap: Pointer to extended capability + * @mac_ctx: global MAC context + * + * This funciton update hash node's RTT capability based on received + * Extended capability IE. + * + * Return: None + */ +void lim_set_stads_rtt_cap(tpDphHashNode sta_ds, struct s_ext_cap *ext_cap, + struct mac_context *mac_ctx) +{ + sta_ds->timingMeasCap = 0; + sta_ds->timingMeasCap |= (ext_cap->timing_meas) ? + RTT_TIMING_MEAS_CAPABILITY : + RTT_INVALID; + sta_ds->timingMeasCap |= (ext_cap->fine_time_meas_initiator) ? + RTT_FINE_TIME_MEAS_INITIATOR_CAPABILITY : + RTT_INVALID; + sta_ds->timingMeasCap |= (ext_cap->fine_time_meas_responder) ? + RTT_FINE_TIME_MEAS_RESPONDER_CAPABILITY : + RTT_INVALID; + + pe_debug("ExtCap present, timingMeas: %d Initiator: %d Responder: %d", + ext_cap->timing_meas, ext_cap->fine_time_meas_initiator, + ext_cap->fine_time_meas_responder); +} + +#ifdef WLAN_SUPPORT_TWT +void lim_set_peer_twt_cap(struct pe_session *session, struct s_ext_cap *ext_cap) +{ + if (session->enable_session_twt_support) { + session->peer_twt_requestor = ext_cap->twt_requestor_support; + session->peer_twt_responder = ext_cap->twt_responder_support; + } + + pe_debug("Ext Cap peer TWT requestor: %d, responder: %d, enable_twt %d", + ext_cap->twt_requestor_support, + ext_cap->twt_responder_support, + session->enable_session_twt_support); +} +#endif + +/** + * lim_send_ie() - sends IE to wma + * @mac_ctx: global MAC context + * @vdev_id: vdev_id + * @eid: IE id + * @band: band for which IE is intended + * @buf: buffer containing IE + * @len: length of buffer + * + * This funciton sends the IE data to WMA. + * + * Return: status of operation + */ +static QDF_STATUS lim_send_ie(struct mac_context *mac_ctx, uint32_t vdev_id, + uint8_t eid, enum cds_band_type band, + uint8_t *buf, uint32_t len) +{ + struct vdev_ie_info *ie_msg; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + /* Allocate memory for the WMI request */ + ie_msg = qdf_mem_malloc(sizeof(*ie_msg) + len); + if (!ie_msg) + return QDF_STATUS_E_NOMEM; + + ie_msg->vdev_id = vdev_id; + ie_msg->ie_id = eid; + ie_msg->length = len; + ie_msg->band = band; + /* IE data buffer starts at end of the struct */ + ie_msg->data = (uint8_t *)&ie_msg[1]; + + qdf_mem_copy(ie_msg->data, buf, len); + msg.type = WMA_SET_IE_INFO; + msg.bodyptr = ie_msg; + msg.reserved = 0; + + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (QDF_STATUS_SUCCESS != status) { + pe_err("Not able to post WMA_SET_IE_INFO to WMA"); + qdf_mem_free(ie_msg); + return status; + } + + return status; +} + +/** + * lim_get_rx_ldpc() - gets ldpc setting for given channel(band) + * @mac_ctx: global mac context + * @ch: channel enum for which ldpc setting is required + * Note: ch param is not absolute channel number rather it is + * channel number enum. + * + * Return: true if enabled and false otherwise + */ +static inline bool lim_get_rx_ldpc(struct mac_context *mac_ctx, + enum channel_enum ch) +{ + if (mac_ctx->mlme_cfg->ht_caps.ht_cap_info.adv_coding_cap && + wma_is_rx_ldpc_supported_for_channel(wlan_reg_ch_to_freq(ch))) + return true; + else + return false; +} + +/** + * lim_populate_mcs_set_ht_per_vdev() - update the MCS set according to vdev nss + * @mac_ctx: global mac context + * @ht_cap: pointer to ht caps + * @vdev_id: vdev for which IE is targeted + * @band: band for which the MCS set has to be updated + * + * This function updates the MCS set according to vdev nss + * + * Return: None + */ +static void lim_populate_mcs_set_ht_per_vdev(struct mac_context *mac_ctx, + struct sHtCaps *ht_cap, + uint8_t vdev_id, + uint8_t band) +{ + struct wlan_mlme_nss_chains *nss_chains_ini_cfg; + struct wlan_objmgr_vdev *vdev = + wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + vdev_id, + WLAN_MLME_SB_ID); + if (!vdev) { + pe_err("Got NULL vdev obj, returning"); + return; + } + if (!ht_cap->supportedMCSSet[1]) + goto end; + nss_chains_ini_cfg = mlme_get_ini_vdev_config(vdev); + if (!nss_chains_ini_cfg) { + pe_err("nss chain dynamic config NULL"); + goto end; + } + + /* convert from unpacked to packed structure */ + if (nss_chains_ini_cfg->rx_nss[band] == 1) + ht_cap->supportedMCSSet[1] = 0; + +end: + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID); +} + +/** + * lim_populate_mcs_set_vht_per_vdev() - update MCS set according to vdev nss + * @mac_ctx: global mac context + * @vht_caps: pointer to vht_caps + * @vdev_id: vdev for which IE is targeted + * @band: band for which the MCS set has to be updated + * + * This function updates the MCS set according to vdev nss + * + * Return: None + */ +static void lim_populate_mcs_set_vht_per_vdev(struct mac_context *mac_ctx, + uint8_t *vht_caps, + uint8_t vdev_id, + uint8_t band) +{ + struct wlan_mlme_nss_chains *nss_chains_ini_cfg; + tSirVhtMcsInfo *vht_mcs; + struct wlan_objmgr_vdev *vdev = + wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + vdev_id, + WLAN_MLME_SB_ID); + if (!vdev) { + pe_err("Got NULL vdev obj, returning"); + return; + } + + nss_chains_ini_cfg = mlme_get_ini_vdev_config(vdev); + if (!nss_chains_ini_cfg) { + pe_err("nss chain dynamic config NULL"); + goto end; + } + + vht_mcs = (tSirVhtMcsInfo *)&vht_caps[2 + + sizeof(tSirMacVHTCapabilityInfo)]; + if (nss_chains_ini_cfg->tx_nss[band] == 1) { + /* Populate VHT MCS Information */ + vht_mcs->txMcsMap |= DISABLE_NSS2_MCS; + vht_mcs->txHighest = + VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + } + + if (nss_chains_ini_cfg->rx_nss[band] == 1) { + /* Populate VHT MCS Information */ + vht_mcs->rxMcsMap |= DISABLE_NSS2_MCS; + vht_mcs->rxHighest = + VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + } + +end: + wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID); +} + +/** + * is_dot11mode_support_ht_cap() - Check dot11mode supports HT capability + * @dot11mode: dot11mode + * + * This function checks whether dot11mode support HT capability or not + * + * Return: True, if supports. False otherwise + */ +static bool is_dot11mode_support_ht_cap(enum csr_cfgdot11mode dot11mode) +{ + if ((dot11mode == eCSR_CFG_DOT11_MODE_AUTO) || + (dot11mode == eCSR_CFG_DOT11_MODE_11N) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AC) || + (dot11mode == eCSR_CFG_DOT11_MODE_11N_ONLY) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AC_ONLY) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AX) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AX_ONLY)) { + return true; + } + + return false; +} + +/** + * is_dot11mode_support_vht_cap() - Check dot11mode supports VHT capability + * @dot11mode: dot11mode + * + * This function checks whether dot11mode support VHT capability or not + * + * Return: True, if supports. False otherwise + */ +static bool is_dot11mode_support_vht_cap(enum csr_cfgdot11mode dot11mode) +{ + if ((dot11mode == eCSR_CFG_DOT11_MODE_AUTO) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AC) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AC_ONLY) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AX) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AX_ONLY)) { + return true; + } + + return false; +} + +/** + * is_dot11mode_support_he_cap() - Check dot11mode supports HE capability + * @dot11mode: dot11mode + * + * This function checks whether dot11mode support HE capability or not + * + * Return: True, if supports. False otherwise + */ +static bool is_dot11mode_support_he_cap(enum csr_cfgdot11mode dot11mode) +{ + if ((dot11mode == eCSR_CFG_DOT11_MODE_AUTO) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AX) || + (dot11mode == eCSR_CFG_DOT11_MODE_11AX_ONLY)) { + return true; + } + + return false; +} + +/** + * lim_send_ht_caps_ie() - gets HT capability and send to firmware via wma + * @mac_ctx: global mac context + * @session: pe session. This can be NULL. In that case self cap will be sent + * @device_mode: VDEV op mode + * @vdev_id: vdev for which IE is targeted + * + * This function gets HT capability and send to firmware via wma + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_send_ht_caps_ie(struct mac_context *mac_ctx, + struct pe_session *session, + enum QDF_OPMODE device_mode, + uint8_t vdev_id) +{ + uint8_t ht_caps[DOT11F_IE_HTCAPS_MIN_LEN + 2] = {0}; + tHtCaps *p_ht_cap = (tHtCaps *)(&ht_caps[2]); + QDF_STATUS status_5g, status_2g; + + ht_caps[0] = DOT11F_EID_HTCAPS; + ht_caps[1] = DOT11F_IE_HTCAPS_MIN_LEN; + lim_set_ht_caps(mac_ctx, session, ht_caps, + DOT11F_IE_HTCAPS_MIN_LEN + 2); + /* Get LDPC and over write for 2G */ + p_ht_cap->advCodingCap = lim_get_rx_ldpc(mac_ctx, + CHAN_ENUM_2437); + /* Get self cap for HT40 support in 2G */ + if (mac_ctx->roam.configParam.channelBondingMode24GHz) { + p_ht_cap->supportedChannelWidthSet = 1; + p_ht_cap->shortGI40MHz = 1; + } else { + p_ht_cap->supportedChannelWidthSet = 0; + p_ht_cap->shortGI40MHz = 0; + } + + lim_populate_mcs_set_ht_per_vdev(mac_ctx, p_ht_cap, vdev_id, + NSS_CHAINS_BAND_2GHZ); + if(device_mode == QDF_NDI_MODE) { + p_ht_cap->txBF = 0; + p_ht_cap->implicitTxBF = 0; + p_ht_cap->explicitCSITxBF = 0; + } + + status_2g = lim_send_ie(mac_ctx, vdev_id, DOT11F_EID_HTCAPS, + CDS_BAND_2GHZ, &ht_caps[2], + DOT11F_IE_HTCAPS_MIN_LEN); + /* + * Get LDPC and over write for 5G - using channel 64 because it + * is available in all reg domains. + */ + p_ht_cap->advCodingCap = lim_get_rx_ldpc(mac_ctx, CHAN_ENUM_5320); + /* Get self cap for HT40 support in 5G */ + if (mac_ctx->roam.configParam.channelBondingMode5GHz) { + p_ht_cap->supportedChannelWidthSet = 1; + p_ht_cap->shortGI40MHz = 1; + } else { + p_ht_cap->supportedChannelWidthSet = 0; + p_ht_cap->shortGI40MHz = 0; + } + lim_populate_mcs_set_ht_per_vdev(mac_ctx, p_ht_cap, vdev_id, + NSS_CHAINS_BAND_5GHZ); + status_5g = lim_send_ie(mac_ctx, vdev_id, DOT11F_EID_HTCAPS, + CDS_BAND_5GHZ, &ht_caps[2], + DOT11F_IE_HTCAPS_MIN_LEN); + + if (QDF_IS_STATUS_SUCCESS(status_2g) && + QDF_IS_STATUS_SUCCESS(status_5g)) + return QDF_STATUS_SUCCESS; + + return QDF_STATUS_E_FAILURE; +} + +/** + * lim_send_vht_caps_ie() - gets VHT capability and send to firmware via wma + * @mac_ctx: global mac context + * @session: pe session. This can be NULL. In that case self cap will be sent + * @device_mode: VDEV op mode + * @vdev_id: vdev for which IE is targeted + * + * This function gets VHT capability and send to firmware via wma + * + * Return: QDF_STATUS + */ +static QDF_STATUS lim_send_vht_caps_ie(struct mac_context *mac_ctx, + struct pe_session *session, + enum QDF_OPMODE device_mode, + uint8_t vdev_id) +{ + uint8_t vht_caps[DOT11F_IE_VHTCAPS_MAX_LEN + 2] = {0}; + tSirMacVHTCapabilityInfo *p_vht_cap = + (tSirMacVHTCapabilityInfo *)(&vht_caps[2]); + QDF_STATUS status_5g, status_2g; + + vht_caps[0] = DOT11F_EID_VHTCAPS; + vht_caps[1] = DOT11F_IE_VHTCAPS_MAX_LEN; + lim_set_vht_caps(mac_ctx, session, vht_caps, + DOT11F_IE_VHTCAPS_MIN_LEN + 2); + /* + * Get LDPC and over write for 5G - using channel 64 because it + * is available in all reg domains. + */ + p_vht_cap->ldpcCodingCap = lim_get_rx_ldpc(mac_ctx, CHAN_ENUM_5320); + lim_populate_mcs_set_vht_per_vdev(mac_ctx, vht_caps, vdev_id, + NSS_CHAINS_BAND_5GHZ); + + if (device_mode == QDF_NDI_MODE) { + p_vht_cap->muBeamformeeCap = 0; + p_vht_cap->muBeamformerCap = 0; + p_vht_cap->suBeamformeeCap = 0; + p_vht_cap->suBeamFormerCap = 0; + } + /* + * Self VHT channel width for 5G is already negotiated + * with FW + */ + status_5g = lim_send_ie(mac_ctx, vdev_id, DOT11F_EID_VHTCAPS, + CDS_BAND_5GHZ, &vht_caps[2], + DOT11F_IE_VHTCAPS_MIN_LEN); + + /* Get LDPC and over write for 2G */ + p_vht_cap->ldpcCodingCap = lim_get_rx_ldpc(mac_ctx, CHAN_ENUM_2437); + /* Self VHT 80/160/80+80 channel width for 2G is 0 */ + p_vht_cap->supportedChannelWidthSet = 0; + p_vht_cap->shortGI80MHz = 0; + p_vht_cap->shortGI160and80plus80MHz = 0; + lim_populate_mcs_set_vht_per_vdev(mac_ctx, vht_caps, vdev_id, + NSS_CHAINS_BAND_2GHZ); + status_2g = lim_send_ie(mac_ctx, vdev_id, DOT11F_EID_VHTCAPS, + CDS_BAND_2GHZ, &vht_caps[2], + DOT11F_IE_VHTCAPS_MIN_LEN); + + if (QDF_IS_STATUS_SUCCESS(status_2g) && + QDF_IS_STATUS_SUCCESS(status_5g)) + return QDF_STATUS_SUCCESS; + + return QDF_STATUS_E_FAILURE; +} + +QDF_STATUS lim_send_ies_per_band(struct mac_context *mac_ctx, + struct pe_session *session, uint8_t vdev_id, + enum csr_cfgdot11mode dot11_mode, + enum QDF_OPMODE device_mode) +{ + QDF_STATUS status_ht = QDF_STATUS_SUCCESS; + QDF_STATUS status_vht = QDF_STATUS_SUCCESS; + QDF_STATUS status_he = QDF_STATUS_SUCCESS; + + /* + * Note: Do not use Dot11f VHT structure, since 1 byte present flag in + * it is causing weird padding errors. Instead use Sir Mac VHT struct + * to send IE to wma. + */ + if (is_dot11mode_support_ht_cap(dot11_mode)) + status_ht = lim_send_ht_caps_ie(mac_ctx, session, + device_mode, vdev_id); + + if (is_dot11mode_support_vht_cap(dot11_mode)) + status_vht = lim_send_vht_caps_ie(mac_ctx, session, + device_mode, vdev_id); + + if (is_dot11mode_support_he_cap(dot11_mode)) { + status_he = lim_send_he_caps_ie(mac_ctx, session, + device_mode, vdev_id); + + if (QDF_IS_STATUS_SUCCESS(status_he)) + status_he = lim_send_he_6g_band_caps_ie(mac_ctx, + session, + vdev_id); + } + + if (QDF_IS_STATUS_SUCCESS(status_ht) && + QDF_IS_STATUS_SUCCESS(status_vht) && + QDF_IS_STATUS_SUCCESS(status_he)) + return QDF_STATUS_SUCCESS; + + return QDF_STATUS_E_FAILURE; +} + +/** + * lim_send_ext_cap_ie() - send ext cap IE to FW + * @mac_ctx: global MAC context + * @session_entry: PE session + * @extra_extcap: extracted ext cap + * @merge: merge extra ext cap + * + * This function is invoked after VDEV is created to update firmware + * about the extended capabilities that the corresponding VDEV is capable + * of. Since STA/SAP can have different Extended capabilities set, this function + * is called per vdev creation. + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_send_ext_cap_ie(struct mac_context *mac_ctx, + uint32_t session_id, + tDot11fIEExtCap *extra_extcap, bool merge) +{ + tDot11fIEExtCap ext_cap_data = {0}; + uint32_t dot11mode, num_bytes; + bool vht_enabled = false; + struct vdev_ie_info *vdev_ie; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + dot11mode = mac_ctx->mlme_cfg->dot11_mode.dot11_mode; + if (IS_DOT11_MODE_VHT(dot11mode)) + vht_enabled = true; + + status = populate_dot11f_ext_cap(mac_ctx, vht_enabled, &ext_cap_data, + NULL); + if (QDF_STATUS_SUCCESS != status) { + pe_err("Failed to populate ext cap IE"); + return QDF_STATUS_E_FAILURE; + } + + num_bytes = ext_cap_data.num_bytes; + + if (merge && extra_extcap && extra_extcap->num_bytes > 0) { + if (extra_extcap->num_bytes > ext_cap_data.num_bytes) + num_bytes = extra_extcap->num_bytes; + lim_merge_extcap_struct(&ext_cap_data, extra_extcap, true); + } + + /* Allocate memory for the WMI request, and copy the parameter */ + vdev_ie = qdf_mem_malloc(sizeof(*vdev_ie) + num_bytes); + if (!vdev_ie) + return QDF_STATUS_E_NOMEM; + + vdev_ie->vdev_id = session_id; + vdev_ie->ie_id = DOT11F_EID_EXTCAP; + vdev_ie->length = num_bytes; + vdev_ie->band = 0; + + vdev_ie->data = (uint8_t *)vdev_ie + sizeof(*vdev_ie); + qdf_mem_copy(vdev_ie->data, ext_cap_data.bytes, num_bytes); + + msg.type = WMA_SET_IE_INFO; + msg.bodyptr = vdev_ie; + msg.reserved = 0; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + pe_err("Not able to post WMA_SET_IE_INFO to WDA"); + qdf_mem_free(vdev_ie); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_strip_ie() - strip requested IE from IE buffer + * @mac_ctx: global MAC context + * @addn_ie: Additional IE buffer + * @addn_ielen: Length of additional IE + * @eid: EID of IE to strip + * @size_of_len_field: length of IE length field + * @oui: if present matches OUI also + * @oui_length: if previous present, this is length of oui + * @extracted_ie: if not NULL, copy the stripped IE to this buffer + * + * This utility function is used to strip of the requested IE if present + * in IE buffer. + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_strip_ie(struct mac_context *mac_ctx, + uint8_t *addn_ie, uint16_t *addn_ielen, + uint8_t eid, eSizeOfLenField size_of_len_field, + uint8_t *oui, uint8_t oui_length, uint8_t *extracted_ie, + uint32_t eid_max_len) +{ + uint8_t *tempbuf = NULL; + uint16_t templen = 0; + int left = *addn_ielen; + uint8_t *ptr = addn_ie; + uint8_t elem_id; + uint16_t elem_len, ie_len, extracted_ie_len = 0; + + if (!addn_ie) { + pe_debug("NULL addn_ie pointer"); + return QDF_STATUS_E_INVAL; + } + + tempbuf = qdf_mem_malloc(left); + if (!tempbuf) + return QDF_STATUS_E_NOMEM; + + if (extracted_ie) + qdf_mem_zero(extracted_ie, eid_max_len + size_of_len_field + 1); + + while (left >= 2) { + elem_id = ptr[0]; + left -= 1; + if (size_of_len_field == TWO_BYTE) { + elem_len = *((uint16_t *)&ptr[1]); + left -= 2; + } else { + elem_len = ptr[1]; + left -= 1; + } + if (elem_len > left) { + pe_err("Invalid IEs eid: %d elem_len: %d left: %d", + elem_id, elem_len, left); + qdf_mem_free(tempbuf); + return QDF_STATUS_E_FAILURE; + } + + if (eid != elem_id || + (oui && qdf_mem_cmp(oui, + &ptr[size_of_len_field + 1], + oui_length))) { + qdf_mem_copy(tempbuf + templen, &ptr[0], + elem_len + size_of_len_field + 1); + templen += (elem_len + size_of_len_field + 1); + } else { + /* + * eid matched and if provided OUI also matched + * take oui IE and store in provided buffer. + */ + if (extracted_ie) { + ie_len = elem_len + size_of_len_field + 1; + if (ie_len <= eid_max_len - extracted_ie_len) { + qdf_mem_copy( + extracted_ie + extracted_ie_len, + &ptr[0], ie_len); + extracted_ie_len += ie_len; + } + } + } + left -= elem_len; + ptr += (elem_len + size_of_len_field + 1); + } + qdf_mem_copy(addn_ie, tempbuf, templen); + + *addn_ielen = templen; + qdf_mem_free(tempbuf); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_11W +void lim_del_pmf_sa_query_timer(struct mac_context *mac_ctx, struct pe_session *pe_session) +{ + uint32_t associated_sta; + tpDphHashNode sta_ds = NULL; + + for (associated_sta = 1; + associated_sta < + mac_ctx->mlme_cfg->sap_cfg.assoc_sta_limit; + associated_sta++) { + sta_ds = dph_get_hash_entry(mac_ctx, associated_sta, + &pe_session->dph.dphHashTable); + if (!sta_ds) + continue; + if (!sta_ds->rmfEnabled) { + pe_debug("no PMF timer for assoc-id:%d sta mac" + QDF_MAC_ADDR_FMT, sta_ds->assocId, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + continue; + } + + pe_debug("Deleting pmfSaQueryTimer for assoc-id:%d sta mac" + QDF_MAC_ADDR_FMT, sta_ds->assocId, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + tx_timer_deactivate(&sta_ds->pmfSaQueryTimer); + tx_timer_delete(&sta_ds->pmfSaQueryTimer); + } +} +#endif + +QDF_STATUS lim_strip_supp_op_class_update_struct(struct mac_context *mac_ctx, + uint8_t *addn_ie, uint16_t *addn_ielen, + tDot11fIESuppOperatingClasses *dst) +{ + uint8_t extracted_buff[DOT11F_IE_SUPPOPERATINGCLASSES_MAX_LEN + 2]; + QDF_STATUS status; + + qdf_mem_zero((uint8_t *)&extracted_buff[0], + DOT11F_IE_SUPPOPERATINGCLASSES_MAX_LEN + 2); + status = lim_strip_ie(mac_ctx, addn_ie, addn_ielen, + DOT11F_EID_SUPPOPERATINGCLASSES, ONE_BYTE, + NULL, 0, extracted_buff, + DOT11F_IE_SUPPOPERATINGCLASSES_MAX_LEN); + if (QDF_STATUS_SUCCESS != status) { + pe_warn("Failed to strip supp_op_mode IE status: %d", + status); + return status; + } + + if (DOT11F_EID_SUPPOPERATINGCLASSES != extracted_buff[0] || + extracted_buff[1] > DOT11F_IE_SUPPOPERATINGCLASSES_MAX_LEN) { + pe_warn("Invalid IEs eid: %d elem_len: %d", + extracted_buff[0], extracted_buff[1]); + return QDF_STATUS_E_FAILURE; + } + + /* update the extracted supp op class to struct*/ + if (DOT11F_PARSE_SUCCESS != dot11f_unpack_ie_supp_operating_classes( + mac_ctx, &extracted_buff[2], extracted_buff[1], dst, false)) { + pe_err("dot11f_unpack Parse Error"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +uint8_t lim_op_class_from_bandwidth(struct mac_context *mac_ctx, + uint16_t channel_freq, + enum phy_ch_width ch_bandwidth, + enum offset_t offset) +{ + uint8_t op_class = 0; + uint16_t ch_behav_limit = BEHAV_NONE; + uint8_t channel; + + if (ch_bandwidth == CH_WIDTH_40MHZ && + wlan_reg_is_24ghz_ch_freq(channel_freq)) { + if (offset == BW40_LOW_PRIMARY) + ch_behav_limit = BEHAV_BW40_LOW_PRIMARY; + else + ch_behav_limit = BEHAV_BW40_HIGH_PRIMARY; + } else if (ch_bandwidth == CH_WIDTH_80P80MHZ) { + ch_behav_limit = BEHAV_BW80_PLUS; + } + wlan_reg_freq_width_to_chan_op_class_auto + (mac_ctx->pdev, channel_freq, + ch_width_in_mhz(ch_bandwidth), + true, BIT(ch_behav_limit), &op_class, + &channel); + + return op_class; +} + +/** + * lim_update_extcap_struct() - poputlate the dot11f structure + * @mac_ctx: global MAC context + * @buf: extracted IE buffer + * @dst: extended capability IE structure to be updated + * + * This function is used to update the extended capability structure + * with @buf. + * + * Return: None + */ +void lim_update_extcap_struct(struct mac_context *mac_ctx, + uint8_t *buf, tDot11fIEExtCap *dst) +{ + uint8_t out[DOT11F_IE_EXTCAP_MAX_LEN]; + uint32_t status; + + if (!buf) { + pe_err("Invalid Buffer Address"); + return; + } + + if (!dst) { + pe_err("NULL dst pointer"); + return; + } + + if (DOT11F_EID_EXTCAP != buf[0] || buf[1] > DOT11F_IE_EXTCAP_MAX_LEN) { + pe_debug_rl("Invalid IEs eid: %d elem_len: %d", buf[0], buf[1]); + return; + } + + qdf_mem_zero((uint8_t *)&out[0], DOT11F_IE_EXTCAP_MAX_LEN); + qdf_mem_copy(&out[0], &buf[2], buf[1]); + + status = dot11f_unpack_ie_ext_cap(mac_ctx, &out[0], + buf[1], dst, false); + if (DOT11F_PARSE_SUCCESS != status) + pe_err("dot11f_unpack Parse Error %d", status); +} + +/** + * lim_strip_extcap_update_struct - strip extended capability IE and populate + * the dot11f structure + * @mac_ctx: global MAC context + * @addn_ie: Additional IE buffer + * @addn_ielen: Length of additional IE + * @dst: extended capability IE structure to be updated + * + * This function is used to strip extended capability IE from IE buffer and + * update the passed structure. + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_strip_extcap_update_struct(struct mac_context *mac_ctx, + uint8_t *addn_ie, uint16_t *addn_ielen, tDot11fIEExtCap *dst) +{ + uint8_t extracted_buff[DOT11F_IE_EXTCAP_MAX_LEN + 2]; + QDF_STATUS status; + + qdf_mem_zero((uint8_t *)&extracted_buff[0], + DOT11F_IE_EXTCAP_MAX_LEN + 2); + status = lim_strip_ie(mac_ctx, addn_ie, addn_ielen, + DOT11F_EID_EXTCAP, ONE_BYTE, + NULL, 0, extracted_buff, + DOT11F_IE_EXTCAP_MAX_LEN); + if (QDF_STATUS_SUCCESS != status) { + pe_debug("Failed to strip extcap IE status: %d", status); + return status; + } + + /* update the extracted ExtCap to struct*/ + lim_update_extcap_struct(mac_ctx, extracted_buff, dst); + return status; +} + +/** + * lim_merge_extcap_struct() - merge extended capabilities info + * @dst: destination extended capabilities + * @src: source extended capabilities + * @add: true if add the capabilities, false if strip the capabilities. + * + * This function is used to take @src info and add/strip it to/from + * @dst extended capabilities info. + * + * Return: None + */ +void lim_merge_extcap_struct(tDot11fIEExtCap *dst, + tDot11fIEExtCap *src, + bool add) +{ + uint8_t *tempdst = (uint8_t *)dst->bytes; + uint8_t *tempsrc = (uint8_t *)src->bytes; + uint8_t structlen = member_size(tDot11fIEExtCap, bytes); + + /* Return if @src not present */ + if (!src->present) + return; + + /* Return if strip the capabilities from @dst which not present */ + if (!dst->present && !add) + return; + + /* Merge the capabilities info in other cases */ + while (tempdst && tempsrc && structlen--) { + if (add) + *tempdst |= *tempsrc; + else + *tempdst &= *tempsrc; + tempdst++; + tempsrc++; + } + dst->num_bytes = lim_compute_ext_cap_ie_length(dst); + if (dst->num_bytes == 0) + dst->present = 0; + else + dst->present = 1; +} + +/** + * lim_send_action_frm_tb_ppdu_cfg_flush_cb() - flush TB PPDU cfg msg + * @msg: Message pointer + * + * Flushes the send action frame in HE TB PPDU configuration message. + * + * Return: None + */ +static void lim_send_action_frm_tb_ppdu_cfg_flush_cb(struct scheduler_msg *msg) +{ + if (msg->bodyptr) { + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + } +} + +QDF_STATUS lim_send_action_frm_tb_ppdu_cfg(struct mac_context *mac_ctx, + uint32_t vdev_id, uint8_t cfg) +{ + tDot11fvendor_action_frame *frm; + uint8_t frm_len = sizeof(*frm); + struct pe_session *session; + struct cfg_action_frm_tb_ppdu *cfg_msg; + struct scheduler_msg msg = {0}; + uint8_t *data_buf; + + session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session) { + pe_err("pe session does not exist for vdev_id %d", + vdev_id); + return QDF_STATUS_E_FAILURE; + } + + data_buf = qdf_mem_malloc(frm_len + sizeof(*cfg_msg)); + if (!data_buf) + return QDF_STATUS_E_FAILURE; + + cfg_msg = (struct cfg_action_frm_tb_ppdu *)data_buf; + + frm = (tDot11fvendor_action_frame *)(data_buf + sizeof(*cfg_msg)); + + frm->Category.category = SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY; + + frm->vendor_oui.oui_data[0] = 0x00; + frm->vendor_oui.oui_data[1] = 0xA0; + frm->vendor_oui.oui_data[2] = 0xC6; + + frm->vendor_action_subtype.subtype = 0xFF; + + cfg_msg->vdev_id = vdev_id; + cfg_msg->cfg = cfg; + cfg_msg->frm_len = frm_len; + cfg_msg->data = (uint8_t *)frm; + + msg.type = WMA_CFG_VENDOR_ACTION_TB_PPDU; + msg.bodyptr = cfg_msg; + msg.reserved = 0; + msg.flush_callback = lim_send_action_frm_tb_ppdu_cfg_flush_cb; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + pe_err("Not able to post WMA_SET_IE_INFO to WDA"); + qdf_mem_free(data_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * lim_get_80Mhz_center_channel - finds 80 Mhz center channel + * + * @primary_channel: Primary channel for given 80 MHz band + * + * There are fixed 80MHz band and for each fixed band there is only one center + * valid channel. Also location of primary channel decides what 80 MHz band will + * it use, hence it decides what center channel will be used. This function + * does thus calculation and returns the center channel. + * + * Return: center channel + */ +uint8_t +lim_get_80Mhz_center_channel(uint8_t primary_channel) +{ + if (primary_channel >= 36 && primary_channel <= 48) + return (36+48)/2; + if (primary_channel >= 52 && primary_channel <= 64) + return (52+64)/2; + if (primary_channel >= 100 && primary_channel <= 112) + return (100+112)/2; + if (primary_channel >= 116 && primary_channel <= 128) + return (116+128)/2; + if (primary_channel >= 132 && primary_channel <= 144) + return (132+144)/2; + if (primary_channel >= 149 && primary_channel <= 161) + return (149+161)/2; + + return INVALID_CHANNEL_ID; +} + +/** + * lim_bss_type_to_string(): converts bss type enum to string. + * @bss_type: enum value of bss_type. + * + * Return: Printable string for bss_type + */ +const char *lim_bss_type_to_string(const uint16_t bss_type) +{ + switch (bss_type) { + CASE_RETURN_STRING(eSIR_INFRASTRUCTURE_MODE); + CASE_RETURN_STRING(eSIR_INFRA_AP_MODE); + CASE_RETURN_STRING(eSIR_IBSS_MODE); + CASE_RETURN_STRING(eSIR_AUTO_MODE); + CASE_RETURN_STRING(eSIR_NDI_MODE); + default: + return "Unknown bss_type"; + } +} + +/** + * lim_init_obss_params(): Initializes the OBSS Scan Parameters + * @sesssion: LIM session + * @mac_ctx: Mac context + * + * Return: None + */ +void lim_init_obss_params(struct mac_context *mac_ctx, struct pe_session *session) +{ + struct wlan_mlme_obss_ht40 *obss_ht40; + + if (!(mac_ctx->mlme_cfg)) { + pe_err("invalid mlme cfg"); + return; + } + + obss_ht40 = &mac_ctx->mlme_cfg->obss_ht40; + + session->obss_ht40_scanparam.obss_active_dwelltime = + obss_ht40->active_dwelltime; + + session->obss_ht40_scanparam.obss_passive_dwelltime = + obss_ht40->passive_dwelltime; + + session->obss_ht40_scanparam.obss_width_trigger_interval = + obss_ht40->width_trigger_interval; + + session->obss_ht40_scanparam.obss_active_total_per_channel = + obss_ht40->active_per_channel; + + session->obss_ht40_scanparam.obss_passive_total_per_channel = + obss_ht40->passive_per_channel; + + session->obss_ht40_scanparam.bsswidth_ch_trans_delay = + obss_ht40->width_trans_delay; + + session->obss_ht40_scanparam.obss_activity_threshold = + obss_ht40->scan_activity_threshold; +} + +/** + * lim_update_obss_scanparams(): Updates OBSS SCAN IE parameters to session + * @sesssion: LIM session + * @scan_params: Scan parameters + * + * Return: None + */ +void lim_update_obss_scanparams(struct pe_session *session, + tDot11fIEOBSSScanParameters *scan_params) +{ + /* + * If the received value is not in the range specified + * by the Specification then it will be the default value + * configured through cfg + */ + if ((scan_params->obssScanActiveDwell > + cfg_min(CFG_OBSS_HT40_SCAN_ACTIVE_DWELL_TIME)) && + (scan_params->obssScanActiveDwell < + cfg_max(CFG_OBSS_HT40_SCAN_ACTIVE_DWELL_TIME))) + session->obss_ht40_scanparam.obss_active_dwelltime = + scan_params->obssScanActiveDwell; + + if ((scan_params->obssScanPassiveDwell > + cfg_min(CFG_OBSS_HT40_SCAN_PASSIVE_DWELL_TIME)) && + (scan_params->obssScanPassiveDwell < + cfg_max(CFG_OBSS_HT40_SCAN_PASSIVE_DWELL_TIME))) + session->obss_ht40_scanparam.obss_passive_dwelltime = + scan_params->obssScanPassiveDwell; + + if ((scan_params->bssWidthChannelTransitionDelayFactor > + cfg_min(CFG_OBSS_HT40_WIDTH_CH_TRANSITION_DELAY)) && + (scan_params->bssWidthChannelTransitionDelayFactor < + cfg_max(CFG_OBSS_HT40_WIDTH_CH_TRANSITION_DELAY))) + session->obss_ht40_scanparam.bsswidth_ch_trans_delay = + scan_params->bssWidthChannelTransitionDelayFactor; + + if ((scan_params->obssScanActiveTotalPerChannel > + cfg_min(CFG_OBSS_HT40_SCAN_ACTIVE_TOTAL_PER_CHANNEL)) && + (scan_params->obssScanActiveTotalPerChannel < + cfg_max(CFG_OBSS_HT40_SCAN_ACTIVE_TOTAL_PER_CHANNEL))) + session->obss_ht40_scanparam.obss_active_total_per_channel = + scan_params->obssScanActiveTotalPerChannel; + + if ((scan_params->obssScanPassiveTotalPerChannel > + cfg_min(CFG_OBSS_HT40_SCAN_PASSIVE_TOTAL_PER_CHANNEL)) && + (scan_params->obssScanPassiveTotalPerChannel < + cfg_max(CFG_OBSS_HT40_SCAN_PASSIVE_TOTAL_PER_CHANNEL))) + session->obss_ht40_scanparam.obss_passive_total_per_channel = + scan_params->obssScanPassiveTotalPerChannel; + + if ((scan_params->bssChannelWidthTriggerScanInterval > + cfg_min(CFG_OBSS_HT40_SCAN_WIDTH_TRIGGER_INTERVAL)) && + (scan_params->bssChannelWidthTriggerScanInterval < + cfg_max(CFG_OBSS_HT40_SCAN_WIDTH_TRIGGER_INTERVAL))) + session->obss_ht40_scanparam.obss_width_trigger_interval = + scan_params->bssChannelWidthTriggerScanInterval; + + if ((scan_params->obssScanActivityThreshold > + cfg_min(CFG_OBSS_HT40_SCAN_ACTIVITY_THRESHOLD)) && + (scan_params->obssScanActivityThreshold < + cfg_max(CFG_OBSS_HT40_SCAN_ACTIVITY_THRESHOLD))) + session->obss_ht40_scanparam.obss_activity_threshold = + scan_params->obssScanActivityThreshold; + return; +} + +/** + * lim_is_robust_mgmt_action_frame() - Check if action category is + * robust action frame + * @action_category: Action frame category. + * + * This function is used to check if given action category is robust + * action frame. + * + * Return: bool + */ +bool lim_is_robust_mgmt_action_frame(uint8_t action_category) +{ + switch (action_category) { + /* + * NOTE: This function doesn't take care of the DMG + * (Directional Multi-Gigatbit) BSS case as 8011ad + * support is not yet added. In future, if the support + * is required then this function need few more arguments + * and little change in logic. + */ + case ACTION_CATEGORY_SPECTRUM_MGMT: + case ACTION_CATEGORY_QOS: + case ACTION_CATEGORY_DLS: + case ACTION_CATEGORY_BACK: + case ACTION_CATEGORY_RRM: + case ACTION_FAST_BSS_TRNST: + case ACTION_CATEGORY_SA_QUERY: + case ACTION_CATEGORY_PROTECTED_DUAL_OF_PUBLIC_ACTION: + case ACTION_CATEGORY_WNM: + case ACTION_CATEGORY_MESH_ACTION: + case ACTION_CATEGORY_MULTIHOP_ACTION: + case ACTION_CATEGORY_FST: + return true; + default: + pe_debug("non-PMF action category: %d", action_category); + break; + } + return false; +} + +/** + * lim_compute_ext_cap_ie_length - compute the length of ext cap ie + * based on the bits set + * @ext_cap: extended IEs structure + * + * Return: length of the ext cap ie, 0 means should not present + */ +uint8_t lim_compute_ext_cap_ie_length(tDot11fIEExtCap *ext_cap) +{ + uint8_t i = DOT11F_IE_EXTCAP_MAX_LEN; + + while (i) { + if (ext_cap->bytes[i-1]) + break; + i--; + } + + return i; +} + +/** + * lim_update_caps_info_for_bss - Update capability info for this BSS + * + * @mac_ctx: mac context + * @caps: Pointer to capability info to be updated + * @bss_caps: Capability info of the BSS + * + * Update the capability info in Assoc/Reassoc request frames and reset + * the spectrum management, short preamble, immediate block ack bits + * if the BSS doesnot support it + * + * Return: None + */ +void lim_update_caps_info_for_bss(struct mac_context *mac_ctx, + uint16_t *caps, uint16_t bss_caps) +{ + if (!(bss_caps & LIM_SPECTRUM_MANAGEMENT_BIT_MASK)) { + *caps &= (~LIM_SPECTRUM_MANAGEMENT_BIT_MASK); + pe_debug("Clearing spectrum management:no AP support"); + } + + if (!(bss_caps & LIM_SHORT_PREAMBLE_BIT_MASK)) { + *caps &= (~LIM_SHORT_PREAMBLE_BIT_MASK); + pe_debug("Clearing short preamble:no AP support"); + } + + if (!(bss_caps & LIM_IMMEDIATE_BLOCK_ACK_MASK)) { + *caps &= (~LIM_IMMEDIATE_BLOCK_ACK_MASK); + pe_debug("Clearing Immed Blk Ack:no AP support"); + } +} +/** + * lim_send_set_dtim_period(): Send SIR_HAL_SET_DTIM_PERIOD message + * to set dtim period. + * + * @sesssion: LIM session + * @dtim_period: dtim value + * @mac_ctx: Mac context + * @return None + */ +void lim_send_set_dtim_period(struct mac_context *mac_ctx, uint8_t dtim_period, + struct pe_session *session) +{ + struct set_dtim_params *dtim_params = NULL; + QDF_STATUS ret = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0}; + + if (!session) { + pe_err("Inavalid parameters"); + return; + } + dtim_params = qdf_mem_malloc(sizeof(*dtim_params)); + if (!dtim_params) + return; + dtim_params->dtim_period = dtim_period; + dtim_params->session_id = session->smeSessionId; + msg.type = WMA_SET_DTIM_PERIOD; + msg.bodyptr = dtim_params; + msg.bodyval = 0; + pe_debug("Post WMA_SET_DTIM_PERIOD to WMA"); + ret = wma_post_ctrl_msg(mac_ctx, &msg); + if (QDF_STATUS_SUCCESS != ret) { + pe_err("wma_post_ctrl_msg() failed"); + qdf_mem_free(dtim_params); + } +} + +/** + * lim_is_valid_frame(): validate RX frame using last processed frame details + * to find if it is duplicate frame. + * + * @last_processed_frm: last processed frame pointer. + * @pRxPacketInfo: RX packet. + * + * Frame treat as duplicate: + * if retry bit is set and + * if source address and seq number matches with the last processed frame + * + * Return: false if duplicate frame, else true. + */ +bool lim_is_valid_frame(last_processed_msg *last_processed_frm, + uint8_t *pRxPacketInfo) +{ + uint16_t seq_num; + tpSirMacMgmtHdr pHdr; + + if (!pRxPacketInfo) { + pe_err("Invalid RX frame"); + return false; + } + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + + if (pHdr->fc.retry == 0) + return true; + + seq_num = (((pHdr->seqControl.seqNumHi << + HIGH_SEQ_NUM_OFFSET) | + pHdr->seqControl.seqNumLo)); + + if (last_processed_frm->seq_num == seq_num && + qdf_mem_cmp(last_processed_frm->sa, pHdr->sa, ETH_ALEN) == 0) { + pe_err("Duplicate frame from "QDF_MAC_ADDR_FMT " Seq Number %d", + QDF_MAC_ADDR_REF(pHdr->sa), seq_num); + return false; + } + return true; +} + +/** + * lim_update_last_processed_frame(): update new processed frame info to cache. + * + * @last_processed_frm: last processed frame pointer. + * @pRxPacketInfo: Successfully processed RX packet. + * + * Return: None. + */ +void lim_update_last_processed_frame(last_processed_msg *last_processed_frm, + uint8_t *pRxPacketInfo) +{ + uint16_t seq_num; + tpSirMacMgmtHdr pHdr; + + if (!pRxPacketInfo) { + pe_err("Invalid RX frame"); + return; + } + + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + seq_num = (((pHdr->seqControl.seqNumHi << + HIGH_SEQ_NUM_OFFSET) | + pHdr->seqControl.seqNumLo)); + + qdf_mem_copy(last_processed_frm->sa, pHdr->sa, ETH_ALEN); + last_processed_frm->seq_num = seq_num; +} + +#if defined(CONFIG_BAND_6GHZ) && defined(WLAN_FEATURE_11AX) +static bool lim_support_6ghz_band_op_class(struct mac_context *mac_ctx, + tDot11fIESuppOperatingClasses * + op_class_ie) +{ + uint16_t i; + + if (!op_class_ie->present) + return false; + for (i = 0; i < op_class_ie->num_classes; i++) { + if (wlan_reg_is_6ghz_op_class(mac_ctx->pdev, + op_class_ie->classes[i])) + break; + } + if (i < op_class_ie->num_classes) + return true; + + return false; +} + +void lim_ap_check_6g_compatible_peer(struct mac_context *mac_ctx, + struct pe_session *session) +{ + uint16_t i; + tpDphHashNode sta_ds; + bool legacy_client_present = false; + + if (!LIM_IS_AP_ROLE(session)) + return; + + for (i = 1; i < session->dph.dphHashTable.size; i++) { + sta_ds = dph_get_hash_entry(mac_ctx, i, + &session->dph.dphHashTable); + if (!sta_ds) + continue; + if (sta_ds->staType != STA_ENTRY_PEER) + continue; + if (!lim_support_6ghz_band_op_class( + mac_ctx, &sta_ds->supp_operating_classes)) { + legacy_client_present = true; + pe_debug("peer "QDF_MAC_ADDR_FMT" 6ghz not supported", + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + break; + } + pe_debug("peer "QDF_MAC_ADDR_FMT" 6ghz supported", + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + } + if (legacy_client_present) + policy_mgr_set_ap_6ghz_capable( + mac_ctx->psoc, session->vdev_id, false, + CONN_6GHZ_FLAG_NO_LEGACY_CLIENT); + else + policy_mgr_set_ap_6ghz_capable( + mac_ctx->psoc, session->vdev_id, true, + CONN_6GHZ_FLAG_NO_LEGACY_CLIENT); +} +#endif + +#ifdef WLAN_FEATURE_11AX +void lim_update_he_6ghz_band_caps(struct mac_context *mac, + tDot11fIEhe_6ghz_band_cap *he_6ghz_band_cap, + tpAddStaParams params) +{ + qdf_mem_copy(¶ms->he_6ghz_band_caps, he_6ghz_band_cap, + sizeof(tDot11fIEhe_6ghz_band_cap)); + + lim_log_he_6g_cap(mac, ¶ms->he_6ghz_band_caps); +} + +void lim_add_he_cap(struct mac_context *mac_ctx, struct pe_session *pe_session, + tpAddStaParams add_sta_params, tpSirAssocReq assoc_req) +{ + if (!add_sta_params->he_capable || !assoc_req) + return; + + qdf_mem_copy(&add_sta_params->he_config, &assoc_req->he_cap, + sizeof(add_sta_params->he_config)); + + if (lim_is_he_6ghz_band(pe_session)) + lim_update_he_6ghz_band_caps(mac_ctx, + &assoc_req->he_6ghz_band_cap, + add_sta_params); + +} + +void lim_add_self_he_cap(tpAddStaParams add_sta_params, struct pe_session *session) +{ + if (!session) + return; + + add_sta_params->he_capable = true; + + qdf_mem_copy(&add_sta_params->he_config, &session->he_config, + sizeof(add_sta_params->he_config)); + qdf_mem_copy(&add_sta_params->he_op, &session->he_op, + sizeof(add_sta_params->he_op)); +} + +/** + * lim_intersect_he_caps() - Intersect peer capability and self capability + * @rcvd_he: pointer to received peer capability + * @session_he: pointer to self capability + * @peer_he: pointer to Intersected capability + * + * Return: None + */ +static void lim_intersect_he_caps(tDot11fIEhe_cap *rcvd_he, + tDot11fIEhe_cap *session_he, + tDot11fIEhe_cap *peer_he) +{ + uint8_t val; + + qdf_mem_copy(peer_he, rcvd_he, sizeof(*peer_he)); + + peer_he->fragmentation = QDF_MIN(session_he->fragmentation, + peer_he->fragmentation); + + peer_he->ldpc_coding &= session_he->ldpc_coding; + + if (session_he->tb_ppdu_tx_stbc_lt_80mhz && peer_he->rx_stbc_lt_80mhz) + peer_he->rx_stbc_lt_80mhz = 1; + else + peer_he->rx_stbc_lt_80mhz = 0; + + if (session_he->rx_stbc_lt_80mhz && peer_he->tb_ppdu_tx_stbc_lt_80mhz) + peer_he->tb_ppdu_tx_stbc_lt_80mhz = 1; + else + peer_he->tb_ppdu_tx_stbc_lt_80mhz = 0; + + if (session_he->tb_ppdu_tx_stbc_gt_80mhz && peer_he->rx_stbc_gt_80mhz) + peer_he->rx_stbc_gt_80mhz = 1; + else + peer_he->rx_stbc_gt_80mhz = 0; + + if (session_he->rx_stbc_gt_80mhz && peer_he->tb_ppdu_tx_stbc_gt_80mhz) + peer_he->tb_ppdu_tx_stbc_gt_80mhz = 1; + else + peer_he->tb_ppdu_tx_stbc_gt_80mhz = 0; + + /* Tx Doppler is first bit and Rx Doppler is second bit */ + if (session_he->doppler) { + val = 0; + if ((session_he->doppler & 0x1) && (peer_he->doppler & 0x10)) + val |= (1 << 1); + if ((session_he->doppler & 0x10) && (peer_he->doppler & 0x1)) + val |= (1 << 0); + peer_he->doppler = val; + } + + peer_he->su_beamformer = session_he->su_beamformee ? + peer_he->su_beamformer : 0; + peer_he->su_beamformee = (session_he->su_beamformer || + session_he->mu_beamformer) ? + peer_he->su_beamformee : 0; + peer_he->mu_beamformer = session_he->su_beamformee ? + peer_he->mu_beamformer : 0; + + peer_he->twt_request = session_he->twt_responder ? + peer_he->twt_request : 0; + peer_he->twt_responder = session_he->twt_request ? + peer_he->twt_responder : 0; +} + +void lim_intersect_sta_he_caps(tpSirAssocReq assoc_req, struct pe_session *session, + tpDphHashNode sta_ds) +{ + tDot11fIEhe_cap *rcvd_he = &assoc_req->he_cap; + tDot11fIEhe_cap *session_he = &session->he_config; + tDot11fIEhe_cap *peer_he = &sta_ds->he_config; + + if (sta_ds->mlmStaContext.he_capable) + lim_intersect_he_caps(rcvd_he, session_he, peer_he); +} + +void lim_intersect_ap_he_caps(struct pe_session *session, struct bss_params *add_bss, + tSchBeaconStruct *beacon, tpSirAssocRsp assoc_rsp) +{ + tDot11fIEhe_cap *rcvd_he; + tDot11fIEhe_cap *session_he = &session->he_config; + tDot11fIEhe_cap *peer_he = &add_bss->staContext.he_config; + + if (beacon) + rcvd_he = &beacon->he_cap; + else + rcvd_he = &assoc_rsp->he_cap; + + lim_intersect_he_caps(rcvd_he, session_he, peer_he); + add_bss->staContext.he_capable = true; +} + +void lim_add_bss_he_cap(struct bss_params *add_bss, tpSirAssocRsp assoc_rsp) +{ + tDot11fIEhe_cap *he_cap; + tDot11fIEhe_op *he_op; + + he_cap = &assoc_rsp->he_cap; + he_op = &assoc_rsp->he_op; + add_bss->he_capable = he_cap->present; + if (he_cap) + qdf_mem_copy(&add_bss->staContext.he_config, + he_cap, sizeof(*he_cap)); + if (he_op) + qdf_mem_copy(&add_bss->staContext.he_op, + he_op, sizeof(*he_op)); +} + +void lim_add_bss_he_cfg(struct bss_params *add_bss, struct pe_session *session) +{ + add_bss->he_sta_obsspd = session->he_sta_obsspd; +} + +void lim_update_he_6gop_assoc_resp(struct bss_params *pAddBssParams, + tDot11fIEhe_op *he_op, + struct pe_session *pe_session) +{ + if (!pe_session->he_6ghz_band) + return; + + if (!he_op->oper_info_6g_present) { + pe_debug("6G operation info not present in beacon"); + return; + } + if (!pe_session->ch_width) + return; + + pAddBssParams->ch_width = QDF_MIN(he_op->oper_info_6g.info.ch_width, + pe_session->ch_width); + + if (pAddBssParams->ch_width == CH_WIDTH_160MHZ) + pAddBssParams->ch_width = pe_session->ch_width; + pAddBssParams->staContext.ch_width = pAddBssParams->ch_width; +} + +static bool lim_check_is_bss_greater_than_4_nss_supp(struct pe_session * +session, + tDot11fIEhe_cap *he_cap) +{ + uint8_t i; + uint16_t mcs_map; +#define NSS_4 4 +#define NSS_8 8 + + if (!session->he_capable || !he_cap->present) + return false; + mcs_map = he_cap->rx_he_mcs_map_lt_80; + for (i = NSS_4; i < NSS_8; i++) { + if (((mcs_map >> (i * 2)) & 0x3) != 0x3) + return true; + } + + return false; +} + +static bool lim_check_he_80_mcs11_supp(struct pe_session *session, + tDot11fIEhe_cap *he_cap) +{ + uint16_t rx_mcs_map; + uint16_t tx_mcs_map; + + rx_mcs_map = he_cap->rx_he_mcs_map_lt_80; + tx_mcs_map = he_cap->tx_he_mcs_map_lt_80; + if ((session->nss == NSS_1x1_MODE) && + ((HE_GET_MCS_4_NSS(rx_mcs_map, 1) == HE_MCS_0_11) || + (HE_GET_MCS_4_NSS(tx_mcs_map, 1) == HE_MCS_0_11))) + return true; + + if ((session->nss == NSS_2x2_MODE) && + ((HE_GET_MCS_4_NSS(rx_mcs_map, 2) == HE_MCS_0_11) || + (HE_GET_MCS_4_NSS(tx_mcs_map, 2) == HE_MCS_0_11))) + return true; + + return false; +} + +/** + * lim_check_he_ldpc_cap() - set he ladpc coding to one if + * channel width is > 20 or mcs 10/11 bit are supported or + * nss is greater than 4. + * @beacon_struct: beacon structure + * @session: A pointer to session entry. + * + * Return: None + */ + +static void lim_check_and_force_he_ldpc_cap(struct pe_session *session, + tDot11fIEhe_cap *he_cap) +{ + if (!he_cap->ldpc_coding && + (session->ch_width > CH_WIDTH_20MHZ || + lim_check_he_80_mcs11_supp(session, he_cap) || + lim_check_is_bss_greater_than_4_nss_supp(session, he_cap))) + he_cap->ldpc_coding = 1; +} + +void lim_update_stads_he_caps(tpDphHashNode sta_ds, tpSirAssocRsp assoc_rsp, + struct pe_session *session_entry, + tSchBeaconStruct *beacon) +{ + tDot11fIEhe_cap *he_cap; + + he_cap = &assoc_rsp->he_cap; + sta_ds->mlmStaContext.he_capable = he_cap->present; + + if (!he_cap->present) + return; + + /* setting lpdc_coding if any of assoc_rsp or beacon has ladpc_coding + * enabled + */ + if (beacon) + he_cap->ldpc_coding |= beacon->he_cap.ldpc_coding; + lim_check_and_force_he_ldpc_cap(session_entry, he_cap); + qdf_mem_copy(&sta_ds->he_config, he_cap, sizeof(*he_cap)); +} + +void lim_update_stads_he_6ghz_op(struct pe_session *session, + tpDphHashNode sta_ds) +{ + tDot11fIEhe_cap *peer_he = &sta_ds->he_config; + enum phy_ch_width ch_width; + + if (!session->he_6ghz_band) + return; + + if (!peer_he->present) { + pe_debug("HE cap not present in peer"); + return; + } + + if (peer_he->chan_width_3) + ch_width = CH_WIDTH_80P80MHZ; + else if (peer_he->chan_width_2) + ch_width = CH_WIDTH_160MHZ; + else if (peer_he->chan_width_1) + ch_width = CH_WIDTH_80MHZ; + else + ch_width = CH_WIDTH_20MHZ; + if (ch_width > session->ch_width) + ch_width = session->ch_width; + sta_ds->ch_width = ch_width; +} + +void lim_update_usr_he_cap(struct mac_context *mac_ctx, struct pe_session *session) +{ + struct add_ie_params *add_ie = &session->add_ie_params; + tDot11fIEhe_cap *he_cap = &session->he_config; + struct he_cap_network_endian *he_cap_from_ie; + uint8_t extracted_buff[DOT11F_IE_HE_CAP_MAX_LEN + 2]; + QDF_STATUS status; + struct sir_vht_config *vht_cfg = &session->vht_config; + qdf_mem_zero(extracted_buff, sizeof(extracted_buff)); + status = lim_strip_ie(mac_ctx, add_ie->probeRespBCNData_buff, + &add_ie->probeRespBCNDataLen, + DOT11F_EID_HE_CAP, ONE_BYTE, + HE_CAP_OUI_TYPE, (uint8_t)HE_CAP_OUI_SIZE, + extracted_buff, DOT11F_IE_HE_CAP_MAX_LEN); + if (QDF_STATUS_SUCCESS != status) { + pe_debug("Failed to strip HE cap IE status: %d", status); + return; + } + + pe_debug("Before update: su_beamformer: %d, su_beamformee: %d, mu_beamformer: %d", + he_cap->su_beamformer, he_cap->su_beamformee, he_cap->mu_beamformer); + + he_cap_from_ie = (struct he_cap_network_endian *) + &extracted_buff[HE_CAP_OUI_SIZE + 2]; + + he_cap->su_beamformer = + he_cap->su_beamformer & he_cap_from_ie->su_beamformer; + he_cap->su_beamformee = + he_cap->su_beamformee & he_cap_from_ie->su_beamformee; + he_cap->mu_beamformer = + he_cap->mu_beamformer & he_cap_from_ie->mu_beamformer; + + pe_debug("After update: su_beamformer: %d, su_beamformee: %d, mu_beamformer: %d", + he_cap->su_beamformer, he_cap->su_beamformee, he_cap->mu_beamformer); + if (!he_cap->su_beamformer) { + he_cap->mu_beamformer = 0; + he_cap->num_sounding_lt_80 = 0; + he_cap->num_sounding_gt_80 = 0; + vht_cfg->su_beam_former = 0; + vht_cfg->mu_beam_former = 0; + vht_cfg->num_soundingdim = 0; + } + if (!he_cap->su_beamformee) { + he_cap->bfee_sts_lt_80 = 0; + he_cap->bfee_sts_gt_80 = 0; + vht_cfg->su_beam_formee = 0; + vht_cfg->mu_beam_formee = 0; + vht_cfg->csnof_beamformer_antSup = 0; + } + wma_set_he_txbf_params(session->vdev_id, he_cap->su_beamformer, + he_cap->su_beamformee, he_cap->mu_beamformer); +} + +void lim_decide_he_op(struct mac_context *mac_ctx, uint32_t *mlme_he_ops, + struct pe_session *session) +{ + uint32_t val; + uint8_t color; + struct he_ops_network_endian *he_ops_from_ie; + tDot11fIEhe_op he_ops = {0}; + struct add_ie_params *add_ie = &session->add_ie_params; + uint8_t extracted_buff[DOT11F_IE_HE_OP_MAX_LEN + 2]; + QDF_STATUS status; + + qdf_mem_zero(extracted_buff, sizeof(extracted_buff)); + status = lim_strip_ie(mac_ctx, add_ie->probeRespBCNData_buff, + &add_ie->probeRespBCNDataLen, + DOT11F_EID_HE_OP, ONE_BYTE, + HE_OP_OUI_TYPE, (uint8_t)HE_OP_OUI_SIZE, + extracted_buff, DOT11F_IE_HE_OP_MAX_LEN); + if (QDF_STATUS_SUCCESS != status) { + pe_debug("Failed to strip HE OP IE status: %d", status); + return; + } + he_ops_from_ie = (struct he_ops_network_endian *) + &extracted_buff[HE_OP_OUI_SIZE + 2]; + + if (he_ops_from_ie->bss_color) { + he_ops.bss_color = he_ops_from_ie->bss_color; + } else { + qdf_get_random_bytes(&color, sizeof(color)); + /* make sure color is within 1-63*/ + he_ops.bss_color = (color % WNI_CFG_HE_OPS_BSS_COLOR_MAX) + 1; + } + he_ops.default_pe = he_ops_from_ie->default_pe; + he_ops.twt_required = he_ops_from_ie->twt_required; + he_ops.txop_rts_threshold = he_ops_from_ie->txop_rts_threshold; + he_ops.partial_bss_col = he_ops_from_ie->partial_bss_col; + he_ops.bss_col_disabled = he_ops_from_ie->bss_col_disabled; + + val = mac_ctx->mlme_cfg->he_caps.he_ops_basic_mcs_nss; + + *((uint16_t *)he_ops.basic_mcs_nss) = (uint16_t)val; + + qdf_mem_copy(&session->he_op, &he_ops, sizeof(tDot11fIEhe_op)); + + pe_debug("HE Op: bss_color: 0x%0x, default_pe_duration: 0x%0x", + he_ops.bss_color, he_ops.default_pe); + pe_debug("He Op: twt_required: 0x%0x, txop_rts_threshold: 0x%0x", + he_ops.twt_required, he_ops.txop_rts_threshold); + pe_debug("HE Op: partial_bss_color: 0x%0x", + he_ops.partial_bss_col); + pe_debug("HE Op: BSS color disabled: 0x%0x", + he_ops.bss_col_disabled); + pe_debug("HE Op: Basic MCS NSS: 0x%04x", + *((uint16_t *)he_ops.basic_mcs_nss)); + + wma_update_vdev_he_ops(mlme_he_ops, &he_ops); +} + +void lim_copy_bss_he_cap(struct pe_session *session, + struct start_bss_req *sme_start_bss_req) +{ + qdf_mem_copy(&(session->he_config), &(sme_start_bss_req->he_config), + sizeof(session->he_config)); +} + +void lim_copy_join_req_he_cap(struct pe_session *session, + struct join_req *sme_join_req) +{ + qdf_mem_copy(&(session->he_config), &(sme_join_req->he_config), + sizeof(session->he_config)); +} + +void lim_log_he_cap(struct mac_context *mac, tDot11fIEhe_cap *he_cap) +{ + uint8_t chan_width; + struct ppet_hdr *hdr; + + if (!he_cap->present) + return; + + pe_nofl_debug("HE Capabilities: htc_he 0x%x twt_req 0x%x twt_res 0x%x fragmentation 0x%x max frag msdu amsdu 0x%x min frag 0x%x", + he_cap->htc_he, he_cap->twt_request, + he_cap->twt_responder, he_cap->fragmentation, + he_cap->max_num_frag_msdu_amsdu_exp, + he_cap->min_frag_size); + pe_nofl_debug("\ttrig frm mac pad 0x%x multi tid aggr supp 0x%x link adaptaion 0x%x all ack 0x%x trigd_rsp_sched 0x%x a_bsr 0x%x", + he_cap->trigger_frm_mac_pad, + he_cap->multi_tid_aggr_rx_supp, + he_cap->he_link_adaptation, he_cap->all_ack, + he_cap->trigd_rsp_sched, he_cap->a_bsr); + pe_nofl_debug("\tBC twt 0x%x ba_32bit_bitmap supp 0x%x mu_cascade 0x%x ack_enabled_multitid 0x%x omi_a_ctrl 0x%x ofdma_ra 0x%x", + he_cap->broadcast_twt, he_cap->ba_32bit_bitmap, + he_cap->mu_cascade, he_cap->ack_enabled_multitid, + he_cap->omi_a_ctrl, he_cap->ofdma_ra); + pe_nofl_debug("\tmax_ampdu_len exp ext 0x%x amsdu_frag 0x%x flex_twt_sched 0x%x rx_ctrl frm 0x%x bsrp_ampdu_aggr 0x%x qtp 0x%x a_bqr 0x%x", + he_cap->max_ampdu_len_exp_ext, he_cap->amsdu_frag, + he_cap->flex_twt_sched, he_cap->rx_ctrl_frame, + he_cap->bsrp_ampdu_aggr, he_cap->qtp, he_cap->a_bqr); + pe_nofl_debug("\tSR Reponder 0x%x ndp_feedback 0x%x ops_supp 0x%x amsdu_in_ampdu 0x%x multi_tid_aggr_tx 0x%x he_sub_ch_sel_tx 0x%x", + he_cap->spatial_reuse_param_rspder, + he_cap->ndp_feedback_supp, + he_cap->ops_supp, he_cap->amsdu_in_ampdu, + he_cap->multi_tid_aggr_tx_supp, + he_cap->he_sub_ch_sel_tx_supp); + + pe_nofl_debug("\tul_2x996_tone_ru 0x%x om_ctrl_ul_mu_data_dis_rx 0x%x dynamic_smps 0x%x punctured_sounding 0x%x ht_vht_trg_frm_rx 0x%x", + he_cap->ul_2x996_tone_ru_supp, + he_cap->om_ctrl_ul_mu_data_dis_rx, + he_cap->he_dynamic_smps, he_cap->punctured_sounding_supp, + he_cap->ht_vht_trg_frm_rx_supp); + + chan_width = HE_CH_WIDTH_COMBINE(he_cap->chan_width_0, + he_cap->chan_width_1, he_cap->chan_width_2, + he_cap->chan_width_3, he_cap->chan_width_4, + he_cap->chan_width_5, he_cap->chan_width_6); + + pe_nofl_debug("\tchan width %d rx_pream_puncturing 0x%x device_class 0x%x ldpc_coding 0x%x 1x_ltf_800_gi_ppdu 0x%x midamble_tx_rx_max_nsts 0x%x", + chan_width, he_cap->rx_pream_puncturing, + he_cap->device_class, + he_cap->ldpc_coding, he_cap->he_1x_ltf_800_gi_ppdu, + he_cap->midamble_tx_rx_max_nsts); + + pe_nofl_debug("\t4x_ltf_3200_gi_ndp 0x%x tb_ppdu_tx_stbc_lt_80mhz 0x%x rx_stbc_lt_80mhz 0x%x doppler 0x%x ul_mu 0x%x dcm_enc_tx 0x%x dcm_enc_rx 0x%x", + he_cap->he_4x_ltf_3200_gi_ndp, + he_cap->tb_ppdu_tx_stbc_lt_80mhz, + he_cap->rx_stbc_lt_80mhz, he_cap->doppler, he_cap->ul_mu, + he_cap->dcm_enc_tx, he_cap->dcm_enc_rx); + + pe_nofl_debug("\tul_he_mu 0x%x su_bfer 0x%x su_fee 0x%x mu_bfer 0x%x bfee_sts_lt_80 0x%x bfee_sts_gt_80 0x%x num_sd_lt_80 0x%x num_sd_gt_80 0x%x", + he_cap->ul_he_mu, he_cap->su_beamformer, + he_cap->su_beamformee, + he_cap->mu_beamformer, he_cap->bfee_sts_lt_80, + he_cap->bfee_sts_gt_80, he_cap->num_sounding_lt_80, + he_cap->num_sounding_gt_80); + + pe_nofl_debug("\tsu_fb_tone16 0x%x mu_fb_tone16 0x%x codebook_su 0x%x codebook_mu 0x%x bforming_feedback 0x%x he_er_su_ppdu 0x%x dl_mu_mimo_part_bw 0x%x", + he_cap->su_feedback_tone16, he_cap->mu_feedback_tone16, + he_cap->codebook_su, he_cap->codebook_mu, + he_cap->beamforming_feedback, he_cap->he_er_su_ppdu, + he_cap->dl_mu_mimo_part_bw); + + pe_nofl_debug("\tppet_present 0x%x srp 0x%x power_boost 0x%x ltf_800_gi_4x 0x%x tb_ppdu_tx_stbc_gt_80mhz 0x%x rx_stbc_gt_80mhz 0x%x max_nc 0x%x", + he_cap->ppet_present, he_cap->srp, + he_cap->power_boost, he_cap->he_ltf_800_gi_4x, + he_cap->tb_ppdu_tx_stbc_gt_80mhz, + he_cap->rx_stbc_gt_80mhz, he_cap->max_nc); + + pe_nofl_debug("\ter_ltf_800_gi_4x 0x%x ppdu_20_in_40Mhz_2G 0x%x ppdu_20_in_160_80p80Mhz 0x%x ppdu_80_in_160_80p80Mhz 0x%x er_1x_ltf_gi 0x%x", + he_cap->er_he_ltf_800_gi_4x, + he_cap->he_ppdu_20_in_40Mhz_2G, + he_cap->he_ppdu_20_in_160_80p80Mhz, + he_cap->he_ppdu_80_in_160_80p80Mhz, + he_cap->er_1x_he_ltf_gi); + + pe_nofl_debug("\tmidamble_tx_rx_1x_ltf 0x%x dcm_max_bw 0x%x longer_than_16_he_sigb_ofdm_sym 0x%x non_trig_cqi_feedback 0x%x tx_1024_qam_lt_242_tone_ru 0x%x", + he_cap->midamble_tx_rx_1x_he_ltf, he_cap->dcm_max_bw, + he_cap->longer_than_16_he_sigb_ofdm_sym, + he_cap->non_trig_cqi_feedback, + he_cap->tx_1024_qam_lt_242_tone_ru); + + pe_nofl_debug("\trx_1024_qam_lt_242_tone_ru 0x%x rx_full_bw_su_he_mu_compress_sigb 0x%x rx_he_mcs_map_lt_80 0x%x tx_he_mcs_map_lt_80 0x%x", + he_cap->rx_1024_qam_lt_242_tone_ru, + he_cap->rx_full_bw_su_he_mu_compress_sigb, + he_cap->rx_he_mcs_map_lt_80, + he_cap->tx_he_mcs_map_lt_80); + + hdr = (struct ppet_hdr *)&he_cap->ppet; + pe_nofl_debug("\tRx MCS map 160 Mhz: 0x%x Tx MCS map 160 Mhz: 0x%x Rx MCS map 80+80 Mhz: 0x%x Tx MCS map 80+80 Mhz: 0x%x ppe_th:: nss_count: %d, ru_idx_msk: %d", + *((uint16_t *)he_cap->rx_he_mcs_map_160), + *((uint16_t *)he_cap->tx_he_mcs_map_160), + *((uint16_t *)he_cap->rx_he_mcs_map_80_80), + *((uint16_t *)he_cap->tx_he_mcs_map_80_80), + hdr->nss, hdr->ru_idx_mask); + + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + &he_cap->ppet, HE_MAX_PPET_SIZE); +} + +void lim_log_he_op(struct mac_context *mac, tDot11fIEhe_op *he_ops, + struct pe_session *session) +{ + pe_debug("bss_color 0x%x, default_pe_dur 0x%x, twt_req 0x%x, txop_rts_thres 0x%x, vht_op 0x%x", + he_ops->bss_color, he_ops->default_pe, + he_ops->twt_required, he_ops->txop_rts_threshold, + he_ops->vht_oper_present); + pe_debug("\tpart_bss_color 0x%x, Co-located BSS 0x%x, BSS color dis 0x%x basic mcs nss: 0x%x", + he_ops->partial_bss_col, he_ops->co_located_bss, + he_ops->bss_col_disabled, + *((uint16_t *)he_ops->basic_mcs_nss)); + + if (!session->he_6ghz_band) { + if (!he_ops->vht_oper_present) + pe_debug("VHT Info not present in HE Operation"); + else + pe_debug("VHT Info: ch_bw %d cntr_freq0 %d cntr_freq1 %d", + he_ops->vht_oper.info.chan_width, + he_ops->vht_oper.info.center_freq_seg0, + he_ops->vht_oper.info.center_freq_seg1); + } else { + if (!he_ops->oper_info_6g_present) + pe_debug("6G op_info not present in HE Operation"); + else + pe_debug("6G_op_info:ch_bw %d cntr_freq0 %d cntr_freq1 %d dup_bcon %d, min_rate %d", + he_ops->oper_info_6g.info.ch_width, + he_ops->oper_info_6g.info.center_freq_seg0, + he_ops->oper_info_6g.info.center_freq_seg1, + he_ops->oper_info_6g.info.dup_bcon, + he_ops->oper_info_6g.info.min_rate); + } + +} + +void lim_log_he_6g_cap(struct mac_context *mac, + tDot11fIEhe_6ghz_band_cap *he_6g_cap) +{ + pe_debug("min_mpdu_space: %0d, max_mpdu_len_exp: %0x, max_mpdu_len %0x, smps %0x, rd %0x rx_ant_ptn %d tx_ant_ptn %d", + he_6g_cap->min_mpdu_start_spacing, + he_6g_cap->max_ampdu_len_exp, he_6g_cap->max_mpdu_len, + he_6g_cap->sm_pow_save, he_6g_cap->rd_responder, + he_6g_cap->rx_ant_pattern_consistency, + he_6g_cap->tx_ant_pattern_consistency); +} + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +void lim_log_he_bss_color(struct mac_context *mac, + tDot11fIEbss_color_change *he_bss_color) +{ + pe_debug("countdown: %d, new_color: %d", + he_bss_color->countdown, he_bss_color->new_color); +} +#endif + +void lim_update_sta_he_capable(struct mac_context *mac, + tpAddStaParams add_sta_params, tpDphHashNode sta_ds, + struct pe_session *session_entry) +{ + if (LIM_IS_AP_ROLE(session_entry) || LIM_IS_IBSS_ROLE(session_entry)) + add_sta_params->he_capable = sta_ds->mlmStaContext.he_capable && + session_entry->he_capable; +#ifdef FEATURE_WLAN_TDLS + else if (STA_ENTRY_TDLS_PEER == sta_ds->staType) + add_sta_params->he_capable = sta_ds->mlmStaContext.he_capable; +#endif + else + add_sta_params->he_capable = session_entry->he_capable; + + pe_debug("he_capable: %d", add_sta_params->he_capable); +} + +void lim_update_bss_he_capable(struct mac_context *mac, + struct bss_params *add_bss) +{ + add_bss->he_capable = true; + pe_debug("he_capable: %d", add_bss->he_capable); +} + +void lim_update_stads_he_capable(tpDphHashNode sta_ds, tpSirAssocReq assoc_req) +{ + sta_ds->mlmStaContext.he_capable = assoc_req->he_cap.present; +} + +void lim_update_session_he_capable(struct mac_context *mac, struct pe_session *session) +{ + session->he_capable = true; + if (wlan_reg_is_6ghz_chan_freq(session->curr_op_freq)) { + session->htCapability = 0; + session->vhtCapability = 0; + session->he_6ghz_band = 1; + } + if (wlan_reg_is_24ghz_ch_freq(session->curr_op_freq) && + !mac->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band) + session->vhtCapability = 0; + + pe_debug("he_capable: %d", session->he_capable); +} + +void lim_update_session_he_capable_chan_switch(struct mac_context *mac, + struct pe_session *session, + uint32_t new_chan_freq) +{ + session->he_capable = true; + if (wlan_reg_is_6ghz_chan_freq(session->curr_op_freq) && + !wlan_reg_is_6ghz_chan_freq(new_chan_freq)) { + session->htCapability = 1; + session->vhtCapability = 1; + session->he_6ghz_band = 0; + } else if (!wlan_reg_is_6ghz_chan_freq(session->curr_op_freq) && + wlan_reg_is_6ghz_chan_freq(new_chan_freq)) { + session->htCapability = 0; + session->vhtCapability = 0; + session->he_6ghz_band = 1; + } + + /* + * If new channel is 2.4gh set VHT as per the b24ghz_band INI + * if new channel is 5Ghz set the vht, this will happen if we move from + * 2.4Ghz to 5Ghz. + */ + if (wlan_reg_is_24ghz_ch_freq(new_chan_freq) && + !mac->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band) + session->vhtCapability = 0; + else if (wlan_reg_is_5ghz_ch_freq(new_chan_freq)) + session->vhtCapability = 1; + + pe_debug("he_capable: %d ht %d vht %d 6ghz_band %d new freq %d vht in 2.4gh %d", + session->he_capable, session->htCapability, + session->vhtCapability, session->he_6ghz_band, new_chan_freq, + mac->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band); +} + +void lim_set_he_caps(struct mac_context *mac, struct pe_session *session, + uint8_t *ie_start, uint32_t num_bytes) +{ + const uint8_t *ie = NULL; + tDot11fIEhe_cap dot11_cap; + struct he_capability_info *he_cap; + + populate_dot11f_he_caps(mac, session, &dot11_cap); + lim_log_he_cap(mac, &dot11_cap); + ie = wlan_get_ext_ie_ptr_from_ext_id(HE_CAP_OUI_TYPE, + HE_CAP_OUI_SIZE, ie_start, num_bytes); + if (ie) { + /* convert from unpacked to packed structure */ + he_cap = (struct he_capability_info *) &ie[2 + HE_CAP_OUI_SIZE]; + + he_cap->htc_he = dot11_cap.htc_he; + he_cap->twt_request = dot11_cap.twt_request; + he_cap->twt_responder = dot11_cap.twt_responder; + he_cap->fragmentation = dot11_cap.fragmentation; + he_cap->max_num_frag_msdu_amsdu_exp = + dot11_cap.max_num_frag_msdu_amsdu_exp; + he_cap->min_frag_size = dot11_cap.min_frag_size; + he_cap->trigger_frm_mac_pad = dot11_cap.trigger_frm_mac_pad; + he_cap->multi_tid_aggr_rx_supp = + dot11_cap.multi_tid_aggr_rx_supp; + he_cap->he_link_adaptation = dot11_cap.he_link_adaptation; + he_cap->all_ack = dot11_cap.all_ack; + he_cap->trigd_rsp_sched = dot11_cap.trigd_rsp_sched; + he_cap->a_bsr = dot11_cap.a_bsr; + he_cap->broadcast_twt = dot11_cap.broadcast_twt; + he_cap->ba_32bit_bitmap = dot11_cap.ba_32bit_bitmap; + he_cap->mu_cascade = dot11_cap.mu_cascade; + he_cap->ack_enabled_multitid = dot11_cap.ack_enabled_multitid; + he_cap->omi_a_ctrl = dot11_cap.omi_a_ctrl; + he_cap->ofdma_ra = dot11_cap.ofdma_ra; + he_cap->max_ampdu_len_exp_ext = dot11_cap.max_ampdu_len_exp_ext; + he_cap->amsdu_frag = dot11_cap.amsdu_frag; + he_cap->flex_twt_sched = dot11_cap.flex_twt_sched; + he_cap->rx_ctrl_frame = dot11_cap.rx_ctrl_frame; + + he_cap->bsrp_ampdu_aggr = dot11_cap.bsrp_ampdu_aggr; + he_cap->qtp = dot11_cap.qtp; + he_cap->a_bqr = dot11_cap.a_bqr; + he_cap->spatial_reuse_param_rspder = + dot11_cap.spatial_reuse_param_rspder; + he_cap->ops_supp = dot11_cap.ops_supp; + he_cap->ndp_feedback_supp = dot11_cap.ndp_feedback_supp; + he_cap->amsdu_in_ampdu = dot11_cap.amsdu_in_ampdu; + + he_cap->chan_width = HE_CH_WIDTH_COMBINE(dot11_cap.chan_width_0, + dot11_cap.chan_width_1, dot11_cap.chan_width_2, + dot11_cap.chan_width_3, dot11_cap.chan_width_4, + dot11_cap.chan_width_5, dot11_cap.chan_width_6); + + he_cap->rx_pream_puncturing = dot11_cap.rx_pream_puncturing; + he_cap->device_class = dot11_cap.device_class; + he_cap->ldpc_coding = dot11_cap.ldpc_coding; + he_cap->he_1x_ltf_800_gi_ppdu = dot11_cap.he_1x_ltf_800_gi_ppdu; + he_cap->midamble_tx_rx_max_nsts = + dot11_cap.midamble_tx_rx_max_nsts; + he_cap->he_4x_ltf_3200_gi_ndp = dot11_cap.he_4x_ltf_3200_gi_ndp; + he_cap->tb_ppdu_tx_stbc_lt_80mhz = + dot11_cap.tb_ppdu_tx_stbc_lt_80mhz; + he_cap->rx_stbc_lt_80mhz = dot11_cap.rx_stbc_lt_80mhz; + he_cap->tb_ppdu_tx_stbc_gt_80mhz = + dot11_cap.tb_ppdu_tx_stbc_gt_80mhz; + he_cap->rx_stbc_gt_80mhz = dot11_cap.rx_stbc_gt_80mhz; + he_cap->doppler = dot11_cap.doppler; + he_cap->ul_mu = dot11_cap.ul_mu; + he_cap->dcm_enc_tx = dot11_cap.dcm_enc_tx; + he_cap->dcm_enc_rx = dot11_cap.dcm_enc_rx; + he_cap->ul_he_mu = dot11_cap.ul_he_mu; + he_cap->su_beamformer = dot11_cap.su_beamformer; + + he_cap->su_beamformee = dot11_cap.su_beamformee; + he_cap->mu_beamformer = dot11_cap.mu_beamformer; + he_cap->bfee_sts_lt_80 = dot11_cap.bfee_sts_lt_80; + he_cap->bfee_sts_gt_80 = dot11_cap.bfee_sts_gt_80; + he_cap->num_sounding_lt_80 = dot11_cap.num_sounding_lt_80; + he_cap->num_sounding_gt_80 = dot11_cap.num_sounding_gt_80; + he_cap->su_feedback_tone16 = dot11_cap.su_feedback_tone16; + he_cap->mu_feedback_tone16 = dot11_cap.mu_feedback_tone16; + he_cap->codebook_su = dot11_cap.codebook_su; + he_cap->codebook_mu = dot11_cap.codebook_mu; + he_cap->beamforming_feedback = dot11_cap.beamforming_feedback; + he_cap->he_er_su_ppdu = dot11_cap.he_er_su_ppdu; + he_cap->dl_mu_mimo_part_bw = dot11_cap.dl_mu_mimo_part_bw; + he_cap->ppet_present = dot11_cap.ppet_present; + he_cap->srp = dot11_cap.srp; + he_cap->power_boost = dot11_cap.power_boost; + + he_cap->he_ltf_800_gi_4x = dot11_cap.he_ltf_800_gi_4x; + he_cap->max_nc = dot11_cap.max_nc; + he_cap->er_he_ltf_800_gi_4x = dot11_cap.er_he_ltf_800_gi_4x; + he_cap->he_ppdu_20_in_40Mhz_2G = + dot11_cap.he_ppdu_20_in_40Mhz_2G; + he_cap->he_ppdu_20_in_160_80p80Mhz = + dot11_cap.he_ppdu_20_in_160_80p80Mhz; + he_cap->he_ppdu_80_in_160_80p80Mhz = + dot11_cap.he_ppdu_80_in_160_80p80Mhz; + he_cap->er_1x_he_ltf_gi = + dot11_cap.er_1x_he_ltf_gi; + he_cap->midamble_tx_rx_1x_he_ltf = + dot11_cap.midamble_tx_rx_1x_he_ltf; + he_cap->reserved2 = dot11_cap.reserved2; + + he_cap->rx_he_mcs_map_lt_80 = dot11_cap.rx_he_mcs_map_lt_80; + he_cap->tx_he_mcs_map_lt_80 = dot11_cap.tx_he_mcs_map_lt_80; + if (dot11_cap.chan_width_2) { + he_cap->rx_he_mcs_map_160 = + *((uint16_t *)dot11_cap.rx_he_mcs_map_160); + he_cap->tx_he_mcs_map_160 = + *((uint16_t *)dot11_cap.tx_he_mcs_map_160); + ie_start[1] += HE_CAP_160M_MCS_MAP_LEN; + } + if (dot11_cap.chan_width_3) { + he_cap->rx_he_mcs_map_80_80 = + *((uint16_t *)dot11_cap.rx_he_mcs_map_80_80); + he_cap->tx_he_mcs_map_80_80 = + *((uint16_t *)dot11_cap.tx_he_mcs_map_80_80); + ie_start[1] += HE_CAP_80P80_MCS_MAP_LEN; + } + } +} + +static void lim_intersect_he_ch_width_2g(struct mac_context *mac, + struct he_capability_info *he_cap) +{ + struct wlan_objmgr_psoc *psoc; + uint32_t cbm_24ghz; + QDF_STATUS ret; + + psoc = mac->psoc; + if (!psoc) + return; + + ret = ucfg_mlme_get_channel_bonding_24ghz(psoc, &cbm_24ghz); + if (QDF_IS_STATUS_ERROR(ret)) + return; + + pe_debug("channel bonding mode 2.4GHz %d", cbm_24ghz); + + if (!cbm_24ghz) { + /* B0: 40Mhz channel width in the 2.4GHz band */ + he_cap->chan_width = HE_CH_WIDTH_CLR_BIT(he_cap->chan_width, 0); + he_cap->he_ppdu_20_in_40Mhz_2G = 0; + } + + pe_debug("HE cap: chan_width: 0x%07x he_ppdu_20_in_40Mhz_2G %d", + he_cap->chan_width, he_cap->he_ppdu_20_in_40Mhz_2G); +} + +static uint8_t lim_set_he_caps_ppet(struct mac_context *mac, uint8_t *ie, + enum cds_band_type band) +{ + uint8_t ppe_th[WNI_CFG_HE_PPET_LEN] = {0}; + /* Append at the end after ID + LEN + OUI + IE_Data */ + uint8_t offset = ie[1] + 1 + 1 + 1; + uint8_t num_ppe_th; + + if (band == CDS_BAND_2GHZ) + qdf_mem_copy(ppe_th, mac->mlme_cfg->he_caps.he_ppet_2g, + WNI_CFG_HE_PPET_LEN); + else if (band == CDS_BAND_5GHZ) + qdf_mem_copy(ppe_th, mac->mlme_cfg->he_caps.he_ppet_5g, + WNI_CFG_HE_PPET_LEN); + else + return 0; + + num_ppe_th = lim_truncate_ppet(ppe_th, WNI_CFG_HE_PPET_LEN); + + qdf_mem_copy(ie + offset, ppe_th, num_ppe_th); + + return num_ppe_th; +} + +QDF_STATUS lim_send_he_caps_ie(struct mac_context *mac_ctx, + struct pe_session *session, + enum QDF_OPMODE device_mode, + uint8_t vdev_id) +{ + uint8_t he_caps[SIR_MAC_HE_CAP_MIN_LEN + HE_CAP_OUI_LEN + + HE_CAP_160M_MCS_MAP_LEN + HE_CAP_80P80_MCS_MAP_LEN + + WNI_CFG_HE_PPET_LEN]; + struct he_capability_info *he_cap; + QDF_STATUS status_5g, status_2g; + uint8_t he_cap_total_len = SIR_MAC_HE_CAP_MIN_LEN + HE_CAP_OUI_LEN + + HE_CAP_160M_MCS_MAP_LEN + + HE_CAP_80P80_MCS_MAP_LEN; + uint8_t num_ppe_th = 0; + + /* Sending only minimal info(no PPET) to FW now, update if required */ + qdf_mem_zero(he_caps, he_cap_total_len); + he_caps[0] = DOT11F_EID_HE_CAP; + he_caps[1] = SIR_MAC_HE_CAP_MIN_LEN; + qdf_mem_copy(&he_caps[2], HE_CAP_OUI_TYPE, HE_CAP_OUI_SIZE); + lim_set_he_caps(mac_ctx, session, he_caps, he_cap_total_len); + he_cap = (struct he_capability_info *) (&he_caps[2 + HE_CAP_OUI_SIZE]); + if(device_mode == QDF_NDI_MODE) { + he_cap->su_beamformee = 0; + he_cap->su_beamformer = 0; + he_cap->mu_beamformer = 0; + he_cap->bfee_sts_gt_80 = 0; + he_cap->bfee_sts_lt_80 = 0; + he_cap->num_sounding_gt_80 = 0; + he_cap->num_sounding_lt_80 = 0; + he_cap->su_feedback_tone16 = 0; + he_cap->mu_feedback_tone16 = 0; + } + + if (he_cap->ppet_present) + num_ppe_th = lim_set_he_caps_ppet(mac_ctx, he_caps, + CDS_BAND_5GHZ); + + status_5g = lim_send_ie(mac_ctx, vdev_id, DOT11F_EID_HE_CAP, + CDS_BAND_5GHZ, &he_caps[2], + he_caps[1] + 1 + num_ppe_th); + if (QDF_IS_STATUS_ERROR(status_5g)) + pe_err("Unable send HE Cap IE for 5GHZ band, status: %d", + status_5g); + + lim_intersect_he_ch_width_2g(mac_ctx, he_cap); + + if (he_cap->ppet_present) + num_ppe_th = lim_set_he_caps_ppet(mac_ctx, he_caps, + CDS_BAND_2GHZ); + + status_2g = lim_send_ie(mac_ctx, vdev_id, DOT11F_EID_HE_CAP, + CDS_BAND_2GHZ, &he_caps[2], + he_caps[1] + 1 + num_ppe_th); + if (QDF_IS_STATUS_ERROR(status_2g)) + pe_err("Unable send HE Cap IE for 2GHZ band, status: %d", + status_2g); + + if (QDF_IS_STATUS_SUCCESS(status_2g) && + QDF_IS_STATUS_SUCCESS(status_5g)) + return QDF_STATUS_SUCCESS; + + return QDF_STATUS_E_FAILURE; +} + +/** + * lim_populate_he_mcs_per_bw() - pouldate HE mcs set per BW (le 80, 160, 80+80) + * @mac_ctx: Global MAC context + * @self_rx: self rx mcs set + * @self_tx: self tx mcs set + * @peer_rx: peer rx mcs set + * @peer_tx: peer tx mcs set + * @nss: nss + * @cfg_rx_param: rx wni param to read + * @cfg_tx_param: tx wni param to read + * + * MCS values are interpreted as in IEEE 11ax-D1.4 spec onwards + * +-----------------------------------------------------+ + * | SS8 | SS7 | SS6 | SS5 | SS4 | SS3 | SS2 | SS1 | + * +-----------------------------------------------------+ + * | 15-14 | 13-12 | 11-10 | 9-8 | 7-6 | 5-4 | 3-2 | 1-0 | + * +-----------------------------------------------------+ + * + * Return: status of operation + */ +static QDF_STATUS lim_populate_he_mcs_per_bw(struct mac_context *mac_ctx, + uint16_t *supp_rx_mcs, uint16_t *supp_tx_mcs, + uint16_t peer_rx, uint16_t peer_tx, uint8_t nss, + uint16_t rx_mcs, uint16_t tx_mcs) +{ + + pe_debug("peer rates: rx_mcs - 0x%04x tx_mcs - 0x%04x", + peer_rx, peer_tx); + + *supp_rx_mcs = rx_mcs; + *supp_tx_mcs = tx_mcs; + + *supp_tx_mcs = HE_INTERSECT_MCS(*supp_rx_mcs, peer_tx); + *supp_rx_mcs = HE_INTERSECT_MCS(*supp_tx_mcs, peer_rx); + + if (nss == NSS_1x1_MODE) { + *supp_rx_mcs |= HE_MCS_INV_MSK_4_NSS(1); + *supp_tx_mcs |= HE_MCS_INV_MSK_4_NSS(1); + } + /* if nss is 2, disable higher NSS */ + if (nss == NSS_2x2_MODE) { + *supp_rx_mcs |= (HE_MCS_INV_MSK_4_NSS(1) & + HE_MCS_INV_MSK_4_NSS(2)); + *supp_tx_mcs |= (HE_MCS_INV_MSK_4_NSS(1) & + HE_MCS_INV_MSK_4_NSS(2)); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_populate_he_mcs_set(struct mac_context *mac_ctx, + struct supported_rates *rates, + tDot11fIEhe_cap *peer_he_caps, + struct pe_session *session_entry, uint8_t nss) +{ + bool support_2x2 = false; + uint32_t self_sta_dot11mode = mac_ctx->mlme_cfg->dot11_mode.dot11_mode; + + if (!IS_DOT11_MODE_HE(self_sta_dot11mode)) + return QDF_STATUS_SUCCESS; + + if ((!peer_he_caps) || (!peer_he_caps->present)) { + pe_debug("peer not he capable or he_caps NULL"); + return QDF_STATUS_SUCCESS; + } + + if (!session_entry) { + pe_err("session is NULL"); + return QDF_STATUS_E_FAILURE; + } + + pe_debug("PEER: lt 80: rx 0x%04x tx 0x%04x, 160: rx 0x%04x tx 0x%04x, 80+80: rx 0x%04x tx 0x%04x", + peer_he_caps->rx_he_mcs_map_lt_80, + peer_he_caps->tx_he_mcs_map_lt_80, + (*(uint16_t *)peer_he_caps->rx_he_mcs_map_160), + (*(uint16_t *)peer_he_caps->tx_he_mcs_map_160), + (*(uint16_t *)peer_he_caps->rx_he_mcs_map_80_80), + (*(uint16_t *)peer_he_caps->tx_he_mcs_map_80_80)); + + if (session_entry->nss == NSS_2x2_MODE) { + if (mac_ctx->lteCoexAntShare && + wlan_reg_is_24ghz_ch_freq(session_entry->curr_op_freq)) { + if (IS_2X2_CHAIN(session_entry->chainMask)) + support_2x2 = true; + else + pe_err("2x2 not enabled %d", + session_entry->chainMask); + } else { + support_2x2 = true; + } + } + + lim_populate_he_mcs_per_bw(mac_ctx, + &rates->rx_he_mcs_map_lt_80, &rates->tx_he_mcs_map_lt_80, + peer_he_caps->rx_he_mcs_map_lt_80, + peer_he_caps->tx_he_mcs_map_lt_80, nss, + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.rx_he_mcs_map_lt_80, + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.tx_he_mcs_map_lt_80); + if (session_entry->ch_width == CH_WIDTH_160MHZ) { + lim_populate_he_mcs_per_bw( + mac_ctx, &rates->rx_he_mcs_map_160, + &rates->tx_he_mcs_map_160, + *((uint16_t *)peer_he_caps->rx_he_mcs_map_160), + *((uint16_t *)peer_he_caps->tx_he_mcs_map_160), + nss, + *((uint16_t *)mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + rx_he_mcs_map_160), + *((uint16_t *)mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + tx_he_mcs_map_160)); + } else { + rates->tx_he_mcs_map_160 = HE_MCS_ALL_DISABLED; + rates->rx_he_mcs_map_160 = HE_MCS_ALL_DISABLED; + } + if (session_entry->ch_width == CH_WIDTH_80P80MHZ) { + lim_populate_he_mcs_per_bw( + mac_ctx, &rates->rx_he_mcs_map_80_80, + &rates->tx_he_mcs_map_80_80, + *((uint16_t *)peer_he_caps->rx_he_mcs_map_80_80), + *((uint16_t *)peer_he_caps->tx_he_mcs_map_80_80), nss, + *((uint16_t *)mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + rx_he_mcs_map_80_80), + *((uint16_t *)mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + tx_he_mcs_map_80_80)); + } else { + rates->tx_he_mcs_map_80_80 = HE_MCS_ALL_DISABLED; + rates->rx_he_mcs_map_80_80 = HE_MCS_ALL_DISABLED; + } + if (!support_2x2) { + /* disable 2 and higher NSS MCS sets */ + rates->rx_he_mcs_map_lt_80 |= HE_MCS_INV_MSK_4_NSS(1); + rates->tx_he_mcs_map_lt_80 |= HE_MCS_INV_MSK_4_NSS(1); + rates->rx_he_mcs_map_160 |= HE_MCS_INV_MSK_4_NSS(1); + rates->tx_he_mcs_map_160 |= HE_MCS_INV_MSK_4_NSS(1); + rates->rx_he_mcs_map_80_80 |= HE_MCS_INV_MSK_4_NSS(1); + rates->tx_he_mcs_map_80_80 |= HE_MCS_INV_MSK_4_NSS(1); + } + + pe_debug("lt 80: rx 0x%x tx 0x%x, 160: rx 0x%x tx 0x%x, 80_80: rx 0x%x tx 0x%x", + rates->rx_he_mcs_map_lt_80, rates->tx_he_mcs_map_lt_80, + rates->rx_he_mcs_map_160, rates->tx_he_mcs_map_160, + rates->rx_he_mcs_map_80_80, rates->tx_he_mcs_map_80_80); + + return QDF_STATUS_SUCCESS; +} +#endif + +#if defined(CONFIG_BAND_6GHZ) && defined(WLAN_FEATURE_11AX) +QDF_STATUS lim_send_he_6g_band_caps_ie(struct mac_context *mac_ctx, + struct pe_session *session, + uint8_t vdev_id) +{ + uint8_t he_6g_band_caps_ie[DOT11F_IE_HE_6GHZ_BAND_CAP_MIN_LEN + 3]; + tDot11fIEhe_6ghz_band_cap he_6g_band_cap; + QDF_STATUS status; + uint32_t size = 0; + uint32_t result; + + qdf_mem_zero(&he_6g_band_cap, sizeof(he_6g_band_cap)); + populate_dot11f_he_6ghz_cap(mac_ctx, session, &he_6g_band_cap); + if (!he_6g_band_cap.present) { + pe_debug("no HE 6g band cap for vdev %d", vdev_id); + return QDF_STATUS_SUCCESS; + } + + qdf_mem_zero(he_6g_band_caps_ie, sizeof(he_6g_band_caps_ie)); + result = dot11f_pack_ie_he_6ghz_band_cap(mac_ctx, &he_6g_band_cap, + he_6g_band_caps_ie, + sizeof(he_6g_band_caps_ie), + &size); + if (result != DOT11F_PARSE_SUCCESS) { + pe_err("pack erro for HE 6g band cap for vdev %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + pe_debug("send HE 6ghz band cap: 0x%01x 0x%01x for vdev %d", + he_6g_band_caps_ie[3], he_6g_band_caps_ie[4], + vdev_id); + status = lim_send_ie(mac_ctx, vdev_id, DOT11F_EID_HE_6GHZ_BAND_CAP, + CDS_BAND_5GHZ, &he_6g_band_caps_ie[2], + DOT11F_IE_HE_6GHZ_BAND_CAP_MIN_LEN + 1); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("Unable send HE 6g band Cap IE for 5GHZ band, status: %d", + status); + + return status; +} +#endif + +int +lim_assoc_rej_get_remaining_delta(struct sir_rssi_disallow_lst *node) +{ + qdf_time_t cur_time; + uint32_t time_diff; + + cur_time = qdf_do_div(qdf_get_monotonic_boottime(), + QDF_MC_TIMER_TO_MS_UNIT); + time_diff = cur_time - node->time_during_rejection; + + return node->retry_delay - time_diff; +} + +QDF_STATUS +lim_rem_blacklist_entry_with_lowest_delta(qdf_list_t *list) +{ + struct sir_rssi_disallow_lst *oldest_node = NULL; + struct sir_rssi_disallow_lst *cur_node; + qdf_list_node_t *cur_list = NULL; + qdf_list_node_t *next_list = NULL; + + qdf_list_peek_front(list, &cur_list); + while (cur_list) { + cur_node = qdf_container_of(cur_list, + struct sir_rssi_disallow_lst, node); + if (!oldest_node || + (lim_assoc_rej_get_remaining_delta(oldest_node) > + lim_assoc_rej_get_remaining_delta(cur_node))) + oldest_node = cur_node; + + qdf_list_peek_next(list, cur_list, &next_list); + cur_list = next_list; + next_list = NULL; + } + + if (oldest_node) { + pe_debug("remove node "QDF_MAC_ADDR_FMT" with lowest delta %d", + QDF_MAC_ADDR_REF(oldest_node->bssid.bytes), + lim_assoc_rej_get_remaining_delta(oldest_node)); + qdf_list_remove_node(list, &oldest_node->node); + qdf_mem_free(oldest_node); + return QDF_STATUS_SUCCESS; + } + + return QDF_STATUS_E_INVAL; +} + +void +lim_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev, + struct sir_rssi_disallow_lst *entry) +{ + struct reject_ap_info ap_info; + + ap_info.bssid = entry->bssid; + ap_info.reject_ap_type = DRIVER_RSSI_REJECT_TYPE; + ap_info.rssi_reject_params.expected_rssi = entry->expected_rssi; + ap_info.rssi_reject_params.retry_delay = entry->retry_delay; + ap_info.reject_reason = entry->reject_reason; + ap_info.source = entry->source; + ap_info.rssi_reject_params.received_time = entry->received_time; + ap_info.rssi_reject_params.original_timeout = entry->original_timeout; + /* Add this ap info to the rssi reject ap type in blacklist manager */ + wlan_blm_add_bssid_to_reject_list(pdev, &ap_info); +} + +void lim_decrement_pending_mgmt_count(struct mac_context *mac_ctx) +{ + qdf_spin_lock(&mac_ctx->sys.bbt_mgmt_lock); + if (!mac_ctx->sys.sys_bbt_pending_mgmt_count) { + qdf_spin_unlock(&mac_ctx->sys.bbt_mgmt_lock); + return; + } + mac_ctx->sys.sys_bbt_pending_mgmt_count--; + qdf_spin_unlock(&mac_ctx->sys.bbt_mgmt_lock); +} + +bool lim_check_if_vendor_oui_match(struct mac_context *mac_ctx, + uint8_t *oui, uint8_t oui_len, + uint8_t *ie, uint8_t ie_len) +{ + uint8_t *ptr = ie; + uint8_t elem_id; + + if (!ie || 0 == ie_len) { + pe_err("IE Null or ie len zero %d", ie_len); + return false; + } + + elem_id = *ie; + + if (elem_id == WLAN_ELEMID_VENDOR && + !qdf_mem_cmp(&ptr[2], oui, oui_len)) + return true; + else + return false; +} + +QDF_STATUS lim_util_get_type_subtype(void *pkt, uint8_t *type, + uint8_t *subtype) +{ + cds_pkt_t *cds_pkt; + QDF_STATUS status; + tpSirMacMgmtHdr hdr; + uint8_t *rxpktinfor; + + cds_pkt = (cds_pkt_t *) pkt; + if (!cds_pkt) { + pe_err("NULL packet received"); + return QDF_STATUS_E_FAILURE; + } + status = + wma_ds_peek_rx_packet_info(cds_pkt, (void *)&rxpktinfor, false); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("Failed extract cds packet. status %d", status); + return QDF_STATUS_E_FAILURE; + } + + hdr = WMA_GET_RX_MAC_HEADER(rxpktinfor); + if (hdr->fc.type == SIR_MAC_MGMT_FRAME) { + pe_debug("RxBd: %pK mHdr: %pK Type: %d Subtype: %d SizeFC: %zu", + rxpktinfor, hdr, hdr->fc.type, hdr->fc.subType, + sizeof(tSirMacFrameCtl)); + *type = hdr->fc.type; + *subtype = hdr->fc.subType; + } else { + pe_err("Not a management packet type %d", hdr->fc.type); + return QDF_STATUS_E_INVAL; + } + return QDF_STATUS_SUCCESS; +} + +enum rateid lim_get_min_session_txrate(struct pe_session *session) +{ + enum rateid rid = RATEID_DEFAULT; + uint8_t min_rate = SIR_MAC_RATE_54, curr_rate, i; + tSirMacRateSet *rateset = &session->rateSet; + + if (!session) + return rid; + + for (i = 0; i < rateset->numRates; i++) { + /* Ignore MSB - set to indicate basic rate */ + curr_rate = rateset->rate[i] & 0x7F; + min_rate = (curr_rate < min_rate) ? curr_rate : min_rate; + } + pe_debug("supported min_rate: %0x(%d)", min_rate, min_rate); + + switch (min_rate) { + case SIR_MAC_RATE_1: + rid = RATEID_1MBPS; + break; + case SIR_MAC_RATE_2: + rid = RATEID_2MBPS; + break; + case SIR_MAC_RATE_5_5: + rid = RATEID_5_5MBPS; + break; + case SIR_MAC_RATE_11: + rid = RATEID_11MBPS; + break; + case SIR_MAC_RATE_6: + rid = RATEID_6MBPS; + break; + case SIR_MAC_RATE_9: + rid = RATEID_9MBPS; + break; + case SIR_MAC_RATE_12: + rid = RATEID_12MBPS; + break; + case SIR_MAC_RATE_18: + rid = RATEID_18MBPS; + break; + case SIR_MAC_RATE_24: + rid = RATEID_24MBPS; + break; + case SIR_MAC_RATE_36: + rid = RATEID_36MBPS; + break; + case SIR_MAC_RATE_48: + rid = RATEID_48MBPS; + break; + case SIR_MAC_RATE_54: + rid = RATEID_54MBPS; + break; + default: + rid = RATEID_DEFAULT; + break; + } + + return rid; +} + +void lim_send_sme_mgmt_frame_ind(struct mac_context *mac_ctx, uint8_t frame_type, + uint8_t *frame, uint32_t frame_len, + uint16_t session_id, uint32_t rx_freq, + struct pe_session *psession_entry, + int8_t rx_rssi, enum rxmgmt_flags rx_flags) +{ + tpSirSmeMgmtFrameInd sme_mgmt_frame = NULL; + uint16_t length; + + length = sizeof(tSirSmeMgmtFrameInd) + frame_len; + + sme_mgmt_frame = qdf_mem_malloc(length); + if (!sme_mgmt_frame) + return; + + if (qdf_is_macaddr_broadcast( + (struct qdf_mac_addr *)(frame + 4)) && + !session_id) { + pe_debug("Broadcast action frame"); + session_id = SME_SESSION_ID_BROADCAST; + } + + sme_mgmt_frame->frame_len = frame_len; + sme_mgmt_frame->sessionId = session_id; + sme_mgmt_frame->frameType = frame_type; + sme_mgmt_frame->rxRssi = rx_rssi; + sme_mgmt_frame->rx_freq = rx_freq; + sme_mgmt_frame->rx_flags = rx_flags; + + qdf_mem_zero(sme_mgmt_frame->frameBuf, frame_len); + qdf_mem_copy(sme_mgmt_frame->frameBuf, frame, frame_len); + + if (mac_ctx->mgmt_frame_ind_cb) + mac_ctx->mgmt_frame_ind_cb(sme_mgmt_frame); + else + pe_debug_rl("Management indication callback not registered!!"); + qdf_mem_free(sme_mgmt_frame); + return; +} + +void +lim_send_dfs_chan_sw_ie_update(struct mac_context *mac_ctx, struct pe_session *session) +{ + /* Update the beacon template and send to FW */ + if (sch_set_fixed_beacon_fields(mac_ctx, session) != + QDF_STATUS_SUCCESS) { + pe_err("Unable to set CSA IE in beacon"); + return; + } + + /* Send update beacon template message */ + lim_send_beacon_ind(mac_ctx, session, REASON_CHANNEL_SWITCH); + pe_debug("Updated CSA IE, IE COUNT: %d", + session->gLimChannelSwitch.switchCount); +} + +void lim_process_ap_ecsa_timeout(void *data) +{ + struct pe_session *session = (struct pe_session *)data; + struct mac_context *mac_ctx; + uint8_t bcn_int, ch_width; + uint32_t ch_freq; + QDF_STATUS status; + + if (!session || !session->valid) { + pe_err("Session is not valid"); + return; + } + + mac_ctx = session->mac_ctx; + + if (!session->dfsIncludeChanSwIe) { + pe_debug("session->dfsIncludeChanSwIe not set"); + return; + } + + /* Stop the timer if already running */ + qdf_mc_timer_stop(&session->ap_ecsa_timer); + + if (session->gLimChannelSwitch.switchCount) + /* Decrement the beacon switch count */ + session->gLimChannelSwitch.switchCount--; + + /* + * Send only g_sap_chanswitch_beacon_cnt beacons with CSA IE Set in + * when a radar is detected + */ + if (session->gLimChannelSwitch.switchCount > 0) { + /* Send the next beacon with updated CSA IE count */ + lim_send_dfs_chan_sw_ie_update(mac_ctx, session); + + ch_freq = session->gLimChannelSwitch.sw_target_freq; + ch_width = session->gLimChannelSwitch.ch_width; + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_beacon_tx_enhanced) { + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(ch_freq)) { + send_extended_chan_switch_action_frame + (mac_ctx, ch_freq, ch_width, + session); + } else { + /* Send Action frame after updating beacon */ + lim_send_chan_switch_action_frame + (mac_ctx, ch_freq, ch_width, + session); + } + } + + /* Restart the timer */ + if (session->beaconParams.beaconInterval) + bcn_int = session->beaconParams.beaconInterval; + else + bcn_int = MLME_CFG_BEACON_INTERVAL_DEF; + + status = qdf_mc_timer_start(&session->ap_ecsa_timer, + bcn_int); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("cannot start ap_ecsa_timer"); + lim_process_ap_ecsa_timeout(session); + } + } else { + tSirSmeCSAIeTxCompleteRsp *chan_switch_tx_rsp; + struct scheduler_msg msg = {0}; + uint8_t length = sizeof(*chan_switch_tx_rsp); + + /* Done with CSA IE update, send response back to SME */ + session->gLimChannelSwitch.switchCount = 0; + if (mac_ctx->sap.SapDfsInfo.disable_dfs_ch_switch == false) + session->gLimChannelSwitch.switchMode = 0; + session->dfsIncludeChanSwIe = false; + session->dfsIncludeChanWrapperIe = false; + + chan_switch_tx_rsp = qdf_mem_malloc(length); + if (!chan_switch_tx_rsp) + return; + + chan_switch_tx_rsp->sessionId = session->smeSessionId; + chan_switch_tx_rsp->chanSwIeTxStatus = QDF_STATUS_SUCCESS; + + msg.type = eWNI_SME_DFS_CSAIE_TX_COMPLETE_IND; + msg.bodyptr = chan_switch_tx_rsp; + + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &msg); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(chan_switch_tx_rsp); + } +} + +QDF_STATUS lim_sta_mlme_vdev_start_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct mac_context *mac_ctx; + enum vdev_assoc_type assoc_type; + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + pe_err("mac_ctx is NULL"); + if (data) + qdf_mem_free(data); + return QDF_STATUS_E_INVAL; + } + + assoc_type = mlme_get_assoc_type(vdev_mlme->vdev); + switch (assoc_type) { + case VDEV_ASSOC: + lim_process_mlm_join_req(mac_ctx, (tLimMlmJoinReq *)data); + break; + case VDEV_REASSOC: + lim_process_mlm_reassoc_req(mac_ctx, (tLimMlmReassocReq *)data); + break; + case VDEV_FT_REASSOC: + lim_process_mlm_ft_reassoc_req(mac_ctx, + (tLimMlmReassocReq *)data); + break; + default: + pe_err("assoc_type %d is invalid", assoc_type); + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_sta_mlme_vdev_restart_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct pe_session *session; + + session = (struct pe_session *)data; + if (!session) { + pe_err("Invalid session"); + return QDF_STATUS_E_INVAL; + } + if (!vdev_mlme) { + pe_err("vdev_mlme is NULL"); + return QDF_STATUS_E_INVAL; + } + if (!data) { + pe_err("event_data is NULL"); + return QDF_STATUS_E_INVAL; + } + if (mlme_is_chan_switch_in_progress(vdev_mlme->vdev)) { + switch (session->channelChangeReasonCode) { + case LIM_SWITCH_CHANNEL_OPERATION: + __lim_process_channel_switch_timeout(session); + break; + case LIM_SWITCH_CHANNEL_HT_WIDTH: + lim_ht_switch_chnl_params(session); + break; + case LIM_SWITCH_CHANNEL_REASSOC: + lim_send_switch_chnl_params(session->mac_ctx, session); + break; + default: + break; + } + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_sta_mlme_vdev_stop_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool connection_fail = false; + enum vdev_assoc_type assoc_type; + + if (!vdev_mlme) { + pe_err("vdev_mlme is NULL"); + return QDF_STATUS_E_INVAL; + } + if (!data) { + pe_err("event_data is NULL"); + return QDF_STATUS_E_INVAL; + } + + connection_fail = mlme_is_connection_fail(vdev_mlme->vdev); + pe_debug("Send vdev stop, connection_fail %d", connection_fail); + if (connection_fail) { + assoc_type = mlme_get_assoc_type(vdev_mlme->vdev); + switch (assoc_type) { + case VDEV_ASSOC: + status = + lim_sta_handle_connect_fail((join_params *)data); + break; + case VDEV_REASSOC: + case VDEV_FT_REASSOC: + status = lim_sta_reassoc_error_handler( + (struct reassoc_params *)data); + break; + default: + pe_info("Invalid assoc_type %d", assoc_type); + status = QDF_STATUS_E_INVAL; + break; + } + mlme_set_connection_fail(vdev_mlme->vdev, false); + } else { + status = lim_sta_send_del_bss((struct pe_session *)data); + } + + return status; +} + +QDF_STATUS lim_sta_mlme_vdev_req_fail(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + enum vdev_assoc_type assoc_type; + + if (!vdev_mlme) { + pe_err("vdev_mlme is NULL"); + return QDF_STATUS_E_INVAL; + } + if (!data) { + pe_err("event_data is NULL"); + return QDF_STATUS_E_INVAL; + } + + assoc_type = mlme_get_assoc_type(vdev_mlme->vdev); + switch (assoc_type) { + case VDEV_ASSOC: + status = lim_sta_handle_connect_fail((join_params *)data); + break; + case VDEV_REASSOC: + case VDEV_FT_REASSOC: + status = lim_sta_reassoc_error_handler( + (struct reassoc_params *)data); + break; + default: + pe_info("Invalid assoc_type %d", assoc_type); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +void lim_send_beacon(struct mac_context *mac_ctx, struct pe_session *session) +{ + if (wlan_vdev_mlme_get_state(session->vdev) == + WLAN_VDEV_S_DFS_CAC_WAIT) + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_DFS_CAC_COMPLETED, + sizeof(*session), session); + else if (mac_ctx->mlme_cfg->dfs_cfg.dfs_disable_channel_switch && + (wlan_vdev_mlme_get_substate(session->vdev) == + WLAN_VDEV_SS_SUSPEND_CSA_RESTART)) + wlan_vdev_mlme_sm_deliver_evt( + session->vdev, + WLAN_VDEV_SM_EV_CHAN_SWITCH_DISABLED, + sizeof(*session), session); + else + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START_SUCCESS, + sizeof(*session), session); +} + +void lim_ndi_mlme_vdev_up_transition(struct pe_session *session) +{ + if (!LIM_IS_NDI_ROLE(session)) + return; + + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START_SUCCESS, + sizeof(*session), session); +} + +QDF_STATUS lim_sap_move_to_cac_wait_state(struct pe_session *session) +{ + QDF_STATUS status; + + status = + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_DFS_CAC_WAIT, + sizeof(*session), session); + return status; +} + +QDF_STATUS lim_ap_mlme_vdev_start_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct pe_session *session; + tSirResultCodes ret; + tpLimMlmStartReq start_req = (tLimMlmStartReq *)data; + struct mac_context *mac_ctx; + + if (!data) { + pe_err("data is NULL"); + return QDF_STATUS_E_INVAL; + } + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + pe_err("mac_ctx is NULL"); + return QDF_STATUS_E_INVAL; + } + + session = pe_find_session_by_session_id(mac_ctx, + start_req->sessionId); + if (!session) { + pe_err("session is NULL"); + return QDF_STATUS_E_INVAL; + } + + if (LIM_IS_IBSS_ROLE(session) && + session->mac_ctx->lim.gLimIbssCoalescingHappened) { + ibss_bss_add(session->mac_ctx, session); + ret = lim_mlm_add_bss(session->mac_ctx, start_req, session); + if (ret != eSIR_SME_SUCCESS) { + pe_err("AddBss failure"); + return QDF_STATUS_E_INVAL; + } + } else { + lim_process_mlm_start_req(session->mac_ctx, start_req); + } + + return QDF_STATUS_SUCCESS; +} + +static inline void lim_send_csa_restart_resp(struct mac_context *mac_ctx, + struct pe_session *session) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + msg.type = eWNI_SME_CSA_RESTART_RSP; + msg.bodyptr = NULL; + msg.bodyval = session->smeSessionId; + + status = scheduler_post_message(QDF_MODULE_ID_PE, QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &msg); +} + +QDF_STATUS lim_ap_mlme_vdev_update_beacon(struct vdev_mlme_obj *vdev_mlme, + enum beacon_update_op op, + uint16_t data_len, void *data) +{ + struct pe_session *session; + + if (!data) { + pe_err("event_data is NULL"); + return QDF_STATUS_E_INVAL; + } + session = (struct pe_session *)data; + if (LIM_IS_NDI_ROLE(session)) + return QDF_STATUS_SUCCESS; + + if (op == BEACON_INIT) + lim_send_beacon_ind(session->mac_ctx, session, REASON_DEFAULT); + else if (op == BEACON_UPDATE) + lim_send_beacon_ind(session->mac_ctx, + session, + REASON_CONFIG_UPDATE); + else if (op == BEACON_CSA) + lim_send_csa_restart_resp(session->mac_ctx, session); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_ap_mlme_vdev_up_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + struct pe_session *session = (struct pe_session *)data; + + if (!session) { + pe_err("session is NULL"); + return QDF_STATUS_E_INVAL; + } + if (LIM_IS_NDI_ROLE(session)) + return QDF_STATUS_SUCCESS; + + + msg.type = SIR_HAL_SEND_AP_VDEV_UP; + msg.bodyval = session->smeSessionId; + + status = scheduler_post_message(QDF_MODULE_ID_PE, QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + + return status; +} + +QDF_STATUS lim_ap_mlme_vdev_disconnect_peers(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct pe_session *session = (struct pe_session *)data; + + if (!data) { + pe_err("data is NULL"); + return QDF_STATUS_E_INVAL; + } + + if (LIM_IS_IBSS_ROLE(session)) + lim_ibss_delete_all_peers(session->mac_ctx, session); + else + lim_delete_all_peers(session); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_ap_mlme_vdev_stop_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct pe_session *session = (struct pe_session *)data; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!data) { + pe_err("data is NULL"); + return QDF_STATUS_E_INVAL; + } + + if (LIM_IS_IBSS_ROLE(session) && + session->mac_ctx->lim.gLimIbssCoalescingHappened) + ibss_bss_delete(session->mac_ctx, session); + else + status = lim_send_vdev_stop(session); + + return status; +} + +QDF_STATUS lim_ap_mlme_vdev_restart_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct pe_session *session = (struct pe_session *)data; + + if (!data) { + pe_err("data is NULL"); + return QDF_STATUS_E_INVAL; + } + + if (ap_mlme_is_hidden_ssid_restart_in_progress(vdev_mlme->vdev)) + lim_send_vdev_restart(session->mac_ctx, session, + session->smeSessionId); + else + lim_send_switch_chnl_params(session->mac_ctx, session); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_ap_mlme_vdev_start_req_failed(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct mac_context *mac_ctx; + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + pe_err("mac_ctx is NULL"); + if (data) + qdf_mem_free(data); + return QDF_STATUS_E_INVAL; + } + + lim_process_mlm_start_cnf(mac_ctx, data); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_mon_mlme_vdev_start_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + struct mac_context *mac_ctx; + struct pe_session *session = (struct pe_session *)data; + + if (!data) { + pe_err("data is NULL"); + return QDF_STATUS_E_INVAL; + } + + mac_ctx = session->mac_ctx; + if (!mac_ctx) { + pe_err("mac_ctx is NULL"); + return QDF_STATUS_E_INVAL; + } + + lim_send_switch_chnl_params(mac_ctx, session); + + return QDF_STATUS_SUCCESS; +} + +void lim_send_start_bss_confirm(struct mac_context *mac_ctx, + tLimMlmStartCnf *start_cnf) +{ + if (start_cnf->resultCode == eSIR_SME_SUCCESS) { + lim_post_sme_message(mac_ctx, LIM_MLM_START_CNF, + (uint32_t *)start_cnf); + } else { + struct pe_session *session; + + session = pe_find_session_by_session_id(mac_ctx, + start_cnf->sessionId); + if (!session) { + pe_err("session is NULL"); + return; + } + mlme_set_vdev_start_failed(session->vdev, true); + wlan_vdev_mlme_sm_deliver_evt(session->vdev, + WLAN_VDEV_SM_EV_START_REQ_FAIL, + sizeof(*start_cnf), start_cnf); + } +} + +QDF_STATUS lim_get_capability_info(struct mac_context *mac, uint16_t *pcap, + struct pe_session *pe_session) +{ + uint32_t val = 0; + tpSirMacCapabilityInfo pcap_info; + + *pcap = 0; + pcap_info = (tpSirMacCapabilityInfo)pcap; + + if (LIM_IS_IBSS_ROLE(pe_session)) + pcap_info->ibss = 1; /* IBSS bit */ + else if (LIM_IS_AP_ROLE(pe_session) || + LIM_IS_STA_ROLE(pe_session)) + pcap_info->ess = 1; /* ESS bit */ + else if (LIM_IS_P2P_DEVICE_ROLE(pe_session) || + LIM_IS_NDI_ROLE(pe_session)) { + pcap_info->ess = 0; + pcap_info->ibss = 0; + } else + pe_warn("can't get capability, role is UNKNOWN!!"); + + if (LIM_IS_AP_ROLE(pe_session)) { + val = pe_session->privacy; + } else { + /* PRIVACY bit */ + val = mac->mlme_cfg->wep_params.is_privacy_enabled; + } + if (val) + pcap_info->privacy = 1; + + /* Short preamble bit */ + if (mac->mlme_cfg->ht_caps.short_preamble) + pcap_info->shortPreamble = + mac->mlme_cfg->ht_caps.short_preamble; + + /* PBCC bit */ + pcap_info->pbcc = 0; + + /* Channel agility bit */ + pcap_info->channelAgility = 0; + /* If STA/AP operating in 11B mode, don't set rest of the + * capability info bits. + */ + if (pe_session->dot11mode == MLME_DOT11_MODE_11B) + return QDF_STATUS_SUCCESS; + + /* Short slot time bit */ + if (LIM_IS_AP_ROLE(pe_session)) { + pcap_info->shortSlotTime = pe_session->shortSlotTimeSupported; + } else { + /* When in STA mode, we need to check if short slot is + * enabled as well as check if the current operating + * mode is short slot time and then decide whether to + * enable short slot or not. It is safe to check both + * cfg values to determine short slot value in this + * funcn since this funcn is always used after assoc + * when these cfg values are already set based on + * peer's capability. Even in case of IBSS, its value + * is set to correct value either in delBSS as part of + * deleting the previous IBSS or in start BSS as part + * of coalescing + */ + if (mac->mlme_cfg->feature_flags.enable_short_slot_time_11g) { + pcap_info->shortSlotTime = + pe_session->shortSlotTimeSupported; + } + } + + /* Spectrum Management bit */ + if (!LIM_IS_IBSS_ROLE(pe_session) && pe_session->lim11hEnable) { + if (mac->mlme_cfg->gen.enabled_11h) + pcap_info->spectrumMgt = 1; + } + /* QoS bit */ + if (mac->mlme_cfg->wmm_params.qos_enabled) + pcap_info->qos = 1; + + /* APSD bit */ + if (mac->mlme_cfg->scoring.apsd_enabled) + pcap_info->apsd = 1; + + pcap_info->rrm = mac->rrm.rrmConfig.rrm_enabled; + pe_debug("RRM: %d", pcap_info->rrm); + /* DSSS-OFDM */ + /* FIXME : no config defined yet. */ + + /* Block ack bit */ + val = mac->mlme_cfg->feature_flags.enable_block_ack; + pcap_info->delayedBA = + (uint16_t) ((val >> WNI_CFG_BLOCK_ACK_ENABLED_DELAYED) & 1); + pcap_info->immediateBA = + (uint16_t) ((val >> WNI_CFG_BLOCK_ACK_ENABLED_IMMEDIATE) & 1); + + return QDF_STATUS_SUCCESS; +} + +void lim_flush_bssid(struct mac_context *mac_ctx, uint8_t *bssid) +{ + struct scan_filter *filter; + struct wlan_objmgr_pdev *pdev = NULL; + QDF_STATUS status; + + if (!bssid) + return; + + filter = qdf_mem_malloc(sizeof(*filter)); + if (!filter) + return; + + filter->num_of_bssid = 1; + qdf_mem_copy(filter->bssid_list[0].bytes, bssid, QDF_MAC_ADDR_SIZE); + + pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc, 0, WLAN_LEGACY_MAC_ID); + if (!pdev) { + pe_err("pdev is NULL"); + qdf_mem_free(filter); + return; + } + status = ucfg_scan_flush_results(pdev, filter); + + wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID); + + if (QDF_IS_STATUS_SUCCESS(status)) + pe_debug("Removed BSS entry:"QDF_MAC_ADDR_FMT" from scan cache", + QDF_MAC_ADDR_REF(bssid)); + + if (filter) + qdf_mem_free(filter); +} + +bool lim_is_sha384_akm(enum ani_akm_type akm) +{ + switch (akm) { + case ANI_AKM_TYPE_FILS_SHA384: + case ANI_AKM_TYPE_FT_FILS_SHA384: + case ANI_AKM_TYPE_SUITEB_EAP_SHA384: + case ANI_AKM_TYPE_FT_SUITEB_EAP_SHA384: + return true; + default: + return false; + } +} + +QDF_STATUS lim_set_ch_phy_mode(struct wlan_objmgr_vdev *vdev, uint8_t dot11mode) +{ + struct vdev_mlme_obj *mlme_obj; + struct wlan_channel *des_chan; + uint32_t chan_mode; + enum phy_ch_width ch_width; + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + uint16_t bw_val; + enum reg_wifi_band band; + uint8_t band_mask; + enum channel_state ch_state; + uint32_t start_ch_freq; + + if (!mac_ctx) { + pe_err("Invalid mac context"); + return QDF_STATUS_E_FAILURE; + } + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + pe_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + des_chan = vdev->vdev_mlme.des_chan; + /* + * Set ch_cfreq1 to ch_freq for 20Mhz. If BW is greater than 20 it + * will be updated from ch_freq_seg1. + */ + des_chan->ch_cfreq1 = des_chan->ch_freq; + band = wlan_reg_freq_to_band(des_chan->ch_freq); + band_mask = 1 << band; + ch_width = des_chan->ch_width; + bw_val = wlan_reg_get_bw_value(ch_width); + if (bw_val > 20) { + if (des_chan->ch_freq_seg1) { + des_chan->ch_cfreq1 = + wlan_reg_chan_band_to_freq(mac_ctx->pdev, + des_chan->ch_freq_seg1, + band_mask); + } else { + pe_err("Invalid cntr_freq for bw %d, drop to 20", + bw_val); + ch_width = CH_WIDTH_20MHZ; + bw_val = 20; + if (des_chan->ch_cfreq1) + des_chan->ch_freq_seg1 = + wlan_reg_freq_to_chan( + mac_ctx->pdev, + des_chan->ch_cfreq1); + } + } else if (des_chan->ch_cfreq1) { + des_chan->ch_freq_seg1 = + wlan_reg_freq_to_chan(mac_ctx->pdev, + des_chan->ch_cfreq1); + } + if (bw_val > 80) { + if (des_chan->ch_freq_seg2) { + des_chan->ch_cfreq2 = + wlan_reg_chan_band_to_freq(mac_ctx->pdev, + des_chan->ch_freq_seg2, + band_mask); + } else { + pe_err("Invalid cntr_freq for bw %d, drop to 80", + bw_val); + des_chan->ch_cfreq2 = 0; + des_chan->ch_freq_seg2 = 0; + ch_width = CH_WIDTH_80MHZ; + } + } else { + des_chan->ch_cfreq2 = 0; + des_chan->ch_freq_seg2 = 0; + } + + des_chan->ch_width = ch_width; + + des_chan->ch_flags = 0; + switch (ch_width) { + case CH_WIDTH_20MHZ: + des_chan->ch_flags |= IEEE80211_CHAN_VHT20; + break; + case CH_WIDTH_40MHZ: + des_chan->ch_flags |= IEEE80211_CHAN_VHT40PLUS; + break; + case CH_WIDTH_80MHZ: + des_chan->ch_flags |= IEEE80211_CHAN_VHT80; + break; + case CH_WIDTH_80P80MHZ: + des_chan->ch_flags |= IEEE80211_CHAN_VHT80_80; + break; + case CH_WIDTH_160MHZ: + des_chan->ch_flags |= IEEE80211_CHAN_VHT160; + break; + default: + break; + } + + if (WLAN_REG_IS_24GHZ_CH_FREQ(des_chan->ch_freq)) + des_chan->ch_flags |= IEEE80211_CHAN_2GHZ; + else + des_chan->ch_flags |= IEEE80211_CHAN_5GHZ; + + des_chan->ch_flagext = 0; + if (wlan_reg_is_dfs_for_freq(mac_ctx->pdev, des_chan->ch_freq)) + des_chan->ch_flagext |= IEEE80211_CHAN_DFS; + if (des_chan->ch_cfreq2) { + if (CH_WIDTH_80P80MHZ == des_chan->ch_width) + start_ch_freq = des_chan->ch_cfreq2 - 30; + else + start_ch_freq = des_chan->ch_freq; + + ch_state = wlan_reg_get_5g_bonded_channel_state_for_freq( + mac_ctx->pdev, start_ch_freq, + des_chan->ch_width); + if (CHANNEL_STATE_DFS == ch_state) + des_chan->ch_flagext |= IEEE80211_CHAN_DFS_CFREQ2; + } + + chan_mode = wma_chan_phy_mode(des_chan->ch_freq, ch_width, + dot11mode); + + if (chan_mode == WLAN_PHYMODE_AUTO || chan_mode == WLAN_PHYMODE_MAX) { + pe_err("Invalid phy mode!"); + return QDF_STATUS_E_FAILURE; + } + if (!des_chan->ch_cfreq1) { + pe_err("Invalid center freq1"); + return QDF_STATUS_E_FAILURE; + } + + if ((ch_width == CH_WIDTH_160MHZ || + ch_width == CH_WIDTH_80P80MHZ) && !des_chan->ch_cfreq2) { + pe_err("Invalid center freq2 for 160MHz"); + return QDF_STATUS_E_FAILURE; + } + /* Till conversion is not done in WMI we need to fill fw phy mode */ + mlme_obj->mgmt.generic.phy_mode = wma_host_to_fw_phymode(chan_mode); + des_chan->ch_phymode = chan_mode; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_pre_vdev_start(struct mac_context *mac, + struct vdev_mlme_obj *mlme_obj, + struct pe_session *session) +{ + struct wlan_channel *des_chan; + enum reg_wifi_band band; + uint8_t band_mask; + struct ch_params ch_params = {0}; + qdf_freq_t sec_chan_freq = 0; + + band = wlan_reg_freq_to_band(session->curr_op_freq); + band_mask = 1 << band; + + ch_params.ch_width = session->ch_width; + ch_params.mhz_freq_seg0 = + wlan_reg_chan_band_to_freq(mac->pdev, + session->ch_center_freq_seg0, + band_mask); + + if (session->ch_center_freq_seg1) + ch_params.mhz_freq_seg1 = + wlan_reg_chan_band_to_freq(mac->pdev, + session->ch_center_freq_seg1, + band_mask); + + if (band == (REG_BAND_2G) && (ch_params.ch_width == CH_WIDTH_40MHZ)) { + if (ch_params.mhz_freq_seg0 == session->curr_op_freq + 10) + sec_chan_freq = session->curr_op_freq + 20; + if (ch_params.mhz_freq_seg0 == session->curr_op_freq - 10) + sec_chan_freq = session->curr_op_freq - 20; + } + + wlan_reg_set_channel_params_for_freq(mac->pdev, session->curr_op_freq, + sec_chan_freq, &ch_params); + + pe_debug("vdev id %d freq %d seg0 %d seg1 %d ch_width %d cac_duration_ms %d beacon_interval %d hidden_ssid: %d dtimPeriod %d slot_time %d bcn tx rate %d mhz seg0 %d mhz seg1 %d", + session->vdev_id, session->curr_op_freq, + ch_params.center_freq_seg0, + ch_params.center_freq_seg1, ch_params.ch_width, + session->cac_duration_ms, + session->beaconParams.beaconInterval, + session->ssidHidden, session->dtimPeriod, + session->shortSlotTimeSupported, + session->beacon_tx_rate, + ch_params.mhz_freq_seg0, + ch_params.mhz_freq_seg1); + /* Invalid channel width means no suitable channel bonding in current + * regdomain for requested channel frequency. Abort vdev start. + */ + if (ch_params.ch_width == CH_WIDTH_INVALID) { + pe_debug("abort vdev start invalid chan parameters"); + return QDF_STATUS_E_INVAL; + } + + des_chan = mlme_obj->vdev->vdev_mlme.des_chan; + des_chan->ch_freq = session->curr_op_freq; + des_chan->ch_width = ch_params.ch_width; + des_chan->ch_freq_seg1 = ch_params.center_freq_seg0; + des_chan->ch_freq_seg2 = ch_params.center_freq_seg1; + des_chan->ch_ieee = wlan_reg_freq_to_chan(mac->pdev, des_chan->ch_freq); + session->ch_width = ch_params.ch_width; + session->ch_center_freq_seg0 = ch_params.center_freq_seg0; + session->ch_center_freq_seg1 = ch_params.center_freq_seg1; + + mlme_obj->mgmt.generic.maxregpower = session->maxTxPower; + mlme_obj->proto.generic.beacon_interval = + session->beaconParams.beaconInterval; + if (mlme_obj->mgmt.generic.type == WLAN_VDEV_MLME_TYPE_AP) { + mlme_obj->mgmt.ap.hidden_ssid = session->ssidHidden; + mlme_obj->mgmt.ap.cac_duration_ms = session->cac_duration_ms; + } + mlme_obj->proto.generic.dtim_period = session->dtimPeriod; + mlme_obj->proto.generic.slot_time = session->shortSlotTimeSupported; + mlme_obj->mgmt.rate_info.bcn_tx_rate = session->beacon_tx_rate; + + mlme_obj->proto.ht_info.allow_ht = !!session->htCapability; + mlme_obj->proto.vht_info.allow_vht = !!session->vhtCapability; + + if (cds_is_5_mhz_enabled()) + mlme_obj->mgmt.rate_info.quarter_rate = 1; + else if (cds_is_10_mhz_enabled()) + mlme_obj->mgmt.rate_info.half_rate = 1; + + if (session->nss == 2) { + mlme_obj->mgmt.chainmask_info.num_rx_chain = 2; + mlme_obj->mgmt.chainmask_info.num_tx_chain = 2; + } else { + mlme_obj->mgmt.chainmask_info.num_rx_chain = 1; + mlme_obj->mgmt.chainmask_info.num_tx_chain = 1; + } + wlan_vdev_mlme_set_ssid(mlme_obj->vdev, session->ssId.ssId, + session->ssId.length); + + return lim_set_ch_phy_mode(mlme_obj->vdev, session->dot11mode); +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_utils.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..99a0a790ce371624825644c17d697562928beb22 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_utils.h @@ -0,0 +1,2145 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file lim_utils.h contains the utility definitions + * LIM uses. + * Author: Chandra Modumudi + * Date: 02/13/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ +#ifndef __LIM_UTILS_H +#define __LIM_UTILS_H + +#include "sir_api.h" +#include "sir_debug.h" + +#include "lim_types.h" +#include "lim_scan_result_utils.h" +#include "lim_timer_utils.h" +#include "lim_trace.h" +#include "include/wlan_vdev_mlme.h" +#include "wlan_mlme_vdev_mgr_interface.h" +#include "wlan_qct_sys.h" + +typedef enum { + ONE_BYTE = 1, + TWO_BYTE = 2 +} eSizeOfLenField; + +#define LIM_AID_MASK 0xC000 +#define LIM_SPECTRUM_MANAGEMENT_BIT_MASK 0x0100 +#define LIM_RRM_BIT_MASK 0x1000 +#define LIM_SHORT_PREAMBLE_BIT_MASK 0x0020 +#define LIM_IMMEDIATE_BLOCK_ACK_MASK 0x8000 +#define LIM_MAX_REASSOC_RETRY_LIMIT 2 + +#define VHT_MCS_3x3_MASK 0x30 +#define VHT_MCS_2x2_MASK 0x0C + +#define CENTER_FREQ_DIFF_160MHz 8 +#define CENTER_FREQ_DIFF_80P80MHz 16 + +#define CH_TO_CNTR_FREQ_DIFF_160MHz 14 +#define CH_TO_CNTR_FREQ_DIFF_80MHz 6 + +#define IS_VHT_NSS_1x1(__mcs_map) ((__mcs_map & 0xFFFC) == 0xFFFC) + +#define MGMT_RX_PACKETS_THRESHOLD 200 + +/* 11B AP detection bit position */ +#define OBSS_DETECTION_11B_AP_BIT_MASK 0x0001 +/* 11B STA detection bit position */ +#define OBSS_DETECTION_11B_STA_BIT_MASK 0x0002 +/* 11G AP detection bit position */ +#define OBSS_DETECTION_11G_AP_BIT_MASK 0x0004 +/* 11A AP detection bit position */ +#define OBSS_DETECTION_11A_BIT_MASK 0x0008 +/* HT legacy detection bit position */ +#define OBSS_DETECTION_HT_LEGACY_BIT_MASK 0x0010 +/* HT mixed detection bit position */ +#define OBSS_DETECTION_HT_MIXED_BIT_MASK 0x0020 +/* HT 20mhz detection bit position */ +#define OBSS_DETECTION_HT_20MHZ_BIT_MASK 0x0040 + +/** + * OBSS detection period in ms, used by firmware to decide + * absent detection and also gap between same detection ind. + */ +#define OBSS_DETECTION_PERIOD_MS 4000 + +/* To check if 11B AP detection bit set */ +#define OBSS_DETECTION_IS_11B_AP(_m) ((_m) & OBSS_DETECTION_11B_AP_BIT_MASK) +/* To check if 11B STA detection bit set */ +#define OBSS_DETECTION_IS_11B_STA(_m) ((_m) & OBSS_DETECTION_11B_STA_BIT_MASK) +/* To check if 11G AP detection bit set */ +#define OBSS_DETECTION_IS_11G_AP(_m) ((_m) & OBSS_DETECTION_11G_AP_BIT_MASK) +/* To check if 11A AP detection bit set */ +#define OBSS_DETECTION_IS_11A(_m) ((_m) & OBSS_DETECTION_11A_BIT_MASK) +/* To check if HT legacy detection bit set */ +#define OBSS_DETECTION_IS_HT_LEGACY(_m) \ + ((_m) & OBSS_DETECTION_HT_LEGACY_BIT_MASK) +/* To check if HT mixed detection bit set */ +#define OBSS_DETECTION_IS_HT_MIXED(_m) ((_m) & OBSS_DETECTION_HT_MIXED_BIT_MASK) +/* To check if HT 20mhz detection bit set */ +#define OBSS_DETECTION_IS_HT_20MHZ(_m) ((_m) & OBSS_DETECTION_HT_20MHZ_BIT_MASK) + +#define MAX_WAIT_FOR_BCN_TX_COMPLETE 4000 +#define MAX_WAKELOCK_FOR_CSA 5000 + +#ifdef WLAN_FEATURE_11W +typedef union uPmfSaQueryTimerId { + struct { + uint8_t sessionId; + uint16_t peerIdx; + } fields; + uint32_t value; +} tPmfSaQueryTimerId, *tpPmfSaQueryTimerId; +#endif + +typedef struct last_processed_frame { + tSirMacAddr sa; + uint16_t seq_num; +} last_processed_msg; + +/** + * struct lim_max_tx_pwr_attr - List of tx powers from various sources + * @reg_max: power from regulatory database + * @ap_tx_power: local power constraint adjusted value + * @ini_tx_power: Max tx power from ini config + * @frequency: current operating frequency for which above powers are defined + */ +struct lim_max_tx_pwr_attr { + int8_t reg_max; + int8_t ap_tx_power; + uint8_t ini_tx_power; + uint32_t frequency; +}; + +/* LIM utility functions */ +bool lim_is_valid_frame(last_processed_msg *last_processed_frm, + uint8_t *pRxPacketInfo); +void lim_update_last_processed_frame(last_processed_msg *last_processed_frm, + uint8_t *pRxPacketInfo); + +char *lim_dot11_reason_str(uint16_t reasonCode); +char *lim_mlm_state_str(tLimMlmStates state); +char *lim_sme_state_str(tLimSmeStates state); +char *lim_msg_str(uint32_t msgType); +char *lim_result_code_str(tSirResultCodes resultCode); +char *lim_dot11_mode_str(struct mac_context *mac, uint8_t dot11Mode); +void lim_print_mlm_state(struct mac_context *mac, uint16_t logLevel, + tLimMlmStates state); +void lim_print_sme_state(struct mac_context *mac, uint16_t logLevel, + tLimSmeStates state); +void lim_print_msg_name(struct mac_context *mac, uint16_t logLevel, uint32_t msgType); + +QDF_STATUS lim_send_set_max_tx_power_req(struct mac_context *mac, + int8_t txPower, + struct pe_session *pe_session); + +/** + * lim_get_max_tx_power() - Utility to get maximum tx power + * @mac: mac handle + * @attr: pointer to buffer containing list of tx powers + * + * This function is used to get the maximum possible tx power from the list + * of tx powers mentioned in @attr. + * + * Return: Max tx power + */ +uint8_t lim_get_max_tx_power(struct mac_context *mac, + struct lim_max_tx_pwr_attr *attr); + +/* AID pool management functions */ +void lim_init_peer_idxpool(struct mac_context *, struct pe_session *); +uint16_t lim_assign_peer_idx(struct mac_context *, struct pe_session *); + +void lim_enable_overlap11g_protection(struct mac_context *mac, + tpUpdateBeaconParams pBeaconParams, + tpSirMacMgmtHdr pMh, + struct pe_session *pe_session); +void lim_update_overlap_sta_param(struct mac_context *mac, tSirMacAddr bssId, + tpLimProtStaParams pStaParams); +void lim_update_short_preamble(struct mac_context *mac, tSirMacAddr peerMacAddr, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session); +void lim_update_short_slot_time(struct mac_context *mac, tSirMacAddr peerMacAddr, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session); + +/* + * lim_send_sme_mgmt_frame_ind() - Function to send mgmt frame ind to HDD + * @mac_ctx: Pointer to Global MAC structure + * @frame_type: Type of mgmt frame + * @frame: Frame pointer + * @frame_len: Length og mgmt frame + * @session_id: session id + * @psession_entry: PE Session Entry + * @rx_freq: Frequency on which packet is received + * @rx_rssi: rssi value + * @rx_flags: RXMGMT flags to be set for the frame. Defined in enum rxmgmt_flags + * + * Indicate the Mgmt Frame received to SME to HDD callback + * handle Probe_req/Action frame currently + * + * Return: None +*/ +void lim_send_sme_mgmt_frame_ind(struct mac_context *mac_ctx, uint8_t frame_type, + uint8_t *frame, uint32_t frame_len, + uint16_t session_id, uint32_t rx_freq, + struct pe_session *psession_entry, + int8_t rx_rssi, enum rxmgmt_flags rx_flags); + +/* + * lim_deactivate_timers() - Function to deactivate lim timers + * @mac_ctx: Pointer to global mac structure + * + * This function is used to deactivate lim timers + * + * Return: None + */ +void lim_deactivate_timers(struct mac_context *mac_ctx); + +/* + * lim_deactivate_timers_for_vdev() - Deactivate lim connection timers + * @mac_ctx: Pointer to global mac structure + * @vdev_id: vdev id + * + * This function is used to trigger timeout of lim connection timers to abort + * connect request. + * + * Return: None + */ +void lim_deactivate_timers_for_vdev(struct mac_context *mac_ctx, + uint8_t vdev_id); + +/* + * The below 'product' check tobe removed if 'Association' is + * allowed in IBSS. + */ +void lim_release_peer_idx(struct mac_context *, uint16_t, struct pe_session *); + +void lim_decide_ap_protection(struct mac_context *mac, tSirMacAddr peerMacAddr, + tpUpdateBeaconParams pBeaconParams, struct pe_session *); +void lim_decide_ap_protection_on_delete(struct mac_context *mac, + tpDphHashNode sta, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session); + +QDF_STATUS lim_update_11a_protection(struct mac_context *mac, + uint8_t enable, + uint8_t overlap, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *); +QDF_STATUS lim_enable11g_protection(struct mac_context *mac, + uint8_t enable, + uint8_t overlap, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session); +QDF_STATUS lim_enable_ht_protection_from11g(struct mac_context *mac, + uint8_t enable, + uint8_t overlap, + tpUpdateBeaconParams + pBeaconParams, + struct pe_session *pe_session); +QDF_STATUS lim_enable_ht20_protection(struct mac_context *mac, + uint8_t enable, uint8_t overlap, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session); +QDF_STATUS lim_enable_ht_non_gf_protection(struct mac_context *mac, + uint8_t enable, uint8_t overlap, + tpUpdateBeaconParams + pBeaconParams, struct pe_session *); +QDF_STATUS lim_enable_ht_rifs_protection(struct mac_context *mac, + uint8_t enable, uint8_t overlap, + tpUpdateBeaconParams + pBeaconParams, + struct pe_session *pe_session); +QDF_STATUS lim_enable_ht_lsig_txop_protection(struct mac_context *mac, + uint8_t enable, + uint8_t overlap, + tpUpdateBeaconParams + pBeaconParams, struct pe_session *); +QDF_STATUS lim_enable_short_preamble(struct mac_context *mac, + uint8_t enable, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session); +QDF_STATUS lim_enable_ht_obss_protection(struct mac_context *mac, + uint8_t enable, uint8_t overlap, + tpUpdateBeaconParams + pBeaconParams, struct pe_session *); +void lim_decide_sta_protection(struct mac_context *mac, + tpSchBeaconStruct pBeaconStruct, + tpUpdateBeaconParams pBeaconParams, + struct pe_session *pe_session); +void lim_decide_sta_protection_on_assoc(struct mac_context *mac, + tpSchBeaconStruct pBeaconStruct, + struct pe_session *pe_session); + +/** + * lim_update_sta_run_time_ht_switch_chnl_params() - Process change in HT + * bandwidth + * @mac: pointer to Global MAC structure + * @pHTInfo: ht info IE + * @pe_session: pe session + * + * Return: none + */ +void lim_update_sta_run_time_ht_switch_chnl_params(struct mac_context *mac, + tDot11fIEHTInfo *pHTInfo, + struct pe_session *pe_session); +/* Print MAC address utility function */ +void lim_print_mac_addr(struct mac_context *, tSirMacAddr, uint8_t); + +/* Deferred Message Queue read/write */ +uint8_t lim_write_deferred_msg_q(struct mac_context *mac, + struct scheduler_msg *limMsg); +struct scheduler_msg *lim_read_deferred_msg_q(struct mac_context *mac); +void lim_handle_defer_msg_error(struct mac_context *mac, + struct scheduler_msg *pLimMsg); + +/* Deferred Message Queue Reset */ +void lim_reset_deferred_msg_q(struct mac_context *mac); + +static inline void lim_sys_process_mmh_msg_api(struct mac_context *mac, + struct scheduler_msg *msg) +{ + sys_process_mmh_msg(mac, msg); +} + +void lim_handle_update_olbc_cache(struct mac_context *mac); + +uint8_t lim_is_null_ssid(tSirMacSSid *pSsid); + +/* 11h Support */ +void lim_stop_tx_and_switch_channel(struct mac_context *mac, uint8_t sessionId); + +/** + * lim_process_channel_switch_timeout() - Process chanel switch timeout + * @mac: pointer to Global MAC structure + * + * Return: none + */ +void lim_process_channel_switch_timeout(struct mac_context *); +QDF_STATUS lim_start_channel_switch(struct mac_context *mac, + struct pe_session *pe_session); +void lim_update_channel_switch(struct mac_context *, tpSirProbeRespBeacon, + struct pe_session *pe_session); + +/** + * lim_switch_primary_channel() - switch primary channel of session + * @mac: Global MAC structure + * @new_channel_freq: new chnannel freq in Mhz + * @pe_session: pe session context + * + * This function changes the current operating channel frequency. + * + * return NONE + */ +void lim_switch_primary_channel(struct mac_context *mac, + uint32_t new_channel_freq, + struct pe_session *pe_session); + +/** + * lim_switch_primary_secondary_channel() - switch primary and secondary + * channel of session + * @mac: Global MAC structure + * @pe_session: session context + * @new_channel_freq: new channel frequency (MHz) + * @ch_center_freq_seg0: channel center freq seg0 + * @ch_center_freq_seg1: channel center freq seg1 + * @ch_width: ch width of enum phy_ch_width + * + * This function changes the primary and secondary channel. + * If 11h is enabled and user provides a "new channel freq" + * that is different from the current operating channel, + * then we must set this new channel in session context and + * assign notify LIM of such change. + * + * @return NONE + */ +void lim_switch_primary_secondary_channel(struct mac_context *mac, + struct pe_session *pe_session, + uint32_t new_channel_freq, + uint8_t ch_center_freq_seg0, + uint8_t ch_center_freq_seg1, + enum phy_ch_width ch_width); + +void lim_update_sta_run_time_ht_capability(struct mac_context *mac, + tDot11fIEHTCaps *pHTCaps); +void lim_update_sta_run_time_ht_info(struct mac_context *mac, + tDot11fIEHTInfo *pRcvdHTInfo, + struct pe_session *pe_session); +void lim_cancel_dot11h_channel_switch(struct mac_context *mac, + struct pe_session *pe_session); + +/** + * lim_is_channel_valid_for_channel_switch - check channel valid for switching + * @mac: Global mac context + * @channel_freq: channel freq (MHz) + * + * This function checks if the channel to which AP is expecting us to switch, + * is a valid channel for us. + * + * Return bool, true if channel is valid + */ +bool lim_is_channel_valid_for_channel_switch(struct mac_context *mac, + uint32_t channel_freq); + +QDF_STATUS lim_restore_pre_channel_switch_state(struct mac_context *mac, + struct pe_session *pe_session); + +void lim_prepare_for11h_channel_switch(struct mac_context *mac, + struct pe_session *pe_session); +void lim_switch_channel_cback(struct mac_context *mac, QDF_STATUS status, + uint32_t *data, struct pe_session *pe_session); + +/** + * lim_assoc_rej_get_remaining_delta() - Get remaining time delta for + * the rssi based disallowed list entry + * @node: rssi based disallowed list entry + * + * Return: remaining delta, can be -ve if time has already expired. + */ +int +lim_assoc_rej_get_remaining_delta(struct sir_rssi_disallow_lst *node); + +/** + * lim_rem_blacklist_entry_with_lowest_delta() - Remove the entry with lowest + * time delta + * @list: rssi based rejected BSSID list + * + * Return: QDF_STATUS + */ +QDF_STATUS +lim_rem_blacklist_entry_with_lowest_delta(qdf_list_t *list); + +static inline enum reg_wifi_band lim_get_rf_band(uint32_t chan_freq) +{ + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_freq)) + return REG_BAND_6G; + + if (WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq)) + return REG_BAND_5G; + + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq)) + return REG_BAND_2G; + + return REG_BAND_UNKNOWN; +} + +static inline QDF_STATUS +lim_get_mgmt_staid(struct mac_context *mac, uint16_t *staid, + struct pe_session *pe_session) +{ + if (LIM_IS_AP_ROLE(pe_session)) + *staid = 1; + else if (LIM_IS_STA_ROLE(pe_session)) + *staid = 0; + else + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +static inline int lim_select_cb_mode(tDphHashNode *sta, + struct pe_session *pe_session, uint8_t channel, + uint8_t chan_bw) +{ + if (sta->mlmStaContext.vhtCapability && chan_bw) { + if (channel == 36 || channel == 52 || channel == 100 || + channel == 116 || channel == 149) { + return PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW - 1; + } else if (channel == 40 || channel == 56 || channel == 104 || + channel == 120 || channel == 153) { + return PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW - 1; + } else if (channel == 44 || channel == 60 || channel == 108 || + channel == 124 || channel == 157) { + return PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH - 1; + } else if (channel == 48 || channel == 64 || channel == 112 || + channel == 128 || channel == 161) { + return PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH - 1; + } else if (channel == 165) { + return PHY_SINGLE_CHANNEL_CENTERED; + } + } else if (sta->mlmStaContext.htCapability) { + if (channel == 40 || channel == 48 || channel == 56 || + channel == 64 || channel == 104 || channel == 112 || + channel == 120 || channel == 128 || channel == 136 || + channel == 144 || channel == 153 || channel == 161) { + return PHY_DOUBLE_CHANNEL_LOW_PRIMARY; + } else if (channel == 36 || channel == 44 || channel == 52 || + channel == 60 || channel == 100 || + channel == 108 || channel == 116 || + channel == 124 || channel == 132 || + channel == 140 || channel == 149 || + channel == 157) { + return PHY_DOUBLE_CHANNEL_HIGH_PRIMARY; + } else if (channel == 165) { + return PHY_SINGLE_CHANNEL_CENTERED; + } + } + return PHY_SINGLE_CHANNEL_CENTERED; +} + +/* ANI peer station count management and associated actions */ +void lim_util_count_sta_add(struct mac_context *mac, tpDphHashNode pSta, + struct pe_session *pe_session); +void lim_util_count_sta_del(struct mac_context *mac, tpDphHashNode pSta, + struct pe_session *pe_session); + +uint8_t lim_get_ht_capability(struct mac_context *, uint32_t, struct pe_session *); +QDF_STATUS lim_tx_complete(void *context, qdf_nbuf_t buf, bool free); + +QDF_STATUS lim_validate_delts_req(struct mac_context *mac, + tpSirDeltsReq pDeltsReq, + tSirMacAddr peerMacAddr, + struct pe_session *pe_session); + +void lim_pkt_free(struct mac_context *mac, + eFrameType frmType, uint8_t *pBD, void *body); + +void lim_get_b_dfrom_rx_packet(struct mac_context *mac, void *body, uint32_t **pBD); + +/** + * utils_power_xy() - calc result of base raised to power + * @base: Base value + * @power: Base raised to this Power value + * + * Given a base(X) and power(Y), this API will return + * the result of base raised to power - (X ^ Y) + * + * Return: Result of X^Y + * + */ +static inline uint32_t utils_power_xy(uint16_t base, uint16_t power) +{ + uint32_t result = 1, i; + + for (i = 0; i < power; i++) + result *= base; + + return result; +} + +QDF_STATUS lim_post_sm_state_update(struct mac_context *mac, + tSirMacHTMIMOPowerSaveState MIMOPSState, + uint8_t *pPeerStaMac, uint8_t sessionId); + +void lim_delete_sta_context(struct mac_context *mac, struct scheduler_msg *limMsg); +void lim_delete_dialogue_token_list(struct mac_context *mac); + +/** + * lim_add_channel_status_info() - store + * chan status info into Global MAC structure + * @p_mac: Pointer to Global MAC structure + * @channel_stat: Pointer to chan status info reported by firmware + * @channel_id: current channel id + * + * Return: None + */ +void lim_add_channel_status_info(struct mac_context *p_mac, + struct lim_channel_status *channel_stat, + uint8_t channel_id); + +/** + * lim_get_channel_from_beacon() - extract channel number + * from beacon and convert to channel frequency + * @mac: Pointer to Global MAC structure + * @pBeacon: Pointer to beacon or probe rsp + * + * Return: channel frequency + */ +uint32_t lim_get_channel_from_beacon(struct mac_context *mac, + tpSchBeaconStruct pBeacon); + +/** + * lim_get_nw_type() - Get type of the network from + * data packet or beacon + * @mac: Pointer to Global MAC structure + * @chan_freq: Channel frequency + * @type: Type of packet + * @pBeacon: Pointer to beacon or probe response + * + * Return: Network type a/b/g + */ +tSirNwType lim_get_nw_type(struct mac_context *mac, uint32_t chan_freq, + uint32_t type, tpSchBeaconStruct pBeacon); + +void lim_set_tspec_uapsd_mask_per_session(struct mac_context *mac, + struct pe_session *pe_session, + struct mac_ts_info *pTsInfo, uint32_t action); + +void lim_handle_heart_beat_timeout_for_session(struct mac_context *mac, + struct pe_session *pe_session); + +/** + * lim_process_add_sta_rsp() - process WDA_ADD_STA_RSP from WMA + * @mac_ctx: Pointer to Global MAC structure + * @msg: msg from WMA + * + * Return: void + */ +void lim_process_add_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *msg); + +void lim_update_beacon(struct mac_context *mac); + +void lim_process_ap_mlm_add_sta_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ, + struct pe_session *pe_session); + +/** + * lim_process_ap_mlm_del_bss_rsp() - handle del bss response of AP + * @mac: Pointer to Global MAC structure + * @vdev_stop_rsp: pointer to vdev stop response + * @pe_session: pointer to pe_session + * + * Return: none + */ +void lim_process_ap_mlm_del_bss_rsp(struct mac_context *mac, + struct del_bss_resp *vdev_stop_rsp, + struct pe_session *pe_session); + +void lim_process_ap_mlm_del_sta_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ, + struct pe_session *pe_session); + +#ifdef QCA_IBSS_SUPPORT +/** + * lim_is_ibss_session_active() - API to check IBSS session active + * @mac: Pointer to Global MAC structure + * + * Return: Pointer to active IBSS pe_session else NULL + */ +struct pe_session *lim_is_ibss_session_active(struct mac_context *mac); +#else +/** + * lim_is_ibss_session_active() - API to check IBSS session active + * @mac: Pointer to Global MAC structure + * + * This function is dummy. + * + * Return: NULL + */ +static inline +struct pe_session *lim_is_ibss_session_active(struct mac_context *mac) +{ + return NULL; +} +#endif + +/** + * ch_width_in_mhz() - API to get channel space in MHz + * + * For CH_WIDTH_80P80MHZ, the channel space is max channel space of one + * segment - 80MHz. + * + */ +static inline uint8_t ch_width_in_mhz(enum phy_ch_width ch_width) +{ + switch (ch_width) { + case CH_WIDTH_40MHZ: + return 40; + case CH_WIDTH_80MHZ: + return 80; + case CH_WIDTH_160MHZ: + return 160; + case CH_WIDTH_80P80MHZ: + return 80; + case CH_WIDTH_5MHZ: + return 5; + case CH_WIDTH_10MHZ: + return 10; + default: + return 20; + } +} + +struct pe_session *lim_is_ap_session_active(struct mac_context *mac); +void lim_handle_heart_beat_failure_timeout(struct mac_context *mac); + +#define limGetWscIEPtr(mac, ie, ie_len) \ + wlan_get_vendor_ie_ptr_from_oui(SIR_MAC_WSC_OUI, \ + SIR_MAC_WSC_OUI_SIZE, ie, ie_len) + +#define limGetP2pIEPtr(mac, ie, ie_len) \ + wlan_get_vendor_ie_ptr_from_oui(SIR_MAC_P2P_OUI, \ + SIR_MAC_P2P_OUI_SIZE, ie, ie_len) + +uint8_t lim_get_noa_attr_stream(struct mac_context *mac, uint8_t *pNoaStream, + struct pe_session *pe_session); + +uint8_t lim_build_p2p_ie(struct mac_context *mac, uint8_t *ie, uint8_t *data, + uint8_t ie_len); + +bool lim_isconnected_on_dfs_channel(struct mac_context *mac_ctx, + uint8_t currentChannel); + +uint32_t lim_get_max_rate_flags(struct mac_context *mac_ctx, + tpDphHashNode sta_ds); + +bool lim_check_vht_op_mode_change(struct mac_context *mac, + struct pe_session *pe_session, + uint8_t chanWidth, + uint8_t *peerMac); +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +bool lim_send_he_ie_update(struct mac_context *mac_ctx, struct pe_session *pe_session); +#endif +bool lim_set_nss_change(struct mac_context *mac, struct pe_session *pe_session, + uint8_t rxNss, uint8_t *peerMac); +bool lim_check_membership_user_position(struct mac_context *mac, + struct pe_session *pe_session, + uint32_t membership, uint32_t userPosition); + +/** + * enum ack_status - Indicate TX status of ASSOC/AUTH + * @ACKED : Ack is received. + * @NOT_ACKED : No Ack received. + * @SENT_FAIL : Failure while sending. + * + * Indicate if driver is waiting for ACK status of assoc/auth or ACK received + * for ASSOC/AUTH OR NO ACK is received for the assoc/auth sent or assoc/auth + * sent failed. + */ +enum assoc_ack_status { + ACKED, + NOT_ACKED, + SENT_FAIL, +}; + +typedef enum { + WLAN_PE_DIAG_SCAN_REQ_EVENT = 0, + WLAN_PE_DIAG_SCAN_ABORT_IND_EVENT, + WLAN_PE_DIAG_SCAN_RSP_EVENT, + WLAN_PE_DIAG_JOIN_REQ_EVENT, + WLAN_PE_DIAG_JOIN_RSP_EVENT, + WLAN_PE_DIAG_SETCONTEXT_REQ_EVENT, + WLAN_PE_DIAG_SETCONTEXT_RSP_EVENT, + WLAN_PE_DIAG_REASSOC_REQ_EVENT, + WLAN_PE_DIAG_REASSOC_RSP_EVENT, + WLAN_PE_DIAG_AUTH_REQ_EVENT, + WLAN_PE_DIAG_AUTH_RSP_EVENT = 10, + WLAN_PE_DIAG_DISASSOC_REQ_EVENT, + WLAN_PE_DIAG_DISASSOC_RSP_EVENT, + WLAN_PE_DIAG_DISASSOC_IND_EVENT, + WLAN_PE_DIAG_DISASSOC_CNF_EVENT, + WLAN_PE_DIAG_DEAUTH_REQ_EVENT, + WLAN_PE_DIAG_DEAUTH_RSP_EVENT, + WLAN_PE_DIAG_DEAUTH_IND_EVENT, + WLAN_PE_DIAG_START_BSS_REQ_EVENT, + WLAN_PE_DIAG_START_BSS_RSP_EVENT, + WLAN_PE_DIAG_AUTH_IND_EVENT = 20, + WLAN_PE_DIAG_ASSOC_IND_EVENT, + WLAN_PE_DIAG_ASSOC_CNF_EVENT, + WLAN_PE_DIAG_REASSOC_IND_EVENT, + WLAN_PE_DIAG_SWITCH_CHL_IND_EVENT, + WLAN_PE_DIAG_SWITCH_CHL_RSP_EVENT, + WLAN_PE_DIAG_STOP_BSS_REQ_EVENT, + WLAN_PE_DIAG_STOP_BSS_RSP_EVENT, + WLAN_PE_DIAG_DEAUTH_CNF_EVENT, + WLAN_PE_DIAG_ADDTS_REQ_EVENT, + WLAN_PE_DIAG_ADDTS_RSP_EVENT = 30, + WLAN_PE_DIAG_DELTS_REQ_EVENT, + WLAN_PE_DIAG_DELTS_RSP_EVENT, + WLAN_PE_DIAG_DELTS_IND_EVENT, + WLAN_PE_DIAG_ENTER_BMPS_REQ_EVENT, + WLAN_PE_DIAG_ENTER_BMPS_RSP_EVENT, + WLAN_PE_DIAG_EXIT_BMPS_REQ_EVENT, + WLAN_PE_DIAG_EXIT_BMPS_RSP_EVENT, + WLAN_PE_DIAG_EXIT_BMPS_IND_EVENT, + WLAN_PE_DIAG_ENTER_IMPS_REQ_EVENT, + WLAN_PE_DIAG_ENTER_IMPS_RSP_EVENT = 40, + WLAN_PE_DIAG_EXIT_IMPS_REQ_EVENT, + WLAN_PE_DIAG_EXIT_IMPS_RSP_EVENT, + WLAN_PE_DIAG_ENTER_UAPSD_REQ_EVENT, + WLAN_PE_DIAG_ENTER_UAPSD_RSP_EVENT, + WLAN_PE_DIAG_EXIT_UAPSD_REQ_EVENT, + WLAN_PE_DIAG_EXIT_UAPSD_RSP_EVENT, + WLAN_PE_DIAG_WOWL_ADD_BCAST_PTRN_EVENT, + WLAN_PE_DIAG_WOWL_DEL_BCAST_PTRN_EVENT, + WLAN_PE_DIAG_ENTER_WOWL_REQ_EVENT, + WLAN_PE_DIAG_ENTER_WOWL_RSP_EVENT = 50, + WLAN_PE_DIAG_EXIT_WOWL_REQ_EVENT, + WLAN_PE_DIAG_EXIT_WOWL_RSP_EVENT, + WLAN_PE_DIAG_HAL_ADDBA_REQ_EVENT, + WLAN_PE_DIAG_HAL_ADDBA_RSP_EVENT, + WLAN_PE_DIAG_HAL_DELBA_IND_EVENT, + WLAN_PE_DIAG_HB_FAILURE_TIMEOUT, + WLAN_PE_DIAG_PRE_AUTH_REQ_EVENT, + WLAN_PE_DIAG_PRE_AUTH_RSP_EVENT, + WLAN_PE_DIAG_PREAUTH_DONE, + WLAN_PE_DIAG_REASSOCIATING = 60, + WLAN_PE_DIAG_CONNECTED, + WLAN_PE_DIAG_ASSOC_REQ_EVENT, + WLAN_PE_DIAG_AUTH_COMP_EVENT, + WLAN_PE_DIAG_ASSOC_COMP_EVENT, + WLAN_PE_DIAG_AUTH_START_EVENT, + WLAN_PE_DIAG_ASSOC_START_EVENT, + WLAN_PE_DIAG_REASSOC_START_EVENT, + WLAN_PE_DIAG_ROAM_AUTH_START_EVENT, + WLAN_PE_DIAG_ROAM_AUTH_COMP_EVENT, + WLAN_PE_DIAG_ROAM_ASSOC_START_EVENT = 70, + WLAN_PE_DIAG_ROAM_ASSOC_COMP_EVENT, + WLAN_PE_DIAG_SCAN_COMPLETE_EVENT, + WLAN_PE_DIAG_SCAN_RESULT_FOUND_EVENT, + WLAN_PE_DIAG_ASSOC_TIMEOUT, + WLAN_PE_DIAG_AUTH_TIMEOUT, + WLAN_PE_DIAG_DEAUTH_FRAME_EVENT, + WLAN_PE_DIAG_DISASSOC_FRAME_EVENT, + WLAN_PE_DIAG_AUTH_ACK_EVENT, + WLAN_PE_DIAG_ASSOC_ACK_EVENT, + WLAN_PE_DIAG_AUTH_ALGO_NUM, +} WLAN_PE_DIAG_EVENT_TYPE; + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +void lim_diag_event_report(struct mac_context *mac, uint16_t eventType, + struct pe_session *pe_session, uint16_t status, + uint16_t reasonCode); +/** + * lim_diag_mgmt_tx_event_report() - to log TX event to external application + * @mac_ctx: mac context + * @mgmt_hdr: 802.11 mgmt header of given frame + * @session: PE session for given frame + * @result_code: result code of to be populated in TX frame + * @reason_code: reason code if TX OTA status + * + * Anytime driver sends some mgmt frame down to firmware for OTA delivery, + * log mgmt frame through DIAG utility. Don't log frames which come too + * excessively. + * + * Return: void + */ +void lim_diag_mgmt_tx_event_report(struct mac_context *mac_ctx, void *mgmt_hdr, + struct pe_session *session, uint16_t result_code, + uint16_t reason_code); +/** + * lim_diag_mgmt_rx_event_report() - to log RX event to external application + * @mac_ctx: mac context + * @mgmt_hdr: 802.11 mgmt header of given frame + * @session: PE session for given frame + * @result_code: result code given in RX frame + * @reason_code: reason code for RX OTA status + * + * Anytime driver receives some mgmt frame from firmware OTA, + * log mgmt frame through DIAG utility. Don't log frames which come too + * excessively. + * + * Return: void + */ +void lim_diag_mgmt_rx_event_report(struct mac_context *mac_ctx, void *mgmt_hdr, + struct pe_session *session, uint16_t result_code, + uint16_t reason_code); +#else +static inline void lim_diag_event_report(struct mac_context *mac, uint16_t + eventType, struct pe_session *pe_session, uint16_t status, + uint16_t reasonCode) {} +static inline +void lim_diag_mgmt_tx_event_report(struct mac_context *mac_ctx, void *mgmt_hdr, + struct pe_session *session, uint16_t result_code, + uint16_t reason_code) {} +static inline +void lim_diag_mgmt_rx_event_report(struct mac_context *mac_ctx, void *mgmt_hdr, + struct pe_session *session, uint16_t result_code, + uint16_t reason_code) {} +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +void pe_set_resume_channel(struct mac_context *mac, uint16_t channel, + ePhyChanBondState cbState); + +void lim_get_short_slot_from_phy_mode(struct mac_context *mac, + struct pe_session *pe_session, + uint32_t phyMode, uint8_t *pShortSlotEnable); + +void lim_clean_up_disassoc_deauth_req(struct mac_context *mac, uint8_t *staMac, + bool cleanRxPath); + +bool lim_check_disassoc_deauth_ack_pending(struct mac_context *mac, + uint8_t *staMac); + +#ifdef WLAN_FEATURE_11W +void lim_pmf_sa_query_timer_handler(void *pMacGlobal, uint32_t param); +void lim_pmf_comeback_timer_callback(void *context); +void lim_set_protected_bit(struct mac_context *mac, + struct pe_session *pe_session, + tSirMacAddr peer, tpSirMacMgmtHdr pMacHdr); +#else +static inline void lim_set_protected_bit(struct mac_context *mac, + struct pe_session *pe_session, + tSirMacAddr peer, tpSirMacMgmtHdr pMacHdr) {} +#endif /* WLAN_FEATURE_11W */ + +void lim_set_ht_caps(struct mac_context *p_mac, + struct pe_session *p_session_ntry, + uint8_t *p_ie_start, + uint32_t num_bytes); + +void lim_set_vht_caps(struct mac_context *p_mac, + struct pe_session *p_session_entry, + uint8_t *p_ie_start, + uint32_t num_bytes); +bool lim_validate_received_frame_a1_addr(struct mac_context *mac_ctx, + tSirMacAddr a1, struct pe_session *session); +void lim_set_stads_rtt_cap(tpDphHashNode sta_ds, struct s_ext_cap *ext_cap, + struct mac_context *mac_ctx); + +void lim_check_and_reset_protection_params(struct mac_context *mac_ctx); + +QDF_STATUS lim_send_ext_cap_ie(struct mac_context *mac_ctx, uint32_t session_id, + tDot11fIEExtCap *extracted_extcap, bool merge); + +/** + * lim_send_ies_per_band() - gets ht and vht capability and send to firmware via + * wma + * @mac_ctx: global mac context + * @session: pe session. This can be NULL. In that case self cap will be sent + * @vdev_id: vdev for which IE is targeted + * @dot11_mode: vdev dot11 mode + * @device_mode: device mode + * + * This funciton gets ht and vht capability and send to firmware via wma + * + * Return: status of operation + */ +QDF_STATUS lim_send_ies_per_band(struct mac_context *mac_ctx, + struct pe_session *session, uint8_t vdev_id, + enum csr_cfgdot11mode dot11_mode, + enum QDF_OPMODE device_mode); + +/** + * lim_send_action_frm_tb_ppdu_cfg() - sets action frame in TB PPDU cfg to FW + * @mac_ctx: global MAC context + * @vdev_id: vdev id + * @cfg: config setting + * + * Preapres the vendor action frame and send action frame in HE TB PPDU + * configuration to FW. + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_send_action_frm_tb_ppdu_cfg(struct mac_context *mac_ctx, + uint32_t vdev_id, + uint8_t cfg); + +void lim_update_extcap_struct(struct mac_context *mac_ctx, uint8_t *buf, + tDot11fIEExtCap *ext_cap); +QDF_STATUS lim_strip_extcap_update_struct(struct mac_context *mac_ctx, + uint8_t *addn_ie, uint16_t *addn_ielen, tDot11fIEExtCap *dst); +void lim_merge_extcap_struct(tDot11fIEExtCap *dst, tDot11fIEExtCap *src, + bool add); + +#ifdef WLAN_FEATURE_11W +/** + * lim_del_pmf_sa_query_timer() - This function deletes SA query timer + * @mac_ctx: pointer to mac context + * @pe_session: pointer to PE session + * + * This API is to delete the PMF SA query timer created for each associated STA + * + * Return: none + */ +void lim_del_pmf_sa_query_timer(struct mac_context *mac_ctx, struct pe_session *pe_session); + +/** + * lim_get_vdev_rmf_capable() - get rmf capable - MFPC + * @mac: mac context + * @session: pe session + * + * Get intersection of local & peer (BSS) RSN caps + * and check MFPC bit. + * + * Return: bool + */ +bool lim_get_vdev_rmf_capable(struct mac_context *mac, + struct pe_session *session); +#else +/** + * lim_del_pmf_sa_query_timer() - This function deletes SA query timer + * @mac_ctx: pointer to mac context + * @pe_session: pointer to PE session + * + * This API is to delete the PMF SA query timer created for each associated STA + * + * Return: none + */ +static inline void +lim_del_pmf_sa_query_timer(struct mac_context *mac_ctx, struct pe_session *pe_session) +{ +} + +static inline +bool lim_get_vdev_rmf_capable(struct mac_context *mac, + struct pe_session *session) +{ + return false; +} +#endif + +/** + * lim_add_bssid_to_reject_list:- Add rssi reject Ap info to blacklist mgr. + * @pdev: pdev + * @entry: info of the BSSID to be put in rssi reject list. + * + * This API will add the passed ap info to the rssi reject list. + * + */ +void +lim_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev, + struct sir_rssi_disallow_lst *entry); + +/** + * lim_strip_op_class_update_struct - strip sup op class IE and populate + * the dot11f structure + * @mac_ctx: global MAC context + * @addn_ie: Additional IE buffer + * @addn_ielen: Length of additional IE + * @dst: Supp operating class IE structure to be updated + * + * This function is used to strip supp op class IE from IE buffer and + * update the passed structure. + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_strip_supp_op_class_update_struct(struct mac_context *mac_ctx, + uint8_t *addn_ie, uint16_t *addn_ielen, + tDot11fIESuppOperatingClasses *dst); + +uint8_t lim_get_80Mhz_center_channel(uint8_t primary_channel); +void lim_update_obss_scanparams(struct pe_session *session, + tDot11fIEOBSSScanParameters *scan_params); +void lim_init_obss_params(struct mac_context *mac_ctx, struct pe_session *session); +#ifdef WLAN_FEATURE_HOST_ROAM +uint32_t lim_create_timers_host_roam(struct mac_context *mac_ctx); +/** + * lim_delete_timers_host_roam() - Delete timers used in host based roaming + * @mac_ctx: Global MAC context + * + * Delete reassoc and preauth timers + * + * Return: none + */ +void lim_delete_timers_host_roam(struct mac_context *mac_ctx); +/** + * lim_deactivate_timers_host_roam() - deactivate timers used in host based + * roaming + * @mac_ctx: Global MAC context + * + * Delete reassoc and preauth timers + * + * Return: none + */ +void lim_deactivate_timers_host_roam(struct mac_context *mac_ctx); +void lim_deactivate_and_change_timer_host_roam(struct mac_context *mac_ctx, + uint32_t timer_id); +#else +static inline uint32_t lim_create_timers_host_roam(struct mac_context *mac_ctx) +{ + return 0; +} +static inline void lim_delete_timers_host_roam(struct mac_context *mac_ctx) +{} +static inline void lim_deactivate_timers_host_roam(struct mac_context *mac_ctx) {} +static inline void lim_deactivate_and_change_timer_host_roam( + struct mac_context *mac_ctx, uint32_t timer_id) +{} +#endif + +bool lim_is_robust_mgmt_action_frame(uint8_t action_category); +uint8_t lim_compute_ext_cap_ie_length(tDot11fIEExtCap *ext_cap); + +void lim_update_caps_info_for_bss(struct mac_context *mac_ctx, + uint16_t *caps, uint16_t bss_caps); +void lim_send_set_dtim_period(struct mac_context *mac_ctx, uint8_t dtim_period, + struct pe_session *session); + +QDF_STATUS lim_strip_ie(struct mac_context *mac_ctx, + uint8_t *addn_ie, uint16_t *addn_ielen, + uint8_t eid, eSizeOfLenField size_of_len_field, + uint8_t *oui, uint8_t out_len, uint8_t *extracted_ie, + uint32_t eid_max_len); + +#define MCSMAPMASK1x1 0x3 +#define MCSMAPMASK2x2 0xC + +#ifdef WLAN_FEATURE_11AX + +/** + * lim_intersect_ap_he_caps() - Intersect AP capability with self STA capability + * @session: pointer to PE session + * @add_bss: pointer to ADD BSS params + * @beacon: pointer to beacon + * @assoc_rsp: pointer to assoc response + * + * Return: None + */ +void lim_intersect_ap_he_caps(struct pe_session *session, struct bss_params *add_bss, + tSchBeaconStruct *pBeaconStruct, tpSirAssocRsp assoc_rsp); + +/** + * lim_intersect_sta_he_caps() - Intersect STA capability with SAP capability + * @assoc_req: pointer to assoc request + * @session: pointer to PE session + * @sta_ds: pointer to STA dph hash table entry + * + * Return: None + */ +void lim_intersect_sta_he_caps(tpSirAssocReq assoc_req, struct pe_session *session, + tpDphHashNode sta_ds); + +/** + * lim_add_he_cap() - Copy HE capability into Add sta params + * @mac_ctx: Global MAC context + * @pe_session: pe session entry + * @add_sta_params: pointer to add sta params + * @assoc_req: pointer to Assoc request + * + * Return: None + */ +void lim_add_he_cap(struct mac_context *mac_ctx, struct pe_session *pe_session, + tpAddStaParams add_sta_params, tpSirAssocReq assoc_req); + +/** + * lim_add_self_he_cap() - Copy HE capability into add sta from PE session + * @add_sta_params: pointer to add sta params + * @session: pointer to PE Session + * + * Return: None + */ +void lim_add_self_he_cap(tpAddStaParams add_sta_params, struct pe_session *session); + +/** + * lim_add_bss_he_cap() - Copy HE capability into ADD BSS params + * @add_bss: pointer to add bss params + * @assoc_rsp: pointer to assoc response + * + * Return: None + */ +void lim_add_bss_he_cap(struct bss_params *add_bss, tpSirAssocRsp assoc_rsp); + +/** + * lim_add_bss_he_cfg() - Set HE config to BSS params + * @add_bss: pointer to add bss params + * @session: Pointer to Session entry struct + * + * Return: None + */ +void lim_add_bss_he_cfg(struct bss_params *add_bss, struct pe_session *session); + +/** + * lim_copy_bss_he_cap() - Copy HE capability into PE session from start bss + * @session: pointer to PE session + * @sme_start_bss_req: pointer to start BSS request + * + * Return: None + */ +void lim_copy_bss_he_cap(struct pe_session *session, + struct start_bss_req *sme_start_bss_req); + +/** + * lim_update_he_6gop_assoc_resp() - Update HE 6GHz op info to BSS params + * @add_bss: pointer to add bss params + * @he_op: Pointer to HE operation info IE + * @session: Pointer to Session entry struct + * + * Return: None + */ +void lim_update_he_6gop_assoc_resp(struct bss_params *pAddBssParams, + tDot11fIEhe_op *he_op, + struct pe_session *pe_session); +/** + * lim_copy_join_req_he_cap() - Copy HE capability to PE session from Join req + * and update as per bandwidth supported + * @session: pointer to PE session + * @sme_join_req: pointer to SME join request + * + * Return: None + */ +void lim_copy_join_req_he_cap(struct pe_session *session, + struct join_req *sme_join_req); + +/** + * lim_log_he_6g_cap() - Print HE 6G cap IE + * @mac: pointer to MAC context + * @he_6g_cap: pointer to HE 6G cap IE + * + * Print HE 6G caps stored as dot11f structure + * + * Return: None + */ +void lim_log_he_6g_cap(struct mac_context *mac, + tDot11fIEhe_6ghz_band_cap *he_6g_cap); + +/** + * lim_log_he_op() - Print HE Operation + * @mac: pointer to MAC context + * @he_op: pointer to HE Operation + * @session: pointer to PE session + * + * Print HE operation stored as dot11f structure + * + * Return: None + */ +void lim_log_he_op(struct mac_context *mac, tDot11fIEhe_op *he_ops, + struct pe_session *session); + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +/** + * lim_log_he_bss_color() - Print HE bss color + * @mac: pointer to MAC context + * @he_bss_color: pointer to HE bss color + * + * Print HE bss color IE + * + * Return: None + */ +void lim_log_he_bss_color(struct mac_context *mac, + tDot11fIEbss_color_change *he_bss_color); +#endif + +/** + * lim_log_he_cap() - Print HE capabilities + * @mac: pointer to MAC context + * @he_cap: pointer to HE Capability + * + * Received HE capabilities are converted into dot11f structure. + * This function will print all the HE capabilities as stored + * in the dot11f structure. + * + * Return: None + */ +void lim_log_he_cap(struct mac_context *mac, tDot11fIEhe_cap *he_cap); + +/** + * lim_update_stads_he_caps() - Copy HE capability into STA DPH hash table entry + * @sta_ds: pointer to sta dph hash table entry + * @assoc_rsp: pointer to assoc response + * @session_entry: pointer to PE session + * @beacon: pointer to beacon + * + * Return: None + */ +void lim_update_stads_he_caps(tpDphHashNode sta_ds, tpSirAssocRsp assoc_rsp, + struct pe_session *session_entry, + tSchBeaconStruct *beacon); + +/** + * lim_update_usr_he_cap() - Update HE capability based on userspace + * @mac_ctx: global mac context + * @session: PE session entry + * + * Parse the HE Capability IE and populate the fields to be + * sent to FW as part of add bss and update PE session. + */ +void lim_update_usr_he_cap(struct mac_context *mac_ctx, struct pe_session *session); + +/** + * lim_decide_he_op() - Determine HE operation elements + * @mac_ctx: global mac context + * @he_ops: mlme he ops + * @session: PE session entry + * + * Parse the HE Operation IE and populate the fields to be + * sent to FW as part of add bss. + */ +void lim_decide_he_op(struct mac_context *mac_ctx, uint32_t *mlme_he_ops, + struct pe_session *session); + +/** + * lim_update_sta_he_capable(): Update he_capable in add sta params + * @mac: pointer to MAC context + * @add_sta_params: pointer to add sta params + * @sta_ds: pointer to dph hash table entry + * @session_entry: pointer to PE session + * + * Return: None + */ +void lim_update_sta_he_capable(struct mac_context *mac, + tpAddStaParams add_sta_params, tpDphHashNode sta_ds, + struct pe_session *session_entry); + +static inline bool lim_is_session_he_capable(struct pe_session *session) +{ + return session->he_capable; +} + +/** + * lim_update_he_bw_cap_mcs(): Update he mcs map per bandwidth + * @session_entry: pointer to PE session + * @beacon: pointer to beacon + * + * Return: None + */ +void lim_update_he_bw_cap_mcs(struct pe_session *session, + tSirProbeRespBeacon *beacon); + +static inline bool lim_is_he_6ghz_band(struct pe_session *session) +{ + return session->he_6ghz_band; +} + +/** + * lim_get_session_he_frag_cap(): Get session HE fragmentation cap + * @session: pointer to session + * + * Return: HE fragmentation value + */ +static inline uint8_t lim_get_session_he_frag_cap(struct pe_session *session) +{ + return session->he_config.fragmentation; +} + +static inline bool lim_is_sta_he_capable(tpDphHashNode sta_ds) +{ + return sta_ds->mlmStaContext.he_capable; +} + +/** + * lim_update_bss_he_capable() - Update he_capable in add BSS params + * @mac: pointer to MAC context + * @add_bss: pointer to add BSS params + * + * Return: None + */ +void lim_update_bss_he_capable(struct mac_context *mac, + struct bss_params *add_bss); + +/** + * lim_update_stads_he_capable() - Update he_capable in sta ds context + * @sta_ds: pointer to sta ds + * @assoc_req: pointer to assoc request + * + * Return: None + */ +void lim_update_stads_he_capable(tpDphHashNode sta_ds, tpSirAssocReq assoc_req); + +/** + * lim_update_session_he_capable(): Update he_capable in PE session + * @mac: pointer to MAC context + * @session: pointer to PE session + * + * Return: None + */ +void lim_update_session_he_capable(struct mac_context *mac, struct pe_session *session); + +/** + * lim_update_session_he_capable_chan_switch(): Update he_capable in PE session + * @mac: pointer to MAC context + * @session: pointer to PE session + * @new_chan_freq: new channel frequency Mhz + * + * Update session he capable during AP channel switching + * + * Return: None + */ +void lim_update_session_he_capable_chan_switch(struct mac_context *mac, + struct pe_session *session, + uint32_t new_chan_freq); + +/** + * lim_set_he_caps() - update HE caps to be sent to FW as part of scan IE + * @mac: pointer to MAC + * @session: pointer to PE session + * @ie_start: pointer to start of IE buffer + * @num_bytes: length of IE buffer + * + * Return: None + */ +void lim_set_he_caps(struct mac_context *mac, struct pe_session *session, + uint8_t *ie_start, uint32_t num_bytes); + +/** + * lim_send_he_caps_ie() - gets HE capability and send to firmware via wma + * @mac_ctx: global mac context + * @session: pe session. This can be NULL. In that case self cap will be sent + * @device_mode: VDEV op mode + * @vdev_id: vdev for which IE is targeted + * + * This function gets HE capability and send to firmware via wma + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_send_he_caps_ie(struct mac_context *mac_ctx, + struct pe_session *session, + enum QDF_OPMODE device_mode, + uint8_t vdev_id); + +/** + * lim_populate_he_mcs_set() - function to populate HE mcs rate set + * @mac_ctx: pointer to global mac structure + * @rates: pointer to supported rate set + * @peer_vht_caps: pointer to peer HE capabilities + * @session_entry: pe session entry + * @nss: number of spatial streams + * + * Populates HE mcs rate set based on peer and self capabilities + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_populate_he_mcs_set(struct mac_context *mac_ctx, + struct supported_rates *rates, + tDot11fIEhe_cap *peer_he_caps, + struct pe_session *session_entry, + uint8_t nss); + +/** + * lim_update_stads_he_6ghz_op() - Update sta ds channel info + * @session: pe session + * @sta_ds: pointer to sta ds struct + + * Update sta_ds channel width. + * + * Return: void + */ +void lim_update_stads_he_6ghz_op(struct pe_session *session, + tpDphHashNode sta_ds); + +/** + * lim_update_he_6ghz_band_caps() - Update he 6ghz band caps + * @mac_ctx: Global MAC context + * @pAssocRsp: contains the structured assoc/reassoc Response got from AP + * @add_bss: pointer to ADD BSS params + * + * Update 6ghz band caps based on HE capability + * + * Return: none + */ +void lim_update_he_6ghz_band_caps(struct mac_context *mac, + tDot11fIEhe_6ghz_band_cap *he_6ghz_band_cap, + tpAddStaParams params); + +#else +static inline void lim_add_he_cap(struct mac_context *mac_ctx, + struct pe_session *pe_session, + tpAddStaParams add_sta_params, + tpSirAssocReq assoc_req) +{ +} + +static inline void lim_add_self_he_cap(tpAddStaParams add_sta_params, + struct pe_session *session) +{ +} + +static inline void lim_add_bss_he_cap(struct bss_params *add_bss, + tpSirAssocRsp assoc_rsp) +{ + return; +} + +static inline void lim_add_bss_he_cfg(struct bss_params *add_bss, + struct pe_session *session) +{ +} + +static inline void lim_update_he_6gop_assoc_resp( + struct bss_params *pAddBssParams, + tDot11fIEhe_op *he_op, + struct pe_session *pe_session) +{ +} + +static inline void lim_intersect_ap_he_caps(struct pe_session *session, + struct bss_params *add_bss, tSchBeaconStruct *pBeaconStruct, + tpSirAssocRsp assoc_rsp) +{ + return; +} + +static inline void lim_intersect_sta_he_caps(tpSirAssocReq assoc_req, + struct pe_session *session, tpDphHashNode sta_ds) +{ +} + +static inline void lim_update_stads_he_caps(tpDphHashNode sta_ds, tpSirAssocRsp assoc_rsp, + struct pe_session *session_entry, tSchBeaconStruct *beacon) +{ + return; +} + +static inline void lim_update_usr_he_cap(struct mac_context *mac_ctx, + struct pe_session *session) +{ +} + +static inline void lim_decide_he_op(struct mac_context *mac_ctx, + uint32_t *mlme_he_ops, struct pe_session *session) +{ +} + +static inline +void lim_copy_bss_he_cap(struct pe_session *session, + struct start_bss_req *sme_start_bss_req) +{ +} + +static inline void lim_copy_join_req_he_cap(struct pe_session *session, + struct join_req *sme_join_req) +{ +} + +static inline void lim_log_he_op(struct mac_context *mac, + tDot11fIEhe_op *he_ops, + struct pe_session *session) +{ +} + +static inline void lim_log_he_cap(struct mac_context *mac, + tDot11fIEhe_cap *he_cap) +{ +} + +static inline void lim_update_sta_he_capable(struct mac_context *mac, + tpAddStaParams add_sta_params, + tpDphHashNode sta_ds, struct pe_session *session_entry) +{ +} + +static inline bool lim_is_session_he_capable(struct pe_session *session) +{ + return false; +} + +static inline void lim_update_he_bw_cap_mcs(struct pe_session *session, + tSirProbeRespBeacon *beacon) +{ +} + +static inline bool lim_is_he_6ghz_band(struct pe_session *session) +{ + return false; +} + +static inline uint8_t lim_get_session_he_frag_cap(struct pe_session *session) +{ + return 0; +} + +static inline bool lim_is_sta_he_capable(tpDphHashNode sta_ds) +{ + return false; +} + +static inline void lim_update_bss_he_capable(struct mac_context *mac, + struct bss_params *add_bss) +{ +} + +static inline void lim_update_stads_he_capable(tpDphHashNode sta_ds, + tpSirAssocReq assoc_req) +{ +} + +static inline void lim_update_session_he_capable(struct mac_context *mac, + struct pe_session *session) +{ +} + +static inline +void lim_update_session_he_capable_chan_switch(struct mac_context *mac, + struct pe_session *session, + uint32_t new_chan_freq) +{ +} + +static inline void lim_set_he_caps(struct mac_context *mac, struct pe_session *session, + uint8_t *ie_start, uint32_t num_bytes) +{ +} + +static inline QDF_STATUS lim_send_he_caps_ie(struct mac_context *mac_ctx, + struct pe_session *session, + enum QDF_OPMODE device_mode, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS lim_populate_he_mcs_set(struct mac_context *mac_ctx, + struct supported_rates *rates, + tDot11fIEhe_cap *peer_he_caps, + struct pe_session *session_entry, + uint8_t nss) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void +lim_update_stads_he_6ghz_op(struct pe_session *session, + tpDphHashNode sta_ds) +{ +} + +static inline void +lim_update_he_6ghz_band_caps(struct mac_context *mac, + tDot11fIEhe_6ghz_band_cap *he_6ghz_band_cap, + tpAddStaParams params) +{ +} +#endif + +#if defined(CONFIG_BAND_6GHZ) && defined(WLAN_FEATURE_11AX) +/** + * lim_send_he_6g_band_caps_ie() - Send HE 6ghz band caps to FW + * @mac_ctx: Global MAC context + * @session: session ptr + * @vdev_id: vdev id + * + * Send HE 6ghz band capabilities IE to firmware + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS lim_send_he_6g_band_caps_ie(struct mac_context *mac_ctx, + struct pe_session *session, + uint8_t vdev_id); +#else +static inline +QDF_STATUS lim_send_he_6g_band_caps_ie(struct mac_context *mac_ctx, + struct pe_session *session, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * lim_decrement_pending_mgmt_count: Decrement mgmt frame count + * @mac_ctx: Pointer to global MAC structure + * + * This function is used to decrement pe mgmt count once frame + * removed from queue + * + * Return: None + */ +void lim_decrement_pending_mgmt_count(struct mac_context *mac_ctx); + +/** + * lim_check_if_vendor_oui_match() - Check if the given OUI match in IE buffer + * @mac_ctx: MAC context + * @ie: IE buffer + * @ie_len: length of @ie + * + * This API is used to check if given vendor OUI + * matches in given IE buffer + * + * Return: True, if mataches. False otherwise + */ +bool lim_check_if_vendor_oui_match(struct mac_context *mac_ctx, + uint8_t *oui, uint8_t oui_len, + uint8_t *ie, uint8_t ie_len); + +QDF_STATUS lim_util_get_type_subtype(void *pkt, uint8_t *type, + uint8_t *subtype); + +/** + * lim_get_min_session_txrate() - Get the minimum rate supported in the session + * @session: Pointer to PE session + * + * This API will find the minimum rate supported by the given PE session and + * return the enum rateid corresponding to the rate. + * + * Return: enum rateid + */ +enum rateid lim_get_min_session_txrate(struct pe_session *session); + +/** + * lim_send_dfs_chan_sw_ie_update() - updates the channel switch IE in beacon + * template + * + * @mac_ctx - pointer to global mac context + * @session - A pointer to pesession + * Return None + */ +void lim_send_dfs_chan_sw_ie_update(struct mac_context *mac_ctx, + struct pe_session *session); + +/** + * lim_process_ap_ecsa_timeout() -process ECSA timeout which decrement csa count + * in beacon and update beacon template in firmware + * + * @data - A pointer to pesession + * Return None + */ +void lim_process_ap_ecsa_timeout(void *session); + +/** + * lim_send_stop_bss_failure_resp() -send failure delete bss resp to sme + * @mac_ctx: mac ctx + * @session: session pointer + * + * Return None + */ +void lim_send_stop_bss_failure_resp(struct mac_context *mac_ctx, + struct pe_session *session); + +/** + * lim_delete_all_peers() -delete all connected peers + * @session: session pointer + * + * Return None + */ +void lim_delete_all_peers(struct pe_session *session); + +/** + * lim_send_vdev_stop() -send delete bss/stop vdev req + * @session: session pointer + * + * Return QDF_STATUS + */ +QDF_STATUS lim_send_vdev_stop(struct pe_session *session); + +/** + * lim_send_vdev_stop() -send delete bss/stop vdev req for STA + * @session: session pointer + * + * Return QDF_STATUS + */ +QDF_STATUS lim_sta_send_del_bss(struct pe_session *session); + +/** + * lim_send_start_bss_confirm() -send start bss confirm req + * @mac_ctx: pointer to global mac structure + * @start_cnf: start confirm structure pointer + * + * Return None + */ +void lim_send_start_bss_confirm(struct mac_context *mac_ctx, + tLimMlmStartCnf *start_cnf); + +/** + * lim_send_chan_switch_action_frame()- Send an action frame + * containing CSA IE or ECSA IE depending on the connected + * sta capability. + * + * @mac_ctx: pointer to global mac structure + * @new_channel_freq: new channel freq(Mhz) to switch to. + * @ch_bandwidth: ch bw of enum phy_ch_width + * @session_entry: pe session + * + * Return: void + */ +void lim_send_chan_switch_action_frame(struct mac_context *mac_ctx, + uint16_t new_channel_freq, + enum phy_ch_width ch_bandwidth, + struct pe_session *session_entry); + +/** + * send_extended_chan_switch_action_frame()- function to send ECSA + * action frame for each sta connected to SAP/GO and AP in case of + * STA . + * @mac_ctx: pointer to global mac structure + * @new_channel_freq: new channel to switch to. + * @ch_bandwidth: channel bw of type enum phy_ch_width + * @session_entry: pe session + * + * This function is called to send ECSA frame for STA/CLI and SAP/GO. + * + * Return: void + */ +void send_extended_chan_switch_action_frame(struct mac_context *mac_ctx, + uint16_t new_channel_freq, + enum phy_ch_width ch_bandwidth, + struct pe_session *session_entry); + +/** + * lim_process_obss_detection_ind() - Process obss detection indication + * @mac_ctx: Pointer to Global MAC structure. + * @obss_detection: obss detection info. + * + * Process obss detection indication and apply necessary protection for + * the given AP session. + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_process_obss_detection_ind(struct mac_context *mac_ctx, + struct wmi_obss_detect_info + *obss_detection); + +/** + * lim_obss_send_detection_cfg() - Send obss detection configuration to firmware + * @mac_ctx: Pointer to Global MAC structure + * @session: Pointer to session + * @force: Force to send new configuration even if new cfg same as old + * + * Generate new cfg based on current protection status and send new cfg to + * firmware. + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_obss_send_detection_cfg(struct mac_context *mac_ctx, + struct pe_session *session, + bool force); + +/** + * lim_obss_generate_detection_config() - get new obss offload detection cfg + * @mac_ctx: Pointer to Global MAC structure + * @session: Pointer to session + * @cfg: Obss detection cfg buffer pointer + * + * Generate new cfg based on current protection status. + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_obss_generate_detection_config(struct mac_context *mac_ctx, + struct pe_session *session, + struct obss_detection_cfg *cfg); + +/** + * lim_enable_obss_detection_config() - Enable obss detection + * @mac_ctx: Pointer to Global MAC structure + * @session: Pointer to session + * + * This function will enable legacy obss detection (by starting timer) + * or also offload based detection based on support. + * + * Return: None + */ +void lim_enable_obss_detection_config(struct mac_context *mac_ctx, + struct pe_session *session); + +#ifdef WLAN_SUPPORT_TWT +void lim_set_peer_twt_cap(struct pe_session *session, struct s_ext_cap *ext_cap); +#else +static inline void lim_set_peer_twt_cap(struct pe_session *session, + struct s_ext_cap *ext_cap) +{ +} +#endif + +/** + * lim_rx_invalid_peer_process() - process rx invalid peer indication + * @mac_ctx: Pointer to Global MAC structure + * @lim_msg: Pointer to scheduler message + * + * This function will process the rx data invalid peer indication, + * if the vdev operation mode is SAP, then send the deauth mgmt frame + * to STA. + * + * Return: None + */ +void lim_rx_invalid_peer_process(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg); + +/** + * lim_req_send_delba_ind_process() - process send delba indication + * @mac_ctx: mac context + * @lim_msg: lim message + * + * This function will process the send delba indication from DP. + * + * Return: None + */ +void lim_req_send_delba_ind_process(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg); + +/** + * lim_send_beacon() - send beacon indication to firmware + * @mac_ctx: Pointer to Global MAC structure + * @session: session pointer + * + * Return: None + */ +void lim_send_beacon(struct mac_context *mac_ctx, struct pe_session *session); + +/** + * lim_ndi_mlme_vdev_up_transition() - Send event to transistion NDI VDEV to UP + * @session: session pointer + * + * Return: None + */ +void lim_ndi_mlme_vdev_up_transition(struct pe_session *session); + +/** + * lim_sap_move_to_cac_wait_state() - move to cac wait state + * @sap_ctx: SAP context + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_sap_move_to_cac_wait_state(struct pe_session *session); + +/** + * lim_disconnect_complete - Deliver vdev disconnect complete event or + * STA send deleting bss + * @session: PE session pointer + * @del_bss: Whether to call lim_sta_send_del_bss + * + * API delivers vdev disconnect complete event + * + * Return: None + */ +void lim_disconnect_complete(struct pe_session *session, bool del_bss); + +/** + * lim_sta_mlme_vdev_stop_send() - send VDEV stop + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes vdev stop + * + * Return: SUCCESS on successful completion of vdev stop + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_sta_mlme_vdev_stop_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_sta_mlme_vdev_req_fail() - send VDEV start req failure + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes vdev stop + * + * Return: SUCCESS on successful completion of req failure operation + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_sta_mlme_vdev_req_fail(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_sta_mlme_vdev_start_send() - send VDEV start + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes vdev start + * + * Return: SUCCESS on successful completion of vdev start + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_sta_mlme_vdev_start_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_sta_mlme_vdev_restart_send() - send VDEV restart + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes vdev restart + * + * Return: SUCCESS on successful completion of vdev restart + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_sta_mlme_vdev_restart_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_ap_mlme_vdev_start_send() - Invokes VDEV start operation + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV start operation + * + * Return: SUCCESS on successful completion of start operation + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_ap_mlme_vdev_start_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *event); +/* + * lim_ap_mlme_vdev_update_beacon() - Updates beacon + * @vdev_mlme_obj: VDEV MLME comp object + * @op: beacon update type + * @data_len: data size + * @data: event data + * + * API updates/allocates/frees the beacon + * + * Return: SUCCESS on successful update of beacon + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_ap_mlme_vdev_update_beacon(struct vdev_mlme_obj *vdev_mlme, + enum beacon_update_op op, + uint16_t data_len, void *data); + +/** + * lim_ap_mlme_vdev_up_send() - VDEV up operation + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV up operations + * + * Return: SUCCESS on successful completion of up operation + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_ap_mlme_vdev_up_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_ap_mlme_vdev_disconnect_peers - Disconnect peers + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API trigger stations disconnection connected with AP + * + * Return: SUCCESS on successful invocation of station disconnection + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_ap_mlme_vdev_disconnect_peers(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_ap_mlme_vdev_stop_send - Invokes VDEV stop operation + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV stop operation + * + * Return: SUCCESS on successful completion of stop operation + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_ap_mlme_vdev_stop_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_ap_mlme_vdev_restart_send - Invokes VDEV restart operation + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV restart operation + * + * Return: SUCCESS on successful completion of restart operation + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_ap_mlme_vdev_restart_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_ap_mlme_vdev_start_req_failed - handle vdev start req failure + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes on START fail response + * + * Return: SUCCESS on successful invocation of callback + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_ap_mlme_vdev_start_req_failed(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * lim_mon_mlme_vdev_start_send() - Invokes VDEV start operation + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV start operation + * + * Return: SUCCESS on successful completion of start operation + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_mon_mlme_vdev_start_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *event); + +/** + * lim_get_capability_info() - Get capability information + * @mac: pointer to mac data + * @pcap: pointer to return capability information + * @pe_session: pointer to pe session + * + * Return: SUCCESS on successful get capability information + * FAILURE, if it fails due to any + */ +QDF_STATUS lim_get_capability_info(struct mac_context *mac, uint16_t *pCap, + struct pe_session *pe_session); + +/** + * lim_op_class_from_bandwidth() - get op class from bandwidth + * @mac_ctx: mac context + * @channel_freq: channel frequency MHz + * @ch_bandwidth: channel bandwidth + * @offset: second channel offfset + * + * This API can get the operating class based on channel freq, + * bandwidth and second channel offset. + * + * Return: op class + */ +uint8_t lim_op_class_from_bandwidth(struct mac_context *mac_ctx, + uint16_t channel_freq, + enum phy_ch_width ch_bandwidth, + enum offset_t offset); + +/** + * lim_flush_bssid() - flush bssid from scan cache + * @mac_ctx: pointer to mac data + * @bssid: bssid to be flushed + * + * Return: void + */ +void lim_flush_bssid(struct mac_context *mac_ctx, uint8_t *bssid); + +/** + * lim_is_sha384_akm() - Function to check if the negotiated AKM for the + * current session is based on sha384 key derivation function. + * @mac_ctx: pointer to mac data + * @akm: negotiated AKM for the current session + * + * Return: true if akm is sha384 based kdf or false + */ +bool lim_is_sha384_akm(enum ani_akm_type akm); + + +/** + * lim_pre_vdev_start() - set set vdev params from session + * @mac: pointer to mac context + * @mlme_obj: vdev mlme obj + * @session: pointer to pe session + * + * Return: QDF_STATUS + */ +QDF_STATUS lim_pre_vdev_start(struct mac_context *mac, + struct vdev_mlme_obj *mlme_obj, + struct pe_session *session); + +/** + * lim_set_ch_phy_mode() - set channel phy mode + * @vdev: pointer to vdev + * + * Return: QDF_STATUS + */ +QDF_STATUS +lim_set_ch_phy_mode(struct wlan_objmgr_vdev *vdev, uint8_t dot11mode); + +#if defined(CONFIG_BAND_6GHZ) && defined(WLAN_FEATURE_11AX) +/** + * lim_ap_check_6g_compatible_peer() - check all client support 6Ghz band + * @mac_ctx: mac context + * @session: pe session + * + * Return: void + */ +void lim_ap_check_6g_compatible_peer(struct mac_context *mac_ctx, + struct pe_session *session); +#else +static inline void lim_ap_check_6g_compatible_peer( + struct mac_context *mac_ctx, struct pe_session *session) +{} +#endif +#endif /* __LIM_UTILS_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/nan/nan_datapath.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/nan/nan_datapath.c new file mode 100644 index 0000000000000000000000000000000000000000..0c4868f3fee8050a0288871fdc6919a24f620a1d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/nan/nan_datapath.c @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: nan_datapath.c + * + * MAC NAN Data path API implementation + */ + +#include "lim_utils.h" +#include "lim_api.h" +#include "lim_assoc_utils.h" +#include "nan_datapath.h" +#include "lim_types.h" +#include "lim_send_messages.h" +#include "wma_nan_datapath.h" +#include "os_if_nan.h" +#include "nan_public_structs.h" +#include "nan_ucfg_api.h" + +/** + * lim_add_ndi_peer() - Function to add ndi peer + * @mac_ctx: handle to mac structure + * @vdev_id: vdev id on which peer is added + * @peer_mac_addr: peer to be added + * + * Return: QDF_STATUS_SUCCESS on success; error number otherwise + */ +static QDF_STATUS lim_add_ndi_peer(struct mac_context *mac_ctx, + uint32_t vdev_id, struct qdf_mac_addr peer_mac_addr) +{ + struct pe_session *session; + tpDphHashNode sta_ds; + uint16_t assoc_id, peer_idx; + QDF_STATUS status; + uint8_t zero_mac_addr[QDF_MAC_ADDR_SIZE] = { 0, 0, 0, 0, 0, 0 }; + + if (!wlan_is_vdev_id_up(mac_ctx->pdev, vdev_id)) { + pe_err_rl("NDI vdev is not up"); + return QDF_STATUS_E_FAILURE; + } + + if (!qdf_mem_cmp(&zero_mac_addr, &peer_mac_addr.bytes[0], + QDF_MAC_ADDR_SIZE)) { + pe_err("Failing to add peer with all zero mac addr"); + return QDF_STATUS_E_FAILURE; + } + + session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session) { + /* couldn't find session */ + pe_err("Session not found for vdev_id: %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + sta_ds = dph_lookup_hash_entry(mac_ctx, + peer_mac_addr.bytes, + &assoc_id, &session->dph.dphHashTable); + /* peer exists, don't do anything */ + if (sta_ds) { + pe_err("NDI Peer already exists!!"); + return QDF_STATUS_SUCCESS; + } + pe_info("Need to create NDI Peer :" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_mac_addr.bytes)); + + peer_idx = lim_assign_peer_idx(mac_ctx, session); + if (!peer_idx) { + pe_err("Invalid peer_idx: %d", peer_idx); + return QDF_STATUS_SUCCESS; + } + + sta_ds = dph_add_hash_entry(mac_ctx, peer_mac_addr.bytes, peer_idx, + &session->dph.dphHashTable); + if (!sta_ds) { + pe_err("Couldn't add dph entry"); + /* couldn't add dph entry */ + return QDF_STATUS_E_FAILURE; + } + + /* wma decides NDI mode from wma->inferface struct */ + sta_ds->staType = STA_ENTRY_NDI_PEER; + status = lim_add_sta(mac_ctx, sta_ds, false, session); + if (QDF_STATUS_SUCCESS != status) { + /* couldn't add peer */ + pe_err("limAddSta failed status: %d", + status); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_add_ndi_peer_converged(uint32_t vdev_id, + struct qdf_mac_addr peer_mac_addr) +{ + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac_ctx) + return QDF_STATUS_E_NULL_VALUE; + + return lim_add_ndi_peer(mac_ctx, vdev_id, peer_mac_addr); +} + +/** + * lim_ndp_delete_peer_by_addr() - Delete NAN data peer, given addr and vdev_id + * @mac_ctx: handle to mac context + * @vdev_id: vdev_id on which peer was added + * @peer_ndi_mac_addr: mac addr of peer + * This function deletes a peer if there was NDP_Confirm with REJECT + * + * Return: None + */ +static void lim_ndp_delete_peer_by_addr(struct mac_context *mac_ctx, uint8_t vdev_id, + struct qdf_mac_addr peer_ndi_mac_addr) +{ + struct pe_session *session; + tpDphHashNode sta_ds; + uint16_t peer_idx; + uint8_t zero_mac_addr[QDF_MAC_ADDR_SIZE] = { 0, 0, 0, 0, 0, 0 }; + + if (!qdf_mem_cmp(&zero_mac_addr, &peer_ndi_mac_addr.bytes[0], + QDF_MAC_ADDR_SIZE)) { + pe_err("Failing to delete the peer with all zero mac addr"); + return; + } + + pe_info("deleting peer: "QDF_MAC_ADDR_FMT" confirm rejected", + QDF_MAC_ADDR_REF(peer_ndi_mac_addr.bytes)); + + session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); + if (!session || (session->bssType != eSIR_NDI_MODE)) { + pe_err("PE session is NULL or non-NDI for sme session %d", + vdev_id); + return; + } + + sta_ds = dph_lookup_hash_entry(mac_ctx, peer_ndi_mac_addr.bytes, + &peer_idx, &session->dph.dphHashTable); + if (!sta_ds) { + pe_err("Unknown NDI Peer"); + return; + } + if (sta_ds->staType != STA_ENTRY_NDI_PEER) { + pe_err("Non-NDI Peer ignored"); + return; + } + /* + * Call lim_del_sta() with response required set true. Hence + * DphHashEntry will be deleted after receiving that response. + */ + + lim_del_sta(mac_ctx, sta_ds, true, session); +} + +void lim_ndp_delete_peers_by_addr_converged(uint8_t vdev_id, + struct qdf_mac_addr peer_ndi_mac_addr) +{ + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac_ctx) + return; + + lim_ndp_delete_peer_by_addr(mac_ctx, vdev_id, peer_ndi_mac_addr); +} + +/** + * lim_ndp_delete_peers() - Delete NAN data peers + * @mac_ctx: handle to mac context + * @ndp_map: NDP instance/peer map + * @num_peers: number of peers entries in peer_map + * This function deletes a peer if there are no active NDPs left with that peer + * + * Return: None + */ +static void lim_ndp_delete_peers(struct mac_context *mac_ctx, + struct peer_ndp_map *ndp_map, uint8_t num_peers) +{ + tpDphHashNode sta_ds = NULL; + uint16_t deleted_num = 0; + int i, j; + struct pe_session *session; + struct qdf_mac_addr *deleted_peers; + uint16_t peer_idx; + bool found; + + deleted_peers = qdf_mem_malloc(num_peers * sizeof(*deleted_peers)); + if (!deleted_peers) + return; + + for (i = 0; i < num_peers; i++) { + pe_debug("ndp_map[%d]: MAC: " QDF_MAC_ADDR_FMT " num_active %d", + i, + QDF_MAC_ADDR_REF(ndp_map[i].peer_ndi_mac_addr.bytes), + ndp_map[i].num_active_ndp_sessions); + + /* Do not delete a peer with active NDPs */ + if (ndp_map[i].num_active_ndp_sessions > 0) + continue; + + session = pe_find_session_by_vdev_id(mac_ctx, + ndp_map[i].vdev_id); + if (!session || (session->bssType != eSIR_NDI_MODE)) { + pe_err("PE session is NULL or non-NDI for sme session %d", + ndp_map[i].vdev_id); + continue; + } + + /* Check if this peer is already in the deleted list */ + found = false; + for (j = 0; j < deleted_num && !found; j++) { + if (!qdf_mem_cmp( + &deleted_peers[j].bytes, + &ndp_map[i].peer_ndi_mac_addr.bytes, + QDF_MAC_ADDR_SIZE)) { + found = true; + break; + } + } + if (found) + continue; + + sta_ds = dph_lookup_hash_entry(mac_ctx, + ndp_map[i].peer_ndi_mac_addr.bytes, + &peer_idx, &session->dph.dphHashTable); + if (!sta_ds) { + pe_err("Unknown NDI Peer"); + continue; + } + if (sta_ds->staType != STA_ENTRY_NDI_PEER) { + pe_err("Non-NDI Peer ignored"); + continue; + } + /* + * Call lim_del_sta() with response required set true. + * Hence DphHashEntry will be deleted after receiving + * that response. + */ + lim_del_sta(mac_ctx, sta_ds, true, session); + qdf_copy_macaddr(&deleted_peers[deleted_num++], + &ndp_map[i].peer_ndi_mac_addr); + } + qdf_mem_free(deleted_peers); +} + +void lim_ndp_delete_peers_converged(struct peer_nan_datapath_map *ndp_map, + uint8_t num_peers) +{ + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac_ctx) + return; + + lim_ndp_delete_peers(mac_ctx, (struct peer_ndp_map *)ndp_map, + num_peers); +} + +/** + * lim_process_ndi_del_sta_rsp() - Handle WDA_DELETE_STA_RSP in eLIM_NDI_ROLE + * @mac_ctx: Global MAC context + * @lim_msg: LIM message + * @pe_session: PE session + * + * Return: None + */ +void lim_process_ndi_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg, + struct pe_session *pe_session) +{ + tpDphHashNode sta_ds; + tpDeleteStaParams del_sta_params = (tpDeleteStaParams) lim_msg->bodyptr; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc = mac_ctx->psoc; + struct nan_datapath_peer_ind peer_ind; + + if (!del_sta_params) { + pe_err("del_sta_params is NULL"); + return; + } + if (!LIM_IS_NDI_ROLE(pe_session)) { + pe_err("Session %d is not NDI role", del_sta_params->assocId); + goto skip_event; + } + + sta_ds = dph_get_hash_entry(mac_ctx, del_sta_params->assocId, + &pe_session->dph.dphHashTable); + if (!sta_ds) { + pe_err("DPH Entry for STA %X is missing", + del_sta_params->assocId); + goto skip_event; + } + + if (QDF_STATUS_SUCCESS != del_sta_params->status) { + pe_err("DEL STA failed!"); + goto skip_event; + } + pe_info("Deleted STA AssocID %d MAC " QDF_MAC_ADDR_FMT, + sta_ds->assocId, + QDF_MAC_ADDR_REF(sta_ds->staAddr)); + + qdf_mem_copy(&peer_ind.peer_mac_addr.bytes, + sta_ds->staAddr, sizeof(tSirMacAddr)); + lim_release_peer_idx(mac_ctx, sta_ds->assocId, pe_session); + lim_delete_dph_hash_entry(mac_ctx, sta_ds->staAddr, sta_ds->assocId, + pe_session); + pe_session->limMlmState = eLIM_MLM_IDLE_STATE; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + pe_session->smeSessionId, + WLAN_NAN_ID); + if (!vdev) { + pe_err("Failed to get vdev from id"); + goto skip_event; + } + ucfg_nan_datapath_event_handler(psoc, vdev, NDP_PEER_DEPARTED, + &peer_ind); + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + +skip_event: + qdf_mem_free(del_sta_params); + lim_msg->bodyptr = NULL; +} + +void lim_process_ndi_mlm_add_bss_rsp(struct mac_context *mac_ctx, + struct add_bss_rsp *add_bss_rsp, + struct pe_session *session_entry) +{ + tLimMlmStartCnf mlm_start_cnf; + + if (!add_bss_rsp) { + pe_err("add_bss_rsp is NULL"); + return; + } + pe_debug("Status %d", add_bss_rsp->status); + if (QDF_IS_STATUS_SUCCESS(add_bss_rsp->status)) { + pe_debug("WDA_ADD_BSS_RSP returned QDF_STATUS_SUCCESS"); + session_entry->limMlmState = eLIM_MLM_BSS_STARTED_STATE; + MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, + session_entry->peSessionId, + session_entry->limMlmState)); + session_entry->vdev_id = add_bss_rsp->vdev_id; + session_entry->limSystemRole = eLIM_NDI_ROLE; + session_entry->statypeForBss = STA_ENTRY_SELF; + /* Apply previously set configuration at HW */ + lim_apply_configuration(mac_ctx, session_entry); + mlm_start_cnf.resultCode = eSIR_SME_SUCCESS; + + /* Initialize peer ID pool */ + lim_init_peer_idxpool(mac_ctx, session_entry); + } else { + pe_err("WDA_ADD_BSS_REQ failed with status %d", + add_bss_rsp->status); + mlm_start_cnf.resultCode = eSIR_SME_HAL_SEND_MESSAGE_FAIL; + } + mlm_start_cnf.sessionId = session_entry->peSessionId; + lim_send_start_bss_confirm(mac_ctx, &mlm_start_cnf); +} + +void lim_ndi_del_bss_rsp(struct mac_context * mac_ctx, + struct del_bss_resp *del_bss, + struct pe_session *session_entry) +{ + tSirResultCodes rc = eSIR_SME_SUCCESS; + + SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true); + if (!del_bss) { + pe_err("NDI: DEL_BSS_RSP with no body!"); + rc = eSIR_SME_STOP_BSS_FAILURE; + goto end; + } + session_entry = pe_find_session_by_vdev_id(mac_ctx, del_bss->vdev_id); + if (!session_entry) { + pe_err("Session Does not exist for given sessionID"); + goto end; + } + + if (del_bss->status != QDF_STATUS_SUCCESS) { + pe_err("NDI: DEL_BSS_RSP error (%x)", del_bss->status); + rc = eSIR_SME_STOP_BSS_FAILURE; + goto end; + } + + session_entry->limMlmState = eLIM_MLM_IDLE_STATE; + +end: + /* Delete PE session once BSS is deleted */ + if (session_entry) { + lim_send_sme_rsp(mac_ctx, eWNI_SME_STOP_BSS_RSP, + rc, session_entry->smeSessionId); + pe_delete_session(mac_ctx, session_entry); + session_entry = NULL; + } +} + +static QDF_STATUS lim_send_sme_ndp_add_sta_rsp(struct mac_context *mac_ctx, + struct pe_session *session, + tAddStaParams *add_sta_rsp) +{ + struct nan_datapath_peer_ind *new_peer_ind; + struct wlan_objmgr_psoc *psoc = mac_ctx->psoc; + struct wlan_objmgr_vdev *vdev; + + if (!add_sta_rsp) { + pe_debug("Invalid add_sta_rsp"); + return QDF_STATUS_E_INVAL; + } + + if (!psoc) { + pe_debug("Invalid psoc"); + return QDF_STATUS_E_INVAL; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + add_sta_rsp->smesessionId, + WLAN_NAN_ID); + if (!vdev) { + pe_err("Failed to get vdev from id"); + return QDF_STATUS_E_INVAL; + } + + new_peer_ind = qdf_mem_malloc(sizeof(*new_peer_ind)); + if (!new_peer_ind) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_copy(new_peer_ind->peer_mac_addr.bytes, add_sta_rsp->staMac, + sizeof(tSirMacAddr)); + + ucfg_nan_datapath_event_handler(psoc, vdev, NDP_NEW_PEER, new_peer_ind); + qdf_mem_free(new_peer_ind); + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + return QDF_STATUS_SUCCESS; +} + +/** + * lim_ndp_add_sta_rsp() - handles add sta rsp for NDP from WMA + * @mac_ctx: handle to mac structure + * @session: session pointer + * @add_sta_rsp: add sta response struct + * + * Return: None + */ +void lim_ndp_add_sta_rsp(struct mac_context *mac_ctx, struct pe_session *session, + tAddStaParams *add_sta_rsp) +{ + tpDphHashNode sta_ds; + uint16_t peer_idx; + + if (!add_sta_rsp) { + pe_err("Invalid add_sta_rsp"); + qdf_mem_free(add_sta_rsp); + return; + } + + SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true); + sta_ds = dph_lookup_hash_entry(mac_ctx, add_sta_rsp->staMac, &peer_idx, + &session->dph.dphHashTable); + if (!sta_ds) { + pe_err("NAN: ADD_STA_RSP for unknown MAC addr " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(add_sta_rsp->staMac)); + qdf_mem_free(add_sta_rsp); + return; + } + + if (add_sta_rsp->status != QDF_STATUS_SUCCESS) { + pe_err("NAN: ADD_STA_RSP error %x for MAC addr: "QDF_MAC_ADDR_FMT, + add_sta_rsp->status, + QDF_MAC_ADDR_REF(add_sta_rsp->staMac)); + /* delete the sta_ds allocated during ADD STA */ + lim_delete_dph_hash_entry(mac_ctx, add_sta_rsp->staMac, + peer_idx, session); + qdf_mem_free(add_sta_rsp); + return; + } + sta_ds->valid = 1; + sta_ds->mlmStaContext.mlmState = eLIM_MLM_LINK_ESTABLISHED_STATE; + lim_send_sme_ndp_add_sta_rsp(mac_ctx, session, add_sta_rsp); + qdf_mem_free(add_sta_rsp); +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/nan/nan_datapath.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/nan/nan_datapath.h new file mode 100644 index 0000000000000000000000000000000000000000..332a2d9cd0f9fc7150c396cc9238d2b780ca17c4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/nan/nan_datapath.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: nan_datapath.h + * + * MAC NAN Data path API specification + */ + +#ifndef __MAC_NAN_DATAPATH_H +#define __MAC_NAN_DATAPATH_H + +#ifdef WLAN_FEATURE_NAN + +#include "sir_common.h" +#include "ani_global.h" +#include "sir_params.h" + +struct peer_nan_datapath_map; + +/** + * lim_process_ndi_mlm_add_bss_rsp() - Process ADD_BSS response for NDI + * @mac_ctx: Pointer to Global MAC structure + * @add_bss_rsp: Bss params including rsp data + * @session_entry: PE session + * + * Return: None + */ +void lim_process_ndi_mlm_add_bss_rsp(struct mac_context *mac_ctx, + struct add_bss_rsp *add_bss_rsp, + struct pe_session *session_entry); +/* Handler for DEL BSS resp for NDI interface */ + +/** + * lim_ndi_del_bss_rsp() - Handler for DEL BSS resp for NDI interface + * @mac_ctx: handle to mac structure + * @del_bss: pointer to del bss response + * @session_entry: session entry + * + * Return: None + */ +void lim_ndi_del_bss_rsp(struct mac_context * mac_ctx, + struct del_bss_resp *del_bss, + struct pe_session *session_entry); + +void lim_ndp_add_sta_rsp(struct mac_context *mac_ctx, + struct pe_session *session_entry, + tAddStaParams *add_sta_rsp); + +void lim_process_ndi_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg, + struct pe_session *pe_session); + +QDF_STATUS lim_add_ndi_peer_converged(uint32_t vdev_id, + struct qdf_mac_addr peer_mac_addr); + +void lim_ndp_delete_peers_converged(struct peer_nan_datapath_map *ndp_map, + uint8_t num_peers); + +void lim_ndp_delete_peers_by_addr_converged(uint8_t vdev_id, + struct qdf_mac_addr peer_ndi_mac_addr); + +#else +static inline +void lim_process_ndi_mlm_add_bss_rsp(struct mac_context *mac_ctx, + struct add_bss_rsp *add_bss_rsp, + struct pe_session *session_entry) +{ +} + +/** + * lim_ndi_del_bss_rsp() - Handler for DEL BSS resp for NDI interface + * @mac_ctx: handle to mac structure + * @del_bss: pointer to del bss response + * @session_entry: session entry + * + * Return: None + */ +static inline +void lim_ndi_del_bss_rsp(struct mac_context *mac_ctx, + struct del_bss_resp *del_bss, + struct pe_session *session_entry) +{ +} + +static inline +void lim_process_ndi_del_sta_rsp(struct mac_context *mac_ctx, + struct scheduler_msg *lim_msg, + struct pe_session *pe_session) +{ +} + +static inline +void lim_ndp_add_sta_rsp(struct mac_context *mac_ctx, + struct pe_session *session_entry, + tAddStaParams *add_sta_rsp) +{ +} + +#endif /* WLAN_FEATURE_NAN */ + +#endif /* __MAC_NAN_DATAPATH_H */ + diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/rrm/rrm_api.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/rrm/rrm_api.c new file mode 100644 index 0000000000000000000000000000000000000000..71b4235dbccf1e81980f1bffafd08cb2134d39d6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/rrm/rrm_api.c @@ -0,0 +1,1572 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + \file rrm_api.c + + \brief implementation for PE RRM APIs + + ========================================================================*/ + +/* $Header$ */ + + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "cds_api.h" +#include "wni_api.h" +#include "sir_api.h" +#include "ani_global.h" +#include "wni_cfg.h" +#include "lim_types.h" +#include "lim_utils.h" +#include "lim_send_sme_rsp_messages.h" +#include "parser_api.h" +#include "lim_send_messages.h" +#include "rrm_global.h" +#include "rrm_api.h" + +/* -------------------------------------------------------------------- */ +/** + * rrm_cache_mgmt_tx_power + ** + * FUNCTION: Store Tx power for management frames. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param pe_session session entry. + * @return None + */ +void +rrm_cache_mgmt_tx_power(struct mac_context *mac, int8_t txPower, + struct pe_session *pe_session) +{ + pe_debug("Cache Mgmt Tx Power: %d", txPower); + + if (!pe_session) + mac->rrm.rrmPEContext.txMgmtPower = txPower; + else + pe_session->txMgmtPower = txPower; +} + +/* -------------------------------------------------------------------- */ +/** + * rrm_get_mgmt_tx_power + * + * FUNCTION: Get the Tx power for management frames. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param pe_session session entry. + * @return txPower + */ +int8_t rrm_get_mgmt_tx_power(struct mac_context *mac, struct pe_session *pe_session) +{ + if (!pe_session) + return mac->rrm.rrmPEContext.txMgmtPower; + + pe_debug("tx mgmt pwr %d", pe_session->txMgmtPower); + + return pe_session->txMgmtPower; +} + +/* -------------------------------------------------------------------- */ +/** + * rrm_send_set_max_tx_power_req + * + * FUNCTION: Send WMA_SET_MAX_TX_POWER_REQ message to change the max tx power. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param txPower txPower to be set. + * @param pe_session session entry. + * @return None + */ +QDF_STATUS +rrm_send_set_max_tx_power_req(struct mac_context *mac, int8_t txPower, + struct pe_session *pe_session) +{ + tpMaxTxPowerParams pMaxTxParams; + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + struct scheduler_msg msgQ = {0}; + + if (!pe_session) { + pe_err("Invalid parameters"); + return QDF_STATUS_E_FAILURE; + } + pMaxTxParams = qdf_mem_malloc(sizeof(tMaxTxPowerParams)); + if (!pMaxTxParams) + return QDF_STATUS_E_NOMEM; + /* Allocated memory for pMaxTxParams...will be freed in other module */ + pMaxTxParams->power = txPower; + qdf_mem_copy(pMaxTxParams->bssId.bytes, pe_session->bssId, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(pMaxTxParams->selfStaMacAddr.bytes, + pe_session->self_mac_addr, + QDF_MAC_ADDR_SIZE); + + msgQ.type = WMA_SET_MAX_TX_POWER_REQ; + msgQ.reserved = 0; + msgQ.bodyptr = pMaxTxParams; + msgQ.bodyval = 0; + + pe_debug("Sending WMA_SET_MAX_TX_POWER_REQ with power(%d) to HAL", + txPower); + + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + pe_err("Posting WMA_SET_MAX_TX_POWER_REQ to HAL failed, reason=%X", + retCode); + qdf_mem_free(pMaxTxParams); + return retCode; + } + return retCode; +} + +/* -------------------------------------------------------------------- */ +/** + * rrm_set_max_tx_power_rsp + * + * FUNCTION: Process WMA_SET_MAX_TX_POWER_RSP message. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param txPower txPower to be set. + * @param pe_session session entry. + * @return None + */ +QDF_STATUS rrm_set_max_tx_power_rsp(struct mac_context *mac, + struct scheduler_msg *limMsgQ) +{ + QDF_STATUS retCode = QDF_STATUS_SUCCESS; + tpMaxTxPowerParams pMaxTxParams = (tpMaxTxPowerParams) limMsgQ->bodyptr; + struct pe_session *pe_session; + uint8_t sessionId, i; + + if (qdf_is_macaddr_broadcast(&pMaxTxParams->bssId)) { + for (i = 0; i < mac->lim.maxBssId; i++) { + if (mac->lim.gpSession[i].valid == true) { + pe_session = &mac->lim.gpSession[i]; + rrm_cache_mgmt_tx_power(mac, pMaxTxParams->power, + pe_session); + } + } + } else { + pe_session = pe_find_session_by_bssid(mac, + pMaxTxParams->bssId.bytes, + &sessionId); + if (!pe_session) { + retCode = QDF_STATUS_E_FAILURE; + } else { + rrm_cache_mgmt_tx_power(mac, pMaxTxParams->power, + pe_session); + } + } + + qdf_mem_free(limMsgQ->bodyptr); + limMsgQ->bodyptr = NULL; + return retCode; +} + +/** + * rrm_calculate_and_fill_rcpi() - calculates and fills RCPI value + * @rcpi: pointer to hold calculated RCPI value + * @cur_rssi: value of current RSSI + * + * @return None + */ +static void rrm_calculate_and_fill_rcpi(uint8_t *rcpi, int8_t cur_rssi) +{ + /* 2008 11k spec reference: 18.4.8.5 RCPI Measurement */ + if (cur_rssi <= RCPI_LOW_RSSI_VALUE) + *rcpi = 0; + else if ((cur_rssi > RCPI_LOW_RSSI_VALUE) && (cur_rssi <= 0)) + *rcpi = CALCULATE_RCPI(cur_rssi); + else + *rcpi = RCPI_MAX_VALUE; +} + +/* -------------------------------------------------------------------- */ +/** + * rrm_process_link_measurement_request + * + * FUNCTION: Processes the Link measurement request and send the report. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param pBd pointer to BD to extract RSSI and SNR + * @param pLinkReq pointer to the Link request frame structure. + * @param pe_session session entry. + * @return None + */ +QDF_STATUS +rrm_process_link_measurement_request(struct mac_context *mac, + uint8_t *pRxPacketInfo, + tDot11fLinkMeasurementRequest *pLinkReq, + struct pe_session *pe_session) +{ + tSirMacLinkReport LinkReport; + tpSirMacMgmtHdr pHdr; + int8_t currentRSSI = 0; + struct lim_max_tx_pwr_attr tx_pwr_attr = {0}; + struct vdev_mlme_obj *mlme_obj; + + pe_debug("Received Link measurement request"); + + if (!pRxPacketInfo || !pLinkReq || !pe_session) { + pe_err("Invalid parameters - Ignoring the request"); + return QDF_STATUS_E_FAILURE; + } + pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); + + tx_pwr_attr.reg_max = pe_session->def_max_tx_pwr; + tx_pwr_attr.ap_tx_power = pLinkReq->MaxTxPower.maxTxPower; + tx_pwr_attr.ini_tx_power = mac->mlme_cfg->power.max_tx_power; + + LinkReport.txPower = lim_get_max_tx_power(mac, &tx_pwr_attr); + + /** If firmware updated max tx power is non zero, respond to rrm link + * measurement request with min of firmware updated ap tx power and + * max power derived from lim_get_max_tx_power API. + */ + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(pe_session->vdev); + if (mlme_obj && mlme_obj->mgmt.generic.tx_pwrlimit) + LinkReport.txPower = QDF_MIN(LinkReport.txPower, + mlme_obj->mgmt.generic.tx_pwrlimit); + + if ((LinkReport.txPower != (uint8_t) (pe_session->maxTxPower)) && + (QDF_STATUS_SUCCESS == rrm_send_set_max_tx_power_req(mac, + LinkReport.txPower, + pe_session))) { + pe_warn("maxTx power in link report is not same as local..." + " Local: %d Link Request TxPower: %d" + " Link Report TxPower: %d", + pe_session->maxTxPower, LinkReport.txPower, + pLinkReq->MaxTxPower.maxTxPower); + pe_session->maxTxPower = + LinkReport.txPower; + } + + LinkReport.dialogToken = pLinkReq->DialogToken.token; + LinkReport.rxAntenna = 0; + LinkReport.txAntenna = 0; + currentRSSI = WMA_GET_RX_RSSI_RAW(pRxPacketInfo); + + pe_info("Received Link report frame with %d", currentRSSI); + + rrm_calculate_and_fill_rcpi(&LinkReport.rcpi, currentRSSI); + LinkReport.rsni = WMA_GET_RX_SNR(pRxPacketInfo); + + pe_debug("Sending Link report frame"); + + return lim_send_link_report_action_frame(mac, &LinkReport, pHdr->sa, + pe_session); +} + +/* -------------------------------------------------------------------- */ +/** + * rrm_process_neighbor_report_response + * + * FUNCTION: Processes the Neighbor Report response from the peer AP. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param pNeighborRep pointer to the Neighbor report frame structure. + * @param pe_session session entry. + * @return None + */ +QDF_STATUS +rrm_process_neighbor_report_response(struct mac_context *mac, + tDot11fNeighborReportResponse *pNeighborRep, + struct pe_session *pe_session) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpSirNeighborReportInd pSmeNeighborRpt = NULL; + uint16_t length; + uint8_t i; + struct scheduler_msg mmhMsg = {0}; + + if (!pNeighborRep || !pe_session) { + pe_err("Invalid parameters"); + return status; + } + + pe_debug("Neighbor report response received"); + + /* Dialog token */ + if (mac->rrm.rrmPEContext.DialogToken != + pNeighborRep->DialogToken.token) { + pe_err("Dialog token mismatch in the received Neighbor report"); + return QDF_STATUS_E_FAILURE; + } + if (pNeighborRep->num_NeighborReport == 0) { + pe_err("No neighbor report in the frame...Dropping it"); + return QDF_STATUS_E_FAILURE; + } + pe_debug("RRM:received num neighbor reports: %d", + pNeighborRep->num_NeighborReport); + if (pNeighborRep->num_NeighborReport > MAX_SUPPORTED_NEIGHBOR_RPT) + pNeighborRep->num_NeighborReport = MAX_SUPPORTED_NEIGHBOR_RPT; + length = (sizeof(tSirNeighborReportInd)) + + (sizeof(tSirNeighborBssDescription) * + (pNeighborRep->num_NeighborReport - 1)); + + /* Prepare the request to send to SME. */ + pSmeNeighborRpt = qdf_mem_malloc(length); + if (!pSmeNeighborRpt) + return QDF_STATUS_E_NOMEM; + + /* Allocated memory for pSmeNeighborRpt...will be freed by other module */ + + for (i = 0; i < pNeighborRep->num_NeighborReport; i++) { + pSmeNeighborRpt->sNeighborBssDescription[i].length = sizeof(tSirNeighborBssDescription); /*+ any optional ies */ + qdf_mem_copy(pSmeNeighborRpt->sNeighborBssDescription[i].bssId, + pNeighborRep->NeighborReport[i].bssid, + sizeof(tSirMacAddr)); + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fApPreauthReachable = + pNeighborRep->NeighborReport[i].APReachability; + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fSameSecurityMode = + pNeighborRep->NeighborReport[i].Security; + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fSameAuthenticator = + pNeighborRep->NeighborReport[i].KeyScope; + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fCapSpectrumMeasurement = + pNeighborRep->NeighborReport[i].SpecMgmtCap; + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fCapQos = pNeighborRep->NeighborReport[i].QosCap; + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fCapApsd = pNeighborRep->NeighborReport[i].apsd; + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fCapRadioMeasurement = pNeighborRep->NeighborReport[i].rrm; + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fCapDelayedBlockAck = + pNeighborRep->NeighborReport[i].DelayedBA; + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fCapImmediateBlockAck = + pNeighborRep->NeighborReport[i].ImmBA; + pSmeNeighborRpt->sNeighborBssDescription[i].bssidInfo.rrmInfo. + fMobilityDomain = + pNeighborRep->NeighborReport[i].MobilityDomain; + + if (!wlan_reg_is_6ghz_supported(mac->psoc) && + (wlan_reg_is_6ghz_op_class(mac->pdev, + pNeighborRep->NeighborReport[i]. + regulatoryClass))) { + pe_err("channel belongs to 6 ghz spectrum, abort"); + qdf_mem_free(pSmeNeighborRpt); + return QDF_STATUS_E_FAILURE; + } + + pSmeNeighborRpt->sNeighborBssDescription[i].regClass = + pNeighborRep->NeighborReport[i].regulatoryClass; + pSmeNeighborRpt->sNeighborBssDescription[i].channel = + pNeighborRep->NeighborReport[i].channel; + pSmeNeighborRpt->sNeighborBssDescription[i].phyType = + pNeighborRep->NeighborReport[i].PhyType; + } + + pSmeNeighborRpt->messageType = eWNI_SME_NEIGHBOR_REPORT_IND; + pSmeNeighborRpt->length = length; + pSmeNeighborRpt->measurement_idx = DEFAULT_RRM_IDX; + pSmeNeighborRpt->sessionId = pe_session->smeSessionId; + pSmeNeighborRpt->numNeighborReports = pNeighborRep->num_NeighborReport; + qdf_mem_copy(pSmeNeighborRpt->bssId, pe_session->bssId, + sizeof(tSirMacAddr)); + + /* Send request to SME. */ + mmhMsg.type = pSmeNeighborRpt->messageType; + mmhMsg.bodyptr = pSmeNeighborRpt; + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, mmhMsg.type)); + lim_sys_process_mmh_msg_api(mac, &mmhMsg); + + return status; + +} + +/* -------------------------------------------------------------------- */ +/** + * rrm_process_neighbor_report_req + * + * FUNCTION: + * + * LOGIC: Create a Neighbor report request and send it to peer. + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param pNeighborReq Neighbor report request params . + * @return None + */ +QDF_STATUS +rrm_process_neighbor_report_req(struct mac_context *mac, + tpSirNeighborReportReqInd pNeighborReq) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSirMacNeighborReportReq NeighborReportReq; + struct pe_session *pe_session; + uint8_t sessionId; + + if (!pNeighborReq) { + pe_err("NeighborReq is NULL"); + return QDF_STATUS_E_FAILURE; + } + pe_session = pe_find_session_by_bssid(mac, pNeighborReq->bssId, + &sessionId); + if (!pe_session) { + pe_err("session does not exist for given bssId"); + return QDF_STATUS_E_FAILURE; + } + + pe_debug("SSID present: %d", pNeighborReq->noSSID); + + qdf_mem_zero(&NeighborReportReq, sizeof(tSirMacNeighborReportReq)); + + NeighborReportReq.dialogToken = ++mac->rrm.rrmPEContext.DialogToken; + NeighborReportReq.ssid_present = !pNeighborReq->noSSID; + if (NeighborReportReq.ssid_present) { + qdf_mem_copy(&NeighborReportReq.ssid, &pNeighborReq->ucSSID, + sizeof(tSirMacSSid)); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_DEBUG, + (uint8_t *) NeighborReportReq.ssid.ssId, + NeighborReportReq.ssid.length); + } + + status = + lim_send_neighbor_report_request_frame(mac, &NeighborReportReq, + pNeighborReq->bssId, + pe_session); + + return status; +} + +/** + * rrm_get_country_code_from_connected_profile() - Get country code + * from connected profile + * @mac: Mac context + * @pe_session: pe session + * @country_code: country code + * + * Return: void + */ +static inline void +rrm_get_country_code_from_connected_profile( + struct mac_context *mac, + struct pe_session *pe_session, + uint8_t country_code[WNI_CFG_COUNTRY_CODE_LEN]) +{ + uint8_t id; + uint8_t *country; + + qdf_mem_zero(country_code, sizeof(country_code[0]) * + WNI_CFG_COUNTRY_CODE_LEN); + if (!pe_session) { + pe_err("pe_session is NULL"); + return; + } + id = pe_session->smeSessionId; + if (!CSR_IS_SESSION_VALID(mac, id)) { + pe_err("smeSessionId %d is invalid", id); + return; + } + country = + mac->roam.roamSession[id].connectedProfile.country_code; + if (country[0]) + qdf_mem_copy(country_code, country, sizeof(country_code[0]) * + WNI_CFG_COUNTRY_CODE_LEN); + else + country_code[2] = OP_CLASS_GLOBAL; +} + +#define ABS(x) ((x < 0) ? -x : x) +/* -------------------------------------------------------------------- */ +/** + * rrm_process_beacon_report_req + * + * FUNCTION: Processes the Beacon report request from the peer AP. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param pCurrentReq pointer to the current Req comtext. + * @param pBeaconReq pointer to the beacon report request IE from the peer. + * @param pe_session session entry. + * @return None + */ +static tRrmRetStatus +rrm_process_beacon_report_req(struct mac_context *mac, + tpRRMReq pCurrentReq, + tDot11fIEMeasurementRequest *pBeaconReq, + struct pe_session *pe_session) +{ + struct scheduler_msg mmh_msg = {0}; + tpSirBeaconReportReqInd psbrr; + uint8_t num_rpt, idx_rpt; + uint16_t measDuration, maxMeasduration; + int8_t maxDuration; + uint8_t sign; + tDot11fIEAPChannelReport *ie_ap_chan_rpt; + uint8_t tmp_idx, buf_left, buf_cons; + uint16_t ch_ctr = 0; + char ch_buf[RRM_CH_BUF_LEN]; + char *tmp_buf = NULL; + uint8_t country[WNI_CFG_COUNTRY_CODE_LEN]; + + if (!pe_session) { + pe_err("pe_session is NULL"); + return eRRM_INCAPABLE; + } + + if (pBeaconReq->measurement_request.Beacon.BeaconReporting.present && + (pBeaconReq->measurement_request.Beacon.BeaconReporting. + reportingCondition != 0)) { + /* Repeated measurement is not supported. This means number of repetitions should be zero.(Already checked) */ + /* All test case in VoWifi(as of version 0.36) use zero for number of repetitions. */ + /* Beacon reporting should not be included in request if number of repetitons is zero. */ + /* IEEE Std 802.11k-2008 Table 7-29g and section 11.10.8.1 */ + + pe_nofl_err("RX: [802.11 BCN_RPT] Dropping req: Reporting condition included is not zero"); + return eRRM_INCAPABLE; + } + + /* The logic here is to check the measurement duration passed in the beacon request. Following are the cases handled. + Case 1: If measurement duration received in the beacon request is greater than the max measurement duration advertised + in the RRM capabilities(Assoc Req), and Duration Mandatory bit is set to 1, REFUSE the beacon request + Case 2: If measurement duration received in the beacon request is greater than the max measurement duration advertised + in the RRM capabilities(Assoc Req), and Duration Mandatory bit is set to 0, perform measurement for + the duration advertised in the RRM capabilities + + maxMeasurementDuration = 2^(nonOperatingChanMax - 4) * BeaconInterval + */ + maxDuration = + mac->rrm.rrmPEContext.rrmEnabledCaps.nonOperatingChanMax - 4; + sign = (maxDuration < 0) ? 1 : 0; + maxDuration = (1L << ABS(maxDuration)); + if (!sign) + maxMeasduration = + maxDuration * pe_session->beaconParams.beaconInterval; + else + maxMeasduration = + pe_session->beaconParams.beaconInterval / maxDuration; + + measDuration = pBeaconReq->measurement_request.Beacon.meas_duration; + + pe_nofl_info("RX: [802.11 BCN_RPT] seq:%d SSID:%.*s BSSID:"QDF_MAC_ADDR_FMT" Token:%d op_class:%d ch:%d meas_mode:%d meas_duration:%d max_dur: %d sign: %d max_meas_dur: %d", + mac->rrm.rrmPEContext.prev_rrm_report_seq_num, + pBeaconReq->measurement_request.Beacon.SSID.num_ssid, + pBeaconReq->measurement_request.Beacon.SSID.ssid, + QDF_MAC_ADDR_REF(pBeaconReq->measurement_request.Beacon.BSSID), + pBeaconReq->measurement_token, + pBeaconReq->measurement_request.Beacon.regClass, + pBeaconReq->measurement_request.Beacon.channel, + pBeaconReq->measurement_request.Beacon.meas_mode, + measDuration, maxDuration, sign, maxMeasduration); + + if (measDuration == 0 && + pBeaconReq->measurement_request.Beacon.meas_mode != + eSIR_BEACON_TABLE) { + pe_nofl_err("RX: [802.11 BCN_RPT] Invalid measurement duration"); + return eRRM_REFUSED; + } + + if (maxMeasduration < measDuration) { + if (pBeaconReq->durationMandatory) { + pe_nofl_err("RX: [802.11 BCN_RPT] Dropping the req: duration mandatory & maxduration > measduration"); + return eRRM_REFUSED; + } else + measDuration = maxMeasduration; + } + /* Cache the data required for sending report. */ + pCurrentReq->request.Beacon.reportingDetail = + pBeaconReq->measurement_request.Beacon.BcnReportingDetail. + present ? pBeaconReq->measurement_request.Beacon.BcnReportingDetail. + reportingDetail : BEACON_REPORTING_DETAIL_ALL_FF_IE; + + if (pBeaconReq->measurement_request.Beacon. + last_beacon_report_indication.present) { + pCurrentReq->request.Beacon.last_beacon_report_indication = + pBeaconReq->measurement_request.Beacon. + last_beacon_report_indication.last_fragment; + pe_debug("RX: [802.11 BCN_RPT] Last Bcn Report in the req: %d", + pCurrentReq->request.Beacon.last_beacon_report_indication); + } else { + pCurrentReq->request.Beacon.last_beacon_report_indication = 0; + pe_debug("RX: [802.11 BCN_RPT] Last Bcn rpt ind not present"); + } + + if (pBeaconReq->measurement_request.Beacon.RequestedInfo.present) { + if (!pBeaconReq->measurement_request.Beacon.RequestedInfo. + num_requested_eids) { + pe_debug("RX: [802.11 BCN_RPT]: Requested num of EID is 0"); + return eRRM_FAILURE; + } + pCurrentReq->request.Beacon.reqIes.pElementIds = + qdf_mem_malloc(sizeof(uint8_t) * + pBeaconReq->measurement_request.Beacon. + RequestedInfo.num_requested_eids); + if (!pCurrentReq->request.Beacon.reqIes.pElementIds) + return eRRM_FAILURE; + + pCurrentReq->request.Beacon.reqIes.num = + pBeaconReq->measurement_request.Beacon.RequestedInfo. + num_requested_eids; + qdf_mem_copy(pCurrentReq->request.Beacon.reqIes.pElementIds, + pBeaconReq->measurement_request.Beacon. + RequestedInfo.requested_eids, + pCurrentReq->request.Beacon.reqIes.num); + } + + /* Prepare the request to send to SME. */ + psbrr = qdf_mem_malloc(sizeof(tSirBeaconReportReqInd)); + if (!psbrr) + return eRRM_FAILURE; + + /* Alloc memory for pSmeBcnReportReq, will be freed by other modules */ + qdf_mem_copy(psbrr->bssId, pe_session->bssId, + sizeof(tSirMacAddr)); + psbrr->messageType = eWNI_SME_BEACON_REPORT_REQ_IND; + psbrr->length = sizeof(tSirBeaconReportReqInd); + psbrr->uDialogToken = pBeaconReq->measurement_token; + psbrr->msgSource = eRRM_MSG_SOURCE_11K; + psbrr->randomizationInterval = + SYS_TU_TO_MS(pBeaconReq->measurement_request.Beacon.randomization); + psbrr->measurement_idx = pCurrentReq->measurement_idx; + + if (!wlan_reg_is_6ghz_supported(mac->psoc) && + (wlan_reg_is_6ghz_op_class(mac->pdev, + pBeaconReq->measurement_request.Beacon.regClass))) { + pe_nofl_err("RX: [802.11 BCN_RPT] Ch belongs to 6 ghz spectrum, abort"); + qdf_mem_free(psbrr); + return eRRM_FAILURE; + } + + rrm_get_country_code_from_connected_profile(mac, pe_session, + country); + psbrr->channel_info.chan_num = + pBeaconReq->measurement_request.Beacon.channel; + psbrr->channel_info.reg_class = + pBeaconReq->measurement_request.Beacon.regClass; + if (psbrr->channel_info.chan_num && + psbrr->channel_info.chan_num != 255) { + psbrr->channel_info.chan_freq = + wlan_reg_country_chan_opclass_to_freq( + mac->pdev, + country, + psbrr->channel_info.chan_num, + psbrr->channel_info.reg_class, + false); + if (!psbrr->channel_info.chan_freq) { + pe_debug("invalid ch freq, chan_num %d", + psbrr->channel_info.chan_num); + qdf_mem_free(psbrr); + return eRRM_FAILURE; + } + } else { + psbrr->channel_info.chan_freq = 0; + } + sme_debug("opclass %d, ch %d freq %d AP's country code %c%c 0x%x index:%d", + psbrr->channel_info.reg_class, + psbrr->channel_info.chan_num, + psbrr->channel_info.chan_freq, + country[0], country[1], country[2], + psbrr->measurement_idx); + + psbrr->measurementDuration[0] = measDuration; + psbrr->fMeasurementtype[0] = + pBeaconReq->measurement_request.Beacon.meas_mode; + qdf_mem_copy(psbrr->macaddrBssid, + pBeaconReq->measurement_request.Beacon.BSSID, + sizeof(tSirMacAddr)); + + if (pBeaconReq->measurement_request.Beacon.SSID.present) { + psbrr->ssId.length = + pBeaconReq->measurement_request.Beacon.SSID.num_ssid; + qdf_mem_copy(psbrr->ssId.ssId, + pBeaconReq->measurement_request.Beacon.SSID.ssid, + psbrr->ssId.length); + } + + pCurrentReq->token = pBeaconReq->measurement_token; + + num_rpt = pBeaconReq->measurement_request.Beacon.num_APChannelReport; + for (idx_rpt = 0; idx_rpt < num_rpt; idx_rpt++) { + ie_ap_chan_rpt = + &pBeaconReq->measurement_request.Beacon.APChannelReport[idx_rpt]; + for (tmp_idx = 0; + tmp_idx < ie_ap_chan_rpt->num_channelList; + tmp_idx++) { + if (!wlan_reg_is_6ghz_supported(mac->psoc) && + (wlan_reg_is_6ghz_op_class(mac->pdev, + ie_ap_chan_rpt->regulatoryClass))) { + pe_nofl_err("RX: [802.11 BCN_RPT] Ch belongs to 6 ghz spectrum, abort"); + qdf_mem_free(psbrr); + return eRRM_FAILURE; + } + + psbrr->channel_list.chan_freq_lst[ch_ctr++] = + wlan_reg_country_chan_opclass_to_freq( + mac->pdev, country, + ie_ap_chan_rpt->channelList[tmp_idx], + ie_ap_chan_rpt->regulatoryClass, true); + + if (ch_ctr >= QDF_ARRAY_SIZE(psbrr->channel_list.chan_freq_lst)) + break; + } + if (ch_ctr >= QDF_ARRAY_SIZE(psbrr->channel_list.chan_freq_lst)) + break; + } + + psbrr->channel_list.num_channels = ch_ctr; + buf_left = sizeof(ch_buf); + tmp_buf = ch_buf; + for (idx_rpt = 0; idx_rpt < ch_ctr; idx_rpt++) { + buf_cons = qdf_snprint(tmp_buf, buf_left, "%d ", + psbrr->channel_list.chan_freq_lst[idx_rpt]); + buf_left -= buf_cons; + tmp_buf += buf_cons; + } + + if (ch_ctr) + pe_nofl_info("RX: [802.11 BCN_RPT] Ch-list:%s", ch_buf); + + /* Send request to SME. */ + mmh_msg.type = eWNI_SME_BEACON_REPORT_REQ_IND; + mmh_msg.bodyptr = psbrr; + MTRACE(mac_trace(mac, TRACE_CODE_TX_SME_MSG, + pe_session->peSessionId, mmh_msg.type)); + lim_sys_process_mmh_msg_api(mac, &mmh_msg); + return eRRM_SUCCESS; +} + +/** + * rrm_fill_beacon_ies() - Fills fixed fields and Ies in bss description to an + * array of uint8_t. + * @pIes - pointer to the buffer that should be populated with ies. + * @pNumIes - returns the num of ies filled in this param. + * @pIesMaxSize - Max size of the buffer pIes. + * @eids - pointer to array of eids. If NULL, all ies will be populated. + * @numEids - number of elements in array eids. + * @start_offset: Offset from where the IEs in the bss_desc should be parsed + * @bss_desc - pointer to Bss Description. + * + * Return: Remaining length of IEs in current bss_desc which are not included + * in pIes. + */ +static uint8_t +rrm_fill_beacon_ies(struct mac_context *mac, uint8_t *pIes, + uint8_t *pNumIes, uint8_t pIesMaxSize, uint8_t *eids, + uint8_t numEids, uint8_t start_offset, + struct bss_description *bss_desc) +{ + uint8_t *pBcnIes, count = 0, i; + uint16_t BcnNumIes, total_ies_len, len; + uint8_t rem_len = 0; + + if ((!pIes) || (!pNumIes) || (!bss_desc)) { + pe_err("Invalid parameters"); + return 0; + } + /* Make sure that if eid is null, numEids is set to zero. */ + numEids = (!eids) ? 0 : numEids; + + total_ies_len = GET_IE_LEN_IN_BSS(bss_desc->length); + BcnNumIes = total_ies_len; + if (start_offset > BcnNumIes) { + pe_err("Invalid start offset %d Bcn IE len %d", + start_offset, total_ies_len); + return 0; + } + + pBcnIes = (uint8_t *)&bss_desc->ieFields[0]; + pBcnIes += start_offset; + BcnNumIes = BcnNumIes - start_offset; + + *pNumIes = 0; + + /* + * If start_offset is 0, this is the first fragment of the current + * beacon. Include the Beacon Fixed Fields of length 12 bytes + * (BEACON_FRAME_IES_OFFSET) in the first fragment. + */ + if (start_offset == 0) { + *((uint32_t *)pIes) = bss_desc->timeStamp[0]; + *pNumIes += sizeof(uint32_t); + pIes += sizeof(uint32_t); + *((uint32_t *)pIes) = bss_desc->timeStamp[1]; + *pNumIes += sizeof(uint32_t); + pIes += sizeof(uint32_t); + *((uint16_t *)pIes) = bss_desc->beaconInterval; + *pNumIes += sizeof(uint16_t); + pIes += sizeof(uint16_t); + *((uint16_t *)pIes) = bss_desc->capabilityInfo; + *pNumIes += sizeof(uint16_t); + pIes += sizeof(uint16_t); + } + + while (BcnNumIes >= 2) { + len = *(pBcnIes + 1); + len += 2; /* element id + length. */ + pe_debug("EID = %d, len = %d total = %d", + *pBcnIes, *(pBcnIes + 1), len); + + if (BcnNumIes < len || len <= 2) { + pe_err("RRM: Invalid IE len:%d exp_len:%d", + len, BcnNumIes); + break; + } + + i = 0; + do { + if ((!eids) || (*pBcnIes == eids[i])) { + if (((*pNumIes) + len) < pIesMaxSize) { + qdf_mem_copy(pIes, pBcnIes, len); + pIes += len; + *pNumIes += len; + count++; + } else { + /* + * If max size of fragment is reached, + * calculate the remaining length and + * break. For first fragment, account + * for the fixed fields also. + */ + rem_len = total_ies_len - *pNumIes; + if (start_offset == 0) + rem_len = rem_len + + BEACON_FRAME_IES_OFFSET; + pe_debug("rem_len %d ies added %d", + rem_len, *pNumIes); + } + break; + } + i++; + } while (i < numEids); + + if (rem_len) + break; + + pBcnIes += len; + BcnNumIes -= len; + } + pe_debug("Total length of Ies added = %d rem_len %d", + *pNumIes, rem_len); + + return rem_len; +} + +/** + * rrm_process_beacon_report_xmit() - create a rrm action frame + * @mac_ctx: Global pointer to MAC context + * @beacon_xmit_ind: Data for beacon report IE from SME. + * + * Create a Radio measurement report action frame and send it to peer. + * + * Return: QDF_STATUS + */ +QDF_STATUS +rrm_process_beacon_report_xmit(struct mac_context *mac_ctx, + tpSirBeaconReportXmitInd beacon_xmit_ind) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSirMacRadioMeasureReport *report = NULL; + tSirMacBeaconReport *beacon_report; + struct bss_description *bss_desc; + tpRRMReq curr_req; + struct pe_session *session_entry; + uint8_t session_id, counter; + uint8_t i, j, offset = 0; + uint8_t bss_desc_count = 0; + uint8_t report_index = 0; + uint8_t rem_len = 0; + uint8_t frag_id = 0; + uint8_t num_frames, num_reports_in_frame, final_measurement_index; + bool is_last_measurement_frame; + + + if (!beacon_xmit_ind) { + pe_err("Received beacon_xmit_ind is NULL in PE"); + return QDF_STATUS_E_FAILURE; + } + + if (beacon_xmit_ind->measurement_idx >= + QDF_ARRAY_SIZE(mac_ctx->rrm.rrmPEContext.pCurrentReq)) { + pe_err("Received measurement_idx is out of range: %u - %zu", + beacon_xmit_ind->measurement_idx, + QDF_ARRAY_SIZE(mac_ctx->rrm.rrmPEContext.pCurrentReq)); + return QDF_STATUS_E_FAILURE; + } + + curr_req = mac_ctx->rrm.rrmPEContext. + pCurrentReq[beacon_xmit_ind->measurement_idx]; + if (!curr_req) { + pe_err("Received report xmit while there is no request pending in PE"); + status = QDF_STATUS_E_FAILURE; + goto end; + } + + pe_debug("Received beacon report xmit indication on idx:%d", + beacon_xmit_ind->measurement_idx); + + /* + * Send empty report only if all channels on a measurement index has + * no scan results or if the AP requests for last beacon report + * indication and last channel of the last index has empty report + */ + if (beacon_xmit_ind->numBssDesc || curr_req->sendEmptyBcnRpt || + (beacon_xmit_ind->fMeasureDone && + curr_req->request.Beacon.last_beacon_report_indication && + (mac_ctx->rrm.rrmPEContext.num_active_request - 1) == 0)) { + beacon_xmit_ind->numBssDesc = (beacon_xmit_ind->numBssDesc == + RRM_BCN_RPT_NO_BSS_INFO) ? RRM_BCN_RPT_MIN_RPT : + beacon_xmit_ind->numBssDesc; + + session_entry = pe_find_session_by_bssid(mac_ctx, + beacon_xmit_ind->bssId, &session_id); + if (!session_entry) { + pe_err("TX: [802.11 BCN_RPT] Session does not exist for bssId:"QDF_MAC_ADDR_FMT"", + QDF_MAC_ADDR_REF(beacon_xmit_ind->bssId)); + status = QDF_STATUS_E_FAILURE; + goto end; + } + + report = qdf_mem_malloc(MAX_BEACON_REPORTS * sizeof(*report)); + + if (!report) { + pe_err("RRM Report is NULL, allocation failed"); + status = QDF_STATUS_E_NOMEM; + goto end; + } + + for (i = 0; i < MAX_BEACON_REPORTS && + bss_desc_count < beacon_xmit_ind->numBssDesc; i++) { + beacon_report = &report[i].report.beaconReport; + /* + * If the scan result is NULL then send report request + * with option subelement as NULL. + */ + pe_debug("TX: [802.11 BCN_RPT] report %d bss %d", i, + bss_desc_count); + bss_desc = beacon_xmit_ind-> + pBssDescription[bss_desc_count]; + + /* Prepare the beacon report and send it to the peer.*/ + report[i].token = beacon_xmit_ind->uDialogToken; + report[i].refused = 0; + report[i].incapable = 0; + report[i].type = SIR_MAC_RRM_BEACON_TYPE; + + /* + * Valid response is included if the size of + * becon xmit is == size of beacon xmit ind + ies + */ + if (beacon_xmit_ind->length < sizeof(*beacon_xmit_ind)) + continue; + beacon_report->regClass = beacon_xmit_ind->regClass; + if (bss_desc) { + beacon_report->channel = + wlan_reg_freq_to_chan( + mac_ctx->pdev, + bss_desc->chan_freq); + qdf_mem_copy(beacon_report->measStartTime, + bss_desc->startTSF, + sizeof(bss_desc->startTSF)); + beacon_report->measDuration = + beacon_xmit_ind->duration; + beacon_report->phyType = bss_desc->nwType; + beacon_report->bcnProbeRsp = 1; + beacon_report->rsni = bss_desc->sinr; + + rrm_calculate_and_fill_rcpi(&beacon_report->rcpi, + bss_desc->rssi); + beacon_report->antennaId = 0; + beacon_report->parentTSF = bss_desc->parentTSF; + qdf_mem_copy(beacon_report->bssid, + bss_desc->bssId, sizeof(tSirMacAddr)); + } + + pe_debug("TX: [802.11 BCN_RPT] reporting detail requested %d", + curr_req->request.Beacon.reportingDetail); + switch (curr_req->request.Beacon.reportingDetail) { + case BEACON_REPORTING_DETAIL_NO_FF_IE: + /* 0: No need to include any elements. */ + break; + case BEACON_REPORTING_DETAIL_ALL_FF_REQ_IE: + /* 1: Include all FFs and Requested Ies. */ + if (!bss_desc) + break; + + rem_len = rrm_fill_beacon_ies(mac_ctx, + (uint8_t *)&beacon_report->Ies[0], + (uint8_t *)&beacon_report->numIes, + BEACON_REPORT_MAX_IES, + curr_req->request.Beacon.reqIes. + pElementIds, + curr_req->request.Beacon.reqIes.num, + offset, bss_desc); + break; + case BEACON_REPORTING_DETAIL_ALL_FF_IE: + /* 2: default - Include all FFs and all Ies. */ + default: + if (!bss_desc) + break; + + rem_len = rrm_fill_beacon_ies(mac_ctx, + (uint8_t *) &beacon_report->Ies[0], + (uint8_t *) &beacon_report->numIes, + BEACON_REPORT_MAX_IES, + NULL, + 0, + offset, bss_desc); + break; + } + beacon_report->frame_body_frag_id.id = bss_desc_count; + beacon_report->frame_body_frag_id.frag_id = frag_id; + /* + * If remaining length is non-zero, the beacon needs to + * be fragmented only if the current request supports + * last beacon report indication. + * If last beacon report indication is not supported, + * truncate and move on to the next beacon. + */ + if (rem_len && + curr_req->request.Beacon. + last_beacon_report_indication) { + offset = GET_IE_LEN_IN_BSS( + bss_desc->length) - rem_len; + pe_debug("TX: [802.11 BCN_RPT] offset %d ie_len %lu rem_len %d frag_id %d", + offset, + GET_IE_LEN_IN_BSS(bss_desc->length), + rem_len, frag_id); + frag_id++; + beacon_report->frame_body_frag_id.more_frags = + true; + } else { + offset = 0; + beacon_report->frame_body_frag_id.more_frags = + false; + frag_id = 0; + bss_desc_count++; + pe_debug("TX: [802.11 BCN_RPT] No remaining IEs"); + } + + if (curr_req->request.Beacon.last_beacon_report_indication) + beacon_report->last_bcn_report_ind_support = 1; + + } + + pe_debug("TX: [802.11 BCN_RPT] Total reports filled %d, last bcn_rpt ind:%d", + i , curr_req->request.Beacon.last_beacon_report_indication); + + num_frames = i / RADIO_REPORTS_MAX_IN_A_FRAME; + if (i % RADIO_REPORTS_MAX_IN_A_FRAME) + num_frames++; + + for (j = 0; j < num_frames; j++) { + num_reports_in_frame = QDF_MIN((i - report_index), + RADIO_REPORTS_MAX_IN_A_FRAME); + + final_measurement_index = + mac_ctx->rrm.rrmPEContext.num_active_request; + is_last_measurement_frame = + ((j == num_frames - 1) && + beacon_xmit_ind->fMeasureDone && + !(final_measurement_index - 1)); + + lim_send_radio_measure_report_action_frame(mac_ctx, + curr_req->dialog_token, num_reports_in_frame, + is_last_measurement_frame, + &report[report_index], + beacon_xmit_ind->bssId, session_entry); + report_index += num_reports_in_frame; + } + curr_req->sendEmptyBcnRpt = false; + } + +end: + for (counter = 0; counter < beacon_xmit_ind->numBssDesc; counter++) + qdf_mem_free(beacon_xmit_ind->pBssDescription[counter]); + + if (beacon_xmit_ind->fMeasureDone) { + pe_debug("Measurement done idx:%d", + beacon_xmit_ind->measurement_idx); + rrm_cleanup(mac_ctx, beacon_xmit_ind->measurement_idx); + } + + if (report) + qdf_mem_free(report); + + return status; +} + +static void +rrm_process_beacon_request_failure(struct mac_context *mac, + struct pe_session *pe_session, + tSirMacAddr peer, + tRrmRetStatus status, uint8_t index) +{ + tpSirMacRadioMeasureReport pReport = NULL; + tpRRMReq pCurrentReq = mac->rrm.rrmPEContext.pCurrentReq[index]; + + if (!pCurrentReq) { + pe_err("Current request is NULL"); + return; + } + + pReport = qdf_mem_malloc(sizeof(tSirMacRadioMeasureReport)); + if (!pReport) + return; + pReport->token = pCurrentReq->token; + pReport->type = SIR_MAC_RRM_BEACON_TYPE; + + pe_debug("Measurement index:%d status %d token %d", index, status, + pReport->token); + + switch (status) { + case eRRM_REFUSED: + pReport->refused = 1; + break; + case eRRM_INCAPABLE: + pReport->incapable = 1; + break; + default: + pe_err("RX [802.11 BCN_RPT] Beacon request processing failed no report sent"); + qdf_mem_free(pReport); + return; + } + + if (pCurrentReq->request.Beacon.last_beacon_report_indication) + pReport->report.beaconReport.last_bcn_report_ind_support = 1; + + lim_send_radio_measure_report_action_frame(mac, + pCurrentReq->dialog_token, + 1, true, + pReport, peer, + pe_session); + + qdf_mem_free(pReport); + return; +} + +/** + * rrm_process_beacon_req() - Update curr_req and report + * @mac_ctx: Global pointer to MAC context + * @peer: Macaddress of the peer requesting the radio measurement + * @session_entry: session entry + * @radiomes_report: Pointer to radio measurement report + * @rrm_req: Array of Measurement request IEs + * @num_report: No.of reports + * @index: Index for Measurement request + * + * Update structure sRRMReq and sSirMacRadioMeasureReport and pass it to + * rrm_process_beacon_report_req(). + * + * Return: QDF_STATUS + */ +static +QDF_STATUS rrm_process_beacon_req(struct mac_context *mac_ctx, tSirMacAddr peer, + struct pe_session *session_entry, + tpSirMacRadioMeasureReport *radiomes_report, + tDot11fRadioMeasurementRequest *rrm_req, + uint8_t *num_report, int index) +{ + tRrmRetStatus rrm_status = eRRM_SUCCESS; + tpSirMacRadioMeasureReport report = NULL; + tpRRMReq curr_req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (index >= MAX_MEASUREMENT_REQUEST) { + status = rrm_reject_req(&report, rrm_req, num_report, index, + rrm_req->MeasurementRequest[0]. + measurement_type); + return status; + } + + curr_req = qdf_mem_malloc(sizeof(*curr_req)); + if (!curr_req) { + mac_ctx->rrm.rrmPEContext.pCurrentReq[index] = NULL; + return QDF_STATUS_E_NOMEM; + } + pe_debug("Processing Beacon Report request %d", index); + curr_req->dialog_token = rrm_req->DialogToken.token; + curr_req->token = + rrm_req->MeasurementRequest[index].measurement_token; + curr_req->sendEmptyBcnRpt = true; + curr_req->measurement_idx = index; + mac_ctx->rrm.rrmPEContext.pCurrentReq[index] = curr_req; + mac_ctx->rrm.rrmPEContext.num_active_request++; + pe_debug("Processing Bcn Report req %d num_active_req:%d", + index, mac_ctx->rrm.rrmPEContext.num_active_request); + rrm_status = rrm_process_beacon_report_req(mac_ctx, curr_req, + &rrm_req->MeasurementRequest[index], session_entry); + if (eRRM_SUCCESS != rrm_status) { + rrm_process_beacon_request_failure(mac_ctx, + session_entry, peer, rrm_status, index); + rrm_cleanup(mac_ctx, index); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * update_rrm_report() - Set incapable bit + * @mac_ctx: Global pointer to MAC context + * @report: Pointer to radio measurement report + * @rrm_req: Array of Measurement request IEs + * @num_report: No.of reports + * @index: Index for Measurement request + * + * Send a report with incapabale bit set + * + * Return: QDF_STATUS + */ +static +QDF_STATUS update_rrm_report(struct mac_context *mac_ctx, + tpSirMacRadioMeasureReport *report, + tDot11fRadioMeasurementRequest *rrm_req, + uint8_t *num_report, int index) +{ + tpSirMacRadioMeasureReport rrm_report; + + if (!*report) { + /* + * Allocate memory to send reports for + * any subsequent requests. + */ + *report = qdf_mem_malloc(sizeof(tSirMacRadioMeasureReport) * + (rrm_req->num_MeasurementRequest - index)); + if (!*report) + return QDF_STATUS_E_NOMEM; + pe_debug("rrm beacon type incapable of %d report", *num_report); + } + rrm_report = *report; + rrm_report[*num_report].incapable = 1; + rrm_report[*num_report].type = + rrm_req->MeasurementRequest[index].measurement_type; + rrm_report[*num_report].token = + rrm_req->MeasurementRequest[index].measurement_token; + (*num_report)++; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS rrm_reject_req(tpSirMacRadioMeasureReport *radiomes_report, + tDot11fRadioMeasurementRequest *rrm_req, + uint8_t *num_report, uint8_t index, + uint8_t measurement_type) +{ + tpSirMacRadioMeasureReport report; + + if (!*radiomes_report) { + /* + * Allocate memory to send reports for + * any subsequent requests. + */ + *radiomes_report = qdf_mem_malloc(sizeof(*report) * + (rrm_req->num_MeasurementRequest - index)); + if (!*radiomes_report) + return QDF_STATUS_E_NOMEM; + + pe_debug("rrm beacon refused of %d report, index: %d in beacon table", + *num_report, index); + } + report = *radiomes_report; + report[*num_report].refused = 1; + report[*num_report].type = measurement_type; + report[*num_report].token = + rrm_req->MeasurementRequest[index].measurement_token; + (*num_report)++; + + return QDF_STATUS_SUCCESS; + +} + +/* -------------------------------------------------------------------- */ +/** + * rrm_process_radio_measurement_request - Process rrm request + * @mac_ctx: Global pointer to MAC context + * @peer: Macaddress of the peer requesting the radio measurement. + * @rrm_req: Array of Measurement request IEs + * @session_entry: session entry. + * + * Processes the Radio Resource Measurement request. + * + * Return: QDF_STATUS + */ +QDF_STATUS +rrm_process_radio_measurement_request(struct mac_context *mac_ctx, + tSirMacAddr peer, + tDot11fRadioMeasurementRequest *rrm_req, + struct pe_session *session_entry) +{ + uint8_t i, index; + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpSirMacRadioMeasureReport report = NULL; + uint8_t num_report = 0; + bool reject = false; + + if (!rrm_req->num_MeasurementRequest) { + report = qdf_mem_malloc(sizeof(tSirMacRadioMeasureReport)); + if (!report) + return QDF_STATUS_E_NOMEM; + pe_err("RX: [802.11 RRM] No requestIes in the measurement request, sending incapable report"); + report->incapable = 1; + num_report = 1; + lim_send_radio_measure_report_action_frame(mac_ctx, + rrm_req->DialogToken.token, num_report, true, + report, peer, session_entry); + qdf_mem_free(report); + return QDF_STATUS_E_FAILURE; + } + /* PF Fix */ + if (rrm_req->NumOfRepetitions.repetitions > 0) { + pe_info("RX: [802.11 RRM] number of repetitions %d, sending incapable report", + rrm_req->NumOfRepetitions.repetitions); + /* + * Send a report with incapable bit set. + * Not supporting repetitions. + */ + report = qdf_mem_malloc(sizeof(tSirMacRadioMeasureReport)); + if (!report) + return QDF_STATUS_E_NOMEM; + report->incapable = 1; + report->type = rrm_req->MeasurementRequest[0].measurement_type; + num_report = 1; + goto end; + } + + for (index = 0; index < MAX_MEASUREMENT_REQUEST; index++) { + if (mac_ctx->rrm.rrmPEContext.pCurrentReq[index]) { + reject = true; + pe_debug("RRM req for index: %d is already in progress", + index); + break; + } + } + + if (reject) { + for (i = 0; i < rrm_req->num_MeasurementRequest; i++) { + status = + rrm_reject_req(&report, rrm_req, &num_report, i, + rrm_req->MeasurementRequest[i]. + measurement_type); + if (QDF_IS_STATUS_ERROR(status)) { + pe_debug("Fail to Reject rrm req for index: %d", + i); + return status; + } + } + + goto end; + } + + /* + * Clear global beacon_rpt_chan_list before processing every new + * beacon report request. + */ + qdf_mem_zero(mac_ctx->rrm.rrmPEContext.beacon_rpt_chan_list, + sizeof(uint8_t) * MAX_NUM_CHANNELS); + mac_ctx->rrm.rrmPEContext.beacon_rpt_chan_num = 0; + + for (i = 0; i < rrm_req->num_MeasurementRequest; i++) { + switch (rrm_req->MeasurementRequest[i].measurement_type) { + case SIR_MAC_RRM_BEACON_TYPE: + /* Process beacon request. */ + status = rrm_process_beacon_req(mac_ctx, peer, + session_entry, &report, + rrm_req, &num_report, + i); + if (QDF_IS_STATUS_ERROR(status)) + return status; + break; + case SIR_MAC_RRM_LCI_TYPE: + case SIR_MAC_RRM_LOCATION_CIVIC_TYPE: + case SIR_MAC_RRM_FINE_TIME_MEAS_TYPE: + pe_debug("RRM with type: %d sent to userspace", + rrm_req->MeasurementRequest[i].measurement_type); + break; + default: + /* Send a report with incapabale bit set. */ + status = update_rrm_report(mac_ctx, &report, rrm_req, + &num_report, i); + if (QDF_STATUS_SUCCESS != status) + return status; + break; + } + } + +end: + if (report) { + lim_send_radio_measure_report_action_frame(mac_ctx, + rrm_req->DialogToken.token, num_report, true, + report, peer, session_entry); + qdf_mem_free(report); + } + return status; +} + +/** + * rrm_get_start_tsf + * + * FUNCTION: Get the Start TSF. + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param startTSF - store star TSF in this buffer. + * @return txPower + */ +void rrm_get_start_tsf(struct mac_context *mac, uint32_t *pStartTSF) +{ + pStartTSF[0] = mac->rrm.rrmPEContext.startTSF[0]; + pStartTSF[1] = mac->rrm.rrmPEContext.startTSF[1]; + +} + +/* -------------------------------------------------------------------- */ +/** + * rrm_get_capabilities + * + * FUNCTION: + * Returns a pointer to tpRRMCaps with all the caps enabled in RRM + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param pe_session + * @return pointer to tRRMCaps + */ +tpRRMCaps rrm_get_capabilities(struct mac_context *mac, struct pe_session *pe_session) +{ + return &mac->rrm.rrmPEContext.rrmEnabledCaps; +} + +/** + * rrm_initialize() - Initialize PE RRM parameters + * @mac: Pointer to mac context + * + * Return: QDF_STATUS + */ +QDF_STATUS rrm_initialize(struct mac_context *mac) +{ + tpRRMCaps pRRMCaps = &mac->rrm.rrmPEContext.rrmEnabledCaps; + uint8_t i; + + for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) + mac->rrm.rrmPEContext.pCurrentReq[i] = NULL; + + mac->rrm.rrmPEContext.txMgmtPower = 0; + mac->rrm.rrmPEContext.DialogToken = 0; + + mac->rrm.rrmPEContext.rrmEnable = 0; + mac->rrm.rrmPEContext.prev_rrm_report_seq_num = 0xFFFF; + mac->rrm.rrmPEContext.num_active_request = 0; + + qdf_mem_zero(pRRMCaps, sizeof(tRRMCaps)); + pRRMCaps->LinkMeasurement = 1; + pRRMCaps->NeighborRpt = 1; + pRRMCaps->BeaconPassive = 1; + pRRMCaps->BeaconActive = 1; + pRRMCaps->BeaconTable = 1; + pRRMCaps->APChanReport = 1; + pRRMCaps->fine_time_meas_rpt = 1; + pRRMCaps->lci_capability = 1; + + pRRMCaps->operatingChanMax = 3; + pRRMCaps->nonOperatingChanMax = 3; + + return QDF_STATUS_SUCCESS; +} + +void rrm_cleanup(struct mac_context *mac, uint8_t idx) +{ + tpRRMReq cur_rrm_req = NULL; + + mac->rrm.rrmPEContext.num_active_request--; + pe_debug("Beacon report cleanup idx:%d, num_active_request:%d", + idx, mac->rrm.rrmPEContext.num_active_request); + cur_rrm_req = mac->rrm.rrmPEContext.pCurrentReq[idx]; + if (!cur_rrm_req) + return; + + qdf_mem_free(cur_rrm_req->request.Beacon.reqIes.pElementIds); + cur_rrm_req->request.Beacon.reqIes.pElementIds = NULL; + cur_rrm_req->request.Beacon.reqIes.num = 0; + + qdf_mem_free(cur_rrm_req); + mac->rrm.rrmPEContext.pCurrentReq[idx] = NULL; +} + +/** + * lim_update_rrm_capability() - Update PE context's rrm capability + * @mac_ctx: Global pointer to MAC context + * @join_req: Pointer to SME join request. + * + * Update PE context's rrm capability based on SME join request. + * + * Return: None + */ +void lim_update_rrm_capability(struct mac_context *mac_ctx, + struct join_req *join_req) +{ + mac_ctx->rrm.rrmPEContext.rrmEnable = join_req->rrm_config.rrm_enabled; + qdf_mem_copy(&mac_ctx->rrm.rrmPEContext.rrmEnabledCaps, + &join_req->rrm_config.rm_capability, + RMENABLEDCAP_MAX_LEN); + + return; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_api.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_api.c new file mode 100644 index 0000000000000000000000000000000000000000..130278e276d78301c53aea3597f6de09178010e4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_api.c @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file sch_api.cc contains functions related to the API exposed + * by scheduler module + * + * Author: Sandesh Goel + * Date: 02/25/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#include "cds_api.h" +#include "ani_global.h" +#include "wni_cfg.h" + +#include "sir_mac_prot_def.h" +#include "sir_mac_prop_exts.h" +#include "sir_common.h" + +#include "lim_api.h" + +#include "sch_api.h" + +#include "lim_trace.h" +#include "lim_types.h" +#include "lim_utils.h" + +#include "wma_types.h" + +QDF_STATUS sch_send_beacon_req(struct mac_context *mac, uint8_t *beaconPayload, + uint16_t size, struct pe_session *pe_session, + enum sir_bcn_update_reason reason) +{ + struct scheduler_msg msgQ = {0}; + tpSendbeaconParams beaconParams = NULL; + QDF_STATUS retCode; + + pe_debug("Indicating HAL to copy the beacon template [%d bytes] to memory, reason %d", + size, reason); + + if (LIM_IS_AP_ROLE(pe_session) && + (mac->sch.beacon_changed)) { + retCode = lim_send_probe_rsp_template_to_hal(mac, + pe_session, + &pe_session->DefProbeRspIeBitmap[0]); + if (QDF_STATUS_SUCCESS != retCode) + pe_err("FAILED to send probe response template with retCode %d", + retCode); + } + + beaconParams = qdf_mem_malloc(sizeof(tSendbeaconParams)); + if (!beaconParams) + return QDF_STATUS_E_NOMEM; + + msgQ.type = WMA_SEND_BEACON_REQ; + + /* No Dialog Token reqd, as a response is not solicited */ + msgQ.reserved = 0; + + /* Fill in tSendbeaconParams members */ + qdf_mem_copy(beaconParams->bssId, pe_session->bssId, + sizeof(pe_session->bssId)); + + if (LIM_IS_IBSS_ROLE(pe_session)) { + beaconParams->timIeOffset = 0; + } else { + beaconParams->timIeOffset = pe_session->schBeaconOffsetBegin; + if (pe_session->dfsIncludeChanSwIe) { + beaconParams->csa_count_offset = + mac->sch.csa_count_offset; + beaconParams->ecsa_count_offset = + mac->sch.ecsa_count_offset; + } + } + + beaconParams->vdev_id = pe_session->smeSessionId; + beaconParams->reason = reason; + + /* p2pIeOffset should be atleast greater than timIeOffset */ + if ((mac->sch.p2p_ie_offset != 0) && + (mac->sch.p2p_ie_offset < + pe_session->schBeaconOffsetBegin)) { + pe_err("Invalid p2pIeOffset:[%d]", + mac->sch.p2p_ie_offset); + QDF_ASSERT(0); + qdf_mem_free(beaconParams); + return QDF_STATUS_E_FAILURE; + } + beaconParams->p2pIeOffset = mac->sch.p2p_ie_offset; + + if (size > SIR_MAX_BEACON_SIZE) { + pe_err("beacon size (%d) exceed host limit %d", + size, SIR_MAX_BEACON_SIZE); + QDF_ASSERT(0); + qdf_mem_free(beaconParams); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(beaconParams->beacon, beaconPayload, size); + + beaconParams->beaconLength = (uint32_t) size; + msgQ.bodyptr = beaconParams; + msgQ.bodyval = 0; + + /* Keep a copy of recent beacon frame sent */ + + /* free previous copy of the beacon */ + if (pe_session->beacon) { + qdf_mem_free(pe_session->beacon); + } + + pe_session->bcnLen = 0; + pe_session->beacon = NULL; + + pe_session->beacon = qdf_mem_malloc(size); + if (pe_session->beacon) { + qdf_mem_copy(pe_session->beacon, beaconPayload, size); + pe_session->bcnLen = size; + } + + MTRACE(mac_trace_msg_tx(mac, pe_session->peSessionId, msgQ.type)); + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) + pe_err("Posting SEND_BEACON_REQ to HAL failed, reason=%X", + retCode); + + return retCode; +} + +static uint32_t lim_remove_p2p_ie_from_add_ie(struct mac_context *mac, + struct pe_session *pe_session, + uint8_t *addIeWoP2pIe, + uint32_t *addnIELenWoP2pIe) +{ + uint32_t left = pe_session->add_ie_params.probeRespDataLen; + uint8_t *ptr = pe_session->add_ie_params.probeRespData_buff; + uint8_t elem_id, elem_len; + uint32_t offset = 0; + uint8_t eid = 0xDD; + + qdf_mem_copy(addIeWoP2pIe, ptr, left); + *addnIELenWoP2pIe = left; + + if (addIeWoP2pIe) { + while (left >= 2) { + elem_id = ptr[0]; + elem_len = ptr[1]; + left -= 2; + if (elem_len > left) { + pe_err("Invalid IEs"); + return QDF_STATUS_E_FAILURE; + } + if ((elem_id == eid) && + (!qdf_mem_cmp(&ptr[2], + "\x50\x6f\x9a\x09", 4))) { + left -= elem_len; + ptr += (elem_len + 2); + qdf_mem_copy(&addIeWoP2pIe[offset], ptr, left); + *addnIELenWoP2pIe -= (2 + elem_len); + } else { + left -= elem_len; + ptr += (elem_len + 2); + offset += 2 + elem_len; + } + } + } + return QDF_STATUS_SUCCESS; +} + +uint32_t lim_send_probe_rsp_template_to_hal(struct mac_context *mac, + struct pe_session *pe_session, + uint32_t *IeBitmap) +{ + struct scheduler_msg msgQ = {0}; + uint8_t *pFrame2Hal = pe_session->pSchProbeRspTemplate; + tpSendProbeRespParams pprobeRespParams = NULL; + uint32_t retCode = QDF_STATUS_E_FAILURE; + uint32_t nPayload, nBytes = 0, nStatus; + tpSirMacMgmtHdr pMacHdr; + uint32_t addnIEPresent = false; + uint8_t *addIE = NULL; + uint8_t *addIeWoP2pIe = NULL; + uint32_t addnIELenWoP2pIe = 0; + uint32_t retStatus; + tDot11fIEExtCap extracted_extcap; + bool extcap_present = false; + tDot11fProbeResponse *prb_rsp_frm; + QDF_STATUS status; + uint16_t addn_ielen = 0; + + /* Check if probe response IE is present or not */ + addnIEPresent = (pe_session->add_ie_params.probeRespDataLen != 0); + if (addnIEPresent) { + /* + * probe response template should not have P2P IE. + * In case probe request has P2P IE or WPS IE, the + * probe request will be forwarded to the Host and + * Host will send the probe response. In other cases + * FW will send the probe response. So, if the template + * has P2P IE, the probe response sent to non P2P devices + * by the FW, may also have P2P IE which will fail + * P2P cert case 6.1.3 + */ + addIeWoP2pIe = qdf_mem_malloc(pe_session->add_ie_params. + probeRespDataLen); + if (!addIeWoP2pIe) { + pe_err("FAILED to alloc memory when removing P2P IE"); + return QDF_STATUS_E_NOMEM; + } + + retStatus = lim_remove_p2p_ie_from_add_ie(mac, pe_session, + addIeWoP2pIe, &addnIELenWoP2pIe); + if (retStatus != QDF_STATUS_SUCCESS) { + qdf_mem_free(addIeWoP2pIe); + return QDF_STATUS_E_FAILURE; + } + + /* Probe rsp IE available */ + /*need to check the data length */ + addIE = qdf_mem_malloc(addnIELenWoP2pIe); + if (!addIE) { + pe_err("Unable to get WNI_CFG_PROBE_RSP_ADDNIE_DATA1 length"); + qdf_mem_free(addIeWoP2pIe); + return QDF_STATUS_E_NOMEM; + } + addn_ielen = addnIELenWoP2pIe; + + if (addn_ielen <= WNI_CFG_PROBE_RSP_ADDNIE_DATA1_LEN && + addn_ielen && (nBytes + addn_ielen) <= SIR_MAX_PACKET_SIZE) + qdf_mem_copy(addIE, addIeWoP2pIe, addnIELenWoP2pIe); + + qdf_mem_free(addIeWoP2pIe); + + qdf_mem_zero((uint8_t *)&extracted_extcap, + sizeof(tDot11fIEExtCap)); + status = lim_strip_extcap_update_struct(mac, addIE, + &addn_ielen, &extracted_extcap); + if (QDF_STATUS_SUCCESS != status) { + pe_debug("extcap not extracted"); + } else { + extcap_present = true; + } + } + + /* + * Extcap IE now support variable length, merge Extcap IE from addn_ie + * may change the frame size. Therefore, MUST merge ExtCap IE before + * dot11f get packed payload size. + */ + prb_rsp_frm = &pe_session->probeRespFrame; + if (extcap_present) + lim_merge_extcap_struct(&prb_rsp_frm->ExtCap, + &extracted_extcap, + true); + + nStatus = dot11f_get_packed_probe_response_size(mac, + &pe_session->probeRespFrame, &nPayload); + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to calculate the packed size for a Probe Response (0x%08x)", + nStatus); + /* We'll fall back on the worst case scenario: */ + nPayload = sizeof(tDot11fProbeResponse); + } else if (DOT11F_WARNED(nStatus)) { + pe_err("There were warnings while calculating the packed size for a Probe Response (0x%08x)", + nStatus); + } + + nBytes += nPayload + sizeof(tSirMacMgmtHdr); + + if (addnIEPresent) { + if ((nBytes + addn_ielen) <= SIR_MAX_PROBE_RESP_SIZE) + nBytes += addn_ielen; + else + addnIEPresent = false; /* Dont include the IE. */ + } + + /* Make sure we are not exceeding allocated len */ + if (nBytes > SIR_MAX_PROBE_RESP_SIZE) { + pe_err("nBytes %d greater than max size", nBytes); + qdf_mem_free(addIE); + return QDF_STATUS_E_FAILURE; + } + + /* Paranoia: */ + qdf_mem_zero(pFrame2Hal, nBytes); + + /* Next, we fill out the buffer descriptor: */ + lim_populate_mac_header(mac, pFrame2Hal, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_PROBE_RSP, + pe_session->self_mac_addr, + pe_session->self_mac_addr); + + pMacHdr = (tpSirMacMgmtHdr) pFrame2Hal; + + sir_copy_mac_addr(pMacHdr->bssId, pe_session->bssId); + + /* That done, pack the Probe Response: */ + nStatus = + dot11f_pack_probe_response(mac, &pe_session->probeRespFrame, + pFrame2Hal + sizeof(tSirMacMgmtHdr), + nPayload, &nPayload); + + if (DOT11F_FAILED(nStatus)) { + pe_err("Failed to pack a Probe Response (0x%08x)", + nStatus); + + qdf_mem_free(addIE); + return retCode; /* allocated! */ + } else if (DOT11F_WARNED(nStatus)) { + pe_warn("There were warnings while packing a P" + "robe Response (0x%08x)", nStatus); + } + + if (addnIEPresent) { + qdf_mem_copy(&pFrame2Hal[nBytes - addn_ielen], + &addIE[0], addn_ielen); + } + + qdf_mem_free(addIE); + + pprobeRespParams = qdf_mem_malloc(sizeof(tSendProbeRespParams)); + if (!pprobeRespParams) { + pe_err("malloc failed for bytes %d", nBytes); + } else { + sir_copy_mac_addr(pprobeRespParams->bssId, pe_session->bssId); + qdf_mem_copy(pprobeRespParams->probeRespTemplate, + pFrame2Hal, nBytes); + pprobeRespParams->probeRespTemplateLen = nBytes; + qdf_mem_copy(pprobeRespParams->ucProxyProbeReqValidIEBmap, + IeBitmap, (sizeof(uint32_t) * 8)); + msgQ.type = WMA_SEND_PROBE_RSP_TMPL; + msgQ.reserved = 0; + msgQ.bodyptr = pprobeRespParams; + msgQ.bodyval = 0; + + retCode = wma_post_ctrl_msg(mac, &msgQ); + if (QDF_STATUS_SUCCESS != retCode) { + pe_err("FAIL bytes %d retcode[%X]", nBytes, retCode); + qdf_mem_free(pprobeRespParams); + } else { + pe_debug("Probe response template msg posted to HAL of bytes %d", + nBytes); + } + } + + return retCode; +} + +/** + * sch_gen_timing_advert_frame() - Generate the TA frame and populate the buffer + * @mac: the global MAC context + * @self_addr: the self MAC address + * @buf: the buffer that will contain the frame + * @timestamp_offset: return for the offset of the timestamp field + * @time_value_offset: return for the time_value field in the TA IE + * + * Return: the length of the buffer on success and error code on failure. + */ +int sch_gen_timing_advert_frame(struct mac_context *mac_ctx, tSirMacAddr self_addr, + uint8_t **buf, uint32_t *timestamp_offset, uint32_t *time_value_offset) +{ + tDot11fTimingAdvertisementFrame frame = {0}; + uint32_t payload_size, buf_size; + int status; + struct qdf_mac_addr wildcard_bssid = { + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + }; + + qdf_mem_zero((uint8_t *)&frame, sizeof(tDot11fTimingAdvertisementFrame)); + + /* Populate the TA fields */ + status = populate_dot11f_timing_advert_frame(mac_ctx, &frame); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("Error populating TA frame %x", status); + return qdf_status_to_os_return(status); + } + + status = dot11f_get_packed_timing_advertisement_frame_size(mac_ctx, + &frame, &payload_size); + if (DOT11F_FAILED(status)) { + pe_err("Error getting packed frame size %x", status); + return -EINVAL; + } + + if (DOT11F_WARNED(status)) + pe_warn("Warning getting packed frame size"); + + buf_size = sizeof(tSirMacMgmtHdr) + payload_size; + *buf = qdf_mem_malloc(buf_size); + if (!*buf) + return -ENOMEM; + + payload_size = 0; + status = dot11f_pack_timing_advertisement_frame(mac_ctx, &frame, + *buf + sizeof(tSirMacMgmtHdr), buf_size - + sizeof(tSirMacMgmtHdr), &payload_size); + pe_debug("TA payload size2 = %d", payload_size); + if (DOT11F_FAILED(status)) { + pe_err("Error packing frame %x", status); + goto fail; + } + + if (DOT11F_WARNED(status)) + pe_warn("Warning packing frame"); + + lim_populate_mac_header(mac_ctx, *buf, SIR_MAC_MGMT_FRAME, + SIR_MAC_MGMT_TIME_ADVERT, wildcard_bssid.bytes, self_addr); + + /* The timestamp field is right after the header */ + *timestamp_offset = sizeof(tSirMacMgmtHdr); + + *time_value_offset = sizeof(tSirMacMgmtHdr) + + sizeof(tDot11fFfTimeStamp) + sizeof(tDot11fFfCapabilities); + + /* Add the Country IE length */ + dot11f_get_packed_ie_country(mac_ctx, &frame.Country, + time_value_offset); + /* Add 2 for Country IE EID and Length fields */ + *time_value_offset += 2; + + /* Add the PowerConstraint IE size */ + if (frame.Country.present == 1) + *time_value_offset += 3; + + /* Add the offset inside TA IE */ + *time_value_offset += 3; + + return payload_size + sizeof(tSirMacMgmtHdr); + +fail: + qdf_mem_free(*buf); + *buf = NULL; + return -EINVAL; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_gen.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_gen.c new file mode 100644 index 0000000000000000000000000000000000000000..f1893b712c27b7294a9f3efc0be5a0c6e11f03aa --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_gen.c @@ -0,0 +1,1115 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file sch_beacon_gen.cc contains beacon generation related + * functions + * + * Author: Sandesh Goel + * Date: 02/25/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "cds_api.h" +#include "wni_cfg.h" +#include "ani_global.h" +#include "sir_mac_prot_def.h" + +#include "lim_utils.h" +#include "lim_api.h" + +#include "wma_if.h" +#include "sch_api.h" + +#include "parser_api.h" +#include "wlan_utility.h" + +/* Offset of Channel Switch count field in CSA/ECSA IE */ +#define SCH_CSA_SWITCH_COUNT_OFFSET 2 +#define SCH_ECSA_SWITCH_COUNT_OFFSET 3 + +const uint8_t p2p_oui[] = { 0x50, 0x6F, 0x9A, 0x9 }; + +static QDF_STATUS sch_get_p2p_ie_offset(uint8_t *pextra_ie, + uint32_t extra_ie_len, + uint16_t *pie_offset) +{ + uint8_t elem_id; + uint8_t elem_len; + uint8_t *ie_ptr = pextra_ie; + uint8_t oui_size = sizeof(p2p_oui); + uint32_t p2p_ie_offset = 0; + uint32_t left_len = extra_ie_len; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + *pie_offset = 0; + while (left_len > 2) { + elem_id = ie_ptr[0]; + elem_len = ie_ptr[1]; + left_len -= 2; + + if (elem_len > left_len) + return status; + + if ((elem_id == 0xDD) && (elem_len >= oui_size)) { + if (!qdf_mem_cmp(&ie_ptr[2], &p2p_oui, oui_size)) { + *pie_offset = p2p_ie_offset; + return QDF_STATUS_SUCCESS; + } + } + + left_len -= elem_len; + ie_ptr += (elem_len + 2); + p2p_ie_offset += (elem_len + 2); + }; + + return status; +} + +/** + * sch_append_addn_ie() - adds additional IEs to frame + * @mac_ctx: mac global context + * @session: pe session pointer + * @frm: frame where additional IE is to be added + * @bcn_size_left: beacon size left + * @num_bytes: final size + * @addn_ie: pointer to additional IE + * @addn_ielen: length of additional IE + * + * Return: status of operation + */ +static QDF_STATUS +sch_append_addn_ie(struct mac_context *mac_ctx, struct pe_session *session, + uint8_t *frm, uint32_t bcn_size_left, + uint32_t *num_bytes, uint8_t *addn_ie, uint16_t addn_ielen) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint8_t add_ie[WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN]; + uint8_t *p2p_ie = NULL; + uint8_t noa_len = 0; + uint8_t noa_strm[SIR_MAX_NOA_ATTR_LEN + SIR_P2P_IE_HEADER_LEN]; + uint8_t ext_p2p_ie[DOT11F_IE_P2PBEACON_MAX_LEN + 2]; + bool valid_ie; + + valid_ie = (addn_ielen <= WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN && + addn_ielen && (addn_ielen <= bcn_size_left)); + + if (!valid_ie) + return status; + + qdf_mem_zero(&ext_p2p_ie[0], DOT11F_IE_P2PBEACON_MAX_LEN + 2); + /* + * P2P IE extracted in wlan_hdd_add_hostapd_conf_vsie may not + * be at the end of additional IE buffer. The buffer sent to WMA + * expect P2P IE at the end of beacon buffer and will result in + * beacon corruption if P2P IE is not at end of beacon buffer. + */ + status = lim_strip_ie(mac_ctx, addn_ie, &addn_ielen, WLAN_ELEMID_VENDOR, + ONE_BYTE, SIR_MAC_P2P_OUI, SIR_MAC_P2P_OUI_SIZE, + ext_p2p_ie, DOT11F_IE_P2PBEACON_MAX_LEN); + + qdf_mem_copy(&add_ie[0], addn_ie, addn_ielen); + + if (status == QDF_STATUS_SUCCESS && + ext_p2p_ie[0] == WLAN_ELEMID_VENDOR && + !qdf_mem_cmp(&ext_p2p_ie[2], SIR_MAC_P2P_OUI, + SIR_MAC_P2P_OUI_SIZE)) { + qdf_mem_copy(&add_ie[addn_ielen], ext_p2p_ie, + ext_p2p_ie[1] + 2); + addn_ielen += ext_p2p_ie[1] + 2; + } + + p2p_ie = (uint8_t *)limGetP2pIEPtr(mac_ctx, &add_ie[0], addn_ielen); + if ((p2p_ie) && !mac_ctx->beacon_offload) { + /* get NoA attribute stream P2P IE */ + noa_len = lim_get_noa_attr_stream(mac_ctx, noa_strm, session); + if (noa_len) { + if ((noa_len + addn_ielen) <= + WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN) { + qdf_mem_copy(&add_ie[addn_ielen], noa_strm, + noa_len); + addn_ielen += noa_len; + p2p_ie[1] += noa_len; + } else { + pe_err("Not able to insert NoA because of length constraint"); + } + } + } + if (addn_ielen <= WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN) { + qdf_mem_copy(frm, &add_ie[0], addn_ielen); + *num_bytes = *num_bytes + addn_ielen; + } else { + pe_warn("Not able to insert because of len constraint %d", + addn_ielen); + } + return status; +} + +/** + * sch_get_csa_ecsa_count_offset() - get the offset of Switch count field + * @ie: pointer to the beginning of IEs in the beacon frame buffer + * @ie_len: length of the IEs in the buffer + * @csa_count_offset: pointer to the csa_count_offset variable in the caller + * @ecsa_count_offset: pointer to the ecsa_count_offset variable in the caller + * + * Gets the offset of the switch count field in the CSA/ECSA IEs from the start + * of the IEs buffer. + * + * Return: None + */ +static void sch_get_csa_ecsa_count_offset(uint8_t *ie, uint32_t ie_len, + uint32_t *csa_count_offset, + uint32_t *ecsa_count_offset) +{ + uint8_t *ptr = ie; + uint8_t elem_id; + uint16_t elem_len; + uint32_t offset = 0; + + /* IE is not present */ + if (!ie_len) + return; + + while (ie_len >= 2) { + elem_id = ptr[0]; + elem_len = ptr[1]; + ie_len -= 2; + offset += 2; + + if (elem_id == DOT11F_EID_CHANSWITCHANN && + elem_len == 3) + *csa_count_offset = offset + + SCH_CSA_SWITCH_COUNT_OFFSET; + + if (elem_id == DOT11F_EID_EXT_CHAN_SWITCH_ANN && + elem_len == 4) + *ecsa_count_offset = offset + + SCH_ECSA_SWITCH_COUNT_OFFSET; + + if (ie_len < elem_len) + return; + + ie_len -= elem_len; + offset += elem_len; + ptr += (elem_len + 2); + } + +} + +/** + * sch_set_fixed_beacon_fields() - sets the fixed params in beacon frame + * @mac_ctx: mac global context + * @session: pe session entry + * @band: out param, band caclculated + * @opr_ch: operating channels + * + * Return: status of operation + */ + +QDF_STATUS +sch_set_fixed_beacon_fields(struct mac_context *mac_ctx, struct pe_session *session) +{ + tpAniBeaconStruct bcn_struct = (tpAniBeaconStruct) + session->pSchBeaconFrameBegin; + tpSirMacMgmtHdr mac; + uint16_t offset, bcn_size_left; + uint8_t *ptr; + tDot11fBeacon1 *bcn_1; + tDot11fBeacon2 *bcn_2; + uint32_t i, n_status, n_bytes; + bool wps_ap_enable = 0; + tDot11fIEWscProbeRes *wsc_prb_res; + uint8_t *extra_ie = NULL; + uint32_t extra_ie_len = 0; + uint16_t extra_ie_offset = 0; + uint16_t p2p_ie_offset = 0; + uint32_t csa_count_offset = 0; + uint32_t ecsa_count_offset = 0; + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool is_vht_enabled = false; + uint16_t addn_ielen = 0; + uint8_t *addn_ie = NULL; + tDot11fIEExtCap extracted_extcap; + bool extcap_present = true, addnie_present = false; + bool is_6ghz_chsw; + + bcn_1 = qdf_mem_malloc(sizeof(tDot11fBeacon1)); + if (!bcn_1) + return QDF_STATUS_E_NOMEM; + + bcn_2 = qdf_mem_malloc(sizeof(tDot11fBeacon2)); + if (!bcn_2) { + qdf_mem_free(bcn_1); + return QDF_STATUS_E_NOMEM; + } + + wsc_prb_res = qdf_mem_malloc(sizeof(tDot11fIEWscProbeRes)); + if (!wsc_prb_res) { + qdf_mem_free(bcn_1); + qdf_mem_free(bcn_2); + return QDF_STATUS_E_NOMEM; + } + + pe_debug("Setting fixed beacon fields"); + + /* + * First set the fixed fields: + * set the TFP headers, set the mac header + */ + qdf_mem_zero((uint8_t *) &bcn_struct->macHdr, sizeof(tSirMacMgmtHdr)); + mac = (tpSirMacMgmtHdr) &bcn_struct->macHdr; + mac->fc.type = SIR_MAC_MGMT_FRAME; + mac->fc.subType = SIR_MAC_MGMT_BEACON; + + for (i = 0; i < 6; i++) + mac->da[i] = 0xff; + + qdf_mem_copy(mac->sa, session->self_mac_addr, + sizeof(session->self_mac_addr)); + qdf_mem_copy(mac->bssId, session->bssId, sizeof(session->bssId)); + + mac->fc.fromDS = 0; + mac->fc.toDS = 0; + + /* Skip over the timestamp (it'll be updated later). */ + bcn_1->BeaconInterval.interval = + session->beaconParams.beaconInterval; + populate_dot11f_capabilities(mac_ctx, &bcn_1->Capabilities, session); + if (session->ssidHidden) { + bcn_1->SSID.present = 1; + /* rest of the fileds are 0 for hidden ssid */ + if ((session->ssId.length) && + (session->ssidHidden == eHIDDEN_SSID_ZERO_CONTENTS)) + bcn_1->SSID.num_ssid = session->ssId.length; + } else { + populate_dot11f_ssid(mac_ctx, &session->ssId, &bcn_1->SSID); + } + + populate_dot11f_supp_rates(mac_ctx, POPULATE_DOT11F_RATES_OPERATIONAL, + &bcn_1->SuppRates, session); + populate_dot11f_ds_params(mac_ctx, &bcn_1->DSParams, + wlan_reg_freq_to_chan( + mac_ctx->pdev, session->curr_op_freq)); + populate_dot11f_ibss_params(mac_ctx, &bcn_1->IBSSParams, session); + + offset = sizeof(tAniBeaconStruct); + ptr = session->pSchBeaconFrameBegin + offset; + + if (LIM_IS_AP_ROLE(session)) { + /* Initialize the default IE bitmap to zero */ + qdf_mem_zero((uint8_t *) &(session->DefProbeRspIeBitmap), + (sizeof(uint32_t) * 8)); + + /* Initialize the default IE bitmap to zero */ + qdf_mem_zero((uint8_t *) &(session->probeRespFrame), + sizeof(session->probeRespFrame)); + + /* + * Can be efficiently updated whenever new IE added in Probe + * response in future + */ + if (lim_update_probe_rsp_template_ie_bitmap_beacon1(mac_ctx, + bcn_1, session) != QDF_STATUS_SUCCESS) + pe_err("Failed to build ProbeRsp template"); + } + + n_status = dot11f_pack_beacon1(mac_ctx, bcn_1, ptr, + SIR_MAX_BEACON_SIZE - offset, &n_bytes); + if (DOT11F_FAILED(n_status)) { + pe_err("Failed to packed a tDot11fBeacon1 (0x%08x)", + n_status); + qdf_mem_free(bcn_1); + qdf_mem_free(bcn_2); + qdf_mem_free(wsc_prb_res); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(n_status)) { + pe_warn("Warnings while packing a tDot11fBeacon1(0x%08x)", + n_status); + } + session->schBeaconOffsetBegin = offset + (uint16_t) n_bytes; + pe_debug("Initialized beacon begin, offset %d fixed size %d", offset, + session->schBeaconOffsetBegin); + + /* Initialize the 'new' fields at the end of the beacon */ + is_6ghz_chsw = + WLAN_REG_IS_6GHZ_CHAN_FREQ(session->curr_op_freq) || + WLAN_REG_IS_6GHZ_CHAN_FREQ + (session->gLimChannelSwitch.sw_target_freq); + if (session->limSystemRole == eLIM_AP_ROLE && + session->dfsIncludeChanSwIe == true) { + if (!CHAN_HOP_ALL_BANDS_ENABLE || + session->lim_non_ecsa_cap_num == 0 || is_6ghz_chsw) { + tDot11fIEext_chan_switch_ann *ext_csa = + &bcn_2->ext_chan_switch_ann; + populate_dot_11_f_ext_chann_switch_ann(mac_ctx, + ext_csa, + session); + pe_debug("ecsa: mode:%d reg:%d chan:%d count:%d", + ext_csa->switch_mode, + ext_csa->new_reg_class, + ext_csa->new_channel, + ext_csa->switch_count); + } else { + populate_dot11f_chan_switch_ann(mac_ctx, + &bcn_2->ChanSwitchAnn, + session); + pe_debug("csa: mode:%d chan:%d count:%d", + bcn_2->ChanSwitchAnn.switchMode, + bcn_2->ChanSwitchAnn.newChannel, + bcn_2->ChanSwitchAnn.switchCount); + } + } + + populate_dot11_supp_operating_classes(mac_ctx, + &bcn_2->SuppOperatingClasses, session); + populate_dot11f_country(mac_ctx, &bcn_2->Country, session); + if (bcn_1->Capabilities.qos) + populate_dot11f_edca_param_set(mac_ctx, &bcn_2->EDCAParamSet, + session); + + if (session->lim11hEnable) { + populate_dot11f_power_constraints(mac_ctx, + &bcn_2->PowerConstraints); + populate_dot11f_tpc_report(mac_ctx, &bcn_2->TPCReport, session); + /* Need to insert channel switch announcement here */ + if ((LIM_IS_AP_ROLE(session) + || LIM_IS_P2P_DEVICE_GO(session)) + && session->dfsIncludeChanSwIe == true) { + /* + * Channel switch announcement only if radar is detected + * and SAP has instructed to announce channel switch IEs + * in beacon and probe responses + */ + if (!is_6ghz_chsw) { + populate_dot11f_chan_switch_ann + (mac_ctx, &bcn_2->ChanSwitchAnn, + session); + pe_debug("csa: mode:%d chan:%d count:%d", + bcn_2->ChanSwitchAnn.switchMode, + bcn_2->ChanSwitchAnn.newChannel, + bcn_2->ChanSwitchAnn.switchCount); + } + /* + * TODO: depending the CB mode, extended channel switch + * announcement need to be called + */ + /* + populate_dot11f_ext_chan_switch_ann(mac_ctx, + &bcn_2->ExtChanSwitchAnn, session); + */ + /* + * TODO: If in 11AC mode, wider bw channel switch + * announcement needs to be called + */ + /* + populate_dot11f_wider_bw_chan_switch_ann(mac_ctx, + &bcn_2->WiderBWChanSwitchAnn, session); + */ + /* + * Populate the Channel Switch Wrapper Element if + * SAP operates in 40/80 Mhz Channel Width. + */ + if (true == session->dfsIncludeChanWrapperIe) { + populate_dot11f_chan_switch_wrapper(mac_ctx, + &bcn_2->ChannelSwitchWrapper, session); + pe_debug("wrapper: width:%d f0:%d f1:%d", + bcn_2->ChannelSwitchWrapper. + WiderBWChanSwitchAnn.newChanWidth, + bcn_2->ChannelSwitchWrapper. + WiderBWChanSwitchAnn.newCenterChanFreq0, + bcn_2->ChannelSwitchWrapper. + WiderBWChanSwitchAnn.newCenterChanFreq1 + ); + } + } + } + if (mac_ctx->rrm.rrmConfig.sap_rrm_enabled) + populate_dot11f_rrm_ie(mac_ctx, &bcn_2->RRMEnabledCap, + session); + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + /* populate proprietary IE for MDM device operating in AP-MCC */ + populate_dot11f_avoid_channel_ie(mac_ctx, &bcn_2->QComVendorIE, + session); +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + + if (session->dot11mode != MLME_DOT11_MODE_11B) + populate_dot11f_erp_info(mac_ctx, &bcn_2->ERPInfo, session); + + if (session->htCapability) { + populate_dot11f_ht_caps(mac_ctx, session, &bcn_2->HTCaps); + populate_dot11f_ht_info(mac_ctx, &bcn_2->HTInfo, session); + } + if (session->vhtCapability) { + populate_dot11f_vht_caps(mac_ctx, session, &bcn_2->VHTCaps); + populate_dot11f_vht_operation(mac_ctx, session, + &bcn_2->VHTOperation); + is_vht_enabled = true; + /* following is for MU MIMO: we do not support it yet */ + /* + populate_dot11f_vht_ext_bss_load( mac_ctx, &bcn2.VHTExtBssLoad); + */ + populate_dot11f_vht_tx_power_env(mac_ctx, + &bcn_2->vht_transmit_power_env, + session->ch_width, + session->curr_op_freq); + populate_dot11f_qcn_ie(mac_ctx, &bcn_2->qcn_ie, + QCN_IE_ATTR_ID_ALL); + } + + if (lim_is_session_he_capable(session)) { + pe_debug("Populate HE IEs"); + populate_dot11f_he_caps(mac_ctx, session, + &bcn_2->he_cap); + populate_dot11f_he_operation(mac_ctx, session, + &bcn_2->he_op); + populate_dot11f_he_6ghz_cap(mac_ctx, session, + &bcn_2->he_6ghz_band_cap); + populate_dot11f_he_bss_color_change(mac_ctx, session, + &bcn_2->bss_color_change); + } + + if (session->limSystemRole != eLIM_STA_IN_IBSS_ROLE) + populate_dot11f_ext_cap(mac_ctx, is_vht_enabled, &bcn_2->ExtCap, + session); + populate_dot11f_ext_supp_rates(mac_ctx, + POPULATE_DOT11F_RATES_OPERATIONAL, + &bcn_2->ExtSuppRates, session); + + if (session->pLimStartBssReq) { + populate_dot11f_wpa(mac_ctx, &session->pLimStartBssReq->rsnIE, + &bcn_2->WPA); + populate_dot11f_rsn_opaque(mac_ctx, + &session->pLimStartBssReq->rsnIE, + &bcn_2->RSNOpaque); + } + + if (session->limWmeEnabled) + populate_dot11f_wmm(mac_ctx, &bcn_2->WMMInfoAp, + &bcn_2->WMMParams, &bcn_2->WMMCaps, session); + if (LIM_IS_AP_ROLE(session)) { + if (session->wps_state != SAP_WPS_DISABLED) { + populate_dot11f_beacon_wpsi_es(mac_ctx, + &bcn_2->WscBeacon, session); + } + } else { + wps_ap_enable = mac_ctx->mlme_cfg->wps_params.enable_wps & + WNI_CFG_WPS_ENABLE_AP; + if (wps_ap_enable) + populate_dot11f_wsc(mac_ctx, &bcn_2->WscBeacon); + + if (mac_ctx->lim.wscIeInfo.wscEnrollmentState == + eLIM_WSC_ENROLL_BEGIN) { + populate_dot11f_wsc_registrar_info(mac_ctx, + &bcn_2->WscBeacon); + mac_ctx->lim.wscIeInfo.wscEnrollmentState = + eLIM_WSC_ENROLL_IN_PROGRESS; + } + + if (mac_ctx->lim.wscIeInfo.wscEnrollmentState == + eLIM_WSC_ENROLL_END) { + de_populate_dot11f_wsc_registrar_info(mac_ctx, + &bcn_2->WscBeacon); + mac_ctx->lim.wscIeInfo.wscEnrollmentState = + eLIM_WSC_ENROLL_NOOP; + } + } + + if (LIM_IS_AP_ROLE(session)) { + /* + * Can be efficiently updated whenever new IE added in Probe + * response in future + */ + lim_update_probe_rsp_template_ie_bitmap_beacon2(mac_ctx, bcn_2, + &session->DefProbeRspIeBitmap[0], + &session->probeRespFrame); + + /* update probe response WPS IE instead of beacon WPS IE */ + if (session->wps_state != SAP_WPS_DISABLED) { + if (session->APWPSIEs.SirWPSProbeRspIE.FieldPresent) + populate_dot11f_probe_res_wpsi_es(mac_ctx, + wsc_prb_res, session); + else + wsc_prb_res->present = 0; + if (wsc_prb_res->present) { + set_probe_rsp_ie_bitmap( + &session->DefProbeRspIeBitmap[0], + SIR_MAC_WPA_EID); + qdf_mem_copy((void *) + &session->probeRespFrame.WscProbeRes, + (void *)wsc_prb_res, + sizeof(tDot11fIEWscProbeRes)); + } + } + + } + + addnie_present = (session->add_ie_params.probeRespBCNDataLen != 0); + if (addnie_present) { + addn_ielen = session->add_ie_params.probeRespBCNDataLen; + addn_ie = qdf_mem_malloc(addn_ielen); + if (!addn_ie) { + qdf_mem_free(bcn_1); + qdf_mem_free(bcn_2); + qdf_mem_free(wsc_prb_res); + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(addn_ie, + session->add_ie_params.probeRespBCNData_buff, + addn_ielen); + + qdf_mem_zero((uint8_t *)&extracted_extcap, + sizeof(tDot11fIEExtCap)); + status = lim_strip_extcap_update_struct(mac_ctx, addn_ie, + &addn_ielen, &extracted_extcap); + if (QDF_STATUS_SUCCESS != status) { + extcap_present = false; + pe_debug("extcap not extracted"); + } + /* merge extcap IE */ + if (extcap_present && + session->limSystemRole != eLIM_STA_IN_IBSS_ROLE) + lim_merge_extcap_struct(&bcn_2->ExtCap, + &extracted_extcap, + true); + + } + + if (session->vhtCapability && session->gLimOperatingMode.present) { + populate_dot11f_operating_mode(mac_ctx, &bcn_2->OperatingMode, + session); + lim_strip_ie(mac_ctx, addn_ie, &addn_ielen, + WLAN_ELEMID_OP_MODE_NOTIFY, ONE_BYTE, NULL, 0, + NULL, SIR_MAC_VHT_OPMODE_SIZE - 2); + } + + n_status = dot11f_pack_beacon2(mac_ctx, bcn_2, + session->pSchBeaconFrameEnd, + SIR_MAX_BEACON_SIZE, &n_bytes); + if (DOT11F_FAILED(n_status)) { + pe_err("Failed to packed a tDot11fBeacon2 (0x%08x)", + n_status); + qdf_mem_free(bcn_1); + qdf_mem_free(bcn_2); + qdf_mem_free(wsc_prb_res); + qdf_mem_free(addn_ie); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(n_status)) { + pe_err("Warnings while packing a tDot11fBeacon2(0x%08x)", + n_status); + } + + /* Fill the CSA/ECSA count offsets if the IEs are present */ + mac_ctx->sch.ecsa_count_offset = 0; + mac_ctx->sch.csa_count_offset = 0; + if (session->dfsIncludeChanSwIe) + sch_get_csa_ecsa_count_offset(session->pSchBeaconFrameEnd, + n_bytes, + &csa_count_offset, + &ecsa_count_offset); + + if (csa_count_offset) + mac_ctx->sch.csa_count_offset = + session->schBeaconOffsetBegin + TIM_IE_SIZE + + csa_count_offset; + if (ecsa_count_offset) + mac_ctx->sch.ecsa_count_offset = + session->schBeaconOffsetBegin + TIM_IE_SIZE + + ecsa_count_offset; + + pe_debug("csa_count_offset %d ecsa_count_offset %d", + mac_ctx->sch.csa_count_offset, + mac_ctx->sch.ecsa_count_offset); + + extra_ie = session->pSchBeaconFrameEnd + n_bytes; + extra_ie_offset = n_bytes; + + /* + * Max size left to append additional IE.= (MAX beacon size - TIM IE - + * beacon fix size (bcn_1 + header) - beacon variable size (bcn_1). + */ + bcn_size_left = SIR_MAX_BEACON_SIZE - TIM_IE_SIZE - + session->schBeaconOffsetBegin - + (uint16_t)n_bytes; + pe_debug("max_bcn_size_left %d and addn_ielen %d", bcn_size_left, + addn_ielen); + /* TODO: Append additional IE here. */ + if (addn_ielen > 0) + sch_append_addn_ie(mac_ctx, session, + session->pSchBeaconFrameEnd + n_bytes, + bcn_size_left, &n_bytes, + addn_ie, addn_ielen); + + session->schBeaconOffsetEnd = (uint16_t) n_bytes; + extra_ie_len = n_bytes - extra_ie_offset; + /* Get the p2p Ie Offset */ + status = sch_get_p2p_ie_offset(extra_ie, extra_ie_len, &p2p_ie_offset); + if (QDF_STATUS_SUCCESS == status) + /* Update the P2P Ie Offset */ + mac_ctx->sch.p2p_ie_offset = + session->schBeaconOffsetBegin + TIM_IE_SIZE + + extra_ie_offset + p2p_ie_offset; + else + mac_ctx->sch.p2p_ie_offset = 0; + + pe_debug("Initialized beacon end, offset %d", + session->schBeaconOffsetEnd); + mac_ctx->sch.beacon_changed = 1; + qdf_mem_free(bcn_1); + qdf_mem_free(bcn_2); + qdf_mem_free(wsc_prb_res); + qdf_mem_free(addn_ie); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +lim_update_probe_rsp_template_ie_bitmap_beacon1(struct mac_context *mac, + tDot11fBeacon1 *beacon1, + struct pe_session *pe_session) +{ + uint32_t *DefProbeRspIeBitmap; + tDot11fProbeResponse *prb_rsp; + + if (!pe_session) { + pe_debug("PESession is null!"); + return QDF_STATUS_E_FAILURE; + } + DefProbeRspIeBitmap = &pe_session->DefProbeRspIeBitmap[0]; + prb_rsp = &pe_session->probeRespFrame; + prb_rsp->BeaconInterval = beacon1->BeaconInterval; + qdf_mem_copy((void *)&prb_rsp->Capabilities, + (void *)&beacon1->Capabilities, + sizeof(beacon1->Capabilities)); + + /* SSID */ + if (beacon1->SSID.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_SSID); + /* populating it, because probe response has to go with SSID even in hidden case */ + populate_dot11f_ssid(mac, &pe_session->ssId, &prb_rsp->SSID); + } + /* supported rates */ + if (beacon1->SuppRates.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_RATES); + qdf_mem_copy((void *)&prb_rsp->SuppRates, + (void *)&beacon1->SuppRates, + sizeof(beacon1->SuppRates)); + + } + /* DS Parameter set */ + if (beacon1->DSParams.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_DSPARMS); + qdf_mem_copy((void *)&prb_rsp->DSParams, + (void *)&beacon1->DSParams, + sizeof(beacon1->DSParams)); + + } + + /* IBSS params will not be present in the Beacons transmitted by AP */ + return QDF_STATUS_SUCCESS; +} + +void lim_update_probe_rsp_template_ie_bitmap_beacon2(struct mac_context *mac, + tDot11fBeacon2 *beacon2, + uint32_t *DefProbeRspIeBitmap, + tDot11fProbeResponse *prb_rsp) +{ + /* IBSS parameter set - will not be present in probe response tx by AP */ + /* country */ + if (beacon2->Country.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_COUNTRY); + qdf_mem_copy((void *)&prb_rsp->Country, + (void *)&beacon2->Country, + sizeof(beacon2->Country)); + + } + /* Power constraint */ + if (beacon2->PowerConstraints.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_PWRCNSTR); + qdf_mem_copy((void *)&prb_rsp->PowerConstraints, + (void *)&beacon2->PowerConstraints, + sizeof(beacon2->PowerConstraints)); + + } + /* Channel Switch Annoouncement WLAN_ELEMID_CHANSWITCHANN */ + if (beacon2->ChanSwitchAnn.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_CHANSWITCHANN); + qdf_mem_copy((void *)&prb_rsp->ChanSwitchAnn, + (void *)&beacon2->ChanSwitchAnn, + sizeof(beacon2->ChanSwitchAnn)); + + } + + /* EXT Channel Switch Announcement CHNL_EXTENDED_SWITCH_ANN_EID*/ + if (beacon2->ext_chan_switch_ann.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_EXTCHANSWITCHANN); + qdf_mem_copy((void *)&prb_rsp->ext_chan_switch_ann, + (void *)&beacon2->ext_chan_switch_ann, + sizeof(beacon2->ext_chan_switch_ann)); + } + + /* Supported operating class */ + if (beacon2->SuppOperatingClasses.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_SUPP_OP_CLASS); + qdf_mem_copy((void *)&prb_rsp->SuppOperatingClasses, + (void *)&beacon2->SuppOperatingClasses, + sizeof(beacon2->SuppOperatingClasses)); + } + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + if (beacon2->QComVendorIE.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + SIR_MAC_QCOM_VENDOR_EID); + qdf_mem_copy((void *)&prb_rsp->QComVendorIE, + (void *)&beacon2->QComVendorIE, + sizeof(beacon2->QComVendorIE)); + } +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + + /* ERP information */ + if (beacon2->ERPInfo.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_ERP); + qdf_mem_copy((void *)&prb_rsp->ERPInfo, + (void *)&beacon2->ERPInfo, + sizeof(beacon2->ERPInfo)); + + } + /* Extended supported rates */ + if (beacon2->ExtSuppRates.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_XRATES); + qdf_mem_copy((void *)&prb_rsp->ExtSuppRates, + (void *)&beacon2->ExtSuppRates, + sizeof(beacon2->ExtSuppRates)); + + } + + /* WPA */ + if (beacon2->WPA.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, SIR_MAC_WPA_EID); + qdf_mem_copy((void *)&prb_rsp->WPA, (void *)&beacon2->WPA, + sizeof(beacon2->WPA)); + + } + + /* RSN */ + if (beacon2->RSNOpaque.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_RSN); + qdf_mem_copy((void *)&prb_rsp->RSNOpaque, + (void *)&beacon2->RSNOpaque, + sizeof(beacon2->RSNOpaque)); + } + + /* EDCA Parameter set */ + if (beacon2->EDCAParamSet.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_EDCAPARMS); + qdf_mem_copy((void *)&prb_rsp->EDCAParamSet, + (void *)&beacon2->EDCAParamSet, + sizeof(beacon2->EDCAParamSet)); + + } + /* Vendor specific - currently no vendor specific IEs added */ + /* Requested IEs - currently we are not processing this will be added later */ + /* HT capability IE */ + if (beacon2->HTCaps.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_HTCAP_ANA); + qdf_mem_copy((void *)&prb_rsp->HTCaps, (void *)&beacon2->HTCaps, + sizeof(beacon2->HTCaps)); + } + /* HT Info IE */ + if (beacon2->HTInfo.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_HTINFO_ANA); + qdf_mem_copy((void *)&prb_rsp->HTInfo, (void *)&beacon2->HTInfo, + sizeof(beacon2->HTInfo)); + } + if (beacon2->VHTCaps.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_VHTCAP); + qdf_mem_copy((void *)&prb_rsp->VHTCaps, + (void *)&beacon2->VHTCaps, + sizeof(beacon2->VHTCaps)); + } + if (beacon2->VHTOperation.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_VHTOP); + qdf_mem_copy((void *)&prb_rsp->VHTOperation, + (void *)&beacon2->VHTOperation, + sizeof(beacon2->VHTOperation)); + } + if (beacon2->VHTExtBssLoad.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_EXT_BSS_LOAD); + qdf_mem_copy((void *)&prb_rsp->VHTExtBssLoad, + (void *)&beacon2->VHTExtBssLoad, + sizeof(beacon2->VHTExtBssLoad)); + } + /* WMM IE */ + if (beacon2->WMMParams.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, SIR_MAC_WPA_EID); + qdf_mem_copy((void *)&prb_rsp->WMMParams, + (void *)&beacon2->WMMParams, + sizeof(beacon2->WMMParams)); + } + /* WMM capability - most of the case won't be present */ + if (beacon2->WMMCaps.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, SIR_MAC_WPA_EID); + qdf_mem_copy((void *)&prb_rsp->WMMCaps, + (void *)&beacon2->WMMCaps, + sizeof(beacon2->WMMCaps)); + } + + /* Extended Capability */ + if (beacon2->ExtCap.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, DOT11F_EID_EXTCAP); + qdf_mem_copy((void *)&prb_rsp->ExtCap, + (void *)&beacon2->ExtCap, + sizeof(beacon2->ExtCap)); + } + + if (beacon2->he_cap.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + DOT11F_EID_HE_CAP); + qdf_mem_copy((void *)&prb_rsp->he_cap, + (void *)&beacon2->he_cap, + sizeof(beacon2->he_cap)); + } + if (beacon2->he_op.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + DOT11F_EID_HE_OP); + qdf_mem_copy((void *)&prb_rsp->he_op, + (void *)&beacon2->he_op, + sizeof(beacon2->he_op)); + } + + if (beacon2->he_6ghz_band_cap.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + DOT11F_EID_HE_6GHZ_BAND_CAP); + qdf_mem_copy((void *)&prb_rsp->he_6ghz_band_cap, + (void *)&beacon2->he_6ghz_band_cap, + sizeof(beacon2->he_6ghz_band_cap)); + } + + if (beacon2->TPCReport.present) { + set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, + WLAN_ELEMID_TPCREP); + qdf_mem_copy((void *)&prb_rsp->TPCReport, + (void *)&beacon2->TPCReport, + sizeof(beacon2->TPCReport)); + } + +} + +void set_probe_rsp_ie_bitmap(uint32_t *IeBitmap, uint32_t pos) +{ + uint32_t index, temp; + + index = pos >> 5; + if (index >= 8) { + return; + } + temp = IeBitmap[index]; + + temp |= 1 << (pos & 0x1F); + + IeBitmap[index] = temp; +} + +/** + * write_beacon_to_memory() - send the beacon to the wma + * @mac: pointer to mac structure + * @size: Size of the beacon to write to memory + * @length: Length field of the beacon to write to memory + * @pe_session: pe session + * @reason: beacon update reason + * + * return: success: QDF_STATUS_SUCCESS failure: QDF_STATUS_E_FAILURE + */ +static QDF_STATUS write_beacon_to_memory(struct mac_context *mac, uint16_t size, + uint16_t length, + struct pe_session *pe_session, + enum sir_bcn_update_reason reason) +{ + uint16_t i; + tpAniBeaconStruct pBeacon; + QDF_STATUS status; + + /* copy end of beacon only if length > 0 */ + if (length > 0) { + if (size + pe_session->schBeaconOffsetEnd > + SIR_MAX_BEACON_SIZE) { + pe_err("beacon tmp fail size %d BeaconOffsetEnd %d", + size, pe_session->schBeaconOffsetEnd); + return QDF_STATUS_E_FAILURE; + } + for (i = 0; i < pe_session->schBeaconOffsetEnd; i++) + pe_session->pSchBeaconFrameBegin[size++] = + pe_session->pSchBeaconFrameEnd[i]; + } + /* Update the beacon length */ + pBeacon = (tpAniBeaconStruct) pe_session->pSchBeaconFrameBegin; + /* Do not include the beaconLength indicator itself */ + if (length == 0) { + pBeacon->beaconLength = 0; + /* Dont copy entire beacon, Copy length field alone */ + size = 4; + } else + pBeacon->beaconLength = (uint32_t) size - sizeof(uint32_t); + + if (!mac->sch.beacon_changed) + return QDF_STATUS_E_FAILURE; + + status = sch_send_beacon_req(mac, pe_session->pSchBeaconFrameBegin, + size, pe_session, reason); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("sch_send_beacon_req() returned an error %d, size %d", + status, size); + mac->sch.beacon_changed = 0; + + return status; +} + +/** + * sch_generate_tim + * + * FUNCTION: + * Generate TIM + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param mac pointer to global mac structure + * @param **pPtr pointer to the buffer, where the TIM bit is to be written. + * @param *timLength pointer to limLength, which needs to be returned. + * @return None + */ +void sch_generate_tim(struct mac_context *mac, uint8_t **pPtr, uint16_t *timLength, + uint8_t dtimPeriod) +{ + uint8_t *ptr = *pPtr; + uint32_t val = 0; + uint32_t minAid = 1; /* Always start with AID 1 as minimum */ + uint32_t maxAid = HAL_NUM_STA; + /* Generate partial virtual bitmap */ + uint8_t N1 = minAid / 8; + uint8_t N2 = maxAid / 8; + + if (N1 & 1) + N1--; + + *timLength = N2 - N1 + 4; + val = dtimPeriod; + + /* + * Write 0xFF to firmware's field to detect firmware's mal-function + * early. DTIM count and bitmap control usually cannot be 0xFF, so it + * is easy to know that firmware never updated DTIM count/bitmap control + * field after host driver downloaded beacon template if end-user complaints + * that DTIM count and bitmapControl is 0xFF. + */ + *ptr++ = WLAN_ELEMID_TIM; + *ptr++ = (uint8_t) (*timLength); + /* location for dtimCount. will be filled in by FW. */ + *ptr++ = 0xFF; + *ptr++ = (uint8_t) val; + /* location for bitmap control. will be filled in by FW. */ + *ptr++ = 0xFF; + ptr += (N2 - N1 + 1); + + *pPtr = ptr; +} + +QDF_STATUS sch_process_pre_beacon_ind(struct mac_context *mac, + struct scheduler_msg *limMsg, + enum sir_bcn_update_reason reason) +{ + struct beacon_gen_params *pMsg = limMsg->bodyptr; + uint32_t beaconSize; + struct pe_session *pe_session; + uint8_t sessionId; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + pe_session = pe_find_session_by_bssid(mac, pMsg->bssid, &sessionId); + if (!pe_session) { + pe_err("session lookup fails"); + goto end; + } + + beaconSize = pe_session->schBeaconOffsetBegin; + + /* If SME is not in normal mode, no need to generate beacon */ + if (pe_session->limSmeState != eLIM_SME_NORMAL_STATE) { + pe_debug("PreBeaconInd received in invalid state: %d", + pe_session->limSmeState); + goto end; + } + + switch (GET_LIM_SYSTEM_ROLE(pe_session)) { + + case eLIM_STA_IN_IBSS_ROLE: + /* generate IBSS parameter set */ + if (pe_session->statypeForBss == STA_ENTRY_SELF) + status = + write_beacon_to_memory(mac, (uint16_t) beaconSize, + (uint16_t) beaconSize, + pe_session, reason); + else + pe_err("can not send beacon for PEER session entry"); + break; + + case eLIM_AP_ROLE: { + uint8_t *ptr = + &pe_session->pSchBeaconFrameBegin[pe_session-> + schBeaconOffsetBegin]; + uint16_t timLength = 0; + + if (pe_session->statypeForBss == STA_ENTRY_SELF) { + sch_generate_tim(mac, &ptr, &timLength, + pe_session->dtimPeriod); + beaconSize += 2 + timLength; + status = + write_beacon_to_memory(mac, (uint16_t) beaconSize, + (uint16_t) beaconSize, + pe_session, reason); + } else + pe_err("can not send beacon for PEER session entry"); + } + break; + + default: + pe_err("Error-PE has Receive PreBeconGenIndication when System is in %d role", + GET_LIM_SYSTEM_ROLE(pe_session)); + } + +end: + qdf_mem_free(pMsg); + + return status; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_process.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_process.c new file mode 100644 index 0000000000000000000000000000000000000000..9065f7649a137ba2a712b55fc5dae05d255e81d8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_process.c @@ -0,0 +1,1706 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file sch_beacon_process.cc contains beacon processing related + * functions + * + * Author: Sandesh Goel + * Date: 02/25/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "cds_api.h" +#include "wni_cfg.h" + +#include "cfg_ucfg_api.h" +#include "lim_api.h" +#include "utils_api.h" +#include "sch_api.h" + +#include "lim_utils.h" +#include "lim_send_messages.h" +#include "rrm_api.h" + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +#include "host_diag_core_log.h" +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +#include "wma.h" + +static void +ap_beacon_process_5_ghz(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + tpSchBeaconStruct bcn_struct, + tpUpdateBeaconParams bcn_prm, struct pe_session *session, + uint32_t phy_mode) +{ + tpSirMacMgmtHdr mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + + if (!session->htCapability) + return; + + if (bcn_struct->chan_freq != session->curr_op_freq) + return; + + /* 11a (non HT) AP overlaps or */ + /* HT AP with HT op mode as mixed overlaps. */ + /* HT AP with HT op mode as overlap legacy overlaps. */ + if (!bcn_struct->HTInfo.present + || (eSIR_HT_OP_MODE_MIXED == bcn_struct->HTInfo.opMode) + || (eSIR_HT_OP_MODE_OVERLAP_LEGACY == bcn_struct->HTInfo.opMode)) { + lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId, + &(session->gLimOverlap11aParams)); + + if (session->gLimOverlap11aParams.numSta + && !session->gLimOverlap11aParams.protectionEnabled) { + lim_update_11a_protection(mac_ctx, true, true, + bcn_prm, session); + } + return; + } + /* HT AP with HT20 op mode overlaps. */ + if (eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT != bcn_struct->HTInfo.opMode) + return; + + lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId, + &(session->gLimOverlapHt20Params)); + + if (session->gLimOverlapHt20Params.numSta + && !session->gLimOverlapHt20Params.protectionEnabled) + lim_enable_ht20_protection(mac_ctx, true, true, + bcn_prm, session); +} + +static void +ap_beacon_process_24_ghz(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + tpSchBeaconStruct bcn_struct, + tpUpdateBeaconParams bcn_prm, struct pe_session *session, + uint32_t phy_mode) +{ + tpSirMacMgmtHdr mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + bool tmp_exp = false; + /* We are 11G AP. */ + if ((phy_mode == WNI_CFG_PHY_MODE_11G) && + (false == session->htCapability)) { + if (bcn_struct->chan_freq != session->curr_op_freq) + return; + + tmp_exp = (!bcn_struct->erpPresent && + !bcn_struct->HTInfo.present) || + /* if erp not present then 11B AP overlapping */ + (!mac_ctx->mlme_cfg->sta.ignore_peer_erp_info && + bcn_struct->erpPresent && + (bcn_struct->erpIEInfo.useProtection || + bcn_struct->erpIEInfo.nonErpPresent)); + if (!tmp_exp) + return; +#ifdef FEATURE_WLAN_ESE + if (session->isESEconnection) + QDF_TRACE(QDF_MODULE_ID_PE, + QDF_TRACE_LEVEL_INFO, + FL("[INFOLOG]ESE 11g erpPresent=%d useProtection=%d nonErpPresent=%d"), + bcn_struct->erpPresent, + bcn_struct->erpIEInfo.useProtection, + bcn_struct->erpIEInfo.nonErpPresent); +#endif + lim_enable_overlap11g_protection(mac_ctx, bcn_prm, + mac_hdr, session); + return; + } + /* handling the case when HT AP has overlapping legacy BSS. */ + if (!session->htCapability) + return; + + if (bcn_struct->chan_freq != session->curr_op_freq) + return; + + tmp_exp = (!bcn_struct->erpPresent && !bcn_struct->HTInfo.present) || + /* if erp not present then 11B AP overlapping */ + (!mac_ctx->mlme_cfg->sta.ignore_peer_erp_info && + bcn_struct->erpPresent && + (bcn_struct->erpIEInfo.useProtection || + bcn_struct->erpIEInfo.nonErpPresent)); + if (tmp_exp) { +#ifdef FEATURE_WLAN_ESE + if (session->isESEconnection) { + QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_INFO, + FL("[INFOLOG]ESE 11g erpPresent=%d useProtection=%d nonErpPresent=%d"), + bcn_struct->erpPresent, + bcn_struct->erpIEInfo.useProtection, + bcn_struct->erpIEInfo.nonErpPresent); + } +#endif + lim_enable_overlap11g_protection(mac_ctx, bcn_prm, + mac_hdr, session); + } + /* 11g device overlaps */ + tmp_exp = bcn_struct->erpPresent + && !(bcn_struct->erpIEInfo.useProtection + || bcn_struct->erpIEInfo.nonErpPresent) + && !(bcn_struct->HTInfo.present); + if (tmp_exp) { + lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId, + &(session->gLimOverlap11gParams)); + + if (session->gLimOverlap11gParams.numSta + && !session->gLimOverlap11gParams.protectionEnabled) + lim_enable_ht_protection_from11g(mac_ctx, true, true, + bcn_prm, session); + } + /* ht device overlaps. + * here we will check for HT related devices only which might need + * protection. check for 11b and 11g is already done in the previous + * blocks. so we will not check for HT operating mode as MIXED. + */ + if (!bcn_struct->HTInfo.present) + return; + + /* + * if we are not already in mixed mode or legacy mode as HT operating + * mode and received beacon has HT operating mode as legacy then we need + * to enable protection from 11g station. we don't need protection from + * 11b because if that's needed then our operating mode would have + * already been set to legacy in the previous blocks. + */ + if ((eSIR_HT_OP_MODE_OVERLAP_LEGACY == bcn_struct->HTInfo.opMode) && + !mac_ctx->mlme_cfg->sap_protection_cfg.ignore_peer_ht_opmode) { + if (eSIR_HT_OP_MODE_OVERLAP_LEGACY == mac_ctx->lim.gHTOperMode + || eSIR_HT_OP_MODE_MIXED == mac_ctx->lim.gHTOperMode) + return; + lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId, + &(session->gLimOverlap11gParams)); + if (session->gLimOverlap11gParams.numSta + && !session->gLimOverlap11gParams.protectionEnabled) + lim_enable_ht_protection_from11g(mac_ctx, true, true, + bcn_prm, session); + return; + } + + if (eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT == bcn_struct->HTInfo.opMode) { + lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId, + &(session->gLimOverlapHt20Params)); + if (session->gLimOverlapHt20Params.numSta + && !session->gLimOverlapHt20Params.protectionEnabled) + lim_enable_ht20_protection(mac_ctx, true, true, + bcn_prm, session); + } +} + +/** + * ap_beacon_process() - processes incoming beacons + * + * @mac_ctx: mac global context + * @rx_pkt_info: incoming beacon packet + * @bcn_struct: beacon struct + * @bcn_prm: beacon params + * @session: pe session entry + * + * Return: void + */ +static void +ap_beacon_process(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + tpSchBeaconStruct bcn_struct, + tpUpdateBeaconParams bcn_prm, struct pe_session *session) +{ + uint32_t phy_mode; + enum reg_wifi_band rf_band = REG_BAND_UNKNOWN; + /* Get RF band from session */ + rf_band = session->limRFBand; + + lim_get_phy_mode(mac_ctx, &phy_mode, session); + + if (REG_BAND_5G == rf_band) + ap_beacon_process_5_ghz(mac_ctx, rx_pkt_info, bcn_struct, + bcn_prm, session, phy_mode); + else if (REG_BAND_2G == rf_band) + ap_beacon_process_24_ghz(mac_ctx, rx_pkt_info, bcn_struct, + bcn_prm, session, phy_mode); +} + +/* -------------------------------------------------------------------- */ + +/** + * __sch_beacon_process_no_session + * + * FUNCTION: + * Process the received beacon frame when + * -- Station is not scanning + * -- No corresponding session is found + * + * LOGIC: + * Following scenarios exist when Session Does not exist: + * * IBSS Beacons, when IBSS session already exists with same SSID, + * but from STA which has not yet joined and has a different BSSID. + * - invoke lim_handle_ibs_scoalescing with the session context of existing IBSS session. + * + * * IBSS Beacons when IBSS session does not exist, only Infra or BT-AMP session exists, + * then save the beacon in the scan results and throw it away. + * + * * Infra Beacons + * - beacons received when no session active + * should not come here, it should be handled as part of scanning, + * else they should not be getting received, should update scan results and drop it if that happens. + * - beacons received when IBSS session active: + * update scan results and drop it. + * - beacons received when Infra session(STA) is active: + * update scan results and drop it + * - beacons received when BT-STA session is active: + * update scan results and drop it. + * - beacons received when Infra/BT-STA or Infra/IBSS is active. + * update scan results and drop it. + * + + */ +static void __sch_beacon_process_no_session(struct mac_context *mac, + tpSchBeaconStruct pBeacon, + uint8_t *pRxPacketInfo) +{ + struct pe_session *pe_session = NULL; + + pe_session = lim_is_ibss_session_active(mac); + if (pe_session) { + lim_handle_ibss_coalescing(mac, pBeacon, pRxPacketInfo, + pe_session); + } + return; +} + +/** + * get_operating_channel_width() - Get operating channel width + * @stads - station entry. + * + * This function returns the operating channel width based on + * the supported channel width entry. + * + * Return: tSirMacHTChannelWidth on success + */ +static tSirMacHTChannelWidth get_operating_channel_width(tpDphHashNode stads) +{ + tSirMacHTChannelWidth ch_width = eHT_CHANNEL_WIDTH_20MHZ; + + if (stads->vhtSupportedChannelWidthSet == + WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) + ch_width = eHT_CHANNEL_WIDTH_160MHZ; + else if (stads->vhtSupportedChannelWidthSet == + WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ) + ch_width = eHT_CHANNEL_WIDTH_160MHZ; + else if (stads->vhtSupportedChannelWidthSet == + WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) + ch_width = eHT_CHANNEL_WIDTH_80MHZ; + else if (stads->htSupportedChannelWidthSet) + ch_width = eHT_CHANNEL_WIDTH_40MHZ; + else + ch_width = eHT_CHANNEL_WIDTH_20MHZ; + + return ch_width; +} + +/* + * sch_bcn_process_sta() - Process the received beacon frame for sta + * @mac_ctx: mac_ctx + * @bcn: beacon struct + * @rx_pkt_info: received packet info + * @session: pe session pointer + * @beaconParams: update beacon params + * @sendProbeReq: out flag to indicate if probe rsp is to be sent + * @pMh: mac header + * + * Process the received beacon frame for sta + * + * Return: success of failure of operation + */ +static bool +sch_bcn_process_sta(struct mac_context *mac_ctx, + tpSchBeaconStruct bcn, + uint8_t *rx_pkt_info, + struct pe_session *session, + tUpdateBeaconParams *beaconParams, + uint8_t *sendProbeReq, tpSirMacMgmtHdr pMh) +{ + uint32_t bi; + tpDphHashNode sta = NULL; + QDF_STATUS status; + + /* + * This handles two cases: + * -- Infra STA receiving beacons from AP + */ + + /** + * This is the Beacon received from the AP we're currently associated + * with. Check if there are any changes in AP's capabilities + */ + if (bcn->chan_freq != session->curr_op_freq) { + pe_err("Channel Change freq from %d --> %d - Ignoring beacon!", + session->curr_op_freq, bcn->chan_freq); + return false; + } + + /* + * Ignore bcn as channel switch IE present and csa offload is enabled, + * as in CSA offload enabled case FW will send Event to switch channel + */ + if (bcn->channelSwitchPresent && wma_is_csa_offload_enabled()) { + pe_err_rl("Ignore bcn as channel switch IE present and csa offload is enabled"); + return false; + } + + lim_detect_change_in_ap_capabilities(mac_ctx, bcn, session); + beaconParams->bss_idx = session->vdev_id; + qdf_mem_copy((uint8_t *) &session->lastBeaconTimeStamp, + (uint8_t *) bcn->timeStamp, sizeof(uint64_t)); + session->currentBssBeaconCnt++; + if (session->bcon_dtim_period != bcn->tim.dtimPeriod) { + session->bcon_dtim_period = bcn->tim.dtimPeriod; + lim_send_set_dtim_period(mac_ctx, bcn->tim.dtimPeriod, + session); + } + MTRACE(mac_trace(mac_ctx, TRACE_CODE_RX_MGMT_TSF, + session->peSessionId, bcn->timeStamp[0])); + MTRACE(mac_trace(mac_ctx, TRACE_CODE_RX_MGMT_TSF, + session->peSessionId, bcn->timeStamp[1])); + + /* Read beacon interval session Entry */ + bi = session->beaconParams.beaconInterval; + if (bi != bcn->beaconInterval) { + pe_debug("Beacon interval changed from %d to %d", + bcn->beaconInterval, bi); + + bi = bcn->beaconInterval; + session->beaconParams.beaconInterval = (uint16_t) bi; + beaconParams->paramChangeBitmap |= PARAM_BCN_INTERVAL_CHANGED; + beaconParams->beaconInterval = (uint16_t) bi; + } + + if (bcn->cfPresent) { + if (!cfg_in_range(CFG_CFP_PERIOD, bcn->cfParamSet.cfpPeriod)) { + pe_err("Error in setting CFG item CFP Period"); + return false; + } + mac_ctx->mlme_cfg->rates.cfp_period = bcn->cfParamSet.cfpPeriod; + } + + /* No need to send DTIM Period and Count to HAL/SMAC */ + /* SMAC already parses TIM bit. */ + if (bcn->timPresent) { + if (cfg_in_range(CFG_DTIM_PERIOD, bcn->tim.dtimPeriod)) + mac_ctx->mlme_cfg->sap_cfg.dtim_interval = + bcn->tim.dtimPeriod; + } + + if (mac_ctx->lim.gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) + lim_decide_sta_protection(mac_ctx, bcn, beaconParams, session); + + if (bcn->erpPresent) { + if (bcn->erpIEInfo.barkerPreambleMode) + lim_enable_short_preamble(mac_ctx, false, + beaconParams, session); + else + lim_enable_short_preamble(mac_ctx, true, + beaconParams, session); + } + lim_update_short_slot(mac_ctx, bcn, beaconParams, session); + + sta = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER, + &session->dph.dphHashTable); + if ((bcn->wmeEdcaPresent && session->limWmeEnabled) || + (bcn->edcaPresent && session->limQosEnabled)) { + if (bcn->edcaParams.qosInfo.count != + session->gLimEdcaParamSetCount) { + status = sch_beacon_edca_process(mac_ctx, + &bcn->edcaParams, + session); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("EDCA parameter processing error"); + } else if (sta) { + /* If needed, downgrade the EDCA parameters */ + lim_set_active_edca_params(mac_ctx, + session->gLimEdcaParams, session); + lim_send_edca_params(mac_ctx, + session->gLimEdcaParamsActive, + session->vdev_id, false); + } else { + pe_err("Self Entry missing in Hash Table"); + } + } + return true; + } + + if ((bcn->qosCapabilityPresent && session->limQosEnabled) + && (bcn->qosCapability.qosInfo.count != + session->gLimEdcaParamSetCount)) + *sendProbeReq = true; + + return true; +} + +/** + * update_nss() - Function to update NSS + * @mac_ctx: pointer to Global Mac structure + * @sta_ds: pointer to tpDphHashNode + * @beacon: pointer to tpSchBeaconStruct + * @session_entry: pointer to struct pe_session * + * @mgmt_hdr: pointer to tpSirMacMgmtHdr + * + * function to update NSS + * + * Return: none + */ +static void update_nss(struct mac_context *mac_ctx, tpDphHashNode sta_ds, + tpSchBeaconStruct beacon, struct pe_session *session_entry, + tpSirMacMgmtHdr mgmt_hdr) +{ + if (sta_ds->vhtSupportedRxNss != (beacon->OperatingMode.rxNSS + 1)) { + if (session_entry->nss_forced_1x1) { + pe_debug("Not Updating NSS for special AP"); + return; + } + sta_ds->vhtSupportedRxNss = + beacon->OperatingMode.rxNSS + 1; + lim_set_nss_change(mac_ctx, session_entry, + sta_ds->vhtSupportedRxNss, + mgmt_hdr->sa); + } +} + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +static void +sch_bcn_update_he_ies(struct mac_context *mac_ctx, tpDphHashNode sta_ds, + struct pe_session *session, tpSchBeaconStruct bcn, + tpSirMacMgmtHdr mac_hdr) +{ + uint8_t session_bss_col_disabled_flag; + bool anything_changed = false; + + if (session->is_session_obss_color_collision_det_enabled) + return; + + if (session->he_op.present && bcn->he_op.present) { + if (bcn->vendor_he_bss_color_change.present && + (session->he_op.bss_color != + bcn->vendor_he_bss_color_change.new_color)) { + pe_debug("bss color changed from [%d] to [%d]", + session->he_op.bss_color, + bcn->vendor_he_bss_color_change.new_color); + session->he_op.bss_color = + bcn->vendor_he_bss_color_change.new_color; + anything_changed = true; + } + session_bss_col_disabled_flag = session->he_op.bss_col_disabled; + if (session_bss_col_disabled_flag != + bcn->he_op.bss_col_disabled) { + pe_debug("color disable flag changed from [%d] to [%d]", + session->he_op.bss_col_disabled, + bcn->he_op.bss_col_disabled); + session->he_op.bss_col_disabled = + bcn->he_op.bss_col_disabled; + anything_changed = true; + } + } + if (anything_changed) + lim_send_he_ie_update(mac_ctx, session); +} +#else +static void +sch_bcn_update_he_ies(struct mac_context *mac_ctx, tpDphHashNode sta_ds, + struct pe_session *session, tpSchBeaconStruct bcn, + tpSirMacMgmtHdr mac_hdr) +{ + return; +} +#endif + +static void +sch_bcn_update_opmode_change(struct mac_context *mac_ctx, tpDphHashNode sta_ds, + struct pe_session *session, tpSchBeaconStruct bcn, + tpSirMacMgmtHdr mac_hdr, uint8_t cb_mode) +{ + bool skip_opmode_update = false; + uint8_t oper_mode; + uint32_t fw_vht_ch_wd = wma_get_vht_ch_width(); + uint8_t ch_width = 0; + + /* + * Ignore opmode change during channel change The opmode will be updated + * with the beacons on new channel once the AP move to new channel. + */ + if (session->ch_switch_in_progress) { + pe_debug("Ignore opmode change as channel switch is in progress"); + return; + } + + if (session->vhtCapability && bcn->OperatingMode.present) { + update_nss(mac_ctx, sta_ds, bcn, session, mac_hdr); + oper_mode = get_operating_channel_width(sta_ds); + if ((oper_mode == eHT_CHANNEL_WIDTH_80MHZ) && + (bcn->OperatingMode.chanWidth > eHT_CHANNEL_WIDTH_80MHZ)) + skip_opmode_update = true; + + if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE == cb_mode) { + /* + * if channel bonding is disabled from INI do not + * update the chan width + */ + pe_debug_rl("CB disabled skip bw update: old[%d] new[%d]", + oper_mode, + bcn->OperatingMode.chanWidth); + return; + } + + if (!skip_opmode_update && + ((oper_mode != bcn->OperatingMode.chanWidth) || + (sta_ds->vhtSupportedRxNss != + (bcn->OperatingMode.rxNSS + 1)))) { + pe_debug("received OpMode Chanwidth %d", + bcn->OperatingMode.chanWidth); + pe_debug("MAC - %0x:%0x:%0x:%0x:%0x:%0x", + mac_hdr->sa[0], mac_hdr->sa[1], + mac_hdr->sa[2], mac_hdr->sa[3], + mac_hdr->sa[4], mac_hdr->sa[5]); + + if ((bcn->OperatingMode.chanWidth >= + eHT_CHANNEL_WIDTH_160MHZ) && + (fw_vht_ch_wd > eHT_CHANNEL_WIDTH_80MHZ)) { + pe_debug("Updating the CH Width to 160MHz"); + sta_ds->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ; + sta_ds->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + ch_width = eHT_CHANNEL_WIDTH_160MHZ; + } else if (bcn->OperatingMode.chanWidth >= + eHT_CHANNEL_WIDTH_80MHZ) { + pe_debug("Updating the CH Width to 80MHz"); + sta_ds->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ; + sta_ds->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + ch_width = eHT_CHANNEL_WIDTH_80MHZ; + } else if (bcn->OperatingMode.chanWidth == + eHT_CHANNEL_WIDTH_40MHZ) { + pe_debug("Updating the CH Width to 40MHz"); + sta_ds->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ; + sta_ds->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + ch_width = eHT_CHANNEL_WIDTH_40MHZ; + } else if (bcn->OperatingMode.chanWidth == + eHT_CHANNEL_WIDTH_20MHZ) { + pe_debug("Updating the CH Width to 20MHz"); + sta_ds->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ; + sta_ds->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_20MHZ; + ch_width = eHT_CHANNEL_WIDTH_20MHZ; + } + lim_check_vht_op_mode_change(mac_ctx, session, + ch_width, mac_hdr->sa); + update_nss(mac_ctx, sta_ds, bcn, session, mac_hdr); + } + return; + } + + if (!(session->vhtCapability && bcn->VHTOperation.present)) + return; + + oper_mode = sta_ds->vhtSupportedChannelWidthSet; + if ((oper_mode == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) && + (oper_mode < bcn->VHTOperation.chanWidth)) + skip_opmode_update = true; + + if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE == cb_mode) { + /* + * if channel bonding is disabled from INI do not + * update the chan width + */ + pe_debug_rl("CB disabled skip bw update: old[%d] new[%d]", + oper_mode, bcn->OperatingMode.chanWidth); + + return; + } + + if (!skip_opmode_update && + (oper_mode != bcn->VHTOperation.chanWidth)) { + pe_debug("received VHTOP CHWidth %d", + bcn->VHTOperation.chanWidth); + pe_debug("MAC - %0x:%0x:%0x:%0x:%0x:%0x", + mac_hdr->sa[0], mac_hdr->sa[1], + mac_hdr->sa[2], mac_hdr->sa[3], + mac_hdr->sa[4], mac_hdr->sa[5]); + + if ((bcn->VHTOperation.chanWidth >= + WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) && + (fw_vht_ch_wd > eHT_CHANNEL_WIDTH_80MHZ)) { + pe_debug("Updating the CH Width to 160MHz"); + sta_ds->vhtSupportedChannelWidthSet = + bcn->VHTOperation.chanWidth; + sta_ds->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + ch_width = eHT_CHANNEL_WIDTH_160MHZ; + } else if (bcn->VHTOperation.chanWidth >= + WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) { + pe_debug("Updating the CH Width to 80MHz"); + sta_ds->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ; + sta_ds->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + ch_width = eHT_CHANNEL_WIDTH_80MHZ; + } else if (bcn->VHTOperation.chanWidth == + WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ) { + sta_ds->vhtSupportedChannelWidthSet = + WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ; + if (bcn->HTCaps.supportedChannelWidthSet) { + pe_debug("Updating the CH Width to 40MHz"); + sta_ds->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_40MHZ; + ch_width = eHT_CHANNEL_WIDTH_40MHZ; + } else { + pe_debug("Updating the CH Width to 20MHz"); + sta_ds->htSupportedChannelWidthSet = + eHT_CHANNEL_WIDTH_20MHZ; + ch_width = eHT_CHANNEL_WIDTH_20MHZ; + } + } + lim_check_vht_op_mode_change(mac_ctx, session, ch_width, + mac_hdr->sa); + } +} + +/* + * sch_bcn_process_sta_ibss() - Process the received beacon frame + * for sta and ibss + * @mac_ctx: mac_ctx + * @bcn: beacon struct + * @rx_pkt_info: received packet info + * @session: pe session pointer + * @beaconParams: update beacon params + * @sendProbeReq: out flag to indicate if probe rsp is to be sent + * @pMh: mac header + * + * Process the received beacon frame for sta and ibss + * + * Return: void + */ +static void +sch_bcn_process_sta_ibss(struct mac_context *mac_ctx, + tpSchBeaconStruct bcn, + uint8_t *rx_pkt_info, + struct pe_session *session, + tUpdateBeaconParams *beaconParams, + uint8_t *sendProbeReq, tpSirMacMgmtHdr pMh) +{ + tpDphHashNode sta = NULL; + uint16_t aid; + uint8_t cb_mode; + + if (wlan_reg_is_24ghz_ch_freq(session->curr_op_freq)) { + if (session->force_24ghz_in_ht20) + cb_mode = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + else + cb_mode = + mac_ctx->roam.configParam.channelBondingMode24GHz; + } else + cb_mode = mac_ctx->roam.configParam.channelBondingMode5GHz; + /* check for VHT capability */ + sta = dph_lookup_hash_entry(mac_ctx, pMh->sa, &aid, + &session->dph.dphHashTable); + if ((!sta)) + return; + sch_bcn_update_opmode_change(mac_ctx, sta, session, bcn, pMh, + cb_mode); + sch_bcn_update_he_ies(mac_ctx, sta, session, bcn, pMh); + return; +} + +/** + * get_local_power_constraint_beacon() - extracts local constraint + * from beacon + * @bcn: beacon structure + * @local_constraint: local constraint pointer + * + * Return: None + */ +#ifdef FEATURE_WLAN_ESE +static void get_local_power_constraint_beacon( + tpSchBeaconStruct bcn, + int8_t *local_constraint) +{ + if (bcn->eseTxPwr.present) + *local_constraint = bcn->eseTxPwr.power_limit; +} +#else +static void get_local_power_constraint_beacon( + tpSchBeaconStruct bcn, + int8_t *local_constraint) +{ + +} +#endif + +/* + * __sch_beacon_process_for_session() - Process the received beacon frame when + * station is not scanning and corresponding session is found + * + * + * @mac_ctx: mac_ctx + * @bcn: beacon struct + * @rx_pkt_info: received packet info + * @session: pe session pointer + * + * Following scenarios exist when Session exists + * IBSS STA receiving beacons from IBSS Peers, who are part of IBSS. + * - call lim_handle_ibs_scoalescing with that session context. + * Infra STA receiving beacons from AP to which it is connected + * - call sch_beacon_processFromAP with that session's context. + * - call sch_beacon_processFromAP with that session's context. + * (here need to make sure BTAP creates session entry for BT STA) + * - just update the beacon count for heart beat purposes for now, + * for now, don't process the beacon. + * Infra/IBSS both active and receives IBSS beacon: + * - call lim_handle_ibs_scoalescing with that session context. + * Infra/IBSS both active and receives Infra beacon: + * - call sch_beacon_processFromAP with that session's context. + * any updates to EDCA parameters will be effective for IBSS as well, + * even though no WMM for IBSS ?? Need to figure out how to handle + * this scenario. + * Infra/BTSTA both active and receive Infra beacon. + * - change in EDCA parameters on Infra affect the BTSTA link. + * Update the same parameters on BT link + * Infra/BTSTA both active and receive BT-AP beacon. + * - update beacon cnt for heartbeat + * Infra/BTAP both active and receive Infra beacon. + * - BT-AP starts advertising BE parameters from Infra AP, if they get + * changed. + * Infra/BTAP both active and receive BTSTA beacon. + * - update beacon cnt for heartbeat + * + * Return: void + */ +static void __sch_beacon_process_for_session(struct mac_context *mac_ctx, + tpSchBeaconStruct bcn, + uint8_t *rx_pkt_info, + struct pe_session *session) +{ + tUpdateBeaconParams beaconParams; + uint8_t sendProbeReq = false; + tpSirMacMgmtHdr pMh = WMA_GET_RX_MAC_HEADER(rx_pkt_info); + int8_t regMax = 0, maxTxPower = 0, local_constraint; + struct lim_max_tx_pwr_attr tx_pwr_attr = {0}; + uint32_t chan_freq = 0; + + qdf_mem_zero(&beaconParams, sizeof(tUpdateBeaconParams)); + beaconParams.paramChangeBitmap = 0; + + if (LIM_IS_IBSS_ROLE(session)) { + lim_handle_ibss_coalescing(mac_ctx, bcn, rx_pkt_info, session); + } else if (LIM_IS_STA_ROLE(session)) { + if (false == sch_bcn_process_sta(mac_ctx, bcn, rx_pkt_info, + session, &beaconParams, + &sendProbeReq, pMh)) + return; + } + + /* + * For vht session, if opermode ie or vht oper IE is present + * bandwidth change will be taken care using these vht IEs. + */ + if (!(session->vhtCapability && (bcn->OperatingMode.present || + bcn->VHTOperation.present)) && session->htCapability && + bcn->HTInfo.present && !LIM_IS_IBSS_ROLE(session)) + lim_update_sta_run_time_ht_switch_chnl_params(mac_ctx, + &bcn->HTInfo, session); + + if ((LIM_IS_STA_ROLE(session) && !wma_is_csa_offload_enabled()) + || LIM_IS_IBSS_ROLE(session)) { + /* Channel Switch information element updated */ + if (bcn->channelSwitchPresent) { + /* + * on receiving channel switch announcement from AP, + * delete all TDLS peers before leaving BSS and proceed + * for channel switch + */ + if (LIM_IS_STA_ROLE(session)) { + lim_update_tdls_set_state_for_fw(session, + false); + lim_delete_tdls_peers(mac_ctx, session); + } + lim_update_channel_switch(mac_ctx, bcn, session); + } else if (session->gLimSpecMgmt.dot11hChanSwState == + eLIM_11H_CHANSW_RUNNING) { + lim_cancel_dot11h_channel_switch(mac_ctx, session); + } + } + if (LIM_IS_STA_ROLE(session) + || LIM_IS_IBSS_ROLE(session)) + sch_bcn_process_sta_ibss(mac_ctx, bcn, + rx_pkt_info, session, + &beaconParams, &sendProbeReq, pMh); + /* Obtain the Max Tx power for the current regulatory */ + regMax = wlan_reg_get_channel_reg_power_for_freq( + mac_ctx->pdev, session->curr_op_freq); + + local_constraint = regMax; + + if (mac_ctx->mlme_cfg->sta.allow_tpc_from_ap) { + get_local_power_constraint_beacon(bcn, &local_constraint); + + if (mac_ctx->rrm.rrmPEContext.rrmEnable && + bcn->powerConstraintPresent) { + local_constraint = regMax; + local_constraint -= + bcn->localPowerConstraint.localPowerConstraints; + } + } + + tx_pwr_attr.reg_max = regMax; + tx_pwr_attr.ap_tx_power = local_constraint; + tx_pwr_attr.ini_tx_power = mac_ctx->mlme_cfg->power.max_tx_power; + tx_pwr_attr.frequency = session->curr_op_freq; + + maxTxPower = lim_get_max_tx_power(mac_ctx, &tx_pwr_attr); + + /* If maxTxPower is increased or decreased */ + if (maxTxPower != session->maxTxPower) { + pe_debug("Local power constraint change, Updating new maxTx power %d from old pwr %d (regMax %d local %d)", + maxTxPower, session->maxTxPower, regMax, + local_constraint); + if (lim_send_set_max_tx_power_req(mac_ctx, maxTxPower, session) + == QDF_STATUS_SUCCESS) + session->maxTxPower = maxTxPower; + } + + /* Indicate to LIM that Beacon is received */ + if (bcn->HTInfo.present) { + chan_freq = wlan_reg_legacy_chan_to_freq(mac_ctx->pdev, + bcn->HTInfo.primaryChannel); + lim_received_hb_handler(mac_ctx, chan_freq, session); + } else + lim_received_hb_handler(mac_ctx, bcn->chan_freq, session); + + /* + * I don't know if any additional IE is required here. Currently, not + * include addIE. + */ + if (sendProbeReq) + lim_send_probe_req_mgmt_frame(mac_ctx, &session->ssId, + session->bssId, session->curr_op_freq, + session->self_mac_addr, session->dot11mode, NULL, NULL); + + if ((false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running) + && beaconParams.paramChangeBitmap) { + pe_debug("Beacon for session[%d] got changed param change bitmap: 0x%x", + session->peSessionId, beaconParams.paramChangeBitmap); + lim_send_beacon_params(mac_ctx, &beaconParams, session); + } + + if ((session->opmode == QDF_P2P_CLIENT_MODE) && + session->send_p2p_conf_frame) { + lim_p2p_oper_chan_change_confirm_action_frame(mac_ctx, + session->bssId, + session); + session->send_p2p_conf_frame = false; + } +} + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +static void ap_update_bss_color_info(struct mac_context *mac_ctx, + struct pe_session *session, + uint8_t bss_color) +{ + if (!session) + return; + + if (bss_color < 1 || bss_color > 63) { + pe_warn("Invalid BSS color"); + return; + } + + session->bss_color_info[bss_color - 1].seen_count++; + session->bss_color_info[bss_color - 1].timestamp = + qdf_get_system_timestamp(); +} + +static uint8_t ap_get_new_bss_color(struct mac_context *mac_ctx, struct pe_session *session) +{ + int i; + uint8_t new_bss_color; + struct bss_color_info color_info; + qdf_time_t cur_timestamp; + + if (!session) + return 0; + + color_info = session->bss_color_info[0]; + new_bss_color = 0; + cur_timestamp = qdf_get_system_timestamp(); + for (i = 1; i < MAX_BSS_COLOR_VALUE; i++) { + if (session->bss_color_info[i].seen_count == 0) { + new_bss_color = i + 1; + return new_bss_color; + } + + if (color_info.seen_count > + session->bss_color_info[i].seen_count && + (cur_timestamp - session->bss_color_info[i]. + timestamp) > TIME_BEACON_NOT_UPDATED) { + color_info = session->bss_color_info[i]; + new_bss_color = i + 1; + } + } + pe_debug("new bss color: %d", new_bss_color); + return new_bss_color; +} + +static void sch_check_bss_color_ie(struct mac_context *mac_ctx, + struct pe_session *ap_session, + tSchBeaconStruct *bcn, + tUpdateBeaconParams *bcn_prm) +{ + /* check bss color in the beacon */ + if (ap_session->he_op.present && !ap_session->he_op.bss_color) { + if (bcn->he_op.present && + (bcn->he_op.bss_color == + ap_session->he_op.bss_color)) { + ap_session->he_op.bss_col_disabled = 1; + bcn_prm->paramChangeBitmap |= + PARAM_BSS_COLOR_CHANGED; + ap_session->he_bss_color_change.countdown = + BSS_COLOR_SWITCH_COUNTDOWN; + ap_session->he_bss_color_change.new_color = + ap_get_new_bss_color(mac_ctx, + ap_session); + ap_session->he_op.bss_color = ap_session-> + he_bss_color_change.new_color; + bcn_prm->bss_color = ap_session->he_op.bss_color; + bcn_prm->bss_color_disabled = + ap_session->he_op.bss_col_disabled; + ap_session->bss_color_changing = 1; + } else { + /* update info for the bss color */ + if (bcn->he_op.present) + ap_update_bss_color_info(mac_ctx, + ap_session, + bcn->he_op.bss_color); + } + } +} + +#else +static void sch_check_bss_color_ie(struct mac_context *mac_ctx, + struct pe_session *ap_session, + tSchBeaconStruct *bcn, + tUpdateBeaconParams *bcn_prm) +{ +} +#endif + +void sch_beacon_process_for_ap(struct mac_context *mac_ctx, + uint8_t session_id, + uint8_t *rx_pkt_info, + tSchBeaconStruct *bcn) +{ + struct pe_session *ap_session; + tUpdateBeaconParams bcn_prm; + + if (!bcn || !rx_pkt_info) { + pe_debug("bcn %pK or rx_pkt_info %pKis NULL", + bcn, rx_pkt_info); + return; + } + + ap_session = pe_find_session_by_session_id(mac_ctx, session_id); + if (!ap_session) + return; + + if (!LIM_IS_AP_ROLE(ap_session)) + return; + + qdf_mem_zero(&bcn_prm, sizeof(tUpdateBeaconParams)); + bcn_prm.paramChangeBitmap = 0; + + bcn_prm.bss_idx = ap_session->vdev_id; + + if (!ap_session->is_session_obss_color_collision_det_enabled) + sch_check_bss_color_ie(mac_ctx, ap_session, + bcn, &bcn_prm); + + if ((ap_session->gLimProtectionControl != + MLME_FORCE_POLICY_PROTECTION_DISABLE) && + !ap_session->is_session_obss_offload_enabled) + ap_beacon_process(mac_ctx, rx_pkt_info, + bcn, &bcn_prm, ap_session); + + if ((false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running) + && bcn_prm.paramChangeBitmap) { + /* Update the bcn and apply the new settings to HAL */ + sch_set_fixed_beacon_fields(mac_ctx, ap_session); + pe_debug("Beacon for PE session[%d] got changed", + ap_session->peSessionId); + pe_debug("sending beacon param change bitmap: 0x%x", + bcn_prm.paramChangeBitmap); + lim_send_beacon_params(mac_ctx, &bcn_prm, ap_session); + } +} + +#ifdef WLAN_BCN_RECV_FEATURE +/* + * sch_send_beacon_report() - To Fill beacon report for + * each beacon coming from connected peer and sends it + * to upper layer + * @mac_ctx: Mac context + * @beacon_struct: Pointing to beacon structure + * @session: pointer to the PE session + * + * Return: None + */ +static +void sch_send_beacon_report(struct mac_context *mac_ctx, + struct sSirProbeRespBeacon *beacon_struct, + struct pe_session *session) +{ + struct wlan_beacon_report beacon_report; + + if (!mac_ctx->lim.sme_bcn_rcv_callback) + return; + + if (!LIM_IS_STA_ROLE(session)) + return; + + if (sir_compare_mac_addr(session->bssId, beacon_struct->bssid)) { + /* Prepare beacon report from incoming beacon */ + qdf_mem_copy(beacon_report.bssid.bytes, beacon_struct->bssid, + sizeof(tSirMacAddr)); + + qdf_mem_copy(&beacon_report.time_stamp, + &beacon_struct->timeStamp, sizeof(qdf_time_t)); + beacon_report.beacon_interval = beacon_struct->beaconInterval; + beacon_report.frequency = beacon_struct->chan_freq; + + beacon_report.ssid.length = beacon_struct->ssId.length; + qdf_mem_copy(&beacon_report.ssid.ssid, + &beacon_struct->ssId.ssId, + beacon_report.ssid.length); + + beacon_report.boot_time = + qdf_do_div(qdf_get_monotonic_boottime(), + QDF_MC_TIMER_TO_MS_UNIT); + + beacon_report.vdev_id = session->vdev_id; + + /* Send report to upper layer */ + mac_ctx->lim.sme_bcn_rcv_callback(mac_ctx->hdd_handle, + &beacon_report); + } +} + +#else +static inline +void sch_send_beacon_report(struct mac_context *mac_ctx, + struct sSirProbeRespBeacon *beacon_struct, + struct pe_session *session) +{ +} +#endif + +/** + * sch_beacon_process() - process the beacon frame + * @mac_ctx: mac global context + * @rx_pkt_info: pointer to buffer descriptor + * @session: pointer to the PE session + * + * Return: None + */ +void +sch_beacon_process(struct mac_context *mac_ctx, uint8_t *rx_pkt_info, + struct pe_session *session) +{ + static tSchBeaconStruct bcn; + + /* Convert the beacon frame into a structure */ + if (sir_convert_beacon_frame2_struct(mac_ctx, (uint8_t *) rx_pkt_info, + &bcn) != QDF_STATUS_SUCCESS) { + pe_err_rl("beacon parsing failed"); + return; + } + + /* + * Now process the beacon in the context of the BSS which is + * transmitting the beacons, if one is found + */ + if (!session) { + __sch_beacon_process_no_session(mac_ctx, &bcn, rx_pkt_info); + } else { + sch_send_beacon_report(mac_ctx, &bcn, session); + __sch_beacon_process_for_session(mac_ctx, &bcn, rx_pkt_info, + session); + } +} + +/** + * sch_beacon_edca_process(): Process the EDCA parameter set in the received + * beacon frame + * + * @mac_ctx: mac global context + * @edca: reference to edca parameters in beacon struct + * @session : pesession entry + * + * @return status of operation + */ +QDF_STATUS +sch_beacon_edca_process(struct mac_context *mac, tSirMacEdcaParamSetIE *edca, + struct pe_session *session) +{ + uint8_t i; + bool follow_ap_edca; +#ifdef FEATURE_WLAN_DIAG_SUPPORT + host_log_qos_edca_pkt_type *log_ptr = NULL; +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + if (!(mac->mlme_cfg)) { + pe_err("invalid mlme cfg"); + return QDF_STATUS_E_FAILURE; + } + + follow_ap_edca = mlme_get_follow_ap_edca_flag(session->vdev); + + session->gLimEdcaParamSetCount = edca->qosInfo.count; + session->gLimEdcaParams[QCA_WLAN_AC_BE] = edca->acbe; + session->gLimEdcaParams[QCA_WLAN_AC_BK] = edca->acbk; + session->gLimEdcaParams[QCA_WLAN_AC_VI] = edca->acvi; + session->gLimEdcaParams[QCA_WLAN_AC_VO] = edca->acvo; + + if (mac->mlme_cfg->edca_params.enable_edca_params && !follow_ap_edca) { + session->gLimEdcaParams[QCA_WLAN_AC_VO].aci.aifsn = + mac->mlme_cfg->edca_params.edca_ac_vo.vo_aifs; + session->gLimEdcaParams[QCA_WLAN_AC_VI].aci.aifsn = + mac->mlme_cfg->edca_params.edca_ac_vi.vi_aifs; + session->gLimEdcaParams[QCA_WLAN_AC_BK].aci.aifsn = + mac->mlme_cfg->edca_params.edca_ac_bk.bk_aifs; + session->gLimEdcaParams[QCA_WLAN_AC_BE].aci.aifsn = + mac->mlme_cfg->edca_params.edca_ac_be.be_aifs; + + session->gLimEdcaParams[QCA_WLAN_AC_VO].cw.min = + mac->mlme_cfg->edca_params.edca_ac_vo.vo_cwmin; + session->gLimEdcaParams[QCA_WLAN_AC_VI].cw.min = + mac->mlme_cfg->edca_params.edca_ac_vi.vi_cwmin; + session->gLimEdcaParams[QCA_WLAN_AC_BK].cw.min = + mac->mlme_cfg->edca_params.edca_ac_bk.bk_cwmin; + session->gLimEdcaParams[QCA_WLAN_AC_BE].cw.min = + mac->mlme_cfg->edca_params.edca_ac_be.be_cwmin; + + session->gLimEdcaParams[QCA_WLAN_AC_VO].cw.max = + mac->mlme_cfg->edca_params.edca_ac_vo.vo_cwmax; + session->gLimEdcaParams[QCA_WLAN_AC_VI].cw.max = + mac->mlme_cfg->edca_params.edca_ac_vi.vi_cwmax; + session->gLimEdcaParams[QCA_WLAN_AC_BK].cw.max = + mac->mlme_cfg->edca_params.edca_ac_bk.bk_cwmax; + session->gLimEdcaParams[QCA_WLAN_AC_BE].cw.max = + mac->mlme_cfg->edca_params.edca_ac_be.be_cwmax; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT + WLAN_HOST_DIAG_LOG_ALLOC(log_ptr, host_log_qos_edca_pkt_type, + LOG_WLAN_QOS_EDCA_C); + if (log_ptr) { + log_ptr->aci_be = session->gLimEdcaParams[QCA_WLAN_AC_BE].aci.aci; + log_ptr->cw_be = + session->gLimEdcaParams[QCA_WLAN_AC_BE].cw.max << 4 + | session->gLimEdcaParams[QCA_WLAN_AC_BE].cw.min; + log_ptr->txoplimit_be = + session->gLimEdcaParams[QCA_WLAN_AC_BE].txoplimit; + log_ptr->aci_bk = + session->gLimEdcaParams[QCA_WLAN_AC_BK].aci.aci; + log_ptr->cw_bk = + session->gLimEdcaParams[QCA_WLAN_AC_BK].cw.max << 4 + | session->gLimEdcaParams[QCA_WLAN_AC_BK].cw.min; + log_ptr->txoplimit_bk = + session->gLimEdcaParams[QCA_WLAN_AC_BK].txoplimit; + log_ptr->aci_vi = + session->gLimEdcaParams[QCA_WLAN_AC_VI].aci.aci; + log_ptr->cw_vi = + session->gLimEdcaParams[QCA_WLAN_AC_VI].cw.max << 4 + | session->gLimEdcaParams[QCA_WLAN_AC_VI].cw.min; + log_ptr->txoplimit_vi = + session->gLimEdcaParams[QCA_WLAN_AC_VI].txoplimit; + log_ptr->aci_vo = + session->gLimEdcaParams[QCA_WLAN_AC_VO].aci.aci; + log_ptr->cw_vo = + session->gLimEdcaParams[QCA_WLAN_AC_VO].cw.max << 4 + | session->gLimEdcaParams[QCA_WLAN_AC_VO].cw.min; + log_ptr->txoplimit_vo = + session->gLimEdcaParams[QCA_WLAN_AC_VO].txoplimit; + } + WLAN_HOST_DIAG_LOG_REPORT(log_ptr); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + pe_debug("Edca param enabled %d. Updating Local Params to: ", + mac->mlme_cfg->edca_params.enable_edca_params); + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + pe_nofl_debug("AC[%d]: AIFSN: %d, ACM %d, CWmin %d, CWmax %d, TxOp %d", + i, session->gLimEdcaParams[i].aci.aifsn, + session->gLimEdcaParams[i].aci.acm, + session->gLimEdcaParams[i].cw.min, + session->gLimEdcaParams[i].cw.max, + session->gLimEdcaParams[i].txoplimit); + } + return QDF_STATUS_SUCCESS; +} + +void lim_enable_obss_detection_config(struct mac_context *mac_ctx, + struct pe_session *session) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!session) { + pe_err("Invalid session, protection not enabled"); + return; + } + + if (session->gLimProtectionControl == + MLME_FORCE_POLICY_PROTECTION_DISABLE) { + pe_err("protectiond disabled, force policy, session %d", + session->smeSessionId); + return; + } + + if (mac_ctx->mlme_cfg->obss_ht40.obss_detection_offload_enabled) { + status = lim_obss_send_detection_cfg(mac_ctx, session, true); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("vdev %d: offload enable failed, trying legacy", + session->smeSessionId); + session->is_session_obss_offload_enabled = false; + } else { + pe_debug("vdev %d: offload detection enabled", + session->smeSessionId); + session->is_session_obss_offload_enabled = true; + lim_obss_send_detection_cfg(mac_ctx, session, true); + } + } + + if (!mac_ctx->mlme_cfg->obss_ht40.obss_detection_offload_enabled || + QDF_IS_STATUS_ERROR(status)) { + status = qdf_mc_timer_start(&session-> + protection_fields_reset_timer, + SCH_PROTECTION_RESET_TIME); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("vdev %d: start timer failed", + session->smeSessionId); + else + pe_debug("vdev %d: legacy detection enabled", + session->smeSessionId); + } +} + +QDF_STATUS lim_obss_generate_detection_config(struct mac_context *mac_ctx, + struct pe_session *session, + struct obss_detection_cfg *cfg) +{ + uint32_t phy_mode; + enum reg_wifi_band rf_band = REG_BAND_UNKNOWN; + struct obss_detection_cfg *cur_detect; + + if (!mac_ctx || !session || !cfg) { + pe_err("Invalid params mac_ctx %pK, session %pK, cfg %pK", + mac_ctx, session, cfg); + return QDF_STATUS_E_INVAL; + } + + lim_get_phy_mode(mac_ctx, &phy_mode, session); + rf_band = session->limRFBand; + qdf_mem_zero(cfg, sizeof(*cfg)); + cur_detect = &session->current_obss_detection; + + pe_debug("band:%d, phy_mode:%d, ht_cap:%d, ht_oper_mode:%d", + rf_band, phy_mode, session->htCapability, + mac_ctx->lim.gHTOperMode); + pe_debug("assoc_sta: 11b:%d, 11g:%d, 11a:%d, ht20:%d", + session->gLim11bParams.protectionEnabled, + session->gLim11gParams.protectionEnabled, + session->gLim11aParams.protectionEnabled, + session->gLimHt20Params.protectionEnabled); + pe_debug("obss: 11b:%d, 11g:%d, 11a:%d, ht20:%d", + session->gLimOlbcParams.protectionEnabled, + session->gLimOverlap11gParams.protectionEnabled, + session->gLimOverlap11aParams.protectionEnabled, + session->gLimOverlapHt20Params.protectionEnabled); + pe_debug("detect: b_ap:%d, b_s:%d, g:%d, a:%d, htl:%d, htm:%d, ht20:%d", + cur_detect->obss_11b_ap_detect_mode, + cur_detect->obss_11b_sta_detect_mode, + cur_detect->obss_11g_ap_detect_mode, + cur_detect->obss_11a_detect_mode, + cur_detect->obss_ht_legacy_detect_mode, + cur_detect->obss_ht_mixed_detect_mode, + cur_detect->obss_ht_20mhz_detect_mode); + + if (rf_band == REG_BAND_2G) { + if ((phy_mode == WNI_CFG_PHY_MODE_11G || + session->htCapability) && + !session->gLim11bParams.protectionEnabled) { + if (!session->gLimOlbcParams.protectionEnabled && + !session->gLimOverlap11gParams.protectionEnabled) { + cfg->obss_11b_ap_detect_mode = + OBSS_OFFLOAD_DETECTION_PRESENT; + cfg->obss_11b_sta_detect_mode = + OBSS_OFFLOAD_DETECTION_PRESENT; + } else { + if (cur_detect->obss_11b_ap_detect_mode == + OBSS_OFFLOAD_DETECTION_PRESENT) + cfg->obss_11b_ap_detect_mode = + OBSS_OFFLOAD_DETECTION_ABSENT; + if (cur_detect->obss_11b_sta_detect_mode == + OBSS_OFFLOAD_DETECTION_PRESENT) + cfg->obss_11b_sta_detect_mode = + OBSS_OFFLOAD_DETECTION_ABSENT; + } + } else if (session->gLim11bParams.protectionEnabled) { + session->gLimOlbcParams.protectionEnabled = false; + } + + if (session->htCapability && + session->cfgProtection.overlapFromllg && + !session->gLim11gParams.protectionEnabled) { + if (!session->gLimOverlap11gParams.protectionEnabled) { + cfg->obss_11g_ap_detect_mode = + OBSS_OFFLOAD_DETECTION_PRESENT; + cfg->obss_ht_legacy_detect_mode = + OBSS_OFFLOAD_DETECTION_PRESENT; + cfg->obss_ht_mixed_detect_mode = + OBSS_OFFLOAD_DETECTION_PRESENT; + } else { + if (cur_detect->obss_11g_ap_detect_mode == + OBSS_OFFLOAD_DETECTION_PRESENT) + cfg->obss_11g_ap_detect_mode = + OBSS_OFFLOAD_DETECTION_ABSENT; + if (cur_detect->obss_ht_legacy_detect_mode == + OBSS_OFFLOAD_DETECTION_PRESENT) + cfg->obss_ht_legacy_detect_mode = + OBSS_OFFLOAD_DETECTION_ABSENT; + if (cur_detect->obss_ht_mixed_detect_mode == + OBSS_OFFLOAD_DETECTION_PRESENT) + cfg->obss_ht_mixed_detect_mode = + OBSS_OFFLOAD_DETECTION_ABSENT; + } + } else if (session->gLim11gParams.protectionEnabled) { + session->gLimOverlap11gParams.protectionEnabled = false; + } + + /* INI related settings */ + if (mac_ctx->mlme_cfg->sta.ignore_peer_erp_info) + cfg->obss_11b_sta_detect_mode = + OBSS_OFFLOAD_DETECTION_DISABLED; + + if (mac_ctx->mlme_cfg->sap_protection_cfg.ignore_peer_ht_opmode) + cfg->obss_ht_legacy_detect_mode = + OBSS_OFFLOAD_DETECTION_DISABLED; + } + + if ((rf_band == REG_BAND_5G) && session->htCapability) { + if (!session->gLim11aParams.protectionEnabled) { + if (!session->gLimOverlap11aParams.protectionEnabled) + cfg->obss_11a_detect_mode = + OBSS_OFFLOAD_DETECTION_PRESENT; + else if (cur_detect->obss_11a_detect_mode == + OBSS_OFFLOAD_DETECTION_PRESENT) + cfg->obss_11a_detect_mode = + OBSS_OFFLOAD_DETECTION_ABSENT; + } else { + session->gLimOverlap11aParams.protectionEnabled = false; + } + } + + if (((rf_band == REG_BAND_2G) || (rf_band == REG_BAND_5G)) && + session->htCapability) { + + if (!session->gLimHt20Params.protectionEnabled) { + if (!session->gLimOverlapHt20Params.protectionEnabled) { + cfg->obss_ht_20mhz_detect_mode = + OBSS_OFFLOAD_DETECTION_PRESENT; + } else if (cur_detect->obss_ht_20mhz_detect_mode == + OBSS_OFFLOAD_DETECTION_PRESENT) { + cfg->obss_ht_20mhz_detect_mode = + OBSS_OFFLOAD_DETECTION_ABSENT; + } + } else { + session->gLimOverlapHt20Params.protectionEnabled = + false; + } + } + + pe_debug("b_ap:%d, b_s:%d, g:%d, a:%d, ht_le:%d, ht_m:%d, ht_20:%d", + cfg->obss_11b_ap_detect_mode, + cfg->obss_11b_sta_detect_mode, + cfg->obss_11g_ap_detect_mode, + cfg->obss_11a_detect_mode, + cfg->obss_ht_legacy_detect_mode, + cfg->obss_ht_mixed_detect_mode, + cfg->obss_ht_20mhz_detect_mode); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS lim_obss_send_detection_cfg(struct mac_context *mac_ctx, + struct pe_session *session, bool force) +{ + QDF_STATUS status; + struct obss_detection_cfg obss_cfg; + struct wmi_obss_detection_cfg_param *req_param; + + if (!session) { + pe_err("Invalid session"); + return QDF_STATUS_E_INVAL; + } + + if (!session->is_session_obss_offload_enabled) { + pe_debug("obss offload protectiond disabled, session %d", + session->smeSessionId); + /* Send success */ + return QDF_STATUS_SUCCESS; + } + + if (session->gLimProtectionControl == + MLME_FORCE_POLICY_PROTECTION_DISABLE) { + pe_debug("protectiond disabled, force from policy, session %d", + session->smeSessionId); + /* Send success */ + return QDF_STATUS_SUCCESS; + } + + status = lim_obss_generate_detection_config(mac_ctx, + session, + &obss_cfg); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Failed to generate obss detection cfg, session %d", + session->smeSessionId); + return status; + } + + if (qdf_mem_cmp(&session->obss_offload_cfg, &obss_cfg, sizeof(obss_cfg)) + || force) { + struct scheduler_msg msg = {0}; + req_param = qdf_mem_malloc(sizeof(*req_param)); + if (!req_param) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy(&session->obss_offload_cfg, &obss_cfg, + sizeof(obss_cfg)); + req_param->vdev_id = session->smeSessionId; + req_param->obss_detect_period_ms = OBSS_DETECTION_PERIOD_MS; + req_param->obss_11b_ap_detect_mode = + obss_cfg.obss_11b_ap_detect_mode; + req_param->obss_11b_sta_detect_mode = + obss_cfg.obss_11b_sta_detect_mode; + req_param->obss_11g_ap_detect_mode = + obss_cfg.obss_11g_ap_detect_mode; + req_param->obss_11a_detect_mode = + obss_cfg.obss_11a_detect_mode; + req_param->obss_ht_legacy_detect_mode = + obss_cfg.obss_ht_legacy_detect_mode; + req_param->obss_ht_20mhz_detect_mode = + obss_cfg.obss_ht_20mhz_detect_mode; + req_param->obss_ht_mixed_detect_mode = + obss_cfg.obss_ht_mixed_detect_mode; + + msg.type = WMA_OBSS_DETECTION_REQ; + msg.bodyptr = req_param; + msg.reserved = 0; + status = scheduler_post_message(QDF_MODULE_ID_PE, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(req_param); + return status; + } + } else { + pe_debug("Skiping WMA_OBSS_DETECTION_REQ, force = %d", force); + } + + return status; +} + +QDF_STATUS lim_process_obss_detection_ind(struct mac_context *mac_ctx, + struct wmi_obss_detect_info + *obss_detection) +{ + QDF_STATUS status; + uint32_t detect_masks; + uint32_t reason; + struct obss_detection_cfg *obss_cfg; + bool enable; + struct pe_session *session; + tUpdateBeaconParams bcn_prm; + enum reg_wifi_band rf_band = REG_BAND_UNKNOWN; + struct obss_detection_cfg *cur_detect; + + pe_debug("obss detect ind id %d, reason %d, msk 0x%x, " QDF_MAC_ADDR_FMT, + obss_detection->vdev_id, obss_detection->reason, + obss_detection->matched_detection_masks, + QDF_MAC_ADDR_REF(obss_detection->matched_bssid_addr)); + + session = pe_find_session_by_vdev_id(mac_ctx, obss_detection->vdev_id); + if (!session) { + pe_err("Failed to get session for id %d", + obss_detection->vdev_id); + return QDF_STATUS_E_INVAL; + } + + if (!LIM_IS_AP_ROLE(session)) { + pe_err("session %d is not AP", obss_detection->vdev_id); + return QDF_STATUS_E_INVAL; + } + + if (!session->is_session_obss_offload_enabled) { + pe_err("Offload already disabled for session %d", + obss_detection->vdev_id); + return QDF_STATUS_SUCCESS; + } + + reason = obss_detection->reason; + detect_masks = obss_detection->matched_detection_masks; + + if (reason == OBSS_OFFLOAD_DETECTION_PRESENT) { + enable = true; + } else if (reason == OBSS_OFFLOAD_DETECTION_ABSENT) { + enable = false; + } else if (reason == OBSS_OFFLOAD_DETECTION_DISABLED) { + /* + * Most common reason for this event-type from firmware + * is insufficient memory. + * Disable offload OBSS detection and enable legacy-way + * of detecting OBSS by parsing beacons. + **/ + session->is_session_obss_offload_enabled = false; + pe_err("FW indicated obss offload disabled"); + pe_err("Enabling host based detection, session %d", + obss_detection->vdev_id); + + status = qdf_mc_timer_start(&session-> + protection_fields_reset_timer, + SCH_PROTECTION_RESET_TIME); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("cannot start protection reset timer"); + + return QDF_STATUS_SUCCESS; + } else { + pe_err("Invalid reason %d, session %d", + obss_detection->reason, + obss_detection->vdev_id); + return QDF_STATUS_E_INVAL; + } + + rf_band = session->limRFBand; + qdf_mem_zero(&bcn_prm, sizeof(bcn_prm)); + obss_cfg = &session->obss_offload_cfg; + cur_detect = &session->current_obss_detection; + + if (OBSS_DETECTION_IS_11B_AP(detect_masks)) { + if (reason != obss_cfg->obss_11b_ap_detect_mode || + rf_band != REG_BAND_2G) + goto wrong_detection; + + lim_enable11g_protection(mac_ctx, enable, true, + &bcn_prm, session); + cur_detect->obss_11b_ap_detect_mode = reason; + } + if (OBSS_DETECTION_IS_11B_STA(detect_masks)) { + if (reason != obss_cfg->obss_11b_sta_detect_mode || + rf_band != REG_BAND_2G) + goto wrong_detection; + + lim_enable11g_protection(mac_ctx, enable, true, + &bcn_prm, session); + cur_detect->obss_11b_sta_detect_mode = reason; + } + if (OBSS_DETECTION_IS_11G_AP(detect_masks)) { + if (reason != obss_cfg->obss_11g_ap_detect_mode || + rf_band != REG_BAND_2G) + goto wrong_detection; + + lim_enable_ht_protection_from11g(mac_ctx, enable, true, + &bcn_prm, session); + cur_detect->obss_11g_ap_detect_mode = reason; + } + if (OBSS_DETECTION_IS_11A(detect_masks)) { + if (reason != obss_cfg->obss_11a_detect_mode || + rf_band != REG_BAND_5G) + goto wrong_detection; + + lim_update_11a_protection(mac_ctx, enable, true, + &bcn_prm, session); + cur_detect->obss_11a_detect_mode = reason; + } + if (OBSS_DETECTION_IS_HT_LEGACY(detect_masks)) { + /* for 5GHz, we have only 11a detection, which covers legacy */ + if (reason != obss_cfg->obss_ht_legacy_detect_mode || + rf_band != REG_BAND_2G) + goto wrong_detection; + + lim_enable_ht_protection_from11g(mac_ctx, enable, true, + &bcn_prm, session); + cur_detect->obss_ht_legacy_detect_mode = reason; + } + if (OBSS_DETECTION_IS_HT_MIXED(detect_masks)) { + /* for 5GHz, we have only 11a detection, which covers ht mix */ + if (reason != obss_cfg->obss_ht_mixed_detect_mode || + rf_band != REG_BAND_2G) + goto wrong_detection; + + lim_enable_ht_protection_from11g(mac_ctx, enable, true, + &bcn_prm, session); + cur_detect->obss_ht_mixed_detect_mode = reason; + } + if (OBSS_DETECTION_IS_HT_20MHZ(detect_masks)) { + if (reason != obss_cfg->obss_ht_20mhz_detect_mode) + goto wrong_detection; + + lim_enable_ht20_protection(mac_ctx, enable, true, + &bcn_prm, session); + cur_detect->obss_ht_20mhz_detect_mode = reason; + } + + if ((false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running) && + bcn_prm.paramChangeBitmap) { + /* Update the bcn and apply the new settings to HAL */ + sch_set_fixed_beacon_fields(mac_ctx, session); + pe_debug("Beacon for PE session: %d got changed: 0x%x", + session->smeSessionId, bcn_prm.paramChangeBitmap); + if (!QDF_IS_STATUS_SUCCESS(lim_send_beacon_params( + mac_ctx, &bcn_prm, session))) { + pe_err("Failed to send beacon param, session %d", + obss_detection->vdev_id); + return QDF_STATUS_E_FAULT; + } + } + + status = lim_obss_send_detection_cfg(mac_ctx, session, true); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Failed to send obss detection cfg, session %d", + obss_detection->vdev_id); + return status; + } + + return QDF_STATUS_SUCCESS; + +wrong_detection: + /* + * We may get this wrong detection before FW can update latest cfg, + * So keeping log level debug + **/ + pe_debug("Wrong detection, session %d", obss_detection->vdev_id); + + return QDF_STATUS_E_INVAL; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_message.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_message.c new file mode 100644 index 0000000000000000000000000000000000000000..5582cd546f4946366e64b5c7c33213ff9f4d3539 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_message.c @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cds_api.h" +#include "sir_common.h" + +#include "wni_cfg.h" +#include "ani_global.h" +#include "lim_api.h" +#include "lim_send_messages.h" + +#include "sch_api.h" +#include "wlan_mlme_api.h" + +/* / Minimum beacon interval allowed (in Kus) */ +#define SCH_BEACON_INTERVAL_MIN 10 + +/* / Maximum beacon interval allowed (in Kus) */ +#define SCH_BEACON_INTERVAL_MAX 10000 + +/* / convert the CW values into a uint16_t */ +#define GET_CW(pCw) ((uint16_t) ((*(pCw) << 8) + *((pCw) + 1))) + +/* local functions */ +static QDF_STATUS +get_wmm_local_params(struct mac_context *mac, + uint32_t params[][CFG_EDCA_DATA_LEN]); +static void +set_sch_edca_params(struct mac_context *mac, + uint32_t params[][CFG_EDCA_DATA_LEN], + struct pe_session *pe_session); + +/* -------------------------------------------------------------------- */ +/** + * sch_set_beacon_interval + * + * FUNCTION: + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param None + * @return None + */ + +void sch_set_beacon_interval(struct mac_context *mac, + struct pe_session *pe_session) +{ + uint32_t bi; + + bi = pe_session->beaconParams.beaconInterval; + + if (bi < SCH_BEACON_INTERVAL_MIN || bi > SCH_BEACON_INTERVAL_MAX) { + pe_debug("Invalid beacon interval %d (should be [%d,%d]", bi, + SCH_BEACON_INTERVAL_MIN, SCH_BEACON_INTERVAL_MAX); + return; + } + + mac->sch.beacon_interval = (uint16_t) bi; +} + +void sch_edca_profile_update_all(struct mac_context *pmac) +{ + uint32_t i; + struct pe_session *psession_entry; + + for (i = 0; i < pmac->lim.maxBssId; i++) { + psession_entry = &pmac->lim.gpSession[i]; + if (psession_entry->valid) + sch_edca_profile_update(pmac, psession_entry); + } +} + +/** + * sch_get_params() - get the local or broadcast parameters based on the profile + * sepcified in the config params are delivered in this order: BE, BK, VI, VO + */ +static QDF_STATUS +sch_get_params(struct mac_context *mac, + uint32_t params[][CFG_EDCA_DATA_LEN], + uint8_t local) +{ + uint32_t val; + uint32_t i, idx; + uint32_t *prf; + struct wlan_mlme_edca_params *edca_params; + QDF_STATUS status; + uint8_t country_code_str[CFG_COUNTRY_CODE_LEN]; + uint32_t country_code_len = CFG_COUNTRY_CODE_LEN; + uint32_t ani_l[] = {edca_ani_acbe_local, edca_ani_acbk_local, + edca_ani_acvi_local, edca_ani_acvo_local}; + + uint32_t wme_l[] = {edca_wme_acbe_local, edca_wme_acbk_local, + edca_wme_acvi_local, edca_wme_acvo_local}; + + uint32_t etsi_l[] = {edca_etsi_acbe_local, edca_etsi_acbk_local, + edca_etsi_acvi_local, edca_etsi_acvo_local}; + + uint32_t ani_b[] = {edca_ani_acbe_bcast, edca_ani_acbk_bcast, + edca_ani_acvi_bcast, edca_ani_acvo_bcast}; + + uint32_t wme_b[] = {edca_wme_acbe_bcast, edca_wme_acbk_bcast, + edca_wme_acvi_bcast, edca_wme_acvo_bcast}; + + uint32_t etsi_b[] = {edca_etsi_acbe_bcast, edca_etsi_acbk_bcast, + edca_etsi_acvi_bcast, edca_etsi_acvo_bcast}; + edca_params = &mac->mlme_cfg->edca_params; + + country_code_len = (uint32_t)mac->mlme_cfg->reg.country_code_len; + qdf_mem_copy(country_code_str, mac->mlme_cfg->reg.country_code, + country_code_len); + if (cds_is_etsi_europe_country(country_code_str)) { + val = WNI_CFG_EDCA_PROFILE_ETSI_EUROPE; + pe_debug("switch to ETSI EUROPE profile country code %c%c", + country_code_str[0], country_code_str[1]); + } else { + val = mac->mlme_cfg->wmm_params.edca_profile; + } + if (val >= WNI_CFG_EDCA_PROFILE_MAX) { + pe_warn("Invalid EDCA_PROFILE %d, using %d instead", val, + WNI_CFG_EDCA_PROFILE_ANI); + val = WNI_CFG_EDCA_PROFILE_ANI; + } + + pe_debug("EdcaProfile: Using %d (%s)", val, + ((val == WNI_CFG_EDCA_PROFILE_WMM) ? "WMM" : "HiPerf")); + + if (local) { + switch (val) { + case WNI_CFG_EDCA_PROFILE_WMM: + prf = &wme_l[0]; + break; + case WNI_CFG_EDCA_PROFILE_ETSI_EUROPE: + prf = &etsi_l[0]; + break; + case WNI_CFG_EDCA_PROFILE_ANI: + default: + prf = &ani_l[0]; + break; + } + } else { + switch (val) { + case WNI_CFG_EDCA_PROFILE_WMM: + prf = &wme_b[0]; + break; + case WNI_CFG_EDCA_PROFILE_ETSI_EUROPE: + prf = &etsi_b[0]; + break; + case WNI_CFG_EDCA_PROFILE_ANI: + default: + prf = &ani_b[0]; + break; + } + } + + for (i = 0; i < 4; i++) { + uint8_t data[CFG_EDCA_DATA_LEN]; + + status = wlan_mlme_get_edca_params(edca_params, + (uint8_t *)&data[0], + (uint8_t)prf[i]); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Get failed for ac:%d", i); + return QDF_STATUS_E_FAILURE; + } + + for (idx = 0; idx < CFG_EDCA_DATA_LEN; idx++) + params[i][idx] = (uint32_t) data[idx]; + } + pe_debug("GetParams: local=%d, profile = %d Done", local, val); + + return QDF_STATUS_SUCCESS; +} + +/** + * broadcast_wmm_of_concurrent_sta_session() - broadcasts wmm info + * @mac_ctx: mac global context + * @session: pesession entry + * + * Return: true if wmm param updated, false if wmm param not updated + */ +static bool +broadcast_wmm_of_concurrent_sta_session(struct mac_context *mac_ctx, + struct pe_session *session) +{ + uint8_t i, j; + struct pe_session *concurrent_session = NULL; + + for (i = 0; i < mac_ctx->lim.maxBssId; i++) { + /* + * Find another INFRA STA AP session on same operating channel. + * The session entry passed to this API is for GO/SoftAP session + * that is getting added currently + */ + if (!((mac_ctx->lim.gpSession[i].valid == true) && + (mac_ctx->lim.gpSession[i].peSessionId != + session->peSessionId) && + (mac_ctx->lim.gpSession[i].curr_op_freq == + session->curr_op_freq) && + (mac_ctx->lim.gpSession[i].limSystemRole == + eLIM_STA_ROLE))) + continue; + + concurrent_session = &(mac_ctx->lim.gpSession[i]); + break; + } + + if (!concurrent_session) + return false; + + if (!qdf_mem_cmp(session->gLimEdcaParamsBC, + concurrent_session->gLimEdcaParams, + sizeof(concurrent_session->gLimEdcaParams))) + return false; + + /* + * Once atleast one concurrent session on same channel is found and WMM + * broadcast params for current SoftAP/GO session updated, return + */ + for (j = 0; j < QCA_WLAN_AC_ALL; j++) { + session->gLimEdcaParamsBC[j].aci.acm = + concurrent_session->gLimEdcaParams[j].aci.acm; + session->gLimEdcaParamsBC[j].aci.aifsn = + concurrent_session->gLimEdcaParams[j].aci.aifsn; + session->gLimEdcaParamsBC[j].cw.min = + concurrent_session->gLimEdcaParams[j].cw.min; + session->gLimEdcaParamsBC[j].cw.max = + concurrent_session->gLimEdcaParams[j].cw.max; + session->gLimEdcaParamsBC[j].txoplimit = + concurrent_session->gLimEdcaParams[j].txoplimit; + pe_debug("QoSUpdateBCast changed again due to concurrent INFRA STA session: AC :%d: AIFSN: %d, ACM %d, CWmin %d, CWmax %d, TxOp %d", + j, session->gLimEdcaParamsBC[j].aci.aifsn, + session->gLimEdcaParamsBC[j].aci.acm, + session->gLimEdcaParamsBC[j].cw.min, + session->gLimEdcaParamsBC[j].cw.max, + session->gLimEdcaParamsBC[j].txoplimit); + } + return true; +} + +void sch_qos_update_broadcast(struct mac_context *mac, struct pe_session *pe_session) +{ + uint32_t params[4][CFG_EDCA_DATA_LEN]; + uint32_t cwminidx, cwmaxidx, txopidx; + uint32_t phyMode; + uint8_t i; + bool updated = false; + QDF_STATUS status; + + if (sch_get_params(mac, params, false) != QDF_STATUS_SUCCESS) { + pe_debug("QosUpdateBroadcast: failed"); + return; + } + lim_get_phy_mode(mac, &phyMode, pe_session); + + pe_debug("QosUpdBcast: mode %d", phyMode); + + if (phyMode == WNI_CFG_PHY_MODE_11G) { + cwminidx = CFG_EDCA_PROFILE_CWMING_IDX; + cwmaxidx = CFG_EDCA_PROFILE_CWMAXG_IDX; + txopidx = CFG_EDCA_PROFILE_TXOPG_IDX; + } else if (phyMode == WNI_CFG_PHY_MODE_11B) { + cwminidx = CFG_EDCA_PROFILE_CWMINB_IDX; + cwmaxidx = CFG_EDCA_PROFILE_CWMAXB_IDX; + txopidx = CFG_EDCA_PROFILE_TXOPB_IDX; + } else { + /* This can happen if mode is not set yet, assume 11a mode */ + cwminidx = CFG_EDCA_PROFILE_CWMINA_IDX; + cwmaxidx = CFG_EDCA_PROFILE_CWMAXA_IDX; + txopidx = CFG_EDCA_PROFILE_TXOPA_IDX; + } + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + if (pe_session->gLimEdcaParamsBC[i].aci.acm != + (uint8_t)params[i][CFG_EDCA_PROFILE_ACM_IDX]) { + pe_session->gLimEdcaParamsBC[i].aci.acm = + (uint8_t)params[i][CFG_EDCA_PROFILE_ACM_IDX]; + updated = true; + } + if (pe_session->gLimEdcaParamsBC[i].aci.aifsn != + (uint8_t)params[i][CFG_EDCA_PROFILE_AIFSN_IDX]) { + pe_session->gLimEdcaParamsBC[i].aci.aifsn = + (uint8_t)params[i][CFG_EDCA_PROFILE_AIFSN_IDX]; + updated = true; + } + if (pe_session->gLimEdcaParamsBC[i].cw.min != + convert_cw(GET_CW(¶ms[i][cwminidx]))) { + pe_session->gLimEdcaParamsBC[i].cw.min = + convert_cw(GET_CW(¶ms[i][cwminidx])); + updated = true; + } + if (pe_session->gLimEdcaParamsBC[i].cw.max != + convert_cw(GET_CW(¶ms[i][cwmaxidx]))) { + pe_session->gLimEdcaParamsBC[i].cw.max = + convert_cw(GET_CW(¶ms[i][cwmaxidx])); + updated = true; + } + if (pe_session->gLimEdcaParamsBC[i].txoplimit != + (uint16_t)params[i][txopidx]) { + pe_session->gLimEdcaParamsBC[i].txoplimit = + (uint16_t)params[i][txopidx]; + updated = true; + } + + pe_debug("QoSUpdateBCast: AC :%d: AIFSN: %d, ACM %d, CWmin %d, CWmax %d, TxOp %d", + i, pe_session->gLimEdcaParamsBC[i].aci.aifsn, + pe_session->gLimEdcaParamsBC[i].aci.acm, + pe_session->gLimEdcaParamsBC[i].cw.min, + pe_session->gLimEdcaParamsBC[i].cw.max, + pe_session->gLimEdcaParamsBC[i].txoplimit); + + } + + /* + * If there exists a concurrent STA-AP session, use its WMM + * params to broadcast in beacons. WFA Wifi Direct test plan + * 6.1.14 requirement + */ + if (broadcast_wmm_of_concurrent_sta_session(mac, pe_session)) + updated = true; + if (updated) + pe_session->gLimEdcaParamSetCount++; + + status = sch_set_fixed_beacon_fields(mac, pe_session); + if (QDF_IS_STATUS_ERROR(status)) + pe_err("Unable to set beacon fields!"); +} + +void sch_qos_update_local(struct mac_context *mac, struct pe_session *pe_session) +{ + + uint32_t params[4][CFG_EDCA_DATA_LEN]; + QDF_STATUS status; + + status = sch_get_params(mac, params, true /*local */); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("sch_get_params(local) failed"); + return; + } + + set_sch_edca_params(mac, params, pe_session); + + /* For AP, the bssID is stored in LIM Global context. */ + lim_send_edca_params(mac, pe_session->gLimEdcaParams, + pe_session->vdev_id, false); +} + +/** + * sch_set_default_edca_params() - This function sets the gLimEdcaParams to the + * default local wmm profile. + * @mac - Global mac context + * @pe_session - PE session + * + * return none + */ +void sch_set_default_edca_params(struct mac_context *mac, struct pe_session *pe_session) +{ + uint32_t params[4][CFG_EDCA_DATA_LEN]; + + if (get_wmm_local_params(mac, params) != QDF_STATUS_SUCCESS) { + pe_err("get_wmm_local_params() failed"); + return; + } + + set_sch_edca_params(mac, params, pe_session); + return; +} + +/** + * set_sch_edca_params() - This function fills in the gLimEdcaParams structure + * with the given edca params. + * @mac - global mac context + * @pe_session - PE session + * @params - EDCA parameters + * + * Return none + */ +static void +set_sch_edca_params(struct mac_context *mac, + uint32_t params[][CFG_EDCA_DATA_LEN], + struct pe_session *pe_session) +{ + uint32_t i; + uint32_t cwminidx, cwmaxidx, txopidx; + uint32_t phyMode; + + lim_get_phy_mode(mac, &phyMode, pe_session); + + pe_debug("lim_get_phy_mode() = %d", phyMode); + /* if (mac->lim.gLimPhyMode == WNI_CFG_PHY_MODE_11G) */ + if (phyMode == WNI_CFG_PHY_MODE_11G) { + cwminidx = CFG_EDCA_PROFILE_CWMING_IDX; + cwmaxidx = CFG_EDCA_PROFILE_CWMAXG_IDX; + txopidx = CFG_EDCA_PROFILE_TXOPG_IDX; + } + /* else if (mac->lim.gLimPhyMode == WNI_CFG_PHY_MODE_11B) */ + else if (phyMode == WNI_CFG_PHY_MODE_11B) { + cwminidx = CFG_EDCA_PROFILE_CWMINB_IDX; + cwmaxidx = CFG_EDCA_PROFILE_CWMAXB_IDX; + txopidx = CFG_EDCA_PROFILE_TXOPB_IDX; + } else { + /* This can happen if mode is not set yet, assume 11a mode */ + cwminidx = CFG_EDCA_PROFILE_CWMINA_IDX; + cwmaxidx = CFG_EDCA_PROFILE_CWMAXA_IDX; + txopidx = CFG_EDCA_PROFILE_TXOPA_IDX; + } + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + pe_session->gLimEdcaParams[i].aci.acm = + (uint8_t)params[i][CFG_EDCA_PROFILE_ACM_IDX]; + pe_session->gLimEdcaParams[i].aci.aifsn = + (uint8_t)params[i][CFG_EDCA_PROFILE_AIFSN_IDX]; + pe_session->gLimEdcaParams[i].cw.min = + convert_cw(GET_CW(¶ms[i][cwminidx])); + pe_session->gLimEdcaParams[i].cw.max = + convert_cw(GET_CW(¶ms[i][cwmaxidx])); + pe_session->gLimEdcaParams[i].txoplimit = + (uint16_t)params[i][txopidx]; + + pe_debug("AC :%d: AIFSN: %d, ACM %d, CWmin %d, CWmax %d, TxOp %d", + i, pe_session->gLimEdcaParams[i].aci.aifsn, + pe_session->gLimEdcaParams[i].aci.acm, + pe_session->gLimEdcaParams[i].cw.min, + pe_session->gLimEdcaParams[i].cw.max, + pe_session->gLimEdcaParams[i].txoplimit); + + } + return; +} + +/** + * get_wmm_local_params() - This function gets the WMM local edca parameters. + * @mac + * @params[][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN] + * + * Return none + */ +static QDF_STATUS +get_wmm_local_params(struct mac_context *mac_ctx, + uint32_t params[][CFG_EDCA_DATA_LEN]) +{ + uint32_t i, idx; + QDF_STATUS status; + struct wlan_mlme_edca_params *edca_params; + uint32_t wme_l[] = {edca_wme_acbe_local, edca_wme_acbk_local, + edca_wme_acvi_local, edca_wme_acvo_local}; + + if (!mac_ctx->mlme_cfg) { + pe_err("NULL mlme cfg"); + return QDF_STATUS_E_FAILURE; + } + + edca_params = &mac_ctx->mlme_cfg->edca_params; + for (i = 0; i < 4; i++) { + uint8_t data[CFG_EDCA_DATA_LEN]; + + status = wlan_mlme_get_edca_params(edca_params, + (uint8_t *)&data[0], + (uint8_t)wme_l[i]); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("Get failed for ac:[%d]", i); + return QDF_STATUS_E_FAILURE; + } + for (idx = 0; idx < CFG_EDCA_DATA_LEN; idx++) + params[i][idx] = (uint32_t) data[idx]; + } + return QDF_STATUS_SUCCESS; +} + +/** + * sch_edca_profile_update() - This function updates the local and broadcast + * EDCA params in the gLimEdcaParams structure. It also updates the + * edcaParamSetCount. + * + * @mac - global mac context + * + * Return none + */ +void sch_edca_profile_update(struct mac_context *mac, struct pe_session *pe_session) +{ + if (LIM_IS_AP_ROLE(pe_session) || + LIM_IS_IBSS_ROLE(pe_session)) { + sch_qos_update_local(mac, pe_session); + sch_qos_update_broadcast(mac, pe_session); + } +} + +/* -------------------------------------------------------------------- */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_sys_params.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_sys_params.h new file mode 100644 index 0000000000000000000000000000000000000000..e55fb03898b38c752f5529889418e61b67bfeea6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_sys_params.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011-2012, 2014, 2017 The Linux Foundation. + * All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file sch_sys_params.h contains scheduler parameter definitions + * + * Author: Sandesh Goel + * Date: 02/25/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#ifndef __SCH_SYS_PARAMS_H__ +#define __SCH_SYS_PARAMS_H__ + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/common/inc/wlan_qct_sys.h b/drivers/staging/qcacld-3.0/core/mac/src/sys/common/inc/wlan_qct_sys.h new file mode 100644 index 0000000000000000000000000000000000000000..12974587a334594671aed6ff5293450be3db8a0c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/common/inc/wlan_qct_sys.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(WLAN_QCT_SYS_H__) +#define WLAN_QCT_SYS_H__ + +/**=========================================================================== + + \file wlan_qct_sys.h + + \brief System module API + + ==========================================================================*/ + +/* $HEADER$ */ + +/*--------------------------------------------------------------------------- + Include files + -------------------------------------------------------------------------*/ +#include +#include +#include + +struct mac_context; + +/*--------------------------------------------------------------------------- + Preprocessor definitions and constants + -------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- + Type declarations + -------------------------------------------------------------------------*/ + +/** + * sys_rsp_cb() - SYS async resonse callback + * @user_data: context data for callback + * + * This is a protype for the callback function that SYS makes to various + * modules in the system. + * + * Return: None + */ +typedef void (*sys_rsp_cb)(void *user_data); + +/** + * sys_build_message_header() - to build the sys message header + * @msg_id: message id + * @msg: pointer to message context + * + * This function will initialize the SYS message header with the + * message type and any internal fields needed for a new SYS + * message. This function sets all but the message body, which is up + * to the caller to setup based on the specific message being built. + * + * NOTE: There are internal / reserved items in a SYS message that + * must be set correctly for the message to be recognized as a SYS + * message by the SYS message handlers. It is important for every SYS + * message to be setup / built / initialized through this function. + * + * Return: QDF_STATUS + */ +QDF_STATUS sys_build_message_header(SYS_MSG_ID msg_id, + struct scheduler_msg *msg); + +/** + * umac_stop() - send schedule message to mc thread to stop umac (sme and mac) + * + * Return: status of operation + */ +QDF_STATUS umac_stop(void); + +QDF_STATUS sys_mc_process_handler(struct scheduler_msg *msg); + +/** + * sys_process_mmh_msg() - api to process an mmh message + * @mac: pointer to mac context + * @msg: pointer to message + * + * This API is used to process an mmh message. + * + * NOTE WELL: Ownership of the @msg bodyptr, if present, is always + * transferred, and the caller must not attempt to dereference or free + * the bodyptr after invoking this API. + * + * Return: none + */ +void sys_process_mmh_msg(struct mac_context *mac, + struct scheduler_msg *msg); + +#endif /* WLAN_QCT_SYS_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/common/src/wlan_qct_sys.c b/drivers/staging/qcacld-3.0/core/mac/src/sys/common/src/wlan_qct_sys.c new file mode 100644 index 0000000000000000000000000000000000000000..2e9f8490c6eaf2a5aac44a29fafde4aca499b21e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/common/src/wlan_qct_sys.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include /* needed for tSirMbMsg */ +#include /* needed for SIR_... message types */ +#include /* needed for WNI_... message types */ +#include "ani_global.h" +#include "wma_types.h" +#include "sme_api.h" +#include "mac_init_api.h" +#include "qdf_trace.h" + +/* + * Cookie for SYS messages. Note that anyone posting a SYS Message + * has to write the COOKIE in the reserved field of the message. The + * SYS Module relies on this COOKIE + */ +#define SYS_MSG_COOKIE 0xFACE + +/* SYS stop timeout 30 seconds */ +#define SYS_STOP_TIMEOUT (30000) +static qdf_event_t g_stop_evt; + +QDF_STATUS sys_build_message_header(SYS_MSG_ID msg_id, + struct scheduler_msg *msg) +{ + msg->type = msg_id; + msg->reserved = SYS_MSG_COOKIE; + + return QDF_STATUS_SUCCESS; +} + +/** + * umac_stop_complete_cb() - a callback when system stop completes + * @user_data: pointer to user provided data context + * + * this callback is used once system stop is completed. + * + * Return: none + */ +#ifdef QDF_ENABLE_TRACING +static void umac_stop_complete_cb(void *user_data) +{ + qdf_event_t *stop_evt = (qdf_event_t *) user_data; + QDF_STATUS qdf_status = qdf_event_set(stop_evt); + + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); +} +#else +static void umac_stop_complete_cb(void *user_data) +{ + return; +} +#endif + +static inline QDF_STATUS umac_stop_flush_cb(struct scheduler_msg *msg) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * umac_stop() - To post stop message to system module + * + * This API is used post a stop message to system module + * + * Return: QDF_STATUS + */ +QDF_STATUS umac_stop(void) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct scheduler_msg umac_stop_msg; + + /* Initialize the stop event */ + qdf_status = qdf_event_create(&g_stop_evt); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + return qdf_status; + + /* post a message to SYS module in MC to stop SME and MAC */ + sys_build_message_header(SYS_MSG_ID_UMAC_STOP, &umac_stop_msg); + + /* Save the user callback and user data */ + umac_stop_msg.callback = umac_stop_complete_cb; + umac_stop_msg.bodyptr = (void *)&g_stop_evt; + umac_stop_msg.flush_callback = umac_stop_flush_cb; + + /* post the message.. */ + qdf_status = scheduler_post_message(QDF_MODULE_ID_SYS, + QDF_MODULE_ID_SYS, + QDF_MODULE_ID_SYS, &umac_stop_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_status = QDF_STATUS_E_BADMSG; + + qdf_status = qdf_wait_single_event(&g_stop_evt, SYS_STOP_TIMEOUT); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + + qdf_status = qdf_event_destroy(&g_stop_evt); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + + return qdf_status; +} + +/** + * sys_mc_process_msg() - to process system mc thread messages + * @pMsg: message pointer + * + * This API is used to process the message + * + * Return: QDF_STATUS + */ +static QDF_STATUS sys_mc_process_msg(struct scheduler_msg *pMsg) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + data_stall_detect_cb data_stall_detect_callback; + mac_handle_t mac_handle; + + if (!pMsg) { + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, + "%s: NULL pointer to struct scheduler_msg", __func__); + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + /* + * All 'new' SYS messages are identified by a cookie in the reserved + * field of the message as well as the message type. This prevents + * the possibility of overlap in the message types defined for new + * SYS messages with the 'legacy' message types. The legacy messages + * will not have this cookie in the reserved field + */ + if (SYS_MSG_COOKIE == pMsg->reserved) { + /* Process all the new SYS messages.. */ + switch (pMsg->type) { + case SYS_MSG_ID_UMAC_STOP: + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, + "Processing SYS MC STOP"); + mac_handle = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_handle) { + QDF_TRACE(QDF_MODULE_ID_SYS, + QDF_TRACE_LEVEL_ERROR, + "%s: Invalid mac_handle", __func__); + break; + } + qdf_status = sme_stop(mac_handle); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + qdf_status = mac_stop(mac_handle); + QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); + ((sys_rsp_cb) pMsg->callback)(pMsg->bodyptr); + qdf_status = QDF_STATUS_SUCCESS; + break; + + case SYS_MSG_ID_DATA_STALL_MSG: + data_stall_detect_callback = pMsg->callback; + if (data_stall_detect_callback) + data_stall_detect_callback(pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + break; + default: + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, + "Unknown message type msgType= %d [0x%08x]", + pMsg->type, pMsg->type); + break; + + } + } else { + QDF_TRACE(QDF_MODULE_ID_SYS, + QDF_TRACE_LEVEL_ERROR, + "Rx SYS unknown MC msgtype= %d [0x%08X]", + pMsg->type, pMsg->type); + QDF_ASSERT(0); + qdf_status = QDF_STATUS_E_BADMSG; + + if (pMsg->bodyptr) + qdf_mem_free(pMsg->bodyptr); + } + return qdf_status; +} + +QDF_STATUS sys_mc_process_handler(struct scheduler_msg *msg) +{ + return sys_mc_process_msg(msg); +} + +void sys_process_mmh_msg(struct mac_context *mac, struct scheduler_msg *msg) +{ + QDF_MODULE_ID dest_module = QDF_MODULE_ID_SYS; + + if (!msg) { + QDF_ASSERT(0); + return; + } + + switch (msg->type) { + case eWNI_SME_SYS_READY_IND: + /* Forward this message to the PE module */ + dest_module = QDF_MODULE_ID_PE; + break; + default: + if ((msg->type >= eWNI_SME_MSG_TYPES_BEGIN) && + (msg->type <= eWNI_SME_MSG_TYPES_END)) { + dest_module = QDF_MODULE_ID_SME; + break; + } + + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, + "Message of ID %d is not yet handled by SYS", + msg->type); + QDF_ASSERT(0); + } + + /* + * Post now the message to the appropriate module for handling + */ + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SYS, + QDF_MODULE_ID_SYS, + dest_module, + msg)) + qdf_mem_free(msg->bodyptr); +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/platform/inc/sys_wrapper.h b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/platform/inc/sys_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..15ce5340c60fdd26708faa2a36582f3ee59d1146 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/platform/inc/sys_wrapper.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * @file VossWrapper.h + * + * @brief This header file contains the various structure definitions and + * function prototypes for the RTOS abstraction layer, implemented for VOSS + */ + +#ifndef __VOSS_WRAPPER_H +#define __VOSS_WRAPPER_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/*--------------------------------------------------------------------------- + * Include Files + * ------------------------------------------------------------------------*/ + +#include "sir_types.h" +#include "sir_params.h" +#include "sys_def.h" +#include "qdf_mc_timer.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "qdf_mem.h" + +/* Interlocked Compare Exchange related definitions */ + +/* Define basic constants for the ThreadX kernel. */ + +#define TX_AUTO_ACTIVATE 1 +#define TX_NO_ACTIVATE 0 + +/* API return values. */ +#define TX_SUCCESS 0x00 +#define TX_QUEUE_FULL 0x01 +/* ... */ +#define TX_NO_INSTANCE 0x0D +/* ... */ +#define TX_TIMER_ERROR 0x15 +#define TX_TICK_ERROR 0x16 +/* ... */ + +#ifndef true +#define true 1 +#endif + +#ifndef false +#define false 0 +#endif + +/* Following macro specifies the number of milliseconds which constitute 1 ThreadX timer tick. Used + for mimicking the ThreadX timer behaviour on VOSS. */ +/* Use the same MACRO used by firmware modules to calculate TICKs from mSec */ +/* Mismatch would cause worng timer value to be programmed */ +#define TX_MSECS_IN_1_TICK SYS_TICK_DUR_MS + +/* Signature with which the TX_TIMER struct is initialized, when the timer is created */ +#define TX_AIRGO_TMR_SIGNATURE 0xDEADBEEF + +#ifdef TIMER_MANAGER +#define tx_timer_create(a, b, c, d, e, f, g, h) tx_timer_create_intern_debug((void *)a, b, c, d, e, f, g, h, __FILE__, __LINE__) +#else +#define tx_timer_create(a, b, c, d, e, f, g, h) tx_timer_create_intern((void *)a, b, c, d, e, f, g, h) +#endif + +/*--------------------------------------------------------------------*/ +/* Timer structure */ +/* This structure is used to implement ThreadX timer facility. Just */ +/* like ThreadX, timer expiration handler executes at the highest */ +/* possible priority level, i.e. DISPATCH_LEVEL. */ +/*--------------------------------------------------------------------*/ +typedef struct TX_TIMER_STRUCT { +#ifdef WLAN_DEBUG +#define TIMER_MAX_NAME_LEN 50 + char timerName[TIMER_MAX_NAME_LEN]; +#endif + uint8_t sessionId; + uint32_t expireInput; + uint64_t tmrSignature; + void (*pExpireFunc)(void *, uint32_t); + uint64_t initScheduleTimeInMsecs; + uint64_t rescheduleTimeInMsecs; + qdf_mc_timer_t qdf_timer; + + /* Pointer to the MAC global structure, which stores the context for the NIC, */ + /* for which this timer is supposed to operate. */ + void *mac; + +} TX_TIMER; + +#define TX_TIMER_VALID(timer) (timer.mac != 0) + +uint32_t tx_timer_activate(TX_TIMER *); +uint32_t tx_timer_change(TX_TIMER *, uint64_t, uint64_t); +uint32_t tx_timer_change_context(TX_TIMER *, uint32_t); +#ifdef TIMER_MANAGER +uint32_t tx_timer_create_intern_debug(void *, TX_TIMER *, + char *, void (*)(void *, + uint32_t), + uint32_t, uint64_t, + uint64_t, uint64_t, + char *fileName, + uint32_t lineNum); +#else +uint32_t tx_timer_create_intern(void *, TX_TIMER *, char *, + void (*)(void *, uint32_t), + uint32_t, uint64_t, uint64_t, + uint64_t); +#endif +uint32_t tx_timer_deactivate(TX_TIMER *); +uint32_t tx_timer_delete(TX_TIMER *); +bool tx_timer_running(TX_TIMER *); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/platform/src/sys_wrapper.c b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/platform/src/sys_wrapper.c new file mode 100644 index 0000000000000000000000000000000000000000..144d26228982ce448d0517fa6d4fe8eedcce1c83 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/platform/src/sys_wrapper.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*=========================================================================== + @file VossWrapper.c + + @brief This source file contains the various function definitions for the + RTOS abstraction layer, implemented for VOSS + ===========================================================================*/ + +/*=========================================================================== + + EDIT HISTORY FOR FILE + + This section contains comments describing changes made to the module. + Notice that changes are listed in reverse chronological order. + + $Header:$ $DateTime: $ $Author: $ + + when who what, where, why + -------- --- -------------------------------------------------------- + 03/31/09 sho Remove the use of qdf_timerIsActive flag as it is not + thread-safe + 02/17/08 sho Fix the timer callback function to work when it is called + after the timer has stopped due to a race condition. + 02/10/08 sho Refactor the TX timer to use VOS timer directly instead + of using VOS utility timer + 12/15/08 sho Resolved errors and warnings from the AMSS compiler when + this is ported from WM + 11/20/08 sho Renamed this to VosWrapper.c; remove all dependencies on + WM platform and allow this to work on all VOSS enabled + platform + 06/24/08 tbh Modified the file to remove the dependecy on HDD files as + part of Gen6 bring up process. + 10/29/02 Neelay Das Created file. + + ===========================================================================*/ + +/*--------------------------------------------------------------------------- + * Include Files + * ------------------------------------------------------------------------*/ +#include "sys_wrapper.h" + +#ifdef WLAN_DEBUG +#define TIMER_NAME (timer_ptr->timerName) +#else +#define TIMER_NAME "N/A" +#endif + +/**--------------------------------------------------------------------- + * tx_timer_activate() + * + * FUNCTION: + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param + * + * @return TX_SUCCESS. + * + */ +uint32_t tx_timer_activate(TX_TIMER *timer_ptr) +{ + QDF_STATUS status; + + /* Uncomment the asserts, if the intention is to debug the occurrence of the */ + /* following anomalous cnditions. */ + + /* Assert that the timer structure pointer passed, is not NULL */ + /* dbgAssert(timer_ptr); */ + + /* If the NIC is halting just spoof a successful timer activation, so that all */ + /* the timers can be cleaned up. */ + + if (!timer_ptr) + return TX_TIMER_ERROR; + + /* Put a check for the free builds */ + if (TX_AIRGO_TMR_SIGNATURE != timer_ptr->tmrSignature) { + QDF_ASSERT(timer_ptr->tmrSignature == 0); + + return TX_TIMER_ERROR; + + } + /* Check for an uninitialized timer */ + QDF_ASSERT(0 != strlen(TIMER_NAME)); + + status = qdf_mc_timer_start(&timer_ptr->qdf_timer, + timer_ptr->initScheduleTimeInMsecs); + + if (QDF_STATUS_SUCCESS == status) { + return TX_SUCCESS; + } else if (QDF_STATUS_E_ALREADY == status) { + /* starting timer fails because timer is already started; this is okay */ + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_DEBUG, + "Timer %s is already running\n", TIMER_NAME); + return TX_SUCCESS; + } else { + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, + "Timer %s fails to activate\n", TIMER_NAME); + return TX_TIMER_ERROR; + } +} /*** tx_timer_activate() ***/ + +/**--------------------------------------------------------------------- + * tx_timer_change() + * + * FUNCTION: + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param + * + * @return TX_SUCCESS. + * + */ +uint32_t tx_timer_change(TX_TIMER *timer_ptr, + uint64_t initScheduleTimeInTicks, + uint64_t rescheduleTimeInTicks) +{ + /* Put a check for the free builds */ + if (TX_AIRGO_TMR_SIGNATURE != timer_ptr->tmrSignature) { + QDF_ASSERT(timer_ptr->tmrSignature == 0); + return TX_TIMER_ERROR; + } + /* changes cannot be applied until timer stops running */ + if (QDF_TIMER_STATE_STOPPED == + qdf_mc_timer_get_current_state(&timer_ptr->qdf_timer)) { + timer_ptr->initScheduleTimeInMsecs = + TX_MSECS_IN_1_TICK * initScheduleTimeInTicks; + timer_ptr->rescheduleTimeInMsecs = + TX_MSECS_IN_1_TICK * rescheduleTimeInTicks; + return TX_SUCCESS; + } else { + return TX_TIMER_ERROR; + } +} /*** tx_timer_change() ***/ + +/**--------------------------------------------------------------------- + * tx_timer_change_context() + * + * FUNCTION: + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param + * + * @return TX_SUCCESS. + * + */ +uint32_t tx_timer_change_context(TX_TIMER *timer_ptr, + uint32_t expiration_input) +{ + + /* Put a check for the free builds */ + if (TX_AIRGO_TMR_SIGNATURE != timer_ptr->tmrSignature) { + QDF_ASSERT(timer_ptr->tmrSignature == 0); + + return TX_TIMER_ERROR; + } + /* changes cannot be applied until timer stops running */ + if (QDF_TIMER_STATE_STOPPED == + qdf_mc_timer_get_current_state(&timer_ptr->qdf_timer)) { + timer_ptr->expireInput = expiration_input; + return TX_SUCCESS; + } else { + return TX_TIMER_ERROR; + } +} /*** tx_timer_change() ***/ + +/**--------------------------------------------------------------------- + * tx_main_timer_func() + * + * FUNCTION: + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param + * + * @return None. + * + */ +static void tx_main_timer_func(void *functionContext) +{ + TX_TIMER *timer_ptr = (TX_TIMER *) functionContext; + + if (!timer_ptr) { + QDF_ASSERT(0); + return; + } + + if (!timer_ptr->pExpireFunc) { + QDF_ASSERT(0); + return; + } + + /* Now call the actual timer function, taking the function pointer, */ + /* from the timer structure. */ + (*timer_ptr->pExpireFunc)(timer_ptr->mac, timer_ptr->expireInput); + + /* check if this needs to be rescheduled */ + if (0 != timer_ptr->rescheduleTimeInMsecs) { + QDF_STATUS status; + + status = qdf_mc_timer_start(&timer_ptr->qdf_timer, + timer_ptr->rescheduleTimeInMsecs); + timer_ptr->rescheduleTimeInMsecs = 0; + + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_WARN, + "Unable to reschedule timer %s; status=%d", + TIMER_NAME, status); + } + } +} /*** tx_timer_change() ***/ + +#ifdef TIMER_MANAGER +uint32_t tx_timer_create_intern_debug(void *pMacGlobal, + TX_TIMER *timer_ptr, char *name_ptr, + void (*expiration_function)(void *, + uint32_t), + uint32_t expiration_input, + uint64_t initScheduleTimeInTicks, + uint64_t rescheduleTimeInTicks, + uint64_t auto_activate, char *fileName, + uint32_t lineNum) +{ + QDF_STATUS status; + + if (!expiration_function) { + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, + "NULL timer expiration"); + QDF_ASSERT(0); + return TX_TIMER_ERROR; + } + + if (!name_ptr) { + + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, + "NULL name pointer for timer"); + QDF_ASSERT(0); + return TX_TIMER_ERROR; + } + if (!initScheduleTimeInTicks) + return TX_TICK_ERROR; + + if (!timer_ptr) + return TX_TIMER_ERROR; + + /* Initialize timer structure */ + timer_ptr->pExpireFunc = expiration_function; + timer_ptr->expireInput = expiration_input; + timer_ptr->initScheduleTimeInMsecs = + TX_MSECS_IN_1_TICK * initScheduleTimeInTicks; + timer_ptr->rescheduleTimeInMsecs = + TX_MSECS_IN_1_TICK * rescheduleTimeInTicks; + timer_ptr->mac = pMacGlobal; + + /* Set the flag indicating that the timer was created */ + timer_ptr->tmrSignature = TX_AIRGO_TMR_SIGNATURE; + +#ifdef WLAN_DEBUG + /* Store the timer name */ + strlcpy(timer_ptr->timerName, name_ptr, sizeof(timer_ptr->timerName)); +#endif /* Store the timer name, for Debug build only */ + + status = + qdf_mc_timer_init_debug(&timer_ptr->qdf_timer, QDF_TIMER_TYPE_SW, + tx_main_timer_func, (void *) timer_ptr, + fileName, lineNum); + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, + "Cannot create timer for %s\n", TIMER_NAME); + return TX_TIMER_ERROR; + } + + if (0 != rescheduleTimeInTicks) { + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_DEBUG, + "Creating periodic timer for %s\n", TIMER_NAME); + } + /* Activate this timer if required */ + if (auto_activate) { + tx_timer_activate(timer_ptr); + } + + return TX_SUCCESS; + +} /* ** tx_timer_create() *** / */ +#else +uint32_t tx_timer_create_intern(void *pMacGlobal, TX_TIMER *timer_ptr, + char *name_ptr, + void (*expiration_function)(void *, + uint32_t), + uint32_t expiration_input, + uint64_t initScheduleTimeInTicks, + uint64_t rescheduleTimeInTicks, + uint64_t auto_activate) +{ + QDF_STATUS status; + + if ((!name_ptr) || (!expiration_function)) + return TX_TIMER_ERROR; + + if (!initScheduleTimeInTicks) + return TX_TICK_ERROR; + + if (!timer_ptr) + return TX_TIMER_ERROR; + + /* Initialize timer structure */ + timer_ptr->pExpireFunc = expiration_function; + timer_ptr->expireInput = expiration_input; + timer_ptr->initScheduleTimeInMsecs = + TX_MSECS_IN_1_TICK * initScheduleTimeInTicks; + timer_ptr->rescheduleTimeInMsecs = + TX_MSECS_IN_1_TICK * rescheduleTimeInTicks; + timer_ptr->mac = pMacGlobal; + + /* Set the flag indicating that the timer was created */ + timer_ptr->tmrSignature = TX_AIRGO_TMR_SIGNATURE; + +#ifdef WLAN_DEBUG + /* Store the timer name */ + strlcpy(timer_ptr->timerName, name_ptr, sizeof(timer_ptr->timerName)); +#endif /* Store the timer name, for Debug build only */ + + status = qdf_mc_timer_init(&timer_ptr->qdf_timer, QDF_TIMER_TYPE_SW, + tx_main_timer_func, (void *) timer_ptr); + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, + "Cannot create timer for %s\n", TIMER_NAME); + return TX_TIMER_ERROR; + } + + if (0 != rescheduleTimeInTicks) { + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_INFO, + "Creating periodic timer for %s\n", TIMER_NAME); + } + /* Activate this timer if required */ + if (auto_activate) { + tx_timer_activate(timer_ptr); + } + + return TX_SUCCESS; + +} /* ** tx_timer_create() *** / */ +#endif + +/**--------------------------------------------------------------------- + * tx_timer_deactivate() + * + * FUNCTION: + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param + * + * @return TX_SUCCESS. + * + */ +uint32_t tx_timer_deactivate(TX_TIMER *timer_ptr) +{ + QDF_STATUS vStatus; + + /* Put a check for the free builds */ + if (TX_AIRGO_TMR_SIGNATURE != timer_ptr->tmrSignature) { + return TX_TIMER_ERROR; + } + /* if the timer is not running then we do not need to do anything here */ + vStatus = qdf_mc_timer_stop(&timer_ptr->qdf_timer); + if (QDF_STATUS_SUCCESS != vStatus) { + QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_INFO_HIGH, + "Unable to stop timer %s; status =%d\n", + TIMER_NAME, vStatus); + } + + return TX_SUCCESS; + +} /*** tx_timer_deactivate() ***/ + +uint32_t tx_timer_delete(TX_TIMER *timer_ptr) +{ + /* Put a check for the free builds */ + if (TX_AIRGO_TMR_SIGNATURE != timer_ptr->tmrSignature) { + return TX_TIMER_ERROR; + } + + qdf_mc_timer_destroy(&timer_ptr->qdf_timer); + timer_ptr->tmrSignature = 0; + + return TX_SUCCESS; +} /*** tx_timer_delete() ***/ + +/**--------------------------------------------------------------------- + * tx_timer_running() + * + * FUNCTION: + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param + * + * @return TX_SUCCESS. + * + */ +bool tx_timer_running(TX_TIMER *timer_ptr) +{ + /* Put a check for the free builds */ + if (TX_AIRGO_TMR_SIGNATURE != timer_ptr->tmrSignature) + return false; + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&timer_ptr->qdf_timer)) { + return true; + } + return false; + +} /*** tx_timer_running() ***/ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/inc/sys_def.h b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/inc/sys_def.h new file mode 100644 index 0000000000000000000000000000000000000000..640b25031f55f89589b7a1525b1df69fe2feafe8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/inc/sys_def.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011-2014, 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file sys_def.h contains the common definitions used to bring up + * Sirius system. + * Author: V. K. Kandarpa + * Date: 04/13/2002 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ + +#ifndef __SYSDEF_H +#define __SYSDEF_H + +/* / Sirius system level definitions */ +/* NOTE: Do not program system timer tick duration to less than 1msec */ + +/* / System timer tick duration in nanoseconds */ +#define SYS_TICK_DUR_NS 10000000 /* 10ms */ +#define SYS_TICK_TO_MICRO_SECOND 10000 + +/* / System timer tick duration in milliseconds */ +#define SYS_TICK_DUR_MS (SYS_TICK_DUR_NS/1000000) + +/* / Macro to convert MS to Ticks */ +#define SYS_MS_TO_TICKS(x) ((x) / SYS_TICK_DUR_MS) + +/* / MS to Time Units */ +#define SYS_MS_TO_TU(x) ((x * 1000) >> 10) + +/* / TU to MS */ +#define SYS_TU_TO_MS(x) ((x << 10) / 1000) + +#endif /* __SYSDEF_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/inc/sys_entry_func.h b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/inc/sys_entry_func.h new file mode 100644 index 0000000000000000000000000000000000000000..c3df87973ce5c6b9d2c3038f481f65c9efdfcfa8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/inc/sys_entry_func.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011-2012, 2014, 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file sys_entry_func.h contains module entry functions definitions + * Author: V. K. Kandarpa + * Date: 04/13/2002 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + */ +#ifndef __SYS_ENTRY_FUNC_H +#define __SYS_ENTRY_FUNC_H + +#include "ani_global.h" + +QDF_STATUS sys_init_globals(struct mac_context *mac); + +#endif /* __SYS_ENTRY_FUNC_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/inc/sys_startup.h b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/inc/sys_startup.h new file mode 100644 index 0000000000000000000000000000000000000000..e587b384fcd95b5810938182b72747f89d4d422a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/inc/sys_startup.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011-2014, 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * sys_startup.h: System startup header file. + * Author: V. K. Kandarpa + * Date: 01/29/2002 + * + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------------- + * + */ + +#ifndef __SYSSTARTUP_H +#define __SYSSTARTUP_H + +#include "sir_params.h" + +/* Defines */ + +/* Function */ + +/** + * sys_bbt_process_message_core() - to process BBT messages + * @mac_ctx: pointer to mac context + * @msg: message pointer + * @type: type of persona + * @subtype: subtype of persona + * + * This routine is to process some bbt messages + * + * Return: None + */ +QDF_STATUS sys_bbt_process_message_core(struct mac_context *mac_ctx, + struct scheduler_msg *msg, + uint32_t type, uint32_t subtype); + +#endif /* __SYSSTARTUP_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/src/mac_init_api.c b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/src/mac_init_api.c new file mode 100644 index 0000000000000000000000000000000000000000..7437189b1be6e12bc1741e35835b0a8bbeb1870f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/src/mac_init_api.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * mac_init_api.c - This file has all the mac level init functions + * for all the defined threads at system level. + * Author: Dinesh Upadhyay + * Date: 04/23/2007 + * History:- + * Date: 04/08/2008 Modified by: Santosh Mandiganal + * Modification Information: Code to allocate and free the memory for DumpTable entry. + * -------------------------------------------------------------------------- + * + */ +/* Standard include files */ +#include "lim_api.h" /* lim_cleanup */ +#include "sir_types.h" +#include "sys_entry_func.h" +#include "mac_init_api.h" +#include "wlan_mlme_main.h" +#include "wlan_psoc_mlme_api.h" + +#ifdef TRACE_RECORD +#include "mac_trace.h" +#endif + +#ifdef WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY +static struct mac_context *global_mac_context; + +static inline struct mac_context *mac_allocate_context_buffer(void) +{ + global_mac_context = qdf_mem_malloc(sizeof(*global_mac_context)); + + return global_mac_context; +} + +static inline void mac_free_context_buffer(void) +{ + qdf_mem_free(global_mac_context); + global_mac_context = NULL; +} +#else /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ +static struct mac_context global_mac_context; + +static inline struct mac_context *mac_allocate_context_buffer(void) +{ + return &global_mac_context; +} + +static inline void mac_free_context_buffer(void) +{ +} +#endif /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ + +QDF_STATUS mac_start(mac_handle_t mac_handle, + struct mac_start_params *params) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac || !params) { + QDF_ASSERT(0); + status = QDF_STATUS_E_FAILURE; + return status; + } + + mac->gDriverType = params->driver_type; + + if (ANI_DRIVER_TYPE(mac) != QDF_DRIVER_TYPE_MFG) + status = pe_start(mac); + + return status; +} + +QDF_STATUS mac_stop(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + pe_stop(mac); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS mac_open(struct wlan_objmgr_psoc *psoc, mac_handle_t *mac_handle, + hdd_handle_t hdd_handle, struct cds_config_info *cds_cfg) +{ + struct mac_context *mac; + QDF_STATUS status; + struct wlan_mlme_psoc_ext_obj *mlme_ext_obj; + + QDF_BUG(mac_handle); + if (!mac_handle) + return QDF_STATUS_E_FAILURE; + + mac = mac_allocate_context_buffer(); + if (!mac) + return QDF_STATUS_E_NOMEM; + + /* + * Set various global fields of mac here + * (Could be platform dependent as some variables in mac are platform + * dependent) + */ + mac->hdd_handle = hdd_handle; + + status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_LEGACY_MAC_ID); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("PSOC get ref failure"); + goto free_mac_context; + } + + mac->psoc = psoc; + mlme_ext_obj = wlan_psoc_mlme_get_ext_hdl(psoc); + if (!mlme_ext_obj) { + pe_err("Failed to get MLME Obj"); + status = QDF_STATUS_E_FAILURE; + goto release_psoc_ref; + } + mac->mlme_cfg = &mlme_ext_obj->cfg; + + *mac_handle = MAC_HANDLE(mac); + + /* For Non-FTM cases this value will be reset during mac_start */ + if (cds_cfg->driver_type) + mac->gDriverType = QDF_DRIVER_TYPE_MFG; + + sys_init_globals(mac); + + /* FW: 0 to 2047 and Host: 2048 to 4095 */ + mac->mgmtSeqNum = WLAN_HOST_SEQ_NUM_MIN - 1; + mac->he_sgi_ltf_cfg_bit_mask = DEF_HE_AUTO_SGI_LTF; + mac->is_usr_cfg_amsdu_enabled = true; + + status = pe_open(mac, cds_cfg); + if (QDF_IS_STATUS_ERROR(status)) { + pe_err("failed to open PE; status:%u", status); + goto release_psoc_ref; + } + + return QDF_STATUS_SUCCESS; + +release_psoc_ref: + wlan_objmgr_psoc_release_ref(psoc, WLAN_LEGACY_MAC_ID); + +free_mac_context: + mac_free_context_buffer(); + + return status; +} + +QDF_STATUS mac_close(mac_handle_t mac_handle) +{ + + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) + return QDF_STATUS_E_FAILURE; + + pe_close(mac); + + if (mac->pdev) { + wlan_objmgr_pdev_release_ref(mac->pdev, WLAN_LEGACY_MAC_ID); + mac->pdev = NULL; + } + wlan_objmgr_psoc_release_ref(mac->psoc, WLAN_LEGACY_MAC_ID); + mac->mlme_cfg = NULL; + mac->psoc = NULL; + qdf_mem_zero(mac, sizeof(*mac)); + mac_free_context_buffer(); + + return QDF_STATUS_SUCCESS; +} + +void mac_register_sesssion_open_close_cb(mac_handle_t mac_handle, + csr_session_close_cb close_session, + csr_roam_complete_cb callback) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + mac->session_close_cb = close_session; + mac->session_roam_complete_cb = callback; +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/src/sys_entry_func.c b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/src/sys_entry_func.c new file mode 100644 index 0000000000000000000000000000000000000000..c1457410fa7c8ec7df2e090b2547961d0f10a4c8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/system/src/sys_entry_func.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * sys_entry_func.cc - This file has all the system level entry functions + * for all the defined threads at system level. + * Author: V. K. Kandarpa + * Date: 01/16/2002 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------------- + * + */ +/* Standard include files */ + +/* Application Specific include files */ +#include "sir_common.h" +#include "ani_global.h" + +#include "lim_api.h" +#include "sch_api.h" +#include "utils_api.h" + +#include "sys_def.h" +#include "sys_entry_func.h" +#include "sys_startup.h" +#include "lim_trace.h" +#include "wma_types.h" +#include "qdf_types.h" +#include "cds_packet.h" + +/** + * sys_init_globals + * + * FUNCTION: + * Initializes system level global parameters + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param struct mac_context *Sirius software parameter struct pointer + * @return None + */ + +QDF_STATUS sys_init_globals(struct mac_context *mac) +{ + + qdf_mem_zero((uint8_t *) &mac->sys, sizeof(mac->sys)); + + mac->sys.gSysEnableLinkMonitorMode = 0; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sys_bbt_process_message_core(struct mac_context *mac_ctx, + struct scheduler_msg *msg, + uint32_t type, uint32_t subtype) +{ + uint32_t framecount; + QDF_STATUS ret; + void *bd_ptr; + tMgmtFrmDropReason dropreason; + cds_pkt_t *vos_pkt = (cds_pkt_t *) msg->bodyptr; + QDF_STATUS qdf_status = + wma_ds_peek_rx_packet_info(vos_pkt, &bd_ptr, false); + + mac_ctx->sys.gSysBbtReceived++; + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + goto fail; + + mac_ctx->sys.gSysFrameCount[type][subtype]++; + framecount = mac_ctx->sys.gSysFrameCount[type][subtype]; + + if (type == SIR_MAC_MGMT_FRAME) { + tpSirMacMgmtHdr mac_hdr; + + /* + * Drop beacon frames in deferred state to avoid VOSS run out of + * message wrappers. + */ + if ((subtype == SIR_MAC_MGMT_BEACON) && + !GET_LIM_PROCESS_DEFD_MESGS(mac_ctx)) { + pe_debug("dropping received beacon in deffered state"); + goto fail; + } + + dropreason = lim_is_pkt_candidate_for_drop(mac_ctx, bd_ptr, + subtype); + if (eMGMT_DROP_NO_DROP != dropreason) { + pe_debug("Mgmt Frame %d being dropped, reason: %d\n", + subtype, dropreason); + MTRACE(mac_trace(mac_ctx, + TRACE_CODE_RX_MGMT_DROP, NO_SESSION, + dropreason)); + goto fail; + } + + mac_hdr = WMA_GET_RX_MAC_HEADER(bd_ptr); + if (subtype == SIR_MAC_MGMT_ASSOC_REQ) { + pe_debug("ASSOC REQ frame allowed: da: " QDF_MAC_ADDR_FMT ", sa: " QDF_MAC_ADDR_FMT ", bssid: " QDF_MAC_ADDR_FMT ", Assoc Req count so far: %d", + QDF_MAC_ADDR_REF(mac_hdr->da), + QDF_MAC_ADDR_REF(mac_hdr->sa), + QDF_MAC_ADDR_REF(mac_hdr->bssId), + mac_ctx->sys.gSysFrameCount[type][subtype]); + } + if (subtype == SIR_MAC_MGMT_DEAUTH) { + pe_debug("DEAUTH frame allowed: da: " QDF_MAC_ADDR_FMT ", sa: " QDF_MAC_ADDR_FMT ", bssid: " QDF_MAC_ADDR_FMT ", DEAUTH count so far: %d", + QDF_MAC_ADDR_REF(mac_hdr->da), + QDF_MAC_ADDR_REF(mac_hdr->sa), + QDF_MAC_ADDR_REF(mac_hdr->bssId), + mac_ctx->sys.gSysFrameCount[type][subtype]); + } + if (subtype == SIR_MAC_MGMT_DISASSOC) { + pe_debug("DISASSOC frame allowed: da: " QDF_MAC_ADDR_FMT ", sa: " QDF_MAC_ADDR_FMT ", bssid: " QDF_MAC_ADDR_FMT ", DISASSOC count so far: %d", + QDF_MAC_ADDR_REF(mac_hdr->da), + QDF_MAC_ADDR_REF(mac_hdr->sa), + QDF_MAC_ADDR_REF(mac_hdr->bssId), + mac_ctx->sys.gSysFrameCount[type][subtype]); + } + + /* Post the message to PE Queue */ + ret = lim_post_msg_api(mac_ctx, msg); + if (ret != QDF_STATUS_SUCCESS) { + pe_err("posting to LIM2 failed, ret %d\n", ret); + goto fail; + } + mac_ctx->sys.gSysBbtPostedToLim++; +#ifdef FEATURE_WLAN_ESE + } else if (type == SIR_MAC_DATA_FRAME) { + pe_debug("IAPP Frame..."); + /* Post the message to PE Queue */ + ret = lim_post_msg_api(mac_ctx, msg); + if (ret != QDF_STATUS_SUCCESS) { + pe_err("posting to LIM2 failed, ret: %d", ret); + goto fail; + } + mac_ctx->sys.gSysBbtPostedToLim++; +#endif + } else { + pe_debug("BBT received Invalid type: %d subtype: %d " + "LIM state %X", type, subtype, + lim_get_sme_state(mac_ctx)); + goto fail; + } + return QDF_STATUS_SUCCESS; +fail: + mac_ctx->sys.gSysBbtDropped++; + return QDF_STATUS_E_FAILURE; +} + diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/inc/dot11fdefs.h b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/inc/dot11fdefs.h new file mode 100644 index 0000000000000000000000000000000000000000..2e38267cc3964ddda443931fb4b4ab3f12e5f057 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/inc/dot11fdefs.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011-2012, 2014-2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DOT11FDEFS_H_82A7B72E_C36C_465D_82A7_139EA5322582 +#define DOT11FDEFS_H_82A7B72E_C36C_465D_82A7_139EA5322582 +/** + * \file dot11fdefs.h + * + * \brief C defines customizing our framesc-generated code + * + * + * + * + * 'framesc' generates code written in terms of a number of macros + * intended for customization. + * + * + */ + +#include "parser_api.h" + +/* This controls how the "dot11f" code copies memory */ +#define DOT11F_MEMCPY(ctx, dst, src, len) \ + qdf_mem_copy((uint8_t *)(dst), (uint8_t *)(src), (len)) + +/* This controls how the "dot11f" code compares memory */ +#define DOT11F_MEMCMP(ctx, lhs, rhs, len) \ + (qdf_mem_cmp((uint8_t *)(lhs), (uint8_t *)(rhs), (len))) + +#if defined(DBG) && (DBG != 0) + +# /* define DOT11F_ENABLE_LOGGING */ +# /* define DOT11F_DUMP_FRAMES */ +#define DOT11F_LOG_GATE (4) +#define FRAMES_SEV_FOR_FRAME(ctx, sig) \ + (DOT11F_ASSOCREQUEST == (sig) ? 3 : 5) + +#if defined(DOT11F_ENABLE_LOGGING) + +#define DOT11F_HAVE_LOG_MACROS + +#define FRAMES_LOG0(ctx, sev, fmt) \ + QDF_TRACE(QDF_MODULE_ID_PE, (sev), (fmt)); + +#define FRAMES_LOG1(ctx, sev, fmt, p1) \ + QDF_TRACE(QDF_MODULE_ID_PE, (sev), (fmt), (p1)); + +#define FRAMES_LOG2(ctx, sev, fmt, p1, p2) \ + QDF_TRACE(QDF_MODULE_ID_PE, (sev), (fmt), (p1), (p2)); + +#define FRAMES_LOG3(ctx, sev, fmt, p1, p2, p3) \ + QDF_TRACE(QDF_MODULE_ID_PE, (sev), (fmt), (p1), (p2), (p3)); + +#define FRAMES_DUMP(ctx, sev, p, n) \ + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, (sev), (p), (n); + +#endif /* #if defined( DOT11F_ENABLE_LOGGING ) */ + +#else + +#undef DOT11F_ENABLE_LOGGING +#undef DOT11F_DUMP_FRAMES +#define DOT11F_LOG_GATE (1) + +#endif + +/* #define DOT11F_ENABLE_DBG_BREAK ( 1 ) */ + +/* Local Variables: */ +/* fill-column: 72 */ +/* indent-tabs-mode: nil */ +/* show-trailing-whitespace: t */ +/* End: */ + +#endif /* DOT11FDEFS_H_82A7B72E_C36C_465D_82A7_139EA5322582 */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/inc/utils_parser.h b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/inc/utils_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..4c4c8b8ecf8f3affa0727fd86281e9a0edadc19b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/inc/utils_parser.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file utils_parser.h contains the utility function protos + * used internally by the parser + * Author: Chandra Modumudi + * Date: 02/11/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ +#ifndef __UTILS_PARSE_H__ +#define __UTILS_PARSE_H__ + +#include +#include "sir_api.h" +#include "dot11f.h" +#include "utils_api.h" + +void convert_ssid(struct mac_context *, tSirMacSSid *, tDot11fIESSID *); +void convert_supp_rates(struct mac_context *, tSirMacRateSet *, tDot11fIESuppRates *); +void convert_fh_params(struct mac_context *, tSirMacFHParamSet *, + tDot11fIEFHParamSet *); +void convert_ext_supp_rates(struct mac_context *, tSirMacRateSet *, + tDot11fIEExtSuppRates *); +void convert_qos_caps(struct mac_context *, tSirMacQosCapabilityIE *, + tDot11fIEQOSCapsAp *); +void convert_qos_caps_station(struct mac_context *, tSirMacQosCapabilityStaIE *, + tDot11fIEQOSCapsStation *); +QDF_STATUS convert_wpa(struct mac_context *, tSirMacWpaInfo *, tDot11fIEWPA *); +QDF_STATUS convert_wpa_opaque(struct mac_context *, tSirMacWpaInfo *, + tDot11fIEWPAOpaque *); +QDF_STATUS convert_wapi_opaque(struct mac_context *, tSirMacWapiInfo *, + tDot11fIEWAPIOpaque *); +QDF_STATUS convert_rsn(struct mac_context *, tSirMacRsnInfo *, tDot11fIERSN *); +QDF_STATUS convert_rsn_opaque(struct mac_context *, tSirMacRsnInfo *, + tDot11fIERSNOpaque *); +void convert_power_caps(struct mac_context *, tSirMacPowerCapabilityIE *, + tDot11fIEPowerCaps *); +void convert_supp_channels(struct mac_context *, tSirMacSupportedChannelIE *, + tDot11fIESuppChannels *); +void convert_cf_params(struct mac_context *, tSirMacCfParamSet *, tDot11fIECFParams *); +void convert_tim(struct mac_context *, tSirMacTim *, tDot11fIETIM *); +void convert_country(struct mac_context *, tSirCountryInformation *, + tDot11fIECountry *); +void convert_wmm_params(struct mac_context *, tSirMacEdcaParamSetIE *, + tDot11fIEWMMParams *); +void convert_erp_info(struct mac_context *, tSirMacErpInfo *, tDot11fIEERPInfo *); +void convert_edca_param(struct mac_context *, tSirMacEdcaParamSetIE *, + tDot11fIEEDCAParamSet *); +void convert_mu_edca_param(struct mac_context * mac_ctx, + tSirMacEdcaParamSetIE *mu_edca, + tDot11fIEmu_edca_param_set *ie); +void convert_tspec(struct mac_context *, struct mac_tspec_ie *, + tDot11fIETSPEC *); +QDF_STATUS convert_tclas(struct mac_context *, tSirTclasInfo *, + tDot11fIETCLAS *); +void convert_wmmtspec(struct mac_context *, struct mac_tspec_ie *, + tDot11fIEWMMTSPEC *); +QDF_STATUS convert_wmmtclas(struct mac_context *, tSirTclasInfo *, + tDot11fIEWMMTCLAS *); +void convert_ts_delay(struct mac_context *, tSirMacTsDelayIE *, tDot11fIETSDelay *); +void convert_schedule(struct mac_context *, tSirMacScheduleIE *, tDot11fIESchedule *); +void convert_wmm_schedule(struct mac_context *, tSirMacScheduleIE *, + tDot11fIEWMMSchedule *); +QDF_STATUS convert_wsc_opaque(struct mac_context *, tSirAddie *, + tDot11fIEWscIEOpaque *); +QDF_STATUS convert_p2p_opaque(struct mac_context *, tSirAddie *, + tDot11fIEP2PIEOpaque *); +#ifdef WLAN_FEATURE_WFD +QDF_STATUS convert_wfd_opaque(struct mac_context *, tSirAddie *, + tDot11fIEWFDIEOpaque *); +#endif +void convert_qos_mapset_frame(struct mac_context *, struct qos_map_set *, + tDot11fIEQosMapSet *); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/dot11f.c b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/dot11f.c new file mode 100644 index 0000000000000000000000000000000000000000..4385cbba5e0d33011d970000bff02606e3616b48 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/dot11f.c @@ -0,0 +1,30134 @@ +/* + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * \file dot11f.c + * + * \brief Structures, functions & definitions for + * working with 802.11 Frames + * + * + * This file was automatically generated by 'framesc' + * Wed Sep 29 13:23:21 2021 from the following file(s): + * + * dot11f.frms + * + * PLEASE DON'T EDIT THIS FILE BY HAND! + * + */ + +#if !defined ANI_OS_TYPE_OSX && !defined ANI_OS_TYPE_LINUX && !defined ANI_OS_TYPE_ANDROID +#include /* For memcpy */ +#include /* For _vsnprintf */ +#include /* For offsetof */ +#endif + +#include +#include +#include "dot11fdefs.h" +#include "dot11f.h" + +#if defined(_MSC_VER) +#pragma warning (disable:4244) +#pragma warning (disable:4505) +#pragma warning (disable:4702) +#pragma warning (disable:4996)/* ... was declared deprecated */ +#endif /* Microsoft C/C++ */ + +typedef unsigned char tFRAMES_BOOL; +typedef void (*pfnGeneric_t)(void); + +typedef struct sFFDefn { + const char *name; + uint32_t offset; + uint16_t sig; + uint8_t size; +} tFFDefn; + +typedef struct sIEDefn { + uint32_t offset; + uint32_t presenceOffset; + uint32_t countOffset; + const char *name; + uint16_t arraybound; + uint16_t minSize; + uint16_t maxSize; + uint16_t sig; + unsigned char oui[5]; + unsigned char noui; + uint8_t eid; + uint8_t extn_eid; + tFRAMES_BOOL fMandatory; +} tIEDefn; + +#if !defined(countof) +#define countof(x) (sizeof((x)) / sizeof((x)[0])) +#endif + +#if !defined(DOT11F_MEMCPY) +#define DOT11F_MEMCPY(ctx, dst, src, len) \ + memcpy((dst), (src), (len)) +#endif + +#if !defined(DOT11F_MEMCMP) +#define DOT11F_MEMCMP(ctx, lhs, rhs, len) \ + memcmp((lhs), (rhs), (len)) +#endif + +#ifndef DOT11F_HAVE_LOG_SEVERITIES +#define FRLOG_OFF (0) +#define FRLOGP (1) +#define FRLOGE (2) +#define FRLOGW (3) +#define FRLOG1 (4) +#define FRLOG2 (5) +#define FRLOG3 (6) +#define FRLOG4 (7) +#endif + +#define FRFL(x) x + +#define FRAMES_LOG0(ctx, sev, fmt) +#define FRAMES_LOG1(ctx, sev, fmt, p1) +#define FRAMES_LOG2(ctx, sev, fmt, p1, p2) +#define FRAMES_LOG3(ctx, sev, fmt, p1, p2, p3) +#define FRAMES_LOG4(ctx, sev, fmt, p1, p2, p3, p4) +#define FRAMES_DUMP(ctx, sev, p, n) +#ifndef FRAMES_SEV_FOR_FRAME +#define FRAMES_SEV_FOR_FRAME(ctx, sig) FRLOG3 +#endif + +#if defined(DOT11F_ENABLE_DBG_BREAK) && defined (WIN32) +#define FRAMES_DBG_BREAK() { _asm int 3 } +#else +#define FRAMES_DBG_BREAK() +#endif + +#if !defined(DOT11F_PARAMETER_CHECK) +#if defined (DOT11F_HAVE_WIN32_API) + +#define DOT11F_PARAMETER_CHECK(pBuf, nBuf, pFrm, nFrm) \ + do { \ + if (!pBuf || IsBadReadPtr(pBuf, nBuf))\ + return DOT11F_BAD_INPUT_BUFFER; \ + if (!pFrm || IsBadWritePtr(pFrm, nFrm))\ + return DOT11F_BAD_OUTPUT_BUFFER; \ + } while (0) + +#define DOT11F_PARAMETER_CHECK2(pSrc, pBuf, nBuf, pnConsumed) \ + do { \ + if (!pSrc || IsBadReadPtr(pSrc, 4))\ + eturn DOT11F_BAD_INPUT_BUFFER; \ + if (!pBuf || IsBadWritePtr(pBuf, nBuf))\ + return DOT11F_BAD_OUTPUT_BUFFER; \ + if (!nBuf)\ + return DOT11F_BAD_OUTPUT_BUFFER; \ + if (IsBadWritePtr(pnConsumed, 4))\ + return DOT11F_BAD_OUTPUT_BUFFER; \ + } while (0) + +#else + +#define DOT11F_PARAMETER_CHECK(pBuf, nBuf, pFrm, nFrm) \ + if (!pBuf)\ + return DOT11F_BAD_INPUT_BUFFER; \ + if (!pFrm)\ + return DOT11F_BAD_OUTPUT_BUFFER \ + +#define DOT11F_PARAMETER_CHECK2(pSrc, pBuf, nBuf, pnConsumed) \ + if (!pSrc)\ + return DOT11F_BAD_INPUT_BUFFER; \ + if (!pBuf)\ + return DOT11F_BAD_OUTPUT_BUFFER; \ + if (!nBuf)\ + return DOT11F_BAD_OUTPUT_BUFFER; \ + if (!pnConsumed)\ + return DOT11F_BAD_OUTPUT_BUFFER \ + +#endif +#endif + +static void framesntohs(tpAniSirGlobal pCtx, + uint16_t *pOut, + uint8_t *pIn, + tFRAMES_BOOL fMsb) +{ + (void)pCtx; +#if defined (DOT11F_LITTLE_ENDIAN_HOST) + if (!fMsb) + DOT11F_MEMCPY(pCtx, (uint16_t *)pOut, pIn, 2); + else + *pOut = (uint16_t)(*pIn << 8) | *(pIn + 1); +#else + if (!fMsb) + *pOut = (uint16_t)(*pIn | (*(pIn + 1) << 8)); + else + DOT11F_MEMCPY(pCtx, (uint16_t *)pOut, pIn, 2); +#endif +} + +static void framesntohl(tpAniSirGlobal pCtx, + uint32_t *pOut, + uint8_t *pIn, + tFRAMES_BOOL fMsb) +{ + (void)pCtx; +#if defined (DOT11F_LITTLE_ENDIAN_HOST) + if (!fMsb) + DOT11F_MEMCPY(pCtx, (uint32_t *)pOut, pIn, 4); + else + *pOut = (uint32_t)(*pIn << 24) | + (*(pIn + 1) << 16) | + (*(pIn + 2) << 8) | + (*(pIn + 3)); +#else + if (!fMsb) + *pOut = (uint32_t)(*(pIn + 3) << 24) | + (*(pIn + 2) << 16) | + (*(pIn + 1) << 8) | + (*(pIn)); +else + *pOut = *(uint32_t *)pIn; +#endif +} + +static void framesntohq(tpAniSirGlobal pCtx, + tDOT11F_U64 *pOut, + uint8_t *pIn, + tFRAMES_BOOL fMsb) +{ +#if defined (DOT11F_LITTLE_ENDIAN_HOST) + framesntohl(pCtx, &((*pOut)[0]), pIn, fMsb); + framesntohl(pCtx, &((*pOut)[1]), pIn + 4, fMsb); +#else + framesntohl(pCtx, &((*pOut)[1]), pIn, fMsb); + framesntohl(pCtx, &((*pOut)[0]), pIn + 4, fMsb); +#endif +} + +static void frameshtons(tpAniSirGlobal pCtx, + uint8_t *pOut, + uint16_t pIn, + tFRAMES_BOOL fMsb) +{ + (void)pCtx; +#if defined (DOT11F_LITTLE_ENDIAN_HOST) + if (!fMsb) { + DOT11F_MEMCPY(pCtx, pOut, &pIn, 2); + } else { + *pOut = (pIn & 0xff00) >> 8; + *(pOut + 1) = pIn & 0xff; + } +#else + if (!fMsb) { + *pOut = pIn & 0xff; + *(pOut + 1) = (pIn & 0xff00) >> 8; + } else { + DOT11F_MEMCPY(pCtx, pOut, &pIn, 2); + } +#endif +} + +static void frameshtonl(tpAniSirGlobal pCtx, + uint8_t *pOut, + uint32_t pIn, + tFRAMES_BOOL fMsb) +{ + (void)pCtx; +#if defined (DOT11F_LITTLE_ENDIAN_HOST) + if (!fMsb) { + DOT11F_MEMCPY(pCtx, pOut, &pIn, 4); + } else { + *pOut = (pIn & 0xff000000) >> 24; + *(pOut + 1) = (pIn & 0x00ff0000) >> 16; + *(pOut + 2) = (pIn & 0x0000ff00) >> 8; + *(pOut + 3) = (pIn & 0x000000ff); + } +#else + if (!fMsb) { + *pOut = (pIn & 0x000000ff); + *(pOut + 1) = (pIn & 0x0000ff00) >> 8; + *(pOut + 2) = (pIn & 0x00ff0000) >> 16; + *(pOut + 3) = (pIn & 0xff000000) >> 24; + } else { + DOT11F_MEMCPY(pCtx, pOut, &pIn, 4); + } +#endif +} + +static void frameshtonq(tpAniSirGlobal pCtx, + uint8_t *pOut, + tDOT11F_U64 pIn, + tFRAMES_BOOL fMsb) +{ +#if defined (DOT11F_LITTLE_ENDIAN_HOST) + frameshtonl(pCtx, pOut, pIn[0], fMsb); + frameshtonl(pCtx, pOut + 4, pIn[1], fMsb); +#else + frameshtonl(pCtx, pOut + 4, pIn[1], fMsb); + frameshtonl(pCtx, pOut, pIn[0], fMsb); +#endif +} + +static const tIEDefn *find_ie_defn(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint32_t nBuf, + const tIEDefn IEs[]) +{ + const tIEDefn *pIe; + (void)pCtx; + + pIe = &(IEs[0]); + while (0xff != pIe->eid || pIe->extn_eid) { + if (*pBuf == pIe->eid) { + if (pIe->eid == 0xff) { + if ((nBuf > 2) && + (*(pBuf + 2)) == pIe->extn_eid) + return pIe; + } else { + if (0 == pIe->noui) + return pIe; + + if ((nBuf > (uint32_t)(pIe->noui + 2)) && + (!DOT11F_MEMCMP(pCtx, pBuf + 2, pIe->oui, + pIe->noui))) + return pIe; + } + } + + ++pIe; + } + + return NULL; +} + +static uint32_t get_container_ies_len(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint32_t nBuf, + uint8_t *pnConsumed, + const tIEDefn IEs[]) +{ + const tIEDefn *pIe, *pIeFirst; + uint8_t *pBufRemaining = pBuf; + uint32_t len = 0; + (void)pCtx; + + pIeFirst = &(IEs[0]); + + if (*pBufRemaining != pIeFirst->eid) + return DOT11F_INTERNAL_ERROR; + len += *(pBufRemaining+1); + pBufRemaining += len + 2; + len += 2; + while (len + 1 < nBuf) { + pIe = find_ie_defn(pCtx, pBufRemaining, nBuf - len, IEs); + if (NULL == pIe) + break; + if (pIe->eid == pIeFirst->eid) + break; + len += *(pBufRemaining + 1) + 2; + pBufRemaining += *(pBufRemaining + 1) + 2; + } + + if ((len > 0xFF) || (len > nBuf)) + return DOT11F_INTERNAL_ERROR; + *pnConsumed = len; + return DOT11F_PARSE_SUCCESS; + +} + + +static uint32_t unpack_core(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint32_t nBuf, + const tFFDefn FFs[], + const tIEDefn IEs[], + uint8_t *pFrm, + size_t nFrm, + bool append_ie); +static uint32_t pack_core(tpAniSirGlobal pCtx, + uint8_t *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed, + const tFFDefn FFs[], + const tIEDefn IEs[]); +static uint32_t get_packed_size_core(tpAniSirGlobal pCtx, + uint8_t *pFrm, + uint32_t *pnNeeded, + const tIEDefn IEs[]); + +static uint32_t dot11f_unpack_tlv_common_func(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint16_t tlvlen, + uint8_t *pDstPresent, uint8_t *pDstField) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)tlvlen; /* Shutup the compiler */ + + *pDstPresent = 1; + *pDstField = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_common_func. */ + +static uint32_t dot11f_unpack_tlv_common_func2(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint16_t tlvlen, + uint8_t *pDstPresent, uint16_t *pDstState) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)tlvlen; /* Shutup the compiler */ + + *pDstPresent = 1; + framesntohs(pCtx, pDstState, pBuf, 1); + (void)pCtx; + return status; + +} /* End dot11f_unpack_tlv_common_func2. */ + +static void dot11f_unpack_ff_common_func(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint16_t *pDstField) +{ + framesntohs(pCtx, pDstField, pBuf, 0); + (void)pCtx; +} /* End dot11f_unpack_ff_common_func. */ + +static uint32_t dot11f_unpack_ie_common_func(tpAniSirGlobal pCtx, uint8_t *pBuf, + uint8_t ielen, uint8_t *pDstPresent, + uint8_t *pDstField) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)ielen; + (void)pBuf; + if ((*pDstPresent)) + status = DOT11F_DUPLICATE_IE; + *pDstPresent = 1; + *pDstField = *pBuf; + (void)pCtx; + + return status; +} /* End dot11f_unpack_ie_common_func */ + +typedef struct sTLVDefn { + uint32_t offset; + uint32_t presenceOffset; + const char *name; + uint16_t sig; + uint32_t id; + uint32_t pec; + uint32_t minSize; + uint32_t maxSize; + uint8_t fMandatory; + uint8_t sType; + uint8_t sLen; + uint8_t fMsb; +} tTLVDefn; + +static const tTLVDefn *find_tlv_defn(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint32_t nBuf, + const tTLVDefn TLVs[]) +{ + const tTLVDefn *pTlv; + uint32_t pec; + uint16_t id; + + pTlv = &(TLVs[0]); + (void)pCtx; + if (pTlv->sType == 2) + framesntohs(pCtx, &id, pBuf, 1); + else + id = *pBuf; + + while (0xffff != pTlv->id) { + if (id == pTlv->id) { + if (0 == pTlv->pec) + return pTlv; + + if (nBuf > 5) { + pec = ((*(pBuf + 4)) << 16) | + ((*(pBuf + 5)) << 8) | + *(pBuf + 6); + if (pec == pTlv->pec) + return pTlv; + } + } + + ++pTlv; + } + + return NULL; +} + +static uint32_t unpack_tlv_core(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint32_t nBuf, + const tTLVDefn TLVs[], + uint8_t *pFrm, + size_t nFrm); +static uint32_t pack_tlv_core(tpAniSirGlobal pCtx, + uint8_t *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed, + const tTLVDefn TLVs[], + uint32_t *pidx); +static uint32_t get_packed_size_tlv_core(tpAniSirGlobal pCtx, + uint8_t *pFrm, + uint32_t *pnNeeded, + const tTLVDefn TLVs[]); + +#define SigFfAID (0x0001) + +void dot11f_unpack_ff_action(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfAction *pDst) +{ + pDst->action = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_action. */ + +#define SigFfAction (0x0002) + +#define SigFfAuthAlgo (0x0003) + +#define SigFfAuthSeqNo (0x0004) + +#define SigFfBeaconInterval (0x0005) + +void dot11f_unpack_ff_capabilities(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfCapabilities *pDst) +{ + uint16_t tmp0__; + framesntohs(pCtx, &tmp0__, pBuf, 0); + pDst->ess = tmp0__ >> 0 & 0x1; + pDst->ibss = tmp0__ >> 1 & 0x1; + pDst->cfPollable = tmp0__ >> 2 & 0x1; + pDst->cfPollReq = tmp0__ >> 3 & 0x1; + pDst->privacy = tmp0__ >> 4 & 0x1; + pDst->shortPreamble = tmp0__ >> 5 & 0x1; + pDst->pbcc = tmp0__ >> 6 & 0x1; + pDst->channelAgility = tmp0__ >> 7 & 0x1; + pDst->spectrumMgt = tmp0__ >> 8 & 0x1; + pDst->qos = tmp0__ >> 9 & 0x1; + pDst->shortSlotTime = tmp0__ >> 10 & 0x1; + pDst->apsd = tmp0__ >> 11 & 0x1; + pDst->rrm = tmp0__ >> 12 & 0x1; + pDst->dsssOfdm = tmp0__ >> 13 & 0x1; + pDst->delayedBA = tmp0__ >> 14 & 0x1; + pDst->immediateBA = tmp0__ >> 15 & 0x1; + (void)pCtx; +} /* End dot11f_unpack_ff_capabilities. */ + +#define SigFfCapabilities (0x0006) + +void dot11f_unpack_ff_category(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfCategory *pDst) +{ + pDst->category = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_category. */ + +#define SigFfCategory (0x0007) + +void dot11f_unpack_ff_current_ap_address(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfCurrentAPAddress *pDst) +{ + DOT11F_MEMCPY(pCtx, pDst->mac, pBuf, 6); + (void)pCtx; +} /* End dot11f_unpack_ff_current_ap_address. */ + +#define SigFfCurrentAPAddress (0x0008) + +void dot11f_unpack_ff_dialog_token(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfDialogToken *pDst) +{ + pDst->token = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_dialog_token. */ + +#define SigFfDialogToken (0x0009) + +void dot11f_unpack_ff_link_margin(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfLinkMargin *pDst) +{ + pDst->linkMargin = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_link_margin. */ + +#define SigFfLinkMargin (0x000a) + +#define SigFfListenInterval (0x000b) + +void dot11f_unpack_ff_max_tx_power(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfMaxTxPower *pDst) +{ + pDst->maxTxPower = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_max_tx_power. */ + +#define SigFfMaxTxPower (0x000c) + +void dot11f_unpack_ff_num_of_repetitions(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfNumOfRepetitions *pDst) +{ + framesntohs(pCtx, &pDst->repetitions, pBuf, 0); + (void)pCtx; +} /* End dot11f_unpack_ff_num_of_repetitions. */ + +#define SigFfNumOfRepetitions (0x000d) + +void dot11f_unpack_ff_operating_mode(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfOperatingMode *pDst) +{ + uint8_t tmp1__; + tmp1__ = *pBuf; + pDst->chanWidth = tmp1__ >> 0 & 0x3; + pDst->reserved = tmp1__ >> 2 & 0x3; + pDst->rxNSS = tmp1__ >> 4 & 0x7; + pDst->rxNSSType = tmp1__ >> 7 & 0x1; + (void)pCtx; +} /* End dot11f_unpack_ff_operating_mode. */ + +#define SigFfOperatingMode (0x000e) + +void dot11f_unpack_ff_rcpi(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfRCPI *pDst) +{ + pDst->rcpi = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_rcpi. */ + +#define SigFfRCPI (0x000f) + +void dot11f_unpack_ff_rsni(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfRSNI *pDst) +{ + pDst->rsni = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_rsni. */ + +#define SigFfRSNI (0x0010) + +#define SigFfReason (0x0011) + +void dot11f_unpack_ff_rx_antenna_id(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfRxAntennaId *pDst) +{ + pDst->antennaId = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_rx_antenna_id. */ + +#define SigFfRxAntennaId (0x0012) + +void dot11f_unpack_ff_sm_power_mode_set(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfSMPowerModeSet *pDst) +{ + uint8_t tmp2__; + tmp2__ = *pBuf; + pDst->PowerSave_En = tmp2__ >> 0 & 0x1; + pDst->Mode = tmp2__ >> 1 & 0x1; + pDst->reserved = tmp2__ >> 2 & 0x3f; + (void)pCtx; +} /* End dot11f_unpack_ff_sm_power_mode_set. */ + +#define SigFfSMPowerModeSet (0x0013) + +#define SigFfStatus (0x0014) + +void dot11f_unpack_ff_status_code(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfStatusCode *pDst) +{ + pDst->statusCode = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_status_code. */ + +#define SigFfStatusCode (0x0015) + +void dot11f_unpack_ff_tpc_ele_id(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfTPCEleID *pDst) +{ + pDst->TPCId = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_tpc_ele_id. */ + +#define SigFfTPCEleID (0x0016) + +void dot11f_unpack_ff_tpc_ele_len(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfTPCEleLen *pDst) +{ + pDst->TPCLen = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_tpc_ele_len. */ + +#define SigFfTPCEleLen (0x0017) + +void dot11f_unpack_ff_ts_info(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfTSInfo *pDst) +{ + uint32_t tmp3__; + framesntohl(pCtx, &tmp3__, pBuf, 0); + pDst->traffic_type = tmp3__ >> 0 & 0x1; + pDst->tsid = tmp3__ >> 1 & 0xf; + pDst->direction = tmp3__ >> 5 & 0x3; + pDst->access_policy = tmp3__ >> 7 & 0x3; + pDst->aggregation = tmp3__ >> 9 & 0x1; + pDst->psb = tmp3__ >> 10 & 0x1; + pDst->user_priority = tmp3__ >> 11 & 0x7; + pDst->tsinfo_ack_pol = tmp3__ >> 14 & 0x3; + pDst->schedule = tmp3__ >> 16 & 0x1; + pDst->unused = tmp3__ >> 17 & 0x7fff; + (void)pCtx; +} /* End dot11f_unpack_ff_ts_info. */ + +#define SigFfTSInfo (0x0018) + +void dot11f_unpack_ff_time_stamp(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfTimeStamp *pDst) +{ + framesntohq(pCtx, &pDst->timestamp, pBuf, 0); + (void)pCtx; +} /* End dot11f_unpack_ff_time_stamp. */ + +#define SigFfTimeStamp (0x0019) + +void dot11f_unpack_ff_transaction_id(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfTransactionId *pDst) +{ + DOT11F_MEMCPY(pCtx, pDst->transId, pBuf, 2); + (void)pCtx; +} /* End dot11f_unpack_ff_transaction_id. */ + +#define SigFfTransactionId (0x001a) + +void dot11f_unpack_ff_tx_antenna_id(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfTxAntennaId *pDst) +{ + pDst->antennaId = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_tx_antenna_id. */ + +#define SigFfTxAntennaId (0x001b) + +void dot11f_unpack_ff_tx_power(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfTxPower *pDst) +{ + pDst->txPower = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_tx_power. */ + +#define SigFfTxPower (0x001c) + +void dot11f_unpack_ff_vht_membership_status_array(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfVhtMembershipStatusArray *pDst) +{ + DOT11F_MEMCPY(pCtx, pDst->membershipStatusArray, pBuf, 8); + (void)pCtx; +} /* End dot11f_unpack_ff_vht_membership_status_array. */ + +#define SigFfVhtMembershipStatusArray (0x001d) + +void dot11f_unpack_ff_vht_user_position_array(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfVhtUserPositionArray *pDst) +{ + DOT11F_MEMCPY(pCtx, pDst->userPositionArray, pBuf, 16); + (void)pCtx; +} /* End dot11f_unpack_ff_vht_user_position_array. */ + +#define SigFfVhtUserPositionArray (0x001e) + +void dot11f_unpack_ff_addba_param_set(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfaddba_param_set *pDst) +{ + uint16_t tmp4__; + framesntohs(pCtx, &tmp4__, pBuf, 0); + pDst->amsdu_supp = tmp4__ >> 0 & 0x1; + pDst->policy = tmp4__ >> 1 & 0x1; + pDst->tid = tmp4__ >> 2 & 0xf; + pDst->buff_size = tmp4__ >> 6 & 0x3ff; + (void)pCtx; +} /* End dot11f_unpack_ff_addba_param_set. */ + +#define SigFfaddba_param_set (0x001f) + +void dot11f_unpack_ff_ba_start_seq_ctrl(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfba_start_seq_ctrl *pDst) +{ + uint16_t tmp5__; + framesntohs(pCtx, &tmp5__, pBuf, 0); + pDst->frag_number = tmp5__ >> 0 & 0xf; + pDst->ssn = tmp5__ >> 4 & 0xfff; + (void)pCtx; +} /* End dot11f_unpack_ff_ba_start_seq_ctrl. */ + +#define SigFfba_start_seq_ctrl (0x0020) + +void dot11f_unpack_ff_ba_timeout(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfba_timeout *pDst) +{ + framesntohs(pCtx, &pDst->timeout, pBuf, 0); + (void)pCtx; +} /* End dot11f_unpack_ff_ba_timeout. */ + +#define SigFfba_timeout (0x0021) + +void dot11f_unpack_ff_delba_param_set(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfdelba_param_set *pDst) +{ + uint16_t tmp6__; + framesntohs(pCtx, &tmp6__, pBuf, 0); + pDst->reserved = tmp6__ >> 0 & 0x7ff; + pDst->initiator = tmp6__ >> 11 & 0x1; + pDst->tid = tmp6__ >> 12 & 0xf; + (void)pCtx; +} /* End dot11f_unpack_ff_delba_param_set. */ + +#define SigFfdelba_param_set (0x0022) + +void dot11f_unpack_ff_ext_chan_switch_ann_action(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfext_chan_switch_ann_action *pDst) +{ + uint32_t tmp7__; + framesntohl(pCtx, &tmp7__, pBuf, 0); + pDst->switch_mode = tmp7__ >> 0 & 0xff; + pDst->op_class = tmp7__ >> 8 & 0xff; + pDst->new_channel = tmp7__ >> 16 & 0xff; + pDst->switch_count = tmp7__ >> 24 & 0xff; + (void)pCtx; +} /* End dot11f_unpack_ff_ext_chan_switch_ann_action. */ + +#define SigFfext_chan_switch_ann_action (0x0023) + +void dot11f_unpack_ff_p2p_action_oui(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfp2p_action_oui *pDst) +{ + DOT11F_MEMCPY(pCtx, pDst->oui_data, pBuf, 4); + (void)pCtx; +} /* End dot11f_unpack_ff_p2p_action_oui. */ + +#define SigFfp2p_action_oui (0x0024) + +void dot11f_unpack_ff_p2p_action_subtype(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfp2p_action_subtype *pDst) +{ + pDst->subtype = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_p2p_action_subtype. */ + +#define SigFfp2p_action_subtype (0x0025) + +void dot11f_unpack_ff_vendor_action_subtype(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfvendor_action_subtype *pDst) +{ + pDst->subtype = *pBuf; + (void)pCtx; +} /* End dot11f_unpack_ff_vendor_action_subtype. */ + +#define SigFfvendor_action_subtype (0x0026) + +void dot11f_unpack_ff_vendor_oui(tpAniSirGlobal pCtx, + uint8_t *pBuf, + tDot11fFfvendor_oui *pDst) +{ + DOT11F_MEMCPY(pCtx, pDst->oui_data, pBuf, 3); + (void)pCtx; +} /* End dot11f_unpack_ff_vendor_oui. */ + +#define SigFfvendor_oui (0x0027) + +uint32_t dot11f_unpack_tlv_authorized_ma_cs(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVAuthorizedMACs *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->mac, pBuf, 6); + pBuf += 6; + tlvlen -= (uint8_t)6; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_authorized_ma_cs. */ + +#define SigTlvAuthorizedMACs (0x0001) + + +#define SigTlvRequestToEnroll (0x0002) + + +uint32_t dot11f_unpack_tlv_version2(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVVersion2 *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp8__; + pDst->present = 1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp8__ = *pBuf; + pBuf += 1; + tlvlen -= 1; + pDst->minor = tmp8__ >> 0 & 0xf; + pDst->major = tmp8__ >> 4 & 0xf; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_version2. */ + +#define SigTlvVersion2 (0x0003) + + +#define SigTlvAPSetupLocked (0x0004) + + +#define SigTlvAssociationState (0x0005) + + +#define SigTlvConfigMethods (0x0006) + + +#define SigTlvConfigurationError (0x0007) + + +uint32_t dot11f_unpack_tlv_device_name(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVDeviceName *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + pDst->num_text = (uint8_t)(tlvlen); + if (tlvlen > 32) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->text, pBuf, (tlvlen)); + pBuf += (tlvlen); + tlvlen -= (tlvlen); + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_device_name. */ + +#define SigTlvDeviceName (0x0008) + + +#define SigTlvDevicePasswordID (0x0009) + + +uint32_t dot11f_unpack_tlv_extended_listen_timing(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVExtendedListenTiming *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->availibilityPeriod, pBuf, 0); + pBuf += 2; + tlvlen -= (uint8_t)2; + if (unlikely(tlvlen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->availibilityInterval, pBuf, 0); + pBuf += 2; + tlvlen -= (uint8_t)2; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_extended_listen_timing. */ + +#define SigTlvExtendedListenTiming (0x000a) + + +uint32_t dot11f_unpack_tlv_listen_channel(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVListenChannel *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 3)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->countryString, pBuf, 3); + pBuf += 3; + tlvlen -= (uint8_t)3; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->regulatoryClass = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->channel = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_listen_channel. */ + +#define SigTlvListenChannel (0x000b) + + +uint32_t dot11f_unpack_tlv_manufacturer(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVManufacturer *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + pDst->num_name = (uint8_t)(tlvlen); + if (tlvlen > 64) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->name, pBuf, (tlvlen)); + pBuf += (tlvlen); + tlvlen -= (tlvlen); + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_manufacturer. */ + +#define SigTlvManufacturer (0x000c) + + +#define SigTlvMinorReasonCode (0x000d) + + +uint32_t dot11f_unpack_tlv_model_name(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVModelName *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + pDst->num_text = (uint8_t)(tlvlen); + if (tlvlen > 32) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->text, pBuf, (tlvlen)); + pBuf += (tlvlen); + tlvlen -= (tlvlen); + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_model_name. */ + +#define SigTlvModelName (0x000e) + + +uint32_t dot11f_unpack_tlv_model_number(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVModelNumber *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + pDst->num_text = (uint8_t)(tlvlen); + if (tlvlen > 32) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->text, pBuf, (tlvlen)); + pBuf += (tlvlen); + tlvlen -= (tlvlen); + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_model_number. */ + +#define SigTlvModelNumber (0x000f) + + +uint32_t dot11f_unpack_tlv_notice_of_absence(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVNoticeOfAbsence *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->index = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->CTSWindowOppPS = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + pDst->num_NoADesc = (uint8_t)(tlvlen); + if (tlvlen > 36) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->NoADesc, pBuf, (tlvlen)); + pBuf += (tlvlen); + tlvlen -= (tlvlen); + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_notice_of_absence. */ + +#define SigTlvNoticeOfAbsence (0x0010) + + +uint32_t dot11f_unpack_tlv_operating_channel(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVOperatingChannel *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 3)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->countryString, pBuf, 3); + pBuf += 3; + tlvlen -= (uint8_t)3; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->regulatoryClass = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->channel = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_operating_channel. */ + +#define SigTlvOperatingChannel (0x0011) + + +uint32_t dot11f_unpack_tlv_p2_p_capability(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVP2PCapability *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->deviceCapability = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->groupCapability = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_p2_p_capability. */ + +#define SigTlvP2PCapability (0x0012) + + +uint32_t dot11f_unpack_tlv_p2_p_device_id(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVP2PDeviceId *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->P2PDeviceAddress, pBuf, 6); + pBuf += 6; + tlvlen -= (uint8_t)6; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_p2_p_device_id. */ + +#define SigTlvP2PDeviceId (0x0013) + + +static const tTLVDefn TLVS_P2PDeviceInfo[] = { + { offsetof(tDot11fTLVP2PDeviceInfo, DeviceName), + offsetof(tDot11fTLVDeviceName, present), "DeviceName", SigTlvDeviceName, + DOT11F_TLV_DEVICENAME, 0, 4, 36, 1, 2, 2, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_tlv_p2_p_device_info(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVP2PDeviceInfo *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->P2PDeviceAddress, pBuf, 6); + pBuf += 6; + tlvlen -= (uint8_t)6; + if (unlikely(tlvlen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->configMethod, pBuf, 0); + pBuf += 2; + tlvlen -= (uint8_t)2; + if (unlikely(tlvlen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->primaryDeviceType, pBuf, 8); + pBuf += 8; + tlvlen -= (uint8_t)8; + (void)pCtx; + status |= unpack_tlv_core(pCtx, + pBuf, + tlvlen, + TLVS_P2PDeviceInfo, + (uint8_t *)pDst, + sizeof(*pDst)); + return status; +} /* End dot11f_unpack_tlv_p2_p_device_info. */ + +#define SigTlvP2PDeviceInfo (0x0014) + + +uint32_t dot11f_unpack_tlv_p2_p_group_info(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVP2PGroupInfo *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + pDst->num_P2PClientInfoDesc = (uint8_t)(tlvlen); + DOT11F_MEMCPY(pCtx, pDst->P2PClientInfoDesc, pBuf, (tlvlen)); + pBuf += (tlvlen); + tlvlen -= (tlvlen); + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_p2_p_group_info. */ + +#define SigTlvP2PGroupInfo (0x0015) + + +#define SigTlvP2PStatus (0x0016) + + +uint32_t dot11f_unpack_tlv_primary_device_type(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVPrimaryDeviceType *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)tlvlen; /* Shutup the compiler */ + pDst->present = 1; + if (unlikely(tlvlen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->primary_category, pBuf, 1); + pBuf += 2; + tlvlen -= (uint8_t)2; + if (unlikely(tlvlen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->oui, pBuf, 4); + pBuf += 4; + tlvlen -= (uint8_t)4; + if (unlikely(tlvlen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->sub_category, pBuf, 1); + pBuf += 2; + tlvlen -= (uint8_t)2; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_primary_device_type. */ + +#define SigTlvPrimaryDeviceType (0x0017) + + +#define SigTlvRFBands (0x0018) + + +uint32_t dot11f_unpack_tlv_request_device_type(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVRequestDeviceType *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->primary_category, pBuf, 1); + pBuf += 2; + tlvlen -= (uint8_t)2; + if (unlikely(tlvlen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->oui, pBuf, 4); + pBuf += 4; + tlvlen -= (uint8_t)4; + if (unlikely(tlvlen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->sub_category, pBuf, 1); + pBuf += 2; + tlvlen -= (uint8_t)2; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_request_device_type. */ + +#define SigTlvRequestDeviceType (0x0019) + + +#define SigTlvRequestType (0x001a) + + +#define SigTlvResponseType (0x001b) + + +#define SigTlvSelectedRegistrar (0x001c) + + +#define SigTlvSelectedRegistrarConfigMethods (0x001d) + + +uint32_t dot11f_unpack_tlv_serial_number(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVSerialNumber *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + pDst->num_text = (uint8_t)(tlvlen); + if (tlvlen > 32) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->text, pBuf, (tlvlen)); + pBuf += (tlvlen); + tlvlen -= (tlvlen); + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_serial_number. */ + +#define SigTlvSerialNumber (0x001e) + + +uint32_t dot11f_unpack_tlv_uuid_e(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVUUID_E *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->uuid, pBuf, 16); + pBuf += 16; + tlvlen -= (uint8_t)16; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_uuid_e. */ + +#define SigTlvUUID_E (0x001f) + + +uint32_t dot11f_unpack_tlv_uuid_r(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVUUID_R *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->uuid, pBuf, 16); + pBuf += 16; + tlvlen -= (uint8_t)16; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_uuid_r. */ + +#define SigTlvUUID_R (0x0020) + + +static const tTLVDefn TLVS_VendorExtension[] = { + { offsetof(tDot11fTLVVendorExtension, Version2), + offsetof(tDot11fTLVVersion2, present), "Version2", SigTlvVersion2, + DOT11F_TLV_VERSION2, 0, 3, 3, 0, 1, 1, 1, }, + { offsetof(tDot11fTLVVendorExtension, AuthorizedMACs), + offsetof(tDot11fTLVAuthorizedMACs, present), "AuthorizedMACs", + SigTlvAuthorizedMACs, DOT11F_TLV_AUTHORIZEDMACS, 0, 8, 8, 0, 1, 1, 1, }, + { offsetof(tDot11fTLVVendorExtension, RequestToEnroll), + offsetof(tDot11fTLVRequestToEnroll, present), "RequestToEnroll", + SigTlvRequestToEnroll, DOT11F_TLV_REQUESTTOENROLL, + 0, 3, 3, 0, 1, 1, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_tlv_vendor_extension(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVVendorExtension *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 3)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->vendorId, pBuf, 3); + pBuf += 3; + tlvlen -= (uint8_t)3; + (void)pCtx; + status |= unpack_tlv_core(pCtx, + pBuf, + tlvlen, + TLVS_VendorExtension, + (uint8_t *)pDst, + sizeof(*pDst)); + return status; +} /* End dot11f_unpack_tlv_vendor_extension. */ + +#define SigTlvVendorExtension (0x0021) + + +uint32_t dot11f_unpack_tlv_version(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVVersion *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp9__; + pDst->present = 1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp9__ = *pBuf; + pBuf += 1; + tlvlen -= 1; + pDst->minor = tmp9__ >> 0 & 0xf; + pDst->major = tmp9__ >> 4 & 0xf; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_version. */ + +#define SigTlvVersion (0x0022) + + +#define SigTlvWPSState (0x0023) + + +#define SigTlvassoc_disallowed (0x0024) + + +#define SigTlvassoc_retry_delay (0x0025) + + +#define SigTlvcellular_data_cap (0x0026) + + +#define SigTlvcellular_data_con_pref (0x0027) + + +#define SigTlvmbo_ap_cap (0x0028) + + +uint32_t dot11f_unpack_tlv_non_prefferd_chan_rep(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVnon_prefferd_chan_rep *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->oper_class = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + pDst->num_channel_report = (uint8_t)(tlvlen); + if (tlvlen > 254) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->channel_report, pBuf, (tlvlen)); + pBuf += (tlvlen); + tlvlen -= (tlvlen); + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_non_prefferd_chan_rep. */ + +#define SigTlvnon_prefferd_chan_rep (0x0029) + + +uint32_t dot11f_unpack_tlv_oce_cap(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVoce_cap *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp10__; + pDst->present = 1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp10__ = *pBuf; + pBuf += 1; + tlvlen -= 1; + pDst->oce_release = tmp10__ >> 0 & 0x7; + pDst->is_sta_cfon = tmp10__ >> 3 & 0x1; + pDst->non_oce_ap_present = tmp10__ >> 4 & 0x1; + pDst->reserved = tmp10__ >> 5 & 0x7; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_oce_cap. */ + +#define SigTlvoce_cap (0x002a) + + +uint32_t dot11f_unpack_tlv_reduced_wan_metrics(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVreduced_wan_metrics *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp11__; + pDst->present = 1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp11__ = *pBuf; + pBuf += 1; + tlvlen -= 1; + pDst->downlink_av_cap = tmp11__ >> 0 & 0xf; + pDst->uplink_av_cap = tmp11__ >> 4 & 0xf; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_reduced_wan_metrics. */ + +#define SigTlvreduced_wan_metrics (0x002b) + + +uint32_t dot11f_unpack_tlv_rssi_assoc_rej(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVrssi_assoc_rej *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->delta_rssi = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + if (unlikely(tlvlen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->retry_delay = *pBuf; + pBuf += 1; + tlvlen -= (uint8_t)1; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_rssi_assoc_rej. */ + +#define SigTlvrssi_assoc_rej (0x002c) + + +#define SigTlvtransition_reason (0x002d) + + +#define SigTlvtransition_reject_reason (0x002e) + + +uint32_t dot11f_unpack_tlv_p2_p_interface(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint16_t tlvlen, + tDot11fTLVP2PInterface *pDst) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + pDst->present = 1; + if (unlikely(tlvlen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->P2PDeviceAddress, pBuf, 6); + pBuf += 6; + tlvlen -= (uint8_t)6; + (void)pCtx; + return status; +} /* End dot11f_unpack_tlv_p2_p_interface. */ + +#define SigTlvP2PInterface (0x002f) + + +#define SigTlvP2PManageability (0x0030) + + +uint32_t dot11f_unpack_ie_gtk(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEGTK *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp12__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp12__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->keyId = tmp12__ >> 0 & 0x3; + pDst->reserved = tmp12__ >> 2 & 0x3feb; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->keyLength = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->RSC, pBuf, 8); + pBuf += 8; + ielen -= (uint8_t)8; + pDst->num_key = (uint8_t)(ielen); + if (ielen > 32) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->key, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_gtk. */ + +#define SigIeGTK (0x0001) + + +uint32_t dot11f_unpack_ie_igtk(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEIGTK *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->keyID, pBuf, 2); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->IPN, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->keyLength = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 24)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->key, pBuf, 24); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_igtk. */ + +#define SigIeIGTK (0x0002) + + +uint32_t dot11f_unpack_ie_r0_kh_id(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIER0KH_ID *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_PMK_R0_ID = (uint8_t)(ielen); + if (ielen > 48) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->PMK_R0_ID, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_r0_kh_id. */ + +#define SigIeR0KH_ID (0x0003) + + +uint32_t dot11f_unpack_ie_r1_kh_id(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIER1KH_ID *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->PMK_R1_ID, pBuf, 6); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_r1_kh_id. */ + +#define SigIeR1KH_ID (0x0004) + + +uint32_t dot11f_unpack_ie_version_attr(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEversion_attr *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->sub_version = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_version_attr. */ + +#define SigIeversion_attr (0x0005) + + +uint32_t dot11f_unpack_ie_vht_mcs11_attr(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEvht_mcs11_attr *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->vht_mcs_10_11_supp = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_vht_mcs11_attr. */ + +#define SigIevht_mcs11_attr (0x0006) + + +uint32_t dot11f_unpack_ie_ap_channel_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEAPChannelReport *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->regulatoryClass = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + pDst->num_channelList = (uint8_t)(ielen); + if (ielen > 50) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->channelList, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ap_channel_report. */ + +#define SigIeAPChannelReport (0x0007) + + +uint32_t dot11f_unpack_ie_bcn_reporting_detail(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEBcnReportingDetail *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->reportingDetail = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_bcn_reporting_detail. */ + +#define SigIeBcnReportingDetail (0x0008) + + +uint32_t dot11f_unpack_ie_beacon_report_frm_body(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEBeaconReportFrmBody *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_reportedFields = (uint8_t)(ielen); + if (ielen > 224) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->reportedFields, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_beacon_report_frm_body. */ + +#define SigIeBeaconReportFrmBody (0x0009) + + +uint32_t dot11f_unpack_ie_beacon_reporting(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEBeaconReporting *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->reportingCondition = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->threshold = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_beacon_reporting. */ + +#define SigIeBeaconReporting (0x000a) + + +uint32_t dot11f_unpack_ie_condensed_country_str(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIECondensedCountryStr *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->countryStr, pBuf, 2); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_condensed_country_str. */ + +#define SigIeCondensedCountryStr (0x000b) + + +uint32_t dot11f_unpack_ie_measurement_pilot(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEMeasurementPilot *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurementPilot = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + pDst->num_vendorSpecific = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->vendorSpecific, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_measurement_pilot. */ + +#define SigIeMeasurementPilot (0x000c) + + +uint32_t dot11f_unpack_ie_multi_bssid(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEMultiBssid *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->maxBSSIDIndicator = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + pDst->num_vendorSpecific = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->vendorSpecific, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_multi_bssid. */ + +#define SigIeMultiBssid (0x000d) + + +uint32_t dot11f_unpack_ie_ric_data(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIERICData *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->Identifier = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->resourceDescCount = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->statusCode, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ric_data. */ + +#define SigIeRICData (0x000e) + + +uint32_t dot11f_unpack_ie_ric_descriptor(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIERICDescriptor *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->resourceType = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + pDst->num_variableData = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->variableData, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ric_descriptor. */ + +#define SigIeRICDescriptor (0x000f) + + +uint32_t dot11f_unpack_ie_rrm_enabled_cap(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIERRMEnabledCap *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp13__; + uint8_t tmp14__; + uint8_t tmp15__; + uint8_t tmp16__; + uint8_t tmp17__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp13__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->LinkMeasurement = tmp13__ >> 0 & 0x1; + pDst->NeighborRpt = tmp13__ >> 1 & 0x1; + pDst->parallel = tmp13__ >> 2 & 0x1; + pDst->repeated = tmp13__ >> 3 & 0x1; + pDst->BeaconPassive = tmp13__ >> 4 & 0x1; + pDst->BeaconActive = tmp13__ >> 5 & 0x1; + pDst->BeaconTable = tmp13__ >> 6 & 0x1; + pDst->BeaconRepCond = tmp13__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp14__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->FrameMeasurement = tmp14__ >> 0 & 0x1; + pDst->ChannelLoad = tmp14__ >> 1 & 0x1; + pDst->NoiseHistogram = tmp14__ >> 2 & 0x1; + pDst->statistics = tmp14__ >> 3 & 0x1; + pDst->LCIMeasurement = tmp14__ >> 4 & 0x1; + pDst->LCIAzimuth = tmp14__ >> 5 & 0x1; + pDst->TCMCapability = tmp14__ >> 6 & 0x1; + pDst->triggeredTCM = tmp14__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp15__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->APChanReport = tmp15__ >> 0 & 0x1; + pDst->RRMMIBEnabled = tmp15__ >> 1 & 0x1; + pDst->operatingChanMax = tmp15__ >> 2 & 0x7; + pDst->nonOperatinChanMax = tmp15__ >> 5 & 0x7; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp16__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->MeasurementPilot = tmp16__ >> 0 & 0x7; + pDst->MeasurementPilotEnabled = tmp16__ >> 3 & 0x1; + pDst->NeighborTSFOffset = tmp16__ >> 4 & 0x1; + pDst->RCPIMeasurement = tmp16__ >> 5 & 0x1; + pDst->RSNIMeasurement = tmp16__ >> 6 & 0x1; + pDst->BssAvgAccessDelay = tmp16__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp17__ = *pBuf; + pDst->BSSAvailAdmission = tmp17__ >> 0 & 0x1; + pDst->AntennaInformation = tmp17__ >> 1 & 0x1; + pDst->fine_time_meas_rpt = tmp17__ >> 2 & 0x1; + pDst->lci_capability = tmp17__ >> 3 & 0x1; + pDst->reserved = tmp17__ >> 4 & 0xf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_rrm_enabled_cap. */ + +#define SigIeRRMEnabledCap (0x0010) + + +uint32_t dot11f_unpack_ie_requested_info(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIERequestedInfo *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_requested_eids = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->requested_eids, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_requested_info. */ + +#define SigIeRequestedInfo (0x0011) + + +uint32_t dot11f_unpack_ie_ssid(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIESSID *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) { + status = DOT11F_DUPLICATE_IE; + return status; + } + pDst->present = 1; + pDst->num_ssid = (uint8_t)(ielen); + if (ielen > 32) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->ssid, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ssid. */ + +#define SigIeSSID (0x0012) + + +uint32_t dot11f_unpack_ie_schedule(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIESchedule *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp18__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp18__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->aggregation = tmp18__ >> 0 & 0x1; + pDst->tsid = tmp18__ >> 1 & 0xf; + pDst->direction = tmp18__ >> 5 & 0x3; + pDst->reserved = tmp18__ >> 7 & 0x1ff; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->service_start_time, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->service_interval, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->max_service_dur, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->spec_interval, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_schedule. */ + +#define SigIeSchedule (0x0013) + + +uint32_t dot11f_unpack_ie_tclas(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIETCLAS *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->user_priority = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->classifier_type = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->classifier_mask = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + switch (pDst->classifier_type) { + case 0: + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.EthParams.source, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.EthParams.dest, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.EthParams.type, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + break; + case 1: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->info.IpParams.version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + switch (pDst->info.IpParams.version) { + case 4: + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV4Params.source, pBuf, 4); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV4Params.dest, pBuf, 4); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.IpParams.params.IpV4Params.src_port, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.IpParams.params.IpV4Params.dest_port, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->info.IpParams.params.IpV4Params.DSCP = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->info.IpParams.params.IpV4Params.proto = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->info.IpParams.params.IpV4Params.reserved = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + break; + case 6: + if (unlikely(ielen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV6Params.source, pBuf, 16); + pBuf += 16; + ielen -= (uint8_t)16; + if (unlikely(ielen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV6Params.dest, pBuf, 16); + pBuf += 16; + ielen -= (uint8_t)16; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.IpParams.params.IpV6Params.src_port, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.IpParams.params.IpV6Params.dest_port, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 3)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV6Params.flow_label, pBuf, 3); + pBuf += 3; + ielen -= (uint8_t)3; + break; + } + break; + case 2: + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.Params8021dq.tag_type, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + break; + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_tclas. */ + +#define SigIeTCLAS (0x0014) + + +#define SigIeTCLASSPROC (0x0015) + + +uint32_t dot11f_unpack_ie_ts_delay(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIETSDelay *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->delay, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ts_delay. */ + +#define SigIeTSDelay (0x0016) + + +uint32_t dot11f_unpack_ie_tsf_info(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIETSFInfo *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->TsfOffset, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->BeaconIntvl, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_tsf_info. */ + +#define SigIeTSFInfo (0x0017) + + +uint32_t dot11f_unpack_ie_tspec(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIETSPEC *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp19__; + uint8_t tmp20__; + uint16_t tmp21__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp19__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->traffic_type = tmp19__ >> 0 & 0x1; + pDst->tsid = tmp19__ >> 1 & 0xf; + pDst->direction = tmp19__ >> 5 & 0x3; + pDst->access_policy = tmp19__ >> 7 & 0x3; + pDst->aggregation = tmp19__ >> 9 & 0x1; + pDst->psb = tmp19__ >> 10 & 0x1; + pDst->user_priority = tmp19__ >> 11 & 0x7; + pDst->tsinfo_ack_pol = tmp19__ >> 14 & 0x3; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp20__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->schedule = tmp20__ >> 0 & 0x1; + pDst->unused = tmp20__ >> 1 & 0x7f; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp21__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->size = tmp21__ >> 0 & 0x7fff; + pDst->fixed = tmp21__ >> 15 & 0x1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->max_msdu_size, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->min_service_int, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->max_service_int, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->inactivity_int, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->suspension_int, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->service_start_time, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->min_data_rate, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->mean_data_rate, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->peak_data_rate, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->burst_size, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->delay_bound, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->min_phy_rate, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->surplus_bw_allowance, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->medium_time, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_tspec. */ + +#define SigIeTSPEC (0x0018) + + +uint32_t dot11f_unpack_ie_vht_caps(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEVHTCaps *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t tmp22__; + uint16_t tmp23__; + uint16_t tmp24__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &tmp22__, pBuf, 0); + pBuf += 4; + ielen -= 4; + pDst->maxMPDULen = tmp22__ >> 0 & 0x3; + pDst->supportedChannelWidthSet = tmp22__ >> 2 & 0x3; + pDst->ldpcCodingCap = tmp22__ >> 4 & 0x1; + pDst->shortGI80MHz = tmp22__ >> 5 & 0x1; + pDst->shortGI160and80plus80MHz = tmp22__ >> 6 & 0x1; + pDst->txSTBC = tmp22__ >> 7 & 0x1; + pDst->rxSTBC = tmp22__ >> 8 & 0x7; + pDst->suBeamFormerCap = tmp22__ >> 11 & 0x1; + pDst->suBeamformeeCap = tmp22__ >> 12 & 0x1; + pDst->csnofBeamformerAntSup = tmp22__ >> 13 & 0x7; + pDst->numSoundingDim = tmp22__ >> 16 & 0x7; + pDst->muBeamformerCap = tmp22__ >> 19 & 0x1; + pDst->muBeamformeeCap = tmp22__ >> 20 & 0x1; + pDst->vhtTXOPPS = tmp22__ >> 21 & 0x1; + pDst->htcVHTCap = tmp22__ >> 22 & 0x1; + pDst->maxAMPDULenExp = tmp22__ >> 23 & 0x7; + pDst->vhtLinkAdaptCap = tmp22__ >> 26 & 0x3; + pDst->rxAntPattern = tmp22__ >> 28 & 0x1; + pDst->txAntPattern = tmp22__ >> 29 & 0x1; + pDst->extended_nss_bw_supp = tmp22__ >> 30 & 0x3; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->rxMCSMap, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp23__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->rxHighSupDataRate = tmp23__ >> 0 & 0x1fff; + pDst->max_nsts_total = tmp23__ >> 13 & 0x7; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->txMCSMap, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp24__, pBuf, 0); + pDst->txSupDataRate = tmp24__ >> 0 & 0x1fff; + pDst->vht_extended_nss_bw_cap = tmp24__ >> 13 & 0x1; + pDst->reserved = tmp24__ >> 14 & 0x3; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_vht_caps. */ + +#define SigIeVHTCaps (0x0019) + + +uint32_t dot11f_unpack_ie_vht_operation(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEVHTOperation *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->chanWidth = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->chan_center_freq_seg0 = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->chan_center_freq_seg1 = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->basicMCSSet, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_vht_operation. */ + +#define SigIeVHTOperation (0x001a) + + +uint32_t dot11f_unpack_ie_wmm_schedule(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWMMSchedule *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp25__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp25__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->aggregation = tmp25__ >> 0 & 0x1; + pDst->tsid = tmp25__ >> 1 & 0xf; + pDst->direction = tmp25__ >> 5 & 0x3; + pDst->reserved = tmp25__ >> 7 & 0x1ff; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->service_start_time, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->service_interval, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->max_service_dur, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->spec_interval, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wmm_schedule. */ + +#define SigIeWMMSchedule (0x001b) + + +uint32_t dot11f_unpack_ie_wmmtclas(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWMMTCLAS *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->user_priority = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->classifier_type = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->classifier_mask = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + switch (pDst->classifier_type) { + case 0: + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.EthParams.source, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.EthParams.dest, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.EthParams.type, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + break; + case 1: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->info.IpParams.version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + switch (pDst->info.IpParams.version) { + case 4: + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV4Params.source, pBuf, 4); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV4Params.dest, pBuf, 4); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.IpParams.params.IpV4Params.src_port, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.IpParams.params.IpV4Params.dest_port, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->info.IpParams.params.IpV4Params.DSCP = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->info.IpParams.params.IpV4Params.proto = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->info.IpParams.params.IpV4Params.reserved = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + break; + case 6: + if (unlikely(ielen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV6Params.source, pBuf, 16); + pBuf += 16; + ielen -= (uint8_t)16; + if (unlikely(ielen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV6Params.dest, pBuf, 16); + pBuf += 16; + ielen -= (uint8_t)16; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.IpParams.params.IpV6Params.src_port, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.IpParams.params.IpV6Params.dest_port, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 3)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->info.IpParams.params.IpV6Params.flow_label, pBuf, 3); + pBuf += 3; + ielen -= (uint8_t)3; + break; + } + break; + case 2: + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->info.Params8021dq.tag_type, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + break; + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wmmtclas. */ + +#define SigIeWMMTCLAS (0x001c) + + +uint32_t dot11f_unpack_ie_wmmtclasproc(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWMMTCLASPROC *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->processing = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wmmtclasproc. */ + +#define SigIeWMMTCLASPROC (0x001d) + + +uint32_t dot11f_unpack_ie_wmmts_delay(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWMMTSDelay *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->delay, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wmmts_delay. */ + +#define SigIeWMMTSDelay (0x001e) + + +uint32_t dot11f_unpack_ie_wmmtspec(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWMMTSPEC *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp26__; + uint8_t tmp27__; + uint16_t tmp28__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp26__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->traffic_type = tmp26__ >> 0 & 0x1; + pDst->tsid = tmp26__ >> 1 & 0xf; + pDst->direction = tmp26__ >> 5 & 0x3; + pDst->access_policy = tmp26__ >> 7 & 0x3; + pDst->aggregation = tmp26__ >> 9 & 0x1; + pDst->psb = tmp26__ >> 10 & 0x1; + pDst->user_priority = tmp26__ >> 11 & 0x7; + pDst->tsinfo_ack_pol = tmp26__ >> 14 & 0x3; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp27__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->tsinfo_rsvd = tmp27__ >> 0 & 0x7f; + pDst->burst_size_defn = tmp27__ >> 7 & 0x1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp28__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->size = tmp28__ >> 0 & 0x7fff; + pDst->fixed = tmp28__ >> 15 & 0x1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->max_msdu_size, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->min_service_int, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->max_service_int, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->inactivity_int, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->suspension_int, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->service_start_time, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->min_data_rate, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->mean_data_rate, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->peak_data_rate, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->burst_size, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->delay_bound, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->min_phy_rate, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->surplus_bw_allowance, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->medium_time, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wmmtspec. */ + +#define SigIeWMMTSPEC (0x001f) + + +uint32_t dot11f_unpack_ie_wider_bw_chan_switch_ann(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWiderBWChanSwitchAnn *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->newChanWidth = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->newCenterChanFreq0 = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->newCenterChanFreq1 = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wider_bw_chan_switch_ann. */ + +#define SigIeWiderBWChanSwitchAnn (0x0020) + + +uint32_t dot11f_unpack_ie_azimuth_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEazimuth_req *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->request = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_azimuth_req. */ + +#define SigIeazimuth_req (0x0021) + + +uint32_t dot11f_unpack_ie_beacon_report_frm_body_fragment_id(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEbeacon_report_frm_body_fragment_id *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp29__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp29__, pBuf, 0); + pDst->beacon_report_id = tmp29__ >> 0 & 0xff; + pDst->fragment_id_number = tmp29__ >> 8 & 0x7f; + pDst->more_fragments = tmp29__ >> 15 & 0x1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_beacon_report_frm_body_fragment_id. */ + +#define SigIebeacon_report_frm_body_fragment_id (0x0022) + + +uint32_t dot11f_unpack_ie_last_beacon_report_indication(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIElast_beacon_report_indication *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->last_fragment = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_last_beacon_report_indication. */ + +#define SigIelast_beacon_report_indication (0x0023) + + +uint32_t dot11f_unpack_ie_max_age(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEmax_age *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->max_age, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_max_age. */ + +#define SigIemax_age (0x0024) + + +static const tFFDefn FFS_neighbor_rpt[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_neighbor_rpt[] = { + { offsetof(tDot11fIEneighbor_rpt, TSFInfo), offsetof(tDot11fIETSFInfo, + present), 0, "TSFInfo", 0, 6, 6, SigIeTSFInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TSFINFO, 0, 0, }, + { offsetof(tDot11fIEneighbor_rpt, CondensedCountryStr), + offsetof(tDot11fIECondensedCountryStr, present), 0, "CondensedCountryStr", + 0, 4, 4, SigIeCondensedCountryStr, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CONDENSEDCOUNTRYSTR, 0, 0, }, + { offsetof(tDot11fIEneighbor_rpt, MeasurementPilot), + offsetof(tDot11fIEMeasurementPilot, present), 0, "MeasurementPilot", + 0, 3, 258, SigIeMeasurementPilot, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MEASUREMENTPILOT, 0, 0, }, + { offsetof(tDot11fIEneighbor_rpt, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fIEneighbor_rpt, MultiBssid), + offsetof(tDot11fIEMultiBssid, present), 0, "MultiBssid", + 0, 3, 258, SigIeMultiBssid, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MULTIBSSID, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +uint32_t dot11f_unpack_ie_neighbor_rpt(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEneighbor_rpt *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp30__; + uint8_t tmp31__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->bssid, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp30__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->APReachability = tmp30__ >> 0 & 0x3; + pDst->Security = tmp30__ >> 2 & 0x1; + pDst->KeyScope = tmp30__ >> 3 & 0x1; + pDst->SpecMgmtCap = tmp30__ >> 4 & 0x1; + pDst->QosCap = tmp30__ >> 5 & 0x1; + pDst->apsd = tmp30__ >> 6 & 0x1; + pDst->rrm = tmp30__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp31__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->DelayedBA = tmp31__ >> 0 & 0x1; + pDst->ImmBA = tmp31__ >> 1 & 0x1; + pDst->MobilityDomain = tmp31__ >> 2 & 0x1; + pDst->reserved = tmp31__ >> 3 & 0x1f; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->reserved1, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->regulatoryClass = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->channel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->PhyType = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + (void)pCtx; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_neighbor_rpt, + IES_neighbor_rpt, + (uint8_t *)pDst, + sizeof(*pDst), + append_ie); + return status; +} /* End dot11f_unpack_ie_neighbor_rpt. */ + +#define SigIeneighbor_rpt (0x0025) + + +uint32_t dot11f_unpack_ie_req_mac_addr(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEreq_mac_addr *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->addr, pBuf, 6); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_req_mac_addr. */ + +#define SigIereq_mac_addr (0x0026) + + +uint32_t dot11f_unpack_ie_tgt_mac_addr(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEtgt_mac_addr *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->addr, pBuf, 6); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_tgt_mac_addr. */ + +#define SigIetgt_mac_addr (0x0027) + + +uint32_t dot11f_unpack_ie_vht_transmit_power_env(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEvht_transmit_power_env *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_bytes = (uint8_t)(ielen); + if (ielen > 5) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->bytes, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_vht_transmit_power_env. */ + +#define SigIevht_transmit_power_env (0x0028) + + +uint32_t dot11f_unpack_ie_aid(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEAID *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->assocId, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_aid. */ + +#define SigIeAID (0x0029) + + +uint32_t dot11f_unpack_ie_BeaconReportStatus(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEBeaconReportStatus *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->sub_type = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->length = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->reason_code = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_BeaconReportStatus. */ + +#define SigIeBeaconReportStatus (0x002a) + + +uint32_t dot11f_unpack_ie_cf_params(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIECFParams *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->cfp_count = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->cfp_period = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->cfp_maxduration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->cfp_durremaining, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_cf_params. */ + +#define SigIeCFParams (0x002b) + + +uint32_t dot11f_unpack_ie_challenge_text(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEChallengeText *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_text = (uint8_t)(ielen); + if (ielen > 253) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->text, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_challenge_text. */ + +#define SigIeChallengeText (0x002c) + + +uint32_t dot11f_unpack_ie_chan_switch_ann(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEChanSwitchAnn *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->switchMode = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->newChannel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->switchCount = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_chan_switch_ann. */ + +#define SigIeChanSwitchAnn (0x002d) + + +static const tFFDefn FFS_ChannelSwitchWrapper[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_ChannelSwitchWrapper[] = { + { offsetof(tDot11fIEChannelSwitchWrapper, WiderBWChanSwitchAnn), + offsetof(tDot11fIEWiderBWChanSwitchAnn, present), 0, + "WiderBWChanSwitchAnn", 0, 5, 5, SigIeWiderBWChanSwitchAnn, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_WIDERBWCHANSWITCHANN, 0, 0, }, + { offsetof(tDot11fIEChannelSwitchWrapper, vht_transmit_power_env), + offsetof(tDot11fIEvht_transmit_power_env, present), 0, + "vht_transmit_power_env", 0, 4, 7, SigIevht_transmit_power_env, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_VHT_TRANSMIT_POWER_ENV, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +uint32_t dot11f_unpack_ie_channel_switch_wrapper(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEChannelSwitchWrapper *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + (void)pCtx; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_ChannelSwitchWrapper, + IES_ChannelSwitchWrapper, + (uint8_t *)pDst, + sizeof(*pDst), + append_ie); + return status; +} /* End dot11f_unpack_ie_channel_switch_wrapper. */ + +#define SigIeChannelSwitchWrapper (0x002e) + + +uint32_t dot11f_unpack_ie_country(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIECountry *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 3)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->country, pBuf, 3); + pBuf += 3; + ielen -= (uint8_t)3; + if (!ielen) { + pDst->num_triplets = 0U; + return 0U; + } else { + pDst->num_triplets = (uint8_t)(ielen / 3); + if (ielen > 84 * 3) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->triplets, pBuf, (ielen)); + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_country. */ + +#define SigIeCountry (0x002f) + + +#define SigIeDSParams (0x0030) + + +uint32_t dot11f_unpack_ie_edca_param_set(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEEDCAParamSet *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp32__; + uint8_t tmp33__; + uint8_t tmp34__; + uint8_t tmp35__; + uint8_t tmp36__; + uint8_t tmp37__; + uint8_t tmp38__; + uint8_t tmp39__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->qos = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->reserved = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp32__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbe_aifsn = tmp32__ >> 0 & 0xf; + pDst->acbe_acm = tmp32__ >> 4 & 0x1; + pDst->acbe_aci = tmp32__ >> 5 & 0x3; + pDst->unused1 = tmp32__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp33__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbe_acwmin = tmp33__ >> 0 & 0xf; + pDst->acbe_acwmax = tmp33__ >> 4 & 0xf; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->acbe_txoplimit, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp34__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbk_aifsn = tmp34__ >> 0 & 0xf; + pDst->acbk_acm = tmp34__ >> 4 & 0x1; + pDst->acbk_aci = tmp34__ >> 5 & 0x3; + pDst->unused2 = tmp34__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp35__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbk_acwmin = tmp35__ >> 0 & 0xf; + pDst->acbk_acwmax = tmp35__ >> 4 & 0xf; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->acbk_txoplimit, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp36__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvi_aifsn = tmp36__ >> 0 & 0xf; + pDst->acvi_acm = tmp36__ >> 4 & 0x1; + pDst->acvi_aci = tmp36__ >> 5 & 0x3; + pDst->unused3 = tmp36__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp37__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvi_acwmin = tmp37__ >> 0 & 0xf; + pDst->acvi_acwmax = tmp37__ >> 4 & 0xf; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->acvi_txoplimit, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp38__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvo_aifsn = tmp38__ >> 0 & 0xf; + pDst->acvo_acm = tmp38__ >> 4 & 0x1; + pDst->acvo_aci = tmp38__ >> 5 & 0x3; + pDst->unused4 = tmp38__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp39__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvo_acwmin = tmp39__ >> 0 & 0xf; + pDst->acvo_acwmax = tmp39__ >> 4 & 0xf; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->acvo_txoplimit, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_edca_param_set. */ + +#define SigIeEDCAParamSet (0x0031) + + +uint32_t dot11f_unpack_ie_erp_info(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEERPInfo *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp40__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp40__ = *pBuf; + pDst->non_erp_present = tmp40__ >> 0 & 0x1; + pDst->use_prot = tmp40__ >> 1 & 0x1; + pDst->barker_preamble = tmp40__ >> 2 & 0x1; + pDst->unused = tmp40__ >> 3 & 0x1f; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_erp_info. */ + +#define SigIeERPInfo (0x0032) + + +uint32_t dot11f_unpack_ie_ese_cckm_opaque(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEESECckmOpaque *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + if (ielen > 20) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ese_cckm_opaque. */ + +#define SigIeESECckmOpaque (0x0033) + + +uint32_t dot11f_unpack_ie_ese_rad_mgmt_cap(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEESERadMgmtCap *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp41__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->mgmt_state = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp41__ = *pBuf; + pDst->mbssid_mask = tmp41__ >> 0 & 0x7; + pDst->reserved = tmp41__ >> 3 & 0x1f; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ese_rad_mgmt_cap. */ + +#define SigIeESERadMgmtCap (0x0034) + + +uint32_t dot11f_unpack_ie_ese_traf_strm_met(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEESETrafStrmMet *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->tsid = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->state = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->msmt_interval, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ese_traf_strm_met. */ + +#define SigIeESETrafStrmMet (0x0035) + + +uint32_t dot11f_unpack_ie_ese_traf_strm_rate_set(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEESETrafStrmRateSet *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->tsid = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + pDst->num_tsrates = (uint8_t)(ielen); + if (ielen > 8) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->tsrates, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ese_traf_strm_rate_set. */ + +#define SigIeESETrafStrmRateSet (0x0036) + + +uint32_t dot11f_unpack_ie_ese_txmit_power(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEESETxmitPower *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->power_limit = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->reserved = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ese_txmit_power. */ + +#define SigIeESETxmitPower (0x0037) + + +uint32_t dot11f_unpack_ie_ese_version(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEESEVersion *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ese_version. */ + +#define SigIeESEVersion (0x0038) + + +uint32_t dot11f_unpack_ie_ext_cap(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEExtCap *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + + if (!ielen) /* Check to ensure copying of ielen bytes */ + goto endUnpackIeExtCap; + pDst->num_bytes = (uint8_t)(ielen); + if (ielen > 15) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->bytes, pBuf, (ielen)); + +endUnpackIeExtCap: + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ext_cap. */ + +#define SigIeExtCap (0x0039) + + +uint32_t dot11f_unpack_ie_ext_supp_rates(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEExtSuppRates *pDst, + bool append_ie) +{ + uint8_t i; + uint8_t rate_indx = 0; + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + for (i = 0; i < ielen; i++) { + if ((DOT11F_IS_BG_RATE(pBuf[i] & 0x7F)) && + (rate_indx < 12)) { + pDst->rates[rate_indx++] = pBuf[i]; + } + } + + if (rate_indx == 0) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + pDst->num_rates = rate_indx; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ext_supp_rates. */ + +#define SigIeExtSuppRates (0x003a) + + +uint32_t dot11f_unpack_ie_fh_param_set(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEFHParamSet *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->dwell_time, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->hop_set = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->hop_pattern = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->hop_index = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fh_param_set. */ + +#define SigIeFHParamSet (0x003b) + + +uint32_t dot11f_unpack_ie_fh_params(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEFHParams *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->radix = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->nchannels = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fh_params. */ + +#define SigIeFHParams (0x003c) + + +uint32_t dot11f_unpack_ie_fh_patt_table(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEFHPattTable *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->flag = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->nsets = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->modulus = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->offset = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + pDst->num_randtable = (uint8_t)(ielen); + if (ielen > 251) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->randtable, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fh_patt_table. */ + +#define SigIeFHPattTable (0x003d) + + +static const tFFDefn FFS_FTInfo[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_FTInfo[] = { + { offsetof(tDot11fIEFTInfo, R1KH_ID), offsetof(tDot11fIER1KH_ID, present), + 0, "R1KH_ID", 0, 8, 8, SigIeR1KH_ID, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_R1KH_ID, 0, 0, }, + { offsetof(tDot11fIEFTInfo, GTK), offsetof(tDot11fIEGTK, present), 0, "GTK", + 0, 18, 45, SigIeGTK, {0, 0, 0, 0, 0}, 0, DOT11F_EID_GTK, 0, 0, }, + { offsetof(tDot11fIEFTInfo, R0KH_ID), offsetof(tDot11fIER0KH_ID, present), + 0, "R0KH_ID", 0, 3, 50, SigIeR0KH_ID, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_R0KH_ID, 0, 0, }, + { offsetof(tDot11fIEFTInfo, IGTK), offsetof(tDot11fIEIGTK, present), 0, + "IGTK", 0, 35, 35, SigIeIGTK, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_IGTK, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +uint32_t dot11f_unpack_ie_ft_info(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEFTInfo *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp42__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp42__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->reserved = tmp42__ >> 0 & 0xff; + pDst->IECount = tmp42__ >> 8 & 0xff; + if (unlikely(ielen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->MIC, pBuf, 16); + pBuf += 16; + ielen -= (uint8_t)16; + if (unlikely(ielen < 32)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->Anonce, pBuf, 32); + pBuf += 32; + ielen -= (uint8_t)32; + if (unlikely(ielen < 32)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->Snonce, pBuf, 32); + pBuf += 32; + ielen -= (uint8_t)32; + (void)pCtx; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_FTInfo, + IES_FTInfo, + (uint8_t *)pDst, + sizeof(*pDst), + append_ie); + return status; +} /* End dot11f_unpack_ie_ft_info. */ + +#define SigIeFTInfo (0x003e) + + +uint32_t dot11f_unpack_ie_ht_caps(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEHTCaps *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp43__; + uint8_t tmp44__; + uint16_t tmp45__; + uint32_t tmp46__; + uint8_t tmp47__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp43__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->advCodingCap = tmp43__ >> 0 & 0x1; + pDst->supportedChannelWidthSet = tmp43__ >> 1 & 0x1; + pDst->mimoPowerSave = tmp43__ >> 2 & 0x3; + pDst->greenField = tmp43__ >> 4 & 0x1; + pDst->shortGI20MHz = tmp43__ >> 5 & 0x1; + pDst->shortGI40MHz = tmp43__ >> 6 & 0x1; + pDst->txSTBC = tmp43__ >> 7 & 0x1; + pDst->rxSTBC = tmp43__ >> 8 & 0x3; + pDst->delayedBA = tmp43__ >> 10 & 0x1; + pDst->maximalAMSDUsize = tmp43__ >> 11 & 0x1; + pDst->dsssCckMode40MHz = tmp43__ >> 12 & 0x1; + pDst->psmp = tmp43__ >> 13 & 0x1; + pDst->stbcControlFrame = tmp43__ >> 14 & 0x1; + pDst->lsigTXOPProtection = tmp43__ >> 15 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp44__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->maxRxAMPDUFactor = tmp44__ >> 0 & 0x3; + pDst->mpduDensity = tmp44__ >> 2 & 0x7; + pDst->reserved1 = tmp44__ >> 5 & 0x7; + if (unlikely(ielen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->supportedMCSSet, pBuf, 16); + pBuf += 16; + ielen -= (uint8_t)16; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp45__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->pco = tmp45__ >> 0 & 0x1; + pDst->transitionTime = tmp45__ >> 1 & 0x3; + pDst->reserved2 = tmp45__ >> 3 & 0x1f; + pDst->mcsFeedback = tmp45__ >> 8 & 0x3; + pDst->reserved3 = tmp45__ >> 10 & 0x3f; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &tmp46__, pBuf, 0); + pBuf += 4; + ielen -= 4; + pDst->txBF = tmp46__ >> 0 & 0x1; + pDst->rxStaggeredSounding = tmp46__ >> 1 & 0x1; + pDst->txStaggeredSounding = tmp46__ >> 2 & 0x1; + pDst->rxZLF = tmp46__ >> 3 & 0x1; + pDst->txZLF = tmp46__ >> 4 & 0x1; + pDst->implicitTxBF = tmp46__ >> 5 & 0x1; + pDst->calibration = tmp46__ >> 6 & 0x3; + pDst->explicitCSITxBF = tmp46__ >> 8 & 0x1; + pDst->explicitUncompressedSteeringMatrix = tmp46__ >> 9 & 0x1; + pDst->explicitBFCSIFeedback = tmp46__ >> 10 & 0x7; + pDst->explicitUncompressedSteeringMatrixFeedback = tmp46__ >> 13 & 0x7; + pDst->explicitCompressedSteeringMatrixFeedback = tmp46__ >> 16 & 0x7; + pDst->csiNumBFAntennae = tmp46__ >> 19 & 0x3; + pDst->uncompressedSteeringMatrixBFAntennae = tmp46__ >> 21 & 0x3; + pDst->compressedSteeringMatrixBFAntennae = tmp46__ >> 23 & 0x3; + pDst->reserved4 = tmp46__ >> 25 & 0x7f; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp47__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->antennaSelection = tmp47__ >> 0 & 0x1; + pDst->explicitCSIFeedbackTx = tmp47__ >> 1 & 0x1; + pDst->antennaIndicesFeedbackTx = tmp47__ >> 2 & 0x1; + pDst->explicitCSIFeedback = tmp47__ >> 3 & 0x1; + pDst->antennaIndicesFeedback = tmp47__ >> 4 & 0x1; + pDst->rxAS = tmp47__ >> 5 & 0x1; + pDst->txSoundingPPDUs = tmp47__ >> 6 & 0x1; + pDst->reserved5 = tmp47__ >> 7 & 0x1; + pDst->num_rsvd = (uint8_t)(ielen); + if (ielen > 32) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->rsvd, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ht_caps. */ + +#define SigIeHTCaps (0x003f) + + +uint32_t dot11f_unpack_ie_ht_info(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEHTInfo *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp48__; + uint16_t tmp49__; + uint16_t tmp50__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->primaryChannel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp48__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->secondaryChannelOffset = tmp48__ >> 0 & 0x3; + pDst->recommendedTxWidthSet = tmp48__ >> 2 & 0x1; + pDst->rifsMode = tmp48__ >> 3 & 0x1; + pDst->controlledAccessOnly = tmp48__ >> 4 & 0x1; + pDst->serviceIntervalGranularity = tmp48__ >> 5 & 0x7; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp49__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->opMode = tmp49__ >> 0 & 0x3; + pDst->nonGFDevicesPresent = tmp49__ >> 2 & 0x1; + pDst->transmitBurstLimit = tmp49__ >> 3 & 0x1; + pDst->obssNonHTStaPresent = tmp49__ >> 4 & 0x1; + pDst->chan_center_freq_seg2 = tmp49__ >> 5 & 0xff; + pDst->reserved = tmp49__ >> 13 & 0x7; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp50__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->basicSTBCMCS = tmp50__ >> 0 & 0x7f; + pDst->dualCTSProtection = tmp50__ >> 7 & 0x1; + pDst->secondaryBeacon = tmp50__ >> 8 & 0x1; + pDst->lsigTXOPProtectionFullSupport = tmp50__ >> 9 & 0x1; + pDst->pcoActive = tmp50__ >> 10 & 0x1; + pDst->pcoPhase = tmp50__ >> 11 & 0x1; + pDst->reserved2 = tmp50__ >> 12 & 0xf; + if (unlikely(ielen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->basicMCSSet, pBuf, 16); + pBuf += 16; + ielen -= (uint8_t)16; + pDst->num_rsvd = (uint8_t)(ielen); + if (ielen > 32) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->rsvd, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ht_info. */ + +#define SigIeHTInfo (0x0040) + + +uint32_t dot11f_unpack_ie_ibss_params(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEIBSSParams *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->atim, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ibss_params. */ + +#define SigIeIBSSParams (0x0041) + + +uint32_t dot11f_unpack_ie_link_identifier(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIELinkIdentifier *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->bssid, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->InitStaAddr, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->RespStaAddr, pBuf, 6); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_link_identifier. */ + +#define SigIeLinkIdentifier (0x0042) + + +static const tTLVDefn TLVS_MBO_IE[] = { + { offsetof(tDot11fIEMBO_IE, mbo_ap_cap), offsetof(tDot11fTLVmbo_ap_cap, + present), "mbo_ap_cap", SigTlvmbo_ap_cap, DOT11F_TLV_MBO_AP_CAP, + 0, 3, 3, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, non_prefferd_chan_rep), + offsetof(tDot11fTLVnon_prefferd_chan_rep, present), + "non_prefferd_chan_rep", SigTlvnon_prefferd_chan_rep, + DOT11F_TLV_NON_PREFFERD_CHAN_REP, 0, 6, 257, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, cellular_data_cap), + offsetof(tDot11fTLVcellular_data_cap, present), "cellular_data_cap", + SigTlvcellular_data_cap, DOT11F_TLV_CELLULAR_DATA_CAP, + 0, 3, 3, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, assoc_disallowed), + offsetof(tDot11fTLVassoc_disallowed, present), "assoc_disallowed", + SigTlvassoc_disallowed, DOT11F_TLV_ASSOC_DISALLOWED, + 0, 3, 3, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, cellular_data_con_pref), + offsetof(tDot11fTLVcellular_data_con_pref, present), + "cellular_data_con_pref", SigTlvcellular_data_con_pref, + DOT11F_TLV_CELLULAR_DATA_CON_PREF, 0, 3, 3, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, transition_reason), + offsetof(tDot11fTLVtransition_reason, present), "transition_reason", + SigTlvtransition_reason, DOT11F_TLV_TRANSITION_REASON, + 0, 3, 3, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, transition_reject_reason), + offsetof(tDot11fTLVtransition_reject_reason, present), + "transition_reject_reason", SigTlvtransition_reject_reason, + DOT11F_TLV_TRANSITION_REJECT_REASON, 0, 3, 3, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, assoc_retry_delay), + offsetof(tDot11fTLVassoc_retry_delay, present), "assoc_retry_delay", + SigTlvassoc_retry_delay, DOT11F_TLV_ASSOC_RETRY_DELAY, + 0, 4, 4, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, oce_cap), offsetof(tDot11fTLVoce_cap, + present), "oce_cap", SigTlvoce_cap, DOT11F_TLV_OCE_CAP, + 0, 3, 3, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, rssi_assoc_rej), + offsetof(tDot11fTLVrssi_assoc_rej, present), "rssi_assoc_rej", + SigTlvrssi_assoc_rej, DOT11F_TLV_RSSI_ASSOC_REJ, 0, 4, 4, 0, 1, 1, 0, }, + { offsetof(tDot11fIEMBO_IE, reduced_wan_metrics), + offsetof(tDot11fTLVreduced_wan_metrics, present), "reduced_wan_metrics", + SigTlvreduced_wan_metrics, DOT11F_TLV_REDUCED_WAN_METRICS, + 0, 3, 3, 0, 1, 1, 0, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_MBO_IE(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEMBO_IE *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_MBO_IE, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_MBO_IE. */ + +#define SigIeMBO_IE (0x0043) + + +static const tFFDefn FFS_reportBeacon[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_reportBeacon[] = { + { offsetof(tDot11fIEMeasurementReport, + report.Beacon.BeaconReportFrmBody), + offsetof(tDot11fIEBeaconReportFrmBody, present), 0, "BeaconReportFrmBody", + 0, 2, 226, SigIeBeaconReportFrmBody, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BEACONREPORTFRMBODY, 0, 0, }, + { offsetof(tDot11fIEMeasurementReport, + report.Beacon.beacon_report_frm_body_fragment_id), + offsetof(tDot11fIEbeacon_report_frm_body_fragment_id, present), 0, + "beacon_report_frm_body_fragment_id", + 0, 4, 4, SigIebeacon_report_frm_body_fragment_id, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BEACON_REPORT_FRM_BODY_FRAGMENT_ID, 0, 0, }, + { offsetof(tDot11fIEMeasurementReport, + report.Beacon.last_beacon_report_indication), + offsetof(tDot11fIElast_beacon_report_indication, present), 0, + "last_beacon_report_indication", + 0, 3, 3, SigIelast_beacon_report_indication, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LAST_BEACON_REPORT_INDICATION, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +uint32_t dot11f_unpack_ie_measurement_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEMeasurementReport *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp51__; + uint8_t tmp52__; + uint8_t tmp53__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->token = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp51__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->late = tmp51__ >> 0 & 0x1; + pDst->incapable = tmp51__ >> 1 & 0x1; + pDst->refused = tmp51__ >> 2 & 0x1; + pDst->unused = tmp51__ >> 3 & 0x1f; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->type = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (!ielen) { + return 0U; + } else { + switch (pDst->type) { + case 0: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.Basic.channel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohq(pCtx, &pDst->report.Basic.meas_start_time, pBuf, 0); + pBuf += 8; + ielen -= (uint8_t)8; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->report.Basic.meas_duration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp52__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->report.Basic.bss = tmp52__ >> 0 & 0x1; + pDst->report.Basic.ofdm_preamble = tmp52__ >> 1 & 0x1; + pDst->report.Basic.unid_signal = tmp52__ >> 2 & 0x1; + pDst->report.Basic.rader = tmp52__ >> 3 & 0x1; + pDst->report.Basic.unmeasured = tmp52__ >> 4 & 0x1; + pDst->report.Basic.unused = tmp52__ >> 5 & 0x7; + break; + case 1: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.CCA.channel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohq(pCtx, &pDst->report.CCA.meas_start_time, pBuf, 0); + pBuf += 8; + ielen -= (uint8_t)8; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->report.CCA.meas_duration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.CCA.cca_busy_fraction = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + break; + case 2: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.RPIHistogram.channel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohq(pCtx, &pDst->report.RPIHistogram.meas_start_time, pBuf, 0); + pBuf += 8; + ielen -= (uint8_t)8; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->report.RPIHistogram.meas_duration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.RPIHistogram.rpi0_density = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.RPIHistogram.rpi1_density = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.RPIHistogram.rpi2_density = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.RPIHistogram.rpi3_density = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.RPIHistogram.rpi4_density = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.RPIHistogram.rpi5_density = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.RPIHistogram.rpi6_density = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.RPIHistogram.rpi7_density = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + break; + case 5: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.Beacon.regClass = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.Beacon.channel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohq(pCtx, &pDst->report.Beacon.meas_start_time, pBuf, 0); + pBuf += 8; + ielen -= (uint8_t)8; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->report.Beacon.meas_duration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp53__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->report.Beacon.condensed_PHY = tmp53__ >> 0 & 0x7f; + pDst->report.Beacon.reported_frame_type = tmp53__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.Beacon.RCPI = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.Beacon.RSNI = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->report.Beacon.BSSID, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->report.Beacon.antenna_id = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->report.Beacon.parent_TSF, pBuf, 0); + pBuf += 4; + ielen -= (uint8_t)4; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_reportBeacon, + IES_reportBeacon, + (uint8_t *)pDst, + sizeof(*pDst), append_ie); + break; + } + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_measurement_report. */ + +#define SigIeMeasurementReport (0x0044) + + +static const tFFDefn FFS_measurement_requestBeacon[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_measurement_requestBeacon[] = { + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.Beacon.SSID), offsetof(tDot11fIESSID, present), 0, + "SSID", 0, 2, 34, SigIeSSID, {0, 0, 0, 0, 0}, 0, DOT11F_EID_SSID, 0, 0, }, + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.Beacon.BeaconReporting), + offsetof(tDot11fIEBeaconReporting, present), 0, "BeaconReporting", + 0, 4, 4, SigIeBeaconReporting, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BEACONREPORTING, 0, 0, }, + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.Beacon.BcnReportingDetail), + offsetof(tDot11fIEBcnReportingDetail, present), 0, "BcnReportingDetail", + 0, 3, 3, SigIeBcnReportingDetail, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BCNREPORTINGDETAIL, 0, 0, }, + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.Beacon.RequestedInfo), + offsetof(tDot11fIERequestedInfo, present), 0, "RequestedInfo", + 0, 2, 257, SigIeRequestedInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_REQUESTEDINFO, 0, 0, }, + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.Beacon.APChannelReport), + offsetof(tDot11fIEAPChannelReport, present), + offsetof(tDot11fIEMeasurementRequest, measurement_request.Beacon.num_APChannelReport), "APChannelReport", 2, 3, 53, SigIeAPChannelReport, {0, 0, 0, 0, 0}, 0, DOT11F_EID_APCHANNELREPORT, 0, 0, }, + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.Beacon.last_beacon_report_indication), + offsetof(tDot11fIElast_beacon_report_indication, present), 0, + "last_beacon_report_indication", + 0, 3, 3, SigIelast_beacon_report_indication, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LAST_BEACON_REPORT_INDICATION, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +static const tFFDefn FFS_measurement_requestlci[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_measurement_requestlci[] = { + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.lci.azimuth_req), offsetof(tDot11fIEazimuth_req, + present), 0, "azimuth_req", 0, 3, 3, SigIeazimuth_req, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_AZIMUTH_REQ, 0, 0, }, + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.lci.req_mac_addr), offsetof(tDot11fIEreq_mac_addr, + present), 0, "req_mac_addr", 0, 8, 8, SigIereq_mac_addr, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_REQ_MAC_ADDR, 0, 0, }, + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.lci.tgt_mac_addr), offsetof(tDot11fIEtgt_mac_addr, + present), 0, "tgt_mac_addr", 0, 8, 8, SigIetgt_mac_addr, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TGT_MAC_ADDR, 0, 0, }, + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.lci.max_age), offsetof(tDot11fIEmax_age, present), 0, + "max_age", 0, 4, 4, SigIemax_age, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MAX_AGE, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +static const tFFDefn FFS_measurement_requestftmrr[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_measurement_requestftmrr[] = { + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.ftmrr.neighbor_rpt), offsetof(tDot11fIEneighbor_rpt, + present), 0, "neighbor_rpt", 0, 15, 548, SigIeneighbor_rpt, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_NEIGHBOR_RPT, 0, 0, }, + { offsetof(tDot11fIEMeasurementRequest, + measurement_request.ftmrr.max_age), offsetof(tDot11fIEmax_age, present), + 0, "max_age", 0, 4, 4, SigIemax_age, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MAX_AGE, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +uint32_t dot11f_unpack_ie_measurement_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEMeasurementRequest *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp54__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_token = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp54__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->parallel = tmp54__ >> 0 & 0x1; + pDst->enable = tmp54__ >> 1 & 0x1; + pDst->request = tmp54__ >> 2 & 0x1; + pDst->report = tmp54__ >> 3 & 0x1; + pDst->durationMandatory = tmp54__ >> 4 & 0x1; + pDst->unused = tmp54__ >> 5 & 0x7; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_type = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + switch (pDst->measurement_type) { + case 0: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_request.Basic.channel_no = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->measurement_request.Basic.meas_start_time, pBuf, 8); + pBuf += 8; + ielen -= (uint8_t)8; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->measurement_request.Basic.meas_duration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + break; + case 1: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_request.CCA.channel_no = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->measurement_request.CCA.meas_start_time, pBuf, 8); + pBuf += 8; + ielen -= (uint8_t)8; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->measurement_request.CCA.meas_duration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + break; + case 2: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_request.RPIHistogram.channel_no = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->measurement_request.RPIHistogram.meas_start_time, pBuf, 8); + pBuf += 8; + ielen -= (uint8_t)8; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->measurement_request.RPIHistogram.meas_duration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + break; + case 5: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_request.Beacon.regClass = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_request.Beacon.channel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->measurement_request.Beacon.randomization, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->measurement_request.Beacon.meas_duration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_request.Beacon.meas_mode = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->measurement_request.Beacon.BSSID, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_measurement_requestBeacon, + IES_measurement_requestBeacon, + (uint8_t *)pDst, + sizeof(*pDst), append_ie); + break; + case 8: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_request.lci.loc_subject = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_measurement_requestlci, + IES_measurement_requestlci, + (uint8_t *)pDst, + sizeof(*pDst), append_ie); + break; + case 16: + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->measurement_request.ftmrr.random_interval, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->measurement_request.ftmrr.min_ap_count = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_measurement_requestftmrr, + IES_measurement_requestftmrr, + (uint8_t *)pDst, + sizeof(*pDst), append_ie); + break; + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_measurement_request. */ + +#define SigIeMeasurementRequest (0x0045) + + +uint32_t dot11f_unpack_ie_mobility_domain(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEMobilityDomain *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp55__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->MDID, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp55__ = *pBuf; + pDst->overDSCap = tmp55__ >> 0 & 0x1; + pDst->resourceReqCap = tmp55__ >> 1 & 0x1; + pDst->reserved = tmp55__ >> 2 & 0x3f; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_mobility_domain. */ + +#define SigIeMobilityDomain (0x0046) + + +static const tFFDefn FFS_NeighborReport[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_NeighborReport[] = { + { offsetof(tDot11fIENeighborReport, TSFInfo), offsetof(tDot11fIETSFInfo, + present), 0, "TSFInfo", 0, 6, 6, SigIeTSFInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TSFINFO, 0, 0, }, + { offsetof(tDot11fIENeighborReport, CondensedCountryStr), + offsetof(tDot11fIECondensedCountryStr, present), 0, "CondensedCountryStr", + 0, 4, 4, SigIeCondensedCountryStr, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CONDENSEDCOUNTRYSTR, 0, 0, }, + { offsetof(tDot11fIENeighborReport, MeasurementPilot), + offsetof(tDot11fIEMeasurementPilot, present), 0, "MeasurementPilot", + 0, 3, 258, SigIeMeasurementPilot, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MEASUREMENTPILOT, 0, 0, }, + { offsetof(tDot11fIENeighborReport, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fIENeighborReport, MultiBssid), + offsetof(tDot11fIEMultiBssid, present), 0, "MultiBssid", + 0, 3, 258, SigIeMultiBssid, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MULTIBSSID, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +uint32_t dot11f_unpack_ie_neighbor_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIENeighborReport *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp56__; + uint8_t tmp57__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->bssid, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp56__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->APReachability = tmp56__ >> 0 & 0x3; + pDst->Security = tmp56__ >> 2 & 0x1; + pDst->KeyScope = tmp56__ >> 3 & 0x1; + pDst->SpecMgmtCap = tmp56__ >> 4 & 0x1; + pDst->QosCap = tmp56__ >> 5 & 0x1; + pDst->apsd = tmp56__ >> 6 & 0x1; + pDst->rrm = tmp56__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp57__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->DelayedBA = tmp57__ >> 0 & 0x1; + pDst->ImmBA = tmp57__ >> 1 & 0x1; + pDst->MobilityDomain = tmp57__ >> 2 & 0x1; + pDst->reserved = tmp57__ >> 3 & 0x1f; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->reserved1, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->regulatoryClass = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->channel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->PhyType = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + (void)pCtx; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_NeighborReport, + IES_NeighborReport, + (uint8_t *)pDst, + sizeof(*pDst), + append_ie); + return status; +} /* End dot11f_unpack_ie_neighbor_report. */ + +#define SigIeNeighborReport (0x0047) + + +uint32_t dot11f_unpack_ie_obss_scan_parameters(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEOBSSScanParameters *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->obssScanPassiveDwell, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->obssScanActiveDwell, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->bssChannelWidthTriggerScanInterval, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->obssScanPassiveTotalPerChannel, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->obssScanActiveTotalPerChannel, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->bssWidthChannelTransitionDelayFactor, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->obssScanActivityThreshold, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_obss_scan_parameters. */ + +#define SigIeOBSSScanParameters (0x0048) + + +uint32_t dot11f_unpack_ie_operating_mode(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEOperatingMode *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp58__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp58__ = *pBuf; + pDst->chanWidth = tmp58__ >> 0 & 0x3; + pDst->vht_160_80p80_supp = tmp58__ >> 2 & 0x1; + pDst->no_ldpc = tmp58__ >> 3 & 0x1; + pDst->rxNSS = tmp58__ >> 4 & 0x7; + pDst->rxNSSType = tmp58__ >> 7 & 0x1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_operating_mode. */ + +#define SigIeOperatingMode (0x0049) + + +static const tTLVDefn TLVS_P2PAssocReq[] = { + { offsetof(tDot11fIEP2PAssocReq, P2PCapability), + offsetof(tDot11fTLVP2PCapability, present), "P2PCapability", + SigTlvP2PCapability, DOT11F_TLV_P2PCAPABILITY, 0, 5, 5, 1, 1, 2, 0, }, + { offsetof(tDot11fIEP2PAssocReq, ExtendedListenTiming), + offsetof(tDot11fTLVExtendedListenTiming, present), + "ExtendedListenTiming", SigTlvExtendedListenTiming, + DOT11F_TLV_EXTENDEDLISTENTIMING, 0, 7, 7, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PAssocReq, P2PDeviceInfo), + offsetof(tDot11fTLVP2PDeviceInfo, present), "P2PDeviceInfo", + SigTlvP2PDeviceInfo, DOT11F_TLV_P2PDEVICEINFO, 0, 19, 55, 1, 1, 2, 0, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_p2_p_assoc_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEP2PAssocReq *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_P2PAssocReq, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_p2_p_assoc_req. */ + +#define SigIeP2PAssocReq (0x004a) + + +static const tTLVDefn TLVS_P2PAssocRes[] = { + { offsetof(tDot11fIEP2PAssocRes, P2PStatus), + offsetof(tDot11fTLVP2PStatus, present), "P2PStatus", SigTlvP2PStatus, + DOT11F_TLV_P2PSTATUS, 0, 4, 4, 1, 1, 2, 0, }, + { offsetof(tDot11fIEP2PAssocRes, ExtendedListenTiming), + offsetof(tDot11fTLVExtendedListenTiming, present), + "ExtendedListenTiming", SigTlvExtendedListenTiming, + DOT11F_TLV_EXTENDEDLISTENTIMING, 0, 7, 7, 0, 1, 2, 0, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_p2_p_assoc_res(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEP2PAssocRes *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_P2PAssocRes, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_p2_p_assoc_res. */ + +#define SigIeP2PAssocRes (0x004b) + + +static const tTLVDefn TLVS_P2PBeacon[] = { + { offsetof(tDot11fIEP2PBeacon, P2PCapability), + offsetof(tDot11fTLVP2PCapability, present), "P2PCapability", + SigTlvP2PCapability, DOT11F_TLV_P2PCAPABILITY, 0, 5, 5, 1, 1, 2, 0, }, + { offsetof(tDot11fIEP2PBeacon, P2PDeviceId), + offsetof(tDot11fTLVP2PDeviceId, present), "P2PDeviceId", + SigTlvP2PDeviceId, DOT11F_TLV_P2PDEVICEID, 0, 9, 9, 1, 1, 2, 0, }, + { offsetof(tDot11fIEP2PBeacon, NoticeOfAbsence), + offsetof(tDot11fTLVNoticeOfAbsence, present), "NoticeOfAbsence", + SigTlvNoticeOfAbsence, DOT11F_TLV_NOTICEOFABSENCE, + 0, 5, 41, 0, 1, 2, 0, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_p2_p_beacon(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEP2PBeacon *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_P2PBeacon, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_p2_p_beacon. */ + +#define SigIeP2PBeacon (0x004c) + + +static const tTLVDefn TLVS_P2PBeaconProbeRes[] = { + { offsetof(tDot11fIEP2PBeaconProbeRes, P2PCapability), + offsetof(tDot11fTLVP2PCapability, present), "P2PCapability", + SigTlvP2PCapability, DOT11F_TLV_P2PCAPABILITY, 0, 5, 5, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PBeaconProbeRes, P2PDeviceId), + offsetof(tDot11fTLVP2PDeviceId, present), "P2PDeviceId", + SigTlvP2PDeviceId, DOT11F_TLV_P2PDEVICEID, 0, 9, 9, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PBeaconProbeRes, ExtendedListenTiming), + offsetof(tDot11fTLVExtendedListenTiming, present), + "ExtendedListenTiming", SigTlvExtendedListenTiming, + DOT11F_TLV_EXTENDEDLISTENTIMING, 0, 7, 7, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PBeaconProbeRes, NoticeOfAbsence), + offsetof(tDot11fTLVNoticeOfAbsence, present), "NoticeOfAbsence", + SigTlvNoticeOfAbsence, DOT11F_TLV_NOTICEOFABSENCE, + 0, 5, 41, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PBeaconProbeRes, P2PDeviceInfo), + offsetof(tDot11fTLVP2PDeviceInfo, present), "P2PDeviceInfo", + SigTlvP2PDeviceInfo, DOT11F_TLV_P2PDEVICEINFO, 0, 19, 55, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PBeaconProbeRes, P2PGroupInfo), + offsetof(tDot11fTLVP2PGroupInfo, present), "P2PGroupInfo", + SigTlvP2PGroupInfo, DOT11F_TLV_P2PGROUPINFO, 0, 3, 1027, 0, 1, 2, 0, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_p2_p_beacon_probe_res(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEP2PBeaconProbeRes *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_P2PBeaconProbeRes, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_p2_p_beacon_probe_res. */ + +#define SigIeP2PBeaconProbeRes (0x004d) + + +static const tTLVDefn TLVS_P2PDeAuth[] = { + { offsetof(tDot11fIEP2PDeAuth, MinorReasonCode), + offsetof(tDot11fTLVMinorReasonCode, present), "MinorReasonCode", + SigTlvMinorReasonCode, DOT11F_TLV_MINORREASONCODE, + 0, 4, 4, 1, 1, 2, 0, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_p2_p_de_auth(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEP2PDeAuth *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_P2PDeAuth, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_p2_p_de_auth. */ + +#define SigIeP2PDeAuth (0x004e) + + +static const tTLVDefn TLVS_P2PDisAssoc[] = { + { offsetof(tDot11fIEP2PDisAssoc, MinorReasonCode), + offsetof(tDot11fTLVMinorReasonCode, present), "MinorReasonCode", + SigTlvMinorReasonCode, DOT11F_TLV_MINORREASONCODE, + 0, 4, 4, 1, 1, 2, 0, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_p2_p_dis_assoc(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEP2PDisAssoc *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_P2PDisAssoc, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_p2_p_dis_assoc. */ + +#define SigIeP2PDisAssoc (0x004f) + + +uint32_t dot11f_unpack_ie_p2_pie_opaque(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEP2PIEOpaque *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + if (ielen > 249) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_p2_pie_opaque. */ + +#define SigIeP2PIEOpaque (0x0050) + + +static const tTLVDefn TLVS_P2PProbeReq[] = { + { offsetof(tDot11fIEP2PProbeReq, P2PCapability), + offsetof(tDot11fTLVP2PCapability, present), "P2PCapability", + SigTlvP2PCapability, DOT11F_TLV_P2PCAPABILITY, 0, 5, 5, 1, 1, 2, 0, }, + { offsetof(tDot11fIEP2PProbeReq, P2PDeviceId), + offsetof(tDot11fTLVP2PDeviceId, present), "P2PDeviceId", + SigTlvP2PDeviceId, DOT11F_TLV_P2PDEVICEID, 0, 9, 9, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PProbeReq, ListenChannel), + offsetof(tDot11fTLVListenChannel, present), "ListenChannel", + SigTlvListenChannel, DOT11F_TLV_LISTENCHANNEL, 0, 8, 8, 1, 1, 2, 0, }, + { offsetof(tDot11fIEP2PProbeReq, ExtendedListenTiming), + offsetof(tDot11fTLVExtendedListenTiming, present), + "ExtendedListenTiming", SigTlvExtendedListenTiming, + DOT11F_TLV_EXTENDEDLISTENTIMING, 0, 7, 7, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PProbeReq, OperatingChannel), + offsetof(tDot11fTLVOperatingChannel, present), "OperatingChannel", + SigTlvOperatingChannel, DOT11F_TLV_OPERATINGCHANNEL, + 0, 8, 8, 0, 1, 2, 0, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_p2_p_probe_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEP2PProbeReq *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_P2PProbeReq, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_p2_p_probe_req. */ + +#define SigIeP2PProbeReq (0x0051) + + +static const tTLVDefn TLVS_P2PProbeRes[] = { + { offsetof(tDot11fIEP2PProbeRes, P2PCapability), + offsetof(tDot11fTLVP2PCapability, present), "P2PCapability", + SigTlvP2PCapability, DOT11F_TLV_P2PCAPABILITY, 0, 5, 5, 1, 1, 2, 0, }, + { offsetof(tDot11fIEP2PProbeRes, ExtendedListenTiming), + offsetof(tDot11fTLVExtendedListenTiming, present), + "ExtendedListenTiming", SigTlvExtendedListenTiming, + DOT11F_TLV_EXTENDEDLISTENTIMING, 0, 7, 7, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PProbeRes, NoticeOfAbsence), + offsetof(tDot11fTLVNoticeOfAbsence, present), "NoticeOfAbsence", + SigTlvNoticeOfAbsence, DOT11F_TLV_NOTICEOFABSENCE, + 0, 5, 41, 0, 1, 2, 0, }, + { offsetof(tDot11fIEP2PProbeRes, P2PDeviceInfo), + offsetof(tDot11fTLVP2PDeviceInfo, present), "P2PDeviceInfo", + SigTlvP2PDeviceInfo, DOT11F_TLV_P2PDEVICEINFO, 0, 19, 55, 1, 1, 2, 0, }, + { offsetof(tDot11fIEP2PProbeRes, P2PGroupInfo), + offsetof(tDot11fTLVP2PGroupInfo, present), "P2PGroupInfo", + SigTlvP2PGroupInfo, DOT11F_TLV_P2PGROUPINFO, 0, 3, 1027, 0, 1, 2, 0, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_p2_p_probe_res(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEP2PProbeRes *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_P2PProbeRes, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_p2_p_probe_res. */ + +#define SigIeP2PProbeRes (0x0052) + + +uint32_t dot11f_unpack_ie_pti_control(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEPTIControl *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->tid = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->sequence_control, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_pti_control. */ + +#define SigIePTIControl (0x0053) + + +uint32_t dot11f_unpack_ie_pu_buffer_status(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEPUBufferStatus *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp59__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp59__ = *pBuf; + pDst->ac_bk_traffic_aval = tmp59__ >> 0 & 0x1; + pDst->ac_be_traffic_aval = tmp59__ >> 1 & 0x1; + pDst->ac_vi_traffic_aval = tmp59__ >> 2 & 0x1; + pDst->ac_vo_traffic_aval = tmp59__ >> 3 & 0x1; + pDst->reserved = tmp59__ >> 4 & 0xf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_pu_buffer_status. */ + +#define SigIePUBufferStatus (0x0054) + + +uint32_t dot11f_unpack_ie_power_caps(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEPowerCaps *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->minTxPower = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->maxTxPower = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_power_caps. */ + +#define SigIePowerCaps (0x0055) + + +uint32_t dot11f_unpack_ie_power_constraints(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEPowerConstraints *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->localPowerConstraints = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_power_constraints. */ + +#define SigIePowerConstraints (0x0056) + + +uint32_t dot11f_unpack_ie_qbss_load(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEQBSSLoad *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->stacount, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->chautil = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->avail, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_qbss_load. */ + +#define SigIeQBSSLoad (0x0057) + + +uint32_t dot11f_unpack_ie_QComVendorIE(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEQComVendorIE *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->type = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->channel = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_QComVendorIE. */ + +#define SigIeQComVendorIE (0x0058) + + +uint32_t dot11f_unpack_ie_qos_caps_ap(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEQOSCapsAp *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp60__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp60__ = *pBuf; + pDst->count = tmp60__ >> 0 & 0xf; + pDst->qack = tmp60__ >> 4 & 0x1; + pDst->qreq = tmp60__ >> 5 & 0x1; + pDst->txopreq = tmp60__ >> 6 & 0x1; + pDst->reserved = tmp60__ >> 7 & 0x1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_qos_caps_ap. */ + +#define SigIeQOSCapsAp (0x0059) + + +uint32_t dot11f_unpack_ie_qos_caps_station(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEQOSCapsStation *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp61__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp61__ = *pBuf; + pDst->acvo_uapsd = tmp61__ >> 0 & 0x1; + pDst->acvi_uapsd = tmp61__ >> 1 & 0x1; + pDst->acbk_uapsd = tmp61__ >> 2 & 0x1; + pDst->acbe_uapsd = tmp61__ >> 3 & 0x1; + pDst->qack = tmp61__ >> 4 & 0x1; + pDst->max_sp_length = tmp61__ >> 5 & 0x3; + pDst->more_data_ack = tmp61__ >> 7 & 0x1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_qos_caps_station. */ + +#define SigIeQOSCapsStation (0x005a) + + +uint32_t dot11f_unpack_ie_qos_map_set(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEQosMapSet *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_dscp_exceptions = (uint8_t)(ielen); + if (ielen > 58) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->dscp_exceptions, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_qos_map_set. */ + +#define SigIeQosMapSet (0x005b) + + +uint32_t dot11f_unpack_ie_quiet(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEQuiet *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->count = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->period = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->duration, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->offset, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_quiet. */ + +#define SigIeQuiet (0x005c) + + +uint32_t dot11f_unpack_ie_rcpiie(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIERCPIIE *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->rcpi = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_rcpiie. */ + +#define SigIeRCPIIE (0x005d) + + +static const tFFDefn FFS_RICDataDesc[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_RICDataDesc[] = { + { offsetof(tDot11fIERICDataDesc, RICData), offsetof(tDot11fIERICData, + present), 0, "RICData", 0, 6, 6, SigIeRICData, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RICDATA, 0, 1, }, + { offsetof(tDot11fIERICDataDesc, RICDescriptor), + offsetof(tDot11fIERICDescriptor, present), 0, "RICDescriptor", + 0, 3, 258, SigIeRICDescriptor, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RICDESCRIPTOR, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, TSPEC), offsetof(tDot11fIETSPEC, + present), 0, "TSPEC", 0, 57, 57, SigIeTSPEC, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TSPEC, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, TCLAS), offsetof(tDot11fIETCLAS, + present), offsetof(tDot11fIERICDataDesc, num_TCLAS), "TCLAS", + 2, 7, 45, SigIeTCLAS, {0, 0, 0, 0, 0}, 0, DOT11F_EID_TCLAS, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, TCLASSPROC), + offsetof(tDot11fIETCLASSPROC, present), 0, "TCLASSPROC", + 0, 3, 3, SigIeTCLASSPROC, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TCLASSPROC, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, TSDelay), offsetof(tDot11fIETSDelay, + present), 0, "TSDelay", 0, 6, 6, SigIeTSDelay, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TSDELAY, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, Schedule), offsetof(tDot11fIESchedule, + present), 0, "Schedule", 0, 16, 16, SigIeSchedule, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SCHEDULE, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, WMMTSPEC), offsetof(tDot11fIEWMMTSPEC, + present), 0, "WMMTSPEC", 0, 63, 63, SigIeWMMTSPEC, {0, 80, 242, 2, 2}, + 5, DOT11F_EID_WMMTSPEC, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, WMMTCLAS), offsetof(tDot11fIEWMMTCLAS, + present), offsetof(tDot11fIERICDataDesc, num_WMMTCLAS), "WMMTCLAS", + 2, 13, 51, SigIeWMMTCLAS, {0, 80, 242, 2, 6}, + 5, DOT11F_EID_WMMTCLAS, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, WMMTCLASPROC), + offsetof(tDot11fIEWMMTCLASPROC, present), 0, "WMMTCLASPROC", + 0, 9, 9, SigIeWMMTCLASPROC, {0, 80, 242, 2, 7}, + 5, DOT11F_EID_WMMTCLASPROC, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, WMMTSDelay), + offsetof(tDot11fIEWMMTSDelay, present), 0, "WMMTSDelay", + 0, 12, 12, SigIeWMMTSDelay, {0, 80, 242, 2, 8}, + 5, DOT11F_EID_WMMTSDELAY, 0, 0, }, + { offsetof(tDot11fIERICDataDesc, WMMSchedule), + offsetof(tDot11fIEWMMSchedule, present), 0, "WMMSchedule", + 0, 22, 22, SigIeWMMSchedule, {0, 80, 242, 2, 9}, + 5, DOT11F_EID_WMMSCHEDULE, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +uint32_t dot11f_unpack_ie_ric_data_desc(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIERICDataDesc *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + (void)pCtx; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_RICDataDesc, + IES_RICDataDesc, + (uint8_t *)pDst, + sizeof(*pDst), + append_ie); + return status; +} /* End dot11f_unpack_ie_ric_data_desc. */ + +#define SigIeRICDataDesc (0x005e) + + +uint32_t dot11f_unpack_ie_rsn(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIERSN *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t def_cipher_suite[4] = {0x00, 0x0f, 0xac, 0x04}; + uint8_t def_akm_suite[4] = {0x00, 0x0f, 0xac, 0x01}; + + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->version, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (!ielen) { + pDst->RSN_Cap_present = 0U; + pDst->gp_mgmt_cipher_suite_present = 0U; + pDst->gp_cipher_suite_present = 1; + DOT11F_MEMCPY(pCtx, pDst->gp_cipher_suite, def_cipher_suite, 4); + pDst->pwise_cipher_suite_count = 1; + DOT11F_MEMCPY(pCtx, + pDst->pwise_cipher_suites, def_cipher_suite, 4); + pDst->akm_suite_cnt = 1; + DOT11F_MEMCPY(pCtx, pDst->akm_suite, def_akm_suite, 4); + pDst->pmkid_count = 0U; + return 0U; + } else { + pDst->gp_cipher_suite_present = 1; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->gp_cipher_suite, pBuf, 4); + pBuf += 4; + ielen -= (uint8_t)4; + } + if (!ielen) { + pDst->RSN_Cap_present = 0U; + pDst->gp_mgmt_cipher_suite_present = 0U; + pDst->pwise_cipher_suite_count = 1; + DOT11F_MEMCPY(pCtx, + pDst->pwise_cipher_suites, def_cipher_suite, 4); + pDst->akm_suite_cnt = 1; + DOT11F_MEMCPY(pCtx, pDst->akm_suite, def_akm_suite, 4); + pDst->pmkid_count = 0U; + return 0U; + } else { + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->pwise_cipher_suite_count, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + } + if (unlikely(ielen < pDst->pwise_cipher_suite_count * 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (!pDst->pwise_cipher_suite_count || + pDst->pwise_cipher_suite_count > 6) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->pwise_cipher_suites, pBuf, (pDst->pwise_cipher_suite_count * 4)); + pBuf += (pDst->pwise_cipher_suite_count * 4); + ielen -= (pDst->pwise_cipher_suite_count * 4); + if (!ielen) { + pDst->RSN_Cap_present = 0U; + pDst->gp_mgmt_cipher_suite_present = 0U; + pDst->akm_suite_cnt = 1; + DOT11F_MEMCPY(pCtx, pDst->akm_suite, def_akm_suite, 4); + pDst->pmkid_count = 0U; + return 0U; + } else { + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->akm_suite_cnt, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + } + if (unlikely(ielen < pDst->akm_suite_cnt * 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (!pDst->akm_suite_cnt || + pDst->akm_suite_cnt > 6) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->akm_suite, pBuf, (pDst->akm_suite_cnt * 4)); + pBuf += (pDst->akm_suite_cnt * 4); + ielen -= (pDst->akm_suite_cnt * 4); + if (!ielen) { + pDst->RSN_Cap_present = 0U; + pDst->gp_mgmt_cipher_suite_present = 0U; + pDst->pmkid_count = 0U; + return 0U; + } else { + pDst->RSN_Cap_present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->RSN_Cap, pBuf, 2); + pBuf += 2; + ielen -= (uint8_t)2; + } + if (!ielen) { + pDst->gp_mgmt_cipher_suite_present = 0U; + pDst->pmkid_count = 0U; + return 0U; + } else { + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->pmkid_count, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + } + if (unlikely(ielen < pDst->pmkid_count * 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->pmkid_count > 4) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->pmkid, pBuf, (pDst->pmkid_count * 16)); + pBuf += (pDst->pmkid_count * 16); + ielen -= (pDst->pmkid_count * 16); + if (!ielen) { + return 0U; + } else { + pDst->gp_mgmt_cipher_suite_present = 1; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->gp_mgmt_cipher_suite, pBuf, 4); + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_rsn. */ + +#define SigIeRSN (0x005f) + + +uint32_t dot11f_unpack_ie_rsniie(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIERSNIIE *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->rsni = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_rsniie. */ + +#define SigIeRSNIIE (0x0060) + + +uint32_t dot11f_unpack_ie_rsn_opaque(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIERSNOpaque *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + if (ielen > 253) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_rsn_opaque. */ + +#define SigIeRSNOpaque (0x0061) + + +uint32_t dot11f_unpack_ie_supp_channels(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIESuppChannels *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_bands = (uint8_t)(ielen / 2); + if (ielen > 48 * 2) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->bands, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_supp_channels. */ + +#define SigIeSuppChannels (0x0062) + + +uint32_t dot11f_unpack_ie_supp_operating_classes(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIESuppOperatingClasses *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_classes = (uint8_t)(ielen); + if (ielen > 32) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->classes, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_supp_operating_classes. */ + +#define SigIeSuppOperatingClasses (0x0063) + + +uint32_t dot11f_unpack_ie_supp_rates(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIESuppRates *pDst, + bool append_ie) +{ + uint8_t i; + uint8_t rate_indx = 0; + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + for (i = 0; i < ielen; i++) { + if ((DOT11F_IS_BG_RATE(pBuf[i] & 0x7F)) && + (rate_indx < 12)) { + pDst->rates[rate_indx++] = pBuf[i]; + } + } + + if (rate_indx == 0) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + pDst->num_rates = rate_indx; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_supp_rates. */ + +#define SigIeSuppRates (0x0064) + + +uint32_t dot11f_unpack_ie_tim(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIETIM *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->dtim_count = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->dtim_period = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->bmpctl = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + pDst->num_vbmp = (uint8_t)(ielen); + if (ielen > 251) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->vbmp, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_tim. */ + +#define SigIeTIM (0x0065) + + +uint32_t dot11f_unpack_ie_tpc_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIETPCReport *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->tx_power = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->link_margin = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_tpc_report. */ + +#define SigIeTPCReport (0x0066) + + +uint32_t dot11f_unpack_ie_tpc_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIETPCRequest *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_tpc_request. */ + +#define SigIeTPCRequest (0x0067) + + +uint32_t dot11f_unpack_ie_time_advertisement(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIETimeAdvertisement *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->timing_capabilities = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 10)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->time_value, pBuf, 10); + pBuf += 10; + ielen -= (uint8_t)10; + if (unlikely(ielen < 5)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->time_error, pBuf, 5); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_time_advertisement. */ + +#define SigIeTimeAdvertisement (0x0068) + + +uint32_t dot11f_unpack_ie_timeout_interval(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIETimeoutInterval *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->timeoutType = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &pDst->timeoutValue, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_timeout_interval. */ + +#define SigIeTimeoutInterval (0x0069) + + +uint32_t dot11f_unpack_ie_vht_ext_bss_load(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEVHTExtBssLoad *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->muMIMOCapStaCount = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->ssUnderUtil = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->FortyMHzUtil = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->EightyMHzUtil = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->OneSixtyMHzUtil = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_vht_ext_bss_load. */ + +#define SigIeVHTExtBssLoad (0x006a) + + +uint32_t dot11f_unpack_ie_vendor1_ie(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEVendor1IE *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_vendor1_ie. */ + +#define SigIeVendor1IE (0x006b) + + +uint32_t dot11f_unpack_ie_vendor3_ie(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEVendor3IE *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_vendor3_ie. */ + +#define SigIeVendor3IE (0x006c) + + +uint32_t dot11f_unpack_ie_wapi(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWAPI *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp62__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->version, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->akm_suite_count, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < pDst->akm_suite_count * 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->akm_suite_count > 4) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->akm_suites, pBuf, (pDst->akm_suite_count * 4)); + pBuf += (pDst->akm_suite_count * 4); + ielen -= (pDst->akm_suite_count * 4); + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->unicast_cipher_suite_count, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < pDst->unicast_cipher_suite_count * 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->unicast_cipher_suite_count > 4) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->unicast_cipher_suites, pBuf, (pDst->unicast_cipher_suite_count * 4)); + pBuf += (pDst->unicast_cipher_suite_count * 4); + ielen -= (pDst->unicast_cipher_suite_count * 4); + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->multicast_cipher_suite, pBuf, 4); + pBuf += 4; + ielen -= (uint8_t)4; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp62__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->preauth = tmp62__ >> 0 & 0x1; + pDst->reserved = tmp62__ >> 1 & 0x7fff; + if (!ielen) { + pDst->bkid_count = 0U; + return 0U; + } else { + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->bkid_count, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + } + if (unlikely(ielen < pDst->bkid_count * 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->bkid_count > 4) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->bkid, pBuf, (pDst->bkid_count * 16)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wapi. */ + +#define SigIeWAPI (0x006d) + + +uint32_t dot11f_unpack_ie_wapi_opaque(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWAPIOpaque *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + if (ielen > 253) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wapi_opaque. */ + +#define SigIeWAPIOpaque (0x006e) + + +uint32_t dot11f_unpack_ie_wfatpc(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWFATPC *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->txPower = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->linkMargin = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wfatpc. */ + +#define SigIeWFATPC (0x006f) + + +uint32_t dot11f_unpack_ie_wfdie_opaque(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWFDIEOpaque *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + if (ielen > 249) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wfdie_opaque. */ + +#define SigIeWFDIEOpaque (0x0070) + + +uint32_t dot11f_unpack_ie_wmm_caps(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWMMCaps *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp63__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp63__ = *pBuf; + pDst->reserved = tmp63__ >> 0 & 0xf; + pDst->qack = tmp63__ >> 4 & 0x1; + pDst->queue_request = tmp63__ >> 5 & 0x1; + pDst->txop_request = tmp63__ >> 6 & 0x1; + pDst->more_ack = tmp63__ >> 7 & 0x1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wmm_caps. */ + +#define SigIeWMMCaps (0x0071) + + +uint32_t dot11f_unpack_ie_wmm_info_ap(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWMMInfoAp *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp64__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp64__ = *pBuf; + pDst->param_set_count = tmp64__ >> 0 & 0xf; + pDst->reserved = tmp64__ >> 4 & 0x7; + pDst->uapsd = tmp64__ >> 7 & 0x1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wmm_info_ap. */ + +#define SigIeWMMInfoAp (0x0072) + + +uint32_t dot11f_unpack_ie_wmm_info_station(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWMMInfoStation *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp65__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp65__ = *pBuf; + pDst->acvo_uapsd = tmp65__ >> 0 & 0x1; + pDst->acvi_uapsd = tmp65__ >> 1 & 0x1; + pDst->acbk_uapsd = tmp65__ >> 2 & 0x1; + pDst->acbe_uapsd = tmp65__ >> 3 & 0x1; + pDst->reserved1 = tmp65__ >> 4 & 0x1; + pDst->max_sp_length = tmp65__ >> 5 & 0x3; + pDst->reserved2 = tmp65__ >> 7 & 0x1; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wmm_info_station. */ + +#define SigIeWMMInfoStation (0x0073) + + +uint32_t dot11f_unpack_ie_wmm_params(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWMMParams *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp66__; + uint8_t tmp67__; + uint8_t tmp68__; + uint8_t tmp69__; + uint8_t tmp70__; + uint8_t tmp71__; + uint8_t tmp72__; + uint8_t tmp73__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->version = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->qosInfo = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->reserved2 = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp66__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbe_aifsn = tmp66__ >> 0 & 0xf; + pDst->acbe_acm = tmp66__ >> 4 & 0x1; + pDst->acbe_aci = tmp66__ >> 5 & 0x3; + pDst->unused1 = tmp66__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp67__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbe_acwmin = tmp67__ >> 0 & 0xf; + pDst->acbe_acwmax = tmp67__ >> 4 & 0xf; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->acbe_txoplimit, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp68__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbk_aifsn = tmp68__ >> 0 & 0xf; + pDst->acbk_acm = tmp68__ >> 4 & 0x1; + pDst->acbk_aci = tmp68__ >> 5 & 0x3; + pDst->unused2 = tmp68__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp69__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbk_acwmin = tmp69__ >> 0 & 0xf; + pDst->acbk_acwmax = tmp69__ >> 4 & 0xf; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->acbk_txoplimit, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp70__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvi_aifsn = tmp70__ >> 0 & 0xf; + pDst->acvi_acm = tmp70__ >> 4 & 0x1; + pDst->acvi_aci = tmp70__ >> 5 & 0x3; + pDst->unused3 = tmp70__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp71__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvi_acwmin = tmp71__ >> 0 & 0xf; + pDst->acvi_acwmax = tmp71__ >> 4 & 0xf; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->acvi_txoplimit, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp72__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvo_aifsn = tmp72__ >> 0 & 0xf; + pDst->acvo_acm = tmp72__ >> 4 & 0x1; + pDst->acvo_aci = tmp72__ >> 5 & 0x3; + pDst->unused4 = tmp72__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp73__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvo_acwmin = tmp73__ >> 0 & 0xf; + pDst->acvo_acwmax = tmp73__ >> 4 & 0xf; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->acvo_txoplimit, pBuf, 0); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wmm_params. */ + +#define SigIeWMMParams (0x0074) + + +uint32_t dot11f_unpack_ie_wpa(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWPA *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->version, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (pDst->version != 0x1) { + pDst->present = 0; + return status | DOT11F_BAD_FIXED_VALUE; + } + if (!ielen) { + pDst->multicast_cipher_present = 0U; + pDst->unicast_cipher_count = 0U; + pDst->auth_suite_count = 0U; + return 0U; + } else { + pDst->multicast_cipher_present = 1U; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->multicast_cipher, pBuf, 4); + pBuf += 4; + ielen -= (uint8_t)4; + } + if (!ielen) { + pDst->unicast_cipher_count = 0U; + pDst->auth_suite_count = 0U; + return 0U; + } else { + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->unicast_cipher_count, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + } + if (unlikely(ielen < pDst->unicast_cipher_count * 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->unicast_cipher_count > 4) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->unicast_ciphers, pBuf, (pDst->unicast_cipher_count * 4)); + pBuf += (pDst->unicast_cipher_count * 4); + ielen -= (pDst->unicast_cipher_count * 4); + if (!ielen) { + pDst->auth_suite_count = 0U; + return 0U; + } else { + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->auth_suite_count, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + } + if (unlikely(ielen < pDst->auth_suite_count * 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->auth_suite_count > 4) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->auth_suites, pBuf, (pDst->auth_suite_count * 4)); + pBuf += (pDst->auth_suite_count * 4); + ielen -= (pDst->auth_suite_count * 4); + if (!ielen) { + return 0U; + } else { + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->caps, pBuf, 0); + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wpa. */ + +#define SigIeWPA (0x0075) + + +uint32_t dot11f_unpack_ie_wpa_opaque(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWPAOpaque *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + if (ielen > 249) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wpa_opaque. */ + +#define SigIeWPAOpaque (0x0076) + + +static const tTLVDefn TLVS_WSC[] = { + { offsetof(tDot11fIEWSC, Version), offsetof(tDot11fTLVVersion, present), + "Version", SigTlvVersion, DOT11F_TLV_VERSION, 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, WPSState), offsetof(tDot11fTLVWPSState, present), + "WPSState", SigTlvWPSState, DOT11F_TLV_WPSSTATE, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, APSetupLocked), + offsetof(tDot11fTLVAPSetupLocked, present), "APSetupLocked", + SigTlvAPSetupLocked, DOT11F_TLV_APSETUPLOCKED, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, SelectedRegistrarConfigMethods), + offsetof(tDot11fTLVSelectedRegistrarConfigMethods, present), + "SelectedRegistrarConfigMethods", SigTlvSelectedRegistrarConfigMethods, + DOT11F_TLV_SELECTEDREGISTRARCONFIGMETHODS, 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, UUID_E), offsetof(tDot11fTLVUUID_E, present), + "UUID_E", SigTlvUUID_E, DOT11F_TLV_UUID_E, 0, 20, 20, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, UUID_R), offsetof(tDot11fTLVUUID_R, present), + "UUID_R", SigTlvUUID_R, DOT11F_TLV_UUID_R, 0, 20, 20, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, RFBands), offsetof(tDot11fTLVRFBands, present), + "RFBands", SigTlvRFBands, DOT11F_TLV_RFBANDS, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, SelectedRegistrar), + offsetof(tDot11fTLVSelectedRegistrar, present), "SelectedRegistrar", + SigTlvSelectedRegistrar, DOT11F_TLV_SELECTEDREGISTRAR, + 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, ConfigMethods), + offsetof(tDot11fTLVConfigMethods, present), "ConfigMethods", + SigTlvConfigMethods, DOT11F_TLV_CONFIGMETHODS, 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, AssociationState), + offsetof(tDot11fTLVAssociationState, present), "AssociationState", + SigTlvAssociationState, DOT11F_TLV_ASSOCIATIONSTATE, + 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, ConfigurationError), + offsetof(tDot11fTLVConfigurationError, present), "ConfigurationError", + SigTlvConfigurationError, DOT11F_TLV_CONFIGURATIONERROR, + 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, Manufacturer), offsetof(tDot11fTLVManufacturer, + present), "Manufacturer", SigTlvManufacturer, DOT11F_TLV_MANUFACTURER, + 0, 4, 68, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, ModelName), offsetof(tDot11fTLVModelName, + present), "ModelName", SigTlvModelName, DOT11F_TLV_MODELNAME, + 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, ModelNumber), offsetof(tDot11fTLVModelNumber, + present), "ModelNumber", SigTlvModelNumber, DOT11F_TLV_MODELNUMBER, + 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, SerialNumber), offsetof(tDot11fTLVSerialNumber, + present), "SerialNumber", SigTlvSerialNumber, DOT11F_TLV_SERIALNUMBER, + 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, DeviceName), offsetof(tDot11fTLVDeviceName, + present), "DeviceName", SigTlvDeviceName, DOT11F_TLV_DEVICENAME, + 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, DevicePasswordID), + offsetof(tDot11fTLVDevicePasswordID, present), "DevicePasswordID", + SigTlvDevicePasswordID, DOT11F_TLV_DEVICEPASSWORDID, + 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, PrimaryDeviceType), + offsetof(tDot11fTLVPrimaryDeviceType, present), "PrimaryDeviceType", + SigTlvPrimaryDeviceType, DOT11F_TLV_PRIMARYDEVICETYPE, + 0, 12, 12, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, RequestType), offsetof(tDot11fTLVRequestType, + present), "RequestType", SigTlvRequestType, DOT11F_TLV_REQUESTTYPE, + 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, ResponseType), offsetof(tDot11fTLVResponseType, + present), "ResponseType", SigTlvResponseType, DOT11F_TLV_RESPONSETYPE, + 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, VendorExtension), + offsetof(tDot11fTLVVendorExtension, present), "VendorExtension", + SigTlvVendorExtension, DOT11F_TLV_VENDOREXTENSION, + 0, 7, 21, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWSC, RequestDeviceType), + offsetof(tDot11fTLVRequestDeviceType, present), "RequestDeviceType", + SigTlvRequestDeviceType, DOT11F_TLV_REQUESTDEVICETYPE, + 0, 12, 12, 0, 2, 2, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_wsc(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWSC *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_WSC, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_wsc. */ + +#define SigIeWSC (0x0077) + + +static const tTLVDefn TLVS_WscAssocReq[] = { + { offsetof(tDot11fIEWscAssocReq, Version), offsetof(tDot11fTLVVersion, + present), "Version", SigTlvVersion, DOT11F_TLV_VERSION, + 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscAssocReq, RequestType), + offsetof(tDot11fTLVRequestType, present), "RequestType", + SigTlvRequestType, DOT11F_TLV_REQUESTTYPE, 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscAssocReq, VendorExtension), + offsetof(tDot11fTLVVendorExtension, present), "VendorExtension", + SigTlvVendorExtension, DOT11F_TLV_VENDOREXTENSION, + 0, 7, 21, 0, 2, 2, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_wsc_assoc_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWscAssocReq *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_WscAssocReq, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_wsc_assoc_req. */ + +#define SigIeWscAssocReq (0x0078) + + +static const tTLVDefn TLVS_WscAssocRes[] = { + { offsetof(tDot11fIEWscAssocRes, Version), offsetof(tDot11fTLVVersion, + present), "Version", SigTlvVersion, DOT11F_TLV_VERSION, + 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscAssocRes, ResponseType), + offsetof(tDot11fTLVResponseType, present), "ResponseType", + SigTlvResponseType, DOT11F_TLV_RESPONSETYPE, 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscAssocRes, VendorExtension), + offsetof(tDot11fTLVVendorExtension, present), "VendorExtension", + SigTlvVendorExtension, DOT11F_TLV_VENDOREXTENSION, + 0, 7, 21, 0, 2, 2, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_wsc_assoc_res(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWscAssocRes *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_WscAssocRes, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_wsc_assoc_res. */ + +#define SigIeWscAssocRes (0x0079) + + +static const tTLVDefn TLVS_WscBeacon[] = { + { offsetof(tDot11fIEWscBeacon, Version), offsetof(tDot11fTLVVersion, + present), "Version", SigTlvVersion, DOT11F_TLV_VERSION, + 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeacon, WPSState), offsetof(tDot11fTLVWPSState, + present), "WPSState", SigTlvWPSState, DOT11F_TLV_WPSSTATE, + 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeacon, APSetupLocked), + offsetof(tDot11fTLVAPSetupLocked, present), "APSetupLocked", + SigTlvAPSetupLocked, DOT11F_TLV_APSETUPLOCKED, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeacon, SelectedRegistrar), + offsetof(tDot11fTLVSelectedRegistrar, present), "SelectedRegistrar", + SigTlvSelectedRegistrar, DOT11F_TLV_SELECTEDREGISTRAR, + 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeacon, DevicePasswordID), + offsetof(tDot11fTLVDevicePasswordID, present), "DevicePasswordID", + SigTlvDevicePasswordID, DOT11F_TLV_DEVICEPASSWORDID, + 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeacon, SelectedRegistrarConfigMethods), + offsetof(tDot11fTLVSelectedRegistrarConfigMethods, present), + "SelectedRegistrarConfigMethods", SigTlvSelectedRegistrarConfigMethods, + DOT11F_TLV_SELECTEDREGISTRARCONFIGMETHODS, 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeacon, UUID_E), offsetof(tDot11fTLVUUID_E, + present), "UUID_E", SigTlvUUID_E, DOT11F_TLV_UUID_E, + 0, 20, 20, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeacon, RFBands), offsetof(tDot11fTLVRFBands, + present), "RFBands", SigTlvRFBands, DOT11F_TLV_RFBANDS, + 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeacon, VendorExtension), + offsetof(tDot11fTLVVendorExtension, present), "VendorExtension", + SigTlvVendorExtension, DOT11F_TLV_VENDOREXTENSION, + 0, 7, 21, 0, 2, 2, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_wsc_beacon(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWscBeacon *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_WscBeacon, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_wsc_beacon. */ + +#define SigIeWscBeacon (0x007a) + + +static const tTLVDefn TLVS_WscBeaconProbeRes[] = { + { offsetof(tDot11fIEWscBeaconProbeRes, Version), + offsetof(tDot11fTLVVersion, present), "Version", SigTlvVersion, + DOT11F_TLV_VERSION, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, WPSState), + offsetof(tDot11fTLVWPSState, present), "WPSState", SigTlvWPSState, + DOT11F_TLV_WPSSTATE, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, APSetupLocked), + offsetof(tDot11fTLVAPSetupLocked, present), "APSetupLocked", + SigTlvAPSetupLocked, DOT11F_TLV_APSETUPLOCKED, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, SelectedRegistrar), + offsetof(tDot11fTLVSelectedRegistrar, present), "SelectedRegistrar", + SigTlvSelectedRegistrar, DOT11F_TLV_SELECTEDREGISTRAR, + 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, DevicePasswordID), + offsetof(tDot11fTLVDevicePasswordID, present), "DevicePasswordID", + SigTlvDevicePasswordID, DOT11F_TLV_DEVICEPASSWORDID, + 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, SelectedRegistrarConfigMethods), + offsetof(tDot11fTLVSelectedRegistrarConfigMethods, present), + "SelectedRegistrarConfigMethods", SigTlvSelectedRegistrarConfigMethods, + DOT11F_TLV_SELECTEDREGISTRARCONFIGMETHODS, 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, ResponseType), + offsetof(tDot11fTLVResponseType, present), "ResponseType", + SigTlvResponseType, DOT11F_TLV_RESPONSETYPE, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, UUID_E), + offsetof(tDot11fTLVUUID_E, present), "UUID_E", SigTlvUUID_E, + DOT11F_TLV_UUID_E, 0, 20, 20, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, Manufacturer), + offsetof(tDot11fTLVManufacturer, present), "Manufacturer", + SigTlvManufacturer, DOT11F_TLV_MANUFACTURER, 0, 4, 68, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, ModelName), + offsetof(tDot11fTLVModelName, present), "ModelName", SigTlvModelName, + DOT11F_TLV_MODELNAME, 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, ModelNumber), + offsetof(tDot11fTLVModelNumber, present), "ModelNumber", + SigTlvModelNumber, DOT11F_TLV_MODELNUMBER, 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, SerialNumber), + offsetof(tDot11fTLVSerialNumber, present), "SerialNumber", + SigTlvSerialNumber, DOT11F_TLV_SERIALNUMBER, 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, PrimaryDeviceType), + offsetof(tDot11fTLVPrimaryDeviceType, present), "PrimaryDeviceType", + SigTlvPrimaryDeviceType, DOT11F_TLV_PRIMARYDEVICETYPE, + 0, 12, 12, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, DeviceName), + offsetof(tDot11fTLVDeviceName, present), "DeviceName", SigTlvDeviceName, + DOT11F_TLV_DEVICENAME, 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, ConfigMethods), + offsetof(tDot11fTLVConfigMethods, present), "ConfigMethods", + SigTlvConfigMethods, DOT11F_TLV_CONFIGMETHODS, 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, RFBands), + offsetof(tDot11fTLVRFBands, present), "RFBands", SigTlvRFBands, + DOT11F_TLV_RFBANDS, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscBeaconProbeRes, VendorExtension), + offsetof(tDot11fTLVVendorExtension, present), "VendorExtension", + SigTlvVendorExtension, DOT11F_TLV_VENDOREXTENSION, + 0, 7, 21, 0, 2, 2, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_wsc_beacon_probe_res(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWscBeaconProbeRes *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_WscBeaconProbeRes, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_wsc_beacon_probe_res. */ + +#define SigIeWscBeaconProbeRes (0x007b) + + +uint32_t dot11f_unpack_ie_wsc_ie_opaque(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWscIEOpaque *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + if (ielen > 249) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_wsc_ie_opaque. */ + +#define SigIeWscIEOpaque (0x007c) + + +static const tTLVDefn TLVS_WscProbeReq[] = { + { offsetof(tDot11fIEWscProbeReq, Version), offsetof(tDot11fTLVVersion, + present), "Version", SigTlvVersion, DOT11F_TLV_VERSION, + 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, RequestType), + offsetof(tDot11fTLVRequestType, present), "RequestType", + SigTlvRequestType, DOT11F_TLV_REQUESTTYPE, 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, ConfigMethods), + offsetof(tDot11fTLVConfigMethods, present), "ConfigMethods", + SigTlvConfigMethods, DOT11F_TLV_CONFIGMETHODS, 0, 6, 6, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, UUID_E), offsetof(tDot11fTLVUUID_E, + present), "UUID_E", SigTlvUUID_E, DOT11F_TLV_UUID_E, + 0, 20, 20, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, PrimaryDeviceType), + offsetof(tDot11fTLVPrimaryDeviceType, present), "PrimaryDeviceType", + SigTlvPrimaryDeviceType, DOT11F_TLV_PRIMARYDEVICETYPE, + 0, 12, 12, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, RFBands), offsetof(tDot11fTLVRFBands, + present), "RFBands", SigTlvRFBands, DOT11F_TLV_RFBANDS, + 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, AssociationState), + offsetof(tDot11fTLVAssociationState, present), "AssociationState", + SigTlvAssociationState, DOT11F_TLV_ASSOCIATIONSTATE, + 0, 6, 6, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, ConfigurationError), + offsetof(tDot11fTLVConfigurationError, present), "ConfigurationError", + SigTlvConfigurationError, DOT11F_TLV_CONFIGURATIONERROR, + 0, 6, 6, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, DevicePasswordID), + offsetof(tDot11fTLVDevicePasswordID, present), "DevicePasswordID", + SigTlvDevicePasswordID, DOT11F_TLV_DEVICEPASSWORDID, + 0, 6, 6, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, Manufacturer), + offsetof(tDot11fTLVManufacturer, present), "Manufacturer", + SigTlvManufacturer, DOT11F_TLV_MANUFACTURER, 0, 4, 68, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, ModelName), + offsetof(tDot11fTLVModelName, present), "ModelName", SigTlvModelName, + DOT11F_TLV_MODELNAME, 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, ModelNumber), + offsetof(tDot11fTLVModelNumber, present), "ModelNumber", + SigTlvModelNumber, DOT11F_TLV_MODELNUMBER, 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, DeviceName), + offsetof(tDot11fTLVDeviceName, present), "DeviceName", SigTlvDeviceName, + DOT11F_TLV_DEVICENAME, 0, 4, 36, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, VendorExtension), + offsetof(tDot11fTLVVendorExtension, present), "VendorExtension", + SigTlvVendorExtension, DOT11F_TLV_VENDOREXTENSION, + 0, 7, 21, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeReq, RequestDeviceType), + offsetof(tDot11fTLVRequestDeviceType, present), "RequestDeviceType", + SigTlvRequestDeviceType, DOT11F_TLV_REQUESTDEVICETYPE, + 0, 12, 12, 0, 2, 2, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_wsc_probe_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWscProbeReq *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_WscProbeReq, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_wsc_probe_req. */ + +#define SigIeWscProbeReq (0x007d) + + +static const tTLVDefn TLVS_WscProbeRes[] = { + { offsetof(tDot11fIEWscProbeRes, Version), offsetof(tDot11fTLVVersion, + present), "Version", SigTlvVersion, DOT11F_TLV_VERSION, + 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, WPSState), offsetof(tDot11fTLVWPSState, + present), "WPSState", SigTlvWPSState, DOT11F_TLV_WPSSTATE, + 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, APSetupLocked), + offsetof(tDot11fTLVAPSetupLocked, present), "APSetupLocked", + SigTlvAPSetupLocked, DOT11F_TLV_APSETUPLOCKED, 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, SelectedRegistrar), + offsetof(tDot11fTLVSelectedRegistrar, present), "SelectedRegistrar", + SigTlvSelectedRegistrar, DOT11F_TLV_SELECTEDREGISTRAR, + 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, DevicePasswordID), + offsetof(tDot11fTLVDevicePasswordID, present), "DevicePasswordID", + SigTlvDevicePasswordID, DOT11F_TLV_DEVICEPASSWORDID, + 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, SelectedRegistrarConfigMethods), + offsetof(tDot11fTLVSelectedRegistrarConfigMethods, present), + "SelectedRegistrarConfigMethods", SigTlvSelectedRegistrarConfigMethods, + DOT11F_TLV_SELECTEDREGISTRARCONFIGMETHODS, 0, 6, 6, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, ResponseType), + offsetof(tDot11fTLVResponseType, present), "ResponseType", + SigTlvResponseType, DOT11F_TLV_RESPONSETYPE, 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, UUID_E), offsetof(tDot11fTLVUUID_E, + present), "UUID_E", SigTlvUUID_E, DOT11F_TLV_UUID_E, + 0, 20, 20, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, Manufacturer), + offsetof(tDot11fTLVManufacturer, present), "Manufacturer", + SigTlvManufacturer, DOT11F_TLV_MANUFACTURER, 0, 4, 68, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, ModelName), + offsetof(tDot11fTLVModelName, present), "ModelName", SigTlvModelName, + DOT11F_TLV_MODELNAME, 0, 4, 36, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, ModelNumber), + offsetof(tDot11fTLVModelNumber, present), "ModelNumber", + SigTlvModelNumber, DOT11F_TLV_MODELNUMBER, 0, 4, 36, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, SerialNumber), + offsetof(tDot11fTLVSerialNumber, present), "SerialNumber", + SigTlvSerialNumber, DOT11F_TLV_SERIALNUMBER, 0, 4, 36, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, PrimaryDeviceType), + offsetof(tDot11fTLVPrimaryDeviceType, present), "PrimaryDeviceType", + SigTlvPrimaryDeviceType, DOT11F_TLV_PRIMARYDEVICETYPE, + 0, 12, 12, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, DeviceName), + offsetof(tDot11fTLVDeviceName, present), "DeviceName", SigTlvDeviceName, + DOT11F_TLV_DEVICENAME, 0, 4, 36, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, ConfigMethods), + offsetof(tDot11fTLVConfigMethods, present), "ConfigMethods", + SigTlvConfigMethods, DOT11F_TLV_CONFIGMETHODS, 0, 6, 6, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, RFBands), offsetof(tDot11fTLVRFBands, + present), "RFBands", SigTlvRFBands, DOT11F_TLV_RFBANDS, + 0, 5, 5, 0, 2, 2, 1, }, + { offsetof(tDot11fIEWscProbeRes, VendorExtension), + offsetof(tDot11fTLVVendorExtension, present), "VendorExtension", + SigTlvVendorExtension, DOT11F_TLV_VENDOREXTENSION, + 0, 7, 21, 0, 2, 2, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_wsc_probe_res(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWscProbeRes *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_WscProbeRes, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_wsc_probe_res. */ + +#define SigIeWscProbeRes (0x007e) + + +static const tTLVDefn TLVS_WscReassocRes[] = { + { offsetof(tDot11fIEWscReassocRes, Version), offsetof(tDot11fTLVVersion, + present), "Version", SigTlvVersion, DOT11F_TLV_VERSION, + 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscReassocRes, ResponseType), + offsetof(tDot11fTLVResponseType, present), "ResponseType", + SigTlvResponseType, DOT11F_TLV_RESPONSETYPE, 0, 5, 5, 1, 2, 2, 1, }, + { offsetof(tDot11fIEWscReassocRes, VendorExtension), + offsetof(tDot11fTLVVendorExtension, present), "VendorExtension", + SigTlvVendorExtension, DOT11F_TLV_VENDOREXTENSION, + 0, 7, 21, 0, 2, 2, 1, }, + {0, 0, NULL, 0, 0xffff, 0, 0, 0, 0, 0, 0}, +}; + +uint32_t dot11f_unpack_ie_wsc_reassoc_res(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEWscReassocRes *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pBuf; (void)ielen; /* Shutup the compiler */ + pDst->present = 1; + status = unpack_tlv_core(pCtx, pBuf, ielen, + TLVS_WscReassocRes, + (uint8_t *)pDst, sizeof(*pDst)); + return status; +} /* End dot11f_unpack_ie_wsc_reassoc_res. */ + +#define SigIeWscReassocRes (0x007f) + + +uint32_t dot11f_unpack_ie_addba_extn_element(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEaddba_extn_element *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp74__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp74__ = *pBuf; + pDst->no_fragmentation = tmp74__ >> 0 & 0x1; + pDst->he_frag_operation = tmp74__ >> 1 & 0x3; + pDst->reserved = tmp74__ >> 3 & 0x1f; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_addba_extn_element. */ + +#define SigIeaddba_extn_element (0x0080) + + +uint32_t dot11f_unpack_ie_bss_color_change(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEbss_color_change *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp75__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->countdown = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp75__ = *pBuf; + pDst->new_color = tmp75__ >> 0 & 0x3f; + pDst->reserved = tmp75__ >> 6 & 0x3; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_bss_color_change. */ + +#define SigIebss_color_change (0x0081) + + +uint32_t dot11f_unpack_ie_dh_parameter_element(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEdh_parameter_element *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->group, pBuf, 2); + pBuf += 2; + ielen -= (uint8_t)2; + pDst->num_public_key = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->public_key, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_dh_parameter_element. */ + +#define SigIedh_parameter_element (0x0082) + + +uint32_t dot11f_unpack_ie_esp_information(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEesp_information *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + if (ielen > 96) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_esp_information. */ + +#define SigIeesp_information (0x0083) + + +uint32_t dot11f_unpack_ie_ext_chan_switch_ann(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEext_chan_switch_ann *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->switch_mode = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->new_reg_class = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->new_channel = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->switch_count = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ext_chan_switch_ann. */ + +#define SigIeext_chan_switch_ann (0x0084) + + +uint32_t dot11f_unpack_ie_fils_assoc_delay_info(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfils_assoc_delay_info *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->assoc_delay_info = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fils_assoc_delay_info. */ + +#define SigIefils_assoc_delay_info (0x0085) + + +uint32_t dot11f_unpack_ie_fils_hlp_container(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfils_hlp_container *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->dest_mac, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + if (unlikely(ielen < 6)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->src_mac, pBuf, 6); + pBuf += 6; + ielen -= (uint8_t)6; + pDst->num_hlp_packet = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->hlp_packet, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fils_hlp_container. */ + +#define SigIefils_hlp_container (0x0086) + + +uint32_t dot11f_unpack_ie_fils_indication(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfils_indication *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp76__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp76__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->public_key_identifiers_cnt = tmp76__ >> 0 & 0x7; + pDst->realm_identifiers_cnt = tmp76__ >> 3 & 0x7; + pDst->is_ip_config_supported = tmp76__ >> 6 & 0x1; + pDst->is_cache_id_present = tmp76__ >> 7 & 0x1; + pDst->is_hessid_present = tmp76__ >> 8 & 0x1; + pDst->is_fils_sk_auth_supported = tmp76__ >> 9 & 0x1; + pDst->is_fils_sk_auth_pfs_supported = tmp76__ >> 10 & 0x1; + pDst->is_pk_auth_supported = tmp76__ >> 11 & 0x1; + pDst->reserved = tmp76__ >> 12 & 0xf; + pDst->num_variable_data = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->variable_data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fils_indication. */ + +#define SigIefils_indication (0x0087) + + +uint32_t dot11f_unpack_ie_fils_kde(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfils_kde *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->key_rsc, pBuf, 8); + pBuf += 8; + ielen -= (uint8_t)8; + pDst->num_kde_list = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->kde_list, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fils_kde. */ + +#define SigIefils_kde (0x0088) + + +uint32_t dot11f_unpack_ie_fils_key_confirmation(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfils_key_confirmation *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_key_auth = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->key_auth, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fils_key_confirmation. */ + +#define SigIefils_key_confirmation (0x0089) + + +uint32_t dot11f_unpack_ie_fils_nonce(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfils_nonce *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 16)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->nonce, pBuf, 16); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fils_nonce. */ + +#define SigIefils_nonce (0x008a) + + +uint32_t dot11f_unpack_ie_fils_public_key(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfils_public_key *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->key_type = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + pDst->num_public_key = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->public_key, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fils_public_key. */ + +#define SigIefils_public_key (0x008b) + + +uint32_t dot11f_unpack_ie_fils_session(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfils_session *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 8)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->session, pBuf, 8); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fils_session. */ + +#define SigIefils_session (0x008c) + + +uint32_t dot11f_unpack_ie_fils_wrapped_data(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfils_wrapped_data *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_wrapped_data = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->wrapped_data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fils_wrapped_data. */ + +#define SigIefils_wrapped_data (0x008d) + + +uint32_t dot11f_unpack_ie_fragment_ie(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEfragment_ie *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_fragment_ie. */ + +#define SigIefragment_ie (0x008e) + + +uint32_t dot11f_unpack_ie_he_6ghz_band_cap(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEhe_6ghz_band_cap *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp77__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp77__, pBuf, 0); + pDst->min_mpdu_start_spacing = tmp77__ >> 0 & 0x7; + pDst->max_ampdu_len_exp = tmp77__ >> 3 & 0x7; + pDst->max_mpdu_len = tmp77__ >> 6 & 0x7; + pDst->sm_pow_save = tmp77__ >> 9 & 0x3; + pDst->rd_responder = tmp77__ >> 11 & 0x1; + pDst->rx_ant_pattern_consistency = tmp77__ >> 12 & 0x1; + pDst->tx_ant_pattern_consistency = tmp77__ >> 13 & 0x1; + pDst->reserved = tmp77__ >> 14 & 0x3; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_he_6ghz_band_cap. */ + +#define SigIehe_6ghz_band_cap (0x008f) + + +uint32_t dot11f_unpack_ie_he_cap(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEhe_cap *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t tmp78__; + uint16_t tmp79__; + uint32_t tmp80__; + uint32_t tmp81__; + uint16_t tmp82__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &tmp78__, pBuf, 0); + pBuf += 4; + ielen -= 4; + pDst->htc_he = tmp78__ >> 0 & 0x1; + pDst->twt_request = tmp78__ >> 1 & 0x1; + pDst->twt_responder = tmp78__ >> 2 & 0x1; + pDst->fragmentation = tmp78__ >> 3 & 0x3; + pDst->max_num_frag_msdu_amsdu_exp = tmp78__ >> 5 & 0x7; + pDst->min_frag_size = tmp78__ >> 8 & 0x3; + pDst->trigger_frm_mac_pad = tmp78__ >> 10 & 0x3; + pDst->multi_tid_aggr_rx_supp = tmp78__ >> 12 & 0x7; + pDst->he_link_adaptation = tmp78__ >> 15 & 0x3; + pDst->all_ack = tmp78__ >> 17 & 0x1; + pDst->trigd_rsp_sched = tmp78__ >> 18 & 0x1; + pDst->a_bsr = tmp78__ >> 19 & 0x1; + pDst->broadcast_twt = tmp78__ >> 20 & 0x1; + pDst->ba_32bit_bitmap = tmp78__ >> 21 & 0x1; + pDst->mu_cascade = tmp78__ >> 22 & 0x1; + pDst->ack_enabled_multitid = tmp78__ >> 23 & 0x1; + pDst->reserved = tmp78__ >> 24 & 0x1; + pDst->omi_a_ctrl = tmp78__ >> 25 & 0x1; + pDst->ofdma_ra = tmp78__ >> 26 & 0x1; + pDst->max_ampdu_len_exp_ext = tmp78__ >> 27 & 0x3; + pDst->amsdu_frag = tmp78__ >> 29 & 0x1; + pDst->flex_twt_sched = tmp78__ >> 30 & 0x1; + pDst->rx_ctrl_frame = tmp78__ >> 31 & 0x1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp79__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->bsrp_ampdu_aggr = tmp79__ >> 0 & 0x1; + pDst->qtp = tmp79__ >> 1 & 0x1; + pDst->a_bqr = tmp79__ >> 2 & 0x1; + pDst->spatial_reuse_param_rspder = tmp79__ >> 3 & 0x1; + pDst->ndp_feedback_supp = tmp79__ >> 4 & 0x1; + pDst->ops_supp = tmp79__ >> 5 & 0x1; + pDst->amsdu_in_ampdu = tmp79__ >> 6 & 0x1; + pDst->multi_tid_aggr_tx_supp = tmp79__ >> 7 & 0x7; + pDst->he_sub_ch_sel_tx_supp = tmp79__ >> 10 & 0x1; + pDst->ul_2x996_tone_ru_supp = tmp79__ >> 11 & 0x1; + pDst->om_ctrl_ul_mu_data_dis_rx = tmp79__ >> 12 & 0x1; + pDst->he_dynamic_smps = tmp79__ >> 13 & 0x1; + pDst->punctured_sounding_supp = tmp79__ >> 14 & 0x1; + pDst->ht_vht_trg_frm_rx_supp = tmp79__ >> 15 & 0x1; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &tmp80__, pBuf, 0); + pBuf += 4; + ielen -= 4; + pDst->reserved2 = tmp80__ >> 0 & 0x1; + pDst->chan_width_0 = tmp80__ >> 1 & 0x1; + pDst->chan_width_1 = tmp80__ >> 2 & 0x1; + pDst->chan_width_2 = tmp80__ >> 3 & 0x1; + pDst->chan_width_3 = tmp80__ >> 4 & 0x1; + pDst->chan_width_4 = tmp80__ >> 5 & 0x1; + pDst->chan_width_5 = tmp80__ >> 6 & 0x1; + pDst->chan_width_6 = tmp80__ >> 7 & 0x1; + pDst->rx_pream_puncturing = tmp80__ >> 8 & 0xf; + pDst->device_class = tmp80__ >> 12 & 0x1; + pDst->ldpc_coding = tmp80__ >> 13 & 0x1; + pDst->he_1x_ltf_800_gi_ppdu = tmp80__ >> 14 & 0x1; + pDst->midamble_tx_rx_max_nsts = tmp80__ >> 15 & 0x3; + pDst->he_4x_ltf_3200_gi_ndp = tmp80__ >> 17 & 0x1; + pDst->tb_ppdu_tx_stbc_lt_80mhz = tmp80__ >> 18 & 0x1; + pDst->rx_stbc_lt_80mhz = tmp80__ >> 19 & 0x1; + pDst->doppler = tmp80__ >> 20 & 0x3; + pDst->ul_mu = tmp80__ >> 22 & 0x3; + pDst->dcm_enc_tx = tmp80__ >> 24 & 0x7; + pDst->dcm_enc_rx = tmp80__ >> 27 & 0x7; + pDst->ul_he_mu = tmp80__ >> 30 & 0x1; + pDst->su_beamformer = tmp80__ >> 31 & 0x1; + if (unlikely(ielen < 4)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohl(pCtx, &tmp81__, pBuf, 0); + pBuf += 4; + ielen -= 4; + pDst->su_beamformee = tmp81__ >> 0 & 0x1; + pDst->mu_beamformer = tmp81__ >> 1 & 0x1; + pDst->bfee_sts_lt_80 = tmp81__ >> 2 & 0x7; + pDst->bfee_sts_gt_80 = tmp81__ >> 5 & 0x7; + pDst->num_sounding_lt_80 = tmp81__ >> 8 & 0x7; + pDst->num_sounding_gt_80 = tmp81__ >> 11 & 0x7; + pDst->su_feedback_tone16 = tmp81__ >> 14 & 0x1; + pDst->mu_feedback_tone16 = tmp81__ >> 15 & 0x1; + pDst->codebook_su = tmp81__ >> 16 & 0x1; + pDst->codebook_mu = tmp81__ >> 17 & 0x1; + pDst->beamforming_feedback = tmp81__ >> 18 & 0x7; + pDst->he_er_su_ppdu = tmp81__ >> 21 & 0x1; + pDst->dl_mu_mimo_part_bw = tmp81__ >> 22 & 0x1; + pDst->ppet_present = tmp81__ >> 23 & 0x1; + pDst->srp = tmp81__ >> 24 & 0x1; + pDst->power_boost = tmp81__ >> 25 & 0x1; + pDst->he_ltf_800_gi_4x = tmp81__ >> 26 & 0x1; + pDst->max_nc = tmp81__ >> 27 & 0x7; + pDst->tb_ppdu_tx_stbc_gt_80mhz = tmp81__ >> 30 & 0x1; + pDst->rx_stbc_gt_80mhz = tmp81__ >> 31 & 0x1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp82__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->er_he_ltf_800_gi_4x = tmp82__ >> 0 & 0x1; + pDst->he_ppdu_20_in_40Mhz_2G = tmp82__ >> 1 & 0x1; + pDst->he_ppdu_20_in_160_80p80Mhz = tmp82__ >> 2 & 0x1; + pDst->he_ppdu_80_in_160_80p80Mhz = tmp82__ >> 3 & 0x1; + pDst->er_1x_he_ltf_gi = tmp82__ >> 4 & 0x1; + pDst->midamble_tx_rx_1x_he_ltf = tmp82__ >> 5 & 0x1; + pDst->dcm_max_bw = tmp82__ >> 6 & 0x3; + pDst->longer_than_16_he_sigb_ofdm_sym = tmp82__ >> 8 & 0x1; + pDst->non_trig_cqi_feedback = tmp82__ >> 9 & 0x1; + pDst->tx_1024_qam_lt_242_tone_ru = tmp82__ >> 10 & 0x1; + pDst->rx_1024_qam_lt_242_tone_ru = tmp82__ >> 11 & 0x1; + pDst->rx_full_bw_su_he_mu_compress_sigb = tmp82__ >> 12 & 0x1; + pDst->rx_full_bw_su_he_mu_non_cmpr_sigb = tmp82__ >> 13 & 0x1; + pDst->reserved3 = tmp82__ >> 14 & 0x3; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->reserved4 = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->rx_he_mcs_map_lt_80, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->tx_he_mcs_map_lt_80, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + if (unlikely(ielen < pDst->chan_width_2 * 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->chan_width_2 > 1) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->rx_he_mcs_map_160, pBuf, (pDst->chan_width_2 * 2)); + pBuf += (pDst->chan_width_2 * 2); + ielen -= (pDst->chan_width_2 * 2); + if (unlikely(ielen < pDst->chan_width_2 * 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->chan_width_2 > 1) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->tx_he_mcs_map_160, pBuf, (pDst->chan_width_2 * 2)); + pBuf += (pDst->chan_width_2 * 2); + ielen -= (pDst->chan_width_2 * 2); + if (unlikely(ielen < pDst->chan_width_3 * 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->chan_width_3 > 1) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->rx_he_mcs_map_80_80, pBuf, (pDst->chan_width_3 * 2)); + pBuf += (pDst->chan_width_3 * 2); + ielen -= (pDst->chan_width_3 * 2); + if (unlikely(ielen < pDst->chan_width_3 * 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + if (pDst->chan_width_3 > 1) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->tx_he_mcs_map_80_80, pBuf, (pDst->chan_width_3 * 2)); + pBuf += (pDst->chan_width_3 * 2); + ielen -= (pDst->chan_width_3 * 2); + switch (pDst->ppet_present) { + case 1: + pDst->ppet.ppe_threshold.num_ppe_th = (uint8_t)(ielen); + if (ielen > 25) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->ppet.ppe_threshold.ppe_th, pBuf, (ielen)); + pBuf += (ielen); + ielen -= (ielen); + break; + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_he_cap. */ + +#define SigIehe_cap (0x0090) + + +uint32_t dot11f_unpack_ie_he_op(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEhe_op *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint16_t tmp83__; + uint8_t tmp84__; + uint8_t tmp85__; + uint8_t tmp86__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &tmp83__, pBuf, 0); + pBuf += 2; + ielen -= 2; + pDst->default_pe = tmp83__ >> 0 & 0x7; + pDst->twt_required = tmp83__ >> 3 & 0x1; + pDst->txop_rts_threshold = tmp83__ >> 4 & 0x3ff; + pDst->vht_oper_present = tmp83__ >> 14 & 0x1; + pDst->co_located_bss = tmp83__ >> 15 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp84__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->er_su_disable = tmp84__ >> 0 & 0x1; + pDst->oper_info_6g_present = tmp84__ >> 1 & 0x1; + pDst->reserved2 = tmp84__ >> 2 & 0x3f; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp85__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->bss_color = tmp85__ >> 0 & 0x3f; + pDst->partial_bss_col = tmp85__ >> 6 & 0x1; + pDst->bss_col_disabled = tmp85__ >> 7 & 0x1; + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->basic_mcs_nss, pBuf, 2); + pBuf += 2; + ielen -= (uint8_t)2; + switch (pDst->vht_oper_present) { + case 1: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->vht_oper.info.chan_width = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->vht_oper.info.center_freq_seg0 = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->vht_oper.info.center_freq_seg1 = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + break; + } + switch (pDst->co_located_bss) { + case 1: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->maxbssid_ind.info.data = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + break; + } + switch (pDst->oper_info_6g_present) { + case 1: + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->oper_info_6g.info.primary_ch = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp86__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->oper_info_6g.info.ch_width = tmp86__ >> 0 & 0x3; + pDst->oper_info_6g.info.dup_bcon = tmp86__ >> 2 & 0x1; + pDst->oper_info_6g.info.reserved = tmp86__ >> 3 & 0x1f; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->oper_info_6g.info.center_freq_seg0 = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->oper_info_6g.info.center_freq_seg1 = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->oper_info_6g.info.min_rate = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + break; + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_he_op. */ + +#define SigIehe_op (0x0091) + + +uint32_t dot11f_unpack_ie_hs20vendor_ie(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEhs20vendor_ie *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp87__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp87__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->dgaf_dis = tmp87__ >> 0 & 0x1; + pDst->hs_id_present = tmp87__ >> 1 & 0x3; + pDst->reserved = tmp87__ >> 3 & 0x1; + pDst->release_num = tmp87__ >> 4 & 0xf; + if (!ielen) { + return 0U; + } else { + switch (pDst->hs_id_present) { + case 1: + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->hs_id.pps_mo.pps_mo_id, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + break; + case 2: + if (unlikely(ielen < 2)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + framesntohs(pCtx, &pDst->hs_id.anqp_domain.anqp_domain_id, pBuf, 0); + pBuf += 2; + ielen -= (uint8_t)2; + break; + } + } + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_hs20vendor_ie. */ + +#define SigIehs20vendor_ie (0x0092) + + +uint32_t dot11f_unpack_ie_ht2040_bss_coexistence(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEht2040_bss_coexistence *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp88__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp88__ = *pBuf; + pDst->info_request = tmp88__ >> 0 & 0x1; + pDst->forty_mhz_intolerant = tmp88__ >> 1 & 0x1; + pDst->twenty_mhz_bsswidth_req = tmp88__ >> 2 & 0x1; + pDst->obss_scan_exemption_req = tmp88__ >> 3 & 0x1; + pDst->obss_scan_exemption_grant = tmp88__ >> 4 & 0x1; + pDst->unused = tmp88__ >> 5 & 0x7; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ht2040_bss_coexistence. */ + +#define SigIeht2040_bss_coexistence (0x0093) + + +uint32_t dot11f_unpack_ie_ht2040_bss_intolerant_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEht2040_bss_intolerant_report *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->operating_class = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + pDst->num_channel_list = (uint8_t)(ielen); + if (ielen > 50) { + pDst->present = 0; + return DOT11F_SKIPPED_BAD_IE; + } + + DOT11F_MEMCPY(pCtx, pDst->channel_list, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_ht2040_bss_intolerant_report. */ + +#define SigIeht2040_bss_intolerant_report (0x0094) + + +uint32_t dot11f_unpack_ie_mu_edca_param_set(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEmu_edca_param_set *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + uint8_t tmp89__; + uint8_t tmp90__; + uint8_t tmp91__; + uint8_t tmp92__; + uint8_t tmp93__; + uint8_t tmp94__; + uint8_t tmp95__; + uint8_t tmp96__; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->qos = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp89__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbe_aifsn = tmp89__ >> 0 & 0xf; + pDst->acbe_acm = tmp89__ >> 4 & 0x1; + pDst->acbe_aci = tmp89__ >> 5 & 0x3; + pDst->unused1 = tmp89__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp90__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbe_acwmin = tmp90__ >> 0 & 0xf; + pDst->acbe_acwmax = tmp90__ >> 4 & 0xf; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->acbe_muedca_timer = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp91__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbk_aifsn = tmp91__ >> 0 & 0xf; + pDst->acbk_acm = tmp91__ >> 4 & 0x1; + pDst->acbk_aci = tmp91__ >> 5 & 0x3; + pDst->unused2 = tmp91__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp92__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acbk_acwmin = tmp92__ >> 0 & 0xf; + pDst->acbk_acwmax = tmp92__ >> 4 & 0xf; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->acbk_muedca_timer = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp93__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvi_aifsn = tmp93__ >> 0 & 0xf; + pDst->acvi_acm = tmp93__ >> 4 & 0x1; + pDst->acvi_aci = tmp93__ >> 5 & 0x3; + pDst->unused3 = tmp93__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp94__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvi_acwmin = tmp94__ >> 0 & 0xf; + pDst->acvi_acwmax = tmp94__ >> 4 & 0xf; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->acvi_muedca_timer = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp95__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvo_aifsn = tmp95__ >> 0 & 0xf; + pDst->acvo_acm = tmp95__ >> 4 & 0x1; + pDst->acvo_aci = tmp95__ >> 5 & 0x3; + pDst->unused4 = tmp95__ >> 7 & 0x1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + tmp96__ = *pBuf; + pBuf += 1; + ielen -= 1; + pDst->acvo_acwmin = tmp96__ >> 0 & 0xf; + pDst->acvo_acwmax = tmp96__ >> 4 & 0xf; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->acvo_muedca_timer = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_mu_edca_param_set. */ + +#define SigIemu_edca_param_set (0x0095) + + +uint32_t dot11f_unpack_ie_osen_ie(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEosen_ie *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_osen_ie. */ + +#define SigIeosen_ie (0x0096) + + +static const tFFDefn FFS_qcn_ie[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_qcn_ie[] = { + { offsetof(tDot11fIEqcn_ie, version_attr), + offsetof(tDot11fIEversion_attr, present), 0, "version_attr", + 0, 4, 4, SigIeversion_attr, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VERSION_ATTR, 0, 0, }, + { offsetof(tDot11fIEqcn_ie, vht_mcs11_attr), + offsetof(tDot11fIEvht_mcs11_attr, present), 0, "vht_mcs11_attr", + 0, 3, 3, SigIevht_mcs11_attr, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHT_MCS11_ATTR, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +uint32_t dot11f_unpack_ie_qcn_ie(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEqcn_ie *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + (void)pCtx; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_qcn_ie, + IES_qcn_ie, + (uint8_t *)pDst, + sizeof(*pDst), + append_ie); + return status; +} /* End dot11f_unpack_ie_qcn_ie. */ + +#define SigIeqcn_ie (0x0097) + + +uint32_t dot11f_unpack_ie_roaming_consortium_sel(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEroaming_consortium_sel *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + pDst->num_data = (uint8_t)(ielen); + DOT11F_MEMCPY(pCtx, pDst->data, pBuf, (ielen)); + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_roaming_consortium_sel. */ + +#define SigIeroaming_consortium_sel (0x0098) + + +uint32_t dot11f_unpack_ie_sec_chan_offset_ele(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEsec_chan_offset_ele *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->secondaryChannelOffset = *pBuf; + (void)pCtx; + return status; +} /* End dot11f_unpack_ie_sec_chan_offset_ele. */ + +#define SigIesec_chan_offset_ele (0x0099) + + +static const tFFDefn FFS_vendor_vht_ie[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_vendor_vht_ie[] = { + { offsetof(tDot11fIEvendor_vht_ie, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fIEvendor_vht_ie, VHTOperation), + offsetof(tDot11fIEVHTOperation, present), 0, "VHTOperation", + 0, 7, 7, SigIeVHTOperation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTOPERATION, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, +}; + +uint32_t dot11f_unpack_ie_vendor_vht_ie(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint8_t ielen, + tDot11fIEvendor_vht_ie *pDst, + bool append_ie) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void) pBuf; (void)ielen; /* Shutup the compiler */ + if (pDst->present) + return DOT11F_DUPLICATE_IE; + pDst->present = 1; + if (unlikely(ielen < 1)) { + pDst->present = 0; + return DOT11F_INCOMPLETE_IE; + } + + pDst->sub_type = *pBuf; + pBuf += 1; + ielen -= (uint8_t)1; + (void)pCtx; + status |= unpack_core(pCtx, + pBuf, + ielen, + FFS_vendor_vht_ie, + IES_vendor_vht_ie, + (uint8_t *)pDst, + sizeof(*pDst), + append_ie); + return status; +} /* End dot11f_unpack_ie_vendor_vht_ie. */ + +#define SigIevendor_vht_ie (0x009a) + + +static const tFFDefn FFS_AddTSRequest[] = { + { "Category", offsetof(tDot11fAddTSRequest, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fAddTSRequest, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fAddTSRequest, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_AddTSRequest[] = { + { offsetof(tDot11fAddTSRequest, TSPEC), offsetof(tDot11fIETSPEC, present), + 0, "TSPEC", 0, 57, 57, SigIeTSPEC, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TSPEC, 0, 1, }, + { offsetof(tDot11fAddTSRequest, TCLAS), offsetof(tDot11fIETCLAS, present), + offsetof(tDot11fAddTSRequest, num_TCLAS), "TCLAS", 2, 7, 45, SigIeTCLAS, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_TCLAS, 0, 0, }, + { offsetof(tDot11fAddTSRequest, TCLASSPROC), + offsetof(tDot11fIETCLASSPROC, present), 0, "TCLASSPROC", + 0, 3, 3, SigIeTCLASSPROC, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TCLASSPROC, 0, 0, }, + { offsetof(tDot11fAddTSRequest, WMMTSPEC), offsetof(tDot11fIEWMMTSPEC, + present), 0, "WMMTSPEC", 0, 63, 63, SigIeWMMTSPEC, {0, 80, 242, 2, 2}, + 5, DOT11F_EID_WMMTSPEC, 0, 0, }, + { offsetof(tDot11fAddTSRequest, WMMTCLAS), offsetof(tDot11fIEWMMTCLAS, + present), offsetof(tDot11fAddTSRequest, num_WMMTCLAS), "WMMTCLAS", + 2, 13, 51, SigIeWMMTCLAS, {0, 80, 242, 2, 6}, + 5, DOT11F_EID_WMMTCLAS, 0, 0, }, + { offsetof(tDot11fAddTSRequest, WMMTCLASPROC), + offsetof(tDot11fIEWMMTCLASPROC, present), 0, "WMMTCLASPROC", + 0, 9, 9, SigIeWMMTCLASPROC, {0, 80, 242, 2, 7}, + 5, DOT11F_EID_WMMTCLASPROC, 0, 0, }, + { offsetof(tDot11fAddTSRequest, ESETrafStrmRateSet), + offsetof(tDot11fIEESETrafStrmRateSet, present), 0, "ESETrafStrmRateSet", + 0, 7, 15, SigIeESETrafStrmRateSet, {0, 64, 150, 8, 0}, + 4, DOT11F_EID_ESETRAFSTRMRATESET, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_add_ts_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAddTSRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_AddTSRequest, IES_AddTSRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_add_ts_request. */ + +static const tFFDefn FFS_AddTSResponse[] = { + { "Category", offsetof(tDot11fAddTSResponse, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fAddTSResponse, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fAddTSResponse, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "Status", offsetof(tDot11fAddTSResponse, Status), SigFfStatus, + DOT11F_FF_STATUS_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_AddTSResponse[] = { + { offsetof(tDot11fAddTSResponse, TSDelay), offsetof(tDot11fIETSDelay, + present), 0, "TSDelay", 0, 6, 6, SigIeTSDelay, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TSDELAY, 0, 1, }, + { offsetof(tDot11fAddTSResponse, TSPEC), offsetof(tDot11fIETSPEC, + present), 0, "TSPEC", 0, 57, 57, SigIeTSPEC, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TSPEC, 0, 1, }, + { offsetof(tDot11fAddTSResponse, TCLAS), offsetof(tDot11fIETCLAS, + present), offsetof(tDot11fAddTSResponse, num_TCLAS), "TCLAS", + 2, 7, 45, SigIeTCLAS, {0, 0, 0, 0, 0}, 0, DOT11F_EID_TCLAS, 0, 0, }, + { offsetof(tDot11fAddTSResponse, TCLASSPROC), + offsetof(tDot11fIETCLASSPROC, present), 0, "TCLASSPROC", + 0, 3, 3, SigIeTCLASSPROC, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TCLASSPROC, 0, 0, }, + { offsetof(tDot11fAddTSResponse, Schedule), offsetof(tDot11fIESchedule, + present), 0, "Schedule", 0, 16, 16, SigIeSchedule, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SCHEDULE, 0, 0, }, + { offsetof(tDot11fAddTSResponse, WMMTSDelay), + offsetof(tDot11fIEWMMTSDelay, present), 0, "WMMTSDelay", + 0, 12, 12, SigIeWMMTSDelay, {0, 80, 242, 2, 8}, + 5, DOT11F_EID_WMMTSDELAY, 0, 0, }, + { offsetof(tDot11fAddTSResponse, WMMSchedule), + offsetof(tDot11fIEWMMSchedule, present), 0, "WMMSchedule", + 0, 22, 22, SigIeWMMSchedule, {0, 80, 242, 2, 9}, + 5, DOT11F_EID_WMMSCHEDULE, 0, 0, }, + { offsetof(tDot11fAddTSResponse, WMMTSPEC), offsetof(tDot11fIEWMMTSPEC, + present), 0, "WMMTSPEC", 0, 63, 63, SigIeWMMTSPEC, {0, 80, 242, 2, 2}, + 5, DOT11F_EID_WMMTSPEC, 0, 0, }, + { offsetof(tDot11fAddTSResponse, WMMTCLAS), offsetof(tDot11fIEWMMTCLAS, + present), offsetof(tDot11fAddTSResponse, num_WMMTCLAS), "WMMTCLAS", + 2, 13, 51, SigIeWMMTCLAS, {0, 80, 242, 2, 6}, + 5, DOT11F_EID_WMMTCLAS, 0, 0, }, + { offsetof(tDot11fAddTSResponse, WMMTCLASPROC), + offsetof(tDot11fIEWMMTCLASPROC, present), 0, "WMMTCLASPROC", + 0, 9, 9, SigIeWMMTCLASPROC, {0, 80, 242, 2, 7}, + 5, DOT11F_EID_WMMTCLASPROC, 0, 0, }, + { offsetof(tDot11fAddTSResponse, ESETrafStrmMet), + offsetof(tDot11fIEESETrafStrmMet, present), 0, "ESETrafStrmMet", + 0, 10, 10, SigIeESETrafStrmMet, {0, 64, 150, 7, 0}, + 4, DOT11F_EID_ESETRAFSTRMMET, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_add_ts_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAddTSResponse *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_AddTSResponse, IES_AddTSResponse, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_add_ts_response. */ + +static const tFFDefn FFS_AssocRequest[] = { + { "Capabilities", offsetof(tDot11fAssocRequest, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { "ListenInterval", offsetof(tDot11fAssocRequest, ListenInterval), + SigFfListenInterval, DOT11F_FF_LISTENINTERVAL_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_AssocRequest[] = { + { offsetof(tDot11fAssocRequest, SSID), offsetof(tDot11fIESSID, present), 0, + "SSID", 0, 2, 34, SigIeSSID, {0, 0, 0, 0, 0}, 0, DOT11F_EID_SSID, 0, 1, }, + { offsetof(tDot11fAssocRequest, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fAssocRequest, OperatingMode), + offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode", + 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OPERATINGMODE, 0, 0, }, + { offsetof(tDot11fAssocRequest, PowerCaps), offsetof(tDot11fIEPowerCaps, + present), 0, "PowerCaps", 0, 4, 4, SigIePowerCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_POWERCAPS, 0, 0, }, + { offsetof(tDot11fAssocRequest, SuppChannels), + offsetof(tDot11fIESuppChannels, present), 0, "SuppChannels", + 0, 2, 98, SigIeSuppChannels, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPCHANNELS, 0, 0, }, + { offsetof(tDot11fAssocRequest, HTCaps), offsetof(tDot11fIEHTCaps, + present), 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fAssocRequest, QOSCapsStation), + offsetof(tDot11fIEQOSCapsStation, present), 0, "QOSCapsStation", + 0, 3, 3, SigIeQOSCapsStation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSCAPSSTATION, 0, 0, }, + { offsetof(tDot11fAssocRequest, RSNOpaque), offsetof(tDot11fIERSNOpaque, + present), 0, "RSNOpaque", 0, 2, 255, SigIeRSNOpaque, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RSNOPAQUE, 0, 0, }, + { offsetof(tDot11fAssocRequest, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fAssocRequest, MobilityDomain), + offsetof(tDot11fIEMobilityDomain, present), 0, "MobilityDomain", + 0, 5, 5, SigIeMobilityDomain, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MOBILITYDOMAIN, 0, 0, }, + { offsetof(tDot11fAssocRequest, FTInfo), offsetof(tDot11fIEFTInfo, + present), 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fAssocRequest, SuppOperatingClasses), + offsetof(tDot11fIESuppOperatingClasses, present), 0, + "SuppOperatingClasses", 0, 3, 34, SigIeSuppOperatingClasses, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_SUPPOPERATINGCLASSES, 0, 0, }, + { offsetof(tDot11fAssocRequest, WAPIOpaque), + offsetof(tDot11fIEWAPIOpaque, present), 0, "WAPIOpaque", + 0, 8, 255, SigIeWAPIOpaque, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_WAPIOPAQUE, 0, 0, }, + { offsetof(tDot11fAssocRequest, WAPI), offsetof(tDot11fIEWAPI, present), 0, + "WAPI", 0, 14, 112, SigIeWAPI, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_WAPI, 0, 0, }, + { offsetof(tDot11fAssocRequest, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fAssocRequest, QosMapSet), offsetof(tDot11fIEQosMapSet, + present), 0, "QosMapSet", 0, 18, 60, SigIeQosMapSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSMAPSET, 0, 0, }, + { offsetof(tDot11fAssocRequest, ExtCap), offsetof(tDot11fIEExtCap, + present), 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fAssocRequest, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fAssocRequest, fils_session), + offsetof(tDot11fIEfils_session, present), 0, "fils_session", + 0, 10, 10, SigIefils_session, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_SESSION, 4, 0, }, + { offsetof(tDot11fAssocRequest, fils_public_key), + offsetof(tDot11fIEfils_public_key, present), 0, "fils_public_key", + 0, 3, 258, SigIefils_public_key, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_PUBLIC_KEY, 12, 0, }, + { offsetof(tDot11fAssocRequest, fils_key_confirmation), + offsetof(tDot11fIEfils_key_confirmation, present), 0, + "fils_key_confirmation", 0, 2, 257, SigIefils_key_confirmation, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_FILS_KEY_CONFIRMATION, 3, 0, }, + { offsetof(tDot11fAssocRequest, fils_hlp_container), + offsetof(tDot11fIEfils_hlp_container, present), 0, "fils_hlp_container", + 0, 14, 269, SigIefils_hlp_container, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_HLP_CONTAINER, 5, 0, }, + { offsetof(tDot11fAssocRequest, fragment_ie), + offsetof(tDot11fIEfragment_ie, present), 0, "fragment_ie", + 0, 2, 257, SigIefragment_ie, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FRAGMENT_IE, 0, 0, }, + { offsetof(tDot11fAssocRequest, dh_parameter_element), + offsetof(tDot11fIEdh_parameter_element, present), 0, + "dh_parameter_element", 0, 4, 259, SigIedh_parameter_element, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_DH_PARAMETER_ELEMENT, 32, 0, }, + { offsetof(tDot11fAssocRequest, WPAOpaque), offsetof(tDot11fIEWPAOpaque, + present), 0, "WPAOpaque", 0, 8, 255, SigIeWPAOpaque, {0, 80, 242, 1, 0}, + 4, DOT11F_EID_WPAOPAQUE, 0, 0, }, + { offsetof(tDot11fAssocRequest, WMMCaps), offsetof(tDot11fIEWMMCaps, + present), 0, "WMMCaps", 0, 9, 9, SigIeWMMCaps, {0, 80, 242, 2, 5}, + 5, DOT11F_EID_WMMCAPS, 0, 0, }, + { offsetof(tDot11fAssocRequest, WMMInfoStation), + offsetof(tDot11fIEWMMInfoStation, present), 0, "WMMInfoStation", + 0, 9, 9, SigIeWMMInfoStation, {0, 80, 242, 2, 0}, + 5, DOT11F_EID_WMMINFOSTATION, 0, 0, }, + { offsetof(tDot11fAssocRequest, WscIEOpaque), + offsetof(tDot11fIEWscIEOpaque, present), 0, "WscIEOpaque", + 0, 8, 255, SigIeWscIEOpaque, {0, 80, 242, 4, 0}, + 4, DOT11F_EID_WSCIEOPAQUE, 0, 0, }, + { offsetof(tDot11fAssocRequest, ESERadMgmtCap), + offsetof(tDot11fIEESERadMgmtCap, present), 0, "ESERadMgmtCap", + 0, 8, 8, SigIeESERadMgmtCap, {0, 64, 150, 1, 0}, + 4, DOT11F_EID_ESERADMGMTCAP, 0, 0, }, + { offsetof(tDot11fAssocRequest, ESEVersion), + offsetof(tDot11fIEESEVersion, present), 0, "ESEVersion", + 0, 7, 7, SigIeESEVersion, {0, 64, 150, 3, 0}, + 4, DOT11F_EID_ESEVERSION, 0, 0, }, + { offsetof(tDot11fAssocRequest, P2PIEOpaque), + offsetof(tDot11fIEP2PIEOpaque, present), 0, "P2PIEOpaque", + 0, 8, 255, SigIeP2PIEOpaque, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PIEOPAQUE, 0, 0, }, + { offsetof(tDot11fAssocRequest, WFDIEOpaque), + offsetof(tDot11fIEWFDIEOpaque, present), 0, "WFDIEOpaque", + 0, 8, 255, SigIeWFDIEOpaque, {80, 111, 154, 10, 0}, + 4, DOT11F_EID_WFDIEOPAQUE, 0, 0, }, + { offsetof(tDot11fAssocRequest, vendor_vht_ie), + offsetof(tDot11fIEvendor_vht_ie, present), 0, "vendor_vht_ie", + 0, 7, 28, SigIevendor_vht_ie, {0, 144, 76, 4, 0}, + 4, DOT11F_EID_VENDOR_VHT_IE, 0, 0, }, + { offsetof(tDot11fAssocRequest, hs20vendor_ie), + offsetof(tDot11fIEhs20vendor_ie, present), 0, "hs20vendor_ie", + 0, 7, 9, SigIehs20vendor_ie, {80, 111, 154, 16, 0}, + 4, DOT11F_EID_HS20VENDOR_IE, 0, 0, }, + { offsetof(tDot11fAssocRequest, qcn_ie), offsetof(tDot11fIEqcn_ie, + present), 0, "qcn_ie", 0, 6, 13, SigIeqcn_ie, {140, 253, 240, 1, 0}, + 4, DOT11F_EID_QCN_IE, 0, 0, }, + { offsetof(tDot11fAssocRequest, he_cap), offsetof(tDot11fIEhe_cap, + present), 0, "he_cap", 0, 23, 56, SigIehe_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_CAP, 35, 0, }, + { offsetof(tDot11fAssocRequest, he_6ghz_band_cap), + offsetof(tDot11fIEhe_6ghz_band_cap, present), 0, "he_6ghz_band_cap", + 0, 4, 4, SigIehe_6ghz_band_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_6GHZ_BAND_CAP, 59, 0, }, + { offsetof(tDot11fAssocRequest, osen_ie), offsetof(tDot11fIEosen_ie, + present), 0, "osen_ie", 0, 6, 261, SigIeosen_ie, {80, 111, 154, 18, 0}, + 4, DOT11F_EID_OSEN_IE, 0, 0, }, + { offsetof(tDot11fAssocRequest, roaming_consortium_sel), + offsetof(tDot11fIEroaming_consortium_sel, present), 0, + "roaming_consortium_sel", 0, 6, 261, SigIeroaming_consortium_sel, + {80, 111, 154, 29, 0}, 4, DOT11F_EID_ROAMING_CONSORTIUM_SEL, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_assoc_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAssocRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_AssocRequest, IES_AssocRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_assoc_request. */ + +static const tFFDefn FFS_AssocResponse[] = { + { "Capabilities", offsetof(tDot11fAssocResponse, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { "Status", offsetof(tDot11fAssocResponse, Status), SigFfStatus, + DOT11F_FF_STATUS_LEN, }, + { "AID", offsetof(tDot11fAssocResponse, AID), SigFfAID, + DOT11F_FF_AID_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_AssocResponse[] = { + { offsetof(tDot11fAssocResponse, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fAssocResponse, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fAssocResponse, EDCAParamSet), + offsetof(tDot11fIEEDCAParamSet, present), 0, "EDCAParamSet", + 0, 20, 20, SigIeEDCAParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EDCAPARAMSET, 0, 0, }, + { offsetof(tDot11fAssocResponse, RCPIIE), offsetof(tDot11fIERCPIIE, + present), 0, "RCPIIE", 0, 3, 3, SigIeRCPIIE, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RCPIIE, 0, 0, }, + { offsetof(tDot11fAssocResponse, RSNIIE), offsetof(tDot11fIERSNIIE, + present), 0, "RSNIIE", 0, 3, 3, SigIeRSNIIE, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RSNIIE, 0, 0, }, + { offsetof(tDot11fAssocResponse, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fAssocResponse, MobilityDomain), + offsetof(tDot11fIEMobilityDomain, present), 0, "MobilityDomain", + 0, 5, 5, SigIeMobilityDomain, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MOBILITYDOMAIN, 0, 0, }, + { offsetof(tDot11fAssocResponse, FTInfo), offsetof(tDot11fIEFTInfo, + present), 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fAssocResponse, RICDataDesc), + offsetof(tDot11fIERICDataDesc, present), + offsetof(tDot11fAssocResponse, num_RICDataDesc), "RICDataDesc", + 2, 2, 550, SigIeRICDataDesc, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RICDATADESC, 0, 0, }, + { offsetof(tDot11fAssocResponse, WPA), offsetof(tDot11fIEWPA, present), 0, + "WPA", 0, 8, 50, SigIeWPA, {0, 80, 242, 1, 0}, 4, DOT11F_EID_WPA, 0, 0, }, + { offsetof(tDot11fAssocResponse, TimeoutInterval), + offsetof(tDot11fIETimeoutInterval, present), 0, "TimeoutInterval", + 0, 7, 7, SigIeTimeoutInterval, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TIMEOUTINTERVAL, 0, 0, }, + { offsetof(tDot11fAssocResponse, HTCaps), offsetof(tDot11fIEHTCaps, + present), 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fAssocResponse, HTInfo), offsetof(tDot11fIEHTInfo, + present), 0, "HTInfo", 0, 24, 56, SigIeHTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTINFO, 0, 0, }, + { offsetof(tDot11fAssocResponse, WMMParams), offsetof(tDot11fIEWMMParams, + present), 0, "WMMParams", 0, 26, 26, SigIeWMMParams, {0, 80, 242, 2, 1}, + 5, DOT11F_EID_WMMPARAMS, 0, 0, }, + { offsetof(tDot11fAssocResponse, WMMCaps), offsetof(tDot11fIEWMMCaps, + present), 0, "WMMCaps", 0, 9, 9, SigIeWMMCaps, {0, 80, 242, 2, 5}, + 5, DOT11F_EID_WMMCAPS, 0, 0, }, + { offsetof(tDot11fAssocResponse, ESERadMgmtCap), + offsetof(tDot11fIEESERadMgmtCap, present), 0, "ESERadMgmtCap", + 0, 8, 8, SigIeESERadMgmtCap, {0, 64, 150, 1, 0}, + 4, DOT11F_EID_ESERADMGMTCAP, 0, 0, }, + { offsetof(tDot11fAssocResponse, ESETrafStrmMet), + offsetof(tDot11fIEESETrafStrmMet, present), 0, "ESETrafStrmMet", + 0, 10, 10, SigIeESETrafStrmMet, {0, 64, 150, 7, 0}, + 4, DOT11F_EID_ESETRAFSTRMMET, 0, 0, }, + { offsetof(tDot11fAssocResponse, ESETxmitPower), + offsetof(tDot11fIEESETxmitPower, present), 0, "ESETxmitPower", + 0, 8, 8, SigIeESETxmitPower, {0, 64, 150, 0, 0}, + 4, DOT11F_EID_ESETXMITPOWER, 0, 0, }, + { offsetof(tDot11fAssocResponse, WMMTSPEC), offsetof(tDot11fIEWMMTSPEC, + present), offsetof(tDot11fAssocResponse, num_WMMTSPEC), "WMMTSPEC", + 4, 63, 63, SigIeWMMTSPEC, {0, 80, 242, 2, 2}, + 5, DOT11F_EID_WMMTSPEC, 0, 0, }, + { offsetof(tDot11fAssocResponse, WscAssocRes), + offsetof(tDot11fIEWscAssocRes, present), 0, "WscAssocRes", + 0, 6, 37, SigIeWscAssocRes, {0, 80, 242, 4, 0}, + 4, DOT11F_EID_WSCASSOCRES, 0, 0, }, + { offsetof(tDot11fAssocResponse, P2PAssocRes), + offsetof(tDot11fIEP2PAssocRes, present), 0, "P2PAssocRes", + 0, 6, 17, SigIeP2PAssocRes, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PASSOCRES, 0, 0, }, + { offsetof(tDot11fAssocResponse, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fAssocResponse, VHTOperation), + offsetof(tDot11fIEVHTOperation, present), 0, "VHTOperation", + 0, 7, 7, SigIeVHTOperation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTOPERATION, 0, 0, }, + { offsetof(tDot11fAssocResponse, ExtCap), offsetof(tDot11fIEExtCap, + present), 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fAssocResponse, OBSSScanParameters), + offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters", + 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, 0, }, + { offsetof(tDot11fAssocResponse, QosMapSet), offsetof(tDot11fIEQosMapSet, + present), 0, "QosMapSet", 0, 18, 60, SigIeQosMapSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSMAPSET, 0, 0, }, + { offsetof(tDot11fAssocResponse, fils_session), + offsetof(tDot11fIEfils_session, present), 0, "fils_session", + 0, 10, 10, SigIefils_session, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_SESSION, 4, 0, }, + { offsetof(tDot11fAssocResponse, fils_public_key), + offsetof(tDot11fIEfils_public_key, present), 0, "fils_public_key", + 0, 3, 258, SigIefils_public_key, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_PUBLIC_KEY, 12, 0, }, + { offsetof(tDot11fAssocResponse, fils_key_confirmation), + offsetof(tDot11fIEfils_key_confirmation, present), 0, + "fils_key_confirmation", 0, 2, 257, SigIefils_key_confirmation, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_FILS_KEY_CONFIRMATION, 3, 0, }, + { offsetof(tDot11fAssocResponse, fils_hlp_container), + offsetof(tDot11fIEfils_hlp_container, present), 0, "fils_hlp_container", + 0, 14, 269, SigIefils_hlp_container, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_HLP_CONTAINER, 5, 0, }, + { offsetof(tDot11fAssocResponse, fragment_ie), + offsetof(tDot11fIEfragment_ie, present), 0, "fragment_ie", + 0, 2, 257, SigIefragment_ie, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FRAGMENT_IE, 0, 0, }, + { offsetof(tDot11fAssocResponse, fils_kde), offsetof(tDot11fIEfils_kde, + present), 0, "fils_kde", 0, 10, 265, SigIefils_kde, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_KDE, 7, 0, }, + { offsetof(tDot11fAssocResponse, vendor_vht_ie), + offsetof(tDot11fIEvendor_vht_ie, present), 0, "vendor_vht_ie", + 0, 7, 28, SigIevendor_vht_ie, {0, 144, 76, 4, 0}, + 4, DOT11F_EID_VENDOR_VHT_IE, 0, 0, }, + { offsetof(tDot11fAssocResponse, qcn_ie), offsetof(tDot11fIEqcn_ie, + present), 0, "qcn_ie", 0, 6, 13, SigIeqcn_ie, {140, 253, 240, 1, 0}, + 4, DOT11F_EID_QCN_IE, 0, 0, }, + { offsetof(tDot11fAssocResponse, he_cap), offsetof(tDot11fIEhe_cap, + present), 0, "he_cap", 0, 23, 56, SigIehe_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_CAP, 35, 0, }, + { offsetof(tDot11fAssocResponse, he_op), offsetof(tDot11fIEhe_op, + present), 0, "he_op", 0, 8, 17, SigIehe_op, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_OP, 36, 0, }, + { offsetof(tDot11fAssocResponse, he_6ghz_band_cap), + offsetof(tDot11fIEhe_6ghz_band_cap, present), 0, "he_6ghz_band_cap", + 0, 4, 4, SigIehe_6ghz_band_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_6GHZ_BAND_CAP, 59, 0, }, + { offsetof(tDot11fAssocResponse, bss_color_change), + offsetof(tDot11fIEbss_color_change, present), 0, "bss_color_change", + 0, 4, 4, SigIebss_color_change, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BSS_COLOR_CHANGE, 42, 0, }, + { offsetof(tDot11fAssocResponse, mu_edca_param_set), + offsetof(tDot11fIEmu_edca_param_set, present), 0, "mu_edca_param_set", + 0, 15, 15, SigIemu_edca_param_set, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MU_EDCA_PARAM_SET, 38, 0, }, + { offsetof(tDot11fAssocResponse, MBO_IE), offsetof(tDot11fIEMBO_IE, + present), 0, "MBO_IE", 0, 6, 295, SigIeMBO_IE, {80, 111, 154, 22, 0}, + 4, DOT11F_EID_MBO_IE, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_assoc_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAssocResponse *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_AssocResponse, IES_AssocResponse, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_assoc_response. */ + +static const tFFDefn FFS_Authentication[] = { + { "AuthAlgo", offsetof(tDot11fAuthentication, AuthAlgo), SigFfAuthAlgo, + DOT11F_FF_AUTHALGO_LEN, }, + { "AuthSeqNo", offsetof(tDot11fAuthentication, AuthSeqNo), SigFfAuthSeqNo, + DOT11F_FF_AUTHSEQNO_LEN, }, + { "Status", offsetof(tDot11fAuthentication, Status), SigFfStatus, + DOT11F_FF_STATUS_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_Authentication[] = { + { offsetof(tDot11fAuthentication, ChallengeText), + offsetof(tDot11fIEChallengeText, present), 0, "ChallengeText", + 0, 3, 255, SigIeChallengeText, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CHALLENGETEXT, 0, 0, }, + { offsetof(tDot11fAuthentication, RSNOpaque), + offsetof(tDot11fIERSNOpaque, present), 0, "RSNOpaque", + 0, 2, 255, SigIeRSNOpaque, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RSNOPAQUE, 0, 0, }, + { offsetof(tDot11fAuthentication, MobilityDomain), + offsetof(tDot11fIEMobilityDomain, present), 0, "MobilityDomain", + 0, 5, 5, SigIeMobilityDomain, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MOBILITYDOMAIN, 0, 0, }, + { offsetof(tDot11fAuthentication, FTInfo), offsetof(tDot11fIEFTInfo, + present), 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fAuthentication, TimeoutInterval), + offsetof(tDot11fIETimeoutInterval, present), 0, "TimeoutInterval", + 0, 7, 7, SigIeTimeoutInterval, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TIMEOUTINTERVAL, 0, 0, }, + { offsetof(tDot11fAuthentication, RICDataDesc), + offsetof(tDot11fIERICDataDesc, present), + offsetof(tDot11fAuthentication, num_RICDataDesc), "RICDataDesc", + 2, 2, 550, SigIeRICDataDesc, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RICDATADESC, 0, 0, }, + { offsetof(tDot11fAuthentication, fils_nonce), + offsetof(tDot11fIEfils_nonce, present), 0, "fils_nonce", + 0, 18, 18, SigIefils_nonce, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_NONCE, 13, 0, }, + { offsetof(tDot11fAuthentication, fils_session), + offsetof(tDot11fIEfils_session, present), 0, "fils_session", + 0, 10, 10, SigIefils_session, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_SESSION, 4, 0, }, + { offsetof(tDot11fAuthentication, fils_wrapped_data), + offsetof(tDot11fIEfils_wrapped_data, present), 0, "fils_wrapped_data", + 0, 2, 257, SigIefils_wrapped_data, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_WRAPPED_DATA, 8, 0, }, + { offsetof(tDot11fAuthentication, fils_assoc_delay_info), + offsetof(tDot11fIEfils_assoc_delay_info, present), 0, + "fils_assoc_delay_info", 0, 3, 3, SigIefils_assoc_delay_info, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_FILS_ASSOC_DELAY_INFO, 1, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_authentication(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fAuthentication *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_Authentication, IES_Authentication, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_authentication. */ + +static const tFFDefn FFS_Beacon[] = { + { "TimeStamp", offsetof(tDot11fBeacon, TimeStamp), SigFfTimeStamp, + DOT11F_FF_TIMESTAMP_LEN, }, + { "BeaconInterval", offsetof(tDot11fBeacon, BeaconInterval), + SigFfBeaconInterval, DOT11F_FF_BEACONINTERVAL_LEN, }, + { "Capabilities", offsetof(tDot11fBeacon, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_Beacon[] = { + { offsetof(tDot11fBeacon, SSID), offsetof(tDot11fIESSID, present), 0, + "SSID", 0, 2, 34, SigIeSSID, {0, 0, 0, 0, 0}, 0, DOT11F_EID_SSID, 0, 1, }, + { offsetof(tDot11fBeacon, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fBeacon, FHParamSet), offsetof(tDot11fIEFHParamSet, + present), 0, "FHParamSet", 0, 7, 7, SigIeFHParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FHPARAMSET, 0, 0, }, + { offsetof(tDot11fBeacon, DSParams), offsetof(tDot11fIEDSParams, present), + 0, "DSParams", 0, 3, 3, SigIeDSParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_DSPARAMS, 0, 0, }, + { offsetof(tDot11fBeacon, CFParams), offsetof(tDot11fIECFParams, present), + 0, "CFParams", 0, 8, 8, SigIeCFParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CFPARAMS, 0, 0, }, + { offsetof(tDot11fBeacon, IBSSParams), offsetof(tDot11fIEIBSSParams, + present), 0, "IBSSParams", 0, 4, 4, SigIeIBSSParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_IBSSPARAMS, 0, 0, }, + { offsetof(tDot11fBeacon, TIM), offsetof(tDot11fIETIM, present), 0, "TIM", + 0, 6, 256, SigIeTIM, {0, 0, 0, 0, 0}, 0, DOT11F_EID_TIM, 0, 0, }, + { offsetof(tDot11fBeacon, Country), offsetof(tDot11fIECountry, present), 0, + "Country", 0, 5, 257, SigIeCountry, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_COUNTRY, 0, 0, }, + { offsetof(tDot11fBeacon, FHParams), offsetof(tDot11fIEFHParams, present), + 0, "FHParams", 0, 4, 4, SigIeFHParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FHPARAMS, 0, 0, }, + { offsetof(tDot11fBeacon, FHPattTable), offsetof(tDot11fIEFHPattTable, + present), 0, "FHPattTable", 0, 6, 257, SigIeFHPattTable, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FHPATTTABLE, 0, 0, }, + { offsetof(tDot11fBeacon, PowerConstraints), + offsetof(tDot11fIEPowerConstraints, present), 0, "PowerConstraints", + 0, 3, 3, SigIePowerConstraints, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_POWERCONSTRAINTS, 0, 0, }, + { offsetof(tDot11fBeacon, ChanSwitchAnn), + offsetof(tDot11fIEChanSwitchAnn, present), 0, "ChanSwitchAnn", + 0, 5, 5, SigIeChanSwitchAnn, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CHANSWITCHANN, 0, 0, }, + { offsetof(tDot11fBeacon, ext_chan_switch_ann), + offsetof(tDot11fIEext_chan_switch_ann, present), 0, "ext_chan_switch_ann", + 0, 6, 6, SigIeext_chan_switch_ann, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXT_CHAN_SWITCH_ANN, 0, 0, }, + { offsetof(tDot11fBeacon, SuppOperatingClasses), + offsetof(tDot11fIESuppOperatingClasses, present), 0, + "SuppOperatingClasses", 0, 3, 34, SigIeSuppOperatingClasses, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_SUPPOPERATINGCLASSES, 0, 0, }, + { offsetof(tDot11fBeacon, Quiet), offsetof(tDot11fIEQuiet, present), 0, + "Quiet", 0, 8, 8, SigIeQuiet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QUIET, 0, 0, }, + { offsetof(tDot11fBeacon, TPCReport), offsetof(tDot11fIETPCReport, + present), 0, "TPCReport", 0, 4, 4, SigIeTPCReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TPCREPORT, 0, 0, }, + { offsetof(tDot11fBeacon, ERPInfo), offsetof(tDot11fIEERPInfo, present), 0, + "ERPInfo", 0, 3, 3, SigIeERPInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ERPINFO, 0, 0, }, + { offsetof(tDot11fBeacon, ExtSuppRates), offsetof(tDot11fIEExtSuppRates, + present), 0, "ExtSuppRates", 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fBeacon, RSN), offsetof(tDot11fIERSN, present), 0, "RSN", + 0, 4, 132, SigIeRSN, {0, 0, 0, 0, 0}, 0, DOT11F_EID_RSN, 0, 0, }, + { offsetof(tDot11fBeacon, QBSSLoad), offsetof(tDot11fIEQBSSLoad, present), + 0, "QBSSLoad", 0, 7, 7, SigIeQBSSLoad, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QBSSLOAD, 0, 0, }, + { offsetof(tDot11fBeacon, EDCAParamSet), offsetof(tDot11fIEEDCAParamSet, + present), 0, "EDCAParamSet", 0, 20, 20, SigIeEDCAParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EDCAPARAMSET, 0, 0, }, + { offsetof(tDot11fBeacon, QOSCapsAp), offsetof(tDot11fIEQOSCapsAp, + present), 0, "QOSCapsAp", 0, 3, 3, SigIeQOSCapsAp, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSCAPSAP, 0, 0, }, + { offsetof(tDot11fBeacon, APChannelReport), + offsetof(tDot11fIEAPChannelReport, present), 0, "APChannelReport", + 0, 3, 53, SigIeAPChannelReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_APCHANNELREPORT, 0, 0, }, + { offsetof(tDot11fBeacon, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fBeacon, MobilityDomain), + offsetof(tDot11fIEMobilityDomain, present), 0, "MobilityDomain", + 0, 5, 5, SigIeMobilityDomain, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MOBILITYDOMAIN, 0, 0, }, + { offsetof(tDot11fBeacon, WPA), offsetof(tDot11fIEWPA, present), 0, "WPA", + 0, 8, 50, SigIeWPA, {0, 80, 242, 1, 0}, 4, DOT11F_EID_WPA, 0, 0, }, + { offsetof(tDot11fBeacon, HTCaps), offsetof(tDot11fIEHTCaps, present), 0, + "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fBeacon, HTInfo), offsetof(tDot11fIEHTInfo, present), 0, + "HTInfo", 0, 24, 56, SigIeHTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTINFO, 0, 0, }, + { offsetof(tDot11fBeacon, sec_chan_offset_ele), + offsetof(tDot11fIEsec_chan_offset_ele, present), 0, "sec_chan_offset_ele", + 0, 3, 3, SigIesec_chan_offset_ele, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SEC_CHAN_OFFSET_ELE, 0, 0, }, + { offsetof(tDot11fBeacon, WMMInfoAp), offsetof(tDot11fIEWMMInfoAp, + present), 0, "WMMInfoAp", 0, 9, 9, SigIeWMMInfoAp, {0, 80, 242, 2, 0}, + 5, DOT11F_EID_WMMINFOAP, 0, 0, }, + { offsetof(tDot11fBeacon, WMMParams), offsetof(tDot11fIEWMMParams, + present), 0, "WMMParams", 0, 26, 26, SigIeWMMParams, {0, 80, 242, 2, 1}, + 5, DOT11F_EID_WMMPARAMS, 0, 0, }, + { offsetof(tDot11fBeacon, WMMCaps), offsetof(tDot11fIEWMMCaps, present), 0, + "WMMCaps", 0, 9, 9, SigIeWMMCaps, {0, 80, 242, 2, 5}, + 5, DOT11F_EID_WMMCAPS, 0, 0, }, + { offsetof(tDot11fBeacon, WAPI), offsetof(tDot11fIEWAPI, present), 0, + "WAPI", 0, 14, 112, SigIeWAPI, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_WAPI, 0, 0, }, + { offsetof(tDot11fBeacon, ESERadMgmtCap), + offsetof(tDot11fIEESERadMgmtCap, present), 0, "ESERadMgmtCap", + 0, 8, 8, SigIeESERadMgmtCap, {0, 64, 150, 1, 0}, + 4, DOT11F_EID_ESERADMGMTCAP, 0, 0, }, + { offsetof(tDot11fBeacon, ESETrafStrmMet), + offsetof(tDot11fIEESETrafStrmMet, present), 0, "ESETrafStrmMet", + 0, 10, 10, SigIeESETrafStrmMet, {0, 64, 150, 7, 0}, + 4, DOT11F_EID_ESETRAFSTRMMET, 0, 0, }, + { offsetof(tDot11fBeacon, ESETxmitPower), + offsetof(tDot11fIEESETxmitPower, present), 0, "ESETxmitPower", + 0, 8, 8, SigIeESETxmitPower, {0, 64, 150, 0, 0}, + 4, DOT11F_EID_ESETXMITPOWER, 0, 0, }, + { offsetof(tDot11fBeacon, WscBeacon), offsetof(tDot11fIEWscBeacon, + present), 0, "WscBeacon", 0, 6, 84, SigIeWscBeacon, {0, 80, 242, 4, 0}, + 4, DOT11F_EID_WSCBEACON, 0, 0, }, + { offsetof(tDot11fBeacon, P2PBeacon), offsetof(tDot11fIEP2PBeacon, + present), 0, "P2PBeacon", 0, 6, 61, SigIeP2PBeacon, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PBEACON, 0, 0, }, + { offsetof(tDot11fBeacon, VHTCaps), offsetof(tDot11fIEVHTCaps, present), 0, + "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fBeacon, VHTOperation), offsetof(tDot11fIEVHTOperation, + present), 0, "VHTOperation", 0, 7, 7, SigIeVHTOperation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTOPERATION, 0, 0, }, + { offsetof(tDot11fBeacon, VHTExtBssLoad), + offsetof(tDot11fIEVHTExtBssLoad, present), 0, "VHTExtBssLoad", + 0, 7, 7, SigIeVHTExtBssLoad, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTEXTBSSLOAD, 0, 0, }, + { offsetof(tDot11fBeacon, ExtCap), offsetof(tDot11fIEExtCap, present), 0, + "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fBeacon, OperatingMode), + offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode", + 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OPERATINGMODE, 0, 0, }, + { offsetof(tDot11fBeacon, WiderBWChanSwitchAnn), + offsetof(tDot11fIEWiderBWChanSwitchAnn, present), 0, + "WiderBWChanSwitchAnn", 0, 5, 5, SigIeWiderBWChanSwitchAnn, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_WIDERBWCHANSWITCHANN, 0, 0, }, + { offsetof(tDot11fBeacon, OBSSScanParameters), + offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters", + 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, 0, }, + { offsetof(tDot11fBeacon, fils_indication), + offsetof(tDot11fIEfils_indication, present), 0, "fils_indication", + 0, 6, 259, SigIefils_indication, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_INDICATION, 0, 0, }, + { offsetof(tDot11fBeacon, Vendor1IE), offsetof(tDot11fIEVendor1IE, + present), 0, "Vendor1IE", 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, + 3, DOT11F_EID_VENDOR1IE, 0, 0, }, + { offsetof(tDot11fBeacon, vendor_vht_ie), + offsetof(tDot11fIEvendor_vht_ie, present), 0, "vendor_vht_ie", + 0, 7, 28, SigIevendor_vht_ie, {0, 144, 76, 4, 0}, + 4, DOT11F_EID_VENDOR_VHT_IE, 0, 0, }, + { offsetof(tDot11fBeacon, Vendor3IE), offsetof(tDot11fIEVendor3IE, + present), 0, "Vendor3IE", 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, + 3, DOT11F_EID_VENDOR3IE, 0, 0, }, + { offsetof(tDot11fBeacon, hs20vendor_ie), + offsetof(tDot11fIEhs20vendor_ie, present), 0, "hs20vendor_ie", + 0, 7, 9, SigIehs20vendor_ie, {80, 111, 154, 16, 0}, + 4, DOT11F_EID_HS20VENDOR_IE, 0, 0, }, + { offsetof(tDot11fBeacon, ChannelSwitchWrapper), + offsetof(tDot11fIEChannelSwitchWrapper, present), 0, + "ChannelSwitchWrapper", 0, 2, 14, SigIeChannelSwitchWrapper, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_CHANNELSWITCHWRAPPER, 0, 0, }, + { offsetof(tDot11fBeacon, QComVendorIE), offsetof(tDot11fIEQComVendorIE, + present), 0, "QComVendorIE", 0, 7, 7, SigIeQComVendorIE, + {0, 160, 198, 0, 0}, 3, DOT11F_EID_QCOMVENDORIE, 0, 0, }, + { offsetof(tDot11fBeacon, ESEVersion), offsetof(tDot11fIEESEVersion, + present), 0, "ESEVersion", 0, 7, 7, SigIeESEVersion, {0, 64, 150, 3, 0}, + 4, DOT11F_EID_ESEVERSION, 0, 0, }, + { offsetof(tDot11fBeacon, MBO_IE), offsetof(tDot11fIEMBO_IE, present), 0, + "MBO_IE", 0, 6, 295, SigIeMBO_IE, {80, 111, 154, 22, 0}, + 4, DOT11F_EID_MBO_IE, 0, 0, }, + { offsetof(tDot11fBeacon, qcn_ie), offsetof(tDot11fIEqcn_ie, present), 0, + "qcn_ie", 0, 6, 13, SigIeqcn_ie, {140, 253, 240, 1, 0}, + 4, DOT11F_EID_QCN_IE, 0, 0, }, + { offsetof(tDot11fBeacon, he_cap), offsetof(tDot11fIEhe_cap, present), 0, + "he_cap", 0, 23, 56, SigIehe_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_CAP, 35, 0, }, + { offsetof(tDot11fBeacon, he_op), offsetof(tDot11fIEhe_op, present), 0, + "he_op", 0, 8, 17, SigIehe_op, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_OP, 36, 0, }, + { offsetof(tDot11fBeacon, he_6ghz_band_cap), + offsetof(tDot11fIEhe_6ghz_band_cap, present), 0, "he_6ghz_band_cap", + 0, 4, 4, SigIehe_6ghz_band_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_6GHZ_BAND_CAP, 59, 0, }, + { offsetof(tDot11fBeacon, bss_color_change), + offsetof(tDot11fIEbss_color_change, present), 0, "bss_color_change", + 0, 4, 4, SigIebss_color_change, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BSS_COLOR_CHANGE, 42, 0, }, + { offsetof(tDot11fBeacon, mu_edca_param_set), + offsetof(tDot11fIEmu_edca_param_set, present), 0, "mu_edca_param_set", + 0, 15, 15, SigIemu_edca_param_set, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MU_EDCA_PARAM_SET, 38, 0, }, + { offsetof(tDot11fBeacon, esp_information), + offsetof(tDot11fIEesp_information, present), 0, "esp_information", + 0, 2, 98, SigIeesp_information, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ESP_INFORMATION, 11, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_beacon(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fBeacon *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_Beacon, IES_Beacon, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_beacon. */ + +static const tFFDefn FFS_Beacon1[] = { + { "TimeStamp", offsetof(tDot11fBeacon1, TimeStamp), SigFfTimeStamp, + DOT11F_FF_TIMESTAMP_LEN, }, + { "BeaconInterval", offsetof(tDot11fBeacon1, BeaconInterval), + SigFfBeaconInterval, DOT11F_FF_BEACONINTERVAL_LEN, }, + { "Capabilities", offsetof(tDot11fBeacon1, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_Beacon1[] = { + { offsetof(tDot11fBeacon1, SSID), offsetof(tDot11fIESSID, present), 0, + "SSID", 0, 2, 34, SigIeSSID, {0, 0, 0, 0, 0}, 0, DOT11F_EID_SSID, 0, 1, }, + { offsetof(tDot11fBeacon1, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fBeacon1, DSParams), offsetof(tDot11fIEDSParams, + present), 0, "DSParams", 0, 3, 3, SigIeDSParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_DSPARAMS, 0, 0, }, + { offsetof(tDot11fBeacon1, IBSSParams), offsetof(tDot11fIEIBSSParams, + present), 0, "IBSSParams", 0, 4, 4, SigIeIBSSParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_IBSSPARAMS, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_beacon1(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fBeacon1 *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_Beacon1, IES_Beacon1, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_beacon1. */ + +static const tFFDefn FFS_Beacon2[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_Beacon2[] = { + { offsetof(tDot11fBeacon2, Country), offsetof(tDot11fIECountry, present), + 0, "Country", 0, 5, 257, SigIeCountry, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_COUNTRY, 0, 0, }, + { offsetof(tDot11fBeacon2, PowerConstraints), + offsetof(tDot11fIEPowerConstraints, present), 0, "PowerConstraints", + 0, 3, 3, SigIePowerConstraints, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_POWERCONSTRAINTS, 0, 0, }, + { offsetof(tDot11fBeacon2, ChanSwitchAnn), + offsetof(tDot11fIEChanSwitchAnn, present), 0, "ChanSwitchAnn", + 0, 5, 5, SigIeChanSwitchAnn, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CHANSWITCHANN, 0, 0, }, + { offsetof(tDot11fBeacon2, ext_chan_switch_ann), + offsetof(tDot11fIEext_chan_switch_ann, present), 0, "ext_chan_switch_ann", + 0, 6, 6, SigIeext_chan_switch_ann, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXT_CHAN_SWITCH_ANN, 0, 0, }, + { offsetof(tDot11fBeacon2, SuppOperatingClasses), + offsetof(tDot11fIESuppOperatingClasses, present), 0, + "SuppOperatingClasses", 0, 3, 34, SigIeSuppOperatingClasses, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_SUPPOPERATINGCLASSES, 0, 0, }, + { offsetof(tDot11fBeacon2, Quiet), offsetof(tDot11fIEQuiet, present), 0, + "Quiet", 0, 8, 8, SigIeQuiet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QUIET, 0, 0, }, + { offsetof(tDot11fBeacon2, TPCReport), offsetof(tDot11fIETPCReport, + present), 0, "TPCReport", 0, 4, 4, SigIeTPCReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TPCREPORT, 0, 0, }, + { offsetof(tDot11fBeacon2, ERPInfo), offsetof(tDot11fIEERPInfo, present), + 0, "ERPInfo", 0, 3, 3, SigIeERPInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ERPINFO, 0, 0, }, + { offsetof(tDot11fBeacon2, ExtSuppRates), offsetof(tDot11fIEExtSuppRates, + present), 0, "ExtSuppRates", 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fBeacon2, RSNOpaque), offsetof(tDot11fIERSNOpaque, + present), 0, "RSNOpaque", 0, 2, 255, SigIeRSNOpaque, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RSNOPAQUE, 0, 0, }, + { offsetof(tDot11fBeacon2, EDCAParamSet), offsetof(tDot11fIEEDCAParamSet, + present), 0, "EDCAParamSet", 0, 20, 20, SigIeEDCAParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EDCAPARAMSET, 0, 0, }, + { offsetof(tDot11fBeacon2, APChannelReport), + offsetof(tDot11fIEAPChannelReport, present), 0, "APChannelReport", + 0, 3, 53, SigIeAPChannelReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_APCHANNELREPORT, 0, 0, }, + { offsetof(tDot11fBeacon2, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fBeacon2, MobilityDomain), + offsetof(tDot11fIEMobilityDomain, present), 0, "MobilityDomain", + 0, 5, 5, SigIeMobilityDomain, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MOBILITYDOMAIN, 0, 0, }, + { offsetof(tDot11fBeacon2, WPA), offsetof(tDot11fIEWPA, present), 0, "WPA", + 0, 8, 50, SigIeWPA, {0, 80, 242, 1, 0}, 4, DOT11F_EID_WPA, 0, 0, }, + { offsetof(tDot11fBeacon2, HTCaps), offsetof(tDot11fIEHTCaps, present), 0, + "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fBeacon2, HTInfo), offsetof(tDot11fIEHTInfo, present), 0, + "HTInfo", 0, 24, 56, SigIeHTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTINFO, 0, 0, }, + { offsetof(tDot11fBeacon2, sec_chan_offset_ele), + offsetof(tDot11fIEsec_chan_offset_ele, present), 0, "sec_chan_offset_ele", + 0, 3, 3, SigIesec_chan_offset_ele, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SEC_CHAN_OFFSET_ELE, 0, 0, }, + { offsetof(tDot11fBeacon2, WMMInfoAp), offsetof(tDot11fIEWMMInfoAp, + present), 0, "WMMInfoAp", 0, 9, 9, SigIeWMMInfoAp, {0, 80, 242, 2, 0}, + 5, DOT11F_EID_WMMINFOAP, 0, 0, }, + { offsetof(tDot11fBeacon2, WMMParams), offsetof(tDot11fIEWMMParams, + present), 0, "WMMParams", 0, 26, 26, SigIeWMMParams, {0, 80, 242, 2, 1}, + 5, DOT11F_EID_WMMPARAMS, 0, 0, }, + { offsetof(tDot11fBeacon2, WMMCaps), offsetof(tDot11fIEWMMCaps, present), + 0, "WMMCaps", 0, 9, 9, SigIeWMMCaps, {0, 80, 242, 2, 5}, + 5, DOT11F_EID_WMMCAPS, 0, 0, }, + { offsetof(tDot11fBeacon2, WscBeacon), offsetof(tDot11fIEWscBeacon, + present), 0, "WscBeacon", 0, 6, 84, SigIeWscBeacon, {0, 80, 242, 4, 0}, + 4, DOT11F_EID_WSCBEACON, 0, 0, }, + { offsetof(tDot11fBeacon2, WAPI), offsetof(tDot11fIEWAPI, present), 0, + "WAPI", 0, 14, 112, SigIeWAPI, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_WAPI, 0, 0, }, + { offsetof(tDot11fBeacon2, ESERadMgmtCap), + offsetof(tDot11fIEESERadMgmtCap, present), 0, "ESERadMgmtCap", + 0, 8, 8, SigIeESERadMgmtCap, {0, 64, 150, 1, 0}, + 4, DOT11F_EID_ESERADMGMTCAP, 0, 0, }, + { offsetof(tDot11fBeacon2, ESETrafStrmMet), + offsetof(tDot11fIEESETrafStrmMet, present), 0, "ESETrafStrmMet", + 0, 10, 10, SigIeESETrafStrmMet, {0, 64, 150, 7, 0}, + 4, DOT11F_EID_ESETRAFSTRMMET, 0, 0, }, + { offsetof(tDot11fBeacon2, ESETxmitPower), + offsetof(tDot11fIEESETxmitPower, present), 0, "ESETxmitPower", + 0, 8, 8, SigIeESETxmitPower, {0, 64, 150, 0, 0}, + 4, DOT11F_EID_ESETXMITPOWER, 0, 0, }, + { offsetof(tDot11fBeacon2, P2PBeacon), offsetof(tDot11fIEP2PBeacon, + present), 0, "P2PBeacon", 0, 6, 61, SigIeP2PBeacon, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PBEACON, 0, 0, }, + { offsetof(tDot11fBeacon2, VHTCaps), offsetof(tDot11fIEVHTCaps, present), + 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fBeacon2, VHTOperation), offsetof(tDot11fIEVHTOperation, + present), 0, "VHTOperation", 0, 7, 7, SigIeVHTOperation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTOPERATION, 0, 0, }, + { offsetof(tDot11fBeacon2, vht_transmit_power_env), + offsetof(tDot11fIEvht_transmit_power_env, present), 0, + "vht_transmit_power_env", 0, 4, 7, SigIevht_transmit_power_env, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_VHT_TRANSMIT_POWER_ENV, 0, 0, }, + { offsetof(tDot11fBeacon2, ChannelSwitchWrapper), + offsetof(tDot11fIEChannelSwitchWrapper, present), 0, + "ChannelSwitchWrapper", 0, 2, 14, SigIeChannelSwitchWrapper, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_CHANNELSWITCHWRAPPER, 0, 0, }, + { offsetof(tDot11fBeacon2, VHTExtBssLoad), + offsetof(tDot11fIEVHTExtBssLoad, present), 0, "VHTExtBssLoad", + 0, 7, 7, SigIeVHTExtBssLoad, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTEXTBSSLOAD, 0, 0, }, + { offsetof(tDot11fBeacon2, ExtCap), offsetof(tDot11fIEExtCap, present), 0, + "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fBeacon2, OperatingMode), + offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode", + 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OPERATINGMODE, 0, 0, }, + { offsetof(tDot11fBeacon2, WiderBWChanSwitchAnn), + offsetof(tDot11fIEWiderBWChanSwitchAnn, present), 0, + "WiderBWChanSwitchAnn", 0, 5, 5, SigIeWiderBWChanSwitchAnn, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_WIDERBWCHANSWITCHANN, 0, 0, }, + { offsetof(tDot11fBeacon2, OBSSScanParameters), + offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters", + 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, 0, }, + { offsetof(tDot11fBeacon2, fils_indication), + offsetof(tDot11fIEfils_indication, present), 0, "fils_indication", + 0, 6, 259, SigIefils_indication, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_INDICATION, 0, 0, }, + { offsetof(tDot11fBeacon2, Vendor1IE), offsetof(tDot11fIEVendor1IE, + present), 0, "Vendor1IE", 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, + 3, DOT11F_EID_VENDOR1IE, 0, 0, }, + { offsetof(tDot11fBeacon2, vendor_vht_ie), + offsetof(tDot11fIEvendor_vht_ie, present), 0, "vendor_vht_ie", + 0, 7, 28, SigIevendor_vht_ie, {0, 144, 76, 4, 0}, + 4, DOT11F_EID_VENDOR_VHT_IE, 0, 0, }, + { offsetof(tDot11fBeacon2, Vendor3IE), offsetof(tDot11fIEVendor3IE, + present), 0, "Vendor3IE", 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, + 3, DOT11F_EID_VENDOR3IE, 0, 0, }, + { offsetof(tDot11fBeacon2, hs20vendor_ie), + offsetof(tDot11fIEhs20vendor_ie, present), 0, "hs20vendor_ie", + 0, 7, 9, SigIehs20vendor_ie, {80, 111, 154, 16, 0}, + 4, DOT11F_EID_HS20VENDOR_IE, 0, 0, }, + { offsetof(tDot11fBeacon2, QComVendorIE), offsetof(tDot11fIEQComVendorIE, + present), 0, "QComVendorIE", 0, 7, 7, SigIeQComVendorIE, + {0, 160, 198, 0, 0}, 3, DOT11F_EID_QCOMVENDORIE, 0, 0, }, + { offsetof(tDot11fBeacon2, ESEVersion), offsetof(tDot11fIEESEVersion, + present), 0, "ESEVersion", 0, 7, 7, SigIeESEVersion, {0, 64, 150, 3, 0}, + 4, DOT11F_EID_ESEVERSION, 0, 0, }, + { offsetof(tDot11fBeacon2, qcn_ie), offsetof(tDot11fIEqcn_ie, present), 0, + "qcn_ie", 0, 6, 13, SigIeqcn_ie, {140, 253, 240, 1, 0}, + 4, DOT11F_EID_QCN_IE, 0, 0, }, + { offsetof(tDot11fBeacon2, he_cap), offsetof(tDot11fIEhe_cap, present), 0, + "he_cap", 0, 23, 56, SigIehe_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_CAP, 35, 0, }, + { offsetof(tDot11fBeacon2, he_op), offsetof(tDot11fIEhe_op, present), 0, + "he_op", 0, 8, 17, SigIehe_op, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_OP, 36, 0, }, + { offsetof(tDot11fBeacon2, he_6ghz_band_cap), + offsetof(tDot11fIEhe_6ghz_band_cap, present), 0, "he_6ghz_band_cap", + 0, 4, 4, SigIehe_6ghz_band_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_6GHZ_BAND_CAP, 59, 0, }, + { offsetof(tDot11fBeacon2, bss_color_change), + offsetof(tDot11fIEbss_color_change, present), 0, "bss_color_change", + 0, 4, 4, SigIebss_color_change, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BSS_COLOR_CHANGE, 42, 0, }, + { offsetof(tDot11fBeacon2, mu_edca_param_set), + offsetof(tDot11fIEmu_edca_param_set, present), 0, "mu_edca_param_set", + 0, 15, 15, SigIemu_edca_param_set, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MU_EDCA_PARAM_SET, 38, 0, }, + { offsetof(tDot11fBeacon2, esp_information), + offsetof(tDot11fIEesp_information, present), 0, "esp_information", + 0, 2, 98, SigIeesp_information, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ESP_INFORMATION, 11, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_beacon2(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fBeacon2 *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_Beacon2, IES_Beacon2, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_beacon2. */ + +static const tFFDefn FFS_BeaconIEs[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_BeaconIEs[] = { + { offsetof(tDot11fBeaconIEs, SSID), offsetof(tDot11fIESSID, present), 0, + "SSID", 0, 2, 34, SigIeSSID, {0, 0, 0, 0, 0}, 0, DOT11F_EID_SSID, 0, 1, }, + { offsetof(tDot11fBeaconIEs, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fBeaconIEs, FHParamSet), offsetof(tDot11fIEFHParamSet, + present), 0, "FHParamSet", 0, 7, 7, SigIeFHParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FHPARAMSET, 0, 0, }, + { offsetof(tDot11fBeaconIEs, DSParams), offsetof(tDot11fIEDSParams, + present), 0, "DSParams", 0, 3, 3, SigIeDSParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_DSPARAMS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, CFParams), offsetof(tDot11fIECFParams, + present), 0, "CFParams", 0, 8, 8, SigIeCFParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CFPARAMS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, IBSSParams), offsetof(tDot11fIEIBSSParams, + present), 0, "IBSSParams", 0, 4, 4, SigIeIBSSParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_IBSSPARAMS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, TIM), offsetof(tDot11fIETIM, present), 0, + "TIM", 0, 6, 256, SigIeTIM, {0, 0, 0, 0, 0}, 0, DOT11F_EID_TIM, 0, 0, }, + { offsetof(tDot11fBeaconIEs, Country), offsetof(tDot11fIECountry, + present), 0, "Country", 0, 5, 257, SigIeCountry, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_COUNTRY, 0, 0, }, + { offsetof(tDot11fBeaconIEs, FHParams), offsetof(tDot11fIEFHParams, + present), 0, "FHParams", 0, 4, 4, SigIeFHParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FHPARAMS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, FHPattTable), offsetof(tDot11fIEFHPattTable, + present), 0, "FHPattTable", 0, 6, 257, SigIeFHPattTable, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FHPATTTABLE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, PowerConstraints), + offsetof(tDot11fIEPowerConstraints, present), 0, "PowerConstraints", + 0, 3, 3, SigIePowerConstraints, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_POWERCONSTRAINTS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ChanSwitchAnn), + offsetof(tDot11fIEChanSwitchAnn, present), 0, "ChanSwitchAnn", + 0, 5, 5, SigIeChanSwitchAnn, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CHANSWITCHANN, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ext_chan_switch_ann), + offsetof(tDot11fIEext_chan_switch_ann, present), 0, "ext_chan_switch_ann", + 0, 6, 6, SigIeext_chan_switch_ann, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXT_CHAN_SWITCH_ANN, 0, 0, }, + { offsetof(tDot11fBeaconIEs, SuppOperatingClasses), + offsetof(tDot11fIESuppOperatingClasses, present), 0, + "SuppOperatingClasses", 0, 3, 34, SigIeSuppOperatingClasses, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_SUPPOPERATINGCLASSES, 0, 0, }, + { offsetof(tDot11fBeaconIEs, Quiet), offsetof(tDot11fIEQuiet, present), 0, + "Quiet", 0, 8, 8, SigIeQuiet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QUIET, 0, 0, }, + { offsetof(tDot11fBeaconIEs, TPCReport), offsetof(tDot11fIETPCReport, + present), 0, "TPCReport", 0, 4, 4, SigIeTPCReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TPCREPORT, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ERPInfo), offsetof(tDot11fIEERPInfo, + present), 0, "ERPInfo", 0, 3, 3, SigIeERPInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ERPINFO, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fBeaconIEs, RSN), offsetof(tDot11fIERSN, present), 0, + "RSN", 0, 4, 132, SigIeRSN, {0, 0, 0, 0, 0}, 0, DOT11F_EID_RSN, 0, 0, }, + { offsetof(tDot11fBeaconIEs, QBSSLoad), offsetof(tDot11fIEQBSSLoad, + present), 0, "QBSSLoad", 0, 7, 7, SigIeQBSSLoad, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QBSSLOAD, 0, 0, }, + { offsetof(tDot11fBeaconIEs, EDCAParamSet), + offsetof(tDot11fIEEDCAParamSet, present), 0, "EDCAParamSet", + 0, 20, 20, SigIeEDCAParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EDCAPARAMSET, 0, 0, }, + { offsetof(tDot11fBeaconIEs, QOSCapsAp), offsetof(tDot11fIEQOSCapsAp, + present), 0, "QOSCapsAp", 0, 3, 3, SigIeQOSCapsAp, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSCAPSAP, 0, 0, }, + { offsetof(tDot11fBeaconIEs, APChannelReport), + offsetof(tDot11fIEAPChannelReport, present), 0, "APChannelReport", + 0, 3, 53, SigIeAPChannelReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_APCHANNELREPORT, 0, 0, }, + { offsetof(tDot11fBeaconIEs, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fBeaconIEs, MobilityDomain), + offsetof(tDot11fIEMobilityDomain, present), 0, "MobilityDomain", + 0, 5, 5, SigIeMobilityDomain, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MOBILITYDOMAIN, 0, 0, }, + { offsetof(tDot11fBeaconIEs, WPA), offsetof(tDot11fIEWPA, present), 0, + "WPA", 0, 8, 50, SigIeWPA, {0, 80, 242, 1, 0}, 4, DOT11F_EID_WPA, 0, 0, }, + { offsetof(tDot11fBeaconIEs, HTCaps), offsetof(tDot11fIEHTCaps, present), + 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, HTInfo), offsetof(tDot11fIEHTInfo, present), + 0, "HTInfo", 0, 24, 56, SigIeHTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTINFO, 0, 0, }, + { offsetof(tDot11fBeaconIEs, sec_chan_offset_ele), + offsetof(tDot11fIEsec_chan_offset_ele, present), 0, "sec_chan_offset_ele", + 0, 3, 3, SigIesec_chan_offset_ele, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SEC_CHAN_OFFSET_ELE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, WMMInfoAp), offsetof(tDot11fIEWMMInfoAp, + present), 0, "WMMInfoAp", 0, 9, 9, SigIeWMMInfoAp, {0, 80, 242, 2, 0}, + 5, DOT11F_EID_WMMINFOAP, 0, 0, }, + { offsetof(tDot11fBeaconIEs, WMMParams), offsetof(tDot11fIEWMMParams, + present), 0, "WMMParams", 0, 26, 26, SigIeWMMParams, {0, 80, 242, 2, 1}, + 5, DOT11F_EID_WMMPARAMS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, WMMCaps), offsetof(tDot11fIEWMMCaps, + present), 0, "WMMCaps", 0, 9, 9, SigIeWMMCaps, {0, 80, 242, 2, 5}, + 5, DOT11F_EID_WMMCAPS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, WAPI), offsetof(tDot11fIEWAPI, present), 0, + "WAPI", 0, 14, 112, SigIeWAPI, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_WAPI, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ESEVersion), offsetof(tDot11fIEESEVersion, + present), 0, "ESEVersion", 0, 7, 7, SigIeESEVersion, {0, 64, 150, 3, 0}, + 4, DOT11F_EID_ESEVERSION, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ESERadMgmtCap), + offsetof(tDot11fIEESERadMgmtCap, present), 0, "ESERadMgmtCap", + 0, 8, 8, SigIeESERadMgmtCap, {0, 64, 150, 1, 0}, + 4, DOT11F_EID_ESERADMGMTCAP, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ESETrafStrmMet), + offsetof(tDot11fIEESETrafStrmMet, present), 0, "ESETrafStrmMet", + 0, 10, 10, SigIeESETrafStrmMet, {0, 64, 150, 7, 0}, + 4, DOT11F_EID_ESETRAFSTRMMET, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ESETxmitPower), + offsetof(tDot11fIEESETxmitPower, present), 0, "ESETxmitPower", + 0, 8, 8, SigIeESETxmitPower, {0, 64, 150, 0, 0}, + 4, DOT11F_EID_ESETXMITPOWER, 0, 0, }, + { offsetof(tDot11fBeaconIEs, WscBeaconProbeRes), + offsetof(tDot11fIEWscBeaconProbeRes, present), 0, "WscBeaconProbeRes", + 0, 6, 319, SigIeWscBeaconProbeRes, {0, 80, 242, 4, 0}, + 4, DOT11F_EID_WSCBEACONPROBERES, 0, 0, }, + { offsetof(tDot11fBeaconIEs, P2PBeaconProbeRes), + offsetof(tDot11fIEP2PBeaconProbeRes, present), 0, "P2PBeaconProbeRes", + 0, 6, 1150, SigIeP2PBeaconProbeRes, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PBEACONPROBERES, 0, 0, }, + { offsetof(tDot11fBeaconIEs, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, VHTOperation), + offsetof(tDot11fIEVHTOperation, present), 0, "VHTOperation", + 0, 7, 7, SigIeVHTOperation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTOPERATION, 0, 0, }, + { offsetof(tDot11fBeaconIEs, VHTExtBssLoad), + offsetof(tDot11fIEVHTExtBssLoad, present), 0, "VHTExtBssLoad", + 0, 7, 7, SigIeVHTExtBssLoad, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTEXTBSSLOAD, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ExtCap), offsetof(tDot11fIEExtCap, present), + 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fBeaconIEs, OperatingMode), + offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode", + 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OPERATINGMODE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, WiderBWChanSwitchAnn), + offsetof(tDot11fIEWiderBWChanSwitchAnn, present), 0, + "WiderBWChanSwitchAnn", 0, 5, 5, SigIeWiderBWChanSwitchAnn, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_WIDERBWCHANSWITCHANN, 0, 0, }, + { offsetof(tDot11fBeaconIEs, OBSSScanParameters), + offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters", + 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, 0, }, + { offsetof(tDot11fBeaconIEs, fils_indication), + offsetof(tDot11fIEfils_indication, present), 0, "fils_indication", + 0, 6, 259, SigIefils_indication, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_INDICATION, 0, 0, }, + { offsetof(tDot11fBeaconIEs, Vendor1IE), offsetof(tDot11fIEVendor1IE, + present), 0, "Vendor1IE", 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, + 3, DOT11F_EID_VENDOR1IE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, vendor_vht_ie), + offsetof(tDot11fIEvendor_vht_ie, present), 0, "vendor_vht_ie", + 0, 7, 28, SigIevendor_vht_ie, {0, 144, 76, 4, 0}, + 4, DOT11F_EID_VENDOR_VHT_IE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, Vendor3IE), offsetof(tDot11fIEVendor3IE, + present), 0, "Vendor3IE", 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, + 3, DOT11F_EID_VENDOR3IE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, hs20vendor_ie), + offsetof(tDot11fIEhs20vendor_ie, present), 0, "hs20vendor_ie", + 0, 7, 9, SigIehs20vendor_ie, {80, 111, 154, 16, 0}, + 4, DOT11F_EID_HS20VENDOR_IE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, ChannelSwitchWrapper), + offsetof(tDot11fIEChannelSwitchWrapper, present), 0, + "ChannelSwitchWrapper", 0, 2, 14, SigIeChannelSwitchWrapper, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_CHANNELSWITCHWRAPPER, 0, 0, }, + { offsetof(tDot11fBeaconIEs, QComVendorIE), + offsetof(tDot11fIEQComVendorIE, present), 0, "QComVendorIE", + 0, 7, 7, SigIeQComVendorIE, {0, 160, 198, 0, 0}, + 3, DOT11F_EID_QCOMVENDORIE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, MBO_IE), offsetof(tDot11fIEMBO_IE, present), + 0, "MBO_IE", 0, 6, 295, SigIeMBO_IE, {80, 111, 154, 22, 0}, + 4, DOT11F_EID_MBO_IE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, qcn_ie), offsetof(tDot11fIEqcn_ie, present), + 0, "qcn_ie", 0, 6, 13, SigIeqcn_ie, {140, 253, 240, 1, 0}, + 4, DOT11F_EID_QCN_IE, 0, 0, }, + { offsetof(tDot11fBeaconIEs, he_cap), offsetof(tDot11fIEhe_cap, present), + 0, "he_cap", 0, 23, 56, SigIehe_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_CAP, 35, 0, }, + { offsetof(tDot11fBeaconIEs, he_op), offsetof(tDot11fIEhe_op, present), 0, + "he_op", 0, 8, 17, SigIehe_op, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_OP, 36, 0, }, + { offsetof(tDot11fBeaconIEs, he_6ghz_band_cap), + offsetof(tDot11fIEhe_6ghz_band_cap, present), 0, "he_6ghz_band_cap", + 0, 4, 4, SigIehe_6ghz_band_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_6GHZ_BAND_CAP, 59, 0, }, + { offsetof(tDot11fBeaconIEs, bss_color_change), + offsetof(tDot11fIEbss_color_change, present), 0, "bss_color_change", + 0, 4, 4, SigIebss_color_change, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BSS_COLOR_CHANGE, 42, 0, }, + { offsetof(tDot11fBeaconIEs, mu_edca_param_set), + offsetof(tDot11fIEmu_edca_param_set, present), 0, "mu_edca_param_set", + 0, 15, 15, SigIemu_edca_param_set, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MU_EDCA_PARAM_SET, 38, 0, }, + { offsetof(tDot11fBeaconIEs, esp_information), + offsetof(tDot11fIEesp_information, present), 0, "esp_information", + 0, 2, 98, SigIeesp_information, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ESP_INFORMATION, 11, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_beacon_i_es(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fBeaconIEs *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_BeaconIEs, IES_BeaconIEs, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_beacon_i_es. */ + +static const tFFDefn FFS_ChannelSwitch[] = { + { "Category", offsetof(tDot11fChannelSwitch, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fChannelSwitch, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_ChannelSwitch[] = { + { offsetof(tDot11fChannelSwitch, ChanSwitchAnn), + offsetof(tDot11fIEChanSwitchAnn, present), 0, "ChanSwitchAnn", + 0, 5, 5, SigIeChanSwitchAnn, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CHANSWITCHANN, 0, 1, }, + { offsetof(tDot11fChannelSwitch, sec_chan_offset_ele), + offsetof(tDot11fIEsec_chan_offset_ele, present), 0, "sec_chan_offset_ele", + 0, 3, 3, SigIesec_chan_offset_ele, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SEC_CHAN_OFFSET_ELE, 0, 0, }, + { offsetof(tDot11fChannelSwitch, WiderBWChanSwitchAnn), + offsetof(tDot11fIEWiderBWChanSwitchAnn, present), 0, + "WiderBWChanSwitchAnn", 0, 5, 5, SigIeWiderBWChanSwitchAnn, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_WIDERBWCHANSWITCHANN, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_channel_switch(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fChannelSwitch *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_ChannelSwitch, IES_ChannelSwitch, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_channel_switch. */ + +static const tFFDefn FFS_DeAuth[] = { + { "Reason", offsetof(tDot11fDeAuth, Reason), SigFfReason, + DOT11F_FF_REASON_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_DeAuth[] = { + { offsetof(tDot11fDeAuth, P2PDeAuth), offsetof(tDot11fIEP2PDeAuth, + present), 0, "P2PDeAuth", 0, 6, 10, SigIeP2PDeAuth, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PDEAUTH, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_de_auth(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fDeAuth *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_DeAuth, IES_DeAuth, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_de_auth. */ + +static const tFFDefn FFS_DelTS[] = { + { "Category", offsetof(tDot11fDelTS, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fDelTS, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "TSInfo", offsetof(tDot11fDelTS, TSInfo), SigFfTSInfo, + DOT11F_FF_TSINFO_LEN, }, + { "Reason", offsetof(tDot11fDelTS, Reason), SigFfReason, + DOT11F_FF_REASON_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_DelTS[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_del_ts(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fDelTS *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_DelTS, IES_DelTS, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_del_ts. */ + +static const tFFDefn FFS_Disassociation[] = { + { "Reason", offsetof(tDot11fDisassociation, Reason), SigFfReason, + DOT11F_FF_REASON_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_Disassociation[] = { + { offsetof(tDot11fDisassociation, P2PDisAssoc), + offsetof(tDot11fIEP2PDisAssoc, present), 0, "P2PDisAssoc", + 0, 6, 10, SigIeP2PDisAssoc, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PDISASSOC, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_disassociation(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fDisassociation *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_Disassociation, IES_Disassociation, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_disassociation. */ + +static const tFFDefn FFS_LinkMeasurementReport[] = { + { "Category", offsetof(tDot11fLinkMeasurementReport, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fLinkMeasurementReport, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fLinkMeasurementReport, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "TPCEleID", offsetof(tDot11fLinkMeasurementReport, TPCEleID), + SigFfTPCEleID, DOT11F_FF_TPCELEID_LEN, }, + { "TPCEleLen", offsetof(tDot11fLinkMeasurementReport, TPCEleLen), + SigFfTPCEleLen, DOT11F_FF_TPCELELEN_LEN, }, + { "TxPower", offsetof(tDot11fLinkMeasurementReport, TxPower), + SigFfTxPower, DOT11F_FF_TXPOWER_LEN, }, + { "LinkMargin", offsetof(tDot11fLinkMeasurementReport, LinkMargin), + SigFfLinkMargin, DOT11F_FF_LINKMARGIN_LEN, }, + { "RxAntennaId", offsetof(tDot11fLinkMeasurementReport, RxAntennaId), + SigFfRxAntennaId, DOT11F_FF_RXANTENNAID_LEN, }, + { "TxAntennaId", offsetof(tDot11fLinkMeasurementReport, TxAntennaId), + SigFfTxAntennaId, DOT11F_FF_TXANTENNAID_LEN, }, + { "RCPI", offsetof(tDot11fLinkMeasurementReport, RCPI), SigFfRCPI, + DOT11F_FF_RCPI_LEN, }, + { "RSNI", offsetof(tDot11fLinkMeasurementReport, RSNI), SigFfRSNI, + DOT11F_FF_RSNI_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_LinkMeasurementReport[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_link_measurement_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fLinkMeasurementReport *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_LinkMeasurementReport, IES_LinkMeasurementReport, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_link_measurement_report. */ + +static const tFFDefn FFS_LinkMeasurementRequest[] = { + { "Category", offsetof(tDot11fLinkMeasurementRequest, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fLinkMeasurementRequest, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fLinkMeasurementRequest, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "TxPower", offsetof(tDot11fLinkMeasurementRequest, TxPower), + SigFfTxPower, DOT11F_FF_TXPOWER_LEN, }, + { "MaxTxPower", offsetof(tDot11fLinkMeasurementRequest, MaxTxPower), + SigFfMaxTxPower, DOT11F_FF_MAXTXPOWER_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_LinkMeasurementRequest[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_link_measurement_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fLinkMeasurementRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_LinkMeasurementRequest, IES_LinkMeasurementRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_link_measurement_request. */ + +static const tFFDefn FFS_MeasurementReport[] = { + { "Category", offsetof(tDot11fMeasurementReport, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fMeasurementReport, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fMeasurementReport, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_MeasurementReport[] = { + { offsetof(tDot11fMeasurementReport, MeasurementReport), + offsetof(tDot11fIEMeasurementReport, present), 0, "MeasurementReport", + 0, 5, 31, SigIeMeasurementReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MEASUREMENTREPORT, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_measurement_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fMeasurementReport *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_MeasurementReport, IES_MeasurementReport, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_measurement_report. */ + +static const tFFDefn FFS_MeasurementRequest[] = { + { "Category", offsetof(tDot11fMeasurementRequest, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fMeasurementRequest, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fMeasurementRequest, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_MeasurementRequest[] = { + { offsetof(tDot11fMeasurementRequest, MeasurementRequest), + offsetof(tDot11fIEMeasurementRequest, present), + offsetof(tDot11fMeasurementRequest, num_MeasurementRequest), + "MeasurementRequest", 4, 6, 18, SigIeMeasurementRequest, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MEASUREMENTREQUEST, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_measurement_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fMeasurementRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_MeasurementRequest, IES_MeasurementRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_measurement_request. */ + +static const tFFDefn FFS_NeighborReportRequest[] = { + { "Category", offsetof(tDot11fNeighborReportRequest, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fNeighborReportRequest, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fNeighborReportRequest, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_NeighborReportRequest[] = { + { offsetof(tDot11fNeighborReportRequest, SSID), offsetof(tDot11fIESSID, + present), 0, "SSID", 0, 2, 34, SigIeSSID, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SSID, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_neighbor_report_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fNeighborReportRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_NeighborReportRequest, IES_NeighborReportRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_neighbor_report_request. */ + +static const tFFDefn FFS_NeighborReportResponse[] = { + { "Category", offsetof(tDot11fNeighborReportResponse, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fNeighborReportResponse, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fNeighborReportResponse, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_NeighborReportResponse[] = { + { offsetof(tDot11fNeighborReportResponse, NeighborReport), + offsetof(tDot11fIENeighborReport, present), + offsetof(tDot11fNeighborReportResponse, num_NeighborReport), + "NeighborReport", 15, 15, 548, SigIeNeighborReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_NEIGHBORREPORT, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_neighbor_report_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fNeighborReportResponse *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_NeighborReportResponse, IES_NeighborReportResponse, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_neighbor_report_response. */ + +static const tFFDefn FFS_OperatingMode[] = { + { "Category", offsetof(tDot11fOperatingMode, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fOperatingMode, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "OperatingMode", offsetof(tDot11fOperatingMode, OperatingMode), + SigFfOperatingMode, DOT11F_FF_OPERATINGMODE_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_OperatingMode[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_operating_mode(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fOperatingMode *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_OperatingMode, IES_OperatingMode, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_operating_mode. */ + +static const tFFDefn FFS_ProbeRequest[] = { + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_ProbeRequest[] = { + { offsetof(tDot11fProbeRequest, SSID), offsetof(tDot11fIESSID, present), 0, + "SSID", 0, 2, 34, SigIeSSID, {0, 0, 0, 0, 0}, 0, DOT11F_EID_SSID, 0, 1, }, + { offsetof(tDot11fProbeRequest, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fProbeRequest, RequestedInfo), + offsetof(tDot11fIERequestedInfo, present), 0, "RequestedInfo", + 0, 2, 257, SigIeRequestedInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_REQUESTEDINFO, 0, 0, }, + { offsetof(tDot11fProbeRequest, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fProbeRequest, DSParams), offsetof(tDot11fIEDSParams, + present), 0, "DSParams", 0, 3, 3, SigIeDSParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_DSPARAMS, 0, 0, }, + { offsetof(tDot11fProbeRequest, HTCaps), offsetof(tDot11fIEHTCaps, + present), 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fProbeRequest, WscProbeReq), + offsetof(tDot11fIEWscProbeReq, present), 0, "WscProbeReq", + 0, 6, 286, SigIeWscProbeReq, {0, 80, 242, 4, 0}, + 4, DOT11F_EID_WSCPROBEREQ, 0, 0, }, + { offsetof(tDot11fProbeRequest, WFATPC), offsetof(tDot11fIEWFATPC, + present), 0, "WFATPC", 0, 9, 9, SigIeWFATPC, {0, 80, 242, 8, 0}, + 5, DOT11F_EID_WFATPC, 0, 0, }, + { offsetof(tDot11fProbeRequest, P2PProbeReq), + offsetof(tDot11fIEP2PProbeReq, present), 0, "P2PProbeReq", + 0, 6, 43, SigIeP2PProbeReq, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PPROBEREQ, 0, 0, }, + { offsetof(tDot11fProbeRequest, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fProbeRequest, ExtCap), offsetof(tDot11fIEExtCap, + present), 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fProbeRequest, qcn_ie), offsetof(tDot11fIEqcn_ie, + present), 0, "qcn_ie", 0, 6, 13, SigIeqcn_ie, {140, 253, 240, 1, 0}, + 4, DOT11F_EID_QCN_IE, 0, 0, }, + { offsetof(tDot11fProbeRequest, he_cap), offsetof(tDot11fIEhe_cap, + present), 0, "he_cap", 0, 23, 56, SigIehe_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_CAP, 35, 0, }, + { offsetof(tDot11fProbeRequest, he_6ghz_band_cap), + offsetof(tDot11fIEhe_6ghz_band_cap, present), 0, "he_6ghz_band_cap", + 0, 4, 4, SigIehe_6ghz_band_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_6GHZ_BAND_CAP, 59, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_probe_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fProbeRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_ProbeRequest, IES_ProbeRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_probe_request. */ + +static const tFFDefn FFS_ProbeResponse[] = { + { "TimeStamp", offsetof(tDot11fProbeResponse, TimeStamp), SigFfTimeStamp, + DOT11F_FF_TIMESTAMP_LEN, }, + { "BeaconInterval", offsetof(tDot11fProbeResponse, BeaconInterval), + SigFfBeaconInterval, DOT11F_FF_BEACONINTERVAL_LEN, }, + { "Capabilities", offsetof(tDot11fProbeResponse, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_ProbeResponse[] = { + { offsetof(tDot11fProbeResponse, SSID), offsetof(tDot11fIESSID, present), + 0, "SSID", 0, 2, 34, SigIeSSID, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SSID, 0, 1, }, + { offsetof(tDot11fProbeResponse, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fProbeResponse, FHParamSet), + offsetof(tDot11fIEFHParamSet, present), 0, "FHParamSet", + 0, 7, 7, SigIeFHParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FHPARAMSET, 0, 0, }, + { offsetof(tDot11fProbeResponse, DSParams), offsetof(tDot11fIEDSParams, + present), 0, "DSParams", 0, 3, 3, SigIeDSParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_DSPARAMS, 0, 0, }, + { offsetof(tDot11fProbeResponse, CFParams), offsetof(tDot11fIECFParams, + present), 0, "CFParams", 0, 8, 8, SigIeCFParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CFPARAMS, 0, 0, }, + { offsetof(tDot11fProbeResponse, IBSSParams), + offsetof(tDot11fIEIBSSParams, present), 0, "IBSSParams", + 0, 4, 4, SigIeIBSSParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_IBSSPARAMS, 0, 0, }, + { offsetof(tDot11fProbeResponse, Country), offsetof(tDot11fIECountry, + present), 0, "Country", 0, 5, 257, SigIeCountry, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_COUNTRY, 0, 0, }, + { offsetof(tDot11fProbeResponse, FHParams), offsetof(tDot11fIEFHParams, + present), 0, "FHParams", 0, 4, 4, SigIeFHParams, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FHPARAMS, 0, 0, }, + { offsetof(tDot11fProbeResponse, FHPattTable), + offsetof(tDot11fIEFHPattTable, present), 0, "FHPattTable", + 0, 6, 257, SigIeFHPattTable, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FHPATTTABLE, 0, 0, }, + { offsetof(tDot11fProbeResponse, PowerConstraints), + offsetof(tDot11fIEPowerConstraints, present), 0, "PowerConstraints", + 0, 3, 3, SigIePowerConstraints, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_POWERCONSTRAINTS, 0, 0, }, + { offsetof(tDot11fProbeResponse, ChanSwitchAnn), + offsetof(tDot11fIEChanSwitchAnn, present), 0, "ChanSwitchAnn", + 0, 5, 5, SigIeChanSwitchAnn, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_CHANSWITCHANN, 0, 0, }, + { offsetof(tDot11fProbeResponse, ext_chan_switch_ann), + offsetof(tDot11fIEext_chan_switch_ann, present), 0, "ext_chan_switch_ann", + 0, 6, 6, SigIeext_chan_switch_ann, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXT_CHAN_SWITCH_ANN, 0, 0, }, + { offsetof(tDot11fProbeResponse, SuppOperatingClasses), + offsetof(tDot11fIESuppOperatingClasses, present), 0, + "SuppOperatingClasses", 0, 3, 34, SigIeSuppOperatingClasses, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_SUPPOPERATINGCLASSES, 0, 0, }, + { offsetof(tDot11fProbeResponse, Quiet), offsetof(tDot11fIEQuiet, + present), 0, "Quiet", 0, 8, 8, SigIeQuiet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QUIET, 0, 0, }, + { offsetof(tDot11fProbeResponse, TPCReport), offsetof(tDot11fIETPCReport, + present), 0, "TPCReport", 0, 4, 4, SigIeTPCReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TPCREPORT, 0, 0, }, + { offsetof(tDot11fProbeResponse, ERPInfo), offsetof(tDot11fIEERPInfo, + present), 0, "ERPInfo", 0, 3, 3, SigIeERPInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ERPINFO, 0, 0, }, + { offsetof(tDot11fProbeResponse, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fProbeResponse, RSNOpaque), offsetof(tDot11fIERSNOpaque, + present), 0, "RSNOpaque", 0, 2, 255, SigIeRSNOpaque, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RSNOPAQUE, 0, 0, }, + { offsetof(tDot11fProbeResponse, QBSSLoad), offsetof(tDot11fIEQBSSLoad, + present), 0, "QBSSLoad", 0, 7, 7, SigIeQBSSLoad, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QBSSLOAD, 0, 0, }, + { offsetof(tDot11fProbeResponse, EDCAParamSet), + offsetof(tDot11fIEEDCAParamSet, present), 0, "EDCAParamSet", + 0, 20, 20, SigIeEDCAParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EDCAPARAMSET, 0, 0, }, + { offsetof(tDot11fProbeResponse, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fProbeResponse, APChannelReport), + offsetof(tDot11fIEAPChannelReport, present), 0, "APChannelReport", + 0, 3, 53, SigIeAPChannelReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_APCHANNELREPORT, 0, 0, }, + { offsetof(tDot11fProbeResponse, MobilityDomain), + offsetof(tDot11fIEMobilityDomain, present), 0, "MobilityDomain", + 0, 5, 5, SigIeMobilityDomain, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MOBILITYDOMAIN, 0, 0, }, + { offsetof(tDot11fProbeResponse, WPA), offsetof(tDot11fIEWPA, present), 0, + "WPA", 0, 8, 50, SigIeWPA, {0, 80, 242, 1, 0}, 4, DOT11F_EID_WPA, 0, 0, }, + { offsetof(tDot11fProbeResponse, HTCaps), offsetof(tDot11fIEHTCaps, + present), 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fProbeResponse, HTInfo), offsetof(tDot11fIEHTInfo, + present), 0, "HTInfo", 0, 24, 56, SigIeHTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTINFO, 0, 0, }, + { offsetof(tDot11fProbeResponse, sec_chan_offset_ele), + offsetof(tDot11fIEsec_chan_offset_ele, present), 0, "sec_chan_offset_ele", + 0, 3, 3, SigIesec_chan_offset_ele, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SEC_CHAN_OFFSET_ELE, 0, 0, }, + { offsetof(tDot11fProbeResponse, WMMInfoAp), offsetof(tDot11fIEWMMInfoAp, + present), 0, "WMMInfoAp", 0, 9, 9, SigIeWMMInfoAp, {0, 80, 242, 2, 0}, + 5, DOT11F_EID_WMMINFOAP, 0, 0, }, + { offsetof(tDot11fProbeResponse, WMMParams), offsetof(tDot11fIEWMMParams, + present), 0, "WMMParams", 0, 26, 26, SigIeWMMParams, {0, 80, 242, 2, 1}, + 5, DOT11F_EID_WMMPARAMS, 0, 0, }, + { offsetof(tDot11fProbeResponse, WMMCaps), offsetof(tDot11fIEWMMCaps, + present), 0, "WMMCaps", 0, 9, 9, SigIeWMMCaps, {0, 80, 242, 2, 5}, + 5, DOT11F_EID_WMMCAPS, 0, 0, }, + { offsetof(tDot11fProbeResponse, WAPI), offsetof(tDot11fIEWAPI, present), + 0, "WAPI", 0, 14, 112, SigIeWAPI, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_WAPI, 0, 0, }, + { offsetof(tDot11fProbeResponse, ESERadMgmtCap), + offsetof(tDot11fIEESERadMgmtCap, present), 0, "ESERadMgmtCap", + 0, 8, 8, SigIeESERadMgmtCap, {0, 64, 150, 1, 0}, + 4, DOT11F_EID_ESERADMGMTCAP, 0, 0, }, + { offsetof(tDot11fProbeResponse, ESETrafStrmMet), + offsetof(tDot11fIEESETrafStrmMet, present), 0, "ESETrafStrmMet", + 0, 10, 10, SigIeESETrafStrmMet, {0, 64, 150, 7, 0}, + 4, DOT11F_EID_ESETRAFSTRMMET, 0, 0, }, + { offsetof(tDot11fProbeResponse, ESETxmitPower), + offsetof(tDot11fIEESETxmitPower, present), 0, "ESETxmitPower", + 0, 8, 8, SigIeESETxmitPower, {0, 64, 150, 0, 0}, + 4, DOT11F_EID_ESETXMITPOWER, 0, 0, }, + { offsetof(tDot11fProbeResponse, WscProbeRes), + offsetof(tDot11fIEWscProbeRes, present), 0, "WscProbeRes", + 0, 6, 319, SigIeWscProbeRes, {0, 80, 242, 4, 0}, + 4, DOT11F_EID_WSCPROBERES, 0, 0, }, + { offsetof(tDot11fProbeResponse, P2PProbeRes), + offsetof(tDot11fIEP2PProbeRes, present), 0, "P2PProbeRes", + 0, 6, 1141, SigIeP2PProbeRes, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PPROBERES, 0, 0, }, + { offsetof(tDot11fProbeResponse, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fProbeResponse, VHTOperation), + offsetof(tDot11fIEVHTOperation, present), 0, "VHTOperation", + 0, 7, 7, SigIeVHTOperation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTOPERATION, 0, 0, }, + { offsetof(tDot11fProbeResponse, VHTExtBssLoad), + offsetof(tDot11fIEVHTExtBssLoad, present), 0, "VHTExtBssLoad", + 0, 7, 7, SigIeVHTExtBssLoad, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTEXTBSSLOAD, 0, 0, }, + { offsetof(tDot11fProbeResponse, ExtCap), offsetof(tDot11fIEExtCap, + present), 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fProbeResponse, OBSSScanParameters), + offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters", + 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, 0, }, + { offsetof(tDot11fProbeResponse, fils_indication), + offsetof(tDot11fIEfils_indication, present), 0, "fils_indication", + 0, 6, 259, SigIefils_indication, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FILS_INDICATION, 0, 0, }, + { offsetof(tDot11fProbeResponse, Vendor1IE), offsetof(tDot11fIEVendor1IE, + present), 0, "Vendor1IE", 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, + 3, DOT11F_EID_VENDOR1IE, 0, 0, }, + { offsetof(tDot11fProbeResponse, vendor_vht_ie), + offsetof(tDot11fIEvendor_vht_ie, present), 0, "vendor_vht_ie", + 0, 7, 28, SigIevendor_vht_ie, {0, 144, 76, 4, 0}, + 4, DOT11F_EID_VENDOR_VHT_IE, 0, 0, }, + { offsetof(tDot11fProbeResponse, Vendor3IE), offsetof(tDot11fIEVendor3IE, + present), 0, "Vendor3IE", 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, + 3, DOT11F_EID_VENDOR3IE, 0, 0, }, + { offsetof(tDot11fProbeResponse, hs20vendor_ie), + offsetof(tDot11fIEhs20vendor_ie, present), 0, "hs20vendor_ie", + 0, 7, 9, SigIehs20vendor_ie, {80, 111, 154, 16, 0}, + 4, DOT11F_EID_HS20VENDOR_IE, 0, 0, }, + { offsetof(tDot11fProbeResponse, ChannelSwitchWrapper), + offsetof(tDot11fIEChannelSwitchWrapper, present), 0, + "ChannelSwitchWrapper", 0, 2, 14, SigIeChannelSwitchWrapper, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_CHANNELSWITCHWRAPPER, 0, 0, }, + { offsetof(tDot11fProbeResponse, QComVendorIE), + offsetof(tDot11fIEQComVendorIE, present), 0, "QComVendorIE", + 0, 7, 7, SigIeQComVendorIE, {0, 160, 198, 0, 0}, + 3, DOT11F_EID_QCOMVENDORIE, 0, 0, }, + { offsetof(tDot11fProbeResponse, ESEVersion), + offsetof(tDot11fIEESEVersion, present), 0, "ESEVersion", + 0, 7, 7, SigIeESEVersion, {0, 64, 150, 3, 0}, + 4, DOT11F_EID_ESEVERSION, 0, 0, }, + { offsetof(tDot11fProbeResponse, MBO_IE), offsetof(tDot11fIEMBO_IE, + present), 0, "MBO_IE", 0, 6, 295, SigIeMBO_IE, {80, 111, 154, 22, 0}, + 4, DOT11F_EID_MBO_IE, 0, 0, }, + { offsetof(tDot11fProbeResponse, qcn_ie), offsetof(tDot11fIEqcn_ie, + present), 0, "qcn_ie", 0, 6, 13, SigIeqcn_ie, {140, 253, 240, 1, 0}, + 4, DOT11F_EID_QCN_IE, 0, 0, }, + { offsetof(tDot11fProbeResponse, he_cap), offsetof(tDot11fIEhe_cap, + present), 0, "he_cap", 0, 23, 56, SigIehe_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_CAP, 35, 0, }, + { offsetof(tDot11fProbeResponse, he_op), offsetof(tDot11fIEhe_op, + present), 0, "he_op", 0, 8, 17, SigIehe_op, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_OP, 36, 0, }, + { offsetof(tDot11fProbeResponse, he_6ghz_band_cap), + offsetof(tDot11fIEhe_6ghz_band_cap, present), 0, "he_6ghz_band_cap", + 0, 4, 4, SigIehe_6ghz_band_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_6GHZ_BAND_CAP, 59, 0, }, + { offsetof(tDot11fProbeResponse, bss_color_change), + offsetof(tDot11fIEbss_color_change, present), 0, "bss_color_change", + 0, 4, 4, SigIebss_color_change, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BSS_COLOR_CHANGE, 42, 0, }, + { offsetof(tDot11fProbeResponse, mu_edca_param_set), + offsetof(tDot11fIEmu_edca_param_set, present), 0, "mu_edca_param_set", + 0, 15, 15, SigIemu_edca_param_set, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MU_EDCA_PARAM_SET, 38, 0, }, + { offsetof(tDot11fProbeResponse, esp_information), + offsetof(tDot11fIEesp_information, present), 0, "esp_information", + 0, 2, 98, SigIeesp_information, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ESP_INFORMATION, 11, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_probe_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fProbeResponse *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_ProbeResponse, IES_ProbeResponse, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_probe_response. */ + +static const tFFDefn FFS_QosMapConfigure[] = { + { "Category", offsetof(tDot11fQosMapConfigure, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fQosMapConfigure, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_QosMapConfigure[] = { + { offsetof(tDot11fQosMapConfigure, QosMapSet), + offsetof(tDot11fIEQosMapSet, present), 0, "QosMapSet", + 0, 18, 60, SigIeQosMapSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSMAPSET, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_qos_map_configure(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fQosMapConfigure *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_QosMapConfigure, IES_QosMapConfigure, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_qos_map_configure. */ + +static const tFFDefn FFS_RadioMeasurementReport[] = { + { "Category", offsetof(tDot11fRadioMeasurementReport, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fRadioMeasurementReport, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fRadioMeasurementReport, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_RadioMeasurementReport[] = { + { offsetof(tDot11fRadioMeasurementReport, MeasurementReport), + offsetof(tDot11fIEMeasurementReport, present), + offsetof(tDot11fRadioMeasurementReport, num_MeasurementReport), + "MeasurementReport", 4, 5, 31, SigIeMeasurementReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MEASUREMENTREPORT, 0, 1, }, + { offsetof(tDot11fRadioMeasurementReport, BeaconReportStatus), + offsetof(tDot11fIEBeaconReportStatus, present), 0, "BeaconReportStatus", + 0, 10, 10, SigIeBeaconReportStatus, {0, 0, 15, 34, 0}, + 4, DOT11F_EID_BEACONREPORTSTATUS, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_radio_measurement_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fRadioMeasurementReport *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_RadioMeasurementReport, IES_RadioMeasurementReport, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_radio_measurement_report. */ + +static const tFFDefn FFS_RadioMeasurementRequest[] = { + { "Category", offsetof(tDot11fRadioMeasurementRequest, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fRadioMeasurementRequest, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fRadioMeasurementRequest, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "NumOfRepetitions", offsetof(tDot11fRadioMeasurementRequest, + NumOfRepetitions), SigFfNumOfRepetitions, + DOT11F_FF_NUMOFREPETITIONS_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_RadioMeasurementRequest[] = { + { offsetof(tDot11fRadioMeasurementRequest, MeasurementRequest), + offsetof(tDot11fIEMeasurementRequest, present), + offsetof(tDot11fRadioMeasurementRequest, num_MeasurementRequest), + "MeasurementRequest", 5, 6, 18, SigIeMeasurementRequest, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MEASUREMENTREQUEST, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_radio_measurement_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fRadioMeasurementRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_RadioMeasurementRequest, IES_RadioMeasurementRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_radio_measurement_request. */ + +static const tFFDefn FFS_ReAssocRequest[] = { + { "Capabilities", offsetof(tDot11fReAssocRequest, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { "ListenInterval", offsetof(tDot11fReAssocRequest, ListenInterval), + SigFfListenInterval, DOT11F_FF_LISTENINTERVAL_LEN, }, + { "CurrentAPAddress", offsetof(tDot11fReAssocRequest, CurrentAPAddress), + SigFfCurrentAPAddress, DOT11F_FF_CURRENTAPADDRESS_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_ReAssocRequest[] = { + { offsetof(tDot11fReAssocRequest, SSID), offsetof(tDot11fIESSID, present), + 0, "SSID", 0, 2, 34, SigIeSSID, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SSID, 0, 1, }, + { offsetof(tDot11fReAssocRequest, SuppRates), + offsetof(tDot11fIESuppRates, present), 0, "SuppRates", + 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fReAssocRequest, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fReAssocRequest, PowerCaps), + offsetof(tDot11fIEPowerCaps, present), 0, "PowerCaps", + 0, 4, 4, SigIePowerCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_POWERCAPS, 0, 0, }, + { offsetof(tDot11fReAssocRequest, SuppChannels), + offsetof(tDot11fIESuppChannels, present), 0, "SuppChannels", + 0, 2, 98, SigIeSuppChannels, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPCHANNELS, 0, 0, }, + { offsetof(tDot11fReAssocRequest, RSNOpaque), + offsetof(tDot11fIERSNOpaque, present), 0, "RSNOpaque", + 0, 2, 255, SigIeRSNOpaque, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RSNOPAQUE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, QOSCapsStation), + offsetof(tDot11fIEQOSCapsStation, present), 0, "QOSCapsStation", + 0, 3, 3, SigIeQOSCapsStation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSCAPSSTATION, 0, 0, }, + { offsetof(tDot11fReAssocRequest, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fReAssocRequest, MobilityDomain), + offsetof(tDot11fIEMobilityDomain, present), 0, "MobilityDomain", + 0, 5, 5, SigIeMobilityDomain, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MOBILITYDOMAIN, 0, 0, }, + { offsetof(tDot11fReAssocRequest, FTInfo), offsetof(tDot11fIEFTInfo, + present), 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fReAssocRequest, RICDataDesc), + offsetof(tDot11fIERICDataDesc, present), + offsetof(tDot11fReAssocRequest, num_RICDataDesc), "RICDataDesc", + 2, 2, 550, SigIeRICDataDesc, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RICDATADESC, 0, 0, }, + { offsetof(tDot11fReAssocRequest, SuppOperatingClasses), + offsetof(tDot11fIESuppOperatingClasses, present), 0, + "SuppOperatingClasses", 0, 3, 34, SigIeSuppOperatingClasses, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_SUPPOPERATINGCLASSES, 0, 0, }, + { offsetof(tDot11fReAssocRequest, WPAOpaque), + offsetof(tDot11fIEWPAOpaque, present), 0, "WPAOpaque", + 0, 8, 255, SigIeWPAOpaque, {0, 80, 242, 1, 0}, + 4, DOT11F_EID_WPAOPAQUE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, HTCaps), offsetof(tDot11fIEHTCaps, + present), 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fReAssocRequest, WMMCaps), offsetof(tDot11fIEWMMCaps, + present), 0, "WMMCaps", 0, 9, 9, SigIeWMMCaps, {0, 80, 242, 2, 5}, + 5, DOT11F_EID_WMMCAPS, 0, 0, }, + { offsetof(tDot11fReAssocRequest, WMMInfoStation), + offsetof(tDot11fIEWMMInfoStation, present), 0, "WMMInfoStation", + 0, 9, 9, SigIeWMMInfoStation, {0, 80, 242, 2, 0}, + 5, DOT11F_EID_WMMINFOSTATION, 0, 0, }, + { offsetof(tDot11fReAssocRequest, WscIEOpaque), + offsetof(tDot11fIEWscIEOpaque, present), 0, "WscIEOpaque", + 0, 8, 255, SigIeWscIEOpaque, {0, 80, 242, 4, 0}, + 4, DOT11F_EID_WSCIEOPAQUE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, WAPIOpaque), + offsetof(tDot11fIEWAPIOpaque, present), 0, "WAPIOpaque", + 0, 8, 255, SigIeWAPIOpaque, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_WAPIOPAQUE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, WAPI), offsetof(tDot11fIEWAPI, present), + 0, "WAPI", 0, 14, 112, SigIeWAPI, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_WAPI, 0, 0, }, + { offsetof(tDot11fReAssocRequest, ESERadMgmtCap), + offsetof(tDot11fIEESERadMgmtCap, present), 0, "ESERadMgmtCap", + 0, 8, 8, SigIeESERadMgmtCap, {0, 64, 150, 1, 0}, + 4, DOT11F_EID_ESERADMGMTCAP, 0, 0, }, + { offsetof(tDot11fReAssocRequest, ESEVersion), + offsetof(tDot11fIEESEVersion, present), 0, "ESEVersion", + 0, 7, 7, SigIeESEVersion, {0, 64, 150, 3, 0}, + 4, DOT11F_EID_ESEVERSION, 0, 0, }, + { offsetof(tDot11fReAssocRequest, ESECckmOpaque), + offsetof(tDot11fIEESECckmOpaque, present), 0, "ESECckmOpaque", + 0, 12, 26, SigIeESECckmOpaque, {0, 64, 150, 0, 0}, + 4, DOT11F_EID_ESECCKMOPAQUE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, WMMTSPEC), offsetof(tDot11fIEWMMTSPEC, + present), offsetof(tDot11fReAssocRequest, num_WMMTSPEC), "WMMTSPEC", + 4, 63, 63, SigIeWMMTSPEC, {0, 80, 242, 2, 2}, + 5, DOT11F_EID_WMMTSPEC, 0, 0, }, + { offsetof(tDot11fReAssocRequest, ESETrafStrmRateSet), + offsetof(tDot11fIEESETrafStrmRateSet, present), 0, "ESETrafStrmRateSet", + 0, 7, 15, SigIeESETrafStrmRateSet, {0, 64, 150, 8, 0}, + 4, DOT11F_EID_ESETRAFSTRMRATESET, 0, 0, }, + { offsetof(tDot11fReAssocRequest, P2PIEOpaque), + offsetof(tDot11fIEP2PIEOpaque, present), 0, "P2PIEOpaque", + 0, 8, 255, SigIeP2PIEOpaque, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PIEOPAQUE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, WFDIEOpaque), + offsetof(tDot11fIEWFDIEOpaque, present), 0, "WFDIEOpaque", + 0, 8, 255, SigIeWFDIEOpaque, {80, 111, 154, 10, 0}, + 4, DOT11F_EID_WFDIEOPAQUE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fReAssocRequest, ExtCap), offsetof(tDot11fIEExtCap, + present), 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fReAssocRequest, OperatingMode), + offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode", + 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OPERATINGMODE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, QosMapSet), + offsetof(tDot11fIEQosMapSet, present), 0, "QosMapSet", + 0, 18, 60, SigIeQosMapSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSMAPSET, 0, 0, }, + { offsetof(tDot11fReAssocRequest, vendor_vht_ie), + offsetof(tDot11fIEvendor_vht_ie, present), 0, "vendor_vht_ie", + 0, 7, 28, SigIevendor_vht_ie, {0, 144, 76, 4, 0}, + 4, DOT11F_EID_VENDOR_VHT_IE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, hs20vendor_ie), + offsetof(tDot11fIEhs20vendor_ie, present), 0, "hs20vendor_ie", + 0, 7, 9, SigIehs20vendor_ie, {80, 111, 154, 16, 0}, + 4, DOT11F_EID_HS20VENDOR_IE, 0, 0, }, + { offsetof(tDot11fReAssocRequest, he_cap), offsetof(tDot11fIEhe_cap, + present), 0, "he_cap", 0, 23, 56, SigIehe_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_CAP, 35, 0, }, + { offsetof(tDot11fReAssocRequest, he_6ghz_band_cap), + offsetof(tDot11fIEhe_6ghz_band_cap, present), 0, "he_6ghz_band_cap", + 0, 4, 4, SigIehe_6ghz_band_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_6GHZ_BAND_CAP, 59, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_re_assoc_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fReAssocRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_ReAssocRequest, IES_ReAssocRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_re_assoc_request. */ + +static const tFFDefn FFS_ReAssocResponse[] = { + { "Capabilities", offsetof(tDot11fReAssocResponse, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { "Status", offsetof(tDot11fReAssocResponse, Status), SigFfStatus, + DOT11F_FF_STATUS_LEN, }, + { "AID", offsetof(tDot11fReAssocResponse, AID), SigFfAID, + DOT11F_FF_AID_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_ReAssocResponse[] = { + { offsetof(tDot11fReAssocResponse, SuppRates), + offsetof(tDot11fIESuppRates, present), 0, "SuppRates", + 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fReAssocResponse, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fReAssocResponse, EDCAParamSet), + offsetof(tDot11fIEEDCAParamSet, present), 0, "EDCAParamSet", + 0, 20, 20, SigIeEDCAParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EDCAPARAMSET, 0, 0, }, + { offsetof(tDot11fReAssocResponse, RCPIIE), offsetof(tDot11fIERCPIIE, + present), 0, "RCPIIE", 0, 3, 3, SigIeRCPIIE, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RCPIIE, 0, 0, }, + { offsetof(tDot11fReAssocResponse, RSNIIE), offsetof(tDot11fIERSNIIE, + present), 0, "RSNIIE", 0, 3, 3, SigIeRSNIIE, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RSNIIE, 0, 0, }, + { offsetof(tDot11fReAssocResponse, RRMEnabledCap), + offsetof(tDot11fIERRMEnabledCap, present), 0, "RRMEnabledCap", + 0, 7, 7, SigIeRRMEnabledCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RRMENABLEDCAP, 0, 0, }, + { offsetof(tDot11fReAssocResponse, RSNOpaque), + offsetof(tDot11fIERSNOpaque, present), 0, "RSNOpaque", + 0, 2, 255, SigIeRSNOpaque, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RSNOPAQUE, 0, 0, }, + { offsetof(tDot11fReAssocResponse, MobilityDomain), + offsetof(tDot11fIEMobilityDomain, present), 0, "MobilityDomain", + 0, 5, 5, SigIeMobilityDomain, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MOBILITYDOMAIN, 0, 0, }, + { offsetof(tDot11fReAssocResponse, FTInfo), offsetof(tDot11fIEFTInfo, + present), 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fReAssocResponse, RICDataDesc), + offsetof(tDot11fIERICDataDesc, present), + offsetof(tDot11fReAssocResponse, num_RICDataDesc), "RICDataDesc", + 2, 2, 550, SigIeRICDataDesc, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RICDATADESC, 0, 0, }, + { offsetof(tDot11fReAssocResponse, WPA), offsetof(tDot11fIEWPA, present), + 0, "WPA", 0, 8, 50, SigIeWPA, {0, 80, 242, 1, 0}, + 4, DOT11F_EID_WPA, 0, 0, }, + { offsetof(tDot11fReAssocResponse, TimeoutInterval), + offsetof(tDot11fIETimeoutInterval, present), 0, "TimeoutInterval", + 0, 7, 7, SigIeTimeoutInterval, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TIMEOUTINTERVAL, 0, 0, }, + { offsetof(tDot11fReAssocResponse, HTCaps), offsetof(tDot11fIEHTCaps, + present), 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fReAssocResponse, HTInfo), offsetof(tDot11fIEHTInfo, + present), 0, "HTInfo", 0, 24, 56, SigIeHTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTINFO, 0, 0, }, + { offsetof(tDot11fReAssocResponse, WMMParams), + offsetof(tDot11fIEWMMParams, present), 0, "WMMParams", + 0, 26, 26, SigIeWMMParams, {0, 80, 242, 2, 1}, + 5, DOT11F_EID_WMMPARAMS, 0, 0, }, + { offsetof(tDot11fReAssocResponse, ESERadMgmtCap), + offsetof(tDot11fIEESERadMgmtCap, present), 0, "ESERadMgmtCap", + 0, 8, 8, SigIeESERadMgmtCap, {0, 64, 150, 1, 0}, + 4, DOT11F_EID_ESERADMGMTCAP, 0, 0, }, + { offsetof(tDot11fReAssocResponse, ESETrafStrmMet), + offsetof(tDot11fIEESETrafStrmMet, present), 0, "ESETrafStrmMet", + 0, 10, 10, SigIeESETrafStrmMet, {0, 64, 150, 7, 0}, + 4, DOT11F_EID_ESETRAFSTRMMET, 0, 0, }, + { offsetof(tDot11fReAssocResponse, ESETxmitPower), + offsetof(tDot11fIEESETxmitPower, present), 0, "ESETxmitPower", + 0, 8, 8, SigIeESETxmitPower, {0, 64, 150, 0, 0}, + 4, DOT11F_EID_ESETXMITPOWER, 0, 0, }, + { offsetof(tDot11fReAssocResponse, WMMTSPEC), offsetof(tDot11fIEWMMTSPEC, + present), offsetof(tDot11fReAssocResponse, num_WMMTSPEC), "WMMTSPEC", + 4, 63, 63, SigIeWMMTSPEC, {0, 80, 242, 2, 2}, + 5, DOT11F_EID_WMMTSPEC, 0, 0, }, + { offsetof(tDot11fReAssocResponse, ESETrafStrmRateSet), + offsetof(tDot11fIEESETrafStrmRateSet, present), 0, "ESETrafStrmRateSet", + 0, 7, 15, SigIeESETrafStrmRateSet, {0, 64, 150, 8, 0}, + 4, DOT11F_EID_ESETRAFSTRMRATESET, 0, 0, }, + { offsetof(tDot11fReAssocResponse, WscReassocRes), + offsetof(tDot11fIEWscReassocRes, present), 0, "WscReassocRes", + 0, 6, 37, SigIeWscReassocRes, {0, 80, 242, 4, 0}, + 4, DOT11F_EID_WSCREASSOCRES, 0, 0, }, + { offsetof(tDot11fReAssocResponse, P2PAssocRes), + offsetof(tDot11fIEP2PAssocRes, present), 0, "P2PAssocRes", + 0, 6, 17, SigIeP2PAssocRes, {80, 111, 154, 9, 0}, + 4, DOT11F_EID_P2PASSOCRES, 0, 0, }, + { offsetof(tDot11fReAssocResponse, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fReAssocResponse, VHTOperation), + offsetof(tDot11fIEVHTOperation, present), 0, "VHTOperation", + 0, 7, 7, SigIeVHTOperation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTOPERATION, 0, 0, }, + { offsetof(tDot11fReAssocResponse, ExtCap), offsetof(tDot11fIEExtCap, + present), 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fReAssocResponse, OBSSScanParameters), + offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters", + 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, 0, }, + { offsetof(tDot11fReAssocResponse, QosMapSet), + offsetof(tDot11fIEQosMapSet, present), 0, "QosMapSet", + 0, 18, 60, SigIeQosMapSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSMAPSET, 0, 0, }, + { offsetof(tDot11fReAssocResponse, vendor_vht_ie), + offsetof(tDot11fIEvendor_vht_ie, present), 0, "vendor_vht_ie", + 0, 7, 28, SigIevendor_vht_ie, {0, 144, 76, 4, 0}, + 4, DOT11F_EID_VENDOR_VHT_IE, 0, 0, }, + { offsetof(tDot11fReAssocResponse, he_cap), offsetof(tDot11fIEhe_cap, + present), 0, "he_cap", 0, 23, 56, SigIehe_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_CAP, 35, 0, }, + { offsetof(tDot11fReAssocResponse, he_op), offsetof(tDot11fIEhe_op, + present), 0, "he_op", 0, 8, 17, SigIehe_op, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_OP, 36, 0, }, + { offsetof(tDot11fReAssocResponse, he_6ghz_band_cap), + offsetof(tDot11fIEhe_6ghz_band_cap, present), 0, "he_6ghz_band_cap", + 0, 4, 4, SigIehe_6ghz_band_cap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HE_6GHZ_BAND_CAP, 59, 0, }, + { offsetof(tDot11fReAssocResponse, bss_color_change), + offsetof(tDot11fIEbss_color_change, present), 0, "bss_color_change", + 0, 4, 4, SigIebss_color_change, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_BSS_COLOR_CHANGE, 42, 0, }, + { offsetof(tDot11fReAssocResponse, mu_edca_param_set), + offsetof(tDot11fIEmu_edca_param_set, present), 0, "mu_edca_param_set", + 0, 15, 15, SigIemu_edca_param_set, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_MU_EDCA_PARAM_SET, 38, 0, }, + { offsetof(tDot11fReAssocResponse, MBO_IE), offsetof(tDot11fIEMBO_IE, + present), 0, "MBO_IE", 0, 6, 295, SigIeMBO_IE, {80, 111, 154, 22, 0}, + 4, DOT11F_EID_MBO_IE, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_re_assoc_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fReAssocResponse *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_ReAssocResponse, IES_ReAssocResponse, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_re_assoc_response. */ + +static const tFFDefn FFS_SMPowerSave[] = { + { "Category", offsetof(tDot11fSMPowerSave, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fSMPowerSave, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "SMPowerModeSet", offsetof(tDot11fSMPowerSave, SMPowerModeSet), + SigFfSMPowerModeSet, DOT11F_FF_SMPOWERMODESET_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_SMPowerSave[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_sm_power_save(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fSMPowerSave *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_SMPowerSave, IES_SMPowerSave, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_sm_power_save. */ + +static const tFFDefn FFS_SaQueryReq[] = { + { "Category", offsetof(tDot11fSaQueryReq, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fSaQueryReq, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "TransactionId", offsetof(tDot11fSaQueryReq, TransactionId), + SigFfTransactionId, DOT11F_FF_TRANSACTIONID_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_SaQueryReq[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_sa_query_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fSaQueryReq *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_SaQueryReq, IES_SaQueryReq, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_sa_query_req. */ + +static const tFFDefn FFS_SaQueryRsp[] = { + { "Category", offsetof(tDot11fSaQueryRsp, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fSaQueryRsp, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "TransactionId", offsetof(tDot11fSaQueryRsp, TransactionId), + SigFfTransactionId, DOT11F_FF_TRANSACTIONID_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_SaQueryRsp[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_sa_query_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fSaQueryRsp *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_SaQueryRsp, IES_SaQueryRsp, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_sa_query_rsp. */ + +static const tFFDefn FFS_TDLSDisReq[] = { + { "Category", offsetof(tDot11fTDLSDisReq, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTDLSDisReq, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fTDLSDisReq, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TDLSDisReq[] = { + { offsetof(tDot11fTDLSDisReq, LinkIdentifier), + offsetof(tDot11fIELinkIdentifier, present), 0, "LinkIdentifier", + 0, 20, 20, SigIeLinkIdentifier, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LINKIDENTIFIER, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tdls_dis_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSDisReq *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TDLSDisReq, IES_TDLSDisReq, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tdls_dis_req. */ + +static const tFFDefn FFS_TDLSDisRsp[] = { + { "Category", offsetof(tDot11fTDLSDisRsp, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTDLSDisRsp, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fTDLSDisRsp, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "Capabilities", offsetof(tDot11fTDLSDisRsp, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TDLSDisRsp[] = { + { offsetof(tDot11fTDLSDisRsp, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fTDLSDisRsp, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, SuppChannels), + offsetof(tDot11fIESuppChannels, present), 0, "SuppChannels", + 0, 2, 98, SigIeSuppChannels, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPCHANNELS, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, SuppOperatingClasses), + offsetof(tDot11fIESuppOperatingClasses, present), 0, + "SuppOperatingClasses", 0, 3, 34, SigIeSuppOperatingClasses, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_SUPPOPERATINGCLASSES, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, RSN), offsetof(tDot11fIERSN, present), 0, + "RSN", 0, 4, 132, SigIeRSN, {0, 0, 0, 0, 0}, 0, DOT11F_EID_RSN, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, ExtCap), offsetof(tDot11fIEExtCap, present), + 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, FTInfo), offsetof(tDot11fIEFTInfo, present), + 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, TimeoutInterval), + offsetof(tDot11fIETimeoutInterval, present), 0, "TimeoutInterval", + 0, 7, 7, SigIeTimeoutInterval, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TIMEOUTINTERVAL, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, RICData), offsetof(tDot11fIERICData, + present), 0, "RICData", 0, 6, 6, SigIeRICData, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RICDATA, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, HTCaps), offsetof(tDot11fIEHTCaps, present), + 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, ht2040_bss_coexistence), + offsetof(tDot11fIEht2040_bss_coexistence, present), 0, + "ht2040_bss_coexistence", 0, 3, 3, SigIeht2040_bss_coexistence, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_HT2040_BSS_COEXISTENCE, 0, 0, }, + { offsetof(tDot11fTDLSDisRsp, LinkIdentifier), + offsetof(tDot11fIELinkIdentifier, present), 0, "LinkIdentifier", + 0, 20, 20, SigIeLinkIdentifier, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LINKIDENTIFIER, 0, 1, }, + { offsetof(tDot11fTDLSDisRsp, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tdls_dis_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSDisRsp *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TDLSDisRsp, IES_TDLSDisRsp, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tdls_dis_rsp. */ + +static const tFFDefn FFS_TDLSPeerTrafficInd[] = { + { "Category", offsetof(tDot11fTDLSPeerTrafficInd, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTDLSPeerTrafficInd, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fTDLSPeerTrafficInd, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TDLSPeerTrafficInd[] = { + { offsetof(tDot11fTDLSPeerTrafficInd, LinkIdentifier), + offsetof(tDot11fIELinkIdentifier, present), 0, "LinkIdentifier", + 0, 20, 20, SigIeLinkIdentifier, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LINKIDENTIFIER, 0, 1, }, + { offsetof(tDot11fTDLSPeerTrafficInd, PTIControl), + offsetof(tDot11fIEPTIControl, present), 0, "PTIControl", + 0, 5, 5, SigIePTIControl, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_PTICONTROL, 0, 0, }, + { offsetof(tDot11fTDLSPeerTrafficInd, PUBufferStatus), + offsetof(tDot11fIEPUBufferStatus, present), 0, "PUBufferStatus", + 0, 3, 3, SigIePUBufferStatus, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_PUBUFFERSTATUS, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tdls_peer_traffic_ind(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSPeerTrafficInd *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TDLSPeerTrafficInd, IES_TDLSPeerTrafficInd, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tdls_peer_traffic_ind. */ + +static const tFFDefn FFS_TDLSPeerTrafficRsp[] = { + { "Category", offsetof(tDot11fTDLSPeerTrafficRsp, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTDLSPeerTrafficRsp, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fTDLSPeerTrafficRsp, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TDLSPeerTrafficRsp[] = { + { offsetof(tDot11fTDLSPeerTrafficRsp, LinkIdentifier), + offsetof(tDot11fIELinkIdentifier, present), 0, "LinkIdentifier", + 0, 20, 20, SigIeLinkIdentifier, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LINKIDENTIFIER, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tdls_peer_traffic_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSPeerTrafficRsp *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TDLSPeerTrafficRsp, IES_TDLSPeerTrafficRsp, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tdls_peer_traffic_rsp. */ + +static const tFFDefn FFS_TDLSSetupCnf[] = { + { "Category", offsetof(tDot11fTDLSSetupCnf, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTDLSSetupCnf, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "Status", offsetof(tDot11fTDLSSetupCnf, Status), SigFfStatus, + DOT11F_FF_STATUS_LEN, }, + { "DialogToken", offsetof(tDot11fTDLSSetupCnf, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TDLSSetupCnf[] = { + { offsetof(tDot11fTDLSSetupCnf, RSN), offsetof(tDot11fIERSN, present), 0, + "RSN", 0, 4, 132, SigIeRSN, {0, 0, 0, 0, 0}, 0, DOT11F_EID_RSN, 0, 0, }, + { offsetof(tDot11fTDLSSetupCnf, EDCAParamSet), + offsetof(tDot11fIEEDCAParamSet, present), 0, "EDCAParamSet", + 0, 20, 20, SigIeEDCAParamSet, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EDCAPARAMSET, 0, 0, }, + { offsetof(tDot11fTDLSSetupCnf, FTInfo), offsetof(tDot11fIEFTInfo, + present), 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fTDLSSetupCnf, TimeoutInterval), + offsetof(tDot11fIETimeoutInterval, present), 0, "TimeoutInterval", + 0, 7, 7, SigIeTimeoutInterval, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TIMEOUTINTERVAL, 0, 0, }, + { offsetof(tDot11fTDLSSetupCnf, HTInfo), offsetof(tDot11fIEHTInfo, + present), 0, "HTInfo", 0, 24, 56, SigIeHTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTINFO, 0, 0, }, + { offsetof(tDot11fTDLSSetupCnf, LinkIdentifier), + offsetof(tDot11fIELinkIdentifier, present), 0, "LinkIdentifier", + 0, 20, 20, SigIeLinkIdentifier, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LINKIDENTIFIER, 0, 0, }, + { offsetof(tDot11fTDLSSetupCnf, WMMParams), offsetof(tDot11fIEWMMParams, + present), 0, "WMMParams", 0, 26, 26, SigIeWMMParams, {0, 80, 242, 2, 1}, + 5, DOT11F_EID_WMMPARAMS, 0, 0, }, + { offsetof(tDot11fTDLSSetupCnf, VHTOperation), + offsetof(tDot11fIEVHTOperation, present), 0, "VHTOperation", + 0, 7, 7, SigIeVHTOperation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTOPERATION, 0, 0, }, + { offsetof(tDot11fTDLSSetupCnf, OperatingMode), + offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode", + 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OPERATINGMODE, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tdls_setup_cnf(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSSetupCnf *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TDLSSetupCnf, IES_TDLSSetupCnf, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tdls_setup_cnf. */ + +static const tFFDefn FFS_TDLSSetupReq[] = { + { "Category", offsetof(tDot11fTDLSSetupReq, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTDLSSetupReq, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fTDLSSetupReq, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "Capabilities", offsetof(tDot11fTDLSSetupReq, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TDLSSetupReq[] = { + { offsetof(tDot11fTDLSSetupReq, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 1, }, + { offsetof(tDot11fTDLSSetupReq, Country), offsetof(tDot11fIECountry, + present), 0, "Country", 0, 5, 257, SigIeCountry, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_COUNTRY, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, SuppChannels), + offsetof(tDot11fIESuppChannels, present), 0, "SuppChannels", + 0, 2, 98, SigIeSuppChannels, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPCHANNELS, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, RSN), offsetof(tDot11fIERSN, present), 0, + "RSN", 0, 4, 132, SigIeRSN, {0, 0, 0, 0, 0}, 0, DOT11F_EID_RSN, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, ExtCap), offsetof(tDot11fIEExtCap, + present), 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, SuppOperatingClasses), + offsetof(tDot11fIESuppOperatingClasses, present), 0, + "SuppOperatingClasses", 0, 3, 34, SigIeSuppOperatingClasses, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_SUPPOPERATINGCLASSES, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, QOSCapsStation), + offsetof(tDot11fIEQOSCapsStation, present), 0, "QOSCapsStation", + 0, 3, 3, SigIeQOSCapsStation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSCAPSSTATION, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, FTInfo), offsetof(tDot11fIEFTInfo, + present), 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, TimeoutInterval), + offsetof(tDot11fIETimeoutInterval, present), 0, "TimeoutInterval", + 0, 7, 7, SigIeTimeoutInterval, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TIMEOUTINTERVAL, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, RICData), offsetof(tDot11fIERICData, + present), 0, "RICData", 0, 6, 6, SigIeRICData, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RICDATA, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, HTCaps), offsetof(tDot11fIEHTCaps, + present), 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, ht2040_bss_coexistence), + offsetof(tDot11fIEht2040_bss_coexistence, present), 0, + "ht2040_bss_coexistence", 0, 3, 3, SigIeht2040_bss_coexistence, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_HT2040_BSS_COEXISTENCE, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, LinkIdentifier), + offsetof(tDot11fIELinkIdentifier, present), 0, "LinkIdentifier", + 0, 20, 20, SigIeLinkIdentifier, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LINKIDENTIFIER, 0, 1, }, + { offsetof(tDot11fTDLSSetupReq, WMMInfoStation), + offsetof(tDot11fIEWMMInfoStation, present), 0, "WMMInfoStation", + 0, 9, 9, SigIeWMMInfoStation, {0, 80, 242, 2, 0}, + 5, DOT11F_EID_WMMINFOSTATION, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, AID), offsetof(tDot11fIEAID, present), 0, + "AID", 0, 4, 4, SigIeAID, {0, 0, 0, 0, 0}, 0, DOT11F_EID_AID, 0, 0, }, + { offsetof(tDot11fTDLSSetupReq, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tdls_setup_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSSetupReq *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TDLSSetupReq, IES_TDLSSetupReq, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tdls_setup_req. */ + +static const tFFDefn FFS_TDLSSetupRsp[] = { + { "Category", offsetof(tDot11fTDLSSetupRsp, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTDLSSetupRsp, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "Status", offsetof(tDot11fTDLSSetupRsp, Status), SigFfStatus, + DOT11F_FF_STATUS_LEN, }, + { "DialogToken", offsetof(tDot11fTDLSSetupRsp, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "Capabilities", offsetof(tDot11fTDLSSetupRsp, Capabilities), + SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TDLSSetupRsp[] = { + { offsetof(tDot11fTDLSSetupRsp, SuppRates), offsetof(tDot11fIESuppRates, + present), 0, "SuppRates", 0, 2, 14, SigIeSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPRATES, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, Country), offsetof(tDot11fIECountry, + present), 0, "Country", 0, 5, 257, SigIeCountry, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_COUNTRY, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, ExtSuppRates), + offsetof(tDot11fIEExtSuppRates, present), 0, "ExtSuppRates", + 0, 3, 14, SigIeExtSuppRates, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTSUPPRATES, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, SuppChannels), + offsetof(tDot11fIESuppChannels, present), 0, "SuppChannels", + 0, 2, 98, SigIeSuppChannels, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_SUPPCHANNELS, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, RSN), offsetof(tDot11fIERSN, present), 0, + "RSN", 0, 4, 132, SigIeRSN, {0, 0, 0, 0, 0}, 0, DOT11F_EID_RSN, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, ExtCap), offsetof(tDot11fIEExtCap, + present), 0, "ExtCap", 0, 3, 17, SigIeExtCap, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, SuppOperatingClasses), + offsetof(tDot11fIESuppOperatingClasses, present), 0, + "SuppOperatingClasses", 0, 3, 34, SigIeSuppOperatingClasses, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_SUPPOPERATINGCLASSES, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, QOSCapsStation), + offsetof(tDot11fIEQOSCapsStation, present), 0, "QOSCapsStation", + 0, 3, 3, SigIeQOSCapsStation, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_QOSCAPSSTATION, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, FTInfo), offsetof(tDot11fIEFTInfo, + present), 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, TimeoutInterval), + offsetof(tDot11fIETimeoutInterval, present), 0, "TimeoutInterval", + 0, 7, 7, SigIeTimeoutInterval, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TIMEOUTINTERVAL, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, RICData), offsetof(tDot11fIERICData, + present), 0, "RICData", 0, 6, 6, SigIeRICData, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_RICDATA, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, HTCaps), offsetof(tDot11fIEHTCaps, + present), 0, "HTCaps", 0, 28, 60, SigIeHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, ht2040_bss_coexistence), + offsetof(tDot11fIEht2040_bss_coexistence, present), 0, + "ht2040_bss_coexistence", 0, 3, 3, SigIeht2040_bss_coexistence, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_HT2040_BSS_COEXISTENCE, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, LinkIdentifier), + offsetof(tDot11fIELinkIdentifier, present), 0, "LinkIdentifier", + 0, 20, 20, SigIeLinkIdentifier, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LINKIDENTIFIER, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, WMMInfoStation), + offsetof(tDot11fIEWMMInfoStation, present), 0, "WMMInfoStation", + 0, 9, 9, SigIeWMMInfoStation, {0, 80, 242, 2, 0}, + 5, DOT11F_EID_WMMINFOSTATION, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, AID), offsetof(tDot11fIEAID, present), 0, + "AID", 0, 4, 4, SigIeAID, {0, 0, 0, 0, 0}, 0, DOT11F_EID_AID, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, VHTCaps), offsetof(tDot11fIEVHTCaps, + present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fTDLSSetupRsp, OperatingMode), + offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode", + 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OPERATINGMODE, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tdls_setup_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSSetupRsp *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TDLSSetupRsp, IES_TDLSSetupRsp, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tdls_setup_rsp. */ + +static const tFFDefn FFS_TDLSTeardown[] = { + { "Category", offsetof(tDot11fTDLSTeardown, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTDLSTeardown, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "Reason", offsetof(tDot11fTDLSTeardown, Reason), SigFfReason, + DOT11F_FF_REASON_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TDLSTeardown[] = { + { offsetof(tDot11fTDLSTeardown, FTInfo), offsetof(tDot11fIEFTInfo, + present), 0, "FTInfo", 0, 84, 222, SigIeFTInfo, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_FTINFO, 0, 0, }, + { offsetof(tDot11fTDLSTeardown, LinkIdentifier), + offsetof(tDot11fIELinkIdentifier, present), 0, "LinkIdentifier", + 0, 20, 20, SigIeLinkIdentifier, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_LINKIDENTIFIER, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tdls_teardown(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTDLSTeardown *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TDLSTeardown, IES_TDLSTeardown, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tdls_teardown. */ + +static const tFFDefn FFS_TPCReport[] = { + { "Category", offsetof(tDot11fTPCReport, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTPCReport, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fTPCReport, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TPCReport[] = { + { offsetof(tDot11fTPCReport, TPCReport), offsetof(tDot11fIETPCReport, + present), 0, "TPCReport", 0, 4, 4, SigIeTPCReport, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TPCREPORT, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tpc_report(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTPCReport *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TPCReport, IES_TPCReport, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tpc_report. */ + +static const tFFDefn FFS_TPCRequest[] = { + { "Category", offsetof(tDot11fTPCRequest, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fTPCRequest, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fTPCRequest, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TPCRequest[] = { + { offsetof(tDot11fTPCRequest, TPCRequest), offsetof(tDot11fIETPCRequest, + present), 0, "TPCRequest", 0, 2, 2, SigIeTPCRequest, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TPCREQUEST, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_tpc_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTPCRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TPCRequest, IES_TPCRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_tpc_request. */ + +static const tFFDefn FFS_TimingAdvertisementFrame[] = { + { "TimeStamp", offsetof(tDot11fTimingAdvertisementFrame, TimeStamp), + SigFfTimeStamp, DOT11F_FF_TIMESTAMP_LEN, }, + { "Capabilities", offsetof(tDot11fTimingAdvertisementFrame, + Capabilities), SigFfCapabilities, DOT11F_FF_CAPABILITIES_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_TimingAdvertisementFrame[] = { + { offsetof(tDot11fTimingAdvertisementFrame, Country), + offsetof(tDot11fIECountry, present), 0, "Country", 0, 5, 257, SigIeCountry, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_COUNTRY, 0, 0, }, + { offsetof(tDot11fTimingAdvertisementFrame, PowerConstraints), + offsetof(tDot11fIEPowerConstraints, present), 0, "PowerConstraints", + 0, 3, 3, SigIePowerConstraints, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_POWERCONSTRAINTS, 0, 0, }, + { offsetof(tDot11fTimingAdvertisementFrame, TimeAdvertisement), + offsetof(tDot11fIETimeAdvertisement, present), 0, "TimeAdvertisement", + 0, 18, 18, SigIeTimeAdvertisement, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_TIMEADVERTISEMENT, 0, 0, }, + { offsetof(tDot11fTimingAdvertisementFrame, ExtCap), + offsetof(tDot11fIEExtCap, present), 0, "ExtCap", 0, 3, 17, SigIeExtCap, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_EXTCAP, 0, 0, }, + { offsetof(tDot11fTimingAdvertisementFrame, Vendor1IE), + offsetof(tDot11fIEVendor1IE, present), 0, "Vendor1IE", + 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, + 3, DOT11F_EID_VENDOR1IE, 0, 0, }, + { offsetof(tDot11fTimingAdvertisementFrame, Vendor3IE), + offsetof(tDot11fIEVendor3IE, present), 0, "Vendor3IE", + 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, + 3, DOT11F_EID_VENDOR3IE, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_timing_advertisement_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fTimingAdvertisementFrame *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_TimingAdvertisementFrame, IES_TimingAdvertisementFrame, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_timing_advertisement_frame. */ + +static const tFFDefn FFS_VHTGidManagementActionFrame[] = { + { "Category", offsetof(tDot11fVHTGidManagementActionFrame, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fVHTGidManagementActionFrame, Action), + SigFfAction, DOT11F_FF_ACTION_LEN, }, + { "VhtMembershipStatusArray", + offsetof(tDot11fVHTGidManagementActionFrame, VhtMembershipStatusArray), + SigFfVhtMembershipStatusArray, + DOT11F_FF_VHTMEMBERSHIPSTATUSARRAY_LEN, }, + { "VhtUserPositionArray", offsetof(tDot11fVHTGidManagementActionFrame, + VhtUserPositionArray), SigFfVhtUserPositionArray, + DOT11F_FF_VHTUSERPOSITIONARRAY_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_VHTGidManagementActionFrame[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_vht_gid_management_action_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fVHTGidManagementActionFrame *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_VHTGidManagementActionFrame, IES_VHTGidManagementActionFrame, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_vht_gid_management_action_frame. */ + +static const tFFDefn FFS_WMMAddTSRequest[] = { + { "Category", offsetof(tDot11fWMMAddTSRequest, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fWMMAddTSRequest, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fWMMAddTSRequest, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "StatusCode", offsetof(tDot11fWMMAddTSRequest, StatusCode), + SigFfStatusCode, DOT11F_FF_STATUSCODE_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_WMMAddTSRequest[] = { + { offsetof(tDot11fWMMAddTSRequest, WMMTSPEC), offsetof(tDot11fIEWMMTSPEC, + present), 0, "WMMTSPEC", 0, 63, 63, SigIeWMMTSPEC, {0, 80, 242, 2, 2}, + 5, DOT11F_EID_WMMTSPEC, 0, 1, }, + { offsetof(tDot11fWMMAddTSRequest, ESETrafStrmRateSet), + offsetof(tDot11fIEESETrafStrmRateSet, present), 0, "ESETrafStrmRateSet", + 0, 7, 15, SigIeESETrafStrmRateSet, {0, 64, 150, 8, 0}, + 4, DOT11F_EID_ESETRAFSTRMRATESET, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_wmm_add_ts_request(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fWMMAddTSRequest *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_WMMAddTSRequest, IES_WMMAddTSRequest, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_wmm_add_ts_request. */ + +static const tFFDefn FFS_WMMAddTSResponse[] = { + { "Category", offsetof(tDot11fWMMAddTSResponse, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fWMMAddTSResponse, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fWMMAddTSResponse, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "StatusCode", offsetof(tDot11fWMMAddTSResponse, StatusCode), + SigFfStatusCode, DOT11F_FF_STATUSCODE_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_WMMAddTSResponse[] = { + { offsetof(tDot11fWMMAddTSResponse, WMMTSPEC), + offsetof(tDot11fIEWMMTSPEC, present), 0, "WMMTSPEC", + 0, 63, 63, SigIeWMMTSPEC, {0, 80, 242, 2, 2}, + 5, DOT11F_EID_WMMTSPEC, 0, 0, }, + { offsetof(tDot11fWMMAddTSResponse, ESETrafStrmMet), + offsetof(tDot11fIEESETrafStrmMet, present), 0, "ESETrafStrmMet", + 0, 10, 10, SigIeESETrafStrmMet, {0, 64, 150, 7, 0}, + 4, DOT11F_EID_ESETRAFSTRMMET, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_wmm_add_ts_response(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fWMMAddTSResponse *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_WMMAddTSResponse, IES_WMMAddTSResponse, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_wmm_add_ts_response. */ + +static const tFFDefn FFS_WMMDelTS[] = { + { "Category", offsetof(tDot11fWMMDelTS, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fWMMDelTS, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11fWMMDelTS, DialogToken), SigFfDialogToken, + DOT11F_FF_DIALOGTOKEN_LEN, }, + { "StatusCode", offsetof(tDot11fWMMDelTS, StatusCode), SigFfStatusCode, + DOT11F_FF_STATUSCODE_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_WMMDelTS[] = { + { offsetof(tDot11fWMMDelTS, WMMTSPEC), offsetof(tDot11fIEWMMTSPEC, + present), 0, "WMMTSPEC", 0, 63, 63, SigIeWMMTSPEC, {0, 80, 242, 2, 2}, + 5, DOT11F_EID_WMMTSPEC, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_wmm_del_ts(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fWMMDelTS *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_WMMDelTS, IES_WMMDelTS, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_wmm_del_ts. */ + +static const tFFDefn FFS_addba_req[] = { + { "Category", offsetof(tDot11faddba_req, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11faddba_req, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11faddba_req, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "addba_param_set", offsetof(tDot11faddba_req, addba_param_set), + SigFfaddba_param_set, DOT11F_FF_ADDBA_PARAM_SET_LEN, }, + { "ba_timeout", offsetof(tDot11faddba_req, ba_timeout), SigFfba_timeout, + DOT11F_FF_BA_TIMEOUT_LEN, }, + { "ba_start_seq_ctrl", offsetof(tDot11faddba_req, ba_start_seq_ctrl), + SigFfba_start_seq_ctrl, DOT11F_FF_BA_START_SEQ_CTRL_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_addba_req[] = { + { offsetof(tDot11faddba_req, addba_extn_element), + offsetof(tDot11fIEaddba_extn_element, present), 0, "addba_extn_element", + 0, 3, 3, SigIeaddba_extn_element, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ADDBA_EXTN_ELEMENT, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_addba_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11faddba_req *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_addba_req, IES_addba_req, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_addba_req. */ + +static const tFFDefn FFS_addba_rsp[] = { + { "Category", offsetof(tDot11faddba_rsp, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11faddba_rsp, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "DialogToken", offsetof(tDot11faddba_rsp, DialogToken), + SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { "Status", offsetof(tDot11faddba_rsp, Status), SigFfStatus, + DOT11F_FF_STATUS_LEN, }, + { "addba_param_set", offsetof(tDot11faddba_rsp, addba_param_set), + SigFfaddba_param_set, DOT11F_FF_ADDBA_PARAM_SET_LEN, }, + { "ba_timeout", offsetof(tDot11faddba_rsp, ba_timeout), SigFfba_timeout, + DOT11F_FF_BA_TIMEOUT_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_addba_rsp[] = { + { offsetof(tDot11faddba_rsp, addba_extn_element), + offsetof(tDot11fIEaddba_extn_element, present), 0, "addba_extn_element", + 0, 3, 3, SigIeaddba_extn_element, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_ADDBA_EXTN_ELEMENT, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_addba_rsp(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11faddba_rsp *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_addba_rsp, IES_addba_rsp, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_addba_rsp. */ + +static const tFFDefn FFS_delba_req[] = { + { "Category", offsetof(tDot11fdelba_req, Category), SigFfCategory, + DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fdelba_req, Action), SigFfAction, + DOT11F_FF_ACTION_LEN, }, + { "delba_param_set", offsetof(tDot11fdelba_req, delba_param_set), + SigFfdelba_param_set, DOT11F_FF_DELBA_PARAM_SET_LEN, }, + { "Reason", offsetof(tDot11fdelba_req, Reason), SigFfReason, + DOT11F_FF_REASON_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_delba_req[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_delba_req(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fdelba_req *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_delba_req, IES_delba_req, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_delba_req. */ + +static const tFFDefn FFS_ext_channel_switch_action_frame[] = { + { "Category", offsetof(tDot11fext_channel_switch_action_frame, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fext_channel_switch_action_frame, Action), + SigFfAction, DOT11F_FF_ACTION_LEN, }, + { "ext_chan_switch_ann_action", + offsetof(tDot11fext_channel_switch_action_frame, + ext_chan_switch_ann_action), SigFfext_chan_switch_ann_action, + DOT11F_FF_EXT_CHAN_SWITCH_ANN_ACTION_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_ext_channel_switch_action_frame[] = { + { offsetof(tDot11fext_channel_switch_action_frame, + WiderBWChanSwitchAnn), offsetof(tDot11fIEWiderBWChanSwitchAnn, present), + 0, "WiderBWChanSwitchAnn", 0, 5, 5, SigIeWiderBWChanSwitchAnn, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_WIDERBWCHANSWITCHANN, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_ext_channel_switch_action_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fext_channel_switch_action_frame *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_ext_channel_switch_action_frame, IES_ext_channel_switch_action_frame, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_ext_channel_switch_action_frame. */ + +static const tFFDefn FFS_ht2040_bss_coexistence_mgmt_action_frame[] = { + { "Category", offsetof(tDot11fht2040_bss_coexistence_mgmt_action_frame, + Category), SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "Action", offsetof(tDot11fht2040_bss_coexistence_mgmt_action_frame, + Action), SigFfAction, DOT11F_FF_ACTION_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_ht2040_bss_coexistence_mgmt_action_frame[] = { + { offsetof(tDot11fht2040_bss_coexistence_mgmt_action_frame, + ht2040_bss_coexistence), offsetof(tDot11fIEht2040_bss_coexistence, + present), 0, "ht2040_bss_coexistence", + 0, 3, 3, SigIeht2040_bss_coexistence, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HT2040_BSS_COEXISTENCE, 0, 1, }, + { offsetof(tDot11fht2040_bss_coexistence_mgmt_action_frame, + ht2040_bss_intolerant_report), + offsetof(tDot11fIEht2040_bss_intolerant_report, present), 0, + "ht2040_bss_intolerant_report", + 0, 3, 53, SigIeht2040_bss_intolerant_report, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_HT2040_BSS_INTOLERANT_REPORT, 0, 1, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_ht2040_bss_coexistence_mgmt_action_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fht2040_bss_coexistence_mgmt_action_frame *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_ht2040_bss_coexistence_mgmt_action_frame, IES_ht2040_bss_coexistence_mgmt_action_frame, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_ht2040_bss_coexistence_mgmt_action_frame. */ + +static const tFFDefn FFS_p2p_oper_chan_change_confirm[] = { + { "Category", offsetof(tDot11fp2p_oper_chan_change_confirm, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "p2p_action_oui", offsetof(tDot11fp2p_oper_chan_change_confirm, + p2p_action_oui), SigFfp2p_action_oui, DOT11F_FF_P2P_ACTION_OUI_LEN, }, + { "p2p_action_subtype", offsetof(tDot11fp2p_oper_chan_change_confirm, + p2p_action_subtype), SigFfp2p_action_subtype, + DOT11F_FF_P2P_ACTION_SUBTYPE_LEN, }, + { "DialogToken", offsetof(tDot11fp2p_oper_chan_change_confirm, + DialogToken), SigFfDialogToken, DOT11F_FF_DIALOGTOKEN_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_p2p_oper_chan_change_confirm[] = { + { offsetof(tDot11fp2p_oper_chan_change_confirm, HTCaps), + offsetof(tDot11fIEHTCaps, present), 0, "HTCaps", 0, 28, 60, SigIeHTCaps, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_HTCAPS, 0, 0, }, + { offsetof(tDot11fp2p_oper_chan_change_confirm, VHTCaps), + offsetof(tDot11fIEVHTCaps, present), 0, "VHTCaps", 0, 14, 14, SigIeVHTCaps, + {0, 0, 0, 0, 0}, 0, DOT11F_EID_VHTCAPS, 0, 0, }, + { offsetof(tDot11fp2p_oper_chan_change_confirm, OperatingMode), + offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode", + 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, + 0, DOT11F_EID_OPERATINGMODE, 0, 0, }, + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_p2p_oper_chan_change_confirm(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fp2p_oper_chan_change_confirm *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_p2p_oper_chan_change_confirm, IES_p2p_oper_chan_change_confirm, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_p2p_oper_chan_change_confirm. */ + +static const tFFDefn FFS_vendor_action_frame[] = { + { "Category", offsetof(tDot11fvendor_action_frame, Category), + SigFfCategory, DOT11F_FF_CATEGORY_LEN, }, + { "vendor_oui", offsetof(tDot11fvendor_action_frame, vendor_oui), + SigFfvendor_oui, DOT11F_FF_VENDOR_OUI_LEN, }, + { "vendor_action_subtype", offsetof(tDot11fvendor_action_frame, + vendor_action_subtype), SigFfvendor_action_subtype, + DOT11F_FF_VENDOR_ACTION_SUBTYPE_LEN, }, + { NULL, 0, 0, 0,}, +}; + +static const tIEDefn IES_vendor_action_frame[] = { + {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },}; + +uint32_t dot11f_unpack_vendor_action_frame(tpAniSirGlobal pCtx, + uint8_t *pBuf, uint32_t nBuf, + tDot11fvendor_action_frame *pFrm, bool append_ie) +{ + uint32_t i = 0; + uint32_t status = 0; + status = unpack_core(pCtx, pBuf, nBuf, + FFS_vendor_action_frame, IES_vendor_action_frame, + (uint8_t *)pFrm, sizeof(*pFrm), append_ie); + + (void)i; + return status; + +} /* End dot11f_unpack_vendor_action_frame. */ + +/** + * Note: If @append_ie is set TRUE, pFrm will not be reset to zero, + * but parsed IE's would be populated to pFrm with already + * populated IE's in pFrm + */ +static uint32_t unpack_core(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint32_t nBuf, + const tFFDefn FFs[], + const tIEDefn IEs[], + uint8_t *pFrm, + size_t nFrm, + bool append_ie) +{ + const tFFDefn *pFf; + const tIEDefn *pIe; + uint8_t *pBufRemaining; + uint32_t nBufRemaining, status; + uint8_t eid, len, extn_eid; + tFRAMES_BOOL *pfFound; + uint32_t countOffset = 0; + + DOT11F_PARAMETER_CHECK(pBuf, nBuf, pFrm, nFrm); + (void)nFrm; + + (void)pCtx; + status = DOT11F_PARSE_SUCCESS; + pBufRemaining = pBuf; + nBufRemaining = nBuf; + + pIe = &IEs[0]; + while (!append_ie && (0xff != pIe->eid || pIe->extn_eid)) { + pfFound = (tFRAMES_BOOL *)(pFrm + pIe->offset + + pIe->presenceOffset); + *pfFound = 0U; + if (pIe->countOffset) + *(uint16_t *)(pFrm + pIe->countOffset) = 0U; + ++pIe; + } + + pFf = &FFs[0]; + while (!append_ie && pFf->size) { + if (pFf->size > nBufRemaining) { + FRAMES_LOG3(pCtx, FRLOGE, FRFL("Fixed field %s is %d" + "bytes in size, but there are only %d bytes left i" + "n this frame.\n"), pFf->name, pFf->size, + nBufRemaining); + FRAMES_DBG_BREAK(); + return DOT11F_MISSING_FIXED_FIELD; + } + + switch (pFf->sig) { + + case SigFfAID: + dot11f_unpack_ff_common_func(pCtx, + pBufRemaining, (uint16_t *)&(((tDot11fFfAID *) + (pFrm + pFf->offset))->associd)); + break; + case SigFfAction: + dot11f_unpack_ff_action(pCtx, + pBufRemaining, (tDot11fFfAction *) + (pFrm + pFf->offset)); + break; + case SigFfAuthAlgo: + dot11f_unpack_ff_common_func(pCtx, + pBufRemaining, (uint16_t *)&(((tDot11fFfAuthAlgo *) + (pFrm + pFf->offset))->algo)); + break; + case SigFfAuthSeqNo: + dot11f_unpack_ff_common_func(pCtx, + pBufRemaining, (uint16_t *)&(((tDot11fFfAuthSeqNo *) + (pFrm + pFf->offset))->no)); + break; + case SigFfBeaconInterval: + dot11f_unpack_ff_common_func(pCtx, + pBufRemaining, (uint16_t *)&(((tDot11fFfBeaconInterval *) + (pFrm + pFf->offset))->interval)); + break; + case SigFfCapabilities: + dot11f_unpack_ff_capabilities(pCtx, + pBufRemaining, (tDot11fFfCapabilities *) + (pFrm + pFf->offset)); + break; + case SigFfCategory: + dot11f_unpack_ff_category(pCtx, + pBufRemaining, (tDot11fFfCategory *) + (pFrm + pFf->offset)); + break; + case SigFfCurrentAPAddress: + dot11f_unpack_ff_current_ap_address(pCtx, + pBufRemaining, (tDot11fFfCurrentAPAddress *) + (pFrm + pFf->offset)); + break; + case SigFfDialogToken: + dot11f_unpack_ff_dialog_token(pCtx, + pBufRemaining, (tDot11fFfDialogToken *) + (pFrm + pFf->offset)); + break; + case SigFfLinkMargin: + dot11f_unpack_ff_link_margin(pCtx, + pBufRemaining, (tDot11fFfLinkMargin *) + (pFrm + pFf->offset)); + break; + case SigFfListenInterval: + dot11f_unpack_ff_common_func(pCtx, + pBufRemaining, (uint16_t *)&(((tDot11fFfListenInterval *) + (pFrm + pFf->offset))->interval)); + break; + case SigFfMaxTxPower: + dot11f_unpack_ff_max_tx_power(pCtx, + pBufRemaining, (tDot11fFfMaxTxPower *) + (pFrm + pFf->offset)); + break; + case SigFfNumOfRepetitions: + dot11f_unpack_ff_num_of_repetitions(pCtx, + pBufRemaining, (tDot11fFfNumOfRepetitions *) + (pFrm + pFf->offset)); + break; + case SigFfOperatingMode: + dot11f_unpack_ff_operating_mode(pCtx, + pBufRemaining, (tDot11fFfOperatingMode *) + (pFrm + pFf->offset)); + break; + case SigFfRCPI: + dot11f_unpack_ff_rcpi(pCtx, + pBufRemaining, (tDot11fFfRCPI *) + (pFrm + pFf->offset)); + break; + case SigFfRSNI: + dot11f_unpack_ff_rsni(pCtx, + pBufRemaining, (tDot11fFfRSNI *) + (pFrm + pFf->offset)); + break; + case SigFfReason: + dot11f_unpack_ff_common_func(pCtx, + pBufRemaining, (uint16_t *)&(((tDot11fFfReason *) + (pFrm + pFf->offset))->code)); + break; + case SigFfRxAntennaId: + dot11f_unpack_ff_rx_antenna_id(pCtx, + pBufRemaining, (tDot11fFfRxAntennaId *) + (pFrm + pFf->offset)); + break; + case SigFfSMPowerModeSet: + dot11f_unpack_ff_sm_power_mode_set(pCtx, + pBufRemaining, (tDot11fFfSMPowerModeSet *) + (pFrm + pFf->offset)); + break; + case SigFfStatus: + dot11f_unpack_ff_common_func(pCtx, + pBufRemaining, (uint16_t *)&(((tDot11fFfStatus *) + (pFrm + pFf->offset))->status)); + break; + case SigFfStatusCode: + dot11f_unpack_ff_status_code(pCtx, + pBufRemaining, (tDot11fFfStatusCode *) + (pFrm + pFf->offset)); + break; + case SigFfTPCEleID: + dot11f_unpack_ff_tpc_ele_id(pCtx, + pBufRemaining, (tDot11fFfTPCEleID *) + (pFrm + pFf->offset)); + break; + case SigFfTPCEleLen: + dot11f_unpack_ff_tpc_ele_len(pCtx, + pBufRemaining, (tDot11fFfTPCEleLen *) + (pFrm + pFf->offset)); + break; + case SigFfTSInfo: + dot11f_unpack_ff_ts_info(pCtx, + pBufRemaining, (tDot11fFfTSInfo *) + (pFrm + pFf->offset)); + break; + case SigFfTimeStamp: + dot11f_unpack_ff_time_stamp(pCtx, + pBufRemaining, (tDot11fFfTimeStamp *) + (pFrm + pFf->offset)); + break; + case SigFfTransactionId: + dot11f_unpack_ff_transaction_id(pCtx, + pBufRemaining, (tDot11fFfTransactionId *) + (pFrm + pFf->offset)); + break; + case SigFfTxAntennaId: + dot11f_unpack_ff_tx_antenna_id(pCtx, + pBufRemaining, (tDot11fFfTxAntennaId *) + (pFrm + pFf->offset)); + break; + case SigFfTxPower: + dot11f_unpack_ff_tx_power(pCtx, + pBufRemaining, (tDot11fFfTxPower *) + (pFrm + pFf->offset)); + break; + case SigFfVhtMembershipStatusArray: + dot11f_unpack_ff_vht_membership_status_array(pCtx, + pBufRemaining, (tDot11fFfVhtMembershipStatusArray *) + (pFrm + pFf->offset)); + break; + case SigFfVhtUserPositionArray: + dot11f_unpack_ff_vht_user_position_array(pCtx, + pBufRemaining, (tDot11fFfVhtUserPositionArray *) + (pFrm + pFf->offset)); + break; + case SigFfaddba_param_set: + dot11f_unpack_ff_addba_param_set(pCtx, + pBufRemaining, (tDot11fFfaddba_param_set *) + (pFrm + pFf->offset)); + break; + case SigFfba_start_seq_ctrl: + dot11f_unpack_ff_ba_start_seq_ctrl(pCtx, + pBufRemaining, (tDot11fFfba_start_seq_ctrl *) + (pFrm + pFf->offset)); + break; + case SigFfba_timeout: + dot11f_unpack_ff_ba_timeout(pCtx, + pBufRemaining, (tDot11fFfba_timeout *) + (pFrm + pFf->offset)); + break; + case SigFfdelba_param_set: + dot11f_unpack_ff_delba_param_set(pCtx, + pBufRemaining, (tDot11fFfdelba_param_set *) + (pFrm + pFf->offset)); + break; + case SigFfext_chan_switch_ann_action: + dot11f_unpack_ff_ext_chan_switch_ann_action(pCtx, + pBufRemaining, (tDot11fFfext_chan_switch_ann_action *) + (pFrm + pFf->offset)); + break; + case SigFfp2p_action_oui: + dot11f_unpack_ff_p2p_action_oui(pCtx, + pBufRemaining, (tDot11fFfp2p_action_oui *) + (pFrm + pFf->offset)); + break; + case SigFfp2p_action_subtype: + dot11f_unpack_ff_p2p_action_subtype(pCtx, + pBufRemaining, (tDot11fFfp2p_action_subtype *) + (pFrm + pFf->offset)); + break; + case SigFfvendor_action_subtype: + dot11f_unpack_ff_vendor_action_subtype(pCtx, + pBufRemaining, (tDot11fFfvendor_action_subtype *) + (pFrm + pFf->offset)); + break; + case SigFfvendor_oui: + dot11f_unpack_ff_vendor_oui(pCtx, + pBufRemaining, (tDot11fFfvendor_oui *) + (pFrm + pFf->offset)); + break; + default: + FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR: I don'" + "t know about the FF signature %d-- this is most " + "likely a 'framesc' bug.\n"), pFf->sig); + return DOT11F_INTERNAL_ERROR; + } + + pBufRemaining += pFf->size; + nBufRemaining -= pFf->size; + ++pFf; + } + + while (nBufRemaining) { + if (1 == nBufRemaining) { + FRAMES_LOG0(pCtx, FRLOGE, FRFL("This frame reports " + "only one byte remaining after it's fixed fields.\n")); + status |= DOT11F_INCOMPLETE_IE; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } + + pIe = find_ie_defn(pCtx, pBufRemaining, nBufRemaining, IEs); + + eid = *pBufRemaining++; --nBufRemaining; + len = *pBufRemaining++; --nBufRemaining; + if (pIe && pIe->extn_eid) { + extn_eid = *pBufRemaining++; --nBufRemaining; + len--; + } + + if (pIe && pIe->noui) { + if (pIe->noui > nBufRemaining) { + FRAMES_LOG4(pCtx, FRLOGW, FRFL("IE %d extn id %d reports " + "length %d, but it has an OUI of %d bytes.\n"), + eid, extn_eid, len, pIe->noui); + FRAMES_DUMP(pCtx, FRLOG1, pBuf, nBuf); + FRAMES_LOG2(pCtx, FRLOG1, FRFL("We've parsed %d by" + "tes of this buffer, and show %d left.\n"), + pBufRemaining - pBuf, nBufRemaining); + status |= DOT11F_INCOMPLETE_IE; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } + pBufRemaining += pIe->noui; + nBufRemaining -= pIe->noui; + len -= pIe->noui; + } + + if (len > nBufRemaining) { + FRAMES_LOG4(pCtx, FRLOGW, FRFL("IE %d extn id %d reports length %" + "d, but there are only %d bytes remaining in this" + " frame.\n"), eid, extn_eid, len, nBufRemaining); + FRAMES_DUMP(pCtx, FRLOG1, pBuf, nBuf); + FRAMES_LOG2(pCtx, FRLOG1, FRFL("We've parsed %d by" + "tes of this buffer, and show %d left.\n"), + pBufRemaining - pBuf, nBufRemaining); + status |= DOT11F_INCOMPLETE_IE; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } + + if (pIe) { + if ((nBufRemaining < pIe->minSize - pIe->noui - 2U)) { + FRAMES_LOG3(pCtx, FRLOGW, FRFL("The IE %s must " + "be at least %d bytes in size, but " + "there are only %d bytes remaining in " + "this frame\n"), + pIe->name, pIe->minSize, nBufRemaining); + FRAMES_DUMP(pCtx, FRLOG1, pBuf, nBuf); + status |= DOT11F_INCOMPLETE_IE; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } else { + if (len < pIe->minSize - pIe->noui - 2U) { + FRAMES_LOG3(pCtx, FRLOGW, FRFL("The IE %s must " + "be at least %d bytes in size, but " + "there are only %d bytes in the IE\n"), + pIe->name, pIe->minSize, (len + pIe->noui + 2U)); + goto skip_ie; + } + + if (len > pIe->maxSize - pIe->noui - 2U) { + FRAMES_LOG1(pCtx, FRLOGW, FRFL("The IE %s reports " + "an unexpectedly large size; it is presumably " + "more up-to-date than this parser, but this wa" + "rning may also indicate a corrupt frame.\n"), + pIe->name); + FRAMES_DUMP(pCtx, FRLOG1, pBuf, nBuf); + } + + countOffset = ((0 != pIe->arraybound) * + (*(uint16_t *)(pFrm + pIe->countOffset))); + if (0 != pIe->arraybound && countOffset >= pIe->arraybound) { + status |= DOT11F_DUPLICATE_IE; + goto skip_ie; + } + switch (pIe->sig) { + case SigIeGTK: + status |= + dot11f_unpack_ie_gtk( + pCtx, pBufRemaining, len, + (tDot11fIEGTK *) + (pFrm + pIe->offset + + sizeof(tDot11fIEGTK) * + countOffset), + append_ie); + break; + case SigIeIGTK: + status |= + dot11f_unpack_ie_igtk( + pCtx, pBufRemaining, len, + (tDot11fIEIGTK *) + (pFrm + pIe->offset + + sizeof(tDot11fIEIGTK) * + countOffset), + append_ie); + break; + case SigIeR0KH_ID: + status |= + dot11f_unpack_ie_r0_kh_id( + pCtx, pBufRemaining, len, + (tDot11fIER0KH_ID *) + (pFrm + pIe->offset + + sizeof(tDot11fIER0KH_ID) * + countOffset), + append_ie); + break; + case SigIeR1KH_ID: + status |= + dot11f_unpack_ie_r1_kh_id( + pCtx, pBufRemaining, len, + (tDot11fIER1KH_ID *) + (pFrm + pIe->offset + + sizeof(tDot11fIER1KH_ID) * + countOffset), + append_ie); + break; + case SigIeversion_attr: + status |= + dot11f_unpack_ie_version_attr( + pCtx, pBufRemaining, len, + (tDot11fIEversion_attr *) + (pFrm + pIe->offset + + sizeof(tDot11fIEversion_attr) * + countOffset), + append_ie); + break; + case SigIevht_mcs11_attr: + status |= + dot11f_unpack_ie_vht_mcs11_attr( + pCtx, pBufRemaining, len, + (tDot11fIEvht_mcs11_attr *) + (pFrm + pIe->offset + + sizeof(tDot11fIEvht_mcs11_attr) * + countOffset), + append_ie); + break; + case SigIeAPChannelReport: + status |= + dot11f_unpack_ie_ap_channel_report( + pCtx, pBufRemaining, len, + (tDot11fIEAPChannelReport *) + (pFrm + pIe->offset + + sizeof(tDot11fIEAPChannelReport) * + countOffset), + append_ie); + break; + case SigIeBcnReportingDetail: + status |= + dot11f_unpack_ie_bcn_reporting_detail( + pCtx, pBufRemaining, len, + (tDot11fIEBcnReportingDetail *) + (pFrm + pIe->offset + + sizeof(tDot11fIEBcnReportingDetail) * + countOffset), + append_ie); + break; + case SigIeBeaconReportFrmBody: + status |= + dot11f_unpack_ie_beacon_report_frm_body( + pCtx, pBufRemaining, len, + (tDot11fIEBeaconReportFrmBody *) + (pFrm + pIe->offset + + sizeof(tDot11fIEBeaconReportFrmBody) * + countOffset), + append_ie); + break; + case SigIeBeaconReporting: + status |= + dot11f_unpack_ie_beacon_reporting( + pCtx, pBufRemaining, len, + (tDot11fIEBeaconReporting *) + (pFrm + pIe->offset + + sizeof(tDot11fIEBeaconReporting) * + countOffset), + append_ie); + break; + case SigIeCondensedCountryStr: + status |= + dot11f_unpack_ie_condensed_country_str( + pCtx, pBufRemaining, len, + (tDot11fIECondensedCountryStr *) + (pFrm + pIe->offset + + sizeof(tDot11fIECondensedCountryStr) * + countOffset), + append_ie); + break; + case SigIeMeasurementPilot: + status |= + dot11f_unpack_ie_measurement_pilot( + pCtx, pBufRemaining, len, + (tDot11fIEMeasurementPilot *) + (pFrm + pIe->offset + + sizeof(tDot11fIEMeasurementPilot) * + countOffset), + append_ie); + break; + case SigIeMultiBssid: + status |= + dot11f_unpack_ie_multi_bssid( + pCtx, pBufRemaining, len, + (tDot11fIEMultiBssid *) + (pFrm + pIe->offset + + sizeof(tDot11fIEMultiBssid) * + countOffset), + append_ie); + break; + case SigIeRICData: + status |= + dot11f_unpack_ie_ric_data( + pCtx, pBufRemaining, len, + (tDot11fIERICData *) + (pFrm + pIe->offset + + sizeof(tDot11fIERICData) * + countOffset), + append_ie); + break; + case SigIeRICDescriptor: + status |= + dot11f_unpack_ie_ric_descriptor( + pCtx, pBufRemaining, len, + (tDot11fIERICDescriptor *) + (pFrm + pIe->offset + + sizeof(tDot11fIERICDescriptor) * + countOffset), + append_ie); + break; + case SigIeRRMEnabledCap: + status |= + dot11f_unpack_ie_rrm_enabled_cap( + pCtx, pBufRemaining, len, + (tDot11fIERRMEnabledCap *) + (pFrm + pIe->offset + + sizeof(tDot11fIERRMEnabledCap) * + countOffset), + append_ie); + break; + case SigIeRequestedInfo: + status |= + dot11f_unpack_ie_requested_info( + pCtx, pBufRemaining, len, + (tDot11fIERequestedInfo *) + (pFrm + pIe->offset + + sizeof(tDot11fIERequestedInfo) * + countOffset), + append_ie); + break; + case SigIeSSID: + status |= + dot11f_unpack_ie_ssid( + pCtx, pBufRemaining, len, + (tDot11fIESSID *) + (pFrm + pIe->offset + + sizeof(tDot11fIESSID) * + countOffset), + append_ie); + break; + case SigIeSchedule: + status |= + dot11f_unpack_ie_schedule( + pCtx, pBufRemaining, len, + (tDot11fIESchedule *) + (pFrm + pIe->offset + + sizeof(tDot11fIESchedule) * + countOffset), + append_ie); + break; + case SigIeTCLAS: + status |= + dot11f_unpack_ie_tclas( + pCtx, pBufRemaining, len, + (tDot11fIETCLAS *) + (pFrm + pIe->offset + + sizeof(tDot11fIETCLAS) * + countOffset), + append_ie); + break; + case SigIeTCLASSPROC: + status |= dot11f_unpack_ie_common_func(pCtx, pBufRemaining, len, + (uint8_t *) &(((tDot11fIETCLASSPROC *)(pFrm + pIe->offset + sizeof(tDot11fIETCLASSPROC)*countOffset))->present), + (uint8_t *) &(((tDot11fIETCLASSPROC *)(pFrm + pIe->offset + sizeof(tDot11fIETCLASSPROC)*countOffset))->processing)); + break; + case SigIeTSDelay: + status |= + dot11f_unpack_ie_ts_delay( + pCtx, pBufRemaining, len, + (tDot11fIETSDelay *) + (pFrm + pIe->offset + + sizeof(tDot11fIETSDelay) * + countOffset), + append_ie); + break; + case SigIeTSFInfo: + status |= + dot11f_unpack_ie_tsf_info( + pCtx, pBufRemaining, len, + (tDot11fIETSFInfo *) + (pFrm + pIe->offset + + sizeof(tDot11fIETSFInfo) * + countOffset), + append_ie); + break; + case SigIeTSPEC: + status |= + dot11f_unpack_ie_tspec( + pCtx, pBufRemaining, len, + (tDot11fIETSPEC *) + (pFrm + pIe->offset + + sizeof(tDot11fIETSPEC) * + countOffset), + append_ie); + break; + case SigIeVHTCaps: + status |= + dot11f_unpack_ie_vht_caps( + pCtx, pBufRemaining, len, + (tDot11fIEVHTCaps *) + (pFrm + pIe->offset + + sizeof(tDot11fIEVHTCaps) * + countOffset), + append_ie); + break; + case SigIeVHTOperation: + status |= + dot11f_unpack_ie_vht_operation( + pCtx, pBufRemaining, len, + (tDot11fIEVHTOperation *) + (pFrm + pIe->offset + + sizeof(tDot11fIEVHTOperation) * + countOffset), + append_ie); + break; + case SigIeWMMSchedule: + status |= + dot11f_unpack_ie_wmm_schedule( + pCtx, pBufRemaining, len, + (tDot11fIEWMMSchedule *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWMMSchedule) * + countOffset), + append_ie); + break; + case SigIeWMMTCLAS: + status |= + dot11f_unpack_ie_wmmtclas( + pCtx, pBufRemaining, len, + (tDot11fIEWMMTCLAS *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWMMTCLAS) * + countOffset), + append_ie); + break; + case SigIeWMMTCLASPROC: + status |= + dot11f_unpack_ie_wmmtclasproc( + pCtx, pBufRemaining, len, + (tDot11fIEWMMTCLASPROC *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWMMTCLASPROC) * + countOffset), + append_ie); + break; + case SigIeWMMTSDelay: + status |= + dot11f_unpack_ie_wmmts_delay( + pCtx, pBufRemaining, len, + (tDot11fIEWMMTSDelay *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWMMTSDelay) * + countOffset), + append_ie); + break; + case SigIeWMMTSPEC: + status |= + dot11f_unpack_ie_wmmtspec( + pCtx, pBufRemaining, len, + (tDot11fIEWMMTSPEC *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWMMTSPEC) * + countOffset), + append_ie); + break; + case SigIeWiderBWChanSwitchAnn: + status |= + dot11f_unpack_ie_wider_bw_chan_switch_ann( + pCtx, pBufRemaining, len, + (tDot11fIEWiderBWChanSwitchAnn *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWiderBWChanSwitchAnn) * + countOffset), + append_ie); + break; + case SigIeazimuth_req: + status |= + dot11f_unpack_ie_azimuth_req( + pCtx, pBufRemaining, len, + (tDot11fIEazimuth_req *) + (pFrm + pIe->offset + + sizeof(tDot11fIEazimuth_req) * + countOffset), + append_ie); + break; + case SigIebeacon_report_frm_body_fragment_id: + status |= + dot11f_unpack_ie_beacon_report_frm_body_fragment_id( + pCtx, pBufRemaining, len, + (tDot11fIEbeacon_report_frm_body_fragment_id *) + (pFrm + pIe->offset + + sizeof(tDot11fIEbeacon_report_frm_body_fragment_id) * + countOffset), + append_ie); + break; + case SigIelast_beacon_report_indication: + status |= + dot11f_unpack_ie_last_beacon_report_indication( + pCtx, pBufRemaining, len, + (tDot11fIElast_beacon_report_indication *) + (pFrm + pIe->offset + + sizeof(tDot11fIElast_beacon_report_indication) * + countOffset), + append_ie); + break; + case SigIemax_age: + status |= + dot11f_unpack_ie_max_age( + pCtx, pBufRemaining, len, + (tDot11fIEmax_age *) + (pFrm + pIe->offset + + sizeof(tDot11fIEmax_age) * + countOffset), + append_ie); + break; + case SigIeneighbor_rpt: + status |= + dot11f_unpack_ie_neighbor_rpt( + pCtx, pBufRemaining, len, + (tDot11fIEneighbor_rpt *) + (pFrm + pIe->offset + + sizeof(tDot11fIEneighbor_rpt) * + countOffset), + append_ie); + break; + case SigIereq_mac_addr: + status |= + dot11f_unpack_ie_req_mac_addr( + pCtx, pBufRemaining, len, + (tDot11fIEreq_mac_addr *) + (pFrm + pIe->offset + + sizeof(tDot11fIEreq_mac_addr) * + countOffset), + append_ie); + break; + case SigIetgt_mac_addr: + status |= + dot11f_unpack_ie_tgt_mac_addr( + pCtx, pBufRemaining, len, + (tDot11fIEtgt_mac_addr *) + (pFrm + pIe->offset + + sizeof(tDot11fIEtgt_mac_addr) * + countOffset), + append_ie); + break; + case SigIevht_transmit_power_env: + status |= + dot11f_unpack_ie_vht_transmit_power_env( + pCtx, pBufRemaining, len, + (tDot11fIEvht_transmit_power_env *) + (pFrm + pIe->offset + + sizeof(tDot11fIEvht_transmit_power_env) * + countOffset), + append_ie); + break; + case SigIeAID: + status |= + dot11f_unpack_ie_aid( + pCtx, pBufRemaining, len, + (tDot11fIEAID *) + (pFrm + pIe->offset + + sizeof(tDot11fIEAID) * + countOffset), + append_ie); + break; + case SigIeBeaconReportStatus: + status |= + dot11f_unpack_ie_BeaconReportStatus( + pCtx, pBufRemaining, len, + (tDot11fIEBeaconReportStatus *) + (pFrm + pIe->offset + + sizeof(tDot11fIEBeaconReportStatus) * + countOffset), + append_ie); + break; + case SigIeCFParams: + status |= + dot11f_unpack_ie_cf_params( + pCtx, pBufRemaining, len, + (tDot11fIECFParams *) + (pFrm + pIe->offset + + sizeof(tDot11fIECFParams) * + countOffset), + append_ie); + break; + case SigIeChallengeText: + status |= + dot11f_unpack_ie_challenge_text( + pCtx, pBufRemaining, len, + (tDot11fIEChallengeText *) + (pFrm + pIe->offset + + sizeof(tDot11fIEChallengeText) * + countOffset), + append_ie); + break; + case SigIeChanSwitchAnn: + status |= + dot11f_unpack_ie_chan_switch_ann( + pCtx, pBufRemaining, len, + (tDot11fIEChanSwitchAnn *) + (pFrm + pIe->offset + + sizeof(tDot11fIEChanSwitchAnn) * + countOffset), + append_ie); + break; + case SigIeChannelSwitchWrapper: + status |= + dot11f_unpack_ie_channel_switch_wrapper( + pCtx, pBufRemaining, len, + (tDot11fIEChannelSwitchWrapper *) + (pFrm + pIe->offset + + sizeof(tDot11fIEChannelSwitchWrapper) * + countOffset), + append_ie); + break; + case SigIeCountry: + status |= + dot11f_unpack_ie_country( + pCtx, pBufRemaining, len, + (tDot11fIECountry *) + (pFrm + pIe->offset + + sizeof(tDot11fIECountry) * + countOffset), + append_ie); + break; + case SigIeDSParams: + status |= dot11f_unpack_ie_common_func(pCtx, pBufRemaining, len, + (uint8_t *) &(((tDot11fIEDSParams *)(pFrm + pIe->offset + sizeof(tDot11fIEDSParams)*countOffset))->present), + (uint8_t *) &(((tDot11fIEDSParams *)(pFrm + pIe->offset + sizeof(tDot11fIEDSParams)*countOffset))->curr_channel)); + break; + case SigIeEDCAParamSet: + status |= + dot11f_unpack_ie_edca_param_set( + pCtx, pBufRemaining, len, + (tDot11fIEEDCAParamSet *) + (pFrm + pIe->offset + + sizeof(tDot11fIEEDCAParamSet) * + countOffset), + append_ie); + break; + case SigIeERPInfo: + status |= + dot11f_unpack_ie_erp_info( + pCtx, pBufRemaining, len, + (tDot11fIEERPInfo *) + (pFrm + pIe->offset + + sizeof(tDot11fIEERPInfo) * + countOffset), + append_ie); + break; + case SigIeESECckmOpaque: + status |= + dot11f_unpack_ie_ese_cckm_opaque( + pCtx, pBufRemaining, len, + (tDot11fIEESECckmOpaque *) + (pFrm + pIe->offset + + sizeof(tDot11fIEESECckmOpaque) * + countOffset), + append_ie); + break; + case SigIeESERadMgmtCap: + status |= + dot11f_unpack_ie_ese_rad_mgmt_cap( + pCtx, pBufRemaining, len, + (tDot11fIEESERadMgmtCap *) + (pFrm + pIe->offset + + sizeof(tDot11fIEESERadMgmtCap) * + countOffset), + append_ie); + break; + case SigIeESETrafStrmMet: + status |= + dot11f_unpack_ie_ese_traf_strm_met( + pCtx, pBufRemaining, len, + (tDot11fIEESETrafStrmMet *) + (pFrm + pIe->offset + + sizeof(tDot11fIEESETrafStrmMet) * + countOffset), + append_ie); + break; + case SigIeESETrafStrmRateSet: + status |= + dot11f_unpack_ie_ese_traf_strm_rate_set( + pCtx, pBufRemaining, len, + (tDot11fIEESETrafStrmRateSet *) + (pFrm + pIe->offset + + sizeof(tDot11fIEESETrafStrmRateSet) * + countOffset), + append_ie); + break; + case SigIeESETxmitPower: + status |= + dot11f_unpack_ie_ese_txmit_power( + pCtx, pBufRemaining, len, + (tDot11fIEESETxmitPower *) + (pFrm + pIe->offset + + sizeof(tDot11fIEESETxmitPower) * + countOffset), + append_ie); + break; + case SigIeESEVersion: + status |= + dot11f_unpack_ie_ese_version( + pCtx, pBufRemaining, len, + (tDot11fIEESEVersion *) + (pFrm + pIe->offset + + sizeof(tDot11fIEESEVersion) * + countOffset), + append_ie); + break; + case SigIeExtCap: + status |= + dot11f_unpack_ie_ext_cap( + pCtx, pBufRemaining, len, + (tDot11fIEExtCap *) + (pFrm + pIe->offset + + sizeof(tDot11fIEExtCap) * + countOffset), + append_ie); + break; + case SigIeExtSuppRates: + status |= + dot11f_unpack_ie_ext_supp_rates( + pCtx, pBufRemaining, len, + (tDot11fIEExtSuppRates *) + (pFrm + pIe->offset + + sizeof(tDot11fIEExtSuppRates) * + countOffset), + append_ie); + break; + case SigIeFHParamSet: + status |= + dot11f_unpack_ie_fh_param_set( + pCtx, pBufRemaining, len, + (tDot11fIEFHParamSet *) + (pFrm + pIe->offset + + sizeof(tDot11fIEFHParamSet) * + countOffset), + append_ie); + break; + case SigIeFHParams: + status |= + dot11f_unpack_ie_fh_params( + pCtx, pBufRemaining, len, + (tDot11fIEFHParams *) + (pFrm + pIe->offset + + sizeof(tDot11fIEFHParams) * + countOffset), + append_ie); + break; + case SigIeFHPattTable: + status |= + dot11f_unpack_ie_fh_patt_table( + pCtx, pBufRemaining, len, + (tDot11fIEFHPattTable *) + (pFrm + pIe->offset + + sizeof(tDot11fIEFHPattTable) * + countOffset), + append_ie); + break; + case SigIeFTInfo: + status |= + dot11f_unpack_ie_ft_info( + pCtx, pBufRemaining, len, + (tDot11fIEFTInfo *) + (pFrm + pIe->offset + + sizeof(tDot11fIEFTInfo) * + countOffset), + append_ie); + break; + case SigIeHTCaps: + status |= + dot11f_unpack_ie_ht_caps( + pCtx, pBufRemaining, len, + (tDot11fIEHTCaps *) + (pFrm + pIe->offset + + sizeof(tDot11fIEHTCaps) * + countOffset), + append_ie); + break; + case SigIeHTInfo: + status |= + dot11f_unpack_ie_ht_info( + pCtx, pBufRemaining, len, + (tDot11fIEHTInfo *) + (pFrm + pIe->offset + + sizeof(tDot11fIEHTInfo) * + countOffset), + append_ie); + break; + case SigIeIBSSParams: + status |= + dot11f_unpack_ie_ibss_params( + pCtx, pBufRemaining, len, + (tDot11fIEIBSSParams *) + (pFrm + pIe->offset + + sizeof(tDot11fIEIBSSParams) * + countOffset), + append_ie); + break; + case SigIeLinkIdentifier: + status |= + dot11f_unpack_ie_link_identifier( + pCtx, pBufRemaining, len, + (tDot11fIELinkIdentifier *) + (pFrm + pIe->offset + + sizeof(tDot11fIELinkIdentifier) * + countOffset), + append_ie); + break; + case SigIeMBO_IE: + status |= + dot11f_unpack_ie_MBO_IE( + pCtx, pBufRemaining, len, + (tDot11fIEMBO_IE *) + (pFrm + pIe->offset + + sizeof(tDot11fIEMBO_IE) * + countOffset), + append_ie); + break; + case SigIeMeasurementReport: + status |= + dot11f_unpack_ie_measurement_report( + pCtx, pBufRemaining, len, + (tDot11fIEMeasurementReport *) + (pFrm + pIe->offset + + sizeof(tDot11fIEMeasurementReport) * + countOffset), + append_ie); + break; + case SigIeMeasurementRequest: + status |= + dot11f_unpack_ie_measurement_request( + pCtx, pBufRemaining, len, + (tDot11fIEMeasurementRequest *) + (pFrm + pIe->offset + + sizeof(tDot11fIEMeasurementRequest) * + countOffset), + append_ie); + break; + case SigIeMobilityDomain: + status |= + dot11f_unpack_ie_mobility_domain( + pCtx, pBufRemaining, len, + (tDot11fIEMobilityDomain *) + (pFrm + pIe->offset + + sizeof(tDot11fIEMobilityDomain) * + countOffset), + append_ie); + break; + case SigIeNeighborReport: + status |= + dot11f_unpack_ie_neighbor_report( + pCtx, pBufRemaining, len, + (tDot11fIENeighborReport *) + (pFrm + pIe->offset + + sizeof(tDot11fIENeighborReport) * + countOffset), + append_ie); + break; + case SigIeOBSSScanParameters: + status |= + dot11f_unpack_ie_obss_scan_parameters( + pCtx, pBufRemaining, len, + (tDot11fIEOBSSScanParameters *) + (pFrm + pIe->offset + + sizeof(tDot11fIEOBSSScanParameters) * + countOffset), + append_ie); + break; + case SigIeOperatingMode: + status |= + dot11f_unpack_ie_operating_mode( + pCtx, pBufRemaining, len, + (tDot11fIEOperatingMode *) + (pFrm + pIe->offset + + sizeof(tDot11fIEOperatingMode) * + countOffset), + append_ie); + break; + case SigIeP2PAssocReq: + status |= + dot11f_unpack_ie_p2_p_assoc_req( + pCtx, pBufRemaining, len, + (tDot11fIEP2PAssocReq *) + (pFrm + pIe->offset + + sizeof(tDot11fIEP2PAssocReq) * + countOffset), + append_ie); + break; + case SigIeP2PAssocRes: + status |= + dot11f_unpack_ie_p2_p_assoc_res( + pCtx, pBufRemaining, len, + (tDot11fIEP2PAssocRes *) + (pFrm + pIe->offset + + sizeof(tDot11fIEP2PAssocRes) * + countOffset), + append_ie); + break; + case SigIeP2PBeacon: + status |= + dot11f_unpack_ie_p2_p_beacon( + pCtx, pBufRemaining, len, + (tDot11fIEP2PBeacon *) + (pFrm + pIe->offset + + sizeof(tDot11fIEP2PBeacon) * + countOffset), + append_ie); + break; + case SigIeP2PBeaconProbeRes: + status |= + dot11f_unpack_ie_p2_p_beacon_probe_res( + pCtx, pBufRemaining, len, + (tDot11fIEP2PBeaconProbeRes *) + (pFrm + pIe->offset + + sizeof(tDot11fIEP2PBeaconProbeRes) * + countOffset), + append_ie); + break; + case SigIeP2PDeAuth: + status |= + dot11f_unpack_ie_p2_p_de_auth( + pCtx, pBufRemaining, len, + (tDot11fIEP2PDeAuth *) + (pFrm + pIe->offset + + sizeof(tDot11fIEP2PDeAuth) * + countOffset), + append_ie); + break; + case SigIeP2PDisAssoc: + status |= + dot11f_unpack_ie_p2_p_dis_assoc( + pCtx, pBufRemaining, len, + (tDot11fIEP2PDisAssoc *) + (pFrm + pIe->offset + + sizeof(tDot11fIEP2PDisAssoc) * + countOffset), + append_ie); + break; + case SigIeP2PIEOpaque: + status |= + dot11f_unpack_ie_p2_pie_opaque( + pCtx, pBufRemaining, len, + (tDot11fIEP2PIEOpaque *) + (pFrm + pIe->offset + + sizeof(tDot11fIEP2PIEOpaque) * + countOffset), + append_ie); + break; + case SigIeP2PProbeReq: + status |= + dot11f_unpack_ie_p2_p_probe_req( + pCtx, pBufRemaining, len, + (tDot11fIEP2PProbeReq *) + (pFrm + pIe->offset + + sizeof(tDot11fIEP2PProbeReq) * + countOffset), + append_ie); + break; + case SigIeP2PProbeRes: + status |= + dot11f_unpack_ie_p2_p_probe_res( + pCtx, pBufRemaining, len, + (tDot11fIEP2PProbeRes *) + (pFrm + pIe->offset + + sizeof(tDot11fIEP2PProbeRes) * + countOffset), + append_ie); + break; + case SigIePTIControl: + status |= + dot11f_unpack_ie_pti_control( + pCtx, pBufRemaining, len, + (tDot11fIEPTIControl *) + (pFrm + pIe->offset + + sizeof(tDot11fIEPTIControl) * + countOffset), + append_ie); + break; + case SigIePUBufferStatus: + status |= + dot11f_unpack_ie_pu_buffer_status( + pCtx, pBufRemaining, len, + (tDot11fIEPUBufferStatus *) + (pFrm + pIe->offset + + sizeof(tDot11fIEPUBufferStatus) * + countOffset), + append_ie); + break; + case SigIePowerCaps: + status |= + dot11f_unpack_ie_power_caps( + pCtx, pBufRemaining, len, + (tDot11fIEPowerCaps *) + (pFrm + pIe->offset + + sizeof(tDot11fIEPowerCaps) * + countOffset), + append_ie); + break; + case SigIePowerConstraints: + status |= + dot11f_unpack_ie_power_constraints( + pCtx, pBufRemaining, len, + (tDot11fIEPowerConstraints *) + (pFrm + pIe->offset + + sizeof(tDot11fIEPowerConstraints) * + countOffset), + append_ie); + break; + case SigIeQBSSLoad: + status |= + dot11f_unpack_ie_qbss_load( + pCtx, pBufRemaining, len, + (tDot11fIEQBSSLoad *) + (pFrm + pIe->offset + + sizeof(tDot11fIEQBSSLoad) * + countOffset), + append_ie); + break; + case SigIeQComVendorIE: + status |= + dot11f_unpack_ie_QComVendorIE( + pCtx, pBufRemaining, len, + (tDot11fIEQComVendorIE *) + (pFrm + pIe->offset + + sizeof(tDot11fIEQComVendorIE) * + countOffset), + append_ie); + break; + case SigIeQOSCapsAp: + status |= + dot11f_unpack_ie_qos_caps_ap( + pCtx, pBufRemaining, len, + (tDot11fIEQOSCapsAp *) + (pFrm + pIe->offset + + sizeof(tDot11fIEQOSCapsAp) * + countOffset), + append_ie); + break; + case SigIeQOSCapsStation: + status |= + dot11f_unpack_ie_qos_caps_station( + pCtx, pBufRemaining, len, + (tDot11fIEQOSCapsStation *) + (pFrm + pIe->offset + + sizeof(tDot11fIEQOSCapsStation) * + countOffset), + append_ie); + break; + case SigIeQosMapSet: + status |= + dot11f_unpack_ie_qos_map_set( + pCtx, pBufRemaining, len, + (tDot11fIEQosMapSet *) + (pFrm + pIe->offset + + sizeof(tDot11fIEQosMapSet) * + countOffset), + append_ie); + break; + case SigIeQuiet: + status |= + dot11f_unpack_ie_quiet( + pCtx, pBufRemaining, len, + (tDot11fIEQuiet *) + (pFrm + pIe->offset + + sizeof(tDot11fIEQuiet) * + countOffset), + append_ie); + break; + case SigIeRCPIIE: + status |= + dot11f_unpack_ie_rcpiie( + pCtx, pBufRemaining, len, + (tDot11fIERCPIIE *) + (pFrm + pIe->offset + + sizeof(tDot11fIERCPIIE) * + countOffset), + append_ie); + break; + case SigIeRICDataDesc: + /* reset the pointers back since this is a container IE and it doesn't have its own EID and Len. */ + pBufRemaining -= 2; + nBufRemaining += 2; + if (pIe && pIe->noui) { + pBufRemaining -= pIe->noui; + nBufRemaining += pIe->noui; + len += pIe->noui; + } + status |= get_container_ies_len(pCtx, pBufRemaining, nBufRemaining, &len, IES_RICDataDesc); + if (status != DOT11F_PARSE_SUCCESS && status != DOT11F_UNKNOWN_IES) + break; + status |= + dot11f_unpack_ie_ric_data_desc( + pCtx, pBufRemaining, len, + (tDot11fIERICDataDesc *) + (pFrm + pIe->offset + + sizeof(tDot11fIERICDataDesc) * + countOffset), + append_ie); + break; + case SigIeRSN: + status |= + dot11f_unpack_ie_rsn( + pCtx, pBufRemaining, len, + (tDot11fIERSN *) + (pFrm + pIe->offset + + sizeof(tDot11fIERSN) * + countOffset), + append_ie); + break; + case SigIeRSNIIE: + status |= + dot11f_unpack_ie_rsniie( + pCtx, pBufRemaining, len, + (tDot11fIERSNIIE *) + (pFrm + pIe->offset + + sizeof(tDot11fIERSNIIE) * + countOffset), + append_ie); + break; + case SigIeRSNOpaque: + status |= + dot11f_unpack_ie_rsn_opaque( + pCtx, pBufRemaining, len, + (tDot11fIERSNOpaque *) + (pFrm + pIe->offset + + sizeof(tDot11fIERSNOpaque) * + countOffset), + append_ie); + break; + case SigIeSuppChannels: + status |= + dot11f_unpack_ie_supp_channels( + pCtx, pBufRemaining, len, + (tDot11fIESuppChannels *) + (pFrm + pIe->offset + + sizeof(tDot11fIESuppChannels) * + countOffset), + append_ie); + break; + case SigIeSuppOperatingClasses: + status |= + dot11f_unpack_ie_supp_operating_classes( + pCtx, pBufRemaining, len, + (tDot11fIESuppOperatingClasses *) + (pFrm + pIe->offset + + sizeof(tDot11fIESuppOperatingClasses) * + countOffset), + append_ie); + break; + case SigIeSuppRates: + status |= + dot11f_unpack_ie_supp_rates( + pCtx, pBufRemaining, len, + (tDot11fIESuppRates *) + (pFrm + pIe->offset + + sizeof(tDot11fIESuppRates) * + countOffset), + append_ie); + break; + case SigIeTIM: + status |= + dot11f_unpack_ie_tim( + pCtx, pBufRemaining, len, + (tDot11fIETIM *) + (pFrm + pIe->offset + + sizeof(tDot11fIETIM) * + countOffset), + append_ie); + break; + case SigIeTPCReport: + status |= + dot11f_unpack_ie_tpc_report( + pCtx, pBufRemaining, len, + (tDot11fIETPCReport *) + (pFrm + pIe->offset + + sizeof(tDot11fIETPCReport) * + countOffset), + append_ie); + break; + case SigIeTPCRequest: + status |= + dot11f_unpack_ie_tpc_request( + pCtx, pBufRemaining, len, + (tDot11fIETPCRequest *) + (pFrm + pIe->offset + + sizeof(tDot11fIETPCRequest) * + countOffset), + append_ie); + break; + case SigIeTimeAdvertisement: + status |= + dot11f_unpack_ie_time_advertisement( + pCtx, pBufRemaining, len, + (tDot11fIETimeAdvertisement *) + (pFrm + pIe->offset + + sizeof(tDot11fIETimeAdvertisement) * + countOffset), + append_ie); + break; + case SigIeTimeoutInterval: + status |= + dot11f_unpack_ie_timeout_interval( + pCtx, pBufRemaining, len, + (tDot11fIETimeoutInterval *) + (pFrm + pIe->offset + + sizeof(tDot11fIETimeoutInterval) * + countOffset), + append_ie); + break; + case SigIeVHTExtBssLoad: + status |= + dot11f_unpack_ie_vht_ext_bss_load( + pCtx, pBufRemaining, len, + (tDot11fIEVHTExtBssLoad *) + (pFrm + pIe->offset + + sizeof(tDot11fIEVHTExtBssLoad) * + countOffset), + append_ie); + break; + case SigIeVendor1IE: + status |= + dot11f_unpack_ie_vendor1_ie( + pCtx, pBufRemaining, len, + (tDot11fIEVendor1IE *) + (pFrm + pIe->offset + + sizeof(tDot11fIEVendor1IE) * + countOffset), + append_ie); + break; + case SigIeVendor3IE: + status |= + dot11f_unpack_ie_vendor3_ie( + pCtx, pBufRemaining, len, + (tDot11fIEVendor3IE *) + (pFrm + pIe->offset + + sizeof(tDot11fIEVendor3IE) * + countOffset), + append_ie); + break; + case SigIeWAPI: + status |= + dot11f_unpack_ie_wapi( + pCtx, pBufRemaining, len, + (tDot11fIEWAPI *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWAPI) * + countOffset), + append_ie); + break; + case SigIeWAPIOpaque: + status |= + dot11f_unpack_ie_wapi_opaque( + pCtx, pBufRemaining, len, + (tDot11fIEWAPIOpaque *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWAPIOpaque) * + countOffset), + append_ie); + break; + case SigIeWFATPC: + status |= + dot11f_unpack_ie_wfatpc( + pCtx, pBufRemaining, len, + (tDot11fIEWFATPC *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWFATPC) * + countOffset), + append_ie); + break; + case SigIeWFDIEOpaque: + status |= + dot11f_unpack_ie_wfdie_opaque( + pCtx, pBufRemaining, len, + (tDot11fIEWFDIEOpaque *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWFDIEOpaque) * + countOffset), + append_ie); + break; + case SigIeWMMCaps: + status |= + dot11f_unpack_ie_wmm_caps( + pCtx, pBufRemaining, len, + (tDot11fIEWMMCaps *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWMMCaps) * + countOffset), + append_ie); + break; + case SigIeWMMInfoAp: + status |= + dot11f_unpack_ie_wmm_info_ap( + pCtx, pBufRemaining, len, + (tDot11fIEWMMInfoAp *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWMMInfoAp) * + countOffset), + append_ie); + break; + case SigIeWMMInfoStation: + status |= + dot11f_unpack_ie_wmm_info_station( + pCtx, pBufRemaining, len, + (tDot11fIEWMMInfoStation *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWMMInfoStation) * + countOffset), + append_ie); + break; + case SigIeWMMParams: + status |= + dot11f_unpack_ie_wmm_params( + pCtx, pBufRemaining, len, + (tDot11fIEWMMParams *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWMMParams) * + countOffset), + append_ie); + break; + case SigIeWPA: + status |= + dot11f_unpack_ie_wpa( + pCtx, pBufRemaining, len, + (tDot11fIEWPA *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWPA) * + countOffset), + append_ie); + break; + case SigIeWPAOpaque: + status |= + dot11f_unpack_ie_wpa_opaque( + pCtx, pBufRemaining, len, + (tDot11fIEWPAOpaque *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWPAOpaque) * + countOffset), + append_ie); + break; + case SigIeWSC: + status |= + dot11f_unpack_ie_wsc( + pCtx, pBufRemaining, len, + (tDot11fIEWSC *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWSC) * + countOffset), + append_ie); + break; + case SigIeWscAssocReq: + status |= + dot11f_unpack_ie_wsc_assoc_req( + pCtx, pBufRemaining, len, + (tDot11fIEWscAssocReq *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWscAssocReq) * + countOffset), + append_ie); + break; + case SigIeWscAssocRes: + status |= + dot11f_unpack_ie_wsc_assoc_res( + pCtx, pBufRemaining, len, + (tDot11fIEWscAssocRes *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWscAssocRes) * + countOffset), + append_ie); + break; + case SigIeWscBeacon: + status |= + dot11f_unpack_ie_wsc_beacon( + pCtx, pBufRemaining, len, + (tDot11fIEWscBeacon *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWscBeacon) * + countOffset), + append_ie); + break; + case SigIeWscBeaconProbeRes: + status |= + dot11f_unpack_ie_wsc_beacon_probe_res( + pCtx, pBufRemaining, len, + (tDot11fIEWscBeaconProbeRes *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWscBeaconProbeRes) * + countOffset), + append_ie); + break; + case SigIeWscIEOpaque: + status |= + dot11f_unpack_ie_wsc_ie_opaque( + pCtx, pBufRemaining, len, + (tDot11fIEWscIEOpaque *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWscIEOpaque) * + countOffset), + append_ie); + break; + case SigIeWscProbeReq: + status |= + dot11f_unpack_ie_wsc_probe_req( + pCtx, pBufRemaining, len, + (tDot11fIEWscProbeReq *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWscProbeReq) * + countOffset), + append_ie); + break; + case SigIeWscProbeRes: + status |= + dot11f_unpack_ie_wsc_probe_res( + pCtx, pBufRemaining, len, + (tDot11fIEWscProbeRes *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWscProbeRes) * + countOffset), + append_ie); + break; + case SigIeWscReassocRes: + status |= + dot11f_unpack_ie_wsc_reassoc_res( + pCtx, pBufRemaining, len, + (tDot11fIEWscReassocRes *) + (pFrm + pIe->offset + + sizeof(tDot11fIEWscReassocRes) * + countOffset), + append_ie); + break; + case SigIeaddba_extn_element: + status |= + dot11f_unpack_ie_addba_extn_element( + pCtx, pBufRemaining, len, + (tDot11fIEaddba_extn_element *) + (pFrm + pIe->offset + + sizeof(tDot11fIEaddba_extn_element) * + countOffset), + append_ie); + break; + case SigIebss_color_change: + status |= + dot11f_unpack_ie_bss_color_change( + pCtx, pBufRemaining, len, + (tDot11fIEbss_color_change *) + (pFrm + pIe->offset + + sizeof(tDot11fIEbss_color_change) * + countOffset), + append_ie); + break; + case SigIedh_parameter_element: + status |= + dot11f_unpack_ie_dh_parameter_element( + pCtx, pBufRemaining, len, + (tDot11fIEdh_parameter_element *) + (pFrm + pIe->offset + + sizeof(tDot11fIEdh_parameter_element) * + countOffset), + append_ie); + break; + case SigIeesp_information: + status |= + dot11f_unpack_ie_esp_information( + pCtx, pBufRemaining, len, + (tDot11fIEesp_information *) + (pFrm + pIe->offset + + sizeof(tDot11fIEesp_information) * + countOffset), + append_ie); + break; + case SigIeext_chan_switch_ann: + status |= + dot11f_unpack_ie_ext_chan_switch_ann( + pCtx, pBufRemaining, len, + (tDot11fIEext_chan_switch_ann *) + (pFrm + pIe->offset + + sizeof(tDot11fIEext_chan_switch_ann) * + countOffset), + append_ie); + break; + case SigIefils_assoc_delay_info: + status |= + dot11f_unpack_ie_fils_assoc_delay_info( + pCtx, pBufRemaining, len, + (tDot11fIEfils_assoc_delay_info *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfils_assoc_delay_info) * + countOffset), + append_ie); + break; + case SigIefils_hlp_container: + status |= + dot11f_unpack_ie_fils_hlp_container( + pCtx, pBufRemaining, len, + (tDot11fIEfils_hlp_container *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfils_hlp_container) * + countOffset), + append_ie); + break; + case SigIefils_indication: + status |= + dot11f_unpack_ie_fils_indication( + pCtx, pBufRemaining, len, + (tDot11fIEfils_indication *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfils_indication) * + countOffset), + append_ie); + break; + case SigIefils_kde: + status |= + dot11f_unpack_ie_fils_kde( + pCtx, pBufRemaining, len, + (tDot11fIEfils_kde *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfils_kde) * + countOffset), + append_ie); + break; + case SigIefils_key_confirmation: + status |= + dot11f_unpack_ie_fils_key_confirmation( + pCtx, pBufRemaining, len, + (tDot11fIEfils_key_confirmation *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfils_key_confirmation) * + countOffset), + append_ie); + break; + case SigIefils_nonce: + status |= + dot11f_unpack_ie_fils_nonce( + pCtx, pBufRemaining, len, + (tDot11fIEfils_nonce *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfils_nonce) * + countOffset), + append_ie); + break; + case SigIefils_public_key: + status |= + dot11f_unpack_ie_fils_public_key( + pCtx, pBufRemaining, len, + (tDot11fIEfils_public_key *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfils_public_key) * + countOffset), + append_ie); + break; + case SigIefils_session: + status |= + dot11f_unpack_ie_fils_session( + pCtx, pBufRemaining, len, + (tDot11fIEfils_session *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfils_session) * + countOffset), + append_ie); + break; + case SigIefils_wrapped_data: + status |= + dot11f_unpack_ie_fils_wrapped_data( + pCtx, pBufRemaining, len, + (tDot11fIEfils_wrapped_data *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfils_wrapped_data) * + countOffset), + append_ie); + break; + case SigIefragment_ie: + status |= + dot11f_unpack_ie_fragment_ie( + pCtx, pBufRemaining, len, + (tDot11fIEfragment_ie *) + (pFrm + pIe->offset + + sizeof(tDot11fIEfragment_ie) * + countOffset), + append_ie); + break; + case SigIehe_6ghz_band_cap: + status |= + dot11f_unpack_ie_he_6ghz_band_cap( + pCtx, pBufRemaining, len, + (tDot11fIEhe_6ghz_band_cap *) + (pFrm + pIe->offset + + sizeof(tDot11fIEhe_6ghz_band_cap) * + countOffset), + append_ie); + break; + case SigIehe_cap: + status |= + dot11f_unpack_ie_he_cap( + pCtx, pBufRemaining, len, + (tDot11fIEhe_cap *) + (pFrm + pIe->offset + + sizeof(tDot11fIEhe_cap) * + countOffset), + append_ie); + break; + case SigIehe_op: + status |= + dot11f_unpack_ie_he_op( + pCtx, pBufRemaining, len, + (tDot11fIEhe_op *) + (pFrm + pIe->offset + + sizeof(tDot11fIEhe_op) * + countOffset), + append_ie); + break; + case SigIehs20vendor_ie: + status |= + dot11f_unpack_ie_hs20vendor_ie( + pCtx, pBufRemaining, len, + (tDot11fIEhs20vendor_ie *) + (pFrm + pIe->offset + + sizeof(tDot11fIEhs20vendor_ie) * + countOffset), + append_ie); + break; + case SigIeht2040_bss_coexistence: + status |= + dot11f_unpack_ie_ht2040_bss_coexistence( + pCtx, pBufRemaining, len, + (tDot11fIEht2040_bss_coexistence *) + (pFrm + pIe->offset + + sizeof(tDot11fIEht2040_bss_coexistence) * + countOffset), + append_ie); + break; + case SigIeht2040_bss_intolerant_report: + status |= + dot11f_unpack_ie_ht2040_bss_intolerant_report( + pCtx, pBufRemaining, len, + (tDot11fIEht2040_bss_intolerant_report *) + (pFrm + pIe->offset + + sizeof(tDot11fIEht2040_bss_intolerant_report) * + countOffset), + append_ie); + break; + case SigIemu_edca_param_set: + status |= + dot11f_unpack_ie_mu_edca_param_set( + pCtx, pBufRemaining, len, + (tDot11fIEmu_edca_param_set *) + (pFrm + pIe->offset + + sizeof(tDot11fIEmu_edca_param_set) * + countOffset), + append_ie); + break; + case SigIeosen_ie: + status |= + dot11f_unpack_ie_osen_ie( + pCtx, pBufRemaining, len, + (tDot11fIEosen_ie *) + (pFrm + pIe->offset + + sizeof(tDot11fIEosen_ie) * + countOffset), + append_ie); + break; + case SigIeqcn_ie: + status |= + dot11f_unpack_ie_qcn_ie( + pCtx, pBufRemaining, len, + (tDot11fIEqcn_ie *) + (pFrm + pIe->offset + + sizeof(tDot11fIEqcn_ie) * + countOffset), + append_ie); + break; + case SigIeroaming_consortium_sel: + status |= + dot11f_unpack_ie_roaming_consortium_sel( + pCtx, pBufRemaining, len, + (tDot11fIEroaming_consortium_sel *) + (pFrm + pIe->offset + + sizeof(tDot11fIEroaming_consortium_sel) * + countOffset), + append_ie); + break; + case SigIesec_chan_offset_ele: + status |= + dot11f_unpack_ie_sec_chan_offset_ele( + pCtx, pBufRemaining, len, + (tDot11fIEsec_chan_offset_ele *) + (pFrm + pIe->offset + + sizeof(tDot11fIEsec_chan_offset_ele) * + countOffset), + append_ie); + break; + case SigIevendor_vht_ie: + status |= + dot11f_unpack_ie_vendor_vht_ie( + pCtx, pBufRemaining, len, + (tDot11fIEvendor_vht_ie *) + (pFrm + pIe->offset + + sizeof(tDot11fIEvendor_vht_ie) * + countOffset), + append_ie); + break; + default: + FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR" + ": I don't know about the IE signature %d" + "-- this is most likely a 'framesc' bug.\n"), + pIe->sig); + FRAMES_DBG_BREAK(); + return DOT11F_INTERNAL_ERROR; + } + if (pIe->arraybound) + (++(*(uint16_t *)(pFrm + pIe->countOffset))); + } + } else { + FRAMES_LOG3(pCtx, FRLOG3, FRFL("Skipping unknown IE %d extn ID %d" + " (length %d)\n"), eid, extn_eid, len); + FRAMES_DUMP(pCtx, FRLOG3, pBufRemaining - 2, len); + status |= DOT11F_UNKNOWN_IES; + } + +skip_ie: + pBufRemaining += len; + + if (len > nBufRemaining) { + FRAMES_LOG0(pCtx, FRLOGW, FRFL("This IE extends past " + "the buffer as it was defined to us. This could" + "mean a corrupt frame, or just an incorrect leng" + "th parameter.\n")); + FRAMES_DBG_BREAK(); + status |= DOT11F_LAST_IE_TOO_LONG; + goto MandatoryCheck; + } + + nBufRemaining -= len; + + } + +MandatoryCheck: + pIe = &IEs[0]; + while (0xff != pIe->eid || pIe->extn_eid) { + if (pIe->fMandatory) { + pfFound = (tFRAMES_BOOL *)(pFrm + pIe->offset + + pIe->presenceOffset); + if (!*pfFound) { + FRAMES_LOG1(pCtx, FRLOGW, FRFL("ERROR: The mandato" + "ry IE %s wasn't seen.\n"), + pIe->name); + FRAMES_DBG_BREAK(); + status |= DOT11F_MANDATORY_IE_MISSING; + } + } + ++pIe; + } + + return status; +} /* End unpack_core. */ + +static uint32_t unpack_tlv_core(tpAniSirGlobal pCtx, + uint8_t *pBuf, + uint32_t nBuf, + const tTLVDefn TLVs[], + uint8_t *pFrm, + size_t nFrm) +{ + const tTLVDefn *pTlv; + uint32_t nBufRemaining, status, npec; + uint16_t id, len; + uint8_t *pBufRemaining, *pfFound; + + (void)pCtx; /* Shutup the compiler */ + (void)nFrm; + status = DOT11F_PARSE_SUCCESS; + pBufRemaining = pBuf; + nBufRemaining = nBuf; + + /* While we have data... */ + while (nBufRemaining) { + if (3 > nBufRemaining) { + FRAMES_LOG0(pCtx, FRLOGE, FRFL("This frame reports " + "fewer three byte(s) remaining.\n")); + status |= DOT11F_INCOMPLETE_TLV; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } + + npec = 0U; + + /* Look for a matching TLV definition, */ + pTlv = find_tlv_defn(pCtx, pBufRemaining, nBufRemaining, TLVs); + /* consume the type, */ + if (pTlv) { + if (pTlv->sType == 2) { + framesntohs(pCtx, &id, pBufRemaining, pTlv->fMsb); + pBufRemaining += 2; + nBufRemaining -= 2; + } else { + id = *pBufRemaining; + pBufRemaining += 1; + nBufRemaining -= 1; + } + /* & length, */ + if (pTlv->sLen == 2) { + if (2 > nBufRemaining) { + FRAMES_LOG0(pCtx, FRLOGE, FRFL("This frame reports " + "fewer two byte(s) remaining.\n")); + status |= DOT11F_INCOMPLETE_TLV; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } + framesntohs(pCtx, &len, pBufRemaining, pTlv->fMsb); + pBufRemaining += 2; + nBufRemaining -= 2; + } else { + len = *pBufRemaining; + pBufRemaining += 1; + nBufRemaining -= 1; + } + } else { + if (TLVs[0].sType > nBufRemaining) { + FRAMES_LOG0(pCtx, FRLOGE, FRFL("This frame reports " + "fewer LVs[0].sType byte(s) remaining.\n")); + status |= DOT11F_INCOMPLETE_TLV; + goto MandatoryCheck; + } + pBufRemaining += TLVs[0].sType; + nBufRemaining -= TLVs[0].sType; + if (2 > nBufRemaining) { + FRAMES_LOG0(pCtx, FRLOGE, FRFL("This frame reports " + "fewer two byte(s) remaining.\n")); + status |= DOT11F_INCOMPLETE_TLV; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } + framesntohs(pCtx, &len, pBufRemaining, (TLVs[0].sType == 2)); + pBufRemaining += 2; + nBufRemaining -= 2; + } + + if (pTlv && pTlv->pec) { + npec = 3U; + if (3 > nBufRemaining) { + FRAMES_LOG2(pCtx, FRLOGW, FRFL("TLV %d reports length" + "%d, but it has a Private Enterprise Code (3 byte" + "s.\n"), id, len); + FRAMES_DUMP(pCtx, FRLOG1, pBuf, nBuf); + FRAMES_LOG2(pCtx, FRLOG1, FRFL("We've parsed %d bytes" + "of this buffer, and show %d left.\n"), + pBufRemaining - pBuf, nBufRemaining); + status |= DOT11F_INCOMPLETE_TLV; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } + pBufRemaining += 3; + nBufRemaining -= 3; + len -= 3; + } + + /* Whether we found a hit or not, we can validate the reported */ + /* length of this TLV: */ + if (len > nBufRemaining) { + FRAMES_LOG3(pCtx, FRLOGW, FRFL("TLV %d reports length %" + "d, but there are only %d bytes remaining in this f" + "rame.\n"), id, len, nBufRemaining); + FRAMES_DUMP(pCtx, FRLOG1, pBuf, nBuf); + FRAMES_LOG2(pCtx, FRLOG1, FRFL("We've parsed %d bytes" + " of this buffer, and show %d left.\n"), + pBufRemaining - pBuf, nBufRemaining); + status |= DOT11F_INCOMPLETE_TLV; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } + + /* Now, *if* we found a hit... */ + if (pTlv) { + if (len + (pTlv->sType + pTlv->sLen) < pTlv->minSize - npec) { + FRAMES_LOG3(pCtx, FRLOGW, FRFL("The IE %s must be " + "at least %d bytes in size, but the size is only " + "%d bytes.\n"), + pTlv->name, pTlv->minSize, len); + FRAMES_DUMP(pCtx, FRLOG1, pBuf, nBuf); + status |= DOT11F_INCOMPLETE_TLV; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } + if (nBufRemaining < pTlv->minSize - npec - (pTlv->sType + pTlv->sLen)) { + FRAMES_LOG3(pCtx, FRLOGW, FRFL("The IE %s must be " + "at least %d bytes in size, but there are only " + "%d bytes remaining in this frame.\n"), + pTlv->name, pTlv->minSize, nBufRemaining); + FRAMES_DUMP(pCtx, FRLOG1, pBuf, nBuf); + status |= DOT11F_INCOMPLETE_TLV; + FRAMES_DBG_BREAK(); + goto MandatoryCheck; + } else if (len > pTlv->maxSize - npec - (pTlv->sType + pTlv->sLen)) { + FRAMES_LOG1(pCtx, FRLOGW, FRFL("The TLV %s reports " + "an illegally large size; this TLV is presumably" + "corrupt or otherwise invalid & will be skipped " + "ipped.\n"), pTlv->name); + FRAMES_DUMP(pCtx, FRLOG1, pBuf, nBuf); + FRAMES_LOG2(pCtx, FRLOG1, FRFL("We've parsed %d by" + "tes of this buffer, and show %d left.\n"), + pBufRemaining - pBuf, nBufRemaining); + FRAMES_DBG_BREAK(); + status |= DOT11F_SKIPPED_BAD_TLV; + } else { + switch (pTlv->sig) { + case SigTlvAuthorizedMACs: + status |= + dot11f_unpack_tlv_authorized_ma_cs(pCtx, + pBufRemaining, len, + (tDot11fTLVAuthorizedMACs *) + (pFrm + pTlv->offset)); + break; + case SigTlvRequestToEnroll: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVRequestToEnroll *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVRequestToEnroll *) + (pFrm + pTlv->offset))->req)); + break; + case SigTlvVersion2: + status |= + dot11f_unpack_tlv_version2(pCtx, + pBufRemaining, len, + (tDot11fTLVVersion2 *) + (pFrm + pTlv->offset)); + break; + case SigTlvAPSetupLocked: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVAPSetupLocked *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVAPSetupLocked *) + (pFrm + pTlv->offset))->fLocked)); + break; + case SigTlvAssociationState: + status |= + dot11f_unpack_tlv_common_func2(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVAssociationState *) + (pFrm + pTlv->offset))->present), + (uint16_t *)&(((tDot11fTLVAssociationState *) + (pFrm + pTlv->offset))->state)); + break; + case SigTlvConfigMethods: + status |= + dot11f_unpack_tlv_common_func2(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVConfigMethods *) + (pFrm + pTlv->offset))->present), + (uint16_t *)&(((tDot11fTLVConfigMethods *) + (pFrm + pTlv->offset))->methods)); + break; + case SigTlvConfigurationError: + status |= + dot11f_unpack_tlv_common_func2(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVConfigurationError *) + (pFrm + pTlv->offset))->present), + (uint16_t *)&(((tDot11fTLVConfigurationError *) + (pFrm + pTlv->offset))->error)); + break; + case SigTlvDeviceName: + status |= + dot11f_unpack_tlv_device_name(pCtx, + pBufRemaining, len, + (tDot11fTLVDeviceName *) + (pFrm + pTlv->offset)); + break; + case SigTlvDevicePasswordID: + status |= + dot11f_unpack_tlv_common_func2(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVDevicePasswordID *) + (pFrm + pTlv->offset))->present), + (uint16_t *)&(((tDot11fTLVDevicePasswordID *) + (pFrm + pTlv->offset))->id)); + break; + case SigTlvExtendedListenTiming: + status |= + dot11f_unpack_tlv_extended_listen_timing(pCtx, + pBufRemaining, len, + (tDot11fTLVExtendedListenTiming *) + (pFrm + pTlv->offset)); + break; + case SigTlvListenChannel: + status |= + dot11f_unpack_tlv_listen_channel(pCtx, + pBufRemaining, len, + (tDot11fTLVListenChannel *) + (pFrm + pTlv->offset)); + break; + case SigTlvManufacturer: + status |= + dot11f_unpack_tlv_manufacturer(pCtx, + pBufRemaining, len, + (tDot11fTLVManufacturer *) + (pFrm + pTlv->offset)); + break; + case SigTlvMinorReasonCode: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVMinorReasonCode *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVMinorReasonCode *) + (pFrm + pTlv->offset))->minorReasonCode)); + break; + case SigTlvModelName: + status |= + dot11f_unpack_tlv_model_name(pCtx, + pBufRemaining, len, + (tDot11fTLVModelName *) + (pFrm + pTlv->offset)); + break; + case SigTlvModelNumber: + status |= + dot11f_unpack_tlv_model_number(pCtx, + pBufRemaining, len, + (tDot11fTLVModelNumber *) + (pFrm + pTlv->offset)); + break; + case SigTlvNoticeOfAbsence: + status |= + dot11f_unpack_tlv_notice_of_absence(pCtx, + pBufRemaining, len, + (tDot11fTLVNoticeOfAbsence *) + (pFrm + pTlv->offset)); + break; + case SigTlvOperatingChannel: + status |= + dot11f_unpack_tlv_operating_channel(pCtx, + pBufRemaining, len, + (tDot11fTLVOperatingChannel *) + (pFrm + pTlv->offset)); + break; + case SigTlvP2PCapability: + status |= + dot11f_unpack_tlv_p2_p_capability(pCtx, + pBufRemaining, len, + (tDot11fTLVP2PCapability *) + (pFrm + pTlv->offset)); + break; + case SigTlvP2PDeviceId: + status |= + dot11f_unpack_tlv_p2_p_device_id(pCtx, + pBufRemaining, len, + (tDot11fTLVP2PDeviceId *) + (pFrm + pTlv->offset)); + break; + case SigTlvP2PDeviceInfo: + status |= + dot11f_unpack_tlv_p2_p_device_info(pCtx, + pBufRemaining, len, + (tDot11fTLVP2PDeviceInfo *) + (pFrm + pTlv->offset)); + break; + case SigTlvP2PGroupInfo: + status |= + dot11f_unpack_tlv_p2_p_group_info(pCtx, + pBufRemaining, len, + (tDot11fTLVP2PGroupInfo *) + (pFrm + pTlv->offset)); + break; + case SigTlvP2PStatus: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVP2PStatus *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVP2PStatus *) + (pFrm + pTlv->offset))->status)); + break; + case SigTlvPrimaryDeviceType: + status |= + dot11f_unpack_tlv_primary_device_type(pCtx, + pBufRemaining, len, + (tDot11fTLVPrimaryDeviceType *) + (pFrm + pTlv->offset)); + break; + case SigTlvRFBands: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVRFBands *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVRFBands *) + (pFrm + pTlv->offset))->bands)); + break; + case SigTlvRequestDeviceType: + status |= + dot11f_unpack_tlv_request_device_type(pCtx, + pBufRemaining, len, + (tDot11fTLVRequestDeviceType *) + (pFrm + pTlv->offset)); + break; + case SigTlvRequestType: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVRequestType *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVRequestType *) + (pFrm + pTlv->offset))->reqType)); + break; + case SigTlvResponseType: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVResponseType *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVResponseType *) + (pFrm + pTlv->offset))->resType)); + break; + case SigTlvSelectedRegistrar: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVSelectedRegistrar *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVSelectedRegistrar *) + (pFrm + pTlv->offset))->selected)); + break; + case SigTlvSelectedRegistrarConfigMethods: + status |= + dot11f_unpack_tlv_common_func2(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVSelectedRegistrarConfigMethods *) + (pFrm + pTlv->offset))->present), + (uint16_t *)&(((tDot11fTLVSelectedRegistrarConfigMethods *) + (pFrm + pTlv->offset))->methods)); + break; + case SigTlvSerialNumber: + status |= + dot11f_unpack_tlv_serial_number(pCtx, + pBufRemaining, len, + (tDot11fTLVSerialNumber *) + (pFrm + pTlv->offset)); + break; + case SigTlvUUID_E: + status |= + dot11f_unpack_tlv_uuid_e(pCtx, + pBufRemaining, len, + (tDot11fTLVUUID_E *) + (pFrm + pTlv->offset)); + break; + case SigTlvUUID_R: + status |= + dot11f_unpack_tlv_uuid_r(pCtx, + pBufRemaining, len, + (tDot11fTLVUUID_R *) + (pFrm + pTlv->offset)); + break; + case SigTlvVendorExtension: + status |= + dot11f_unpack_tlv_vendor_extension(pCtx, + pBufRemaining, len, + (tDot11fTLVVendorExtension *) + (pFrm + pTlv->offset)); + break; + case SigTlvVersion: + status |= + dot11f_unpack_tlv_version(pCtx, + pBufRemaining, len, + (tDot11fTLVVersion *) + (pFrm + pTlv->offset)); + break; + case SigTlvWPSState: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVWPSState *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVWPSState *) + (pFrm + pTlv->offset))->state)); + break; + case SigTlvassoc_disallowed: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVassoc_disallowed *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVassoc_disallowed *) + (pFrm + pTlv->offset))->reason_code)); + break; + case SigTlvassoc_retry_delay: + status |= + dot11f_unpack_tlv_common_func2(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVassoc_retry_delay *) + (pFrm + pTlv->offset))->present), + (uint16_t *)&(((tDot11fTLVassoc_retry_delay *) + (pFrm + pTlv->offset))->delay)); + break; + case SigTlvcellular_data_cap: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVcellular_data_cap *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVcellular_data_cap *) + (pFrm + pTlv->offset))->cellular_connectivity)); + break; + case SigTlvcellular_data_con_pref: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVcellular_data_con_pref *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVcellular_data_con_pref *) + (pFrm + pTlv->offset))->cellular_preference)); + break; + case SigTlvmbo_ap_cap: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVmbo_ap_cap *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVmbo_ap_cap *) + (pFrm + pTlv->offset))->mbo_cap_ind)); + break; + case SigTlvnon_prefferd_chan_rep: + status |= + dot11f_unpack_tlv_non_prefferd_chan_rep(pCtx, + pBufRemaining, len, + (tDot11fTLVnon_prefferd_chan_rep *) + (pFrm + pTlv->offset)); + break; + case SigTlvoce_cap: + status |= + dot11f_unpack_tlv_oce_cap(pCtx, + pBufRemaining, len, + (tDot11fTLVoce_cap *) + (pFrm + pTlv->offset)); + break; + case SigTlvreduced_wan_metrics: + status |= + dot11f_unpack_tlv_reduced_wan_metrics(pCtx, + pBufRemaining, len, + (tDot11fTLVreduced_wan_metrics *) + (pFrm + pTlv->offset)); + break; + case SigTlvrssi_assoc_rej: + status |= + dot11f_unpack_tlv_rssi_assoc_rej(pCtx, + pBufRemaining, len, + (tDot11fTLVrssi_assoc_rej *) + (pFrm + pTlv->offset)); + break; + case SigTlvtransition_reason: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVtransition_reason *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVtransition_reason *) + (pFrm + pTlv->offset))->transition_reason_code)); + break; + case SigTlvtransition_reject_reason: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVtransition_reject_reason *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVtransition_reject_reason *) + (pFrm + pTlv->offset))->transition_reject_code)); + break; + case SigTlvP2PInterface: + status |= + dot11f_unpack_tlv_p2_p_interface(pCtx, + pBufRemaining, len, + (tDot11fTLVP2PInterface *) + (pFrm + pTlv->offset)); + break; + case SigTlvP2PManageability: + status |= + dot11f_unpack_tlv_common_func(pCtx, + pBufRemaining, len, + (uint8_t *)&(((tDot11fTLVP2PManageability *) + (pFrm + pTlv->offset))->present), + (uint8_t *)&(((tDot11fTLVP2PManageability *) + (pFrm + pTlv->offset))->manageability)); + break; + default: + FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR: I" + " don't know about the TLV signature %d-- thi" + "s is most likely a 'framesc' bug.\n"), + pTlv->sig); + FRAMES_DBG_BREAK(); + return DOT11F_INTERNAL_ERROR; + } /* End switch on sig. */ + } /* End if on length check. */ + + } else { + FRAMES_LOG2(pCtx, FRLOG3, FRFL("Skipping unknown TLV %d (" + "length %d)\n"), id, len); + FRAMES_DUMP(pCtx, FRLOG3, pBufRemaining - (pTlv->sType + pTlv->sLen), len); + status |= DOT11F_UNKNOWN_TLVS; + } + + /* Advance to the next TLV */ + pBufRemaining += len; + + if (len > nBufRemaining) { + FRAMES_LOG0(pCtx, FRLOGW, FRFL("This TLV extends past th" + "e buffer as it was defined to us. This could mean " + "a corrupt frame, or just an incorrect length parame" + "ter.\n")); + FRAMES_DBG_BREAK(); + status |= DOT11F_LAST_TLV_TOO_LONG; + goto MandatoryCheck; + } + + nBufRemaining -= len; + + } /* End iteration over TLVs.*/ + +MandatoryCheck: + pTlv = &TLVs[0]; + while (0xffff != pTlv->id) { + if (pTlv->fMandatory) { + pfFound = (uint8_t *)(pFrm + pTlv->offset + + pTlv->presenceOffset); + if (!*pfFound) { + FRAMES_LOG1(pCtx, FRLOGW, FRFL("ERROR: The mandatory " + "TLV %s wasn't seen.\n"), + pTlv->name); + FRAMES_DBG_BREAK(); + status |= DOT11F_MANDATORY_TLV_MISSING; + } + + } + ++pTlv; + } + + return status; +} /* End UnpacTlvkCore. */ +uint32_t dot11f_get_packed_ietclas(tpAniSirGlobal pCtx, + tDot11fIETCLAS *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + switch (pIe->classifier_type) { + case 0: + *pnNeeded += 6; + *pnNeeded += 6; + *pnNeeded += 2; + break; + case 1: + *pnNeeded += 1; + switch (pIe->info.IpParams.version) { + case 4: + *pnNeeded += 4; + *pnNeeded += 4; + *pnNeeded += 2; + *pnNeeded += 2; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + break; + case 6: + *pnNeeded += 16; + *pnNeeded += 16; + *pnNeeded += 2; + *pnNeeded += 2; + *pnNeeded += 3; + break; + } + break; + case 2: + *pnNeeded += 2; + break; + } + break; + } + return status; +} /* End dot11f_get_packed_ietclas. */ + +uint32_t dot11f_get_packed_iewmmtclas(tpAniSirGlobal pCtx, + tDot11fIEWMMTCLAS *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + switch (pIe->classifier_type) { + case 0: + *pnNeeded += 6; + *pnNeeded += 6; + *pnNeeded += 2; + break; + case 1: + *pnNeeded += 1; + switch (pIe->info.IpParams.version) { + case 4: + *pnNeeded += 4; + *pnNeeded += 4; + *pnNeeded += 2; + *pnNeeded += 2; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + break; + case 6: + *pnNeeded += 16; + *pnNeeded += 16; + *pnNeeded += 2; + *pnNeeded += 2; + *pnNeeded += 3; + break; + } + break; + case 2: + *pnNeeded += 2; + break; + } + break; + } + return status; +} /* End dot11f_get_packed_iewmmtclas. */ + +uint32_t dot11f_get_packed_ie_neighbor_rpt(tpAniSirGlobal pCtx, + tDot11fIEneighbor_rpt *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 6; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 2; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, + IES_neighbor_rpt); + break; + } + return status; +} /* End dot11f_get_packed_ie_neighbor_rpt. */ + +uint32_t dot11f_get_packed_ie_channel_switch_wrapper(tpAniSirGlobal pCtx, + tDot11fIEChannelSwitchWrapper *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, + IES_ChannelSwitchWrapper); + break; + } + return status; +} /* End dot11f_get_packed_ie_channel_switch_wrapper. */ + +uint32_t dot11f_get_packed_ie_country(tpAniSirGlobal pCtx, + tDot11fIECountry *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 3; + if (pIe->num_triplets) { + *pnNeeded += (pIe->num_triplets * 3); + } else { + break; + } + break; + } + return status; +} /* End dot11f_get_packed_ie_country. */ + +uint32_t dot11f_get_packed_ieft_info(tpAniSirGlobal pCtx, + tDot11fIEFTInfo *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 2; + *pnNeeded += 16; + *pnNeeded += 32; + *pnNeeded += 32; + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, + IES_FTInfo); + break; + } + return status; +} /* End dot11f_get_packed_ieft_info. */ + +uint32_t dot11f_get_packed_ie_MBO_IE(tpAniSirGlobal pCtx, + tDot11fIEMBO_IE *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_MBO_IE); + break; + } + return status; +} /* End dot11f_get_packed_ie_MBO_IE. */ + +uint32_t dot11f_get_packed_ie_measurement_report(tpAniSirGlobal pCtx, + tDot11fIEMeasurementReport *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + if (pIe->type) { + switch (pIe->type) { + case 0: + *pnNeeded += 1; + *pnNeeded += 8; + *pnNeeded += 2; + *pnNeeded += 1; + break; + case 1: + *pnNeeded += 1; + *pnNeeded += 8; + *pnNeeded += 2; + *pnNeeded += 1; + break; + case 2: + *pnNeeded += 1; + *pnNeeded += 8; + *pnNeeded += 2; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + break; + case 5: + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 8; + *pnNeeded += 2; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 6; + *pnNeeded += 1; + *pnNeeded += 4; + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, IES_reportBeacon); + break; + } + } else { + break; + } + break; + } + return status; +} /* End dot11f_get_packed_ie_measurement_report. */ + +uint32_t dot11f_get_packed_ie_measurement_request(tpAniSirGlobal pCtx, + tDot11fIEMeasurementRequest *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + switch (pIe->measurement_type) { + case 0: + *pnNeeded += 1; + *pnNeeded += 8; + *pnNeeded += 2; + break; + case 1: + *pnNeeded += 1; + *pnNeeded += 8; + *pnNeeded += 2; + break; + case 2: + *pnNeeded += 1; + *pnNeeded += 8; + *pnNeeded += 2; + break; + case 5: + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 2; + *pnNeeded += 2; + *pnNeeded += 1; + *pnNeeded += 6; + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, IES_measurement_requestBeacon); + break; + case 8: + *pnNeeded += 1; + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, IES_measurement_requestlci); + break; + case 16: + *pnNeeded += 2; + *pnNeeded += 1; + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, IES_measurement_requestftmrr); + break; + } + break; + } + return status; +} /* End dot11f_get_packed_ie_measurement_request. */ + +uint32_t dot11f_get_packed_ie_neighbor_report(tpAniSirGlobal pCtx, + tDot11fIENeighborReport *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 6; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 2; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, + IES_NeighborReport); + break; + } + return status; +} /* End dot11f_get_packed_ie_neighbor_report. */ + +uint32_t dot11f_get_packed_iep2_p_assoc_req(tpAniSirGlobal pCtx, + tDot11fIEP2PAssocReq *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_P2PAssocReq); + break; + } + return status; +} /* End dot11f_get_packed_iep2_p_assoc_req. */ + +uint32_t dot11f_get_packed_iep2_p_assoc_res(tpAniSirGlobal pCtx, + tDot11fIEP2PAssocRes *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_P2PAssocRes); + break; + } + return status; +} /* End dot11f_get_packed_iep2_p_assoc_res. */ + +uint32_t dot11f_get_packed_iep2_p_beacon(tpAniSirGlobal pCtx, + tDot11fIEP2PBeacon *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_P2PBeacon); + break; + } + return status; +} /* End dot11f_get_packed_iep2_p_beacon. */ + +uint32_t dot11f_get_packed_iep2_p_beacon_probe_res(tpAniSirGlobal pCtx, + tDot11fIEP2PBeaconProbeRes *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_P2PBeaconProbeRes); + break; + } + return status; +} /* End dot11f_get_packed_iep2_p_beacon_probe_res. */ + +uint32_t dot11f_get_packed_iep2_p_de_auth(tpAniSirGlobal pCtx, + tDot11fIEP2PDeAuth *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_P2PDeAuth); + break; + } + return status; +} /* End dot11f_get_packed_iep2_p_de_auth. */ + +uint32_t dot11f_get_packed_iep2_p_dis_assoc(tpAniSirGlobal pCtx, + tDot11fIEP2PDisAssoc *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_P2PDisAssoc); + break; + } + return status; +} /* End dot11f_get_packed_iep2_p_dis_assoc. */ + +uint32_t dot11f_get_packed_iep2_p_probe_req(tpAniSirGlobal pCtx, + tDot11fIEP2PProbeReq *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_P2PProbeReq); + break; + } + return status; +} /* End dot11f_get_packed_iep2_p_probe_req. */ + +uint32_t dot11f_get_packed_iep2_p_probe_res(tpAniSirGlobal pCtx, + tDot11fIEP2PProbeRes *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_P2PProbeRes); + break; + } + return status; +} /* End dot11f_get_packed_iep2_p_probe_res. */ + +uint32_t dot11f_get_packed_ieric_data_desc(tpAniSirGlobal pCtx, + tDot11fIERICDataDesc *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, + IES_RICDataDesc); + break; + } + return status; +} /* End dot11f_get_packed_ieric_data_desc. */ + +uint32_t dot11f_get_packed_iersn(tpAniSirGlobal pCtx, + tDot11fIERSN *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 2; + if (pIe->gp_cipher_suite_present) { + + *pnNeeded += 4; + } else { + break; + } + if (pIe->pwise_cipher_suite_count) { + *pnNeeded += 2; + } else { + break; + } + *pnNeeded += (pIe->pwise_cipher_suite_count * 4); + if (pIe->akm_suite_cnt) { + *pnNeeded += 2; + } else { + break; + } + *pnNeeded += (pIe->akm_suite_cnt * 4); + if (pIe->RSN_Cap_present) { + + *pnNeeded += 2; + } else { + break; + } + if (pIe->pmkid_count) { + *pnNeeded += 2; + } else { + if (pIe->gp_mgmt_cipher_suite_present) { + *pnNeeded += 2; + } + } + *pnNeeded += (pIe->pmkid_count * 16); + if (pIe->gp_mgmt_cipher_suite_present) { + + *pnNeeded += 4; + } else { + break; + } + break; + } + return status; +} /* End dot11f_get_packed_iersn. */ + +uint32_t dot11f_get_packed_iewapi(tpAniSirGlobal pCtx, + tDot11fIEWAPI *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 2; + *pnNeeded += 2; + *pnNeeded += (pIe->akm_suite_count * 4); + *pnNeeded += 2; + *pnNeeded += (pIe->unicast_cipher_suite_count * 4); + *pnNeeded += 4; + *pnNeeded += 2; + if (pIe->bkid_count) { + *pnNeeded += 2; + } else { + break; + } + *pnNeeded += (pIe->bkid_count * 16); + break; + } + return status; +} /* End dot11f_get_packed_iewapi. */ + +uint32_t dot11f_get_packed_iewpa(tpAniSirGlobal pCtx, + tDot11fIEWPA *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 2; + if (pIe->multicast_cipher_present) { + + *pnNeeded += 4; + } else { + break; + } + if (pIe->unicast_cipher_count) { + *pnNeeded += 2; + } else { + break; + } + *pnNeeded += (pIe->unicast_cipher_count * 4); + if (pIe->auth_suite_count) { + *pnNeeded += 2; + } else { + break; + } + *pnNeeded += (pIe->auth_suite_count * 4); + if (pIe->caps) { + *pnNeeded += 2; + } else { + break; + } + break; + } + return status; +} /* End dot11f_get_packed_iewpa. */ + +uint32_t dot11f_get_packed_iewsc(tpAniSirGlobal pCtx, + tDot11fIEWSC *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_WSC); + break; + } + return status; +} /* End dot11f_get_packed_iewsc. */ + +uint32_t dot11f_get_packed_ie_wsc_assoc_req(tpAniSirGlobal pCtx, + tDot11fIEWscAssocReq *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_WscAssocReq); + break; + } + return status; +} /* End dot11f_get_packed_ie_wsc_assoc_req. */ + +uint32_t dot11f_get_packed_ie_wsc_assoc_res(tpAniSirGlobal pCtx, + tDot11fIEWscAssocRes *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_WscAssocRes); + break; + } + return status; +} /* End dot11f_get_packed_ie_wsc_assoc_res. */ + +uint32_t dot11f_get_packed_ie_wsc_beacon(tpAniSirGlobal pCtx, + tDot11fIEWscBeacon *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_WscBeacon); + break; + } + return status; +} /* End dot11f_get_packed_ie_wsc_beacon. */ + +uint32_t dot11f_get_packed_ie_wsc_beacon_probe_res(tpAniSirGlobal pCtx, + tDot11fIEWscBeaconProbeRes *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_WscBeaconProbeRes); + break; + } + return status; +} /* End dot11f_get_packed_ie_wsc_beacon_probe_res. */ + +uint32_t dot11f_get_packed_ie_wsc_probe_req(tpAniSirGlobal pCtx, + tDot11fIEWscProbeReq *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_WscProbeReq); + break; + } + return status; +} /* End dot11f_get_packed_ie_wsc_probe_req. */ + +uint32_t dot11f_get_packed_ie_wsc_probe_res(tpAniSirGlobal pCtx, + tDot11fIEWscProbeRes *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_WscProbeRes); + break; + } + return status; +} /* End dot11f_get_packed_ie_wsc_probe_res. */ + +uint32_t dot11f_get_packed_ie_wsc_reassoc_res(tpAniSirGlobal pCtx, + tDot11fIEWscReassocRes *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pIe, pnNeeded, + TLVS_WscReassocRes); + break; + } + return status; +} /* End dot11f_get_packed_ie_wsc_reassoc_res. */ + +uint32_t dot11f_get_packed_ie_he_cap(tpAniSirGlobal pCtx, + tDot11fIEhe_cap *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 4; + *pnNeeded += 2; + *pnNeeded += 4; + *pnNeeded += 4; + *pnNeeded += 2; + *pnNeeded += 1; + *pnNeeded += 2; + *pnNeeded += 2; + *pnNeeded += (pIe->chan_width_2 * 2); + *pnNeeded += (pIe->chan_width_2 * 2); + *pnNeeded += (pIe->chan_width_3 * 2); + *pnNeeded += (pIe->chan_width_3 * 2); + if (pIe->ppet_present) { + switch (pIe->ppet_present) { + case 1: + *pnNeeded += pIe->ppet.ppe_threshold.num_ppe_th; + break; + } + } else { + break; + } + break; + } + return status; +} /* End dot11f_get_packed_ie_he_cap. */ + +uint32_t dot11f_get_packed_ie_he_op(tpAniSirGlobal pCtx, + tDot11fIEhe_op *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 2; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 2; + if (pIe->vht_oper_present) { + switch (pIe->vht_oper_present) { + case 1: + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + break; + } + } + if (pIe->co_located_bss) { + switch (pIe->co_located_bss) { + case 1: + *pnNeeded += 1; + break; + } + } + if (pIe->oper_info_6g_present) { + switch (pIe->oper_info_6g_present) { + case 1: + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + *pnNeeded += 1; + break; + } + } + break; + } + return status; +} /* End dot11f_get_packed_ie_he_op. */ + +uint32_t dot11f_get_packed_ie_hs20vendor_ie(tpAniSirGlobal pCtx, + tDot11fIEhs20vendor_ie *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 1; + if (pIe->hs_id_present) { + switch (pIe->hs_id_present) { + case 1: + *pnNeeded += 2; + break; + case 2: + *pnNeeded += 2; + break; + } + } else { + break; + } + break; + } + return status; +} /* End dot11f_get_packed_ie_hs20vendor_ie. */ + +uint32_t dot11f_get_packed_ie_qcn_ie(tpAniSirGlobal pCtx, + tDot11fIEqcn_ie *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, + IES_qcn_ie); + break; + } + return status; +} /* End dot11f_get_packed_ie_qcn_ie. */ + +uint32_t dot11f_get_packed_ie_vendor_vht_ie(tpAniSirGlobal pCtx, + tDot11fIEvendor_vht_ie *pIe, uint32_t *pnNeeded) +{ + uint32_t status = DOT11F_PARSE_SUCCESS; + (void)pCtx; + while (pIe->present) { + *pnNeeded += 1; + status = get_packed_size_core(pCtx, (uint8_t *)pIe, pnNeeded, + IES_vendor_vht_ie); + break; + } + return status; +} /* End dot11f_get_packed_ie_vendor_vht_ie. */ + +uint32_t dot11f_get_packed_add_ts_request_size(tpAniSirGlobal pCtx, + tDot11fAddTSRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_AddTSRequest); + return status; +} /* End dot11f_get_packed_add_ts_request_size. */ + +uint32_t dot11f_get_packed_add_ts_response_size(tpAniSirGlobal pCtx, + tDot11fAddTSResponse *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 5; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_AddTSResponse); + return status; +} /* End dot11f_get_packed_add_ts_response_size. */ + +uint32_t dot11f_get_packed_assoc_request_size(tpAniSirGlobal pCtx, + tDot11fAssocRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 4; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_AssocRequest); + return status; +} /* End dot11f_get_packed_assoc_request_size. */ + +uint32_t dot11f_get_packed_assoc_response_size(tpAniSirGlobal pCtx, + tDot11fAssocResponse *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 6; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_AssocResponse); + return status; +} /* End dot11f_get_packed_assoc_response_size. */ + +uint32_t dot11f_get_packed_authentication_size(tpAniSirGlobal pCtx, + tDot11fAuthentication *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 6; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_Authentication); + return status; +} /* End dot11f_get_packed_authentication_size. */ + +uint32_t dot11f_get_packed_beacon_size(tpAniSirGlobal pCtx, + tDot11fBeacon *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 12; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_Beacon); + return status; +} /* End dot11f_get_packed_beacon_size. */ + +uint32_t dot11f_get_packed_beacon1_size(tpAniSirGlobal pCtx, + tDot11fBeacon1 *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 12; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_Beacon1); + return status; +} /* End dot11f_get_packed_beacon1_size. */ + +uint32_t dot11f_get_packed_beacon2_size(tpAniSirGlobal pCtx, + tDot11fBeacon2 *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 0; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_Beacon2); + return status; +} /* End dot11f_get_packed_beacon2_size. */ + +uint32_t dot11f_get_packed_beacon_i_es_size(tpAniSirGlobal pCtx, + tDot11fBeaconIEs *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 0; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_BeaconIEs); + return status; +} /* End dot11f_get_packed_beacon_i_es_size. */ + +uint32_t dot11f_get_packed_channel_switch_size(tpAniSirGlobal pCtx, + tDot11fChannelSwitch *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 2; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_ChannelSwitch); + return status; +} /* End dot11f_get_packed_channel_switch_size. */ + +uint32_t dot11f_get_packed_de_auth_size(tpAniSirGlobal pCtx, + tDot11fDeAuth *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 2; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_DeAuth); + return status; +} /* End dot11f_get_packed_de_auth_size. */ + +uint32_t dot11f_get_packed_del_ts_size(tpAniSirGlobal pCtx, + tDot11fDelTS *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 7; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_DelTS); + return status; +} /* End dot11f_get_packed_del_ts_size. */ + +uint32_t dot11f_get_packed_disassociation_size(tpAniSirGlobal pCtx, + tDot11fDisassociation *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 2; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_Disassociation); + return status; +} /* End dot11f_get_packed_disassociation_size. */ + +uint32_t dot11f_get_packed_link_measurement_report_size(tpAniSirGlobal pCtx, + tDot11fLinkMeasurementReport *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 11; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_LinkMeasurementReport); + return status; +} /* End dot11f_get_packed_link_measurement_report_size. */ + +uint32_t dot11f_get_packed_link_measurement_request_size(tpAniSirGlobal pCtx, + tDot11fLinkMeasurementRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 5; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_LinkMeasurementRequest); + return status; +} /* End dot11f_get_packed_link_measurement_request_size. */ + +uint32_t dot11f_get_packed_measurement_report_size(tpAniSirGlobal pCtx, + tDot11fMeasurementReport *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_MeasurementReport); + return status; +} /* End dot11f_get_packed_measurement_report_size. */ + +uint32_t dot11f_get_packed_measurement_request_size(tpAniSirGlobal pCtx, + tDot11fMeasurementRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_MeasurementRequest); + return status; +} /* End dot11f_get_packed_measurement_request_size. */ + +uint32_t dot11f_get_packed_neighbor_report_request_size(tpAniSirGlobal pCtx, + tDot11fNeighborReportRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_NeighborReportRequest); + return status; +} /* End dot11f_get_packed_neighbor_report_request_size. */ + +uint32_t dot11f_get_packed_neighbor_report_response_size(tpAniSirGlobal pCtx, + tDot11fNeighborReportResponse *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_NeighborReportResponse); + return status; +} /* End dot11f_get_packed_neighbor_report_response_size. */ + +uint32_t dot11f_get_packed_operating_mode_size(tpAniSirGlobal pCtx, + tDot11fOperatingMode *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_OperatingMode); + return status; +} /* End dot11f_get_packed_operating_mode_size. */ + +uint32_t dot11f_get_packed_probe_request_size(tpAniSirGlobal pCtx, + tDot11fProbeRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 0; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_ProbeRequest); + return status; +} /* End dot11f_get_packed_probe_request_size. */ + +uint32_t dot11f_get_packed_probe_response_size(tpAniSirGlobal pCtx, + tDot11fProbeResponse *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 12; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_ProbeResponse); + return status; +} /* End dot11f_get_packed_probe_response_size. */ + +uint32_t dot11f_get_packed_qos_map_configure_size(tpAniSirGlobal pCtx, + tDot11fQosMapConfigure *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 2; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_QosMapConfigure); + return status; +} /* End dot11f_get_packed_qos_map_configure_size. */ + +uint32_t dot11f_get_packed_radio_measurement_report_size(tpAniSirGlobal pCtx, + tDot11fRadioMeasurementReport *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_RadioMeasurementReport); + return status; +} /* End dot11f_get_packed_radio_measurement_report_size. */ + +uint32_t dot11f_get_packed_radio_measurement_request_size(tpAniSirGlobal pCtx, + tDot11fRadioMeasurementRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 5; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_RadioMeasurementRequest); + return status; +} /* End dot11f_get_packed_radio_measurement_request_size. */ + +uint32_t dot11f_get_packed_re_assoc_request_size(tpAniSirGlobal pCtx, + tDot11fReAssocRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 10; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_ReAssocRequest); + return status; +} /* End dot11f_get_packed_re_assoc_request_size. */ + +uint32_t dot11f_get_packed_re_assoc_response_size(tpAniSirGlobal pCtx, + tDot11fReAssocResponse *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 6; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_ReAssocResponse); + return status; +} /* End dot11f_get_packed_re_assoc_response_size. */ + +uint32_t dot11f_get_packed_sm_power_save_size(tpAniSirGlobal pCtx, + tDot11fSMPowerSave *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_SMPowerSave); + return status; +} /* End dot11f_get_packed_sm_power_save_size. */ + +uint32_t dot11f_get_packed_sa_query_req_size(tpAniSirGlobal pCtx, + tDot11fSaQueryReq *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 4; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_SaQueryReq); + return status; +} /* End dot11f_get_packed_sa_query_req_size. */ + +uint32_t dot11f_get_packed_sa_query_rsp_size(tpAniSirGlobal pCtx, + tDot11fSaQueryRsp *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 4; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_SaQueryRsp); + return status; +} /* End dot11f_get_packed_sa_query_rsp_size. */ + +uint32_t dot11f_get_packed_tdls_dis_req_size(tpAniSirGlobal pCtx, + tDot11fTDLSDisReq *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TDLSDisReq); + return status; +} /* End dot11f_get_packed_tdls_dis_req_size. */ + +uint32_t dot11f_get_packed_tdls_dis_rsp_size(tpAniSirGlobal pCtx, + tDot11fTDLSDisRsp *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 5; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TDLSDisRsp); + return status; +} /* End dot11f_get_packed_tdls_dis_rsp_size. */ + +uint32_t dot11f_get_packed_tdls_peer_traffic_ind_size(tpAniSirGlobal pCtx, + tDot11fTDLSPeerTrafficInd *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TDLSPeerTrafficInd); + return status; +} /* End dot11f_get_packed_tdls_peer_traffic_ind_size. */ + +uint32_t dot11f_get_packed_tdls_peer_traffic_rsp_size(tpAniSirGlobal pCtx, + tDot11fTDLSPeerTrafficRsp *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TDLSPeerTrafficRsp); + return status; +} /* End dot11f_get_packed_tdls_peer_traffic_rsp_size. */ + +uint32_t dot11f_get_packed_tdls_setup_cnf_size(tpAniSirGlobal pCtx, + tDot11fTDLSSetupCnf *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 5; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TDLSSetupCnf); + return status; +} /* End dot11f_get_packed_tdls_setup_cnf_size. */ + +uint32_t dot11f_get_packed_tdls_setup_req_size(tpAniSirGlobal pCtx, + tDot11fTDLSSetupReq *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 5; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TDLSSetupReq); + return status; +} /* End dot11f_get_packed_tdls_setup_req_size. */ + +uint32_t dot11f_get_packed_tdls_setup_rsp_size(tpAniSirGlobal pCtx, + tDot11fTDLSSetupRsp *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 7; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TDLSSetupRsp); + return status; +} /* End dot11f_get_packed_tdls_setup_rsp_size. */ + +uint32_t dot11f_get_packed_tdls_teardown_size(tpAniSirGlobal pCtx, + tDot11fTDLSTeardown *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 4; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TDLSTeardown); + return status; +} /* End dot11f_get_packed_tdls_teardown_size. */ + +uint32_t dot11f_get_packed_tpc_report_size(tpAniSirGlobal pCtx, + tDot11fTPCReport *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TPCReport); + return status; +} /* End dot11f_get_packed_tpc_report_size. */ + +uint32_t dot11f_get_packed_tpc_request_size(tpAniSirGlobal pCtx, + tDot11fTPCRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 3; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TPCRequest); + return status; +} /* End dot11f_get_packed_tpc_request_size. */ + +uint32_t dot11f_get_packed_timing_advertisement_frame_size(tpAniSirGlobal pCtx, + tDot11fTimingAdvertisementFrame *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 10; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_TimingAdvertisementFrame); + return status; +} /* End dot11f_get_packed_timing_advertisement_frame_size. */ + +uint32_t dot11f_get_packed_vht_gid_management_action_frame_size(tpAniSirGlobal pCtx, + tDot11fVHTGidManagementActionFrame *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 26; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_VHTGidManagementActionFrame); + return status; +} /* End dot11f_get_packed_vht_gid_management_action_frame_size. */ + +uint32_t dot11f_get_packed_wmm_add_ts_request_size(tpAniSirGlobal pCtx, + tDot11fWMMAddTSRequest *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 4; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_WMMAddTSRequest); + return status; +} /* End dot11f_get_packed_wmm_add_ts_request_size. */ + +uint32_t dot11f_get_packed_wmm_add_ts_response_size(tpAniSirGlobal pCtx, + tDot11fWMMAddTSResponse *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 4; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_WMMAddTSResponse); + return status; +} /* End dot11f_get_packed_wmm_add_ts_response_size. */ + +uint32_t dot11f_get_packed_wmm_del_ts_size(tpAniSirGlobal pCtx, + tDot11fWMMDelTS *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 4; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_WMMDelTS); + return status; +} /* End dot11f_get_packed_wmm_del_ts_size. */ + +uint32_t dot11f_get_packed_addba_req_size(tpAniSirGlobal pCtx, + tDot11faddba_req *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 9; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_addba_req); + return status; +} /* End dot11f_get_packed_addba_req_size. */ + +uint32_t dot11f_get_packed_addba_rsp_size(tpAniSirGlobal pCtx, + tDot11faddba_rsp *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 9; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_addba_rsp); + return status; +} /* End dot11f_get_packed_addba_rsp_size. */ + +uint32_t dot11f_get_packed_delba_req_size(tpAniSirGlobal pCtx, + tDot11fdelba_req *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 6; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_delba_req); + return status; +} /* End dot11f_get_packed_delba_req_size. */ + +uint32_t dot11f_get_packed_ext_channel_switch_action_frame_size(tpAniSirGlobal pCtx, + tDot11fext_channel_switch_action_frame *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 6; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_ext_channel_switch_action_frame); + return status; +} /* End dot11f_get_packed_ext_channel_switch_action_frame_size. */ + +uint32_t dot11f_get_packed_ht2040_bss_coexistence_mgmt_action_frameSize(tpAniSirGlobal pCtx, + tDot11fht2040_bss_coexistence_mgmt_action_frame *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 2; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_ht2040_bss_coexistence_mgmt_action_frame); + return status; +} /* End dot11f_get_packed_ht2040_bss_coexistence_mgmt_action_frameSize. */ + +uint32_t dot11f_get_packed_p2p_oper_chan_change_confirmSize(tpAniSirGlobal pCtx, + tDot11fp2p_oper_chan_change_confirm *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 7; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_p2p_oper_chan_change_confirm); + return status; +} /* End dot11f_get_packed_p2p_oper_chan_change_confirmSize. */ + +uint32_t dot11f_get_packed_vendor_action_frameSize(tpAniSirGlobal pCtx, + tDot11fvendor_action_frame *pFrm, uint32_t *pnNeeded) +{ + uint32_t status = 0; + *pnNeeded = 5; + status = get_packed_size_core(pCtx, (uint8_t *)pFrm, pnNeeded, + IES_vendor_action_frame); + return status; +} /* End dot11f_get_packed_vendor_action_frameSize. */ + +static uint32_t get_packed_size_core(tpAniSirGlobal pCtx, + uint8_t *pFrm, + uint32_t *pnNeeded, + const tIEDefn IEs[]) +{ + const tIEDefn *pIe; + uint16_t i, n; + uint32_t status; + tFRAMES_BOOL *pfFound; + uint32_t countOffset = 0; + uint32_t byteCount = 0; + uint8_t pIePresent = 0; + uint32_t offset = 0; + + status = DOT11F_PARSE_SUCCESS; + + (void)pCtx; /* Shutup the compiler if we have no FFs nor IEs... */ + i = 0; + n = 0; + pIe = &(IEs[0]); + while (0xff != pIe->eid || pIe->extn_eid) { + pfFound = (tFRAMES_BOOL *)(pFrm + pIe->offset + + pIe->presenceOffset); + if (*pfFound) { + countOffset = ((0 == pIe->arraybound) ? 1 : (*(uint16_t *)(pFrm + pIe->countOffset))); + for (i = 0U; i < countOffset; ++i) { + *pnNeeded += 2U + pIe->noui; + if (pIe->extn_eid) + (*pnNeeded)++; + byteCount = 0; + switch (pIe->sig) { + case SigIeGTK: + offset = sizeof(tDot11fIEGTK); + byteCount = ((tDot11fIEGTK *) + (pFrm + pIe->offset + offset * i))-> + num_key + 11; + pIePresent = ((tDot11fIEGTK *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeIGTK: + offset = sizeof(tDot11fIEIGTK); + byteCount = 33; + pIePresent = ((tDot11fIEIGTK *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeR0KH_ID: + offset = sizeof(tDot11fIER0KH_ID); + byteCount = ((tDot11fIER0KH_ID *) + (pFrm + pIe->offset + offset * i))-> + num_PMK_R0_ID; + pIePresent = ((tDot11fIER0KH_ID *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeR1KH_ID: + offset = sizeof(tDot11fIER1KH_ID); + byteCount = 6; + pIePresent = ((tDot11fIER1KH_ID *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeversion_attr: + offset = sizeof(tDot11fIEversion_attr); + byteCount = 2; + pIePresent = ((tDot11fIEversion_attr *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIevht_mcs11_attr: + offset = sizeof(tDot11fIEvht_mcs11_attr); + byteCount = 1; + pIePresent = ((tDot11fIEvht_mcs11_attr *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeAPChannelReport: + offset = sizeof(tDot11fIEAPChannelReport); + byteCount = ((tDot11fIEAPChannelReport *) + (pFrm + pIe->offset + offset * i))-> + num_channelList + 1; + pIePresent = ((tDot11fIEAPChannelReport *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeBcnReportingDetail: + offset = sizeof(tDot11fIEBcnReportingDetail); + byteCount = 1; + pIePresent = ((tDot11fIEBcnReportingDetail *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeBeaconReportFrmBody: + offset = sizeof(tDot11fIEBeaconReportFrmBody); + byteCount = ((tDot11fIEBeaconReportFrmBody *) + (pFrm + pIe->offset + offset * i))-> + num_reportedFields; + pIePresent = ((tDot11fIEBeaconReportFrmBody *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeBeaconReporting: + offset = sizeof(tDot11fIEBeaconReporting); + byteCount = 2; + pIePresent = ((tDot11fIEBeaconReporting *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeCondensedCountryStr: + offset = sizeof(tDot11fIECondensedCountryStr); + byteCount = 2; + pIePresent = ((tDot11fIECondensedCountryStr *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeMeasurementPilot: + offset = sizeof(tDot11fIEMeasurementPilot); + byteCount = ((tDot11fIEMeasurementPilot *) + (pFrm + pIe->offset + offset * i))-> + num_vendorSpecific + 1; + pIePresent = ((tDot11fIEMeasurementPilot *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeMultiBssid: + offset = sizeof(tDot11fIEMultiBssid); + byteCount = ((tDot11fIEMultiBssid *) + (pFrm + pIe->offset + offset * i))-> + num_vendorSpecific + 1; + pIePresent = ((tDot11fIEMultiBssid *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeRICData: + offset = sizeof(tDot11fIERICData); + byteCount = 4; + pIePresent = ((tDot11fIERICData *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeRICDescriptor: + offset = sizeof(tDot11fIERICDescriptor); + byteCount = ((tDot11fIERICDescriptor *) + (pFrm + pIe->offset + offset * i))-> + num_variableData + 1; + pIePresent = ((tDot11fIERICDescriptor *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeRRMEnabledCap: + offset = sizeof(tDot11fIERRMEnabledCap); + byteCount = 5; + pIePresent = ((tDot11fIERRMEnabledCap *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeRequestedInfo: + offset = sizeof(tDot11fIERequestedInfo); + byteCount = ((tDot11fIERequestedInfo *) + (pFrm + pIe->offset + offset * i))-> + num_requested_eids; + pIePresent = ((tDot11fIERequestedInfo *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeSSID: + offset = sizeof(tDot11fIESSID); + byteCount = ((tDot11fIESSID *) + (pFrm + pIe->offset + offset * i))-> + num_ssid; + pIePresent = ((tDot11fIESSID *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeSchedule: + offset = sizeof(tDot11fIESchedule); + byteCount = 14; + pIePresent = ((tDot11fIESchedule *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeTCLAS: + offset = sizeof(tDot11fIETCLAS); + status |= + dot11f_get_packed_ietclas( + pCtx, (tDot11fIETCLAS *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeTCLASSPROC: + offset = sizeof(tDot11fIETCLASSPROC); + byteCount = 1; + pIePresent = ((tDot11fIETCLASSPROC *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeTSDelay: + offset = sizeof(tDot11fIETSDelay); + byteCount = 4; + pIePresent = ((tDot11fIETSDelay *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeTSFInfo: + offset = sizeof(tDot11fIETSFInfo); + byteCount = 4; + pIePresent = ((tDot11fIETSFInfo *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeTSPEC: + offset = sizeof(tDot11fIETSPEC); + byteCount = 55; + pIePresent = ((tDot11fIETSPEC *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeVHTCaps: + offset = sizeof(tDot11fIEVHTCaps); + byteCount = 12; + pIePresent = ((tDot11fIEVHTCaps *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeVHTOperation: + offset = sizeof(tDot11fIEVHTOperation); + byteCount = 5; + pIePresent = ((tDot11fIEVHTOperation *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWMMSchedule: + offset = sizeof(tDot11fIEWMMSchedule); + byteCount = 15; + pIePresent = ((tDot11fIEWMMSchedule *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWMMTCLAS: + offset = sizeof(tDot11fIEWMMTCLAS); + status |= + dot11f_get_packed_iewmmtclas( + pCtx, (tDot11fIEWMMTCLAS *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWMMTCLASPROC: + offset = sizeof(tDot11fIEWMMTCLASPROC); + byteCount = 2; + pIePresent = ((tDot11fIEWMMTCLASPROC *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWMMTSDelay: + offset = sizeof(tDot11fIEWMMTSDelay); + byteCount = 5; + pIePresent = ((tDot11fIEWMMTSDelay *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWMMTSPEC: + offset = sizeof(tDot11fIEWMMTSPEC); + byteCount = 56; + pIePresent = ((tDot11fIEWMMTSPEC *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWiderBWChanSwitchAnn: + offset = sizeof(tDot11fIEWiderBWChanSwitchAnn); + byteCount = 3; + pIePresent = ((tDot11fIEWiderBWChanSwitchAnn *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeazimuth_req: + offset = sizeof(tDot11fIEazimuth_req); + byteCount = 1; + pIePresent = ((tDot11fIEazimuth_req *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIebeacon_report_frm_body_fragment_id: + offset = sizeof(tDot11fIEbeacon_report_frm_body_fragment_id); + byteCount = 2; + pIePresent = ((tDot11fIEbeacon_report_frm_body_fragment_id *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIelast_beacon_report_indication: + offset = sizeof(tDot11fIElast_beacon_report_indication); + byteCount = 1; + pIePresent = ((tDot11fIElast_beacon_report_indication *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIemax_age: + offset = sizeof(tDot11fIEmax_age); + byteCount = 2; + pIePresent = ((tDot11fIEmax_age *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeneighbor_rpt: + offset = sizeof(tDot11fIEneighbor_rpt); + status |= + dot11f_get_packed_ie_neighbor_rpt( + pCtx, (tDot11fIEneighbor_rpt *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIereq_mac_addr: + offset = sizeof(tDot11fIEreq_mac_addr); + byteCount = 6; + pIePresent = ((tDot11fIEreq_mac_addr *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIetgt_mac_addr: + offset = sizeof(tDot11fIEtgt_mac_addr); + byteCount = 6; + pIePresent = ((tDot11fIEtgt_mac_addr *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIevht_transmit_power_env: + offset = sizeof(tDot11fIEvht_transmit_power_env); + byteCount = ((tDot11fIEvht_transmit_power_env *) + (pFrm + pIe->offset + offset * i))-> + num_bytes; + pIePresent = ((tDot11fIEvht_transmit_power_env *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeAID: + offset = sizeof(tDot11fIEAID); + byteCount = 2; + pIePresent = ((tDot11fIEAID *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeBeaconReportStatus: + offset = sizeof(tDot11fIEBeaconReportStatus); + byteCount = 4; + pIePresent = ((tDot11fIEBeaconReportStatus *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeCFParams: + offset = sizeof(tDot11fIECFParams); + byteCount = 6; + pIePresent = ((tDot11fIECFParams *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeChallengeText: + offset = sizeof(tDot11fIEChallengeText); + byteCount = ((tDot11fIEChallengeText *) + (pFrm + pIe->offset + offset * i))-> + num_text; + pIePresent = ((tDot11fIEChallengeText *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeChanSwitchAnn: + offset = sizeof(tDot11fIEChanSwitchAnn); + byteCount = 3; + pIePresent = ((tDot11fIEChanSwitchAnn *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeChannelSwitchWrapper: + offset = sizeof(tDot11fIEChannelSwitchWrapper); + status |= + dot11f_get_packed_ie_channel_switch_wrapper( + pCtx, (tDot11fIEChannelSwitchWrapper *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeCountry: + offset = sizeof(tDot11fIECountry); + status |= + dot11f_get_packed_ie_country( + pCtx, (tDot11fIECountry *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeDSParams: + offset = sizeof(tDot11fIEDSParams); + byteCount = 1; + pIePresent = ((tDot11fIEDSParams *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeEDCAParamSet: + offset = sizeof(tDot11fIEEDCAParamSet); + byteCount = 18; + pIePresent = ((tDot11fIEEDCAParamSet *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeERPInfo: + offset = sizeof(tDot11fIEERPInfo); + byteCount = 1; + pIePresent = ((tDot11fIEERPInfo *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeESECckmOpaque: + offset = sizeof(tDot11fIEESECckmOpaque); + byteCount = ((tDot11fIEESECckmOpaque *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEESECckmOpaque *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeESERadMgmtCap: + offset = sizeof(tDot11fIEESERadMgmtCap); + byteCount = 2; + pIePresent = ((tDot11fIEESERadMgmtCap *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeESETrafStrmMet: + offset = sizeof(tDot11fIEESETrafStrmMet); + byteCount = 4; + pIePresent = ((tDot11fIEESETrafStrmMet *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeESETrafStrmRateSet: + offset = sizeof(tDot11fIEESETrafStrmRateSet); + byteCount = ((tDot11fIEESETrafStrmRateSet *) + (pFrm + pIe->offset + offset * i))-> + num_tsrates + 1; + pIePresent = ((tDot11fIEESETrafStrmRateSet *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeESETxmitPower: + offset = sizeof(tDot11fIEESETxmitPower); + byteCount = 2; + pIePresent = ((tDot11fIEESETxmitPower *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeESEVersion: + offset = sizeof(tDot11fIEESEVersion); + byteCount = 1; + pIePresent = ((tDot11fIEESEVersion *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeExtCap: + offset = sizeof(tDot11fIEExtCap); + byteCount = ((tDot11fIEExtCap *) + (pFrm + pIe->offset + offset * i))-> + num_bytes; + pIePresent = ((tDot11fIEExtCap *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeExtSuppRates: + offset = sizeof(tDot11fIEExtSuppRates); + byteCount = ((tDot11fIEExtSuppRates *) + (pFrm + pIe->offset + offset * i))-> + num_rates; + pIePresent = ((tDot11fIEExtSuppRates *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeFHParamSet: + offset = sizeof(tDot11fIEFHParamSet); + byteCount = 5; + pIePresent = ((tDot11fIEFHParamSet *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeFHParams: + offset = sizeof(tDot11fIEFHParams); + byteCount = 2; + pIePresent = ((tDot11fIEFHParams *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeFHPattTable: + offset = sizeof(tDot11fIEFHPattTable); + byteCount = ((tDot11fIEFHPattTable *) + (pFrm + pIe->offset + offset * i))-> + num_randtable + 4; + pIePresent = ((tDot11fIEFHPattTable *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeFTInfo: + offset = sizeof(tDot11fIEFTInfo); + status |= + dot11f_get_packed_ieft_info( + pCtx, (tDot11fIEFTInfo *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeHTCaps: + offset = sizeof(tDot11fIEHTCaps); + byteCount = ((tDot11fIEHTCaps *) + (pFrm + pIe->offset + offset * i))-> + num_rsvd + 26; + pIePresent = ((tDot11fIEHTCaps *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeHTInfo: + offset = sizeof(tDot11fIEHTInfo); + byteCount = ((tDot11fIEHTInfo *) + (pFrm + pIe->offset + offset * i))-> + num_rsvd + 22; + pIePresent = ((tDot11fIEHTInfo *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeIBSSParams: + offset = sizeof(tDot11fIEIBSSParams); + byteCount = 2; + pIePresent = ((tDot11fIEIBSSParams *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeLinkIdentifier: + offset = sizeof(tDot11fIELinkIdentifier); + byteCount = 18; + pIePresent = ((tDot11fIELinkIdentifier *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeMBO_IE: + offset = sizeof(tDot11fIEMBO_IE); + status |= + dot11f_get_packed_ie_MBO_IE( + pCtx, (tDot11fIEMBO_IE *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeMeasurementReport: + offset = sizeof(tDot11fIEMeasurementReport); + status |= + dot11f_get_packed_ie_measurement_report( + pCtx, (tDot11fIEMeasurementReport *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeMeasurementRequest: + offset = sizeof(tDot11fIEMeasurementRequest); + status |= + dot11f_get_packed_ie_measurement_request( + pCtx, (tDot11fIEMeasurementRequest *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeMobilityDomain: + offset = sizeof(tDot11fIEMobilityDomain); + byteCount = 3; + pIePresent = ((tDot11fIEMobilityDomain *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeNeighborReport: + offset = sizeof(tDot11fIENeighborReport); + status |= + dot11f_get_packed_ie_neighbor_report( + pCtx, (tDot11fIENeighborReport *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeOBSSScanParameters: + offset = sizeof(tDot11fIEOBSSScanParameters); + byteCount = 14; + pIePresent = ((tDot11fIEOBSSScanParameters *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeOperatingMode: + offset = sizeof(tDot11fIEOperatingMode); + byteCount = 1; + pIePresent = ((tDot11fIEOperatingMode *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeP2PAssocReq: + offset = sizeof(tDot11fIEP2PAssocReq); + status |= + dot11f_get_packed_iep2_p_assoc_req( + pCtx, (tDot11fIEP2PAssocReq *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeP2PAssocRes: + offset = sizeof(tDot11fIEP2PAssocRes); + status |= + dot11f_get_packed_iep2_p_assoc_res( + pCtx, (tDot11fIEP2PAssocRes *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeP2PBeacon: + offset = sizeof(tDot11fIEP2PBeacon); + status |= + dot11f_get_packed_iep2_p_beacon( + pCtx, (tDot11fIEP2PBeacon *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeP2PBeaconProbeRes: + offset = sizeof(tDot11fIEP2PBeaconProbeRes); + status |= + dot11f_get_packed_iep2_p_beacon_probe_res( + pCtx, (tDot11fIEP2PBeaconProbeRes *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeP2PDeAuth: + offset = sizeof(tDot11fIEP2PDeAuth); + status |= + dot11f_get_packed_iep2_p_de_auth( + pCtx, (tDot11fIEP2PDeAuth *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeP2PDisAssoc: + offset = sizeof(tDot11fIEP2PDisAssoc); + status |= + dot11f_get_packed_iep2_p_dis_assoc( + pCtx, (tDot11fIEP2PDisAssoc *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeP2PIEOpaque: + offset = sizeof(tDot11fIEP2PIEOpaque); + byteCount = ((tDot11fIEP2PIEOpaque *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEP2PIEOpaque *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeP2PProbeReq: + offset = sizeof(tDot11fIEP2PProbeReq); + status |= + dot11f_get_packed_iep2_p_probe_req( + pCtx, (tDot11fIEP2PProbeReq *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeP2PProbeRes: + offset = sizeof(tDot11fIEP2PProbeRes); + status |= + dot11f_get_packed_iep2_p_probe_res( + pCtx, (tDot11fIEP2PProbeRes *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIePTIControl: + offset = sizeof(tDot11fIEPTIControl); + byteCount = 3; + pIePresent = ((tDot11fIEPTIControl *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIePUBufferStatus: + offset = sizeof(tDot11fIEPUBufferStatus); + byteCount = 1; + pIePresent = ((tDot11fIEPUBufferStatus *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIePowerCaps: + offset = sizeof(tDot11fIEPowerCaps); + byteCount = 2; + pIePresent = ((tDot11fIEPowerCaps *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIePowerConstraints: + offset = sizeof(tDot11fIEPowerConstraints); + byteCount = 1; + pIePresent = ((tDot11fIEPowerConstraints *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeQBSSLoad: + offset = sizeof(tDot11fIEQBSSLoad); + byteCount = 5; + pIePresent = ((tDot11fIEQBSSLoad *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeQComVendorIE: + offset = sizeof(tDot11fIEQComVendorIE); + byteCount = 2; + pIePresent = ((tDot11fIEQComVendorIE *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeQOSCapsAp: + offset = sizeof(tDot11fIEQOSCapsAp); + byteCount = 1; + pIePresent = ((tDot11fIEQOSCapsAp *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeQOSCapsStation: + offset = sizeof(tDot11fIEQOSCapsStation); + byteCount = 1; + pIePresent = ((tDot11fIEQOSCapsStation *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeQosMapSet: + offset = sizeof(tDot11fIEQosMapSet); + byteCount = ((tDot11fIEQosMapSet *) + (pFrm + pIe->offset + offset * i))-> + num_dscp_exceptions; + pIePresent = ((tDot11fIEQosMapSet *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeQuiet: + offset = sizeof(tDot11fIEQuiet); + byteCount = 6; + pIePresent = ((tDot11fIEQuiet *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeRCPIIE: + offset = sizeof(tDot11fIERCPIIE); + byteCount = 1; + pIePresent = ((tDot11fIERCPIIE *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeRICDataDesc: + offset = sizeof(tDot11fIERICDataDesc); + pnNeeded -= 2 ; /* Subtract the length and Oui as this is our container IE to group Ies and it doesn't have its own length and OUI. */ + status |= + dot11f_get_packed_ieric_data_desc( + pCtx, (tDot11fIERICDataDesc *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeRSN: + offset = sizeof(tDot11fIERSN); + status |= + dot11f_get_packed_iersn( + pCtx, (tDot11fIERSN *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeRSNIIE: + offset = sizeof(tDot11fIERSNIIE); + byteCount = 1; + pIePresent = ((tDot11fIERSNIIE *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeRSNOpaque: + offset = sizeof(tDot11fIERSNOpaque); + byteCount = ((tDot11fIERSNOpaque *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIERSNOpaque *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeSuppChannels: + offset = sizeof(tDot11fIESuppChannels); + byteCount = ((tDot11fIESuppChannels *) + (pFrm + pIe->offset + offset * i))-> + num_bands * 2; + pIePresent = ((tDot11fIESuppChannels *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeSuppOperatingClasses: + offset = sizeof(tDot11fIESuppOperatingClasses); + byteCount = ((tDot11fIESuppOperatingClasses *) + (pFrm + pIe->offset + offset * i))-> + num_classes; + pIePresent = ((tDot11fIESuppOperatingClasses *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeSuppRates: + offset = sizeof(tDot11fIESuppRates); + byteCount = ((tDot11fIESuppRates *) + (pFrm + pIe->offset + offset * i))-> + num_rates; + pIePresent = ((tDot11fIESuppRates *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeTIM: + offset = sizeof(tDot11fIETIM); + byteCount = ((tDot11fIETIM *) + (pFrm + pIe->offset + offset * i))-> + num_vbmp + 3; + pIePresent = ((tDot11fIETIM *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeTPCReport: + offset = sizeof(tDot11fIETPCReport); + byteCount = 2; + pIePresent = ((tDot11fIETPCReport *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeTPCRequest: + offset = sizeof(tDot11fIETPCRequest); + byteCount = 0; + pIePresent = ((tDot11fIETPCRequest *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeTimeAdvertisement: + offset = sizeof(tDot11fIETimeAdvertisement); + byteCount = 16; + pIePresent = ((tDot11fIETimeAdvertisement *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeTimeoutInterval: + offset = sizeof(tDot11fIETimeoutInterval); + byteCount = 5; + pIePresent = ((tDot11fIETimeoutInterval *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeVHTExtBssLoad: + offset = sizeof(tDot11fIEVHTExtBssLoad); + byteCount = 5; + pIePresent = ((tDot11fIEVHTExtBssLoad *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeVendor1IE: + offset = sizeof(tDot11fIEVendor1IE); + byteCount = 0; + pIePresent = ((tDot11fIEVendor1IE *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeVendor3IE: + offset = sizeof(tDot11fIEVendor3IE); + byteCount = 0; + pIePresent = ((tDot11fIEVendor3IE *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWAPI: + offset = sizeof(tDot11fIEWAPI); + status |= + dot11f_get_packed_iewapi( + pCtx, (tDot11fIEWAPI *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWAPIOpaque: + offset = sizeof(tDot11fIEWAPIOpaque); + byteCount = ((tDot11fIEWAPIOpaque *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEWAPIOpaque *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWFATPC: + offset = sizeof(tDot11fIEWFATPC); + byteCount = 2; + pIePresent = ((tDot11fIEWFATPC *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWFDIEOpaque: + offset = sizeof(tDot11fIEWFDIEOpaque); + byteCount = ((tDot11fIEWFDIEOpaque *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEWFDIEOpaque *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWMMCaps: + offset = sizeof(tDot11fIEWMMCaps); + byteCount = 2; + pIePresent = ((tDot11fIEWMMCaps *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWMMInfoAp: + offset = sizeof(tDot11fIEWMMInfoAp); + byteCount = 2; + pIePresent = ((tDot11fIEWMMInfoAp *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWMMInfoStation: + offset = sizeof(tDot11fIEWMMInfoStation); + byteCount = 2; + pIePresent = ((tDot11fIEWMMInfoStation *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWMMParams: + offset = sizeof(tDot11fIEWMMParams); + byteCount = 19; + pIePresent = ((tDot11fIEWMMParams *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWPA: + offset = sizeof(tDot11fIEWPA); + status |= + dot11f_get_packed_iewpa( + pCtx, (tDot11fIEWPA *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWPAOpaque: + offset = sizeof(tDot11fIEWPAOpaque); + byteCount = ((tDot11fIEWPAOpaque *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEWPAOpaque *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWSC: + offset = sizeof(tDot11fIEWSC); + status |= + dot11f_get_packed_iewsc( + pCtx, (tDot11fIEWSC *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWscAssocReq: + offset = sizeof(tDot11fIEWscAssocReq); + status |= + dot11f_get_packed_ie_wsc_assoc_req( + pCtx, (tDot11fIEWscAssocReq *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWscAssocRes: + offset = sizeof(tDot11fIEWscAssocRes); + status |= + dot11f_get_packed_ie_wsc_assoc_res( + pCtx, (tDot11fIEWscAssocRes *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWscBeacon: + offset = sizeof(tDot11fIEWscBeacon); + status |= + dot11f_get_packed_ie_wsc_beacon( + pCtx, (tDot11fIEWscBeacon *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWscBeaconProbeRes: + offset = sizeof(tDot11fIEWscBeaconProbeRes); + status |= + dot11f_get_packed_ie_wsc_beacon_probe_res( + pCtx, (tDot11fIEWscBeaconProbeRes *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWscIEOpaque: + offset = sizeof(tDot11fIEWscIEOpaque); + byteCount = ((tDot11fIEWscIEOpaque *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEWscIEOpaque *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeWscProbeReq: + offset = sizeof(tDot11fIEWscProbeReq); + status |= + dot11f_get_packed_ie_wsc_probe_req( + pCtx, (tDot11fIEWscProbeReq *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWscProbeRes: + offset = sizeof(tDot11fIEWscProbeRes); + status |= + dot11f_get_packed_ie_wsc_probe_res( + pCtx, (tDot11fIEWscProbeRes *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeWscReassocRes: + offset = sizeof(tDot11fIEWscReassocRes); + status |= + dot11f_get_packed_ie_wsc_reassoc_res( + pCtx, (tDot11fIEWscReassocRes *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeaddba_extn_element: + offset = sizeof(tDot11fIEaddba_extn_element); + byteCount = 1; + pIePresent = ((tDot11fIEaddba_extn_element *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIebss_color_change: + offset = sizeof(tDot11fIEbss_color_change); + byteCount = 2; + pIePresent = ((tDot11fIEbss_color_change *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIedh_parameter_element: + offset = sizeof(tDot11fIEdh_parameter_element); + byteCount = ((tDot11fIEdh_parameter_element *) + (pFrm + pIe->offset + offset * i))-> + num_public_key + 2; + pIePresent = ((tDot11fIEdh_parameter_element *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeesp_information: + offset = sizeof(tDot11fIEesp_information); + byteCount = ((tDot11fIEesp_information *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEesp_information *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeext_chan_switch_ann: + offset = sizeof(tDot11fIEext_chan_switch_ann); + byteCount = 4; + pIePresent = ((tDot11fIEext_chan_switch_ann *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefils_assoc_delay_info: + offset = sizeof(tDot11fIEfils_assoc_delay_info); + byteCount = 1; + pIePresent = ((tDot11fIEfils_assoc_delay_info *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefils_hlp_container: + offset = sizeof(tDot11fIEfils_hlp_container); + byteCount = ((tDot11fIEfils_hlp_container *) + (pFrm + pIe->offset + offset * i))-> + num_hlp_packet + 12; + pIePresent = ((tDot11fIEfils_hlp_container *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefils_indication: + offset = sizeof(tDot11fIEfils_indication); + byteCount = ((tDot11fIEfils_indication *) + (pFrm + pIe->offset + offset * i))-> + num_variable_data + 2; + pIePresent = ((tDot11fIEfils_indication *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefils_kde: + offset = sizeof(tDot11fIEfils_kde); + byteCount = ((tDot11fIEfils_kde *) + (pFrm + pIe->offset + offset * i))-> + num_kde_list + 8; + pIePresent = ((tDot11fIEfils_kde *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefils_key_confirmation: + offset = sizeof(tDot11fIEfils_key_confirmation); + byteCount = ((tDot11fIEfils_key_confirmation *) + (pFrm + pIe->offset + offset * i))-> + num_key_auth; + pIePresent = ((tDot11fIEfils_key_confirmation *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefils_nonce: + offset = sizeof(tDot11fIEfils_nonce); + byteCount = 16; + pIePresent = ((tDot11fIEfils_nonce *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefils_public_key: + offset = sizeof(tDot11fIEfils_public_key); + byteCount = ((tDot11fIEfils_public_key *) + (pFrm + pIe->offset + offset * i))-> + num_public_key + 1; + pIePresent = ((tDot11fIEfils_public_key *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefils_session: + offset = sizeof(tDot11fIEfils_session); + byteCount = 8; + pIePresent = ((tDot11fIEfils_session *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefils_wrapped_data: + offset = sizeof(tDot11fIEfils_wrapped_data); + byteCount = ((tDot11fIEfils_wrapped_data *) + (pFrm + pIe->offset + offset * i))-> + num_wrapped_data; + pIePresent = ((tDot11fIEfils_wrapped_data *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIefragment_ie: + offset = sizeof(tDot11fIEfragment_ie); + byteCount = ((tDot11fIEfragment_ie *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEfragment_ie *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIehe_6ghz_band_cap: + offset = sizeof(tDot11fIEhe_6ghz_band_cap); + byteCount = 2; + pIePresent = ((tDot11fIEhe_6ghz_band_cap *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIehe_cap: + offset = sizeof(tDot11fIEhe_cap); + status |= + dot11f_get_packed_ie_he_cap( + pCtx, (tDot11fIEhe_cap *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIehe_op: + offset = sizeof(tDot11fIEhe_op); + status |= + dot11f_get_packed_ie_he_op( + pCtx, (tDot11fIEhe_op *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIehs20vendor_ie: + offset = sizeof(tDot11fIEhs20vendor_ie); + status |= + dot11f_get_packed_ie_hs20vendor_ie( + pCtx, (tDot11fIEhs20vendor_ie *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeht2040_bss_coexistence: + offset = sizeof(tDot11fIEht2040_bss_coexistence); + byteCount = 1; + pIePresent = ((tDot11fIEht2040_bss_coexistence *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeht2040_bss_intolerant_report: + offset = sizeof(tDot11fIEht2040_bss_intolerant_report); + byteCount = ((tDot11fIEht2040_bss_intolerant_report *) + (pFrm + pIe->offset + offset * i))-> + num_channel_list + 1; + pIePresent = ((tDot11fIEht2040_bss_intolerant_report *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIemu_edca_param_set: + offset = sizeof(tDot11fIEmu_edca_param_set); + byteCount = 13; + pIePresent = ((tDot11fIEmu_edca_param_set *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeosen_ie: + offset = sizeof(tDot11fIEosen_ie); + byteCount = ((tDot11fIEosen_ie *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEosen_ie *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIeqcn_ie: + offset = sizeof(tDot11fIEqcn_ie); + status |= + dot11f_get_packed_ie_qcn_ie( + pCtx, (tDot11fIEqcn_ie *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + case SigIeroaming_consortium_sel: + offset = sizeof(tDot11fIEroaming_consortium_sel); + byteCount = ((tDot11fIEroaming_consortium_sel *) + (pFrm + pIe->offset + offset * i))-> + num_data; + pIePresent = ((tDot11fIEroaming_consortium_sel *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIesec_chan_offset_ele: + offset = sizeof(tDot11fIEsec_chan_offset_ele); + byteCount = 1; + pIePresent = ((tDot11fIEsec_chan_offset_ele *) + (pFrm + pIe->offset + offset * i))-> + present; + break; + case SigIevendor_vht_ie: + offset = sizeof(tDot11fIEvendor_vht_ie); + status |= + dot11f_get_packed_ie_vendor_vht_ie( + pCtx, (tDot11fIEvendor_vht_ie *) + (pFrm + pIe->offset + offset * i), + pnNeeded); + break; + default: + FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR-- I don" + "'t know about the IE signature %d; this is most l" + "ikely a bug in 'framesc'.\n"), pIe->sig); + return DOT11F_INTERNAL_ERROR; + } /*End of switch Case*/ + + if (byteCount && pIePresent) + *pnNeeded += byteCount; + } /*End of for loop*/ + } + ++pIe; + } + return status; + +} + +static uint32_t get_packed_size_tlv_core(tpAniSirGlobal pCtx, + uint8_t *pFrm, + uint32_t *pnNeeded, + const tTLVDefn TLVs[]) +{ + const tTLVDefn *pTlv; + uint32_t status; + tFRAMES_BOOL *pfFound; + uint32_t byteCount = 0; + uint8_t pTlvPresent = 0; + + status = DOT11F_PARSE_SUCCESS; + + pTlv = &(TLVs[0]); + while (0xffff != pTlv->id) { + pfFound = (tFRAMES_BOOL *)(pFrm + pTlv->offset + + pTlv->presenceOffset); + if (*pfFound) { + *pnNeeded += (pTlv->sType + pTlv->sLen); + if (pTlv->pec) + *pnNeeded += 3U; + switch (pTlv->sig) { + case SigTlvAuthorizedMACs: + byteCount = 6; + pTlvPresent = ((tDot11fTLVAuthorizedMACs *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvRequestToEnroll: + byteCount = 1; + pTlvPresent = ((tDot11fTLVRequestToEnroll *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvVersion2: + byteCount = 1; + pTlvPresent = ((tDot11fTLVVersion2 *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvAPSetupLocked: + byteCount = 1; + pTlvPresent = ((tDot11fTLVAPSetupLocked *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvAssociationState: + byteCount = 2; + pTlvPresent = ((tDot11fTLVAssociationState *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvConfigMethods: + byteCount = 2; + pTlvPresent = ((tDot11fTLVConfigMethods *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvConfigurationError: + byteCount = 2; + pTlvPresent = ((tDot11fTLVConfigurationError *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvDeviceName: + byteCount = ((tDot11fTLVDeviceName *)(pFrm + pTlv->offset))->num_text; + pTlvPresent = ((tDot11fTLVDeviceName *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvDevicePasswordID: + byteCount = 2; + pTlvPresent = ((tDot11fTLVDevicePasswordID *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvExtendedListenTiming: + byteCount = 4; + pTlvPresent = ((tDot11fTLVExtendedListenTiming *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvListenChannel: + byteCount = 5; + pTlvPresent = ((tDot11fTLVListenChannel *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvManufacturer: + byteCount = ((tDot11fTLVManufacturer *)(pFrm + pTlv->offset))->num_name; + pTlvPresent = ((tDot11fTLVManufacturer *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvMinorReasonCode: + byteCount = 1; + pTlvPresent = ((tDot11fTLVMinorReasonCode *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvModelName: + byteCount = ((tDot11fTLVModelName *)(pFrm + pTlv->offset))->num_text; + pTlvPresent = ((tDot11fTLVModelName *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvModelNumber: + byteCount = ((tDot11fTLVModelNumber *)(pFrm + pTlv->offset))->num_text; + pTlvPresent = ((tDot11fTLVModelNumber *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvNoticeOfAbsence: + byteCount = ((tDot11fTLVNoticeOfAbsence *)(pFrm + pTlv->offset))->num_NoADesc+2; + pTlvPresent = ((tDot11fTLVNoticeOfAbsence *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvOperatingChannel: + byteCount = 5; + pTlvPresent = ((tDot11fTLVOperatingChannel *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvP2PCapability: + byteCount = 2; + pTlvPresent = ((tDot11fTLVP2PCapability *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvP2PDeviceId: + byteCount = 6; + pTlvPresent = ((tDot11fTLVP2PDeviceId *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvP2PDeviceInfo: + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pFrm + pTlv->offset, pnNeeded, TLVS_P2PDeviceInfo); + byteCount = 16; + pTlvPresent = ((tDot11fTLVP2PDeviceInfo *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvP2PGroupInfo: + byteCount = ((tDot11fTLVP2PGroupInfo *)(pFrm + pTlv->offset))->num_P2PClientInfoDesc; + pTlvPresent = ((tDot11fTLVP2PGroupInfo *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvP2PStatus: + byteCount = 1; + pTlvPresent = ((tDot11fTLVP2PStatus *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvPrimaryDeviceType: + byteCount = 8; + pTlvPresent = ((tDot11fTLVPrimaryDeviceType *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvRFBands: + byteCount = 1; + pTlvPresent = ((tDot11fTLVRFBands *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvRequestDeviceType: + byteCount = 8; + pTlvPresent = ((tDot11fTLVRequestDeviceType *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvRequestType: + byteCount = 1; + pTlvPresent = ((tDot11fTLVRequestType *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvResponseType: + byteCount = 1; + pTlvPresent = ((tDot11fTLVResponseType *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvSelectedRegistrar: + byteCount = 1; + pTlvPresent = ((tDot11fTLVSelectedRegistrar *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvSelectedRegistrarConfigMethods: + byteCount = 2; + pTlvPresent = ((tDot11fTLVSelectedRegistrarConfigMethods *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvSerialNumber: + byteCount = ((tDot11fTLVSerialNumber *)(pFrm + pTlv->offset))->num_text; + pTlvPresent = ((tDot11fTLVSerialNumber *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvUUID_E: + byteCount = 16; + pTlvPresent = ((tDot11fTLVUUID_E *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvUUID_R: + byteCount = 16; + pTlvPresent = ((tDot11fTLVUUID_R *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvVendorExtension: + status = get_packed_size_tlv_core(pCtx, (uint8_t *)pFrm + pTlv->offset, pnNeeded, TLVS_VendorExtension); + byteCount = 3; + pTlvPresent = ((tDot11fTLVVendorExtension *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvVersion: + byteCount = 1; + pTlvPresent = ((tDot11fTLVVersion *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvWPSState: + byteCount = 1; + pTlvPresent = ((tDot11fTLVWPSState *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvassoc_disallowed: + byteCount = 1; + pTlvPresent = ((tDot11fTLVassoc_disallowed *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvassoc_retry_delay: + byteCount = 2; + pTlvPresent = ((tDot11fTLVassoc_retry_delay *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvcellular_data_cap: + byteCount = 1; + pTlvPresent = ((tDot11fTLVcellular_data_cap *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvcellular_data_con_pref: + byteCount = 1; + pTlvPresent = ((tDot11fTLVcellular_data_con_pref *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvmbo_ap_cap: + byteCount = 1; + pTlvPresent = ((tDot11fTLVmbo_ap_cap *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvnon_prefferd_chan_rep: + byteCount = ((tDot11fTLVnon_prefferd_chan_rep *)(pFrm + pTlv->offset))->num_channel_report+1; + pTlvPresent = ((tDot11fTLVnon_prefferd_chan_rep *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvoce_cap: + byteCount = 1; + pTlvPresent = ((tDot11fTLVoce_cap *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvreduced_wan_metrics: + byteCount = 1; + pTlvPresent = ((tDot11fTLVreduced_wan_metrics *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvrssi_assoc_rej: + byteCount = 2; + pTlvPresent = ((tDot11fTLVrssi_assoc_rej *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvtransition_reason: + byteCount = 1; + pTlvPresent = ((tDot11fTLVtransition_reason *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvtransition_reject_reason: + byteCount = 1; + pTlvPresent = ((tDot11fTLVtransition_reject_reason *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvP2PInterface: + byteCount = 6; + pTlvPresent = ((tDot11fTLVP2PInterface *) + (pFrm + pTlv->offset))->present; + break; + case SigTlvP2PManageability: + byteCount = 1; + pTlvPresent = ((tDot11fTLVP2PManageability *) + (pFrm + pTlv->offset))->present; + break; + default: + FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR-- I don" + "'t know about the TLV signature %d; this is most l" + "ikely a bug in 'framesc'.\n"), pTlv->sig); + return DOT11F_INTERNAL_ERROR; + } + if (pTlvPresent) { + *pnNeeded += byteCount; + } + } + ++pTlv; + } + return status; +} +void dot11f_pack_ff_aid(tpAniSirGlobal pCtx, + tDot11fFfAID *pSrc, + uint8_t *pBuf) +{ + frameshtons(pCtx, pBuf, pSrc->associd, 0); + (void)pCtx; +} /* End dot11f_pack_ff_aid. */ + +void dot11f_pack_ff_action(tpAniSirGlobal pCtx, + tDot11fFfAction *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->action; + (void)pCtx; +} /* End dot11f_pack_ff_action. */ + +void dot11f_pack_ff_auth_algo(tpAniSirGlobal pCtx, + tDot11fFfAuthAlgo *pSrc, + uint8_t *pBuf) +{ + frameshtons(pCtx, pBuf, pSrc->algo, 0); + (void)pCtx; +} /* End dot11f_pack_ff_auth_algo. */ + +void dot11f_pack_ff_auth_seq_no(tpAniSirGlobal pCtx, + tDot11fFfAuthSeqNo *pSrc, + uint8_t *pBuf) +{ + frameshtons(pCtx, pBuf, pSrc->no, 0); + (void)pCtx; +} /* End dot11f_pack_ff_auth_seq_no. */ + +void dot11f_pack_ff_beacon_interval(tpAniSirGlobal pCtx, + tDot11fFfBeaconInterval *pSrc, + uint8_t *pBuf) +{ + frameshtons(pCtx, pBuf, pSrc->interval, 0); + (void)pCtx; +} /* End dot11f_pack_ff_beacon_interval. */ + +void dot11f_pack_ff_capabilities(tpAniSirGlobal pCtx, + tDot11fFfCapabilities *pSrc, + uint8_t *pBuf) +{ + uint16_t tmp97__; + tmp97__ = 0U; + tmp97__ |= (pSrc->ess << 0); + tmp97__ |= (pSrc->ibss << 1); + tmp97__ |= (pSrc->cfPollable << 2); + tmp97__ |= (pSrc->cfPollReq << 3); + tmp97__ |= (pSrc->privacy << 4); + tmp97__ |= (pSrc->shortPreamble << 5); + tmp97__ |= (pSrc->pbcc << 6); + tmp97__ |= (pSrc->channelAgility << 7); + tmp97__ |= (pSrc->spectrumMgt << 8); + tmp97__ |= (pSrc->qos << 9); + tmp97__ |= (pSrc->shortSlotTime << 10); + tmp97__ |= (pSrc->apsd << 11); + tmp97__ |= (pSrc->rrm << 12); + tmp97__ |= (pSrc->dsssOfdm << 13); + tmp97__ |= (pSrc->delayedBA << 14); + tmp97__ |= (pSrc->immediateBA << 15); + frameshtons(pCtx, pBuf, tmp97__, 0); + (void)pCtx; +} /* End dot11f_pack_ff_capabilities. */ + +void dot11f_pack_ff_category(tpAniSirGlobal pCtx, + tDot11fFfCategory *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->category; + (void)pCtx; +} /* End dot11f_pack_ff_category. */ + +void dot11f_pack_ff_current_ap_address(tpAniSirGlobal pCtx, + tDot11fFfCurrentAPAddress *pSrc, + uint8_t *pBuf) +{ + DOT11F_MEMCPY(pCtx, pBuf, pSrc->mac, 6); + (void)pCtx; +} /* End dot11f_pack_ff_current_ap_address. */ + +void dot11f_pack_ff_dialog_token(tpAniSirGlobal pCtx, + tDot11fFfDialogToken *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->token; + (void)pCtx; +} /* End dot11f_pack_ff_dialog_token. */ + +void dot11f_pack_ff_link_margin(tpAniSirGlobal pCtx, + tDot11fFfLinkMargin *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->linkMargin; + (void)pCtx; +} /* End dot11f_pack_ff_link_margin. */ + +void dot11f_pack_ff_listen_interval(tpAniSirGlobal pCtx, + tDot11fFfListenInterval *pSrc, + uint8_t *pBuf) +{ + frameshtons(pCtx, pBuf, pSrc->interval, 0); + (void)pCtx; +} /* End dot11f_pack_ff_listen_interval. */ + +void dot11f_pack_ff_max_tx_power(tpAniSirGlobal pCtx, + tDot11fFfMaxTxPower *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->maxTxPower; + (void)pCtx; +} /* End dot11f_pack_ff_max_tx_power. */ + +void dot11f_pack_ff_num_of_repetitions(tpAniSirGlobal pCtx, + tDot11fFfNumOfRepetitions *pSrc, + uint8_t *pBuf) +{ + frameshtons(pCtx, pBuf, pSrc->repetitions, 0); + (void)pCtx; +} /* End dot11f_pack_ff_num_of_repetitions. */ + +void dot11f_pack_ff_operating_mode(tpAniSirGlobal pCtx, + tDot11fFfOperatingMode *pSrc, + uint8_t *pBuf) +{ + uint8_t tmp98__; + tmp98__ = 0U; + tmp98__ |= (pSrc->chanWidth << 0); + tmp98__ |= (pSrc->reserved << 2); + tmp98__ |= (pSrc->rxNSS << 4); + tmp98__ |= (pSrc->rxNSSType << 7); + *pBuf = tmp98__; + (void)pCtx; +} /* End dot11f_pack_ff_operating_mode. */ + +void dot11f_pack_ff_rcpi(tpAniSirGlobal pCtx, + tDot11fFfRCPI *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->rcpi; + (void)pCtx; +} /* End dot11f_pack_ff_rcpi. */ + +void dot11f_pack_ff_rsni(tpAniSirGlobal pCtx, + tDot11fFfRSNI *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->rsni; + (void)pCtx; +} /* End dot11f_pack_ff_rsni. */ + +void dot11f_pack_ff_reason(tpAniSirGlobal pCtx, + tDot11fFfReason *pSrc, + uint8_t *pBuf) +{ + frameshtons(pCtx, pBuf, pSrc->code, 0); + (void)pCtx; +} /* End dot11f_pack_ff_reason. */ + +void dot11f_pack_ff_rx_antenna_id(tpAniSirGlobal pCtx, + tDot11fFfRxAntennaId *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->antennaId; + (void)pCtx; +} /* End dot11f_pack_ff_rx_antenna_id. */ + +void dot11f_pack_ff_sm_power_mode_set(tpAniSirGlobal pCtx, + tDot11fFfSMPowerModeSet *pSrc, + uint8_t *pBuf) +{ + uint8_t tmp99__; + tmp99__ = 0U; + tmp99__ |= (pSrc->PowerSave_En << 0); + tmp99__ |= (pSrc->Mode << 1); + tmp99__ |= (pSrc->reserved << 2); + *pBuf = tmp99__; + (void)pCtx; +} /* End dot11f_pack_ff_sm_power_mode_set. */ + +void dot11f_pack_ff_status(tpAniSirGlobal pCtx, + tDot11fFfStatus *pSrc, + uint8_t *pBuf) +{ + frameshtons(pCtx, pBuf, pSrc->status, 0); + (void)pCtx; +} /* End dot11f_pack_ff_status. */ + +void dot11f_pack_ff_status_code(tpAniSirGlobal pCtx, + tDot11fFfStatusCode *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->statusCode; + (void)pCtx; +} /* End dot11f_pack_ff_status_code. */ + +void dot11f_pack_ff_tpc_ele_id(tpAniSirGlobal pCtx, + tDot11fFfTPCEleID *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->TPCId; + (void)pCtx; +} /* End dot11f_pack_ff_tpc_ele_id. */ + +void dot11f_pack_ff_tpc_ele_len(tpAniSirGlobal pCtx, + tDot11fFfTPCEleLen *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->TPCLen; + (void)pCtx; +} /* End dot11f_pack_ff_tpc_ele_len. */ + +void dot11f_pack_ff_ts_info(tpAniSirGlobal pCtx, + tDot11fFfTSInfo *pSrc, + uint8_t *pBuf) +{ + uint32_t tmp100__; + tmp100__ = 0U; + tmp100__ |= (pSrc->traffic_type << 0); + tmp100__ |= (pSrc->tsid << 1); + tmp100__ |= (pSrc->direction << 5); + tmp100__ |= (pSrc->access_policy << 7); + tmp100__ |= (pSrc->aggregation << 9); + tmp100__ |= (pSrc->psb << 10); + tmp100__ |= (pSrc->user_priority << 11); + tmp100__ |= (pSrc->tsinfo_ack_pol << 14); + tmp100__ |= (pSrc->schedule << 16); + tmp100__ |= (pSrc->unused << 17); + frameshtonl(pCtx, pBuf, tmp100__, 0); + (void)pCtx; +} /* End dot11f_pack_ff_ts_info. */ + +void dot11f_pack_ff_time_stamp(tpAniSirGlobal pCtx, + tDot11fFfTimeStamp *pSrc, + uint8_t *pBuf) +{ + frameshtonq(pCtx, pBuf, pSrc->timestamp, 0); + (void)pCtx; +} /* End dot11f_pack_ff_time_stamp. */ + +void dot11f_pack_ff_transaction_id(tpAniSirGlobal pCtx, + tDot11fFfTransactionId *pSrc, + uint8_t *pBuf) +{ + DOT11F_MEMCPY(pCtx, pBuf, pSrc->transId, 2); + (void)pCtx; +} /* End dot11f_pack_ff_transaction_id. */ + +void dot11f_pack_ff_tx_antenna_id(tpAniSirGlobal pCtx, + tDot11fFfTxAntennaId *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->antennaId; + (void)pCtx; +} /* End dot11f_pack_ff_tx_antenna_id. */ + +void dot11f_pack_ff_tx_power(tpAniSirGlobal pCtx, + tDot11fFfTxPower *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->txPower; + (void)pCtx; +} /* End dot11f_pack_ff_tx_power. */ + +void dot11f_pack_ff_vht_membership_status_array(tpAniSirGlobal pCtx, + tDot11fFfVhtMembershipStatusArray *pSrc, + uint8_t *pBuf) +{ + DOT11F_MEMCPY(pCtx, pBuf, pSrc->membershipStatusArray, 8); + (void)pCtx; +} /* End dot11f_pack_ff_vht_membership_status_array. */ + +void dot11f_pack_ff_vht_user_position_array(tpAniSirGlobal pCtx, + tDot11fFfVhtUserPositionArray *pSrc, + uint8_t *pBuf) +{ + DOT11F_MEMCPY(pCtx, pBuf, pSrc->userPositionArray, 16); + (void)pCtx; +} /* End dot11f_pack_ff_vht_user_position_array. */ + +void dot11f_pack_ff_addba_param_set(tpAniSirGlobal pCtx, + tDot11fFfaddba_param_set *pSrc, + uint8_t *pBuf) +{ + uint16_t tmp101__; + tmp101__ = 0U; + tmp101__ |= (pSrc->amsdu_supp << 0); + tmp101__ |= (pSrc->policy << 1); + tmp101__ |= (pSrc->tid << 2); + tmp101__ |= (pSrc->buff_size << 6); + frameshtons(pCtx, pBuf, tmp101__, 0); + (void)pCtx; +} /* End dot11f_pack_ff_addba_param_set. */ + +void dot11f_pack_ff_ba_start_seq_ctrl(tpAniSirGlobal pCtx, + tDot11fFfba_start_seq_ctrl *pSrc, + uint8_t *pBuf) +{ + uint16_t tmp102__; + tmp102__ = 0U; + tmp102__ |= (pSrc->frag_number << 0); + tmp102__ |= (pSrc->ssn << 4); + frameshtons(pCtx, pBuf, tmp102__, 0); + (void)pCtx; +} /* End dot11f_pack_ff_ba_start_seq_ctrl. */ + +void dot11f_pack_ff_ba_timeout(tpAniSirGlobal pCtx, + tDot11fFfba_timeout *pSrc, + uint8_t *pBuf) +{ + frameshtons(pCtx, pBuf, pSrc->timeout, 0); + (void)pCtx; +} /* End dot11f_pack_ff_ba_timeout. */ + +void dot11f_pack_ff_delba_param_set(tpAniSirGlobal pCtx, + tDot11fFfdelba_param_set *pSrc, + uint8_t *pBuf) +{ + uint16_t tmp103__; + tmp103__ = 0U; + tmp103__ |= (pSrc->reserved << 0); + tmp103__ |= (pSrc->initiator << 11); + tmp103__ |= (pSrc->tid << 12); + frameshtons(pCtx, pBuf, tmp103__, 0); + (void)pCtx; +} /* End dot11f_pack_ff_delba_param_set. */ + +void dot11f_pack_ff_ext_chan_switch_ann_action(tpAniSirGlobal pCtx, + tDot11fFfext_chan_switch_ann_action *pSrc, + uint8_t *pBuf) +{ + uint32_t tmp104__; + tmp104__ = 0U; + tmp104__ |= (pSrc->switch_mode << 0); + tmp104__ |= (pSrc->op_class << 8); + tmp104__ |= (pSrc->new_channel << 16); + tmp104__ |= (pSrc->switch_count << 24); + frameshtonl(pCtx, pBuf, tmp104__, 0); + (void)pCtx; +} /* End dot11f_pack_ff_ext_chan_switch_ann_action. */ + +void dot11f_pack_ff_p2p_action_oui(tpAniSirGlobal pCtx, + tDot11fFfp2p_action_oui *pSrc, + uint8_t *pBuf) +{ + DOT11F_MEMCPY(pCtx, pBuf, pSrc->oui_data, 4); + (void)pCtx; +} /* End dot11f_pack_ff_p2p_action_oui. */ + +void dot11f_pack_ff_p2p_action_subtype(tpAniSirGlobal pCtx, + tDot11fFfp2p_action_subtype *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->subtype; + (void)pCtx; +} /* End dot11f_pack_ff_p2p_action_subtype. */ + +void dot11f_pack_ff_vendor_action_subtype(tpAniSirGlobal pCtx, + tDot11fFfvendor_action_subtype *pSrc, + uint8_t *pBuf) +{ + *pBuf = pSrc->subtype; + (void)pCtx; +} /* End dot11f_pack_ff_vendor_action_subtype. */ + +void dot11f_pack_ff_vendor_oui(tpAniSirGlobal pCtx, + tDot11fFfvendor_oui *pSrc, + uint8_t *pBuf) +{ + DOT11F_MEMCPY(pCtx, pBuf, pSrc->oui_data, 3); + (void)pCtx; +} /* End dot11f_pack_ff_vendor_oui. */ + +uint32_t dot11f_pack_tlv_authorized_ma_cs(tpAniSirGlobal pCtx, + tDot11fTLVAuthorizedMACs *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 8; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 1; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->mac, 6); + *pnConsumed += 6; + pBuf += 6; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_authorized_ma_cs. */ + +uint32_t dot11f_pack_tlv_request_to_enroll(tpAniSirGlobal pCtx, + tDot11fTLVRequestToEnroll *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 3; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + *pBuf = pSrc->req; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_request_to_enroll. */ + +uint32_t dot11f_pack_tlv_version2(tpAniSirGlobal pCtx, + tDot11fTLVVersion2 *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp105__; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 0; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + tmp105__ = 0U; + tmp105__ |= (pSrc->minor << 0); + tmp105__ |= (pSrc->major << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp105__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_version2. */ + +uint32_t dot11f_pack_tlv_ap_setup_locked(tpAniSirGlobal pCtx, + tDot11fTLVAPSetupLocked *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4183, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->fLocked; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_ap_setup_locked. */ + +uint32_t dot11f_pack_tlv_association_state(tpAniSirGlobal pCtx, + tDot11fTLVAssociationState *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4098, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + frameshtons(pCtx, pBuf, pSrc->state, 1); + *pnConsumed += 2; + pBuf += 2; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_association_state. */ + +uint32_t dot11f_pack_tlv_config_methods(tpAniSirGlobal pCtx, + tDot11fTLVConfigMethods *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4104, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + frameshtons(pCtx, pBuf, pSrc->methods, 1); + *pnConsumed += 2; + pBuf += 2; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_config_methods. */ + +uint32_t dot11f_pack_tlv_configuration_error(tpAniSirGlobal pCtx, + tDot11fTLVConfigurationError *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4105, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + frameshtons(pCtx, pBuf, pSrc->error, 1); + *pnConsumed += 2; + pBuf += 2; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_configuration_error. */ + +uint32_t dot11f_pack_tlv_device_name(tpAniSirGlobal pCtx, + tDot11fTLVDeviceName *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_text + 4) ; + + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4113, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->text), pSrc->num_text); + *pnConsumed += pSrc->num_text; + pBuf += pSrc->num_text; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_device_name. */ + +uint32_t dot11f_pack_tlv_device_password_id(tpAniSirGlobal pCtx, + tDot11fTLVDevicePasswordID *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4114, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + frameshtons(pCtx, pBuf, pSrc->id, 1); + *pnConsumed += 2; + pBuf += 2; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_device_password_id. */ + +uint32_t dot11f_pack_tlv_extended_listen_timing(tpAniSirGlobal pCtx, + tDot11fTLVExtendedListenTiming *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 7; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 8; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + frameshtons(pCtx, pBuf, pSrc->availibilityPeriod, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->availibilityInterval, 0); + *pnConsumed += 2; + pBuf += 2; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_extended_listen_timing. */ + +uint32_t dot11f_pack_tlv_listen_channel(tpAniSirGlobal pCtx, + tDot11fTLVListenChannel *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 8; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 6; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->countryString, 3); + *pnConsumed += 3; + pBuf += 3; + *pBuf = pSrc->regulatoryClass; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->channel; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_listen_channel. */ + +uint32_t dot11f_pack_tlv_manufacturer(tpAniSirGlobal pCtx, + tDot11fTLVManufacturer *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_name + 4) ; + + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4129, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->name), pSrc->num_name); + *pnConsumed += pSrc->num_name; + pBuf += pSrc->num_name; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_manufacturer. */ + +uint32_t dot11f_pack_tlv_minor_reason_code(tpAniSirGlobal pCtx, + tDot11fTLVMinorReasonCode *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 1; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->minorReasonCode; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_minor_reason_code. */ + +uint32_t dot11f_pack_tlv_model_name(tpAniSirGlobal pCtx, + tDot11fTLVModelName *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_text + 4) ; + + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4131, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->text), pSrc->num_text); + *pnConsumed += pSrc->num_text; + pBuf += pSrc->num_text; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_model_name. */ + +uint32_t dot11f_pack_tlv_model_number(tpAniSirGlobal pCtx, + tDot11fTLVModelNumber *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_text + 4) ; + + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4132, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->text), pSrc->num_text); + *pnConsumed += pSrc->num_text; + pBuf += pSrc->num_text; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_model_number. */ + +uint32_t dot11f_pack_tlv_notice_of_absence(tpAniSirGlobal pCtx, + tDot11fTLVNoticeOfAbsence *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_NoADesc + 5) ; + + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 12; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->index; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->CTSWindowOppPS; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->NoADesc), pSrc->num_NoADesc); + *pnConsumed += pSrc->num_NoADesc; + pBuf += pSrc->num_NoADesc; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_notice_of_absence. */ + +uint32_t dot11f_pack_tlv_operating_channel(tpAniSirGlobal pCtx, + tDot11fTLVOperatingChannel *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 8; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 17; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->countryString, 3); + *pnConsumed += 3; + pBuf += 3; + *pBuf = pSrc->regulatoryClass; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->channel; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_operating_channel. */ + +uint32_t dot11f_pack_tlv_p2_p_capability(tpAniSirGlobal pCtx, + tDot11fTLVP2PCapability *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 2; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->deviceCapability; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->groupCapability; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_p2_p_capability. */ + +uint32_t dot11f_pack_tlv_p2_p_device_id(tpAniSirGlobal pCtx, + tDot11fTLVP2PDeviceId *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 9; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 3; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->P2PDeviceAddress, 6); + *pnConsumed += 6; + pBuf += 6; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_p2_p_device_id. */ + +uint32_t dot11f_pack_tlv_p2_p_device_info(tpAniSirGlobal pCtx, + tDot11fTLVP2PDeviceInfo *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + uint32_t idx = 0; + nNeeded += 19; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 13; + pBuf += 1; nBuf -= 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; nBuf -= 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->P2PDeviceAddress, 6); + *pnConsumed += 6; + pBuf += 6; + frameshtons(pCtx, pBuf, pSrc->configMethod, 1); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->primaryDeviceType, 8); + *pnConsumed += 8; + pBuf += 8; + status |= pack_tlv_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + TLVS_P2PDeviceInfo, &idx); + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return status; +} /* End dot11f_pack_tlv_p2_p_device_info. */ + +uint32_t dot11f_pack_tlv_p2_p_group_info(tpAniSirGlobal pCtx, + tDot11fTLVP2PGroupInfo *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_P2PClientInfoDesc + 3) ; + + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 14; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->P2PClientInfoDesc), pSrc->num_P2PClientInfoDesc); + *pnConsumed += pSrc->num_P2PClientInfoDesc; + pBuf += pSrc->num_P2PClientInfoDesc; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_p2_p_group_info. */ + +uint32_t dot11f_pack_tlv_p2_p_status(tpAniSirGlobal pCtx, + tDot11fTLVP2PStatus *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 0; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->status; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_p2_p_status. */ + +uint32_t dot11f_pack_tlv_primary_device_type(tpAniSirGlobal pCtx, + tDot11fTLVPrimaryDeviceType *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 12; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4180, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + frameshtons(pCtx, pBuf, pSrc->primary_category, 1); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->oui, 4); + *pnConsumed += 4; + pBuf += 4; + frameshtons(pCtx, pBuf, pSrc->sub_category, 1); + *pnConsumed += 2; + pBuf += 2; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_primary_device_type. */ + +uint32_t dot11f_pack_tlv_rf_bands(tpAniSirGlobal pCtx, + tDot11fTLVRFBands *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4156, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->bands; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_rf_bands. */ + +uint32_t dot11f_pack_tlv_request_device_type(tpAniSirGlobal pCtx, + tDot11fTLVRequestDeviceType *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 12; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4202, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + frameshtons(pCtx, pBuf, pSrc->primary_category, 1); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->oui, 4); + *pnConsumed += 4; + pBuf += 4; + frameshtons(pCtx, pBuf, pSrc->sub_category, 1); + *pnConsumed += 2; + pBuf += 2; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_request_device_type. */ + +uint32_t dot11f_pack_tlv_request_type(tpAniSirGlobal pCtx, + tDot11fTLVRequestType *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4154, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->reqType; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_request_type. */ + +uint32_t dot11f_pack_tlv_response_type(tpAniSirGlobal pCtx, + tDot11fTLVResponseType *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4155, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->resType; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_response_type. */ + +uint32_t dot11f_pack_tlv_selected_registrar(tpAniSirGlobal pCtx, + tDot11fTLVSelectedRegistrar *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4161, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->selected; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_selected_registrar. */ + +uint32_t dot11f_pack_tlv_selected_registrar_config_methods(tpAniSirGlobal pCtx, + tDot11fTLVSelectedRegistrarConfigMethods *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4179, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + frameshtons(pCtx, pBuf, pSrc->methods, 1); + *pnConsumed += 2; + pBuf += 2; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_selected_registrar_config_methods. */ + +uint32_t dot11f_pack_tlv_serial_number(tpAniSirGlobal pCtx, + tDot11fTLVSerialNumber *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_text + 4) ; + + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4162, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->text), pSrc->num_text); + *pnConsumed += pSrc->num_text; + pBuf += pSrc->num_text; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_serial_number. */ + +uint32_t dot11f_pack_tlv_uuid_e(tpAniSirGlobal pCtx, + tDot11fTLVUUID_E *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 20; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4167, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->uuid, 16); + *pnConsumed += 16; + pBuf += 16; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_uuid_e. */ + +uint32_t dot11f_pack_tlv_uuid_r(tpAniSirGlobal pCtx, + tDot11fTLVUUID_R *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 20; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4168, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->uuid, 16); + *pnConsumed += 16; + pBuf += 16; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_uuid_r. */ + +uint32_t dot11f_pack_tlv_vendor_extension(tpAniSirGlobal pCtx, + tDot11fTLVVendorExtension *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + uint32_t idx = 0; + nNeeded += 7; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4169, 1); + pBuf += 2; nBuf -= 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; nBuf -= 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->vendorId, 3); + *pnConsumed += 3; + pBuf += 3; + status |= pack_tlv_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + TLVS_VendorExtension, &idx); + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return status; +} /* End dot11f_pack_tlv_vendor_extension. */ + +uint32_t dot11f_pack_tlv_version(tpAniSirGlobal pCtx, + tDot11fTLVVersion *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp106__; + nNeeded += 5; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4170, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + tmp106__ = 0U; + tmp106__ |= (pSrc->minor << 0); + tmp106__ |= (pSrc->major << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp106__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_version. */ + +uint32_t dot11f_pack_tlv_wps_state(tpAniSirGlobal pCtx, + tDot11fTLVWPSState *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + frameshtons(pCtx, pBuf, 4164, 1); + pBuf += 2; *pnConsumed += 2; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->state; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 4, 1); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_wps_state. */ + +uint32_t dot11f_pack_tlv_assoc_disallowed(tpAniSirGlobal pCtx, + tDot11fTLVassoc_disallowed *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 4; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + *pBuf = pSrc->reason_code; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_assoc_disallowed. */ + +uint32_t dot11f_pack_tlv_assoc_retry_delay(tpAniSirGlobal pCtx, + tDot11fTLVassoc_retry_delay *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 8; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + frameshtons(pCtx, pBuf, pSrc->delay, 0); + *pnConsumed += 2; + pBuf += 2; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_assoc_retry_delay. */ + +uint32_t dot11f_pack_tlv_cellular_data_cap(tpAniSirGlobal pCtx, + tDot11fTLVcellular_data_cap *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 3; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + *pBuf = pSrc->cellular_connectivity; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_cellular_data_cap. */ + +uint32_t dot11f_pack_tlv_cellular_data_con_pref(tpAniSirGlobal pCtx, + tDot11fTLVcellular_data_con_pref *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 5; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + *pBuf = pSrc->cellular_preference; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_cellular_data_con_pref. */ + +uint32_t dot11f_pack_tlv_mbo_ap_cap(tpAniSirGlobal pCtx, + tDot11fTLVmbo_ap_cap *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 1; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + *pBuf = pSrc->mbo_cap_ind; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_mbo_ap_cap. */ + +uint32_t dot11f_pack_tlv_non_prefferd_chan_rep(tpAniSirGlobal pCtx, + tDot11fTLVnon_prefferd_chan_rep *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_channel_report + 3) ; + + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 2; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + *pBuf = pSrc->oper_class; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->channel_report), pSrc->num_channel_report); + *pnConsumed += pSrc->num_channel_report; + pBuf += pSrc->num_channel_report; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_non_prefferd_chan_rep. */ + +uint32_t dot11f_pack_tlv_oce_cap(tpAniSirGlobal pCtx, + tDot11fTLVoce_cap *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp107__; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 101; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + tmp107__ = 0U; + tmp107__ |= (pSrc->oce_release << 0); + tmp107__ |= (pSrc->is_sta_cfon << 3); + tmp107__ |= (pSrc->non_oce_ap_present << 4); + tmp107__ |= (pSrc->reserved << 5); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp107__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_oce_cap. */ + +uint32_t dot11f_pack_tlv_reduced_wan_metrics(tpAniSirGlobal pCtx, + tDot11fTLVreduced_wan_metrics *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp108__; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 103; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + tmp108__ = 0U; + tmp108__ |= (pSrc->downlink_av_cap << 0); + tmp108__ |= (pSrc->uplink_av_cap << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp108__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_reduced_wan_metrics. */ + +uint32_t dot11f_pack_tlv_rssi_assoc_rej(tpAniSirGlobal pCtx, + tDot11fTLVrssi_assoc_rej *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 102; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + *pBuf = pSrc->delta_rssi; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->retry_delay; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_rssi_assoc_rej. */ + +uint32_t dot11f_pack_tlv_transition_reason(tpAniSirGlobal pCtx, + tDot11fTLVtransition_reason *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 6; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + *pBuf = pSrc->transition_reason_code; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_transition_reason. */ + +uint32_t dot11f_pack_tlv_transition_reject_reason(tpAniSirGlobal pCtx, + tDot11fTLVtransition_reject_reason *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 7; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 1; *pnConsumed += 1; + *pBuf = pSrc->transition_reject_code; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + *pTlvLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_transition_reject_reason. */ + +uint32_t dot11f_pack_tlv_p2_p_interface(tpAniSirGlobal pCtx, + tDot11fTLVP2PInterface *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 9; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 16; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->P2PDeviceAddress, 6); + *pnConsumed += 6; + pBuf += 6; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_p2_p_interface. */ + +uint32_t dot11f_pack_tlv_p2_p_manageability(tpAniSirGlobal pCtx, + tDot11fTLVP2PManageability *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pTlvLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + while (pSrc->present) { + *pBuf = 10; + pBuf += 1; *pnConsumed += 1; + pTlvLen = pBuf; + pBuf += 2; *pnConsumed += 2; + *pBuf = pSrc->manageability; + *pnConsumed += 1; + pBuf += 1; + break; + } + (void)pCtx; + if (pTlvLen) { + frameshtons(pCtx, pTlvLen, *pnConsumed - nConsumedOnEntry - 3, 0); + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_tlv_p2_p_manageability. */ + +uint32_t dot11f_pack_ie_gtk(tpAniSirGlobal pCtx, + tDot11fIEGTK *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp109__; + nNeeded += (pSrc->num_key + 11); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 2; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp109__ = 0U; + tmp109__ |= (pSrc->keyId << 0); + tmp109__ |= (pSrc->reserved << 2); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp109__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + *pBuf = pSrc->keyLength; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->RSC, 8); + *pnConsumed += 8; + pBuf += 8; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->key), pSrc->num_key); + *pnConsumed += pSrc->num_key; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_gtk. */ + +uint32_t dot11f_pack_ie_igtk(tpAniSirGlobal pCtx, + tDot11fIEIGTK *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 33; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 4; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->keyID, 2); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->IPN, 6); + *pnConsumed += 6; + pBuf += 6; + *pBuf = pSrc->keyLength; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->key, 24); + *pnConsumed += 24; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_igtk. */ + +uint32_t dot11f_pack_ie_r0_kh_id(tpAniSirGlobal pCtx, + tDot11fIER0KH_ID *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_PMK_R0_ID; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 3; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->PMK_R0_ID), pSrc->num_PMK_R0_ID); + *pnConsumed += pSrc->num_PMK_R0_ID; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_r0_kh_id. */ + +uint32_t dot11f_pack_ie_r1_kh_id(tpAniSirGlobal pCtx, + tDot11fIER1KH_ID *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 1; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->PMK_R1_ID, 6); + *pnConsumed += 6; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_r1_kh_id. */ + +uint32_t dot11f_pack_ie_version_attr(tpAniSirGlobal pCtx, + tDot11fIEversion_attr *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 1; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->sub_version; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_version_attr. */ + +uint32_t dot11f_pack_ie_vht_mcs11_attr(tpAniSirGlobal pCtx, + tDot11fIEvht_mcs11_attr *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 2; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->vht_mcs_10_11_supp; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_vht_mcs11_attr. */ + +uint32_t dot11f_pack_ie_ap_channel_report(tpAniSirGlobal pCtx, + tDot11fIEAPChannelReport *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_channelList + 1); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 51; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->regulatoryClass; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->channelList), pSrc->num_channelList); + *pnConsumed += pSrc->num_channelList; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ap_channel_report. */ + +uint32_t dot11f_pack_ie_bcn_reporting_detail(tpAniSirGlobal pCtx, + tDot11fIEBcnReportingDetail *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 2; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->reportingDetail; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_bcn_reporting_detail. */ + +uint32_t dot11f_pack_ie_beacon_report_frm_body(tpAniSirGlobal pCtx, + tDot11fIEBeaconReportFrmBody *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_reportedFields; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 1; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->reportedFields), pSrc->num_reportedFields); + *pnConsumed += pSrc->num_reportedFields; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_beacon_report_frm_body. */ + +uint32_t dot11f_pack_ie_beacon_reporting(tpAniSirGlobal pCtx, + tDot11fIEBeaconReporting *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 1; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->reportingCondition; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->threshold; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_beacon_reporting. */ + +uint32_t dot11f_pack_ie_condensed_country_str(tpAniSirGlobal pCtx, + tDot11fIECondensedCountryStr *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 2; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->countryStr, 2); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_condensed_country_str. */ + +uint32_t dot11f_pack_ie_measurement_pilot(tpAniSirGlobal pCtx, + tDot11fIEMeasurementPilot *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_vendorSpecific + 1); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 66; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->measurementPilot; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->vendorSpecific), pSrc->num_vendorSpecific); + *pnConsumed += pSrc->num_vendorSpecific; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_measurement_pilot. */ + +uint32_t dot11f_pack_ie_multi_bssid(tpAniSirGlobal pCtx, + tDot11fIEMultiBssid *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_vendorSpecific + 1); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 71; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->maxBSSIDIndicator; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->vendorSpecific), pSrc->num_vendorSpecific); + *pnConsumed += pSrc->num_vendorSpecific; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_multi_bssid. */ + +uint32_t dot11f_pack_ie_ric_data(tpAniSirGlobal pCtx, + tDot11fIERICData *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 57; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->Identifier; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->resourceDescCount; + *pnConsumed += 1; + pBuf += 1; + frameshtons(pCtx, pBuf, pSrc->statusCode, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ric_data. */ + +uint32_t dot11f_pack_ie_ric_descriptor(tpAniSirGlobal pCtx, + tDot11fIERICDescriptor *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_variableData + 1); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 75; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->resourceType; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->variableData), pSrc->num_variableData); + *pnConsumed += pSrc->num_variableData; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ric_descriptor. */ + +uint32_t dot11f_pack_ie_rrm_enabled_cap(tpAniSirGlobal pCtx, + tDot11fIERRMEnabledCap *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp110__; + uint8_t tmp111__; + uint8_t tmp112__; + uint8_t tmp113__; + uint8_t tmp114__; + nNeeded += 5; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 70; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp110__ = 0U; + tmp110__ |= (pSrc->LinkMeasurement << 0); + tmp110__ |= (pSrc->NeighborRpt << 1); + tmp110__ |= (pSrc->parallel << 2); + tmp110__ |= (pSrc->repeated << 3); + tmp110__ |= (pSrc->BeaconPassive << 4); + tmp110__ |= (pSrc->BeaconActive << 5); + tmp110__ |= (pSrc->BeaconTable << 6); + tmp110__ |= (pSrc->BeaconRepCond << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp110__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp111__ = 0U; + tmp111__ |= (pSrc->FrameMeasurement << 0); + tmp111__ |= (pSrc->ChannelLoad << 1); + tmp111__ |= (pSrc->NoiseHistogram << 2); + tmp111__ |= (pSrc->statistics << 3); + tmp111__ |= (pSrc->LCIMeasurement << 4); + tmp111__ |= (pSrc->LCIAzimuth << 5); + tmp111__ |= (pSrc->TCMCapability << 6); + tmp111__ |= (pSrc->triggeredTCM << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp111__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp112__ = 0U; + tmp112__ |= (pSrc->APChanReport << 0); + tmp112__ |= (pSrc->RRMMIBEnabled << 1); + tmp112__ |= (pSrc->operatingChanMax << 2); + tmp112__ |= (pSrc->nonOperatinChanMax << 5); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp112__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp113__ = 0U; + tmp113__ |= (pSrc->MeasurementPilot << 0); + tmp113__ |= (pSrc->MeasurementPilotEnabled << 3); + tmp113__ |= (pSrc->NeighborTSFOffset << 4); + tmp113__ |= (pSrc->RCPIMeasurement << 5); + tmp113__ |= (pSrc->RSNIMeasurement << 6); + tmp113__ |= (pSrc->BssAvgAccessDelay << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp113__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp114__ = 0U; + tmp114__ |= (pSrc->BSSAvailAdmission << 0); + tmp114__ |= (pSrc->AntennaInformation << 1); + tmp114__ |= (pSrc->fine_time_meas_rpt << 2); + tmp114__ |= (pSrc->lci_capability << 3); + tmp114__ |= (pSrc->reserved << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp114__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_rrm_enabled_cap. */ + +uint32_t dot11f_pack_ie_requested_info(tpAniSirGlobal pCtx, + tDot11fIERequestedInfo *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_requested_eids; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 10; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->requested_eids), pSrc->num_requested_eids); + *pnConsumed += pSrc->num_requested_eids; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_requested_info. */ + +uint32_t dot11f_pack_ie_ssid(tpAniSirGlobal pCtx, + tDot11fIESSID *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_ssid; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 0; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->ssid), pSrc->num_ssid); + *pnConsumed += pSrc->num_ssid; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ssid. */ + +uint32_t dot11f_pack_ie_schedule(tpAniSirGlobal pCtx, + tDot11fIESchedule *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp115__; + nNeeded += 14; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 15; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp115__ = 0U; + tmp115__ |= (pSrc->aggregation << 0); + tmp115__ |= (pSrc->tsid << 1); + tmp115__ |= (pSrc->direction << 5); + tmp115__ |= (pSrc->reserved << 7); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp115__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + frameshtonl(pCtx, pBuf, pSrc->service_start_time, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->service_interval, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtons(pCtx, pBuf, pSrc->max_service_dur, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->spec_interval, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_schedule. */ + +uint32_t dot11f_pack_ie_tclas(tpAniSirGlobal pCtx, + tDot11fIETCLAS *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ietclas(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 14; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->user_priority; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->classifier_type; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->classifier_mask; + *pnConsumed += 1; + pBuf += 1; + switch (pSrc->classifier_type) { + case 0: + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.EthParams.source, 6); + *pnConsumed += 6; + pBuf += 6; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.EthParams.dest, 6); + *pnConsumed += 6; + pBuf += 6; + frameshtons(pCtx, pBuf, pSrc->info.EthParams.type, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + case 1: + *pBuf = pSrc->info.IpParams.version; + *pnConsumed += 1; + pBuf += 1; + switch (pSrc->info.IpParams.version) { + case 4: + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV4Params.source, 4); + *pnConsumed += 4; + pBuf += 4; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV4Params.dest, 4); + *pnConsumed += 4; + pBuf += 4; + frameshtons(pCtx, pBuf, pSrc->info.IpParams.params.IpV4Params.src_port, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->info.IpParams.params.IpV4Params.dest_port, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->info.IpParams.params.IpV4Params.DSCP; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->info.IpParams.params.IpV4Params.proto; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->info.IpParams.params.IpV4Params.reserved; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + case 6: + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.source, 16); + *pnConsumed += 16; + pBuf += 16; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.dest, 16); + *pnConsumed += 16; + pBuf += 16; + frameshtons(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.src_port, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.dest_port, 0); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.flow_label, 3); + *pnConsumed += 3; + /* fieldsEndFlag = 1 */ + break; + } + break; + case 2: + frameshtons(pCtx, pBuf, pSrc->info.Params8021dq.tag_type, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_tclas. */ + +uint32_t dot11f_pack_ie_tclassproc(tpAniSirGlobal pCtx, + tDot11fIETCLASSPROC *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 44; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->processing; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_tclassproc. */ + +uint32_t dot11f_pack_ie_ts_delay(tpAniSirGlobal pCtx, + tDot11fIETSDelay *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 43; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtonl(pCtx, pBuf, pSrc->delay, 0); + *pnConsumed += 4; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ts_delay. */ + +uint32_t dot11f_pack_ie_tsf_info(tpAniSirGlobal pCtx, + tDot11fIETSFInfo *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 1; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->TsfOffset, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->BeaconIntvl, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_tsf_info. */ + +uint32_t dot11f_pack_ie_tspec(tpAniSirGlobal pCtx, + tDot11fIETSPEC *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp116__; + uint8_t tmp117__; + uint16_t tmp118__; + nNeeded += 55; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 13; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp116__ = 0U; + tmp116__ |= (pSrc->traffic_type << 0); + tmp116__ |= (pSrc->tsid << 1); + tmp116__ |= (pSrc->direction << 5); + tmp116__ |= (pSrc->access_policy << 7); + tmp116__ |= (pSrc->aggregation << 9); + tmp116__ |= (pSrc->psb << 10); + tmp116__ |= (pSrc->user_priority << 11); + tmp116__ |= (pSrc->tsinfo_ack_pol << 14); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp116__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + tmp117__ = 0U; + tmp117__ |= (pSrc->schedule << 0); + tmp117__ |= (pSrc->unused << 1); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp117__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp118__ = 0U; + tmp118__ |= (pSrc->size << 0); + tmp118__ |= (pSrc->fixed << 15); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp118__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + frameshtons(pCtx, pBuf, pSrc->max_msdu_size, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtonl(pCtx, pBuf, pSrc->min_service_int, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->max_service_int, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->inactivity_int, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->suspension_int, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->service_start_time, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->min_data_rate, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->mean_data_rate, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->peak_data_rate, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->burst_size, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->delay_bound, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->min_phy_rate, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtons(pCtx, pBuf, pSrc->surplus_bw_allowance, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->medium_time, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_tspec. */ + +uint32_t dot11f_pack_ie_vht_caps(tpAniSirGlobal pCtx, + tDot11fIEVHTCaps *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t tmp119__; + uint16_t tmp120__; + uint16_t tmp121__; + nNeeded += 12; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 191; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp119__ = 0U; + tmp119__ |= (pSrc->maxMPDULen << 0); + tmp119__ |= (pSrc->supportedChannelWidthSet << 2); + tmp119__ |= (pSrc->ldpcCodingCap << 4); + tmp119__ |= (pSrc->shortGI80MHz << 5); + tmp119__ |= (pSrc->shortGI160and80plus80MHz << 6); + tmp119__ |= (pSrc->txSTBC << 7); + tmp119__ |= (pSrc->rxSTBC << 8); + tmp119__ |= (pSrc->suBeamFormerCap << 11); + tmp119__ |= (pSrc->suBeamformeeCap << 12); + tmp119__ |= (pSrc->csnofBeamformerAntSup << 13); + tmp119__ |= (pSrc->numSoundingDim << 16); + tmp119__ |= (pSrc->muBeamformerCap << 19); + tmp119__ |= (pSrc->muBeamformeeCap << 20); + tmp119__ |= (pSrc->vhtTXOPPS << 21); + tmp119__ |= (pSrc->htcVHTCap << 22); + tmp119__ |= (pSrc->maxAMPDULenExp << 23); + tmp119__ |= (pSrc->vhtLinkAdaptCap << 26); + tmp119__ |= (pSrc->rxAntPattern << 28); + tmp119__ |= (pSrc->txAntPattern << 29); + tmp119__ |= (pSrc->extended_nss_bw_supp << 30); + if (unlikely(nBuf < 4)) + return DOT11F_INCOMPLETE_IE; + + frameshtonl(pCtx, pBuf, tmp119__, 0); + *pnConsumed += 4; + pBuf += 4; + nBuf -= 4 ; + frameshtons(pCtx, pBuf, pSrc->rxMCSMap, 0); + *pnConsumed += 2; + pBuf += 2; + tmp120__ = 0U; + tmp120__ |= (pSrc->rxHighSupDataRate << 0); + tmp120__ |= (pSrc->max_nsts_total << 13); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp120__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + frameshtons(pCtx, pBuf, pSrc->txMCSMap, 0); + *pnConsumed += 2; + pBuf += 2; + tmp121__ = 0U; + tmp121__ |= (pSrc->txSupDataRate << 0); + tmp121__ |= (pSrc->vht_extended_nss_bw_cap << 13); + tmp121__ |= (pSrc->reserved << 14); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp121__, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + nBuf -= 2 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_vht_caps. */ + +uint32_t dot11f_pack_ie_vht_operation(tpAniSirGlobal pCtx, + tDot11fIEVHTOperation *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 192; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->chanWidth; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->chan_center_freq_seg0; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->chan_center_freq_seg1; + *pnConsumed += 1; + pBuf += 1; + frameshtons(pCtx, pBuf, pSrc->basicMCSSet, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_vht_operation. */ + +uint32_t dot11f_pack_ie_wmm_schedule(tpAniSirGlobal pCtx, + tDot11fIEWMMSchedule *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp122__; + nNeeded += 15; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + tmp122__ = 0U; + tmp122__ |= (pSrc->aggregation << 0); + tmp122__ |= (pSrc->tsid << 1); + tmp122__ |= (pSrc->direction << 5); + tmp122__ |= (pSrc->reserved << 7); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp122__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + frameshtonl(pCtx, pBuf, pSrc->service_start_time, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->service_interval, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtons(pCtx, pBuf, pSrc->max_service_dur, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->spec_interval, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wmm_schedule. */ + +uint32_t dot11f_pack_ie_wmmtclas(tpAniSirGlobal pCtx, + tDot11fIEWMMTCLAS *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_iewmmtclas(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x6; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->user_priority; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->classifier_type; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->classifier_mask; + *pnConsumed += 1; + pBuf += 1; + switch (pSrc->classifier_type) { + case 0: + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.EthParams.source, 6); + *pnConsumed += 6; + pBuf += 6; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.EthParams.dest, 6); + *pnConsumed += 6; + pBuf += 6; + frameshtons(pCtx, pBuf, pSrc->info.EthParams.type, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + case 1: + *pBuf = pSrc->info.IpParams.version; + *pnConsumed += 1; + pBuf += 1; + switch (pSrc->info.IpParams.version) { + case 4: + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV4Params.source, 4); + *pnConsumed += 4; + pBuf += 4; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV4Params.dest, 4); + *pnConsumed += 4; + pBuf += 4; + frameshtons(pCtx, pBuf, pSrc->info.IpParams.params.IpV4Params.src_port, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->info.IpParams.params.IpV4Params.dest_port, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->info.IpParams.params.IpV4Params.DSCP; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->info.IpParams.params.IpV4Params.proto; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->info.IpParams.params.IpV4Params.reserved; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + case 6: + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.source, 10); + *pnConsumed += 10; + pBuf += 10; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.dest, 10); + *pnConsumed += 10; + pBuf += 10; + frameshtons(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.src_port, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.dest_port, 0); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->info.IpParams.params.IpV6Params.flow_label, 3); + *pnConsumed += 3; + /* fieldsEndFlag = 1 */ + break; + } + break; + case 2: + frameshtons(pCtx, pBuf, pSrc->info.Params8021dq.tag_type, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_wmmtclas. */ + +uint32_t dot11f_pack_ie_wmmtclasproc(tpAniSirGlobal pCtx, + tDot11fIEWMMTCLASPROC *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x7; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->processing; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wmmtclasproc. */ + +uint32_t dot11f_pack_ie_wmmts_delay(tpAniSirGlobal pCtx, + tDot11fIEWMMTSDelay *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x8; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + frameshtonl(pCtx, pBuf, pSrc->delay, 0); + *pnConsumed += 4; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wmmts_delay. */ + +uint32_t dot11f_pack_ie_wmmtspec(tpAniSirGlobal pCtx, + tDot11fIEWMMTSPEC *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp123__; + uint8_t tmp124__; + uint16_t tmp125__; + nNeeded += 38; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + tmp123__ = 0U; + tmp123__ |= (pSrc->traffic_type << 0); + tmp123__ |= (pSrc->tsid << 1); + tmp123__ |= (pSrc->direction << 5); + tmp123__ |= (pSrc->access_policy << 7); + tmp123__ |= (pSrc->aggregation << 9); + tmp123__ |= (pSrc->psb << 10); + tmp123__ |= (pSrc->user_priority << 11); + tmp123__ |= (pSrc->tsinfo_ack_pol << 14); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp123__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + tmp124__ = 0U; + tmp124__ |= (pSrc->tsinfo_rsvd << 0); + tmp124__ |= (pSrc->burst_size_defn << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp124__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp125__ = 0U; + tmp125__ |= (pSrc->size << 0); + tmp125__ |= (pSrc->fixed << 15); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp125__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + frameshtons(pCtx, pBuf, pSrc->max_msdu_size, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtonl(pCtx, pBuf, pSrc->min_service_int, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->max_service_int, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->inactivity_int, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->suspension_int, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->service_start_time, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->min_data_rate, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->mean_data_rate, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->peak_data_rate, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->burst_size, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->delay_bound, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtonl(pCtx, pBuf, pSrc->min_phy_rate, 0); + *pnConsumed += 4; + pBuf += 4; + frameshtons(pCtx, pBuf, pSrc->surplus_bw_allowance, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->medium_time, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wmmtspec. */ + +uint32_t dot11f_pack_ie_wider_bw_chan_switch_ann(tpAniSirGlobal pCtx, + tDot11fIEWiderBWChanSwitchAnn *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 194; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->newChanWidth; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->newCenterChanFreq0; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->newCenterChanFreq1; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wider_bw_chan_switch_ann. */ + +uint32_t dot11f_pack_ie_azimuth_req(tpAniSirGlobal pCtx, + tDot11fIEazimuth_req *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 1; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->request; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_azimuth_req. */ + +uint32_t dot11f_pack_ie_beacon_report_frm_body_fragment_id(tpAniSirGlobal pCtx, + tDot11fIEbeacon_report_frm_body_fragment_id *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp126__; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 2; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp126__ = 0U; + tmp126__ |= (pSrc->beacon_report_id << 0); + tmp126__ |= (pSrc->fragment_id_number << 8); + tmp126__ |= (pSrc->more_fragments << 15); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp126__, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + nBuf -= 2 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_beacon_report_frm_body_fragment_id. */ + +uint32_t dot11f_pack_ie_last_beacon_report_indication(tpAniSirGlobal pCtx, + tDot11fIElast_beacon_report_indication *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 164; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->last_fragment; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_last_beacon_report_indication. */ + +uint32_t dot11f_pack_ie_max_age(tpAniSirGlobal pCtx, + tDot11fIEmax_age *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 4; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->max_age, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_max_age. */ + +uint32_t dot11f_pack_ie_neighbor_rpt(tpAniSirGlobal pCtx, + tDot11fIEneighbor_rpt *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp127__; + uint8_t tmp128__; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_neighbor_rpt(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 52; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->bssid, 6); + *pnConsumed += 6; + pBuf += 6; + tmp127__ = 0U; + tmp127__ |= (pSrc->APReachability << 0); + tmp127__ |= (pSrc->Security << 2); + tmp127__ |= (pSrc->KeyScope << 3); + tmp127__ |= (pSrc->SpecMgmtCap << 4); + tmp127__ |= (pSrc->QosCap << 5); + tmp127__ |= (pSrc->apsd << 6); + tmp127__ |= (pSrc->rrm << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp127__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp128__ = 0U; + tmp128__ |= (pSrc->DelayedBA << 0); + tmp128__ |= (pSrc->ImmBA << 1); + tmp128__ |= (pSrc->MobilityDomain << 2); + tmp128__ |= (pSrc->reserved << 3); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp128__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->reserved1, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->regulatoryClass; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->channel; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->PhyType; + *pnConsumed += 1; + pBuf += 1; + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_neighbor_rpt, + IES_neighbor_rpt); + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_neighbor_rpt. */ + +uint32_t dot11f_pack_ie_req_mac_addr(tpAniSirGlobal pCtx, + tDot11fIEreq_mac_addr *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 2; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->addr, 6); + *pnConsumed += 6; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_req_mac_addr. */ + +uint32_t dot11f_pack_ie_tgt_mac_addr(tpAniSirGlobal pCtx, + tDot11fIEtgt_mac_addr *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 3; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->addr, 6); + *pnConsumed += 6; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_tgt_mac_addr. */ + +uint32_t dot11f_pack_ie_vht_transmit_power_env(tpAniSirGlobal pCtx, + tDot11fIEvht_transmit_power_env *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_bytes; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 195; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->bytes), pSrc->num_bytes); + *pnConsumed += pSrc->num_bytes; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_vht_transmit_power_env. */ + +uint32_t dot11f_pack_ie_aid(tpAniSirGlobal pCtx, + tDot11fIEAID *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 197; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->assocId, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_aid. */ + +uint32_t dot11f_pack_ie_BeaconReportStatus(tpAniSirGlobal pCtx, + tDot11fIEBeaconReportStatus *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x22; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->sub_type; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->length; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->reason_code; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_BeaconReportStatus. */ + +uint32_t dot11f_pack_ie_cf_params(tpAniSirGlobal pCtx, + tDot11fIECFParams *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 4; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->cfp_count; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->cfp_period; + *pnConsumed += 1; + pBuf += 1; + frameshtons(pCtx, pBuf, pSrc->cfp_maxduration, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->cfp_durremaining, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_cf_params. */ + +uint32_t dot11f_pack_ie_challenge_text(tpAniSirGlobal pCtx, + tDot11fIEChallengeText *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_text; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 16; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->text), pSrc->num_text); + *pnConsumed += pSrc->num_text; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_challenge_text. */ + +uint32_t dot11f_pack_ie_chan_switch_ann(tpAniSirGlobal pCtx, + tDot11fIEChanSwitchAnn *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 37; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->switchMode; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->newChannel; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->switchCount; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_chan_switch_ann. */ + +uint32_t dot11f_pack_ie_channel_switch_wrapper(tpAniSirGlobal pCtx, + tDot11fIEChannelSwitchWrapper *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_channel_switch_wrapper(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 196; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_ChannelSwitchWrapper, + IES_ChannelSwitchWrapper); + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_channel_switch_wrapper. */ + +uint32_t dot11f_pack_ie_country(tpAniSirGlobal pCtx, + tDot11fIECountry *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_country(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 7; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->country, 3); + *pnConsumed += 3; + pBuf += 3; + if (pSrc->num_triplets) { + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->triplets), (pSrc->num_triplets * 3)); + *pnConsumed += (pSrc->num_triplets * 3); + /* fieldsEndFlag = 1 */ + } else { + break; + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_country. */ + +uint32_t dot11f_pack_ie_ds_params(tpAniSirGlobal pCtx, + tDot11fIEDSParams *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 3; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->curr_channel; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ds_params. */ + +uint32_t dot11f_pack_ie_edca_param_set(tpAniSirGlobal pCtx, + tDot11fIEEDCAParamSet *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp129__; + uint8_t tmp130__; + uint8_t tmp131__; + uint8_t tmp132__; + uint8_t tmp133__; + uint8_t tmp134__; + uint8_t tmp135__; + uint8_t tmp136__; + nNeeded += 18; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 12; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->qos; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->reserved; + *pnConsumed += 1; + pBuf += 1; + tmp129__ = 0U; + tmp129__ |= (pSrc->acbe_aifsn << 0); + tmp129__ |= (pSrc->acbe_acm << 4); + tmp129__ |= (pSrc->acbe_aci << 5); + tmp129__ |= (pSrc->unused1 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp129__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp130__ = 0U; + tmp130__ |= (pSrc->acbe_acwmin << 0); + tmp130__ |= (pSrc->acbe_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp130__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->acbe_txoplimit, 0); + *pnConsumed += 2; + pBuf += 2; + tmp131__ = 0U; + tmp131__ |= (pSrc->acbk_aifsn << 0); + tmp131__ |= (pSrc->acbk_acm << 4); + tmp131__ |= (pSrc->acbk_aci << 5); + tmp131__ |= (pSrc->unused2 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp131__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp132__ = 0U; + tmp132__ |= (pSrc->acbk_acwmin << 0); + tmp132__ |= (pSrc->acbk_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp132__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->acbk_txoplimit, 0); + *pnConsumed += 2; + pBuf += 2; + tmp133__ = 0U; + tmp133__ |= (pSrc->acvi_aifsn << 0); + tmp133__ |= (pSrc->acvi_acm << 4); + tmp133__ |= (pSrc->acvi_aci << 5); + tmp133__ |= (pSrc->unused3 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp133__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp134__ = 0U; + tmp134__ |= (pSrc->acvi_acwmin << 0); + tmp134__ |= (pSrc->acvi_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp134__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->acvi_txoplimit, 0); + *pnConsumed += 2; + pBuf += 2; + tmp135__ = 0U; + tmp135__ |= (pSrc->acvo_aifsn << 0); + tmp135__ |= (pSrc->acvo_acm << 4); + tmp135__ |= (pSrc->acvo_aci << 5); + tmp135__ |= (pSrc->unused4 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp135__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp136__ = 0U; + tmp136__ |= (pSrc->acvo_acwmin << 0); + tmp136__ |= (pSrc->acvo_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp136__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->acvo_txoplimit, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_edca_param_set. */ + +uint32_t dot11f_pack_ie_erp_info(tpAniSirGlobal pCtx, + tDot11fIEERPInfo *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp137__; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 42; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp137__ = 0U; + tmp137__ |= (pSrc->non_erp_present << 0); + tmp137__ |= (pSrc->use_prot << 1); + tmp137__ |= (pSrc->barker_preamble << 2); + tmp137__ |= (pSrc->unused << 3); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp137__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_erp_info. */ + +uint32_t dot11f_pack_ie_ese_cckm_opaque(tpAniSirGlobal pCtx, + tDot11fIEESECckmOpaque *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 156; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x40; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x96; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ese_cckm_opaque. */ + +uint32_t dot11f_pack_ie_ese_rad_mgmt_cap(tpAniSirGlobal pCtx, + tDot11fIEESERadMgmtCap *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp138__; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x40; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x96; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x1; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->mgmt_state; + *pnConsumed += 1; + pBuf += 1; + tmp138__ = 0U; + tmp138__ |= (pSrc->mbssid_mask << 0); + tmp138__ |= (pSrc->reserved << 3); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp138__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ese_rad_mgmt_cap. */ + +uint32_t dot11f_pack_ie_ese_traf_strm_met(tpAniSirGlobal pCtx, + tDot11fIEESETrafStrmMet *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x40; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x96; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x7; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->tsid; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->state; + *pnConsumed += 1; + pBuf += 1; + frameshtons(pCtx, pBuf, pSrc->msmt_interval, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ese_traf_strm_met. */ + +uint32_t dot11f_pack_ie_ese_traf_strm_rate_set(tpAniSirGlobal pCtx, + tDot11fIEESETrafStrmRateSet *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_tsrates + 1); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x40; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x96; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x8; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->tsid; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->tsrates), pSrc->num_tsrates); + *pnConsumed += pSrc->num_tsrates; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ese_traf_strm_rate_set. */ + +uint32_t dot11f_pack_ie_ese_txmit_power(tpAniSirGlobal pCtx, + tDot11fIEESETxmitPower *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 150; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x40; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x96; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->power_limit; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->reserved; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ese_txmit_power. */ + +uint32_t dot11f_pack_ie_ese_version(tpAniSirGlobal pCtx, + tDot11fIEESEVersion *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x40; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x96; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x3; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ese_version. */ + +uint32_t dot11f_pack_ie_ext_cap(tpAniSirGlobal pCtx, + tDot11fIEExtCap *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_bytes; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 127; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->bytes), pSrc->num_bytes); + *pnConsumed += pSrc->num_bytes; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ext_cap. */ + +uint32_t dot11f_pack_ie_ext_supp_rates(tpAniSirGlobal pCtx, + tDot11fIEExtSuppRates *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_rates; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 50; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->rates), pSrc->num_rates); + *pnConsumed += pSrc->num_rates; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ext_supp_rates. */ + +uint32_t dot11f_pack_ie_fh_param_set(tpAniSirGlobal pCtx, + tDot11fIEFHParamSet *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 2; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->dwell_time, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->hop_set; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->hop_pattern; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->hop_index; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fh_param_set. */ + +uint32_t dot11f_pack_ie_fh_params(tpAniSirGlobal pCtx, + tDot11fIEFHParams *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 8; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->radix; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->nchannels; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fh_params. */ + +uint32_t dot11f_pack_ie_fh_patt_table(tpAniSirGlobal pCtx, + tDot11fIEFHPattTable *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_randtable + 4); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 9; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->flag; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->nsets; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->modulus; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->offset; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->randtable), pSrc->num_randtable); + *pnConsumed += pSrc->num_randtable; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fh_patt_table. */ + +uint32_t dot11f_pack_ie_ft_info(tpAniSirGlobal pCtx, + tDot11fIEFTInfo *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp139__; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ieft_info(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 55; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + tmp139__ = 0U; + tmp139__ |= (pSrc->reserved << 0); + tmp139__ |= (pSrc->IECount << 8); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp139__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->MIC, 16); + *pnConsumed += 16; + pBuf += 16; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->Anonce, 32); + *pnConsumed += 32; + pBuf += 32; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->Snonce, 32); + *pnConsumed += 32; + pBuf += 32; + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_FTInfo, + IES_FTInfo); + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_ft_info. */ + +uint32_t dot11f_pack_ie_ht_caps(tpAniSirGlobal pCtx, + tDot11fIEHTCaps *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp140__; + uint8_t tmp141__; + uint16_t tmp142__; + uint32_t tmp143__; + uint8_t tmp144__; + nNeeded += (pSrc->num_rsvd + 26); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 45; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp140__ = 0U; + tmp140__ |= (pSrc->advCodingCap << 0); + tmp140__ |= (pSrc->supportedChannelWidthSet << 1); + tmp140__ |= (pSrc->mimoPowerSave << 2); + tmp140__ |= (pSrc->greenField << 4); + tmp140__ |= (pSrc->shortGI20MHz << 5); + tmp140__ |= (pSrc->shortGI40MHz << 6); + tmp140__ |= (pSrc->txSTBC << 7); + tmp140__ |= (pSrc->rxSTBC << 8); + tmp140__ |= (pSrc->delayedBA << 10); + tmp140__ |= (pSrc->maximalAMSDUsize << 11); + tmp140__ |= (pSrc->dsssCckMode40MHz << 12); + tmp140__ |= (pSrc->psmp << 13); + tmp140__ |= (pSrc->stbcControlFrame << 14); + tmp140__ |= (pSrc->lsigTXOPProtection << 15); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp140__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + tmp141__ = 0U; + tmp141__ |= (pSrc->maxRxAMPDUFactor << 0); + tmp141__ |= (pSrc->mpduDensity << 2); + tmp141__ |= (pSrc->reserved1 << 5); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp141__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->supportedMCSSet, 16); + *pnConsumed += 16; + pBuf += 16; + tmp142__ = 0U; + tmp142__ |= (pSrc->pco << 0); + tmp142__ |= (pSrc->transitionTime << 1); + tmp142__ |= (pSrc->reserved2 << 3); + tmp142__ |= (pSrc->mcsFeedback << 8); + tmp142__ |= (pSrc->reserved3 << 10); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp142__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + tmp143__ = 0U; + tmp143__ |= (pSrc->txBF << 0); + tmp143__ |= (pSrc->rxStaggeredSounding << 1); + tmp143__ |= (pSrc->txStaggeredSounding << 2); + tmp143__ |= (pSrc->rxZLF << 3); + tmp143__ |= (pSrc->txZLF << 4); + tmp143__ |= (pSrc->implicitTxBF << 5); + tmp143__ |= (pSrc->calibration << 6); + tmp143__ |= (pSrc->explicitCSITxBF << 8); + tmp143__ |= (pSrc->explicitUncompressedSteeringMatrix << 9); + tmp143__ |= (pSrc->explicitBFCSIFeedback << 10); + tmp143__ |= (pSrc->explicitUncompressedSteeringMatrixFeedback << 13); + tmp143__ |= (pSrc->explicitCompressedSteeringMatrixFeedback << 16); + tmp143__ |= (pSrc->csiNumBFAntennae << 19); + tmp143__ |= (pSrc->uncompressedSteeringMatrixBFAntennae << 21); + tmp143__ |= (pSrc->compressedSteeringMatrixBFAntennae << 23); + tmp143__ |= (pSrc->reserved4 << 25); + if (unlikely(nBuf < 4)) + return DOT11F_INCOMPLETE_IE; + + frameshtonl(pCtx, pBuf, tmp143__, 0); + *pnConsumed += 4; + pBuf += 4; + nBuf -= 4 ; + tmp144__ = 0U; + tmp144__ |= (pSrc->antennaSelection << 0); + tmp144__ |= (pSrc->explicitCSIFeedbackTx << 1); + tmp144__ |= (pSrc->antennaIndicesFeedbackTx << 2); + tmp144__ |= (pSrc->explicitCSIFeedback << 3); + tmp144__ |= (pSrc->antennaIndicesFeedback << 4); + tmp144__ |= (pSrc->rxAS << 5); + tmp144__ |= (pSrc->txSoundingPPDUs << 6); + tmp144__ |= (pSrc->reserved5 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp144__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->rsvd), pSrc->num_rsvd); + *pnConsumed += pSrc->num_rsvd; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ht_caps. */ + +uint32_t dot11f_pack_ie_ht_info(tpAniSirGlobal pCtx, + tDot11fIEHTInfo *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp145__; + uint16_t tmp146__; + uint16_t tmp147__; + nNeeded += (pSrc->num_rsvd + 22); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 61; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->primaryChannel; + *pnConsumed += 1; + pBuf += 1; + tmp145__ = 0U; + tmp145__ |= (pSrc->secondaryChannelOffset << 0); + tmp145__ |= (pSrc->recommendedTxWidthSet << 2); + tmp145__ |= (pSrc->rifsMode << 3); + tmp145__ |= (pSrc->controlledAccessOnly << 4); + tmp145__ |= (pSrc->serviceIntervalGranularity << 5); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp145__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp146__ = 0U; + tmp146__ |= (pSrc->opMode << 0); + tmp146__ |= (pSrc->nonGFDevicesPresent << 2); + tmp146__ |= (pSrc->transmitBurstLimit << 3); + tmp146__ |= (pSrc->obssNonHTStaPresent << 4); + tmp146__ |= (pSrc->chan_center_freq_seg2 << 5); + tmp146__ |= (pSrc->reserved << 13); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp146__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + tmp147__ = 0U; + tmp147__ |= (pSrc->basicSTBCMCS << 0); + tmp147__ |= (pSrc->dualCTSProtection << 7); + tmp147__ |= (pSrc->secondaryBeacon << 8); + tmp147__ |= (pSrc->lsigTXOPProtectionFullSupport << 9); + tmp147__ |= (pSrc->pcoActive << 10); + tmp147__ |= (pSrc->pcoPhase << 11); + tmp147__ |= (pSrc->reserved2 << 12); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp147__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->basicMCSSet, 16); + *pnConsumed += 16; + pBuf += 16; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->rsvd), pSrc->num_rsvd); + *pnConsumed += pSrc->num_rsvd; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ht_info. */ + +uint32_t dot11f_pack_ie_ibss_params(tpAniSirGlobal pCtx, + tDot11fIEIBSSParams *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 6; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->atim, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ibss_params. */ + +uint32_t dot11f_pack_ie_link_identifier(tpAniSirGlobal pCtx, + tDot11fIELinkIdentifier *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 18; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 101; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->bssid, 6); + *pnConsumed += 6; + pBuf += 6; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->InitStaAddr, 6); + *pnConsumed += 6; + pBuf += 6; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->RespStaAddr, 6); + *pnConsumed += 6; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_link_identifier. */ + +uint32_t dot11f_pack_ie_MBO_IE(tpAniSirGlobal pCtx, + tDot11fIEMBO_IE *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_ie_MBO_IE(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x16; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_MBO_IE + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_MBO_IE. */ + +uint32_t dot11f_pack_ie_measurement_report(tpAniSirGlobal pCtx, + tDot11fIEMeasurementReport *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp148__; + uint8_t tmp149__; + uint8_t tmp150__; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_measurement_report(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 39; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->token; + *pnConsumed += 1; + pBuf += 1; + tmp148__ = 0U; + tmp148__ |= (pSrc->late << 0); + tmp148__ |= (pSrc->incapable << 1); + tmp148__ |= (pSrc->refused << 2); + tmp148__ |= (pSrc->unused << 3); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp148__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + *pBuf = pSrc->type; + *pnConsumed += 1; + pBuf += 1; + if (pSrc->type) { + switch (pSrc->type) { + case 0: + *pBuf = pSrc->report.Basic.channel; + *pnConsumed += 1; + pBuf += 1; + frameshtonq(pCtx, pBuf, pSrc->report.Basic.meas_start_time, 0); + *pnConsumed += 8; + pBuf += 8; + frameshtons(pCtx, pBuf, pSrc->report.Basic.meas_duration, 0); + *pnConsumed += 2; + pBuf += 2; + tmp149__ = 0U; + tmp149__ |= (pSrc->report.Basic.bss << 0); + tmp149__ |= (pSrc->report.Basic.ofdm_preamble << 1); + tmp149__ |= (pSrc->report.Basic.unid_signal << 2); + tmp149__ |= (pSrc->report.Basic.rader << 3); + tmp149__ |= (pSrc->report.Basic.unmeasured << 4); + tmp149__ |= (pSrc->report.Basic.unused << 5); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp149__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + case 1: + *pBuf = pSrc->report.CCA.channel; + *pnConsumed += 1; + pBuf += 1; + frameshtonq(pCtx, pBuf, pSrc->report.CCA.meas_start_time, 0); + *pnConsumed += 8; + pBuf += 8; + frameshtons(pCtx, pBuf, pSrc->report.CCA.meas_duration, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->report.CCA.cca_busy_fraction; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + case 2: + *pBuf = pSrc->report.RPIHistogram.channel; + *pnConsumed += 1; + pBuf += 1; + frameshtonq(pCtx, pBuf, pSrc->report.RPIHistogram.meas_start_time, 0); + *pnConsumed += 8; + pBuf += 8; + frameshtons(pCtx, pBuf, pSrc->report.RPIHistogram.meas_duration, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->report.RPIHistogram.rpi0_density; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->report.RPIHistogram.rpi1_density; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->report.RPIHistogram.rpi2_density; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->report.RPIHistogram.rpi3_density; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->report.RPIHistogram.rpi4_density; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->report.RPIHistogram.rpi5_density; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->report.RPIHistogram.rpi6_density; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->report.RPIHistogram.rpi7_density; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + case 5: + *pBuf = pSrc->report.Beacon.regClass; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->report.Beacon.channel; + *pnConsumed += 1; + pBuf += 1; + frameshtonq(pCtx, pBuf, pSrc->report.Beacon.meas_start_time, 0); + *pnConsumed += 8; + pBuf += 8; + frameshtons(pCtx, pBuf, pSrc->report.Beacon.meas_duration, 0); + *pnConsumed += 2; + pBuf += 2; + tmp150__ = 0U; + tmp150__ |= (pSrc->report.Beacon.condensed_PHY << 0); + tmp150__ |= (pSrc->report.Beacon.reported_frame_type << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp150__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + *pBuf = pSrc->report.Beacon.RCPI; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->report.Beacon.RSNI; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->report.Beacon.BSSID, 6); + *pnConsumed += 6; + pBuf += 6; + *pBuf = pSrc->report.Beacon.antenna_id; + *pnConsumed += 1; + pBuf += 1; + frameshtonl(pCtx, pBuf, pSrc->report.Beacon.parent_TSF, 0); + *pnConsumed += 4; + pBuf += 4; + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_reportBeacon, + IES_reportBeacon); + break; + } + } else { + break; + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_measurement_report. */ + +uint32_t dot11f_pack_ie_measurement_request(tpAniSirGlobal pCtx, + tDot11fIEMeasurementRequest *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp151__; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_measurement_request(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 38; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->measurement_token; + *pnConsumed += 1; + pBuf += 1; + tmp151__ = 0U; + tmp151__ |= (pSrc->parallel << 0); + tmp151__ |= (pSrc->enable << 1); + tmp151__ |= (pSrc->request << 2); + tmp151__ |= (pSrc->report << 3); + tmp151__ |= (pSrc->durationMandatory << 4); + tmp151__ |= (pSrc->unused << 5); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp151__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + *pBuf = pSrc->measurement_type; + *pnConsumed += 1; + pBuf += 1; + switch (pSrc->measurement_type) { + case 0: + *pBuf = pSrc->measurement_request.Basic.channel_no; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->measurement_request.Basic.meas_start_time, 8); + *pnConsumed += 8; + pBuf += 8; + frameshtons(pCtx, pBuf, pSrc->measurement_request.Basic.meas_duration, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + case 1: + *pBuf = pSrc->measurement_request.CCA.channel_no; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->measurement_request.CCA.meas_start_time, 8); + *pnConsumed += 8; + pBuf += 8; + frameshtons(pCtx, pBuf, pSrc->measurement_request.CCA.meas_duration, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + case 2: + *pBuf = pSrc->measurement_request.RPIHistogram.channel_no; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->measurement_request.RPIHistogram.meas_start_time, 8); + *pnConsumed += 8; + pBuf += 8; + frameshtons(pCtx, pBuf, pSrc->measurement_request.RPIHistogram.meas_duration, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + case 5: + *pBuf = pSrc->measurement_request.Beacon.regClass; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->measurement_request.Beacon.channel; + *pnConsumed += 1; + pBuf += 1; + frameshtons(pCtx, pBuf, pSrc->measurement_request.Beacon.randomization, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->measurement_request.Beacon.meas_duration, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->measurement_request.Beacon.meas_mode; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->measurement_request.Beacon.BSSID, 6); + *pnConsumed += 6; + pBuf += 6; + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_measurement_requestBeacon, + IES_measurement_requestBeacon); + break; + case 8: + *pBuf = pSrc->measurement_request.lci.loc_subject; + *pnConsumed += 1; + pBuf += 1; + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_measurement_requestlci, + IES_measurement_requestlci); + break; + case 16: + frameshtons(pCtx, pBuf, pSrc->measurement_request.ftmrr.random_interval, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->measurement_request.ftmrr.min_ap_count; + *pnConsumed += 1; + pBuf += 1; + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_measurement_requestftmrr, + IES_measurement_requestftmrr); + break; + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_measurement_request. */ + +uint32_t dot11f_pack_ie_mobility_domain(tpAniSirGlobal pCtx, + tDot11fIEMobilityDomain *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp152__; + nNeeded += 3; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 54; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->MDID, 0); + *pnConsumed += 2; + pBuf += 2; + tmp152__ = 0U; + tmp152__ |= (pSrc->overDSCap << 0); + tmp152__ |= (pSrc->resourceReqCap << 1); + tmp152__ |= (pSrc->reserved << 2); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp152__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_mobility_domain. */ + +uint32_t dot11f_pack_ie_neighbor_report(tpAniSirGlobal pCtx, + tDot11fIENeighborReport *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp153__; + uint8_t tmp154__; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_neighbor_report(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 52; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->bssid, 6); + *pnConsumed += 6; + pBuf += 6; + tmp153__ = 0U; + tmp153__ |= (pSrc->APReachability << 0); + tmp153__ |= (pSrc->Security << 2); + tmp153__ |= (pSrc->KeyScope << 3); + tmp153__ |= (pSrc->SpecMgmtCap << 4); + tmp153__ |= (pSrc->QosCap << 5); + tmp153__ |= (pSrc->apsd << 6); + tmp153__ |= (pSrc->rrm << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp153__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp154__ = 0U; + tmp154__ |= (pSrc->DelayedBA << 0); + tmp154__ |= (pSrc->ImmBA << 1); + tmp154__ |= (pSrc->MobilityDomain << 2); + tmp154__ |= (pSrc->reserved << 3); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp154__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->reserved1, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->regulatoryClass; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->channel; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->PhyType; + *pnConsumed += 1; + pBuf += 1; + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_NeighborReport, + IES_NeighborReport); + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_neighbor_report. */ + +uint32_t dot11f_pack_ie_obss_scan_parameters(tpAniSirGlobal pCtx, + tDot11fIEOBSSScanParameters *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 14; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 74; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->obssScanPassiveDwell, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->obssScanActiveDwell, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->bssChannelWidthTriggerScanInterval, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->obssScanPassiveTotalPerChannel, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->obssScanActiveTotalPerChannel, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->bssWidthChannelTransitionDelayFactor, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->obssScanActivityThreshold, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_obss_scan_parameters. */ + +uint32_t dot11f_pack_ie_operating_mode(tpAniSirGlobal pCtx, + tDot11fIEOperatingMode *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp155__; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 199; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp155__ = 0U; + tmp155__ |= (pSrc->chanWidth << 0); + tmp155__ |= (pSrc->vht_160_80p80_supp << 2); + tmp155__ |= (pSrc->no_ldpc << 3); + tmp155__ |= (pSrc->rxNSS << 4); + tmp155__ |= (pSrc->rxNSSType << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp155__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_operating_mode. */ + +uint32_t dot11f_pack_ie_p2_p_assoc_req(tpAniSirGlobal pCtx, + tDot11fIEP2PAssocReq *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_iep2_p_assoc_req(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_P2PAssocReq + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_p2_p_assoc_req. */ + +uint32_t dot11f_pack_ie_p2_p_assoc_res(tpAniSirGlobal pCtx, + tDot11fIEP2PAssocRes *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_iep2_p_assoc_res(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_P2PAssocRes + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_p2_p_assoc_res. */ + +uint32_t dot11f_pack_ie_p2_p_beacon(tpAniSirGlobal pCtx, + tDot11fIEP2PBeacon *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_iep2_p_beacon(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_P2PBeacon + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_p2_p_beacon. */ + +uint32_t dot11f_pack_ie_p2_p_beacon_probe_res(tpAniSirGlobal pCtx, + tDot11fIEP2PBeaconProbeRes *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_iep2_p_beacon_probe_res(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_P2PBeaconProbeRes + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_p2_p_beacon_probe_res. */ + +uint32_t dot11f_pack_ie_p2_p_de_auth(tpAniSirGlobal pCtx, + tDot11fIEP2PDeAuth *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_iep2_p_de_auth(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_P2PDeAuth + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_p2_p_de_auth. */ + +uint32_t dot11f_pack_ie_p2_p_dis_assoc(tpAniSirGlobal pCtx, + tDot11fIEP2PDisAssoc *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_iep2_p_dis_assoc(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_P2PDisAssoc + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_p2_p_dis_assoc. */ + +uint32_t dot11f_pack_ie_p2_pie_opaque(tpAniSirGlobal pCtx, + tDot11fIEP2PIEOpaque *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_p2_pie_opaque. */ + +uint32_t dot11f_pack_ie_p2_p_probe_req(tpAniSirGlobal pCtx, + tDot11fIEP2PProbeReq *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_iep2_p_probe_req(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_P2PProbeReq + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_p2_p_probe_req. */ + +uint32_t dot11f_pack_ie_p2_p_probe_res(tpAniSirGlobal pCtx, + tDot11fIEP2PProbeRes *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_iep2_p_probe_res(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x9; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_P2PProbeRes + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_p2_p_probe_res. */ + +uint32_t dot11f_pack_ie_pti_control(tpAniSirGlobal pCtx, + tDot11fIEPTIControl *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 3; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 105; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->tid; + *pnConsumed += 1; + pBuf += 1; + frameshtons(pCtx, pBuf, pSrc->sequence_control, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_pti_control. */ + +uint32_t dot11f_pack_ie_pu_buffer_status(tpAniSirGlobal pCtx, + tDot11fIEPUBufferStatus *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp156__; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 106; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp156__ = 0U; + tmp156__ |= (pSrc->ac_bk_traffic_aval << 0); + tmp156__ |= (pSrc->ac_be_traffic_aval << 1); + tmp156__ |= (pSrc->ac_vi_traffic_aval << 2); + tmp156__ |= (pSrc->ac_vo_traffic_aval << 3); + tmp156__ |= (pSrc->reserved << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp156__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_pu_buffer_status. */ + +uint32_t dot11f_pack_ie_power_caps(tpAniSirGlobal pCtx, + tDot11fIEPowerCaps *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 33; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->minTxPower; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->maxTxPower; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_power_caps. */ + +uint32_t dot11f_pack_ie_power_constraints(tpAniSirGlobal pCtx, + tDot11fIEPowerConstraints *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 32; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->localPowerConstraints; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_power_constraints. */ + +uint32_t dot11f_pack_ie_qbss_load(tpAniSirGlobal pCtx, + tDot11fIEQBSSLoad *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 11; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->stacount, 0); + *pnConsumed += 2; + pBuf += 2; + *pBuf = pSrc->chautil; + *pnConsumed += 1; + pBuf += 1; + frameshtons(pCtx, pBuf, pSrc->avail, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_qbss_load. */ + +uint32_t dot11f_pack_ie_QComVendorIE(tpAniSirGlobal pCtx, + tDot11fIEQComVendorIE *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xa0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xc6; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->type; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->channel; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_QComVendorIE. */ + +uint32_t dot11f_pack_ie_qos_caps_ap(tpAniSirGlobal pCtx, + tDot11fIEQOSCapsAp *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp157__; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 46; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp157__ = 0U; + tmp157__ |= (pSrc->count << 0); + tmp157__ |= (pSrc->qack << 4); + tmp157__ |= (pSrc->qreq << 5); + tmp157__ |= (pSrc->txopreq << 6); + tmp157__ |= (pSrc->reserved << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp157__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_qos_caps_ap. */ + +uint32_t dot11f_pack_ie_qos_caps_station(tpAniSirGlobal pCtx, + tDot11fIEQOSCapsStation *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp158__; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 46; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp158__ = 0U; + tmp158__ |= (pSrc->acvo_uapsd << 0); + tmp158__ |= (pSrc->acvi_uapsd << 1); + tmp158__ |= (pSrc->acbk_uapsd << 2); + tmp158__ |= (pSrc->acbe_uapsd << 3); + tmp158__ |= (pSrc->qack << 4); + tmp158__ |= (pSrc->max_sp_length << 5); + tmp158__ |= (pSrc->more_data_ack << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp158__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_qos_caps_station. */ + +uint32_t dot11f_pack_ie_qos_map_set(tpAniSirGlobal pCtx, + tDot11fIEQosMapSet *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_dscp_exceptions; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 110; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->dscp_exceptions), pSrc->num_dscp_exceptions); + *pnConsumed += pSrc->num_dscp_exceptions; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_qos_map_set. */ + +uint32_t dot11f_pack_ie_quiet(tpAniSirGlobal pCtx, + tDot11fIEQuiet *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 6; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 40; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->count; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->period; + *pnConsumed += 1; + pBuf += 1; + frameshtons(pCtx, pBuf, pSrc->duration, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->offset, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_quiet. */ + +uint32_t dot11f_pack_ie_rcpiie(tpAniSirGlobal pCtx, + tDot11fIERCPIIE *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 53; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->rcpi; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_rcpiie. */ + +uint32_t dot11f_pack_ie_ric_data_desc(tpAniSirGlobal pCtx, + tDot11fIERICDataDesc *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint32_t nNeeded = 0U; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ieric_data_desc(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_RICDataDesc, + IES_RICDataDesc); + break; + } + (void)pCtx; + return status; +} /* End dot11f_pack_ie_ric_data_desc. */ + +uint32_t dot11f_pack_ie_rsn(tpAniSirGlobal pCtx, + tDot11fIERSN *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_iersn(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 48; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->version, 0); + *pnConsumed += 2; + pBuf += 2; + if (pSrc->gp_cipher_suite_present) { + DOT11F_MEMCPY(pCtx, pBuf, pSrc->gp_cipher_suite, 4); + *pnConsumed += 4; + pBuf += 4; + } else { + break; + } + if (pSrc->pwise_cipher_suite_count) { + frameshtons(pCtx, pBuf, pSrc->pwise_cipher_suite_count, 0); + *pnConsumed += 2; + pBuf += 2; + } else { + break; + } + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->pwise_cipher_suites), (pSrc->pwise_cipher_suite_count * 4)); + *pnConsumed += (pSrc->pwise_cipher_suite_count * 4); + pBuf += (pSrc->pwise_cipher_suite_count * 4); + if (pSrc->akm_suite_cnt) { + frameshtons(pCtx, pBuf, pSrc->akm_suite_cnt, 0); + *pnConsumed += 2; + pBuf += 2; + } else { + break; + } + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->akm_suite), (pSrc->akm_suite_cnt * 4)); + *pnConsumed += (pSrc->akm_suite_cnt * 4); + pBuf += (pSrc->akm_suite_cnt * 4); + if (pSrc->RSN_Cap_present) { + DOT11F_MEMCPY(pCtx, pBuf, pSrc->RSN_Cap, 2); + *pnConsumed += 2; + pBuf += 2; + } else { + break; + } + if (pSrc->pmkid_count) { + frameshtons(pCtx, pBuf, pSrc->pmkid_count, 0); + *pnConsumed += 2; + pBuf += 2; + } else { + if (pSrc->gp_mgmt_cipher_suite_present) { + frameshtons(pCtx, pBuf, pSrc->pmkid_count, 0); + *pnConsumed += 2; + pBuf += 2; + } + } + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->pmkid), (pSrc->pmkid_count * 16)); + *pnConsumed += (pSrc->pmkid_count * 16); + pBuf += (pSrc->pmkid_count * 16); + if (pSrc->gp_mgmt_cipher_suite_present) { + DOT11F_MEMCPY(pCtx, pBuf, pSrc->gp_mgmt_cipher_suite, 4); + *pnConsumed += 4; + /* fieldsEndFlag = 1 */ + } else { + break; + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_rsn. */ + +uint32_t dot11f_pack_ie_rsniie(tpAniSirGlobal pCtx, + tDot11fIERSNIIE *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 65; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->rsni; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_rsniie. */ + +uint32_t dot11f_pack_ie_rsn_opaque(tpAniSirGlobal pCtx, + tDot11fIERSNOpaque *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 48; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_rsn_opaque. */ + +uint32_t dot11f_pack_ie_supp_channels(tpAniSirGlobal pCtx, + tDot11fIESuppChannels *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_bands * 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 36; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->bands), (pSrc->num_bands * 2)); + *pnConsumed += (pSrc->num_bands * 2); + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_supp_channels. */ + +uint32_t dot11f_pack_ie_supp_operating_classes(tpAniSirGlobal pCtx, + tDot11fIESuppOperatingClasses *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_classes; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 59; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->classes), pSrc->num_classes); + *pnConsumed += pSrc->num_classes; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_supp_operating_classes. */ + +uint32_t dot11f_pack_ie_supp_rates(tpAniSirGlobal pCtx, + tDot11fIESuppRates *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_rates; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 1; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->rates), pSrc->num_rates); + *pnConsumed += pSrc->num_rates; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_supp_rates. */ + +uint32_t dot11f_pack_ie_tim(tpAniSirGlobal pCtx, + tDot11fIETIM *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_vbmp + 3); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 5; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->dtim_count; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->dtim_period; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->bmpctl; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->vbmp), pSrc->num_vbmp); + *pnConsumed += pSrc->num_vbmp; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_tim. */ + +uint32_t dot11f_pack_ie_tpc_report(tpAniSirGlobal pCtx, + tDot11fIETPCReport *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 35; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->tx_power; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->link_margin; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_tpc_report. */ + +uint32_t dot11f_pack_ie_tpc_request(tpAniSirGlobal pCtx, + tDot11fIETPCRequest *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 0; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 34; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_tpc_request. */ + +uint32_t dot11f_pack_ie_time_advertisement(tpAniSirGlobal pCtx, + tDot11fIETimeAdvertisement *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 16; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 69; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->timing_capabilities; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->time_value, 10); + *pnConsumed += 10; + pBuf += 10; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->time_error, 5); + *pnConsumed += 5; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_time_advertisement. */ + +uint32_t dot11f_pack_ie_timeout_interval(tpAniSirGlobal pCtx, + tDot11fIETimeoutInterval *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 56; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->timeoutType; + *pnConsumed += 1; + pBuf += 1; + frameshtonl(pCtx, pBuf, pSrc->timeoutValue, 0); + *pnConsumed += 4; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_timeout_interval. */ + +uint32_t dot11f_pack_ie_vht_ext_bss_load(tpAniSirGlobal pCtx, + tDot11fIEVHTExtBssLoad *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 5; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 193; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->muMIMOCapStaCount; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->ssUnderUtil; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->FortyMHzUtil; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->EightyMHzUtil; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->OneSixtyMHzUtil; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_vht_ext_bss_load. */ + +uint32_t dot11f_pack_ie_vendor1_ie(tpAniSirGlobal pCtx, + tDot11fIEVendor1IE *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 0; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x10; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x18; + ++pBuf; ++(*pnConsumed); + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_vendor1_ie. */ + +uint32_t dot11f_pack_ie_vendor3_ie(tpAniSirGlobal pCtx, + tDot11fIEVendor3IE *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 0; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x16; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x32; + ++pBuf; ++(*pnConsumed); + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_vendor3_ie. */ + +uint32_t dot11f_pack_ie_wapi(tpAniSirGlobal pCtx, + tDot11fIEWAPI *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp159__; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_iewapi(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 68; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->version, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->akm_suite_count, 0); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->akm_suites), (pSrc->akm_suite_count * 4)); + *pnConsumed += (pSrc->akm_suite_count * 4); + pBuf += (pSrc->akm_suite_count * 4); + frameshtons(pCtx, pBuf, pSrc->unicast_cipher_suite_count, 0); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->unicast_cipher_suites), (pSrc->unicast_cipher_suite_count * 4)); + *pnConsumed += (pSrc->unicast_cipher_suite_count * 4); + pBuf += (pSrc->unicast_cipher_suite_count * 4); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->multicast_cipher_suite, 4); + *pnConsumed += 4; + pBuf += 4; + tmp159__ = 0U; + tmp159__ |= (pSrc->preauth << 0); + tmp159__ |= (pSrc->reserved << 1); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp159__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + if (pSrc->bkid_count) { + frameshtons(pCtx, pBuf, pSrc->bkid_count, 0); + *pnConsumed += 2; + pBuf += 2; + } else { + break; + } + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->bkid), (pSrc->bkid_count * 16)); + *pnConsumed += (pSrc->bkid_count * 16); + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_wapi. */ + +uint32_t dot11f_pack_ie_wapi_opaque(tpAniSirGlobal pCtx, + tDot11fIEWAPIOpaque *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 68; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wapi_opaque. */ + +uint32_t dot11f_pack_ie_wfatpc(tpAniSirGlobal pCtx, + tDot11fIEWFATPC *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x8; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->txPower; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->linkMargin; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wfatpc. */ + +uint32_t dot11f_pack_ie_wfdie_opaque(tpAniSirGlobal pCtx, + tDot11fIEWFDIEOpaque *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xa; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wfdie_opaque. */ + +uint32_t dot11f_pack_ie_wmm_caps(tpAniSirGlobal pCtx, + tDot11fIEWMMCaps *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp160__; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x5; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + tmp160__ = 0U; + tmp160__ |= (pSrc->reserved << 0); + tmp160__ |= (pSrc->qack << 4); + tmp160__ |= (pSrc->queue_request << 5); + tmp160__ |= (pSrc->txop_request << 6); + tmp160__ |= (pSrc->more_ack << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp160__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wmm_caps. */ + +uint32_t dot11f_pack_ie_wmm_info_ap(tpAniSirGlobal pCtx, + tDot11fIEWMMInfoAp *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp161__; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + tmp161__ = 0U; + tmp161__ |= (pSrc->param_set_count << 0); + tmp161__ |= (pSrc->reserved << 4); + tmp161__ |= (pSrc->uapsd << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp161__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wmm_info_ap. */ + +uint32_t dot11f_pack_ie_wmm_info_station(tpAniSirGlobal pCtx, + tDot11fIEWMMInfoStation *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp162__; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + tmp162__ = 0U; + tmp162__ |= (pSrc->acvo_uapsd << 0); + tmp162__ |= (pSrc->acvi_uapsd << 1); + tmp162__ |= (pSrc->acbk_uapsd << 2); + tmp162__ |= (pSrc->acbe_uapsd << 3); + tmp162__ |= (pSrc->reserved1 << 4); + tmp162__ |= (pSrc->max_sp_length << 5); + tmp162__ |= (pSrc->reserved2 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp162__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wmm_info_station. */ + +uint32_t dot11f_pack_ie_wmm_params(tpAniSirGlobal pCtx, + tDot11fIEWMMParams *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp163__; + uint8_t tmp164__; + uint8_t tmp165__; + uint8_t tmp166__; + uint8_t tmp167__; + uint8_t tmp168__; + uint8_t tmp169__; + uint8_t tmp170__; + nNeeded += 19; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x1; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->version; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->qosInfo; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->reserved2; + *pnConsumed += 1; + pBuf += 1; + tmp163__ = 0U; + tmp163__ |= (pSrc->acbe_aifsn << 0); + tmp163__ |= (pSrc->acbe_acm << 4); + tmp163__ |= (pSrc->acbe_aci << 5); + tmp163__ |= (pSrc->unused1 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp163__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp164__ = 0U; + tmp164__ |= (pSrc->acbe_acwmin << 0); + tmp164__ |= (pSrc->acbe_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp164__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->acbe_txoplimit, 0); + *pnConsumed += 2; + pBuf += 2; + tmp165__ = 0U; + tmp165__ |= (pSrc->acbk_aifsn << 0); + tmp165__ |= (pSrc->acbk_acm << 4); + tmp165__ |= (pSrc->acbk_aci << 5); + tmp165__ |= (pSrc->unused2 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp165__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp166__ = 0U; + tmp166__ |= (pSrc->acbk_acwmin << 0); + tmp166__ |= (pSrc->acbk_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp166__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->acbk_txoplimit, 0); + *pnConsumed += 2; + pBuf += 2; + tmp167__ = 0U; + tmp167__ |= (pSrc->acvi_aifsn << 0); + tmp167__ |= (pSrc->acvi_acm << 4); + tmp167__ |= (pSrc->acvi_aci << 5); + tmp167__ |= (pSrc->unused3 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp167__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp168__ = 0U; + tmp168__ |= (pSrc->acvi_acwmin << 0); + tmp168__ |= (pSrc->acvi_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp168__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->acvi_txoplimit, 0); + *pnConsumed += 2; + pBuf += 2; + tmp169__ = 0U; + tmp169__ |= (pSrc->acvo_aifsn << 0); + tmp169__ |= (pSrc->acvo_acm << 4); + tmp169__ |= (pSrc->acvo_aci << 5); + tmp169__ |= (pSrc->unused4 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp169__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp170__ = 0U; + tmp170__ |= (pSrc->acvo_acwmin << 0); + tmp170__ |= (pSrc->acvo_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp170__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + frameshtons(pCtx, pBuf, pSrc->acvo_txoplimit, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wmm_params. */ + +uint32_t dot11f_pack_ie_wpa(tpAniSirGlobal pCtx, + tDot11fIEWPA *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_iewpa(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x1; + ++pBuf; ++(*pnConsumed); + frameshtons(pCtx, pBuf, pSrc->version, 0); + *pnConsumed += 2; + pBuf += 2; + if (pSrc->multicast_cipher_present) { + DOT11F_MEMCPY(pCtx, pBuf, pSrc->multicast_cipher, 4); + *pnConsumed += 4; + pBuf += 4; + } else { + break; + } + if (pSrc->unicast_cipher_count) { + frameshtons(pCtx, pBuf, pSrc->unicast_cipher_count, 0); + *pnConsumed += 2; + pBuf += 2; + } else { + break; + } + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->unicast_ciphers), (pSrc->unicast_cipher_count * 4)); + *pnConsumed += (pSrc->unicast_cipher_count * 4); + pBuf += (pSrc->unicast_cipher_count * 4); + if (pSrc->auth_suite_count) { + frameshtons(pCtx, pBuf, pSrc->auth_suite_count, 0); + *pnConsumed += 2; + pBuf += 2; + } else { + break; + } + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->auth_suites), (pSrc->auth_suite_count * 4)); + *pnConsumed += (pSrc->auth_suite_count * 4); + pBuf += (pSrc->auth_suite_count * 4); + if (pSrc->caps) { + frameshtons(pCtx, pBuf, pSrc->caps, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + } else { + break; + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_wpa. */ + +uint32_t dot11f_pack_ie_wpa_opaque(tpAniSirGlobal pCtx, + tDot11fIEWPAOpaque *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x1; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wpa_opaque. */ + +uint32_t dot11f_pack_ie_wsc(tpAniSirGlobal pCtx, + tDot11fIEWSC *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_iewsc(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_WSC + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_wsc. */ + +uint32_t dot11f_pack_ie_wsc_assoc_req(tpAniSirGlobal pCtx, + tDot11fIEWscAssocReq *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_ie_wsc_assoc_req(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_WscAssocReq + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_wsc_assoc_req. */ + +uint32_t dot11f_pack_ie_wsc_assoc_res(tpAniSirGlobal pCtx, + tDot11fIEWscAssocRes *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_ie_wsc_assoc_res(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_WscAssocRes + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_wsc_assoc_res. */ + +uint32_t dot11f_pack_ie_wsc_beacon(tpAniSirGlobal pCtx, + tDot11fIEWscBeacon *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_ie_wsc_beacon(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_WscBeacon + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_wsc_beacon. */ + +uint32_t dot11f_pack_ie_wsc_beacon_probe_res(tpAniSirGlobal pCtx, + tDot11fIEWscBeaconProbeRes *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_ie_wsc_beacon_probe_res(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_WscBeaconProbeRes + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_wsc_beacon_probe_res. */ + +uint32_t dot11f_pack_ie_wsc_ie_opaque(tpAniSirGlobal pCtx, + tDot11fIEWscIEOpaque *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_wsc_ie_opaque. */ + +uint32_t dot11f_pack_ie_wsc_probe_req(tpAniSirGlobal pCtx, + tDot11fIEWscProbeReq *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_ie_wsc_probe_req(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_WscProbeReq + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_wsc_probe_req. */ + +uint32_t dot11f_pack_ie_wsc_probe_res(tpAniSirGlobal pCtx, + tDot11fIEWscProbeRes *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_ie_wsc_probe_res(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_WscProbeRes + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_wsc_probe_res. */ + +uint32_t dot11f_pack_ie_wsc_reassoc_res(tpAniSirGlobal pCtx, + tDot11fIEWscReassocRes *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t n, idx = 0, idxlast; + uint32_t nConsumedSoFar, nConsumedNow; + uint32_t status = DOT11F_PARSE_SUCCESS; + uint32_t nNeeded = 0U; + status = dot11f_get_packed_ie_wsc_reassoc_res(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + (void)pCtx; + if (pSrc->present) { + do { + nConsumedSoFar = *pnConsumed; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xf2; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; --nBuf; ++(*pnConsumed); + n = (255 - 4) < nBuf ? (255 - 4) : nBuf; + nConsumedNow = *pnConsumed; + idxlast = idx; + status = pack_tlv_core(pCtx, (uint8_t *)pSrc, pBuf, n, + pnConsumed, + TLVS_WscReassocRes + + idx, &idx); + nConsumedNow = *pnConsumed - nConsumedNow; + *pIeLen = *pnConsumed - nConsumedSoFar - 2; + pBuf += nConsumedNow; + nBuf -= nConsumedNow; + } while (DOT11F_BUFFER_OVERFLOW == status && idxlast != idx); + } + return status; +} /* End dot11f_pack_ie_wsc_reassoc_res. */ + +uint32_t dot11f_pack_ie_addba_extn_element(tpAniSirGlobal pCtx, + tDot11fIEaddba_extn_element *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp171__; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 159; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp171__ = 0U; + tmp171__ |= (pSrc->no_fragmentation << 0); + tmp171__ |= (pSrc->he_frag_operation << 1); + tmp171__ |= (pSrc->reserved << 3); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp171__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_addba_extn_element. */ + +uint32_t dot11f_pack_ie_bss_color_change(tpAniSirGlobal pCtx, + tDot11fIEbss_color_change *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp172__; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 42; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->countdown; + *pnConsumed += 1; + pBuf += 1; + tmp172__ = 0U; + tmp172__ |= (pSrc->new_color << 0); + tmp172__ |= (pSrc->reserved << 6); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp172__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_bss_color_change. */ + +uint32_t dot11f_pack_ie_dh_parameter_element(tpAniSirGlobal pCtx, + tDot11fIEdh_parameter_element *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_public_key + 2); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 32; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->group, 2); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->public_key), pSrc->num_public_key); + *pnConsumed += pSrc->num_public_key; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_dh_parameter_element. */ + +uint32_t dot11f_pack_ie_esp_information(tpAniSirGlobal pCtx, + tDot11fIEesp_information *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 11; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_esp_information. */ + +uint32_t dot11f_pack_ie_ext_chan_switch_ann(tpAniSirGlobal pCtx, + tDot11fIEext_chan_switch_ann *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 4; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 60; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->switch_mode; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->new_reg_class; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->new_channel; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->switch_count; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ext_chan_switch_ann. */ + +uint32_t dot11f_pack_ie_fils_assoc_delay_info(tpAniSirGlobal pCtx, + tDot11fIEfils_assoc_delay_info *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 1; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->assoc_delay_info; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fils_assoc_delay_info. */ + +uint32_t dot11f_pack_ie_fils_hlp_container(tpAniSirGlobal pCtx, + tDot11fIEfils_hlp_container *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_hlp_packet + 12); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 5; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->dest_mac, 6); + *pnConsumed += 6; + pBuf += 6; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->src_mac, 6); + *pnConsumed += 6; + pBuf += 6; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->hlp_packet), pSrc->num_hlp_packet); + *pnConsumed += pSrc->num_hlp_packet; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fils_hlp_container. */ + +uint32_t dot11f_pack_ie_fils_indication(tpAniSirGlobal pCtx, + tDot11fIEfils_indication *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp173__; + nNeeded += (pSrc->num_variable_data + 2); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 240; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp173__ = 0U; + tmp173__ |= (pSrc->public_key_identifiers_cnt << 0); + tmp173__ |= (pSrc->realm_identifiers_cnt << 3); + tmp173__ |= (pSrc->is_ip_config_supported << 6); + tmp173__ |= (pSrc->is_cache_id_present << 7); + tmp173__ |= (pSrc->is_hessid_present << 8); + tmp173__ |= (pSrc->is_fils_sk_auth_supported << 9); + tmp173__ |= (pSrc->is_fils_sk_auth_pfs_supported << 10); + tmp173__ |= (pSrc->is_pk_auth_supported << 11); + tmp173__ |= (pSrc->reserved << 12); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp173__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->variable_data), pSrc->num_variable_data); + *pnConsumed += pSrc->num_variable_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fils_indication. */ + +uint32_t dot11f_pack_ie_fils_kde(tpAniSirGlobal pCtx, + tDot11fIEfils_kde *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_kde_list + 8); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 7; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->key_rsc, 8); + *pnConsumed += 8; + pBuf += 8; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->kde_list), pSrc->num_kde_list); + *pnConsumed += pSrc->num_kde_list; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fils_kde. */ + +uint32_t dot11f_pack_ie_fils_key_confirmation(tpAniSirGlobal pCtx, + tDot11fIEfils_key_confirmation *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_key_auth; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 3; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->key_auth), pSrc->num_key_auth); + *pnConsumed += pSrc->num_key_auth; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fils_key_confirmation. */ + +uint32_t dot11f_pack_ie_fils_nonce(tpAniSirGlobal pCtx, + tDot11fIEfils_nonce *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 16; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 13; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->nonce, 16); + *pnConsumed += 16; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fils_nonce. */ + +uint32_t dot11f_pack_ie_fils_public_key(tpAniSirGlobal pCtx, + tDot11fIEfils_public_key *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_public_key + 1); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 12; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->key_type; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->public_key), pSrc->num_public_key); + *pnConsumed += pSrc->num_public_key; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fils_public_key. */ + +uint32_t dot11f_pack_ie_fils_session(tpAniSirGlobal pCtx, + tDot11fIEfils_session *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 8; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 4; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, pSrc->session, 8); + *pnConsumed += 8; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fils_session. */ + +uint32_t dot11f_pack_ie_fils_wrapped_data(tpAniSirGlobal pCtx, + tDot11fIEfils_wrapped_data *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_wrapped_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 8; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->wrapped_data), pSrc->num_wrapped_data); + *pnConsumed += pSrc->num_wrapped_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fils_wrapped_data. */ + +uint32_t dot11f_pack_ie_fragment_ie(tpAniSirGlobal pCtx, + tDot11fIEfragment_ie *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 242; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_fragment_ie. */ + +uint32_t dot11f_pack_ie_he_6ghz_band_cap(tpAniSirGlobal pCtx, + tDot11fIEhe_6ghz_band_cap *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp174__; + nNeeded += 2; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 59; + ++pBuf; ++(*pnConsumed); + tmp174__ = 0U; + tmp174__ |= (pSrc->min_mpdu_start_spacing << 0); + tmp174__ |= (pSrc->max_ampdu_len_exp << 3); + tmp174__ |= (pSrc->max_mpdu_len << 6); + tmp174__ |= (pSrc->sm_pow_save << 9); + tmp174__ |= (pSrc->rd_responder << 11); + tmp174__ |= (pSrc->rx_ant_pattern_consistency << 12); + tmp174__ |= (pSrc->tx_ant_pattern_consistency << 13); + tmp174__ |= (pSrc->reserved << 14); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp174__, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + nBuf -= 2 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_he_6ghz_band_cap. */ + +uint32_t dot11f_pack_ie_he_cap(tpAniSirGlobal pCtx, + tDot11fIEhe_cap *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t tmp175__; + uint16_t tmp176__; + uint32_t tmp177__; + uint32_t tmp178__; + uint16_t tmp179__; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_he_cap(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 35; + ++pBuf; ++(*pnConsumed); + tmp175__ = 0U; + tmp175__ |= (pSrc->htc_he << 0); + tmp175__ |= (pSrc->twt_request << 1); + tmp175__ |= (pSrc->twt_responder << 2); + tmp175__ |= (pSrc->fragmentation << 3); + tmp175__ |= (pSrc->max_num_frag_msdu_amsdu_exp << 5); + tmp175__ |= (pSrc->min_frag_size << 8); + tmp175__ |= (pSrc->trigger_frm_mac_pad << 10); + tmp175__ |= (pSrc->multi_tid_aggr_rx_supp << 12); + tmp175__ |= (pSrc->he_link_adaptation << 15); + tmp175__ |= (pSrc->all_ack << 17); + tmp175__ |= (pSrc->trigd_rsp_sched << 18); + tmp175__ |= (pSrc->a_bsr << 19); + tmp175__ |= (pSrc->broadcast_twt << 20); + tmp175__ |= (pSrc->ba_32bit_bitmap << 21); + tmp175__ |= (pSrc->mu_cascade << 22); + tmp175__ |= (pSrc->ack_enabled_multitid << 23); + tmp175__ |= (pSrc->reserved << 24); + tmp175__ |= (pSrc->omi_a_ctrl << 25); + tmp175__ |= (pSrc->ofdma_ra << 26); + tmp175__ |= (pSrc->max_ampdu_len_exp_ext << 27); + tmp175__ |= (pSrc->amsdu_frag << 29); + tmp175__ |= (pSrc->flex_twt_sched << 30); + tmp175__ |= (pSrc->rx_ctrl_frame << 31); + if (unlikely(nBuf < 4)) + return DOT11F_INCOMPLETE_IE; + + frameshtonl(pCtx, pBuf, tmp175__, 0); + *pnConsumed += 4; + pBuf += 4; + nBuf -= 4 ; + tmp176__ = 0U; + tmp176__ |= (pSrc->bsrp_ampdu_aggr << 0); + tmp176__ |= (pSrc->qtp << 1); + tmp176__ |= (pSrc->a_bqr << 2); + tmp176__ |= (pSrc->spatial_reuse_param_rspder << 3); + tmp176__ |= (pSrc->ndp_feedback_supp << 4); + tmp176__ |= (pSrc->ops_supp << 5); + tmp176__ |= (pSrc->amsdu_in_ampdu << 6); + tmp176__ |= (pSrc->multi_tid_aggr_tx_supp << 7); + tmp176__ |= (pSrc->he_sub_ch_sel_tx_supp << 10); + tmp176__ |= (pSrc->ul_2x996_tone_ru_supp << 11); + tmp176__ |= (pSrc->om_ctrl_ul_mu_data_dis_rx << 12); + tmp176__ |= (pSrc->he_dynamic_smps << 13); + tmp176__ |= (pSrc->punctured_sounding_supp << 14); + tmp176__ |= (pSrc->ht_vht_trg_frm_rx_supp << 15); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp176__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + tmp177__ = 0U; + tmp177__ |= (pSrc->reserved2 << 0); + tmp177__ |= (pSrc->chan_width_0 << 1); + tmp177__ |= (pSrc->chan_width_1 << 2); + tmp177__ |= (pSrc->chan_width_2 << 3); + tmp177__ |= (pSrc->chan_width_3 << 4); + tmp177__ |= (pSrc->chan_width_4 << 5); + tmp177__ |= (pSrc->chan_width_5 << 6); + tmp177__ |= (pSrc->chan_width_6 << 7); + tmp177__ |= (pSrc->rx_pream_puncturing << 8); + tmp177__ |= (pSrc->device_class << 12); + tmp177__ |= (pSrc->ldpc_coding << 13); + tmp177__ |= (pSrc->he_1x_ltf_800_gi_ppdu << 14); + tmp177__ |= (pSrc->midamble_tx_rx_max_nsts << 15); + tmp177__ |= (pSrc->he_4x_ltf_3200_gi_ndp << 17); + tmp177__ |= (pSrc->tb_ppdu_tx_stbc_lt_80mhz << 18); + tmp177__ |= (pSrc->rx_stbc_lt_80mhz << 19); + tmp177__ |= (pSrc->doppler << 20); + tmp177__ |= (pSrc->ul_mu << 22); + tmp177__ |= (pSrc->dcm_enc_tx << 24); + tmp177__ |= (pSrc->dcm_enc_rx << 27); + tmp177__ |= (pSrc->ul_he_mu << 30); + tmp177__ |= (pSrc->su_beamformer << 31); + if (unlikely(nBuf < 4)) + return DOT11F_INCOMPLETE_IE; + + frameshtonl(pCtx, pBuf, tmp177__, 0); + *pnConsumed += 4; + pBuf += 4; + nBuf -= 4 ; + tmp178__ = 0U; + tmp178__ |= (pSrc->su_beamformee << 0); + tmp178__ |= (pSrc->mu_beamformer << 1); + tmp178__ |= (pSrc->bfee_sts_lt_80 << 2); + tmp178__ |= (pSrc->bfee_sts_gt_80 << 5); + tmp178__ |= (pSrc->num_sounding_lt_80 << 8); + tmp178__ |= (pSrc->num_sounding_gt_80 << 11); + tmp178__ |= (pSrc->su_feedback_tone16 << 14); + tmp178__ |= (pSrc->mu_feedback_tone16 << 15); + tmp178__ |= (pSrc->codebook_su << 16); + tmp178__ |= (pSrc->codebook_mu << 17); + tmp178__ |= (pSrc->beamforming_feedback << 18); + tmp178__ |= (pSrc->he_er_su_ppdu << 21); + tmp178__ |= (pSrc->dl_mu_mimo_part_bw << 22); + tmp178__ |= (pSrc->ppet_present << 23); + tmp178__ |= (pSrc->srp << 24); + tmp178__ |= (pSrc->power_boost << 25); + tmp178__ |= (pSrc->he_ltf_800_gi_4x << 26); + tmp178__ |= (pSrc->max_nc << 27); + tmp178__ |= (pSrc->tb_ppdu_tx_stbc_gt_80mhz << 30); + tmp178__ |= (pSrc->rx_stbc_gt_80mhz << 31); + if (unlikely(nBuf < 4)) + return DOT11F_INCOMPLETE_IE; + + frameshtonl(pCtx, pBuf, tmp178__, 0); + *pnConsumed += 4; + pBuf += 4; + nBuf -= 4 ; + tmp179__ = 0U; + tmp179__ |= (pSrc->er_he_ltf_800_gi_4x << 0); + tmp179__ |= (pSrc->he_ppdu_20_in_40Mhz_2G << 1); + tmp179__ |= (pSrc->he_ppdu_20_in_160_80p80Mhz << 2); + tmp179__ |= (pSrc->he_ppdu_80_in_160_80p80Mhz << 3); + tmp179__ |= (pSrc->er_1x_he_ltf_gi << 4); + tmp179__ |= (pSrc->midamble_tx_rx_1x_he_ltf << 5); + tmp179__ |= (pSrc->dcm_max_bw << 6); + tmp179__ |= (pSrc->longer_than_16_he_sigb_ofdm_sym << 8); + tmp179__ |= (pSrc->non_trig_cqi_feedback << 9); + tmp179__ |= (pSrc->tx_1024_qam_lt_242_tone_ru << 10); + tmp179__ |= (pSrc->rx_1024_qam_lt_242_tone_ru << 11); + tmp179__ |= (pSrc->rx_full_bw_su_he_mu_compress_sigb << 12); + tmp179__ |= (pSrc->rx_full_bw_su_he_mu_non_cmpr_sigb << 13); + tmp179__ |= (pSrc->reserved3 << 14); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp179__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + *pBuf = pSrc->reserved4; + *pnConsumed += 1; + pBuf += 1; + frameshtons(pCtx, pBuf, pSrc->rx_he_mcs_map_lt_80, 0); + *pnConsumed += 2; + pBuf += 2; + frameshtons(pCtx, pBuf, pSrc->tx_he_mcs_map_lt_80, 0); + *pnConsumed += 2; + pBuf += 2; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->rx_he_mcs_map_160), (pSrc->chan_width_2 * 2)); + *pnConsumed += (pSrc->chan_width_2 * 2); + pBuf += (pSrc->chan_width_2 * 2); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->tx_he_mcs_map_160), (pSrc->chan_width_2 * 2)); + *pnConsumed += (pSrc->chan_width_2 * 2); + pBuf += (pSrc->chan_width_2 * 2); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->rx_he_mcs_map_80_80), (pSrc->chan_width_3 * 2)); + *pnConsumed += (pSrc->chan_width_3 * 2); + pBuf += (pSrc->chan_width_3 * 2); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->tx_he_mcs_map_80_80), (pSrc->chan_width_3 * 2)); + *pnConsumed += (pSrc->chan_width_3 * 2); + pBuf += (pSrc->chan_width_3 * 2); + if (pSrc->ppet_present) { + switch (pSrc->ppet_present) { + case 1: + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->ppet.ppe_threshold.ppe_th), pSrc->ppet.ppe_threshold.num_ppe_th); + *pnConsumed += pSrc->ppet.ppe_threshold.num_ppe_th; + /* fieldsEndFlag = 1 */ + break; + } + } else { + break; + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_he_cap. */ + +uint32_t dot11f_pack_ie_he_op(tpAniSirGlobal pCtx, + tDot11fIEhe_op *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint16_t tmp180__; + uint8_t tmp181__; + uint8_t tmp182__; + uint8_t tmp183__; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_he_op(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 36; + ++pBuf; ++(*pnConsumed); + tmp180__ = 0U; + tmp180__ |= (pSrc->default_pe << 0); + tmp180__ |= (pSrc->twt_required << 3); + tmp180__ |= (pSrc->txop_rts_threshold << 4); + tmp180__ |= (pSrc->vht_oper_present << 14); + tmp180__ |= (pSrc->co_located_bss << 15); + if (unlikely(nBuf < 2)) + return DOT11F_INCOMPLETE_IE; + + frameshtons(pCtx, pBuf, tmp180__, 0); + *pnConsumed += 2; + pBuf += 2; + nBuf -= 2 ; + tmp181__ = 0U; + tmp181__ |= (pSrc->er_su_disable << 0); + tmp181__ |= (pSrc->oper_info_6g_present << 1); + tmp181__ |= (pSrc->reserved2 << 2); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp181__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp182__ = 0U; + tmp182__ |= (pSrc->bss_color << 0); + tmp182__ |= (pSrc->partial_bss_col << 6); + tmp182__ |= (pSrc->bss_col_disabled << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp182__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + DOT11F_MEMCPY(pCtx, pBuf, pSrc->basic_mcs_nss, 2); + *pnConsumed += 2; + pBuf += 2; + if (pSrc->vht_oper_present) { + switch (pSrc->vht_oper_present) { + case 1: + *pBuf = pSrc->vht_oper.info.chan_width; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->vht_oper.info.center_freq_seg0; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->vht_oper.info.center_freq_seg1; + *pnConsumed += 1; + pBuf += 1; + break; + } + } + if (pSrc->co_located_bss) { + switch (pSrc->co_located_bss) { + case 1: + *pBuf = pSrc->maxbssid_ind.info.data; + *pnConsumed += 1; + pBuf += 1; + break; + } + } + if (pSrc->oper_info_6g_present) { + switch (pSrc->oper_info_6g_present) { + case 1: + *pBuf = pSrc->oper_info_6g.info.primary_ch; + *pnConsumed += 1; + pBuf += 1; + tmp183__ = 0U; + tmp183__ |= (pSrc->oper_info_6g.info.ch_width << 0); + tmp183__ |= (pSrc->oper_info_6g.info.dup_bcon << 2); + tmp183__ |= (pSrc->oper_info_6g.info.reserved << 3); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp183__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + *pBuf = pSrc->oper_info_6g.info.center_freq_seg0; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->oper_info_6g.info.center_freq_seg1; + *pnConsumed += 1; + pBuf += 1; + *pBuf = pSrc->oper_info_6g.info.min_rate; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_he_op. */ + +uint32_t dot11f_pack_ie_hs20vendor_ie(tpAniSirGlobal pCtx, + tDot11fIEhs20vendor_ie *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp184__; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_hs20vendor_ie(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x10; + ++pBuf; ++(*pnConsumed); + tmp184__ = 0U; + tmp184__ |= (pSrc->dgaf_dis << 0); + tmp184__ |= (pSrc->hs_id_present << 1); + tmp184__ |= (pSrc->reserved << 3); + tmp184__ |= (pSrc->release_num << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp184__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + if (pSrc->hs_id_present) { + switch (pSrc->hs_id_present) { + case 1: + frameshtons(pCtx, pBuf, pSrc->hs_id.pps_mo.pps_mo_id, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + case 2: + frameshtons(pCtx, pBuf, pSrc->hs_id.anqp_domain.anqp_domain_id, 0); + *pnConsumed += 2; + /* fieldsEndFlag = 1 */ + break; + } + } else { + break; + } + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_hs20vendor_ie. */ + +uint32_t dot11f_pack_ie_ht2040_bss_coexistence(tpAniSirGlobal pCtx, + tDot11fIEht2040_bss_coexistence *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp185__; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 72; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + tmp185__ = 0U; + tmp185__ |= (pSrc->info_request << 0); + tmp185__ |= (pSrc->forty_mhz_intolerant << 1); + tmp185__ |= (pSrc->twenty_mhz_bsswidth_req << 2); + tmp185__ |= (pSrc->obss_scan_exemption_req << 3); + tmp185__ |= (pSrc->obss_scan_exemption_grant << 4); + tmp185__ |= (pSrc->unused << 5); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp185__; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + nBuf -= 1 ; + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ht2040_bss_coexistence. */ + +uint32_t dot11f_pack_ie_ht2040_bss_intolerant_report(tpAniSirGlobal pCtx, + tDot11fIEht2040_bss_intolerant_report *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += (pSrc->num_channel_list + 1); + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 73; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->operating_class; + *pnConsumed += 1; + pBuf += 1; + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->channel_list), pSrc->num_channel_list); + *pnConsumed += pSrc->num_channel_list; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_ht2040_bss_intolerant_report. */ + +uint32_t dot11f_pack_ie_mu_edca_param_set(tpAniSirGlobal pCtx, + tDot11fIEmu_edca_param_set *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint8_t tmp186__; + uint8_t tmp187__; + uint8_t tmp188__; + uint8_t tmp189__; + uint8_t tmp190__; + uint8_t tmp191__; + uint8_t tmp192__; + uint8_t tmp193__; + nNeeded += 13; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 255; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 38; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->qos; + *pnConsumed += 1; + pBuf += 1; + tmp186__ = 0U; + tmp186__ |= (pSrc->acbe_aifsn << 0); + tmp186__ |= (pSrc->acbe_acm << 4); + tmp186__ |= (pSrc->acbe_aci << 5); + tmp186__ |= (pSrc->unused1 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp186__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp187__ = 0U; + tmp187__ |= (pSrc->acbe_acwmin << 0); + tmp187__ |= (pSrc->acbe_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp187__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + *pBuf = pSrc->acbe_muedca_timer; + *pnConsumed += 1; + pBuf += 1; + tmp188__ = 0U; + tmp188__ |= (pSrc->acbk_aifsn << 0); + tmp188__ |= (pSrc->acbk_acm << 4); + tmp188__ |= (pSrc->acbk_aci << 5); + tmp188__ |= (pSrc->unused2 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp188__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp189__ = 0U; + tmp189__ |= (pSrc->acbk_acwmin << 0); + tmp189__ |= (pSrc->acbk_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp189__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + *pBuf = pSrc->acbk_muedca_timer; + *pnConsumed += 1; + pBuf += 1; + tmp190__ = 0U; + tmp190__ |= (pSrc->acvi_aifsn << 0); + tmp190__ |= (pSrc->acvi_acm << 4); + tmp190__ |= (pSrc->acvi_aci << 5); + tmp190__ |= (pSrc->unused3 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp190__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp191__ = 0U; + tmp191__ |= (pSrc->acvi_acwmin << 0); + tmp191__ |= (pSrc->acvi_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp191__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + *pBuf = pSrc->acvi_muedca_timer; + *pnConsumed += 1; + pBuf += 1; + tmp192__ = 0U; + tmp192__ |= (pSrc->acvo_aifsn << 0); + tmp192__ |= (pSrc->acvo_acm << 4); + tmp192__ |= (pSrc->acvo_aci << 5); + tmp192__ |= (pSrc->unused4 << 7); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp192__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + tmp193__ = 0U; + tmp193__ |= (pSrc->acvo_acwmin << 0); + tmp193__ |= (pSrc->acvo_acwmax << 4); + if (unlikely(nBuf < 1)) + return DOT11F_INCOMPLETE_IE; + + *pBuf = tmp193__; + *pnConsumed += 1; + pBuf += 1; + nBuf -= 1 ; + *pBuf = pSrc->acvo_muedca_timer; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_mu_edca_param_set. */ + +uint32_t dot11f_pack_ie_osen_ie(tpAniSirGlobal pCtx, + tDot11fIEosen_ie *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x12; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_osen_ie. */ + +uint32_t dot11f_pack_ie_qcn_ie(tpAniSirGlobal pCtx, + tDot11fIEqcn_ie *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_qcn_ie(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x8c; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xfd; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0xf0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x1; + ++pBuf; --nBuf; ++(*pnConsumed); + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_qcn_ie, + IES_qcn_ie); + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_qcn_ie. */ + +uint32_t dot11f_pack_ie_roaming_consortium_sel(tpAniSirGlobal pCtx, + tDot11fIEroaming_consortium_sel *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += pSrc->num_data; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x50; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x6f; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x9a; + ++pBuf; ++(*pnConsumed); + *pBuf = 0x1d; + ++pBuf; ++(*pnConsumed); + DOT11F_MEMCPY(pCtx, pBuf, &(pSrc->data), pSrc->num_data); + *pnConsumed += pSrc->num_data; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_roaming_consortium_sel. */ + +uint32_t dot11f_pack_ie_sec_chan_offset_ele(tpAniSirGlobal pCtx, + tDot11fIEsec_chan_offset_ele *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + nNeeded += 1; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 62; + ++pBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; ++(*pnConsumed); + *pBuf = pSrc->secondaryChannelOffset; + *pnConsumed += 1; + /* fieldsEndFlag = 1 */ + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return DOT11F_PARSE_SUCCESS; +} /* End dot11f_pack_ie_sec_chan_offset_ele. */ + +uint32_t dot11f_pack_ie_vendor_vht_ie(tpAniSirGlobal pCtx, + tDot11fIEvendor_vht_ie *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed) +{ + uint8_t *pIeLen = 0; + uint32_t nConsumedOnEntry = *pnConsumed; + uint32_t nNeeded = 0U; + uint32_t status = DOT11F_PARSE_SUCCESS; + status = dot11f_get_packed_ie_vendor_vht_ie(pCtx, pSrc, &nNeeded); + if (!DOT11F_SUCCEEDED(status)) + return status; + while (pSrc->present) { + if (nNeeded > nBuf) + return DOT11F_BUFFER_OVERFLOW; + *pBuf = 221; + ++pBuf; --nBuf; ++(*pnConsumed); + pIeLen = pBuf; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x0; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x90; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4c; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = 0x4; + ++pBuf; --nBuf; ++(*pnConsumed); + *pBuf = pSrc->sub_type; + *pnConsumed += 1; + pBuf += 1; + status = pack_core(pCtx, + (uint8_t *)pSrc, + pBuf, + nBuf, + pnConsumed, + FFS_vendor_vht_ie, + IES_vendor_vht_ie); + break; + } + (void)pCtx; + if (pIeLen) { + *pIeLen = *pnConsumed - nConsumedOnEntry - 2; + } + return status; +} /* End dot11f_pack_ie_vendor_vht_ie. */ + +uint32_t dot11f_pack_add_ts_request(tpAniSirGlobal pCtx, + tDot11fAddTSRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_AddTSRequest, IES_AddTSRequest); + + return status; + +} /* End dot11f_unpack_add_ts_request. */ + +uint32_t dot11f_pack_add_ts_response(tpAniSirGlobal pCtx, + tDot11fAddTSResponse *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_AddTSResponse, IES_AddTSResponse); + + return status; + +} /* End dot11f_unpack_add_ts_response. */ + +uint32_t dot11f_pack_assoc_request(tpAniSirGlobal pCtx, + tDot11fAssocRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_AssocRequest, IES_AssocRequest); + + return status; + +} /* End dot11f_unpack_assoc_request. */ + +uint32_t dot11f_pack_assoc_response(tpAniSirGlobal pCtx, + tDot11fAssocResponse *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_AssocResponse, IES_AssocResponse); + + return status; + +} /* End dot11f_unpack_assoc_response. */ + +uint32_t dot11f_pack_authentication(tpAniSirGlobal pCtx, + tDot11fAuthentication *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_Authentication, IES_Authentication); + + return status; + +} /* End dot11f_unpack_authentication. */ + +uint32_t dot11f_pack_beacon(tpAniSirGlobal pCtx, + tDot11fBeacon *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_Beacon, IES_Beacon); + + return status; + +} /* End dot11f_unpack_beacon. */ + +uint32_t dot11f_pack_beacon1(tpAniSirGlobal pCtx, + tDot11fBeacon1 *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_Beacon1, IES_Beacon1); + + return status; + +} /* End dot11f_unpack_beacon1. */ + +uint32_t dot11f_pack_beacon2(tpAniSirGlobal pCtx, + tDot11fBeacon2 *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_Beacon2, IES_Beacon2); + + return status; + +} /* End dot11f_unpack_beacon2. */ + +uint32_t dot11f_pack_beacon_i_es(tpAniSirGlobal pCtx, + tDot11fBeaconIEs *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_BeaconIEs, IES_BeaconIEs); + + return status; + +} /* End dot11f_unpack_beacon_i_es. */ + +uint32_t dot11f_pack_channel_switch(tpAniSirGlobal pCtx, + tDot11fChannelSwitch *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_ChannelSwitch, IES_ChannelSwitch); + + return status; + +} /* End dot11f_unpack_channel_switch. */ + +uint32_t dot11f_pack_de_auth(tpAniSirGlobal pCtx, + tDot11fDeAuth *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_DeAuth, IES_DeAuth); + + return status; + +} /* End dot11f_unpack_de_auth. */ + +uint32_t dot11f_pack_del_ts(tpAniSirGlobal pCtx, + tDot11fDelTS *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_DelTS, IES_DelTS); + + return status; + +} /* End dot11f_unpack_del_ts. */ + +uint32_t dot11f_pack_disassociation(tpAniSirGlobal pCtx, + tDot11fDisassociation *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_Disassociation, IES_Disassociation); + + return status; + +} /* End dot11f_unpack_disassociation. */ + +uint32_t dot11f_pack_link_measurement_report(tpAniSirGlobal pCtx, + tDot11fLinkMeasurementReport *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_LinkMeasurementReport, IES_LinkMeasurementReport); + + return status; + +} /* End dot11f_unpack_link_measurement_report. */ + +uint32_t dot11f_pack_link_measurement_request(tpAniSirGlobal pCtx, + tDot11fLinkMeasurementRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_LinkMeasurementRequest, IES_LinkMeasurementRequest); + + return status; + +} /* End dot11f_unpack_link_measurement_request. */ + +uint32_t dot11f_pack_measurement_report(tpAniSirGlobal pCtx, + tDot11fMeasurementReport *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_MeasurementReport, IES_MeasurementReport); + + return status; + +} /* End dot11f_unpack_measurement_report. */ + +uint32_t dot11f_pack_measurement_request(tpAniSirGlobal pCtx, + tDot11fMeasurementRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_MeasurementRequest, IES_MeasurementRequest); + + return status; + +} /* End dot11f_unpack_measurement_request. */ + +uint32_t dot11f_pack_neighbor_report_request(tpAniSirGlobal pCtx, + tDot11fNeighborReportRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_NeighborReportRequest, IES_NeighborReportRequest); + + return status; + +} /* End dot11f_unpack_neighbor_report_request. */ + +uint32_t dot11f_pack_neighbor_report_response(tpAniSirGlobal pCtx, + tDot11fNeighborReportResponse *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_NeighborReportResponse, IES_NeighborReportResponse); + + return status; + +} /* End dot11f_unpack_neighbor_report_response. */ + +uint32_t dot11f_pack_operating_mode(tpAniSirGlobal pCtx, + tDot11fOperatingMode *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_OperatingMode, IES_OperatingMode); + + return status; + +} /* End dot11f_unpack_operating_mode. */ + +uint32_t dot11f_pack_probe_request(tpAniSirGlobal pCtx, + tDot11fProbeRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_ProbeRequest, IES_ProbeRequest); + + return status; + +} /* End dot11f_unpack_probe_request. */ + +uint32_t dot11f_pack_probe_response(tpAniSirGlobal pCtx, + tDot11fProbeResponse *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_ProbeResponse, IES_ProbeResponse); + + return status; + +} /* End dot11f_unpack_probe_response. */ + +uint32_t dot11f_pack_qos_map_configure(tpAniSirGlobal pCtx, + tDot11fQosMapConfigure *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_QosMapConfigure, IES_QosMapConfigure); + + return status; + +} /* End dot11f_unpack_qos_map_configure. */ + +uint32_t dot11f_pack_radio_measurement_report(tpAniSirGlobal pCtx, + tDot11fRadioMeasurementReport *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_RadioMeasurementReport, IES_RadioMeasurementReport); + + return status; + +} /* End dot11f_unpack_radio_measurement_report. */ + +uint32_t dot11f_pack_radio_measurement_request(tpAniSirGlobal pCtx, + tDot11fRadioMeasurementRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_RadioMeasurementRequest, IES_RadioMeasurementRequest); + + return status; + +} /* End dot11f_unpack_radio_measurement_request. */ + +uint32_t dot11f_pack_re_assoc_request(tpAniSirGlobal pCtx, + tDot11fReAssocRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_ReAssocRequest, IES_ReAssocRequest); + + return status; + +} /* End dot11f_unpack_re_assoc_request. */ + +uint32_t dot11f_pack_re_assoc_response(tpAniSirGlobal pCtx, + tDot11fReAssocResponse *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_ReAssocResponse, IES_ReAssocResponse); + + return status; + +} /* End dot11f_unpack_re_assoc_response. */ + +uint32_t dot11f_pack_sm_power_save(tpAniSirGlobal pCtx, + tDot11fSMPowerSave *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_SMPowerSave, IES_SMPowerSave); + + return status; + +} /* End dot11f_unpack_sm_power_save. */ + +uint32_t dot11f_pack_sa_query_req(tpAniSirGlobal pCtx, + tDot11fSaQueryReq *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_SaQueryReq, IES_SaQueryReq); + + return status; + +} /* End dot11f_unpack_sa_query_req. */ + +uint32_t dot11f_pack_sa_query_rsp(tpAniSirGlobal pCtx, + tDot11fSaQueryRsp *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_SaQueryRsp, IES_SaQueryRsp); + + return status; + +} /* End dot11f_unpack_sa_query_rsp. */ + +uint32_t dot11f_pack_tdls_dis_req(tpAniSirGlobal pCtx, + tDot11fTDLSDisReq *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TDLSDisReq, IES_TDLSDisReq); + + return status; + +} /* End dot11f_unpack_tdls_dis_req. */ + +uint32_t dot11f_pack_tdls_dis_rsp(tpAniSirGlobal pCtx, + tDot11fTDLSDisRsp *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TDLSDisRsp, IES_TDLSDisRsp); + + return status; + +} /* End dot11f_unpack_tdls_dis_rsp. */ + +uint32_t dot11f_pack_tdls_peer_traffic_ind(tpAniSirGlobal pCtx, + tDot11fTDLSPeerTrafficInd *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TDLSPeerTrafficInd, IES_TDLSPeerTrafficInd); + + return status; + +} /* End dot11f_unpack_tdls_peer_traffic_ind. */ + +uint32_t dot11f_pack_tdls_peer_traffic_rsp(tpAniSirGlobal pCtx, + tDot11fTDLSPeerTrafficRsp *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TDLSPeerTrafficRsp, IES_TDLSPeerTrafficRsp); + + return status; + +} /* End dot11f_unpack_tdls_peer_traffic_rsp. */ + +uint32_t dot11f_pack_tdls_setup_cnf(tpAniSirGlobal pCtx, + tDot11fTDLSSetupCnf *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TDLSSetupCnf, IES_TDLSSetupCnf); + + return status; + +} /* End dot11f_unpack_tdls_setup_cnf. */ + +uint32_t dot11f_pack_tdls_setup_req(tpAniSirGlobal pCtx, + tDot11fTDLSSetupReq *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TDLSSetupReq, IES_TDLSSetupReq); + + return status; + +} /* End dot11f_unpack_tdls_setup_req. */ + +uint32_t dot11f_pack_tdls_setup_rsp(tpAniSirGlobal pCtx, + tDot11fTDLSSetupRsp *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TDLSSetupRsp, IES_TDLSSetupRsp); + + return status; + +} /* End dot11f_unpack_tdls_setup_rsp. */ + +uint32_t dot11f_pack_tdls_teardown(tpAniSirGlobal pCtx, + tDot11fTDLSTeardown *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TDLSTeardown, IES_TDLSTeardown); + + return status; + +} /* End dot11f_unpack_tdls_teardown. */ + +uint32_t dot11f_pack_tpc_report(tpAniSirGlobal pCtx, + tDot11fTPCReport *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TPCReport, IES_TPCReport); + + return status; + +} /* End dot11f_unpack_tpc_report. */ + +uint32_t dot11f_pack_tpc_request(tpAniSirGlobal pCtx, + tDot11fTPCRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TPCRequest, IES_TPCRequest); + + return status; + +} /* End dot11f_unpack_tpc_request. */ + +uint32_t dot11f_pack_timing_advertisement_frame(tpAniSirGlobal pCtx, + tDot11fTimingAdvertisementFrame *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_TimingAdvertisementFrame, IES_TimingAdvertisementFrame); + + return status; + +} /* End dot11f_unpack_timing_advertisement_frame. */ + +uint32_t dot11f_pack_vht_gid_management_action_frame(tpAniSirGlobal pCtx, + tDot11fVHTGidManagementActionFrame *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_VHTGidManagementActionFrame, IES_VHTGidManagementActionFrame); + + return status; + +} /* End dot11f_unpack_vht_gid_management_action_frame. */ + +uint32_t dot11f_pack_wmm_add_ts_request(tpAniSirGlobal pCtx, + tDot11fWMMAddTSRequest *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_WMMAddTSRequest, IES_WMMAddTSRequest); + + return status; + +} /* End dot11f_unpack_wmm_add_ts_request. */ + +uint32_t dot11f_pack_wmm_add_ts_response(tpAniSirGlobal pCtx, + tDot11fWMMAddTSResponse *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_WMMAddTSResponse, IES_WMMAddTSResponse); + + return status; + +} /* End dot11f_unpack_wmm_add_ts_response. */ + +uint32_t dot11f_pack_wmm_del_ts(tpAniSirGlobal pCtx, + tDot11fWMMDelTS *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_WMMDelTS, IES_WMMDelTS); + + return status; + +} /* End dot11f_unpack_wmm_del_ts. */ + +uint32_t dot11f_pack_addba_req(tpAniSirGlobal pCtx, + tDot11faddba_req *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_addba_req, IES_addba_req); + + return status; + +} /* End dot11f_unpack_addba_req. */ + +uint32_t dot11f_pack_addba_rsp(tpAniSirGlobal pCtx, + tDot11faddba_rsp *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_addba_rsp, IES_addba_rsp); + + return status; + +} /* End dot11f_unpack_addba_rsp. */ + +uint32_t dot11f_pack_delba_req(tpAniSirGlobal pCtx, + tDot11fdelba_req *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_delba_req, IES_delba_req); + + return status; + +} /* End dot11f_unpack_delba_req. */ + +uint32_t dot11f_pack_ext_channel_switch_action_frame(tpAniSirGlobal pCtx, + tDot11fext_channel_switch_action_frame *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_ext_channel_switch_action_frame, IES_ext_channel_switch_action_frame); + + return status; + +} /* End dot11f_unpack_ext_channel_switch_action_frame. */ + +uint32_t dot11f_pack_ht2040_bss_coexistence_mgmt_action_frame(tpAniSirGlobal pCtx, + tDot11fht2040_bss_coexistence_mgmt_action_frame *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_ht2040_bss_coexistence_mgmt_action_frame, IES_ht2040_bss_coexistence_mgmt_action_frame); + + return status; + +} /* End dot11f_unpack_ht2040_bss_coexistence_mgmt_action_frame. */ + +uint32_t dot11f_pack_p2p_oper_chan_change_confirm(tpAniSirGlobal pCtx, + tDot11fp2p_oper_chan_change_confirm *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_p2p_oper_chan_change_confirm, IES_p2p_oper_chan_change_confirm); + + return status; + +} /* End dot11f_unpack_p2p_oper_chan_change_confirm. */ + +uint32_t dot11f_pack_vendor_action_frame(tpAniSirGlobal pCtx, + tDot11fvendor_action_frame *pFrm, + uint8_t *pBuf, uint32_t nBuf, uint32_t *pnConsumed) +{ + uint32_t i = 0; + uint32_t status = 0; + (void)i; + *pnConsumed = 0U; + status = pack_core(pCtx, (uint8_t *)pFrm, pBuf, nBuf, pnConsumed, + FFS_vendor_action_frame, IES_vendor_action_frame); + + return status; + +} /* End dot11f_unpack_vendor_action_frame. */ + +static uint32_t pack_core(tpAniSirGlobal pCtx, + uint8_t *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed, + const tFFDefn FFs[], + const tIEDefn IEs[]) +{ + const tFFDefn *pFf; + const tIEDefn *pIe; + tFRAMES_BOOL *pfFound; + uint8_t *pBufRemaining; + uint16_t i; + uint32_t nBufRemaining, status, len; + uint32_t countOffset = 0; + + (void)pCtx; /* Shutup the compiler if we have no FFs nor IEs... */ + i = 0; + + DOT11F_PARAMETER_CHECK2(pSrc, pBuf, nBuf, pnConsumed); + + status = DOT11F_PARSE_SUCCESS; + pBufRemaining = pBuf; + nBufRemaining = nBuf; + + pFf = &(FFs[0]); + while (pFf->size) { + if (pFf->size > nBufRemaining) { + FRAMES_LOG3(pCtx, FRLOGE, FRFL("The Fixed Field %s req" + "uires %d bytes, but there are only %d remaining.\n"), + pFf->name, pFf->size, nBufRemaining); + return DOT11F_BUFFER_OVERFLOW; + } + + switch (pFf->sig) { + case SigFfAID: + dot11f_pack_ff_aid( + pCtx, (tDot11fFfAID *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfAction: + dot11f_pack_ff_action( + pCtx, (tDot11fFfAction *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfAuthAlgo: + dot11f_pack_ff_auth_algo( + pCtx, (tDot11fFfAuthAlgo *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfAuthSeqNo: + dot11f_pack_ff_auth_seq_no( + pCtx, (tDot11fFfAuthSeqNo *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfBeaconInterval: + dot11f_pack_ff_beacon_interval( + pCtx, (tDot11fFfBeaconInterval *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfCapabilities: + dot11f_pack_ff_capabilities( + pCtx, (tDot11fFfCapabilities *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfCategory: + dot11f_pack_ff_category( + pCtx, (tDot11fFfCategory *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfCurrentAPAddress: + dot11f_pack_ff_current_ap_address( + pCtx, (tDot11fFfCurrentAPAddress *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfDialogToken: + dot11f_pack_ff_dialog_token( + pCtx, (tDot11fFfDialogToken *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfLinkMargin: + dot11f_pack_ff_link_margin( + pCtx, (tDot11fFfLinkMargin *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfListenInterval: + dot11f_pack_ff_listen_interval( + pCtx, (tDot11fFfListenInterval *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfMaxTxPower: + dot11f_pack_ff_max_tx_power( + pCtx, (tDot11fFfMaxTxPower *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfNumOfRepetitions: + dot11f_pack_ff_num_of_repetitions( + pCtx, (tDot11fFfNumOfRepetitions *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfOperatingMode: + dot11f_pack_ff_operating_mode( + pCtx, (tDot11fFfOperatingMode *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfRCPI: + dot11f_pack_ff_rcpi( + pCtx, (tDot11fFfRCPI *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfRSNI: + dot11f_pack_ff_rsni( + pCtx, (tDot11fFfRSNI *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfReason: + dot11f_pack_ff_reason( + pCtx, (tDot11fFfReason *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfRxAntennaId: + dot11f_pack_ff_rx_antenna_id( + pCtx, (tDot11fFfRxAntennaId *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfSMPowerModeSet: + dot11f_pack_ff_sm_power_mode_set( + pCtx, (tDot11fFfSMPowerModeSet *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfStatus: + dot11f_pack_ff_status( + pCtx, (tDot11fFfStatus *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfStatusCode: + dot11f_pack_ff_status_code( + pCtx, (tDot11fFfStatusCode *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfTPCEleID: + dot11f_pack_ff_tpc_ele_id( + pCtx, (tDot11fFfTPCEleID *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfTPCEleLen: + dot11f_pack_ff_tpc_ele_len( + pCtx, (tDot11fFfTPCEleLen *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfTSInfo: + dot11f_pack_ff_ts_info( + pCtx, (tDot11fFfTSInfo *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfTimeStamp: + dot11f_pack_ff_time_stamp( + pCtx, (tDot11fFfTimeStamp *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfTransactionId: + dot11f_pack_ff_transaction_id( + pCtx, (tDot11fFfTransactionId *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfTxAntennaId: + dot11f_pack_ff_tx_antenna_id( + pCtx, (tDot11fFfTxAntennaId *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfTxPower: + dot11f_pack_ff_tx_power( + pCtx, (tDot11fFfTxPower *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfVhtMembershipStatusArray: + dot11f_pack_ff_vht_membership_status_array( + pCtx, (tDot11fFfVhtMembershipStatusArray *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfVhtUserPositionArray: + dot11f_pack_ff_vht_user_position_array( + pCtx, (tDot11fFfVhtUserPositionArray *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfaddba_param_set: + dot11f_pack_ff_addba_param_set( + pCtx, (tDot11fFfaddba_param_set *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfba_start_seq_ctrl: + dot11f_pack_ff_ba_start_seq_ctrl( + pCtx, (tDot11fFfba_start_seq_ctrl *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfba_timeout: + dot11f_pack_ff_ba_timeout( + pCtx, (tDot11fFfba_timeout *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfdelba_param_set: + dot11f_pack_ff_delba_param_set( + pCtx, (tDot11fFfdelba_param_set *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfext_chan_switch_ann_action: + dot11f_pack_ff_ext_chan_switch_ann_action( + pCtx, (tDot11fFfext_chan_switch_ann_action *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfp2p_action_oui: + dot11f_pack_ff_p2p_action_oui( + pCtx, (tDot11fFfp2p_action_oui *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfp2p_action_subtype: + dot11f_pack_ff_p2p_action_subtype( + pCtx, (tDot11fFfp2p_action_subtype *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfvendor_action_subtype: + dot11f_pack_ff_vendor_action_subtype( + pCtx, (tDot11fFfvendor_action_subtype *) + (pSrc + pFf->offset), pBufRemaining); + break; + case SigFfvendor_oui: + dot11f_pack_ff_vendor_oui( + pCtx, (tDot11fFfvendor_oui *) + (pSrc + pFf->offset), pBufRemaining); + break; + default: + FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR-- I don" + "'t know about the Fixed Field %d; this is most l" + "ikely a bug in 'framesg'.\n"), pFf->sig); + return DOT11F_INTERNAL_ERROR; + } + + pBufRemaining += pFf->size; + nBufRemaining -= pFf->size; + *pnConsumed += pFf->size; + ++pFf; + + } + + pIe = &(IEs[0]); + while (0xff != pIe->eid || pIe->extn_eid) { + pfFound = (tFRAMES_BOOL *)(pSrc + pIe->offset + + pIe->presenceOffset); + if (*pfFound && pIe->minSize > nBufRemaining) { + FRAMES_LOG3(pCtx, FRLOGE, FRFL("The IE %s takes at le" + "ast %d bytes, but there are only %d left in the b" + "uffer.\n"), pIe->name, pIe->minSize, nBufRemaining); + return DOT11F_BUFFER_OVERFLOW; + } + + + countOffset = ((0 == pIe->arraybound) ? 1 : *(uint16_t *)(pSrc + pIe->countOffset)); + for (i = 0; i < countOffset; ++i) { + len = 0U; + switch (pIe->sig) { + case SigIeGTK: + status |= + dot11f_pack_ie_gtk( + pCtx, (tDot11fIEGTK *) + (pSrc + pIe->offset + + sizeof(tDot11fIEGTK) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeIGTK: + status |= + dot11f_pack_ie_igtk( + pCtx, (tDot11fIEIGTK *) + (pSrc + pIe->offset + + sizeof(tDot11fIEIGTK) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeR0KH_ID: + status |= + dot11f_pack_ie_r0_kh_id( + pCtx, (tDot11fIER0KH_ID *) + (pSrc + pIe->offset + + sizeof(tDot11fIER0KH_ID) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeR1KH_ID: + status |= + dot11f_pack_ie_r1_kh_id( + pCtx, (tDot11fIER1KH_ID *) + (pSrc + pIe->offset + + sizeof(tDot11fIER1KH_ID) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeversion_attr: + status |= + dot11f_pack_ie_version_attr( + pCtx, (tDot11fIEversion_attr *) + (pSrc + pIe->offset + + sizeof(tDot11fIEversion_attr) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIevht_mcs11_attr: + status |= + dot11f_pack_ie_vht_mcs11_attr( + pCtx, (tDot11fIEvht_mcs11_attr *) + (pSrc + pIe->offset + + sizeof(tDot11fIEvht_mcs11_attr) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeAPChannelReport: + status |= + dot11f_pack_ie_ap_channel_report( + pCtx, (tDot11fIEAPChannelReport *) + (pSrc + pIe->offset + + sizeof(tDot11fIEAPChannelReport) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeBcnReportingDetail: + status |= + dot11f_pack_ie_bcn_reporting_detail( + pCtx, (tDot11fIEBcnReportingDetail *) + (pSrc + pIe->offset + + sizeof(tDot11fIEBcnReportingDetail) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeBeaconReportFrmBody: + status |= + dot11f_pack_ie_beacon_report_frm_body( + pCtx, (tDot11fIEBeaconReportFrmBody *) + (pSrc + pIe->offset + + sizeof(tDot11fIEBeaconReportFrmBody) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeBeaconReporting: + status |= + dot11f_pack_ie_beacon_reporting( + pCtx, (tDot11fIEBeaconReporting *) + (pSrc + pIe->offset + + sizeof(tDot11fIEBeaconReporting) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeCondensedCountryStr: + status |= + dot11f_pack_ie_condensed_country_str( + pCtx, (tDot11fIECondensedCountryStr *) + (pSrc + pIe->offset + + sizeof(tDot11fIECondensedCountryStr) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeMeasurementPilot: + status |= + dot11f_pack_ie_measurement_pilot( + pCtx, (tDot11fIEMeasurementPilot *) + (pSrc + pIe->offset + + sizeof(tDot11fIEMeasurementPilot) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeMultiBssid: + status |= + dot11f_pack_ie_multi_bssid( + pCtx, (tDot11fIEMultiBssid *) + (pSrc + pIe->offset + + sizeof(tDot11fIEMultiBssid) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeRICData: + status |= + dot11f_pack_ie_ric_data( + pCtx, (tDot11fIERICData *) + (pSrc + pIe->offset + + sizeof(tDot11fIERICData) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeRICDescriptor: + status |= + dot11f_pack_ie_ric_descriptor( + pCtx, (tDot11fIERICDescriptor *) + (pSrc + pIe->offset + + sizeof(tDot11fIERICDescriptor) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeRRMEnabledCap: + status |= + dot11f_pack_ie_rrm_enabled_cap( + pCtx, (tDot11fIERRMEnabledCap *) + (pSrc + pIe->offset + + sizeof(tDot11fIERRMEnabledCap) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeRequestedInfo: + status |= + dot11f_pack_ie_requested_info( + pCtx, (tDot11fIERequestedInfo *) + (pSrc + pIe->offset + + sizeof(tDot11fIERequestedInfo) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeSSID: + status |= + dot11f_pack_ie_ssid( + pCtx, (tDot11fIESSID *) + (pSrc + pIe->offset + + sizeof(tDot11fIESSID) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeSchedule: + status |= + dot11f_pack_ie_schedule( + pCtx, (tDot11fIESchedule *) + (pSrc + pIe->offset + + sizeof(tDot11fIESchedule) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTCLAS: + status |= + dot11f_pack_ie_tclas( + pCtx, (tDot11fIETCLAS *) + (pSrc + pIe->offset + + sizeof(tDot11fIETCLAS) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTCLASSPROC: + status |= + dot11f_pack_ie_tclassproc( + pCtx, (tDot11fIETCLASSPROC *) + (pSrc + pIe->offset + + sizeof(tDot11fIETCLASSPROC) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTSDelay: + status |= + dot11f_pack_ie_ts_delay( + pCtx, (tDot11fIETSDelay *) + (pSrc + pIe->offset + + sizeof(tDot11fIETSDelay) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTSFInfo: + status |= + dot11f_pack_ie_tsf_info( + pCtx, (tDot11fIETSFInfo *) + (pSrc + pIe->offset + + sizeof(tDot11fIETSFInfo) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTSPEC: + status |= + dot11f_pack_ie_tspec( + pCtx, (tDot11fIETSPEC *) + (pSrc + pIe->offset + + sizeof(tDot11fIETSPEC) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeVHTCaps: + status |= + dot11f_pack_ie_vht_caps( + pCtx, (tDot11fIEVHTCaps *) + (pSrc + pIe->offset + + sizeof(tDot11fIEVHTCaps) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeVHTOperation: + status |= + dot11f_pack_ie_vht_operation( + pCtx, (tDot11fIEVHTOperation *) + (pSrc + pIe->offset + + sizeof(tDot11fIEVHTOperation) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWMMSchedule: + status |= + dot11f_pack_ie_wmm_schedule( + pCtx, (tDot11fIEWMMSchedule *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWMMSchedule) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWMMTCLAS: + status |= + dot11f_pack_ie_wmmtclas( + pCtx, (tDot11fIEWMMTCLAS *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWMMTCLAS) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWMMTCLASPROC: + status |= + dot11f_pack_ie_wmmtclasproc( + pCtx, (tDot11fIEWMMTCLASPROC *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWMMTCLASPROC) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWMMTSDelay: + status |= + dot11f_pack_ie_wmmts_delay( + pCtx, (tDot11fIEWMMTSDelay *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWMMTSDelay) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWMMTSPEC: + status |= + dot11f_pack_ie_wmmtspec( + pCtx, (tDot11fIEWMMTSPEC *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWMMTSPEC) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWiderBWChanSwitchAnn: + status |= + dot11f_pack_ie_wider_bw_chan_switch_ann( + pCtx, (tDot11fIEWiderBWChanSwitchAnn *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWiderBWChanSwitchAnn) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeazimuth_req: + status |= + dot11f_pack_ie_azimuth_req( + pCtx, (tDot11fIEazimuth_req *) + (pSrc + pIe->offset + + sizeof(tDot11fIEazimuth_req) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIebeacon_report_frm_body_fragment_id: + status |= + dot11f_pack_ie_beacon_report_frm_body_fragment_id( + pCtx, (tDot11fIEbeacon_report_frm_body_fragment_id *) + (pSrc + pIe->offset + + sizeof(tDot11fIEbeacon_report_frm_body_fragment_id) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIelast_beacon_report_indication: + status |= + dot11f_pack_ie_last_beacon_report_indication( + pCtx, (tDot11fIElast_beacon_report_indication *) + (pSrc + pIe->offset + + sizeof(tDot11fIElast_beacon_report_indication) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIemax_age: + status |= + dot11f_pack_ie_max_age( + pCtx, (tDot11fIEmax_age *) + (pSrc + pIe->offset + + sizeof(tDot11fIEmax_age) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeneighbor_rpt: + status |= + dot11f_pack_ie_neighbor_rpt( + pCtx, (tDot11fIEneighbor_rpt *) + (pSrc + pIe->offset + + sizeof(tDot11fIEneighbor_rpt) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIereq_mac_addr: + status |= + dot11f_pack_ie_req_mac_addr( + pCtx, (tDot11fIEreq_mac_addr *) + (pSrc + pIe->offset + + sizeof(tDot11fIEreq_mac_addr) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIetgt_mac_addr: + status |= + dot11f_pack_ie_tgt_mac_addr( + pCtx, (tDot11fIEtgt_mac_addr *) + (pSrc + pIe->offset + + sizeof(tDot11fIEtgt_mac_addr) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIevht_transmit_power_env: + status |= + dot11f_pack_ie_vht_transmit_power_env( + pCtx, (tDot11fIEvht_transmit_power_env *) + (pSrc + pIe->offset + + sizeof(tDot11fIEvht_transmit_power_env) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeAID: + status |= + dot11f_pack_ie_aid( + pCtx, (tDot11fIEAID *) + (pSrc + pIe->offset + + sizeof(tDot11fIEAID) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeBeaconReportStatus: + status |= + dot11f_pack_ie_BeaconReportStatus( + pCtx, (tDot11fIEBeaconReportStatus *) + (pSrc + pIe->offset + + sizeof(tDot11fIEBeaconReportStatus) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeCFParams: + status |= + dot11f_pack_ie_cf_params( + pCtx, (tDot11fIECFParams *) + (pSrc + pIe->offset + + sizeof(tDot11fIECFParams) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeChallengeText: + status |= + dot11f_pack_ie_challenge_text( + pCtx, (tDot11fIEChallengeText *) + (pSrc + pIe->offset + + sizeof(tDot11fIEChallengeText) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeChanSwitchAnn: + status |= + dot11f_pack_ie_chan_switch_ann( + pCtx, (tDot11fIEChanSwitchAnn *) + (pSrc + pIe->offset + + sizeof(tDot11fIEChanSwitchAnn) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeChannelSwitchWrapper: + status |= + dot11f_pack_ie_channel_switch_wrapper( + pCtx, (tDot11fIEChannelSwitchWrapper *) + (pSrc + pIe->offset + + sizeof(tDot11fIEChannelSwitchWrapper) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeCountry: + status |= + dot11f_pack_ie_country( + pCtx, (tDot11fIECountry *) + (pSrc + pIe->offset + + sizeof(tDot11fIECountry) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeDSParams: + status |= + dot11f_pack_ie_ds_params( + pCtx, (tDot11fIEDSParams *) + (pSrc + pIe->offset + + sizeof(tDot11fIEDSParams) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeEDCAParamSet: + status |= + dot11f_pack_ie_edca_param_set( + pCtx, (tDot11fIEEDCAParamSet *) + (pSrc + pIe->offset + + sizeof(tDot11fIEEDCAParamSet) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeERPInfo: + status |= + dot11f_pack_ie_erp_info( + pCtx, (tDot11fIEERPInfo *) + (pSrc + pIe->offset + + sizeof(tDot11fIEERPInfo) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeESECckmOpaque: + status |= + dot11f_pack_ie_ese_cckm_opaque( + pCtx, (tDot11fIEESECckmOpaque *) + (pSrc + pIe->offset + + sizeof(tDot11fIEESECckmOpaque) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeESERadMgmtCap: + status |= + dot11f_pack_ie_ese_rad_mgmt_cap( + pCtx, (tDot11fIEESERadMgmtCap *) + (pSrc + pIe->offset + + sizeof(tDot11fIEESERadMgmtCap) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeESETrafStrmMet: + status |= + dot11f_pack_ie_ese_traf_strm_met( + pCtx, (tDot11fIEESETrafStrmMet *) + (pSrc + pIe->offset + + sizeof(tDot11fIEESETrafStrmMet) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeESETrafStrmRateSet: + status |= + dot11f_pack_ie_ese_traf_strm_rate_set( + pCtx, (tDot11fIEESETrafStrmRateSet *) + (pSrc + pIe->offset + + sizeof(tDot11fIEESETrafStrmRateSet) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeESETxmitPower: + status |= + dot11f_pack_ie_ese_txmit_power( + pCtx, (tDot11fIEESETxmitPower *) + (pSrc + pIe->offset + + sizeof(tDot11fIEESETxmitPower) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeESEVersion: + status |= + dot11f_pack_ie_ese_version( + pCtx, (tDot11fIEESEVersion *) + (pSrc + pIe->offset + + sizeof(tDot11fIEESEVersion) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeExtCap: + status |= + dot11f_pack_ie_ext_cap( + pCtx, (tDot11fIEExtCap *) + (pSrc + pIe->offset + + sizeof(tDot11fIEExtCap) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeExtSuppRates: + status |= + dot11f_pack_ie_ext_supp_rates( + pCtx, (tDot11fIEExtSuppRates *) + (pSrc + pIe->offset + + sizeof(tDot11fIEExtSuppRates) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeFHParamSet: + status |= + dot11f_pack_ie_fh_param_set( + pCtx, (tDot11fIEFHParamSet *) + (pSrc + pIe->offset + + sizeof(tDot11fIEFHParamSet) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeFHParams: + status |= + dot11f_pack_ie_fh_params( + pCtx, (tDot11fIEFHParams *) + (pSrc + pIe->offset + + sizeof(tDot11fIEFHParams) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeFHPattTable: + status |= + dot11f_pack_ie_fh_patt_table( + pCtx, (tDot11fIEFHPattTable *) + (pSrc + pIe->offset + + sizeof(tDot11fIEFHPattTable) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeFTInfo: + status |= + dot11f_pack_ie_ft_info( + pCtx, (tDot11fIEFTInfo *) + (pSrc + pIe->offset + + sizeof(tDot11fIEFTInfo) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeHTCaps: + status |= + dot11f_pack_ie_ht_caps( + pCtx, (tDot11fIEHTCaps *) + (pSrc + pIe->offset + + sizeof(tDot11fIEHTCaps) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeHTInfo: + status |= + dot11f_pack_ie_ht_info( + pCtx, (tDot11fIEHTInfo *) + (pSrc + pIe->offset + + sizeof(tDot11fIEHTInfo) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeIBSSParams: + status |= + dot11f_pack_ie_ibss_params( + pCtx, (tDot11fIEIBSSParams *) + (pSrc + pIe->offset + + sizeof(tDot11fIEIBSSParams) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeLinkIdentifier: + status |= + dot11f_pack_ie_link_identifier( + pCtx, (tDot11fIELinkIdentifier *) + (pSrc + pIe->offset + + sizeof(tDot11fIELinkIdentifier) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeMBO_IE: + status |= + dot11f_pack_ie_MBO_IE( + pCtx, (tDot11fIEMBO_IE *) + (pSrc + pIe->offset + + sizeof(tDot11fIEMBO_IE) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeMeasurementReport: + status |= + dot11f_pack_ie_measurement_report( + pCtx, (tDot11fIEMeasurementReport *) + (pSrc + pIe->offset + + sizeof(tDot11fIEMeasurementReport) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeMeasurementRequest: + status |= + dot11f_pack_ie_measurement_request( + pCtx, (tDot11fIEMeasurementRequest *) + (pSrc + pIe->offset + + sizeof(tDot11fIEMeasurementRequest) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeMobilityDomain: + status |= + dot11f_pack_ie_mobility_domain( + pCtx, (tDot11fIEMobilityDomain *) + (pSrc + pIe->offset + + sizeof(tDot11fIEMobilityDomain) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeNeighborReport: + status |= + dot11f_pack_ie_neighbor_report( + pCtx, (tDot11fIENeighborReport *) + (pSrc + pIe->offset + + sizeof(tDot11fIENeighborReport) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeOBSSScanParameters: + status |= + dot11f_pack_ie_obss_scan_parameters( + pCtx, (tDot11fIEOBSSScanParameters *) + (pSrc + pIe->offset + + sizeof(tDot11fIEOBSSScanParameters) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeOperatingMode: + status |= + dot11f_pack_ie_operating_mode( + pCtx, (tDot11fIEOperatingMode *) + (pSrc + pIe->offset + + sizeof(tDot11fIEOperatingMode) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeP2PAssocReq: + status |= + dot11f_pack_ie_p2_p_assoc_req( + pCtx, (tDot11fIEP2PAssocReq *) + (pSrc + pIe->offset + + sizeof(tDot11fIEP2PAssocReq) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeP2PAssocRes: + status |= + dot11f_pack_ie_p2_p_assoc_res( + pCtx, (tDot11fIEP2PAssocRes *) + (pSrc + pIe->offset + + sizeof(tDot11fIEP2PAssocRes) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeP2PBeacon: + status |= + dot11f_pack_ie_p2_p_beacon( + pCtx, (tDot11fIEP2PBeacon *) + (pSrc + pIe->offset + + sizeof(tDot11fIEP2PBeacon) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeP2PBeaconProbeRes: + status |= + dot11f_pack_ie_p2_p_beacon_probe_res( + pCtx, (tDot11fIEP2PBeaconProbeRes *) + (pSrc + pIe->offset + + sizeof(tDot11fIEP2PBeaconProbeRes) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeP2PDeAuth: + status |= + dot11f_pack_ie_p2_p_de_auth( + pCtx, (tDot11fIEP2PDeAuth *) + (pSrc + pIe->offset + + sizeof(tDot11fIEP2PDeAuth) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeP2PDisAssoc: + status |= + dot11f_pack_ie_p2_p_dis_assoc( + pCtx, (tDot11fIEP2PDisAssoc *) + (pSrc + pIe->offset + + sizeof(tDot11fIEP2PDisAssoc) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeP2PIEOpaque: + status |= + dot11f_pack_ie_p2_pie_opaque( + pCtx, (tDot11fIEP2PIEOpaque *) + (pSrc + pIe->offset + + sizeof(tDot11fIEP2PIEOpaque) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeP2PProbeReq: + status |= + dot11f_pack_ie_p2_p_probe_req( + pCtx, (tDot11fIEP2PProbeReq *) + (pSrc + pIe->offset + + sizeof(tDot11fIEP2PProbeReq) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeP2PProbeRes: + status |= + dot11f_pack_ie_p2_p_probe_res( + pCtx, (tDot11fIEP2PProbeRes *) + (pSrc + pIe->offset + + sizeof(tDot11fIEP2PProbeRes) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIePTIControl: + status |= + dot11f_pack_ie_pti_control( + pCtx, (tDot11fIEPTIControl *) + (pSrc + pIe->offset + + sizeof(tDot11fIEPTIControl) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIePUBufferStatus: + status |= + dot11f_pack_ie_pu_buffer_status( + pCtx, (tDot11fIEPUBufferStatus *) + (pSrc + pIe->offset + + sizeof(tDot11fIEPUBufferStatus) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIePowerCaps: + status |= + dot11f_pack_ie_power_caps( + pCtx, (tDot11fIEPowerCaps *) + (pSrc + pIe->offset + + sizeof(tDot11fIEPowerCaps) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIePowerConstraints: + status |= + dot11f_pack_ie_power_constraints( + pCtx, (tDot11fIEPowerConstraints *) + (pSrc + pIe->offset + + sizeof(tDot11fIEPowerConstraints) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeQBSSLoad: + status |= + dot11f_pack_ie_qbss_load( + pCtx, (tDot11fIEQBSSLoad *) + (pSrc + pIe->offset + + sizeof(tDot11fIEQBSSLoad) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeQComVendorIE: + status |= + dot11f_pack_ie_QComVendorIE( + pCtx, (tDot11fIEQComVendorIE *) + (pSrc + pIe->offset + + sizeof(tDot11fIEQComVendorIE) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeQOSCapsAp: + status |= + dot11f_pack_ie_qos_caps_ap( + pCtx, (tDot11fIEQOSCapsAp *) + (pSrc + pIe->offset + + sizeof(tDot11fIEQOSCapsAp) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeQOSCapsStation: + status |= + dot11f_pack_ie_qos_caps_station( + pCtx, (tDot11fIEQOSCapsStation *) + (pSrc + pIe->offset + + sizeof(tDot11fIEQOSCapsStation) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeQosMapSet: + status |= + dot11f_pack_ie_qos_map_set( + pCtx, (tDot11fIEQosMapSet *) + (pSrc + pIe->offset + + sizeof(tDot11fIEQosMapSet) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeQuiet: + status |= + dot11f_pack_ie_quiet( + pCtx, (tDot11fIEQuiet *) + (pSrc + pIe->offset + + sizeof(tDot11fIEQuiet) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeRCPIIE: + status |= + dot11f_pack_ie_rcpiie( + pCtx, (tDot11fIERCPIIE *) + (pSrc + pIe->offset + + sizeof(tDot11fIERCPIIE) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeRICDataDesc: + status |= + dot11f_pack_ie_ric_data_desc( + pCtx, (tDot11fIERICDataDesc *) + (pSrc + pIe->offset + + sizeof(tDot11fIERICDataDesc) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeRSN: + status |= + dot11f_pack_ie_rsn( + pCtx, (tDot11fIERSN *) + (pSrc + pIe->offset + + sizeof(tDot11fIERSN) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeRSNIIE: + status |= + dot11f_pack_ie_rsniie( + pCtx, (tDot11fIERSNIIE *) + (pSrc + pIe->offset + + sizeof(tDot11fIERSNIIE) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeRSNOpaque: + status |= + dot11f_pack_ie_rsn_opaque( + pCtx, (tDot11fIERSNOpaque *) + (pSrc + pIe->offset + + sizeof(tDot11fIERSNOpaque) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeSuppChannels: + status |= + dot11f_pack_ie_supp_channels( + pCtx, (tDot11fIESuppChannels *) + (pSrc + pIe->offset + + sizeof(tDot11fIESuppChannels) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeSuppOperatingClasses: + status |= + dot11f_pack_ie_supp_operating_classes( + pCtx, (tDot11fIESuppOperatingClasses *) + (pSrc + pIe->offset + + sizeof(tDot11fIESuppOperatingClasses) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeSuppRates: + status |= + dot11f_pack_ie_supp_rates( + pCtx, (tDot11fIESuppRates *) + (pSrc + pIe->offset + + sizeof(tDot11fIESuppRates) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTIM: + status |= + dot11f_pack_ie_tim( + pCtx, (tDot11fIETIM *) + (pSrc + pIe->offset + + sizeof(tDot11fIETIM) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTPCReport: + status |= + dot11f_pack_ie_tpc_report( + pCtx, (tDot11fIETPCReport *) + (pSrc + pIe->offset + + sizeof(tDot11fIETPCReport) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTPCRequest: + status |= + dot11f_pack_ie_tpc_request( + pCtx, (tDot11fIETPCRequest *) + (pSrc + pIe->offset + + sizeof(tDot11fIETPCRequest) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTimeAdvertisement: + status |= + dot11f_pack_ie_time_advertisement( + pCtx, (tDot11fIETimeAdvertisement *) + (pSrc + pIe->offset + + sizeof(tDot11fIETimeAdvertisement) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeTimeoutInterval: + status |= + dot11f_pack_ie_timeout_interval( + pCtx, (tDot11fIETimeoutInterval *) + (pSrc + pIe->offset + + sizeof(tDot11fIETimeoutInterval) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeVHTExtBssLoad: + status |= + dot11f_pack_ie_vht_ext_bss_load( + pCtx, (tDot11fIEVHTExtBssLoad *) + (pSrc + pIe->offset + + sizeof(tDot11fIEVHTExtBssLoad) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeVendor1IE: + status |= + dot11f_pack_ie_vendor1_ie( + pCtx, (tDot11fIEVendor1IE *) + (pSrc + pIe->offset + + sizeof(tDot11fIEVendor1IE) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeVendor3IE: + status |= + dot11f_pack_ie_vendor3_ie( + pCtx, (tDot11fIEVendor3IE *) + (pSrc + pIe->offset + + sizeof(tDot11fIEVendor3IE) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWAPI: + status |= + dot11f_pack_ie_wapi( + pCtx, (tDot11fIEWAPI *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWAPI) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWAPIOpaque: + status |= + dot11f_pack_ie_wapi_opaque( + pCtx, (tDot11fIEWAPIOpaque *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWAPIOpaque) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWFATPC: + status |= + dot11f_pack_ie_wfatpc( + pCtx, (tDot11fIEWFATPC *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWFATPC) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWFDIEOpaque: + status |= + dot11f_pack_ie_wfdie_opaque( + pCtx, (tDot11fIEWFDIEOpaque *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWFDIEOpaque) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWMMCaps: + status |= + dot11f_pack_ie_wmm_caps( + pCtx, (tDot11fIEWMMCaps *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWMMCaps) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWMMInfoAp: + status |= + dot11f_pack_ie_wmm_info_ap( + pCtx, (tDot11fIEWMMInfoAp *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWMMInfoAp) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWMMInfoStation: + status |= + dot11f_pack_ie_wmm_info_station( + pCtx, (tDot11fIEWMMInfoStation *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWMMInfoStation) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWMMParams: + status |= + dot11f_pack_ie_wmm_params( + pCtx, (tDot11fIEWMMParams *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWMMParams) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWPA: + status |= + dot11f_pack_ie_wpa( + pCtx, (tDot11fIEWPA *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWPA) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWPAOpaque: + status |= + dot11f_pack_ie_wpa_opaque( + pCtx, (tDot11fIEWPAOpaque *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWPAOpaque) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWSC: + status |= + dot11f_pack_ie_wsc( + pCtx, (tDot11fIEWSC *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWSC) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWscAssocReq: + status |= + dot11f_pack_ie_wsc_assoc_req( + pCtx, (tDot11fIEWscAssocReq *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWscAssocReq) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWscAssocRes: + status |= + dot11f_pack_ie_wsc_assoc_res( + pCtx, (tDot11fIEWscAssocRes *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWscAssocRes) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWscBeacon: + status |= + dot11f_pack_ie_wsc_beacon( + pCtx, (tDot11fIEWscBeacon *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWscBeacon) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWscBeaconProbeRes: + status |= + dot11f_pack_ie_wsc_beacon_probe_res( + pCtx, (tDot11fIEWscBeaconProbeRes *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWscBeaconProbeRes) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWscIEOpaque: + status |= + dot11f_pack_ie_wsc_ie_opaque( + pCtx, (tDot11fIEWscIEOpaque *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWscIEOpaque) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWscProbeReq: + status |= + dot11f_pack_ie_wsc_probe_req( + pCtx, (tDot11fIEWscProbeReq *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWscProbeReq) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWscProbeRes: + status |= + dot11f_pack_ie_wsc_probe_res( + pCtx, (tDot11fIEWscProbeRes *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWscProbeRes) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeWscReassocRes: + status |= + dot11f_pack_ie_wsc_reassoc_res( + pCtx, (tDot11fIEWscReassocRes *) + (pSrc + pIe->offset + + sizeof(tDot11fIEWscReassocRes) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeaddba_extn_element: + status |= + dot11f_pack_ie_addba_extn_element( + pCtx, (tDot11fIEaddba_extn_element *) + (pSrc + pIe->offset + + sizeof(tDot11fIEaddba_extn_element) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIebss_color_change: + status |= + dot11f_pack_ie_bss_color_change( + pCtx, (tDot11fIEbss_color_change *) + (pSrc + pIe->offset + + sizeof(tDot11fIEbss_color_change) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIedh_parameter_element: + status |= + dot11f_pack_ie_dh_parameter_element( + pCtx, (tDot11fIEdh_parameter_element *) + (pSrc + pIe->offset + + sizeof(tDot11fIEdh_parameter_element) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeesp_information: + status |= + dot11f_pack_ie_esp_information( + pCtx, (tDot11fIEesp_information *) + (pSrc + pIe->offset + + sizeof(tDot11fIEesp_information) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeext_chan_switch_ann: + status |= + dot11f_pack_ie_ext_chan_switch_ann( + pCtx, (tDot11fIEext_chan_switch_ann *) + (pSrc + pIe->offset + + sizeof(tDot11fIEext_chan_switch_ann) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefils_assoc_delay_info: + status |= + dot11f_pack_ie_fils_assoc_delay_info( + pCtx, (tDot11fIEfils_assoc_delay_info *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfils_assoc_delay_info) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefils_hlp_container: + status |= + dot11f_pack_ie_fils_hlp_container( + pCtx, (tDot11fIEfils_hlp_container *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfils_hlp_container) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefils_indication: + status |= + dot11f_pack_ie_fils_indication( + pCtx, (tDot11fIEfils_indication *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfils_indication) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefils_kde: + status |= + dot11f_pack_ie_fils_kde( + pCtx, (tDot11fIEfils_kde *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfils_kde) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefils_key_confirmation: + status |= + dot11f_pack_ie_fils_key_confirmation( + pCtx, (tDot11fIEfils_key_confirmation *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfils_key_confirmation) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefils_nonce: + status |= + dot11f_pack_ie_fils_nonce( + pCtx, (tDot11fIEfils_nonce *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfils_nonce) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefils_public_key: + status |= + dot11f_pack_ie_fils_public_key( + pCtx, (tDot11fIEfils_public_key *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfils_public_key) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefils_session: + status |= + dot11f_pack_ie_fils_session( + pCtx, (tDot11fIEfils_session *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfils_session) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefils_wrapped_data: + status |= + dot11f_pack_ie_fils_wrapped_data( + pCtx, (tDot11fIEfils_wrapped_data *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfils_wrapped_data) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIefragment_ie: + status |= + dot11f_pack_ie_fragment_ie( + pCtx, (tDot11fIEfragment_ie *) + (pSrc + pIe->offset + + sizeof(tDot11fIEfragment_ie) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIehe_6ghz_band_cap: + status |= + dot11f_pack_ie_he_6ghz_band_cap( + pCtx, (tDot11fIEhe_6ghz_band_cap *) + (pSrc + pIe->offset + + sizeof(tDot11fIEhe_6ghz_band_cap) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIehe_cap: + status |= + dot11f_pack_ie_he_cap( + pCtx, (tDot11fIEhe_cap *) + (pSrc + pIe->offset + + sizeof(tDot11fIEhe_cap) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIehe_op: + status |= + dot11f_pack_ie_he_op( + pCtx, (tDot11fIEhe_op *) + (pSrc + pIe->offset + + sizeof(tDot11fIEhe_op) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIehs20vendor_ie: + status |= + dot11f_pack_ie_hs20vendor_ie( + pCtx, (tDot11fIEhs20vendor_ie *) + (pSrc + pIe->offset + + sizeof(tDot11fIEhs20vendor_ie) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeht2040_bss_coexistence: + status |= + dot11f_pack_ie_ht2040_bss_coexistence( + pCtx, (tDot11fIEht2040_bss_coexistence *) + (pSrc + pIe->offset + + sizeof(tDot11fIEht2040_bss_coexistence) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeht2040_bss_intolerant_report: + status |= + dot11f_pack_ie_ht2040_bss_intolerant_report( + pCtx, (tDot11fIEht2040_bss_intolerant_report *) + (pSrc + pIe->offset + + sizeof(tDot11fIEht2040_bss_intolerant_report) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIemu_edca_param_set: + status |= + dot11f_pack_ie_mu_edca_param_set( + pCtx, (tDot11fIEmu_edca_param_set *) + (pSrc + pIe->offset + + sizeof(tDot11fIEmu_edca_param_set) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeosen_ie: + status |= + dot11f_pack_ie_osen_ie( + pCtx, (tDot11fIEosen_ie *) + (pSrc + pIe->offset + + sizeof(tDot11fIEosen_ie) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeqcn_ie: + status |= + dot11f_pack_ie_qcn_ie( + pCtx, (tDot11fIEqcn_ie *) + (pSrc + pIe->offset + + sizeof(tDot11fIEqcn_ie) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIeroaming_consortium_sel: + status |= + dot11f_pack_ie_roaming_consortium_sel( + pCtx, (tDot11fIEroaming_consortium_sel *) + (pSrc + pIe->offset + + sizeof(tDot11fIEroaming_consortium_sel) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIesec_chan_offset_ele: + status |= + dot11f_pack_ie_sec_chan_offset_ele( + pCtx, (tDot11fIEsec_chan_offset_ele *) + (pSrc + pIe->offset + + sizeof(tDot11fIEsec_chan_offset_ele) * i), + pBufRemaining, nBufRemaining, &len); + break; + case SigIevendor_vht_ie: + status |= + dot11f_pack_ie_vendor_vht_ie( + pCtx, (tDot11fIEvendor_vht_ie *) + (pSrc + pIe->offset + + sizeof(tDot11fIEvendor_vht_ie) * i), + pBufRemaining, nBufRemaining, &len); + break; + default: + FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR-- I don" + "'t know about the IE %d; this is most likely a b" + "ug in 'framesc'.\n"), pFf->sig); + return DOT11F_INTERNAL_ERROR; + } + + pBufRemaining += len; + nBufRemaining -= len; + *pnConsumed += len; + } + + ++pIe; + + } + + return status; + +} + +static uint32_t pack_tlv_core(tpAniSirGlobal pCtx, + uint8_t *pSrc, + uint8_t *pBuf, + uint32_t nBuf, + uint32_t *pnConsumed, + const tTLVDefn TLVs[], + uint32_t *pidx) +{ + const tTLVDefn *pTlv; + tFRAMES_BOOL *pfFound; + uint8_t *pBufRemaining; + uint32_t nBufRemaining, status, len; + + DOT11F_PARAMETER_CHECK2(pSrc, pBuf, nBuf, pnConsumed); + + (void)pCtx; + status = DOT11F_PARSE_SUCCESS; + pBufRemaining = pBuf; + nBufRemaining = nBuf; + + pTlv = &(TLVs[0]); + while (0xffff != pTlv->id) { + pfFound = (tFRAMES_BOOL *)(pSrc + pTlv->offset + + pTlv->presenceOffset); + if (*pfFound && pTlv->minSize > nBufRemaining) { + FRAMES_LOG3(pCtx, FRLOGE, FRFL("The TLV %s takes at least" + " %d bytes, but there are only %d left in the buffer." + "\n"), pTlv->name, pTlv->minSize, nBufRemaining); + return DOT11F_BUFFER_OVERFLOW; + } + + len = 0U; + + if (*pfFound) { + switch (pTlv->sig) { + case SigTlvAuthorizedMACs: + status |= + dot11f_pack_tlv_authorized_ma_cs( + pCtx, (tDot11fTLVAuthorizedMACs *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvRequestToEnroll: + status |= + dot11f_pack_tlv_request_to_enroll( + pCtx, (tDot11fTLVRequestToEnroll *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvVersion2: + status |= + dot11f_pack_tlv_version2( + pCtx, (tDot11fTLVVersion2 *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvAPSetupLocked: + status |= + dot11f_pack_tlv_ap_setup_locked( + pCtx, (tDot11fTLVAPSetupLocked *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvAssociationState: + status |= + dot11f_pack_tlv_association_state( + pCtx, (tDot11fTLVAssociationState *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvConfigMethods: + status |= + dot11f_pack_tlv_config_methods( + pCtx, (tDot11fTLVConfigMethods *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvConfigurationError: + status |= + dot11f_pack_tlv_configuration_error( + pCtx, (tDot11fTLVConfigurationError *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvDeviceName: + status |= + dot11f_pack_tlv_device_name( + pCtx, (tDot11fTLVDeviceName *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvDevicePasswordID: + status |= + dot11f_pack_tlv_device_password_id( + pCtx, (tDot11fTLVDevicePasswordID *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvExtendedListenTiming: + status |= + dot11f_pack_tlv_extended_listen_timing( + pCtx, (tDot11fTLVExtendedListenTiming *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvListenChannel: + status |= + dot11f_pack_tlv_listen_channel( + pCtx, (tDot11fTLVListenChannel *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvManufacturer: + status |= + dot11f_pack_tlv_manufacturer( + pCtx, (tDot11fTLVManufacturer *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvMinorReasonCode: + status |= + dot11f_pack_tlv_minor_reason_code( + pCtx, (tDot11fTLVMinorReasonCode *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvModelName: + status |= + dot11f_pack_tlv_model_name( + pCtx, (tDot11fTLVModelName *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvModelNumber: + status |= + dot11f_pack_tlv_model_number( + pCtx, (tDot11fTLVModelNumber *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvNoticeOfAbsence: + status |= + dot11f_pack_tlv_notice_of_absence( + pCtx, (tDot11fTLVNoticeOfAbsence *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvOperatingChannel: + status |= + dot11f_pack_tlv_operating_channel( + pCtx, (tDot11fTLVOperatingChannel *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvP2PCapability: + status |= + dot11f_pack_tlv_p2_p_capability( + pCtx, (tDot11fTLVP2PCapability *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvP2PDeviceId: + status |= + dot11f_pack_tlv_p2_p_device_id( + pCtx, (tDot11fTLVP2PDeviceId *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvP2PDeviceInfo: + status |= + dot11f_pack_tlv_p2_p_device_info( + pCtx, (tDot11fTLVP2PDeviceInfo *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvP2PGroupInfo: + status |= + dot11f_pack_tlv_p2_p_group_info( + pCtx, (tDot11fTLVP2PGroupInfo *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvP2PStatus: + status |= + dot11f_pack_tlv_p2_p_status( + pCtx, (tDot11fTLVP2PStatus *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvPrimaryDeviceType: + status |= + dot11f_pack_tlv_primary_device_type( + pCtx, (tDot11fTLVPrimaryDeviceType *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvRFBands: + status |= + dot11f_pack_tlv_rf_bands( + pCtx, (tDot11fTLVRFBands *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvRequestDeviceType: + status |= + dot11f_pack_tlv_request_device_type( + pCtx, (tDot11fTLVRequestDeviceType *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvRequestType: + status |= + dot11f_pack_tlv_request_type( + pCtx, (tDot11fTLVRequestType *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvResponseType: + status |= + dot11f_pack_tlv_response_type( + pCtx, (tDot11fTLVResponseType *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvSelectedRegistrar: + status |= + dot11f_pack_tlv_selected_registrar( + pCtx, (tDot11fTLVSelectedRegistrar *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvSelectedRegistrarConfigMethods: + status |= + dot11f_pack_tlv_selected_registrar_config_methods( + pCtx, (tDot11fTLVSelectedRegistrarConfigMethods *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvSerialNumber: + status |= + dot11f_pack_tlv_serial_number( + pCtx, (tDot11fTLVSerialNumber *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvUUID_E: + status |= + dot11f_pack_tlv_uuid_e( + pCtx, (tDot11fTLVUUID_E *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvUUID_R: + status |= + dot11f_pack_tlv_uuid_r( + pCtx, (tDot11fTLVUUID_R *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvVendorExtension: + status |= + dot11f_pack_tlv_vendor_extension( + pCtx, (tDot11fTLVVendorExtension *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvVersion: + status |= + dot11f_pack_tlv_version( + pCtx, (tDot11fTLVVersion *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvWPSState: + status |= + dot11f_pack_tlv_wps_state( + pCtx, (tDot11fTLVWPSState *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvassoc_disallowed: + status |= + dot11f_pack_tlv_assoc_disallowed( + pCtx, (tDot11fTLVassoc_disallowed *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvassoc_retry_delay: + status |= + dot11f_pack_tlv_assoc_retry_delay( + pCtx, (tDot11fTLVassoc_retry_delay *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvcellular_data_cap: + status |= + dot11f_pack_tlv_cellular_data_cap( + pCtx, (tDot11fTLVcellular_data_cap *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvcellular_data_con_pref: + status |= + dot11f_pack_tlv_cellular_data_con_pref( + pCtx, (tDot11fTLVcellular_data_con_pref *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvmbo_ap_cap: + status |= + dot11f_pack_tlv_mbo_ap_cap( + pCtx, (tDot11fTLVmbo_ap_cap *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvnon_prefferd_chan_rep: + status |= + dot11f_pack_tlv_non_prefferd_chan_rep( + pCtx, (tDot11fTLVnon_prefferd_chan_rep *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvoce_cap: + status |= + dot11f_pack_tlv_oce_cap( + pCtx, (tDot11fTLVoce_cap *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvreduced_wan_metrics: + status |= + dot11f_pack_tlv_reduced_wan_metrics( + pCtx, (tDot11fTLVreduced_wan_metrics *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvrssi_assoc_rej: + status |= + dot11f_pack_tlv_rssi_assoc_rej( + pCtx, (tDot11fTLVrssi_assoc_rej *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvtransition_reason: + status |= + dot11f_pack_tlv_transition_reason( + pCtx, (tDot11fTLVtransition_reason *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvtransition_reject_reason: + status |= + dot11f_pack_tlv_transition_reject_reason( + pCtx, (tDot11fTLVtransition_reject_reason *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvP2PInterface: + status |= + dot11f_pack_tlv_p2_p_interface( + pCtx, (tDot11fTLVP2PInterface *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + case SigTlvP2PManageability: + status |= + dot11f_pack_tlv_p2_p_manageability( + pCtx, (tDot11fTLVP2PManageability *) + (pSrc + pTlv->offset), pBufRemaining, + nBufRemaining, &len); + break; + default: + FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR-- I don't " + "know about the TLV %d; this is most likely a bug in " + "'framesc'.\n"), pTlv->sig); + return DOT11F_INTERNAL_ERROR; + } + + } /* End if on *pfFound */ + pBufRemaining += len; + nBufRemaining -= len; + *pnConsumed += len; + ++pTlv; + if (len) + ++*pidx; + } + + return status; + +} diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/mac_trace.c b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/mac_trace.c new file mode 100644 index 0000000000000000000000000000000000000000..af14f4632df042b2c9a195a4379c320956e1fd4f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/mac_trace.c @@ -0,0 +1,715 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/**========================================================================= + + \file mac_trace.c + + \brief implementation for trace related APIs + + \author Sunit Bhatia + + ========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ + +#include "mac_trace.h" +#include "wma_types.h" +#include "csr_neighbor_roam.h" +#include "csr_internal.h" +#include "lim_global.h" +#include "lim_types.h" +#include "qdf_mem.h" +#include "qdf_trace.h" +#include "wma_if.h" + +/** + * mac_trace_get_neighbour_roam_state() - Get the neighbor roam state + * @neighbourroamstate: State in numeric form + * + * This function will return a string equivalent of the state. + * + * Return: String equivalent of the state. + **/ +uint8_t *mac_trace_get_neighbour_roam_state(uint16_t neighbourroamstate) +{ + switch (neighbourroamstate) { + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_CLOSED); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_INIT); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_CONNECTED); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_PREAUTHENTICATING); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE); + CASE_RETURN_STRING(eNEIGHBOR_STATE_MAX); + + default: + return (uint8_t *) "UNKNOWN"; + break; + } +} + +/** + * mac_trace_getcsr_roam_state() - Get the csr roam state + * @csr_roam_state: State in numeric form + * + * This function will return a string equivalent of the state. + * + * Return: String equivalent of the state. + **/ +uint8_t *mac_trace_getcsr_roam_state(uint16_t csr_roam_state) +{ + switch (csr_roam_state) { + CASE_RETURN_STRING(eCSR_ROAMING_STATE_STOP); + CASE_RETURN_STRING(eCSR_ROAMING_STATE_IDLE); + CASE_RETURN_STRING(eCSR_ROAMING_STATE_JOINING); + CASE_RETURN_STRING(eCSR_ROAMING_STATE_JOINED); + + default: + return (uint8_t *) "UNKNOWN"; + break; + } +} + +/** + * mac_trace_getcsr_roam_sub_state() - Get the csr roam sub state + * @csr_roam_sub_state: State in numeric form + * + * This function will return a string equivalent of the state. + * + * Return: String equivalent of the state. + **/ +uint8_t *mac_trace_getcsr_roam_sub_state(uint16_t csr_roam_sub_state) +{ + switch (csr_roam_sub_state) { + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_NONE); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_START_BSS_REQ); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_JOIN_REQ); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_REASSOC_REQ); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_DISASSOC_REQ); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_STOP_BSS_REQ); + CASE_RETURN_STRING + (eCSR_ROAM_SUBSTATE_DISCONNECT_CONTINUE_ROAMING); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_AUTH_REQ); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_CONFIG); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_DEAUTH_REQ); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_DISASSOC_NOTHING_TO_JOIN); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_DISASSOC_REASSOC_FAILURE); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_DISASSOC_FORCED); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_WAIT_FOR_KEY); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_DISASSOC_HANDOFF); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_JOINED_NO_TRAFFIC); + CASE_RETURN_STRING + (eCSR_ROAM_SUBSTATE_JOINED_NON_REALTIME_TRAFFIC); + CASE_RETURN_STRING(eCSR_ROAM_SUBSTATE_JOINED_REALTIME_TRAFFIC); + + default: + return (uint8_t *) "UNKNOWN"; + break; + } +} + +/** + * mac_trace_get_lim_sme_state() - Get the lim sme state + * @lim_state: State in numeric form + * + * This function will return a string equivalent of the state. + * + * Return: String equivalent of the state. + **/ +uint8_t *mac_trace_get_lim_sme_state(uint16_t lim_state) +{ + switch (lim_state) { + CASE_RETURN_STRING(eLIM_SME_OFFLINE_STATE); + CASE_RETURN_STRING(eLIM_SME_IDLE_STATE); + CASE_RETURN_STRING(eLIM_SME_SUSPEND_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_JOIN_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_AUTH_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_ASSOC_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_REASSOC_STATE); + CASE_RETURN_STRING(eLIM_SME_JOIN_FAILURE_STATE); + CASE_RETURN_STRING(eLIM_SME_ASSOCIATED_STATE); + CASE_RETURN_STRING(eLIM_SME_REASSOCIATED_STATE); + CASE_RETURN_STRING(eLIM_SME_LINK_EST_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_PRE_AUTH_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_DISASSOC_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_DEAUTH_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_START_BSS_STATE); + CASE_RETURN_STRING(eLIM_SME_WT_STOP_BSS_STATE); + CASE_RETURN_STRING(eLIM_SME_NORMAL_STATE); + + default: + return (uint8_t *) "UNKNOWN"; + break; + } +} + +/** + * mac_trace_get_lim_mlm_state() - Get the lim mlm state + * @mlmstate: State in numeric form + * + * This function will return a string equivalent of the state. + * + * Return: String equivalent of the state. + **/ +uint8_t *mac_trace_get_lim_mlm_state(uint16_t mlm_state) +{ + switch (mlm_state) { + CASE_RETURN_STRING(eLIM_MLM_OFFLINE_STATE); + CASE_RETURN_STRING(eLIM_MLM_IDLE_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_JOIN_BEACON_STATE); + CASE_RETURN_STRING(eLIM_MLM_JOINED_STATE); + CASE_RETURN_STRING(eLIM_MLM_BSS_STARTED_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_AUTH_FRAME2_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_AUTH_FRAME3_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_AUTH_FRAME4_STATE); + CASE_RETURN_STRING(eLIM_MLM_AUTH_RSP_TIMEOUT_STATE); + CASE_RETURN_STRING(eLIM_MLM_AUTHENTICATED_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ASSOC_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_REASSOC_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_ASSOCIATED_STATE); + CASE_RETURN_STRING(eLIM_MLM_REASSOCIATED_STATE); + CASE_RETURN_STRING(eLIM_MLM_LINK_ESTABLISHED_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ASSOC_CNF_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_BSS_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_DEL_BSS_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_BSS_RSP_ASSOC_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_BSS_RSP_REASSOC_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_BSS_RSP_PREASSOC_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_ADD_STA_RSP_STATE); + CASE_RETURN_STRING(eLIM_MLM_WT_DEL_STA_RSP_STATE); + + default: + return (uint8_t *) "UNKNOWN"; + break; + } +} + +#ifdef TRACE_RECORD +/** + * mac_trace_get_sme_msg_string() - Get the msg + * @sme_msg: message type in numeric form + * + * This function will return a string equivalent of the message. + * + * Return: String equivalent of the message type. + **/ +uint8_t *mac_trace_get_sme_msg_string(uint16_t sme_msg) +{ + switch (sme_msg) { + CASE_RETURN_STRING(eWNI_SME_SYS_READY_IND); + CASE_RETURN_STRING(eWNI_SME_JOIN_REQ); + CASE_RETURN_STRING(eWNI_SME_JOIN_RSP); + CASE_RETURN_STRING(eWNI_SME_SETCONTEXT_RSP); + CASE_RETURN_STRING(eWNI_SME_REASSOC_REQ); + CASE_RETURN_STRING(eWNI_SME_REASSOC_RSP); + CASE_RETURN_STRING(eWNI_SME_DISASSOC_REQ); + CASE_RETURN_STRING(eWNI_SME_DISASSOC_RSP); + CASE_RETURN_STRING(eWNI_SME_DISASSOC_IND); + CASE_RETURN_STRING(eWNI_SME_DISASSOC_CNF); + CASE_RETURN_STRING(eWNI_SME_DEAUTH_REQ); + CASE_RETURN_STRING(eWNI_SME_DEAUTH_RSP); + CASE_RETURN_STRING(eWNI_SME_DEAUTH_IND); + CASE_RETURN_STRING(eWNI_SME_DISCONNECT_DONE_IND); + CASE_RETURN_STRING(eWNI_SME_WM_STATUS_CHANGE_NTF); + CASE_RETURN_STRING(eWNI_SME_IBSS_NEW_PEER_IND); + CASE_RETURN_STRING(eWNI_SME_IBSS_PEER_DEPARTED_IND); + CASE_RETURN_STRING(eWNI_SME_START_BSS_REQ); + CASE_RETURN_STRING(eWNI_SME_START_BSS_RSP); + CASE_RETURN_STRING(eWNI_SME_ASSOC_IND); + CASE_RETURN_STRING(eWNI_SME_ASSOC_CNF); + CASE_RETURN_STRING(eWNI_SME_SWITCH_CHL_IND); + CASE_RETURN_STRING(eWNI_SME_STOP_BSS_REQ); + CASE_RETURN_STRING(eWNI_SME_STOP_BSS_RSP); + CASE_RETURN_STRING(eWNI_SME_DEAUTH_CNF); + CASE_RETURN_STRING(eWNI_SME_MIC_FAILURE_IND); + CASE_RETURN_STRING(eWNI_SME_ADDTS_REQ); + CASE_RETURN_STRING(eWNI_SME_ADDTS_RSP); + CASE_RETURN_STRING(eWNI_SME_DELTS_REQ); + CASE_RETURN_STRING(eWNI_SME_DELTS_RSP); + CASE_RETURN_STRING(eWNI_SME_DELTS_IND); + CASE_RETURN_STRING(eWNI_SME_ASSOC_IND_UPPER_LAYER); + CASE_RETURN_STRING(eWNI_SME_WPS_PBC_PROBE_REQ_IND); + CASE_RETURN_STRING(eWNI_SME_UPPER_LAYER_ASSOC_CNF); + CASE_RETURN_STRING(eWNI_SME_SESSION_UPDATE_PARAM); + CASE_RETURN_STRING(eWNI_SME_CHNG_MCC_BEACON_INTERVAL); + CASE_RETURN_STRING(eWNI_SME_GET_SNR_REQ); + CASE_RETURN_STRING(eWNI_SME_LINK_STATUS_IND); + CASE_RETURN_STRING(eWNI_SME_RRM_MSG_TYPE_BEGIN); + CASE_RETURN_STRING(eWNI_SME_NEIGHBOR_REPORT_REQ_IND); + CASE_RETURN_STRING(eWNI_SME_NEIGHBOR_REPORT_IND); + CASE_RETURN_STRING(eWNI_SME_BEACON_REPORT_REQ_IND); + CASE_RETURN_STRING(eWNI_SME_BEACON_REPORT_RESP_XMIT_IND); + CASE_RETURN_STRING(eWNI_SME_FT_PRE_AUTH_REQ); + CASE_RETURN_STRING(eWNI_SME_FT_PRE_AUTH_RSP); + CASE_RETURN_STRING(eWNI_SME_FT_AGGR_QOS_REQ); + CASE_RETURN_STRING(eWNI_SME_FT_AGGR_QOS_RSP); +#if defined FEATURE_WLAN_ESE + CASE_RETURN_STRING(eWNI_SME_ESE_ADJACENT_AP_REPORT); +#endif + CASE_RETURN_STRING(eWNI_SME_REGISTER_MGMT_FRAME_REQ); + CASE_RETURN_STRING(eWNI_SME_GENERIC_CHANGE_COUNTRY_CODE); + CASE_RETURN_STRING(eWNI_SME_MAX_ASSOC_EXCEEDED); +#ifdef WLAN_FEATURE_GTK_OFFLOAD + CASE_RETURN_STRING(eWNI_PMC_GTK_OFFLOAD_GETINFO_RSP); +#endif /* WLAN_FEATURE_GTK_OFFLOAD */ + CASE_RETURN_STRING(eWNI_SME_ROAM_SCAN_OFFLOAD_RSP); + CASE_RETURN_STRING(eWNI_SME_IBSS_PEER_INFO_RSP); + CASE_RETURN_STRING(eWNI_SME_DFS_RADAR_FOUND); + CASE_RETURN_STRING(eWNI_SME_CHANNEL_CHANGE_REQ); + CASE_RETURN_STRING(eWNI_SME_CHANNEL_CHANGE_RSP); + CASE_RETURN_STRING(eWNI_SME_START_BEACON_REQ); + CASE_RETURN_STRING(eWNI_SME_DFS_BEACON_CHAN_SW_IE_REQ); + CASE_RETURN_STRING(eWNI_SME_DFS_CSAIE_TX_COMPLETE_IND); + CASE_RETURN_STRING(eWNI_SME_STATS_EXT_EVENT); + CASE_RETURN_STRING(eWNI_SME_UPDATE_ADDITIONAL_IES); + CASE_RETURN_STRING(eWNI_SME_MODIFY_ADDITIONAL_IES); +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + CASE_RETURN_STRING(eWNI_SME_AUTO_SHUTDOWN_IND); +#endif + CASE_RETURN_STRING(eWNI_SME_SET_HT_2040_MODE); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + CASE_RETURN_STRING(eWNI_SME_HO_FAIL_IND); +#endif +#ifdef FEATURE_WLAN_TDLS + CASE_RETURN_STRING(eWNI_SME_TDLS_SEND_MGMT_REQ); + CASE_RETURN_STRING(eWNI_SME_TDLS_SEND_MGMT_RSP); + CASE_RETURN_STRING(eWNI_SME_TDLS_ADD_STA_REQ); + CASE_RETURN_STRING(eWNI_SME_TDLS_ADD_STA_RSP); + CASE_RETURN_STRING(eWNI_SME_TDLS_DEL_STA_REQ); + CASE_RETURN_STRING(eWNI_SME_TDLS_DEL_STA_RSP); + CASE_RETURN_STRING(eWNI_SME_TDLS_DEL_STA_IND); + CASE_RETURN_STRING(eWNI_SME_TDLS_DEL_ALL_PEER_IND); + CASE_RETURN_STRING(eWNI_SME_MGMT_FRM_TX_COMPLETION_IND); + CASE_RETURN_STRING(eWNI_SME_TDLS_LINK_ESTABLISH_REQ); + CASE_RETURN_STRING(eWNI_SME_TDLS_LINK_ESTABLISH_RSP); + CASE_RETURN_STRING(eWNI_SME_TDLS_SHOULD_DISCOVER); + CASE_RETURN_STRING(eWNI_SME_TDLS_SHOULD_TEARDOWN); + CASE_RETURN_STRING(eWNI_SME_TDLS_PEER_DISCONNECTED); +#endif + CASE_RETURN_STRING(eWNI_SME_RESET_AP_CAPS_CHANGED); +#ifdef WLAN_FEATURE_11W + CASE_RETURN_STRING(eWNI_SME_UNPROT_MGMT_FRM_IND); +#endif + CASE_RETURN_STRING(eWNI_SME_CANDIDATE_FOUND_IND); + CASE_RETURN_STRING(eWNI_SME_HANDOFF_REQ); + CASE_RETURN_STRING(eWNI_SME_GET_TSM_STATS_REQ); + CASE_RETURN_STRING(eWNI_SME_GET_TSM_STATS_RSP); + CASE_RETURN_STRING(eWNI_SME_TSM_IE_IND); + CASE_RETURN_STRING(eWNI_SME_SET_HW_MODE_REQ); + CASE_RETURN_STRING(eWNI_SME_SET_HW_MODE_RESP); + CASE_RETURN_STRING(eWNI_SME_HW_MODE_TRANS_IND); + CASE_RETURN_STRING(eWNI_SME_NSS_UPDATE_REQ); + CASE_RETURN_STRING(eWNI_SME_NSS_UPDATE_RSP); + CASE_RETURN_STRING(eWNI_SME_REGISTER_MGMT_FRAME_CB); + CASE_RETURN_STRING(eWNI_SME_HT40_OBSS_SCAN_IND); +#ifdef WLAN_FEATURE_NAN + CASE_RETURN_STRING(eWNI_SME_NAN_EVENT); +#endif +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + CASE_RETURN_STRING(eWNI_SME_READY_TO_EXTWOW_IND); +#endif + CASE_RETURN_STRING(eWNI_SME_MSG_GET_TEMPERATURE_IND); + CASE_RETURN_STRING(eWNI_SME_SNR_IND); +#ifdef FEATURE_WLAN_EXTSCAN + CASE_RETURN_STRING(eWNI_SME_EXTSCAN_FULL_SCAN_RESULT_IND); + CASE_RETURN_STRING(eWNI_SME_EPNO_NETWORK_FOUND_IND); +#endif + CASE_RETURN_STRING(eWNI_SME_SET_THERMAL_LEVEL_IND); + CASE_RETURN_STRING(eWNI_SME_OCB_SET_CONFIG_RSP); + CASE_RETURN_STRING(eWNI_SME_OCB_GET_TSF_TIMER_RSP); + CASE_RETURN_STRING(eWNI_SME_DCC_GET_STATS_RSP); + CASE_RETURN_STRING(eWNI_SME_DCC_UPDATE_NDL_RSP); + CASE_RETURN_STRING(eWNI_SME_DCC_STATS_EVENT); + CASE_RETURN_STRING(eWNI_SME_SET_DUAL_MAC_CFG_REQ); + CASE_RETURN_STRING(eWNI_SME_SET_DUAL_MAC_CFG_RESP); + CASE_RETURN_STRING(eWNI_SME_SET_IE_REQ); + CASE_RETURN_STRING(eWNI_SME_EXT_CHANGE_CHANNEL); + CASE_RETURN_STRING(eWNI_SME_EXT_CHANGE_CHANNEL_IND); + CASE_RETURN_STRING(eWNI_SME_SET_ANTENNA_MODE_REQ); + CASE_RETURN_STRING(eWNI_SME_SET_ANTENNA_MODE_RESP); + CASE_RETURN_STRING(eWNI_SME_TSF_EVENT); + CASE_RETURN_STRING(eWNI_SME_MON_INIT_SESSION); + CASE_RETURN_STRING(eWNI_SME_MON_DEINIT_SESSION); + CASE_RETURN_STRING(eWNI_SME_PDEV_SET_HT_VHT_IE); + CASE_RETURN_STRING(eWNI_SME_SET_VDEV_IES_PER_BAND); + CASE_RETURN_STRING(eWNI_SME_SEND_DISASSOC_FRAME); + CASE_RETURN_STRING(eWNI_SME_UPDATE_ACCESS_POLICY_VENDOR_IE); + CASE_RETURN_STRING(eWNI_SME_DEFAULT_SCAN_IE); + CASE_RETURN_STRING(eWNI_SME_ROAM_SCAN_OFFLOAD_REQ); + CASE_RETURN_STRING(eWNI_SME_ROAM_INIT_PARAM); + CASE_RETURN_STRING(eWNI_SME_LOST_LINK_INFO_IND); + CASE_RETURN_STRING(eWNI_SME_GET_PEER_INFO_EXT_IND); + CASE_RETURN_STRING(eWNI_SME_RSO_CMD_STATUS_IND); + CASE_RETURN_STRING(eWNI_SME_TRIGGER_SAE); + CASE_RETURN_STRING(eWNI_SME_SEND_MGMT_FRAME_TX); + CASE_RETURN_STRING(eWNI_SME_SEND_SAE_MSG); + CASE_RETURN_STRING(eWNI_SME_CSA_RESTART_REQ); + CASE_RETURN_STRING(eWNI_SME_CSA_RESTART_RSP); + CASE_RETURN_STRING(eWNI_SME_MSG_TYPES_END); + CASE_RETURN_STRING(eWNI_SME_HIDDEN_SSID_RESTART_RSP); + CASE_RETURN_STRING(eWNI_SME_STA_CSA_CONTINUE_REQ); + CASE_RETURN_STRING(eWNI_SME_ANTENNA_ISOLATION_RSP); + default: + return (uint8_t *) "UNKNOWN"; + break; + } +} +#endif + +/** + * mac_trace_get_wma_msg_string() - Get the msg + * @wma_msg: message type in numeric form + * + * This function will return a string equivalent of the message. + * + * Return: String equivalent of the message type. + **/ +uint8_t *mac_trace_get_wma_msg_string(uint16_t wma_msg) +{ + switch (wma_msg) { + CASE_RETURN_STRING(WMA_ADD_STA_REQ); + CASE_RETURN_STRING(WMA_ADD_STA_RSP); + CASE_RETURN_STRING(WMA_DELETE_STA_REQ); + CASE_RETURN_STRING(WMA_DELETE_STA_RSP); + CASE_RETURN_STRING(WMA_ADD_BSS_REQ); + CASE_RETURN_STRING(WMA_DELETE_BSS_REQ); + CASE_RETURN_STRING(WMA_DELETE_BSS_HO_FAIL_REQ); + CASE_RETURN_STRING(WMA_DELETE_BSS_RSP); + CASE_RETURN_STRING(WMA_DELETE_BSS_HO_FAIL_RSP); + CASE_RETURN_STRING(WMA_SEND_BEACON_REQ); + CASE_RETURN_STRING(WMA_SET_BSSKEY_RSP); + CASE_RETURN_STRING(WMA_SET_STAKEY_RSP); + CASE_RETURN_STRING(WMA_UPDATE_EDCA_PROFILE_IND); + + CASE_RETURN_STRING(WMA_UPDATE_BEACON_IND); + CASE_RETURN_STRING(WMA_CHNL_SWITCH_REQ); + CASE_RETURN_STRING(WMA_ADD_TS_REQ); + CASE_RETURN_STRING(WMA_DEL_TS_REQ); + CASE_RETURN_STRING(WMA_MISSED_BEACON_IND); + + CASE_RETURN_STRING(WMA_SWITCH_CHANNEL_RSP); + CASE_RETURN_STRING(WMA_P2P_NOA_ATTR_IND); + CASE_RETURN_STRING(WMA_PWR_SAVE_CFG); + + CASE_RETURN_STRING(WMA_IBSS_STA_ADD); + CASE_RETURN_STRING(WMA_TIMER_ADJUST_ADAPTIVE_THRESHOLD_IND); + CASE_RETURN_STRING(WMA_SET_STA_BCASTKEY_RSP); + CASE_RETURN_STRING(WMA_ADD_TS_RSP); + CASE_RETURN_STRING(WMA_DPU_MIC_ERROR); + + CASE_RETURN_STRING(WMA_TIMER_CHIP_MONITOR_TIMEOUT); + CASE_RETURN_STRING(WMA_TIMER_TRAFFIC_ACTIVITY_REQ); + CASE_RETURN_STRING(WMA_TIMER_ADC_RSSI_STATS); +#ifdef FEATURE_WLAN_ESE + CASE_RETURN_STRING(WMA_TSM_STATS_REQ); + CASE_RETURN_STRING(WMA_TSM_STATS_RSP); +#endif + CASE_RETURN_STRING(WMA_HT40_OBSS_SCAN_IND); + CASE_RETURN_STRING(WMA_SET_MIMOPS_REQ); + CASE_RETURN_STRING(WMA_SET_MIMOPS_RSP); + CASE_RETURN_STRING(WMA_SYS_READY_IND); + CASE_RETURN_STRING(WMA_SET_TX_POWER_REQ); + CASE_RETURN_STRING(WMA_SET_TX_POWER_RSP); + CASE_RETURN_STRING(WMA_GET_TX_POWER_REQ); + + CASE_RETURN_STRING(WMA_ENABLE_UAPSD_REQ); + CASE_RETURN_STRING(WMA_DISABLE_UAPSD_REQ); + CASE_RETURN_STRING(WMA_SET_KEY_DONE); + + CASE_RETURN_STRING(WMA_BTC_SET_CFG); + CASE_RETURN_STRING(WMA_HANDLE_FW_MBOX_RSP); + CASE_RETURN_STRING(WMA_SEND_PROBE_RSP_TMPL); + CASE_RETURN_STRING(WMA_SET_MAX_TX_POWER_REQ); + CASE_RETURN_STRING(WMA_SET_HOST_OFFLOAD); + CASE_RETURN_STRING(WMA_SET_KEEP_ALIVE); +#ifdef WLAN_NS_OFFLOAD + CASE_RETURN_STRING(WMA_SET_NS_OFFLOAD); +#endif /* WLAN_NS_OFFLOAD */ + CASE_RETURN_STRING(WMA_WLAN_SUSPEND_IND); + CASE_RETURN_STRING(WMA_WLAN_RESUME_REQ); +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + CASE_RETURN_STRING(WMA_WLAN_EXT_WOW); + CASE_RETURN_STRING(WMA_WLAN_SET_APP_TYPE1_PARAMS); + CASE_RETURN_STRING(WMA_WLAN_SET_APP_TYPE2_PARAMS); +#endif + CASE_RETURN_STRING(WMA_MSG_TYPES_END); + CASE_RETURN_STRING(WMA_AGGR_QOS_REQ); + CASE_RETURN_STRING(WMA_AGGR_QOS_RSP); + CASE_RETURN_STRING(WMA_ROAM_SCAN_OFFLOAD_REQ); + CASE_RETURN_STRING(WMA_ROAM_PRE_AUTH_STATUS); +#ifdef WLAN_FEATURE_PACKET_FILTERING + CASE_RETURN_STRING(WMA_8023_MULTICAST_LIST_REQ); + CASE_RETURN_STRING(WMA_RECEIVE_FILTER_SET_FILTER_REQ); + CASE_RETURN_STRING + (WMA_PACKET_COALESCING_FILTER_MATCH_COUNT_REQ); + CASE_RETURN_STRING + (WMA_PACKET_COALESCING_FILTER_MATCH_COUNT_RSP); + CASE_RETURN_STRING(WMA_RECEIVE_FILTER_CLEAR_FILTER_REQ); +#endif /* WLAN_FEATURE_PACKET_FILTERING */ +#ifdef WLAN_FEATURE_GTK_OFFLOAD + CASE_RETURN_STRING(WMA_GTK_OFFLOAD_REQ); + CASE_RETURN_STRING(WMA_GTK_OFFLOAD_GETINFO_REQ); + CASE_RETURN_STRING(WMA_GTK_OFFLOAD_GETINFO_RSP); +#endif /* WLAN_FEATURE_GTK_OFFLOAD */ + CASE_RETURN_STRING(WMA_SET_TM_LEVEL_REQ); + CASE_RETURN_STRING(WMA_UPDATE_OP_MODE); + CASE_RETURN_STRING(WMA_UPDATE_MEMBERSHIP); + CASE_RETURN_STRING(WMA_UPDATE_USERPOS); + CASE_RETURN_STRING(WMA_UPDATE_CHAN_LIST_REQ); + CASE_RETURN_STRING(WMA_CLI_SET_CMD); +#ifndef REMOVE_PKT_LOG + CASE_RETURN_STRING(WMA_PKTLOG_ENABLE_REQ); +#endif +#ifdef FEATURE_WLAN_ESE + CASE_RETURN_STRING(WMA_SET_PLM_REQ); +#endif + CASE_RETURN_STRING(WMA_RATE_UPDATE_IND); + CASE_RETURN_STRING(WMA_INIT_BAD_PEER_TX_CTL_INFO_CMD); +#ifdef FEATURE_WLAN_TDLS + CASE_RETURN_STRING(WMA_UPDATE_TDLS_PEER_STATE); +#endif + CASE_RETURN_STRING(WMA_ADD_PERIODIC_TX_PTRN_IND); + CASE_RETURN_STRING(WMA_TX_POWER_LIMIT); + CASE_RETURN_STRING(WMA_DHCP_START_IND); + CASE_RETURN_STRING(WMA_DHCP_STOP_IND); +#ifdef FEATURE_WLAN_CH_AVOID + CASE_RETURN_STRING(WMA_CH_AVOID_UPDATE_REQ); +#endif +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + CASE_RETURN_STRING(WMA_SET_AUTO_SHUTDOWN_TIMER_REQ); +#endif + CASE_RETURN_STRING(WMA_IBSS_CESIUM_ENABLE_IND); + CASE_RETURN_STRING(WMA_GET_IBSS_PEER_INFO_REQ); + CASE_RETURN_STRING(WMA_TX_FAIL_MONITOR_IND); +#ifdef FEATURE_WLAN_RMC + CASE_RETURN_STRING(WMA_RMC_ENABLE_IND); + CASE_RETURN_STRING(WMA_RMC_DISABLE_IND); + CASE_RETURN_STRING(WMA_RMC_ACTION_PERIOD_IND); +#endif + CASE_RETURN_STRING(WMA_INIT_THERMAL_INFO_CMD); + CASE_RETURN_STRING(WMA_SET_THERMAL_LEVEL); + CASE_RETURN_STRING(WMA_SET_SAP_INTRABSS_DIS); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + CASE_RETURN_STRING(WMA_ROAM_OFFLOAD_SYNCH_FAIL); +#endif + CASE_RETURN_STRING(SIR_HAL_SET_BASE_MACADDR_IND); + CASE_RETURN_STRING(WMA_LINK_STATUS_GET_REQ); +#ifdef DHCP_SERVER_OFFLOAD + CASE_RETURN_STRING(WMA_SET_DHCP_SERVER_OFFLOAD_CMD); +#endif + CASE_RETURN_STRING(WMA_OCB_SET_CONFIG_CMD); + CASE_RETURN_STRING(WMA_OCB_SET_UTC_TIME_CMD); + CASE_RETURN_STRING(WMA_OCB_START_TIMING_ADVERT_CMD); + CASE_RETURN_STRING(WMA_OCB_STOP_TIMING_ADVERT_CMD); + CASE_RETURN_STRING(WMA_OCB_GET_TSF_TIMER_CMD); + CASE_RETURN_STRING(WMA_DCC_GET_STATS_CMD); + CASE_RETURN_STRING(WMA_DCC_CLEAR_STATS_CMD); + CASE_RETURN_STRING(WMA_DCC_UPDATE_NDL_CMD); + CASE_RETURN_STRING(WMA_SET_IE_INFO); + CASE_RETURN_STRING(WMA_LRO_CONFIG_CMD); + CASE_RETURN_STRING(WMA_GW_PARAM_UPDATE_REQ); + CASE_RETURN_STRING(WMA_ADD_BCN_FILTER_CMDID); + CASE_RETURN_STRING(WMA_REMOVE_BCN_FILTER_CMDID); + CASE_RETURN_STRING(WMA_SET_ADAPT_DWELLTIME_CONF_PARAMS); + CASE_RETURN_STRING(WDA_APF_GET_CAPABILITIES_REQ); + CASE_RETURN_STRING(WMA_ROAM_SYNC_TIMEOUT); + CASE_RETURN_STRING(WMA_SET_PDEV_IE_REQ); + CASE_RETURN_STRING(WMA_UPDATE_WEP_DEFAULT_KEY); + CASE_RETURN_STRING(WMA_SEND_FREQ_RANGE_CONTROL_IND); + CASE_RETURN_STRING(WMA_POWER_DEBUG_STATS_REQ); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + CASE_RETURN_STRING(SIR_HAL_ROAM_INVOKE); +#endif + CASE_RETURN_STRING(SIR_HAL_SET_MAS); + CASE_RETURN_STRING(SIR_HAL_SET_MIRACAST); + CASE_RETURN_STRING(SIR_HAL_CONFIG_STATS_FACTOR); + CASE_RETURN_STRING(SIR_HAL_CONFIG_GUARD_TIME); + CASE_RETURN_STRING(SIR_HAL_START_STOP_LOGGING); + CASE_RETURN_STRING(SIR_HAL_FLUSH_LOG_TO_FW); + CASE_RETURN_STRING(SIR_HAL_PDEV_SET_PCL_TO_FW); + CASE_RETURN_STRING(SIR_HAL_PDEV_SET_HW_MODE); + CASE_RETURN_STRING(SIR_HAL_PDEV_DUAL_MAC_CFG_REQ); + CASE_RETURN_STRING(WMA_RADAR_DETECTED_IND); + CASE_RETURN_STRING(WMA_TIMER_TRAFFIC_STATS_IND); +#ifdef WLAN_FEATURE_11W + CASE_RETURN_STRING(WMA_EXCLUDE_UNENCRYPTED_IND); +#endif + CASE_RETURN_STRING(WMA_SET_MAX_TX_POWER_RSP); + CASE_RETURN_STRING(WMA_SET_DTIM_PERIOD); + CASE_RETURN_STRING(WMA_SET_MAX_TX_POWER_PER_BAND_REQ); +#ifdef FEATURE_WLAN_TDLS + CASE_RETURN_STRING(WMA_SET_TDLS_LINK_ESTABLISH_REQ); + CASE_RETURN_STRING(WMA_SET_TDLS_LINK_ESTABLISH_REQ_RSP); +#endif + CASE_RETURN_STRING(WMA_CSA_OFFLOAD_EVENT); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + CASE_RETURN_STRING(WMA_ROAM_OFFLOAD_SYNCH_IND); +#endif + CASE_RETURN_STRING(WMA_UPDATE_RX_NSS); +#ifdef WLAN_FEATURE_NAN + CASE_RETURN_STRING(WMA_NAN_REQUEST); +#endif + CASE_RETURN_STRING(WMA_RX_SCAN_EVENT); + CASE_RETURN_STRING(WMA_RX_CHN_STATUS_EVENT); + CASE_RETURN_STRING(WMA_IBSS_PEER_INACTIVITY_IND); + CASE_RETURN_STRING(WMA_DEL_PERIODIC_TX_PTRN_IND); +#ifdef FEATURE_WLAN_TDLS + CASE_RETURN_STRING(WMA_TDLS_SHOULD_DISCOVER_CMD); + CASE_RETURN_STRING(WMA_TDLS_SHOULD_TEARDOWN_CMD); + CASE_RETURN_STRING(WMA_TDLS_PEER_DISCONNECTED_CMD); +#endif + CASE_RETURN_STRING(WMA_DFS_BEACON_TX_SUCCESS_IND); + CASE_RETURN_STRING(WMA_DISASSOC_TX_COMP); + CASE_RETURN_STRING(WMA_DEAUTH_TX_COMP); + CASE_RETURN_STRING(WMA_MODEM_POWER_STATE_IND); +#ifdef WLAN_FEATURE_STATS_EXT + CASE_RETURN_STRING(WMA_STATS_EXT_REQUEST); +#endif + CASE_RETURN_STRING(WMA_GET_TEMPERATURE_REQ); +#ifdef FEATURE_WLAN_EXTSCAN + CASE_RETURN_STRING(WMA_EXTSCAN_GET_CAPABILITIES_REQ); + CASE_RETURN_STRING(WMA_EXTSCAN_START_REQ); + CASE_RETURN_STRING(WMA_EXTSCAN_STOP_REQ); + CASE_RETURN_STRING(WMA_EXTSCAN_SET_BSSID_HOTLIST_REQ); + CASE_RETURN_STRING(WMA_EXTSCAN_RESET_BSSID_HOTLIST_REQ); + CASE_RETURN_STRING(WMA_EXTSCAN_SET_SIGNF_CHANGE_REQ); + CASE_RETURN_STRING(WMA_EXTSCAN_RESET_SIGNF_CHANGE_REQ); + CASE_RETURN_STRING(WMA_EXTSCAN_GET_CACHED_RESULTS_REQ); + CASE_RETURN_STRING(WMA_SET_EPNO_LIST_REQ); + CASE_RETURN_STRING(WMA_SET_PASSPOINT_LIST_REQ); + CASE_RETURN_STRING(WMA_RESET_PASSPOINT_LIST_REQ); +#endif /* FEATURE_WLAN_EXTSCAN */ +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + CASE_RETURN_STRING(WMA_LINK_LAYER_STATS_CLEAR_REQ); + CASE_RETURN_STRING(WMA_LINK_LAYER_STATS_SET_REQ); + CASE_RETURN_STRING(WMA_LINK_LAYER_STATS_GET_REQ); + CASE_RETURN_STRING(WMA_LINK_LAYER_STATS_RESULTS_RSP); +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + CASE_RETURN_STRING(WMA_SET_SCAN_MAC_OUI_REQ); + CASE_RETURN_STRING(WMA_TSF_GPIO_PIN); +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING + CASE_RETURN_STRING(WMA_LED_FLASHING_REQ); +#endif +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + CASE_RETURN_STRING(WMA_UPDATE_Q2Q_IE_IND); +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + CASE_RETURN_STRING(WMA_SET_RSSI_MONITOR_REQ); + CASE_RETURN_STRING(WMA_SET_WISA_PARAMS); + CASE_RETURN_STRING(WMA_SET_WOW_PULSE_CMD); + CASE_RETURN_STRING(WMA_SET_PER_ROAM_CONFIG_CMD); + CASE_RETURN_STRING(WMA_GET_RCPI_REQ); + CASE_RETURN_STRING(WMA_SET_DBS_SCAN_SEL_CONF_PARAMS); + CASE_RETURN_STRING(WMA_GET_ROAM_SCAN_STATS); +#ifdef FW_THERMAL_THROTTLE_SUPPORT + CASE_RETURN_STRING(WMA_SET_THERMAL_THROTTLE_CFG); + CASE_RETURN_STRING(WMA_SET_THERMAL_MGMT); +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + default: + return (uint8_t *) "UNKNOWN"; + break; + } +} + +#ifdef TRACE_RECORD +/** + * mac_trace_get_lim_msg_string() - Get the msg + * @lim_msg: message type in numeric form + * + * This function will return a string equivalent of the message. + * + * Return: String equivalent of the message type. + **/ +uint8_t *mac_trace_get_lim_msg_string(uint16_t lim_msg) +{ + switch (lim_msg) { + CASE_RETURN_STRING(SIR_BB_XPORT_MGMT_MSG); + CASE_RETURN_STRING(SIR_LIM_DELETE_STA_CONTEXT_IND); + CASE_RETURN_STRING(SIR_LIM_UPDATE_BEACON); + CASE_RETURN_STRING(SIR_LIM_JOIN_FAIL_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_AUTH_FAIL_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_AUTH_RSP_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_ASSOC_FAIL_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_REASSOC_FAIL_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_HEART_BEAT_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_PROBE_HB_FAILURE_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_ADDTS_RSP_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_LINK_TEST_DURATION_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_CNF_WAIT_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_UPDATE_OLBC_CACHEL_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_CHANNEL_SWITCH_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_WPS_OVERLAP_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_FT_PREAUTH_RSP_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_BEACON_GEN_IND); + CASE_RETURN_STRING(SIR_LIM_DISASSOC_ACK_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_DEAUTH_ACK_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_PERIODIC_JOIN_PROBE_REQ_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_AUTH_RETRY_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_AUTH_SAE_TIMEOUT); + CASE_RETURN_STRING(SIR_LIM_MSG_TYPES_END); + CASE_RETURN_STRING(LIM_MLM_SCAN_REQ); + CASE_RETURN_STRING(LIM_MLM_SCAN_CNF); + CASE_RETURN_STRING(LIM_MLM_START_CNF); + CASE_RETURN_STRING(LIM_MLM_JOIN_REQ); + CASE_RETURN_STRING(LIM_MLM_JOIN_CNF); + CASE_RETURN_STRING(LIM_MLM_AUTH_REQ); + CASE_RETURN_STRING(LIM_MLM_AUTH_CNF); + CASE_RETURN_STRING(LIM_MLM_AUTH_IND); + CASE_RETURN_STRING(LIM_MLM_ASSOC_REQ); + CASE_RETURN_STRING(LIM_MLM_ASSOC_CNF); + CASE_RETURN_STRING(LIM_MLM_ASSOC_IND); + CASE_RETURN_STRING(LIM_MLM_DISASSOC_REQ); + CASE_RETURN_STRING(LIM_MLM_DISASSOC_CNF); + CASE_RETURN_STRING(LIM_MLM_DISASSOC_IND); + CASE_RETURN_STRING(LIM_MLM_REASSOC_CNF); + CASE_RETURN_STRING(LIM_MLM_REASSOC_IND); + CASE_RETURN_STRING(LIM_MLM_DEAUTH_REQ); + CASE_RETURN_STRING(LIM_MLM_DEAUTH_CNF); + CASE_RETURN_STRING(LIM_MLM_DEAUTH_IND); + CASE_RETURN_STRING(LIM_MLM_TSPEC_REQ); + CASE_RETURN_STRING(LIM_MLM_TSPEC_CNF); + CASE_RETURN_STRING(LIM_MLM_SETKEYS_CNF); + CASE_RETURN_STRING(LIM_MLM_LINK_TEST_STOP_REQ); + CASE_RETURN_STRING(LIM_MLM_PURGE_STA_IND); + default: + return (uint8_t *) "UNKNOWN"; + break; + } +} + +/** + * mac_trace_get_info_log_string() - Get the log info + * @info_log: message type in numeric form + * + * This function will return a string equivalent of the message. + * + * Return: String equivalent of the message type. + **/ +uint8_t *mac_trace_get_info_log_string(uint16_t info_log) +{ + switch (info_log) { + CASE_RETURN_STRING(eLOG_NODROP_MISSED_BEACON_SCENARIO); + CASE_RETURN_STRING(eLOG_PROC_DEAUTH_FRAME_SCENARIO); + default: + return (uint8_t *) "UNKNOWN"; + break; + } +} + +#endif diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/parse_mac_trace.cmm b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/parse_mac_trace.cmm new file mode 100644 index 0000000000000000000000000000000000000000..be649ea99d1e5cbf6440b8d7db8bec378a21a2c5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/parse_mac_trace.cmm @@ -0,0 +1,1000 @@ +;Copyright (c) 2013-2017 The Linux Foundation. All rights reserved. + +;Permission to use, copy, modify, and/or distribute this software for +;any purpose with or without fee is hereby granted, provided that the +;above copyright notice and this permission notice appear in all +;copies. + +;THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +;WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +;WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +;AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +;DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +;PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +;TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +;PERFORMANCE OF THIS SOFTWARE. + +;parsemactrace.cmm - This script parses MAC trace table in UMAC layer +;This script relies on message id's placed in interface header file. +;If some message ID's are changed later, since they do not use enum, this script +;might show incorrect data. So message ID's should always be in sync +;Author: +;Date: 09/09/2013 +;History:- +;Date Modified by Modification Information +;-------------------------------------------------------------------- + + +ENTRY &FILE + +IF "&FILE"=="" +( +DIALOG.file *.txt +ENTRY &FILE +) + +OPEN #1 "&FILE" /Create /Write /Append + + +Var.NEW char [406][50] \halmsgtype + +Var.SET \halmsgtype[0x20]="SIR_HAL_RADAR_DETECTED_IND" +Var.SET \halmsgtype[0x21]="SIR_HAL_ADD_STA_REQ" +Var.SET \halmsgtype[0x22]="SIR_HAL_ADD_STA_RSP" +Var.SET \halmsgtype[0x23]="SIR_HAL_DELETE_STA_REQ" +Var.SET \halmsgtype[0x24]="SIR_HAL_DELETE_STA_RSP" +Var.SET \halmsgtype[0x25]="SIR_HAL_ADD_BSS_REQ" +Var.SET \halmsgtype[0x26]="SIR_HAL_ADD_BSS_RSP" +Var.SET \halmsgtype[0x27]="SIR_HAL_DELETE_BSS_REQ" +Var.SET \halmsgtype[0x28]="SIR_HAL_DELETE_BSS_RSP" +Var.SET \halmsgtype[0x29]="SIR_HAL_INIT_SCAN_REQ" +Var.SET \halmsgtype[0x2A]="SIR_HAL_INIT_SCAN_RSP" +Var.SET \halmsgtype[0x2B]="SIR_HAL_START_SCAN_REQ" +Var.SET \halmsgtype[0x2C]="SIR_HAL_START_SCAN_RSP" +Var.SET \halmsgtype[0x2D]="SIR_HAL_END_SCAN_REQ" +Var.SET \halmsgtype[0x2E]="SIR_HAL_END_SCAN_RSP" +Var.SET \halmsgtype[0x2F]="SIR_HAL_FINISH_SCAN_REQ" +Var.SET \halmsgtype[0x30]="SIR_HAL_FINISH_SCAN_RSP" +Var.SET \halmsgtype[0x31]="SIR_HAL_SEND_BEACON_REQ" +Var.SET \halmsgtype[0x32]="SIR_HAL_SET_BSSKEY_REQ" +Var.SET \halmsgtype[0x33]="SIR_HAL_SET_BSSKEY_RSP" +Var.SET \halmsgtype[0x34]="SIR_HAL_SET_STAKEY_REQ" +Var.SET \halmsgtype[0x35]="SIR_HAL_SET_STAKEY_RSP" +Var.SET \halmsgtype[0x36]="SIR_HAL_UPDATE_EDCA_PROFILE_IND" +Var.SET \halmsgtype[0x37]="SIR_HAL_UPDATE_BEACON_IND" +Var.SET \halmsgtype[0x38]="SIR_HAL_UPDATE_CF_IND" +Var.SET \halmsgtype[0x39]="SIR_HAL_CHNL_SWITCH_REQ" +Var.SET \halmsgtype[0x3A]="SIR_HAL_ADD_TS_REQ" +Var.SET \halmsgtype[0x3B]="SIR_HAL_DEL_TS_REQ" +;28 to 33 macros unused +Var.SET \halmsgtype[0x42]="SIR_HAL_MISSED_BEACON_IND" +Var.SET \halmsgtype[0x43]="SIR_HAL_SWITCH_CHANNEL_RSP" +Var.SET \halmsgtype[0x44]="SIR_HAL_PWR_SAVE_CFG" +Var.SET \halmsgtype[0x45]="SIR_HAL_REGISTER_PE_CALLBACK" +;38 to 42 unused +Var.SET \halmsgtype[0x4A]="SIR_HAL_IBSS_STA_ADD" +Var.SET \halmsgtype[0x4B]="SIR_HAL_TIMER_ADJUST_ADAPTIVE_THRESHOLD_IND" +Var.SET \halmsgtype[0x4C]="SIR_HAL_SET_LINK_STATE" +Var.SET \halmsgtype[0x4D]="SIR_HAL_DELETE_BSS_HO_FAIL_REQ" +Var.SET \halmsgtype[0x4E]="SIR_HAL_DELETE_BSS_HO_FAIL_RSP" +;48 to 57 unused +Var.SET \halmsgtype[0x59]="SIR_HAL_SET_STA_BCASTKEY_REQ" +Var.SET \halmsgtype[0x5A]="SIR_HAL_SET_STA_BCASTKEY_RSP" +Var.SET \halmsgtype[0x5B]="SIR_HAL_ADD_TS_RSP" +Var.SET \halmsgtype[0x5C]="SIR_HAL_DPU_MIC_ERROR" +;62 unused +Var.SET \halmsgtype[0x5E]="SIR_HAL_TIMER_CHIP_MONITOR_TIMEOUT" +Var.SET \halmsgtype[0x5F]="SIR_HAL_TIMER_TRAFFIC_ACTIVITY_REQ" +Var.SET \halmsgtype[0x60]="SIR_HAL_TIMER_ADC_RSSI_STATS" +;66 unused +Var.SET \halmsgtype[0x62]="SIR_HAL_SET_MIMOPS_REQ" +Var.SET \halmsgtype[0x63]="SIR_HAL_SET_MIMOPS_RSP" +Var.SET \halmsgtype[0x64]="SIR_HAL_SYS_READY_IND" +Var.SET \halmsgtype[0x65]="SIR_HAL_SET_TX_POWER_REQ" +Var.SET \halmsgtype[0x66]="SIR_HAL_SET_TX_POWER_RSP" +Var.SET \halmsgtype[0x67]="SIR_HAL_GET_TX_POWER_REQ" +Var.SET \halmsgtype[0x68]="SIR_HAL_GET_TX_POWER_RSP" +Var.SET \halmsgtype[0x69]="SIR_HAL_GET_NOISE_RSP" +Var.SET \halmsgtype[0x6A]="SIR_HAL_TRANSMISSION_CONTROL_IND" +;76 to 79 unused +Var.SET \halmsgtype[0x6F]="SIR_HAL_LOW_RSSI_IND" +Var.SET \halmsgtype[0x70]="SIR_HAL_BEACON_FILTER_IND" +Var.SET \halmsgtype[0x71]="SIR_HAL_WOW_ADD_PTRN" +Var.SET \halmsgtype[0x72]="SIR_HAL_WOW_DEL_PTRN" +Var.SET \halmsgtype[0x73]="SIR_HAL_WOWL_ENTER_REQ" +Var.SET \halmsgtype[0x74]="SIR_HAL_WOWL_ENTER_RSP" +Var.SET \halmsgtype[0x75]="SIR_HAL_WOWL_EXIT_REQ" +Var.SET \halmsgtype[0x76]="SIR_HAL_WOWL_EXIT_RSP" +Var.SET \halmsgtype[0x77]="SIR_HAL_GET_STATISTICS_REQ" +Var.SET \halmsgtype[0x78]="SIR_HAL_GET_STATISTICS_RSP" +Var.SET \halmsgtype[0x79]="SIR_HAL_SET_KEY_DONE" +Var.SET \halmsgtype[0x7A]="SIR_HAL_BTC_SET_CFG" +;92 unused +Var.SET \halmsgtype[0x7C]="SIR_HAL_HANDLE_FW_MBOX_RSP" +Var.SET \halmsgtype[0x7D]="SIR_HAL_SEND_PROBE_RSP_TMPL" +Var.SET \halmsgtype[0x7E]="SIR_LIM_ADDR2_MISS_IND" +Var.SET \halmsgtype[0x7F]="SIR_HAL_START_OEM_DATA_REQ" +Var.SET \halmsgtype[0x80]="SIR_HAL_START_OEM_DATA_RSP" +Var.SET \halmsgtype[0x81]="SIR_HAL_SET_MAX_TX_POWER_REQ" +Var.SET \halmsgtype[0x82]="SIR_HAL_SET_MAX_TX_POWER_RSP" +Var.SET \halmsgtype[0x83]="SIR_HAL_SET_HOST_OFFLOAD" +Var.SET \halmsgtype[0x84]="SIR_HAL_ADD_STA_SELF_REQ" +Var.SET \halmsgtype[0x85]="SIR_HAL_ADD_STA_SELF_RSP" +Var.SET \halmsgtype[0x86]="SIR_HAL_DEL_STA_SELF_REQ" +Var.SET \halmsgtype[0x87]="SIR_HAL_DEL_STA_SELF_RSP" +;105 unused +Var.SET \halmsgtype[0x88]="SIR_HAL_CFG_RXP_FILTER_REQ" +Var.SET \halmsgtype[0x89]="SIR_HAL_AGGR_ADD_TS_REQ" +Var.SET \halmsgtype[0x8A]="SIR_HAL_AGGR_ADD_TS_RSP" +Var.SET \halmsgtype[0x8B]="SIR_HAL_AGGR_QOS_REQ" +Var.SET \halmsgtype[0x8C]="SIR_HAL_AGGR_QOS_RSP" +Var.SET \halmsgtype[0x8D]="SIR_HAL_SET_P2P_GO_NOA_REQ" +Var.SET \halmsgtype[0x8E]="SIR_HAL_P2P_NOA_ATTR_IND" +Var.SET \halmsgtype[0x8F]="SIR_HAL_P2P_NOA_START_IND" +Var.SET \halmsgtype[0x90]="SIR_HAL_SET_LINK_STATE_RSP" +Var.SET \halmsgtype[0x91]="SIR_HAL_WLAN_SUSPEND_IND" +Var.SET \halmsgtype[0x92]="SIR_HAL_WLAN_RESUME_REQ" +Var.SET \halmsgtype[0x93]="SIR_HAL_SET_KEEP_ALIVE" +Var.SET \halmsgtype[0x94]="SIR_HAL_SET_NS_OFFLOAD" +Var.SET \halmsgtype[0x95]="SIR_HAL_SET_PNO_REQ" +Var.SET \halmsgtype[0x96]="SIR_HAL_SOC_ANTENNA_MODE_REQ" +Var.SET \halmsgtype[0x97]="SIR_HAL_SOC_ANTENNA_MODE_RESP" +;122 unused +Var.SET \halmsgtype[0x98]="SIR_HAL_8023_MULTICAST_LIST_REQ" +Var.SET \halmsgtype[0x99]="SIR_HAL_RECEIVE_FILTER_SET_FILTER_REQ" +Var.SET \halmsgtype[0x9A]="SIR_HAL_PACKET_COALESCING_FILTER_MATCH_COUNT_REQ" +Var.SET \halmsgtype[0x9B]="SIR_HAL_PACKET_COALESCING_FILTER_MATCH_COUNT_RSP" +Var.SET \halmsgtype[0x9C]="SIR_HAL_RECEIVE_FILTER_CLEAR_FILTER_REQ" +;128 unused +Var.SET \halmsgtype[0x9F]="SIR_HAL_GTK_OFFLOAD_REQ" +Var.SET \halmsgtype[0xA0]="SIR_HAL_GTK_OFFLOAD_GETINFO_REQ" +Var.SET \halmsgtype[0xA1]="SIR_HAL_GTK_OFFLOAD_GETINFO_RSP" +Var.SET \halmsgtype[0xA2]="SIR_HAL_TSM_STATS_REQ" +Var.SET \halmsgtype[0xA3]="SIR_HAL_TSM_STATS_RSP" +Var.SET \halmsgtype[0xA4]="SIR_HAL_SET_TM_LEVEL_REQ" +Var.SET \halmsgtype[0xA5]="SIR_HAL_UPDATE_OP_MODE" +Var.SET \halmsgtype[0xA6]="SIR_HAL_TDLS_LINK_ESTABLISH" +Var.SET \halmsgtype[0xA7]="SIR_HAL_TDLS_LINK_TEARDOWN" +Var.SET \halmsgtype[0xA8]="SIR_HAL_ROAM_SCAN_OFFLOAD_REQ" +;139 and 140 unused +Var.SET \halmsgtype[0xAB]="SIR_HAL_TRAFFIC_STATS_IND" +Var.SET \halmsgtype[0xAC]="SIR_HAL_EXCLUDE_UNENCRYPTED_IND" +Var.SET \halmsgtype[0xAD]="SIR_HAL_TDLS_LINK_ESTABLISH_REQ" +Var.SET \halmsgtype[0xAE]="SIR_HAL_TDLS_LINK_ESTABLISH_REQ_RSP" +Var.SET \halmsgtype[0xAF]="SIR_HAL_TDLS_IND" +Var.SET \halmsgtype[0xB0]="SIR_HAL_STOP_SCAN_OFFLOAD_REQ" +Var.SET \halmsgtype[0xB1]="SIR_HAL_RX_SCAN_EVENT" +Var.SET \halmsgtype[0xB2]="SIR_HAL_DHCP_START_IND" +Var.SET \halmsgtype[0xB3]="SIR_HAL_DHCP_STOP_IND" +Var.SET \halmsgtype[0xB4]="SIR_HAL_IBSS_PEER_INACTIVITY_IND" +Var.SET \halmsgtype[0xB5]="SIR_HAL_LPHB_CONF_IND" +Var.SET \halmsgtype[0xB6]="SIR_HAL_ADD_PERIODIC_TX_PTRN_IND" +Var.SET \halmsgtype[0xB7]="SIR_HAL_DEL_PERIODIC_TX_PTRN_IND" +Var.SET \halmsgtype[0xB8]="SIR_HAL_PDEV_DUAL_MAC_CFG_REQ" +Var.SET \halmsgtype[0xB9]="SIR_HAL_PDEV_MAC_CFG_RESP" +;156 and 157 unused +Var.SET \halmsgtype[0xBC]="SIR_HAL_IBSS_PEER_INFO_REQ" +Var.SET \halmsgtype[0xBD]="SIR_HAL_RATE_UPDATE_IND" +Var.SET \halmsgtype[0xBE]="SIR_HAL_FLUSH_LOG_TO_FW" +Var.SET \halmsgtype[0xBF]="SIR_HAL_PDEV_SET_PCL_TO_FW" +;162 unused +Var.SET \halmsgtype[0xC1]="SIR_HAL_CLI_SET_CMD" +Var.SET \halmsgtype[0xC2]="SIR_HAL_PKTLOG_ENABLE_REQ" +Var.SET \halmsgtype[0xC3]="SIR_HAL_SME_SCAN_CACHE_UPDATED" +Var.SET \halmsgtype[0xC4]="SIR_HAL_START_SCAN_OFFLOAD_REQ" +Var.SET \halmsgtype[0xC5]="SIR_HAL_UPDATE_CHAN_LIST_REQ" +;168 unused +Var.SET \halmsgtype[0xC7]="SIR_CSA_OFFLOAD_EVENT" +Var.SET \halmsgtype[0xC8]="SIR_HAL_SET_MAX_TX_POWER_PER_BAND_REQ" +Var.SET \halmsgtype[0xC9]="SIR_HAL_TX_FAIL_MONITOR_IND" +Var.SET \halmsgtype[0xCA]="SIR_HAL_UPDATE_MEMBERSHIP" +Var.SET \halmsgtype[0xCB]="SIR_HAL_UPDATE_USERPOS" +Var.SET \halmsgtype[0xCC]="SIR_HAL_UPDATE_FW_TDLS_STATE" +Var.SET \halmsgtype[0xCD]="SIR_HAL_UPDATE_TDLS_PEER_STATE" +Var.SET \halmsgtype[0xCE]="SIR_HAL_TDLS_SHOULD_DISCOVER" +Var.SET \halmsgtype[0xCF]="SIR_HAL_TDLS_SHOULD_TEARDOWN" +Var.SET \halmsgtype[0xD0]="SIR_HAL_TDLS_PEER_DISCONNECTED" +Var.SET \halmsgtype[0xD1]="SIR_HAL_BEACON_TX_SUCCESS_IND" +Var.SET \halmsgtype[0xD2]="SIR_HAL_DFS_RADAR_IND" +Var.SET \halmsgtype[0xD3]="SIR_HAL_IBSS_CESIUM_ENABLE_IND" +Var.SET \halmsgtype[0xD4]="SIR_HAL_RMC_ENABLE_IND" +Var.SET \halmsgtype[0xD5]="SIR_HAL_RMC_DISABLE_IND" +Var.SET \halmsgtype[0xD6]="SIR_HAL_RMC_ACTION_PERIOD_IND" +Var.SET \halmsgtype[0xD7]="SIR_HAL_INIT_THERMAL_INFO_CMD" +Var.SET \halmsgtype[0xD8]="SIR_HAL_SET_THERMAL_LEVEL" +Var.SET \halmsgtype[0xD9]="SIR_HAL_SET_PLM_REQ" +Var.SET \halmsgtype[0xDA]="SIR_HAL_SET_TX_POWER_LIMIT" +Var.SET \halmsgtype[0xDB]="SIR_HAL_SET_SAP_INTRABSS_DIS" +Var.SET \halmsgtype[0xDC]="SIR_HAL_MODEM_POWER_STATE_IND" +Var.SET \halmsgtype[0xDD]="SIR_HAL_DISASSOC_TX_COMP" +Var.SET \halmsgtype[0xDE]="SIR_HAL_DEAUTH_TX_COMP" +Var.SET \halmsgtype[0xDF]="SIR_HAL_UPDATE_RX_NSS" +Var.SET \halmsgtype[0xE0]="SIR_HAL_STATS_EXT_REQUEST" +Var.SET \halmsgtype[0xE1]="SIR_HAL_STATS_EXT_EVENT" +Var.SET \halmsgtype[0xE2]="SIR_HAL_HIDE_SSID_VDEV_RESTART" +Var.SET \halmsgtype[0xE3]="SIR_HAL_GET_LINK_SPEED" +Var.SET \halmsgtype[0xE4]="SIR_HAL_EXTSCAN_GET_CAPABILITIES_REQ" +Var.SET \halmsgtype[0xE5]="SIR_HAL_EXTSCAN_START_REQ" +Var.SET \halmsgtype[0xE6]="SIR_HAL_EXTSCAN_STOP_REQ" +Var.SET \halmsgtype[0xE7]="SIR_HAL_EXTSCAN_SET_BSS_HOTLIST_REQ" +Var.SET \halmsgtype[0xE8]="SIR_HAL_EXTSCAN_RESET_BSS_HOTLIST_REQ" +Var.SET \halmsgtype[0xE9]="SIR_HAL_EXTSCAN_SET_SIGNF_CHANGE_REQ" +Var.SET \halmsgtype[0xEA]="SIR_HAL_EXTSCAN_RESET_SIGNF_CHANGE_REQ" +Var.SET \halmsgtype[0xEB]="SIR_HAL_EXTSCAN_GET_CACHED_RESULTS_REQ" +Var.SET \halmsgtype[0xEC]="SIR_HAL_CH_AVOID_UPDATE_REQ" +Var.SET \halmsgtype[0xED]="SIR_HAL_LL_STATS_CLEAR_REQ" +Var.SET \halmsgtype[0xEE]="SIR_HAL_LL_STATS_SET_REQ" +Var.SET \halmsgtype[0xEF]="SIR_HAL_LL_STATS_GET_REQ" +Var.SET \halmsgtype[0xF0]="SIR_HAL_LL_STATS_RESULTS_RSP" +Var.SET \halmsgtype[0xF1]="SIR_HAL_ROAM_OFFLOAD_SYNCH_CNF" +Var.SET \halmsgtype[0xF2]="SIR_HAL_NAN_REQUEST" +Var.SET \halmsgtype[0xF3]="SIR_HAL_SET_AUTO_SHUTDOWN_TIMER_REQ" +Var.SET \halmsgtype[0xF4]="SIR_HAL_SET_BASE_MACADDR_IND" +Var.SET \halmsgtype[0xF5]="SIR_HAL_SET_BASE_MACADDR_IND" +Var.SET \halmsgtype[0xF6]="SIR_HAL_LINK_STATUS_GET_REQ" +Var.SET \halmsgtype[0xF7]="SIR_HAL_CONFIG_EXT_WOW" +Var.SET \halmsgtype[0xF8]="SIR_HAL_CONFIG_APP_TYPE1_PARAMS" +Var.SET \halmsgtype[0xF9]="SIR_HAL_CONFIG_APP_TYPE2_PARAMS" +Var.SET \halmsgtype[0xFA]="SIR_HAL_GET_TEMPERATURE_REQ" +Var.SET \halmsgtype[0xFB]="SIR_HAL_SET_SCAN_MAC_OUI_REQ" +Var.SET \halmsgtype[0xFC]="SIR_HAL_SET_DHCP_SERVER_OFFLOAD" +Var.SET \halmsgtype[0xFD]="SIR_HAL_LED_FLASHING_REQ" +Var.SET \halmsgtype[0xFE]="SIR_HAL_LED_FLASHING_REQ" +Var.SET \halmsgtype[0xFF]="SIR_HAL_ROAM_OFFLOAD_SYNCH_IND" +Var.SET \halmsgtype[0x100]="SIR_HAL_ROAM_OFFLOAD_SYNCH_FAIL" +Var.SET \halmsgtype[0x101]="SIR_HAL_ROAM_INVOKE" +Var.SET \halmsgtype[0x102]="SIR_HAL_TDLS_SET_OFFCHAN_MODE" +Var.SET \halmsgtype[0x103]="SIR_HAL_TDLS_SET_OFFCHAN_MODE" +Var.SET \halmsgtype[0x104]="SIR_HAL_SET_MIRACAST" +Var.SET \halmsgtype[0x105]="SIR_HAL_UPDATE_Q2Q_IE_IND" +Var.SET \halmsgtype[0x106]="SIR_HAL_UPDATE_Q2Q_IE_IND" +Var.SET \halmsgtype[0x107]="SIR_HAL_CONFIG_GUARD_TIME" +Var.SET \halmsgtype[0x108]="SIR_HAL_IPA_OFFLOAD_ENABLE_DISABLE" +Var.SET \halmsgtype[0x109]="SIR_HAL_ENTER_PS_REQ" +Var.SET \halmsgtype[0x10A]="SIR_HAL_EXIT_PS_REQ" +Var.SET \halmsgtype[0x10B]="SIR_HAL_ENABLE_UAPSD_REQ" +Var.SET \halmsgtype[0x10C]="SIR_HAL_DISABLE_UAPSD_REQ" +Var.SET \halmsgtype[0x10D]="SIR_HAL_GATEWAY_PARAM_UPDATE_REQ" +;240 to 307 unused +Var.SET \halmsgtype[0x152]="SIR_HAL_RUNTIME_PM_SUSPEND_IND" +Var.SET \halmsgtype[0x153]="SIR_HAL_RUNTIME_PM_RESUME_IND" +;310 to 312 unused +Var.SET \halmsgtype[0x157]="SIR_HAL_SET_EPNO_LIST_REQ" +;314 and 315 unused +Var.SET \halmsgtype[0x15A]="SIR_HAL_SET_PASSPOINT_LIST_REQ" +Var.SET \halmsgtype[0x15B]="SIR_HAL_RESET_PASSPOINT_LIST_REQ" +;318 unused +Var.SET \halmsgtype[0x15D]="SIR_HAL_OCB_SET_CONFIG_CMD" +Var.SET \halmsgtype[0x15E]="SIR_HAL_OCB_SET_UTC_TIME_CMD" +Var.SET \halmsgtype[0x15F]="SIR_HAL_OCB_START_TIMING_ADVERT_CMD" +Var.SET \halmsgtype[0x160]="SIR_HAL_OCB_STOP_TIMING_ADVERT_CMD" +Var.SET \halmsgtype[0x161]="SIR_HAL_OCB_GET_TSF_TIMER_CMD" +Var.SET \halmsgtype[0x162]="SIR_HAL_DCC_GET_STATS_CMD" +Var.SET \halmsgtype[0x163]="SIR_HAL_DCC_CLEAR_STATS_CMD" +Var.SET \halmsgtype[0x164]="SIR_HAL_DCC_UPDATE_NDL_CMD" +Var.SET \halmsgtype[0x165]="SIR_HAL_FW_MEM_DUMP_REQ" +Var.SET \halmsgtype[0x166]="SIR_HAL_START_STOP_LOGGING" +Var.SET \halmsgtype[0x167]="SIR_HAL_PDEV_SET_HW_MODE" +Var.SET \halmsgtype[0x168]="SIR_HAL_PDEV_SET_HW_MODE_RESP" +Var.SET \halmsgtype[0x169]="SIR_HAL_PDEV_HW_MODE_TRANS_IND" +Var.SET \halmsgtype[0x169]="SIR_HAL_BAD_PEER_TX_CTL_INI_CMD" +Var.SET \halmsgtype[0x16A]="SIR_HAL_SET_RSSI_MONITOR_REQ" +Var.SET \halmsgtype[0x16B]="SIR_HAL_SET_IE_INFO" +Var.SET \halmsgtype[0x16C]="SIR_HAL_LRO_CONFIG_CMD" +Var.SET \halmsgtype[0x16D]="SIR_HAL_SET_EGAP_CONF_PARAMS" +Var.SET \halmsgtype[0x16E]="SIR_HAL_HT40_OBSS_SCAN_IND" +Var.SET \halmsgtype[0x16F]="SIR_HAL_TSF_GPIO_PIN_REQ" +Var.SET \halmsgtype[0x170]="SIR_HAL_ADD_BCN_FILTER_CMDID" +Var.SET \halmsgtype[0x171]="SIR_HAL_REMOVE_BCN_FILTER_CMDID" +Var.SET \halmsgtype[0x172]="SIR_HAL_BPF_GET_CAPABILITIES_REQ" +Var.SET \halmsgtype[0x173]="SIR_HAL_BPF_SET_INSTRUCTIONS_REQ" +Var.SET \halmsgtype[0x174]="SIR_HAL_SET_WISA_PARAMS" +Var.SET \halmsgtype[0x175]="SIR_HAL_SET_ADAPT_DWELLTIME_PARAMS" +Var.SET \halmsgtype[0x176]="SIR_HAL_SET_PDEV_IE_REQ" +Var.SET \halmsgtype[0x177]="SIR_HAL_TDLS_CONNECTION_TRACKER_NOTIFICATION" +Var.SET \halmsgtype[0x178]="SIR_HAL_NDP_GET_CAP_REQ" +Var.SET \halmsgtype[0x179]="SIR_HAL_NDP_INITIATOR_REQ" +Var.SET \halmsgtype[0x17A]="SIR_HAL_NDP_RESPONDER_REQ" +Var.SET \halmsgtype[0x17B]="SIR_HAL_NDP_END_REQ" +Var.SET \halmsgtype[0x17C]="SIR_HAL_NDI_CAP_RSP" +Var.SET \halmsgtype[0x17D]="SIR_HAL_IBSS_PEER_INFO_RSP" +Var.SET \halmsgtype[0x17E]="SIR_HAL_RATE_UPDATE_IND" +Var.SET \halmsgtype[0x17F]="SIR_HAL_NDP_INITIATOR_RSP" +Var.SET \halmsgtype[0x180]="SIR_HAL_NDP_RESPONDER_RSP" +Var.SET \halmsgtype[0x181]="SIR_HAL_NDP_END_RSP" +Var.SET \halmsgtype[0x182]="SIR_HAL_NDP_INDICATION" +Var.SET \halmsgtype[0x183]="SIR_HAL_NDP_CONFIRM" +Var.SET \halmsgtype[0x184]="SIR_HAL_NDP_END_IND" +Var.SET \halmsgtype[0x185]="SIR_HAL_UPDATE_WEP_DEFAULT_KEY" +;359 unused +Var.SET \halmsgtype[0x187]="SIR_HAL_SEND_FREQ_RANGE_CONTROL_IND" +;361 unused +Var.SET \halmsgtype[0x189]="SIR_HAL_POWER_DBG_CMD" +Var.SET \halmsgtype[0x18A]="SIR_HAL_SET_DTIM_PERIOD" +Var.SET \halmsgtype[0x18B]="SIR_HAL_ENCRYPT_DECRYPT_MSG" +Var.SET \halmsgtype[0x18C]="SIR_HAL_SHORT_RETRY_LIMIT_CNT" +Var.SET \halmsgtype[0x18D]="SIR_HAL_LONG_RETRY_LIMIT_CNT" +Var.SET \halmsgtype[0x18E]="SIR_HAL_UPDATE_TX_FAIL_CNT_TH" +Var.SET \halmsgtype[0x18F]="SIR_HAL_POWER_DEBUG_STATS_REQ" +Var.SET \halmsgtype[0x190]="SIR_HAL_SET_WOW_PULSE_CMD" +Var.SET \halmsgtype[0x191]="SIR_HAL_SET_UDP_RESP_OFFLOAD" +Var.SET \halmsgtype[0x192]="SIR_HAL_SET_PER_ROAM_CONFIG_CMD" +Var.SET \halmsgtype[0x193]="SIR_HAL_GET_RCPI_REQ" +Var.SET \halmsgtype[0x194]="SIR_HAL_ENABLE_BCAST_FILTER" +Var.SET \halmsgtype[0x195]="SIR_HAL_DISABLE_BCAST_FILTER" + + +Var.NEW char [256][100] \smecodetype + +Var.SET \smecodetype[0x00]="TRACE_CODE_SME_RX_HDD_MSG_SCAN_REQ" +Var.SET \smecodetype[0x01]="TRACE_CODE_SME_RX_HDD_MSG_SCAN_GET_RESULTS" +Var.SET \smecodetype[0x02]="TRACE_CODE_SME_RX_HDD_MSG_CONNECT" +Var.SET \smecodetype[0x03]="TRACE_CODE_SME_RX_HDD_MSG_SET_11DINFO" +Var.SET \smecodetype[0x04]="TRACE_CODE_SME_RX_HDD_MSG_GET_SOFTAP_DOMAIN" +Var.SET \smecodetype[0x05]="TRACE_CODE_SME_RX_HDD_MSG_SET_REGINFO" +Var.SET \smecodetype[0x06]="TRACE_CODE_SME_RX_HDD_MSG_UPDATE_CHANNEL_CONFIG" +Var.SET \smecodetype[0x07]="TRACE_CODE_SME_RX_HDD_MSG_UPDATE_CONFIG" +Var.SET \smecodetype[0x08]="TRACE_CODE_SME_RX_HDD_MSG_HDDREADYIND" +Var.SET \smecodetype[0x09]="TRACE_CODE_SME_RX_HDD_MSG_SCAN_FLUSH_RESULTS" +Var.SET \smecodetype[0x0A]="TRACE_CODE_SME_RX_HDD_MSG_SCAN_FLUSH_P2PRESULTS" +Var.SET \smecodetype[0x0B]="TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_GETFIRST" +Var.SET \smecodetype[0x0C]="TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_GETNEXT" +Var.SET \smecodetype[0x0D]="TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_PURGE" +Var.SET \smecodetype[0x0E]="TRACE_CODE_SME_RX_HDD_ROAM_REASSOC" +Var.SET \smecodetype[0x0F]="TRACE_CODE_SME_RX_HDD_ROAM_DISCONNECT" +Var.SET \smecodetype[0x10]="TRACE_CODE_SME_RX_HDD_ROAM_GET_CONNECTPROFILE" +Var.SET \smecodetype[0x11]="TRACE_CODE_SME_RX_HDD_ROAM_FREE_CONNECTPROFILE" +Var.SET \smecodetype[0x12]="TRACE_CODE_SME_RX_HDD_ROAM_SET_PMKIDCACHE" +Var.SET \smecodetype[0x12]="TRACE_CODE_SME_RX_HDD_ROAM_GET_PMKIDCACHE" +Var.SET \smecodetype[0x13]="TRACE_CODE_SME_RX_HDD_GET_CONFIGPARAM" +Var.SET \smecodetype[0x14]="TRACE_CODE_SME_RX_HDD_GET_MODPROFFIELDS" +Var.SET \smecodetype[0x15]="TRACE_CODE_SME_RX_HDD_SET_CONFIG_PWRSAVE" +Var.SET \smecodetype[0x16]="TRACE_CODE_SME_RX_HDD_GET_CONFIG_PWRSAVE" +Var.SET \smecodetype[0x17]="TRACE_CODE_SME_RX_HDD_ENABLE_PWRSAVE" +Var.SET \smecodetype[0x18]="TRACE_CODE_SME_RX_HDD_DISABLE_PWRSAVE" +Var.SET \smecodetype[0x19]="TRACE_CODE_SME_RX_HDD_START_AUTO_BMPSTIMER" +Var.SET \smecodetype[0x1A]="TRACE_CODE_SME_RX_HDD_STOP_AUTO_BMPSTIMER" +Var.SET \smecodetype[0x1B]="TRACE_CODE_SME_RX_HDD_IS_PWRSAVE_ENABLED" +Var.SET \smecodetype[0x1C]="TRACE_CODE_SME_RX_HDD_REQUEST_FULLPOWER" +Var.SET \smecodetype[0x1D]="TRACE_CODE_SME_RX_HDD_REQUEST_BMPS" +Var.SET \smecodetype[0x1E]="TRACE_CODE_SME_RX_HDD_SET_DHCP_FLAG" +Var.SET \smecodetype[0x1F]="TRACE_CODE_SME_RX_HDD_REQUEST_STANDBY" +Var.SET \smecodetype[0x20]="TRACE_CODE_SME_RX_HDD_WOWL_ADDBCAST_PATTERN" +Var.SET \smecodetype[0x21]="TRACE_CODE_SME_RX_HDD_WOWL_DELBCAST_PATTERN" +Var.SET \smecodetype[0x22]="TRACE_CODE_SME_RX_HDD_ENTER_WOWL" +Var.SET \smecodetype[0x23]="TRACE_CODE_SME_RX_HDD_EXIT_WOWL" +Var.SET \smecodetype[0x24]="TRACE_CODE_SME_RX_HDD_SET_KEY" +Var.SET \smecodetype[0x25]="TRACE_CODE_SME_RX_HDD_REMOVE_KEY" +Var.SET \smecodetype[0x26]="TRACE_CODE_SME_RX_HDD_GET_STATS" +Var.SET \smecodetype[0x27]="TRACE_CODE_SME_RX_HDD_GET_RSSI" +Var.SET \smecodetype[0x28]="TRACE_CODE_SME_RX_HDD_GET_CNTRYCODE" +Var.SET \smecodetype[0x29]="TRACE_CODE_SME_RX_HDD_SET_CNTRYCODE" +Var.SET \smecodetype[0x2A]="TRACE_CODE_SME_RX_HDD_CHANGE_CNTRYCODE" +Var.SET \smecodetype[0x2B]="TRACE_CODE_SME_RX_HDD_SET_CFGPRIVACY" +Var.SET \smecodetype[0x2C]="TRACE_CODE_SME_RX_HDD_NEIGHBOR_REPORTREQ" +Var.SET \smecodetype[0x2D]="TRACE_CODE_SME_RX_HDD_DBG_READREG" +Var.SET \smecodetype[0x2E]="TRACE_CODE_SME_RX_HDD_DBG_WRITEREG" +Var.SET \smecodetype[0x2F]="TRACE_CODE_SME_RX_HDD_DBG_READMEM" +Var.SET \smecodetype[0x30]="TRACE_CODE_SME_RX_HDD_DBG_WRITEMEM" +Var.SET \smecodetype[0x31]="TRACE_CODE_SME_RX_HDD_OPEN_SESSION" +Var.SET \smecodetype[0x32]="TRACE_CODE_SME_RX_HDD_CLOSE_SESSION" +Var.SET \smecodetype[0x33]="TRACE_CODE_SME_RX_HDD_SET_HOSTOFFLOAD" +Var.SET \smecodetype[0x34]="TRACE_CODE_SME_RX_HDD_SET_GTKOFFLOAD" +Var.SET \smecodetype[0x35]="TRACE_CODE_SME_RX_HDD_GET_GTKOFFLOAD" +Var.SET \smecodetype[0x36]="TRACE_CODE_SME_RX_HDD_ABORT_MACSCAN" +Var.SET \smecodetype[0x37]="TRACE_CODE_SME_RX_HDD_REGISTER_MGMTFR" +Var.SET \smecodetype[0x38]="TRACE_CODE_SME_RX_HDD_DEREGISTER_MGMTFR" +Var.SET \smecodetype[0x39]="TRACE_CODE_SME_RX_HDD_REMAIN_ONCHAN" +Var.SET \smecodetype[0x3A]="TRACE_CODE_SME_RX_HDD_SEND_ACTION" +Var.SET \smecodetype[0x3B]="TRACE_CODE_SME_RX_HDD_CANCEL_REMAIN_ONCHAN" +Var.SET \smecodetype[0x3C]="TRACE_CODE_SME_RX_HDD_CONFIG_RXPFIL" +Var.SET \smecodetype[0x3D]="TRACE_CODE_SME_RX_HDD_CONFIG_SUSPENDIND" +Var.SET \smecodetype[0x3E]="TRACE_CODE_SME_RX_HDD_CONFIG_RESUMEREQ" +Var.SET \smecodetype[0x3F]="TRACE_CODE_SME_RX_HDD_CONFIG_EXTWOW" +Var.SET \smecodetype[0x40]="TRACE_CODE_SME_RX_HDD_CONFIG_APP_TYPE1" +Var.SET \smecodetype[0x41]="TRACE_CODE_SME_RX_HDD_CONFIG_APP_TYPE2" +Var.SET \smecodetype[0x42]="TRACE_CODE_SME_RX_HDD_SET_MAXTXPOW" +Var.SET \smecodetype[0x43]="TRACE_CODE_SME_RX_HDD_SET_TXPOW" +Var.SET \smecodetype[0x44]="TRACE_CODE_SME_RX_HDD_SET_TMLEVEL" +Var.SET \smecodetype[0x45]="TRACE_CODE_SME_RX_HDD_CAPS_EXCH" +Var.SET \smecodetype[0x46]="TRACE_CODE_SME_RX_HDD_DISABLE_CAP" +Var.SET \smecodetype[0x47]="TRACE_CODE_SME_RX_HDD_GET_DEFCCNV" +Var.SET \smecodetype[0x48]="TRACE_CODE_SME_RX_HDD_GET_CURCC" +Var.SET \smecodetype[0x49]="TRACE_CODE_SME_RX_HDD_RESET_PW5G" +Var.SET \smecodetype[0x4A]="TRACE_CODE_SME_RX_HDD_UPDATE_RP5G" +Var.SET \smecodetype[0x4B]="TRACE_CODE_SME_RX_HDD_SET_ROAMIBAND" +Var.SET \smecodetype[0x4C]="TRACE_CODE_SME_RX_HDD_GET_ROAMIBAND" +Var.SET \smecodetype[0x4D]="TRACE_CODE_SME_RX_HDD_UPDATE_RSSIDIFF" +Var.SET \smecodetype[0x4E]="TRACE_CODE_SME_RX_HDD_UPDATE_IMMRSSIDIFF" +Var.SET \smecodetype[0x4F]="TRACE_CODE_SME_RX_HDD_UPDATE_FTENABLED" +Var.SET \smecodetype[0x50]="TRACE_CODE_SME_RX_HDD_UPDATE_WESMODE" +Var.SET \smecodetype[0x51]="TRACE_CODE_SME_RX_HDD_SET_SCANCTRL" +Var.SET \smecodetype[0x52]="TRACE_CODE_SME_RX_HDD_UPDATE_P2P_IE" +Var.SET \smecodetype[0x53]="TRACE_CODE_SME_RX_HDD_UPDATE_ROAM_SCAN_N_PROBES" +Var.SET \smecodetype[0x54]="TRACE_CODE_SME_RX_HDD_UPDATE_ROAM_SCAN_HOME_AWAY_TIME" +Var.SET \smecodetype[0x55]="TRACE_CODE_SME_RX_HDD_STORE_JOIN_REQ" +Var.SET \smecodetype[0x56]="TRACE_CODE_SME_RX_HDD_CLEAR_JOIN_REQ" +Var.SET \smecodetype[0x57]="TRACE_CODE_SME_RX_HDD_ISSUE_JOIN_REQ" +Var.SET \smecodetype[0x58]="TRACE_CODE_SME_RX_HDD_MSG_DEAUTH_STA" +;#ifdef FEATURE_WLAN_TDLS //assuming this flag is enabled by default +Var.SET \smecodetype[0x59]="TRACE_CODE_SME_RX_HDD_TDLS_LINK_ESTABLISH_PARAM" +Var.SET \smecodetype[0x5A]="TRACE_CODE_SME_RX_HDD_TDLS_CHAN_SWITCH_REQ" +Var.SET \smecodetype[0x5B]="TRACE_CODE_SME_RX_HDD_TDLS_SEND_MGMT_FRAME" +Var.SET \smecodetype[0x5C]="TRACE_CODE_SME_RX_HDD_TDLS_CHANGE_PEER_STA" +Var.SET \smecodetype[0x5D]="TRACE_CODE_SME_RX_HDD_TDLS_ADD_PEER_STA" +Var.SET \smecodetype[0x5E]="TRACE_CODE_SME_RX_HDD_TDLS_DEL_PEER_STA" +;#endif +Var.SET \smecodetype[0x5F]="TRACE_CODE_SME_RX_HDD_PREF_NET_LIST" +;#ifdef FEATURE_WLAN_LPHB //assuming this flag is enabled by default +Var.SET \smecodetype[0x60]="TRACE_CODE_SME_RX_HDD_LPHB_CONFIG_REQ" +;#endif /* FEATURE_WLAN_LPHB */ +Var.SET \smecodetype[0x61]="TRACE_CODE_SME_RX_HDD_ROAM_DEL_PMKIDCACHE" +;From here hardcoded to 250 in host code +Var.SET \smecodetype[0xFA]="TRACE_CODE_SME_COMMAND" +Var.SET \smecodetype[0xFB]="TRACE_CODE_SME_TX_WMA_MSG" +Var.SET \smecodetype[0xFC]="TRACE_CODE_SME_RX_WMA_MSG" + +Var.NEW char [256][50] \cfgmsgtype + +Var.SET \cfgmsgtype[0xB0]="SIR_CFG_PARAM_UPDATE_IND" +Var.SET \cfgmsgtype[0xB1]="SIR_CFG_DOWNLOAD_COMPLETE_IND" + + +Var.NEW char [256][50] \limmsgtype + +Var.SET \limmsgtype[0xB3]="SIR_LIM_RETRY_INTERRUPT_MSG" +Var.SET \limmsgtype[0xB4]="SIR_BB_XPORT_MGMT_MSG" +;5 and 6 unused +Var.SET \limmsgtype[0xB7]="SIR_LIM_INV_KEY_INTERRUPT_MSG" +Var.SET \limmsgtype[0xB8]="SIR_LIM_KEY_ID_INTERRUPT_MSG" +Var.SET \limmsgtype[0xB9]="SIR_LIM_REPLAY_THRES_INTERRUPT_MSG" +Var.SET \limmsgtype[0xBA]="SIR_LIM_TD_DUMMY_CALLBACK_MSG" +Var.SET \limmsgtype[0xBB]="SIR_LIM_SCH_CLEAN_MSG" +Var.SET \limmsgtype[0xBC]="SIR_LIM_RADAR_DETECT_IND" +;0xD unused +Var.SET \limmsgtype[0xBE]="SIR_LIM_DEL_TS_IND" +;0xF and 0x10 unused +Var.SET \limmsgtype[0xC1]="SIR_LIM_DELETE_STA_CONTEXT_IND" +;0x12 unused +Var.SET \limmsgtype[0xC3]="SIR_LIM_UPDATE_BEACON" +;0 and 1 unused +Var.SET \limmsgtype[0xD2]="SIR_LIM_JOIN_FAIL_TIMEOUT" +Var.SET \limmsgtype[0xD3]="SIR_LIM_AUTH_FAIL_TIMEOUT" +Var.SET \limmsgtype[0xD4]="SIR_LIM_AUTH_RSP_TIMEOUT" +Var.SET \limmsgtype[0xD5]="SIR_LIM_ASSOC_FAIL_TIMEOUT" +Var.SET \limmsgtype[0xD6]="SIR_LIM_REASSOC_FAIL_TIMEOUT" +Var.SET \limmsgtype[0xD7]="SIR_LIM_HEART_BEAT_TIMEOUT" +;8, 9 and A unused +Var.SET \limmsgtype[0xDB]="SIR_LIM_PROBE_HB_FAILURE_TIMEOUT" +Var.SET \limmsgtype[0xDC]="SIR_LIM_ADDTS_RSP_TIMEOUT" +;0xD to 0x12 unused +Var.SET \limmsgtype[0xE3]="SIR_LIM_LINK_TEST_DURATION_TIMEOUT" +;0x14 to 0x16 unused +Var.SET \limmsgtype[0xE7]="SIR_LIM_CNF_WAIT_TIMEOUT" +;0x18 unused +Var.SET \limmsgtype[0xE9]="SIR_LIM_UPDATE_OLBC_CACHEL_TIMEOUT" +Var.SET \limmsgtype[0xEA]="SIR_LIM_CHANNEL_SWITCH_TIMEOUT" +Var.SET \limmsgtype[0xEB]="SIR_LIM_QUIET_TIMEOUT" +Var.SET \limmsgtype[0xEC]="SIR_LIM_QUIET_BSS_TIMEOUT" +Var.SET \limmsgtype[0xED]="SIR_LIM_WPS_OVERLAP_TIMEOUT" +Var.SET \limmsgtype[0xEE]="SIR_LIM_FT_PREAUTH_RSP_TIMEOUT" +Var.SET \limmsgtype[0xEF]="SIR_LIM_REMAIN_CHN_TIMEOUT" +Var.SET \limmsgtype[0xF0]="SIR_LIM_INSERT_SINGLESHOT_NOA_TIMEOUT" +;0x21 and 0x22 unused +Var.SET \limmsgtype[0xF3]="SIR_LIM_BEACON_GEN_IND" +Var.SET \limmsgtype[0xF4]="SIR_LIM_PERIODIC_PROBE_REQ_TIMEOUT" +;0x25 unused +Var.SET \limmsgtype[0xF6]="SIR_LIM_DISASSOC_ACK_TIMEOUT" +Var.SET \limmsgtype[0xF7]="SIR_LIM_DEAUTH_ACK_TIMEOUT" +Var.SET \limmsgtype[0xF8]="SIR_LIM_PERIODIC_JOIN_PROBE_REQ_TIMEOUT" +;0x29, 0x2A and 0x2B unused +Var.SET \limmsgtype[0xFC]="SIR_LIM_CONVERT_ACTIVE_CHANNEL_TO_PASSIVE" +Var.SET \limmsgtype[0xFD]="SIR_LIM_AUTH_RETRY_TIMEOUT" +Var.SET \limmsgtype[0xFF]="SIR_LIM_MSG_TYPES_END" + + +Var.NEW char [256][50] \schmsgtype + +Var.SET \schmsgtype[0x0]="SIR_SCH_CHANNEL_SWITCH_REQUEST" +Var.SET \schmsgtype[0x1]="SIR_SCH_START_SCAN_REQ" +Var.SET \schmsgtype[0x2]="SIR_SCH_START_SCAN_RSP" +Var.SET \schmsgtype[0x3]="SIR_SCH_END_SCAN_NTF" +Var.SET \schmsgtype[0xFF]="SIR_SCH_MSG_TYPES_END" + +Var.NEW char [256][50] \pmmmsgtype + +Var.SET \pmmmsgtype[0x0]="SIR_PMM_CHANGE_PM_MODE" +Var.SET \pmmmsgtype[0xFF]="SIR_PMM_MSG_TYPES_END" + + +Var.NEW char [256][50] \mntmsgtype + +Var.SET \mntmsgtype[0x0]="SIR_MNT_RELEASE_BD" +Var.SET \mntmsgtype[0xFF]="SIR_MNT_MSG_TYPES_END" + +Var.NEW char [0x301][50] \pttmsgtype + +Var.SET \pttmsgtype[0x0]="SIR_PTT_MSG_TYPES_BEGIN" +Var.SET \pttmsgtype[0x300]="SIR_PTT_MSG_TYPES_END" + + +Var.NEW char [18][50] \code + +Var.SET \code[0]="TRACE_CODE_MLM_STATE" +Var.SET \code[1]="TRACE_CODE_SME_STATE" +Var.SET \code[2]="TRACE_CODE_TX_MGMT" +Var.SET \code[3]="TRACE_CODE_RX_MGMT" +Var.SET \code[4]="TRACE_CODE_RX_MGMT_TSF" +Var.SET \code[5]="TRACE_CODE_TX_COMPLETE" +Var.SET \code[6]="TRACE_CODE_TX_SME_MSG" +Var.SET \code[7]="TRACE_CODE_RX_SME_MSG" +Var.SET \code[8]="TRACE_CODE_TX_WMA_MSG" +Var.SET \code[9]="TRACE_CODE_RX_WMA_MSG" +Var.SET \code[10]="TRACE_CODE_TX_LIM_MSG" +Var.SET \code[11]="TRACE_CODE_RX_LIM_MSG" +Var.SET \code[12]="TRACE_CODE_TX_CFG_MSG" +Var.SET \code[13]="TRACE_CODE_RX_CFG_MSG" +Var.SET \code[14]="TRACE_CODE_RX_MGMT_DROP" +Var.SET \code[15]="TRACE_CODE_TIMER_ACTIVATE" +Var.SET \code[16]="TRACE_CODE_TIMER_DEACTIVATE" +Var.SET \code[17]="TRACE_CODE_INFO_LOG" + + +Var.NEW char [13][50] \module + +Var.SET \module[1]="QDF_MODULE_ID_TLSHIM" +Var.SET \module[2]="QDF_MODULE_ID_WMI" +Var.SET \module[3]="QDF_MODULE_ID_HTT" +Var.SET \module[4]="QDF_MODULE_ID_RSV4" +Var.SET \module[5]="QDF_MODULE_ID_HDD" +Var.SET \module[6]="QDF_MODULE_ID_SME" +Var.SET \module[7]="QDF_MODULE_ID_PE" +Var.SET \module[8]="QDF_MODULE_ID_WMA" +Var.SET \module[9]="QDF_MODULE_ID_SYS" +Var.SET \module[10]="QDF_MODULE_ID_QDF" +Var.SET \module[11]="QDF_MODULE_ID_SAP" +Var.SET \module[12]="QDF_MODULE_ID_HDD_SOFTAP" + +Var.NEW char [16][50] \mgmttype + +Var.SET \mgmttype[0]="SIR_MAC_MGMT_ASSOC_REQ" +Var.SET \mgmttype[1]="SIR_MAC_MGMT_ASSOC_RSP" +Var.SET \mgmttype[2]="SIR_MAC_MGMT_REASSOC_REQ" +Var.SET \mgmttype[3]="SIR_MAC_MGMT_REASSOC_RSP" +Var.SET \mgmttype[4]="SIR_MAC_MGMT_PROBE_REQ" +Var.SET \mgmttype[5]="SIR_MAC_MGMT_PROBE_RSP" +Var.SET \mgmttype[6]="SIR_MAC_MGMT_TIME_ADVERT" +Var.SET \mgmttype[8]="SIR_MAC_MGMT_BEACON" +Var.SET \mgmttype[9]="SIR_MAC_MGMT_ATIM" +Var.SET \mgmttype[10]="SIR_MAC_MGMT_DISASSOC" +Var.SET \mgmttype[11]="SIR_MAC_MGMT_AUTH" +Var.SET \mgmttype[12]="SIR_MAC_MGMT_DEAUTH" +Var.SET \mgmttype[13]="SIR_MAC_MGMT_ACTION" +Var.SET \mgmttype[15]="SIR_MAC_MGMT_RESERVED15" + +Var.NEW char [30][50] \limtimertype + +Var.SET \limtimertype[0]="eLIM_MIN_CHANNEL_TIMER" +Var.SET \limtimertype[1]="eLIM_MAX_CHANNEL_TIMER" +Var.SET \limtimertype[2]="eLIM_JOIN_FAIL_TIMER" +Var.SET \limtimertype[3]="eLIM_AUTH_FAIL_TIMER" +Var.SET \limtimertype[4]="eLIM_AUTH_RESP_TIMER" +Var.SET \limtimertype[5]="eLIM_ASSOC_FAIL_TIMER" +Var.SET \limtimertype[6]="eLIM_REASSOC_FAIL_TIMER" +Var.SET \limtimertype[7]="eLIM_PRE_AUTH_CLEANUP_TIMER" +Var.SET \limtimertype[8]="eLIM_CNF_WAIT_TIMER" +Var.SET \limtimertype[9]="eLIM_AUTH_RSP_TIMER" +Var.SET \limtimertype[10]="eLIM_UPDATE_OLBC_CACHE_TIMER" +Var.SET \limtimertype[11]="eLIM_PROBE_AFTER_HB_TIMER" +Var.SET \limtimertype[12]="eLIM_ADDTS_RSP_TIMER" +Var.SET \limtimertype[13]="eLIM_CHANNEL_SWITCH_TIMER" +Var.SET \limtimertype[14]="eLIM_LEARN_DURATION_TIMER" +Var.SET \limtimertype[15]="eLIM_QUIET_TIMER" +Var.SET \limtimertype[16]="eLIM_QUIET_BSS_TIMER" +Var.SET \limtimertype[17]="eLIM_WPS_OVERLAP_TIMER" +Var.SET \limtimertype[18]="eLIM_FT_PREAUTH_RSP_TIMER" +Var.SET \limtimertype[19]="eLIM_REMAIN_CHN_TIMER" +Var.SET \limtimertype[20]="eLIM_PERIODIC_PROBE_REQ_TIMER" +;#ifdef FEATURE_WLAN_CCX +;Var.SET \limtimertype[0]="eLIM_TSM_TIMER" +;#endif +Var.SET \limtimertype[22]="eLIM_DISASSOC_ACK_TIMER" +Var.SET \limtimertype[23]="eLIM_DEAUTH_ACK_TIMER" +Var.SET \limtimertype[24]="eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER" +Var.SET \limtimertype[25]="eLIM_INSERT_SINGLESHOT_NOA_TIMER" +Var.SET \limtimertype[26]="eLIM_CONVERT_ACTIVE_CHANNEL_TO_PASSIVE" + + + +Var.NEW char [256][100] \hddcodetype + +Var.SET \hddcodetype[0x00]="TRACE_CODE_HDD_OPEN_REQUEST" +Var.SET \hddcodetype[0x01]="TRACE_CODE_HDD_STOP_REQUEST" +Var.SET \hddcodetype[0x02]="TRACE_CODE_HDD_TX_TIMEOUT" +Var.SET \hddcodetype[0x03]="TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL" +Var.SET \hddcodetype[0x04]="TRACE_CODE_HDD_SETSUSPENDMODE_IOCTL" +Var.SET \hddcodetype[0x05]="TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL" +Var.SET \hddcodetype[0x06]="TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL" +Var.SET \hddcodetype[0x07]="TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL" +Var.SET \hddcodetype[0x08]="TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL" +Var.SET \hddcodetype[0x09]="TRACE_CODE_HDD_SETROAMDELTA_IOCTL" +Var.SET \hddcodetype[0x0A]="TRACE_CODE_HDD_GETROAMDELTA_IOCTL" +Var.SET \hddcodetype[0x0B]="TRACE_CODE_HDD_GETBAND_IOCTL" +Var.SET \hddcodetype[0x0C]="TRACE_CODE_HDD_GETCOUNTRYREV_IOCTL" +Var.SET \hddcodetype[0x0D]="TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL" +Var.SET \hddcodetype[0x0E]="TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL" +Var.SET \hddcodetype[0x0F]="TRACE_CODE_HDD_HOSTAPD_OPEN_REQUEST" +Var.SET \hddcodetype[0x10]="TRACE_CODE_HDD_HOSTAPD_STOP_REQUEST" +Var.SET \hddcodetype[0x11]="TRACE_CODE_HDD_HOSTAPD_UNINIT_REQUEST" +Var.SET \hddcodetype[0x12]="TRACE_CODE_HDD_SOFTAP_TX_TIMEOUT" +Var.SET \hddcodetype[0x13]="TRACE_CODE_HDD_HOSTAPD_SET_MAC_ADDR" +Var.SET \hddcodetype[0x14]="TRACE_CODE_HDD_HOSTAPD_P2P_SET_NOA_IOCTL" +Var.SET \hddcodetype[0x15]="TRACE_CODE_HDD_HOSTAPD_P2P_SET_PS_IOCTL" +Var.SET \hddcodetype[0x16]="TRACE_CODE_HDD_HOSTAPD_SET_SAP_CHANNEL_LIST_IOCTL" +Var.SET \hddcodetype[0x17]="TRACE_CODE_HDD_ADD_VIRTUAL_INTF" +Var.SET \hddcodetype[0x18]="TRACE_CODE_HDD_DEL_VIRTUAL_INTF" +Var.SET \hddcodetype[0x19]="TRACE_CODE_HDD_CHANGE_VIRTUAL_INTF" +Var.SET \hddcodetype[0x1A]="TRACE_CODE_HDD_CFG80211_START_AP" +Var.SET \hddcodetype[0x1B]="TRACE_CODE_HDD_CFG80211_CHANGE_BEACON" +Var.SET \hddcodetype[0x1C]="TRACE_CODE_HDD_CFG80211_STOP_AP" +Var.SET \hddcodetype[0x1D]="TRACE_CODE_HDD_CFG80211_CHANGE_BSS" +Var.SET \hddcodetype[0x1E]="TRACE_CODE_HDD_CFG80211_ADD_KEY" +Var.SET \hddcodetype[0x1F]="TRACE_CODE_HDD_CFG80211_GET_KEY" +Var.SET \hddcodetype[0x20]="TRACE_CODE_HDD_CFG80211_SET_DEFAULT_KEY" +Var.SET \hddcodetype[0x21]="TRACE_CODE_HDD_CFG80211_CONNECT" +Var.SET \hddcodetype[0x22]="TRACE_CODE_HDD_CFG80211_DISCONNECT" +Var.SET \hddcodetype[0x23]="TRACE_CODE_HDD_CFG80211_JOIN_IBSS" +Var.SET \hddcodetype[0x24]="TRACE_CODE_HDD_CFG80211_LEAVE_IBSS" +Var.SET \hddcodetype[0x25]="TRACE_CODE_HDD_CFG80211_SET_WIPHY_PARAMS" +Var.SET \hddcodetype[0x26]="TRACE_CODE_HDD_CFG80211_SET_TXPOWER" +Var.SET \hddcodetype[0x27]="TRACE_CODE_HDD_CFG80211_GET_TXPOWER" +Var.SET \hddcodetype[0x28]="TRACE_CODE_HDD_CFG80211_SET_CHANNEL" +Var.SET \hddcodetype[0x29]="TRACE_CODE_HDD_CFG80211_ADD_BEACON" +Var.SET \hddcodetype[0x2A]="TRACE_CODE_HDD_CFG80211_SET_BEACON" +Var.SET \hddcodetype[0x2B]="TRACE_CODE_HDD_CFG80211_CHANGE_IFACE" +Var.SET \hddcodetype[0x2C]="TRACE_CODE_HDD_CHANGE_STATION" +Var.SET \hddcodetype[0x2D]="TRACE_CODE_HDD_CFG80211_UPDATE_BSS" +Var.SET \hddcodetype[0x2E]="TRACE_CODE_HDD_CFG80211_SCAN" +Var.SET \hddcodetype[0x2F]="TRACE_CODE_HDD_REMAIN_ON_CHANNEL" +Var.SET \hddcodetype[0x30]="TRACE_CODE_HDD_REMAINCHANREADYHANDLER" +Var.SET \hddcodetype[0x31]="TRACE_CODE_HDD_CFG80211_CANCEL_REMAIN_ON_CHANNEL" +Var.SET \hddcodetype[0x32]="TRACE_CODE_HDD_ACTION" +Var.SET \hddcodetype[0x33]="TRACE_CODE_HDD_MGMT_TX_CANCEL_WAIT" +Var.SET \hddcodetype[0x34]="TRACE_CODE_HDD_CFG80211_GET_STA" +Var.SET \hddcodetype[0x35]="TRACE_CODE_HDD_CFG80211_SET_POWER_MGMT" +Var.SET \hddcodetype[0x36]="TRACE_CODE_HDD_CFG80211_DEL_STA" +Var.SET \hddcodetype[0x37]="TRACE_CODE_HDD_CFG80211_ADD_STA" +Var.SET \hddcodetype[0x38]="TRACE_CODE_HDD_CFG80211_SET_PMKSA" +Var.SET \hddcodetype[0x39]="TRACE_CODE_HDD_CFG80211_UPDATE_FT_IES" +Var.SET \hddcodetype[0x3A]="TRACE_CODE_HDD_CFG80211_TDLS_MGMT" +Var.SET \hddcodetype[0x3B]="TRACE_CODE_HDD_CFG80211_TDLS_OPER" +Var.SET \hddcodetype[0x3C]="TRACE_CODE_HDD_CFG80211_SET_REKEY_DATA" +Var.SET \hddcodetype[0x3D]="TRACE_CODE_HDD_UNSUPPORTED_IOCTL" +Var.SET \hddcodetype[0x3E]="TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL" +Var.SET \hddcodetype[0x3F]="TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL" +Var.SET \hddcodetype[0x40]="TRACE_CODE_HDD_STORE_JOIN_REQ" +Var.SET \hddcodetype[0x41]="TRACE_CODE_HDD_CLEAR_JOIN_REQ" +Var.SET \hddcodetype[0x42]="TRACE_CODE_HDD_ISSUE_JOIN_REQ" +Var.SET \hddcodetype[0x43]="TRACE_CODE_HDD_CFG80211_RESUME_WLAN" +Var.SET \hddcodetype[0x44]="TRACE_CODE_HDD_CFG80211_SUSPEND_WLAN" +Var.SET \hddcodetype[0x45]="TRACE_CODE_HDD_CFG80211_SET_MAC_ACL" +Var.SET \hddcodetype[0x46]="TRACE_CODE_HDD_CFG80211_TESTMODE" +Var.SET \hddcodetype[0x47]="TRACE_CODE_HDD_CFG80211_DUMP_SURVEY" +Var.SET \hddcodetype[0x48]="TRACE_CODE_HDD_CFG80211_SCHED_SCAN_START" +Var.SET \hddcodetype[0x49]="TRACE_CODE_HDD_CFG80211_SCHED_SCAN_STOP" +Var.SET \hddcodetype[0x4A]="TRACE_CODE_HDD_CFG80211_DEL_PMKSA" +;New CFG80211 enums to be added before this comment +;TRACE_CODE_HDD_RX_SME_MSG is used as code for MTRACE commands +;and also update the below index value +Var.SET \hddcodetype[0x4B]="TRACE_CODE_HDD_RX_SME_MSG" + +&TRACETYPESIZE=v.value(sizeof(qdf_trace_record_t)) +&TRACESIZE=v.value(sizeof(g_qdf_trace_tbl)) +&TRACEMAXINDEX=v.value(&TRACESIZE/&TRACETYPESIZE) + +&HEAD=v.value(g_qdf_trace_data.head) +&TAIL=v.value(g_qdf_trace_data.tail) + +IF ((&HEAD>&TRACEMAXINDEX)||(&TAIL>&TRACEMAXINDEX)||(&TAIL==&HEAD)) +( + GOTO ENDSCRIPT +) + +&INDEX=&HEAD + +TRACESTART: + +&TIME=v.value(g_qdf_trace_tbl[&INDEX].time) +&MODULE=v.value(g_qdf_trace_tbl[&INDEX].module) +&CODE=v.value(g_qdf_trace_tbl[&INDEX].code) +&SESSION=v.value(g_qdf_trace_tbl[&INDEX].session) +&DATA=v.value(g_qdf_trace_tbl[&INDEX].data) + + + WRITE #1 "TIME: " &TIME + Var.Write #1 %STRING \module[&MODULE] + +IF (&MODULE==0x7) +( +if (&CODE>=0)&&(&CODE<=0x12) +( + Var.Write #1 %STRING \code[&CODE] " [" %Hex &CODE "]" +) + + IF (&SESSION==0xFF) + ( + WRITE #1 "NO SESSION" + ) + ELSE + ( + WRITE #1 "SESSION: " &SESSION + ) + + +;0 TRACE_CODE_MLM_STATE +IF (&CODE==0x0) +( + Var.NEW tLimMlmStates \mlmstate + Var.Set \mlmstate=&DATA + ;Var.Write #1 \mlmstate " [" %Hex &DATA "]" +) + +;1 TRACE_CODE_SME_STATE +IF (&CODE==0x1) +( + Var.NEW tLimSmeStates \smestate + Var.Set \smestate=&DATA + Var.Write #1 \smestate " [" %Hex &DATA "]" +) + +;2 TRACE_CODE_TX_MGMT +IF (&CODE==0x2) +( + WRITE #1 "DATA: " &DATA +) + +;3 TRACE_CODE_RX_MGMT +IF (&CODE==0x3) +( + &SERIAL=v.value(&DATA>>16) + &SUBTYPE=v.value(&DATA&0xFF) + if (&SUBTYPE<=0xF) + ( + Var.Write #1 %STRING \mgmttype[&SUBTYPE] + WRITE #1 "SEQ NUM: " &SERIAL + ) + else + ( + WRITE #1 "INCORRECT DATA" + ) +) + +;4 TRACE_CODE_RX_MGMT_TSF +IF (&CODE==0x4) +( + WRITE #1 "BEACON TS: " &DATA +) + +;5 TRACE_CODE_TX_COMPLETE +IF (&CODE==0x5) +( + Var.Write #1 %STRING \mgmttype[&DATA] " [" %Hex &DATA "]" +) + +;14 TRACE_CODE_RX_MGMT_DROP +IF (&CODE==0xE) +( + Var.NEW tMgmtFrmDropReason \dropreason + Var.Set \dropreason=&DATA + Var.Write #1 \dropreason " [" %Hex &DATA "]" +) + + +;15 TRACE_CODE_TIMER_ACTIVATE/DEACTIVATE +IF (&CODE==0xF)||(&CODE==0x10) +( + Var.Write #1 %STRING \limtimertype[&DATA] " [" %Hex &DATA "]" +) + +;6 TRACE_CODE_TX_SME_MSG +IF (&CODE==0x6) +( +&MOD=v.value(&DATA>>8) +&MSG=v.value(&DATA&0xFF) +IF (&MOD==0x12)&&(&DATA>=0x12B0) +( +Var.Write #1 %STRING \limmsgtype[&MSG] " [" %Hex &MSG "]" +) +IF (&DATA>=0x1500) +( + Var.NEW enum eWniMsgTypes \smemsg + Var.Set \smemsg=&DATA + Var.Write #1 \smemsg " [" %Hex &DATA "]" + ) +) + +;7 TRACE_CODE_RX_SME_MSG +IF (&CODE==0x7) +( +&MOD=v.value(&DATA>>8) +&MSG=v.value(&DATA&0xFF) +IF (&MOD==0x12)&&(&DATA>=0x12B0) +( +Var.Write #1 %STRING \limmsgtype[&MSG] " [" %Hex &MSG "]" +) +IF (&DATA>=0x1500) +( + Var.NEW enum eWniMsgTypes \smemsg + Var.Set \smemsg=&DATA + Var.Write #1 \smemsg " [" %Hex &DATA "]" +) +) + +;8 TRACE_CODE_TX_WMA_MSG +IF (&CODE==0x8) +( +&MOD=v.value(&DATA>>8) +&MSG=v.value(&DATA&0xFF) +IF (&MOD==0x10) +( +Var.Write #1 %STRING \halmsgtype[&MSG] " [" %Hex &MSG "]" +) +) + +;9 TRACE_CODE_RX_WMA_MSG +IF (&CODE==0x9) +( +&MOD=v.value(&DATA>>8) +&MSG=v.value(&DATA&0xFF) +IF (&MOD==0x10) +( +Var.Write #1 %STRING \halmsgtype[&MSG] " [" %Hex &MSG "]" +) +) + +;10 TRACE_CODE_TX_LIM_MSG +IF (&CODE==0xA) +( +&MOD=v.value(&DATA>>8) +&MSG=v.value(&DATA&0xFF) +IF (&MOD==0x12)&&(&DATA>=0x12B0) +( +Var.Write #1 %STRING \limmsgtype[&MSG] " [" %Hex &MSG "]" +) +IF (&DATA>=0x1500) +( + Var.NEW enum eWniMsgTypes \smemsg + Var.Set \smemsg=&DATA + Var.Write #1 \smemsg " [" %Hex &DATA "]" +) +) + +;11 TRACE_CODE_RX_LIM_MSG +IF (&CODE==0xB) +( +&MOD=v.value(&DATA>>8) +&MSG=v.value(&DATA&0xFF) +IF (&MOD==0x12)&&(&DATA>=0x12B0) +( +Var.Write #1 %STRING \limmsgtype[&MSG] " [" %Hex &MSG "]" +) +IF (&DATA>=0x1500) +( + Var.NEW enum eWniMsgTypes \smemsg + &DATA=v.value(&DATA&0xFFFF) + Var.Set \smemsg=&DATA + Var.Write #1 \smemsg " [" %Hex &DATA "]" +) +) + +;12 TRACE_CODE_TX_CFG_MSG +IF (&CODE==0xC) +( +&MOD=v.value(&DATA>>8) +&MSG=v.value(&DATA&0xFF) +IF (&MOD==0x11)&&(&DATA>=0x11B0) +( +Var.Write #1 %STRING \cfgmsgtype[&MSG] " [" %Hex &MSG "]" +) +) + +;13 TRACE_CODE_RX_CFG_MSG +IF (&CODE==0xD) +( +&MOD=v.value(&DATA>>8) +&MSG=v.value(&DATA&0xFF) +IF (&MOD==0x11)&&(&DATA>=0x11B0) +( +Var.Write #1 %STRING \cfgmsgtype[&MSG] " [" %Hex &MSG "]" +) +) + +IF (&DATA>=0x1500) +( + Var.NEW enum eWniMsgTypes \smemsg + &DATA=v.value(&DATA&0xFFFF) + Var.Set \smemsg=&DATA + Var.Write #1 \smemsg " [" %Hex &DATA "]" +) + +) + + +IF (&MODULE==0x6) +( + + IF ((&CODE>=0x0)&&(&CODE<=0x61)) + ( + Var.Write #1 %STRING \smecodetype[&CODE] " [" %Hex &CODE "]" + ) + ELSE + ( + IF ((&CODE>=0xFA)&&(&CODE<=0xFC)) + ( + Var.Write #1 %STRING \smecodetype[&CODE] " [" %Hex &CODE "]" + ) + ELSE + ( + WRITE #1 "CODE: " &CODE + ) + ) + WRITE #1 "DATA: " &DATA + + IF (&SESSION==0xFF) + ( + WRITE #1 "NO SESSION" + ) + ELSE + ( + WRITE #1 "SESSION: " &SESSION + ) +) + +IF (&MODULE==0x5) +( + + IF ((&CODE>=0x0)&&(&CODE<=0x4A)) + ( + Var.Write #1 %STRING \hddcodetype[&CODE] " [" %Hex &CODE "]" + WRITE #1 "DATA: " &DATA + ) + ELSE + ( + IF (&CODE==0x4B) + ( + Var.Write #1 %STRING \hddcodetype[&CODE] " [" %Hex &CODE "]" + Var.NEW eRoamCmdStatus \csrstatus + Var.Set \csrstatus=&DATA + Var.Write #1 \csrstatus " [" %Hex &DATA "]" + ) + ELSE + ( + WRITE #1 "CODE: " &CODE + WRITE #1 "DATA: " &DATA + ) + ) + + IF (&SESSION==0xFF) + ( + WRITE #1 "NO SESSION" + ) + ELSE + ( + WRITE #1 "SESSION: " &SESSION + ) +) + +WRITE #1 " " + +&INDEX=v.value((&INDEX+1)%(&TRACEMAXINDEX)) + + IF (&INDEX!=&HEAD) + ( + GOTO TRACESTART + ) + + + +ENDSCRIPT: +CLOSE #1 +ENDDO diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/parser_api.c b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/parser_api.c new file mode 100644 index 0000000000000000000000000000000000000000..0a78d2b7bc2f0d3266d2935c3924b4081539cd64 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/parser_api.c @@ -0,0 +1,6305 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file parser_api.cc contains the code for parsing + * 802.11 messages. + * Author: Pierre Vandwalle + * Date: 03/18/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "sir_api.h" +#include "ani_global.h" +#include "parser_api.h" +#include "lim_utils.h" +#include "utils_parser.h" +#include "lim_ser_des_utils.h" +#include "sch_api.h" +#include "wmm_apsd.h" +#include "rrm_api.h" + +#include "cds_regdomain.h" +#include "qdf_crypto.h" +#include "lim_process_fils.h" +#include "wlan_utility.h" +#include "wifi_pos_api.h" +#include "wlan_mlme_public_struct.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_mlme_api.h" +#include "wlan_crypto_global_api.h" + +#define RSN_OUI_SIZE 4 +/* ////////////////////////////////////////////////////////////////////// */ +void swap_bit_field16(uint16_t in, uint16_t *out) +{ +#ifdef ANI_LITTLE_BIT_ENDIAN + *out = in; +#else /* Big-Endian... */ + *out = ((in & 0x8000) >> 15) | + ((in & 0x4000) >> 13) | + ((in & 0x2000) >> 11) | + ((in & 0x1000) >> 9) | + ((in & 0x0800) >> 7) | + ((in & 0x0400) >> 5) | + ((in & 0x0200) >> 3) | + ((in & 0x0100) >> 1) | + ((in & 0x0080) << 1) | + ((in & 0x0040) << 3) | + ((in & 0x0020) << 5) | + ((in & 0x0010) << 7) | + ((in & 0x0008) << 9) | + ((in & 0x0004) << 11) | + ((in & 0x0002) << 13) | ((in & 0x0001) << 15); +#endif /* ANI_LITTLE_BIT_ENDIAN */ +} + +static inline void __print_wmm_params(struct mac_context *mac, + tDot11fIEWMMParams *pWmm) +{ + pe_debug("WMM Parameters Received:"); + pe_debug("BE: aifsn %d, acm %d, aci %d, cwmin %d, cwmax %d, txop %d", + pWmm->acbe_aifsn, pWmm->acbe_acm, pWmm->acbe_aci, + pWmm->acbe_acwmin, pWmm->acbe_acwmax, pWmm->acbe_txoplimit); + + pe_debug("BK: aifsn %d, acm %d, aci %d, cwmin %d, cwmax %d, txop %d", + pWmm->acbk_aifsn, pWmm->acbk_acm, pWmm->acbk_aci, + pWmm->acbk_acwmin, pWmm->acbk_acwmax, pWmm->acbk_txoplimit); + + pe_debug("VI: aifsn %d, acm %d, aci %d, cwmin %d, cwmax %d, txop %d", + pWmm->acvi_aifsn, pWmm->acvi_acm, pWmm->acvi_aci, + pWmm->acvi_acwmin, pWmm->acvi_acwmax, pWmm->acvi_txoplimit); + + pe_debug("VO: aifsn %d, acm %d, aci %d, cwmin %d, cwmax %d, txop %d", + pWmm->acvo_aifsn, pWmm->acvo_acm, pWmm->acvo_aci, + pWmm->acvo_acwmin, pWmm->acvo_acwmax, pWmm->acvo_txoplimit); + + return; +} + +/* ////////////////////////////////////////////////////////////////////// */ +/* Functions for populating "dot11f" style IEs */ + +/* return: >= 0, the starting location of the IE in rsnIEdata inside tSirRSNie */ +/* < 0, cannot find */ +int find_ie_location(struct mac_context *mac, tpSirRSNie pRsnIe, uint8_t EID) +{ + int idx, ieLen, bytesLeft; + int ret_val = -1; + + /* Here's what's going on: 'rsnIe' looks like this: */ + + /* typedef struct sSirRSNie */ + /* { */ + /* uint16_t length; */ + /* uint8_t rsnIEdata[WLAN_MAX_IE_LEN+2]; */ + /* } tSirRSNie, *tpSirRSNie; */ + + /* other code records both the WPA & RSN IEs (including their EIDs & */ + /* lengths) into the array 'rsnIEdata'. We may have: */ + + /* With WAPI support, there may be 3 IEs here */ + /* It can be only WPA IE, or only RSN IE or only WAPI IE */ + /* Or two or all three of them with no particular ordering */ + + /* The if/then/else statements that follow are here to figure out */ + /* whether we have the WPA IE, and where it is if we *do* have it. */ + + /* Save the first IE length */ + ieLen = pRsnIe->rsnIEdata[1] + 2; + idx = 0; + bytesLeft = pRsnIe->length; + + while (1) { + if (EID == pRsnIe->rsnIEdata[idx]) + /* Found it */ + return idx; + if (EID != pRsnIe->rsnIEdata[idx] && + /* & if no more IE, */ + bytesLeft <= (uint16_t)(ieLen)) + return ret_val; + + bytesLeft -= ieLen; + ieLen = pRsnIe->rsnIEdata[idx + 1] + 2; + idx += ieLen; + } + + return ret_val; +} + +QDF_STATUS +populate_dot11f_capabilities(struct mac_context *mac, + tDot11fFfCapabilities *pDot11f, + struct pe_session *pe_session) +{ + uint16_t cfg; + QDF_STATUS nSirStatus; + + nSirStatus = lim_get_capability_info(mac, &cfg, pe_session); + if (QDF_STATUS_SUCCESS != nSirStatus) { + pe_err("Failed to retrieve the Capabilities bitfield from CFG status: %d", + nSirStatus); + return nSirStatus; + } + + swap_bit_field16(cfg, (uint16_t *) pDot11f); + + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_capabilities. */ + +/** + * populate_dot_11_f_ext_chann_switch_ann() - Function to populate ECS + * @mac_ptr: Pointer to PMAC structure + * @dot_11_ptr: ECS element + * @session_entry: PE session entry + * + * This function is used to populate the extended channel switch element + * + * Return: None + */ +void populate_dot_11_f_ext_chann_switch_ann(struct mac_context *mac_ptr, + tDot11fIEext_chan_switch_ann *dot_11_ptr, + struct pe_session *session_entry) +{ + uint8_t ch_offset; + uint32_t sw_target_freq; + uint8_t primary_channel; + enum phy_ch_width ch_width; + + ch_width = session_entry->gLimChannelSwitch.ch_width; + ch_offset = session_entry->gLimChannelSwitch.sec_ch_offset; + + dot_11_ptr->switch_mode = session_entry->gLimChannelSwitch.switchMode; + sw_target_freq = session_entry->gLimChannelSwitch.sw_target_freq; + primary_channel = session_entry->gLimChannelSwitch.primaryChannel; + dot_11_ptr->new_reg_class = + lim_op_class_from_bandwidth(mac_ptr, sw_target_freq, ch_width, + ch_offset); + dot_11_ptr->new_channel = + session_entry->gLimChannelSwitch.primaryChannel; + dot_11_ptr->switch_count = + session_entry->gLimChannelSwitch.switchCount; + dot_11_ptr->present = 1; + + pe_debug("country:%s chan:%d freq %d width:%d reg:%d off:%d", + mac_ptr->scan.countryCodeCurrent, + session_entry->gLimChannelSwitch.primaryChannel, + sw_target_freq, + session_entry->gLimChannelSwitch.ch_width, + dot_11_ptr->new_reg_class, + session_entry->gLimChannelSwitch.sec_ch_offset); +} + +void +populate_dot11f_chan_switch_ann(struct mac_context *mac, + tDot11fIEChanSwitchAnn *pDot11f, + struct pe_session *pe_session) +{ + pDot11f->switchMode = pe_session->gLimChannelSwitch.switchMode; + pDot11f->newChannel = pe_session->gLimChannelSwitch.primaryChannel; + pDot11f->switchCount = + (uint8_t) pe_session->gLimChannelSwitch.switchCount; + + pDot11f->present = 1; +} + +/** + * populate_dot11_supp_operating_classes() - Function to populate supported + * operating class IE + * @mac_ptr: Pointer to PMAC structure + * @dot_11_ptr: Operating class element + * @session_entry: PE session entry + * + * Return: None + */ +void +populate_dot11_supp_operating_classes(struct mac_context *mac_ptr, + tDot11fIESuppOperatingClasses *dot_11_ptr, + struct pe_session *session_entry) +{ + uint8_t ch_offset; + + if (session_entry->ch_width == CH_WIDTH_80MHZ) { + ch_offset = BW80; + } else { + switch (session_entry->htSecondaryChannelOffset) { + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + ch_offset = BW40_HIGH_PRIMARY; + break; + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + ch_offset = BW40_LOW_PRIMARY; + break; + default: + ch_offset = BW20; + break; + } + } + + wlan_reg_dmn_get_curr_opclasses(&dot_11_ptr->num_classes, + &dot_11_ptr->classes[1]); + dot_11_ptr->classes[0] = + lim_op_class_from_bandwidth(mac_ptr, + session_entry->curr_op_freq, + session_entry->ch_width, + ch_offset); + dot_11_ptr->num_classes++; + dot_11_ptr->present = 1; +} + +void +populate_dot11f_vht_tx_power_env(struct mac_context *mac, + tDot11fIEvht_transmit_power_env *pDot11f, + enum phy_ch_width ch_width, uint32_t chan_freq) +{ + uint8_t num_tx_power, i, tx_power; + int reg_max; + + switch (ch_width) { + case CH_WIDTH_20MHZ: + /* Max Transmit Power count = 0 (20 MHz) */ + num_tx_power = 0; + break; + case CH_WIDTH_40MHZ: + /* Max Transmit Power count = 1 (20, 40 MHz) */ + num_tx_power = 1; + break; + case CH_WIDTH_80MHZ: + /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ + num_tx_power = 2; + break; + case CH_WIDTH_160MHZ: + case CH_WIDTH_80P80MHZ: + /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ + num_tx_power = 3; + break; + default: + return; + } + + reg_max = wlan_reg_get_channel_reg_power_for_freq(mac->pdev, chan_freq); + + /* in 0.5 dB steps */ + reg_max *= 2; + if (reg_max > 127) + /* 63.5 has special meaning of 63.5 dBm or higher */ + reg_max = 127; + + if (reg_max < -128) + reg_max = -128; + + if (reg_max < 0) + tx_power = 0x80 + reg_max + 128; + else + tx_power = reg_max; + + /* Ignore EID field */ + pDot11f->present = 1; + pDot11f->num_bytes = num_tx_power + 2; + /* + * Max Transmit Power count and + * Max Transmit Power units = 0 (EIRP) + */ + pDot11f->bytes[0] = num_tx_power; + for (i = 0; i <= num_tx_power; i++) + pDot11f->bytes[i + 1] = tx_power; +} + +void +populate_dot11f_chan_switch_wrapper(struct mac_context *mac, + tDot11fIEChannelSwitchWrapper *pDot11f, + struct pe_session *pe_session) +{ + /* + * The new country subelement is present only when + * 1. AP performs Extended Channel switching to new country. + * 2. New Operating Class table or a changed set of operating + * classes relative to the contents of the country element sent + * in the beacons. + * + * In the current scenario Channel Switch wrapper IE is included + * when we a radar is found and the AP does a channel change in + * the same regulatory domain(No country change or Operating class + * table). So, we do not need to include the New Country IE. + * + * Transmit Power Envlope Subelement is optional + * in Channel Switch Wrapper IE. So, not setting + * the TPE subelement. We include only WiderBWChanSwitchAnn. + */ + pDot11f->present = 1; + + /* + * Add the Wide Channel Bandwidth Sublement. + */ + pDot11f->WiderBWChanSwitchAnn.newChanWidth = + pe_session->gLimWiderBWChannelSwitch.newChanWidth; + pDot11f->WiderBWChanSwitchAnn.newCenterChanFreq0 = + pe_session->gLimWiderBWChannelSwitch.newCenterChanFreq0; + pDot11f->WiderBWChanSwitchAnn.newCenterChanFreq1 = + pe_session->gLimWiderBWChannelSwitch.newCenterChanFreq1; + pDot11f->WiderBWChanSwitchAnn.present = 1; + + /* + * Add the VHT Transmit power Envelope Sublement. + */ + if (pe_session->vhtCapability) { + populate_dot11f_vht_tx_power_env(mac, + &pDot11f->vht_transmit_power_env, + pe_session->gLimChannelSwitch.ch_width, + pe_session->gLimChannelSwitch.primaryChannel); + } +} + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +void +populate_dot11f_avoid_channel_ie(struct mac_context *mac_ctx, + tDot11fIEQComVendorIE *dot11f, + struct pe_session *pe_session) +{ + if (!pe_session->sap_advertise_avoid_ch_ie) + return; + + dot11f->present = true; + dot11f->type = QCOM_VENDOR_IE_MCC_AVOID_CH; + dot11f->channel = wlan_reg_freq_to_chan( + mac_ctx->pdev, pe_session->curr_op_freq); +} +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +QDF_STATUS +populate_dot11f_country(struct mac_context *mac, + tDot11fIECountry *ctry_ie, struct pe_session *pe_session) +{ + uint8_t code[REG_ALPHA2_LEN + 1]; + qdf_freq_t cur_triplet_freq; + uint8_t cur_triplet_num_chans; + uint8_t cur_triplet_tx_power; + bool cur_triplet_valid; + enum reg_wifi_band cur_triplet_band; + int chan_enum; + struct regulatory_channel *cur_chan_list; + QDF_STATUS status; + + cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(*cur_chan_list)); + if (!cur_chan_list) + return QDF_STATUS_E_NOMEM; + + status = wlan_reg_get_current_chan_list(mac->pdev, cur_chan_list); + if (status != QDF_STATUS_SUCCESS) { + pe_err("failed to get cur_chan list"); + qdf_mem_free(cur_chan_list); + return status; + } + + wlan_reg_read_current_country(mac->psoc, code); + qdf_mem_copy(ctry_ie->country, code, REG_ALPHA2_LEN); + + /* advertise global operating class */ + ctry_ie->country[REG_ALPHA2_LEN] = 0x04; + + cur_triplet_valid = false; + ctry_ie->num_triplets = 0; + for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { + if (wlan_reg_is_6ghz_chan_freq( + cur_chan_list[chan_enum].center_freq)) { + if (cur_triplet_valid) { + ctry_ie->triplets[ctry_ie->num_triplets][0] = + wlan_reg_freq_to_chan(mac->pdev, + cur_triplet_freq); + ctry_ie->triplets[ctry_ie->num_triplets][1] = + cur_triplet_num_chans; + ctry_ie->triplets[ctry_ie->num_triplets][2] = + cur_triplet_tx_power; + ctry_ie->num_triplets++; + cur_triplet_valid = false; + } + break; + } + + if (cur_chan_list[chan_enum].chan_flags & + REGULATORY_CHAN_DISABLED) { + if (cur_triplet_valid) { + ctry_ie->triplets[ctry_ie->num_triplets][0] = + wlan_reg_freq_to_chan(mac->pdev, + cur_triplet_freq); + ctry_ie->triplets[ctry_ie->num_triplets][1] = + cur_triplet_num_chans; + ctry_ie->triplets[ctry_ie->num_triplets][2] = + cur_triplet_tx_power; + ctry_ie->num_triplets++; + cur_triplet_valid = false; + } + continue; + } + + if (cur_triplet_valid) { + if ((cur_chan_list[chan_enum].tx_power == + cur_triplet_tx_power) && + (cur_triplet_band == + wlan_reg_freq_to_band(cur_chan_list[chan_enum].center_freq))) + cur_triplet_num_chans++; + else { + ctry_ie->triplets[ctry_ie->num_triplets][0] = + wlan_reg_freq_to_chan(mac->pdev, + cur_triplet_freq); + ctry_ie->triplets[ctry_ie->num_triplets][1] = + cur_triplet_num_chans; + ctry_ie->triplets[ctry_ie->num_triplets][2] = + cur_triplet_tx_power; + ctry_ie->num_triplets++; + + cur_triplet_freq = + cur_chan_list[chan_enum].center_freq; + cur_triplet_num_chans = 1; + cur_triplet_tx_power = + cur_chan_list[chan_enum].tx_power; + cur_triplet_band = wlan_reg_freq_to_band(cur_triplet_freq); + } + } else { + cur_triplet_freq = cur_chan_list[chan_enum].center_freq; + cur_triplet_num_chans = 1; + cur_triplet_tx_power = + cur_chan_list[chan_enum].tx_power; + cur_triplet_band = wlan_reg_freq_to_band(cur_triplet_freq); + cur_triplet_valid = true; + } + } + + if (ctry_ie->num_triplets == 0) { + /* at-least one triplet should be present */ + qdf_mem_free(cur_chan_list); + return QDF_STATUS_SUCCESS; + } + + ctry_ie->present = 1; + + qdf_mem_free(cur_chan_list); + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_country. */ + +/** + * populate_dot11f_ds_params() - To populate DS IE params + * mac_ctx: Pointer to global mac context + * dot11f_param: pointer to DS params IE + * channel: channel number + * + * This routine will populate DS param in management frame like + * beacon, probe response, and etc. + * + * Return: Overall success + */ +QDF_STATUS +populate_dot11f_ds_params(struct mac_context *mac_ctx, + tDot11fIEDSParams *dot11f_param, uint8_t channel) +{ + if (IS_24G_CH(channel)) { + /* .11b/g mode PHY => Include the DS Parameter Set IE: */ + dot11f_param->curr_channel = channel; + dot11f_param->present = 1; + } + + return QDF_STATUS_SUCCESS; +} + +#define SET_AIFSN(aifsn) (((aifsn) < 2) ? 2 : (aifsn)) + +void +populate_dot11f_edca_param_set(struct mac_context *mac, + tDot11fIEEDCAParamSet *pDot11f, + struct pe_session *pe_session) +{ + + if (pe_session->limQosEnabled) { + /* change to bitwise operation, after this is fixed in frames. */ + pDot11f->qos = + (uint8_t) (0xf0 & + (pe_session->gLimEdcaParamSetCount << 4)); + + /* Fill each EDCA parameter set in order: be, bk, vi, vo */ + pDot11f->acbe_aifsn = + (0xf & + SET_AIFSN(pe_session->gLimEdcaParamsBC[0].aci.aifsn)); + pDot11f->acbe_acm = + (0x1 & pe_session->gLimEdcaParamsBC[0].aci.acm); + pDot11f->acbe_aci = (0x3 & QCA_WLAN_AC_BE); + pDot11f->acbe_acwmin = + (0xf & pe_session->gLimEdcaParamsBC[0].cw.min); + pDot11f->acbe_acwmax = + (0xf & pe_session->gLimEdcaParamsBC[0].cw.max); + pDot11f->acbe_txoplimit = + pe_session->gLimEdcaParamsBC[0].txoplimit; + + pDot11f->acbk_aifsn = + (0xf & + SET_AIFSN(pe_session->gLimEdcaParamsBC[1].aci.aifsn)); + pDot11f->acbk_acm = + (0x1 & pe_session->gLimEdcaParamsBC[1].aci.acm); + pDot11f->acbk_aci = (0x3 & QCA_WLAN_AC_BK); + pDot11f->acbk_acwmin = + (0xf & pe_session->gLimEdcaParamsBC[1].cw.min); + pDot11f->acbk_acwmax = + (0xf & pe_session->gLimEdcaParamsBC[1].cw.max); + pDot11f->acbk_txoplimit = + pe_session->gLimEdcaParamsBC[1].txoplimit; + + pDot11f->acvi_aifsn = + (0xf & + SET_AIFSN(pe_session->gLimEdcaParamsBC[2].aci.aifsn)); + pDot11f->acvi_acm = + (0x1 & pe_session->gLimEdcaParamsBC[2].aci.acm); + pDot11f->acvi_aci = (0x3 & QCA_WLAN_AC_VI); + pDot11f->acvi_acwmin = + (0xf & pe_session->gLimEdcaParamsBC[2].cw.min); + pDot11f->acvi_acwmax = + (0xf & pe_session->gLimEdcaParamsBC[2].cw.max); + pDot11f->acvi_txoplimit = + pe_session->gLimEdcaParamsBC[2].txoplimit; + + pDot11f->acvo_aifsn = + (0xf & + SET_AIFSN(pe_session->gLimEdcaParamsBC[3].aci.aifsn)); + pDot11f->acvo_acm = + (0x1 & pe_session->gLimEdcaParamsBC[3].aci.acm); + pDot11f->acvo_aci = (0x3 & QCA_WLAN_AC_VO); + pDot11f->acvo_acwmin = + (0xf & pe_session->gLimEdcaParamsBC[3].cw.min); + pDot11f->acvo_acwmax = + (0xf & pe_session->gLimEdcaParamsBC[3].cw.max); + pDot11f->acvo_txoplimit = + pe_session->gLimEdcaParamsBC[3].txoplimit; + + pDot11f->present = 1; + } + +} /* End PopluateDot11fEDCAParamSet. */ + +QDF_STATUS +populate_dot11f_erp_info(struct mac_context *mac, + tDot11fIEERPInfo *pDot11f, struct pe_session *pe_session) +{ + uint32_t val; + enum reg_wifi_band rfBand = REG_BAND_UNKNOWN; + + lim_get_rf_band_new(mac, &rfBand, pe_session); + if (REG_BAND_2G == rfBand) { + pDot11f->present = 1; + + val = pe_session->cfgProtection.fromllb; + if (!val) { + pe_err("11B protection not enabled. Not populating ERP IE %d", + val); + return QDF_STATUS_SUCCESS; + } + + if (pe_session->gLim11bParams.protectionEnabled) { + pDot11f->non_erp_present = 1; + pDot11f->use_prot = 1; + } + + if (pe_session->gLimOlbcParams.protectionEnabled) { + /* FIXME_PROTECTION: we should be setting non_erp present also. */ + /* check the test plan first. */ + pDot11f->use_prot = 1; + } + + if ((pe_session->gLimNoShortParams.numNonShortPreambleSta) + || !pe_session->beaconParams.fShortPreamble) { + pDot11f->barker_preamble = 1; + + } + } + + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_erp_info. */ + +QDF_STATUS +populate_dot11f_ext_supp_rates(struct mac_context *mac, uint8_t nChannelNum, + tDot11fIEExtSuppRates *pDot11f, + struct pe_session *pe_session) +{ + QDF_STATUS nsir_status; + qdf_size_t n_rates = 0; + uint8_t rates[WLAN_SUPPORTED_RATES_IE_MAX_LEN]; + + /* Use the ext rates present in session entry whenever nChannelNum is set to OPERATIONAL + else use the ext supported rate set from CFG, which is fixed and does not change dynamically and is used for + sending mgmt frames (lile probe req) which need to go out before any session is present. + */ + if (POPULATE_DOT11F_RATES_OPERATIONAL == nChannelNum) { + if (pe_session) { + n_rates = pe_session->extRateSet.numRates; + qdf_mem_copy(rates, pe_session->extRateSet.rate, + n_rates); + } else { + pe_err("no session context exists while populating Operational Rate Set"); + } + } else if (HIGHEST_24GHZ_CHANNEL_NUM >= nChannelNum) { + n_rates = mac->mlme_cfg->rates.ext_opr_rate_set.len; + nsir_status = wlan_mlme_get_cfg_str( + rates, + &mac->mlme_cfg->rates.ext_opr_rate_set, &n_rates); + if (QDF_IS_STATUS_ERROR(nsir_status)) { + n_rates = 0; + pe_err("Failed to retrieve nItem from CFG status: %d", + (nsir_status)); + return nsir_status; + } + } + + if (0 != n_rates) { + pe_debug("ext supp rates present, num %d", (uint8_t)n_rates); + pDot11f->num_rates = (uint8_t)n_rates; + qdf_mem_copy(pDot11f->rates, rates, n_rates); + pDot11f->present = 1; + } + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_ext_supp_rates. */ + +QDF_STATUS +populate_dot11f_ext_supp_rates1(struct mac_context *mac, + uint8_t nChannelNum, + tDot11fIEExtSuppRates *pDot11f) +{ + qdf_size_t nRates; + QDF_STATUS nsir_status; + uint8_t rates[SIR_MAC_MAX_NUMBER_OF_RATES]; + + if (14 < nChannelNum) { + pDot11f->present = 0; + return QDF_STATUS_SUCCESS; + } + /* N.B. I have *no* idea why we're calling 'wlan_cfg_get_str' with an argument */ + /* of WNI_CFG_SUPPORTED_RATES_11A here, but that's what was done */ + /* previously & I'm afraid to change it! */ + nRates = SIR_MAC_MAX_NUMBER_OF_RATES; + nsir_status = wlan_mlme_get_cfg_str( + rates, + &mac->mlme_cfg->rates.supported_11a, + &nRates); + if (QDF_IS_STATUS_ERROR(nsir_status)) { + pe_err("Failed to retrieve nItem from CFG status: %d", + (nsir_status)); + return nsir_status; + } + + if (0 != nRates) { + pDot11f->num_rates = (uint8_t) nRates; + qdf_mem_copy(pDot11f->rates, rates, nRates); + pDot11f->present = 1; + } + + return QDF_STATUS_SUCCESS; +} /* populate_dot11f_ext_supp_rates1. */ + +QDF_STATUS +populate_dot11f_ht_caps(struct mac_context *mac, + struct pe_session *pe_session, tDot11fIEHTCaps *pDot11f) +{ + qdf_size_t ncfglen; + QDF_STATUS nSirStatus; + uint8_t disable_high_ht_mcs_2x2 = 0; + + tSirMacTxBFCapabilityInfo *pTxBFCapabilityInfo; + tSirMacASCapabilityInfo *pASCapabilityInfo; + struct mlme_ht_capabilities_info *ht_cap_info; + struct mlme_vht_capabilities_info *vht_cap_info; + + ht_cap_info = &mac->mlme_cfg->ht_caps.ht_cap_info; + vht_cap_info = &mac->mlme_cfg->vht_caps.vht_cap_info; + + pDot11f->mimoPowerSave = ht_cap_info->mimo_power_save; + pDot11f->greenField = ht_cap_info->green_field; + pDot11f->delayedBA = ht_cap_info->delayed_ba; + pDot11f->maximalAMSDUsize = ht_cap_info->maximal_amsdu_size; + pDot11f->dsssCckMode40MHz = ht_cap_info->dsss_cck_mode_40_mhz; + pDot11f->psmp = ht_cap_info->psmp; + pDot11f->stbcControlFrame = ht_cap_info->stbc_control_frame; + pDot11f->lsigTXOPProtection = ht_cap_info->l_sig_tx_op_protection; + + /* All sessionized entries will need the check below */ + if (!pe_session) { /* Only in case of NO session */ + pDot11f->supportedChannelWidthSet = + ht_cap_info->supported_channel_width_set; + pDot11f->advCodingCap = ht_cap_info->adv_coding_cap; + pDot11f->txSTBC = ht_cap_info->tx_stbc; + pDot11f->rxSTBC = ht_cap_info->rx_stbc; + pDot11f->shortGI20MHz = ht_cap_info->short_gi_20_mhz; + pDot11f->shortGI40MHz = ht_cap_info->short_gi_40_mhz; + } else { + pDot11f->advCodingCap = pe_session->ht_config.ht_rx_ldpc; + pDot11f->supportedChannelWidthSet = + pe_session->htSupportedChannelWidthSet; + pDot11f->txSTBC = pe_session->ht_config.ht_tx_stbc; + pDot11f->rxSTBC = pe_session->ht_config.ht_rx_stbc; + pDot11f->shortGI20MHz = pe_session->ht_config.ht_sgi20; + pDot11f->shortGI40MHz = pe_session->ht_config.ht_sgi40; + } + + /* Ensure that shortGI40MHz is Disabled if supportedChannelWidthSet is + eHT_CHANNEL_WIDTH_20MHZ */ + if (pDot11f->supportedChannelWidthSet == eHT_CHANNEL_WIDTH_20MHZ) { + pDot11f->shortGI40MHz = 0; + } + + pDot11f->maxRxAMPDUFactor = + mac->mlme_cfg->ht_caps.ampdu_params.max_rx_ampdu_factor; + pDot11f->mpduDensity = + mac->mlme_cfg->ht_caps.ampdu_params.mpdu_density; + pDot11f->reserved1 = mac->mlme_cfg->ht_caps.ampdu_params.reserved; + + ncfglen = SIZE_OF_SUPPORTED_MCS_SET; + nSirStatus = wlan_mlme_get_cfg_str( + pDot11f->supportedMCSSet, + &mac->mlme_cfg->rates.supported_mcs_set, + &ncfglen); + if (QDF_IS_STATUS_ERROR(nSirStatus)) { + pe_err("Failed to retrieve nItem from CFG status: %d", + (nSirStatus)); + return nSirStatus; + } + + if (pe_session) { + disable_high_ht_mcs_2x2 = + mac->mlme_cfg->rates.disable_high_ht_mcs_2x2; + if (pe_session->nss == NSS_1x1_MODE) { + pDot11f->supportedMCSSet[1] = 0; + } else if (wlan_reg_is_24ghz_ch_freq( + pe_session->curr_op_freq) && + disable_high_ht_mcs_2x2 && + (pe_session->opmode == QDF_STA_MODE)) { + pe_debug("Disabling high HT MCS [%d]", + disable_high_ht_mcs_2x2); + pDot11f->supportedMCSSet[1] = + (pDot11f->supportedMCSSet[1] >> + disable_high_ht_mcs_2x2); + } + } + + /* If STA mode, session supported NSS > 1 and + * SMPS enabled publish HT SMPS IE + */ + if (pe_session && + LIM_IS_STA_ROLE(pe_session) && + (pe_session->enableHtSmps) && + (!pe_session->supported_nss_1x1)) { + pe_debug("Add SM power save IE: %d", + pe_session->htSmpsvalue); + pDot11f->mimoPowerSave = pe_session->htSmpsvalue; + } + + pDot11f->pco = mac->mlme_cfg->ht_caps.ext_cap_info.pco; + pDot11f->transitionTime = + mac->mlme_cfg->ht_caps.ext_cap_info.transition_time; + pDot11f->mcsFeedback = + mac->mlme_cfg->ht_caps.ext_cap_info.mcs_feedback; + + pTxBFCapabilityInfo = + (tSirMacTxBFCapabilityInfo *)&vht_cap_info->tx_bf_cap; + pDot11f->txBF = pTxBFCapabilityInfo->txBF; + pDot11f->rxStaggeredSounding = pTxBFCapabilityInfo->rxStaggeredSounding; + pDot11f->txStaggeredSounding = pTxBFCapabilityInfo->txStaggeredSounding; + pDot11f->rxZLF = pTxBFCapabilityInfo->rxZLF; + pDot11f->txZLF = pTxBFCapabilityInfo->txZLF; + pDot11f->implicitTxBF = pTxBFCapabilityInfo->implicitTxBF; + pDot11f->calibration = pTxBFCapabilityInfo->calibration; + pDot11f->explicitCSITxBF = pTxBFCapabilityInfo->explicitCSITxBF; + pDot11f->explicitUncompressedSteeringMatrix = + pTxBFCapabilityInfo->explicitUncompressedSteeringMatrix; + pDot11f->explicitBFCSIFeedback = + pTxBFCapabilityInfo->explicitBFCSIFeedback; + pDot11f->explicitUncompressedSteeringMatrixFeedback = + pTxBFCapabilityInfo->explicitUncompressedSteeringMatrixFeedback; + pDot11f->explicitCompressedSteeringMatrixFeedback = + pTxBFCapabilityInfo->explicitCompressedSteeringMatrixFeedback; + pDot11f->csiNumBFAntennae = pTxBFCapabilityInfo->csiNumBFAntennae; + pDot11f->uncompressedSteeringMatrixBFAntennae = + pTxBFCapabilityInfo->uncompressedSteeringMatrixBFAntennae; + pDot11f->compressedSteeringMatrixBFAntennae = + pTxBFCapabilityInfo->compressedSteeringMatrixBFAntennae; + + pASCapabilityInfo = (tSirMacASCapabilityInfo *)&vht_cap_info->as_cap; + pDot11f->antennaSelection = pASCapabilityInfo->antennaSelection; + pDot11f->explicitCSIFeedbackTx = + pASCapabilityInfo->explicitCSIFeedbackTx; + pDot11f->antennaIndicesFeedbackTx = + pASCapabilityInfo->antennaIndicesFeedbackTx; + pDot11f->explicitCSIFeedback = pASCapabilityInfo->explicitCSIFeedback; + pDot11f->antennaIndicesFeedback = + pASCapabilityInfo->antennaIndicesFeedback; + pDot11f->rxAS = pASCapabilityInfo->rxAS; + pDot11f->txSoundingPPDUs = pASCapabilityInfo->txSoundingPPDUs; + + pDot11f->present = 1; + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_ht_caps. */ + +void lim_log_vht_cap(struct mac_context *mac, tDot11fIEVHTCaps *pDot11f) +{ +#ifdef DUMP_MGMT_CNTNTS + pe_debug("maxMPDULen (2): %d", pDot11f->maxMPDULen); + pe_debug("supportedChannelWidthSet (2): %d", + pDot11f->supportedChannelWidthSet); + pe_debug("ldpcCodingCap (1): %d", + pDot11f->ldpcCodingCap); + pe_debug("shortGI80MHz (1): %d", pDot11f->shortGI80MHz); + pe_debug("shortGI160and80plus80MHz (1): %d", + pDot11f->shortGI160and80plus80MHz); + pe_debug("txSTBC (1): %d", pDot11f->txSTBC); + pe_debug("rxSTBC (3): %d", pDot11f->rxSTBC); + pe_debug("suBeamFormerCap (1): %d", + pDot11f->suBeamFormerCap); + pe_debug("suBeamformeeCap (1): %d", + pDot11f->suBeamformeeCap); + pe_debug("csnofBeamformerAntSup (3): %d", + pDot11f->csnofBeamformerAntSup); + pe_debug("numSoundingDim (3): %d", + pDot11f->numSoundingDim); + pe_debug("muBeamformerCap (1): %d", + pDot11f->muBeamformerCap); + pe_debug("muBeamformeeCap (1): %d", + pDot11f->muBeamformeeCap); + pe_debug("vhtTXOPPS (1): %d", pDot11f->vhtTXOPPS); + pe_debug("htcVHTCap (1): %d", pDot11f->htcVHTCap); + pe_debug("maxAMPDULenExp (3): %d", + pDot11f->maxAMPDULenExp); + pe_debug("vhtLinkAdaptCap (2): %d", + pDot11f->vhtLinkAdaptCap); + pe_debug("rxAntPattern (1): %d", + pDot11f->rxAntPattern; + pe_debug("txAntPattern (1): %d", + pDot11f->txAntPattern); + pe_debug("reserved1 (2): %d", pDot11f->reserved1); + pe_debug("rxMCSMap (16): %d", pDot11f->rxMCSMap); + pe_debug("rxHighSupDataRate (13): %d", + pDot11f->rxHighSupDataRate); + pe_debug("reserved2(3): %d", pDot11f->reserved2); + pe_debug("txMCSMap (16): %d", pDot11f->txMCSMap); + pe_debug("txSupDataRate (13): %d"), + pDot11f->txSupDataRate; + pe_debug("reserved3 (3): %d", pDot11f->reserved3); +#endif /* DUMP_MGMT_CNTNTS */ +} + +static void lim_log_vht_operation(struct mac_context *mac, + tDot11fIEVHTOperation *pDot11f) +{ +#ifdef DUMP_MGMT_CNTNTS + pe_debug("chanWidth: %d", pDot11f->chanWidth); + pe_debug("chan_center_freq_seg0: %d", + pDot11f->chan_center_freq_seg0); + pe_debug("chan_center_freq_seg1: %d", + pDot11f->chan_center_freq_seg1); + pe_debug("basicMCSSet: %d", pDot11f->basicMCSSet); +#endif /* DUMP_MGMT_CNTNTS */ +} + +static void lim_log_operating_mode(struct mac_context *mac, + tDot11fIEOperatingMode *pDot11f) +{ +#ifdef DUMP_MGMT_CNTNTS + pe_debug("ChanWidth: %d", pDot11f->chanWidth); + pe_debug("reserved: %d", pDot11f->reserved); + pe_debug("rxNSS: %d", pDot11f->rxNSS); + pe_debug("rxNSS Type: %d", pDot11f->rxNSSType); +#endif /* DUMP_MGMT_CNTNTS */ +} + +static void lim_log_qos_map_set(struct mac_context *mac, + struct qos_map_set *pQosMapSet) +{ + uint8_t i; + + if (pQosMapSet->num_dscp_exceptions > QOS_MAP_MAX_EX) + pQosMapSet->num_dscp_exceptions = QOS_MAP_MAX_EX; + + pe_debug("num of dscp exceptions: %d", + pQosMapSet->num_dscp_exceptions); + for (i = 0; i < pQosMapSet->num_dscp_exceptions; i++) + pe_nofl_debug("dscp value: %d, User priority value: %d", + pQosMapSet->dscp_exceptions[i][0], + pQosMapSet->dscp_exceptions[i][1]); + + for (i = 0; i < 8; i++) + pe_nofl_debug("For up %d: dscp low: %d, dscp high: %d", i, + pQosMapSet->dscp_range[i][0], + pQosMapSet->dscp_range[i][1]); +} + +QDF_STATUS +populate_dot11f_vht_caps(struct mac_context *mac, + struct pe_session *pe_session, tDot11fIEVHTCaps *pDot11f) +{ + uint32_t nCfgValue = 0; + struct mlme_vht_capabilities_info *vht_cap_info; + + if (!(mac->mlme_cfg)) { + pe_err("invalid mlme cfg"); + return QDF_STATUS_E_FAILURE; + } + vht_cap_info = &mac->mlme_cfg->vht_caps.vht_cap_info; + + pDot11f->present = 1; + + nCfgValue = vht_cap_info->ampdu_len; + pDot11f->maxMPDULen = (nCfgValue & 0x0003); + + nCfgValue = vht_cap_info->supp_chan_width; + pDot11f->supportedChannelWidthSet = (nCfgValue & 0x0003); + + nCfgValue = 0; + /* With VHT it suffices if we just examine HT */ + if (pe_session) { + if (lim_is_he_6ghz_band(pe_session)) { + pDot11f->present = 0; + return QDF_STATUS_SUCCESS; + } + + if (pe_session->ht_config.ht_rx_ldpc) + pDot11f->ldpcCodingCap = + pe_session->vht_config.ldpc_coding; + if (pe_session->ch_width < CH_WIDTH_80MHZ) { + pDot11f->shortGI80MHz = 0; + } else { + pDot11f->shortGI80MHz = + pe_session->vht_config.shortgi80; + } + + if (pe_session->ch_width < CH_WIDTH_160MHZ) { + pDot11f->shortGI160and80plus80MHz = 0; + pDot11f->supportedChannelWidthSet = 0; + } else { + pDot11f->shortGI160and80plus80MHz = + pe_session->vht_config.shortgi160and80plus80; + } + + if (pe_session->ht_config.ht_tx_stbc) + pDot11f->txSTBC = pe_session->vht_config.tx_stbc; + + if (pe_session->ht_config.ht_rx_stbc) + pDot11f->rxSTBC = pe_session->vht_config.rx_stbc; + + pDot11f->suBeamformeeCap = + pe_session->vht_config.su_beam_formee; + if (pe_session->vht_config.su_beam_formee) { + pDot11f->muBeamformeeCap = + pe_session->vht_config.mu_beam_formee; + pDot11f->csnofBeamformerAntSup = + pe_session->vht_config.csnof_beamformer_antSup; + } else { + pDot11f->muBeamformeeCap = 0; + } + pDot11f->suBeamFormerCap = + pe_session->vht_config.su_beam_former; + + pDot11f->vhtTXOPPS = pe_session->vht_config.vht_txops; + + pDot11f->numSoundingDim = + pe_session->vht_config.num_soundingdim; + + pDot11f->htcVHTCap = pe_session->vht_config.htc_vhtcap; + + pDot11f->rxAntPattern = pe_session->vht_config.rx_antpattern; + + pDot11f->txAntPattern = pe_session->vht_config.tx_antpattern; + pDot11f->extended_nss_bw_supp = + pe_session->vht_config.extended_nss_bw_supp; + + pDot11f->maxAMPDULenExp = + pe_session->vht_config.max_ampdu_lenexp; + + pDot11f->vhtLinkAdaptCap = + pe_session->vht_config.vht_link_adapt; + } else { + nCfgValue = vht_cap_info->ldpc_coding_cap; + pDot11f->ldpcCodingCap = (nCfgValue & 0x0001); + + nCfgValue = vht_cap_info->short_gi_80mhz; + pDot11f->shortGI80MHz = (nCfgValue & 0x0001); + + nCfgValue = vht_cap_info->short_gi_160mhz; + pDot11f->shortGI160and80plus80MHz = (nCfgValue & 0x0001); + + nCfgValue = vht_cap_info->tx_stbc; + pDot11f->txSTBC = (nCfgValue & 0x0001); + + nCfgValue = vht_cap_info->rx_stbc; + pDot11f->rxSTBC = (nCfgValue & 0x0007); + + nCfgValue = vht_cap_info->su_bformee; + pDot11f->suBeamformeeCap = (nCfgValue & 0x0001); + + nCfgValue = vht_cap_info->enable_mu_bformee; + pDot11f->muBeamformeeCap = (nCfgValue & 0x0001); + + nCfgValue = vht_cap_info->su_bformer; + pDot11f->suBeamFormerCap = (nCfgValue & 0x0001); + + nCfgValue = vht_cap_info->tx_bfee_ant_supp; + pDot11f->csnofBeamformerAntSup = (nCfgValue & 0x0007); + + nCfgValue = vht_cap_info->txop_ps; + pDot11f->vhtTXOPPS = (nCfgValue & 0x0001); + + nCfgValue = vht_cap_info->num_soundingdim; + pDot11f->numSoundingDim = (nCfgValue & 0x0007); + + nCfgValue = vht_cap_info->htc_vhtc; + pDot11f->htcVHTCap = (nCfgValue & 0x0001); + + pDot11f->rxAntPattern = vht_cap_info->rx_antpattern; + + pDot11f->txAntPattern = vht_cap_info->tx_antpattern; + + nCfgValue = vht_cap_info->ampdu_len_exponent; + pDot11f->maxAMPDULenExp = (nCfgValue & 0x0007); + + nCfgValue = vht_cap_info->link_adap_cap; + pDot11f->vhtLinkAdaptCap = (nCfgValue & 0x0003); + + pDot11f->extended_nss_bw_supp = + vht_cap_info->extended_nss_bw_supp; + } + + pDot11f->max_nsts_total = vht_cap_info->max_nsts_total; + pDot11f->vht_extended_nss_bw_cap = + vht_cap_info->vht_extended_nss_bw_cap; + + nCfgValue = vht_cap_info->mu_bformer; + pDot11f->muBeamformerCap = (nCfgValue & 0x0001); + + + nCfgValue = vht_cap_info->rx_mcs_map; + pDot11f->rxMCSMap = (nCfgValue & 0x0000FFFF); + + nCfgValue = vht_cap_info->rx_supp_data_rate; + pDot11f->rxHighSupDataRate = (nCfgValue & 0x00001FFF); + + nCfgValue = vht_cap_info->tx_mcs_map; + pDot11f->txMCSMap = (nCfgValue & 0x0000FFFF); + + nCfgValue = vht_cap_info->tx_supp_data_rate; + pDot11f->txSupDataRate = (nCfgValue & 0x00001FFF); + + if (pe_session) { + if (pe_session->nss == NSS_1x1_MODE) { + pDot11f->txMCSMap |= DISABLE_NSS2_MCS; + pDot11f->rxMCSMap |= DISABLE_NSS2_MCS; + pDot11f->txSupDataRate = + VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + pDot11f->rxHighSupDataRate = + VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1; + if (!pe_session->ch_width && + !vht_cap_info->enable_vht20_mcs9 && + ((pDot11f->txMCSMap & VHT_1x1_MCS_MASK) == + VHT_1x1_MCS9_MAP)) { + DISABLE_VHT_MCS_9(pDot11f->txMCSMap, + NSS_1x1_MODE); + DISABLE_VHT_MCS_9(pDot11f->rxMCSMap, + NSS_1x1_MODE); + } + } else { + if (!pe_session->ch_width && + !vht_cap_info->enable_vht20_mcs9 && + ((pDot11f->txMCSMap & VHT_2x2_MCS_MASK) == + VHT_2x2_MCS9_MAP)) { + DISABLE_VHT_MCS_9(pDot11f->txMCSMap, + NSS_2x2_MODE); + DISABLE_VHT_MCS_9(pDot11f->rxMCSMap, + NSS_2x2_MODE); + } + } + } + lim_log_vht_cap(mac, pDot11f); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +populate_dot11f_vht_operation(struct mac_context *mac, + struct pe_session *pe_session, + tDot11fIEVHTOperation *pDot11f) +{ + if (!pe_session || !pe_session->vhtCapability) + return QDF_STATUS_SUCCESS; + + pDot11f->present = 1; + + if (pe_session->ch_width > CH_WIDTH_40MHZ) { + pDot11f->chanWidth = 1; + pDot11f->chan_center_freq_seg0 = + pe_session->ch_center_freq_seg0; + if (pe_session->ch_width == CH_WIDTH_80P80MHZ || + pe_session->ch_width == CH_WIDTH_160MHZ) + pDot11f->chan_center_freq_seg1 = + pe_session->ch_center_freq_seg1; + else + pDot11f->chan_center_freq_seg1 = 0; + } else { + pDot11f->chanWidth = 0; + pDot11f->chan_center_freq_seg0 = 0; + pDot11f->chan_center_freq_seg1 = 0; + } + + pDot11f->basicMCSSet = + (uint16_t)mac->mlme_cfg->vht_caps.vht_cap_info.basic_mcs_set; + + lim_log_vht_operation(mac, pDot11f); + + return QDF_STATUS_SUCCESS; + +} + +QDF_STATUS +populate_dot11f_ext_cap(struct mac_context *mac, + bool isVHTEnabled, tDot11fIEExtCap *pDot11f, + struct pe_session *pe_session) +{ + struct s_ext_cap *p_ext_cap; + + pDot11f->present = 1; + + if (!pe_session) { + pe_debug("11MC - enabled for non-SAP cases"); + pDot11f->num_bytes = DOT11F_IE_EXTCAP_MAX_LEN; + } else if (pe_session->sap_dot11mc) { + pe_debug("11MC support enabled"); + pDot11f->num_bytes = DOT11F_IE_EXTCAP_MAX_LEN; + } else { + if (eLIM_AP_ROLE != pe_session->limSystemRole) + pDot11f->num_bytes = DOT11F_IE_EXTCAP_MAX_LEN; + else + pDot11f->num_bytes = DOT11F_IE_EXTCAP_MIN_LEN; + } + + p_ext_cap = (struct s_ext_cap *)pDot11f->bytes; + if (isVHTEnabled == true) + p_ext_cap->oper_mode_notification = 1; + + if (mac->mlme_cfg->gen.rtt3_enabled) { + uint32_t ftm = ucfg_wifi_pos_get_ftm_cap(mac->psoc); + if (!pe_session || LIM_IS_STA_ROLE(pe_session)) { + p_ext_cap->fine_time_meas_initiator = + (ftm & WMI_FW_STA_RTT_INITR) ? 1 : 0; + p_ext_cap->fine_time_meas_responder = + (ftm & WMI_FW_STA_RTT_RESPR) ? 1 : 0; + } else if (LIM_IS_AP_ROLE(pe_session)) { + p_ext_cap->fine_time_meas_initiator = + (ftm & WMI_FW_AP_RTT_INITR) ? 1 : 0; + p_ext_cap->fine_time_meas_responder = + (ftm & WMI_FW_AP_RTT_RESPR) ? 1 : 0; + } + } +#ifdef QCA_HT_2040_COEX + if (mac->roam.configParam.obssEnabled) + p_ext_cap->bss_coexist_mgmt_support = 1; +#endif + p_ext_cap->ext_chan_switch = 1; + + if (pe_session && pe_session->enable_bcast_probe_rsp) + p_ext_cap->fils_capability = 1; + + if (pe_session && pe_session->is_mbssid_enabled) + p_ext_cap->multi_bssid = 1; + + /* Need to calculate the num_bytes based on bits set */ + if (pDot11f->present) + pDot11f->num_bytes = lim_compute_ext_cap_ie_length(pDot11f); + + return QDF_STATUS_SUCCESS; +} + +void populate_dot11f_qcn_ie(struct mac_context *mac, + tDot11fIEqcn_ie *qcn_ie, + uint8_t attr_id) +{ + qcn_ie->present = 0; + if (mac->mlme_cfg->sta.qcn_ie_support && + ((attr_id == QCN_IE_ATTR_ID_ALL) || + (attr_id == QCN_IE_ATTR_ID_VERSION))) { + qcn_ie->present = 1; + qcn_ie->version_attr.present = 1; + qcn_ie->version_attr.version = QCN_IE_VERSION_SUPPORTED; + qcn_ie->version_attr.sub_version = QCN_IE_SUBVERSION_SUPPORTED; + } + if (mac->mlme_cfg->vht_caps.vht_cap_info.vht_mcs_10_11_supp) { + qcn_ie->present = 1; + qcn_ie->vht_mcs11_attr.present = 1; + qcn_ie->vht_mcs11_attr.vht_mcs_10_11_supp = 1; + } +} + +QDF_STATUS +populate_dot11f_operating_mode(struct mac_context *mac, + tDot11fIEOperatingMode *pDot11f, + struct pe_session *pe_session) +{ + pDot11f->present = 1; + + pDot11f->chanWidth = pe_session->gLimOperatingMode.chanWidth; + pDot11f->rxNSS = pe_session->gLimOperatingMode.rxNSS; + pDot11f->rxNSSType = pe_session->gLimOperatingMode.rxNSSType; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +populate_dot11f_ht_info(struct mac_context *mac, + tDot11fIEHTInfo *pDot11f, struct pe_session *pe_session) +{ + qdf_size_t ncfglen; + QDF_STATUS nSirStatus; + + if (!pe_session) { + pe_err("Invalid session entry"); + return QDF_STATUS_E_FAILURE; + } + + pDot11f->primaryChannel = wlan_reg_freq_to_chan( + mac->pdev, pe_session->curr_op_freq); + + pDot11f->secondaryChannelOffset = + pe_session->htSecondaryChannelOffset; + pDot11f->recommendedTxWidthSet = + pe_session->htRecommendedTxWidthSet; + pDot11f->rifsMode = pe_session->beaconParams.fRIFSMode; + pDot11f->controlledAccessOnly = + mac->mlme_cfg->ht_caps.info_field_1.controlled_access_only; + pDot11f->serviceIntervalGranularity = + mac->lim.gHTServiceIntervalGranularity; + + if (LIM_IS_AP_ROLE(pe_session)) { + pDot11f->opMode = pe_session->htOperMode; + pDot11f->nonGFDevicesPresent = + pe_session->beaconParams.llnNonGFCoexist; + pDot11f->obssNonHTStaPresent = + pe_session->beaconParams.gHTObssMode; + pDot11f->reserved = 0; + } else { + pDot11f->opMode = 0; + pDot11f->nonGFDevicesPresent = 0; + pDot11f->obssNonHTStaPresent = 0; + pDot11f->reserved = 0; + } + + pDot11f->basicSTBCMCS = mac->lim.gHTSTBCBasicMCS; + pDot11f->dualCTSProtection = mac->lim.gHTDualCTSProtection; + pDot11f->secondaryBeacon = mac->lim.gHTSecondaryBeacon; + pDot11f->lsigTXOPProtectionFullSupport = + pe_session->beaconParams.fLsigTXOPProtectionFullSupport; + pDot11f->pcoActive = mac->lim.gHTPCOActive; + pDot11f->pcoPhase = mac->lim.gHTPCOPhase; + pDot11f->reserved2 = 0; + + ncfglen = SIZE_OF_BASIC_MCS_SET; + nSirStatus = wlan_mlme_get_cfg_str(pDot11f->basicMCSSet, + &mac->mlme_cfg->rates.basic_mcs_set, + &ncfglen); + if (QDF_IS_STATUS_ERROR(nSirStatus)) { + pe_err("Failed to retrieve nItem from CFG status: %d", + (nSirStatus)); + return nSirStatus; + } + + pDot11f->present = 1; + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_ht_info. */ + +void +populate_dot11f_ibss_params(struct mac_context *mac, + tDot11fIEIBSSParams *pDot11f, + struct pe_session *pe_session) +{ + if (LIM_IS_IBSS_ROLE(pe_session)) { + pDot11f->atim = mac->mlme_cfg->ibss.atim_win_size; + pDot11f->present = 1; + } +} /* End populate_dot11f_ibss_params. */ + +#ifdef ANI_SUPPORT_11H +QDF_STATUS +populate_dot11f_measurement_report0(struct mac_context *mac, + tpSirMacMeasReqActionFrame pReq, + tDot11fIEMeasurementReport *pDot11f) +{ + pDot11f->token = pReq->measReqIE.measToken; + pDot11f->late = 0; + pDot11f->incapable = 0; + pDot11f->refused = 1; + pDot11f->type = SIR_MAC_BASIC_MEASUREMENT_TYPE; + + pDot11f->present = 1; + + return QDF_STATUS_SUCCESS; + +} /* End PopulatedDot11fMeasurementReport0. */ +QDF_STATUS +populate_dot11f_measurement_report1(struct mac_context *mac, + tpSirMacMeasReqActionFrame pReq, + tDot11fIEMeasurementReport *pDot11f) +{ + pDot11f->token = pReq->measReqIE.measToken; + pDot11f->late = 0; + pDot11f->incapable = 0; + pDot11f->refused = 1; + pDot11f->type = SIR_MAC_CCA_MEASUREMENT_TYPE; + pDot11f->present = 1; + return QDF_STATUS_SUCCESS; +} /* End PopulatedDot11fMeasurementReport1. */ +QDF_STATUS +populate_dot11f_measurement_report2(struct mac_context *mac, + tpSirMacMeasReqActionFrame pReq, + tDot11fIEMeasurementReport *pDot11f) +{ + pDot11f->token = pReq->measReqIE.measToken; + pDot11f->late = 0; + pDot11f->incapable = 0; + pDot11f->refused = 1; + pDot11f->type = SIR_MAC_RPI_MEASUREMENT_TYPE; + pDot11f->present = 1; + return QDF_STATUS_SUCCESS; +} /* End PopulatedDot11fMeasurementReport2. */ +#endif + +void +populate_dot11f_power_caps(struct mac_context *mac, + tDot11fIEPowerCaps *pCaps, + uint8_t nAssocType, struct pe_session *pe_session) +{ + struct vdev_mlme_obj *mlme_obj; + + if (nAssocType == LIM_REASSOC) { + pCaps->minTxPower = + pe_session->pLimReAssocReq->powerCap.minTxPower; + pCaps->maxTxPower = + pe_session->pLimReAssocReq->powerCap.maxTxPower; + } else { + pCaps->minTxPower = + pe_session->lim_join_req->powerCap.minTxPower; + pCaps->maxTxPower = + pe_session->lim_join_req->powerCap.maxTxPower; + + } + + /* Use firmware updated max tx power if non zero */ + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(pe_session->vdev); + if (mlme_obj && mlme_obj->mgmt.generic.tx_pwrlimit) + pCaps->maxTxPower = mlme_obj->mgmt.generic.tx_pwrlimit; + + pCaps->present = 1; +} /* End populate_dot11f_power_caps. */ + +QDF_STATUS +populate_dot11f_power_constraints(struct mac_context *mac, + tDot11fIEPowerConstraints *pDot11f) +{ + pDot11f->localPowerConstraints = + mac->mlme_cfg->power.local_power_constraint; + + pDot11f->present = 1; + + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_power_constraints. */ + +void +populate_dot11f_qos_caps_station(struct mac_context *mac, struct pe_session *pe_session, + tDot11fIEQOSCapsStation *pDot11f) +{ + uint8_t max_sp_length = 0; + + max_sp_length = mac->mlme_cfg->wmm_params.max_sp_length; + + pDot11f->more_data_ack = 0; + pDot11f->max_sp_length = max_sp_length; + pDot11f->qack = 0; + + if (mac->lim.gUapsdEnable) { + pDot11f->acbe_uapsd = + LIM_UAPSD_GET(ACBE, pe_session->gUapsdPerAcBitmask); + pDot11f->acbk_uapsd = + LIM_UAPSD_GET(ACBK, pe_session->gUapsdPerAcBitmask); + pDot11f->acvi_uapsd = + LIM_UAPSD_GET(ACVI, pe_session->gUapsdPerAcBitmask); + pDot11f->acvo_uapsd = + LIM_UAPSD_GET(ACVO, pe_session->gUapsdPerAcBitmask); + } + pDot11f->present = 1; +} /* End PopulatedDot11fQOSCaps. */ + +QDF_STATUS +populate_dot11f_rsn(struct mac_context *mac, + tpSirRSNie pRsnIe, tDot11fIERSN *pDot11f) +{ + uint32_t status; + int idx; + + if (pRsnIe->length) { + idx = find_ie_location(mac, pRsnIe, DOT11F_EID_RSN); + if (0 <= idx) { + status = dot11f_unpack_ie_rsn(mac, pRsnIe->rsnIEdata + idx + 2, /* EID, length */ + pRsnIe->rsnIEdata[idx + 1], + pDot11f, false); + if (DOT11F_FAILED(status)) { + pe_err("Parse failure in Populate Dot11fRSN (0x%08x)", + status); + return QDF_STATUS_E_FAILURE; + } + pe_debug("status 0x%08x", status); + } + + } + + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_rsn. */ + +QDF_STATUS populate_dot11f_rsn_opaque(struct mac_context *mac, + tpSirRSNie pRsnIe, + tDot11fIERSNOpaque *pDot11f) +{ + int idx; + + if (pRsnIe->length) { + idx = find_ie_location(mac, pRsnIe, DOT11F_EID_RSN); + if (0 <= idx) { + pDot11f->present = 1; + pDot11f->num_data = pRsnIe->rsnIEdata[idx + 1]; + qdf_mem_copy(pDot11f->data, pRsnIe->rsnIEdata + idx + 2, /* EID, len */ + pRsnIe->rsnIEdata[idx + 1]); + } + } + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_rsn_opaque. */ + +#if defined(FEATURE_WLAN_WAPI) + +QDF_STATUS +populate_dot11f_wapi(struct mac_context *mac, + tpSirRSNie pRsnIe, tDot11fIEWAPI *pDot11f) +{ + uint32_t status; + int idx; + + if (pRsnIe->length) { + idx = find_ie_location(mac, pRsnIe, DOT11F_EID_WAPI); + if (0 <= idx) { + status = dot11f_unpack_ie_wapi(mac, pRsnIe->rsnIEdata + idx + 2, /* EID, length */ + pRsnIe->rsnIEdata[idx + 1], + pDot11f, false); + if (DOT11F_FAILED(status)) { + pe_err("Parse failure (0x%08x)", status); + return QDF_STATUS_E_FAILURE; + } + pe_debug("status 0x%08x", status); + } + } + + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_wapi. */ + +QDF_STATUS populate_dot11f_wapi_opaque(struct mac_context *mac, + tpSirRSNie pRsnIe, + tDot11fIEWAPIOpaque *pDot11f) +{ + int idx; + + if (pRsnIe->length) { + idx = find_ie_location(mac, pRsnIe, DOT11F_EID_WAPI); + if (0 <= idx) { + pDot11f->present = 1; + pDot11f->num_data = pRsnIe->rsnIEdata[idx + 1]; + qdf_mem_copy(pDot11f->data, pRsnIe->rsnIEdata + idx + 2, /* EID, len */ + pRsnIe->rsnIEdata[idx + 1]); + } + } + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_wapi_opaque. */ + +#endif /* defined(FEATURE_WLAN_WAPI) */ + +void +populate_dot11f_ssid(struct mac_context *mac, + tSirMacSSid *pInternal, tDot11fIESSID *pDot11f) +{ + pDot11f->present = 1; + pDot11f->num_ssid = pInternal->length; + if (pInternal->length) { + qdf_mem_copy((uint8_t *) pDot11f->ssid, + (uint8_t *) &pInternal->ssId, pInternal->length); + } +} /* End populate_dot11f_ssid. */ + +QDF_STATUS populate_dot11f_ssid2(struct mac_context *mac, tDot11fIESSID *pDot11f) +{ + + qdf_mem_copy(pDot11f->ssid, mac->mlme_cfg->sap_cfg.cfg_ssid, + mac->mlme_cfg->sap_cfg.cfg_ssid_len); + pDot11f->num_ssid = mac->mlme_cfg->sap_cfg.cfg_ssid_len; + pDot11f->present = 1; + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_ssid2. */ + +void +populate_dot11f_schedule(tSirMacScheduleIE *pSchedule, + tDot11fIESchedule *pDot11f) +{ + pDot11f->aggregation = pSchedule->info.aggregation; + pDot11f->tsid = pSchedule->info.tsid; + pDot11f->direction = pSchedule->info.direction; + pDot11f->reserved = pSchedule->info.rsvd; + pDot11f->service_start_time = pSchedule->svcStartTime; + pDot11f->service_interval = pSchedule->svcInterval; + pDot11f->max_service_dur = pSchedule->maxSvcDuration; + pDot11f->spec_interval = pSchedule->specInterval; + + pDot11f->present = 1; +} /* End populate_dot11f_schedule. */ + +void +populate_dot11f_supp_channels(struct mac_context *mac, + tDot11fIESuppChannels *pDot11f, + uint8_t nAssocType, struct pe_session *pe_session) +{ + uint8_t i; + uint8_t *p; + + if (nAssocType == LIM_REASSOC) { + p = (uint8_t *) pe_session->pLimReAssocReq-> + supportedChannels.channelList; + pDot11f->num_bands = + pe_session->pLimReAssocReq->supportedChannels.numChnl; + } else { + p = (uint8_t *)pe_session->lim_join_req->supportedChannels. + channelList; + pDot11f->num_bands = + pe_session->lim_join_req->supportedChannels.numChnl; + } + for (i = 0U; i < pDot11f->num_bands; ++i, ++p) { + pDot11f->bands[i][0] = *p; + pDot11f->bands[i][1] = 1; + } + + pDot11f->present = 1; + +} /* End populate_dot11f_supp_channels. */ + +QDF_STATUS +populate_dot11f_supp_rates(struct mac_context *mac, + uint8_t nChannelNum, + tDot11fIESuppRates *pDot11f, + struct pe_session *pe_session) +{ + QDF_STATUS nsir_status; + qdf_size_t nRates; + uint8_t rates[SIR_MAC_MAX_NUMBER_OF_RATES]; + + /* Use the operational rates present in session entry whenever + * nChannelNum is set to OPERATIONAL else use the supported + * rate set from CFG, which is fixed and does not change + * dynamically and is used for sending mgmt frames (lile probe + * req) which need to go out before any session is present. + */ + if (POPULATE_DOT11F_RATES_OPERATIONAL == nChannelNum) { + if (pe_session) { + nRates = pe_session->rateSet.numRates; + qdf_mem_copy(rates, pe_session->rateSet.rate, + nRates); + } else { + pe_err("no session context exists while populating Operational Rate Set"); + nRates = 0; + } + } else if (14 >= nChannelNum) { + nRates = SIR_MAC_MAX_NUMBER_OF_RATES; + nsir_status = wlan_mlme_get_cfg_str( + rates, + &mac->mlme_cfg->rates.supported_11b, + &nRates); + if (QDF_IS_STATUS_ERROR(nsir_status)) { + pe_err("Failed to retrieve nItem from CFG status: %d", + (nsir_status)); + return nsir_status; + } + } else { + nRates = SIR_MAC_MAX_NUMBER_OF_RATES; + nsir_status = wlan_mlme_get_cfg_str( + rates, + &mac->mlme_cfg->rates.supported_11a, + &nRates); + if (QDF_IS_STATUS_ERROR(nsir_status)) { + pe_err("Failed to retrieve nItem from CFG status: %d", + (nsir_status)); + return nsir_status; + } + } + + if (0 != nRates) { + pDot11f->num_rates = (uint8_t) nRates; + qdf_mem_copy(pDot11f->rates, rates, nRates); + pDot11f->present = 1; + } + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_supp_rates. */ + +/** + * populate_dot11f_rates_tdls() - populate supported rates and + * extended supported rates IE. + * @p_mac gloabl - header. + * @p_supp_rates - pointer to supported rates IE + * @p_ext_supp_rates - pointer to extended supported rates IE + * @curr_oper_channel - current operating channel + * + * This function populates the supported rates and extended supported + * rates IE based in the STA capability. If the number of rates + * supported is less than MAX_NUM_SUPPORTED_RATES, only supported rates + * IE is populated. + * + * Return: QDF_STATUS QDF_STATUS_SUCCESS on Success and QDF_STATUS_E_FAILURE + * on failure. + */ + +QDF_STATUS +populate_dot11f_rates_tdls(struct mac_context *p_mac, + tDot11fIESuppRates *p_supp_rates, + tDot11fIEExtSuppRates *p_ext_supp_rates, + uint8_t curr_oper_channel) +{ + tSirMacRateSet temp_rateset; + tSirMacRateSet temp_rateset2; + uint32_t i; + uint32_t self_dot11mode = 0; + qdf_size_t num_rates; + + self_dot11mode = p_mac->mlme_cfg->dot11_mode.dot11_mode; + + /** + * Include 11b rates only when the device configured in + * auto, 11a/b/g or 11b_only and also if current base + * channel is 5 GHz then no need to advertise the 11b rates. + * If devices move to 2.4GHz off-channel then they can communicate + * in 11g rates i.e. (6, 9, 12, 18, 24, 36 and 54). + */ + pe_debug("Current operating channel %d self_dot11mode = %d", + curr_oper_channel, self_dot11mode); + + if ((curr_oper_channel <= SIR_11B_CHANNEL_END) && + ((self_dot11mode == MLME_DOT11_MODE_ALL) || + (self_dot11mode == MLME_DOT11_MODE_11A) || + (self_dot11mode == MLME_DOT11_MODE_11AC) || + (self_dot11mode == MLME_DOT11_MODE_11N) || + (self_dot11mode == MLME_DOT11_MODE_11G) || + (self_dot11mode == MLME_DOT11_MODE_11B))) { + num_rates = p_mac->mlme_cfg->rates.supported_11b.len; + wlan_mlme_get_cfg_str((uint8_t *)&temp_rateset.rate, + &p_mac->mlme_cfg->rates.supported_11b, + &num_rates); + temp_rateset.numRates = (uint8_t)num_rates; + } else { + temp_rateset.numRates = 0; + } + + /* Include 11a rates when the device configured in non-11b mode */ + if (!IS_DOT11_MODE_11B(self_dot11mode)) { + num_rates = p_mac->mlme_cfg->rates.supported_11a.len; + wlan_mlme_get_cfg_str((uint8_t *)&temp_rateset2.rate, + &p_mac->mlme_cfg->rates.supported_11a, + &num_rates); + temp_rateset2.numRates = (uint8_t)num_rates; + } else { + temp_rateset2.numRates = 0; + } + + if ((temp_rateset.numRates + temp_rateset2.numRates) > + SIR_MAC_MAX_NUMBER_OF_RATES) { + pe_err("more than %d rates in CFG", + SIR_MAC_MAX_NUMBER_OF_RATES); + return QDF_STATUS_E_FAILURE; + } + + /** + * copy all rates in temp_rateset, + * there are SIR_MAC_MAX_NUMBER_OF_RATES rates max + */ + for (i = 0; i < temp_rateset2.numRates; i++) + temp_rateset.rate[i + temp_rateset.numRates] = + temp_rateset2.rate[i]; + + temp_rateset.numRates += temp_rateset2.numRates; + + if (temp_rateset.numRates <= MAX_NUM_SUPPORTED_RATES) { + p_supp_rates->num_rates = temp_rateset.numRates; + qdf_mem_copy(p_supp_rates->rates, temp_rateset.rate, + p_supp_rates->num_rates); + p_supp_rates->present = 1; + } else { /* Populate extended capability as well */ + p_supp_rates->num_rates = MAX_NUM_SUPPORTED_RATES; + qdf_mem_copy(p_supp_rates->rates, temp_rateset.rate, + p_supp_rates->num_rates); + p_supp_rates->present = 1; + + p_ext_supp_rates->num_rates = temp_rateset.numRates - + MAX_NUM_SUPPORTED_RATES; + qdf_mem_copy(p_ext_supp_rates->rates, + (uint8_t *)temp_rateset.rate + + MAX_NUM_SUPPORTED_RATES, + p_ext_supp_rates->num_rates); + p_ext_supp_rates->present = 1; + } + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_rates_tdls */ + + +QDF_STATUS +populate_dot11f_tpc_report(struct mac_context *mac, + tDot11fIETPCReport *pDot11f, struct pe_session *pe_session) +{ + uint16_t staid; + uint8_t tx_power; + QDF_STATUS nSirStatus; + + nSirStatus = lim_get_mgmt_staid(mac, &staid, pe_session); + if (QDF_STATUS_SUCCESS != nSirStatus) { + pe_err("Failed to get the STAID in Populate Dot11fTPCReport; lim_get_mgmt_staid returned status %d", + nSirStatus); + return QDF_STATUS_E_FAILURE; + } + tx_power = wlan_reg_get_channel_reg_power_for_freq( + mac->pdev, pe_session->curr_op_freq); + pDot11f->tx_power = tx_power; + pDot11f->link_margin = 0; + pDot11f->present = 1; + + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_tpc_report. */ + +void populate_dot11f_ts_info(struct mac_ts_info *pInfo, + tDot11fFfTSInfo *pDot11f) +{ + pDot11f->traffic_type = pInfo->traffic.trafficType; + pDot11f->tsid = pInfo->traffic.tsid; + pDot11f->direction = pInfo->traffic.direction; + pDot11f->access_policy = pInfo->traffic.accessPolicy; + pDot11f->aggregation = pInfo->traffic.aggregation; + pDot11f->psb = pInfo->traffic.psb; + pDot11f->user_priority = pInfo->traffic.userPrio; + pDot11f->tsinfo_ack_pol = pInfo->traffic.ackPolicy; + pDot11f->schedule = pInfo->schedule.schedule; +} /* End PopulatedDot11fTSInfo. */ + +void populate_dot11f_wmm(struct mac_context *mac, + tDot11fIEWMMInfoAp *pInfo, + tDot11fIEWMMParams *pParams, + tDot11fIEWMMCaps *pCaps, struct pe_session *pe_session) +{ + if (pe_session->limWmeEnabled) { + if (LIM_IS_IBSS_ROLE(pe_session)) { + /* if ( ! sirIsPropCapabilityEnabled( mac, SIR_MAC_PROP_CAPABILITY_WME ) ) */ + { + populate_dot11f_wmm_info_ap(mac, pInfo, + pe_session); + } + } else { + { + populate_dot11f_wmm_params(mac, pParams, + pe_session); + } + + if (pe_session->limWsmEnabled) { + populate_dot11f_wmm_caps(pCaps); + } + } + } +} /* End populate_dot11f_wmm. */ + +void populate_dot11f_wmm_caps(tDot11fIEWMMCaps *pCaps) +{ + pCaps->version = SIR_MAC_OUI_VERSION_1; + pCaps->qack = 0; + pCaps->queue_request = 1; + pCaps->txop_request = 0; + pCaps->more_ack = 0; + pCaps->present = 1; +} /* End PopulateDot11fWmmCaps. */ + +#ifdef FEATURE_WLAN_ESE +void populate_dot11f_re_assoc_tspec(struct mac_context *mac, + tDot11fReAssocRequest *pReassoc, + struct pe_session *pe_session) +{ + uint8_t numTspecs = 0, idx; + tTspecInfo *pTspec = NULL; + + numTspecs = pe_session->pLimReAssocReq->eseTspecInfo.numTspecs; + pTspec = &pe_session->pLimReAssocReq->eseTspecInfo.tspec[0]; + pReassoc->num_WMMTSPEC = numTspecs; + if (numTspecs) { + for (idx = 0; idx < numTspecs; idx++) { + populate_dot11f_wmmtspec(&pTspec->tspec, + &pReassoc->WMMTSPEC[idx]); + pTspec->tspec.mediumTime = 0; + pTspec++; + } + } +} + +void ese_populate_wmm_tspec(struct mac_tspec_ie *source, + ese_wmm_tspec_ie *dest) +{ + dest->traffic_type = source->tsinfo.traffic.trafficType; + dest->tsid = source->tsinfo.traffic.tsid; + dest->direction = source->tsinfo.traffic.direction; + dest->access_policy = source->tsinfo.traffic.accessPolicy; + dest->aggregation = source->tsinfo.traffic.aggregation; + dest->psb = source->tsinfo.traffic.psb; + dest->user_priority = source->tsinfo.traffic.userPrio; + dest->tsinfo_ack_pol = source->tsinfo.traffic.ackPolicy; + dest->burst_size_defn = source->tsinfo.traffic.burstSizeDefn; + /* As defined in IEEE 802.11-2007, section 7.3.2.30 + * Nominal MSDU size: Bit[0:14]=Size, Bit[15]=Fixed + */ + dest->size = (source->nomMsduSz & SIZE_MASK); + dest->fixed = (source->nomMsduSz & FIXED_MASK) ? 1 : 0; + dest->max_msdu_size = source->maxMsduSz; + dest->min_service_int = source->minSvcInterval; + dest->max_service_int = source->maxSvcInterval; + dest->inactivity_int = source->inactInterval; + dest->suspension_int = source->suspendInterval; + dest->service_start_time = source->svcStartTime; + dest->min_data_rate = source->minDataRate; + dest->mean_data_rate = source->meanDataRate; + dest->peak_data_rate = source->peakDataRate; + dest->burst_size = source->maxBurstSz; + dest->delay_bound = source->delayBound; + dest->min_phy_rate = source->minPhyRate; + dest->surplus_bw_allowance = source->surplusBw; + dest->medium_time = source->mediumTime; +} + +#endif + +void populate_dot11f_wmm_info_ap(struct mac_context *mac, tDot11fIEWMMInfoAp *pInfo, + struct pe_session *pe_session) +{ + pInfo->version = SIR_MAC_OUI_VERSION_1; + + /* WMM Specification 3.1.3, 3.2.3 + * An IBSS station shall always use its default WMM parameters. + */ + if (LIM_IS_IBSS_ROLE(pe_session)) { + pInfo->param_set_count = 0; + pInfo->uapsd = 0; + } else { + pInfo->param_set_count = + (0xf & pe_session->gLimEdcaParamSetCount); + if (LIM_IS_AP_ROLE(pe_session)) { + pInfo->uapsd = (0x1 & pe_session->apUapsdEnable); + } else + pInfo->uapsd = (0x1 & mac->lim.gUapsdEnable); + } + pInfo->present = 1; +} + +void populate_dot11f_wmm_info_station_per_session(struct mac_context *mac, + struct pe_session *pe_session, + tDot11fIEWMMInfoStation *pInfo) +{ + uint8_t max_sp_length = 0; + + max_sp_length = mac->mlme_cfg->wmm_params.max_sp_length; + pInfo->version = SIR_MAC_OUI_VERSION_1; + pInfo->acvo_uapsd = + LIM_UAPSD_GET(ACVO, pe_session->gUapsdPerAcBitmask); + pInfo->acvi_uapsd = + LIM_UAPSD_GET(ACVI, pe_session->gUapsdPerAcBitmask); + pInfo->acbk_uapsd = + LIM_UAPSD_GET(ACBK, pe_session->gUapsdPerAcBitmask); + pInfo->acbe_uapsd = + LIM_UAPSD_GET(ACBE, pe_session->gUapsdPerAcBitmask); + + pInfo->max_sp_length = max_sp_length; + pInfo->present = 1; +} + +void populate_dot11f_wmm_params(struct mac_context *mac, + tDot11fIEWMMParams *pParams, + struct pe_session *pe_session) +{ + pParams->version = SIR_MAC_OUI_VERSION_1; + + if (LIM_IS_AP_ROLE(pe_session)) + pParams->qosInfo = + (pe_session-> + apUapsdEnable << 7) | ((uint8_t) (0x0f & pe_session-> + gLimEdcaParamSetCount)); + else + pParams->qosInfo = + (mac->lim. + gUapsdEnable << 7) | ((uint8_t) (0x0f & pe_session-> + gLimEdcaParamSetCount)); + + /* Fill each EDCA parameter set in order: be, bk, vi, vo */ + pParams->acbe_aifsn = + (0xf & SET_AIFSN(pe_session->gLimEdcaParamsBC[0].aci.aifsn)); + pParams->acbe_acm = (0x1 & pe_session->gLimEdcaParamsBC[0].aci.acm); + pParams->acbe_aci = (0x3 & QCA_WLAN_AC_BE); + pParams->acbe_acwmin = + (0xf & pe_session->gLimEdcaParamsBC[0].cw.min); + pParams->acbe_acwmax = + (0xf & pe_session->gLimEdcaParamsBC[0].cw.max); + pParams->acbe_txoplimit = pe_session->gLimEdcaParamsBC[0].txoplimit; + + pParams->acbk_aifsn = + (0xf & SET_AIFSN(pe_session->gLimEdcaParamsBC[1].aci.aifsn)); + pParams->acbk_acm = (0x1 & pe_session->gLimEdcaParamsBC[1].aci.acm); + pParams->acbk_aci = (0x3 & QCA_WLAN_AC_BK); + pParams->acbk_acwmin = + (0xf & pe_session->gLimEdcaParamsBC[1].cw.min); + pParams->acbk_acwmax = + (0xf & pe_session->gLimEdcaParamsBC[1].cw.max); + pParams->acbk_txoplimit = pe_session->gLimEdcaParamsBC[1].txoplimit; + + if (LIM_IS_AP_ROLE(pe_session)) + pParams->acvi_aifsn = + (0xf & pe_session->gLimEdcaParamsBC[2].aci.aifsn); + else + pParams->acvi_aifsn = + (0xf & + SET_AIFSN(pe_session->gLimEdcaParamsBC[2].aci.aifsn)); + + pParams->acvi_acm = (0x1 & pe_session->gLimEdcaParamsBC[2].aci.acm); + pParams->acvi_aci = (0x3 & QCA_WLAN_AC_VI); + pParams->acvi_acwmin = + (0xf & pe_session->gLimEdcaParamsBC[2].cw.min); + pParams->acvi_acwmax = + (0xf & pe_session->gLimEdcaParamsBC[2].cw.max); + pParams->acvi_txoplimit = pe_session->gLimEdcaParamsBC[2].txoplimit; + + if (LIM_IS_AP_ROLE(pe_session)) + pParams->acvo_aifsn = + (0xf & pe_session->gLimEdcaParamsBC[3].aci.aifsn); + else + pParams->acvo_aifsn = + (0xf & + SET_AIFSN(pe_session->gLimEdcaParamsBC[3].aci.aifsn)); + + pParams->acvo_acm = (0x1 & pe_session->gLimEdcaParamsBC[3].aci.acm); + pParams->acvo_aci = (0x3 & QCA_WLAN_AC_VO); + pParams->acvo_acwmin = + (0xf & pe_session->gLimEdcaParamsBC[3].cw.min); + pParams->acvo_acwmax = + (0xf & pe_session->gLimEdcaParamsBC[3].cw.max); + pParams->acvo_txoplimit = pe_session->gLimEdcaParamsBC[3].txoplimit; + + pParams->present = 1; + +} /* End populate_dot11f_wmm_params. */ + +QDF_STATUS +populate_dot11f_wpa(struct mac_context *mac, + tpSirRSNie pRsnIe, tDot11fIEWPA *pDot11f) +{ + uint32_t status; + int idx; + + if (pRsnIe->length) { + idx = find_ie_location(mac, pRsnIe, DOT11F_EID_WPA); + if (0 <= idx) { + status = dot11f_unpack_ie_wpa(mac, pRsnIe->rsnIEdata + idx + 2 + 4, /* EID, length, OUI */ + pRsnIe->rsnIEdata[idx + 1] - 4, /* OUI */ + pDot11f, false); + if (DOT11F_FAILED(status)) { + pe_err("Parse failure in Populate Dot11fWPA (0x%08x)", + status); + return QDF_STATUS_E_FAILURE; + } + } + } + + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_wpa. */ + +QDF_STATUS populate_dot11f_wpa_opaque(struct mac_context *mac, + tpSirRSNie pRsnIe, + tDot11fIEWPAOpaque *pDot11f) +{ + int idx; + + if (pRsnIe->length) { + idx = find_ie_location(mac, pRsnIe, DOT11F_EID_WPA); + if (0 <= idx) { + pDot11f->present = 1; + pDot11f->num_data = pRsnIe->rsnIEdata[idx + 1] - 4; + qdf_mem_copy(pDot11f->data, pRsnIe->rsnIEdata + idx + 2 + 4, /* EID, len, OUI */ + pRsnIe->rsnIEdata[idx + 1] - 4); /* OUI */ + } + } + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_wpa_opaque. */ + +/* ////////////////////////////////////////////////////////////////////// */ + +QDF_STATUS +sir_convert_probe_req_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, + uint32_t nFrame, tpSirProbeReq pProbeReq) +{ + uint32_t status; + tDot11fProbeRequest pr; + + /* Ok, zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) pProbeReq, sizeof(tSirProbeReq)); + + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_probe_request(mac, pFrame, nFrame, &pr, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse a Probe Request (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pFrame, nFrame); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking a Probe Request (0x%08x, %d bytes):", + status, nFrame); + } + /* & "transliterate" from a 'tDot11fProbeRequestto' a 'tSirProbeReq'... */ + if (!pr.SSID.present) { + pe_debug("Mandatory IE SSID not present!"); + } else { + pProbeReq->ssidPresent = 1; + convert_ssid(mac, &pProbeReq->ssId, &pr.SSID); + } + + if (!pr.SuppRates.present) { + pe_debug_rl("Mandatory IE Supported Rates not present!"); + return QDF_STATUS_E_FAILURE; + } else { + pProbeReq->suppRatesPresent = 1; + convert_supp_rates(mac, &pProbeReq->supportedRates, + &pr.SuppRates); + } + + if (pr.ExtSuppRates.present) { + pProbeReq->extendedRatesPresent = 1; + convert_ext_supp_rates(mac, &pProbeReq->extendedRates, + &pr.ExtSuppRates); + } + + if (pr.HTCaps.present) { + qdf_mem_copy(&pProbeReq->HTCaps, &pr.HTCaps, + sizeof(tDot11fIEHTCaps)); + } + + if (pr.WscProbeReq.present) { + pProbeReq->wscIePresent = 1; + memcpy(&pProbeReq->probeReqWscIeInfo, &pr.WscProbeReq, + sizeof(tDot11fIEWscProbeReq)); + } + if (pr.VHTCaps.present) { + qdf_mem_copy(&pProbeReq->VHTCaps, &pr.VHTCaps, + sizeof(tDot11fIEVHTCaps)); + } + if (pr.P2PProbeReq.present) { + pProbeReq->p2pIePresent = 1; + } + if (pr.he_cap.present) { + qdf_mem_copy(&pProbeReq->he_cap, &pr.he_cap, + sizeof(tDot11fIEhe_cap)); + pe_debug("11AX: HE cap IE present"); + } + return QDF_STATUS_SUCCESS; +} /* End sir_convert_probe_req_frame2_struct. */ + + +/** + * sir_validate_and_rectify_ies() - API to check malformed frame + * @mac_ctx: mac context + * @mgmt_frame: pointer to management frame + * @frame_bytes: no of bytes in frame + * @missing_rsn_bytes: missing rsn bytes + * + * The frame would contain fixed IEs of 12 bytes followed by variable IEs + * (Tagged elements). Every Tagged IE has tag number, tag length and data. + * Tag length indicates the size of data in bytes. + * This function checks for size of Frame received with the sum of all IEs. + * And also rectifies missing optional fields in IE. + * + * NOTE : Presently this function rectifies RSN capability in RSN IE, can + * be extended to rectify other optional fields in other IEs. + * + * Return: 0 on success, error number otherwise. + */ +QDF_STATUS +sir_validate_and_rectify_ies(struct mac_context *mac_ctx, + uint8_t *mgmt_frame, + uint32_t frame_bytes, + uint32_t *missing_rsn_bytes) +{ + uint32_t length = SIZE_OF_FIXED_PARAM; + uint8_t *ref_frame = NULL; + + /* Frame contains atleast one IE */ + if (frame_bytes > (SIZE_OF_FIXED_PARAM + + SIZE_OF_TAG_PARAM_NUM + SIZE_OF_TAG_PARAM_LEN)) { + while (length < frame_bytes) { + /* ref frame points to next IE */ + ref_frame = mgmt_frame + length; + length += (uint32_t)(SIZE_OF_TAG_PARAM_NUM + + SIZE_OF_TAG_PARAM_LEN + + (*(ref_frame + SIZE_OF_TAG_PARAM_NUM))); + } + if (length != frame_bytes) { + /* + * Workaround : Some APs may not include RSN + * Capability but the length of which is included in + * RSN IE length. This may cause in updating RSN + * Capability with junk value. To avoid this, add RSN + * Capability value with default value. + */ + if (ref_frame && (*ref_frame == RSNIEID) && + (length == (frame_bytes + + RSNIE_CAPABILITY_LEN))) { + /* Assume RSN Capability as 00 */ + qdf_mem_set((uint8_t *)(mgmt_frame + + (frame_bytes)), + RSNIE_CAPABILITY_LEN, + DEFAULT_RSNIE_CAP_VAL); + *missing_rsn_bytes = RSNIE_CAPABILITY_LEN; + pe_debug("Added RSN Capability to RSNIE as 0x00 0x00"); + return QDF_STATUS_SUCCESS; + } + return QDF_STATUS_E_FAILURE; + } + } + return QDF_STATUS_SUCCESS; +} + +void sir_copy_caps_info(struct mac_context *mac_ctx, tDot11fFfCapabilities caps, + tpSirProbeRespBeacon pProbeResp) +{ + pProbeResp->capabilityInfo.ess = caps.ess; + pProbeResp->capabilityInfo.ibss = caps.ibss; + pProbeResp->capabilityInfo.cfPollable = caps.cfPollable; + pProbeResp->capabilityInfo.cfPollReq = caps.cfPollReq; + pProbeResp->capabilityInfo.privacy = caps.privacy; + pProbeResp->capabilityInfo.shortPreamble = caps.shortPreamble; + pProbeResp->capabilityInfo.pbcc = caps.pbcc; + pProbeResp->capabilityInfo.channelAgility = caps.channelAgility; + pProbeResp->capabilityInfo.spectrumMgt = caps.spectrumMgt; + pProbeResp->capabilityInfo.qos = caps.qos; + pProbeResp->capabilityInfo.shortSlotTime = caps.shortSlotTime; + pProbeResp->capabilityInfo.apsd = caps.apsd; + pProbeResp->capabilityInfo.rrm = caps.rrm; + pProbeResp->capabilityInfo.dsssOfdm = caps.dsssOfdm; + pProbeResp->capabilityInfo.delayedBA = caps.delayedBA; + pProbeResp->capabilityInfo.immediateBA = caps.immediateBA; +} + +#ifdef WLAN_FEATURE_FILS_SK +static void populate_dot11f_fils_rsn(struct mac_context *mac_ctx, + tDot11fIERSNOpaque *p_dot11f, + uint8_t *rsn_ie) +{ + pe_debug("FILS RSN IE length %d", rsn_ie[1]); + if (rsn_ie[1]) { + p_dot11f->present = 1; + p_dot11f->num_data = rsn_ie[1]; + qdf_mem_copy(p_dot11f->data, &rsn_ie[2], rsn_ie[1]); + } +} + +void populate_dot11f_fils_params(struct mac_context *mac_ctx, + tDot11fAssocRequest *frm, + struct pe_session *pe_session) +{ + struct pe_fils_session *fils_info = pe_session->fils_info; + + /* Populate RSN IE with FILS AKM */ + populate_dot11f_fils_rsn(mac_ctx, &frm->RSNOpaque, + fils_info->rsn_ie); + + /* Populate FILS session IE */ + frm->fils_session.present = true; + qdf_mem_copy(frm->fils_session.session, + fils_info->fils_session, FILS_SESSION_LENGTH); + + /* Populate FILS Key confirmation IE */ + if (fils_info->key_auth_len) { + frm->fils_key_confirmation.present = true; + frm->fils_key_confirmation.num_key_auth = + fils_info->key_auth_len; + + qdf_mem_copy(frm->fils_key_confirmation.key_auth, + fils_info->key_auth, fils_info->key_auth_len); + } +} + +/** + * update_fils_data: update fils params from beacon/probe response + * @fils_ind: pointer to sir_fils_indication + * @fils_indication: pointer to tDot11fIEfils_indication + * + * Return: None + */ +void update_fils_data(struct sir_fils_indication *fils_ind, + tDot11fIEfils_indication *fils_indication) +{ + uint8_t *data; + uint8_t remaining_data = fils_indication->num_variable_data; + + data = fils_indication->variable_data; + fils_ind->is_present = true; + fils_ind->is_ip_config_supported = + fils_indication->is_ip_config_supported; + fils_ind->is_fils_sk_auth_supported = + fils_indication->is_fils_sk_auth_supported; + fils_ind->is_fils_sk_auth_pfs_supported = + fils_indication->is_fils_sk_auth_pfs_supported; + fils_ind->is_pk_auth_supported = + fils_indication->is_pk_auth_supported; + if (fils_indication->is_cache_id_present) { + if (remaining_data < SIR_CACHE_IDENTIFIER_LEN) { + pe_err("Failed to copy Cache Identifier, Invalid remaining data %d", + remaining_data); + return; + } + fils_ind->cache_identifier.is_present = true; + qdf_mem_copy(fils_ind->cache_identifier.identifier, + data, SIR_CACHE_IDENTIFIER_LEN); + data = data + SIR_CACHE_IDENTIFIER_LEN; + remaining_data = remaining_data - SIR_CACHE_IDENTIFIER_LEN; + } + if (fils_indication->is_hessid_present) { + if (remaining_data < SIR_HESSID_LEN) { + pe_err("Failed to copy HESSID, Invalid remaining data %d", + remaining_data); + return; + } + fils_ind->hessid.is_present = true; + qdf_mem_copy(fils_ind->hessid.hessid, + data, SIR_HESSID_LEN); + data = data + SIR_HESSID_LEN; + remaining_data = remaining_data - SIR_HESSID_LEN; + } + if (fils_indication->realm_identifiers_cnt) { + if (remaining_data < (fils_indication->realm_identifiers_cnt * + SIR_REALM_LEN)) { + pe_err("Failed to copy Realm Identifier, Invalid remaining data %d realm_cnt %d", + remaining_data, + fils_indication->realm_identifiers_cnt); + return; + } + fils_ind->realm_identifier.is_present = true; + fils_ind->realm_identifier.realm_cnt = + fils_indication->realm_identifiers_cnt; + qdf_mem_copy(fils_ind->realm_identifier.realm, + data, fils_ind->realm_identifier.realm_cnt * + SIR_REALM_LEN); + } +} +#endif + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +static void update_bss_color_change_ie_from_probe_rsp( + tDot11fProbeResponse *prb_frm, + tpSirProbeRespBeacon prb_rsp_struct) +{ + if (prb_frm->bss_color_change.present) { + pe_debug("11AX: HE BSS color change present"); + qdf_mem_copy(&prb_rsp_struct->vendor_he_bss_color_change, + &prb_frm->bss_color_change, + sizeof(tDot11fIEbss_color_change)); + } +} +#else +static inline void update_bss_color_change_ie_from_probe_rsp( + tDot11fProbeResponse *prb_frm, + tpSirProbeRespBeacon prb_rsp_struct) +{} +#endif +QDF_STATUS sir_convert_probe_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, + uint32_t nFrame, + tpSirProbeRespBeacon pProbeResp) +{ + uint32_t status; + tDot11fProbeResponse *pr; + + /* Ok, zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) pProbeResp, sizeof(tSirProbeRespBeacon)); + + pr = qdf_mem_malloc(sizeof(tDot11fProbeResponse)); + if (!pr) + return QDF_STATUS_E_NOMEM; + + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_probe_response(mac, pFrame, nFrame, pr, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse a Probe Response (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + pFrame, nFrame); + qdf_mem_free(pr); + return QDF_STATUS_E_FAILURE; + } + /* & "transliterate" from a 'tDot11fProbeResponse' to a 'tSirProbeRespBeacon'... */ + + /* Timestamp */ + qdf_mem_copy((uint8_t *) pProbeResp->timeStamp, + (uint8_t *) &pr->TimeStamp, sizeof(tSirMacTimeStamp)); + + /* Beacon Interval */ + pProbeResp->beaconInterval = pr->BeaconInterval.interval; + + sir_copy_caps_info(mac, pr->Capabilities, pProbeResp); + + if (!pr->SSID.present) { + pe_debug("Mandatory IE SSID not present!"); + } else { + pProbeResp->ssidPresent = 1; + convert_ssid(mac, &pProbeResp->ssId, &pr->SSID); + } + + if (!pr->SuppRates.present) { + pe_debug_rl("Mandatory IE Supported Rates not present!"); + } else { + pProbeResp->suppRatesPresent = 1; + convert_supp_rates(mac, &pProbeResp->supportedRates, + &pr->SuppRates); + } + + if (pr->ExtSuppRates.present) { + pProbeResp->extendedRatesPresent = 1; + convert_ext_supp_rates(mac, &pProbeResp->extendedRates, + &pr->ExtSuppRates); + } + + if (pr->CFParams.present) { + pProbeResp->cfPresent = 1; + convert_cf_params(mac, &pProbeResp->cfParamSet, &pr->CFParams); + } + + if (pr->Country.present) { + pProbeResp->countryInfoPresent = 1; + convert_country(mac, &pProbeResp->countryInfoParam, + &pr->Country); + } + + if (pr->EDCAParamSet.present) { + pProbeResp->edcaPresent = 1; + convert_edca_param(mac, &pProbeResp->edcaParams, + &pr->EDCAParamSet); + } + + if (pr->ChanSwitchAnn.present) { + pProbeResp->channelSwitchPresent = 1; + qdf_mem_copy(&pProbeResp->channelSwitchIE, &pr->ChanSwitchAnn, + sizeof(pProbeResp->channelSwitchIE)); + } + + if (pr->ext_chan_switch_ann.present) { + pProbeResp->ext_chan_switch_present = 1; + qdf_mem_copy(&pProbeResp->ext_chan_switch, + &pr->ext_chan_switch_ann, + sizeof(tDot11fIEext_chan_switch_ann)); + } + + if (pr->SuppOperatingClasses.present) { + pProbeResp->supp_operating_class_present = 1; + qdf_mem_copy(&pProbeResp->supp_operating_classes, + &pr->SuppOperatingClasses, + sizeof(tDot11fIESuppOperatingClasses)); + } + + if (pr->sec_chan_offset_ele.present) { + pProbeResp->sec_chan_offset_present = 1; + qdf_mem_copy(&pProbeResp->sec_chan_offset, + &pr->sec_chan_offset_ele, + sizeof(pProbeResp->sec_chan_offset)); + } + + if (pr->TPCReport.present) { + pProbeResp->tpcReportPresent = 1; + qdf_mem_copy(&pProbeResp->tpcReport, &pr->TPCReport, + sizeof(tDot11fIETPCReport)); + } + + if (pr->PowerConstraints.present) { + pProbeResp->powerConstraintPresent = 1; + qdf_mem_copy(&pProbeResp->localPowerConstraint, + &pr->PowerConstraints, + sizeof(tDot11fIEPowerConstraints)); + } + + if (pr->Quiet.present) { + pProbeResp->quietIEPresent = 1; + qdf_mem_copy(&pProbeResp->quietIE, &pr->Quiet, + sizeof(tDot11fIEQuiet)); + } + + if (pr->HTCaps.present) { + qdf_mem_copy(&pProbeResp->HTCaps, &pr->HTCaps, + sizeof(tDot11fIEHTCaps)); + } + + if (pr->HTInfo.present) { + qdf_mem_copy(&pProbeResp->HTInfo, &pr->HTInfo, + sizeof(tDot11fIEHTInfo)); + } + + if (pr->he_op.oper_info_6g_present) { + pProbeResp->chan_freq = wlan_reg_chan_band_to_freq(mac->pdev, + pr->he_op.oper_info_6g.info.primary_ch, + BIT(REG_BAND_6G)); + } else if (pr->DSParams.present) { + pProbeResp->dsParamsPresent = 1; + pProbeResp->chan_freq = + wlan_reg_legacy_chan_to_freq(mac->pdev, pr->DSParams.curr_channel); + } else if (pr->HTInfo.present) { + pProbeResp->chan_freq = + wlan_reg_legacy_chan_to_freq(mac->pdev, pr->HTInfo.primaryChannel); + } + + if (pr->RSNOpaque.present) { + pProbeResp->rsnPresent = 1; + convert_rsn_opaque(mac, &pProbeResp->rsn, &pr->RSNOpaque); + } + + if (pr->WPA.present) { + pProbeResp->wpaPresent = 1; + convert_wpa(mac, &pProbeResp->wpa, &pr->WPA); + } + + if (pr->WMMParams.present) { + pProbeResp->wmeEdcaPresent = 1; + convert_wmm_params(mac, &pProbeResp->edcaParams, &pr->WMMParams); + } + + if (pr->WMMInfoAp.present) { + pProbeResp->wmeInfoPresent = 1; + pe_debug("WMM Information Element present in Probe Response Frame!"); + } + + if (pr->WMMCaps.present) { + pProbeResp->wsmCapablePresent = 1; + } + + if (pr->ERPInfo.present) { + pProbeResp->erpPresent = 1; + convert_erp_info(mac, &pProbeResp->erpIEInfo, &pr->ERPInfo); + } + if (pr->MobilityDomain.present) { + /* MobilityDomain */ + pProbeResp->mdiePresent = 1; + qdf_mem_copy((uint8_t *) &(pProbeResp->mdie[0]), + (uint8_t *) &(pr->MobilityDomain.MDID), + sizeof(uint16_t)); + pProbeResp->mdie[2] = + ((pr->MobilityDomain.overDSCap << 0) | (pr->MobilityDomain. + resourceReqCap << + 1)); + pe_debug("mdie=%02x%02x%02x", + (unsigned int)pProbeResp->mdie[0], + (unsigned int)pProbeResp->mdie[1], + (unsigned int)pProbeResp->mdie[2]); + } + +#if defined FEATURE_WLAN_ESE + if (pr->ESEVersion.present) + pProbeResp->is_ese_ver_ie_present = 1; + if (pr->QBSSLoad.present) { + qdf_mem_copy(&pProbeResp->QBSSLoad, &pr->QBSSLoad, + sizeof(tDot11fIEQBSSLoad)); + } +#endif + if (pr->P2PProbeRes.present) { + qdf_mem_copy(&pProbeResp->P2PProbeRes, &pr->P2PProbeRes, + sizeof(tDot11fIEP2PProbeRes)); + } + if (pr->VHTCaps.present) { + qdf_mem_copy(&pProbeResp->VHTCaps, &pr->VHTCaps, + sizeof(tDot11fIEVHTCaps)); + } + if (pr->VHTOperation.present) { + qdf_mem_copy(&pProbeResp->VHTOperation, &pr->VHTOperation, + sizeof(tDot11fIEVHTOperation)); + } + if (pr->VHTExtBssLoad.present) { + qdf_mem_copy(&pProbeResp->VHTExtBssLoad, &pr->VHTExtBssLoad, + sizeof(tDot11fIEVHTExtBssLoad)); + } + pProbeResp->Vendor1IEPresent = pr->Vendor1IE.present; + pProbeResp->Vendor3IEPresent = pr->Vendor3IE.present; + + pProbeResp->vendor_vht_ie.present = pr->vendor_vht_ie.present; + if (pr->vendor_vht_ie.present) + pProbeResp->vendor_vht_ie.sub_type = pr->vendor_vht_ie.sub_type; + if (pr->vendor_vht_ie.VHTCaps.present) { + qdf_mem_copy(&pProbeResp->vendor_vht_ie.VHTCaps, + &pr->vendor_vht_ie.VHTCaps, + sizeof(tDot11fIEVHTCaps)); + } + if (pr->vendor_vht_ie.VHTOperation.present) { + qdf_mem_copy(&pProbeResp->vendor_vht_ie.VHTOperation, + &pr->vendor_vht_ie.VHTOperation, + sizeof(tDot11fIEVHTOperation)); + } + /* Update HS 2.0 Information Element */ + if (pr->hs20vendor_ie.present) { + pe_debug("HS20 Indication Element Present, rel#:%u, id:%u", + pr->hs20vendor_ie.release_num, + pr->hs20vendor_ie.hs_id_present); + qdf_mem_copy(&pProbeResp->hs20vendor_ie, + &pr->hs20vendor_ie, + sizeof(tDot11fIEhs20vendor_ie) - + sizeof(pr->hs20vendor_ie.hs_id)); + if (pr->hs20vendor_ie.hs_id_present) + qdf_mem_copy(&pProbeResp->hs20vendor_ie.hs_id, + &pr->hs20vendor_ie.hs_id, + sizeof(pr->hs20vendor_ie.hs_id)); + } + if (pr->MBO_IE.present) { + pProbeResp->MBO_IE_present = true; + if (pr->MBO_IE.cellular_data_cap.present) + pProbeResp->MBO_capability = + pr->MBO_IE.cellular_data_cap.cellular_connectivity; + + if (pr->MBO_IE.assoc_disallowed.present) { + pProbeResp->assoc_disallowed = true; + pProbeResp->assoc_disallowed_reason = + pr->MBO_IE.assoc_disallowed.reason_code; + } + } + + if (pr->qcn_ie.present) + qdf_mem_copy(&pProbeResp->qcn_ie, &pr->qcn_ie, + sizeof(tDot11fIEqcn_ie)); + + if (pr->he_cap.present) { + pe_debug("11AX: HE cap IE present"); + qdf_mem_copy(&pProbeResp->he_cap, &pr->he_cap, + sizeof(tDot11fIEhe_cap)); + } + if (pr->he_op.present) { + pe_debug("11AX: HE operation IE present"); + qdf_mem_copy(&pProbeResp->he_op, &pr->he_op, + sizeof(tDot11fIEhe_op)); + } + + update_bss_color_change_ie_from_probe_rsp(pr, pProbeResp); + + qdf_mem_free(pr); + return QDF_STATUS_SUCCESS; + +} /* End sir_convert_probe_frame2_struct. */ + +QDF_STATUS +sir_convert_assoc_req_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, + uint32_t nFrame, tpSirAssocReq pAssocReq) +{ + tDot11fAssocRequest *ar; + uint32_t status; + + ar = qdf_mem_malloc(sizeof(tDot11fAssocRequest)); + if (!ar) + return QDF_STATUS_E_NOMEM; + /* Zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) pAssocReq, sizeof(tSirAssocReq)); + + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_assoc_request(mac, pFrame, nFrame, ar, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse an Association Request (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pFrame, nFrame); + qdf_mem_free(ar); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking an Assoication Request (0x%08x, %d bytes):", + status, nFrame); + } + /* & "transliterate" from a 'tDot11fAssocRequest' to a 'tSirAssocReq'... */ + + /* make sure this is seen as an assoc request */ + pAssocReq->reassocRequest = 0; + + /* Capabilities */ + pAssocReq->capabilityInfo.ess = ar->Capabilities.ess; + pAssocReq->capabilityInfo.ibss = ar->Capabilities.ibss; + pAssocReq->capabilityInfo.cfPollable = ar->Capabilities.cfPollable; + pAssocReq->capabilityInfo.cfPollReq = ar->Capabilities.cfPollReq; + pAssocReq->capabilityInfo.privacy = ar->Capabilities.privacy; + pAssocReq->capabilityInfo.shortPreamble = + ar->Capabilities.shortPreamble; + pAssocReq->capabilityInfo.pbcc = ar->Capabilities.pbcc; + pAssocReq->capabilityInfo.channelAgility = + ar->Capabilities.channelAgility; + pAssocReq->capabilityInfo.spectrumMgt = ar->Capabilities.spectrumMgt; + pAssocReq->capabilityInfo.qos = ar->Capabilities.qos; + pAssocReq->capabilityInfo.shortSlotTime = + ar->Capabilities.shortSlotTime; + pAssocReq->capabilityInfo.apsd = ar->Capabilities.apsd; + pAssocReq->capabilityInfo.rrm = ar->Capabilities.rrm; + pAssocReq->capabilityInfo.dsssOfdm = ar->Capabilities.dsssOfdm; + pAssocReq->capabilityInfo.delayedBA = ar->Capabilities.delayedBA; + pAssocReq->capabilityInfo.immediateBA = ar->Capabilities.immediateBA; + + /* Listen Interval */ + pAssocReq->listenInterval = ar->ListenInterval.interval; + + /* SSID */ + if (ar->SSID.present) { + pAssocReq->ssidPresent = 1; + convert_ssid(mac, &pAssocReq->ssId, &ar->SSID); + } + /* Supported Rates */ + if (ar->SuppRates.present) { + pAssocReq->suppRatesPresent = 1; + convert_supp_rates(mac, &pAssocReq->supportedRates, + &ar->SuppRates); + } + /* Extended Supported Rates */ + if (ar->ExtSuppRates.present) { + pAssocReq->extendedRatesPresent = 1; + convert_ext_supp_rates(mac, &pAssocReq->extendedRates, + &ar->ExtSuppRates); + } + /* QOS Capabilities: */ + if (ar->QOSCapsStation.present) { + pAssocReq->qosCapabilityPresent = 1; + convert_qos_caps_station(mac, &pAssocReq->qosCapability, + &ar->QOSCapsStation); + } + /* WPA */ + if (ar->WPAOpaque.present) { + pAssocReq->wpaPresent = 1; + convert_wpa_opaque(mac, &pAssocReq->wpa, &ar->WPAOpaque); + } +#ifdef FEATURE_WLAN_WAPI + if (ar->WAPIOpaque.present) { + pAssocReq->wapiPresent = 1; + convert_wapi_opaque(mac, &pAssocReq->wapi, &ar->WAPIOpaque); + } +#endif + /* RSN */ + if (ar->RSNOpaque.present) { + pAssocReq->rsnPresent = 1; + convert_rsn_opaque(mac, &pAssocReq->rsn, &ar->RSNOpaque); + } + /* WSC IE */ + if (ar->WscIEOpaque.present) { + pAssocReq->addIEPresent = 1; + convert_wsc_opaque(mac, &pAssocReq->addIE, &ar->WscIEOpaque); + } + + if (ar->P2PIEOpaque.present) { + pAssocReq->addIEPresent = 1; + convert_p2p_opaque(mac, &pAssocReq->addIE, &ar->P2PIEOpaque); + } +#ifdef WLAN_FEATURE_WFD + if (ar->WFDIEOpaque.present) { + pAssocReq->addIEPresent = 1; + convert_wfd_opaque(mac, &pAssocReq->addIE, &ar->WFDIEOpaque); + } +#endif + + /* Power Capabilities */ + if (ar->PowerCaps.present) { + pAssocReq->powerCapabilityPresent = 1; + convert_power_caps(mac, &pAssocReq->powerCapability, + &ar->PowerCaps); + } + /* Supported Channels */ + if (ar->SuppChannels.present) { + pAssocReq->supportedChannelsPresent = 1; + convert_supp_channels(mac, &pAssocReq->supportedChannels, + &ar->SuppChannels); + } + + if (ar->HTCaps.present) { + qdf_mem_copy(&pAssocReq->HTCaps, &ar->HTCaps, + sizeof(tDot11fIEHTCaps)); + } + + if (ar->WMMInfoStation.present) { + pAssocReq->wmeInfoPresent = 1; + qdf_mem_copy(&pAssocReq->WMMInfoStation, &ar->WMMInfoStation, + sizeof(tDot11fIEWMMInfoStation)); + + } + + if (ar->WMMCaps.present) + pAssocReq->wsmCapablePresent = 1; + + if (!pAssocReq->ssidPresent) { + pe_debug("Received Assoc without SSID IE"); + qdf_mem_free(ar); + return QDF_STATUS_E_FAILURE; + } + + if (!pAssocReq->suppRatesPresent && !pAssocReq->extendedRatesPresent) { + pe_debug("Received Assoc without supp rate IE"); + qdf_mem_free(ar); + return QDF_STATUS_E_FAILURE; + } + if (ar->VHTCaps.present) { + qdf_mem_copy(&pAssocReq->VHTCaps, &ar->VHTCaps, + sizeof(tDot11fIEVHTCaps)); + pe_debug("Received Assoc Req with VHT Cap"); + lim_log_vht_cap(mac, &pAssocReq->VHTCaps); + } + if (ar->OperatingMode.present) { + qdf_mem_copy(&pAssocReq->operMode, &ar->OperatingMode, + sizeof(tDot11fIEOperatingMode)); + pe_debug("Received Assoc Req with Operating Mode IE"); + lim_log_operating_mode(mac, &pAssocReq->operMode); + } + if (ar->ExtCap.present) { + struct s_ext_cap *ext_cap; + + qdf_mem_copy(&pAssocReq->ExtCap, &ar->ExtCap, + sizeof(tDot11fIEExtCap)); + ext_cap = (struct s_ext_cap *)&pAssocReq->ExtCap.bytes; + pe_debug("timingMeas: %d, finetimingMeas Init: %d, Resp: %d", + ext_cap->timing_meas, ext_cap->fine_time_meas_initiator, + ext_cap->fine_time_meas_responder); + } + if (ar->SuppOperatingClasses.present) { + uint8_t num_classes = ar->SuppOperatingClasses.num_classes; + + if (num_classes > sizeof(ar->SuppOperatingClasses.classes)) + num_classes = + sizeof(ar->SuppOperatingClasses.classes); + qdf_mem_copy(&pAssocReq->supp_operating_classes, + &ar->SuppOperatingClasses, + sizeof(tDot11fIESuppOperatingClasses)); + QDF_TRACE_HEX_DUMP( + QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + ar->SuppOperatingClasses.classes, num_classes); + } + + pAssocReq->vendor_vht_ie.present = ar->vendor_vht_ie.present; + if (ar->vendor_vht_ie.present) { + pAssocReq->vendor_vht_ie.sub_type = ar->vendor_vht_ie.sub_type; + if (ar->vendor_vht_ie.VHTCaps.present) { + qdf_mem_copy(&pAssocReq->vendor_vht_ie.VHTCaps, + &ar->vendor_vht_ie.VHTCaps, + sizeof(tDot11fIEVHTCaps)); + pe_debug("Received Assoc Request with Vendor specific VHT Cap"); + lim_log_vht_cap(mac, &pAssocReq->VHTCaps); + } + } + if (ar->qcn_ie.present) + qdf_mem_copy(&pAssocReq->qcn_ie, &ar->qcn_ie, + sizeof(tDot11fIEqcn_ie)); + if (ar->he_cap.present) { + qdf_mem_copy(&pAssocReq->he_cap, &ar->he_cap, + sizeof(tDot11fIEhe_cap)); + pe_debug("Received Assoc Req with HE Capability IE"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + &pAssocReq->he_cap, sizeof(tDot11fIEhe_cap)); + } + if (ar->he_6ghz_band_cap.present) { + qdf_mem_copy(&pAssocReq->he_6ghz_band_cap, + &ar->he_6ghz_band_cap, + sizeof(tDot11fIEhe_6ghz_band_cap)); + pe_debug("Received Assoc Req with HE Band Capability IE"); + } + qdf_mem_free(ar); + return QDF_STATUS_SUCCESS; + +} /* End sir_convert_assoc_req_frame2_struct. */ + +/** + * dot11f_parse_assoc_response() - API to parse Assoc IE buffer to struct + * @mac_ctx: MAC context + * @p_buf: Pointer to the assoc IE buffer + * @n_buf: length of the @p_buf + * @p_frm: Struct to populate the IE buffer after parsing + * @append_ie: Boolean to indicate whether to reset @p_frm or not. If @append_ie + * is true, @p_frm struct is not reset to zeros. + * + * Return: QDF_STATUS + */ +static QDF_STATUS dot11f_parse_assoc_response(struct mac_context *mac_ctx, + uint8_t *p_buf, uint32_t n_buf, + tDot11fAssocResponse *p_frm, + bool append_ie) +{ + uint32_t status; + + status = dot11f_unpack_assoc_response(mac_ctx, p_buf, + n_buf, p_frm, append_ie); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse an Association Response (0x%08x, %d bytes):", + status, n_buf); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + p_buf, n_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_FILS_SK +/** + * fils_convert_assoc_rsp_frame2_struct() - Copy FILS IE's to Assoc rsp struct + * @ar: frame parser Assoc response struct + * @pAssocRsp: LIM Assoc response + * + * Return: None + */ +static void fils_convert_assoc_rsp_frame2_struct(tDot11fAssocResponse *ar, + tpSirAssocRsp pAssocRsp) +{ + if (ar->fils_session.present) { + pe_debug("fils session IE present"); + pAssocRsp->fils_session.present = true; + qdf_mem_copy(pAssocRsp->fils_session.session, + ar->fils_session.session, + DOT11F_IE_FILS_SESSION_MAX_LEN); + } + + if (ar->fils_key_confirmation.present) { + pe_debug("fils key conf IE present"); + pAssocRsp->fils_key_auth.num_key_auth = + ar->fils_key_confirmation.num_key_auth; + qdf_mem_copy(pAssocRsp->fils_key_auth.key_auth, + ar->fils_key_confirmation.key_auth, + pAssocRsp->fils_key_auth.num_key_auth); + } + + if (ar->fils_kde.present) { + pe_debug("fils kde IE present %d", + ar->fils_kde.num_kde_list); + pAssocRsp->fils_kde.num_kde_list = + ar->fils_kde.num_kde_list; + qdf_mem_copy(pAssocRsp->fils_kde.key_rsc, + ar->fils_kde.key_rsc, KEY_RSC_LEN); + qdf_mem_copy(&pAssocRsp->fils_kde.kde_list, + &ar->fils_kde.kde_list, + pAssocRsp->fils_kde.num_kde_list); + } + + if (ar->fils_hlp_container.present) { + pe_debug("FILS HLP container IE present"); + sir_copy_mac_addr(pAssocRsp->dst_mac.bytes, + ar->fils_hlp_container.dest_mac); + sir_copy_mac_addr(pAssocRsp->src_mac.bytes, + ar->fils_hlp_container.src_mac); + pAssocRsp->hlp_data_len = ar->fils_hlp_container.num_hlp_packet; + qdf_mem_copy(pAssocRsp->hlp_data, + ar->fils_hlp_container.hlp_packet, + pAssocRsp->hlp_data_len); + + if (ar->fragment_ie.present) { + pe_debug("FILS fragment ie present"); + qdf_mem_copy(pAssocRsp->hlp_data + + pAssocRsp->hlp_data_len, + ar->fragment_ie.data, + ar->fragment_ie.num_data); + pAssocRsp->hlp_data_len += ar->fragment_ie.num_data; + } + } +} +#else +static inline void fils_convert_assoc_rsp_frame2_struct(tDot11fAssocResponse + *ar, tpSirAssocRsp + pAssocRsp) +{ } +#endif + +QDF_STATUS wlan_parse_ftie_sha384(uint8_t *frame, uint32_t frame_len, + struct sSirAssocRsp *assoc_rsp) +{ + const uint8_t *ie, *ie_end, *pos; + uint8_t ie_len, remaining_ie_len; + struct wlan_sha384_ftinfo_subelem *ft_subelem; + + ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_FTINFO, frame, frame_len); + if (!ie) { + pe_err("FT IE not present"); + return QDF_STATUS_E_FAILURE; + } + + if (!ie[1]) { + pe_err("FT IE length is zero"); + return QDF_STATUS_E_FAILURE; + } + + ie_len = ie[1]; + if (ie_len < sizeof(struct wlan_sha384_ftinfo)) { + pe_err("Invalid FTIE len:%d", ie_len); + return QDF_STATUS_E_FAILURE; + } + remaining_ie_len = ie_len; + pos = ie + 2; + qdf_mem_copy(&assoc_rsp->sha384_ft_info, pos, + sizeof(struct wlan_sha384_ftinfo)); + ie_end = ie + ie_len; + pos += sizeof(struct wlan_sha384_ftinfo); + remaining_ie_len -= sizeof(struct wlan_sha384_ftinfo); + ft_subelem = &assoc_rsp->sha384_ft_subelem; + qdf_mem_zero(ft_subelem, sizeof(*ft_subelem)); + while (ie_end - pos >= 2) { + uint8_t id, len; + + id = *pos++; + len = *pos++; + /* Subtract data length(len) + 1 bytes for + * Subelement ID + 1 bytes for length from + * remaining FTIE buffer len (ie_len). + * Subelement Parameter(s) field : + * Subelement ID Length Data + * Octets: 1 1 variable + */ + if (len < 1 || remaining_ie_len < (len + 2)) { + pe_err("Invalid FT subelem length"); + return QDF_STATUS_E_FAILURE; + } + + remaining_ie_len -= (len + 2); + + switch (id) { + case FTIE_SUBELEM_R1KH_ID: + if (len != FTIE_R1KH_LEN) { + pe_err("Invalid R1KH-ID length: %d", + len); + return QDF_STATUS_E_FAILURE; + } + ft_subelem->r1kh_id.present = 1; + qdf_mem_copy(ft_subelem->r1kh_id.PMK_R1_ID, + pos, FTIE_R1KH_LEN); + break; + case FTIE_SUBELEM_GTK: + if (ft_subelem->gtk) { + qdf_mem_zero(ft_subelem->gtk, + ft_subelem->gtk_len); + ft_subelem->gtk_len = 0; + qdf_mem_free(ft_subelem->gtk); + } + ft_subelem->gtk = qdf_mem_malloc(len); + if (!ft_subelem->gtk) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(ft_subelem->gtk, pos, len); + ft_subelem->gtk_len = len; + break; + case FTIE_SUBELEM_R0KH_ID: + if (len < 1 || len > FTIE_R0KH_MAX_LEN) { + pe_err("Invalid R0KH-ID length: %d", + len); + return QDF_STATUS_E_FAILURE; + } + ft_subelem->r0kh_id.present = 1; + ft_subelem->r0kh_id.num_PMK_R0_ID = len; + qdf_mem_copy(ft_subelem->r0kh_id.PMK_R0_ID, + pos, len); + break; + case FTIE_SUBELEM_IGTK: + if (ft_subelem->igtk) { + qdf_mem_zero(ft_subelem->igtk, + ft_subelem->igtk_len); + ft_subelem->igtk_len = 0; + qdf_mem_free(ft_subelem->igtk); + } + ft_subelem->igtk = qdf_mem_malloc(len); + if (!ft_subelem->igtk) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(ft_subelem->igtk, pos, len); + ft_subelem->igtk_len = len; + + break; + default: + pe_debug("Unknown subelem id %d len:%d", + id, len); + break; + } + pos += len; + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +sir_convert_assoc_resp_frame2_struct(struct mac_context *mac, + struct pe_session *session_entry, + uint8_t *frame, uint32_t frame_len, + tpSirAssocRsp pAssocRsp) +{ + tDot11fAssocResponse *ar; + enum ani_akm_type auth_type; + uint32_t status, ie_len; + QDF_STATUS qdf_status; + uint8_t cnt = 0; + bool sha384_akm; + uint8_t *ie_ptr; + + ar = qdf_mem_malloc(sizeof(*ar)); + if (!ar) + return QDF_STATUS_E_FAILURE; + + /* decrypt the cipher text using AEAD decryption */ + if (lim_is_fils_connection(session_entry)) { + status = aead_decrypt_assoc_rsp(mac, session_entry, + ar, frame, &frame_len); + if (!QDF_IS_STATUS_SUCCESS(status)) { + pe_err("FILS assoc rsp AEAD decrypt fails"); + qdf_mem_free(ar); + return QDF_STATUS_E_FAILURE; + } + } + + status = dot11f_parse_assoc_response(mac, frame, frame_len, ar, false); + if (QDF_STATUS_SUCCESS != status) { + qdf_mem_free(ar); + return status; + } + + /* Capabilities */ + pAssocRsp->capabilityInfo.ess = ar->Capabilities.ess; + pAssocRsp->capabilityInfo.ibss = ar->Capabilities.ibss; + pAssocRsp->capabilityInfo.cfPollable = ar->Capabilities.cfPollable; + pAssocRsp->capabilityInfo.cfPollReq = ar->Capabilities.cfPollReq; + pAssocRsp->capabilityInfo.privacy = ar->Capabilities.privacy; + pAssocRsp->capabilityInfo.shortPreamble = + ar->Capabilities.shortPreamble; + pAssocRsp->capabilityInfo.pbcc = ar->Capabilities.pbcc; + pAssocRsp->capabilityInfo.channelAgility = + ar->Capabilities.channelAgility; + pAssocRsp->capabilityInfo.spectrumMgt = ar->Capabilities.spectrumMgt; + pAssocRsp->capabilityInfo.qos = ar->Capabilities.qos; + pAssocRsp->capabilityInfo.shortSlotTime = + ar->Capabilities.shortSlotTime; + pAssocRsp->capabilityInfo.apsd = ar->Capabilities.apsd; + pAssocRsp->capabilityInfo.rrm = ar->Capabilities.rrm; + pAssocRsp->capabilityInfo.dsssOfdm = ar->Capabilities.dsssOfdm; + pAssocRsp->capabilityInfo.delayedBA = ar->Capabilities.delayedBA; + pAssocRsp->capabilityInfo.immediateBA = ar->Capabilities.immediateBA; + + pAssocRsp->status_code = ar->Status.status; + pAssocRsp->aid = ar->AID.associd; +#ifdef WLAN_FEATURE_11W + if (ar->TimeoutInterval.present) { + pAssocRsp->TimeoutInterval.present = 1; + pAssocRsp->TimeoutInterval.timeoutType = + ar->TimeoutInterval.timeoutType; + pAssocRsp->TimeoutInterval.timeoutValue = + ar->TimeoutInterval.timeoutValue; + } +#endif + + if (!ar->SuppRates.present) { + pAssocRsp->suppRatesPresent = 0; + pe_debug_rl("Mandatory IE Supported Rates not present!"); + } else { + pAssocRsp->suppRatesPresent = 1; + convert_supp_rates(mac, &pAssocRsp->supportedRates, + &ar->SuppRates); + } + + if (ar->ExtSuppRates.present) { + pAssocRsp->extendedRatesPresent = 1; + convert_ext_supp_rates(mac, &pAssocRsp->extendedRates, + &ar->ExtSuppRates); + } + + if (ar->EDCAParamSet.present) { + pAssocRsp->edcaPresent = 1; + convert_edca_param(mac, &pAssocRsp->edca, &ar->EDCAParamSet); + } + if (ar->WMMParams.present) { + pAssocRsp->wmeEdcaPresent = 1; + convert_wmm_params(mac, &pAssocRsp->edca, &ar->WMMParams); + pe_debug("Received Assoc Resp with WMM Param"); + __print_wmm_params(mac, &ar->WMMParams); + } + + if (ar->HTCaps.present) { + pe_debug("Received Assoc Resp with HT Cap"); + qdf_mem_copy(&pAssocRsp->HTCaps, &ar->HTCaps, + sizeof(tDot11fIEHTCaps)); + } + + if (ar->HTInfo.present) { + pe_debug("Received Assoc Resp with HT Info"); + qdf_mem_copy(&pAssocRsp->HTInfo, &ar->HTInfo, + sizeof(tDot11fIEHTInfo)); + } + if (ar->MobilityDomain.present) { + /* MobilityDomain */ + pAssocRsp->mdiePresent = 1; + qdf_mem_copy((uint8_t *) &(pAssocRsp->mdie[0]), + (uint8_t *) &(ar->MobilityDomain.MDID), + sizeof(uint16_t)); + pAssocRsp->mdie[2] = ((ar->MobilityDomain.overDSCap << 0) | + (ar->MobilityDomain.resourceReqCap << 1)); + pe_debug("new mdie=%02x%02x%02x", + (unsigned int)pAssocRsp->mdie[0], + (unsigned int)pAssocRsp->mdie[1], + (unsigned int)pAssocRsp->mdie[2]); + } + + /* + * If the connection is based on SHA384 AKM suite, + * then the length of MIC is 24 bytes, but frame parser + * has FTIE MIC of 16 bytes only. This results in parsing FTIE + * failure and R0KH and R1KH are not sent to firmware over RSO + * command. Frame parser doesn't have + * info on the connected AKM. So parse the FTIE again if + * AKM is sha384 based and extract the R0KH and R1KH using the new + * parsing logic. + */ + auth_type = session_entry->connected_akm; + sha384_akm = lim_is_sha384_akm(auth_type); + if (sha384_akm) { + ie_ptr = frame + FIXED_PARAM_OFFSET_ASSOC_RSP; + ie_len = frame_len - FIXED_PARAM_OFFSET_ASSOC_RSP; + qdf_status = wlan_parse_ftie_sha384(ie_ptr, ie_len, pAssocRsp); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + pe_err("FT IE parsing failed status:%d", status); + } else { + pe_debug("FT: R0KH present:%d len:%d R1KH present%d", + pAssocRsp->sha384_ft_subelem.r0kh_id.present, + pAssocRsp->sha384_ft_subelem.r0kh_id.num_PMK_R0_ID, + pAssocRsp->sha384_ft_subelem.r1kh_id.present); + ar->FTInfo.present = false; + } + } else if (ar->FTInfo.present) { + pe_debug("FT: R0KH present:%d, len:%d R1KH present:%d", + ar->FTInfo.R0KH_ID.present, + ar->FTInfo.R0KH_ID.num_PMK_R0_ID, + ar->FTInfo.R1KH_ID.present); + pAssocRsp->ftinfoPresent = 1; + qdf_mem_copy(&pAssocRsp->FTInfo, &ar->FTInfo, + sizeof(tDot11fIEFTInfo)); + } + + if (ar->num_RICDataDesc && ar->num_RICDataDesc <= 2) { + for (cnt = 0; cnt < ar->num_RICDataDesc; cnt++) { + if (ar->RICDataDesc[cnt].present) { + qdf_mem_copy(&pAssocRsp->RICData[cnt], + &ar->RICDataDesc[cnt], + sizeof(tDot11fIERICDataDesc)); + } + } + pAssocRsp->num_RICData = ar->num_RICDataDesc; + pAssocRsp->ricPresent = true; + } + +#ifdef FEATURE_WLAN_ESE + if (ar->num_WMMTSPEC) { + pAssocRsp->num_tspecs = ar->num_WMMTSPEC; + for (cnt = 0; cnt < ar->num_WMMTSPEC; cnt++) { + qdf_mem_copy(&pAssocRsp->TSPECInfo[cnt], + &ar->WMMTSPEC[cnt], + sizeof(tDot11fIEWMMTSPEC)); + } + pAssocRsp->tspecPresent = true; + } + + if (ar->ESETrafStrmMet.present) { + pAssocRsp->tsmPresent = 1; + qdf_mem_copy(&pAssocRsp->tsmIE.tsid, + &ar->ESETrafStrmMet.tsid, + sizeof(struct ese_tsm_ie)); + } +#endif + + if (ar->VHTCaps.present) { + qdf_mem_copy(&pAssocRsp->VHTCaps, &ar->VHTCaps, + sizeof(tDot11fIEVHTCaps)); + pe_debug("Received Assoc Response with VHT Cap"); + lim_log_vht_cap(mac, &pAssocRsp->VHTCaps); + } + if (ar->VHTOperation.present) { + qdf_mem_copy(&pAssocRsp->VHTOperation, &ar->VHTOperation, + sizeof(tDot11fIEVHTOperation)); + pe_debug("Received Assoc Response with VHT Operation"); + lim_log_vht_operation(mac, &pAssocRsp->VHTOperation); + } + + if (ar->ExtCap.present) { + struct s_ext_cap *ext_cap; + + qdf_mem_copy(&pAssocRsp->ExtCap, &ar->ExtCap, + sizeof(tDot11fIEExtCap)); + ext_cap = (struct s_ext_cap *)&pAssocRsp->ExtCap.bytes; + pe_debug("timingMeas: %d, finetimingMeas Init: %d, Resp: %d", + ext_cap->timing_meas, ext_cap->fine_time_meas_initiator, + ext_cap->fine_time_meas_responder); + } + + if (ar->QosMapSet.present) { + pAssocRsp->QosMapSet.present = 1; + convert_qos_mapset_frame(mac, &pAssocRsp->QosMapSet, + &ar->QosMapSet); + pe_debug("Received Assoc Response with Qos Map Set"); + lim_log_qos_map_set(mac, &pAssocRsp->QosMapSet); + } + + pAssocRsp->vendor_vht_ie.present = ar->vendor_vht_ie.present; + if (ar->vendor_vht_ie.present) + pAssocRsp->vendor_vht_ie.sub_type = ar->vendor_vht_ie.sub_type; + if (ar->OBSSScanParameters.present) { + qdf_mem_copy(&pAssocRsp->obss_scanparams, + &ar->OBSSScanParameters, + sizeof(struct sDot11fIEOBSSScanParameters)); + } + if (ar->vendor_vht_ie.VHTCaps.present) { + qdf_mem_copy(&pAssocRsp->vendor_vht_ie.VHTCaps, + &ar->vendor_vht_ie.VHTCaps, + sizeof(tDot11fIEVHTCaps)); + pe_debug("Received Assoc Response with Vendor specific VHT Cap"); + lim_log_vht_cap(mac, &pAssocRsp->VHTCaps); + } + if (ar->vendor_vht_ie.VHTOperation.present) { + qdf_mem_copy(&pAssocRsp->vendor_vht_ie.VHTOperation, + &ar->vendor_vht_ie.VHTOperation, + sizeof(tDot11fIEVHTOperation)); + pe_debug("Received Assoc Response with Vendor specific VHT Oper"); + lim_log_vht_operation(mac, &pAssocRsp->VHTOperation); + } + + if (ar->qcn_ie.present) + qdf_mem_copy(&pAssocRsp->qcn_ie, &ar->qcn_ie, + sizeof(tDot11fIEqcn_ie)); + if (ar->he_cap.present) { + pe_debug("11AX: HE cap IE present"); + qdf_mem_copy(&pAssocRsp->he_cap, &ar->he_cap, + sizeof(tDot11fIEhe_cap)); + } + if (ar->he_op.present) { + pe_debug("11AX: HE Operation IE present"); + qdf_mem_copy(&pAssocRsp->he_op, &ar->he_op, + sizeof(tDot11fIEhe_op)); + pe_debug("bss_clr %d def_pe %d part_bss_clr %d bss_col_dis %d", + pAssocRsp->he_op.bss_color, + pAssocRsp->he_op.default_pe, + pAssocRsp->he_op.partial_bss_col, + pAssocRsp->he_op.bss_col_disabled); + } + + if (ar->he_6ghz_band_cap.present) { + pe_debug("11AX: HE Band Capability IE present"); + qdf_mem_copy(&pAssocRsp->he_6ghz_band_cap, + &ar->he_6ghz_band_cap, + sizeof(tDot11fIEhe_6ghz_band_cap)); + } + + if (ar->mu_edca_param_set.present) { + pe_debug("11AX: HE MU EDCA param IE present"); + pAssocRsp->mu_edca_present = true; + convert_mu_edca_param(mac, &pAssocRsp->mu_edca, + &ar->mu_edca_param_set); + } + + if (ar->MBO_IE.present && ar->MBO_IE.rssi_assoc_rej.present) { + qdf_mem_copy(&pAssocRsp->rssi_assoc_rej, + &ar->MBO_IE.rssi_assoc_rej, + sizeof(tDot11fTLVrssi_assoc_rej)); + pe_debug("Received Assoc Response with rssi based assoc rej"); + } + + fils_convert_assoc_rsp_frame2_struct(ar, pAssocRsp); + + qdf_mem_free(ar); + return QDF_STATUS_SUCCESS; + +} /* End sir_convert_assoc_resp_frame2_struct. */ + +QDF_STATUS +sir_convert_reassoc_req_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, + uint32_t nFrame, tpSirAssocReq pAssocReq) +{ + tDot11fReAssocRequest *ar; + uint32_t status; + + ar = qdf_mem_malloc(sizeof(*ar)); + if (!ar) + return QDF_STATUS_E_NOMEM; + /* Zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) pAssocReq, sizeof(tSirAssocReq)); + + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_re_assoc_request(mac, pFrame, nFrame, + ar, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse a Re-association Request (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pFrame, nFrame); + qdf_mem_free(ar); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking a Re-association Request (0x%08x, %d bytes):", + status, nFrame); + } + /* & "transliterate" from a 'tDot11fReAssocRequest' to a 'tSirAssocReq'... */ + + /* make sure this is seen as a re-assoc request */ + pAssocReq->reassocRequest = 1; + + /* Capabilities */ + pAssocReq->capabilityInfo.ess = ar->Capabilities.ess; + pAssocReq->capabilityInfo.ibss = ar->Capabilities.ibss; + pAssocReq->capabilityInfo.cfPollable = ar->Capabilities.cfPollable; + pAssocReq->capabilityInfo.cfPollReq = ar->Capabilities.cfPollReq; + pAssocReq->capabilityInfo.privacy = ar->Capabilities.privacy; + pAssocReq->capabilityInfo.shortPreamble = ar->Capabilities.shortPreamble; + pAssocReq->capabilityInfo.pbcc = ar->Capabilities.pbcc; + pAssocReq->capabilityInfo.channelAgility = + ar->Capabilities.channelAgility; + pAssocReq->capabilityInfo.spectrumMgt = ar->Capabilities.spectrumMgt; + pAssocReq->capabilityInfo.qos = ar->Capabilities.qos; + pAssocReq->capabilityInfo.shortSlotTime = ar->Capabilities.shortSlotTime; + pAssocReq->capabilityInfo.apsd = ar->Capabilities.apsd; + pAssocReq->capabilityInfo.rrm = ar->Capabilities.rrm; + pAssocReq->capabilityInfo.dsssOfdm = ar->Capabilities.dsssOfdm; + pAssocReq->capabilityInfo.delayedBA = ar->Capabilities.delayedBA; + pAssocReq->capabilityInfo.immediateBA = ar->Capabilities.immediateBA; + + /* Listen Interval */ + pAssocReq->listenInterval = ar->ListenInterval.interval; + + /* SSID */ + if (ar->SSID.present) { + pAssocReq->ssidPresent = 1; + convert_ssid(mac, &pAssocReq->ssId, &ar->SSID); + } + /* Supported Rates */ + if (ar->SuppRates.present) { + pAssocReq->suppRatesPresent = 1; + convert_supp_rates(mac, &pAssocReq->supportedRates, + &ar->SuppRates); + } + /* Extended Supported Rates */ + if (ar->ExtSuppRates.present) { + pAssocReq->extendedRatesPresent = 1; + convert_ext_supp_rates(mac, &pAssocReq->extendedRates, + &ar->ExtSuppRates); + } + /* QOS Capabilities: */ + if (ar->QOSCapsStation.present) { + pAssocReq->qosCapabilityPresent = 1; + convert_qos_caps_station(mac, &pAssocReq->qosCapability, + &ar->QOSCapsStation); + } + /* WPA */ + if (ar->WPAOpaque.present) { + pAssocReq->wpaPresent = 1; + convert_wpa_opaque(mac, &pAssocReq->wpa, &ar->WPAOpaque); + } + /* RSN */ + if (ar->RSNOpaque.present) { + pAssocReq->rsnPresent = 1; + convert_rsn_opaque(mac, &pAssocReq->rsn, &ar->RSNOpaque); + } + + /* Power Capabilities */ + if (ar->PowerCaps.present) { + pAssocReq->powerCapabilityPresent = 1; + convert_power_caps(mac, &pAssocReq->powerCapability, + &ar->PowerCaps); + } + /* Supported Channels */ + if (ar->SuppChannels.present) { + pAssocReq->supportedChannelsPresent = 1; + convert_supp_channels(mac, &pAssocReq->supportedChannels, + &ar->SuppChannels); + } + + if (ar->HTCaps.present) { + qdf_mem_copy(&pAssocReq->HTCaps, &ar->HTCaps, + sizeof(tDot11fIEHTCaps)); + } + + if (ar->WMMInfoStation.present) { + pAssocReq->wmeInfoPresent = 1; + qdf_mem_copy(&pAssocReq->WMMInfoStation, &ar->WMMInfoStation, + sizeof(tDot11fIEWMMInfoStation)); + + } + + if (ar->WMMCaps.present) + pAssocReq->wsmCapablePresent = 1; + + if (!pAssocReq->ssidPresent) { + pe_debug("Received Assoc without SSID IE"); + qdf_mem_free(ar); + return QDF_STATUS_E_FAILURE; + } + + if (!pAssocReq->suppRatesPresent && !pAssocReq->extendedRatesPresent) { + pe_debug("Received Assoc without supp rate IE"); + qdf_mem_free(ar); + return QDF_STATUS_E_FAILURE; + } + /* Why no call to 'updateAssocReqFromPropCapability' here, like */ + /* there is in 'sir_convert_assoc_req_frame2_struct'? */ + + /* WSC IE */ + if (ar->WscIEOpaque.present) { + pAssocReq->addIEPresent = 1; + convert_wsc_opaque(mac, &pAssocReq->addIE, &ar->WscIEOpaque); + } + + if (ar->P2PIEOpaque.present) { + pAssocReq->addIEPresent = 1; + convert_p2p_opaque(mac, &pAssocReq->addIE, &ar->P2PIEOpaque); + } +#ifdef WLAN_FEATURE_WFD + if (ar->WFDIEOpaque.present) { + pAssocReq->addIEPresent = 1; + convert_wfd_opaque(mac, &pAssocReq->addIE, &ar->WFDIEOpaque); + } +#endif + if (ar->SuppOperatingClasses.present) { + uint8_t num_classes = ar->SuppOperatingClasses.num_classes; + + if (num_classes > sizeof(ar->SuppOperatingClasses.classes)) + num_classes = + sizeof(ar->SuppOperatingClasses.classes); + qdf_mem_copy(&pAssocReq->supp_operating_classes, + &ar->SuppOperatingClasses, + sizeof(tDot11fIESuppOperatingClasses)); + QDF_TRACE_HEX_DUMP( + QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + ar->SuppOperatingClasses.classes, num_classes); + } + if (ar->VHTCaps.present) { + qdf_mem_copy(&pAssocReq->VHTCaps, &ar->VHTCaps, + sizeof(tDot11fIEVHTCaps)); + } + if (ar->OperatingMode.present) { + qdf_mem_copy(&pAssocReq->operMode, &ar->OperatingMode, + sizeof(tDot11fIEOperatingMode)); + pe_warn("Received Assoc Req with Operating Mode IE"); + lim_log_operating_mode(mac, &pAssocReq->operMode); + } + if (ar->ExtCap.present) { + struct s_ext_cap *ext_cap; + + qdf_mem_copy(&pAssocReq->ExtCap, &ar->ExtCap, + sizeof(tDot11fIEExtCap)); + ext_cap = (struct s_ext_cap *)&pAssocReq->ExtCap.bytes; + pe_debug("timingMeas: %d, finetimingMeas Init: %d, Resp: %d", + ext_cap->timing_meas, ext_cap->fine_time_meas_initiator, + ext_cap->fine_time_meas_responder); + } + if (ar->he_cap.present) { + qdf_mem_copy(&pAssocReq->he_cap, &ar->he_cap, + sizeof(tDot11fIEhe_cap)); + } + if (ar->he_6ghz_band_cap.present) { + qdf_mem_copy(&pAssocReq->he_6ghz_band_cap, + &ar->he_6ghz_band_cap, + sizeof(tDot11fIEhe_6ghz_band_cap)); + } + + qdf_mem_free(ar); + + return QDF_STATUS_SUCCESS; + +} /* End sir_convert_reassoc_req_frame2_struct. */ + +#ifdef FEATURE_WLAN_ESE +QDF_STATUS +sir_beacon_ie_ese_bcn_report(struct mac_context *mac, + uint8_t *pPayload, const uint32_t nPayload, + uint8_t **outIeBuf, uint32_t *pOutIeLen) +{ + tDot11fBeaconIEs *pBies = NULL; + uint32_t status = QDF_STATUS_SUCCESS; + QDF_STATUS retStatus = QDF_STATUS_SUCCESS; + tSirEseBcnReportMandatoryIe eseBcnReportMandatoryIe; + + /* To store how many bytes are required to be allocated + for Bcn report mandatory Ies */ + uint16_t numBytes = 0, freeBytes = 0; + uint8_t *pos = NULL; + + /* Zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) &eseBcnReportMandatoryIe, + sizeof(eseBcnReportMandatoryIe)); + pBies = qdf_mem_malloc(sizeof(tDot11fBeaconIEs)); + if (!pBies) + return QDF_STATUS_E_NOMEM; + qdf_mem_zero(pBies, sizeof(tDot11fBeaconIEs)); + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_beacon_i_es(mac, pPayload, nPayload, + pBies, false); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse Beacon IEs (0x%08x, %d bytes):", + status, nPayload); + qdf_mem_free(pBies); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking Beacon IEs (0x%08x, %d bytes):", + status, nPayload); + } + /* & "transliterate" from a 'tDot11fBeaconIEs' to a 'eseBcnReportMandatoryIe'... */ + if (!pBies->SSID.present) { + pe_debug("Mandatory IE SSID not present!"); + } else { + eseBcnReportMandatoryIe.ssidPresent = 1; + convert_ssid(mac, &eseBcnReportMandatoryIe.ssId, &pBies->SSID); + /* 1 for EID, 1 for length and length bytes */ + numBytes += 1 + 1 + eseBcnReportMandatoryIe.ssId.length; + } + + if (!pBies->SuppRates.present) { + pe_debug_rl("Mandatory IE Supported Rates not present!"); + } else { + eseBcnReportMandatoryIe.suppRatesPresent = 1; + convert_supp_rates(mac, &eseBcnReportMandatoryIe.supportedRates, + &pBies->SuppRates); + numBytes += + 1 + 1 + eseBcnReportMandatoryIe.supportedRates.numRates; + } + + if (pBies->FHParamSet.present) { + eseBcnReportMandatoryIe.fhParamPresent = 1; + convert_fh_params(mac, &eseBcnReportMandatoryIe.fhParamSet, + &pBies->FHParamSet); + numBytes += 1 + 1 + WLAN_FH_PARAM_IE_MAX_LEN; + } + + if (pBies->DSParams.present) { + eseBcnReportMandatoryIe.dsParamsPresent = 1; + eseBcnReportMandatoryIe.dsParamSet.channelNumber = + pBies->DSParams.curr_channel; + numBytes += 1 + 1 + WLAN_DS_PARAM_IE_MAX_LEN; + } + + if (pBies->CFParams.present) { + eseBcnReportMandatoryIe.cfPresent = 1; + convert_cf_params(mac, &eseBcnReportMandatoryIe.cfParamSet, + &pBies->CFParams); + numBytes += 1 + 1 + WLAN_CF_PARAM_IE_MAX_LEN; + } + + if (pBies->IBSSParams.present) { + eseBcnReportMandatoryIe.ibssParamPresent = 1; + eseBcnReportMandatoryIe.ibssParamSet.atim = + pBies->IBSSParams.atim; + numBytes += 1 + 1 + WLAN_IBSS_IE_MAX_LEN; + } + + if (pBies->TIM.present) { + eseBcnReportMandatoryIe.timPresent = 1; + eseBcnReportMandatoryIe.tim.dtimCount = pBies->TIM.dtim_count; + eseBcnReportMandatoryIe.tim.dtimPeriod = pBies->TIM.dtim_period; + eseBcnReportMandatoryIe.tim.bitmapControl = pBies->TIM.bmpctl; + /* As per the ESE spec, May truncate and report first 4 octets only */ + numBytes += 1 + 1 + SIR_MAC_TIM_EID_MIN; + } + + if (pBies->RRMEnabledCap.present) { + eseBcnReportMandatoryIe.rrmPresent = 1; + qdf_mem_copy(&eseBcnReportMandatoryIe.rmEnabledCapabilities, + &pBies->RRMEnabledCap, + sizeof(tDot11fIERRMEnabledCap)); + numBytes += 1 + 1 + WLAN_RM_CAPABILITY_IE_MAX_LEN; + } + + *outIeBuf = qdf_mem_malloc(numBytes); + if (!*outIeBuf) { + qdf_mem_free(pBies); + return QDF_STATUS_E_NOMEM; + } + pos = *outIeBuf; + *pOutIeLen = numBytes; + freeBytes = numBytes; + + /* Start filling the output Ie with Mandatory IE information */ + /* Fill SSID IE */ + if (eseBcnReportMandatoryIe.ssidPresent) { + if (freeBytes < (1 + 1 + eseBcnReportMandatoryIe.ssId.length)) { + pe_err("Insufficient memory to copy SSID"); + retStatus = QDF_STATUS_E_FAILURE; + goto err_bcnrep; + } + *pos = WLAN_ELEMID_SSID; + pos++; + *pos = eseBcnReportMandatoryIe.ssId.length; + pos++; + qdf_mem_copy(pos, + (uint8_t *) eseBcnReportMandatoryIe.ssId.ssId, + eseBcnReportMandatoryIe.ssId.length); + pos += eseBcnReportMandatoryIe.ssId.length; + freeBytes -= (1 + 1 + eseBcnReportMandatoryIe.ssId.length); + } + + /* Fill Supported Rates IE */ + if (eseBcnReportMandatoryIe.suppRatesPresent) { + if (freeBytes < + (1 + 1 + eseBcnReportMandatoryIe.supportedRates.numRates)) { + pe_err("Insufficient memory to copy Rates IE"); + retStatus = QDF_STATUS_E_FAILURE; + goto err_bcnrep; + } + if (eseBcnReportMandatoryIe.supportedRates.numRates <= + WLAN_SUPPORTED_RATES_IE_MAX_LEN) { + *pos = WLAN_ELEMID_RATES; + pos++; + *pos = eseBcnReportMandatoryIe.supportedRates.numRates; + pos++; + qdf_mem_copy(pos, + (uint8_t *) eseBcnReportMandatoryIe.supportedRates. + rate, + eseBcnReportMandatoryIe.supportedRates.numRates); + pos += eseBcnReportMandatoryIe.supportedRates.numRates; + freeBytes -= + (1 + 1 + + eseBcnReportMandatoryIe.supportedRates.numRates); + } + } + + /* Fill FH Parameter set IE */ + if (eseBcnReportMandatoryIe.fhParamPresent) { + if (freeBytes < (1 + 1 + WLAN_FH_PARAM_IE_MAX_LEN)) { + pe_err("Insufficient memory to copy FHIE"); + retStatus = QDF_STATUS_E_FAILURE; + goto err_bcnrep; + } + *pos = WLAN_ELEMID_FHPARMS; + pos++; + *pos = WLAN_FH_PARAM_IE_MAX_LEN; + pos++; + qdf_mem_copy(pos, + (uint8_t *) &eseBcnReportMandatoryIe.fhParamSet, + WLAN_FH_PARAM_IE_MAX_LEN); + pos += WLAN_FH_PARAM_IE_MAX_LEN; + freeBytes -= (1 + 1 + WLAN_FH_PARAM_IE_MAX_LEN); + } + + /* Fill DS Parameter set IE */ + if (eseBcnReportMandatoryIe.dsParamsPresent) { + if (freeBytes < (1 + 1 + WLAN_DS_PARAM_IE_MAX_LEN)) { + pe_err("Insufficient memory to copy DS IE"); + retStatus = QDF_STATUS_E_FAILURE; + goto err_bcnrep; + } + *pos = WLAN_ELEMID_DSPARMS; + pos++; + *pos = WLAN_DS_PARAM_IE_MAX_LEN; + pos++; + *pos = eseBcnReportMandatoryIe.dsParamSet.channelNumber; + pos += WLAN_DS_PARAM_IE_MAX_LEN; + freeBytes -= (1 + 1 + WLAN_DS_PARAM_IE_MAX_LEN); + } + + /* Fill CF Parameter set */ + if (eseBcnReportMandatoryIe.cfPresent) { + if (freeBytes < (1 + 1 + WLAN_CF_PARAM_IE_MAX_LEN)) { + pe_err("Insufficient memory to copy CF IE"); + retStatus = QDF_STATUS_E_FAILURE; + goto err_bcnrep; + } + *pos = WLAN_ELEMID_CFPARMS; + pos++; + *pos = WLAN_CF_PARAM_IE_MAX_LEN; + pos++; + qdf_mem_copy(pos, + (uint8_t *) &eseBcnReportMandatoryIe.cfParamSet, + WLAN_CF_PARAM_IE_MAX_LEN); + pos += WLAN_CF_PARAM_IE_MAX_LEN; + freeBytes -= (1 + 1 + WLAN_CF_PARAM_IE_MAX_LEN); + } + + /* Fill IBSS Parameter set IE */ + if (eseBcnReportMandatoryIe.ibssParamPresent) { + if (freeBytes < (1 + 1 + WLAN_IBSS_IE_MAX_LEN)) { + pe_err("Insufficient memory to copy IBSS IE"); + retStatus = QDF_STATUS_E_FAILURE; + goto err_bcnrep; + } + *pos = WLAN_ELEMID_IBSSPARMS; + pos++; + *pos = WLAN_IBSS_IE_MAX_LEN; + pos++; + qdf_mem_copy(pos, + (uint8_t *) &eseBcnReportMandatoryIe.ibssParamSet. + atim, WLAN_IBSS_IE_MAX_LEN); + pos += WLAN_IBSS_IE_MAX_LEN; + freeBytes -= (1 + 1 + WLAN_IBSS_IE_MAX_LEN); + } + + /* Fill TIM IE */ + if (eseBcnReportMandatoryIe.timPresent) { + if (freeBytes < (1 + 1 + SIR_MAC_TIM_EID_MIN)) { + pe_err("Insufficient memory to copy TIM IE"); + retStatus = QDF_STATUS_E_FAILURE; + goto err_bcnrep; + } + *pos = WLAN_ELEMID_TIM; + pos++; + *pos = SIR_MAC_TIM_EID_MIN; + pos++; + qdf_mem_copy(pos, + (uint8_t *) &eseBcnReportMandatoryIe.tim, + SIR_MAC_TIM_EID_MIN); + pos += SIR_MAC_TIM_EID_MIN; + freeBytes -= (1 + 1 + SIR_MAC_TIM_EID_MIN); + } + + /* Fill RM Capability IE */ + if (eseBcnReportMandatoryIe.rrmPresent) { + if (freeBytes < (1 + 1 + WLAN_RM_CAPABILITY_IE_MAX_LEN)) { + pe_err("Insufficient memory to copy RRM IE"); + retStatus = QDF_STATUS_E_FAILURE; + goto err_bcnrep; + } + *pos = WLAN_ELEMID_RRM; + pos++; + *pos = WLAN_RM_CAPABILITY_IE_MAX_LEN; + pos++; + qdf_mem_copy(pos, + (uint8_t *) &eseBcnReportMandatoryIe. + rmEnabledCapabilities, + WLAN_RM_CAPABILITY_IE_MAX_LEN); + freeBytes -= (1 + 1 + WLAN_RM_CAPABILITY_IE_MAX_LEN); + } + + if (freeBytes != 0) { + pe_err("Mismatch in allocation and copying of IE in Bcn Rep"); + retStatus = QDF_STATUS_E_FAILURE; + } + +err_bcnrep: + /* The message counter would not be incremented in case of + * returning failure and hence next time, this function gets + * called, it would be using the same msg ctr for a different + * BSS.So, it is good to clear the memory allocated for a BSS + * that is returning failure.On success, the caller would take + * care of freeing up the memory*/ + if (retStatus == QDF_STATUS_E_FAILURE) { + qdf_mem_free(*outIeBuf); + *outIeBuf = NULL; + } + + qdf_mem_free(pBies); + return retStatus; +} + +#endif /* FEATURE_WLAN_ESE */ + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +static void update_bss_color_change_from_beacon_ies(tDot11fBeaconIEs *bcn_ies, + tpSirProbeRespBeacon bcn_struct) +{ + if (bcn_ies->bss_color_change.present) { + qdf_mem_copy(&bcn_struct->vendor_he_bss_color_change, + &bcn_ies->bss_color_change, + sizeof(tDot11fIEbss_color_change)); + } +} +#else +static inline void update_bss_color_change_from_beacon_ies( + tDot11fBeaconIEs *bcn_ies, + tpSirProbeRespBeacon bcn_struct) +{} +#endif + +QDF_STATUS +sir_parse_beacon_ie(struct mac_context *mac, + tpSirProbeRespBeacon pBeaconStruct, + uint8_t *pPayload, uint32_t nPayload) +{ + tDot11fBeaconIEs *pBies; + uint32_t status; + + /* Zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) pBeaconStruct, sizeof(tSirProbeRespBeacon)); + + pBies = qdf_mem_malloc(sizeof(tDot11fBeaconIEs)); + if (!pBies) + return QDF_STATUS_E_NOMEM; + qdf_mem_zero(pBies, sizeof(tDot11fBeaconIEs)); + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_beacon_i_es(mac, pPayload, nPayload, + pBies, false); + + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse Beacon IEs (0x%08x, %d bytes):", + status, nPayload); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pPayload, nPayload); + qdf_mem_free(pBies); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("warnings (0x%08x, %d bytes):", status, nPayload); + } + /* & "transliterate" from a 'tDot11fBeaconIEs' to a 'tSirProbeRespBeacon'... */ + if (!pBies->SSID.present) { + pe_debug("Mandatory IE SSID not present!"); + } else { + pBeaconStruct->ssidPresent = 1; + convert_ssid(mac, &pBeaconStruct->ssId, &pBies->SSID); + } + + if (!pBies->SuppRates.present) { + pe_debug_rl("Mandatory IE Supported Rates not present!"); + } else { + pBeaconStruct->suppRatesPresent = 1; + convert_supp_rates(mac, &pBeaconStruct->supportedRates, + &pBies->SuppRates); + } + + if (pBies->ExtSuppRates.present) { + pBeaconStruct->extendedRatesPresent = 1; + convert_ext_supp_rates(mac, &pBeaconStruct->extendedRates, + &pBies->ExtSuppRates); + } + + if (pBies->CFParams.present) { + pBeaconStruct->cfPresent = 1; + convert_cf_params(mac, &pBeaconStruct->cfParamSet, + &pBies->CFParams); + } + + if (pBies->TIM.present) { + pBeaconStruct->timPresent = 1; + convert_tim(mac, &pBeaconStruct->tim, &pBies->TIM); + } + + if (pBies->Country.present) { + pBeaconStruct->countryInfoPresent = 1; + convert_country(mac, &pBeaconStruct->countryInfoParam, + &pBies->Country); + } + /* 11h IEs */ + if (pBies->TPCReport.present) { + pBeaconStruct->tpcReportPresent = 1; + qdf_mem_copy(&pBeaconStruct->tpcReport, + &pBies->TPCReport, sizeof(tDot11fIETPCReport)); + } + + if (pBies->PowerConstraints.present) { + pBeaconStruct->powerConstraintPresent = 1; + qdf_mem_copy(&pBeaconStruct->localPowerConstraint, + &pBies->PowerConstraints, + sizeof(tDot11fIEPowerConstraints)); + } +#ifdef FEATURE_WLAN_ESE + if (pBies->ESEVersion.present) + pBeaconStruct->is_ese_ver_ie_present = 1; + if (pBies->ESETxmitPower.present) { + pBeaconStruct->eseTxPwr.present = 1; + pBeaconStruct->eseTxPwr.power_limit = + pBies->ESETxmitPower.power_limit; + } + if (pBies->QBSSLoad.present) { + qdf_mem_copy(&pBeaconStruct->QBSSLoad, &pBies->QBSSLoad, + sizeof(tDot11fIEQBSSLoad)); + } +#endif + + if (pBies->EDCAParamSet.present) { + pBeaconStruct->edcaPresent = 1; + convert_edca_param(mac, &pBeaconStruct->edcaParams, + &pBies->EDCAParamSet); + } + /* QOS Capabilities: */ + if (pBies->QOSCapsAp.present) { + pBeaconStruct->qosCapabilityPresent = 1; + convert_qos_caps(mac, &pBeaconStruct->qosCapability, + &pBies->QOSCapsAp); + } + + if (pBies->ChanSwitchAnn.present) { + pBeaconStruct->channelSwitchPresent = 1; + qdf_mem_copy(&pBeaconStruct->channelSwitchIE, + &pBies->ChanSwitchAnn, + sizeof(pBeaconStruct->channelSwitchIE)); + } + + if (pBies->SuppOperatingClasses.present) { + pBeaconStruct->supp_operating_class_present = 1; + qdf_mem_copy(&pBeaconStruct->supp_operating_classes, + &pBies->SuppOperatingClasses, + sizeof(tDot11fIESuppOperatingClasses)); + } + + if (pBies->ext_chan_switch_ann.present) { + pBeaconStruct->ext_chan_switch_present = 1; + qdf_mem_copy(&pBeaconStruct->ext_chan_switch, + &pBies->ext_chan_switch_ann, + sizeof(tDot11fIEext_chan_switch_ann)); + } + + if (pBies->sec_chan_offset_ele.present) { + pBeaconStruct->sec_chan_offset_present = 1; + qdf_mem_copy(&pBeaconStruct->sec_chan_offset, + &pBies->sec_chan_offset_ele, + sizeof(pBeaconStruct->sec_chan_offset)); + } + + if (pBies->Quiet.present) { + pBeaconStruct->quietIEPresent = 1; + qdf_mem_copy(&pBeaconStruct->quietIE, &pBies->Quiet, + sizeof(tDot11fIEQuiet)); + } + + if (pBies->HTCaps.present) { + qdf_mem_copy(&pBeaconStruct->HTCaps, &pBies->HTCaps, + sizeof(tDot11fIEHTCaps)); + } + + if (pBies->HTInfo.present) { + qdf_mem_copy(&pBeaconStruct->HTInfo, &pBies->HTInfo, + sizeof(tDot11fIEHTInfo)); + } + + if (pBies->he_op.oper_info_6g_present) { + pBeaconStruct->chan_freq = wlan_reg_chan_band_to_freq(mac->pdev, + pBies->he_op.oper_info_6g.info.primary_ch, + BIT(REG_BAND_6G)); + } else if (pBies->DSParams.present) { + pBeaconStruct->dsParamsPresent = 1; + pBeaconStruct->chan_freq = + wlan_reg_legacy_chan_to_freq(mac->pdev, + pBies->DSParams.curr_channel); + } else if (pBies->HTInfo.present) { + pBeaconStruct->chan_freq = + wlan_reg_legacy_chan_to_freq(mac->pdev, + pBies->HTInfo.primaryChannel); + } + + if (pBies->RSN.present) { + pBeaconStruct->rsnPresent = 1; + convert_rsn(mac, &pBeaconStruct->rsn, &pBies->RSN); + } + + if (pBies->WPA.present) { + pBeaconStruct->wpaPresent = 1; + convert_wpa(mac, &pBeaconStruct->wpa, &pBies->WPA); + } + + if (pBies->WMMParams.present) { + pBeaconStruct->wmeEdcaPresent = 1; + convert_wmm_params(mac, &pBeaconStruct->edcaParams, + &pBies->WMMParams); + } + + if (pBies->WMMInfoAp.present) { + pBeaconStruct->wmeInfoPresent = 1; + } + + if (pBies->WMMCaps.present) { + pBeaconStruct->wsmCapablePresent = 1; + } + + if (pBies->ERPInfo.present) { + pBeaconStruct->erpPresent = 1; + convert_erp_info(mac, &pBeaconStruct->erpIEInfo, + &pBies->ERPInfo); + } + if (pBies->VHTCaps.present) { + pBeaconStruct->VHTCaps.present = 1; + qdf_mem_copy(&pBeaconStruct->VHTCaps, &pBies->VHTCaps, + sizeof(tDot11fIEVHTCaps)); + } + if (pBies->VHTOperation.present) { + pBeaconStruct->VHTOperation.present = 1; + qdf_mem_copy(&pBeaconStruct->VHTOperation, &pBies->VHTOperation, + sizeof(tDot11fIEVHTOperation)); + } + if (pBies->VHTExtBssLoad.present) { + pBeaconStruct->VHTExtBssLoad.present = 1; + qdf_mem_copy(&pBeaconStruct->VHTExtBssLoad, + &pBies->VHTExtBssLoad, + sizeof(tDot11fIEVHTExtBssLoad)); + } + if (pBies->OperatingMode.present) { + pBeaconStruct->OperatingMode.present = 1; + qdf_mem_copy(&pBeaconStruct->OperatingMode, + &pBies->OperatingMode, + sizeof(tDot11fIEOperatingMode)); + } + if (pBies->MobilityDomain.present) { + pBeaconStruct->mdiePresent = 1; + qdf_mem_copy(pBeaconStruct->mdie, &pBies->MobilityDomain.MDID, + SIR_MDIE_SIZE); + } + + pBeaconStruct->Vendor1IEPresent = pBies->Vendor1IE.present; + pBeaconStruct->Vendor3IEPresent = pBies->Vendor3IE.present; + pBeaconStruct->vendor_vht_ie.present = pBies->vendor_vht_ie.present; + if (pBies->vendor_vht_ie.present) + pBeaconStruct->vendor_vht_ie.sub_type = + pBies->vendor_vht_ie.sub_type; + + if (pBies->vendor_vht_ie.VHTCaps.present) { + pBeaconStruct->vendor_vht_ie.VHTCaps.present = 1; + qdf_mem_copy(&pBeaconStruct->vendor_vht_ie.VHTCaps, + &pBies->vendor_vht_ie.VHTCaps, + sizeof(tDot11fIEVHTCaps)); + } + if (pBies->vendor_vht_ie.VHTOperation.present) { + pBeaconStruct->vendor_vht_ie.VHTOperation.present = 1; + qdf_mem_copy(&pBeaconStruct->vendor_vht_ie.VHTOperation, + &pBies->vendor_vht_ie.VHTOperation, + sizeof(tDot11fIEVHTOperation)); + } + if (pBies->ExtCap.present) { + qdf_mem_copy(&pBeaconStruct->ext_cap, &pBies->ExtCap, + sizeof(tDot11fIEExtCap)); + } + /* Update HS 2.0 Information Element */ + if (pBies->hs20vendor_ie.present) { + pe_debug("HS20 Indication Element Present, rel#:%u, id:%u", + pBies->hs20vendor_ie.release_num, + pBies->hs20vendor_ie.hs_id_present); + qdf_mem_copy(&pBeaconStruct->hs20vendor_ie, + &pBies->hs20vendor_ie, + sizeof(tDot11fIEhs20vendor_ie) - + sizeof(pBies->hs20vendor_ie.hs_id)); + if (pBies->hs20vendor_ie.hs_id_present) + qdf_mem_copy(&pBeaconStruct->hs20vendor_ie.hs_id, + &pBies->hs20vendor_ie.hs_id, + sizeof(pBies->hs20vendor_ie.hs_id)); + } + + if (pBies->MBO_IE.present) { + pBeaconStruct->MBO_IE_present = true; + if (pBies->MBO_IE.cellular_data_cap.present) + pBeaconStruct->MBO_capability = + pBies->MBO_IE.cellular_data_cap.cellular_connectivity; + + if (pBies->MBO_IE.assoc_disallowed.present) { + pBeaconStruct->assoc_disallowed = true; + pBeaconStruct->assoc_disallowed_reason = + pBies->MBO_IE.assoc_disallowed.reason_code; + } + } + + if (pBies->qcn_ie.present) + qdf_mem_copy(&pBeaconStruct->qcn_ie, &pBies->qcn_ie, + sizeof(tDot11fIEqcn_ie)); + + if (pBies->he_cap.present) { + qdf_mem_copy(&pBeaconStruct->he_cap, &pBies->he_cap, + sizeof(tDot11fIEhe_cap)); + } + if (pBies->he_op.present) { + qdf_mem_copy(&pBeaconStruct->he_op, &pBies->he_op, + sizeof(tDot11fIEhe_op)); + } + + update_bss_color_change_from_beacon_ies(pBies, pBeaconStruct); + + qdf_mem_free(pBies); + return QDF_STATUS_SUCCESS; +} /* End sir_parse_beacon_ie. */ + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +static void convert_bcon_bss_color_change_ie(tDot11fBeacon *bcn_frm, + tpSirProbeRespBeacon bcn_struct) +{ + if (bcn_frm->bss_color_change.present) { + pe_debug("11AX: HE BSS color change present"); + qdf_mem_copy(&bcn_struct->vendor_he_bss_color_change, + &bcn_frm->bss_color_change, + sizeof(tDot11fIEbss_color_change)); + } +} +#else +static inline void convert_bcon_bss_color_change_ie(tDot11fBeacon *bcn_frm, + tpSirProbeRespBeacon bcn_struct) +{} +#endif + +QDF_STATUS +sir_convert_beacon_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, + tpSirProbeRespBeacon pBeaconStruct) +{ + tDot11fBeacon *pBeacon; + uint32_t status, nPayload; + uint8_t *pPayload; + tpSirMacMgmtHdr pHdr; + + pPayload = WMA_GET_RX_MPDU_DATA(pFrame); + nPayload = WMA_GET_RX_PAYLOAD_LEN(pFrame); + pHdr = WMA_GET_RX_MAC_HEADER(pFrame); + + /* Zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) pBeaconStruct, sizeof(tSirProbeRespBeacon)); + + pBeacon = qdf_mem_malloc(sizeof(tDot11fBeacon)); + if (!pBeacon) + return QDF_STATUS_E_NOMEM; + + /* get the MAC address out of the BD, */ + qdf_mem_copy(pBeaconStruct->bssid, pHdr->sa, 6); + + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_beacon(mac, pPayload, nPayload, pBeacon, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse Beacon IEs (0x%08x, %d bytes):", + status, nPayload); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + pPayload, nPayload); + qdf_mem_free(pBeacon); + return QDF_STATUS_E_FAILURE; + } + /* & "transliterate" from a 'tDot11fBeacon' to a 'tSirProbeRespBeacon'... */ + /* Timestamp */ + qdf_mem_copy((uint8_t *) pBeaconStruct->timeStamp, + (uint8_t *) &pBeacon->TimeStamp, + sizeof(tSirMacTimeStamp)); + + /* Beacon Interval */ + pBeaconStruct->beaconInterval = pBeacon->BeaconInterval.interval; + + /* Capabilities */ + pBeaconStruct->capabilityInfo.ess = pBeacon->Capabilities.ess; + pBeaconStruct->capabilityInfo.ibss = pBeacon->Capabilities.ibss; + pBeaconStruct->capabilityInfo.cfPollable = + pBeacon->Capabilities.cfPollable; + pBeaconStruct->capabilityInfo.cfPollReq = + pBeacon->Capabilities.cfPollReq; + pBeaconStruct->capabilityInfo.privacy = pBeacon->Capabilities.privacy; + pBeaconStruct->capabilityInfo.shortPreamble = + pBeacon->Capabilities.shortPreamble; + pBeaconStruct->capabilityInfo.pbcc = pBeacon->Capabilities.pbcc; + pBeaconStruct->capabilityInfo.channelAgility = + pBeacon->Capabilities.channelAgility; + pBeaconStruct->capabilityInfo.spectrumMgt = + pBeacon->Capabilities.spectrumMgt; + pBeaconStruct->capabilityInfo.qos = pBeacon->Capabilities.qos; + pBeaconStruct->capabilityInfo.shortSlotTime = + pBeacon->Capabilities.shortSlotTime; + pBeaconStruct->capabilityInfo.apsd = pBeacon->Capabilities.apsd; + pBeaconStruct->capabilityInfo.rrm = pBeacon->Capabilities.rrm; + pBeaconStruct->capabilityInfo.dsssOfdm = pBeacon->Capabilities.dsssOfdm; + pBeaconStruct->capabilityInfo.delayedBA = + pBeacon->Capabilities.delayedBA; + pBeaconStruct->capabilityInfo.immediateBA = + pBeacon->Capabilities.immediateBA; + + if (!pBeacon->SSID.present) { + pe_debug("Mandatory IE SSID not present!"); + } else { + pBeaconStruct->ssidPresent = 1; + convert_ssid(mac, &pBeaconStruct->ssId, &pBeacon->SSID); + } + + if (!pBeacon->SuppRates.present) { + pe_debug_rl("Mandatory IE Supported Rates not present!"); + } else { + pBeaconStruct->suppRatesPresent = 1; + convert_supp_rates(mac, &pBeaconStruct->supportedRates, + &pBeacon->SuppRates); + } + + if (pBeacon->ExtSuppRates.present) { + pBeaconStruct->extendedRatesPresent = 1; + convert_ext_supp_rates(mac, &pBeaconStruct->extendedRates, + &pBeacon->ExtSuppRates); + } + + if (pBeacon->CFParams.present) { + pBeaconStruct->cfPresent = 1; + convert_cf_params(mac, &pBeaconStruct->cfParamSet, + &pBeacon->CFParams); + } + + if (pBeacon->TIM.present) { + pBeaconStruct->timPresent = 1; + convert_tim(mac, &pBeaconStruct->tim, &pBeacon->TIM); + } + + if (pBeacon->Country.present) { + pBeaconStruct->countryInfoPresent = 1; + convert_country(mac, &pBeaconStruct->countryInfoParam, + &pBeacon->Country); + } + /* QOS Capabilities: */ + if (pBeacon->QOSCapsAp.present) { + pBeaconStruct->qosCapabilityPresent = 1; + convert_qos_caps(mac, &pBeaconStruct->qosCapability, + &pBeacon->QOSCapsAp); + } + + if (pBeacon->EDCAParamSet.present) { + pBeaconStruct->edcaPresent = 1; + convert_edca_param(mac, &pBeaconStruct->edcaParams, + &pBeacon->EDCAParamSet); + } + + if (pBeacon->ChanSwitchAnn.present) { + pBeaconStruct->channelSwitchPresent = 1; + qdf_mem_copy(&pBeaconStruct->channelSwitchIE, + &pBeacon->ChanSwitchAnn, + sizeof(pBeaconStruct->channelSwitchIE)); + } + + if (pBeacon->ext_chan_switch_ann.present) { + pBeaconStruct->ext_chan_switch_present = 1; + qdf_mem_copy(&pBeaconStruct->ext_chan_switch, + &pBeacon->ext_chan_switch_ann, + sizeof(tDot11fIEext_chan_switch_ann)); + } + + if (pBeacon->sec_chan_offset_ele.present) { + pBeaconStruct->sec_chan_offset_present = 1; + qdf_mem_copy(&pBeaconStruct->sec_chan_offset, + &pBeacon->sec_chan_offset_ele, + sizeof(pBeaconStruct->sec_chan_offset)); + } + + if (pBeacon->TPCReport.present) { + pBeaconStruct->tpcReportPresent = 1; + qdf_mem_copy(&pBeaconStruct->tpcReport, &pBeacon->TPCReport, + sizeof(tDot11fIETPCReport)); + } + + if (pBeacon->PowerConstraints.present) { + pBeaconStruct->powerConstraintPresent = 1; + qdf_mem_copy(&pBeaconStruct->localPowerConstraint, + &pBeacon->PowerConstraints, + sizeof(tDot11fIEPowerConstraints)); + } + + if (pBeacon->Quiet.present) { + pBeaconStruct->quietIEPresent = 1; + qdf_mem_copy(&pBeaconStruct->quietIE, &pBeacon->Quiet, + sizeof(tDot11fIEQuiet)); + } + + if (pBeacon->HTCaps.present) { + qdf_mem_copy(&pBeaconStruct->HTCaps, &pBeacon->HTCaps, + sizeof(tDot11fIEHTCaps)); + } + + if (pBeacon->HTInfo.present) { + qdf_mem_copy(&pBeaconStruct->HTInfo, &pBeacon->HTInfo, + sizeof(tDot11fIEHTInfo)); + + } + + if (pBeacon->he_op.oper_info_6g_present) { + pBeaconStruct->chan_freq = wlan_reg_chan_band_to_freq(mac->pdev, + pBeacon->he_op.oper_info_6g.info.primary_ch, + BIT(REG_BAND_6G)); + } else if (pBeacon->DSParams.present) { + pBeaconStruct->dsParamsPresent = 1; + pBeaconStruct->chan_freq = + wlan_reg_legacy_chan_to_freq(mac->pdev, + pBeacon->DSParams.curr_channel); + } else if (pBeacon->HTInfo.present) { + pBeaconStruct->chan_freq = + wlan_reg_legacy_chan_to_freq(mac->pdev, + pBeacon->HTInfo.primaryChannel); + } else { + pBeaconStruct->chan_freq = WMA_GET_RX_FREQ(pFrame); + pe_debug_rl("In Beacon No Channel info"); + } + + if (pBeacon->RSN.present) { + pBeaconStruct->rsnPresent = 1; + convert_rsn(mac, &pBeaconStruct->rsn, &pBeacon->RSN); + } + + if (pBeacon->WPA.present) { + pBeaconStruct->wpaPresent = 1; + convert_wpa(mac, &pBeaconStruct->wpa, &pBeacon->WPA); + } + + if (pBeacon->WMMParams.present) { + pBeaconStruct->wmeEdcaPresent = 1; + convert_wmm_params(mac, &pBeaconStruct->edcaParams, + &pBeacon->WMMParams); + } + + if (pBeacon->WMMInfoAp.present) { + pBeaconStruct->wmeInfoPresent = 1; + pe_debug("WMM Info present in Beacon Frame!"); + } + + if (pBeacon->WMMCaps.present) { + pBeaconStruct->wsmCapablePresent = 1; + } + + if (pBeacon->ERPInfo.present) { + pBeaconStruct->erpPresent = 1; + convert_erp_info(mac, &pBeaconStruct->erpIEInfo, + &pBeacon->ERPInfo); + } + if (pBeacon->MobilityDomain.present) { + /* MobilityDomain */ + pBeaconStruct->mdiePresent = 1; + qdf_mem_copy((uint8_t *) &(pBeaconStruct->mdie[0]), + (uint8_t *) &(pBeacon->MobilityDomain.MDID), + sizeof(uint16_t)); + pBeaconStruct->mdie[2] = + ((pBeacon->MobilityDomain.overDSCap << 0) | (pBeacon-> + MobilityDomain. + resourceReqCap + << 1)); + + } + +#ifdef FEATURE_WLAN_ESE + if (pBeacon->ESEVersion.present) + pBeaconStruct->is_ese_ver_ie_present = 1; + if (pBeacon->ESETxmitPower.present) { + /* copy ESE TPC info element */ + pBeaconStruct->eseTxPwr.present = 1; + qdf_mem_copy(&pBeaconStruct->eseTxPwr, + &pBeacon->ESETxmitPower, + sizeof(tDot11fIEESETxmitPower)); + } + if (pBeacon->QBSSLoad.present) { + qdf_mem_copy(&pBeaconStruct->QBSSLoad, + &pBeacon->QBSSLoad, sizeof(tDot11fIEQBSSLoad)); + } +#endif + if (pBeacon->VHTCaps.present) { + qdf_mem_copy(&pBeaconStruct->VHTCaps, &pBeacon->VHTCaps, + sizeof(tDot11fIEVHTCaps)); + } + if (pBeacon->VHTOperation.present) { + qdf_mem_copy(&pBeaconStruct->VHTOperation, + &pBeacon->VHTOperation, + sizeof(tDot11fIEVHTOperation)); + } + if (pBeacon->VHTExtBssLoad.present) { + qdf_mem_copy(&pBeaconStruct->VHTExtBssLoad, + &pBeacon->VHTExtBssLoad, + sizeof(tDot11fIEVHTExtBssLoad)); + } + if (pBeacon->OperatingMode.present) { + qdf_mem_copy(&pBeaconStruct->OperatingMode, + &pBeacon->OperatingMode, + sizeof(tDot11fIEOperatingMode)); + } + if (pBeacon->WiderBWChanSwitchAnn.present) { + pBeaconStruct->WiderBWChanSwitchAnnPresent = 1; + qdf_mem_copy(&pBeaconStruct->WiderBWChanSwitchAnn, + &pBeacon->WiderBWChanSwitchAnn, + sizeof(tDot11fIEWiderBWChanSwitchAnn)); + } + /* IBSS Peer Params */ + if (pBeacon->IBSSParams.present) { + pBeaconStruct->IBSSParams.present = 1; + qdf_mem_copy(&pBeaconStruct->IBSSParams, &pBeacon->IBSSParams, + sizeof(tDot11fIEIBSSParams)); + } + + pBeaconStruct->Vendor1IEPresent = pBeacon->Vendor1IE.present; + pBeaconStruct->Vendor3IEPresent = pBeacon->Vendor3IE.present; + + pBeaconStruct->vendor_vht_ie.present = pBeacon->vendor_vht_ie.present; + if (pBeacon->vendor_vht_ie.present) { + pBeaconStruct->vendor_vht_ie.sub_type = + pBeacon->vendor_vht_ie.sub_type; + } + + if (pBeacon->vendor_vht_ie.VHTCaps.present) { + qdf_mem_copy(&pBeaconStruct->vendor_vht_ie.VHTCaps, + &pBeacon->vendor_vht_ie.VHTCaps, + sizeof(tDot11fIEVHTCaps)); + } + if (pBeacon->vendor_vht_ie.VHTOperation.present) { + qdf_mem_copy(&pBeaconStruct->vendor_vht_ie.VHTOperation, + &pBeacon->VHTOperation, + sizeof(tDot11fIEVHTOperation)); + } + /* Update HS 2.0 Information Element */ + if (pBeacon->hs20vendor_ie.present) { + qdf_mem_copy(&pBeaconStruct->hs20vendor_ie, + &pBeacon->hs20vendor_ie, + sizeof(tDot11fIEhs20vendor_ie) - + sizeof(pBeacon->hs20vendor_ie.hs_id)); + if (pBeacon->hs20vendor_ie.hs_id_present) + qdf_mem_copy(&pBeaconStruct->hs20vendor_ie.hs_id, + &pBeacon->hs20vendor_ie.hs_id, + sizeof(pBeacon->hs20vendor_ie.hs_id)); + } +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + if (pBeacon->QComVendorIE.present) { + pBeaconStruct->AvoidChannelIE.present = + pBeacon->QComVendorIE.present; + pBeaconStruct->AvoidChannelIE.type = + pBeacon->QComVendorIE.type; + pBeaconStruct->AvoidChannelIE.channel = + pBeacon->QComVendorIE.channel; + } +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + if (pBeacon->OBSSScanParameters.present) { + qdf_mem_copy(&pBeaconStruct->obss_scanparams, + &pBeacon->OBSSScanParameters, + sizeof(struct sDot11fIEOBSSScanParameters)); + } + if (pBeacon->MBO_IE.present) { + pBeaconStruct->MBO_IE_present = true; + if (pBeacon->MBO_IE.cellular_data_cap.present) + pBeaconStruct->MBO_capability = + pBeacon->MBO_IE.cellular_data_cap.cellular_connectivity; + + if (pBeacon->MBO_IE.assoc_disallowed.present) { + pBeaconStruct->assoc_disallowed = true; + pBeaconStruct->assoc_disallowed_reason = + pBeacon->MBO_IE.assoc_disallowed.reason_code; + } + } + + if (pBeacon->qcn_ie.present) + qdf_mem_copy(&pBeaconStruct->qcn_ie, &pBeacon->qcn_ie, + sizeof(tDot11fIEqcn_ie)); + + if (pBeacon->he_cap.present) { + pe_debug("11AX: HE cap IE present"); + qdf_mem_copy(&pBeaconStruct->he_cap, + &pBeacon->he_cap, + sizeof(tDot11fIEhe_cap)); + } + if (pBeacon->he_op.present) { + pe_debug("11AX: HE operation IE present"); + qdf_mem_copy(&pBeaconStruct->he_op, + &pBeacon->he_op, + sizeof(tDot11fIEhe_op)); + } + + convert_bcon_bss_color_change_ie(pBeacon, pBeaconStruct); + + qdf_mem_free(pBeacon); + return QDF_STATUS_SUCCESS; + +} /* End sir_convert_beacon_frame2_struct. */ + +#ifdef WLAN_FEATURE_FILS_SK + +/* update_ftie_in_fils_conf() - API to update fils info from auth + * response packet from AP + * @auth: auth packet pointer received from AP + * @auth_frame: data structure needs to be updated + * + * Return: None + */ +static void +update_ftie_in_fils_conf(tDot11fAuthentication *auth, + tpSirMacAuthFrameBody auth_frame) +{ + /** + * Copy the FTIE sent by the AP in the auth request frame. + * This is required for FT-FILS connection. + * This FTIE will be sent in Assoc request frame without + * any modification. + */ + if (auth->FTInfo.present) { + pe_debug("FT-FILS: r0kh_len:%d r1kh_present:%d", + auth->FTInfo.R0KH_ID.num_PMK_R0_ID, + auth->FTInfo.R1KH_ID.present); + + auth_frame->ft_ie.present = 1; + if (auth->FTInfo.R1KH_ID.present) { + qdf_mem_copy(auth_frame->ft_ie.r1kh_id, + auth->FTInfo.R1KH_ID.PMK_R1_ID, + FT_R1KH_ID_LEN); + } + + if (auth->FTInfo.R0KH_ID.present) { + qdf_mem_copy(auth_frame->ft_ie.r0kh_id, + auth->FTInfo.R0KH_ID.PMK_R0_ID, + auth->FTInfo.R0KH_ID.num_PMK_R0_ID); + auth_frame->ft_ie.r0kh_id_len = + auth->FTInfo.R0KH_ID.num_PMK_R0_ID; + } + + if (auth_frame->ft_ie.gtk_ie.present) { + pe_debug("FT-FILS: GTK present"); + qdf_mem_copy(&auth_frame->ft_ie.gtk_ie, + &auth->FTInfo.GTK, + sizeof(struct mac_ft_gtk_ie)); + } + + if (auth_frame->ft_ie.igtk_ie.present) { + pe_debug("FT-FILS: IGTK present"); + qdf_mem_copy(&auth_frame->ft_ie.igtk_ie, + &auth->FTInfo.IGTK, + sizeof(struct mac_ft_igtk_ie)); + } + + qdf_mem_copy(auth_frame->ft_ie.anonce, auth->FTInfo.Anonce, + FT_NONCE_LEN); + qdf_mem_copy(auth_frame->ft_ie.snonce, auth->FTInfo.Snonce, + FT_NONCE_LEN); + + qdf_mem_copy(auth_frame->ft_ie.mic, auth->FTInfo.MIC, + FT_MIC_LEN); + auth_frame->ft_ie.element_count = auth->FTInfo.IECount; + } +} + +/* sir_update_auth_frame2_struct_fils_conf: API to update fils info from auth + * packet type 2 + * @auth: auth packet pointer received from AP + * @auth_frame: data structure needs to be updated + * + * Return: None + */ +static void +sir_update_auth_frame2_struct_fils_conf(tDot11fAuthentication *auth, + tpSirMacAuthFrameBody auth_frame) +{ + if (auth->AuthAlgo.algo != SIR_FILS_SK_WITHOUT_PFS) + return; + + if (auth->fils_assoc_delay_info.present) + auth_frame->assoc_delay_info = + auth->fils_assoc_delay_info.assoc_delay_info; + + if (auth->fils_session.present) + qdf_mem_copy(auth_frame->session, auth->fils_session.session, + SIR_FILS_SESSION_LENGTH); + + if (auth->fils_nonce.present) + qdf_mem_copy(auth_frame->nonce, auth->fils_nonce.nonce, + SIR_FILS_NONCE_LENGTH); + + if (auth->fils_wrapped_data.present) { + qdf_mem_copy(auth_frame->wrapped_data, + auth->fils_wrapped_data.wrapped_data, + auth->fils_wrapped_data.num_wrapped_data); + auth_frame->wrapped_data_len = + auth->fils_wrapped_data.num_wrapped_data; + } + if (auth->RSNOpaque.present) { + qdf_mem_copy(auth_frame->rsn_ie.info, auth->RSNOpaque.data, + auth->RSNOpaque.num_data); + auth_frame->rsn_ie.length = auth->RSNOpaque.num_data; + } + + update_ftie_in_fils_conf(auth, auth_frame); + +} +#else +static void sir_update_auth_frame2_struct_fils_conf(tDot11fAuthentication *auth, + tpSirMacAuthFrameBody auth_frame) +{ } +#endif + +QDF_STATUS +sir_convert_auth_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, + uint32_t nFrame, tpSirMacAuthFrameBody pAuth) +{ + static tDot11fAuthentication auth; + uint32_t status; + + /* Zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) pAuth, sizeof(tSirMacAuthFrameBody)); + + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_authentication(mac, pFrame, nFrame, + &auth, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse an Authentication frame (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pFrame, nFrame); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking an Authentication frame (0x%08x, %d bytes):", + status, nFrame); + } + /* & "transliterate" from a 'tDot11fAuthentication' to a 'tSirMacAuthFrameBody'... */ + pAuth->authAlgoNumber = auth.AuthAlgo.algo; + pAuth->authTransactionSeqNumber = auth.AuthSeqNo.no; + pAuth->authStatusCode = auth.Status.status; + + if (auth.ChallengeText.present) { + pAuth->type = WLAN_ELEMID_CHALLENGE; + pAuth->length = auth.ChallengeText.num_text; + qdf_mem_copy(pAuth->challengeText, auth.ChallengeText.text, + auth.ChallengeText.num_text); + } + sir_update_auth_frame2_struct_fils_conf(&auth, pAuth); + + return QDF_STATUS_SUCCESS; + +} /* End sir_convert_auth_frame2_struct. */ + +QDF_STATUS +sir_convert_addts_rsp2_struct(struct mac_context *mac, + uint8_t *pFrame, + uint32_t nFrame, tSirAddtsRspInfo *pAddTs) +{ + tDot11fAddTSResponse addts = { {0} }; + tDot11fWMMAddTSResponse wmmaddts = { {0} }; + uint8_t j; + uint16_t i; + uint32_t status; + + if (QOS_ADD_TS_RSP != *(pFrame + 1)) { + pe_err("Action of %d; this is not supported & is probably an error", + *(pFrame + 1)); + return QDF_STATUS_E_FAILURE; + } + /* Zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) pAddTs, sizeof(tSirAddtsRspInfo)); + qdf_mem_zero((uint8_t *) &addts, sizeof(tDot11fAddTSResponse)); + qdf_mem_zero((uint8_t *) &wmmaddts, sizeof(tDot11fWMMAddTSResponse)); + + /* delegate to the framesc-generated code, */ + switch (*pFrame) { + case ACTION_CATEGORY_QOS: + status = + dot11f_unpack_add_ts_response(mac, pFrame, nFrame, + &addts, false); + break; + case ACTION_CATEGORY_WMM: + status = + dot11f_unpack_wmm_add_ts_response(mac, pFrame, nFrame, + &wmmaddts, false); + break; + default: + pe_err("Category of %d; this is not supported & is probably an error", + *pFrame); + return QDF_STATUS_E_FAILURE; + } + + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse an Add TS Response frame (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pFrame, nFrame); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking an Add TS Response frame (0x%08x,%d bytes):", + status, nFrame); + } + /* & "transliterate" from a 'tDot11fAddTSResponse' or a */ + /* 'tDot11WMMAddTSResponse' to a 'tSirMacAddtsRspInfo'... */ + if (ACTION_CATEGORY_QOS == *pFrame) { + pAddTs->dialogToken = addts.DialogToken.token; + pAddTs->status = (enum mac_status_code)addts.Status.status; + + if (addts.TSDelay.present) { + convert_ts_delay(mac, &pAddTs->delay, &addts.TSDelay); + } + /* TS Delay is present iff status indicates its presence */ + if (eSIR_MAC_TS_NOT_CREATED_STATUS == pAddTs->status + && !addts.TSDelay.present) { + pe_warn("Missing TSDelay IE"); + } + + if (addts.TSPEC.present) { + convert_tspec(mac, &pAddTs->tspec, &addts.TSPEC); + } else { + pe_err("Mandatory TSPEC element missing in Add TS Response"); + return QDF_STATUS_E_FAILURE; + } + + if (addts.num_TCLAS) { + pAddTs->numTclas = (uint8_t) addts.num_TCLAS; + + for (i = 0U; i < addts.num_TCLAS; ++i) { + if (QDF_STATUS_SUCCESS != + convert_tclas(mac, &(pAddTs->tclasInfo[i]), + &(addts.TCLAS[i]))) { + pe_err("Failed to convert a TCLAS IE"); + return QDF_STATUS_E_FAILURE; + } + } + } + + if (addts.TCLASSPROC.present) { + pAddTs->tclasProcPresent = 1; + pAddTs->tclasProc = addts.TCLASSPROC.processing; + } +#ifdef FEATURE_WLAN_ESE + if (addts.ESETrafStrmMet.present) { + pAddTs->tsmPresent = 1; + qdf_mem_copy(&pAddTs->tsmIE.tsid, + &addts.ESETrafStrmMet.tsid, + sizeof(struct ese_tsm_ie)); + } +#endif + if (addts.Schedule.present) { + pAddTs->schedulePresent = 1; + convert_schedule(mac, &pAddTs->schedule, + &addts.Schedule); + } + + if (addts.WMMSchedule.present) { + pAddTs->schedulePresent = 1; + convert_wmm_schedule(mac, &pAddTs->schedule, + &addts.WMMSchedule); + } + + if (addts.WMMTSPEC.present) { + pAddTs->wsmTspecPresent = 1; + convert_wmmtspec(mac, &pAddTs->tspec, &addts.WMMTSPEC); + } + + if (addts.num_WMMTCLAS) { + j = (uint8_t) (pAddTs->numTclas + addts.num_WMMTCLAS); + if (SIR_MAC_TCLASIE_MAXNUM < j) + j = SIR_MAC_TCLASIE_MAXNUM; + + for (i = pAddTs->numTclas; i < j; ++i) { + if (QDF_STATUS_SUCCESS != + convert_wmmtclas(mac, + &(pAddTs->tclasInfo[i]), + &(addts.WMMTCLAS[i]))) { + pe_err("Failed to convert a TCLAS IE"); + return QDF_STATUS_E_FAILURE; + } + } + } + + if (addts.WMMTCLASPROC.present) { + pAddTs->tclasProcPresent = 1; + pAddTs->tclasProc = addts.WMMTCLASPROC.processing; + } + + if (1 < pAddTs->numTclas && (!pAddTs->tclasProcPresent)) { + pe_err("%d TCLAS IE but not TCLASPROC IE", + pAddTs->numTclas); + return QDF_STATUS_E_FAILURE; + } + } else { + pAddTs->dialogToken = wmmaddts.DialogToken.token; + pAddTs->status = + (enum mac_status_code)wmmaddts.StatusCode.statusCode; + + if (wmmaddts.WMMTSPEC.present) { + pAddTs->wmeTspecPresent = 1; + convert_wmmtspec(mac, &pAddTs->tspec, + &wmmaddts.WMMTSPEC); + } else { + pe_err("Mandatory WME TSPEC element missing!"); + return QDF_STATUS_E_FAILURE; + } + +#ifdef FEATURE_WLAN_ESE + if (wmmaddts.ESETrafStrmMet.present) { + pAddTs->tsmPresent = 1; + qdf_mem_copy(&pAddTs->tsmIE.tsid, + &wmmaddts.ESETrafStrmMet.tsid, + sizeof(struct ese_tsm_ie)); + } +#endif + + } + + return QDF_STATUS_SUCCESS; + +} /* End sir_convert_addts_rsp2_struct. */ + +QDF_STATUS +sir_convert_delts_req2_struct(struct mac_context *mac, + uint8_t *pFrame, + uint32_t nFrame, struct delts_req_info *pDelTs) +{ + tDot11fDelTS delts = { {0} }; + tDot11fWMMDelTS wmmdelts = { {0} }; + uint32_t status; + + if (QOS_DEL_TS_REQ != *(pFrame + 1)) { + pe_err("sirConvertDeltsRsp2Struct invoked " + "with an Action of %d; this is not " + "supported & is probably an error", + *(pFrame + 1)); + return QDF_STATUS_E_FAILURE; + } + /* Zero-init our [out] parameter, */ + qdf_mem_zero(pDelTs, sizeof(*pDelTs)); + + /* delegate to the framesc-generated code, */ + switch (*pFrame) { + case ACTION_CATEGORY_QOS: + status = dot11f_unpack_del_ts(mac, pFrame, nFrame, + &delts, false); + break; + case ACTION_CATEGORY_WMM: + status = dot11f_unpack_wmm_del_ts(mac, pFrame, nFrame, + &wmmdelts, false); + break; + default: + pe_err("sirConvertDeltsRsp2Struct invoked " + "with a Category of %d; this is not" + " supported & is probably an error", + *pFrame); + return QDF_STATUS_E_FAILURE; + } + + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse an Del TS Request frame (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pFrame, nFrame); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking an Del TS Request frame (0x%08x,%d bytes):", + status, nFrame); + } + /* & "transliterate" from a 'tDot11fDelTSResponse' or a */ + /* 'tDot11WMMDelTSResponse' to a 'tSirMacDeltsReqInfo'... */ + if (ACTION_CATEGORY_QOS == *pFrame) { + pDelTs->tsinfo.traffic.trafficType = + (uint16_t) delts.TSInfo.traffic_type; + pDelTs->tsinfo.traffic.tsid = (uint16_t) delts.TSInfo.tsid; + pDelTs->tsinfo.traffic.direction = + (uint16_t) delts.TSInfo.direction; + pDelTs->tsinfo.traffic.accessPolicy = + (uint16_t) delts.TSInfo.access_policy; + pDelTs->tsinfo.traffic.aggregation = + (uint16_t) delts.TSInfo.aggregation; + pDelTs->tsinfo.traffic.psb = (uint16_t) delts.TSInfo.psb; + pDelTs->tsinfo.traffic.userPrio = + (uint16_t) delts.TSInfo.user_priority; + pDelTs->tsinfo.traffic.ackPolicy = + (uint16_t) delts.TSInfo.tsinfo_ack_pol; + + pDelTs->tsinfo.schedule.schedule = + (uint8_t) delts.TSInfo.schedule; + } else { + if (wmmdelts.WMMTSPEC.present) { + pDelTs->wmeTspecPresent = 1; + convert_wmmtspec(mac, &pDelTs->tspec, + &wmmdelts.WMMTSPEC); + } else { + pe_err("Mandatory WME TSPEC element missing!"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; + +} /* End sir_convert_delts_req2_struct. */ + +QDF_STATUS +sir_convert_qos_map_configure_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, + uint32_t nFrame, + struct qos_map_set *pQosMapSet) +{ + tDot11fQosMapConfigure mapConfigure; + uint32_t status; + + status = + dot11f_unpack_qos_map_configure(mac, pFrame, nFrame, + &mapConfigure, false); + if (DOT11F_FAILED(status) || !mapConfigure.QosMapSet.present) { + pe_err("Failed to parse Qos Map Configure frame (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pFrame, nFrame); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking Qos Map Configure frame (0x%08x, %d bytes):", + status, nFrame); + } + pQosMapSet->present = mapConfigure.QosMapSet.present; + convert_qos_mapset_frame(mac, pQosMapSet, &mapConfigure.QosMapSet); + lim_log_qos_map_set(mac, pQosMapSet); + return QDF_STATUS_SUCCESS; +} + +#ifdef ANI_SUPPORT_11H +QDF_STATUS +sir_convert_tpc_req_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, + tpSirMacTpcReqActionFrame pTpcReqFrame, + uint32_t nFrame) +{ + tDot11fTPCRequest req; + uint32_t status; + + qdf_mem_zero((uint8_t *) pTpcReqFrame, + sizeof(tSirMacTpcReqActionFrame)); + status = dot11f_unpack_tpc_request(mac, pFrame, nFrame, &req, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse a TPC Request frame (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pFrame, nFrame); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking a TPC Request frame (0x%08x, %d bytes):", + status, nFrame); + } + /* & "transliterate" from a 'tDot11fTPCRequest' to a */ + /* 'tSirMacTpcReqActionFrame'... */ + pTpcReqFrame->actionHeader.category = req.Category.category; + pTpcReqFrame->actionHeader.actionID = req.Action.action; + pTpcReqFrame->actionHeader.dialogToken = req.DialogToken.token; + if (req.TPCRequest.present) { + pTpcReqFrame->type = DOT11F_EID_TPCREQUEST; + pTpcReqFrame->length = 0; + } else { + pe_warn("!!!Rcv TPC Req of inalid type!"); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} /* End sir_convert_tpc_req_frame2_struct. */ +QDF_STATUS +sir_convert_meas_req_frame2_struct(struct mac_context *mac, + uint8_t *pFrame, + tpSirMacMeasReqActionFrame pMeasReqFrame, + uint32_t nFrame) +{ + tDot11fMeasurementRequest mr; + uint32_t status; + + /* Zero-init our [out] parameter, */ + qdf_mem_zero((uint8_t *) pMeasReqFrame, + sizeof(tpSirMacMeasReqActionFrame)); + + /* delegate to the framesc-generated code, */ + status = dot11f_unpack_measurement_request(mac, pFrame, + nFrame, &mr, false); + if (DOT11F_FAILED(status)) { + pe_err("Failed to parse a Measurement Request frame (0x%08x, %d bytes):", + status, nFrame); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_ERROR, + pFrame, nFrame); + return QDF_STATUS_E_FAILURE; + } else if (DOT11F_WARNED(status)) { + pe_debug("There were warnings while unpacking a Measurement Request frame (0x%08x, %d bytes):", + status, nFrame); + } + /* & "transliterate" from a 'tDot11fMeasurementRequest' to a */ + /* 'tpSirMacMeasReqActionFrame'... */ + pMeasReqFrame->actionHeader.category = mr.Category.category; + pMeasReqFrame->actionHeader.actionID = mr.Action.action; + pMeasReqFrame->actionHeader.dialogToken = mr.DialogToken.token; + + if (0 == mr.num_MeasurementRequest) { + pe_err("Missing mandatory IE in Measurement Request Frame"); + return QDF_STATUS_E_FAILURE; + } else if (1 < mr.num_MeasurementRequest) { + pe_warn( + FL("Warning: dropping extra Measurement Request IEs!")); + } + + pMeasReqFrame->measReqIE.type = DOT11F_EID_MEASUREMENTREQUEST; + pMeasReqFrame->measReqIE.length = DOT11F_IE_MEASUREMENTREQUEST_MIN_LEN; + pMeasReqFrame->measReqIE.measToken = + mr.MeasurementRequest[0].measurement_token; + pMeasReqFrame->measReqIE.measReqMode = + (mr.MeasurementRequest[0].reserved << 3) | (mr. + MeasurementRequest[0]. + enable << 2) | (mr. + MeasurementRequest + [0]. + request + << 1) | + (mr.MeasurementRequest[0].report /*<< 0 */); + pMeasReqFrame->measReqIE.measType = + mr.MeasurementRequest[0].measurement_type; + + pMeasReqFrame->measReqIE.measReqField.channelNumber = + mr.MeasurementRequest[0].channel_no; + + qdf_mem_copy(pMeasReqFrame->measReqIE.measReqField.measStartTime, + mr.MeasurementRequest[0].meas_start_time, 8); + + pMeasReqFrame->measReqIE.measReqField.measDuration = + mr.MeasurementRequest[0].meas_duration; + + return QDF_STATUS_SUCCESS; + +} /* End sir_convert_meas_req_frame2_struct. */ +#endif + +void populate_dot11f_tspec(struct mac_tspec_ie *pOld, tDot11fIETSPEC *pDot11f) +{ + pDot11f->traffic_type = pOld->tsinfo.traffic.trafficType; + pDot11f->tsid = pOld->tsinfo.traffic.tsid; + pDot11f->direction = pOld->tsinfo.traffic.direction; + pDot11f->access_policy = pOld->tsinfo.traffic.accessPolicy; + pDot11f->aggregation = pOld->tsinfo.traffic.aggregation; + pDot11f->psb = pOld->tsinfo.traffic.psb; + pDot11f->user_priority = pOld->tsinfo.traffic.userPrio; + pDot11f->tsinfo_ack_pol = pOld->tsinfo.traffic.ackPolicy; + pDot11f->schedule = pOld->tsinfo.schedule.schedule; + /* As defined in IEEE 802.11-2007, section 7.3.2.30 + * Nominal MSDU size: Bit[0:14]=Size, Bit[15]=Fixed + */ + pDot11f->size = (pOld->nomMsduSz & 0x7fff); + pDot11f->fixed = (pOld->nomMsduSz & 0x8000) ? 1 : 0; + pDot11f->max_msdu_size = pOld->maxMsduSz; + pDot11f->min_service_int = pOld->minSvcInterval; + pDot11f->max_service_int = pOld->maxSvcInterval; + pDot11f->inactivity_int = pOld->inactInterval; + pDot11f->suspension_int = pOld->suspendInterval; + pDot11f->service_start_time = pOld->svcStartTime; + pDot11f->min_data_rate = pOld->minDataRate; + pDot11f->mean_data_rate = pOld->meanDataRate; + pDot11f->peak_data_rate = pOld->peakDataRate; + pDot11f->burst_size = pOld->maxBurstSz; + pDot11f->delay_bound = pOld->delayBound; + pDot11f->min_phy_rate = pOld->minPhyRate; + pDot11f->surplus_bw_allowance = pOld->surplusBw; + pDot11f->medium_time = pOld->mediumTime; + + pDot11f->present = 1; + +} /* End populate_dot11f_tspec. */ + +void populate_dot11f_wmmtspec(struct mac_tspec_ie *pOld, + tDot11fIEWMMTSPEC *pDot11f) +{ + pDot11f->traffic_type = pOld->tsinfo.traffic.trafficType; + pDot11f->tsid = pOld->tsinfo.traffic.tsid; + pDot11f->direction = pOld->tsinfo.traffic.direction; + pDot11f->access_policy = pOld->tsinfo.traffic.accessPolicy; + pDot11f->aggregation = pOld->tsinfo.traffic.aggregation; + pDot11f->psb = pOld->tsinfo.traffic.psb; + pDot11f->user_priority = pOld->tsinfo.traffic.userPrio; + pDot11f->tsinfo_ack_pol = pOld->tsinfo.traffic.ackPolicy; + pDot11f->burst_size_defn = pOld->tsinfo.traffic.burstSizeDefn; + /* As defined in IEEE 802.11-2007, section 7.3.2.30 + * Nominal MSDU size: Bit[0:14]=Size, Bit[15]=Fixed + */ + pDot11f->size = (pOld->nomMsduSz & 0x7fff); + pDot11f->fixed = (pOld->nomMsduSz & 0x8000) ? 1 : 0; + pDot11f->max_msdu_size = pOld->maxMsduSz; + pDot11f->min_service_int = pOld->minSvcInterval; + pDot11f->max_service_int = pOld->maxSvcInterval; + pDot11f->inactivity_int = pOld->inactInterval; + pDot11f->suspension_int = pOld->suspendInterval; + pDot11f->service_start_time = pOld->svcStartTime; + pDot11f->min_data_rate = pOld->minDataRate; + pDot11f->mean_data_rate = pOld->meanDataRate; + pDot11f->peak_data_rate = pOld->peakDataRate; + pDot11f->burst_size = pOld->maxBurstSz; + pDot11f->delay_bound = pOld->delayBound; + pDot11f->min_phy_rate = pOld->minPhyRate; + pDot11f->surplus_bw_allowance = pOld->surplusBw; + pDot11f->medium_time = pOld->mediumTime; + + pDot11f->version = 1; + pDot11f->present = 1; + +} /* End populate_dot11f_wmmtspec. */ + +#if defined(FEATURE_WLAN_ESE) +/* Fill the ESE version currently supported */ +void populate_dot11f_ese_version(tDot11fIEESEVersion *pESEVersion) +{ + pESEVersion->present = 1; + pESEVersion->version = ESE_VERSION_SUPPORTED; +} + +/* Fill the ESE ie for the station. */ +/* The State is Normal (1) */ +/* The MBSSID for station is set to 0. */ +void populate_dot11f_ese_rad_mgmt_cap(tDot11fIEESERadMgmtCap *pESERadMgmtCap) +{ + pESERadMgmtCap->present = 1; + pESERadMgmtCap->mgmt_state = RM_STATE_NORMAL; + pESERadMgmtCap->mbssid_mask = 0; + pESERadMgmtCap->reserved = 0; +} + +QDF_STATUS populate_dot11f_ese_cckm_opaque(struct mac_context *mac, + tpSirCCKMie pCCKMie, + tDot11fIEESECckmOpaque *pDot11f) +{ + int idx; + + if (pCCKMie->length) { + idx = find_ie_location(mac, (tpSirRSNie) pCCKMie, + DOT11F_EID_ESECCKMOPAQUE); + if (0 <= idx) { + pDot11f->present = 1; + /* Dont include OUI */ + pDot11f->num_data = pCCKMie->cckmIEdata[idx + 1] - 4; + qdf_mem_copy(pDot11f->data, pCCKMie->cckmIEdata + idx + 2 + 4, /* EID,len,OUI */ + pCCKMie->cckmIEdata[idx + 1] - 4); /* Skip OUI */ + } + } + return QDF_STATUS_SUCCESS; +} /* End populate_dot11f_ese_cckm_opaque. */ + +void populate_dot11_tsrsie(struct mac_context *mac, + struct ese_tsrs_ie *pOld, + tDot11fIEESETrafStrmRateSet *pDot11f, + uint8_t rate_length) +{ + pDot11f->tsid = pOld->tsid; + qdf_mem_copy(pDot11f->tsrates, pOld->rates, rate_length); + pDot11f->num_tsrates = rate_length; + pDot11f->present = 1; +} +#endif + +QDF_STATUS +populate_dot11f_tclas(struct mac_context *mac, + tSirTclasInfo *pOld, tDot11fIETCLAS *pDot11f) +{ + pDot11f->user_priority = pOld->tclas.userPrio; + pDot11f->classifier_type = pOld->tclas.classifierType; + pDot11f->classifier_mask = pOld->tclas.classifierMask; + + switch (pDot11f->classifier_type) { + case SIR_MAC_TCLASTYPE_ETHERNET: + qdf_mem_copy((uint8_t *) &pDot11f->info.EthParams.source, + (uint8_t *) &pOld->tclasParams.eth.srcAddr, 6); + qdf_mem_copy((uint8_t *) &pDot11f->info.EthParams.dest, + (uint8_t *) &pOld->tclasParams.eth.dstAddr, 6); + pDot11f->info.EthParams.type = pOld->tclasParams.eth.type; + break; + case SIR_MAC_TCLASTYPE_TCPUDPIP: + pDot11f->info.IpParams.version = pOld->version; + if (SIR_MAC_TCLAS_IPV4 == pDot11f->info.IpParams.version) { + qdf_mem_copy(pDot11f->info.IpParams.params.IpV4Params. + source, pOld->tclasParams.ipv4.srcIpAddr, + 4); + qdf_mem_copy(pDot11f->info.IpParams.params.IpV4Params. + dest, pOld->tclasParams.ipv4.dstIpAddr, 4); + pDot11f->info.IpParams.params.IpV4Params.src_port = + pOld->tclasParams.ipv4.srcPort; + pDot11f->info.IpParams.params.IpV4Params.dest_port = + pOld->tclasParams.ipv4.dstPort; + pDot11f->info.IpParams.params.IpV4Params.DSCP = + pOld->tclasParams.ipv4.dscp; + pDot11f->info.IpParams.params.IpV4Params.proto = + pOld->tclasParams.ipv4.protocol; + pDot11f->info.IpParams.params.IpV4Params.reserved = + pOld->tclasParams.ipv4.rsvd; + } else { + qdf_mem_copy((uint8_t *) &pDot11f->info.IpParams. + params.IpV6Params.source, + (uint8_t *) pOld->tclasParams.ipv6. + srcIpAddr, 16); + qdf_mem_copy((uint8_t *) &pDot11f->info.IpParams. + params.IpV6Params.dest, + (uint8_t *) pOld->tclasParams.ipv6. + dstIpAddr, 16); + pDot11f->info.IpParams.params.IpV6Params.src_port = + pOld->tclasParams.ipv6.srcPort; + pDot11f->info.IpParams.params.IpV6Params.dest_port = + pOld->tclasParams.ipv6.dstPort; + qdf_mem_copy((uint8_t *) &pDot11f->info.IpParams. + params.IpV6Params.flow_label, + (uint8_t *) pOld->tclasParams.ipv6. + flowLabel, 3); + } + break; + case SIR_MAC_TCLASTYPE_8021DQ: + pDot11f->info.Params8021dq.tag_type = + pOld->tclasParams.t8021dq.tag; + break; + default: + pe_err("Bad TCLAS type %d", pDot11f->classifier_type); + return QDF_STATUS_E_FAILURE; + } + + pDot11f->present = 1; + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_tclas. */ + +QDF_STATUS +populate_dot11f_wmmtclas(struct mac_context *mac, + tSirTclasInfo *pOld, tDot11fIEWMMTCLAS *pDot11f) +{ + pDot11f->version = 1; + pDot11f->user_priority = pOld->tclas.userPrio; + pDot11f->classifier_type = pOld->tclas.classifierType; + pDot11f->classifier_mask = pOld->tclas.classifierMask; + + switch (pDot11f->classifier_type) { + case SIR_MAC_TCLASTYPE_ETHERNET: + qdf_mem_copy((uint8_t *) &pDot11f->info.EthParams.source, + (uint8_t *) &pOld->tclasParams.eth.srcAddr, 6); + qdf_mem_copy((uint8_t *) &pDot11f->info.EthParams.dest, + (uint8_t *) &pOld->tclasParams.eth.dstAddr, 6); + pDot11f->info.EthParams.type = pOld->tclasParams.eth.type; + break; + case SIR_MAC_TCLASTYPE_TCPUDPIP: + pDot11f->info.IpParams.version = pOld->version; + if (SIR_MAC_TCLAS_IPV4 == pDot11f->info.IpParams.version) { + qdf_mem_copy((uint8_t *) &pDot11f->info.IpParams. + params.IpV4Params.source, + (uint8_t *) pOld->tclasParams.ipv4. + srcIpAddr, 4); + qdf_mem_copy((uint8_t *) &pDot11f->info.IpParams. + params.IpV4Params.dest, + (uint8_t *) pOld->tclasParams.ipv4. + dstIpAddr, 4); + pDot11f->info.IpParams.params.IpV4Params.src_port = + pOld->tclasParams.ipv4.srcPort; + pDot11f->info.IpParams.params.IpV4Params.dest_port = + pOld->tclasParams.ipv4.dstPort; + pDot11f->info.IpParams.params.IpV4Params.DSCP = + pOld->tclasParams.ipv4.dscp; + pDot11f->info.IpParams.params.IpV4Params.proto = + pOld->tclasParams.ipv4.protocol; + pDot11f->info.IpParams.params.IpV4Params.reserved = + pOld->tclasParams.ipv4.rsvd; + } else { + qdf_mem_copy((uint8_t *) &pDot11f->info.IpParams. + params.IpV6Params.source, + (uint8_t *) pOld->tclasParams.ipv6. + srcIpAddr, 16); + qdf_mem_copy((uint8_t *) &pDot11f->info.IpParams. + params.IpV6Params.dest, + (uint8_t *) pOld->tclasParams.ipv6. + dstIpAddr, 16); + pDot11f->info.IpParams.params.IpV6Params.src_port = + pOld->tclasParams.ipv6.srcPort; + pDot11f->info.IpParams.params.IpV6Params.dest_port = + pOld->tclasParams.ipv6.dstPort; + qdf_mem_copy((uint8_t *) &pDot11f->info.IpParams. + params.IpV6Params.flow_label, + (uint8_t *) pOld->tclasParams.ipv6. + flowLabel, 3); + } + break; + case SIR_MAC_TCLASTYPE_8021DQ: + pDot11f->info.Params8021dq.tag_type = + pOld->tclasParams.t8021dq.tag; + break; + default: + pe_err("Bad TCLAS type %d in populate_dot11f_tclas", + pDot11f->classifier_type); + return QDF_STATUS_E_FAILURE; + } + + pDot11f->present = 1; + + return QDF_STATUS_SUCCESS; + +} /* End populate_dot11f_wmmtclas. */ + +QDF_STATUS populate_dot11f_wsc(struct mac_context *mac, + tDot11fIEWscBeacon *pDot11f) +{ + + uint32_t wpsState; + + pDot11f->Version.present = 1; + pDot11f->Version.major = 0x01; + pDot11f->Version.minor = 0x00; + + wpsState = mac->mlme_cfg->wps_params.wps_state; + + pDot11f->WPSState.present = 1; + pDot11f->WPSState.state = (uint8_t) wpsState; + + pDot11f->APSetupLocked.present = 0; + + pDot11f->SelectedRegistrar.present = 0; + + pDot11f->DevicePasswordID.present = 0; + + pDot11f->SelectedRegistrarConfigMethods.present = 0; + + pDot11f->UUID_E.present = 0; + + pDot11f->RFBands.present = 0; + + pDot11f->present = 1; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS populate_dot11f_wsc_registrar_info(struct mac_context *mac, + tDot11fIEWscBeacon *pDot11f) +{ + const struct sLimWscIeInfo *const pWscIeInfo = &(mac->lim.wscIeInfo); + + pDot11f->APSetupLocked.present = 1; + pDot11f->APSetupLocked.fLocked = pWscIeInfo->apSetupLocked; + + pDot11f->SelectedRegistrar.present = 1; + pDot11f->SelectedRegistrar.selected = pWscIeInfo->selectedRegistrar; + + pDot11f->DevicePasswordID.present = 1; + pDot11f->DevicePasswordID.id = + (uint16_t)mac->mlme_cfg->wps_params.wps_device_password_id; + + pDot11f->SelectedRegistrarConfigMethods.present = 1; + pDot11f->SelectedRegistrarConfigMethods.methods = + pWscIeInfo->selectedRegistrarConfigMethods; + + /* UUID_E and RF Bands are applicable only for dual band AP */ + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS de_populate_dot11f_wsc_registrar_info(struct mac_context *mac, + tDot11fIEWscBeacon *pDot11f) +{ + pDot11f->APSetupLocked.present = 0; + pDot11f->SelectedRegistrar.present = 0; + pDot11f->DevicePasswordID.present = 0; + pDot11f->SelectedRegistrarConfigMethods.present = 0; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS populate_dot11f_probe_res_wpsi_es(struct mac_context *mac, + tDot11fIEWscProbeRes *pDot11f, + struct pe_session *pe_session) +{ + + tSirWPSProbeRspIE *pSirWPSProbeRspIE; + + pSirWPSProbeRspIE = &pe_session->APWPSIEs.SirWPSProbeRspIE; + + if (pSirWPSProbeRspIE->FieldPresent & SIR_WPS_PROBRSP_VER_PRESENT) { + pDot11f->present = 1; + pDot11f->Version.present = 1; + pDot11f->Version.major = + (uint8_t) ((pSirWPSProbeRspIE->Version & 0xF0) >> 4); + pDot11f->Version.minor = + (uint8_t) (pSirWPSProbeRspIE->Version & 0x0F); + } else { + pDot11f->present = 0; + pDot11f->Version.present = 0; + } + + if (pSirWPSProbeRspIE->FieldPresent & SIR_WPS_PROBRSP_STATE_PRESENT) { + + pDot11f->WPSState.present = 1; + pDot11f->WPSState.state = (uint8_t) pSirWPSProbeRspIE->wpsState; + } else + pDot11f->WPSState.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_APSETUPLOCK_PRESENT) { + pDot11f->APSetupLocked.present = 1; + pDot11f->APSetupLocked.fLocked = + pSirWPSProbeRspIE->APSetupLocked; + } else + pDot11f->APSetupLocked.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_SELECTEDREGISTRA_PRESENT) { + pDot11f->SelectedRegistrar.present = 1; + pDot11f->SelectedRegistrar.selected = + pSirWPSProbeRspIE->SelectedRegistra; + } else + pDot11f->SelectedRegistrar.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_DEVICEPASSWORDID_PRESENT) { + pDot11f->DevicePasswordID.present = 1; + pDot11f->DevicePasswordID.id = + pSirWPSProbeRspIE->DevicePasswordID; + } else + pDot11f->DevicePasswordID.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_SELECTEDREGISTRACFGMETHOD_PRESENT) { + pDot11f->SelectedRegistrarConfigMethods.present = 1; + pDot11f->SelectedRegistrarConfigMethods.methods = + pSirWPSProbeRspIE->SelectedRegistraCfgMethod; + } else + pDot11f->SelectedRegistrarConfigMethods.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_RESPONSETYPE_PRESENT) { + pDot11f->ResponseType.present = 1; + pDot11f->ResponseType.resType = pSirWPSProbeRspIE->ResponseType; + } else + pDot11f->ResponseType.present = 0; + + if (pSirWPSProbeRspIE->FieldPresent & SIR_WPS_PROBRSP_UUIDE_PRESENT) { + pDot11f->UUID_E.present = 1; + qdf_mem_copy(pDot11f->UUID_E.uuid, pSirWPSProbeRspIE->UUID_E, + WNI_CFG_WPS_UUID_LEN); + } else + pDot11f->UUID_E.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_MANUFACTURE_PRESENT) { + pDot11f->Manufacturer.present = 1; + pDot11f->Manufacturer.num_name = + pSirWPSProbeRspIE->Manufacture.num_name; + qdf_mem_copy(pDot11f->Manufacturer.name, + pSirWPSProbeRspIE->Manufacture.name, + pSirWPSProbeRspIE->Manufacture.num_name); + } else + pDot11f->Manufacturer.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_MODELNUMBER_PRESENT) { + pDot11f->ModelName.present = 1; + pDot11f->ModelName.num_text = + pSirWPSProbeRspIE->ModelName.num_text; + qdf_mem_copy(pDot11f->ModelName.text, + pSirWPSProbeRspIE->ModelName.text, + pDot11f->ModelName.num_text); + } else + pDot11f->ModelName.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_MODELNUMBER_PRESENT) { + pDot11f->ModelNumber.present = 1; + pDot11f->ModelNumber.num_text = + pSirWPSProbeRspIE->ModelNumber.num_text; + qdf_mem_copy(pDot11f->ModelNumber.text, + pSirWPSProbeRspIE->ModelNumber.text, + pDot11f->ModelNumber.num_text); + } else + pDot11f->ModelNumber.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_SERIALNUMBER_PRESENT) { + pDot11f->SerialNumber.present = 1; + pDot11f->SerialNumber.num_text = + pSirWPSProbeRspIE->SerialNumber.num_text; + qdf_mem_copy(pDot11f->SerialNumber.text, + pSirWPSProbeRspIE->SerialNumber.text, + pDot11f->SerialNumber.num_text); + } else + pDot11f->SerialNumber.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_PRIMARYDEVICETYPE_PRESENT) { + pDot11f->PrimaryDeviceType.present = 1; + qdf_mem_copy(pDot11f->PrimaryDeviceType.oui, + pSirWPSProbeRspIE->PrimaryDeviceOUI, + sizeof(pSirWPSProbeRspIE->PrimaryDeviceOUI)); + pDot11f->PrimaryDeviceType.primary_category = + (uint16_t) pSirWPSProbeRspIE->PrimaryDeviceCategory; + pDot11f->PrimaryDeviceType.sub_category = + (uint16_t) pSirWPSProbeRspIE->DeviceSubCategory; + } else + pDot11f->PrimaryDeviceType.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_DEVICENAME_PRESENT) { + pDot11f->DeviceName.present = 1; + pDot11f->DeviceName.num_text = + pSirWPSProbeRspIE->DeviceName.num_text; + qdf_mem_copy(pDot11f->DeviceName.text, + pSirWPSProbeRspIE->DeviceName.text, + pDot11f->DeviceName.num_text); + } else + pDot11f->DeviceName.present = 0; + + if (pSirWPSProbeRspIE-> + FieldPresent & SIR_WPS_PROBRSP_CONFIGMETHODS_PRESENT) { + pDot11f->ConfigMethods.present = 1; + pDot11f->ConfigMethods.methods = + pSirWPSProbeRspIE->ConfigMethod; + } else + pDot11f->ConfigMethods.present = 0; + + if (pSirWPSProbeRspIE->FieldPresent & SIR_WPS_PROBRSP_RF_BANDS_PRESENT) { + pDot11f->RFBands.present = 1; + pDot11f->RFBands.bands = pSirWPSProbeRspIE->RFBand; + } else + pDot11f->RFBands.present = 0; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS populate_dot11f_beacon_wpsi_es(struct mac_context *mac, + tDot11fIEWscBeacon *pDot11f, + struct pe_session *pe_session) +{ + + tSirWPSBeaconIE *pSirWPSBeaconIE; + + pSirWPSBeaconIE = &pe_session->APWPSIEs.SirWPSBeaconIE; + + if (pSirWPSBeaconIE->FieldPresent & SIR_WPS_PROBRSP_VER_PRESENT) { + pDot11f->present = 1; + pDot11f->Version.present = 1; + pDot11f->Version.major = + (uint8_t) ((pSirWPSBeaconIE->Version & 0xF0) >> 4); + pDot11f->Version.minor = + (uint8_t) (pSirWPSBeaconIE->Version & 0x0F); + } else { + pDot11f->present = 0; + pDot11f->Version.present = 0; + } + + if (pSirWPSBeaconIE->FieldPresent & SIR_WPS_BEACON_STATE_PRESENT) { + + pDot11f->WPSState.present = 1; + pDot11f->WPSState.state = (uint8_t) pSirWPSBeaconIE->wpsState; + } else + pDot11f->WPSState.present = 0; + + if (pSirWPSBeaconIE->FieldPresent & SIR_WPS_BEACON_APSETUPLOCK_PRESENT) { + pDot11f->APSetupLocked.present = 1; + pDot11f->APSetupLocked.fLocked = pSirWPSBeaconIE->APSetupLocked; + } else + pDot11f->APSetupLocked.present = 0; + + if (pSirWPSBeaconIE-> + FieldPresent & SIR_WPS_BEACON_SELECTEDREGISTRA_PRESENT) { + pDot11f->SelectedRegistrar.present = 1; + pDot11f->SelectedRegistrar.selected = + pSirWPSBeaconIE->SelectedRegistra; + } else + pDot11f->SelectedRegistrar.present = 0; + + if (pSirWPSBeaconIE-> + FieldPresent & SIR_WPS_BEACON_DEVICEPASSWORDID_PRESENT) { + pDot11f->DevicePasswordID.present = 1; + pDot11f->DevicePasswordID.id = + pSirWPSBeaconIE->DevicePasswordID; + } else + pDot11f->DevicePasswordID.present = 0; + + if (pSirWPSBeaconIE-> + FieldPresent & SIR_WPS_BEACON_SELECTEDREGISTRACFGMETHOD_PRESENT) { + pDot11f->SelectedRegistrarConfigMethods.present = 1; + pDot11f->SelectedRegistrarConfigMethods.methods = + pSirWPSBeaconIE->SelectedRegistraCfgMethod; + } else + pDot11f->SelectedRegistrarConfigMethods.present = 0; + + if (pSirWPSBeaconIE->FieldPresent & SIR_WPS_BEACON_UUIDE_PRESENT) { + pDot11f->UUID_E.present = 1; + qdf_mem_copy(pDot11f->UUID_E.uuid, pSirWPSBeaconIE->UUID_E, + WNI_CFG_WPS_UUID_LEN); + } else + pDot11f->UUID_E.present = 0; + + if (pSirWPSBeaconIE->FieldPresent & SIR_WPS_BEACON_RF_BANDS_PRESENT) { + pDot11f->RFBands.present = 1; + pDot11f->RFBands.bands = pSirWPSBeaconIE->RFBand; + } else + pDot11f->RFBands.present = 0; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS populate_dot11f_wsc_in_probe_res(struct mac_context *mac, + tDot11fIEWscProbeRes *pDot11f) +{ + uint32_t cfgStrLen; + uint32_t val; + uint32_t wpsVersion, wpsState; + + wpsVersion = mac->mlme_cfg->wps_params.wps_version; + + pDot11f->Version.present = 1; + pDot11f->Version.major = (uint8_t) ((wpsVersion & 0xF0) >> 4); + pDot11f->Version.minor = (uint8_t) (wpsVersion & 0x0F); + + wpsState = mac->mlme_cfg->wps_params.wps_state; + pDot11f->WPSState.present = 1; + pDot11f->WPSState.state = (uint8_t) wpsState; + + pDot11f->APSetupLocked.present = 0; + + pDot11f->SelectedRegistrar.present = 0; + + pDot11f->DevicePasswordID.present = 0; + + pDot11f->SelectedRegistrarConfigMethods.present = 0; + + pDot11f->ResponseType.present = 1; + if ((mac->lim.wscIeInfo.reqType == REQ_TYPE_REGISTRAR) || + (mac->lim.wscIeInfo.reqType == REQ_TYPE_WLAN_MANAGER_REGISTRAR)) { + pDot11f->ResponseType.resType = RESP_TYPE_ENROLLEE_OPEN_8021X; + } else { + pDot11f->ResponseType.resType = RESP_TYPE_AP; + } + + /* UUID is a 16 byte long binary*/ + pDot11f->UUID_E.present = 1; + *pDot11f->UUID_E.uuid = '\0'; + + wlan_mlme_get_wps_uuid(&mac->mlme_cfg->wps_params, + pDot11f->UUID_E.uuid); + + pDot11f->Manufacturer.present = 1; + cfgStrLen = sizeof(pDot11f->Manufacturer.name); + if (wlan_mlme_get_manufacturer_name(mac->psoc, + pDot11f->Manufacturer.name, + &cfgStrLen) != QDF_STATUS_SUCCESS) { + pDot11f->Manufacturer.num_name = 0; + } else { + pDot11f->Manufacturer.num_name = + (uint8_t) (cfgStrLen & 0x000000FF); + } + + pDot11f->ModelName.present = 1; + cfgStrLen = sizeof(pDot11f->ModelName.text); + if (wlan_mlme_get_model_name(mac->psoc, + pDot11f->ModelName.text, + &cfgStrLen) != QDF_STATUS_SUCCESS) { + pDot11f->ModelName.num_text = 0; + } else { + pDot11f->ModelName.num_text = + (uint8_t) (cfgStrLen & 0x000000FF); + } + + pDot11f->ModelNumber.present = 1; + cfgStrLen = sizeof(pDot11f->ModelNumber.text); + if (wlan_mlme_get_model_number(mac->psoc, + pDot11f->ModelNumber.text, + &cfgStrLen) != QDF_STATUS_SUCCESS) { + pDot11f->ModelNumber.num_text = 0; + } else { + pDot11f->ModelNumber.num_text = + (uint8_t) (cfgStrLen & 0x000000FF); + } + + pDot11f->SerialNumber.present = 1; + cfgStrLen = sizeof(pDot11f->SerialNumber.text); + if (wlan_mlme_get_manufacture_product_version + (mac->psoc, + pDot11f->SerialNumber.text, + &cfgStrLen) != QDF_STATUS_SUCCESS) { + pDot11f->SerialNumber.num_text = 0; + } else { + pDot11f->SerialNumber.num_text = + (uint8_t) (cfgStrLen & 0x000000FF); + } + + pDot11f->PrimaryDeviceType.present = 1; + + pDot11f->PrimaryDeviceType.primary_category = + (uint16_t)mac->mlme_cfg->wps_params.wps_primary_device_category; + + val = mac->mlme_cfg->wps_params.wps_primary_device_oui; + *(pDot11f->PrimaryDeviceType.oui) = + (uint8_t) ((val >> 24) & 0xff); + *(pDot11f->PrimaryDeviceType.oui + 1) = + (uint8_t) ((val >> 16) & 0xff); + *(pDot11f->PrimaryDeviceType.oui + 2) = + (uint8_t) ((val >> 8) & 0xff); + *(pDot11f->PrimaryDeviceType.oui + 3) = + (uint8_t) ((val & 0xff)); + + pDot11f->PrimaryDeviceType.sub_category = + (uint16_t)mac->mlme_cfg->wps_params.wps_device_sub_category; + + + pDot11f->DeviceName.present = 1; + cfgStrLen = sizeof(pDot11f->DeviceName.text); + if (wlan_mlme_get_manufacture_product_name(mac->psoc, + pDot11f->DeviceName.text, + &cfgStrLen) != + QDF_STATUS_SUCCESS) { + pDot11f->DeviceName.num_text = 0; + } else { + pDot11f->DeviceName.num_text = + (uint8_t) (cfgStrLen & 0x000000FF); + } + + pDot11f->ConfigMethods.present = 1; + pDot11f->ConfigMethods.methods = + (uint16_t)(mac->mlme_cfg->wps_params.wps_cfg_method & + 0x0000FFFF); + + pDot11f->RFBands.present = 0; + + pDot11f->present = 1; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +populate_dot11f_wsc_registrar_info_in_probe_res(struct mac_context *mac, + tDot11fIEWscProbeRes *pDot11f) +{ + const struct sLimWscIeInfo *const pWscIeInfo = &(mac->lim.wscIeInfo); + + pDot11f->APSetupLocked.present = 1; + pDot11f->APSetupLocked.fLocked = pWscIeInfo->apSetupLocked; + + pDot11f->SelectedRegistrar.present = 1; + pDot11f->SelectedRegistrar.selected = pWscIeInfo->selectedRegistrar; + + pDot11f->DevicePasswordID.present = 1; + pDot11f->DevicePasswordID.id = + (uint16_t)mac->mlme_cfg->wps_params.wps_device_password_id; + + pDot11f->SelectedRegistrarConfigMethods.present = 1; + pDot11f->SelectedRegistrarConfigMethods.methods = + pWscIeInfo->selectedRegistrarConfigMethods; + + /* UUID_E and RF Bands are applicable only for dual band AP */ + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +de_populate_dot11f_wsc_registrar_info_in_probe_res(struct mac_context *mac, + tDot11fIEWscProbeRes * + pDot11f) +{ + pDot11f->APSetupLocked.present = 0; + pDot11f->SelectedRegistrar.present = 0; + pDot11f->DevicePasswordID.present = 0; + pDot11f->SelectedRegistrarConfigMethods.present = 0; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS populate_dot11_assoc_res_p2p_ie(struct mac_context *mac, + tDot11fIEP2PAssocRes *pDot11f, + tpSirAssocReq pRcvdAssocReq) +{ + const uint8_t *p2pIe; + + p2pIe = limGetP2pIEPtr(mac, pRcvdAssocReq->addIE.addIEdata, + pRcvdAssocReq->addIE.length); + if (p2pIe) { + pDot11f->present = 1; + pDot11f->P2PStatus.present = 1; + pDot11f->P2PStatus.status = QDF_STATUS_SUCCESS; + pDot11f->ExtendedListenTiming.present = 0; + } + return QDF_STATUS_SUCCESS; +} + + +QDF_STATUS populate_dot11f_wfatpc(struct mac_context *mac, + tDot11fIEWFATPC *pDot11f, uint8_t txPower, + uint8_t linkMargin) +{ + pDot11f->txPower = txPower; + pDot11f->linkMargin = linkMargin; + pDot11f->present = 1; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +populate_dot11f_beacon_report(struct mac_context *mac, + tDot11fIEMeasurementReport *pDot11f, + tSirMacBeaconReport *pBeaconReport, + bool is_last_frame) +{ + tDot11fIEbeacon_report_frm_body_fragment_id *frm_body_frag_id; + + pDot11f->report.Beacon.regClass = pBeaconReport->regClass; + pDot11f->report.Beacon.channel = pBeaconReport->channel; + qdf_mem_copy(pDot11f->report.Beacon.meas_start_time, + pBeaconReport->measStartTime, + sizeof(pDot11f->report.Beacon.meas_start_time)); + pDot11f->report.Beacon.meas_duration = pBeaconReport->measDuration; + pDot11f->report.Beacon.condensed_PHY = pBeaconReport->phyType; + pDot11f->report.Beacon.reported_frame_type = + !pBeaconReport->bcnProbeRsp; + pDot11f->report.Beacon.RCPI = pBeaconReport->rcpi; + pDot11f->report.Beacon.RSNI = pBeaconReport->rsni; + qdf_mem_copy(pDot11f->report.Beacon.BSSID, pBeaconReport->bssid, + sizeof(tSirMacAddr)); + pDot11f->report.Beacon.antenna_id = pBeaconReport->antennaId; + pDot11f->report.Beacon.parent_TSF = pBeaconReport->parentTSF; + + if (pBeaconReport->numIes) { + pDot11f->report.Beacon.BeaconReportFrmBody.present = 1; + qdf_mem_copy(pDot11f->report.Beacon.BeaconReportFrmBody. + reportedFields, pBeaconReport->Ies, + pBeaconReport->numIes); + pDot11f->report.Beacon.BeaconReportFrmBody.num_reportedFields = + pBeaconReport->numIes; + } + + if (pBeaconReport->last_bcn_report_ind_support) { + pe_debug("Including Last Beacon Report in RRM Frame"); + frm_body_frag_id = &pDot11f->report.Beacon. + beacon_report_frm_body_fragment_id; + + frm_body_frag_id->present = 1; + frm_body_frag_id->beacon_report_id = + pBeaconReport->frame_body_frag_id.id; + frm_body_frag_id->fragment_id_number = + pBeaconReport->frame_body_frag_id.frag_id; + frm_body_frag_id->more_fragments = + pBeaconReport->frame_body_frag_id.more_frags; + + pDot11f->report.Beacon.last_beacon_report_indication.present = + 1; + + pDot11f->report.Beacon.last_beacon_report_indication. + last_fragment = is_last_frame; + pe_debug("id %d frag_id %d more_frags %d is_last_frame %d", + frm_body_frag_id->beacon_report_id, + frm_body_frag_id->fragment_id_number, + frm_body_frag_id->more_fragments, + is_last_frame); + } + return QDF_STATUS_SUCCESS; + +} + +QDF_STATUS populate_dot11f_rrm_ie(struct mac_context *mac, + tDot11fIERRMEnabledCap *pDot11f, + struct pe_session *pe_session) +{ + tpRRMCaps pRrmCaps; + uint8_t *bytes; + + pRrmCaps = rrm_get_capabilities(mac, pe_session); + + pDot11f->LinkMeasurement = pRrmCaps->LinkMeasurement; + pDot11f->NeighborRpt = pRrmCaps->NeighborRpt; + pDot11f->parallel = pRrmCaps->parallel; + pDot11f->repeated = pRrmCaps->repeated; + pDot11f->BeaconPassive = pRrmCaps->BeaconPassive; + pDot11f->BeaconActive = pRrmCaps->BeaconActive; + pDot11f->BeaconTable = pRrmCaps->BeaconTable; + pDot11f->BeaconRepCond = pRrmCaps->BeaconRepCond; + pDot11f->FrameMeasurement = pRrmCaps->FrameMeasurement; + pDot11f->ChannelLoad = pRrmCaps->ChannelLoad; + pDot11f->NoiseHistogram = pRrmCaps->NoiseHistogram; + pDot11f->statistics = pRrmCaps->statistics; + pDot11f->LCIMeasurement = pRrmCaps->LCIMeasurement; + pDot11f->LCIAzimuth = pRrmCaps->LCIAzimuth; + pDot11f->TCMCapability = pRrmCaps->TCMCapability; + pDot11f->triggeredTCM = pRrmCaps->triggeredTCM; + pDot11f->APChanReport = pRrmCaps->APChanReport; + pDot11f->RRMMIBEnabled = pRrmCaps->RRMMIBEnabled; + pDot11f->operatingChanMax = pRrmCaps->operatingChanMax; + pDot11f->nonOperatinChanMax = pRrmCaps->nonOperatingChanMax; + pDot11f->MeasurementPilot = pRrmCaps->MeasurementPilot; + pDot11f->MeasurementPilotEnabled = pRrmCaps->MeasurementPilotEnabled; + pDot11f->NeighborTSFOffset = pRrmCaps->NeighborTSFOffset; + pDot11f->RCPIMeasurement = pRrmCaps->RCPIMeasurement; + pDot11f->RSNIMeasurement = pRrmCaps->RSNIMeasurement; + pDot11f->BssAvgAccessDelay = pRrmCaps->BssAvgAccessDelay; + pDot11f->BSSAvailAdmission = pRrmCaps->BSSAvailAdmission; + pDot11f->AntennaInformation = pRrmCaps->AntennaInformation; + pDot11f->fine_time_meas_rpt = pRrmCaps->fine_time_meas_rpt; + pDot11f->lci_capability = pRrmCaps->lci_capability; + pDot11f->reserved = pRrmCaps->reserved; + + bytes = (uint8_t *) pDot11f + 1; /* ignore present field */ + pe_debug("RRM Enabled Cap IE: %02x %02x %02x %02x %02x", + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]); + + pDot11f->present = 1; + return QDF_STATUS_SUCCESS; +} + +void populate_mdie(struct mac_context *mac, + tDot11fIEMobilityDomain *pDot11f, + uint8_t mdie[SIR_MDIE_SIZE]) +{ + pDot11f->present = 1; + pDot11f->MDID = (uint16_t) ((mdie[1] << 8) | (mdie[0])); + + /* Plugfest fix */ + pDot11f->overDSCap = (mdie[2] & 0x01); + pDot11f->resourceReqCap = ((mdie[2] >> 1) & 0x01); + +} + +#ifdef WLAN_FEATURE_FILS_SK +void populate_fils_ft_info(struct mac_context *mac, tDot11fIEFTInfo *ft_info, + struct pe_session *pe_session) +{ + struct pe_fils_session *ft_fils_info = pe_session->fils_info; + + if (!ft_fils_info) + return; + + if (!ft_fils_info->ft_ie.present) { + ft_info->present = 0; + pe_err("FT IE doesn't exist"); + return; + } + + ft_info->IECount = ft_fils_info->ft_ie.element_count; + + qdf_mem_copy(ft_info->MIC, ft_fils_info->ft_ie.mic, + FT_MIC_LEN); + + qdf_mem_copy(ft_info->Anonce, ft_fils_info->ft_ie.anonce, + FT_NONCE_LEN); + + qdf_mem_copy(ft_info->Snonce, ft_fils_info->ft_ie.snonce, + FT_NONCE_LEN); + + if (ft_fils_info->ft_ie.r0kh_id_len > 0) { + ft_info->R0KH_ID.present = 1; + qdf_mem_copy(ft_info->R0KH_ID.PMK_R0_ID, + ft_fils_info->ft_ie.r0kh_id, + ft_fils_info->ft_ie.r0kh_id_len); + ft_info->R0KH_ID.num_PMK_R0_ID = + ft_fils_info->ft_ie.r0kh_id_len; + } + + ft_info->R1KH_ID.present = 1; + qdf_mem_copy(ft_info->R1KH_ID.PMK_R1_ID, + ft_fils_info->ft_ie.r1kh_id, + FT_R1KH_ID_LEN); + + qdf_mem_copy(&ft_info->GTK, &ft_fils_info->ft_ie.gtk_ie, + sizeof(struct mac_ft_gtk_ie)); + qdf_mem_copy(&ft_info->IGTK, &ft_fils_info->ft_ie.igtk_ie, + sizeof(struct mac_ft_igtk_ie)); + + ft_info->present = 1; +} +#endif + +void populate_dot11f_assoc_rsp_rates(struct mac_context *mac, + tDot11fIESuppRates *pSupp, + tDot11fIEExtSuppRates *pExt, + uint16_t *_11bRates, uint16_t *_11aRates) +{ + uint8_t num_supp = 0, num_ext = 0; + uint8_t i, j; + + for (i = 0; (i < SIR_NUM_11B_RATES && _11bRates[i]); i++, num_supp++) { + pSupp->rates[num_supp] = (uint8_t) _11bRates[i]; + } + for (j = 0; (j < SIR_NUM_11A_RATES && _11aRates[j]); j++) { + if (num_supp < 8) + pSupp->rates[num_supp++] = (uint8_t) _11aRates[j]; + else + pExt->rates[num_ext++] = (uint8_t) _11aRates[j]; + } + + if (num_supp) { + pSupp->num_rates = num_supp; + pSupp->present = 1; + } + if (num_ext) { + pExt->num_rates = num_ext; + pExt->present = 1; + } +} + +void populate_dot11f_timeout_interval(struct mac_context *mac, + tDot11fIETimeoutInterval *pDot11f, + uint8_t type, uint32_t value) +{ + pDot11f->present = 1; + pDot11f->timeoutType = type; + pDot11f->timeoutValue = value; +} + +/** + * populate_dot11f_timing_advert_frame() - Populate the TA mgmt frame fields + * @mac: the MAC context + * @frame: pointer to the TA frame + * + * Return: The SIR status. + */ +QDF_STATUS +populate_dot11f_timing_advert_frame(struct mac_context *mac_ctx, + tDot11fTimingAdvertisementFrame *frame) +{ + uint32_t val, len, j = 0; + uint8_t temp[CFG_MAX_STR_LEN], code[3]; + tSirMacChanInfo *max_tx_power_data; + int32_t rem_length = 0, copied_length = 0; + + /* Capabilities */ + val = mac_ctx->mlme_cfg->wep_params.is_privacy_enabled; + if (val) + frame->Capabilities.privacy = 1; + + if (mac_ctx->mlme_cfg->ht_caps.short_preamble) + frame->Capabilities.shortPreamble = 1; + + if (mac_ctx->mlme_cfg->gen.enabled_11h) + frame->Capabilities.spectrumMgt = 1; + + if (mac_ctx->mlme_cfg->wmm_params.qos_enabled) + frame->Capabilities.qos = 1; + + if (mac_ctx->mlme_cfg->scoring.apsd_enabled) + frame->Capabilities.apsd = 1; + + val = mac_ctx->mlme_cfg->feature_flags.enable_block_ack; + frame->Capabilities.delayedBA = + (uint16_t)((val >> WNI_CFG_BLOCK_ACK_ENABLED_DELAYED) & 1); + frame->Capabilities.immediateBA = + (uint16_t)((val >> WNI_CFG_BLOCK_ACK_ENABLED_IMMEDIATE) & 1); + + /* Country */ + len = mac_ctx->mlme_cfg->power.max_tx_power_5.len; + max_tx_power_data = + (tSirMacChanInfo *)mac_ctx->mlme_cfg->power.max_tx_power_5.data; + rem_length = len; + while (rem_length >= (sizeof(tSirMacChanInfo))) { + temp[copied_length++] = + (uint8_t)wlan_reg_freq_to_chan( + mac_ctx->pdev, + max_tx_power_data[j].first_freq); + + temp[copied_length++] = max_tx_power_data[j].numChannels; + temp[copied_length++] = max_tx_power_data[j].maxTxPower; + j++; + rem_length -= (sizeof(tSirMacChanInfo)); + } + + wlan_reg_read_current_country(mac_ctx->psoc, code); + qdf_mem_copy(&frame->Country, code, 2); + if (copied_length > MAX_SIZE_OF_TRIPLETS_IN_COUNTRY_IE) + copied_length = MAX_SIZE_OF_TRIPLETS_IN_COUNTRY_IE; + + frame->Country.num_triplets = (uint8_t)(copied_length / 3); + qdf_mem_copy((uint8_t *)&frame->Country.triplets, temp, copied_length); + frame->Country.present = 1; + + /* PowerConstraints */ + frame->PowerConstraints.localPowerConstraints = + mac_ctx->mlme_cfg->power.local_power_constraint; + + frame->PowerConstraints.present = 1; + + /* TimeAdvertisement */ + frame->TimeAdvertisement.present = 1; + frame->TimeAdvertisement.timing_capabilities = 1; + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_11AX +/** + * populate_dot11f_he_caps() - pouldate HE Capability IE + * @mac_ctx: Global MAC context + * @session: PE session + * @he_cap: pointer to HE capability IE + * + * Populdate the HE capability IE based on the session. + */ +QDF_STATUS populate_dot11f_he_caps(struct mac_context *mac_ctx, struct pe_session *session, + tDot11fIEhe_cap *he_cap) +{ + uint8_t *ppet; + uint32_t value = 0; + + he_cap->present = 1; + + if (!session) { + qdf_mem_copy(he_cap, &mac_ctx->mlme_cfg->he_caps.dot11_he_cap, + sizeof(tDot11fIEhe_cap)); + return QDF_STATUS_SUCCESS; + } + /** TODO: String items needs attention. **/ + qdf_mem_copy(he_cap, &session->he_config, sizeof(*he_cap)); + if (he_cap->ppet_present) { + value = WNI_CFG_HE_PPET_LEN; + /* if session is present, populate PPET based on band */ + if (!wlan_reg_is_24ghz_ch_freq(session->curr_op_freq)) + qdf_mem_copy(he_cap->ppet.ppe_threshold.ppe_th, + mac_ctx->mlme_cfg->he_caps.he_ppet_5g, + value); + else + qdf_mem_copy(he_cap->ppet.ppe_threshold.ppe_th, + mac_ctx->mlme_cfg->he_caps.he_ppet_2g, + value); + + ppet = he_cap->ppet.ppe_threshold.ppe_th; + he_cap->ppet.ppe_threshold.num_ppe_th = + lim_truncate_ppet(ppet, value); + } else { + he_cap->ppet.ppe_threshold.num_ppe_th = 0; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * populate_dot11f_he_operation() - pouldate HE Operation IE + * @mac_ctx: Global MAC context + * @session: PE session + * @he_op: pointer to HE Operation IE + * + * Populdate the HE Operation IE based on the session. + */ +QDF_STATUS +populate_dot11f_he_operation(struct mac_context *mac_ctx, + struct pe_session *session, tDot11fIEhe_op *he_op) +{ + qdf_mem_copy(he_op, &session->he_op, sizeof(*he_op)); + + he_op->present = 1; + if (!session->he_6ghz_band) { + he_op->vht_oper_present = 1; + if (session->ch_width > CH_WIDTH_40MHZ) { + he_op->vht_oper.info.chan_width = 1; + he_op->vht_oper.info.center_freq_seg0 = + session->ch_center_freq_seg0; + if (session->ch_width == CH_WIDTH_80P80MHZ || + session->ch_width == CH_WIDTH_160MHZ) + he_op->vht_oper.info.center_freq_seg1 = + session->ch_center_freq_seg1; + else + he_op->vht_oper.info.center_freq_seg1 = 0; + } else { + he_op->vht_oper.info.chan_width = 0; + he_op->vht_oper.info.center_freq_seg0 = 0; + he_op->vht_oper.info.center_freq_seg1 = 0; + } + } else { + he_op->oper_info_6g_present = 1; + he_op->oper_info_6g.info.ch_width = session->ch_width; + he_op->oper_info_6g.info.center_freq_seg0 = + session->ch_center_freq_seg0; + if (session->ch_width == CH_WIDTH_80P80MHZ || + session->ch_width == CH_WIDTH_160MHZ) { + he_op->oper_info_6g.info.center_freq_seg1 = + session->ch_center_freq_seg1; + he_op->oper_info_6g.info.ch_width = CH_WIDTH_160MHZ; + } else { + he_op->oper_info_6g.info.center_freq_seg1 = 0; + } + he_op->oper_info_6g.info.primary_ch = + wlan_reg_freq_to_chan(mac_ctx->pdev, + session->curr_op_freq); + he_op->oper_info_6g.info.dup_bcon = 0; + he_op->oper_info_6g.info.min_rate = 0; + } + lim_log_he_op(mac_ctx, he_op, session); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +populate_dot11f_he_6ghz_cap(struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fIEhe_6ghz_band_cap *he_6g_cap) +{ + struct mlme_ht_capabilities_info *ht_cap_info; + struct mlme_vht_capabilities_info *vht_cap_info; + + if (session && !session->he_6ghz_band) + return QDF_STATUS_SUCCESS; + + ht_cap_info = &mac_ctx->mlme_cfg->ht_caps.ht_cap_info; + vht_cap_info = &mac_ctx->mlme_cfg->vht_caps.vht_cap_info; + + he_6g_cap->present = 1; + he_6g_cap->min_mpdu_start_spacing = + mac_ctx->mlme_cfg->ht_caps.ampdu_params.mpdu_density; + if (session) + he_6g_cap->max_ampdu_len_exp = + session->vht_config.max_ampdu_lenexp; + else + he_6g_cap->max_ampdu_len_exp = + vht_cap_info->ampdu_len_exponent & 0x7; + he_6g_cap->max_mpdu_len = vht_cap_info->ampdu_len; + he_6g_cap->sm_pow_save = ht_cap_info->mimo_power_save; + he_6g_cap->rd_responder = 0; + he_6g_cap->rx_ant_pattern_consistency = 0; + he_6g_cap->tx_ant_pattern_consistency = 0; + + lim_log_he_6g_cap(mac_ctx, he_6g_cap); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +/** + * populate_dot11f_he_bss_color_change() - pouldate HE BSS color change IE + * @mac_ctx: Global MAC context + * @session: PE session + * @he_bss_color: pointer to HE BSS color change IE + * + * Populdate the HE BSS color change IE based on the session. + */ +QDF_STATUS +populate_dot11f_he_bss_color_change(struct mac_context *mac_ctx, + struct pe_session *session, + tDot11fIEbss_color_change *he_bss_color) +{ + if (!session->bss_color_changing) { + he_bss_color->present = 0; + return QDF_STATUS_SUCCESS; + } + + he_bss_color->present = 1; + he_bss_color->countdown = session->he_bss_color_change.countdown; + he_bss_color->new_color = session->he_bss_color_change.new_color; + + lim_log_he_bss_color(mac_ctx, he_bss_color); + + return QDF_STATUS_SUCCESS; +} +#endif +#endif + +#ifdef WLAN_SUPPORT_TWT +QDF_STATUS populate_dot11f_twt_extended_caps(struct mac_context *mac_ctx, + struct pe_session *pe_session, + tDot11fIEExtCap *dot11f) +{ + struct s_ext_cap *p_ext_cap; + + if (!pe_session->enable_session_twt_support) + return QDF_STATUS_SUCCESS; + + dot11f->num_bytes = DOT11F_IE_EXTCAP_MAX_LEN; + p_ext_cap = (struct s_ext_cap *)dot11f->bytes; + + if (pe_session->opmode == QDF_STA_MODE) + p_ext_cap->twt_requestor_support = + mac_ctx->mlme_cfg->twt_cfg.is_twt_requestor_enabled; + if (pe_session->opmode == QDF_SAP_MODE) + p_ext_cap->twt_responder_support = + mac_ctx->mlme_cfg->twt_cfg.is_twt_responder_enabled; + + dot11f->num_bytes = lim_compute_ext_cap_ie_length(dot11f); + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS populate_dot11f_btm_caps(struct mac_context *mac_ctx, + struct pe_session *pe_session, + struct sDot11fIEExtCap *dot11f) +{ + struct s_ext_cap *p_ext_cap; + uint32_t fw_akm_bitmap; + bool sae_can_roam; + + dot11f->num_bytes = DOT11F_IE_EXTCAP_MAX_LEN; + p_ext_cap = (struct s_ext_cap *)dot11f->bytes; + fw_akm_bitmap = mac_ctx->mlme_cfg->lfr.fw_akm_bitmap; + sae_can_roam = (((fw_akm_bitmap) & (1 << AKM_SAE)) ? true : false); + + if (pe_session->connected_akm == ANI_AKM_TYPE_SAE && + !sae_can_roam) { + p_ext_cap->bss_transition = 0; + pe_debug("Disable btm cap for SAE roam not supported"); + } + + dot11f->num_bytes = lim_compute_ext_cap_ie_length(dot11f); + + return QDF_STATUS_SUCCESS; +} + +/* parser_api.c ends here. */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/utils_parser.c b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/utils_parser.c new file mode 100644 index 0000000000000000000000000000000000000000..c4b8cc82ed404b80fc46fa0488ed48af4bc81152 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/mac/src/sys/legacy/src/utils/src/utils_parser.c @@ -0,0 +1,685 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * + * This file utils_parser.cc contains the code for parsing + * 802.11 messages. + * Author: Pierre Vandwalle + * Date: 03/18/02 + * History:- + * Date Modified by Modification Information + * -------------------------------------------------------------------- + * + */ + +#include "ani_global.h" +#include "utils_parser.h" +#include "lim_ser_des_utils.h" + +void convert_ssid(struct mac_context *mac, tSirMacSSid *pOld, tDot11fIESSID *pNew) +{ + pOld->length = pNew->num_ssid; + qdf_mem_copy(pOld->ssId, pNew->ssid, pNew->num_ssid); +} + +void convert_supp_rates(struct mac_context *mac, + tSirMacRateSet *pOld, tDot11fIESuppRates *pNew) +{ + pOld->numRates = pNew->num_rates; + qdf_mem_copy(pOld->rate, pNew->rates, pNew->num_rates); +} + +void convert_ext_supp_rates(struct mac_context *mac, + tSirMacRateSet *pOld, tDot11fIEExtSuppRates *pNew) +{ + pOld->numRates = pNew->num_rates; + qdf_mem_copy(pOld->rate, pNew->rates, pNew->num_rates); +} + +void convert_qos_caps(struct mac_context *mac, + tSirMacQosCapabilityIE *pOld, tDot11fIEQOSCapsAp *pNew) +{ + pOld->type = 46; + pOld->length = 1; + + pOld->qosInfo.count = pNew->count; +} + +void convert_qos_caps_station(struct mac_context *mac, + tSirMacQosCapabilityStaIE *pOld, + tDot11fIEQOSCapsStation *pNew) +{ + pOld->type = 46; + pOld->length = 1; + + pOld->qosInfo.moreDataAck = pNew->more_data_ack; + pOld->qosInfo.maxSpLen = pNew->max_sp_length; + pOld->qosInfo.qack = pNew->qack; + pOld->qosInfo.acbe_uapsd = pNew->acbe_uapsd; + pOld->qosInfo.acbk_uapsd = pNew->acbk_uapsd; + pOld->qosInfo.acvi_uapsd = pNew->acvi_uapsd; + pOld->qosInfo.acvo_uapsd = pNew->acvo_uapsd; +} + +QDF_STATUS convert_wpa(struct mac_context *mac, + tSirMacWpaInfo *pOld, tDot11fIEWPA *pNew) +{ + /* This is awful, I know, but the old code just rammed the IE into an */ + /* array... */ + uint8_t buffer[257]; + uint32_t status, written = 0, nbuffer = 257; + + status = dot11f_pack_ie_wpa(mac, pNew, buffer, nbuffer, &written); + if (DOT11F_FAILED(status)) { + pe_err("Failed to re-pack the WPA IE (0x%0x8)", status); + return QDF_STATUS_E_FAILURE; + } + + pOld->length = (uint8_t) written - 2; + qdf_mem_copy(pOld->info, buffer + 2, pOld->length); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS convert_wpa_opaque(struct mac_context *mac, + tSirMacWpaInfo *pOld, tDot11fIEWPAOpaque *pNew) +{ + /* This is awful, I know, but the old code just rammed the IE into */ + /* an opaque array. Note that we need to explicitly add the OUI! */ + pOld->length = pNew->num_data + 4; + pOld->info[0] = 0x00; + pOld->info[1] = 0x50; + pOld->info[2] = 0xf2; + pOld->info[3] = 0x01; + qdf_mem_copy(pOld->info + 4, pNew->data, pNew->num_data); + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_WAPI +QDF_STATUS convert_wapi_opaque(struct mac_context *mac, + tSirMacWapiInfo *pOld, + tDot11fIEWAPIOpaque *pNew) +{ + /* This is awful, I know, but the old code just rammed the IE into */ + /* an opaque array. Note that we need to explicitly add the OUI! */ + pOld->length = pNew->num_data; + qdf_mem_copy(pOld->info, pNew->data, pNew->num_data); + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS convert_wsc_opaque(struct mac_context *mac, + tSirAddie *pOld, tDot11fIEWscIEOpaque *pNew) +{ + /* This is awful, I know, but the old code just rammed the IE into */ + /* an opaque array. Note that we need to explicitly add the vendorIE and OUI ! */ + uint16_t curAddIELen = pOld->length; + + pOld->length = curAddIELen + pNew->num_data + 6; + pOld->addIEdata[curAddIELen++] = 0xdd; + pOld->addIEdata[curAddIELen++] = pNew->num_data + 4; + pOld->addIEdata[curAddIELen++] = 0x00; + pOld->addIEdata[curAddIELen++] = 0x50; + pOld->addIEdata[curAddIELen++] = 0xf2; + pOld->addIEdata[curAddIELen++] = 0x04; + qdf_mem_copy(pOld->addIEdata + curAddIELen, pNew->data, pNew->num_data); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS convert_p2p_opaque(struct mac_context *mac, + tSirAddie *pOld, tDot11fIEP2PIEOpaque *pNew) +{ + /* This is awful, I know, but the old code just rammed the IE into */ + /* an opaque array. Note that we need to explicitly add the vendorIE and OUI ! */ + uint16_t curAddIELen = pOld->length; + + pOld->length = curAddIELen + pNew->num_data + 6; + pOld->addIEdata[curAddIELen++] = 0xdd; + pOld->addIEdata[curAddIELen++] = pNew->num_data + 4; + pOld->addIEdata[curAddIELen++] = 0x50; + pOld->addIEdata[curAddIELen++] = 0x6f; + pOld->addIEdata[curAddIELen++] = 0x9A; + pOld->addIEdata[curAddIELen++] = 0x09; + qdf_mem_copy(pOld->addIEdata + curAddIELen, pNew->data, pNew->num_data); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_WFD +QDF_STATUS convert_wfd_opaque(struct mac_context *mac, + tSirAddie *pOld, tDot11fIEWFDIEOpaque *pNew) +{ + /* This is awful, I know, but the old code just rammed the IE into */ + /* an opaque array. Note that we need to explicitly add the vendorIE and OUI ! */ + uint16_t curAddIELen = pOld->length; + + pOld->length = curAddIELen + pNew->num_data + 6; + pOld->addIEdata[curAddIELen++] = 0xdd; + pOld->addIEdata[curAddIELen++] = pNew->num_data + 4; + pOld->addIEdata[curAddIELen++] = 0x50; + pOld->addIEdata[curAddIELen++] = 0x6f; + pOld->addIEdata[curAddIELen++] = 0x9A; + pOld->addIEdata[curAddIELen++] = 0x0a; + qdf_mem_copy(pOld->addIEdata + curAddIELen, pNew->data, pNew->num_data); + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS convert_rsn(struct mac_context *mac, + tSirMacRsnInfo *pOld, tDot11fIERSN *pNew) +{ + uint8_t buffer[257]; + uint32_t status, written = 0, nbuffer = 257; + + status = dot11f_pack_ie_rsn(mac, pNew, buffer, nbuffer, &written); + if (DOT11F_FAILED(status)) { + pe_err("Failed to re-pack the RSN IE (0x%0x8)", status); + return QDF_STATUS_E_FAILURE; + } + + pOld->length = (uint8_t) written - 2; + qdf_mem_copy(pOld->info, buffer + 2, pOld->length); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS convert_rsn_opaque(struct mac_context *mac, + tSirMacRsnInfo *pOld, tDot11fIERSNOpaque *pNew) +{ + /* This is awful, I know, but the old code just rammed the IE into */ + /* an opaque array. */ + pOld->length = pNew->num_data; + qdf_mem_copy(pOld->info, pNew->data, pOld->length); + + return QDF_STATUS_SUCCESS; +} + +void convert_power_caps(struct mac_context *mac, + tSirMacPowerCapabilityIE *pOld, + tDot11fIEPowerCaps *pNew) +{ + pOld->type = 33; + pOld->length = 2; + pOld->minTxPower = pNew->minTxPower; + pOld->maxTxPower = pNew->maxTxPower; +} + +void convert_supp_channels(struct mac_context *mac, + tSirMacSupportedChannelIE *pOld, + tDot11fIESuppChannels *pNew) +{ + pOld->type = 36; + pOld->length = (pNew->num_bands * 2); + qdf_mem_copy((uint8_t *) pOld->supportedChannels, + (uint8_t *) pNew->bands, pOld->length); +} + +void convert_cf_params(struct mac_context *mac, + tSirMacCfParamSet *pOld, tDot11fIECFParams *pNew) +{ + pOld->cfpCount = pNew->cfp_count; + pOld->cfpPeriod = pNew->cfp_period; + pOld->cfpMaxDuration = pNew->cfp_maxduration; + pOld->cfpDurRemaining = pNew->cfp_durremaining; +} + +void convert_fh_params(struct mac_context *mac, + tSirMacFHParamSet *pOld, tDot11fIEFHParamSet *pNew) +{ + pOld->dwellTime = pNew->dwell_time; + pOld->hopSet = pNew->hop_set; + pOld->hopPattern = pNew->hop_pattern; + pOld->hopIndex = pNew->hop_index; +} + +void convert_tim(struct mac_context *mac, tSirMacTim *pOld, tDot11fIETIM *pNew) +{ + pOld->dtimCount = pNew->dtim_count; + pOld->dtimPeriod = pNew->dtim_period; + pOld->bitmapControl = pNew->bmpctl; + pOld->bitmapLength = pNew->num_vbmp; + + qdf_mem_copy(pOld->bitmap, pNew->vbmp, pNew->num_vbmp); +} + +void convert_country(struct mac_context *mac, + tSirCountryInformation *pOld, tDot11fIECountry *pNew) +{ + int i; + + qdf_mem_copy(pOld->countryString, pNew->country, COUNTRY_STRING_LENGTH); + + pOld->numIntervals = pNew->num_triplets; + + for (i = 0; i < pNew->num_triplets; ++i) { + pOld->channelTransmitPower[i].channelNumber = + pNew->triplets[i][0]; + pOld->channelTransmitPower[i].numChannel = pNew->triplets[i][1]; + pOld->channelTransmitPower[i].maxTransmitPower = + pNew->triplets[i][2]; + } +} + +void convert_wmm_params(struct mac_context *mac, + tSirMacEdcaParamSetIE *pOld, tDot11fIEWMMParams *pNew) +{ + pOld->type = 221; + pOld->length = 24; + + qdf_mem_copy((uint8_t *) &pOld->qosInfo, (uint8_t *) &pNew->qosInfo, + 1); + + pOld->acbe.aci.aifsn = pNew->acbe_aifsn; + pOld->acbe.aci.acm = pNew->acbe_acm; + pOld->acbe.aci.aci = pNew->acbe_aci; + pOld->acbe.cw.min = pNew->acbe_acwmin; + pOld->acbe.cw.max = pNew->acbe_acwmax; + pOld->acbe.txoplimit = pNew->acbe_txoplimit; + + pOld->acbk.aci.aifsn = pNew->acbk_aifsn; + pOld->acbk.aci.acm = pNew->acbk_acm; + pOld->acbk.aci.aci = pNew->acbk_aci; + pOld->acbk.cw.min = pNew->acbk_acwmin; + pOld->acbk.cw.max = pNew->acbk_acwmax; + pOld->acbk.txoplimit = pNew->acbk_txoplimit; + + pOld->acvi.aci.aifsn = pNew->acvi_aifsn; + pOld->acvi.aci.acm = pNew->acvi_acm; + pOld->acvi.aci.aci = pNew->acvi_aci; + pOld->acvi.cw.min = pNew->acvi_acwmin; + pOld->acvi.cw.max = pNew->acvi_acwmax; + pOld->acvi.txoplimit = pNew->acvi_txoplimit; + + pOld->acvo.aci.aifsn = pNew->acvo_aifsn; + pOld->acvo.aci.acm = pNew->acvo_acm; + pOld->acvo.aci.aci = pNew->acvo_aci; + pOld->acvo.cw.min = pNew->acvo_acwmin; + pOld->acvo.cw.max = pNew->acvo_acwmax; + pOld->acvo.txoplimit = pNew->acvo_txoplimit; +} + +void convert_erp_info(struct mac_context *mac, + tSirMacErpInfo *pOld, tDot11fIEERPInfo *pNew) +{ + pOld->nonErpPresent = pNew->non_erp_present; + pOld->useProtection = pNew->use_prot; + pOld->barkerPreambleMode = pNew->barker_preamble; +} + +void convert_edca_param(struct mac_context *mac, + tSirMacEdcaParamSetIE *pOld, + tDot11fIEEDCAParamSet *pNew) +{ + pOld->type = 12; + pOld->length = 20; + + qdf_mem_copy((uint8_t *) &pOld->qosInfo, (uint8_t *) &pNew->qos, 1); + + pOld->acbe.aci.aifsn = pNew->acbe_aifsn; + pOld->acbe.aci.acm = pNew->acbe_acm; + pOld->acbe.aci.aci = pNew->acbe_aci; + pOld->acbe.cw.min = pNew->acbe_acwmin; + pOld->acbe.cw.max = pNew->acbe_acwmax; + pOld->acbe.txoplimit = pNew->acbe_txoplimit; + + pOld->acbk.aci.aifsn = pNew->acbk_aifsn; + pOld->acbk.aci.acm = pNew->acbk_acm; + pOld->acbk.aci.aci = pNew->acbk_aci; + pOld->acbk.cw.min = pNew->acbk_acwmin; + pOld->acbk.cw.max = pNew->acbk_acwmax; + pOld->acbk.txoplimit = pNew->acbk_txoplimit; + + pOld->acvi.aci.aifsn = pNew->acvi_aifsn; + pOld->acvi.aci.acm = pNew->acvi_acm; + pOld->acvi.aci.aci = pNew->acvi_aci; + pOld->acvi.cw.min = pNew->acvi_acwmin; + pOld->acvi.cw.max = pNew->acvi_acwmax; + pOld->acvi.txoplimit = pNew->acvi_txoplimit; + + pOld->acvo.aci.aifsn = pNew->acvo_aifsn; + pOld->acvo.aci.acm = pNew->acvo_acm; + pOld->acvo.aci.aci = pNew->acvo_aci; + pOld->acvo.cw.min = pNew->acvo_acwmin; + pOld->acvo.cw.max = pNew->acvo_acwmax; + pOld->acvo.txoplimit = pNew->acvo_txoplimit; + +} + +void convert_mu_edca_param(struct mac_context *mac_ctx, + tSirMacEdcaParamSetIE *mu_edca, + tDot11fIEmu_edca_param_set *ie) +{ + qdf_mem_copy((uint8_t *) &mu_edca->qosInfo, (uint8_t *) &ie->qos, 1); + + mu_edca->acbe.aci.aifsn = ie->acbe_aifsn; + mu_edca->acbe.aci.acm = ie->acbe_acm; + mu_edca->acbe.aci.aci = ie->acbe_aci; + mu_edca->acbe.cw.min = ie->acbe_acwmin; + mu_edca->acbe.cw.max = ie->acbe_acwmax; + mu_edca->acbe.mu_edca_timer = ie->acbe_muedca_timer; + + mu_edca->acbk.aci.aifsn = ie->acbk_aifsn; + mu_edca->acbk.aci.acm = ie->acbk_acm; + mu_edca->acbk.aci.aci = ie->acbk_aci; + mu_edca->acbk.cw.min = ie->acbk_acwmin; + mu_edca->acbk.cw.max = ie->acbk_acwmax; + mu_edca->acbk.mu_edca_timer = ie->acbk_muedca_timer; + + mu_edca->acvi.aci.aifsn = ie->acvi_aifsn; + mu_edca->acvi.aci.acm = ie->acvi_acm; + mu_edca->acvi.aci.aci = ie->acvi_aci; + mu_edca->acvi.cw.min = ie->acvi_acwmin; + mu_edca->acvi.cw.max = ie->acvi_acwmax; + mu_edca->acvi.mu_edca_timer = ie->acvi_muedca_timer; + + mu_edca->acvo.aci.aifsn = ie->acvo_aifsn; + mu_edca->acvo.aci.acm = ie->acvo_acm; + mu_edca->acvo.aci.aci = ie->acvo_aci; + mu_edca->acvo.cw.min = ie->acvo_acwmin; + mu_edca->acvo.cw.max = ie->acvo_acwmax; + mu_edca->acvo.mu_edca_timer = ie->acvo_muedca_timer; + +} + +void convert_tspec(struct mac_context *mac, + struct mac_tspec_ie *pOld, tDot11fIETSPEC *pNew) +{ + pOld->tsinfo.traffic.trafficType = (uint16_t) pNew->traffic_type; + pOld->tsinfo.traffic.tsid = (uint16_t) pNew->tsid; + pOld->tsinfo.traffic.direction = (uint16_t) pNew->direction; + pOld->tsinfo.traffic.accessPolicy = (uint16_t) pNew->access_policy; + pOld->tsinfo.traffic.aggregation = (uint16_t) pNew->aggregation; + pOld->tsinfo.traffic.psb = (uint16_t) pNew->psb; + pOld->tsinfo.traffic.userPrio = (uint16_t) pNew->user_priority; + pOld->tsinfo.traffic.ackPolicy = (uint16_t) pNew->tsinfo_ack_pol; + + pOld->tsinfo.schedule.schedule = (uint8_t) pNew->schedule; + + pOld->nomMsduSz = pNew->size; + pOld->maxMsduSz = pNew->max_msdu_size; + pOld->minSvcInterval = pNew->min_service_int; + pOld->maxSvcInterval = pNew->max_service_int; + pOld->inactInterval = pNew->inactivity_int; + pOld->suspendInterval = pNew->suspension_int; + pOld->svcStartTime = pNew->service_start_time; + pOld->minDataRate = pNew->min_data_rate; + pOld->meanDataRate = pNew->mean_data_rate; + pOld->peakDataRate = pNew->peak_data_rate; + pOld->maxBurstSz = pNew->burst_size; + pOld->delayBound = pNew->delay_bound; + pOld->minPhyRate = pNew->min_phy_rate; + pOld->surplusBw = pNew->surplus_bw_allowance; + pOld->mediumTime = pNew->medium_time; +} + +QDF_STATUS convert_tclas(struct mac_context *mac, + tSirTclasInfo *pOld, tDot11fIETCLAS *pNew) +{ + uint32_t length = 0; + + if (DOT11F_FAILED(dot11f_get_packed_ietclas(mac, pNew, &length))) { + return QDF_STATUS_E_FAILURE; + } + + pOld->tclas.type = DOT11F_EID_TCLAS; + pOld->tclas.length = (uint8_t) length; + pOld->tclas.userPrio = pNew->user_priority; + pOld->tclas.classifierType = pNew->classifier_type; + pOld->tclas.classifierMask = pNew->classifier_mask; + + switch (pNew->classifier_type) { + case 0: + qdf_mem_copy(pOld->tclasParams.eth.srcAddr, + pNew->info.EthParams.source, 6); + qdf_mem_copy(pOld->tclasParams.eth.dstAddr, + pNew->info.EthParams.dest, 6); + pOld->tclasParams.eth.type = pNew->info.EthParams.type; + break; + case 1: + pOld->version = pNew->info.IpParams.version; + if (4 == pNew->info.IpParams.version) { + pOld->tclasParams.ipv4.version = 4; + qdf_mem_copy(pOld->tclasParams.ipv4.srcIpAddr, + pNew->info.IpParams.params.IpV4Params. + source, 4); + qdf_mem_copy(pOld->tclasParams.ipv4.dstIpAddr, + pNew->info.IpParams.params.IpV4Params.dest, + 4); + pOld->tclasParams.ipv4.srcPort = + pNew->info.IpParams.params.IpV4Params.src_port; + pOld->tclasParams.ipv4.dstPort = + pNew->info.IpParams.params.IpV4Params.dest_port; + pOld->tclasParams.ipv4.dscp = + pNew->info.IpParams.params.IpV4Params.DSCP; + pOld->tclasParams.ipv4.protocol = + pNew->info.IpParams.params.IpV4Params.proto; + pOld->tclasParams.ipv4.rsvd = + pNew->info.IpParams.params.IpV4Params.reserved; + } else if (6 == pNew->info.IpParams.version) { + pOld->tclasParams.ipv6.version = 6; + qdf_mem_copy((uint8_t *) pOld->tclasParams.ipv6. + srcIpAddr, + (uint8_t *) pNew->info.IpParams.params. + IpV6Params.source, 16); + qdf_mem_copy((uint8_t *) pOld->tclasParams.ipv6. + dstIpAddr, + (uint8_t *) pNew->info.IpParams.params. + IpV6Params.dest, 16); + pOld->tclasParams.ipv6.srcPort = + pNew->info.IpParams.params.IpV6Params.src_port; + pOld->tclasParams.ipv6.dstPort = + pNew->info.IpParams.params.IpV6Params.dest_port; + qdf_mem_copy((uint8_t *) pOld->tclasParams.ipv6. + flowLabel, + (uint8_t *) pNew->info.IpParams.params. + IpV6Params.flow_label, 3); + } else { + return QDF_STATUS_E_FAILURE; + } + break; + case 2: + pOld->tclasParams.t8021dq.tag = + pNew->info.Params8021dq.tag_type; + break; + default: + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +void convert_wmmtspec(struct mac_context *mac, + struct mac_tspec_ie *pOld, tDot11fIEWMMTSPEC *pNew) +{ + pOld->tsinfo.traffic.trafficType = (uint16_t) pNew->traffic_type; + pOld->tsinfo.traffic.tsid = (uint16_t) pNew->tsid; + pOld->tsinfo.traffic.direction = (uint16_t) pNew->direction; + pOld->tsinfo.traffic.accessPolicy = (uint16_t) pNew->access_policy; + pOld->tsinfo.traffic.aggregation = (uint16_t) pNew->aggregation; + pOld->tsinfo.traffic.psb = (uint16_t) pNew->psb; + pOld->tsinfo.traffic.userPrio = (uint16_t) pNew->user_priority; + pOld->tsinfo.traffic.ackPolicy = (uint16_t) pNew->tsinfo_ack_pol; + pOld->nomMsduSz = (pNew->fixed << 15) | pNew->size; + pOld->maxMsduSz = pNew->max_msdu_size; + pOld->minSvcInterval = pNew->min_service_int; + pOld->maxSvcInterval = pNew->max_service_int; + pOld->inactInterval = pNew->inactivity_int; + pOld->suspendInterval = pNew->suspension_int; + pOld->svcStartTime = pNew->service_start_time; + pOld->minDataRate = pNew->min_data_rate; + pOld->meanDataRate = pNew->mean_data_rate; + pOld->peakDataRate = pNew->peak_data_rate; + pOld->maxBurstSz = pNew->burst_size; + pOld->delayBound = pNew->delay_bound; + pOld->minPhyRate = pNew->min_phy_rate; + pOld->surplusBw = pNew->surplus_bw_allowance; + pOld->mediumTime = pNew->medium_time; +} + +QDF_STATUS convert_wmmtclas(struct mac_context *mac, + tSirTclasInfo *pOld, tDot11fIEWMMTCLAS *pNew) +{ + uint32_t length = 0; + + if (DOT11F_FAILED(dot11f_get_packed_iewmmtclas(mac, pNew, &length))) { + return QDF_STATUS_E_FAILURE; + } + + pOld->tclas.type = DOT11F_EID_WMMTCLAS; + pOld->tclas.length = (uint8_t) length; + pOld->tclas.userPrio = pNew->user_priority; + pOld->tclas.classifierType = pNew->classifier_type; + pOld->tclas.classifierMask = pNew->classifier_mask; + + switch (pNew->classifier_type) { + case 0: + qdf_mem_copy(pOld->tclasParams.eth.srcAddr, + pNew->info.EthParams.source, 6); + qdf_mem_copy(pOld->tclasParams.eth.dstAddr, + pNew->info.EthParams.dest, 6); + pOld->tclasParams.eth.type = pNew->info.EthParams.type; + break; + case 1: + pOld->version = pNew->info.IpParams.version; + if (4 == pNew->info.IpParams.version) { + pOld->tclasParams.ipv4.version = 4; + qdf_mem_copy(pOld->tclasParams.ipv4.srcIpAddr, + pNew->info.IpParams.params.IpV4Params. + source, 4); + qdf_mem_copy(pOld->tclasParams.ipv4.dstIpAddr, + pNew->info.IpParams.params.IpV4Params.dest, + 4); + pOld->tclasParams.ipv4.srcPort = + pNew->info.IpParams.params.IpV4Params.src_port; + pOld->tclasParams.ipv4.dstPort = + pNew->info.IpParams.params.IpV4Params.dest_port; + pOld->tclasParams.ipv4.dscp = + pNew->info.IpParams.params.IpV4Params.DSCP; + pOld->tclasParams.ipv4.protocol = + pNew->info.IpParams.params.IpV4Params.proto; + pOld->tclasParams.ipv4.rsvd = + pNew->info.IpParams.params.IpV4Params.reserved; + } else if (6 == pNew->info.IpParams.version) { + pOld->tclasParams.ipv6.version = 6; + qdf_mem_copy((uint8_t *) pOld->tclasParams.ipv6. + srcIpAddr, + (uint8_t *) pNew->info.IpParams.params. + IpV6Params.source, 16); + qdf_mem_copy((uint8_t *) pOld->tclasParams.ipv6. + dstIpAddr, + (uint8_t *) pNew->info.IpParams.params. + IpV6Params.dest, 16); + pOld->tclasParams.ipv6.srcPort = + pNew->info.IpParams.params.IpV6Params.src_port; + pOld->tclasParams.ipv6.dstPort = + pNew->info.IpParams.params.IpV6Params.dest_port; + qdf_mem_copy((uint8_t *) pOld->tclasParams.ipv6. + flowLabel, + (uint8_t *) pNew->info.IpParams.params. + IpV6Params.flow_label, 3); + } else { + return QDF_STATUS_E_FAILURE; + } + break; + case 2: + pOld->tclasParams.t8021dq.tag = + pNew->info.Params8021dq.tag_type; + break; + default: + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +void convert_ts_delay(struct mac_context *mac, + tSirMacTsDelayIE *pOld, tDot11fIETSDelay *pNew) +{ + pOld->type = DOT11F_EID_TSDELAY; + pOld->length = 4U; + pOld->delay = pNew->delay; +} + +void convert_schedule(struct mac_context *mac, + tSirMacScheduleIE *pOld, tDot11fIESchedule *pNew) +{ + pOld->type = DOT11F_EID_SCHEDULE; + pOld->length = DOT11F_IE_SCHEDULE_MIN_LEN; + + pOld->info.aggregation = pNew->aggregation; + pOld->info.tsid = pNew->tsid; + pOld->info.direction = pNew->direction; + + pOld->svcStartTime = pNew->service_start_time; + pOld->svcInterval = pNew->service_interval; + pOld->specInterval = pNew->spec_interval; +} + +void convert_wmm_schedule(struct mac_context *mac, + tSirMacScheduleIE *pOld, tDot11fIEWMMSchedule *pNew) +{ + pOld->type = DOT11F_EID_WMMSCHEDULE; + pOld->length = DOT11F_IE_WMMSCHEDULE_MIN_LEN; + + pOld->info.aggregation = pNew->aggregation; + pOld->info.tsid = pNew->tsid; + pOld->info.direction = pNew->direction; + + pOld->svcStartTime = pNew->service_start_time; + pOld->svcInterval = pNew->service_interval; + pOld->specInterval = pNew->spec_interval; +} + +void convert_qos_mapset_frame(struct mac_context *mac, struct qos_map_set *qos, + tDot11fIEQosMapSet *dot11f_ie) +{ + uint8_t i, j = 0; + uint8_t dot11_dscp_exception_sz; + + if (dot11f_ie->num_dscp_exceptions < DOT11F_IE_QOSMAPSET_MIN_LEN || + dot11f_ie->num_dscp_exceptions % 2) { + pe_debug("Invalid num_dscp_exceptions %d", + dot11f_ie->num_dscp_exceptions); + return; + } + + dot11_dscp_exception_sz = dot11f_ie->num_dscp_exceptions - + DOT11F_IE_QOSMAPSET_MIN_LEN; + qos->num_dscp_exceptions = dot11_dscp_exception_sz / 2; + if (qos->num_dscp_exceptions > QOS_MAP_MAX_EX) + qos->num_dscp_exceptions = QOS_MAP_MAX_EX; + + for (i = 0; i < qos->num_dscp_exceptions && + j < dot11_dscp_exception_sz - 1; i++) { + qos->dscp_exceptions[i][0] = dot11f_ie->dscp_exceptions[j++]; + qos->dscp_exceptions[i][1] = dot11f_ie->dscp_exceptions[j++]; + } + + for (i = 0; i < QOS_MAP_RANGE_NUM && + j < dot11f_ie->num_dscp_exceptions - 1; i++) { + qos->dscp_range[i][0] = dot11f_ie->dscp_exceptions[j++]; + qos->dscp_range[i][1] = dot11f_ie->dscp_exceptions[j++]; + } +} + +/* utils_parser.c ends here. */ diff --git a/drivers/staging/qcacld-3.0/core/pld/inc/pld_common.h b/drivers/staging/qcacld-3.0/core/pld/inc/pld_common.h new file mode 100644 index 0000000000000000000000000000000000000000..e648a76b9f6efb795fbee4d00db1273dc44691ef --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/inc/pld_common.h @@ -0,0 +1,943 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __PLD_COMMON_H__ +#define __PLD_COMMON_H__ + +#include +#include +#include +#include + +#ifdef CONFIG_CNSS_UTILS +#include +#endif + +#define PLD_IMAGE_FILE "athwlan.bin" +#define PLD_UTF_FIRMWARE_FILE "utf.bin" +#define PLD_BOARD_DATA_FILE "fakeboar.bin" +#define PLD_OTP_FILE "otp.bin" +#define PLD_SETUP_FILE "athsetup.bin" +#define PLD_EPPING_FILE "epping.bin" +#define PLD_EVICTED_FILE "" + +#define TOTAL_DUMP_SIZE 0x00200000 + +#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC +#include +#endif + +/** + * enum pld_bus_type - bus type + * @PLD_BUS_TYPE_NONE: invalid bus type, only return in error cases + * @PLD_BUS_TYPE_PCIE: PCIE bus + * @PLD_BUS_TYPE_SNOC: SNOC bus + * @PLD_BUS_TYPE_SDIO: SDIO bus + * @PLD_BUS_TYPE_USB : USB bus + * @PLD_BUS_TYPE_SNOC_FW_SIM : SNOC FW SIM bus + * @PLD_BUS_TYPE_PCIE_FW_SIM : PCIE FW SIM bus + * @PLD_BUS_TYPE_IPCI : IPCI bus + */ +enum pld_bus_type { + PLD_BUS_TYPE_NONE = -1, + PLD_BUS_TYPE_PCIE = 0, + PLD_BUS_TYPE_SNOC, + PLD_BUS_TYPE_SDIO, + PLD_BUS_TYPE_USB, + PLD_BUS_TYPE_SNOC_FW_SIM, + PLD_BUS_TYPE_PCIE_FW_SIM, + PLD_BUS_TYPE_IPCI, +}; + +#define PLD_MAX_FIRMWARE_SIZE (1 * 1024 * 1024) + +/** + * enum pld_bus_width_type - bus bandwidth + * @PLD_BUS_WIDTH_NONE: don't vote for bus bandwidth + * @PLD_BUS_WIDTH_IDLE: vote for idle bandwidth + * @PLD_BUS_WIDTH_LOW: vote for low bus bandwidth + * @PLD_BUS_WIDTH_MEDIUM: vote for medium bus bandwidth + * @PLD_BUS_WIDTH_HIGH: vote for high bus bandwidth + * @PLD_BUS_WIDTH_VERY_HIGH: vote for very high bus bandwidth + * @PLD_BUS_WIDTH_LOW_LATENCY: vote for low latency bus bandwidth + */ +enum pld_bus_width_type { + PLD_BUS_WIDTH_NONE, + PLD_BUS_WIDTH_IDLE, + PLD_BUS_WIDTH_LOW, + PLD_BUS_WIDTH_MEDIUM, + PLD_BUS_WIDTH_HIGH, + PLD_BUS_WIDTH_VERY_HIGH, + PLD_BUS_WIDTH_LOW_LATENCY, +}; + +#define PLD_MAX_FILE_NAME NAME_MAX + +/** + * struct pld_fw_file - WLAN FW file names + * @image_file: WLAN FW image file + * @board_data: WLAN FW board data file + * @otp_data: WLAN FW OTP file + * @utf_file: WLAN FW UTF file + * @utf_board_data: WLAN FW UTF board data file + * @epping_file: WLAN FW EPPING mode file + * @evicted_data: WLAN FW evicted file + * @setup_file: WLAN FW setup file + * + * pld_fw_files is used to store WLAN FW file names + */ +struct pld_fw_files { + char image_file[PLD_MAX_FILE_NAME]; + char board_data[PLD_MAX_FILE_NAME]; + char otp_data[PLD_MAX_FILE_NAME]; + char utf_file[PLD_MAX_FILE_NAME]; + char utf_board_data[PLD_MAX_FILE_NAME]; + char epping_file[PLD_MAX_FILE_NAME]; + char evicted_data[PLD_MAX_FILE_NAME]; + char setup_file[PLD_MAX_FILE_NAME]; + char ibss_image_file[PLD_MAX_FILE_NAME]; +}; + +/** + * enum pld_platform_cap_flag - platform capability flag + * @PLD_HAS_EXTERNAL_SWREG: has external regulator + * @PLD_HAS_UART_ACCESS: has UART access + * @PLD_HAS_DRV_SUPPORT: has PCIe DRV support + */ +enum pld_platform_cap_flag { + PLD_HAS_EXTERNAL_SWREG = 0x01, + PLD_HAS_UART_ACCESS = 0x02, + PLD_HAS_DRV_SUPPORT = 0x04, +}; + +/** + * struct pld_platform_cap - platform capabilities + * @cap_flag: capabilities flag + * + * pld_platform_cap provides platform capabilities which are + * extracted from DTS. + */ +struct pld_platform_cap { + u32 cap_flag; +}; + +/** + * enum pld_uevent - PLD uevent event types + * @PLD_FW_DOWN: firmware is down + * @PLD_FW_CRASHED: firmware has crashed + * @PLD_FW_RECOVERY_START: firmware is starting recovery + */ +enum pld_uevent { + PLD_FW_DOWN, + PLD_FW_CRASHED, + PLD_FW_RECOVERY_START, + PLD_FW_HANG_EVENT, +}; + +/** + * struct pld_uevent_data - uevent status received from platform driver + * @uevent: uevent type + * @fw_down: FW down info + */ +struct pld_uevent_data { + enum pld_uevent uevent; + union { + struct { + bool crashed; + } fw_down; + struct { + void *hang_event_data; + u16 hang_event_data_len; + } hang_data; + }; +}; + +/** + * struct pld_ce_tgt_pipe_cfg - copy engine target pipe configuration + * @pipe_num: pipe number + * @pipe_dir: pipe direction + * @nentries: number of entries + * @nbytes_max: max number of bytes + * @flags: flags + * @reserved: reserved + * + * pld_ce_tgt_pipe_cfg is used to store copy engine target pipe + * configuration. + */ +struct pld_ce_tgt_pipe_cfg { + u32 pipe_num; + u32 pipe_dir; + u32 nentries; + u32 nbytes_max; + u32 flags; + u32 reserved; +}; + +/** + * struct pld_ce_svc_pipe_cfg - copy engine service pipe configuration + * @service_id: service ID + * @pipe_dir: pipe direction + * @pipe_num: pipe number + * + * pld_ce_svc_pipe_cfg is used to store copy engine service pipe + * configuration. + */ +struct pld_ce_svc_pipe_cfg { + u32 service_id; + u32 pipe_dir; + u32 pipe_num; +}; + +/** + * struct pld_shadow_reg_cfg - shadow register configuration + * @ce_id: copy engine ID + * @reg_offset: register offset + * + * pld_shadow_reg_cfg is used to store shadow register configuration. + */ +struct pld_shadow_reg_cfg { + u16 ce_id; + u16 reg_offset; +}; + +/** + * struct pld_shadow_reg_v2_cfg - shadow register version 2 configuration + * @addr: shadow register physical address + * + * pld_shadow_reg_v2_cfg is used to store shadow register version 2 + * configuration. + */ +struct pld_shadow_reg_v2_cfg { + u32 addr; +}; + +/** + * struct pld_rri_over_ddr_cfg_s - rri_over_ddr configuration + * @base_addr_low: lower 32bit + * @base_addr_high: higher 32bit + * + * pld_rri_over_ddr_cfg_s is used in Genoa to pass rri_over_ddr configuration + * to firmware to update ring/write index in host DDR. + */ +struct pld_rri_over_ddr_cfg { + u32 base_addr_low; + u32 base_addr_high; +}; + +/** + * struct pld_wlan_enable_cfg - WLAN FW configuration + * @num_ce_tgt_cfg: number of CE target configuration + * @ce_tgt_cfg: CE target configuration + * @num_ce_svc_pipe_cfg: number of CE service configuration + * @ce_svc_cfg: CE service configuration + * @num_shadow_reg_cfg: number of shadow register configuration + * @shadow_reg_cfg: shadow register configuration + * @num_shadow_reg_v2_cfg: number of shadow register version 2 configuration + * @shadow_reg_v2_cfg: shadow register version 2 configuration + * @rri_over_ddr_cfg_valid: valid flag for rri_over_ddr config + * @rri_over_ddr_cfg: rri over ddr config + * + * pld_wlan_enable_cfg stores WLAN FW configurations. It will be + * passed to WLAN FW when WLAN host driver calls wlan_enable. + */ +struct pld_wlan_enable_cfg { + u32 num_ce_tgt_cfg; + struct pld_ce_tgt_pipe_cfg *ce_tgt_cfg; + u32 num_ce_svc_pipe_cfg; + struct pld_ce_svc_pipe_cfg *ce_svc_cfg; + u32 num_shadow_reg_cfg; + struct pld_shadow_reg_cfg *shadow_reg_cfg; + u32 num_shadow_reg_v2_cfg; + struct pld_shadow_reg_v2_cfg *shadow_reg_v2_cfg; + bool rri_over_ddr_cfg_valid; + struct pld_rri_over_ddr_cfg rri_over_ddr_cfg; +}; + +/** + * enum pld_driver_mode - WLAN host driver mode + * @PLD_MISSION: mission mode + * @PLD_FTM: FTM mode + * @PLD_EPPING: EPPING mode + * @PLD_WALTEST: WAL test mode, FW standalone test mode + * @PLD_OFF: OFF mode + * @PLD_COLDBOOT_CALIBRATION: Cold Boot Calibration Mode + * @PLD_FTM_COLDBOOT_CALIBRATION: Cold Boot Calibration for FTM Mode + */ +enum pld_driver_mode { + PLD_MISSION, + PLD_FTM, + PLD_EPPING, + PLD_WALTEST, + PLD_OFF, + PLD_COLDBOOT_CALIBRATION = 7, + PLD_FTM_COLDBOOT_CALIBRATION = 10 +}; + +/** + * struct pld_device_version - WLAN device version info + * @family_number: family number of WLAN SOC HW + * @device_number: device number of WLAN SOC HW + * @major_version: major version of WLAN SOC HW + * @minor_version: minor version of WLAN SOC HW + * + * pld_device_version is used to store WLAN device version info + */ + +struct pld_device_version { + u32 family_number; + u32 device_number; + u32 major_version; + u32 minor_version; +}; + +#define PLD_MAX_TIMESTAMP_LEN 32 + +/** + * struct pld_soc_info - SOC information + * @v_addr: virtual address of preallocated memory + * @p_addr: physical address of preallcoated memory + * @chip_id: chip ID + * @chip_family: chip family + * @board_id: board ID + * @soc_id: SOC ID + * @fw_version: FW version + * @fw_build_timestamp: FW build timestamp + * @device_version: WLAN device version info + * + * pld_soc_info is used to store WLAN SOC information. + */ +struct pld_soc_info { + void __iomem *v_addr; + phys_addr_t p_addr; + u32 chip_id; + u32 chip_family; + u32 board_id; + u32 soc_id; + u32 fw_version; + char fw_build_timestamp[PLD_MAX_TIMESTAMP_LEN + 1]; + struct pld_device_version device_version; +}; + +/** + * enum pld_recovery_reason - WLAN host driver recovery reason + * @PLD_REASON_DEFAULT: default + * @PLD_REASON_LINK_DOWN: PCIe link down + */ +enum pld_recovery_reason { + PLD_REASON_DEFAULT, + PLD_REASON_LINK_DOWN +}; + +#ifdef FEATURE_WLAN_TIME_SYNC_FTM +/** + * enum pld_wlan_time_sync_trigger_type - WLAN time sync trigger type + * @PLD_TRIGGER_POSITIVE_EDGE: Positive edge trigger + * @PLD_TRIGGER_NEGATIVE_EDGE: Negative edge trigger + */ +enum pld_wlan_time_sync_trigger_type { + PLD_TRIGGER_POSITIVE_EDGE, + PLD_TRIGGER_NEGATIVE_EDGE +}; +#endif /* FEATURE_WLAN_TIME_SYNC_FTM */ + +/** + * struct pld_driver_ops - driver callback functions + * @probe: required operation, will be called when device is detected + * @remove: required operation, will be called when device is removed + * @idle_shutdown: required operation, will be called when device is doing + * idle shutdown after interface inactivity timer has fired + * @idle_restart: required operation, will be called when device is doing + * idle restart after idle shutdown + * @shutdown: optional operation, will be called during SSR + * @reinit: optional operation, will be called during SSR + * @crash_shutdown: optional operation, will be called when a crash is + * detected + * @suspend: required operation, will be called for power management + * is enabled + * @resume: required operation, will be called for power management + * is enabled + * @modem_status: optional operation, will be called when platform driver + * sending modem power status to WLAN FW + * @uevent: optional operation, will be called when platform driver + * updating driver status + * @runtime_suspend: optional operation, prepare the device for a condition + * in which it won't be able to communicate with the CPU(s) + * and RAM due to power management. + * @runtime_resume: optional operation, put the device into the fully + * active state in response to a wakeup event generated by + * hardware or at the request of software. + * @suspend_noirq: optional operation, complete the actions started by suspend() + * @resume_noirq: optional operation, prepare for the execution of resume() + */ +struct pld_driver_ops { + int (*probe)(struct device *dev, + enum pld_bus_type bus_type, + void *bdev, void *id); + void (*remove)(struct device *dev, + enum pld_bus_type bus_type); + int (*idle_shutdown)(struct device *dev, + enum pld_bus_type bus_type); + int (*idle_restart)(struct device *dev, + enum pld_bus_type bus_type); + void (*shutdown)(struct device *dev, + enum pld_bus_type bus_type); + int (*reinit)(struct device *dev, + enum pld_bus_type bus_type, + void *bdev, void *id); + void (*crash_shutdown)(struct device *dev, + enum pld_bus_type bus_type); + int (*suspend)(struct device *dev, + enum pld_bus_type bus_type, + pm_message_t state); + int (*resume)(struct device *dev, + enum pld_bus_type bus_type); + int (*reset_resume)(struct device *dev, + enum pld_bus_type bus_type); + void (*modem_status)(struct device *dev, + enum pld_bus_type bus_type, + int state); + void (*uevent)(struct device *dev, struct pld_uevent_data *uevent); + int (*runtime_suspend)(struct device *dev, + enum pld_bus_type bus_type); + int (*runtime_resume)(struct device *dev, + enum pld_bus_type bus_type); + int (*suspend_noirq)(struct device *dev, + enum pld_bus_type bus_type); + int (*resume_noirq)(struct device *dev, + enum pld_bus_type bus_type); +}; + +int pld_init(void); +void pld_deinit(void); + +int pld_register_driver(struct pld_driver_ops *ops); +void pld_unregister_driver(void); + +int pld_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode); +int pld_wlan_disable(struct device *dev, enum pld_driver_mode mode); +int pld_set_fw_log_mode(struct device *dev, u8 fw_log_mode); +void pld_get_default_fw_files(struct pld_fw_files *pfw_files); +int pld_get_fw_files_for_target(struct device *dev, + struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version); +int pld_prevent_l1(struct device *dev); +void pld_allow_l1(struct device *dev); +void pld_is_pci_link_down(struct device *dev); +void pld_get_bus_reg_dump(struct device *dev, uint8_t *buf, uint32_t len); +int pld_shadow_control(struct device *dev, bool enable); +void pld_schedule_recovery_work(struct device *dev, + enum pld_recovery_reason reason); + +#ifdef FEATURE_WLAN_TIME_SYNC_FTM +int pld_get_audio_wlan_timestamp(struct device *dev, + enum pld_wlan_time_sync_trigger_type type, + uint64_t *ts); +#endif /* FEATURE_WLAN_TIME_SYNC_FTM */ + +#ifdef CONFIG_CNSS_UTILS +/** + * pld_set_wlan_unsafe_channel() - Set unsafe channel + * @dev: device + * @unsafe_ch_list: unsafe channel list + * @ch_count: number of channel + * + * Return: 0 for success + * Non zero failure code for errors + */ +static inline int pld_set_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, + u16 ch_count) +{ + return cnss_utils_set_wlan_unsafe_channel(dev, unsafe_ch_list, + ch_count); +} +/** + * pld_get_wlan_unsafe_channel() - Get unsafe channel + * @dev: device + * @unsafe_ch_list: buffer to unsafe channel list + * @ch_count: number of channel + * @buf_len: buffer length + * + * Return WLAN unsafe channel to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static inline int pld_get_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, + u16 *ch_count, u16 buf_len) +{ + return cnss_utils_get_wlan_unsafe_channel(dev, unsafe_ch_list, + ch_count, buf_len); +} +/** + * pld_wlan_set_dfs_nol() - Set DFS info + * @dev: device + * @info: DFS info + * @info_len: info length + * + * Return: 0 for success + * Non zero failure code for errors + */ +static inline int pld_wlan_set_dfs_nol(struct device *dev, void *info, + u16 info_len) +{ + return cnss_utils_wlan_set_dfs_nol(dev, info, info_len); +} +/** + * pld_wlan_get_dfs_nol() - Get DFS info + * @dev: device + * @info: buffer to DFS info + * @info_len: info length + * + * Return DFS info to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static inline int pld_wlan_get_dfs_nol(struct device *dev, + void *info, u16 info_len) +{ + return cnss_utils_wlan_get_dfs_nol(dev, info, info_len); +} +/** + * pld_get_wlan_mac_address() - API to query MAC address from Platform + * Driver + * @dev: Device Structure + * @num: Pointer to number of MAC address supported + * + * Platform Driver can have MAC address stored. This API needs to be used + * to get those MAC address + * + * Return: Pointer to the list of MAC address + */ +static inline uint8_t *pld_get_wlan_mac_address(struct device *dev, + uint32_t *num) +{ + return cnss_utils_get_wlan_mac_address(dev, num); +} + +/** + * pld_get_wlan_derived_mac_address() - API to query derived MAC address + * from platform Driver + * @dev: Device Structure + * @num: Pointer to number of MAC address supported + * + * Platform Driver can have MAC address stored. This API needs to be used + * to get those MAC address + * + * Return: Pointer to the list of MAC address + */ +static inline uint8_t *pld_get_wlan_derived_mac_address(struct device *dev, + uint32_t *num) +{ + return cnss_utils_get_wlan_derived_mac_address(dev, num); +} + +/** + * pld_increment_driver_load_cnt() - Maintain driver load count + * @dev: device + * + * This function maintain a count which get increase whenever wiphy + * is registered + * + * Return: void + */ +static inline void pld_increment_driver_load_cnt(struct device *dev) +{ + cnss_utils_increment_driver_load_cnt(dev); +} +/** + * pld_get_driver_load_cnt() - get driver load count + * @dev: device + * + * This function provide total wiphy registration count from starting + * + * Return: driver load count + */ +static inline int pld_get_driver_load_cnt(struct device *dev) +{ + return cnss_utils_get_driver_load_cnt(dev); +} +#else +static inline int pld_set_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, + u16 ch_count) +{ + return -EINVAL; +} +static inline int pld_get_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, + u16 *ch_count, u16 buf_len) +{ + return -EINVAL; +} +static inline int pld_wlan_set_dfs_nol(struct device *dev, + void *info, u16 info_len) +{ + return -EINVAL; +} +static inline int pld_wlan_get_dfs_nol(struct device *dev, + void *info, u16 info_len) +{ + return -EINVAL; +} +static inline uint8_t *pld_get_wlan_mac_address(struct device *dev, + uint32_t *num) +{ + *num = 0; + return NULL; +} + +static inline uint8_t *pld_get_wlan_derived_mac_address(struct device *dev, + uint32_t *num) +{ + *num = 0; + return NULL; +} + +static inline void pld_increment_driver_load_cnt(struct device *dev) {} +static inline int pld_get_driver_load_cnt(struct device *dev) +{ + return -EINVAL; +} +#endif +int pld_wlan_pm_control(struct device *dev, bool vote); +void *pld_get_virt_ramdump_mem(struct device *dev, unsigned long *size); + +/** + * pld_release_virt_ramdump_mem() - Release virtual ramdump memory + * @dev: device + * @address: buffer to virtual memory address + * + * Return: void + */ +void pld_release_virt_ramdump_mem(struct device *dev, void *address); +void pld_device_crashed(struct device *dev); +void pld_device_self_recovery(struct device *dev, + enum pld_recovery_reason reason); +void pld_intr_notify_q6(struct device *dev); +void pld_request_pm_qos(struct device *dev, u32 qos_val); +void pld_remove_pm_qos(struct device *dev); +int pld_request_bus_bandwidth(struct device *dev, int bandwidth); +int pld_get_platform_cap(struct device *dev, struct pld_platform_cap *cap); +int pld_get_sha_hash(struct device *dev, const u8 *data, + u32 data_len, u8 *hash_idx, u8 *out); +void *pld_get_fw_ptr(struct device *dev); +int pld_auto_suspend(struct device *dev); +int pld_auto_resume(struct device *dev); +int pld_force_wake_request(struct device *dev); + +/** + * pld_force_wake_request_sync() - Request to awake MHI synchronously + * @dev: device + * @timeout_us: timeout in micro-sec request to wake + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_force_wake_request_sync(struct device *dev, int timeout_us); +int pld_is_device_awake(struct device *dev); +int pld_force_wake_release(struct device *dev); +int pld_ce_request_irq(struct device *dev, unsigned int ce_id, + irqreturn_t (*handler)(int, void *), + unsigned long flags, const char *name, void *ctx); +int pld_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx); +void pld_enable_irq(struct device *dev, unsigned int ce_id); +void pld_disable_irq(struct device *dev, unsigned int ce_id); +int pld_get_soc_info(struct device *dev, struct pld_soc_info *info); +int pld_get_ce_id(struct device *dev, int irq); +int pld_get_irq(struct device *dev, int ce_id); +void pld_lock_pm_sem(struct device *dev); +void pld_release_pm_sem(struct device *dev); +void pld_lock_reg_window(struct device *dev, unsigned long *flags); +void pld_unlock_reg_window(struct device *dev, unsigned long *flags); +int pld_power_on(struct device *dev); +int pld_power_off(struct device *dev); +int pld_athdiag_read(struct device *dev, uint32_t offset, uint32_t memtype, + uint32_t datalen, uint8_t *output); +int pld_athdiag_write(struct device *dev, uint32_t offset, uint32_t memtype, + uint32_t datalen, uint8_t *input); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +void *pld_smmu_get_domain(struct device *dev); +#else +void *pld_smmu_get_mapping(struct device *dev); +#endif +int pld_smmu_map(struct device *dev, phys_addr_t paddr, + uint32_t *iova_addr, size_t size); +#ifdef CONFIG_SMMU_S1_UNMAP +int pld_smmu_unmap(struct device *dev, + uint32_t iova_addr, size_t size); +#else +static inline int pld_smmu_unmap(struct device *dev, + uint32_t iova_addr, size_t size) +{ + return 0; +} +#endif +int pld_get_user_msi_assignment(struct device *dev, char *user_name, + int *num_vectors, uint32_t *user_base_data, + uint32_t *base_vector); +int pld_get_msi_irq(struct device *dev, unsigned int vector); +void pld_get_msi_address(struct device *dev, uint32_t *msi_addr_low, + uint32_t *msi_addr_high); +int pld_is_drv_connected(struct device *dev); +unsigned int pld_socinfo_get_serial_number(struct device *dev); +int pld_is_qmi_disable(struct device *dev); +int pld_is_fw_down(struct device *dev); +int pld_force_assert_target(struct device *dev); +int pld_collect_rddm(struct device *dev); +int pld_qmi_send_get(struct device *dev); +int pld_qmi_send_put(struct device *dev); +int pld_qmi_send(struct device *dev, int type, void *cmd, + int cmd_len, void *cb_ctx, + int (*cb)(void *ctx, void *event, int event_len)); +bool pld_is_fw_dump_skipped(struct device *dev); + +/** + * pld_is_pdr() - Check WLAN PD is Restarted + * + * Help the driver decide whether FW down is due to + * WLAN PD Restart. + * + * Return: 1 WLAN PD is Restarted + * 0 WLAN PD is not Restarted + */ +int pld_is_pdr(struct device *dev); + +/** + * pld_is_fw_rejuvenate() - Check WLAN fw is rejuvenating + * + * Help the driver decide whether FW down is due to + * SSR or FW rejuvenate. + * + * Return: 1 FW is rejuvenating + * 0 FW is not rejuvenating + */ +int pld_is_fw_rejuvenate(struct device *dev); + +/** + * pld_have_platform_driver_support() - check if platform driver support + * @dev: device + * + * Return: true if platform driver support. + */ +bool pld_have_platform_driver_support(struct device *dev); + +/** + * pld_idle_shutdown - request idle shutdown callback from platform driver + * @dev: pointer to struct dev + * @shutdown_cb: pointer to hdd psoc idle shutdown callback handler + * + * Return: 0 for success and non-zero negative error code for failure + */ +int pld_idle_shutdown(struct device *dev, + int (*shutdown_cb)(struct device *dev)); + +/** + * pld_idle_restart - request idle restart callback from platform driver + * @dev: pointer to struct dev + * @restart_cb: pointer to hdd psoc idle restart callback handler + * + * Return: 0 for success and non-zero negative error code for failure + */ +int pld_idle_restart(struct device *dev, + int (*restart_cb)(struct device *dev)); +/** + * pld_srng_request_irq() - Register IRQ for SRNG + * @dev: device + * @irq: IRQ number + * @handler: IRQ callback function + * @irqflags: IRQ flags + * @name: IRQ name + * @ctx: IRQ context + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_srng_request_irq(struct device *dev, int irq, irq_handler_t handler, + unsigned long irqflags, + const char *name, + void *ctx); +/** + * pld_srng_free_irq() - Free IRQ for SRNG + * @dev: device + * @irq: IRQ number + * @ctx: IRQ context + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_srng_free_irq(struct device *dev, int irq, void *ctx); + +/** + * pld_srng_enable_irq() - Enable IRQ for SRNG + * @dev: device + * @irq: IRQ number + * + * Return: void + */ +void pld_srng_enable_irq(struct device *dev, int irq); + +/** + * pld_srng_disable_irq() - Disable IRQ for SRNG + * @dev: device + * @irq: IRQ number + * + * Return: void + */ +void pld_srng_disable_irq(struct device *dev, int irq); + +/** + * pld_pci_read_config_word() - Read PCI config + * @pdev: pci device + * @offset: Config space offset + * @val : Value + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pci_read_config_word(struct pci_dev *pdev, int offset, uint16_t *val); + +/** + * pld_pci_write_config_word() - Write PCI config + * @pdev: pci device + * @offset: Config space offset + * @val : Value + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pci_write_config_word(struct pci_dev *pdev, int offset, uint16_t val); + +/** + * pld_pci_read_config_dword() - Read PCI config + * @pdev: pci device + * @offset: Config space offset + * @val : Value + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pci_read_config_dword(struct pci_dev *pdev, int offset, uint32_t *val); + +/** + * pld_pci_write_config_dword() - Write PCI config + * @pdev: pci device + * @offset: Config space offset + * @val : Value + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pci_write_config_dword(struct pci_dev *pdev, int offset, uint32_t val); +#if defined(CONFIG_WCNSS_MEM_PRE_ALLOC) && defined(FEATURE_SKB_PRE_ALLOC) + +/** + * pld_nbuf_pre_alloc() - get allocated nbuf from platform driver. + * @size: Netbuf requested size + * + * Return: nbuf or NULL if no memory + */ +static inline struct sk_buff *pld_nbuf_pre_alloc(size_t size) +{ + struct sk_buff *skb = NULL; + + if (size >= WCNSS_PRE_SKB_ALLOC_GET_THRESHOLD) + skb = wcnss_skb_prealloc_get(size); + + return skb; +} + +/** + * pld_nbuf_pre_alloc_free() - free the nbuf allocated in platform driver. + * @skb: Pointer to network buffer + * + * Return: TRUE if the nbuf is freed + */ +static inline int pld_nbuf_pre_alloc_free(struct sk_buff *skb) +{ + return wcnss_skb_prealloc_put(skb); +} +#else +static inline struct sk_buff *pld_nbuf_pre_alloc(size_t size) +{ + return NULL; +} +static inline int pld_nbuf_pre_alloc_free(struct sk_buff *skb) +{ + return 0; +} +#endif +/** + * pld_get_bus_type() - Bus type of the device + * @dev: device + * + * Return: PLD bus type + */ +enum pld_bus_type pld_get_bus_type(struct device *dev); + +static inline int pfrm_request_irq(struct device *dev, unsigned int ce_id, + irqreturn_t (*handler)(int, void *), + unsigned long flags, const char *name, + void *ctx) +{ + return pld_srng_request_irq(dev, ce_id, handler, flags, name, ctx); +} + +static inline int pfrm_free_irq(struct device *dev, int irq, void *ctx) +{ + return pld_srng_free_irq(dev, irq, ctx); +} + +static inline void pfrm_enable_irq(struct device *dev, int irq) +{ + pld_srng_enable_irq(dev, irq); +} + +static inline void pfrm_disable_irq_nosync(struct device *dev, int irq) +{ + pld_srng_disable_irq(dev, irq); +} + +static inline int pfrm_read_config_word(struct pci_dev *pdev, int offset, + uint16_t *val) +{ + return pld_pci_read_config_word(pdev, offset, val); +} + +static inline int pfrm_write_config_word(struct pci_dev *pdev, int offset, + uint16_t val) +{ + return pld_pci_write_config_word(pdev, offset, val); +} + +static inline int pfrm_read_config_dword(struct pci_dev *pdev, int offset, + uint32_t *val) +{ + return pld_pci_read_config_dword(pdev, offset, val); +} + +static inline int pfrm_write_config_dword(struct pci_dev *pdev, int offset, + uint32_t val) +{ + return pld_pci_write_config_dword(pdev, offset, val); +} + +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_common.c b/drivers/staging/qcacld-3.0/core/pld/src/pld_common.c new file mode 100644 index 0000000000000000000000000000000000000000..fa37778f15348946cdb5104dc41b81c4b485e0a5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_common.c @@ -0,0 +1,2824 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#define pr_fmt(fmt) "wlan_pld:%s:%d:: " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PLD_SDIO_CNSS +#include +#endif +#ifdef CONFIG_PLD_PCIE_CNSS +#include +#endif +#ifdef CONFIG_PLD_SNOC_ICNSS +#include +#endif +#ifdef CONFIG_PLD_IPCI_ICNSS +#include +#endif + +#include "pld_pcie.h" +#include "pld_ipci.h" +#include "pld_pcie_fw_sim.h" +#include "pld_snoc_fw_sim.h" +#include "pld_snoc.h" +#include "pld_sdio.h" +#include "pld_usb.h" +#include "qwlan_version.h" + +#define PLD_PCIE_REGISTERED BIT(0) +#define PLD_SNOC_REGISTERED BIT(1) +#define PLD_SDIO_REGISTERED BIT(2) +#define PLD_USB_REGISTERED BIT(3) +#define PLD_SNOC_FW_SIM_REGISTERED BIT(4) +#define PLD_PCIE_FW_SIM_REGISTERED BIT(5) +#define PLD_IPCI_REGISTERED BIT(6) + +#define PLD_BUS_MASK 0xf + +static struct pld_context *pld_ctx; + +/** + * pld_init() - Initialize PLD module + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_init(void) +{ + struct pld_context *pld_context; + + pld_context = kzalloc(sizeof(*pld_context), GFP_KERNEL); + if (!pld_context) + return -ENOMEM; + + spin_lock_init(&pld_context->pld_lock); + + INIT_LIST_HEAD(&pld_context->dev_list); + + pld_ctx = pld_context; + + return 0; +} + +/** + * pld_deinit() - Uninitialize PLD module + * + * Return: void + */ +void pld_deinit(void) +{ + struct dev_node *dev_node; + struct pld_context *pld_context; + unsigned long flags; + + pld_context = pld_ctx; + if (!pld_context) { + pld_ctx = NULL; + return; + } + + spin_lock_irqsave(&pld_context->pld_lock, flags); + while (!list_empty(&pld_context->dev_list)) { + dev_node = list_first_entry(&pld_context->dev_list, + struct dev_node, list); + list_del(&dev_node->list); + kfree(dev_node); + } + spin_unlock_irqrestore(&pld_context->pld_lock, flags); + + kfree(pld_context); + + pld_ctx = NULL; +} + +/** + * pld_get_global_context() - Get global context of PLD + * + * Return: PLD global context + */ +struct pld_context *pld_get_global_context(void) +{ + return pld_ctx; +} + +/** + * pld_add_dev() - Add dev node to global context + * @pld_context: PLD global context + * @dev: device + * @ifdev: interface device + * @type: Bus type + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_add_dev(struct pld_context *pld_context, + struct device *dev, struct device *ifdev, + enum pld_bus_type type) +{ + unsigned long flags; + struct dev_node *dev_node; + + dev_node = kzalloc(sizeof(*dev_node), GFP_KERNEL); + if (!dev_node) + return -ENOMEM; + + dev_node->dev = dev; + dev_node->ifdev = ifdev; + dev_node->bus_type = type; + + spin_lock_irqsave(&pld_context->pld_lock, flags); + list_add_tail(&dev_node->list, &pld_context->dev_list); + spin_unlock_irqrestore(&pld_context->pld_lock, flags); + + return 0; +} + +/** + * pld_del_dev() - Delete dev node from global context + * @pld_context: PLD global context + * @dev: device + * + * Return: void + */ +void pld_del_dev(struct pld_context *pld_context, + struct device *dev) +{ + unsigned long flags; + struct dev_node *dev_node, *tmp; + + spin_lock_irqsave(&pld_context->pld_lock, flags); + list_for_each_entry_safe(dev_node, tmp, &pld_context->dev_list, list) { + if (dev_node->dev == dev) { + list_del(&dev_node->list); + kfree(dev_node); + } + } + spin_unlock_irqrestore(&pld_context->pld_lock, flags); +} + +static struct dev_node *pld_get_dev_node(struct device *dev) +{ + struct pld_context *pld_context; + struct dev_node *dev_node; + unsigned long flags; + + pld_context = pld_get_global_context(); + + if (!dev || !pld_context) { + pr_err("Invalid info: dev %pK, context %pK\n", + dev, pld_context); + return NULL; + } + + spin_lock_irqsave(&pld_context->pld_lock, flags); + list_for_each_entry(dev_node, &pld_context->dev_list, list) { + if (dev_node->dev == dev) { + spin_unlock_irqrestore(&pld_context->pld_lock, flags); + return dev_node; + } + } + spin_unlock_irqrestore(&pld_context->pld_lock, flags); + + return NULL; +} + +/** + * pld_get_bus_type() - Bus type of the device + * @dev: device + * + * Return: PLD bus type + */ +enum pld_bus_type pld_get_bus_type(struct device *dev) +{ + struct dev_node *dev_node = pld_get_dev_node(dev); + + if (dev_node) + return dev_node->bus_type; + else + return PLD_BUS_TYPE_NONE; +} + +/** + * pld_get_if_dev() - Bus interface/pipe dev of the device + * @dev: device + * + * Return: Bus sub-interface or pipe dev. + */ +static struct device *pld_get_if_dev(struct device *dev) +{ + struct dev_node *dev_node = pld_get_dev_node(dev); + + if (dev_node) + return dev_node->ifdev; + else + return NULL; +} + +/** + * pld_register_driver() - Register driver to kernel + * @ops: Callback functions that will be registered to kernel + * + * This function should be called when other modules want to + * register platform driver callback functions to kernel. The + * probe() is expected to be called after registration if the + * device is online. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_register_driver(struct pld_driver_ops *ops) +{ + int ret = 0; + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + + if (!pld_context) { + pr_err("global context is NULL\n"); + ret = -ENODEV; + goto out; + } + + if (pld_context->ops) { + pr_err("driver already registered\n"); + ret = -EEXIST; + goto out; + } + + if (!ops || !ops->probe || !ops->remove || + !ops->suspend || !ops->resume) { + pr_err("Required callback functions are missing\n"); + ret = -EINVAL; + goto out; + } + + pld_context->ops = ops; + pld_context->pld_driver_state = 0; + + ret = pld_pcie_register_driver(); + if (ret) { + pr_err("Fail to register pcie driver\n"); + goto fail_pcie; + } + pld_context->pld_driver_state |= PLD_PCIE_REGISTERED; + + ret = pld_snoc_register_driver(); + if (ret) { + pr_err("Fail to register snoc driver\n"); + goto fail_snoc; + } + pld_context->pld_driver_state |= PLD_SNOC_REGISTERED; + + ret = pld_sdio_register_driver(); + if (ret) { + pr_err("Fail to register sdio driver\n"); + goto fail_sdio; + } + pld_context->pld_driver_state |= PLD_SDIO_REGISTERED; + + ret = pld_snoc_fw_sim_register_driver(); + if (ret) { + pr_err("Fail to register snoc fw sim driver\n"); + goto fail_snoc_fw_sim; + } + pld_context->pld_driver_state |= PLD_SNOC_FW_SIM_REGISTERED; + + ret = pld_pcie_fw_sim_register_driver(); + if (ret) { + pr_err("Fail to register pcie fw sim driver\n"); + goto fail_pcie_fw_sim; + } + pld_context->pld_driver_state |= PLD_PCIE_FW_SIM_REGISTERED; + + ret = pld_usb_register_driver(); + if (ret) { + pr_err("Fail to register usb driver\n"); + goto fail_usb; + } + pld_context->pld_driver_state |= PLD_USB_REGISTERED; + + ret = pld_ipci_register_driver(); + if (ret) { + pr_err("Fail to register ipci driver\n"); + goto fail_ipci; + } + pld_context->pld_driver_state |= PLD_IPCI_REGISTERED; + + return ret; + +fail_ipci: + pld_usb_unregister_driver(); +fail_usb: + pld_pcie_fw_sim_unregister_driver(); +fail_pcie_fw_sim: + pld_snoc_fw_sim_unregister_driver(); +fail_snoc_fw_sim: + pld_sdio_unregister_driver(); +fail_sdio: + pld_snoc_unregister_driver(); +fail_snoc: + pld_pcie_unregister_driver(); +fail_pcie: + pld_context->pld_driver_state = 0; + pld_context->ops = NULL; +out: + return ret; +} + +/** + * pld_unregister_driver() - Unregister driver to kernel + * + * This function should be called when other modules want to + * unregister callback functions from kernel. The remove() is + * expected to be called after registration. + * + * Return: void + */ +void pld_unregister_driver(void) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + + if (!pld_context) { + pr_err("global context is NULL\n"); + return; + } + + if (!pld_context->ops) { + pr_err("driver not registered\n"); + return; + } + + pld_pcie_unregister_driver(); + pld_snoc_fw_sim_unregister_driver(); + pld_pcie_fw_sim_unregister_driver(); + pld_snoc_unregister_driver(); + pld_sdio_unregister_driver(); + pld_usb_unregister_driver(); + pld_ipci_unregister_driver(); + + pld_context->pld_driver_state = 0; + + pld_context->ops = NULL; +} + +/** + * pld_wlan_enable() - Enable WLAN + * @dev: device + * @config: WLAN configuration data + * @mode: WLAN mode + * + * This function enables WLAN FW. It passed WLAN configuration data, + * WLAN mode and host software version to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode) +{ + int ret = 0; + struct device *ifdev; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_wlan_enable(dev, config, mode, QWLAN_VERSIONSTR); + break; + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_wlan_enable(dev, config, mode, QWLAN_VERSIONSTR); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + ret = pld_snoc_fw_sim_wlan_enable(dev, config, mode, + QWLAN_VERSIONSTR); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = pld_pcie_fw_sim_wlan_enable(dev, config, mode, + QWLAN_VERSIONSTR); + break; + case PLD_BUS_TYPE_SDIO: + ret = pld_sdio_wlan_enable(dev, config, mode, QWLAN_VERSIONSTR); + break; + case PLD_BUS_TYPE_USB: + ifdev = pld_get_if_dev(dev); + ret = pld_usb_wlan_enable(ifdev, config, mode, + QWLAN_VERSIONSTR); + break; + case PLD_BUS_TYPE_IPCI: + ret = pld_ipci_wlan_enable(dev, config, mode, QWLAN_VERSIONSTR); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_wlan_disable() - Disable WLAN + * @dev: device + * @mode: WLAN mode + * + * This function disables WLAN FW. It passes WLAN mode to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_wlan_disable(struct device *dev, enum pld_driver_mode mode) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_wlan_disable(dev, mode); + break; + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_wlan_disable(dev, mode); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + ret = pld_snoc_fw_sim_wlan_disable(dev, mode); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = pld_pcie_fw_sim_wlan_disable(dev, mode); + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_IPCI: + ret = pld_ipci_wlan_disable(dev, mode); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_set_fw_log_mode() - Set FW debug log mode + * @dev: device + * @fw_log_mode: 0 for No log, 1 for WMI, 2 for DIAG + * + * Switch Fw debug log mode between DIAG logging and WMI logging. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_set_fw_log_mode(struct device *dev, u8 fw_log_mode) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_set_fw_log_mode(dev, fw_log_mode); + break; + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_set_fw_log_mode(dev, fw_log_mode); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_IPCI: + ret = pld_ipci_set_fw_log_mode(dev, fw_log_mode); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_get_default_fw_files() - Get default FW file names + * @pfw_files: buffer for FW file names + * + * Return default FW file names to the buffer. + * + * Return: void + */ +void pld_get_default_fw_files(struct pld_fw_files *pfw_files) +{ + memset(pfw_files, 0, sizeof(*pfw_files)); + + strlcpy(pfw_files->image_file, PREFIX PLD_IMAGE_FILE, + PLD_MAX_FILE_NAME); + strlcpy(pfw_files->board_data, PREFIX PLD_BOARD_DATA_FILE, + PLD_MAX_FILE_NAME); + strlcpy(pfw_files->otp_data, PREFIX PLD_OTP_FILE, + PLD_MAX_FILE_NAME); + strlcpy(pfw_files->utf_file, PREFIX PLD_UTF_FIRMWARE_FILE, + PLD_MAX_FILE_NAME); + strlcpy(pfw_files->utf_board_data, PREFIX PLD_BOARD_DATA_FILE, + PLD_MAX_FILE_NAME); + strlcpy(pfw_files->epping_file, PREFIX PLD_EPPING_FILE, + PLD_MAX_FILE_NAME); + strlcpy(pfw_files->setup_file, PREFIX PLD_SETUP_FILE, + PLD_MAX_FILE_NAME); +} + +/** + * pld_get_fw_files_for_target() - Get FW file names + * @dev: device + * @pfw_files: buffer for FW file names + * @target_type: target type + * @target_version: target version + * + * Return target specific FW file names to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_get_fw_files_for_target(struct device *dev, + struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_get_fw_files_for_target(dev, pfw_files, + target_type, + target_version); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + ret = pld_sdio_get_fw_files_for_target(pfw_files, + target_type, + target_version); + break; + case PLD_BUS_TYPE_USB: + ret = pld_usb_get_fw_files_for_target(pfw_files, + target_type, + target_version); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_prevent_l1() - Prevent PCIe enter L1 state + * @dev: device + * + * Prevent PCIe enter L1 and L1ss states + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_prevent_l1(struct device *dev) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_prevent_l1(dev); + break; + default: + ret = -EINVAL; + pr_err("Invalid device type\n"); + break; + } + + return ret; +} + +/** + * pld_allow_l1() - Allow PCIe enter L1 state + * @dev: device + * + * Allow PCIe enter L1 and L1ss states + * + * Return: void + */ +void pld_allow_l1(struct device *dev) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_allow_l1(dev); + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_is_pci_link_down() - Notification for pci link down event + * @dev: device + * + * Notify platform that pci link is down. + * + * Return: void + */ +void pld_is_pci_link_down(struct device *dev) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE: + pld_pcie_link_down(dev); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_get_bus_reg_dump() - Get bus reg dump + * @dev: device + * @buffer: buffer for hang data + * @len: len of hang data + * + * Get pci reg dump for hang data. + * + * Return: void + */ +void pld_get_bus_reg_dump(struct device *dev, uint8_t *buf, uint32_t len) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE: + pld_pcie_get_reg_dump(dev, buf, len); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_schedule_recovery_work() - Schedule recovery work + * @dev: device + * @reason: recovery reason + * + * Schedule a system self recovery work. + * + * Return: void + */ +void pld_schedule_recovery_work(struct device *dev, + enum pld_recovery_reason reason) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_schedule_recovery_work(dev, reason); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_wlan_pm_control() - WLAN PM control on PCIE + * @dev: device + * @vote: 0 for enable PCIE PC, 1 for disable PCIE PC + * + * This is for PCIE power collaps control during suspend/resume. + * When PCIE power collaps is disabled, WLAN FW can access memory + * through PCIE when system is suspended. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_wlan_pm_control(struct device *dev, bool vote) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_wlan_pm_control(dev, vote); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_get_virt_ramdump_mem() - Get virtual ramdump memory + * @dev: device + * @size: buffer to virtual memory size + * + * Return: virtual ramdump memory address + */ +void *pld_get_virt_ramdump_mem(struct device *dev, unsigned long *size) +{ + void *mem = NULL; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + mem = pld_pcie_get_virt_ramdump_mem(dev, size); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + mem = pld_sdio_get_virt_ramdump_mem(dev, size); + break; + default: + pr_err("Invalid device type\n"); + break; + } + + return mem; +} + +void pld_release_virt_ramdump_mem(struct device *dev, void *address) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_release_virt_ramdump_mem(address); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + pld_sdio_release_virt_ramdump_mem(address); + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_device_crashed() - Notification for device crash event + * @dev: device + * + * Notify subsystem a device crashed event. A subsystem restart + * is expected to happen after calling this function. + * + * Return: void + */ +void pld_device_crashed(struct device *dev) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_device_crashed(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + pld_sdio_device_crashed(dev); + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_device_self_recovery() - Device self recovery + * @dev: device + * @reason: recovery reason + * + * Return: void + */ +void pld_device_self_recovery(struct device *dev, + enum pld_recovery_reason reason) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_device_self_recovery(dev, reason); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + pld_sdio_device_self_recovery(dev); + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_intr_notify_q6() - Notify Q6 FW interrupts + * @dev: device + * + * Notify Q6 that a FW interrupt is triggered. + * + * Return: void + */ +void pld_intr_notify_q6(struct device *dev) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_intr_notify_q6(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_request_pm_qos() - Request system PM + * @dev: device + * @qos_val: request value + * + * It votes for the value of aggregate QoS expectations. + * + * Return: void + */ +void pld_request_pm_qos(struct device *dev, u32 qos_val) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_request_pm_qos(dev, qos_val); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + /* To do Add call cns API */ + break; + case PLD_BUS_TYPE_USB: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_remove_pm_qos() - Remove system PM + * @dev: device + * + * Remove the vote request for Qos expectations. + * + * Return: void + */ +void pld_remove_pm_qos(struct device *dev) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_remove_pm_qos(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + /* To do Add call cns API */ + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_request_bus_bandwidth() - Request bus bandwidth + * @dev: device + * @bandwidth: bus bandwidth + * + * Votes for HIGH/MEDIUM/LOW bus bandwidth. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_request_bus_bandwidth(struct device *dev, int bandwidth) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_request_bus_bandwidth(dev, bandwidth); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + /* To do Add call cns API */ + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_get_platform_cap() - Get platform capabilities + * @dev: device + * @cap: buffer to the capabilities + * + * Return capabilities to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_get_platform_cap(struct device *dev, struct pld_platform_cap *cap) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_get_platform_cap(dev, cap); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = pld_pcie_fw_sim_get_platform_cap(dev, cap); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_get_sha_hash() - Get sha hash number + * @dev: device + * @data: input data + * @data_len: data length + * @hash_idx: hash index + * @out: output buffer + * + * Return computed hash to the out buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_get_sha_hash(struct device *dev, const u8 *data, + u32 data_len, u8 *hash_idx, u8 *out) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_get_sha_hash(dev, data, data_len, + hash_idx, out); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_get_fw_ptr() - Get secure FW memory address + * @dev: device + * + * Return: secure memory address + */ +void *pld_get_fw_ptr(struct device *dev) +{ + void *ptr = NULL; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ptr = pld_pcie_get_fw_ptr(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + default: + pr_err("Invalid device type\n"); + break; + } + + return ptr; +} + +/** + * pld_auto_suspend() - Auto suspend + * @dev: device + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_auto_suspend(struct device *dev) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_auto_suspend(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_auto_resume() - Auto resume + * @dev: device + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_auto_resume(struct device *dev) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_auto_resume(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_force_wake_request() - Request vote to assert WAKE register + * @dev: device + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_force_wake_request(struct device *dev) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_force_wake_request(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +int pld_force_wake_request_sync(struct device *dev, int timeout_us) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_force_wake_request_sync(dev, timeout_us); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_is_device_awake() - Check if it's ready to access MMIO registers + * @dev: device + * + * Return: True for device awake + * False for device not awake + * Negative failure code for errors + */ +int pld_is_device_awake(struct device *dev) +{ + int ret = true; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_is_device_awake(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_force_wake_release() - Release vote to assert WAKE register + * @dev: device + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_force_wake_release(struct device *dev) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_force_wake_release(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_ce_request_irq() - Register IRQ for CE + * @dev: device + * @ce_id: CE number + * @handler: IRQ callback function + * @flags: IRQ flags + * @name: IRQ name + * @ctx: IRQ context + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_ce_request_irq(struct device *dev, unsigned int ce_id, + irqreturn_t (*handler)(int, void *), + unsigned long flags, const char *name, void *ctx) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_ce_request_irq(dev, ce_id, + handler, flags, name, ctx); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + ret = pld_snoc_fw_sim_ce_request_irq(dev, ce_id, + handler, flags, name, ctx); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_PCIE: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_ce_free_irq() - Free IRQ for CE + * @dev: device + * @ce_id: CE number + * @ctx: IRQ context + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_ce_free_irq(dev, ce_id, ctx); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + ret = pld_snoc_fw_sim_ce_free_irq(dev, ce_id, ctx); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_PCIE: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_enable_irq() - Enable IRQ for CE + * @dev: device + * @ce_id: CE number + * + * Return: void + */ +void pld_enable_irq(struct device *dev, unsigned int ce_id) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + pld_snoc_enable_irq(dev, ce_id); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + pld_snoc_fw_sim_enable_irq(dev, ce_id); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_PCIE: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_disable_irq() - Disable IRQ for CE + * @dev: device + * @ce_id: CE number + * + * Return: void + */ +void pld_disable_irq(struct device *dev, unsigned int ce_id) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + pld_snoc_disable_irq(dev, ce_id); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + pld_snoc_fw_sim_disable_irq(dev, ce_id); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_PCIE: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_get_soc_info() - Get SOC information + * @dev: device + * @info: buffer to SOC information + * + * Return SOC info to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_get_soc_info(struct device *dev, struct pld_soc_info *info) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_get_soc_info(dev, info); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + ret = pld_snoc_fw_sim_get_soc_info(dev, info); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = pld_pcie_fw_sim_get_soc_info(dev, info); + break; + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_get_soc_info(dev, info); + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_IPCI: + ret = pld_ipci_get_soc_info(dev, info); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_get_ce_id() - Get CE number for the provided IRQ + * @dev: device + * @irq: IRQ number + * + * Return: CE number + */ +int pld_get_ce_id(struct device *dev, int irq) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_get_ce_id(dev, irq); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + ret = pld_snoc_fw_sim_get_ce_id(dev, irq); + break; + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_get_ce_id(dev, irq); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_get_irq() - Get IRQ number for given CE ID + * @dev: device + * @ce_id: CE ID + * + * Return: IRQ number + */ +int pld_get_irq(struct device *dev, int ce_id) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_get_irq(dev, ce_id); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + ret = pld_snoc_fw_sim_get_irq(dev, ce_id); + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_PCIE: + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_lock_pm_sem() - Lock PM semaphore + * @dev: device + * + * Return: void + */ +void pld_lock_pm_sem(struct device *dev) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_lock_pm_sem(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_USB: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_release_pm_sem() - Release PM semaphore + * @dev: device + * + * Return: void + */ +void pld_release_pm_sem(struct device *dev) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_release_pm_sem(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_USB: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_lock_reg_window() - Lock register window spinlock + * @dev: device pointer + * @flags: variable pointer to save CPU states + * + * It uses spinlock_bh so avoid calling in top half context. + * + * Return: void + */ +void pld_lock_reg_window(struct device *dev, unsigned long *flags) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_lock_reg_window(dev, flags); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_USB: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_unlock_reg_window() - Unlock register window spinlock + * @dev: device pointer + * @flags: variable pointer to save CPU states + * + * It uses spinlock_bh so avoid calling in top half context. + * + * Return: void + */ +void pld_unlock_reg_window(struct device *dev, unsigned long *flags) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_unlock_reg_window(dev, flags); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_USB: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_power_on() - Power on WLAN hardware + * @dev: device + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_power_on(struct device *dev) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + /* cnss platform driver handles PCIe SoC + * power on/off seqeunce so let CNSS driver + * handle the power on sequence for PCIe SoC + */ + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_power_on(dev); + break; + case PLD_BUS_TYPE_IPCI: + ret = pld_ipci_power_on(dev); + break; + default: + pr_err("Invalid device type\n"); + break; + } + + return ret; +} + +/** + * pld_power_off() - Power off WLAN hardware + * @dev: device + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_power_off(struct device *dev) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + /* cnss platform driver handles PCIe SoC + * power on/off seqeunce so let CNSS driver + * handle the power off sequence for PCIe SoC + */ + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_power_off(dev); + break; + case PLD_BUS_TYPE_IPCI: + ret = pld_ipci_power_off(dev); + break; + default: + pr_err("Invalid device type\n"); + break; + } + + return ret; +} + +/** + * pld_athdiag_read() - Read data from WLAN FW + * @dev: device + * @offset: address offset + * @memtype: memory type + * @datalen: data length + * @output: output buffer + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_athdiag_read(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *output) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_athdiag_read(dev, offset, memtype, + datalen, output); + break; + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_athdiag_read(dev, offset, memtype, + datalen, output); + break; + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_IPCI: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_athdiag_write() - Write data to WLAN FW + * @dev: device + * @offset: address offset + * @memtype: memory type + * @datalen: data length + * @input: input buffer + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_athdiag_write(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *input) +{ + int ret = 0; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_athdiag_write(dev, offset, memtype, + datalen, input); + break; + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_athdiag_write(dev, offset, memtype, + datalen, input); + break; + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_IPCI: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_smmu_get_domain() - Get SMMU domain + * @dev: device + * + * Return: Pointer to the domain + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +void *pld_smmu_get_domain(struct device *dev) +{ + void *ptr = NULL; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SNOC: + ptr = pld_snoc_smmu_get_domain(dev); + break; + case PLD_BUS_TYPE_PCIE: + ptr = pld_pcie_smmu_get_domain(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + pr_err("Not supported on type %d\n", type); + break; + default: + pr_err("Invalid device type %d\n", type); + break; + } + + return ptr; +} +#else +/** + * pld_smmu_get_mapping() - Get SMMU mapping context + * @dev: device + * + * Return: Pointer to the mapping context + */ +void *pld_smmu_get_mapping(struct device *dev) +{ + void *ptr = NULL; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SNOC: + ptr = pld_snoc_smmu_get_mapping(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_PCIE: + ptr = pld_pcie_smmu_get_mapping(dev); + break; + default: + pr_err("Invalid device type %d\n", type); + break; + } + + return ptr; +} +#endif + +/** + * pld_smmu_map() - Map SMMU + * @dev: device + * @paddr: physical address that needs to map to + * @iova_addr: IOVA address + * @size: size to be mapped + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_smmu_map(struct device *dev, phys_addr_t paddr, + uint32_t *iova_addr, size_t size) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_smmu_map(dev, paddr, iova_addr, size); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_smmu_map(dev, paddr, iova_addr, size); + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +#ifdef CONFIG_SMMU_S1_UNMAP +/** + * pld_smmu_unmap() - Unmap SMMU + * @dev: device + * @iova_addr: IOVA address to be unmapped + * @size: size to be unmapped + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_smmu_unmap(struct device *dev, + uint32_t iova_addr, size_t size) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_smmu_unmap(dev, iova_addr, size); + break; + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_smmu_unmap(dev, iova_addr, size); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_IPCI: + pr_err("Not supported on type %d\n", type); + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} +#endif + +/** + * pld_get_user_msi_assignment() - Get MSI assignment information + * @dev: device structure + * @user_name: name of the user who requests the MSI assignment + * @num_vectors: number of the MSI vectors assigned for the user + * @user_base_data: MSI base data assigned for the user, this equals to + * endpoint base data from config space plus base vector + * @base_vector: base MSI vector (offset) number assigned for the user + * + * Return: 0 for success + * Negative failure code for errors + */ +int pld_get_user_msi_assignment(struct device *dev, char *user_name, + int *num_vectors, uint32_t *user_base_data, + uint32_t *base_vector) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_get_user_msi_assignment(dev, user_name, + num_vectors, + user_base_data, + base_vector); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = pld_pcie_fw_sim_get_user_msi_assignment(dev, user_name, + num_vectors, + user_base_data, + base_vector); + break; + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_SNOC_FW_SIM: + pr_err("Not supported on type %d\n", type); + ret = -ENODEV; + break; + case PLD_BUS_TYPE_IPCI: + ret = pld_ipci_get_user_msi_assignment(dev, user_name, + num_vectors, + user_base_data, + base_vector); + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_srng_request_irq() - Register IRQ for SRNG + * @dev: device + * @irq: IRQ number + * @handler: IRQ callback function + * @flags: IRQ flags + * @name: IRQ name + * @ctx: IRQ context + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_srng_request_irq(struct device *dev, int irq, irq_handler_t handler, + unsigned long irqflags, + const char *devname, + void *dev_data) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + ret = request_irq(irq, handler, irqflags, devname, dev_data); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = pld_pcie_fw_sim_request_irq(dev, irq, handler, + irqflags, devname, + dev_data); + break; + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_SNOC_FW_SIM: + pr_err("Not supported on type %d\n", type); + ret = -ENODEV; + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_srng_free_irq() - Free IRQ for SRNG + * @dev: device + * @irq: IRQ number + * @handler: IRQ callback function + * @flags: IRQ flags + * @name: IRQ name + * @ctx: IRQ context + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_srng_free_irq(struct device *dev, int irq, void *dev_data) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + free_irq(irq, dev_data); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = pld_pcie_fw_sim_free_irq(dev, irq, dev_data); + break; + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_SNOC_FW_SIM: + pr_err("Not supported on type %d\n", type); + ret = -ENODEV; + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_srng_enable_irq() - Enable IRQ for SRNG + * @dev: device + * @irq: IRQ number + * + * Return: void + */ +void pld_srng_enable_irq(struct device *dev, int irq) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + pld_pcie_fw_sim_enable_irq(dev, irq); + break; + case PLD_BUS_TYPE_PCIE: + enable_irq(irq); + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_disable_irq() - Disable IRQ for SRNG + * @dev: device + * @irq: IRQ number + * + * Return: void + */ +void pld_srng_disable_irq(struct device *dev, int irq) +{ + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + pld_pcie_fw_sim_disable_irq(dev, irq); + break; + case PLD_BUS_TYPE_PCIE: + disable_irq_nosync(irq); + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type\n"); + break; + } +} + +/** + * pld_pci_read_config_word() - Read PCI config + * @pdev: pci device + * @offset: Config space offset + * @val : Value + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pci_read_config_word(struct pci_dev *pdev, int offset, uint16_t *val) +{ + int ret = 0; + + switch (pld_get_bus_type(&pdev->dev)) { + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = pld_pcie_fw_sim_read_config_word(&pdev->dev, offset, val); + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_PCIE: + ret = pci_read_config_word(pdev, offset, val); + break; + case PLD_BUS_TYPE_SDIO: + break; + default: + pr_err("Invalid device type\n"); + break; + } + + return ret; +} + +/** + * pld_pci_write_config_word() - Write PCI config + * @pdev: pci device + * @offset: Config space offset + * @val : Value + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pci_write_config_word(struct pci_dev *pdev, int offset, uint16_t val) +{ + int ret = 0; + + switch (pld_get_bus_type(&pdev->dev)) { + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE: + ret = pci_write_config_word(pdev, offset, val); + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type\n"); + break; + } + + return ret; +} + +/** + * pld_pci_read_config_dword() - Read PCI config + * @pdev: pci device + * @offset: Config space offset + * @val : Value + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pci_read_config_dword(struct pci_dev *pdev, int offset, uint32_t *val) +{ + int ret = 0; + + switch (pld_get_bus_type(&pdev->dev)) { + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE: + ret = pci_read_config_dword(pdev, offset, val); + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type\n"); + break; + } + + return ret; +} + +/** + * pld_pci_write_config_dword() - Write PCI config + * @pdev: pci device + * @offset: Config space offset + * @val : Value + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pci_write_config_dword(struct pci_dev *pdev, int offset, uint32_t val) +{ + int ret = 0; + + switch (pld_get_bus_type(&pdev->dev)) { + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE: + ret = pci_write_config_dword(pdev, offset, val); + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type\n"); + break; + } + + return ret; +} + +/** + * pld_get_msi_irq() - Get MSI IRQ number used for request_irq() + * @dev: device structure + * @vector: MSI vector (offset) number + * + * Return: Positive IRQ number for success + * Negative failure code for errors + */ +int pld_get_msi_irq(struct device *dev, unsigned int vector) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_get_msi_irq(dev, vector); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = pld_pcie_fw_sim_get_msi_irq(dev, vector); + break; + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_SNOC_FW_SIM: + pr_err("Not supported on type %d\n", type); + ret = -ENODEV; + break; + case PLD_BUS_TYPE_IPCI: + ret = pld_ipci_get_msi_irq(dev, vector); + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_get_msi_address() - Get the MSI address + * @dev: device structure + * @msi_addr_low: lower 32-bit of the address + * @msi_addr_high: higher 32-bit of the address + * + * Return: Void + */ +void pld_get_msi_address(struct device *dev, uint32_t *msi_addr_low, + uint32_t *msi_addr_high) +{ + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + pld_pcie_get_msi_address(dev, msi_addr_low, msi_addr_high); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + pld_pcie_fw_sim_get_msi_address(dev, msi_addr_low, + msi_addr_high); + break; + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_SNOC_FW_SIM: + pr_err("Not supported on type %d\n", type); + break; + case PLD_BUS_TYPE_IPCI: + pld_ipci_get_msi_address(dev, msi_addr_low, msi_addr_high); + break; + default: + pr_err("Invalid device type %d\n", type); + break; + } +} + +/** + * pld_is_drv_connected() - Check if DRV subsystem is connected + * @dev: device structure + * + * Return: 1 DRV is connected + * 0 DRV is not connected + * Non zero failure code for errors + */ +int pld_is_drv_connected(struct device *dev) +{ + enum pld_bus_type type = pld_get_bus_type(dev); + int ret = 0; + + switch (type) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_is_drv_connected(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_socinfo_get_serial_number() - Get SOC serial number + * @dev: device + * + * Return: SOC serial number + */ +unsigned int pld_socinfo_get_serial_number(struct device *dev) +{ + unsigned int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_socinfo_get_serial_number(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_PCIE: + pr_err("Not supported on type %d\n", type); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_IPCI: + break; + default: + pr_err("Invalid device type %d\n", type); + break; + } + + return ret; +} + +/** + * pld_is_qmi_disable() - Check QMI support is present or not + * @dev: device + * + * Return: 1 QMI is not supported + * 0 QMI is supported + * Non zero failure code for errors + */ +int pld_is_qmi_disable(struct device *dev) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_is_qmi_disable(dev); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_PCIE: + case PLD_BUS_TYPE_SDIO: + pr_err("Not supported on type %d\n", type); + ret = -EINVAL; + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_is_fw_down() - Check WLAN fw is down or not + * + * @dev: device + * + * This API will be called to check if WLAN FW is down or not. + * + * Return: 0 FW is not down + * Otherwise FW is down + * Always return 0 for unsupported bus type + */ +int pld_is_fw_down(struct device *dev) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + struct device *ifdev; + + switch (type) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_is_fw_down(dev); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + ret = pld_snoc_fw_sim_is_fw_down(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + break; + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_is_fw_down(dev); + break; + case PLD_BUS_TYPE_SDIO: + break; + case PLD_BUS_TYPE_USB: + ifdev = pld_get_if_dev(dev); + ret = pld_usb_is_fw_down(ifdev); + break; + case PLD_BUS_TYPE_IPCI: + ret = pld_ipci_is_fw_down(dev); + break; + default: + pr_err("Invalid device type %d\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * pld_force_assert_target() - Send a force assert to FW. + * This can use various sideband requests available at platform to + * initiate a FW assert. + * @dev: device + * + * Return: 0 if force assert of target was triggered successfully + * Non zero failure code for errors + */ +int pld_force_assert_target(struct device *dev) +{ + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SNOC: + return pld_snoc_force_assert_target(dev); + case PLD_BUS_TYPE_PCIE: + return pld_pcie_force_assert_target(dev); + case PLD_BUS_TYPE_PCIE_FW_SIM: + return -EOPNOTSUPP; + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SDIO: + return -EINVAL; + case PLD_BUS_TYPE_IPCI: + return pld_ipci_force_assert_target(dev); + default: + pr_err("Invalid device type %d\n", type); + return -EINVAL; + } +} + +/** + * pld_collect_rddm() - Collect ramdump before FW assert. + * This can used to collect ramdump before FW assert. + * @dev: device + * + * Return: 0 if ramdump is collected successfully + * Non zero failure code for errors + */ +int pld_collect_rddm(struct device *dev) +{ + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + return pld_pcie_collect_rddm(dev); + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + return 0; + default: + pr_err("Invalid device type %d\n", type); + return -EINVAL; + } +} + +/** + * pld_qmi_send_get() - Indicate certain data to be sent over QMI + * @dev: device pointer + * + * This API can be used to indicate certain data to be sent over QMI. + * pld_qmi_send() is expected to be called later. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_qmi_send_get(struct device *dev) +{ + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + return pld_pcie_qmi_send_get(dev); + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + return 0; + default: + pr_err("Invalid device type %d\n", type); + return -EINVAL; + } +} + +/** + * pld_qmi_send_put() - Indicate response sent over QMI has been processed + * @dev: device pointer + * + * This API can be used to indicate response of the data sent over QMI has + * been processed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_qmi_send_put(struct device *dev) +{ + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_PCIE: + return pld_pcie_qmi_send_put(dev); + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + return 0; + default: + pr_err("Invalid device type %d\n", type); + return -EINVAL; + } +} + +/** + * pld_qmi_send() - Send data request over QMI + * @dev: device pointer + * @type: type of the send data operation + * @cmd: buffer pointer of send data request command + * @cmd_len: size of the command buffer + * @cb_ctx: context pointer if any to pass back in callback + * @cb: callback pointer to pass response back + * + * This API can be used to send data request over QMI. + * + * Return: 0 if data request sends successfully + * Non zero failure code for errors + */ +int pld_qmi_send(struct device *dev, int type, void *cmd, + int cmd_len, void *cb_ctx, + int (*cb)(void *ctx, void *event, int event_len)) +{ + enum pld_bus_type bus_type = pld_get_bus_type(dev); + + switch (bus_type) { + case PLD_BUS_TYPE_PCIE: + return pld_pcie_qmi_send(dev, type, cmd, cmd_len, cb_ctx, cb); + case PLD_BUS_TYPE_SNOC: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + return -EINVAL; + default: + pr_err("Invalid device type %d\n", bus_type); + return -EINVAL; + } +} + +/** + * pld_is_fw_dump_skipped() - get fw dump skipped status. + * The subsys ssr status help the driver to decide whether to skip + * the FW memory dump when FW assert. + * For SDIO case, the memory dump progress takes 1 minutes to + * complete, which is not acceptable in SSR enabled. + * + * Return: true if need to skip FW dump. + */ +bool pld_is_fw_dump_skipped(struct device *dev) +{ + bool ret = false; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SDIO: + ret = pld_sdio_is_fw_dump_skipped(); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_IPCI: + default: + break; + } + return ret; +} + +int pld_is_pdr(struct device *dev) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_is_pdr(); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_IPCI: + default: + break; + } + return ret; +} + +int pld_is_fw_rejuvenate(struct device *dev) +{ + int ret = 0; + enum pld_bus_type type = pld_get_bus_type(dev); + + switch (type) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_is_fw_rejuvenate(); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_IPCI: + default: + break; + } + return ret; +} + +bool pld_have_platform_driver_support(struct device *dev) +{ + bool ret = false; + + switch (pld_get_bus_type(dev)) { + case PLD_BUS_TYPE_PCIE: + ret = pld_pcie_platform_driver_support(); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + ret = true; + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_SNOC: + break; + case PLD_BUS_TYPE_IPCI: + break; + case PLD_BUS_TYPE_SDIO: + ret = pld_sdio_platform_driver_support(); + break; + default: + pr_err("Invalid device type\n"); + break; + } + + return ret; +} + +int pld_idle_shutdown(struct device *dev, + int (*shutdown_cb)(struct device *dev)) +{ + int errno = -EINVAL; + enum pld_bus_type type; + + if (!shutdown_cb) + return -EINVAL; + + type = pld_get_bus_type(dev); + switch (type) { + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + errno = shutdown_cb(dev); + break; + case PLD_BUS_TYPE_SNOC: + errno = pld_snoc_idle_shutdown(dev); + break; + case PLD_BUS_TYPE_PCIE: + errno = pld_pcie_idle_shutdown(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + errno = pld_pcie_fw_sim_idle_shutdown(dev); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + errno = pld_snoc_fw_sim_idle_shutdown(dev); + break; + case PLD_BUS_TYPE_IPCI: + errno = pld_ipci_idle_shutdown(dev); + break; + default: + pr_err("Invalid device type %d\n", type); + break; + } + + return errno; +} + +int pld_idle_restart(struct device *dev, + int (*restart_cb)(struct device *dev)) +{ + int errno = -EINVAL; + enum pld_bus_type type; + + if (!restart_cb) + return -EINVAL; + + type = pld_get_bus_type(dev); + switch (type) { + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + errno = restart_cb(dev); + break; + case PLD_BUS_TYPE_SNOC: + errno = pld_snoc_idle_restart(dev); + break; + case PLD_BUS_TYPE_PCIE: + errno = pld_pcie_idle_restart(dev); + break; + case PLD_BUS_TYPE_PCIE_FW_SIM: + errno = pld_pcie_fw_sim_idle_restart(dev); + break; + case PLD_BUS_TYPE_SNOC_FW_SIM: + errno = pld_snoc_fw_sim_idle_restart(dev); + break; + case PLD_BUS_TYPE_IPCI: + errno = pld_ipci_idle_restart(dev); + break; + default: + pr_err("Invalid device type %d\n", type); + break; + } + + return errno; +} + +#ifdef FEATURE_WLAN_TIME_SYNC_FTM +/** + * pld_get_audio_wlan_timestamp() - Get audio timestamp + * @dev: device pointer + * @type: trigger type + * @ts: audio timestamp + * + * This API can be used to get audio timestamp. + * + * Return: 0 if trigger to get audio timestamp is successful + * Non zero failure code for errors + */ +int pld_get_audio_wlan_timestamp(struct device *dev, + enum pld_wlan_time_sync_trigger_type type, + uint64_t *ts) +{ + int ret = 0; + enum pld_bus_type bus_type; + + bus_type = pld_get_bus_type(dev); + switch (bus_type) { + case PLD_BUS_TYPE_SNOC: + ret = pld_snoc_get_audio_wlan_timestamp(dev, type, ts); + break; + case PLD_BUS_TYPE_PCIE: + case PLD_BUS_TYPE_SNOC_FW_SIM: + case PLD_BUS_TYPE_PCIE_FW_SIM: + case PLD_BUS_TYPE_SDIO: + case PLD_BUS_TYPE_USB: + case PLD_BUS_TYPE_IPCI: + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +#endif /* FEATURE_WLAN_TIME_SYNC_FTM */ diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_internal.h b/drivers/staging/qcacld-3.0/core/pld/src/pld_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..78b394e74e141bd98cf3b47b85154a51b873aaee --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_internal.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __PLD_COMMON_I_H__ +#define __PLD_COMMON_I_H__ + +#include "pld_common.h" + +struct dev_node { + struct device *dev; + struct device *ifdev; + struct list_head list; + enum pld_bus_type bus_type; +}; + +struct pld_context { + struct pld_driver_ops *ops; + spinlock_t pld_lock; + struct list_head dev_list; + uint32_t pld_driver_state; +}; + +struct pld_context *pld_get_global_context(void); +int pld_add_dev(struct pld_context *pld_context, + struct device *dev, struct device *ifdev, + enum pld_bus_type type); +void pld_del_dev(struct pld_context *pld_context, + struct device *dev); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_ipci.c b/drivers/staging/qcacld-3.0/core/pld/src/pld_ipci.c new file mode 100644 index 0000000000000000000000000000000000000000..a55a7f186f99fbb50b1991a62a6ec9d2d02a699a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_ipci.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#ifdef CONFIG_PLD_IPCI_ICNSS +#include +#endif + +#include "pld_internal.h" +#include "pld_ipci.h" + +#ifdef CONFIG_PLD_IPCI_ICNSS +/** + * pld_ipci_probe() - Probe function for platform driver + * @dev: device + * + * The probe function will be called when platform device + * is detected. + * + * Return: int + */ +static int pld_ipci_probe(struct device *dev) +{ + struct pld_context *pld_context; + int ret = 0; + + pld_context = pld_get_global_context(); + if (!pld_context) { + ret = -ENODEV; + goto out; + } + + ret = pld_add_dev(pld_context, dev, NULL, PLD_BUS_TYPE_IPCI); + if (ret) + goto out; + + return pld_context->ops->probe(dev, PLD_BUS_TYPE_IPCI, + NULL, NULL); + +out: + return ret; +} + +/** + * pld_ipci_remove() - Remove function for platform device + * @dev: device + * + * The remove function will be called when platform device + * is disconnected + * + * Return: void + */ +static void pld_ipci_remove(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + + if (!pld_context) + return; + + pld_context->ops->remove(dev, PLD_BUS_TYPE_SNOC); + + pld_del_dev(pld_context, dev); +} + +/** + * pld_ipci_reinit() - SSR re-initialize function for platform device + * @dev: device + * + * During subsystem restart(SSR), this function will be called to + * re-initialize platform device. + * + * Return: int + */ +static int pld_ipci_reinit(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->reinit) + return pld_context->ops->reinit(dev, PLD_BUS_TYPE_IPCI, + NULL, NULL); + + return -ENODEV; +} + +/** + * pld_ipci_shutdown() - SSR shutdown function for platform device + * @dev: device + * + * During SSR, this function will be called to shutdown platform device. + * + * Return: void + */ +static void pld_ipci_shutdown(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + pld_context->ops->shutdown(dev, PLD_BUS_TYPE_IPCI); +} + +/** + * pld_ipci_crash_shutdown() - Crash shutdown function for platform device + * @dev: device + * + * This function will be called when a crash is detected, it will shutdown + * platform device. + * + * Return: void + */ +static void pld_ipci_crash_shutdown(void *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->crash_shutdown) + pld_context->ops->crash_shutdown(dev, PLD_BUS_TYPE_IPCI); +} + +/** + * pld_ipci_pm_suspend() - PM suspend callback function for power management + * @dev: device + * + * This function is to suspend the platform device when power management + * is enabled. + * + * Return: void + */ +static int pld_ipci_pm_suspend(struct device *dev) +{ + struct pld_context *pld_context; + pm_message_t state; + + state.event = PM_EVENT_SUSPEND; + pld_context = pld_get_global_context(); + return pld_context->ops->suspend(dev, PLD_BUS_TYPE_IPCI, state); +} + +/** + * pld_ipci_pm_resume() - PM resume callback function for power management + * @pdev: device + * + * This function is to resume the platform device when power management + * is enabled. + * + * Return: void + */ +static int pld_ipci_pm_resume(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + return pld_context->ops->resume(dev, PLD_BUS_TYPE_IPCI); +} + +/** + * pld_ipci_suspend_noirq() - Complete the actions started by suspend() + * @dev: device + * + * Complete the actions started by suspend(). Carry out any + * additional operations required for suspending the device that might be + * racing with its driver's interrupt handler, which is guaranteed not to + * run while suspend_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_ipci_suspend_noirq(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->suspend_noirq) + return pld_context->ops->suspend_noirq(dev, PLD_BUS_TYPE_IPCI); + return 0; +} + +/** + * pld_ipci_resume_noirq() - Prepare for the execution of resume() + * @pdev: device + * + * Prepare for the execution of resume() by carrying out any + * operations required for resuming the device that might be racing with + * its driver's interrupt handler, which is guaranteed not to run while + * resume_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_ipci_resume_noirq(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->resume_noirq) + return pld_context->ops->resume_noirq(dev, PLD_BUS_TYPE_IPCI); + + return 0; +} + +static int pld_ipci_uevent(struct device *dev, + struct icnss_uevent_data *uevent) +{ + struct pld_context *pld_context; + struct icnss_uevent_fw_down_data *uevent_data = NULL; + struct pld_uevent_data data = {0}; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (!pld_context->ops->uevent) + goto out; + + if (!uevent) + return -EINVAL; + + switch (uevent->uevent) { + case ICNSS_UEVENT_FW_CRASHED: + data.uevent = PLD_FW_CRASHED; + break; + case ICNSS_UEVENT_FW_DOWN: + if (!uevent->data) + return -EINVAL; + uevent_data = (struct icnss_uevent_fw_down_data *)uevent->data; + data.uevent = PLD_FW_DOWN; + data.fw_down.crashed = uevent_data->crashed; + break; + default: + goto out; + } + + pld_context->ops->uevent(dev, &data); +out: + return 0; +} + +#ifdef MULTI_IF_NAME +#define PLD_IPCI_OPS_NAME "pld_ipci_" MULTI_IF_NAME +#else +#define PLD_IPCI_OPS_NAME "pld_ipci" +#endif + +struct icnss_driver_ops pld_ipci_ops = { + .name = PLD_IPCI_OPS_NAME, + .probe = pld_ipci_probe, + .remove = pld_ipci_remove, + .shutdown = pld_ipci_shutdown, + .reinit = pld_ipci_reinit, + .crash_shutdown = pld_ipci_crash_shutdown, + .pm_suspend = pld_ipci_pm_suspend, + .pm_resume = pld_ipci_pm_resume, + .suspend_noirq = pld_ipci_suspend_noirq, + .resume_noirq = pld_ipci_resume_noirq, + .uevent = pld_ipci_uevent, +}; + +/** + * pld_ipci_register_driver() - Register platform device callback functions + * + * Return: int + */ +int pld_ipci_register_driver(void) +{ + return icnss_register_driver(&pld_ipci_ops); +} + +/** + * pld_ipci_unregister_driver() - Unregister platform device callback functions + * + * Return: void + */ +void pld_ipci_unregister_driver(void) +{ + icnss_unregister_driver(&pld_ipci_ops); +} + +/** + * pld_ipci_wlan_enable() - Enable WLAN + * @dev: device + * @config: WLAN configuration data + * @mode: WLAN mode + * @host_version: host software version + * + * This function enables WLAN FW. It passed WLAN configuration data, + * WLAN mode and host software version to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ + +int pld_ipci_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version) +{ + struct icnss_wlan_enable_cfg cfg; + enum icnss_driver_mode icnss_mode; + + if (!dev) + return -ENODEV; + + cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg; + cfg.ce_tgt_cfg = (struct ce_tgt_pipe_cfg *) + config->ce_tgt_cfg; + cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg; + cfg.ce_svc_cfg = (struct ce_svc_pipe_cfg *) + config->ce_svc_cfg; + cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg; + cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *) + config->shadow_reg_cfg; + + switch (mode) { + case PLD_FTM: + icnss_mode = ICNSS_FTM; + break; + case PLD_EPPING: + icnss_mode = ICNSS_EPPING; + break; + default: + icnss_mode = ICNSS_MISSION; + break; + } + + return icnss_wlan_enable(dev, &cfg, icnss_mode, host_version); +} + +/** + * pld_ipci_wlan_disable() - Disable WLAN + * @dev: device + * @mode: WLAN mode + * + * This function disables WLAN FW. It passes WLAN mode to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_ipci_wlan_disable(struct device *dev, enum pld_driver_mode mode) +{ + if (!dev) + return -ENODEV; + + return icnss_wlan_disable(dev, ICNSS_OFF); +} + +/** + * pld_ipci_get_soc_info() - Get SOC information + * @dev: device + * @info: buffer to SOC information + * + * Return SOC info to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_ipci_get_soc_info(struct device *dev, struct pld_soc_info *info) +{ + int errno; + struct icnss_soc_info icnss_info = {0}; + + if (!info || !dev) + return -ENODEV; + + errno = icnss_get_soc_info(dev, &icnss_info); + if (errno) + return errno; + + info->v_addr = icnss_info.v_addr; + info->p_addr = icnss_info.p_addr; + info->chip_id = icnss_info.chip_id; + info->chip_family = icnss_info.chip_family; + info->board_id = icnss_info.board_id; + info->soc_id = icnss_info.soc_id; + info->fw_version = icnss_info.fw_version; + strlcpy(info->fw_build_timestamp, icnss_info.fw_build_timestamp, + sizeof(info->fw_build_timestamp)); + + return 0; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_ipci.h b/drivers/staging/qcacld-3.0/core/pld/src/pld_ipci.h new file mode 100644 index 0000000000000000000000000000000000000000..8de7006803c2ae2af8945d3fc21edacf22a61b4f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_ipci.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __PLD_IPCI_H__ +#define __PLD_IPCI_H__ + +#ifdef CONFIG_PLD_IPCI_ICNSS +#include +#endif +#include "pld_internal.h" + +#ifndef CONFIG_PLD_IPCI_ICNSS +static inline int pld_ipci_register_driver(void) +{ + return 0; +} + +static inline void pld_ipci_unregister_driver(void) +{ +} + +static inline int pld_ipci_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, + const char *host_version) +{ + return 0; +} + +static inline int pld_ipci_wlan_disable(struct device *dev, + enum pld_driver_mode mode) +{ + return 0; +} + +static inline int pld_ipci_get_soc_info(struct device *dev, + struct pld_soc_info *info) +{ + return 0; +} + +static inline int pld_ipci_power_on(struct device *dev) +{ + return 0; +} + +static inline int pld_ipci_power_off(struct device *dev) +{ + return 0; +} + +static inline int pld_ipci_idle_restart(struct device *dev) +{ + return 0; +} + +static inline int pld_ipci_idle_shutdown(struct device *dev) +{ + return 0; +} + +static inline int pld_ipci_force_assert_target(struct device *dev) +{ + return -EINVAL; +} + +static inline int pld_ipci_get_user_msi_assignment(struct device *dev, + char *user_name, + int *num_vectors, + uint32_t *user_base_data, + uint32_t *base_vector) +{ + return 0; +} + +static inline int pld_ipci_get_msi_irq(struct device *dev, unsigned int vector) +{ + return 0; +} + +static inline void pld_ipci_get_msi_address(struct device *dev, + uint32_t *msi_addr_low, + uint32_t *msi_addr_high) +{ +} + +static inline int pld_ipci_is_fw_down(struct device *dev) +{ + return 0; +} + +static inline int pld_ipci_set_fw_log_mode(struct device *dev, u8 fw_log_mode) +{ + return 0; +} + +#else +int pld_ipci_register_driver(void); +void pld_ipci_unregister_driver(void); +int pld_ipci_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version); +int pld_ipci_wlan_disable(struct device *dev, enum pld_driver_mode mode); +int pld_ipci_get_soc_info(struct device *dev, struct pld_soc_info *info); + +static inline int pld_ipci_power_on(struct device *dev) +{ + return icnss_power_on(dev); +} + +static inline int pld_ipci_power_off(struct device *dev) +{ + return icnss_power_off(dev); +} + +static inline int pld_ipci_idle_restart(struct device *dev) +{ + return icnss_idle_restart(dev); +} + +static inline int pld_ipci_idle_shutdown(struct device *dev) +{ + return icnss_idle_shutdown(dev); +} + +static inline int pld_ipci_force_assert_target(struct device *dev) +{ + return icnss_trigger_recovery(dev); +} + +static inline int pld_ipci_get_user_msi_assignment(struct device *dev, + char *user_name, + int *num_vectors, + uint32_t *user_base_data, + uint32_t *base_vector) +{ + return icnss_get_user_msi_assignment(dev, user_name, num_vectors, + user_base_data, base_vector); +} + +static inline int pld_ipci_get_msi_irq(struct device *dev, unsigned int vector) +{ + return icnss_get_msi_irq(dev, vector); +} + +static inline void pld_ipci_get_msi_address(struct device *dev, + uint32_t *msi_addr_low, + uint32_t *msi_addr_high) +{ + icnss_get_msi_address(dev, msi_addr_low, msi_addr_high); +} + +static inline int pld_ipci_is_fw_down(struct device *dev) +{ + return icnss_is_fw_down(); +} + +static inline int pld_ipci_set_fw_log_mode(struct device *dev, u8 fw_log_mode) +{ + return icnss_set_fw_log_mode(dev, fw_log_mode); +} + +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie.c b/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie.c new file mode 100644 index 0000000000000000000000000000000000000000..d3a17dff765bca93937dd82a96b7719db26a44f1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie.c @@ -0,0 +1,897 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_PLD_PCIE_CNSS +#include +#endif + +#include "pld_internal.h" +#include "pld_pcie.h" +#include "osif_psoc_sync.h" + +#ifdef CONFIG_PCI + +#ifdef QCA_WIFI_3_0_ADRASTEA +#define CE_COUNT_MAX 12 +#else +#define CE_COUNT_MAX 8 +#endif + +/** + * pld_pcie_probe() - Probe function for PCIE platform driver + * @pdev: PCIE device + * @id: PCIE device ID table + * + * The probe function will be called when PCIE device provided + * in the ID table is detected. + * + * Return: int + */ +static int pld_pcie_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pld_context *pld_context; + int ret = 0; + + pld_context = pld_get_global_context(); + if (!pld_context) { + ret = -ENODEV; + goto out; + } + + ret = pld_add_dev(pld_context, &pdev->dev, NULL, PLD_BUS_TYPE_PCIE); + if (ret) + goto out; + + return pld_context->ops->probe(&pdev->dev, + PLD_BUS_TYPE_PCIE, pdev, (void *)id); + +out: + return ret; +} + + +/** + * pld_pcie_remove() - Remove function for PCIE device + * @pdev: PCIE device + * + * The remove function will be called when PCIE device is disconnected + * + * Return: void + */ +static void pld_pcie_remove(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + int errno; + struct osif_psoc_sync *psoc_sync; + + errno = osif_psoc_sync_trans_start_wait(&pdev->dev, &psoc_sync); + if (errno) + return; + + osif_psoc_sync_unregister(&pdev->dev); + + osif_psoc_sync_wait_for_ops(psoc_sync); + + pld_context = pld_get_global_context(); + + if (!pld_context) + goto out; + + pld_context->ops->remove(&pdev->dev, PLD_BUS_TYPE_PCIE); + + pld_del_dev(pld_context, &pdev->dev); + +out: + osif_psoc_sync_trans_stop(psoc_sync); + osif_psoc_sync_destroy(psoc_sync); +} + +#ifdef CONFIG_PLD_PCIE_CNSS +/** + * pld_pcie_idle_restart_cb() - Perform idle restart + * @pdev: PCIE device + * @id: PCIE device ID + * + * This function will be called if there is an idle restart request + * + * Return: int + */ +static int pld_pcie_idle_restart_cb(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->idle_restart) + return pld_context->ops->idle_restart(&pdev->dev, + PLD_BUS_TYPE_PCIE); + + return -ENODEV; +} + +/** + * pld_pcie_idle_shutdown_cb() - Perform idle shutdown + * @pdev: PCIE device + * @id: PCIE device ID + * + * This function will be called if there is an idle shutdown request + * + * Return: int + */ +static int pld_pcie_idle_shutdown_cb(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + return pld_context->ops->idle_shutdown(&pdev->dev, + PLD_BUS_TYPE_PCIE); + + return -ENODEV; +} + +/** + * pld_pcie_reinit() - SSR re-initialize function for PCIE device + * @pdev: PCIE device + * @id: PCIE device ID + * + * During subsystem restart(SSR), this function will be called to + * re-initialize PCIE device. + * + * Return: int + */ +static int pld_pcie_reinit(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->reinit) + return pld_context->ops->reinit(&pdev->dev, + PLD_BUS_TYPE_PCIE, pdev, (void *)id); + + return -ENODEV; +} + +/** + * pld_pcie_shutdown() - SSR shutdown function for PCIE device + * @pdev: PCIE device + * + * During SSR, this function will be called to shutdown PCIE device. + * + * Return: void + */ +static void pld_pcie_shutdown(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + pld_context->ops->shutdown(&pdev->dev, PLD_BUS_TYPE_PCIE); +} + +/** + * pld_pcie_crash_shutdown() - Crash shutdown function for PCIE device + * @pdev: PCIE device + * + * This function will be called when a crash is detected, it will shutdown + * the PCIE device. + * + * Return: void + */ +static void pld_pcie_crash_shutdown(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->crash_shutdown) + pld_context->ops->crash_shutdown(&pdev->dev, PLD_BUS_TYPE_PCIE); +} + +/** + * pld_pcie_notify_handler() - Modem state notification callback function + * @pdev: PCIE device + * @state: modem power state + * + * This function will be called when there's a modem power state change. + * + * Return: void + */ +static void pld_pcie_notify_handler(struct pci_dev *pdev, int state) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->modem_status) + pld_context->ops->modem_status(&pdev->dev, + PLD_BUS_TYPE_PCIE, state); +} + +/** + * pld_pcie_uevent() - update wlan driver status callback function + * @pdev: PCIE device + * @status driver uevent status + * + * This function will be called when platform driver wants to update wlan + * driver's status. + * + * Return: void + */ +static void pld_pcie_uevent(struct pci_dev *pdev, uint32_t status) +{ + struct pld_context *pld_context; + struct pld_uevent_data data = {0}; + + pld_context = pld_get_global_context(); + if (!pld_context) + return; + + switch (status) { + case CNSS_RECOVERY: + data.uevent = PLD_FW_RECOVERY_START; + break; + case CNSS_FW_DOWN: + data.uevent = PLD_FW_DOWN; + break; + default: + goto out; + } + + if (pld_context->ops->uevent) + pld_context->ops->uevent(&pdev->dev, &data); + +out: + return; +} + +/** + * pld_pcie_update_event() - update wlan driver status callback function + * @pdev: PCIE device + * @cnss_uevent_data: driver uevent data + * + * This function will be called when platform driver wants to update wlan + * driver's status. + * + * Return: void + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static int pld_pcie_update_event(struct pci_dev *pdev, + struct cnss_uevent_data *uevent_data) +{ + struct pld_context *pld_context; + struct pld_uevent_data data = {0}; + struct cnss_hang_event *hang_event; + + pld_context = pld_get_global_context(); + + if (!pld_context || !uevent_data) + return -EINVAL; + + switch (uevent_data->status) { + case CNSS_HANG_EVENT: + if (!uevent_data->data) + return -EINVAL; + hang_event = (struct cnss_hang_event *)uevent_data->data; + data.uevent = PLD_FW_HANG_EVENT; + data.hang_data.hang_event_data = hang_event->hang_event_data; + data.hang_data.hang_event_data_len = + hang_event->hang_event_data_len; + break; + default: + goto out; + } + + if (pld_context->ops->uevent) + pld_context->ops->uevent(&pdev->dev, &data); + +out: + return 0; +} +#endif + +#ifdef FEATURE_RUNTIME_PM +/** + * pld_pcie_runtime_suspend() - PM runtime suspend + * @pdev: PCIE device + * + * PM runtime suspend callback function. + * + * Return: int + */ +static int pld_pcie_runtime_suspend(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->runtime_suspend) + return pld_context->ops->runtime_suspend(&pdev->dev, + PLD_BUS_TYPE_PCIE); + + return -ENODEV; +} + +/** + * pld_pcie_runtime_resume() - PM runtime resume + * @pdev: PCIE device + * + * PM runtime resume callback function. + * + * Return: int + */ +static int pld_pcie_runtime_resume(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->runtime_resume) + return pld_context->ops->runtime_resume(&pdev->dev, + PLD_BUS_TYPE_PCIE); + + return -ENODEV; +} +#endif +#endif + +#ifdef CONFIG_PM +#ifdef CONFIG_PLD_PCIE_CNSS +/** + * pld_pcie_suspend() - Suspend callback function for power management + * @pdev: PCIE device + * @state: power state + * + * This function is to suspend the PCIE device when power management is + * enabled. + * + * Return: void + */ +static int pld_pcie_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + return pld_context->ops->suspend(&pdev->dev, + PLD_BUS_TYPE_PCIE, state); +} + +/** + * pld_pcie_resume() - Resume callback function for power management + * @pdev: PCIE device + * + * This function is to resume the PCIE device when power management is + * enabled. + * + * Return: void + */ +static int pld_pcie_resume(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + return pld_context->ops->resume(&pdev->dev, PLD_BUS_TYPE_PCIE); +} + +/** + * pld_pcie_suspend_noirq() - Complete the actions started by suspend() + * @pdev: PCI device + * + * Complete the actions started by suspend(). Carry out any additional + * operations required for suspending the device that might be racing + * with its driver's interrupt handler, which is guaranteed not to run + * while suspend_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_pcie_suspend_noirq(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->suspend_noirq) + return pld_context->ops-> + suspend_noirq(&pdev->dev, PLD_BUS_TYPE_PCIE); + return 0; +} + +/** + * pld_pcie_resume_noirq() - Prepare for the execution of resume() + * @pdev: PCI device + * + * Prepare for the execution of resume() by carrying out any additional + * operations required for resuming the device that might be racing with + * its driver's interrupt handler, which is guaranteed not to run while + * resume_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_pcie_resume_noirq(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->resume_noirq) + return pld_context->ops-> + resume_noirq(&pdev->dev, PLD_BUS_TYPE_PCIE); + return 0; +} +#else +/** + * pld_pcie_pm_suspend() - Suspend callback function for power management + * @dev: device + * + * This function is to suspend the PCIE device when power management is + * enabled. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_pcie_pm_suspend(struct device *dev) +{ + struct pld_context *pld_context; + + pm_message_t state = { .event = PM_EVENT_SUSPEND }; + + pld_context = pld_get_global_context(); + return pld_context->ops->suspend(dev, PLD_BUS_TYPE_PCIE, state); +} + +/** + * pld_pcie_pm_resume() - Resume callback function for power management + * @dev: device + * + * This function is to resume the PCIE device when power management is + * enabled. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_pcie_pm_resume(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + return pld_context->ops->resume(dev, PLD_BUS_TYPE_PCIE); +} + +/** + * pld_pcie_pm_suspend_noirq() - Complete the actions started by suspend() + * @dev: device + * + * Complete the actions started by suspend(). Carry out any additional + * operations required for suspending the device that might be racing + * with its driver's interrupt handler, which is guaranteed not to run + * while suspend_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_pcie_pm_suspend_noirq(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->suspend_noirq) + return pld_context->ops->suspend_noirq(dev, PLD_BUS_TYPE_PCIE); + return 0; +} + +/** + * pld_pcie_pm_resume_noirq() - Prepare for the execution of resume() + * @dev: device + * + * Prepare for the execution of resume() by carrying out any additional + * operations required for resuming the device that might be racing with + * its driver's interrupt handler, which is guaranteed not to run while + * resume_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_pcie_pm_resume_noirq(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->resume_noirq) + return pld_context->ops-> + resume_noirq(dev, PLD_BUS_TYPE_PCIE); + return 0; +} +#endif +#endif + +static struct pci_device_id pld_pcie_id_table[] = { +#ifdef CONFIG_AR6320_SUPPORT + { 0x168c, 0x003e, PCI_ANY_ID, PCI_ANY_ID }, +#elif defined(QCA_WIFI_QCA6290) + { 0x17cb, 0x1100, PCI_ANY_ID, PCI_ANY_ID }, +#elif defined(QCA_WIFI_QCA6390) + { 0x17cb, 0x1101, PCI_ANY_ID, PCI_ANY_ID }, +#elif defined(QCA_WIFI_QCA6490) + { 0x17cb, 0x1103, PCI_ANY_ID, PCI_ANY_ID }, +#elif defined(QCN7605_SUPPORT) + { 0x17cb, 0x1102, PCI_ANY_ID, PCI_ANY_ID }, +#else + { 0x168c, 0x003c, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x0041, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0xabcd, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x7021, PCI_ANY_ID, PCI_ANY_ID }, +#endif + { 0 } +}; + +#ifdef MULTI_IF_NAME +#define PLD_PCIE_OPS_NAME "pld_pcie_" MULTI_IF_NAME +#else +#define PLD_PCIE_OPS_NAME "pld_pcie" +#endif + +#ifdef CONFIG_PLD_PCIE_CNSS +#ifdef FEATURE_RUNTIME_PM +struct cnss_wlan_runtime_ops runtime_pm_ops = { + .runtime_suspend = pld_pcie_runtime_suspend, + .runtime_resume = pld_pcie_runtime_resume, +}; +#endif + +struct cnss_wlan_driver pld_pcie_ops = { + .name = PLD_PCIE_OPS_NAME, + .id_table = pld_pcie_id_table, + .probe = pld_pcie_probe, + .remove = pld_pcie_remove, + .idle_restart = pld_pcie_idle_restart_cb, + .idle_shutdown = pld_pcie_idle_shutdown_cb, + .reinit = pld_pcie_reinit, + .shutdown = pld_pcie_shutdown, + .crash_shutdown = pld_pcie_crash_shutdown, + .modem_status = pld_pcie_notify_handler, + .update_status = pld_pcie_uevent, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + .update_event = pld_pcie_update_event, +#endif +#ifdef CONFIG_PM + .suspend = pld_pcie_suspend, + .resume = pld_pcie_resume, + .suspend_noirq = pld_pcie_suspend_noirq, + .resume_noirq = pld_pcie_resume_noirq, +#endif +#ifdef FEATURE_RUNTIME_PM + .runtime_ops = &runtime_pm_ops, +#endif +}; + +/** + * pld_pcie_register_driver() - Register PCIE device callback functions + * + * Return: int + */ +int pld_pcie_register_driver(void) +{ + return cnss_wlan_register_driver(&pld_pcie_ops); +} + +/** + * pld_pcie_unregister_driver() - Unregister PCIE device callback functions + * + * Return: void + */ +void pld_pcie_unregister_driver(void) +{ + cnss_wlan_unregister_driver(&pld_pcie_ops); +} +#else +#ifdef CONFIG_PM +static const struct dev_pm_ops pld_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pld_pcie_pm_suspend, pld_pcie_pm_resume) + .suspend_noirq = pld_pcie_pm_suspend_noirq, + .resume_noirq = pld_pcie_pm_resume_noirq, +}; +#endif + +struct pci_driver pld_pcie_ops = { + .name = PLD_PCIE_OPS_NAME, + .id_table = pld_pcie_id_table, + .probe = pld_pcie_probe, + .remove = pld_pcie_remove, + .driver = { +#ifdef CONFIG_PM + .pm = &pld_pm_ops, +#endif + }, +}; + +int pld_pcie_register_driver(void) +{ + return pci_register_driver(&pld_pcie_ops); +} + +void pld_pcie_unregister_driver(void) +{ + pci_unregister_driver(&pld_pcie_ops); +} +#endif + +/** + * pld_pcie_get_ce_id() - Get CE number for the provided IRQ + * @dev: device + * @irq: IRQ number + * + * Return: CE number + */ +int pld_pcie_get_ce_id(struct device *dev, int irq) +{ + int ce_id = irq - 100; + + if (ce_id < CE_COUNT_MAX && ce_id >= 0) + return ce_id; + + return -EINVAL; +} + +#ifdef CONFIG_PLD_PCIE_CNSS +/** + * pld_pcie_wlan_enable() - Enable WLAN + * @dev: device + * @config: WLAN configuration data + * @mode: WLAN mode + * @host_version: host software version + * + * This function enables WLAN FW. It passed WLAN configuration data, + * WLAN mode and host software version to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pcie_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version) +{ + struct cnss_wlan_enable_cfg cfg; + enum cnss_driver_mode cnss_mode; + + cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg; + cfg.ce_tgt_cfg = (struct cnss_ce_tgt_pipe_cfg *) + config->ce_tgt_cfg; + cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg; + cfg.ce_svc_cfg = (struct cnss_ce_svc_pipe_cfg *) + config->ce_svc_cfg; + cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg; + cfg.shadow_reg_cfg = (struct cnss_shadow_reg_cfg *) + config->shadow_reg_cfg; + cfg.num_shadow_reg_v2_cfg = config->num_shadow_reg_v2_cfg; + cfg.shadow_reg_v2_cfg = (struct cnss_shadow_reg_v2_cfg *) + config->shadow_reg_v2_cfg; + cfg.rri_over_ddr_cfg_valid = config->rri_over_ddr_cfg_valid; + if (config->rri_over_ddr_cfg_valid) { + cfg.rri_over_ddr_cfg.base_addr_low = + config->rri_over_ddr_cfg.base_addr_low; + cfg.rri_over_ddr_cfg.base_addr_high = + config->rri_over_ddr_cfg.base_addr_high; + } + + switch (mode) { + case PLD_FTM: + cnss_mode = CNSS_FTM; + break; + case PLD_EPPING: + cnss_mode = CNSS_EPPING; + break; + default: + cnss_mode = CNSS_MISSION; + break; + } + return cnss_wlan_enable(dev, &cfg, cnss_mode, host_version); +} + +/** + * pld_pcie_wlan_disable() - Disable WLAN + * @dev: device + * @mode: WLAN mode + * + * This function disables WLAN FW. It passes WLAN mode to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pcie_wlan_disable(struct device *dev, enum pld_driver_mode mode) +{ + return cnss_wlan_disable(dev, CNSS_OFF); +} + +/** + * pld_pcie_get_fw_files_for_target() - Get FW file names + * @dev: device + * @pfw_files: buffer for FW file names + * @target_type: target type + * @target_version: target version + * + * Return target specific FW file names to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pcie_get_fw_files_for_target(struct device *dev, + struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version) +{ + int ret = 0; + struct cnss_fw_files cnss_fw_files; + + if (!pfw_files) + return -ENODEV; + + memset(pfw_files, 0, sizeof(*pfw_files)); + + ret = cnss_get_fw_files_for_target(dev, &cnss_fw_files, + target_type, target_version); + if (ret) + return ret; + + scnprintf(pfw_files->image_file, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.image_file); + scnprintf(pfw_files->board_data, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.board_data); + scnprintf(pfw_files->otp_data, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.otp_data); + scnprintf(pfw_files->utf_file, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.utf_file); + scnprintf(pfw_files->utf_board_data, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.utf_board_data); + scnprintf(pfw_files->epping_file, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.epping_file); + scnprintf(pfw_files->evicted_data, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.evicted_data); + + return 0; +} + +/** + * pld_pcie_get_platform_cap() - Get platform capabilities + * @dev: device + * @cap: buffer to the capabilities + * + * Return capabilities to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pcie_get_platform_cap(struct device *dev, struct pld_platform_cap *cap) +{ + int ret = 0; + struct cnss_platform_cap cnss_cap; + + if (!cap) + return -ENODEV; + + ret = cnss_get_platform_cap(dev, &cnss_cap); + if (ret) + return ret; + + memcpy(cap, &cnss_cap, sizeof(*cap)); + return 0; +} + +/** + * pld_pcie_get_soc_info() - Get SOC information + * @dev: device + * @info: buffer to SOC information + * + * Return SOC info to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pcie_get_soc_info(struct device *dev, struct pld_soc_info *info) +{ + int ret = 0; + struct cnss_soc_info cnss_info = {0}; + + if (!info) + return -ENODEV; + + ret = cnss_get_soc_info(dev, &cnss_info); + if (ret) + return ret; + + info->v_addr = cnss_info.va; + info->p_addr = cnss_info.pa; + info->chip_id = cnss_info.chip_id; + info->chip_family = cnss_info.chip_family; + info->board_id = cnss_info.board_id; + info->soc_id = cnss_info.soc_id; + info->fw_version = cnss_info.fw_version; + strlcpy(info->fw_build_timestamp, cnss_info.fw_build_timestamp, + sizeof(info->fw_build_timestamp)); + info->device_version.family_number = + cnss_info.device_version.family_number; + info->device_version.device_number = + cnss_info.device_version.device_number; + info->device_version.major_version = + cnss_info.device_version.major_version; + info->device_version.minor_version = + cnss_info.device_version.minor_version; + + return 0; +} + +/** + * pld_pcie_schedule_recovery_work() - schedule recovery work + * @dev: device + * @reason: recovery reason + * + * Return: void + */ +void pld_pcie_schedule_recovery_work(struct device *dev, + enum pld_recovery_reason reason) +{ + enum cnss_recovery_reason cnss_reason; + + switch (reason) { + case PLD_REASON_LINK_DOWN: + cnss_reason = CNSS_REASON_LINK_DOWN; + break; + default: + cnss_reason = CNSS_REASON_DEFAULT; + break; + } + cnss_schedule_recovery(dev, cnss_reason); +} + +/** + * pld_pcie_device_self_recovery() - device self recovery + * @dev: device + * @reason: recovery reason + * + * Return: void + */ +void pld_pcie_device_self_recovery(struct device *dev, + enum pld_recovery_reason reason) +{ + enum cnss_recovery_reason cnss_reason; + + switch (reason) { + case PLD_REASON_LINK_DOWN: + cnss_reason = CNSS_REASON_LINK_DOWN; + break; + default: + cnss_reason = CNSS_REASON_DEFAULT; + break; + } + cnss_self_recovery(dev, cnss_reason); +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie.h b/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie.h new file mode 100644 index 0000000000000000000000000000000000000000..1b64336bcc780d9b590db32524116336ddfefc1d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie.h @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __PLD_PCIE_H__ +#define __PLD_PCIE_H__ + +#ifdef CONFIG_PLD_PCIE_CNSS +#include +#endif +#include +#include "pld_internal.h" + +#ifdef DYNAMIC_SINGLE_CHIP +#define PREFIX DYNAMIC_SINGLE_CHIP "/" +#else + +#ifdef MULTI_IF_NAME +#define PREFIX MULTI_IF_NAME "/" +#else +#define PREFIX "" +#endif + +#endif + +#if !defined(HIF_PCI) || defined(CONFIG_PLD_PCIE_FW_SIM) +static inline int pld_pcie_register_driver(void) +{ + return 0; +} + +static inline void pld_pcie_unregister_driver(void) +{ +} + +static inline int pld_pcie_get_ce_id(struct device *dev, int irq) +{ + return 0; +} +#else +int pld_pcie_register_driver(void); +void pld_pcie_unregister_driver(void); +int pld_pcie_get_ce_id(struct device *dev, int irq); +#endif + +#ifndef CONFIG_PLD_PCIE_CNSS +static inline int pld_pcie_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, + const char *host_version) +{ + return 0; +} + +static inline int pld_pcie_wlan_disable(struct device *dev, + enum pld_driver_mode mode) +{ + return 0; +} +#else +int pld_pcie_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version); +int pld_pcie_wlan_disable(struct device *dev, enum pld_driver_mode mode); +#endif + +#if defined(CONFIG_PLD_PCIE_CNSS) +static inline int pld_pcie_set_fw_log_mode(struct device *dev, u8 fw_log_mode) +{ + return cnss_set_fw_log_mode(dev, fw_log_mode); +} + +static inline void pld_pcie_intr_notify_q6(struct device *dev) +{ +} +#else +static inline int pld_pcie_set_fw_log_mode(struct device *dev, u8 fw_log_mode) +{ + return 0; +} + +static inline void pld_pcie_intr_notify_q6(struct device *dev) +{ +} +#endif + +#if (!defined(CONFIG_PLD_PCIE_CNSS)) || (!defined(CONFIG_CNSS_SECURE_FW)) +static inline int pld_pcie_get_sha_hash(struct device *dev, const u8 *data, + u32 data_len, u8 *hash_idx, u8 *out) +{ + return 0; +} + +static inline void *pld_pcie_get_fw_ptr(struct device *dev) +{ + return NULL; +} +#else +static inline int pld_pcie_get_sha_hash(struct device *dev, const u8 *data, + u32 data_len, u8 *hash_idx, u8 *out) +{ + return cnss_get_sha_hash(data, data_len, hash_idx, out); +} + +static inline void *pld_pcie_get_fw_ptr(struct device *dev) +{ + return cnss_get_fw_ptr(); +} +#endif + +#if (!defined(CONFIG_PLD_PCIE_CNSS)) || (!defined(CONFIG_PCI_MSM)) +static inline int pld_pcie_wlan_pm_control(struct device *dev, bool vote) +{ + return 0; +} +#else +static inline int pld_pcie_wlan_pm_control(struct device *dev, bool vote) +{ + return cnss_wlan_pm_control(dev, vote); +} +#endif + +#ifndef CONFIG_PLD_PCIE_CNSS +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static inline void *pld_pcie_smmu_get_domain(struct device *dev) +{ + return NULL; +} +#else +static inline void *pld_pcie_smmu_get_mapping(struct device *dev) +{ + return NULL; +} +#endif + +static inline int +pld_pcie_smmu_map(struct device *dev, + phys_addr_t paddr, uint32_t *iova_addr, size_t size) +{ + return 0; +} + +static inline int +pld_pcie_smmu_unmap(struct device *dev, uint32_t iova_addr, size_t size) +{ + return 0; +} + +static inline int +pld_pcie_get_fw_files_for_target(struct device *dev, + struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version) +{ + pld_get_default_fw_files(pfw_files); + return 0; +} + +static inline int pld_pcie_prevent_l1(struct device *dev) +{ + return 0; +} + +static inline void pld_pcie_allow_l1(struct device *dev) +{ +} + +static inline void pld_pcie_link_down(struct device *dev) +{ +} + +static inline int pld_pcie_get_reg_dump(struct device *dev, uint8_t *buf, + uint32_t len) +{ + return 0; +} + +static inline int pld_pcie_is_fw_down(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_athdiag_read(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *output) +{ + return 0; +} + +static inline int pld_pcie_athdiag_write(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *input) +{ + return 0; +} + +static inline void +pld_pcie_schedule_recovery_work(struct device *dev, + enum pld_recovery_reason reason) +{ +} + +static inline void *pld_pcie_get_virt_ramdump_mem(struct device *dev, + unsigned long *size) +{ + size_t length = 0; + int flags = GFP_KERNEL; + + length = TOTAL_DUMP_SIZE; + + if (!size) + return NULL; + + *size = (unsigned long)length; + + if (in_interrupt() || irqs_disabled() || in_atomic()) + flags = GFP_ATOMIC; + + return kzalloc(length, flags); +} + +static inline void pld_pcie_release_virt_ramdump_mem(void *address) +{ + kfree(address); +} + +static inline void pld_pcie_device_crashed(struct device *dev) +{ +} + +static inline void pld_pcie_device_self_recovery(struct device *dev, + enum pld_recovery_reason reason) +{ +} + +static inline void pld_pcie_request_pm_qos(struct device *dev, u32 qos_val) +{ +} + +static inline void pld_pcie_remove_pm_qos(struct device *dev) +{ +} + +static inline int pld_pcie_request_bus_bandwidth(struct device *dev, + int bandwidth) +{ + return 0; +} + +static inline int pld_pcie_get_platform_cap(struct device *dev, + struct pld_platform_cap *cap) +{ + return 0; +} + +static inline int pld_pcie_get_soc_info(struct device *dev, + struct pld_soc_info *info) +{ + return 0; +} + +static inline int pld_pcie_auto_suspend(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_auto_resume(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_force_wake_request(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_force_wake_request_sync(struct device *dev, + int timeout_us) +{ + return 0; +} + +static inline int pld_pcie_is_device_awake(struct device *dev) +{ + return true; +} + +static inline int pld_pcie_force_wake_release(struct device *dev) +{ + return 0; +} + +static inline void pld_pcie_lock_pm_sem(struct device *dev) +{ +} + +static inline void pld_pcie_release_pm_sem(struct device *dev) +{ +} + +static inline void pld_pcie_lock_reg_window(struct device *dev, + unsigned long *flags) +{ +} + +static inline void pld_pcie_unlock_reg_window(struct device *dev, + unsigned long *flags) +{ +} + +static inline int pld_pcie_power_on(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_power_off(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_idle_restart(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_idle_shutdown(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_force_assert_target(struct device *dev) +{ + return -EINVAL; +} + +static inline int pld_pcie_collect_rddm(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_qmi_send_get(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_qmi_send_put(struct device *dev) +{ + return 0; +} + +static inline int +pld_pcie_qmi_send(struct device *dev, int type, void *cmd, + int cmd_len, void *cb_ctx, + int (*cb)(void *ctx, void *event, int event_len)) +{ + return -EINVAL; +} + +static inline int pld_pcie_get_user_msi_assignment(struct device *dev, + char *user_name, + int *num_vectors, + uint32_t *user_base_data, + uint32_t *base_vector) +{ + return -EINVAL; +} + +static inline int pld_pcie_get_msi_irq(struct device *dev, unsigned int vector) +{ + return 0; +} + +static inline void pld_pcie_get_msi_address(struct device *dev, + uint32_t *msi_addr_low, + uint32_t *msi_addr_high) +{ + return; +} + +static inline int pld_pcie_is_drv_connected(struct device *dev) +{ + return 0; +} + +static inline bool pld_pcie_platform_driver_support(void) +{ + return false; +} +#else +int pld_pcie_get_fw_files_for_target(struct device *dev, + struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version); +int pld_pcie_get_platform_cap(struct device *dev, struct pld_platform_cap *cap); +int pld_pcie_get_soc_info(struct device *dev, struct pld_soc_info *info); +void pld_pcie_schedule_recovery_work(struct device *dev, + enum pld_recovery_reason reason); +void pld_pcie_device_self_recovery(struct device *dev, + enum pld_recovery_reason reason); +static inline int pld_pcie_collect_rddm(struct device *dev) +{ + return cnss_force_collect_rddm(dev); +} + +static inline int pld_pcie_qmi_send_get(struct device *dev) +{ + return cnss_qmi_send_get(dev); +} + +static inline int pld_pcie_qmi_send_put(struct device *dev) +{ + return cnss_qmi_send_put(dev); +} + +static inline int +pld_pcie_qmi_send(struct device *dev, int type, void *cmd, + int cmd_len, void *cb_ctx, + int (*cb)(void *ctx, void *event, int event_len)) +{ + return cnss_qmi_send(dev, type, cmd, cmd_len, cb_ctx, cb); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static inline void *pld_pcie_smmu_get_domain(struct device *dev) +{ + return cnss_smmu_get_domain(dev); +} +#else +static inline void *pld_pcie_smmu_get_mapping(struct device *dev) +{ + return cnss_smmu_get_mapping(dev); +} +#endif + +static inline int +pld_pcie_smmu_map(struct device *dev, + phys_addr_t paddr, uint32_t *iova_addr, size_t size) +{ + return cnss_smmu_map(dev, paddr, iova_addr, size); +} + +#ifdef CONFIG_SMMU_S1_UNMAP +static inline int +pld_pcie_smmu_unmap(struct device *dev, uint32_t iova_addr, size_t size) +{ + return cnss_smmu_unmap(dev, iova_addr, size); +} +#else /* !CONFIG_SMMU_S1_UNMAP */ +static inline int +pld_pcie_smmu_unmap(struct device *dev, uint32_t iova_addr, size_t size) +{ + return 0; +} +#endif /* CONFIG_SMMU_S1_UNMAP */ + +static inline int pld_pcie_prevent_l1(struct device *dev) +{ + return cnss_pci_prevent_l1(dev); +} + +static inline void pld_pcie_allow_l1(struct device *dev) +{ + cnss_pci_allow_l1(dev); +} + +static inline void pld_pcie_link_down(struct device *dev) +{ + cnss_pci_link_down(dev); +} + +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0))) +static inline int pld_pcie_get_reg_dump(struct device *dev, uint8_t *buf, + uint32_t len) +{ + return cnss_pci_get_reg_dump(dev, buf, len); +} +#else +static inline int pld_pcie_get_reg_dump(struct device *dev, uint8_t *buf, + uint32_t len) +{ + return 0; +} +#endif + +static inline int pld_pcie_is_fw_down(struct device *dev) +{ + return cnss_pci_is_device_down(dev); +} + +static inline int pld_pcie_athdiag_read(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *output) +{ + return cnss_athdiag_read(dev, offset, memtype, datalen, output); +} + +static inline int pld_pcie_athdiag_write(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *input) +{ + return cnss_athdiag_write(dev, offset, memtype, datalen, input); +} + +static inline void *pld_pcie_get_virt_ramdump_mem(struct device *dev, + unsigned long *size) +{ + return cnss_get_virt_ramdump_mem(dev, size); +} + +static inline void pld_pcie_release_virt_ramdump_mem(void *address) +{ +} + +static inline void pld_pcie_device_crashed(struct device *dev) +{ + cnss_device_crashed(dev); +} + +static inline void pld_pcie_request_pm_qos(struct device *dev, u32 qos_val) +{ + cnss_request_pm_qos(dev, qos_val); +} + +static inline void pld_pcie_remove_pm_qos(struct device *dev) +{ + cnss_remove_pm_qos(dev); +} + +static inline int pld_pcie_request_bus_bandwidth(struct device *dev, + int bandwidth) +{ + return cnss_request_bus_bandwidth(dev, bandwidth); +} + +static inline int pld_pcie_auto_suspend(struct device *dev) +{ + return cnss_auto_suspend(dev); +} + +static inline int pld_pcie_auto_resume(struct device *dev) +{ + return cnss_auto_resume(dev); +} + +static inline int pld_pcie_force_wake_request(struct device *dev) +{ + return cnss_pci_force_wake_request(dev); +} + +static inline int pld_pcie_force_wake_request_sync(struct device *dev, + int timeout_us) +{ + return cnss_pci_force_wake_request_sync(dev, timeout_us); +} + +static inline int pld_pcie_is_device_awake(struct device *dev) +{ + return cnss_pci_is_device_awake(dev); +} + +static inline int pld_pcie_force_wake_release(struct device *dev) +{ + return cnss_pci_force_wake_release(dev); +} + +static inline void pld_pcie_lock_pm_sem(struct device *dev) +{ + cnss_lock_pm_sem(dev); +} + +static inline void pld_pcie_release_pm_sem(struct device *dev) +{ + cnss_release_pm_sem(dev); +} + +static inline void pld_pcie_lock_reg_window(struct device *dev, + unsigned long *flags) +{ + cnss_pci_lock_reg_window(dev, flags); +} + +static inline void pld_pcie_unlock_reg_window(struct device *dev, + unsigned long *flags) +{ + cnss_pci_unlock_reg_window(dev, flags); +} + +static inline int pld_pcie_power_on(struct device *dev) +{ + return cnss_power_up(dev); +} + +static inline int pld_pcie_power_off(struct device *dev) +{ + return cnss_power_down(dev); +} + +static inline int pld_pcie_idle_restart(struct device *dev) +{ + return cnss_idle_restart(dev); +} + +static inline int pld_pcie_idle_shutdown(struct device *dev) +{ + return cnss_idle_shutdown(dev); +} + +static inline int pld_pcie_force_assert_target(struct device *dev) +{ + return cnss_force_collect_rddm(dev); +} + +static inline int pld_pcie_get_user_msi_assignment(struct device *dev, + char *user_name, + int *num_vectors, + uint32_t *user_base_data, + uint32_t *base_vector) +{ + return cnss_get_user_msi_assignment(dev, user_name, num_vectors, + user_base_data, base_vector); +} + +static inline int pld_pcie_get_msi_irq(struct device *dev, unsigned int vector) +{ + return cnss_get_msi_irq(dev, vector); +} + +static inline void pld_pcie_get_msi_address(struct device *dev, + uint32_t *msi_addr_low, + uint32_t *msi_addr_high) +{ + cnss_get_msi_address(dev, msi_addr_low, msi_addr_high); +} + +static inline int pld_pcie_is_drv_connected(struct device *dev) +{ + return cnss_pci_is_drv_connected(dev); +} + +static inline bool pld_pcie_platform_driver_support(void) +{ + return true; +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie_fw_sim.c b/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie_fw_sim.c new file mode 100644 index 0000000000000000000000000000000000000000..ed4bf30651ed55dced2817c1138751923db1d30d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie_fw_sim.c @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include "osif_psoc_sync.h" + +#include "pld_pcie_fw_sim.h" + +#ifdef CONFIG_PLD_PCIE_FW_SIM + +#ifdef QCA_WIFI_3_0_ADRASTEA +#define CE_COUNT_MAX 12 +#else +#define CE_COUNT_MAX 8 +#endif + +/** + * pld_pcie_fw_sim_probe() - Probe function for PCIE platform driver + * @pdev: PCIE device + * @id: PCIE device ID table + * + * The probe function will be called when PCIE device provided + * in the ID table is detected. + * + * Return: int + */ +static int pld_pcie_fw_sim_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pld_context *pld_context; + int ret = 0; + + pld_context = pld_get_global_context(); + if (!pld_context) { + ret = -ENODEV; + goto out; + } + + ret = pld_add_dev(pld_context, &pdev->dev, NULL, + PLD_BUS_TYPE_PCIE_FW_SIM); + if (ret) + goto out; + + return pld_context->ops->probe(&pdev->dev, + PLD_BUS_TYPE_PCIE_FW_SIM, pdev, (void *)id); + +out: + return ret; +} + +/** + * pld_pcie_fw_sim_remove() - Remove function for PCIE device + * @pdev: PCIE device + * + * The remove function will be called when PCIE device is disconnected + * + * Return: void + */ +static void pld_pcie_fw_sim_remove(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + int errno; + struct osif_psoc_sync *psoc_sync; + + errno = osif_psoc_sync_trans_start_wait(&pdev->dev, &psoc_sync); + if (errno) + return; + + osif_psoc_sync_unregister(&pdev->dev); + osif_psoc_sync_wait_for_ops(psoc_sync); + + pld_context = pld_get_global_context(); + + if (!pld_context) + goto out; + + pld_context->ops->remove(&pdev->dev, PLD_BUS_TYPE_PCIE_FW_SIM); + + pld_del_dev(pld_context, &pdev->dev); + +out: + osif_psoc_sync_trans_stop(psoc_sync); + osif_psoc_sync_destroy(psoc_sync); +} + +/** + * pld_pcie_fw_sim_idle_restart_cb() - Perform idle restart + * @pdev: PCIE device + * @id: PCIE device ID + * + * This function will be called if there is an idle restart request + * + * Return: int + */ +static int pld_pcie_fw_sim_idle_restart_cb(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->idle_restart) + return pld_context->ops->idle_restart(&pdev->dev, + PLD_BUS_TYPE_PCIE_FW_SIM); + + return -ENODEV; +} + +/** + * pld_pcie_fw_sim_idle_shutdown_cb() - Perform idle shutdown + * @pdev: PCIE device + * @id: PCIE device ID + * + * This function will be called if there is an idle shutdown request + * + * Return: int + */ +static int pld_pcie_fw_sim_idle_shutdown_cb(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + return pld_context->ops->idle_shutdown(&pdev->dev, + PLD_BUS_TYPE_PCIE_FW_SIM); + + return -ENODEV; +} + +/** + * pld_pcie_fw_sim_reinit() - SSR re-initialize function for PCIE device + * @pdev: PCIE device + * @id: PCIE device ID + * + * During subsystem restart(SSR), this function will be called to + * re-initialize pcie device. + * + * Return: int + */ +static int pld_pcie_fw_sim_reinit(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->reinit) + return pld_context->ops->reinit(&pdev->dev, + PLD_BUS_TYPE_PCIE_FW_SIM, pdev, (void *)id); + + return -ENODEV; +} + +/** + * pld_pcie_fw_sim_shutdown() - SSR shutdown function for PCIE device + * @pdev: PCIE device + * + * During SSR, this function will be called to shutdown PCIE device. + * + * Return: void + */ +static void pld_pcie_fw_sim_shutdown(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + pld_context->ops->shutdown(&pdev->dev, + PLD_BUS_TYPE_PCIE_FW_SIM); +} + +/** + * pld_pcie_fw_sim_crash_shutdown() - Crash shutdown function for PCIE device + * @pdev: PCIE device + * + * This function will be called when a crash is detected, it will shutdown + * the PCIE device. + * + * Return: void + */ +static void pld_pcie_fw_sim_crash_shutdown(struct pci_dev *pdev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->crash_shutdown) + pld_context->ops->crash_shutdown(&pdev->dev, + PLD_BUS_TYPE_PCIE_FW_SIM); +} + +/** + * pld_pcie_fw_sim_notify_handler() - Modem state notification callback function + * @pdev: PCIE device + * @state: modem power state + * + * This function will be called when there's a modem power state change. + * + * Return: void + */ +static void pld_pcie_fw_sim_notify_handler(struct pci_dev *pdev, int state) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->modem_status) + pld_context->ops->modem_status(&pdev->dev, + PLD_BUS_TYPE_PCIE_FW_SIM, state); +} + +/** + * pld_pcie_fw_sim_uevent() - update wlan driver status callback function + * @pdev: PCIE device + * @status driver uevent status + * + * This function will be called when platform driver wants to update wlan + * driver's status. + * + * Return: void + */ +static void pld_pcie_fw_sim_uevent(struct pci_dev *pdev, uint32_t status) +{ + struct pld_context *pld_context; + struct pld_uevent_data data = {0}; + + pld_context = pld_get_global_context(); + if (!pld_context) + return; + + switch (status) { + case CNSS_RECOVERY: + data.uevent = PLD_FW_RECOVERY_START; + break; + case CNSS_FW_DOWN: + data.uevent = PLD_FW_DOWN; + break; + default: + goto out; + } + + if (pld_context->ops->uevent) + pld_context->ops->uevent(&pdev->dev, &data); + +out: + return; +} + +static struct pci_device_id pld_pcie_fw_sim_id_table[] = { + { 0x168c, 0x003c, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x003e, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x0041, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0xabcd, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x7021, PCI_ANY_ID, PCI_ANY_ID }, + { 0 } +}; + +#ifdef MULTI_IF_NAME +#define PLD_PCIE_FW_SIM_OPS_NAME "pld_pcie_fw_sim_" MULTI_IF_NAME +#else +#define PLD_PCIE_FW_SIM_OPS_NAME "pld_pcie_fw_sim" +#endif + +struct cnss_wlan_driver pld_pcie_fw_sim_ops = { + .name = PLD_PCIE_FW_SIM_OPS_NAME, + .id_table = pld_pcie_fw_sim_id_table, + .probe = pld_pcie_fw_sim_probe, + .remove = pld_pcie_fw_sim_remove, + .idle_restart = pld_pcie_fw_sim_idle_restart_cb, + .idle_shutdown = pld_pcie_fw_sim_idle_shutdown_cb, + .reinit = pld_pcie_fw_sim_reinit, + .shutdown = pld_pcie_fw_sim_shutdown, + .crash_shutdown = pld_pcie_fw_sim_crash_shutdown, + .modem_status = pld_pcie_fw_sim_notify_handler, + .update_status = pld_pcie_fw_sim_uevent, +}; + +/** + * pld_pcie_fw_sim_register_driver() - Register PCIE device callback functions + * + * Return: int + */ +int pld_pcie_fw_sim_register_driver(void) +{ + return cnss_fw_sim_wlan_register_driver(&pld_pcie_fw_sim_ops); +} + +/** + * pld_pcie_fw_sim_unregister_driver() - Unregister PCIE device callback + * functions + * + * Return: void + */ +void pld_pcie_fw_sim_unregister_driver(void) +{ + cnss_fw_sim_wlan_unregister_driver(&pld_pcie_fw_sim_ops); +} + +/** + * pld_pcie_fw_sim_wlan_enable() - Enable WLAN + * @dev: device + * @config: WLAN configuration data + * @mode: WLAN mode + * @host_version: host software version + * + * This function enables WLAN FW. It passed WLAN configuration data, + * WLAN mode and host software version to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pcie_fw_sim_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, + const char *host_version) +{ + struct cnss_wlan_enable_cfg cfg; + enum cnss_driver_mode cnss_mode; + + cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg; + cfg.ce_tgt_cfg = (struct cnss_ce_tgt_pipe_cfg *) + config->ce_tgt_cfg; + cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg; + cfg.ce_svc_cfg = (struct cnss_ce_svc_pipe_cfg *) + config->ce_svc_cfg; + cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg; + cfg.shadow_reg_cfg = (struct cnss_shadow_reg_cfg *) + config->shadow_reg_cfg; + cfg.num_shadow_reg_v2_cfg = config->num_shadow_reg_v2_cfg; + cfg.shadow_reg_v2_cfg = (struct cnss_shadow_reg_v2_cfg *) + config->shadow_reg_v2_cfg; + cfg.rri_over_ddr_cfg_valid = config->rri_over_ddr_cfg_valid; + if (config->rri_over_ddr_cfg_valid) { + cfg.rri_over_ddr_cfg.base_addr_low = + config->rri_over_ddr_cfg.base_addr_low; + cfg.rri_over_ddr_cfg.base_addr_high = + config->rri_over_ddr_cfg.base_addr_high; + } + + switch (mode) { + case PLD_FTM: + cnss_mode = CNSS_FTM; + break; + case PLD_EPPING: + cnss_mode = CNSS_EPPING; + break; + default: + cnss_mode = CNSS_MISSION; + break; + } + return cnss_fw_sim_wlan_enable(dev, &cfg, cnss_mode, host_version); +} + +/** + * pld_pcie_fw_sim_wlan_disable() - Disable WLAN + * @dev: device + * @mode: WLAN mode + * + * This function disables WLAN FW. It passes WLAN mode to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pcie_fw_sim_wlan_disable(struct device *dev, enum pld_driver_mode mode) +{ + return cnss_fw_sim_wlan_disable(dev, CNSS_OFF); +} + +/** + * pld_pcie_fw_sim_get_soc_info() - Get SOC information + * @dev: device + * @info: buffer to SOC information + * + * Return SOC info to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pcie_fw_sim_get_soc_info(struct device *dev, struct pld_soc_info *info) +{ + int ret = 0; + struct cnss_soc_info cnss_info = {0}; + + if (!info) + return -ENODEV; + + ret = cnss_fw_sim_get_soc_info(dev, &cnss_info); + if (ret) + return ret; + + info->v_addr = cnss_info.va; + info->p_addr = cnss_info.pa; + info->chip_id = cnss_info.chip_id; + info->chip_family = cnss_info.chip_family; + info->board_id = cnss_info.board_id; + info->soc_id = cnss_info.soc_id; + info->fw_version = cnss_info.fw_version; + strlcpy(info->fw_build_timestamp, cnss_info.fw_build_timestamp, + sizeof(info->fw_build_timestamp)); + info->device_version.family_number = + cnss_info.device_version.family_number; + info->device_version.device_number = + cnss_info.device_version.device_number; + info->device_version.major_version = + cnss_info.device_version.major_version; + info->device_version.minor_version = + cnss_info.device_version.minor_version; + + return 0; +} + +/** + * pld_pcie_fw_sim_get_platform_cap() - Get platform capabilities + * @dev: device + * @cap: buffer to the capabilities + * + * Return capabilities to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_pcie_fw_sim_get_platform_cap(struct device *dev, + struct pld_platform_cap *cap) +{ + int ret = 0; + struct cnss_platform_cap cnss_cap; + + if (!cap) + return -ENODEV; + + ret = cnss_fw_sim_get_platform_cap(dev, &cnss_cap); + if (ret) + return ret; + + memcpy(cap, &cnss_cap, sizeof(*cap)); + return 0; +} + +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie_fw_sim.h b/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie_fw_sim.h new file mode 100644 index 0000000000000000000000000000000000000000..566e54db0950c6143567bea23446c7a08519eaa4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_pcie_fw_sim.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __PLD_PCIE_FW_SIM_H__ +#define __PLD_PCIE_FW_SIM_H__ + +#include "pld_internal.h" + +#ifndef CONFIG_PLD_PCIE_FW_SIM + +static inline int pld_pcie_fw_sim_register_driver(void) +{ + return 0; +} + +static inline void pld_pcie_fw_sim_unregister_driver(void) +{ +} + +static inline int pld_pcie_fw_sim_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *cfg, + enum pld_driver_mode mode, + const char *host_version) +{ + return 0; +} + +static inline int pld_pcie_fw_sim_wlan_disable(struct device *dev, + enum pld_driver_mode mode) +{ + return 0; +} + +static inline void pld_pcie_fw_sim_link_down(struct device *dev) +{ +} + +static inline int pld_pcie_fw_sim_is_fw_down(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_fw_sim_get_platform_cap(struct device *dev, + struct pld_platform_cap *cap) +{ + return 0; +} + +static inline int pld_pcie_fw_sim_get_soc_info(struct device *dev, + struct pld_soc_info *info) +{ + return 0; +} + +static inline int pld_pcie_fw_sim_get_user_msi_assignment(struct device *dev, + char *user_name, + int *num_vectors, + uint32_t *base_data, + uint32_t *base_vector) +{ + return -EINVAL; +} + +static inline int pld_pcie_fw_sim_get_msi_irq(struct device *dev, + unsigned int vector) +{ + return 0; +} + +static inline void pld_pcie_fw_sim_get_msi_address(struct device *dev, + uint32_t *msi_addr_low, + uint32_t *msi_addr_high) +{ +} + +static inline int pld_pcie_fw_sim_request_irq(struct device *dev, int irq, + irq_handler_t handler, + unsigned long irqflags, + const char *devname, + void *dev_data) +{ + return 0; +} + +static inline int pld_pcie_fw_sim_read_config_word(struct device *dev, + int offset, uint16_t *val) +{ + return 0; +} + +static inline int pld_pcie_fw_sim_free_irq(struct device *dev, + unsigned int ce_id, void *ctx) +{ + return 0; +} + +static inline void pld_pcie_fw_sim_enable_irq(struct device *dev, + unsigned int irq) +{ +} + +static inline void pld_pcie_fw_sim_disable_irq(struct device *dev, + unsigned int irq) +{ +} + +static inline int pld_pcie_fw_sim_idle_shutdown(struct device *dev) +{ + return 0; +} + +static inline int pld_pcie_fw_sim_idle_restart(struct device *dev) +{ + return 0; +} +#else +#include + +int pld_pcie_fw_sim_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, + const char *host_version); +int pld_pcie_fw_sim_wlan_disable(struct device *dev, enum pld_driver_mode mode); +int pld_pcie_fw_sim_register_driver(void); +void pld_pcie_fw_sim_unregister_driver(void); +int pld_pcie_fw_sim_get_platform_cap(struct device *dev, + struct pld_platform_cap *cap); +int pld_pcie_fw_sim_get_soc_info(struct device *dev, struct pld_soc_info *info); + +static inline int pld_pcie_fw_sim_get_user_msi_assignment(struct device *dev, + char *user_name, + int *num_vectors, + uint32_t *base_data, + uint32_t *base_vector) +{ + return cnss_fw_sim_get_user_msi_assignment(dev, user_name, num_vectors, + base_data, base_vector); +} + +static inline int pld_pcie_fw_sim_get_msi_irq(struct device *dev, + unsigned int vector) +{ + return cnss_fw_sim_get_msi_irq(dev, vector); +} + +static inline void pld_pcie_fw_sim_get_msi_address(struct device *dev, + uint32_t *msi_addr_low, + uint32_t *msi_addr_high) +{ + cnss_fw_sim_get_msi_address(dev, msi_addr_low, msi_addr_high); +} + +static inline int pld_pcie_fw_sim_request_irq(struct device *dev, int irq, + irq_handler_t handler, + unsigned long irqflags, + const char *devname, + void *dev_data) +{ + return cnss_fw_sim_request_irq(dev, irq, handler, + irqflags, devname, dev_data); +} + +static inline int pld_pcie_fw_sim_read_config_word(struct device *dev, + int offset, uint16_t *val) +{ + return cnss_fw_sim_read_config_word(dev, offset, val); +} + +static inline int pld_pcie_fw_sim_free_irq(struct device *dev, + int irq, void *dev_data) +{ + return cnss_fw_sim_free_irq(dev, irq, dev_data); +} + +static inline void pld_pcie_fw_sim_enable_irq(struct device *dev, int irq) +{ + cnss_fw_sim_enable_irq(dev, irq); +} + +static inline void pld_pcie_fw_sim_disable_irq(struct device *dev, int irq) +{ + cnss_fw_sim_disable_irq(dev, irq); +} + +static inline int pld_pcie_fw_sim_idle_shutdown(struct device *dev) +{ + return cnss_fw_sim_idle_shutdown(dev); +} + +static inline int pld_pcie_fw_sim_idle_restart(struct device *dev) +{ + return cnss_fw_sim_idle_restart(dev); +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_sdio.c b/drivers/staging/qcacld-3.0/core/pld/src/pld_sdio.c new file mode 100644 index 0000000000000000000000000000000000000000..7b8553d1e9bbae9130e5235d09d3d55314942190 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_sdio.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_PLD_SDIO_CNSS +#include +#endif +#ifdef CONFIG_PLD_SDIO_CNSS2 +#include +#endif + +#include "pld_common.h" +#include "pld_internal.h" +#include "pld_sdio.h" +#include "osif_psoc_sync.h" + +#ifdef CONFIG_SDIO +/* SDIO manufacturer ID and Codes */ +#define MANUFACTURER_ID_AR6320_BASE 0x500 +#define MANUFACTURER_ID_QCA9377_BASE 0x700 +#define MANUFACTURER_ID_QCA9379_BASE 0x800 +#define MANUFACTURER_CODE 0x271 + +#ifndef CONFIG_CNSS +static const struct pld_fw_files fw_files_qca6174_fw_1_1 = { + PREFIX "qwlan11.bin", PREFIX "bdwlan11.bin", PREFIX "otp11.bin", + PREFIX "utf11.bin", PREFIX "utfbd11.bin", PREFIX "qsetup11.bin", + PREFIX "epping11.bin", ""}; +static const struct pld_fw_files fw_files_qca6174_fw_2_0 = { + PREFIX "qwlan20.bin", PREFIX "bdwlan20.bin", PREFIX "otp20.bin", + PREFIX "utf20.bin", PREFIX "utfbd20.bin", PREFIX "qsetup20.bin", + PREFIX "epping20.bin", ""}; +static const struct pld_fw_files fw_files_qca6174_fw_1_3 = { + PREFIX "qwlan13.bin", PREFIX "bdwlan13.bin", PREFIX "otp13.bin", + PREFIX "utf13.bin", PREFIX "utfbd13.bin", PREFIX "qsetup13.bin", + PREFIX "epping13.bin", ""}; +static const struct pld_fw_files fw_files_qca6174_fw_3_0 = { + PREFIX "qwlan30.bin", PREFIX "bdwlan30.bin", PREFIX "otp30.bin", + PREFIX "utf30.bin", PREFIX "utfbd30.bin", PREFIX "qsetup30.bin", + PREFIX "epping30.bin", PREFIX "qwlan30i.bin"}; +static const struct pld_fw_files fw_files_default = { + PREFIX "qwlan.bin", PREFIX "bdwlan.bin", PREFIX "otp.bin", + PREFIX "utf.bin", PREFIX "utfbd.bin", PREFIX "qsetup.bin", + PREFIX "epping.bin", ""}; +#endif + +/** + * pld_sdio_probe() - Probe function for SDIO platform driver + * sdio_func: pointer to sdio device function + * @id: SDIO device ID table + * + * The probe function will be called when SDIO device provided + * in the ID table is detected. + * + * Return: int + */ +static int pld_sdio_probe(struct sdio_func *sdio_func, + const struct sdio_device_id *id) +{ + struct pld_context *pld_context; + struct device *dev; + int ret; + + pld_context = pld_get_global_context(); + if (!pld_context || !sdio_func) { + ret = -ENODEV; + goto out; + } + + dev = &sdio_func->dev; + ret = pld_add_dev(pld_context, dev, NULL, PLD_BUS_TYPE_SDIO); + if (ret) + goto out; + + return pld_context->ops->probe(dev, PLD_BUS_TYPE_SDIO, + sdio_func, (void *)id); + +out: + return ret; +} + + +/** + * pld_sdio_remove() - Remove function for SDIO device + * @sdio_func: pointer to sdio device function + * + * The remove function will be called when SDIO device is disconnected + * + * Return: void + */ +static void pld_sdio_remove(struct sdio_func *sdio_func) +{ + struct pld_context *pld_context; + struct device *dev = &sdio_func->dev; + int errno; + struct osif_psoc_sync *psoc_sync; + + errno = osif_psoc_sync_trans_start_wait(dev, &psoc_sync); + if (errno) + return; + + osif_psoc_sync_unregister(dev); + osif_psoc_sync_wait_for_ops(psoc_sync); + + pld_context = pld_get_global_context(); + + if (!pld_context) + goto out; + + pld_context->ops->remove(dev, PLD_BUS_TYPE_SDIO); + pld_del_dev(pld_context, dev); + +out: + osif_psoc_sync_trans_stop(psoc_sync); + osif_psoc_sync_destroy(psoc_sync); +} + +#ifdef CONFIG_PLD_SDIO_CNSS +/** + * pld_sdio_reinit() - SSR re-initialize function for SDIO device + * @sdio_func: pointer to sdio device function + * @id: SDIO device ID + * + * During subsystem restart(SSR), this function will be called to + * re-initialize SDIO device. + * + * Return: int + */ +static int pld_sdio_reinit(struct sdio_func *sdio_func, + const struct sdio_device_id *id) +{ + struct pld_context *pld_context; + struct device *dev = &sdio_func->dev; + + pld_context = pld_get_global_context(); + if (pld_context->ops->reinit) + return pld_context->ops->reinit(dev, PLD_BUS_TYPE_SDIO, + sdio_func, (void *)id); + + return -ENODEV; +} + +/** + * pld_sdio_shutdown() - SSR shutdown function for SDIO device + * @sdio_func: pointer to sdio device function + * + * During SSR, this function will be called to shutdown SDIO device. + * + * Return: void + */ +static void pld_sdio_shutdown(struct sdio_func *sdio_func) +{ + struct pld_context *pld_context; + struct device *dev = &sdio_func->dev; + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + pld_context->ops->shutdown(dev, PLD_BUS_TYPE_SDIO); +} + +/** + * pld_sdio_crash_shutdown() - Crash shutdown function for SDIO device + * @sdio_func: pointer to sdio device function + * + * This function will be called when a crash is detected, it will shutdown + * the SDIO device. + * + * Return: void + */ +static void pld_sdio_crash_shutdown(struct sdio_func *sdio_func) +{ + struct pld_context *pld_context; + struct device *dev = &sdio_func->dev; + + pld_context = pld_get_global_context(); + if (pld_context->ops->crash_shutdown) + pld_context->ops->crash_shutdown(dev, PLD_BUS_TYPE_SDIO); +} + +#endif + +#ifdef CONFIG_PM +/** + * pld_sdio_suspend() - Suspend callback function for power management + * @dev: SDIO device + * + * This function is to suspend the SDIO device when power management is + * enabled. + * + * Return: void + */ +static int pld_sdio_suspend(struct device *dev) +{ + struct pld_context *pld_context; + pm_message_t state = { .event = PM_EVENT_SUSPEND }; + + pld_context = pld_get_global_context(); + return pld_context->ops->suspend(dev, + PLD_BUS_TYPE_SDIO, state); +} + +/** + * pld_sdio_resume() - Resume callback function for power management + * @dev: SDIO device + * + * This function is to resume the SDIO device when power management is + * enabled. + * + * Return: void + */ +static int pld_sdio_resume(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + return pld_context->ops->resume(dev, PLD_BUS_TYPE_SDIO); +} +#endif + +static struct sdio_device_id pld_sdio_id_table[] = { + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x0))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x1))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x2))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x3))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x4))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x5))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x6))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x7))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x8))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x9))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xA))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xB))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xC))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xD))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xE))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xF))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x0))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x1))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x2))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x3))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x4))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x5))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x6))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x7))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x8))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x9))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xA))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xB))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xC))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xD))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xE))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xF))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x0))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x1))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x2))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x3))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x4))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x5))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x6))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x7))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x8))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x9))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xA))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xB))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xC))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xD))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xE))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xF))}, + {}, +}; + +#ifdef CONFIG_PLD_SDIO_CNSS2 +/** + * pld_sdio_reinit() - SSR re-initialize function for SDIO device + * @sdio_func: pointer to sdio device function + * @id: SDIO device ID + * + * During subsystem restart(SSR), this function will be called to + * re-initialize SDIO device. + * + * Return: int + */ +static int pld_sdio_reinit(struct sdio_func *sdio_func, + const struct sdio_device_id *id) +{ + /* TODO */ + return -ENODEV; +} + +/** + * pld_sdio_shutdown() - SSR shutdown function for SDIO device + * @sdio_func: pointer to sdio device function + * + * During SSR, this function will be called to shutdown SDIO device. + * + * Return: void + */ +static void pld_sdio_shutdown(struct sdio_func *sdio_func) +{ + /* TODO */ +} + +/** + * pld_sdio_crash_shutdown() - Crash shutdown function for SDIO device + * @sdio_func: pointer to sdio device function + * + * This function will be called when a crash is detected, it will shutdown + * the SDIO device. + * + * Return: void + */ +static void pld_sdio_crash_shutdown(struct sdio_func *sdio_func) +{ + /* TODO */ +} + +static void pld_sdio_uevent(struct sdio_func *sdio_func, uint32_t status) +{ + struct pld_context *pld_context; + struct device *dev = &sdio_func->dev; + struct pld_uevent_data data = {0}; + + pld_context = pld_get_global_context(); + + if (!pld_context) + return; + + switch (status) { + case CNSS_RECOVERY: + data.uevent = PLD_FW_RECOVERY_START; + break; + case CNSS_FW_DOWN: + data.uevent = PLD_FW_DOWN; + break; + default: + goto out; + } + + if (pld_context->ops->uevent) + pld_context->ops->uevent(dev, &data); +out: + return; +} + +struct cnss_sdio_wlan_driver pld_sdio_ops = { + .name = "pld_sdio", + .id_table = pld_sdio_id_table, + .probe = pld_sdio_probe, + .remove = pld_sdio_remove, + .reinit = pld_sdio_reinit, + .shutdown = pld_sdio_shutdown, + .crash_shutdown = pld_sdio_crash_shutdown, + .update_status = pld_sdio_uevent, +#ifdef CONFIG_PM + .suspend = pld_sdio_suspend, + .resume = pld_sdio_resume, +#endif +}; + +/** + * pld_sdio_register_driver() - Register SDIO device callback functions + * + * Return: int + */ +int pld_sdio_register_driver(void) +{ + return cnss_sdio_wlan_register_driver(&pld_sdio_ops); +} + +/** + * pld_sdio_unregister_driver() - Unregister SDIO device callback functions + * + * Return: void + */ +void pld_sdio_unregister_driver(void) +{ + cnss_sdio_wlan_unregister_driver(&pld_sdio_ops); +} + +/** + * pld_sdio_wlan_enable() - Enable WLAN + * @dev: device + * @config: NA + * @mode: WLAN mode + * @host_version: host software version + * + * This function enables WLAN FW. It passed + * WLAN mode and host software version to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_sdio_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version) +{ + struct cnss_wlan_enable_cfg cfg; + enum cnss_driver_mode cnss_mode; + + switch (mode) { + case PLD_FTM: + cnss_mode = CNSS_FTM; + break; + case PLD_EPPING: + cnss_mode = CNSS_EPPING; + break; + default: + cnss_mode = CNSS_MISSION; + break; + } + return cnss_wlan_enable(dev, &cfg, cnss_mode, host_version); +} + +#else + +#ifdef CONFIG_PM +static const struct dev_pm_ops pld_device_pm_ops = { + .suspend = pld_sdio_suspend, + .resume = pld_sdio_resume, +}; +#endif + +struct sdio_driver pld_sdio_ops = { + .name = "pld_sdio", + .id_table = pld_sdio_id_table, + .probe = pld_sdio_probe, + .remove = pld_sdio_remove, +#if defined(CONFIG_PM) + .drv = { + .pm = &pld_device_pm_ops, + } +#endif +}; + +int pld_sdio_register_driver(void) +{ + return sdio_register_driver(&pld_sdio_ops); +} + +void pld_sdio_unregister_driver(void) +{ + sdio_unregister_driver(&pld_sdio_ops); +} +#endif + +#ifdef CONFIG_PLD_SDIO_CNSS +#ifdef CONFIG_TUFELLO_DUAL_FW_SUPPORT +static inline int pld_sdio_is_tufello_dual_fw_supported(void) +{ + return 1; +} +#else +static inline int pld_sdio_is_tufello_dual_fw_supported(void) +{ + return 0; +#endif +} + +/** + * pld_sdio_get_fw_files_for_target() - Get FW file names + * @pfw_files: buffer for FW file names + * @target_type: target type + * @target_version: target version + * + * Return target specific FW file names to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_sdio_get_fw_files_for_target(struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version) +{ + int ret = 0; + struct cnss_fw_files cnss_fw_files; + + if (!pfw_files) + return -ENODEV; + + memset(pfw_files, 0, sizeof(*pfw_files)); + + if (target_version == PLD_QCA9377_REV1_1_VERSION) { + cnss_get_qca9377_fw_files(&cnss_fw_files, PLD_MAX_FILE_NAME, + pld_sdio_is_tufello_dual_fw_supported()); + } else { + ret = cnss_get_fw_files_for_target(&cnss_fw_files, + target_type, target_version); + } + if (0 != ret) + return ret; + + snprintf(pfw_files->image_file, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.image_file); + snprintf(pfw_files->board_data, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.board_data); + snprintf(pfw_files->otp_data, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.otp_data); + snprintf(pfw_files->utf_file, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.utf_file); + snprintf(pfw_files->utf_board_data, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.utf_board_data); + snprintf(pfw_files->epping_file, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.epping_file); + snprintf(pfw_files->evicted_data, PLD_MAX_FILE_NAME, PREFIX "%s", + cnss_fw_files.evicted_data); + + return ret; +} +#else +#ifdef CONFIG_TUFELLO_DUAL_FW_SUPPORT +static inline void get_qca9377_fw_files(struct pld_fw_files *pfw_files, + u32 size) +{ + memcpy(pfw_files, &fw_files_default, sizeof(*pfw_files)); +} +#else +static inline void get_qca9377_fw_files(struct pld_fw_files *pfw_files, + u32 size) +{ + memcpy(pfw_files, &fw_files_qca6174_fw_3_0, sizeof(*pfw_files)); +} +#endif + +int pld_sdio_get_fw_files_for_target(struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version) +{ + if (!pfw_files) + return -ENODEV; + + switch (target_version) { + case PLD_AR6320_REV1_VERSION: + case PLD_AR6320_REV1_1_VERSION: + memcpy(pfw_files, &fw_files_qca6174_fw_1_1, sizeof(*pfw_files)); + break; + case PLD_AR6320_REV1_3_VERSION: + memcpy(pfw_files, &fw_files_qca6174_fw_1_3, sizeof(*pfw_files)); + break; + case PLD_AR6320_REV2_1_VERSION: + memcpy(pfw_files, &fw_files_qca6174_fw_2_0, sizeof(*pfw_files)); + break; + case PLD_AR6320_REV3_VERSION: + case PLD_AR6320_REV3_2_VERSION: + memcpy(pfw_files, &fw_files_qca6174_fw_3_0, sizeof(*pfw_files)); + break; + case PLD_QCA9377_REV1_1_VERSION: + case PLD_QCA9379_REV1_VERSION: + get_qca9377_fw_files(pfw_files, sizeof(*pfw_files)); + break; + default: + memcpy(pfw_files, &fw_files_default, sizeof(*pfw_files)); + pr_err("%s version mismatch 0x%X ", + __func__, target_version); + break; + } + + return 0; +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_sdio.h b/drivers/staging/qcacld-3.0/core/pld/src/pld_sdio.h new file mode 100644 index 0000000000000000000000000000000000000000..66bd846f0359f3730b94cdfb311e62af118d6eaf --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_sdio.h @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __PLD_SDIO_H__ +#define __PLD_SDIO_H__ + +#ifdef CONFIG_PLD_SDIO_CNSS +#include +#endif +#include "pld_common.h" + +#ifdef DYNAMIC_SINGLE_CHIP +#define PREFIX DYNAMIC_SINGLE_CHIP "/" +#else + +#ifdef MULTI_IF_NAME +#define PREFIX MULTI_IF_NAME "/" +#else +#define PREFIX "" +#endif + +#endif + +#define PLD_QCA9377_REV1_1_VERSION 0x5020001 +#define PLD_QCA9379_REV1_VERSION 0x5040000 + +#ifndef CONFIG_CNSS +#define PLD_AR6004_VERSION_REV1_3 0x31c8088a +#define PLD_AR9888_REV2_VERSION 0x4100016c +#define PLD_AR6320_REV1_VERSION 0x5000000 +#define PLD_AR6320_REV1_1_VERSION 0x5000001 +#define PLD_AR6320_REV1_3_VERSION 0x5000003 +#define PLD_AR6320_REV2_1_VERSION 0x5010000 +#define PLD_AR6320_REV3_VERSION 0x5020000 +#define PLD_AR6320_REV3_2_VERSION 0x5030000 +#define PLD_AR6320_DEV_VERSION 0x1000000 + + +#endif + +#ifndef CONFIG_SDIO +static inline int pld_sdio_register_driver(void) +{ + return 0; +} + +static inline void pld_sdio_unregister_driver(void) +{ +} + +static inline +int pld_sdio_get_fw_files_for_target(struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version) +{ + return 0; +} +static inline uint8_t *pld_sdio_get_wlan_mac_address(struct device *dev, + uint32_t *num) +{ + *num = 0; + return NULL; +} +#else +int pld_sdio_register_driver(void); +void pld_sdio_unregister_driver(void); +int pld_sdio_get_fw_files_for_target(struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version); +#ifdef CONFIG_CNSS +static inline uint8_t *pld_sdio_get_wlan_mac_address(struct device *dev, + uint32_t *num) +{ + return cnss_common_get_wlan_mac_address(dev, num); +} +#else +static inline uint8_t *pld_sdio_get_wlan_mac_address(struct device *dev, + uint32_t *num) +{ + *num = 0; + return NULL; +} +#endif +#endif + +#ifdef CONFIG_PLD_SDIO_CNSS +static inline void *pld_sdio_get_virt_ramdump_mem(struct device *dev, + unsigned long *size) +{ + return cnss_common_get_virt_ramdump_mem(dev, size); +} + +static inline void pld_sdio_release_virt_ramdump_mem(void *address) +{ +} + +static inline void pld_sdio_device_crashed(struct device *dev) +{ + cnss_common_device_crashed(dev); +} +static inline bool pld_sdio_is_fw_dump_skipped(void) +{ + return cnss_get_restart_level() == CNSS_RESET_SUBSYS_COUPLED; +} + +static inline void pld_sdio_device_self_recovery(struct device *dev) +{ + cnss_common_device_self_recovery(dev); +} + +static inline bool pld_sdio_platform_driver_support(void) +{ + return true; +} +#else +static inline void *pld_sdio_get_virt_ramdump_mem(struct device *dev, + unsigned long *size) +{ + size_t length = 0; + int flags = GFP_KERNEL; + + length = TOTAL_DUMP_SIZE; + + if (!size) + return NULL; + + *size = (unsigned long)length; + + if (in_interrupt() || irqs_disabled() || in_atomic()) + flags = GFP_ATOMIC; + + return kzalloc(length, flags); +} + +static inline void pld_sdio_release_virt_ramdump_mem(void *address) +{ + kfree(address); +} + +static inline void pld_sdio_device_crashed(struct device *dev) +{ +} +static inline bool pld_sdio_is_fw_dump_skipped(void) +{ + return false; +} + +static inline void pld_sdio_device_self_recovery(struct device *dev) +{ +} + +static inline bool pld_sdio_platform_driver_support(void) +{ + return false; +} +#endif + +#ifdef CONFIG_PLD_SDIO_CNSS +/** + * pld_hif_sdio_get_virt_ramdump_mem() - Get virtual ramdump memory + * @dev: device + * @size: buffer to virtual memory size + * + * Return: virtual ramdump memory address + */ +static inline void *pld_hif_sdio_get_virt_ramdump_mem(struct device *dev, + unsigned long *size) +{ + return cnss_common_get_virt_ramdump_mem(dev, size); +} + +/** + * pld_hif_sdio_release_ramdump_mem() - Release virtual ramdump memory + * @address: virtual ramdump memory address + * + * Return: void + */ +static inline void pld_hif_sdio_release_ramdump_mem(unsigned long *address) +{ +} +#else +#ifdef CONFIG_PLD_SDIO_CNSS2 +#include + +/** + * pld_sdio_get_sdio_al_client_handle() - Get the sdio al client handle + * @func: SDIO function pointer + * + * Return: On success return client handle from al via cnss, else NULL + */ +static inline struct sdio_al_client_handle *pld_sdio_get_sdio_al_client_handle +( +struct sdio_func *func +) +{ + if (!func) + return NULL; + + return cnss_sdio_wlan_get_sdio_al_client_handle(func); +} + +/** + * pld_sdio_register_sdio_al_channel() - Register channel with sdio al + * @al_client: SDIO al client handle + * @ch_data: SDIO client channel data + * + * Return: Channel handle on success, else null + */ +static inline struct sdio_al_channel_handle *pld_sdio_register_sdio_al_channel +( +struct sdio_al_client_handle *al_client, +struct sdio_al_channel_data *ch_data +) +{ + if (!al_client || !ch_data) + return NULL; + + return cnss_sdio_wlan_register_sdio_al_channel(ch_data); +} + +/** + * pld_sdio_unregister_sdio_al_channel() - Unregister the sdio al channel + * @ch_handle: SDIO al channel handle + * + * Return: None + */ +static inline void pld_sdio_unregister_sdio_al_channel +( +struct sdio_al_channel_handle *ch_handle +) +{ + cnss_sdio_wlan_unregister_sdio_al_channel(ch_handle); +} + +int pld_sdio_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version); +#else +static inline int pld_sdio_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, + const char *host_version) +{ + return 0; +} +#endif /* CONFIG_PLD_SDIO_CNSS2 */ + +/** + * pld_hif_sdio_get_virt_ramdump_mem() - Get virtual ramdump memory + * @dev: device + * @size: buffer to virtual memory size + * + * Return: virtual ramdump memory address + */ +static inline void *pld_hif_sdio_get_virt_ramdump_mem(struct device *dev, + unsigned long *size) +{ + size_t length = 0; + int flags = GFP_KERNEL; + + length = TOTAL_DUMP_SIZE; + + if (size) + *size = (unsigned long)length; + + if (in_interrupt() || irqs_disabled() || in_atomic()) + flags = GFP_ATOMIC; + + return kzalloc(length, flags); +} + +/** + * pld_hif_sdio_release_ramdump_mem() - Release virtual ramdump memory + * @address: virtual ramdump memory address + * + * Return: void + */ +static inline void pld_hif_sdio_release_ramdump_mem(unsigned long *address) +{ + if (address) + kfree(address); +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc.c b/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc.c new file mode 100644 index 0000000000000000000000000000000000000000..a5e7c8c804865a55f4f4522991d4b285ef4bfd1a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc.c @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#ifdef CONFIG_PLD_SNOC_ICNSS +#include +#endif + +#include "pld_internal.h" +#include "pld_snoc.h" +#include "osif_psoc_sync.h" + +#ifdef CONFIG_PLD_SNOC_ICNSS +/** + * + *pld_snoc_idle_restart_cb() - Perform idle restart + * @pdev: platform device + * + *This function will be called if there is an idle restart request + * + * Return: int + **/ +static int pld_snoc_idle_restart_cb(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->idle_restart) + return pld_context->ops->idle_restart(dev, PLD_BUS_TYPE_SNOC); + + return -ENODEV; +} + +/** + * pld_snoc_idle_shutdown_cb() - Perform idle shutdown + * @pdev: PCIE device + * @id: PCIE device ID + * + * This function will be called if there is an idle shutdown request + * + * Return: int + */ +static int pld_snoc_idle_shutdown_cb(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + return pld_context->ops->idle_shutdown(dev, PLD_BUS_TYPE_SNOC); + + return -ENODEV; +} + +/** + * pld_snoc_probe() - Probe function for platform driver + * @dev: device + * + * The probe function will be called when platform device + * is detected. + * + * Return: int + */ +static int pld_snoc_probe(struct device *dev) +{ + struct pld_context *pld_context; + int ret = 0; + + pld_context = pld_get_global_context(); + if (!pld_context) { + ret = -ENODEV; + goto out; + } + + ret = pld_add_dev(pld_context, dev, NULL, PLD_BUS_TYPE_SNOC); + if (ret) + goto out; + + return pld_context->ops->probe(dev, PLD_BUS_TYPE_SNOC, + NULL, NULL); + +out: + return ret; +} + +/** + * pld_snoc_remove() - Remove function for platform device + * @dev: device + * + * The remove function will be called when platform device + * is disconnected + * + * Return: void + */ +static void pld_snoc_remove(struct device *dev) +{ + struct pld_context *pld_context; + int errno; + struct osif_psoc_sync *psoc_sync; + + errno = osif_psoc_sync_trans_start_wait(dev, &psoc_sync); + if (errno) + return; + + osif_psoc_sync_unregister(dev); + osif_psoc_sync_wait_for_ops(psoc_sync); + + pld_context = pld_get_global_context(); + + if (!pld_context) + goto out; + + pld_context->ops->remove(dev, PLD_BUS_TYPE_SNOC); + + pld_del_dev(pld_context, dev); + +out: + osif_psoc_sync_trans_stop(psoc_sync); + osif_psoc_sync_destroy(psoc_sync); +} + +/** + * pld_snoc_reinit() - SSR re-initialize function for platform device + * @dev: device + * + * During subsystem restart(SSR), this function will be called to + * re-initialize platform device. + * + * Return: int + */ +static int pld_snoc_reinit(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->reinit) + return pld_context->ops->reinit(dev, PLD_BUS_TYPE_SNOC, + NULL, NULL); + + return -ENODEV; +} + +/** + * pld_snoc_shutdown() - SSR shutdown function for platform device + * @dev: device + * + * During SSR, this function will be called to shutdown platform device. + * + * Return: void + */ +static void pld_snoc_shutdown(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + pld_context->ops->shutdown(dev, PLD_BUS_TYPE_SNOC); +} + +/** + * pld_snoc_crash_shutdown() - Crash shutdown function for platform device + * @dev: device + * + * This function will be called when a crash is detected, it will shutdown + * platform device. + * + * Return: void + */ +static void pld_snoc_crash_shutdown(void *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->crash_shutdown) + pld_context->ops->crash_shutdown(dev, PLD_BUS_TYPE_SNOC); +} + +/** + * pld_snoc_pm_suspend() - PM suspend callback function for power management + * @dev: device + * + * This function is to suspend the platform device when power management + * is enabled. + * + * Return: void + */ +static int pld_snoc_pm_suspend(struct device *dev) +{ + struct pld_context *pld_context; + pm_message_t state; + + state.event = PM_EVENT_SUSPEND; + pld_context = pld_get_global_context(); + return pld_context->ops->suspend(dev, PLD_BUS_TYPE_SNOC, state); +} + +/** + * pld_snoc_pm_resume() - PM resume callback function for power management + * @pdev: device + * + * This function is to resume the platform device when power management + * is enabled. + * + * Return: void + */ +static int pld_snoc_pm_resume(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + return pld_context->ops->resume(dev, PLD_BUS_TYPE_SNOC); +} + +/** + * pld_snoc_suspend_noirq() - Complete the actions started by suspend() + * @dev: device + * + * Complete the actions started by suspend(). Carry out any + * additional operations required for suspending the device that might be + * racing with its driver's interrupt handler, which is guaranteed not to + * run while suspend_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_snoc_suspend_noirq(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->suspend_noirq) + return pld_context->ops->suspend_noirq(dev, PLD_BUS_TYPE_SNOC); + return 0; +} + +/** + * pld_snoc_resume_noirq() - Prepare for the execution of resume() + * @pdev: device + * + * Prepare for the execution of resume() by carrying out any + * operations required for resuming the device that might be racing with + * its driver's interrupt handler, which is guaranteed not to run while + * resume_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_snoc_resume_noirq(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->resume_noirq) + return pld_context->ops->resume_noirq(dev, PLD_BUS_TYPE_SNOC); + + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static int pld_update_hang_evt_data(struct icnss_uevent_hang_data *evt_data, + struct pld_uevent_data *data) +{ + if (!evt_data || !data) + return -EINVAL; + + data->hang_data.hang_event_data = evt_data->hang_event_data; + data->hang_data.hang_event_data_len = evt_data->hang_event_data_len; + return 0; +} + +static int pld_snoc_uevent(struct device *dev, + struct icnss_uevent_data *uevent) +{ + struct pld_context *pld_context; + struct icnss_uevent_fw_down_data *fw_down_data = NULL; + struct icnss_uevent_hang_data *hang_data = NULL; + struct pld_uevent_data data = {0}; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (!pld_context->ops->uevent) + goto out; + + if (!uevent) + return -EINVAL; + + switch (uevent->uevent) { + case ICNSS_UEVENT_FW_CRASHED: + data.uevent = PLD_FW_CRASHED; + break; + case ICNSS_UEVENT_FW_DOWN: + if (!uevent->data) + return -EINVAL; + fw_down_data = (struct icnss_uevent_fw_down_data *)uevent->data; + data.uevent = PLD_FW_DOWN; + data.fw_down.crashed = fw_down_data->crashed; + break; + case ICNSS_UEVENT_HANG_DATA: + if (!uevent->data) + return -EINVAL; + hang_data = (struct icnss_uevent_hang_data *)uevent->data; + data.uevent = PLD_FW_HANG_EVENT; + pld_update_hang_evt_data(hang_data, &data); + break; + default: + goto out; + } + + pld_context->ops->uevent(dev, &data); +out: + return 0; +} +#else +static int pld_snoc_uevent(struct device *dev, + struct icnss_uevent_data *uevent) +{ + struct pld_context *pld_context; + struct icnss_uevent_fw_down_data *fw_down_data = NULL; + struct pld_uevent_data data = {0}; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (!pld_context->ops->uevent) + goto out; + + if (!uevent) + return -EINVAL; + + switch (uevent->uevent) { + case ICNSS_UEVENT_FW_CRASHED: + data.uevent = PLD_FW_CRASHED; + break; + case ICNSS_UEVENT_FW_DOWN: + if (!uevent->data) + return -EINVAL; + fw_down_data = (struct icnss_uevent_fw_down_data *)uevent->data; + data.uevent = PLD_FW_DOWN; + data.fw_down.crashed = fw_down_data->crashed; + break; + default: + goto out; + } + + pld_context->ops->uevent(dev, &data); +out: + return 0; +} +#endif + +#ifdef MULTI_IF_NAME +#define PLD_SNOC_OPS_NAME "pld_snoc_" MULTI_IF_NAME +#else +#define PLD_SNOC_OPS_NAME "pld_snoc" +#endif + +struct icnss_driver_ops pld_snoc_ops = { + .name = PLD_SNOC_OPS_NAME, + .probe = pld_snoc_probe, + .remove = pld_snoc_remove, + .shutdown = pld_snoc_shutdown, + .reinit = pld_snoc_reinit, + .crash_shutdown = pld_snoc_crash_shutdown, + .pm_suspend = pld_snoc_pm_suspend, + .pm_resume = pld_snoc_pm_resume, + .suspend_noirq = pld_snoc_suspend_noirq, + .resume_noirq = pld_snoc_resume_noirq, + .uevent = pld_snoc_uevent, + .idle_restart = pld_snoc_idle_restart_cb, + .idle_shutdown = pld_snoc_idle_shutdown_cb, +}; + +/** + * pld_snoc_register_driver() - Register platform device callback functions + * + * Return: int + */ +int pld_snoc_register_driver(void) +{ + return icnss_register_driver(&pld_snoc_ops); +} + +/** + * pld_snoc_unregister_driver() - Unregister platform device callback functions + * + * Return: void + */ +void pld_snoc_unregister_driver(void) +{ + icnss_unregister_driver(&pld_snoc_ops); +} + +/** + * pld_snoc_wlan_enable() - Enable WLAN + * @dev: device + * @config: WLAN configuration data + * @mode: WLAN mode + * @host_version: host software version + * + * This function enables WLAN FW. It passed WLAN configuration data, + * WLAN mode and host software version to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ + +int pld_snoc_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version) +{ + struct icnss_wlan_enable_cfg cfg; + enum icnss_driver_mode icnss_mode; + + if (!dev) + return -ENODEV; + + cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg; + cfg.ce_tgt_cfg = (struct ce_tgt_pipe_cfg *) + config->ce_tgt_cfg; + cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg; + cfg.ce_svc_cfg = (struct ce_svc_pipe_cfg *) + config->ce_svc_cfg; + cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg; + cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *) + config->shadow_reg_cfg; + + switch (mode) { + case PLD_FTM: + icnss_mode = ICNSS_FTM; + break; + case PLD_EPPING: + icnss_mode = ICNSS_EPPING; + break; + default: + icnss_mode = ICNSS_MISSION; + break; + } + + return icnss_wlan_enable(dev, &cfg, icnss_mode, host_version); +} + +/** + * pld_snoc_wlan_disable() - Disable WLAN + * @dev: device + * @mode: WLAN mode + * + * This function disables WLAN FW. It passes WLAN mode to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_snoc_wlan_disable(struct device *dev, enum pld_driver_mode mode) +{ + if (!dev) + return -ENODEV; + + return icnss_wlan_disable(dev, ICNSS_OFF); +} + +/** + * pld_snoc_get_soc_info() - Get SOC information + * @dev: device + * @info: buffer to SOC information + * + * Return SOC info to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_snoc_get_soc_info(struct device *dev, struct pld_soc_info *info) +{ + int errno; + struct icnss_soc_info icnss_info = {0}; + + if (!info || !dev) + return -ENODEV; + + errno = icnss_get_soc_info(dev, &icnss_info); + if (errno) + return errno; + + info->v_addr = icnss_info.v_addr; + info->p_addr = icnss_info.p_addr; + info->chip_id = icnss_info.chip_id; + info->chip_family = icnss_info.chip_family; + info->board_id = icnss_info.board_id; + info->soc_id = icnss_info.soc_id; + info->fw_version = icnss_info.fw_version; + strlcpy(info->fw_build_timestamp, icnss_info.fw_build_timestamp, + sizeof(info->fw_build_timestamp)); + + return 0; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc.h b/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc.h new file mode 100644 index 0000000000000000000000000000000000000000..ab4221967e66b863873f8995ccb8eee8fcd3e5e9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc.h @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __PLD_SNOC_H__ +#define __PLD_SNOC_H__ + +#ifdef CONFIG_PLD_SNOC_ICNSS +#include +#endif +#include "pld_internal.h" + +#ifndef CONFIG_PLD_SNOC_ICNSS +static inline int pld_snoc_register_driver(void) +{ + return 0; +} + +static inline void pld_snoc_unregister_driver(void) +{ +} +static inline int pld_snoc_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version) +{ + return 0; +} +static inline int pld_snoc_wlan_disable(struct device *dev, + enum pld_driver_mode mode) +{ + return 0; +} +static inline int pld_snoc_ce_request_irq(struct device *dev, + unsigned int ce_id, + irqreturn_t (*handler)(int, void *), + unsigned long flags, + const char *name, void *ctx) +{ + return 0; +} +static inline int pld_snoc_ce_free_irq(struct device *dev, + unsigned int ce_id, void *ctx) +{ + return 0; +} +static inline void pld_snoc_enable_irq(struct device *dev, unsigned int ce_id) +{ +} +static inline void pld_snoc_disable_irq(struct device *dev, unsigned int ce_id) +{ +} +static inline int pld_snoc_get_soc_info(struct device *dev, struct pld_soc_info *info) +{ + return 0; +} +static inline int pld_snoc_get_ce_id(struct device *dev, int irq) +{ + return 0; +} +static inline int pld_snoc_power_on(struct device *dev) +{ + return 0; +} +static inline int pld_snoc_power_off(struct device *dev) +{ + return 0; +} +static inline int pld_snoc_get_irq(struct device *dev, int ce_id) +{ + return 0; +} +static inline int pld_snoc_athdiag_read(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *output) +{ + return 0; +} +static inline int pld_snoc_athdiag_write(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *input) +{ + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static inline void *pld_snoc_smmu_get_domain(struct device *dev) +{ + return NULL; +} + +#else +static inline void *pld_snoc_smmu_get_mapping(struct device *dev) +{ + return NULL; +} +#endif + +static inline int pld_snoc_smmu_map(struct device *dev, phys_addr_t paddr, + uint32_t *iova_addr, size_t size) +{ + return 0; +} + +static inline int pld_snoc_smmu_unmap(struct device *dev, + uint32_t iova_addr, size_t size) +{ + return 0; +} + +static inline +unsigned int pld_snoc_socinfo_get_serial_number(struct device *dev) +{ + return 0; +} +static inline int pld_snoc_is_qmi_disable(struct device *dev) +{ + return 0; +} + +static inline int pld_snoc_is_fw_down(struct device *dev) +{ + return 0; +} +static inline int pld_snoc_set_fw_log_mode(struct device *dev, u8 fw_log_mode) +{ + return 0; +} +static inline int pld_snoc_force_assert_target(struct device *dev) +{ + return 0; +} + +static inline int pld_snoc_is_pdr(void) +{ + return 0; +} + +static inline int pld_snoc_is_fw_rejuvenate(void) +{ + return 0; +} + +static inline void pld_snoc_block_shutdown(bool status) +{ +} + +#ifdef FEATURE_WLAN_TIME_SYNC_FTM +static inline int +pld_snoc_get_audio_wlan_timestamp(struct device *dev, + enum pld_wlan_time_sync_trigger_type type, + uint64_t *ts) +{ + return 0; +} +#endif /* FEATURE_WLAN_TIME_SYNC_FTM */ + +static inline int pld_snoc_idle_restart(struct device *dev) +{ + return 0; +} + +static inline int pld_snoc_idle_shutdown(struct device *dev) +{ + return 0; +} + +#else +int pld_snoc_register_driver(void); +void pld_snoc_unregister_driver(void); +int pld_snoc_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version); +int pld_snoc_wlan_disable(struct device *dev, enum pld_driver_mode mode); +int pld_snoc_get_soc_info(struct device *dev, struct pld_soc_info *info); + +#ifdef FEATURE_WLAN_TIME_SYNC_FTM +/** + * pld_snoc_get_audio_wlan_timestamp() - Get audio timestamp + * @dev: device + * @type: trigger type + * @ts: timestamp + * + * Return audio timestamp to the ts. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static inline int +pld_snoc_get_audio_wlan_timestamp(struct device *dev, + enum pld_wlan_time_sync_trigger_type type, + uint64_t *ts) +{ + if (!dev) + return -ENODEV; + + return 0; +} +#endif /* FEATURE_WLAN_TIME_SYNC_FTM */ +static inline int pld_snoc_ce_request_irq(struct device *dev, + unsigned int ce_id, + irqreturn_t (*handler)(int, void *), + unsigned long flags, + const char *name, void *ctx) +{ + if (!dev) + return -ENODEV; + + return icnss_ce_request_irq(dev, ce_id, handler, flags, name, ctx); +} + +static inline int pld_snoc_ce_free_irq(struct device *dev, + unsigned int ce_id, void *ctx) +{ + if (!dev) + return -ENODEV; + + return icnss_ce_free_irq(dev, ce_id, ctx); +} + +static inline void pld_snoc_enable_irq(struct device *dev, unsigned int ce_id) +{ + if (dev) + icnss_enable_irq(dev, ce_id); +} + +static inline void pld_snoc_disable_irq(struct device *dev, unsigned int ce_id) +{ + if (dev) + icnss_disable_irq(dev, ce_id); +} + +static inline int pld_snoc_get_ce_id(struct device *dev, int irq) +{ + if (!dev) + return -ENODEV; + + return icnss_get_ce_id(dev, irq); +} + +static inline int pld_snoc_get_irq(struct device *dev, int ce_id) +{ + if (!dev) + return -ENODEV; + + return icnss_get_irq(dev, ce_id); +} + +static inline int pld_snoc_power_on(struct device *dev) +{ + return icnss_power_on(dev); +} +static inline int pld_snoc_power_off(struct device *dev) +{ + return icnss_power_off(dev); +} +static inline int pld_snoc_athdiag_read(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *output) +{ + return icnss_athdiag_read(dev, offset, memtype, datalen, output); +} +static inline int pld_snoc_athdiag_write(struct device *dev, uint32_t offset, + uint32_t memtype, uint32_t datalen, + uint8_t *input) +{ + return icnss_athdiag_write(dev, offset, memtype, datalen, input); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static inline void *pld_snoc_smmu_get_domain(struct device *dev) +{ + return icnss_smmu_get_domain(dev); +} + +#else +static inline void *pld_snoc_smmu_get_mapping(struct device *dev) +{ + return icnss_smmu_get_mapping(dev); +} +#endif + +static inline int pld_snoc_smmu_map(struct device *dev, phys_addr_t paddr, + uint32_t *iova_addr, size_t size) +{ + return icnss_smmu_map(dev, paddr, iova_addr, size); +} + +#ifdef CONFIG_SMMU_S1_UNMAP +static inline int pld_snoc_smmu_unmap(struct device *dev, + uint32_t iova_addr, size_t size) +{ + return icnss_smmu_unmap(dev, iova_addr, size); +} + +#else +static inline int pld_snoc_smmu_unmap(struct device *dev, + uint32_t iova_addr, size_t size) +{ + return 0; +} +#endif + +static inline +unsigned int pld_snoc_socinfo_get_serial_number(struct device *dev) +{ + return icnss_socinfo_get_serial_number(dev); +} + +static inline int pld_snoc_is_fw_down(struct device *dev) +{ + return icnss_is_fw_down(); +} + +static inline int pld_snoc_is_qmi_disable(struct device *dev) +{ + if (!dev) + return -ENODEV; + + return icnss_is_qmi_disable(dev); +} + +static inline int pld_snoc_set_fw_log_mode(struct device *dev, u8 fw_log_mode) +{ + if (!dev) + return -ENODEV; + + return icnss_set_fw_log_mode(dev, fw_log_mode); +} + +static inline int pld_snoc_force_assert_target(struct device *dev) +{ + return icnss_trigger_recovery(dev); +} + +static inline int pld_snoc_is_pdr(void) +{ + return icnss_is_pdr(); +} + +static inline int pld_snoc_is_fw_rejuvenate(void) +{ + return icnss_is_rejuvenate(); +} + +static inline void pld_snoc_block_shutdown(bool status) +{ + icnss_block_shutdown(status); +} + +static inline int pld_snoc_idle_restart(struct device *dev) +{ + return icnss_idle_restart(dev); +} + +static inline int pld_snoc_idle_shutdown(struct device *dev) +{ + return icnss_idle_shutdown(dev); +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc_fw_sim.c b/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc_fw_sim.c new file mode 100644 index 0000000000000000000000000000000000000000..54d38c377352715aecd053166dbbefe7d889ece8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc_fw_sim.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "pld_snoc_fw_sim.h" + +#ifdef CONFIG_PLD_SNOC_FW_SIM +/** + * pld_snoc_fw_sim_probe() - Probe function for platform driver + * @dev: device + * + * The probe function will be called when platform device + * is detected. + * + * Return: int + */ +static int pld_snoc_fw_sim_probe(struct device *dev) +{ + struct pld_context *pld_context; + int ret = 0; + + pld_context = pld_get_global_context(); + if (!pld_context) { + ret = -ENODEV; + goto out; + } + + ret = pld_add_dev(pld_context, dev, NULL, PLD_BUS_TYPE_SNOC_FW_SIM); + if (ret) + goto out; + + return pld_context->ops->probe(dev, PLD_BUS_TYPE_SNOC_FW_SIM, + NULL, NULL); + +out: + return ret; +} + +/** + * pld_snoc_fw_sim_remove() - Remove function for platform device + * @dev: device + * + * The remove function will be called when platform device + * is disconnected + * + * Return: void + */ +static void pld_snoc_fw_sim_remove(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + + if (!pld_context) + return; + + pld_context->ops->remove(dev, PLD_BUS_TYPE_SNOC_FW_SIM); + + pld_del_dev(pld_context, dev); +} + +/** + * pld_snoc_fw_sim_reinit() - SSR re-initialize function for platform device + * @dev: device + * + * During subsystem restart(SSR), this function will be called to + * re-initialize platform device. + * + * Return: int + */ +static int pld_snoc_fw_sim_reinit(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->reinit) + return pld_context->ops->reinit(dev, PLD_BUS_TYPE_SNOC_FW_SIM, + NULL, NULL); + + return -ENODEV; +} + +/** + * pld_snoc_fw_sim_shutdown() - SSR shutdown function for platform device + * @dev: device + * + * During SSR, this function will be called to shutdown platform device. + * + * Return: void + */ +static void pld_snoc_fw_sim_shutdown(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + pld_context->ops->shutdown(dev, PLD_BUS_TYPE_SNOC_FW_SIM); +} + +/** + * pld_snoc_fw_sim_crash_shutdown() -Crash shutdown function for platform device + * @dev: device + * + * This function will be called when a crash is detected, it will shutdown + * platform device. + * + * Return: void + */ +static void pld_snoc_fw_sim_crash_shutdown(void *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (pld_context->ops->crash_shutdown) + pld_context->ops->crash_shutdown(dev, PLD_BUS_TYPE_SNOC_FW_SIM); +} + +/** + * pld_snoc_fw_sim_pm_suspend() - PM suspend callback function for power + * management + * @dev: device + * + * This function is to suspend the platform device when power management + * is enabled. + * + * Return: void + */ +static int pld_snoc_fw_sim_pm_suspend(struct device *dev) +{ + struct pld_context *pld_context; + pm_message_t state; + + state.event = PM_EVENT_SUSPEND; + pld_context = pld_get_global_context(); + return pld_context->ops->suspend(dev, PLD_BUS_TYPE_SNOC_FW_SIM, state); +} + +/** + * pld_snoc_fw_sim_pm_resume() -PM resume callback function for power management + * @pdev: device + * + * This function is to resume the platform device when power management + * is enabled. + * + * Return: void + */ +static int pld_snoc_fw_sim_pm_resume(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + return pld_context->ops->resume(dev, PLD_BUS_TYPE_SNOC_FW_SIM); +} + +/** + * pld_snoc_fw_sim_suspend_noirq() - Complete the actions started by suspend() + * @dev: device + * + * Complete the actions started by suspend(). Carry out any + * additional operations required for suspending the device that might be + * racing with its driver's interrupt handler, which is guaranteed not to + * run while suspend_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_snoc_fw_sim_suspend_noirq(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->suspend_noirq) + return pld_context->ops->suspend_noirq(dev, + PLD_BUS_TYPE_SNOC_FW_SIM); + return 0; +} + +/** + * pld_snoc_fw_sim_resume_noirq() - Prepare for the execution of resume() + * @pdev: device + * + * Prepare for the execution of resume() by carrying out any + * operations required for resuming the device that might be racing with + * its driver's interrupt handler, which is guaranteed not to run while + * resume_noirq() is being executed. + * + * Return: 0 for success + * Non zero failure code for errors + */ +static int pld_snoc_fw_sim_resume_noirq(struct device *dev) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (pld_context->ops->resume_noirq) + return pld_context->ops->resume_noirq(dev, + PLD_BUS_TYPE_SNOC_FW_SIM); + + return 0; +} + +static int pld_snoc_fw_sim_uevent(struct device *dev, + struct icnss_uevent_data *uevent) +{ + struct pld_context *pld_context; + struct icnss_uevent_fw_down_data *uevent_data = NULL; + struct pld_uevent_data data = {0}; + + pld_context = pld_get_global_context(); + if (!pld_context) + return -EINVAL; + + if (!pld_context->ops->uevent) + return 0; + + if (!uevent) + return -EINVAL; + + switch (uevent->uevent) { + case ICNSS_UEVENT_FW_CRASHED: + data.uevent = PLD_FW_CRASHED; + break; + case ICNSS_UEVENT_FW_DOWN: + if (!uevent->data) + return -EINVAL; + uevent_data = (struct icnss_uevent_fw_down_data *)uevent->data; + data.uevent = PLD_FW_DOWN; + data.fw_down.crashed = uevent_data->crashed; + break; + default: + return 0; + } + + pld_context->ops->uevent(dev, &data); + return 0; +} + +#ifdef MULTI_IF_NAME +#define PLD_SNOC_FW_SIM_OPS_NAME "pld_snoc_fw_sim_" MULTI_IF_NAME +#else +#define PLD_SNOC_FW_SIM_OPS_NAME "pld_snoc_fw_sim" +#endif + +struct icnss_driver_ops pld_snoc_fw_sim_ops = { + .name = PLD_SNOC_FW_SIM_OPS_NAME, + .probe = pld_snoc_fw_sim_probe, + .remove = pld_snoc_fw_sim_remove, + .shutdown = pld_snoc_fw_sim_shutdown, + .reinit = pld_snoc_fw_sim_reinit, + .crash_shutdown = pld_snoc_fw_sim_crash_shutdown, + .pm_suspend = pld_snoc_fw_sim_pm_suspend, + .pm_resume = pld_snoc_fw_sim_pm_resume, + .suspend_noirq = pld_snoc_fw_sim_suspend_noirq, + .resume_noirq = pld_snoc_fw_sim_resume_noirq, + .uevent = pld_snoc_fw_sim_uevent, +}; + +/** + * pld_snoc_fw_sim_register_driver() - Register platform device callback + * functions + * + * Return: int + */ +int pld_snoc_fw_sim_register_driver(void) +{ + return icnss_register_driver(&pld_snoc_fw_sim_ops); +} + +/** + * pld_snoc_fw_sim_unregister_driver() - Unregister platform device callback + * functions + * + * Return: void + */ +void pld_snoc_fw_sim_unregister_driver(void) +{ + icnss_unregister_driver(&pld_snoc_fw_sim_ops); +} + +/** + * pld_snoc_fw_sim_wlan_enable() - Enable WLAN + * @dev: device + * @config: WLAN configuration data + * @mode: WLAN mode + * @host_version: host software version + * + * This function enables WLAN FW. It passed WLAN configuration data, + * WLAN mode and host software version to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_snoc_fw_sim_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, + const char *host_version) +{ + struct icnss_wlan_enable_cfg cfg; + enum icnss_driver_mode icnss_mode; + + if (!dev) + return -ENODEV; + + cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg; + cfg.ce_tgt_cfg = (struct ce_tgt_pipe_cfg *) + config->ce_tgt_cfg; + cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg; + cfg.ce_svc_cfg = (struct ce_svc_pipe_cfg *) + config->ce_svc_cfg; + cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg; + cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *) + config->shadow_reg_cfg; + + switch (mode) { + case PLD_FTM: + icnss_mode = ICNSS_FTM; + break; + case PLD_EPPING: + icnss_mode = ICNSS_EPPING; + break; + default: + icnss_mode = ICNSS_MISSION; + break; + } + + return icnss_wlan_enable(dev, &cfg, icnss_mode, host_version); +} + +/** + * pld_snoc_fw_sim_wlan_disable() - Disable WLAN + * @dev: device + * @mode: WLAN mode + * + * This function disables WLAN FW. It passes WLAN mode to FW. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_snoc_fw_sim_wlan_disable(struct device *dev, enum pld_driver_mode mode) +{ + if (!dev) + return -ENODEV; + + return icnss_wlan_disable(dev, ICNSS_OFF); +} + +/** + * pld_snoc_fw_sim_get_soc_info() - Get SOC information + * @dev: device + * @info: buffer to SOC information + * + * Return SOC info to the buffer. + * + * Return: 0 for success + * Non zero failure code for errors + */ +int pld_snoc_fw_sim_get_soc_info(struct device *dev, struct pld_soc_info *info) +{ + int ret = 0; + struct icnss_soc_info icnss_info; + + if (!info || !dev) + return -ENODEV; + + ret = icnss_get_soc_info(dev, &icnss_info); + if (0 != ret) + return ret; + + memcpy(info, &icnss_info, sizeof(*info)); + return 0; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc_fw_sim.h b/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc_fw_sim.h new file mode 100644 index 0000000000000000000000000000000000000000..5f68eb278207592d0e33bfe9a16fa80a5ced1282 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_snoc_fw_sim.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __PLD_SNOC_FW_SIM_H__ +#define __PLD_SNOC_FW_SIM_H__ + +#include "pld_internal.h" + +#ifndef CONFIG_PLD_SNOC_FW_SIM +static inline int pld_snoc_fw_sim_register_driver(void) +{ + return 0; +} + +static inline void pld_snoc_fw_sim_unregister_driver(void) +{ +} + +static inline int pld_snoc_fw_sim_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *cfg, + enum pld_driver_mode mode, + const char *host_version) +{ + return 0; +} + +static inline int pld_snoc_fw_sim_wlan_disable(struct device *dev, + enum pld_driver_mode mode) +{ + return 0; +} + +static inline int pld_snoc_fw_sim_ce_request_irq(struct device *dev, + unsigned int ce_id, + irqreturn_t (*handler)(int, void *), + unsigned long flags, + const char *name, void *ctx) +{ + return 0; +} + +static inline int pld_snoc_fw_sim_ce_free_irq(struct device *dev, + unsigned int ce_id, void *ctx) +{ + return 0; +} + +static inline void pld_snoc_fw_sim_enable_irq(struct device *dev, + unsigned int ce_id) +{ +} + +static inline void pld_snoc_fw_sim_disable_irq(struct device *dev, + unsigned int ce_id) +{ +} + +static inline int pld_snoc_fw_sim_get_soc_info(struct device *dev, + struct pld_soc_info *info) +{ + return 0; +} + +static inline int pld_snoc_fw_sim_get_ce_id(struct device *dev, int irq) +{ + return 0; +} + +static inline int pld_snoc_fw_sim_get_irq(struct device *dev, int ce_id) +{ + return 0; +} + +static inline int pld_snoc_fw_sim_is_fw_down(struct device *dev) +{ + return 0; +} + +static inline int pld_snoc_fw_sim_idle_shutdown(struct device *dev) +{ + return 0; +} + +static inline int pld_snoc_fw_sim_idle_restart(struct device *dev) +{ + return 0; +} + +#else +#include + +int pld_snoc_fw_sim_register_driver(void); +void pld_snoc_fw_sim_unregister_driver(void); +int pld_snoc_fw_sim_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, + const char *host_version); +int pld_snoc_fw_sim_wlan_disable(struct device *dev, enum pld_driver_mode mode); +int pld_snoc_fw_sim_get_soc_info(struct device *dev, struct pld_soc_info *info); + +static inline int pld_snoc_fw_sim_ce_request_irq(struct device *dev, + unsigned int ce_id, + irqreturn_t (*handler)(int, void *), + unsigned long flags, + const char *name, void *ctx) +{ + if (!dev) + return -ENODEV; + + return icnss_ce_request_irq(dev, ce_id, handler, flags, name, ctx); +} + +static inline int pld_snoc_fw_sim_ce_free_irq(struct device *dev, + unsigned int ce_id, void *ctx) +{ + if (!dev) + return -ENODEV; + + return icnss_ce_free_irq(dev, ce_id, ctx); +} + +static inline void pld_snoc_fw_sim_enable_irq(struct device *dev, + unsigned int ce_id) +{ + if (dev) + icnss_enable_irq(dev, ce_id); +} + +static inline void pld_snoc_fw_sim_disable_irq(struct device *dev, + unsigned int ce_id) +{ + if (dev) + icnss_disable_irq(dev, ce_id); +} + +static inline int pld_snoc_fw_sim_get_ce_id(struct device *dev, int irq) +{ + if (!dev) + return -ENODEV; + + return icnss_get_ce_id(dev, irq); +} + +static inline int pld_snoc_fw_sim_get_irq(struct device *dev, int ce_id) +{ + if (!dev) + return -ENODEV; + + return icnss_get_irq(dev, ce_id); +} + +static inline int pld_snoc_fw_sim_is_fw_down(struct device *dev) +{ + return icnss_is_fw_down(); +} + +static inline int pld_snoc_fw_sim_idle_shutdown(struct device *dev) +{ + return icnss_idle_shutdown(dev); +} + +static inline int pld_snoc_fw_sim_idle_restart(struct device *dev) +{ + return icnss_idle_restart(dev); +} + +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_usb.c b/drivers/staging/qcacld-3.0/core/pld/src/pld_usb.c new file mode 100644 index 0000000000000000000000000000000000000000..c50d34c641da1acc06d8603febc6b43cec0be40d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_usb.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "pld_usb.h" +#include "pld_internal.h" + +#include +#include +#include +#include +#include +#include +#include "osif_psoc_sync.h" + +#ifdef CONFIG_PLD_USB_CNSS +#include +#endif + + +#define VENDOR_ATHR 0x0CF3 +static struct usb_device_id pld_usb_id_table[] = { + {USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ATHR, 0x9378, 0xFF, 0xFF, 0xFF)}, + {USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ATHR, 0x9379, 0xFF, 0xFF, 0xFF)}, + {} /* Terminating entry */ +}; + +atomic_t pld_usb_reg_done; + +/** + * pld_usb_probe() - pld_usb_probe + * @interface: pointer to usb_interface structure + * @id: pointer to usb_device_id obtained from the enumerated device + + * Return: int 0 on success and errno on failure. + */ +static int pld_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *pdev = interface_to_usbdev(interface); + struct pld_context *pld_context; + int ret = 0; + + pld_context = pld_get_global_context(); + if (!pld_context) { + ret = -ENODEV; + goto out; + } + + ret = pld_add_dev(pld_context, &pdev->dev, &interface->dev, + PLD_BUS_TYPE_USB); + if (ret) + goto out; + + ret = pld_context->ops->probe(&pdev->dev, + PLD_BUS_TYPE_USB, interface, (void *)id); + if (ret != 0) { + pr_err("%s, probe returned %d", __func__, ret); + atomic_set(&pld_usb_reg_done, false); + } else { + atomic_set(&pld_usb_reg_done, true); + } + +out: + return ret; +} + +/** + * pld_usb_remove() - Remove function for USB device + * @interface: pointer to usb_interface for the usb device being removed + * + * Return: void + */ +static void pld_usb_remove(struct usb_interface *interface) +{ + struct usb_device *pdev = interface_to_usbdev(interface); + struct pld_context *pld_context; + int errno; + struct osif_psoc_sync *psoc_sync; + + errno = osif_psoc_sync_trans_start_wait(&pdev->dev, &psoc_sync); + if (errno) + return; + + osif_psoc_sync_unregister(&pdev->dev); + osif_psoc_sync_wait_for_ops(psoc_sync); + + pld_context = pld_get_global_context(); + + if (!pld_context) + goto out; + + if (atomic_read(&pld_usb_reg_done) != true) { + pr_info("%s: already de-registered!\n", __func__); + goto out; + } + + pld_context->ops->remove(&pdev->dev, PLD_BUS_TYPE_USB); + + pld_del_dev(pld_context, &pdev->dev); + + atomic_set(&pld_usb_reg_done, false); +out: + osif_psoc_sync_trans_stop(psoc_sync); + osif_psoc_sync_destroy(psoc_sync); + + pr_info("%s: done!\n", __func__); +} + +/** + * pld_usb_suspend() - Suspend callback function for power management + * @interface: pointer to usb_interface for the usb device + * @state: power state + * + * This function is to suspend the PCIE device when power management is + * enabled. + * + * Return: void + */ +static int pld_usb_suspend(struct usb_interface *interface, + pm_message_t state) +{ + struct usb_device *pdev = interface_to_usbdev(interface); + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + return pld_context->ops->suspend(&pdev->dev, PLD_BUS_TYPE_USB, state); +} + +/** + * pld_usb_resume() - Resume callback function for power management + * @interface: pointer to usb_interface for the usb device + * + * This function is to resume the USB device when power management is + * enabled. + * + * Return: void + */ +static int pld_usb_resume(struct usb_interface *interface) +{ + struct pld_context *pld_context; + struct usb_device *pdev = interface_to_usbdev(interface); + + pld_context = pld_get_global_context(); + return pld_context->ops->resume(&pdev->dev, PLD_BUS_TYPE_USB); +} + +/** + * pld_usb_reset_resume() - pld_usb_reset_resume + * @interface: pointer to usb_interface for the usb device + * + * Return: void + */ +static int pld_usb_reset_resume(struct usb_interface *interface) +{ + struct pld_context *pld_context; + struct usb_device *pdev = interface_to_usbdev(interface); + + pld_context = pld_get_global_context(); + return pld_context->ops->reset_resume(&pdev->dev, PLD_BUS_TYPE_USB); +} + +#ifdef CONFIG_PLD_USB_CNSS +/** + * pld_usb_reinit() - SSR re-initialize function for USB device + * @interface: Pointer to struct usb_interface + * @id: Pointer to USB device ID + * + * Return: int + */ +static int pld_usb_reinit(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct pld_context *pld_context; + struct usb_device *pdev = interface_to_usbdev(interface); + + pld_context = pld_get_global_context(); + if (pld_context->ops->reinit) + return pld_context->ops->reinit(&pdev->dev, PLD_BUS_TYPE_USB, + interface, (void *)id); + + return -ENODEV; +} + +/** + * pld_usb_shutdown() - SSR shutdown function for USB device + * @interface: Pointer to struct usb_interface + * + * Return: void + */ +static void pld_usb_shutdown(struct usb_interface *interface) +{ + struct pld_context *pld_context; + struct usb_device *pdev = interface_to_usbdev(interface); + + pld_context = pld_get_global_context(); + if (pld_context->ops->shutdown) + pld_context->ops->shutdown(&pdev->dev, PLD_BUS_TYPE_USB); +} + +/** + * pld_usb_uevent() - update wlan driver status callback function + * @interface: USB interface + * @status driver uevent status + * + * This function will be called when platform driver wants to update wlan + * driver's status. + * + * Return: void + */ +static void pld_usb_uevent(struct usb_interface *interface, uint32_t status) +{ + struct pld_context *pld_context; + struct pld_uevent_data data = {0}; + struct usb_device *pdev = interface_to_usbdev(interface); + + pld_context = pld_get_global_context(); + if (!pld_context) + return; + + switch (status) { + case CNSS_RECOVERY: + data.uevent = PLD_FW_RECOVERY_START; + break; + case CNSS_FW_DOWN: + data.uevent = PLD_FW_DOWN; + break; + default: + goto out; + } + + if (pld_context->ops->uevent) + pld_context->ops->uevent(&pdev->dev, &data); + +out: + return; +} +struct cnss_usb_wlan_driver pld_usb_ops = { + .name = "pld_usb_cnss", + .id_table = pld_usb_id_table, + .probe = pld_usb_probe, + .remove = pld_usb_remove, + .shutdown = pld_usb_shutdown, + .reinit = pld_usb_reinit, + .update_status = pld_usb_uevent, +#ifdef CONFIG_PM + .suspend = pld_usb_suspend, + .resume = pld_usb_resume, + .reset_resume = pld_usb_reset_resume, +#endif +}; + +/** + * pld_usb_register_driver() - registration routine for wlan usb drive + * + * Return: int negative error code on failure and 0 on success + */ +int pld_usb_register_driver(void) +{ + pr_info("%s usb_register\n", __func__); + return cnss_usb_wlan_register_driver(&pld_usb_ops); +} + +/** + * pld_usb_unregister_driver() - de-registration routine for wlan usb driver + * + * Return: void + */ +void pld_usb_unregister_driver(void) +{ + cnss_usb_wlan_unregister_driver(&pld_usb_ops); + pr_info("%s usb_deregister done!\n", __func__); +} + +int pld_usb_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version) +{ + struct cnss_wlan_enable_cfg cfg; + enum cnss_driver_mode cnss_mode; + + switch (mode) { + case PLD_FTM: + cnss_mode = CNSS_FTM; + break; + case PLD_EPPING: + cnss_mode = CNSS_EPPING; + break; + default: + cnss_mode = CNSS_MISSION; + break; + } + return cnss_wlan_enable(dev, &cfg, cnss_mode, host_version); +} + +int pld_usb_is_fw_down(struct device *dev) +{ + return cnss_usb_is_device_down(dev); +} + +#else /* CONFIG_PLD_USB_CNSS */ + +struct usb_driver pld_usb_ops = { + .name = "pld_usb", + .id_table = pld_usb_id_table, + .probe = pld_usb_probe, + .disconnect = pld_usb_remove, +#ifdef CONFIG_PM + .suspend = pld_usb_suspend, + .resume = pld_usb_resume, + .reset_resume = pld_usb_reset_resume, +#endif + .supports_autosuspend = true, +}; + +/** + * pld_usb_register_driver() - registration routine for wlan usb driver + * + * Return: int negative error code on failure and 0 on success + */ +int pld_usb_register_driver(void) +{ + int status; + + usb_register(&pld_usb_ops); + + if (atomic_read(&pld_usb_reg_done) == true) { + status = 0; + } else { + usb_deregister(&pld_usb_ops); + status = -1; + } + + pr_info("%s usb_register %s, status %d\n", __func__, + (status == 0) ? "done" : "failed", status); + + return status; +} + +/** + * pld_usb_unregister_driver() - de-registration routine for wlan usb driver + * + * Return: void + */ +void pld_usb_unregister_driver(void) +{ + struct pld_context *pld_context; + + pld_context = pld_get_global_context(); + if (atomic_read(&pld_usb_reg_done) == false) + return; + + pld_context->ops->remove(NULL, PLD_BUS_TYPE_USB); + + atomic_set(&pld_usb_reg_done, false); + usb_deregister(&pld_usb_ops); + pr_info("%s usb_deregister done!\n", __func__); +} + +int pld_usb_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version) +{ + return 0; +} + +int pld_usb_is_fw_down(struct device *dev) +{ + return 0; +} + +#endif /* CONFIG_PLD_USB_CNSS */ diff --git a/drivers/staging/qcacld-3.0/core/pld/src/pld_usb.h b/drivers/staging/qcacld-3.0/core/pld/src/pld_usb.h new file mode 100644 index 0000000000000000000000000000000000000000..1dcc11d323713c5baffad48b231fc43f4bac55b8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/pld/src/pld_usb.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __PLD_USB_H__ +#define __PLD_USB_H__ + +#include "pld_common.h" + +#ifdef HIF_USB +int pld_usb_register_driver(void); +void pld_usb_unregister_driver(void); +int pld_usb_get_ce_id(int irq); +int pld_usb_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, const char *host_version); +int pld_usb_is_fw_down(struct device *dev); + +#else +static inline int pld_usb_register_driver(void) +{ + return 0; +} + +static inline void pld_usb_unregister_driver(void) +{} + +static inline int pld_usb_wlan_enable(struct device *dev, + struct pld_wlan_enable_cfg *config, + enum pld_driver_mode mode, + const char *host_version) +{ + return 0; +} + +static inline int pld_usb_is_fw_down(struct device *dev) +{ + return 0; +} + +#endif + +static inline int +pld_usb_get_fw_files_for_target(struct pld_fw_files *pfw_files, + u32 target_type, u32 target_version) +{ + pld_get_default_fw_files(pfw_files); + return 0; +} + +#endif /*__PLD_USB_H__*/ diff --git a/drivers/staging/qcacld-3.0/core/sap/inc/sap_api.h b/drivers/staging/qcacld-3.0/core/sap/inc/sap_api.h new file mode 100644 index 0000000000000000000000000000000000000000..5f3eff964c34e7a2a670ca9574886345114fe287 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sap/inc/sap_api.h @@ -0,0 +1,1510 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_QCT_WLANSAP_H +#define WLAN_QCT_WLANSAP_H + +/** + * W L A N S O F T A P P A L L A Y E R + * E X T E R N A L A P I + * + * DESCRIPTION + * This file contains the external API exposed by the wlan SAP PAL layer + * module. + */ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ +#include "cds_api.h" +#include "cds_packet.h" +#include "qdf_types.h" + +#include "sme_api.h" +/*---------------------------------------------------------------------------- + * Preprocessor Definitions and Constants + * -------------------------------------------------------------------------*/ +#ifdef __cplusplus +extern "C" { +#endif + +/*--------------------------------------------------------------------------- + * defines and enum + *--------------------------------------------------------------------------*/ +#define MAX_ACL_MAC_ADDRESS 32 +#define AUTO_CHANNEL_SELECT 0 +#define MAX_ASSOC_IND_IE_LEN 255 +#define MAX_ASSOC_REQ_IE_LEN 2000 +#define ASSOC_REQ_IE_OFFSET 4 + +/* defines for WPS config states */ +#define SAP_WPS_DISABLED 0 +#define SAP_WPS_ENABLED_UNCONFIGURED 1 +#define SAP_WPS_ENABLED_CONFIGURED 2 + +#define MAX_CHANNEL_LIST_LEN 256 +#define QDF_MAX_NO_OF_SAP_MODE 2 /* max # of SAP */ +#define SAP_MAX_NUM_SESSION 5 +#define SAP_MAX_OBSS_STA_CNT 1 /* max # of OBSS STA */ +#define SAP_ACS_WEIGHT_MAX (26664) + +#define SAP_DEFAULT_24GHZ_CHANNEL (6) +#define SAP_DEFAULT_5GHZ_CHANNEL (40) +#define SAP_CHANNEL_NOT_SELECTED (0) + +/*-------------------------------------------------------------------------- + * reasonCode taken from 802.11 standard. + * ------------------------------------------------------------------------*/ + +typedef enum { + eSAP_RC_RESERVED0, /*0 */ + eSAP_RC_UNSPECIFIED, /*1 */ + eSAP_RC_PREV_AUTH_INVALID, /*2 */ + eSAP_RC_STA_LEFT_DEAUTH, /*3 */ + eSAP_RC_INACTIVITY_DISASSOC, /*4 */ + eSAP_RC_AP_CAPACITY_FULL, /*5 */ + eSAP_RC_CLS2_FROM_NON_AUTH_STA, /*6 */ + eSAP_RC_CLS3_FROM_NON_AUTH_STA, /*7 */ + eSAP_RC_STA_LEFT_DISASSOC, /*8 */ + eSAP_RC_STA_NOT_AUTH, /*9 */ + eSAP_RC_PC_UNACCEPTABLE, /*10 */ + eSAP_RC_SC_UNACCEPTABLE, /*11 */ + eSAP_RC_RESERVED1, /*12 */ + eSAP_RC_INVALID_IE, /*13 */ + eSAP_RC_MIC_FAIL, /*14 */ + eSAP_RC_4_WAY_HANDSHAKE_TO, /*15 */ + eSAP_RC_GO_KEY_HANDSHAKE_TO, /*16 */ + eSAP_RC_IE_MISMATCH, /*17 */ + eSAP_RC_INVALID_GRP_CHIPHER, /*18 */ + eSAP_RC_INVALID_PAIR_CHIPHER, /*19 */ + eSAP_RC_INVALID_AKMP, /*20 */ + eSAP_RC_UNSUPPORTED_RSN, /*21 */ + eSAP_RC_INVALID_RSN, /*22 */ + eSAP_RC_1X_AUTH_FAILED, /*23 */ + eSAP_RC_CHIPER_SUITE_REJECTED, /*24 */ +} eSapReasonCode; + +typedef enum { + eSAP_ACCEPT_UNLESS_DENIED = 0, + eSAP_DENY_UNLESS_ACCEPTED = 1, + /* this type is added to support accept & deny list at the same time */ + eSAP_SUPPORT_ACCEPT_AND_DENY = 2, + /*In this mode all MAC addresses are allowed to connect */ + eSAP_ALLOW_ALL = 3, +} eSapMacAddrACL; + +typedef enum { + eSAP_BLACK_LIST = 0, /* List of mac addresses NOT allowed to assoc */ + eSAP_WHITE_LIST = 1, /* List of mac addresses allowed to assoc */ +} eSapACLType; + +typedef enum { + ADD_STA_TO_ACL = 0, /* cmd to add STA to access control list */ + DELETE_STA_FROM_ACL = 1, /* cmd to del STA from access control list */ +} eSapACLCmdType; + +typedef enum { + eSAP_START_BSS_EVENT = 0, /* Event sent when BSS is started */ + eSAP_STOP_BSS_EVENT, /* Event sent when BSS is stopped */ + eSAP_STA_ASSOC_IND, /* Indicate assoc req to upper layers */ + /* + * Event sent when we have successfully associated a station and + * upper layer neeeds to allocate a context + */ + eSAP_STA_ASSOC_EVENT, + /* + * Event sent when we have successfully reassociated a station and + * upper layer neeeds to allocate a context + */ + eSAP_STA_REASSOC_EVENT, + /* + * Event sent when associated a station has disassociated as a + * result of various conditions + */ + eSAP_STA_DISASSOC_EVENT, + /* Event sent when user called wlansap_set_key_sta */ + eSAP_STA_SET_KEY_EVENT, + /* Event sent whenever there is MIC failure detected */ + eSAP_STA_MIC_FAILURE_EVENT, + /* Event send on WPS PBC probe request is received */ + eSAP_WPS_PBC_PROBE_REQ_EVENT, + eSAP_DISCONNECT_ALL_P2P_CLIENT, + eSAP_MAC_TRIG_STOP_BSS_EVENT, + /* + * Event send when a STA in neither white list or black list tries to + * associate in softap mode + */ + eSAP_UNKNOWN_STA_JOIN, + /* Event send when a new STA is rejected association since softAP + * max assoc limit has reached + */ + eSAP_MAX_ASSOC_EXCEEDED, + eSAP_CHANNEL_CHANGE_EVENT, + eSAP_DFS_CAC_START, + eSAP_DFS_CAC_INTERRUPTED, + eSAP_DFS_CAC_END, + eSAP_DFS_PRE_CAC_END, + eSAP_DFS_RADAR_DETECT, + eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC, + /* No ch available after DFS RADAR detect */ + eSAP_DFS_NO_AVAILABLE_CHANNEL, + eSAP_STOP_BSS_DUE_TO_NO_CHNL, + eSAP_ACS_SCAN_SUCCESS_EVENT, + eSAP_ACS_CHANNEL_SELECTED, + eSAP_ECSA_CHANGE_CHAN_IND, + eSAP_DFS_NEXT_CHANNEL_REQ, + /* Event sent channel switch status to upper layer */ + eSAP_CHANNEL_CHANGE_RESP, +} eSapHddEvent; + +typedef enum { + eSAP_OPEN_SYSTEM, + eSAP_SHARED_KEY, + eSAP_AUTO_SWITCH +} eSapAuthType; + +typedef enum { + /* Disassociation was internally initated from CORE stack */ + eSAP_MAC_INITATED_DISASSOC = 0x10000, + /* + * Disassociation was internally initated from host by + * invoking wlansap_disassoc_sta call + */ + eSAP_USR_INITATED_DISASSOC +} eSapDisassocReason; + +typedef enum { + eSAP_DFS_NOL_CLEAR, + eSAP_DFS_NOL_RANDOMIZE, +} eSapDfsNolType; + +/*--------------------------------------------------------------------------- + SAP PAL "status" and "reason" error code defines + ---------------------------------------------------------------------------*/ +typedef enum { + eSAP_STATUS_SUCCESS, /* Success. */ + eSAP_STATUS_FAILURE, /* General Failure. */ + /* Channel not selected during initial scan. */ + eSAP_START_BSS_CHANNEL_NOT_SELECTED, + eSAP_ERROR_MAC_START_FAIL, /* Failed to start Infra BSS */ +} eSapStatus; + +/*--------------------------------------------------------------------------- + SAP PAL "status" and "reason" error code defines + ---------------------------------------------------------------------------*/ +typedef enum { + eSAP_WPSPBC_OVERLAP_IN120S, /* Overlap */ + /* no WPS probe request in 120 second */ + eSAP_WPSPBC_NO_WPSPBC_PROBE_REQ_IN120S, + /* One WPS probe request in 120 second */ + eSAP_WPSPBC_ONE_WPSPBC_PROBE_REQ_IN120S, +} eWPSPBCOverlap; + +/*--------------------------------------------------------------------------- + SAP Associated station types + ---------------------------------------------------------------------------*/ +typedef enum { + eSTA_TYPE_NONE = 0x00000000, /* No station type */ + eSTA_TYPE_INFRA = 0x00000001, /* legacy station */ + eSTA_TYPE_P2P_CLI = 0x00000002, /* p2p client */ +} eStationType; + +/*---------------------------------------------------------------------------- + * Typedefs + * -------------------------------------------------------------------------*/ +typedef struct sap_StartBssCompleteEvent_s { + uint8_t status; + uint32_t operating_chan_freq; + enum phy_ch_width ch_width; + uint16_t staId; /* self StaID */ + uint8_t sessionId; /* SoftAP SME session ID */ +} tSap_StartBssCompleteEvent; + +typedef struct sap_StopBssCompleteEvent_s { + uint8_t status; +} tSap_StopBssCompleteEvent; + +typedef struct sap_StationAssocIndication_s { + struct qdf_mac_addr staMac; + uint8_t staId; + uint8_t status; + /* Required for indicating the frames to upper layer */ + uint32_t beaconLength; + uint8_t *beaconPtr; + uint32_t assocReqLength; + uint8_t *assocReqPtr; + bool fWmmEnabled; + enum csr_akm_type negotiatedAuthType; + eCsrEncryptionType negotiatedUCEncryptionType; + eCsrEncryptionType negotiatedMCEncryptionType; + bool fAuthRequired; + uint8_t ecsa_capable; + uint32_t owe_ie_len; + uint8_t *owe_ie; +} tSap_StationAssocIndication; + +typedef struct sap_StationAssocReassocCompleteEvent_s { + struct qdf_mac_addr staMac; + eStationType staType; + uint8_t staId; + uint8_t status; + uint8_t *ies; + uint32_t ies_len; + uint32_t status_code; + bool wmmEnabled; + uint8_t timingMeasCap; + struct oem_channel_info chan_info; + bool ampdu; + bool sgi_enable; + bool tx_stbc; + bool rx_stbc; + tSirMacHTChannelWidth ch_width; + enum sir_sme_phy_mode mode; + uint8_t max_supp_idx; + uint8_t max_ext_idx; + uint8_t max_mcs_idx; + uint8_t rx_mcs_map; + uint8_t tx_mcs_map; + uint8_t ecsa_capable; + tDot11fIEHTCaps ht_caps; + tDot11fIEVHTCaps vht_caps; + tSirMacCapabilityInfo capability_info; + bool he_caps_present; +} tSap_StationAssocReassocCompleteEvent; + +typedef struct sap_StationDisassocCompleteEvent_s { + struct qdf_mac_addr staMac; + uint8_t staId; /* STAID should not be used */ + uint8_t status; + uint32_t status_code; + uint32_t reason_code; + eSapDisassocReason reason; + int rssi; + int tx_rate; + int rx_rate; + uint32_t rx_mc_bc_cnt; + uint32_t rx_retry_cnt; +} tSap_StationDisassocCompleteEvent; + +typedef struct sap_StationSetKeyCompleteEvent_s { + uint8_t status; + struct qdf_mac_addr peerMacAddr; +} tSap_StationSetKeyCompleteEvent; + +/*struct corresponding to SAP_STA_MIC_FAILURE_EVENT */ +typedef struct sap_StationMICFailureEvent_s { + struct qdf_mac_addr srcMacAddr; /* address used to compute MIC */ + struct qdf_mac_addr staMac; /* taMacAddr transmitter address */ + struct qdf_mac_addr dstMacAddr; + bool multicast; + uint8_t IV1; /* first byte of IV */ + uint8_t keyId; /* second byte of IV */ + uint8_t TSC[SIR_CIPHER_SEQ_CTR_SIZE]; /* sequence number */ + +} tSap_StationMICFailureEvent; + +typedef struct sap_WPSPBCProbeReqEvent_s { + uint8_t status; + /* module id that was passed in wlansap_get_assoc_stations API */ + QDF_MODULE_ID module; + tSirWPSPBCProbeReq WPSPBCProbeReq; +} tSap_WPSPBCProbeReqEvent; + +typedef struct sap_SendActionCnf_s { + eSapStatus actionSendSuccess; +} tSap_SendActionCnf; + +typedef struct sap_UnknownSTAJoinEvent_s { + struct qdf_mac_addr macaddr; +} tSap_UnknownSTAJoinEvent; + +typedef struct sap_MaxAssocExceededEvent_s { + struct qdf_mac_addr macaddr; +} tSap_MaxAssocExceededEvent; + +/** + * sap_acs_ch_selected_s - the structure to hold the selected channels + * @pri_chan_freq: Holds the ACS selected primary channel frequency + * @ht_sec_ch_freq: Holds the ACS selected secondary ht channel frequency + * @vht_seg0_center_ch: Holds the ACS selected center channel of vht seg0 + * @vht_seg1_center_ch: Holds the ACS selected center channel of vht seg1 + * @ch_width: Holds the ACS selected channel bandwidth + * + * Holds the primary and secondary channel selected by ACS and is + * used to send it to the HDD. + */ +struct sap_ch_selected_s { + uint32_t pri_ch_freq; + uint32_t ht_sec_ch_freq; + uint16_t vht_seg0_center_ch_freq; + uint16_t vht_seg1_center_ch_freq; + uint16_t ch_width; +}; + +/** + * struct sap_acs_scan_complete_event - acs scan complete event + * @status: status of acs scan + * @freq_list: acs scan channel frequency list + * @num_of_channels: number of channels + */ +struct sap_acs_scan_complete_event { + uint8_t status; + uint32_t *freq_list; + uint8_t num_of_channels; +}; + +/** + * struct sap_ch_change_ind - channel change indication + * @new_chan_freq: channel frequency to change to + */ +struct sap_ch_change_ind { + uint32_t new_chan_freq; +}; + +/* + * This struct will be filled in and passed to sap_event_cb that is + * provided during wlansap_start_bss call The event id corresponding to + * structure in the union is defined in comment next to the structure + */ + +struct sap_event { + eSapHddEvent sapHddEventCode; + union { + /*SAP_START_BSS_EVENT */ + tSap_StartBssCompleteEvent sapStartBssCompleteEvent; + /*SAP_STOP_BSS_EVENT */ + tSap_StopBssCompleteEvent sapStopBssCompleteEvent; + /*SAP_ASSOC_INDICATION */ + tSap_StationAssocIndication sapAssocIndication; + /*SAP_STA_ASSOC_EVENT, SAP_STA_REASSOC_EVENT */ + tSap_StationAssocReassocCompleteEvent + sapStationAssocReassocCompleteEvent; + /*SAP_STA_DISASSOC_EVENT */ + tSap_StationDisassocCompleteEvent + sapStationDisassocCompleteEvent; + /*SAP_STA_SET_KEY_EVENT */ + tSap_StationSetKeyCompleteEvent sapStationSetKeyCompleteEvent; + /*SAP_STA_MIC_FAILURE_EVENT */ + tSap_StationMICFailureEvent sapStationMICFailureEvent; + /*eSAP_WPS_PBC_PROBE_REQ_EVENT */ + tSap_WPSPBCProbeReqEvent sapPBCProbeReqEvent; + tSap_SendActionCnf sapActionCnf; + /* eSAP_UNKNOWN_STA_JOIN */ + tSap_UnknownSTAJoinEvent sapUnknownSTAJoin; + /* eSAP_MAX_ASSOC_EXCEEDED */ + tSap_MaxAssocExceededEvent sapMaxAssocExceeded; + struct sap_ch_selected_s sap_ch_selected; + struct sap_ch_change_ind sap_chan_cng_ind; + struct sap_acs_scan_complete_event sap_acs_scan_comp; + QDF_STATUS ch_change_rsp_status; + } sapevt; +}; + +typedef struct sap_SSID { + uint8_t length; + uint8_t ssId[WLAN_SSID_MAX_LEN]; +} qdf_packed tSap_SSID_t; + +typedef struct sap_SSIDInfo { + tSap_SSID_t ssid; /* SSID of the AP */ + /* SSID should/shouldn't be bcast in probe RSP & beacon */ + uint8_t ssidHidden; +} qdf_packed tSap_SSIDInfo_t; + +struct sap_acs_cfg { + /* ACS Algo Input */ + uint8_t acs_mode; + eCsrPhyMode hw_mode; + uint32_t start_ch_freq; + uint32_t end_ch_freq; + uint32_t *freq_list; + uint8_t ch_list_count; + uint32_t *master_freq_list; + uint8_t master_ch_list_count; +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE + uint8_t skip_scan_status; + uint32_t skip_scan_range1_stch; + uint32_t skip_scan_range1_endch; + uint32_t skip_scan_range2_stch; + uint32_t skip_scan_range2_endch; +#endif + + uint16_t ch_width; + uint32_t pcl_chan_freq[NUM_CHANNELS]; + uint8_t pcl_channels_weight_list[NUM_CHANNELS]; + uint32_t pcl_ch_count; + uint8_t is_ht_enabled; + uint8_t is_vht_enabled; + /* ACS Algo Output */ + uint32_t pri_ch_freq; + uint32_t ht_sec_ch_freq; + uint32_t vht_seg0_center_ch_freq; + uint32_t vht_seg1_center_ch_freq; + uint32_t band; +}; + +/* + * enum sap_acs_dfs_mode- state of DFS mode + * @ACS_DFS_MODE_NONE: DFS mode attribute is not valid + * @ACS_DFS_MODE_ENABLE: DFS mode is enabled + * @ACS_DFS_MODE_DISABLE: DFS mode is disabled + * @ACS_DFS_MODE_DEPRIORITIZE: Deprioritize DFS channels in scanning + */ +enum sap_acs_dfs_mode { + ACS_DFS_MODE_NONE, + ACS_DFS_MODE_ENABLE, + ACS_DFS_MODE_DISABLE, + ACS_DFS_MODE_DEPRIORITIZE +}; + +struct sap_config { + tSap_SSIDInfo_t SSIDinfo; + eCsrPhyMode SapHw_mode; /* Wireless Mode */ + eSapMacAddrACL SapMacaddr_acl; + struct qdf_mac_addr accept_mac[MAX_ACL_MAC_ADDRESS]; /* MAC filtering */ + bool ieee80211d; /* Specify if 11D is enabled or disabled */ + struct qdf_mac_addr deny_mac[MAX_ACL_MAC_ADDRESS]; /* MAC filtering */ + struct qdf_mac_addr self_macaddr; /* self macaddress or BSSID */ + uint32_t chan_freq; /* Operation channel frequency */ + uint32_t sec_ch_freq; + struct ch_params ch_params; + uint32_t ch_width_orig; + uint8_t max_num_sta; /* maximum number of STAs in station table */ + uint8_t dtim_period; /* dtim interval */ + uint8_t num_accept_mac; + uint8_t num_deny_mac; + /* Max ie length 255 * 2(WPA+RSN) + 2 bytes(vendor specific ID) * 2 */ + uint8_t RSNWPAReqIE[(WLAN_MAX_IE_LEN * 2) + 4]; + /* it is ignored if [0] is 0. */ + uint8_t countryCode[CFG_COUNTRY_CODE_LEN]; + uint8_t RSNEncryptType; + uint8_t mcRSNEncryptType; + eSapAuthType authType; + tCsrAuthList akm_list; + bool privacy; + bool fwdWPSPBCProbeReq; + /* 0 - disabled, 1 - not configured , 2 - configured */ + uint8_t wps_state; + uint16_t RSNWPAReqIELength; /* The byte count in the pWPAReqIE */ + uint32_t beacon_int; /* Beacon Interval */ + enum QDF_OPMODE persona; /* Tells us which persona, GO or AP */ + bool enOverLapCh; +#ifdef WLAN_FEATURE_11W + bool mfpRequired; + bool mfpCapable; +#endif +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + uint8_t cc_switch_mode; +#endif + struct sap_acs_cfg acs_cfg; + uint16_t probeRespIEsBufferLen; + /* buffer for addn ies comes from hostapd */ + void *pProbeRespIEsBuffer; + uint16_t assocRespIEsLen; + /* buffer for addn ies comes from hostapd */ + void *pAssocRespIEsBuffer; + uint16_t probeRespBcnIEsLen; + /* buffer for addn ies comes from hostapd */ + void *pProbeRespBcnIEsBuffer; + uint16_t beacon_tx_rate; + uint8_t *vendor_ie; + uint16_t sta_inactivity_timeout; + uint16_t tx_pkt_fail_cnt_threshold; + uint8_t short_retry_limit; + uint8_t long_retry_limit; + tSirMacRateSet supported_rates; + tSirMacRateSet extended_rates; + bool require_h2e; + enum sap_acs_dfs_mode acs_dfs_mode; + struct hdd_channel_info *channel_info; + uint32_t channel_info_count; + bool dfs_cac_offload; +}; + +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE +typedef enum { + eSAP_DO_NEW_ACS_SCAN, + eSAP_DO_PAR_ACS_SCAN, + eSAP_SKIP_ACS_SCAN +} tSap_skip_acs_scan; +#endif + +typedef enum { + eSAP_DFS_DO_NOT_SKIP_CAC, + eSAP_DFS_SKIP_CAC +} eSapDfsCACState_t; + +typedef enum { + eSAP_DFS_CHANNEL_USABLE, + eSAP_DFS_CHANNEL_AVAILABLE, + eSAP_DFS_CHANNEL_UNAVAILABLE +} eSapDfsChanStatus_t; + +typedef struct sSapDfsNolInfo { + uint8_t dfs_channel_number; + eSapDfsChanStatus_t radar_status_flag; + uint64_t radar_found_timestamp; +} tSapDfsNolInfo; + +typedef struct sSapDfsInfo { + qdf_mc_timer_t sap_dfs_cac_timer; + uint8_t sap_radar_found_status; + /* + * New channel frequency to move to when a Radar is + * detected on current Channel + */ + uint32_t target_chan_freq; + uint8_t ignore_cac; + eSapDfsCACState_t cac_state; + uint32_t user_provided_target_chan_freq; + + /* + * Requests for Channel Switch Announcement IE + * generation and transmission + */ + uint8_t csaIERequired; + uint8_t is_dfs_cac_timer_running; + /* + * New channel width and new channel bonding mode + * will only be updated via channel fallback mechanism + */ + enum phy_ch_width orig_chanWidth; + enum phy_ch_width new_chanWidth; + struct ch_params new_ch_params; + + /* + * sap_operating_channel_location holds SAP indoor, + * outdoor location information. Currently, if this + * param is set this Indoor/outdoor channel interop + * restriction will only be implemented for JAPAN + * regulatory domain. + * + * 0 - Indicates that location unknown + * (or) SAP Indoor/outdoor interop is allowed + * + * 1 - Indicates device is operating on Indoor channels + * and SAP cannot pick next random channel from outdoor + * list of channels when a radar is found on current operating + * DFS channel. + * + * 2 - Indicates device is operating on Outdoor Channels + * and SAP cannot pick next random channel from indoor + * list of channels when a radar is found on current + * operating DFS channel. + */ + uint8_t sap_operating_chan_preferred_location; + + /* + * Flag to indicate if DFS test mode is enabled and + * channel switch is disabled. + */ + uint8_t disable_dfs_ch_switch; + uint16_t tx_leakage_threshold; + /* beacon count before channel switch */ + uint8_t sap_ch_switch_beacon_cnt; + uint8_t sap_ch_switch_mode; + uint16_t reduced_beacon_interval; +} tSapDfsInfo; + +struct sap_ctx_list { + void *sap_context; + enum QDF_OPMODE sapPersona; +}; + +typedef struct tagSapStruct { + /* Information Required for SAP DFS Master mode */ + tSapDfsInfo SapDfsInfo; + struct sap_ctx_list sapCtxList[SAP_MAX_NUM_SESSION]; +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + bool sap_channel_avoidance; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + bool acs_with_more_param; + bool enable_dfs_phy_error_logs; +} tSapStruct, *tpSapStruct; + +/** + * struct sap_context - per-BSS Context for SAP + * + * struct sap_context is used to share per-BSS context between SAP and + * its clients. A context is generated by sap_create_ctx() and is + * destroyed by sap_destroy_ctx(). During the lifetime of the BSS the + * SAP context is passed as the primary parameter to SAP APIs. Note + * that by design the contents of the structure are opaque to the + * clients and a SAP context pointer must only be dereferenced by SAP. + */ +struct sap_context; + +/** + * wlansap_roam_callback() - API to get the events for SAP persona + * @ctx: callback context registered with SME (sap context is registered) + * @csr_roam_info: pointer to SME CSR roam info structure + * @roam_id: roam id being used + * @roam_status: status of the event reported by SME to SAP + * @roam_result: result of the event reported by SME to SAP + * + * Any activity like start_bss, stop_bss, and etc for SAP persona + * happens, SME reports the result of those events to SAP through this + * callback. + * + * Return: QDF_STATUS based on overall result + */ +QDF_STATUS wlansap_roam_callback(void *ctx, + struct csr_roam_info *csr_roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result); + +/** + * sap_create_ctx() - API to create the sap context + * + * This API assigns the sap context from global sap context pool + * stored in gp_sap_ctx[i] array. + * + * Return: Pointer to the SAP context, or NULL if a context could not + * be allocated + */ +struct sap_context *sap_create_ctx(void); + +/** + * sap_destroy_ctx - API to destroy the sap context + * @sap_ctx: Pointer to the SAP context + * + * This API puts back the given sap context to global sap context pool which + * makes current sap session's sap context invalid. + * + * Return: The result code associated with performing the operation + * QDF_STATUS_E_FAULT: Pointer to SAP cb is NULL; + * access would cause a page fault + * QDF_STATUS_SUCCESS: Success + */ +QDF_STATUS sap_destroy_ctx(struct sap_context *sap_ctx); + +/** + * sap_init_ctx - Initialize the sap context + * @sap_ctx: Pointer to the SAP context + * @mode: Device mode + * @addr: MAC address of the SAP + * @session_id: Pointer to the session id + * @reinit: if called as part of reinit + * + * sap_create_ctx() allocates the sap context which is uninitialized. + * This API needs to be called to properly initialize the sap context + * which is just created. + * + * Return: The result code associated with performing the operation + * QDF_STATUS_E_FAULT: BSS could not be started + * QDF_STATUS_SUCCESS: Success + */ +QDF_STATUS sap_init_ctx(struct sap_context *sap_ctx, + enum QDF_OPMODE mode, + uint8_t *addr, uint32_t session_id, bool reinit); + +/** + * sap_deinit_ctx() - De-initialize the sap context + * @sap_ctx: Pointer to the SAP context + * + * When SAP session is about to close, this API needs to be called + * to de-initialize all the members of sap context structure, so that + * nobody can accidently start using the sap context. + * + * Return: The result code associated with performing the operation + * QDF_STATUS_E_FAULT: BSS could not be stopped + * QDF_STATUS_SUCCESS: Success + */ +QDF_STATUS sap_deinit_ctx(struct sap_context *sap_ctx); + +/** + * sap_is_auto_channel_select() - is channel AUTO_CHANNEL_SELECT + * @sapcontext: Pointer to the SAP context + * + * Return: true on AUTO_CHANNEL_SELECT, false otherwise + */ +bool sap_is_auto_channel_select(struct sap_context *sapcontext); + +QDF_STATUS wlansap_global_init(void); +QDF_STATUS wlansap_global_deinit(void); +typedef QDF_STATUS (*sap_event_cb)(struct sap_event *sap_event, + void *user_context); + +/** + * wlansap_is_channel_in_nol_list() - This API checks if channel is + * in nol list + * @sap_ctx: SAP context pointer + * @channelNumber: channel number + * @chanBondState: channel bonding state + * + * Return: True if the channel is in the NOL list, false otherwise + */ +bool wlansap_is_channel_in_nol_list(struct sap_context *sap_ctx, + uint8_t channelNumber, + ePhyChanBondState chanBondState); + +/** + * wlansap_is_channel_leaking_in_nol() - This API checks if channel is leaking + * in nol list + * @sap_ctx: SAP context pointer + * @channel: channel + * @chan_bw: channel bandwidth + * + * Return: True/False + */ +bool wlansap_is_channel_leaking_in_nol(struct sap_context *sap_ctx, + uint8_t channel, + uint8_t chan_bw); + +/** + * wlansap_start_bss() - start BSS + * @sap_ctx: Pointer to the SAP context + * @sap_event_cb: Callback function in HDD called by SAP to inform HDD + * about SAP results + * @config: Pointer to configuration structure passed down from + * HDD(HostApd for Android) + * @user_context: Parameter that will be passed back in all the SAP callback + * events. + * + * This api function provides SAP FSM event eWLAN_SAP_PHYSICAL_LINK_CREATE for + * starting AP BSS + * + * Return: The result code associated with performing the operation + * QDF_STATUS_E_FAULT: Pointer to SAP cb is NULL; + * access would cause a page fault + * QDF_STATUS_SUCCESS: Success + */ +QDF_STATUS wlansap_start_bss(struct sap_context *sap_ctx, + sap_event_cb sap_event_cb, + struct sap_config *config, void *user_context); + +/** + * wlansap_stop_bss() - stop BSS. + * @sap_ctx: Pointer to SAP context + * + * This api function provides SAP FSM event eSAP_HDD_STOP_INFRA_BSS for + * stopping AP BSS + * + * Return: The result code associated with performing the operation + * QDF_STATUS_E_FAULT: Pointer to SAP cb is NULL; + * access would cause a page fault + * QDF_STATUS_SUCCESS: Success + */ +QDF_STATUS wlansap_stop_bss(struct sap_context *sap_ctx); + +/** + * wlan_sap_update_next_channel() - Update next channel configured using vendor + * command in SAP context + * @sap_ctx: SAP context + * @channel: channel number + * @chan_bw: channel width + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_sap_update_next_channel(struct sap_context *sap_ctx, + uint8_t channel, + enum phy_ch_width chan_bw); + +/** + * wlan_sap_set_pre_cac_status() - Set the pre cac status + * @sap_ctx: SAP context + * @status: Status of pre cac + * + * Updates the state of pre cac in the SAP context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_sap_set_pre_cac_status(struct sap_context *sap_ctx, + bool status); + +/** + * wlan_sap_set_chan_before_pre_cac() - Save the channel before pre cac + * @sap_ctx: SAP context + * @chan_before_pre_cac: Channel before pre cac + * + * Saves the channel that was in use before pre cac operation + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_sap_set_chan_before_pre_cac(struct sap_context *sap_ctx, + uint8_t chan_before_pre_cac); + +/** + * wlan_sap_set_pre_cac_complete_status() - Sets pre cac complete status + * @sap_ctx: SAP context + * @status: Status of pre cac complete + * + * Sets the status of pre cac i.e., whether pre cac is complete or not + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_sap_set_pre_cac_complete_status(struct sap_context *sap_ctx, + bool status); + +/** + * wlan_sap_is_pre_cac_context() - checks if @context is for a pre-cac adapter + * @context: the SAP context to check + * + * Return: true if @context is for a pre-cac adapter + */ +bool wlan_sap_is_pre_cac_context(struct sap_context *context); + +bool wlan_sap_is_pre_cac_active(mac_handle_t handle); +QDF_STATUS wlan_sap_get_pre_cac_vdev_id(mac_handle_t handle, uint8_t *vdev_id); +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * wlansap_check_cc_intf() - Get interfering concurrent channel + * @sap_ctx: SAP context pointer + * + * Determine if a concurrent channel is interfering. + * + * Return: Channel freq (Mhz) of the interfering channel, or 0 if none. + */ +uint16_t wlansap_check_cc_intf(struct sap_context *sap_ctx); +#endif + +/** + * wlansap_set_mac_acl() - set MAC list entry in ACL. + * @sap_ctx: Pointer to the SAP context + * @config: Pointer to SAP config. + * + * This api function provides SAP to set mac list entry in accept list as well + * as deny list + * + * Return: The result code associated with performing the operation + * QDF_STATUS_E_FAULT: Pointer to SAP cb is NULL; + * access would cause a page fault + * QDF_STATUS_SUCCESS: Success + */ +QDF_STATUS wlansap_set_mac_acl(struct sap_context *sap_ctx, + struct sap_config *config); + +/** + * wlansap_disassoc_sta() - initiate disassociation of station. + * @sap_ctx: Pointer to the SAP context + * @p_del_sta_params: pointer to station deletion parameters + * + * This api function provides for Ap App/HDD initiated disassociation of station + * + * Return: The QDF_STATUS code associated with performing the operation + * QDF_STATUS_SUCCESS: Success + */ +QDF_STATUS wlansap_disassoc_sta(struct sap_context *sap_ctx, + struct csr_del_sta_params *p_del_sta_params); + +/** + * wlansap_deauth_sta() - Ap App/HDD initiated deauthentication of station + * @sap_ctx: Pointer to the SAP context + * @pDelStaParams: Pointer to parameters of the station to deauthenticate + * + * This api function provides for Ap App/HDD initiated deauthentication of + * station + * + * Return: The QDF_STATUS code associated with performing the operation + */ +QDF_STATUS wlansap_deauth_sta(struct sap_context *sap_ctx, + struct csr_del_sta_params *pDelStaParams); + +/** + * wlansap_set_channel_change_with_csa() - Set channel change with CSA + * @sap_ctx: Pointer to SAP context + * @target_chan_freq: Target channel frequncy + * @target_bw: Target bandwidth + * @strict: if true switch to the requested channel always, fail + * otherwise + * + * This api function does a channel change to the target channel specified. + * CSA IE is included in the beacons before doing a channel change. + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_set_channel_change_with_csa(struct sap_context *sap_ctx, + uint32_t target_chan_freq, + enum phy_ch_width target_bw, + bool strict); + + +/** + * wlan_sap_getstation_ie_information() - RSNIE Population + * @sap_ctx: Pointer to the SAP context + * @len: Length of @buf + * @buf: RSNIE IE data + * + * Populate RSN IE from CSR to HDD context + * + * Return: QDF_STATUS enumeration + */ + +QDF_STATUS wlan_sap_getstation_ie_information(struct sap_context *sap_ctx, + uint32_t *len, uint8_t *buf); + +/** + * wlansap_clear_acl() - Clear all ACLs + * @sap_ctx: Pointer to the SAP context + * + * Return: QDF_STATUS. If success the ACLs were cleared, otherwise an + * error occurred. + */ +QDF_STATUS wlansap_clear_acl(struct sap_context *sap_ctx); + +/** + * wlansap_get_acl_accept_list() - Get ACL accept list + * @sap_ctx: Pointer to the SAP context + * @pAcceptList: Pointer to the buffer to store the ACL accept list + * @nAcceptList: Pointer to the location to store the number of + * entries in the ACL accept list. + * + * Return: QDF_STATUS. If success the data was returned, otherwise an + * error occurred. + */ +QDF_STATUS wlansap_get_acl_accept_list(struct sap_context *sap_ctx, + struct qdf_mac_addr *pAcceptList, + uint8_t *nAcceptList); + +/** + * wlansap_is_channel_present_in_acs_list() - Freq present in ACS list or not + * @freq: Frequency to be searched + * @ch_freq_list: channel frequency list. + * @ch_count: Channel frequency list count + * + * Return: True is found, false otherwise + */ +bool wlansap_is_channel_present_in_acs_list(uint32_t freq, + uint32_t *ch_freq_list, + uint8_t ch_count); + +/** + * wlansap_get_acl_deny_list() - Get ACL deny list + * @sap_ctx: Pointer to the SAP context + * @pDenyList: Pointer to the buffer to store the ACL deny list + * @nDenyList: Pointer to the location to store the number of + * entries in the ACL deny list. + * + * Return: QDF_STATUS. If success the data was returned, otherwise an + * error occurred. + */ +QDF_STATUS wlansap_get_acl_deny_list(struct sap_context *sap_ctx, + struct qdf_mac_addr *pDenyList, + uint8_t *nDenyList); + +/** + * wlansap_set_acl_mode() - Set the SAP ACL mode + * @sap_ctx: The SAP context pointer + * @mode: the desired ACL mode + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_set_acl_mode(struct sap_context *sap_ctx, + eSapMacAddrACL mode); + +/** + * wlansap_get_acl_mode() - Get the SAP ACL mode + * @sap_ctx: The SAP context pointer + * @mode: Pointer where to return the current ACL mode + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_get_acl_mode(struct sap_context *sap_ctx, + eSapMacAddrACL *mode); + +/** + * wlansap_modify_acl() - Update ACL entries + * @sap_ctx: Pointer to the SAP context + * @peer_sta_mac: peer sta mac to be updated. + * @list_type: white/Black list type. + * @cmd: command to be executed on ACL. + * + * This function is called when a peer needs to be added or deleted from the + * white/black ACL + * + * Return: Status + */ +QDF_STATUS wlansap_modify_acl(struct sap_context *sap_ctx, + uint8_t *peer_sta_mac, + eSapACLType list_type, eSapACLCmdType cmd); + +/** + * wlansap_channel_change_request() - Send channel change request + * @sap_ctx: Pointer to the SAP context + * @target_channel: Target channel + * + * This API is used to send an Indication to SME/PE to change the + * current operating channel to a different target channel. + * + * The Channel change will be issued by SAP under the following + * scenarios. + * 1. A radar indication is received during SAP CAC WAIT STATE and + * channel change is required. + * 2. A radar indication is received during SAP STARTED STATE and + * channel change is required. + * + * Return: The QDF_STATUS code associated with performing the operation + * QDF_STATUS_SUCCESS: Success + * + */ +QDF_STATUS wlansap_channel_change_request(struct sap_context *sap_ctx, + uint32_t target_chan_freq); + +/** + * wlansap_get_sec_channel() - get the secondary sap channel + * @sec_ch_offset: secondary channel offset. + * @op_chan_freq: Operating sap channel frequency. + * @sec_chan_freq: channel frequency to be filled. + * + * This API will get the secondary sap channel from the offset, and + * operating channel. + * + * Return: None + * + */ +void wlansap_get_sec_channel(uint8_t sec_ch_offset, + uint32_t op_chan_freq, + uint32_t *sec_chan_freq); + +/** + * wlansap_start_beacon_req() - Send Start Beaconing Request + * @sap_ctx: Pointer to the SAP context + * + * This API is used to send an Indication to SME/PE to start + * beaconing on the current operating channel. + * + * When SAP is started on DFS channel and when ADD BSS RESP is received + * LIM temporarily holds off Beaconing for SAP to do CAC WAIT. When + * CAC WAIT is done SAP resumes the Beacon Tx by sending a start beacon + * request to LIM. + * + * Return: The QDF_STATUS code associated with performing the operation + * QDF_STATUS_SUCCESS: Success + */ +QDF_STATUS wlansap_start_beacon_req(struct sap_context *sap_ctx); + +/** + * wlansap_dfs_send_csa_ie_request() - Send CSA IE + * @sap_ctx: Pointer to the SAP context + * + * This API is used to send channel switch announcement request to PE + * + * Return: The QDF_STATUS code associated with performing the operation + * QDF_STATUS_SUCCESS: Success + */ +QDF_STATUS wlansap_dfs_send_csa_ie_request(struct sap_context *sap_ctx); + +/** + * wlansap_get_dfs_ignore_cac() - Get ignore_cac value + * @mac_handle: Opaque handle to the global MAC context + * @ignore_cac: Location to store ignore_cac value + * + * This API is used to Get the value of ignore_cac value + * + * Return: The QDF_STATUS code associated with performing the operation + */ +QDF_STATUS wlansap_get_dfs_ignore_cac(mac_handle_t mac_handle, + uint8_t *ignore_cac); + +/** + * wlansap_set_dfs_ignore_cac() - Set ignore_cac value + * @mac_handle: Opaque handle to the global MAC context + * @ignore_cac: value to set for ignore_cac variable in DFS global structure. + * + * This API is used to Set the value of ignore_cac value + * + * Return: The QDF_STATUS code associated with performing the operation + */ +QDF_STATUS wlansap_set_dfs_ignore_cac(mac_handle_t mac_handle, + uint8_t ignore_cac); + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +QDF_STATUS +wlan_sap_set_channel_avoidance(mac_handle_t mac_handle, + bool sap_channel_avoidance); +#endif + +/** + * wlan_sap_set_acs_with_more_param() - sets acs_with_more_param ini param + * @mac_handle: Opaque handle to the global MAC context + * @acs_with_more_param: ini parameter value + * + * Return: The QDF_STATUS code. + */ +QDF_STATUS +wlan_sap_set_acs_with_more_param(mac_handle_t mac_handle, + bool acs_with_more_param); + +/** + * wlansap_set_dfs_preferred_channel_location() - set dfs preferred channel + * @mac_handle: Opaque handle to the global MAC context + * + * This API is used to set sap preferred channels location + * to resetrict the DFS random channel selection algorithm + * either Indoor/Outdoor channels only. + * dfs_Preferred_Channels_location : + * 0 - Indicates No preferred channel location restrictions + * 1 - Indicates SAP Indoor Channels operation only. + * 2 - Indicates SAP Outdoor Channels operation only. + * + * Return: The QDF_STATUS code associated with performing the operation + * QDF_STATUS_SUCCESS: Success and error code otherwise. + */ +QDF_STATUS wlansap_set_dfs_preferred_channel_location(mac_handle_t mac_handle); + +/** + * wlansap_set_dfs_target_chnl() - Set target channel + * @mac_handle: Opaque handle for the global MAC context + * @target_chan_freq: target channel frequency to be set + * + * This API is used to set next target chnl as provided channel. + * you can provide any valid channel to this API. + * + * Return: The QDF_STATUS code associated with performing the operation + */ +QDF_STATUS wlansap_set_dfs_target_chnl(mac_handle_t mac_handle, + uint32_t target_chan_freq); + +/** + * wlan_sap_get_roam_profile() - Returns sap roam profile. + * @sap_ctx: Pointer to Sap Context. + * + * This function provides the SAP roam profile. + * + * Return: SAP RoamProfile + */ +struct csr_roam_profile *wlan_sap_get_roam_profile(struct sap_context *sap_ctx); + +/** + * wlan_sap_get_phymode() - Returns sap phymode. + * @sap_ctx: Pointer to Sap Context. + * + * This function provides the SAP current phymode. + * + * Return: phymode + */ +eCsrPhyMode wlan_sap_get_phymode(struct sap_context *sap_ctx); + +/** + * wlan_sap_get_vht_ch_width() - Returns SAP VHT channel width. + * @sap_ctx: Pointer to Sap Context + * + * This function provides the SAP current VHT channel with. + * + * Return: VHT channel width + */ +uint32_t wlan_sap_get_vht_ch_width(struct sap_context *sap_ctx); + +/** + * wlan_sap_set_vht_ch_width() - Sets SAP VHT channel width. + * @sap_ctx: Pointer to Sap Context + * @vht_channel_width: SAP VHT channel width value. + * + * This function sets the SAP current VHT channel width. + * + * Return: None + */ +void wlan_sap_set_vht_ch_width(struct sap_context *sap_ctx, + uint32_t vht_channel_width); + +/** + * wlan_sap_get_ch_params() - get ch params + * @sap_ctx: Pointer to Sap Context + * @ch_params: returned ch_params + * + * This function get sap's ch_params + * + * Return: true for success + */ +bool wlan_sap_get_ch_params(struct sap_context *sap_ctx, + struct ch_params *ch_params); + +/** + * wlan_sap_set_sap_ctx_acs_cfg() - Sets acs cfg + * @sap_ctx: Pointer to Sap Context + * @sap_config: Pointer to sap config + * + * This function sets the acs cfg in sap context. + * + * Return: None + */ +void wlan_sap_set_sap_ctx_acs_cfg(struct sap_context *sap_ctx, + struct sap_config *sap_config); + +void sap_config_acs_result(mac_handle_t mac_handle, + struct sap_context *sap_ctx, + uint32_t sec_ch_freq); + +QDF_STATUS wlansap_update_sap_config_add_ie(struct sap_config *config, + const uint8_t *pAdditionIEBuffer, + uint16_t additionIELength, + eUpdateIEsType updateType); + +QDF_STATUS wlansap_reset_sap_config_add_ie(struct sap_config *config, + eUpdateIEsType updateType); + +void wlansap_extend_to_acs_range(mac_handle_t mac_handle, + uint32_t *start_ch_freq, + uint32_t *end_ch_freq, + uint32_t *bandStartChannel, + uint32_t *bandEndChannel); + +/** + * wlansap_set_dfs_nol() - Set dfs nol + * @sap_ctx: SAP context + * @conf: set type + * + * Return: QDF_STATUS + */ +#ifdef DFS_COMPONENT_ENABLE +QDF_STATUS wlansap_set_dfs_nol(struct sap_context *sap_ctx, + eSapDfsNolType conf); +#else +static inline QDF_STATUS wlansap_set_dfs_nol(struct sap_context *sap_ctx, + eSapDfsNolType conf) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * wlan_sap_set_dfs_pri_multiplier() - Set dfs_pri_multiplier + * @mac_handle: Opaque handle to the global MAC context + * + * Return: none + */ +#ifdef DFS_PRI_MULTIPLIER +void wlan_sap_set_dfs_pri_multiplier(mac_handle_t mac_handle); +#else +static inline void wlan_sap_set_dfs_pri_multiplier(mac_handle_t mac_handle) +{ +} +#endif + +/** + * wlan_sap_set_vendor_acs() - Set vendor specific acs in sap context + * @sap_context: SAP context + * @is_vendor_acs: if vendor specific acs is enabled + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_sap_set_vendor_acs(struct sap_context *sap_context, + bool is_vendor_acs); + +/** + * wlansap_populate_del_sta_params() - populate delete station parameter + * @mac: Pointer to peer mac address. + * @reason_code: Reason code for the disassoc/deauth. + * @subtype: Subtype points to either disassoc/deauth frame. + * @params: Parameters to be populated. + * + * This API is used to populate delete station parameter structure + * + * Return: none + */ +void wlansap_populate_del_sta_params(const uint8_t *mac, + uint16_t reason_code, + uint8_t subtype, + struct csr_del_sta_params *params); + +/** + * wlansap_acs_chselect() - Initiates acs channel selection + * @sap_context: Pointer to SAP context structure + * @acs_event_callback: Callback function in hdd called by sap + * to inform hdd about channel selection result + * @config: Pointer to configuration structure + * passed down from hdd + * @pusr_context: Parameter that will be passed back in all + * the sap callback events. + * + * This function serves as an api for hdd to initiate acs scan pre + * start bss. + * + * Return: The QDF_STATUS code associated with performing the operation. + */ +QDF_STATUS wlansap_acs_chselect(struct sap_context *sap_context, + sap_event_cb acs_event_callback, + struct sap_config *config, + void *pusr_context); + +/** + * sap_undo_acs() - Undo acs i.e free the allocated ch lists + * @sap_ctx: pointer to the SAP context + * + * This function will free the memory allocated to the sap ctx channel list, acs + * cfg ch list and master ch list. + * + * Return: None + */ +void sap_undo_acs(struct sap_context *sap_context, struct sap_config *sap_cfg); + +/** + * wlansap_get_chan_width() - get sap channel width. + * @sap_ctx: pointer to the SAP context + * + * This function get channel width of sap. + * + * Return: sap channel width + */ +uint32_t wlansap_get_chan_width(struct sap_context *sap_ctx); + +/* + * wlansap_set_invalid_session() - set session ID to invalid + * @sap_ctx: pointer to the SAP context + * + * This function sets session ID to invalid + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_set_invalid_session(struct sap_context *sap_ctx); + +/* + * wlansap_set_invalid_session() - Release vdev ref taken by sap context + * @sap_ctx: pointer to the SAP context + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_release_vdev_ref(struct sap_context *sap_ctx); + +/** + * sap_get_cac_dur_dfs_region() - get cac duration and dfs region. + * @sap_ctxt: sap context + * @cac_duration_ms: pointer to cac duration + * @dfs_region: pointer to dfs region + * + * Get cac duration and dfs region. + * + * Return: None + */ +void sap_get_cac_dur_dfs_region(struct sap_context *sap_ctx, + uint32_t *cac_duration_ms, + uint32_t *dfs_region); + + +/** + * sap_dfs_set_current_channel() - Set current channel params in dfs component + * @sap_ctx: sap context + * + * Set current channel params in dfs component, this info will be used to mark + * the channels in nol when radar is detected. + * + * Return: None + */ +void sap_dfs_set_current_channel(void *sap_ctx); + +/** + * wlansap_cleanup_cac_timer() - Force cleanup DFS CAC timer + * @sap_ctx: sap context + * + * Force cleanup DFS CAC timer when reset all adapters. It will not + * check concurrency SAP since just called when reset all adapters. + * + * Return: None + */ +void wlansap_cleanup_cac_timer(struct sap_context *sap_ctx); + +/** + * wlansap_update_owe_info() - Update OWE info + * @sap_ctx: sap context + * @peer: peer mac + * @ie: IE from hostapd + * @ie_len: IE length + * @owe_status: status from hostapd + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_update_owe_info(struct sap_context *sap_ctx, + uint8_t *peer, const uint8_t *ie, + uint32_t ie_len, uint16_t owe_status); + +/** + * wlansap_filter_ch_based_acs() -filter out channel based on acs + * @sap_ctx: sap context + * @ch_freq_list: pointer to channel frequency list + * @ch_cnt: channel number of channel list + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_filter_ch_based_acs(struct sap_context *sap_ctx, + uint32_t *ch_freq_list, + uint32_t *ch_cnt); + +/** + * wlansap_is_6ghz_included_in_acs_range() - check 6ghz channel included in + * ACS range + * @sap_ctx: sap context + * + * Return: QDF_STATUS + */ +bool wlansap_is_6ghz_included_in_acs_range(struct sap_context *sap_ctx); + +/** + * wlansap_get_safe_channel_from_pcl_and_acs_range() - Get safe channel for SAP + * restart + * @sap_ctx: sap context + * + * Get a safe channel to restart SAP. PCL already takes into account the + * unsafe channels. So, the PCL is validated with the ACS range to provide + * a safe channel for the SAP to restart. + * + * Return: Chan freq num to restart SAP in case of success. In case of any + * failure, the channel number returned is zero. + */ +uint32_t +wlansap_get_safe_channel_from_pcl_and_acs_range(struct sap_context *sap_ctx); + +/** + * wlansap_get_chan_band_restrict() - get new chan for band change + * @sap_ctx: sap context pointer + * + * Sap/p2p go channel switch from 5G to 2G by CSA when 5G band disabled to + * avoid conflict with modem N79. + * Sap/p2p go channel restore to 5G channel when 5G band enabled. + * + * Return - restart channel in MHZ + */ +qdf_freq_t wlansap_get_chan_band_restrict(struct sap_context *sap_ctx); + +/** + * sap_acquire_vdev_ref() - Increment reference count for vdev object + * @psoc: Object Manager PSoC object + * @sap_ctx: to store vdev object pointer + * @session_id: used to get vdev object + * + * This function is used to increment vdev object reference count and store + * vdev pointer in sap_ctx. + * + * Return: QDF_STATUS_SUCCESS - If able to get vdev object reference + * else qdf status failure codes + */ +QDF_STATUS sap_acquire_vdev_ref(struct wlan_objmgr_psoc *psoc, + struct sap_context *sap_ctx, + uint8_t session_id); + +/** + * sap_dump_acs_channel() - dump acs channel list + * @acs_cfg: acs config + * + * This function dump acs channel list + * + * Return: void. + */ +void sap_dump_acs_channel(struct sap_acs_cfg *acs_cfg); + +/** + * sap_release_vdev_ref() - Decrement reference count for vdev object + * @sap_ctx: for which vdev reference is to be decremented + * + * Return: None + */ +void sap_release_vdev_ref(struct sap_context *sap_ctx); +#ifdef __cplusplus +} +#endif +#endif /* #ifndef WLAN_QCT_WLANSAP_H */ diff --git a/drivers/staging/qcacld-3.0/core/sap/src/sap_api_link_cntl.c b/drivers/staging/qcacld-3.0/core/sap/src/sap_api_link_cntl.c new file mode 100644 index 0000000000000000000000000000000000000000..f1ff2232f9ac22d28d79d8ba5299c613f2ea9e20 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sap/src/sap_api_link_cntl.c @@ -0,0 +1,1390 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*=========================================================================== + + s a p A p i L i n k C n t l . C + + OVERVIEW: + + This software unit holds the implementation of the WLAN SAP modules + Link Control functions. + + The functions externalized by this module are to be called ONLY by other + WLAN modules (HDD) + + DEPENDENCIES: + + Are listed for each API below. + ===========================================================================*/ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ +#include "qdf_trace.h" +/* Pick up the CSR callback definition */ +#include "csr_api.h" +#include "ani_global.h" +#include "csr_inside_api.h" +#include "sme_api.h" +/* SAP Internal API header file */ +#include "sap_internal.h" +#include "wlan_policy_mgr_api.h" +#include "wma.h" +#include +#include +#include "wlan_reg_services_api.h" +#include +#include + +/*---------------------------------------------------------------------------- + * Preprocessor Definitions and Constants + * -------------------------------------------------------------------------*/ +#define SAP_DEBUG + +/*---------------------------------------------------------------------------- + * Type Declarations + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Global Data Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Static Variable Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Static Function Declarations and Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Externalized Function Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Function Declarations and Documentation + * -------------------------------------------------------------------------*/ + +/** + * sap_config_acs_result : Generate ACS result params based on ch constraints + * @sap_ctx: pointer to SAP context data struct + * @mac_handle: Opaque handle to the global MAC context + * @sec_ch: Secondary channel + * + * This function calculates the ACS result params: ht sec channel, vht channel + * information and channel bonding based on selected ACS channel. + * + * Return: None + */ + +void sap_config_acs_result(mac_handle_t mac_handle, + struct sap_context *sap_ctx, + uint32_t sec_ch_freq) +{ + struct ch_params ch_params = {0}; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + ch_params.ch_width = sap_ctx->acs_cfg->ch_width; + wlan_reg_set_channel_params_for_freq( + mac_ctx->pdev, sap_ctx->acs_cfg->pri_ch_freq, + sec_ch_freq, &ch_params); + sap_ctx->acs_cfg->ch_width = ch_params.ch_width; + if (sap_ctx->acs_cfg->ch_width > CH_WIDTH_40MHZ || + WLAN_REG_IS_6GHZ_CHAN_FREQ(sap_ctx->acs_cfg->pri_ch_freq)) + sap_ctx->acs_cfg->vht_seg0_center_ch_freq = + ch_params.mhz_freq_seg0; + else + sap_ctx->acs_cfg->vht_seg0_center_ch_freq = 0; + + if (sap_ctx->acs_cfg->ch_width == CH_WIDTH_80P80MHZ || + (sap_ctx->acs_cfg->ch_width == CH_WIDTH_160MHZ)) + sap_ctx->acs_cfg->vht_seg1_center_ch_freq = + ch_params.mhz_freq_seg1; + else + sap_ctx->acs_cfg->vht_seg1_center_ch_freq = 0; + + if (ch_params.sec_ch_offset == PHY_DOUBLE_CHANNEL_HIGH_PRIMARY) + sap_ctx->acs_cfg->ht_sec_ch_freq = + sap_ctx->acs_cfg->pri_ch_freq - 20; + else if (ch_params.sec_ch_offset == PHY_DOUBLE_CHANNEL_LOW_PRIMARY) + sap_ctx->acs_cfg->ht_sec_ch_freq = + sap_ctx->acs_cfg->pri_ch_freq + 20; + else + sap_ctx->acs_cfg->ht_sec_ch_freq = 0; +} + +/** + * sap_hdd_signal_event_handler() - routine to inform hostapd via callback + * + * ctx: pointer to sap context which was passed to callback + * + * this routine will be registered as callback to sme_close_session, so upon + * closure of sap session it notifies the hostapd + * + * Return: QDF_STATUS + */ +static QDF_STATUS sap_hdd_signal_event_handler(void *ctx) +{ + struct sap_context *sap_ctx = ctx; + QDF_STATUS status; + + if (!sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("sap context is not valid")); + return QDF_STATUS_E_FAILURE; + } + status = sap_signal_hdd_event(sap_ctx, NULL, + sap_ctx->sap_state, + (void *) sap_ctx->sap_status); + return status; +} + +/** + * acs_scan_done_status_str() - parse scan status to string + * @status: scan status + * + * This function parse scan status to string + * + * Return: status string + * + */ +static const char *acs_scan_done_status_str(eCsrScanStatus status) +{ + switch (status) { + case eCSR_SCAN_SUCCESS: + return "Success"; + case eCSR_SCAN_FAILURE: + return "Failure"; + case eCSR_SCAN_ABORT: + return "Abort"; + case eCSR_SCAN_FOUND_PEER: + return "Found peer"; + default: + return "Unknown"; + } +} + +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE +static void wlansap_send_acs_success_event(struct sap_context *sap_ctx, + uint32_t scan_id) +{ + if (scan_id) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Sending ACS Scan skip event")); + sap_signal_hdd_event(sap_ctx, NULL, + eSAP_ACS_SCAN_SUCCESS_EVENT, + (void *)eSAP_STATUS_SUCCESS); + } else { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("ACS scanid: %d (skipped ACS SCAN)"), + scan_id); + } +} +#else +static inline void wlansap_send_acs_success_event(struct sap_context *sap_ctx, + uint32_t scan_id) +{ +} +#endif + +static uint32_t +wlansap_calculate_chan_from_scan_result(mac_handle_t mac_handle, + struct sap_context *sap_ctx, + uint32_t scan_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + qdf_list_t *list = NULL; + struct scan_filter *filter; + uint32_t oper_channel = SAP_CHANNEL_NOT_SELECTED; + + filter = qdf_mem_malloc(sizeof(*filter)); + + if (filter) + filter->age_threshold = qdf_get_time_of_the_day_ms() - + sap_ctx->acs_req_timestamp; + + list = ucfg_scan_get_result(mac_ctx->pdev, filter); + + if (filter) + qdf_mem_free(filter); + + if (list) + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("num_entries %d"), qdf_list_size(list)); + + wlansap_send_acs_success_event(sap_ctx, scan_id); + + oper_channel = sap_select_channel(mac_handle, sap_ctx, list); + ucfg_scan_purge_results(list); + + return oper_channel; +} + +static void +wlansap_filter_unsafe_ch(struct wlan_objmgr_psoc *psoc, + struct sap_context *sap_ctx) +{ + uint16_t i; + uint16_t num_safe_ch = 0; + + /* + * There are two channel list, one acs cfg channel list, and one + * sap_ctx->freq_list, the unsafe channels for acs cfg is updated here + * and the sap_ctx->freq list would be handled in sap_chan_sel_init + * which would consider more params other than unsafe channels. + * So the two lists now would be in sync. But in case the ACS weight + * calculation does not get through due to no scan result or no chan + * selected, or any other reason, the default channel is chosen which + * would contain the channels in acs cfg. Now since the scan takes time + * there could be channels present in acs cfg that could become unsafe + * in the mean time, so it is better to filter out those channels from + * the acs channel list before chosing one of them as a default channel + */ + for (i = 0; i < sap_ctx->acs_cfg->ch_list_count; i++) { + if (!policy_mgr_is_safe_channel( + psoc, sap_ctx->acs_cfg->freq_list[i])) { + sap_debug("unsafe freq %d removed from acs list", + sap_ctx->acs_cfg->freq_list[i]); + continue; + } + /* Add only safe channels to the acs cfg ch list */ + sap_ctx->acs_cfg->freq_list[num_safe_ch++] = + sap_ctx->acs_cfg->freq_list[i]; + } + + sap_debug("Updated ACS ch list len %d", num_safe_ch); + sap_ctx->acs_cfg->ch_list_count = num_safe_ch; +} + +static void +wlan_sap_filter_non_preferred_channels(struct wlan_objmgr_pdev *pdev, + struct sap_context *sap_ctx) +{ + uint16_t i; + uint16_t num_ch = 0; + bool preferred_freq_found = false; + + for (i = 0; i < sap_ctx->acs_cfg->ch_list_count; i++) { + if (sap_ctx->acs_cfg->freq_list[i] == 2467 || + sap_ctx->acs_cfg->freq_list[i] == 2472 || + sap_ctx->acs_cfg->freq_list[i] == 2477) { + sap_debug("Skip freq %d if preferred freq present", + sap_ctx->acs_cfg->freq_list[i]); + continue; + } + sap_ctx->acs_cfg->freq_list[num_ch++] = + sap_ctx->acs_cfg->freq_list[i]; + preferred_freq_found = true; + } + + if (!preferred_freq_found) { + sap_debug("No preferred freq, list unchanged"); + return; + } + sap_debug("preferred frequencies found updated ACS ch list len %d", + num_ch); + sap_ctx->acs_cfg->ch_list_count = num_ch; +} + +QDF_STATUS wlansap_pre_start_bss_acs_scan_callback(mac_handle_t mac_handle, + struct sap_context *sap_ctx, + uint8_t sessionid, + uint32_t scanid, + eCsrScanStatus scan_status) +{ + uint32_t oper_channel = SAP_CHANNEL_NOT_SELECTED; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + host_log_acs_scan_done(acs_scan_done_status_str(scan_status), + sessionid, scanid); + + /* This has to be done before the ACS selects default channel */ + wlansap_filter_unsafe_ch(mac_ctx->psoc, sap_ctx); + + wlan_sap_filter_non_preferred_channels(mac_ctx->pdev, sap_ctx); + if (!sap_ctx->acs_cfg->ch_list_count) { + oper_channel = + sap_select_default_oper_chan(mac_ctx, + sap_ctx->acs_cfg); + sap_ctx->chan_freq = oper_channel; + sap_ctx->acs_cfg->pri_ch_freq = oper_channel; + sap_config_acs_result(mac_handle, sap_ctx, + sap_ctx->acs_cfg->ht_sec_ch_freq); + sap_ctx->sap_state = eSAP_ACS_CHANNEL_SELECTED; + sap_ctx->sap_status = eSAP_STATUS_SUCCESS; + goto close_session; + } + if (eCSR_SCAN_SUCCESS != scan_status) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("CSR scan_status = eCSR_SCAN_ABORT/FAILURE (%d), choose default channel"), + scan_status); + oper_channel = + sap_select_default_oper_chan(mac_ctx, + sap_ctx->acs_cfg); + sap_ctx->chan_freq = oper_channel; + sap_ctx->acs_cfg->pri_ch_freq = oper_channel; + sap_config_acs_result(mac_handle, sap_ctx, + sap_ctx->acs_cfg->ht_sec_ch_freq); + sap_ctx->sap_state = eSAP_ACS_CHANNEL_SELECTED; + sap_ctx->sap_status = eSAP_STATUS_SUCCESS; + goto close_session; + } + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR scan_status = eCSR_SCAN_SUCCESS (%d)"), scan_status); + + oper_channel = wlansap_calculate_chan_from_scan_result(mac_handle, + sap_ctx, scanid); + + if (oper_channel == SAP_CHANNEL_NOT_SELECTED) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO, + FL("No suitable channel, so select default channel")); + oper_channel = sap_select_default_oper_chan(mac_ctx, + sap_ctx->acs_cfg); + } + + sap_ctx->chan_freq = oper_channel; + sap_ctx->acs_cfg->pri_ch_freq = oper_channel; + sap_config_acs_result(mac_handle, sap_ctx, + sap_ctx->acs_cfg->ht_sec_ch_freq); + + sap_ctx->sap_state = eSAP_ACS_CHANNEL_SELECTED; + sap_ctx->sap_status = eSAP_STATUS_SUCCESS; +close_session: +#ifdef SOFTAP_CHANNEL_RANGE + if (sap_ctx->freq_list) { + /* + * Always free up the memory for + * channel selection whatever + * the result + */ + qdf_mem_free(sap_ctx->freq_list); + sap_ctx->freq_list = NULL; + sap_ctx->num_of_channel = 0; + } +#endif + sap_hdd_signal_event_handler(sap_ctx); + + return QDF_STATUS_SUCCESS; +} + +/** + * wlansap_roam_process_ch_change_success() - handles the case for + * eCSR_ROAM_RESULT_CHANNEL_CHANGE_SUCCESS in function wlansap_roam_callback() + * + * @mac_ctx: mac global context + * @sap_ctx: sap context + * @csr_roam_info: raom info struct + * @ret_status: update return status + * + * Return: void + */ +static void +wlansap_roam_process_ch_change_success(struct mac_context *mac_ctx, + struct sap_context *sap_ctx, + struct csr_roam_info *csr_roam_info, + QDF_STATUS *ret_status) +{ + struct sap_sm_event sap_event; + QDF_STATUS qdf_status; + bool is_ch_dfs = false; + uint32_t target_chan_freq; + /* + * Channel change is successful. If the new channel is a DFS channel, + * then we will to perform channel availability check for 60 seconds + */ + sap_nofl_debug("sapdfs: SAP CSA: freq to [%d] state %d", + mac_ctx->sap.SapDfsInfo.target_chan_freq, + sap_ctx->fsm_state); + target_chan_freq = mac_ctx->sap.SapDfsInfo.target_chan_freq; + + /* If SAP is not in starting or started state don't proceed further */ + if (sap_ctx->fsm_state == SAP_INIT || + sap_ctx->fsm_state == SAP_STOPPING) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO, + FL("sapdfs: state [%d] not starting SAP after channel change"), + sap_ctx->fsm_state); + return; + } + + if (sap_ctx->ch_params.ch_width == CH_WIDTH_160MHZ) { + is_ch_dfs = true; + } else if (sap_ctx->ch_params.ch_width == CH_WIDTH_80P80MHZ) { + if (wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + target_chan_freq) == + CHANNEL_STATE_DFS || + wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + sap_ctx->ch_params.mhz_freq_seg1) == + CHANNEL_STATE_DFS) + is_ch_dfs = true; + } else { + if (wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + target_chan_freq) == + CHANNEL_STATE_DFS) + is_ch_dfs = true; + } + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(sap_ctx->chan_freq)) + is_ch_dfs = false; + + sap_ctx->chan_freq = target_chan_freq; + /* check if currently selected channel is a DFS channel */ + if (is_ch_dfs && sap_ctx->pre_cac_complete) { + /* Start beaconing on the new pre cac channel */ + wlansap_start_beacon_req(sap_ctx); + sap_ctx->fsm_state = SAP_STARTING; + mac_ctx->sap.SapDfsInfo.sap_radar_found_status = false; + sap_event.event = eSAP_MAC_START_BSS_SUCCESS; + sap_event.params = csr_roam_info; + sap_event.u1 = eCSR_ROAM_INFRA_IND; + sap_event.u2 = eCSR_ROAM_RESULT_INFRA_STARTED; + } else if (is_ch_dfs) { + if ((false == mac_ctx->sap.SapDfsInfo.ignore_cac) + && (eSAP_DFS_DO_NOT_SKIP_CAC == + mac_ctx->sap.SapDfsInfo.cac_state) && + policy_mgr_get_dfs_master_dynamic_enabled( + mac_ctx->psoc, + sap_ctx->sessionId)) { + sap_ctx->fsm_state = SAP_INIT; + /* DFS Channel */ + sap_event.event = eSAP_DFS_CHANNEL_CAC_START; + sap_event.params = csr_roam_info; + sap_event.u1 = 0; + sap_event.u2 = 0; + } else { + /* Start beaconing on the new channel */ + wlansap_start_beacon_req(sap_ctx); + sap_ctx->fsm_state = SAP_STARTING; + mac_ctx->sap.SapDfsInfo.sap_radar_found_status = false; + sap_event.event = eSAP_MAC_START_BSS_SUCCESS; + sap_event.params = csr_roam_info; + sap_event.u1 = eCSR_ROAM_INFRA_IND; + sap_event.u2 = eCSR_ROAM_RESULT_INFRA_STARTED; + } + } else { + /* non-DFS channel */ + sap_ctx->fsm_state = SAP_STARTING; + mac_ctx->sap.SapDfsInfo.sap_radar_found_status = false; + sap_event.event = eSAP_MAC_START_BSS_SUCCESS; + sap_event.params = csr_roam_info; + sap_event.u1 = eCSR_ROAM_INFRA_IND; + sap_event.u2 = eCSR_ROAM_RESULT_INFRA_STARTED; + } + + /* Handle the event */ + qdf_status = sap_fsm(sap_ctx, &sap_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + *ret_status = QDF_STATUS_E_FAILURE; +} + +/** + * wlansap_roam_process_dfs_chansw_update() - handles the case for + * eCSR_ROAM_RESULT_DFS_CHANSW_UPDATE_SUCCESS in wlansap_roam_callback() + * + * @mac_handle: opaque handle to the global MAC context + * @sap_ctx: sap context + * @ret_status: update return status + * + * Return: void + */ +static void +wlansap_roam_process_dfs_chansw_update(mac_handle_t mac_handle, + struct sap_context *sap_ctx, + QDF_STATUS *ret_status) +{ + uint8_t intf; + QDF_STATUS qdf_status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t dfs_beacon_start_req = 0; + bool sap_scc_dfs; + + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_disable_channel_switch) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + FL("sapdfs: DFS channel switch disabled")); + /* + * Send a beacon start request to PE. CSA IE required flag from + * beacon template will be cleared by now. A new beacon template + * with no CSA IE will be sent to firmware. + */ + dfs_beacon_start_req = true; + sap_ctx->pre_cac_complete = false; + *ret_status = sme_roam_start_beacon_req(mac_handle, + sap_ctx->bssid, + dfs_beacon_start_req); + return; + } + /* + * Irrespective of whether the channel switch IE was sent out + * successfully or not, SAP should still vacate the channel immediately + */ + if (sap_ctx->fsm_state != SAP_STARTED) { + /* Further actions to be taken here */ + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_WARN, + FL("eCSR_ROAM_RESULT_DFS_RADAR_FOUND_IND received in (%d) state"), + sap_ctx->fsm_state); + return; + } + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + FL("sapdfs: from state SAP_STARTED => SAP_STOPPING")); + sap_ctx->is_chan_change_inprogress = true; + /* + * The associated stations have been informed to move to a different + * channel. However, the AP may not always select the advertised channel + * for operation if the radar is seen. In that case, the stations will + * experience link-loss and return back through scanning if they wish to + */ + + /* + * Send channel change request. From spec it is required that the AP + * should continue to operate in the same mode as it is operating + * currently. For e.g. 20/40/80 MHz operation + */ + if (mac_ctx->sap.SapDfsInfo.target_chan_freq) + wlan_reg_set_channel_params_for_freq(mac_ctx->pdev, + mac_ctx->sap.SapDfsInfo.target_chan_freq, + 0, &sap_ctx->ch_params); + + /* + * Fetch the number of SAP interfaces. If the number of sap Interface + * more than one then we will make is_sap_ready_for_chnl_chng to true + * for that sapctx. If there is only one SAP interface then process + * immediately. If Dual BAND SAP is enabled then also process + * immediately, as in this case the both SAP will be in different band + * and channel change on one SAP doesn't mean channel change on + * other interface. + * + * For example, + * Let's say SAP(2G) + SAP(5G-DFS) is initial connection which triggered + * DualBand HW mode and if SAP(5G-DFS) is moving to some channel then + * SAP(2G) doesn't need to move. + * + * If both SAPs are not doing SCC DFS then each of them can change the + * channel independently. Channel change of one SAP became dependent + * second SAP's channel change due to some previous platform's single + * radio limitation. + * + */ + sap_scc_dfs = sap_is_conc_sap_doing_scc_dfs(mac_handle, sap_ctx); + if (sap_get_total_number_sap_intf(mac_handle) <= 1 || + policy_mgr_is_current_hwmode_dbs(mac_ctx->psoc) || + !sap_scc_dfs) { + sap_get_cac_dur_dfs_region(sap_ctx, + &sap_ctx->csr_roamProfile.cac_duration_ms, + &sap_ctx->csr_roamProfile.dfs_regdomain); + /* + * Most likely, radar has been detected and SAP wants to + * change the channel + */ + qdf_status = wlansap_channel_change_request(sap_ctx, + mac_ctx->sap.SapDfsInfo.target_chan_freq); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + *ret_status = QDF_STATUS_E_FAILURE; + return; + } + + sap_ctx->is_sap_ready_for_chnl_chng = true; + /* + * now check if the con-current sap interface is ready + * for channel change. If yes then we issue channel change for + * both the SAPs. If no then simply return success & we will + * issue channel change when second AP's 5 CSA beacon Tx is + * completed. + * + * This check is added to take care of following scenario: + * if SAP1 + SAP2 is doing DFS SCC and radar is detected on that channel + * then SAP1 sends 5 beacons with CSA/ECSA IE and wait for SAP2 to + * finish sending 5 beacons. if SAP1 changes channel before SAP2 finish + * sending beacons then it ends up in + * (SAP1 new channel + SAP2 old channel) MCC with DFS scenario + * which causes some of the stability issues in old platforms. + */ + if (false == + is_concurrent_sap_ready_for_channel_change(mac_handle, sap_ctx)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + FL("sapdfs: sapctx[%pK] ready but not concurrent sap"), + sap_ctx); + *ret_status = QDF_STATUS_SUCCESS; + return; + } + + /* Issue channel change req for each sapctx */ + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + struct sap_context *sap_context; + + if (!((QDF_SAP_MODE == mac_ctx->sap.sapCtxList[intf].sapPersona) + && (mac_ctx->sap.sapCtxList[intf].sap_context))) + continue; + sap_context = mac_ctx->sap.sapCtxList[intf].sap_context; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + FL("sapdfs:issue chnl change for sapctx[%pK]"), + sap_context); + sap_get_cac_dur_dfs_region(sap_context, + &sap_context->csr_roamProfile.cac_duration_ms, + &sap_context->csr_roamProfile.dfs_regdomain); + /* + * Most likely, radar has been detected and SAP wants to + * change the channel + */ + qdf_status = wlansap_channel_change_request(sap_context, + mac_ctx->sap.SapDfsInfo.target_chan_freq); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("post chnl chng req failed, sap[%pK]"), + sap_ctx); + *ret_status = QDF_STATUS_E_FAILURE; + } else { + sap_context->is_sap_ready_for_chnl_chng = false; + } + } + return; +} + +/** + * wlansap_roam_process_dfs_radar_found() - handles the case for + * eCSR_ROAM_RESULT_DFS_RADAR_FOUND_IND in wlansap_roam_callback() + * + * @mac_ctx: mac global context + * @sap_ctx: sap context + * @ret_status: update return status + * + * Return: result of operation + */ +static void +wlansap_roam_process_dfs_radar_found(struct mac_context *mac_ctx, + struct sap_context *sap_ctx, + QDF_STATUS *ret_status) +{ + QDF_STATUS qdf_status; + struct sap_sm_event sap_event; + + if (sap_is_dfs_cac_wait_state(sap_ctx)) { + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_disable_channel_switch) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + "sapdfs: DFS channel switch disabled"); + return; + } + if (false == mac_ctx->sap.SapDfsInfo.sap_radar_found_status) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + "sapdfs: sap_radar_found_status is false"); + return; + } + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_MED, + FL("sapdfs:Posting event eSAP_DFS_CHANNEL_CAC_RADAR_FOUND")); + /* + * If Radar is found, while in DFS CAC WAIT State then post stop + * and destroy the CAC timer and post a + * eSAP_DFS_CHANNEL_CAC_RADAR_FOUND to sapFsm. + */ + if (!sap_ctx->dfs_cac_offload) { + qdf_mc_timer_stop(&mac_ctx-> + sap.SapDfsInfo.sap_dfs_cac_timer); + qdf_mc_timer_destroy(&mac_ctx-> + sap.SapDfsInfo.sap_dfs_cac_timer); + } + mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running = false; + + /* + * User space is already indicated the CAC start and if + * CAC end on this channel is not indicated, the user + * space will be in some undefined state (e.g., UI frozen) + */ + qdf_status = sap_signal_hdd_event(sap_ctx, NULL, + eSAP_DFS_CAC_INTERRUPTED, + (void *) eSAP_STATUS_SUCCESS); + if (QDF_STATUS_SUCCESS != qdf_status) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Failed to send CAC end")); + /* Want to still proceed and try to switch channel. + * Lets try not to be on the DFS channel + */ + } + + sap_event.event = eSAP_DFS_CHANNEL_CAC_RADAR_FOUND; + sap_event.params = 0; + sap_event.u1 = 0; + sap_event.u2 = 0; + qdf_status = sap_fsm(sap_ctx, &sap_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + *ret_status = QDF_STATUS_E_FAILURE; + return; + } + if (sap_ctx->fsm_state == SAP_STARTED) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + FL("sapdfs:Posting event eSAP_DFS_CHNL_SWITCH_ANNOUNCEMENT_START")); + + /* + * Radar found on the operating channel in STARTED state, + * new operating channel has already been selected. Send + * request to SME-->PE for sending CSA IE + */ + sap_event.event = eSAP_DFS_CHNL_SWITCH_ANNOUNCEMENT_START; + sap_event.params = 0; + sap_event.u1 = 0; + sap_event.u2 = 0; + qdf_status = sap_fsm(sap_ctx, &sap_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + *ret_status = QDF_STATUS_E_FAILURE; + return; + } + /* Further actions to be taken here */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("eCSR_ROAM_RESULT_DFS_RADAR_FOUND_IND received in (%d) state"), + sap_ctx->fsm_state); + + return; +} + +/** + * wlansap_roam_process_infra_assoc_ind() - handles the case for + * eCSR_ROAM_RESULT_INFRA_ASSOCIATION_IND in wlansap_roam_callback() + * + * @sap_ctx: sap context + * @roam_result: roam result + * @csr_roam_info: roam info struct + * @ret_status: update return status + * + * Return: result of operation + */ +static void +wlansap_roam_process_infra_assoc_ind(struct sap_context *sap_ctx, + eCsrRoamResult roam_result, + struct csr_roam_info *csr_roam_info, + QDF_STATUS *ret_status) +{ + QDF_STATUS qdf_status; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_INFRA_ASSOCIATION_IND (%d)"), + roam_result); + sap_ctx->nStaWPARSnReqIeLength = csr_roam_info->rsnIELen; + if (sap_ctx->nStaWPARSnReqIeLength) + qdf_mem_copy(sap_ctx->pStaWpaRsnReqIE, csr_roam_info->prsnIE, + sap_ctx->nStaWPARSnReqIeLength); + /* MAC filtering */ + qdf_status = sap_is_peer_mac_allowed(sap_ctx, + (uint8_t *) csr_roam_info->peerMac.bytes); + + if (QDF_STATUS_SUCCESS == qdf_status) { + qdf_status = sap_signal_hdd_event(sap_ctx, + csr_roam_info, eSAP_STA_ASSOC_IND, + (void *) eSAP_STATUS_SUCCESS); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("CSR roam_result = (%d) MAC ("QDF_MAC_ADDR_FMT") fail"), + roam_result, QDF_MAC_ADDR_REF( + csr_roam_info->peerMac.bytes)); + *ret_status = QDF_STATUS_E_FAILURE; + } + } else { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_WARN, + FL("CSR roam_result = (%d) MAC ("QDF_MAC_ADDR_FMT") not allowed"), + roam_result, + QDF_MAC_ADDR_REF(csr_roam_info->peerMac.bytes)); + *ret_status = QDF_STATUS_E_FAILURE; + } + return; +} + +static void wlansap_update_vendor_acs_chan(struct mac_context *mac_ctx, + struct sap_context *sap_ctx) +{ + int intf; + + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return; + } + + mac_ctx->sap.SapDfsInfo.target_chan_freq = + wlan_reg_chan_to_freq(mac_ctx->pdev, sap_ctx->dfs_vendor_channel); + + mac_ctx->sap.SapDfsInfo.new_chanWidth = + sap_ctx->dfs_vendor_chan_bw; + mac_ctx->sap.SapDfsInfo.new_ch_params.ch_width = + sap_ctx->dfs_vendor_chan_bw; + + if (mac_ctx->sap.SapDfsInfo.target_chan_freq != 0) { + mac_ctx->sap.SapDfsInfo.cac_state = + eSAP_DFS_DO_NOT_SKIP_CAC; + sap_cac_reset_notify(MAC_HANDLE(mac_ctx)); + return; + } + /* App failed to provide new channel, try random channel algo */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("failed to get channel from userspace")); + + /* Issue stopbss for each sapctx */ + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + struct sap_context *sap_context; + + if (((QDF_SAP_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona) || + (QDF_P2P_GO_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona)) && + mac_ctx->sap.sapCtxList[intf].sap_context != + NULL) { + sap_context = + mac_ctx->sap.sapCtxList[intf].sap_context; + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + FL("sapdfs: no available channel for sapctx[%pK], StopBss"), + sap_context); + wlansap_stop_bss(sap_context); + } + } +} + +QDF_STATUS wlansap_roam_callback(void *ctx, + struct csr_roam_info *csr_roam_info, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result) +{ + /* sap_ctx value */ + struct sap_context *sap_ctx = ctx; + /* State machine event */ + struct sap_sm_event sap_event; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_ret_status = QDF_STATUS_SUCCESS; + mac_handle_t mac_handle; + struct mac_context *mac_ctx; + uint8_t intf; + + if (QDF_IS_STATUS_ERROR(wlansap_context_get(sap_ctx))) + return QDF_STATUS_E_FAILURE; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + wlansap_context_put(sap_ctx); + return QDF_STATUS_E_NOMEM; + } + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("roam_status = %d, roam_result = %d"), + roam_status, roam_result); + + mac_handle = MAC_HANDLE(mac_ctx); + + switch (roam_status) { + case eCSR_ROAM_INFRA_IND: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_status = eCSR_ROAM_INFRA_IND (%d)"), + roam_status); + if (roam_result == eCSR_ROAM_RESULT_INFRA_START_FAILED) { + /* Fill in the event structure */ + sap_event.event = eSAP_MAC_START_FAILS; + sap_event.params = csr_roam_info; + sap_event.u1 = roam_status; + sap_event.u2 = roam_result; + /* Handle event */ + qdf_status = sap_fsm(sap_ctx, &sap_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; + case eCSR_ROAM_LOSTLINK: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_status = eCSR_ROAM_LOSTLINK (%d)"), + roam_status); + break; + case eCSR_ROAM_MIC_ERROR_IND: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_status = eCSR_ROAM_MIC_ERROR_IND (%d)"), + roam_status); + break; + case eCSR_ROAM_SET_KEY_COMPLETE: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_status = eCSR_ROAM_SET_KEY_COMPLETE (%d)"), + roam_status); + if (roam_result == eCSR_ROAM_RESULT_FAILURE) + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_SET_KEY_EVENT, + (void *) eSAP_STATUS_FAILURE); + break; + case eCSR_ROAM_ASSOCIATION_COMPLETION: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_status = eCSR_ROAM_ASSOCIATION_COMPLETION (%d)"), + roam_status); + if (roam_result == eCSR_ROAM_RESULT_FAILURE) + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_REASSOC_EVENT, + (void *) eSAP_STATUS_FAILURE); + break; + case eCSR_ROAM_DISASSOCIATED: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_status = eCSR_ROAM_DISASSOCIATED (%d)"), + roam_status); + if (roam_result == eCSR_ROAM_RESULT_MIC_FAILURE) + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_MIC_FAILURE_EVENT, + (void *) eSAP_STATUS_FAILURE); + break; + case eCSR_ROAM_WPS_PBC_PROBE_REQ_IND: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_status = eCSR_ROAM_WPS_PBC_PROBE_REQ_IND (%d)"), + roam_status); + break; + case eCSR_ROAM_DISCONNECT_ALL_P2P_CLIENTS: + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_DISCONNECT_ALL_P2P_CLIENT, + (void *) eSAP_STATUS_SUCCESS); + break; + case eCSR_ROAM_SEND_P2P_STOP_BSS: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("Received stopbss")); + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_MAC_TRIG_STOP_BSS_EVENT, + (void *) eSAP_STATUS_SUCCESS); + break; + case eCSR_ROAM_CHANNEL_COMPLETE_IND: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("Received new channel from app")); + wlansap_update_vendor_acs_chan(mac_ctx, sap_ctx); + break; + + case eCSR_ROAM_DFS_RADAR_IND: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + "Rcvd Radar Indication on sap ch freq %d, session %d", + sap_ctx->chan_freq, sap_ctx->sessionId); + + if (!policy_mgr_get_dfs_master_dynamic_enabled( + mac_ctx->psoc, sap_ctx->sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_DEBUG, + FL("Ignore the Radar indication")); + goto EXIT; + } + + if (sap_ctx->fsm_state != SAP_STARTED && + !sap_is_dfs_cac_wait_state(sap_ctx)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Ignore Radar event in sap state %d cac wait state %d"), + sap_ctx->fsm_state, + sap_is_dfs_cac_wait_state(sap_ctx)); + goto EXIT; + } + + if (!sap_chan_bond_dfs_sub_chan( + sap_ctx, wlan_reg_freq_to_chan(mac_ctx->pdev, + sap_ctx->chan_freq), + PHY_CHANNEL_BONDING_STATE_MAX)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + "Ignore Radar event for sap ch freq%d", + sap_ctx->chan_freq); + goto EXIT; + } + + if (sap_ctx->is_pre_cac_on) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("sapdfs: Radar detect on pre cac:%d"), + sap_ctx->sessionId); + if (!sap_ctx->dfs_cac_offload) { + qdf_mc_timer_stop( + &mac_ctx->sap.SapDfsInfo.sap_dfs_cac_timer); + qdf_mc_timer_destroy( + &mac_ctx->sap.SapDfsInfo.sap_dfs_cac_timer); + } + mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running = + false; + sap_signal_hdd_event(sap_ctx, NULL, + eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC, + (void *) eSAP_STATUS_SUCCESS); + break; + } + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + FL("sapdfs: Indicate eSAP_DFS_RADAR_DETECT to HDD")); + sap_signal_hdd_event(sap_ctx, NULL, eSAP_DFS_RADAR_DETECT, + (void *) eSAP_STATUS_SUCCESS); + mac_ctx->sap.SapDfsInfo.target_chan_freq = + wlan_reg_chan_to_freq(mac_ctx->pdev, sap_indicate_radar(sap_ctx)); + /* if there is an assigned next channel hopping */ + if (0 < mac_ctx->sap.SapDfsInfo.user_provided_target_chan_freq) { + mac_ctx->sap.SapDfsInfo.target_chan_freq = + mac_ctx->sap.SapDfsInfo.user_provided_target_chan_freq; + mac_ctx->sap.SapDfsInfo.user_provided_target_chan_freq = + 0; + } + /* if external acs enabled */ + if (sap_ctx->vendor_acs_dfs_lte_enabled && + !mac_ctx->sap.SapDfsInfo.target_chan_freq) { + /* Return from here, processing will be done later */ + goto EXIT; + } + if (mac_ctx->sap.SapDfsInfo.target_chan_freq != 0) { + mac_ctx->sap.SapDfsInfo.cac_state = + eSAP_DFS_DO_NOT_SKIP_CAC; + sap_cac_reset_notify(mac_handle); + break; + } + /* Issue stopbss for each sapctx */ + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + struct sap_context *sap_context; + struct csr_roam_profile *profile; + + if (((QDF_SAP_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona) || + (QDF_P2P_GO_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona)) && + mac_ctx->sap.sapCtxList[intf].sap_context != + NULL) { + sap_context = + mac_ctx->sap.sapCtxList[intf].sap_context; + profile = &sap_context->csr_roamProfile; + if (!wlan_reg_is_passive_or_disable_ch( + mac_ctx->pdev, + wlan_reg_freq_to_chan( + mac_ctx->pdev, + profile->op_freq))) + continue; + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + FL("sapdfs: no available channel for sapctx[%pK], StopBss"), + sap_context); + sap_signal_hdd_event(sap_context, NULL, + eSAP_STOP_BSS_DUE_TO_NO_CHNL, + (void *) eSAP_STATUS_SUCCESS); + } + } + break; + case eCSR_ROAM_DFS_CHAN_SW_NOTIFY: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("Received Chan Sw Update Notification")); + break; + case eCSR_ROAM_SET_CHANNEL_RSP: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("Received set channel response")); + break; + case eCSR_ROAM_CAC_COMPLETE_IND: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("Received cac complete indication")); + break; + case eCSR_ROAM_EXT_CHG_CHNL_IND: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("Received set channel Indication")); + break; + default: + break; + } + + switch (roam_result) { + case eCSR_ROAM_RESULT_INFRA_ASSOCIATION_IND: + if (csr_roam_info) + wlansap_roam_process_infra_assoc_ind(sap_ctx, + roam_result, + csr_roam_info, &qdf_ret_status); + break; + case eCSR_ROAM_RESULT_INFRA_ASSOCIATION_CNF: + if (!csr_roam_info) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "csr_roam_info is NULL"); + qdf_ret_status = QDF_STATUS_E_NULL_VALUE; + break; + } + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_INFRA_ASSOCIATION_CNF (%d)"), + roam_result); + sap_ctx->nStaWPARSnReqIeLength = csr_roam_info->rsnIELen; + if (sap_ctx->nStaWPARSnReqIeLength) + qdf_mem_copy(sap_ctx->pStaWpaRsnReqIE, + csr_roam_info->prsnIE, + sap_ctx->nStaWPARSnReqIeLength); + + /* Fill in the event structure */ + qdf_status = sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_ASSOC_EVENT, + (void *) eSAP_STATUS_SUCCESS); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + break; + case eCSR_ROAM_RESULT_DEAUTH_IND: + case eCSR_ROAM_RESULT_DISASSOC_IND: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_DEAUTH/DISASSOC_IND (%d)"), + roam_result); + /* Fill in the event structure */ + qdf_status = sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_DISASSOC_EVENT, + (void *) eSAP_STATUS_SUCCESS); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + break; + case eCSR_ROAM_RESULT_MIC_ERROR_GROUP: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_MIC_ERROR_GROUP (%d)"), + roam_result); + /* + * Fill in the event structure + * TODO: support for group key MIC failure event to be handled + */ + qdf_status = sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_MIC_FAILURE_EVENT, + (void *) NULL); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + break; + case eCSR_ROAM_RESULT_MIC_ERROR_UNICAST: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_MIC_ERROR_UNICAST (%d)"), + roam_result); + /* + * Fill in the event structure + * TODO: support for unicast key MIC failure event to be handled + */ + qdf_status = + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_MIC_FAILURE_EVENT, + (void *) NULL); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; + case eCSR_ROAM_RESULT_AUTHENTICATED: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_AUTHENTICATED (%d)"), + roam_result); + /* Fill in the event structure */ + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_SET_KEY_EVENT, + (void *) eSAP_STATUS_SUCCESS); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + break; + case eCSR_ROAM_RESULT_ASSOCIATED: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_ASSOCIATED (%d)"), + roam_result); + /* Fill in the event structure */ + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_REASSOC_EVENT, + (void *) eSAP_STATUS_SUCCESS); + break; + case eCSR_ROAM_RESULT_INFRA_STARTED: + if (!csr_roam_info) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "csr_roam_info is NULL"); + qdf_ret_status = QDF_STATUS_E_NULL_VALUE; + break; + } + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_INFRA_STARTED (%d)"), + roam_result); + /* + * In the current implementation, hostapd is not aware that + * drive will support DFS. Hence, driver should inform + * eSAP_MAC_START_BSS_SUCCESS to upper layers and then perform + * CAC underneath + */ + sap_event.event = eSAP_MAC_START_BSS_SUCCESS; + sap_event.params = csr_roam_info; + sap_ctx->sap_sta_id = csr_roam_info->staId; + sap_event.u1 = roam_status; + sap_event.u2 = roam_result; + qdf_status = sap_fsm(sap_ctx, &sap_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + break; + case eCSR_ROAM_RESULT_INFRA_STOPPED: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_INFRA_STOPPED (%d)"), + roam_result); + /* Fill in the event structure */ + sap_event.event = eSAP_MAC_READY_FOR_CONNECTIONS; + sap_event.params = csr_roam_info; + sap_event.u1 = roam_status; + sap_event.u2 = roam_result; + /* Handle event */ + qdf_status = sap_fsm(sap_ctx, &sap_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + break; + case eCSR_ROAM_RESULT_WPS_PBC_PROBE_REQ_IND: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_WPS_PBC_PROBE_REQ_IND (%d)"), + roam_result); + /* + * Fill in the event structure + * TODO: support for group key MIC failure event to be handled + */ + qdf_status = sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_WPS_PBC_PROBE_REQ_EVENT, + (void *) NULL); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + break; + case eCSR_ROAM_RESULT_FORCED: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_FORCED (%d)"), + roam_result); + /* + * This event can be used to inform hdd about user triggered + * disassoc event + * Fill in the event structure + */ + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_DISASSOC_EVENT, + (void *) eSAP_STATUS_SUCCESS); + break; + case eCSR_ROAM_RESULT_NONE: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_NONE (%d)"), + roam_result); + /* + * This event can be used to inform hdd about user triggered + * disassoc event + * Fill in the event structure + */ + if (roam_status == eCSR_ROAM_SET_KEY_COMPLETE) + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_STA_SET_KEY_EVENT, + (void *) eSAP_STATUS_SUCCESS); + break; + case eCSR_ROAM_RESULT_MAX_ASSOC_EXCEEDED: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_MAX_ASSOC_EXCEEDED (%d)"), + roam_result); + /* Fill in the event structure */ + qdf_status = sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_MAX_ASSOC_EXCEEDED, + NULL); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + + break; + case eCSR_ROAM_RESULT_DFS_RADAR_FOUND_IND: + if (!policy_mgr_get_dfs_master_dynamic_enabled( + mac_ctx->psoc, sap_ctx->sessionId)) + break; + wlansap_roam_process_dfs_radar_found(mac_ctx, sap_ctx, + &qdf_ret_status); + break; + case eCSR_ROAM_RESULT_CSA_RESTART_RSP: + qdf_ret_status = wlansap_dfs_send_csa_ie_request(sap_ctx); + + if (!QDF_IS_STATUS_SUCCESS(qdf_ret_status)) + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("CSR roam_result = eCSR_ROAM_RESULT_CSA_RESTART_RSP %d"), + roam_result); + break; + case eCSR_ROAM_RESULT_DFS_CHANSW_UPDATE_SUCCESS: + wlansap_roam_process_dfs_chansw_update(mac_handle, sap_ctx, + &qdf_ret_status); + break; + case eCSR_ROAM_RESULT_CAC_END_IND: + sap_dfs_cac_timer_callback(mac_handle); + break; + case eCSR_ROAM_RESULT_CHANNEL_CHANGE_SUCCESS: + wlansap_roam_process_ch_change_success(mac_ctx, sap_ctx, + csr_roam_info, &qdf_ret_status); + + if (QDF_IS_STATUS_ERROR(qdf_ret_status)) + qdf_ret_status = + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_CHANNEL_CHANGE_RESP, + (void *)eSAP_STATUS_FAILURE); + else + qdf_ret_status = + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_CHANNEL_CHANGE_RESP, + (void *)QDF_STATUS_SUCCESS); + break; + case eCSR_ROAM_RESULT_CHANNEL_CHANGE_FAILURE: + /* This is much more serious issue, we have to vacate the + * channel due to the presence of radar but our channel change + * failed, stop the BSS operation completely and inform hostapd + */ + qdf_ret_status = wlansap_stop_bss(sap_ctx); + + qdf_ret_status = + sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_CHANNEL_CHANGE_RESP, + (void *)QDF_STATUS_E_FAILURE); + break; + case eCSR_ROAM_EXT_CHG_CHNL_UPDATE_IND: + qdf_status = sap_signal_hdd_event(sap_ctx, csr_roam_info, + eSAP_ECSA_CHANGE_CHAN_IND, NULL); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_ret_status = QDF_STATUS_E_FAILURE; + break; + default: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("CSR roam_result = %s (%d) not handled"), + get_e_csr_roam_result_str(roam_result), + roam_result); + break; + } +EXIT: + wlansap_context_put(sap_ctx); + return qdf_ret_status; +} + +void sap_scan_event_callback(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{ + uint32_t scan_id; + uint8_t session_id; + bool success = false; + eCsrScanStatus scan_status = eCSR_SCAN_FAILURE; + mac_handle_t mac_handle; + QDF_STATUS status; + + /* + * It may happen that the SAP was deleted before the scan + * cb was called. Here the sap context which was passed as an + * arg to the ACS cb is used after free then, and there is no way + * currently to validate the pointer. Now try get vdev ref before + * the weight calculation algo kicks in, and return if the + * reference cannot be taken to avoid use after free for SAP-context + */ + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_LEGACY_SAP_ID); + if (QDF_IS_STATUS_ERROR(status)) { + sap_err("Hotspot fail, vdev ref get error"); + return; + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SAP_ID); + + session_id = wlan_vdev_get_id(vdev); + scan_id = event->scan_id; + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (!mac_handle) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_FATAL, + FL("invalid MAC handle")); + return; + } + + qdf_mtrace(QDF_MODULE_ID_SCAN, QDF_MODULE_ID_SAP, event->type, + event->vdev_id, event->scan_id); + + if (!util_is_scan_completed(event, &success)) + return; + + if (success) + scan_status = eCSR_SCAN_SUCCESS; + + wlansap_pre_start_bss_acs_scan_callback(mac_handle, + arg, session_id, + scan_id, scan_status); +} diff --git a/drivers/staging/qcacld-3.0/core/sap/src/sap_ch_select.c b/drivers/staging/qcacld-3.0/core/sap/src/sap_ch_select.c new file mode 100644 index 0000000000000000000000000000000000000000..0bbf75bce2f88da4d0bc51089789839ab59ecf28 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sap/src/sap_ch_select.c @@ -0,0 +1,2394 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*=========================================================================== + + s a p C h S e l e c t . C + OVERVIEW: + + This software unit holds the implementation of the WLAN SAP modules + functions for channel selection. + + DEPENDENCIES: + + Are listed for each API below. + ===========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "qdf_trace.h" +#include "csr_api.h" +#include "sme_api.h" +#include "sap_ch_select.h" +#include "sap_internal.h" +#ifdef ANI_OS_TYPE_QNX +#include "stdio.h" +#endif +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +#include "lim_utils.h" +#include "parser_api.h" +#include +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ +#include "cds_utils.h" +#include "pld_common.h" +#include "wlan_reg_services_api.h" +#include + +/*-------------------------------------------------------------------------- + Function definitions + --------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------- + Defines + --------------------------------------------------------------------------*/ +#define SAP_DEBUG + +#define IS_RSSI_VALID(extRssi, rssi) \ + ( \ + ((extRssi < rssi) ? true : false) \ + ) + +#define SET_ACS_BAND(acs_band, sap_ctx) \ +{ \ + if (sap_ctx->acs_cfg->start_ch_freq <= \ + WLAN_REG_CH_TO_FREQ(CHAN_ENUM_2484) && \ + sap_ctx->acs_cfg->end_ch_freq <= \ + WLAN_REG_CH_TO_FREQ(CHAN_ENUM_2484)) \ + acs_band = eCSR_DOT11_MODE_11g; \ + else if (sap_ctx->acs_cfg->start_ch_freq >= \ + WLAN_REG_CH_TO_FREQ(CHAN_ENUM_2484))\ + acs_band = eCSR_DOT11_MODE_11a; \ + else \ + acs_band = eCSR_DOT11_MODE_abg; \ +} + +#define ACS_WEIGHT_AMOUNT_LOCAL 240 + +#define ACS_WEIGHT_AMOUNT_CONFIG(weights) \ + (((weights) & 0xf) + \ + (((weights) & 0xf0) >> 4) + \ + (((weights) & 0xf00) >> 8) + \ + (((weights) & 0xf000) >> 12) + \ + (((weights) & 0xf0000) >> 16) + \ + (((weights) & 0xf00000) >> 20)) + +/* + * LSH/RSH 4 to enhance the accurate since + * need to do modulation to ACS_WEIGHT_AMOUNT_LOCAL. + */ +#define ACS_WEIGHT_COMPUTE(weights, weight, factor, base) \ + (((((((((weight) << 4) * ACS_WEIGHT_AMOUNT_LOCAL * (factor)) + \ + (ACS_WEIGHT_AMOUNT_CONFIG((weights)) >> 1)) / \ + ACS_WEIGHT_AMOUNT_CONFIG((weights))) + \ + ((base) >> 1)) / (base)) + 8) >> 4) + +#define ACS_WEIGHT_CFG_TO_LOCAL(weights, weight) \ + (((((((weight) << 4) * ACS_WEIGHT_AMOUNT_LOCAL) + \ + (ACS_WEIGHT_AMOUNT_CONFIG((weights)) >> 1)) / \ + ACS_WEIGHT_AMOUNT_CONFIG((weights))) + 8) >> 4) + +#define ACS_WEIGHT_SOFTAP_RSSI_CFG(weights) \ + ((weights) & 0xf) + +#define ACS_WEIGHT_SOFTAP_COUNT_CFG(weights) \ + (((weights) & 0xf0) >> 4) + +#define ACS_WEIGHT_SOFTAP_NOISE_FLOOR_CFG(weights) \ + (((weights) & 0xf00) >> 8) + +#define ACS_WEIGHT_SOFTAP_CHANNEL_FREE_CFG(weights) \ + (((weights) & 0xf000) >> 12) + +#define ACS_WEIGHT_SOFTAP_TX_POWER_RANGE_CFG(weights) \ + (((weights) & 0xf0000) >> 16) + +#define ACS_WEIGHT_SOFTAP_TX_POWER_THROUGHPUT_CFG(weights) \ + (((weights) & 0xf00000) >> 20) + +typedef struct { + uint16_t chStartNum; + uint32_t weight; +} sapAcsChannelInfo; + +sapAcsChannelInfo acs_ht40_channels5_g[] = { + {36, SAP_ACS_WEIGHT_MAX}, + {44, SAP_ACS_WEIGHT_MAX}, + {52, SAP_ACS_WEIGHT_MAX}, + {60, SAP_ACS_WEIGHT_MAX}, + {100, SAP_ACS_WEIGHT_MAX}, + {108, SAP_ACS_WEIGHT_MAX}, + {116, SAP_ACS_WEIGHT_MAX}, + {124, SAP_ACS_WEIGHT_MAX}, + {132, SAP_ACS_WEIGHT_MAX}, + {140, SAP_ACS_WEIGHT_MAX}, + {149, SAP_ACS_WEIGHT_MAX}, + {157, SAP_ACS_WEIGHT_MAX}, +}; + +sapAcsChannelInfo acs_ht80_channels[] = { + {36, SAP_ACS_WEIGHT_MAX}, + {52, SAP_ACS_WEIGHT_MAX}, + {100, SAP_ACS_WEIGHT_MAX}, + {116, SAP_ACS_WEIGHT_MAX}, + {132, SAP_ACS_WEIGHT_MAX}, + {149, SAP_ACS_WEIGHT_MAX}, +}; + +sapAcsChannelInfo acs_vht160_channels[] = { + {36, SAP_ACS_WEIGHT_MAX}, + {100, SAP_ACS_WEIGHT_MAX}, +}; + +sapAcsChannelInfo acs_ht40_channels24_g[] = { + {1, SAP_ACS_WEIGHT_MAX}, + {2, SAP_ACS_WEIGHT_MAX}, + {3, SAP_ACS_WEIGHT_MAX}, + {4, SAP_ACS_WEIGHT_MAX}, + {9, SAP_ACS_WEIGHT_MAX}, +}; + +#define CHANNEL_165 165 + +/* rssi discount for channels in PCL */ +#define PCL_RSSI_DISCOUNT 10 + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +/** + * sap_check_n_add_channel() - checks and add given channel in sap context's + * avoid_channels_info struct + * @sap_ctx: sap context. + * @new_channel: channel to be added to sap_ctx's avoid ch info + * + * sap_ctx contains sap_avoid_ch_info strcut containing the list of channels on + * which MDM device's AP with MCC was detected. This function will add channels + * to that list after checking for duplicates. + * + * Return: true: if channel was added or already present + * else false: if channel list was already full. + */ +static bool +sap_check_n_add_channel(struct sap_context *sap_ctx, + uint8_t new_channel) +{ + uint8_t i = 0; + struct sap_avoid_channels_info *ie_info = + &sap_ctx->sap_detected_avoid_ch_ie; + + for (i = 0; i < sizeof(ie_info->channels); i++) { + if (ie_info->channels[i] == new_channel) + break; + + if (ie_info->channels[i] == 0) { + ie_info->channels[i] = new_channel; + break; + } + } + if (i == sizeof(ie_info->channels)) + return false; + else + return true; +} +/** + * sap_check_n_add_overlapped_chnls() - checks & add overlapped channels + * to primary channel in 2.4Ghz band. + * @sap_ctx: sap context. + * @primary_chnl: primary channel to be avoided. + * + * sap_ctx contains sap_avoid_ch_info struct containing the list of channels on + * which MDM device's AP with MCC was detected. This function will add channels + * to that list after checking for duplicates. + * + * Return: true: if channel was added or already present + * else false: if channel list was already full. + */ +static bool +sap_check_n_add_overlapped_chnls(struct sap_context *sap_ctx, + uint8_t primary_channel) +{ + uint8_t i = 0, j = 0, upper_chnl = 0, lower_chnl = 0; + struct sap_avoid_channels_info *ie_info = + &sap_ctx->sap_detected_avoid_ch_ie; + /* + * if primary channel less than channel 1 or out of 2g band then + * no further process is required. return true in this case. + */ + if (primary_channel < CHANNEL_1 || primary_channel > CHANNEL_14) + return true; + + /* lower channel is one channel right before primary channel */ + lower_chnl = primary_channel - 1; + /* upper channel is one channel right after primary channel */ + upper_chnl = primary_channel + 1; + + /* lower channel needs to be non-zero, zero is not valid channel */ + if (lower_chnl > (CHANNEL_1 - 1)) { + for (i = 0; i < sizeof(ie_info->channels); i++) { + if (ie_info->channels[i] == lower_chnl) + break; + if (ie_info->channels[i] == 0) { + ie_info->channels[i] = lower_chnl; + break; + } + } + } + /* upper channel needs to be atleast last channel in 2.4Ghz band */ + if (upper_chnl < (CHANNEL_14 + 1)) { + for (j = 0; j < sizeof(ie_info->channels); j++) { + if (ie_info->channels[j] == upper_chnl) + break; + if (ie_info->channels[j] == 0) { + ie_info->channels[j] = upper_chnl; + break; + } + } + } + if (i == sizeof(ie_info->channels) || j == sizeof(ie_info->channels)) + return false; + else + return true; +} + +/** + * sap_process_avoid_ie() - processes the detected Q2Q IE + * context's avoid_channels_info struct + * @mac_handle: opaque handle to the MAC context + * @sap_ctx: sap context. + * @scan_result: scan results for ACS scan. + * @spect_info: spectrum weights array to update + * + * Detection of Q2Q IE indicates presence of another MDM device with its AP + * operating in MCC mode. This function parses the scan results and processes + * the Q2Q IE if found. It then extracts the channels and populates them in + * sap_ctx struct. It also increases the weights of those channels so that + * ACS logic will avoid those channels in its selection algorithm. + * + * Return: void + */ +static void +sap_process_avoid_ie(mac_handle_t mac_handle, struct sap_context *sap_ctx, + qdf_list_t *scan_list, tSapChSelSpectInfo *spect_info) +{ + const uint8_t *temp_ptr = NULL; + uint8_t i = 0; + struct sAvoidChannelIE *avoid_ch_ie; + struct mac_context *mac_ctx = NULL; + tSapSpectChInfo *spect_ch = NULL; + qdf_list_node_t *cur_lst = NULL, *next_lst = NULL; + struct scan_cache_node *cur_node = NULL; + uint32_t chan_freq; + + mac_ctx = MAC_CONTEXT(mac_handle); + spect_ch = spect_info->pSpectCh; + + if (scan_list) + qdf_list_peek_front(scan_list, &cur_lst); + + while (cur_lst) { + cur_node = qdf_container_of(cur_lst, struct scan_cache_node, + node); + + temp_ptr = wlan_get_vendor_ie_ptr_from_oui( + SIR_MAC_QCOM_VENDOR_OUI, + SIR_MAC_QCOM_VENDOR_SIZE, + util_scan_entry_ie_data(cur_node->entry), + util_scan_entry_ie_len(cur_node->entry)); + + if (temp_ptr) { + avoid_ch_ie = (struct sAvoidChannelIE *)temp_ptr; + if (avoid_ch_ie->type != + QCOM_VENDOR_IE_MCC_AVOID_CH) { + qdf_list_peek_next(scan_list, + cur_lst, &next_lst); + cur_lst = next_lst; + next_lst = NULL; + continue; + } + + sap_ctx->sap_detected_avoid_ch_ie.present = 1; + + chan_freq = + wlan_reg_legacy_chan_to_freq(mac_ctx->pdev, + avoid_ch_ie->channel); + + sap_debug("Q2Q-IE avoid freq = %d", chan_freq); + /* add this channel to to_avoid channel list */ + sap_check_n_add_channel(sap_ctx, avoid_ch_ie->channel); + sap_check_n_add_overlapped_chnls(sap_ctx, + avoid_ch_ie->channel); + /* + * Mark weight of these channel present in IE to MAX + * so that ACS logic will to avoid thse channels + */ + for (i = 0; i < spect_info->numSpectChans; i++) { + if (spect_ch[i].chan_freq != chan_freq) + continue; + /* + * weight is set more than max so that, + * in the case of other channels being + * assigned max weight due to noise, + * they may be preferred over channels + * with Q2Q IE. + */ + spect_ch[i].weight = SAP_ACS_WEIGHT_MAX + 1; + spect_ch[i].weight_copy = + SAP_ACS_WEIGHT_MAX + 1; + break; + } + } + + qdf_list_peek_next(scan_list, cur_lst, &next_lst); + cur_lst = next_lst; + next_lst = NULL; + } +} +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +/** + * sap_select_preferred_channel_from_channel_list() - to calc best cahnnel + * @best_ch_freq: best chan freq already calculated among all the chanels + * @sap_ctx: sap context + * @spectinfo_param: Pointer to tSapChSelSpectInfo structure + * + * This function calculates the best channel among the configured channel list. + * If channel list not configured then returns the best channel calculated + * among all the channel list. + * + * Return: uint32_t best channel frequency + */ +static +uint32_t sap_select_preferred_channel_from_channel_list(uint32_t best_ch_freq, + struct sap_context *sap_ctx, + tSapChSelSpectInfo *spectinfo_param) +{ + /* + * If Channel List is not Configured don't do anything + * Else return the Best Channel from the Channel List + */ + if ((!sap_ctx->acs_cfg->freq_list) || + (!spectinfo_param) || + (!sap_ctx->acs_cfg->ch_list_count)) + return best_ch_freq; + + if (wlansap_is_channel_present_in_acs_list(best_ch_freq, + sap_ctx->acs_cfg->freq_list, + sap_ctx->acs_cfg->ch_list_count)) + return best_ch_freq; + + return SAP_CHANNEL_NOT_SELECTED; +} + +/** + * sap_chan_sel_init() - Initialize channel select + * @mac_handle: Opaque handle to the global MAC context + * @pSpectInfoParams: Pointer to tSapChSelSpectInfo structure + * @sap_ctx: Pointer to SAP Context + * + * Function sap_chan_sel_init allocates the memory, initializes the + * structures used by the channel selection algorithm + * + * Return: bool Success or FAIL + */ +static bool sap_chan_sel_init(mac_handle_t mac_handle, + tSapChSelSpectInfo *pSpectInfoParams, + struct sap_context *sap_ctx) +{ + tSapSpectChInfo *pSpectCh = NULL; + uint32_t *pChans = NULL; + uint16_t channelnum = 0; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + bool include_dfs_ch = true; + uint8_t sta_sap_scc_on_dfs_chnl_config_value; + + pSpectInfoParams->numSpectChans = + mac->scan.base_channels.numChannels; + + /* Allocate memory for weight computation of 2.4GHz */ + pSpectCh = qdf_mem_malloc((pSpectInfoParams->numSpectChans) * + sizeof(*pSpectCh)); + if (!pSpectCh) + return false; + + /* Initialize the pointers in the DfsParams to the allocated memory */ + pSpectInfoParams->pSpectCh = pSpectCh; + + pChans = mac->scan.base_channels.channel_freq_list; + + policy_mgr_get_sta_sap_scc_on_dfs_chnl(mac->psoc, + &sta_sap_scc_on_dfs_chnl_config_value); +#if defined(FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE) + if (sap_ctx->dfs_ch_disable == true) + include_dfs_ch = false; +#endif + if (!mac->mlme_cfg->dfs_cfg.dfs_master_capable || + ACS_DFS_MODE_DISABLE == sap_ctx->dfs_mode) + include_dfs_ch = false; + + /* Fill the channel number in the spectrum in the operating freq band */ + for (channelnum = 0; + channelnum < pSpectInfoParams->numSpectChans; + channelnum++, pChans++, pSpectCh++) { + uint8_t channel; + + channel = wlan_reg_freq_to_chan(mac->pdev, *pChans); + + pSpectCh->chan_freq = *pChans; + /* Initialise for all channels */ + pSpectCh->rssiAgr = SOFTAP_MIN_RSSI; + /* Initialise max ACS weight for all channels */ + pSpectCh->weight = SAP_ACS_WEIGHT_MAX; + + /* check if the channel is in NOL blacklist */ + if (sap_dfs_is_channel_in_nol_list( + sap_ctx, channel, + PHY_SINGLE_CHANNEL_CENTERED)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, Ch %d is in NOL list", __func__, + channel); + continue; + } + + if (!include_dfs_ch || + sta_sap_scc_on_dfs_chnl_config_value == 1) { + if (wlan_reg_is_dfs_for_freq(mac->pdev, + pSpectCh->chan_freq)) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, DFS Ch %d not considered for ACS. include_dfs_ch %u, sta_sap_scc_on_dfs_chnl_config_value %d", + __func__, channel, include_dfs_ch, + sta_sap_scc_on_dfs_chnl_config_value); + continue; + } + } + + if (!policy_mgr_is_safe_channel(mac->psoc, *pChans)) + continue; + + /* OFDM rates are not supported on channel 14 */ + if (channel == 14 && + eCSR_DOT11_MODE_11b != sap_ctx->csr_roamProfile.phyMode) + continue; + + /* Skip DSRC channels */ + if (wlan_reg_is_dsrc_chan(mac->pdev, channel)) + continue; + + /* + * Skip the channels which are not in ACS config from user + * space + */ + if (!wlansap_is_channel_present_in_acs_list(*pChans, + sap_ctx->acs_cfg->freq_list, + sap_ctx->acs_cfg->ch_list_count)) + continue; + + pSpectCh->valid = true; + pSpectCh->weight = 0; + } + + return true; +} + +/** + * sapweight_rssi_count() - calculates the channel weight due to rssi + and data count(here number of BSS observed) + * @sap_ctx : Softap context + * @rssi : Max signal strength receieved from a BSS for the channel + * @count : Number of BSS observed in the channel + * + * Return: uint32_t Calculated channel weight based on above two + */ +static +uint32_t sapweight_rssi_count(struct sap_context *sap_ctx, int8_t rssi, + uint16_t count) +{ + int32_t rssiWeight = 0; + int32_t countWeight = 0; + uint32_t rssicountWeight = 0; + uint8_t softap_rssi_weight_cfg, softap_count_weight_cfg; + uint8_t softap_rssi_weight_local, softap_count_weight_local; + + softap_rssi_weight_cfg = + ACS_WEIGHT_SOFTAP_RSSI_CFG(sap_ctx->auto_channel_select_weight); + + softap_count_weight_cfg = + ACS_WEIGHT_SOFTAP_COUNT_CFG(sap_ctx->auto_channel_select_weight); + + softap_rssi_weight_local = + ACS_WEIGHT_CFG_TO_LOCAL(sap_ctx->auto_channel_select_weight, + softap_rssi_weight_cfg); + + softap_count_weight_local = + ACS_WEIGHT_CFG_TO_LOCAL(sap_ctx->auto_channel_select_weight, + softap_count_weight_cfg); + + /* Weight from RSSI */ + rssiWeight = ACS_WEIGHT_COMPUTE(sap_ctx->auto_channel_select_weight, + softap_rssi_weight_cfg, + rssi - SOFTAP_MIN_RSSI, + SOFTAP_MAX_RSSI - SOFTAP_MIN_RSSI); + + if (rssiWeight > softap_rssi_weight_local) + rssiWeight = softap_rssi_weight_local; + + else if (rssiWeight < 0) + rssiWeight = 0; + + /* Weight from data count */ + countWeight = ACS_WEIGHT_COMPUTE(sap_ctx->auto_channel_select_weight, + softap_count_weight_cfg, + count - SOFTAP_MIN_COUNT, + SOFTAP_MAX_COUNT - SOFTAP_MIN_COUNT); + + if (countWeight > softap_count_weight_local) + countWeight = softap_count_weight_local; + + rssicountWeight = rssiWeight + countWeight; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "rssiWeight=%d, countWeight=%d, rssicountWeight=%d", + rssiWeight, countWeight, rssicountWeight); + + return rssicountWeight; +} + +/** + * sap_get_channel_status() - get channel info via channel number + * @p_mac: Pointer to Global MAC structure + * @channel_id: channel id + * + * Return: chan status info + */ +static struct lim_channel_status *sap_get_channel_status + (struct mac_context *p_mac, uint32_t chan_freq) +{ + return csr_get_channel_status(p_mac, chan_freq); +} + +/** + * sap_clear_channel_status() - clear chan info + * @p_mac: Pointer to Global MAC structure + * + * Return: none + */ +static void sap_clear_channel_status(struct mac_context *p_mac) +{ + csr_clear_channel_status(p_mac); +} + +/** + * sap_weight_channel_noise_floor() - compute noise floor weight + * @sap_ctx: sap context + * @chn_stat: Pointer to chan status info + * + * Return: channel noise floor weight + */ +static uint32_t sap_weight_channel_noise_floor(struct sap_context *sap_ctx, + struct lim_channel_status + *channel_stat) +{ + uint32_t noise_floor_weight; + uint8_t softap_nf_weight_cfg; + uint8_t softap_nf_weight_local; + + softap_nf_weight_cfg = + ACS_WEIGHT_SOFTAP_NOISE_FLOOR_CFG + (sap_ctx->auto_channel_select_weight); + + softap_nf_weight_local = + ACS_WEIGHT_CFG_TO_LOCAL(sap_ctx->auto_channel_select_weight, + softap_nf_weight_cfg); + + if (!channel_stat || channel_stat->channelfreq == 0) + return softap_nf_weight_local; + + noise_floor_weight = (channel_stat->noise_floor == 0) ? 0 : + (ACS_WEIGHT_COMPUTE( + sap_ctx->auto_channel_select_weight, + softap_nf_weight_cfg, + channel_stat->noise_floor - + SOFTAP_MIN_NF, + SOFTAP_MAX_NF - SOFTAP_MIN_NF)); + + if (noise_floor_weight > softap_nf_weight_local) + noise_floor_weight = softap_nf_weight_local; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "nf=%d, nfwc=%d, nfwl=%d, nfw=%d", + channel_stat->noise_floor, + softap_nf_weight_cfg, softap_nf_weight_local, + noise_floor_weight); + + return noise_floor_weight; +} + +/** + * sap_weight_channel_free() - compute channel free weight + * @sap_ctx: sap context + * @chn_stat: Pointer to chan status info + * + * Return: channel free weight + */ +static uint32_t sap_weight_channel_free(struct sap_context *sap_ctx, + struct lim_channel_status + *channel_stat) +{ + uint32_t channel_free_weight; + uint8_t softap_channel_free_weight_cfg; + uint8_t softap_channel_free_weight_local; + uint32_t rx_clear_count = 0; + uint32_t cycle_count = 0; + + softap_channel_free_weight_cfg = + ACS_WEIGHT_SOFTAP_CHANNEL_FREE_CFG + (sap_ctx->auto_channel_select_weight); + + softap_channel_free_weight_local = + ACS_WEIGHT_CFG_TO_LOCAL(sap_ctx->auto_channel_select_weight, + softap_channel_free_weight_cfg); + + if (!channel_stat || channel_stat->channelfreq == 0) + return softap_channel_free_weight_local; + + rx_clear_count = channel_stat->rx_clear_count - + channel_stat->tx_frame_count - + channel_stat->rx_frame_count; + cycle_count = channel_stat->cycle_count; + + /* LSH 4, otherwise it is always 0. */ + channel_free_weight = (cycle_count == 0) ? 0 : + (ACS_WEIGHT_COMPUTE( + sap_ctx->auto_channel_select_weight, + softap_channel_free_weight_cfg, + ((rx_clear_count << 8) + + (cycle_count >> 1))/cycle_count - + (SOFTAP_MIN_CHNFREE << 8), + (SOFTAP_MAX_CHNFREE - + SOFTAP_MIN_CHNFREE) << 8)); + + if (channel_free_weight > softap_channel_free_weight_local) + channel_free_weight = softap_channel_free_weight_local; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "rcc=%d, cc=%d, tc=%d, rc=%d, cfwc=%d, cfwl=%d, cfw=%d", + rx_clear_count, cycle_count, + channel_stat->tx_frame_count, + channel_stat->rx_frame_count, + softap_channel_free_weight_cfg, + softap_channel_free_weight_local, + channel_free_weight); + + return channel_free_weight; +} + +/** + * sap_weight_channel_txpwr_range() - compute channel tx power range weight + * @sap_ctx: sap context + * @chn_stat: Pointer to chan status info + * + * Return: tx power range weight + */ +static uint32_t sap_weight_channel_txpwr_range(struct sap_context *sap_ctx, + struct lim_channel_status + *channel_stat) +{ + uint32_t txpwr_weight_low_speed; + uint8_t softap_txpwr_range_weight_cfg; + uint8_t softap_txpwr_range_weight_local; + + softap_txpwr_range_weight_cfg = + ACS_WEIGHT_SOFTAP_TX_POWER_RANGE_CFG + (sap_ctx->auto_channel_select_weight); + + softap_txpwr_range_weight_local = + ACS_WEIGHT_CFG_TO_LOCAL(sap_ctx->auto_channel_select_weight, + softap_txpwr_range_weight_cfg); + + if (!channel_stat || channel_stat->channelfreq == 0) + return softap_txpwr_range_weight_local; + + + txpwr_weight_low_speed = (channel_stat->chan_tx_pwr_range == 0) ? 0 : + (ACS_WEIGHT_COMPUTE( + sap_ctx->auto_channel_select_weight, + softap_txpwr_range_weight_cfg, + SOFTAP_MAX_TXPWR - + channel_stat->chan_tx_pwr_range, + SOFTAP_MAX_TXPWR - SOFTAP_MIN_TXPWR)); + + if (txpwr_weight_low_speed > softap_txpwr_range_weight_local) + txpwr_weight_low_speed = softap_txpwr_range_weight_local; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "tpr=%d, tprwc=%d, tprwl=%d, tprw=%d", + channel_stat->chan_tx_pwr_range, + softap_txpwr_range_weight_cfg, + softap_txpwr_range_weight_local, + txpwr_weight_low_speed); + + return txpwr_weight_low_speed; +} + +/** + * sap_weight_channel_txpwr_tput() - compute channel tx power + * throughput weight + * @sap_ctx: sap context + * @chn_stat: Pointer to chan status info + * + * Return: tx power throughput weight + */ +static uint32_t sap_weight_channel_txpwr_tput(struct sap_context *sap_ctx, + struct lim_channel_status + *channel_stat) +{ + uint32_t txpwr_weight_high_speed; + uint8_t softap_txpwr_tput_weight_cfg; + uint8_t softap_txpwr_tput_weight_local; + + softap_txpwr_tput_weight_cfg = + ACS_WEIGHT_SOFTAP_TX_POWER_THROUGHPUT_CFG + (sap_ctx->auto_channel_select_weight); + + softap_txpwr_tput_weight_local = + ACS_WEIGHT_CFG_TO_LOCAL(sap_ctx->auto_channel_select_weight, + softap_txpwr_tput_weight_cfg); + + if (!channel_stat || channel_stat->channelfreq == 0) + return softap_txpwr_tput_weight_local; + + txpwr_weight_high_speed = (channel_stat->chan_tx_pwr_throughput == 0) + ? 0 : (ACS_WEIGHT_COMPUTE( + sap_ctx->auto_channel_select_weight, + softap_txpwr_tput_weight_cfg, + SOFTAP_MAX_TXPWR - + channel_stat->chan_tx_pwr_throughput, + SOFTAP_MAX_TXPWR - SOFTAP_MIN_TXPWR)); + + if (txpwr_weight_high_speed > softap_txpwr_tput_weight_local) + txpwr_weight_high_speed = softap_txpwr_tput_weight_local; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "tpt=%d, tptwc=%d, tptwl=%d, tptw=%d", + channel_stat->chan_tx_pwr_throughput, + softap_txpwr_tput_weight_cfg, + softap_txpwr_tput_weight_local, + txpwr_weight_high_speed); + + return txpwr_weight_high_speed; +} + +/** + * sap_weight_channel_status() - compute chan status weight + * @sap_ctx: sap context + * @chn_stat: Pointer to chan status info + * + * Return: chan status weight + */ +static +uint32_t sap_weight_channel_status(struct sap_context *sap_ctx, + struct lim_channel_status *channel_stat) +{ + return sap_weight_channel_noise_floor(sap_ctx, channel_stat) + + sap_weight_channel_free(sap_ctx, channel_stat) + + sap_weight_channel_txpwr_range(sap_ctx, channel_stat) + + sap_weight_channel_txpwr_tput(sap_ctx, channel_stat); +} + +/** + * sap_update_rssi_bsscount() - updates bss count and rssi effect. + * + * @pSpectCh: Channel Information + * @offset: Channel Offset + * @sap_24g: Channel is in 2.4G or 5G + * @spectch_start: the start of spect ch array + * @spectch_end: the end of spect ch array + * + * sap_update_rssi_bsscount updates bss count and rssi effect based + * on the channel offset. + * + * Return: None. + */ + +static void sap_update_rssi_bsscount(tSapSpectChInfo *pSpectCh, int32_t offset, + bool sap_24g, tSapSpectChInfo *spectch_start, + tSapSpectChInfo *spectch_end) +{ + tSapSpectChInfo *pExtSpectCh = NULL; + int32_t rssi, rsssi_effect; + + pExtSpectCh = (pSpectCh + offset); + if (pExtSpectCh && pExtSpectCh >= spectch_start && + pExtSpectCh < spectch_end) { + if (!WLAN_REG_IS_SAME_BAND_FREQS(pSpectCh->chan_freq, + pExtSpectCh->chan_freq)) + return; + ++pExtSpectCh->bssCount; + switch (offset) { + case -1: + case 1: + rsssi_effect = sap_24g ? + SAP_24GHZ_FIRST_OVERLAP_CHAN_RSSI_EFFECT_PRIMARY : + SAP_SUBBAND1_RSSI_EFFECT_PRIMARY; + break; + case -2: + case 2: + rsssi_effect = sap_24g ? + SAP_24GHZ_SEC_OVERLAP_CHAN_RSSI_EFFECT_PRIMARY : + SAP_SUBBAND2_RSSI_EFFECT_PRIMARY; + break; + case -3: + case 3: + rsssi_effect = sap_24g ? + SAP_24GHZ_THIRD_OVERLAP_CHAN_RSSI_EFFECT_PRIMARY : + SAP_SUBBAND3_RSSI_EFFECT_PRIMARY; + break; + case -4: + case 4: + rsssi_effect = sap_24g ? + SAP_24GHZ_FOURTH_OVERLAP_CHAN_RSSI_EFFECT_PRIMARY : + SAP_SUBBAND4_RSSI_EFFECT_PRIMARY; + break; + case -5: + case 5: + rsssi_effect = SAP_SUBBAND5_RSSI_EFFECT_PRIMARY; + break; + case -6: + case 6: + rsssi_effect = SAP_SUBBAND6_RSSI_EFFECT_PRIMARY; + break; + case -7: + case 7: + rsssi_effect = SAP_SUBBAND7_RSSI_EFFECT_PRIMARY; + break; + default: + rsssi_effect = 0; + break; + } + + rssi = pSpectCh->rssiAgr + rsssi_effect; + if (IS_RSSI_VALID(pExtSpectCh->rssiAgr, rssi)) + pExtSpectCh->rssiAgr = rssi; + if (pExtSpectCh->rssiAgr < SOFTAP_MIN_RSSI) + pExtSpectCh->rssiAgr = SOFTAP_MIN_RSSI; + } +} + +/** + * sap_update_rssi_bsscount_vht_5G() - updates bss count and rssi effect. + * + * @spect_ch: Channel Information + * @offset: Channel Offset + * @num_ch: no.of channels + * @spectch_start: the start of spect ch array + * @spectch_end: the end of spect ch array + * + * sap_update_rssi_bsscount_vht_5G updates bss count and rssi effect based + * on the channel offset. + * + * Return: None. + */ + +static void sap_update_rssi_bsscount_vht_5G(tSapSpectChInfo *spect_ch, + int32_t offset, + uint16_t num_ch, + tSapSpectChInfo *spectch_start, + tSapSpectChInfo *spectch_end) +{ + int32_t ch_offset; + uint16_t i, cnt; + + if (!offset) + return; + if (offset > 0) + cnt = num_ch; + else + cnt = num_ch + 1; + for (i = 0; i < cnt; i++) { + ch_offset = offset + i; + if (ch_offset == 0) + continue; + sap_update_rssi_bsscount(spect_ch, ch_offset, false, + spectch_start, spectch_end); + } +} +/** + * sap_interference_rssi_count_5G() - sap_interference_rssi_count + * considers the Adjacent channel rssi and + * data count(here number of BSS observed) + * @spect_ch: Channel Information + * @chan_width: Channel width parsed from beacon IE + * @sec_chan_offset: Secondary Channel Offset + * @center_freq: Central frequency for the given channel. + * @channel_id: channel_id + * @spectch_start: the start of spect ch array + * @spectch_end: the end of spect ch array + * + * sap_interference_rssi_count_5G considers the Adjacent channel rssi + * and data count(here number of BSS observed) + * + * Return: NA. + */ + +static void sap_interference_rssi_count_5G(tSapSpectChInfo *spect_ch, + uint16_t chan_width, + uint16_t sec_chan_offset, + uint32_t ch_freq0, + uint32_t ch_freq1, + uint32_t op_chan_freq, + tSapSpectChInfo *spectch_start, + tSapSpectChInfo *spectch_end) +{ + uint16_t num_ch; + int32_t offset = 0; + + sap_debug("freq = %d, ch width = %d, ch_freq0 = %d ch_freq1 = %d", + op_chan_freq, chan_width, ch_freq0, ch_freq1); + + switch (chan_width) { + case eHT_CHANNEL_WIDTH_40MHZ: + switch (sec_chan_offset) { + /* Above the Primary Channel */ + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + sap_update_rssi_bsscount(spect_ch, 1, false, + spectch_start, spectch_end); + return; + + /* Below the Primary channel */ + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + sap_update_rssi_bsscount(spect_ch, -1, false, + spectch_start, spectch_end); + return; + } + return; + case eHT_CHANNEL_WIDTH_80MHZ: + case eHT_CHANNEL_WIDTH_80P80MHZ: + num_ch = 3; + if ((ch_freq0 - op_chan_freq) == 30) { + offset = 1; + } else if ((ch_freq0 - op_chan_freq) == 10) { + offset = -1; + } else if ((ch_freq0 - op_chan_freq) == -10) { + offset = -2; + } else if ((ch_freq0 - op_chan_freq) == -30) { + offset = -3; + } + break; + case eHT_CHANNEL_WIDTH_160MHZ: + num_ch = 7; + if ((ch_freq0 - op_chan_freq) == 70) + offset = 1; + else if ((ch_freq0 - op_chan_freq) == 50) + offset = -1; + else if ((ch_freq0 - op_chan_freq) == 30) + offset = -2; + else if ((ch_freq0 - op_chan_freq) == 10) + offset = -3; + else if ((ch_freq0 - op_chan_freq) == -10) + offset = -4; + else if ((ch_freq0 - op_chan_freq) == -30) + offset = -5; + else if ((ch_freq0 - op_chan_freq) == -50) + offset = -6; + else if ((ch_freq0 - op_chan_freq) == -70) + offset = -7; + break; + default: + return; + } + + sap_update_rssi_bsscount_vht_5G(spect_ch, offset, num_ch, spectch_start, + spectch_end); +} + +/** + * sap_interference_rssi_count() - sap_interference_rssi_count + * considers the Adjacent channel rssi + * and data count(here number of BSS observed) + * @spect_ch Channel Information + * @spectch_start: the start of spect ch array + * @spectch_end: the end of spect ch array + * + * sap_interference_rssi_count considers the Adjacent channel rssi + * and data count(here number of BSS observed) + * + * Return: None. + */ + +static void sap_interference_rssi_count(tSapSpectChInfo *spect_ch, + tSapSpectChInfo *spectch_start, + tSapSpectChInfo *spectch_end) +{ + if (!spect_ch) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "%s: spect_ch is NULL", __func__); + return; + } + + switch (wlan_freq_to_chan(spect_ch->chan_freq)) { + case CHANNEL_1: + sap_update_rssi_bsscount(spect_ch, 1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 4, true, + spectch_start, spectch_end); + break; + + case CHANNEL_2: + sap_update_rssi_bsscount(spect_ch, -1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 4, true, + spectch_start, spectch_end); + break; + case CHANNEL_3: + sap_update_rssi_bsscount(spect_ch, -2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 4, true, + spectch_start, spectch_end); + break; + case CHANNEL_4: + sap_update_rssi_bsscount(spect_ch, -3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 4, true, + spectch_start, spectch_end); + break; + + case CHANNEL_5: + case CHANNEL_6: + case CHANNEL_7: + case CHANNEL_8: + case CHANNEL_9: + case CHANNEL_10: + sap_update_rssi_bsscount(spect_ch, -4, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 4, true, + spectch_start, spectch_end); + break; + + case CHANNEL_11: + sap_update_rssi_bsscount(spect_ch, -4, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 3, true, + spectch_start, spectch_end); + break; + + case CHANNEL_12: + sap_update_rssi_bsscount(spect_ch, -4, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 2, true, + spectch_start, spectch_end); + break; + + case CHANNEL_13: + sap_update_rssi_bsscount(spect_ch, -4, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -1, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, 1, true, + spectch_start, spectch_end); + break; + + case CHANNEL_14: + sap_update_rssi_bsscount(spect_ch, -4, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -3, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -2, true, + spectch_start, spectch_end); + sap_update_rssi_bsscount(spect_ch, -1, true, + spectch_start, spectch_end); + break; + + default: + break; + } +} + +/** + * ch_in_pcl() - Is channel in the Preferred Channel List (PCL) + * @sap_ctx: SAP context which contains the current PCL + * @channel: Input channel number to be checked + * + * Check if a channel is in the preferred channel list + * + * Return: + * true: channel is in PCL, + * false: channel is not in PCL + */ +static bool ch_in_pcl(struct sap_context *sap_ctx, uint32_t ch_freq) +{ + uint32_t i; + + for (i = 0; i < sap_ctx->acs_cfg->pcl_ch_count; i++) { + if (ch_freq == sap_ctx->acs_cfg->pcl_chan_freq[i]) + return true; + } + + return false; +} + +/** + * sap_upd_chan_spec_params() - sap_upd_chan_spec_params + * updates channel parameters obtained from Beacon + * @scan_entry: Beacon strucutre populated by scan + * @ch_width: Channel width + * @sec_ch_offset: Secondary Channel Offset + * @center_freq0: Central frequency 0 for the given channel + * @center_freq1: Central frequency 1 for the given channel + * + * sap_upd_chan_spec_params updates the spectrum channels based on the + * scan_entry + * + * Return: NA. + */ +static void +sap_upd_chan_spec_params(struct scan_cache_node *scan_entry, + tSirMacHTChannelWidth *ch_width, + uint16_t *sec_ch_offset, + uint32_t *center_freq0, + uint32_t *center_freq1) +{ + enum wlan_phymode phy_mode; + struct channel_info *chan; + + phy_mode = util_scan_entry_phymode(scan_entry->entry); + chan = util_scan_entry_channel(scan_entry->entry); + + if (IS_WLAN_PHYMODE_160MHZ(phy_mode)) { + if (phy_mode == WLAN_PHYMODE_11AC_VHT80_80 || + phy_mode == WLAN_PHYMODE_11AXA_HE80_80) { + *ch_width = eHT_CHANNEL_WIDTH_80P80MHZ; + *center_freq0 = chan->cfreq0; + *center_freq1 = chan->cfreq1; + } else { + *ch_width = eHT_CHANNEL_WIDTH_160MHZ; + if (chan->cfreq1) + *center_freq0 = chan->cfreq1; + else + *center_freq0 = chan->cfreq0; + } + + } else if (IS_WLAN_PHYMODE_80MHZ(phy_mode)) { + *ch_width = eHT_CHANNEL_WIDTH_80MHZ; + *center_freq0 = chan->cfreq0; + } else if (IS_WLAN_PHYMODE_40MHZ(phy_mode)) { + if (chan->cfreq0 > chan->chan_freq) + *sec_ch_offset = PHY_DOUBLE_CHANNEL_LOW_PRIMARY; + else + *sec_ch_offset = PHY_DOUBLE_CHANNEL_HIGH_PRIMARY; + *ch_width = eHT_CHANNEL_WIDTH_40MHZ; + *center_freq0 = chan->cfreq0; + } else { + *ch_width = eHT_CHANNEL_WIDTH_20MHZ; + } +} + +/** + * sap_compute_spect_weight() - Compute spectrum weight + * @pSpectInfoParams: Pointer to the tSpectInfoParams structure + * @mac_handle: Opaque handle to the global MAC context + * @pResult: Pointer to tScanResultHandle + * @sap_ctx: Context of the SAP + * + * Main function for computing the weight of each channel in the + * spectrum based on the RSSI value of the BSSes on the channel + * and number of BSS + */ +static void sap_compute_spect_weight(tSapChSelSpectInfo *pSpectInfoParams, + mac_handle_t mac_handle, + qdf_list_t *scan_list, + struct sap_context *sap_ctx) +{ + int8_t rssi = 0; + uint8_t chn_num = 0; + tSapSpectChInfo *pSpectCh = pSpectInfoParams->pSpectCh; + tSirMacHTChannelWidth ch_width = 0; + uint16_t secondaryChannelOffset; + uint32_t center_freq0, center_freq1; + uint8_t i; + bool found; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tSapSpectChInfo *spectch_start = pSpectInfoParams->pSpectCh; + tSapSpectChInfo *spectch_end = pSpectInfoParams->pSpectCh + + pSpectInfoParams->numSpectChans; + qdf_list_node_t *cur_lst = NULL, *next_lst = NULL; + struct scan_cache_node *cur_node = NULL; + uint32_t normalized_weight; + uint8_t normalize_factor; + uint32_t chan_freq; + struct acs_weight *weight_list = + mac->mlme_cfg->acs.normalize_weight_chan; + struct acs_weight_range *range_list = + mac->mlme_cfg->acs.normalize_weight_range; + bool freq_present_in_list = false; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, Computing spectral weight", __func__); + + if (scan_list) + qdf_list_peek_front(scan_list, &cur_lst); + while (cur_lst) { + cur_node = qdf_container_of(cur_lst, struct scan_cache_node, + node); + pSpectCh = pSpectInfoParams->pSpectCh; + /* Defining the default values, so that any value will hold the default values */ + + secondaryChannelOffset = PHY_SINGLE_CHANNEL_CENTERED; + center_freq0 = 0; + center_freq1 = 0; + + chan_freq = + util_scan_entry_channel_frequency(cur_node->entry); + + sap_upd_chan_spec_params(cur_node, &ch_width, + &secondaryChannelOffset, + ¢er_freq0, ¢er_freq1); + + /* Processing for each tCsrScanResultInfo in the tCsrScanResult DLink list */ + for (chn_num = 0; chn_num < pSpectInfoParams->numSpectChans; + chn_num++) { + + if (chan_freq != pSpectCh->chan_freq) { + pSpectCh++; + continue; + } + + if (pSpectCh->rssiAgr < cur_node->entry->rssi_raw) + pSpectCh->rssiAgr = cur_node->entry->rssi_raw; + + ++pSpectCh->bssCount; + + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq)) + sap_interference_rssi_count(pSpectCh, + spectch_start, spectch_end); + else + sap_interference_rssi_count_5G( + pSpectCh, ch_width, secondaryChannelOffset, + center_freq0, center_freq1, chan_freq, + spectch_start, spectch_end); + + pSpectCh++; + break; + + } + + qdf_list_peek_next(scan_list, cur_lst, &next_lst); + cur_lst = next_lst; + next_lst = NULL; + } + + /* Calculate the weights for all channels in the spectrum pSpectCh */ + pSpectCh = pSpectInfoParams->pSpectCh; + + for (chn_num = 0; chn_num < (pSpectInfoParams->numSpectChans); + chn_num++) { + + /* + rssi : Maximum received signal strength among all BSS on that channel + bssCount : Number of BSS on that channel + */ + + rssi = (int8_t) pSpectCh->rssiAgr; + if (ch_in_pcl(sap_ctx, pSpectCh->chan_freq)) + rssi -= PCL_RSSI_DISCOUNT; + + if (rssi < SOFTAP_MIN_RSSI) + rssi = SOFTAP_MIN_RSSI; + + if (pSpectCh->weight == SAP_ACS_WEIGHT_MAX) { + pSpectCh->weight_copy = pSpectCh->weight; + goto debug_info; + } + + /* There may be channels in scanlist, which were not sent to + * FW for scanning as part of ACS scan list, but they do have an + * effect on the neighbouring channels, so they help to find a + * suitable channel, but there weight should be max as they were + * and not meant to be included in the ACS scan results. + * So just assign RSSI as -100, bsscount as 0, and weight as max + * to them, so that they always stay low in sorting of best + * channles which were included in ACS scan list + */ + found = false; + for (i = 0; i < sap_ctx->num_of_channel; i++) { + if (pSpectCh->chan_freq == sap_ctx->freq_list[i]) { + /* Scan channel was included in ACS scan list */ + found = true; + break; + } + } + + if (found) + pSpectCh->weight = + SAPDFS_NORMALISE_1000 * + (sapweight_rssi_count(sap_ctx, rssi, + pSpectCh->bssCount) + sap_weight_channel_status( + sap_ctx, sap_get_channel_status(mac, + pSpectCh->chan_freq))); + else { + pSpectCh->weight = SAP_ACS_WEIGHT_MAX; + pSpectCh->rssiAgr = SOFTAP_MIN_RSSI; + rssi = SOFTAP_MIN_RSSI; + pSpectCh->bssCount = SOFTAP_MIN_COUNT; + } + + chan_freq = pSpectCh->chan_freq; + + if (wlan_reg_is_dfs_for_freq(mac->pdev, chan_freq)) { + normalize_factor = + MLME_GET_DFS_CHAN_WEIGHT( + mac->mlme_cfg->acs.np_chan_weightage); + freq_present_in_list = true; + } + /* Check if the freq is present in range list */ + for (i = 0; i < mac->mlme_cfg->acs.num_weight_range; i++) { + if (chan_freq >= range_list[i].start_freq && + chan_freq <= range_list[i].end_freq) { + normalize_factor = + range_list[i].normalize_weight; + sap_debug("Range list, freq %d normalize weight factor %d", + chan_freq, normalize_factor); + freq_present_in_list = true; + } + } + + /* Check if user wants a special factor for this freq */ + + for (i = 0; i < mac->mlme_cfg->acs.normalize_weight_num_chan; + i++) { + if (chan_freq == weight_list[i].chan_freq) { + normalize_factor = + weight_list[i].normalize_weight; + sap_debug("freq %d normalize weight factor %d", + chan_freq, normalize_factor); + freq_present_in_list = true; + } + } + + if (freq_present_in_list) { + normalized_weight = + ((SAP_ACS_WEIGHT_MAX - pSpectCh->weight) * + (100 - normalize_factor)) / 100; + sap_debug("freq %d old weight %d new weight %d", + chan_freq, pSpectCh->weight, + pSpectCh->weight + normalized_weight); + pSpectCh->weight += normalized_weight; + freq_present_in_list = false; + } + + if (pSpectCh->weight > SAP_ACS_WEIGHT_MAX) + pSpectCh->weight = SAP_ACS_WEIGHT_MAX; + pSpectCh->weight_copy = pSpectCh->weight; + +debug_info: + sap_debug("freq = %d, weight = %d rssi = %d bss count = %d", + pSpectCh->chan_freq, pSpectCh->weight, + pSpectCh->rssiAgr, pSpectCh->bssCount); + + pSpectCh++; + } + sap_clear_channel_status(mac); +} + +/*========================================================================== + FUNCTION sap_chan_sel_exit + + DESCRIPTION + Exit function for free out the allocated memory, to be called + at the end of the dfsSelectChannel function + + DEPENDENCIES + NA. + + PARAMETERS + + IN + pSpectInfoParams : Pointer to the tSapChSelSpectInfo structure + + RETURN VALUE + void : NULL + + SIDE EFFECTS + ============================================================================*/ +static void sap_chan_sel_exit(tSapChSelSpectInfo *pSpectInfoParams) +{ + /* Free all the allocated memory */ + qdf_mem_free(pSpectInfoParams->pSpectCh); +} + +/*========================================================================== + FUNCTION sap_sort_chl_weight + + DESCRIPTION + Function to sort the channels with the least weight first for 20MHz channels + + DEPENDENCIES + NA. + + PARAMETERS + + IN + pSpectInfoParams : Pointer to the tSapChSelSpectInfo structure + + RETURN VALUE + void : NULL + + SIDE EFFECTS + ============================================================================*/ +static void sap_sort_chl_weight(tSapChSelSpectInfo *pSpectInfoParams) +{ + tSapSpectChInfo temp; + + tSapSpectChInfo *pSpectCh = NULL; + uint32_t i = 0, j = 0, minWeightIndex = 0; + + pSpectCh = pSpectInfoParams->pSpectCh; + for (i = 0; i < pSpectInfoParams->numSpectChans; i++) { + minWeightIndex = i; + for (j = i + 1; j < pSpectInfoParams->numSpectChans; j++) { + if (pSpectCh[j].weight < + pSpectCh[minWeightIndex].weight) { + minWeightIndex = j; + } else if (pSpectCh[j].weight == + pSpectCh[minWeightIndex].weight) { + if (pSpectCh[j].bssCount < + pSpectCh[minWeightIndex].bssCount) + minWeightIndex = j; + } + } + if (minWeightIndex != i) { + qdf_mem_copy(&temp, &pSpectCh[minWeightIndex], + sizeof(*pSpectCh)); + qdf_mem_copy(&pSpectCh[minWeightIndex], &pSpectCh[i], + sizeof(*pSpectCh)); + qdf_mem_copy(&pSpectCh[i], &temp, sizeof(*pSpectCh)); + } + } +} + +/** + * sap_sort_chl_weight_80_mhz() - to sort the channels with the least weight + * @pSpectInfoParams: Pointer to the tSapChSelSpectInfo structure + * Function to sort the channels with the least weight first for HT80 channels + * + * Return: none + */ +static void sap_sort_chl_weight_80_mhz(struct mac_context *mac_ctx, + tSapChSelSpectInfo *pSpectInfoParams) +{ + uint8_t i, j; + tSapSpectChInfo *pSpectInfo; + uint8_t minIdx; + struct ch_params acs_ch_params; + int8_t center_freq_diff; + uint32_t combined_weight; + uint32_t min_ch_weight; + + pSpectInfo = pSpectInfoParams->pSpectCh; + + for (j = 0; j < pSpectInfoParams->numSpectChans; j++) { + + if (pSpectInfo[j].weight_calc_done) + continue; + + acs_ch_params.ch_width = CH_WIDTH_80MHZ; + + wlan_reg_set_channel_params_for_freq(mac_ctx->pdev, + pSpectInfo[j].chan_freq, + 0, &acs_ch_params); + + /* Check if the freq supports 80 Mhz */ + if (acs_ch_params.ch_width != CH_WIDTH_80MHZ) { + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 4; + pSpectInfo[j].weight_calc_done = true; + continue; + } + + center_freq_diff = acs_ch_params.mhz_freq_seg0 - + pSpectInfo[j].chan_freq; + + /* This channel frequency does not have all channels */ + if (center_freq_diff != 30) { + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 4; + pSpectInfo[j].weight_calc_done = true; + continue; + } + + /* no other freq left for 80 Mhz operation in spectrum */ + if (j + 3 > pSpectInfoParams->numSpectChans) + continue; + + /* Check whether all frequencies are present for 80 Mhz */ + + if (!(((pSpectInfo[j].chan_freq + 20) == + pSpectInfo[j + 1].chan_freq) && + ((pSpectInfo[j].chan_freq + 40) == + pSpectInfo[j + 2].chan_freq) && + ((pSpectInfo[j].chan_freq + 60) == + pSpectInfo[j + 3].chan_freq))) { + /* + * some channels does not exist in pSectInfo array, + * skip this channel and those in the same HT80 width + */ + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 4; + pSpectInfo[j].weight_calc_done = true; + if ((pSpectInfo[j].chan_freq + 20) == + pSpectInfo[j + 1].chan_freq) { + pSpectInfo[j + 1].weight = + SAP_ACS_WEIGHT_MAX * 4; + pSpectInfo[j +1].weight_calc_done = true; + } + if ((pSpectInfo[j].chan_freq + 40) == + pSpectInfo[j + 2].chan_freq) { + pSpectInfo[j + 2].weight = + SAP_ACS_WEIGHT_MAX * 4; + pSpectInfo[j +2].weight_calc_done = true; + } + if ((pSpectInfo[j].chan_freq + 60) == + pSpectInfo[j + 3].chan_freq) { + pSpectInfo[j + 3].weight = + SAP_ACS_WEIGHT_MAX * 4; + pSpectInfo[j +3].weight_calc_done = true; + } + + continue; + } + + /* We have 4 channels to calculate cumulative weight */ + + combined_weight = pSpectInfo[j].weight + + pSpectInfo[j + 1].weight + + pSpectInfo[j + 2].weight + + pSpectInfo[j + 3].weight; + + min_ch_weight = pSpectInfo[j].weight; + minIdx = 0; + + for (i = 0; i < 4; i++) { + if (min_ch_weight > pSpectInfo[j + i].weight) { + min_ch_weight = pSpectInfo[j + i].weight; + minIdx = i; + } + pSpectInfo[j + i].weight = SAP_ACS_WEIGHT_MAX * 4; + pSpectInfo[j + i].weight_calc_done = true; + } + + pSpectInfo[j + minIdx].weight = combined_weight; + + sap_debug("best freq = %d for 80mhz center freq %d combined weight = %d", + pSpectInfo[j + minIdx].chan_freq, + acs_ch_params.mhz_freq_seg0, + combined_weight); + } + + sap_sort_chl_weight(pSpectInfoParams); + + pSpectInfo = pSpectInfoParams->pSpectCh; + + for (j = 0; j < (pSpectInfoParams->numSpectChans); j++) { + sap_debug("freq = %d weight = %d rssi = %d bss count = %d", + pSpectInfo->chan_freq, pSpectInfo->weight, + pSpectInfo->rssiAgr, pSpectInfo->bssCount); + + pSpectInfo++; + } +} + +/** + * sap_sort_chl_weight_vht160() - to sort the channels with the least weight + * @pSpectInfoParams: Pointer to the tSapChSelSpectInfo structure + * + * Function to sort the channels with the least weight first for VHT160 channels + * + * Return: none + */ +static void sap_sort_chl_weight_160_mhz(struct mac_context *mac_ctx, + tSapChSelSpectInfo *pSpectInfoParams) +{ + uint8_t i, j; + tSapSpectChInfo *pSpectInfo; + uint8_t minIdx; + struct ch_params acs_ch_params; + int8_t center_freq_diff; + uint32_t combined_weight; + uint32_t min_ch_weight; + + pSpectInfo = pSpectInfoParams->pSpectCh; + + for (j = 0; j < pSpectInfoParams->numSpectChans; j++) { + + if (pSpectInfo[j].weight_calc_done) + continue; + + acs_ch_params.ch_width = CH_WIDTH_160MHZ; + + wlan_reg_set_channel_params_for_freq(mac_ctx->pdev, + pSpectInfo[j].chan_freq, + 0, &acs_ch_params); + + /* Check if the freq supports 160 Mhz */ + if (acs_ch_params.ch_width != CH_WIDTH_160MHZ) { + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j].weight_calc_done = true; + continue; + } + + center_freq_diff = acs_ch_params.mhz_freq_seg1 - + pSpectInfo[j].chan_freq; + + /* This channel frequency does not have all channels */ + if (center_freq_diff != 70) { + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j].weight_calc_done = true; + continue; + } + + /* no other freq left for 160 Mhz operation in spectrum */ + if (j + 7 > pSpectInfoParams->numSpectChans) + continue; + + /* Check whether all frequencies are present for 160 Mhz */ + + if (!(((pSpectInfo[j].chan_freq + 20) == + pSpectInfo[j + 1].chan_freq) && + ((pSpectInfo[j].chan_freq + 40) == + pSpectInfo[j + 2].chan_freq) && + ((pSpectInfo[j].chan_freq + 60) == + pSpectInfo[j + 3].chan_freq) && + ((pSpectInfo[j].chan_freq + 80) == + pSpectInfo[j + 4].chan_freq) && + ((pSpectInfo[j].chan_freq + 100) == + pSpectInfo[j + 5].chan_freq) && + ((pSpectInfo[j].chan_freq + 120) == + pSpectInfo[j + 6].chan_freq) && + ((pSpectInfo[j].chan_freq + 140) == + pSpectInfo[j + 7].chan_freq))) { + /* + * some channels does not exist in pSectInfo array, + * skip this channel and those in the same VHT160 width + */ + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j].weight_calc_done = true; + if ((pSpectInfo[j].chan_freq + 20) == + pSpectInfo[j + 1].chan_freq) { + pSpectInfo[j + 1].weight = + SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j + 1].weight_calc_done = true; + } + if ((pSpectInfo[j].chan_freq + 40) == + pSpectInfo[j + 2].chan_freq) { + pSpectInfo[j + 2].weight = + SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j + 2].weight_calc_done = true; + } + if ((pSpectInfo[j].chan_freq + 60) == + pSpectInfo[j + 3].chan_freq) { + pSpectInfo[j + 3].weight = + SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j + 3].weight_calc_done = true; + } + if ((pSpectInfo[j].chan_freq + 80) == + pSpectInfo[j + 4].chan_freq) { + pSpectInfo[j + 4].weight = + SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j + 4].weight_calc_done = true; + } + if ((pSpectInfo[j].chan_freq + 100) == + pSpectInfo[j + 5].chan_freq) { + pSpectInfo[j + 5].weight = + SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j + 5].weight_calc_done = true; + } + if ((pSpectInfo[j].chan_freq + 120) == + pSpectInfo[j + 6].chan_freq) { + pSpectInfo[j + 6].weight = + SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j + 6].weight_calc_done = true; + } + if ((pSpectInfo[j].chan_freq + 140) == + pSpectInfo[j + 7].chan_freq) { + pSpectInfo[j + 7].weight = + SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j + 7].weight_calc_done = true; + } + + continue; + } + + + /* We have 8 channels to calculate cumulative weight */ + + combined_weight = pSpectInfo[j].weight + + pSpectInfo[j + 1].weight + + pSpectInfo[j + 2].weight + + pSpectInfo[j + 3].weight + + pSpectInfo[j + 4].weight + + pSpectInfo[j + 5].weight + + pSpectInfo[j + 6].weight + + pSpectInfo[j + 7].weight; + + min_ch_weight = pSpectInfo[j].weight; + minIdx = 0; + + for (i = 0; i < 8; i++) { + if (min_ch_weight > pSpectInfo[j + i].weight) { + min_ch_weight = pSpectInfo[j + i].weight; + minIdx = i; + } + pSpectInfo[j + i].weight = SAP_ACS_WEIGHT_MAX * 8; + pSpectInfo[j + i].weight_calc_done = true; + } + + pSpectInfo[j + minIdx].weight = combined_weight; + + sap_debug("best freq = %d for 160mhz center freq %d combined weight = %d", + pSpectInfo[j + minIdx].chan_freq, + acs_ch_params.mhz_freq_seg1, + combined_weight); + } + + sap_sort_chl_weight(pSpectInfoParams); + + pSpectInfo = pSpectInfoParams->pSpectCh; + for (j = 0; j < (pSpectInfoParams->numSpectChans); j++) { + sap_debug("freq = %d weight = %d rssi = %d bss count = %d", + pSpectInfo->chan_freq, pSpectInfo->weight, + pSpectInfo->rssiAgr, pSpectInfo->bssCount); + + pSpectInfo++; + } +} + +/** + * sap_allocate_max_weight_ht40_24_g() - allocate max weight for 40Mhz + * to all 2.4Ghz channels + * @spect_info_params: Pointer to the tSapChSelSpectInfo structure + * + * Return: none + */ +static void +sap_allocate_max_weight_40_mhz_24_g(tSapChSelSpectInfo *spect_info_params) +{ + tSapSpectChInfo *spect_info; + uint8_t j; + + /* + * Assign max weight for 40Mhz (SAP_ACS_WEIGHT_MAX * 2) to all + * 2.4 Ghz channels + */ + spect_info = spect_info_params->pSpectCh; + for (j = 0; j < spect_info_params->numSpectChans; j++) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(spect_info[j].chan_freq)) + spect_info[j].weight = SAP_ACS_WEIGHT_MAX * 2; + } +} + +/** + * sap_allocate_max_weight_ht40_5_g() - allocate max weight for 40Mhz + * to all 5Ghz channels + * @spect_info_params: Pointer to the tSapChSelSpectInfo structure + * + * Return: none + */ +static void +sap_allocate_max_weight_40_mhz(tSapChSelSpectInfo *spect_info_params) +{ + tSapSpectChInfo *spect_info; + uint8_t j; + + /* + * Assign max weight for 40Mhz (SAP_ACS_WEIGHT_MAX * 2) to all + * 5 Ghz channels + */ + spect_info = spect_info_params->pSpectCh; + for (j = 0; j < spect_info_params->numSpectChans; j++) { + if (WLAN_REG_IS_5GHZ_CH_FREQ(spect_info[j].chan_freq) || + WLAN_REG_IS_6GHZ_CHAN_FREQ(spect_info[j].chan_freq)) + spect_info[j].weight = SAP_ACS_WEIGHT_MAX * 2; + } +} + +/** + * sap_sort_chl_weight_ht40_24_g() - to sort channel with the least weight + * @pSpectInfoParams: Pointer to the tSapChSelSpectInfo structure + * + * Function to sort the channels with the least weight first for HT40 channels + * + * Return: none + */ +static void sap_sort_chl_weight_ht40_24_g(struct mac_context *mac_ctx, + tSapChSelSpectInfo *pSpectInfoParams, + v_REGDOMAIN_t domain) +{ + uint8_t i, j; + tSapSpectChInfo *pSpectInfo; + uint32_t tmpWeight1, tmpWeight2; + uint32_t ht40plus2gendch = 0; + uint32_t channel; + uint32_t chan_freq; + + pSpectInfo = pSpectInfoParams->pSpectCh; + /* + * for each HT40 channel, calculate the combined weight of the + * two 20MHz weight + */ + for (i = 0; i < ARRAY_SIZE(acs_ht40_channels24_g); i++) { + for (j = 0; j < pSpectInfoParams->numSpectChans; j++) { + channel = wlan_freq_to_chan(pSpectInfo[j].chan_freq); + if (channel == acs_ht40_channels24_g[i].chStartNum) + break; + } + if (j == pSpectInfoParams->numSpectChans) + continue; + + if (!((pSpectInfo[j].chan_freq + 20) == + pSpectInfo[j + 4].chan_freq)) { + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 2; + continue; + } + /* + * check if there is another channel combination possiblity + * e.g., {1, 5} & {5, 9} + */ + if ((pSpectInfo[j + 4].chan_freq + 20) == + pSpectInfo[j + 8].chan_freq) { + /* need to compare two channel pairs */ + tmpWeight1 = pSpectInfo[j].weight + + pSpectInfo[j + 4].weight; + tmpWeight2 = pSpectInfo[j + 4].weight + + pSpectInfo[j + 8].weight; + if (tmpWeight1 <= tmpWeight2) { + if (pSpectInfo[j].weight <= + pSpectInfo[j + 4].weight) { + pSpectInfo[j].weight = + tmpWeight1; + pSpectInfo[j + 4].weight = + SAP_ACS_WEIGHT_MAX * 2; + pSpectInfo[j + 8].weight = + SAP_ACS_WEIGHT_MAX * 2; + } else { + pSpectInfo[j + 4].weight = + tmpWeight1; + /* for secondary channel selection */ + pSpectInfo[j].weight = + SAP_ACS_WEIGHT_MAX * 2 + - 1; + pSpectInfo[j + 8].weight = + SAP_ACS_WEIGHT_MAX * 2; + } + } else { + if (pSpectInfo[j + 4].weight <= + pSpectInfo[j + 8].weight) { + pSpectInfo[j + 4].weight = + tmpWeight2; + pSpectInfo[j].weight = + SAP_ACS_WEIGHT_MAX * 2; + /* for secondary channel selection */ + pSpectInfo[j + 8].weight = + SAP_ACS_WEIGHT_MAX * 2 + - 1; + } else { + pSpectInfo[j + 8].weight = + tmpWeight2; + pSpectInfo[j].weight = + SAP_ACS_WEIGHT_MAX * 2; + pSpectInfo[j + 4].weight = + SAP_ACS_WEIGHT_MAX * 2; + } + } + } else { + tmpWeight1 = pSpectInfo[j].weight_copy + + pSpectInfo[j + 4].weight_copy; + if (pSpectInfo[j].weight_copy <= + pSpectInfo[j + 4].weight_copy) { + pSpectInfo[j].weight = tmpWeight1; + pSpectInfo[j + 4].weight = + SAP_ACS_WEIGHT_MAX * 2; + } else { + pSpectInfo[j + 4].weight = tmpWeight1; + pSpectInfo[j].weight = + SAP_ACS_WEIGHT_MAX * 2; + } + } + } + /* + * Every channel should be checked. Add the check for the omissive + * channel. Mark the channel whose combination can't satisfy 40MHZ + * as max value, so that it will be sorted to the bottom. + */ + if (REGDOMAIN_FCC == domain) + ht40plus2gendch = HT40PLUS_2G_FCC_CH_END; + else + ht40plus2gendch = HT40PLUS_2G_EURJAP_CH_END; + for (i = HT40MINUS_2G_CH_START; i <= ht40plus2gendch; i++) { + chan_freq = wlan_reg_legacy_chan_to_freq(mac_ctx->pdev, i); + for (j = 0; j < pSpectInfoParams->numSpectChans; j++) { + if (pSpectInfo[j].chan_freq == chan_freq && + ((pSpectInfo[j].chan_freq + 20) != + pSpectInfo[j + 4].chan_freq) && + ((pSpectInfo[j].chan_freq - 20) != + pSpectInfo[j - 4].chan_freq)) + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 2; + } + } + for (i = ht40plus2gendch + 1; i <= HT40MINUS_2G_CH_END; i++) { + chan_freq = wlan_reg_legacy_chan_to_freq(mac_ctx->pdev, i); + for (j = 0; j < pSpectInfoParams->numSpectChans; j++) { + if (pSpectInfo[j].chan_freq == chan_freq && + (pSpectInfo[j].chan_freq - 20) != + pSpectInfo[j - 4].chan_freq) + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 2; + } + } + + pSpectInfo = pSpectInfoParams->pSpectCh; + for (j = 0; j < (pSpectInfoParams->numSpectChans); j++) { + sap_debug("freq = %d weight = %d rssi = %d bss count = %d", + pSpectInfo->chan_freq, pSpectInfo->weight, + pSpectInfo->rssiAgr, pSpectInfo->bssCount); + + pSpectInfo++; + } + + sap_sort_chl_weight(pSpectInfoParams); +} + +static void sap_sort_chl_weight_40_mhz(struct mac_context *mac_ctx, + tSapChSelSpectInfo *pSpectInfoParams) +{ + uint8_t i, j; + tSapSpectChInfo *pSpectInfo; + uint8_t minIdx; + struct ch_params acs_ch_params; + int8_t center_freq_diff; + uint32_t combined_weight; + uint32_t min_ch_weight; + + pSpectInfo = pSpectInfoParams->pSpectCh; + + for (j = 0; j < pSpectInfoParams->numSpectChans; j++) { + + if (WLAN_REG_IS_24GHZ_CH_FREQ(pSpectInfo[j].chan_freq)) + continue; + + if (pSpectInfo[j].weight_calc_done) + continue; + + acs_ch_params.ch_width = CH_WIDTH_40MHZ; + + wlan_reg_set_channel_params_for_freq(mac_ctx->pdev, + pSpectInfo[j].chan_freq, + 0, &acs_ch_params); + + /* Check if the freq supports 40 Mhz */ + if (acs_ch_params.ch_width != CH_WIDTH_40MHZ) { + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 2; + pSpectInfo[j].weight_calc_done = true; + continue; + } + + center_freq_diff = acs_ch_params.mhz_freq_seg0 - + pSpectInfo[j].chan_freq; + + /* This channel frequency does not have all channels */ + if (center_freq_diff != 10) { + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 2; + pSpectInfo[j].weight_calc_done = true; + continue; + } + + /* no other freq left for 40 Mhz operation in spectrum */ + if (j + 1 > pSpectInfoParams->numSpectChans) + continue; + + /* Check whether all frequencies are present for 40 Mhz */ + + if (!((pSpectInfo[j].chan_freq + 20) == + pSpectInfo[j + 1].chan_freq)) { + /* + * some channels does not exist in pSectInfo array, + * skip this channel and those in the same 40 width + */ + pSpectInfo[j].weight = SAP_ACS_WEIGHT_MAX * 2; + pSpectInfo[j].weight_calc_done = true; + + if ((pSpectInfo[j].chan_freq + 20) == + pSpectInfo[j + 1].chan_freq) { + pSpectInfo[j + 1].weight = + SAP_ACS_WEIGHT_MAX * 2; + pSpectInfo[j +1].weight_calc_done = true; + } + + continue; + } + + /* We have 2 channels to calculate cumulative weight */ + + combined_weight = pSpectInfo[j].weight + + pSpectInfo[j + 1].weight; + + min_ch_weight = pSpectInfo[j].weight; + minIdx = 0; + + for (i = 0; i < 2; i++) { + if (min_ch_weight > pSpectInfo[j + i].weight) { + min_ch_weight = pSpectInfo[j + i].weight; + minIdx = i; + } + pSpectInfo[j + i].weight = SAP_ACS_WEIGHT_MAX * 2; + pSpectInfo[j + i].weight_calc_done = true; + } + + pSpectInfo[j + minIdx].weight = combined_weight; + + sap_debug("best freq = %d for 40mhz center freq %d combined weight = %d", + pSpectInfo[j + minIdx].chan_freq, + acs_ch_params.mhz_freq_seg0, + combined_weight); + } + + sap_sort_chl_weight(pSpectInfoParams); + + pSpectInfo = pSpectInfoParams->pSpectCh; + for (j = 0; j < (pSpectInfoParams->numSpectChans); j++) { + sap_debug("freq = %d weight = %d rssi = %d bss count = %d", + pSpectInfo->chan_freq, pSpectInfo->weight, + pSpectInfo->rssiAgr, pSpectInfo->bssCount); + + pSpectInfo++; + } +} + +/*========================================================================== + FUNCTION sap_sort_chl_weight_all + + DESCRIPTION + Function to sort the channels with the least weight first + + DEPENDENCIES + NA. + + PARAMETERS + + IN + sap_ctx : Pointer to the struct sap_context *structure + pSpectInfoParams : Pointer to the tSapChSelSpectInfo structure + + RETURN VALUE + void : NULL + + SIDE EFFECTS + ============================================================================*/ +static void sap_sort_chl_weight_all(struct mac_context *mac_ctx, + struct sap_context *sap_ctx, + tSapChSelSpectInfo *pSpectInfoParams, + uint32_t operatingBand, + v_REGDOMAIN_t domain) +{ + tSapSpectChInfo *pSpectCh = NULL; + uint32_t j = 0; + + pSpectCh = pSpectInfoParams->pSpectCh; + + switch (sap_ctx->acs_cfg->ch_width) { + case CH_WIDTH_40MHZ: + /* + * Assign max weight to all 5Ghz channels when operating band + * is 11g and to all 2.4Ghz channels when operating band is 11a + * or 11abg to avoid selection in ACS algorithm for starting SAP + */ + if (eCSR_DOT11_MODE_11g == operatingBand) { + sap_allocate_max_weight_40_mhz(pSpectInfoParams); + sap_sort_chl_weight_ht40_24_g(mac_ctx, + pSpectInfoParams, + domain); + } else { + sap_allocate_max_weight_40_mhz_24_g(pSpectInfoParams); + sap_sort_chl_weight_40_mhz(mac_ctx, pSpectInfoParams); + } + break; + case CH_WIDTH_80MHZ: + case CH_WIDTH_80P80MHZ: + sap_sort_chl_weight_80_mhz(mac_ctx, pSpectInfoParams); + break; + case CH_WIDTH_160MHZ: + sap_sort_chl_weight_160_mhz(mac_ctx, pSpectInfoParams); + break; + case CH_WIDTH_20MHZ: + default: + /* Sorting the channels as per weights as 20MHz channels */ + sap_sort_chl_weight(pSpectInfoParams); + } + + pSpectCh = pSpectInfoParams->pSpectCh; + for (j = 0; j < (pSpectInfoParams->numSpectChans); j++) { + sap_debug("Freq = %d weight = %d rssi aggr = %d bss count = %d", + pSpectCh->chan_freq, pSpectCh->weight, + pSpectCh->rssiAgr, pSpectCh->bssCount); + pSpectCh++; + } + +} + +/** + * sap_is_ch_non_overlap() - returns true if non-overlapping channel + * @sap_ctx: Sap context + * @ch: channel number + * + * Returns: true if non-overlapping (1, 6, 11) channel, false otherwise + */ +static bool sap_is_ch_non_overlap(struct sap_context *sap_ctx, uint16_t ch) +{ + if (sap_ctx->enableOverLapCh) + return true; + + if ((ch == CHANNEL_1) || (ch == CHANNEL_6) || (ch == CHANNEL_11)) + return true; + + return false; +} + +uint32_t sap_select_channel(mac_handle_t mac_handle, + struct sap_context *sap_ctx, + qdf_list_t *scan_list) +{ + /* DFS param object holding all the data req by the algo */ + tSapChSelSpectInfo spect_info_obj = { NULL, 0 }; + tSapChSelSpectInfo *spect_info = &spect_info_obj; + uint8_t best_ch_num = SAP_CHANNEL_NOT_SELECTED; + uint32_t best_ch_weight = SAP_ACS_WEIGHT_MAX; + uint32_t ht40plus2gendch = 0; + v_REGDOMAIN_t domain; + uint8_t country[CDS_COUNTRY_CODE_LEN + 1]; + uint8_t count; + uint32_t operating_band = 0; + struct mac_context *mac_ctx; + uint32_t best_chan_freq = 0; + + mac_ctx = MAC_CONTEXT(mac_handle); + + /* Initialize the structure pointed by spect_info */ + if (sap_chan_sel_init(mac_handle, spect_info, sap_ctx) != true) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Ch Select initialization failed")); + return SAP_CHANNEL_NOT_SELECTED; + } + + /* Compute the weight of the entire spectrum in the operating band */ + sap_compute_spect_weight(spect_info, mac_handle, scan_list, sap_ctx); + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + /* process avoid channel IE to collect all channels to avoid */ + sap_process_avoid_ie(mac_handle, sap_ctx, scan_list, spect_info); +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + + wlan_reg_read_current_country(mac_ctx->psoc, country); + wlan_reg_get_domain_from_country_code(&domain, country, SOURCE_DRIVER); + + SET_ACS_BAND(operating_band, sap_ctx); + + /* Sort the ch lst as per the computed weights, lesser weight first. */ + sap_sort_chl_weight_all(mac_ctx, sap_ctx, spect_info, operating_band, + domain); + + /*Loop till get the best channel in the given range */ + for (count = 0; count < spect_info->numSpectChans; count++) { + best_chan_freq = spect_info->pSpectCh[count].chan_freq; + /* check if best_ch_num is in preferred channel list */ + best_chan_freq = + sap_select_preferred_channel_from_channel_list( + best_chan_freq, sap_ctx, spect_info); + /* if not in preferred ch lst, go to nxt best ch */ + if (best_chan_freq == SAP_CHANNEL_NOT_SELECTED) + continue; + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + /* + * Weight of the channels(device's AP is operating) + * increased to MAX+1 so that they will be chosen only + * when there is no other best channel to choose + */ + if (!WLAN_REG_IS_6GHZ_CHAN_FREQ(best_chan_freq) && + sap_check_in_avoid_ch_list(sap_ctx, + wlan_reg_freq_to_chan(mac_ctx->pdev, best_chan_freq))) { + best_chan_freq = SAP_CHANNEL_NOT_SELECTED; + continue; + } +#endif + + /* Give preference to Non-overlap channels */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(best_chan_freq) && + !sap_is_ch_non_overlap(sap_ctx, + wlan_reg_freq_to_chan(mac_ctx->pdev, best_chan_freq))) { + sap_debug("freq %d is overlapping, skipped", + best_chan_freq); + continue; + } + + best_ch_weight = spect_info->pSpectCh[count].weight; + sap_debug("Freq = %d selected as best frequency weight = %d", + best_chan_freq, best_ch_weight); + + break; + } + + /* + * in case the best channel seleted is not in PCL and there is another + * channel which has same weightage and is in PCL, choose the one in + * PCL + */ + if (!ch_in_pcl(sap_ctx, best_chan_freq)) { + uint32_t cal_chan_freq, cal_chan_weight; + for (count = 0; count < spect_info->numSpectChans; count++) { + cal_chan_freq = spect_info->pSpectCh[count].chan_freq; + cal_chan_weight = spect_info->pSpectCh[count].weight; + /* skip pcl channel whose weight is bigger than best */ + if (!ch_in_pcl(sap_ctx, cal_chan_freq) || + (cal_chan_weight > best_ch_weight)) + continue; + + if (best_chan_freq == cal_chan_freq) + continue; + + if (sap_select_preferred_channel_from_channel_list( + cal_chan_freq, sap_ctx, + spect_info) + == SAP_CHANNEL_NOT_SELECTED) + continue; + + best_chan_freq = cal_chan_freq; + sap_debug("Changed best freq to %d Preferred freq", + best_chan_freq); + + break; + } + } + + sap_ctx->acs_cfg->pri_ch_freq = best_chan_freq; + + /* Below code is for 2.4Ghz freq, so freq to channel is safe here */ + + /* determine secondary channel for 2.4G channel 5, 6, 7 in HT40 */ + if ((operating_band != eCSR_DOT11_MODE_11g) || + (sap_ctx->acs_cfg->ch_width != CH_WIDTH_40MHZ)) + goto sap_ch_sel_end; + + best_ch_num = wlan_reg_freq_to_chan(mac_ctx->pdev, best_chan_freq); + + if (REGDOMAIN_FCC == domain) + ht40plus2gendch = HT40PLUS_2G_FCC_CH_END; + else + ht40plus2gendch = HT40PLUS_2G_EURJAP_CH_END; + if ((best_ch_num >= HT40MINUS_2G_CH_START) && + (best_ch_num <= ht40plus2gendch)) { + int weight_below, weight_above, i; + tSapSpectChInfo *pspect_info; + + weight_below = weight_above = SAP_ACS_WEIGHT_MAX; + pspect_info = spect_info->pSpectCh; + for (i = 0; i < spect_info->numSpectChans; i++) { + if (pspect_info[i].chan_freq == (best_chan_freq - 20)) + weight_below = pspect_info[i].weight; + if (pspect_info[i].chan_freq == (best_ch_num + 20)) + weight_above = pspect_info[i].weight; + } + + if (weight_below < weight_above) + sap_ctx->acs_cfg->ht_sec_ch_freq = + sap_ctx->acs_cfg->pri_ch_freq - 20; + else + sap_ctx->acs_cfg->ht_sec_ch_freq = + sap_ctx->acs_cfg->pri_ch_freq + 20; + } else if (best_ch_num >= 1 && best_ch_num <= 4) { + sap_ctx->acs_cfg->ht_sec_ch_freq = + sap_ctx->acs_cfg->pri_ch_freq + 20; + } else if (best_ch_num >= ht40plus2gendch && best_ch_num <= + HT40MINUS_2G_CH_END) { + sap_ctx->acs_cfg->ht_sec_ch_freq = + sap_ctx->acs_cfg->pri_ch_freq - 20; + } else if (best_ch_num == 14) { + sap_ctx->acs_cfg->ht_sec_ch_freq = 0; + } + sap_ctx->sec_ch_freq = sap_ctx->acs_cfg->ht_sec_ch_freq; + +sap_ch_sel_end: + /* Free all the allocated memory */ + sap_chan_sel_exit(spect_info); + + return best_chan_freq; +} diff --git a/drivers/staging/qcacld-3.0/core/sap/src/sap_ch_select.h b/drivers/staging/qcacld-3.0/core/sap/src/sap_ch_select.h new file mode 100644 index 0000000000000000000000000000000000000000..5dddc4aba00c69b1999dbc89a474c8a44b75bf36 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sap/src/sap_ch_select.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2012-2015, 2017-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SAP_CH_SELECT_H) +#define __SAP_CH_SELECT_H + +/*=========================================================================== + + sapChSelect.h + + OVERVIEW: + + This software unit holds the implementation of the WLAN SAP modules + functions for channel selection. + + DEPENDENCIES: + + Are listed for each API below. + ===========================================================================*/ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "ani_global.h" +/*-------------------------------------------------------------------------- + defines and enum + --------------------------------------------------------------------------*/ + +#define SPECT_24GHZ_CH_COUNT (11) /* USA regulatory domain */ +#define SAPDFS_NORMALISE_1000 (1000/9) /* Case of spec20 with channel diff = 0 */ +/* Gen 5 values + #define SOFTAP_MIN_RSSI (-85) + #define SOFTAP_MAX_RSSI (-45) + */ +#define SOFTAP_MIN_RSSI (-100) +#define SOFTAP_MAX_RSSI (0) +#define SOFTAP_MIN_COUNT (0) +#define SOFTAP_MAX_COUNT (60) + +#define SOFTAP_MIN_NF (-120) +#define SOFTAP_MAX_NF (-60) +#define SOFTAP_MIN_CHNFREE (0) +#define SOFTAP_MAX_CHNFREE (1) +#define SOFTAP_MIN_TXPWR (0) +#define SOFTAP_MAX_TXPWR (63) + +#define SOFTAP_HT20_CHANNELWIDTH 0 +/* In HT40/VHT80, Effect of primary Channel RSSi on Subband1 */ +#define SAP_SUBBAND1_RSSI_EFFECT_PRIMARY (-20) +/* In VHT80, Effect of primary Channel RSSI on Subband2 */ +#define SAP_SUBBAND2_RSSI_EFFECT_PRIMARY (-30) +/* In VHT80, Effect of Primary Channel RSSI on Subband3 */ +#define SAP_SUBBAND3_RSSI_EFFECT_PRIMARY (-40) +/* In VHT80, Effect of Primary Channel RSSI on Subband4 */ +#define SAP_SUBBAND4_RSSI_EFFECT_PRIMARY (-50) +/* In VHT80, Effect of Primary Channel RSSI on Subband5 */ +#define SAP_SUBBAND5_RSSI_EFFECT_PRIMARY (-60) +/* In VHT80, Effect of Primary Channel RSSI on Subband6 */ +#define SAP_SUBBAND6_RSSI_EFFECT_PRIMARY (-70) +/* In VHT80, Effect of Primary Channel RSSI on Subband7 */ +#define SAP_SUBBAND7_RSSI_EFFECT_PRIMARY (-80) + +#define SAP_24GHZ_FIRST_OVERLAP_CHAN_RSSI_EFFECT_PRIMARY (-10) /* In 2.4GHZ, Effect of Primary Channel RSSI on First Overlapping Channel */ +#define SAP_24GHZ_SEC_OVERLAP_CHAN_RSSI_EFFECT_PRIMARY (-20) /* In 2.4GHZ, Effect of Primary Channel RSSI on Second Overlapping Channel */ +#define SAP_24GHZ_THIRD_OVERLAP_CHAN_RSSI_EFFECT_PRIMARY (-30) /* In 2.4GHZ, Effect of Primary Channel RSSI on Third Overlapping Channel */ +#define SAP_24GHZ_FOURTH_OVERLAP_CHAN_RSSI_EFFECT_PRIMARY (-40) /* In 2.4GHZ, Effect of Primary Channel RSSI on Fourth Overlapping Channel */ + +typedef enum { + CHANNEL_1 = 1, + CHANNEL_2, + CHANNEL_3, + CHANNEL_4, + CHANNEL_5, + CHANNEL_6, + CHANNEL_7, + CHANNEL_8, + CHANNEL_9, + CHANNEL_10, + CHANNEL_11, + CHANNEL_12, + CHANNEL_13, + CHANNEL_14 +} tSapChannel; + +#define MAX_80MHZ_BANDS 6 +#define SAP_80MHZ_MASK 0x0F +#define SAP_40MHZ_MASK_L 0x03 +#define SAP_40MHZ_MASK_H 0x0C + +typedef struct { + uint32_t chan_freq; + uint16_t bssCount; /* bss found in scanresult for this channel */ + int32_t rssiAgr; /* Max value of rssi among all BSS(es) from scanresult for this channel */ + uint32_t weight; /* Weightage of this channel */ + uint32_t weight_copy; /* copy of the orignal weight */ + bool valid; /* Is this a valid center frequency for regulatory domain */ + bool weight_calc_done; +} tSapSpectChInfo; /* tDfsSpectChInfo; */ + +/** + * Structure holding all the information required to make a + * decision for the best operating channel based on dfs formula + */ + +typedef struct { + tSapSpectChInfo *pSpectCh; /* tDfsSpectChInfo *pSpectCh; // Ptr to the channels in the entire spectrum band */ + uint8_t numSpectChans; /* Total num of channels in the spectrum */ +} tSapChSelSpectInfo; /* tDfsChSelParams; */ + +#endif /* if !defined __SAP_CH_SELECT_H */ diff --git a/drivers/staging/qcacld-3.0/core/sap/src/sap_fsm.c b/drivers/staging/qcacld-3.0/core/sap/src/sap_fsm.c new file mode 100644 index 0000000000000000000000000000000000000000..3a43f338d99ea9e389ab5ff67bfdb3e0440e2258 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sap/src/sap_fsm.c @@ -0,0 +1,3969 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*=========================================================================== + + s a p F s m . C + + OVERVIEW: + + This software unit holds the implementation of the WLAN SAP Finite + State Machine modules + + DEPENDENCIES: + + Are listed for each API below. + ===========================================================================*/ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ +#include "sap_internal.h" +#include +#include +#include +#include +/* Pick up the SME API definitions */ +#include "sme_api.h" +/* Pick up the PMC API definitions */ +#include "cds_utils.h" +#include "cds_ieee80211_common_i.h" +#include "cds_reg_service.h" +#include "qdf_util.h" +#include "wlan_policy_mgr_api.h" +#include +#include +#include +#include +#include +#include +#include +#include "wlan_reg_services_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_policy_mgr_ucfg.h" +#include "cfg_ucfg_api.h" + +/*---------------------------------------------------------------------------- + * Preprocessor Definitions and Constants + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Type Declarations + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Global Data Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * External declarations for global context + * -------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------- + * Static Variable Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Static Function Declarations and Definitions + * -------------------------------------------------------------------------*/ +#ifdef SOFTAP_CHANNEL_RANGE +static QDF_STATUS sap_get_freq_list(struct sap_context *sap_ctx, + uint32_t **freq_list, + uint8_t *num_ch); +#endif + +/*========================================================================== + FUNCTION sapStopDfsCacTimer + + DESCRIPTION + Function to sttop the DFS CAC timer when SAP is stopped + DEPENDENCIES + NA. + + PARAMETERS + + IN + sap_ctx: SAP Context + RETURN VALUE + DFS Timer start status + SIDE EFFECTS + ============================================================================*/ + +static int sap_stop_dfs_cac_timer(struct sap_context *sap_ctx); + +/*========================================================================== + FUNCTION sapStartDfsCacTimer + + DESCRIPTION + Function to start the DFS CAC timer when SAP is started on DFS Channel + DEPENDENCIES + NA. + + PARAMETERS + + IN + sap_ctx: SAP Context + RETURN VALUE + DFS Timer start status + SIDE EFFECTS + ============================================================================*/ + +static int sap_start_dfs_cac_timer(struct sap_context *sap_ctx); + +/** sap_hdd_event_to_string() - convert hdd event to string + * @event: eSapHddEvent event type + * + * This function converts eSapHddEvent into string + * + * Return: string for the @event. + */ +static uint8_t *sap_hdd_event_to_string(eSapHddEvent event) +{ + switch (event) { + CASE_RETURN_STRING(eSAP_START_BSS_EVENT); + CASE_RETURN_STRING(eSAP_STOP_BSS_EVENT); + CASE_RETURN_STRING(eSAP_STA_ASSOC_IND); + CASE_RETURN_STRING(eSAP_STA_ASSOC_EVENT); + CASE_RETURN_STRING(eSAP_STA_REASSOC_EVENT); + CASE_RETURN_STRING(eSAP_STA_DISASSOC_EVENT); + CASE_RETURN_STRING(eSAP_STA_SET_KEY_EVENT); + CASE_RETURN_STRING(eSAP_STA_MIC_FAILURE_EVENT); + CASE_RETURN_STRING(eSAP_WPS_PBC_PROBE_REQ_EVENT); + CASE_RETURN_STRING(eSAP_DISCONNECT_ALL_P2P_CLIENT); + CASE_RETURN_STRING(eSAP_MAC_TRIG_STOP_BSS_EVENT); + CASE_RETURN_STRING(eSAP_UNKNOWN_STA_JOIN); + CASE_RETURN_STRING(eSAP_MAX_ASSOC_EXCEEDED); + CASE_RETURN_STRING(eSAP_CHANNEL_CHANGE_EVENT); + CASE_RETURN_STRING(eSAP_DFS_CAC_START); + CASE_RETURN_STRING(eSAP_DFS_CAC_INTERRUPTED); + CASE_RETURN_STRING(eSAP_DFS_CAC_END); + CASE_RETURN_STRING(eSAP_DFS_PRE_CAC_END); + CASE_RETURN_STRING(eSAP_DFS_RADAR_DETECT); + CASE_RETURN_STRING(eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC); + CASE_RETURN_STRING(eSAP_DFS_NO_AVAILABLE_CHANNEL); +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE + CASE_RETURN_STRING(eSAP_ACS_SCAN_SUCCESS_EVENT); +#endif + CASE_RETURN_STRING(eSAP_ACS_CHANNEL_SELECTED); + CASE_RETURN_STRING(eSAP_ECSA_CHANGE_CHAN_IND); + default: + return "eSAP_HDD_EVENT_UNKNOWN"; + } +} + +/*---------------------------------------------------------------------------- + * Externalized Function Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Function Declarations and Documentation + * -------------------------------------------------------------------------*/ + +#ifdef DFS_COMPONENT_ENABLE +/** + * sap_random_channel_sel() - This function randomly pick up an available + * channel + * @sap_ctx: sap context. + * + * This function first eliminates invalid channel, then selects random channel + * using following algorithm: + * + * Return: channel number picked + */ +static uint8_t sap_random_channel_sel(struct sap_context *sap_ctx) +{ + uint8_t ch; + uint8_t ch_wd; + struct wlan_objmgr_pdev *pdev = NULL; + struct ch_params *ch_params; + uint32_t hw_mode, flag = 0; + struct mac_context *mac_ctx; + struct dfs_acs_info acs_info = {0}; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return 0; + } + + pdev = mac_ctx->pdev; + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("null pdev")); + return 0; + } + + ch_params = &mac_ctx->sap.SapDfsInfo.new_ch_params; + if (mac_ctx->sap.SapDfsInfo.orig_chanWidth == 0) { + ch_wd = sap_ctx->ch_width_orig; + mac_ctx->sap.SapDfsInfo.orig_chanWidth = ch_wd; + } else { + ch_wd = mac_ctx->sap.SapDfsInfo.orig_chanWidth; + } + + ch_params->ch_width = ch_wd; + if (sap_ctx->acs_cfg) { + acs_info.acs_mode = sap_ctx->acs_cfg->acs_mode; + acs_info.chan_freq_list = sap_ctx->acs_cfg->master_freq_list; + acs_info.num_of_channel = + sap_ctx->acs_cfg->master_ch_list_count; + } else { + acs_info.acs_mode = false; + } + + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_prefer_non_dfs) + flag |= DFS_RANDOM_CH_FLAG_NO_DFS_CH; + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_disable_japan_w53) + flag |= DFS_RANDOM_CH_FLAG_NO_JAPAN_W53_CH; + if (mac_ctx->sap.SapDfsInfo.sap_operating_chan_preferred_location + == 1) + flag |= DFS_RANDOM_CH_FLAG_NO_UPEER_5G_CH; + else if (mac_ctx->sap.SapDfsInfo. + sap_operating_chan_preferred_location == 2) + flag |= DFS_RANDOM_CH_FLAG_NO_LOWER_5G_CH; + + if (QDF_IS_STATUS_ERROR(utils_dfs_get_vdev_random_channel( + pdev, sap_ctx->vdev, flag, ch_params, &hw_mode, &ch, &acs_info))) { + /* No available channel found */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("No available channel found!!!")); + sap_signal_hdd_event(sap_ctx, NULL, + eSAP_DFS_NO_AVAILABLE_CHANNEL, + (void *)eSAP_STATUS_SUCCESS); + return 0; + } + mac_ctx->sap.SapDfsInfo.new_chanWidth = ch_params->ch_width; + sap_ctx->ch_params.ch_width = ch_params->ch_width; + sap_ctx->ch_params.sec_ch_offset = ch_params->sec_ch_offset; + sap_ctx->ch_params.center_freq_seg0 = ch_params->center_freq_seg0; + sap_ctx->ch_params.center_freq_seg1 = ch_params->center_freq_seg1; + return ch; +} +#else +static uint8_t sap_random_channel_sel(struct sap_context *sap_ctx) +{ + return 0; +} +#endif + +/** + * sap_is_channel_bonding_etsi_weather_channel() - check weather chan bonding. + * @sap_ctx: sap context. + * + * Check if the current SAP operating channel is bonded to weather radar + * channel in ETSI domain. + * + * Return: True if bonded to weather channel in ETSI + */ +static bool +sap_is_channel_bonding_etsi_weather_channel(struct sap_context *sap_ctx) +{ + if (IS_CH_BONDING_WITH_WEATHER_CH(wlan_freq_to_chan( + sap_ctx->chan_freq)) && + (sap_ctx->ch_params.ch_width != CH_WIDTH_20MHZ)) + return true; + + return false; +} + +/* + * sap_get_bonding_channels() - get bonding channels from primary channel. + * @sap_ctx: Handle to SAP context. + * @channel: Channel to get bonded channels. + * @channels: Bonded channel list + * @size: Max bonded channels + * @chanBondState: The channel bonding mode of the passed channel. + * + * Return: Number of sub channels + */ +static uint8_t sap_get_bonding_channels(struct sap_context *sap_ctx, + uint8_t channel, + uint8_t *channels, uint8_t size, + ePhyChanBondState chanBondState) +{ + uint8_t numChannel; + + if (!channels) + return 0; + + if (size < MAX_BONDED_CHANNELS) + return 0; + + switch (chanBondState) { + case PHY_SINGLE_CHANNEL_CENTERED: + numChannel = 1; + channels[0] = channel; + break; + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + numChannel = 2; + channels[0] = channel - 4; + channels[1] = channel; + break; + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + numChannel = 2; + channels[0] = channel; + channels[1] = channel + 4; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW: + numChannel = 4; + channels[0] = channel; + channels[1] = channel + 4; + channels[2] = channel + 8; + channels[3] = channel + 12; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW: + numChannel = 4; + channels[0] = channel - 4; + channels[1] = channel; + channels[2] = channel + 4; + channels[3] = channel + 8; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH: + numChannel = 4; + channels[0] = channel - 8; + channels[1] = channel - 4; + channels[2] = channel; + channels[3] = channel + 4; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH: + numChannel = 4; + channels[0] = channel - 12; + channels[1] = channel - 8; + channels[2] = channel - 4; + channels[3] = channel; + break; + default: + numChannel = 1; + channels[0] = channel; + break; + } + + return numChannel; +} + +/** + * sap_ch_params_to_bonding_channels() - get bonding channels from channel param + * @ch_params: channel params ( bw, pri and sec channel info) + * @channels: bonded channel list + * + * Return: Number of sub channels + */ +static uint8_t sap_ch_params_to_bonding_channels( + struct ch_params *ch_params, + uint8_t *channels) +{ + uint8_t center_chan = ch_params->center_freq_seg0; + uint8_t nchannels = 0; + + switch (ch_params->ch_width) { + case CH_WIDTH_160MHZ: + nchannels = 8; + center_chan = ch_params->center_freq_seg1; + channels[0] = center_chan - 14; + channels[1] = center_chan - 10; + channels[2] = center_chan - 6; + channels[3] = center_chan - 2; + channels[4] = center_chan + 2; + channels[5] = center_chan + 6; + channels[6] = center_chan + 10; + channels[7] = center_chan + 14; + break; + case CH_WIDTH_80P80MHZ: + nchannels = 8; + channels[0] = center_chan - 6; + channels[1] = center_chan - 2; + channels[2] = center_chan + 2; + channels[3] = center_chan + 6; + + center_chan = ch_params->center_freq_seg1; + channels[4] = center_chan - 6; + channels[5] = center_chan - 2; + channels[6] = center_chan + 2; + channels[7] = center_chan + 6; + break; + case CH_WIDTH_80MHZ: + nchannels = 4; + channels[0] = center_chan - 6; + channels[1] = center_chan - 2; + channels[2] = center_chan + 2; + channels[3] = center_chan + 6; + break; + case CH_WIDTH_40MHZ: + nchannels = 2; + channels[0] = center_chan - 2; + channels[1] = center_chan + 2; + break; + default: + nchannels = 1; + channels[0] = center_chan; + break; + } + + return nchannels; +} + +void sap_get_cac_dur_dfs_region(struct sap_context *sap_ctx, + uint32_t *cac_duration_ms, + uint32_t *dfs_region) +{ + int i; + uint8_t channels[MAX_BONDED_CHANNELS]; + uint8_t num_channels; + struct ch_params *ch_params = &sap_ctx->ch_params; + struct mac_context *mac; + + if (!sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "%s: null sap_ctx", __func__); + return; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return; + } + + wlan_reg_get_dfs_region(mac->pdev, dfs_region); + if (mac->sap.SapDfsInfo.ignore_cac) { + *cac_duration_ms = 0; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + "%s: ignore_cac is set", __func__); + return; + } + *cac_duration_ms = DEFAULT_CAC_TIMEOUT; + + if (*dfs_region != DFS_ETSI_REGION) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("sapdfs: default cac duration")); + return; + } + + if (sap_is_channel_bonding_etsi_weather_channel(sap_ctx)) { + *cac_duration_ms = ETSI_WEATHER_CH_CAC_TIMEOUT; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("sapdfs: bonding_etsi_weather_channel")); + return; + } + + qdf_mem_zero(channels, sizeof(channels)); + num_channels = sap_ch_params_to_bonding_channels(ch_params, channels); + for (i = 0; i < num_channels; i++) { + if (IS_ETSI_WEATHER_CH(channels[i])) { + *cac_duration_ms = ETSI_WEATHER_CH_CAC_TIMEOUT; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("sapdfs: ch=%d is etsi weather channel"), + channels[i]); + return; + } + } + +} + +void sap_dfs_set_current_channel(void *ctx) +{ + struct sap_context *sap_ctx = ctx; + uint8_t vht_seg0 = sap_ctx->csr_roamProfile.ch_params.center_freq_seg0; + uint8_t vht_seg1 = sap_ctx->csr_roamProfile.ch_params.center_freq_seg1; + struct wlan_objmgr_pdev *pdev; + struct mac_context *mac_ctx; + uint32_t use_nol = 0; + int error; + bool is_dfs; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return; + } + + pdev = mac_ctx->pdev; + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("null pdev")); + return; + } + + is_dfs = wlan_reg_is_dfs_for_freq(pdev, sap_ctx->chan_freq); + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("freq=%d, dfs %d seg0=%d, seg1=%d, bw %d"), + sap_ctx->chan_freq, is_dfs, vht_seg0, vht_seg1, + sap_ctx->csr_roamProfile.ch_params.ch_width); + + if (is_dfs) { + if (policy_mgr_concurrent_beaconing_sessions_running( + mac_ctx->psoc)) { + uint16_t con_ch_freq; + mac_handle_t handle = MAC_HANDLE(mac_ctx); + + con_ch_freq = + sme_get_beaconing_concurrent_operation_channel( + handle, sap_ctx->sessionId); + if (!con_ch_freq || + !wlan_reg_is_dfs_for_freq(pdev, + con_ch_freq)) + tgt_dfs_get_radars(pdev); + } else { + tgt_dfs_get_radars(pdev); + } + tgt_dfs_set_phyerr_filter_offload(pdev); + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_disable_channel_switch) + tgt_dfs_control(pdev, DFS_SET_USENOL, &use_nol, + sizeof(uint32_t), NULL, NULL, &error); + } +} + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +/** + * sap_check_in_avoid_ch_list() - checks if given channel present is channel + * avoidance list + * + * @sap_ctx: sap context. + * @channel: channel to be checked in sap_ctx's avoid ch list + * + * sap_ctx contains sap_avoid_ch_info strcut containing the list of channels on + * which MDM device's AP with MCC was detected. This function checks if given + * channel is present in that list. + * + * Return: true, if channel was present, false othersie. + */ +bool sap_check_in_avoid_ch_list(struct sap_context *sap_ctx, uint8_t channel) +{ + uint8_t i = 0; + struct sap_avoid_channels_info *ie_info = + &sap_ctx->sap_detected_avoid_ch_ie; + for (i = 0; i < sizeof(ie_info->channels); i++) + if (ie_info->channels[i] == channel) + return true; + return false; +} +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +/** + * sap_dfs_is_channel_in_nol_list() - given bonded channel is available + * @sap_context: Handle to SAP context. + * @channel_number: Channel on which availability should be checked. + * @chan_bondState: The channel bonding mode of the passed channel. + * + * This function Checks if a given bonded channel is available or + * usable for DFS operation. + * + * Return: false if channel is available, true if channel is in NOL. + */ +bool +sap_dfs_is_channel_in_nol_list(struct sap_context *sap_context, + uint8_t channel_number, + ePhyChanBondState chan_bondState) +{ + int i; + struct mac_context *mac_ctx; + uint8_t channels[MAX_BONDED_CHANNELS]; + uint8_t num_channels; + struct wlan_objmgr_pdev *pdev = NULL; + enum channel_state ch_state; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return false; + } + + pdev = mac_ctx->pdev; + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("null pdev")); + return false; + } + + /* get the bonded channels */ + if ((channel_number == wlan_reg_freq_to_chan(pdev, + sap_context->chan_freq)) && + chan_bondState >= PHY_CHANNEL_BONDING_STATE_MAX) + num_channels = sap_ch_params_to_bonding_channels( + &sap_context->ch_params, channels); + else + num_channels = sap_get_bonding_channels(sap_context, + channel_number, channels, + MAX_BONDED_CHANNELS, chan_bondState); + + /* check for NOL, first on will break the loop */ + for (i = 0; i < num_channels; i++) { + ch_state = wlan_reg_get_channel_state(pdev, channels[i]); + if (CHANNEL_STATE_ENABLE != ch_state && + CHANNEL_STATE_DFS != ch_state) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Invalid ch num=%d, ch state=%d"), + channels[i], ch_state); + return true; + } + } /* loop for bonded channels */ + + return false; +} + +bool +sap_chan_bond_dfs_sub_chan(struct sap_context *sap_context, + uint8_t channel_number, + ePhyChanBondState bond_state) +{ + int i; + struct mac_context *mac_ctx; + uint8_t channels[MAX_BONDED_CHANNELS]; + uint8_t num_channels; + struct wlan_objmgr_pdev *pdev; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return false; + } + + pdev = mac_ctx->pdev; + if (!pdev) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("null pdev")); + return false; + } + + if (wlan_reg_chan_has_dfs_attribute(pdev, channel_number)) + return true; + + /* get the bonded channels */ + if ((channel_number == wlan_reg_freq_to_chan(pdev, + sap_context->chan_freq)) && + bond_state >= PHY_CHANNEL_BONDING_STATE_MAX) + num_channels = sap_ch_params_to_bonding_channels( + &sap_context->ch_params, channels); + else + num_channels = sap_get_bonding_channels( + sap_context, channel_number, channels, + MAX_BONDED_CHANNELS, bond_state); + + for (i = 0; i < num_channels; i++) { + if (wlan_reg_chan_has_dfs_attribute(pdev, channels[i])) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("sub ch num=%d is dfs in %d"), + channels[i], channel_number); + return true; + } + } + + return false; +} + +uint32_t sap_select_default_oper_chan(struct mac_context *mac_ctx, + struct sap_acs_cfg *acs_cfg) +{ + uint16_t i; + uint32_t freq0 = 0, freq1 = 0, freq2 = 0, default_freq; + + if (!acs_cfg) + return 0; + + if (!acs_cfg->ch_list_count || !acs_cfg->freq_list) { + if (mac_ctx->mlme_cfg->acs.force_sap_start) { + sap_debug("SAP forced, freq selected %d", + acs_cfg->master_freq_list[0]); + return acs_cfg->master_freq_list[0]; + } else { + sap_debug("No channel left for operation"); + return 0; + } + } + /* + * There could be both 2.4Ghz and 5ghz channels present in the list + * based upon the Hw mode received from hostapd, it is always better + * to chose a default 5ghz operating channel than 2.4ghz, as it can + * provide a better throughput, latency than 2.4ghz. Also 40 Mhz is + * rare in 2.4ghz band, so 5ghz should be preferred. If we get a 5Ghz + * chan in the acs cfg ch list , we should go for that first else the + * default channel can be 2.4ghz. + * Add check regulatory channel state before select the channel. + */ + + for (i = 0; i < acs_cfg->ch_list_count; i++) { + enum channel_state state = + wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, acs_cfg->freq_list[i]); + if (!freq0 && state == CHANNEL_STATE_ENABLE && + WLAN_REG_IS_5GHZ_CH_FREQ(acs_cfg->freq_list[i])) { + freq0 = acs_cfg->freq_list[i]; + break; + } else if (!freq1 && state == CHANNEL_STATE_DFS && + WLAN_REG_IS_5GHZ_CH_FREQ(acs_cfg->freq_list[i])) { + freq1 = acs_cfg->freq_list[i]; + } else if (!freq2 && state == CHANNEL_STATE_ENABLE) { + freq2 = acs_cfg->freq_list[i]; + } + } + default_freq = freq0; + if (!default_freq) + default_freq = freq1; + if (!default_freq) + default_freq = freq2; + if (!default_freq) + default_freq = acs_cfg->freq_list[0]; + + sap_debug("default freq %d chosen from %d %d %d %d", default_freq, + freq0, freq1, freq2, acs_cfg->freq_list[0]); + + return default_freq; +} + +static bool is_mcc_preferred(struct sap_context *sap_context, + uint32_t con_ch_freq) +{ + /* + * If SAP ACS channel list is 1-11 and STA is on non-preferred + * channel i.e. 12, 13, 14 then MCC is unavoidable. This is because + * if SAP is started on 12,13,14 some clients may not be able to + * join dependending on their regulatory country. + */ + if ((con_ch_freq >= 2467) && (con_ch_freq <= 2484) && + (sap_context->acs_cfg->start_ch_freq >= 2412 && + sap_context->acs_cfg->end_ch_freq <= 2462)) { + sap_debug("conc ch freq %d & sap acs ch list is 1-11, prefer mcc", + con_ch_freq); + return true; + } + + return false; +} + +QDF_STATUS +sap_validate_chan(struct sap_context *sap_context, + bool pre_start_bss, + bool check_for_connection_update) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac_ctx; + mac_handle_t mac_handle; + uint32_t con_ch_freq; + bool sta_sap_scc_on_dfs_chan; + uint32_t sta_go_bit_mask = QDF_STA_MASK | QDF_P2P_GO_MASK; + uint32_t sta_sap_bit_mask = QDF_STA_MASK | QDF_SAP_MASK; + uint32_t concurrent_state; + bool go_force_scc; + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + mac_ctx = MAC_CONTEXT(mac_handle); + if (!mac_ctx) { + /* we have a serious problem */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_FATAL, + FL("invalid MAC handle")); + return QDF_STATUS_E_FAULT; + } + + if (!sap_context->chan_freq) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Invalid channel")); + return QDF_STATUS_E_FAILURE; + } + go_force_scc = policy_mgr_go_scc_enforced(mac_ctx->psoc); + if (sap_context->vdev && !go_force_scc && + (wlan_vdev_mlme_get_opmode(sap_context->vdev) == QDF_P2P_GO_MODE)) + goto validation_done; + + concurrent_state = policy_mgr_get_concurrency_mode(mac_ctx->psoc); + if (policy_mgr_concurrent_beaconing_sessions_running(mac_ctx->psoc) || + ((concurrent_state & sta_sap_bit_mask) == sta_sap_bit_mask) || + ((concurrent_state & sta_go_bit_mask) == sta_go_bit_mask)) { +#ifdef FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE + if (wlan_reg_is_dfs_for_freq(mac_ctx->pdev, + sap_context->chan_freq)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_WARN, + FL("DFS not supported in STA_AP Mode")); + return QDF_STATUS_E_ABORTED; + } +#endif +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + if (sap_context->cc_switch_mode != + QDF_MCC_TO_SCC_SWITCH_DISABLE) { + con_ch_freq = sme_check_concurrent_channel_overlap( + mac_handle, + sap_context->chan_freq, + sap_context->csr_roamProfile.phyMode, + sap_context->cc_switch_mode); + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("After check overlap: con_ch:%d"), + con_ch_freq); + if (sap_context->cc_switch_mode != + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) { + if (QDF_IS_STATUS_ERROR( + policy_mgr_valid_sap_conc_channel_check( + mac_ctx->psoc, &con_ch_freq, + sap_context->chan_freq, + sap_context->sessionId))) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_WARN, + FL("SAP can't start (no MCC)")); + return QDF_STATUS_E_ABORTED; + } + } + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("After check concurrency: con_ch:%d"), + con_ch_freq); + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan( + mac_ctx->psoc); + if (con_ch_freq && + (policy_mgr_sta_sap_scc_on_lte_coex_chan( + mac_ctx->psoc) || + policy_mgr_is_safe_channel( + mac_ctx->psoc, con_ch_freq)) && + (!wlan_reg_is_dfs_for_freq( + mac_ctx->pdev, con_ch_freq) || + sta_sap_scc_on_dfs_chan)) { + if (is_mcc_preferred(sap_context, con_ch_freq)) + goto validation_done; + + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_DEBUG, + "%s: Override ch freq %d to %d due to CC Intf", + __func__, sap_context->chan_freq, + con_ch_freq); + sap_context->chan_freq = con_ch_freq; + if (WLAN_REG_IS_24GHZ_CH_FREQ( + sap_context->chan_freq)) + sap_context->ch_params.ch_width = + CH_WIDTH_20MHZ; + wlan_reg_set_channel_params_for_freq( + mac_ctx->pdev, + sap_context->chan_freq, + 0, + &sap_context->ch_params); + } + } +#endif + } +validation_done: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("for configured channel, Ch_freq = %d"), + sap_context->chan_freq); + + if (check_for_connection_update) { + /* This wait happens in the hostapd context. The event + * is set in the MC thread context. + */ + qdf_status = + policy_mgr_update_and_wait_for_connection_update( + mac_ctx->psoc, sap_context->sessionId, + sap_context->chan_freq, + POLICY_MGR_UPDATE_REASON_START_AP); + if (QDF_IS_STATUS_ERROR(qdf_status)) + return qdf_status; + } + + if (pre_start_bss) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO, + FL("ACS end due to Ch override. Sel Ch freq = %d"), + sap_context->chan_freq); + sap_context->acs_cfg->pri_ch_freq = sap_context->chan_freq; + sap_context->acs_cfg->ch_width = + sap_context->ch_width_orig; + sap_config_acs_result(mac_handle, sap_context, 0); + return QDF_STATUS_E_CANCELED; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sap_channel_sel(struct sap_context *sap_context) +{ + QDF_STATUS qdf_ret_status; + struct mac_context *mac_ctx; + struct scan_start_request *req; + struct wlan_objmgr_vdev *vdev = NULL; + uint8_t i; + uint32_t *freq_list = NULL; + uint8_t num_of_channels = 0; + mac_handle_t mac_handle; + uint32_t con_ch_freq; + uint8_t vdev_id; + uint32_t scan_id; + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (!mac_handle) { + /* we have a serious problem */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_FATAL, + FL("invalid mac_handle")); + return QDF_STATUS_E_FAULT; + } + + mac_ctx = MAC_CONTEXT(mac_handle); + if (!mac_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Invalid MAC context")); + return QDF_STATUS_E_FAILURE; + } + if (sap_context->chan_freq) + return sap_validate_chan(sap_context, true, false); + + if (policy_mgr_concurrent_beaconing_sessions_running(mac_ctx->psoc) || + ((sap_context->cc_switch_mode == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) && + (policy_mgr_mode_specific_connection_count(mac_ctx->psoc, + PM_SAP_MODE, NULL) || + policy_mgr_mode_specific_connection_count(mac_ctx->psoc, + PM_P2P_GO_MODE, + NULL)))) { + con_ch_freq = sme_get_beaconing_concurrent_operation_channel( + mac_handle, sap_context->sessionId); +#ifdef FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE + if (con_ch_freq) + sap_context->dfs_ch_disable = true; +#endif + } + + if ((policy_mgr_get_concurrency_mode(mac_ctx->psoc) == + (QDF_STA_MASK | QDF_SAP_MASK)) || + ((sap_context->cc_switch_mode == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) && + (policy_mgr_get_concurrency_mode(mac_ctx->psoc) == + (QDF_STA_MASK | QDF_P2P_GO_MASK)))) { +#ifdef FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE + sap_context->dfs_ch_disable = true; +#endif + } +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("%s skip_acs_status = %d "), __func__, + sap_context->acs_cfg->skip_scan_status); + if (sap_context->acs_cfg->skip_scan_status != + eSAP_SKIP_ACS_SCAN) { +#endif + + if (sap_context->freq_list) { + qdf_mem_free(sap_context->freq_list); + sap_context->freq_list = NULL; + sap_context->num_of_channel = 0; + } + + sap_get_freq_list(sap_context, &freq_list, &num_of_channels); + if (!num_of_channels) { + sap_err("No freq sutiable for SAP in current list, SAP failed"); + return QDF_STATUS_E_FAILURE; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + qdf_mem_free(freq_list); + return QDF_STATUS_E_NOMEM; + } + + vdev_id = sap_context->sessionId; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Invalid vdev objmgr")); + qdf_mem_free(freq_list); + qdf_mem_free(req); + return QDF_STATUS_E_INVAL; + } + + /* Initiate a SCAN request */ + ucfg_scan_init_default_params(vdev, req); + scan_id = ucfg_scan_get_scan_id(mac_ctx->psoc); + req->scan_req.scan_id = scan_id; + req->scan_req.vdev_id = vdev_id; + req->scan_req.scan_f_passive = false; + req->scan_req.scan_req_id = sap_context->req_id; + req->scan_req.scan_priority = SCAN_PRIORITY_HIGH; + req->scan_req.scan_f_bcast_probe = true; + + req->scan_req.chan_list.num_chan = num_of_channels; + for (i = 0; i < num_of_channels; i++) + req->scan_req.chan_list.chan[i].freq = freq_list[i]; + sap_context->freq_list = freq_list; + sap_context->num_of_channel = num_of_channels; + /* Set requestType to Full scan */ + + sap_context->acs_req_timestamp = qdf_get_time_of_the_day_ms(); + qdf_ret_status = ucfg_scan_start(req); + if (qdf_ret_status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("scan request fail %d!!!"), + qdf_ret_status); + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("SAP Configuring default ch, Ch_freq=%d"), + sap_context->chan_freq); + sap_context->chan_freq = sap_select_default_oper_chan( + mac_ctx, sap_context->acs_cfg); + + if (sap_context->freq_list) { + sap_context->chan_freq = + sap_context->freq_list[0]; + qdf_mem_free(sap_context->freq_list); + sap_context->freq_list = NULL; + sap_context->num_of_channel = 0; + } + /* + * In case of ACS req before start Bss, + * return failure so that the calling + * function can use the default channel. + */ + qdf_ret_status = QDF_STATUS_E_FAILURE; + goto release_vdev_ref; + } else { + host_log_acs_scan_start(scan_id, vdev_id); + } +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE + } else { + sap_context->acs_cfg->skip_scan_status = eSAP_SKIP_ACS_SCAN; + } + + if (sap_context->acs_cfg->skip_scan_status == eSAP_SKIP_ACS_SCAN) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("## %s SKIPPED ACS SCAN"), __func__); + wlansap_pre_start_bss_acs_scan_callback(mac_handle, + sap_context, sap_context->sessionId, 0, + eCSR_SCAN_SUCCESS); + } +#endif + + qdf_ret_status = QDF_STATUS_SUCCESS; + +release_vdev_ref: + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return qdf_ret_status; +} + +/** + * sap_find_valid_concurrent_session() - to find valid concurrent session + * @mac_handle: Opaque handle to the global MAC context + * + * This API will check if any valid concurrent SAP session is present + * + * Return: pointer to sap context of valid concurrent session + */ +static struct sap_context * +sap_find_valid_concurrent_session(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t intf = 0; + struct sap_context *sap_ctx; + + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + if (((QDF_SAP_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona) || + (QDF_P2P_GO_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona)) && + mac_ctx->sap.sapCtxList[intf].sap_context) { + sap_ctx = mac_ctx->sap.sapCtxList[intf].sap_context; + if (sap_ctx->fsm_state != SAP_INIT) + return sap_ctx; + } + } + + return NULL; +} + +static QDF_STATUS sap_clear_global_dfs_param(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct sap_context *sap_ctx; + + sap_ctx = sap_find_valid_concurrent_session(mac_handle); + if (sap_ctx && WLAN_REG_IS_5GHZ_CH_FREQ(sap_ctx->chan_freq)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + "conc session exists, no need to clear dfs struct"); + return QDF_STATUS_SUCCESS; + } + /* + * CAC timer will be initiated and started only when SAP starts + * on DFS channel and it will be stopped and destroyed + * immediately once the radar detected or timedout. So + * as per design CAC timer should be destroyed after stop + */ + if (mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running) { + qdf_mc_timer_stop(&mac_ctx->sap.SapDfsInfo.sap_dfs_cac_timer); + mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running = 0; + qdf_mc_timer_destroy( + &mac_ctx->sap.SapDfsInfo.sap_dfs_cac_timer); + } + mac_ctx->sap.SapDfsInfo.cac_state = eSAP_DFS_DO_NOT_SKIP_CAC; + sap_cac_reset_notify(mac_handle); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sap_acquire_vdev_ref(struct wlan_objmgr_psoc *psoc, + struct sap_context *sap_ctx, + uint8_t session_id) +{ + struct wlan_objmgr_vdev *vdev; + + if (sap_ctx->vdev) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Invalid vdev obj in sap context")); + return QDF_STATUS_E_FAULT; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, session_id, + WLAN_LEGACY_SAP_ID); + if (!vdev) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("vdev is NULL for vdev_id: %u"), session_id); + return QDF_STATUS_E_FAILURE; + } + + sap_ctx->vdev = vdev; + return QDF_STATUS_SUCCESS; +} + +void sap_release_vdev_ref(struct sap_context *sap_ctx) +{ + struct wlan_objmgr_vdev *vdev; + + if (!sap_ctx) { + sap_debug("Invalid SAP pointer"); + return; + } + + vdev = sap_ctx->vdev; + if (vdev) { + sap_ctx->vdev = NULL; + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SAP_ID); + } +} + +QDF_STATUS sap_set_session_param(mac_handle_t mac_handle, + struct sap_context *sapctx, + uint32_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + int i; + + sapctx->sessionId = session_id; + sapctx->is_pre_cac_on = false; + sapctx->pre_cac_complete = false; + sapctx->chan_before_pre_cac = 0; + + /* When SSR, SAP will restart, clear the old context,sessionId */ + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + if (mac_ctx->sap.sapCtxList[i].sap_context == sapctx) + mac_ctx->sap.sapCtxList[i].sap_context = NULL; + } + + mac_ctx->sap.sapCtxList[sapctx->sessionId].sap_context = sapctx; + mac_ctx->sap.sapCtxList[sapctx->sessionId].sapPersona = + sapctx->csr_roamProfile.csrPersona; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + "%s: Initializing sap_ctx = %pK with session = %d", __func__, + sapctx, session_id); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sap_clear_session_param(mac_handle_t mac_handle, + struct sap_context *sapctx, + uint32_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + if (sapctx->sessionId >= SAP_MAX_NUM_SESSION) + return QDF_STATUS_E_FAILURE; + + mac_ctx->sap.sapCtxList[sapctx->sessionId].sap_context = NULL; + mac_ctx->sap.sapCtxList[sapctx->sessionId].sapPersona = + QDF_MAX_NO_OF_MODE; + sap_clear_global_dfs_param(mac_handle); + sap_free_roam_profile(&sapctx->csr_roamProfile); + sap_err("Set sapCtxList null for session %d", sapctx->sessionId); + qdf_mem_zero(sapctx, sizeof(*sapctx)); + sapctx->sessionId = WLAN_UMAC_VDEV_ID_MAX; + + return QDF_STATUS_SUCCESS; +} + +/** + * sap_goto_stopping() - Processing of SAP FSM stopping state + * @sap_ctx: pointer to sap Context + * + * Return: QDF_STATUS code associated with performing the operation + */ +static QDF_STATUS sap_goto_stopping(struct sap_context *sap_ctx) +{ + QDF_STATUS status; + struct mac_context *mac_ctx; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + /* we have a serious problem */ + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + sap_free_roam_profile(&sap_ctx->csr_roamProfile); + status = sme_roam_stop_bss(MAC_HANDLE(mac_ctx), sap_ctx->sessionId); + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "Error: In %s calling sme_roam_stop_bss status = %d", + __func__, status); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * sap_goto_init() - Function for setting the SAP FSM to init state + * @sap_ctx: pointer to sap context + * + * Return: QDF_STATUS code associated with performing the operation + */ +static QDF_STATUS sap_goto_init(struct sap_context *sap_ctx) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct sap_sm_event sap_event; + /* Processing has to be coded */ + + /* + * Clean up stations from TL etc as AP BSS is shut down + * then set event + */ + + /* hardcoded event */ + sap_event.event = eSAP_MAC_READY_FOR_CONNECTIONS; + sap_event.params = 0; + sap_event.u1 = 0; + sap_event.u2 = 0; + /* Handle event */ + qdf_status = sap_fsm(sap_ctx, &sap_event); + + return qdf_status; +} + +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE +/** + * sap_handle_acs_scan_event() - handle acs scan event for SAP + * @sap_context: ptSapContext + * @sap_event: struct sap_event + * @status: status of acs scan + * + * The function is to handle the eSAP_ACS_SCAN_SUCCESS_EVENT event. + * + * Return: void + */ +static void sap_handle_acs_scan_event(struct sap_context *sap_context, + struct sap_event *sap_event, eSapStatus status) +{ + sap_event->sapHddEventCode = eSAP_ACS_SCAN_SUCCESS_EVENT; + sap_event->sapevt.sap_acs_scan_comp.status = status; + sap_event->sapevt.sap_acs_scan_comp.num_of_channels = + sap_context->num_of_channel; + sap_event->sapevt.sap_acs_scan_comp.freq_list = + sap_context->freq_list; +} +#else +static void sap_handle_acs_scan_event(struct sap_context *sap_context, + struct sap_event *sap_event, eSapStatus status) +{ +} +#endif + +#define DH_OUI_TYPE "\x20" +#define DH_OUI_TYPE_SIZE (1) +/** + * sap_fill_owe_ie_in_assoc_ind() - Fill OWE IE in assoc indication + * Function to fill OWE IE in assoc indication + * @assoc_ind: SAP STA association indication + * @sme_assoc_ind: SME association indication + * + * This function is to get OWE IEs (RSN IE, DH IE etc) from assoc request + * and fill them in association indication. + * + * Return: true for success and false for failure + */ +static bool sap_fill_owe_ie_in_assoc_ind(tSap_StationAssocIndication *assoc_ind, + struct assoc_ind *sme_assoc_ind) +{ + uint32_t owe_ie_len, rsn_ie_len, dh_ie_len; + const uint8_t *rsn_ie, *dh_ie; + + if (assoc_ind->assocReqLength < ASSOC_REQ_IE_OFFSET) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid assoc req"); + return false; + } + + rsn_ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_RSN, + assoc_ind->assocReqPtr + ASSOC_REQ_IE_OFFSET, + assoc_ind->assocReqLength - ASSOC_REQ_IE_OFFSET); + if (!rsn_ie) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "RSN IE is not present"); + return false; + } + rsn_ie_len = rsn_ie[1] + 2; + if (rsn_ie_len < DOT11F_IE_RSN_MIN_LEN || + rsn_ie_len > DOT11F_IE_RSN_MAX_LEN) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid RSN IE len %d", + rsn_ie_len); + return false; + } + + dh_ie = wlan_get_ext_ie_ptr_from_ext_id(DH_OUI_TYPE, DH_OUI_TYPE_SIZE, + assoc_ind->assocReqPtr + ASSOC_REQ_IE_OFFSET, + (uint16_t)(assoc_ind->assocReqLength - ASSOC_REQ_IE_OFFSET)); + if (!dh_ie) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "DH IE is not present"); + return false; + } + dh_ie_len = dh_ie[1] + 2; + if (dh_ie_len < DOT11F_IE_DH_PARAMETER_ELEMENT_MIN_LEN || + dh_ie_len > DOT11F_IE_DH_PARAMETER_ELEMENT_MAX_LEN) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid DH IE len %d", + dh_ie_len); + return false; + } + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO, + FL("rsn_ie_len = %d, dh_ie_len = %d"), rsn_ie_len, dh_ie_len); + + owe_ie_len = rsn_ie_len + dh_ie_len; + assoc_ind->owe_ie = qdf_mem_malloc(owe_ie_len); + if (!assoc_ind->owe_ie) + return false; + + qdf_mem_copy(assoc_ind->owe_ie, rsn_ie, rsn_ie_len); + qdf_mem_copy(assoc_ind->owe_ie + rsn_ie_len, dh_ie, dh_ie_len); + assoc_ind->owe_ie_len = owe_ie_len; + + return true; +} + +/** + * sap_save_owe_pending_assoc_ind() - Save pending assoc indication + * Function to save pending assoc indication in SAP context + * @sap_ctx: SAP context + * @sme_assoc_ind: SME association indication + * + * This function is to save pending assoc indication in linked list + * in SAP context. + * + * Return: true for success and false for failure + */ +static bool sap_save_owe_pending_assoc_ind(struct sap_context *sap_ctx, + struct assoc_ind *sme_assoc_ind) +{ + struct owe_assoc_ind *assoc_ind; + QDF_STATUS status; + + assoc_ind = qdf_mem_malloc(sizeof(*assoc_ind)); + if (!assoc_ind) + return false; + assoc_ind->assoc_ind = sme_assoc_ind; + status = qdf_list_insert_back(&sap_ctx->owe_pending_assoc_ind_list, + &assoc_ind->node); + if (QDF_STATUS_SUCCESS != status) { + qdf_mem_free(assoc_ind); + return false; + } + + return true; +} + +/** + * sap_signal_hdd_event() - send event notification + * @sap_ctx: Sap Context + * @csr_roaminfo: Pointer to CSR roam information + * @sap_hddevent: SAP HDD event + * @context: to pass the element for future support + * + * Function for HDD to send the event notification using callback + * + * Return: QDF_STATUS + */ +QDF_STATUS sap_signal_hdd_event(struct sap_context *sap_ctx, + struct csr_roam_info *csr_roaminfo, eSapHddEvent sap_hddevent, + void *context) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct sap_event sap_ap_event = {0}; + struct mac_context *mac_ctx; + struct oem_channel_info *chaninfo; + tSap_StationAssocIndication *assoc_ind; + tSap_StartBssCompleteEvent *bss_complete; + struct sap_ch_selected_s *acs_selected; + tSap_StationAssocReassocCompleteEvent *reassoc_complete; + tSap_StationDisassocCompleteEvent *disassoc_comp; + tSap_StationSetKeyCompleteEvent *key_complete; + tSap_StationMICFailureEvent *mic_failure; + + /* Format the Start BSS Complete event to return... */ + if (!sap_ctx->sap_event_cb) + return QDF_STATUS_E_FAILURE; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAILURE; + } + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("SAP event callback event = %s"), + sap_hdd_event_to_string(sap_hddevent)); + + switch (sap_hddevent) { + case eSAP_STA_ASSOC_IND: + if (!csr_roaminfo) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Invalid CSR Roam Info")); + return QDF_STATUS_E_INVAL; + } + /* TODO - Indicate the assoc request indication to OS */ + sap_ap_event.sapHddEventCode = eSAP_STA_ASSOC_IND; + assoc_ind = &sap_ap_event.sapevt.sapAssocIndication; + + qdf_copy_macaddr(&assoc_ind->staMac, &csr_roaminfo->peerMac); + assoc_ind->staId = csr_roaminfo->staId; + assoc_ind->status = 0; + /* Required for indicating the frames to upper layer */ + assoc_ind->beaconLength = csr_roaminfo->beaconLength; + assoc_ind->beaconPtr = csr_roaminfo->beaconPtr; + assoc_ind->assocReqLength = csr_roaminfo->assocReqLength; + assoc_ind->assocReqPtr = csr_roaminfo->assocReqPtr; + assoc_ind->fWmmEnabled = csr_roaminfo->wmmEnabledSta; + assoc_ind->ecsa_capable = csr_roaminfo->ecsa_capable; + if (csr_roaminfo->u.pConnectedProfile) { + assoc_ind->negotiatedAuthType = + csr_roaminfo->u.pConnectedProfile->AuthType; + assoc_ind->negotiatedUCEncryptionType = + csr_roaminfo->u.pConnectedProfile->EncryptionType; + assoc_ind->negotiatedMCEncryptionType = + csr_roaminfo->u.pConnectedProfile->mcEncryptionType; + assoc_ind->fAuthRequired = csr_roaminfo->fAuthRequired; + } + if (csr_roaminfo->owe_pending_assoc_ind) { + if (!sap_fill_owe_ie_in_assoc_ind(assoc_ind, + csr_roaminfo->owe_pending_assoc_ind)) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + FL("Failed to fill OWE IE")); + qdf_mem_free(csr_roaminfo-> + owe_pending_assoc_ind); + csr_roaminfo->owe_pending_assoc_ind = NULL; + return QDF_STATUS_E_INVAL; + } + if (!sap_save_owe_pending_assoc_ind(sap_ctx, + csr_roaminfo->owe_pending_assoc_ind)) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + FL("Failed to save assoc ind")); + qdf_mem_free(csr_roaminfo-> + owe_pending_assoc_ind); + csr_roaminfo->owe_pending_assoc_ind = NULL; + return QDF_STATUS_E_INVAL; + } + csr_roaminfo->owe_pending_assoc_ind = NULL; + } + break; + case eSAP_START_BSS_EVENT: + sap_ap_event.sapHddEventCode = eSAP_START_BSS_EVENT; + bss_complete = &sap_ap_event.sapevt.sapStartBssCompleteEvent; + + bss_complete->sessionId = sap_ctx->sessionId; + if (bss_complete->sessionId == WLAN_UMAC_VDEV_ID_MAX) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sessionId")); + return QDF_STATUS_E_INVAL; + } + + bss_complete->status = (eSapStatus) context; + bss_complete->staId = sap_ctx->sap_sta_id; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("(eSAP_START_BSS_EVENT): staId = %d"), + bss_complete->staId); + + bss_complete->operating_chan_freq = sap_ctx->chan_freq; + bss_complete->ch_width = sap_ctx->ch_params.ch_width; + break; + case eSAP_DFS_CAC_START: + case eSAP_DFS_CAC_INTERRUPTED: + case eSAP_DFS_CAC_END: + case eSAP_DFS_PRE_CAC_END: + case eSAP_DFS_RADAR_DETECT: + case eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC: + case eSAP_DFS_NO_AVAILABLE_CHANNEL: + sap_ap_event.sapHddEventCode = sap_hddevent; + sap_ap_event.sapevt.sapStopBssCompleteEvent.status = + (eSapStatus) context; + break; + case eSAP_ACS_SCAN_SUCCESS_EVENT: + sap_handle_acs_scan_event(sap_ctx, &sap_ap_event, + (eSapStatus)context); + break; + case eSAP_ACS_CHANNEL_SELECTED: + sap_ap_event.sapHddEventCode = sap_hddevent; + acs_selected = &sap_ap_event.sapevt.sap_ch_selected; + if (eSAP_STATUS_SUCCESS == (eSapStatus)context) { + acs_selected->pri_ch_freq = + sap_ctx->acs_cfg->pri_ch_freq; + acs_selected->ht_sec_ch_freq = + sap_ctx->acs_cfg->ht_sec_ch_freq; + acs_selected->ch_width = sap_ctx->acs_cfg->ch_width; + acs_selected->vht_seg0_center_ch_freq = + sap_ctx->acs_cfg->vht_seg0_center_ch_freq; + acs_selected->vht_seg1_center_ch_freq = + sap_ctx->acs_cfg->vht_seg1_center_ch_freq; + } else if (eSAP_STATUS_FAILURE == (eSapStatus)context) { + acs_selected->pri_ch_freq = 0; + } + break; + + case eSAP_STOP_BSS_EVENT: + sap_ap_event.sapHddEventCode = eSAP_STOP_BSS_EVENT; + sap_ap_event.sapevt.sapStopBssCompleteEvent.status = + (eSapStatus) context; + break; + + case eSAP_STA_ASSOC_EVENT: + case eSAP_STA_REASSOC_EVENT: + + if (!csr_roaminfo) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Invalid CSR Roam Info")); + return QDF_STATUS_E_INVAL; + } + if (sap_ctx->fsm_state == SAP_STOPPING) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "SAP is stopping, not able to handle any incoming (re)assoc req"); + return QDF_STATUS_E_ABORTED; + } + + reassoc_complete = + &sap_ap_event.sapevt.sapStationAssocReassocCompleteEvent; + + if (csr_roaminfo->fReassocReq) + sap_ap_event.sapHddEventCode = eSAP_STA_REASSOC_EVENT; + else + sap_ap_event.sapHddEventCode = eSAP_STA_ASSOC_EVENT; + + qdf_copy_macaddr(&reassoc_complete->staMac, + &csr_roaminfo->peerMac); + reassoc_complete->staId = csr_roaminfo->staId; + reassoc_complete->status_code = csr_roaminfo->status_code; + + if (csr_roaminfo->assocReqLength < ASSOC_REQ_IE_OFFSET) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Invalid assoc request length:%d"), + csr_roaminfo->assocReqLength); + return QDF_STATUS_E_INVAL; + } + reassoc_complete->ies_len = (csr_roaminfo->assocReqLength - + ASSOC_REQ_IE_OFFSET); + reassoc_complete->ies = (csr_roaminfo->assocReqPtr + + ASSOC_REQ_IE_OFFSET); + + /* skip current AP address in reassoc frame */ + if (csr_roaminfo->fReassocReq) { + reassoc_complete->ies_len -= QDF_MAC_ADDR_SIZE; + reassoc_complete->ies += QDF_MAC_ADDR_SIZE; + } + + if (csr_roaminfo->addIELen) { + if (wlan_get_vendor_ie_ptr_from_oui( + SIR_MAC_P2P_OUI, SIR_MAC_P2P_OUI_SIZE, + csr_roaminfo->paddIE, csr_roaminfo->addIELen)) { + reassoc_complete->staType = eSTA_TYPE_P2P_CLI; + } else { + reassoc_complete->staType = eSTA_TYPE_INFRA; + } + } + + /* also fill up the channel info from the csr_roamInfo */ + chaninfo = &reassoc_complete->chan_info; + + chaninfo->mhz = csr_roaminfo->chan_info.mhz; + chaninfo->info = csr_roaminfo->chan_info.info; + chaninfo->band_center_freq1 = + csr_roaminfo->chan_info.band_center_freq1; + chaninfo->band_center_freq2 = + csr_roaminfo->chan_info.band_center_freq2; + chaninfo->reg_info_1 = + csr_roaminfo->chan_info.reg_info_1; + chaninfo->reg_info_2 = + csr_roaminfo->chan_info.reg_info_2; + chaninfo->nss = csr_roaminfo->chan_info.nss; + chaninfo->rate_flags = csr_roaminfo->chan_info.rate_flags; + + reassoc_complete->wmmEnabled = csr_roaminfo->wmmEnabledSta; + reassoc_complete->status = (eSapStatus) context; + reassoc_complete->timingMeasCap = csr_roaminfo->timingMeasCap; + reassoc_complete->ampdu = csr_roaminfo->ampdu; + reassoc_complete->sgi_enable = csr_roaminfo->sgi_enable; + reassoc_complete->tx_stbc = csr_roaminfo->tx_stbc; + reassoc_complete->rx_stbc = csr_roaminfo->rx_stbc; + reassoc_complete->ch_width = csr_roaminfo->ch_width; + reassoc_complete->mode = csr_roaminfo->mode; + reassoc_complete->max_supp_idx = csr_roaminfo->max_supp_idx; + reassoc_complete->max_ext_idx = csr_roaminfo->max_ext_idx; + reassoc_complete->max_mcs_idx = csr_roaminfo->max_mcs_idx; + reassoc_complete->rx_mcs_map = csr_roaminfo->rx_mcs_map; + reassoc_complete->tx_mcs_map = csr_roaminfo->tx_mcs_map; + reassoc_complete->ecsa_capable = csr_roaminfo->ecsa_capable; + if (csr_roaminfo->ht_caps.present) + reassoc_complete->ht_caps = csr_roaminfo->ht_caps; + if (csr_roaminfo->vht_caps.present) + reassoc_complete->vht_caps = csr_roaminfo->vht_caps; + reassoc_complete->he_caps_present = + csr_roaminfo->he_caps_present; + reassoc_complete->capability_info = + csr_roaminfo->capability_info; + + break; + + case eSAP_STA_DISASSOC_EVENT: + if (!csr_roaminfo) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Invalid CSR Roam Info")); + return QDF_STATUS_E_INVAL; + } + sap_ap_event.sapHddEventCode = eSAP_STA_DISASSOC_EVENT; + disassoc_comp = + &sap_ap_event.sapevt.sapStationDisassocCompleteEvent; + + qdf_copy_macaddr(&disassoc_comp->staMac, + &csr_roaminfo->peerMac); + disassoc_comp->staId = csr_roaminfo->staId; + if (csr_roaminfo->reasonCode == eCSR_ROAM_RESULT_FORCED) + disassoc_comp->reason = eSAP_USR_INITATED_DISASSOC; + else + disassoc_comp->reason = eSAP_MAC_INITATED_DISASSOC; + + disassoc_comp->status_code = csr_roaminfo->status_code; + disassoc_comp->status = (eSapStatus) context; + disassoc_comp->rssi = csr_roaminfo->rssi; + disassoc_comp->rx_rate = csr_roaminfo->rx_rate; + disassoc_comp->tx_rate = csr_roaminfo->tx_rate; + disassoc_comp->rx_mc_bc_cnt = csr_roaminfo->rx_mc_bc_cnt; + disassoc_comp->rx_retry_cnt = csr_roaminfo->rx_retry_cnt; + disassoc_comp->reason_code = csr_roaminfo->disassoc_reason; + break; + + case eSAP_STA_SET_KEY_EVENT: + + if (!csr_roaminfo) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Invalid CSR Roam Info")); + return QDF_STATUS_E_INVAL; + } + sap_ap_event.sapHddEventCode = eSAP_STA_SET_KEY_EVENT; + key_complete = + &sap_ap_event.sapevt.sapStationSetKeyCompleteEvent; + key_complete->status = (eSapStatus) context; + qdf_copy_macaddr(&key_complete->peerMacAddr, + &csr_roaminfo->peerMac); + break; + + case eSAP_STA_MIC_FAILURE_EVENT: + + if (!csr_roaminfo) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Invalid CSR Roam Info")); + return QDF_STATUS_E_INVAL; + } + sap_ap_event.sapHddEventCode = eSAP_STA_MIC_FAILURE_EVENT; + mic_failure = &sap_ap_event.sapevt.sapStationMICFailureEvent; + + qdf_mem_copy(&mic_failure->srcMacAddr, + csr_roaminfo->u.pMICFailureInfo->srcMacAddr, + sizeof(tSirMacAddr)); + qdf_mem_copy(&mic_failure->staMac.bytes, + csr_roaminfo->u.pMICFailureInfo->taMacAddr, + sizeof(tSirMacAddr)); + qdf_mem_copy(&mic_failure->dstMacAddr.bytes, + csr_roaminfo->u.pMICFailureInfo->dstMacAddr, + sizeof(tSirMacAddr)); + mic_failure->multicast = + csr_roaminfo->u.pMICFailureInfo->multicast; + mic_failure->IV1 = csr_roaminfo->u.pMICFailureInfo->IV1; + mic_failure->keyId = csr_roaminfo->u.pMICFailureInfo->keyId; + qdf_mem_copy(mic_failure->TSC, + csr_roaminfo->u.pMICFailureInfo->TSC, + SIR_CIPHER_SEQ_CTR_SIZE); + break; + + case eSAP_WPS_PBC_PROBE_REQ_EVENT: + + if (!csr_roaminfo) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Invalid CSR Roam Info")); + return QDF_STATUS_E_INVAL; + } + sap_ap_event.sapHddEventCode = eSAP_WPS_PBC_PROBE_REQ_EVENT; + + qdf_mem_copy(&sap_ap_event.sapevt.sapPBCProbeReqEvent. + WPSPBCProbeReq, csr_roaminfo->u.pWPSPBCProbeReq, + sizeof(tSirWPSPBCProbeReq)); + break; + + case eSAP_DISCONNECT_ALL_P2P_CLIENT: + sap_ap_event.sapHddEventCode = eSAP_DISCONNECT_ALL_P2P_CLIENT; + sap_ap_event.sapevt.sapActionCnf.actionSendSuccess = + (eSapStatus) context; + break; + + case eSAP_MAC_TRIG_STOP_BSS_EVENT: + sap_ap_event.sapHddEventCode = eSAP_MAC_TRIG_STOP_BSS_EVENT; + sap_ap_event.sapevt.sapActionCnf.actionSendSuccess = + (eSapStatus) context; + break; + + case eSAP_UNKNOWN_STA_JOIN: + sap_ap_event.sapHddEventCode = eSAP_UNKNOWN_STA_JOIN; + qdf_mem_copy((void *) sap_ap_event.sapevt.sapUnknownSTAJoin. + macaddr.bytes, (void *) context, + QDF_MAC_ADDR_SIZE); + break; + + case eSAP_MAX_ASSOC_EXCEEDED: + + if (!csr_roaminfo) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Invalid CSR Roam Info")); + return QDF_STATUS_E_INVAL; + } + sap_ap_event.sapHddEventCode = eSAP_MAX_ASSOC_EXCEEDED; + qdf_copy_macaddr(&sap_ap_event.sapevt. + sapMaxAssocExceeded.macaddr, + &csr_roaminfo->peerMac); + break; + + case eSAP_CHANNEL_CHANGE_EVENT: + /* + * Reconfig ACS result info. For DFS AP-AP Mode Sec AP ACS + * follows pri AP + */ + sap_ctx->acs_cfg->pri_ch_freq = sap_ctx->chan_freq; + sap_ctx->acs_cfg->ch_width = + sap_ctx->csr_roamProfile.ch_params.ch_width; + sap_config_acs_result(MAC_HANDLE(mac_ctx), sap_ctx, + sap_ctx->sec_ch_freq); + + sap_ap_event.sapHddEventCode = eSAP_CHANNEL_CHANGE_EVENT; + + acs_selected = &sap_ap_event.sapevt.sap_ch_selected; + acs_selected->pri_ch_freq = sap_ctx->chan_freq; + acs_selected->ht_sec_ch_freq = sap_ctx->sec_ch_freq; + acs_selected->ch_width = + sap_ctx->acs_cfg->ch_width; + acs_selected->vht_seg0_center_ch_freq = + sap_ctx->acs_cfg->vht_seg0_center_ch_freq; + acs_selected->vht_seg1_center_ch_freq = + sap_ctx->acs_cfg->vht_seg1_center_ch_freq; + break; + + case eSAP_ECSA_CHANGE_CHAN_IND: + + if (!csr_roaminfo) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("Invalid CSR Roam Info")); + return QDF_STATUS_E_INVAL; + } + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, SAP event callback event = %s", + __func__, "eSAP_ECSA_CHANGE_CHAN_IND"); + sap_ap_event.sapHddEventCode = eSAP_ECSA_CHANGE_CHAN_IND; + sap_ap_event.sapevt.sap_chan_cng_ind.new_chan_freq = + csr_roaminfo->target_chan_freq; + break; + case eSAP_DFS_NEXT_CHANNEL_REQ: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, SAP event callback event = %s", + __func__, "eSAP_DFS_NEXT_CHANNEL_REQ"); + sap_ap_event.sapHddEventCode = eSAP_DFS_NEXT_CHANNEL_REQ; + break; + case eSAP_STOP_BSS_DUE_TO_NO_CHNL: + sap_ap_event.sapHddEventCode = eSAP_STOP_BSS_DUE_TO_NO_CHNL; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("stopping session_id:%d, bssid:"QDF_MAC_ADDR_FMT", chan_freq:%d"), + sap_ctx->sessionId, + QDF_MAC_ADDR_REF(sap_ctx->self_mac_addr), + sap_ctx->chan_freq); + break; + + case eSAP_CHANNEL_CHANGE_RESP: + sap_ap_event.sapHddEventCode = eSAP_CHANNEL_CHANGE_RESP; + acs_selected = &sap_ap_event.sapevt.sap_ch_selected; + acs_selected->pri_ch_freq = sap_ctx->chan_freq; + acs_selected->ht_sec_ch_freq = sap_ctx->sec_ch_freq; + acs_selected->ch_width = + sap_ctx->csr_roamProfile.ch_params.ch_width; + acs_selected->vht_seg0_center_ch_freq = + sap_ctx->csr_roamProfile.ch_params.mhz_freq_seg0; + acs_selected->vht_seg1_center_ch_freq = + sap_ctx->csr_roamProfile.ch_params.mhz_freq_seg1; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, SAP event callback event = %s", + __func__, "eSAP_CHANNEL_CHANGE_RESP"); + break; + + default: + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("SAP Unknown callback event = %d"), + sap_hddevent); + break; + } + qdf_status = (*sap_ctx->sap_event_cb) + (&sap_ap_event, sap_ctx->user_context); + + return qdf_status; + +} + +bool sap_is_dfs_cac_wait_state(struct sap_context *sap_ctx) +{ + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + struct mac_context *mac_ctx; + mac_handle_t mac_handle; + + if (!sap_ctx) { + sap_err("Invalid sap context"); + return false; + } + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (!mac_handle) { + sap_err("invalid mac_handle"); + return false; + } + + mac_ctx = MAC_CONTEXT(mac_handle); + if (!mac_ctx) { + sap_err("Invalid MAC context"); + return false; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + sap_ctx->sessionId, + WLAN_DFS_ID); + if (!vdev) { + sap_err("vdev is NULL for vdev_id: %u", sap_ctx->sessionId); + return false; + } + + status = wlan_vdev_is_dfs_cac_wait(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_DFS_ID); + + return QDF_IS_STATUS_SUCCESS(status); +} + +/** + * sap_find_cac_wait_session() - Get context of a SAP session in CAC wait state + * @handle: Global MAC handle + * + * Finds and gets the context of a SAP session in CAC wait state. + * + * Return: Valid SAP context on success, else NULL + */ +static struct sap_context *sap_find_cac_wait_session(mac_handle_t handle) +{ + struct mac_context *mac = MAC_CONTEXT(handle); + uint8_t i = 0; + struct sap_context *sap_ctx; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + "%s", __func__); + + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + sap_ctx = mac->sap.sapCtxList[i].sap_context; + if (((QDF_SAP_MODE == mac->sap.sapCtxList[i].sapPersona) + || + (QDF_P2P_GO_MODE == mac->sap.sapCtxList[i].sapPersona)) && + (sap_is_dfs_cac_wait_state(sap_ctx))) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + "%s: found SAP in cac wait state", __func__); + return sap_ctx; + } + if (sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + "sapdfs: mode:%d intf:%d state:%d", + mac->sap.sapCtxList[i].sapPersona, i, + sap_ctx->fsm_state); + } + } + + return NULL; +} + +void sap_cac_reset_notify(mac_handle_t mac_handle) +{ + uint8_t intf = 0; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + struct sap_context *sap_context = + mac->sap.sapCtxList[intf].sap_context; + if (((QDF_SAP_MODE == mac->sap.sapCtxList[intf].sapPersona) + || + (QDF_P2P_GO_MODE == mac->sap.sapCtxList[intf].sapPersona)) + && mac->sap.sapCtxList[intf].sap_context) { + sap_context->isCacStartNotified = false; + sap_context->isCacEndNotified = false; + } + } +} + +/** + * sap_cac_start_notify() - Notify CAC start to HDD + * @mac_handle: Opaque handle to the global MAC context + * + * Function will be called to notify eSAP_DFS_CAC_START event to HDD + * + * Return: QDF_STATUS_SUCCESS if the notification was sent, otherwise + * an appropriate QDF_STATUS error + */ +static QDF_STATUS sap_cac_start_notify(mac_handle_t mac_handle) +{ + uint8_t intf = 0; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + qdf_freq_t ch_freq; + + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + struct sap_context *sap_context = + mac->sap.sapCtxList[intf].sap_context; + struct csr_roam_profile *profile; + + if (((QDF_SAP_MODE == mac->sap.sapCtxList[intf].sapPersona) + || + (QDF_P2P_GO_MODE == mac->sap.sapCtxList[intf].sapPersona)) + && mac->sap.sapCtxList[intf].sap_context && + (false == sap_context->isCacStartNotified)) { + /* Don't start CAC for non-dfs channel, its violation */ + profile = &sap_context->csr_roamProfile; + ch_freq = profile->op_freq; + if (!wlan_reg_is_dfs_for_freq(mac->pdev, + ch_freq)) + continue; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + "sapdfs: Signaling eSAP_DFS_CAC_START to HDD for sapctx[%pK]", + sap_context); + + qdf_status = sap_signal_hdd_event(sap_context, NULL, + eSAP_DFS_CAC_START, + (void *) + eSAP_STATUS_SUCCESS); + if (QDF_STATUS_SUCCESS != qdf_status) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + "In %s, failed setting isCacStartNotified on interface[%d]", + __func__, intf); + return qdf_status; + } + sap_context->isCacStartNotified = true; + } + } + return qdf_status; +} + +/** + * wlansap_update_pre_cac_end() - Update pre cac end to upper layer + * @sap_context: SAP context + * @mac: Global MAC structure + * @intf: Interface number + * + * Notifies pre cac end to upper layer + * + * Return: QDF_STATUS + */ +static QDF_STATUS wlansap_update_pre_cac_end(struct sap_context *sap_context, + struct mac_context *mac, uint8_t intf) +{ + QDF_STATUS qdf_status; + + sap_context->isCacEndNotified = true; + mac->sap.SapDfsInfo.sap_radar_found_status = false; + sap_context->fsm_state = SAP_STARTED; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "In %s, pre cac end notify on %d: move to state SAP_STARTED", + __func__, intf); + qdf_status = sap_signal_hdd_event(sap_context, + NULL, eSAP_DFS_PRE_CAC_END, + (void *)eSAP_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + "In %s, pre cac notify failed on intf %d", + __func__, intf); + return qdf_status; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * sap_cac_end_notify() - Notify CAC end to HDD + * @mac_handle: Opaque handle to the global MAC context + * + * Function will be called to notify eSAP_DFS_CAC_END event to HDD + * + * Return: QDF_STATUS_SUCCESS if the notification was sent, otherwise + * an appropriate QDF_STATUS error + */ +static QDF_STATUS sap_cac_end_notify(mac_handle_t mac_handle, + struct csr_roam_info *roamInfo) +{ + uint8_t intf; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + uint32_t freq; + + /* + * eSAP_DFS_CHANNEL_CAC_END: + * CAC Period elapsed and there was no radar + * found so, SAP can continue beaconing. + * sap_radar_found_status is set to 0 + */ + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + struct sap_context *sap_context = + mac->sap.sapCtxList[intf].sap_context; + struct csr_roam_profile *profile; + + if (((QDF_SAP_MODE == mac->sap.sapCtxList[intf].sapPersona) + || + (QDF_P2P_GO_MODE == mac->sap.sapCtxList[intf].sapPersona)) + && mac->sap.sapCtxList[intf].sap_context && + (false == sap_context->isCacEndNotified) && + sap_is_dfs_cac_wait_state(sap_context)) { + sap_context = mac->sap.sapCtxList[intf].sap_context; + /* Don't check CAC for non-dfs channel */ + profile = &sap_context->csr_roamProfile; + freq = profile->op_freq; + if (CHANNEL_STATE_DFS != + wlan_reg_get_5g_bonded_channel_state_for_freq(mac->pdev, + freq, + profile->ch_params.ch_width)) + continue; + + /* If this is an end notification of a pre cac, the + * SAP must not start beaconing and must delete the + * temporary interface created for pre cac and switch + * the original SAP to the pre CAC channel. + */ + if (sap_context->is_pre_cac_on) { + qdf_status = wlansap_update_pre_cac_end( + sap_context, mac, intf); + if (QDF_IS_STATUS_ERROR(qdf_status)) + return qdf_status; + /* pre CAC is not allowed with any concurrency. + * So, we can break from here. + */ + break; + } + + qdf_status = sap_signal_hdd_event(sap_context, NULL, + eSAP_DFS_CAC_END, + (void *) + eSAP_STATUS_SUCCESS); + if (QDF_STATUS_SUCCESS != qdf_status) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + "In %s, failed setting isCacEndNotified on interface[%d]", + __func__, intf); + return qdf_status; + } + sap_context->isCacEndNotified = true; + mac->sap.SapDfsInfo.sap_radar_found_status = false; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + "sapdfs: Start beacon request on sapctx[%pK]", + sap_context); + + /* Start beaconing on the new channel */ + wlansap_start_beacon_req(sap_context); + + /* Transition from SAP_STARTING to SAP_STARTED + * (both without substates) + */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + "sapdfs: chan_freq[%d] from state %s => %s", + sap_context->chan_freq, "SAP_STARTING", + "SAP_STARTED"); + + sap_context->fsm_state = SAP_STARTED; + + /*Action code for transition */ + qdf_status = sap_signal_hdd_event(sap_context, roamInfo, + eSAP_START_BSS_EVENT, + (void *) + eSAP_STATUS_SUCCESS); + if (QDF_STATUS_SUCCESS != qdf_status) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + "In %s, failed setting isCacEndNotified on interface[%d]", + __func__, intf); + return qdf_status; + } + } + } + /* + * All APs are done with CAC timer, all APs should start beaconing. + * Lets assume AP1 and AP2 started beaconing on DFS channel, Now lets + * say AP1 goes down and comes back on same DFS channel. In this case + * AP1 shouldn't start CAC timer and start beacon immediately beacause + * AP2 is already beaconing on this channel. This case will be handled + * by checking against eSAP_DFS_SKIP_CAC while starting the timer. + */ + mac->sap.SapDfsInfo.cac_state = eSAP_DFS_SKIP_CAC; + return qdf_status; +} + +/** + * sap_validate_dfs_nol() - Validate SAP channel with NOL list + * @sap_ctx: SAP context + * @sap_ctx: MAC context + * + * Function will be called to validate SAP channel and bonded sub channels + * included in DFS NOL or not. + * + * Return: QDF_STATUS_SUCCESS for NOT in NOL + */ +static QDF_STATUS sap_validate_dfs_nol(struct sap_context *sap_ctx, + struct mac_context *mac_ctx) +{ + bool b_leak_chan = false; + uint8_t temp_chan; + uint8_t sap_chan; + + sap_chan = wlan_reg_freq_to_chan(mac_ctx->pdev, sap_ctx->chan_freq); + temp_chan = sap_chan; + utils_dfs_mark_leaking_ch(mac_ctx->pdev, + sap_ctx->ch_params.ch_width, + 1, &temp_chan); + + /* + * if selelcted channel has leakage to channels + * in NOL, the temp_chan will be reset + */ + b_leak_chan = (temp_chan != sap_chan); + /* + * check if channel is in DFS_NOL or if the channel + * has leakage to the channels in NOL + */ + if (sap_dfs_is_channel_in_nol_list(sap_ctx, sap_chan, + PHY_CHANNEL_BONDING_STATE_MAX) || + b_leak_chan) { + uint8_t ch; + + /* find a new available channel */ + ch = sap_random_channel_sel(sap_ctx); + if (!ch) { + /* No available channel found */ + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + FL("No available channel found!!!")); + sap_signal_hdd_event(sap_ctx, NULL, + eSAP_DFS_NO_AVAILABLE_CHANNEL, + (void *)eSAP_STATUS_SUCCESS); + return QDF_STATUS_E_FAULT; + } + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("ch_freq %d is in NOL, Start Bss on new chan %d"), + sap_ctx->chan_freq, ch); + + sap_ctx->chan_freq = wlan_reg_chan_to_freq(mac_ctx->pdev, ch); + wlan_reg_set_channel_params_for_freq(mac_ctx->pdev, + sap_ctx->chan_freq, + sap_ctx->sec_ch_freq, + &sap_ctx->ch_params); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * sap_goto_starting() - Trigger softap start + * @sap_ctx: SAP context + * @sap_event: SAP event buffer + * @mac_ctx: global MAC context + * @mac_handle: Opaque handle to the global MAC context + * + * This function triggers start of softap. Before starting, it can select + * new channel if given channel has leakage or if given channel in DFS_NOL. + * + * Return: QDF_STATUS + */ +static QDF_STATUS sap_goto_starting(struct sap_context *sap_ctx, + struct sap_sm_event *sap_event, + struct mac_context *mac_ctx, + mac_handle_t mac_handle) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + + /* + * check if channel is in DFS_NOL or if the channel + * has leakage to the channels in NOL. + */ + if (!WLAN_REG_IS_6GHZ_CHAN_FREQ(sap_ctx->chan_freq)) { + qdf_status = sap_validate_dfs_nol(sap_ctx, mac_ctx); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + return qdf_status; + } + + /* + * when AP2 is started while AP1 is performing ACS, we may not + * have the AP1 channel yet.So here after the completion of AP2 + * ACS check if AP1 ACS resulting channel is DFS and if yes + * override AP2 ACS scan result with AP1 DFS channel + */ + if (policy_mgr_concurrent_beaconing_sessions_running(mac_ctx->psoc)) { + uint32_t con_ch_freq; + uint16_t con_ch; + + con_ch_freq = sme_get_beaconing_concurrent_operation_channel( + mac_handle, sap_ctx->sessionId); + con_ch = wlan_reg_freq_to_chan(mac_ctx->pdev, con_ch_freq); + /* Overwrite second AP's channel with first only when: + * 1. If operating mode is single mac + * 2. or if 2nd AP is coming up on 5G band channel + */ + if ((!policy_mgr_is_hw_dbs_capable(mac_ctx->psoc) || + WLAN_REG_IS_5GHZ_CH_FREQ(sap_ctx->chan_freq)) && + con_ch && + wlan_reg_is_dfs_for_freq(mac_ctx->pdev, + con_ch_freq)) { + sap_ctx->chan_freq = con_ch_freq; + wlan_reg_set_channel_params_for_freq( + mac_ctx->pdev, + con_ch_freq, 0, + &sap_ctx->ch_params); + } + } + if (WLAN_REG_IS_5GHZ_CH_FREQ(sap_ctx->chan_freq) && + (sap_ctx->csr_roamProfile.phyMode == eCSR_DOT11_MODE_11g || + sap_ctx->csr_roamProfile.phyMode == + eCSR_DOT11_MODE_11g_ONLY)) + sap_ctx->csr_roamProfile.phyMode = eCSR_DOT11_MODE_11a; + else if (WLAN_REG_IS_24GHZ_CH_FREQ(sap_ctx->chan_freq) && + (sap_ctx->csr_roamProfile.phyMode == eCSR_DOT11_MODE_11a)) + sap_ctx->csr_roamProfile.phyMode = eCSR_DOT11_MODE_11g; + + /* + * Transition from SAP_INIT to SAP_STARTING + * (both without substates) + */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("from state %s => %s"), + "SAP_INIT", "SAP_STARTING"); + /* Channel selected. Now can sap_goto_starting */ + sap_ctx->fsm_state = SAP_STARTING; + /* Specify the channel */ + sap_ctx->csr_roamProfile.ChannelInfo.numOfChannels = + 1; + sap_ctx->csr_roamProfile.ChannelInfo.freq_list = + &sap_ctx->csr_roamProfile.op_freq; + sap_ctx->csr_roamProfile.op_freq = sap_ctx->chan_freq; + + sap_ctx->csr_roamProfile.ch_params = sap_ctx->ch_params; + sap_get_cac_dur_dfs_region(sap_ctx, + &sap_ctx->csr_roamProfile.cac_duration_ms, + &sap_ctx->csr_roamProfile.dfs_regdomain); + sap_ctx->csr_roamProfile.beacon_tx_rate = + sap_ctx->beacon_tx_rate; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("notify hostapd about chan freq selection: %d"), + sap_ctx->chan_freq); + sap_signal_hdd_event(sap_ctx, NULL, + eSAP_CHANNEL_CHANGE_EVENT, + (void *)eSAP_STATUS_SUCCESS); + sap_dfs_set_current_channel(sap_ctx); + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, "%s: session: %d", + __func__, sap_ctx->sessionId); + + qdf_status = sme_roam_connect(mac_handle, sap_ctx->sessionId, + &sap_ctx->csr_roamProfile, + &sap_ctx->csr_roamId); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "%s: Failed to issue sme_roam_connect", __func__); + + return qdf_status; +} + +/** + * sap_fsm_cac_start() - start cac wait timer + * @sap_ctx: SAP context + * @mac_ctx: global MAC context + * @mac_handle: Opaque handle to the global MAC context + * + * Return: QDF_STATUS + */ +static QDF_STATUS sap_fsm_cac_start(struct sap_context *sap_ctx, + struct mac_context *mac_ctx, + mac_handle_t mac_handle) +{ + sap_ctx->fsm_state = SAP_STARTING; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + FL("Move sap state to SAP_STARTING")); + if (!mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + FL("sapdfs: starting dfs cac timer on sapctx[%pK]"), + sap_ctx); + sap_start_dfs_cac_timer(sap_ctx); + } + + return sap_cac_start_notify(mac_handle); +} + +/** + * sap_fsm_state_init() - utility function called from sap fsm + * @sap_ctx: SAP context + * @sap_event: SAP event buffer + * @mac_ctx: global MAC context + * @mac_handle: Opaque handle to the global MAC context + * + * This function is called for state transition from "SAP_INIT" + * + * Return: QDF_STATUS + */ +static QDF_STATUS sap_fsm_state_init(struct sap_context *sap_ctx, + struct sap_sm_event *sap_event, + struct mac_context *mac_ctx, + mac_handle_t mac_handle) +{ + uint32_t msg = sap_event->event; + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + + if (msg == eSAP_HDD_START_INFRA_BSS) { + /* init dfs channel nol */ + sap_init_dfs_channel_nol_list(sap_ctx); + + /* + * Perform sme_ScanRequest. This scan request is post start bss + * request so, set the third to false. + */ + qdf_status = sap_validate_chan(sap_ctx, false, true); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + FL("channel is not valid!")); + goto exit; + } + + qdf_status = sap_goto_starting(sap_ctx, sap_event, + mac_ctx, mac_handle); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sap_err("sap_goto_starting failed"); + } else if (msg == eSAP_DFS_CHANNEL_CAC_START) { + qdf_status = sap_fsm_cac_start(sap_ctx, mac_ctx, mac_handle); + } else { + sap_err("in state %s, event msg %d", "SAP_INIT", msg); + } + +exit: + return qdf_status; +} + +/** + * sap_fsm_handle_radar_during_cac() - uhandle radar event during cac + * @sap_ctx: SAP context + * @mac_ctx: global MAC context + * + * Return: QDF_STATUS + */ +static QDF_STATUS sap_fsm_handle_radar_during_cac(struct sap_context *sap_ctx, + struct mac_context *mac_ctx) +{ + uint8_t intf; + + if (mac_ctx->sap.SapDfsInfo.target_chan_freq) { + wlan_reg_set_channel_params_for_freq(mac_ctx->pdev, + mac_ctx->sap.SapDfsInfo.target_chan_freq, 0, + &sap_ctx->ch_params); + } else { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Invalid target channel freq %d"), + mac_ctx->sap.SapDfsInfo.target_chan_freq); + return QDF_STATUS_E_FAILURE; + } + + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + struct sap_context *t_sap_ctx; + struct csr_roam_profile *profile; + + t_sap_ctx = mac_ctx->sap.sapCtxList[intf].sap_context; + if (((QDF_SAP_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona) || + (QDF_P2P_GO_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona)) && + t_sap_ctx && t_sap_ctx->fsm_state != SAP_INIT) { + profile = &t_sap_ctx->csr_roamProfile; + if (!wlan_reg_is_passive_or_disable_ch( + mac_ctx->pdev, + wlan_reg_freq_to_chan(mac_ctx->pdev, + profile->op_freq))) + continue; + t_sap_ctx->is_chan_change_inprogress = true; + /* + * eSAP_DFS_CHANNEL_CAC_RADAR_FOUND: + * A Radar is found on current DFS Channel + * while in CAC WAIT period So, do a channel + * switch to randomly selected target channel. + * Send the Channel change message to SME/PE. + * sap_radar_found_status is set to 1 + */ + wlansap_channel_change_request(t_sap_ctx, + mac_ctx->sap.SapDfsInfo.target_chan_freq); + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * sap_fsm_handle_start_failure() - handle sap start failure + * @sap_ctx: SAP context + * @msg: event msg + * @mac_handle: Opaque handle to the global MAC context + * + * Return: QDF_STATUS + */ +static QDF_STATUS sap_fsm_handle_start_failure(struct sap_context *sap_ctx, + uint32_t msg, + mac_handle_t mac_handle) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + + if (msg == eSAP_HDD_STOP_INFRA_BSS) { + /* Transition from SAP_STARTING to SAP_STOPPING */ + sap_debug("SAP start is in progress, state from state %s => %s", + "SAP_STARTING", "SAP_STOPPING"); + /* + * Stop the CAC timer only in following conditions + * single AP: if there is a single AP then stop timer + * mulitple APs: incase of multiple APs, make sure that + * all APs are down. + */ + if (!sap_find_valid_concurrent_session(mac_handle)) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_MED, + FL("sapdfs: no sessions are valid, stopping timer")); + sap_stop_dfs_cac_timer(sap_ctx); + } + + sap_ctx->fsm_state = SAP_STOPPING; + qdf_status = sap_goto_stopping(sap_ctx); + } else { + /* + * Transition from SAP_STARTING to SAP_INIT + * (both without substates) + */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("from state %s => %s"), + "SAP_STARTING", "SAP_INIT"); + + /* Advance outer statevar */ + sap_ctx->fsm_state = SAP_INIT; + qdf_status = sap_signal_hdd_event(sap_ctx, NULL, + eSAP_START_BSS_EVENT, + (void *) + eSAP_STATUS_FAILURE); + qdf_status = sap_goto_init(sap_ctx); + } + + return qdf_status; +} + +/** + * sap_propagate_cac_events() - Indicate CAC START/END event + * @sap_ctx: SAP context + * + * This function is to indicate CAC START/END event if CAC process + * is skipped. + * + * Return: void + */ +static void sap_propagate_cac_events(struct sap_context *sap_ctx) +{ + QDF_STATUS qdf_status; + + qdf_status = sap_signal_hdd_event(sap_ctx, NULL, + eSAP_DFS_CAC_START, + (void *) + eSAP_STATUS_SUCCESS); + if (qdf_status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_DEBUG, + "failed to indicate CAC START vdev %d", + sap_ctx->sessionId); + return; + } + + qdf_status = sap_signal_hdd_event(sap_ctx, NULL, + eSAP_DFS_CAC_END, + (void *) + eSAP_STATUS_SUCCESS); + if (qdf_status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_DEBUG, + "failed to indicate CAC End vdev %d", + sap_ctx->sessionId); + } +} + +/** + * sap_fsm_state_starting() - utility function called from sap fsm + * @sap_ctx: SAP context + * @sap_event: SAP event buffer + * @mac_ctx: global MAC context + * @mac_handle: Opaque handle to the global MAC context + * + * This function is called for state transition from "SAP_STARTING" + * + * Return: QDF_STATUS + */ +static QDF_STATUS sap_fsm_state_starting(struct sap_context *sap_ctx, + struct sap_sm_event *sap_event, + struct mac_context *mac_ctx, + mac_handle_t mac_handle) +{ + uint32_t msg = sap_event->event; + struct csr_roam_info *roam_info = + (struct csr_roam_info *) (sap_event->params); + tSapDfsInfo *sap_dfs_info; + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + uint8_t is_dfs = false; + uint32_t sap_chan_freq; + uint32_t ch_cfreq1 = 0; + enum reg_wifi_band band; + + if (msg == eSAP_MAC_START_BSS_SUCCESS) { + /* + * Transition from SAP_STARTING to SAP_STARTED + * (both without substates) + */ + sap_debug("Chan %d %s => %s ch_width %d", + sap_ctx->chan_freq, "SAP_STARTING", "SAP_STARTED", + sap_ctx->ch_params.ch_width); + sap_ctx->fsm_state = SAP_STARTED; + + if (sap_ctx->is_chan_change_inprogress) { + /* SAP channel change request processing is completed */ + qdf_status = sap_signal_hdd_event(sap_ctx, roam_info, + eSAP_CHANNEL_CHANGE_EVENT, + (void *)eSAP_STATUS_SUCCESS); + sap_ctx->is_chan_change_inprogress = false; + } else { + /* Action code for transition */ + qdf_status = sap_signal_hdd_event(sap_ctx, roam_info, + eSAP_START_BSS_EVENT, + (void *) eSAP_STATUS_SUCCESS); + } + sap_chan_freq = sap_ctx->chan_freq; + band = wlan_reg_freq_to_band(sap_ctx->chan_freq); + if (sap_ctx->ch_params.center_freq_seg1) + ch_cfreq1 = wlan_reg_chan_band_to_freq( + mac_ctx->pdev, + sap_ctx->ch_params.center_freq_seg1, + BIT(band)); + + /* + * The upper layers have been informed that AP is up and + * running, however, the AP is still not beaconing, until + * CAC is done if the operating channel is DFS + */ + if (sap_ctx->ch_params.ch_width == CH_WIDTH_160MHZ) { + is_dfs = wlan_reg_get_5g_bonded_channel_state_for_freq( + mac_ctx->pdev, sap_chan_freq, + CH_WIDTH_160MHZ) == CHANNEL_STATE_DFS; + } else if (sap_ctx->ch_params.ch_width == CH_WIDTH_80P80MHZ) { + if (wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + sap_chan_freq) == + CHANNEL_STATE_DFS || + wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + ch_cfreq1) == + CHANNEL_STATE_DFS) + is_dfs = true; + } else { + if (wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + sap_chan_freq) == + CHANNEL_STATE_DFS) + is_dfs = true; + } + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(sap_ctx->chan_freq)) + is_dfs = false; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("is_dfs %d"), is_dfs); + if (is_dfs) { + sap_dfs_info = &mac_ctx->sap.SapDfsInfo; + if ((false == sap_dfs_info->ignore_cac) && + (eSAP_DFS_DO_NOT_SKIP_CAC == + sap_dfs_info->cac_state) && + !sap_ctx->pre_cac_complete && + policy_mgr_get_dfs_master_dynamic_enabled( + mac_ctx->psoc, + sap_ctx->sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_HIGH, + FL("start cac timer")); + qdf_status = sap_fsm_cac_start(sap_ctx, mac_ctx, + mac_handle); + } else { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_HIGH, + FL("skip cac timer")); + /* + * If hostapd starts AP on dfs channel, + * hostapd will wait for CAC START/CAC END + * event and finish AP start process. + * If we skip CAC timer, we will need to + * indicate the CAC event even though driver + * doesn't perform CAC. + */ + sap_propagate_cac_events(sap_ctx); + + wlansap_start_beacon_req(sap_ctx); + } + } + } else if (msg == eSAP_MAC_START_FAILS || + msg == eSAP_HDD_STOP_INFRA_BSS) { + qdf_status = sap_fsm_handle_start_failure(sap_ctx, msg, + mac_handle); + } else if (msg == eSAP_OPERATING_CHANNEL_CHANGED) { + /* The operating channel has changed, update hostapd */ + sap_ctx->chan_freq = mac_ctx->sap.SapDfsInfo.target_chan_freq; + + sap_ctx->fsm_state = SAP_STARTED; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("from state %s => %s"), + "SAP_STARTING", "SAP_STARTED"); + + /* Indicate change in the state to upper layers */ + qdf_status = sap_signal_hdd_event(sap_ctx, roam_info, + eSAP_START_BSS_EVENT, + (void *)eSAP_STATUS_SUCCESS); + } else if (msg == eSAP_DFS_CHANNEL_CAC_RADAR_FOUND) { + qdf_status = sap_fsm_handle_radar_during_cac(sap_ctx, mac_ctx); + } else if (msg == eSAP_DFS_CHANNEL_CAC_END) { + qdf_status = sap_cac_end_notify(mac_handle, roam_info); + } else { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("in state %s, invalid event msg %d"), + "SAP_STARTING", msg); + } + + return qdf_status; +} + +/** + * sap_fsm_send_csa_restart_req() - send csa start event + * @mac_ctx: mac ctx + * @sap_ctx: SAP context + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +sap_fsm_send_csa_restart_req(struct mac_context *mac_ctx, + struct sap_context *sap_ctx) +{ + QDF_STATUS status; + + status = policy_mgr_check_and_set_hw_mode_for_channel_switch( + mac_ctx->psoc, sap_ctx->sessionId, + mac_ctx->sap.SapDfsInfo.target_chan_freq, + POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_SAP); + + /* + * If hw_mode_status is QDF_STATUS_E_FAILURE, mean HW + * mode change was required but driver failed to set HW + * mode so ignore CSA for the channel. + */ + if (status == QDF_STATUS_E_FAILURE) { + sap_err("HW change required but failed to set hw mode"); + return status; + } + + /* + * If hw_mode_status is QDF_STATUS_SUCCESS mean HW mode + * change was required and was successfully requested so + * the channel switch will continue after HW mode change + * completion. + */ + if (QDF_IS_STATUS_SUCCESS(status)) { + sap_info("Channel change will continue after HW mode change"); + return QDF_STATUS_SUCCESS; + } + + return sme_csa_restart(mac_ctx, sap_ctx->sessionId); +} + +/** + * sap_fsm_state_started() - utility function called from sap fsm + * @sap_ctx: SAP context + * @sap_event: SAP event buffer + * @mac_ctx: global MAC context + * + * This function is called for state transition from "SAP_STARTED" + * + * Return: QDF_STATUS + */ +static QDF_STATUS sap_fsm_state_started(struct sap_context *sap_ctx, + struct sap_sm_event *sap_event, + struct mac_context *mac_ctx) +{ + uint32_t msg = sap_event->event; + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + + if (msg == eSAP_HDD_STOP_INFRA_BSS) { + /* + * Transition from SAP_STARTED to SAP_STOPPING + * (both without substates) + */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("from state %s => %s"), + "SAP_STARTED", "SAP_STOPPING"); + sap_ctx->fsm_state = SAP_STOPPING; + qdf_status = sap_goto_stopping(sap_ctx); + } else if (eSAP_DFS_CHNL_SWITCH_ANNOUNCEMENT_START == msg) { + uint8_t intf; + if (!mac_ctx->sap.SapDfsInfo.target_chan_freq) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("Invalid target channel freq %d"), + mac_ctx->sap.SapDfsInfo.target_chan_freq); + return qdf_status; + } + + /* + * Radar is seen on the current operating channel + * send CSA IE for all associated stations + * Request for CSA IE transmission + */ + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + struct sap_context *temp_sap_ctx; + struct csr_roam_profile *profile; + + if (((QDF_SAP_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona) || + (QDF_P2P_GO_MODE == + mac_ctx->sap.sapCtxList[intf].sapPersona)) && + mac_ctx->sap.sapCtxList[intf].sap_context) { + temp_sap_ctx = + mac_ctx->sap.sapCtxList[intf].sap_context; + /* + * Radar won't come on non-dfs channel, so + * no need to move them + */ + profile = &temp_sap_ctx->csr_roamProfile; + if (!wlan_reg_is_passive_or_disable_ch( + mac_ctx->pdev, + wlan_reg_freq_to_chan( + mac_ctx->pdev, + profile->op_freq))) + continue; + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_MED, + FL("sapdfs: Sending CSAIE for sapctx[%pK]"), + temp_sap_ctx); + qdf_status = + sap_fsm_send_csa_restart_req(mac_ctx, + temp_sap_ctx); + } + } + } else { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("in state %s, invalid event msg %d"), + "SAP_STARTED", msg); + } + + return qdf_status; +} + +/** + * sap_fsm_state_stopping() - utility function called from sap fsm + * @sap_ctx: SAP context + * @sap_event: SAP event buffer + * @mac_ctx: global MAC context + * + * This function is called for state transition from "SAP_STOPPING" + * + * Return: QDF_STATUS + */ +static QDF_STATUS +sap_fsm_state_stopping(struct sap_context *sap_ctx, + struct sap_sm_event *sap_event, + struct mac_context *mac_ctx, + mac_handle_t mac_handle) +{ + uint32_t msg = sap_event->event; + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + + if (msg == eSAP_MAC_READY_FOR_CONNECTIONS) { + /* + * Transition from SAP_STOPPING to SAP_INIT + * (both without substates) + */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("from state %s => %s"), + "SAP_STOPPING", "SAP_INIT"); + sap_ctx->fsm_state = SAP_INIT; + + /* Close the SME session */ + qdf_status = sap_signal_hdd_event(sap_ctx, NULL, + eSAP_STOP_BSS_EVENT, + (void *)eSAP_STATUS_SUCCESS); + } else if (msg == eSAP_HDD_STOP_INFRA_BSS) { + /* + * In case the SAP is already in stopping case and + * we get a STOP request, return success. + */ + sap_debug("SAP already in Stopping state"); + qdf_status = QDF_STATUS_SUCCESS; + } else { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("in state %s, invalid event msg %d"), + "SAP_STOPPING", msg); + } + + return qdf_status; +} + +/** + * sap_fsm() - SAP statem machine entry function + * @sap_ctx: SAP context + * @sap_event: SAP event + * + * SAP state machine entry function + * + * Return: QDF_STATUS + */ +QDF_STATUS sap_fsm(struct sap_context *sap_ctx, struct sap_sm_event *sap_event) +{ + /* + * Retrieve the phy link state machine structure + * from the sap_ctx value + * state var that keeps track of state machine + */ + enum sap_fsm_state state_var = sap_ctx->fsm_state; + uint32_t msg = sap_event->event; /* State machine input event message */ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx; + mac_handle_t mac_handle; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAILURE; + } + mac_handle = MAC_HANDLE(mac_ctx); + + sap_debug("state=%d handle event=%d", state_var, msg); + + switch (state_var) { + case SAP_INIT: + qdf_status = sap_fsm_state_init(sap_ctx, sap_event, + mac_ctx, mac_handle); + break; + + case SAP_STARTING: + qdf_status = sap_fsm_state_starting(sap_ctx, sap_event, + mac_ctx, mac_handle); + break; + + case SAP_STARTED: + qdf_status = sap_fsm_state_started(sap_ctx, sap_event, + mac_ctx); + break; + + case SAP_STOPPING: + qdf_status = sap_fsm_state_stopping(sap_ctx, sap_event, + mac_ctx, mac_handle); + break; + } + return qdf_status; +} + +eSapStatus +sapconvert_to_csr_profile(struct sap_config *config, eCsrRoamBssType bssType, + struct csr_roam_profile *profile) +{ + int qdf_status = QDF_STATUS_SUCCESS; + bool sap_uapsd = true, chan_switch_hostapd_rate_enabled = true; + bool ap_obss_prot = false; + uint16_t ap_prot = cfg_default(CFG_AP_PROTECTION_MODE); + struct mac_context *mac_ctx; + uint8_t mcc_to_scc_switch = 0; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + sap_err("Invalid MAC context"); + return eSAP_STATUS_FAILURE; + } + + /* Create Roam profile for SoftAP to connect */ + profile->BSSType = eCSR_BSS_TYPE_INFRA_AP; + profile->SSIDs.numOfSSIDs = 1; + profile->csrPersona = config->persona; + + qdf_mem_zero(profile->SSIDs.SSIDList[0].SSID.ssId, + sizeof(profile->SSIDs.SSIDList[0].SSID.ssId)); + + /* Flag to not broadcast the SSID information */ + profile->SSIDs.SSIDList[0].ssidHidden = + config->SSIDinfo.ssidHidden; + + profile->SSIDs.SSIDList[0].SSID.length = + config->SSIDinfo.ssid.length; + qdf_mem_copy(&profile->SSIDs.SSIDList[0].SSID.ssId, + config->SSIDinfo.ssid.ssId, + sizeof(config->SSIDinfo.ssid.ssId)); + + profile->negotiatedAuthType = eCSR_AUTH_TYPE_OPEN_SYSTEM; + + if (config->authType == eSAP_OPEN_SYSTEM) { + profile->negotiatedAuthType = eCSR_AUTH_TYPE_OPEN_SYSTEM; + } else if (config->authType == eSAP_SHARED_KEY) { + profile->negotiatedAuthType = eCSR_AUTH_TYPE_SHARED_KEY; + } else { + profile->negotiatedAuthType = eCSR_AUTH_TYPE_AUTOSWITCH; + } + + profile->AuthType.numEntries = 1; + profile->AuthType.authType[0] = eCSR_AUTH_TYPE_OPEN_SYSTEM; + + profile->akm_list = config->akm_list; + + /* Always set the Encryption Type */ + profile->EncryptionType.numEntries = 1; + profile->EncryptionType.encryptionType[0] = + config->RSNEncryptType; + + profile->mcEncryptionType.numEntries = 1; + profile->mcEncryptionType.encryptionType[0] = + config->mcRSNEncryptType; + + if (config->privacy & eSAP_SHARED_KEY) { + profile->AuthType.authType[0] = eCSR_AUTH_TYPE_SHARED_KEY; + } + + profile->privacy = config->privacy; + profile->fwdWPSPBCProbeReq = config->fwdWPSPBCProbeReq; + + if (config->authType == eSAP_SHARED_KEY) { + profile->csr80211AuthType = eSIR_SHARED_KEY; + } else if (config->authType == eSAP_OPEN_SYSTEM) { + profile->csr80211AuthType = eSIR_OPEN_SYSTEM; + } else { + profile->csr80211AuthType = eSIR_AUTO_SWITCH; + } + + /* Initialize we are not going to use it */ + profile->pWPAReqIE = NULL; + profile->nWPAReqIELength = 0; + + if (profile->pRSNReqIE) { + sap_debug("pRSNReqIE already allocated."); + qdf_mem_free(profile->pRSNReqIE); + profile->pRSNReqIE = NULL; + } + + /* set the RSN/WPA IE */ + profile->nRSNReqIELength = config->RSNWPAReqIELength; + if (config->RSNWPAReqIELength) { + profile->pRSNReqIE = + qdf_mem_malloc(config->RSNWPAReqIELength); + if (!profile->pRSNReqIE) + return eSAP_STATUS_FAILURE; + + qdf_mem_copy(profile->pRSNReqIE, config->RSNWPAReqIE, + config->RSNWPAReqIELength); + profile->nRSNReqIELength = config->RSNWPAReqIELength; + } + + /* set the phyMode to accept anything */ + /* Best means everything because it covers all the things we support */ + /* eCSR_DOT11_MODE_BEST */ + profile->phyMode = config->SapHw_mode; + + /* Configure beaconInterval */ + profile->beaconInterval = (uint16_t) config->beacon_int; + + /* set DTIM period */ + profile->dtimPeriod = config->dtim_period; + + /* set Uapsd enable bit */ + qdf_status = ucfg_mlme_is_sap_uapsd_enabled(mac_ctx->psoc, &sap_uapsd); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sap_err("Get ap UAPSD enabled/disabled failed"); + + profile->ApUapsdEnable = sap_uapsd; + /* Enable protection parameters */ + profile->protEnabled = ucfg_mlme_is_ap_prot_enabled(mac_ctx->psoc); + + /* Enable OBSS protection */ + qdf_status = ucfg_mlme_is_ap_obss_prot_enabled(mac_ctx->psoc, + &ap_obss_prot); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sap_err("Get ap obss protection failed"); + profile->obssProtEnabled = ap_obss_prot; + + qdf_status = ucfg_mlme_get_ap_protection_mode(mac_ctx->psoc, &ap_prot); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sap_err("Get ap protection mode failed using default value"); + profile->cfg_protection = ap_prot; + + /* country code */ + if (config->countryCode[0]) + qdf_mem_copy(profile->countryCode, config->countryCode, + CFG_COUNTRY_CODE_LEN); + profile->ieee80211d = config->ieee80211d; + /* wps config info */ + profile->wps_state = config->wps_state; + +#ifdef WLAN_FEATURE_11W + /* MFP capable/required */ + profile->MFPCapable = config->mfpCapable ? 1 : 0; + profile->MFPRequired = config->mfpRequired ? 1 : 0; +#endif + + if (config->probeRespIEsBufferLen > 0 && + config->pProbeRespIEsBuffer) { + profile->add_ie_params.probeRespDataLen = + config->probeRespIEsBufferLen; + profile->add_ie_params.probeRespData_buff = + config->pProbeRespIEsBuffer; + } else { + profile->add_ie_params.probeRespDataLen = 0; + profile->add_ie_params.probeRespData_buff = NULL; + } + /*assoc resp IE */ + if (config->assocRespIEsLen > 0 && + config->pAssocRespIEsBuffer) { + profile->add_ie_params.assocRespDataLen = + config->assocRespIEsLen; + profile->add_ie_params.assocRespData_buff = + config->pAssocRespIEsBuffer; + } else { + profile->add_ie_params.assocRespDataLen = 0; + profile->add_ie_params.assocRespData_buff = NULL; + } + + if (config->probeRespBcnIEsLen > 0 && + config->pProbeRespBcnIEsBuffer) { + profile->add_ie_params.probeRespBCNDataLen = + config->probeRespBcnIEsLen; + profile->add_ie_params.probeRespBCNData_buff = + config->pProbeRespBcnIEsBuffer; + } else { + profile->add_ie_params.probeRespBCNDataLen = 0; + profile->add_ie_params.probeRespBCNData_buff = NULL; + } + + if (config->supported_rates.numRates) { + qdf_mem_copy(profile->supported_rates.rate, + config->supported_rates.rate, + config->supported_rates.numRates); + profile->supported_rates.numRates = + config->supported_rates.numRates; + } + + if (config->extended_rates.numRates) { + qdf_mem_copy(profile->extended_rates.rate, + config->extended_rates.rate, + config->extended_rates.numRates); + profile->extended_rates.numRates = + config->extended_rates.numRates; + } + + profile->require_h2e = config->require_h2e; + + qdf_status = ucfg_mlme_get_sap_chan_switch_rate_enabled( + mac_ctx->psoc, + &chan_switch_hostapd_rate_enabled); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + sap_err("ucfg_mlme_get_sap_chan_switch_rate_enabled, set def"); + + profile->chan_switch_hostapd_rate_enabled = + chan_switch_hostapd_rate_enabled; + if (QDF_STATUS_SUCCESS == + ucfg_policy_mgr_get_mcc_scc_switch(mac_ctx->psoc, + &mcc_to_scc_switch)) { + if (mcc_to_scc_switch != QDF_MCC_TO_SCC_SWITCH_DISABLE) + profile->chan_switch_hostapd_rate_enabled = false; + } + + return eSAP_STATUS_SUCCESS; +} + +void sap_free_roam_profile(struct csr_roam_profile *profile) +{ + if (profile->pRSNReqIE) { + qdf_mem_free(profile->pRSNReqIE); + profile->pRSNReqIE = NULL; + } +} + +void sap_sort_mac_list(struct qdf_mac_addr *macList, uint8_t size) +{ + uint8_t outer, inner; + struct qdf_mac_addr temp; + int32_t nRes = -1; + + if ((!macList) || (size > MAX_ACL_MAC_ADDRESS)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("either buffer is NULL or size = %d is more"), size); + return; + } + + for (outer = 0; outer < size; outer++) { + for (inner = 0; inner < size - 1; inner++) { + nRes = + qdf_mem_cmp((macList + inner)->bytes, + (macList + inner + 1)->bytes, + QDF_MAC_ADDR_SIZE); + if (nRes > 0) { + qdf_mem_copy(temp.bytes, + (macList + inner + 1)->bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy((macList + inner + 1)->bytes, + (macList + inner)->bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy((macList + inner)->bytes, + temp.bytes, QDF_MAC_ADDR_SIZE); + } + } + } +} + +bool +sap_search_mac_list(struct qdf_mac_addr *macList, + uint8_t num_mac, uint8_t *peerMac, + uint8_t *index) +{ + int32_t nRes = -1; + int8_t nStart = 0, nEnd, nMiddle; + + nEnd = num_mac - 1; + + if ((!macList) || (num_mac > MAX_ACL_MAC_ADDRESS)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("either buffer is NULL or size = %d is more."), num_mac); + return false; + } + + while (nStart <= nEnd) { + nMiddle = (nStart + nEnd) / 2; + nRes = + qdf_mem_cmp(&macList[nMiddle], peerMac, + QDF_MAC_ADDR_SIZE); + + if (0 == nRes) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "search SUCC"); + /* "index equals NULL" means the caller does not need the */ + /* index value of the peerMac being searched */ + if (index) { + *index = (uint8_t) nMiddle; + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_HIGH, "index %d", + *index); + } + return true; + } + if (nRes < 0) + nStart = nMiddle + 1; + else + nEnd = nMiddle - 1; + } + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "search not succ"); + return false; +} + +void sap_add_mac_to_acl(struct qdf_mac_addr *macList, + uint8_t *size, uint8_t *peerMac) +{ + int32_t nRes = -1; + int i; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "add acl entered"); + + if (!macList || *size > MAX_ACL_MAC_ADDRESS) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("either buffer is NULL or size = %d is incorrect."), + *size); + return; + } + + for (i = ((*size) - 1); i >= 0; i--) { + nRes = + qdf_mem_cmp(&macList[i], peerMac, QDF_MAC_ADDR_SIZE); + if (nRes > 0) { + /* Move alphabetically greater mac addresses one index down to allow for insertion + of new mac in sorted order */ + qdf_mem_copy((macList + i + 1)->bytes, + (macList + i)->bytes, QDF_MAC_ADDR_SIZE); + } else { + break; + } + } + /* This should also take care of if the element is the first to be added in the list */ + qdf_mem_copy((macList + i + 1)->bytes, peerMac, QDF_MAC_ADDR_SIZE); + /* increment the list size */ + (*size)++; +} + +void sap_remove_mac_from_acl(struct qdf_mac_addr *macList, + uint8_t *size, uint8_t index) +{ + int i; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "remove acl entered"); + /* + * Return if the list passed is empty. Ideally this should never happen + * since this funcn is always called after sap_search_mac_list to get + * the index of the mac addr to be removed and this will only get + * called if the search is successful. Still no harm in having the check + */ + if ((!macList) || (*size == 0) || + (*size > MAX_ACL_MAC_ADDRESS)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("either buffer is NULL or size %d is incorrect."), + *size); + return; + } + for (i = index; i < ((*size) - 1); i++) { + /* Move mac addresses starting from "index" passed one index up to delete the void + created by deletion of a mac address in ACL */ + qdf_mem_copy((macList + i)->bytes, (macList + i + 1)->bytes, + QDF_MAC_ADDR_SIZE); + } + /* The last space should be made empty since all mac addesses moved one step up */ + qdf_mem_zero((macList + (*size) - 1)->bytes, QDF_MAC_ADDR_SIZE); + /* reduce the list size by 1 */ + (*size)--; +} + +void sap_print_acl(struct qdf_mac_addr *macList, uint8_t size) +{ + int i; + uint8_t *macArray; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "print acl entered"); + + if ((!macList) || (size == 0) || (size >= MAX_ACL_MAC_ADDRESS)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, either buffer is NULL or size %d is incorrect.", + __func__, size); + return; + } + + for (i = 0; i < size; i++) { + macArray = (macList + i)->bytes; + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "** ACL entry %i - " QDF_MAC_ADDR_FMT, i, + QDF_MAC_ADDR_REF(macArray)); + } + return; +} + +QDF_STATUS sap_is_peer_mac_allowed(struct sap_context *sap_ctx, + uint8_t *peerMac) +{ + if (eSAP_ALLOW_ALL == sap_ctx->eSapMacAddrAclMode) + return QDF_STATUS_SUCCESS; + + if (sap_search_mac_list + (sap_ctx->acceptMacList, sap_ctx->nAcceptMac, peerMac, NULL)) + return QDF_STATUS_SUCCESS; + + if (sap_search_mac_list + (sap_ctx->denyMacList, sap_ctx->nDenyMac, peerMac, NULL)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, Peer " QDF_MAC_ADDR_FMT " in deny list", + __func__, QDF_MAC_ADDR_REF(peerMac)); + return QDF_STATUS_E_FAILURE; + } + /* A new station CAN associate, unless in deny list. Less stringent mode */ + if (eSAP_ACCEPT_UNLESS_DENIED == sap_ctx->eSapMacAddrAclMode) + return QDF_STATUS_SUCCESS; + + /* A new station CANNOT associate, unless in accept list. More stringent mode */ + if (eSAP_DENY_UNLESS_ACCEPTED == sap_ctx->eSapMacAddrAclMode) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, Peer " QDF_MAC_ADDR_FMT + " denied, Mac filter mode is eSAP_DENY_UNLESS_ACCEPTED", + __func__, QDF_MAC_ADDR_REF(peerMac)); + return QDF_STATUS_E_FAILURE; + } + + /* The new STA is neither in accept list nor in deny list. In this case, deny the association + * but send a wifi event notification indicating the mac address being denied + */ + if (eSAP_SUPPORT_ACCEPT_AND_DENY == sap_ctx->eSapMacAddrAclMode) { + sap_signal_hdd_event(sap_ctx, NULL, eSAP_UNKNOWN_STA_JOIN, + (void *) peerMac); + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "In %s, Peer " QDF_MAC_ADDR_FMT + " denied, Mac filter mode is eSAP_SUPPORT_ACCEPT_AND_DENY", + __func__, QDF_MAC_ADDR_REF(peerMac)); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +void sap_dump_acs_channel(struct sap_acs_cfg *acs_cfg) +{ + uint32_t buf_len = 0, len = 0, i; + uint8_t *chan_buff = NULL; + + /* + * Buffer of (num channl * 5) + 1 to consider the 4 char freq + * and 1 space after it for each channel and 1 to end the string + * with NULL. + */ + buf_len = (acs_cfg->ch_list_count * 5) + 1; + chan_buff = qdf_mem_malloc(buf_len); + if (!chan_buff) + return; + + for (i = 0; i < acs_cfg->ch_list_count; i++) + len += qdf_scnprintf(chan_buff + len, buf_len - len, + " %d", acs_cfg->freq_list[i]); + + sap_nofl_debug("ACS freq list[%d]:%s", + acs_cfg->ch_list_count, chan_buff); + qdf_mem_free(chan_buff); +} + +#ifdef SOFTAP_CHANNEL_RANGE +/** + * sap_get_freq_list() - get the list of channel frequency + * @sap_ctx: sap context + * @freq_list: pointer to channel list array + * @num_ch: pointer to number of channels. + * + * This function populates the list of channel frequency for scanning. + * + * Return: QDF_STATUS + */ +static QDF_STATUS sap_get_freq_list(struct sap_context *sap_ctx, + uint32_t **freq_list, + uint8_t *num_ch) +{ + uint8_t loop_count; + uint32_t *list; + uint8_t ch_count; + uint8_t dfs_master_enable; + uint32_t start_ch_freq, band_start_ch; + uint32_t end_ch_freq, band_end_ch; + uint32_t en_lte_coex; + struct mac_context *mac_ctx; + uint16_t ch_width; + uint8_t normalize_factor = 100; + uint32_t chan_freq; + struct acs_weight *weight_list; + struct acs_weight_range *range_list; + bool freq_present_in_list = false; + uint8_t i; + bool srd_chan_enabled; + enum QDF_OPMODE vdev_opmode; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + *num_ch = 0; + *freq_list = NULL; + return QDF_STATUS_E_FAULT; + } + + weight_list = mac_ctx->mlme_cfg->acs.normalize_weight_chan; + range_list = mac_ctx->mlme_cfg->acs.normalize_weight_range; + + dfs_master_enable = mac_ctx->mlme_cfg->dfs_cfg.dfs_master_capable; + if (sap_ctx->dfs_mode == ACS_DFS_MODE_DISABLE) + dfs_master_enable = false; + + start_ch_freq = sap_ctx->acs_cfg->start_ch_freq; + end_ch_freq = sap_ctx->acs_cfg->end_ch_freq; + ch_width = sap_ctx->acs_cfg->ch_width; + + sap_debug("startChannel %d, EndChannel %d, ch_width %d, HW:%d", + start_ch_freq, end_ch_freq, ch_width, + sap_ctx->acs_cfg->hw_mode); + + wlansap_extend_to_acs_range(MAC_HANDLE(mac_ctx), + &start_ch_freq, &end_ch_freq, + &band_start_ch, &band_end_ch); + + sap_debug("expanded startChannel %d,EndChannel %d band_start_ch %d, band_end_ch %d", + start_ch_freq, end_ch_freq, band_start_ch, band_end_ch); + + en_lte_coex = mac_ctx->mlme_cfg->sap_cfg.enable_lte_coex; + + /* Check if LTE coex is enabled and 2.4GHz is selected */ + if (en_lte_coex && (band_start_ch == CHAN_ENUM_2412) && + (band_end_ch == CHAN_ENUM_2484)) { + /* Set 2.4GHz upper limit to channel 9 for LTE COEX */ + band_end_ch = CHAN_ENUM_2452; + } + + /* Allocate the max number of channel supported */ + list = qdf_mem_malloc((NUM_CHANNELS) * sizeof(uint32_t)); + if (!list) { + *num_ch = 0; + *freq_list = NULL; + return QDF_STATUS_E_NOMEM; + } + + /* Search for the Active channels in the given range */ + ch_count = 0; + for (loop_count = band_start_ch; loop_count <= band_end_ch; + loop_count++) { + chan_freq = WLAN_REG_CH_TO_FREQ(loop_count); + + /* go to next channel if rf_channel is out of range */ + if (start_ch_freq > WLAN_REG_CH_TO_FREQ(loop_count) || + end_ch_freq < WLAN_REG_CH_TO_FREQ(loop_count)) + continue; + /* + * go to next channel if none of these condition pass + * - DFS scan enabled and chan not in CHANNEL_STATE_DISABLE + * - DFS scan disable but chan in CHANNEL_STATE_ENABLE + */ + if (!(((true == mac_ctx->scan.fEnableDFSChnlScan) && + wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, WLAN_REG_CH_TO_FREQ(loop_count))) + || + ((false == mac_ctx->scan.fEnableDFSChnlScan) && + (CHANNEL_STATE_ENABLE == + wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, WLAN_REG_CH_TO_FREQ(loop_count))) + ))) + continue; + + /* check if the channel is in NOL blacklist */ + if (!WLAN_REG_IS_6GHZ_CHAN_FREQ(WLAN_REG_CH_TO_FREQ( + loop_count))) { + if (sap_dfs_is_channel_in_nol_list( + sap_ctx, + WLAN_REG_CH_NUM(loop_count), + PHY_SINGLE_CHANNEL_CENTERED)) { + sap_debug("Ch %d in NOL list", + WLAN_REG_CH_NUM(loop_count)); + continue; + } + } + /* Skip DSRC channels */ + if (wlan_reg_is_dsrc_freq(WLAN_REG_CH_TO_FREQ(loop_count))) + continue; + + /* + * Skip the channels which are not in ACS config from user + * space + */ + if (!wlansap_is_channel_present_in_acs_list( + chan_freq, + sap_ctx->acs_cfg->freq_list, + sap_ctx->acs_cfg->ch_list_count)) + continue; + /* Dont scan DFS channels in case of MCC disallowed + * As it can result in SAP starting on DFS channel + * resulting MCC on DFS channel + */ + + if (wlan_reg_is_dfs_for_freq( + mac_ctx->pdev, + WLAN_REG_CH_TO_FREQ(loop_count))) { + if (!dfs_master_enable) + continue; + else if (policy_mgr_disallow_mcc( + mac_ctx->psoc, + WLAN_REG_CH_TO_FREQ(loop_count))) + continue; + normalize_factor = + MLME_GET_DFS_CHAN_WEIGHT( + mac_ctx->mlme_cfg->acs.np_chan_weightage); + freq_present_in_list = true; + } + + vdev_opmode = wlan_vdev_mlme_get_opmode(sap_ctx->vdev); + wlan_mlme_get_srd_master_mode_for_vdev(mac_ctx->psoc, + vdev_opmode, + &srd_chan_enabled); + + if (!srd_chan_enabled && + wlan_reg_is_etsi13_srd_chan_for_freq(mac_ctx->pdev, + WLAN_REG_CH_TO_FREQ(loop_count))) { + sap_debug("vdev opmode %d not allowed on SRD freq %d", + vdev_opmode, WLAN_REG_CH_TO_FREQ(loop_count)); + continue; + } + + /* Check if the freq is present in range list */ + for (i = 0; i < mac_ctx->mlme_cfg->acs.num_weight_range; i++) { + if (chan_freq >= range_list[i].start_freq && + chan_freq <= range_list[i].end_freq) { + normalize_factor = + range_list[i].normalize_weight; + sap_debug("Range list, freq %d normalize weight factor %d", + chan_freq, normalize_factor); + freq_present_in_list = true; + } + } + + for (i = 0; + i < mac_ctx->mlme_cfg->acs.normalize_weight_num_chan; + i++) { + if (chan_freq == weight_list[i].chan_freq) { + normalize_factor = + weight_list[i].normalize_weight; + sap_debug("freq %d normalize weight factor %d", + chan_freq, normalize_factor); + freq_present_in_list = true; + } + } + + /* This would mean that the user does not want this freq */ + if (freq_present_in_list && !normalize_factor) { + sap_debug("chan_freq %d ecluded normalize weight 0", + chan_freq); + freq_present_in_list = false; + continue; + } +#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE + if ((sap_ctx->acs_cfg->skip_scan_status == + eSAP_DO_PAR_ACS_SCAN)) { + uint32_t ch_freq; + + ch_freq = WLAN_REG_CH_TO_FREQ(loop_count); + if ((ch_freq >= + sap_ctx->acs_cfg->skip_scan_range1_stch && + ch_freq <= + sap_ctx->acs_cfg->skip_scan_range1_endch) || + (ch_freq >= + sap_ctx->acs_cfg->skip_scan_range2_stch && + ch_freq <= + sap_ctx->acs_cfg->skip_scan_range2_endch)) { + list[ch_count] = + WLAN_REG_CH_TO_FREQ(loop_count); + ch_count++; + sap_debug("%d %d added to ACS ch range", + ch_count, ch_freq); + } else { + sap_debug("%d %d skipped from ACS ch range", + ch_count, ch_freq); + } + } else { + list[ch_count] = WLAN_REG_CH_TO_FREQ(loop_count); + ch_count++; + sap_debug("%d added to ACS ch range", ch_count); + } +#else + list[ch_count] = WLAN_REG_CH_TO_FREQ(loop_count); + ch_count++; +#endif + } + if (!ch_count) { + sap_info("No active channels present for the current region"); + /* + * LTE COEX: channel range outside the restricted 2.4GHz + * band limits + */ + if (en_lte_coex && + start_ch_freq > WLAN_REG_CH_TO_FREQ(band_end_ch)) + sap_info("SAP can't be started as due to LTE COEX"); + } + + /* return the channel list and number of channels to scan */ + *num_ch = ch_count; + if (ch_count != 0) { + *freq_list = list; + } else { + *freq_list = NULL; + qdf_mem_free(list); + return QDF_STATUS_SUCCESS; + } + + for (loop_count = 0; loop_count < ch_count; loop_count++) { + sap_ctx->acs_cfg->freq_list[loop_count] = list[loop_count]; + } + sap_ctx->acs_cfg->ch_list_count = ch_count; + sap_dump_acs_channel(sap_ctx->acs_cfg); + + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef DFS_COMPONENT_ENABLE +uint8_t sap_indicate_radar(struct sap_context *sap_ctx) +{ + uint8_t target_channel = 0; + struct mac_context *mac; + + if (!sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + FL("null sap_ctx")); + return 0; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return 0; + } + + /* + * SAP needs to generate Channel Switch IE + * if the radar is found in the STARTED state + */ + if (sap_ctx->fsm_state == SAP_STARTED) + mac->sap.SapDfsInfo.csaIERequired = true; + + if (mac->mlme_cfg->dfs_cfg.dfs_disable_channel_switch) + return wlan_reg_freq_to_chan(mac->pdev, sap_ctx->chan_freq); + + /* set the Radar Found flag in SapDfsInfo */ + mac->sap.SapDfsInfo.sap_radar_found_status = true; + + if (sap_ctx->chan_before_pre_cac) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO, + FL("sapdfs: set chan before pre cac %d as target chan"), + sap_ctx->chan_before_pre_cac); + return sap_ctx->chan_before_pre_cac; + } + + if (sap_ctx->vendor_acs_dfs_lte_enabled && (QDF_STATUS_SUCCESS == + sap_signal_hdd_event(sap_ctx, NULL, eSAP_DFS_NEXT_CHANNEL_REQ, + (void *) eSAP_STATUS_SUCCESS))) + return 0; + + target_channel = sap_random_channel_sel(sap_ctx); + if (!target_channel) + sap_signal_hdd_event(sap_ctx, NULL, + eSAP_DFS_NO_AVAILABLE_CHANNEL, (void *) eSAP_STATUS_SUCCESS); + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_WARN, + FL("sapdfs: New selected target channel is [%d]"), + target_channel); + + return target_channel; +} +#endif + +/* + * CAC timer callback function. + * Post eSAP_DFS_CHANNEL_CAC_END event to sap_fsm(). + */ +void sap_dfs_cac_timer_callback(void *data) +{ + struct sap_context *sap_ctx; + struct sap_sm_event sap_event; + mac_handle_t mac_handle = data; + struct mac_context *mac; + + if (!mac_handle) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "In %s invalid mac_handle", __func__); + return; + } + mac = MAC_CONTEXT(mac_handle); + sap_ctx = sap_find_cac_wait_session(mac_handle); + if (!sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "%s: no SAP contexts in wait state", __func__); + return; + } + + /* + * SAP may not be in CAC wait state, when the timer runs out. + * if following flag is set, then timer is in initialized state, + * destroy timer here. + */ + if (mac->sap.SapDfsInfo.is_dfs_cac_timer_running == true) { + if (!sap_ctx->dfs_cac_offload) + qdf_mc_timer_destroy( + &mac->sap.SapDfsInfo.sap_dfs_cac_timer); + mac->sap.SapDfsInfo.is_dfs_cac_timer_running = false; + } + + /* + * CAC Complete, post eSAP_DFS_CHANNEL_CAC_END to sap_fsm + */ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + "sapdfs: Sending eSAP_DFS_CHANNEL_CAC_END for target_chan_freq = %d on sapctx[%pK]", + sap_ctx->chan_freq, sap_ctx); + + sap_event.event = eSAP_DFS_CHANNEL_CAC_END; + sap_event.params = 0; + sap_event.u1 = 0; + sap_event.u2 = 0; + + sap_fsm(sap_ctx, &sap_event); +} + +/* + * Function to stop the DFS CAC Timer + */ +static int sap_stop_dfs_cac_timer(struct sap_context *sap_ctx) +{ + struct mac_context *mac; + + if (!sap_ctx) + return 0; + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return 0; + } + + if (sap_ctx->dfs_cac_offload) { + mac->sap.SapDfsInfo.is_dfs_cac_timer_running = 0; + return 0; + } + + if (QDF_TIMER_STATE_RUNNING != + qdf_mc_timer_get_current_state(&mac->sap.SapDfsInfo. + sap_dfs_cac_timer)) { + return 0; + } + + qdf_mc_timer_stop(&mac->sap.SapDfsInfo.sap_dfs_cac_timer); + mac->sap.SapDfsInfo.is_dfs_cac_timer_running = 0; + qdf_mc_timer_destroy(&mac->sap.SapDfsInfo.sap_dfs_cac_timer); + + return 0; +} + +/* + * Function to start the DFS CAC Timer + * when SAP is started on a DFS channel + */ +static int sap_start_dfs_cac_timer(struct sap_context *sap_ctx) +{ + QDF_STATUS status; + uint32_t cac_dur; + struct mac_context *mac; + enum dfs_reg dfs_region; + + if (!sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "%s: null sap_ctx", __func__); + return 0; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return 0; + } + + if (sap_ctx->dfs_cac_offload) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + "%s: cac timer offloaded to firmware", __func__); + mac->sap.SapDfsInfo.is_dfs_cac_timer_running = true; + return 1; + } + + sap_get_cac_dur_dfs_region(sap_ctx, &cac_dur, &dfs_region); + if (0 == cac_dur) + return 0; + +#ifdef QCA_WIFI_NAPIER_EMULATION + cac_dur = cac_dur / 100; +#endif + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, + "sapdfs: SAP_DFS_CHANNEL_CAC_START on CH freq %d, CAC_DUR-%d sec", + sap_ctx->chan_freq, cac_dur / 1000); + + qdf_mc_timer_init(&mac->sap.SapDfsInfo.sap_dfs_cac_timer, + QDF_TIMER_TYPE_SW, + sap_dfs_cac_timer_callback, MAC_HANDLE(mac)); + + /* Start the CAC timer */ + status = qdf_mc_timer_start(&mac->sap.SapDfsInfo.sap_dfs_cac_timer, + cac_dur); + if (QDF_IS_STATUS_ERROR(status)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, + "%s: failed to start cac timer", __func__); + goto destroy_timer; + } + + mac->sap.SapDfsInfo.is_dfs_cac_timer_running = true; + + return 0; + +destroy_timer: + mac->sap.SapDfsInfo.is_dfs_cac_timer_running = false; + qdf_mc_timer_destroy(&mac->sap.SapDfsInfo.sap_dfs_cac_timer); + + return 1; +} + +/* + * This function initializes the NOL list + * parameters required to track the radar + * found DFS channels in the current Reg. Domain . + */ +QDF_STATUS sap_init_dfs_channel_nol_list(struct sap_context *sap_ctx) +{ + struct mac_context *mac; + + if (!sap_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid SAP context"); + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + utils_dfs_init_nol(mac->pdev); + + return QDF_STATUS_SUCCESS; +} + +/* + * This function will calculate how many interfaces + * have sap persona and returns total number of sap persona. + */ +uint8_t sap_get_total_number_sap_intf(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + uint8_t intf = 0; + uint8_t intf_count = 0; + + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + if (((QDF_SAP_MODE == mac->sap.sapCtxList[intf].sapPersona) + || + (QDF_P2P_GO_MODE == mac->sap.sapCtxList[intf].sapPersona)) + && mac->sap.sapCtxList[intf].sap_context) { + intf_count++; + } + } + return intf_count; +} + +/** + * is_concurrent_sap_ready_for_channel_change() - to check all saps are ready + * for channel change + * @mac_handle: Opaque handle to the global MAC context + * @sap_ctx: sap context for which this function has been called + * + * This function will find the concurrent sap context apart from + * passed sap context and return its channel change ready status + * + * + * Return: true if other SAP personas are ready to channel switch else false + */ +bool is_concurrent_sap_ready_for_channel_change(mac_handle_t mac_handle, + struct sap_context *sap_ctx) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct sap_context *sap_context; + uint8_t intf = 0; + + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + if (((QDF_SAP_MODE == mac->sap.sapCtxList[intf].sapPersona) + || + (QDF_P2P_GO_MODE == mac->sap.sapCtxList[intf].sapPersona)) + && mac->sap.sapCtxList[intf].sap_context) { + sap_context = + mac->sap.sapCtxList[intf].sap_context; + if (sap_context == sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + FL("sapCtx matched [%pK]"), + sap_ctx); + continue; + } else { + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_ERROR, + FL + ("concurrent sapCtx[%pK] didn't matche with [%pK]"), + sap_context, sap_ctx); + return sap_context->is_sap_ready_for_chnl_chng; + } + } + } + return false; +} + +/** + * sap_is_conc_sap_doing_scc_dfs() - check if conc SAPs are doing SCC DFS + * @mac_handle: Opaque handle to the global MAC context + * @sap_context: current SAP persona's channel + * + * If provided SAP's channel is DFS then Loop through each SAP or GO persona and + * check if other beaconing entity's channel is same DFS channel. If they are + * same then concurrent sap is doing SCC DFS. + * + * Return: true if two or more beaconing entitity doing SCC DFS else false + */ +bool sap_is_conc_sap_doing_scc_dfs(mac_handle_t mac_handle, + struct sap_context *given_sapctx) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct sap_context *sap_ctx; + uint8_t intf = 0, scc_dfs_counter = 0; + qdf_freq_t ch_freq; + + ch_freq = wlan_reg_legacy_chan_to_freq(mac->pdev, + given_sapctx->csr_roamProfile.op_freq); + /* + * current SAP persona's channel itself is not DFS, so no need to check + * what other persona's channel is + */ + if (!wlan_reg_is_dfs_for_freq(mac->pdev, + ch_freq)) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, + FL("skip this loop as provided channel is non-dfs")); + return false; + } + + for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) { + if ((QDF_SAP_MODE != mac->sap.sapCtxList[intf].sapPersona) && + (QDF_P2P_GO_MODE != mac->sap.sapCtxList[intf].sapPersona)) + continue; + if (!mac->sap.sapCtxList[intf].sap_context) + continue; + sap_ctx = mac->sap.sapCtxList[intf].sap_context; + /* if same SAP contexts then skip to next context */ + if (sap_ctx == given_sapctx) + continue; + if (given_sapctx->csr_roamProfile.op_freq == + sap_ctx->csr_roamProfile.op_freq) + scc_dfs_counter++; + } + + /* Found atleast two of the beaconing entities doing SCC DFS */ + if (scc_dfs_counter) + return true; + + return false; +} diff --git a/drivers/staging/qcacld-3.0/core/sap/src/sap_fsm_ext.h b/drivers/staging/qcacld-3.0/core/sap/src/sap_fsm_ext.h new file mode 100644 index 0000000000000000000000000000000000000000..648af1c807f087a5662de09c7ed5988a4912983f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sap/src/sap_fsm_ext.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* This file is generated from btampFsm.cdd - do not edit manually*/ +/* Generated on: Thu Oct 16 15:40:39 PDT 2008 */ + +#ifndef __SAPFSM_EXT_H__ +#define __SAPFSM_EXT_H__ + +/* Events that can be sent to the SAP state-machine */ +typedef enum { + eSAP_TIMER_CONNECT_ACCEPT_TIMEOUT = 0U, + eSAP_MAC_CONNECT_COMPLETED, + eSAP_MAC_CONNECT_INDICATION, + eSAP_MAC_KEY_SET_SUCCESS, + eSAP_RSN_FAILURE, + eSAP_HDD_START_INFRA_BSS, + eSAP_MAC_READY_FOR_CONNECTIONS, + eSAP_MAC_START_BSS_SUCCESS, + eSAP_MAC_START_BSS_FAILURE, + eSAP_RSN_SUCCESS, + eSAP_MAC_START_FAILS, + eSAP_HDD_STOP_INFRA_BSS, + eSAP_WRITE_REMOTE_AMP_ASSOC, + eSAP_DFS_CHANNEL_CAC_START, + eSAP_DFS_CHANNEL_CAC_RADAR_FOUND, + eSAP_DFS_CHANNEL_CAC_END, + eSAP_DFS_CHNL_SWITCH_ANNOUNCEMENT_START, + eSAP_OPERATING_CHANNEL_CHANGED, + eSAP_NO_MSG +} eSapMsg_t; + +#endif diff --git a/drivers/staging/qcacld-3.0/core/sap/src/sap_internal.h b/drivers/staging/qcacld-3.0/core/sap/src/sap_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..b9be0ea29c57c41a9611d3d4f5301f002ae59ce6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sap/src/sap_internal.h @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_QCT_WLANSAP_INTERNAL_H +#define WLAN_QCT_WLANSAP_INTERNAL_H + +/* + * This file contains the internal API exposed by the wlan SAP PAL layer + * module. + */ + +#include "cds_api.h" +#include "cds_packet.h" + +/* Pick up the CSR API definitions */ +#include "csr_api.h" +#include "sap_api.h" +#include "sap_fsm_ext.h" +#include "sap_ch_select.h" +#include +#include +#include "wlan_vdev_mlme_main.h" +#include "wlan_vdev_mlme_api.h" + +/* DFS Non Occupancy Period =30 minutes, in microseconds */ +#define SAP_DFS_NON_OCCUPANCY_PERIOD (30 * 60 * 1000 * 1000) + +#define SAP_DEBUG + +#define IS_ETSI_WEATHER_CH(_ch) ((_ch >= 120) && (_ch <= 130)) +#define IS_CH_BONDING_WITH_WEATHER_CH(_ch) (_ch == 116) +#define IS_CHAN_JAPAN_INDOOR(_ch) ((_ch >= 36) && (_ch <= 64)) +#define IS_CHAN_JAPAN_OUTDOOR(_ch)((_ch >= 100) && (_ch <= 140)) +#define DEFAULT_CAC_TIMEOUT (60 * 1000) /* msecs - 1 min */ +#define ETSI_WEATHER_CH_CAC_TIMEOUT (10 * 60 * 1000) /* msecs - 10 min */ +#define SAP_CHAN_PREFERRED_INDOOR 1 +#define SAP_CHAN_PREFERRED_OUTDOOR 2 + +/*SAP Specific logging*/ + +#define sap_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_SAP, params) +#define sap_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, params) +#define sap_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_SAP, params) +#define sap_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_SAP, params) +#define sap_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_SAP, params) + +#define sap_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_SAP, params) +#define sap_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_SAP, params) +#define sap_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_SAP, params) +#define sap_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_SAP, params) +#define sap_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_SAP, params) + +#define sap_alert_rl(params...) QDF_TRACE_FATAL_RL(QDF_MODULE_ID_SAP, params) +#define sap_err_rl(params...) QDF_TRACE_ERROR_RL(QDF_MODULE_ID_SAP, params) +#define sap_warn_rl(params...) QDF_TRACE_WARN_RL(QDF_MODULE_ID_SAP, params) +#define sap_info_rl(params...) QDF_TRACE_INFO_RL(QDF_MODULE_ID_SAP, params) +#define sap_debug_rl(params...) QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_SAP, params) + +/*---------------------------------------------------------------------------- + * Typedefs + * -------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------- + * Type Declarations - For internal SAP context information + * -------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------- + * Opaque SAP context Type Declaration + * -------------------------------------------------------------------------*/ +/* We were only using this syntax, when this was truly opaque. */ +/* (I.E., it was defined in a different file.) */ + +/** + * enum sap_fsm_state - SAP FSM states for Access Point role + * @SAP_INIT: init state + * @SAP_STARTING: starting phase + * @SAP_STARTED: up and running + * @SAP_STOPPING: about to stop and transitions to init + */ +enum sap_fsm_state { + SAP_INIT, + SAP_STARTING, + SAP_STARTED, + SAP_STOPPING +}; + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +/* + * In a setup having two MDM both operating in AP+AP MCC scenario + * if both the AP decides to use same or close channel set, CTS to + * self, mechanism is causing issues with connectivity. For this, its + * proposed that 2nd MDM devices which comes up later should detect + * presence of first MDM device via special Q2Q IE present in becon + * and avoid those channels mentioned in IE. + * + * Following struct will keep this info in sapCtx struct, and will be used + * to avoid such channels in Random Channel Select in case of radar ind. + */ +struct sap_avoid_channels_info { + bool present; + uint8_t channels[CFG_VALID_CHANNEL_LIST_LEN]; +}; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +struct sap_context { + + /* Include the current channel frequency of AP */ + uint32_t chan_freq; + uint32_t sec_ch_freq; + + /* Include the SME(CSR) sessionId here */ + uint8_t sessionId; + + /* vdev object corresponding to sessionId */ + struct wlan_objmgr_vdev *vdev; + + /* Include the associations MAC addresses */ + uint8_t self_mac_addr[CDS_MAC_ADDRESS_LEN]; + + /* Include the SME(CSR) context here */ + struct csr_roam_profile csr_roamProfile; + uint32_t csr_roamId; + + /* SAP event Callback to hdd */ + sap_event_cb sap_event_cb; + + /* + * Include the state machine structure here, state var that keeps + * track of state machine + */ + enum sap_fsm_state fsm_state; + enum sap_csa_reason_code csa_reason; + + /* Actual storage for AP and self (STA) SSID */ + tCsrSSIDInfo SSIDList[2]; + + /* Actual storage for AP bssid */ + struct qdf_mac_addr bssid; + + /* Mac filtering settings */ + eSapMacAddrACL eSapMacAddrAclMode; + struct qdf_mac_addr acceptMacList[MAX_ACL_MAC_ADDRESS]; + uint8_t nAcceptMac; + struct qdf_mac_addr denyMacList[MAX_ACL_MAC_ADDRESS]; + uint8_t nDenyMac; + + void *user_context; + + uint32_t nStaWPARSnReqIeLength; + uint8_t pStaWpaRsnReqIE[MAX_ASSOC_IND_IE_LEN]; + + uint32_t *freq_list; + uint8_t num_of_channel; + uint16_t ch_width_orig; + struct ch_params ch_params; + uint32_t chan_freq_before_switch_band; + enum phy_ch_width chan_width_before_switch_band; + uint32_t auto_channel_select_weight; + bool enableOverLapCh; + struct sap_acs_cfg *acs_cfg; + + qdf_time_t acs_req_timestamp; + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + uint8_t cc_switch_mode; +#endif + +#if defined(FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE) + bool dfs_ch_disable; +#endif + bool isCacEndNotified; + bool isCacStartNotified; + bool is_sap_ready_for_chnl_chng; + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + /* + * In a setup having two MDM both operating in AP+AP MCC scenario + * if both the AP decides to use same or close channel set, CTS to + * self, mechanism is causing issues with connectivity. For this, its + * proposed that 2nd MDM devices which comes up later should detect + * presence of first MDM device via special Q2Q IE present in becon + * and avoid those channels mentioned in IE. + * + * this struct contains the list of channels on which another MDM AP + * in MCC mode were detected. + */ + struct sap_avoid_channels_info sap_detected_avoid_ch_ie; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + /* + * sap_state, sap_status are created + * to inform upper layers about ACS scan status. + * Don't use these members for anyother purposes. + */ + eSapHddEvent sap_state; + eSapStatus sap_status; + uint32_t roc_ind_scan_id; + bool is_pre_cac_on; + bool pre_cac_complete; + bool vendor_acs_dfs_lte_enabled; + uint8_t dfs_vendor_channel; + uint8_t dfs_vendor_chan_bw; + uint8_t chan_before_pre_cac; + uint16_t beacon_tx_rate; + enum sap_acs_dfs_mode dfs_mode; + wlan_scan_requester req_id; + uint8_t sap_sta_id; + bool dfs_cac_offload; + bool is_chan_change_inprogress; + qdf_list_t owe_pending_assoc_ind_list; + uint32_t freq_before_ch_switch; +}; + +/*---------------------------------------------------------------------------- + * External declarations for global context + * -------------------------------------------------------------------------*/ + +/** + * struct sap_sm_event - SAP state machine event definition + * @params: A VOID pointer type for all possible inputs + * @event: State machine input event message + * @u1: Introduced to handle csr_roam_complete_cb roamStatus + * @u2: Introduced to handle csr_roam_complete_cb roamResult + */ +struct sap_sm_event { + void *params; + uint32_t event; + uint32_t u1; + uint32_t u2; +}; + +/*---------------------------------------------------------------------------- + * Function Declarations and Documentation + * -------------------------------------------------------------------------*/ + +/** + * sap_get_mac_context() - Get a pointer to the global MAC context + * + * Return: pointer to the global MAC context, or NULL if the MAC + * context is no longer registered + */ +static inline struct mac_context *sap_get_mac_context(void) +{ + return cds_get_context(QDF_MODULE_ID_PE); +} + +QDF_STATUS wlansap_context_get(struct sap_context *ctx); +void wlansap_context_put(struct sap_context *ctx); + +/** + * wlansap_pre_start_bss_acs_scan_callback() - callback for scan results + * @mac_handle: the mac_handle passed in with the scan request + * @sap_ctx: the SAP context pointer. + * @scanid: scan id passed + * @sessionid: session identifier + * @scan_status: status of scan -success, failure or abort + * + * Api for scan callback. This function is invoked as a result of scan + * completion and reports the scan results. + * + * Return: The QDF_STATUS code associated with performing the operation + */ +QDF_STATUS wlansap_pre_start_bss_acs_scan_callback(mac_handle_t mac_handle, + struct sap_context *sap_ctx, + uint8_t sessionid, + uint32_t scanid, + eCsrScanStatus scan_status); + +/** + * sap_select_channel() - select SAP channel + * @mac_handle: Opaque handle to the global MAC context + * @sap_ctx: Sap context + * @scan_list: scan entry list + * + * Runs a algorithm to select the best channel to operate in based on BSS + * rssi and bss count on each channel + * + * Returns: channel frequency if success, 0 otherwise + */ +uint32_t sap_select_channel(mac_handle_t mac_handle, struct sap_context *sap_ctx, + qdf_list_t *scan_list); + +QDF_STATUS +sap_signal_hdd_event(struct sap_context *sap_ctx, + struct csr_roam_info *pCsrRoamInfo, + eSapHddEvent sapHddevent, void *); + +QDF_STATUS sap_fsm(struct sap_context *sap_ctx, struct sap_sm_event *sap_event); + +eSapStatus +sapconvert_to_csr_profile(struct sap_config *config, + eCsrRoamBssType bssType, + struct csr_roam_profile *profile); + +void sap_free_roam_profile(struct csr_roam_profile *profile); + +QDF_STATUS +sap_is_peer_mac_allowed(struct sap_context *sap_ctx, uint8_t *peerMac); + +void +sap_sort_mac_list(struct qdf_mac_addr *macList, uint8_t size); + +void +sap_add_mac_to_acl(struct qdf_mac_addr *macList, uint8_t *size, + uint8_t *peerMac); + +void +sap_remove_mac_from_acl(struct qdf_mac_addr *macList, uint8_t *size, + uint8_t index); + +void +sap_print_acl(struct qdf_mac_addr *macList, uint8_t size); + +bool +sap_search_mac_list(struct qdf_mac_addr *macList, uint8_t num_mac, + uint8_t *peerMac, uint8_t *index); + +QDF_STATUS sap_init_dfs_channel_nol_list(struct sap_context *sap_ctx); + +bool sap_dfs_is_channel_in_nol_list(struct sap_context *sap_ctx, + uint8_t channelNumber, + ePhyChanBondState chanBondState); +void sap_dfs_cac_timer_callback(void *data); + +/** + * sap_cac_reset_notify() - BSS cleanup notification handler + * @mac_handle: Opaque handle to the global MAC context + * + * This function should be called upon stop bss indication to clean up + * DFS global structure. + */ +void sap_cac_reset_notify(mac_handle_t mac_handle); + +bool is_concurrent_sap_ready_for_channel_change(mac_handle_t mac_handle, + struct sap_context *sap_ctx); + +bool sap_is_conc_sap_doing_scc_dfs(mac_handle_t mac_handle, + struct sap_context *given_sapctx); + +uint8_t sap_get_total_number_sap_intf(mac_handle_t mac_handle); + +/** + * sap_channel_sel - Function for initiating scan request for ACS + * @sap_context: Sap Context value. + * + * Initiates Scan for ACS to pick a channel. + * + * Return: The QDF_STATUS code associated with performing the operation. + */ +QDF_STATUS sap_channel_sel(struct sap_context *sap_ctx); + +/** + * sap_validate_chan - Function validate the channel and forces SCC + * @sap_context: Sap Context value. + * @pre_start_bss: if its called pre start BSS with valid channel. + * @check_for_connection_update: true, check and wait for connection update + * false, do not perform connection update + * + * validate and update the channel in case of force SCC. + * + * Return: The QDF_STATUS code associated with performing the operation. + */ +QDF_STATUS +sap_validate_chan(struct sap_context *sap_context, + bool pre_start_bss, + bool check_for_connection_update); + +/** + * sap_check_in_avoid_ch_list() - checks if given channel present is channel + * avoidance list + * avoid_channels_info struct + * @sap_ctx: sap context. + * @channel: channel to be checked in sap_ctx's avoid ch list + * + * sap_ctx contains sap_avoid_ch_info strcut containing the list of channels on + * which MDM device's AP with MCC was detected. This function checks if given + * channel is present in that list. + * + * Return: true, if channel was present, false othersie. + */ +bool +sap_check_in_avoid_ch_list(struct sap_context *sap_ctx, uint8_t channel); + +/** + * sap_set_session_param() - set sap related param to sap context and global var + * @mac_handle: Opaque handle to the global MAC context + * @sapctx: pointer to sapctx + * @session_id: session id for sap + * + * This API will set appropriate softap parameters to sap context + * + * Return: QDF_STATUS + */ +QDF_STATUS sap_set_session_param(mac_handle_t mac_handle, + struct sap_context *sapctx, + uint32_t session_id); + +/** + * sap_clear_session_param() - clear sap related param from sap context + * @mac_handle: Opaque handle to the global MAC context + * @sapctx: pointer to sapctx + * @session_id: session id for sap + * + * This API will clear appropriate softap parameters from sap context + * + * Return: QDF_STATUS + */ +QDF_STATUS sap_clear_session_param(mac_handle_t mac_handle, + struct sap_context *sapctx, + uint32_t session_id); + +void sap_scan_event_callback(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg); + +#ifdef DFS_COMPONENT_ENABLE +/** + * sap_indicate_radar() - Process radar indication + * @sap_ctx: pointer to sap context + * + * process radar indication. + * + * Return: channel to which sap wishes to switch. + */ +uint8_t sap_indicate_radar(struct sap_context *sap_ctx); +#else +static inline uint8_t sap_indicate_radar(struct sap_context *sap_ctx) +{ + return 0; +} +#endif + +/** + * sap_select_default_oper_chan() - Select AP mode default operating channel + * @mac_ctx: mac context + * @acs_cfg: pointer to ACS config info + * + * Select AP mode default operating channel based on ACS hw mode and channel + * range configuration when ACS scan fails due to some reasons, such as scan + * timeout, etc. + * + * Return: Selected operating channel frequency + */ +uint32_t sap_select_default_oper_chan(struct mac_context *mac_ctx, + struct sap_acs_cfg *acs_cfg); + +/* + * sap_is_dfs_cac_wait_state() - check if sap is in cac wait state + * @sap_ctx: sap context to check + * + * Return: true if sap is in cac wait state + */ +bool sap_is_dfs_cac_wait_state(struct sap_context *sap_ctx); + +/** + * sap_chan_bond_dfs_sub_chan - check bonded channel includes dfs sub chan + * @sap_context: Handle to SAP context. + * @channel_number: chan whose bonded chan will be checked + * @bond_state: The channel bonding mode of the passed channel. + * + * This function checks if a given bonded channel includes dfs sub chan. + * + * Return: true if at least one dfs sub chan is bonded, otherwise false + */ +bool +sap_chan_bond_dfs_sub_chan(struct sap_context *sap_context, + uint8_t channel_number, + ePhyChanBondState bond_state); +#endif diff --git a/drivers/staging/qcacld-3.0/core/sap/src/sap_module.c b/drivers/staging/qcacld-3.0/core/sap/src/sap_module.c new file mode 100644 index 0000000000000000000000000000000000000000..90973e7c885708a94f7a1e13ac1f7b5b553e807f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sap/src/sap_module.c @@ -0,0 +1,3046 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * =========================================================================== + * sap_module.c + * OVERVIEW: + * This software unit holds the implementation of the WLAN SAP modules + * functions providing EXTERNAL APIs. It is also where the global SAP module + * context gets initialised + * DEPENDENCIES: + * Are listed for each API below. + * =========================================================================== + */ + +/* $Header$ */ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ +#include "qdf_trace.h" +#include "qdf_util.h" +#include "qdf_atomic.h" +/* Pick up the sme callback registration API */ +#include "sme_api.h" + +/* SAP API header file */ + +#include "sap_internal.h" +#include "sme_inside.h" +#include "cds_ieee80211_common_i.h" +#include "cds_regdomain.h" +#include "wlan_policy_mgr_api.h" +#include +#include "wlan_reg_services_api.h" +#include +#include +#include +#include +#include "cfg_ucfg_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "wlan_mlme_vdev_mgr_interface.h" + +#define SAP_DEBUG +static struct sap_context *gp_sap_ctx[SAP_MAX_NUM_SESSION]; +static qdf_atomic_t sap_ctx_ref_count[SAP_MAX_NUM_SESSION]; +static qdf_mutex_t sap_context_lock; + +/** + * wlansap_global_init() - Initialize SAP globals + * + * Initializes the SAP global data structures + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_global_init(void) +{ + uint32_t i; + + if (QDF_IS_STATUS_ERROR(qdf_mutex_create(&sap_context_lock))) { + sap_err("failed to init sap_context_lock"); + return QDF_STATUS_E_FAULT; + } + + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + gp_sap_ctx[i] = NULL; + qdf_atomic_init(&sap_ctx_ref_count[i]); + } + + sap_debug("sap global context initialized"); + + return QDF_STATUS_SUCCESS; +} + +/** + * wlansap_global_deinit() - De-initialize SAP globals + * + * De-initializes the SAP global data structures + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_global_deinit(void) +{ + uint32_t i; + + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + if (gp_sap_ctx[i]) { + sap_err("we could be leaking context:%d", i); + } + gp_sap_ctx[i] = NULL; + qdf_atomic_init(&sap_ctx_ref_count[i]); + } + + if (QDF_IS_STATUS_ERROR(qdf_mutex_destroy(&sap_context_lock))) { + sap_err("failed to destroy sap_context_lock"); + return QDF_STATUS_E_FAULT; + } + + sap_debug("sap global context deinitialized"); + + return QDF_STATUS_SUCCESS; +} + +/** + * wlansap_save_context() - Save the context in global SAP context + * @ctx: SAP context to be stored + * + * Stores the given SAP context in the global SAP context array + * + * Return: QDF_STATUS + */ +static QDF_STATUS wlansap_save_context(struct sap_context *ctx) +{ + uint32_t i; + + qdf_mutex_acquire(&sap_context_lock); + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + if (!gp_sap_ctx[i]) { + gp_sap_ctx[i] = ctx; + qdf_atomic_inc(&sap_ctx_ref_count[i]); + qdf_mutex_release(&sap_context_lock); + sap_debug("sap context saved at index: %d", i); + return QDF_STATUS_SUCCESS; + } + } + qdf_mutex_release(&sap_context_lock); + + sap_err("failed to save sap context"); + + return QDF_STATUS_E_FAILURE; +} + +/** + * wlansap_context_get() - Verify SAP context and increment ref count + * @ctx: Context to be checked + * + * Verifies the SAP context and increments the reference count maintained for + * the corresponding SAP context. + * + * Return: QDF_STATUS + */ +QDF_STATUS wlansap_context_get(struct sap_context *ctx) +{ + uint32_t i; + + qdf_mutex_acquire(&sap_context_lock); + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + if (ctx && (gp_sap_ctx[i] == ctx)) { + qdf_atomic_inc(&sap_ctx_ref_count[i]); + qdf_mutex_release(&sap_context_lock); + return QDF_STATUS_SUCCESS; + } + } + qdf_mutex_release(&sap_context_lock); + + sap_debug("sap session is not valid"); + return QDF_STATUS_E_FAILURE; +} + +/** + * wlansap_context_put() - Check the reference count and free SAP context + * @ctx: SAP context to be checked and freed + * + * Checks the reference count and frees the SAP context + * + * Return: None + */ +void wlansap_context_put(struct sap_context *ctx) +{ + uint32_t i; + + if (!ctx) + return; + + qdf_mutex_acquire(&sap_context_lock); + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + if (gp_sap_ctx[i] == ctx) { + if (qdf_atomic_dec_and_test(&sap_ctx_ref_count[i])) { + if (ctx->freq_list) { + qdf_mem_free(ctx->freq_list); + ctx->freq_list = NULL; + ctx->num_of_channel = 0; + } + qdf_mem_free(ctx); + gp_sap_ctx[i] = NULL; + sap_debug("sap session freed: %d", i); + } + qdf_mutex_release(&sap_context_lock); + return; + } + } + qdf_mutex_release(&sap_context_lock); +} + +struct sap_context *sap_create_ctx(void) +{ + struct sap_context *sap_ctx; + QDF_STATUS status; + + /* dynamically allocate the sap_ctx */ + sap_ctx = qdf_mem_malloc(sizeof(*sap_ctx)); + if (!sap_ctx) + return NULL; + + /* Clean up SAP control block, initialize all values */ + /* Save the SAP context pointer */ + status = wlansap_save_context(sap_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + sap_err("failed to save SAP context"); + qdf_mem_free(sap_ctx); + return NULL; + } + sap_debug("Exit"); + + return sap_ctx; +} /* sap_create_ctx */ + +static QDF_STATUS wlansap_owe_init(struct sap_context *sap_ctx) +{ + qdf_list_create(&sap_ctx->owe_pending_assoc_ind_list, 0); + + return QDF_STATUS_SUCCESS; +} + +static void wlansap_owe_cleanup(struct sap_context *sap_ctx) +{ + struct mac_context *mac; + struct owe_assoc_ind *owe_assoc_ind; + struct assoc_ind *assoc_ind = NULL; + qdf_list_node_t *node = NULL, *next_node = NULL; + QDF_STATUS status; + + if (!sap_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid SAP context"); + return; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return; + } + + if (QDF_STATUS_SUCCESS != + qdf_list_peek_front(&sap_ctx->owe_pending_assoc_ind_list, + &node)) { + sap_debug("Failed to find assoc ind list"); + return; + } + + while (node) { + qdf_list_peek_next(&sap_ctx->owe_pending_assoc_ind_list, + node, &next_node); + owe_assoc_ind = qdf_container_of(node, struct owe_assoc_ind, + node); + status = qdf_list_remove_node( + &sap_ctx->owe_pending_assoc_ind_list, + node); + if (status == QDF_STATUS_SUCCESS) { + assoc_ind = owe_assoc_ind->assoc_ind; + qdf_mem_free(owe_assoc_ind); + assoc_ind->owe_ie = NULL; + assoc_ind->owe_ie_len = 0; + assoc_ind->owe_status = eSIR_MAC_UNSPEC_FAILURE_STATUS; + status = sme_update_owe_info(mac, assoc_ind); + qdf_mem_free(assoc_ind); + } else { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, + "Failed to remove assoc ind"); + } + node = next_node; + next_node = NULL; + } +} + +static void wlansap_owe_deinit(struct sap_context *sap_ctx) +{ + qdf_list_destroy(&sap_ctx->owe_pending_assoc_ind_list); +} + +QDF_STATUS sap_init_ctx(struct sap_context *sap_ctx, + enum QDF_OPMODE mode, + uint8_t *addr, uint32_t session_id, bool reinit) +{ + QDF_STATUS status; + struct mac_context *mac; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "wlansap_start invoked successfully"); + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + /* Now configure the roaming profile links. To SSID and bssid.*/ + /* We have room for two SSIDs. */ + sap_ctx->csr_roamProfile.SSIDs.numOfSSIDs = 1; /* This is true for now. */ + sap_ctx->csr_roamProfile.SSIDs.SSIDList = sap_ctx->SSIDList; /* Array of two */ + sap_ctx->csr_roamProfile.SSIDs.SSIDList[0].SSID.length = 0; + sap_ctx->csr_roamProfile.SSIDs.SSIDList[0].handoffPermitted = false; + sap_ctx->csr_roamProfile.SSIDs.SSIDList[0].ssidHidden = + sap_ctx->SSIDList[0].ssidHidden; + + sap_ctx->csr_roamProfile.BSSIDs.numOfBSSIDs = 1; /* This is true for now. */ + sap_ctx->csa_reason = CSA_REASON_UNKNOWN; + sap_ctx->csr_roamProfile.BSSIDs.bssid = &sap_ctx->bssid; + sap_ctx->csr_roamProfile.csrPersona = mode; + qdf_mem_copy(sap_ctx->self_mac_addr, addr, QDF_MAC_ADDR_SIZE); + + /* Now configure the auth type in the roaming profile. To open. */ + sap_ctx->csr_roamProfile.negotiatedAuthType = eCSR_AUTH_TYPE_OPEN_SYSTEM; /* open is the default */ + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_INVAL; + } + + status = sap_set_session_param(MAC_HANDLE(mac), sap_ctx, session_id); + if (QDF_STATUS_SUCCESS != status) { + sap_err("Calling sap_set_session_param status = %d", status); + return QDF_STATUS_E_FAILURE; + } + /* Register with scan component only during init */ + if (!reinit) + sap_ctx->req_id = + ucfg_scan_register_requester(mac->psoc, "SAP", + sap_scan_event_callback, sap_ctx); + + if (!reinit) { + status = wlansap_owe_init(sap_ctx); + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, + "OWE init failed"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sap_deinit_ctx(struct sap_context *sap_ctx) +{ + struct mac_context *mac; + + /* Sanity check - Extract SAP control block */ + sap_debug("wlansap_stop invoked successfully "); + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + wlansap_owe_cleanup(sap_ctx); + wlansap_owe_deinit(sap_ctx); + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + ucfg_scan_unregister_requester(mac->psoc, sap_ctx->req_id); + + if (sap_ctx->freq_list) { + qdf_mem_free(sap_ctx->freq_list); + sap_ctx->freq_list = NULL; + sap_ctx->num_of_channel = 0; + } + sap_free_roam_profile(&sap_ctx->csr_roamProfile); + if (sap_ctx->sessionId != WLAN_UMAC_VDEV_ID_MAX) { + /* empty queues/lists/pkts if any */ + sap_clear_session_param(MAC_HANDLE(mac), sap_ctx, + sap_ctx->sessionId); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sap_destroy_ctx(struct sap_context *sap_ctx) +{ + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("Enter")); + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + /* Cleanup SAP control block */ + /* + * wlansap_context_put will release actual sap_ctx memory + * allocated during sap_create_ctx + */ + wlansap_context_put(sap_ctx); + + return QDF_STATUS_SUCCESS; +} /* sap_destroy_ctx */ + +bool wlansap_is_channel_in_nol_list(struct sap_context *sap_ctx, + uint8_t channelNumber, + ePhyChanBondState chanBondState) +{ + if (!sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Invalid SAP pointer from pCtx", __func__); + return QDF_STATUS_E_FAULT; + } + + return sap_dfs_is_channel_in_nol_list(sap_ctx, channelNumber, + chanBondState); +} + +static QDF_STATUS wlansap_mark_leaking_channel(struct wlan_objmgr_pdev *pdev, + uint8_t *leakage_adjusted_lst, + uint8_t chan_bw) +{ + + return utils_dfs_mark_leaking_ch(pdev, chan_bw, 1, + leakage_adjusted_lst); +} + +bool wlansap_is_channel_leaking_in_nol(struct sap_context *sap_ctx, + uint8_t channel, + uint8_t chan_bw) +{ + struct mac_context *mac_ctx; + uint8_t leakage_adjusted_lst[1]; + + leakage_adjusted_lst[0] = channel; + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + if (QDF_IS_STATUS_ERROR(wlansap_mark_leaking_channel(mac_ctx->pdev, + leakage_adjusted_lst, chan_bw))) + return true; + + if (!leakage_adjusted_lst[0]) + return true; + + return false; +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +uint16_t wlansap_check_cc_intf(struct sap_context *sap_ctx) +{ + struct mac_context *mac; + uint16_t intf_ch_freq; + eCsrPhyMode phy_mode; + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return 0; + } + phy_mode = sap_ctx->csr_roamProfile.phyMode; + intf_ch_freq = sme_check_concurrent_channel_overlap( + MAC_HANDLE(mac), + sap_ctx->chan_freq, + phy_mode, + sap_ctx->cc_switch_mode); + return intf_ch_freq; +} +#endif + + /** + * wlansap_set_scan_acs_channel_params() - Config scan and channel parameters. + * config: Pointer to the SAP config + * psap_ctx: Pointer to the SAP Context. + * pusr_context: Parameter that will be passed + * back in all the SAP callback events. + * + * This api function is used to copy Scan and Channel parameters from sap + * config to sap context. + * + * Return: The result code associated with + * performing the operation + */ +static QDF_STATUS +wlansap_set_scan_acs_channel_params(struct sap_config *config, + struct sap_context *psap_ctx, + void *pusr_context) +{ + struct mac_context *mac; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t auto_channel_select_weight; + + if (!config) { + sap_err("Invalid config passed "); + return QDF_STATUS_E_FAULT; + } + + if (!psap_ctx) { + sap_err("Invalid config passed "); + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + sap_err("Invalid MAC context"); + return QDF_STATUS_E_INVAL; + } + + /* Channel selection is auto or configured */ + psap_ctx->chan_freq = config->chan_freq; + psap_ctx->dfs_mode = config->acs_dfs_mode; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + psap_ctx->cc_switch_mode = config->cc_switch_mode; +#endif + status = ucfg_mlme_get_auto_channel_weight( + mac->psoc, + &auto_channel_select_weight); + + if (!QDF_IS_STATUS_SUCCESS(status)) + sap_err("get_auto_channel_weight failed"); + + psap_ctx->auto_channel_select_weight = auto_channel_select_weight; + sap_debug("auto_channel_select_weight %d", + psap_ctx->auto_channel_select_weight); + + psap_ctx->user_context = pusr_context; + psap_ctx->enableOverLapCh = config->enOverLapCh; + psap_ctx->acs_cfg = &config->acs_cfg; + psap_ctx->ch_width_orig = config->acs_cfg.ch_width; + psap_ctx->sec_ch_freq = config->sec_ch_freq; + + /* + * Set the BSSID to your "self MAC Addr" read + * the mac address from Configuation ITEM received + * from HDD + */ + psap_ctx->csr_roamProfile.BSSIDs.numOfBSSIDs = 1; + + /* Save a copy to SAP context */ + qdf_mem_copy(psap_ctx->csr_roamProfile.BSSIDs.bssid, + config->self_macaddr.bytes, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(psap_ctx->self_mac_addr, + config->self_macaddr.bytes, QDF_MAC_ADDR_SIZE); + + return status; +} + +/** + * wlan_sap_get_roam_profile() - Returns sap roam profile. + * @sap_ctx: Pointer to Sap Context. + * + * This function provides the SAP roam profile. + * + * Return: SAP RoamProfile + */ +struct csr_roam_profile *wlan_sap_get_roam_profile(struct sap_context *sap_ctx) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer from ctx"); + return NULL; + } + return &sap_ctx->csr_roamProfile; +} + +eCsrPhyMode wlan_sap_get_phymode(struct sap_context *sap_ctx) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer from ctx"); + return 0; + } + return sap_ctx->csr_roamProfile.phyMode; +} + +uint32_t wlan_sap_get_vht_ch_width(struct sap_context *sap_ctx) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return 0; + } + + return sap_ctx->ch_params.ch_width; +} + +void wlan_sap_set_vht_ch_width(struct sap_context *sap_ctx, + uint32_t vht_channel_width) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return; + } + + sap_ctx->ch_params.ch_width = vht_channel_width; +} + +bool wlan_sap_get_ch_params(struct sap_context *sap_ctx, + struct ch_params *ch_params) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return false; + } + + *ch_params = sap_ctx->ch_params; + return true; +} + +/** + * wlan_sap_validate_channel_switch() - validate target channel switch w.r.t + * concurreny rules set to avoid channel interference. + * @mac_handle: Opaque handle to the global MAC context + * @sap_ch: channel to switch + * @sap_context: sap session context + * + * Return: true if there is no channel interference else return false + */ +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +static bool wlan_sap_validate_channel_switch(mac_handle_t mac_handle, + uint32_t sap_ch_freq, + struct sap_context *sap_context) +{ + return sme_validate_sap_channel_switch( + mac_handle, + sap_ch_freq, + sap_context->csr_roamProfile.phyMode, + sap_context->cc_switch_mode, + sap_context->sessionId); +} +#else +static bool wlan_sap_validate_channel_switch(mac_handle_t mac_handle, + uint32_t sap_ch_freq, + struct sap_context *sap_context) +{ + return true; +} +#endif + +void wlan_sap_set_sap_ctx_acs_cfg(struct sap_context *sap_ctx, + struct sap_config *sap_config) +{ + if (!sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Invalid SAP pointer", + __func__); + return; + } + + sap_ctx->acs_cfg = &sap_config->acs_cfg; +} + +#ifdef WLAN_CONV_CRYPTO_SUPPORTED +static inline QDF_STATUS +wlansap_set_vdev_crypto_prarams_from_ie(struct wlan_objmgr_vdev *vdev, + uint8_t *ie_ptr, + uint16_t ie_len) +{ + return wlan_set_vdev_crypto_prarams_from_ie(vdev, ie_ptr, ie_len); +} +#else +static inline QDF_STATUS +wlansap_set_vdev_crypto_prarams_from_ie(struct wlan_objmgr_vdev *vdev, + uint8_t *ie_ptr, + uint16_t ie_len) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS wlansap_start_bss(struct sap_context *sap_ctx, + sap_event_cb sap_event_cb, + struct sap_config *config, void *user_context) +{ + struct sap_sm_event sap_event; /* State machine event */ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + uint32_t auto_channel_select_weight = + cfg_default(CFG_AUTO_CHANNEL_SELECT_WEIGHT); + int reduced_beacon_interval; + struct mac_context *pmac = NULL; + int sap_chanswitch_beacon_cnt; + bool sap_chanswitch_mode; + + if (!sap_ctx) { + sap_info("Invalid SAP context"); + return QDF_STATUS_E_FAULT; + } + + pmac = sap_get_mac_context(); + if (!pmac) { + sap_err("Invalid sap MAC context"); + qdf_status = QDF_STATUS_E_INVAL; + goto fail; + } + + sap_ctx->fsm_state = SAP_INIT; + + qdf_status = wlansap_set_vdev_crypto_prarams_from_ie( + sap_ctx->vdev, + config->RSNWPAReqIE, + config->RSNWPAReqIELength); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sap_debug("Failed to set crypto params from IE"); + + /* Channel selection is auto or configured */ + sap_ctx->chan_freq = config->chan_freq; + sap_ctx->dfs_mode = config->acs_dfs_mode; + sap_ctx->ch_params.ch_width = config->ch_params.ch_width; + sap_ctx->ch_params.center_freq_seg0 = + config->ch_params.center_freq_seg0; + sap_ctx->ch_params.center_freq_seg1 = + config->ch_params.center_freq_seg1; + sap_ctx->ch_params.sec_ch_offset = + config->ch_params.sec_ch_offset; + sap_ctx->ch_width_orig = config->ch_width_orig; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + sap_ctx->cc_switch_mode = config->cc_switch_mode; +#endif + + qdf_status = ucfg_mlme_get_auto_channel_weight( + pmac->psoc, + &auto_channel_select_weight); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + sap_err("get_auto_channel_weight failed"); + + sap_ctx->auto_channel_select_weight = auto_channel_select_weight; + + sap_ctx->user_context = user_context; + sap_ctx->enableOverLapCh = config->enOverLapCh; + sap_ctx->acs_cfg = &config->acs_cfg; + sap_ctx->sec_ch_freq = config->sec_ch_freq; + sap_ctx->dfs_cac_offload = config->dfs_cac_offload; + sap_ctx->isCacEndNotified = false; + sap_ctx->is_chan_change_inprogress = false; + + /* Set the BSSID to your "self MAC Addr" read the mac address + from Configuation ITEM received from HDD */ + sap_ctx->csr_roamProfile.BSSIDs.numOfBSSIDs = 1; + qdf_mem_copy(sap_ctx->csr_roamProfile.BSSIDs.bssid, + sap_ctx->self_mac_addr, sizeof(struct qdf_mac_addr)); + + /* Save a copy to SAP context */ + qdf_mem_copy(sap_ctx->csr_roamProfile.BSSIDs.bssid, + config->self_macaddr.bytes, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(sap_ctx->self_mac_addr, + config->self_macaddr.bytes, QDF_MAC_ADDR_SIZE); + + /* copy the configuration items to csrProfile */ + sapconvert_to_csr_profile(config, eCSR_BSS_TYPE_INFRA_AP, + &sap_ctx->csr_roamProfile); + + /* + * Set the DFS Test Mode setting + * Set beacon channel count before chanel switch + */ + qdf_status = ucfg_mlme_get_sap_chn_switch_bcn_count( + pmac->psoc, + &sap_chanswitch_beacon_cnt); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + sap_err("ucfg_mlme_get_sap_chn_switch_bcn_count fail, set def"); + + pmac->sap.SapDfsInfo.sap_ch_switch_beacon_cnt = + sap_chanswitch_beacon_cnt; + pmac->sap.SapDfsInfo.sap_ch_switch_mode = + sap_chanswitch_beacon_cnt; + + qdf_status = ucfg_mlme_get_sap_channel_switch_mode( + pmac->psoc, + &sap_chanswitch_mode); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sap_err("ucfg_mlme_get_sap_channel_switch_mode, set def"); + + pmac->sap.SapDfsInfo.sap_ch_switch_mode = sap_chanswitch_mode; + pmac->sap.sapCtxList[sap_ctx->sessionId].sap_context = sap_ctx; + pmac->sap.sapCtxList[sap_ctx->sessionId].sapPersona = + sap_ctx->csr_roamProfile.csrPersona; + + qdf_status = ucfg_mlme_get_sap_reduces_beacon_interval( + pmac->psoc, + &reduced_beacon_interval); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + sap_err("ucfg_mlme_get_sap_reduces_beacon_interval fail"); + + pmac->sap.SapDfsInfo.reduced_beacon_interval = + reduced_beacon_interval; + sap_debug("SAP: auth ch select weight:%d chswitch bcn cnt:%d chswitch mode:%d reduced bcn intv:%d", + sap_ctx->auto_channel_select_weight, + sap_chanswitch_beacon_cnt, + pmac->sap.SapDfsInfo.sap_ch_switch_mode, + pmac->sap.SapDfsInfo.reduced_beacon_interval); + + /* Copy MAC filtering settings to sap context */ + sap_ctx->eSapMacAddrAclMode = config->SapMacaddr_acl; + qdf_mem_copy(sap_ctx->acceptMacList, config->accept_mac, + sizeof(config->accept_mac)); + sap_ctx->nAcceptMac = config->num_accept_mac; + sap_sort_mac_list(sap_ctx->acceptMacList, sap_ctx->nAcceptMac); + qdf_mem_copy(sap_ctx->denyMacList, config->deny_mac, + sizeof(config->deny_mac)); + sap_ctx->nDenyMac = config->num_deny_mac; + sap_sort_mac_list(sap_ctx->denyMacList, sap_ctx->nDenyMac); + sap_ctx->beacon_tx_rate = config->beacon_tx_rate; + + /* Fill in the event structure for FSM */ + sap_event.event = eSAP_HDD_START_INFRA_BSS; + sap_event.params = 0; /* pSapPhysLinkCreate */ + + /* Store the HDD callback in SAP context */ + sap_ctx->sap_event_cb = sap_event_cb; + + /* Handle event */ + qdf_status = sap_fsm(sap_ctx, &sap_event); +fail: + if (QDF_IS_STATUS_ERROR(qdf_status)) + sap_free_roam_profile(&sap_ctx->csr_roamProfile); + + return qdf_status; +} /* wlansap_start_bss */ + +QDF_STATUS wlansap_set_mac_acl(struct sap_context *sap_ctx, + struct sap_config *config) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "wlansap_set_mac_acl"); + + if (!sap_ctx) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Invalid SAP pointer", __func__); + return QDF_STATUS_E_FAULT; + } + /* Copy MAC filtering settings to sap context */ + sap_ctx->eSapMacAddrAclMode = config->SapMacaddr_acl; + + if (eSAP_DENY_UNLESS_ACCEPTED == sap_ctx->eSapMacAddrAclMode) { + qdf_mem_copy(sap_ctx->acceptMacList, + config->accept_mac, + sizeof(config->accept_mac)); + sap_ctx->nAcceptMac = config->num_accept_mac; + sap_sort_mac_list(sap_ctx->acceptMacList, + sap_ctx->nAcceptMac); + } else if (eSAP_ACCEPT_UNLESS_DENIED == sap_ctx->eSapMacAddrAclMode) { + qdf_mem_copy(sap_ctx->denyMacList, config->deny_mac, + sizeof(config->deny_mac)); + sap_ctx->nDenyMac = config->num_deny_mac; + sap_sort_mac_list(sap_ctx->denyMacList, sap_ctx->nDenyMac); + } + + return qdf_status; +} /* wlansap_set_mac_acl */ + +QDF_STATUS wlansap_stop_bss(struct sap_context *sap_ctx) +{ + struct sap_sm_event sap_event; /* State machine event */ + QDF_STATUS qdf_status; + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + /* Fill in the event structure for FSM */ + sap_event.event = eSAP_HDD_STOP_INFRA_BSS; + sap_event.params = 0; + + /* Handle event */ + qdf_status = sap_fsm(sap_ctx, &sap_event); + + return qdf_status; +} + +/* This routine will set the mode of operation for ACL dynamically*/ +QDF_STATUS wlansap_set_acl_mode(struct sap_context *sap_ctx, + eSapMacAddrACL mode) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + sap_ctx->eSapMacAddrAclMode = mode; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlansap_get_acl_mode(struct sap_context *sap_ctx, + eSapMacAddrACL *mode) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + *mode = sap_ctx->eSapMacAddrAclMode; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlansap_get_acl_accept_list(struct sap_context *sap_ctx, + struct qdf_mac_addr *pAcceptList, + uint8_t *nAcceptList) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + memcpy(pAcceptList, sap_ctx->acceptMacList, + (sap_ctx->nAcceptMac * QDF_MAC_ADDR_SIZE)); + *nAcceptList = sap_ctx->nAcceptMac; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlansap_get_acl_deny_list(struct sap_context *sap_ctx, + struct qdf_mac_addr *pDenyList, + uint8_t *nDenyList) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer from p_cds_gctx"); + return QDF_STATUS_E_FAULT; + } + + memcpy(pDenyList, sap_ctx->denyMacList, + (sap_ctx->nDenyMac * QDF_MAC_ADDR_SIZE)); + *nDenyList = sap_ctx->nDenyMac; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlansap_clear_acl(struct sap_context *sap_ctx) +{ + uint8_t i; + + if (!sap_ctx) { + return QDF_STATUS_E_RESOURCES; + } + + for (i = 0; i < (sap_ctx->nDenyMac - 1); i++) { + qdf_mem_zero((sap_ctx->denyMacList + i)->bytes, + QDF_MAC_ADDR_SIZE); + } + + sap_print_acl(sap_ctx->denyMacList, sap_ctx->nDenyMac); + sap_ctx->nDenyMac = 0; + + for (i = 0; i < (sap_ctx->nAcceptMac - 1); i++) { + qdf_mem_zero((sap_ctx->acceptMacList + i)->bytes, + QDF_MAC_ADDR_SIZE); + } + + sap_print_acl(sap_ctx->acceptMacList, sap_ctx->nAcceptMac); + sap_ctx->nAcceptMac = 0; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlansap_modify_acl(struct sap_context *sap_ctx, + uint8_t *peer_sta_mac, + eSapACLType list_type, eSapACLCmdType cmd) +{ + bool sta_white_list = false, sta_black_list = false; + uint8_t staWLIndex, staBLIndex; + + if (!sap_ctx) { + sap_err("Invalid SAP Context"); + return QDF_STATUS_E_FAULT; + } + if (qdf_mem_cmp(sap_ctx->bssid.bytes, peer_sta_mac, + QDF_MAC_ADDR_SIZE) == 0) { + sap_err("requested peer mac is" QDF_MAC_ADDR_FMT + "our own SAP BSSID. Do not blacklist or whitelist this BSSID", + QDF_MAC_ADDR_REF(peer_sta_mac)); + return QDF_STATUS_E_FAULT; + } + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_LOW, + "Modify ACL entered\n" "Before modification of ACL\n" + "size of accept and deny lists %d %d", sap_ctx->nAcceptMac, + sap_ctx->nDenyMac); + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "*** WHITE LIST ***"); + sap_print_acl(sap_ctx->acceptMacList, sap_ctx->nAcceptMac); + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "*** BLACK LIST ***"); + sap_print_acl(sap_ctx->denyMacList, sap_ctx->nDenyMac); + + /* the expectation is a mac addr will not be in both the lists + * at the same time. It is the responsiblity of userspace to + * ensure this + */ + sta_white_list = + sap_search_mac_list(sap_ctx->acceptMacList, sap_ctx->nAcceptMac, + peer_sta_mac, &staWLIndex); + sta_black_list = + sap_search_mac_list(sap_ctx->denyMacList, sap_ctx->nDenyMac, + peer_sta_mac, &staBLIndex); + + if (sta_white_list && sta_black_list) { + sap_err("Peer mac " QDF_MAC_ADDR_FMT + " found in white and black lists." + "Initial lists passed incorrect. Cannot execute this command.", + QDF_MAC_ADDR_REF(peer_sta_mac)); + return QDF_STATUS_E_FAILURE; + + } + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_LOW, + "cmd %d", cmd); + + switch (list_type) { + case eSAP_WHITE_LIST: + if (cmd == ADD_STA_TO_ACL) { + /* error check */ + /* if list is already at max, return failure */ + if (sap_ctx->nAcceptMac == MAX_ACL_MAC_ADDRESS) { + sap_err("White list is already maxed out. Cannot accept " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_sta_mac)); + return QDF_STATUS_E_FAILURE; + } + if (sta_white_list) { + /* Do nothing if already present in white list. Just print a warning */ + sap_warn("MAC address already present in white list " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_sta_mac)); + return QDF_STATUS_SUCCESS; + } + if (sta_black_list) { + /* remove it from black list before adding to the white list */ + sap_warn("STA present in black list so first remove from it"); + sap_remove_mac_from_acl(sap_ctx->denyMacList, + &sap_ctx->nDenyMac, + staBLIndex); + } + sap_debug("... Now add to the white list"); + sap_add_mac_to_acl(sap_ctx->acceptMacList, + &sap_ctx->nAcceptMac, + peer_sta_mac); + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_LOW, + "size of accept and deny lists %d %d", + sap_ctx->nAcceptMac, + sap_ctx->nDenyMac); + } else if (cmd == DELETE_STA_FROM_ACL) { + if (sta_white_list) { + + struct csr_del_sta_params delStaParams; + + sap_info("Delete from white list"); + sap_remove_mac_from_acl(sap_ctx->acceptMacList, + &sap_ctx->nAcceptMac, + staWLIndex); + /* If a client is deleted from white list and it is connected, send deauth */ + wlansap_populate_del_sta_params(peer_sta_mac, + eCsrForcedDeauthSta, + SIR_MAC_MGMT_DEAUTH, + &delStaParams); + wlansap_deauth_sta(sap_ctx, &delStaParams); + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_LOW, + "size of accept and deny lists %d %d", + sap_ctx->nAcceptMac, + sap_ctx->nDenyMac); + } else { + sap_warn("MAC address to be deleted is not present in the white list " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_sta_mac)); + return QDF_STATUS_E_FAILURE; + } + } else { + sap_err("Invalid cmd type passed"); + return QDF_STATUS_E_FAILURE; + } + break; + + case eSAP_BLACK_LIST: + + if (cmd == ADD_STA_TO_ACL) { + struct csr_del_sta_params delStaParams; + /* error check */ + /* if list is already at max, return failure */ + if (sap_ctx->nDenyMac == MAX_ACL_MAC_ADDRESS) { + sap_err("Black list is already maxed out. Cannot accept " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_sta_mac)); + return QDF_STATUS_E_FAILURE; + } + if (sta_black_list) { + /* Do nothing if already present in white list */ + sap_warn("MAC address already present in black list " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_sta_mac)); + return QDF_STATUS_SUCCESS; + } + if (sta_white_list) { + /* remove it from white list before adding to the black list */ + sap_warn("Present in white list so first remove from it"); + sap_remove_mac_from_acl(sap_ctx->acceptMacList, + &sap_ctx->nAcceptMac, + staWLIndex); + } + /* If we are adding a client to the black list; if its connected, send deauth */ + wlansap_populate_del_sta_params(peer_sta_mac, + eCsrForcedDeauthSta, + SIR_MAC_MGMT_DEAUTH, + &delStaParams); + wlansap_deauth_sta(sap_ctx, &delStaParams); + sap_info("... Now add to black list"); + sap_add_mac_to_acl(sap_ctx->denyMacList, + &sap_ctx->nDenyMac, peer_sta_mac); + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_LOW, + "size of accept and deny lists %d %d", + sap_ctx->nAcceptMac, + sap_ctx->nDenyMac); + } else if (cmd == DELETE_STA_FROM_ACL) { + if (sta_black_list) { + sap_info("Delete from black list"); + sap_remove_mac_from_acl(sap_ctx->denyMacList, + &sap_ctx->nDenyMac, + staBLIndex); + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_LOW, + "no accept and deny mac %d %d", + sap_ctx->nAcceptMac, + sap_ctx->nDenyMac); + } else { + sap_warn("MAC address to be deleted is not present in the black list " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_sta_mac)); + return QDF_STATUS_E_FAILURE; + } + } else { + sap_err("Invalid cmd type passed"); + return QDF_STATUS_E_FAILURE; + } + break; + + default: + { + sap_err("Invalid list type passed %d", list_type); + return QDF_STATUS_E_FAILURE; + } + } + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_LOW, + "After modification of ACL"); + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "*** WHITE LIST ***"); + sap_print_acl(sap_ctx->acceptMacList, sap_ctx->nAcceptMac); + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "*** BLACK LIST ***"); + sap_print_acl(sap_ctx->denyMacList, sap_ctx->nDenyMac); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlansap_disassoc_sta(struct sap_context *sap_ctx, + struct csr_del_sta_params *params) +{ + struct mac_context *mac; + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + return sme_roam_disconnect_sta(MAC_HANDLE(mac), sap_ctx->sessionId, + params); +} + +QDF_STATUS wlansap_deauth_sta(struct sap_context *sap_ctx, + struct csr_del_sta_params *params) +{ + struct mac_context *mac; + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + return sme_roam_deauth_sta(MAC_HANDLE(mac), sap_ctx->sessionId, + params); +} + +/** + * wlansap_update_csa_channel_params() - function to populate channel width and + * bonding modes. + * @sap_context: sap adapter context + * @channel: target channel + * + * Return: The QDF_STATUS code associated with performing the operation + */ +static QDF_STATUS +wlansap_update_csa_channel_params(struct sap_context *sap_context, + uint32_t chan_freq) +{ + struct mac_context *mac_ctx; + uint32_t max_fw_bw; + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq)) { + /* + * currently OBSS scan is done in hostapd, so to avoid + * SAP coming up in HT40 on channel switch we are + * disabling channel bonding in 2.4Ghz. + */ + mac_ctx->sap.SapDfsInfo.new_chanWidth = CH_WIDTH_20MHZ; + } else { + if (sap_context->csr_roamProfile.phyMode == + eCSR_DOT11_MODE_11ac || + sap_context->csr_roamProfile.phyMode == + eCSR_DOT11_MODE_11ac_ONLY || + sap_context->csr_roamProfile.phyMode == + eCSR_DOT11_MODE_11ax || + sap_context->csr_roamProfile.phyMode == + eCSR_DOT11_MODE_11ax_ONLY) { + max_fw_bw = sme_get_vht_ch_width(); + if (max_fw_bw >= WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) + mac_ctx->sap.SapDfsInfo.new_chanWidth = + CH_WIDTH_160MHZ; + else + mac_ctx->sap.SapDfsInfo.new_chanWidth = + CH_WIDTH_80MHZ; + } else if (sap_context->csr_roamProfile.phyMode == + eCSR_DOT11_MODE_11n || + sap_context->csr_roamProfile.phyMode == + eCSR_DOT11_MODE_11n_ONLY) { + mac_ctx->sap.SapDfsInfo.new_chanWidth = CH_WIDTH_40MHZ; + } else { + /* For legacy 11a mode return 20MHz */ + mac_ctx->sap.SapDfsInfo.new_chanWidth = CH_WIDTH_20MHZ; + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * sap_start_csa_restart() - send csa start event + * @mac_ctx: mac ctx + * @sap_ctx: SAP context + * + * Return: QDF_STATUS + */ +static inline void sap_start_csa_restart(struct mac_context *mac, + struct sap_context *sap_ctx) +{ + sme_csa_restart(mac, sap_ctx->sessionId); +} + +/** + * sap_get_csa_reason_str() - Get csa reason in string + * @reason: sap reason enum value + * + * Return: string reason + */ +static char *sap_get_csa_reason_str(enum sap_csa_reason_code reason) +{ + switch (reason) { + case CSA_REASON_UNKNOWN: + return "UNKNOWN"; + case CSA_REASON_STA_CONNECT_DFS_TO_NON_DFS: + return "STA_CONNECT_DFS_TO_NON_DFS"; + case CSA_REASON_USER_INITIATED: + return "USER_INITIATED"; + case CSA_REASON_PEER_ACTION_FRAME: + return "PEER_ACTION_FRAME"; + case CSA_REASON_PRE_CAC_SUCCESS: + return "PRE_CAC_SUCCESS"; + case CSA_REASON_CONCURRENT_STA_CHANGED_CHANNEL: + return "CONCURRENT_STA_CHANGED_CHANNEL"; + case CSA_REASON_UNSAFE_CHANNEL: + return "UNSAFE_CHANNEL"; + case CSA_REASON_LTE_COEX: + return "LTE_COEX"; + case CSA_REASON_CONCURRENT_NAN_EVENT: + return "CONCURRENT_NAN_EVENT"; + case CSA_REASON_BAND_RESTRICTED: + return "BAND_RESTRICTED"; + default: + return "UNKNOWN"; + } +} + +QDF_STATUS wlansap_set_channel_change_with_csa(struct sap_context *sap_ctx, + uint32_t target_chan_freq, + enum phy_ch_width target_bw, + bool strict) +{ + struct mac_context *mac; + mac_handle_t mac_handle; + bool valid; + QDF_STATUS status, hw_mode_status; + bool sta_sap_scc_on_dfs_chan; + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + mac_handle = MAC_HANDLE(mac); + + if (strict && !policy_mgr_is_safe_channel( + mac->psoc, target_chan_freq)) { + sap_err("%u is unsafe channel freq", target_chan_freq); + return QDF_STATUS_E_FAULT; + } + sap_nofl_debug("SAP CSA: %d ---> %d conn on 5GHz:%d, csa_reason:%s(%d) strict %d vdev %d", + sap_ctx->chan_freq, target_chan_freq, + policy_mgr_is_any_mode_active_on_band_along_with_session( + mac->psoc, sap_ctx->sessionId, POLICY_MGR_BAND_5), + sap_get_csa_reason_str(sap_ctx->csa_reason), + sap_ctx->csa_reason, strict, sap_ctx->sessionId); + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(mac->psoc); + /* + * Now, validate if the passed channel is valid in the + * current regulatory domain. + */ + if (sap_ctx->chan_freq != target_chan_freq && + ((wlan_reg_get_channel_state_for_freq(mac->pdev, target_chan_freq) == + CHANNEL_STATE_ENABLE) || + (wlan_reg_get_channel_state_for_freq(mac->pdev, target_chan_freq) == + CHANNEL_STATE_DFS && + (!policy_mgr_is_any_mode_active_on_band_along_with_session( + mac->psoc, sap_ctx->sessionId, + POLICY_MGR_BAND_5) || + sta_sap_scc_on_dfs_chan)))) { + /* + * validate target channel switch w.r.t various concurrency + * rules set. + */ + if (!strict) { + valid = wlan_sap_validate_channel_switch(mac_handle, + target_chan_freq, + sap_ctx); + if (!valid) { + sap_err("Channel freq switch to %u is not allowed due to concurrent channel interference", + target_chan_freq); + return QDF_STATUS_E_FAULT; + } + } + /* + * Post a CSA IE request to SAP state machine with + * target channel information and also CSA IE required + * flag set in sap_ctx only, if SAP is in SAP_STARTED + * state. + */ + if (sap_ctx->fsm_state == SAP_STARTED) { + status = wlansap_update_csa_channel_params(sap_ctx, + target_chan_freq); + if (status != QDF_STATUS_SUCCESS) + return status; + + hw_mode_status = + policy_mgr_check_and_set_hw_mode_for_channel_switch( + mac->psoc, sap_ctx->sessionId, + target_chan_freq, + POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_SAP); + + /* + * If hw_mode_status is QDF_STATUS_E_FAILURE, mean HW + * mode change was required but driver failed to set HW + * mode so ignore CSA for the channel. + */ + if (hw_mode_status == QDF_STATUS_E_FAILURE) { + sap_err("HW change required but failed to set hw mode"); + return hw_mode_status; + } + + status = policy_mgr_reset_chan_switch_complete_evt( + mac->psoc); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_check_n_start_opportunistic_timer( + mac->psoc); + return status; + } + /* + * Copy the requested target channel + * to sap context. + */ + mac->sap.SapDfsInfo.target_chan_freq = target_chan_freq; + mac->sap.SapDfsInfo.new_ch_params.ch_width = + mac->sap.SapDfsInfo.new_chanWidth; + + /* By this time, the best bandwidth is calculated for + * the given target channel. Now, if there was a + * request from user to move to a selected bandwidth, + * we can see if it can be honored. + * + * Ex1: BW80 was selected for the target channel and + * user wants BW40, it can be allowed + * Ex2: BW40 was selected for the target channel and + * user wants BW80, it cannot be allowed for the given + * target channel. + * + * So, the MIN of the selected channel bandwidth and + * user input is used for the bandwidth + */ + if (target_bw != CH_WIDTH_MAX) { + sap_nofl_debug("SAP CSA: target bw:%d new width:%d", + target_bw, + mac->sap.SapDfsInfo. + new_ch_params.ch_width); + mac->sap.SapDfsInfo.new_ch_params.ch_width = + mac->sap.SapDfsInfo.new_chanWidth = + QDF_MIN(mac->sap.SapDfsInfo. + new_ch_params.ch_width, + target_bw); + } + wlan_reg_set_channel_params_for_freq(mac->pdev, target_chan_freq, + 0, &mac->sap.SapDfsInfo.new_ch_params); + /* + * Set the CSA IE required flag. + */ + mac->sap.SapDfsInfo.csaIERequired = true; + + /* + * Set the radar found status to allow the channel + * change to happen same as in the case of a radar + * detection. Since, this will allow SAP to be in + * correct state and also resume the netif queues + * that were suspended in HDD before the channel + * request was issued. + */ + mac->sap.SapDfsInfo.sap_radar_found_status = true; + mac->sap.SapDfsInfo.cac_state = + eSAP_DFS_DO_NOT_SKIP_CAC; + sap_cac_reset_notify(mac_handle); + + /* + * If hw_mode_status is QDF_STATUS_SUCCESS mean HW mode + * change was required and was successfully requested so + * the channel switch will continue after HW mode change + * completion. + */ + if (QDF_IS_STATUS_SUCCESS(hw_mode_status)) { + sap_info("Channel change will continue after HW mode change"); + return QDF_STATUS_SUCCESS; + } + /* + * If hw_mode_status is QDF_STATUS_E_NOSUPPORT or + * QDF_STATUS_E_ALREADY (not QDF_STATUS_E_FAILURE and + * not QDF_STATUS_SUCCESS), mean DBS is not supported or + * required HW mode is already set, So contunue with + * CSA from here. + */ + sap_start_csa_restart(mac, sap_ctx); + } else { + sap_err("Failed to request Channel Change, since SAP is not in SAP_STARTED state"); + return QDF_STATUS_E_FAULT; + } + + } else { + sap_err("Channel freq = %d is not valid in the current" + "regulatory domain", target_chan_freq); + + return QDF_STATUS_E_FAULT; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_sap_getstation_ie_information(struct sap_context *sap_ctx, + uint32_t *len, uint8_t *buf) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + uint32_t ie_len = 0; + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + if (len) { + ie_len = *len; + *len = sap_ctx->nStaWPARSnReqIeLength; + sap_info("WPAIE len : %x", *len); + if ((buf) && (ie_len >= sap_ctx->nStaWPARSnReqIeLength)) { + qdf_mem_copy(buf, + sap_ctx->pStaWpaRsnReqIE, + sap_ctx->nStaWPARSnReqIeLength); + sap_info("WPAIE: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(buf)); + qdf_status = QDF_STATUS_SUCCESS; + } + } + return qdf_status; +} + +QDF_STATUS wlan_sap_update_next_channel(struct sap_context *sap_ctx, + uint8_t channel, + enum phy_ch_width chan_bw) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + sap_ctx->dfs_vendor_channel = channel; + sap_ctx->dfs_vendor_chan_bw = chan_bw; + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_SAP_COND_CHAN_SWITCH +QDF_STATUS wlan_sap_set_pre_cac_status(struct sap_context *sap_ctx, + bool status) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + sap_ctx->is_pre_cac_on = status; + sap_debug("is_pre_cac_on:%d", sap_ctx->is_pre_cac_on); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_sap_set_chan_before_pre_cac(struct sap_context *sap_ctx, + uint8_t chan_before_pre_cac) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + sap_ctx->chan_before_pre_cac = chan_before_pre_cac; + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_SAP_COND_CHAN_SWITCH */ + +QDF_STATUS wlan_sap_set_pre_cac_complete_status(struct sap_context *sap_ctx, + bool status) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + sap_ctx->pre_cac_complete = status; + + sap_debug("pre cac complete status:%d session:%d", + status, sap_ctx->sessionId); + + return QDF_STATUS_SUCCESS; +} + +bool wlan_sap_is_pre_cac_context(struct sap_context *context) +{ + return context && context->is_pre_cac_on; +} + +/** + * wlan_sap_is_pre_cac_active() - Checks if pre cac in in progress + * @handle: Global MAC handle + * + * Checks if pre cac is in progress in any of the SAP contexts + * + * Return: True is pre cac is active, false otherwise + */ +bool wlan_sap_is_pre_cac_active(mac_handle_t handle) +{ + struct mac_context *mac = NULL; + struct sap_ctx_list *ctx_list; + int i; + + mac = MAC_CONTEXT(handle); + if (!mac) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Invalid mac context", __func__); + return false; + } + + ctx_list = mac->sap.sapCtxList; + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + if (wlan_sap_is_pre_cac_context(ctx_list[i].sap_context)) + return true; + } + + return false; +} + +/** + * wlan_sap_get_pre_cac_vdev_id() - Get vdev id of the pre cac interface + * @handle: Global handle + * @vdev_id: vdev id + * + * Fetches the vdev id of the pre cac interface + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_sap_get_pre_cac_vdev_id(mac_handle_t handle, uint8_t *vdev_id) +{ + struct mac_context *mac = NULL; + uint8_t i; + + mac = MAC_CONTEXT(handle); + if (!mac) { + QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Invalid mac context", __func__); + return QDF_STATUS_E_FAULT; + } + + for (i = 0; i < SAP_MAX_NUM_SESSION; i++) { + struct sap_context *context = + mac->sap.sapCtxList[i].sap_context; + if (context && context->is_pre_cac_on) { + *vdev_id = i; + return QDF_STATUS_SUCCESS; + } + } + return QDF_STATUS_E_FAILURE; +} + +void wlansap_get_sec_channel(uint8_t sec_ch_offset, + uint32_t op_chan_freq, + uint32_t *sec_chan_freq) +{ + switch (sec_ch_offset) { + case LOW_PRIMARY_CH: + *sec_chan_freq = op_chan_freq + 20; + break; + case HIGH_PRIMARY_CH: + *sec_chan_freq = op_chan_freq - 20; + break; + default: + *sec_chan_freq = 0; + } +} + +static void +wlansap_set_cac_required_for_chan(struct mac_context *mac_ctx, + struct sap_context *sap_ctx) +{ + bool is_ch_dfs = false; + bool cac_required; + uint32_t chan_freq; + uint8_t vdev_id_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t freq_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t sta_cnt, i; + + chan_freq = sap_ctx->chan_freq; + if (sap_ctx->ch_params.ch_width == CH_WIDTH_160MHZ) { + is_ch_dfs = true; + } else if (sap_ctx->ch_params.ch_width == CH_WIDTH_80P80MHZ) { + if (wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + sap_ctx->chan_freq) == + CHANNEL_STATE_DFS || + wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + sap_ctx->ch_params.mhz_freq_seg1) == + CHANNEL_STATE_DFS) + is_ch_dfs = true; + } else { + if (wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + sap_ctx->chan_freq) == + CHANNEL_STATE_DFS) + is_ch_dfs = true; + } + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(sap_ctx->chan_freq)) + is_ch_dfs = false; + + sap_debug("vdev id %d chan %d is_ch_dfs %d pre_cac_complete %d ignore_cac %d cac_state %d", + sap_ctx->sessionId, chan_freq, is_ch_dfs, + sap_ctx->pre_cac_complete, mac_ctx->sap.SapDfsInfo.ignore_cac, + mac_ctx->sap.SapDfsInfo.cac_state); + + if (!is_ch_dfs || sap_ctx->pre_cac_complete || + mac_ctx->sap.SapDfsInfo.ignore_cac || + (mac_ctx->sap.SapDfsInfo.cac_state == eSAP_DFS_SKIP_CAC)) + cac_required = false; + else + cac_required = true; + + if (cac_required) { + sta_cnt = + policy_mgr_get_mode_specific_conn_info(mac_ctx->psoc, + freq_list, + vdev_id_list, + PM_STA_MODE); + + for (i = 0; i < sta_cnt; i++) { + if (sap_ctx->chan_freq == freq_list[i]) { + sap_debug("STA vdev id %d exists, ignore CAC", + vdev_id_list[i]); + cac_required = false; + } + } + } + + mlme_set_cac_required(sap_ctx->vdev, cac_required); +} + +QDF_STATUS wlansap_channel_change_request(struct sap_context *sap_ctx, + uint32_t target_chan_freq) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx; + eCsrPhyMode phy_mode; + struct ch_params *ch_params; + + if (!target_chan_freq) { + sap_err("channel 0 requested"); + return QDF_STATUS_E_FAULT; + } + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + mac_ctx = sap_get_mac_context(); + if (!mac_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + phy_mode = sap_ctx->csr_roamProfile.phyMode; + + /* Update phy_mode if the target channel is in the other band */ + if (WLAN_REG_IS_5GHZ_CH_FREQ(target_chan_freq) && + ((phy_mode == eCSR_DOT11_MODE_11g) || + (phy_mode == eCSR_DOT11_MODE_11g_ONLY))) + phy_mode = eCSR_DOT11_MODE_11a; + else if (WLAN_REG_IS_24GHZ_CH_FREQ(target_chan_freq) && + (phy_mode == eCSR_DOT11_MODE_11a)) + phy_mode = eCSR_DOT11_MODE_11g; + + sap_ctx->csr_roamProfile.phyMode = phy_mode; + + if (sap_ctx->csr_roamProfile.ChannelInfo.numOfChannels == 0 || + !sap_ctx->csr_roamProfile.ChannelInfo.freq_list) { + sap_err("Invalid channel list"); + return QDF_STATUS_E_FAULT; + } + sap_ctx->csr_roamProfile.ChannelInfo.freq_list[0] = target_chan_freq; + + /* + * We are getting channel bonding mode from sapDfsInfor structure + * because we've implemented channel width fallback mechanism for DFS + * which will result in channel width changing dynamically. + */ + ch_params = &mac_ctx->sap.SapDfsInfo.new_ch_params; + wlan_reg_set_channel_params_for_freq(mac_ctx->pdev, target_chan_freq, + 0, ch_params); + sap_ctx->ch_params = *ch_params; + sap_ctx->freq_before_ch_switch = sap_ctx->chan_freq; + /* Update the channel as this will be used to + * send event to supplicant + */ + sap_ctx->chan_freq = target_chan_freq; + wlansap_get_sec_channel(ch_params->sec_ch_offset, sap_ctx->chan_freq, + &sap_ctx->sec_ch_freq); + sap_ctx->csr_roamProfile.ch_params = *ch_params; + sap_dfs_set_current_channel(sap_ctx); + wlansap_set_cac_required_for_chan(mac_ctx, sap_ctx); + + status = sme_roam_channel_change_req(MAC_HANDLE(mac_ctx), + sap_ctx->bssid, + ch_params, + &sap_ctx->csr_roamProfile); + + sap_debug("chan_freq:%d phy_mode %d width:%d offset:%d seg0:%d seg1:%d", + sap_ctx->chan_freq, phy_mode, ch_params->ch_width, + ch_params->sec_ch_offset, ch_params->center_freq_seg0, + ch_params->center_freq_seg1); + + return status; +} + +QDF_STATUS wlansap_start_beacon_req(struct sap_context *sap_ctx) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t dfs_cac_wait_status; + struct mac_context *mac; + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + /* No Radar was found during CAC WAIT, So start Beaconing */ + if (mac->sap.SapDfsInfo.sap_radar_found_status == false) { + /* CAC Wait done without any Radar Detection */ + dfs_cac_wait_status = true; + sap_ctx->pre_cac_complete = false; + status = sme_roam_start_beacon_req(MAC_HANDLE(mac), + sap_ctx->bssid, + dfs_cac_wait_status); + } + + return status; +} + +QDF_STATUS wlansap_dfs_send_csa_ie_request(struct sap_context *sap_ctx) +{ + struct mac_context *mac; + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + mac->sap.SapDfsInfo.new_ch_params.ch_width = + mac->sap.SapDfsInfo.new_chanWidth; + wlan_reg_set_channel_params_for_freq(mac->pdev, + mac->sap.SapDfsInfo.target_chan_freq, + 0, &mac->sap.SapDfsInfo.new_ch_params); + + sap_debug("chan freq:%d req:%d width:%d off:%d", + mac->sap.SapDfsInfo.target_chan_freq, + mac->sap.SapDfsInfo.csaIERequired, + mac->sap.SapDfsInfo.new_ch_params.ch_width, + mac->sap.SapDfsInfo.new_ch_params.sec_ch_offset); + + return sme_roam_csa_ie_request(MAC_HANDLE(mac), + sap_ctx->bssid, + mac->sap.SapDfsInfo.target_chan_freq, + mac->sap.SapDfsInfo.csaIERequired, + &mac->sap.SapDfsInfo.new_ch_params); +} + +QDF_STATUS wlansap_get_dfs_ignore_cac(mac_handle_t mac_handle, + uint8_t *ignore_cac) +{ + struct mac_context *mac = NULL; + + if (mac_handle) { + mac = MAC_CONTEXT(mac_handle); + } else { + sap_err("Invalid mac_handle pointer"); + return QDF_STATUS_E_FAULT; + } + + *ignore_cac = mac->sap.SapDfsInfo.ignore_cac; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlansap_set_dfs_ignore_cac(mac_handle_t mac_handle, + uint8_t ignore_cac) +{ + struct mac_context *mac = NULL; + + if (mac_handle) { + mac = MAC_CONTEXT(mac_handle); + } else { + sap_err("Invalid mac_handle pointer"); + return QDF_STATUS_E_FAULT; + } + + mac->sap.SapDfsInfo.ignore_cac = (ignore_cac >= true) ? + true : false; + return QDF_STATUS_SUCCESS; +} + +bool sap_is_auto_channel_select(struct sap_context *sapcontext) +{ + if (!sapcontext) { + sap_err("Invalid SAP pointer"); + return false; + } + return sapcontext->chan_freq == AUTO_CHANNEL_SELECT; +} + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +/** + * wlan_sap_set_channel_avoidance() - sets sap mcc channel avoidance ini param + * @mac_handle: Opaque handle to the global MAC context + * @sap_channel_avoidance: ini parameter value + * + * sets sap mcc channel avoidance ini param, to be called in sap_start + * + * Return: success of failure of operation + */ +QDF_STATUS +wlan_sap_set_channel_avoidance(mac_handle_t mac_handle, + bool sap_channel_avoidance) +{ + struct mac_context *mac_ctx = NULL; + + if (mac_handle) { + mac_ctx = MAC_CONTEXT(mac_handle); + } else { + sap_err("mac_handle or mac_ctx pointer NULL"); + return QDF_STATUS_E_FAULT; + } + mac_ctx->sap.sap_channel_avoidance = sap_channel_avoidance; + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +QDF_STATUS +wlan_sap_set_acs_with_more_param(mac_handle_t mac_handle, + bool acs_with_more_param) +{ + struct mac_context *mac_ctx; + + if (mac_handle) { + mac_ctx = MAC_CONTEXT(mac_handle); + } else { + sap_err("mac_handle or mac_ctx pointer NULL"); + return QDF_STATUS_E_FAULT; + } + mac_ctx->sap.acs_with_more_param = acs_with_more_param; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlansap_set_dfs_preferred_channel_location(mac_handle_t mac_handle) +{ + struct mac_context *mac = NULL; + QDF_STATUS status; + enum dfs_reg dfs_region; + uint8_t dfs_preferred_channels_location = 0; + + if (mac_handle) { + mac = MAC_CONTEXT(mac_handle); + } else { + sap_err("Invalid mac_handle pointer"); + return QDF_STATUS_E_FAULT; + } + + wlan_reg_get_dfs_region(mac->pdev, &dfs_region); + + /* + * The Indoor/Outdoor only random channel selection + * restriction is currently enforeced only for + * JAPAN regulatory domain. + */ + ucfg_mlme_get_pref_chan_location(mac->psoc, + &dfs_preferred_channels_location); + sap_debug("dfs_preferred_channels_location %d", + dfs_preferred_channels_location); + + if (DFS_MKK_REGION == dfs_region) { + mac->sap.SapDfsInfo.sap_operating_chan_preferred_location = + dfs_preferred_channels_location; + QDF_TRACE(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_INFO_LOW, + FL + ("sapdfs:Set Preferred Operating Channel location=%d"), + mac->sap.SapDfsInfo. + sap_operating_chan_preferred_location); + + status = QDF_STATUS_SUCCESS; + } else { + sap_debug("sapdfs:NOT JAPAN REG, Invalid Set preferred chans location"); + + status = QDF_STATUS_E_FAULT; + } + + return status; +} + +QDF_STATUS wlansap_set_dfs_target_chnl(mac_handle_t mac_handle, + uint32_t target_chan_freq) +{ + struct mac_context *mac = NULL; + + if (mac_handle) { + mac = MAC_CONTEXT(mac_handle); + } else { + sap_err("Invalid mac_handle pointer"); + return QDF_STATUS_E_FAULT; + } + if (target_chan_freq > 0) { + mac->sap.SapDfsInfo.user_provided_target_chan_freq = + target_chan_freq; + } else { + mac->sap.SapDfsInfo.user_provided_target_chan_freq = 0; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlansap_update_sap_config_add_ie(struct sap_config *config, + const uint8_t *pAdditionIEBuffer, + uint16_t additionIELength, + eUpdateIEsType updateType) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t bufferValid = false; + uint16_t bufferLength = 0; + uint8_t *pBuffer = NULL; + + if (!config) { + return QDF_STATUS_E_FAULT; + } + + if ((pAdditionIEBuffer) && (additionIELength != 0)) { + /* initialize the buffer pointer so that pe can copy */ + if (additionIELength > 0) { + bufferLength = additionIELength; + pBuffer = qdf_mem_malloc(bufferLength); + if (!pBuffer) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(pBuffer, pAdditionIEBuffer, bufferLength); + bufferValid = true; + sap_debug("update_type: %d", updateType); + qdf_trace_hex_dump(QDF_MODULE_ID_SAP, + QDF_TRACE_LEVEL_DEBUG, pBuffer, bufferLength); + } + } + + switch (updateType) { + case eUPDATE_IE_PROBE_BCN: + if (config->pProbeRespBcnIEsBuffer) + qdf_mem_free(config->pProbeRespBcnIEsBuffer); + if (bufferValid) { + config->probeRespBcnIEsLen = bufferLength; + config->pProbeRespBcnIEsBuffer = pBuffer; + } else { + config->probeRespBcnIEsLen = 0; + config->pProbeRespBcnIEsBuffer = NULL; + } + break; + case eUPDATE_IE_PROBE_RESP: + if (config->pProbeRespIEsBuffer) + qdf_mem_free(config->pProbeRespIEsBuffer); + if (bufferValid) { + config->probeRespIEsBufferLen = bufferLength; + config->pProbeRespIEsBuffer = pBuffer; + } else { + config->probeRespIEsBufferLen = 0; + config->pProbeRespIEsBuffer = NULL; + } + break; + case eUPDATE_IE_ASSOC_RESP: + if (config->pAssocRespIEsBuffer) + qdf_mem_free(config->pAssocRespIEsBuffer); + if (bufferValid) { + config->assocRespIEsLen = bufferLength; + config->pAssocRespIEsBuffer = pBuffer; + } else { + config->assocRespIEsLen = 0; + config->pAssocRespIEsBuffer = NULL; + } + break; + default: + sap_debug("No matching buffer type %d", updateType); + if (pBuffer) + qdf_mem_free(pBuffer); + break; + } + + return status; +} + +QDF_STATUS +wlansap_reset_sap_config_add_ie(struct sap_config *config, + eUpdateIEsType updateType) +{ + if (!config) { + sap_err("Invalid Config pointer"); + return QDF_STATUS_E_FAULT; + } + + switch (updateType) { + case eUPDATE_IE_ALL: /*only used to reset */ + case eUPDATE_IE_PROBE_RESP: + if (config->pProbeRespIEsBuffer) { + qdf_mem_free(config->pProbeRespIEsBuffer); + config->probeRespIEsBufferLen = 0; + config->pProbeRespIEsBuffer = NULL; + } + if (eUPDATE_IE_ALL != updateType) + break; + + case eUPDATE_IE_ASSOC_RESP: + if (config->pAssocRespIEsBuffer) { + qdf_mem_free(config->pAssocRespIEsBuffer); + config->assocRespIEsLen = 0; + config->pAssocRespIEsBuffer = NULL; + } + if (eUPDATE_IE_ALL != updateType) + break; + + case eUPDATE_IE_PROBE_BCN: + if (config->pProbeRespBcnIEsBuffer) { + qdf_mem_free(config->pProbeRespBcnIEsBuffer); + config->probeRespBcnIEsLen = 0; + config->pProbeRespBcnIEsBuffer = NULL; + } + if (eUPDATE_IE_ALL != updateType) + break; + + default: + if (eUPDATE_IE_ALL != updateType) + sap_err("Invalid buffer type %d", updateType); + break; + } + return QDF_STATUS_SUCCESS; +} + +#define ACS_WLAN_20M_CH_INC 20 +#define ACS_2G_EXTEND ACS_WLAN_20M_CH_INC +#define ACS_5G_EXTEND (ACS_WLAN_20M_CH_INC * 3) + +#ifdef CONFIG_BAND_6GHZ +static void wlansap_update_start_range_6ghz( + uint32_t *start_ch_freq, uint32_t *bandStartChannel) +{ + *bandStartChannel = CHAN_ENUM_5945; + *start_ch_freq = (*start_ch_freq - ACS_5G_EXTEND) > + wlan_reg_ch_to_freq(CHAN_ENUM_5945) ? + (*start_ch_freq - ACS_5G_EXTEND) : + wlan_reg_ch_to_freq(CHAN_ENUM_5945); +} + +static void wlansap_update_end_range_6ghz( + uint32_t *end_ch_freq, uint32_t *bandEndChannel) +{ + *bandEndChannel = CHAN_ENUM_7105; + *end_ch_freq = (*end_ch_freq + ACS_5G_EXTEND) <= + wlan_reg_ch_to_freq(CHAN_ENUM_7105) ? + (*end_ch_freq + ACS_5G_EXTEND) : + wlan_reg_ch_to_freq(CHAN_ENUM_7105); +} +#else +static void wlansap_update_start_range_6ghz( + uint32_t *start_ch_freq, uint32_t *bandStartChannel) +{ +} + +static void wlansap_update_end_range_6ghz( + uint32_t *end_ch_freq, uint32_t *bandEndChannel) +{ +} +#endif + +/*========================================================================== + FUNCTION wlansap_extend_to_acs_range + + DESCRIPTION Function extends give channel range to consider ACS chan bonding + + DEPENDENCIES PARAMETERS + + IN /OUT + * start_ch_freq : ACS extend start ch + * end_ch_freq : ACS extended End ch + * bandStartChannel: Band start ch + * bandEndChannel : Band end ch + + RETURN VALUE NONE + + SIDE EFFECTS + ============================================================================*/ +void wlansap_extend_to_acs_range(mac_handle_t mac_handle, + uint32_t *start_ch_freq, + uint32_t *end_ch_freq, + uint32_t *bandStartChannel, + uint32_t *bandEndChannel) +{ + uint32_t tmp_start_ch_freq = 0, tmp_end_ch_freq = 0; + struct mac_context *mac_ctx; + + mac_ctx = MAC_CONTEXT(mac_handle); + if (!mac_ctx) { + sap_err("Invalid mac_ctx"); + return; + } + if (*start_ch_freq <= wlan_reg_ch_to_freq(CHAN_ENUM_2484)) { + *bandStartChannel = CHAN_ENUM_2412; + tmp_start_ch_freq = *start_ch_freq > + wlan_reg_ch_to_freq(CHAN_ENUM_2432) ? + (*start_ch_freq - ACS_2G_EXTEND) : + wlan_reg_ch_to_freq(CHAN_ENUM_2412); + } else if (*start_ch_freq <= wlan_reg_ch_to_freq(CHAN_ENUM_5865)) { + *bandStartChannel = CHAN_ENUM_5180; + tmp_start_ch_freq = (*start_ch_freq - ACS_5G_EXTEND) > + wlan_reg_ch_to_freq(CHAN_ENUM_5180) ? + (*start_ch_freq - ACS_5G_EXTEND) : + wlan_reg_ch_to_freq(CHAN_ENUM_5180); + } else if (WLAN_REG_IS_6GHZ_CHAN_FREQ(*start_ch_freq)) { + tmp_start_ch_freq = *start_ch_freq; + wlansap_update_start_range_6ghz(&tmp_start_ch_freq, + bandStartChannel); + } else { + *bandStartChannel = CHAN_ENUM_2412; + tmp_start_ch_freq = *start_ch_freq > + wlan_reg_ch_to_freq(CHAN_ENUM_2432) ? + (*start_ch_freq - ACS_2G_EXTEND) : + wlan_reg_ch_to_freq(CHAN_ENUM_2412); + sap_err("unexpected start freq %d", + *start_ch_freq); + } + + if (*end_ch_freq <= wlan_reg_ch_to_freq(CHAN_ENUM_2484)) { + *bandEndChannel = CHAN_ENUM_2484; + tmp_end_ch_freq = (*end_ch_freq + ACS_2G_EXTEND) <= + wlan_reg_ch_to_freq(CHAN_ENUM_2484) ? + (*end_ch_freq + ACS_2G_EXTEND) : + wlan_reg_ch_to_freq(CHAN_ENUM_2484); + } else if (*end_ch_freq <= wlan_reg_ch_to_freq(CHAN_ENUM_5865)) { + *bandEndChannel = CHAN_ENUM_5865; + tmp_end_ch_freq = (*end_ch_freq + ACS_5G_EXTEND) <= + wlan_reg_ch_to_freq(CHAN_ENUM_5865) ? + (*end_ch_freq + ACS_5G_EXTEND) : + wlan_reg_ch_to_freq(CHAN_ENUM_5865); + } else if (WLAN_REG_IS_6GHZ_CHAN_FREQ(*end_ch_freq)) { + tmp_end_ch_freq = *end_ch_freq; + wlansap_update_end_range_6ghz(&tmp_end_ch_freq, + bandEndChannel); + } else { + *bandEndChannel = CHAN_ENUM_5865; + tmp_end_ch_freq = (*end_ch_freq + ACS_5G_EXTEND) <= + wlan_reg_ch_to_freq(CHAN_ENUM_5865) ? + (*end_ch_freq + ACS_5G_EXTEND) : + wlan_reg_ch_to_freq(CHAN_ENUM_5865); + + sap_err("unexpected end freq %d", *end_ch_freq); + } + *start_ch_freq = tmp_start_ch_freq; + *end_ch_freq = tmp_end_ch_freq; + /* Note if the ACS range include only DFS channels, do not cross range + * Active scanning in adjacent non DFS channels results in transmission + * spikes in DFS specturm channels which is due to emission spill. + * Remove the active channels from extend ACS range for DFS only range + */ + if (wlan_reg_is_dfs_for_freq(mac_ctx->pdev, *start_ch_freq)) { + while (!wlan_reg_is_dfs_for_freq( + mac_ctx->pdev, + tmp_start_ch_freq) && + tmp_start_ch_freq < *start_ch_freq) + tmp_start_ch_freq += ACS_WLAN_20M_CH_INC; + + *start_ch_freq = tmp_start_ch_freq; + } + if (wlan_reg_is_dfs_for_freq(mac_ctx->pdev, *end_ch_freq)) { + while (!wlan_reg_is_dfs_for_freq( + mac_ctx->pdev, + tmp_end_ch_freq) && + tmp_end_ch_freq > *end_ch_freq) + tmp_end_ch_freq -= ACS_WLAN_20M_CH_INC; + + *end_ch_freq = tmp_end_ch_freq; + } +} + +QDF_STATUS wlan_sap_set_vendor_acs(struct sap_context *sap_context, + bool is_vendor_acs) +{ + if (!sap_context) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + sap_context->vendor_acs_dfs_lte_enabled = is_vendor_acs; + + return QDF_STATUS_SUCCESS; +} + +#ifdef DFS_COMPONENT_ENABLE +QDF_STATUS wlansap_set_dfs_nol(struct sap_context *sap_ctx, + eSapDfsNolType conf) +{ + struct mac_context *mac; + + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + if (conf == eSAP_DFS_NOL_CLEAR) { + struct wlan_objmgr_pdev *pdev; + + sap_err("clear the DFS NOL"); + + pdev = mac->pdev; + if (!pdev) { + sap_err("null pdev"); + return QDF_STATUS_E_FAULT; + } + utils_dfs_clear_nol_channels(pdev); + } else if (conf == eSAP_DFS_NOL_RANDOMIZE) { + sap_err("Randomize the DFS NOL"); + + } else { + sap_err("unsupport type %d", conf); + } + + return QDF_STATUS_SUCCESS; +} +#endif + +void wlansap_populate_del_sta_params(const uint8_t *mac, + uint16_t reason_code, + uint8_t subtype, + struct csr_del_sta_params *params) +{ + if (!mac) + qdf_set_macaddr_broadcast(¶ms->peerMacAddr); + else + qdf_mem_copy(params->peerMacAddr.bytes, mac, + QDF_MAC_ADDR_SIZE); + + if (reason_code == 0) + params->reason_code = eSIR_MAC_DEAUTH_LEAVING_BSS_REASON; + else + params->reason_code = reason_code; + + if (subtype == SIR_MAC_MGMT_DEAUTH || subtype == SIR_MAC_MGMT_DISASSOC) + params->subtype = subtype; + else + params->subtype = SIR_MAC_MGMT_DEAUTH; + + sap_debug("Delete STA with RC:%hu subtype:%hhu MAC::" QDF_MAC_ADDR_FMT, + params->reason_code, params->subtype, + QDF_MAC_ADDR_REF(params->peerMacAddr.bytes)); +} + +void sap_undo_acs(struct sap_context *sap_ctx, struct sap_config *sap_cfg) +{ + struct sap_acs_cfg *acs_cfg; + + if (!sap_ctx) + return; + + acs_cfg = &sap_cfg->acs_cfg; + if (!acs_cfg) + return; + + if (acs_cfg->freq_list) { + sap_debug("Clearing ACS cfg ch freq list"); + qdf_mem_free(acs_cfg->freq_list); + acs_cfg->freq_list = NULL; + } + if (acs_cfg->master_freq_list) { + sap_debug("Clearing master ACS cfg chan freq list"); + qdf_mem_free(acs_cfg->master_freq_list); + acs_cfg->master_freq_list = NULL; + } + if (sap_ctx->freq_list) { + sap_debug("Clearing sap context ch freq list"); + qdf_mem_free(sap_ctx->freq_list); + sap_ctx->freq_list = NULL; + } + acs_cfg->ch_list_count = 0; + acs_cfg->master_ch_list_count = 0; + acs_cfg->acs_mode = false; + sap_ctx->num_of_channel = 0; +} + +QDF_STATUS wlansap_acs_chselect(struct sap_context *sap_context, + sap_event_cb acs_event_callback, + struct sap_config *config, + void *pusr_context) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct mac_context *mac; + + if (!sap_context) { + sap_err("Invalid SAP pointer"); + + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + sap_context->acs_cfg = &config->acs_cfg; + sap_context->ch_width_orig = config->acs_cfg.ch_width; + sap_context->csr_roamProfile.phyMode = config->acs_cfg.hw_mode; + + /* + * Now, configure the scan and ACS channel params + * to issue a scan request. + */ + wlansap_set_scan_acs_channel_params(config, sap_context, + pusr_context); + + /* + * Copy the HDD callback function to report the + * ACS result after scan in SAP context callback function. + */ + sap_context->sap_event_cb = acs_event_callback; + /* + * init dfs channel nol + */ + sap_init_dfs_channel_nol_list(sap_context); + + /* + * Issue the scan request. This scan request is + * issued before the start BSS is done so + * + * 1. No need to pass the second parameter + * as the SAP state machine is not started yet + * and there is no need for any event posting. + * + * 2. Set third parameter to TRUE to indicate the + * channel selection function to register a + * different scan callback function to process + * the results pre start BSS. + */ + qdf_status = sap_channel_sel(sap_context); + + if (QDF_STATUS_E_ABORTED == qdf_status) { + sap_err("DFS not supported in the current operating mode"); + return QDF_STATUS_E_FAILURE; + } else if (QDF_STATUS_E_CANCELED == qdf_status) { + /* + * ERROR is returned when either the SME scan request + * failed or ACS is overridden due to other constrainst + * So send selected channel to HDD + */ + sap_err("Scan Req Failed/ACS Overridden"); + sap_err("Selected channel frequency = %d", + sap_context->chan_freq); + + return sap_signal_hdd_event(sap_context, NULL, + eSAP_ACS_CHANNEL_SELECTED, + (void *) eSAP_STATUS_SUCCESS); + } + + return qdf_status; +} + +/** + * wlan_sap_enable_phy_error_logs() - Enable DFS phy error logs + * @mac_handle: Opaque handle to the global MAC context + * @enable_log: value to set + * + * Since the frequency of DFS phy error is very high, enabling logs for them + * all the times can cause crash and will also create lot of useless logs + * causing difficulties in debugging other issue. This function will be called + * from iwpriv cmd to enable such logs temporarily. + * + * Return: void + */ +void wlan_sap_enable_phy_error_logs(mac_handle_t mac_handle, + uint32_t enable_log) +{ + int error; + + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + mac_ctx->sap.enable_dfs_phy_error_logs = !!enable_log; + tgt_dfs_control(mac_ctx->pdev, DFS_SET_DEBUG_LEVEL, &enable_log, + sizeof(uint32_t), NULL, NULL, &error); +} + +#ifdef DFS_PRI_MULTIPLIER +void wlan_sap_set_dfs_pri_multiplier(mac_handle_t mac_handle) +{ + int error; + + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + tgt_dfs_control(mac_ctx->pdev, DFS_SET_PRI_MULTIPILER, + &mac_ctx->mlme_cfg->dfs_cfg.dfs_pri_multiplier, + sizeof(uint32_t), NULL, NULL, &error); +} +#endif + +uint32_t wlansap_get_chan_width(struct sap_context *sap_ctx) +{ + return wlan_sap_get_vht_ch_width(sap_ctx); +} + +QDF_STATUS wlansap_set_invalid_session(struct sap_context *sap_ctx) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAILURE; + } + + sap_ctx->sessionId = WLAN_UMAC_VDEV_ID_MAX; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlansap_release_vdev_ref(struct sap_context *sap_ctx) +{ + if (!sap_ctx) { + sap_err("Invalid SAP pointer"); + return QDF_STATUS_E_FAILURE; + } + + sap_release_vdev_ref(sap_ctx); + + return QDF_STATUS_SUCCESS; +} + +void wlansap_cleanup_cac_timer(struct sap_context *sap_ctx) +{ + struct mac_context *mac; + + if (!sap_ctx) { + sap_debug("Invalid SAP context"); + return; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return; + } + + if (mac->sap.SapDfsInfo.is_dfs_cac_timer_running) { + qdf_mc_timer_stop(&mac->sap.SapDfsInfo. + sap_dfs_cac_timer); + mac->sap.SapDfsInfo.is_dfs_cac_timer_running = 0; + qdf_mc_timer_destroy( + &mac->sap.SapDfsInfo.sap_dfs_cac_timer); + sap_err("sapdfs, force cleanup running dfs cac timer"); + } +} + +#define DH_OUI_TYPE (0x20) +/** + * wlansap_validate_owe_ie() - validate OWE IE + * @ie: IE buffer + * @remaining_ie_len: remaining IE length + * + * Return: validated IE length, negative for failure + */ +static int wlansap_validate_owe_ie(const uint8_t *ie, uint32_t remaining_ie_len) +{ + uint8_t ie_id, ie_len, ie_ext_id = 0; + + if (remaining_ie_len < 2) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "IE too short"); + return -EINVAL; + } + + ie_id = ie[0]; + ie_len = ie[1]; + + /* IEs that we are expecting in OWE IEs + * - RSN IE + * - DH IE + */ + switch (ie_id) { + case DOT11F_EID_RSN: + if (ie_len < DOT11F_IE_RSN_MIN_LEN || + ie_len > DOT11F_IE_RSN_MAX_LEN) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, + "Invalid RSN IE len %d", ie_len); + return -EINVAL; + } + ie_len += 2; + break; + case DOT11F_EID_DH_PARAMETER_ELEMENT: + ie_ext_id = ie[2]; + if (ie_ext_id != DH_OUI_TYPE) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, + "Invalid DH IE ID %d", ie_ext_id); + return -EINVAL; + } + if (ie_len < DOT11F_IE_DH_PARAMETER_ELEMENT_MIN_LEN || + ie_len > DOT11F_IE_DH_PARAMETER_ELEMENT_MAX_LEN) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, + "Invalid DH IE len %d", ie_len); + return -EINVAL; + } + ie_len += 2; + break; + default: + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid IE %d", ie_id); + return -EINVAL; + } + + if (ie_len > remaining_ie_len) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid IE len"); + return -EINVAL; + } + + return ie_len; +} + +/** + * wlansap_validate_owe_ies() - validate OWE IEs + * @ie: IE buffer + * @ie_len: IE length + * + * Return: true if validated + */ +static bool wlansap_validate_owe_ies(const uint8_t *ie, uint32_t ie_len) +{ + const uint8_t *remaining_ie = ie; + uint32_t remaining_ie_len = ie_len; + int validated_len; + bool validated = true; + + while (remaining_ie_len) { + validated_len = wlansap_validate_owe_ie(remaining_ie, + remaining_ie_len); + if (validated_len < 0) { + validated = false; + break; + } + remaining_ie += validated_len; + remaining_ie_len -= validated_len; + } + + return validated; +} + +QDF_STATUS wlansap_update_owe_info(struct sap_context *sap_ctx, + uint8_t *peer, const uint8_t *ie, + uint32_t ie_len, uint16_t owe_status) +{ + struct mac_context *mac; + struct owe_assoc_ind *owe_assoc_ind; + struct assoc_ind *assoc_ind = NULL; + qdf_list_node_t *node = NULL, *next_node = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!wlansap_validate_owe_ies(ie, ie_len)) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid OWE IE"); + return QDF_STATUS_E_FAULT; + } + + if (!sap_ctx) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid SAP context"); + return QDF_STATUS_E_FAULT; + } + + mac = sap_get_mac_context(); + if (!mac) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, "Invalid MAC context"); + return QDF_STATUS_E_FAULT; + } + + if (QDF_STATUS_SUCCESS != + qdf_list_peek_front(&sap_ctx->owe_pending_assoc_ind_list, + &next_node)) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, + "Failed to find assoc ind list"); + return QDF_STATUS_E_FAILURE; + } + + do { + node = next_node; + owe_assoc_ind = qdf_container_of(node, struct owe_assoc_ind, + node); + if (qdf_mem_cmp(peer, + owe_assoc_ind->assoc_ind->peerMacAddr, + QDF_MAC_ADDR_SIZE) == 0) { + status = qdf_list_remove_node( + &sap_ctx->owe_pending_assoc_ind_list, + node); + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SAP, + "Failed to remove assoc ind"); + return status; + } + assoc_ind = owe_assoc_ind->assoc_ind; + qdf_mem_free(owe_assoc_ind); + break; + } + } while (QDF_STATUS_SUCCESS == + qdf_list_peek_next(&sap_ctx->owe_pending_assoc_ind_list, + node, &next_node)); + + if (assoc_ind) { + assoc_ind->owe_ie = ie; + assoc_ind->owe_ie_len = ie_len; + assoc_ind->owe_status = owe_status; + status = sme_update_owe_info(mac, assoc_ind); + qdf_mem_free(assoc_ind); + } + + return status; +} + +bool wlansap_is_channel_present_in_acs_list(uint32_t freq, + uint32_t *ch_freq_list, + uint8_t ch_count) +{ + uint8_t i; + + for(i = 0; i < ch_count; i++) + if (ch_freq_list[i] == freq) + return true; + + return false; +} + +QDF_STATUS wlansap_filter_ch_based_acs(struct sap_context *sap_ctx, + uint32_t *ch_freq_list, + uint32_t *ch_cnt) +{ + size_t ch_index; + size_t target_ch_cnt = 0; + + if (!sap_ctx || !ch_freq_list || !ch_cnt || + !sap_ctx->acs_cfg->master_freq_list || + !sap_ctx->acs_cfg->master_ch_list_count) { + sap_err("NULL parameters"); + return QDF_STATUS_E_FAULT; + } + + for (ch_index = 0; ch_index < *ch_cnt; ch_index++) { + if (wlansap_is_channel_present_in_acs_list( + ch_freq_list[ch_index], + sap_ctx->acs_cfg->master_freq_list, + sap_ctx->acs_cfg->master_ch_list_count)) + ch_freq_list[target_ch_cnt++] = ch_freq_list[ch_index]; + } + + *ch_cnt = target_ch_cnt; + + return QDF_STATUS_SUCCESS; +} + +bool wlansap_is_6ghz_included_in_acs_range(struct sap_context *sap_ctx) +{ + uint32_t i; + uint32_t *ch_freq_list; + + if (!sap_ctx || !sap_ctx->acs_cfg || + !sap_ctx->acs_cfg->master_freq_list || + !sap_ctx->acs_cfg->master_ch_list_count) { + sap_err("NULL parameters"); + return false; + } + ch_freq_list = sap_ctx->acs_cfg->master_freq_list; + for (i = 0; i < sap_ctx->acs_cfg->master_ch_list_count; i++) { + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(ch_freq_list[i])) + return true; + } + return false; +} + +#if defined(FEATURE_WLAN_CH_AVOID) +/** + * wlansap_get_safe_channel() - Get safe channel from current regulatory + * @sap_ctx: Pointer to SAP context + * + * This function is used to get safe channel from current regulatory valid + * channels to restart SAP if failed to get safe channel from PCL. + * + * Return: Chan freq num to restart SAP in case of success. In case of any + * failure, the channel number returned is zero. + */ +static uint32_t +wlansap_get_safe_channel(struct sap_context *sap_ctx) +{ + struct mac_context *mac; + uint32_t pcl_freqs[NUM_CHANNELS]; + QDF_STATUS status; + mac_handle_t mac_handle; + uint32_t pcl_len = 0; + + if (!sap_ctx) { + sap_err("NULL parameter"); + return INVALID_CHANNEL_ID; + } + + mac = sap_get_mac_context(); + if (!mac) { + sap_err("Invalid MAC context"); + return INVALID_CHANNEL_ID; + } + mac_handle = MAC_HANDLE(mac); + + /* get the channel list for current domain */ + status = policy_mgr_get_valid_chans(mac->psoc, pcl_freqs, &pcl_len); + if (QDF_IS_STATUS_ERROR(status)) { + sap_err("Error in getting valid channels"); + return INVALID_CHANNEL_ID; + } + + status = wlansap_filter_ch_based_acs(sap_ctx, pcl_freqs, &pcl_len); + + if (QDF_IS_STATUS_ERROR(status)) { + sap_err("failed to filter ch from acs %d", status); + return INVALID_CHANNEL_ID; + } + + if (pcl_len) { + status = policy_mgr_get_valid_chans_from_range(mac->psoc, + pcl_freqs, + &pcl_len, + PM_SAP_MODE); + if (QDF_IS_STATUS_ERROR(status)) { + sap_err("failed to get valid channel: %d", status); + return INVALID_CHANNEL_ID; + } + + if (pcl_len) { + sap_debug("select %d from valid channel list", + pcl_freqs[0]); + return pcl_freqs[0]; + } + } + + return INVALID_CHANNEL_ID; +} +#else +/** + * wlansap_get_safe_channel() - Get safe channel from current regulatory + * @sap_ctx: Pointer to SAP context + * + * This function is used to get safe channel from current regulatory valid + * channels to restart SAP if failed to get safe channel from PCL. + * + * Return: Channel number to restart SAP in case of success. In case of any + * failure, the channel number returned is zero. + */ +static uint8_t +wlansap_get_safe_channel(struct sap_context *sap_ctx) +{ + return 0; +} +#endif + +uint32_t +wlansap_get_safe_channel_from_pcl_and_acs_range(struct sap_context *sap_ctx) +{ + struct mac_context *mac; + struct sir_pcl_list pcl = {0}; + uint32_t pcl_freqs[NUM_CHANNELS] = {0}; + QDF_STATUS status; + mac_handle_t mac_handle; + uint32_t pcl_len = 0; + + if (!sap_ctx) { + sap_err("NULL parameter"); + return INVALID_CHANNEL_ID; + } + + mac = sap_get_mac_context(); + if (!mac) { + sap_err("Invalid MAC context"); + return INVALID_CHANNEL_ID; + } + mac_handle = MAC_HANDLE(mac); + + if (policy_mgr_get_connection_count(mac->psoc) == 1) { + sap_debug("only SAP present return best channel from ACS list"); + return wlansap_get_safe_channel(sap_ctx); + } + + status = policy_mgr_get_pcl_for_existing_conn( + mac->psoc, PM_SAP_MODE, pcl_freqs, &pcl_len, + pcl.weight_list, QDF_ARRAY_SIZE(pcl.weight_list), + false); + if (QDF_IS_STATUS_ERROR(status)) { + sap_err("Get PCL failed"); + return INVALID_CHANNEL_ID; + } + + if (pcl_len) { + status = wlansap_filter_ch_based_acs(sap_ctx, pcl_freqs, + &pcl_len); + if (QDF_IS_STATUS_ERROR(status)) { + sap_err("failed to filter ch from acs %d", status); + return INVALID_CHANNEL_ID; + } + + if (pcl_len) { + sap_debug("select %d from valid ch freq list", + pcl_freqs[0]); + return pcl_freqs[0]; + } + sap_debug("no safe channel from PCL found in ACS range"); + } else { + sap_debug("pcl length is zero!"); + } + + /* + * In some scenarios, like hw dbs disabled, sap+sap case, if operating + * channel is unsafe channel, the pcl may be empty, instead of return, + * try to choose a safe channel from acs range. + */ + return wlansap_get_safe_channel(sap_ctx); +} + +static uint32_t wlansap_get_2g_first_safe_chan_freq(struct sap_context *sap_ctx) +{ + uint32_t i; + uint32_t freq; + enum channel_state state; + struct regulatory_channel *cur_chan_list; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_psoc *psoc; + uint32_t *acs_freq_list; + uint8_t acs_list_count; + + pdev = sap_ctx->vdev->vdev_objmgr.wlan_pdev; + psoc = pdev->pdev_objmgr.wlan_psoc; + + cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * + sizeof(struct regulatory_channel)); + if (!cur_chan_list) + return TWOG_CHAN_6_IN_MHZ; + + if (wlan_reg_get_current_chan_list(pdev, cur_chan_list) != + QDF_STATUS_SUCCESS) { + freq = TWOG_CHAN_6_IN_MHZ; + goto err; + } + + acs_freq_list = sap_ctx->acs_cfg->master_freq_list; + acs_list_count = sap_ctx->acs_cfg->master_ch_list_count; + for (i = 0; i < NUM_CHANNELS; i++) { + freq = cur_chan_list[i].center_freq; + state = wlan_reg_get_channel_state_for_freq(pdev, freq); + if (state != CHANNEL_STATE_DISABLE && + state != CHANNEL_STATE_INVALID && + wlan_reg_is_24ghz_ch_freq(freq) && + policy_mgr_is_safe_channel(psoc, freq) && + wlansap_is_channel_present_in_acs_list(freq, + acs_freq_list, + acs_list_count)) { + sap_debug("find a 2g channel: %d", freq); + goto err; + } + } + + freq = TWOG_CHAN_6_IN_MHZ; +err: + qdf_mem_free(cur_chan_list); + return freq; +} + +qdf_freq_t wlansap_get_chan_band_restrict(struct sap_context *sap_ctx) +{ + uint32_t restart_freq; + enum phy_ch_width restart_ch_width; + uint16_t intf_ch_freq; + uint32_t phy_mode; + struct mac_context *mac; + uint8_t cc_mode; + uint8_t vdev_id; + enum reg_wifi_band sap_band; + enum band_info band; + + if (!sap_ctx) { + sap_err("sap_ctx NULL parameter"); + return 0; + } + if (cds_is_driver_recovering()) + return 0; + + mac = cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + sap_err("Invalid MAC context"); + return 0; + } + + if (ucfg_reg_get_band(mac->pdev, &band) != QDF_STATUS_SUCCESS) { + sap_err("Failed to get current band config"); + return 0; + } + sap_band = wlan_reg_freq_to_band(sap_ctx->chan_freq); + sap_debug("SAP/Go current band: %d, pdev band capability: %d", + sap_band, band); + if (sap_band == REG_BAND_5G && band == BIT(REG_BAND_2G)) { + sap_ctx->chan_freq_before_switch_band = sap_ctx->chan_freq; + sap_ctx->chan_width_before_switch_band = + sap_ctx->ch_params.ch_width; + sap_debug("Save chan info before switch: %d, width: %d", + sap_ctx->chan_freq, sap_ctx->ch_params.ch_width); + restart_freq = wlansap_get_2g_first_safe_chan_freq(sap_ctx); + if (restart_freq == 0) { + sap_debug("use default chan 6"); + restart_freq = TWOG_CHAN_6_IN_MHZ; + } + restart_ch_width = sap_ctx->ch_params.ch_width; + if (restart_ch_width > CH_WIDTH_40MHZ) { + sap_debug("set 40M when switch SAP to 2G"); + restart_ch_width = CH_WIDTH_40MHZ; + } + } else if (sap_band == REG_BAND_2G && (band & BIT(REG_BAND_5G))) { + if (sap_ctx->chan_freq_before_switch_band == 0) + return 0; + restart_freq = sap_ctx->chan_freq_before_switch_band; + restart_ch_width = sap_ctx->chan_width_before_switch_band; + sap_debug("Restore chan freq: %d, width: %d", + restart_freq, restart_ch_width); + } else { + sap_debug("No need switch SAP/Go channel"); + return 0; + } + + cc_mode = sap_ctx->cc_switch_mode; + phy_mode = sap_ctx->csr_roamProfile.phyMode; + intf_ch_freq = sme_check_concurrent_channel_overlap( + MAC_HANDLE(mac), + restart_freq, + phy_mode, + cc_mode); + if (intf_ch_freq) + restart_freq = intf_ch_freq; + vdev_id = sap_ctx->vdev->vdev_objmgr.vdev_id; + sap_debug("vdev: %d, CSA target freq: %d", vdev_id, restart_freq); + + return restart_freq; +} + diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/csr_api.h b/drivers/staging/qcacld-3.0/core/sme/inc/csr_api.h new file mode 100644 index 0000000000000000000000000000000000000000..ecafada0dcd653a3ac9cab110c6b401055c4de86 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/csr_api.h @@ -0,0 +1,1536 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file csr_api.h + * + * Exports and types for the Common Scan and Roaming Module interfaces. + */ + +#ifndef CSRAPI_H__ +#define CSRAPI_H__ + +#include "sir_api.h" +#include "sir_mac_prot_def.h" +#include "csr_link_list.h" +#include "wlan_scan_public_structs.h" + +#define CSR_INVALID_SCANRESULT_HANDLE (NULL) + +enum csr_akm_type { + /* never used */ + eCSR_AUTH_TYPE_NONE, + /* MAC layer authentication types */ + eCSR_AUTH_TYPE_OPEN_SYSTEM, + eCSR_AUTH_TYPE_SHARED_KEY, + eCSR_AUTH_TYPE_SAE, + eCSR_AUTH_TYPE_AUTOSWITCH, + + /* Upper layer authentication types */ + eCSR_AUTH_TYPE_WPA, + eCSR_AUTH_TYPE_WPA_PSK, + eCSR_AUTH_TYPE_WPA_NONE, + + eCSR_AUTH_TYPE_RSN, + eCSR_AUTH_TYPE_RSN_PSK, + eCSR_AUTH_TYPE_FT_RSN, + eCSR_AUTH_TYPE_FT_RSN_PSK, +#ifdef FEATURE_WLAN_WAPI + eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE, + eCSR_AUTH_TYPE_WAPI_WAI_PSK, +#endif /* FEATURE_WLAN_WAPI */ + eCSR_AUTH_TYPE_CCKM_WPA, + eCSR_AUTH_TYPE_CCKM_RSN, + eCSR_AUTH_TYPE_RSN_PSK_SHA256, + eCSR_AUTH_TYPE_RSN_8021X_SHA256, + eCSR_AUTH_TYPE_FILS_SHA256, + eCSR_AUTH_TYPE_FILS_SHA384, + eCSR_AUTH_TYPE_FT_FILS_SHA256, + eCSR_AUTH_TYPE_FT_FILS_SHA384, + eCSR_AUTH_TYPE_DPP_RSN, + eCSR_AUTH_TYPE_OWE, + eCSR_AUTH_TYPE_SUITEB_EAP_SHA256, + eCSR_AUTH_TYPE_SUITEB_EAP_SHA384, + eCSR_AUTH_TYPE_OSEN, + eCSR_AUTH_TYPE_FT_SAE, + eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384, + eCSR_NUM_OF_SUPPORT_AUTH_TYPE, + eCSR_AUTH_TYPE_FAILED = 0xff, + eCSR_AUTH_TYPE_UNKNOWN = eCSR_AUTH_TYPE_FAILED, + +}; + +typedef enum { + eCSR_ENCRYPT_TYPE_NONE, + eCSR_ENCRYPT_TYPE_WEP40_STATICKEY, + eCSR_ENCRYPT_TYPE_WEP104_STATICKEY, + eCSR_ENCRYPT_TYPE_WEP40, + eCSR_ENCRYPT_TYPE_WEP104, + eCSR_ENCRYPT_TYPE_TKIP, + eCSR_ENCRYPT_TYPE_AES,/* CCMP */ +#ifdef FEATURE_WLAN_WAPI + /* WAPI */ + eCSR_ENCRYPT_TYPE_WPI, +#endif /* FEATURE_WLAN_WAPI */ + eCSR_ENCRYPT_TYPE_KRK, + eCSR_ENCRYPT_TYPE_BTK, + eCSR_ENCRYPT_TYPE_AES_CMAC, + eCSR_ENCRYPT_TYPE_AES_GMAC_128, + eCSR_ENCRYPT_TYPE_AES_GMAC_256, + eCSR_ENCRYPT_TYPE_AES_GCMP, + eCSR_ENCRYPT_TYPE_AES_GCMP_256, + eCSR_ENCRYPT_TYPE_ANY, + eCSR_NUM_OF_ENCRYPT_TYPE = eCSR_ENCRYPT_TYPE_ANY, + + eCSR_ENCRYPT_TYPE_FAILED = 0xff, + eCSR_ENCRYPT_TYPE_UNKNOWN = eCSR_ENCRYPT_TYPE_FAILED, + +} eCsrEncryptionType; + +typedef enum { + /* 11a/b/g only, no HT, no proprietary */ + eCSR_DOT11_MODE_abg = 0x0001, + eCSR_DOT11_MODE_11a = 0x0002, + eCSR_DOT11_MODE_11b = 0x0004, + eCSR_DOT11_MODE_11g = 0x0008, + eCSR_DOT11_MODE_11n = 0x0010, + eCSR_DOT11_MODE_11g_ONLY = 0x0020, + eCSR_DOT11_MODE_11n_ONLY = 0x0040, + eCSR_DOT11_MODE_11b_ONLY = 0x0080, + eCSR_DOT11_MODE_11ac = 0x0100, + eCSR_DOT11_MODE_11ac_ONLY = 0x0200, + /* + * This is for WIFI test. It is same as eWNIAPI_MAC_PROTOCOL_ALL + * except when it starts IBSS in 11B of 2.4GHz + * It is for CSR internal use + */ + eCSR_DOT11_MODE_AUTO = 0x0400, + eCSR_DOT11_MODE_11ax = 0x0800, + eCSR_DOT11_MODE_11ax_ONLY = 0x1000, + + /* specify the number of maximum bits for phyMode */ + eCSR_NUM_PHY_MODE = 16, +} eCsrPhyMode; + +/** + * enum eCsrRoamBssType - BSS type in CSR operations + * @eCSR_BSS_TYPE_INFRASTRUCTURE: Infrastructure station + * @eCSR_BSS_TYPE_INFRA_AP: SoftAP + * @eCSR_BSS_TYPE_IBSS: IBSS network we'll not start + * @eCSR_BSS_TYPE_START_IBSS: IBSS network we'll start if no partners found + * @eCSR_BSS_TYPE_NDI: NAN datapath interface + * @eCSR_BSS_TYPE_ANY: any BSS type (IBSS or Infrastructure) + */ +typedef enum { + eCSR_BSS_TYPE_INFRASTRUCTURE, + eCSR_BSS_TYPE_INFRA_AP, + eCSR_BSS_TYPE_IBSS, + eCSR_BSS_TYPE_START_IBSS, + eCSR_BSS_TYPE_NDI, + eCSR_BSS_TYPE_ANY, +} eCsrRoamBssType; + +typedef enum { + eCSR_SCAN_SUCCESS, + eCSR_SCAN_FAILURE, + eCSR_SCAN_ABORT, + eCSR_SCAN_FOUND_PEER, +} eCsrScanStatus; + +typedef enum { + eCSR_BW_20MHz_VAL = 20, + eCSR_BW_40MHz_VAL = 40, + eCSR_BW_80MHz_VAL = 80, + eCSR_BW_160MHz_VAL = 160 +} eCSR_BW_Val; + +typedef enum { + eCSR_INI_SINGLE_CHANNEL_CENTERED = 0, + eCSR_INI_DOUBLE_CHANNEL_LOW_PRIMARY = 1, + eCSR_INI_DOUBLE_CHANNEL_HIGH_PRIMARY = 3, + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED = 4, + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED = 5, + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED = 6, + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW = 7, + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW = 8, + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH = 9, + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH = 10, + eCSR_INI_CHANNEL_BONDING_STATE_MAX = 11 +} eIniChanBondState; + +#define CSR_RSN_MAX_PMK_LEN 48 +#define CSR_MAX_PMKID_ALLOWED 32 +#define CSR_TKIP_KEY_LEN 32 +#define CSR_AES_KEY_LEN 16 +#define CSR_AES_GCMP_KEY_LEN 16 +#define CSR_AES_GCMP_256_KEY_LEN 32 +#define CSR_AES_GMAC_128_KEY_LEN 16 +#define CSR_AES_GMAC_256_KEY_LEN 32 +#define CSR_MAX_TX_POWER (WNI_CFG_CURRENT_TX_POWER_LEVEL_STAMAX) +#ifdef FEATURE_WLAN_WAPI +#define CSR_WAPI_BKID_SIZE 16 +#define CSR_MAX_BKID_ALLOWED 16 +#define CSR_WAPI_KEY_LEN 32 +#define CSR_MAX_KEY_LEN (CSR_WAPI_KEY_LEN) /* longest one is for WAPI */ +#else +#define CSR_MAX_KEY_LEN (CSR_TKIP_KEY_LEN) /* longest one is for TKIP */ +#endif /* FEATURE_WLAN_WAPI */ +#ifdef FEATURE_WLAN_ESE +#define CSR_KRK_KEY_LEN 16 +#endif + +typedef struct tagCsrChannelInfo { + uint8_t numOfChannels; + uint32_t *freq_list; +} tCsrChannelInfo, *tpCsrChannelInfo; + +typedef enum { + eHIDDEN_SSID_NOT_IN_USE, + eHIDDEN_SSID_ZERO_LEN, + eHIDDEN_SSID_ZERO_CONTENTS +} tHiddenssId; + +typedef struct tagCsrSSIDInfo { + tSirMacSSid SSID; + bool handoffPermitted; + tHiddenssId ssidHidden; +} tCsrSSIDInfo; + +typedef struct tagCsrSSIDs { + uint32_t numOfSSIDs; + tCsrSSIDInfo *SSIDList; /* To be allocated for array of SSIDs */ +} tCsrSSIDs; + +typedef struct tagCsrBSSIDs { + uint32_t numOfBSSIDs; + struct qdf_mac_addr *bssid; +} tCsrBSSIDs; + +typedef struct tagCsrScanResultInfo { + /* + * Carry the IEs for the current BSSDescription. + * A pointer to tDot11fBeaconIEs. Maybe NULL for start BSS. + */ + void *pvIes; + tAniSSID ssId; + unsigned long timer; /* timer is variable for hidden SSID timer */ + /* + * This member must be the last in the structure because the + * end of struct bss_description is an + * array with nonknown size at this time */ + struct bss_description BssDescriptor; +} tCsrScanResultInfo; + +typedef struct tagCsrEncryptionList { + + uint32_t numEntries; + eCsrEncryptionType encryptionType[eCSR_NUM_OF_ENCRYPT_TYPE]; + +} tCsrEncryptionList, *tpCsrEncryptionList; + +typedef struct tagCsrAuthList { + uint32_t numEntries; + enum csr_akm_type authType[eCSR_NUM_OF_SUPPORT_AUTH_TYPE]; +} tCsrAuthList, *tpCsrAuthList; + +#ifdef FEATURE_WLAN_ESE +typedef struct tagCsrEseCckmInfo { + uint32_t reassoc_req_num; + bool krk_plumbed; + uint8_t krk[SIR_KRK_KEY_LEN]; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + uint8_t btk[SIR_BTK_KEY_LEN]; +#endif +} tCsrEseCckmInfo; + +typedef struct tagCsrEseCckmIe { + uint8_t cckmIe[DOT11F_IE_RSN_MAX_LEN]; + uint8_t cckmIeLen; +} tCsrEseCckmIe; +#endif /* FEATURE_WLAN_ESE */ + +typedef struct sCsrChannel_ { + uint8_t numChannels; + uint32_t channel_freq_list[CFG_VALID_CHANNEL_LIST_LEN]; +} sCsrChannel; + +typedef struct sCsrChnPower_ { + uint32_t first_chan_freq; + uint8_t numChannels; + uint8_t maxtxPower; +} sCsrChnPower; + +typedef struct tagCsr11dinfo { + sCsrChannel Channels; + uint8_t countryCode[CFG_COUNTRY_CODE_LEN + 1]; + /* max power channel list */ + sCsrChnPower ChnPower[CFG_VALID_CHANNEL_LIST_LEN]; +} tCsr11dinfo; + +typedef enum { + eCSR_ROAM_CANCELLED = 1, + /* it means error happens before assoc_start/roaming_start is called. */ + eCSR_ROAM_FAILED, + /* + * a CSR trigger roaming operation starts, + * callback may get a pointer to tCsrConnectedProfile + */ + eCSR_ROAM_ROAMING_START, + /* a CSR trigger roaming operation is completed */ + eCSR_ROAM_ROAMING_COMPLETION, + /* Connection completed status. */ + eCSR_ROAM_CONNECT_COMPLETION, + /* + * an association or start_IBSS operation starts, + * callback may get a pointer to struct csr_roam_profile and + * a pointer to struct bss_description + */ + eCSR_ROAM_ASSOCIATION_START, + /* + * a roaming operation is finish, see eCsrRoamResult for + * possible data passed back + */ + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_DISASSOCIATED, + eCSR_ROAM_ASSOCIATION_FAILURE, + /* when callback with this flag. it gets a pointer to the BSS desc. */ + eCSR_ROAM_SHOULD_ROAM, + /* A new candidate for PMKID is found */ + eCSR_ROAM_SCAN_FOUND_NEW_BSS, + /* CSR is done lostlink roaming and still cannot reconnect */ + eCSR_ROAM_LOSTLINK, + /* a link lost is detected. CSR starts roaming. */ + eCSR_ROAM_LOSTLINK_DETECTED, + /* + * TKIP MIC error detected, callback gets a pointer + * to struct mic_failure_ind + */ + eCSR_ROAM_MIC_ERROR_IND, + /* IBSS indications. */ + eCSR_ROAM_IBSS_IND, + /* + * Update the connection status, useful for IBSS: new peer added, + * network is active etc. + */ + eCSR_ROAM_CONNECT_STATUS_UPDATE, + eCSR_ROAM_GEN_INFO, + eCSR_ROAM_SET_KEY_COMPLETE, + eCSR_ROAM_IBSS_LEAVE, /* IBSS indications. */ + /* BSS in SoftAP mode status indication */ + eCSR_ROAM_INFRA_IND, + eCSR_ROAM_WPS_PBC_PROBE_REQ_IND, + eCSR_ROAM_FT_RESPONSE, + eCSR_ROAM_FT_START, + /* this mean error happens before assoc_start/roam_start is called. */ + eCSR_ROAM_SESSION_OPENED, + eCSR_ROAM_FT_REASSOC_FAILED, + eCSR_ROAM_PMK_NOTIFY, + /* + * Following 4 enums are used by FEATURE_WLAN_LFR_METRICS + * but they are needed for compilation even when + * FEATURE_WLAN_LFR_METRICS is not defined. + */ + eCSR_ROAM_PREAUTH_INIT_NOTIFY, + eCSR_ROAM_PREAUTH_STATUS_SUCCESS, + eCSR_ROAM_PREAUTH_STATUS_FAILURE, + eCSR_ROAM_HANDOVER_SUCCESS, + /* + * TDLS callback events + */ + eCSR_ROAM_TDLS_STATUS_UPDATE, + eCSR_ROAM_RESULT_MGMT_TX_COMPLETE_IND, + + /* Disaconnect all the clients */ + eCSR_ROAM_DISCONNECT_ALL_P2P_CLIENTS, + /* Stopbss triggered from SME due to different */ + eCSR_ROAM_SEND_P2P_STOP_BSS, + /* beacon interval */ +#ifdef WLAN_FEATURE_11W + eCSR_ROAM_UNPROT_MGMT_FRAME_IND, +#endif + + eCSR_ROAM_IBSS_PEER_INFO_COMPLETE, + +#ifdef FEATURE_WLAN_ESE + eCSR_ROAM_TSM_IE_IND, + eCSR_ROAM_CCKM_PREAUTH_NOTIFY, + eCSR_ROAM_ESE_ADJ_AP_REPORT_IND, + eCSR_ROAM_ESE_BCN_REPORT_IND, +#endif /* FEATURE_WLAN_ESE */ + + /* Radar indication from lower layers */ + eCSR_ROAM_DFS_RADAR_IND, + eCSR_ROAM_SET_CHANNEL_RSP, + + /* Channel sw update notification */ + eCSR_ROAM_DFS_CHAN_SW_NOTIFY, + eCSR_ROAM_EXT_CHG_CHNL_IND, + eCSR_ROAM_STA_CHANNEL_SWITCH, + eCSR_ROAM_NDP_STATUS_UPDATE, + eCSR_ROAM_UPDATE_SCAN_RESULT, + eCSR_ROAM_START, + eCSR_ROAM_ABORT, + eCSR_ROAM_NAPI_OFF, + eCSR_ROAM_CHANNEL_COMPLETE_IND, + eCSR_ROAM_CAC_COMPLETE_IND, + eCSR_ROAM_SAE_COMPUTE, + /* LFR3 Roam sync complete */ + eCSR_ROAM_SYNCH_COMPLETE, + eCSR_ROAM_FIPS_PMK_REQUEST, +} eRoamCmdStatus; + +/* comment inside indicates what roaming callback gets */ +typedef enum { + eCSR_ROAM_RESULT_NONE, + eCSR_ROAM_RESULT_SUCCESS = eCSR_ROAM_RESULT_NONE, + /* + * If roamStatus is eCSR_ROAM_ASSOCIATION_COMPLETION, + * struct csr_roam_info's bss_desc may pass back + */ + eCSR_ROAM_RESULT_FAILURE, + /* Pass back pointer to struct csr_roam_info */ + eCSR_ROAM_RESULT_ASSOCIATED, + eCSR_ROAM_RESULT_NOT_ASSOCIATED, + eCSR_ROAM_RESULT_MIC_FAILURE, + eCSR_ROAM_RESULT_FORCED, + eCSR_ROAM_RESULT_DISASSOC_IND, + eCSR_ROAM_RESULT_DEAUTH_IND, + eCSR_ROAM_RESULT_CAP_CHANGED, + /* + * This means we starts an IBSS struct csr_roam_info's + * bss_desc may pass back + */ + eCSR_ROAM_RESULT_IBSS_STARTED, + eCSR_ROAM_RESULT_IBSS_START_FAILED, + eCSR_ROAM_RESULT_IBSS_JOIN_SUCCESS, + eCSR_ROAM_RESULT_IBSS_JOIN_FAILED, + eCSR_ROAM_RESULT_IBSS_CONNECT, + eCSR_ROAM_RESULT_IBSS_INACTIVE, + /* + * If roamStatus is eCSR_ROAM_ASSOCIATION_COMPLETION struct + * csr_roam_info's bss_desc may pass back and the peer's MAC + * address in peerMacOrBssid. If roamStatus is + * eCSR_ROAM_IBSS_IND, the peer's MAC address in + * peerMacOrBssid and a beacon frame of the IBSS in pbFrames + */ + eCSR_ROAM_RESULT_IBSS_NEW_PEER, + /* + * Peer departed from IBSS, Callback may get a pointer tSmeIbssPeerInd + * in pIbssPeerInd + */ + eCSR_ROAM_RESULT_IBSS_PEER_DEPARTED, + /* + * Coalescing in the IBSS network (joined an IBSS network) + * Callback pass a BSSID in peerMacOrBssid + */ + eCSR_ROAM_RESULT_IBSS_COALESCED, + /* + * If roamStatus is eCSR_ROAM_ROAMING_START, callback may get a pointer + * to tCsrConnectedProfile used to connect. + */ + eCSR_ROAM_RESULT_IBSS_STOP, + eCSR_ROAM_RESULT_LOSTLINK, + eCSR_ROAM_RESULT_MIC_ERROR_UNICAST, + eCSR_ROAM_RESULT_MIC_ERROR_GROUP, + eCSR_ROAM_RESULT_AUTHENTICATED, + eCSR_ROAM_RESULT_NEW_RSN_BSS, +#ifdef FEATURE_WLAN_WAPI + eCSR_ROAM_RESULT_NEW_WAPI_BSS, +#endif /* FEATURE_WLAN_WAPI */ + /* INFRA started successfully */ + eCSR_ROAM_RESULT_INFRA_STARTED, + /* INFRA start failed */ + eCSR_ROAM_RESULT_INFRA_START_FAILED, + /* INFRA stopped */ + eCSR_ROAM_RESULT_INFRA_STOPPED, + /* A station joining INFRA AP */ + eCSR_ROAM_RESULT_INFRA_ASSOCIATION_IND, + /* A station joined INFRA AP */ + eCSR_ROAM_RESULT_INFRA_ASSOCIATION_CNF, + /* INFRA disassociated */ + eCSR_ROAM_RESULT_INFRA_DISASSOCIATED, + eCSR_ROAM_RESULT_WPS_PBC_PROBE_REQ_IND, + eCSR_ROAM_RESULT_SEND_ACTION_FAIL, + /* peer rejected assoc because max assoc limit reached */ + eCSR_ROAM_RESULT_MAX_ASSOC_EXCEEDED, + /* Assoc rejected due to concurrent session running on a diff channel */ + eCSR_ROAM_RESULT_ASSOC_FAIL_CON_CHANNEL, + /* TDLS events */ + eCSR_ROAM_RESULT_ADD_TDLS_PEER, + eCSR_ROAM_RESULT_UPDATE_TDLS_PEER, + eCSR_ROAM_RESULT_DELETE_TDLS_PEER, + eCSR_ROAM_RESULT_TEARDOWN_TDLS_PEER_IND, + eCSR_ROAM_RESULT_DELETE_ALL_TDLS_PEER_IND, + eCSR_ROAM_RESULT_LINK_ESTABLISH_REQ_RSP, + eCSR_ROAM_RESULT_TDLS_SHOULD_DISCOVER, + eCSR_ROAM_RESULT_TDLS_SHOULD_TEARDOWN, + eCSR_ROAM_RESULT_TDLS_SHOULD_PEER_DISCONNECTED, + eCSR_ROAM_RESULT_TDLS_CONNECTION_TRACKER_NOTIFICATION, + + eCSR_ROAM_RESULT_IBSS_PEER_INFO_SUCCESS, + eCSR_ROAM_RESULT_IBSS_PEER_INFO_FAILED, + eCSR_ROAM_RESULT_DFS_RADAR_FOUND_IND, + eCSR_ROAM_RESULT_CHANNEL_CHANGE_SUCCESS, + eCSR_ROAM_RESULT_CHANNEL_CHANGE_FAILURE, + eCSR_ROAM_RESULT_CSA_RESTART_RSP, + eCSR_ROAM_RESULT_DFS_CHANSW_UPDATE_SUCCESS, + eCSR_ROAM_EXT_CHG_CHNL_UPDATE_IND, + + eCSR_ROAM_RESULT_NDI_CREATE_RSP, + eCSR_ROAM_RESULT_NDI_DELETE_RSP, + eCSR_ROAM_RESULT_NDP_INITIATOR_RSP, + eCSR_ROAM_RESULT_NDP_NEW_PEER_IND, + eCSR_ROAM_RESULT_NDP_CONFIRM_IND, + eCSR_ROAM_RESULT_NDP_INDICATION, + eCSR_ROAM_RESULT_NDP_SCHED_UPDATE_RSP, + eCSR_ROAM_RESULT_NDP_RESPONDER_RSP, + eCSR_ROAM_RESULT_NDP_END_RSP, + eCSR_ROAM_RESULT_NDP_PEER_DEPARTED_IND, + eCSR_ROAM_RESULT_NDP_END_IND, + eCSR_ROAM_RESULT_CAC_END_IND, + /* If Scan for SSID failed to found proper BSS */ + eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE, + eCSR_ROAM_RESULT_INVOKE_FAILED, +} eCsrRoamResult; + +typedef enum { + eCSR_DISCONNECT_REASON_UNSPECIFIED = 0, + eCSR_DISCONNECT_REASON_MIC_ERROR, + eCSR_DISCONNECT_REASON_DISASSOC, + eCSR_DISCONNECT_REASON_DEAUTH, + eCSR_DISCONNECT_REASON_HANDOFF, + eCSR_DISCONNECT_REASON_IBSS_LEAVE, + eCSR_DISCONNECT_REASON_STA_HAS_LEFT, + eCSR_DISCONNECT_REASON_NDI_DELETE, + eCSR_DISCONNECT_REASON_ROAM_HO_FAIL, +} eCsrRoamDisconnectReason; + +typedef enum { + /* Not associated in Infra or participating in an IBSS/Ad-hoc */ + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED, + /* Associated in an Infrastructure network. */ + eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED, + /* Participating in IBSS network though disconnection */ + eCSR_ASSOC_STATE_TYPE_IBSS_DISCONNECTED, + /* Participating in IBSS network with partner stations also present */ + eCSR_ASSOC_STATE_TYPE_IBSS_CONNECTED, + /* Participating in WDS network in AP/STA mode but not connected yet */ + eCSR_ASSOC_STATE_TYPE_WDS_DISCONNECTED, + /* Participating in a WDS network and connected peer to peer */ + eCSR_ASSOC_STATE_TYPE_WDS_CONNECTED, + /* Participating in a Infra network in AP not yet in connected state */ + eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTED, + /* Participating in a Infra network and connected to a peer */ + eCSR_ASSOC_STATE_TYPE_INFRA_CONNECTED, + /* Disconnecting with AP or stop connecting process */ + eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTING, + /* NAN Data interface not started */ + eCSR_CONNECT_STATE_TYPE_NDI_NOT_STARTED, + /* NAN Data interface started */ + eCSR_CONNECT_STATE_TYPE_NDI_STARTED, + +} eCsrConnectState; + +/* + * This parameter is no longer supported in the Profile. + * Need to set this in the global properties for the adapter. + */ +typedef enum eCSR_MEDIUM_ACCESS { + eCSR_MEDIUM_ACCESS_AUTO = 0, + eCSR_MEDIUM_ACCESS_DCF, + eCSR_MEDIUM_ACCESS_eDCF, + eCSR_MEDIUM_ACCESS_HCF, + + eCSR_MEDIUM_ACCESS_WMM_eDCF_802dot1p, + eCSR_MEDIUM_ACCESS_WMM_eDCF_DSCP, + eCSR_MEDIUM_ACCESS_WMM_eDCF_NoClassify, + eCSR_MEDIUM_ACCESS_11e_eDCF = eCSR_MEDIUM_ACCESS_eDCF, + eCSR_MEDIUM_ACCESS_11e_HCF = eCSR_MEDIUM_ACCESS_HCF, +} eCsrMediaAccessType; + +typedef enum { + eCSR_OPERATING_CHANNEL_ALL = 0, + eCSR_OPERATING_CHANNEL_AUTO = eCSR_OPERATING_CHANNEL_ALL, + eCSR_OPERATING_CHANNEL_ANY = eCSR_OPERATING_CHANNEL_ALL, +} eOperationChannel; + +/* + * For channel bonding, the channel number gap is 4, either up or down. + * For both 11a and 11g mode. + */ +#define CSR_CB_CHANNEL_GAP 4 +/* Considering 5 MHz Channel BW */ +#define CSR_CB_CENTER_CHANNEL_OFFSET 10 +#define CSR_SEC_CHANNEL_OFFSET 20 + + +/* WEP keysize (in bits) */ +typedef enum { + /* 40 bit key + 24bit IV = 64bit WEP */ + eCSR_SECURITY_WEP_KEYSIZE_40 = 40, + /* 104bit key + 24bit IV = 128bit WEP */ + eCSR_SECURITY_WEP_KEYSIZE_104 = 104, + eCSR_SECURITY_WEP_KEYSIZE_MIN = eCSR_SECURITY_WEP_KEYSIZE_40, + eCSR_SECURITY_WEP_KEYSIZE_MAX = eCSR_SECURITY_WEP_KEYSIZE_104, + eCSR_SECURITY_WEP_KEYSIZE_MAX_BYTES = + (eCSR_SECURITY_WEP_KEYSIZE_MAX / 8), +} eCsrWEPKeySize; + +/* Possible values for the WEP static key ID */ +typedef enum { + + eCSR_SECURITY_WEP_STATIC_KEY_ID_MIN = 0, + eCSR_SECURITY_WEP_STATIC_KEY_ID_MAX = 3, + eCSR_SECURITY_WEP_STATIC_KEY_ID_DEFAULT = 0, + + eCSR_SECURITY_WEP_STATIC_KEY_ID_INVALID = -1, + +} eCsrWEPStaticKeyID; + +/* Two extra key indicies are used for the IGTK (which is used by BIP) */ +#define CSR_MAX_NUM_KEY (eCSR_SECURITY_WEP_STATIC_KEY_ID_MAX + 2 + 1) + +typedef enum { + /* + * Roaming because HDD requested for reassoc by changing one of the + * fields in tCsrRoamModifyProfileFields. OR Roaming because SME + * requested for reassoc by changing one of the fields in + * tCsrRoamModifyProfileFields. + */ + eCsrRoamReasonStaCapabilityChanged, + /* + * Roaming because SME requested for reassoc to a different AP, + * as part of inter AP handoff. + */ + eCsrRoamReasonBetterAP, + /* + * Roaming because SME requested it as the link is lost - placeholder, + * will clean it up once handoff code gets in + */ + eCsrRoamReasonSmeIssuedForLostLink, + +} eCsrRoamReasonCodes; + +typedef enum { + eCsrRoamWmmAuto = 0, + eCsrRoamWmmQbssOnly = 1, + eCsrRoamWmmNoQos = 2, + +} eCsrRoamWmmUserModeType; + +typedef struct tagPmkidCandidateInfo { + struct qdf_mac_addr BSSID; + bool preAuthSupported; +} tPmkidCandidateInfo; + +typedef struct tagPmkidCacheInfo { + struct qdf_mac_addr BSSID; + uint8_t PMKID[PMKID_LEN]; + uint8_t pmk[CSR_RSN_MAX_PMK_LEN]; + uint8_t pmk_len; + uint8_t ssid_len; + uint8_t ssid[WLAN_SSID_MAX_LEN]; + uint8_t cache_id[CACHE_ID_LEN]; +} tPmkidCacheInfo; + +#ifdef FEATURE_WLAN_WAPI +typedef struct tagBkidCandidateInfo { + struct qdf_mac_addr BSSID; + bool preAuthSupported; +} tBkidCandidateInfo; + +typedef struct tagBkidCacheInfo { + struct qdf_mac_addr BSSID; + uint8_t BKID[CSR_WAPI_BKID_SIZE]; +} tBkidCacheInfo; +#endif /* FEATURE_WLAN_WAPI */ + +typedef struct tagCsrKeys { + /* Also use to indicate whether the key index is set */ + uint8_t KeyLength[CSR_MAX_NUM_KEY]; + uint8_t KeyMaterial[CSR_MAX_NUM_KEY][CSR_MAX_KEY_LEN]; + uint8_t defaultIndex; +} tCsrKeys; + +/* + * Following fields which're part of tCsrRoamConnectedProfile might need + * modification dynamically once STA is up & running & this'd trigger reassoc + */ +typedef struct tagCsrRoamModifyProfileFields { + /* + * during connect this specifies ACs U-APSD is to be setup + * for (Bit0:VO; Bit1:VI; Bit2:BK; Bit3:BE all other bits are ignored). + * During assoc response this COULD carry confirmation of what + * ACs U-APSD got setup for. Later if an APP looking for APSD, + * SME-QoS might need to modify this field + */ + uint8_t uapsd_mask; + /* HDD might ask to modify this field */ + uint16_t listen_interval; +} tCsrRoamModifyProfileFields; + +struct csr_roam_profile { + tCsrSSIDs SSIDs; + tCsrBSSIDs BSSIDs; + /* this is bit mask of all the needed phy mode defined in eCsrPhyMode */ + uint32_t phyMode; + eCsrRoamBssType BSSType; + tCsrAuthList AuthType; + enum csr_akm_type negotiatedAuthType; + tCsrAuthList akm_list; + tCsrEncryptionList EncryptionType; + /* This field is for output only, not for input */ + eCsrEncryptionType negotiatedUCEncryptionType; + /* + * eCSR_ENCRYPT_TYPE_ANY cannot be set in multicast encryption type. + * If caller doesn't case, put all supported encryption types in here + */ + tCsrEncryptionList mcEncryptionType; + /* This field is for output only, not for input */ + eCsrEncryptionType negotiatedMCEncryptionType; +#ifdef WLAN_FEATURE_11W + /* Management Frame Protection */ + bool MFPEnabled; + uint8_t MFPRequired; + uint8_t MFPCapable; +#endif + tAniEdType mgmt_encryption_type; + tCsrKeys Keys; + tCsrChannelInfo ChannelInfo; + uint32_t op_freq; + struct ch_params ch_params; + /* If this is 0, SME will fill in for caller. */ + uint16_t beaconInterval; + /* + * during connect this specifies ACs U-APSD is to be setup + * for (Bit0:VO; Bit1:VI; Bit2:BK; Bit3:BE all other bits are ignored). + * During assoc resp this'd carry cnf of what ACs U-APSD got setup for + */ + uint8_t uapsd_mask; + uint32_t nWPAReqIELength; /* The byte count in the pWPAReqIE */ + uint8_t *pWPAReqIE; /* If not null,it's IE byte stream for WPA */ + uint32_t nRSNReqIELength; /* The byte count in the pRSNReqIE */ + uint8_t *pRSNReqIE; /* If not null,it's IE byte stream for RSN */ +#ifdef FEATURE_WLAN_WAPI + uint32_t nWAPIReqIELength;/* The byte count in the pWAPIReqIE */ + uint8_t *pWAPIReqIE; /* If not null,it's IE byte stream for WAPI */ +#endif /* FEATURE_WLAN_WAPI */ + + uint32_t nAddIEScanLength;/* pAddIE for scan (at the time of join) */ + /* + * If not null,it's the IE byte stream for additional IE, + * which can be WSC IE and/or P2P IE + */ + uint8_t *pAddIEScan; + uint32_t nAddIEAssocLength; /* The byte count in the pAddIE for assoc */ + /* + * If not null, it has the IE byte stream for additional IE, + * which can be WSC IE and/or P2P IE + */ + uint8_t *pAddIEAssoc; + /* it is ignored if [0] is 0. */ + uint8_t countryCode[CFG_COUNTRY_CODE_LEN]; + /* WPS Association if true => auth and ecryption should be ignored */ + bool bWPSAssociation; + bool bOSENAssociation; + uint8_t ieee80211d; + uint8_t privacy; + bool fwdWPSPBCProbeReq; + tAniAuthType csr80211AuthType; + uint32_t dtimPeriod; + bool ApUapsdEnable; + bool protEnabled; + bool obssProtEnabled; + bool chan_switch_hostapd_rate_enabled; + uint16_t cfg_protection; + uint8_t wps_state; + struct mobility_domain_info mdid; + enum QDF_OPMODE csrPersona; + /* addIe params */ + struct add_ie_params add_ie_params; + uint16_t beacon_tx_rate; + tSirMacRateSet supported_rates; + tSirMacRateSet extended_rates; + bool require_h2e; + struct qdf_mac_addr bssid_hint; + bool force_24ghz_in_ht20; + uint32_t cac_duration_ms; + uint32_t dfs_regdomain; +#ifdef WLAN_FEATURE_FILS_SK + uint8_t *hlp_ie; + uint32_t hlp_ie_len; + struct cds_fils_connection_info *fils_con_info; +#endif + bool force_rsne_override; +}; + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +typedef struct tagCsrRoamHTProfile { + uint8_t phymode; + uint8_t htCapability; + uint8_t htSupportedChannelWidthSet; + uint8_t htRecommendedTxWidthSet; + ePhyChanBondState htSecondaryChannelOffset; + uint8_t vhtCapability; + uint8_t apCenterChan; + uint8_t apChanWidth; +} tCsrRoamHTProfile; +#endif + +typedef struct tagCsrRoamConnectedProfile { + tSirMacSSid SSID; + bool handoffPermitted; + bool ssidHidden; + uint32_t op_freq; + struct qdf_mac_addr bssid; + uint16_t beaconInterval; + eCsrRoamBssType BSSType; + enum csr_akm_type AuthType; + tCsrAuthList AuthInfo; + tCsrAuthList akm_list; + eCsrEncryptionType EncryptionType; + tCsrEncryptionList EncryptionInfo; + eCsrEncryptionType mcEncryptionType; + tCsrEncryptionList mcEncryptionInfo; + /* group management cipher suite used for 11w */ + tAniEdType mgmt_encryption_type; + uint8_t country_code[WNI_CFG_COUNTRY_CODE_LEN]; + uint32_t vht_channel_width; + tCsrKeys Keys; + /* + * meaningless on connect. It's an OUT param from CSR's point of view + * During assoc response carries the ACM bit-mask i.e. what + * ACs have ACM=1 (if any),(Bit0:VO; Bit1:VI; Bit2:BK; Bit3:BE + * all other bits are ignored) + */ + uint8_t acm_mask; + tCsrRoamModifyProfileFields modifyProfileFields; + bool qosConnection; /* A connection is QoS enabled */ + uint32_t nAddIEAssocLength; + /* + * If not null,it's IE byte stream for additional IE, + * which can be WSC IE and/or P2P IE + */ + uint8_t *pAddIEAssoc; + struct bss_description *bss_desc; + bool qap; /* AP supports QoS */ + struct mobility_domain_info mdid; +#ifdef FEATURE_WLAN_ESE + tCsrEseCckmInfo eseCckmInfo; + bool isESEAssoc; +#endif + uint32_t dot11Mode; + uint8_t proxy_arp_service; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + tCsrRoamHTProfile ht_profile; +#endif +#ifdef WLAN_FEATURE_11W + /* Management Frame Protection */ + bool MFPEnabled; + uint8_t MFPRequired; + uint8_t MFPCapable; +#endif +} tCsrRoamConnectedProfile; + +/** + * enum sta_roam_policy_dfs_mode - state of DFS mode for STA ROME policy + * @CSR_STA_ROAM_POLICY_NONE: DFS mode attribute is not valid + * @CSR_STA_ROAM_POLICY_DFS_ENABLED: DFS mode is enabled + * @CSR_STA_ROAM_POLICY_DFS_DISABLED: DFS mode is disabled + * @CSR_STA_ROAM_POLICY_DFS_DEPRIORITIZE: Deprioritize DFS channels in scanning + */ +enum sta_roam_policy_dfs_mode { + CSR_STA_ROAM_POLICY_NONE, + CSR_STA_ROAM_POLICY_DFS_ENABLED, + CSR_STA_ROAM_POLICY_DFS_DISABLED, + CSR_STA_ROAM_POLICY_DFS_DEPRIORITIZE +}; + +/** + * struct csr_sta_roam_policy_params - sta roam policy params for station + * @dfs_mode: tell is DFS channels needs to be skipped while scanning + * @skip_unsafe_channels: tells if unsafe channels needs to be skip in scanning + * @sap_operating_band: Opearting band for SAP + */ +struct csr_sta_roam_policy_params { + enum sta_roam_policy_dfs_mode dfs_mode; + bool skip_unsafe_channels; + uint8_t sap_operating_band; +}; + +/** + * struct csr_neighbor_report_offload_params - neighbor report offload params + * @params_bitmask: bitmask to specify which of the below are enabled + * @time_offset: time offset after 11k offload command to trigger a neighbor + * report request (in seconds) + * @low_rssi_offset: Offset from rssi threshold to trigger neighbor + * report request (in dBm) + * @bmiss_count_trigger: Number of beacon miss events to trigger neighbor + * report request + * @per_threshold_offset: offset from PER threshold to trigger neighbor + * report request (in %) + * @neighbor_report_cache_timeout: timeout after which new trigger can enable + * sending of a neighbor report request (in seconds) + * @max_neighbor_report_req_cap: max number of neighbor report requests that + * can be sent to the peer in the current session + */ +struct csr_neighbor_report_offload_params { + uint8_t params_bitmask; + uint32_t time_offset; + uint32_t low_rssi_offset; + uint32_t bmiss_count_trigger; + uint32_t per_threshold_offset; + uint32_t neighbor_report_cache_timeout; + uint32_t max_neighbor_report_req_cap; +}; + +struct csr_config_params { + /* keep this uint32_t. This gets converted to ePhyChannelBondState */ + uint32_t channelBondingMode24GHz; + uint8_t nud_fail_behaviour; + uint32_t channelBondingMode5GHz; + eCsrPhyMode phyMode; + uint32_t HeartbeatThresh50; + eCsrRoamWmmUserModeType WMMSupportMode; + bool Is11eSupportEnabled; + bool ProprietaryRatesEnabled; + uint32_t ad_hoc_ch_freq_5g; + uint32_t ad_hoc_ch_freq_2g; + /* + * this number minus one is the number of times a scan doesn't find it + * before it is removed + */ + /* to set the RSSI difference for each category */ + uint8_t bCatRssiOffset; + /* to set MCC Enable/Disable mode */ + uint8_t fEnableMCCMode; + bool mcc_rts_cts_prot_enable; + bool mcc_bcast_prob_resp_enable; + /* + * To allow MCC GO different B.I than STA's. + * NOTE: make sure if RIVA firmware can handle this combination before + * enabling this at the moment, this flag is provided only to pass + * Wi-Fi Cert. 5.1.12 + */ + uint8_t fAllowMCCGODiffBI; + tCsr11dinfo Csr11dinfo; + /* stats request frequency from PE while in full power */ + uint32_t statsReqPeriodicity; + /* stats request frequency from PE while in power save */ + uint32_t statsReqPeriodicityInPS; +#ifdef FEATURE_WLAN_ESE + uint8_t isEseIniFeatureEnabled; +#endif + uint8_t isFastRoamIniFeatureEnabled; + struct mawc_params csr_mawc_config; + /* + * Customer wants to optimize the scan time. Avoiding scans(passive) + * on DFS channels while swipping through both bands can save some time + * (apprx 1.3 sec) + */ + uint8_t fEnableDFSChnlScan; + /* + * To enable/disable scanning 2.4Ghz channels twice on a single scan + * request from HDD + */ + bool fScanTwice; + bool vendor_vht_sap; + bool send_smps_action; + + uint8_t disable_high_ht_mcs_2x2; + uint8_t isCoalesingInIBSSAllowed; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + uint8_t cc_switch_mode; +#endif + uint8_t allowDFSChannelRoam; + bool obssEnabled; + uint8_t conc_custom_rule1; + uint8_t conc_custom_rule2; + uint8_t is_sta_connection_in_5gz_enabled; + + uint8_t max_intf_count; + uint32_t f_sta_miracast_mcc_rest_time_val; +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + bool sap_channel_avoidance; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + uint32_t dual_mac_feature_disable; + uint32_t sta_sap_scc_on_dfs_chan; + uint32_t roam_dense_rssi_thresh_offset; + uint32_t roam_dense_min_aps; + int8_t roam_bg_scan_bad_rssi_thresh; + uint8_t roam_bad_rssi_thresh_offset_2g; + uint32_t roam_data_rssi_threshold_triggers; + int32_t roam_data_rssi_threshold; + uint32_t rx_data_inactivity_time; + struct csr_sta_roam_policy_params sta_roam_policy_params; + bool enable_bcast_probe_rsp; + bool is_fils_enabled; + enum force_1x1_type is_force_1x1; + uint8_t oce_feature_bitmap; + uint32_t offload_11k_enable_bitmask; + bool wep_tkip_in_he; + struct csr_neighbor_report_offload_params neighbor_report_offload; +}; + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +#define csr_is_roam_offload_enabled(mac) \ + (mac->mlme_cfg->lfr.lfr3_roaming_offload) +#define DEFAULT_REASSOC_FAILURE_TIMEOUT 1000 +#else +#define csr_is_roam_offload_enabled(mac) false +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/* connected but not authenticated */ +#define CSR_ROAM_AUTH_STATUS_CONNECTED 0x1 +/* connected and authenticated */ +#define CSR_ROAM_AUTH_STATUS_AUTHENTICATED 0x2 +#endif + +struct csr_roam_info { + struct csr_roam_profile *pProfile; + struct bss_description *bss_desc; + uint32_t nBeaconLength; + uint32_t nAssocReqLength; + uint32_t nAssocRspLength; + uint32_t nFrameLength; + uint8_t frameType; + /* + * Point to a buffer contain the beacon, assoc req, assoc rsp frame, + * in that order user needs to use nBeaconLength, nAssocReqLength, + * nAssocRspLength to desice where each frame starts and ends. + */ + uint8_t *pbFrames; + bool fReassocReq; /* set to true if for re-association */ + bool fReassocRsp; /* set to true if for re-association */ + struct qdf_mac_addr bssid; + /* + * Only valid in IBSS. this is the peers MAC address for + * eCSR_ROAM_RESULT_IBSS_NEW_PEER or PEER_DEPARTED + */ + struct qdf_mac_addr peerMac; + tSirResultCodes status_code; + /* this'd be our own defined or sent from otherBSS(per 802.11spec) */ + uint32_t reasonCode; + + uint8_t disassoc_reason; + + uint8_t staId; /* Peer stationId when connected */ + /* false means auth needed from supplicant. true means authenticated */ + bool fAuthRequired; + uint8_t sessionId; + uint8_t rsnIELen; + uint8_t *prsnIE; + uint8_t wapiIELen; + uint8_t *pwapiIE; + uint8_t addIELen; + uint8_t *paddIE; + union { + tSirMicFailureInfo *pMICFailureInfo; + tCsrRoamConnectedProfile *pConnectedProfile; + tSirWPSPBCProbeReq *pWPSPBCProbeReq; + } u; + bool wmmEnabledSta; /* set to true if WMM enabled STA */ + uint32_t dtimPeriod; +#ifdef FEATURE_WLAN_ESE + bool isESEAssoc; + struct tsm_ie tsm_ie; + uint32_t timestamp[2]; + uint16_t tsmRoamDelay; + struct ese_bcn_report_rsp *pEseBcnReportRsp; +#endif +#ifdef FEATURE_WLAN_TDLS + /* + * TDLS parameters to check whether TDLS + * and TDLS channel switch is allowed in the + * AP network + */ + bool tdls_prohibited; /* per ExtCap in Assoc/Reassoc resp */ + bool tdls_chan_swit_prohibited; /* per ExtCap in Assoc/Reassoc resp */ +#endif + /* Required for indicating the frames to upper layer */ + uint32_t beaconLength; + uint8_t *beaconPtr; + uint32_t assocReqLength; + uint8_t *assocReqPtr; + int8_t rxRssi; + tSirSmeDfsEventInd dfs_event; + tSirChanChangeResponse *channelChangeRespEvent; + /* Timing and fine Timing measurement capability clubbed together */ + uint8_t timingMeasCap; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + uint8_t roamSynchInProgress; + uint8_t synchAuthStatus; + uint8_t kck[KCK_256BIT_KEY_LEN]; + uint8_t kck_len; + uint8_t kek[SIR_KEK_KEY_LEN_FILS]; + uint8_t kek_len; + uint32_t pmk_len; + uint8_t pmk[SIR_PMK_LEN]; + uint8_t pmkid[PMKID_LEN]; + bool update_erp_next_seq_num; + uint16_t next_erp_seq_num; + uint8_t replay_ctr[SIR_REPLAY_CTR_LEN]; + uint8_t subnet_change_status; +#endif + struct oem_channel_info chan_info; + uint32_t target_chan_freq; + +#ifdef WLAN_FEATURE_NAN + union { + struct ndi_create_rsp ndi_create_params; + struct ndi_delete_rsp ndi_delete_params; + } ndp; +#endif + tDot11fIEHTCaps ht_caps; + tDot11fIEVHTCaps vht_caps; + bool he_caps_present; + tDot11fIEhs20vendor_ie hs20vendor_ie; + tDot11fIEVHTOperation vht_operation; + tDot11fIEHTInfo ht_operation; +#ifdef WLAN_FEATURE_11AX + tDot11fIEhe_op he_operation; +#endif + bool reassoc; + bool ampdu; + bool sgi_enable; + bool tx_stbc; + bool rx_stbc; + tSirMacHTChannelWidth ch_width; + enum sir_sme_phy_mode mode; + uint8_t max_supp_idx; + uint8_t max_ext_idx; + uint8_t max_mcs_idx; + uint8_t rx_mcs_map; + uint8_t tx_mcs_map; + /* Extended capabilities of STA */ + uint8_t ecsa_capable; + bool is_fils_connection; +#ifdef WLAN_FEATURE_FILS_SK + uint16_t fils_seq_num; + struct fils_join_rsp_params *fils_join_rsp; +#endif + int rssi; + int tx_rate; + int rx_rate; + tSirMacCapabilityInfo capability_info; + uint32_t rx_mc_bc_cnt; + uint32_t rx_retry_cnt; +#ifdef WLAN_FEATURE_SAE + struct sir_sae_info *sae_info; +#endif + struct assoc_ind *owe_pending_assoc_ind; + uint16_t roam_reason; + struct wlan_ies *disconnect_ies; +}; + +typedef struct sSirSmeAssocIndToUpperLayerCnf { + uint16_t messageType; /* eWNI_SME_ASSOC_CNF */ + uint16_t length; + uint8_t sessionId; + tSirResultCodes status_code; + tSirMacAddr bssId; /* Self BSSID */ + tSirMacAddr peerMacAddr; + uint16_t aid; + uint8_t wmmEnabledSta; /* set to true if WMM enabled STA */ + tSirRSNie rsnIE; /* RSN IE received from peer */ + tSirWAPIie wapiIE; /* WAPI IE received from peer */ + tSirAddie addIE; /* this can be WSC and/or P2P IE */ + uint8_t reassocReq; /* set to true if reassoc */ + /* Timing and fine Timing measurement capability clubbed together */ + uint8_t timingMeasCap; + struct oem_channel_info chan_info; + uint8_t target_channel; + bool ampdu; + bool sgi_enable; + bool tx_stbc; + tSirMacHTChannelWidth ch_width; + enum sir_sme_phy_mode mode; + bool rx_stbc; + uint8_t max_supp_idx; + uint8_t max_ext_idx; + uint8_t max_mcs_idx; + uint8_t rx_mcs_map; + uint8_t tx_mcs_map; + /* Extended capabilities of STA */ + uint8_t ecsa_capable; + + uint32_t ies_len; + uint8_t *ies; + tDot11fIEHTCaps ht_caps; + tDot11fIEVHTCaps vht_caps; + tSirMacCapabilityInfo capability_info; + bool he_caps_present; +} tSirSmeAssocIndToUpperLayerCnf, *tpSirSmeAssocIndToUpperLayerCnf; + +typedef struct tagCsrSummaryStatsInfo { + uint32_t snr; + int8_t rssi; + uint32_t retry_cnt[4]; + uint32_t multiple_retry_cnt[4]; + uint32_t tx_frm_cnt[4]; + /* uint32_t num_rx_frm_crc_err; same as rx_error_cnt */ + /* uint32_t num_rx_frm_crc_ok; same as rx_frm_cnt */ + uint32_t rx_frm_cnt; + uint32_t frm_dup_cnt; + uint32_t fail_cnt[4]; + uint32_t rts_fail_cnt; + uint32_t ack_fail_cnt; + uint32_t rts_succ_cnt; + uint32_t rx_discard_cnt; + uint32_t rx_error_cnt; + uint32_t tx_byte_cnt; + +} tCsrSummaryStatsInfo; + +typedef struct tagCsrGlobalClassAStatsInfo { + uint8_t tx_nss; + uint8_t rx_nss; + uint32_t max_pwr; + uint32_t tx_rate; + uint32_t rx_rate; + /* mcs index for HT20 and HT40 rates */ + uint32_t tx_mcs_index; + uint32_t rx_mcs_index; + enum tx_rate_info tx_mcs_rate_flags; + enum tx_rate_info rx_mcs_rate_flags; + uint8_t tx_dcm; + uint8_t rx_dcm; + enum txrate_gi tx_gi; + enum txrate_gi rx_gi; + /* to diff between HT20 & HT40 rates;short & long guard interval */ + enum tx_rate_info tx_rx_rate_flags; + +} tCsrGlobalClassAStatsInfo; + +typedef struct tagCsrGlobalClassDStatsInfo { + uint32_t tx_uc_frm_cnt; + uint32_t tx_mc_frm_cnt; + uint32_t tx_bc_frm_cnt; + uint32_t rx_uc_frm_cnt; + uint32_t rx_mc_frm_cnt; + uint32_t rx_bc_frm_cnt; + uint32_t tx_uc_byte_cnt[4]; + uint32_t tx_mc_byte_cnt; + uint32_t tx_bc_byte_cnt; + uint32_t rx_uc_byte_cnt[4]; + uint32_t rx_mc_byte_cnt; + uint32_t rx_bc_byte_cnt; + uint32_t rx_byte_cnt; + uint32_t num_rx_bytes_crc_ok; + uint32_t rx_rate; + +} tCsrGlobalClassDStatsInfo; + +/** + * struct csr_per_chain_rssi_stats_info - stores chain rssi + * @rssi: array containing rssi for all chains + * @peer_mac_addr: peer mac address + */ +struct csr_per_chain_rssi_stats_info { + int8_t rssi[NUM_CHAINS_MAX]; + tSirMacAddr peer_mac_addr; +}; + +typedef struct tagCsrRoamSetKey { + eCsrEncryptionType encType; + tAniKeyDirection keyDirection; /* Tx, Rx or Tx-and-Rx */ + struct qdf_mac_addr peerMac; /* Peer MAC. ALL 1's for group key */ + uint8_t paeRole; /* 0 for supplicant */ + uint8_t keyId; /* Key index */ + uint16_t keyLength; /* Number of bytes containing the key in pKey */ + uint8_t Key[CSR_MAX_KEY_LEN]; + uint8_t keyRsc[WLAN_CRYPTO_RSC_SIZE]; +} tCsrRoamSetKey; + +typedef void *tScanResultHandle; + +typedef enum { + REASSOC = 0, + FASTREASSOC = 1, + CONNECT_CMD_USERSPACE = 2, +} handoff_src; + +typedef struct tagCsrHandoffRequest { + struct qdf_mac_addr bssid; + uint32_t ch_freq; + uint8_t src; /* To check if its a REASSOC or a FASTREASSOC IOCTL */ +} tCsrHandoffRequest; + +#ifdef FEATURE_WLAN_ESE +typedef struct tagCsrEseBeaconReqParams { + uint16_t measurementToken; + uint32_t ch_freq; + uint8_t scanMode; + uint16_t measurementDuration; +} tCsrEseBeaconReqParams, *tpCsrEseBeaconReqParams; + +typedef struct tagCsrEseBeaconReq { + uint8_t numBcnReqIe; + tCsrEseBeaconReqParams bcnReq[SIR_ESE_MAX_MEAS_IE_REQS]; +} tCsrEseBeaconReq, *tpCsrEseBeaconReq; +#endif /* FEATURE_WLAN_ESE */ + +struct csr_del_sta_params { + struct qdf_mac_addr peerMacAddr; + uint16_t reason_code; + uint8_t subtype; +}; + +/** + * struct wep_update_default_key_idx: wep default key index structure + * @session_id: session ID for the connection session + * @default_idx: default key index for wep + * + * structure includes sesssion id for connection and default key + * index used for wep + */ +struct wep_update_default_key_idx { + uint8_t session_id; + uint8_t default_idx; +}; + +typedef QDF_STATUS (*csr_roam_complete_cb)(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + struct csr_roam_info *param, + uint32_t roam_id, + eRoamCmdStatus roam_status, + eCsrRoamResult roam_result); +typedef QDF_STATUS (*csr_session_open_cb)(uint8_t session_id, + QDF_STATUS qdf_status); +typedef QDF_STATUS (*csr_session_close_cb)(uint8_t session_id); + +#define CSR_IS_START_IBSS(pProfile) (eCSR_BSS_TYPE_START_IBSS == \ + (pProfile)->BSSType) +#define CSR_IS_JOIN_TO_IBSS(pProfile) (eCSR_BSS_TYPE_IBSS == \ + (pProfile)->BSSType) +#define CSR_IS_IBSS(pProfile) (CSR_IS_START_IBSS(pProfile) || \ + CSR_IS_JOIN_TO_IBSS(pProfile)) +#define CSR_IS_INFRASTRUCTURE(pProfile) (eCSR_BSS_TYPE_INFRASTRUCTURE == \ + (pProfile)->BSSType) +#define CSR_IS_ANY_BSS_TYPE(pProfile) (eCSR_BSS_TYPE_ANY == \ + (pProfile)->BSSType) +#define CSR_IS_INFRA_AP(pProfile) (eCSR_BSS_TYPE_INFRA_AP == \ + (pProfile)->BSSType) +#ifdef WLAN_FEATURE_NAN +#define CSR_IS_NDI(profile) (eCSR_BSS_TYPE_NDI == (profile)->BSSType) +#else +#define CSR_IS_NDI(profile) (false) +#endif +#define CSR_IS_CONN_INFRA_AP(pProfile) (eCSR_BSS_TYPE_INFRA_AP == \ + (pProfile)->BSSType) +#ifdef WLAN_FEATURE_NAN +#define CSR_IS_CONN_NDI(profile) (eCSR_BSS_TYPE_NDI == (profile)->BSSType) +#else +#define CSR_IS_CONN_NDI(profile) (false) +#endif + +#ifdef WLAN_FEATURE_SAE +#define CSR_IS_AUTH_TYPE_SAE(auth_type) \ + (eCSR_AUTH_TYPE_SAE == auth_type) + +#define CSR_IS_AKM_FT_SAE(auth_type) \ + (eCSR_AUTH_TYPE_FT_SAE == (auth_type)) + +#define CSR_IS_FW_FT_SAE_SUPPORTED(fw_akm_bitmap) \ + (((fw_akm_bitmap) & (1 << AKM_FT_SAE)) ? true : false) + +#define CSR_IS_FW_SAE_ROAM_SUPPORTED(fw_akm_bitmap) \ + (((fw_akm_bitmap) & (1 << AKM_SAE)) ? true : false) +#else +#define CSR_IS_AUTH_TYPE_SAE(auth_type) (false) + +#define CSR_IS_AKM_FT_SAE(auth_type) (false) + +#define CSR_IS_FW_FT_SAE_SUPPORTED(fw_akm_bitmap) (false) + +#define CSR_IS_FW_SAE_ROAM_SUPPORTED(fw_akm_bitmap) (false) +#endif + +#define CSR_IS_FW_OWE_ROAM_SUPPORTED(fw_akm_bitmap) \ + (((fw_akm_bitmap) & (1 << AKM_OWE)) ? true : false) + +#define CSR_IS_AKM_FT_SUITEB_SHA384(auth_type) \ + (eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384 == (auth_type)) + +#define CSR_IS_AKM_FILS(auth_type) \ + ((eCSR_AUTH_TYPE_FILS_SHA256 == auth_type) || \ + (eCSR_AUTH_TYPE_FILS_SHA384 == auth_type)) + +#define CSR_IS_AKM_FT_FILS(auth_type) \ + ((eCSR_AUTH_TYPE_FT_FILS_SHA256 == (auth_type)) || \ + (eCSR_AUTH_TYPE_FT_FILS_SHA384 == (auth_type))) + +#define CSR_IS_FW_FT_SUITEB_SUPPORTED(fw_akm_bitmap) \ + (((fw_akm_bitmap) & (1 << AKM_FT_SUITEB_SHA384)) ? true : false) + +#define CSR_IS_FW_FT_FILS_SUPPORTED(fw_akm_bitmap) \ + (((fw_akm_bitmap) & (1 << AKM_FT_FILS)) ? true : false) + +QDF_STATUS csr_set_channels(struct mac_context *mac, + struct csr_config_params *pParam); + +/* enum to string conversion for debug output */ +const char *get_e_roam_cmd_status_str(eRoamCmdStatus val); +const char *get_e_csr_roam_result_str(eCsrRoamResult val); +const char *csr_phy_mode_str(eCsrPhyMode phy_mode); + +#ifdef FEATURE_WLAN_ESE +typedef void (*tCsrTsmStatsCallback)(tAniTrafStrmMetrics tsmMetrics, + void *pContext); +#endif /* FEATURE_WLAN_ESE */ +typedef void (*tCsrSnrCallback)(int8_t snr, void *pContext); + +/** + * csr_roam_issue_ft_preauth_req() - Initiate Preauthentication request + * @max_ctx: Global MAC context + * @session_id: SME Session ID + * @bss_desc: BSS descriptor + * + * Return: Success or Failure + */ +#ifdef WLAN_FEATURE_HOST_ROAM +QDF_STATUS csr_roam_issue_ft_preauth_req(struct mac_context *mac_ctx, + uint32_t session_id, + struct bss_description *bss_desc); + +QDF_STATUS csr_continue_lfr2_connect(struct mac_context *mac, + uint32_t session_id); +#else +static inline +QDF_STATUS csr_roam_issue_ft_preauth_req(struct mac_context *mac_ctx, + uint32_t session_id, + struct bss_description *bss_desc) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline +QDF_STATUS csr_continue_lfr2_connect(struct mac_context *mac, + uint32_t session_id) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif + +typedef void (*csr_readyToSuspendCallback)(void *pContext, bool suspended); +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +typedef void (*csr_readyToExtWoWCallback)(void *pContext, bool status); +#endif +typedef void (*csr_link_status_callback)(uint8_t status, void *context); +#ifdef FEATURE_WLAN_TDLS +void csr_roam_fill_tdls_info(struct mac_context *mac_ctx, + struct csr_roam_info *roam_info, + struct join_rsp *join_rsp); +#else +static inline void csr_roam_fill_tdls_info(struct mac_context *mac_ctx, + struct csr_roam_info *roam_info, + struct join_rsp *join_rsp) +{} +#endif + +typedef void (*sme_get_raom_scan_ch_callback)( + hdd_handle_t hdd_handle, + struct roam_scan_ch_resp *roam_ch, + void *context); + +#if defined(WLAN_LOGGING_SOCK_SVC_ENABLE) && \ + defined(FEATURE_PKTLOG) && !defined(REMOVE_PKT_LOG) +/** + * csr_packetdump_timer_stop() - stops packet dump timer + * + * This function is used to stop packet dump timer + * + * Return: None + * + */ +void csr_packetdump_timer_stop(void); + +/** + * csr_packetdump_timer_start() - start packet dump timer + * + * This function is used to start packet dump timer + * + * Return: None + * + */ +void csr_packetdump_timer_start(void); +#else +static inline void csr_packetdump_timer_stop(void) {} +static inline void csr_packetdump_timer_start(void) {} +#endif + +/** + * csr_get_channel_status() - get chan info via channel number + * @mac: Pointer to Global MAC structure + * @chan_freq: channel frequency + * + * Return: chan status info + */ +struct lim_channel_status * +csr_get_channel_status(struct mac_context *mac, uint32_t chan_freq); + +/** + * csr_clear_channel_status() - clear chan info + * @mac: Pointer to Global MAC structure + * + * Return: none + */ +void csr_clear_channel_status(struct mac_context *mac); + +/** + * csr_update_owe_info() - Update OWE info + * @mac: mac context + * @assoc_ind: assoc ind + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_update_owe_info(struct mac_context *mac, + struct assoc_ind *assoc_ind); + +/** + * csr_send_roam_offload_init_msg() - Send roam enable/disable flag to fw + * @mac: mac context + * @vdev_id: vdev id + * @enable: enable/disable roam flag + * + * Return: QDF_STATUS + */ +QDF_STATUS +csr_send_roam_offload_init_msg(struct mac_context *mac, uint32_t vdev_id, + bool enable); + +typedef void (*csr_ani_callback)(int8_t *ani, void *context); + +#ifdef WLAN_FEATURE_11W +/** + * csr_update_pmf_cap_from_connected_profile() - Update pmf cap from profile + * @profile: connected profile + * @filter: scan filter + * + * Return: None + */ +void +csr_update_pmf_cap_from_connected_profile(tCsrRoamConnectedProfile *profile, + struct scan_filter *filter); +#else +void +csr_update_pmf_cap_from_connected_profile(tCsrRoamConnectedProfile *profile, + struct scan_filter *filter); +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/csr_host_scan_roam.h b/drivers/staging/qcacld-3.0/core/sme/inc/csr_host_scan_roam.h new file mode 100644 index 0000000000000000000000000000000000000000..292c93d74b547584d7ea4ba0a0c494d7ab9a6cf9 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/csr_host_scan_roam.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CSR_HOST_SCAN_ROAM_H +#define CSR_HOST_SCAN_ROAM_H + +#ifdef WLAN_FEATURE_HOST_ROAM +/** + * csr_roam_issue_reassociate() - Issue Reassociate + * @mac: Global MAC Context + * @vdev_id: SME vdev ID + * @bss_desc: BSS Descriptor + * @ies: Pointer to the IE's + * @roam_profile: Roaming profile + * + * Return: Success or Failure + */ +QDF_STATUS +csr_roam_issue_reassociate(struct mac_context *mac, uint32_t vdev_id, + struct bss_description *bss_desc, + tDot11fBeaconIEs *bcn_ies, + struct csr_roam_profile *roam_profile); + +/** + * csr_roam_issue_reassociate_cmd() - Issue the reassociate command + * @mac: Global MAC Context + * @vdev_id: SME vdev ID + * + * Return: Success or Failure status + */ +QDF_STATUS +csr_roam_issue_reassociate_cmd(struct mac_context *mac, uint32_t vdev_id); + +/** + * csr_neighbor_roam_process_scan_results() - build roaming candidate list + * + * @mac_ctx: The handle returned by mac_open. + * @sessionid: Session information + * @scan_results_list: List obtained from csr_scan_get_result() + * + * This function applies various candidate checks like LFR, 11r, preauth, ESE + * and builds a roamable AP list. It applies age limit only if no suitable + * recent candidates are found. + * + * Output list is built in mac_ctx->roam.neighborRoamInfo[sessionid]. + * + * Return: void + */ +void +csr_neighbor_roam_process_scan_results(struct mac_context *mac_ctx, + uint8_t vdev_id, + tScanResultHandle *scan_results_list); +/** + * csr_neighbor_roam_trigger_handoff() - Start roaming + * @mac_ctx: Global MAC Context + * @vdev_id: SME vdev ID + * + * Return: None + */ +void csr_neighbor_roam_trigger_handoff(struct mac_context *mac_ctx, + uint8_t vdev_id); +/** + * csr_neighbor_roam_process_scan_complete() - Post process the scan results + * @mac: Global MAC Context + * @vdev_id: SME vdev ID + * + * Return: Success or Failure + */ +QDF_STATUS csr_neighbor_roam_process_scan_complete(struct mac_context *mac, + uint8_t vdev_id); + +/** + * csr_neighbor_roam_candidate_found_ind_hdlr() - Handle roam candidate found + * indication from firmware + * @mac_ctx: Pointer to Global MAC structure + * @msg_buf: pointer to msg buff + * + * This function is called by CSR as soon as TL posts the candidate + * found indication to SME via MC thread + * + * Return: QDF_STATUS_SUCCESS on success, corresponding error code otherwise + */ +QDF_STATUS csr_neighbor_roam_candidate_found_ind_hdlr(struct mac_context *mac, + void *msg_buf); + +/** + * csr_neighbor_roam_free_roamable_bss_list() - Frees roamable APs list + * @mac_ctx: The handle returned by mac_open. + * @llist: Neighbor Roam BSS List to be emptied + * + * Empties and frees all the nodes in the roamable AP list + * + * Return: none + */ +void csr_neighbor_roam_free_roamable_bss_list(struct mac_context *mac_ctx, + tDblLinkList *llist); + +/** + * csr_neighbor_roam_remove_roamable_ap_list_entry() - Remove roamable + * candidate APs from list + * @mac_ctx: Pointer to Global MAC structure + * @list: The list from which the entry should be removed + * @neigh_entry: Neighbor Roam BSS Node to be removed + * + * This function removes a given entry from the given list + * + * Return: true if successfully removed, else false + */ +bool csr_neighbor_roam_remove_roamable_ap_list_entry(struct mac_context *mac, + tDblLinkList *list, tpCsrNeighborRoamBSSInfo neigh_entry); + +/** + * csr_neighbor_roam_next_roamable_ap() - Get next AP from roamable AP list + * @mac_ctx - The handle returned by mac_open. + * @plist - The list from which the entry should be returned + * @neighbor_entry - Neighbor Roam BSS Node whose next entry should be returned + * + * Gets the entry next to passed entry. If NULL is passed, return the entry + * in the head of the list + * + * Return: Neighbor Roam BSS Node to be returned + */ +tpCsrNeighborRoamBSSInfo +csr_neighbor_roam_next_roamable_ap(struct mac_context *mac_ctx, + tDblLinkList *llist, + tpCsrNeighborRoamBSSInfo neighbor_entry); + +/** + * csr_neighbor_roam_request_handoff() - Handoff to a different AP + * @mac_ctx: Pointer to Global MAC structure + * @vdev_id: vdev id + * + * This function triggers actual switching from one AP to the new AP. + * It issues disassociate with reason code as Handoff and CSR as a part of + * handling disassoc rsp, issues reassociate to the new AP + * + * Return: none + */ +void +csr_neighbor_roam_request_handoff(struct mac_context *mac, uint8_t vdev_id); + +/** + * csr_neighbor_roam_get_handoff_ap_info - Identifies the best AP for roaming + * @mac: mac context + * @session_id: vdev Id + * @hand_off_node: AP node that is the handoff candidate returned + * + * This function returns the best possible AP for handoff. For 11R case, it + * returns the 1st entry from pre-auth done list. For non-11r case, it returns + * the 1st entry from roamable AP list + * + * Return: true if able find handoff AP, false otherwise + */ +bool +csr_neighbor_roam_get_handoff_ap_info(struct mac_context *mac, + tpCsrNeighborRoamBSSInfo hand_off_node, + uint8_t vdev_id); + +/** + * csr_neighbor_roam_is_handoff_in_progress() - Check whether roam handoff is + * in progress + * @mac_ctx: Pointer to Global MAC structure + * @vdev_id: vdev id + * + * This function returns whether handoff is in progress or not based on + * the current neighbor roam state + * + * Return: true if reassoc in progress, false otherwise + */ +bool csr_neighbor_roam_is_handoff_in_progress(struct mac_context *mac, + uint8_t vdev_id); + +/** + * csr_neighbor_roam_free_neighbor_roam_bss_node() - Free Roam BSS node + * @mac_ctx: Pointer to Global MAC structure + * @roam_bss_node: Neighbor Roam BSS Node to be freed + * + * This function frees all the internal pointers CSR NeighborRoam BSS Info + * and also frees the node itself + * + * Return: None + */ +void +csr_neighbor_roam_free_neighbor_roam_bss_node(struct mac_context *mac, + tpCsrNeighborRoamBSSInfo roam_bss_node); + +#else +static inline QDF_STATUS +csr_roam_issue_reassociate(struct mac_context *mac, uint32_t vdev_id, + struct bss_description *bss_desc, + tDot11fBeaconIEs *bcn_ies, + struct csr_roam_profile *roam_profile) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline void +csr_neighbor_roam_process_scan_results(struct mac_context *mac_ctx, + uint8_t vdev_id, + tScanResultHandle *scan_results_list) +{} + +static inline void +csr_neighbor_roam_trigger_handoff(struct mac_context *mac_ctx, uint8_t vdev_id) +{} + +static inline void +csr_neighbor_roam_request_handoff(struct mac_context *mac, uint8_t vdev_id) +{} + +static inline bool +csr_neighbor_roam_get_handoff_ap_info(struct mac_context *mac, + tpCsrNeighborRoamBSSInfo pHandoffNode, + uint8_t vdev_id) +{ + return false; +} + +static inline void +csr_neighbor_roam_free_roamable_bss_list(struct mac_context *mac_ctx, + tDblLinkList *llist) +{} + +static inline QDF_STATUS +csr_neighbor_roam_process_scan_complete(struct mac_context *mac, + uint8_t vdev_id) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline QDF_STATUS +csr_roam_issue_reassociate_cmd(struct mac_context *mac, uint32_t vdev_id) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline bool +csr_neighbor_roam_is_handoff_in_progress(struct mac_context *mac, + uint8_t vdev_id) +{ + return false; +} + +static inline QDF_STATUS +csr_neighbor_roam_candidate_found_ind_hdlr(struct mac_context *mac, + void *msg_buf) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif /* WLAN_FEATURE_HOST_ROAM */ +#endif /* CSR_HOST_SCAN_ROAM_H */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/csr_internal.h b/drivers/staging/qcacld-3.0/core/sme/inc/csr_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..92615cdfacfd7cd65a382d47fbf5276e18f7af7a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/csr_internal.h @@ -0,0 +1,1224 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file csr_internal.h + * + * Define internal data structure for MAC. + */ +#ifndef CSRINTERNAL_H__ +#define CSRINTERNAL_H__ + +#include "qdf_status.h" +#include "qdf_lock.h" + +#include "qdf_mc_timer.h" +#include "csr_support.h" +#include "cds_reg_service.h" +#include "wlan_scan_public_structs.h" +#include "csr_neighbor_roam.h" + +#include "sir_types.h" +#include "wlan_mlme_public_struct.h" +#include "csr_host_scan_roam.h" + +#define CSR_NUM_RSSI_CAT 15 +#define CSR_ROAM_SCAN_CHANNEL_SWITCH_TIME 3 + +/* No of sessions to be supported, and a session is for Infra, IBSS or BT-AMP */ +#define CSR_IS_SESSION_VALID(mac, sessionId) \ + ((sessionId) < WLAN_MAX_VDEVS && \ + (mac)->roam.roamSession[(sessionId)].sessionActive) + +#define CSR_GET_SESSION(mac, sessionId) \ + (sessionId < WLAN_MAX_VDEVS ? \ + &(mac)->roam.roamSession[(sessionId)] : NULL) + +#define CSR_IS_SESSION_ANY(sessionId) (sessionId == SME_SESSION_ID_ANY) +#define CSR_IS_DFS_CH_ROAM_ALLOWED(mac_ctx) \ + ( \ + ((((mac_ctx)->mlme_cfg->lfr.roaming_dfs_channel) != \ + ROAMING_DFS_CHANNEL_DISABLED) ? true : false) \ + ) +#define CSR_IS_SELECT_5GHZ_MARGIN(mac) \ + ( \ + (((mac)->roam.configParam.nSelect5GHzMargin) ? true : false) \ + ) +#define CSR_IS_ROAM_PREFER_5GHZ(mac) \ + ( \ + ((mac)->mlme_cfg->lfr.roam_prefer_5ghz) \ + ) +#define CSR_IS_ROAM_INTRA_BAND_ENABLED(mac) \ + ( \ + ((mac)->mlme_cfg->lfr.roam_intra_band) \ + ) +#define CSR_IS_FASTROAM_IN_CONCURRENCY_INI_FEATURE_ENABLED(mac) \ + ( \ + ((mac)->mlme_cfg->lfr.enable_fast_roam_in_concurrency) \ + ) +#define CSR_IS_CHANNEL_24GHZ(chnNum) \ + (((chnNum) > 0) && ((chnNum) <= 14)) +/* Support for "Fast roaming" (i.e., ESE, LFR, or 802.11r.) */ +#define CSR_BG_SCAN_OCCUPIED_CHANNEL_LIST_LEN 15 + +/* Used to determine what to set to the MLME_DOT11_MODE */ +enum csr_cfgdot11mode { + eCSR_CFG_DOT11_MODE_ABG, + eCSR_CFG_DOT11_MODE_11A, + eCSR_CFG_DOT11_MODE_11B, + eCSR_CFG_DOT11_MODE_11G, + eCSR_CFG_DOT11_MODE_11N, + eCSR_CFG_DOT11_MODE_11AC, + eCSR_CFG_DOT11_MODE_11G_ONLY, + eCSR_CFG_DOT11_MODE_11N_ONLY, + eCSR_CFG_DOT11_MODE_11AC_ONLY, + /* This value can never set to CFG. Its for CSR's internal use */ + eCSR_CFG_DOT11_MODE_AUTO, + eCSR_CFG_DOT11_MODE_11AX, + eCSR_CFG_DOT11_MODE_11AX_ONLY, +}; + +enum csr_scan_reason { + eCsrScanForSsid, +}; + +enum csr_roam_reason { + /* Roaming because we've not established the initial connection. */ + eCsrNoConnection, + /* roaming because LIM reported a cap change in the associated AP. */ + eCsrCapsChange, + /* roaming because someone asked us to Disassoc & stay disassociated. */ + eCsrForcedDisassoc, + /* roaming because an 802.11 request was issued to the driver. */ + eCsrHddIssued, + /* roaming because we need to force a Disassoc due to MIC failure */ + eCsrForcedDisassocMICFailure, + eCsrHddIssuedReassocToSameAP, + eCsrSmeIssuedReassocToSameAP, + /* roaming because someone asked us to deauth and stay disassociated. */ + eCsrForcedDeauth, + /* will be issued by Handoff logic to disconect from current AP */ + eCsrSmeIssuedDisassocForHandoff, + /* will be issued by Handoff logic to join a new AP with same profile */ + eCsrSmeIssuedAssocToSimilarAP, + eCsrForcedIbssLeave, + eCsrStopBss, + eCsrSmeIssuedFTReassoc, + eCsrForcedDisassocSta, + eCsrForcedDeauthSta, + eCsrPerformPreauth, + /* Roaming disabled from driver during connect/start BSS */ + ecsr_driver_disabled, +}; + +enum csr_roam_substate { + eCSR_ROAM_SUBSTATE_NONE = 0, + eCSR_ROAM_SUBSTATE_START_BSS_REQ, + eCSR_ROAM_SUBSTATE_JOIN_REQ, + eCSR_ROAM_SUBSTATE_REASSOC_REQ, + eCSR_ROAM_SUBSTATE_DISASSOC_REQ, + eCSR_ROAM_SUBSTATE_STOP_BSS_REQ, + /* Continue the current roam command after disconnect */ + eCSR_ROAM_SUBSTATE_DISCONNECT_CONTINUE_ROAMING, + eCSR_ROAM_SUBSTATE_AUTH_REQ, + eCSR_ROAM_SUBSTATE_CONFIG, + eCSR_ROAM_SUBSTATE_DEAUTH_REQ, + eCSR_ROAM_SUBSTATE_DISASSOC_NOTHING_TO_JOIN, + eCSR_ROAM_SUBSTATE_DISASSOC_REASSOC_FAILURE, + eCSR_ROAM_SUBSTATE_DISASSOC_FORCED, + eCSR_ROAM_SUBSTATE_WAIT_FOR_KEY, + eCSR_ROAM_SUBSTATE_DISASSOC_HANDOFF, + eCSR_ROAM_SUBSTATE_JOINED_NO_TRAFFIC, + eCSR_ROAM_SUBSTATE_JOINED_NON_REALTIME_TRAFFIC, + eCSR_ROAM_SUBSTATE_JOINED_REALTIME_TRAFFIC, + eCSR_ROAM_SUBSTATE_DISASSOC_STA_HAS_LEFT, + /* max is 15 unless the bitfield is expanded... */ +}; + +enum csr_roam_state { + eCSR_ROAMING_STATE_STOP = 0, + eCSR_ROAMING_STATE_IDLE, + eCSR_ROAMING_STATE_JOINING, + eCSR_ROAMING_STATE_JOINED, +}; + +enum csr_join_state { + eCsrContinueRoaming, + eCsrStopRoaming, + eCsrStartIbss, + eCsrStartIbssSameIbss, + eCsrReassocToSelfNoCapChange, + eCsrStopRoamingDueToConcurrency, + +}; + +enum csr_roaming_reason { + eCsrNotRoaming, + eCsrLostlinkRoamingDisassoc, + eCsrLostlinkRoamingDeauth, + eCsrDynamicRoaming, + eCsrReassocRoaming, +}; + +enum csr_roam_wmstatus_changetypes { + eCsrDisassociated, + eCsrDeauthenticated +}; + +enum csr_diagwlan_status_eventsubtype { + eCSR_WLAN_STATUS_CONNECT = 0, + eCSR_WLAN_STATUS_DISCONNECT +}; + +enum csr_diagwlan_status_eventreason { + eCSR_REASON_UNSPECIFIED = 0, + eCSR_REASON_USER_REQUESTED, + eCSR_REASON_MIC_ERROR, + eCSR_REASON_DISASSOC, + eCSR_REASON_DEAUTH, + eCSR_REASON_HANDOFF, + eCSR_REASON_ROAM_SYNCH_IND, + eCSR_REASON_ROAM_SYNCH_CNF, + eCSR_REASON_ROAM_HO_FAIL, +}; + +struct csr_channel { + uint8_t numChannels; + uint32_t channel_freq_list[CFG_VALID_CHANNEL_LIST_LEN]; +}; + +struct bss_config_param { + eCsrMediaAccessType qosType; + tSirMacSSid SSID; + enum csr_cfgdot11mode uCfgDot11Mode; + enum reg_wifi_band band; + tAniAuthType authType; + eCsrEncryptionType encType; + uint32_t uShortSlotTime; + uint32_t uHTSupport; + uint32_t uPowerLimit; + uint32_t uHeartBeatThresh; + uint32_t uJoinTimeOut; + tSirMacCapabilityInfo BssCap; + bool f11hSupport; + ePhyChanBondState cbMode; +}; + +struct csr_roamstart_bssparams { + tSirMacSSid ssId; + + /* + * This is the BSSID for the party we want to + * join (only use for IBSS or WDS). + */ + struct qdf_mac_addr bssid; + tSirNwType sirNwType; + ePhyChanBondState cbMode; + tSirMacRateSet operationalRateSet; + tSirMacRateSet extendedRateSet; + uint32_t operation_chan_freq; + struct ch_params ch_params; + enum csr_cfgdot11mode uCfgDot11Mode; + uint8_t privacy; + bool fwdWPSPBCProbeReq; + bool protEnabled; + bool obssProtEnabled; + tAniAuthType authType; + uint16_t beaconInterval; /* If this is 0, SME'll fill in for caller */ + uint16_t ht_protection; + uint32_t dtimPeriod; + uint8_t ApUapsdEnable; + uint8_t ssidHidden; + uint8_t wps_state; + enum QDF_OPMODE bssPersona; + uint16_t nRSNIELength; /* If 0, pRSNIE is ignored. */ + uint8_t *pRSNIE; /* If not null, it has IE byte stream for RSN */ + /* Flag used to indicate update beaconInterval */ + bool updatebeaconInterval; +#ifdef WLAN_FEATURE_11W + bool mfpCapable; + bool mfpRequired; +#endif + struct add_ie_params add_ie_params; + uint8_t sap_dot11mc; + uint16_t beacon_tx_rate; + uint32_t cac_duration_ms; + uint32_t dfs_regdomain; +}; + +struct roam_cmd { + uint32_t roamId; + enum csr_roam_reason roamReason; + struct csr_roam_profile roamProfile; + tScanResultHandle hBSSList; /* BSS list fits the profile */ + /* + * point to the current BSS in the list that is roaming. + * It starts from head to tail + * */ + tListElem *pRoamBssEntry; + + /* the last BSS we try and failed */ + struct bss_description *pLastRoamBss; + bool fReleaseBssList; /* whether to free hBSSList */ + bool fReleaseProfile; /* whether to free roamProfile */ + bool fReassoc; /* whether this cmd is for reassoc */ + /* whether mac->roam.pCurRoamProfile needs to be updated */ + bool fUpdateCurRoamProfile; + /* + * this is for CSR internal used only. And it should not be assigned + * when creating the command. This causes the roam cmd not todo anything + */ + bool fReassocToSelfNoCapChange; + + bool fStopWds; + tSirMacAddr peerMac; + tSirMacReasonCodes reason; + tSirMacReasonCodes disconnect_reason; +}; + +struct setkey_cmd { + uint32_t roamId; + eCsrEncryptionType encType; + enum csr_akm_type authType; + tAniKeyDirection keyDirection; /* Tx, Rx or Tx-and-Rx */ + struct qdf_mac_addr peermac; /* Peer's MAC address. ALL 1's for group key */ + uint8_t paeRole; /* 0 for supplicant */ + uint8_t keyId; /* Kye index */ + uint8_t keyLength; /* Number of bytes containing the key in pKey */ + uint8_t Key[CSR_MAX_KEY_LEN]; + uint8_t keyRsc[WLAN_CRYPTO_RSC_SIZE]; +}; + +struct wmstatus_changecmd { + enum csr_roam_wmstatus_changetypes Type; + union { + struct deauth_ind DeauthIndMsg; + struct disassoc_ind DisassocIndMsg; + } u; + +}; + +struct delstafor_sessionCmd { + /* Session self mac addr */ + tSirMacAddr self_mac_addr; + csr_session_close_cb session_close_cb; + void *context; +}; + +/* + * Neighbor Report Params Bitmask + */ +#define NEIGHBOR_REPORT_PARAMS_TIME_OFFSET 0x01 +#define NEIGHBOR_REPORT_PARAMS_LOW_RSSI_OFFSET 0x02 +#define NEIGHBOR_REPORT_PARAMS_BMISS_COUNT_TRIGGER 0x04 +#define NEIGHBOR_REPORT_PARAMS_PER_THRESHOLD_OFFSET 0x08 +#define NEIGHBOR_REPORT_PARAMS_CACHE_TIMEOUT 0x10 +#define NEIGHBOR_REPORT_PARAMS_MAX_REQ_CAP 0x20 +#define NEIGHBOR_REPORT_PARAMS_ALL 0x3F + +struct csr_config { + uint32_t agingCount; + uint32_t channelBondingMode24GHz; + uint32_t channelBondingMode5GHz; + eCsrPhyMode phyMode; + enum csr_cfgdot11mode uCfgDot11Mode; + uint32_t HeartbeatThresh50; + uint32_t HeartbeatThresh24; + eCsrRoamWmmUserModeType WMMSupportMode; + bool Is11eSupportEnabled; + bool ProprietaryRatesEnabled; + bool fenableMCCMode; + bool mcc_rts_cts_prot_enable; + bool mcc_bcast_prob_resp_enable; + uint8_t fAllowMCCGODiffBI; + uint32_t ad_hoc_ch_freq_2g; + uint32_t ad_hoc_ch_freq_5g; + /* each RSSI category has one value */ + uint32_t BssPreferValue[CSR_NUM_RSSI_CAT]; + int RSSICat[CSR_NUM_RSSI_CAT]; + uint8_t bCatRssiOffset; /* to set RSSI difference for each category */ + uint32_t statsReqPeriodicity; /* stats req freq while in fullpower */ + uint32_t statsReqPeriodicityInPS;/* stats req freq while in powersave */ + uint32_t dtimPeriod; + bool ssidHidden; + struct mawc_params csr_mawc_config; + uint8_t isRoamOffloadScanEnabled; + bool nRoamScanControl; + + /* + * Remove this code once SLM_Sessionization is supported + * BMPS_WORKAROUND_NOT_NEEDED + */ + bool doBMPSWorkaround; + /* To enable scanning 2g channels twice on single scan req from HDD */ + bool fScanTwice; + uint32_t nVhtChannelWidth; + bool send_smps_action; + uint8_t disable_high_ht_mcs_2x2; + /* + * Enable/Disable heartbeat offload + */ + bool enableHeartBeatOffload; + uint8_t isCoalesingInIBSSAllowed; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + uint8_t cc_switch_mode; +#endif + bool obssEnabled; + uint8_t conc_custom_rule1; + uint8_t conc_custom_rule2; + uint8_t is_sta_connection_in_5gz_enabled; + struct roam_ext_params roam_params; + bool vendor_vht_sap; + struct csr_sta_roam_policy_params sta_roam_policy; + bool enable_bcast_probe_rsp; + bool is_fils_enabled; + enum force_1x1_type is_force_1x1; + uint8_t oce_feature_bitmap; + uint32_t offload_11k_enable_bitmask; + bool wep_tkip_in_he; + struct csr_neighbor_report_offload_params neighbor_report_offload; +}; + +struct csr_channel_powerinfo { + tListElem link; + uint32_t first_chan_freq; + uint8_t numChannels; + uint8_t txPower; + uint8_t interChannelOffset; +}; + +struct csr_roam_joinstatus { + tSirResultCodes status_code; + /* + * this is set to unspecified if status_code indicates timeout. + * Or it is the failed reason from the other BSS(per 802.11 spec) + */ + uint32_t reasonCode; + tSirMacAddr bssId; +}; + +struct csr_scanstruct { + tSirScanType curScanType; + struct csr_channel channels11d; + struct channel_power defaultPowerTable[CFG_VALID_CHANNEL_LIST_LEN]; + uint32_t numChannelsDefault; + struct csr_channel base_channels; /* The channel base to work on */ + tDblLinkList channelPowerInfoList24; + tDblLinkList channelPowerInfoList5G; + uint32_t nLastAgeTimeOut; + uint32_t nAgingCountDown; + uint8_t countryCodeDefault[CFG_COUNTRY_CODE_LEN]; + uint8_t countryCodeCurrent[CFG_COUNTRY_CODE_LEN]; + uint8_t countryCode11d[CFG_COUNTRY_CODE_LEN]; + v_REGDOMAIN_t domainIdDefault; /* default regulatory domain */ + v_REGDOMAIN_t domainIdCurrent; /* current regulatory domain */ + + /* + * in 11d IE from probe rsp or beacons of neighboring APs + * will use the most popular one (max count) + */ + uint8_t countryCodeElected[CFG_COUNTRY_CODE_LEN]; + /* + * Customer wants to optimize the scan time. Avoiding scans(passive) + * on DFS channels while swipping through both bands can save some time + * (apprx 1.3 sec) + */ + uint8_t fEnableDFSChnlScan; + bool fDropScanCmd; /* true means we don't accept scan commands */ + + /* This includes all channels on which candidate APs are found */ + struct csr_channel occupiedChannels[WLAN_MAX_VDEVS]; + int8_t roam_candidate_count[WLAN_MAX_VDEVS]; + int8_t inScanResultBestAPRssi; + bool fcc_constraint; + bool pending_channel_list_req; + wlan_scan_requester requester_id; +}; + +/* + * Save the connected information. This structure + connectedProfile + * should contain all information about the connection + */ +struct csr_roam_connectedinfo { + uint32_t nBeaconLength; + uint32_t nAssocReqLength; + uint32_t nAssocRspLength; + /* len of the parsed RIC resp IEs received in reassoc response */ + uint32_t nRICRspLength; +#ifdef FEATURE_WLAN_ESE + uint32_t nTspecIeLength; +#endif + /* + * Point to a buffer contain the beacon, assoc req, assoc rsp frame, in + * that order user needs to use nBeaconLength, nAssocReqLength, + * nAssocRspLength to desice where each frame starts and ends. + */ + uint8_t *pbFrames; + uint8_t staId; +}; + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +enum csr_roamoffload_authstatus { + /* reassociation is done but couldn't finish security handshake */ + eSIR_ROAM_AUTH_STATUS_CONNECTED = 1, + /* roam successfully completed by firmware */ + eSIR_ROAM_AUTH_STATUS_AUTHENTICATED = 2, + /* unknown error */ + eSIR_ROAM_AUTH_STATUS_UNKNOWN = 0xff +}; +#endif + +struct csr_roam_stored_profile { + uint32_t session_id; + struct csr_roam_profile profile; + tScanResultHandle bsslist_handle; + enum csr_roam_reason reason; + uint32_t roam_id; + bool imediate_flag; + bool clear_flag; +}; + +/** + * struct scan_cmd_info - Scan cache entry node + * @scan_id: scan id + * @scan_reason: scan reason + * @profile: roam profile + * @roam_id: Roam id + * @roambssentry: scan entries + */ +struct scan_cmd_info { + wlan_scan_id scan_id; + enum csr_scan_reason scan_reason; + struct csr_roam_profile *profile; + uint32_t roam_id; + tListElem *roambssentry; +}; + +/** + * struct csr_disconnect_stats - Disconnect Stats per session + * @disconnection_cnt: total no. of disconnections + * @disconnection_by_app: diconnections triggered by application + * @disassoc_by_peer: disassoc sent by peer + * @deauth_by_peer: deauth sent by peer + * @bmiss: disconnect triggered by beacon miss + * @peer_kickout: disconnect triggered by peer kickout + */ +struct csr_disconnect_stats { + uint32_t disconnection_cnt; + uint32_t disconnection_by_app; + uint32_t disassoc_by_peer; + uint32_t deauth_by_peer; + uint32_t bmiss; + uint32_t peer_kickout; +}; + +/** + * struct csr_roam_session - CSR per-vdev context + * @vdev_id: ID of the vdev for which this entry is applicable + * @is_bcn_recv_start: Allow to process bcn recv indication + * @beacon_report_do_not_resume: Do not resume the beacon reporting after scan + */ +struct csr_roam_session { + union { + uint8_t sessionId; + uint8_t vdev_id; + }; + bool sessionActive; /* true if it is used */ + + /* For BT-AMP station, this serve as BSSID for self-BSS. */ + struct qdf_mac_addr self_mac_addr; + + eCsrConnectState connectState; + tCsrRoamConnectedProfile connectedProfile; + struct csr_roam_connectedinfo connectedInfo; + struct csr_roam_connectedinfo prev_assoc_ap_info; + struct csr_roam_profile *pCurRoamProfile; + struct bss_description *pConnectBssDesc; + uint16_t NumPmkidCache; /* valid number of pmkid in the cache*/ + uint16_t curr_cache_idx; /* the index in pmkidcache to write next to */ + tPmkidCacheInfo PmkidCacheInfo[CSR_MAX_PMKID_ALLOWED]; + uint8_t cJoinAttemps; + int32_t sPendingCommands; /* 0 means CSR is ok to low power */ +#ifdef FEATURE_WLAN_WAPI + uint16_t NumBkidCache; + tBkidCacheInfo BkidCacheInfo[CSR_MAX_BKID_ALLOWED]; +#endif /* FEATURE_WLAN_WAPI */ + /* + * indicate whether CSR is roaming + * (either via lostlink or dynamic roaming) + */ + bool fRoaming; + /* + * to remember some parameters needed for START_BSS. + * All member must be set every time we try to join or start an IBSS + */ + struct csr_roamstart_bssparams bssParams; + /* the byte count of pWpaRsnIE; */ + uint32_t nWpaRsnReqIeLength; + /* contain the WPA/RSN IE in assoc req or one sent in beacon(IBSS) */ + uint8_t *pWpaRsnReqIE; + /* the byte count for pWpaRsnRspIE */ + uint32_t nWpaRsnRspIeLength; + /* this contain the WPA/RSN IE in beacon/probe rsp */ + uint8_t *pWpaRsnRspIE; +#ifdef FEATURE_WLAN_WAPI + /* the byte count of pWapiReqIE; */ + uint32_t nWapiReqIeLength; + /* this contain the WAPI IE in assoc req or one sent in beacon (IBSS) */ + uint8_t *pWapiReqIE; + /* the byte count for pWapiRspIE */ + uint32_t nWapiRspIeLength; + /* this contain the WAPI IE in beacon/probe rsp */ + uint8_t *pWapiRspIE; +#endif /* FEATURE_WLAN_WAPI */ + uint32_t nAddIEScanLength; /* the byte count of pAddIeScanIE; */ + /* contains the additional IE in (unicast) probe req at time of join */ + uint8_t *pAddIEScan; + uint32_t nAddIEAssocLength; /* the byte count for pAddIeAssocIE */ + uint8_t *pAddIEAssoc; + struct csr_timer_info roamingTimerInfo; + enum csr_roaming_reason roamingReason; + bool fCancelRoaming; + qdf_mc_timer_t hTimerRoaming; +#ifdef WLAN_BCN_RECV_FEATURE + bool is_bcn_recv_start; + bool beacon_report_do_not_resume; +#endif + /* the roamResult that is used when the roaming timer fires */ + eCsrRoamResult roamResult; + /* This is the reason code for join(assoc) failure */ + struct csr_roam_joinstatus joinFailStatusCode; + /* status from PE for deauth/disassoc(lostlink) or our own dyn roam */ + uint32_t roamingStatusCode; + uint16_t NumPmkidCandidate; + tPmkidCandidateInfo PmkidCandidateInfo[CSR_MAX_PMKID_ALLOWED]; + bool fWMMConnection; + bool fQOSConnection; +#ifdef FEATURE_WLAN_ESE + tCsrEseCckmInfo eseCckmInfo; + bool isPrevApInfoValid; + tSirMacSSid prevApSSID; + struct qdf_mac_addr prevApBssid; + uint16_t clientDissSecs; + uint32_t roamTS1; + tCsrEseCckmIe suppCckmIeInfo; +#endif + uint8_t bRefAssocStartCnt; /* Tracking assoc start indication */ + struct ht_config ht_config; + struct sir_vht_config vht_config; +#ifdef WLAN_FEATURE_11AX + tDot11fIEhe_cap he_config; + uint32_t he_sta_obsspd; +#endif +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + uint8_t psk_pmk[SIR_ROAM_SCAN_PSK_SIZE]; + size_t pmk_len; + uint8_t RoamKeyMgmtOffloadEnabled; + struct roam_offload_synch_ind *roam_synch_data; + struct pmkid_mode_bits pmkid_modes; +#endif + tftSMEContext ftSmeContext; + /* This count represents the number of bssid's we try to join. */ + uint8_t join_bssid_count; + struct csr_roam_stored_profile stored_roam_profile; + bool ch_switch_in_progress; + bool roam_synch_in_progress; + bool supported_nss_1x1; + uint8_t vdev_nss; + uint8_t nss; + bool nss_forced_1x1; + bool disable_hi_rssi; + bool dhcp_done; + tSirMacReasonCodes disconnect_reason; + uint8_t uapsd_mask; + struct scan_cmd_info scan_info; + qdf_mc_timer_t roaming_offload_timer; + bool is_fils_connection; + uint16_t fils_seq_num; + bool discon_in_progress; + bool is_adaptive_11r_connection; + struct csr_disconnect_stats disconnect_stats; + qdf_mc_timer_t join_retry_timer; +}; + +struct csr_roamstruct { + uint32_t nextRoamId; + struct csr_config configParam; + enum csr_roam_state curState[WLAN_MAX_VDEVS]; + enum csr_roam_substate curSubState[WLAN_MAX_VDEVS]; + /* + * This may or may not have the up-to-date valid channel list. It is + * used to get CFG_VALID_CHANNEL_LIST and not alloc mem all time + */ + uint32_t valid_ch_freq_list[CFG_VALID_CHANNEL_LIST_LEN]; + uint32_t numValidChannels; /* total number of channels in CFG */ + int32_t sPendingCommands; + qdf_mc_timer_t hTimerWaitForKey; /* support timeout for WaitForKey */ + struct csr_timer_info WaitForKeyTimerInfo; + struct csr_roam_session *roamSession; + tCsrNeighborRoamControlInfo neighborRoamInfo[WLAN_MAX_VDEVS]; + uint8_t isFastRoamIniFeatureEnabled; +#ifdef FEATURE_WLAN_ESE + uint8_t isEseIniFeatureEnabled; +#endif + uint8_t RoamRssiDiff; + bool isWESModeEnabled; + uint32_t deauthRspStatus; + uint8_t *pReassocResp; /* reassociation response from new AP */ + uint16_t reassocRespLen; /* length of reassociation response */ +#if defined(WLAN_LOGGING_SOCK_SVC_ENABLE) && \ + defined(FEATURE_PKTLOG) && !defined(REMOVE_PKT_LOG) + qdf_mc_timer_t packetdump_timer; +#endif + spinlock_t roam_state_lock; +}; + +#define GET_NEXT_ROAM_ID(pRoamStruct) (((pRoamStruct)->nextRoamId + 1 == 0) ? \ + 1 : (pRoamStruct)->nextRoamId) +#define CSR_IS_ROAM_STATE(mac, state, sessionId) \ + ((state) == (mac)->roam.curState[sessionId]) +#define CSR_IS_ROAM_STOP(mac, sessionId) \ + CSR_IS_ROAM_STATE((mac), eCSR_ROAMING_STATE_STOP, sessionId) +#define CSR_IS_ROAM_INIT(mac, sessionId) \ + CSR_IS_ROAM_STATE((mac), eCSR_ROAMING_STATE_INIT, sessionId) +#define CSR_IS_ROAM_JOINING(mac, sessionId) \ + CSR_IS_ROAM_STATE(mac, eCSR_ROAMING_STATE_JOINING, sessionId) +#define CSR_IS_ROAM_IDLE(mac, sessionId) \ + CSR_IS_ROAM_STATE(mac, eCSR_ROAMING_STATE_IDLE, sessionId) +#define CSR_IS_ROAM_JOINED(mac, sessionId) \ + CSR_IS_ROAM_STATE(mac, eCSR_ROAMING_STATE_JOINED, sessionId) +#define CSR_IS_ROAM_SUBSTATE(mac, subState, sessionId) \ + ((subState) == (mac)->roam.curSubState[sessionId]) +#define CSR_IS_ROAM_SUBSTATE_JOIN_REQ(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), eCSR_ROAM_SUBSTATE_JOIN_REQ, sessionId) +#define CSR_IS_ROAM_SUBSTATE_AUTH_REQ(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), eCSR_ROAM_SUBSTATE_AUTH_REQ, sessionId) +#define CSR_IS_ROAM_SUBSTATE_REASSOC_REQ(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), eCSR_ROAM_SUBSTATE_REASSOC_REQ, sessionId) +#define CSR_IS_ROAM_SUBSTATE_DISASSOC_REQ(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), eCSR_ROAM_SUBSTATE_DISASSOC_REQ, sessionId) +#define CSR_IS_ROAM_SUBSTATE_DISASSOC_NO_JOIN(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_DISASSOC_NOTHING_TO_JOIN, sessionId) +#define CSR_IS_ROAM_SUBSTATE_REASSOC_FAIL(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_DISASSOC_REASSOC_FAILURE, sessionId) +#define CSR_IS_ROAM_SUBSTATE_DISASSOC_FORCED(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_DISASSOC_FORCED, sessionId) +#define CSR_IS_ROAM_SUBSTATE_DEAUTH_REQ(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_DEAUTH_REQ, sessionId) +#define CSR_IS_ROAM_SUBSTATE_START_BSS_REQ(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_START_BSS_REQ, sessionId) +#define CSR_IS_ROAM_SUBSTATE_STOP_BSS_REQ(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_STOP_BSS_REQ, sessionId) +#define CSR_IS_ROAM_SUBSTATE_DISCONNECT_CONTINUE(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_DISCONNECT_CONTINUE_ROAMING, sessionId) +#define CSR_IS_ROAM_SUBSTATE_CONFIG(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_CONFIG, sessionId) +#define CSR_IS_ROAM_SUBSTATE_WAITFORKEY(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_WAIT_FOR_KEY, sessionId) +#define CSR_IS_ROAM_SUBSTATE_DISASSOC_HO(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_DISASSOC_HANDOFF, sessionId) +#define CSR_IS_ROAM_SUBSTATE_HO_NT(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_JOINED_NO_TRAFFIC, sessionId) +#define CSR_IS_ROAM_SUBSTATE_HO_NRT(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac), \ + eCSR_ROAM_SUBSTATE_JOINED_NON_REALTIME_TRAFFIC,\ + sessionId) +#define CSR_IS_ROAM_SUBSTATE_HO_RT(mac, sessionId) \ + CSR_IS_ROAM_SUBSTATE((mac),\ + eCSR_ROAM_SUBSTATE_JOINED_REALTIME_TRAFFIC, sessionId) +#define CSR_IS_PHY_MODE_B_ONLY(mac) \ + ((eCSR_DOT11_MODE_11b == (mac)->roam.configParam.phyMode) || \ + (eCSR_DOT11_MODE_11b_ONLY == (mac)->roam.configParam.phyMode)) + +#define CSR_IS_PHY_MODE_G_ONLY(mac) \ + (eCSR_DOT11_MODE_11g == (mac)->roam.configParam.phyMode \ + || eCSR_DOT11_MODE_11g_ONLY == (mac)->roam.configParam.phyMode) + +#define CSR_IS_PHY_MODE_A_ONLY(mac) \ + (eCSR_DOT11_MODE_11a == (mac)->roam.configParam.phyMode) + +#define CSR_IS_PHY_MODE_DUAL_BAND(phyMode) \ + ((eCSR_DOT11_MODE_abg & (phyMode)) || \ + (eCSR_DOT11_MODE_11n & (phyMode)) || \ + (eCSR_DOT11_MODE_11ac & (phyMode)) || \ + (eCSR_DOT11_MODE_11ax & (phyMode)) || \ + (eCSR_DOT11_MODE_AUTO & (phyMode))) + +#define CSR_IS_PHY_MODE_11n(phy_mode) \ + ((eCSR_DOT11_MODE_11n == phy_mode) || \ + (eCSR_DOT11_MODE_11n_ONLY == phy_mode) || \ + (eCSR_DOT11_MODE_11ac == phy_mode) || \ + (eCSR_DOT11_MODE_11ac_ONLY == phy_mode)) + +#define CSR_IS_PHY_MODE_11ac(phy_mode) \ + ((eCSR_DOT11_MODE_11ac == phy_mode) || \ + (eCSR_DOT11_MODE_11ac_ONLY == phy_mode)) + +#define CSR_IS_DOT11_MODE_11N(dot11mode) \ + ((dot11mode == eCSR_CFG_DOT11_MODE_AUTO) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11N) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AC) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11N_ONLY) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AC_ONLY) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AX) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AX_ONLY)) + +#define CSR_IS_DOT11_MODE_11AC(dot11mode) \ + ((dot11mode == eCSR_CFG_DOT11_MODE_AUTO) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AC) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AC_ONLY) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AX) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AX_ONLY)) + +#define CSR_IS_DOT11_MODE_11AX(dot11mode) \ + ((dot11mode == eCSR_CFG_DOT11_MODE_AUTO) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AX) || \ + (dot11mode == eCSR_CFG_DOT11_MODE_11AX_ONLY)) +/* Get number of bits from the index bit */ +#define CSR_GET_BITS(_val, _index, _num_bits) \ + (((_val) >> (_index)) & ((1 << (_num_bits)) - 1)) +/* + * this function returns true if the NIC is operating exclusively in + * the 2.4 GHz band, meaning. it is NOT operating in the 5.0 GHz band. + */ +#define CSR_IS_24_BAND_ONLY(mac) \ + (BIT(REG_BAND_2G) == (mac)->mlme_cfg->gen.band) + +#define CSR_IS_5G_BAND_ONLY(mac) \ + (BIT(REG_BAND_5G) == (mac)->mlme_cfg->gen.band) + +#define CSR_IS_RADIO_DUAL_BAND(mac) \ + ((BIT(REG_BAND_2G) | BIT(REG_BAND_5G)) == \ + (mac)->mlme_cfg->gen.band_capability) + +#define CSR_IS_RADIO_BG_ONLY(mac) \ + (BIT(REG_BAND_2G) == (mac)->mlme_cfg->gen.band_capability) + +/* + * this function returns true if the NIC is operating exclusively in the 5.0 GHz + * band, meaning. it is NOT operating in the 2.4 GHz band + */ +#define CSR_IS_RADIO_A_ONLY(mac) \ + (BAND_5G == (mac)->mlme_cfg->gen.band_capability) +/* this function returns true if the NIC is operating in both bands. */ +#define CSR_IS_OPEARTING_DUAL_BAND(mac) \ + ((BAND_ALL == (mac)->mlme_cfg->gen.band_capability) && \ + (BAND_ALL == (mac)->mlme_cfg->gen.band)) +/* + * this function returns true if the NIC can operate in the 5.0 GHz band + * (could operate in the 2.4 GHz band also) + */ +#define CSR_IS_OPERATING_A_BAND(mac) \ + (CSR_IS_OPEARTING_DUAL_BAND((mac)) || \ + CSR_IS_RADIO_A_ONLY((mac)) || CSR_IS_5G_BAND_ONLY((mac))) + +/* + * this function returns true if the NIC can operate in the 2.4 GHz band + * (could operate in the 5.0 GHz band also). + */ +#define CSR_IS_OPERATING_BG_BAND(mac) \ + (CSR_IS_OPEARTING_DUAL_BAND((mac)) || \ + CSR_IS_RADIO_BG_ONLY((mac)) || CSR_IS_24_BAND_ONLY((mac))) + +#define CSR_IS_ROAMING(pSession) \ + ((CSR_IS_LOSTLINK_ROAMING((pSession)->roamingReason)) || \ + (eCsrDynamicRoaming == (pSession)->roamingReason) || \ + (eCsrReassocRoaming == (pSession)->roamingReason)) +#define CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(mac) \ + (mac->mlme_cfg->wmm_params.wmm_tspec_element.ts_acm_is_off) +#define CSR_IS_LOSTLINK_ROAMING(reason) \ + ((eCsrLostlinkRoamingDisassoc == (reason)) || \ + (eCsrLostlinkRoamingDeauth == (reason))) + +#ifdef FEATURE_LFR_SUBNET_DETECTION +/* bit-4 and bit-5 indicate the subnet status */ +#define CSR_GET_SUBNET_STATUS(roam_reason) (((roam_reason) & 0x30) >> 4) +#else +#define CSR_GET_SUBNET_STATUS(roam_reason) (0) +#endif + +/** + * csr_get_vdev_dot11_mode() - get the supported dot11mode by vdev + * @mac_ctx: pointer to global mac structure + * @device_mode: vdev mode + * @curr_dot11_mode: Current dot11 mode + * + * The function return the min of supported dot11 mode and vdev type dot11mode + * for given vdev type. + * + * Return:csr_cfgdot11mode + */ +enum csr_cfgdot11mode +csr_get_vdev_dot11_mode(struct mac_context *mac, + enum QDF_OPMODE device_mode, + enum csr_cfgdot11mode curr_dot11_mode); + +QDF_STATUS csr_get_channel_and_power_list(struct mac_context *mac); + +QDF_STATUS csr_scan_filter_results(struct mac_context *mac); + +QDF_STATUS csr_set_modify_profile_fields(struct mac_context *mac, + uint32_t sessionId, tCsrRoamModifyProfileFields * + pModifyProfileFields); +QDF_STATUS csr_get_modify_profile_fields(struct mac_context *mac, + uint32_t sessionId, tCsrRoamModifyProfileFields * + pModifyProfileFields); +void csr_set_global_cfgs(struct mac_context *mac); +void csr_set_default_dot11_mode(struct mac_context *mac); +bool csr_is_conn_state_disconnected(struct mac_context *mac, + uint32_t sessionId); +bool csr_is_conn_state_connected_infra(struct mac_context *mac, + uint32_t sessionId); +bool csr_is_conn_state_connected(struct mac_context *mac, + uint32_t sessionId); +bool csr_is_conn_state_infra(struct mac_context *mac, + uint32_t sessionId); + +#ifdef QCA_IBSS_SUPPORT +/** + * csr_is_conn_state_ibss() - get the connection state for ibss session + * @mac_ctx: pointer to global mac structure + * @sessionId: session id + * + * + * Return: true if IBSS connected/disconnected state, else flase + */ +bool csr_is_conn_state_ibss(struct mac_context *mac, uint32_t sessionId); + +/** + * csr_is_conn_state_connected_ibss() - get the connected state for ibss + * @mac_ctx: pointer to global mac structure + * @sessionId: session id + * + * + * Return: true if IBSS connected state, else false + */ +bool csr_is_conn_state_connected_ibss(struct mac_context *mac, + uint32_t sessionId); + +/** + * csr_is_conn_state_connected_ibss() - get the connected state for ibss + * @mac_ctx: pointer to global mac structure + * @sessionId: session id + * + * + * Return: true if IBSS disconnected state, else false + */ +bool csr_is_conn_state_disconnected_ibss(struct mac_context *mac, + uint32_t sessionId); +#else +static inline bool +csr_is_conn_state_ibss(struct mac_context *mac, uint32_t sessionId) +{ + return false; +} + +static inline bool +csr_is_conn_state_connected_ibss(struct mac_context *mac, uint32_t sessionId) +{ + return false; +} + +static inline bool +csr_is_conn_state_disconnected_ibss(struct mac_context *mac, + uint32_t sessionId) +{ + return false; +} +#endif + +bool csr_is_conn_state_wds(struct mac_context *mac, uint32_t sessionId); +bool csr_is_conn_state_connected_wds(struct mac_context *mac, + uint32_t sessionId); +bool csr_is_conn_state_disconnected_wds(struct mac_context *mac, + uint32_t sessionId); +bool csr_is_any_session_in_connect_state(struct mac_context *mac); +bool csr_is_all_session_disconnected(struct mac_context *mac); + +/** + * csr_get_connected_infra() - get the session id of the connected infra + * @mac_ctx: pointer to global mac structure + * + * The function check if any infra is present in connected state and if present + * return the session id of the connected infra else if no infra is in connected + * state return WLAN_UMAC_VDEV_ID_MAX + * + * Return: session id of the connected infra + */ +uint8_t csr_get_connected_infra(struct mac_context *mac_ctx); +bool csr_is_concurrent_session_running(struct mac_context *mac); +bool csr_is_infra_ap_started(struct mac_context *mac); +bool csr_is_valid_mc_concurrent_session(struct mac_context *mac, + uint32_t sessionId, + struct bss_description *bss_desc); +bool csr_is_conn_state_connected_infra_ap(struct mac_context *mac, + uint32_t sessionId); +QDF_STATUS csr_get_snr(struct mac_context *mac, tCsrSnrCallback callback, + struct qdf_mac_addr bssId, void *pContext); +QDF_STATUS csr_get_config_param(struct mac_context *mac, + struct csr_config_params *pParam); +QDF_STATUS csr_change_default_config_param(struct mac_context *mac, + struct csr_config_params *pParam); +QDF_STATUS csr_msg_processor(struct mac_context *mac, void *msg_buf); +QDF_STATUS csr_open(struct mac_context *mac); +QDF_STATUS csr_init_chan_list(struct mac_context *mac, uint8_t *alpha2); +QDF_STATUS csr_close(struct mac_context *mac); +QDF_STATUS csr_start(struct mac_context *mac); +QDF_STATUS csr_stop(struct mac_context *mac); +QDF_STATUS csr_ready(struct mac_context *mac); + +#ifdef FEATURE_WLAN_WAPI +uint8_t csr_construct_wapi_ie(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrWapiIe *pWapiIe); +#endif /* FEATURE_WLAN_WAPI */ + +void csr_set_cfg_privacy(struct mac_context *mac, + struct csr_roam_profile *pProfile, + bool fPrivacy); + +/** + * csr_get_infra_operation_chan_freq() - get operating chan freq of + * given vdev id + * @mac_ctx: Pointer to mac context + * @vdev_id: vdev id + * + * Return: chan freq of given vdev id + */ +uint32_t csr_get_infra_operation_chan_freq( + struct mac_context *mac, uint8_t vdev_id); + +bool csr_is_session_client_and_connected(struct mac_context *mac, + uint8_t sessionId); +/** + * csr_get_concurrent_operation_freq() - To get concurrent operating freq + * @mac_ctx: Pointer to mac context + * + * This routine will return operating freq on FIRST BSS that is + * active/operating to be used for concurrency mode. + * If other BSS is not up or not connected it will return 0 + * + * Return: uint32_t + */ +uint32_t csr_get_concurrent_operation_freq(struct mac_context *mac_ctx); + +/** + * csr_get_beaconing_concurrent_channel() - To get concurrent operating channel + * frequency of beaconing interface + * @mac_ctx: Pointer to mac context + * @vdev_id_to_skip: channel of which vdev id to skip + * + * This routine will return operating channel of active AP/GO channel + * and will skip the channel of vdev_id_to_skip. + * If other no reqested mode is active it will return 0 + * + * Return: uint32_t + */ +uint32_t csr_get_beaconing_concurrent_channel(struct mac_context *mac_ctx, + uint8_t vdev_id_to_skip); + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +uint16_t csr_check_concurrent_channel_overlap( + struct mac_context *mac, + uint32_t sap_ch_freq, eCsrPhyMode sap_phymode, + uint8_t cc_switch_mode); +#endif +QDF_STATUS csr_roam_copy_connect_profile(struct mac_context *mac, + uint32_t sessionId, tCsrRoamConnectedProfile *pProfile); +bool csr_is_set_key_allowed(struct mac_context *mac, uint32_t sessionId); + +/* Returns whether the current association is a 11r assoc or not */ +bool csr_roam_is11r_assoc(struct mac_context *mac, uint8_t sessionId); + +#ifdef FEATURE_WLAN_ESE +/* Returns whether the current association is a ESE assoc or not */ +bool csr_roam_is_ese_assoc(struct mac_context *mac, uint32_t sessionId); +bool csr_roam_is_ese_ini_feature_enabled(struct mac_context *mac); +QDF_STATUS csr_get_tsm_stats(struct mac_context *mac, + tCsrTsmStatsCallback callback, + struct qdf_mac_addr bssId, + void *pContext, uint8_t tid); +#endif + +/* Returns whether "Legacy Fast Roaming" is enabled...or not */ +bool csr_roam_is_fast_roam_enabled(struct mac_context *mac, + uint32_t sessionId); +bool csr_roam_is_roam_offload_scan_enabled( + struct mac_context *mac); +bool csr_is_channel_present_in_list(uint32_t *pChannelList, + int numChannels, uint32_t chan_freq); +QDF_STATUS csr_add_to_channel_list_front(uint32_t *pChannelList, + int numChannels, uint32_t chan_freq); +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +QDF_STATUS csr_roam_offload_scan_rsp_hdlr(struct mac_context *mac, + struct roam_offload_scan_rsp *scanOffloadRsp); +#else +static inline QDF_STATUS csr_roam_offload_scan_rsp_hdlr( + struct mac_context *mac, + struct roam_offload_scan_rsp *scanOffloadRsp) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif +QDF_STATUS csr_handoff_request(struct mac_context *mac, uint8_t sessionId, + tCsrHandoffRequest + *pHandoffInfo); +bool csr_roam_is_sta_mode(struct mac_context *mac, uint32_t sessionId); + +/* Post Channel Change Indication */ +QDF_STATUS csr_roam_channel_change_req(struct mac_context *mac, + struct qdf_mac_addr + bssid, struct ch_params *ch_params, + struct csr_roam_profile *profile); + +/* Post Beacon Tx Start Indication */ +QDF_STATUS csr_roam_start_beacon_req(struct mac_context *mac, + struct qdf_mac_addr bssid, uint8_t dfsCacWaitStatus); + +QDF_STATUS csr_roam_send_chan_sw_ie_request(struct mac_context *mac, + struct qdf_mac_addr bssid, + uint32_t target_chan_freq, + uint8_t csaIeReqd, + struct ch_params *ch_params); +QDF_STATUS csr_roam_modify_add_ies(struct mac_context *mac, + tSirModifyIE *pModifyIE, + eUpdateIEsType updateType); +QDF_STATUS +csr_roam_update_add_ies(struct mac_context *mac, + tSirUpdateIE *pUpdateIE, eUpdateIEsType updateType); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * csr_scan_save_roam_offload_ap_to_scan_cache() - This function parses the + * received beacon/probe response from the firmware as part of the roam synch + * indication. The beacon or the probe response is parsed and is also + * saved into the scan cache + * + * @mac: mac Pointer to Global Mac + * @roam_sync_ind_ptr: Roam Synch Indication from firmware + * + * @Return QDF_STATUS + */ +QDF_STATUS +csr_rso_save_ap_to_scan_cache(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_ind, + struct bss_description *bss_desc_ptr); + +/** + * csr_process_ho_fail_ind - This function will process the Hand Off Failure + * indication received from the firmware. It will trigger a disconnect on + * the session which the firmware reported a hand off failure. + * @mac: Pointer to global Mac + * @msg_buf: Pointer to wma Ho fail indication message + * + * Return: None + */ +void csr_process_ho_fail_ind(struct mac_context *mac, void *msg_buf); +#endif +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +void csr_roaming_report_diag_event(struct mac_context *mac_ctx, + struct roam_offload_synch_ind *roam_synch_ind_ptr, + enum csr_diagwlan_status_eventreason reason); +#else +static inline void csr_roaming_report_diag_event( + struct mac_context *mac_ctx, + struct roam_offload_synch_ind *roam_synch_ind_ptr, + enum csr_diagwlan_status_eventreason reason) +{} +#endif + +QDF_STATUS csr_get_channels_and_power(struct mac_context *mac); + +bool csr_nonscan_active_ll_remove_entry( + struct mac_context *mac_ctx, + tListElem *pEntryToRemove, bool inter_locked); +tListElem *csr_nonscan_active_ll_peek_head( + struct mac_context *mac_ctx, + bool inter_locked); +tListElem *csr_nonscan_pending_ll_peek_head( + struct mac_context *mac_ctx, + bool inter_locked); +tListElem *csr_nonscan_pending_ll_next( + struct mac_context *mac_ctx, + tListElem *entry, bool inter_locked); + +/** + * csr_purge_vdev_pending_ser_cmd_list() - purge all scan and non-scan + * pending cmds for the vdev id + * @mac_ctx: pointer to global MAC context + * @vdev_id : vdev id for which the pending cmds need to be purged + * + * Return : none + */ +void csr_purge_vdev_pending_ser_cmd_list(struct mac_context *mac_ctx, + uint32_t vdev_id); + +/** + * csr_purge_vdev_all_scan_ser_cmd_list() - purge all scan active and pending + * cmds for the vdev id + * @mac_ctx: pointer to global MAC context + * @vdev_id : vdev id for which cmds need to be purged + * + * Return : none + */ +void csr_purge_vdev_all_scan_ser_cmd_list(struct mac_context *mac_ctx, + uint32_t vdev_id); + +/** + * csr_purge_pdev_all_ser_cmd_list() - purge all scan and non-scan + * active and pending cmds for all vdevs in pdev + * @mac_ctx: pointer to global MAC context + * + * Return : none + */ +void csr_purge_pdev_all_ser_cmd_list(struct mac_context *mac_ctx); + +bool csr_wait_for_connection_update(struct mac_context *mac, + bool do_release_reacquire_lock); +enum QDF_OPMODE csr_get_session_persona(struct mac_context *pmac, + uint32_t session_id); +void csr_roam_substate_change( + struct mac_context *mac, enum csr_roam_substate + NewSubstate, uint32_t sessionId); + +bool csr_is_ndi_started(struct mac_context *mac_ctx, uint32_t session_id); + +QDF_STATUS csr_roam_update_config( + struct mac_context *mac_ctx, uint8_t session_id, + uint16_t capab, uint32_t value); + +/** + * csr_is_mcc_channel() - check if using the channel results into MCC + * @mac_ctx: pointer to global MAC context + * @chan_freq: channel frequency to check for MCC scenario + * + * Return : true if channel causes MCC, else false + */ +bool csr_is_mcc_channel(struct mac_context *mac_ctx, uint32_t chan_freq); +#endif diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/csr_link_list.h b/drivers/staging/qcacld-3.0/core/sme/inc/csr_link_list.h new file mode 100644 index 0000000000000000000000000000000000000000..85f35e624d2f901b656d3026698e65e6ebfed5be --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/csr_link_list.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2011-2012, 2014-2016, 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file csr_link_list.h + * + * Exports and types for the Common link list interfaces. + */ + +#ifndef CSR_LINK_LIST_H__ +#define CSR_LINK_LIST_H__ + +#include "qdf_lock.h" +#include "qdf_mc_timer.h" +#include "cds_api.h" +#include "sir_types.h" + +#define LL_ACCESS_LOCK true +#define LL_ACCESS_NOLOCK false + +typedef struct tagListElem { + struct tagListElem *last; + struct tagListElem *next; +} tListElem; + +typedef enum { + LIST_FLAG_CLOSE = 0, + LIST_FLAG_OPEN = 0xa1b2c4d7, +} tListFlag; + +/* This is a circular double link list */ +typedef struct tagDblLinkList { + tListElem ListHead; + qdf_mutex_t Lock; + uint32_t Count; + tListFlag Flag; +} tDblLinkList; + +/* + * To get the address of an object of (type) base on the (address) + * of one of its (field) + */ +#define GET_BASE_ADDR(address, type, field) ((type *)( \ + (uint8_t *)(address) - \ + (uint8_t *)(&((type *)0)->field))) +/* To get the offset of (field) inside structure (type) */ +#define GET_FIELD_OFFSET(type, field) ((uintptr_t)(&(((type *)0)->field))) +#define GET_ROUND_UP(_Field, _Boundary) \ + (((_Field) + ((_Boundary) - 1)) & ~((_Boundary) - 1)) +#define BITS_ON(_Field, _Bitmask) ((_Field) |= (_Bitmask)) +#define BITS_OFF(_Field, _Bitmask) ((_Field) &= ~(_Bitmask)) +#define csrIsListEmpty(pHead) ((pHead)->next == (pHead)) + +uint32_t csr_ll_count(tDblLinkList *pList); +QDF_STATUS csr_ll_open(tDblLinkList *pList); +void csr_ll_close(tDblLinkList *pList); +void csr_ll_lock(tDblLinkList *pList); +void csr_ll_unlock(tDblLinkList *pList); +bool csr_ll_is_list_empty(tDblLinkList *pList, bool fInterlocked); +void csr_ll_insert_head(tDblLinkList *pList, tListElem *pEntry, + bool fInterlocked); +void csr_ll_insert_tail(tDblLinkList *pList, tListElem *pEntry, + bool fInterlocked); +/* This function put pNewEntry before pEntry. Caller should have found pEntry */ +void csr_ll_insert_entry(tDblLinkList *pList, tListElem *pEntry, + tListElem *pNewEntry, bool fInterlocked); +tListElem *csr_ll_peek_head(tDblLinkList *pList, bool fInterlocked); +tListElem *csr_ll_peek_tail(tDblLinkList *pList, bool fInterlocked); +tListElem *csr_ll_remove_head(tDblLinkList *pList, bool fInterlocked); +tListElem *csr_ll_remove_tail(tDblLinkList *pList, bool fInterlocked); +bool csr_ll_remove_entry(tDblLinkList *pList, tListElem *pEntryToRemove, + bool fInterlocked); +void csr_ll_purge(tDblLinkList *pList, bool fInterlocked); +/* csr_ll_next return NULL if reaching the end or list is empty */ +tListElem *csr_ll_next(tDblLinkList *pList, tListElem *pEntry, + bool fInterlocked); +tListElem *csr_ll_previous(tDblLinkList *pList, tListElem *pEntry, + bool fInterlocked); +bool csr_ll_find_entry(tDblLinkList *pList, tListElem *pEntryToFind); +#endif diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/csr_neighbor_roam.h b/drivers/staging/qcacld-3.0/core/sme/inc/csr_neighbor_roam.h new file mode 100644 index 0000000000000000000000000000000000000000..922045cd6ad477c6156b9b66f5632ca04d0ba38e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/csr_neighbor_roam.h @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file csr_neighbor_roam.h + * + * Exports and types for the neighbor roaming algorithm which is sepcifically + * designed for Android. + */ + +#ifndef CSR_NEIGHBOR_ROAM_H +#define CSR_NEIGHBOR_ROAM_H + +#include "sme_api.h" + +#define ROAM_AP_AGE_LIMIT_MS 10000 + +/* Enumeration of various states in neighbor roam algorithm */ +typedef enum { + eCSR_NEIGHBOR_ROAM_STATE_CLOSED, + eCSR_NEIGHBOR_ROAM_STATE_INIT, + eCSR_NEIGHBOR_ROAM_STATE_CONNECTED, + eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING, + eCSR_NEIGHBOR_ROAM_STATE_PREAUTHENTICATING, + eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE, + eNEIGHBOR_STATE_MAX +} eCsrNeighborRoamState; + +/* Parameters that are obtained from CFG */ +typedef struct sCsrNeighborRoamCfgParams { + uint32_t neighborScanPeriod; + uint32_t neighbor_scan_min_period; + tCsrChannelInfo specific_chan_info; + uint8_t neighborLookupThreshold; + int8_t rssi_thresh_offset_5g; + uint8_t neighborReassocThreshold; + uint32_t minChannelScanTime; + uint32_t maxChannelScanTime; + uint16_t neighborResultsRefreshPeriod; + uint16_t emptyScanRefreshPeriod; + uint8_t nOpportunisticThresholdDiff; + uint8_t nRoamRescanRssiDiff; + uint8_t nRoamBmissFirstBcnt; + uint8_t nRoamBmissFinalBcnt; + uint8_t nRoamBeaconRssiWeight; + uint8_t delay_before_vdev_stop; + uint32_t hi_rssi_scan_max_count; + uint32_t hi_rssi_scan_rssi_delta; + uint32_t hi_rssi_scan_delay; + int32_t hi_rssi_scan_rssi_ub; + tCsrChannelInfo pref_chan_info; + uint32_t full_roam_scan_period; + bool enable_scoring_for_roam; + uint8_t roam_rssi_diff; + uint16_t roam_scan_home_away_time; + uint8_t roam_scan_n_probes; + uint32_t roam_scan_inactivity_time; + uint32_t roam_inactive_data_packet_count; + uint32_t roam_scan_period_after_inactivity; +} tCsrNeighborRoamCfgParams, *tpCsrNeighborRoamCfgParams; + +#define CSR_NEIGHBOR_ROAM_INVALID_CHANNEL_INDEX 255 +typedef struct sCsrNeighborRoamChannelInfo { + /* Flag to mark reception of IAPP Neighbor list */ + bool IAPPNeighborListReceived; + /* Current channel index that is being scanned */ + uint8_t currentChanIndex; + /* Max number of channels in channel list and the list of channels */ + tCsrChannelInfo currentChannelListInfo; +} tCsrNeighborRoamChannelInfo, *tpCsrNeighborRoamChannelInfo; + +typedef struct sCsrNeighborRoamBSSInfo { + tListElem List; + uint8_t apPreferenceVal; + struct bss_description *pBssDescription; +} tCsrNeighborRoamBSSInfo, *tpCsrNeighborRoamBSSInfo; + +#define CSR_NEIGHBOR_ROAM_REPORT_QUERY_TIMEOUT 1000 /* in milliseconds */ +/* Max number of MAC addresses with which the pre-auth was failed */ +#define MAX_NUM_PREAUTH_FAIL_LIST_ADDRESS 10 +#define CSR_NEIGHBOR_ROAM_MAX_NUM_PREAUTH_RETRIES 3 + +/* Black listed APs. List of MAC Addresses with which the Preauth was failed */ +typedef struct sCsrPreauthFailListInfo { + uint8_t numMACAddress; + tSirMacAddr macAddress[MAX_NUM_PREAUTH_FAIL_LIST_ADDRESS]; +} tCsrPreauthFailListInfo, *tpCsrPreauthFailListInfo; + +typedef struct sCsr11rAssocNeighborInfo { + bool preauthRspPending; + bool neighborRptPending; + uint8_t currentNeighborRptRetryNum; + tCsrPreauthFailListInfo preAuthFailList; + uint32_t neighborReportTimeout; + uint8_t numPreAuthRetries; + tDblLinkList preAuthDoneList; /* llist which consists/preauth nodes */ +} tCsr11rAssocNeighborInfo, *tpCsr11rAssocNeighborInfo; + +/** + * struct sCsr11rAssocNeighborInfo - Control info for neighbor roam algorithm + * @roam_control_enable: Flag used to cache the status of roam control + * configuration. This will be set only if the + * corresponding vendor command data is configured to + * driver/firmware successfully. The same shall be + * returned to userspace whenever queried for roam + * control config status. + */ +typedef struct sCsrNeighborRoamControlInfo { + eCsrNeighborRoamState neighborRoamState; + eCsrNeighborRoamState prevNeighborRoamState; + tCsrNeighborRoamCfgParams cfgParams; + struct qdf_mac_addr currAPbssid; /* current assoc AP */ + uint32_t curr_ap_op_chan_freq; /* current assoc AP */ + tCsrNeighborRoamChannelInfo roamChannelInfo; + uint8_t currentNeighborLookupThreshold; + uint8_t currentOpportunisticThresholdDiff; + uint8_t currentRoamRescanRssiDiff; + tDblLinkList roamableAPList; /* List of current FT candidates */ + struct csr_roam_profile csrNeighborRoamProfile; + bool is11rAssoc; + tCsr11rAssocNeighborInfo FTRoamInfo; +#ifdef FEATURE_WLAN_ESE + bool isESEAssoc; + bool isVOAdmitted; + uint16_t MinQBssLoadRequired; +#endif + /* + * Previous connected profile. + * If the new profile does not match previous we re-initialize + * occupied channel list + */ + tCsrRoamConnectedProfile prevConnProfile; + /* upper layer requested a reassoc */ + uint8_t uOsRequestedHandoff; + /* handoff related info came with upper layer's req for reassoc */ + tCsrHandoffRequest handoffReqInfo; + uint8_t currentRoamBmissFirstBcnt; + uint8_t currentRoamBmissFinalBcnt; + uint8_t currentRoamBeaconRssiWeight; + uint8_t last_sent_cmd; + struct scan_result_list *scan_res_lfr2_roam_ap; + bool roam_control_enable; +} tCsrNeighborRoamControlInfo, *tpCsrNeighborRoamControlInfo; + +/* All the necessary Function declarations are here */ +QDF_STATUS csr_neighbor_roam_indicate_connect(struct mac_context *mac, + uint8_t sessionId, QDF_STATUS status); +QDF_STATUS csr_neighbor_roam_indicate_disconnect(struct mac_context *mac, + uint8_t sessionId); +QDF_STATUS csr_neighbor_roam_init(struct mac_context *mac, uint8_t sessionId); +void csr_neighbor_roam_close(struct mac_context *mac, uint8_t sessionId); +QDF_STATUS csr_neighbor_roam_preauth_rsp_handler(struct mac_context *mac, + uint8_t sessionId, QDF_STATUS limStatus); +bool csr_neighbor_roam_is11r_assoc(struct mac_context *mac, uint8_t sessionId); +#ifdef WLAN_FEATURE_HOST_ROAM +void csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + struct mac_context *mac, uint8_t sessionId); +bool csr_neighbor_roam_state_preauth_done(struct mac_context *mac, + uint8_t sessionId); +void csr_neighbor_roam_reset_preauth_control_info( + struct mac_context *mac_ctx, uint8_t session_id); +void csr_neighbor_roam_purge_preauth_failed_list(struct mac_context *mac); +#else +static inline bool csr_neighbor_roam_state_preauth_done(struct mac_context *mac, + uint8_t sessionId) +{ + return false; +} +static inline void csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + struct mac_context *mac, uint8_t sessionId) +{} +static inline void csr_neighbor_roam_reset_preauth_control_info( + struct mac_context *mac_ctx, uint8_t session_id) +{} +static inline void csr_neighbor_roam_purge_preauth_failed_list( + struct mac_context *mac) +{} +#endif +bool csr_neighbor_middle_of_roaming(struct mac_context *mac, uint8_t sessionId); +QDF_STATUS csr_neighbor_roam_update_config(struct mac_context *mac_ctx, + uint8_t session_id, uint8_t value, uint8_t reason); +QDF_STATUS csr_neighbor_roam_update_fast_roaming_enabled(struct mac_context *mac, + uint8_t sessionId, const bool fastRoamEnabled); +QDF_STATUS csr_neighbor_roam_channels_filter_by_current_band( + struct mac_context *mac, uint8_t sessionId, + uint32_t *input_chan_freq_list, + uint8_t inputNumOfChannels, + uint32_t *out_chan_freq_list, + uint8_t *pMergedOutputNumOfChannels); +QDF_STATUS csr_neighbor_roam_merge_channel_lists(struct mac_context *mac, + uint32_t *pinput_chan_freq_list, + uint8_t inputNumOfChannels, + uint32_t *out_chan_freq_list, + uint8_t outputNumOfChannels, + uint8_t *pMergedOutputNumOfChannels); +void csr_roam_reset_roam_params(struct mac_context *mac_ptr); +#define ROAM_SCAN_OFFLOAD_START 1 +#define ROAM_SCAN_OFFLOAD_STOP 2 +#define ROAM_SCAN_OFFLOAD_RESTART 3 +#define ROAM_SCAN_OFFLOAD_UPDATE_CFG 4 +#define ROAM_SCAN_OFFLOAD_ABORT_SCAN 5 + +#define REASON_CONNECT 1 +#define REASON_CHANNEL_LIST_CHANGED 2 +#define REASON_LOOKUP_THRESH_CHANGED 3 +#define REASON_DISCONNECTED 4 +#define REASON_RSSI_DIFF_CHANGED 5 +#define REASON_ESE_INI_CFG_CHANGED 6 +#define REASON_NEIGHBOR_SCAN_REFRESH_PERIOD_CHANGED 7 +#define REASON_VALID_CHANNEL_LIST_CHANGED 8 +#define REASON_FLUSH_CHANNEL_LIST 9 +#define REASON_EMPTY_SCAN_REF_PERIOD_CHANGED 10 +#define REASON_PREAUTH_FAILED_FOR_ALL 11 +#define REASON_NO_CAND_FOUND_OR_NOT_ROAMING_NOW 12 +#define REASON_NPROBES_CHANGED 13 +#define REASON_HOME_AWAY_TIME_CHANGED 14 +#define REASON_OS_REQUESTED_ROAMING_NOW 15 +#define REASON_SCAN_CH_TIME_CHANGED 16 +#define REASON_SCAN_HOME_TIME_CHANGED 17 +#define REASON_OPPORTUNISTIC_THRESH_DIFF_CHANGED 18 +#define REASON_ROAM_RESCAN_RSSI_DIFF_CHANGED 19 +#define REASON_ROAM_BMISS_FIRST_BCNT_CHANGED 20 +#define REASON_ROAM_BMISS_FINAL_BCNT_CHANGED 21 +#define REASON_ROAM_BEACON_RSSI_WEIGHT_CHANGED 22 +#define REASON_ROAM_DFS_SCAN_MODE_CHANGED 23 +#define REASON_ROAM_ABORT_ROAM_SCAN 24 +#define REASON_ROAM_EXT_SCAN_PARAMS_CHANGED 25 +#define REASON_ROAM_SET_SSID_ALLOWED 26 +#define REASON_ROAM_SET_FAVORED_BSSID 27 +#define REASON_ROAM_GOOD_RSSI_CHANGED 28 +#define REASON_ROAM_SET_BLACKLIST_BSSID 29 +#define REASON_ROAM_SCAN_HI_RSSI_MAXCOUNT_CHANGED 30 +#define REASON_ROAM_SCAN_HI_RSSI_DELTA_CHANGED 31 +#define REASON_ROAM_SCAN_HI_RSSI_DELAY_CHANGED 32 +#define REASON_ROAM_SCAN_HI_RSSI_UB_CHANGED 33 +#define REASON_CONNECT_IES_CHANGED 34 +#define REASON_ROAM_SCAN_STA_ROAM_POLICY_CHANGED 35 +#define REASON_ROAM_SYNCH_FAILED 36 +#define REASON_ROAM_PSK_PMK_CHANGED 37 +#define REASON_ROAM_STOP_ALL 38 +#define REASON_SUPPLICANT_DISABLED_ROAMING 39 +#define REASON_CTX_INIT 40 +#define REASON_FILS_PARAMS_CHANGED 41 +#define REASON_SME_ISSUED 42 +#define REASON_DRIVER_ENABLED 43 +#define REASON_ROAM_FULL_SCAN_PERIOD_CHANGED 44 +#define REASON_SCORING_CRITERIA_CHANGED 45 +#define REASON_SUPPLICANT_INIT_ROAMING 46 +#define REASON_SUPPLICANT_DE_INIT_ROAMING 47 +#define REASON_DRIVER_DISABLED 48 +#define REASON_ROAM_CONTROL_CONFIG_RESTORED 49 +#define REASON_ROAM_CONTROL_CONFIG_ENABLED 50 + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +QDF_STATUS csr_roam_offload_scan(struct mac_context *mac, uint8_t sessionId, + uint8_t command, uint8_t reason); + +/** + * csr_post_roam_state_change() - Post roam state change to roam state machine + * @mac: mac context + * @vdev_id: vdev id + * @state: roam state to be set for the requested vdev id + * @reason: reason for changing roam state for the requested vdev id + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_post_roam_state_change(struct mac_context *mac, uint8_t vdev_id, + enum roam_offload_state state, + uint8_t reason); + +/** + * csr_post_rso_stop() - Post RSO stop message to WMA + * @mac: mac context + * @vdev_id: vdev id + * @reason: reason for requesting RSO stop + * + * Return: QDF_STATUS + */ +QDF_STATUS +csr_post_rso_stop(struct mac_context *mac, uint8_t vdev_id, uint16_t reason); + +/** + * csr_enable_roaming_on_connected_sta() - Enable roaming on other connected + * sta vdev + * @mac: mac context + * @vdev_id: vdev id on which roaming should not be enabled + * @reason: reason for enabling roaming on connected sta vdev + * + * Return: QDF_STATUS + */ +QDF_STATUS +csr_enable_roaming_on_connected_sta(struct mac_context *mac, uint8_t vdev_id); + +/** + * csr_roam_update_cfg() - Process RSO update cfg request + * @mac: mac context + * @vdev_id: vdev id + * @reason: reason for requesting RSO update cfg + * + * Return: QDF_STATUS + */ +QDF_STATUS +csr_roam_update_cfg(struct mac_context *mac, uint8_t vdev_id, uint8_t reason); +#else +static inline QDF_STATUS csr_roam_offload_scan(struct mac_context *mac, + uint8_t sessionId, uint8_t command, uint8_t reason) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline +QDF_STATUS csr_post_roam_state_change(struct mac_context *mac, uint8_t vdev_id, + enum roam_offload_state state, + uint8_t reason) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline +csr_post_rso_stop(struct mac_context *mac, uint8_t vdev_id, uint16_t reason) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline QDF_STATUS +csr_enable_roaming_on_connected_sta(struct mac_context *mac, uint8_t vdev_id) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline QDF_STATUS +csr_roam_update_cfg(struct mac_context *mac, uint8_t vdev_id, uint8_t reason) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif + +/** + * csr_get_roam_enabled_sta_sessionid() - get the session id of the sta on which + * roaming is enabled. + * @mac_ctx: pointer to global mac structure + * + * The function check if any sta is present and has roaming enabled and return + * the session id of the sta with roaming enabled else if roaming is not enabled + * on any STA return WLAN_UMAC_VDEV_ID_MAX + * + * Return: session id of STA on which roaming is enabled + */ +uint8_t csr_get_roam_enabled_sta_sessionid( + struct mac_context *mac_ctx); + +#if defined(WLAN_FEATURE_FILS_SK) +/** + * csr_update_fils_config - Update FILS config to CSR roam session + * @mac: MAC context + * @session_id: session id + * @src_profile: Source profile having latest FILS config + * + * API to update FILS config to roam csr session + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_update_fils_config(struct mac_context *mac, uint8_t session_id, + struct csr_roam_profile *src_profile); +#endif + +QDF_STATUS csr_neighbor_roam_handoff_req_hdlr(struct mac_context *mac, void *pMsg); +QDF_STATUS csr_neighbor_roam_proceed_with_handoff_req(struct mac_context *mac, + uint8_t sessionId); +QDF_STATUS csr_neighbor_roam_sssid_scan_done(struct mac_context *mac, + uint8_t sessionId, QDF_STATUS status); +QDF_STATUS csr_neighbor_roam_start_lfr_scan(struct mac_context *mac, + uint8_t sessionId); + +#ifdef FEATURE_WLAN_ESE +QDF_STATUS csr_set_cckm_ie(struct mac_context *mac, const uint8_t sessionId, + const uint8_t *pCckmIe, const uint8_t ccKmIeLen); +QDF_STATUS csr_roam_read_tsf(struct mac_context *mac, uint8_t *pTimestamp, + const uint8_t sessionId); +#endif /* FEATURE_WLAN_ESE */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS csr_roam_synch_callback(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, enum sir_roam_op_code reason); + +/** + * csr_roam_auth_offload_callback() - Registered CSR Callback function to handle + * WPA3 roam pre-auth event from firmware. + * @mac_ctx: Global mac context pointer + * @vdev_id: Vdev id + * @bssid: candidate AP bssid + */ +QDF_STATUS +csr_roam_auth_offload_callback(struct mac_context *mac_ctx, + uint8_t vdev_id, + struct qdf_mac_addr bssid); + +/** + * csr_fast_reassoc() - invokes FAST REASSOC command + * @mac_handle: handle returned by mac_open + * @profile: current connected profile + * @bssid: bssid to look for in scan cache + * @ch_freq: channel on which reassoc should be send + * @vdev_id: vdev id + * @connected_bssid: bssid of currently connected profile + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_fast_reassoc(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const tSirMacAddr bssid, uint32_t ch_freq, + uint8_t vdev_id, const tSirMacAddr connected_bssid); + +#ifdef WLAN_FEATURE_FIPS +/** + * csr_roam_pmkid_req_callback() - Registered CSR Callback function to handle + * roam event from firmware for pmkid generation fallback. + * @vdev_id: Vdev id + * @bss_list: candidate AP bssid list + */ +QDF_STATUS +csr_roam_pmkid_req_callback(uint8_t vdev_id, + struct roam_pmkid_req_event *bss_list); + +/** + * csr_process_roam_pmkid_req_callback() - API to trigger the pmkid + * generation fallback event for candidate AP received from firmware. + * @mac_ctx: Global mac context pointer + * @vdev_id: Vdev id + * @roam_bsslist: roam candidate AP bssid list + * + * This function calls the hdd_sme_roam_callback with reason + * eCSR_ROAM_FIPS_PMK_REQUEST to trigger pmkid generation in supplicant. + */ +QDF_STATUS +csr_process_roam_pmkid_req_callback(struct mac_context *mac_ctx, + uint8_t vdev_id, + struct roam_pmkid_req_event *roam_bsslist); +#else +static inline QDF_STATUS +csr_roam_pmkid_req_callback(uint8_t vdev_id, + struct roam_pmkid_req_event *bss_list) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_FIPS */ + +#else +static inline QDF_STATUS csr_roam_synch_callback(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, enum sir_roam_op_code reason) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline QDF_STATUS +csr_roam_auth_offload_callback(struct mac_context *mac_ctx, + uint8_t vdev_id, + struct qdf_mac_addr bssid) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline +QDF_STATUS csr_fast_reassoc(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const tSirMacAddr bssid, uint32_t ch_freq, + uint8_t vdev_id, const tSirMacAddr connected_bssid) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +csr_roam_pmkid_req_callback(uint8_t vdev_id, + struct roam_pmkid_req_event *bss_list) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif +void csr_neighbor_roam_state_transition(struct mac_context *mac_ctx, + uint8_t newstate, uint8_t session); +uint8_t *csr_neighbor_roam_state_to_string(uint8_t state); +QDF_STATUS csr_neighbor_roam_issue_preauth_req(struct mac_context *mac, + uint8_t sessionId); +bool csr_neighbor_roam_is_preauth_candidate(struct mac_context *mac, + uint8_t sessionId, tSirMacAddr bssId); +#ifdef FEATURE_WLAN_LFR_METRICS +void csr_neighbor_roam_send_lfr_metric_event(struct mac_context *mac_ctx, + uint8_t session_id, tSirMacAddr bssid, eRoamCmdStatus status); +#else +static inline void csr_neighbor_roam_send_lfr_metric_event( + struct mac_context *mac_ctx, uint8_t session_id, + tSirMacAddr bssid, eRoamCmdStatus status) +{} +#endif +QDF_STATUS csr_roam_stop_wait_for_key_timer(struct mac_context *mac); +QDF_STATUS csr_roam_copy_connected_profile(struct mac_context *mac, + uint32_t sessionId, struct csr_roam_profile *pDstProfile); + +/** + * csr_invoke_neighbor_report_request - Send neighbor report invoke command to + * WMA + * @mac_ctx: MAC context + * @session_id: session id + * + * API called from IW to invoke neighbor report request to WMA then to FW + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_invoke_neighbor_report_request(uint8_t session_id, + struct sRrmNeighborReq *neighbor_report_req, + bool send_resp_to_host); + +#endif /* CSR_NEIGHBOR_ROAM_H */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/csr_support.h b/drivers/staging/qcacld-3.0/core/sme/inc/csr_support.h new file mode 100644 index 0000000000000000000000000000000000000000..e8c25ef37f2df1590403481d1b1679aeac6da506 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/csr_support.h @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file csr_support.h + * + * Exports and types for the Common Scan and Roaming supporting interfaces. + */ +#ifndef CSR_SUPPORT_H__ +#define CSR_SUPPORT_H__ + +#include "csr_link_list.h" +#include "csr_api.h" +#include "cds_reg_service.h" + +#ifdef FEATURE_WLAN_WAPI +#define CSR_WAPI_OUI_SIZE (4) +#define CSR_WAPI_VERSION_SUPPORTED (1) +#define CSR_WAPI_MAX_AUTH_SUITES (2) +#define CSR_WAPI_MAX_CYPHERS (5) +#define CSR_WAPI_MAX_UNICAST_CYPHERS (5) +#define CSR_WAPI_MAX_MULTICAST_CYPHERS (1) +#endif /* FEATURE_WLAN_WAPI */ + +#define CSR_RSN_OUI_SIZE (4) +#define CSR_RSN_VERSION_SUPPORTED (1) +#define CSR_RSN_MAX_AUTH_SUITES (5) +#define CSR_RSN_MAX_CYPHERS (5) +#define CSR_RSN_MAX_UNICAST_CYPHERS (5) +#define CSR_RSN_MAX_MULTICAST_CYPHERS (1) + +#define CSR_WPA_OUI_SIZE (4) +#define CSR_WPA_VERSION_SUPPORTED (1) +#define CSR_WME_OUI_SIZE (4) +#define CSR_WPA_MAX_AUTH_SUITES (2) +#define CSR_WPA_MAX_CYPHERS (5) +#define CSR_WPA_MAX_UNICAST_CYPHERS (5) +#define CSR_WPA_MAX_MULTICAST_CYPHERS (1) +/* minimum size of the IE->length is the size of the Oui + Version. */ +#define CSR_WPA_IE_MIN_SIZE (6) +#define CSR_WPA_IE_MIN_SIZE_W_MULTICAST (HDD_WPA_IE_MIN_SIZE + HDD_WPA_OUI_SIZE) +#define CSR_WPA_IE_MIN_SIZE_W_UNICAST (HDD_WPA_IE_MIN_SIZE + \ + HDD_WPA_OUI_SIZE + sizeof(pWpaIe->cUnicastCyphers)) + +#define CSR_DOT11_SUPPORTED_RATES_MAX (12) +#define CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX (8) + +#define CSR_DOT11_BASIC_RATE_MASK (0x80) + +/* NOTE these index are use as array index for csr_rsn_oui */ +#define CSR_OUI_USE_GROUP_CIPHER_INDEX 0x00 +#define CSR_OUI_WEP40_OR_1X_INDEX 0x01 +#define CSR_OUI_TKIP_OR_PSK_INDEX 0x02 +#define CSR_OUI_RESERVED_INDEX 0x03 +#define CSR_OUI_AES_INDEX 0x04 +#define CSR_OUI_WEP104_INDEX 0x05 +/* ENUM_FILS_SHA384 9 */ +/* ENUM_FILS_SHA384 10 */ +/* ENUM_FT_FILS_SHA256 11 */ +/* ENUM_FT_FILS_SHA384 12 */ +#define CSR_OUI_AES_GCMP_INDEX 0x0D +#define CSR_OUI_AES_GCMP_256_INDEX 0x0E + +#ifdef FEATURE_WLAN_WAPI +#define CSR_OUI_WAPI_RESERVED_INDEX 0x00 +#define CSR_OUI_WAPI_WAI_CERT_OR_SMS4_INDEX 0x01 +#define CSR_OUI_WAPI_WAI_PSK_INDEX 0x02 +/* max idx, should be last & highest */ +#define CSR_OUI_WAPI_WAI_MAX_INDEX 0x03 +#endif /* FEATURE_WLAN_WAPI */ + +typedef enum { + /* 11b rates */ + eCsrSuppRate_1Mbps = 1 * 2, + eCsrSuppRate_2Mbps = 2 * 2, + eCsrSuppRate_5_5Mbps = 11, /* 5.5 * 2 */ + eCsrSuppRate_11Mbps = 11 * 2, + + /* 11a / 11g rates */ + eCsrSuppRate_6Mbps = 6 * 2, + eCsrSuppRate_9Mbps = 9 * 2, + eCsrSuppRate_12Mbps = 12 * 2, + eCsrSuppRate_18Mbps = 18 * 2, + eCsrSuppRate_24Mbps = 24 * 2, + eCsrSuppRate_36Mbps = 36 * 2, + eCsrSuppRate_48Mbps = 48 * 2, + eCsrSuppRate_54Mbps = 54 * 2, + + /* airgo proprietary rates */ + eCsrSuppRate_10Mbps = 10 * 2, + eCsrSuppRate_10_5Mbps = 21, /* 10.5 * 2 */ + eCsrSuppRate_20Mbps = 20 * 2, + eCsrSuppRate_21Mbps = 21 * 2, + eCsrSuppRate_40Mbps = 40 * 2, + eCsrSuppRate_42Mbps = 42 * 2, + eCsrSuppRate_60Mbps = 60 * 2, + eCsrSuppRate_63Mbps = 63 * 2, + eCsrSuppRate_72Mbps = 72 * 2, + eCsrSuppRate_80Mbps = 80 * 2, + eCsrSuppRate_84Mbps = 84 * 2, + eCsrSuppRate_96Mbps = 96 * 2, + eCsrSuppRate_108Mbps = 108 * 2, + eCsrSuppRate_120Mbps = 120 * 2, + eCsrSuppRate_126Mbps = 126 * 2, + eCsrSuppRate_144Mbps = 144 * 2, + eCsrSuppRate_160Mbps = 160 * 2, + eCsrSuppRate_168Mbps = 168 * 2, + eCsrSuppRate_192Mbps = 192 * 2, + eCsrSuppRate_216Mbps = 216 * 2, + eCsrSuppRate_240Mbps = 240 * 2 +} eCsrSupportedRates; + +/* Generic Information Element Structure */ +typedef struct sDot11IEHeader { + uint8_t ElementID; + uint8_t Length; +} qdf_packed tDot11IEHeader; + +typedef struct tagCsrWpaIe { + tDot11IEHeader IeHeader; + uint8_t Oui[CSR_WPA_OUI_SIZE]; + uint16_t Version; + uint8_t MulticastOui[CSR_WPA_OUI_SIZE]; + uint16_t cUnicastCyphers; + struct { + uint8_t Oui[CSR_WPA_OUI_SIZE]; + } qdf_packed UnicastOui[1]; +} qdf_packed tCsrWpaIe; + +typedef struct tagCsrWpaAuthIe { + uint16_t cAuthenticationSuites; + struct { + uint8_t Oui[CSR_WPA_OUI_SIZE]; + } qdf_packed AuthOui[1]; +} qdf_packed tCsrWpaAuthIe; + +typedef struct tagCsrRSNIe { + tDot11IEHeader IeHeader; + uint16_t Version; + uint8_t MulticastOui[CSR_RSN_OUI_SIZE]; + uint16_t cUnicastCyphers; + struct { + uint8_t Oui[CSR_RSN_OUI_SIZE]; + } qdf_packed UnicastOui[1]; +} qdf_packed tCsrRSNIe; + +typedef struct tagCsrRSNAuthIe { + uint16_t cAuthenticationSuites; + struct { + uint8_t Oui[CSR_RSN_OUI_SIZE]; + } qdf_packed AuthOui[1]; +} qdf_packed tCsrRSNAuthIe; + +typedef struct tagCsrRSNPMKIe { + uint16_t cPMKIDs; + struct { + uint8_t PMKID[PMKID_LEN]; + } qdf_packed PMKIDList[1]; +} qdf_packed tCsrRSNPMKIe; + +typedef struct tCsrIELenInfo { + uint8_t min; + uint8_t max; +} qdf_packed tCsrIELenInfo; + +#ifdef FEATURE_WLAN_WAPI +typedef struct tagCsrWapiIe { + tDot11IEHeader IeHeader; + uint16_t Version; + uint16_t cAuthenticationSuites; + struct { + uint8_t Oui[CSR_WAPI_OUI_SIZE]; + } qdf_packed AuthOui[1]; + uint16_t cUnicastCyphers; + struct { + uint8_t Oui[CSR_WAPI_OUI_SIZE]; + } qdf_packed UnicastOui[1]; + uint8_t MulticastOui[CSR_WAPI_OUI_SIZE]; + struct { + uint16_t PreAuthSupported:1; + uint16_t Reserved:15; + } qdf_packed tCsrWapiCapabilities; +} qdf_packed tCsrWapiIe; +#endif /* FEATURE_WLAN_WAPI */ + +/** + * struct csr_timer_info - CSR-specific timer context + * @mac: Global MAC context associated with the timer + * @vdev_id: Session associated with the timer + */ +struct csr_timer_info { + struct mac_context *mac; + uint8_t vdev_id; +}; + +#define CSR_IS_11A_BSS(bss_desc) (eSIR_11A_NW_TYPE == (bss_desc)->nwType) +#define CSR_IS_BASIC_RATE(rate) ((rate) & CSR_DOT11_BASIC_RATE_MASK) +#define CSR_IS_QOS_BSS(pIes) \ + ((pIes)->WMMParams.present || (pIes)->WMMInfoAp.present) +#define CSR_IS_UAPSD_BSS(pIes) \ + (((pIes)->WMMParams.present && \ + ((pIes)->WMMParams.qosInfo & SME_QOS_AP_SUPPORTS_APSD)) || \ + ((pIes)->WMMInfoAp.present && (pIes)->WMMInfoAp.uapsd)) + +bool csr_get_bss_id_bss_desc(struct bss_description *pSirBssDesc, + struct qdf_mac_addr *pBssId); +bool csr_is_bss_id_equal(struct bss_description *pSirBssDesc1, + struct bss_description *pSirBssDesc2); + +eCsrMediaAccessType +csr_get_qos_from_bss_desc(struct mac_context *mac_ctx, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes); + +bool csr_is_nullssid(uint8_t *pBssSsid, uint8_t len); +bool csr_is_infra_bss_desc(struct bss_description *pSirBssDesc); + +#ifdef QCA_IBSS_SUPPORT +/** + * csr_is_ibss_bss_desc() - API to check bss desc of ibss type + * @pSirBssDesc: pointer to pSirBssDesc structure + * + * Return: true if bss desc of ibss type, else false + */ +bool csr_is_ibss_bss_desc(struct bss_description *pSirBssDesc); + +/** + * csr_is_ibss_bss_desc() - API to check bss desc of ibss type + * @bssType: bss type + * + * Return: true if bss type is ibss type, else false + */ +bool csr_is_bss_type_ibss(eCsrRoamBssType bssType); +#else +static inline +bool csr_is_bss_type_ibss(eCsrRoamBssType bssType) +{ + return false; +} + +static inline +bool csr_is_ibss_bss_desc(struct bss_description *pSirBssDesc) +{ + return false; +} +#endif +tSirResultCodes csr_get_de_auth_rsp_status_code(struct deauth_rsp *pSmeRsp); +uint32_t csr_get_frag_thresh(struct mac_context *mac_ctx); +uint32_t csr_get_rts_thresh(struct mac_context *mac_ctx); +uint32_t csr_get11h_power_constraint(struct mac_context *mac_ctx, + tDot11fIEPowerConstraints *constraints); +uint8_t csr_construct_rsn_ie(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrRSNIe *pRSNIe); + +uint8_t csr_construct_wpa_ie(struct mac_context *mac, uint8_t session_id, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrWpaIe *pWpaIe); + +#ifdef FEATURE_WLAN_WAPI +bool csr_is_profile_wapi(struct csr_roam_profile *pProfile); +#endif /* FEATURE_WLAN_WAPI */ +/* + * If a WPAIE exists in the profile, just use it. + * Or else construct one from the BSS Caller allocated memory for pWpaIe and + * guarrantee it can contain a max length WPA IE + */ +uint8_t csr_retrieve_wpa_ie(struct mac_context *mac, uint8_t session_id, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrWpaIe *pWpaIe); + +bool csr_is_ssid_equal(struct mac_context *mac, + struct bss_description *pSirBssDesc1, + struct bss_description *pSirBssDesc2, + tDot11fBeaconIEs *pIes2); + +/* Null ssid means match */ +bool csr_is_ssid_in_list(tSirMacSSid *pSsid, tCsrSSIDs *pSsidList); +bool csr_is_profile_wpa(struct csr_roam_profile *pProfile); +bool csr_is_profile_rsn(struct csr_roam_profile *pProfile); +/* + * If a RSNIE exists in the profile, just use it. Or + * else construct one from the BSS Caller allocated memory for pWpaIe and + * guarantee it can contain a max length WPA IE + */ +uint8_t csr_retrieve_rsn_ie(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrRSNIe *pRsnIe); +#ifdef FEATURE_WLAN_WAPI +/* + * If a WAPI IE exists in the profile, just use it. + * Or else construct one from the BSS. Caller allocated memory for pWapiIe and + * guarrantee it can contain a max length WAPI IE + */ +uint8_t csr_retrieve_wapi_ie(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrWapiIe *pWapiIe); +#endif /* FEATURE_WLAN_WAPI */ +bool csr_rates_is_dot11_rate11b_supported_rate(uint8_t dot11Rate); +bool csr_rates_is_dot11_rate11a_supported_rate(uint8_t dot11Rate); +tAniEdType csr_translate_encrypt_type_to_ed_type( + eCsrEncryptionType EncryptType); + +bool csr_is_bssid_match(struct qdf_mac_addr *pProfBssid, + struct qdf_mac_addr *BssBssid); +void csr_add_rate_bitmap(uint8_t rate, uint16_t *pRateBitmap); +bool csr_check_rate_bitmap(uint8_t rate, uint16_t RateBitmap); +bool csr_rates_is_dot11_rate_supported(struct mac_context *mac_ctx, uint8_t rate); +enum bss_type csr_translate_bsstype_to_mac_type(eCsrRoamBssType csrtype); +/* Caller allocates memory for pIEStruct */ +QDF_STATUS csr_parse_bss_description_ies(struct mac_context *mac_ctx, + struct bss_description *bss_desc, + tDot11fBeaconIEs *pIEStruct); +/* + * This function will allocate memory for the parsed IEs to the caller. + * Caller must free the memory. after it is done with the data only if + * this function succeeds + */ +QDF_STATUS csr_get_parsed_bss_description_ies(struct mac_context *mac_ctx, + struct bss_description *bss_desc, + tDot11fBeaconIEs **ppIEStruct); + +tSirScanType csr_get_scan_type(struct mac_context *mac, uint8_t chnId); + +QDF_STATUS csr_get_phy_mode_from_bss(struct mac_context *mac, + struct bss_description *pBSSDescription, + eCsrPhyMode *pPhyMode, tDot11fBeaconIEs *pIes); +/* + * fForce -- force reassoc regardless of whether there is any change. + * The reason is that for UAPSD-bypass, the code underneath this call determine + * whether to allow UAPSD. The information in pModProfileFields reflects what + * the user wants. There may be discrepency in it. UAPSD-bypass logic should + * decide if it needs to reassoc + */ +QDF_STATUS csr_reassoc(struct mac_context *mac, uint32_t sessionId, + tCsrRoamModifyProfileFields *pModProfileFields, + uint32_t *pRoamId, bool fForce); + +/** + * csr_validate_mcc_beacon_interval() - to validate the mcc beacon interval + * @mac_ctx: pointer to mac context + * @ch_freq: channel frequency + * @bcn_interval: provided beacon interval + * @cur_session_id: current session id + * @cur_bss_persona: Current BSS persona + * + * This API will validate the mcc beacon interval + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_validate_mcc_beacon_interval(struct mac_context *mac_ctx, + uint32_t ch_freq, + uint16_t *bcn_interval, + uint32_t cur_session_id, + enum QDF_OPMODE cur_bss_persona); + +bool csr_is_profile11r(struct mac_context *mac, struct csr_roam_profile *pProfile); +bool csr_is_auth_type11r(struct mac_context *mac, enum csr_akm_type AuthType, + uint8_t mdiePresent); +#ifdef FEATURE_WLAN_ESE +bool csr_is_profile_ese(struct csr_roam_profile *pProfile); +#endif + +/** + * csr_is_auth_type_ese() - Checks whether Auth type is ESE or not + * @AuthType: Authentication type + * + * Return: true, if auth type is ese, false otherwise + */ +bool csr_is_auth_type_ese(enum csr_akm_type AuthType); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_api.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_api.h new file mode 100644 index 0000000000000000000000000000000000000000..a44e8e8c15d1a8b4eae1ad516371db8e47dc9fa1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_api.h @@ -0,0 +1,4207 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SME_API_H) +#define __SME_API_H + +/** + * file smeApi.h + * + * brief prototype for SME APIs + */ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "csr_api.h" +#include "qdf_lock.h" +#include "qdf_types.h" +#include "sir_api.h" +#include "cds_regdomain.h" +#include "sme_internal.h" +#include "wma_tgt_cfg.h" +#include "wma_fips_public_structs.h" +#include "wma_sar_public_structs.h" +#include "wlan_mlme_public_struct.h" +#include "sme_rrm_internal.h" +#include "sir_types.h" +#include "scheduler_api.h" +#include "wlan_serialization_legacy_api.h" +#include +#include "wmi_unified.h" +#include "wmi_unified_param.h" + +/*-------------------------------------------------------------------------- + Preprocessor definitions and constants + ------------------------------------------------------------------------*/ + +#define sme_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_SME, params) +#define sme_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_SME, params) +#define sme_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_SME, params) +#define sme_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_SME, params) +#define sme_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_SME, params) + +#define sme_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_SME, params) +#define sme_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_SME, params) +#define sme_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_SME, params) +#define sme_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_SME, params) +#define sme_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_SME, params) + +#define sme_alert_rl(params...) QDF_TRACE_FATAL_RL(QDF_MODULE_ID_SME, params) +#define sme_err_rl(params...) QDF_TRACE_ERROR_RL(QDF_MODULE_ID_SME, params) +#define sme_warn_rl(params...) QDF_TRACE_WARN_RL(QDF_MODULE_ID_SME, params) +#define sme_info_rl(params...) QDF_TRACE_INFO_RL(QDF_MODULE_ID_SME, params) +#define sme_debug_rl(params...) QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_SME, params) + +#define SME_ENTER() QDF_TRACE_ENTER(QDF_MODULE_ID_SME, "enter") +#define SME_EXIT() QDF_TRACE_EXIT(QDF_MODULE_ID_SME, "exit") + +#define SME_SESSION_ID_ANY 50 +#define SME_SESSION_ID_BROADCAST 0xFF + +#define SME_INVALID_COUNTRY_CODE "XX" +#define INVALID_ROAM_ID 0 + +#define SME_SET_CHANNEL_REG_POWER(reg_info_1, val) do { \ + reg_info_1 &= 0xff00ffff; \ + reg_info_1 |= ((val & 0xff) << 16); \ +} while (0) + +#define SME_SET_CHANNEL_MAX_TX_POWER(reg_info_2, val) do { \ + reg_info_2 &= 0xffff00ff; \ + reg_info_2 |= ((val & 0xff) << 8); \ +} while (0) + +#define SME_CONFIG_TO_ROAM_CONFIG 1 +#define ROAM_CONFIG_TO_SME_CONFIG 2 + +#define NUM_OF_BANDS 2 + +#define SUPPORTED_CRYPTO_CAPS 0x3FFFF + +#define SME_ACTIVE_LIST_CMD_TIMEOUT_VALUE (30*1000) +#define SME_CMD_TIMEOUT_VALUE (SME_ACTIVE_LIST_CMD_TIMEOUT_VALUE + 1000) + +/* Disconnect timeout = vdev stop + bss peer delete + 1 sec */ +#define SME_CMD_VDEV_DISCONNECT_TIMEOUT (STOP_RESPONSE_TIMER + \ + SIR_DELETE_STA_TIMEOUT + 1000) +#define SME_DISCONNECT_TIMEOUT (SME_CMD_VDEV_DISCONNECT_TIMEOUT + 1000) + +/* AP start timeout = vdev start + 2 sec */ +#define SME_CMD_VDEV_START_BSS_TIMEOUT (START_RESPONSE_TIMER + 2000) +#define SME_CMD_START_BSS_TIMEOUT (SME_CMD_VDEV_START_BSS_TIMEOUT + 1000) + +/* AP stop timeout = vdev stop + self peer delete + 1 sec */ +#define SME_CMD_STOP_BSS_CMD_TIMEOUT (STOP_RESPONSE_TIMER + \ + SIR_DELETE_STA_TIMEOUT + 1000) +#define SME_CMD_STOP_BSS_TIMEOUT (SME_CMD_STOP_BSS_CMD_TIMEOUT + 1000) + +/* Peer disconenct timeout = peer delete + 1 sec */ +#define SME_CMD_PEER_DISCONNECT_TIMEOUT (SIR_DELETE_STA_TIMEOUT + 1000) +#define SME_PEER_DISCONNECT_TIMEOUT (SME_CMD_PEER_DISCONNECT_TIMEOUT + 1000) + +#define SME_CMD_GET_DISCONNECT_STATS_TIMEOUT 200 + +/* Roam cmds timeout = vdev start + peer assoc + 1 sec */ +#define SME_CMD_ROAM_CMD_TIMEOUT (START_RESPONSE_TIMER + \ + SIR_PEER_ASSOC_TIMEOUT + 1000) +#define SME_CMD_ADD_DEL_TS_TIMEOUT (4 * 1000) + +/* + * POLICY_MGR_SER_CMD_TIMEOUT should be same as SME_CMD_POLICY_MGR_CMD_TIMEOUT + * if SME_CMD_POLICY_MGR_CMD_TIMEOUT is changed change + * POLICY_MGR_SER_CMD_TIMEOUT as well. + */ +#define SME_CMD_POLICY_MGR_CMD_TIMEOUT (SIR_VDEV_PLCY_MGR_TIMEOUT + 1000) +#define SME_POLICY_MGR_CMD_TIMEOUT (SME_CMD_POLICY_MGR_CMD_TIMEOUT + 1000) + +#define SME_VDEV_DELETE_CMD_TIMEOUT (DELETE_RESPONSE_TIMER + 2000) +#define SME_CMD_VDEV_CREATE_DELETE_TIMEOUT QDF_MAX(13000, \ + SME_VDEV_DELETE_CMD_TIMEOUT + 1) + +/*-------------------------------------------------------------------------- + Type declarations + ------------------------------------------------------------------------*/ +struct sme_config_params { + struct csr_config_params csr_config; +}; + +#ifdef FEATURE_WLAN_TDLS +#define BW_20_OFFSET_BIT 0 +#define BW_40_OFFSET_BIT 1 +#define BW_80_OFFSET_BIT 2 +#define BW_160_OFFSET_BIT 3 +#endif /* FEATURE_WLAN_TDLS */ + +/* Thermal Mitigation*/ +typedef struct { + uint16_t smeMinTempThreshold; + uint16_t smeMaxTempThreshold; +} tSmeThermalLevelInfo; + +typedef enum { + SME_AC_BK = 0, + SME_AC_BE = 1, + SME_AC_VI = 2, + SME_AC_VO = 3 +} sme_ac_enum_type; + +/* + * Enumeration of the various TSPEC directions + * From 802.11e/WMM specifications + */ +enum sme_qos_wmm_dir_type { + SME_QOS_WMM_TS_DIR_UPLINK = 0, + SME_QOS_WMM_TS_DIR_DOWNLINK = 1, + SME_QOS_WMM_TS_DIR_RESV = 2, /* Reserved */ + SME_QOS_WMM_TS_DIR_BOTH = 3, +}; + +/** + * struct sme_oem_capability - OEM capability to be exchanged between host + * and userspace + * @ftm_rr: FTM range report capability bit + * @lci_capability: LCI capability bit + * @reserved1: reserved + * @reserved2: reserved + */ +struct sme_oem_capability { + uint32_t ftm_rr:1; + uint32_t lci_capability:1; + uint32_t reserved1:30; + uint32_t reserved2; +}; + +/** + * struct sme_5g_pref_params : 5G preference params to be read from ini + * @rssi_boost_threshold_5g: RSSI threshold above which 5 GHz is favored + * @rssi_boost_factor_5g: Factor by which 5GHz RSSI is boosted + * @max_rssi_boost_5g: Maximum boost that can be applied to 5GHz RSSI + * @rssi_penalize_threshold_5g: RSSI threshold below which 5G is not favored + * @rssi_penalize_factor_5g: Factor by which 5GHz RSSI is penalized + * @max_rssi_penalize_5g: Maximum penalty that can be applied to 5G RSSI + */ +struct sme_5g_band_pref_params { + int8_t rssi_boost_threshold_5g; + uint8_t rssi_boost_factor_5g; + uint8_t max_rssi_boost_5g; + int8_t rssi_penalize_threshold_5g; + uint8_t rssi_penalize_factor_5g; + uint8_t max_rssi_penalize_5g; +}; + +/** + * struct sme_session_params: Session creation params passed by HDD layer + * @vdev: pointer to vdev object + */ +struct sme_session_params { + struct wlan_objmgr_vdev *vdev; +}; + +#define MAX_CANDIDATE_INFO 10 + +/** + * struct bss_candidate_info - Candidate bss information + * + * @bssid : BSSID of candidate bss + * @status : status code for candidate bss + */ +struct bss_candidate_info { + struct qdf_mac_addr bssid; + uint32_t status; +}; + +/* + * MBO transition reason codes + */ +enum { + MBO_TRANSITION_REASON_UNSPECIFIED, + MBO_TRANSITION_REASON_EXCESSIVE_FRAME_LOSS_RATE, + MBO_TRANSITION_REASON_EXCESSIVE_DELAY_FOR_CURRENT_TRAFFIC, + MBO_TRANSITION_REASON_INSUFFICIENT_BANDWIDTH_FOR_CURRENT_TRAFFIC, + MBO_TRANSITION_REASON_LOAD_BALANCING, + MBO_TRANSITION_REASON_LOW_RSSI, + MBO_TRANSITION_REASON_RECEIVED_EXCESSIVE_RETRANSMISSIONS, + MBO_TRANSITION_REASON_HIGH_INTERFERENCE, + MBO_TRANSITION_REASON_GRAY_ZONE, + MBO_TRANSITION_REASON_TRANSITIONING_TO_PREMIUM_AP, +}; + +/*------------------------------------------------------------------------- + Function declarations and documenation + ------------------------------------------------------------------------*/ +QDF_STATUS sme_open(mac_handle_t mac_handle); +QDF_STATUS sme_init_chan_list(mac_handle_t mac_handle, uint8_t *alpha2, + enum country_src cc_src); +QDF_STATUS sme_close(mac_handle_t mac_handle); +QDF_STATUS sme_start(mac_handle_t mac_handle); + +/** + * sme_stop() - Stop all SME modules and put them at idle state + * @mac_handle: Opaque handle to the MAC context + * + * The function stops each module in SME. Upon return, all modules are + * at idle state ready to start. + * + * This is a synchronous call + * + * Return: QDF_STATUS_SUCCESS if SME is stopped. Other status means + * SME failed to stop one or more modules but caller should + * still consider SME is stopped. + */ +QDF_STATUS sme_stop(mac_handle_t mac_handle); + +/** + * sme_populate_nss_chain_params() - fill vdev nss chain params from ini + * @mac_handle: The handle returned by mac_open. + * @vdev_ini_cfg: pointer to the structure to be filled + * @device_mode: device mode (eg STA, SAP etc.) + * @rf_chains_supported: number of chains supported by fw(updated during + * service ready event) + * + * This API will fill the nss chain params for the particular vdev from ini + * configuration for the respective vdev. + * + * Return: none + */ +void sme_populate_nss_chain_params(mac_handle_t mac_handle, + struct wlan_mlme_nss_chains *vdev_ini_cfg, + enum QDF_OPMODE device_mode, + uint8_t rf_chains_supported); + +/** + * sme_store_nss_chains_cfg_in_vdev() - fill vdev nss chain params from ini + * @vdev: Pointer to vdev obj + * @vdev_ini_cfg: pointer to the structure the values are to be filled from + * + * This API will copy the nss chain params for the particular vdev from ini + * configuration to the respective vdev's dynamic, and ini config. + * + * Return: none + */ +void +sme_store_nss_chains_cfg_in_vdev(struct wlan_objmgr_vdev *vdev, + struct wlan_mlme_nss_chains *vdev_ini_cfg); + +/** + * sme_modify_nss_chains_tgt_cfg() - Change the nss in ini for + * particular opmode, and band, according to the chain config supported by FW. + * @mac_handle: The handle returned by mac_open. + * @vdev_op_mode: vdev operation mode. + * @band:- band for which user wants to change nss. + * + * This API will change the nss in ini (for eg. rx_nss_2g) in the mlme cfg i.e + * the global config structure kept in mac context, according to the max + * supported chains per band which is got as part of ext service ready event. + * + * Return: none + */ +void +sme_modify_nss_chains_tgt_cfg(mac_handle_t mac_handle, + enum QDF_OPMODE vdev_op_mode, + enum nss_chains_band_info band); + +/** + * sme_update_nss_in_mlme_cfg() - Change the nss in ini(rx_nss_(band)) for + * particular opmode, and band. + * @mac_handle: The handle returned by mac_open. + * @rx_nss: new value of rx nss that user wants to change. + * @tx_nss: new value of tx nss that user wants to change. + * @vdev_op_mode: vdev operation mode. + * @band:- band for which user wants to change nss. + * + * This API will change the nss in ini (for eg. rx_nss_2g) in the mlme cfg i.e + * the global config structure kept in mac context. + * + * Return: none + */ +void +sme_update_nss_in_mlme_cfg(mac_handle_t mac_handle, + uint8_t rx_nss, uint8_t tx_nss, + enum QDF_OPMODE vdev_op_mode, + enum nss_chains_band_info band); + +/** + * sme_nss_chains_update() - validate and send the user params to fw + * @mac_handle: The handle returned by mac_open. + * @user_cfg: pointer to the structure to be validated and sent to fw + * @vdev_id: vdev id + * + * + * This API will validate the config, and if found correct will update the + * config in dynamic config, and send to the fw. + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_nss_chains_update(mac_handle_t mac_handle, + struct wlan_mlme_nss_chains *user_cfg, + uint8_t vdev_id); + +/** + * sme_vdev_create() - Create vdev for given persona + * @mac_handle: The handle returned by mac_open + * @vdev_params: params required for vdev creation + * + * This API will create the object manager vdev and in the same + * context vdev mlme object manager notification is invoked, which + * will send the vdev create to the firmware. + * + * If the vdev creation is successful the following object is referenced + * by below modules: + * 1) WLAN_OBJMGR_ID + * 2) WLAN_LEGACY_SME_ID + * 3) WLAN_LEGACY_WMA_ID + * + * Return: Newly created Vdev object or NULL incase in any error + */ +struct wlan_objmgr_vdev *sme_vdev_create(mac_handle_t mac_handle, + struct wlan_vdev_create_params *vdev_params); + + +/** + * sme_vdev_delete() - Delete vdev for given id + * @mac_handle: The handle returned by mac_open. + * @vdev: VDEV Object + * + * This is a synchronous API. This API needs to be called to delete vdev + * in SME module before terminating the session completely. + * + * The following modules releases their reference to the vdev object: + * 1) WLAN_LEGACY_WMA_ID + * 2) WLAN_LEGACY_SME_ID + * + * Return: QDF_STATUS_SUCCESS - vdev is deleted. + * QDF_STATUS_E_INVAL when failed to delete vdev. + */ +QDF_STATUS sme_vdev_delete(mac_handle_t mac_handle, + struct wlan_objmgr_vdev *vdev); + +/** + * sme_cleanup_session() - clean up sme session info for vdev + * @mac_handle: mac handle + * @vdev_id: vdev id + * + * Return: none + */ +void sme_cleanup_session(mac_handle_t mac_handle, uint8_t vdev_id); + +/** + * sme_set_curr_device_mode() - Sets the current operating device mode. + * @mac_handle: The handle returned by mac_open. + * @curr_device_mode: Current operating device mode. + */ +void sme_set_curr_device_mode(mac_handle_t mac_handle, + enum QDF_OPMODE curr_device_mode); + +/** + * sme_update_nud_config() - update nud config + * @mac_handle: The handle returned by mac_open. + * @nud_fail_behavior: Vlaue of nud fail behaviour + */ +void sme_update_nud_config(mac_handle_t mac_handle, uint8_t nud_fail_behavior); + +QDF_STATUS sme_update_roam_params(mac_handle_t mac_handle, + uint8_t session_id, + struct roam_ext_params *roam_params_src, + int update_param); +QDF_STATUS sme_update_config(mac_handle_t mac_handle, + struct sme_config_params *pSmeConfigParams); + +QDF_STATUS sme_set11dinfo(mac_handle_t mac_handle, + struct sme_config_params *pSmeConfigParams); +QDF_STATUS sme_hdd_ready_ind(mac_handle_t mac_handle); + +#ifdef WLAN_BCN_RECV_FEATURE +/* + * sme_register_bcn_report_pe_cb() - Register SME callback + * @mac_handle: The handle returned by mac_open. + * @cb: cb of type beacon_report_cb + * + * This function Register SME callback in order to send + * beacon report to upper layer + * + * Return QDF_STATUS_SUCCESS - + */ +QDF_STATUS +sme_register_bcn_report_pe_cb(mac_handle_t mac_handle, beacon_report_cb cb); +#else +static inline QDF_STATUS +sme_register_bcn_report_pe_cb(mac_handle_t mac_handle, beacon_report_cb cb) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * sme_ser_cmd_callback() - callback from serialization module + * @cmd: serialization command + * @reason: reason why serialization module has given this callback + * + * Serialization module will give callback to SME for why it triggered + * the callback + * + * Return: QDF_STATUS_SUCCESS + */ +QDF_STATUS sme_ser_cmd_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason); + +/** + * sme_purge_pdev_all_ser_cmd_list() - purge all scan and non-scan + * active and pending cmds for pdev + * @mac_handle: pointer to global MAC context + * + * Return : none + */ +void sme_purge_pdev_all_ser_cmd_list(mac_handle_t mac_handle); + +/* + * sme_process_msg() - The main message processor for SME. + * @mac: The global mac context + * @msg: The message to be processed. + * + * This function is called by a message dispatcher when to process a message + * targeted for SME. + * This is a synchronous call + * + * Return: QDF_STATUS_SUCCESS - SME successfully processed the message. + * Other status means SME failed to process the message to HAL. + */ +QDF_STATUS sme_process_msg(struct mac_context *mac, struct scheduler_msg *pMsg); + +QDF_STATUS sme_mc_process_handler(struct scheduler_msg *msg); +/* + * sme_scan_get_result() - Return scan results based on filter + * @mac: Pointer to Global MAC structure + * @vdev_id: vdev_id + * @filter: If pFilter is NULL, all cached results are returned + * @phResult: an object for the result. + * + * Return QDF_STATUS + */ +QDF_STATUS sme_scan_get_result(mac_handle_t mac_handle, uint8_t vdev_id, + struct scan_filter *filter, + tScanResultHandle *phResult); +QDF_STATUS sme_get_ap_channel_from_scan_cache( + struct csr_roam_profile *profile, + tScanResultHandle *scan_cache, + uint32_t *ap_ch_freq); +/** + * sme_get_ap_channel_from_scan() - a wrapper function to get + * AP's channel id from + * CSR by filtering the + * result which matches + * our roam profile. + * @profile: SAP profile + * @ap_ch_freq: pointer to channel id of SAP. Fill the value after finding the + * best ap from scan cache. + * + * This function is written to get AP's channel id from CSR by filtering + * the result which matches our roam profile. This is a synchronous call. + * + * Return: QDF_STATUS. + */ +QDF_STATUS sme_get_ap_channel_from_scan(void *profile, + tScanResultHandle *scan_cache, + uint32_t *ap_ch_freq); + +tCsrScanResultInfo *sme_scan_result_get_first(mac_handle_t, + tScanResultHandle hScanResult); +tCsrScanResultInfo *sme_scan_result_get_next(mac_handle_t, + tScanResultHandle hScanResult); +QDF_STATUS sme_scan_result_purge(tScanResultHandle hScanResult); +QDF_STATUS sme_roam_connect(mac_handle_t mac_handle, uint8_t sessionId, + struct csr_roam_profile *pProfile, uint32_t *pRoamId); +QDF_STATUS sme_roam_reassoc(mac_handle_t mac_handle, uint8_t sessionId, + struct csr_roam_profile *pProfile, + tCsrRoamModifyProfileFields modProfileFields, + uint32_t *pRoamId, bool fForce); + +/** + * sme_roam_disconnect() - API to request CSR to disconnect + * @mac_handle: Opaque handle to the global MAC context + * @session: SME session identifier + * @reason: Reason to disconnect + * @mac_reason: Reason to disconnect as per enum eSirMacReasonCodes + * + * Return: QDF Status success or failure + */ +QDF_STATUS sme_roam_disconnect(mac_handle_t mac_handle, uint8_t session, + eCsrRoamDisconnectReason reason, + tSirMacReasonCodes mac_reason); + +void sme_dhcp_done_ind(mac_handle_t mac_handle, uint8_t session_id); +QDF_STATUS sme_roam_stop_bss(mac_handle_t mac_handle, uint8_t sessionId); +QDF_STATUS sme_roam_disconnect_sta(mac_handle_t mac_handle, uint8_t sessionId, + struct csr_del_sta_params *p_del_sta_params); +QDF_STATUS sme_roam_deauth_sta(mac_handle_t mac_handle, uint8_t sessionId, + struct csr_del_sta_params *pDelStaParams); +QDF_STATUS sme_roam_get_connect_profile(mac_handle_t mac_handle, + uint8_t sessionId, + tCsrRoamConnectedProfile *pProfile); +void sme_roam_free_connect_profile(tCsrRoamConnectedProfile *profile); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * sme_set_roam_scan_ch_event_cb() - Register roam scan ch callback + * @mac_handle: Opaque handle to the MAC context + * @cb: callback to be registered + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_set_roam_scan_ch_event_cb(mac_handle_t mac_handle, + sme_get_raom_scan_ch_callback cb); + +/** + * sme_get_roam_scan_ch() -API to get roam scan channels + * @mac_handle: Pointer to mac handle + * @sta_id: vdev id + * @pcontext: pointer to the context + * + * Extract number of frequencies and frequency list from chan_info and print + * to the logs. + * + * Return: None + */ +QDF_STATUS +sme_get_roam_scan_ch(mac_handle_t mac_handle, + uint8_t vdev_id, void *pcontext); + +/** + * sme_get_pmk_info(): A wrapper function to request CSR to save PMK + * @mac_handle: Global structure + * @session_id: SME session_id + * @pmk_cache: pointer to a structure of pmk + * + * Return: none + */ +void sme_get_pmk_info(mac_handle_t mac_handle, uint8_t session_id, + tPmkidCacheInfo *pmk_cache); + +QDF_STATUS sme_roam_set_psk_pmk(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t *psk_pmk, size_t pmk_len, + bool update_to_fw); +#else +static inline +void sme_get_pmk_info(mac_handle_t mac_handle, uint8_t session_id, + tPmkidCacheInfo *pmk_cache) +{} + +static inline QDF_STATUS +sme_get_roam_scan_ch(mac_handle_t mac_handle, + uint8_t vdev_id, void *pcontext) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +sme_set_roam_scan_ch_event_cb(mac_handle_t mac_handle, + void *cb) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline +QDF_STATUS sme_roam_set_psk_pmk(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t *psk_pmk, size_t pmk_len, + bool update_to_fw) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * sme_roam_get_wpa_rsn_req_ie() - Retrieve WPA/RSN Request IE + * @mac_handle: Opaque handle to the global MAC context + * @session_id: ID of the specific session + * @len: Caller allocated memory that has the length of @buf as input. + * Upon returned, @len has the length of the IE store in @buf + * @buf: Caller allocated memory that contain the IE field, if any, + * upon return + * + * A wrapper function to request CSR to return the WPA or RSN IE CSR + * passes to PE to JOIN request or START_BSS request + * This is a synchronous call. + * + * Return: QDF_STATUS - when fail, it usually means the buffer allocated is not + * big enough + */ +QDF_STATUS sme_roam_get_wpa_rsn_req_ie(mac_handle_t mac_handle, + uint8_t session_id, + uint32_t *len, uint8_t *buf); + +/** + * sme_roam_get_wpa_rsn_rsp_ie() - Retrieve WPA/RSN Response IE + * @mac_handle: Opaque handle to the global MAC context + * @session_id: ID of the specific session + * @len: Caller allocated memory that has the length of @buf as input. + * Upon returned, @len has the length of the IE store in @buf + * @buf: Caller allocated memory that contain the IE field, if any, + * upon return + * + * A wrapper function to request CSR to return the WPA or RSN IE CSR + * passes to PE to JOIN request or START_BSS request + * This is a synchronous call. + * + * Return: QDF_STATUS - when fail, it usually means the buffer allocated is not + * big enough + */ +QDF_STATUS sme_roam_get_wpa_rsn_rsp_ie(mac_handle_t mac_handle, + uint8_t session_id, + uint32_t *len, uint8_t *buf); + +QDF_STATUS sme_get_config_param(mac_handle_t mac_handle, + struct sme_config_params *pParam); +QDF_STATUS sme_get_snr(mac_handle_t mac_handle, + tCsrSnrCallback callback, + struct qdf_mac_addr bssId, void *pContext); +#ifdef FEATURE_WLAN_ESE +QDF_STATUS sme_get_tsm_stats(mac_handle_t mac_handle, + tCsrTsmStatsCallback callback, + struct qdf_mac_addr bssId, + void *pContext, uint8_t tid); +QDF_STATUS sme_set_cckm_ie(mac_handle_t mac_handle, + uint8_t sessionId, + uint8_t *pCckmIe, uint8_t cckmIeLen); +QDF_STATUS sme_set_ese_beacon_request(mac_handle_t mac_handle, + const uint8_t sessionId, + const tCsrEseBeaconReq *in_req); + +/** + * sme_set_plm_request() - set plm request + * @mac_handle: Opaque handle to the global MAC context + * @req: Pointer to input plm request + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_set_plm_request(mac_handle_t mac_handle, + struct plm_req_params *req); +#endif /*FEATURE_WLAN_ESE */ + +QDF_STATUS sme_get_modify_profile_fields(mac_handle_t mac_handle, + uint8_t sessionId, + tCsrRoamModifyProfileFields * + pModifyProfileFields); + +#ifdef FEATURE_OEM_DATA_SUPPORT +QDF_STATUS sme_register_oem_data_rsp_callback(mac_handle_t mac_handle, + sme_send_oem_data_rsp_msg callback); +void sme_deregister_oem_data_rsp_callback(mac_handle_t mac_handle); + +#else +static inline +QDF_STATUS sme_register_oem_data_rsp_callback(mac_handle_t mac_handle, + void *callback) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void sme_deregister_oem_data_rsp_callback(mac_handle_t mac_handle) +{ +} + +#endif + +QDF_STATUS sme_get_country_code(mac_handle_t mac_handle, uint8_t *pBuf, + uint8_t *pbLen); + +QDF_STATUS sme_generic_change_country_code(mac_handle_t mac_handle, + uint8_t *pCountry); + + +/** + * sme_update_channel_list() - Update configured channel list to fwr + * This is a synchronous API. + * @mac_handle: Opaque handle to the global MAC context. + * + * Return: QDF_STATUS SUCCESS. + * FAILURE or RESOURCES The API finished and failed. + */ +QDF_STATUS sme_update_channel_list(mac_handle_t mac_handle); + +QDF_STATUS sme_tx_fail_monitor_start_stop_ind(mac_handle_t mac_handle, + uint8_t tx_fail_count, + void *txFailIndCallback); +QDF_STATUS sme_dhcp_start_ind(mac_handle_t mac_handle, + uint8_t device_mode, + uint8_t *macAddr, uint8_t sessionId); +QDF_STATUS sme_dhcp_stop_ind(mac_handle_t mac_handle, + uint8_t device_mode, + uint8_t *macAddr, uint8_t sessionId); +QDF_STATUS sme_neighbor_report_request(mac_handle_t mac_handle, + uint8_t sessionId, + tpRrmNeighborReq pRrmNeighborReq, + tpRrmNeighborRspCallbackInfo callbackInfo); + +#ifdef FEATURE_OEM_DATA +/** + * sme_oem_data_cmd() - the wrapper to send oem data cmd to wma + * @mac_handle: Opaque handle to the global MAC context. + * @@oem_data_event_handler_cb: callback to be registered + * @oem_data: the pointer of oem data + * @vdev id: vdev id to fetch adapter + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_oem_data_cmd(mac_handle_t mac_handle, + void (*oem_data_event_handler_cb) + (const struct oem_data *oem_event_data, + uint8_t vdev_id), + struct oem_data *oem_data, + uint8_t vdev_id); +#endif + +#ifdef FEATURE_OEM_DATA_SUPPORT +/** + * sme_oem_req_cmd() - send oem request cmd to WMA + * @mac_handle: Opaque handle to the global MAC context + * @oem_req: OEM data request + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_oem_req_cmd(mac_handle_t mac_handle, + struct oem_data_req *oem_req); +QDF_STATUS sme_oem_update_capability(mac_handle_t mac_handle, + struct sme_oem_capability *cap); +QDF_STATUS sme_oem_get_capability(mac_handle_t mac_handle, + struct sme_oem_capability *cap); +#endif /*FEATURE_OEM_DATA_SUPPORT */ +QDF_STATUS sme_change_mcc_beacon_interval(uint8_t sessionId); +QDF_STATUS sme_set_host_offload(mac_handle_t mac_handle, uint8_t sessionId, + struct sir_host_offload_req *pRequest); +QDF_STATUS sme_set_keep_alive(mac_handle_t mac_handle, uint8_t sessionId, + struct keep_alive_req *pRequest); +QDF_STATUS sme_get_operation_channel(mac_handle_t mac_handle, + uint32_t *chan_freq, + uint8_t sessionId); +QDF_STATUS sme_register_mgmt_frame(mac_handle_t mac_handle, uint8_t sessionId, + uint16_t frameType, uint8_t *matchData, + uint16_t matchLen); +QDF_STATUS sme_deregister_mgmt_frame(mac_handle_t mac_handle, + uint8_t sessionId, + uint16_t frameType, uint8_t *matchData, + uint16_t matchLen); +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +QDF_STATUS sme_configure_ext_wow(mac_handle_t mac_handle, + tpSirExtWoWParams wlanExtParams, + csr_readyToSuspendCallback callback, + void *callbackContext); +QDF_STATUS sme_configure_app_type1_params(mac_handle_t mac_handle, + tpSirAppType1Params wlanAppType1Params); +QDF_STATUS sme_configure_app_type2_params(mac_handle_t mac_handle, + tpSirAppType2Params wlanAppType2Params); +#endif +/** + * sme_get_beaconing_concurrent_operation_channel() - To get concurrent + * operating channel frequency of beaconing interface + * @mac_handle: Pointer to mac context + * @vdev_id_to_skip: channel of which vdev id to skip + * + * This routine will return operating channel of active AP/GO channel + * and will skip the channel of vdev_id_to_skip. + * If other no reqested mode is active it will return 0 + * + * Return: uint32_t + */ +uint32_t sme_get_beaconing_concurrent_operation_channel(mac_handle_t mac_handle, + uint8_t vdev_id_to_skip); +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * sme_check_concurrent_channel_overlap() - Get interfering concurrent channel + * @mac_handle: SAP context pointer + * @sap_ch_freq: SAP home channel frequency + * @sapPhyMode: sap phymode + * @cc_switch_mode: force scc channel switch mode + * + * Determine if a concurrent channel is interfering. + * + * Return: Channel freq (Mhz) of the interfering channel, or 0 if none. + */ +uint16_t sme_check_concurrent_channel_overlap(mac_handle_t mac_handle, + uint16_t sap_ch_freq, + eCsrPhyMode sapPhyMode, + uint8_t cc_switch_mode); +#endif + +/** + * sme_get_cfg_valid_channels() - To get valid channel list + * @valid_ch_freq: pointer to array which save the valid channel list + * @len: the length of the valid channel list + * + * Return: QDF status + */ +QDF_STATUS sme_get_cfg_valid_channels(uint32_t *valid_ch_freq, uint32_t *len); + +#ifdef WLAN_FEATURE_PACKET_FILTERING +QDF_STATUS sme_8023_multicast_list(mac_handle_t mac_handle, uint8_t sessionId, + tpSirRcvFltMcAddrList pMulticastAddrs); +#endif /* WLAN_FEATURE_PACKET_FILTERING */ +uint16_t sme_chn_to_freq(uint8_t chanNum); + +/* + * sme_is_channel_valid() - validate a channel against current regdmn + * To check if the channel is valid for currently established domain + * This is a synchronous API. + * + * mac_handle - The handle returned by mac_open. + * chan_freq - channel to verify + * + * Return: true/false, true if channel is valid + */ +bool sme_is_channel_valid(mac_handle_t mac_handle, uint32_t chan_freq); + +QDF_STATUS sme_set_max_tx_power(mac_handle_t mac_handle, + struct qdf_mac_addr pBssid, + struct qdf_mac_addr pSelfMacAddress, int8_t dB); +QDF_STATUS sme_set_max_tx_power_per_band(enum band_info band, int8_t db); +QDF_STATUS sme_set_tx_power(mac_handle_t mac_handle, uint8_t sessionId, + struct qdf_mac_addr bssid, + enum QDF_OPMODE dev_mode, int power); +QDF_STATUS sme_set_custom_mac_addr(tSirMacAddr customMacAddr); +QDF_STATUS sme_hide_ssid(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t ssidHidden); + +/** + * sme_update_roam_scan_n_probes() - Update no.of roam scan probes + * @mac_handle: The handle returned by mac_open + * @vdev_id: vdev identifier + * @probes: number of probe requests to be sent out + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_update_roam_scan_n_probes(mac_handle_t mac_handle, + uint8_t vdev_id, + const uint8_t probes); + +/** + * sme_update_roam_scan_home_away_time() - Update roam scan Home away time + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev identifier + * @roam_scan_home_away_time: Scan home away time + * @send_offload_cmd: If it's true, the command is sent to firmware, + * otherwise the command is not sent to firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_update_roam_scan_home_away_time(mac_handle_t mac_handle, uint8_t vdev_id, + const uint16_t roam_scan_home_away_time, + const bool send_offload_cmd); + +bool sme_get_roam_intra_band(mac_handle_t mac_handle); + +/** + * sme_get_roam_scan_n_probes() - get Roam scan number of probes + * @mac_handle: The handle returned by mac_open + * @vdev_id: vdev identifier + * @roam_scan_n_probes: Buffer to fill the number of probes. + * Valid only if the return status is success. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_roam_scan_n_probes(mac_handle_t mac_handle, uint8_t vdev_id, + uint8_t *roam_scan_n_probes); + +/** + * sme_update_roam_rssi_diff() - Update RoamRssiDiff + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev identifier + * @roam_rssi_diff: Minimum rssi difference between potential candidate and + * current AP. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_update_roam_rssi_diff(mac_handle_t mac_handle, uint8_t vdev_id, + uint8_t roam_rssi_diff); + +/** + * sme_get_roam_scan_home_away_time() - get Roam scan home away time + * @mac_handle: The handle returned by mac_open + * @vdev_id: vdev identifier + * @roam_scan_home_away_time: Buffer to fill the roam scan home away time. + * Valid only if the return status is success. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_roam_scan_home_away_time(mac_handle_t mac_handle, + uint8_t vdev_id, + uint16_t *roam_scan_home_away_time); +QDF_STATUS sme_update_wes_mode(mac_handle_t mac_handle, bool isWESModeEnabled, + uint8_t sessionId); +QDF_STATUS sme_set_roam_scan_control(mac_handle_t mac_handle, uint8_t sessionId, + bool roamScanControl); + +QDF_STATUS sme_update_is_fast_roam_ini_feature_enabled(mac_handle_t mac_handle, + uint8_t sessionId, + const bool + isFastRoamIniFeatureEnabled); + +QDF_STATUS sme_config_fast_roaming(mac_handle_t mac_handle, uint8_t session_id, + const bool is_fast_roam_enabled); + +QDF_STATUS sme_stop_roaming(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t reason, uint32_t requestor); + +QDF_STATUS sme_start_roaming(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t reason, uint32_t requestor); +#ifdef FEATURE_WLAN_ESE +QDF_STATUS sme_update_is_ese_feature_enabled(mac_handle_t mac_handle, + uint8_t sessionId, + const bool isEseIniFeatureEnabled); +#endif /* FEATURE_WLAN_ESE */ +QDF_STATUS sme_set_roam_rescan_rssi_diff(mac_handle_t mac_handle, + uint8_t sessionId, + const uint8_t nRoamRescanRssiDiff); +uint8_t sme_get_roam_rescan_rssi_diff(mac_handle_t mac_handle); + +QDF_STATUS sme_set_roam_opportunistic_scan_threshold_diff( + mac_handle_t mac_handle, + uint8_t sessionId, + const uint8_t nOpportunisticThresholdDiff); +uint8_t sme_get_roam_opportunistic_scan_threshold_diff(mac_handle_t mac_handle); + +/** + * sme_set_neighbor_lookup_rssi_threshold() - update neighbor lookup rssi thr + * @mac_handle: The handle returned by mac_open + * @vdev_id: vdev identifier + * @neighbor_lookup_rssi_threshold: Neighbor lookup rssi threshold + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_set_neighbor_lookup_rssi_threshold(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t neighbor_lookup_rssi_threshold); + +/** + * sme_get_neighbor_lookup_rssi_threshold() - get neighbor lookup rssi threshold + * @mac_handle: The handle returned by mac_open + * @vdev_id: vdev identifier + * @lookup_threshold: Buffer to fill the neighbor lookup threshold. + * Valid only if the return status is success. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_neighbor_lookup_rssi_threshold(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t *lookup_threshold); +QDF_STATUS sme_set_neighbor_scan_refresh_period(mac_handle_t mac_handle, + uint8_t sessionId, uint16_t neighborScanResultsRefreshPeriod); +uint16_t sme_get_neighbor_scan_refresh_period(mac_handle_t mac_handle); + +/** + * sme_get_empty_scan_refresh_period_global() - get global scan refresh period + * @mac_handle: The handle returned by mac_open + * + * Return: Empty scan refresh period configured through ini + */ +uint16_t sme_get_empty_scan_refresh_period_global(mac_handle_t mac_handle); + +/** + * sme_get_empty_scan_refresh_period() - get empty scan refresh period + * @mac_handle: The handle returned by mac_open. + * @vdev_id: vdev identifier + * @refresh_threshold: Buffer to fill the empty scan refresh period. + * Valid only if the return status is success. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_empty_scan_refresh_period(mac_handle_t mac_handle, + uint8_t vdev_id, + uint16_t *refresh_threshold); +QDF_STATUS sme_update_empty_scan_refresh_period(mac_handle_t mac_handle, + uint8_t sessionId, uint16_t empty_scan_refresh_period); +/** + * sme_update_full_roam_scan_period() - Send full roam scan period to SME + * @mac_handle: Opaque handle to the MAC context + * @vdev_id: vdev id + * @full_roam_scan_period: Idle period in seconds between two successive + * full channel roam scans + * + * Updated full scan period in roam info and a roam_offload_scan request. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_update_full_roam_scan_period(mac_handle_t mac_handle, + uint8_t vdev_id, + uint32_t full_roam_scan_period); + +/** + * sme_modify_roam_cand_sel_criteria() - Modify candidate selection criteria + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev Identifier + * @enable_scoring_for_roam: Carries enable/disable indication + * + * Enable/disable scoring for roam candidate selection based on the value of + * enable_scoring_for_roam. Below is the description of enable/disable, + * Disable-0: Disable scoring for roam candidate selection. Roaming + * shall fallback to legacy selection criteria, only RSSI. + * Enable-1 : Enable scoring for roam candidate selection. + * + * Return: Success or failure + */ +QDF_STATUS +sme_modify_roam_cand_sel_criteria(mac_handle_t mac_handle, + uint8_t vdev_id, + bool enable_scoring_for_roam); + +/** + * sme_roam_control_restore_default_config - Restore roam config to default + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev Identifier + * + * Restore enable_scoring_for_roam, emptyScanRefreshPeriod, + * full_roam_scan_period to their default values and send RSO command to + * firmware with the updated values. + * + * Return: Success or failure + */ +QDF_STATUS sme_roam_control_restore_default_config(mac_handle_t mac_handle, + uint8_t vdev_id); + +/** + * sme_roam_reset_configs() - API to reset roam config + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev Identifier + * + * Return: void + */ +void sme_roam_reset_configs(mac_handle_t mac_handle, uint8_t vdev_id); + +QDF_STATUS sme_set_neighbor_scan_min_chan_time(mac_handle_t mac_handle, + const uint16_t nNeighborScanMinChanTime, + uint8_t sessionId); +QDF_STATUS sme_set_neighbor_scan_max_chan_time(mac_handle_t mac_handle, + uint8_t sessionId, + const uint16_t nNeighborScanMaxChanTime); +uint16_t sme_get_neighbor_scan_min_chan_time(mac_handle_t mac_handle, + uint8_t sessionId); +uint32_t sme_get_neighbor_roam_state(mac_handle_t mac_handle, + uint8_t sessionId); +uint32_t sme_get_current_roam_state(mac_handle_t mac_handle, uint8_t sessionId); +uint32_t sme_get_current_roam_sub_state(mac_handle_t mac_handle, + uint8_t sessionId); +uint32_t sme_get_lim_sme_state(mac_handle_t mac_handle); +uint32_t sme_get_lim_mlm_state(mac_handle_t mac_handle); +bool sme_is_lim_session_valid(mac_handle_t mac_handle, uint8_t sessionId); +uint32_t sme_get_lim_sme_session_state(mac_handle_t mac_handle, + uint8_t sessionId); +uint32_t sme_get_lim_mlm_session_state(mac_handle_t mac_handle, + uint8_t sessionId); +uint16_t sme_get_neighbor_scan_max_chan_time(mac_handle_t mac_handle, + uint8_t sessionId); +QDF_STATUS sme_set_neighbor_scan_period(mac_handle_t mac_handle, + uint8_t sessionId, + const uint16_t nNeighborScanPeriod); +uint16_t sme_get_neighbor_scan_period(mac_handle_t mac_handle, + uint8_t sessionId); +QDF_STATUS sme_set_roam_bmiss_first_bcnt(mac_handle_t mac_handle, + uint8_t sessionId, const uint8_t nRoamBmissFirstBcnt); +QDF_STATUS sme_set_roam_bmiss_final_bcnt(mac_handle_t mac_handle, + uint8_t sessionId, + const uint8_t nRoamBmissFinalBcnt); +/** + * sme_get_roam_rssi_diff() - get Roam rssi diff + * @mac_handle: The handle returned by mac_open + * @vdev_id: vdev identifier + * @rssi_diff: Buffer to fill the roam RSSI diff. + * Valid only if the return status is success. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_roam_rssi_diff(mac_handle_t mac_handle, uint8_t vdev_id, + uint8_t *rssi_diff); +QDF_STATUS sme_change_roam_scan_channel_list(mac_handle_t mac_handle, + uint8_t sessionId, + uint32_t *channel_freq_list, + uint8_t numChannels); + +/** + * sme_update_roam_scan_freq_list() - Update roam scan freq list + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev identifier + * @freq_list: List of frequencies to be configured + * @num_channels: Number of frequencies to be configured + * @freq_list_type: Type of frequency list to be configured to + * + * Update the frequencies from freq_list to the corresponding channel list + * in neighborRoamInfo + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_update_roam_scan_freq_list(mac_handle_t mac_handle, uint8_t vdev_id, + uint32_t *freq_list, uint8_t num_chan, + uint32_t freq_list_type); +QDF_STATUS sme_set_ese_roam_scan_channel_list(mac_handle_t mac_handle, + uint8_t sessionId, + uint32_t *chan_freq_list, + uint8_t numChannels); +QDF_STATUS sme_get_roam_scan_channel_list(mac_handle_t mac_handle, + uint32_t *freq_list, + uint8_t *pNumChannels, + uint8_t sessionId); + +/** + * sme_dump_freq_list() - Dump the frequencies from given chan info + * @chan_info: Contains the frequency list and number of frequencies + * + * Extract number of frequencies and frequency list from chan_info and print + * to the logs. + * + * Return: None + */ +void sme_dump_freq_list(tCsrChannelInfo *chan_info); +bool sme_get_is_ese_feature_enabled(mac_handle_t mac_handle); +bool sme_get_wes_mode(mac_handle_t mac_handle); +bool sme_get_roam_scan_control(mac_handle_t mac_handle); +bool sme_get_is_lfr_feature_enabled(mac_handle_t mac_handle); +bool sme_get_is_ft_feature_enabled(mac_handle_t mac_handle); +bool sme_is_feature_supported_by_fw(enum cap_bitmap feature); + +QDF_STATUS sme_set_phy_mode(mac_handle_t mac_handle, eCsrPhyMode phyMode); +eCsrPhyMode sme_get_phy_mode(mac_handle_t mac_handle); +QDF_STATUS sme_handoff_request(mac_handle_t mac_handle, uint8_t sessionId, + tCsrHandoffRequest *pHandoffInfo); + +QDF_STATUS sme_add_periodic_tx_ptrn(mac_handle_t mac_handle, + tSirAddPeriodicTxPtrn *addPeriodicTxPtrnParams); +QDF_STATUS sme_del_periodic_tx_ptrn(mac_handle_t mac_handle, + tSirDelPeriodicTxPtrn *delPeriodicTxPtrnParams); +QDF_STATUS sme_send_rate_update_ind(mac_handle_t mac_handle, + tSirRateUpdateInd *rateUpdateParams); +QDF_STATUS sme_roam_del_pmkid_from_cache(mac_handle_t mac_handle, + uint8_t vdev_id, + struct wlan_crypto_pmksa *pmksa, + bool set_pmk); + +void sme_get_command_q_status(mac_handle_t mac_handle); + +#ifdef FEATURE_WLAN_RMC +QDF_STATUS sme_enable_rmc(mac_handle_t mac_handle, uint32_t sessionId); +QDF_STATUS sme_disable_rmc(mac_handle_t mac_handle, uint32_t sessionId); +QDF_STATUS sme_send_rmc_action_period(mac_handle_t mac_handle, + uint32_t sessionId); +#endif + +#ifdef QCA_IBSS_SUPPORT +/* + * sme_request_ibss_peer_info() - request ibss peer info + * @mac_handle: Opaque handle to the global MAC context + * @cb_context: Pointer to user data + * @peer_info_cb: Peer info callback + * @allPeerInfoReqd: All peer info required or not + * @staIdx: sta index + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_request_ibss_peer_info(mac_handle_t mac_handle, + void *cb_context, + ibss_peer_info_cb peer_info_cb, + bool allPeerInfoReqd, + uint8_t *mac_addr); +#else +static inline +QDF_STATUS sme_request_ibss_peer_info(mac_handle_t mac_handle, + void *cb_context, + ibss_peer_info_cb peer_info_cb, + bool allPeerInfoReqd, + uint8_t *mac_addr) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS sme_send_cesium_enable_ind(mac_handle_t mac_handle, + uint32_t sessionId); + +/** + * sme_set_wlm_latency_level_ind() - Used to set the latency level to fw + * @mac_handle + * @session_id + * @latency_level + * + * Return QDF_STATUS + */ +QDF_STATUS sme_set_wlm_latency_level(mac_handle_t mac_handle, + uint16_t session_id, + uint16_t latency_level); +/* + * SME API to enable/disable idle mode powersave + * This should be called only if powersave offload + * is enabled + */ +QDF_STATUS sme_set_idle_powersave_config(bool value); +QDF_STATUS sme_notify_modem_power_state(mac_handle_t mac_handle, + uint32_t value); + +/*SME API to convert convert the ini value to the ENUM used in csr and MAC*/ +ePhyChanBondState sme_get_cb_phy_state_from_cb_ini_value(uint32_t cb_ini_value); +int sme_update_ht_config(mac_handle_t mac_handle, uint8_t sessionId, + uint16_t htCapab, + int value); +int16_t sme_get_ht_config(mac_handle_t mac_handle, uint8_t session_id, + uint16_t ht_capab); +#ifdef QCA_HT_2040_COEX +QDF_STATUS sme_notify_ht2040_mode(mac_handle_t mac_handle, + struct qdf_mac_addr macAddrSTA, + uint8_t sessionId, + uint8_t channel_type); +QDF_STATUS sme_set_ht2040_mode(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t channel_type, bool obssEnabled); +#endif + +/** + * sme_get_reg_info() - To get tx power information + * @mac_handle: Opaque handle to the global MAC context + * @chan_freq: channel freq + * @regInfo1: first reg info to fill + * @regInfo2: second reg info to fill + * + * This routine will give you tx power information + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_reg_info(mac_handle_t mac_handle, uint32_t chan_freq, + uint32_t *regInfo1, uint32_t *regInfo2); + +#ifdef FEATURE_WLAN_CH_AVOID +QDF_STATUS sme_ch_avoid_update_req(mac_handle_t mac_handle); +#else +static inline +QDF_STATUS sme_ch_avoid_update_req(mac_handle_t mac_handle) +{ + return QDF_STATUS_SUCCESS; +} +#endif +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +/** + * sme_set_auto_shutdown_cb() - Register auto shutdown evt handler + * @mac_handle: Handle to the global MAC context + * @callback_fn: callback function to be invoked when an auto shutdown + * event is received + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_auto_shutdown_cb(mac_handle_t mac_handle, + void (*callback_fn)(void)); + +QDF_STATUS sme_set_auto_shutdown_timer(mac_handle_t mac_handle, + uint32_t timer_value); +#endif +QDF_STATUS sme_roam_channel_change_req(mac_handle_t mac_handle, + struct qdf_mac_addr bssid, + struct ch_params *ch_params, + struct csr_roam_profile *profile); + +QDF_STATUS sme_roam_start_beacon_req(mac_handle_t mac_handle, + struct qdf_mac_addr bssid, + uint8_t dfsCacWaitStatus); + +/** + * sme_roam_csa_ie_request() - request CSA IE transmission from PE + * @mac_handle: handle returned by mac_open + * @bssid: SAP bssid + * @target_chan_freq: target channel frequency information + * @csaIeReqd: CSA IE Request + * @ch_params: channel information + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_csa_restart(struct mac_context *mac_ctx, uint8_t session_id); + +QDF_STATUS sme_roam_csa_ie_request(mac_handle_t mac_handle, + struct qdf_mac_addr bssid, + uint32_t target_chan_freq, uint8_t csaIeReqd, + struct ch_params *ch_params); + +/** + * sme_set_addba_accept() - Allow/Reject the ADDBA req session + * @mac_handle: handle returned by mac_open + * @session_id: sme session id + * @value: Allow/Reject AddBA session + * + * Allows/Rejects the ADDBA req session + * + * Return: 0 on success else errno + */ +int sme_set_addba_accept(mac_handle_t mac_handle, uint8_t session_id, + int value); + +QDF_STATUS sme_init_thermal_info(mac_handle_t mac_handle); + +QDF_STATUS sme_set_thermal_level(mac_handle_t mac_handle, uint8_t level); +QDF_STATUS sme_txpower_limit(mac_handle_t mac_handle, + struct tx_power_limit *psmetx); + +/** + * sme_get_link_speed() - Retrieve current link speed + * @mac_handle: Global MAC handle + * @req: Link speed request structure + * @context: User context to be passed back when invoking @cb + * @cb: Callback function to be invoked with link speed results + * + * Return: QDF_STATUS_SUCCESS if the request was accepted, otherwise + * an appropriate error status. + */ +QDF_STATUS sme_get_link_speed(mac_handle_t mac_handle, + struct link_speed_info *req, + void *context, + sme_link_speed_cb cb); + +QDF_STATUS sme_modify_add_ie(mac_handle_t mac_handle, + tSirModifyIE *pModifyIE, eUpdateIEsType updateType); +QDF_STATUS sme_update_add_ie(mac_handle_t mac_handle, + tSirUpdateIE *pUpdateIE, eUpdateIEsType updateType); +QDF_STATUS sme_update_connect_debug(mac_handle_t mac_handle, + uint32_t set_value); +const char *sme_bss_type_to_string(const uint8_t bss_type); +QDF_STATUS sme_ap_disable_intra_bss_fwd(mac_handle_t mac_handle, + uint8_t sessionId, + bool disablefwd); + +/** + * sme_send_unit_test_cmd() - send unit test command to lower layer + * @session_id: sme session id to be filled while forming the command + * @module_id: module id given by user to be filled in the command + * @arg_count: number of argument count + * @arg: pointer to argument list + * + * This API exposed to HDD layer which takes the argument from user and sends + * down to lower layer for further processing + * + * Return: QDF_STATUS based on overall success + */ +QDF_STATUS sme_send_unit_test_cmd(uint32_t vdev_id, uint32_t module_id, + uint32_t arg_count, uint32_t *arg); + +typedef struct sStatsExtRequestReq { + uint32_t request_data_len; + uint8_t *request_data; +} tStatsExtRequestReq, *tpStatsExtRequestReq; + +#ifdef WLAN_FEATURE_STATS_EXT +/** + * sme_stats_ext_register_callback() - Register stats ext callback + * @mac_handle: Opaque handle to the MAC context + * @callback: Function to be invoked for stats ext events + * + * This function is called to register the callback that send vendor + * event for stats ext + */ +void sme_stats_ext_register_callback(mac_handle_t mac_handle, + stats_ext_cb callback); + +/** + * sme_stats_ext_deregister_callback() - Deregister stats ext callback + * @mac_handle: Opaque handle to the MAC context + * + * This function is called to deregister the callback that send vendor + * event for stats ext + */ +void sme_stats_ext_deregister_callback(mac_handle_t mac_handle); + +/** + * sme_stats_ext2_register_callback() - Register stats ext2 callback + * @mac_handle: Opaque handle to the MAC context + * @callback: Function to be invoked for stats ext2 events + * + * This function will register a callback for frame aggregation failure + * indications processing. + * + * Return: void + */ +void sme_stats_ext2_register_callback(mac_handle_t mac_handle, + stats_ext2_cb callback); + +QDF_STATUS sme_stats_ext_request(uint8_t session_id, + tpStatsExtRequestReq input); +#else +static inline void +sme_stats_ext_register_callback(mac_handle_t mac_handle, + stats_ext_cb callback) +{ +} + +static inline void +sme_stats_ext_deregister_callback(mac_handle_t mac_handle) +{ +} + +static inline void +sme_stats_ext2_register_callback(mac_handle_t mac_handle, + stats_ext2_cb callback) +{ +} +#endif /* WLAN_FEATURE_STATS_EXT */ +QDF_STATUS sme_update_dfs_scan_mode(mac_handle_t mac_handle, + uint8_t sessionId, + uint8_t allowDFSChannelRoam); +uint8_t sme_get_dfs_scan_mode(mac_handle_t mac_handle); + +/** + * sme_get_valid_channels_by_band() - to fetch valid channels filtered by band + * @mac_handle: Opaque handle to the global MAC context + * @wifi_band: RF band information + * @valid_chan_list: output array to store channel info + * @valid_chan_len: output number of channels + * + * SME API to fetch all valid channels filtered by band + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_valid_channels_by_band(mac_handle_t mac_handle, + uint8_t wifi_band, + uint32_t *valid_chan_list, + uint8_t *valid_chan_len); + +#ifdef FEATURE_WLAN_EXTSCAN +/** + * sme_ext_scan_get_capabilities() - SME API to fetch extscan capabilities + * @mac_handle: Opaque handle to the MAC context + * @params: extscan capabilities request structure + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_ext_scan_get_capabilities(mac_handle_t mac_handle, + struct extscan_capabilities_params *params); + +/** + * sme_ext_scan_start() - SME API to issue extscan start + * @mac_handle: Opaque handle to the MAC context + * @params: extscan start structure + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_ext_scan_start(mac_handle_t mac_handle, + struct wifi_scan_cmd_req_params *params); + +/** + * sme_ext_scan_stop() - SME API to issue extscan stop + * @mac_handle: Opaque handle to the MAC context + * @params: extscan stop structure + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_ext_scan_stop(mac_handle_t mac_handle, + struct extscan_stop_req_params *params); + +/** + * sme_set_bss_hotlist() - SME API to set BSSID hotlist + * @mac_handle: Opaque handle to the MAC context + * @params: extscan set hotlist structure + * + * Handles the request to set the BSSID hotlist in firmware. + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_set_bss_hotlist(mac_handle_t mac_handle, + struct extscan_bssid_hotlist_set_params *params); + +/** + * sme_reset_bss_hotlist() - SME API to reset BSSID hotlist + * @mac_handle: Opaque handle to the MAC context + * @params: extscan reset hotlist structure + * + * Handles the request to reset the BSSID hotlist in firmware. + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_reset_bss_hotlist(mac_handle_t mac_handle, + struct extscan_bssid_hotlist_reset_params *params); + +/** + * sme_set_significant_change() - SME API to set significant change + * @mac_handle: Opaque handle to the MAC context + * @params: extscan set significant change structure + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_set_significant_change(mac_handle_t mac_handle, + struct extscan_set_sig_changereq_params *params); + +/** + * sme_reset_significant_change() - SME API to reset significant change + * @mac_handle: Opaque handle to the MAC context + * @params: extscan reset significant change structure + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_reset_significant_change(mac_handle_t mac_handle, + struct extscan_capabilities_reset_params *params); + +/** + * sme_get_cached_results() - SME API to get cached results + * @mac_handle: Opaque handle to the MAC context + * @params: extscan get cached results structure + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_get_cached_results(mac_handle_t mac_handle, + struct extscan_cached_result_params *params); + +/** + * sme_set_epno_list() - set epno network list + * @mac_handle: Opaque handle to the MAC context + * @params: request message + * + * This function sends an Enhanced PNO configuration to firmware. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_set_epno_list(mac_handle_t mac_handle, + struct wifi_enhanced_pno_params *params); + +/** + * sme_set_passpoint_list() - set passpoint network list + * @mac_handle: Opaque handle to the MAC context + * @params: set passpoint list request parameters + * + * This function constructs the cds message and fill in message type, + * bodyptr with @params and posts it to WDA queue. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_set_passpoint_list(mac_handle_t mac_handle, + struct wifi_passpoint_req_param *params); + +/** + * sme_reset_passpoint_list() - reset passpoint network list + * @mac_handle: Opaque handle to the MAC context + * @params: reset passpoint list request parameters + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_reset_passpoint_list(mac_handle_t mac_handle, + struct wifi_passpoint_req_param *params); + +QDF_STATUS sme_ext_scan_register_callback(mac_handle_t mac_handle, + ext_scan_ind_cb ext_scan_ind_cb); +#else +static inline +QDF_STATUS sme_ext_scan_register_callback(mac_handle_t mac_handle, + ext_scan_ind_cb ext_scan_ind_cb) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_WLAN_EXTSCAN */ +QDF_STATUS sme_abort_roam_scan(mac_handle_t mac_handle, uint8_t sessionId); + +/** + * sme_get_vht_ch_width() - SME API to get the max supported FW chan width + * + * Return: Max channel width supported by FW (eg. 20, 40, 80, 160, 80+80) + */ +uint32_t sme_get_vht_ch_width(void); + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS +QDF_STATUS sme_ll_stats_clear_req(mac_handle_t mac_handle, + tSirLLStatsClearReq * pclearStatsReq); +QDF_STATUS sme_ll_stats_set_req(mac_handle_t mac_handle, + tSirLLStatsSetReq *psetStatsReq); + +/** + * sme_ll_stats_get_req() - SME API to get the Link Layer Statistics + * @mac_handle: Global MAC handle + * @get_stats_req: Link Layer get stats request params structure + * @context: Callback context + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_ll_stats_get_req(mac_handle_t mac_handle, + tSirLLStatsGetReq *get_stats_req, + void *context); + +/** + * sme_set_link_layer_stats_ind_cb() - + * SME API to trigger the stats are available after get request + * @mac_handle: MAC handle + * @callback: HDD callback which needs to be invoked after + * getting status notification from FW + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_link_layer_stats_ind_cb(mac_handle_t mac_handle, + link_layer_stats_cb callback); + +QDF_STATUS sme_set_link_layer_ext_cb(mac_handle_t mac_handle, + void (*ll_stats_ext_cb)(hdd_handle_t callback_ctx, + tSirLLStatsResults * rsp)); +QDF_STATUS sme_reset_link_layer_stats_ind_cb(mac_handle_t mac_handle); +QDF_STATUS sme_ll_stats_set_thresh(mac_handle_t mac_handle, + struct sir_ll_ext_stats_threshold *threshold); +#else /* WLAN_FEATURE_LINK_LAYER_STATS */ +static inline QDF_STATUS +sme_set_link_layer_ext_cb(mac_handle_t mac_handle, void (*ll_stats_ext_cb) + (hdd_handle_t callback_ctx, tSirLLStatsResults + *rsp)) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +sme_set_link_layer_stats_ind_cb(mac_handle_t mac_handle, + link_layer_stats_cb callback) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +sme_reset_link_layer_stats_ind_cb(mac_handle_t mac_handle) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +QDF_STATUS sme_set_wisa_params(mac_handle_t mac_handle, + struct sir_wisa_params *wisa_params); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS sme_update_roam_key_mgmt_offload_enabled(mac_handle_t mac_handle, + uint8_t session_id, + bool key_mgmt_offload_enabled, + struct pmkid_mode_bits *pmkid_modes); +#endif +QDF_STATUS sme_get_link_status(mac_handle_t mac_handle, + csr_link_status_callback callback, + void *context, uint8_t session_id); +QDF_STATUS sme_get_temperature(mac_handle_t mac_handle, + void *tempContext, + void (*pCallbackfn)(int temperature, + void *pContext)); + +/** + * sme_set_scanning_mac_oui() - SME API to set scanning mac oui + * @mac_handle: MAC Handle + * @scan_mac_oui: Scanning Mac Oui + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_scanning_mac_oui(mac_handle_t mac_handle, + struct scan_mac_oui *scan_mac_oui); + +#ifdef DHCP_SERVER_OFFLOAD +/** + * sme_set_dhcp_srv_offload() - Set DHCP server offload + * @mac_handle: Handle to the global MAC context + * @dhcp_srv_info : DHCP server offload info struct + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_set_dhcp_srv_offload(mac_handle_t mac_handle, + struct dhcp_offload_info_params *dhcp_srv_info); +#endif /* DHCP_SERVER_OFFLOAD */ +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING +QDF_STATUS sme_set_led_flashing(mac_handle_t mac_handle, uint8_t type, + uint32_t x0, uint32_t x1); +#endif +QDF_STATUS sme_enable_dfs_chan_scan(mac_handle_t mac_handle, uint8_t dfs_flag); +QDF_STATUS sme_set_mas(uint32_t val); +QDF_STATUS sme_set_miracast(mac_handle_t mac_handle, uint8_t filter_type); +QDF_STATUS sme_ext_change_channel(mac_handle_t mac_handle, uint32_t channel, + uint8_t session_id); + +QDF_STATUS sme_configure_stats_avg_factor(mac_handle_t mac_handle, + uint8_t session_id, + uint16_t stats_avg_factor); + +QDF_STATUS sme_configure_guard_time(mac_handle_t mac_handle, uint8_t session_id, + uint32_t guard_time); + +QDF_STATUS sme_wifi_start_logger(mac_handle_t mac_handle, + struct sir_wifi_start_log start_log); + +bool sme_neighbor_middle_of_roaming(mac_handle_t mac_handle, + uint8_t sessionId); + +/** + * sme_is_any_session_in_middle_of_roaming() - check if roaming is in progress + * @mac_handle: MAC Handle + * + * Checks if any SME session is in middle of roaming + * + * Return: true if roaming is in progress else false + */ +bool sme_is_any_session_in_middle_of_roaming(mac_handle_t mac_handle); + +/** + * sme_send_flush_logs_cmd_to_fw() - Initiate command to FW to flush logs + * + * This function will initiate a command to firmware to flush their logs. + * This should normally be done in response to an anomaly detected by the + * host. + * + * Return: QDF_STATUS_SUCCESS if the command was sent, otherwise an + * appropriate QDF_STATUS error + */ +QDF_STATUS sme_send_flush_logs_cmd_to_fw(void); + +/** + * sme_enable_uapsd_for_ac() - enable uapsd for access category request to WMA + * @ac: access category + * @tid: tid value + * @pri: user priority + * @srvc_int: service interval + * @sus_int: suspend interval + * @dir: tspec direction + * @psb: PSB value + * @sessionId: session id + * @delay_interval: delay interval + * + * Return: QDF status + */ +QDF_STATUS sme_enable_uapsd_for_ac(sme_ac_enum_type ac, uint8_t tid, + uint8_t pri, uint32_t srvc_int, + uint32_t sus_int, + enum sme_qos_wmm_dir_type dir, + uint8_t psb, uint32_t sessionId, + uint32_t delay_interval); + +/** + * sme_disable_uapsd_for_ac() - disable uapsd access category request to WMA + * @ac: access category + * @sessionId: session id + * + * Return: QDF status + */ +QDF_STATUS sme_disable_uapsd_for_ac(sme_ac_enum_type ac, uint32_t sessionId); + +#ifdef FEATURE_RSSI_MONITOR +QDF_STATUS sme_set_rssi_monitoring(mac_handle_t mac_handle, + struct rssi_monitor_param *input); + +/** + * sme_set_rssi_threshold_breached_cb() - Set RSSI threshold breached callback + * @mac_handle: global MAC handle + * @cb: callback function pointer + * + * This function registers the RSSI threshold breached callback function. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_set_rssi_threshold_breached_cb(mac_handle_t mac_handle, + rssi_threshold_breached_cb cb); +#else /* FEATURE_RSSI_MONITOR */ +static inline +QDF_STATUS sme_set_rssi_threshold_breached_cb(mac_handle_t mac_handle, + rssi_threshold_breached_cb cb) +{ + return QDF_STATUS_SUCCESS; +} +#endif +/** + * sme_reset_rssi_threshold_breached_cb() - Reset RSSI threshold breached + * callback + * @mac_handle: global MAC handle + * + * This function de-registers the RSSI threshold breached callback function. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_reset_rssi_threshold_breached_cb(mac_handle_t mac_handle); + +QDF_STATUS sme_register_mgmt_frame_ind_callback(mac_handle_t mac_handle, + sir_mgmt_frame_ind_callback callback); + +QDF_STATUS sme_update_nss(mac_handle_t mac_handle, uint8_t nss); +void sme_update_user_configured_nss(mac_handle_t mac_handle, uint8_t nss); + +bool sme_is_any_session_in_connected_state(mac_handle_t mac_handle); + +QDF_STATUS sme_pdev_set_pcl(struct policy_mgr_pcl_list *msg); +QDF_STATUS sme_pdev_set_hw_mode(struct policy_mgr_hw_mode msg); +QDF_STATUS sme_nss_update_request(uint32_t vdev_id, + uint8_t new_nss, uint8_t ch_width, + policy_mgr_nss_update_cback cback, + uint8_t next_action, + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id); + +typedef void (*sme_peer_authorized_fp) (uint32_t vdev_id); +QDF_STATUS sme_set_peer_authorized(uint8_t *peer_addr, + sme_peer_authorized_fp auth_fp, + uint32_t vdev_id); +QDF_STATUS sme_soc_set_dual_mac_config(struct policy_mgr_dual_mac_config msg); +QDF_STATUS sme_soc_set_antenna_mode(mac_handle_t mac_handle, + struct sir_antenna_mode_param *msg); + +void sme_setdef_dot11mode(mac_handle_t mac_handle); + +/** + * sme_update_tx_bfee_supp() - sets the Tx Bfee support + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @cfg_val: Tx Bfee config value + * + * Return: 0 on success else err code + */ +int sme_update_tx_bfee_supp(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); + +/** + * sme_update_tx_bfee_nsts() - sets the Tx Bfee nsts + * @mac_handle: MAC handle + * @session_id: SME session id + * @usr_cfg_val: user config value + * @nsts_val: Tx Bfee nsts config value + * + * Return: 0 on success else err code + */ +int sme_update_tx_bfee_nsts(mac_handle_t mac_handle, uint8_t session_id, + uint8_t usr_cfg_val, uint8_t nsts_val); + +void wlan_sap_enable_phy_error_logs(mac_handle_t mac_handle, + uint32_t enable_log); +#ifdef WLAN_FEATURE_DSRC +int sme_ocb_gen_timing_advert_frame(mac_handle_t mac_handle, + tSirMacAddr self_addr, + uint8_t **buf, uint32_t *timestamp_offset, + uint32_t *time_value_offset); + +#else +static inline +int sme_ocb_gen_timing_advert_frame(mac_handle_t mac_handle, + tSirMacAddr self_addr, uint8_t **buf, + uint32_t *timestamp_offset, + uint32_t *time_value_offset) +{ + return 0; +} + +#endif + +void sme_add_set_thermal_level_callback(mac_handle_t mac_handle, + sme_set_thermal_level_callback callback); + +void sme_update_tgt_services(mac_handle_t mac_handle, + struct wma_tgt_services *cfg); + +bool sme_validate_sap_channel_switch(mac_handle_t mac_handle, + uint32_t sap_ch_freq, eCsrPhyMode sap_phy_mode, + uint8_t cc_switch_mode, + uint8_t session_id); + +bool sme_is_session_id_valid(mac_handle_t mac_handle, uint32_t session_id); + +#ifdef FEATURE_WLAN_TDLS +void sme_get_opclass(mac_handle_t mac_handle, uint8_t channel, + uint8_t bw_offset, uint8_t *opclass); +#else +static inline void +sme_get_opclass(mac_handle_t mac_handle, uint8_t channel, uint8_t bw_offset, + uint8_t *opclass) +{ +} +#endif + +#ifdef FEATURE_LFR_SUBNET_DETECTION +QDF_STATUS sme_gateway_param_update(mac_handle_t mac_handle, + struct gateway_update_req_param *request); +#endif + +void sme_update_fine_time_measurement_capab(mac_handle_t mac_handle, + uint8_t session_id, + uint32_t val); +QDF_STATUS sme_ht40_stop_obss_scan(mac_handle_t mac_handle, uint32_t vdev_id); +QDF_STATUS sme_set_fw_test(struct set_fwtest_params *fw_test); +QDF_STATUS sme_set_tsfcb(mac_handle_t mac_handle, + int (*cb_fn)(void *cb_ctx, struct stsf *ptsf), void *cb_ctx); + +QDF_STATUS sme_reset_tsfcb(mac_handle_t mac_handle); + +#if defined(WLAN_FEATURE_TSF) && !defined(WLAN_FEATURE_TSF_PLUS_NOIRQ) +QDF_STATUS sme_set_tsf_gpio(mac_handle_t mac_handle, uint32_t pinvalue); +#endif + +QDF_STATUS sme_update_mimo_power_save(mac_handle_t mac_handle, + uint8_t is_ht_smps_enabled, + uint8_t ht_smps_mode, + bool send_smps_action); +#ifdef WLAN_BCN_RECV_FEATURE +/** + * sme_handle_bcn_recv_start() - Enable fw to start sending + * beacons of the current connected AP + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: SME session id + * @nth_value: Beacon report period + * @do_not_resume: beacon reporting resume after a pause is completed + * + * This function remove beacon filter. It allow fw to send + * all beacons from connected peer to driver. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_handle_bcn_recv_start(mac_handle_t mac_handle, + uint32_t vdev_id, + uint32_t nth_value, + bool do_not_resume); + +/** + * sme_is_beacon_report_started() - Check bcn recv started + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * + * This function is to check beacon report started or not. + * + * Return: true on success + */ +bool sme_is_beacon_report_started(mac_handle_t mac_handle, + uint32_t session_id); + +/** + * sme_is_beacon_reporting_do_not_resume() - Check auto resume allowed or not + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * + * This function is to check auto resume of beacon reporting is allowed or not. + * + * Return: true on success + */ +bool sme_is_beacon_reporting_do_not_resume(mac_handle_t mac_handle, + uint32_t session_id); + +/** + * stop_beacon_report() - To stop beacon report + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * + * Return: None + */ +void sme_stop_beacon_report(mac_handle_t mac_handle, + uint32_t session_id); + +#else +static inline +bool sme_is_beacon_report_started(mac_handle_t mac_handle, + uint32_t session_id) +{ + return true; +} + +static inline +bool sme_is_beacon_reporting_do_not_resume(mac_handle_t mac_handle, + uint32_t session_id) +{ + return false; +} + +static inline +void sme_stop_beacon_report(mac_handle_t mac_handle, + uint32_t session_id) +{ +} + +#endif + +QDF_STATUS sme_add_beacon_filter(mac_handle_t mac_handle, + uint32_t session_id, uint32_t *ie_map); +QDF_STATUS sme_remove_beacon_filter(mac_handle_t mac_handle, + uint32_t session_id); + +#ifdef FEATURE_WLAN_APF +/** + * sme_get_apf_capabilities() - Get APF capabilities + * @mac_handle: Opaque handle to the global MAC context + * @callback: Callback function to be called with the result + * @context: Opaque context to be used by the caller to associate the + * request with the response + * + * This function constructs the cds message and fill in message type, + * post the same to WDA. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_get_apf_capabilities(mac_handle_t mac_handle, + apf_get_offload_cb callback, + void *context); + +/** + * sme_set_apf_instructions() - Set APF apf filter instructions. + * @mac_handle: Opaque handle to the global MAC context + * @apf_set_offload: struct to set apf filter instructions. + * + * APFv2 (Legacy APF) API to set the APF packet filter. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_set_apf_instructions(mac_handle_t mac_handle, + struct sir_apf_set_offload + *apf_set_offload); + +/** + * sme_set_apf_enable_disable - Send apf enable/disable cmd + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev id + * @apf_enable: true: Enable APF Int., false: Disable APF Int. + * + * API to either enable or disable the APF interpreter. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_set_apf_enable_disable(mac_handle_t mac_handle, uint8_t vdev_id, + bool apf_enable); + +/** + * sme_apf_write_work_memory - Write into the apf work memory + * @mac_handle: Opaque handle to the global MAC context + * @write_params: APF parameters for the write operation + * + * API for writing into the APF work memory. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_apf_write_work_memory(mac_handle_t mac_handle, + struct wmi_apf_write_memory_params + *write_params); + +/** + * sme_apf_read_work_memory - Read part of apf work memory + * @mac_handle: Opaque handle to the global MAC context + * @read_params: APF parameters for the get operation + * @callback: callback to handle the the read response + * + * API for issuing a APF read memory request. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS +sme_apf_read_work_memory(mac_handle_t mac_handle, + struct wmi_apf_read_memory_params *read_params, + apf_read_mem_cb callback); + +#endif /* FEATURE_WLAN_APF */ + +uint32_t sme_get_wni_dot11_mode(mac_handle_t mac_handle); +QDF_STATUS sme_create_mon_session(mac_handle_t mac_handle, uint8_t *bssid, + uint8_t vdev_id); + +/** + * sme_delete_mon_session() - post message to delete PE session for mon_mode + * operation + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: sme session id + * + * Return: QDF_STATUS_SUCCESS on success, non-zero error code on failure. + */ +QDF_STATUS sme_delete_mon_session(mac_handle_t mac_handle, uint8_t vdev_id); + +/** + * sme_set_vdev_ies_per_band() - sends the per band IEs to vdev + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev_id for which IE is targeted + * @device_mode: vdev mode + * + * Return: None + */ +void sme_set_vdev_ies_per_band(mac_handle_t mac_handle, uint8_t vdev_id, + enum QDF_OPMODE device_mode); + +void sme_set_pdev_ht_vht_ies(mac_handle_t mac_handle, bool enable2x2); + +/** + * sme_update_vdev_type_nss() - sets the nss per vdev type + * @mac_handle: Opaque handle to the global MAC context + * @max_supp_nss: max_supported Nss + * @band: 5G or 2.4G band + * + * Sets the per band Nss for each vdev type based on INI and configured + * chain mask value. + * + * Return: None + */ +void sme_update_vdev_type_nss(mac_handle_t mac_handle, uint8_t max_supp_nss, + enum nss_chains_band_info band); + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +void sme_register_p2p_lo_event(mac_handle_t mac_handle, void *context, + p2p_lo_callback callback); +#else +static inline void sme_register_p2p_lo_event(mac_handle_t mac_handle, + void *context, + p2p_lo_callback callback) +{ +} +#endif + +QDF_STATUS sme_remove_bssid_from_scan_list(mac_handle_t mac_handle, + tSirMacAddr bssid); + +QDF_STATUS sme_process_mac_pwr_dbg_cmd(mac_handle_t mac_handle, + uint32_t session_id, + struct sir_mac_pwr_dbg_cmd* + dbg_args); + +void sme_get_vdev_type_nss(enum QDF_OPMODE dev_mode, + uint8_t *nss_2g, uint8_t *nss_5g); +QDF_STATUS sme_roam_set_default_key_index(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t default_idx); +void sme_send_disassoc_req_frame(mac_handle_t mac_handle, + uint8_t session_id, uint8_t *peer_mac, + uint16_t reason, uint8_t wait_for_ack); +QDF_STATUS sme_update_access_policy_vendor_ie(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t *vendor_ie, + int access_policy); + +/** + * sme_set_peer_param() - set peer param + * @vdev_id: vdev ID + * @peer_addr: peer MAC address + * @param_id: param ID to be updated + * @param_Value: paraam value + * + * This SME API is used to send the peer param to WMA to be sent to FW. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_peer_param(uint8_t *peer_addr, uint32_t param_id, + uint32_t param_value, uint32_t vdev_id); + +QDF_STATUS sme_update_sta_roam_policy(mac_handle_t mac_handle, + enum sta_roam_policy_dfs_mode dfs_mode, + bool skip_unsafe_channels, + uint8_t session_id, uint8_t sap_operating_band); +QDF_STATUS sme_enable_disable_chanavoidind_event(mac_handle_t mac_handle, + uint8_t set_value); +QDF_STATUS sme_set_default_scan_ie(mac_handle_t mac_handle, uint16_t session_id, + uint8_t *ie_data, uint16_t ie_len); +/** + * sme_update_session_param() - API to update PE session param + * @mac_handle: Opaque handle to the global MAC context + * @session_id: Session ID + * @param_type: Param type to be updated + * @param_val: Param value to be update + * + * Note: this setting will not persist over reboots. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_update_session_param(mac_handle_t mac_handle, uint8_t session_id, + uint32_t param_type, uint32_t param_val); +#ifdef WLAN_FEATURE_FIPS +/** + * sme_fips_request() - Perform a FIPS certification operation + * @mac_handle: Opaque handle to the global MAC context + * @param: The FIPS certification parameters + * @callback: Callback function to invoke with the results + * @context: Opaque context to pass back to caller in the callback + * + * Return: QDF_STATUS_SUCCESS if the request is successfully sent + * to firmware for processing, otherwise an error status. + */ +QDF_STATUS sme_fips_request(mac_handle_t mac_handle, struct fips_params *param, + wma_fips_cb callback, void *context); +#else +static inline +QDF_STATUS sme_fips_request(mac_handle_t mac_handle, struct fips_params *param, + wma_fips_cb callback, void *context) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif /* WLAN_FEATURE_FIPS */ + +/** + * sme_set_cts2self_for_p2p_go() - sme function to set ini parms to FW. + * @mac_handle: Opaque handle to the global MAC context + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_cts2self_for_p2p_go(mac_handle_t mac_handle); + +QDF_STATUS sme_update_tx_fail_cnt_threshold(mac_handle_t mac_handle, + uint8_t session_id, uint32_t tx_fail_count); + +/** + * sme_roam_is_ese_assoc() - Check if association type is ESE + * @roam_info: Pointer to roam info + * + * Return: true if ESE Association, false otherwise. + */ +#ifdef FEATURE_WLAN_ESE +bool sme_roam_is_ese_assoc(struct csr_roam_info *roam_info); +#else +static inline bool sme_roam_is_ese_assoc(struct csr_roam_info *roam_info) +{ + return false; +} +#endif +/** + * sme_neighbor_roam_is11r_assoc() - Check if association type is 11R + * @mac_handle: MAC_HANDLE handle + * @session_id: session id + * + * Return: true if 11r Association, false otherwise. + */ +bool sme_neighbor_roam_is11r_assoc(mac_handle_t mac_handle, uint8_t session_id); + +/** + * sme_update_sta_inactivity_timeout(): Update sta_inactivity_timeout to FW + * @mac_handle: Handle returned by mac_open + * @sta_inactivity_timer: struct for sta inactivity timer + * + * If a station does not send anything in sta_inactivity_timeout seconds, an + * empty data frame is sent to it in order to verify whether it is + * still in range. If this frame is not ACKed, the station will be + * disassociated and then deauthenticated. + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure. +*/ +QDF_STATUS sme_update_sta_inactivity_timeout(mac_handle_t mac_handle, + struct sme_sta_inactivity_timeout *sta_inactivity_timer); + +/** + * sme_set_lost_link_info_cb() - plug in callback function for receiving + * @mac_handle: Opaque handle to the MAC context + * @cb: callback function + * + * Return: HAL status + */ +QDF_STATUS sme_set_lost_link_info_cb(mac_handle_t mac_handle, + lost_link_info_cb cb); + +/** + * sme_update_new_channel_event() - update new channel event for sapFsm + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure. + */ +QDF_STATUS sme_update_new_channel_event(mac_handle_t mac_handle, + uint8_t session_id); +#ifdef WLAN_POWER_DEBUG +/** + * sme_reset_power_debug_stats_cb() - SME API to reset Power debug stats cb + * @mac_handle: Opaque handle to the global MAC context + * + * Resets the power stats callback and context to NULL + * + * Return: None + */ +void sme_reset_power_debug_stats_cb(mac_handle_t mac_handle); + +/** + * sme_power_debug_stats_req() - SME API to collect Power debug stats + * @mac_handle: Opaque handle to the global MAC context + * @callback_fn: Pointer to the callback function for Power stats event + * @power_stats_context: Pointer to context + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_power_debug_stats_req( + mac_handle_t mac_handle, + void (*callback_fn)(struct power_stats_response *response, + void *context), + void *power_stats_context); +#endif + +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS +/** + * sme_beacon_debug_stats_req() - SME API to collect beacon debug stats + * @vdev_id: Vdev id on which stats is being requested + * @callback_fn: Pointer to the callback function for beacon stats event + * @beacon_stats_context: Pointer to context + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_beacon_debug_stats_req( + mac_handle_t mac_handle, uint32_t vdev_id, + void (*callback_fn)(struct bcn_reception_stats_rsp + *response, void *context), + void *beacon_stats_context); +#endif + +/** + * sme_get_sar_power_limits() - get SAR limits + * @mac_handle: Opaque handle to the global MAC context + * @callback: Callback function to invoke with the results + * @context: Opaque context to pass back to caller in the callback + * + * Return: QDF_STATUS_SUCCESS if the request is successfully sent + * to firmware for processing, otherwise an error status. + */ +QDF_STATUS sme_get_sar_power_limits(mac_handle_t mac_handle, + wma_sar_cb callback, void *context); + +/** + * sme_set_sar_power_limits() - set sar limits + * @mac_handle: Opaque handle to the global MAC context + * @sar_limit_cmd: struct to send sar limit cmd. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_set_sar_power_limits(mac_handle_t mac_handle, + struct sar_limit_cmd_params *sar_limit_cmd); + +/** + * sme_send_coex_config_cmd() - Send COEX config params + * @coex_cfg_params: struct to coex config params + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_send_coex_config_cmd(struct coex_config_params *coex_cfg_params); + +void sme_set_cc_src(mac_handle_t mac_handle, enum country_src); + + +#ifdef WLAN_FEATURE_WOW_PULSE +QDF_STATUS sme_set_wow_pulse(struct wow_pulse_mode *wow_pulse_set_info); +#endif + +/* ARP DEBUG STATS */ +QDF_STATUS sme_set_nud_debug_stats(mac_handle_t mac_handle, + struct set_arp_stats_params + *set_stats_param); +QDF_STATUS sme_get_nud_debug_stats(mac_handle_t mac_handle, + struct get_arp_stats_params + *get_stats_param); +QDF_STATUS sme_set_nud_debug_stats_cb(mac_handle_t mac_handle, + void (*cb)(void *, struct rsp_stats *, void *context), + void *context); + +/** + * sme_set_del_peers_ind_callback() - Register del peers ind callback + * @mac_handle - MAC global handle + * @callback_routine - callback routine from HDD + * + * This API is invoked by HDD to register its callback to mac + * + * Return: QDF_STATUS + */ +void +sme_set_del_peers_ind_callback(mac_handle_t mac_handle, + void (*callback)(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id)); + +/** + * sme_set_chan_info_callback() - Register chan info callback + * @mac_handle - MAC global handle + * @callback_routine - callback routine from HDD + * + * This API is invoked by HDD to register its callback to mac + * + * Return: QDF_STATUS + */ +void sme_set_chan_info_callback(mac_handle_t mac_handle, + void (*callback)(struct scan_chan_info *chan_info)); + +/** + * sme_get_rssi_snr_by_bssid() - gets the rssi and snr by bssid from scan cache + * @mac_handle: handle returned by mac_open + * @profile: current connected profile + * @bssid: bssid to look for in scan cache + * @rssi: rssi value found + * @snr: snr value found + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_rssi_snr_by_bssid(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const uint8_t *bssid, int8_t *rssi, + int8_t *snr); + +/** + * sme_get_beacon_frm() - gets the bss descriptor from scan cache and prepares + * beacon frame + * @mac_handle: handle returned by mac_open + * @profile: current connected profile + * @bssid: bssid to look for in scan cache + * @frame_buf: frame buffer to populate + * @frame_len: length of constructed frame + * @ch_freq: Pointer to channel freq info to be filled + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_beacon_frm(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const tSirMacAddr bssid, + uint8_t **frame_buf, uint32_t *frame_len, + uint32_t *ch_freq); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * sme_fast_reassoc() - invokes FAST REASSOC command + * @mac_handle: handle returned by mac_open + * @profile: current connected profile + * @bssid: bssid to look for in scan cache + * @ch_freq: channel on which reassoc should be send + * @vdev_id: vdev id + * @connected_bssid: bssid of currently connected profile + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_fast_reassoc(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const tSirMacAddr bssid, uint32_t ch_freq, + uint8_t vdev_id, const tSirMacAddr connected_bssid); + +/** + * sme_roam_invoke_nud_fail() - invokes REASSOC command to best available bssid + * @mac_handle: handle returned by mac_open + * @vdev_id: vdev id + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_roam_invoke_nud_fail(mac_handle_t mac_handle, uint8_t vdev_id); + +#else +static inline +QDF_STATUS sme_fast_reassoc(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const tSirMacAddr bssid, uint32_t ch_freq, + uint8_t vdev_id, const tSirMacAddr connected_bssid) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS sme_roam_invoke_nud_fail(mac_handle_t mac_handle, uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +#endif + +/** + * sme_register_tx_queue_cb(): Register tx queue callback + * @mac_handle: Opaque handle for MAC context + * @tx_queue_cb: Transmit Queues callback + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_register_tx_queue_cb(mac_handle_t mac_handle, + tx_queue_cb tx_queue_cb); + +/** + * sme_deregister_tx_queue_cb() - Deregister the tx queue callback + * @mac_handle: Opaque handle for MAC context + * + * Return: QDF status + */ +QDF_STATUS sme_deregister_tx_queue_cb(mac_handle_t mac_handle); + +/** + * sme_rso_cmd_status_cb() - Set RSO cmd status callback + * @mac_handle: Opaque handle for the MAC context + * @cb: HDD Callback to rso command status read + * + * This function is used to save HDD RSO Command status callback in MAC + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_rso_cmd_status_cb(mac_handle_t mac_handle, + rso_cmd_status_cb cb); + +/** + * sme_register_set_connection_info_cb() - Register connection + * info callback + * @mac_handle - MAC global handle + * @set_connection_info_cb - callback routine from HDD to set + * connection info flag + * @get_connection_info_cb - callback routine from HDD to get + * connection info + * + * This API is invoked by HDD to register its callback to mac + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_register_set_connection_info_cb(mac_handle_t mac_handle, + bool (*set_connection_info_cb)(bool), + bool (*get_connection_info_cb)(uint8_t *session_id, + enum scan_reject_states *reason)); + +/** + * sme_set_dbs_scan_selection_config() - Update DBS scan selection + * configuration + * @mac_handle: The handle returned by macOpen + * @params: wmi_dbs_scan_sel_params config + * + * Return: QDF_STATUS if DBS scan selection update + * configuration success else failure status + */ +QDF_STATUS sme_set_dbs_scan_selection_config(mac_handle_t mac_handle, + struct wmi_dbs_scan_sel_params *params); + +/** + * sme_store_pdev() - store pdev + * @mac_handle - MAC global handle + * @pdev - pdev ptr + * + * Return: QDF_STATUS + */ +void sme_store_pdev(mac_handle_t mac_handle, struct wlan_objmgr_pdev *pdev); + +/** + * sme_set_reorder_timeout() - set reorder timeout value + * including Voice,Video,Besteffort,Background parameters + * @mac_handle: Opaque handle to the global MAC context + * @reg: struct sir_set_rx_reorder_timeout_val + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure. + */ +QDF_STATUS sme_set_reorder_timeout(mac_handle_t mac_handle, + struct sir_set_rx_reorder_timeout_val *req); + +/** + * sme_set_rx_set_blocksize() - set blocksize value + * including mac_addr and win_limit parameters + * @mac_handle: Opaque handle to the global MAC context + * @reg: struct sir_peer_set_rx_blocksize + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure. + */ + +QDF_STATUS sme_set_rx_set_blocksize(mac_handle_t mac_handle, + struct sir_peer_set_rx_blocksize *req); + +/** + * sme_get_rcpi() - gets the rcpi value for peer mac addr + * @mac_handle: handle returned by mac_open + * @rcpi: rcpi request containing peer mac addr, callback and related info + * + * This function posts the rcpi measurement request message to wma queue + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_rcpi(mac_handle_t mac_handle, struct sme_rcpi_req *rcpi); + +/** + * sme_set_chip_pwr_save_fail_cb() - set chip power save failure callback + * @mac_handle: opaque handle to the MAC context + * @cb: callback function pointer + * + * This function stores the chip power save failure callback function. + * + * Return: QDF_STATUS enumeration. + */ + +QDF_STATUS sme_set_chip_pwr_save_fail_cb(mac_handle_t mac_handle, + pwr_save_fail_cb cb); +/** + * sme_cli_set_command() - SME wrapper API over WMA "set" command + * processor cmd + * @vdev_id: virtual device for the command + * @param_id: parameter id + * @sval: parameter value + * @vpdev: parameter category + * + * Command handler for set operations + * + * Return: 0 on success, errno on failure + */ +int sme_cli_set_command(int vdev_id, int param_id, int sval, int vpdev); + +/** + * sme_set_bt_activity_info_cb - set the callback handler for bt events + * @mac_handle: handle returned by mac_open + * @cb: callback handler + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_bt_activity_info_cb(mac_handle_t mac_handle, + bt_activity_info_cb cb); + +/** + * sme_set_enable_mem_deep_sleep - set the mem deep sleep config to FW + * @mac_handle: handle returned by mac_open + * @vdev_id: vdev id + * + * Return: 0 for success else failure code + */ +int sme_set_enable_mem_deep_sleep(mac_handle_t mac_handle, int vdev_id); + +/** + * sme_set_cck_tx_fir_override - set the CCK TX FIR Override to FW + * @mac_handle: handle returned by mac_open + * @vdev_id: vdev id + * + * Return: 0 for success else failure code + */ +int sme_set_cck_tx_fir_override(mac_handle_t mac_handle, int vdev_id); + +/** + * sme_set_smps_cfg() - set SMPS config params + * @vdev_id: virtual device for the command + * @param_id: parameter id + * @param_val: parameter value + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ + +QDF_STATUS sme_set_smps_cfg(uint32_t vdev_id, uint32_t param_id, + uint32_t param_val); + +/** + * sme_get_peer_info_ext() - sme api to get peer ext info + * @mac_handle: Opaque handle to the global MAC context + * @req: peer ext info request struct send to wma + * @context: context of callback function + * @callbackfn: hdd callback function when receive response + * + * This function will send WMA_GET_PEER_INFO_EXT to WMA + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_get_peer_info_ext(mac_handle_t mac_handle, + struct sir_peer_info_ext_req *req, + void *context, + void (*callbackfn)(struct sir_peer_info_ext_resp *param, + void *pcontext)); + +/** + * sme_get_chain_rssi() - Get chain rssi + * @mac_handle: Opaque handle to the global MAC context + * @input: get chain rssi req params + * @callback: Callback function to be called with the result + * @context: Opaque context to be used by the caller to associate the + * request with the response + * + * This function constructs the cds message and fill in message type, + * post the same to WDA. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_get_chain_rssi(mac_handle_t mac_handle, + struct get_chain_rssi_req_params *input, + get_chain_rssi_callback callback, + void *context); + +/** + * sme_get_isolation() - sme api to get antenna isolation + * @mac_handle: hal handle for getting global mac struct + * @context: context of callback function + * @callbackfn: hdd callback function when receive response + * + * This function will send WMA_GET_ISOLATION to WMA + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_get_isolation(mac_handle_t mac_handle, + void *context, + sme_get_isolation_cb callbackfn); + +#ifdef FEATURE_FW_STATE +/** + * sme_get_fw_state() - Get fw state + * @mac_handle: Opaque handle to the global MAC context + * @callback: Callback function to be called with the result + * @context: Opaque context to be used by the caller to associate the + * request with the response + * + * This function constructs the cds message and fill in message type, + * post the same to WDA. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_get_fw_state(mac_handle_t mac_handle, + fw_state_callback callback, + void *context); +#endif /* FEATURE_FW_STATE */ + +/** + * sme_get_valid_channels() - sme api to get valid channels for + * current regulatory domain + * @ch_freq_list: list of the valid channel frequencies + * @list_len: length of the channel list + * + * This function will get valid channels for current regulatory + * domain + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_get_valid_channels(uint32_t *ch_freq_list, uint32_t *list_len); + +/** + * sme_get_mac_context() - sme api to get the pmac context + * + * This function will return the pmac context + * + * Return: pointer to pmac context + */ +struct mac_context *sme_get_mac_context(void); + +/** + * sme_display_disconnect_stats() - Display per session Disconnect stats + * @mac_handle: Opaque handle to the global MAC context + * session_id: SME session id + * + * Return: None + */ +void sme_display_disconnect_stats(mac_handle_t mac_handle, uint8_t session_id); + +/** + * sme_process_msg_callback() - process callback message from LIM + * @mac: global mac context + * @msg: scheduler message + * + * This function process the callback messages from LIM. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_process_msg_callback(struct mac_context *mac, + struct scheduler_msg *msg); + +/** + * sme_set_bmiss_bcnt() - set bmiss config parameters + * @vdev_id: virtual device for the command + * @first_cnt: bmiss first value + * @final_cnt: bmiss final value + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_set_bmiss_bcnt(uint32_t vdev_id, uint32_t first_cnt, + uint32_t final_cnt); + +/** + * sme_send_limit_off_channel_params() - send limit off channel parameters + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev id + * @is_tos_active: tos active or inactive + * @max_off_chan_time: max off channel time + * @rest_time: rest time + * @skip_dfs_chan: skip dfs channel + * + * This function sends command to WMA for setting limit off channel command + * parameters. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_send_limit_off_channel_params(mac_handle_t mac_handle, + uint8_t vdev_id, + bool is_tos_active, + uint32_t max_off_chan_time, + uint32_t rest_time, + bool skip_dfs_chan); + +#ifdef FEATURE_WLAN_DYNAMIC_CVM +/** + * sme_set_vc_mode_config() - Set voltage corner config to FW. + * @bitmap: Bitmap that refers to voltage corner config with + * different phymode and bw configuration + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_vc_mode_config(uint32_t vc_bitmap); +#endif + +/** + * sme_set_del_pmkid_cache() - API to update PMKID cache + * @psoc: psoc common object + * @session_id: Session id + * @pmk_cache_info: Pointer to PMK cache info + * @is_add: boolean that implies whether to add or delete PMKID entry + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_del_pmkid_cache(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + tPmkidCacheInfo *pmk_cache_info, + bool is_add); + +/** + * sme_clear_sae_single_pmk_info() - Clear sae_single_pmk onfo + * @psoc: Psoc object + * @session_id: session id + * @pmk_cache_info: pmk cache info + * + * This function will clear sae_single_pmk info while processing delete pmk + * command from userspace. + * + * Return: None + */ +void sme_clear_sae_single_pmk_info(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + tPmkidCacheInfo *pmk_cache_info); + +/** + * sme_send_hlp_ie_info() - API to send HLP IE info to fw + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev id + * @profile: CSR Roam profile + * @if_addr: IP address + * + * This API is used to send HLP IE info along with IP address + * to fw if LFR3 is enabled. + * + * Return: None + */ +void sme_send_hlp_ie_info(mac_handle_t mac_handle, uint8_t vdev_id, + struct csr_roam_profile *profile, uint32_t if_addr); + +/** + * sme_update_session_assoc_ie() - Updates the assoc IEs to csr_roam_session + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev id + * @src_profile: Pointer to Roam profile in HDD + * + * This API is used to copy the assoc IE sent from user space to + * csr_roam_session + * + * Return: None + */ +void sme_update_session_assoc_ie(mac_handle_t mac_handle, + uint8_t vdev_id, + struct csr_roam_profile *src_profile); + +/** + * sme_send_rso_connect_params() - Updates the assoc IEs to csr_roam_session + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev id + * @src_profile: CSR Roam profile + * + * When the user space updates the assoc IEs or FILS auth type or FILS ERP info, + * host driver needs to send these updated parameters to firmware via + * RSO update command. + * + * Return: None + */ +QDF_STATUS sme_send_rso_connect_params(mac_handle_t mac_handle, + uint8_t vdev_id, + struct csr_roam_profile *src_profile); + +#if defined(WLAN_FEATURE_FILS_SK) +/** + * sme_update_fils_config - Update FILS config to CSR roam session + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev id + * @src_profile: Source roam profile having latest FILS config + * + * API to update FILS config to roam csr session. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_update_fils_config(mac_handle_t mac_handle, uint8_t vdev_id, + struct csr_roam_profile *src_profile); + +/** + * sme_free_join_rsp_fils_params - free fils params + * @roam_info: roam info + * + * Return: void + */ +void sme_free_join_rsp_fils_params(struct csr_roam_info *roam_info); +#else +static inline +QDF_STATUS sme_update_fils_config(mac_handle_t mac_handle, uint8_t vdev_id, + struct csr_roam_profile *src_profile) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void sme_free_join_rsp_fils_params(struct csr_roam_info *roam_info) +{} + +#endif + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +/** + * sme_set_he_bss_color() - Sets the HE BSS color + * + * @mac_handle: The handle returned by mac_open + * @session_id: session_id of the request + * @bss_color: HE BSS color value to set + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_he_bss_color(mac_handle_t mac_handle, uint8_t session_id, + uint8_t bss_color); +#else +static inline +QDF_STATUS sme_set_he_bss_color(mac_handle_t mac_handle, uint8_t session_id, + uint8_t bss_color) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * sme_is_conn_state_connected() -- check if SME connection state is connected + * @mac_handle: Opaque handle to the global MAC context + * @session_id: current Session Id + * + * This API checks if the current SME connection state is connected for the + * given session id. + * + * Return: True if connected, false if any other state. + */ +bool sme_is_conn_state_connected(mac_handle_t mac_handle, uint8_t session_id); + +/** + * sme_scan_get_result_for_bssid - gets the scan result from scan cache for the + * bssid specified + * @mac_handle: handle returned by mac_open + * @bssid: bssid to get the scan result for + * @res: pointer to tCsrScanResultInfo allocated from caller + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_scan_get_result_for_bssid(mac_handle_t mac_handle, + struct qdf_mac_addr *bssid, + tCsrScanResultInfo *res); + +/** + * sme_get_bss_transition_status() - get bss transition status all cadidates + * @mac_handle: handle returned by mac_open + * @transition_reason : Transition reason + * @bssid: bssid to get BSS transition status + * @info : bss candidate information + * @n_candidates : number of candidates + * @is_bt_in_progress: bt activity indicator + * + * Return: QDF_STATUS_SUCCESS on success otherwise a QDF_STATUS error + */ +QDF_STATUS sme_get_bss_transition_status(mac_handle_t mac_handle, + uint8_t transition_reason, + struct qdf_mac_addr *bssid, + struct bss_candidate_info *info, + uint16_t n_candidates, + bool is_bt_in_progress); + +/** + * sme_unpack_rsn_ie: wrapper to unpack RSN IE and update def RSN params + * if optional fields are not present. + * @mac_handle: handle returned by mac_open + * @buf: rsn ie buffer pointer + * @buf_len: rsn ie buffer length + * @rsn_ie: outframe rsn ie structure + * @append_ie: flag to indicate if the rsn_ie need to be appended from buf + * + * Return: parse status + */ +uint32_t sme_unpack_rsn_ie(mac_handle_t mac_handle, uint8_t *buf, + uint8_t buf_len, tDot11fIERSN *rsn_ie, + bool append_ie); + +/** + * sme_add_qcn_ie: Adds QCN IE data to IE buffer + * @mac_handle: handle returned by mac_open + * @ie_data: ie buffer pointer + * @ie_len: ie length pointer + * + * Return: none + */ +void sme_add_qcn_ie(mac_handle_t mac_handle, uint8_t *ie_data, + uint16_t *ie_len); + +/** + * sme_get_oper_chan_freq - gets the operating channel freq + * @vdev: vdev handle + * + * Return: operating channel frequency + */ +int16_t sme_get_oper_chan_freq(struct wlan_objmgr_vdev *vdev); + +/** + * sme_get_oper_ch_width - gets the operating channel width + * @vdev: vdev handle + * + * Return: operating channel width + */ +enum phy_ch_width sme_get_oper_ch_width(struct wlan_objmgr_vdev *vdev); + +/** + * sme_get_oper_ch_width - gets the secondary channel frequency + * @vdev: vdev handle + * @sec20chan_freq: secondary channel frequency + * + * Return: secondary channel frequency + */ +int sme_get_sec20chan_freq_mhz(struct wlan_objmgr_vdev *vdev, + uint16_t *sec20chan_freq); + +/** + * sme_enable_roaming_on_connected_sta() - Enable roaming on an connected sta + * @mac_handle: handle returned by mac_open + * @vdev_id: vdev id + * + * The function check if any connected STA is present on which roaming is not + * enabled and if present enabled roaming on that STA. + * + * Return: none + */ +void sme_enable_roaming_on_connected_sta(mac_handle_t mac_handle, + uint8_t vdev_id); + +/** + * sme_send_mgmt_tx() - Sends mgmt frame from CSR to LIM + * @mac_handle: The handle returned by mac_open + * @session_id: session id + * @buf: pointer to frame + * @len: frame length + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_send_mgmt_tx(mac_handle_t mac_handle, uint8_t session_id, + const uint8_t *buf, uint32_t len); + +#ifdef WLAN_FEATURE_SAE +/** + * sme_handle_sae_msg() - Sends SAE message received from supplicant + * @mac_handle: The handle returned by mac_open + * @session_id: session id + * @sae_status: status of SAE authentication + * @peer_mac_addr: mac address of the peer to be authenticated + * @pmkid: PMKID derived at the end of SAE authentication + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_handle_sae_msg(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t sae_status, + struct qdf_mac_addr peer_mac_addr, + const uint8_t *pmkid); +#else +static inline +QDF_STATUS sme_handle_sae_msg(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t sae_status, + struct qdf_mac_addr peer_mac_addr, + const uint8_t *pmkid) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * sme_set_ba_buff_size() - sets BA buffer size + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @buff_size: BA buffer size + * + * Return: 0 on success else err code + */ +int sme_set_ba_buff_size(mac_handle_t mac_handle, uint8_t session_id, + uint16_t buff_size); + +/** + * sme_send_addba_req() - send ADDBA request with user config + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @tid: tid val for BA session + * @buff_size: BA buffer size + * + * Return: 0 on success else err code + */ +int sme_send_addba_req(mac_handle_t mac_handle, uint8_t session_id, uint8_t tid, + uint16_t buff_size); + +/** + * sme_set_no_ack_policy() - Sets no ack policy for AC + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @val: no ack policy value + * @ac: access category + * + * Return: 0 on success else err code + */ +int sme_set_no_ack_policy(mac_handle_t mac_handle, uint8_t session_id, + uint8_t val, uint8_t ac); + +/** + * sme_set_auto_rate_he_sgi() - Sets SGI for auto rate + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @cfg_val: SGI configuration value + * + * Return: 0 on success else err code + */ +int sme_set_auto_rate_he_sgi(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); + +/** + * sme_set_auto_rate_ldpc() - Sets LDPC for auto rate + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @ldpc_disable: LDPC configuration value + * + * Return: 0 on success else err code + */ +int sme_set_auto_rate_ldpc(mac_handle_t mac_handle, uint8_t session_id, + uint8_t ldpc_disable); + +/** + * sme_set_auto_rate_he_ltf() - Sets HE LTF for auto rate + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @cfg_val: LTF configuration value + * + * Return: 0 on success else err code + */ +int sme_set_auto_rate_he_ltf(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); + +#ifdef WLAN_FEATURE_11AX +/** + * sme_update_tgt_he_cap() - sets the HE caps to pmac + * @mac_handle: Pointer to MAC handle + * @cfg: Pointer to WMA target CFG + * @he_cap_ini: Pointer to HE CAP configured by INI + * + * Return: None + */ +void sme_update_tgt_he_cap(mac_handle_t mac_handle, + struct wma_tgt_cfg *cfg, + tDot11fIEhe_cap *he_cap_ini); + +/** + * sme_update_he_cap_nss() - sets the nss based on user request + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @nss: no.of spatial streams value + * + * Return: None + */ +void sme_update_he_cap_nss(mac_handle_t mac_handle, uint8_t session_id, + uint8_t nss); + +/** + * sme_update_he_tx_bfee_supp() - sets the HE Tx Bfee support + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @cfg_val: Tx Bfee config value + * + * Return: 0 on success else err code + */ +int sme_update_he_tx_bfee_supp(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); + +/** + * sme_update_he_tx_bfee_nsts() - sets the HE Tx Bfee NSTS + * @mac_handle: MAC handle + * @session_id: SME session id + * @cfg_val: Tx Bfee NSTS value + * + * Return: 0 on success else err code + */ +int sme_update_he_tx_bfee_nsts(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); + +/** + * sme_set_he_tx_bf_cbf_rates() - sets the HE Tx Bfee CBF frame rates to FW + * @session_id: SME session id + * + * Return: None + */ +void sme_set_he_tx_bf_cbf_rates(uint8_t session_id); + +/** + * sme_config_su_ppdu_queue() - Configures SU PPDU queue enable/disable in FW + * @session_id: SME session id + * @enable: Enable/Disable config + * + * Return: None + */ +void sme_config_su_ppdu_queue(uint8_t session_id, bool enable); + +/** + * sme_update_he_mcs() - sets the HE MCS based on user request + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @he_mcs: HE MCS value + * + * Return: 0 on success else err code + */ +int sme_update_he_mcs(mac_handle_t mac_handle, uint8_t session_id, + uint16_t he_mcs); + +/** + * sme_update_he_trigger_frm_mac_pad() - sets the HE MAC padding capability + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @cfg_val: HE MAC padding duration value + * + * Return: 0 on success else err code + */ +int sme_update_he_trigger_frm_mac_pad(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t cfg_val); + +/** + * sme_update_he_om_ctrl_supp() - sets the HE OM control capability + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @cfg_val: HE OM control config + * + * Return: 0 on success else err code + */ +int sme_update_he_om_ctrl_supp(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); + +#define A_CTRL_ID_OMI 0x1 +struct omi_ctrl_tx { + uint32_t omi_in_vht:1; + uint32_t omi_in_he:1; + uint32_t a_ctrl_id:4; + uint32_t rx_nss:3; + uint32_t ch_bw:2; + uint32_t ul_mu_dis:1; + uint32_t tx_nsts:3; + uint32_t er_su_dis:1; + uint32_t dl_mu_mimo_resound:1; + uint32_t ul_mu_data_dis:1; + uint32_t reserved:14; +}; + +int sme_send_he_om_ctrl_bw_update(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); + +int sme_send_he_om_ctrl_nss_update(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); + +void sme_reset_he_om_ctrl(mac_handle_t mac_handle); + +/** + * sme_config_action_tx_in_tb_ppdu() - Sends action frame in TB PPDU cfg to FW + * @mac_handle: Pointer to MAC handle + * @session_id: SME session id + * @cfg_val: configuration setting value + * + * Return: 0 on success else err code + */ +int sme_config_action_tx_in_tb_ppdu(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); + +/** + * sme_send_he_om_ctrl_update() - Send HE OM ctrl Tx cmd to FW + * @mac_handle: Pointer to mac handle + * @session_id: SME session id + * + * Return: 0 on success else err code + */ +int sme_send_he_om_ctrl_update(mac_handle_t mac_handle, uint8_t session_id); + +/** + * sme_set_he_om_ctrl_param() - Update HE OM control params for OMI Tx + * @mac_handle: Pointer to mac handle + * @session_id: SME session id + * @param: HE om control parameter + * @cfg_val: HE OM control parameter config value + * + * Return: 0 on success else err code + */ +int sme_set_he_om_ctrl_param(mac_handle_t mac_handle, uint8_t session_id, + enum qca_wlan_vendor_attr_he_omi_tx param, + uint8_t cfg_val); + +/** + * sme_set_usr_cfg_mu_edca() - sets the user cfg MU EDCA params flag + * @mac_handle: Opaque handle to the global MAC context + * @val: value to be set + * + * Return: none + */ +void sme_set_usr_cfg_mu_edca(mac_handle_t mac_handle, bool val); + +/** + * sme_set_he_mu_edca_def_cfg() - sets the default MU EDCA params values + * @mac_handle: Opaque handle to the global MAC context + * + * Return: none + */ +void sme_set_he_mu_edca_def_cfg(mac_handle_t mac_handle); + +/** + * sme_update_he_htc_he_supp() - Update +HTC-HE support in HE capabilities + * @mac_handle: Pointer to mac handle + * @session_id: SME session id + * @cfg_val: config setting + * + * Return: 0 on success else err code + */ +int sme_update_he_htc_he_supp(mac_handle_t mac_handle, uint8_t session_id, + bool cfg_val); + +/** + * sme_update_mu_edca_params() - updates MU EDCA params values + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * + * Return: 0 on success else err code + */ +int sme_update_mu_edca_params(mac_handle_t mac_handle, uint8_t session_id); + +/** + * sme_update_he_tx_stbc_cap() - Sets the HE Tx STBC capability + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @value: set value + * + * Return: 0 on success else err code + */ +int sme_update_he_tx_stbc_cap(mac_handle_t mac_handle, uint8_t session_id, + int value); + +/** + * sme_update_he_rx_stbc_cap() - Sets the HE Rx STBC capability + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @value: set value + * + * Return: 0 on success else err code + */ +int sme_update_he_rx_stbc_cap(mac_handle_t mac_handle, uint8_t session_id, + int value); + +/** + * sme_update_he_frag_supp() - sets the HE fragmentation support + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @he_frag: HE fragmention support value + * + * Return: 0 on success else err code + */ +int sme_update_he_frag_supp(mac_handle_t mac_handle, uint8_t session_id, + uint16_t he_frag); + +/** + * sme_update_he_ldpc_supp() - sets the HE LDPC support + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @he_ldpc: HE LDPC support value + * + * Return: 0 on success else err code + */ +int sme_update_he_ldpc_supp(mac_handle_t mac_handle, uint8_t session_id, + uint16_t he_ldpc); + +/** + * sme_update_he_twt_req_support() - Sets twt request capability + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME session id + * @value: set value + * + * Return: 0 on success else err code + */ +int sme_update_he_twt_req_support(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val); +#else +static inline void sme_update_tgt_he_cap(mac_handle_t mac_handle, + struct wma_tgt_cfg *cfg, + tDot11fIEhe_cap *he_cap_ini) +{} +static inline void sme_update_he_cap_nss(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t nss) +{} +static inline int sme_update_he_mcs(mac_handle_t mac_handle, uint8_t session_id, + uint16_t he_mcs) +{ + return 0; +} + +static inline void sme_set_he_mu_edca_def_cfg(mac_handle_t mac_handle) +{ +} + +static inline int sme_update_mu_edca_params(mac_handle_t mac_handle, + uint8_t session_id) +{ + return 0; +} + +static inline int sme_update_he_trigger_frm_mac_pad(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t cfg_val) +{ + return 0; +} + +static inline int sme_update_he_om_ctrl_supp(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t cfg_val) +{ + return 0; +} + + +static inline int +sme_set_he_om_ctrl_param(mac_handle_t mac_handle, uint8_t session_id, + enum qca_wlan_vendor_attr_he_omi_tx param, + uint8_t cfg_val) +{ + return 0; +} + +static inline void sme_reset_he_om_ctrl(mac_handle_t mac_handle) +{ +} + +static inline int sme_config_action_tx_in_tb_ppdu(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t cfg_val) +{ + return 0; +} + +static inline int sme_update_he_htc_he_supp(mac_handle_t mac_handle, + uint8_t session_id, + bool cfg_val) +{ + return 0; +} + +static inline int +sme_send_he_om_ctrl_update(mac_handle_t mac_handle, uint8_t session_id) +{ + return 0; +} +static inline void sme_set_usr_cfg_mu_edca(mac_handle_t mac_handle, bool val) +{ +} + +static inline int sme_update_he_tx_stbc_cap(mac_handle_t mac_handle, + uint8_t session_id, + int value) +{ + return 0; +} + +static inline int sme_update_he_rx_stbc_cap(mac_handle_t mac_handle, + uint8_t session_id, + int value) +{ + return 0; +} + +static inline int sme_update_he_frag_supp(mac_handle_t mac_handle, + uint8_t session_id, + uint16_t he_frag) +{ + return 0; +} + +static inline int sme_update_he_ldpc_supp(mac_handle_t mac_handle, + uint8_t session_id, + uint16_t he_ldpc) +{ + return 0; +} + +static inline int sme_update_he_tx_bfee_supp(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t cfg_val) +{ + return 0; +} +static inline int sme_update_he_tx_bfee_nsts(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t cfg_val) +{ + return 0; +} + +static inline void sme_set_he_tx_bf_cbf_rates(uint8_t session_id) +{ +} + +static inline void sme_config_su_ppdu_queue(uint8_t session_id, bool enable) +{ +} + +static inline int sme_update_he_twt_req_support(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t cfg_val) +{ + return 0; +} + +#endif + +/** + * sme_update_session_txq_edca_params() - sets the configured + * internal EDCA params values + * + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * @txq_edca_params: edca parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_update_session_txq_edca_params(mac_handle_t mac_handle, uint8_t session_id, + tSirMacEdcaParamRecord *txq_edca_params); + +/** + * sme_is_sta_key_exchange_in_progress() - checks whether the STA/P2P client + * session has key exchange in progress + * + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * + * Return: true - if key exchange in progress + * false - if not in progress + */ +bool sme_is_sta_key_exchange_in_progress(mac_handle_t mac_handle, + uint8_t session_id); + +/* + * sme_validate_channel_list() - Validate the given channel list + * @mac_handle: Opaque handle to the global MAC context + * @chan_freq_list: Pointer to the channel list + * @num_channels: number of channels present in the chan_list + * + * Validates the given channel list with base channels in mac context + * + * Return: True if all channels in the list are valid, false otherwise + */ +bool sme_validate_channel_list(mac_handle_t mac_handle, + uint32_t *chan_freq_list, + uint8_t num_channels); +/** + * sme_set_amsdu() - set amsdu enable/disable based on user cfg + * @mac_handle: Opaque handle to the global MAC context + * @enable: enable or disable + * + * Return: None + */ +void sme_set_amsdu(mac_handle_t mac_handle, bool enable); + +#ifdef WLAN_FEATURE_11AX +void sme_set_he_testbed_def(mac_handle_t mac_handle, uint8_t vdev_id); +void sme_reset_he_caps(mac_handle_t mac_handle, uint8_t vdev_id); +#else +static inline void sme_set_he_testbed_def(mac_handle_t mac_handle, + uint8_t vdev_id) +{ +} +static inline void sme_reset_he_caps(mac_handle_t mac_handle, uint8_t vdev_id) +{ +} +#endif + +/** + * sme_get_mcs_idx() - gets mcs index + * @raw_rate: raw rate from fw + * @rate_flags: rate flags + * @nss: number of nss + * @dcm: dcm will be calculated from rate + * @guard_interval: guard interval info from rate + * @mcs_rate_flags: mcs rate flag + * + * Return: return mcs index + */ +uint8_t sme_get_mcs_idx(uint16_t raw_rate, enum tx_rate_info rate_flags, + uint8_t *nss, uint8_t *dcm, + enum txrate_gi *guard_interval, + enum tx_rate_info *mcs_rate_flags); + +#ifdef WLAN_SUPPORT_TWT +/** + * sme_register_twt_enable_complete_cb() - TWT enable registrar + * @mac_handle: MAC handle + * @twt_enable_cb: Function callback to handle enable event + * + * Return: QDF Status + */ +QDF_STATUS sme_register_twt_enable_complete_cb(mac_handle_t mac_handle, + twt_enable_cb twt_enable_cb); + +/** + * sme_register_twt_disable_complete_cb - TWT disable registrar + * @mac_handle: MAC handle + * @twt_disable_cb: Function callback to handle disable event + * + * Return: QDF Status + */ +QDF_STATUS sme_register_twt_disable_complete_cb(mac_handle_t mac_handle, + twt_disable_cb twt_disable_cb); + +/** + * sme_deregister_twt_enable_complete_cb() - TWT enable deregistrar + * @mac_handle: Opaque handle to the global MAC context + * + * Return: QDF Status + */ +QDF_STATUS sme_deregister_twt_enable_complete_cb(mac_handle_t mac_handle); + +/** + * sme_deregister_twt_disable_complete_cb - TWT disable deregistrar + * @mac_handle: Opaque handle to the global MAC context + * + * Return: QDF Status + */ +QDF_STATUS sme_deregister_twt_disable_complete_cb(mac_handle_t mac_handle); + +#else +static inline +QDF_STATUS sme_register_twt_enable_complete_cb(mac_handle_t mac_handle, + twt_enable_cb twt_enable_cb) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS sme_register_twt_disable_complete_cb(mac_handle_t mac_handle, + twt_disable_cb twt_disable_cb) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS sme_deregister_twt_enable_complete_cb(mac_handle_t mac_handle) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS sme_deregister_twt_disable_complete_cb(mac_handle_t mac_handle) +{ + return QDF_STATUS_SUCCESS; +} +#endif +/** + * sme_get_sta_cxn_info() - This function populates all the connection + * information which is formed by DUT-STA to AP + * by calling CSR helper API. + * @mac_ctx: pointer to mac context + * @session: pointer to sta session + * @conn_profile: pointer to connected DUTSTA-REFAP profile + * @buf: pointer to char buffer to write all the connection information. + * @buf_size: maximum size of the provided buffer + * + * Returns: QDF_STATUS + */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +QDF_STATUS sme_get_sta_cxn_info(mac_handle_t mac_handle, uint32_t session_id, + char *buf, uint32_t buf_sz); +#else +static inline QDF_STATUS +sme_get_sta_cxn_info(mac_handle_t mac_handle, uint32_t session_id, + char *buf, uint32_t buf_sz) +{ + qdf_scnprintf(buf, buf_sz, + "\nDiag macro disable, ask vendor to enable"); + return QDF_STATUS_SUCCESS; +} +#endif + +#if defined(FEATURE_WLAN_ESE) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * sme_add_key_btk() - Add BTK key + * @mac_handle: MAC handle + * @session_id: SME session identifier + * @key: key material + * @key_len: length of the key + * + * Return: 0 on success and negative value for failure + */ +int sme_add_key_btk(mac_handle_t mac_handle, uint8_t session_id, + const uint8_t *key, const int key_len); + +#else +static inline int sme_add_key_btk(mac_handle_t mac_handle, uint8_t session_id, + const uint8_t *key, const int key_len) +{ + return 0; +} +#endif + +#ifdef FEATURE_WLAN_ESE +/** + * sme_add_key_krk() - Add KRK key + * @mac_handle: MAC handle + * @session_id: SME session identifier + * @key: key material + * @key_len: length of the key + * + * Return: 0 on success and negative value for failure + */ +int sme_add_key_krk(mac_handle_t mac_handle, uint8_t session_id, + const uint8_t *key, const int key_len); + +#else + +static inline int sme_add_key_krk(mac_handle_t mac_handle, uint8_t session_id, + const uint8_t *key, const int key_len) +{ + return 0; +} +#endif + +/** + * sme_get_roam_scan_stats() - Send roam scan stats cmd to wma + * @mac_handle: handle returned by mac_open + * @cb: call-back invoked for roam scan stats response + * @context: context of callback + * @vdev_id: vdev id + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_get_roam_scan_stats(mac_handle_t mac_handle, roam_scan_stats_cb cb, + void *context, uint32_t vdev_id); + +/** + * sme_update_score_config() - Update the Scoring Config from MLME + * @mac_handle: Mac Handle + * @score_config: Pointer to the scoring config structure to be populated + * + * Return: None + */ +void sme_update_score_config(mac_handle_t mac_handle, + struct scoring_config *score_config); + +/** + * sme_enable_fw_module_log_level() - enable fw module log level + * @mac_handle: handle returned by mac_open + * @vdev_id: vdev id + * + * Return: None + */ +void sme_enable_fw_module_log_level(mac_handle_t mac_handle, int vdev_id); + +#ifdef WLAN_FEATURE_MOTION_DETECTION +/** + * sme_motion_det_cfg - motion detection configuration + * @vdev_id: vdev id + * @time_t1: Time T1 for motion detection in msecs + * @time_t2: Time T2 for motion detection in msecs + * @n1: number of packets for coarse detection + * @n2: number of packets for fine detection + * @time_t1_gap: gap between packets in coarse detection in msecs + * @time_t2_gap: gap between packets in fine detection in msecs + * @coarse_k: number of times fine motion detection has to be performed for + * coarse detection + * @fine_k: number of times fine motion detection has to be performed for + * fine detection + * @coarse_q: number of times motion is expected to be detected for success + * case in coarse detection + * @fine_q: number of times motion is expected to be detected for success + * case in fine detection + * @md_coarse_thr_high: higher threshold value (in percent) from host to FW, + * which will be used in coarse detection phase of motion + * detection. This is the threshold for the correlation of + * the old RF local-scattering environment with current RF + * local-scattering environment. Value of 100(%) indicates + * that neither the transceiver nor any nearby objects + * have changed position + * @md_fine_thr_high: higher threshold value (in percent) from host to FW, which + * will be used in fine detection phase of motion detection. + * This is the threshold for correlation between the old and + * current RF environments, as explained above + * @md_coarse_thr_low: lower threshold value (in percent) for immediate + * detection of motion in coarse detection phase. This is + * the threshold for correlation between the old and current + * RF environments, as explained above + * @md_fine_thr_low: lower threshold value (in percent) for immediate detection + * of motion in fine detection phase. This is the threshold + * for correlation between the old and current RF + * environments, as explained above + * @eSME_TDLS_PEER_REMOVE_MAC_ADDR: remove peer mac from connection table + */ + +struct sme_motion_det_cfg { + uint8_t vdev_id; + uint32_t time_t1; + uint32_t time_t2; + uint32_t n1; + uint32_t n2; + uint32_t time_t1_gap; + uint32_t time_t2_gap; + uint32_t coarse_K; + uint32_t fine_K; + uint32_t coarse_Q; + uint32_t fine_Q; + uint8_t md_coarse_thr_high; + uint8_t md_fine_thr_high; + uint8_t md_coarse_thr_low; + uint8_t md_fine_thr_low; +}; + +/** + * sme_motion_det_base_line_cfg - motion detection base line configuration + * @vdev_id : vdev id + * @bl_time_t: time T for baseline (in ms), every bl_time_t, bl_n pkts are sent + * @bl_packet_gap: gap between packets for baseline in msecs + * bl_n: number of packets to be sent during one baseline + * bl_num_meas: number of times the baseline measurement to be done + */ +struct sme_motion_det_base_line_cfg { + uint8_t vdev_id; + uint32_t bl_time_t; + uint32_t bl_packet_gap; + uint32_t bl_n; + uint32_t bl_num_meas; +}; + +/** + * sme_motion_det_en - Start/Stop motion detection + * @vdev_id: vdev_id + * @enable: start = 1, stop =0 + */ +struct sme_motion_det_en { + uint8_t vdev_id; + bool enable; +}; + +/** + * sme_motion_det_base_line_en - Start/Stop motion detection base line + * @vdev_id: vdev_id + * @enable: start = 1, stop =0 + */ +struct sme_motion_det_base_line_en { + uint8_t vdev_id; + bool enable; +}; + +/** + * sme_motion_det_config - Post motion detection configuration msg to scheduler + * @mac_handle: mac handle + * @motion_det_cfg: motion detection configuration + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_motion_det_config(mac_handle_t mac_handle, + struct sme_motion_det_cfg *motion_det_cfg); + +/** + * sme_motion_det_enable - Post motion detection start/stop msg to scheduler + * @mac_handle: mac handle + * @motion_det_en: motion detection start/stop + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_motion_det_enable(mac_handle_t mac_handle, + struct sme_motion_det_en *motion_det_en); + +/** + * sme_motion_det_base_line_config - Post md baselining cfg msg to scheduler + * @mac_handle: mac handle + * @motion_det_base_line_cfg: motion detection baselining configuration + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_motion_det_base_line_config( + mac_handle_t mac_handle, + struct sme_motion_det_base_line_cfg *motion_det_base_line_cfg); + +/** + * sme_motion_det_base_line_enable - Post md baselining enable msg to scheduler + * @mac_handle: mac handle + * @motion_det_base_line_en: motion detection baselining start/stop + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_motion_det_base_line_enable( + mac_handle_t mac_handle, + struct sme_motion_det_base_line_en *motion_det_base_line_en); + +/** + * sme_set_md_host_evt_cb - Register/set motion detection callback + * @mac_handle: mac handle + * @callback_fn: motion detection callback function pointer + * @hdd_ctx: hdd context + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_set_md_host_evt_cb +( + mac_handle_t mac_handle, + QDF_STATUS (*callback_fn)(void *ctx, struct sir_md_evt *event), + void *hdd_ctx +); + +/** + * sme_set_md_bl_evt_cb - Register/set motion detection baseline callback + * @mac_handle: mac handle + * @callback_fn: callback function pointer + * @hdd_ctx: hdd context + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_set_md_bl_evt_cb +( + mac_handle_t mac_handle, + QDF_STATUS (*callback_fn)(void *ctx, struct sir_md_bl_evt *event), + void *hdd_ctx +); +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +#ifdef FW_THERMAL_THROTTLE_SUPPORT +/** + * sme_set_thermal_throttle_cfg() - SME API to set the thermal throttle + * configuration parameters + * @mac_handle: Opaque handle to the global MAC context + * @enable: Enable Throttle + * @dc: duty cycle in msecs + * @dc_off_percent: duty cycle off percentage + * @prio: Disables the transmit queues in fw that have lower priority + * than value defined by prio + * @target_temp: Target temperature + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_thermal_throttle_cfg(mac_handle_t mac_handle, bool enable, + uint32_t dc, uint32_t dc_off_percent, + uint32_t prio, uint32_t target_temp); + +/** + * sme_set_thermal_mgmt() - SME API to set the thermal management params + * @mac_handle: Opaque handle to the global MAC context + * @lower_thresh_deg: Lower threshold value of Temperature + * @higher_thresh_deg: Higher threshold value of Temperature + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_thermal_mgmt(mac_handle_t mac_handle, + uint16_t lower_thresh_deg, + uint16_t higher_thresh_deg); +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + +/** + * sme_update_hidden_ssid_status_cb() - cb fun to update hidden ssid stats + * @mac_handle: mac handler + * @cb: cb of type hidden_ssid_cb + */ +QDF_STATUS sme_update_hidden_ssid_status_cb(mac_handle_t mac_handle, + hidden_ssid_cb cb); + +/** + * sme_update_owe_info() - Update OWE info + * @mac: mac context + * @assoc_ind: assoc ind + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_update_owe_info(struct mac_context *mac, + struct assoc_ind *assoc_ind); + +#ifdef WLAN_MWS_INFO_DEBUGFS +/** + * sme_get_mws_coex_info() - SME API to get the coex information + * @mac_handle: mac handler + * @vdev_id: Vdev_id + * @cmd_id: enum mws_coex_cmdid which information is needed. + * @callback_fn: Callback function + * @context: callback context + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_get_mws_coex_info(mac_handle_t mac_handle, uint32_t vdev_id, + uint32_t cmd_id, void (*callback_fn)(void *coex_info_data, + void *context, + wmi_mws_coex_cmd_id + cmd_id), + void *context); +#endif /* WLAN_MWS_INFO_DEBUGFS */ + +#ifdef WLAN_BCN_RECV_FEATURE +/** + * sme_register_bcn_recv_pause_ind_cb() - Register pause ind cb + * mac_handle: man handler + * cb: callback function to HDD + * + * This function register HDD callback in order to indicate beacon + * receive pause indication to userspace. + * + * return QDF_STATUS of cb registration + */ +QDF_STATUS sme_register_bcn_recv_pause_ind_cb(mac_handle_t mac_handle, + beacon_pause_cb cb); + +#else +static inline +QDF_STATUS sme_register_bcn_recv_pause_ind_cb(mac_handle_t mac_handle, + beacon_pause_cb cb) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * sme_set_disconnect_ies() - set disconnect IEs + * @mac_handle: handle returned by mac_open + * @vdev_id: vdev id + * @ie_data: Disconnect IE data + * @ie_len: Disconnect IE length + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_disconnect_ies(mac_handle_t mac_handle, uint8_t vdev_id, + uint8_t *ie_data, uint16_t ie_len); + +void sme_chan_to_freq_list( + struct wlan_objmgr_pdev *pdev, + uint32_t *freq_list, + const uint8_t *chan_list, + uint32_t chan_list_len); + +/** + * sme_set_roam_triggers() - Send roam trigger bitmap to WMA + * @mac_handle: Opaque handle to the MAC context + * @triggers: Carries pointer of the object containing vdev id and + * roam_trigger_bitmap. + * + * Send the roam trigger bitmap received to WMA/WMI. WMI converts + * the bitmap to firmware compatible bitmap as per reasons + * defined @WMI_ROAM_TRIGGER_REASON_ID + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_roam_triggers(mac_handle_t mac_handle, + struct roam_triggers *triggers); + +/** + * sme_set_roam_config_enable() - Cache roam config status in SME + * @mac_handle: Opaque handle to the MAC context + * @vdev_id: vdev id + * @roam_control_enable: Carries a non-zero value if the current set request is + * for enable, otherwise carries a 0. + * + * Cache control roam config enable/disable status in SME so that the + * userspace can query for the status based on a vdev/session at any time. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_roam_config_enable(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t roam_control_enable); + +/** + * sme_get_roam_config_status() - Get roam config status from SME + * @mac_handle: Opaque handle to the MAC context + * @vdev_id: vdev id + * @config_status: Pointer of a buffer to fill the status + * + * Get the cached control roam config status in SME and copy to status. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_roam_config_status(mac_handle_t mac_handle, uint8_t vdev_id, + uint8_t *config_status); + +/** + * sme_get_full_roam_scan_period_global() - get global full scan refresh period + * @mac_handle: The handle returned by mac_open + * + * Return: Full roam scan period configured through ini + */ +uint16_t sme_get_full_roam_scan_period_global(mac_handle_t mac_handle); + +/** + * sme_get_full_roam_scan_period() - Get full roam scan period + * @mac_handle: Opaque handle to the MAC context + * @vdev_id: vdev id + * @full_roam_scan_period: Pointer of a buffer to fill the full roam scan period + * + * Get the full scan period cached in neighborRoamInfo and fill in the given + * buffer full_roam_scan_period. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_full_roam_scan_period(mac_handle_t mac_handle, + uint8_t vdev_id, + uint32_t *full_roam_scan_period); + +/** + * sme_check_for_duplicate_session() - check for duplicate session + * @mac_handle: Opaque handle to the MAC context + * @peer_addr: Peer device mac address + * + * Check for duplicate mac address is available on other vdev. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_check_for_duplicate_session(mac_handle_t mac_handle, + uint8_t *peer_addr); +#ifdef FEATURE_ANI_LEVEL_REQUEST +/* + * sme_get_ani_level() - + * A wrapper function that client calls to register a callback to get ani level + * + * @mac_handle - pointer to mac handle + * @freqs - frequencies for which ANI level has to be fetched + * @num_freqs - number of frequencies + * @callback - SME sends back the ani level using the callback + * @context - user context to be passed back along with the callback + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_ani_level(mac_handle_t mac_handle, uint32_t *freqs, + uint8_t num_freqs, void (*callback)( + struct wmi_host_ani_level_event *ani, uint8_t num, + void *context), void *context); +#endif /* FEATURE_ANI_LEVEL_REQUEST */ + +/** + * sme_get_prev_connected_bss_ies() - Get the previous connected AP IEs + * @mac_handle: The handle returned by mac_open. + * @vdev_id: vdev id + * @ies: IEs of the disconnected AP. Currently to carry beacon IEs. + * @ie_len: Length of the @ies + * + * This API extracts the IEs from the previous connected AP info and update + * them to the ies and ie_len. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_prev_connected_bss_ies(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t **ies, uint32_t *ie_len); + +#ifdef FEATURE_MONITOR_MODE_SUPPORT +/** + * sme_set_monitor_mode_cb() - Register monitor mode vdev up operation callback + * @mac_handle: Opaque handle to the MAC context + * @monitor_mode_cb: callback to be registered + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_monitor_mode_cb(mac_handle_t mac_handle, + void (*monitor_mode_cb)(uint8_t vdev_id)); + +/* + * sme_process_monitor_mode_vdev_up_evt() - Handle vdev up completion + * @vdev_id: vdev id + * + * Return: QDF_STATUS. + */ +QDF_STATUS sme_process_monitor_mode_vdev_up_evt(uint8_t vdev_id); +#else +static inline +QDF_STATUS sme_set_monitor_mode_cb(mac_handle_t mac_handle, + void (*monitor_mode_cb)(uint8_t vdev_id)) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +sme_process_monitor_mode_vdev_up_evt(uint8_t vdev_id) +{ + return QDF_STATUS_E_FAILURE; +} +#endif + +#endif /* #if !defined( __SME_API_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_ft_api.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_ft_api.h new file mode 100644 index 0000000000000000000000000000000000000000..8a67bea4d9ad20414c5103488db5d00d617fd4d0 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_ft_api.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2013-2016, 2018, 2019-2020 The Linux Foundation. + * All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SME_FTAPI_H) +#define __SME_FTAPI_H + +typedef enum eFTIEState { + eFT_START_READY, /* Start before and after 11r assoc */ + eFT_AUTH_REQ_READY, /* When we have recvd the 1st or nth auth req */ + /* + * Sent auth1 and waiting auth2 We are now ready for FT phase, + * send auth1, recd auth2 + */ + eFT_WAIT_AUTH2, + eFT_AUTH_COMPLETE, + /* Now we have sent Auth Rsp to the supplicant and waiting */ + /* Reassoc Req from the supplicant. */ + eFT_REASSOC_REQ_WAIT, + /* + * We have received the Reassoc request from supplicant. + * Waiting for the keys. + */ + eFT_SET_KEY_WAIT, +} tFTIEStates; + +/* FT neighbor roam callback user context */ +typedef struct sFTRoamCallbackUsrCtx { + struct mac_context *mac; + uint8_t sessionId; +} tFTRoamCallbackUsrCtx, *tpFTRoamCallbackUsrCtx; + +typedef struct sFTSMEContext { + /* Received and processed during pre-auth */ + uint8_t *auth_ft_ies; + uint32_t auth_ft_ies_length; + /* Received and processed during re-assoc */ + uint8_t *reassoc_ft_ies; + uint16_t reassoc_ft_ies_length; + /* Pre-Auth info */ + tFTIEStates FTState; /* The state of FT in the current 11rAssoc */ + tSirMacAddr preAuthbssId; /* BSSID to preauth to */ + uint32_t vdev_id; + /* Saved pFTPreAuthRsp */ + tpSirFTPreAuthRsp psavedFTPreAuthRsp; + bool setFTPreAuthState; + /* Time to trigger reassoc once pre-auth is successful */ + qdf_mc_timer_t preAuthReassocIntvlTimer; + bool addMDIE; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + uint32_t r0kh_id_len; + uint8_t r0kh_id[SIR_ROAM_R0KH_ID_MAX_LEN]; +#endif + /* User context for the timer callback */ + tpFTRoamCallbackUsrCtx pUsrCtx; +} tftSMEContext, *tpftSMEContext; + +/*-------------------------------------------------------------------------- + Prototype functions + ------------------------------------------------------------------------*/ +void sme_ft_open(mac_handle_t mac_handle, uint32_t sessionId); +void sme_ft_close(mac_handle_t mac_handle, uint32_t sessionId); +void sme_ft_reset(mac_handle_t mac_handle, uint32_t sessionId); +void sme_set_ft_ies(mac_handle_t mac_handle, uint32_t sessionId, + const uint8_t *ft_ies, uint16_t ft_ies_length); +QDF_STATUS sme_ft_update_key(mac_handle_t mac_handle, uint32_t sessionId, + tCsrRoamSetKey *pFTKeyInfo); +void sme_get_ft_pre_auth_response(mac_handle_t mac_handle, uint32_t sessionId, + uint8_t *ft_ies, uint32_t ft_ies_ip_len, + uint16_t *ft_ies_length); +void sme_get_rici_es(mac_handle_t mac_handle, uint32_t sessionId, + uint8_t *ric_ies, + uint32_t ric_ies_ip_len, uint32_t *ric_ies_length); +/** + * sme_check_ft_status() - Check for key wait status in FT mode + * @mac_handle: MAC handle + * @session_id: vdev identifier + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_check_ft_status(mac_handle_t mac_handle, uint32_t session_id); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * sme_reset_key() -Reset key information + * @mac_handle: MAC handle + * @vdev_id: vdev identifier + * + * Return: None + */ +void sme_reset_key(mac_handle_t mac_handle, uint32_t vdev_id); +#else +static inline void sme_reset_key(mac_handle_t mac_handle, uint32_t vdev_id) +{ +} +#endif + +void sme_preauth_reassoc_intvl_timer_callback(void *context); +void sme_set_ft_pre_auth_state(mac_handle_t mac_handle, uint32_t sessionId, + bool state); +bool sme_get_ft_pre_auth_state(mac_handle_t mac_handle, uint32_t sessionId); +#endif diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_inside.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_inside.h new file mode 100644 index 0000000000000000000000000000000000000000..d9ffedcf9fc86f0586f142b5110d9f10b822a788 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_inside.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SMEINSIDE_H) +#define __SMEINSIDE_H + +/** + * \file sme_inside.h + * + * \brief prototype for SME structures and APIs used insside SME + */ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "qdf_status.h" +#include "qdf_lock.h" +#include "qdf_trace.h" +#include "qdf_mem.h" +#include "qdf_types.h" +#include "sir_api.h" +#include "csr_internal.h" +#include "sme_qos_api.h" +#include "sme_qos_internal.h" + +#include "sme_rrm_api.h" +#include "wlan_serialization_legacy_api.h" +ePhyChanBondState csr_convert_cb_ini_value_to_phy_cb_state(uint32_t cbIniValue); + +/*-------------------------------------------------------------------------- + Type declarations + ------------------------------------------------------------------------*/ +/* + * In case MAX num of STA are connected to SAP, switching off SAP causes + * two SME cmd to be enqueued for each STA. Keeping SME total cmds as following + * to make sure we have space for these cmds + some additional cmds. + */ +#define SME_TOTAL_COMMAND (HAL_NUM_STA * 3) + +typedef struct sGenericQosCmd { + struct sme_qos_wmmtspecinfo tspecInfo; + enum qca_wlan_ac_type ac; + uint8_t tspec_mask; +} tGenericQosCmd; + +/** + * struct s_nss_update_cmd - Format of nss update request + * @new_nss: new nss value + * @ch_width: new channel width - optional + * @session_id: Session ID + * @set_hw_mode_cb: HDD nss update callback + * @context: Adapter context + * @next_action: Action to be taken after nss update + * @reason: reason for nss update + * @original_vdev_id: original request hwmode change vdev id + */ +struct s_nss_update_cmd { + uint32_t new_nss; + uint32_t ch_width; + uint32_t session_id; + void *nss_update_cb; + void *context; + uint8_t next_action; + enum policy_mgr_conn_update_reason reason; + uint32_t original_vdev_id; +}; + +/** + * struct sir_disconnect_stats_cmd: command structure to get disconnect stats + * @peer_mac_addr: MAC address of the peer disconnected + * + */ +struct sir_disconnect_stats_cmd { + struct qdf_mac_addr peer_mac_addr; +}; + +typedef struct tagSmeCmd { + tListElem Link; + eSmeCommandType command; + uint32_t cmd_id; + uint32_t vdev_id; + union { + struct roam_cmd roamCmd; + struct wmstatus_changecmd wmStatusChangeCmd; + tGenericQosCmd qosCmd; + struct delstafor_sessionCmd delStaSessionCmd; + struct policy_mgr_hw_mode set_hw_mode_cmd; + struct s_nss_update_cmd nss_update_cmd; + struct policy_mgr_dual_mac_config set_dual_mac_cmd; + struct sir_antenna_mode_param set_antenna_mode_cmd; + struct sir_disconnect_stats_cmd disconnect_stats_cmd; + } u; +} tSmeCmd; + +/*-------------------------------------------------------------------------- + Internal to SME + ------------------------------------------------------------------------*/ +/** + * csr_get_cmd_type() - to convert sme command type to serialization cmd type + * @sme_cmd: sme command pointer + * + * This API will convert SME command type to serialization command type which + * new serialization module understands + * + * Return: serialization cmd type based on sme command type + */ +enum wlan_serialization_cmd_type csr_get_cmd_type(tSmeCmd *sme_cmd); +/** + * csr_set_serialization_params_to_cmd() - take sme params and create new + * serialization command + * @mac_ctx: pointer to mac context + * @sme_cmd: sme command pointer + * @cmd: serialization command pointer + * @high_priority: if command is high priority + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_E_FAILURE + */ +QDF_STATUS csr_set_serialization_params_to_cmd(struct mac_context *mac_ctx, + tSmeCmd *sme_cmd, struct wlan_serialization_command *cmd, + uint8_t high_priority); +tSmeCmd *sme_get_command_buffer(struct mac_context *mac); +void sme_release_command(struct mac_context *mac, tSmeCmd *pCmd); +bool qos_process_command(struct mac_context *mac, tSmeCmd *pCommand); +void qos_release_command(struct mac_context *mac, tSmeCmd *pCommand); +QDF_STATUS csr_roam_process_command(struct mac_context *mac, tSmeCmd *pCommand); + +/** + * csr_roam_wm_status_change_complete() - Remove WM status change command + * from SME active command list + * @mac_ctx: global mac context + * @session_id: session id + * + * This API removes WM status change command from SME active command list + * if present. + * + * Return: void + */ +void csr_roam_wm_status_change_complete(struct mac_context *mac_ctx, + uint8_t session_id); +void csr_roam_process_wm_status_change_command(struct mac_context *mac, + tSmeCmd *pCommand); + +/** + * csr_roam_get_disconnect_stats_complete() - Remove get disconnect stats + * command from SME active command list + * @mac_ctx: global mac context + * This API removes get disconnect stats command from SME active command list + * if present. + * + * Return: void + */ +void csr_roam_get_disconnect_stats_complete(struct mac_context *mac_ctx); + +/** + * csr_roam_process_get_disconnect_stats_command() - Process get disconnect + * stats + * @mac_ctx: global mac context + * @pCommand: Command to be processed + * + * Return: void + */ +void csr_roam_process_get_disconnect_stats_command(struct mac_context *mac, + tSmeCmd *cmd); +void csr_reinit_roam_cmd(struct mac_context *mac, tSmeCmd *pCommand); +void csr_reinit_wm_status_change_cmd(struct mac_context *mac, + tSmeCmd *pCommand); +QDF_STATUS csr_is_valid_channel(struct mac_context *mac, uint32_t freq); + +QDF_STATUS sme_acquire_global_lock(struct sme_context *sme); +QDF_STATUS sme_release_global_lock(struct sme_context *sme); + +QDF_STATUS +csr_process_vdev_del_rsp(struct mac_context *mac, uint8_t *pmsg); + +/** + * csr_flush_cfg_bg_scan_roam_channel_list() - Flush the channel list + * @channel_info: Channel list to be flushed + * + * Return: None + */ +void csr_flush_cfg_bg_scan_roam_channel_list(tCsrChannelInfo *channel_info); + +/** + * csr_create_bg_scan_roam_channel_list() - Create roam scan chan list + * @mac: global mac context + * @channel_info: Channel list to be populated for roam scan + * @chan_freq_list: Channel list to be populated from + * @num_chan: Number of channels + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_E_FAILURE + */ +QDF_STATUS csr_create_bg_scan_roam_channel_list(struct mac_context *mac, + tCsrChannelInfo *channel_info, + const uint32_t *chan_freq_list, + const uint8_t num_chan); + +#ifdef FEATURE_WLAN_ESE +QDF_STATUS csr_create_roam_scan_channel_list(struct mac_context *mac, + uint8_t sessionId, + uint32_t *chan_freq_list, + uint8_t numChannels, + const enum band_info band); +#endif + +ePhyChanBondState csr_convert_cb_ini_value_to_phy_cb_state(uint32_t cbIniValue); +void csr_process_set_dual_mac_config(struct mac_context *mac, tSmeCmd *command); +void csr_process_set_antenna_mode(struct mac_context *mac, tSmeCmd *command); +void csr_process_set_hw_mode(struct mac_context *mac, tSmeCmd *command); +void csr_process_nss_update_req(struct mac_context *mac, tSmeCmd *command); +#endif /* #if !defined( __SMEINSIDE_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_internal.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..0411b499449be29c3a34035622abfce3956a8577 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_internal.h @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SMEINTERNAL_H) +#define __SMEINTERNAL_H + +/** + * \file sme_internal.h + * + * \brief prototype for SME internal structures and APIs used for SME and MAC + */ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "qdf_status.h" +#include "qdf_lock.h" +#include "qdf_trace.h" +#include "qdf_mem.h" +#include "qdf_types.h" +#include "host_diag_core_event.h" +#include "csr_link_list.h" +#include "sme_power_save.h" +#include "wmi_unified.h" +#include "wmi_unified_param.h" + +struct wmi_twt_enable_complete_event_param; +/*-------------------------------------------------------------------------- + Type declarations + ------------------------------------------------------------------------*/ + +/* Mask can be only have one bit set */ +typedef enum eSmeCommandType { + eSmeNoCommand = 0, + /* this is not a command, it is to identify this is a CSR command */ + eSmeCsrCommandMask = 0x10000, + eSmeCommandRoam, + eSmeCommandWmStatusChange, + eSmeCommandGetdisconnectStats, + /* QOS */ + eSmeQosCommandMask = 0x40000, /* To identify Qos commands */ + eSmeCommandAddTs, + eSmeCommandDelTs, + e_sme_command_set_hw_mode, + e_sme_command_nss_update, + e_sme_command_set_dual_mac_config, + e_sme_command_set_antenna_mode, +} eSmeCommandType; + +typedef enum eSmeState { + SME_STATE_STOP, + SME_STATE_START, + SME_STATE_READY, +} eSmeState; + +#define SME_IS_START(mac) (SME_STATE_STOP != (mac)->sme.state) +#define SME_IS_READY(mac) (SME_STATE_READY == (mac)->sme.state) + +/* HDD Callback function */ +typedef void (*ibss_peer_info_cb)(void *cb_context, + tSirPeerInfoRspParams *infoParam); + +/* Peer info */ +struct ibss_peer_info_cb_info { + void *peer_info_cb_context; + ibss_peer_info_cb peer_info_cb; +}; + +/** + * struct stats_ext_event - stats_ext_event payload + * @vdev_id: ID of the vdev for the stats + * @event_data_len: length of the @event_data + * @event_data: actual ext stats + */ +struct stats_ext_event { + uint32_t vdev_id; + uint32_t event_data_len; + uint8_t event_data[]; +}; + +/** + * typedef stats_ext_cb - stats ext callback + * @hdd_handle: Opaque handle to the HDD context + * @data: stats ext payload from firmware + */ +typedef void (*stats_ext_cb)(hdd_handle_t hdd_handle, + struct stats_ext_event *data); + +/** + * typedef stats_ext2_cb - stats ext2 callback + * @hdd_handle: Opaque handle to the HDD context + * @data: stats ext2 payload from firmware + */ +typedef void (*stats_ext2_cb)(hdd_handle_t hdd_handle, + struct sir_sme_rx_aggr_hole_ind *data); + +#define MAX_ACTIVE_CMD_STATS 16 + +typedef struct sActiveCmdStats { + eSmeCommandType command; + uint32_t reason; + uint32_t sessionId; + uint64_t timestamp; +} tActiveCmdStats; + +typedef struct sSelfRecoveryStats { + tActiveCmdStats activeCmdStats[MAX_ACTIVE_CMD_STATS]; + uint8_t cmdStatsIndx; +} tSelfRecoveryStats; + +typedef void (*link_layer_stats_cb)(hdd_handle_t hdd_handle, + int indication_type, + tSirLLStatsResults *results, + void *cookie); + +typedef void (*ext_scan_ind_cb)(hdd_handle_t hdd_handle, + const uint16_t, void *); + +/** + * typedef sme_link_speed_cb - sme_get_link_speed() callback function + * @info: link speed information + * @context: user context supplied to sme_get_link_speed() + * + * This is the signature of a callback function whose addresses is + * passed as the asynchronous callback function to sme_get_link_speed(). + */ + +typedef void (*sme_link_speed_cb)(struct link_speed_info *info, + void *context); + +typedef void (*ocb_callback)(void *context, void *response); +typedef void (*sme_set_thermal_level_callback)(hdd_handle_t hdd_handle, + u_int8_t level); +typedef void (*p2p_lo_callback)(void *context, + struct sir_p2p_lo_event *event); +#ifdef FEATURE_OEM_DATA_SUPPORT +typedef void (*sme_send_oem_data_rsp_msg)(struct oem_data_rsp *); +#endif + +typedef void (*twt_enable_cb)(hdd_handle_t hdd_handle, + struct wmi_twt_enable_complete_event_param *params); +typedef void (*twt_disable_cb)(hdd_handle_t hdd_handle); + +#ifdef FEATURE_WLAN_APF +/** + * typedef apf_get_offload_cb - APF offload callback signature + * @context: Opaque context that the client can use to associate the + * callback with the request + * @caps: APF offload capabilities as reported by firmware + */ +struct sir_apf_get_offload; +typedef void (*apf_get_offload_cb)(void *context, + struct sir_apf_get_offload *caps); + +/** + * typedef apf_read_mem_cb - APF read memory response callback + * @context: Opaque context that the client can use to associate the + * callback with the request + * @evt: APF read memory response event parameters + */ +typedef void (*apf_read_mem_cb)(void *context, + struct wmi_apf_read_memory_resp_event_params + *evt); +#endif /* FEATURE_WLAN_APF */ + +/** + * typedef rssi_threshold_breached_cb - RSSI threshold breach callback + * @hdd_handle: Opaque handle to the HDD context + * @event: The RSSI breach event + */ +typedef void (*rssi_threshold_breached_cb)(hdd_handle_t hdd_handle, + struct rssi_breach_event *event); + +/** + * typedef get_chain_rssi_callback - get chain rssi callback + * @context: Opaque context that the client can use to associate the + * callback with the request + * @data: chain rssi result reported by firmware + */ +struct chain_rssi_result; +typedef void (*get_chain_rssi_callback)(void *context, + struct chain_rssi_result *data); + +#ifdef FEATURE_FW_STATE +/** + * typedef fw_state_callback - get firmware state callback + * @context: Opaque context that the client can use to associate the + * callback with the request + */ +typedef void (*fw_state_callback)(void *context); +#endif /* FEATURE_FW_STATE */ + +typedef void (*tx_queue_cb)(hdd_handle_t hdd_handle, uint32_t vdev_id, + enum netif_action_type action, + enum netif_reason_type reason); + +/** + * typedef pwr_save_fail_cb - power save fail callback function + * @hdd_handle: HDD handle registered with SME + * @params: failure parameters + */ +struct chip_pwr_save_fail_detected_params; +typedef void (*pwr_save_fail_cb)(hdd_handle_t hdd_handle, + struct chip_pwr_save_fail_detected_params *params); + +/** + * typedef bt_activity_info_cb - bluetooth activity callback function + * @hdd_handle: HDD handle registered with SME + * @bt_activity: bluetooth activity information + */ +typedef void (*bt_activity_info_cb)(hdd_handle_t hdd_handle, + uint32_t bt_activity); + +/** + * typedef rso_cmd_status_cb - RSO command status callback function + * @hdd_handle: HDD handle registered with SME + * @rso_status: Status of the operation + */ +typedef void (*rso_cmd_status_cb)(hdd_handle_t hdd_handle, + struct rso_cmd_status *rso_status); + +/** + * typedef lost_link_info_cb - lost link indication callback function + * @hdd_handle: HDD handle registered with SME + * @lost_link_info: Information about the lost link + */ +typedef void (*lost_link_info_cb)(hdd_handle_t hdd_handle, + struct sir_lost_link_info *lost_link_info); +/** + * typedef hidden_ssid_cb - hidden ssid rsp callback fun + * @hdd_handle: HDD handle registered with SME + * @vdev_id: Vdev Id + */ +typedef void (*hidden_ssid_cb)(hdd_handle_t hdd_handle, + uint8_t vdev_id); + +/** + * typedef bcn_report_cb - recv bcn callback fun + * @hdd_handle: HDD handle registered with SME + * @beacon_report: Beacon report structure + */ +typedef QDF_STATUS (*beacon_report_cb) + (hdd_handle_t hdd_handle, struct wlan_beacon_report *beacon_report); + +/** + * beacon_pause_cb : scan start callback fun + * @hdd_handler: HDD handler + * @vdev_id: vdev id + * @type: scan event type + * @is_disconnected: Driver is in dis connected state or not + */ +typedef void (*beacon_pause_cb)(hdd_handle_t hdd_handle, + uint8_t vdev_id, + enum scan_event_type type, + bool is_disconnected); + +/** + * typedef sme_get_isolation_cb - get isolation callback fun + * @param: isolation result reported by firmware + * @pcontext: Opaque context that the client can use to associate the + * callback with the request + */ +typedef void (*sme_get_isolation_cb)(struct sir_isolation_resp *param, + void *pcontext); + +#ifdef WLAN_FEATURE_MOTION_DETECTION +typedef QDF_STATUS (*md_host_evt_cb)(void *hdd_ctx, struct sir_md_evt *event); +typedef QDF_STATUS (*md_bl_evt_cb)(void *hdd_ctx, struct sir_md_bl_evt *event); +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +struct sme_context { + eSmeState state; + qdf_mutex_t sme_global_lock; + uint32_t sme_cmd_count; + /* following pointer contains array of pointers for tSmeCmd* */ + void **sme_cmd_buf_addr; + tDblLinkList sme_cmd_freelist; /* preallocated roam cmd list */ + enum QDF_OPMODE curr_device_mode; + struct ibss_peer_info_cb_info peer_info_cb_info; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + host_event_wlan_status_payload_type eventPayload; +#endif + void *ll_stats_context; + link_layer_stats_cb link_layer_stats_cb; + void (*link_layer_stats_ext_cb)(hdd_handle_t callback_ctx, + tSirLLStatsResults *rsp); +#ifdef WLAN_POWER_DEBUG + void *power_debug_stats_context; + void (*power_stats_resp_callback)(struct power_stats_response *rsp, + void *callback_context); + void (*sme_power_debug_stats_callback)( + struct mac_context *mac, + struct power_stats_response *response); +#endif +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS + void *beacon_stats_context; + void (*beacon_stats_resp_callback)(struct bcn_reception_stats_rsp *rsp, + void *callback_context); +#endif +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + void (*auto_shutdown_cb)(void); +#endif + /* Maximum interfaces allowed by the host */ + uint8_t max_intf_count; + stats_ext_cb stats_ext_cb; + stats_ext2_cb stats_ext2_cb; + /* linkspeed callback */ + sme_link_speed_cb link_speed_cb; + void *link_speed_context; + + /* get extended peer info callback */ + void (*pget_peer_info_ext_ind_cb)(struct sir_peer_info_ext_resp *param, + void *pcontext); + void *pget_peer_info_ext_cb_context; + sme_get_isolation_cb get_isolation_cb; + void *get_isolation_cb_context; +#ifdef FEATURE_WLAN_EXTSCAN + ext_scan_ind_cb ext_scan_ind_cb; +#endif /* FEATURE_WLAN_EXTSCAN */ + csr_link_status_callback link_status_callback; + void *link_status_context; + int (*get_tsf_cb)(void *pcb_cxt, struct stsf *ptsf); + void *get_tsf_cxt; + /* get temperature event context and callback */ + void *temperature_cb_context; + void (*temperature_cb)(int temperature, void *context); + uint8_t miracast_value; + struct ps_global_info ps_global_info; + rssi_threshold_breached_cb rssi_threshold_breached_cb; + sme_set_thermal_level_callback set_thermal_level_cb; + void *apf_get_offload_context; +#ifdef FEATURE_P2P_LISTEN_OFFLOAD + p2p_lo_callback p2p_lo_event_callback; + void *p2p_lo_event_context; +#endif +#ifdef FEATURE_OEM_DATA_SUPPORT + sme_send_oem_data_rsp_msg oem_data_rsp_callback; +#endif + lost_link_info_cb lost_link_info_cb; + + bool (*set_connection_info_cb)(bool); + bool (*get_connection_info_cb)(uint8_t *session_id, + enum scan_reject_states *reason); + rso_cmd_status_cb rso_cmd_status_cb; + pwr_save_fail_cb chip_power_save_fail_cb; + bt_activity_info_cb bt_activity_info_cb; + void *get_arp_stats_context; + void (*get_arp_stats_cb)(void *, struct rsp_stats *, void *); + get_chain_rssi_callback get_chain_rssi_cb; + void *get_chain_rssi_context; +#ifdef FEATURE_FW_STATE + fw_state_callback fw_state_cb; + void *fw_state_context; +#endif /* FEATURE_FW_STATE */ + tx_queue_cb tx_queue_cb; + twt_enable_cb twt_enable_cb; + twt_disable_cb twt_disable_cb; +#ifdef FEATURE_WLAN_APF + apf_get_offload_cb apf_get_offload_cb; + apf_read_mem_cb apf_read_mem_cb; +#endif +#ifdef WLAN_FEATURE_MOTION_DETECTION + md_host_evt_cb md_host_evt_cb; + md_bl_evt_cb md_bl_evt_cb; + void *md_ctx; +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + /* hidden ssid rsp callback */ + hidden_ssid_cb hidden_ssid_cb; +#ifdef WLAN_MWS_INFO_DEBUGFS + void *mws_coex_info_ctx; + void (*mws_coex_info_state_resp_callback)(void *coex_info_data, + void *context, + wmi_mws_coex_cmd_id cmd_id); +#endif /* WLAN_MWS_INFO_DEBUGFS */ + +#ifdef WLAN_BCN_RECV_FEATURE + beacon_report_cb beacon_report_cb; + beacon_pause_cb beacon_pause_cb; +#endif +#ifdef FEATURE_OEM_DATA + void (*oem_data_event_handler_cb) + (const struct oem_data *oem_event_data, + uint8_t vdev_id); + uint8_t oem_data_vdev_id; +#endif + sme_get_raom_scan_ch_callback roam_scan_ch_callback; + void *roam_scan_ch_get_context; +#ifdef FEATURE_MONITOR_MODE_SUPPORT + void (*monitor_mode_cb)(uint8_t vdev_id); +#endif +}; + +#endif /* #if !defined( __SMEINTERNAL_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_nan_datapath.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_nan_datapath.h new file mode 100644 index 0000000000000000000000000000000000000000..9ca08c8eff29a67e02e29ffc288c59f4c6b99e27 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_nan_datapath.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: sme_nan_datapath.h + * + * SME NAN Data path API specification + */ + +#ifndef __SME_NAN_DATAPATH_H +#define __SME_NAN_DATAPATH_H + +#include "csr_inside_api.h" + +#ifdef WLAN_FEATURE_NAN +/* Start NDI BSS */ +QDF_STATUS csr_roam_start_ndi(struct mac_context *mac_ctx, uint32_t session_id, + struct csr_roam_profile *profile); + +void csr_roam_save_ndi_connected_info(struct mac_context *mac_ctx, + uint32_t session_id, + struct csr_roam_profile *roam_profile, + struct bss_description *bss_desc); + +void csr_roam_update_ndp_return_params(struct mac_context *mac_ctx, + uint32_t result, + uint32_t *roam_status, + uint32_t *roam_result, + struct csr_roam_info *roam_info); + +#else /* WLAN_FEATURE_NAN */ +/* Start NDI BSS */ +static inline QDF_STATUS csr_roam_start_ndi(struct mac_context *mac_ctx, + uint32_t session_id, + struct csr_roam_profile *profile) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void csr_roam_save_ndi_connected_info(struct mac_context *mac_ctx, + uint32_t session_id, + struct csr_roam_profile *roam_profile, + struct bss_description *bss_desc) +{ +} + +static inline void csr_roam_update_ndp_return_params(struct mac_context *mac_ctx, + uint32_t result, + uint32_t *roam_status, + uint32_t *roam_result, + struct csr_roam_info *roam_info) +{ +} + +#endif /* WLAN_FEATURE_NAN */ + +#endif /* __SME_NAN_DATAPATH_H */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_power_save.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_power_save.h new file mode 100644 index 0000000000000000000000000000000000000000..237e92a04248dc427c97eeeea6c3a609796b1dba --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_power_save.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SME_POWER_SAVE_H) +#define __SME_POWER_SAVE_H +#include "qdf_lock.h" +#include "qdf_trace.h" +#include "qdf_mem.h" +#include "qdf_types.h" +#include "ani_system_defs.h" +#include "sir_api.h" + +/* + * Auto Ps Entry User default timeout value, used instead of negative timeouts + * from user space - 5000ms + */ +#define AUTO_PS_ENTRY_USER_TIMER_DEFAULT_VALUE 5000 +#define AUTO_PS_ENTRY_TIMER_DEFAULT_VALUE 1000 +#define AUTO_PS_DEFER_TIMEOUT_MS 1500 + +/** + * enum ps_state - State of the power save + * @FULL_POWER_MODE: for Full power mode + * @LEGACY_POWER_SAVE_MODE: For Legacy Power Save mode + * @UAPSD_MODE: for UAPSD power save + */ + +enum ps_state { + FULL_POWER_MODE, + LEGACY_POWER_SAVE_MODE, + UAPSD_MODE +}; + +/** + * struct ps_params - maintain power save state and USAPD params + * @mac_ctx: mac_ctx + * @session_id: Session Id. + * @ps_state : State of the power save + * @uapsd_per_ac_trigger_enable_mask: dynamic UPASD mask setting + * derived from AddTS Rsp and DelTS frame. + * If a particular AC bit is set, it means AC is trigger enabled. + * @uapsd_per_ac_delivery_enable_mask: dynamic UPASD mask setting + * derived from AddTS Rsp and DelTs frame. + * If a particular AC bit is set, it means AC is delivery enabled. + * @ac_admit_mask: used for AC downgrade. This is a dynamic mask + * setting which keep tracks of ACs being admitted. + * If bit is set to 0: That particular AC is not admitted + * If bit is set to 1: That particular AC is admitted + * @uapsd_per_ac_bit_mask: This is a static UAPSD mask setting + * derived from SME_JOIN_REQ and SME_REASSOC_REQ. + * If a particular AC bit is set, it means the AC is both + * trigger enabled and delivery enabled. + * @auto_ps_enable_timer: Upon expiration of this timer Power Save Offload + * module will try to enable sta mode ps + */ + +struct ps_params { + void *mac_ctx; + uint32_t session_id; + enum ps_state ps_state; + uint8_t uapsd_per_ac_trigger_enable_mask; + uint8_t uapsd_per_ac_delivery_enable_mask; + uint8_t ac_admit_mask[SIR_MAC_DIRECTION_DIRECT]; + uint8_t uapsd_per_ac_bit_mask; + qdf_mc_timer_t auto_ps_enable_timer; + +}; + +/** + * struct ps_global_info - global struct for Power save information + * @ps_params: maintain power save state and USAPD params + * @remain_in_power_active_till_dhcp: remain in Power active till DHCP completes + */ +struct ps_global_info { + struct ps_params ps_params[WLAN_MAX_VDEVS]; + bool remain_in_power_active_till_dhcp; +}; + +/** + * enum sme_ps_cmd: power save message to send WMA + * @SME_PS_ENABLE: For power save enable. + * @SME_PS_DISABLE: for Power save disable. + * @SME_PS_UAPSD_ENABLE; for UAPSD enable. + * @SME_PS_UAPSD_DISABLE: for UAPSD disable. + * @SME_PS_WOWL_ENTER: for WOWL Enter. + * @SME_PS_WOWL_EXIT: for WOWL Exit. + * @SME_PS_WOWL_ADD_BCAST_PTRN: Add bcst WOWL pattern. + * @SME_PS_WOWL_DEL_BCAST_PTRN: Del Bcsr Wowl Pattern. + */ +enum sme_ps_cmd { + SME_PS_ENABLE = 0, + SME_PS_DISABLE, + SME_PS_UAPSD_ENABLE, + SME_PS_UAPSD_DISABLE, + SME_PS_WOWL_ENTER, + SME_PS_WOWL_EXIT, + SME_PS_WOWL_ADD_BCAST_PTRN, + SME_PS_WOWL_DEL_BCAST_PTRN, +}; + +#endif diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_power_save_api.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_power_save_api.h new file mode 100644 index 0000000000000000000000000000000000000000..74513126f8c02a9292b7b043f4a7e011e121df76 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_power_save_api.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SME_POWER_SAVE_API_H) +#define __SME_POWER_SAVE_API_H + +#include "sme_power_save.h" +#include "ani_global.h" +#include "sme_inside.h" + +QDF_STATUS sme_ps_enable_disable(mac_handle_t mac_handle, uint32_t session_id, + enum sme_ps_cmd command); + +QDF_STATUS sme_ps_timer_flush_sync(mac_handle_t mac_handle, + uint8_t session_id); + +QDF_STATUS sme_ps_uapsd_enable(mac_handle_t mac_handle, uint32_t session_id); + +QDF_STATUS sme_ps_uapsd_disable(mac_handle_t mac_handle, uint32_t session_id); + +QDF_STATUS sme_enable_sta_ps_check(struct mac_context *mac_ctx, + uint32_t session_id, + enum sme_ps_cmd command); + +QDF_STATUS sme_ps_process_command(struct mac_context *mac_ctx, + uint32_t session_id, + enum sme_ps_cmd command); + +void sme_set_tspec_uapsd_mask_per_session(struct mac_context *mac_ctx, + struct mac_ts_info *ts_info, + uint8_t session_id); + +QDF_STATUS sme_ps_start_uapsd(mac_handle_t mac_handle, uint32_t session_id); +QDF_STATUS sme_set_ps_host_offload(mac_handle_t mac_handle, + struct sir_host_offload_req *request, + uint8_t session_id); + +#ifdef WLAN_NS_OFFLOAD +QDF_STATUS sme_set_ps_ns_offload(mac_handle_t mac_handle, + struct sir_host_offload_req *request, + uint8_t session_id); + +#endif /* WLAN_NS_OFFLOAD */ +/* / Post a message to PE module */ +QDF_STATUS sme_post_pe_message(struct mac_context *mac_ctx, + struct scheduler_msg *pMsg); + +/** + * sme_ps_enable_auto_ps_timer(): Enable power-save auto timer with timeout + * @mac_handle: Opaque handle to the global MAC context + * @session_id: adapter session Id + * @timeout: timeout period in ms + * + * Returns: QDF_STATUS + */ +QDF_STATUS sme_ps_enable_auto_ps_timer(mac_handle_t mac_handle, + uint32_t sessionId, uint32_t timeout); +QDF_STATUS sme_ps_disable_auto_ps_timer(mac_handle_t mac_handle, + uint32_t sessionId); + +QDF_STATUS sme_ps_open(mac_handle_t mac_handle); + +QDF_STATUS sme_ps_open_per_session(mac_handle_t mac_handle, + uint32_t session_id); + +void sme_auto_ps_entry_timer_expired(void *ps_param); +QDF_STATUS sme_ps_close(mac_handle_t mac_handle); +QDF_STATUS sme_ps_close_per_session(mac_handle_t mac_handle, + uint32_t session_id); + +/** + * sme_save_usr_ps_cfg(): saves user power save config + * @mac_handle: Opaque handle to the global MAC context + * @val: set val + * + * Returns: QDF_STATUS + */ +void sme_save_usr_ps_cfg(mac_handle_t mac_handle, bool val); + +#endif /* #if !defined(__SME_POWER_SAVE_API_H) */ + diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_qos_api.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_qos_api.h new file mode 100644 index 0000000000000000000000000000000000000000..09d4993f151f6d82bfa8c1a2c69d554f5ec09f83 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_qos_api.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SME_QOSAPI_H) +#define __SME_QOSAPI_H + +/** + * \file sme_qos_api.h + * + * \brief prototype for SME QoS APIs + */ + +/* Include Files */ +#include "qdf_lock.h" +#include "qdf_trace.h" +#include "qdf_mem.h" +#include "qdf_types.h" +#include "ani_global.h" +#include "sir_api.h" + +/* Pre-processor Definitions */ +#define SME_QOS_UAPSD_VO 0x01 +#define SME_QOS_UAPSD_VI 0x02 +#define SME_QOS_UAPSD_BE 0x08 +#define SME_QOS_UAPSD_BK 0x04 + +/* Enumeration of the various QoS status types that would be reported to HDD */ +enum sme_qos_statustype { + /* + * async: once PE notifies successful TSPEC negotiation, or CSR notifies + * for successful reassoc, notifies HDD with current QoS Params + */ + SME_QOS_STATUS_SETUP_SUCCESS_IND = 0, + /* sync: only when App asked for APSD & it's already set with ACM = 0 */ + SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY, + /* sync or async: in case of async notify HDD with current QoS Params */ + SME_QOS_STATUS_SETUP_FAILURE_RSP, + /* sync */ + SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP, + /* sync: AP doesn't support QoS (WMM) */ + SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP, + /* sync: either req has been sent down to PE or just buffered in SME */ + SME_QOS_STATUS_SETUP_REQ_PENDING_RSP, + /* + * async: in case of flow aggregation, if the new TSPEC negotiation + * is successful, OR, notify existing flows that TSPEC is modified with + * current QoS Params + */ + SME_QOS_STATUS_SETUP_MODIFIED_IND, + /* sync: no APSD asked for & ACM = 0 */ + SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP, + /* + * async: In case of UAPSD, once PE notifies successful TSPEC + * negotiation, or CSR notifies for successful reassoc to SME-QoS, + * notify HDD if PMC can't put the module in UAPSD mode right away + * (QDF_STATUS_PMC_PENDING) + */ + SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING, + /* + * async: In case of UAPSD, once PE notifies successful TSPEC + * negotiation, or CSR notifies for successful reassoc to SME-QoS, + * notify HDD if PMC can't put the module in UAPSD mode at all + * (QDF_STATUS_E_FAILURE) + */ + SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED, + /* + * sync: req has been sent down to PE in case of delts or addts + * for remain flows, OR if the AC doesn't have APSD or ACM + * async: once the downgrade req for QoS params is successful + */ + SME_QOS_STATUS_RELEASE_SUCCESS_RSP = 100, + /* sync or async: in case of async notify HDD with current QoS Param */ + SME_QOS_STATUS_RELEASE_FAILURE_RSP, + /* async: AP sent DELTS indication */ + SME_QOS_STATUS_RELEASE_QOS_LOST_IND, + /* + * sync: an addts req has been sent down to PE to downgrade the + * QoS params or just buffered in SME + */ + SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP, + /* sync */ + SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP, + /* + * async: for QoS modify request if modification is successful, + * notifies HDD with current QoS Params + */ + SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND = 200, + /* sync: only when App asked for APSD & it's already set with ACM = 0 */ + SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY, + /* sync or async: in case of async notify HDD with current QoS Param */ + SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP, + /* sync: either req has been sent down to PE or just buffered in SME */ + SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP, + /* sync: no APSD asked for & ACM = 0 */ + SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP, + /* sync */ + SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP, + /* + * async: In case of UAPSD, once PE notifies successful TSPEC + * negotiation or CSR notifies for successful reassoc to SME-QoS, + * notify HDD if PMC can't put the module in UAPSD mode right away + * (QDF_STATUS_PMC_PENDING) + */ + SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_PENDING, + /* + * async: In case of UAPSD, once PE notifies successful TSPEC + * negotiation, or CSR notifies for successful reassoc to SME-QoS, + * notify HDD if PMC can't put the module in UAPSD mode at all + * (QDF_STATUS_E_FAILURE) + */ + SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED, + /* sync: STA is handing off to a new AP */ + SME_QOS_STATUS_HANDING_OFF = 300, + /* async:powersave mode changed by PMC from UAPSD to Full power */ + SME_QOS_STATUS_OUT_OF_APSD_POWER_MODE_IND = 400, + /* async:powersave mode changed by PMC from Full power to UAPSD */ + SME_QOS_STATUS_INTO_APSD_POWER_MODE_IND, + +}; + +#define WLAN_MAX_DSCP 0x3f + +/* + * Enumeration of the various User priority (UP) types + * From 802.1D/802.11e/WMM specifications (all refer to same table) + */ +enum sme_qos_wmmuptype { + SME_QOS_WMM_UP_BE = 0, + SME_QOS_WMM_UP_BK = 1, + SME_QOS_WMM_UP_RESV = 2, /* Reserved */ + SME_QOS_WMM_UP_EE = 3, + SME_QOS_WMM_UP_CL = 4, + SME_QOS_WMM_UP_VI = 5, + SME_QOS_WMM_UP_VO = 6, + SME_QOS_WMM_UP_NC = 7, + SME_QOS_WMM_UP_MAX +}; + +/* + * Enumeration of the various TSPEC ack policies. + * From 802.11 WMM specification + */ +enum sme_qos_wmmack_policytype { + SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK = 0, + SME_QOS_WMM_TS_ACK_POLICY_RESV1 = 1, + SME_QOS_WMM_TS_ACK_POLICY_RESV2 = 2, /* Reserved */ + SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK = 3, + +}; + +/* + * TS Info field in the WMM TSPEC + * See suggestive values above + */ +struct sme_qos_wmmts_infotype { + uint8_t burst_size_defn; + enum sme_qos_wmmack_policytype ack_policy; + enum sme_qos_wmmuptype up; /* User priority */ + uint8_t psb; /* power-save bit */ + enum sme_qos_wmm_dir_type direction; /* Direction */ + uint8_t tid; /* TID : To be filled up by SME-QoS */ +}; + +/* The WMM TSPEC Element (from the WMM spec)*/ +struct sme_qos_wmmtspecinfo { + struct sme_qos_wmmts_infotype ts_info; + uint16_t nominal_msdu_size; + uint16_t maximum_msdu_size; + uint32_t min_service_interval; + uint32_t max_service_interval; + uint32_t inactivity_interval; + uint32_t suspension_interval; + uint32_t svc_start_time; + uint32_t min_data_rate; + uint32_t mean_data_rate; + uint32_t peak_data_rate; + uint32_t max_burst_size; + uint32_t delay_bound; + uint32_t min_phy_rate; + uint16_t surplus_bw_allowance; + uint16_t medium_time; +}; + +static inline enum sme_qos_wmmuptype qca_wlan_ac_to_sme_qos(u8 priority) +{ + switch (priority) { + case QCA_WLAN_AC_BE: + return SME_QOS_WMM_UP_BE; + case QCA_WLAN_AC_BK: + return SME_QOS_WMM_UP_BK; + case QCA_WLAN_AC_VI: + return SME_QOS_WMM_UP_VI; + case QCA_WLAN_AC_VO: + return SME_QOS_WMM_UP_VO; + default: + return SME_QOS_WMM_UP_BE; + } +} + +/* External APIs */ +typedef QDF_STATUS (*sme_QosCallback)(mac_handle_t mac_handle, void *HDDcontext, + struct sme_qos_wmmtspecinfo *pCurrentQoSInfo, + enum sme_qos_statustype status, uint32_t QosFlowID); +enum sme_qos_statustype sme_qos_setup_req(mac_handle_t mac_handle, + uint32_t sessionId, + struct sme_qos_wmmtspecinfo *pQoSInfo, + sme_QosCallback QoSCallback, + void *HDDcontext, + enum sme_qos_wmmuptype UPType, + uint32_t *pQosFlowID); +enum sme_qos_statustype sme_qos_modify_req(mac_handle_t mac_handle, + struct sme_qos_wmmtspecinfo *pQoSInfo, uint32_t QosFlowID); +enum sme_qos_statustype sme_qos_release_req(mac_handle_t mac_handle, + uint8_t session_id, + uint32_t QosFlowID); +bool sme_qos_is_ts_info_ack_policy_valid(mac_handle_t mac_handle, + struct sme_qos_wmmtspecinfo *pQoSInfo, + uint8_t sessionId); +void sme_qos_update_hand_off(uint8_t sessionId, bool updateHandOff); +QDF_STATUS sme_update_dsc_pto_up_mapping(mac_handle_t mac_handle, + enum sme_qos_wmmuptype *dscpmapping, uint8_t sessionId); + +QDF_STATUS sme_offload_qos_process_out_of_uapsd_mode(struct mac_context *mac_ctx, + uint32_t session_id); +QDF_STATUS sme_offload_qos_process_into_uapsd_mode(struct mac_context *mac_ctx, + uint32_t session_id); + + +#endif /* #if !defined( __SME_QOSAPI_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_qos_internal.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_qos_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..f1bfe45f1c6840c14aa8f8bedc2b5158fdbffc63 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_qos_internal.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SMEQOSINTERNAL_H) +#define __SMEQOSINTERNAL_H + +/** + * \file sme_qos_internal.h + * + * \brief prototype for SME QoS APIs + */ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "qdf_lock.h" +#include "qdf_trace.h" +#include "qdf_mem.h" +#include "qdf_types.h" +#include "ani_global.h" +#include "sir_api.h" +#include "sme_qos_api.h" +#include "sme_internal.h" + +/*-------------------------------------------------------------------------- + Type declarations + ------------------------------------------------------------------------*/ +#define SME_QOS_AP_SUPPORTS_APSD 0x80 + +/*--------------------------------------------------------------------------- + Enumeration of the various CSR event indication types that would be reported + by CSR + ---------------------------------------------------------------------------*/ +typedef enum { + SME_QOS_CSR_JOIN_REQ = 0, + SME_QOS_CSR_ASSOC_COMPLETE, + SME_QOS_CSR_REASSOC_REQ, + SME_QOS_CSR_REASSOC_COMPLETE, + SME_QOS_CSR_REASSOC_FAILURE, + SME_QOS_CSR_DISCONNECT_REQ, + SME_QOS_CSR_DISCONNECT_IND, + SME_QOS_CSR_HANDOFF_ASSOC_REQ, + SME_QOS_CSR_HANDOFF_COMPLETE, + SME_QOS_CSR_PREAUTH_SUCCESS_IND, + SME_QOS_CSR_SET_KEY_SUCCESS_IND, +} sme_qos_csr_event_indType; + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +typedef enum { + SME_QOS_DIAG_ADDTS_REQ = 0, + SME_QOS_DIAG_ADDTS_RSP, + SME_QOS_DIAG_DELTS +} sme_QosDiagQosEventSubtype; + +typedef enum { + SME_QOS_DIAG_ADDTS_ADMISSION_ACCEPTED = 0, + SME_QOS_DIAG_ADDTS_INVALID_PARAMS, + SME_QOS_DIAG_ADDTS_RESERVED, + SME_QOS_DIAG_ADDTS_REFUSED, + SME_QOS_DIAG_USER_REQUESTED, + SME_QOS_DIAG_DELTS_IND_FROM_AP, + +} sme_QosDiagQosEventReasonCode; + +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ +/*--------------------------------------------------------------------------- + The association information structure to be passed by CSR after assoc or + reassoc is done + ---------------------------------------------------------------------------*/ +typedef struct { + struct bss_description *bss_desc; + struct csr_roam_profile *pProfile; +} sme_QosAssocInfo; + +/*-------------------------------------------------------------------------- + External APIs for CSR - Internal to SME + ------------------------------------------------------------------------*/ +QDF_STATUS sme_qos_open(struct mac_context *mac); +QDF_STATUS sme_qos_close(struct mac_context *mac); +QDF_STATUS sme_qos_msg_processor(struct mac_context *mac, uint16_t msg_type, + void *msg_buf); + +/*-------------------------------------------------------------------------- + Internal APIs for CSR + ------------------------------------------------------------------------*/ +QDF_STATUS sme_qos_csr_event_ind(struct mac_context *mac, + uint8_t sessionId, + sme_qos_csr_event_indType ind, void *pEvent_info); +uint8_t sme_qos_get_acm_mask(struct mac_context *mac, + struct bss_description *pSirBssDesc, tDot11fBeaconIEs *pIes); +#ifdef FEATURE_WLAN_ESE +uint8_t sme_qos_ese_retrieve_tspec_info(struct mac_context *mac, uint8_t sessionId, + tTspecInfo * pTspecInfo); +#endif + +#endif /* #if !defined( __SMEQOSINTERNAL_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_rrm_api.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_rrm_api.h new file mode 100644 index 0000000000000000000000000000000000000000..3f8ab5a568e9e17ae03b79c596e9556a97eb6f55 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_rrm_api.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SMERRMAPI_H) +#define __SMERRMAPI_H + +/** + * \file sme_rrm_api.h + * + * \brief prototype for SME RRM APIs + */ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "qdf_lock.h" +#include "qdf_trace.h" +#include "qdf_mem.h" +#include "qdf_types.h" +#include "ani_global.h" +#include "sir_api.h" +#include "sme_internal.h" +#include "sme_rrm_internal.h" + +QDF_STATUS sme_rrm_msg_processor(struct mac_context *mac, uint16_t msg_type, + void *msg_buf); +QDF_STATUS rrm_close(struct mac_context *mac); +QDF_STATUS rrm_open(struct mac_context *mac); +QDF_STATUS sme_rrm_neighbor_report_request(struct mac_context *mac, + uint8_t sessionId, tpRrmNeighborReq pNeighborReq, + tpRrmNeighborRspCallbackInfo callbackInfo); +QDF_STATUS sme_rrm_process_beacon_report_req_ind(struct mac_context *mac, + void *msg_buf); + +/** + * rrm_start() - start the RRM module + * @mac_ctx: The handle returned by mac_open. + * + * Return: QDF_STATUS + * QDF_STATUS_SUCCESS success + */ +QDF_STATUS rrm_start(struct mac_context *mac_ctx); + +/** + * rrm_stop() - stop the RRM module + * @mac_ctx: The handle returned by mac_open. + * + * Return: QDF_STATUS + * QDF_STATUS_SUCCESS success + */ +QDF_STATUS rrm_stop(struct mac_context *mac_ctx); + +#endif diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_rrm_internal.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_rrm_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..8545ec9088660bcc09afd44366ff1fb936934914 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_rrm_internal.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011-2012, 2014-2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__SMERRMINTERNAL_H) +#define __SMERRMINTERNAL_H + +/** + * \file sme_rrm_internal.h + * + * \brief prototype for SME RRM APIs + */ + +/*-------------------------------------------------------------------------- + Include Files + ------------------------------------------------------------------------*/ +#include "qdf_lock.h" +#include "qdf_trace.h" +#include "qdf_mem.h" +#include "qdf_types.h" +#include "rrm_global.h" + +/*-------------------------------------------------------------------------- + Type declarations + ------------------------------------------------------------------------*/ +typedef struct sRrmNeighborReportDesc { + tListElem List; + tSirNeighborBssDescription *pNeighborBssDescription; + uint32_t roamScore; + uint8_t sessionId; +} tRrmNeighborReportDesc, *tpRrmNeighborReportDesc; + +typedef void (*NeighborReportRspCallback)(void *context, + QDF_STATUS qdf_status); + +typedef struct sRrmNeighborRspCallbackInfo { + uint32_t timeout; /* in ms.. min value is 10 (10ms) */ + NeighborReportRspCallback neighborRspCallback; + void *neighborRspCallbackContext; +} tRrmNeighborRspCallbackInfo, *tpRrmNeighborRspCallbackInfo; + +typedef struct sRrmNeighborRequestControlInfo { + /* To check whether a neighbor req is already sent & response pending */ + bool isNeighborRspPending; + qdf_mc_timer_t neighborRspWaitTimer; + tRrmNeighborRspCallbackInfo neighborRspCallbackInfo; +} tRrmNeighborRequestControlInfo, *tpRrmNeighborRequestControlInfo; + +typedef struct sRrmSMEContext { + uint16_t token; + struct qdf_mac_addr sessionBssId; + uint8_t regClass; + uint8_t measurement_idx; + /* list of all channels to be measured. */ + tCsrChannelInfo channelList; + uint8_t currentIndex; + /* SSID used in the measuring beacon report. */ + tAniSSID ssId; + tSirMacAddr bssId; /* bssid used for beacon report measurement. */ + /* Randomization interval to be used in subsequent measurements. */ + uint16_t randnIntvl; + uint16_t duration[SIR_ESE_MAX_MEAS_IE_REQS]; + uint8_t measMode[SIR_ESE_MAX_MEAS_IE_REQS]; + uint32_t scan_id; + qdf_mc_timer_t IterMeasTimer; + tDblLinkList neighborReportCache; + tRrmNeighborRequestControlInfo neighborReqControlInfo; + +#ifdef FEATURE_WLAN_ESE + tCsrEseBeaconReq eseBcnReqInfo; + bool eseBcnReqInProgress; +#endif /* FEATURE_WLAN_ESE */ + tRrmMsgReqSource msgSource; + wlan_scan_requester req_id; +} tRrmSMEContext, *tpRrmSMEContext; + +typedef struct sRrmNeighborReq { + uint8_t no_ssid; + tSirMacSSid ssid; + bool neighbor_report_offload; +} tRrmNeighborReq, *tpRrmNeighborReq; + +#endif /* #if !defined( __SMERRMINTERNAL_H ) */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/sme_trace.h b/drivers/staging/qcacld-3.0/core/sme/inc/sme_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..51440838679ab81a25c2d389032940f4cf914c7f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/sme_trace.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \sme_trace.h + * + * \brief definition for trace related APIs + */ + +#ifndef __SME_TRACE_H__ +#define __SME_TRACE_H__ + +#include "mac_trace.h" + +#define NO_SESSION 0xFF +enum { + TRACE_CODE_SME_RX_HDD_MSG_SCAN_REQ, + TRACE_CODE_SME_RX_HDD_MSG_SCAN_GET_RESULTS, + TRACE_CODE_SME_RX_HDD_MSG_CONNECT, + TRACE_CODE_SME_RX_HDD_MSG_SET_11DINFO, + TRACE_CODE_SME_RX_HDD_MSG_GET_SOFTAP_DOMAIN, + TRACE_CODE_SME_RX_HDD_MSG_SET_REGINFO, + TRACE_CODE_SME_RX_HDD_MSG_UPDATE_CHANNEL_CONFIG, + TRACE_CODE_SME_RX_HDD_MSG_UPDATE_CONFIG, + TRACE_CODE_SME_RX_HDD_MSG_HDDREADYIND, + TRACE_CODE_SME_RX_HDD_MSG_SCAN_FLUSH_RESULTS, + TRACE_CODE_SME_RX_HDD_MSG_SCAN_FLUSH_P2PRESULTS, + TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_GETFIRST, + TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_GETNEXT, + TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_PURGE, + TRACE_CODE_SME_RX_HDD_ROAM_REASSOC, + TRACE_CODE_SME_RX_HDD_ROAM_DISCONNECT, + TRACE_CODE_SME_RX_HDD_ROAM_GET_CONNECTPROFILE, + TRACE_CODE_SME_RX_HDD_ROAM_FREE_CONNECTPROFILE, + TRACE_CODE_SME_RX_HDD_ROAM_SET_PMKIDCACHE, + TRACE_CODE_SME_RX_HDD_ROAM_GET_PMKIDCACHE, + TRACE_CODE_SME_RX_HDD_GET_CONFIGPARAM, + TRACE_CODE_SME_RX_HDD_GET_MODPROFFIELDS, + TRACE_CODE_SME_RX_HDD_SET_CONFIG_PWRSAVE, + TRACE_CODE_SME_RX_HDD_GET_CONFIG_PWRSAVE, + TRACE_CODE_SME_RX_HDD_ENABLE_PWRSAVE, + TRACE_CODE_SME_RX_HDD_DISABLE_PWRSAVE, + TRACE_CODE_SME_RX_HDD_SIGNAL_POWER_EVENT, + TRACE_CODE_SME_RX_HDD_START_AUTO_BMPSTIMER, + TRACE_CODE_SME_RX_HDD_STOP_AUTO_BMPSTIMER, + TRACE_CODE_SME_RX_HDD_IS_PWRSAVE_ENABLED, + TRACE_CODE_SME_RX_HDD_REQUEST_FULLPOWER, + TRACE_CODE_SME_RX_HDD_REQUEST_BMPS, + TRACE_CODE_SME_RX_HDD_SET_DHCP_FLAG, + TRACE_CODE_SME_RX_HDD_REQUEST_STANDBY, + TRACE_CODE_SME_RX_HDD_WOWL_ADDBCAST_PATTERN, + TRACE_CODE_SME_RX_HDD_WOWL_DELBCAST_PATTERN, + TRACE_CODE_SME_RX_HDD_ENTER_WOWL, + TRACE_CODE_SME_RX_HDD_EXIT_WOWL, + TRACE_CODE_SME_RX_HDD_SET_KEY, + TRACE_CODE_SME_RX_HDD_REMOVE_KEY, + TRACE_CODE_SME_RX_HDD_GET_CNTRYCODE, + TRACE_CODE_SME_RX_HDD_SET_CNTRYCODE, + TRACE_CODE_SME_RX_HDD_SET_CFGPRIVACY, + TRACE_CODE_SME_RX_HDD_NEIGHBOR_REPORTREQ, + TRACE_CODE_SME_RX_HDD_DBG_READREG, + TRACE_CODE_SME_RX_HDD_DBG_WRITEREG, + TRACE_CODE_SME_RX_HDD_DBG_READMEM, + TRACE_CODE_SME_RX_HDD_DBG_WRITEMEM, + TRACE_CODE_SME_RX_HDD_OPEN_SESSION, + TRACE_CODE_SME_RX_HDD_CLOSE_SESSION, + TRACE_CODE_SME_RX_HDD_SET_HOSTOFFLOAD, + TRACE_CODE_SME_RX_HDD_SET_GTKOFFLOAD, + TRACE_CODE_SME_RX_HDD_GET_GTKOFFLOAD, + TRACE_CODE_SME_RX_HDD_ABORT_MACSCAN, + TRACE_CODE_SME_RX_HDD_REGISTER_MGMTFR, + TRACE_CODE_SME_RX_HDD_DEREGISTER_MGMTFR, + TRACE_CODE_SME_RX_HDD_REMAIN_ONCHAN, + TRACE_CODE_SME_RX_HDD_SEND_ACTION, + TRACE_CODE_SME_RX_HDD_CANCEL_REMAIN_ONCHAN, + TRACE_CODE_SME_RX_HDD_CONFIG_RXPFIL, + TRACE_CODE_SME_RX_HDD_CONFIG_SUSPENDIND, + TRACE_CODE_SME_RX_HDD_CONFIG_RESUMEREQ, +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + TRACE_CODE_SME_RX_HDD_CONFIG_EXTWOW, + TRACE_CODE_SME_RX_HDD_CONFIG_APP_TYPE1, + TRACE_CODE_SME_RX_HDD_CONFIG_APP_TYPE2, +#endif + TRACE_CODE_SME_RX_HDD_SET_MAXTXPOW, + TRACE_CODE_SME_RX_HDD_SET_TXPOW, + TRACE_CODE_SME_RX_HDD_SET_TMLEVEL, + TRACE_CODE_SME_RX_HDD_CAPS_EXCH, + TRACE_CODE_SME_RX_HDD_DISABLE_CAP, + TRACE_CODE_SME_RX_HDD_GET_DEFCCNV, + TRACE_CODE_SME_RX_HDD_GET_CURCC, + TRACE_CODE_SME_RX_HDD_RESET_PW5G, + TRACE_CODE_SME_RX_HDD_UPDATE_RP5G, + TRACE_CODE_SME_RX_HDD_SET_ROAMIBAND, + TRACE_CODE_SME_RX_HDD_GET_ROAMIBAND, + TRACE_CODE_SME_RX_HDD_UPDATE_RSSIDIFF, + TRACE_CODE_SME_RX_HDD_UPDATE_IMMRSSIDIFF, + TRACE_CODE_SME_RX_HDD_UPDATE_FTENABLED, + TRACE_CODE_SME_RX_HDD_UPDATE_WESMODE, + TRACE_CODE_SME_RX_HDD_SET_SCANCTRL, + TRACE_CODE_SME_RX_HDD_UPDATE_P2P_IE, + TRACE_CODE_SME_RX_HDD_UPDATE_ROAM_SCAN_N_PROBES, + TRACE_CODE_SME_RX_HDD_UPDATE_ROAM_SCAN_HOME_AWAY_TIME, + TRACE_CODE_SME_RX_HDD_STORE_JOIN_REQ, + TRACE_CODE_SME_RX_HDD_CLEAR_JOIN_REQ, + TRACE_CODE_SME_RX_HDD_ISSUE_JOIN_REQ, + TRACE_CODE_SME_RX_HDD_MSG_DEAUTH_STA, +#ifdef FEATURE_WLAN_TDLS + TRACE_CODE_SME_RX_HDD_TDLS_LINK_ESTABLISH_PARAM, + TRACE_CODE_SME_RX_HDD_TDLS_CHAN_SWITCH_REQ, + TRACE_CODE_SME_RX_HDD_TDLS_SEND_MGMT_FRAME, + TRACE_CODE_SME_RX_HDD_TDLS_CHANGE_PEER_STA, + TRACE_CODE_SME_RX_HDD_TDLS_ADD_PEER_STA, + TRACE_CODE_SME_RX_HDD_TDLS_DEL_PEER_STA, +#endif + TRACE_CODE_SME_RX_HDD_PREF_NET_LIST, + TRACE_CODE_SME_RX_HDD_ROAM_DEL_PMKIDCACHE, + TRACE_CODE_SME_RX_HDD_SEND_MGMT_TX, + /* + * New trace commands to be added before this comment not at the end + * Trace codes for SME commands + */ + TRACE_CODE_SME_COMMAND = 250, + TRACE_CODE_SME_TX_WMA_MSG, + TRACE_CODE_SME_RX_WMA_MSG, +}; + +void sme_trace_init(struct mac_context *mac); +#endif /* __SME_TRACE_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/sme/inc/wlan_ps_wow_diag.h b/drivers/staging/qcacld-3.0/core/sme/inc/wlan_ps_wow_diag.h new file mode 100644 index 0000000000000000000000000000000000000000..4702eb2ce90e88094fb1890282896263f24c675f --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/inc/wlan_ps_wow_diag.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_PS_WOW_DIAG_H_ +#define _WLAN_PS_WOW_DIAG_H_ + +#include "host_diag_core_event.h" +#include "host_diag_core_log.h" + +#ifdef FEATURE_WLAN_DIAG_SUPPORT + +typedef enum { + WLAN_BMPS_ENTER_REQ = 0, + WLAN_UAPSD_START_REQ = 1, + WLAN_UAPSD_STOP_REQ = 2, + WLAN_ENTER_STANDBY_REQ = 3, + WLAN_ENTER_DEEP_SLEEP_REQ = 4, + WLAN_START_BMPS_AUTO_TIMER_REQ = 5, + WLAN_STOP_BMPS_AUTO_TIMER_REQ = 6, + WLAN_ENTER_FULL_POWER_REQ = 7, + WLAN_PMC_CURRENT_STATE = 8, + WLAN_PS_MODE_ENABLE_REQ = 9, + WLAN_PS_MODE_DISABLE_REQ = 10, + WLAN_WINMOB_D_POWER_STATE = 11, + WLAN_BMPS_DTIM_PERIOD = 12, + WLAN_BMPS_FINAL_LI = 13, + WLAN_BMPS_SET_CONFIG = 14, + +} wlan_ps_evt_subtype_t; + +/* maps directly to eRequestFullPowerReason */ +typedef enum { + /* PE received a MAX_MISSED_BEACON_IND */ + WLAN_MISSED_BEACON_IND_RCVD, + /* PE received a SIR_HAL_BMPS_STATUS_IND */ + WLAN_BMPS_STATUS_IND_RCVD, + /* BMPS mode was disabled by HDD in SME */ + WLAN_BMPS_MODE_DISABLED, + /* Link has been disconnected requested by HDD */ + WLAN_LINK_DISCONNECTED_BY_HDD, + /* Disconnect due to linklost or requested by peer */ + WLAN_LINK_DISCONNECTED_BY_OTHER, + /* HDD request full power for some reason */ + WLAN_FULL_PWR_NEEDED_BY_HDD, + /* BAP request full power for BT_AMP */ + WLAN_FULL_PWR_NEEDED_BY_BAP, + /* CSR requests full power */ + WLAN_FULL_PWR_NEEDED_BY_CSR, + /* QOS requests full power */ + WLAN_FULL_PWR_NEEDED_BY_QOS, + /* No specific reason. General reason code */ + WLAN_REASON_OTHER +} wlan_ps_full_power_request_reason_t; + +/* maps directly to ePmcState */ +typedef enum { + WLAN_PMC_STOPPED, /* PMC is stopped */ + WLAN_PMC_FULL_POWER, /* full power */ + WLAN_PMC_LOW_POWER, /* low power */ + WLAN_PMC_REQUEST_BMPS, /* requesting BMPS */ + WLAN_PMC_BMPS, /* in BMPS */ + WLAN_PMC_REQUEST_FULL_POWER, /* requesting full power */ + WLAN_PMC_REQUEST_START_UAPSD, /* requesting Start UAPSD */ + WLAN_PMC_REQUEST_STOP_UAPSD, /* requesting Stop UAPSD */ + WLAN_PMC_UAPSD, /* in UAPSD */ + WLAN_PMC_REQUEST_STANDBY, /* requesting standby mode */ + WLAN_PMC_STANDBY, /* in standby mode */ + WLAN_PMC_REQUEST_ENTER_WOWL, /* requesting enter WOWL */ + WLAN_PMC_REQUEST_EXIT_WOWL, /* requesting exit WOWL */ + WLAN_PMC_WOWL /* Chip in WOWL mode */ +} wlan_ps_pmc_current_state_t; + +/* maps directly to ePmcPowerSavingMode */ +typedef enum { + WLAN_IDLE_MODE_POWER_SAVE, /* Idle Mode Power Save (IMPS) */ + WLAN_BEACON_MODE_POWER_SAVE, /* Beacon Mode Power Save (BMPS) */ + WLAN_SPATIAL_MULTIPLEX_POWER_SAVE, /* Spatial Multiplexing Power Save */ + WLAN_UAPSD_MODE_POWER_SAVE, /* Unscheduled Auto PS Delivery Mode */ + WLAN_STANDBY_MODE_POWER_SAVE, /* Standby Power Save Mode */ + WLAN_WOWL_MODE_POWER_SAVE /* Wake-on-Wireless Power Save Mode */ +} wlan_ps_enable_disable_ps_mode_t; + +typedef enum { + WLAN_D0, + WLAN_D1, + WLAN_D2, + WLAN_D3, + WLAN_D4 +} wlan_ps_winmob_d_power_state_t; + +typedef enum { + WLAN_WOW_ENTER_REQ = 0, + WLAN_WOW_EXIT_REQ = 1, + WLAN_WOW_DEL_PTRN_REQ = 2, + WLAN_WOW_WAKEUP = 3 +} wlan_ps_wow_evt_subtype_t; + +typedef enum { + WLAN_WOW_TYPE_NONE, + WLAN_WOW_TYPE_MAGIC_PKT_ONLY, + WLAN_WOW_TYPE_PTRN_BYTE_MATCH_ONLY, + WLAN_WOW_TYPE_MAGIC_PKT_PTRN_BYTE_MATCH, +} wlan_ps_wow_type_t; + +typedef enum { + WLAN_WOW_MAGIC_PKT_MATCH, + WLAN_WOW_PTRN_BYTE_MATCH +} wlan_ps_wos_wakeup_cause_t; + +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +#endif /* _WLAN_PS_WOW_DIAG_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/sme/src/common/sme_api.c b/drivers/staging/qcacld-3.0/core/sme/src/common/sme_api.c new file mode 100644 index 0000000000000000000000000000000000000000..fb5e97231b9c52d176c8e93a7cb5ff63fe08052b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/common/sme_api.c @@ -0,0 +1,16471 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: sme_api.c + * + * Definitions for SME APIs + */ + +/* Include Files */ +#include +#include +#include "sme_api.h" +#include "csr_inside_api.h" +#include "sme_inside.h" +#include "csr_internal.h" +#include "wma_types.h" +#include "wma_if.h" +#include "wma.h" +#include "wma_fips_api.h" +#include "wma_fw_state.h" +#include "qdf_trace.h" +#include "sme_trace.h" +#include "qdf_types.h" +#include "qdf_util.h" +#include "qdf_trace.h" +#include "cds_utils.h" +#include "sap_api.h" +#include "mac_trace.h" +#include "cds_regdomain.h" +#include "sme_power_save_api.h" +#include "wma.h" +#include "sch_api.h" +#include "sme_nan_datapath.h" +#include "csr_api.h" +#include "wlan_reg_services_api.h" +#include +#include "wlan_reg_ucfg_api.h" +#include "ol_txrx.h" +#include "wifi_pos_api.h" +#include "net/cfg80211.h" +#include +#include "wlan_mlme_public_struct.h" +#include "wlan_mlme_main.h" +#include "cfg_ucfg_api.h" +#include "wlan_fwol_ucfg_api.h" +#include "wlan_crypto_global_api.h" + +static QDF_STATUS init_sme_cmd_list(struct mac_context *mac); + +static void sme_disconnect_connected_sessions(struct mac_context *mac, + enum eSirMacReasonCodes reason); + +static QDF_STATUS sme_handle_generic_change_country_code(struct mac_context *mac, + void *msg_buf); + +static QDF_STATUS sme_process_nss_update_resp(struct mac_context *mac, uint8_t *msg); + +/* Channel Change Response Indication Handler */ +static QDF_STATUS sme_process_channel_change_resp(struct mac_context *mac, + uint16_t msg_type, void *msg_buf); + +static QDF_STATUS sme_stats_ext_event(struct mac_context *mac, + struct stats_ext_event *msg); + +static QDF_STATUS sme_fw_state_resp(struct mac_context *mac); + +/* Internal SME APIs */ +QDF_STATUS sme_acquire_global_lock(struct sme_context *sme) +{ + if (!sme) + return QDF_STATUS_E_INVAL; + + return qdf_mutex_acquire(&sme->sme_global_lock); +} + +QDF_STATUS sme_release_global_lock(struct sme_context *sme) +{ + if (!sme) + return QDF_STATUS_E_INVAL; + + return qdf_mutex_release(&sme->sme_global_lock); +} + +struct mac_context *sme_get_mac_context(void) +{ + struct mac_context *mac_ctx; + mac_handle_t mac_handle; + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (!mac_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_FATAL, + FL("invalid mac_handle")); + return NULL; + } + + mac_ctx = MAC_CONTEXT(mac_handle); + + return mac_ctx; +} + +/** + * sme_process_set_hw_mode_resp() - Process set HW mode response + * @mac: Global MAC pointer + * @msg: HW mode response + * + * Processes the HW mode response and invokes the HDD callback + * to process further + */ +static QDF_STATUS sme_process_set_hw_mode_resp(struct mac_context *mac, uint8_t *msg) +{ + tListElem *entry; + tSmeCmd *command = NULL; + bool found; + policy_mgr_pdev_set_hw_mode_cback callback = NULL; + struct sir_set_hw_mode_resp *param; + enum policy_mgr_conn_update_reason reason; + struct csr_roam_session *session; + uint32_t session_id; + + param = (struct sir_set_hw_mode_resp *)msg; + if (!param) { + sme_err("HW mode resp param is NULL"); + /* Not returning. Need to check if active command list + * needs to be freed + */ + } + + entry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (!entry) { + sme_err("No cmd found in active list"); + return QDF_STATUS_E_FAILURE; + } + + command = GET_BASE_ADDR(entry, tSmeCmd, Link); + if (!command) { + sme_err("Base address is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (e_sme_command_set_hw_mode != command->command) { + sme_err("Command mismatch!"); + return QDF_STATUS_E_FAILURE; + } + + callback = command->u.set_hw_mode_cmd.set_hw_mode_cb; + reason = command->u.set_hw_mode_cmd.reason; + session_id = command->u.set_hw_mode_cmd.session_id; + + sme_debug("reason: %d session: %d", + command->u.set_hw_mode_cmd.reason, + command->u.set_hw_mode_cmd.session_id); + + if (!callback) { + sme_err("Callback does not exist"); + goto end; + } + + if (!param) { + sme_err("Callback failed since HW mode params is NULL"); + goto end; + } + + /* Irrespective of the reason for which the hw mode change request + * was issued, the policy manager connection table needs to be updated + * with the new vdev-mac id mapping, tx/rx spatial streams etc., if the + * set hw mode was successful. + */ + callback(param->status, + param->cfgd_hw_mode_index, + param->num_vdev_mac_entries, + param->vdev_mac_map, + command->u.set_hw_mode_cmd.next_action, + command->u.set_hw_mode_cmd.reason, + command->u.set_hw_mode_cmd.session_id, + command->u.set_hw_mode_cmd.context); + if (!CSR_IS_SESSION_VALID(mac, session_id)) { + sme_err("session %d is invalid", session_id); + goto end; + } + session = CSR_GET_SESSION(mac, session_id); + if (reason == POLICY_MGR_UPDATE_REASON_HIDDEN_STA) { + /* In the case of hidden SSID, connection update + * (set hw mode) is done after the scan with reason + * code eCsrScanForSsid completes. The connect/failure + * needs to be handled after the response of set hw + * mode + */ + if (param->status == SET_HW_MODE_STATUS_OK || + param->status == SET_HW_MODE_STATUS_ALREADY) { + sme_debug("search for ssid success"); + csr_scan_handle_search_for_ssid(mac, + session_id); + } else { + sme_debug("search for ssid failure"); + csr_scan_handle_search_for_ssid_failure(mac, + session_id); + } + csr_saved_scan_cmd_free_fields(mac, session); + } + if (reason == POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_STA) { + sme_info("Continue channel switch for STA on vdev %d", + session_id); + csr_sta_continue_csa(mac, session_id); + } + + if (reason == POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_SAP) { + sme_info("Continue channel switch for SAP on vdev %d", + session_id); + csr_csa_restart(mac, session_id); + } + if (reason == POLICY_MGR_UPDATE_REASON_LFR2_ROAM) + csr_continue_lfr2_connect(mac, session_id); + +end: + found = csr_nonscan_active_ll_remove_entry(mac, entry, + LL_ACCESS_LOCK); + if (found) + /* Now put this command back on the avilable command list */ + csr_release_command(mac, command); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_process_hw_mode_trans_ind() - Process HW mode transition indication + * @mac: Global MAC pointer + * @msg: HW mode transition response + * + * Processes the HW mode transition indication and invoke the HDD callback + * to process further + */ +static QDF_STATUS sme_process_hw_mode_trans_ind(struct mac_context *mac, + uint8_t *msg) +{ + struct sir_hw_mode_trans_ind *param; + + param = (struct sir_hw_mode_trans_ind *)msg; + if (!param) { + sme_err("HW mode trans ind param is NULL"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_hw_mode_transition_cb(param->old_hw_mode_index, + param->new_hw_mode_index, + param->num_vdev_mac_entries, + param->vdev_mac_map, mac->psoc); + + return QDF_STATUS_SUCCESS; +} + +void sme_purge_pdev_all_ser_cmd_list(mac_handle_t mac_handle) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_ERROR(status)) + return; + + csr_purge_pdev_all_ser_cmd_list(mac_ctx); + sme_release_global_lock(&mac_ctx->sme); +} + +/** + * free_sme_cmds() - This function frees memory allocated for SME commands + * @mac_ctx: Pointer to Global MAC structure + * + * This function frees memory allocated for SME commands + * + * @Return: void + */ +static void free_sme_cmds(struct mac_context *mac_ctx) +{ + uint32_t idx; + + if (!mac_ctx->sme.sme_cmd_buf_addr) + return; + + for (idx = 0; idx < mac_ctx->sme.sme_cmd_count; idx++) + qdf_mem_free(mac_ctx->sme.sme_cmd_buf_addr[idx]); + + qdf_mem_free(mac_ctx->sme.sme_cmd_buf_addr); + mac_ctx->sme.sme_cmd_buf_addr = NULL; +} + +static QDF_STATUS init_sme_cmd_list(struct mac_context *mac) +{ + QDF_STATUS status; + tSmeCmd *cmd; + uint32_t cmd_idx; + uint32_t sme_cmd_ptr_ary_sz; + + mac->sme.sme_cmd_count = SME_TOTAL_COMMAND; + + status = csr_ll_open(&mac->sme.sme_cmd_freelist); + if (!QDF_IS_STATUS_SUCCESS(status)) + goto end; + + /* following pointer contains array of pointers for tSmeCmd* */ + sme_cmd_ptr_ary_sz = sizeof(void *) * mac->sme.sme_cmd_count; + mac->sme.sme_cmd_buf_addr = qdf_mem_malloc(sme_cmd_ptr_ary_sz); + if (!mac->sme.sme_cmd_buf_addr) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + + status = QDF_STATUS_SUCCESS; + for (cmd_idx = 0; cmd_idx < mac->sme.sme_cmd_count; cmd_idx++) { + /* + * Since total size of all commands together can be huge chunk + * of memory, allocate SME cmd individually. These SME CMDs are + * moved between pending and active queues. And these freeing of + * these queues just manipulates the list but does not actually + * frees SME CMD pointers. Hence store each SME CMD address in + * the array, sme.sme_cmd_buf_addr. This will later facilitate + * freeing up of all SME CMDs with just a for loop. + */ + cmd = qdf_mem_malloc(sizeof(*cmd)); + if (!cmd) { + status = QDF_STATUS_E_NOMEM; + free_sme_cmds(mac); + goto end; + } + mac->sme.sme_cmd_buf_addr[cmd_idx] = cmd; + csr_ll_insert_tail(&mac->sme.sme_cmd_freelist, + &cmd->Link, LL_ACCESS_LOCK); + } + +end: + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("Failed to initialize sme command list: %d", status); + + return status; +} + +void sme_release_command(struct mac_context *mac_ctx, tSmeCmd *sme_cmd) +{ + sme_cmd->command = eSmeNoCommand; + csr_ll_insert_tail(&mac_ctx->sme.sme_cmd_freelist, &sme_cmd->Link, + LL_ACCESS_LOCK); +} + +static QDF_STATUS free_sme_cmd_list(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + csr_ll_close(&mac->sme.sme_cmd_freelist); + + status = sme_acquire_global_lock(&mac->sme); + if (status != QDF_STATUS_SUCCESS) + goto done; + + free_sme_cmds(mac); + + status = sme_release_global_lock(&mac->sme); + if (status != QDF_STATUS_SUCCESS) + sme_err("Failed to release the lock status: %d", status); +done: + return status; +} + +static void dump_csr_command_info(struct mac_context *mac, tSmeCmd *pCmd) +{ + switch (pCmd->command) { + case eSmeCommandRoam: + sme_debug("roam command reason is %d", + pCmd->u.roamCmd.roamReason); + break; + + case eSmeCommandWmStatusChange: + sme_debug("WMStatusChange command type is %d", + pCmd->u.wmStatusChangeCmd.Type); + break; + + default: + sme_debug("default: Unhandled command %d", + pCmd->command); + break; + } +} + +tSmeCmd *sme_get_command_buffer(struct mac_context *mac) +{ + tSmeCmd *pRetCmd = NULL, *pTempCmd = NULL; + tListElem *pEntry; + static int sme_command_queue_full; + + pEntry = csr_ll_remove_head(&mac->sme.sme_cmd_freelist, LL_ACCESS_LOCK); + + /* If we can get another MS Msg buffer, then we are ok. Just + * link the entry onto the linked list. (We are using the + * linked list to keep track of the message buffers). + */ + if (pEntry) { + pRetCmd = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + /* reset when free list is available */ + sme_command_queue_full = 0; + } else { + int idx = 1; + + /* Cannot change pRetCmd here since it needs to return later. */ + pEntry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (pEntry) + pTempCmd = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + + sme_err("Out of command buffer.... command (0x%X) stuck", + (pTempCmd) ? pTempCmd->command : eSmeNoCommand); + if (pTempCmd) { + if (eSmeCsrCommandMask & pTempCmd->command) + /* CSR command is stuck. See what the reason + * code is for that command + */ + dump_csr_command_info(mac, pTempCmd); + } /* if(pTempCmd) */ + + /* dump what is in the pending queue */ + pEntry = + csr_nonscan_pending_ll_peek_head(mac, + LL_ACCESS_NOLOCK); + while (pEntry && !sme_command_queue_full) { + pTempCmd = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + /* Print only 1st five commands from pending queue. */ + if (idx <= 5) + sme_err("Out of command buffer.... SME pending command #%d (0x%X)", + idx, pTempCmd->command); + idx++; + if (eSmeCsrCommandMask & pTempCmd->command) + /* CSR command is stuck. See what the reason + * code is for that command + */ + dump_csr_command_info(mac, pTempCmd); + pEntry = csr_nonscan_pending_ll_next(mac, pEntry, + LL_ACCESS_NOLOCK); + } + + if (mac->mlme_cfg->gen.fatal_event_trigger) + cds_flush_logs(WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_REASON_SME_OUT_OF_CMD_BUF, + false, + mac->mlme_cfg->gen.self_recovery); + else + cds_trigger_recovery(QDF_GET_MSG_BUFF_FAILURE); + } + + /* memset to zero */ + if (pRetCmd) { + qdf_mem_zero((uint8_t *)&pRetCmd->command, + sizeof(pRetCmd->command)); + qdf_mem_zero((uint8_t *)&pRetCmd->vdev_id, + sizeof(pRetCmd->vdev_id)); + qdf_mem_zero((uint8_t *)&pRetCmd->u, sizeof(pRetCmd->u)); + } + + return pRetCmd; +} + +/** + * sme_ser_handle_active_cmd() - handle command activation callback from + * new serialization module + * @cmd: pointer to new serialization command + * + * This API is to handle command activation callback from new serialization + * callback + * + * Return: QDF_STATUS_SUCCESS + */ +static +QDF_STATUS sme_ser_handle_active_cmd(struct wlan_serialization_command *cmd) +{ + tSmeCmd *sme_cmd; + mac_handle_t mac_handle; + struct mac_context *mac_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool do_continue; + + if (!cmd) { + sme_err("No serialization command found"); + return QDF_STATUS_E_FAILURE; + } + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (mac_handle) { + mac_ctx = MAC_CONTEXT(mac_handle); + } else { + sme_err("No mac_handle found"); + return QDF_STATUS_E_FAILURE; + } + sme_cmd = cmd->umac_cmd; + if (!sme_cmd) { + sme_err("No SME command found"); + return QDF_STATUS_E_FAILURE; + } + + switch (sme_cmd->command) { + case eSmeCommandRoam: + status = csr_roam_process_command(mac_ctx, sme_cmd); + break; + case eSmeCommandWmStatusChange: + csr_roam_process_wm_status_change_command(mac_ctx, + sme_cmd); + break; + case eSmeCommandGetdisconnectStats: + csr_roam_process_get_disconnect_stats_command(mac_ctx, + sme_cmd); + break; + + case eSmeCommandAddTs: + case eSmeCommandDelTs: +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + do_continue = qos_process_command(mac_ctx, sme_cmd); + if (do_continue) + status = QDF_STATUS_E_FAILURE; +#endif + break; + case e_sme_command_set_hw_mode: + csr_process_set_hw_mode(mac_ctx, sme_cmd); + break; + case e_sme_command_nss_update: + csr_process_nss_update_req(mac_ctx, sme_cmd); + break; + case e_sme_command_set_dual_mac_config: + csr_process_set_dual_mac_config(mac_ctx, sme_cmd); + break; + case e_sme_command_set_antenna_mode: + csr_process_set_antenna_mode(mac_ctx, sme_cmd); + break; + default: + /* something is wrong */ + sme_err("unknown command %d", sme_cmd->command); + status = QDF_STATUS_E_FAILURE; + break; + } + return status; +} + +QDF_STATUS sme_ser_cmd_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason) +{ + mac_handle_t mac_handle; + struct mac_context *mac_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSmeCmd *sme_cmd; + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (mac_handle) { + mac_ctx = MAC_CONTEXT(mac_handle); + } else { + sme_err("mac_handle is null"); + return QDF_STATUS_E_FAILURE; + } + /* + * Do not acquire lock here as sme global lock is already acquired in + * caller or MC thread context + */ + if (!cmd) { + sme_err("serialization command is null"); + return QDF_STATUS_E_FAILURE; + } + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + status = sme_ser_handle_active_cmd(cmd); + break; + case WLAN_SER_CB_CANCEL_CMD: + break; + case WLAN_SER_CB_RELEASE_MEM_CMD: + if (cmd->vdev) + wlan_objmgr_vdev_release_ref(cmd->vdev, + WLAN_LEGACY_SME_ID); + sme_cmd = cmd->umac_cmd; + csr_release_command_buffer(mac_ctx, sme_cmd); + break; + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + break; + default: + sme_debug("unknown reason code"); + return QDF_STATUS_E_FAILURE; + } + return status; +} + +#ifdef WLAN_FEATURE_MEMDUMP_ENABLE +/** + * sme_get_sessionid_from_activelist() - gets vdev_id + * @mac: mac context + * + * This function is used to get session id from sme command + * active list + * + * Return: returns vdev_id + */ +static uint32_t sme_get_sessionid_from_activelist(struct mac_context *mac) +{ + tListElem *entry; + tSmeCmd *command; + uint32_t vdev_id = WLAN_UMAC_VDEV_ID_MAX; + + entry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (entry) { + command = GET_BASE_ADDR(entry, tSmeCmd, Link); + vdev_id = command->vdev_id; + } + + return vdev_id; +} + +/** + * sme_state_info_dump() - prints state information of sme layer + * @buf: buffer pointer + * @size: size of buffer to be filled + * + * This function is used to dump state information of sme layer + * + * Return: None + */ +static void sme_state_info_dump(char **buf_ptr, uint16_t *size) +{ + uint32_t session_id, active_session_id; + mac_handle_t mac_handle; + struct mac_context *mac; + uint16_t len = 0; + char *buf = *buf_ptr; + eCsrConnectState connect_state; + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (!mac_handle) { + QDF_ASSERT(0); + return; + } + + mac = MAC_CONTEXT(mac_handle); + + active_session_id = sme_get_sessionid_from_activelist(mac); + if (active_session_id != WLAN_UMAC_VDEV_ID_MAX) { + len += qdf_scnprintf(buf + len, *size - len, + "\n active command sessionid %d", active_session_id); + } + + for (session_id = 0; session_id < WLAN_MAX_VDEVS; session_id++) { + if (CSR_IS_SESSION_VALID(mac, session_id)) { + connect_state = + mac->roam.roamSession[session_id].connectState; + if ((eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED == + connect_state) + || (eCSR_ASSOC_STATE_TYPE_INFRA_CONNECTED == + connect_state)) { + len += qdf_scnprintf(buf + len, *size - len, + "\n NeighborRoamState: %d", + mac->roam.neighborRoamInfo[session_id]. + neighborRoamState); + len += qdf_scnprintf(buf + len, *size - len, + "\n RoamState: %d", mac->roam. + curState[session_id]); + len += qdf_scnprintf(buf + len, *size - len, + "\n RoamSubState: %d", mac->roam. + curSubState[session_id]); + len += qdf_scnprintf(buf + len, *size - len, + "\n ConnectState: %d", + connect_state); + } + } + } + + *size -= len; + *buf_ptr += len; +} + +/** + * sme_register_debug_callback() - registration function sme layer + * to print sme state information + * + * Return: None + */ +static void sme_register_debug_callback(void) +{ + qdf_register_debug_callback(QDF_MODULE_ID_SME, &sme_state_info_dump); +} +#else /* WLAN_FEATURE_MEMDUMP_ENABLE */ +static void sme_register_debug_callback(void) +{ +} +#endif /* WLAN_FEATURE_MEMDUMP_ENABLE */ + +#ifdef WLAN_POWER_DEBUG +static void sme_power_debug_stats_cb(struct mac_context *mac, + struct power_stats_response *response) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (mac->sme.power_stats_resp_callback) + mac->sme.power_stats_resp_callback( + response, + mac->sme.power_debug_stats_context); + else + sme_err("Null hdd cb"); + mac->sme.power_stats_resp_callback = NULL; + mac->sme.power_debug_stats_context = NULL; + sme_release_global_lock(&mac->sme); + } +} + +static void sme_register_power_debug_stats_cb(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac->sme); + + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.sme_power_debug_stats_callback = + sme_power_debug_stats_cb; + sme_release_global_lock(&mac->sme); + } +} + +static void sme_unregister_power_debug_stats_cb(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.sme_power_debug_stats_callback = NULL; + sme_release_global_lock(&mac->sme); + } +} +#else +static inline void sme_register_power_debug_stats_cb(struct mac_context *mac) +{ +} + +static inline void sme_unregister_power_debug_stats_cb(struct mac_context *mac) +{ +} +#endif + +/* Global APIs */ + +/** + * sme_open() - Initialze all SME modules and put them at idle state + * @mac_handle: The handle returned by mac_open + * + * The function initializes each module inside SME, PMC, CSR, etc. Upon + * successfully return, all modules are at idle state ready to start. + * smeOpen must be called before any other SME APIs can be involved. + * smeOpen must be called after mac_open. + * + * Return: QDF_STATUS_SUCCESS - SME is successfully initialized. + * Other status means SME is failed to be initialized + */ +QDF_STATUS sme_open(mac_handle_t mac_handle) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + mac->sme.state = SME_STATE_STOP; + mac->sme.curr_device_mode = QDF_STA_MODE; + if (!QDF_IS_STATUS_SUCCESS(qdf_mutex_create( + &mac->sme.sme_global_lock))) { + sme_err("Init lock failed"); + return QDF_STATUS_E_FAILURE; + } + status = csr_open(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("csr_open failed, status: %d", status); + return status; + } + + status = sme_ps_open(mac_handle); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("sme_ps_open failed with status: %d", status); + return status; + } + +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + status = sme_qos_open(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Qos open, status: %d", status); + return status; + } +#endif + status = init_sme_cmd_list(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + + status = rrm_open(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("rrm_open failed, status: %d", status); + return status; + } + sme_trace_init(mac); + sme_register_debug_callback(); + sme_register_power_debug_stats_cb(mac); + + return status; +} + +/* + * sme_init_chan_list, triggers channel setup based on country code. + */ +QDF_STATUS sme_init_chan_list(mac_handle_t mac_handle, uint8_t *alpha2, + enum country_src cc_src) +{ + struct mac_context *pmac = MAC_CONTEXT(mac_handle); + + if ((cc_src == SOURCE_USERSPACE) && + (pmac->mlme_cfg->sap_cfg.country_code_priority)) { + pmac->mlme_cfg->gen.enabled_11d = false; + } + + return csr_init_chan_list(pmac, alpha2); +} + +/* + * sme_set11dinfo() - Set the 11d information about valid channels + * and there power using information from nvRAM + * This function is called only for AP. + * + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open. + * pSmeConfigParams - a pointer to a caller allocated object of + * struct sme_config_params. + * + * Return QDF_STATUS_SUCCESS - SME update the config parameters successfully. + * + * Other status means SME is failed to update the config parameters. + */ + +QDF_STATUS sme_set11dinfo(mac_handle_t mac_handle, + struct sme_config_params *pSmeConfigParams) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_MSG_SET_11DINFO, NO_SESSION, 0)); + if (!pSmeConfigParams) { + sme_err("SME config params empty"); + return status; + } + + status = csr_set_channels(mac_ctx, &pSmeConfigParams->csr_config); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("csr_set_channels failed with status: %d", status); + + return status; +} + +/** + * sme_update_fine_time_measurement_capab() - Update the FTM capabitlies from + * incoming val + * @mac_handle: Opaque handle to the global MAC context + * @val: New FTM capability value + * + * Return: None + */ +void sme_update_fine_time_measurement_capab(mac_handle_t mac_handle, + uint8_t session_id, + uint32_t val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + + ucfg_wifi_pos_set_ftm_cap(mac_ctx->psoc, val); + + if (!val) { + mac_ctx->rrm.rrmPEContext.rrmEnabledCaps.fine_time_meas_rpt = 0; + ((tpRRMCaps)mac_ctx->rrm.rrmConfig. + rm_capability)->fine_time_meas_rpt = 0; + } else { + mac_ctx->rrm.rrmPEContext.rrmEnabledCaps.fine_time_meas_rpt = 1; + ((tpRRMCaps)mac_ctx->rrm.rrmConfig. + rm_capability)->fine_time_meas_rpt = 1; + } + + /* Inform this RRM IE change to FW */ + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + csr_roam_update_cfg(mac_ctx, session_id, + REASON_CONNECT_IES_CHANGED); + sme_release_global_lock(&mac_ctx->sme); + } +} + +void sme_update_nud_config(mac_handle_t mac_handle, uint8_t nud_fail_behavior) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + mac->nud_fail_behaviour = nud_fail_behavior; +} + +/** + * sme_update_neighbor_report_config() - Update CSR config for 11k params + * @mac_handle: Pointer to MAC context + * @csr_config: Pointer to CSR config data structure + * + * Return: None + */ +static void +sme_update_neighbor_report_config(struct mac_context *mac, + struct csr_config_params *csr_config) +{ + struct wlan_fwol_neighbor_report_cfg fwol_neighbor_report_cfg = {0}; + QDF_STATUS status; + + status = ucfg_fwol_get_neighbor_report_cfg(mac->psoc, + &fwol_neighbor_report_cfg); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("Using defaults for 11K offload params: Error: %d", + status); + + csr_config->offload_11k_enable_bitmask = + fwol_neighbor_report_cfg.enable_bitmask; + csr_config->neighbor_report_offload.params_bitmask = + fwol_neighbor_report_cfg.params_bitmask; + csr_config->neighbor_report_offload.time_offset = + fwol_neighbor_report_cfg.time_offset; + csr_config->neighbor_report_offload.low_rssi_offset = + fwol_neighbor_report_cfg.low_rssi_offset; + csr_config->neighbor_report_offload.bmiss_count_trigger = + fwol_neighbor_report_cfg.bmiss_count_trigger; + csr_config->neighbor_report_offload.per_threshold_offset = + fwol_neighbor_report_cfg.per_threshold_offset; + csr_config->neighbor_report_offload.neighbor_report_cache_timeout = + fwol_neighbor_report_cfg.cache_timeout; + csr_config->neighbor_report_offload.max_neighbor_report_req_cap = + fwol_neighbor_report_cfg.max_req_cap; +} + +/* + * sme_update_config() - Change configurations for all SME moduels + * The function updates some configuration for modules in SME, CSR, etc + * during SMEs close open sequence. + * Modules inside SME apply the new configuration at the next transaction. + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open. + * pSmeConfigParams - a pointer to a caller allocated object of + * struct sme_config_params. + * Return QDF_STATUS_SUCCESS - SME update the config parameters successfully. + * Other status means SME is failed to update the config parameters. + */ +QDF_STATUS sme_update_config(mac_handle_t mac_handle, + struct sme_config_params *pSmeConfigParams) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_MSG_UPDATE_CONFIG, NO_SESSION, + 0)); + if (!pSmeConfigParams) { + sme_err("SME config params empty"); + return status; + } + sme_update_neighbor_report_config(mac, &pSmeConfigParams->csr_config); + status = csr_change_default_config_param(mac, &pSmeConfigParams-> + csr_config); + + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("csr_change_default_config_param failed status: %d", + status); + + /* For SOC, CFG is set before start We don't want to apply global CFG + * in connect state because that may cause some side affect + */ + if (csr_is_all_session_disconnected(mac)) + csr_set_global_cfgs(mac); + + return status; +} + +/** + * sme_update_roam_params() - Store/Update the roaming params + * @mac_handle: Opaque handle to the global MAC context + * @session_id: SME Session ID + * @roam_params_src: The source buffer to copy + * @update_param: Type of parameter to be updated + * + * Return: Return the status of the updation. + */ +QDF_STATUS sme_update_roam_params(mac_handle_t mac_handle, + uint8_t session_id, + struct roam_ext_params *roam_params_src, + int update_param) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct roam_ext_params *roam_params_dst; + QDF_STATUS status; + uint8_t i; + + roam_params_dst = &mac_ctx->roam.configParam.roam_params; + switch (update_param) { + case REASON_ROAM_EXT_SCAN_PARAMS_CHANGED: + mac_ctx->mlme_cfg->lfr.rssi_boost_threshold_5g = + roam_params_src->raise_rssi_thresh_5g; + mac_ctx->mlme_cfg->lfr.rssi_penalize_threshold_5g = + roam_params_src->drop_rssi_thresh_5g; + mac_ctx->mlme_cfg->lfr.rssi_boost_factor_5g = + roam_params_src->raise_factor_5g; + mac_ctx->mlme_cfg->lfr.rssi_penalize_factor_5g = + roam_params_src->drop_factor_5g; + mac_ctx->mlme_cfg->lfr.max_rssi_boost_5g = + roam_params_src->max_raise_rssi_5g; + mac_ctx->mlme_cfg->lfr.max_rssi_penalize_5g = + roam_params_src->max_drop_rssi_5g; + roam_params_dst->alert_rssi_threshold = + roam_params_src->alert_rssi_threshold; + mac_ctx->mlme_cfg->lfr.enable_5g_band_pref = true; + break; + case REASON_ROAM_SET_SSID_ALLOWED: + qdf_mem_zero(&roam_params_dst->ssid_allowed_list, + sizeof(tSirMacSSid) * MAX_SSID_ALLOWED_LIST); + roam_params_dst->num_ssid_allowed_list = + roam_params_src->num_ssid_allowed_list; + for (i = 0; i < roam_params_dst->num_ssid_allowed_list; i++) { + roam_params_dst->ssid_allowed_list[i].length = + roam_params_src->ssid_allowed_list[i].length; + qdf_mem_copy(roam_params_dst->ssid_allowed_list[i].ssId, + roam_params_src->ssid_allowed_list[i].ssId, + roam_params_dst->ssid_allowed_list[i].length); + } + break; + case REASON_ROAM_SET_FAVORED_BSSID: + qdf_mem_zero(&roam_params_dst->bssid_favored, + sizeof(tSirMacAddr) * MAX_BSSID_FAVORED); + roam_params_dst->num_bssid_favored = + roam_params_src->num_bssid_favored; + for (i = 0; i < roam_params_dst->num_bssid_favored; i++) { + qdf_mem_copy(&roam_params_dst->bssid_favored[i], + &roam_params_src->bssid_favored[i], + sizeof(tSirMacAddr)); + roam_params_dst->bssid_favored_factor[i] = + roam_params_src->bssid_favored_factor[i]; + } + break; + case REASON_ROAM_SET_BLACKLIST_BSSID: + qdf_mem_zero(&roam_params_dst->bssid_avoid_list, + QDF_MAC_ADDR_SIZE * MAX_BSSID_AVOID_LIST); + roam_params_dst->num_bssid_avoid_list = + roam_params_src->num_bssid_avoid_list; + for (i = 0; i < roam_params_dst->num_bssid_avoid_list; i++) { + qdf_copy_macaddr(&roam_params_dst->bssid_avoid_list[i], + &roam_params_src->bssid_avoid_list[i]); + } + break; + case REASON_ROAM_GOOD_RSSI_CHANGED: + roam_params_dst->good_rssi_roam = + roam_params_src->good_rssi_roam; + break; + default: + break; + } + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + csr_roam_update_cfg(mac_ctx, session_id, update_param); + sme_release_global_lock(&mac_ctx->sme); + } + + return 0; +} + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + +/** + * sme_process_ready_to_ext_wow() - inform ready to ExtWoW indication. + * @mac: Global MAC context + * @indication: ready to Ext WoW indication from lower layer + * + * On getting ready to Ext WoW indication, this function calls callback + * registered (HDD callback) with SME to inform ready to ExtWoW indication. + * + * Return: None + */ +static void sme_process_ready_to_ext_wow(struct mac_context *mac, + tpSirReadyToExtWoWInd indication) +{ + if (!mac) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_FATAL, + "%s: mac is null", __func__); + return; + } + + if (mac->readyToExtWoWCallback) { + mac->readyToExtWoWCallback(mac->readyToExtWoWContext, + indication->status); + mac->readyToExtWoWCallback = NULL; + mac->readyToExtWoWContext = NULL; + } + +} +#endif + +/* + * sme_hdd_ready_ind() - SME sends eWNI_SME_SYS_READY_IND to PE to inform + * that the NIC is ready tio run. + * The function is called by HDD at the end of initialization stage so PE/HAL + * can enable the NIC to running state. + * This is a synchronous call + * + * @mac_handle - The handle returned by mac_open. + * Return QDF_STATUS_SUCCESS - eWNI_SME_SYS_READY_IND is sent to PE + * successfully. + * Other status means SME failed to send the message to PE. + */ +QDF_STATUS sme_hdd_ready_ind(mac_handle_t mac_handle) +{ + struct sme_ready_req *msg; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_MSG_HDDREADYIND, NO_SESSION, 0)); + do { + + msg = qdf_mem_malloc(sizeof(*msg)); + if (!msg) + return QDF_STATUS_E_NOMEM; + + msg->messageType = eWNI_SME_SYS_READY_IND; + msg->length = sizeof(*msg); + msg->csr_roam_synch_cb = csr_roam_synch_callback; + msg->sme_msg_cb = sme_process_msg_callback; + msg->stop_roaming_cb = sme_stop_roaming; + msg->csr_roam_auth_event_handle_cb = + csr_roam_auth_offload_callback; + msg->csr_roam_pmkid_req_cb = csr_roam_pmkid_req_callback; + + status = u_mac_post_ctrl_msg(mac_handle, (tSirMbMsg *)msg); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("u_mac_post_ctrl_msg failed to send eWNI_SME_SYS_READY_IND"); + break; + } + + status = csr_ready(mac); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("csr_ready failed with status: %d", status); + break; + } + + mac->sme.state = SME_STATE_READY; + } while (0); + + return status; +} + +#ifdef WLAN_BCN_RECV_FEATURE +QDF_STATUS +sme_register_bcn_report_pe_cb(mac_handle_t mac_handle, beacon_report_cb cb) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + msg.type = WNI_SME_REGISTER_BCN_REPORT_SEND_CB; + msg.callback = cb; + + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) + sme_err("Failed to post message to LIM"); + + return status; +} +#endif + +QDF_STATUS sme_get_valid_channels(uint32_t *ch_freq_list, uint32_t *list_len) +{ + struct mac_context *mac_ctx = sme_get_mac_context(); + uint32_t num_valid_chan; + uint8_t i; + + if (!mac_ctx) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid MAC context")); + *list_len = 0; + return QDF_STATUS_E_FAILURE; + } + + num_valid_chan = mac_ctx->mlme_cfg->reg.valid_channel_list_num; + + if (num_valid_chan > *list_len) { + sme_err("list len size %d less than expected %d", *list_len, + num_valid_chan); + num_valid_chan = *list_len; + } + *list_len = num_valid_chan; + for (i = 0; i < *list_len; i++) { + ch_freq_list[i] = + mac_ctx->mlme_cfg->reg.valid_channel_freq_list[i]; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_CONV_SPECTRAL_ENABLE +static QDF_STATUS sme_register_spectral_cb(struct mac_context *mac_ctx) +{ + struct spectral_legacy_cbacks spectral_cb = {0}; + QDF_STATUS status; + + spectral_cb.vdev_get_chan_freq = sme_get_oper_chan_freq; + spectral_cb.vdev_get_ch_width = sme_get_oper_ch_width; + spectral_cb.vdev_get_sec20chan_freq_mhz = sme_get_sec20chan_freq_mhz; + status = spectral_register_legacy_cb(mac_ctx->psoc, &spectral_cb); + + return status; +} +#else +static QDF_STATUS sme_register_spectral_cb(struct mac_context *mac_ctx) +{ + return QDF_STATUS_SUCCESS; +} +#endif +/* + * sme_start() - Put all SME modules at ready state. + * The function starts each module in SME, PMC, CSR, etc. . Upon + * successfully return, all modules are ready to run. + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open. + * Return QDF_STATUS_SUCCESS - SME is ready. + * Other status means SME is failed to start + */ +QDF_STATUS sme_start(mac_handle_t mac_handle) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct policy_mgr_sme_cbacks sme_cbacks; + + do { + status = csr_start(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("csr_start failed status: %d", status); + break; + } + sme_cbacks.sme_get_nss_for_vdev = sme_get_vdev_type_nss; + sme_cbacks.sme_nss_update_request = sme_nss_update_request; + sme_cbacks.sme_pdev_set_hw_mode = sme_pdev_set_hw_mode; + sme_cbacks.sme_pdev_set_pcl = sme_pdev_set_pcl; + sme_cbacks.sme_soc_set_dual_mac_config = + sme_soc_set_dual_mac_config; + sme_cbacks.sme_change_mcc_beacon_interval = + sme_change_mcc_beacon_interval; + sme_cbacks.sme_get_ap_channel_from_scan = + sme_get_ap_channel_from_scan; + sme_cbacks.sme_scan_result_purge = sme_scan_result_purge; + status = policy_mgr_register_sme_cb(mac->psoc, &sme_cbacks); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to register sme cb with Policy Manager: %d", + status); + break; + } + sme_register_spectral_cb(mac); + mac->sme.state = SME_STATE_START; + + /* START RRM */ + status = rrm_start(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to start RRM"); + break; + } + } while (0); + return status; +} + +static QDF_STATUS dfs_msg_processor(struct mac_context *mac, + struct scheduler_msg *msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_info *roam_info; + tSirSmeCSAIeTxCompleteRsp *csa_ie_tx_complete_rsp; + uint32_t session_id = 0; + eRoamCmdStatus roam_status; + eCsrRoamResult roam_result; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + + switch (msg->type) { + case eWNI_SME_DFS_RADAR_FOUND: + { + session_id = policy_mgr_get_dfs_beaconing_session_id(mac->psoc); + if (!CSR_IS_SESSION_VALID(mac, session_id)) { + sme_err("CSR session not valid: %d", session_id); + qdf_mem_free(roam_info); + return QDF_STATUS_E_FAILURE; + } + roam_status = eCSR_ROAM_DFS_RADAR_IND; + roam_result = eCSR_ROAM_RESULT_DFS_RADAR_FOUND_IND; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "sapdfs: Radar indication event occurred"); + break; + } + case eWNI_SME_DFS_CSAIE_TX_COMPLETE_IND: + { + csa_ie_tx_complete_rsp = + (tSirSmeCSAIeTxCompleteRsp *) msg->bodyptr; + if (!csa_ie_tx_complete_rsp) { + sme_err("eWNI_SME_DFS_CSAIE_TX_COMPLETE_IND null msg"); + qdf_mem_free(roam_info); + return QDF_STATUS_E_FAILURE; + } + session_id = csa_ie_tx_complete_rsp->sessionId; + roam_status = eCSR_ROAM_DFS_CHAN_SW_NOTIFY; + roam_result = eCSR_ROAM_RESULT_DFS_CHANSW_UPDATE_SUCCESS; + break; + } + case eWNI_SME_DFS_CAC_COMPLETE: + { + session_id = msg->bodyval; + roam_status = eCSR_ROAM_CAC_COMPLETE_IND; + roam_result = eCSR_ROAM_RESULT_CAC_END_IND; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "sapdfs: Received eWNI_SME_DFS_CAC_COMPLETE vdevid%d", + session_id); + break; + } + case eWNI_SME_CSA_RESTART_RSP: + { + session_id = msg->bodyval; + roam_status = 0; + roam_result = eCSR_ROAM_RESULT_CSA_RESTART_RSP; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "sapdfs: Received eCSR_ROAM_RESULT_DFS_CHANSW_UPDATE_REQ vdevid%d", + session_id); + break; + } + default: + { + sme_err("Invalid DFS message: 0x%x", msg->type); + qdf_mem_free(roam_info); + status = QDF_STATUS_E_FAILURE; + return status; + } + } + + /* Indicate Radar Event to SAP */ + csr_roam_call_callback(mac, session_id, roam_info, 0, + roam_status, roam_result); + qdf_mem_free(roam_info); + return status; +} + + +#ifdef WLAN_FEATURE_11W +/* + * Handle the unprotected management frame indication from LIM and + * forward it to HDD. + */ +static QDF_STATUS +sme_unprotected_mgmt_frm_ind(struct mac_context *mac, + tpSirSmeUnprotMgmtFrameInd pSmeMgmtFrm) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_info *roam_info; + uint32_t SessionId = pSmeMgmtFrm->sessionId; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + + roam_info->nFrameLength = pSmeMgmtFrm->frameLen; + roam_info->pbFrames = pSmeMgmtFrm->frameBuf; + roam_info->frameType = pSmeMgmtFrm->frameType; + + /* forward the mgmt frame to HDD */ + csr_roam_call_callback(mac, SessionId, roam_info, 0, + eCSR_ROAM_UNPROT_MGMT_FRAME_IND, 0); + + qdf_mem_free(roam_info); + + return status; +} +#endif + +QDF_STATUS sme_update_new_channel_event(mac_handle_t mac_handle, + uint8_t session_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_info *roamInfo; + eRoamCmdStatus roamStatus; + eCsrRoamResult roamResult; + + roamInfo = qdf_mem_malloc(sizeof(*roamInfo)); + if (!roamInfo) + return QDF_STATUS_E_FAILURE; + + roamInfo->dfs_event.sessionId = session_id; + roamStatus = eCSR_ROAM_CHANNEL_COMPLETE_IND; + roamResult = eCSR_ROAM_RESULT_DFS_RADAR_FOUND_IND; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "sapdfs: Updated new channel event"); + + /* Indicate channel Event to SAP */ + csr_roam_call_callback(mac, session_id, roamInfo, 0, + roamStatus, roamResult); + + qdf_mem_free(roamInfo); + return status; +} + + +/** + * sme_extended_change_channel_ind()- function to indicate ECSA + * action frame is received in lim to SAP + * @mac_ctx: pointer to global mac structure + * @msg_buf: contain new channel and session id. + * + * This function is called to post ECSA action frame + * receive event to SAP. + * + * Return: success if msg indicated to SAP else return failure + */ +static QDF_STATUS sme_extended_change_channel_ind(struct mac_context *mac_ctx, + void *msg_buf) +{ + struct sir_sme_ext_cng_chan_ind *ext_chan_ind; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t session_id = 0; + struct csr_roam_info *roam_info; + eRoamCmdStatus roam_status; + eCsrRoamResult roam_result; + + ext_chan_ind = msg_buf; + if (!ext_chan_ind) { + sme_err("ext_chan_ind is NULL"); + return QDF_STATUS_E_FAILURE; + } + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + session_id = ext_chan_ind->session_id; + roam_info->target_chan_freq = ext_chan_ind->new_chan_freq; + roam_status = eCSR_ROAM_EXT_CHG_CHNL_IND; + roam_result = eCSR_ROAM_EXT_CHG_CHNL_UPDATE_IND; + sme_debug("sapdfs: Received eWNI_SME_EXT_CHANGE_CHANNEL_IND for session id [%d]", + session_id); + + /* Indicate Ext Channel Change event to SAP */ + csr_roam_call_callback(mac_ctx, session_id, roam_info, 0, + roam_status, roam_result); + qdf_mem_free(roam_info); + return status; +} + +#ifdef FEATURE_WLAN_ESE +/** + * sme_update_is_ese_feature_enabled() - enable/disable ESE support at runtime + * @mac_handle: Opaque handle to the global MAC context + * @sessionId: session id + * @isEseIniFeatureEnabled: ese ini enabled + * + * It is used at in the REG_DYNAMIC_VARIABLE macro definition of + * isEseIniFeatureEnabled. This is a synchronous call + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_update_is_ese_feature_enabled(mac_handle_t mac_handle, + uint8_t sessionId, const bool isEseIniFeatureEnabled) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + + if (mac->mlme_cfg->lfr.ese_enabled == + isEseIniFeatureEnabled) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: ESE Mode is already enabled or disabled, nothing to do (returning) old(%d) new(%d)", + __func__, + mac->mlme_cfg->lfr.ese_enabled, + isEseIniFeatureEnabled); + return QDF_STATUS_SUCCESS; + } + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: EseEnabled is changed from %d to %d", __func__, + mac->mlme_cfg->lfr.ese_enabled, + isEseIniFeatureEnabled); + mac->mlme_cfg->lfr.ese_enabled = isEseIniFeatureEnabled; + csr_neighbor_roam_update_fast_roaming_enabled( + mac, sessionId, isEseIniFeatureEnabled); + + if (true == isEseIniFeatureEnabled) + mac->mlme_cfg->lfr.fast_transition_enabled = true; + + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) { + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + csr_roam_update_cfg(mac, sessionId, + REASON_ESE_INI_CFG_CHANGED); + sme_release_global_lock(&mac->sme); + } else { + return status; + } + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_set_plm_request(mac_handle_t mac_handle, + struct plm_req_params *req) +{ + QDF_STATUS status; + bool ret = false; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + uint32_t ch_freq_list[CFG_VALID_CHANNEL_LIST_LEN] = { 0 }; + uint8_t count, valid_count = 0; + struct scheduler_msg msg = {0}; + struct csr_roam_session *session; + struct plm_req_params *body; + uint32_t ch_freq; + + if (!req) + return QDF_STATUS_E_FAILURE; + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + + session = CSR_GET_SESSION(mac, req->vdev_id); + if (!session) { + sme_err("session %d not found", req->vdev_id); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + if (!session->sessionActive) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid Sessionid")); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + /* per contract must make a copy of the params when messaging */ + body = qdf_mem_malloc(sizeof(*body)); + + if (!body) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + *body = *req; + + if (!body->enable) + goto send_plm_start; + /* validating channel numbers */ + for (count = 0; count < body->plm_num_ch; count++) { + ch_freq = body->plm_ch_freq_list[count]; + ret = csr_is_supported_channel(mac, ch_freq); + if (!ret) { + /* Not supported, ignore the channel */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Unsupported freq %d ignored for PLM"), + ch_freq); + continue; + } + + if (ch_freq > 2477) { + enum channel_state state = + wlan_reg_get_channel_state_for_freq( + mac->pdev, ch_freq); + + if (state == CHANNEL_STATE_DFS) { + /* DFS channel is provided, no PLM bursts can be + * transmitted. Ignoring these channels. + */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("DFS channel %d ignored for PLM"), + ch_freq); + continue; + } + } + ch_freq_list[valid_count++] = ch_freq; + } /* End of for () */ + + /* Copying back the valid channel list to plm struct */ + qdf_mem_zero(body->plm_ch_freq_list, body->plm_num_ch); + if (valid_count) + qdf_mem_copy(body->plm_ch_freq_list, ch_freq_list, valid_count); + /* All are invalid channels, FW need to send the PLM + * report with "incapable" bit set. + */ + body->plm_num_ch = valid_count; + +send_plm_start: + /* PLM START */ + msg.type = WMA_SET_PLM_REQ; + msg.reserved = 0; + msg.bodyptr = body; + + if (!QDF_IS_STATUS_SUCCESS(scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Not able to post WMA_SET_PLM_REQ to WMA")); + sme_release_global_lock(&mac->sme); + qdf_mem_free(body); + return QDF_STATUS_E_FAILURE; + } + + sme_release_global_lock(&mac->sme); + return QDF_STATUS_SUCCESS; +} + +/** + * sme_tsm_ie_ind() - sme tsm ie indication + * @mac: Global mac context + * @pSmeTsmIeInd: Pointer to tsm ie indication + * + * Handle the tsm ie indication from LIM and forward it to HDD. + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS sme_tsm_ie_ind(struct mac_context *mac, + struct tsm_ie_ind *pSmeTsmIeInd) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_info *roam_info; + uint32_t SessionId = pSmeTsmIeInd->sessionId; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + + roam_info->tsm_ie.tsid = pSmeTsmIeInd->tsm_ie.tsid; + roam_info->tsm_ie.state = pSmeTsmIeInd->tsm_ie.state; + roam_info->tsm_ie.msmt_interval = pSmeTsmIeInd->tsm_ie.msmt_interval; + /* forward the tsm ie information to HDD */ + csr_roam_call_callback(mac, SessionId, roam_info, 0, + eCSR_ROAM_TSM_IE_IND, 0); + qdf_mem_free(roam_info); + return status; +} + +/** + * sme_set_cckm_ie() - set cckm ie + * @mac_handle: Opaque handle to the global MAC context + * @sessionId: session id + * @pCckmIe: Pointer to CCKM Ie + * @cckmIeLen: Length of @pCckmIe + * + * Function to store the CCKM IE passed from supplicant and use + * it while packing reassociation request. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_set_cckm_ie(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t *pCckmIe, uint8_t cckmIeLen) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + csr_set_cckm_ie(mac, sessionId, pCckmIe, cckmIeLen); + sme_release_global_lock(&mac->sme); + } + return status; +} + +/** + * sme_set_ese_beacon_request() - set ese beacon request + * @mac_handle: Opaque handle to the global MAC context + * @sessionId: session id + * @in_req: Ese beacon report request + * + * function to set ESE beacon request parameters + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_set_ese_beacon_request(mac_handle_t mac_handle, + const uint8_t sessionId, + const tCsrEseBeaconReq *in_req) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tpSirBeaconReportReqInd sme_bcn_rpt_req = NULL; + const tCsrEseBeaconReqParams *bcn_req = NULL; + uint8_t counter = 0; + struct csr_roam_session *session = CSR_GET_SESSION(mac, sessionId); + tpRrmSMEContext sme_rrm_ctx = &mac->rrm.rrmSmeContext[0]; + + if (sme_rrm_ctx->eseBcnReqInProgress == true) { + sme_err("A Beacon Report Req is already in progress"); + return QDF_STATUS_E_RESOURCES; + } + + /* Store the info in RRM context */ + qdf_mem_copy(&sme_rrm_ctx->eseBcnReqInfo, in_req, + sizeof(tCsrEseBeaconReq)); + + /* Prepare the request to send to SME. */ + sme_bcn_rpt_req = qdf_mem_malloc(sizeof(tSirBeaconReportReqInd)); + if (!sme_bcn_rpt_req) + return QDF_STATUS_E_NOMEM; + + sme_rrm_ctx->eseBcnReqInProgress = true; + + sme_debug("Sending Beacon Report Req to SME"); + + sme_bcn_rpt_req->messageType = eWNI_SME_BEACON_REPORT_REQ_IND; + sme_bcn_rpt_req->length = sizeof(tSirBeaconReportReqInd); + qdf_mem_copy(sme_bcn_rpt_req->bssId, + session->connectedProfile.bssid.bytes, + sizeof(tSirMacAddr)); + sme_bcn_rpt_req->channel_info.chan_num = 255; + sme_bcn_rpt_req->channel_list.num_channels = in_req->numBcnReqIe; + sme_bcn_rpt_req->msgSource = eRRM_MSG_SOURCE_ESE_UPLOAD; + sme_bcn_rpt_req->measurement_idx = 0; + + for (counter = 0; counter < in_req->numBcnReqIe; counter++) { + bcn_req = &in_req->bcnReq[counter]; + sme_bcn_rpt_req->fMeasurementtype[counter] = + bcn_req->scanMode; + sme_bcn_rpt_req->measurementDuration[counter] = + SYS_TU_TO_MS(bcn_req->measurementDuration); + sme_bcn_rpt_req->channel_list.chan_freq_lst[counter] = + bcn_req->ch_freq; + } + + status = sme_rrm_process_beacon_report_req_ind(mac, sme_bcn_rpt_req); + + if (status != QDF_STATUS_SUCCESS) + sme_rrm_ctx->eseBcnReqInProgress = false; + + qdf_mem_free(sme_bcn_rpt_req); + + return status; +} + +/** + * sme_get_tsm_stats() - SME get tsm stats + * @mac_handle: Opaque handle to the global MAC context + * @callback: SME sends back the requested stats using the callback + * @staId: The station ID for which the stats is requested for + * @bssId: bssid + * @pContext: user context to be passed back along with the callback + * @tid: Traffic id + * + * API register a callback to get TSM Stats. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_get_tsm_stats(mac_handle_t mac_handle, + tCsrTsmStatsCallback callback, + struct qdf_mac_addr bssId, + void *pContext, uint8_t tid) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_get_tsm_stats(mac, callback, + bssId, pContext, + tid); + sme_release_global_lock(&mac->sme); + } + return status; +} + +/** + * sme_set_ese_roam_scan_channel_list() - To set ese roam scan channel list + * @mac_handle: Opaque handle to the global MAC context + * @sessionId: sme session id + * @chan_freq_list: Output channel list + * @numChannels: Output number of channels + * + * This routine is called to set ese roam scan channel list. + * This is a synchronous call + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_ese_roam_scan_channel_list(mac_handle_t mac_handle, + uint8_t sessionId, + uint32_t *chan_freq_list, + uint8_t numChannels) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = NULL; + tpCsrChannelInfo curchnl_list_info = NULL; + uint8_t oldChannelList[CFG_VALID_CHANNEL_LIST_LEN * 5] = { 0 }; + uint8_t newChannelList[CFG_VALID_CHANNEL_LIST_LEN * 5] = { 0 }; + uint8_t i = 0, j = 0; + enum band_info band = -1; + uint32_t band_bitmap; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + pNeighborRoamInfo = &mac->roam.neighborRoamInfo[sessionId]; + curchnl_list_info = + &pNeighborRoamInfo->roamChannelInfo.currentChannelListInfo; + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + + if (curchnl_list_info->freq_list) { + for (i = 0; i < curchnl_list_info->numOfChannels; i++) { + j += snprintf(oldChannelList + j, + sizeof(oldChannelList) - j, "%d", + curchnl_list_info->freq_list[i]); + } + } + ucfg_reg_get_band(mac->pdev, &band_bitmap); + band = wlan_reg_band_bitmap_to_band_info(band_bitmap); + status = csr_create_roam_scan_channel_list(mac, sessionId, + chan_freq_list, numChannels, + band); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (curchnl_list_info->freq_list) { + j = 0; + for (i = 0; i < curchnl_list_info->numOfChannels; i++) { + j += snprintf(newChannelList + j, + sizeof(newChannelList) - j, "%d", + curchnl_list_info->freq_list[i]); + } + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "ESE roam scan chnl list successfully set to %s-old value is %s-roam state is %d", + newChannelList, oldChannelList, + pNeighborRoamInfo->neighborRoamState); + } + + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, sessionId, + REASON_CHANNEL_LIST_CHANGED); + + sme_release_global_lock(&mac->sme); + return status; +} + +#endif /* FEATURE_WLAN_ESE */ + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS sme_get_roam_scan_ch(mac_handle_t mac_handle, + uint8_t vdev_id, void *pcontext) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return QDF_STATUS_E_FAILURE; + + msg.type = WMA_ROAM_SCAN_CH_REQ; + msg.bodyval = vdev_id; + mac->sme.roam_scan_ch_get_context = pcontext; + + if (scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + sme_err("Posting message %d failed", + WMA_ROAM_SCAN_CH_REQ); + mac->sme.roam_scan_ch_get_context = NULL; + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + sme_release_global_lock(&mac->sme); + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef QCA_IBSS_SUPPORT +/** + * sme_ibss_peer_info_response_handler() - Handler for ibss peer info + * @mac: Global MAC pointer + * @sme_ibss_peer_info_response_handler: ibss peer info params + * + * Return: QDF_STATUS + */ +static +QDF_STATUS sme_ibss_peer_info_response_handler(struct mac_context *mac, + tpSirIbssGetPeerInfoRspParams + pIbssPeerInfoParams) +{ + struct ibss_peer_info_cb_info *cb_info; + + if (!mac) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_FATAL, + "%s: mac is null", __func__); + return QDF_STATUS_E_FAILURE; + } + cb_info = &mac->sme.peer_info_cb_info; + if (!cb_info->peer_info_cb) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: HDD callback is null", __func__); + return QDF_STATUS_E_FAILURE; + } + cb_info->peer_info_cb(cb_info->peer_info_cb_context, + &pIbssPeerInfoParams->ibssPeerInfoRspParams); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_request_ibss_peer_info(mac_handle_t mac_handle, void *cb_context, + ibss_peer_info_cb peer_info_cb, + bool allPeerInfoReqd, uint8_t *mac_addr) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tSirIbssGetPeerInfoReqParams *pIbssInfoReqParams; + struct ibss_peer_info_cb_info *cb_info; + + status = sme_acquire_global_lock(&mac->sme); + if (status == QDF_STATUS_SUCCESS) { + cb_info = &mac->sme.peer_info_cb_info; + cb_info->peer_info_cb = peer_info_cb; + cb_info->peer_info_cb_context = cb_context; + + pIbssInfoReqParams = (tSirIbssGetPeerInfoReqParams *) + qdf_mem_malloc(sizeof(tSirIbssGetPeerInfoReqParams)); + if (!pIbssInfoReqParams) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + pIbssInfoReqParams->allPeerInfoReqd = allPeerInfoReqd; + qdf_mem_copy(pIbssInfoReqParams->peer_mac.bytes, mac_addr, + QDF_MAC_ADDR_SIZE); + + message.type = WMA_GET_IBSS_PEER_INFO_REQ; + message.bodyptr = pIbssInfoReqParams; + message.reserved = 0; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (qdf_status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Post WMA_GET_IBSS_PEER_INFO_REQ MSG failed", + __func__); + qdf_mem_free(pIbssInfoReqParams); + qdf_status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + + return qdf_status; +} +#else +static inline +QDF_STATUS sme_ibss_peer_info_response_handler(struct mac_context *mac, + tpSirIbssGetPeerInfoRspParams + pIbssPeerInfoParams) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * sme_process_dual_mac_config_resp() - Process set Dual mac config response + * @mac: Global MAC pointer + * @msg: Dual mac config response + * + * Processes the dual mac configuration response and invokes the HDD callback + * to process further + */ +static QDF_STATUS sme_process_dual_mac_config_resp(struct mac_context *mac, + uint8_t *msg) +{ + tListElem *entry = NULL; + tSmeCmd *command = NULL; + bool found; + dual_mac_cb callback = NULL; + struct sir_dual_mac_config_resp *param; + + param = (struct sir_dual_mac_config_resp *)msg; + if (!param) { + sme_err("Dual mac config resp param is NULL"); + /* Not returning. Need to check if active command list + * needs to be freed + */ + } + + entry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (!entry) { + sme_err("No cmd found in active list"); + return QDF_STATUS_E_FAILURE; + } + + command = GET_BASE_ADDR(entry, tSmeCmd, Link); + if (!command) { + sme_err("Base address is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (e_sme_command_set_dual_mac_config != command->command) { + sme_err("Command mismatch!"); + return QDF_STATUS_E_FAILURE; + } + + callback = command->u.set_dual_mac_cmd.set_dual_mac_cb; + if (callback) { + if (!param) { + sme_err("Callback failed-Dual mac config is NULL"); + } else { + sme_debug("Calling HDD callback for Dual mac config"); + callback(param->status, + command->u.set_dual_mac_cmd.scan_config, + command->u.set_dual_mac_cmd.fw_mode_config); + } + } else { + sme_err("Callback does not exist"); + } + + found = csr_nonscan_active_ll_remove_entry(mac, entry, LL_ACCESS_LOCK); + if (found) + /* Now put this command back on the available command list */ + csr_release_command(mac, command); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS sme_set_roam_scan_ch_event_cb(mac_handle_t mac_handle, + sme_get_raom_scan_ch_callback cb) +{ + QDF_STATUS qdf_status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(qdf_status)) + return qdf_status; + + mac->sme.roam_scan_ch_callback = cb; + sme_release_global_lock(&mac->sme); + + return qdf_status; +} + +/** + * sme_process_roam_scan_ch_list_resp() - Process get roam scan ch list + * response + * @mac: Global MAC pointer + * @msgbuf: pointer to roam scan ch list response + * + * This function checks the roam scan chan list message is for command + * response or a async event and accordingly data is given to user space. + * callback to process further + */ +static void +sme_process_roam_scan_ch_list_resp(struct mac_context *mac, + struct roam_scan_ch_resp *roam_ch) +{ + sme_get_raom_scan_ch_callback callback = + mac->sme.roam_scan_ch_callback; + + if (!roam_ch) + return; + + if (callback) + callback(mac->hdd_handle, roam_ch, + mac->sme.roam_scan_ch_get_context); +} +#else +static void +sme_process_roam_scan_ch_list_resp(tpAniSirGlobal mac, + struct roam_scan_ch_resp *roam_ch) +{ +} +#endif + +/** + * sme_process_antenna_mode_resp() - Process set antenna mode + * response + * @mac: Global MAC pointer + * @msg: antenna mode response + * + * Processes the antenna mode response and invokes the HDD + * callback to process further + */ +static QDF_STATUS sme_process_antenna_mode_resp(struct mac_context *mac, + uint8_t *msg) +{ + tListElem *entry; + tSmeCmd *command; + bool found; + void *context = NULL; + antenna_mode_cb callback; + struct sir_antenna_mode_resp *param; + + param = (struct sir_antenna_mode_resp *)msg; + if (!param) + sme_err("set antenna mode resp is NULL"); + /* Not returning. Need to check if active command list + * needs to be freed + */ + + entry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (!entry) { + sme_err("No cmd found in active list"); + return QDF_STATUS_E_FAILURE; + } + + command = GET_BASE_ADDR(entry, tSmeCmd, Link); + if (!command) { + sme_err("Base address is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (e_sme_command_set_antenna_mode != command->command) { + sme_err("Command mismatch!"); + return QDF_STATUS_E_FAILURE; + } + + context = command->u.set_antenna_mode_cmd.set_antenna_mode_ctx; + callback = command->u.set_antenna_mode_cmd.set_antenna_mode_resp; + if (callback) { + if (!param) + sme_err("Set antenna mode call back is NULL"); + else + callback(param->status, context); + } else { + sme_err("Callback does not exist"); + } + + found = csr_nonscan_active_ll_remove_entry(mac, entry, LL_ACCESS_LOCK); + if (found) + /* Now put this command back on the available command list */ + csr_release_command(mac, command); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_process_msg(struct mac_context *mac, struct scheduler_msg *pMsg) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!pMsg) { + sme_err("Empty message for SME"); + return status; + } + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + if (pMsg->bodyptr) + qdf_mem_free(pMsg->bodyptr); + return status; + } + if (!SME_IS_START(mac)) { + sme_debug("message type %d in stop state ignored", pMsg->type); + if (pMsg->bodyptr) + qdf_mem_free(pMsg->bodyptr); + goto release_lock; + } + switch (pMsg->type) { +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + case eWNI_SME_HO_FAIL_IND: + csr_process_ho_fail_ind(mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + break; +#endif + case eWNI_SME_ADDTS_RSP: + case eWNI_SME_DELTS_RSP: + case eWNI_SME_DELTS_IND: + case eWNI_SME_FT_AGGR_QOS_RSP: + /* QoS */ + if (pMsg->bodyptr) { +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + status = sme_qos_msg_processor(mac, pMsg->type, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); +#endif + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_NEIGHBOR_REPORT_IND: + case eWNI_SME_BEACON_REPORT_REQ_IND: + if (pMsg->bodyptr) { + status = sme_rrm_msg_processor(mac, pMsg->type, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_VDEV_DELETE_RSP: + if (pMsg->bodyptr) { + status = csr_process_vdev_del_rsp(mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_GENERIC_CHANGE_COUNTRY_CODE: + if (pMsg->bodyptr) { + status = sme_handle_generic_change_country_code( + (void *)mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; +#ifdef WLAN_FEATURE_11W + case eWNI_SME_UNPROT_MGMT_FRM_IND: + if (pMsg->bodyptr) { + sme_unprotected_mgmt_frm_ind(mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; +#endif +#ifdef FEATURE_WLAN_ESE + case eWNI_SME_TSM_IE_IND: + if (pMsg->bodyptr) { + sme_tsm_ie_ind(mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; +#endif /* FEATURE_WLAN_ESE */ + case eWNI_SME_ROAM_SCAN_OFFLOAD_RSP: + status = csr_roam_offload_scan_rsp_hdlr((void *)mac, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_IBSS_PEER_INFO_RSP: + if (pMsg->bodyptr) { + sme_ibss_peer_info_response_handler(mac, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + case eWNI_SME_READY_TO_EXTWOW_IND: + if (pMsg->bodyptr) { + sme_process_ready_to_ext_wow(mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; +#endif +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + case eWNI_SME_AUTO_SHUTDOWN_IND: + if (mac->sme.auto_shutdown_cb) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Auto shutdown notification")); + mac->sme.auto_shutdown_cb(); + } + break; +#endif + case eWNI_SME_DFS_RADAR_FOUND: + case eWNI_SME_DFS_CAC_COMPLETE: + case eWNI_SME_DFS_CSAIE_TX_COMPLETE_IND: + case eWNI_SME_CSA_RESTART_RSP: + status = dfs_msg_processor(mac, pMsg); + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_CHANNEL_CHANGE_RSP: + if (pMsg->bodyptr) { + status = sme_process_channel_change_resp(mac, + pMsg->type, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_STATS_EXT_EVENT: + status = sme_stats_ext_event(mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_GET_PEER_INFO_EXT_IND: + if (mac->sme.pget_peer_info_ext_ind_cb) + mac->sme.pget_peer_info_ext_ind_cb(pMsg->bodyptr, + mac->sme.pget_peer_info_ext_cb_context); + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_FW_STATUS_IND: + status = sme_fw_state_resp(mac); + break; + case eWNI_SME_TSF_EVENT: + if (mac->sme.get_tsf_cb) { + mac->sme.get_tsf_cb(mac->sme.get_tsf_cxt, + (struct stsf *)pMsg->bodyptr); + } + if (pMsg->bodyptr) + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_LINK_STATUS_IND: + { + tAniGetLinkStatus *pLinkStatus = + (tAniGetLinkStatus *) pMsg->bodyptr; + if (pLinkStatus) { + if (mac->sme.link_status_callback) + mac->sme.link_status_callback( + pLinkStatus->linkStatus, + mac->sme.link_status_context); + + mac->sme.link_status_callback = NULL; + mac->sme.link_status_context = NULL; + qdf_mem_free(pLinkStatus); + } + break; + } + case eWNI_SME_MSG_GET_TEMPERATURE_IND: + if (mac->sme.temperature_cb) + mac->sme.temperature_cb(pMsg->bodyval, + mac->sme.temperature_cb_context); + break; + case eWNI_SME_SNR_IND: + { + tAniGetSnrReq *pSnrReq = (tAniGetSnrReq *) pMsg->bodyptr; + + if (pSnrReq) { + if (pSnrReq->snrCallback) { + ((tCsrSnrCallback) + (pSnrReq->snrCallback))(pSnrReq->snr, + pSnrReq->pDevContext); + } + qdf_mem_free(pSnrReq); + } + break; + } +#ifdef FEATURE_WLAN_EXTSCAN + case eWNI_SME_EXTSCAN_FULL_SCAN_RESULT_IND: + if (mac->sme.ext_scan_ind_cb) + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_EXTSCAN_FULL_SCAN_RESULT_IND, + pMsg->bodyptr); + else + sme_err("callback not registered to process: %d", + pMsg->type); + + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_EPNO_NETWORK_FOUND_IND: + if (mac->sme.ext_scan_ind_cb) + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_EPNO_NETWORK_FOUND_IND, + pMsg->bodyptr); + else + sme_err("callback not registered to process: %d", + pMsg->type); + + qdf_mem_free(pMsg->bodyptr); + break; +#endif + case eWNI_SME_SET_HW_MODE_RESP: + if (pMsg->bodyptr) { + status = sme_process_set_hw_mode_resp(mac, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_HW_MODE_TRANS_IND: + if (pMsg->bodyptr) { + status = sme_process_hw_mode_trans_ind(mac, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_NSS_UPDATE_RSP: + if (pMsg->bodyptr) { + status = sme_process_nss_update_resp(mac, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_SET_DUAL_MAC_CFG_RESP: + if (pMsg->bodyptr) { + status = sme_process_dual_mac_config_resp(mac, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_SET_THERMAL_LEVEL_IND: + if (mac->sme.set_thermal_level_cb) + mac->sme.set_thermal_level_cb(mac->hdd_handle, + pMsg->bodyval); + break; + case eWNI_SME_EXT_CHANGE_CHANNEL_IND: + status = sme_extended_change_channel_ind(mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_SET_ANTENNA_MODE_RESP: + if (pMsg->bodyptr) { + status = sme_process_antenna_mode_resp(mac, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_LOST_LINK_INFO_IND: + if (mac->sme.lost_link_info_cb) + mac->sme.lost_link_info_cb(mac->hdd_handle, + (struct sir_lost_link_info *)pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_RSO_CMD_STATUS_IND: + if (mac->sme.rso_cmd_status_cb) + mac->sme.rso_cmd_status_cb(mac->hdd_handle, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + break; + case eWMI_SME_LL_STATS_IND: + if (mac->sme.link_layer_stats_ext_cb) + mac->sme.link_layer_stats_ext_cb(mac->hdd_handle, + pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_BT_ACTIVITY_INFO_IND: + if (mac->sme.bt_activity_info_cb) + mac->sme.bt_activity_info_cb(mac->hdd_handle, + pMsg->bodyval); + break; + case eWNI_SME_HIDDEN_SSID_RESTART_RSP: + if (mac->sme.hidden_ssid_cb) + mac->sme.hidden_ssid_cb(mac->hdd_handle, pMsg->bodyval); + else + sme_err("callback is NULL"); + break; + case eWNI_SME_ANTENNA_ISOLATION_RSP: + if (pMsg->bodyptr) { + if (mac->sme.get_isolation_cb) + mac->sme.get_isolation_cb( + (struct sir_isolation_resp *)pMsg->bodyptr, + mac->sme.get_isolation_cb_context); + qdf_mem_free(pMsg->bodyptr); + } else { + sme_err("Empty message for: %d", pMsg->type); + } + break; + case eWNI_SME_GET_ROAM_SCAN_CH_LIST_EVENT: + sme_process_roam_scan_ch_list_resp(mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + break; + case eWNI_SME_MONITOR_MODE_VDEV_UP: + status = sme_process_monitor_mode_vdev_up_evt(pMsg->bodyval); + break; + default: + + if ((pMsg->type >= eWNI_SME_MSG_TYPES_BEGIN) + && (pMsg->type <= eWNI_SME_MSG_TYPES_END)) { + /* CSR */ + if (pMsg->bodyptr) { + status = csr_msg_processor(mac, pMsg->bodyptr); + qdf_mem_free(pMsg->bodyptr); + } else + sme_err("Empty message for: %d", pMsg->type); + } else { + sme_warn("Unknown message type: %d", pMsg->type); + if (pMsg->bodyptr) + qdf_mem_free(pMsg->bodyptr); + } + } /* switch */ +release_lock: + sme_release_global_lock(&mac->sme); + return status; +} + +QDF_STATUS sme_mc_process_handler(struct scheduler_msg *msg) +{ + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_SME); + + if (!mac_ctx) { + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + return sme_process_msg(mac_ctx, msg); +} + +/** + * sme_process_nss_update_resp() - Process nss update response + * @mac: Global MAC pointer + * @msg: nss update response + * + * Processes the nss update response and invokes the HDD + * callback to process further + */ +static QDF_STATUS sme_process_nss_update_resp(struct mac_context *mac, uint8_t *msg) +{ + tListElem *entry = NULL; + tSmeCmd *command = NULL; + bool found; + policy_mgr_nss_update_cback callback = NULL; + struct sir_bcn_update_rsp *param; + + param = (struct sir_bcn_update_rsp *)msg; + if (!param) + sme_err("nss update resp param is NULL"); + /* Not returning. Need to check if active command list + * needs to be freed + */ + + if (param && param->reason != REASON_NSS_UPDATE) { + sme_err("reason not NSS update"); + return QDF_STATUS_E_INVAL; + } + entry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (!entry) { + sme_err("No cmd found in active list"); + return QDF_STATUS_E_FAILURE; + } + + command = GET_BASE_ADDR(entry, tSmeCmd, Link); + if (!command) { + sme_err("Base address is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (e_sme_command_nss_update != command->command) { + sme_err("Command mismatch!"); + return QDF_STATUS_E_FAILURE; + } + + callback = command->u.nss_update_cmd.nss_update_cb; + if (callback) { + if (!param) + sme_err("Callback failed since nss update params is NULL"); + else + callback(command->u.nss_update_cmd.context, + param->status, + param->vdev_id, + command->u.nss_update_cmd.next_action, + command->u.nss_update_cmd.reason, + command->u.nss_update_cmd.original_vdev_id); + } else { + sme_err("Callback does not exisit"); + } + + found = csr_nonscan_active_ll_remove_entry(mac, entry, LL_ACCESS_LOCK); + if (found) { + /* Now put this command back on the avilable command list */ + csr_release_command(mac, command); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_stop(mac_handle_t mac_handle) +{ + QDF_STATUS status; + QDF_STATUS ret_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = rrm_stop(mac); + if (QDF_IS_STATUS_ERROR(status)) { + ret_status = status; + sme_err("rrm_stop failed with status: %d", status); + } + + status = csr_stop(mac); + if (QDF_IS_STATUS_ERROR(status)) { + ret_status = status; + sme_err("csr_stop failed with status: %d", status); + } + + mac->sme.state = SME_STATE_STOP; + + return ret_status; +} + +/* + * sme_close() - Release all SME modules and their resources. + * The function release each module in SME, PMC, CSR, etc. . Upon + * return, all modules are at closed state. + * + * No SME APIs can be involved after smeClose except smeOpen. + * smeClose must be called before mac_close. + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open + * Return QDF_STATUS_SUCCESS - SME is successfully close. + * + * Other status means SME is failed to be closed but caller still cannot + * call any other SME functions except smeOpen. + */ +QDF_STATUS sme_close(mac_handle_t mac_handle) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + QDF_STATUS fail_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) + return QDF_STATUS_E_FAILURE; + + sme_unregister_power_debug_stats_cb(mac); + + status = csr_close(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("csr_close failed with status: %d", status); + fail_status = status; + } +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + status = sme_qos_close(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Qos close failed with status: %d", status); + fail_status = status; + } +#endif + status = sme_ps_close(mac_handle); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("sme_ps_close failed status: %d", status); + fail_status = status; + } + + status = rrm_close(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("RRM close failed with status: %d", status); + fail_status = status; + } + + free_sme_cmd_list(mac); + + status = qdf_mutex_destroy(&mac->sme.sme_global_lock); + if (!QDF_IS_STATUS_SUCCESS(status)) + fail_status = QDF_STATUS_E_FAILURE; + + mac->sme.state = SME_STATE_STOP; + + return fail_status; +} + +/** + * sme_remove_bssid_from_scan_list() - wrapper to remove the bssid from + * scan list + * @mac_handle: Opaque handle to the global MAC context. + * @bssid: bssid to be removed + * + * This function remove the given bssid from scan list. + * + * Return: QDF status. + */ +QDF_STATUS sme_remove_bssid_from_scan_list(mac_handle_t mac_handle, + tSirMacAddr bssid) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + csr_remove_bssid_from_scan_list(mac_ctx, bssid); + sme_release_global_lock(&mac_ctx->sme); + } + + return status; +} + +QDF_STATUS sme_scan_get_result(mac_handle_t mac_handle, uint8_t vdev_id, + struct scan_filter *filter, + tScanResultHandle *phResult) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_MSG_SCAN_GET_RESULTS, vdev_id, + 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_scan_get_result(mac, filter, phResult); + sme_release_global_lock(&mac->sme); + } + + return status; +} + +QDF_STATUS sme_scan_get_result_for_bssid(mac_handle_t mac_handle, + struct qdf_mac_addr *bssid, + tCsrScanResultInfo *res) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_scan_get_result_for_bssid(mac_ctx, bssid, res); + sme_release_global_lock(&mac_ctx->sme); + } + + return status; +} + +QDF_STATUS sme_get_ap_channel_from_scan(void *profile, + tScanResultHandle *scan_cache, + uint32_t *ap_ch_freq) +{ + QDF_STATUS status; + + status = sme_get_ap_channel_from_scan_cache((struct csr_roam_profile *) + profile, + scan_cache, + ap_ch_freq); + return status; +} + +/** + * sme_get_ap_channel_from_scan_cache() - a wrapper function to get AP's + * channel id from CSR by filtering the + * result which matches our roam profile. + * @profile: SAP adapter + * @ap_ch_freq: pointer to channel freq of SAP. Fill the value after finding the + * best ap from scan cache. + * + * This function is written to get AP's channel id from CSR by filtering + * the result which matches our roam profile. This is a synchronous call. + * + * Return: QDF_STATUS. + */ +QDF_STATUS sme_get_ap_channel_from_scan_cache( + struct csr_roam_profile *profile, tScanResultHandle *scan_cache, + uint32_t *ap_ch_freq) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = sme_get_mac_context(); + struct scan_filter *scan_filter; + tScanResultHandle filtered_scan_result = NULL; + struct bss_description first_ap_profile; + + if (!mac_ctx) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("mac_ctx is NULL")); + return QDF_STATUS_E_FAILURE; + } + scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); + if (!scan_filter) + return QDF_STATUS_E_FAILURE; + + qdf_mem_zero(&first_ap_profile, sizeof(struct bss_description)); + if (!profile) { + /* No encryption */ + scan_filter->num_of_enc_type = 1; + scan_filter->enc_type[0] = WLAN_ENCRYPT_TYPE_NONE; + } else { + /* Here is the profile we need to connect to */ + status = csr_roam_get_scan_filter_from_profile(mac_ctx, + profile, + scan_filter, + false); + } + + if (QDF_IS_STATUS_ERROR(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Preparing the profile filter failed")); + qdf_mem_free(scan_filter); + return QDF_STATUS_E_FAILURE; + } + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_STATUS_SUCCESS == status) { + status = csr_scan_get_result(mac_ctx, scan_filter, + &filtered_scan_result); + if (QDF_STATUS_SUCCESS == status) { + csr_get_bssdescr_from_scan_handle(filtered_scan_result, + &first_ap_profile); + *scan_cache = filtered_scan_result; + if (first_ap_profile.chan_freq) { + *ap_ch_freq = first_ap_profile.chan_freq; + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("Found best AP & its on freq[%d]"), + first_ap_profile.chan_freq); + } else { + /* + * This means scan result is empty + * so set the channel to zero, caller should + * take of zero channel id case. + */ + *ap_ch_freq = 0; + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + FL("Scan is empty, set chnl to 0")); + status = QDF_STATUS_E_FAILURE; + } + } else { + sme_err("Failed to get scan get result"); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac_ctx->sme); + } else { + status = QDF_STATUS_E_FAILURE; + } + qdf_mem_free(scan_filter); + + return status; +} + +/* + * sme_scan_result_get_first() - + * A wrapper function to request CSR to returns the first element of + * scan result. + * This is a synchronous call + * + * hScanResult - returned from csr_scan_get_result + * Return tCsrScanResultInfo * - NULL if no result + */ +tCsrScanResultInfo *sme_scan_result_get_first(mac_handle_t mac_handle, + tScanResultHandle hScanResult) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tCsrScanResultInfo *pRet = NULL; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_GETFIRST, + NO_SESSION, 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + pRet = csr_scan_result_get_first(mac, hScanResult); + sme_release_global_lock(&mac->sme); + } + + return pRet; +} + +/* + * sme_scan_result_get_next() - + * A wrapper function to request CSR to returns the next element of + * scan result. It can be called without calling csr_scan_result_get_first first + * This is a synchronous call + * + * hScanResult - returned from csr_scan_get_result + * Return Null if no result or reach the end + */ +tCsrScanResultInfo *sme_scan_result_get_next(mac_handle_t mac_handle, + tScanResultHandle hScanResult) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tCsrScanResultInfo *pRet = NULL; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + pRet = csr_scan_result_get_next(mac, hScanResult); + sme_release_global_lock(&mac->sme); + } + + return pRet; +} + +/* + * sme_scan_result_purge() - + * A wrapper function to request CSR to remove all items(tCsrScanResult) + * in the list and free memory for each item + * This is a synchronous call + * + * hScanResult - returned from csr_scan_get_result. hScanResult is + * considered gone by + * calling this function and even before this function reutrns. + * Return QDF_STATUS + */ +QDF_STATUS sme_scan_result_purge(tScanResultHandle hScanResult) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = sme_get_mac_context(); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_PURGE, + NO_SESSION, 0)); + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_scan_result_purge(mac_ctx, hScanResult); + sme_release_global_lock(&mac_ctx->sme); + } + + return status; +} + +eCsrPhyMode sme_get_phy_mode(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->roam.configParam.phyMode; +} + +/* + * sme_roam_connect() - + * A wrapper function to request CSR to inititiate an association + * This is an asynchronous call. + * + * sessionId - the sessionId returned by sme_open_session. + * pProfile - description of the network to which to connect + * hBssListIn - a list of BSS descriptor to roam to. It is returned + * from csr_scan_get_result + * pRoamId - to get back the request ID + * Return QDF_STATUS + */ +QDF_STATUS sme_roam_connect(mac_handle_t mac_handle, uint8_t sessionId, + struct csr_roam_profile *pProfile, + uint32_t *pRoamId) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) + return QDF_STATUS_E_FAILURE; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_MSG_CONNECT, sessionId, 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, sessionId)) { + status = + csr_roam_connect(mac, sessionId, pProfile, + pRoamId); + } else { + sme_err("Invalid sessionID: %d", sessionId); + status = QDF_STATUS_E_INVAL; + } + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_set_phy_mode() - + * Changes the PhyMode. + * + * mac_handle - The handle returned by mac_open. + * phyMode new phyMode which is to set + * Return QDF_STATUS SUCCESS. + */ +QDF_STATUS sme_set_phy_mode(mac_handle_t mac_handle, eCsrPhyMode phyMode) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + mac->roam.configParam.phyMode = phyMode; + mac->roam.configParam.uCfgDot11Mode = + csr_get_cfg_dot11_mode_from_csr_phy_mode(NULL, + mac->roam.configParam.phyMode, + mac->roam.configParam. + ProprietaryRatesEnabled); + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_roam_reassoc() - + * A wrapper function to request CSR to inititiate a re-association + * + * pProfile - can be NULL to join the currently connected AP. In that + * case modProfileFields should carry the modified field(s) which could trigger + * reassoc + * modProfileFields - fields which are part of tCsrRoamConnectedProfile + * that might need modification dynamically once STA is up & running and this + * could trigger a reassoc + * pRoamId - to get back the request ID + * Return QDF_STATUS + */ +QDF_STATUS sme_roam_reassoc(mac_handle_t mac_handle, uint8_t sessionId, + struct csr_roam_profile *pProfile, + tCsrRoamModifyProfileFields modProfileFields, + uint32_t *pRoamId, bool fForce) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_ROAM_REASSOC, sessionId, 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, sessionId)) { + if ((!pProfile) && (fForce == 1)) + status = csr_reassoc(mac, sessionId, + &modProfileFields, pRoamId, + fForce); + else + status = csr_roam_reassoc(mac, sessionId, + pProfile, + modProfileFields, pRoamId); + } else { + status = QDF_STATUS_E_INVAL; + } + sme_release_global_lock(&mac->sme); + } + + return status; +} + +QDF_STATUS sme_roam_disconnect(mac_handle_t mac_handle, uint8_t session_id, + eCsrRoamDisconnectReason reason, + tSirMacReasonCodes mac_reason) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_ROAM_DISCONNECT, session_id, + reason)); + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac_ctx, session_id)) + status = csr_roam_disconnect(mac_ctx, session_id, + reason, + mac_reason); + else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac_ctx->sme); + } + + return status; +} + +/* sme_dhcp_done_ind() - send dhcp done ind + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * + * Return: void. + */ +void sme_dhcp_done_ind(mac_handle_t mac_handle, uint8_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + if (!mac_ctx) + return; + + session = CSR_GET_SESSION(mac_ctx, session_id); + if (!session) { + sme_err("Session: %d not found", session_id); + return; + } + session->dhcp_done = true; +} + +/* + * sme_roam_stop_bss() - + * To stop BSS for Soft AP. This is an asynchronous API. + * + * mac_handle - Global structure + * sessionId - sessionId of SoftAP + * Return QDF_STATUS SUCCESS Roam callback will be called to indicate + * actual results + */ +QDF_STATUS sme_roam_stop_bss(mac_handle_t mac_handle, uint8_t sessionId) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, sessionId)) + status = csr_roam_issue_stop_bss_cmd(mac, sessionId, + false); + else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/** + * sme_roam_disconnect_sta() - disassociate a station + * @mac_handle: Global structure + * @sessionId: SessionId of SoftAP + * @p_del_sta_params: Pointer to parameters of the station to disassoc + * + * To disassociate a station. This is an asynchronous API. + * + * Return: QDF_STATUS_SUCCESS on success.Roam callback will + * be called to indicate actual result. + */ +QDF_STATUS sme_roam_disconnect_sta(mac_handle_t mac_handle, uint8_t sessionId, + struct csr_del_sta_params *p_del_sta_params) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) { + QDF_ASSERT(0); + return status; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, sessionId)) + status = csr_roam_issue_disassociate_sta_cmd(mac, + sessionId, p_del_sta_params); + else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/** + * sme_roam_deauth_sta() - deauthenticate a station + * @mac_handle: Global structure + * @sessionId: SessionId of SoftAP + * @pDelStaParams: Pointer to parameters of the station to deauthenticate + * + * To disassociate a station. This is an asynchronous API. + * + * Return: QDF_STATUS_SUCCESS on success or another QDF_STATUS error + * code on error. Roam callback will be called to indicate actual + * result + */ +QDF_STATUS sme_roam_deauth_sta(mac_handle_t mac_handle, uint8_t sessionId, + struct csr_del_sta_params *pDelStaParams) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) { + QDF_ASSERT(0); + return status; + } + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_MSG_DEAUTH_STA, + sessionId, pDelStaParams->reason_code)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, sessionId)) + status = + csr_roam_issue_deauth_sta_cmd(mac, sessionId, + pDelStaParams); + else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_roam_get_connect_profile() - + * A wrapper function to request CSR to return the current connect + * profile. Caller must call csr_roam_free_connect_profile after it is done + * and before reuse for another csr_roam_get_connect_profile call. + * This is a synchronous call. + * + * pProfile - pointer to a caller allocated structure + * tCsrRoamConnectedProfile + * eturn QDF_STATUS. Failure if not connected + */ +QDF_STATUS sme_roam_get_connect_profile(mac_handle_t mac_handle, + uint8_t sessionId, + tCsrRoamConnectedProfile *pProfile) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_ROAM_GET_CONNECTPROFILE, + sessionId, 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, sessionId)) + status = csr_roam_get_connect_profile(mac, sessionId, + pProfile); + else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/** + * sme_roam_free_connect_profile - a wrapper function to request CSR to free and + * reinitialize the profile returned previously by csr_roam_get_connect_profile. + * + * @profile - pointer to a caller allocated structure tCsrRoamConnectedProfile + * + * Return: none + */ +void sme_roam_free_connect_profile(tCsrRoamConnectedProfile *profile) +{ + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_ROAM_FREE_CONNECTPROFILE, + NO_SESSION, 0)); + csr_roam_free_connect_profile(profile); +} + +QDF_STATUS sme_roam_del_pmkid_from_cache(mac_handle_t mac_handle, + uint8_t vdev_id, + struct wlan_crypto_pmksa *pmksa, + bool set_pmk) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct wlan_objmgr_vdev *vdev; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_ROAM_DEL_PMKIDCACHE, + vdev_id, set_pmk)); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Vdev[%d] is NULL!", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_crypto_set_del_pmksa(vdev, pmksa, set_pmk); + if (QDF_IS_STATUS_ERROR(status)) + sme_err("Delete PMK entry failed"); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return status; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +void sme_get_pmk_info(mac_handle_t mac_handle, uint8_t session_id, + tPmkidCacheInfo *pmk_cache) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status = sme_acquire_global_lock(&mac_ctx->sme); + + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac_ctx, session_id)) + csr_get_pmk_info(mac_ctx, session_id, pmk_cache); + sme_release_global_lock(&mac_ctx->sme); + } +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/* + * sme_roam_set_psk_pmk() - a wrapper function to request CSR to save PSK/PMK + * This is a synchronous call. + * @mac_handle: Global structure + * @sessionId: SME sessionId + * @pPSK_PMK: pointer to an array of Psk[]/Pmk + * @pmk_len: Length could be only 16 bytes in case if LEAP + * connections. Need to pass this information to + * firmware. + * @update_to_fw: True - send RSO update to firmware after updating + * session->psk_pmk. + * False - Copy the pmk to session->psk_pmk and return + * + * Return: QDF_STATUS -status whether PSK/PMK is set or not + */ +QDF_STATUS sme_roam_set_psk_pmk(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t *psk_pmk, size_t pmk_len, + bool update_to_fw) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, sessionId)) + status = csr_roam_set_psk_pmk(mac, sessionId, psk_pmk, + pmk_len, update_to_fw); + else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac->sme); + } + return status; +} +#endif + +QDF_STATUS sme_roam_get_wpa_rsn_req_ie(mac_handle_t mac_handle, + uint8_t session_id, + uint32_t *len, uint8_t *buf) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, session_id)) + status = csr_roam_get_wpa_rsn_req_ie(mac, session_id, + len, buf); + else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +QDF_STATUS sme_roam_get_wpa_rsn_rsp_ie(mac_handle_t mac_handle, + uint8_t session_id, + uint32_t *len, uint8_t *buf) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, session_id)) + status = csr_roam_get_wpa_rsn_rsp_ie(mac, session_id, + len, buf); + else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_get_config_param() - + * A wrapper function that HDD calls to get the global settings + * currently maintained by CSR. + * This is a synchronous call. + * + * pParam - caller allocated memory + * Return QDF_STATUS + */ +QDF_STATUS sme_get_config_param(mac_handle_t mac_handle, + struct sme_config_params *pParam) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_GET_CONFIGPARAM, NO_SESSION, 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_get_config_param(mac, &pParam->csr_config); + if (status != QDF_STATUS_SUCCESS) { + sme_err("csr_get_config_param failed"); + sme_release_global_lock(&mac->sme); + return status; + } + sme_release_global_lock(&mac->sme); + } + + return status; +} + +uint32_t sme_get_vht_ch_width(void) +{ + return wma_get_vht_ch_width(); +} + +/* + * sme_get_modify_profile_fields() - + * HDD or SME - QOS calls this function to get the current values of + * connected profile fields, changing which can cause reassoc. + * This function must be called after CFG is downloaded and STA is in connected + * state. Also, make sure to call this function to get the current profile + * fields before calling the reassoc. So that pModifyProfileFields will have + * all the latest values plus the one(s) has been updated as part of reassoc + * request. + * + * pModifyProfileFields - pointer to the connected profile fields + * changing which can cause reassoc + * Return QDF_STATUS + */ +QDF_STATUS sme_get_modify_profile_fields(mac_handle_t mac_handle, + uint8_t sessionId, + tCsrRoamModifyProfileFields * + pModifyProfileFields) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_GET_MODPROFFIELDS, sessionId, + 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac, sessionId)) + status = csr_get_modify_profile_fields(mac, sessionId, + pModifyProfileFields); + else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +#ifdef FEATURE_OEM_DATA_SUPPORT +/** + * sme_register_oem_data_rsp_callback() - Register a routine of + * type send_oem_data_rsp_msg + * @mac_handle: Handle returned by mac_open. + * @callback: Callback to send response + * to oem application. + * + * sme_oem_data_rsp_callback is used to register sme_send_oem_data_rsp_msg + * callback function. + * + * Return: QDF_STATUS. + */ +QDF_STATUS sme_register_oem_data_rsp_callback(mac_handle_t mac_handle, + sme_send_oem_data_rsp_msg callback) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *pmac = MAC_CONTEXT(mac_handle); + + pmac->sme.oem_data_rsp_callback = callback; + + return status; + +} + +/** + * sme_deregister_oem_data_rsp_callback() - De-register OEM datarsp callback + * @mac_handle: Handler return by mac_open + * This function De-registers the OEM data response callback to SME + * + * Return: None + */ +void sme_deregister_oem_data_rsp_callback(mac_handle_t mac_handle) +{ + struct mac_context *pmac; + + if (!mac_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("mac_handle is not valid")); + return; + } + pmac = MAC_CONTEXT(mac_handle); + + pmac->sme.oem_data_rsp_callback = NULL; +} + +/** + * sme_oem_update_capability() - update UMAC's oem related capability. + * @mac_handle: Handle returned by mac_open + * @oem_cap: pointer to oem_capability + * + * This function updates OEM capability to UMAC. Currently RTT + * related capabilities are updated. More capabilities can be + * added in future. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_oem_update_capability(mac_handle_t mac_handle, + struct sme_oem_capability *cap) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *pmac = MAC_CONTEXT(mac_handle); + uint8_t *bytes; + + bytes = pmac->rrm.rrmConfig.rm_capability; + + if (cap->ftm_rr) + bytes[4] |= RM_CAP_FTM_RANGE_REPORT; + if (cap->lci_capability) + bytes[4] |= RM_CAP_CIVIC_LOC_MEASUREMENT; + + return status; +} + +/** + * sme_oem_get_capability() - get oem capability + * @mac_handle: Handle returned by mac_open + * @oem_cap: pointer to oem_capability + * + * This function is used to get the OEM capability from UMAC. + * Currently RTT related capabilities are received. More + * capabilities can be added in future. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_oem_get_capability(mac_handle_t mac_handle, + struct sme_oem_capability *cap) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *pmac = MAC_CONTEXT(mac_handle); + uint8_t *bytes; + + bytes = pmac->rrm.rrmConfig.rm_capability; + + cap->ftm_rr = bytes[4] & RM_CAP_FTM_RANGE_REPORT; + cap->lci_capability = bytes[4] & RM_CAP_CIVIC_LOC_MEASUREMENT; + + return status; +} +#endif + +/** + * sme_roam_set_default_key_index - To set default wep key idx + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * @default_idx: default wep key index + * + * This function prepares a message and post to WMA to set wep default + * key index + * + * Return: Success:QDF_STATUS_SUCCESS Failure: Error value + */ +QDF_STATUS sme_roam_set_default_key_index(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t default_idx) +{ + struct scheduler_msg msg = {0}; + struct wep_update_default_key_idx *update_key; + + update_key = qdf_mem_malloc(sizeof(*update_key)); + if (!update_key) + return QDF_STATUS_E_NOMEM; + + update_key->session_id = session_id; + update_key->default_idx = default_idx; + + msg.type = WMA_UPDATE_WEP_DEFAULT_KEY; + msg.reserved = 0; + msg.bodyptr = (void *)update_key; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + qdf_mem_free(update_key); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_get_snr() - + * A wrapper function that client calls to register a callback to get SNR + * + * callback - SME sends back the requested stats using the callback + * staId - The station ID for which the stats is requested for + * pContext - user context to be passed back along with the callback + * p_cds_context - cds context + * \return QDF_STATUS + */ +QDF_STATUS sme_get_snr(mac_handle_t mac_handle, + tCsrSnrCallback callback, + struct qdf_mac_addr bssId, void *pContext) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_get_snr(mac, callback, bssId, pContext); + sme_release_global_lock(&mac->sme); + } + return status; +} + +QDF_STATUS sme_get_link_status(mac_handle_t mac_handle, + csr_link_status_callback callback, + void *context, uint8_t session_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tAniGetLinkStatus *msg; + struct scheduler_msg message = {0}; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + msg = qdf_mem_malloc(sizeof(*msg)); + if (!msg) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + msg->msgType = WMA_LINK_STATUS_GET_REQ; + msg->msgLen = sizeof(*msg); + msg->sessionId = session_id; + mac->sme.link_status_context = context; + mac->sme.link_status_callback = callback; + + message.type = WMA_LINK_STATUS_GET_REQ; + message.bodyptr = msg; + message.reserved = 0; + + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("post msg failed, %d", status); + qdf_mem_free(msg); + mac->sme.link_status_context = NULL; + mac->sme.link_status_callback = NULL; + } + + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_get_country_code() - + * To return the current country code. If no country code is applied, + * default country code is used to fill the buffer. + * If 11d supported is turned off, an error is return and the last + * applied/default country code is used. + * This is a synchronous API. + * + * pBuf - pointer to a caller allocated buffer for returned country code. + * pbLen For input, this parameter indicates how big is the buffer. + * Upon return, this parameter has the number of bytes for + * country. If pBuf doesn't have enough space, this function + * returns fail status and this parameter contains the number + * that is needed. + * + * Return QDF_STATUS SUCCESS. + * + * FAILURE or RESOURCES The API finished and failed. + */ +QDF_STATUS sme_get_country_code(mac_handle_t mac_handle, uint8_t *pBuf, + uint8_t *pbLen) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_GET_CNTRYCODE, NO_SESSION, 0)); + + return csr_get_country_code(mac, pBuf, pbLen); +} + +/* + * sme_generic_change_country_code() - + * Change Country code from upperlayer during WLAN driver operation. + * This is a synchronous API. + * + * mac_handle - The handle returned by mac_open. + * pCountry New Country Code String + * reg_domain regulatory domain + * Return QDF_STATUS SUCCESS. + * FAILURE or RESOURCES The API finished and failed. + */ +QDF_STATUS sme_generic_change_country_code(mac_handle_t mac_handle, + uint8_t *pCountry) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg msg = {0}; + tAniGenericChangeCountryCodeReq *pMsg; + + if (!mac) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_FATAL, + "%s: mac is null", __func__); + return status; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + pMsg = qdf_mem_malloc(sizeof(tAniGenericChangeCountryCodeReq)); + if (!pMsg) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + pMsg->msgType = eWNI_SME_GENERIC_CHANGE_COUNTRY_CODE; + pMsg->msgLen = + (uint16_t) sizeof(tAniGenericChangeCountryCodeReq); + qdf_mem_copy(pMsg->countryCode, pCountry, 2); + pMsg->countryCode[2] = ' '; + + msg.type = eWNI_SME_GENERIC_CHANGE_COUNTRY_CODE; + msg.bodyptr = pMsg; + msg.reserved = 0; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &msg)) { + qdf_mem_free(pMsg); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_dhcp_start_ind() - + * API to signal the FW about the DHCP Start event. + * + * mac_handle: Opaque handle to the global MAC context. + * device_mode - mode(AP,SAP etc) of the device. + * macAddr - MAC address of the adapter. + * sessionId - session ID. + * Return QDF_STATUS SUCCESS. + * FAILURE or RESOURCES The API finished and failed. + */ +QDF_STATUS sme_dhcp_start_ind(mac_handle_t mac_handle, + uint8_t device_mode, + uint8_t *macAddr, uint8_t sessionId) +{ + QDF_STATUS status; + QDF_STATUS qdf_status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tAniDHCPInd *pMsg; + struct csr_roam_session *pSession; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("Session: %d not found", sessionId); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + pSession->dhcp_done = false; + + pMsg = qdf_mem_malloc(sizeof(tAniDHCPInd)); + if (!pMsg) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + pMsg->msgType = WMA_DHCP_START_IND; + pMsg->msgLen = (uint16_t) sizeof(tAniDHCPInd); + pMsg->device_mode = device_mode; + qdf_mem_copy(pMsg->adapterMacAddr.bytes, macAddr, + QDF_MAC_ADDR_SIZE); + qdf_copy_macaddr(&pMsg->peerMacAddr, + &pSession->connectedProfile.bssid); + + message.type = WMA_DHCP_START_IND; + message.bodyptr = pMsg; + message.reserved = 0; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + sessionId, message.type)); + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Post DHCP Start MSG fail", __func__); + qdf_mem_free(pMsg); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_dhcp_stop_ind() - + * API to signal the FW about the DHCP complete event. + * + * mac_handle: Opaque handle to the global MAC context. + * device_mode - mode(AP, SAP etc) of the device. + * macAddr - MAC address of the adapter. + * sessionId - session ID. + * Return QDF_STATUS SUCCESS. + * FAILURE or RESOURCES The API finished and failed. + */ +QDF_STATUS sme_dhcp_stop_ind(mac_handle_t mac_handle, + uint8_t device_mode, + uint8_t *macAddr, uint8_t sessionId) +{ + QDF_STATUS status; + QDF_STATUS qdf_status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tAniDHCPInd *pMsg; + struct csr_roam_session *pSession; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("Session: %d not found", sessionId); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + pSession->dhcp_done = true; + + pMsg = qdf_mem_malloc(sizeof(tAniDHCPInd)); + if (!pMsg) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + pMsg->msgType = WMA_DHCP_STOP_IND; + pMsg->msgLen = (uint16_t) sizeof(tAniDHCPInd); + pMsg->device_mode = device_mode; + qdf_mem_copy(pMsg->adapterMacAddr.bytes, macAddr, + QDF_MAC_ADDR_SIZE); + qdf_copy_macaddr(&pMsg->peerMacAddr, + &pSession->connectedProfile.bssid); + + message.type = WMA_DHCP_STOP_IND; + message.bodyptr = pMsg; + message.reserved = 0; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + sessionId, message.type)); + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Post DHCP Stop MSG fail", __func__); + qdf_mem_free(pMsg); + status = QDF_STATUS_E_FAILURE; + } + + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_TXFailMonitorStopInd() - + * API to signal the FW to start monitoring TX failures + * + * Return QDF_STATUS SUCCESS. + * FAILURE or RESOURCES The API finished and failed. + */ +QDF_STATUS sme_tx_fail_monitor_start_stop_ind(mac_handle_t mac_handle, + uint8_t tx_fail_count, + void *txFailIndCallback) +{ + QDF_STATUS status; + QDF_STATUS qdf_status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tAniTXFailMonitorInd *pMsg; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + pMsg = qdf_mem_malloc(sizeof(tAniTXFailMonitorInd)); + if (!pMsg) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + pMsg->msgType = WMA_TX_FAIL_MONITOR_IND; + pMsg->msgLen = (uint16_t) sizeof(tAniTXFailMonitorInd); + + /* tx_fail_count = 0 should disable the Monitoring in FW */ + pMsg->tx_fail_count = tx_fail_count; + pMsg->txFailIndCallback = txFailIndCallback; + + message.type = WMA_TX_FAIL_MONITOR_IND; + message.bodyptr = pMsg; + message.reserved = 0; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Post TX Fail monitor Start MSG fail", + __func__); + qdf_mem_free(pMsg); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_neighbor_report_request() - + * API to request neighbor report. + * + * mac_handle - The handle returned by mac_open. + * pRrmNeighborReq - Pointer to a caller allocated object of type + * tRrmNeighborReq. Caller owns the memory and is + * responsible for freeing it. + * Return QDF_STATUS + * QDF_STATUS_E_FAILURE - failure + * QDF_STATUS_SUCCESS success + */ +QDF_STATUS sme_neighbor_report_request( + mac_handle_t mac_handle, + uint8_t sessionId, + tpRrmNeighborReq pRrmNeighborReq, + tpRrmNeighborRspCallbackInfo callbackInfo) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_NEIGHBOR_REPORTREQ, NO_SESSION, + 0)); + + if (pRrmNeighborReq->neighbor_report_offload) { + status = csr_invoke_neighbor_report_request(sessionId, + pRrmNeighborReq, + false); + return status; + } + + if (QDF_STATUS_SUCCESS == sme_acquire_global_lock(&mac->sme)) { + status = + sme_rrm_neighbor_report_request(mac, sessionId, + pRrmNeighborReq, callbackInfo); + sme_release_global_lock(&mac->sme); + } + + return status; +} + +#ifdef FEATURE_OEM_DATA_SUPPORT +QDF_STATUS sme_oem_req_cmd(mac_handle_t mac_handle, + struct oem_data_req *oem_req) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct oem_data_req *oem_data_req; + void *wma_handle; + + SME_ENTER(); + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + sme_err("wma_handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + oem_data_req = qdf_mem_malloc(sizeof(*oem_data_req)); + if (!oem_data_req) + return QDF_STATUS_E_NOMEM; + + oem_data_req->data_len = oem_req->data_len; + oem_data_req->data = qdf_mem_malloc(oem_data_req->data_len); + if (!oem_data_req->data) { + qdf_mem_free(oem_data_req); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_copy(oem_data_req->data, oem_req->data, + oem_data_req->data_len); + + status = wma_start_oem_req_cmd(wma_handle, oem_data_req); + + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("Post oem data request msg fail"); + else + sme_debug("OEM request(length: %d) sent to WMA", + oem_data_req->data_len); + + if (oem_data_req->data_len) + qdf_mem_free(oem_data_req->data); + qdf_mem_free(oem_data_req); + + SME_EXIT(); + return status; +} +#endif /*FEATURE_OEM_DATA_SUPPORT */ + +#ifdef FEATURE_OEM_DATA +QDF_STATUS sme_oem_data_cmd(mac_handle_t mac_handle, + void (*oem_data_event_handler_cb) + (const struct oem_data *oem_event_data, + uint8_t vdev_id), + struct oem_data *oem_data, + uint8_t vdev_id) +{ + QDF_STATUS status; + void *wma_handle; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + SME_ENTER(); + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + sme_err("wma_handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.oem_data_event_handler_cb = oem_data_event_handler_cb; + mac->sme.oem_data_vdev_id = vdev_id; + sme_release_global_lock(&mac->sme); + } + status = wma_start_oem_data_cmd(wma_handle, oem_data); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("fail to call wma_start_oem_data_cmd."); + + SME_EXIT(); + return status; +} +#endif + +#define STA_NSS_CHAINS_SHIFT 0 +#define SAP_NSS_CHAINS_SHIFT 3 +#define P2P_GO_NSS_CHAINS_SHIFT 6 +#define P2P_CLI_CHAINS_SHIFT 9 +#define TDLS_NSS_CHAINS_SHIFT 12 +#define IBSS_NSS_CHAINS_SHIFT 15 +#define P2P_DEV_NSS_CHAINS_SHIFT 18 +#define OCB_NSS_CHAINS_SHIFT 21 +#define NAN_NSS_CHAIN_SHIFT 24 +#define NSS_CHAIN_MASK 0x7 +#define GET_VDEV_NSS_CHAIN(x, y) (((x) >> (y)) & NSS_CHAIN_MASK) + +static uint8_t sme_get_nss_chain_shift(enum QDF_OPMODE device_mode) +{ + switch (device_mode) { + case QDF_STA_MODE: + return STA_NSS_CHAINS_SHIFT; + case QDF_SAP_MODE: + return SAP_NSS_CHAINS_SHIFT; + case QDF_P2P_GO_MODE: + return P2P_GO_NSS_CHAINS_SHIFT; + case QDF_P2P_CLIENT_MODE: + return P2P_CLI_CHAINS_SHIFT; + case QDF_IBSS_MODE: + return IBSS_NSS_CHAINS_SHIFT; + case QDF_P2P_DEVICE_MODE: + return P2P_DEV_NSS_CHAINS_SHIFT; + case QDF_OCB_MODE: + return OCB_NSS_CHAINS_SHIFT; + case QDF_TDLS_MODE: + return TDLS_NSS_CHAINS_SHIFT; + + default: + sme_err("Device mode %d invalid", device_mode); + return STA_NSS_CHAINS_SHIFT; + } +} + +static void +sme_check_nss_chain_ini_param(struct wlan_mlme_nss_chains *vdev_ini_cfg, + uint8_t rf_chains_supported, + enum nss_chains_band_info band) +{ + vdev_ini_cfg->rx_nss[band] = QDF_MIN(vdev_ini_cfg->rx_nss[band], + rf_chains_supported); + vdev_ini_cfg->tx_nss[band] = QDF_MIN(vdev_ini_cfg->tx_nss[band], + rf_chains_supported); +} + +static void +sme_fill_nss_chain_params(struct mac_context *mac_ctx, + struct wlan_mlme_nss_chains *vdev_ini_cfg, + enum QDF_OPMODE device_mode, + enum nss_chains_band_info band, + uint8_t rf_chains_supported) +{ + uint8_t nss_chain_shift; + uint8_t max_supported_nss; + struct wlan_mlme_nss_chains *nss_chains_ini_cfg = + &mac_ctx->mlme_cfg->nss_chains_ini_cfg; + + nss_chain_shift = sme_get_nss_chain_shift(device_mode); + max_supported_nss = mac_ctx->mlme_cfg->vht_caps.vht_cap_info.enable2x2 ? + MAX_VDEV_NSS : 1; + + /* + * If target supports Antenna sharing, set NSS to 1 for 2.4GHz band for + * NDI vdev. + */ + if (device_mode == QDF_NDI_MODE && mac_ctx->lteCoexAntShare && + band == NSS_CHAINS_BAND_2GHZ) + max_supported_nss = NSS_1x1_MODE; + + /* If the fw doesn't support two chains, num rf chains can max be 1 */ + vdev_ini_cfg->num_rx_chains[band] = + QDF_MIN(GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg->num_rx_chains[band], + nss_chain_shift), rf_chains_supported); + + vdev_ini_cfg->num_tx_chains[band] = + QDF_MIN(GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg->num_tx_chains[band], + nss_chain_shift), rf_chains_supported); + + /* If 2x2 mode is disabled, then max rx, tx nss can be 1 */ + vdev_ini_cfg->rx_nss[band] = + QDF_MIN(GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg->rx_nss[band], + nss_chain_shift), max_supported_nss); + + vdev_ini_cfg->tx_nss[band] = + QDF_MIN(GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg->tx_nss[band], + nss_chain_shift), max_supported_nss); + + vdev_ini_cfg->num_tx_chains_11a = + QDF_MIN(GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg->num_tx_chains_11a, + nss_chain_shift), rf_chains_supported); + + /* If the fw doesn't support two chains, num rf chains can max be 1 */ + vdev_ini_cfg->num_tx_chains_11b = + QDF_MIN(GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg->num_tx_chains_11b, + nss_chain_shift), rf_chains_supported); + + vdev_ini_cfg->num_tx_chains_11g = + QDF_MIN(GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg->num_tx_chains_11g, + nss_chain_shift), rf_chains_supported); + + vdev_ini_cfg->disable_rx_mrc[band] = + nss_chains_ini_cfg->disable_rx_mrc[band]; + + vdev_ini_cfg->disable_tx_mrc[band] = + nss_chains_ini_cfg->disable_tx_mrc[band]; + /* + * Check whether the rx/tx nss is greater than the number of rf chains + * supported by FW, if so downgrade the nss to the number of chains + * supported, as higher nss cannot be supported with less chains. + */ + sme_check_nss_chain_ini_param(vdev_ini_cfg, rf_chains_supported, + band); + +} + +void sme_populate_nss_chain_params(mac_handle_t mac_handle, + struct wlan_mlme_nss_chains *vdev_ini_cfg, + enum QDF_OPMODE device_mode, + uint8_t rf_chains_supported) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + enum nss_chains_band_info band; + + for (band = NSS_CHAINS_BAND_2GHZ; band < NSS_CHAINS_BAND_MAX; band++) + sme_fill_nss_chain_params(mac_ctx, vdev_ini_cfg, + device_mode, band, + rf_chains_supported); +} + +void +sme_store_nss_chains_cfg_in_vdev(struct wlan_objmgr_vdev *vdev, + struct wlan_mlme_nss_chains *vdev_ini_cfg) +{ + struct wlan_mlme_nss_chains *ini_cfg; + struct wlan_mlme_nss_chains *dynamic_cfg; + + ini_cfg = mlme_get_ini_vdev_config(vdev); + dynamic_cfg = mlme_get_dynamic_vdev_config(vdev); + + if (!ini_cfg || !dynamic_cfg) { + sme_err("Nss chains ini/dynamic config NULL vdev_id %d", + vdev->vdev_objmgr.vdev_id); + return; + } + + *ini_cfg = *vdev_ini_cfg; + *dynamic_cfg = *vdev_ini_cfg; +} + +static void +sme_populate_user_config(struct wlan_mlme_nss_chains *dynamic_cfg, + struct wlan_mlme_nss_chains *user_cfg, + enum nss_chains_band_info band) +{ + if (!user_cfg->num_rx_chains[band]) + user_cfg->num_rx_chains[band] = + dynamic_cfg->num_rx_chains[band]; + + if (!user_cfg->num_tx_chains[band]) + user_cfg->num_tx_chains[band] = + dynamic_cfg->num_tx_chains[band]; + + if (!user_cfg->rx_nss[band]) + user_cfg->rx_nss[band] = + dynamic_cfg->rx_nss[band]; + + if (!user_cfg->tx_nss[band]) + user_cfg->tx_nss[band] = + dynamic_cfg->tx_nss[band]; + + if (!user_cfg->num_tx_chains_11a) + user_cfg->num_tx_chains_11a = + dynamic_cfg->num_tx_chains_11a; + + if (!user_cfg->num_tx_chains_11b) + user_cfg->num_tx_chains_11b = + dynamic_cfg->num_tx_chains_11b; + + if (!user_cfg->num_tx_chains_11g) + user_cfg->num_tx_chains_11g = + dynamic_cfg->num_tx_chains_11g; + + if (!user_cfg->disable_rx_mrc[band]) + user_cfg->disable_rx_mrc[band] = + dynamic_cfg->disable_rx_mrc[band]; + + if (!user_cfg->disable_tx_mrc[band]) + user_cfg->disable_tx_mrc[band] = + dynamic_cfg->disable_tx_mrc[band]; +} + +static QDF_STATUS +sme_validate_from_ini_config(struct wlan_mlme_nss_chains *user_cfg, + struct wlan_mlme_nss_chains *ini_cfg, + enum nss_chains_band_info band) +{ + if (user_cfg->num_rx_chains[band] > + ini_cfg->num_rx_chains[band]) + return QDF_STATUS_E_FAILURE; + + if (user_cfg->num_tx_chains[band] > + ini_cfg->num_tx_chains[band]) + return QDF_STATUS_E_FAILURE; + + if (user_cfg->rx_nss[band] > + ini_cfg->rx_nss[band]) + return QDF_STATUS_E_FAILURE; + + if (user_cfg->tx_nss[band] > + ini_cfg->tx_nss[band]) + return QDF_STATUS_E_FAILURE; + + if (user_cfg->num_tx_chains_11a > + ini_cfg->num_tx_chains_11a) + return QDF_STATUS_E_FAILURE; + + if (user_cfg->num_tx_chains_11b > + ini_cfg->num_tx_chains_11b) + return QDF_STATUS_E_FAILURE; + + if (user_cfg->num_tx_chains_11g > + ini_cfg->num_tx_chains_11g) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +sme_validate_user_nss_chain_params( + struct wlan_mlme_nss_chains *user_cfg, + enum nss_chains_band_info band) +{ + /* Reject as 2x1 modes are not supported in chains yet */ + + if (user_cfg->num_tx_chains[band] > + user_cfg->num_rx_chains[band]) + return QDF_STATUS_E_FAILURE; + + /* Also if mode is 2x2, we cant have chains as 1x1, or 1x2, or 2x1 */ + + if (user_cfg->tx_nss[band] > + user_cfg->num_tx_chains[band]) + user_cfg->num_tx_chains[band] = + user_cfg->tx_nss[band]; + + if (user_cfg->rx_nss[band] > + user_cfg->num_rx_chains[band]) + user_cfg->num_rx_chains[band] = + user_cfg->rx_nss[band]; + + /* + * It may happen that already chains are in 1x1 mode and nss too + * is in 1x1 mode, but the tx 11a/b/g chains in user config comes + * as 2x1, or 1x2 which cannot support respective mode, as tx chains + * for respective band have max of 1x1 only, so these cannot exceed + * respective band num tx chains. + */ + + if (user_cfg->num_tx_chains_11a > + user_cfg->num_tx_chains[NSS_CHAINS_BAND_5GHZ]) + user_cfg->num_tx_chains_11a = + user_cfg->num_tx_chains[NSS_CHAINS_BAND_5GHZ]; + + if (user_cfg->num_tx_chains_11b > + user_cfg->num_tx_chains[NSS_CHAINS_BAND_2GHZ]) + user_cfg->num_tx_chains_11b = + user_cfg->num_tx_chains[NSS_CHAINS_BAND_2GHZ]; + + if (user_cfg->num_tx_chains_11g > + user_cfg->num_tx_chains[NSS_CHAINS_BAND_2GHZ]) + user_cfg->num_tx_chains_11g = + user_cfg->num_tx_chains[NSS_CHAINS_BAND_2GHZ]; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +sme_validate_nss_chains_config(struct wlan_objmgr_vdev *vdev, + struct wlan_mlme_nss_chains *user_cfg, + struct wlan_mlme_nss_chains *dynamic_cfg) +{ + enum nss_chains_band_info band; + struct wlan_mlme_nss_chains *ini_cfg; + QDF_STATUS status; + + ini_cfg = mlme_get_ini_vdev_config(vdev); + if (!ini_cfg) { + sme_err("nss chain ini config NULL"); + return QDF_STATUS_E_FAILURE; + } + + for (band = NSS_CHAINS_BAND_2GHZ; band < NSS_CHAINS_BAND_MAX; band++) { + sme_populate_user_config(dynamic_cfg, + user_cfg, band); + status = sme_validate_from_ini_config(user_cfg, + ini_cfg, + band); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Validation from ini config failed"); + return QDF_STATUS_E_FAILURE; + } + status = sme_validate_user_nss_chain_params(user_cfg, + band); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("User cfg validation failed"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +static bool +sme_is_nss_update_allowed(struct wlan_mlme_chain_cfg chain_cfg, + uint8_t rx_nss, uint8_t tx_nss, + enum nss_chains_band_info band) +{ + switch (band) { + case NSS_CHAINS_BAND_2GHZ: + if (rx_nss > chain_cfg.max_rx_chains_2g) + return false; + if (tx_nss > chain_cfg.max_tx_chains_2g) + return false; + break; + case NSS_CHAINS_BAND_5GHZ: + if (rx_nss > chain_cfg.max_rx_chains_5g) + return false; + if (tx_nss > chain_cfg.max_tx_chains_5g) + return false; + break; + default: + sme_err("Unknown Band nss change not allowed"); + return false; + } + return true; +} + +static void sme_modify_chains_in_mlme_cfg(mac_handle_t mac_handle, + uint8_t rx_chains, + uint8_t tx_chains, + enum QDF_OPMODE vdev_op_mode, + enum nss_chains_band_info band) +{ + uint8_t nss_shift; + uint32_t nss_mask = 0x7; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + nss_shift = sme_get_nss_chain_shift(vdev_op_mode); + + mac_ctx->mlme_cfg->nss_chains_ini_cfg.num_rx_chains[band] &= + ~(nss_mask << nss_shift); + mac_ctx->mlme_cfg->nss_chains_ini_cfg.num_rx_chains[band] |= + (rx_chains << nss_shift); + mac_ctx->mlme_cfg->nss_chains_ini_cfg.num_tx_chains[band] &= + ~(nss_mask << nss_shift); + mac_ctx->mlme_cfg->nss_chains_ini_cfg.num_tx_chains[band] |= + (tx_chains << nss_shift); + sme_debug("rx chains %d tx chains %d changed for vdev mode %d for band %d", + rx_chains, tx_chains, vdev_op_mode, band); +} + +static void +sme_modify_nss_in_mlme_cfg(mac_handle_t mac_handle, + uint8_t rx_nss, uint8_t tx_nss, + enum QDF_OPMODE vdev_op_mode, + enum nss_chains_band_info band) +{ + uint8_t nss_shift; + uint32_t nss_mask = 0x7; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + if (!sme_is_nss_update_allowed(mac_ctx->fw_chain_cfg, rx_nss, tx_nss, + band)) { + sme_debug("Nss modification failed, fw doesn't support this nss %d", + rx_nss); + return; + } + + nss_shift = sme_get_nss_chain_shift(vdev_op_mode); + + mac_ctx->mlme_cfg->nss_chains_ini_cfg.rx_nss[band] &= + ~(nss_mask << nss_shift); + mac_ctx->mlme_cfg->nss_chains_ini_cfg.rx_nss[band] |= + (rx_nss << nss_shift); + mac_ctx->mlme_cfg->nss_chains_ini_cfg.tx_nss[band] &= + ~(nss_mask << nss_shift); + mac_ctx->mlme_cfg->nss_chains_ini_cfg.tx_nss[band] |= + (tx_nss << nss_shift); + sme_debug("rx nss %d tx nss %d changed for vdev mode %d for band %d", + rx_nss, tx_nss, vdev_op_mode, band); +} + +void +sme_modify_nss_chains_tgt_cfg(mac_handle_t mac_handle, + enum QDF_OPMODE vdev_op_mode, + enum nss_chains_band_info band) +{ + uint8_t ini_rx_nss; + uint8_t ini_tx_nss; + uint8_t max_supported_rx_nss = MAX_VDEV_NSS; + uint8_t max_supported_tx_nss = MAX_VDEV_NSS; + uint8_t ini_rx_chains; + uint8_t ini_tx_chains; + uint8_t max_supported_rx_chains = MAX_VDEV_CHAINS; + uint8_t max_supported_tx_chains = MAX_VDEV_CHAINS; + + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct wlan_mlme_nss_chains *nss_chains_ini_cfg = + &mac_ctx->mlme_cfg->nss_chains_ini_cfg; + uint8_t nss_shift = sme_get_nss_chain_shift(vdev_op_mode); + struct wlan_mlme_chain_cfg chain_cfg = mac_ctx->fw_chain_cfg; + + ini_rx_nss = GET_VDEV_NSS_CHAIN(nss_chains_ini_cfg->rx_nss[band], + nss_shift); + ini_tx_nss = GET_VDEV_NSS_CHAIN(nss_chains_ini_cfg->tx_nss[band], + nss_shift); + + if (band == NSS_CHAINS_BAND_2GHZ) { + max_supported_rx_nss = chain_cfg.max_rx_chains_2g; + max_supported_tx_nss = chain_cfg.max_tx_chains_2g; + } else if (band == NSS_CHAINS_BAND_5GHZ) { + max_supported_rx_nss = chain_cfg.max_rx_chains_5g; + max_supported_tx_nss = chain_cfg.max_tx_chains_5g; + } + + max_supported_rx_nss = QDF_MIN(ini_rx_nss, max_supported_rx_nss); + if (!max_supported_rx_nss) + return; + + max_supported_tx_nss = QDF_MIN(ini_tx_nss, max_supported_tx_nss); + if (!max_supported_tx_nss) + return; + + ini_rx_chains = GET_VDEV_NSS_CHAIN(nss_chains_ini_cfg-> + num_rx_chains[band], + nss_shift); + ini_tx_chains = GET_VDEV_NSS_CHAIN(nss_chains_ini_cfg-> + num_tx_chains[band], + nss_shift); + + if (band == NSS_CHAINS_BAND_2GHZ) { + max_supported_rx_chains = chain_cfg.max_rx_chains_2g; + max_supported_tx_chains = chain_cfg.max_tx_chains_2g; + } else if (band == NSS_CHAINS_BAND_5GHZ) { + max_supported_rx_chains = chain_cfg.max_rx_chains_5g; + max_supported_tx_chains = chain_cfg.max_tx_chains_5g; + } + + max_supported_rx_chains = QDF_MIN(ini_rx_chains, + max_supported_rx_chains); + if (!max_supported_rx_chains) + return; + + max_supported_tx_chains = QDF_MIN(ini_tx_chains, + max_supported_tx_chains); + if (!max_supported_tx_chains) + return; + + sme_modify_chains_in_mlme_cfg(mac_handle, max_supported_rx_chains, + max_supported_tx_chains, vdev_op_mode, + band); + sme_modify_nss_in_mlme_cfg(mac_handle, max_supported_rx_nss, + max_supported_tx_nss, vdev_op_mode, band); +} + +void +sme_update_nss_in_mlme_cfg(mac_handle_t mac_handle, + uint8_t rx_nss, uint8_t tx_nss, + enum QDF_OPMODE vdev_op_mode, + enum nss_chains_band_info band) +{ + /* + * If device mode is P2P-DEVICE, then we want P2P to come in that + * particular nss, then we should change the nss of P@P-CLI, and GO + * and we are unaware that for what will be the device mode after + * negotiation yet. + */ + + if (vdev_op_mode == QDF_P2P_DEVICE_MODE || + vdev_op_mode == QDF_P2P_CLIENT_MODE || + vdev_op_mode == QDF_P2P_GO_MODE) { + sme_modify_nss_in_mlme_cfg(mac_handle, rx_nss, tx_nss, + QDF_P2P_CLIENT_MODE, band); + sme_modify_nss_in_mlme_cfg(mac_handle, rx_nss, tx_nss, + QDF_P2P_GO_MODE, band); + sme_modify_nss_in_mlme_cfg(mac_handle, rx_nss, tx_nss, + QDF_P2P_DEVICE_MODE, band); + } else + sme_modify_nss_in_mlme_cfg(mac_handle, rx_nss, tx_nss, + vdev_op_mode, band); +} + +QDF_STATUS +sme_nss_chains_update(mac_handle_t mac_handle, + struct wlan_mlme_nss_chains *user_cfg, + uint8_t vdev_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + struct wlan_mlme_nss_chains *dynamic_cfg; + struct wlan_objmgr_vdev *vdev = + wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Got NULL vdev obj, returning"); + return QDF_STATUS_E_FAILURE; + } + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_ERROR(status)) + goto release_ref; + + dynamic_cfg = mlme_get_dynamic_vdev_config(vdev); + if (!dynamic_cfg) { + sme_err("nss chain dynamic config NULL"); + status = QDF_STATUS_E_FAILURE; + goto release_lock; + } + + status = sme_validate_nss_chains_config(vdev, user_cfg, + dynamic_cfg); + if (QDF_IS_STATUS_ERROR(status)) + goto release_lock; + + if (!qdf_mem_cmp(dynamic_cfg, user_cfg, + sizeof(struct wlan_mlme_nss_chains))) { + sme_debug("current config same as user config"); + status = QDF_STATUS_SUCCESS; + goto release_lock; + } + sme_debug("User params verified, sending to fw vdev id %d", vdev_id); + + status = wma_vdev_nss_chain_params_send(vdev->vdev_objmgr.vdev_id, + user_cfg); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("params sent failed to fw vdev id %d", vdev_id); + goto release_lock; + } + + *dynamic_cfg = *user_cfg; + +release_lock: + sme_release_global_lock(&mac_ctx->sme); + +release_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return status; +} + +struct wlan_objmgr_vdev *sme_vdev_create(mac_handle_t mac_handle, + struct wlan_vdev_create_params *vdev_params) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t vdev_id; + struct wlan_objmgr_vdev *vdev; + struct vdev_mlme_obj *vdev_mlme; + + sme_debug("addr:"QDF_MAC_ADDR_FMT" opmode:%d", + QDF_MAC_ADDR_REF(vdev_params->macaddr), + vdev_params->opmode); + + vdev = wlan_objmgr_vdev_obj_create(mac_ctx->pdev, vdev_params); + if (!vdev) { + sme_err("Failed to create vdev object"); + return NULL; + } + + if (wlan_objmgr_vdev_try_get_ref(vdev, WLAN_LEGACY_SME_ID) != + QDF_STATUS_SUCCESS) { + wlan_objmgr_vdev_obj_delete(vdev); + return NULL; + } + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + sme_err("Failed to get vdev mlme obj!"); + QDF_BUG(0); + goto vdev_obj_del; + } + + vdev_id = wlan_vdev_get_id(vdev); + status = wma_post_vdev_create_setup(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to setup wma for vdev: %d", vdev_id); + goto vdev_obj_del; + } + + status = csr_setup_vdev_session(vdev_mlme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to setup CSR layer for vdev: %d", vdev_id); + goto cleanup_wma; + } + + status = mlme_vdev_self_peer_create(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to create vdev selfpeer for vdev:%d", vdev_id); + goto csr_cleanup_vdev_session; + } + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_OPEN_SESSION, + vdev_id, 0)); + + return vdev; + +csr_cleanup_vdev_session: + csr_cleanup_vdev_session(mac_ctx, vdev_id); +cleanup_wma: + wma_cleanup_vdev(vdev); +vdev_obj_del: + wlan_objmgr_vdev_obj_delete(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return NULL; +} + +QDF_STATUS sme_vdev_delete(mac_handle_t mac_handle, + struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + uint8_t vdev_id = wlan_vdev_get_id(vdev); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_CLOSE_SESSION, vdev_id, 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_roam_vdev_delete(mac, vdev_id, false); + sme_release_global_lock(&mac->sme); + } + + /* Release the reference acquired during vdev create */ + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return status; +} + +void sme_cleanup_session(mac_handle_t mac_handle, uint8_t vdev_id) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return; + csr_cleanup_vdev_session(mac, vdev_id); + sme_release_global_lock(&mac->sme); +} + +/* + * sme_change_mcc_beacon_interval() - + * To update P2P-GO beaconInterval. This function should be called after + * disassociating all the station is done + * This is an asynchronous API. + * + * @sessionId: Session Identifier + * Return QDF_STATUS SUCCESS + * FAILURE or RESOURCES + * The API finished and failed. + */ +QDF_STATUS sme_change_mcc_beacon_interval(uint8_t sessionId) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = sme_get_mac_context(); + + if (!mac_ctx) { + sme_err("mac_ctx is NULL"); + return status; + } + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_send_chng_mcc_beacon_interval(mac_ctx, + sessionId); + sme_release_global_lock(&mac_ctx->sme); + } + return status; +} + +/** + * sme_set_host_offload(): API to set the host offload feature. + * @mac_handle: The handle returned by mac_open. + * @sessionId: Session Identifier + * @request: Pointer to the offload request. + * + * Return QDF_STATUS + */ +QDF_STATUS sme_set_host_offload(mac_handle_t mac_handle, uint8_t sessionId, + struct sir_host_offload_req *request) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_SET_HOSTOFFLOAD, sessionId, 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { +#ifdef WLAN_NS_OFFLOAD + if (SIR_IPV6_NS_OFFLOAD == request->offloadType) { + status = sme_set_ps_ns_offload(mac_handle, request, + sessionId); + } else +#endif /* WLAN_NS_OFFLOAD */ + { + status = sme_set_ps_host_offload(mac_handle, request, + sessionId); + } + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_set_keep_alive() - + * API to set the Keep Alive feature. + * + * mac_handle - The handle returned by mac_open. + * request - Pointer to the Keep Alive request. + * Return QDF_STATUS + */ +QDF_STATUS sme_set_keep_alive(mac_handle_t mac_handle, uint8_t session_id, + struct keep_alive_req *request) +{ + struct keep_alive_req *request_buf; + struct scheduler_msg msg = {0}; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, session_id); + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("WMA_SET_KEEP_ALIVE message")); + + if (!pSession) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Session not Found")); + return QDF_STATUS_E_FAILURE; + } + request_buf = qdf_mem_malloc(sizeof(*request_buf)); + if (!request_buf) + return QDF_STATUS_E_NOMEM; + + qdf_copy_macaddr(&request->bssid, &pSession->connectedProfile.bssid); + qdf_mem_copy(request_buf, request, sizeof(*request_buf)); + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "buff TP %d input TP %d ", request_buf->timePeriod, + request->timePeriod); + request_buf->sessionId = session_id; + + msg.type = WMA_SET_KEEP_ALIVE; + msg.reserved = 0; + msg.bodyptr = request_buf; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + session_id, msg.type)); + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Not able to post WMA_SET_KEEP_ALIVE message to WMA"); + qdf_mem_free(request_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_get_operation_channel() - + * API to get current channel on which STA is parked his function gives + * channel information only of infra station or IBSS station + * + * mac_handle, pointer to memory location and sessionId + * Returns QDF_STATUS_SUCCESS + * QDF_STATUS_E_FAILURE + */ +QDF_STATUS sme_get_operation_channel(mac_handle_t mac_handle, + uint32_t *chan_freq, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession; + + if (CSR_IS_SESSION_VALID(mac, sessionId)) { + pSession = CSR_GET_SESSION(mac, sessionId); + + if ((pSession->connectedProfile.BSSType == + eCSR_BSS_TYPE_INFRASTRUCTURE) + || (pSession->connectedProfile.BSSType == + eCSR_BSS_TYPE_IBSS) + || (pSession->connectedProfile.BSSType == + eCSR_BSS_TYPE_INFRA_AP) + || (pSession->connectedProfile.BSSType == + eCSR_BSS_TYPE_START_IBSS)) { + *chan_freq = pSession->connectedProfile.op_freq; + return QDF_STATUS_SUCCESS; + } + } + return QDF_STATUS_E_FAILURE; +} /* sme_get_operation_channel ends here */ + +/** + * sme_register_mgmt_frame_ind_callback() - Register a callback for + * management frame indication to PE. + * + * @mac_handle: Opaque handle to the global MAC context + * @callback: callback pointer to be registered + * + * This function is used to register a callback for management + * frame indication to PE. + * + * Return: Success if msg is posted to PE else Failure. + */ +QDF_STATUS sme_register_mgmt_frame_ind_callback(mac_handle_t mac_handle, + sir_mgmt_frame_ind_callback callback) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct sir_sme_mgmt_frame_cb_req *msg; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (QDF_STATUS_SUCCESS == + sme_acquire_global_lock(&mac_ctx->sme)) { + msg = qdf_mem_malloc(sizeof(*msg)); + if (!msg) { + sme_release_global_lock(&mac_ctx->sme); + return QDF_STATUS_E_NOMEM; + } + msg->message_type = eWNI_SME_REGISTER_MGMT_FRAME_CB; + msg->length = sizeof(*msg); + + msg->callback = callback; + status = umac_send_mb_message_to_mac(msg); + sme_release_global_lock(&mac_ctx->sme); + return status; + } + return QDF_STATUS_E_FAILURE; +} + +/* + * sme_RegisterMgtFrame() - + * To register management frame of specified type and subtype. + * + * frameType - type of the frame that needs to be passed to HDD. + * matchData - data which needs to be matched before passing frame + * to HDD. + * matchDataLen - Length of matched data. + * Return QDF_STATUS + */ +QDF_STATUS sme_register_mgmt_frame(mac_handle_t mac_handle, uint8_t sessionId, + uint16_t frameType, uint8_t *matchData, + uint16_t matchLen) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + struct register_mgmt_frame *pMsg; + uint16_t len; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, + sessionId); + + if (!CSR_IS_SESSION_ANY(sessionId) && !pSession) { + sme_err("Session %d not found", sessionId); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + if (!CSR_IS_SESSION_ANY(sessionId) && + !pSession->sessionActive) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s Invalid Sessionid", __func__); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(*pMsg) + matchLen; + + pMsg = qdf_mem_malloc(len); + if (!pMsg) + status = QDF_STATUS_E_NOMEM; + else { + pMsg->messageType = eWNI_SME_REGISTER_MGMT_FRAME_REQ; + pMsg->length = len; + pMsg->sessionId = sessionId; + pMsg->registerFrame = true; + pMsg->frameType = frameType; + pMsg->matchLen = matchLen; + qdf_mem_copy(pMsg->matchData, matchData, matchLen); + status = umac_send_mb_message_to_mac(pMsg); + } + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_DeregisterMgtFrame() - + * To De-register management frame of specified type and subtype. + * + * frameType - type of the frame that needs to be passed to HDD. + * matchData - data which needs to be matched before passing frame + * to HDD. + * matchDataLen - Length of matched data. + * Return QDF_STATUS + */ +QDF_STATUS sme_deregister_mgmt_frame(mac_handle_t mac_handle, uint8_t sessionId, + uint16_t frameType, uint8_t *matchData, + uint16_t matchLen) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_DEREGISTER_MGMTFR, sessionId, + 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + struct register_mgmt_frame *pMsg; + uint16_t len; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, + sessionId); + + if (!CSR_IS_SESSION_ANY(sessionId) && !pSession) { + sme_err("Session %d not found", sessionId); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + if (!CSR_IS_SESSION_ANY(sessionId) && + !pSession->sessionActive) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s Invalid Sessionid", __func__); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(*pMsg) + matchLen; + + pMsg = qdf_mem_malloc(len); + if (!pMsg) + status = QDF_STATUS_E_NOMEM; + else { + pMsg->messageType = eWNI_SME_REGISTER_MGMT_FRAME_REQ; + pMsg->length = len; + pMsg->registerFrame = false; + pMsg->frameType = frameType; + pMsg->matchLen = matchLen; + qdf_mem_copy(pMsg->matchData, matchData, matchLen); + status = umac_send_mb_message_to_mac(pMsg); + } + sme_release_global_lock(&mac->sme); + } + return status; +} + +/** + * sme_prepare_mgmt_tx() - Prepares mgmt frame + * @mac_handle: The handle returned by mac_open + * @vdev_id: vdev id + * @buf: pointer to frame + * @len: frame length + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_prepare_mgmt_tx(mac_handle_t mac_handle, + uint8_t vdev_id, + const uint8_t *buf, uint32_t len) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct sir_mgmt_msg *msg; + uint16_t msg_len; + struct scheduler_msg sch_msg = {0}; + + sme_debug("prepares auth frame"); + + msg_len = sizeof(*msg) + len; + msg = qdf_mem_malloc(msg_len); + if (!msg) { + status = QDF_STATUS_E_NOMEM; + } else { + msg->type = eWNI_SME_SEND_MGMT_FRAME_TX; + msg->msg_len = msg_len; + msg->vdev_id = vdev_id; + msg->data = (uint8_t *)msg + sizeof(*msg); + qdf_mem_copy(msg->data, buf, len); + + sch_msg.type = eWNI_SME_SEND_MGMT_FRAME_TX; + sch_msg.bodyptr = msg; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &sch_msg); + } + return status; +} + +QDF_STATUS sme_send_mgmt_tx(mac_handle_t mac_handle, uint8_t session_id, + const uint8_t *buf, uint32_t len) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = sme_prepare_mgmt_tx(mac_handle, session_id, buf, len); + sme_release_global_lock(&mac->sme); + } + + return status; +} + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +/** + * sme_configure_ext_wow() - configure Extr WoW + * @mac_handle - The handle returned by mac_open. + * @wlanExtParams - Depicts the wlan Ext params. + * @callback - ext_wow callback to be registered. + * @callback_context - ext_wow callback context + * + * SME will pass this request to lower mac to configure Extr WoW + * Return: QDF_STATUS + */ +QDF_STATUS sme_configure_ext_wow(mac_handle_t mac_handle, + tpSirExtWoWParams wlanExtParams, + csr_readyToExtWoWCallback callback, + void *callback_context) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tpSirExtWoWParams MsgPtr = qdf_mem_malloc(sizeof(*MsgPtr)); + + if (!MsgPtr) + return QDF_STATUS_E_NOMEM; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_CONFIG_EXTWOW, NO_SESSION, 0)); + + mac->readyToExtWoWCallback = callback; + mac->readyToExtWoWContext = callback_context; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + + /* serialize the req through MC thread */ + qdf_mem_copy(MsgPtr, wlanExtParams, sizeof(*MsgPtr)); + message.bodyptr = MsgPtr; + message.type = WMA_WLAN_EXT_WOW; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + mac->readyToExtWoWCallback = NULL; + mac->readyToExtWoWContext = NULL; + qdf_mem_free(MsgPtr); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } else { + mac->readyToExtWoWCallback = NULL; + mac->readyToExtWoWContext = NULL; + qdf_mem_free(MsgPtr); + } + + return status; +} + +/* + * sme_configure_app_type1_params() - + * SME will pass this request to lower mac to configure Indoor WoW parameters. + * + * mac_handle - The handle returned by mac_open. + * wlanAppType1Params- Depicts the wlan App Type 1(Indoor) params + * Return QDF_STATUS + */ +QDF_STATUS sme_configure_app_type1_params(mac_handle_t mac_handle, + tpSirAppType1Params wlanAppType1Params) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tpSirAppType1Params MsgPtr = qdf_mem_malloc(sizeof(*MsgPtr)); + + if (!MsgPtr) + return QDF_STATUS_E_NOMEM; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_CONFIG_APP_TYPE1, NO_SESSION, + 0)); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* serialize the req through MC thread */ + qdf_mem_copy(MsgPtr, wlanAppType1Params, sizeof(*MsgPtr)); + message.bodyptr = MsgPtr; + message.type = WMA_WLAN_SET_APP_TYPE1_PARAMS; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + qdf_mem_free(MsgPtr); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } else { + qdf_mem_free(MsgPtr); + } + + return status; +} + +/* + * sme_configure_app_type2_params() - + * SME will pass this request to lower mac to configure Indoor WoW parameters. + * + * mac_handle - The handle returned by mac_open. + * wlanAppType2Params- Depicts the wlan App Type 2 (Outdoor) params + * Return QDF_STATUS + */ +QDF_STATUS sme_configure_app_type2_params(mac_handle_t mac_handle, + tpSirAppType2Params wlanAppType2Params) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tpSirAppType2Params MsgPtr = qdf_mem_malloc(sizeof(*MsgPtr)); + + if (!MsgPtr) + return QDF_STATUS_E_NOMEM; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_CONFIG_APP_TYPE2, NO_SESSION, + 0)); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* serialize the req through MC thread */ + qdf_mem_copy(MsgPtr, wlanAppType2Params, sizeof(*MsgPtr)); + message.bodyptr = MsgPtr; + message.type = WMA_WLAN_SET_APP_TYPE2_PARAMS; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + qdf_mem_free(MsgPtr); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } else { + qdf_mem_free(MsgPtr); + } + + return status; +} +#endif + +uint32_t sme_get_beaconing_concurrent_operation_channel(mac_handle_t mac_handle, + uint8_t vdev_id_to_skip) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + uint32_t chan_freq = 0; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + + chan_freq = csr_get_beaconing_concurrent_channel(mac, + vdev_id_to_skip); + sme_info("Other Concurrent Chan_freq: %d skipped vdev_id %d", + chan_freq, vdev_id_to_skip); + sme_release_global_lock(&mac->sme); + } + + return chan_freq; +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +uint16_t sme_check_concurrent_channel_overlap(mac_handle_t mac_handle, + uint16_t sap_ch_freq, + eCsrPhyMode sapPhyMode, + uint8_t cc_switch_mode) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + uint16_t intf_ch_freq = 0; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + intf_ch_freq = csr_check_concurrent_channel_overlap( + mac, sap_ch_freq, sapPhyMode, cc_switch_mode); + sme_release_global_lock(&mac->sme); + } + + return intf_ch_freq; +} +#endif + +/** + * sme_set_tsfcb() - Set callback for TSF capture + * @mac_handle: Handler return by mac_open + * @cb_fn: Callback function pointer + * @db_ctx: Callback data + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_tsfcb(mac_handle_t mac_handle, + int (*cb_fn)(void *cb_ctx, struct stsf *ptsf), void *cb_ctx) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.get_tsf_cb = cb_fn; + mac->sme.get_tsf_cxt = cb_ctx; + sme_release_global_lock(&mac->sme); + } + return status; +} + +/** + * sme_reset_tsfcb() - Reset callback for TSF capture + * @mac_handle: Handler return by mac_open + * + * This function reset the tsf capture callback to SME + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_reset_tsfcb(mac_handle_t mac_handle) +{ + struct mac_context *mac; + QDF_STATUS status; + + if (!mac_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("mac_handle is not valid")); + return QDF_STATUS_E_INVAL; + } + mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.get_tsf_cb = NULL; + mac->sme.get_tsf_cxt = NULL; + sme_release_global_lock(&mac->sme); + } + return status; +} + +#if defined(WLAN_FEATURE_TSF) && !defined(WLAN_FEATURE_TSF_PLUS_NOIRQ) +/* + * sme_set_tsf_gpio() - set gpio pin that be toggled when capture tsf + * @mac_handle: Handler return by mac_open + * @pinvalue: gpio pin id + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_tsf_gpio(mac_handle_t mac_handle, uint32_t pinvalue) +{ + QDF_STATUS status; + struct scheduler_msg tsf_msg = {0}; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + tsf_msg.type = WMA_TSF_GPIO_PIN; + tsf_msg.reserved = 0; + tsf_msg.bodyval = pinvalue; + + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &tsf_msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Unable to post WMA_TSF_GPIO_PIN"); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return status; +} +#endif + +QDF_STATUS sme_get_cfg_valid_channels(uint32_t *valid_ch_freq, uint32_t *len) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = sme_get_mac_context(); + uint32_t *valid_ch_freq_list; + uint32_t i; + + if (!mac_ctx) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid MAC context")); + return QDF_STATUS_E_FAILURE; + } + + valid_ch_freq_list = qdf_mem_malloc(CFG_VALID_CHANNEL_LIST_LEN * + sizeof(uint32_t)); + if (!valid_ch_freq_list) + return QDF_STATUS_E_NOMEM; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_get_cfg_valid_channels(mac_ctx, + valid_ch_freq_list, len); + sme_release_global_lock(&mac_ctx->sme); + } + + for (i = 0; i < *len; i++) + valid_ch_freq[i] = valid_ch_freq_list[i]; + + qdf_mem_free(valid_ch_freq_list); + + return status; +} + +static uint8_t *sme_reg_hint_to_str(const enum country_src src) +{ + switch (src) { + case SOURCE_CORE: + return "WORLD MODE"; + + case SOURCE_DRIVER: + return "BDF file"; + + case SOURCE_USERSPACE: + return "user-space"; + + case SOURCE_11D: + return "802.11D IEs in beacons"; + + default: + return "unknown"; + } +} + +void sme_set_cc_src(mac_handle_t mac_handle, enum country_src cc_src) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + mac_ctx->reg_hint_src = cc_src; + + sme_debug("Country source is %s", + sme_reg_hint_to_str(cc_src)); +} + +/** + * sme_handle_generic_change_country_code() - handles country ch req + * @mac_ctx: mac global context + * @msg: request msg packet + * + * If Supplicant country code is priority than 11d is disabled. + * If 11D is enabled, we update the country code after every scan. + * Hence when Supplicant country code is priority, we don't need 11D info. + * Country code from Supplicant is set as current country code. + * + * Return: status of operation + */ +static QDF_STATUS +sme_handle_generic_change_country_code(struct mac_context *mac_ctx, + void *msg_buf) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* get the channels based on new cc */ + status = csr_get_channel_and_power_list(mac_ctx); + + if (status != QDF_STATUS_SUCCESS) { + sme_err("fail to get Channels"); + return status; + } + + sme_disconnect_connected_sessions(mac_ctx, + eSIR_MAC_UNSPEC_FAILURE_REASON); + + /* reset info based on new cc, and we are done */ + csr_apply_channel_power_info_wrapper(mac_ctx); + + csr_scan_filter_results(mac_ctx); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_update_channel_list(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Update umac channel (enable/disable) from cds channels */ + status = csr_get_channel_and_power_list(mac_ctx); + if (status != QDF_STATUS_SUCCESS) { + sme_err("fail to get Channels"); + sme_release_global_lock(&mac_ctx->sme); + return status; + } + + csr_apply_channel_power_info_wrapper(mac_ctx); + csr_scan_filter_results(mac_ctx); + sme_disconnect_connected_sessions(mac_ctx, + eSIR_MAC_OPER_CHANNEL_USER_DISABLED); + sme_release_global_lock(&mac_ctx->sme); + } + + return status; +} + +/** + * sme_search_in_base_ch_freq_lst() - is given ch_freq in base ch freq + * @mac_ctx: mac global context + * @chan_freq: current channel freq + * + * Return: true if given ch_freq is in base ch freq + */ +static bool sme_search_in_base_ch_freq_lst( + struct mac_context *mac_ctx, uint32_t chan_freq) +{ + uint8_t i; + struct csr_channel *ch_lst_info; + + ch_lst_info = &mac_ctx->scan.base_channels; + for (i = 0; i < ch_lst_info->numChannels; i++) { + if (ch_lst_info->channel_freq_list[i] == chan_freq) + return true; + } + + return false; +} + +/** + * sme_disconnect_connected_sessions() - Disconnect STA and P2P client session + * if channel is not supported + * @mac_ctx: mac global context + * @reason: Mac Disconnect reason code as per @enum eSirMacReasonCodes + * + * If new country code does not support the channel on which STA/P2P client + * is connetced, it sends the disconnect to the AP/P2P GO + * + * Return: void + */ +static void sme_disconnect_connected_sessions(struct mac_context *mac_ctx, + enum eSirMacReasonCodes reason) +{ + uint8_t session_id, found = false; + uint32_t chan_freq; + + for (session_id = 0; session_id < WLAN_MAX_VDEVS; session_id++) { + if (!csr_is_session_client_and_connected(mac_ctx, session_id)) + continue; + found = false; + /* Session is connected.Check the channel */ + chan_freq = csr_get_infra_operation_chan_freq( + mac_ctx, session_id); + sme_debug("Current Operating channel : %d, session :%d", + chan_freq, session_id); + found = sme_search_in_base_ch_freq_lst(mac_ctx, chan_freq); + if (!found) { + sme_debug("Disconnect Session: %d", session_id); + csr_roam_disconnect(mac_ctx, session_id, + eCSR_DISCONNECT_REASON_UNSPECIFIED, + reason); + } + } +} + +#ifdef WLAN_FEATURE_PACKET_FILTERING +QDF_STATUS sme_8023_multicast_list(mac_handle_t mac_handle, uint8_t sessionId, + tpSirRcvFltMcAddrList pMulticastAddrs) +{ + tpSirRcvFltMcAddrList request_buf; + struct scheduler_msg msg = {0}; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = NULL; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: ulMulticastAddrCnt: %d, multicastAddr[0]: %pK", __func__, + pMulticastAddrs->ulMulticastAddrCnt, + pMulticastAddrs->multicastAddr[0].bytes); + + /* Find the connected Infra / P2P_client connected session */ + pSession = CSR_GET_SESSION(mac, sessionId); + if (!CSR_IS_SESSION_VALID(mac, sessionId) || + (!csr_is_conn_state_infra(mac, sessionId) && + !csr_is_ndi_started(mac, sessionId))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Unable to find the session Id: %d", __func__, + sessionId); + return QDF_STATUS_E_FAILURE; + } + + request_buf = qdf_mem_malloc(sizeof(tSirRcvFltMcAddrList)); + if (!request_buf) + return QDF_STATUS_E_NOMEM; + + if (!csr_is_conn_state_connected_infra(mac, sessionId) && + !csr_is_ndi_started(mac, sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Request ignored, session %d is not connected or started", + __func__, sessionId); + qdf_mem_free(request_buf); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_copy(request_buf, pMulticastAddrs, + sizeof(tSirRcvFltMcAddrList)); + + qdf_copy_macaddr(&request_buf->self_macaddr, &pSession->self_mac_addr); + qdf_copy_macaddr(&request_buf->bssid, + &pSession->connectedProfile.bssid); + + msg.type = WMA_8023_MULTICAST_LIST_REQ; + msg.reserved = 0; + msg.bodyptr = request_buf; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + sessionId, msg.type)); + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WMA_8023_MULTICAST_LIST message to WMA", + __func__); + qdf_mem_free(request_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_PACKET_FILTERING */ + +bool sme_is_channel_valid(mac_handle_t mac_handle, uint32_t chan_freq) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + bool valid = false; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + + valid = csr_roam_is_channel_valid(mac, chan_freq); + + sme_release_global_lock(&mac->sme); + } + + return valid; +} + +/* + * sme_set_max_tx_power_per_band() - + * Set the Maximum Transmit Power specific to band dynamically. + * Note: this setting will not persist over reboots. + * + * band + * power to set in dB + * Return QDF_STATUS + */ +QDF_STATUS sme_set_max_tx_power_per_band(enum band_info band, int8_t dB) +{ + struct scheduler_msg msg = {0}; + tpMaxTxPowerPerBandParams pMaxTxPowerPerBandParams = NULL; + + pMaxTxPowerPerBandParams = + qdf_mem_malloc(sizeof(tMaxTxPowerPerBandParams)); + if (!pMaxTxPowerPerBandParams) + return QDF_STATUS_E_NOMEM; + + pMaxTxPowerPerBandParams->power = dB; + pMaxTxPowerPerBandParams->bandInfo = band; + + msg.type = WMA_SET_MAX_TX_POWER_PER_BAND_REQ; + msg.reserved = 0; + msg.bodyptr = pMaxTxPowerPerBandParams; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, msg.type)); + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s:Not able to post WMA_SET_MAX_TX_POWER_PER_BAND_REQ", + __func__); + qdf_mem_free(pMaxTxPowerPerBandParams); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_set_max_tx_power() - + * Set the Maximum Transmit Power dynamically. Note: this setting will + * not persist over reboots. + * + * mac_handle + * pBssid BSSID to set the power cap for + * pBssid pSelfMacAddress self MAC Address + * pBssid power to set in dB + * Return QDF_STATUS + */ +QDF_STATUS sme_set_max_tx_power(mac_handle_t mac_handle, + struct qdf_mac_addr pBssid, + struct qdf_mac_addr pSelfMacAddress, int8_t dB) +{ + struct scheduler_msg msg = {0}; + tpMaxTxPowerParams pMaxTxParams = NULL; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_SET_MAXTXPOW, NO_SESSION, 0)); + pMaxTxParams = qdf_mem_malloc(sizeof(tMaxTxPowerParams)); + if (!pMaxTxParams) + return QDF_STATUS_E_NOMEM; + + qdf_copy_macaddr(&pMaxTxParams->bssId, &pBssid); + qdf_copy_macaddr(&pMaxTxParams->selfStaMacAddr, &pSelfMacAddress); + pMaxTxParams->power = dB; + + msg.type = WMA_SET_MAX_TX_POWER_REQ; + msg.reserved = 0; + msg.bodyptr = pMaxTxParams; + + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WMA_SET_MAX_TX_POWER_REQ message to WMA", + __func__); + qdf_mem_free(pMaxTxParams); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_set_custom_mac_addr() - + * Set the customer Mac Address. + * + * customMacAddr customer MAC Address + * Return QDF_STATUS + */ +QDF_STATUS sme_set_custom_mac_addr(tSirMacAddr customMacAddr) +{ + struct scheduler_msg msg = {0}; + tSirMacAddr *pBaseMacAddr; + + pBaseMacAddr = qdf_mem_malloc(sizeof(tSirMacAddr)); + if (!pBaseMacAddr) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(*pBaseMacAddr, customMacAddr, sizeof(tSirMacAddr)); + + msg.type = SIR_HAL_SET_BASE_MACADDR_IND; + msg.reserved = 0; + msg.bodyptr = pBaseMacAddr; + + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Not able to post SIR_HAL_SET_BASE_MACADDR_IND message to WMA"); + qdf_mem_free(pBaseMacAddr); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_set_tx_power() - + * Set Transmit Power dynamically. + * + * mac_handle + * sessionId Target Session ID + * BSSID + * dev_mode dev_mode such as station, P2PGO, SAP + * dBm power to set + * Return QDF_STATUS + */ +QDF_STATUS sme_set_tx_power(mac_handle_t mac_handle, uint8_t sessionId, + struct qdf_mac_addr pBSSId, + enum QDF_OPMODE dev_mode, int dBm) +{ + struct scheduler_msg msg = {0}; + tpMaxTxPowerParams pTxParams = NULL; + int8_t power = (int8_t) dBm; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_SET_TXPOW, sessionId, 0)); + + /* make sure there is no overflow */ + if ((int)power != dBm) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: error, invalid power = %d", __func__, dBm); + return QDF_STATUS_E_FAILURE; + } + + pTxParams = qdf_mem_malloc(sizeof(tMaxTxPowerParams)); + if (!pTxParams) + return QDF_STATUS_E_NOMEM; + + qdf_copy_macaddr(&pTxParams->bssId, &pBSSId); + pTxParams->power = power; /* unit is dBm */ + pTxParams->dev_mode = dev_mode; + msg.type = WMA_SET_TX_POWER_REQ; + msg.reserved = 0; + msg.bodyptr = pTxParams; + + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + qdf_mem_free(pTxParams); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_update_session_param(mac_handle_t mac_handle, uint8_t session_id, + uint32_t param_type, uint32_t param_val) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint16_t len; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + struct sir_update_session_param *msg; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, + session_id); + + if (!session) { + sme_err("Session: %d not found", session_id); + sme_release_global_lock(&mac_ctx->sme); + return status; + } + + if (param_type == SIR_PARAM_IGNORE_ASSOC_DISALLOWED) + mac_ctx->ignore_assoc_disallowed = param_val; + + if (!session->sessionActive) + QDF_ASSERT(0); + + len = sizeof(*msg); + msg = qdf_mem_malloc(len); + if (!msg) + status = QDF_STATUS_E_NOMEM; + else { + msg->message_type = eWNI_SME_SESSION_UPDATE_PARAM; + msg->length = len; + msg->vdev_id = session_id; + msg->param_type = param_type; + msg->param_val = param_val; + status = umac_send_mb_message_to_mac(msg); + } + sme_release_global_lock(&mac_ctx->sme); + } + return status; +} + +QDF_STATUS sme_update_roam_scan_n_probes(mac_handle_t mac_handle, + uint8_t vdev_id, + const uint8_t probes) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tCsrNeighborRoamControlInfo *neighbor_roam_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_UPDATE_ROAM_SCAN_N_PROBES, + vdev_id, 0)); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to acquire sme lock; status: %d", status); + return status; + } + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + sme_debug("gRoamScanNProbes is changed from %u to %u", + neighbor_roam_info->cfgParams.roam_scan_n_probes, probes); + neighbor_roam_info->cfgParams.roam_scan_n_probes = probes; + + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, vdev_id, REASON_NPROBES_CHANGED); + sme_release_global_lock(&mac->sme); + + return status; +} + +QDF_STATUS +sme_update_roam_scan_home_away_time(mac_handle_t mac_handle, uint8_t vdev_id, + const uint16_t roam_scan_home_away_time, + const bool send_offload_cmd) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tCsrNeighborRoamControlInfo *neighbor_roam_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_UPDATE_ROAM_SCAN_HOME_AWAY_TIME, + vdev_id, 0)); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to acquire sme lock; status: %d", status); + return status; + } + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + + if (neighbor_roam_info->cfgParams.roam_scan_home_away_time == + roam_scan_home_away_time) { + sme_debug("Not updated as current value is :%u", + roam_scan_home_away_time); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_SUCCESS; + } + + sme_debug("gRoamScanHomeAwayTime is changed from %d to %d", + neighbor_roam_info->cfgParams.roam_scan_home_away_time, + roam_scan_home_away_time); + neighbor_roam_info->cfgParams.roam_scan_home_away_time = + roam_scan_home_away_time; + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled && send_offload_cmd) + csr_roam_update_cfg(mac, vdev_id, + REASON_HOME_AWAY_TIME_CHANGED); + sme_release_global_lock(&mac->sme); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_ext_change_channel()- function to post send ECSA + * action frame to csr. + * @mac_handle: Opaque handle to the global MAC context + * @channel freq: new channel freq to switch + * @session_id: senssion it should be sent on. + * + * This function is called to post ECSA frame to csr. + * + * Return: success if msg is sent else return failure + */ +QDF_STATUS sme_ext_change_channel(mac_handle_t mac_handle, uint32_t channel, + uint8_t session_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t channel_state; + + sme_err("Set Channel: %d", channel); + channel_state = + wlan_reg_get_channel_state(mac_ctx->pdev, channel); + + if (CHANNEL_STATE_DISABLE == channel_state) { + sme_err("Invalid channel: %d", channel); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac_ctx->sme); + + if (QDF_STATUS_SUCCESS == status) { + /* update the channel list to the firmware */ + status = csr_send_ext_change_channel(mac_ctx, + channel, session_id); + sme_release_global_lock(&mac_ctx->sme); + } + + return status; +} + +/* + * sme_get_roam_intra_band() - + * get Intra band roaming + * + * mac_handle: Opaque handle to the global MAC context + * Return Success or failure + */ +bool sme_get_roam_intra_band(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_GET_ROAMIBAND, NO_SESSION, 0)); + + return mac->mlme_cfg->lfr.roam_intra_band; +} + +QDF_STATUS sme_get_roam_scan_n_probes(mac_handle_t mac_handle, uint8_t vdev_id, + uint8_t *roam_scan_n_probes) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tCsrNeighborRoamControlInfo *neighbor_roam_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to acquire sme lock; status: %d", status); + return status; + } + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + *roam_scan_n_probes = neighbor_roam_info->cfgParams.roam_scan_n_probes; + sme_release_global_lock(&mac->sme); + + return status; +} + +QDF_STATUS sme_get_roam_scan_home_away_time(mac_handle_t mac_handle, + uint8_t vdev_id, + uint16_t *roam_scan_home_away_time) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tCsrNeighborRoamControlInfo *neighbor_roam_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to acquire sme lock; status: %d", status); + return status; + } + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + *roam_scan_home_away_time = + neighbor_roam_info->cfgParams.roam_scan_home_away_time; + sme_release_global_lock(&mac->sme); + + return status; +} + +QDF_STATUS sme_update_roam_rssi_diff(mac_handle_t mac_handle, uint8_t vdev_id, + uint8_t roam_rssi_diff) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tCsrNeighborRoamControlInfo *neighbor_roam_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid sme vdev id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + sme_debug("LFR runtime successfully set roam rssi diff to %d - old value is %d - roam state is %s", + roam_rssi_diff, + neighbor_roam_info->cfgParams.roam_rssi_diff, + mac_trace_get_neighbour_roam_state( + neighbor_roam_info->neighborRoamState)); + + neighbor_roam_info->cfgParams.roam_rssi_diff = roam_rssi_diff; + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, vdev_id, + REASON_RSSI_DIFF_CHANGED); + + sme_release_global_lock(&mac->sme); + return status; +} + +void sme_update_session_assoc_ie(mac_handle_t mac_handle, + uint8_t vdev_id, + struct csr_roam_profile *src_profile) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session = CSR_GET_SESSION(mac, vdev_id); + + if (!session) { + sme_err("Session: %d not found", vdev_id); + return; + } + + qdf_mem_free(session->pAddIEAssoc); + session->pAddIEAssoc = NULL; + session->nAddIEAssocLength = 0; + + if (!src_profile->nAddIEAssocLength) { + sme_debug("Assoc IE len 0"); + return; + } + + session->pAddIEAssoc = qdf_mem_malloc(src_profile->nAddIEAssocLength); + if (!session->pAddIEAssoc) + return; + + session->nAddIEAssocLength = src_profile->nAddIEAssocLength; + qdf_mem_copy(session->pAddIEAssoc, src_profile->pAddIEAssoc, + src_profile->nAddIEAssocLength); +} + +QDF_STATUS sme_send_rso_connect_params(mac_handle_t mac_handle, + uint8_t vdev_id, + struct csr_roam_profile *src_profile) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamControlInfo neighbor_roam_info = + &mac->roam.neighborRoamInfo[vdev_id]; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid sme vdev id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + if (!src_profile) { + sme_err("src roam profile NULL"); + return QDF_STATUS_E_INVAL; + } + + if (!mac->mlme_cfg->lfr.lfr_enabled || + (neighbor_roam_info->neighborRoamState != + eCSR_NEIGHBOR_ROAM_STATE_CONNECTED)) { + sme_info("Fast roam is disabled or not connected(%d)", + neighbor_roam_info->neighborRoamState); + return QDF_STATUS_E_PERM; + } + + if (csr_is_roam_offload_enabled(mac)) { + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + sme_debug("Updating fils config to fw"); + csr_roam_update_cfg(mac, vdev_id, + REASON_FILS_PARAMS_CHANGED); + sme_release_global_lock(&mac->sme); + } else { + sme_err("Failed to acquire SME lock"); + } + } else { + sme_info("LFR3 not enabled"); + return QDF_STATUS_E_INVAL; + } + + return status; +} + +#ifdef WLAN_FEATURE_FILS_SK +QDF_STATUS sme_update_fils_config(mac_handle_t mac_handle, uint8_t vdev_id, + struct csr_roam_profile *src_profile) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + csr_update_fils_config(mac, vdev_id, src_profile); + + return status; +} + +void sme_send_hlp_ie_info(mac_handle_t mac_handle, uint8_t vdev_id, + struct csr_roam_profile *profile, uint32_t if_addr) +{ + int i; + struct scheduler_msg msg; + QDF_STATUS status; + struct hlp_params *params; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session = CSR_GET_SESSION(mac, vdev_id); + tpCsrNeighborRoamControlInfo neighbor_roam_info = + &mac->roam.neighborRoamInfo[vdev_id]; + + if (!session) { + sme_err("session NULL"); + return; + } + + if (!mac->mlme_cfg->lfr.lfr_enabled || + (neighbor_roam_info->neighborRoamState != + eCSR_NEIGHBOR_ROAM_STATE_CONNECTED)) { + sme_debug("Fast roam is disabled or not connected(%d)", + neighbor_roam_info->neighborRoamState); + return; + } + + params = qdf_mem_malloc(sizeof(*params)); + if (!params) + return; + + if ((profile->hlp_ie_len + + QDF_IPV4_ADDR_SIZE) > FILS_MAX_HLP_DATA_LEN) { + sme_err("HLP IE len exceeds %d", + profile->hlp_ie_len); + qdf_mem_free(params); + return; + } + + params->vdev_id = vdev_id; + params->hlp_ie_len = profile->hlp_ie_len + QDF_IPV4_ADDR_SIZE; + + for (i = 0; i < QDF_IPV4_ADDR_SIZE; i++) + params->hlp_ie[i] = (if_addr >> (i * 8)) & 0xFF; + + qdf_mem_copy(params->hlp_ie + QDF_IPV4_ADDR_SIZE, + profile->hlp_ie, profile->hlp_ie_len); + + msg.type = SIR_HAL_HLP_IE_INFO; + msg.reserved = 0; + msg.bodyptr = params; + status = sme_acquire_global_lock(&mac->sme); + if (status != QDF_STATUS_SUCCESS) { + sme_err("sme lock acquire fails"); + qdf_mem_free(params); + return; + } + + if (!QDF_IS_STATUS_SUCCESS + (scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg))) { + sme_err("Not able to post WMA_HLP_IE_INFO message to HAL"); + sme_release_global_lock(&mac->sme); + qdf_mem_free(params); + return; + } + + sme_release_global_lock(&mac->sme); +} + +void sme_free_join_rsp_fils_params(struct csr_roam_info *roam_info) +{ + struct fils_join_rsp_params *roam_fils_params; + + if (!roam_info) + return; + + roam_fils_params = roam_info->fils_join_rsp; + if (!roam_fils_params) + return; + + if (roam_fils_params->fils_pmk) + qdf_mem_free(roam_fils_params->fils_pmk); + + qdf_mem_free(roam_fils_params); + + roam_info->fils_join_rsp = NULL; +} + +#else +inline void sme_send_hlp_ie_info(mac_handle_t mac_handle, uint8_t vdev_id, + struct csr_roam_profile *profile, uint32_t if_addr) +{} +#endif + +/* + * sme_update_wes_mode() - + * Update WES Mode + * This function is called through dynamic setConfig callback function + * to configure isWESModeEnabled + * + * mac_handle: Opaque handle to the global MAC context + * isWESModeEnabled - WES mode + * sessionId - Session Identifier + * Return QDF_STATUS_SUCCESS - SME update isWESModeEnabled config successfully. + * Other status means SME is failed to update isWESModeEnabled. + */ + +QDF_STATUS sme_update_wes_mode(mac_handle_t mac_handle, bool isWESModeEnabled, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR runtime successfully set WES Mode to %d - old value is %d - roam state is %s", + isWESModeEnabled, + mac->mlme_cfg->lfr.wes_mode_enabled, + mac_trace_get_neighbour_roam_state(mac->roam. + neighborRoamInfo + [sessionId]. + neighborRoamState)); + mac->mlme_cfg->lfr.wes_mode_enabled = isWESModeEnabled; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_set_roam_scan_control() - + * Set roam scan control + * This function is called to set roam scan control + * if roam scan control is set to 0, roaming scan cache is cleared + * any value other than 0 is treated as invalid value + * mac_handle: Opaque handle to the global MAC context + * sessionId - Session Identifier + * Return QDF_STATUS_SUCCESS - SME update config successfully. + * Other status means SME failure to update + */ +QDF_STATUS sme_set_roam_scan_control(mac_handle_t mac_handle, uint8_t sessionId, + bool roamScanControl) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + tCsrChannelInfo *specific_channel_info; + tCsrNeighborRoamControlInfo *neighbor_roam_info; + + MTRACE(qdf_trace(QDF_MODULE_ID_SME, + TRACE_CODE_SME_RX_HDD_SET_SCANCTRL, NO_SESSION, 0)); + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + neighbor_roam_info = &mac->roam.neighborRoamInfo[sessionId]; + sme_debug("LFR runtime successfully set roam scan control to %d - old value is %d - roam state is %s", + roamScanControl, + mac->roam.configParam.nRoamScanControl, + mac_trace_get_neighbour_roam_state( + neighbor_roam_info->neighborRoamState)); + if (!roamScanControl && mac->roam.configParam.nRoamScanControl) { + /** + * Clear the specific channel info cache when roamScanControl + * is set to 0. If any preffered channel list is configured, + * that will be sent to firmware for further roam scans. + */ + sme_debug("LFR runtime successfully cleared roam scan cache"); + specific_channel_info = + &neighbor_roam_info->cfgParams.specific_chan_info; + csr_flush_cfg_bg_scan_roam_channel_list(specific_channel_info); + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) { + /** Clear the static channel in FW by REASON_FLUSH_CHANNEL_LIST + * and then append channel list with dynamic channels in the FW + * using REASON_CHANNEL_LIST_CHANGED. + */ + csr_roam_update_cfg(mac, sessionId, + REASON_FLUSH_CHANNEL_LIST); + + csr_roam_update_cfg(mac, sessionId, + REASON_CHANNEL_LIST_CHANGED); + } + } + mac->roam.configParam.nRoamScanControl = roamScanControl; + sme_release_global_lock(&mac->sme); + + return status; +} + +/* + * sme_update_is_fast_roam_ini_feature_enabled() - enable/disable LFR + * support at runtime + * It is used at in the REG_DYNAMIC_VARIABLE macro definition of + * isFastRoamIniFeatureEnabled. + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return QDF_STATUS_SUCCESS - SME update isFastRoamIniFeatureEnabled config + * successfully. + * Other status means SME is failed to update isFastRoamIniFeatureEnabled. + */ +QDF_STATUS sme_update_is_fast_roam_ini_feature_enabled(mac_handle_t mac_handle, + uint8_t sessionId, const bool isFastRoamIniFeatureEnabled) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (mac->mlme_cfg->lfr.lfr_enabled == + isFastRoamIniFeatureEnabled) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: FastRoam is already enabled or disabled, nothing to do (returning) old(%d) new(%d)", + __func__, + mac->mlme_cfg->lfr.lfr_enabled, + isFastRoamIniFeatureEnabled); + return QDF_STATUS_SUCCESS; + } + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: FastRoamEnabled is changed from %d to %d", __func__, + mac->mlme_cfg->lfr.lfr_enabled, + isFastRoamIniFeatureEnabled); + mac->mlme_cfg->lfr.lfr_enabled = isFastRoamIniFeatureEnabled; + csr_neighbor_roam_update_fast_roaming_enabled(mac, sessionId, + isFastRoamIniFeatureEnabled); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_config_fast_roaming() - enable/disable LFR support at runtime + * @mac_handle - The handle returned by macOpen. + * @session_id - Session Identifier + * @is_fast_roam_enabled - flag to enable/disable roaming + * + * When Supplicant issues enabled/disable fast roaming on the basis + * of the Bssid modification in network block (e.g. AutoJoin mode N/W block) + * + * Return: QDF_STATUS + */ + +QDF_STATUS sme_config_fast_roaming(mac_handle_t mac_handle, uint8_t session_id, + const bool is_fast_roam_enabled) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + enum roam_offload_state state; + QDF_STATUS status; + bool supplicant_disabled_roaming; + + /* + * supplicant_disabled_roaming flag is altered when supplicant sends + * vendor command to enable/disable roaming after association. + * + * This request from wpa_supplicant will be skipped in this function + * if roaming is disabled using driver command or INI and + * supplicant_disabled_roaming flag remains set. So make sure to set + * supplicant_disabled_roaming flag as per wpa_supplicant even if roam + * request from wpa_supplicant ignored. + */ + if (!mac_ctx->mlme_cfg->lfr.lfr_enabled) { + sme_debug("ROAM: Fast roam is disabled through ini"); + if (!is_fast_roam_enabled) + return QDF_STATUS_SUCCESS; + return QDF_STATUS_E_FAILURE; + } + + supplicant_disabled_roaming = + mlme_get_supplicant_disabled_roaming(mac_ctx->psoc, + session_id); + if (!is_fast_roam_enabled && supplicant_disabled_roaming) { + sme_debug("ROAM: RSO disabled by wpa supplicant already"); + return QDF_STATUS_E_ALREADY; + } + mlme_set_supplicant_disabled_roaming(mac_ctx->psoc, session_id, + !is_fast_roam_enabled); + + state = (is_fast_roam_enabled) ? ROAM_RSO_STARTED : ROAM_RSO_STOPPED; + status = csr_post_roam_state_change(mac_ctx, session_id, state, + REASON_SUPPLICANT_DISABLED_ROAMING); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("ROAM: update fast roaming failed. status: %d", status); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_ESE +int sme_add_key_krk(mac_handle_t mac_handle, uint8_t session_id, + const uint8_t *key, const int key_len) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + if (key_len < SIR_KRK_KEY_LEN) { + sme_warn("Invalid KRK keylength [= %d]", key_len); + return -EINVAL; + } + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("incorrect session/vdev ID"); + return -EINVAL; + } + + session = CSR_GET_SESSION(mac_ctx, session_id); + + qdf_mem_copy(session->eseCckmInfo.krk, key, SIR_KRK_KEY_LEN); + session->eseCckmInfo.reassoc_req_num = 1; + session->eseCckmInfo.krk_plumbed = true; + + return 0; +} +#endif + +#if defined(FEATURE_WLAN_ESE) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +int sme_add_key_btk(mac_handle_t mac_handle, uint8_t session_id, + const uint8_t *key, const int key_len) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + if (key_len < SIR_BTK_KEY_LEN) { + sme_warn("Invalid BTK keylength [= %d]", key_len); + return -EINVAL; + } + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("incorrect session/vdev ID"); + return -EINVAL; + } + + session = CSR_GET_SESSION(mac_ctx, session_id); + + qdf_mem_copy(session->eseCckmInfo.btk, key, SIR_BTK_KEY_LEN); + /* + * KRK and BTK are updated by upper layer back to back. Send + * updated KRK and BTK together to FW here. + */ + csr_roam_update_cfg(mac_ctx, session_id, REASON_ROAM_PSK_PMK_CHANGED); + + return 0; +} +#endif + +/** + * sme_stop_roaming() - Stop roaming for a given sessionId + * This is a synchronous call + * + * @mac_handle - The handle returned by mac_open + * @sessionId - Session Identifier + * + * Return QDF_STATUS_SUCCESS on success + * Other status on failure + */ +QDF_STATUS sme_stop_roaming(mac_handle_t mac_handle, uint8_t session_id, + uint8_t reason, uint32_t requestor) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("ROAM: incorrect vdev ID %d", session_id); + return QDF_STATUS_E_FAILURE; + } + + if (reason == REASON_DRIVER_DISABLED && requestor) + mlme_set_operations_bitmap(mac_ctx->psoc, session_id, + requestor, false); + + status = csr_post_roam_state_change(mac_ctx, session_id, + ROAM_RSO_STOPPED, + REASON_DRIVER_DISABLED); + + return status; +} + +/* + * sme_start_roaming() - Start roaming for a given sessionId + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open + * sessionId - Session Identifier + * Return QDF_STATUS_SUCCESS on success + * Other status on failure + */ +QDF_STATUS sme_start_roaming(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t reason, uint32_t requestor) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (reason == REASON_DRIVER_ENABLED && requestor) { + mlme_set_operations_bitmap(mac->psoc, sessionId, requestor, + true); + } + + status = sme_acquire_global_lock(&mac->sme); + + if (QDF_IS_STATUS_ERROR(status)) + return QDF_STATUS_E_FAILURE; + + status = csr_post_roam_state_change(mac, sessionId, ROAM_RSO_STARTED, + REASON_DRIVER_ENABLED); + + sme_release_global_lock(&mac->sme); + + return status; +} + +/* + * sme_set_roam_opportunistic_scan_threshold_diff() - + * Update Opportunistic Scan threshold diff + * This function is called through dynamic setConfig callback function + * to configure nOpportunisticThresholdDiff + * + * mac_handle: Opaque handle to the global MAC context + * sessionId - Session Identifier + * nOpportunisticThresholdDiff - Opportunistic Scan threshold diff + * Return QDF_STATUS_SUCCESS - SME update nOpportunisticThresholdDiff config + * successfully. + * else SME is failed to update nOpportunisticThresholdDiff. + */ +QDF_STATUS sme_set_roam_opportunistic_scan_threshold_diff( + mac_handle_t mac_handle, + uint8_t sessionId, + const uint8_t nOpportunisticThresholdDiff) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_neighbor_roam_update_config(mac, sessionId, + nOpportunisticThresholdDiff, + REASON_OPPORTUNISTIC_THRESH_DIFF_CHANGED); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->mlme_cfg->lfr.opportunistic_scan_threshold_diff = + nOpportunisticThresholdDiff; + } + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_get_roam_opportunistic_scan_threshold_diff() + * gets Opportunistic Scan threshold diff + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open + * Return uint8_t - nOpportunisticThresholdDiff + */ +uint8_t sme_get_roam_opportunistic_scan_threshold_diff(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->mlme_cfg->lfr.opportunistic_scan_threshold_diff; +} + +/* + * sme_set_roam_rescan_rssi_diff() - Update roam rescan rssi diff + * This function is called through dynamic setConfig callback function + * to configure nRoamRescanRssiDiff + * + * mac_handle: Opaque handle to the global MAC context + * sessionId - Session Identifier + * nRoamRescanRssiDiff - roam rescan rssi diff + * Return QDF_STATUS_SUCCESS - SME update nRoamRescanRssiDiff config + * successfully. + * else SME is failed to update nRoamRescanRssiDiff. + */ +QDF_STATUS sme_set_roam_rescan_rssi_diff(mac_handle_t mac_handle, + uint8_t sessionId, + const uint8_t nRoamRescanRssiDiff) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_neighbor_roam_update_config(mac, sessionId, + nRoamRescanRssiDiff, + REASON_ROAM_RESCAN_RSSI_DIFF_CHANGED); + if (QDF_IS_STATUS_SUCCESS(status)) + mac->mlme_cfg->lfr.roam_rescan_rssi_diff = + nRoamRescanRssiDiff; + + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_get_roam_rescan_rssi_diff() + * gets roam rescan rssi diff + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open + * Return int8_t - nRoamRescanRssiDiff + */ +uint8_t sme_get_roam_rescan_rssi_diff(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->mlme_cfg->lfr.roam_rescan_rssi_diff; +} + +/* + * sme_set_roam_bmiss_first_bcnt() - + * Update Roam count for first beacon miss + * This function is called through dynamic setConfig callback function + * to configure nRoamBmissFirstBcnt + * mac_handle: Opaque handle to the global MAC context + * sessionId - Session Identifier + * nRoamBmissFirstBcnt - Roam first bmiss count + * Return QDF_STATUS_SUCCESS - SME update nRoamBmissFirstBcnt + * successfully. + * else SME is failed to update nRoamBmissFirstBcnt + */ +QDF_STATUS sme_set_roam_bmiss_first_bcnt(mac_handle_t mac_handle, + uint8_t sessionId, + const uint8_t nRoamBmissFirstBcnt) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_neighbor_roam_update_config(mac, sessionId, + nRoamBmissFirstBcnt, + REASON_ROAM_BMISS_FIRST_BCNT_CHANGED); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->mlme_cfg->lfr.roam_bmiss_first_bcnt = + nRoamBmissFirstBcnt; + } + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_set_roam_bmiss_final_bcnt() - + * Update Roam count for final beacon miss + * This function is called through dynamic setConfig callback function + * to configure nRoamBmissFinalBcnt + * mac_handle: Opaque handle to the global MAC context + * sessionId - Session Identifier + * nRoamBmissFinalBcnt - Roam final bmiss count + * Return QDF_STATUS_SUCCESS - SME update nRoamBmissFinalBcnt + * successfully. + * else SME is failed to update nRoamBmissFinalBcnt + */ +QDF_STATUS sme_set_roam_bmiss_final_bcnt(mac_handle_t mac_handle, + uint8_t sessionId, + const uint8_t nRoamBmissFinalBcnt) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_neighbor_roam_update_config(mac, sessionId, + nRoamBmissFinalBcnt, + REASON_ROAM_BMISS_FINAL_BCNT_CHANGED); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->mlme_cfg->lfr.roam_bmiss_final_bcnt = + nRoamBmissFinalBcnt; + } + sme_release_global_lock(&mac->sme); + } + return status; +} + +QDF_STATUS +sme_set_neighbor_lookup_rssi_threshold(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t neighbor_lookup_rssi_threshold) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %u", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + csr_neighbor_roam_update_config(mac, vdev_id, + neighbor_lookup_rssi_threshold, + REASON_LOOKUP_THRESH_CHANGED); + sme_release_global_lock(&mac->sme); + return status; +} + +QDF_STATUS sme_get_neighbor_lookup_rssi_threshold(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t *lookup_threshold) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tCsrNeighborRoamControlInfo *neighbor_roam_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + + *lookup_threshold = + neighbor_roam_info->cfgParams.neighborLookupThreshold; + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_set_neighbor_scan_refresh_period() - set neighbor scan results + * refresh period + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return QDF_STATUS_SUCCESS - SME update config successful. + * Other status means SME is failed to update + */ +QDF_STATUS sme_set_neighbor_scan_refresh_period(mac_handle_t mac_handle, + uint8_t sessionId, uint16_t neighborScanResultsRefreshPeriod) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = NULL; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + pNeighborRoamInfo = &mac->roam.neighborRoamInfo[sessionId]; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR runtime successfully set roam scan refresh period to %d- old value is %d - roam state is %s", + neighborScanResultsRefreshPeriod, + mac->mlme_cfg->lfr. + neighbor_scan_results_refresh_period, + mac_trace_get_neighbour_roam_state(mac->roam. + neighborRoamInfo + [sessionId]. + neighborRoamState)); + mac->mlme_cfg->lfr.neighbor_scan_results_refresh_period = + neighborScanResultsRefreshPeriod; + pNeighborRoamInfo->cfgParams.neighborResultsRefreshPeriod = + neighborScanResultsRefreshPeriod; + + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, sessionId, + REASON_NEIGHBOR_SCAN_REFRESH_PERIOD_CHANGED); + + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_get_neighbor_scan_refresh_period() - get neighbor scan results + * refresh period + * This is a synchronous call + * + * \param mac_handle - The handle returned by mac_open. + * \return uint16_t - Neighbor scan results refresh period value + */ +uint16_t sme_get_neighbor_scan_refresh_period(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->mlme_cfg->lfr.neighbor_scan_results_refresh_period; +} + +uint16_t sme_get_empty_scan_refresh_period_global(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->mlme_cfg->lfr.empty_scan_refresh_period; +} + +QDF_STATUS sme_get_empty_scan_refresh_period(mac_handle_t mac_handle, + uint8_t vdev_id, + uint16_t *refresh_threshold) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tCsrNeighborRoamControlInfo *neighbor_roam_info; + QDF_STATUS status; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + + *refresh_threshold = + neighbor_roam_info->cfgParams.emptyScanRefreshPeriod; + sme_release_global_lock(&mac->sme); + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_update_empty_scan_refresh_period + * Update empty_scan_refresh_period + * This function is called through dynamic setConfig callback function + * to configure empty_scan_refresh_period + * Usage: adb shell iwpriv wlan0 setConfig + * empty_scan_refresh_period=[0 .. 60] + * + * mac_handle: Opaque handle to the global MAC context + * sessionId - Session Identifier + * empty_scan_refresh_period - scan period following empty scan results. + * Return Success or failure + */ + +QDF_STATUS sme_update_empty_scan_refresh_period(mac_handle_t mac_handle, + uint8_t sessionId, uint16_t + empty_scan_refresh_period) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = NULL; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + pNeighborRoamInfo = &mac->roam.neighborRoamInfo[sessionId]; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR runtime successfully set roam scan period to %d -old value is %d - roam state is %s", + empty_scan_refresh_period, + pNeighborRoamInfo->cfgParams.emptyScanRefreshPeriod, + mac_trace_get_neighbour_roam_state(mac->roam. + neighborRoamInfo + [sessionId]. + neighborRoamState)); + pNeighborRoamInfo->cfgParams.emptyScanRefreshPeriod = + empty_scan_refresh_period; + + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, sessionId, + REASON_EMPTY_SCAN_REF_PERIOD_CHANGED); + + sme_release_global_lock(&mac->sme); + } + + return status; +} + +QDF_STATUS sme_update_full_roam_scan_period(mac_handle_t mac_handle, + uint8_t vdev_id, + uint32_t full_roam_scan_period) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tpCsrNeighborRoamControlInfo neighbor_roam_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + sme_debug("LFR runtime successfully set full roam scan period to %d -old value is %d - roam state is %s", + full_roam_scan_period, + neighbor_roam_info->cfgParams.full_roam_scan_period, + mac_trace_get_neighbour_roam_state( + neighbor_roam_info->neighborRoamState)); + neighbor_roam_info->cfgParams.full_roam_scan_period = + full_roam_scan_period; + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, vdev_id, + REASON_ROAM_FULL_SCAN_PERIOD_CHANGED); + + sme_release_global_lock(&mac->sme); + + return status; +} + +QDF_STATUS +sme_modify_roam_cand_sel_criteria(mac_handle_t mac_handle, + uint8_t vdev_id, + bool enable_scoring_for_roam) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tpCsrNeighborRoamControlInfo neighbor_roam_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + if (!mac->mlme_cfg->lfr.roam_scan_offload_enabled) { + status = QDF_STATUS_E_INVAL; + goto out; + } + + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + neighbor_roam_info->cfgParams.enable_scoring_for_roam = + enable_scoring_for_roam; + status = csr_roam_update_cfg(mac, vdev_id, + REASON_SCORING_CRITERIA_CHANGED); +out: + sme_release_global_lock(&mac->sme); + + return status; +} + +/** + * sme_restore_default_roaming_params() - Restore neighbor roam config + * @mac: mac context + * @roam_info: Neighbor roam info pointer to be populated + * + * Restore neighbor roam info request params with lfr config params + * + * Return: None + */ +static void +sme_restore_default_roaming_params(struct mac_context *mac, + tCsrNeighborRoamControlInfo *roam_info) +{ + roam_info->cfgParams.enable_scoring_for_roam = + mac->mlme_cfg->scoring.enable_scoring_for_roam; + roam_info->cfgParams.emptyScanRefreshPeriod = + mac->mlme_cfg->lfr.empty_scan_refresh_period; + roam_info->cfgParams.full_roam_scan_period = + mac->mlme_cfg->lfr.roam_full_scan_period; + roam_info->cfgParams.maxChannelScanTime = + mac->mlme_cfg->lfr.neighbor_scan_max_chan_time; + roam_info->cfgParams.neighborScanPeriod = + mac->mlme_cfg->lfr.neighbor_scan_timer_period; + roam_info->cfgParams.neighborLookupThreshold = + mac->mlme_cfg->lfr.neighbor_lookup_rssi_threshold; + roam_info->cfgParams.roam_rssi_diff = + mac->mlme_cfg->lfr.roam_rssi_diff; + roam_info->cfgParams.maxChannelScanTime = + mac->mlme_cfg->lfr.neighbor_scan_max_chan_time; + roam_info->cfgParams.roam_scan_home_away_time = + mac->mlme_cfg->lfr.roam_scan_home_away_time; + roam_info->cfgParams.roam_scan_n_probes = + mac->mlme_cfg->lfr.roam_scan_n_probes; + roam_info->cfgParams.roam_scan_inactivity_time = + mac->mlme_cfg->lfr.roam_scan_inactivity_time; + roam_info->cfgParams.roam_inactive_data_packet_count = + mac->mlme_cfg->lfr.roam_inactive_data_packet_count; + roam_info->cfgParams.roam_scan_period_after_inactivity = + mac->mlme_cfg->lfr.roam_scan_period_after_inactivity; +} + +void sme_roam_reset_configs(mac_handle_t mac_handle, uint8_t vdev_id) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tCsrNeighborRoamControlInfo *neighbor_roam_info; + + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + sme_restore_default_roaming_params(mac, neighbor_roam_info); +} + +QDF_STATUS sme_roam_control_restore_default_config(mac_handle_t mac_handle, + uint8_t vdev_id) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tpCsrNeighborRoamControlInfo neighbor_roam_info; + tCsrChannelInfo *chan_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + if (!mac->mlme_cfg->lfr.roam_scan_offload_enabled) { + sme_err("roam_scan_offload_enabled is not supported"); + status = QDF_STATUS_E_INVAL; + goto out; + } + + mac->roam.configParam.nRoamScanControl = false; + + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + + chan_info = &neighbor_roam_info->cfgParams.pref_chan_info; + csr_flush_cfg_bg_scan_roam_channel_list(chan_info); + + chan_info = &neighbor_roam_info->cfgParams.specific_chan_info; + csr_flush_cfg_bg_scan_roam_channel_list(chan_info); + + mlme_reinit_control_config_lfr_params(mac->psoc, &mac->mlme_cfg->lfr); + + sme_restore_default_roaming_params(mac, neighbor_roam_info); + + /* Flush static and dynamic channels in ROAM scan list in firmware */ + csr_roam_update_cfg(mac, vdev_id, REASON_FLUSH_CHANNEL_LIST); + csr_roam_update_cfg(mac, vdev_id, REASON_SCORING_CRITERIA_CHANGED); + +out: + sme_release_global_lock(&mac->sme); + + return status; +} + +/* + * sme_set_neighbor_scan_min_chan_time() - + * Update nNeighborScanMinChanTime + * This function is called through dynamic setConfig callback function + * to configure gNeighborScanChannelMinTime + * Usage: adb shell iwpriv wlan0 setConfig + * gNeighborScanChannelMinTime=[0 .. 60] + * + * mac_handle: Opaque handle to the global MAC context + * nNeighborScanMinChanTime - Channel minimum dwell time + * sessionId - Session Identifier + * Return Success or failure + */ +QDF_STATUS sme_set_neighbor_scan_min_chan_time(mac_handle_t mac_handle, + const uint16_t + nNeighborScanMinChanTime, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR runtime successfully set channel min dwell time to %d - old value is %d - roam state is %s", + nNeighborScanMinChanTime, + mac->mlme_cfg->lfr.neighbor_scan_min_chan_time, + mac_trace_get_neighbour_roam_state(mac->roam. + neighborRoamInfo + [sessionId]. + neighborRoamState)); + + mac->mlme_cfg->lfr.neighbor_scan_min_chan_time = + nNeighborScanMinChanTime; + mac->roam.neighborRoamInfo[sessionId].cfgParams. + minChannelScanTime = nNeighborScanMinChanTime; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_set_neighbor_scan_max_chan_time() - + * Update nNeighborScanMaxChanTime + * This function is called through dynamic setConfig callback function + * to configure gNeighborScanChannelMaxTime + * Usage: adb shell iwpriv wlan0 setConfig + * gNeighborScanChannelMaxTime=[0 .. 60] + * + * mac_handle: Opaque handle to the global MAC context + * sessionId - Session Identifier + * nNeighborScanMinChanTime - Channel maximum dwell time + * Return Success or failure + */ +QDF_STATUS sme_set_neighbor_scan_max_chan_time(mac_handle_t mac_handle, + uint8_t sessionId, + const uint16_t + nNeighborScanMaxChanTime) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = NULL; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + pNeighborRoamInfo = &mac->roam.neighborRoamInfo[sessionId]; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR runtime successfully set channel max dwell time to %d - old value is %d - roam state is %s", + nNeighborScanMaxChanTime, + pNeighborRoamInfo->cfgParams.maxChannelScanTime, + mac_trace_get_neighbour_roam_state(mac->roam. + neighborRoamInfo + [sessionId]. + neighborRoamState)); + pNeighborRoamInfo->cfgParams.maxChannelScanTime = + nNeighborScanMaxChanTime; + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, sessionId, + REASON_SCAN_CH_TIME_CHANGED); + + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_get_neighbor_scan_min_chan_time() - + * get neighbor scan min channel time + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return uint16_t - channel min time value + */ +uint16_t sme_get_neighbor_scan_min_chan_time(mac_handle_t mac_handle, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return 0; + } + + return mac->roam.neighborRoamInfo[sessionId].cfgParams. + minChannelScanTime; +} + +/* + * sme_get_neighbor_roam_state() - + * get neighbor roam state + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return uint32_t - neighbor roam state + */ +uint32_t sme_get_neighbor_roam_state(mac_handle_t mac_handle, uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return 0; + } + + return mac->roam.neighborRoamInfo[sessionId].neighborRoamState; +} + +/* + * sme_get_current_roam_state() - + * get current roam state + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return uint32_t - current roam state + */ +uint32_t sme_get_current_roam_state(mac_handle_t mac_handle, uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->roam.curState[sessionId]; +} + +/* + * sme_get_current_roam_sub_state() - + * \brief get neighbor roam sub state + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return uint32_t - current roam sub state + */ +uint32_t sme_get_current_roam_sub_state(mac_handle_t mac_handle, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->roam.curSubState[sessionId]; +} + +/* + * sme_get_lim_sme_state() - + * get Lim Sme state + * + * mac_handle - The handle returned by mac_open. + * Return uint32_t - Lim Sme state + */ +uint32_t sme_get_lim_sme_state(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->lim.gLimSmeState; +} + +/* + * sme_get_lim_mlm_state() - + * get Lim Mlm state + * + * mac_handle - The handle returned by mac_open. + * Return uint32_t - Lim Mlm state + */ +uint32_t sme_get_lim_mlm_state(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->lim.gLimMlmState; +} + +/* + * sme_is_lim_session_valid() - + * is Lim session valid + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return bool - true or false + */ +bool sme_is_lim_session_valid(mac_handle_t mac_handle, uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (sessionId > mac->lim.maxBssId) + return false; + + return mac->lim.gpSession[sessionId].valid; +} + +/* + * sme_get_lim_sme_session_state() - + * get Lim Sme session state + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return uint32_t - Lim Sme session state + */ +uint32_t sme_get_lim_sme_session_state(mac_handle_t mac_handle, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->lim.gpSession[sessionId].limSmeState; +} + +/* + * sme_get_lim_mlm_session_state() - + * \brief get Lim Mlm session state + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return uint32_t - Lim Mlm session state + */ +uint32_t sme_get_lim_mlm_session_state(mac_handle_t mac_handle, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->lim.gpSession[sessionId].limMlmState; +} + +/* + * sme_get_neighbor_scan_max_chan_time() - + * get neighbor scan max channel time + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return uint16_t - channel max time value + */ +uint16_t sme_get_neighbor_scan_max_chan_time(mac_handle_t mac_handle, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return 0; + } + + return mac->roam.neighborRoamInfo[sessionId].cfgParams. + maxChannelScanTime; +} + +/* + * sme_set_neighbor_scan_period() - + * Update nNeighborScanPeriod + * This function is called through dynamic setConfig callback function + * to configure nNeighborScanPeriod + * Usage: adb shell iwpriv wlan0 setConfig + * nNeighborScanPeriod=[0 .. 1000] + * + * mac_handle: Opaque handle to the global MAC context + * sessionId - Session Identifier + * nNeighborScanPeriod - neighbor scan period + * Return Success or failure + */ +QDF_STATUS sme_set_neighbor_scan_period(mac_handle_t mac_handle, + uint8_t sessionId, + const uint16_t nNeighborScanPeriod) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = NULL; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + pNeighborRoamInfo = &mac->roam.neighborRoamInfo[sessionId]; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR runtime successfully set neighbor scan period to %d - old value is %d - roam state is %s", + nNeighborScanPeriod, + pNeighborRoamInfo->cfgParams.neighborScanPeriod, + mac_trace_get_neighbour_roam_state(mac->roam. + neighborRoamInfo + [sessionId]. + neighborRoamState)); + pNeighborRoamInfo->cfgParams.neighborScanPeriod = + nNeighborScanPeriod; + + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, sessionId, + REASON_SCAN_HOME_TIME_CHANGED); + + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_get_neighbor_scan_period() - + * get neighbor scan period + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return uint16_t - neighbor scan period + */ +uint16_t sme_get_neighbor_scan_period(mac_handle_t mac_handle, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return 0; + } + + return mac->roam.neighborRoamInfo[sessionId].cfgParams. + neighborScanPeriod; +} + +QDF_STATUS sme_get_roam_rssi_diff(mac_handle_t mac_handle, uint8_t vdev_id, + uint8_t *rssi_diff) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tCsrNeighborRoamControlInfo *neighbor_roam_info; + QDF_STATUS status; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + *rssi_diff = neighbor_roam_info->cfgParams.roam_rssi_diff; + sme_release_global_lock(&mac->sme); + + return QDF_STATUS_SUCCESS; +} + +void sme_dump_freq_list(tCsrChannelInfo *chan_info) +{ + uint8_t *channel_list; + uint8_t i = 0, j = 0; + uint32_t buflen = CFG_VALID_CHANNEL_LIST_LEN * 4; + + channel_list = qdf_mem_malloc(buflen); + if (!channel_list) + return; + + if (chan_info->freq_list) { + for (i = 0; i < chan_info->numOfChannels; i++) { + if (j < buflen) + j += snprintf(channel_list + j, buflen - j, + "%d ", chan_info->freq_list[i]); + else + break; + } + } + + sme_debug("frequency list [%u]: %s", i, channel_list); + qdf_mem_free(channel_list); +} + +static bool sme_validate_freq_list(mac_handle_t mac_handle, + uint32_t *freq_list, + uint8_t num_channels) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t i = 0, j; + bool found; + struct csr_channel *ch_lst_info = &mac_ctx->scan.base_channels; + + if (!freq_list || !num_channels) { + sme_err("Freq list empty %pK or num_channels is 0", freq_list); + return false; + } + + while (i < num_channels) { + found = false; + for (j = 0; j < ch_lst_info->numChannels; j++) { + if (ch_lst_info->channel_freq_list[j] == freq_list[i]) { + found = true; + break; + } + } + + if (!found) { + sme_debug("Invalid frequency %u", freq_list[i]); + return false; + } + + i++; + } + + return true; +} + +static uint8_t +csr_append_pref_chan_list(tCsrChannelInfo *chan_info, uint32_t *freq_list, + uint8_t num_chan) +{ + uint8_t i = 0, j = 0; + + for (i = 0; i < chan_info->numOfChannels; i++) { + for (j = 0; j < num_chan; j++) + if (chan_info->freq_list[i] == freq_list[j]) + break; + + if (j < num_chan) + continue; + if (num_chan == SIR_ROAM_MAX_CHANNELS) + break; + freq_list[num_chan++] = chan_info->freq_list[i]; + } + + return num_chan; +} + +/** + * sme_update_roam_scan_channel_list() - to update scan channel list + * @mac_handle: Opaque handle to the global MAC context + * @vdev_id: vdev identifier + * @chan_info: Channel information to be updated to. + * @freq_list: Frequency list to be updated from. + * @num_chan: Number of channels + * + * Updates the chan_info by flushing the current frequency list and update + * the same with freq_list. + * + * Return: QDF_STATUS + */ +static QDF_STATUS +sme_update_roam_scan_channel_list(mac_handle_t mac_handle, uint8_t vdev_id, + tCsrChannelInfo *chan_info, + uint32_t *freq_list, uint8_t num_chan) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + uint16_t pref_chan_cnt = 0; + + if (chan_info->numOfChannels) { + sme_debug("Current channels:"); + sme_dump_freq_list(chan_info); + } + + pref_chan_cnt = csr_append_pref_chan_list(chan_info, freq_list, + num_chan); + num_chan = pref_chan_cnt; + + csr_flush_cfg_bg_scan_roam_channel_list(chan_info); + csr_create_bg_scan_roam_channel_list(mac, chan_info, freq_list, + num_chan); + sme_debug("New channels:"); + sme_dump_freq_list(chan_info); + sme_debug("Updated roam scan channels - roam state is %d", + mac->roam.neighborRoamInfo[vdev_id].neighborRoamState); + + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + status = csr_roam_update_cfg(mac, vdev_id, + REASON_CHANNEL_LIST_CHANGED); + + return status; +} + +/** + * sme_change_roam_scan_channel_list() - to change scan channel list + * @mac_handle: Opaque handle to the global MAC context + * @sessionId: sme session id + * @channel_freq_list: Output channel list + * @numChannels: Output number of channels + * + * This routine is called to Change roam scan channel list. + * This is a synchronous call + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_change_roam_scan_channel_list(mac_handle_t mac_handle, + uint8_t sessionId, + uint32_t *channel_freq_list, + uint8_t numChannels) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = NULL; + uint8_t oldChannelList[CFG_VALID_CHANNEL_LIST_LEN * 5] = { 0 }; + uint8_t newChannelList[CFG_VALID_CHANNEL_LIST_LEN * 5] = { 0 }; + uint8_t i = 0, j = 0; + tCsrChannelInfo *chan_info; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + pNeighborRoamInfo = &mac->roam.neighborRoamInfo[sessionId]; + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to acquire SME lock"); + return status; + } + chan_info = &pNeighborRoamInfo->cfgParams.specific_chan_info; + + if (chan_info->freq_list) { + for (i = 0; i < chan_info->numOfChannels; i++) { + if (j < sizeof(oldChannelList)) + j += snprintf(oldChannelList + j, + sizeof(oldChannelList) - + j, " %d", + chan_info->freq_list[i]); + else + break; + } + } + csr_flush_cfg_bg_scan_roam_channel_list(chan_info); + csr_create_bg_scan_roam_channel_list(mac, chan_info, channel_freq_list, + numChannels); + sme_set_roam_scan_control(mac_handle, sessionId, 1); + if (chan_info->freq_list) { + j = 0; + for (i = 0; i < chan_info->numOfChannels; i++) { + if (j < sizeof(newChannelList)) + j += snprintf(newChannelList + j, + sizeof(newChannelList) - + j, " %d", chan_info->freq_list[i]); + else + break; + } + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR runtime successfully set roam scan channels to %s - old value is %s - roam state is %d", + newChannelList, oldChannelList, + mac->roam.neighborRoamInfo[sessionId].neighborRoamState); + + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, sessionId, + REASON_CHANNEL_LIST_CHANGED); + + sme_release_global_lock(&mac->sme); + return status; +} + +QDF_STATUS +sme_update_roam_scan_freq_list(mac_handle_t mac_handle, uint8_t vdev_id, + uint32_t *freq_list, uint8_t num_chan, + uint32_t freq_list_type) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tpCsrNeighborRoamControlInfo neighbor_roam_info; + tCsrChannelInfo *channel_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + if (!sme_validate_freq_list(mac_handle, freq_list, num_chan)) { + sme_err("List contains invalid channel(s)"); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to acquire SME lock"); + return status; + } + + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + if (neighbor_roam_info->cfgParams.specific_chan_info.numOfChannels && + freq_list_type == QCA_PREFERRED_SCAN_FREQ_LIST) { + sme_err("Specific channel list is already configured"); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_INVAL; + } + + sme_debug("frequency list type %d", freq_list_type); + if (freq_list_type == QCA_PREFERRED_SCAN_FREQ_LIST) { + channel_info = &neighbor_roam_info->cfgParams.pref_chan_info; + status = sme_update_roam_scan_channel_list( + mac_handle, vdev_id, channel_info, + freq_list, num_chan); + } else { + status = sme_change_roam_scan_channel_list(mac_handle, vdev_id, + freq_list, num_chan); + } + sme_release_global_lock(&mac->sme); + + return status; +} + +/** + * sme_get_roam_scan_channel_list() - To get roam scan channel list + * @mac_handle: Opaque handle to the global MAC context + * @freq_list: Output channel freq list + * @pNumChannels: Output number of channels + * @sessionId: Session Identifier + * + * To get roam scan channel list This is a synchronous call + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_roam_scan_channel_list(mac_handle_t mac_handle, + uint32_t *freq_list, uint8_t *pNumChannels, + uint8_t sessionId) +{ + int i = 0, chan_cnt = 0; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + tCsrChannelInfo *chan_info; + struct csr_channel *occupied_ch_lst = + &mac->scan.occupiedChannels[sessionId]; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + pNeighborRoamInfo = &mac->roam.neighborRoamInfo[sessionId]; + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + + chan_info = &pNeighborRoamInfo->cfgParams.specific_chan_info; + if (chan_info->numOfChannels) { + *pNumChannels = chan_info->numOfChannels; + for (i = 0; i < (*pNumChannels) && + i < WNI_CFG_VALID_CHANNEL_LIST_LEN; i++) + freq_list[i] = chan_info->freq_list[i]; + + *pNumChannels = i; + } else { + chan_info = &pNeighborRoamInfo->cfgParams.pref_chan_info; + *pNumChannels = chan_info->numOfChannels; + if (chan_info->numOfChannels) { + for (chan_cnt = 0; chan_cnt < (*pNumChannels) && + chan_cnt < WNI_CFG_VALID_CHANNEL_LIST_LEN; + chan_cnt++) + freq_list[chan_cnt] = + chan_info->freq_list[chan_cnt]; + } + + if (occupied_ch_lst->numChannels) { + for (i = 0; i < occupied_ch_lst->numChannels && + chan_cnt < WNI_CFG_VALID_CHANNEL_LIST_LEN; i++) + freq_list[chan_cnt++] = + occupied_ch_lst->channel_freq_list[i]; + } + + *pNumChannels = chan_cnt; + if (!(chan_info->numOfChannels || + occupied_ch_lst->numChannels)) { + sme_err("Roam Scan channel list is NOT yet initialized"); + *pNumChannels = 0; + status = QDF_STATUS_E_INVAL; + } + } + + sme_release_global_lock(&mac->sme); + return status; +} + +/* + * sme_get_is_ese_feature_enabled() - get ESE feature enabled or not + * This is a synchronuous call + * + * mac_handle - The handle returned by mac_open. + * Return true (1) - if the ESE feature is enabled + * false (0) - if feature is disabled (compile or runtime) + */ +bool sme_get_is_ese_feature_enabled(mac_handle_t mac_handle) +{ +#ifdef FEATURE_WLAN_ESE + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return csr_roam_is_ese_ini_feature_enabled(mac); +#else + return false; +#endif +} + +/* + * sme_get_wes_mode() - get WES Mode + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open + * Return uint8_t - WES Mode Enabled(1)/Disabled(0) + */ +bool sme_get_wes_mode(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->mlme_cfg->lfr.wes_mode_enabled; +} + +/* + * sme_get_roam_scan_control() - get scan control + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open. + * Return bool - Enabled(1)/Disabled(0) + */ +bool sme_get_roam_scan_control(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->roam.configParam.nRoamScanControl; +} + +/* + * sme_get_is_lfr_feature_enabled() - get LFR feature enabled or not + * This is a synchronuous call + * mac_handle - The handle returned by mac_open. + * Return true (1) - if the feature is enabled + * false (0) - if feature is disabled (compile or runtime) + */ +bool sme_get_is_lfr_feature_enabled(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->mlme_cfg->lfr.lfr_enabled; +} + +/* + * sme_get_is_ft_feature_enabled() - get FT feature enabled or not + * This is a synchronuous call + * + * mac_handle - The handle returned by mac_open. + * Return true (1) - if the feature is enabled + * false (0) - if feature is disabled (compile or runtime) + */ +bool sme_get_is_ft_feature_enabled(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->mlme_cfg->lfr.fast_transition_enabled; +} + +/** + * sme_is_feature_supported_by_fw() - check if feature is supported by FW + * @feature: enum value of requested feature. + * + * Retrun: 1 if supported; 0 otherwise + */ +bool sme_is_feature_supported_by_fw(enum cap_bitmap feature) +{ + return IS_FEATURE_SUPPORTED_BY_FW(feature); +} + +QDF_STATUS sme_get_link_speed(mac_handle_t mac_handle, + struct link_speed_info *req, + void *context, + sme_link_speed_cb cb) +{ + QDF_STATUS status; + struct mac_context *mac; + void *wma_handle; + + if (!mac_handle || !cb || !req) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid parameter")); + return QDF_STATUS_E_FAILURE; + } + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mac = MAC_CONTEXT(mac_handle); + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS != status) { + sme_err("Failed to acquire global lock"); + return QDF_STATUS_E_FAILURE; + } + + mac->sme.link_speed_context = context; + mac->sme.link_speed_cb = cb; + status = wma_get_link_speed(wma_handle, req); + sme_release_global_lock(&mac->sme); + return status; +} + +QDF_STATUS sme_get_peer_info_ext(mac_handle_t mac_handle, + struct sir_peer_info_ext_req *req, + void *context, + void (*callbackfn)(struct sir_peer_info_ext_resp *param, + void *pcontext)) +{ + QDF_STATUS status; + QDF_STATUS qdf_status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + if (!callbackfn) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Indication Call back is NULL", + __func__); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + mac->sme.pget_peer_info_ext_ind_cb = callbackfn; + mac->sme.pget_peer_info_ext_cb_context = context; + + /* serialize the req through MC thread */ + message.bodyptr = + qdf_mem_malloc(sizeof(struct sir_peer_info_ext_req)); + if (!message.bodyptr) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(message.bodyptr, + req, + sizeof(struct sir_peer_info_ext_req)); + message.type = WMA_GET_PEER_INFO_EXT; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Post get rssi msg fail", __func__); + qdf_mem_free(message.bodyptr); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return status; +} + +QDF_STATUS sme_get_isolation(mac_handle_t mac_handle, void *context, + sme_get_isolation_cb callbackfn) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + + if (!callbackfn) { + sme_err("Indication Call back is NULL"); + return QDF_STATUS_E_FAILURE; + } + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + mac->sme.get_isolation_cb = callbackfn; + mac->sme.get_isolation_cb_context = context; + message.bodyptr = NULL; + message.type = WMA_GET_ISOLATION; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("failed to post WMA_GET_ISOLATION"); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + return status; +} + +/*convert the ini value to the ENUM used in csr and MAC for CB state*/ +ePhyChanBondState sme_get_cb_phy_state_from_cb_ini_value(uint32_t cb_ini_value) +{ + return csr_convert_cb_ini_value_to_phy_cb_state(cb_ini_value); +} + +void sme_set_curr_device_mode(mac_handle_t mac_handle, + enum QDF_OPMODE curr_device_mode) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + mac->sme.curr_device_mode = curr_device_mode; +} + +/* + * sme_handoff_request() - a wrapper function to Request a handoff from CSR. + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open + * sessionId - Session Identifier + * pHandoffInfo - info provided by HDD with the handoff request (namely: + * BSSID, channel etc.) + * Return QDF_STATUS_SUCCESS - SME passed the request to CSR successfully. + * Other status means SME is failed to send the request. + */ + +QDF_STATUS sme_handoff_request(mac_handle_t mac_handle, + uint8_t sessionId, + tCsrHandoffRequest *pHandoffInfo) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: invoked", __func__); + status = csr_handoff_request(mac, sessionId, pHandoffInfo); + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/** + * sme_add_periodic_tx_ptrn() - Add Periodic TX Pattern + * @mac_handle: Opaque handle to the global MAC context + * @addPeriodicTxPtrnParams: request message + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS +sme_add_periodic_tx_ptrn(mac_handle_t mac_handle, + struct sSirAddPeriodicTxPtrn *addPeriodicTxPtrnParams) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct sSirAddPeriodicTxPtrn *req_msg; + struct scheduler_msg msg = {0}; + + SME_ENTER(); + + req_msg = qdf_mem_malloc(sizeof(*req_msg)); + if (!req_msg) + return QDF_STATUS_E_NOMEM; + + *req_msg = *addPeriodicTxPtrnParams; + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("sme_acquire_global_lock failed!(status=%d)", + status); + qdf_mem_free(req_msg); + return status; + } + + /* Serialize the req through MC thread */ + msg.bodyptr = req_msg; + msg.type = WMA_ADD_PERIODIC_TX_PTRN_IND; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, msg.type)); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", + status); + qdf_mem_free(req_msg); + } + sme_release_global_lock(&mac->sme); + return status; +} + +/** + * sme_del_periodic_tx_ptrn() - Delete Periodic TX Pattern + * @mac_handle: Opaque handle to the global MAC context + * @delPeriodicTxPtrnParams: request message + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS +sme_del_periodic_tx_ptrn(mac_handle_t mac_handle, + struct sSirDelPeriodicTxPtrn *delPeriodicTxPtrnParams) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct sSirDelPeriodicTxPtrn *req_msg; + struct scheduler_msg msg = {0}; + + SME_ENTER(); + + req_msg = qdf_mem_malloc(sizeof(*req_msg)); + if (!req_msg) + return QDF_STATUS_E_NOMEM; + + *req_msg = *delPeriodicTxPtrnParams; + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("sme_acquire_global_lock failed!(status=%d)", + status); + qdf_mem_free(req_msg); + return status; + } + + /* Serialize the req through MC thread */ + msg.bodyptr = req_msg; + msg.type = WMA_DEL_PERIODIC_TX_PTRN_IND; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, msg.type)); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", + status); + qdf_mem_free(req_msg); + } + sme_release_global_lock(&mac->sme); + return status; +} + +#ifdef FEATURE_WLAN_RMC +/* + * sme_enable_rmc() - enables RMC + * @mac_handle: Opaque handle to the global MAC context + * @sessionId : Session ID + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_enable_rmc(mac_handle_t mac_handle, uint32_t sessionId) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + SME_ENTER(); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + message.bodyptr = NULL; + message.type = WMA_RMC_ENABLE_IND; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + status = QDF_STATUS_E_FAILURE; + + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_disable_rmc() - disables RMC + * @mac_handle: Opaque handle to the global MAC context + * @sessionId : Session ID + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_disable_rmc(mac_handle_t mac_handle, uint32_t sessionId) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + SME_ENTER(); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + message.bodyptr = NULL; + message.type = WMA_RMC_DISABLE_IND; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + status = QDF_STATUS_E_FAILURE; + + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_send_rmc_action_period() - sends RMC action period param to target + * @mac_handle: Opaque handle to the global MAC context + * @sessionId : Session ID + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_send_rmc_action_period(mac_handle_t mac_handle, + uint32_t sessionId) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + message.bodyptr = NULL; + message.type = WMA_RMC_ACTION_PERIOD_IND; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + status = QDF_STATUS_E_FAILURE; + + sme_release_global_lock(&mac->sme); + } + + return status; +} +#endif /* FEATURE_WLAN_RMC */ + +/* + * sme_send_cesium_enable_ind() - + * Used to send proprietary cesium enable indication to fw + * + * mac_handle + * sessionId + * Return QDF_STATUS + */ +QDF_STATUS sme_send_cesium_enable_ind(mac_handle_t mac_handle, + uint32_t sessionId) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + message.bodyptr = NULL; + message.type = WMA_IBSS_CESIUM_ENABLE_IND; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + sme_release_global_lock(&mac->sme); + } + + return status; +} + +QDF_STATUS sme_set_wlm_latency_level(mac_handle_t mac_handle, + uint16_t session_id, + uint16_t latency_level) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct wlm_latency_level_param params; + void *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) + return QDF_STATUS_E_FAILURE; + + if (!mac_ctx->mlme_cfg->wlm_config.latency_enable) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: WLM latency level setting is disabled", + __func__); + return QDF_STATUS_E_FAILURE; + } + if (!wma) { + sme_err("wma is NULL"); + return QDF_STATUS_E_FAILURE; + } + + params.wlm_latency_level = latency_level; + params.wlm_latency_flags = + mac_ctx->mlme_cfg->wlm_config.latency_flags[latency_level]; + params.vdev_id = session_id; + + status = wma_set_wlm_latency_level(wma, ¶ms); + + return status; +} + +void sme_get_command_q_status(mac_handle_t mac_handle) +{ + tSmeCmd *pTempCmd = NULL; + tListElem *pEntry; + struct mac_context *mac; + + if (!mac_handle) + return; + + mac = MAC_CONTEXT(mac_handle); + + pEntry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (pEntry) + pTempCmd = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + + sme_err("WLAN_BUG_RCA: Currently smeCmdActiveList has command (0x%X)", + (pTempCmd) ? pTempCmd->command : eSmeNoCommand); + if (pTempCmd) { + if (eSmeCsrCommandMask & pTempCmd->command) + /* CSR command is stuck. See what the reason code is + * for that command + */ + dump_csr_command_info(mac, pTempCmd); + } /* if(pTempCmd) */ + + sme_err("Currently smeCmdPendingList has %d commands", + wlan_serialization_get_pending_list_count(mac->psoc, false)); + +} + +#ifdef WLAN_FEATURE_DSRC +/** + * sme_ocb_gen_timing_advert_frame() - generate TA frame and populate the buffer + * @mac_handle: Opaque handle to the global MAC context + * @self_addr: the self MAC address + * @buf: the buffer that will contain the frame + * @timestamp_offset: return for the offset of the timestamp field + * @time_value_offset: return for the time_value field in the TA IE + * + * Return: the length of the buffer on success and error code on failure. + */ +int sme_ocb_gen_timing_advert_frame(mac_handle_t mac_handle, + tSirMacAddr self_addr, uint8_t **buf, + uint32_t *timestamp_offset, + uint32_t *time_value_offset) +{ + int template_length; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + template_length = sch_gen_timing_advert_frame(mac_ctx, self_addr, buf, + timestamp_offset, + time_value_offset); + return template_length; +} +#endif + +QDF_STATUS sme_notify_modem_power_state(mac_handle_t mac_handle, uint32_t value) +{ + struct scheduler_msg msg = {0}; + tpSirModemPowerStateInd request_buf; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) + return QDF_STATUS_E_FAILURE; + + request_buf = qdf_mem_malloc(sizeof(tSirModemPowerStateInd)); + if (!request_buf) + return QDF_STATUS_E_NOMEM; + + request_buf->param = value; + + msg.type = WMA_MODEM_POWER_STATE_IND; + msg.reserved = 0; + msg.bodyptr = request_buf; + if (!QDF_IS_STATUS_SUCCESS + (scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WMA_MODEM_POWER_STATE_IND message to WMA", + __func__); + qdf_mem_free(request_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef QCA_HT_2040_COEX +QDF_STATUS sme_notify_ht2040_mode(mac_handle_t mac_handle, + struct qdf_mac_addr macAddrSTA, + uint8_t sessionId, + uint8_t channel_type) +{ + struct scheduler_msg msg = {0}; + tUpdateVHTOpMode *pHtOpMode = NULL; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) + return QDF_STATUS_E_FAILURE; + + pHtOpMode = qdf_mem_malloc(sizeof(tUpdateVHTOpMode)); + if (!pHtOpMode) + return QDF_STATUS_E_NOMEM; + + switch (channel_type) { + case eHT_CHAN_HT20: + pHtOpMode->opMode = eHT_CHANNEL_WIDTH_20MHZ; + break; + + case eHT_CHAN_HT40MINUS: + case eHT_CHAN_HT40PLUS: + pHtOpMode->opMode = eHT_CHANNEL_WIDTH_40MHZ; + break; + + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid OP mode", __func__); + qdf_mem_free(pHtOpMode); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_copy(pHtOpMode->peer_mac, macAddrSTA.bytes, + sizeof(tSirMacAddr)); + pHtOpMode->smesessionId = sessionId; + + msg.type = WMA_UPDATE_OP_MODE; + msg.reserved = 0; + msg.bodyptr = pHtOpMode; + if (!QDF_IS_STATUS_SUCCESS + (scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WMA_UPDATE_OP_MODE message to WMA", + __func__); + qdf_mem_free(pHtOpMode); + return QDF_STATUS_E_FAILURE; + } + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: Notified FW about OP mode: %d", + __func__, pHtOpMode->opMode); + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_set_ht2040_mode() - + * To update HT Operation beacon IE. + * + * Return QDF_STATUS SUCCESS + * FAILURE or RESOURCES + * The API finished and failed. + */ +QDF_STATUS sme_set_ht2040_mode(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t channel_type, bool obssEnabled) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + ePhyChanBondState cbMode; + struct csr_roam_session *session = CSR_GET_SESSION(mac, sessionId); + + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + sme_err("Session not valid for session id %d", sessionId); + return QDF_STATUS_E_INVAL; + } + session = CSR_GET_SESSION(mac, sessionId); + sme_debug("Update HT operation beacon IE, channel_type=%d cur cbmode %d", + channel_type, session->bssParams.cbMode); + + switch (channel_type) { + case eHT_CHAN_HT20: + if (!session->bssParams.cbMode) + return QDF_STATUS_SUCCESS; + cbMode = PHY_SINGLE_CHANNEL_CENTERED; + break; + case eHT_CHAN_HT40MINUS: + if (session->bssParams.cbMode) + return QDF_STATUS_SUCCESS; + cbMode = PHY_DOUBLE_CHANNEL_HIGH_PRIMARY; + break; + case eHT_CHAN_HT40PLUS: + if (session->bssParams.cbMode) + return QDF_STATUS_SUCCESS; + cbMode = PHY_DOUBLE_CHANNEL_LOW_PRIMARY; + break; + default: + sme_err("Error!!! Invalid HT20/40 mode !"); + return QDF_STATUS_E_FAILURE; + } + session->bssParams.cbMode = cbMode; + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_set_ht2040_mode(mac, sessionId, + cbMode, obssEnabled); + sme_release_global_lock(&mac->sme); + } + return status; +} + +#endif + +/* + * SME API to enable/disable idle mode powersave + * This should be called only if powersave offload + * is enabled + */ +QDF_STATUS sme_set_idle_powersave_config(bool value) +{ + void *wmaContext = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wmaContext) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: wmaContext is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + " Idle Ps Set Value %d", value); + + if (QDF_STATUS_SUCCESS != wma_set_idle_ps_config(wmaContext, value)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + " Failed to Set Idle Ps Value %d", value); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +int16_t sme_get_ht_config(mac_handle_t mac_handle, uint8_t session_id, + uint16_t ht_capab) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, session_id); + + if (!pSession) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: pSession is NULL", __func__); + return -EIO; + } + switch (ht_capab) { + case WNI_CFG_HT_CAP_INFO_ADVANCE_CODING: + return pSession->ht_config.ht_rx_ldpc; + case WNI_CFG_HT_CAP_INFO_TX_STBC: + return pSession->ht_config.ht_tx_stbc; + case WNI_CFG_HT_CAP_INFO_RX_STBC: + return pSession->ht_config.ht_rx_stbc; + case WNI_CFG_HT_CAP_INFO_SHORT_GI_20MHZ: + return pSession->ht_config.ht_sgi20; + case WNI_CFG_HT_CAP_INFO_SHORT_GI_40MHZ: + return pSession->ht_config.ht_sgi40; + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "invalid ht capability"); + return -EIO; + } +} + +int sme_update_ht_config(mac_handle_t mac_handle, uint8_t sessionId, + uint16_t htCapab, int value) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: pSession is NULL", __func__); + return -EIO; + } + + if (QDF_STATUS_SUCCESS != wma_set_htconfig(sessionId, htCapab, value)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Failed to set ht capability in target"); + return -EIO; + } + + switch (htCapab) { + case WNI_CFG_HT_CAP_INFO_ADVANCE_CODING: + pSession->ht_config.ht_rx_ldpc = value; + break; + case WNI_CFG_HT_CAP_INFO_TX_STBC: + pSession->ht_config.ht_tx_stbc = value; + break; + case WNI_CFG_HT_CAP_INFO_RX_STBC: + pSession->ht_config.ht_rx_stbc = value; + break; + case WNI_CFG_HT_CAP_INFO_SHORT_GI_20MHZ: + value = value ? 1 : 0; /* HT SGI can be only 1 or 0 */ + pSession->ht_config.ht_sgi20 = value; + break; + case WNI_CFG_HT_CAP_INFO_SHORT_GI_40MHZ: + value = value ? 1 : 0; /* HT SGI can be only 1 or 0 */ + pSession->ht_config.ht_sgi40 = value; + break; + } + + csr_roam_update_config(mac, sessionId, htCapab, value); + return 0; +} + +int sme_set_addba_accept(mac_handle_t mac_handle, uint8_t session_id, int value) +{ + struct sme_addba_accept *addba_accept; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + addba_accept = qdf_mem_malloc(sizeof(*addba_accept)); + if (!addba_accept) + return -EIO; + + addba_accept->session_id = session_id; + addba_accept->addba_accept = value; + qdf_mem_zero(&msg, sizeof(msg)); + msg.type = eWNI_SME_SET_ADDBA_ACCEPT; + msg.reserved = 0; + msg.bodyptr = addba_accept; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (status != QDF_STATUS_SUCCESS) { + sme_err("Not able to post addba reject"); + qdf_mem_free(addba_accept); + return -EIO; + } + return 0; +} + +int sme_set_ba_buff_size(mac_handle_t mac_handle, uint8_t session_id, + uint16_t buff_size) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + if (!buff_size) { + sme_err("invalid buff size %d", buff_size); + return -EINVAL; + } + mac_ctx->usr_cfg_ba_buff_size = buff_size; + sme_debug("addba buff size is set to %d", + mac_ctx->usr_cfg_ba_buff_size); + + return 0; +} + +#define DEFAULT_BA_BUFF_SIZE 64 +int sme_send_addba_req(mac_handle_t mac_handle, uint8_t session_id, uint8_t tid, + uint16_t buff_size) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint16_t ba_buff = 0; + QDF_STATUS status; + struct scheduler_msg msg = {0}; + struct send_add_ba_req *send_ba_req; + struct csr_roam_session *csr_session = NULL; + + if (!csr_is_conn_state_connected_infra(mac_ctx, session_id)) { + sme_err("STA not infra/connected state session_id: %d", + session_id); + return -EINVAL; + } + csr_session = CSR_GET_SESSION(mac_ctx, session_id); + if (!csr_session) { + sme_err("CSR session is NULL"); + return -EINVAL; + } + send_ba_req = qdf_mem_malloc(sizeof(*send_ba_req)); + if (!send_ba_req) + return -EIO; + + qdf_mem_copy(send_ba_req->mac_addr, + csr_session->connectedProfile.bssid.bytes, + QDF_MAC_ADDR_SIZE); + ba_buff = buff_size; + if (!buff_size) { + if (mac_ctx->usr_cfg_ba_buff_size) + ba_buff = mac_ctx->usr_cfg_ba_buff_size; + else + ba_buff = DEFAULT_BA_BUFF_SIZE; + } + send_ba_req->param.vdev_id = session_id; + send_ba_req->param.tidno = tid; + send_ba_req->param.buffersize = ba_buff; + msg.type = WMA_SEND_ADDBA_REQ; + msg.bodyptr = send_ba_req; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (QDF_STATUS_SUCCESS != status) { + qdf_mem_free(send_ba_req); + return -EIO; + } + sme_debug("ADDBA_REQ sent to FW: tid %d buff_size %d", tid, ba_buff); + + return 0; +} + +int sme_set_no_ack_policy(mac_handle_t mac_handle, uint8_t session_id, + uint8_t val, uint8_t ac) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t i, set_val; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + if (ac > QCA_WLAN_AC_ALL) { + sme_err("invalid ac val %d", ac); + return -EINVAL; + } + if (val) + set_val = 1; + else + set_val = 0; + if (ac == QCA_WLAN_AC_ALL) { + for (i = 0; i < ac; i++) + mac_ctx->no_ack_policy_cfg[i] = set_val; + } else { + mac_ctx->no_ack_policy_cfg[ac] = set_val; + } + sme_debug("no ack is set to %d for ac %d", set_val, ac); + qdf_mem_zero(&msg, sizeof(msg)); + msg.type = eWNI_SME_UPDATE_EDCA_PROFILE; + msg.reserved = 0; + msg.bodyval = session_id; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (status != QDF_STATUS_SUCCESS) { + sme_err("Not able to post update edca profile"); + return -EIO; + } + + return 0; +} + +int sme_set_auto_rate_he_ltf(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint32_t set_val; + uint32_t bit_mask = 0; + int status; + + if (cfg_val > QCA_WLAN_HE_LTF_4X) { + sme_err("invalid HE LTF cfg %d", cfg_val); + return -EINVAL; + } + + /*set the corresponding HE LTF cfg BIT*/ + if (cfg_val == QCA_WLAN_HE_LTF_AUTO) + bit_mask = HE_LTF_ALL; + else + bit_mask = (1 << (cfg_val - 1)); + + set_val = mac_ctx->he_sgi_ltf_cfg_bit_mask; + + SET_AUTO_RATE_HE_LTF_VAL(set_val, bit_mask); + + mac_ctx->he_sgi_ltf_cfg_bit_mask = set_val; + status = wma_cli_set_command(session_id, + WMI_VDEV_PARAM_AUTORATE_MISC_CFG, + set_val, VDEV_CMD); + if (status) { + sme_err("failed to set he_ltf_sgi"); + return status; + } + + sme_debug("HE SGI_LTF is set to 0x%08X", + mac_ctx->he_sgi_ltf_cfg_bit_mask); + + return 0; +} + +int sme_set_auto_rate_he_sgi(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint32_t set_val; + uint32_t sgi_bit_mask = 0; + int status; + + if ((cfg_val > AUTO_RATE_GI_3200NS) || + (cfg_val < AUTO_RATE_GI_400NS)) { + sme_err("invalid auto rate GI cfg %d", cfg_val); + return -EINVAL; + } + + sgi_bit_mask = (1 << cfg_val); + + set_val = mac_ctx->he_sgi_ltf_cfg_bit_mask; + SET_AUTO_RATE_SGI_VAL(set_val, sgi_bit_mask); + + mac_ctx->he_sgi_ltf_cfg_bit_mask = set_val; + status = wma_cli_set_command(session_id, + WMI_VDEV_PARAM_AUTORATE_MISC_CFG, + set_val, VDEV_CMD); + if (status) { + sme_err("failed to set he_ltf_sgi"); + return status; + } + + sme_debug("auto rate HE SGI_LTF is set to 0x%08X", + mac_ctx->he_sgi_ltf_cfg_bit_mask); + + return 0; +} + +int sme_set_auto_rate_ldpc(mac_handle_t mac_handle, uint8_t session_id, + uint8_t ldpc_disable) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint32_t set_val; + int status; + + set_val = mac_ctx->he_sgi_ltf_cfg_bit_mask; + + set_val |= (ldpc_disable << AUTO_RATE_LDPC_DIS_BIT); + + status = wma_cli_set_command(session_id, + WMI_VDEV_PARAM_AUTORATE_MISC_CFG, + set_val, VDEV_CMD); + if (status) { + sme_err("failed to set auto rate LDPC cfg"); + return status; + } + + sme_debug("auto rate misc cfg set to 0x%08X", set_val); + + return 0; +} + +#define HT20_SHORT_GI_MCS7_RATE 722 +/* + * sme_send_rate_update_ind() - + * API to Update rate + * + * mac_handle - The handle returned by mac_open + * rateUpdateParams - Pointer to rate update params + * Return QDF_STATUS + */ +QDF_STATUS sme_send_rate_update_ind(mac_handle_t mac_handle, + tSirRateUpdateInd *rateUpdateParams) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + struct scheduler_msg msg = {0}; + tSirRateUpdateInd *rate_upd = qdf_mem_malloc(sizeof(tSirRateUpdateInd)); + + if (!rate_upd) + return QDF_STATUS_E_FAILURE; + + *rate_upd = *rateUpdateParams; + + if (rate_upd->mcastDataRate24GHz == HT20_SHORT_GI_MCS7_RATE) + rate_upd->mcastDataRate24GHzTxFlag = + TX_RATE_HT20 | TX_RATE_SGI; + else if (rate_upd->reliableMcastDataRate == + HT20_SHORT_GI_MCS7_RATE) + rate_upd->reliableMcastDataRateTxFlag = + TX_RATE_HT20 | TX_RATE_SGI; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(rate_upd); + return status; + } + + msg.type = WMA_RATE_UPDATE_IND; + msg.bodyptr = rate_upd; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, msg.type)); + + status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WMA_RATE_UPDATE_IND to WMA!", + __func__); + qdf_mem_free(rate_upd); + status = QDF_STATUS_E_FAILURE; + } + + sme_release_global_lock(&mac->sme); + + return status; +} + +/** + * sme_update_access_policy_vendor_ie() - update vendor ie and access policy. + * @mac_handle: Pointer to the mac context + * @vdev_id: vdev_id + * @vendor_ie: vendor ie + * @access_policy: vendor ie access policy + * + * This function updates the vendor ie and access policy to lim. + * + * Return: success or failure. + */ +QDF_STATUS sme_update_access_policy_vendor_ie(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t *vendor_ie, + int access_policy) +{ + struct sme_update_access_policy_vendor_ie *msg; + uint16_t msg_len; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + msg_len = sizeof(*msg); + + msg = qdf_mem_malloc(msg_len); + if (!msg) { + return QDF_STATUS_E_NOMEM; + } + + msg->msg_type = (uint16_t)eWNI_SME_UPDATE_ACCESS_POLICY_VENDOR_IE; + msg->length = (uint16_t)msg_len; + + qdf_mem_copy(&msg->ie[0], vendor_ie, sizeof(msg->ie)); + + msg->vdev_id = vdev_id; + msg->access_policy = access_policy; + + sme_debug("vdev_id: %hu, access_policy: %d", vdev_id, access_policy); + status = umac_send_mb_message_to_mac(msg); + + return status; +} + +/** + * sme_update_sta_inactivity_timeout(): Update sta_inactivity_timeout to FW + * @mac_handle: Handle returned by mac_open + * @session_id: Session ID on which sta_inactivity_timeout needs + * to be updated to FW + * @sta_inactivity_timeout: sta inactivity timeout. + * + * If a station does not send anything in sta_inactivity_timeout seconds, an + * empty data frame is sent to it in order to verify whether it is + * still in range. If this frame is not ACKed, the station will be + * disassociated and then deauthenticated. + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure. + */ +QDF_STATUS sme_update_sta_inactivity_timeout(mac_handle_t mac_handle, + struct sme_sta_inactivity_timeout *sta_inactivity_timer) +{ + struct sme_sta_inactivity_timeout *inactivity_time; + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + inactivity_time = qdf_mem_malloc(sizeof(*inactivity_time)); + if (!inactivity_time) + return QDF_STATUS_E_FAILURE; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("sta_inactivity_timeout: %d"), + sta_inactivity_timer->sta_inactivity_timeout); + inactivity_time->session_id = sta_inactivity_timer->session_id; + inactivity_time->sta_inactivity_timeout = + sta_inactivity_timer->sta_inactivity_timeout; + + wma_update_sta_inactivity_timeout(wma_handle, inactivity_time); + qdf_mem_free(inactivity_time); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_get_reg_info(mac_handle_t mac_handle, uint32_t chan_freq, + uint32_t *regInfo1, uint32_t *regInfo2) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + uint8_t i; + bool found = false; + + status = sme_acquire_global_lock(&mac->sme); + *regInfo1 = 0; + *regInfo2 = 0; + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + + for (i = 0; i < CFG_VALID_CHANNEL_LIST_LEN; i++) { + if (mac->scan.defaultPowerTable[i].center_freq == chan_freq) { + SME_SET_CHANNEL_REG_POWER(*regInfo1, + mac->scan.defaultPowerTable[i].tx_power); + + SME_SET_CHANNEL_MAX_TX_POWER(*regInfo2, + mac->scan.defaultPowerTable[i].tx_power); + found = true; + break; + } + } + if (!found) + status = QDF_STATUS_E_FAILURE; + + sme_release_global_lock(&mac->sme); + return status; +} + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +QDF_STATUS sme_set_auto_shutdown_cb(mac_handle_t mac_handle, + void (*callback_fn)(void)) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Plug in Auto shutdown event callback", __func__); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + if (callback_fn) + mac->sme.auto_shutdown_cb = callback_fn; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +/* + * sme_set_auto_shutdown_timer() - + * API to set auto shutdown timer value in FW. + * + * mac_handle - The handle returned by mac_open + * timer_val - The auto shutdown timer value to be set + * Return Configuration message posting status, SUCCESS or Fail + */ +QDF_STATUS sme_set_auto_shutdown_timer(mac_handle_t mac_handle, + uint32_t timer_val) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct auto_shutdown_cmd *auto_sh_cmd; + struct scheduler_msg message = {0}; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + auto_sh_cmd = qdf_mem_malloc(sizeof(*auto_sh_cmd)); + if (!auto_sh_cmd) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + auto_sh_cmd->timer_val = timer_val; + + /* serialize the req through MC thread */ + message.bodyptr = auto_sh_cmd; + message.type = WMA_SET_AUTO_SHUTDOWN_TIMER_REQ; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Post Auto shutdown MSG fail", __func__); + qdf_mem_free(auto_sh_cmd); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: Posted Auto shutdown MSG", __func__); + sme_release_global_lock(&mac->sme); + } + + return status; +} +#endif + +#ifdef FEATURE_WLAN_CH_AVOID +/* + * sme_ch_avoid_update_req() - + * API to request channel avoidance update from FW. + * + * mac_handle - The handle returned by mac_open + * update_type - The update_type parameter of this request call + * Return Configuration message posting status, SUCCESS or Fail + */ +QDF_STATUS sme_ch_avoid_update_req(mac_handle_t mac_handle) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tSirChAvoidUpdateReq *cauReq; + struct scheduler_msg message = {0}; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + cauReq = qdf_mem_malloc(sizeof(tSirChAvoidUpdateReq)); + if (!cauReq) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + cauReq->reserved_param = 0; + + /* serialize the req through MC thread */ + message.bodyptr = cauReq; + message.type = WMA_CH_AVOID_UPDATE_REQ; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Post Ch Avoid Update MSG fail", + __func__); + qdf_mem_free(cauReq); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: Posted Ch Avoid Update MSG", __func__); + sme_release_global_lock(&mac->sme); + } + + return status; +} +#endif + +/** + * sme_set_miracast() - Function to set miracast value to UMAC + * @mac_handle: Handle returned by macOpen + * @filter_type: 0-Disabled, 1-Source, 2-sink + * + * This function passes down the value of miracast set by + * framework to UMAC + * + * Return: Configuration message posting status, SUCCESS or Fail + * + */ +QDF_STATUS sme_set_miracast(mac_handle_t mac_handle, uint8_t filter_type) +{ + struct scheduler_msg msg = {0}; + uint32_t *val; + struct mac_context *mac_ptr = MAC_CONTEXT(mac_handle); + + if (!mac_ptr) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Invalid pointer", __func__); + return QDF_STATUS_E_INVAL; + } + + val = qdf_mem_malloc(sizeof(*val)); + if (!val) + return QDF_STATUS_E_NOMEM; + + *val = filter_type; + + msg.type = SIR_HAL_SET_MIRACAST; + msg.reserved = 0; + msg.bodyptr = val; + + if (!QDF_IS_STATUS_SUCCESS( + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WDA_SET_MAS_ENABLE_DISABLE to WMA!", + __func__); + qdf_mem_free(val); + return QDF_STATUS_E_FAILURE; + } + + mac_ptr->sme.miracast_value = filter_type; + return QDF_STATUS_SUCCESS; +} + +/** + * sme_set_mas() - Function to set MAS value to UMAC + * @val: 1-Enable, 0-Disable + * + * This function passes down the value of MAS to the UMAC. A + * value of 1 will enable MAS and a value of 0 will disable MAS + * + * Return: Configuration message posting status, SUCCESS or Fail + * + */ +QDF_STATUS sme_set_mas(uint32_t val) +{ + struct scheduler_msg msg = {0}; + uint32_t *ptr_val; + + ptr_val = qdf_mem_malloc(sizeof(*ptr_val)); + if (!ptr_val) + return QDF_STATUS_E_NOMEM; + + *ptr_val = val; + + msg.type = SIR_HAL_SET_MAS; + msg.reserved = 0; + msg.bodyptr = ptr_val; + + if (!QDF_IS_STATUS_SUCCESS( + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WDA_SET_MAS_ENABLE_DISABLE to WMA!", + __func__); + qdf_mem_free(ptr_val); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** + * sme_roam_channel_change_req() - Channel change to new target channel + * @mac_handle: handle returned by mac_open + * @bssid: mac address of BSS + * @ch_params: target channel information + * @profile: CSR profile + * + * API to Indicate Channel change to new target channel + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_roam_channel_change_req(mac_handle_t mac_handle, + struct qdf_mac_addr bssid, + struct ch_params *ch_params, + struct csr_roam_profile *profile) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + + status = csr_roam_channel_change_req(mac, bssid, ch_params, + profile); + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_process_channel_change_resp() - + * API to Indicate Channel change response message to SAP. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_process_channel_change_resp(struct mac_context *mac, + uint16_t msg_type, void *msg_buf) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_info *roam_info; + eCsrRoamResult roamResult; + uint8_t session_id; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + + roam_info->channelChangeRespEvent = + (struct sSirChanChangeResponse *)msg_buf; + + session_id = roam_info->channelChangeRespEvent->sessionId; + + if (roam_info->channelChangeRespEvent->channelChangeStatus == + QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "sapdfs: Received success eWNI_SME_CHANNEL_CHANGE_RSP for sessionId[%d]", + session_id); + roamResult = eCSR_ROAM_RESULT_CHANNEL_CHANGE_SUCCESS; + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "sapdfs: Received failure eWNI_SME_CHANNEL_CHANGE_RSP for sessionId[%d]", + session_id); + roamResult = eCSR_ROAM_RESULT_CHANNEL_CHANGE_FAILURE; + } + + csr_roam_call_callback(mac, session_id, roam_info, 0, + eCSR_ROAM_SET_CHANNEL_RSP, roamResult); + + qdf_mem_free(roam_info); + + return status; +} + +/* + * sme_roam_start_beacon_req() - + * API to Indicate LIM to start Beacon Tx after SAP CAC Wait is completed. + * + * mac_handle - The handle returned by mac_open + * sessionId - session ID + * dfsCacWaitStatus - CAC WAIT status flag + * Return QDF_STATUS + */ +QDF_STATUS sme_roam_start_beacon_req(mac_handle_t mac_handle, + struct qdf_mac_addr bssid, + uint8_t dfsCacWaitStatus) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_roam_start_beacon_req(mac, bssid, + dfsCacWaitStatus); + sme_release_global_lock(&mac->sme); + } + return status; +} + +QDF_STATUS sme_csa_restart(struct mac_context *mac_ctx, uint8_t session_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_csa_restart(mac_ctx, session_id); + sme_release_global_lock(&mac_ctx->sme); + } + + return status; +} + +QDF_STATUS sme_roam_csa_ie_request(mac_handle_t mac_handle, + struct qdf_mac_addr bssid, + uint32_t target_chan_freq, uint8_t csaIeReqd, + struct ch_params *ch_params) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_roam_send_chan_sw_ie_request(mac, bssid, + target_chan_freq, csaIeReqd, ch_params); + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_init_thermal_info() - + * SME API to initialize the thermal mitigation parameters + * + * mac_handle + * thermalParam : thermal mitigation parameters + * Return QDF_STATUS + */ +QDF_STATUS sme_init_thermal_info(mac_handle_t mac_handle) +{ + t_thermal_mgmt *pWmaParam; + struct scheduler_msg msg = {0}; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct wlan_fwol_thermal_temp thermal_temp = {0}; + QDF_STATUS status; + + pWmaParam = qdf_mem_malloc(sizeof(t_thermal_mgmt)); + if (!pWmaParam) + return QDF_STATUS_E_NOMEM; + + status = ucfg_fwol_get_thermal_temp(mac->psoc, &thermal_temp); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + pWmaParam->thermalMgmtEnabled = thermal_temp.thermal_mitigation_enable; + pWmaParam->throttlePeriod = thermal_temp.throttle_period; + + pWmaParam->throttle_duty_cycle_tbl[0] = + thermal_temp.throttle_dutycycle_level[0]; + pWmaParam->throttle_duty_cycle_tbl[1] = + thermal_temp.throttle_dutycycle_level[1]; + pWmaParam->throttle_duty_cycle_tbl[2] = + thermal_temp.throttle_dutycycle_level[2]; + pWmaParam->throttle_duty_cycle_tbl[3] = + thermal_temp.throttle_dutycycle_level[3]; + + pWmaParam->thermalLevels[0].minTempThreshold = + thermal_temp.thermal_temp_min_level[0]; + pWmaParam->thermalLevels[0].maxTempThreshold = + thermal_temp.thermal_temp_max_level[0]; + pWmaParam->thermalLevels[1].minTempThreshold = + thermal_temp.thermal_temp_min_level[1]; + pWmaParam->thermalLevels[1].maxTempThreshold = + thermal_temp.thermal_temp_max_level[1]; + pWmaParam->thermalLevels[2].minTempThreshold = + thermal_temp.thermal_temp_min_level[2]; + pWmaParam->thermalLevels[2].maxTempThreshold = + thermal_temp.thermal_temp_max_level[2]; + pWmaParam->thermalLevels[3].minTempThreshold = + thermal_temp.thermal_temp_min_level[3]; + pWmaParam->thermalLevels[3].maxTempThreshold = + thermal_temp.thermal_temp_max_level[3]; + + if (QDF_STATUS_SUCCESS == sme_acquire_global_lock(&mac->sme)) { + msg.type = WMA_INIT_THERMAL_INFO_CMD; + msg.bodyptr = pWmaParam; + + if (!QDF_IS_STATUS_SUCCESS + (scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WMA_SET_THERMAL_INFO_CMD to WMA!", + __func__); + qdf_mem_free(pWmaParam); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + return QDF_STATUS_SUCCESS; + } + qdf_mem_free(pWmaParam); + return QDF_STATUS_E_FAILURE; +} + +/** + * sme_add_set_thermal_level_callback() - Plug in set thermal level callback + * @mac_handle: Handle returned by macOpen + * @callback: sme_set_thermal_level_callback + * + * Plug in set thermal level callback + * + * Return: none + */ +void sme_add_set_thermal_level_callback(mac_handle_t mac_handle, + sme_set_thermal_level_callback callback) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + mac->sme.set_thermal_level_cb = callback; +} + +/** + * sme_set_thermal_level() - SME API to set the thermal mitigation level + * @mac_handle: Opaque handle to the global MAC context + * @level: Thermal mitigation level + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_thermal_level(mac_handle_t mac_handle, uint8_t level) +{ + struct scheduler_msg msg = {0}; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + if (QDF_STATUS_SUCCESS == sme_acquire_global_lock(&mac->sme)) { + qdf_mem_zero(&msg, sizeof(msg)); + msg.type = WMA_SET_THERMAL_LEVEL; + msg.bodyval = level; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WMA_SET_THERMAL_LEVEL to WMA!", + __func__); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + return QDF_STATUS_SUCCESS; + } + return QDF_STATUS_E_FAILURE; +} + +/* + * sme_txpower_limit() - + * SME API to set txpower limits + * + * mac_handle + * psmetx : power limits for 2g/5g + * Return QDF_STATUS + */ +QDF_STATUS sme_txpower_limit(mac_handle_t mac_handle, + struct tx_power_limit *psmetx) +{ + QDF_STATUS status; + struct scheduler_msg message = {0}; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct tx_power_limit *tx_power_limit; + + tx_power_limit = qdf_mem_malloc(sizeof(*tx_power_limit)); + if (!tx_power_limit) + return QDF_STATUS_E_FAILURE; + + *tx_power_limit = *psmetx; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(tx_power_limit); + return status; + } + + message.type = WMA_TX_POWER_LIMIT; + message.bodyptr = tx_power_limit; + status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (QDF_IS_STATUS_ERROR(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: not able to post WMA_TX_POWER_LIMIT", + __func__); + status = QDF_STATUS_E_FAILURE; + qdf_mem_free(tx_power_limit); + } + + sme_release_global_lock(&mac->sme); + + return status; +} + +QDF_STATUS sme_update_connect_debug(mac_handle_t mac_handle, uint32_t set_value) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + mac->mlme_cfg->gen.debug_packet_log = set_value; + return status; +} + +/* + * sme_ap_disable_intra_bss_fwd() - + * SME will send message to WMA to set Intra BSS in txrx + * + * mac_handle - The handle returned by mac_open + * sessionId - session id ( vdev id) + * disablefwd - bool value that indicate disable intrabss fwd disable + * Return QDF_STATUS + */ +QDF_STATUS sme_ap_disable_intra_bss_fwd(mac_handle_t mac_handle, + uint8_t sessionId, + bool disablefwd) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + int status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct scheduler_msg message = {0}; + tpDisableIntraBssFwd pSapDisableIntraFwd = NULL; + + /* Prepare the request to send to SME. */ + pSapDisableIntraFwd = qdf_mem_malloc(sizeof(tDisableIntraBssFwd)); + if (!pSapDisableIntraFwd) { + sme_err("Memory Allocation Failure!!!"); + return QDF_STATUS_E_NOMEM; + } + + pSapDisableIntraFwd->sessionId = sessionId; + pSapDisableIntraFwd->disableintrabssfwd = disablefwd; + + status = sme_acquire_global_lock(&mac->sme); + + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(pSapDisableIntraFwd); + return QDF_STATUS_E_FAILURE; + } + /* serialize the req through MC thread */ + message.bodyptr = pSapDisableIntraFwd; + message.type = WMA_SET_SAP_INTRABSS_DIS; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (QDF_IS_STATUS_ERROR(status)) { + status = QDF_STATUS_E_FAILURE; + qdf_mem_free(pSapDisableIntraFwd); + } + sme_release_global_lock(&mac->sme); + + return status; +} + +#ifdef WLAN_FEATURE_STATS_EXT + +void sme_stats_ext_register_callback(mac_handle_t mac_handle, + stats_ext_cb callback) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) { + sme_err("Invalid mac context"); + return; + } + + mac->sme.stats_ext_cb = callback; +} + +void sme_stats_ext_deregister_callback(mac_handle_t mac_handle) +{ + sme_stats_ext_register_callback(mac_handle, NULL); +} + +void sme_stats_ext2_register_callback(mac_handle_t mac_handle, + stats_ext2_cb callback) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) { + sme_err("Invalid mac context"); + return; + } + + mac->sme.stats_ext2_cb = callback; +} + +/* + * sme_stats_ext_request() - + * Function called when HDD receives STATS EXT vendor command from userspace + * + * sessionID - vdevID for the stats ext request + * input - Stats Ext Request structure ptr + * Return QDF_STATUS + */ +QDF_STATUS sme_stats_ext_request(uint8_t session_id, tpStatsExtRequestReq input) +{ + struct scheduler_msg msg = {0}; + tpStatsExtRequest data; + size_t data_len; + + data_len = sizeof(tStatsExtRequest) + input->request_data_len; + data = qdf_mem_malloc(data_len); + if (!data) + return QDF_STATUS_E_NOMEM; + + data->vdev_id = session_id; + data->request_data_len = input->request_data_len; + if (input->request_data_len) + qdf_mem_copy(data->request_data, + input->request_data, input->request_data_len); + + msg.type = WMA_STATS_EXT_REQUEST; + msg.reserved = 0; + msg.bodyptr = data; + + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post WMA_STATS_EXT_REQUEST message to WMA", + __func__); + qdf_mem_free(data); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_stats_ext_event() - eWNI_SME_STATS_EXT_EVENT processor + * @mac: Global MAC context + * @msg: "stats ext" message + + * This callback function called when SME received eWNI_SME_STATS_EXT_EVENT + * response from WMA + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_stats_ext_event(struct mac_context *mac, + struct stats_ext_event *msg) +{ + if (!msg) { + sme_err("Null msg"); + return QDF_STATUS_E_FAILURE; + } + + if (mac->sme.stats_ext_cb) + mac->sme.stats_ext_cb(mac->hdd_handle, msg); + + return QDF_STATUS_SUCCESS; +} + +#else + +static QDF_STATUS sme_stats_ext_event(struct mac_context *mac, + struct stats_ext_event *msg) +{ + return QDF_STATUS_SUCCESS; +} + +#endif + +#ifdef FEATURE_FW_STATE +QDF_STATUS sme_get_fw_state(mac_handle_t mac_handle, + fw_state_callback callback, + void *context) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + tp_wma_handle wma_handle; + + SME_ENTER(); + + mac_ctx->sme.fw_state_cb = callback; + mac_ctx->sme.fw_state_context = context; + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + status = wma_get_fw_state(wma_handle); + + SME_EXIT(); + return status; +} + +/** + * sme_fw_state_resp() - eWNI_SME_FW_STATUS_IND processor + * @mac: Global MAC context + + * This callback function called when SME received eWNI_SME_FW_STATUS_IND + * response from WMA + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_fw_state_resp(struct mac_context *mac) +{ + if (mac->sme.fw_state_cb) + mac->sme.fw_state_cb(mac->sme.fw_state_context); + mac->sme.fw_state_cb = NULL; + mac->sme.fw_state_context = NULL; + + return QDF_STATUS_SUCCESS; +} + +#else /* FEATURE_FW_STATE */ +static QDF_STATUS sme_fw_state_resp(struct mac_context *mac) +{ + return QDF_STATUS_SUCCESS; +} + +#endif /* FEATURE_FW_STATE */ + +/* + * sme_update_dfs_scan_mode() - + * Update DFS roam scan mode + * This function is called through dynamic setConfig callback function + * to configure allowDFSChannelRoam. + * mac_handle: Opaque handle to the global MAC context + * sessionId - Session Identifier + * allowDFSChannelRoam - DFS roaming scan mode 0 (disable), + * 1 (passive), 2 (active) + * Return QDF_STATUS_SUCCESS - SME update DFS roaming scan config + * successfully. + * Other status means SME failed to update DFS roaming scan config. + */ +QDF_STATUS sme_update_dfs_scan_mode(mac_handle_t mac_handle, uint8_t sessionId, + uint8_t allowDFSChannelRoam) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (sessionId >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid sme session id: %d"), sessionId); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR runtime successfully set AllowDFSChannelRoam Mode to %d - old value is %d - roam state is %s", + allowDFSChannelRoam, + mac->mlme_cfg->lfr.roaming_dfs_channel, + mac_trace_get_neighbour_roam_state(mac->roam. + neighborRoamInfo + [sessionId]. + neighborRoamState)); + mac->mlme_cfg->lfr.roaming_dfs_channel = + allowDFSChannelRoam; + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) + csr_roam_update_cfg(mac, sessionId, + REASON_ROAM_DFS_SCAN_MODE_CHANGED); + + sme_release_global_lock(&mac->sme); + } + + + return status; +} + +/* + * sme_get_dfs_scan_mode() - get DFS roam scan mode + * This is a synchronous call + * + * mac_handle - The handle returned by mac_open. + * Return DFS roaming scan mode 0 (disable), 1 (passive), 2 (active) + */ +uint8_t sme_get_dfs_scan_mode(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->mlme_cfg->lfr.roaming_dfs_channel; +} + +/* + * sme_modify_add_ie() - + * This function sends msg to updates the additional IE buffers in PE + * + * mac_handle - global structure + * pModifyIE - pointer to tModifyIE structure + * updateType - type of buffer + * Return Success or failure + */ +QDF_STATUS sme_modify_add_ie(mac_handle_t mac_handle, + tSirModifyIE *pModifyIE, eUpdateIEsType updateType) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_roam_modify_add_ies(mac, pModifyIE, updateType); + sme_release_global_lock(&mac->sme); + } + return status; +} + +/* + * sme_update_add_ie() - + * This function sends msg to updates the additional IE buffers in PE + * + * mac_handle - global structure + * pUpdateIE - pointer to structure tUpdateIE + * updateType - type of buffer + * Return Success or failure + */ +QDF_STATUS sme_update_add_ie(mac_handle_t mac_handle, + tSirUpdateIE *pUpdateIE, eUpdateIEsType updateType) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_roam_update_add_ies(mac, pUpdateIE, updateType); + sme_release_global_lock(&mac->sme); + } + return status; +} + +/** + * sme_update_dsc_pto_up_mapping() + * @mac_handle: Opaque handle to the global MAC context + * @dscpmapping: pointer to DSCP mapping structure + * @sessionId: SME session id + * + * This routine is called to update dscp mapping + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_update_dsc_pto_up_mapping(mac_handle_t mac_handle, + enum sme_qos_wmmuptype *dscpmapping, + uint8_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t i, j, peSessionId; + struct csr_roam_session *pCsrSession = NULL; + struct pe_session *pSession = NULL; + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + pCsrSession = CSR_GET_SESSION(mac, sessionId); + if (!pCsrSession) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Session lookup fails for CSR session")); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Invalid session Id %u"), sessionId); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + pSession = pe_find_session_by_bssid(mac, + pCsrSession->connectedProfile.bssid.bytes, + &peSessionId); + + if (!pSession) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL(" Session lookup fails for BSSID")); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + if (!pSession->QosMapSet.present) { + sme_debug("QOS Mapping IE not present"); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } + + for (i = 0; i < SME_QOS_WMM_UP_MAX; i++) { + for (j = pSession->QosMapSet.dscp_range[i][0]; + j <= pSession->QosMapSet.dscp_range[i][1] && + j <= WLAN_MAX_DSCP; j++) + dscpmapping[j] = i; + } + for (i = 0; i < pSession->QosMapSet.num_dscp_exceptions; i++) + if (pSession->QosMapSet.dscp_exceptions[i][0] <= WLAN_MAX_DSCP) + dscpmapping[pSession->QosMapSet.dscp_exceptions[i][0]] = + pSession->QosMapSet.dscp_exceptions[i][1]; + + sme_release_global_lock(&mac->sme); + return status; +} + +/* + * sme_abort_roam_scan() - + * API to abort current roam scan cycle by roam scan offload module. + * + * mac_handle - The handle returned by mac_open. + * sessionId - Session Identifier + * Return QDF_STATUS + */ + +QDF_STATUS sme_abort_roam_scan(mac_handle_t mac_handle, uint8_t sessionId) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (mac->mlme_cfg->lfr.roam_scan_offload_enabled) { + /* acquire the lock for the sme object */ + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + csr_roam_offload_scan(mac, sessionId, + ROAM_SCAN_OFFLOAD_ABORT_SCAN, + REASON_ROAM_ABORT_ROAM_SCAN); + /* release the lock for the sme object */ + sme_release_global_lock(&mac->sme); + } + } + + return status; +} + +QDF_STATUS sme_get_valid_channels_by_band(mac_handle_t mac_handle, + uint8_t wifi_band, + uint32_t *valid_chan_list, + uint8_t *valid_chan_len) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t chan_freq_list[CFG_VALID_CHANNEL_LIST_LEN] = { 0 }; + uint8_t num_channels = 0; + uint8_t i = 0; + uint32_t valid_channels = CFG_VALID_CHANNEL_LIST_LEN; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + if (!valid_chan_list || !valid_chan_len) { + sme_err("Output channel list/NumChannels is NULL"); + return QDF_STATUS_E_INVAL; + } + + if (wifi_band >= WIFI_BAND_MAX) { + sme_err("Invalid wifi Band: %d", wifi_band); + return QDF_STATUS_E_INVAL; + } + + status = sme_get_cfg_valid_channels(&chan_freq_list[0], + &valid_channels); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Fail to get valid channel list (err=%d)", status); + return status; + } + + switch (wifi_band) { + case WIFI_BAND_UNSPECIFIED: + sme_debug("Unspec Band, return all %d valid channels", + valid_channels); + num_channels = valid_channels; + for (i = 0; i < valid_channels; i++) + valid_chan_list[i] = chan_freq_list[i]; + break; + + case WIFI_BAND_BG: + sme_debug("WIFI_BAND_BG (2.4 GHz)"); + for (i = 0; i < valid_channels; i++) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq_list[i])) + valid_chan_list[num_channels++] = + chan_freq_list[i]; + } + break; + + case WIFI_BAND_A: + sme_debug("WIFI_BAND_A (5 GHz without DFS)"); + for (i = 0; i < valid_channels; i++) { + if (WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq_list[i]) && + !wlan_reg_is_dfs_for_freq(mac_ctx->pdev, + chan_freq_list[i])) + valid_chan_list[num_channels++] = + chan_freq_list[i]; + } + break; + + case WIFI_BAND_ABG: + sme_debug("WIFI_BAND_ABG (2.4 GHz + 5 GHz; no DFS)"); + for (i = 0; i < valid_channels; i++) { + if ((WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq_list[i]) || + WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq_list[i])) && + !wlan_reg_is_dfs_for_freq(mac_ctx->pdev, + chan_freq_list[i])) + valid_chan_list[num_channels++] = + chan_freq_list[i]; + } + break; + + case WIFI_BAND_A_DFS_ONLY: + sme_debug("WIFI_BAND_A_DFS (5 GHz DFS only)"); + for (i = 0; i < valid_channels; i++) { + if (WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq_list[i]) && + wlan_reg_is_dfs_for_freq(mac_ctx->pdev, + chan_freq_list[i])) + valid_chan_list[num_channels++] = + chan_freq_list[i]; + } + break; + + case WIFI_BAND_A_WITH_DFS: + sme_debug("WIFI_BAND_A_WITH_DFS (5 GHz with DFS)"); + for (i = 0; i < valid_channels; i++) { + if (WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq_list[i])) + valid_chan_list[num_channels++] = + chan_freq_list[i]; + } + break; + + case WIFI_BAND_ABG_WITH_DFS: + sme_debug("WIFI_BAND_ABG_WITH_DFS (2.4 GHz+5 GHz with DFS)"); + for (i = 0; i < valid_channels; i++) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq_list[i]) || + WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq_list[i])) + valid_chan_list[num_channels++] = + chan_freq_list[i]; + } + break; + + default: + sme_err("Unknown wifi Band: %d", wifi_band); + return QDF_STATUS_E_INVAL; + } + *valid_chan_len = num_channels; + + return status; +} + +#ifdef FEATURE_WLAN_EXTSCAN + +QDF_STATUS +sme_ext_scan_get_capabilities(mac_handle_t mac_handle, + struct extscan_capabilities_params *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct extscan_capabilities_params *bodyptr; + + /* per contract must make a copy of the params when messaging */ + bodyptr = qdf_mem_malloc(sizeof(*bodyptr)); + if (!bodyptr) + return QDF_STATUS_E_NOMEM; + *bodyptr = *params; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = bodyptr; + message.type = WMA_EXTSCAN_GET_CAPABILITIES_REQ; + qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + sme_release_global_lock(&mac->sme); + } + + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failure: %d", status); + qdf_mem_free(bodyptr); + } + return status; +} + +QDF_STATUS +sme_ext_scan_start(mac_handle_t mac_handle, + struct wifi_scan_cmd_req_params *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct wifi_scan_cmd_req_params *bodyptr; + + /* per contract must make a copy of the params when messaging */ + bodyptr = qdf_mem_malloc(sizeof(*bodyptr)); + if (!bodyptr) + return QDF_STATUS_E_NOMEM; + *bodyptr = *params; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = bodyptr; + message.type = WMA_EXTSCAN_START_REQ; + qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + sme_release_global_lock(&mac->sme); + } + + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failure: %d", status); + qdf_mem_free(bodyptr); + } + return status; +} + +QDF_STATUS sme_ext_scan_stop(mac_handle_t mac_handle, + struct extscan_stop_req_params *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct extscan_stop_req_params *bodyptr; + + /* per contract must make a copy of the params when messaging */ + bodyptr = qdf_mem_malloc(sizeof(*bodyptr)); + if (!bodyptr) + return QDF_STATUS_E_NOMEM; + *bodyptr = *params; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = bodyptr; + message.type = WMA_EXTSCAN_STOP_REQ; + qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + sme_release_global_lock(&mac->sme); + } + + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failure: %d", status); + qdf_mem_free(bodyptr); + } + return status; +} + +QDF_STATUS +sme_set_bss_hotlist(mac_handle_t mac_handle, + struct extscan_bssid_hotlist_set_params *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct extscan_bssid_hotlist_set_params *bodyptr; + + /* per contract must make a copy of the params when messaging */ + bodyptr = qdf_mem_malloc(sizeof(*bodyptr)); + if (!bodyptr) + return QDF_STATUS_E_NOMEM; + + *bodyptr = *params; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = bodyptr; + message.type = WMA_EXTSCAN_SET_BSSID_HOTLIST_REQ; + qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + sme_release_global_lock(&mac->sme); + } + + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failure: %d", status); + qdf_mem_free(bodyptr); + } + return status; +} + +QDF_STATUS +sme_reset_bss_hotlist(mac_handle_t mac_handle, + struct extscan_bssid_hotlist_reset_params *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct extscan_bssid_hotlist_reset_params *bodyptr; + + /* per contract must make a copy of the params when messaging */ + bodyptr = qdf_mem_malloc(sizeof(*bodyptr)); + if (!bodyptr) + return QDF_STATUS_E_NOMEM; + + *bodyptr = *params; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = bodyptr; + message.type = WMA_EXTSCAN_RESET_BSSID_HOTLIST_REQ; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type)); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + sme_release_global_lock(&mac->sme); + } + + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failure: %d", status); + qdf_mem_free(bodyptr); + } + return status; +} + +QDF_STATUS +sme_set_significant_change(mac_handle_t mac_handle, + struct extscan_set_sig_changereq_params *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct extscan_set_sig_changereq_params *bodyptr; + + /* per contract must make a copy of the params when messaging */ + bodyptr = qdf_mem_malloc(sizeof(*bodyptr)); + if (!bodyptr) + return QDF_STATUS_E_NOMEM; + + *bodyptr = *params; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = bodyptr; + message.type = WMA_EXTSCAN_SET_SIGNF_CHANGE_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + sme_release_global_lock(&mac->sme); + } + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failure: %d", status); + qdf_mem_free(bodyptr); + } + return status; +} + +QDF_STATUS +sme_reset_significant_change(mac_handle_t mac_handle, + struct extscan_capabilities_reset_params *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct extscan_capabilities_reset_params *bodyptr; + + /* per contract must make a copy of the params when messaging */ + bodyptr = qdf_mem_malloc(sizeof(*bodyptr)); + if (!bodyptr) + return QDF_STATUS_E_NOMEM; + + *bodyptr = *params; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = bodyptr; + message.type = WMA_EXTSCAN_RESET_SIGNF_CHANGE_REQ; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type)); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + sme_release_global_lock(&mac->sme); + } + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failure: %d", status); + qdf_mem_free(bodyptr); + } + + return status; +} + +QDF_STATUS +sme_get_cached_results(mac_handle_t mac_handle, + struct extscan_cached_result_params *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct extscan_cached_result_params *bodyptr; + + /* per contract must make a copy of the params when messaging */ + bodyptr = qdf_mem_malloc(sizeof(*bodyptr)); + if (!bodyptr) + return QDF_STATUS_E_NOMEM; + *bodyptr = *params; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = bodyptr; + message.type = WMA_EXTSCAN_GET_CACHED_RESULTS_REQ; + qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + sme_release_global_lock(&mac->sme); + } + + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failure: %d", status); + qdf_mem_free(bodyptr); + } + return status; +} + +QDF_STATUS sme_set_epno_list(mac_handle_t mac_handle, + struct wifi_enhanced_pno_params *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct wifi_enhanced_pno_params *req_msg; + int len; + + SME_ENTER(); + + /* per contract must make a copy of the params when messaging */ + len = sizeof(*req_msg) + + (params->num_networks * sizeof(req_msg->networks[0])); + + req_msg = qdf_mem_malloc(len); + if (!req_msg) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy(req_msg, params, len); + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("sme_acquire_global_lock failed!(status=%d)", + status); + qdf_mem_free(req_msg); + return status; + } + + /* Serialize the req through MC thread */ + message.bodyptr = req_msg; + message.type = WMA_SET_EPNO_LIST_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", status); + qdf_mem_free(req_msg); + } + sme_release_global_lock(&mac->sme); + + return status; +} + +QDF_STATUS sme_set_passpoint_list(mac_handle_t mac_handle, + struct wifi_passpoint_req_param *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct wifi_passpoint_req_param *req_msg; + int len; + + SME_ENTER(); + + len = sizeof(*req_msg) + + (params->num_networks * sizeof(params->networks[0])); + req_msg = qdf_mem_malloc(len); + if (!req_msg) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy(req_msg, params, len); + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("sme_acquire_global_lock failed!(status=%d)", + status); + qdf_mem_free(req_msg); + return status; + } + + /* Serialize the req through MC thread */ + message.bodyptr = req_msg; + message.type = WMA_SET_PASSPOINT_LIST_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", + status); + qdf_mem_free(req_msg); + } + sme_release_global_lock(&mac->sme); + return status; +} + +QDF_STATUS sme_reset_passpoint_list(mac_handle_t mac_handle, + struct wifi_passpoint_req_param *params) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct wifi_passpoint_req_param *req_msg; + + SME_ENTER(); + + req_msg = qdf_mem_malloc(sizeof(*req_msg)); + if (!req_msg) + return QDF_STATUS_E_NOMEM; + *req_msg = *params; + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("sme_acquire_global_lock failed!(status=%d)", + status); + qdf_mem_free(req_msg); + return status; + } + + /* Serialize the req through MC thread */ + message.bodyptr = req_msg; + message.type = WMA_RESET_PASSPOINT_LIST_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", + status); + qdf_mem_free(req_msg); + } + sme_release_global_lock(&mac->sme); + return status; +} + +QDF_STATUS sme_ext_scan_register_callback(mac_handle_t mac_handle, + ext_scan_ind_cb ext_scan_ind_cb) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.ext_scan_ind_cb = ext_scan_ind_cb; + sme_release_global_lock(&mac->sme); + } + return status; +} +#endif /* FEATURE_WLAN_EXTSCAN */ + +/** + * sme_send_wisa_params(): Pass WISA mode to WMA + * @mac_handle: Opaque handle to the global MAC context + * @wisa_params: pointer to WISA params struct + * @sessionId: SME session id + * + * Pass WISA params to WMA + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_wisa_params(mac_handle_t mac_handle, + struct sir_wisa_params *wisa_params) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct sir_wisa_params *cds_msg_wisa_params; + + cds_msg_wisa_params = qdf_mem_malloc(sizeof(struct sir_wisa_params)); + if (!cds_msg_wisa_params) + return QDF_STATUS_E_NOMEM; + + *cds_msg_wisa_params = *wisa_params; + status = sme_acquire_global_lock(&mac->sme); + + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(cds_msg_wisa_params); + return QDF_STATUS_E_FAILURE; + } + message.bodyptr = cds_msg_wisa_params; + message.type = WMA_SET_WISA_PARAMS; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(cds_msg_wisa_params); + sme_release_global_lock(&mac->sme); + return status; +} + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + +/* + * sme_ll_stats_clear_req() - + * SME API to clear Link Layer Statistics + * + * mac_handle + * pclearStatsReq: Link Layer clear stats request params structure + * Return QDF_STATUS + */ +QDF_STATUS sme_ll_stats_clear_req(mac_handle_t mac_handle, + tSirLLStatsClearReq *pclearStatsReq) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tSirLLStatsClearReq *clear_stats_req; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "staId = %u", pclearStatsReq->staId); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "statsClearReqMask = 0x%X", + pclearStatsReq->statsClearReqMask); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "stopReq = %u", pclearStatsReq->stopReq); + if (!sme_is_session_id_valid(mac_handle, pclearStatsReq->staId)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: invalid staId %d", + __func__, pclearStatsReq->staId); + return QDF_STATUS_E_INVAL; + } + + clear_stats_req = qdf_mem_malloc(sizeof(*clear_stats_req)); + if (!clear_stats_req) + return QDF_STATUS_E_NOMEM; + + *clear_stats_req = *pclearStatsReq; + + if (QDF_STATUS_SUCCESS == sme_acquire_global_lock(&mac->sme)) { + /* Serialize the req through MC thread */ + message.bodyptr = clear_stats_req; + message.type = WMA_LINK_LAYER_STATS_CLEAR_REQ; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type)); + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: not able to post WMA_LL_STATS_CLEAR_REQ", + __func__); + qdf_mem_free(clear_stats_req); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: sme_acquire_global_lock error", __func__); + qdf_mem_free(clear_stats_req); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +/* + * sme_ll_stats_set_req() - + * SME API to set the Link Layer Statistics + * + * mac_handle + * psetStatsReq: Link Layer set stats request params structure + * Return QDF_STATUS + */ +QDF_STATUS sme_ll_stats_set_req(mac_handle_t mac_handle, tSirLLStatsSetReq + *psetStatsReq) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tSirLLStatsSetReq *set_stats_req; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: MPDU Size = %u", __func__, + psetStatsReq->mpduSizeThreshold); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + " Aggressive Stats Collections = %u", + psetStatsReq->aggressiveStatisticsGathering); + + set_stats_req = qdf_mem_malloc(sizeof(*set_stats_req)); + if (!set_stats_req) + return QDF_STATUS_E_NOMEM; + + *set_stats_req = *psetStatsReq; + + if (QDF_STATUS_SUCCESS == sme_acquire_global_lock(&mac->sme)) { + /* Serialize the req through MC thread */ + message.bodyptr = set_stats_req; + message.type = WMA_LINK_LAYER_STATS_SET_REQ; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type)); + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: not able to post WMA_LL_STATS_SET_REQ", + __func__); + qdf_mem_free(set_stats_req); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: sme_acquire_global_lock error", __func__); + qdf_mem_free(set_stats_req); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +QDF_STATUS sme_ll_stats_get_req(mac_handle_t mac_handle, + tSirLLStatsGetReq *get_stats_req, + void *context) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + tSirLLStatsGetReq *ll_stats_get_req; + + ll_stats_get_req = qdf_mem_malloc(sizeof(*ll_stats_get_req)); + if (!ll_stats_get_req) + return QDF_STATUS_E_NOMEM; + + *ll_stats_get_req = *get_stats_req; + + mac->sme.ll_stats_context = context; + if (sme_acquire_global_lock(&mac->sme) == QDF_STATUS_SUCCESS) { + /* Serialize the req through MC thread */ + message.bodyptr = ll_stats_get_req; + message.type = WMA_LINK_LAYER_STATS_GET_REQ; + qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Not able to post WMA_LL_STATS_GET_REQ"); + qdf_mem_free(ll_stats_get_req); + } + sme_release_global_lock(&mac->sme); + } else { + sme_err("sme_acquire_global_lock error"); + qdf_mem_free(ll_stats_get_req); + } + + return status; +} + +QDF_STATUS sme_set_link_layer_stats_ind_cb(mac_handle_t mac_handle, + link_layer_stats_cb callback) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.link_layer_stats_cb = callback; + sme_release_global_lock(&mac->sme); + } else { + sme_err("sme_acquire_global_lock error"); + } + + return status; +} + +/** + * sme_set_link_layer_ext_cb() - Register callback for link layer statistics + * @mac_handle: Mac global handle + * @ll_stats_ext_cb: HDD callback which needs to be invoked after getting + * status notification from FW + * + * Return: QDF_STATUS + */ +QDF_STATUS +sme_set_link_layer_ext_cb(mac_handle_t mac_handle, + void (*ll_stats_ext_cb)(hdd_handle_t callback_ctx, + tSirLLStatsResults *rsp)) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (status == QDF_STATUS_SUCCESS) { + mac->sme.link_layer_stats_ext_cb = ll_stats_ext_cb; + sme_release_global_lock(&mac->sme); + } else + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: sme_qcquire_global_lock error", __func__); + return status; +} + +/** + * sme_reset_link_layer_stats_ind_cb() - SME API to reset link layer stats + * indication + * @mac_handle: Opaque handle to the global MAC context + * + * This function reset's the link layer stats indication + * + * Return: QDF_STATUS Enumeration + */ + +QDF_STATUS sme_reset_link_layer_stats_ind_cb(mac_handle_t mac_handle) +{ + QDF_STATUS status; + struct mac_context *pmac; + + if (!mac_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("mac_handle is not valid")); + return QDF_STATUS_E_INVAL; + } + pmac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&pmac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + pmac->sme.link_layer_stats_cb = NULL; + sme_release_global_lock(&pmac->sme); + } else + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: sme_acquire_global_lock error", __func__); + + return status; +} + +/** + * sme_ll_stats_set_thresh - set threshold for mac counters + * @mac_handle: Opaque handle to the global MAC context + * @threshold, threshold for mac counters + * + * Return: QDF_STATUS Enumeration + */ +QDF_STATUS sme_ll_stats_set_thresh(mac_handle_t mac_handle, + struct sir_ll_ext_stats_threshold *threshold) +{ + QDF_STATUS status; + struct mac_context *mac; + struct scheduler_msg message = {0}; + struct sir_ll_ext_stats_threshold *thresh; + + if (!threshold) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("threshold is not valid")); + return QDF_STATUS_E_INVAL; + } + + if (!mac_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("mac_handle is not valid")); + return QDF_STATUS_E_INVAL; + } + mac = MAC_CONTEXT(mac_handle); + + thresh = qdf_mem_malloc(sizeof(*thresh)); + if (!thresh) + return QDF_STATUS_E_NOMEM; + + *thresh = *threshold; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = thresh; + message.type = WDA_LINK_LAYER_STATS_SET_THRESHOLD; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, message.type)); + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: not able to post WDA_LL_STATS_GET_REQ", + __func__); + qdf_mem_free(thresh); + } + sme_release_global_lock(&mac->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_acquire_global_lock error")); + qdf_mem_free(thresh); + } + return status; +} + +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +#ifdef WLAN_POWER_DEBUG +void sme_reset_power_debug_stats_cb(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac_ctx->sme.power_debug_stats_context = NULL; + mac_ctx->sme.power_stats_resp_callback = NULL; + sme_release_global_lock(&mac_ctx->sme); + } +} + +QDF_STATUS sme_power_debug_stats_req( + mac_handle_t mac_handle, + void (*callback_fn)(struct power_stats_response *response, + void *context), + void *power_stats_context) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct scheduler_msg msg = {0}; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (!callback_fn) { + sme_err("Indication callback did not registered"); + sme_release_global_lock(&mac_ctx->sme); + return QDF_STATUS_E_FAILURE; + } + + if (mac_ctx->sme.power_debug_stats_context || + mac_ctx->sme.power_stats_resp_callback) { + sme_err("Already one power stats req in progress"); + sme_release_global_lock(&mac_ctx->sme); + return QDF_STATUS_E_ALREADY; + } + mac_ctx->sme.power_debug_stats_context = power_stats_context; + mac_ctx->sme.power_stats_resp_callback = callback_fn; + msg.bodyptr = NULL; + msg.type = WMA_POWER_DEBUG_STATS_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("not able to post WDA_POWER_DEBUG_STATS_REQ"); + sme_release_global_lock(&mac_ctx->sme); + } + return status; +} +#endif + +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS +QDF_STATUS sme_beacon_debug_stats_req( + mac_handle_t mac_handle, uint32_t vdev_id, + void (*callback_fn)(struct bcn_reception_stats_rsp + *response, void *context), + void *beacon_stats_context) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint32_t *val; + struct scheduler_msg msg = {0}; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (!callback_fn) { + sme_err("Indication callback did not registered"); + sme_release_global_lock(&mac_ctx->sme); + return QDF_STATUS_E_FAILURE; + } + + if (!mac_ctx->bcn_reception_stats && + !mac_ctx->mlme_cfg->gen.enable_beacon_reception_stats) { + sme_err("Beacon Reception stats not supported"); + sme_release_global_lock(&mac_ctx->sme); + return QDF_STATUS_E_NOSUPPORT; + } + + val = qdf_mem_malloc(sizeof(*val)); + if (!val) { + sme_release_global_lock(&mac_ctx->sme); + return QDF_STATUS_E_NOMEM; + } + + *val = vdev_id; + mac_ctx->sme.beacon_stats_context = beacon_stats_context; + mac_ctx->sme.beacon_stats_resp_callback = callback_fn; + msg.bodyptr = val; + msg.type = WMA_BEACON_DEBUG_STATS_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("not able to post WMA_BEACON_DEBUG_STATS_REQ"); + qdf_mem_free(val); + } + sme_release_global_lock(&mac_ctx->sme); + } + return status; +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * sme_update_roam_key_mgmt_offload_enabled() - enable/disable key mgmt offload + * This is a synchronous call + * @mac_handle: The handle returned by mac_open. + * @session_id: Session Identifier + * @key_mgmt_offload_enabled: key mgmt enable/disable flag + * @pmkid_modes: PMKID modes of PMKSA caching and OKC + * Return: QDF_STATUS_SUCCESS - SME updated config successfully. + * Other status means SME is failed to update. + */ + +QDF_STATUS sme_update_roam_key_mgmt_offload_enabled(mac_handle_t mac_handle, + uint8_t session_id, + bool key_mgmt_offload_enabled, + struct pmkid_mode_bits *pmkid_modes) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: LFR3: key_mgmt_offload_enabled changed to %d", + __func__, key_mgmt_offload_enabled); + status = csr_roam_set_key_mgmt_offload(mac_ctx, + session_id, + key_mgmt_offload_enabled, + pmkid_modes); + } else + status = QDF_STATUS_E_INVAL; + sme_release_global_lock(&mac_ctx->sme); + } + + return status; +} +#endif + +/** + * sme_get_temperature() - SME API to get the pdev temperature + * @mac_handle: Handle to global MAC context + * @cb_context: temperature callback context + * @cb: callback function with response (temperature) + * Return: QDF_STATUS + */ +QDF_STATUS sme_get_temperature(mac_handle_t mac_handle, + void *cb_context, + void (*cb)(int temperature, + void *context)) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + if ((!cb) && + (!mac->sme.temperature_cb)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Indication Call back did not registered"); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_FAILURE; + } else if (cb) { + mac->sme.temperature_cb_context = cb_context; + mac->sme.temperature_cb = cb; + } + /* serialize the req through MC thread */ + message.bodyptr = NULL; + message.type = WMA_GET_TEMPERATURE_REQ; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Post Get Temperature msg fail")); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return status; +} + +QDF_STATUS sme_set_scanning_mac_oui(mac_handle_t mac_handle, + struct scan_mac_oui *scan_mac_oui) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct scan_mac_oui *bodyptr; + + /* per contract must make a copy of the params when messaging */ + bodyptr = qdf_mem_malloc(sizeof(*bodyptr)); + if (!bodyptr) + return QDF_STATUS_E_NOMEM; + *bodyptr = *scan_mac_oui; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + /* Serialize the req through MC thread */ + message.bodyptr = bodyptr; + message.type = WMA_SET_SCAN_MAC_OUI_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + sme_release_global_lock(&mac->sme); + } + + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failure: %d", status); + qdf_mem_free(bodyptr); + } + + return status; +} + +#ifdef DHCP_SERVER_OFFLOAD +QDF_STATUS +sme_set_dhcp_srv_offload(mac_handle_t mac_handle, + struct dhcp_offload_info_params *dhcp_srv_info) +{ + struct scheduler_msg message = {0}; + struct dhcp_offload_info_params *payload; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + payload = qdf_mem_malloc(sizeof(*payload)); + if (!payload) + return QDF_STATUS_E_NOMEM; + + *payload = *dhcp_srv_info; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == status) { + /* serialize the req through MC thread */ + message.type = WMA_SET_DHCP_SERVER_OFFLOAD_CMD; + message.bodyptr = payload; + + if (!QDF_IS_STATUS_SUCCESS + (scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s:WMA_SET_DHCP_SERVER_OFFLOAD_CMD failed", + __func__); + qdf_mem_free(payload); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: sme_acquire_global_lock error!", __func__); + qdf_mem_free(payload); + } + + return status; +} +#endif /* DHCP_SERVER_OFFLOAD */ + +QDF_STATUS sme_send_unit_test_cmd(uint32_t vdev_id, uint32_t module_id, + uint32_t arg_count, uint32_t *arg) +{ + return wma_form_unit_test_cmd_and_send(vdev_id, module_id, + arg_count, arg); +} + +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING +/* + * sme_set_led_flashing() - + * API to set the Led flashing parameters. + * + * mac_handle - The handle returned by mac_open. + * x0, x1 - led flashing parameters + * Return QDF_STATUS + */ +QDF_STATUS sme_set_led_flashing(mac_handle_t mac_handle, uint8_t type, + uint32_t x0, uint32_t x1) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct flashing_req_params *ledflashing; + + ledflashing = qdf_mem_malloc(sizeof(*ledflashing)); + if (!ledflashing) + return QDF_STATUS_E_NOMEM; + + ledflashing->req_id = 0; + ledflashing->pattern_id = type; + ledflashing->led_x0 = x0; + ledflashing->led_x1 = x1; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Serialize the req through MC thread */ + message.bodyptr = ledflashing; + message.type = WMA_LED_FLASHING_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + sme_release_global_lock(&mac->sme); + } + if (!QDF_IS_STATUS_SUCCESS(status)) + qdf_mem_free(ledflashing); + + return status; +} +#endif + +/** + * sme_enable_dfS_chan_scan() - set DFS channel scan enable/disable + * @mac_handle: corestack handler + * @dfs_flag: flag indicating dfs channel enable/disable + * Return: QDF_STATUS + */ +QDF_STATUS sme_enable_dfs_chan_scan(mac_handle_t mac_handle, uint8_t dfs_flag) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac; + + if (!mac_handle) { + sme_err("mac_handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + mac = MAC_CONTEXT(mac_handle); + + mac->scan.fEnableDFSChnlScan = dfs_flag; + + return status; +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * sme_validate_sap_channel_switch() - validate target channel switch w.r.t + * concurreny rules set to avoid channel interference. + * @mac_handle: Opaque handle to the global MAC context + * @sap_ch - channel to switch + * @sap_phy_mode - phy mode of SAP + * @cc_switch_mode - concurreny switch mode + * @session_id - sme session id. + * + * Return: true if there is no channel interference else return false + */ +bool sme_validate_sap_channel_switch(mac_handle_t mac_handle, + uint32_t sap_ch_freq, + eCsrPhyMode sap_phy_mode, + uint8_t cc_switch_mode, + uint8_t session_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session = CSR_GET_SESSION(mac, session_id); + uint16_t intf_channel_freq = 0; + + if (!session) + return false; + + session->ch_switch_in_progress = true; + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + intf_channel_freq = csr_check_concurrent_channel_overlap( + mac, sap_ch_freq, sap_phy_mode, cc_switch_mode); + sme_release_global_lock(&mac->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_acquire_global_lock error!")); + session->ch_switch_in_progress = false; + return false; + } + + session->ch_switch_in_progress = false; + return (intf_channel_freq == 0) ? true : false; +} +#endif + +/** + * sme_configure_stats_avg_factor() - function to config avg. stats factor + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session ID + * @stats_avg_factor: average stats factor + * + * This function configures the stats avg factor in firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_configure_stats_avg_factor(mac_handle_t mac_handle, + uint8_t session_id, + uint16_t stats_avg_factor) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct sir_stats_avg_factor *stats_factor; + + stats_factor = qdf_mem_malloc(sizeof(*stats_factor)); + if (!stats_factor) + return QDF_STATUS_E_NOMEM; + + status = sme_acquire_global_lock(&mac->sme); + + if (QDF_STATUS_SUCCESS == status) { + + stats_factor->vdev_id = session_id; + stats_factor->stats_avg_factor = stats_avg_factor; + + /* serialize the req through MC thread */ + msg.type = SIR_HAL_CONFIG_STATS_FACTOR; + msg.bodyptr = stats_factor; + + if (!QDF_IS_STATUS_SUCCESS( + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post SIR_HAL_CONFIG_STATS_FACTOR to WMA!", + __func__); + qdf_mem_free(stats_factor); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: sme_acquire_global_lock error!", + __func__); + qdf_mem_free(stats_factor); + } + + return status; +} + +/** + * sme_configure_guard_time() - function to configure guard time + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * @guard_time: guard time + * + * This function configures the guard time in firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_configure_guard_time(mac_handle_t mac_handle, uint8_t session_id, + uint32_t guard_time) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct sir_guard_time_request *g_time; + + g_time = qdf_mem_malloc(sizeof(*g_time)); + if (!g_time) + return QDF_STATUS_E_NOMEM; + + status = sme_acquire_global_lock(&mac->sme); + + if (QDF_STATUS_SUCCESS == status) { + + g_time->vdev_id = session_id; + g_time->guard_time = guard_time; + + /* serialize the req through MC thread */ + msg.type = SIR_HAL_CONFIG_GUARD_TIME; + msg.bodyptr = g_time; + + if (!QDF_IS_STATUS_SUCCESS( + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post SIR_HAL_CONFIG_GUARD_TIME to WMA!", + __func__); + qdf_mem_free(g_time); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: sme_acquire_global_lock error!", + __func__); + qdf_mem_free(g_time); + } + + return status; +} + +/* + * sme_wifi_start_logger() - Send the start/stop logging command to WMA + * to either start/stop logging + * @mac_handle: Opaque handle to the global MAC context + * @start_log: Structure containing the wifi start logger params + * + * This function sends the start/stop logging command to WMA + * + * Return: QDF_STATUS_SUCCESS on successful posting + */ +QDF_STATUS sme_wifi_start_logger(mac_handle_t mac_handle, + struct sir_wifi_start_log start_log) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg message = {0}; + struct sir_wifi_start_log *req_msg; + uint32_t len; + + len = sizeof(*req_msg); + req_msg = qdf_mem_malloc(len); + if (!req_msg) + return QDF_STATUS_E_NOMEM; + + req_msg->verbose_level = start_log.verbose_level; + req_msg->is_iwpriv_command = start_log.is_iwpriv_command; + req_msg->ring_id = start_log.ring_id; + req_msg->ini_triggered = start_log.ini_triggered; + req_msg->user_triggered = start_log.user_triggered; + req_msg->size = start_log.size; + req_msg->is_pktlog_buff_clear = start_log.is_pktlog_buff_clear; + + message.bodyptr = req_msg; + message.type = SIR_HAL_START_STOP_LOGGING; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", status); + qdf_mem_free(req_msg); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +/** + * sme_neighbor_middle_of_roaming() - Function to know if + * STA is in the middle of roaming states + * @mac_handle: Handle returned by macOpen + * @sessionId: sessionId of the STA session + * + * This function is a wrapper to call + * csr_neighbor_middle_of_roaming to know STA is in the + * middle of roaming states + * + * Return: True or False + * + */ +bool sme_neighbor_middle_of_roaming(mac_handle_t mac_handle, uint8_t sessionId) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + bool val = false; + + if (CSR_IS_SESSION_VALID(mac_ctx, sessionId)) + val = csr_neighbor_middle_of_roaming(mac_ctx, sessionId); + else + sme_debug("Invalid Session: %d", sessionId); + + return val; +} + +bool sme_is_any_session_in_middle_of_roaming(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t session_id; + + for (session_id = 0; session_id < WLAN_MAX_VDEVS; session_id++) { + if (CSR_IS_SESSION_VALID(mac_ctx, session_id) && + csr_neighbor_middle_of_roaming(mac_ctx, session_id)) + return true; + } + + return false; +} + +/* + * sme_send_flush_logs_cmd_to_fw() - Flush FW logs + * + * This function is used to send the command that will + * be used to flush the logs in the firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_send_flush_logs_cmd_to_fw(void) +{ + QDF_STATUS status; + struct scheduler_msg message = {0}; + + /* Serialize the req through MC thread */ + message.bodyptr = NULL; + message.type = SIR_HAL_FLUSH_LOG_TO_FW; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", status); + status = QDF_STATUS_E_FAILURE; + } + return status; +} + +QDF_STATUS sme_enable_uapsd_for_ac(sme_ac_enum_type ac, uint8_t tid, + uint8_t pri, uint32_t srvc_int, + uint32_t sus_int, + enum sme_qos_wmm_dir_type dir, + uint8_t psb, uint32_t sessionId, + uint32_t delay_interval) +{ + void *wma_handle; + t_wma_trigger_uapsd_params uapsd_params; + enum uapsd_ac access_category; + + if (!psb) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "No need to configure auto trigger:psb is 0"); + return QDF_STATUS_SUCCESS; + } + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma_handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + switch (ac) { + case SME_AC_BK: + access_category = UAPSD_BK; + break; + case SME_AC_BE: + access_category = UAPSD_BE; + break; + case SME_AC_VI: + access_category = UAPSD_VI; + break; + case SME_AC_VO: + access_category = UAPSD_VO; + break; + default: + return QDF_STATUS_E_FAILURE; + } + + uapsd_params.wmm_ac = access_category; + uapsd_params.user_priority = pri; + uapsd_params.service_interval = srvc_int; + uapsd_params.delay_interval = delay_interval; + uapsd_params.suspend_interval = sus_int; + + if (QDF_STATUS_SUCCESS != + wma_trigger_uapsd_params(wma_handle, sessionId, &uapsd_params)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Failed to Trigger Uapsd params for sessionId %d", + sessionId); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_disable_uapsd_for_ac(sme_ac_enum_type ac, uint32_t sessionId) +{ + void *wma_handle; + enum uapsd_ac access_category; + + switch (ac) { + case SME_AC_BK: + access_category = UAPSD_BK; + break; + case SME_AC_BE: + access_category = UAPSD_BE; + break; + case SME_AC_VI: + access_category = UAPSD_VI; + break; + case SME_AC_VO: + access_category = UAPSD_VO; + break; + default: + return QDF_STATUS_E_FAILURE; + } + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + if (QDF_STATUS_SUCCESS != + wma_disable_uapsd_per_ac(wma_handle, sessionId, access_category)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Failed to disable uapsd for ac %d for sessionId %d", + ac, sessionId); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** + * sme_update_nss() - SME API to change the number for spatial streams + * (1 or 2) + * @mac_handle: Handle returned by mac open + * @nss: Number of spatial streams + * + * This function is used to update the number of spatial streams supported. + * + * Return: Success upon successfully changing nss else failure + * + */ +QDF_STATUS sme_update_nss(mac_handle_t mac_handle, uint8_t nss) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint32_t i; + struct mlme_ht_capabilities_info *ht_cap_info; + struct csr_roam_session *csr_session; + struct mlme_vht_capabilities_info *vht_cap_info; + + vht_cap_info = &mac_ctx->mlme_cfg->vht_caps.vht_cap_info; + + status = sme_acquire_global_lock(&mac_ctx->sme); + + if (QDF_STATUS_SUCCESS == status) { + vht_cap_info->enable2x2 = (nss == 1) ? 0 : 1; + + /* get the HT capability info*/ + ht_cap_info = &mac_ctx->mlme_cfg->ht_caps.ht_cap_info; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (CSR_IS_SESSION_VALID(mac_ctx, i)) { + csr_session = &mac_ctx->roam.roamSession[i]; + csr_session->ht_config.ht_tx_stbc = + ht_cap_info->tx_stbc; + } + } + + sme_release_global_lock(&mac_ctx->sme); + } + return status; +} + +/** + * sme_update_user_configured_nss() - sets the nss based on user request + * @mac_handle: Opaque handle to the global MAC context + * @nss: number of streams + * + * Return: None + */ +void sme_update_user_configured_nss(mac_handle_t mac_handle, uint8_t nss) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + mac_ctx->user_configured_nss = nss; +} + +int sme_update_tx_bfee_supp(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + mac_ctx->mlme_cfg->vht_caps.vht_cap_info.su_bformee = cfg_val; + + return sme_update_he_tx_bfee_supp(mac_handle, session_id, cfg_val); +} + +int sme_update_tx_bfee_nsts(mac_handle_t mac_handle, uint8_t session_id, + uint8_t usr_cfg_val, uint8_t nsts_val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t nsts_set_val; + struct mlme_vht_capabilities_info *vht_cap_info; + + vht_cap_info = &mac_ctx->mlme_cfg->vht_caps.vht_cap_info; + mac_ctx->usr_cfg_tx_bfee_nsts = usr_cfg_val; + if (usr_cfg_val) + nsts_set_val = usr_cfg_val; + else + nsts_set_val = nsts_val; + + vht_cap_info->tx_bfee_ant_supp = nsts_set_val; + + if (usr_cfg_val) + sme_set_he_tx_bf_cbf_rates(session_id); + + return sme_update_he_tx_bfee_nsts(mac_handle, session_id, nsts_set_val); +} +#ifdef WLAN_FEATURE_11AX +void sme_update_tgt_he_cap(mac_handle_t mac_handle, + struct wma_tgt_cfg *cfg, + tDot11fIEhe_cap *he_cap_ini) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + qdf_mem_copy(&mac_ctx->he_cap_2g, + &cfg->he_cap_2g, + sizeof(tDot11fIEhe_cap)); + + qdf_mem_copy(&mac_ctx->he_cap_5g, + &cfg->he_cap_5g, + sizeof(tDot11fIEhe_cap)); + + /* modify HE Caps field according to INI setting */ + mac_ctx->he_cap_2g.bfee_sts_lt_80 = + QDF_MIN(cfg->he_cap_2g.bfee_sts_lt_80, + he_cap_ini->bfee_sts_lt_80); + + mac_ctx->he_cap_5g.bfee_sts_lt_80 = + QDF_MIN(cfg->he_cap_5g.bfee_sts_lt_80, + he_cap_ini->bfee_sts_lt_80); +} + +void sme_update_he_cap_nss(mac_handle_t mac_handle, uint8_t session_id, + uint8_t nss) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *csr_session; + uint32_t tx_mcs_map = 0; + uint32_t rx_mcs_map = 0; + uint32_t mcs_map = 0; + + if (!nss || (nss > 2)) { + sme_err("invalid Nss value %d", nss); + return; + } + csr_session = CSR_GET_SESSION(mac_ctx, session_id); + if (!csr_session) { + sme_err("No session for id %d", session_id); + return; + } + rx_mcs_map = + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.rx_he_mcs_map_lt_80; + tx_mcs_map = + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.tx_he_mcs_map_lt_80; + mcs_map = rx_mcs_map & 0x3; + + if (nss == 1) { + tx_mcs_map = HE_SET_MCS_4_NSS(tx_mcs_map, HE_MCS_DISABLE, 2); + rx_mcs_map = HE_SET_MCS_4_NSS(rx_mcs_map, HE_MCS_DISABLE, 2); + } else { + tx_mcs_map = HE_SET_MCS_4_NSS(tx_mcs_map, mcs_map, 2); + rx_mcs_map = HE_SET_MCS_4_NSS(rx_mcs_map, mcs_map, 2); + } + sme_info("new HE Nss MCS MAP: Rx 0x%0X, Tx: 0x%0X", + rx_mcs_map, tx_mcs_map); + if (cfg_in_range(CFG_HE_RX_MCS_MAP_LT_80, rx_mcs_map)) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.rx_he_mcs_map_lt_80 = + rx_mcs_map; + if (cfg_in_range(CFG_HE_TX_MCS_MAP_LT_80, tx_mcs_map)) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.tx_he_mcs_map_lt_80 = + tx_mcs_map; + csr_update_session_he_cap(mac_ctx, csr_session); + +} + +int sme_update_he_mcs(mac_handle_t mac_handle, uint8_t session_id, + uint16_t he_mcs) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *csr_session; + uint16_t mcs_val = 0; + uint16_t mcs_map = HE_MCS_ALL_DISABLED; + + csr_session = CSR_GET_SESSION(mac_ctx, session_id); + if (!csr_session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + if ((he_mcs & 0x3) == HE_MCS_DISABLE) { + sme_err("Invalid HE MCS 0x%0x, can't disable 0-7 for 1ss", + he_mcs); + return -EINVAL; + } + mcs_val = he_mcs & 0x3; + switch (he_mcs) { + case HE_80_MCS0_7: + case HE_80_MCS0_9: + case HE_80_MCS0_11: + if (mac_ctx->mlme_cfg->vht_caps.vht_cap_info.enable2x2) { + mcs_map = HE_SET_MCS_4_NSS(mcs_map, mcs_val, 1); + mcs_map = HE_SET_MCS_4_NSS(mcs_map, mcs_val, 2); + } else { + mcs_map = HE_SET_MCS_4_NSS(mcs_map, mcs_val, 1); + } + if (cfg_in_range(CFG_HE_TX_MCS_MAP_LT_80, mcs_map)) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + tx_he_mcs_map_lt_80 = mcs_map; + if (cfg_in_range(CFG_HE_RX_MCS_MAP_LT_80, mcs_map)) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + rx_he_mcs_map_lt_80 = mcs_map; + break; + + case HE_160_MCS0_7: + case HE_160_MCS0_9: + case HE_160_MCS0_11: + mcs_map = HE_SET_MCS_4_NSS(mcs_map, mcs_val, 1); + if (cfg_in_range(CFG_HE_TX_MCS_MAP_160, mcs_map)) + qdf_mem_copy(mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + tx_he_mcs_map_160, &mcs_map, + sizeof(uint16_t)); + if (cfg_in_range(CFG_HE_RX_MCS_MAP_160, mcs_map)) + qdf_mem_copy(mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + rx_he_mcs_map_160, &mcs_map, + sizeof(uint16_t)); + break; + + case HE_80p80_MCS0_7: + case HE_80p80_MCS0_9: + case HE_80p80_MCS0_11: + mcs_map = HE_SET_MCS_4_NSS(mcs_map, mcs_val, 1); + if (cfg_in_range(CFG_HE_TX_MCS_MAP_80_80, mcs_map)) + qdf_mem_copy(mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + tx_he_mcs_map_80_80, &mcs_map, + sizeof(uint16_t)); + if (cfg_in_range(CFG_HE_RX_MCS_MAP_80_80, mcs_map)) + qdf_mem_copy(mac_ctx->mlme_cfg->he_caps.dot11_he_cap. + rx_he_mcs_map_80_80, &mcs_map, + sizeof(uint16_t)); + break; + + default: + sme_err("Invalid HE MCS 0x%0x", he_mcs); + return -EINVAL; + } + sme_info("new HE MCS 0x%0x", mcs_map); + csr_update_session_he_cap(mac_ctx, csr_session); + + return 0; +} + +void sme_set_usr_cfg_mu_edca(mac_handle_t mac_handle, bool val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + mac_ctx->usr_cfg_mu_edca_params = val; +} + +int sme_update_mu_edca_params(mac_handle_t mac_handle, uint8_t session_id) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + qdf_mem_zero(&msg, sizeof(msg)); + msg.type = WNI_SME_UPDATE_MU_EDCA_PARAMS; + msg.reserved = 0; + msg.bodyval = session_id; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (status != QDF_STATUS_SUCCESS) { + sme_err("Not able to post update edca profile"); + return -EIO; + } + + return 0; +} + +void sme_set_he_mu_edca_def_cfg(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t i; + + sme_debug("Set MU EDCA params to default"); + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + mac_ctx->usr_mu_edca_params[i].aci.aifsn = MU_EDCA_DEF_AIFSN; + mac_ctx->usr_mu_edca_params[i].aci.aci = i; + mac_ctx->usr_mu_edca_params[i].cw.max = MU_EDCA_DEF_CW_MAX; + mac_ctx->usr_mu_edca_params[i].cw.min = MU_EDCA_DEF_CW_MIN; + mac_ctx->usr_mu_edca_params[i].mu_edca_timer = + MU_EDCA_DEF_TIMER; + } +} + +int sme_update_he_tx_bfee_supp(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + if (cfg_val <= 1) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.su_beamformee = cfg_val; + else + return -EINVAL; + + csr_update_session_he_cap(mac_ctx, session); + return 0; +} + +int sme_update_he_trigger_frm_mac_pad(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t cfg_val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + if (cfg_in_range(CFG_HE_TRIG_PAD, cfg_val)) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.trigger_frm_mac_pad = + cfg_val; + else + return -EINVAL; + + csr_update_session_he_cap(mac_ctx, session); + return 0; + +} + +int sme_update_he_om_ctrl_supp(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val) +{ + + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.omi_a_ctrl = cfg_val; + + csr_update_session_he_cap(mac_ctx, session); + return 0; +} + +int sme_update_he_htc_he_supp(mac_handle_t mac_handle, uint8_t session_id, + bool cfg_val) +{ + + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.htc_he = cfg_val; + csr_update_session_he_cap(mac_ctx, session); + + return 0; +} + +static QDF_STATUS +sme_validate_session_for_cap_update(struct mac_context *mac_ctx, + uint8_t session_id, + struct csr_roam_session *session) +{ + if (!session) { + sme_err("Session does not exist, Session_id: %d", session_id); + return QDF_STATUS_E_INVAL; + } + if (!csr_is_conn_state_connected_infra(mac_ctx, session_id)) { + sme_info("STA is not connected, Session_id: %d", session_id); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +int sme_send_he_om_ctrl_update(mac_handle_t mac_handle, uint8_t session_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct omi_ctrl_tx omi_data = {0}; + void *wma_handle; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + uint32_t param_val = 0; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + sme_err("wma handle is NULL"); + return -EIO; + } + + status = sme_validate_session_for_cap_update(mac_ctx, session_id, + session); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + omi_data.a_ctrl_id = A_CTRL_ID_OMI; + + if (mac_ctx->he_om_ctrl_cfg_nss_set) + omi_data.rx_nss = mac_ctx->he_om_ctrl_cfg_nss; + else + omi_data.rx_nss = session->nss - 1; + + if (mac_ctx->he_om_ctrl_cfg_tx_nsts_set) + omi_data.tx_nsts = mac_ctx->he_om_ctrl_cfg_tx_nsts; + else + omi_data.tx_nsts = session->nss - 1; + + if (mac_ctx->he_om_ctrl_cfg_bw_set) + omi_data.ch_bw = mac_ctx->he_om_ctrl_cfg_bw; + else + omi_data.ch_bw = session->connectedProfile.vht_channel_width; + + omi_data.ul_mu_dis = mac_ctx->he_om_ctrl_cfg_ul_mu_dis; + omi_data.ul_mu_data_dis = mac_ctx->he_om_ctrl_ul_mu_data_dis; + omi_data.omi_in_vht = 0x1; + omi_data.omi_in_he = 0x1; + + sme_info("OMI: BW %d TxNSTS %d RxNSS %d ULMU %d, OMI_VHT %d, OMI_HE %d", + omi_data.ch_bw, omi_data.tx_nsts, omi_data.rx_nss, + omi_data.ul_mu_dis, omi_data.omi_in_vht, omi_data.omi_in_he); + qdf_mem_copy(¶m_val, &omi_data, sizeof(omi_data)); + sme_debug("param val %08X, bssid:"QDF_MAC_ADDR_FMT, param_val, + QDF_MAC_ADDR_REF(session->connectedProfile.bssid.bytes)); + status = wma_set_peer_param(wma_handle, + session->connectedProfile.bssid.bytes, + WMI_PEER_PARAM_XMIT_OMI, + param_val, session_id); + if (QDF_STATUS_SUCCESS != status) { + sme_err("set_peer_param_cmd returned %d", status); + return -EIO; + } + + return 0; +} + +int sme_set_he_om_ctrl_param(mac_handle_t mac_handle, uint8_t session_id, + enum qca_wlan_vendor_attr_he_omi_tx param, + uint8_t cfg_val) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + status = sme_validate_session_for_cap_update(mac_ctx, session_id, + session); + if (QDF_IS_STATUS_ERROR(status)) + return -EINVAL; + + switch(param) { + case QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE: + sme_debug("Set OM ctrl UL MU dis to %d", cfg_val); + mac_ctx->he_om_ctrl_cfg_ul_mu_dis = cfg_val; + break; + case QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS: + if ((cfg_val + 1) > session->nss) { + sme_debug("OMI Nss %d is > connected Nss %d", + cfg_val, session->nss); + mac_ctx->he_om_ctrl_cfg_nss_set = false; + return 0; + } + sme_debug("Set OM ctrl Rx Nss cfg to %d", cfg_val); + mac_ctx->he_om_ctrl_cfg_nss_set = true; + mac_ctx->he_om_ctrl_cfg_nss = cfg_val; + break; + case QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW: + if (cfg_val > + session->connectedProfile.vht_channel_width) { + sme_info("OMI BW %d is > connected BW %d", + cfg_val, + session->connectedProfile. + vht_channel_width); + mac_ctx->he_om_ctrl_cfg_bw_set = false; + return 0; + } + sme_debug("Set OM ctrl BW cfg to %d", cfg_val); + mac_ctx->he_om_ctrl_cfg_bw_set = true; + mac_ctx->he_om_ctrl_cfg_bw = cfg_val; + break; + case QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: + if ((cfg_val + 1) > session->nss) { + sme_debug("OMI NSTS %d is > connected Nss %d", + cfg_val, session->nss); + mac_ctx->he_om_ctrl_cfg_tx_nsts_set = false; + return 0; + } + sme_debug("Set OM ctrl tx nsts cfg to %d", cfg_val); + mac_ctx->he_om_ctrl_cfg_tx_nsts_set = true; + mac_ctx->he_om_ctrl_cfg_tx_nsts = cfg_val; + break; + case QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: + sme_debug("Set OM ctrl UL MU data dis to %d", cfg_val); + mac_ctx->he_om_ctrl_ul_mu_data_dis = cfg_val; + break; + default: + sme_debug("Invalid OMI param %d", param); + return -EINVAL; + } + + return 0; +} + +void sme_reset_he_om_ctrl(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + mac_ctx->he_om_ctrl_cfg_bw_set = false; + mac_ctx->he_om_ctrl_cfg_nss_set = false; + mac_ctx->he_om_ctrl_cfg_bw = 0; + mac_ctx->he_om_ctrl_cfg_nss = 0; + mac_ctx->he_om_ctrl_cfg_ul_mu_dis = false; + mac_ctx->he_om_ctrl_cfg_tx_nsts_set = false; + mac_ctx->he_om_ctrl_cfg_tx_nsts = 0; + mac_ctx->he_om_ctrl_ul_mu_data_dis = false; +} + +int sme_config_action_tx_in_tb_ppdu(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct scheduler_msg msg = {0}; + struct sir_cfg_action_frm_tb_ppdu *cfg_msg; + + if (!csr_is_conn_state_connected_infra(mac_ctx, session_id)) { + sme_info("STA not in connected state Session_id: %d", + session_id); + return -EINVAL; + } + + cfg_msg = qdf_mem_malloc(sizeof(*cfg_msg)); + + if (!cfg_msg) + return -EIO; + + cfg_msg->type = WNI_SME_CFG_ACTION_FRM_HE_TB_PPDU; + cfg_msg->vdev_id = session_id; + cfg_msg->cfg = cfg_val; + + msg.bodyptr = cfg_msg; + msg.type = WNI_SME_CFG_ACTION_FRM_HE_TB_PPDU; + status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_STATUS_SUCCESS != status) { + sme_err("Failed to send CFG_ACTION_FRAME_IN_TB_PPDU to PE %d", + status); + qdf_mem_free(cfg_msg); + return -EIO; + } + + return 0; +} + +int sme_update_he_tx_bfee_nsts(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + if (cfg_in_range(CFG_HE_BFEE_STS_LT80, cfg_val)) { + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.bfee_sts_lt_80 = + cfg_val; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.bfee_sts_gt_80 = + cfg_val; + } else { + return -EINVAL; + } + + + csr_update_session_he_cap(mac_ctx, session); + return 0; +} + +void sme_set_he_tx_bf_cbf_rates(uint8_t session_id) +{ + uint32_t tx_bf_cbf_rates_5g[] = {91, 1, 0, 3, 2, 4, 0}; + uint32_t tx_bf_cbf_rates_2g[] = {91, 1, 1, 3, 1, 3, 0}; + QDF_STATUS status; + + status = wma_form_unit_test_cmd_and_send(session_id, 0x48, 7, + tx_bf_cbf_rates_5g); + if (QDF_STATUS_SUCCESS != status) + sme_err("send_unit_test_cmd returned %d", status); + + status = wma_form_unit_test_cmd_and_send(session_id, 0x48, 7, + tx_bf_cbf_rates_2g); + if (QDF_STATUS_SUCCESS != status) + sme_err("send_unit_test_cmd returned %d", status); +} + +void sme_config_su_ppdu_queue(uint8_t session_id, bool enable) +{ + uint32_t su_ppdu_enable[] = {69, 1, 1, 1}; + uint32_t su_ppdu_disable[] = {69, 1, 1, 0}; + QDF_STATUS status; + + if (enable) { + sme_debug("Send Tx SU PPDU queue ENABLE cmd to FW"); + status = wma_form_unit_test_cmd_and_send(session_id, 0x48, 4, + su_ppdu_enable); + } else { + sme_debug("Send Tx SU PPDU queue DISABLE cmd to FW"); + status = wma_form_unit_test_cmd_and_send(session_id, 0x48, 4, + su_ppdu_disable); + } + if (QDF_STATUS_SUCCESS != status) + sme_err("send_unit_test_cmd returned %d", status); +} + +int sme_update_he_tx_stbc_cap(mac_handle_t mac_handle, uint8_t session_id, + int value) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + uint32_t he_cap_val = 0; + + he_cap_val = value ? 1 : 0; + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + if (he_cap_val <= 1) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.tb_ppdu_tx_stbc_lt_80mhz + = he_cap_val; + else + return -EINVAL; + if (he_cap_val <= 1) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.tb_ppdu_tx_stbc_gt_80mhz + = he_cap_val; + else + return -EINVAL; + csr_update_session_he_cap(mac_ctx, session); + return 0; +} + +int sme_update_he_rx_stbc_cap(mac_handle_t mac_handle, uint8_t session_id, + int value) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + uint32_t he_cap_val = 0; + + he_cap_val = value ? 1 : 0; + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + if (he_cap_val <= 1) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.rx_stbc_lt_80mhz = + he_cap_val; + else + return -EINVAL; + if (he_cap_val <= 1) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.rx_stbc_gt_80mhz = + he_cap_val; + else + return -EINVAL; + csr_update_session_he_cap(mac_ctx, session); + return 0; +} + +int sme_update_he_frag_supp(mac_handle_t mac_handle, uint8_t session_id, + uint16_t he_frag) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + if (cfg_in_range(CFG_HE_FRAGMENTATION, he_frag)) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.fragmentation = he_frag; + else + return -EINVAL; + + csr_update_session_he_cap(mac_ctx, session); + return 0; + +} + +int sme_update_he_ldpc_supp(mac_handle_t mac_handle, uint8_t session_id, + uint16_t he_ldpc) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + if (he_ldpc <= 1) + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.ldpc_coding = he_ldpc; + else + return -EINVAL; + + csr_update_session_he_cap(mac_ctx, session); + return 0; + +} + +int sme_update_he_twt_req_support(mac_handle_t mac_handle, uint8_t session_id, + uint8_t cfg_val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("No session for id %d", session_id); + return -EINVAL; + } + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.twt_request = cfg_val; + + csr_update_session_he_cap(mac_ctx, session); + + return 0; +} +#endif + +QDF_STATUS +sme_update_session_txq_edca_params(mac_handle_t mac_handle, + uint8_t session_id, + tSirMacEdcaParamRecord *txq_edca_params) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct sir_update_session_txq_edca_param *msg; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_ERROR(status)) + return QDF_STATUS_E_AGAIN; + + msg = qdf_mem_malloc(sizeof(*msg)); + if (!msg) + return QDF_STATUS_E_NOMEM; + + msg->message_type = eWNI_SME_UPDATE_SESSION_EDCA_TXQ_PARAMS; + msg->vdev_id = session_id; + qdf_mem_copy(&msg->txq_edca_params, txq_edca_params, + sizeof(tSirMacEdcaParamRecord)); + msg->length = sizeof(*msg); + + status = umac_send_mb_message_to_mac(msg); + + sme_release_global_lock(&mac_ctx->sme); + if (status != QDF_STATUS_SUCCESS) + return QDF_STATUS_E_IO; + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_set_nud_debug_stats_cb() - set nud debug stats callback + * @mac_handle: Opaque handle to the global MAC context + * @cb: callback function pointer + * @context: callback context + * + * This function stores nud debug stats callback function. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_set_nud_debug_stats_cb(mac_handle_t mac_handle, + void (*cb)(void *, struct rsp_stats *, void *), + void *context) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac; + + if (!mac_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("mac_handle is not valid")); + return QDF_STATUS_E_INVAL; + } + mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_acquire_global_lock failed!(status=%d)"), + status); + return status; + } + + mac->sme.get_arp_stats_cb = cb; + mac->sme.get_arp_stats_context = context; + sme_release_global_lock(&mac->sme); + return status; +} + +/** + * sme_is_any_session_in_connected_state() - SME wrapper API to + * check if any session is in connected state or not. + * + * @mac_handle: Handle returned by mac open + * + * This function is used to check if any valid sme session is in + * connected state or not. + * + * Return: true if any session is connected, else false. + * + */ +bool sme_is_any_session_in_connected_state(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + bool ret = false; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_STATUS_SUCCESS == status) { + ret = csr_is_any_session_in_connect_state(mac_ctx); + sme_release_global_lock(&mac_ctx->sme); + } + return ret; +} + +QDF_STATUS sme_set_chip_pwr_save_fail_cb(mac_handle_t mac_handle, + pwr_save_fail_cb cb) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (status != QDF_STATUS_SUCCESS) { + sme_err("sme_AcquireGlobalLock failed!(status=%d)", status); + return status; + } + mac->sme.chip_power_save_fail_cb = cb; + sme_release_global_lock(&mac->sme); + return status; +} + +#ifdef FEATURE_RSSI_MONITOR +/** + * sme_set_rssi_monitoring() - set rssi monitoring + * @mac_handle: Opaque handle to the global MAC context + * @input: request message + * + * This function constructs the vos message and fill in message type, + * bodyptr with @input and posts it to WDA queue. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_set_rssi_monitoring(mac_handle_t mac_handle, + struct rssi_monitor_param *input) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct rssi_monitor_param *req_msg; + + SME_ENTER(); + req_msg = qdf_mem_malloc(sizeof(*req_msg)); + if (!req_msg) + return QDF_STATUS_E_NOMEM; + + *req_msg = *input; + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("sme_acquire_global_lock failed!(status=%d)", status); + qdf_mem_free(req_msg); + return status; + } + + /* Serialize the req through MC thread */ + message.bodyptr = req_msg; + message.type = WMA_SET_RSSI_MONITOR_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", status); + qdf_mem_free(req_msg); + } + sme_release_global_lock(&mac->sme); + + return status; +} + +QDF_STATUS sme_set_rssi_threshold_breached_cb(mac_handle_t mac_handle, + rssi_threshold_breached_cb cb) +{ + QDF_STATUS status; + struct mac_context *mac; + + mac = MAC_CONTEXT(mac_handle); + if (!mac) { + sme_err("Invalid mac context"); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("sme_acquire_global_lock failed!(status=%d)", + status); + return status; + } + + mac->sme.rssi_threshold_breached_cb = cb; + sme_release_global_lock(&mac->sme); + return status; +} +#endif /* FEATURE_RSSI_MONITOR */ + +QDF_STATUS sme_reset_rssi_threshold_breached_cb(mac_handle_t mac_handle) +{ + return sme_set_rssi_threshold_breached_cb(mac_handle, NULL); +} + +/** + * sme_get_connected_roaming_vdev_band_mask() - get connected vdev band mask + * + * Return: reg wifi band mask + */ +static uint32_t sme_get_connected_roaming_vdev_band_mask(void) +{ + uint32_t band_mask = REG_BAND_MASK_ALL; + struct mac_context *mac = sme_get_mac_context(); + struct csr_roam_session *session; + uint8_t session_id; + + if (!mac) { + sme_debug("MAC Context is NULL"); + return band_mask; + } + session_id = csr_get_roam_enabled_sta_sessionid(mac); + if (session_id != WLAN_UMAC_VDEV_ID_MAX) { + session = CSR_GET_SESSION(mac, session_id); + band_mask = BIT(wlan_reg_freq_to_band( + session->connectedProfile.op_freq)); + return band_mask; + } + + return band_mask; +} + +/* + * sme_pdev_set_pcl() - Send WMI_PDEV_SET_PCL_CMDID to the WMA + * @mac_handle: Handle returned by macOpen + * @msg: PCL channel list and length structure + * + * Sends the command to WMA to send WMI_PDEV_SET_PCL_CMDID to FW + * Return: QDF_STATUS_SUCCESS on successful posting + */ +QDF_STATUS sme_pdev_set_pcl(struct policy_mgr_pcl_list *msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = sme_get_mac_context(); + struct scheduler_msg message = {0}; + struct set_pcl_req *req_msg; + uint32_t i; + + if (!mac) { + sme_err("mac is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (!msg) { + sme_err("msg is NULL"); + return QDF_STATUS_E_FAILURE; + } + + req_msg = qdf_mem_malloc(sizeof(*req_msg)); + if (!req_msg) + return QDF_STATUS_E_NOMEM; + + req_msg->band_mask = REG_BAND_MASK_ALL; + if (CSR_IS_ROAM_INTRA_BAND_ENABLED(mac)) { + req_msg->band_mask = sme_get_connected_roaming_vdev_band_mask(); + sme_debug("Connected STA band mask%d", req_msg->band_mask); + } + for (i = 0; i < msg->pcl_len; i++) { + req_msg->chan_weights.pcl_list[i] = msg->pcl_list[i]; + req_msg->chan_weights.weight_list[i] = msg->weight_list[i]; + } + + req_msg->chan_weights.pcl_len = msg->pcl_len; + + status = sme_acquire_global_lock(&mac->sme); + if (status != QDF_STATUS_SUCCESS) { + sme_err("sme_acquire_global_lock failed!(status=%d)", status); + qdf_mem_free(req_msg); + return status; + } + + /* Serialize the req through MC thread */ + message.bodyptr = req_msg; + message.type = SIR_HAL_PDEV_SET_PCL_TO_FW; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", status); + qdf_mem_free(req_msg); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + + return status; +} + +/* + * sme_pdev_set_hw_mode() - Send WMI_PDEV_SET_HW_MODE_CMDID to the WMA + * @mac_handle: Handle returned by macOpen + * @msg: HW mode structure containing hw mode and callback details + * + * Sends the command to CSR to send WMI_PDEV_SET_HW_MODE_CMDID to FW + * Return: QDF_STATUS_SUCCESS on successful posting + */ +QDF_STATUS sme_pdev_set_hw_mode(struct policy_mgr_hw_mode msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = sme_get_mac_context(); + tSmeCmd *cmd = NULL; + + if (!mac) { + sme_err("mac is NULL"); + return QDF_STATUS_E_FAILURE; + } + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to acquire lock"); + return QDF_STATUS_E_RESOURCES; + } + + cmd = csr_get_command_buffer(mac); + if (!cmd) { + sme_err("Get command buffer failed"); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NULL_VALUE; + } + + cmd->command = e_sme_command_set_hw_mode; + cmd->vdev_id = msg.session_id; + cmd->u.set_hw_mode_cmd.hw_mode_index = msg.hw_mode_index; + cmd->u.set_hw_mode_cmd.set_hw_mode_cb = msg.set_hw_mode_cb; + cmd->u.set_hw_mode_cmd.reason = msg.reason; + cmd->u.set_hw_mode_cmd.session_id = msg.session_id; + cmd->u.set_hw_mode_cmd.next_action = msg.next_action; + cmd->u.set_hw_mode_cmd.action = msg.action; + cmd->u.set_hw_mode_cmd.context = msg.context; + + sme_debug("Queuing set hw mode to CSR, session: %d reason: %d", + cmd->u.set_hw_mode_cmd.session_id, + cmd->u.set_hw_mode_cmd.reason); + csr_queue_sme_command(mac, cmd, false); + + sme_release_global_lock(&mac->sme); + return QDF_STATUS_SUCCESS; +} + +/** + * sme_nss_update_request() - Send beacon templete update to FW with new + * nss value + * @mac_handle: Handle returned by macOpen + * @vdev_id: the session id + * @new_nss: the new nss value + * @ch_width: channel width, optional value + * @cback: hdd callback + * @next_action: next action to happen at policy mgr after beacon update + * @original_vdev_id: original request hwmode change vdev id + * + * Sends the command to CSR to send to PE + * Return: QDF_STATUS_SUCCESS on successful posting + */ +QDF_STATUS sme_nss_update_request(uint32_t vdev_id, + uint8_t new_nss, uint8_t ch_width, + policy_mgr_nss_update_cback cback, + uint8_t next_action, struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = sme_get_mac_context(); + tSmeCmd *cmd = NULL; + + if (!mac) { + sme_err("mac is null"); + return status; + } + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + cmd = csr_get_command_buffer(mac); + if (!cmd) { + sme_err("Get command buffer failed"); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NULL_VALUE; + } + cmd->command = e_sme_command_nss_update; + /* Sessionized modules may require this info */ + cmd->vdev_id = vdev_id; + cmd->u.nss_update_cmd.new_nss = new_nss; + cmd->u.nss_update_cmd.ch_width = ch_width; + cmd->u.nss_update_cmd.session_id = vdev_id; + cmd->u.nss_update_cmd.nss_update_cb = cback; + cmd->u.nss_update_cmd.context = psoc; + cmd->u.nss_update_cmd.next_action = next_action; + cmd->u.nss_update_cmd.reason = reason; + cmd->u.nss_update_cmd.original_vdev_id = original_vdev_id; + + sme_debug("Queuing e_sme_command_nss_update to CSR:vdev (%d %d) ss %d r %d", + vdev_id, original_vdev_id, new_nss, reason); + csr_queue_sme_command(mac, cmd, false); + sme_release_global_lock(&mac->sme); + } + return status; +} + +/** + * sme_soc_set_dual_mac_config() - Set dual mac configurations + * @mac_handle: Handle returned by macOpen + * @msg: Structure containing the dual mac config parameters + * + * Queues configuration information to CSR to configure + * WLAN firmware for the dual MAC features + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_soc_set_dual_mac_config(struct policy_mgr_dual_mac_config msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = sme_get_mac_context(); + tSmeCmd *cmd; + + if (!mac) { + sme_err("mac is null"); + return QDF_STATUS_E_FAILURE; + } + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to acquire lock"); + return QDF_STATUS_E_RESOURCES; + } + + cmd = csr_get_command_buffer(mac); + if (!cmd) { + sme_err("Get command buffer failed"); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NULL_VALUE; + } + + cmd->command = e_sme_command_set_dual_mac_config; + cmd->u.set_dual_mac_cmd.scan_config = msg.scan_config; + cmd->u.set_dual_mac_cmd.fw_mode_config = msg.fw_mode_config; + cmd->u.set_dual_mac_cmd.set_dual_mac_cb = msg.set_dual_mac_cb; + + sme_debug("set_dual_mac_config scan_config: %x fw_mode_config: %x", + cmd->u.set_dual_mac_cmd.scan_config, + cmd->u.set_dual_mac_cmd.fw_mode_config); + status = csr_queue_sme_command(mac, cmd, false); + + sme_release_global_lock(&mac->sme); + return status; +} + +#ifdef FEATURE_LFR_SUBNET_DETECTION +/** + * sme_gateway_param_update() - to update gateway parameters with WMA + * @mac_handle: Opaque handle to the global MAC context + * @gw_params: request parameters from HDD + * + * Return: QDF_STATUS + * + * This routine will update gateway parameters to WMA + */ +QDF_STATUS sme_gateway_param_update(mac_handle_t mac_handle, + struct gateway_update_req_param *gw_params) +{ + QDF_STATUS qdf_status; + struct scheduler_msg message = {0}; + struct gateway_update_req_param *request_buf; + + request_buf = qdf_mem_malloc(sizeof(*request_buf)); + if (!request_buf) + return QDF_STATUS_E_NOMEM; + + *request_buf = *gw_params; + + message.type = WMA_GW_PARAM_UPDATE_REQ; + message.reserved = 0; + message.bodyptr = request_buf; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Not able to post WMA_GW_PARAM_UPDATE_REQ message to HAL"); + qdf_mem_free(request_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_LFR_SUBNET_DETECTION */ + +/** + * sme_soc_set_antenna_mode() - set antenna mode + * @mac_handle: Handle returned by macOpen + * @msg: Structure containing the antenna mode parameters + * + * Send the command to CSR to send + * WMI_SOC_SET_ANTENNA_MODE_CMDID to FW + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_soc_set_antenna_mode(mac_handle_t mac_handle, + struct sir_antenna_mode_param *msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tSmeCmd *cmd; + + if (!msg) { + sme_err("antenna mode mesg is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to acquire lock"); + return QDF_STATUS_E_RESOURCES; + } + + cmd = csr_get_command_buffer(mac); + if (!cmd) { + sme_release_global_lock(&mac->sme); + sme_err("Get command buffer failed"); + return QDF_STATUS_E_NULL_VALUE; + } + + cmd->command = e_sme_command_set_antenna_mode; + cmd->u.set_antenna_mode_cmd = *msg; + + sme_debug("Antenna mode rx_chains: %d tx_chains: %d", + cmd->u.set_antenna_mode_cmd.num_rx_chains, + cmd->u.set_antenna_mode_cmd.num_tx_chains); + + csr_queue_sme_command(mac, cmd, false); + sme_release_global_lock(&mac->sme); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_set_peer_authorized() - call peer authorized callback + * @peer_addr: peer mac address + * @auth_cb: auth callback + * @vdev_id: vdev id + * + * Return: QDF Status + */ +QDF_STATUS sme_set_peer_authorized(uint8_t *peer_addr, + sme_peer_authorized_fp auth_cb, + uint32_t vdev_id) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + wma_set_peer_authorized_cb(wma_handle, auth_cb); + return wma_set_peer_param(wma_handle, peer_addr, WMI_PEER_AUTHORIZE, + 1, vdev_id); +} + +/** + * sme_setdef_dot11mode() - Updates mac with default dot11mode + * @mac_handle: Global MAC pointer + * + * Return: NULL. + */ +void sme_setdef_dot11mode(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + csr_set_default_dot11_mode(mac_ctx); +} + +/** + * sme_update_tgt_services() - update the target services config. + * @mac_handle: Opaque handle to the global MAC context. + * @cfg: wma_tgt_services parameters. + * + * update the target services config. + * + * Return: None. + */ +void sme_update_tgt_services(mac_handle_t mac_handle, + struct wma_tgt_services *cfg) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + mac_ctx->obss_scan_offload = cfg->obss_scan_offload; + sme_debug("obss_scan_offload: %d", mac_ctx->obss_scan_offload); + mac_ctx->lteCoexAntShare = cfg->lte_coex_ant_share; + mac_ctx->mlme_cfg->gen.as_enabled = cfg->lte_coex_ant_share; + mac_ctx->beacon_offload = cfg->beacon_offload; + mac_ctx->pmf_offload = cfg->pmf_offload; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("mac_ctx->pmf_offload: %d"), mac_ctx->pmf_offload); + mac_ctx->is_fils_roaming_supported = + cfg->is_fils_roaming_supported; + mac_ctx->is_11k_offload_supported = + cfg->is_11k_offload_supported; + sme_debug("pmf_offload: %d fils_roam support %d 11k_offload %d", + mac_ctx->pmf_offload, mac_ctx->is_fils_roaming_supported, + mac_ctx->is_11k_offload_supported); + mac_ctx->bcn_reception_stats = cfg->bcn_reception_stats; +} + +/** + * sme_is_session_id_valid() - Check if the session id is valid + * @mac_handle: Opaque handle to the global MAC context + * @session_id: Session id + * + * Checks if the session id is valid or not + * + * Return: True is the session id is valid, false otherwise + */ +bool sme_is_session_id_valid(mac_handle_t mac_handle, uint32_t session_id) +{ + struct mac_context *mac; + + if (mac_handle) { + mac = MAC_CONTEXT(mac_handle); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: null mac pointer", __func__); + return false; + } + + if (CSR_IS_SESSION_VALID(mac, session_id)) + return true; + + return false; +} + +#ifdef FEATURE_WLAN_TDLS + +/** + * sme_get_opclass() - determine operating class + * @mac_handle: Opaque handle to the global MAC context + * @channel: channel id + * @bw_offset: bandwidth offset + * @opclass: pointer to operating class + * + * Function will determine operating class from regdm_get_opclass_from_channel + * + * Return: none + */ +void sme_get_opclass(mac_handle_t mac_handle, uint8_t channel, + uint8_t bw_offset, uint8_t *opclass) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + /* redgm opclass table contains opclass for 40MHz low primary, + * 40MHz high primary and 20MHz. No support for 80MHz yet. So + * first we will check if bit for 40MHz is set and if so find + * matching opclass either with low primary or high primary + * (a channel would never be in both) and then search for opclass + * matching 20MHz, else for any BW. + */ + if (bw_offset & (1 << BW_40_OFFSET_BIT)) { + *opclass = wlan_reg_dmn_get_opclass_from_channel( + mac_ctx->scan.countryCodeCurrent, + channel, BW40_LOW_PRIMARY); + if (!(*opclass)) { + *opclass = wlan_reg_dmn_get_opclass_from_channel( + mac_ctx->scan.countryCodeCurrent, + channel, BW40_HIGH_PRIMARY); + } + } else if (bw_offset & (1 << BW_20_OFFSET_BIT)) { + *opclass = wlan_reg_dmn_get_opclass_from_channel( + mac_ctx->scan.countryCodeCurrent, + channel, BW20); + } else { + *opclass = wlan_reg_dmn_get_opclass_from_channel( + mac_ctx->scan.countryCodeCurrent, + channel, BWALL); + } +} +#endif + +/** + * sme_set_fw_test() - set fw test + * @fw_test: fw test param + * + * Return: Return QDF_STATUS, otherwise appropriate failure code + */ +QDF_STATUS sme_set_fw_test(struct set_fwtest_params *fw_test) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wma_process_fw_test_cmd(wma_handle, fw_test); +} + +/** + * sme_ht40_stop_obss_scan() - ht40 obss stop scan + * @mac_handle: mac handel + * @vdev_id: vdev identifier + * + * Return: Return QDF_STATUS, otherwise appropriate failure code + */ +QDF_STATUS sme_ht40_stop_obss_scan(mac_handle_t mac_handle, uint32_t vdev_id) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + wma_ht40_stop_obss_scan(wma_handle, vdev_id); + return QDF_STATUS_SUCCESS; +} + +/** + * sme_update_mimo_power_save() - Update MIMO power save + * configuration + * @mac_handle: The handle returned by macOpen + * @is_ht_smps_enabled: enable/disable ht smps + * @ht_smps_mode: smps mode disabled/static/dynamic + * @send_smps_action: flag to send smps force mode command + * to FW + * + * Return: QDF_STATUS if SME update mimo power save + * configuration success else failure status + */ +QDF_STATUS sme_update_mimo_power_save(mac_handle_t mac_handle, + uint8_t is_ht_smps_enabled, + uint8_t ht_smps_mode, + bool send_smps_action) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + sme_debug("SMPS enable: %d mode: %d send action: %d", + is_ht_smps_enabled, ht_smps_mode, + send_smps_action); + mac_ctx->mlme_cfg->ht_caps.enable_smps = + is_ht_smps_enabled; + mac_ctx->mlme_cfg->ht_caps.smps = ht_smps_mode; + mac_ctx->roam.configParam.send_smps_action = + send_smps_action; + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_BCN_RECV_FEATURE +QDF_STATUS sme_handle_bcn_recv_start(mac_handle_t mac_handle, + uint32_t vdev_id, uint32_t nth_value, + bool do_not_resume) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + QDF_STATUS status; + int ret; + + session = CSR_GET_SESSION(mac_ctx, vdev_id); + if (!session) { + sme_err("vdev_id %d not found", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + if (!CSR_IS_SESSION_VALID(mac_ctx, vdev_id)) { + sme_err("CSR session not valid: %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (session->is_bcn_recv_start) { + sme_release_global_lock(&mac_ctx->sme); + sme_err("Beacon receive already started"); + return QDF_STATUS_SUCCESS; + } + session->is_bcn_recv_start = true; + session->beacon_report_do_not_resume = do_not_resume; + sme_release_global_lock(&mac_ctx->sme); + } + + /* + * Allows fw to send beacons of connected AP to driver. + * MSB set : means fw do not wakeup host in wow mode + * LSB set: Value of beacon report period (say n), Means fw sends nth + * beacons of connected AP to HOST + */ + ret = sme_cli_set_command(vdev_id, + WMI_VDEV_PARAM_NTH_BEACON_TO_HOST, + nth_value, VDEV_CMD); + if (ret) { + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + session->is_bcn_recv_start = false; + session->beacon_report_do_not_resume = false; + sme_release_global_lock(&mac_ctx->sme); + } + sme_err("WMI_VDEV_PARAM_NTH_BEACON_TO_HOST %d", ret); + status = qdf_status_from_os_return(ret); + } + + return status; +} + +void sme_stop_beacon_report(mac_handle_t mac_handle, uint32_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + QDF_STATUS status; + int ret; + + session = CSR_GET_SESSION(mac_ctx, session_id); + if (!session) { + sme_err("vdev_id %d not found", session_id); + return; + } + + ret = sme_cli_set_command(session_id, + WMI_VDEV_PARAM_NTH_BEACON_TO_HOST, 0, + VDEV_CMD); + if (ret) + sme_err("WMI_VDEV_PARAM_NTH_BEACON_TO_HOST command failed to FW"); + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + session->is_bcn_recv_start = false; + session->beacon_report_do_not_resume = false; + sme_release_global_lock(&mac_ctx->sme); + } +} + +bool sme_is_beacon_report_started(mac_handle_t mac_handle, uint32_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + if (!session) { + sme_err("vdev_id %d not found", session_id); + return false; + } + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("CSR session not valid: %d", session_id); + return false; + } + + return session->is_bcn_recv_start; +} + +bool sme_is_beacon_reporting_do_not_resume(mac_handle_t mac_handle, + uint32_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + if (!session) { + sme_err("vdev_id %d not found", session_id); + return false; + } + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("CSR session not valid: %d", session_id); + return false; + } + + return session->beacon_report_do_not_resume; +} +#endif + +/** + * sme_add_beacon_filter() - set the beacon filter configuration + * @mac_handle: The handle returned by macOpen + * @session_id: session id + * @ie_map: bitwise array of IEs + * + * Return: Return QDF_STATUS, otherwise appropriate failure code + */ +QDF_STATUS sme_add_beacon_filter(mac_handle_t mac_handle, + uint32_t session_id, + uint32_t *ie_map) +{ + struct scheduler_msg message = {0}; + QDF_STATUS qdf_status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct beacon_filter_param *filter_param; + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("CSR session not valid: %d", session_id); + return QDF_STATUS_E_FAILURE; + } + + filter_param = qdf_mem_malloc(sizeof(*filter_param)); + if (!filter_param) + return QDF_STATUS_E_FAILURE; + + filter_param->vdev_id = session_id; + + qdf_mem_copy(filter_param->ie_map, ie_map, + BCN_FLT_MAX_ELEMS_IE_LIST * sizeof(uint32_t)); + + message.type = WMA_ADD_BCN_FILTER_CMDID; + message.bodyptr = filter_param; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post msg to WDA!", + __func__); + + qdf_mem_free(filter_param); + } + return qdf_status; +} + +/** + * sme_remove_beacon_filter() - set the beacon filter configuration + * @mac_handle: The handle returned by macOpen + * @session_id: session id + * + * Return: Return QDF_STATUS, otherwise appropriate failure code + */ +QDF_STATUS sme_remove_beacon_filter(mac_handle_t mac_handle, + uint32_t session_id) +{ + struct scheduler_msg message = {0}; + QDF_STATUS qdf_status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct beacon_filter_param *filter_param; + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("CSR session not valid: %d", session_id); + return QDF_STATUS_E_FAILURE; + } + + filter_param = qdf_mem_malloc(sizeof(*filter_param)); + if (!filter_param) + return QDF_STATUS_E_FAILURE; + + filter_param->vdev_id = session_id; + + message.type = WMA_REMOVE_BCN_FILTER_CMDID; + message.bodyptr = filter_param; + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post msg to WDA!", + __func__); + + qdf_mem_free(filter_param); + } + return qdf_status; +} + +/** + * sme_send_disassoc_req_frame - send disassoc req + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * @peer_mac: peer mac address + * @reason: reason for disassociation + * wait_for_ack: wait for acknowledgment + * + * function to send disassoc request to lim + * + * return: none + */ +void sme_send_disassoc_req_frame(mac_handle_t mac_handle, uint8_t session_id, + uint8_t *peer_mac, uint16_t reason, + uint8_t wait_for_ack) +{ + struct sme_send_disassoc_frm_req *msg; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + msg = qdf_mem_malloc(sizeof(*msg)); + if (!msg) + return; + + msg->msg_type = eWNI_SME_SEND_DISASSOC_FRAME; + msg->length = sizeof(*msg); + msg->vdev_id = session_id; + qdf_mem_copy(msg->peer_mac, peer_mac, QDF_MAC_ADDR_SIZE); + msg->reason = reason; + msg->wait_for_ack = wait_for_ack; + + qdf_status = umac_send_mb_message_to_mac(msg); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sme_err("umac_send_mb_message_to_mac failed, %d", + qdf_status); +} + +#ifdef FEATURE_WLAN_APF +QDF_STATUS sme_get_apf_capabilities(mac_handle_t mac_handle, + apf_get_offload_cb callback, + void *context) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context * mac_ctx = MAC_CONTEXT(mac_handle); + struct scheduler_msg cds_msg = {0}; + + SME_ENTER(); + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_STATUS_SUCCESS == status) { + /* Serialize the req through MC thread */ + mac_ctx->sme.apf_get_offload_cb = callback; + mac_ctx->sme.apf_get_offload_context = context; + cds_msg.bodyptr = NULL; + cds_msg.type = WDA_APF_GET_CAPABILITIES_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &cds_msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Post apf get offload msg fail")); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac_ctx->sme); + } + + SME_EXIT(); + return status; +} + +QDF_STATUS sme_set_apf_instructions(mac_handle_t mac_handle, + struct sir_apf_set_offload *req) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wma_set_apf_instructions(wma_handle, req); +} + +QDF_STATUS sme_set_apf_enable_disable(mac_handle_t mac_handle, uint8_t vdev_id, + bool apf_enable) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wma_send_apf_enable_cmd(wma_handle, vdev_id, apf_enable); +} + +QDF_STATUS +sme_apf_write_work_memory(mac_handle_t mac_handle, + struct wmi_apf_write_memory_params *write_params) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wma_send_apf_write_work_memory_cmd(wma_handle, write_params); +} + +QDF_STATUS +sme_apf_read_work_memory(mac_handle_t mac_handle, + struct wmi_apf_read_memory_params *read_params, + apf_read_mem_cb callback) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + void *wma_handle; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.apf_read_mem_cb = callback; + sme_release_global_lock(&mac->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_acquire_global_lock failed")); + } + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wma_send_apf_read_work_memory_cmd(wma_handle, read_params); +} +#endif /* FEATURE_WLAN_APF */ + +/** + * sme_get_wni_dot11_mode() - return configured wni dot11mode + * @mac_handle: Opaque handle to the global MAC context + * + * Return: wni dot11 mode. + */ +uint32_t sme_get_wni_dot11_mode(mac_handle_t mac_handle) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + return csr_translate_to_wni_cfg_dot11_mode(mac_ctx, + mac_ctx->roam.configParam.uCfgDot11Mode); +} + +/** + * sme_create_mon_session() - post message to create PE session for monitormode + * operation + * @mac_handle: Opaque handle to the global MAC context + * @bssid: pointer to bssid + * @vdev_id: sme session id + * + * Return: QDF_STATUS_SUCCESS on success, non-zero error code on failure. + */ +QDF_STATUS sme_create_mon_session(mac_handle_t mac_handle, tSirMacAddr bss_id, + uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct sir_create_session *msg; + + msg = qdf_mem_malloc(sizeof(*msg)); + if (msg) { + msg->type = eWNI_SME_MON_INIT_SESSION; + msg->vdev_id = vdev_id; + msg->msg_len = sizeof(*msg); + qdf_mem_copy(msg->bss_id.bytes, bss_id, QDF_MAC_ADDR_SIZE); + status = umac_send_mb_message_to_mac(msg); + } + return status; +} + +QDF_STATUS sme_delete_mon_session(mac_handle_t mac_handle, uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct sir_delete_session *msg; + + msg = qdf_mem_malloc(sizeof(*msg)); + if (msg) { + msg->type = eWNI_SME_MON_DEINIT_SESSION; + msg->vdev_id = vdev_id; + msg->msg_len = sizeof(*msg); + status = umac_send_mb_message_to_mac(msg); + } + + return status; +} + +void +sme_set_del_peers_ind_callback(mac_handle_t mac_handle, + void (*callback)(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id)) +{ + struct mac_context *mac; + + if (!mac_handle) { + QDF_ASSERT(0); + return; + } + mac = MAC_CONTEXT(mac_handle); + mac->del_peers_ind_cb = callback; +} + +void sme_set_chan_info_callback(mac_handle_t mac_handle, + void (*callback)(struct scan_chan_info *chan_info)) +{ + struct mac_context *mac; + + if (!mac_handle) { + QDF_ASSERT(0); + return; + } + mac = MAC_CONTEXT(mac_handle); + mac->chan_info_cb = callback; +} + +void sme_set_vdev_ies_per_band(mac_handle_t mac_handle, uint8_t vdev_id, + enum QDF_OPMODE device_mode) +{ + struct sir_set_vdev_ies_per_band *p_msg; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + enum csr_cfgdot11mode curr_dot11_mode = + mac_ctx->roam.configParam.uCfgDot11Mode; + + p_msg = qdf_mem_malloc(sizeof(*p_msg)); + if (!p_msg) + return; + + + p_msg->vdev_id = vdev_id; + p_msg->device_mode = device_mode; + p_msg->dot11_mode = csr_get_vdev_dot11_mode(mac_ctx, device_mode, + curr_dot11_mode); + p_msg->msg_type = eWNI_SME_SET_VDEV_IES_PER_BAND; + p_msg->len = sizeof(*p_msg); + sme_debug("SET_VDEV_IES_PER_BAND: vdev_id %d dot11mode %d dev_mode %d", + vdev_id, p_msg->dot11_mode, device_mode); + status = umac_send_mb_message_to_mac(p_msg); + if (QDF_STATUS_SUCCESS != status) + sme_err("Send eWNI_SME_SET_VDEV_IES_PER_BAND fail"); +} + +/** + * sme_set_pdev_ht_vht_ies() - sends the set pdev IE req + * @mac_handle: Opaque handle to the global MAC context + * @enable2x2: 1x1 or 2x2 mode. + * + * Sends the set pdev IE req with Nss value. + * + * Return: None + */ +void sme_set_pdev_ht_vht_ies(mac_handle_t mac_handle, bool enable2x2) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct sir_set_ht_vht_cfg *ht_vht_cfg; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!((mac_ctx->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_AUTO) || + (mac_ctx->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_11N) || + (mac_ctx->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_11N_ONLY) || + (mac_ctx->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_11AC) || + (mac_ctx->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_11AC_ONLY))) + return; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_STATUS_SUCCESS == status) { + ht_vht_cfg = qdf_mem_malloc(sizeof(*ht_vht_cfg)); + if (!ht_vht_cfg) { + sme_release_global_lock(&mac_ctx->sme); + return; + } + + ht_vht_cfg->pdev_id = 0; + if (enable2x2) + ht_vht_cfg->nss = 2; + else + ht_vht_cfg->nss = 1; + ht_vht_cfg->dot11mode = + (uint8_t)csr_translate_to_wni_cfg_dot11_mode(mac_ctx, + mac_ctx->roam.configParam.uCfgDot11Mode); + + ht_vht_cfg->msg_type = eWNI_SME_PDEV_SET_HT_VHT_IE; + ht_vht_cfg->len = sizeof(*ht_vht_cfg); + sme_debug("SET_HT_VHT_IE with nss: %d, dot11mode: %d", + ht_vht_cfg->nss, + ht_vht_cfg->dot11mode); + status = umac_send_mb_message_to_mac(ht_vht_cfg); + if (QDF_STATUS_SUCCESS != status) + sme_err("Send SME_PDEV_SET_HT_VHT_IE fail"); + + sme_release_global_lock(&mac_ctx->sme); + } +} + +void sme_update_vdev_type_nss(mac_handle_t mac_handle, uint8_t max_supp_nss, + enum nss_chains_band_info band) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct vdev_type_nss *vdev_nss; + + struct wlan_mlme_nss_chains *nss_chains_ini_cfg = + &mac_ctx->mlme_cfg->nss_chains_ini_cfg; + + if (band == NSS_CHAINS_BAND_5GHZ) + vdev_nss = &mac_ctx->vdev_type_nss_5g; + else + vdev_nss = &mac_ctx->vdev_type_nss_2g; + + vdev_nss->sta = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + STA_NSS_CHAINS_SHIFT)); + vdev_nss->sap = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + SAP_NSS_CHAINS_SHIFT)); + vdev_nss->p2p_go = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + P2P_GO_NSS_CHAINS_SHIFT)); + vdev_nss->p2p_cli = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + P2P_CLI_CHAINS_SHIFT)); + vdev_nss->p2p_dev = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + P2P_DEV_NSS_CHAINS_SHIFT)); + vdev_nss->ibss = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + IBSS_NSS_CHAINS_SHIFT)); + vdev_nss->tdls = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + TDLS_NSS_CHAINS_SHIFT)); + vdev_nss->ocb = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + OCB_NSS_CHAINS_SHIFT)); + vdev_nss->nan = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + NAN_NSS_CHAIN_SHIFT)); + vdev_nss->ndi = QDF_MIN(max_supp_nss, GET_VDEV_NSS_CHAIN( + nss_chains_ini_cfg-> + rx_nss[band], + NAN_NSS_CHAIN_SHIFT)); + + sme_debug("band %d NSS:sta %d sap %d cli %d go %d dev %d ibss %d tdls %d ocb %d nan %d", + band, vdev_nss->sta, vdev_nss->sap, vdev_nss->p2p_cli, + vdev_nss->p2p_go, vdev_nss->p2p_dev, vdev_nss->ibss, + vdev_nss->tdls, vdev_nss->ocb, vdev_nss->nan); +} + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +#define MAX_BSS_COLOR_VAL 63 +#define MIN_BSS_COLOR_VAL 1 + +QDF_STATUS sme_set_he_bss_color(mac_handle_t mac_handle, uint8_t session_id, + uint8_t bss_color) + +{ + struct sir_set_he_bss_color *bss_color_msg; + uint8_t len; + + if (!mac_handle) { + sme_err("Invalid mac_handle pointer"); + return QDF_STATUS_E_FAULT; + } + + sme_debug("Set HE bss_color %d", bss_color); + + if (bss_color < MIN_BSS_COLOR_VAL || bss_color > MAX_BSS_COLOR_VAL) { + sme_debug("Invalid HE bss_color %d", bss_color); + return QDF_STATUS_E_INVAL; + } + len = sizeof(*bss_color_msg); + bss_color_msg = qdf_mem_malloc(len); + if (!bss_color_msg) + return QDF_STATUS_E_NOMEM; + + bss_color_msg->message_type = eWNI_SME_SET_HE_BSS_COLOR; + bss_color_msg->length = len; + bss_color_msg->vdev_id = session_id; + bss_color_msg->bss_color = bss_color; + return umac_send_mb_message_to_mac(bss_color_msg); +} +#endif + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * sme_register_p2p_lo_event() - Register for the p2p lo event + * @mac_handle: Opaque handle to the global MAC context + * @context: the context of the call + * @callback: the callback to hdd + * + * This function registers the callback function for P2P listen + * offload stop event. + * + * Return: none + */ +void sme_register_p2p_lo_event(mac_handle_t mac_handle, void *context, + p2p_lo_callback callback) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + status = sme_acquire_global_lock(&mac->sme); + mac->sme.p2p_lo_event_callback = callback; + mac->sme.p2p_lo_event_context = context; + sme_release_global_lock(&mac->sme); +} +#endif + +/** + * sme_process_mac_pwr_dbg_cmd() - enable mac pwr debugging + * @mac_handle: The handle returned by macOpen + * @session_id: session id + * @dbg_args: args for mac pwr debug command + * Return: Return QDF_STATUS, otherwise appropriate failure code + */ +QDF_STATUS sme_process_mac_pwr_dbg_cmd(mac_handle_t mac_handle, + uint32_t session_id, + struct sir_mac_pwr_dbg_cmd *dbg_args) +{ + struct scheduler_msg message = {0}; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct sir_mac_pwr_dbg_cmd *req; + int i; + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("CSR session not valid: %d", session_id); + return QDF_STATUS_E_FAILURE; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return QDF_STATUS_E_FAILURE; + + req->module_id = dbg_args->module_id; + req->pdev_id = dbg_args->pdev_id; + req->num_args = dbg_args->num_args; + for (i = 0; i < req->num_args; i++) + req->args[i] = dbg_args->args[i]; + + message.type = SIR_HAL_POWER_DBG_CMD; + message.bodyptr = req; + + if (!QDF_IS_STATUS_SUCCESS(scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post msg to WDA!", + __func__); + qdf_mem_free(req); + } + return QDF_STATUS_SUCCESS; +} +/** + * sme_get_vdev_type_nss() - gets the nss per vdev type + * @dev_mode: connection type. + * @nss2g: Pointer to the 2G Nss parameter. + * @nss5g: Pointer to the 5G Nss parameter. + * + * Fills the 2G and 5G Nss values based on connection type. + * + * Return: None + */ +void sme_get_vdev_type_nss(enum QDF_OPMODE dev_mode, + uint8_t *nss_2g, uint8_t *nss_5g) +{ + csr_get_vdev_type_nss(dev_mode, nss_2g, nss_5g); +} + +/** + * sme_update_sta_roam_policy() - update sta roam policy for + * unsafe and DFS channels. + * @mac_handle: Opaque handle to the global MAC context + * @dfs_mode: dfs mode which tell if dfs channel needs to be + * skipped or not + * @skip_unsafe_channels: Param to tell if driver needs to + * skip unsafe channels or not. + * @vdev_id: vdev_id + * @sap_operating_band: Band on which SAP is operating + * + * sme_update_sta_roam_policy update sta rome policies to csr + * this function will call csrUpdateChannelList as well + * to include/exclude DFS channels and unsafe channels. + * + * Return: eHAL_STATUS_SUCCESS or non-zero on failure. + */ +QDF_STATUS sme_update_sta_roam_policy(mac_handle_t mac_handle, + enum sta_roam_policy_dfs_mode dfs_mode, + bool skip_unsafe_channels, + uint8_t vdev_id, + uint8_t sap_operating_band) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct sme_config_params *sme_config; + + if (!mac_ctx) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_FATAL, + "%s: mac_ctx is null", __func__); + return QDF_STATUS_E_FAILURE; + } + + sme_config = qdf_mem_malloc(sizeof(*sme_config)); + if (!sme_config) + return QDF_STATUS_E_FAILURE; + + qdf_mem_zero(sme_config, sizeof(*sme_config)); + sme_get_config_param(mac_handle, sme_config); + + sme_config->csr_config.sta_roam_policy_params.dfs_mode = + dfs_mode; + sme_config->csr_config.sta_roam_policy_params.skip_unsafe_channels = + skip_unsafe_channels; + sme_config->csr_config.sta_roam_policy_params.sap_operating_band = + sap_operating_band; + + sme_update_config(mac_handle, sme_config); + + status = csr_update_channel_list(mac_ctx); + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("failed to update the supported channel list")); + } + + if (mac_ctx->mlme_cfg->lfr.roam_scan_offload_enabled) { + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + csr_roam_update_cfg(mac_ctx, vdev_id, + REASON_ROAM_SCAN_STA_ROAM_POLICY_CHANGED); + sme_release_global_lock(&mac_ctx->sme); + } + } + qdf_mem_free(sme_config); + return status; +} + +/** + * sme_enable_disable_chanavoidind_event - configure ca event ind + * @mac_handle: Opaque handle to the global MAC context + * @set_value: enable/disable + * + * function to enable/disable chan avoidance indication + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_enable_disable_chanavoidind_event(mac_handle_t mac_handle, + uint8_t set_value) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct scheduler_msg msg = {0}; + + if (!mac_ctx->mlme_cfg->gen.optimize_ca_event) { + sme_debug("optimize_ca_event not enabled in ini"); + return QDF_STATUS_E_NOSUPPORT; + } + + sme_debug("set_value: %d", set_value); + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_STATUS_SUCCESS == status) { + qdf_mem_zero(&msg, sizeof(struct scheduler_msg)); + msg.type = WMA_SEND_FREQ_RANGE_CONTROL_IND; + msg.bodyval = set_value; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + sme_release_global_lock(&mac_ctx->sme); + return status; + } + return status; +} + +/* + * sme_set_default_scan_ie() - API to send default scan IE to LIM + * @mac_handle: Opaque handle to the global MAC context + * @session_id: current session ID + * @ie_data: Pointer to Scan IE data + * @ie_len: Length of @ie_data + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_default_scan_ie(mac_handle_t mac_handle, uint16_t session_id, + uint8_t *ie_data, uint16_t ie_len) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct hdd_default_scan_ie *set_ie_params; + + if (!ie_data) + return QDF_STATUS_E_INVAL; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + set_ie_params = qdf_mem_malloc(sizeof(*set_ie_params)); + if (!set_ie_params) + status = QDF_STATUS_E_NOMEM; + else { + set_ie_params->message_type = eWNI_SME_DEFAULT_SCAN_IE; + set_ie_params->length = sizeof(*set_ie_params); + set_ie_params->vdev_id = session_id; + set_ie_params->ie_len = ie_len; + qdf_mem_copy(set_ie_params->ie_data, ie_data, ie_len); + status = umac_send_mb_message_to_mac(set_ie_params); + } + sme_release_global_lock(&mac_ctx->sme); + } + return status; +} + +QDF_STATUS sme_get_sar_power_limits(mac_handle_t mac_handle, + wma_sar_cb callback, void *context) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wma_get_sar_limit(wma_handle, callback, context); +} + +QDF_STATUS sme_set_sar_power_limits(mac_handle_t mac_handle, + struct sar_limit_cmd_params *sar_limit_cmd) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wma_set_sar_limit(wma_handle, sar_limit_cmd); +} + +QDF_STATUS sme_send_coex_config_cmd(struct coex_config_params *coex_cfg_params) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + sme_err("wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + return wma_send_coex_config_cmd(wma_handle, coex_cfg_params); +} + +#ifdef WLAN_FEATURE_FIPS +QDF_STATUS sme_fips_request(mac_handle_t mac_handle, struct fips_params *param, + wma_fips_cb callback, void *context) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + sme_err("wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wma_fips_request(wma_handle, param, callback, context); +} +#endif + +QDF_STATUS sme_set_cts2self_for_p2p_go(mac_handle_t mac_handle) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma_handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + if (QDF_STATUS_SUCCESS != + wma_set_cts2self_for_p2p_go(wma_handle, true)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Failed to set cts2self for p2p GO to firmware", + __func__); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** + * sme_update_tx_fail_cnt_threshold() - update tx fail count Threshold + * @mac_handle: Handle returned by mac_open + * @session_id: Session ID on which tx fail count needs to be updated to FW + * @tx_fail_count: Count for tx fail threshold after which FW will disconnect + * + * This function is used to set tx fail count threshold to firmware. + * firmware will issue disocnnect with peer device once this threshold is + * reached. + * + * Return: Return QDF_STATUS, otherwise appropriate failure code + */ +QDF_STATUS sme_update_tx_fail_cnt_threshold(mac_handle_t mac_handle, + uint8_t session_id, + uint32_t tx_fail_count) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct sme_tx_fail_cnt_threshold *tx_fail_cnt; + struct scheduler_msg msg = {0}; + + tx_fail_cnt = qdf_mem_malloc(sizeof(*tx_fail_cnt)); + if (!tx_fail_cnt) + return QDF_STATUS_E_FAILURE; + + sme_debug("session_id: %d tx_fail_count: %d", + session_id, tx_fail_count); + tx_fail_cnt->session_id = session_id; + tx_fail_cnt->tx_fail_cnt_threshold = tx_fail_count; + + qdf_mem_zero(&msg, sizeof(struct scheduler_msg)); + msg.type = SIR_HAL_UPDATE_TX_FAIL_CNT_TH; + msg.reserved = 0; + msg.bodyptr = tx_fail_cnt; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Not able to post Tx fail count message to WDA")); + qdf_mem_free(tx_fail_cnt); + } + return status; +} + +QDF_STATUS sme_set_lost_link_info_cb(mac_handle_t mac_handle, + lost_link_info_cb cb) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.lost_link_info_cb = cb; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +#ifdef FEATURE_WLAN_ESE +bool sme_roam_is_ese_assoc(struct csr_roam_info *roam_info) +{ + return roam_info->isESEAssoc; +} +#endif + +bool sme_neighbor_roam_is11r_assoc(mac_handle_t mac_handle, uint8_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + return csr_neighbor_roam_is11r_assoc(mac_ctx, session_id); +} + +#ifdef WLAN_FEATURE_WOW_PULSE +/** + * sme_set_wow_pulse() - set wow pulse info + * @wow_pulse_set_info: wow_pulse_mode structure pointer + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_wow_pulse(struct wow_pulse_mode *wow_pulse_set_info) +{ + struct scheduler_msg message = {0}; + QDF_STATUS status; + struct wow_pulse_mode *wow_pulse_set_cmd; + + if (!wow_pulse_set_info) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: invalid wow_pulse_set_info pointer", __func__); + return QDF_STATUS_E_FAILURE; + } + + wow_pulse_set_cmd = qdf_mem_malloc(sizeof(*wow_pulse_set_cmd)); + if (!wow_pulse_set_cmd) + return QDF_STATUS_E_NOMEM; + + *wow_pulse_set_cmd = *wow_pulse_set_info; + + message.type = WMA_SET_WOW_PULSE_CMD; + message.bodyptr = wow_pulse_set_cmd; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post msg to WDA!", + __func__); + qdf_mem_free(wow_pulse_set_cmd); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} +#endif + +/** + * sme_prepare_beacon_from_bss_descp() - prepares beacon frame by populating + * different fields and IEs from bss descriptor. + * @frame_buf: frame buffer to populate + * @bss_descp: bss descriptor + * @bssid: bssid of the beacon frame to populate + * @ie_len: length of IE fields + * + * Return: None + */ +static void sme_prepare_beacon_from_bss_descp(uint8_t *frame_buf, + struct bss_description *bss_descp, + const tSirMacAddr bssid, + uint32_t ie_len) +{ + tDot11fBeacon1 *bcn_fixed; + tpSirMacMgmtHdr mac_hdr = (tpSirMacMgmtHdr)frame_buf; + + /* populate mac header first to indicate beacon */ + mac_hdr->fc.protVer = SIR_MAC_PROTOCOL_VERSION; + mac_hdr->fc.type = SIR_MAC_MGMT_FRAME; + mac_hdr->fc.subType = SIR_MAC_MGMT_BEACON; + qdf_mem_copy((uint8_t *) mac_hdr->da, + (uint8_t *) "\xFF\xFF\xFF\xFF\xFF\xFF", + sizeof(struct qdf_mac_addr)); + qdf_mem_copy((uint8_t *) mac_hdr->sa, bssid, + sizeof(struct qdf_mac_addr)); + qdf_mem_copy((uint8_t *) mac_hdr->bssId, bssid, + sizeof(struct qdf_mac_addr)); + + /* now populate fixed params */ + bcn_fixed = (tDot11fBeacon1 *)(frame_buf + SIR_MAC_HDR_LEN_3A); + /* populate timestamp */ + qdf_mem_copy(&bcn_fixed->TimeStamp.timestamp, &bss_descp->timeStamp, + sizeof(bss_descp->timeStamp)); + /* populate beacon interval */ + bcn_fixed->BeaconInterval.interval = bss_descp->beaconInterval; + /* populate capability */ + qdf_mem_copy(&bcn_fixed->Capabilities, &bss_descp->capabilityInfo, + sizeof(bss_descp->capabilityInfo)); + + /* copy IEs now */ + qdf_mem_copy(frame_buf + SIR_MAC_HDR_LEN_3A + + SIR_MAC_B_PR_SSID_OFFSET, + &bss_descp->ieFields, ie_len); +} + +QDF_STATUS sme_get_rssi_snr_by_bssid(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const uint8_t *bssid, + int8_t *rssi, int8_t *snr) +{ + struct bss_description *bss_descp; + struct scan_filter *scan_filter; + struct scan_result_list *bss_list; + tScanResultHandle result_handle = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); + if (!scan_filter) { + status = QDF_STATUS_E_NOMEM; + goto exit; + } + + status = csr_roam_get_scan_filter_from_profile(mac_ctx, + profile, scan_filter, + false); + if (QDF_STATUS_SUCCESS != status) { + sme_err("prepare_filter failed"); + qdf_mem_free(scan_filter); + goto exit; + } + + /* update filter to get scan result with just target BSSID */ + scan_filter->num_of_bssid = 1; + qdf_mem_copy(scan_filter->bssid_list[0].bytes, + bssid, sizeof(struct qdf_mac_addr)); + + status = csr_scan_get_result(mac_ctx, scan_filter, &result_handle); + qdf_mem_free(scan_filter); + if (QDF_STATUS_SUCCESS != status) { + sme_debug("parse_scan_result failed"); + goto exit; + } + + bss_list = (struct scan_result_list *)result_handle; + bss_descp = csr_get_fst_bssdescr_ptr(bss_list); + if (!bss_descp) { + sme_err("unable to fetch bss descriptor"); + status = QDF_STATUS_E_FAULT; + goto exit; + } + + sme_debug("snr: %d, rssi: %d, raw_rssi: %d", + bss_descp->sinr, bss_descp->rssi, bss_descp->rssi_raw); + + if (rssi) + *rssi = bss_descp->rssi; + if (snr) + *snr = bss_descp->sinr; + +exit: + if (result_handle) + csr_scan_result_purge(mac_ctx, result_handle); + + return status; +} + +QDF_STATUS sme_get_beacon_frm(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const tSirMacAddr bssid, + uint8_t **frame_buf, uint32_t *frame_len, + uint32_t *ch_freq) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tScanResultHandle result_handle = NULL; + struct scan_filter *scan_filter; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct bss_description *bss_descp; + struct scan_result_list *bss_list; + uint32_t ie_len; + + scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); + if (!scan_filter) { + status = QDF_STATUS_E_NOMEM; + goto exit; + } + status = csr_roam_get_scan_filter_from_profile(mac_ctx, + profile, scan_filter, + false); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("prepare_filter failed"); + status = QDF_STATUS_E_FAULT; + qdf_mem_free(scan_filter); + goto exit; + } + + /* update filter to get scan result with just target BSSID */ + scan_filter->num_of_bssid = 1; + qdf_mem_copy(scan_filter->bssid_list[0].bytes, + bssid, sizeof(struct qdf_mac_addr)); + + status = csr_scan_get_result(mac_ctx, scan_filter, &result_handle); + qdf_mem_free(scan_filter); + if (QDF_STATUS_SUCCESS != status) { + sme_err("parse_scan_result failed"); + status = QDF_STATUS_E_FAULT; + goto exit; + } + + bss_list = (struct scan_result_list *)result_handle; + bss_descp = csr_get_fst_bssdescr_ptr(bss_list); + if (!bss_descp) { + sme_err("unable to fetch bss descriptor"); + status = QDF_STATUS_E_FAULT; + goto exit; + } + + /** + * Length of BSS descriptor is without length of + * length itself and length of pointer that holds ieFields. + * + * struct bss_description + * +--------+---------------------------------+---------------+ + * | length | other fields | pointer to IEs| + * +--------+---------------------------------+---------------+ + * ^ + * ieFields + */ + ie_len = bss_descp->length + sizeof(bss_descp->length) + - (uint16_t)(offsetof(struct bss_description, ieFields[0])); + sme_debug("found bss_descriptor ie_len: %d frequency %d", + ie_len, bss_descp->chan_freq); + + /* include mac header and fixed params along with IEs in frame */ + *frame_len = SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_SSID_OFFSET + ie_len; + *frame_buf = qdf_mem_malloc(*frame_len); + if (!*frame_buf) { + status = QDF_STATUS_E_NOMEM; + goto exit; + } + + sme_prepare_beacon_from_bss_descp(*frame_buf, bss_descp, bssid, ie_len); + + if (!*ch_freq) + *ch_freq = bss_descp->chan_freq; +exit: + if (result_handle) + csr_scan_result_purge(mac_ctx, result_handle); + + return status; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS sme_roam_invoke_nud_fail(mac_handle_t mac_handle, uint8_t vdev_id) +{ + struct wma_roam_invoke_cmd *roam_invoke_params; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct mlme_roam_after_data_stall *vdev_roam_params; + struct csr_roam_session *session; + bool control_bitmap; + + if (!mac_ctx->mlme_cfg->gen.data_stall_recovery_fw_support) { + sme_debug("FW does not support data stall recovery, aborting roam invoke"); + return QDF_STATUS_E_NOSUPPORT; + } + + session = CSR_GET_SESSION(mac_ctx, vdev_id); + if (!session) { + sme_err("session %d not found", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + control_bitmap = mlme_get_operations_bitmap(mac_ctx->psoc, vdev_id); + if (control_bitmap || + !MLME_IS_ROAM_INITIALIZED(mac_ctx->psoc, vdev_id)) { + sme_debug("ROAM: RSO Disabled internaly: vdev[%d] bitmap[0x%x]", + vdev_id, control_bitmap); + return QDF_STATUS_E_FAILURE; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + + if (!vdev) { + sme_err("vdev is NULL, aborting roam invoke"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev_roam_params = mlme_get_roam_invoke_params(vdev); + + if (!vdev_roam_params) { + sme_err("Invalid vdev roam params, aborting roam invoke"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return QDF_STATUS_E_NULL_VALUE; + } + + if (vdev_roam_params->roam_invoke_in_progress) { + sme_debug("Roaming already initiated by %d source", + vdev_roam_params->source); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return QDF_STATUS_E_BUSY; + } + + roam_invoke_params = qdf_mem_malloc(sizeof(*roam_invoke_params)); + if (!roam_invoke_params) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return QDF_STATUS_E_NOMEM; + } + roam_invoke_params->vdev_id = vdev_id; + /* Set forced roaming as true so that FW scans all ch, and connect */ + roam_invoke_params->forced_roaming = true; + + msg.type = eWNI_SME_ROAM_INVOKE; + msg.reserved = 0; + msg.bodyptr = roam_invoke_params; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Not able to post ROAM_INVOKE_CMD message to PE"); + qdf_mem_free(roam_invoke_params); + } else { + vdev_roam_params->roam_invoke_in_progress = true; + vdev_roam_params->source = CONNECTION_MGR_INITIATED; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return status; +} + +QDF_STATUS sme_fast_reassoc(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const tSirMacAddr bssid, uint32_t ch_freq, + uint8_t vdev_id, + const tSirMacAddr connected_bssid) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) + return QDF_STATUS_E_FAILURE; + + if (!CSR_IS_SESSION_VALID(mac, vdev_id)) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + if (QDF_IS_STATUS_ERROR(sme_acquire_global_lock(&mac->sme))) + return QDF_STATUS_E_FAILURE; + + status = csr_fast_reassoc(mac_handle, profile, bssid, ch_freq, vdev_id, + connected_bssid); + + sme_release_global_lock(&mac->sme); + + return status; +} + +#endif + +void sme_clear_sae_single_pmk_info(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + tPmkidCacheInfo *pmk_cache_info) +{ + struct wlan_crypto_pmksa *pmksa; + struct wlan_objmgr_vdev *vdev; + tPmkidCacheInfo pmk_to_del; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, session_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Invalid vdev"); + return; + } + pmksa = wlan_crypto_get_pmksa(vdev, &pmk_cache_info->BSSID); + if (!pmksa) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return; + } + qdf_mem_copy(&pmk_to_del.pmk, pmksa->pmk, pmksa->pmk_len); + pmk_to_del.pmk_len = pmksa->pmk_len; + csr_clear_sae_single_pmk(psoc, session_id, &pmk_to_del); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); +} + +QDF_STATUS sme_set_del_pmkid_cache(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + tPmkidCacheInfo *pmk_cache_info, + bool is_add) +{ + struct wmi_unified_pmk_cache *pmk_cache; + struct scheduler_msg msg; + + pmk_cache = qdf_mem_malloc(sizeof(*pmk_cache)); + if (!pmk_cache) + return QDF_STATUS_E_NOMEM; + + qdf_mem_zero(pmk_cache, sizeof(*pmk_cache)); + + pmk_cache->vdev_id = session_id; + + if (!pmk_cache_info) { + pmk_cache->is_flush_all = true; + csr_clear_sae_single_pmk(psoc, session_id, NULL); + goto send_flush_cmd; + } + + if (!pmk_cache_info->ssid_len) { + pmk_cache->cat_flag = WMI_PMK_CACHE_CAT_FLAG_BSSID; + WMI_CHAR_ARRAY_TO_MAC_ADDR(pmk_cache_info->BSSID.bytes, + &pmk_cache->bssid); + } else { + pmk_cache->cat_flag = WMI_PMK_CACHE_CAT_FLAG_SSID_CACHE_ID; + pmk_cache->ssid.length = pmk_cache_info->ssid_len; + qdf_mem_copy(pmk_cache->ssid.mac_ssid, + pmk_cache_info->ssid, + pmk_cache->ssid.length); + } + pmk_cache->cache_id = (uint32_t) (pmk_cache_info->cache_id[0] << 8 | + pmk_cache_info->cache_id[1]); + + if (is_add) + pmk_cache->action_flag = WMI_PMK_CACHE_ACTION_FLAG_ADD_ENTRY; + else + pmk_cache->action_flag = WMI_PMK_CACHE_ACTION_FLAG_DEL_ENTRY; + + pmk_cache->pmkid_len = PMKID_LEN; + qdf_mem_copy(pmk_cache->pmkid, pmk_cache_info->PMKID, + PMKID_LEN); + + pmk_cache->pmk_len = pmk_cache_info->pmk_len; + qdf_mem_copy(pmk_cache->pmk, pmk_cache_info->pmk, + pmk_cache->pmk_len); + +send_flush_cmd: + msg.type = SIR_HAL_SET_DEL_PMKID_CACHE; + msg.reserved = 0; + msg.bodyptr = pmk_cache; + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + sme_err("Not able to post message to WDA"); + if (pmk_cache) { + qdf_mem_zero(pmk_cache, sizeof(*pmk_cache)); + qdf_mem_free(pmk_cache); + } + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/* ARP DEBUG STATS */ + +/** + * sme_set_nud_debug_stats() - sme api to set nud debug stats + * @mac_handle: Opaque handle to the global MAC context + * @set_stats_param: pointer to set stats param + * + * Return: Return QDF_STATUS. + */ +QDF_STATUS sme_set_nud_debug_stats(mac_handle_t mac_handle, + struct set_arp_stats_params + *set_stats_param) +{ + struct set_arp_stats_params *arp_set_param; + struct scheduler_msg msg; + + arp_set_param = qdf_mem_malloc(sizeof(*arp_set_param)); + if (!arp_set_param) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(arp_set_param, set_stats_param, sizeof(*arp_set_param)); + + msg.type = WMA_SET_ARP_STATS_REQ; + msg.reserved = 0; + msg.bodyptr = arp_set_param; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + sme_err("Not able to post message to WDA"); + qdf_mem_free(arp_set_param); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_get_nud_debug_stats() - sme api to get nud debug stats + * @mac_handle: Opaque handle to the global MAC context + * @get_stats_param: pointer to set stats param + * + * Return: Return QDF_STATUS. + */ +QDF_STATUS sme_get_nud_debug_stats(mac_handle_t mac_handle, + struct get_arp_stats_params + *get_stats_param) +{ + struct get_arp_stats_params *arp_get_param; + struct scheduler_msg msg; + + arp_get_param = qdf_mem_malloc(sizeof(*arp_get_param)); + if (!arp_get_param) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(arp_get_param, get_stats_param, sizeof(*arp_get_param)); + + msg.type = WMA_GET_ARP_STATS_REQ; + msg.reserved = 0; + msg.bodyptr = arp_get_param; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + sme_err("Not able to post message to WDA"); + qdf_mem_free(arp_get_param); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_set_peer_param(uint8_t *peer_addr, uint32_t param_id, + uint32_t param_value, uint32_t vdev_id) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + sme_err("wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return wma_set_peer_param(wma_handle, peer_addr, param_id, + param_value, vdev_id); +} + +QDF_STATUS sme_register_set_connection_info_cb(mac_handle_t mac_handle, + bool (*set_connection_info_cb)(bool), + bool (*get_connection_info_cb)(uint8_t *session_id, + enum scan_reject_states *reason)) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.set_connection_info_cb = set_connection_info_cb; + mac->sme.get_connection_info_cb = get_connection_info_cb; + sme_release_global_lock(&mac->sme); + } + return status; +} + +QDF_STATUS sme_rso_cmd_status_cb(mac_handle_t mac_handle, + rso_cmd_status_cb cb) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + mac->sme.rso_cmd_status_cb = cb; + sme_debug("Registered RSO command status callback"); + return status; +} + +QDF_STATUS sme_set_dbs_scan_selection_config(mac_handle_t mac_handle, + struct wmi_dbs_scan_sel_params *params) +{ + struct scheduler_msg message = {0}; + QDF_STATUS status; + struct wmi_dbs_scan_sel_params *dbs_scan_params; + uint32_t i; + + if (0 == params->num_clients) { + sme_err("Num of clients is 0"); + return QDF_STATUS_E_FAILURE; + } + + dbs_scan_params = qdf_mem_malloc(sizeof(*dbs_scan_params)); + if (!dbs_scan_params) + return QDF_STATUS_E_NOMEM; + + dbs_scan_params->num_clients = params->num_clients; + dbs_scan_params->pdev_id = params->pdev_id; + for (i = 0; i < params->num_clients; i++) { + dbs_scan_params->module_id[i] = params->module_id[i]; + dbs_scan_params->num_dbs_scans[i] = params->num_dbs_scans[i]; + dbs_scan_params->num_non_dbs_scans[i] = + params->num_non_dbs_scans[i]; + } + message.type = WMA_SET_DBS_SCAN_SEL_CONF_PARAMS; + message.bodyptr = dbs_scan_params; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Not able to post msg to WMA!"); + qdf_mem_free(dbs_scan_params); + } + + return status; +} + +QDF_STATUS sme_get_rcpi(mac_handle_t mac_handle, struct sme_rcpi_req *rcpi) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg msg = {0}; + struct sme_rcpi_req *rcpi_req; + + rcpi_req = qdf_mem_malloc(sizeof(*rcpi_req)); + if (!rcpi_req) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(rcpi_req, rcpi, sizeof(*rcpi_req)); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + msg.bodyptr = rcpi_req; + msg.type = WMA_GET_RCPI_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + sme_release_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("post get rcpi req failed")); + status = QDF_STATUS_E_FAILURE; + qdf_mem_free(rcpi_req); + } + } else { + qdf_mem_free(rcpi_req); + } + + return status; +} + +void sme_store_pdev(mac_handle_t mac_handle, struct wlan_objmgr_pdev *pdev) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + void *wma_handle; + QDF_STATUS status; + + status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_LEGACY_MAC_ID); + if (QDF_STATUS_SUCCESS != status) { + mac_ctx->pdev = NULL; + return; + } + mac_ctx->pdev = pdev; + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("wma handle is NULL")); + return; + } + wma_store_pdev(wma_handle, pdev); + pdev->pdev_nif.pdev_fw_caps |= SUPPORTED_CRYPTO_CAPS; +} + +QDF_STATUS sme_register_tx_queue_cb(mac_handle_t mac_handle, + tx_queue_cb tx_queue_cb) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.tx_queue_cb = tx_queue_cb; + sme_release_global_lock(&mac->sme); + sme_debug("Tx queue callback set"); + } + + return status; +} + +QDF_STATUS sme_deregister_tx_queue_cb(mac_handle_t mac_handle) +{ + return sme_register_tx_queue_cb(mac_handle, NULL); +} + +#ifdef WLAN_SUPPORT_TWT +QDF_STATUS sme_register_twt_enable_complete_cb(mac_handle_t mac_handle, + twt_enable_cb twt_enable_cb) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.twt_enable_cb = twt_enable_cb; + sme_release_global_lock(&mac->sme); + sme_debug("TWT: enable callback set"); + } + + return status; +} + +QDF_STATUS sme_register_twt_disable_complete_cb(mac_handle_t mac_handle, + twt_disable_cb twt_disable_cb) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.twt_disable_cb = twt_disable_cb; + sme_release_global_lock(&mac->sme); + sme_debug("TWT: disable callback set"); + } + + return status; +} + +QDF_STATUS sme_deregister_twt_enable_complete_cb(mac_handle_t mac_handle) +{ + return sme_register_twt_enable_complete_cb(mac_handle, NULL); +} + +QDF_STATUS sme_deregister_twt_disable_complete_cb(mac_handle_t mac_handle) +{ + return sme_register_twt_disable_complete_cb(mac_handle, NULL); +} +#endif + +QDF_STATUS sme_set_smps_cfg(uint32_t vdev_id, uint32_t param_id, + uint32_t param_val) +{ + return wma_configure_smps_params(vdev_id, param_id, param_val); +} + +QDF_STATUS sme_set_reorder_timeout(mac_handle_t mac_handle, + struct sir_set_rx_reorder_timeout_val *req) +{ + QDF_STATUS status; + tp_wma_handle wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + status = wma_set_rx_reorder_timeout_val(wma_handle, req); + + return status; +} + +QDF_STATUS sme_set_rx_set_blocksize(mac_handle_t mac_handle, + struct sir_peer_set_rx_blocksize *req) +{ + QDF_STATUS status; + tp_wma_handle wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + status = wma_set_rx_blocksize(wma_handle, req); + + return status; +} + +int sme_cli_set_command(int vdev_id, int param_id, int sval, int vpdev) +{ + return wma_cli_set_command(vdev_id, param_id, sval, vpdev); +} + +int sme_set_enable_mem_deep_sleep(mac_handle_t mac_handle, int vdev_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + return wma_cli_set_command(vdev_id, WMI_PDEV_PARAM_HYST_EN, + mac_ctx->mlme_cfg->gen.memory_deep_sleep, + PDEV_CMD); +} + +int sme_set_cck_tx_fir_override(mac_handle_t mac_handle, int vdev_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + return wma_cli_set_command(vdev_id, + WMI_PDEV_PARAM_ENABLE_CCK_TXFIR_OVERRIDE, + mac_ctx->mlme_cfg->gen.cck_tx_fir_override, + PDEV_CMD); +} + +QDF_STATUS sme_set_bt_activity_info_cb(mac_handle_t mac_handle, + bt_activity_info_cb cb) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.bt_activity_info_cb = cb; + sme_release_global_lock(&mac->sme); + sme_debug("bt activity info callback set"); + } + + return status; +} + +QDF_STATUS sme_get_chain_rssi(mac_handle_t mac_handle, + struct get_chain_rssi_req_params *input, + get_chain_rssi_callback callback, + void *context) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + tp_wma_handle wma_handle; + + SME_ENTER(); + + if (!input) { + sme_err("Invalid req params"); + return QDF_STATUS_E_INVAL; + } + + mac_ctx->sme.get_chain_rssi_cb = callback; + mac_ctx->sme.get_chain_rssi_context = context; + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + wma_get_chain_rssi(wma_handle, input); + + SME_EXIT(); + return status; +} + +QDF_STATUS sme_process_msg_callback(struct mac_context *mac, + struct scheduler_msg *msg) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!msg) { + sme_err("Empty message for SME Msg callback"); + return status; + } + status = sme_process_msg(mac, msg); + return status; +} + +void sme_display_disconnect_stats(mac_handle_t mac_handle, uint8_t session_id) +{ + struct csr_roam_session *session; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("%s Invalid session id: %d", __func__, session_id); + return; + } + + session = CSR_GET_SESSION(mac_ctx, session_id); + if (!session) { + sme_err("%s Failed to get session for id: %d", + __func__, session_id); + return; + } + + sme_nofl_info("Total No. of Disconnections: %d", + session->disconnect_stats.disconnection_cnt); + + sme_nofl_info("No. of Diconnects Triggered by Application: %d", + session->disconnect_stats.disconnection_by_app); + + sme_nofl_info("No. of Disassoc Sent by Peer: %d", + session->disconnect_stats.disassoc_by_peer); + + sme_nofl_info("No. of Deauth Sent by Peer: %d", + session->disconnect_stats.deauth_by_peer); + + sme_nofl_info("No. of Disconnections due to Beacon Miss: %d", + session->disconnect_stats.bmiss); + + sme_nofl_info("No. of Disconnections due to Peer Kickout: %d", + session->disconnect_stats.peer_kickout); +} + +#ifdef FEATURE_WLAN_DYNAMIC_CVM + /** + * sme_set_vc_mode_config() - Set voltage corner config to FW + * @bitmap: Bitmap that referes to voltage corner config with + * different phymode and bw configuration + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_vc_mode_config(uint32_t vc_bitmap) +{ + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "wma_handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + if (QDF_STATUS_SUCCESS != + wma_set_vc_mode_config(wma_handle, vc_bitmap)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Failed to set Voltage Control config to FW", + __func__); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * sme_set_bmiss_bcnt() - set bmiss config parameters + * @vdev_id: virtual device for the command + * @first_cnt: bmiss first value + * @final_cnt: bmiss final value + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_set_bmiss_bcnt(uint32_t vdev_id, uint32_t first_cnt, + uint32_t final_cnt) +{ + return wma_config_bmiss_bcnt_params(vdev_id, first_cnt, final_cnt); +} + +QDF_STATUS sme_send_limit_off_channel_params(mac_handle_t mac_handle, + uint8_t vdev_id, + bool is_tos_active, + uint32_t max_off_chan_time, + uint32_t rest_time, + bool skip_dfs_chan) +{ + struct sir_limit_off_chan *cmd; + struct scheduler_msg msg = {0}; + + cmd = qdf_mem_malloc(sizeof(*cmd)); + if (!cmd) + return QDF_STATUS_E_NOMEM; + + cmd->vdev_id = vdev_id; + cmd->is_tos_active = is_tos_active; + cmd->max_off_chan_time = max_off_chan_time; + cmd->rest_time = rest_time; + cmd->skip_dfs_chans = skip_dfs_chan; + + msg.type = WMA_SET_LIMIT_OFF_CHAN; + msg.reserved = 0; + msg.bodyptr = cmd; + + if (!QDF_IS_STATUS_SUCCESS(scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg))) { + sme_err("Not able to post WMA_SET_LIMIT_OFF_CHAN to WMA"); + qdf_mem_free(cmd); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +uint32_t sme_unpack_rsn_ie(mac_handle_t mac_handle, uint8_t *buf, + uint8_t buf_len, tDot11fIERSN *rsn_ie, + bool append_ie) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + return dot11f_unpack_ie_rsn(mac_ctx, buf, buf_len, rsn_ie, append_ie); +} + +void sme_add_qcn_ie(mac_handle_t mac_handle, uint8_t *ie_data, + uint16_t *ie_len) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + static const uint8_t qcn_ie[] = {WLAN_ELEMID_VENDOR, 8, + 0x8C, 0xFD, 0xF0, 0x1, + QCN_IE_VERSION_SUBATTR_ID, + QCN_IE_VERSION_SUBATTR_DATA_LEN, + QCN_IE_VERSION_SUPPORTED, + QCN_IE_SUBVERSION_SUPPORTED}; + + if (!mac_ctx->mlme_cfg->sta.qcn_ie_support) { + sme_debug("QCN IE is not supported"); + return; + } + + if (((*ie_len) + sizeof(qcn_ie)) > MAX_DEFAULT_SCAN_IE_LEN) { + sme_err("IE buffer not enough for QCN IE"); + return; + } + + qdf_mem_copy(ie_data + (*ie_len), qcn_ie, sizeof(qcn_ie)); + (*ie_len) += sizeof(qcn_ie); +} + +#ifdef FEATURE_BSS_TRANSITION +/** + * sme_get_status_for_candidate() - Get bss transition status for candidate + * @mac_handle: Opaque handle to the global MAC context + * @conn_bss_desc: connected bss descriptor + * @bss_desc: candidate bss descriptor + * @info: candiadate bss information + * @trans_reason: transition reason code + * @is_bt_in_progress: bt activity indicator + * + * Return : true if candidate is rejected and reject reason is filled + * @info->status. Otherwise returns false. + */ +static bool sme_get_status_for_candidate(mac_handle_t mac_handle, + struct bss_description *conn_bss_desc, + struct bss_description *bss_desc, + struct bss_candidate_info *info, + uint8_t trans_reason, + bool is_bt_in_progress) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct wlan_mlme_mbo *mbo_cfg; + int8_t current_rssi_mcc_thres; + uint32_t bss_chan_freq, conn_bss_chan_freq; + bool bss_chan_safe, conn_bss_chan_safe; + + if (!(mac_ctx->mlme_cfg)) { + pe_err("mlme cfg is NULL"); + return false; + } + mbo_cfg = &mac_ctx->mlme_cfg->mbo_cfg; + + /* + * Low RSSI based rejection + * If candidate rssi is less than mbo_candidate_rssi_thres and connected + * bss rssi is greater than mbo_current_rssi_thres, then reject the + * candidate with MBO reason code 4. + */ + if ((bss_desc->rssi < mbo_cfg->mbo_candidate_rssi_thres) && + (conn_bss_desc->rssi > mbo_cfg->mbo_current_rssi_thres)) { + sme_err("Candidate BSS "QDF_MAC_ADDR_FMT" has LOW RSSI(%d), hence reject", + QDF_MAC_ADDR_REF(bss_desc->bssId), bss_desc->rssi); + info->status = QCA_STATUS_REJECT_LOW_RSSI; + return true; + } + + if (trans_reason == MBO_TRANSITION_REASON_LOAD_BALANCING || + trans_reason == MBO_TRANSITION_REASON_TRANSITIONING_TO_PREMIUM_AP) { + bss_chan_freq = bss_desc->chan_freq; + conn_bss_chan_freq = conn_bss_desc->chan_freq; + /* + * MCC rejection + * If moving to candidate's channel will result in MCC scenario + * and the rssi of connected bss is greater than + * mbo_current_rssi_mss_thres, then reject the candidate with + * MBO reason code 3. + */ + current_rssi_mcc_thres = mbo_cfg->mbo_current_rssi_mcc_thres; + if ((conn_bss_desc->rssi > current_rssi_mcc_thres) && + csr_is_mcc_channel(mac_ctx, bss_chan_freq)) { + sme_err("Candidate BSS "QDF_MAC_ADDR_FMT" causes MCC, hence reject", + QDF_MAC_ADDR_REF(bss_desc->bssId)); + info->status = + QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY; + return true; + } + + /* + * BT coex rejection + * If AP is trying to move the client from 5G to 2.4G and moving + * to 2.4G will result in BT coex and candidate channel rssi is + * less than mbo_candidate_rssi_btc_thres, then reject the + * candidate with MBO reason code 2. + */ + if (WLAN_REG_IS_5GHZ_CH_FREQ(conn_bss_chan_freq) && + WLAN_REG_IS_24GHZ_CH_FREQ(bss_chan_freq) && + is_bt_in_progress && + (bss_desc->rssi < mbo_cfg->mbo_candidate_rssi_btc_thres)) { + sme_err("Candidate BSS "QDF_MAC_ADDR_FMT" causes BT coex, hence reject", + QDF_MAC_ADDR_REF(bss_desc->bssId)); + info->status = + QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED; + return true; + } + + /* + * LTE coex rejection + * If moving to candidate's channel can cause LTE coex, then + * reject the candidate with MBO reason code 5. + */ + conn_bss_chan_safe = policy_mgr_is_safe_channel( + mac_ctx->psoc, conn_bss_chan_freq); + bss_chan_safe = policy_mgr_is_safe_channel( + mac_ctx->psoc, bss_chan_freq); + + if (conn_bss_chan_safe && !bss_chan_safe) { + sme_err("High interference expected if transitioned to BSS " + QDF_MAC_ADDR_FMT" hence reject", + QDF_MAC_ADDR_REF(bss_desc->bssId)); + info->status = + QCA_STATUS_REJECT_HIGH_INTERFERENCE; + return true; + } + } + + return false; +} + +QDF_STATUS sme_get_bss_transition_status(mac_handle_t mac_handle, + uint8_t transition_reason, + struct qdf_mac_addr *bssid, + struct bss_candidate_info *info, + uint16_t n_candidates, + bool is_bt_in_progress) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct bss_description *bss_desc, *conn_bss_desc; + tCsrScanResultInfo *res, *conn_res; + uint16_t i; + + if (!n_candidates || !info) { + sme_err("No candidate info available"); + return QDF_STATUS_E_INVAL; + } + + conn_res = qdf_mem_malloc(sizeof(tCsrScanResultInfo)); + if (!conn_res) + return QDF_STATUS_E_NOMEM; + + res = qdf_mem_malloc(sizeof(tCsrScanResultInfo)); + if (!res) { + status = QDF_STATUS_E_NOMEM; + goto free; + } + + /* Get the connected BSS descriptor */ + status = sme_scan_get_result_for_bssid(mac_handle, bssid, conn_res); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to find connected BSS in scan list"); + goto free; + } + conn_bss_desc = &conn_res->BssDescriptor; + + for (i = 0; i < n_candidates; i++) { + /* Get candidate BSS descriptors */ + status = sme_scan_get_result_for_bssid(mac_handle, + &info[i].bssid, + res); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("BSS "QDF_MAC_ADDR_FMT" not present in scan list", + QDF_MAC_ADDR_REF(info[i].bssid.bytes)); + info[i].status = QCA_STATUS_REJECT_UNKNOWN; + continue; + } + + bss_desc = &res->BssDescriptor; + if (!sme_get_status_for_candidate(mac_handle, conn_bss_desc, + bss_desc, &info[i], + transition_reason, + is_bt_in_progress)) { + /* + * If status is not over written, it means it is a + * candidate for accept. + */ + info[i].status = QCA_STATUS_ACCEPT; + } + } + + /* success */ + status = QDF_STATUS_SUCCESS; + +free: + /* free allocated memory */ + if (conn_res) + qdf_mem_free(conn_res); + if (res) + qdf_mem_free(res); + + return status; +} +#endif /* FEATURE_BSS_TRANSITION */ + +bool sme_is_conn_state_connected(mac_handle_t mac_handle, uint8_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + return csr_is_conn_state_connected(mac_ctx, session_id); +} + +void sme_enable_roaming_on_connected_sta(mac_handle_t mac_handle, + uint8_t vdev_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_ERROR(status)) + return; + + csr_enable_roaming_on_connected_sta(mac_ctx, vdev_id); + sme_release_global_lock(&mac_ctx->sme); +} + +int16_t sme_get_oper_chan_freq(struct wlan_objmgr_vdev *vdev) +{ + uint8_t vdev_id; + struct csr_roam_session *session; + struct mac_context *mac_ctx; + mac_handle_t mac_handle; + + if (!vdev) { + sme_err("Invalid vdev id is passed"); + return 0; + } + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (!mac_handle) { + sme_err("mac_handle is null"); + return 0; + } + mac_ctx = MAC_CONTEXT(mac_handle); + vdev_id = wlan_vdev_get_id(vdev); + if (!CSR_IS_SESSION_VALID(mac_ctx, vdev_id)) { + sme_err("Invalid vdev id is passed"); + return 0; + } + + session = CSR_GET_SESSION(mac_ctx, vdev_id); + + return csr_get_infra_operation_chan_freq(mac_ctx, vdev_id); +} + +enum phy_ch_width sme_get_oper_ch_width(struct wlan_objmgr_vdev *vdev) +{ + uint8_t vdev_id; + struct csr_roam_session *session; + struct mac_context *mac_ctx; + mac_handle_t mac_handle; + enum phy_ch_width ch_width = CH_WIDTH_20MHZ; + + if (!vdev) { + sme_err("Invalid vdev id is passed"); + return CH_WIDTH_INVALID; + } + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (!mac_handle) { + sme_err("mac_handle is null"); + return CH_WIDTH_INVALID; + } + mac_ctx = MAC_CONTEXT(mac_handle); + vdev_id = wlan_vdev_get_id(vdev); + if (!CSR_IS_SESSION_VALID(mac_ctx, vdev_id)) { + sme_err("Invalid vdev id is passed"); + return CH_WIDTH_INVALID; + } + + session = CSR_GET_SESSION(mac_ctx, vdev_id); + + if (csr_is_conn_state_connected(mac_ctx, vdev_id)) + ch_width = session->connectedProfile.vht_channel_width; + + return ch_width; +} + +int sme_get_sec20chan_freq_mhz(struct wlan_objmgr_vdev *vdev, + uint16_t *sec20chan_freq) +{ + uint8_t vdev_id; + + vdev_id = wlan_vdev_get_id(vdev); + /* Need to extend */ + return 0; +} + +#ifdef WLAN_FEATURE_SAE +QDF_STATUS sme_handle_sae_msg(mac_handle_t mac_handle, + uint8_t session_id, + uint8_t sae_status, + struct qdf_mac_addr peer_mac_addr, + const uint8_t *pmkid) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct sir_sae_msg *sae_msg; + struct scheduler_msg sch_msg = {0}; + struct wmi_roam_auth_status_params *params; + struct csr_roam_session *csr_session; + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(qdf_status)) + return qdf_status; + + csr_session = CSR_GET_SESSION(mac, session_id); + if (!csr_session) { + sme_err("session %d not found", session_id); + qdf_status = QDF_STATUS_E_FAILURE; + goto error; + } + + /* Update the status to SME in below cases + * 1. SAP mode: Always + * 2. STA mode: When the device is not in joined state + * + * If the device is in joined state, send the status to WMA which + * is meant for roaming. + */ + if ((csr_session->pCurRoamProfile && + csr_session->pCurRoamProfile->csrPersona == QDF_SAP_MODE) || + !CSR_IS_ROAM_JOINED(mac, session_id)) { + sae_msg = qdf_mem_malloc(sizeof(*sae_msg)); + if (!sae_msg) { + qdf_status = QDF_STATUS_E_NOMEM; + goto error; + } + + sae_msg->message_type = eWNI_SME_SEND_SAE_MSG; + sae_msg->length = sizeof(*sae_msg); + sae_msg->vdev_id = session_id; + sae_msg->sae_status = sae_status; + qdf_mem_copy(sae_msg->peer_mac_addr, + peer_mac_addr.bytes, + QDF_MAC_ADDR_SIZE); + sme_debug("SAE: sae_status %d vdev_id %d Peer: " + QDF_MAC_ADDR_FMT, sae_msg->sae_status, + sae_msg->vdev_id, + QDF_MAC_ADDR_REF(sae_msg->peer_mac_addr)); + + sch_msg.type = eWNI_SME_SEND_SAE_MSG; + sch_msg.bodyptr = sae_msg; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, + &sch_msg); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + qdf_mem_free(sae_msg); + goto error; + } + } else { + /* + * For WPA3 SAE roaming, external auth offload is enabled. The + * firmware will send preauth start event after candidate + * selection. The supplicant will perform the SAE authentication + * and will send the auth status, PMKID in the external auth + * cmd. + * + * csr roam state is CSR_ROAM_STATE_JOINED. So this SAE + * external auth event is for wpa3 roam pre-auth offload. + * + * Post the preauth status to WMA. + */ + params = qdf_mem_malloc(sizeof(*params)); + if (!params) { + qdf_status = QDF_STATUS_E_NOMEM; + goto error; + } + + params->vdev_id = session_id; + params->preauth_status = sae_status; + qdf_copy_macaddr(¶ms->bssid, &peer_mac_addr); + + qdf_mem_zero(params->pmkid, PMKID_LEN); + if (pmkid) + qdf_mem_copy(params->pmkid, pmkid, PMKID_LEN); + + sch_msg.type = WMA_ROAM_PRE_AUTH_STATUS; + sch_msg.bodyptr = params; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &sch_msg); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + sme_err("WMA_ROAM_PRE_AUTH_STATUS cmd posting failed"); + qdf_mem_free(params); + } + } +error: + sme_release_global_lock(&mac->sme); + + return qdf_status; +} +#endif + +bool sme_is_sta_key_exchange_in_progress(mac_handle_t mac_handle, + uint8_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("Invalid session id: %d", session_id); + return false; + } + + return CSR_IS_WAIT_FOR_KEY(mac_ctx, session_id); +} + +bool sme_validate_channel_list(mac_handle_t mac_handle, + uint32_t *chan_freq_list, + uint8_t num_channels) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t i = 0; + uint8_t j; + bool found; + struct csr_channel *ch_lst_info = &mac_ctx->scan.base_channels; + + if (!chan_freq_list || !num_channels) { + sme_err("Chan list empty %pK or num_channels is 0", + chan_freq_list); + return false; + } + + while (i < num_channels) { + found = false; + for (j = 0; j < ch_lst_info->numChannels; j++) { + if (ch_lst_info->channel_freq_list[j] == + chan_freq_list[i]) { + found = true; + break; + } + } + + if (!found) { + sme_debug("Invalid channel %d", chan_freq_list[i]); + return false; + } + + i++; + } + + return true; +} + +void sme_set_amsdu(mac_handle_t mac_handle, bool enable) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + mac_ctx->is_usr_cfg_amsdu_enabled = enable; +} + +#ifdef WLAN_FEATURE_11AX +void sme_set_he_testbed_def(mac_handle_t mac_handle, uint8_t vdev_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, vdev_id); + + if (!session) { + sme_debug("No session for id %d", vdev_id); + return; + } + sme_debug("set HE testbed defaults"); + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.amsdu_in_ampdu = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.twt_request = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.omi_a_ctrl = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.he_ppdu_20_in_160_80p80Mhz = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.he_ppdu_20_in_40Mhz_2G = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.he_ppdu_80_in_160_80p80Mhz = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.dcm_enc_tx = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.dcm_enc_rx = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.ul_mu = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.max_nc = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.trigger_frm_mac_pad = + QCA_WLAN_HE_16US_OF_PROCESS_TIME; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.flex_twt_sched = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.ofdma_ra = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.he_4x_ltf_3200_gi_ndp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.qtp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.bsrp_ampdu_aggr = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.a_bqr = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.he_sub_ch_sel_tx_supp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.ndp_feedback_supp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.ops_supp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.srp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.power_boost = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.num_sounding_lt_80 = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.num_sounding_gt_80 = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.dl_mu_mimo_part_bw = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.non_trig_cqi_feedback = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.tx_1024_qam_lt_242_tone_ru = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.rx_1024_qam_lt_242_tone_ru = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.rx_full_bw_su_he_mu_compress_sigb = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.rx_full_bw_su_he_mu_non_cmpr_sigb = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.su_beamformer = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.multi_tid_aggr_rx_supp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.multi_tid_aggr_tx_supp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.he_dynamic_smps = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.punctured_sounding_supp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.ht_vht_trg_frm_rx_supp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.su_feedback_tone16 = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.mu_feedback_tone16 = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.codebook_su = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.codebook_mu = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.ul_2x996_tone_ru_supp = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.beamforming_feedback = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.he_er_su_ppdu = 0; + mac_ctx->mlme_cfg->he_caps.dot11_he_cap.dl_mu_mimo_part_bw = 0; + csr_update_session_he_cap(mac_ctx, session); +} + +void sme_reset_he_caps(mac_handle_t mac_handle, uint8_t vdev_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, vdev_id); + + if (!session) { + sme_err("No session for id %d", vdev_id); + return; + } + sme_debug("reset HE caps"); + mac_ctx->mlme_cfg->he_caps.dot11_he_cap = + mac_ctx->mlme_cfg->he_caps.he_cap_orig; + csr_update_session_he_cap(mac_ctx, session); +} +#endif + +uint8_t sme_get_mcs_idx(uint16_t raw_rate, enum tx_rate_info rate_flags, + uint8_t *nss, uint8_t *dcm, + enum txrate_gi *guard_interval, + enum tx_rate_info *mcs_rate_flags) +{ + return wma_get_mcs_idx(raw_rate, rate_flags, + nss, dcm, guard_interval, mcs_rate_flags); +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +QDF_STATUS sme_get_sta_cxn_info(mac_handle_t mac_handle, uint32_t session_id, + char *buf, uint32_t buf_sz) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct tagCsrRoamConnectedProfile *conn_profile; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + if (!session || !session->pCurRoamProfile) { + status = QDF_STATUS_E_FAILURE; + goto end; + } + conn_profile = &session->connectedProfile; + if (!conn_profile) { + status = QDF_STATUS_E_FAILURE; + goto end; + } + csr_get_sta_cxn_info(mac_ctx, session, conn_profile, buf, buf_sz); +end: + sme_release_global_lock(&mac_ctx->sme); + + return status; +} +#endif +QDF_STATUS +sme_get_roam_scan_stats(mac_handle_t mac_handle, + roam_scan_stats_cb cb, void *context, + uint32_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg msg = {0}; + struct sir_roam_scan_stats *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return QDF_STATUS_E_NOMEM; + + req->vdev_id = vdev_id; + req->cb = cb; + req->context = context; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + msg.bodyptr = req; + msg.type = WMA_GET_ROAM_SCAN_STATS; + msg.reserved = 0; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg); + sme_release_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("post roam scan stats req failed")); + status = QDF_STATUS_E_FAILURE; + qdf_mem_free(req); + } + } else { + qdf_mem_free(req); + } + + return status; +} + +void sme_update_score_config(mac_handle_t mac_handle, + struct scoring_config *score_config) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct wlan_mlme_scoring_cfg *mlme_scoring_cfg; + + mlme_scoring_cfg = &mac_ctx->mlme_cfg->scoring; + + score_config->weight_cfg.rssi_weightage = + mlme_scoring_cfg->weight_cfg.rssi_weightage; + score_config->weight_cfg.ht_caps_weightage = + mlme_scoring_cfg->weight_cfg.ht_caps_weightage; + score_config->weight_cfg.vht_caps_weightage = + mlme_scoring_cfg->weight_cfg.vht_caps_weightage; + score_config->weight_cfg.he_caps_weightage = + mlme_scoring_cfg->weight_cfg.he_caps_weightage; + score_config->weight_cfg.chan_width_weightage = + mlme_scoring_cfg->weight_cfg.chan_width_weightage; + score_config->weight_cfg.chan_band_weightage = + mlme_scoring_cfg->weight_cfg.chan_band_weightage; + score_config->weight_cfg.nss_weightage = + mlme_scoring_cfg->weight_cfg.nss_weightage; + score_config->weight_cfg.beamforming_cap_weightage = + mlme_scoring_cfg->weight_cfg.beamforming_cap_weightage; + score_config->weight_cfg.pcl_weightage = + mlme_scoring_cfg->weight_cfg.pcl_weightage; + score_config->weight_cfg.channel_congestion_weightage = + mlme_scoring_cfg->weight_cfg.channel_congestion_weightage; + score_config->weight_cfg.oce_wan_weightage = + mlme_scoring_cfg->weight_cfg.oce_wan_weightage; + + score_config->bandwidth_weight_per_index = + mlme_scoring_cfg->bandwidth_weight_per_index; + score_config->nss_weight_per_index = + mlme_scoring_cfg->nss_weight_per_index; + score_config->band_weight_per_index = + mlme_scoring_cfg->band_weight_per_index; + + score_config->rssi_score.best_rssi_threshold = + mlme_scoring_cfg->rssi_score.best_rssi_threshold; + score_config->rssi_score.good_rssi_threshold = + mlme_scoring_cfg->rssi_score.good_rssi_threshold; + score_config->rssi_score.bad_rssi_threshold = + mlme_scoring_cfg->rssi_score.bad_rssi_threshold; + score_config->rssi_score.good_rssi_pcnt = + mlme_scoring_cfg->rssi_score.good_rssi_pcnt; + score_config->rssi_score.bad_rssi_pcnt = + mlme_scoring_cfg->rssi_score.bad_rssi_pcnt; + score_config->rssi_score.good_rssi_bucket_size = + mlme_scoring_cfg->rssi_score.good_rssi_bucket_size; + score_config->rssi_score.bad_rssi_bucket_size = + mlme_scoring_cfg->rssi_score.bad_rssi_bucket_size; + score_config->rssi_score.rssi_pref_5g_rssi_thresh = + mlme_scoring_cfg->rssi_score.rssi_pref_5g_rssi_thresh; + + score_config->esp_qbss_scoring.num_slot = + mlme_scoring_cfg->esp_qbss_scoring.num_slot; + score_config->esp_qbss_scoring.score_pcnt3_to_0 = + mlme_scoring_cfg->esp_qbss_scoring.score_pcnt3_to_0; + score_config->esp_qbss_scoring.score_pcnt7_to_4 = + mlme_scoring_cfg->esp_qbss_scoring.score_pcnt7_to_4; + score_config->esp_qbss_scoring.score_pcnt11_to_8 = + mlme_scoring_cfg->esp_qbss_scoring.score_pcnt11_to_8; + score_config->esp_qbss_scoring.score_pcnt15_to_12 = + mlme_scoring_cfg->esp_qbss_scoring.score_pcnt15_to_12; + + score_config->oce_wan_scoring.num_slot = + mlme_scoring_cfg->oce_wan_scoring.num_slot; + score_config->oce_wan_scoring.score_pcnt3_to_0 = + mlme_scoring_cfg->oce_wan_scoring.score_pcnt3_to_0; + score_config->oce_wan_scoring.score_pcnt7_to_4 = + mlme_scoring_cfg->oce_wan_scoring.score_pcnt7_to_4; + score_config->oce_wan_scoring.score_pcnt11_to_8 = + mlme_scoring_cfg->oce_wan_scoring.score_pcnt11_to_8; + score_config->oce_wan_scoring.score_pcnt15_to_12 = + mlme_scoring_cfg->oce_wan_scoring.score_pcnt15_to_12; +} + +static void +__sme_enable_fw_module_log_level(uint8_t *enable_fw_module_log_level, + uint8_t enable_fw_module_log_level_num, + int vdev_id, int param_id) +{ + uint8_t count = 0; + uint32_t value = 0; + int ret; + + while (count < enable_fw_module_log_level_num) { + /* + * FW module log level input array looks like + * below: + * enable_fw_module_log_level = {, + * ,...} + * For example: + * enable_fw_module_log_level= + * {1,0,2,1,3,2,4,3,5,4,6,5,7,6} + * Above input array means : + * For FW module ID 1 enable log level 0 + * For FW module ID 2 enable log level 1 + * For FW module ID 3 enable log level 2 + * For FW module ID 4 enable log level 3 + * For FW module ID 5 enable log level 4 + * For FW module ID 6 enable log level 5 + * For FW module ID 7 enable log level 6 + */ + + if ((enable_fw_module_log_level[count] > WLAN_MODULE_ID_MAX) || + (enable_fw_module_log_level[count + 1] > DBGLOG_LVL_MAX)) { + sme_err("Module id %d or dbglog level %d input value is more than max", + enable_fw_module_log_level[count], + enable_fw_module_log_level[count + 1]); + count += 2; + continue; + } + + value = enable_fw_module_log_level[count] << 16; + value |= enable_fw_module_log_level[count + 1]; + ret = sme_cli_set_command(vdev_id, param_id, value, DBG_CMD); + if (ret != 0) + sme_err("Failed to enable FW module log level %d ret %d", + value, ret); + + count += 2; + } +} + +void sme_enable_fw_module_log_level(mac_handle_t mac_handle, int vdev_id) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + uint8_t *enable_fw_module_log_level; + uint8_t enable_fw_module_log_level_num; + + status = ucfg_fwol_get_enable_fw_module_log_level( + mac_ctx->psoc, &enable_fw_module_log_level, + &enable_fw_module_log_level_num); + if (QDF_IS_STATUS_ERROR(status)) + return; + __sme_enable_fw_module_log_level(enable_fw_module_log_level, + enable_fw_module_log_level_num, + vdev_id, + WMI_DBGLOG_MOD_LOG_LEVEL); + + enable_fw_module_log_level_num = 0; + status = ucfg_fwol_wow_get_enable_fw_module_log_level( + mac_ctx->psoc, &enable_fw_module_log_level, + &enable_fw_module_log_level_num); + if (QDF_IS_STATUS_ERROR(status)) + return; + __sme_enable_fw_module_log_level(enable_fw_module_log_level, + enable_fw_module_log_level_num, + vdev_id, + WMI_DBGLOG_MOD_WOW_LOG_LEVEL); +} + +#ifdef WLAN_FEATURE_MOTION_DETECTION +/** + * sme_set_md_bl_evt_cb - Register/set motion detection baseline callback + * @mac_handle: mac handle + * @callback_fn: callback function pointer + * @hdd_ctx: hdd context + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_set_md_bl_evt_cb( + mac_handle_t mac_handle, + QDF_STATUS (*callback_fn)(void *ctx, struct sir_md_bl_evt *event), + void *hdd_ctx +) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + mac->sme.md_bl_evt_cb = callback_fn; + mac->sme.md_ctx = hdd_ctx; + sme_release_global_lock(&mac->sme); + } + return qdf_status; +} + +/** + * sme_set_md_host_evt_cb - Register/set motion detection callback + * @mac_handle: mac handle + * @callback_fn: motion detection callback function pointer + * @hdd_ctx: hdd context + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_set_md_host_evt_cb( + mac_handle_t mac_handle, + QDF_STATUS (*callback_fn)(void *ctx, struct sir_md_evt *event), + void *hdd_ctx +) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + mac->sme.md_host_evt_cb = callback_fn; + mac->sme.md_ctx = hdd_ctx; + sme_release_global_lock(&mac->sme); + } + return qdf_status; +} + +/** + * sme_motion_det_config - Post motion detection configuration msg to scheduler + * @mac_handle: mac handle + * @motion_det_config: motion detection configuration + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_motion_det_config(mac_handle_t mac_handle, + struct sme_motion_det_cfg *motion_det_config) +{ + struct scheduler_msg msg; + struct sme_motion_det_cfg *motion_det_cfg; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + motion_det_cfg = + qdf_mem_malloc(sizeof(*motion_det_cfg)); + if (!motion_det_cfg) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + *motion_det_cfg = *motion_det_config; + + qdf_mem_set(&msg, sizeof(msg), 0); + msg.type = WMA_SET_MOTION_DET_CONFIG; + msg.bodyptr = motion_det_cfg; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + qdf_mem_free(motion_det_cfg); + qdf_status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return qdf_status; +} + +/** + * sme_motion_det_enable - Post motion detection start/stop msg to scheduler + * @mac_handle: mac handle + * @motion_det_enable: motion detection start/stop + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_motion_det_enable(mac_handle_t mac_handle, + struct sme_motion_det_en *motion_det_enable) +{ + struct scheduler_msg msg; + struct sme_motion_det_en *motion_det_en; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + motion_det_en = qdf_mem_malloc(sizeof(*motion_det_en)); + if (!motion_det_en) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + *motion_det_en = *motion_det_enable; + + qdf_mem_set(&msg, sizeof(msg), 0); + msg.type = WMA_SET_MOTION_DET_ENABLE; + msg.bodyptr = motion_det_en; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + qdf_mem_free(motion_det_en); + qdf_status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return qdf_status; +} + +/** + * sme_motion_det_base_line_config - Post md baselining cfg msg to scheduler + * @mac_handle: mac handle + * @motion_det_base_line_config: motion detection baselining configuration + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_motion_det_base_line_config( + mac_handle_t mac_handle, + struct sme_motion_det_base_line_cfg *motion_det_base_line_config) +{ + struct scheduler_msg msg; + struct sme_motion_det_base_line_cfg *motion_det_base_line_cfg; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + motion_det_base_line_cfg = + qdf_mem_malloc(sizeof(*motion_det_base_line_cfg)); + + if (!motion_det_base_line_cfg) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + *motion_det_base_line_cfg = *motion_det_base_line_config; + + qdf_mem_set(&msg, sizeof(msg), 0); + msg.type = WMA_SET_MOTION_DET_BASE_LINE_CONFIG; + msg.bodyptr = motion_det_base_line_cfg; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + qdf_mem_free(motion_det_base_line_cfg); + qdf_status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return qdf_status; +} + +/** + * sme_motion_det_base_line_enable - Post md baselining enable msg to scheduler + * @mac_handle: mac handle + * @motion_det_base_line_enable: motion detection baselining start/stop + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS sme_motion_det_base_line_enable( + mac_handle_t mac_handle, + struct sme_motion_det_base_line_en *motion_det_base_line_enable) +{ + struct scheduler_msg msg; + struct sme_motion_det_base_line_en *motion_det_base_line_en; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + motion_det_base_line_en = + qdf_mem_malloc(sizeof(*motion_det_base_line_en)); + + if (!motion_det_base_line_en) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + *motion_det_base_line_en = *motion_det_base_line_enable; + + qdf_mem_set(&msg, sizeof(msg), 0); + msg.type = WMA_SET_MOTION_DET_BASE_LINE_ENABLE; + msg.bodyptr = motion_det_base_line_en; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + qdf_mem_free(motion_det_base_line_en); + qdf_status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return qdf_status; +} +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +#ifdef FW_THERMAL_THROTTLE_SUPPORT + +/** + * sme_set_thermal_throttle_cfg() - SME API to set the thermal throttle + * configuration parameters + * @mac_handle: Opaque handle to the global MAC context + * @enable: Enable Throttle + * @dc: duty cycle in msecs + * @dc_off_percent: duty cycle off percentage + * @prio: Disables the transmit queues in fw that have lower priority + * than value defined by prio + * @target_temp: Target temperature + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_thermal_throttle_cfg(mac_handle_t mac_handle, bool enable, + uint32_t dc, uint32_t dc_off_percent, + uint32_t prio, uint32_t target_temp) +{ + struct scheduler_msg msg; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct thermal_mitigation_params *therm_cfg_params; + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == qdf_status) { + therm_cfg_params = qdf_mem_malloc(sizeof(*therm_cfg_params)); + if (!therm_cfg_params) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + therm_cfg_params->enable = enable; + therm_cfg_params->dc = dc; + therm_cfg_params->levelconf[0].dcoffpercent = dc_off_percent; + therm_cfg_params->levelconf[0].priority = prio; + therm_cfg_params->levelconf[0].tmplwm = target_temp; + therm_cfg_params->num_thermal_conf = 1; + + qdf_mem_set(&msg, sizeof(msg), 0); + msg.type = WMA_SET_THERMAL_THROTTLE_CFG; + msg.reserved = 0; + msg.bodyptr = therm_cfg_params; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + sme_err("failed to schedule throttle config req %d", + qdf_status); + qdf_mem_free(therm_cfg_params); + qdf_status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return qdf_status; +} + +/** + * sme_set_thermal_mgmt() - SME API to set the thermal management params + * @mac_handle: Opaque handle to the global MAC context + * @lower_thresh_deg: Lower threshold value of Temperature + * @higher_thresh_deg: Higher threshold value of Temperature + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_set_thermal_mgmt(mac_handle_t mac_handle, + uint16_t lower_thresh_deg, + uint16_t higher_thresh_deg) +{ + struct scheduler_msg msg; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + t_thermal_cmd_params *therm_mgmt_cmd; + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_STATUS_SUCCESS == qdf_status) { + therm_mgmt_cmd = qdf_mem_malloc(sizeof(*therm_mgmt_cmd)); + if (!therm_mgmt_cmd) { + sme_release_global_lock(&mac->sme); + return QDF_STATUS_E_NOMEM; + } + + therm_mgmt_cmd->minTemp = lower_thresh_deg; + therm_mgmt_cmd->maxTemp = higher_thresh_deg; + therm_mgmt_cmd->thermalEnable = 1; + + qdf_mem_set(&msg, sizeof(msg), 0); + msg.type = WMA_SET_THERMAL_MGMT; + msg.reserved = 0; + msg.bodyptr = therm_mgmt_cmd; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + qdf_mem_free(therm_mgmt_cmd); + qdf_status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac->sme); + } + return qdf_status; +} +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + +QDF_STATUS sme_update_hidden_ssid_status_cb(mac_handle_t mac_handle, + hidden_ssid_cb cb) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.hidden_ssid_cb = cb; + sme_release_global_lock(&mac->sme); + } + + return status; +} + +QDF_STATUS sme_update_owe_info(struct mac_context *mac, + struct assoc_ind *assoc_ind) +{ + QDF_STATUS status; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = csr_update_owe_info(mac, assoc_ind); + sme_release_global_lock(&mac->sme); + } + + return status; +} + +#ifdef WLAN_MWS_INFO_DEBUGFS +QDF_STATUS +sme_get_mws_coex_info(mac_handle_t mac_handle, uint32_t vdev_id, + uint32_t cmd_id, void (*callback_fn)(void *coex_info_data, + void *context, + wmi_mws_coex_cmd_id + cmd_id), + void *context) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg msg = {0}; + struct sir_get_mws_coex_info *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + sme_err("Failed allocate memory for MWS coex info req"); + return QDF_STATUS_E_NOMEM; + } + + req->vdev_id = vdev_id; + req->cmd_id = cmd_id; + mac->sme.mws_coex_info_state_resp_callback = callback_fn; + mac->sme.mws_coex_info_ctx = context; + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + msg.bodyptr = req; + msg.type = WMA_GET_MWS_COEX_INFO_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg); + sme_release_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("post MWS coex info req failed"); + status = QDF_STATUS_E_FAILURE; + qdf_mem_free(req); + } + } else { + sme_err("sme_acquire_global_lock failed"); + qdf_mem_free(req); + } + + return status; +} +#endif /* WLAN_MWS_INFO_DEBUGFS */ + +#ifdef WLAN_BCN_RECV_FEATURE +/** + * sme_scan_event_handler() - Scan complete event handler + * @vdev: vdev obj manager + * @event: scan event + * @arg: arg of scan event + * + * This function is getting called after Host receive scan start + * + * Return: None + */ +static void sme_scan_event_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, + void *arg) +{ + struct mac_context *mac = arg; + uint8_t vdev_id; + + if (!mac) { + sme_err("Invalid mac context"); + return; + } + + if (!mac->sme.beacon_pause_cb) + return; + + if (event->type != SCAN_EVENT_TYPE_STARTED) + return; + + for (vdev_id = 0 ; vdev_id < WLAN_MAX_VDEVS ; vdev_id++) { + if (CSR_IS_SESSION_VALID(mac, vdev_id) && + sme_is_beacon_report_started(MAC_HANDLE(mac), vdev_id)) { + sme_debug("Send pause ind for vdev_id : %d", vdev_id); + mac->sme.beacon_pause_cb(mac->hdd_handle, vdev_id, + event->type, false); + } + } +} + +QDF_STATUS sme_register_bcn_recv_pause_ind_cb(mac_handle_t mac_handle, + beacon_pause_cb cb) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!mac) { + sme_err("Invalid mac context"); + return QDF_STATUS_E_NOMEM; + } + + /* scan event de-registration */ + if (!cb) { + ucfg_scan_unregister_event_handler(mac->pdev, + sme_scan_event_handler, mac); + return QDF_STATUS_SUCCESS; + } + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.beacon_pause_cb = cb; + sme_release_global_lock(&mac->sme); + } + + /* scan event registration */ + status = ucfg_scan_register_event_handler(mac->pdev, + sme_scan_event_handler, mac); + if (QDF_IS_STATUS_ERROR(status)) + sme_err("scan event register failed "); + + return status; +} +#endif + +QDF_STATUS sme_set_disconnect_ies(mac_handle_t mac_handle, uint8_t vdev_id, + uint8_t *ie_data, uint16_t ie_len) +{ + struct mac_context *mac_ctx; + struct wlan_objmgr_vdev *vdev; + struct wlan_ies ie; + + if (!ie_data || !ie_len) { + sme_debug("Got NULL disconnect IEs"); + return QDF_STATUS_E_INVAL; + } + + mac_ctx = MAC_CONTEXT(mac_handle); + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Got NULL vdev obj, returning"); + return QDF_STATUS_E_FAILURE; + } + + ie.data = ie_data; + ie.len = ie_len; + + mlme_set_self_disconnect_ies(vdev, &ie); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return QDF_STATUS_SUCCESS; +} + +void sme_chan_to_freq_list( + struct wlan_objmgr_pdev *pdev, + uint32_t *freq_list, + const uint8_t *chan_list, + uint32_t chan_list_len) +{ + uint32_t count; + + for (count = 0; count < chan_list_len; count++) + freq_list[count] = + wlan_reg_chan_to_freq(pdev, (uint32_t)chan_list[count]); +} + +static QDF_STATUS sme_enable_roaming(struct mac_context *mac, uint32_t vdev_id, + bool enable) +{ + struct csr_roam_session *session = CSR_GET_SESSION(mac, vdev_id); + QDF_STATUS status; + uint8_t reason = REASON_SUPPLICANT_DE_INIT_ROAMING; + + if (!session) { + sme_err("Roam session is NULL for vdev %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + if (enable) + reason = REASON_SUPPLICANT_INIT_ROAMING; + + csr_post_roam_state_change(mac, vdev_id, + enable ? ROAM_RSO_STARTED : ROAM_DEINIT, + reason); + + sme_release_global_lock(&mac->sme); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_set_roam_triggers(mac_handle_t mac_handle, + struct roam_triggers *triggers) +{ + QDF_STATUS status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct scheduler_msg message = {0}; + struct roam_triggers *roam_trigger_data; + + mlme_set_roam_trigger_bitmap(mac->psoc, triggers->vdev_id, + triggers->trigger_bitmap); + if (!triggers->trigger_bitmap) + status = sme_enable_roaming(mac, triggers->vdev_id, + false); + else + status = sme_enable_roaming(mac, triggers->vdev_id, + true); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + /* per contract must make a copy of the params when messaging */ + roam_trigger_data = qdf_mem_malloc(sizeof(*roam_trigger_data)); + if (!roam_trigger_data) + return QDF_STATUS_E_NOMEM; + *roam_trigger_data = *triggers; + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(roam_trigger_data); + return status; + } + + /* Serialize the req through MC thread */ + message.bodyptr = roam_trigger_data; + message.type = SIR_HAL_SET_ROAM_TRIGGERS; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &message); + sme_release_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failed to post ROAM_TRIGGERS msg"); + qdf_mem_free(roam_trigger_data); + } + + return status; +} + +QDF_STATUS sme_set_roam_config_enable(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t roam_control_enable) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + tCsrNeighborRoamControlInfo *neighbor_roam_info; + tCsrNeighborRoamCfgParams *cfg_params; + QDF_STATUS status; + + if (!mac->mlme_cfg->lfr.roam_scan_offload_enabled) + return QDF_STATUS_E_INVAL; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to acquire sme lock; status: %d", status); + return status; + } + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + + neighbor_roam_info->roam_control_enable = !!roam_control_enable; + if (roam_control_enable) { + cfg_params = &neighbor_roam_info->cfgParams; + cfg_params->roam_scan_period_after_inactivity = 0; + cfg_params->roam_inactive_data_packet_count = 0; + cfg_params->roam_scan_inactivity_time = 0; + + csr_roam_update_cfg(mac, vdev_id, + REASON_ROAM_CONTROL_CONFIG_ENABLED); + } + sme_release_global_lock(&mac->sme); + + return status; +} + +QDF_STATUS sme_get_roam_config_status(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t *config_status) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to acquire sme lock; status: %d", status); + return status; + } + *config_status = + mac->roam.neighborRoamInfo[vdev_id].roam_control_enable; + sme_release_global_lock(&mac->sme); + + return status; +} + +uint16_t sme_get_full_roam_scan_period_global(mac_handle_t mac_handle) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + return mac->mlme_cfg->lfr.roam_full_scan_period; +} + +QDF_STATUS +sme_get_full_roam_scan_period(mac_handle_t mac_handle, uint8_t vdev_id, + uint32_t *full_roam_scan_period) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status; + tpCsrNeighborRoamControlInfo neighbor_roam_info; + + if (vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid vdev_id: %d", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to acquire sme lock; status: %d", status); + return status; + } + neighbor_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + *full_roam_scan_period = + neighbor_roam_info->cfgParams.full_roam_scan_period; + sme_release_global_lock(&mac->sme); + + return status; +} + +QDF_STATUS sme_check_for_duplicate_session(mac_handle_t mac_handle, + uint8_t *peer_addr) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + bool peer_exist = false; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + if (!soc) { + sme_err("Failed to get soc handle"); + return QDF_STATUS_E_INVAL; + } + + if (QDF_STATUS_SUCCESS != sme_acquire_global_lock(&mac_ctx->sme)) + return status; + + peer_exist = cdp_find_peer_exist(soc, OL_TXRX_PDEV_ID, peer_addr); + if (peer_exist) { + sme_err("Peer exists with same MAC"); + status = QDF_STATUS_E_EXISTS; + } else { + status = QDF_STATUS_SUCCESS; + } + sme_release_global_lock(&mac_ctx->sme); + + return status; +} + +#ifdef FEATURE_ANI_LEVEL_REQUEST +QDF_STATUS sme_get_ani_level(mac_handle_t mac_handle, uint32_t *freqs, + uint8_t num_freqs, void (*callback)( + struct wmi_host_ani_level_event *ani, uint8_t num, + void *context), void *context) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + void *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + sme_err("wma handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mac->ani_params.ani_level_cb = callback; + mac->ani_params.context = context; + + status = wma_send_ani_level_request(wma_handle, freqs, num_freqs); + return status; +} +#endif /* FEATURE_ANI_LEVEL_REQUEST */ + +QDF_STATUS sme_get_prev_connected_bss_ies(mac_handle_t mac_handle, + uint8_t vdev_id, + uint8_t **ies, uint32_t *ie_len) +{ + struct csr_roam_session *session; + struct mac_context *mac; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t len; + uint8_t *beacon_ie; + + if (!mac_handle) { + sme_err("mac_handle is not valid"); + return QDF_STATUS_E_INVAL; + } + mac = MAC_CONTEXT(mac_handle); + session = CSR_GET_SESSION(mac, vdev_id); + if (!session) { + sme_err("session not found"); + return QDF_STATUS_E_INVAL; + } + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to acquire sme lock; status: %d", status); + return status; + } + + len = session->prev_assoc_ap_info.nBeaconLength; + if (!len) { + sme_debug("No IEs to return"); + status = QDF_STATUS_E_INVAL; + goto end; + } + beacon_ie = qdf_mem_malloc(len); + if (!beacon_ie) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + qdf_mem_copy(beacon_ie, session->prev_assoc_ap_info.pbFrames, len); + + *ie_len = len; + *ies = beacon_ie; +end: + sme_release_global_lock(&mac->sme); + return status; +} + +#ifdef FEATURE_MONITOR_MODE_SUPPORT + +QDF_STATUS sme_set_monitor_mode_cb(mac_handle_t mac_handle, + void (*monitor_mode_cb)(uint8_t vdev_id)) +{ + QDF_STATUS qdf_status; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + qdf_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + sme_err("Failed to acquire sme lock; status: %d", qdf_status); + return qdf_status; + } + mac->sme.monitor_mode_cb = monitor_mode_cb; + sme_release_global_lock(&mac->sme); + + return qdf_status; +} + +QDF_STATUS sme_process_monitor_mode_vdev_up_evt(uint8_t vdev_id) +{ + mac_handle_t mac_handle; + struct mac_context *mac; + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + if (!mac_handle) { + sme_err("mac_handle is not valid"); + return QDF_STATUS_E_INVAL; + } + + mac = MAC_CONTEXT(mac_handle); + + if (mac->sme.monitor_mode_cb) + mac->sme.monitor_mode_cb(vdev_id); + else { + sme_warn_rl("monitor_mode_cb is not registered"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/sme/src/common/sme_ft_api.c b/drivers/staging/qcacld-3.0/core/sme/src/common/sme_ft_api.c new file mode 100644 index 0000000000000000000000000000000000000000..63c353a71e5614e2250bbd5c1f22ce076cf93843 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/common/sme_ft_api.c @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +/* Initialize the FT context. */ +void sme_ft_open(mac_handle_t mac_handle, uint32_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (pSession) { + /* Clean up the context */ + qdf_mem_zero(&pSession->ftSmeContext, sizeof(tftSMEContext)); + + pSession->ftSmeContext.pUsrCtx = + qdf_mem_malloc(sizeof(tFTRoamCallbackUsrCtx)); + if (!pSession->ftSmeContext.pUsrCtx) + return; + + pSession->ftSmeContext.pUsrCtx->mac = mac; + pSession->ftSmeContext.pUsrCtx->sessionId = sessionId; + + status = + qdf_mc_timer_init(&pSession->ftSmeContext. + preAuthReassocIntvlTimer, + QDF_TIMER_TYPE_SW, + sme_preauth_reassoc_intvl_timer_callback, + (void *)pSession->ftSmeContext.pUsrCtx); + + if (QDF_STATUS_SUCCESS != status) { + sme_err("Preauth Reassoc interval Timer allocation failed"); + qdf_mem_free(pSession->ftSmeContext.pUsrCtx); + pSession->ftSmeContext.pUsrCtx = NULL; + return; + } + } +} + +/* Cleanup the SME FT Global context. */ +void sme_ft_close(mac_handle_t mac_handle, uint32_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = NULL; + + /* Clear the FT Context */ + sme_ft_reset(mac_handle, sessionId); + + pSession = CSR_GET_SESSION(mac, sessionId); + if (pSession) { + /* check if the timer is running */ + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&pSession->ftSmeContext. + preAuthReassocIntvlTimer)) { + qdf_mc_timer_stop(&pSession->ftSmeContext. + preAuthReassocIntvlTimer); + } + + qdf_mc_timer_destroy(&pSession->ftSmeContext. + preAuthReassocIntvlTimer); + + if (pSession->ftSmeContext.pUsrCtx) { + qdf_mem_free(pSession->ftSmeContext.pUsrCtx); + pSession->ftSmeContext.pUsrCtx = NULL; + } + } +} + +void sme_set_ft_pre_auth_state(mac_handle_t mac_handle, uint32_t sessionId, + bool state) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (pSession) + pSession->ftSmeContext.setFTPreAuthState = state; +} + +bool sme_get_ft_pre_auth_state(mac_handle_t mac_handle, uint32_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (pSession) + return pSession->ftSmeContext.setFTPreAuthState; + + return false; +} + +/** + * sme_set_ft_ies() - to set FT IEs + * @mac_handle: opaque handle to the global MAC context + * @session_id: sme session id + * @ft_ies: pointer to FT IEs + * @ft_ies_length: length of FT IEs + * + * Each time the supplicant sends down the FT IEs to the driver. This function + * is called in SME. This function packages and sends the FT IEs to PE. + * + * Return: none + */ +void sme_set_ft_ies(mac_handle_t mac_handle, uint32_t session_id, + const uint8_t *ft_ies, uint16_t ft_ies_length) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!session || !ft_ies) { + sme_err("ft ies or session is NULL"); + return; + } + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (!(QDF_IS_STATUS_SUCCESS(status))) + return; + + sme_debug("FT IEs Req is received in state %d", + session->ftSmeContext.FTState); + + /* Global Station FT State */ + switch (session->ftSmeContext.FTState) { + case eFT_START_READY: + case eFT_AUTH_REQ_READY: + sme_debug("ft_ies_length: %d", ft_ies_length); + if ((session->ftSmeContext.auth_ft_ies) && + (session->ftSmeContext.auth_ft_ies_length)) { + /* Free the one we recvd last from supplicant */ + qdf_mem_free(session->ftSmeContext.auth_ft_ies); + session->ftSmeContext.auth_ft_ies_length = 0; + session->ftSmeContext.auth_ft_ies = NULL; + } + ft_ies_length = QDF_MIN(ft_ies_length, MAX_FTIE_SIZE); + /* Save the FT IEs */ + session->ftSmeContext.auth_ft_ies = + qdf_mem_malloc(ft_ies_length); + if (!session->ftSmeContext.auth_ft_ies) { + sme_release_global_lock(&mac_ctx->sme); + return; + } + session->ftSmeContext.auth_ft_ies_length = ft_ies_length; + qdf_mem_copy((uint8_t *)session->ftSmeContext.auth_ft_ies, + ft_ies, ft_ies_length); + session->ftSmeContext.FTState = eFT_AUTH_REQ_READY; + break; + + case eFT_AUTH_COMPLETE: + /* + * We will need to re-start preauth. If we received FT + * IEs in eFT_PRE_AUTH_DONE state, it implies there was + * a rekey in our pre-auth state. Hence this implies we + * need Pre-auth again. OK now inform SME we have no + * pre-auth list. Delete the pre-auth node locally. Set + * your self back to restart pre-auth + */ + sme_debug("Preauth done & rcving AUTHREQ in state %d", + session->ftSmeContext.FTState); + sme_debug("Unhandled reception of FT IES in state %d", + session->ftSmeContext.FTState); + break; + + case eFT_REASSOC_REQ_WAIT: + /* + * We are done with pre-auth, hence now waiting for + * reassoc req. This is the new FT Roaming in place At + * this juncture we'r ready to start sending Reassoc req + */ + + ft_ies_length = QDF_MIN(ft_ies_length, MAX_FTIE_SIZE); + + sme_debug("New Reassoc Req: %pK in state %d", + ft_ies, session->ftSmeContext.FTState); + if ((session->ftSmeContext.reassoc_ft_ies) && + (session->ftSmeContext.reassoc_ft_ies_length)) { + /* Free the one we recvd last from supplicant */ + qdf_mem_free(session->ftSmeContext.reassoc_ft_ies); + session->ftSmeContext.reassoc_ft_ies_length = 0; + } + /* Save the FT IEs */ + session->ftSmeContext.reassoc_ft_ies = + qdf_mem_malloc(ft_ies_length); + if (!session->ftSmeContext.reassoc_ft_ies) { + sme_release_global_lock(&mac_ctx->sme); + return; + } + session->ftSmeContext.reassoc_ft_ies_length = + ft_ies_length; + qdf_mem_copy((uint8_t *)session->ftSmeContext.reassoc_ft_ies, + ft_ies, ft_ies_length); + + session->ftSmeContext.FTState = eFT_SET_KEY_WAIT; + sme_debug("ft_ies_length: %d state: %d", ft_ies_length, + session->ftSmeContext.FTState); + + break; + + default: + sme_warn("Unhandled state: %d", session->ftSmeContext.FTState); + break; + } + sme_release_global_lock(&mac_ctx->sme); +} + +QDF_STATUS sme_check_ft_status(mac_handle_t mac_handle, uint32_t session_id) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session = CSR_GET_SESSION(mac, session_id); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!session) { + sme_err("pSession is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = sme_acquire_global_lock(&mac->sme); + if (!(QDF_IS_STATUS_SUCCESS(status))) + return QDF_STATUS_E_FAILURE; + + sme_debug("FT update key is received in state %d", + session->ftSmeContext.FTState); + + /* Global Station FT State */ + switch (session->ftSmeContext.FTState) { + case eFT_SET_KEY_WAIT: + if (sme_get_ft_pre_auth_state(mac_handle, session_id) == true) { + sme_set_ft_pre_auth_state(mac_handle, session_id, + false); + session->ftSmeContext.FTState = eFT_START_READY; + sme_debug("state changed to %d status %d", + session->ftSmeContext.FTState, status); + sme_release_global_lock(&mac->sme); + return QDF_STATUS_SUCCESS; + } + default: + sme_debug("Unhandled state:%d", session->ftSmeContext.FTState); + status = QDF_STATUS_E_FAILURE; + break; + } + sme_release_global_lock(&mac->sme); + + return status; +} + +/* + * HDD Interface to SME. SME now sends the Auth 2 and RIC IEs up to the + * supplicant. The supplicant will then proceed to send down the + * Reassoc Req. + */ +void sme_get_ft_pre_auth_response(mac_handle_t mac_handle, uint32_t sessionId, + uint8_t *ft_ies, uint32_t ft_ies_ip_len, + uint16_t *ft_ies_length) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!pSession) { + sme_err("pSession is NULL"); + return; + } + + *ft_ies_length = 0; + + status = sme_acquire_global_lock(&mac->sme); + if (!(QDF_IS_STATUS_SUCCESS(status))) + return; + + /* All or nothing - proceed only if both BSSID and FT IE fit */ + if ((QDF_MAC_ADDR_SIZE + + pSession->ftSmeContext.psavedFTPreAuthRsp->ft_ies_length) > + ft_ies_ip_len) { + sme_release_global_lock(&mac->sme); + return; + } + /* hdd needs to pack the bssid also along with the */ + /* auth response to supplicant */ + qdf_mem_copy(ft_ies, pSession->ftSmeContext.preAuthbssId, + QDF_MAC_ADDR_SIZE); + + /* Copy the auth resp FTIEs */ + qdf_mem_copy(&(ft_ies[QDF_MAC_ADDR_SIZE]), + pSession->ftSmeContext.psavedFTPreAuthRsp->ft_ies, + pSession->ftSmeContext.psavedFTPreAuthRsp->ft_ies_length); + + *ft_ies_length = QDF_MAC_ADDR_SIZE + + pSession->ftSmeContext.psavedFTPreAuthRsp->ft_ies_length; + + pSession->ftSmeContext.FTState = eFT_REASSOC_REQ_WAIT; + + sme_debug("Filled auth resp: %d", *ft_ies_length); + sme_release_global_lock(&mac->sme); +} + +/* + * SME now sends the RIC IEs up to the supplicant. + * The supplicant will then proceed to send down the + * Reassoc Req. + */ +void sme_get_rici_es(mac_handle_t mac_handle, uint32_t sessionId, + uint8_t *ric_ies, + uint32_t ric_ies_ip_len, uint32_t *ric_ies_length) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!pSession) { + sme_err("pSession is NULL"); + return; + } + + *ric_ies_length = 0; + + status = sme_acquire_global_lock(&mac->sme); + if (!(QDF_IS_STATUS_SUCCESS(status))) + return; + + /* All or nothing */ + if (pSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length > + ric_ies_ip_len) { + sme_release_global_lock(&mac->sme); + return; + } + + qdf_mem_copy(ric_ies, + pSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies, + pSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length); + + *ric_ies_length = + pSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length; + + sme_debug("Filled ric ies: %d", *ric_ies_length); + + sme_release_global_lock(&mac->sme); +} + +/* + * Timer callback for the timer that is started between the preauth completion + * and reassoc request to the PE. In this interval, it is expected that the + * pre-auth response and RIC IEs are passed up to the WPA supplicant and + * received back the necessary FTIEs required to be sent in the reassoc request + */ +void sme_preauth_reassoc_intvl_timer_callback(void *context) +{ + tFTRoamCallbackUsrCtx *pUsrCtx = (tFTRoamCallbackUsrCtx *) context; + + if (pUsrCtx) + csr_neighbor_roam_request_handoff(pUsrCtx->mac, + pUsrCtx->sessionId); +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +#ifdef FEATURE_WLAN_ESE +static void sme_reset_esecckm_info(struct csr_roam_session *session) +{ + qdf_mem_zero(&session->eseCckmInfo, sizeof(session->eseCckmInfo)); +} +#else +static void sme_reset_esecckm_info(struct csr_roam_session *session) +{ +} +#endif +void sme_reset_key(mac_handle_t mac_handle, uint32_t vdev_id) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session = NULL; + + if (!mac) { + sme_err("mac is NULL"); + return; + } + + session = CSR_GET_SESSION(mac, vdev_id); + if (!session) + return; + qdf_mem_zero(&session->psk_pmk, sizeof(session->psk_pmk)); + session->pmk_len = 0; + sme_reset_esecckm_info(session); +} +#endif +/* Reset the FT context. */ +void sme_ft_reset(mac_handle_t mac_handle, uint32_t sessionId) +{ + struct mac_context *mac = MAC_CONTEXT(mac_handle); + struct csr_roam_session *pSession = NULL; + + if (!mac) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("mac is NULL")); + return; + } + + pSession = CSR_GET_SESSION(mac, sessionId); + if (pSession) { + if (pSession->ftSmeContext.auth_ft_ies) { + qdf_mem_free(pSession->ftSmeContext.auth_ft_ies); + pSession->ftSmeContext.auth_ft_ies = NULL; + } + pSession->ftSmeContext.auth_ft_ies_length = 0; + + if (pSession->ftSmeContext.reassoc_ft_ies) { + qdf_mem_free(pSession->ftSmeContext.reassoc_ft_ies); + pSession->ftSmeContext.reassoc_ft_ies = NULL; + } + pSession->ftSmeContext.reassoc_ft_ies_length = 0; + + if (pSession->ftSmeContext.psavedFTPreAuthRsp) { + qdf_mem_free(pSession->ftSmeContext.psavedFTPreAuthRsp); + pSession->ftSmeContext.psavedFTPreAuthRsp = NULL; + } + pSession->ftSmeContext.setFTPreAuthState = false; + + qdf_mem_zero(pSession->ftSmeContext.preAuthbssId, + QDF_MAC_ADDR_SIZE); + pSession->ftSmeContext.FTState = eFT_START_READY; + } +} + +/* End of File */ diff --git a/drivers/staging/qcacld-3.0/core/sme/src/common/sme_power_save.c b/drivers/staging/qcacld-3.0/core/sme/src/common/sme_power_save.c new file mode 100644 index 0000000000000000000000000000000000000000..403b76d830351c159c350913efd773030c5e75c6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/common/sme_power_save.c @@ -0,0 +1,908 @@ +/* + * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "sme_power_save.h" +#include "sme_power_save_api.h" +#include +#include +#include +#include "sme_trace.h" +#include "qdf_mem.h" +#include "qdf_types.h" +#include "wma.h" +#include "wma_internal.h" +#include "wmm_apsd.h" +#include "csr_inside_api.h" + +/** + * sme_post_ps_msg_to_wma(): post message to WMA. + * @type: type + * @body: body pointer + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_post_ps_msg_to_wma(uint16_t type, void *body) +{ + struct scheduler_msg msg = {0}; + + msg.type = type; + msg.reserved = 0; + msg.bodyptr = body; + msg.bodyval = 0; + + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Posting message %d failed", + __func__, type); + qdf_mem_free(body); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** + * sme_ps_enable_uapsd_req_params(): enables UASPD req params + * @mac_ctx: global mac context + * @session_id: session id + * + * Return: QDF_STATUS + */ +static void sme_ps_fill_uapsd_req_params(struct mac_context *mac_ctx, + tUapsd_Params *uapsdParams, uint32_t session_id, + enum ps_state *ps_state) +{ + + uint8_t uapsd_delivery_mask = 0; + uint8_t uapsd_trigger_mask = 0; + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct ps_params *ps_param = &ps_global_info->ps_params[session_id]; + + uapsd_delivery_mask = + ps_param->uapsd_per_ac_bit_mask | + ps_param->uapsd_per_ac_delivery_enable_mask; + + uapsd_trigger_mask = + ps_param->uapsd_per_ac_bit_mask | + ps_param->uapsd_per_ac_trigger_enable_mask; + + uapsdParams->bkDeliveryEnabled = + LIM_UAPSD_GET(ACBK, uapsd_delivery_mask); + + uapsdParams->beDeliveryEnabled = + LIM_UAPSD_GET(ACBE, uapsd_delivery_mask); + + uapsdParams->viDeliveryEnabled = + LIM_UAPSD_GET(ACVI, uapsd_delivery_mask); + + uapsdParams->voDeliveryEnabled = + LIM_UAPSD_GET(ACVO, uapsd_delivery_mask); + + uapsdParams->bkTriggerEnabled = + LIM_UAPSD_GET(ACBK, uapsd_trigger_mask); + + uapsdParams->beTriggerEnabled = + LIM_UAPSD_GET(ACBE, uapsd_trigger_mask); + + uapsdParams->viTriggerEnabled = + LIM_UAPSD_GET(ACVI, uapsd_trigger_mask); + + uapsdParams->voTriggerEnabled = + LIM_UAPSD_GET(ACVO, uapsd_trigger_mask); + if (ps_param->ps_state != FULL_POWER_MODE) { + uapsdParams->enable_ps = true; + *ps_state = UAPSD_MODE; + } else { + uapsdParams->enable_ps = false; + *ps_state = FULL_POWER_MODE; + } +} + +static void sme_set_ps_state(struct mac_context *mac_ctx, + uint32_t session_id, enum ps_state ps_state) +{ + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct ps_params *ps_param = &ps_global_info->ps_params[session_id]; + + ps_param->ps_state = ps_state; +} + +static void sme_get_ps_state(struct mac_context *mac_ctx, + uint32_t session_id, enum ps_state *ps_state) +{ + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct ps_params *ps_param = &ps_global_info->ps_params[session_id]; + *ps_state = ps_param->ps_state; +} +/** + * sme_ps_enable_ps_req_params(): enables power save req params + * @mac_ctx: global mac context + * @session_id: session id + * + * Return: QDF_STATUS + */ +static QDF_STATUS +sme_ps_enable_ps_req_params(struct mac_context *mac_ctx, uint32_t vdev_id) +{ + struct sEnablePsParams *enable_ps_req_params; + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct ps_params *ps_param = &ps_global_info->ps_params[vdev_id]; + enum ps_state ps_state; + + enable_ps_req_params = qdf_mem_malloc(sizeof(*enable_ps_req_params)); + if (!enable_ps_req_params) + return QDF_STATUS_E_NOMEM; + + if (ps_param->uapsd_per_ac_bit_mask) { + enable_ps_req_params->psSetting = eSIR_ADDON_ENABLE_UAPSD; + sme_ps_fill_uapsd_req_params(mac_ctx, + &enable_ps_req_params->uapsdParams, + vdev_id, &ps_state); + ps_state = UAPSD_MODE; + enable_ps_req_params->uapsdParams.enable_ps = true; + } else { + enable_ps_req_params->psSetting = eSIR_ADDON_NOTHING; + ps_state = LEGACY_POWER_SAVE_MODE; + } + enable_ps_req_params->sessionid = vdev_id; + + wma_enable_sta_ps_mode(enable_ps_req_params); + + qdf_mem_free(enable_ps_req_params); + + sme_debug("Powersave Enable sent to FW"); + ps_param->ps_state = ps_state; + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_ps_disable_ps_req_params(): Disable power save req params + * @mac_ctx: global mac context + * @session_id: session id + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_ps_disable_ps_req_params(struct mac_context *mac_ctx, + uint32_t vdev_id) +{ + struct sDisablePsParams *disable_ps_req_params; + + disable_ps_req_params = qdf_mem_malloc(sizeof(*disable_ps_req_params)); + if (!disable_ps_req_params) + return QDF_STATUS_E_NOMEM; + + disable_ps_req_params->psSetting = eSIR_ADDON_NOTHING; + disable_ps_req_params->sessionid = vdev_id; + + wma_disable_sta_ps_mode(disable_ps_req_params); + qdf_mem_free(disable_ps_req_params); + + sme_debug("Powersave disable sent to FW"); + sme_set_ps_state(mac_ctx, vdev_id, FULL_POWER_MODE); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_ps_enable_uapsd_req_params(): enables UASPD req params + * @mac_ctx: global mac context + * @session_id: session id + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_ps_enable_uapsd_req_params(struct mac_context *mac_ctx, + uint32_t session_id) +{ + + struct sEnableUapsdParams *enable_uapsd_req_params; + QDF_STATUS status = QDF_STATUS_SUCCESS; + enum ps_state ps_state; + + enable_uapsd_req_params = + qdf_mem_malloc(sizeof(*enable_uapsd_req_params)); + if (!enable_uapsd_req_params) + return QDF_STATUS_E_NOMEM; + + sme_ps_fill_uapsd_req_params(mac_ctx, + &enable_uapsd_req_params->uapsdParams, + session_id, &ps_state); + enable_uapsd_req_params->sessionid = session_id; + + status = sme_post_ps_msg_to_wma(WMA_ENABLE_UAPSD_REQ, + enable_uapsd_req_params); + if (!QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_E_FAILURE; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Msg WMA_ENABLE_UAPSD_REQ Successfully sent to WMA")); + sme_set_ps_state(mac_ctx, session_id, ps_state); + return QDF_STATUS_SUCCESS; +} + +/** + * sme_ps_disable_uapsd_req_params(): disables UASPD req params + * @mac_ctx: global mac context + * @session_id: session id + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_ps_disable_uapsd_req_params(struct mac_context *mac_ctx, + uint32_t session_id) +{ + struct sDisableUapsdParams *disable_uapsd_req_params; + QDF_STATUS status = QDF_STATUS_SUCCESS; + enum ps_state ps_state; + + sme_get_ps_state(mac_ctx, session_id, &ps_state); + if (ps_state != UAPSD_MODE) { + sme_err("UAPSD is already disabled"); + return QDF_STATUS_SUCCESS; + } + disable_uapsd_req_params = + qdf_mem_malloc(sizeof(*disable_uapsd_req_params)); + if (!disable_uapsd_req_params) + return QDF_STATUS_E_NOMEM; + + disable_uapsd_req_params->sessionid = session_id; + status = sme_post_ps_msg_to_wma(WMA_DISABLE_UAPSD_REQ, + disable_uapsd_req_params); + if (!QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_E_FAILURE; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Message WMA_DISABLE_UAPSD_REQ Successfully sent to WMA")); + sme_set_ps_state(mac_ctx, session_id, LEGACY_POWER_SAVE_MODE); + return QDF_STATUS_SUCCESS; +} + +/** + * sme_ps_process_command(): Sme process power save messages + * and pass messages to WMA. + * @mac_ctx: global mac context + * @session_id: session id + * sme_ps_cmd: power save message + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_ps_process_command(struct mac_context *mac_ctx, uint32_t session_id, + enum sme_ps_cmd command) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("Invalid Session_id: %d", session_id); + return QDF_STATUS_E_INVAL; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Power Save command %d"), command); + switch (command) { + case SME_PS_ENABLE: + status = sme_ps_enable_ps_req_params(mac_ctx, session_id); + break; + case SME_PS_DISABLE: + status = sme_ps_disable_ps_req_params(mac_ctx, session_id); + break; + case SME_PS_UAPSD_ENABLE: + status = sme_ps_enable_uapsd_req_params(mac_ctx, session_id); + break; + case SME_PS_UAPSD_DISABLE: + status = sme_ps_disable_uapsd_req_params(mac_ctx, session_id); + break; + default: + sme_err("Invalid command type: %d", command); + status = QDF_STATUS_E_FAILURE; + break; + } + if (status != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Not able to enter in PS, Command: %d"), command); + } + return status; +} + +/** + * sme_enable_sta_ps_check(): Checks if it is ok to enable power save or not. + * @mac_ctx: global mac context + * @session_id: session id + * @ command: sme_ps_cmd + * Pre Condition for enabling sta mode power save + * 1) Sta Mode Ps should be enabled in ini file. + * 2) Session should be in infra mode and in connected state. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_enable_sta_ps_check(struct mac_context *mac_ctx, + uint32_t session_id, enum sme_ps_cmd command) +{ + struct wlan_mlme_powersave *powersave_params; + + QDF_BUG(session_id < WLAN_MAX_VDEVS); + if (session_id >= WLAN_MAX_VDEVS) + return QDF_STATUS_E_INVAL; + + /* Check if Sta Ps is enabled. */ + powersave_params = &mac_ctx->mlme_cfg->ps_params; + if (!powersave_params->is_bmps_enabled) { + sme_debug("Cannot initiate PS. PS is disabled in ini"); + return QDF_STATUS_E_FAILURE; + } + + if (command == SME_PS_ENABLE && !mac_ctx->usr_cfg_ps_enable) { + sme_debug("Cannot initiate PS. PS is disabled by usr(ioctl)"); + return QDF_STATUS_E_FAILURE; + } + + /* Check whether the given session is Infra and in Connected State + * also if command is power save disable there is not need to check + * for connected state as firmware can handle this + */ + if (!csr_is_conn_state_connected_infra(mac_ctx, session_id)) { + sme_debug("STA not infra/connected state Session_id: %d", + session_id); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +void sme_save_usr_ps_cfg(mac_handle_t mac_handle, bool val) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + mac_ctx->usr_cfg_ps_enable = val; + sme_debug("usr_cfg_ps_enable %d", val); +} + +/** + * sme_ps_enable_disable(): function to enable/disable PS. + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * sme_ps_cmd: power save message + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_ps_enable_disable(mac_handle_t mac_handle, uint32_t session_id, + enum sme_ps_cmd command) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + status = sme_enable_sta_ps_check(mac_ctx, session_id, command); + if (status != QDF_STATUS_SUCCESS) { + /* + * In non associated state driver wont handle the power save + * But kernel expects return status success even + * in the disconnected state. + */ + if (!csr_is_conn_state_connected_infra(mac_ctx, session_id)) + status = QDF_STATUS_SUCCESS; + return status; + } + status = sme_ps_process_command(mac_ctx, session_id, command); + return status; +} + +QDF_STATUS sme_ps_timer_flush_sync(mac_handle_t mac_handle, uint8_t session_id) +{ + QDF_STATUS status; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct ps_params *ps_parm; + enum ps_state ps_state; + QDF_TIMER_STATE tstate; + struct sEnablePsParams *req; + + QDF_BUG(session_id < WLAN_MAX_VDEVS); + if (session_id >= WLAN_MAX_VDEVS) + return QDF_STATUS_E_INVAL; + + status = sme_enable_sta_ps_check(mac_ctx, session_id, SME_PS_ENABLE); + if (QDF_IS_STATUS_ERROR(status)) { + sme_debug("Power save not allowed for vdev id %d", session_id); + return QDF_STATUS_SUCCESS; + } + + ps_parm = &mac_ctx->sme.ps_global_info.ps_params[session_id]; + tstate = qdf_mc_timer_get_current_state(&ps_parm->auto_ps_enable_timer); + if (tstate != QDF_TIMER_STATE_RUNNING) + return QDF_STATUS_SUCCESS; + + sme_debug("flushing powersave enable for vdev %u", session_id); + + qdf_mc_timer_stop(&ps_parm->auto_ps_enable_timer); + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return QDF_STATUS_E_NOMEM; + + if (ps_parm->uapsd_per_ac_bit_mask) { + req->psSetting = eSIR_ADDON_ENABLE_UAPSD; + sme_ps_fill_uapsd_req_params(mac_ctx, &req->uapsdParams, + session_id, &ps_state); + ps_state = UAPSD_MODE; + req->uapsdParams.enable_ps = true; + } else { + req->psSetting = eSIR_ADDON_NOTHING; + ps_state = LEGACY_POWER_SAVE_MODE; + } + req->sessionid = session_id; + + wma_enable_sta_ps_mode(req); + qdf_mem_free(req); + + ps_parm->ps_state = ps_state; + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_ps_uapsd_enable(): function to enable UAPSD. + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_ps_uapsd_enable(mac_handle_t mac_handle, uint32_t session_id) +{ + + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + status = sme_enable_sta_ps_check(mac_ctx, session_id, + SME_PS_UAPSD_ENABLE); + if (status != QDF_STATUS_SUCCESS) + return status; + status = sme_ps_process_command(mac_ctx, session_id, + SME_PS_UAPSD_ENABLE); + if (status == QDF_STATUS_SUCCESS) + sme_offload_qos_process_into_uapsd_mode(mac_ctx, session_id); + + return status; +} + +/** + * sme_ps_uapsd_disable(): function to disable UAPSD. + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_ps_uapsd_disable(mac_handle_t mac_handle, uint32_t session_id) +{ + + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + status = sme_enable_sta_ps_check(mac_ctx, session_id, + SME_PS_UAPSD_DISABLE); + if (status != QDF_STATUS_SUCCESS) + return status; + status = sme_ps_process_command(mac_ctx, session_id, + SME_PS_UAPSD_DISABLE); + if (status == QDF_STATUS_SUCCESS) + sme_offload_qos_process_out_of_uapsd_mode(mac_ctx, session_id); + + return status; +} + +/** + * sme_set_tspec_uapsd_mask_per_session(): set tspec UAPSD mask per session + * @mac_ctx: global mac context + * @ts_info: tspec info. + * @session_id: session id + * + * Return: QDF_STATUS + */ +void sme_set_tspec_uapsd_mask_per_session(struct mac_context *mac_ctx, + struct mac_ts_info *ts_info, + uint8_t session_id) +{ + uint8_t user_prio = (uint8_t) ts_info->traffic.userPrio; + uint16_t direction = ts_info->traffic.direction; + uint8_t ac = upToAc(user_prio); + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct ps_params *ps_param = &ps_global_info->ps_params[session_id]; + + sme_err("Set UAPSD mask for AC: %d dir: %d action: %d", + ac, direction, ts_info->traffic.psb); + + /* Converting AC to appropriate Uapsd Bit Mask + * AC_BE(0) --> UAPSD_BITOFFSET_ACVO(3) + * AC_BK(1) --> UAPSD_BITOFFSET_ACVO(2) + * AC_VI(2) --> UAPSD_BITOFFSET_ACVO(1) + * AC_VO(3) --> UAPSD_BITOFFSET_ACVO(0) + */ + ac = ((~ac) & 0x3); + if (ts_info->traffic.psb) { + ps_param->uapsd_per_ac_bit_mask |= (1 << ac); + if (direction == SIR_MAC_DIRECTION_UPLINK) + ps_param->uapsd_per_ac_trigger_enable_mask |= + (1 << ac); + else if (direction == SIR_MAC_DIRECTION_DNLINK) + ps_param->uapsd_per_ac_delivery_enable_mask |= + (1 << ac); + else if (direction == SIR_MAC_DIRECTION_BIDIR) { + ps_param->uapsd_per_ac_trigger_enable_mask |= + (1 << ac); + ps_param->uapsd_per_ac_delivery_enable_mask |= + (1 << ac); + } + } else { + ps_param->uapsd_per_ac_bit_mask &= ~(1 << ac); + if (direction == SIR_MAC_DIRECTION_UPLINK) + ps_param->uapsd_per_ac_trigger_enable_mask &= + ~(1 << ac); + else if (direction == SIR_MAC_DIRECTION_DNLINK) + ps_param->uapsd_per_ac_delivery_enable_mask &= + ~(1 << ac); + else if (direction == SIR_MAC_DIRECTION_BIDIR) { + ps_param->uapsd_per_ac_trigger_enable_mask &= + ~(1 << ac); + ps_param->uapsd_per_ac_delivery_enable_mask &= + ~(1 << ac); + } + } + + /* + * ADDTS success, so AC is now admitted. We shall now use the default + * EDCA parameters as advertised by AP and send the updated EDCA params + * to HAL. + */ + if (direction == SIR_MAC_DIRECTION_UPLINK) { + ps_param->ac_admit_mask[SIR_MAC_DIRECTION_UPLINK] |= + (1 << ac); + } else if (direction == SIR_MAC_DIRECTION_DNLINK) { + ps_param->ac_admit_mask[SIR_MAC_DIRECTION_DNLINK] |= + (1 << ac); + } else if (direction == SIR_MAC_DIRECTION_BIDIR) { + ps_param->ac_admit_mask[SIR_MAC_DIRECTION_UPLINK] |= + (1 << ac); + ps_param->ac_admit_mask[SIR_MAC_DIRECTION_DNLINK] |= + (1 << ac); + } + + sme_debug("New ps_param->uapsd_per_ac_trigger_enable_mask: 0x%x", + ps_param->uapsd_per_ac_trigger_enable_mask); + sme_debug("New ps_param->uapsd_per_ac_delivery_enable_mask: 0x%x", + ps_param->uapsd_per_ac_delivery_enable_mask); + sme_debug("New ps_param->ac_admit_mask[SIR_MAC_DIRECTION_UPLINK]: 0x%x", + ps_param->ac_admit_mask[SIR_MAC_DIRECTION_UPLINK]); +} + +/** + * sme_ps_start_uapsd(): function to start UAPSD. + * @mac_handle: Opaque handle to the global MAC context + * @session_id: session id + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_ps_start_uapsd(mac_handle_t mac_handle, uint32_t session_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + status = sme_ps_uapsd_enable(mac_handle, session_id); + return status; +} + +/** + * sme_set_ps_host_offload(): Set the host offload feature. + * @mac_handle - The handle returned by mac_open. + * @request - Pointer to the offload request. + * + * Return QDF_STATUS + * QDF_STATUS_E_FAILURE Cannot set the offload. + * QDF_STATUS_SUCCESS Request accepted. + */ +QDF_STATUS sme_set_ps_host_offload(mac_handle_t mac_handle, + struct sir_host_offload_req *request, + uint8_t session_id) +{ + struct sir_host_offload_req *request_buf; + struct scheduler_msg msg = {0}; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: IP address = %d.%d.%d.%d", __func__, + request->params.hostIpv4Addr[0], + request->params.hostIpv4Addr[1], + request->params.hostIpv4Addr[2], + request->params.hostIpv4Addr[3]); + + if (!session) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: SESSION not Found", __func__); + return QDF_STATUS_E_FAILURE; + } + + request_buf = qdf_mem_malloc(sizeof(struct sir_host_offload_req)); + if (!request_buf) + return QDF_STATUS_E_NOMEM; + + qdf_copy_macaddr(&request->bssid, &session->connectedProfile.bssid); + + qdf_mem_copy(request_buf, request, sizeof(struct sir_host_offload_req)); + + msg.type = WMA_SET_HOST_OFFLOAD; + msg.reserved = 0; + msg.bodyptr = request_buf; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + session_id, msg.type)); + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Not able to post WMA_SET_HOST_OFFLOAD msg to WMA")); + qdf_mem_free(request_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_NS_OFFLOAD +/** + * sme_set_ps_ns_offload(): Set the host offload feature. + * @mac_handle - The handle returned by mac_open. + * @request - Pointer to the offload request. + * + * Return QDF_STATUS + * QDF_STATUS_E_FAILURE Cannot set the offload. + * QDF_STATUS_SUCCESS Request accepted. + */ +QDF_STATUS sme_set_ps_ns_offload(mac_handle_t mac_handle, + struct sir_host_offload_req *request, + uint8_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct sir_host_offload_req *request_buf; + struct scheduler_msg msg = {0}; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("Session not found"); + return QDF_STATUS_E_FAILURE; + } + + qdf_copy_macaddr(&request->bssid, &session->connectedProfile.bssid); + + request_buf = qdf_mem_malloc(sizeof(*request_buf)); + if (!request_buf) + return QDF_STATUS_E_NOMEM; + + *request_buf = *request; + + msg.type = WMA_SET_NS_OFFLOAD; + msg.reserved = 0; + msg.bodyptr = request_buf; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + session_id, msg.type)); + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Not able to post SIR_HAL_SET_HOST_OFFLOAD message to HAL"); + qdf_mem_free(request_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#endif /* WLAN_NS_OFFLOAD */ +/** + * sme_post_pe_message + * + * FUNCTION: + * Post a message to the pmm message queue + * + * LOGIC: + * + * ASSUMPTIONS: + * + * NOTE: + * + * @param msg pointer to message + * @return None + */ + +QDF_STATUS sme_post_pe_message(struct mac_context *mac_ctx, + struct scheduler_msg *msg) +{ + QDF_STATUS qdf_status; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, + msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + sme_err("scheduler_post_msg failed with status: %d", + qdf_status); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_ps_enable_auto_ps_timer(mac_handle_t mac_handle, + uint32_t session_id, uint32_t timeout) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct ps_params *ps_param = &ps_global_info->ps_params[session_id]; + QDF_STATUS qdf_status; + QDF_TIMER_STATE cur_state; + + if (!timeout && !mac_ctx->usr_cfg_ps_enable) { + sme_debug("auto_ps_timer called with timeout 0; ignore"); + return QDF_STATUS_SUCCESS; + } + if (!timeout) + timeout = AUTO_PS_ENTRY_TIMER_DEFAULT_VALUE; + + cur_state = + qdf_mc_timer_get_current_state(&ps_param->auto_ps_enable_timer); + if (cur_state == QDF_TIMER_STATE_STARTING || + cur_state == QDF_TIMER_STATE_RUNNING) { + sme_debug("auto_ps_timer is already started: %d", cur_state); + return QDF_STATUS_SUCCESS; + } + + sme_debug("Start auto_ps_timer for %d ms", timeout); + qdf_status = qdf_mc_timer_start(&ps_param->auto_ps_enable_timer, + timeout); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + if (QDF_STATUS_E_ALREADY == qdf_status) { + /* Consider this ok since the timer is already started*/ + sme_debug("auto_ps_timer is already started"); + } else { + sme_err("Cannot start auto_ps_timer"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_ps_disable_auto_ps_timer(mac_handle_t mac_handle, + uint32_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct ps_params *ps_param; + + if (!sme_is_session_id_valid(mac_handle, session_id)) + return QDF_STATUS_SUCCESS; + + ps_param = &ps_global_info->ps_params[session_id]; + /* + * Stop the auto ps entry timer if runnin + */ + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state( + &ps_param->auto_ps_enable_timer)) { + sme_debug("Stop auto_ps_enable_timer Timer for session ID: %d", + session_id); + qdf_mc_timer_stop(&ps_param->auto_ps_enable_timer); + } + return QDF_STATUS_SUCCESS; +} + + +QDF_STATUS sme_ps_open(mac_handle_t mac_handle) +{ + + uint32_t i; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + + mac_ctx->usr_cfg_ps_enable = true; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (QDF_STATUS_SUCCESS != sme_ps_open_per_session(mac_handle, + i)) { + sme_err("PMC Init Failed for session: %d", i); + return QDF_STATUS_E_FAILURE; + } + } + return QDF_STATUS_SUCCESS; +} + + +QDF_STATUS sme_ps_open_per_session(mac_handle_t mac_handle, uint32_t session_id) +{ + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct ps_params *ps_param = &ps_global_info->ps_params[session_id]; + + ps_param->session_id = session_id; + ps_param->mac_ctx = mac_ctx; + /* Allocate a timer to enable ps automatically */ + if (!QDF_IS_STATUS_SUCCESS(qdf_mc_timer_init( + &ps_param->auto_ps_enable_timer, + QDF_TIMER_TYPE_SW, + sme_auto_ps_entry_timer_expired, + ps_param))) { + sme_err("Cannot allocate timer for auto ps entry"); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; + +} + +void sme_auto_ps_entry_timer_expired(void *data) +{ + struct ps_params *ps_params = (struct ps_params *)data; + struct mac_context *mac_ctx; + uint32_t session_id; + QDF_STATUS status; + + if (!ps_params) { + sme_err("ps_params is NULL"); + return; + } + mac_ctx = (struct mac_context *)ps_params->mac_ctx; + if (!mac_ctx) { + sme_err("mac_ctx is NULL"); + return; + } + session_id = ps_params->session_id; + sme_debug("auto_ps_timer expired, enabling powersave"); + + status = sme_enable_sta_ps_check(mac_ctx, session_id, SME_PS_ENABLE); + if (QDF_STATUS_SUCCESS == status) + sme_ps_enable_disable(MAC_HANDLE(mac_ctx), session_id, + SME_PS_ENABLE); +} + +QDF_STATUS sme_ps_close(mac_handle_t mac_handle) +{ + uint32_t i; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) + sme_ps_close_per_session(mac_handle, i); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_ps_close_per_session(mac_handle_t mac_handle, + uint32_t session_id) +{ + + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct ps_params *ps_param = &ps_global_info->ps_params[session_id]; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + /* + * Stop the auto ps entry timer if running + */ + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state( + &ps_param->auto_ps_enable_timer)) + qdf_mc_timer_stop(&ps_param->auto_ps_enable_timer); + + qdf_status = + qdf_mc_timer_destroy(&ps_param->auto_ps_enable_timer); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + sme_err("Cannot deallocate suto PS timer"); + return qdf_status; +} diff --git a/drivers/staging/qcacld-3.0/core/sme/src/common/sme_trace.c b/drivers/staging/qcacld-3.0/core/sme/src/common/sme_trace.c new file mode 100644 index 0000000000000000000000000000000000000000..f09ad8a6d00ca3b06951d29ebfb4948a6ac0ac7b --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/common/sme_trace.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: smeTrace.c + * Implementation for trace related APIs + * + * Author Kiran Kumar Reddy CH L V + */ +#include "ani_global.h" /* for struct mac_context **/ +#include "mac_trace.h" +#include "sme_trace.h" +#include "sme_internal.h" +#ifndef SME_TRACE_RECORD +void sme_trace_init(struct mac_context *mac) +{ + +} +#endif +#ifdef SME_TRACE_RECORD + +static uint8_t *sme_trace_get_rx_msg_string(uint32_t code) +{ + switch (code) { + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_SCAN_REQ); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_SCAN_GET_RESULTS); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_CONNECT); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_SET_11DINFO); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_GET_SOFTAP_DOMAIN); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_SET_REGINFO); + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_MSG_UPDATE_CHANNEL_CONFIG); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_UPDATE_CONFIG); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_HDDREADYIND); + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_MSG_SCAN_FLUSH_RESULTS); + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_MSG_SCAN_FLUSH_P2PRESULTS); + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_GETFIRST); + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_GETNEXT); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_SCAN_RESULT_PURGE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_ROAM_REASSOC); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_ROAM_DISCONNECT); + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_ROAM_GET_CONNECTPROFILE); + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_ROAM_FREE_CONNECTPROFILE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_ROAM_SET_PMKIDCACHE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_ROAM_DEL_PMKIDCACHE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_ROAM_GET_PMKIDCACHE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_GET_CONFIGPARAM); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_GET_MODPROFFIELDS); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_CONFIG_PWRSAVE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_GET_CONFIG_PWRSAVE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_ENABLE_PWRSAVE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_DISABLE_PWRSAVE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SIGNAL_POWER_EVENT); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_START_AUTO_BMPSTIMER); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_STOP_AUTO_BMPSTIMER); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_IS_PWRSAVE_ENABLED); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_REQUEST_FULLPOWER); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_REQUEST_BMPS); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_DHCP_FLAG); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_REQUEST_STANDBY); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_WOWL_ADDBCAST_PATTERN); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_WOWL_DELBCAST_PATTERN); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_ENTER_WOWL); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_EXIT_WOWL); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_KEY); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_REMOVE_KEY); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_GET_CNTRYCODE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_CNTRYCODE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_CFGPRIVACY); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_NEIGHBOR_REPORTREQ); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_DBG_READREG); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_DBG_WRITEREG); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_DBG_READMEM); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_DBG_WRITEMEM); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_OPEN_SESSION); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CLOSE_SESSION); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_HOSTOFFLOAD); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_GTKOFFLOAD); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_GET_GTKOFFLOAD); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_ABORT_MACSCAN); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_REGISTER_MGMTFR); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_DEREGISTER_MGMTFR); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_REMAIN_ONCHAN); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SEND_ACTION); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CANCEL_REMAIN_ONCHAN); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CONFIG_RXPFIL); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CONFIG_SUSPENDIND); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CONFIG_RESUMEREQ); +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CONFIG_EXTWOW); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CONFIG_APP_TYPE1); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CONFIG_APP_TYPE2); +#endif + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_MAXTXPOW); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_TXPOW); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_TMLEVEL); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CAPS_EXCH); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_DISABLE_CAP); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_GET_DEFCCNV); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_GET_CURCC); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_RESET_PW5G); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_UPDATE_RP5G); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_ROAMIBAND); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_GET_ROAMIBAND); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_UPDATE_RSSIDIFF); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_UPDATE_IMMRSSIDIFF); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_UPDATE_FTENABLED); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_UPDATE_WESMODE); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_SET_SCANCTRL); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_UPDATE_P2P_IE); + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_UPDATE_ROAM_SCAN_N_PROBES); + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_UPDATE_ROAM_SCAN_HOME_AWAY_TIME); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_STORE_JOIN_REQ); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_CLEAR_JOIN_REQ); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_ISSUE_JOIN_REQ); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_MSG_DEAUTH_STA); +#ifdef FEATURE_WLAN_TDLS + CASE_RETURN_STRING + (TRACE_CODE_SME_RX_HDD_TDLS_LINK_ESTABLISH_PARAM); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_TDLS_CHAN_SWITCH_REQ); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_TDLS_SEND_MGMT_FRAME); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_TDLS_CHANGE_PEER_STA); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_TDLS_ADD_PEER_STA); + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_TDLS_DEL_PEER_STA); +#endif + CASE_RETURN_STRING(TRACE_CODE_SME_RX_HDD_PREF_NET_LIST); + default: + return "UNKNOWN"; + } +} + +static uint8_t *sme_trace_get_command_string(uint32_t command) +{ + switch (command) { + CASE_RETURN_STRING(eSmeNoCommand); + CASE_RETURN_STRING(eSmeCsrCommandMask); + CASE_RETURN_STRING(eSmeCommandRoam); + CASE_RETURN_STRING(eSmeCommandWmStatusChange); + CASE_RETURN_STRING(eSmeQosCommandMask); + CASE_RETURN_STRING(eSmeCommandAddTs); + CASE_RETURN_STRING(eSmeCommandDelTs); + CASE_RETURN_STRING(e_sme_command_set_hw_mode); + CASE_RETURN_STRING(e_sme_command_nss_update); + CASE_RETURN_STRING(e_sme_command_set_dual_mac_config); + CASE_RETURN_STRING(e_sme_command_set_antenna_mode); + default: + return "UNKNOWN"; + } +} + +static void sme_trace_dump(void *mac_ctx, tp_qdf_trace_record record, + uint16_t rec_index) +{ + switch (record->code) { + case TRACE_CODE_SME_COMMAND: + sme_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", + rec_index, record->qtime, record->time, + record->session, "SME COMMAND:", + sme_trace_get_command_string(record->data), + record->data); + break; + case TRACE_CODE_SME_TX_WMA_MSG: + sme_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", + rec_index, record->qtime, record->time, + record->session, "TX WMA Msg:", + mac_trace_get_wma_msg_string((uint16_t)record->data), + record->data); + break; + case TRACE_CODE_SME_RX_WMA_MSG: + sme_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", + rec_index, record->qtime, record->time, record->session, + "RX WMA Msg:", + mac_trace_get_sme_msg_string((uint16_t)record->data), + record->data); + break; + default: + sme_nofl_debug("%04d %012llu %s S%d %-14s %-30s(0x%x)", + rec_index, record->qtime, record->time, record->session, + "RX HDD MSG:", + sme_trace_get_rx_msg_string(record->code), + record->data); + break; + } +} + +void sme_trace_init(struct mac_context *mac) +{ + qdf_trace_register(QDF_MODULE_ID_SME, &sme_trace_dump); +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_roam.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_roam.c new file mode 100644 index 0000000000000000000000000000000000000000..f7164f231239443ff9d4eadc932f8d3d76813f43 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_roam.c @@ -0,0 +1,22936 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: csr_api_roam.c + * + * Implementation for the Common Roaming interfaces. + */ +#include "ani_global.h" /* for struct mac_context **/ +#include "wma_types.h" +#include "wma_if.h" /* for STA_INVALID_IDX. */ +#include "csr_inside_api.h" +#include "sme_trace.h" +#include "sme_qos_internal.h" +#include "sme_inside.h" +#include "host_diag_core_event.h" +#include "host_diag_core_log.h" +#include "csr_api.h" +#include "csr_internal.h" +#include "cds_reg_service.h" +#include "mac_trace.h" +#include "csr_neighbor_roam.h" +#include "cds_regdomain.h" +#include "cds_utils.h" +#include "sir_types.h" +#include "cfg_ucfg_api.h" +#include "sme_power_save_api.h" +#include "wma.h" +#include "wlan_policy_mgr_api.h" +#include "sme_nan_datapath.h" +#include "pld_common.h" +#include "wlan_reg_services_api.h" +#include "qdf_crypto.h" +#include +#include "wlan_objmgr_psoc_obj.h" +#include +#include +#include +#include +#include +#include +#include +#include "wlan_mlme_api.h" +#include "wlan_mlme_ucfg_api.h" +#include +#include "cfg_mlme.h" +#include "wlan_mlme_public_struct.h" +#include +#include "wlan_qct_sys.h" +#include "wlan_blm_api.h" +#include "wlan_policy_mgr_i.h" +#include "wlan_scan_utils_api.h" +#include "wlan_p2p_cfg_api.h" +#include "cfg_nan_api.h" +#include "nan_ucfg_api.h" + +#include + +#define RSN_AUTH_KEY_MGMT_SAE WLAN_RSN_SEL(WLAN_AKM_SAE) +#define MAX_PWR_FCC_CHAN_12 8 +#define MAX_PWR_FCC_CHAN_13 2 + +#define CSR_NUM_IBSS_START_CHAN_50 5 +#define CSR_NUM_IBSS_START_CHANNELS_24 3 +/* 70 seconds, for WPA, WPA2, CCKM */ +#define CSR_WAIT_FOR_KEY_TIMEOUT_PERIOD \ + (SIR_INSTALL_KEY_TIMEOUT_SEC * QDF_MC_TIMER_TO_SEC_UNIT) +/* 120 seconds, for WPS */ +#define CSR_WAIT_FOR_WPS_KEY_TIMEOUT_PERIOD (120 * QDF_MC_TIMER_TO_SEC_UNIT) + +#define CSR_MIN_GLOBAL_STAT_QUERY_PERIOD 500 /* ms */ +#define CSR_MIN_GLOBAL_STAT_QUERY_PERIOD_IN_BMPS 2000 /* ms */ + +#define CSR_SINGLE_PMK_OUI "\x00\x40\x96\x03" +#define CSR_SINGLE_PMK_OUI_SIZE 4 + +/* Flag to send/do not send disassoc frame over the air */ +#define CSR_DONT_SEND_DISASSOC_OVER_THE_AIR 1 +#define RSSI_HACK_BMPS (-40) +#define MAX_CB_VALUE_IN_INI (2) + +#define MAX_SOCIAL_CHANNELS 3 + +/* packet dump timer duration of 60 secs */ +#define PKT_DUMP_TIMER_DURATION 60 + +/* Choose the largest possible value that can be accommodated in 8 bit signed */ +/* variable. */ +#define SNR_HACK_BMPS (127) + +/* + * ROAMING_OFFLOAD_TIMER_START - Indicates the action to start the timer + * ROAMING_OFFLOAD_TIMER_STOP - Indicates the action to stop the timer + * CSR_ROAMING_OFFLOAD_TIMEOUT_PERIOD - Timeout value for roaming offload timer + */ +#define ROAMING_OFFLOAD_TIMER_START 1 +#define ROAMING_OFFLOAD_TIMER_STOP 2 +#define CSR_ROAMING_OFFLOAD_TIMEOUT_PERIOD (5 * QDF_MC_TIMER_TO_SEC_UNIT) + +/* + * Neighbor report offload needs to send 0xFFFFFFFF if a particular + * parameter is disabled from the ini + */ +#define NEIGHBOR_REPORT_PARAM_INVALID (0xFFFFFFFFU) + +/* + * To get 4 LSB of roam reason of roam_synch_data + * received from firmware + */ +#define ROAM_REASON_MASK 0x0F +/** + * csr_get_ielen_from_bss_description() - to get IE length + * from struct bss_description structure + * @pBssDescr: pBssDescr + * + * This function is called in various places to get IE length + * from struct bss_description structure + * + * @Return: total IE length + */ +static inline uint16_t +csr_get_ielen_from_bss_description(struct bss_description *pBssDescr) +{ + uint16_t ielen; + + if (!pBssDescr) + return 0; + + /* + * Length of BSS desription is without length of + * length itself and length of pointer + * that holds ieFields + * + * <------------sizeof(struct bss_description)--------------------> + * +--------+---------------------------------+---------------+ + * | length | other fields | pointer to IEs| + * +--------+---------------------------------+---------------+ + * ^ + * ieFields + */ + + ielen = (uint16_t)(pBssDescr->length + sizeof(pBssDescr->length) - + GET_FIELD_OFFSET(struct bss_description, ieFields)); + + return ielen; +} + +#ifdef WLAN_FEATURE_SAE +/** + * csr_sae_callback - Update SAE info to CSR roam session + * @mac_ctx: MAC context + * @msg_ptr: pointer to SAE message + * + * API to update SAE info to roam csr session + * + * Return: QDF_STATUS + */ +static QDF_STATUS csr_sae_callback(struct mac_context *mac_ctx, + tSirSmeRsp *msg_ptr) +{ + struct csr_roam_info *roam_info; + uint32_t session_id; + struct sir_sae_info *sae_info; + + sae_info = (struct sir_sae_info *) msg_ptr; + if (!sae_info) { + sme_err("SAE info is NULL"); + return QDF_STATUS_E_FAILURE; + } + + sme_debug("vdev_id %d "QDF_MAC_ADDR_FMT, + sae_info->vdev_id, + QDF_MAC_ADDR_REF(sae_info->peer_mac_addr.bytes)); + + session_id = sae_info->vdev_id; + if (session_id == WLAN_UMAC_VDEV_ID_MAX) + return QDF_STATUS_E_INVAL; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_FAILURE; + + roam_info->sae_info = sae_info; + + csr_roam_call_callback(mac_ctx, session_id, roam_info, + 0, eCSR_ROAM_SAE_COMPUTE, + eCSR_ROAM_RESULT_NONE); + qdf_mem_free(roam_info); + + return QDF_STATUS_SUCCESS; +} +#else +static inline QDF_STATUS csr_sae_callback(struct mac_context *mac_ctx, + tSirSmeRsp *msg_ptr) +{ + return QDF_STATUS_SUCCESS; +} +#endif + + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +enum mgmt_auth_type diag_auth_type_from_csr_type(enum csr_akm_type authtype) +{ + int n = AUTH_OPEN; + + switch (authtype) { + case eCSR_AUTH_TYPE_SHARED_KEY: + n = AUTH_SHARED; + break; + case eCSR_AUTH_TYPE_WPA: + n = AUTH_WPA_EAP; + break; + case eCSR_AUTH_TYPE_WPA_PSK: + n = AUTH_WPA_PSK; + break; + case eCSR_AUTH_TYPE_RSN: +#ifdef WLAN_FEATURE_11W + case eCSR_AUTH_TYPE_RSN_8021X_SHA256: +#endif + n = AUTH_WPA2_EAP; + break; + case eCSR_AUTH_TYPE_RSN_PSK: +#ifdef WLAN_FEATURE_11W + case eCSR_AUTH_TYPE_RSN_PSK_SHA256: +#endif + n = AUTH_WPA2_PSK; + break; +#ifdef FEATURE_WLAN_WAPI + case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE: + n = AUTH_WAPI_CERT; + break; + case eCSR_AUTH_TYPE_WAPI_WAI_PSK: + n = AUTH_WAPI_PSK; + break; +#endif /* FEATURE_WLAN_WAPI */ + default: + break; + } + return n; +} + +enum mgmt_encrypt_type diag_enc_type_from_csr_type(eCsrEncryptionType enctype) +{ + int n = ENC_MODE_OPEN; + + switch (enctype) { + case eCSR_ENCRYPT_TYPE_WEP40_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP40: + n = ENC_MODE_WEP40; + break; + case eCSR_ENCRYPT_TYPE_WEP104_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP104: + n = ENC_MODE_WEP104; + break; + case eCSR_ENCRYPT_TYPE_TKIP: + n = ENC_MODE_TKIP; + break; + case eCSR_ENCRYPT_TYPE_AES: + n = ENC_MODE_AES; + break; + case eCSR_ENCRYPT_TYPE_AES_GCMP: + n = ENC_MODE_AES_GCMP; + break; + case eCSR_ENCRYPT_TYPE_AES_GCMP_256: + n = ENC_MODE_AES_GCMP_256; + break; +#ifdef FEATURE_WLAN_WAPI + case eCSR_ENCRYPT_TYPE_WPI: + n = ENC_MODE_SMS4; + break; +#endif /* FEATURE_WLAN_WAPI */ + default: + break; + } + return n; +} + +enum mgmt_dot11_mode +diag_dot11_mode_from_csr_type(enum csr_cfgdot11mode dot11mode) +{ + switch (dot11mode) { + case eCSR_CFG_DOT11_MODE_ABG: + return DOT11_MODE_ABG; + case eCSR_CFG_DOT11_MODE_11A: + return DOT11_MODE_11A; + case eCSR_CFG_DOT11_MODE_11B: + return DOT11_MODE_11B; + case eCSR_CFG_DOT11_MODE_11G: + return DOT11_MODE_11G; + case eCSR_CFG_DOT11_MODE_11N: + return DOT11_MODE_11N; + case eCSR_CFG_DOT11_MODE_11AC: + return DOT11_MODE_11AC; + case eCSR_CFG_DOT11_MODE_11G_ONLY: + return DOT11_MODE_11G_ONLY; + case eCSR_CFG_DOT11_MODE_11N_ONLY: + return DOT11_MODE_11N_ONLY; + case eCSR_CFG_DOT11_MODE_11AC_ONLY: + return DOT11_MODE_11AC_ONLY; + case eCSR_CFG_DOT11_MODE_AUTO: + return DOT11_MODE_AUTO; + case eCSR_CFG_DOT11_MODE_11AX: + return DOT11_MODE_11AX; + case eCSR_CFG_DOT11_MODE_11AX_ONLY: + return DOT11_MODE_11AX_ONLY; + default: + return DOT11_MODE_MAX; + } +} + +enum mgmt_ch_width diag_ch_width_from_csr_type(enum phy_ch_width ch_width) +{ + switch (ch_width) { + case CH_WIDTH_20MHZ: + return BW_20MHZ; + case CH_WIDTH_40MHZ: + return BW_40MHZ; + case CH_WIDTH_80MHZ: + return BW_80MHZ; + case CH_WIDTH_160MHZ: + return BW_160MHZ; + case CH_WIDTH_80P80MHZ: + return BW_80P80MHZ; + case CH_WIDTH_5MHZ: + return BW_5MHZ; + case CH_WIDTH_10MHZ: + return BW_10MHZ; + default: + return BW_MAX; + } +} + +enum mgmt_bss_type diag_persona_from_csr_type(enum QDF_OPMODE persona) +{ + switch (persona) { + case QDF_STA_MODE: + return STA_PERSONA; + case QDF_SAP_MODE: + return SAP_PERSONA; + case QDF_P2P_CLIENT_MODE: + return P2P_CLIENT_PERSONA; + case QDF_P2P_GO_MODE: + return P2P_GO_PERSONA; + case QDF_FTM_MODE: + return FTM_PERSONA; + case QDF_IBSS_MODE: + return IBSS_PERSONA; + case QDF_MONITOR_MODE: + return MONITOR_PERSONA; + case QDF_P2P_DEVICE_MODE: + return P2P_DEVICE_PERSONA; + case QDF_OCB_MODE: + return OCB_PERSONA; + case QDF_EPPING_MODE: + return EPPING_PERSONA; + case QDF_QVIT_MODE: + return QVIT_PERSONA; + case QDF_NDI_MODE: + return NDI_PERSONA; + case QDF_WDS_MODE: + return WDS_PERSONA; + case QDF_BTAMP_MODE: + return BTAMP_PERSONA; + case QDF_AHDEMO_MODE: + return AHDEMO_PERSONA; + default: + return MAX_PERSONA; + } +} +#endif /* #ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR */ + +static const uint32_t +csr_start_ibss_channels50[CSR_NUM_IBSS_START_CHAN_50] = + { 5180, 5220, 5260, 5280, 5700 }; +static const uint32_t +csr_start_ibss_channels24[CSR_NUM_IBSS_START_CHANNELS_24] = + { 2412, 2437, 2462 }; + +static const uint32_t +social_channel_freq[MAX_SOCIAL_CHANNELS] = { 2412, 2437, 2462 }; + +static void init_config_param(struct mac_context *mac); +static bool csr_roam_process_results(struct mac_context *mac, tSmeCmd *pCommand, + enum csr_roamcomplete_result Result, + void *Context); +static void csr_roam_update_connected_profile_from_new_bss(struct mac_context *mac, + uint32_t sessionId, + struct new_bss_info * + pNewBss); +static ePhyChanBondState csr_get_cb_mode_from_ies(struct mac_context *mac, + uint32_t primary_ch_freq, + tDot11fBeaconIEs *pIes); + +static void csr_roaming_state_config_cnf_processor(struct mac_context *mac, + tSmeCmd *pCommand, uint8_t session_id); +static QDF_STATUS csr_roam_open(struct mac_context *mac); +static QDF_STATUS csr_roam_close(struct mac_context *mac); +static bool csr_roam_is_same_profile_keys(struct mac_context *mac, + tCsrRoamConnectedProfile *pConnProfile, + struct csr_roam_profile *pProfile2); + +static QDF_STATUS csr_roam_start_roaming_timer(struct mac_context *mac, + uint32_t vdev_id, + uint32_t interval); +static QDF_STATUS csr_roam_stop_roaming_timer(struct mac_context *mac, + uint32_t sessionId); +static void csr_roam_roaming_timer_handler(void *pv); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static void csr_roam_roaming_offload_timer_action(struct mac_context *mac_ctx, + uint32_t interval, uint8_t session_id, uint8_t action); +#endif +static void csr_roam_roaming_offload_timeout_handler(void *timer_data); +static QDF_STATUS csr_roam_start_wait_for_key_timer(struct mac_context *mac, + uint32_t interval); +static void csr_roam_wait_for_key_time_out_handler(void *pv); +static QDF_STATUS csr_init11d_info(struct mac_context *mac, tCsr11dinfo *ps11dinfo); +static QDF_STATUS csr_init_channel_power_list(struct mac_context *mac, + tCsr11dinfo *ps11dinfo); +static QDF_STATUS csr_roam_free_connected_info(struct mac_context *mac, + struct csr_roam_connectedinfo * + pConnectedInfo); +static void csr_roam_link_up(struct mac_context *mac, struct qdf_mac_addr bssid); +static void csr_roam_link_down(struct mac_context *mac, uint32_t sessionId); +static enum csr_cfgdot11mode +csr_roam_get_phy_mode_band_for_bss(struct mac_context *mac, + struct csr_roam_profile *pProfile, + uint32_t bss_op_ch_freq, + enum reg_wifi_band *pBand); +static QDF_STATUS csr_roam_get_qos_info_from_bss( +struct mac_context *mac, struct bss_description *bss_desc); +static uint32_t csr_find_session_by_type(struct mac_context *, + enum QDF_OPMODE); +static bool csr_is_conn_allow_2g_band(struct mac_context *mac, + uint32_t chnl); +static bool csr_is_conn_allow_5g_band(struct mac_context *mac, + uint32_t chnl); +static QDF_STATUS csr_roam_start_wds(struct mac_context *mac, + uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *bss_desc); +static void csr_init_session(struct mac_context *mac, uint32_t sessionId); + +static QDF_STATUS +csr_roam_get_qos_info_from_bss(struct mac_context *mac, + struct bss_description *bss_desc); + +static void csr_init_operating_classes(struct mac_context *mac); + +static void csr_add_len_of_social_channels(struct mac_context *mac, + uint8_t *num_chan); +static void csr_add_social_channels(struct mac_context *mac, + tSirUpdateChanList *chan_list, struct csr_scanstruct *pScan, + uint8_t *num_chan); + +#ifdef WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY +static struct csr_roam_session *csr_roam_roam_session; + +/* Allocate and initialize global variables */ +static QDF_STATUS csr_roam_init_globals(struct mac_context *mac) +{ + uint32_t buf_size; + QDF_STATUS status; + + buf_size = WLAN_MAX_VDEVS * sizeof(struct csr_roam_session); + + csr_roam_roam_session = qdf_mem_malloc(buf_size); + if (csr_roam_roam_session) { + mac->roam.roamSession = csr_roam_roam_session; + status = QDF_STATUS_SUCCESS; + } else { + status = QDF_STATUS_E_NOMEM; + } + + return status; +} + +/* Free memory allocated dynamically */ +static inline void csr_roam_free_globals(void) +{ + qdf_mem_free(csr_roam_roam_session); + csr_roam_roam_session = NULL; +} + +#else /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ +static struct csr_roam_session csr_roam_roam_session[WLAN_MAX_VDEVS]; + +/* Initialize global variables */ +static QDF_STATUS csr_roam_init_globals(struct mac_context *mac) +{ + qdf_mem_zero(&csr_roam_roam_session, + sizeof(csr_roam_roam_session)); + mac->roam.roamSession = csr_roam_roam_session; + + return QDF_STATUS_SUCCESS; +} + +static inline void csr_roam_free_globals(void) +{ +} +#endif /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ + +/* Returns whether handoff is currently in progress or not */ +static +bool csr_roam_is_handoff_in_progress(struct mac_context *mac, uint8_t sessionId) +{ + return csr_neighbor_roam_is_handoff_in_progress(mac, sessionId); +} + +static QDF_STATUS +csr_roam_issue_disassociate(struct mac_context *mac, uint32_t sessionId, + enum csr_roam_substate NewSubstate, + bool fMICFailure) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct qdf_mac_addr bssId = QDF_MAC_ADDR_BCAST_INIT; + uint16_t reasonCode; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + if (fMICFailure) { + reasonCode = eSIR_MAC_MIC_FAILURE_REASON; + } else if (NewSubstate == eCSR_ROAM_SUBSTATE_DISASSOC_HANDOFF) { + reasonCode = eSIR_MAC_DISASSOC_DUE_TO_FTHANDOFF_REASON; + } else if (eCSR_ROAM_SUBSTATE_DISASSOC_STA_HAS_LEFT == NewSubstate) { + reasonCode = eSIR_MAC_DISASSOC_LEAVING_BSS_REASON; + NewSubstate = eCSR_ROAM_SUBSTATE_DISASSOC_FORCED; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "set to reason code eSIR_MAC_DISASSOC_LEAVING_BSS_REASON and set back NewSubstate"); + } else { + reasonCode = eSIR_MAC_UNSPEC_FAILURE_REASON; + } + if ((csr_roam_is_handoff_in_progress(mac, sessionId)) && + (NewSubstate != eCSR_ROAM_SUBSTATE_DISASSOC_HANDOFF)) { + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + qdf_copy_macaddr(&bssId, + pNeighborRoamInfo->csrNeighborRoamProfile.BSSIDs. + bssid); + } else if (pSession->pConnectBssDesc) { + qdf_mem_copy(&bssId.bytes, pSession->pConnectBssDesc->bssId, + sizeof(struct qdf_mac_addr)); + } + + sme_debug("Disassociate Bssid " QDF_MAC_ADDR_FMT " subState: %s reason: %d", + QDF_MAC_ADDR_REF(bssId.bytes), + mac_trace_getcsr_roam_sub_state(NewSubstate), + reasonCode); + + csr_roam_substate_change(mac, NewSubstate, sessionId); + + status = csr_send_mb_disassoc_req_msg(mac, sessionId, bssId.bytes, + reasonCode); + + if (QDF_IS_STATUS_SUCCESS(status)) { + csr_roam_link_down(mac, sessionId); +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + /* no need to tell QoS that we are disassociating, it will be + * taken care off in assoc req for HO + */ + if (eCSR_ROAM_SUBSTATE_DISASSOC_HANDOFF != NewSubstate) { + /* notify QoS module that disassoc happening */ + sme_qos_csr_event_ind(mac, (uint8_t)sessionId, + SME_QOS_CSR_DISCONNECT_REQ, NULL); + } +#endif + } else { + sme_warn("csr_send_mb_disassoc_req_msg failed status: %d", + status); + } + + return status; +} + +/* This function assumes that we only support one IBSS session. + * We cannot use BSSID to identify session because for IBSS, + * the bssid changes. + */ +#ifdef QCA_IBSS_SUPPORT +static uint32_t csr_find_ibss_session(struct mac_context *mac) +{ + uint32_t i, nRet = WLAN_UMAC_VDEV_ID_MAX; + struct csr_roam_session *pSession; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (CSR_IS_SESSION_VALID(mac, i)) { + pSession = CSR_GET_SESSION(mac, i); + if (pSession->pCurRoamProfile && + (csr_is_bss_type_ibss + (pSession->connectedProfile.BSSType))) { + /* Found it */ + nRet = i; + break; + } + } + } + return nRet; +} + +static QDF_STATUS +csr_roam_start_ibss(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + bool *pfSameIbss) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool fSameIbss = false; + + if (csr_is_conn_state_ibss(mac, sessionId)) { + /* Check if any profile parameter has changed ? If any profile + * parameter has changed then stop old BSS and start a new one + * with new parameters + */ + if (csr_is_same_profile(mac, + &mac->roam.roamSession[sessionId]. + connectedProfile, pProfile)) + fSameIbss = true; + else + status = csr_roam_issue_stop_bss(mac, sessionId, + eCSR_ROAM_SUBSTATE_DISCONNECT_CONTINUE_ROAMING); + + } else if (csr_is_conn_state_connected_infra(mac, sessionId)) + /* Disassociate from the connected Infrastructure network... */ + status = csr_roam_issue_disassociate(mac, sessionId, + eCSR_ROAM_SUBSTATE_DISCONNECT_CONTINUE_ROAMING, + false); + else { + struct bss_config_param *pBssConfig; + + pBssConfig = qdf_mem_malloc(sizeof(struct bss_config_param)); + if (!pBssConfig) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (QDF_IS_STATUS_SUCCESS(status)) { + /* there is no Bss description before we start an IBSS + * so we need to adopt all Bss configuration parameters + * from the Profile. + */ + status = csr_roam_prepare_bss_config_from_profile(mac, + pProfile, + pBssConfig, + NULL); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* save dotMode */ + mac->roam.roamSession[sessionId].bssParams. + uCfgDot11Mode = pBssConfig->uCfgDot11Mode; + /* Prepare some more parameters for this IBSS */ + csr_roam_prepare_bss_params(mac, sessionId, + pProfile, NULL, + pBssConfig, NULL); + status = csr_roam_set_bss_config_cfg(mac, + sessionId, + pProfile, NULL, + pBssConfig, + NULL, false); + } + + qdf_mem_free(pBssConfig); + } /* Allocate memory */ + } + + if (pfSameIbss) + *pfSameIbss = fSameIbss; + return status; +} + +static void +csr_roam_chk_lnk_ibss_new_peer_ind(struct mac_context *mac_ctx, + tSirSmeRsp *msg_ptr) +{ + struct csr_roam_session *session; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct csr_roam_info *roam_info = NULL; + tSmeIbssPeerInd *pIbssPeerInd = (tSmeIbssPeerInd *) msg_ptr; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + host_log_ibss_pkt_type *pIbssLog; + + WLAN_HOST_DIAG_LOG_ALLOC(pIbssLog, host_log_ibss_pkt_type, + LOG_WLAN_IBSS_C); + if (pIbssLog) { + pIbssLog->eventId = WLAN_IBSS_EVENT_PEER_JOIN; + qdf_copy_macaddr(&pIbssLog->peer_macaddr, + &pIbssPeerInd->peer_addr); + WLAN_HOST_DIAG_LOG_REPORT(pIbssLog); + } +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + + sessionId = csr_find_ibss_session(mac_ctx); + if (WLAN_UMAC_VDEV_ID_MAX == sessionId) + return; + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (!session) { + sme_err("session %d not found", sessionId); + return; + } + /* + * Issue the set Context request to LIM to establish the Unicast STA + * context for the new peer... + */ + if (!session->pConnectBssDesc) { + sme_warn("CSR: connected BSS is empty"); + goto callback_and_free; + } + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + goto callback_and_free; + qdf_copy_macaddr(&roam_info->peerMac, &pIbssPeerInd->peer_addr); + qdf_mem_copy(&roam_info->bssid, session->pConnectBssDesc->bssId, + sizeof(struct qdf_mac_addr)); + if (pIbssPeerInd->mesgLen > sizeof(tSmeIbssPeerInd)) { + roam_info->pbFrames = qdf_mem_malloc((pIbssPeerInd->mesgLen - + sizeof(tSmeIbssPeerInd))); + if (!roam_info->pbFrames) { + status = QDF_STATUS_E_NOMEM; + } else { + status = QDF_STATUS_SUCCESS; + roam_info->nBeaconLength = pIbssPeerInd->mesgLen - + sizeof(tSmeIbssPeerInd); + qdf_mem_copy(roam_info->pbFrames, + ((uint8_t *)pIbssPeerInd) + + sizeof(tSmeIbssPeerInd), + roam_info->nBeaconLength); + } + roam_info->bss_desc = qdf_mem_malloc( + session->pConnectBssDesc->length); + if (!roam_info->bss_desc) { + status = QDF_STATUS_E_NOMEM; + } else { + status = QDF_STATUS_SUCCESS; + qdf_mem_copy(roam_info->bss_desc, + session->pConnectBssDesc, + session->pConnectBssDesc->length); + } + } + if (eCSR_ENCRYPT_TYPE_NONE == + session->connectedProfile.EncryptionType) { + /* NO keys. these key parameters don't matter */ + csr_issue_set_context_req_helper(mac_ctx, + session->pCurRoamProfile, + sessionId, + &pIbssPeerInd->peer_addr.bytes, + false, true, eSIR_TX_RX, + 0, 0, NULL); + } + +callback_and_free: + /* send up the sec type for the new peer */ + if (roam_info) + roam_info->u.pConnectedProfile = &session->connectedProfile; + csr_roam_call_callback(mac_ctx, sessionId, roam_info, 0, + eCSR_ROAM_CONNECT_STATUS_UPDATE, + eCSR_ROAM_RESULT_IBSS_NEW_PEER); + if (roam_info) { + qdf_mem_free(roam_info->pbFrames); + qdf_mem_free(roam_info->bss_desc); + qdf_mem_free(roam_info); + } +} + +static void +csr_roam_chk_lnk_ibss_peer_departed_ind(struct mac_context *mac_ctx, + tSirSmeRsp *msg_ptr) +{ + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + struct csr_roam_info *roam_info; + tSmeIbssPeerInd *pIbssPeerInd; + + if (!msg_ptr) { + sme_err("IBSS peer ind. message is NULL"); + return; + } + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + pIbssPeerInd = (tSmeIbssPeerInd *)msg_ptr; + sessionId = csr_find_ibss_session(mac_ctx); + if (WLAN_UMAC_VDEV_ID_MAX != sessionId) { +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + host_log_ibss_pkt_type *pIbssLog; + + WLAN_HOST_DIAG_LOG_ALLOC(pIbssLog, host_log_ibss_pkt_type, + LOG_WLAN_IBSS_C); + if (pIbssLog) { + pIbssLog->eventId = WLAN_IBSS_EVENT_PEER_LEAVE; + if (pIbssPeerInd) { + qdf_copy_macaddr(&pIbssLog->peer_macaddr, + &pIbssPeerInd->peer_addr); + } + WLAN_HOST_DIAG_LOG_REPORT(pIbssLog); + } +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + sme_debug("CSR: Peer departed notification from LIM"); + qdf_copy_macaddr(&roam_info->peerMac, &pIbssPeerInd->peer_addr); + csr_roam_call_callback(mac_ctx, sessionId, roam_info, 0, + eCSR_ROAM_CONNECT_STATUS_UPDATE, + eCSR_ROAM_RESULT_IBSS_PEER_DEPARTED); + } + qdf_mem_free(roam_info); +} + +static uint32_t +csr_roam_get_ibss_start_chan_freq50(struct mac_context *mac) +{ + uint32_t ch_freq = 0; + uint32_t idx; + uint32_t id_Valid_ch; + bool fFound = false; + uint32_t len = sizeof(mac->roam.valid_ch_freq_list); + + if (mac->roam.configParam.ad_hoc_ch_freq_5g) { + ch_freq = mac->roam.configParam.ad_hoc_ch_freq_5g; + if (!csr_roam_is_channel_valid(mac, ch_freq)) + ch_freq = 0; + } + if (0 == ch_freq + && + QDF_IS_STATUS_SUCCESS(csr_get_cfg_valid_channels(mac, + mac->roam.valid_ch_freq_list, &len))) { + for (idx = 0; (idx < CSR_NUM_IBSS_START_CHAN_50) && !fFound; + idx++) { + for (id_Valid_ch = 0; + (id_Valid_ch < len) && !fFound; + id_Valid_ch++) { + if (csr_start_ibss_channels50[idx] == + mac->roam.valid_ch_freq_list[id_Valid_ch]) { + fFound = true; + ch_freq = + csr_start_ibss_channels50[idx]; + } + } + } + /* + * this is rare, but if it does happen, + * we find anyone in 11a bandwidth and + * return the first 11a channel found! + */ + if (!fFound) { + for (id_Valid_ch = 0; id_Valid_ch < len; + id_Valid_ch++) { + if (WLAN_REG_IS_5GHZ_CH_FREQ( + mac->roam.valid_ch_freq_list[id_Valid_ch])) { + /* the max channel# in 11g is 14 */ + if (id_Valid_ch < + CSR_NUM_IBSS_START_CHAN_50) { + ch_freq = + mac->roam.valid_ch_freq_list + [id_Valid_ch]; + } + break; + } + } + } + } /* if */ + + return ch_freq; +} + +static uint32_t +csr_roam_get_ibss_start_chan_freq24(struct mac_context *mac) +{ + uint32_t ch_freq = 2412; + uint32_t idx; + uint32_t id_Valid_ch; + bool fFound = false; + uint32_t len = sizeof(mac->roam.valid_ch_freq_list); + + if (mac->roam.configParam.ad_hoc_ch_freq_2g) { + ch_freq = mac->roam.configParam.ad_hoc_ch_freq_2g; + if (!csr_roam_is_channel_valid(mac, ch_freq)) + ch_freq = 0; + } + + if (0 == ch_freq + && + QDF_IS_STATUS_SUCCESS(csr_get_cfg_valid_channels(mac, + mac->roam.valid_ch_freq_list, + &len))) { + for (idx = 0; (idx < CSR_NUM_IBSS_START_CHANNELS_24) && !fFound; + idx++) { + for (id_Valid_ch = 0; + (id_Valid_ch < len) && !fFound; + id_Valid_ch++) { + if (csr_start_ibss_channels24[idx] == + mac->roam.valid_ch_freq_list[id_Valid_ch]) { + fFound = true; + ch_freq = + csr_start_ibss_channels24[idx]; + } + } + } + } + + return ch_freq; +} +#else +static inline uint32_t +csr_roam_get_ibss_start_chan_freq50(struct mac_context *mac) +{ + return 0; +} + +static inline uint32_t +csr_roam_get_ibss_start_chan_freq24(struct mac_context *mac) +{ + return 0; +} + +static inline void +csr_roam_chk_lnk_ibss_new_peer_ind(struct mac_context *mac_ctx, + tSirSmeRsp *msg_ptr) +{ +} + +static void +csr_roam_chk_lnk_ibss_peer_departed_ind(struct mac_context *mac_ctx, + tSirSmeRsp *msg_ptr) +{ +} + +static QDF_STATUS +csr_roam_start_ibss(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + bool *pfSameIbss) +{ + return QDF_STATUS_SUCCESS; +} + +static inline uint32_t +csr_find_ibss_session(struct mac_context *mac) +{ + return WLAN_MAX_VDEVS; +} +#endif + +static void csr_roam_de_init_globals(struct mac_context *mac) +{ + uint8_t i; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (mac->roam.roamSession[i].pCurRoamProfile) + csr_release_profile(mac, + mac->roam.roamSession[i]. + pCurRoamProfile); + csr_release_profile(mac, + &mac->roam.roamSession[i]. + stored_roam_profile.profile); + } + csr_roam_free_globals(); + mac->roam.roamSession = NULL; +} + +QDF_STATUS csr_open(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t i; + + do { + /* Initialize CSR Roam Globals */ + status = csr_roam_init_globals(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) + csr_roam_state_change(mac, eCSR_ROAMING_STATE_STOP, i); + + init_config_param(mac); + status = csr_scan_open(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + csr_roam_free_globals(); + break; + } + status = csr_roam_open(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + csr_roam_free_globals(); + break; + } + mac->roam.nextRoamId = 1; /* Must not be 0 */ + } while (0); + + return status; +} + +QDF_STATUS csr_init_chan_list(struct mac_context *mac, uint8_t *alpha2) +{ + QDF_STATUS status; + + mac->scan.countryCodeDefault[0] = alpha2[0]; + mac->scan.countryCodeDefault[1] = alpha2[1]; + mac->scan.countryCodeDefault[2] = alpha2[2]; + + sme_debug("init time country code %.2s", mac->scan.countryCodeDefault); + + mac->scan.domainIdDefault = 0; + mac->scan.domainIdCurrent = 0; + + qdf_mem_copy(mac->scan.countryCodeCurrent, + mac->scan.countryCodeDefault, CFG_COUNTRY_CODE_LEN); + qdf_mem_copy(mac->scan.countryCodeElected, + mac->scan.countryCodeDefault, CFG_COUNTRY_CODE_LEN); + status = csr_get_channel_and_power_list(mac); + + return status; +} + +QDF_STATUS csr_set_channels(struct mac_context *mac, + struct csr_config_params *pParam) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t index = 0; + + qdf_mem_copy(pParam->Csr11dinfo.countryCode, + mac->scan.countryCodeCurrent, CFG_COUNTRY_CODE_LEN); + for (index = 0; index < mac->scan.base_channels.numChannels; + index++) { + pParam->Csr11dinfo.Channels.channel_freq_list[index] = + mac->scan.base_channels.channel_freq_list[index]; + pParam->Csr11dinfo.ChnPower[index].first_chan_freq = + mac->scan.base_channels.channel_freq_list[index]; + pParam->Csr11dinfo.ChnPower[index].numChannels = 1; + pParam->Csr11dinfo.ChnPower[index].maxtxPower = + mac->scan.defaultPowerTable[index].tx_power; + } + pParam->Csr11dinfo.Channels.numChannels = + mac->scan.base_channels.numChannels; + + return status; +} + +QDF_STATUS csr_close(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + csr_roam_close(mac); + csr_scan_close(mac); + /* DeInit Globals */ + csr_roam_de_init_globals(mac); + return status; +} + +static int8_t +csr_find_channel_pwr(struct channel_power *pdefaultPowerTable, + uint32_t chan_freq) +{ + uint8_t i; + /* TODO: if defaultPowerTable is guaranteed to be in ascending */ + /* order of channel numbers, we can employ binary search */ + for (i = 0; i < CFG_VALID_CHANNEL_LIST_LEN; i++) { + if (pdefaultPowerTable[i].center_freq == chan_freq) + return pdefaultPowerTable[i].tx_power; + } + /* could not find the channel list in default list */ + /* this should not have occurred */ + QDF_ASSERT(0); + return 0; +} + +/** + * csr_roam_arrange_ch_list() - Updates the channel list modified with greedy + * order for 5 Ghz preference and DFS channels. + * @mac_ctx: pointer to mac context. + * @chan_list: channel list updated with greedy channel order. + * @num_channel: Number of channels in list + * + * To allow Early Stop Roaming Scan feature to co-exist with 5G preference, + * this function moves 5G channels ahead of 2G channels. This function can + * also move 2G channels, ahead of DFS channel or vice versa. Order is + * maintained among same category channels + * + * Return: None + */ +static void csr_roam_arrange_ch_list(struct mac_context *mac_ctx, + tSirUpdateChanParam *chan_list, uint8_t num_channel) +{ + bool prefer_5g = CSR_IS_ROAM_PREFER_5GHZ(mac_ctx); + bool prefer_dfs = CSR_IS_DFS_CH_ROAM_ALLOWED(mac_ctx); + int i, j = 0; + tSirUpdateChanParam *tmp_list = NULL; + + if (!prefer_5g) + return; + + tmp_list = qdf_mem_malloc(sizeof(tSirUpdateChanParam) * num_channel); + if (!tmp_list) + return; + + /* Fist copy Non-DFS 5g channels */ + for (i = 0; i < num_channel; i++) { + if (WLAN_REG_IS_5GHZ_CH_FREQ(chan_list[i].freq) && + !wlan_reg_is_dfs_for_freq(mac_ctx->pdev, + chan_list[i].freq)) { + qdf_mem_copy(&tmp_list[j++], + &chan_list[i], sizeof(tSirUpdateChanParam)); + chan_list[i].freq = 0; + } + } + if (prefer_dfs) { + /* next copy DFS channels (remaining channels in 5G) */ + for (i = 0; i < num_channel; i++) { + if (WLAN_REG_IS_5GHZ_CH_FREQ(chan_list[i].freq)) { + qdf_mem_copy(&tmp_list[j++], &chan_list[i], + sizeof(tSirUpdateChanParam)); + chan_list[i].freq = 0; + } + } + } else { + /* next copy 2G channels */ + for (i = 0; i < num_channel; i++) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_list[i].freq)) { + qdf_mem_copy(&tmp_list[j++], &chan_list[i], + sizeof(tSirUpdateChanParam)); + chan_list[i].freq = 0; + } + } + } + /* copy rest of the channels in same order to tmp list */ + for (i = 0; i < num_channel; i++) { + if (chan_list[i].freq) { + qdf_mem_copy(&tmp_list[j++], &chan_list[i], + sizeof(tSirUpdateChanParam)); + chan_list[i].freq = 0; + } + } + /* copy tmp list to original channel list buffer */ + qdf_mem_copy(chan_list, tmp_list, + sizeof(tSirUpdateChanParam) * num_channel); + qdf_mem_free(tmp_list); +} + +/** + * csr_roam_sort_channel_for_early_stop() - Sort the channels + * @mac_ctx: mac global context + * @chan_list: Original channel list from the upper layers + * @num_channel: Number of original channels + * + * For Early stop scan feature, the channel list should be in an order, + * where-in there is a maximum chance to detect an AP in the initial + * channels in the list so that the scanning can be stopped early as the + * feature demands. + * Below fixed greedy channel list has been provided + * based on most of the enterprise wifi installations across the globe. + * + * Identify all the greedy channels within the channel list from user space. + * Identify all the non-greedy channels in the user space channel list. + * Merge greedy channels followed by non-greedy channels back into the + * chan_list. + * + * Return: None + */ +static void csr_roam_sort_channel_for_early_stop(struct mac_context *mac_ctx, + tSirUpdateChanList *chan_list, uint8_t num_channel) +{ + tSirUpdateChanList *chan_list_greedy, *chan_list_non_greedy; + uint8_t i, j; + static const uint32_t fixed_greedy_freq_list[] = {2412, 2437, 2462, + 5180, 5240, 5200, 5220, 2457, 2417, 2452, 5745, 5785, 5805, + 2422, 2427, 2447, 5765, 5825, 2442, 2432, 5680, 5700, 5260, + 5580, 5280, 5520, 5320, 5300, 5500, 5600, 2472, 2484, 5560, + 5660, 5755, 5775}; + uint8_t num_fixed_greedy_chan; + uint8_t num_greedy_chan = 0; + uint8_t num_non_greedy_chan = 0; + uint8_t match_found = false; + uint32_t buf_size; + + buf_size = sizeof(tSirUpdateChanList) + + (sizeof(tSirUpdateChanParam) * num_channel); + chan_list_greedy = qdf_mem_malloc(buf_size); + chan_list_non_greedy = qdf_mem_malloc(buf_size); + if (!chan_list_greedy || !chan_list_non_greedy) + goto scan_list_sort_error; + /* + * fixed_greedy_freq_list is an evaluated freq list based on most of + * the enterprise wifi deployments and the order of the channels + * determines the highest possibility of finding an AP. + * chan_list is the channel list provided by upper layers based on the + * regulatory domain. + */ + num_fixed_greedy_chan = sizeof(fixed_greedy_freq_list) / + sizeof(uint32_t); + /* + * Browse through the chan_list and put all the non-greedy channels + * into a separate list by name chan_list_non_greedy + */ + for (i = 0; i < num_channel; i++) { + for (j = 0; j < num_fixed_greedy_chan; j++) { + if (chan_list->chanParam[i].freq == + fixed_greedy_freq_list[j]) { + match_found = true; + break; + } + } + if (!match_found) { + qdf_mem_copy( + &chan_list_non_greedy->chanParam[num_non_greedy_chan], + &chan_list->chanParam[i], + sizeof(tSirUpdateChanParam)); + num_non_greedy_chan++; + } else { + match_found = false; + } + } + /* + * Browse through the fixed_greedy_chan_list and put all the greedy + * channels in the chan_list into a separate list by name + * chan_list_greedy + */ + for (i = 0; i < num_fixed_greedy_chan; i++) { + for (j = 0; j < num_channel; j++) { + if (fixed_greedy_freq_list[i] == + chan_list->chanParam[j].freq) { + qdf_mem_copy( + &chan_list_greedy->chanParam[num_greedy_chan], + &chan_list->chanParam[j], + sizeof(tSirUpdateChanParam)); + num_greedy_chan++; + break; + } + } + } + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_DEBUG, + "greedy=%d, non-greedy=%d, tot=%d", + num_greedy_chan, num_non_greedy_chan, num_channel); + if ((num_greedy_chan + num_non_greedy_chan) != num_channel) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + "incorrect sorting of channels"); + goto scan_list_sort_error; + } + /* Copy the Greedy channels first */ + i = 0; + qdf_mem_copy(&chan_list->chanParam[i], + &chan_list_greedy->chanParam[i], + num_greedy_chan * sizeof(tSirUpdateChanParam)); + /* Copy the remaining Non Greedy channels */ + i = num_greedy_chan; + j = 0; + qdf_mem_copy(&chan_list->chanParam[i], + &chan_list_non_greedy->chanParam[j], + num_non_greedy_chan * sizeof(tSirUpdateChanParam)); + + /* Update channel list for 5g preference and allow DFS roam */ + csr_roam_arrange_ch_list(mac_ctx, chan_list->chanParam, num_channel); +scan_list_sort_error: + qdf_mem_free(chan_list_greedy); + qdf_mem_free(chan_list_non_greedy); +} + +/** + * csr_emu_chan_req() - update the required channel list for emulation + * @channel: channel number to check + * + * To reduce scan time during emulation platforms, this function + * restricts the scanning to be done on selected channels + * + * Return: QDF_STATUS enumeration + */ +#ifdef QCA_WIFI_NAPIER_EMULATION +#define SCAN_CHAN_LIST_5G_LEN 6 +#define SCAN_CHAN_LIST_2G_LEN 3 +static const uint8_t +csr_scan_chan_list_5g[SCAN_CHAN_LIST_5G_LEN] = { 5180, 5220, 5260, 5280, 5700, 5745 }; +static const uint8_t +csr_scan_chan_list_2g[SCAN_CHAN_LIST_2G_LEN] = { 2412, 2437, 2462 }; +static QDF_STATUS csr_emu_chan_req(uint32_t channel) +{ + int i; + + if (WLAN_REG_IS_24GHZ_CH_FREQ(channel)) { + for (i = 0; i < QDF_ARRAY_SIZE(csr_scan_chan_list_2g); i++) { + if (csr_scan_chan_list_2g[i] == channel) + return QDF_STATUS_SUCCESS; + } + } else if (WLAN_REG_IS_5GHZ_CH_FREQ(channel)) { + for (i = 0; i < QDF_ARRAY_SIZE(csr_scan_chan_list_5g); i++) { + if (csr_scan_chan_list_5g[i] == channel) + return QDF_STATUS_SUCCESS; + } + } + return QDF_STATUS_E_FAILURE; +} +#else +static QDF_STATUS csr_emu_chan_req(uint32_t channel_num) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY +static void csr_add_len_of_social_channels(struct mac_context *mac, + uint8_t *num_chan) +{ + uint8_t i; + uint8_t no_chan = *num_chan; + + sme_debug("add len of social channels, before adding - num_chan:%hu", + *num_chan); + if (CSR_IS_5G_BAND_ONLY(mac)) { + for (i = 0; i < MAX_SOCIAL_CHANNELS; i++) { + if (wlan_reg_get_channel_state_for_freq( + mac->pdev, social_channel_freq[i]) == + CHANNEL_STATE_ENABLE) + no_chan++; + } + } + *num_chan = no_chan; + sme_debug("after adding - num_chan:%hu", *num_chan); +} + +static void csr_add_social_channels(struct mac_context *mac, + tSirUpdateChanList *chan_list, struct csr_scanstruct *pScan, + uint8_t *num_chan) +{ + uint8_t i; + uint8_t no_chan = *num_chan; + + sme_debug("add social channels chan_list %pK, num_chan %hu", chan_list, + *num_chan); + if (CSR_IS_5G_BAND_ONLY(mac)) { + for (i = 0; i < MAX_SOCIAL_CHANNELS; i++) { + if (wlan_reg_get_channel_state_for_freq( + mac->pdev, social_channel_freq[i]) != + CHANNEL_STATE_ENABLE) + continue; + chan_list->chanParam[no_chan].freq = + social_channel_freq[i]; + chan_list->chanParam[no_chan].pwr = + csr_find_channel_pwr(pScan->defaultPowerTable, + social_channel_freq[i]); + chan_list->chanParam[no_chan].dfsSet = false; + if (cds_is_5_mhz_enabled()) + chan_list->chanParam[no_chan].quarter_rate + = 1; + else if (cds_is_10_mhz_enabled()) + chan_list->chanParam[no_chan].half_rate = 1; + no_chan++; + } + sme_debug("after adding -num_chan %hu", no_chan); + } + *num_chan = no_chan; +} +#else +static void csr_add_len_of_social_channels(struct mac_context *mac, + uint8_t *num_chan) +{ + sme_debug("skip adding len of social channels"); +} +static void csr_add_social_channels(struct mac_context *mac, + tSirUpdateChanList *chan_list, struct csr_scanstruct *pScan, + uint8_t *num_chan) +{ + sme_debug("skip social channels"); +} +#endif + +/** + * csr_scan_event_handler() - callback for scan event + * @vdev: wlan objmgr vdev pointer + * @event: scan event + * @arg: global mac context pointer + * + * Return: void + */ +static void csr_scan_event_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, + void *arg) +{ + bool success = false; + QDF_STATUS lock_status; + struct mac_context *mac = arg; + + if (!mac) + return; + + if (!util_is_scan_completed(event, &success)) + return; + + lock_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(lock_status)) + return; + + if (mac->scan.pending_channel_list_req) + csr_update_channel_list(mac); + sme_release_global_lock(&mac->sme); +} + +QDF_STATUS csr_update_channel_list(struct mac_context *mac) +{ + tSirUpdateChanList *pChanList; + struct csr_scanstruct *pScan = &mac->scan; + uint8_t numChan = pScan->base_channels.numChannels; + uint8_t num_channel = 0; + uint32_t bufLen; + struct scheduler_msg msg = {0}; + uint8_t i; + uint8_t channel_state; + uint16_t unsafe_chan[NUM_CHANNELS]; + uint16_t unsafe_chan_cnt = 0; + uint16_t cnt = 0; + uint32_t channel_freq; + bool is_unsafe_chan; + bool is_same_band; + bool is_5mhz_enabled; + bool is_10mhz_enabled; + enum scm_scan_status scan_status; + QDF_STATUS lock_status; + + qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!qdf_ctx) { + sme_err("qdf_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + + lock_status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_ERROR(lock_status)) + return lock_status; + + if (mac->mlme_cfg->reg.enable_pending_chan_list_req) { + scan_status = ucfg_scan_get_pdev_status(mac->pdev); + if (scan_status == SCAN_IS_ACTIVE || + scan_status == SCAN_IS_ACTIVE_AND_PENDING) { + mac->scan.pending_channel_list_req = true; + sme_release_global_lock(&mac->sme); + sme_debug("scan in progress postpone channel list req "); + return QDF_STATUS_SUCCESS; + } + mac->scan.pending_channel_list_req = false; + } + sme_release_global_lock(&mac->sme); + + pld_get_wlan_unsafe_channel(qdf_ctx->dev, unsafe_chan, + &unsafe_chan_cnt, + sizeof(unsafe_chan)); + + csr_add_len_of_social_channels(mac, &numChan); + + bufLen = sizeof(tSirUpdateChanList) + + (sizeof(tSirUpdateChanParam) * (numChan)); + + csr_init_operating_classes(mac); + pChanList = qdf_mem_malloc(bufLen); + if (!pChanList) + return QDF_STATUS_E_NOMEM; + + is_5mhz_enabled = cds_is_5_mhz_enabled(); + if (is_5mhz_enabled) + sme_nofl_debug("quarter_rate enabled"); + is_10mhz_enabled = cds_is_10_mhz_enabled(); + if (is_10mhz_enabled) + sme_nofl_debug("half_rate enabled"); + + for (i = 0; i < pScan->base_channels.numChannels; i++) { + struct csr_sta_roam_policy_params *roam_policy = + &mac->roam.configParam.sta_roam_policy; + if (QDF_STATUS_SUCCESS != + csr_emu_chan_req(pScan->base_channels.channel_freq_list[i])) + continue; + + channel_freq = pScan->base_channels.channel_freq_list[i]; + /* Scan is not performed on DSRC channels*/ + if (wlan_reg_is_dsrc_freq(channel_freq)) + continue; + + channel_state = + wlan_reg_get_channel_state_for_freq( + mac->pdev, channel_freq); + if ((CHANNEL_STATE_ENABLE == channel_state) || + mac->scan.fEnableDFSChnlScan) { + if ((mac->roam.configParam.sta_roam_policy.dfs_mode == + CSR_STA_ROAM_POLICY_DFS_DISABLED) && + (channel_state == CHANNEL_STATE_DFS)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("skip dfs channel frequency %d"), + channel_freq); + continue; + } + if (mac->roam.configParam.sta_roam_policy. + skip_unsafe_channels && + unsafe_chan_cnt) { + is_unsafe_chan = false; + for (cnt = 0; cnt < unsafe_chan_cnt; cnt++) { + if (unsafe_chan[cnt] == channel_freq) { + is_unsafe_chan = true; + break; + } + } + is_same_band = + (WLAN_REG_IS_24GHZ_CH_FREQ( + channel_freq) && + roam_policy->sap_operating_band == + BAND_2G) || + (WLAN_REG_IS_5GHZ_CH_FREQ( + channel_freq) && + roam_policy->sap_operating_band == + BAND_5G); + if (is_unsafe_chan && is_same_band) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("ignoring unsafe channel freq %d"), + channel_freq); + continue; + } + } + pChanList->chanParam[num_channel].freq = + pScan->base_channels.channel_freq_list[i]; + pChanList->chanParam[num_channel].pwr = + csr_find_channel_pwr( + pScan->defaultPowerTable, + pScan->base_channels.channel_freq_list[i]); + + if (pScan->fcc_constraint) { + if (2467 == + pScan->base_channels.channel_freq_list[i]) { + pChanList->chanParam[num_channel].pwr = + MAX_PWR_FCC_CHAN_12; + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "txpow for channel 12 is %d", + MAX_PWR_FCC_CHAN_12); + } + if (2472 == + pScan->base_channels.channel_freq_list[i]) { + pChanList->chanParam[num_channel].pwr = + MAX_PWR_FCC_CHAN_13; + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "txpow for channel 13 is %d", + MAX_PWR_FCC_CHAN_13); + } + } + + if (!ucfg_is_nan_allowed_on_freq(mac->pdev, + pChanList->chanParam[num_channel].freq)) + pChanList->chanParam[num_channel].nan_disabled = + true; + + if (CHANNEL_STATE_ENABLE != channel_state) + pChanList->chanParam[num_channel].dfsSet = + true; + + pChanList->chanParam[num_channel].quarter_rate = + is_5mhz_enabled; + + pChanList->chanParam[num_channel].half_rate = + is_10mhz_enabled; + + num_channel++; + } + } + + csr_add_social_channels(mac, pChanList, pScan, &num_channel); + + if (mac->mlme_cfg->lfr.early_stop_scan_enable) + csr_roam_sort_channel_for_early_stop(mac, pChanList, + num_channel); + else + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Early Stop Scan Feature not supported")); + + if ((mac->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_AUTO) || + (mac->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_11AC) || + (mac->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_11AC_ONLY)) { + pChanList->vht_en = true; + if (mac->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band) + pChanList->vht_24_en = true; + } + if ((mac->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_AUTO) || + (mac->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_11N) || + (mac->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_11N_ONLY)) { + pChanList->ht_en = true; + } + if ((mac->roam.configParam.uCfgDot11Mode == eCSR_CFG_DOT11_MODE_AUTO) || + (mac->roam.configParam.uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11AX) || + (mac->roam.configParam.uCfgDot11Mode == + eCSR_CFG_DOT11_MODE_11AX_ONLY)) + pChanList->he_en = true; + + msg.type = WMA_UPDATE_CHAN_LIST_REQ; + msg.reserved = 0; + msg.bodyptr = pChanList; + pChanList->numChan = num_channel; + MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG, + NO_SESSION, msg.type)); + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + qdf_mem_free(pChanList); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS csr_start(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t i; + + do { + for (i = 0; i < WLAN_MAX_VDEVS; i++) + csr_roam_state_change(mac, eCSR_ROAMING_STATE_IDLE, i); + + status = csr_roam_start(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + + mac->roam.sPendingCommands = 0; + for (i = 0; i < WLAN_MAX_VDEVS; i++) + status = csr_neighbor_roam_init(mac, i); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_warn("Couldn't Init HO control blk"); + break; + } + /* Register with scan component */ + mac->scan.requester_id = ucfg_scan_register_requester( + mac->psoc, + "CSR", csr_scan_callback, mac); + + if (mac->mlme_cfg->reg.enable_pending_chan_list_req) { + status = ucfg_scan_register_event_handler(mac->pdev, + csr_scan_event_handler, mac); + + if (QDF_IS_STATUS_ERROR(status)) + sme_err("scan event registration failed "); + } + } while (0); + return status; +} + +QDF_STATUS csr_stop(struct mac_context *mac) +{ + uint32_t sessionId; + + if (mac->mlme_cfg->reg.enable_pending_chan_list_req) + ucfg_scan_unregister_event_handler(mac->pdev, + csr_scan_event_handler, + mac); + ucfg_scan_psoc_set_disable(mac->psoc, REASON_SYSTEM_DOWN); + ucfg_scan_unregister_requester(mac->psoc, mac->scan.requester_id); + + /* + * purge all serialization commnad if there are any pending to make + * sure memory and vdev ref are freed. + */ + csr_purge_pdev_all_ser_cmd_list(mac); + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; sessionId++) + csr_roam_vdev_delete(mac, sessionId, true); + + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; sessionId++) + csr_neighbor_roam_close(mac, sessionId); + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; sessionId++) + if (CSR_IS_SESSION_VALID(mac, sessionId)) + ucfg_scan_flush_results(mac->pdev, NULL); + + /* Reset the domain back to the deault */ + mac->scan.domainIdCurrent = mac->scan.domainIdDefault; + + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; sessionId++) { + csr_roam_state_change(mac, eCSR_ROAMING_STATE_STOP, sessionId); + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_NONE, + sessionId); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS csr_ready(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + /* If the gScanAgingTime is set to '0' then scan results aging timeout + * based on timer feature is not enabled + */ + status = csr_apply_channel_and_power_list(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("csr_apply_channel_and_power_list failed status: %d", + status); + + return status; +} + +void csr_set_default_dot11_mode(struct mac_context *mac) +{ + mac->mlme_cfg->dot11_mode.dot11_mode = + csr_translate_to_wni_cfg_dot11_mode(mac, + mac->roam.configParam.uCfgDot11Mode); +} + +void csr_set_global_cfgs(struct mac_context *mac) +{ + wlan_mlme_set_frag_threshold(mac->psoc, csr_get_frag_thresh(mac)); + wlan_mlme_set_rts_threshold(mac->psoc, csr_get_rts_thresh(mac)); + /* For now we will just use the 5GHz CB mode ini parameter to decide + * whether CB supported or not in Probes when there is no session + * Once session is established we will use the session related params + * stored in PE session for CB mode + */ + if (cfg_in_range(CFG_CHANNEL_BONDING_MODE_5GHZ, + mac->roam.configParam.channelBondingMode5GHz)) + ucfg_mlme_set_channel_bonding_5ghz(mac->psoc, + mac->roam.configParam. + channelBondingMode5GHz); + if (cfg_in_range(CFG_CHANNEL_BONDING_MODE_24GHZ, + mac->roam.configParam.channelBondingMode24GHz)) + ucfg_mlme_set_channel_bonding_24ghz(mac->psoc, + mac->roam.configParam. + channelBondingMode24GHz); + + if (cfg_in_range(CFG_HEART_BEAT_THRESHOLD, + mac->roam.configParam.HeartbeatThresh24)) + mac->mlme_cfg->timeouts.heart_beat_threshold = + mac->roam.configParam.HeartbeatThresh24; + else + mac->mlme_cfg->timeouts.heart_beat_threshold = + cfg_default(CFG_HEART_BEAT_THRESHOLD); + + /* Update the operating mode to configured value during + * initialization, So that client can advertise full + * capabilities in Probe request frame. + */ + csr_set_default_dot11_mode(mac); +} + +#if defined(WLAN_LOGGING_SOCK_SVC_ENABLE) && \ + defined(FEATURE_PKTLOG) && !defined(REMOVE_PKT_LOG) +/** + * csr_packetdump_timer_handler() - packet dump timer + * handler + * @pv: user data + * + * This function is used to handle packet dump timer + * + * Return: None + * + */ +static void csr_packetdump_timer_handler(void *pv) +{ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s Invoking packetdump deregistration API", __func__); + wlan_deregister_txrx_packetdump(OL_TXRX_PDEV_ID); +} + +void csr_packetdump_timer_start(void) +{ + QDF_STATUS status; + mac_handle_t mac_handle; + struct mac_context *mac; + QDF_TIMER_STATE cur_state; + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + mac = MAC_CONTEXT(mac_handle); + if (!mac) { + QDF_ASSERT(0); + return; + } + cur_state = qdf_mc_timer_get_current_state(&mac->roam.packetdump_timer); + if (cur_state == QDF_TIMER_STATE_STARTING || + cur_state == QDF_TIMER_STATE_STARTING) { + sme_debug("packetdump_timer is already started: %d", cur_state); + return; + } + + status = qdf_mc_timer_start(&mac->roam.packetdump_timer, + (PKT_DUMP_TIMER_DURATION * + QDF_MC_TIMER_TO_SEC_UNIT) / + QDF_MC_TIMER_TO_MS_UNIT); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_debug("cannot start packetdump timer status: %d", status); +} + +void csr_packetdump_timer_stop(void) +{ + QDF_STATUS status; + mac_handle_t mac_handle; + struct mac_context *mac; + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + mac = MAC_CONTEXT(mac_handle); + if (!mac) { + QDF_ASSERT(0); + return; + } + + status = qdf_mc_timer_stop(&mac->roam.packetdump_timer); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("cannot stop packetdump timer"); +} + +static QDF_STATUS csr_packetdump_timer_init(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!mac) { + QDF_ASSERT(0); + return -EINVAL; + } + + status = qdf_mc_timer_init(&mac->roam.packetdump_timer, + QDF_TIMER_TYPE_SW, + csr_packetdump_timer_handler, + mac); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("cannot allocate memory for packetdump timer"); + return status; + } + + return status; +} + +static void csr_packetdump_timer_deinit(struct mac_context *mac) +{ + if (!mac) { + QDF_ASSERT(0); + return; + } + + qdf_mc_timer_stop(&mac->roam.packetdump_timer); + qdf_mc_timer_destroy(&mac->roam.packetdump_timer); +} +#else +static inline QDF_STATUS csr_packetdump_timer_init(struct mac_context *mac) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void csr_packetdump_timer_deinit(struct mac_context *mac) {} +#endif + +static QDF_STATUS csr_roam_open(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t i; + struct csr_roam_session *pSession; + + do { + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + pSession = CSR_GET_SESSION(mac, i); + pSession->roamingTimerInfo.mac = mac; + pSession->roamingTimerInfo.vdev_id = + WLAN_UMAC_VDEV_ID_MAX; + } + mac->roam.WaitForKeyTimerInfo.mac = mac; + mac->roam.WaitForKeyTimerInfo.vdev_id = + WLAN_UMAC_VDEV_ID_MAX; + status = qdf_mc_timer_init(&mac->roam.hTimerWaitForKey, + QDF_TIMER_TYPE_SW, + csr_roam_wait_for_key_time_out_handler, + &mac->roam.WaitForKeyTimerInfo); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("cannot allocate memory for WaitForKey time out timer"); + break; + } + status = csr_packetdump_timer_init(mac); + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + + spin_lock_init(&mac->roam.roam_state_lock); + } while (0); + return status; +} + +static QDF_STATUS csr_roam_close(struct mac_context *mac) +{ + uint32_t sessionId; + + /* + * purge all serialization commnad if there are any pending to make + * sure memory and vdev ref are freed. + */ + csr_purge_pdev_all_ser_cmd_list(mac); + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; sessionId++) + csr_roam_vdev_delete(mac, sessionId, true); + + qdf_mc_timer_stop(&mac->roam.hTimerWaitForKey); + qdf_mc_timer_destroy(&mac->roam.hTimerWaitForKey); + csr_packetdump_timer_deinit(mac); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS csr_roam_start(struct mac_context *mac) +{ + (void)mac; + return QDF_STATUS_SUCCESS; +} + +void csr_roam_stop(struct mac_context *mac, uint32_t sessionId) +{ + csr_roam_stop_roaming_timer(mac, sessionId); +} + +QDF_STATUS csr_roam_copy_connect_profile(struct mac_context *mac, + uint32_t sessionId, tCsrRoamConnectedProfile *pProfile) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t size = 0; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + tCsrRoamConnectedProfile *connected_prof; + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + if (!pProfile) { + sme_err("profile not found"); + return QDF_STATUS_E_FAILURE; + } + + if (pSession->pConnectBssDesc) { + size = pSession->pConnectBssDesc->length + + sizeof(pSession->pConnectBssDesc->length); + if (size) { + pProfile->bss_desc = qdf_mem_malloc(size); + if (pProfile->bss_desc) { + qdf_mem_copy(pProfile->bss_desc, + pSession->pConnectBssDesc, + size); + status = QDF_STATUS_SUCCESS; + } else { + return QDF_STATUS_E_FAILURE; + } + } else { + pProfile->bss_desc = NULL; + } + connected_prof = &(pSession->connectedProfile); + pProfile->AuthType = connected_prof->AuthType; + pProfile->akm_list = connected_prof->akm_list; + pProfile->EncryptionType = connected_prof->EncryptionType; + pProfile->mcEncryptionType = connected_prof->mcEncryptionType; + pProfile->BSSType = connected_prof->BSSType; + pProfile->op_freq = connected_prof->op_freq; + qdf_mem_copy(&pProfile->bssid, &connected_prof->bssid, + sizeof(struct qdf_mac_addr)); + qdf_mem_copy(&pProfile->SSID, &connected_prof->SSID, + sizeof(tSirMacSSid)); + pProfile->mdid = connected_prof->mdid; +#ifdef FEATURE_WLAN_ESE + pProfile->isESEAssoc = connected_prof->isESEAssoc; + if (csr_is_auth_type_ese(connected_prof->AuthType)) { + qdf_mem_copy(pProfile->eseCckmInfo.krk, + connected_prof->eseCckmInfo.krk, + SIR_KRK_KEY_LEN); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + qdf_mem_copy(pProfile->eseCckmInfo.btk, + connected_prof->eseCckmInfo.btk, + SIR_BTK_KEY_LEN); +#endif + pProfile->eseCckmInfo.reassoc_req_num = + connected_prof->eseCckmInfo.reassoc_req_num; + pProfile->eseCckmInfo.krk_plumbed = + connected_prof->eseCckmInfo.krk_plumbed; + } +#endif +#ifdef WLAN_FEATURE_11W + pProfile->MFPEnabled = connected_prof->MFPEnabled; + pProfile->MFPRequired = connected_prof->MFPRequired; + pProfile->MFPCapable = connected_prof->MFPCapable; +#endif + } + return status; +} + +QDF_STATUS csr_roam_get_connect_profile(struct mac_context *mac, uint32_t sessionId, + tCsrRoamConnectedProfile *pProfile) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if ((csr_is_conn_state_connected(mac, sessionId)) || + (csr_is_conn_state_ibss(mac, sessionId))) { + if (pProfile) { + status = + csr_roam_copy_connect_profile(mac, sessionId, + pProfile); + } + } + return status; +} + +void csr_roam_free_connect_profile(tCsrRoamConnectedProfile *profile) +{ + if (profile->bss_desc) + qdf_mem_free(profile->bss_desc); + if (profile->pAddIEAssoc) + qdf_mem_free(profile->pAddIEAssoc); + qdf_mem_zero(profile, sizeof(tCsrRoamConnectedProfile)); + profile->AuthType = eCSR_AUTH_TYPE_UNKNOWN; +} + +static QDF_STATUS csr_roam_free_connected_info(struct mac_context *mac, + struct csr_roam_connectedinfo * + pConnectedInfo) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (pConnectedInfo->pbFrames) { + qdf_mem_free(pConnectedInfo->pbFrames); + pConnectedInfo->pbFrames = NULL; + } + pConnectedInfo->nBeaconLength = 0; + pConnectedInfo->nAssocReqLength = 0; + pConnectedInfo->nAssocRspLength = 0; + pConnectedInfo->staId = 0; + pConnectedInfo->nRICRspLength = 0; +#ifdef FEATURE_WLAN_ESE + pConnectedInfo->nTspecIeLength = 0; +#endif + return status; +} + +void csr_release_command_roam(struct mac_context *mac, tSmeCmd *pCommand) +{ + csr_reinit_roam_cmd(mac, pCommand); +} + +void csr_release_command_wm_status_change(struct mac_context *mac, + tSmeCmd *pCommand) +{ + csr_reinit_wm_status_change_cmd(mac, pCommand); +} + +static void csr_release_command_set_hw_mode(struct mac_context *mac, + tSmeCmd *cmd) +{ + struct csr_roam_session *session; + uint32_t session_id; + + if (cmd->u.set_hw_mode_cmd.reason == + POLICY_MGR_UPDATE_REASON_HIDDEN_STA) { + session_id = cmd->u.set_hw_mode_cmd.session_id; + session = CSR_GET_SESSION(mac, session_id); + if (session) + csr_saved_scan_cmd_free_fields(mac, session); + } +} + +void csr_roam_substate_change(struct mac_context *mac, + enum csr_roam_substate NewSubstate, uint32_t sessionId) +{ + if (sessionId >= WLAN_MAX_VDEVS) { + sme_err("Invalid no of concurrent sessions %d", + sessionId); + return; + } + if (mac->roam.curSubState[sessionId] == NewSubstate) + return; + sme_nofl_debug("CSR RoamSubstate: [ %s <== %s ]", + mac_trace_getcsr_roam_sub_state(NewSubstate), + mac_trace_getcsr_roam_sub_state(mac->roam. + curSubState[sessionId])); + spin_lock(&mac->roam.roam_state_lock); + mac->roam.curSubState[sessionId] = NewSubstate; + spin_unlock(&mac->roam.roam_state_lock); +} + +enum csr_roam_state csr_roam_state_change(struct mac_context *mac, + enum csr_roam_state NewRoamState, + uint8_t sessionId) +{ + enum csr_roam_state PreviousState; + + PreviousState = mac->roam.curState[sessionId]; + + if (NewRoamState == mac->roam.curState[sessionId]) + return PreviousState; + + sme_nofl_debug("CSR RoamState[%d]: [ %s <== %s ]", sessionId, + mac_trace_getcsr_roam_state(NewRoamState), + mac_trace_getcsr_roam_state( + mac->roam.curState[sessionId])); + /* + * Whenever we transition OUT of the Roaming state, + * clear the Roaming substate. + */ + if (CSR_IS_ROAM_JOINING(mac, sessionId)) { + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_NONE, + sessionId); + } + + mac->roam.curState[sessionId] = NewRoamState; + + return PreviousState; +} + +void csr_assign_rssi_for_category(struct mac_context *mac, int8_t bestApRssi, + uint8_t catOffset) +{ + int i; + + if (catOffset) { + mac->roam.configParam.bCatRssiOffset = catOffset; + for (i = 0; i < CSR_NUM_RSSI_CAT; i++) { + mac->roam.configParam.RSSICat[CSR_NUM_RSSI_CAT - i - + 1] = + (int)bestApRssi - + mac->mlme_cfg->gen.select_5ghz_margin - + (int)(i * catOffset); + } + } +} + +static void init_config_param(struct mac_context *mac) +{ + int i; + + mac->roam.configParam.agingCount = CSR_AGING_COUNT; + mac->roam.configParam.channelBondingMode24GHz = + WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + mac->roam.configParam.channelBondingMode5GHz = + WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; + + mac->roam.configParam.phyMode = eCSR_DOT11_MODE_AUTO; + mac->roam.configParam.uCfgDot11Mode = eCSR_CFG_DOT11_MODE_AUTO; + mac->roam.configParam.HeartbeatThresh24 = 40; + mac->roam.configParam.HeartbeatThresh50 = 40; + mac->roam.configParam.Is11eSupportEnabled = true; + mac->roam.configParam.WMMSupportMode = eCsrRoamWmmAuto; + mac->roam.configParam.ProprietaryRatesEnabled = true; + for (i = 0; i < CSR_NUM_RSSI_CAT; i++) + mac->roam.configParam.BssPreferValue[i] = i; + csr_assign_rssi_for_category(mac, CSR_BEST_RSSI_VALUE, + CSR_DEFAULT_RSSI_DB_GAP); + mac->roam.configParam.statsReqPeriodicity = + CSR_MIN_GLOBAL_STAT_QUERY_PERIOD; + mac->roam.configParam.statsReqPeriodicityInPS = + CSR_MIN_GLOBAL_STAT_QUERY_PERIOD_IN_BMPS; + + mac->roam.configParam.nVhtChannelWidth = + WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ + 1; + + mac->roam.configParam.fScanTwice = false; + + /* Remove this code once SLM_Sessionization is supported */ + /* BMPS_WORKAROUND_NOT_NEEDED */ + mac->roam.configParam.doBMPSWorkaround = 0; +} + +void csr_flush_cfg_bg_scan_roam_channel_list(tCsrChannelInfo *channel_info) +{ + /* Free up the memory first (if required) */ + if (channel_info->freq_list) { + qdf_mem_free(channel_info->freq_list); + channel_info->freq_list = NULL; + channel_info->numOfChannels = 0; + } +} + +/** + * csr_flush_roam_scan_chan_lists() - Flush the roam channel lists + * @mac: Global MAC context + * @vdev_id: vdev id + * + * Flush the roam channel lists pref_chan_info and specific_chan_info. + * + * Return: None + */ +static void +csr_flush_roam_scan_chan_lists(struct mac_context *mac, uint8_t vdev_id) +{ + tCsrChannelInfo *chan_info; + + chan_info = + &mac->roam.neighborRoamInfo[vdev_id].cfgParams.pref_chan_info; + csr_flush_cfg_bg_scan_roam_channel_list(chan_info); + + chan_info = + &mac->roam.neighborRoamInfo[vdev_id].cfgParams.specific_chan_info; + csr_flush_cfg_bg_scan_roam_channel_list(chan_info); +} + +QDF_STATUS csr_create_bg_scan_roam_channel_list(struct mac_context *mac, + tCsrChannelInfo *channel_info, + const uint32_t *chan_freq_list, + const uint8_t num_chan) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t i; + + channel_info->freq_list = qdf_mem_malloc(sizeof(uint32_t) * num_chan); + if (!channel_info->freq_list) + return QDF_STATUS_E_NOMEM; + + channel_info->numOfChannels = num_chan; + for (i = 0; i < num_chan; i++) + channel_info->freq_list[i] = chan_freq_list[i]; + + return status; +} + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * csr_check_band_freq_match() - check if passed band and ch freq match + * @band: band to match with channel frequency + * @freq: freq to match with band + * + * Return: bool if match else false + */ +static bool +csr_check_band_freq_match(enum band_info band, uint32_t freq) +{ + if (band == BAND_ALL) + return true; + + if (band == BAND_2G && WLAN_REG_IS_24GHZ_CH_FREQ(freq)) + return true; + + if (band == BAND_5G && WLAN_REG_IS_5GHZ_CH_FREQ(freq)) + return true; + + return false; +} + +/** + * is_dfs_unsafe_extra_band_chan() - check if dfs unsafe or extra band channel + * @mac_ctx: MAC context + * @freq: channel freq to check + * @band: band for intra band check +.*. + * Return: bool if match else false + */ +static bool +is_dfs_unsafe_extra_band_chan(struct mac_context *mac_ctx, uint32_t freq, + enum band_info band) +{ + uint16_t unsafe_chan[NUM_CHANNELS]; + uint16_t unsafe_chan_cnt = 0; + uint16_t cnt = 0; + bool is_unsafe_chan; + qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + + if (!qdf_ctx) { + cds_err("qdf_ctx is NULL"); + return true; + } + + if ((mac_ctx->mlme_cfg->lfr.roaming_dfs_channel == + ROAMING_DFS_CHANNEL_DISABLED || + mac_ctx->roam.configParam.sta_roam_policy.dfs_mode == + CSR_STA_ROAM_POLICY_DFS_DISABLED) && + (wlan_reg_is_dfs_for_freq(mac_ctx->pdev, freq))) + return true; + + pld_get_wlan_unsafe_channel(qdf_ctx->dev, unsafe_chan, + &unsafe_chan_cnt, + sizeof(unsafe_chan)); + if (mac_ctx->roam.configParam.sta_roam_policy.skip_unsafe_channels && + unsafe_chan_cnt) { + is_unsafe_chan = false; + for (cnt = 0; cnt < unsafe_chan_cnt; cnt++) { + if (unsafe_chan[cnt] == freq) { + is_unsafe_chan = true; + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + ("ignoring unsafe channel freq %d"), + freq); + return true; + } + } + } + if (!csr_check_band_freq_match(band, freq)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + ("ignoring non-intra band freq %d"), + freq); + return true; + } + + return false; +} +#endif + +#ifdef FEATURE_WLAN_ESE +/** + * csr_create_roam_scan_channel_list() - create roam scan channel list + * @mac: Global mac pointer + * @sessionId: session id + * @chan_freq_list: pointer to channel list + * @numChannels: number of channels + * @band: band enumeration + * + * This function modifies the roam scan channel list as per AP neighbor + * report; AP neighbor report may be empty or may include only other AP + * channels; in any case, we merge the channel list with the learned occupied + * channels list. + * if the band is 2.4G, then make sure channel list contains only 2.4G + * valid channels if the band is 5G, then make sure channel list contains + * only 5G valid channels + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS csr_create_roam_scan_channel_list(struct mac_context *mac, + uint8_t sessionId, + uint32_t *chan_freq_list, + uint8_t numChannels, + const enum band_info band) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + tpCsrNeighborRoamControlInfo pNeighborRoamInfo + = &mac->roam.neighborRoamInfo[sessionId]; + uint8_t out_num_chan = 0; + uint8_t inNumChannels = numChannels; + uint32_t *in_ptr = chan_freq_list; + uint8_t i = 0; + uint32_t csr_freq_list[CFG_VALID_CHANNEL_LIST_LEN] = { 0 }; + uint32_t tmp_chan_freq_list[CFG_VALID_CHANNEL_LIST_LEN] = { 0 }; + uint8_t mergedOutputNumOfChannels = 0; + + tpCsrChannelInfo currChannelListInfo + = &pNeighborRoamInfo->roamChannelInfo.currentChannelListInfo; + /* + * Create a Union of occupied channel list learnt by the DUT along + * with the Neighbor report Channels. This increases the chances of + * the DUT to get a candidate AP while roaming even if the Neighbor + * Report is not able to provide sufficient information. + */ + if (mac->scan.occupiedChannels[sessionId].numChannels) { + csr_neighbor_roam_merge_channel_lists(mac, &mac->scan. + occupiedChannels[sessionId]. + channel_freq_list[0], mac->scan. + occupiedChannels[sessionId]. + numChannels, in_ptr, + inNumChannels, + &mergedOutputNumOfChannels); + inNumChannels = mergedOutputNumOfChannels; + } + if (BAND_2G == band) { + for (i = 0; i < inNumChannels; i++) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(in_ptr[i]) && + csr_roam_is_channel_valid(mac, in_ptr[i])) { + csr_freq_list[out_num_chan++] = in_ptr[i]; + } + } + } else if (BAND_5G == band) { + for (i = 0; i < inNumChannels; i++) { + /* Add 5G Non-DFS channel */ + if (WLAN_REG_IS_5GHZ_CH_FREQ(in_ptr[i]) && + csr_roam_is_channel_valid(mac, in_ptr[i]) && + !wlan_reg_is_dfs_for_freq(mac->pdev, in_ptr[i])) { + csr_freq_list[out_num_chan++] = in_ptr[i]; + } + } + } else if (BAND_ALL == band) { + for (i = 0; i < inNumChannels; i++) { + if (csr_roam_is_channel_valid(mac, in_ptr[i]) && + !wlan_reg_is_dfs_for_freq(mac->pdev, in_ptr[i])) { + csr_freq_list[out_num_chan++] = in_ptr[i]; + } + } + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_WARN, + "Invalid band, No operation carried out (Band %d)", + band); + return QDF_STATUS_E_INVAL; + } + /* + * if roaming within band is enabled, then select only the + * in band channels . + * This is required only if the band capability is set to ALL, + * E.g., if band capability is only 2.4G then all the channels in the + * list are already filtered for 2.4G channels, hence ignore this check + */ + if ((BAND_ALL == band) && CSR_IS_ROAM_INTRA_BAND_ENABLED(mac)) { + csr_neighbor_roam_channels_filter_by_current_band( + mac, + sessionId, + csr_freq_list, + out_num_chan, + tmp_chan_freq_list, + &out_num_chan); + } + /* Prepare final roam scan channel list */ + if (out_num_chan) { + /* Clear the channel list first */ + if (currChannelListInfo->freq_list) { + qdf_mem_free(currChannelListInfo->freq_list); + currChannelListInfo->freq_list = NULL; + currChannelListInfo->numOfChannels = 0; + } + currChannelListInfo->freq_list = + qdf_mem_malloc(out_num_chan * sizeof(uint32_t)); + if (!currChannelListInfo->freq_list) { + currChannelListInfo->numOfChannels = 0; + return QDF_STATUS_E_NOMEM; + } + for (i = 0; i < out_num_chan; i++) + currChannelListInfo->freq_list[i] = + tmp_chan_freq_list[i]; + + currChannelListInfo->numOfChannels = out_num_chan; + } + return status; +} + +/** + * csr_roam_is_ese_assoc() - is this ese association + * @mac_ctx: Global MAC context + * @session_id: session identifier + * + * Returns whether the current association is a ESE assoc or not. + * + * Return: true if ese association; false otherwise + */ +bool csr_roam_is_ese_assoc(struct mac_context *mac_ctx, uint32_t session_id) +{ + return mac_ctx->roam.neighborRoamInfo[session_id].isESEAssoc; +} + + +/** + * csr_roam_is_ese_ini_feature_enabled() - is ese feature enabled + * @mac_ctx: Global MAC context + * + * Return: true if ese feature is enabled; false otherwise + */ +bool csr_roam_is_ese_ini_feature_enabled(struct mac_context *mac) +{ + return mac->mlme_cfg->lfr.ese_enabled; +} + +/** + * csr_tsm_stats_rsp_processor() - tsm stats response processor + * @mac: Global MAC context + * @pMsg: Message pointer + * + * Return: None + */ +static void csr_tsm_stats_rsp_processor(struct mac_context *mac, void *pMsg) +{ + tAniGetTsmStatsRsp *pTsmStatsRsp = (tAniGetTsmStatsRsp *) pMsg; + + if (pTsmStatsRsp) { + /* + * Get roam Rssi request is backed up and passed back + * to the response, Extract the request message + * to fetch callback. + */ + tpAniGetTsmStatsReq reqBkp + = (tAniGetTsmStatsReq *) pTsmStatsRsp->tsmStatsReq; + + if (reqBkp) { + if (reqBkp->tsmStatsCallback) { + ((tCsrTsmStatsCallback) + (reqBkp->tsmStatsCallback))(pTsmStatsRsp-> + tsmMetrics, + reqBkp-> + pDevContext); + reqBkp->tsmStatsCallback = NULL; + } + qdf_mem_free(reqBkp); + pTsmStatsRsp->tsmStatsReq = NULL; + } else { + if (reqBkp) { + qdf_mem_free(reqBkp); + pTsmStatsRsp->tsmStatsReq = NULL; + } + } + } else { + sme_err("pTsmStatsRsp is NULL"); + } +} + +/** + * csr_send_ese_adjacent_ap_rep_ind() - ese send adjacent ap report + * @mac: Global MAC context + * @pSession: Session pointer + * + * Return: None + */ +static void csr_send_ese_adjacent_ap_rep_ind(struct mac_context *mac, + struct csr_roam_session *pSession) +{ + uint32_t roamTS2 = 0; + struct csr_roam_info *roam_info; + struct pe_session *pe_session = NULL; + uint8_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + + if (!pSession) { + sme_err("pSession is NULL"); + return; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + roamTS2 = qdf_mc_timer_get_system_time(); + roam_info->tsmRoamDelay = roamTS2 - pSession->roamTS1; + sme_debug("Bssid(" QDF_MAC_ADDR_FMT ") Roaming Delay(%u ms)", + QDF_MAC_ADDR_REF(pSession->connectedProfile.bssid.bytes), + roam_info->tsmRoamDelay); + + pe_session = pe_find_session_by_bssid(mac, + pSession->connectedProfile.bssid.bytes, + &sessionId); + if (!pe_session) { + sme_err("session %d not found", sessionId); + qdf_mem_free(roam_info); + return; + } + + pe_session->eseContext.tsm.tsmMetrics.RoamingDly + = roam_info->tsmRoamDelay; + + csr_roam_call_callback(mac, pSession->sessionId, roam_info, + 0, eCSR_ROAM_ESE_ADJ_AP_REPORT_IND, 0); + qdf_mem_free(roam_info); +} + +/** + * csr_get_tsm_stats() - get tsm stats + * @mac: Global MAC context + * @callback: TSM stats callback + * @staId: Station id + * @bssId: bssid + * @pContext: pointer to context + * @tid: traffic id + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS csr_get_tsm_stats(struct mac_context *mac, + tCsrTsmStatsCallback callback, + struct qdf_mac_addr bssId, + void *pContext, uint8_t tid) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tAniGetTsmStatsReq *pMsg = NULL; + + pMsg = qdf_mem_malloc(sizeof(tAniGetTsmStatsReq)); + if (!pMsg) { + return QDF_STATUS_E_NOMEM; + } + /* need to initiate a stats request to PE */ + pMsg->msgType = eWNI_SME_GET_TSM_STATS_REQ; + pMsg->msgLen = (uint16_t) sizeof(tAniGetTsmStatsReq); + pMsg->tid = tid; + qdf_copy_macaddr(&pMsg->bssId, &bssId); + pMsg->tsmStatsCallback = callback; + pMsg->pDevContext = pContext; + status = umac_send_mb_message_to_mac(pMsg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_debug("Failed to send down the TSM req (status=%d)", status); + /* pMsg is freed by cds_send_mb_message_to_mac */ + status = QDF_STATUS_E_FAILURE; + } + return status; +} + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * csr_fetch_ch_lst_from_received_list() - fetch channel list from received list + * and update req msg + * parameters + * @mac_ctx: global mac ctx + * @roam_info: roam info struct + * @curr_ch_lst_info: current channel list info + * @req_buf: out param, roam offload scan request packet + * + * Return: void + */ +static void +csr_fetch_ch_lst_from_received_list(struct mac_context *mac_ctx, + tpCsrNeighborRoamControlInfo roam_info, + tpCsrChannelInfo curr_ch_lst_info, + struct roam_offload_scan_req *req_buf) +{ + uint8_t i = 0; + uint8_t num_channels = 0; + uint32_t *freq_lst = NULL; + enum band_info band = BAND_ALL; + + if (curr_ch_lst_info->numOfChannels == 0) + return; + + freq_lst = curr_ch_lst_info->freq_list; + for (i = 0; i < curr_ch_lst_info->numOfChannels; i++) { + if (is_dfs_unsafe_extra_band_chan(mac_ctx, *freq_lst, band)) { + freq_lst++; + continue; + } + req_buf->ConnectedNetwork.chan_freq_cache[num_channels++] = *freq_lst; + freq_lst++; + } + req_buf->ConnectedNetwork.ChannelCount = num_channels; + req_buf->ChannelCacheType = CHANNEL_LIST_DYNAMIC; +} +#endif + +/** + * csr_set_cckm_ie() - set CCKM IE + * @mac: Global MAC context + * @sessionId: session identifier + * @pCckmIe: Pointer to input CCKM IE data + * @ccKmIeLen: Length of @pCckmIe + * + * This function stores the CCKM IE passed by the supplicant + * in a place holder data structure and this IE will be packed inside + * reassociation request + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS csr_set_cckm_ie(struct mac_context *mac, const uint8_t sessionId, + const uint8_t *pCckmIe, const uint8_t ccKmIeLen) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(pSession->suppCckmIeInfo.cckmIe, pCckmIe, ccKmIeLen); + pSession->suppCckmIeInfo.cckmIeLen = ccKmIeLen; + return status; +} + +/** + * csr_roam_read_tsf() - read TSF + * @mac: Global MAC context + * @sessionId: session identifier + * @pTimestamp: output TSF timestamp + * + * This function reads the TSF; and also add the time elapsed since + * last beacon or probe response reception from the hand off AP to arrive at + * the latest TSF value. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS csr_roam_read_tsf(struct mac_context *mac, uint8_t *pTimestamp, + uint8_t sessionId) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tCsrNeighborRoamBSSInfo handoffNode = {{0} }; + uint64_t timer_diff = 0; + uint32_t timeStamp[2]; + struct bss_description *pBssDescription = NULL; + + csr_neighbor_roam_get_handoff_ap_info(mac, &handoffNode, sessionId); + if (!handoffNode.pBssDescription) { + sme_err("Invalid BSS Description"); + return QDF_STATUS_E_INVAL; + } + pBssDescription = handoffNode.pBssDescription; + /* Get the time diff in nano seconds */ + timer_diff = (qdf_get_monotonic_boottime_ns() - + pBssDescription->scansystimensec); + /* Convert msec to micro sec timer */ + timer_diff = do_div(timer_diff, SYSTEM_TIME_NSEC_TO_USEC); + timeStamp[0] = pBssDescription->timeStamp[0]; + timeStamp[1] = pBssDescription->timeStamp[1]; + update_cckmtsf(&(timeStamp[0]), &(timeStamp[1]), &timer_diff); + qdf_mem_copy(pTimestamp, (void *)&timeStamp[0], sizeof(uint32_t) * 2); + return status; +} + +#endif /* FEATURE_WLAN_ESE */ + +/** + * csr_roam_is_roam_offload_scan_enabled() - is roam offload enabled + * @mac_ctx: Global MAC context + * + * Returns whether firmware based background scan is currently enabled or not. + * + * Return: true if roam offload scan enabled; false otherwise + */ +bool csr_roam_is_roam_offload_scan_enabled(struct mac_context *mac_ctx) +{ + return mac_ctx->mlme_cfg->lfr.roam_scan_offload_enabled; +} + +/* The funcns csr_convert_cb_ini_value_to_phy_cb_state and + * csr_convert_phy_cb_state_to_ini_value have been introduced + * to convert the ini value to the ENUM used in csr and MAC for CB state + * Ideally we should have kept the ini value and enum value same and + * representing the same cb values as in 11n standard i.e. + * Set to 1 (SCA) if the secondary channel is above the primary channel + * Set to 3 (SCB) if the secondary channel is below the primary channel + * Set to 0 (SCN) if no secondary channel is present + * However, since our driver is already distributed we will keep the ini + * definition as it is which is: + * 0 - secondary none + * 1 - secondary LOW + * 2 - secondary HIGH + * and convert to enum value used within the driver in + * csr_change_default_config_param using this funcn + * The enum values are as follows: + * PHY_SINGLE_CHANNEL_CENTERED = 0 + * PHY_DOUBLE_CHANNEL_LOW_PRIMARY = 1 + * PHY_DOUBLE_CHANNEL_HIGH_PRIMARY = 3 + */ +ePhyChanBondState csr_convert_cb_ini_value_to_phy_cb_state(uint32_t cbIniValue) +{ + + ePhyChanBondState phyCbState; + + switch (cbIniValue) { + /* secondary none */ + case eCSR_INI_SINGLE_CHANNEL_CENTERED: + phyCbState = PHY_SINGLE_CHANNEL_CENTERED; + break; + /* secondary LOW */ + case eCSR_INI_DOUBLE_CHANNEL_HIGH_PRIMARY: + phyCbState = PHY_DOUBLE_CHANNEL_HIGH_PRIMARY; + break; + /* secondary HIGH */ + case eCSR_INI_DOUBLE_CHANNEL_LOW_PRIMARY: + phyCbState = PHY_DOUBLE_CHANNEL_LOW_PRIMARY; + break; + case eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED: + phyCbState = PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED; + break; + case eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED: + phyCbState = + PHY_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED; + break; + case eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED: + phyCbState = PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED; + break; + case eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW: + phyCbState = PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW; + break; + case eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW: + phyCbState = PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW; + break; + case eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH: + phyCbState = PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH; + break; + case eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH: + phyCbState = PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH; + break; + default: + /* If an invalid value is passed, disable CHANNEL BONDING */ + phyCbState = PHY_SINGLE_CHANNEL_CENTERED; + break; + } + return phyCbState; +} + +static +uint32_t csr_convert_phy_cb_state_to_ini_value(ePhyChanBondState phyCbState) +{ + uint32_t cbIniValue; + + switch (phyCbState) { + /* secondary none */ + case PHY_SINGLE_CHANNEL_CENTERED: + cbIniValue = eCSR_INI_SINGLE_CHANNEL_CENTERED; + break; + /* secondary LOW */ + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + cbIniValue = eCSR_INI_DOUBLE_CHANNEL_HIGH_PRIMARY; + break; + /* secondary HIGH */ + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + cbIniValue = eCSR_INI_DOUBLE_CHANNEL_LOW_PRIMARY; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED: + cbIniValue = + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED: + cbIniValue = + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED: + cbIniValue = + eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW: + cbIniValue = eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW: + cbIniValue = eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH: + cbIniValue = eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH; + break; + case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH: + cbIniValue = eCSR_INI_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH; + break; + default: + /* return some invalid value */ + cbIniValue = eCSR_INI_CHANNEL_BONDING_STATE_MAX; + break; + } + return cbIniValue; +} + +#ifdef WLAN_FEATURE_11AX +#define CSR_REVISE_REQ_HE_CAP_PER_BAND(_req, _pmac, _ch_freq) \ + csr_revise_req_he_cap_per_band(&(_req)->he_config, _pmac, _ch_freq) + +static void +csr_revise_req_he_cap_per_band(tDot11fIEhe_cap *he_config, + struct mac_context *mac, + uint32_t ch_freq) +{ + if (wlan_reg_is_24ghz_ch_freq(ch_freq)) { + he_config->bfee_sts_lt_80 = + mac->he_cap_2g.bfee_sts_lt_80; + } else { + he_config->bfee_sts_lt_80 = + mac->he_cap_5g.bfee_sts_lt_80; + + he_config->num_sounding_lt_80 = + mac->he_cap_5g.num_sounding_lt_80; + if (he_config->chan_width_2 || + he_config->chan_width_3) { + he_config->bfee_sts_gt_80 = + mac->he_cap_5g.bfee_sts_gt_80; + he_config->num_sounding_gt_80 = + mac->he_cap_5g.num_sounding_gt_80; + he_config->he_ppdu_20_in_160_80p80Mhz = + mac->he_cap_5g.he_ppdu_20_in_160_80p80Mhz; + he_config->he_ppdu_80_in_160_80p80Mhz = + mac->he_cap_5g.he_ppdu_80_in_160_80p80Mhz; + } + } +} + +/** + * csr_join_req_copy_he_cap() - Copy HE cap into CSR Join Req + * @csr_join_req: pointer to CSR Join Req + * @session: pointer to CSR session + * + * Return: None + */ +static void csr_join_req_copy_he_cap(struct join_req *csr_join_req, + struct csr_roam_session *session) +{ + qdf_mem_copy(&csr_join_req->he_config, &session->he_config, + sizeof(session->he_config)); +} + +/** + * csr_start_bss_copy_he_cap() - Copy HE cap into CSR Join Req + * @req: pointer to START BSS Req + * @session: pointer to CSR session + * + * Return: None + */ +static void csr_start_bss_copy_he_cap(struct start_bss_req *req, + struct csr_roam_session *session) +{ + qdf_mem_copy(&req->he_config, &session->he_config, + sizeof(session->he_config)); +} + +void csr_init_session_twt_cap(struct csr_roam_session *session, + uint32_t type_of_persona) +{ + if (WMI_VDEV_TYPE_AP == type_of_persona) { + session->he_config.twt_request = 0; + } else if (WMI_VDEV_TYPE_STA == type_of_persona) { + session->he_config.twt_responder = 0; + } +} + +void csr_update_session_he_cap(struct mac_context *mac_ctx, + struct csr_roam_session *session) +{ + enum QDF_OPMODE persona; + tDot11fIEhe_cap *he_cap = &session->he_config; + he_cap->present = true; + + qdf_mem_copy(&session->he_config, + &mac_ctx->mlme_cfg->he_caps.dot11_he_cap, + sizeof(session->he_config)); + + /* + * Do not advertise requester role for SAP & responder role + * for STA + */ + persona = csr_get_session_persona(mac_ctx, session->sessionId); + if (QDF_SAP_MODE == persona) { + session->he_config.twt_request = 0; + } else if (QDF_STA_MODE == persona) { + session->he_config.twt_responder = 0; + } + + if (he_cap->ppet_present) { + /* till now operating channel is not decided yet, use 5g cap */ + qdf_mem_copy(he_cap->ppet.ppe_threshold.ppe_th, + mac_ctx->mlme_cfg->he_caps.he_ppet_5g, + WNI_CFG_HE_PPET_LEN); + he_cap->ppet.ppe_threshold.num_ppe_th = + lim_truncate_ppet(he_cap->ppet.ppe_threshold.ppe_th, + WNI_CFG_HE_PPET_LEN); + } else { + he_cap->ppet.ppe_threshold.num_ppe_th = 0; + } + session->he_sta_obsspd = mac_ctx->mlme_cfg->he_caps.he_sta_obsspd; +} + +#else +#define CSR_REVISE_REQ_HE_CAP_PER_BAND(_req, _pmac, _ch_freq) /* no op */ + +static inline +void csr_join_req_copy_he_cap(struct join_req *csr_join_req, + struct csr_roam_session *session) +{ +} + +static inline +void csr_start_bss_copy_he_cap(struct start_bss_req *req, + struct csr_roam_session *session) +{ +} + +#endif + +/** + * csr_set_11k_offload_config_param() - Update 11k neighbor report config + * + * @csr_config: pointer to csr_config in MAC context + * @pParam: pointer to config params from HDD + * + * Return: none + */ +static +void csr_set_11k_offload_config_param(struct csr_config *csr_config, + struct csr_config_params *param) +{ + csr_config->offload_11k_enable_bitmask = + param->offload_11k_enable_bitmask; + csr_config->neighbor_report_offload.params_bitmask = + param->neighbor_report_offload.params_bitmask; + csr_config->neighbor_report_offload.time_offset = + param->neighbor_report_offload.time_offset; + csr_config->neighbor_report_offload.low_rssi_offset = + param->neighbor_report_offload.low_rssi_offset; + csr_config->neighbor_report_offload.bmiss_count_trigger = + param->neighbor_report_offload.bmiss_count_trigger; + csr_config->neighbor_report_offload.per_threshold_offset = + param->neighbor_report_offload.per_threshold_offset; + csr_config->neighbor_report_offload. + neighbor_report_cache_timeout = + param->neighbor_report_offload. + neighbor_report_cache_timeout; + csr_config->neighbor_report_offload. + max_neighbor_report_req_cap = + param->neighbor_report_offload. + max_neighbor_report_req_cap; +} + +static void +csr_copy_mawc_config(struct mac_context *mac, + struct mawc_params *mawc_config) +{ + mawc_config->mawc_enabled = + mac->mlme_cfg->lfr.mawc_enabled; + mawc_config->mawc_roam_enabled = + mac->mlme_cfg->lfr.mawc_roam_enabled; + mawc_config->mawc_roam_traffic_threshold = + mac->mlme_cfg->lfr.mawc_roam_traffic_threshold; + mawc_config->mawc_roam_ap_rssi_threshold = + mac->mlme_cfg->lfr.mawc_roam_ap_rssi_threshold; + mawc_config->mawc_roam_rssi_high_adjust = + mac->mlme_cfg->lfr.mawc_roam_rssi_high_adjust; + mawc_config->mawc_roam_rssi_low_adjust = + mac->mlme_cfg->lfr.mawc_roam_rssi_low_adjust; +} + +QDF_STATUS csr_change_default_config_param(struct mac_context *mac, + struct csr_config_params *pParam) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (pParam) { + mac->roam.configParam.is_force_1x1 = + pParam->is_force_1x1; + mac->roam.configParam.WMMSupportMode = pParam->WMMSupportMode; + mac->mlme_cfg->wmm_params.wme_enabled = + (pParam->WMMSupportMode == eCsrRoamWmmNoQos) ? 0 : 1; + mac->roam.configParam.Is11eSupportEnabled = + pParam->Is11eSupportEnabled; + + mac->roam.configParam.fenableMCCMode = pParam->fEnableMCCMode; + mac->roam.configParam.mcc_rts_cts_prot_enable = + pParam->mcc_rts_cts_prot_enable; + mac->roam.configParam.mcc_bcast_prob_resp_enable = + pParam->mcc_bcast_prob_resp_enable; + mac->roam.configParam.fAllowMCCGODiffBI = + pParam->fAllowMCCGODiffBI; + + /* channelBondingMode5GHz plays a dual role right now + * INFRA STA will use this non zero value as CB enabled + * and SOFTAP will use this non-zero value to determine + * the secondary channel offset. This is how + * channelBondingMode5GHz works now and this is kept intact + * to avoid any cfg.ini change. + */ + if (pParam->channelBondingMode24GHz > MAX_CB_VALUE_IN_INI) + sme_warn("Invalid CB value from ini in 2.4GHz band %d, CB DISABLED", + pParam->channelBondingMode24GHz); + mac->roam.configParam.channelBondingMode24GHz = + csr_convert_cb_ini_value_to_phy_cb_state(pParam-> + channelBondingMode24GHz); + if (pParam->channelBondingMode5GHz > MAX_CB_VALUE_IN_INI) + sme_warn("Invalid CB value from ini in 5GHz band %d, CB DISABLED", + pParam->channelBondingMode5GHz); + mac->roam.configParam.channelBondingMode5GHz = + csr_convert_cb_ini_value_to_phy_cb_state(pParam-> + channelBondingMode5GHz); + mac->roam.configParam.phyMode = pParam->phyMode; + mac->roam.configParam.HeartbeatThresh24 = + mac->mlme_cfg->timeouts.heart_beat_threshold; + mac->roam.configParam.HeartbeatThresh50 = + pParam->HeartbeatThresh50; + mac->roam.configParam.ProprietaryRatesEnabled = + pParam->ProprietaryRatesEnabled; + mac->roam.configParam.ad_hoc_ch_freq_5g = + pParam->ad_hoc_ch_freq_5g; + mac->roam.configParam.ad_hoc_ch_freq_2g = + pParam->ad_hoc_ch_freq_2g; + + mac->roam.configParam.wep_tkip_in_he = pParam->wep_tkip_in_he; + + mac->roam.configParam.uCfgDot11Mode = + csr_get_cfg_dot11_mode_from_csr_phy_mode(NULL, + mac->roam.configParam. + phyMode, + mac->roam.configParam. + ProprietaryRatesEnabled); + + csr_assign_rssi_for_category(mac, + mac->mlme_cfg->lfr.first_scan_bucket_threshold, + pParam->bCatRssiOffset); + mac->roam.configParam.statsReqPeriodicity = + pParam->statsReqPeriodicity; + mac->roam.configParam.statsReqPeriodicityInPS = + pParam->statsReqPeriodicityInPS; + /* Assign this before calling csr_init11d_info */ + if (wlan_reg_11d_enabled_on_host(mac->psoc)) + status = csr_init11d_info(mac, &pParam->Csr11dinfo); + else + mac->scan.curScanType = eSIR_ACTIVE_SCAN; + + /* Initialize the power + channel information if 11h is + * enabled. If 11d is enabled this information has already + * been initialized + */ + if (csr_is11h_supported(mac) && + !wlan_reg_11d_enabled_on_host(mac->psoc)) + csr_init_channel_power_list(mac, &pParam->Csr11dinfo); + + mac->scan.fEnableDFSChnlScan = pParam->fEnableDFSChnlScan; + mac->roam.configParam.fScanTwice = pParam->fScanTwice; + /* This parameter is not available in cfg and not passed from + * upper layers. Instead it is initialized here This parametere + * is used in concurrency to determine if there are concurrent + * active sessions. Is used as a temporary fix to disconnect + * all active sessions when BMPS enabled so the active session + * if Infra STA will automatically connect back and resume BMPS + * since resume BMPS is not working when moving from concurrent + * to single session + */ + /* Remove this code once SLM_Sessionization is supported */ + /* BMPS_WORKAROUND_NOT_NEEDED */ + mac->roam.configParam.doBMPSWorkaround = 0; + mac->roam.configParam.send_smps_action = + pParam->send_smps_action; + mac->roam.configParam.disable_high_ht_mcs_2x2 = + pParam->disable_high_ht_mcs_2x2; + mac->roam.configParam.isCoalesingInIBSSAllowed = + pParam->isCoalesingInIBSSAllowed; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + mac->roam.configParam.cc_switch_mode = pParam->cc_switch_mode; +#endif + mac->roam.configParam.obssEnabled = pParam->obssEnabled; + mac->roam.configParam.vendor_vht_sap = + pParam->vendor_vht_sap; + mac->roam.configParam.conc_custom_rule1 = + pParam->conc_custom_rule1; + mac->roam.configParam.conc_custom_rule2 = + pParam->conc_custom_rule2; + mac->roam.configParam.is_sta_connection_in_5gz_enabled = + pParam->is_sta_connection_in_5gz_enabled; + + mac->isCoalesingInIBSSAllowed = + pParam->isCoalesingInIBSSAllowed; + + /* update interface configuration */ + mac->sme.max_intf_count = pParam->max_intf_count; + + mac->f_sta_miracast_mcc_rest_time_val = + pParam->f_sta_miracast_mcc_rest_time_val; +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + mac->sap.sap_channel_avoidance = + pParam->sap_channel_avoidance; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + + mac->dual_mac_feature_disable = + pParam->dual_mac_feature_disable; + mac->sta_sap_scc_on_dfs_chan = + pParam->sta_sap_scc_on_dfs_chan; + mac->roam.configParam.sta_roam_policy.dfs_mode = + pParam->sta_roam_policy_params.dfs_mode; + mac->roam.configParam.sta_roam_policy.skip_unsafe_channels = + pParam->sta_roam_policy_params.skip_unsafe_channels; + mac->roam.configParam.sta_roam_policy.sap_operating_band = + pParam->sta_roam_policy_params.sap_operating_band; + + mac->roam.configParam.enable_bcast_probe_rsp = + pParam->enable_bcast_probe_rsp; + mac->roam.configParam.is_fils_enabled = + pParam->is_fils_enabled; + mac->roam.configParam.oce_feature_bitmap = + pParam->oce_feature_bitmap; + + csr_set_11k_offload_config_param(&mac->roam.configParam, + pParam); + } + return status; +} + +/** + * csr_get_11k_offload_config_param() - Get 11k neighbor report config + * + * @csr_config: pointer to csr_config in MAC context + * @pParam: pointer to config params from HDD + * + * Return: none + */ +static +void csr_get_11k_offload_config_param(struct csr_config *csr_config, + struct csr_config_params *param) +{ + param->offload_11k_enable_bitmask = + csr_config->offload_11k_enable_bitmask; + param->neighbor_report_offload.params_bitmask = + csr_config->neighbor_report_offload.params_bitmask; + param->neighbor_report_offload.time_offset = + csr_config->neighbor_report_offload.time_offset; + param->neighbor_report_offload.low_rssi_offset = + csr_config->neighbor_report_offload.low_rssi_offset; + param->neighbor_report_offload.bmiss_count_trigger = + csr_config->neighbor_report_offload.bmiss_count_trigger; + param->neighbor_report_offload.per_threshold_offset = + csr_config->neighbor_report_offload.per_threshold_offset; + param->neighbor_report_offload.neighbor_report_cache_timeout = + csr_config->neighbor_report_offload. + neighbor_report_cache_timeout; + param->neighbor_report_offload.max_neighbor_report_req_cap = + csr_config->neighbor_report_offload. + max_neighbor_report_req_cap; +} + +QDF_STATUS csr_get_config_param(struct mac_context *mac, + struct csr_config_params *pParam) +{ + struct csr_config *cfg_params = &mac->roam.configParam; + + if (!pParam) + return QDF_STATUS_E_INVAL; + + pParam->is_force_1x1 = cfg_params->is_force_1x1; + pParam->WMMSupportMode = cfg_params->WMMSupportMode; + pParam->Is11eSupportEnabled = cfg_params->Is11eSupportEnabled; + pParam->channelBondingMode24GHz = csr_convert_phy_cb_state_to_ini_value( + cfg_params->channelBondingMode24GHz); + pParam->channelBondingMode5GHz = csr_convert_phy_cb_state_to_ini_value( + cfg_params->channelBondingMode5GHz); + pParam->phyMode = cfg_params->phyMode; + pParam->HeartbeatThresh50 = cfg_params->HeartbeatThresh50; + pParam->ProprietaryRatesEnabled = cfg_params->ProprietaryRatesEnabled; + pParam->ad_hoc_ch_freq_5g = cfg_params->ad_hoc_ch_freq_5g; + pParam->ad_hoc_ch_freq_2g = cfg_params->ad_hoc_ch_freq_2g; + pParam->bCatRssiOffset = cfg_params->bCatRssiOffset; + pParam->statsReqPeriodicity = cfg_params->statsReqPeriodicity; + pParam->statsReqPeriodicityInPS = cfg_params->statsReqPeriodicityInPS; + pParam->fEnableDFSChnlScan = mac->scan.fEnableDFSChnlScan; + pParam->fScanTwice = cfg_params->fScanTwice; + pParam->fEnableMCCMode = cfg_params->fenableMCCMode; + pParam->fAllowMCCGODiffBI = cfg_params->fAllowMCCGODiffBI; + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + pParam->cc_switch_mode = cfg_params->cc_switch_mode; +#endif + pParam->wep_tkip_in_he = cfg_params->wep_tkip_in_he; + pParam->disable_high_ht_mcs_2x2 = cfg_params->disable_high_ht_mcs_2x2; + pParam->isCoalesingInIBSSAllowed = cfg_params->isCoalesingInIBSSAllowed; + csr_set_channels(mac, pParam); + pParam->obssEnabled = cfg_params->obssEnabled; + pParam->vendor_vht_sap = + mac->roam.configParam.vendor_vht_sap; + pParam->roam_dense_min_aps = + cfg_params->roam_params.dense_min_aps_cnt; + + pParam->roam_bg_scan_bad_rssi_thresh = + cfg_params->roam_params.bg_scan_bad_rssi_thresh; + pParam->roam_bad_rssi_thresh_offset_2g = + cfg_params->roam_params.roam_bad_rssi_thresh_offset_2g; + pParam->roam_data_rssi_threshold_triggers = + cfg_params->roam_params.roam_data_rssi_threshold_triggers; + pParam->roam_data_rssi_threshold = + cfg_params->roam_params.roam_data_rssi_threshold; + pParam->rx_data_inactivity_time = + cfg_params->roam_params.rx_data_inactivity_time; + + pParam->conc_custom_rule1 = cfg_params->conc_custom_rule1; + pParam->conc_custom_rule2 = cfg_params->conc_custom_rule2; + pParam->is_sta_connection_in_5gz_enabled = + cfg_params->is_sta_connection_in_5gz_enabled; +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + pParam->sap_channel_avoidance = mac->sap.sap_channel_avoidance; +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + pParam->max_intf_count = mac->sme.max_intf_count; + pParam->dual_mac_feature_disable = + mac->dual_mac_feature_disable; + pParam->sta_sap_scc_on_dfs_chan = + mac->sta_sap_scc_on_dfs_chan; + pParam->f_sta_miracast_mcc_rest_time_val = + mac->f_sta_miracast_mcc_rest_time_val; + pParam->send_smps_action = mac->roam.configParam.send_smps_action; + pParam->sta_roam_policy_params.dfs_mode = + mac->roam.configParam.sta_roam_policy.dfs_mode; + pParam->sta_roam_policy_params.skip_unsafe_channels = + mac->roam.configParam.sta_roam_policy.skip_unsafe_channels; + pParam->enable_bcast_probe_rsp = + mac->roam.configParam.enable_bcast_probe_rsp; + pParam->is_fils_enabled = + mac->roam.configParam.is_fils_enabled; + pParam->oce_feature_bitmap = + mac->roam.configParam.oce_feature_bitmap; + + csr_get_11k_offload_config_param(&mac->roam.configParam, pParam); + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_prune_ch_list() - prunes the channel list to keep only a type of channels + * @ch_lst: existing channel list + * @is_24_GHz: indicates if 2.5 GHz or 5 GHz channels are required + * + * Return: void + */ +static void csr_prune_ch_list(struct csr_channel *ch_lst, bool is_24_GHz) +{ + uint8_t idx = 0, num_channels = 0; + + for ( ; idx < ch_lst->numChannels; idx++) { + if (is_24_GHz) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_lst->channel_freq_list[idx])) { + ch_lst->channel_freq_list[num_channels] = + ch_lst->channel_freq_list[idx]; + num_channels++; + } + } else { + if (WLAN_REG_IS_5GHZ_CH_FREQ(ch_lst->channel_freq_list[idx])) { + ch_lst->channel_freq_list[num_channels] = + ch_lst->channel_freq_list[idx]; + num_channels++; + } + } + } + /* + * Cleanup the rest of channels. Note we only need to clean up the + * channels if we had to trim the list. Calling qdf_mem_set() with a 0 + * size is going to throw asserts on the debug builds so let's be a bit + * smarter about that. Zero out the reset of the channels only if we + * need to. The amount of memory to clear is the number of channesl that + * we trimmed (ch_lst->numChannels - num_channels) times the size of a + * channel in the structure. + */ + if (ch_lst->numChannels > num_channels) { + qdf_mem_zero(&ch_lst->channel_freq_list[num_channels], + sizeof(ch_lst->channel_freq_list[0]) * + (ch_lst->numChannels - num_channels)); + } + ch_lst->numChannels = num_channels; +} + +/** + * csr_prune_channel_list_for_mode() - prunes the channel list + * @mac_ctx: global mac context + * @ch_lst: existing channel list + * + * Prunes the channel list according to band stored in mac_ctx + * + * Return: void + */ +void csr_prune_channel_list_for_mode(struct mac_context *mac_ctx, + struct csr_channel *ch_lst) +{ + /* for dual band NICs, don't need to trim the channel list.... */ + if (CSR_IS_OPEARTING_DUAL_BAND(mac_ctx)) + return; + /* + * 2.4 GHz band operation requires the channel list to be trimmed to + * the 2.4 GHz channels only + */ + if (CSR_IS_24_BAND_ONLY(mac_ctx)) + csr_prune_ch_list(ch_lst, true); + else if (CSR_IS_5G_BAND_ONLY(mac_ctx)) + csr_prune_ch_list(ch_lst, false); +} + +#define INFRA_AP_DEFAULT_CHAN_FREQ 2437 +QDF_STATUS csr_is_valid_channel(struct mac_context *mac, uint32_t freq) +{ + uint8_t index = 0; + QDF_STATUS status = QDF_STATUS_E_NOSUPPORT; + + /* regulatory check */ + for (index = 0; index < mac->scan.base_channels.numChannels; + index++) { + if (mac->scan.base_channels.channel_freq_list[index] == + freq){ + status = QDF_STATUS_SUCCESS; + break; + } + } + + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("freq %d is not available"), freq); + } + + return status; +} + +QDF_STATUS csr_get_channel_and_power_list(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t num20MHzChannelsFound = 0; + QDF_STATUS qdf_status; + uint8_t Index = 0; + + qdf_status = wlan_reg_get_channel_list_with_power_for_freq( + mac->pdev, + mac->scan.defaultPowerTable, + &num20MHzChannelsFound); + + if ((QDF_STATUS_SUCCESS != qdf_status) || + (num20MHzChannelsFound == 0)) { + sme_err("failed to get channels"); + status = QDF_STATUS_E_FAILURE; + } else { + if (num20MHzChannelsFound > CFG_VALID_CHANNEL_LIST_LEN) + num20MHzChannelsFound = CFG_VALID_CHANNEL_LIST_LEN; + mac->scan.numChannelsDefault = num20MHzChannelsFound; + /* Move the channel list to the global data */ + /* structure -- this will be used as the scan list */ + for (Index = 0; Index < num20MHzChannelsFound; Index++) + mac->scan.base_channels.channel_freq_list[Index] = + mac->scan.defaultPowerTable[Index].center_freq; + mac->scan.base_channels.numChannels = + num20MHzChannelsFound; + } + return status; +} + +QDF_STATUS csr_apply_channel_and_power_list(struct mac_context *mac) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + csr_prune_channel_list_for_mode(mac, &mac->scan.base_channels); + csr_save_channel_power_for_band(mac, false); + csr_save_channel_power_for_band(mac, true); + csr_apply_channel_power_info_to_fw(mac, + &mac->scan.base_channels, + mac->scan.countryCodeCurrent); + + csr_init_operating_classes(mac); + return status; +} + +static QDF_STATUS csr_init11d_info(struct mac_context *mac, tCsr11dinfo *ps11dinfo) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint8_t index; + uint32_t count = 0; + tSirMacChanInfo *pChanInfo; + tSirMacChanInfo *pChanInfoStart; + bool applyConfig = true; + + if (!ps11dinfo) + return status; + + if (ps11dinfo->Channels.numChannels + && (CFG_VALID_CHANNEL_LIST_LEN >= + ps11dinfo->Channels.numChannels)) { + mac->scan.base_channels.numChannels = + ps11dinfo->Channels.numChannels; + qdf_mem_copy(mac->scan.base_channels.channel_freq_list, + ps11dinfo->Channels.channel_freq_list, + ps11dinfo->Channels.numChannels); + } else { + /* No change */ + return QDF_STATUS_SUCCESS; + } + /* legacy maintenance */ + + qdf_mem_copy(mac->scan.countryCodeDefault, ps11dinfo->countryCode, + CFG_COUNTRY_CODE_LEN); + + /* Tush: at csropen get this initialized with default, + * during csr reset if this already set with some value + * no need initilaize with default again + */ + if (0 == mac->scan.countryCodeCurrent[0]) { + qdf_mem_copy(mac->scan.countryCodeCurrent, + ps11dinfo->countryCode, CFG_COUNTRY_CODE_LEN); + } + /* need to add the max power channel list */ + pChanInfo = + qdf_mem_malloc(sizeof(tSirMacChanInfo) * + CFG_VALID_CHANNEL_LIST_LEN); + if (pChanInfo) { + pChanInfoStart = pChanInfo; + for (index = 0; index < ps11dinfo->Channels.numChannels; + index++) { + pChanInfo->first_freq = ps11dinfo->ChnPower[index].first_chan_freq; + pChanInfo->numChannels = + ps11dinfo->ChnPower[index].numChannels; + pChanInfo->maxTxPower = + QDF_MIN(ps11dinfo->ChnPower[index].maxtxPower, + mac->mlme_cfg->power.max_tx_power); + pChanInfo++; + count++; + } + if (count) { + status = csr_save_to_channel_power2_g_5_g(mac, + count * + sizeof(tSirMacChanInfo), + pChanInfoStart); + } + qdf_mem_free(pChanInfoStart); + } + /* Only apply them to CFG when not in STOP state. + * Otherwise they will be applied later + */ + if (QDF_IS_STATUS_SUCCESS(status)) { + for (index = 0; index < WLAN_MAX_VDEVS; index++) { + if ((CSR_IS_SESSION_VALID(mac, index)) + && CSR_IS_ROAM_STOP(mac, index)) { + applyConfig = false; + } + } + + if (true == applyConfig) { + /* Apply the base channel list, power info, + * and set the Country code. + */ + csr_apply_channel_power_info_to_fw(mac, + &mac->scan. + base_channels, + mac->scan. + countryCodeCurrent); + } + } + return status; +} + +/* Initialize the Channel + Power List in the local cache and in the CFG */ +QDF_STATUS csr_init_channel_power_list(struct mac_context *mac, + tCsr11dinfo *ps11dinfo) +{ + uint8_t index; + uint32_t count = 0; + tSirMacChanInfo *pChanInfo; + tSirMacChanInfo *pChanInfoStart; + + if (!ps11dinfo || !mac) + return QDF_STATUS_E_FAILURE; + + pChanInfo = + qdf_mem_malloc(sizeof(tSirMacChanInfo) * + CFG_VALID_CHANNEL_LIST_LEN); + if (pChanInfo) { + pChanInfoStart = pChanInfo; + + for (index = 0; index < ps11dinfo->Channels.numChannels; + index++) { + pChanInfo->first_freq = ps11dinfo->ChnPower[index].first_chan_freq; + pChanInfo->numChannels = + ps11dinfo->ChnPower[index].numChannels; + pChanInfo->maxTxPower = + QDF_MIN(ps11dinfo->ChnPower[index].maxtxPower, + mac->mlme_cfg->power.max_tx_power); + pChanInfo++; + count++; + } + if (count) { + csr_save_to_channel_power2_g_5_g(mac, + count * + sizeof(tSirMacChanInfo), + pChanInfoStart); + } + qdf_mem_free(pChanInfoStart); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_roam_remove_duplicate_cmd_from_list()- Remove duplicate roam cmd from + * list + * + * @mac_ctx: pointer to global mac + * @vdev_id: vdev_id for the cmd + * @list: pending list from which cmd needs to be removed + * @command: cmd to be removed, can be NULL + * @roam_reason: cmd with reason to be removed + * + * Remove duplicate command from the pending list. + * + * Return: void + */ +static void csr_roam_remove_duplicate_pending_cmd_from_list( + struct mac_context *mac_ctx, + uint32_t vdev_id, + tSmeCmd *command, enum csr_roam_reason roam_reason) +{ + tListElem *entry, *next_entry; + tSmeCmd *dup_cmd; + tDblLinkList local_list; + + qdf_mem_zero(&local_list, sizeof(tDblLinkList)); + if (!QDF_IS_STATUS_SUCCESS(csr_ll_open(&local_list))) { + sme_err("failed to open list"); + return; + } + entry = csr_nonscan_pending_ll_peek_head(mac_ctx, LL_ACCESS_NOLOCK); + while (entry) { + next_entry = csr_nonscan_pending_ll_next(mac_ctx, entry, + LL_ACCESS_NOLOCK); + dup_cmd = GET_BASE_ADDR(entry, tSmeCmd, Link); + /* + * If command is not NULL remove the similar duplicate cmd for + * same reason as command. If command is NULL then check if + * roam_reason is eCsrForcedDisassoc (disconnect) and remove + * all roam command for the sessionId, else if roam_reason is + * eCsrHddIssued (connect) remove all connect (non disconenct) + * commands. + */ + if ((command && (command->vdev_id == dup_cmd->vdev_id) && + ((command->command == dup_cmd->command) && + /* + * This peermac check is required for Softap/GO + * scenarios. for STA scenario below OR check will + * suffice as command will always be NULL for + * STA scenarios + */ + (!qdf_mem_cmp(dup_cmd->u.roamCmd.peerMac, + command->u.roamCmd.peerMac, + sizeof(QDF_MAC_ADDR_SIZE))) && + ((command->u.roamCmd.roamReason == + dup_cmd->u.roamCmd.roamReason) || + (eCsrForcedDisassoc == + command->u.roamCmd.roamReason) || + (eCsrHddIssued == + command->u.roamCmd.roamReason)))) || + /* OR if pCommand is NULL */ + ((vdev_id == dup_cmd->vdev_id) && + (eSmeCommandRoam == dup_cmd->command) && + ((eCsrForcedDisassoc == roam_reason) || + (eCsrHddIssued == roam_reason && + !CSR_IS_DISCONNECT_COMMAND(dup_cmd))))) { + sme_debug("RoamReason: %d", + dup_cmd->u.roamCmd.roamReason); + /* Insert to local_list and remove later */ + csr_ll_insert_tail(&local_list, entry, + LL_ACCESS_NOLOCK); + } + entry = next_entry; + } + + while ((entry = csr_ll_remove_head(&local_list, LL_ACCESS_NOLOCK))) { + dup_cmd = GET_BASE_ADDR(entry, tSmeCmd, Link); + /* Tell caller that the command is cancelled */ + csr_roam_call_callback(mac_ctx, dup_cmd->vdev_id, NULL, + dup_cmd->u.roamCmd.roamId, + eCSR_ROAM_CANCELLED, eCSR_ROAM_RESULT_NONE); + csr_release_command(mac_ctx, dup_cmd); + } + csr_ll_close(&local_list); +} + +/** + * csr_roam_remove_duplicate_command()- Remove duplicate roam cmd + * from pending lists. + * + * @mac_ctx: pointer to global mac + * @session_id: session id for the cmd + * @command: cmd to be removed, can be null + * @roam_reason: cmd with reason to be removed + * + * Remove duplicate command from the sme and roam pending list. + * + * Return: void + */ +void csr_roam_remove_duplicate_command(struct mac_context *mac_ctx, + uint32_t session_id, tSmeCmd *command, + enum csr_roam_reason roam_reason) +{ + csr_roam_remove_duplicate_pending_cmd_from_list(mac_ctx, + session_id, command, roam_reason); +} + +/** + * csr_roam_populate_channels() - Helper function to populate channels + * @beacon_ies: pointer to beacon ie + * @roam_info: Roaming related information + * @chan1: center freq 1 + * @chan2: center freq2 + * + * This function will issue populate chan1 and chan2 based on beacon ie + * + * Return: none. + */ +static void csr_roam_populate_channels(tDot11fBeaconIEs *beacon_ies, + struct csr_roam_info *roam_info, + uint8_t *chan1, uint8_t *chan2) +{ + ePhyChanBondState phy_state; + + if (beacon_ies->VHTOperation.present) { + *chan1 = beacon_ies->VHTOperation.chan_center_freq_seg0; + *chan2 = beacon_ies->VHTOperation.chan_center_freq_seg1; + roam_info->chan_info.info = MODE_11AC_VHT80; + } else if (beacon_ies->HTInfo.present) { + if (beacon_ies->HTInfo.recommendedTxWidthSet == + eHT_CHANNEL_WIDTH_40MHZ) { + phy_state = beacon_ies->HTInfo.secondaryChannelOffset; + if (phy_state == PHY_DOUBLE_CHANNEL_LOW_PRIMARY) + *chan1 = beacon_ies->HTInfo.primaryChannel + + CSR_CB_CENTER_CHANNEL_OFFSET; + else if (phy_state == PHY_DOUBLE_CHANNEL_HIGH_PRIMARY) + *chan1 = beacon_ies->HTInfo.primaryChannel - + CSR_CB_CENTER_CHANNEL_OFFSET; + else + *chan1 = beacon_ies->HTInfo.primaryChannel; + + roam_info->chan_info.info = MODE_11NA_HT40; + } else { + *chan1 = beacon_ies->HTInfo.primaryChannel; + roam_info->chan_info.info = MODE_11NA_HT20; + } + *chan2 = 0; + } else { + *chan1 = 0; + *chan2 = 0; + roam_info->chan_info.info = MODE_11A; + } +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +static const char *csr_get_ch_width_str(uint8_t ch_width) +{ + switch (ch_width) { + CASE_RETURN_STRING(BW_20MHZ); + CASE_RETURN_STRING(BW_40MHZ); + CASE_RETURN_STRING(BW_80MHZ); + CASE_RETURN_STRING(BW_160MHZ); + CASE_RETURN_STRING(BW_80P80MHZ); + CASE_RETURN_STRING(BW_5MHZ); + CASE_RETURN_STRING(BW_10MHZ); + default: + return "Unknown"; + } +} + +static const char *csr_get_persona(enum mgmt_bss_type persona) +{ + switch (persona) { + CASE_RETURN_STRING(STA_PERSONA); + CASE_RETURN_STRING(SAP_PERSONA); + CASE_RETURN_STRING(P2P_CLIENT_PERSONA); + CASE_RETURN_STRING(P2P_GO_PERSONA); + CASE_RETURN_STRING(FTM_PERSONA); + CASE_RETURN_STRING(IBSS_PERSONA); + CASE_RETURN_STRING(MONITOR_PERSONA); + CASE_RETURN_STRING(P2P_DEVICE_PERSONA); + CASE_RETURN_STRING(NDI_PERSONA); + CASE_RETURN_STRING(WDS_PERSONA); + default: + return "Unknown"; + } +} + +static const char *csr_get_dot11_mode_str(enum mgmt_dot11_mode dot11mode) +{ + switch (dot11mode) { + CASE_RETURN_STRING(DOT11_MODE_AUTO); + CASE_RETURN_STRING(DOT11_MODE_ABG); + CASE_RETURN_STRING(DOT11_MODE_11A); + CASE_RETURN_STRING(DOT11_MODE_11B); + CASE_RETURN_STRING(DOT11_MODE_11G); + CASE_RETURN_STRING(DOT11_MODE_11N); + CASE_RETURN_STRING(DOT11_MODE_11AC); + CASE_RETURN_STRING(DOT11_MODE_11G_ONLY); + CASE_RETURN_STRING(DOT11_MODE_11N_ONLY); + CASE_RETURN_STRING(DOT11_MODE_11AC_ONLY); + CASE_RETURN_STRING(DOT11_MODE_11AX); + CASE_RETURN_STRING(DOT11_MODE_11AX_ONLY); + default: + return "Unknown"; + } +} + +static const char *csr_get_auth_type_str(uint8_t auth_type) +{ + switch (auth_type) { + CASE_RETURN_STRING(AUTH_OPEN); + CASE_RETURN_STRING(AUTH_SHARED); + CASE_RETURN_STRING(AUTH_WPA_EAP); + CASE_RETURN_STRING(AUTH_WPA_PSK); + CASE_RETURN_STRING(AUTH_WPA2_EAP); + CASE_RETURN_STRING(AUTH_WPA2_PSK); + CASE_RETURN_STRING(AUTH_WAPI_CERT); + CASE_RETURN_STRING(AUTH_WAPI_PSK); + default: + return "Unknown"; + } +} + +static const char *csr_get_encr_type_str(uint8_t encr_type) +{ + switch (encr_type) { + CASE_RETURN_STRING(ENC_MODE_OPEN); + CASE_RETURN_STRING(ENC_MODE_WEP40); + CASE_RETURN_STRING(ENC_MODE_WEP104); + CASE_RETURN_STRING(ENC_MODE_TKIP); + CASE_RETURN_STRING(ENC_MODE_AES); + CASE_RETURN_STRING(ENC_MODE_AES_GCMP); + CASE_RETURN_STRING(ENC_MODE_AES_GCMP_256); + CASE_RETURN_STRING(ENC_MODE_SMS4); + default: + return "Unknown"; + } +} + +static const uint8_t *csr_get_akm_str(uint8_t akm) +{ + switch (akm) { + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + return "Open"; + case eCSR_AUTH_TYPE_SHARED_KEY: + return "Shared Key"; + case eCSR_AUTH_TYPE_SAE: + return "SAE"; + case eCSR_AUTH_TYPE_WPA: + return "WPA"; + case eCSR_AUTH_TYPE_WPA_PSK: + return "WPA-PSK"; + case eCSR_AUTH_TYPE_WPA_NONE: + return "WPA-NONE"; + case eCSR_AUTH_TYPE_RSN: + return "EAP 802.1x"; + case eCSR_AUTH_TYPE_RSN_PSK: + return "WPA2-PSK"; + case eCSR_AUTH_TYPE_FT_RSN: + return "FT-802.1x"; + case eCSR_AUTH_TYPE_FT_RSN_PSK: + return "FT-PSK"; + case eCSR_AUTH_TYPE_CCKM_WPA: + return "WPA-CCKM"; + case eCSR_AUTH_TYPE_CCKM_RSN: + return "RSN-CCKM"; + case eCSR_AUTH_TYPE_RSN_PSK_SHA256: + return "PSK-SHA256"; + case eCSR_AUTH_TYPE_RSN_8021X_SHA256: + return "EAP 802.1x-SHA256"; + case eCSR_AUTH_TYPE_FILS_SHA256: + return "FILS-SHA256"; + case eCSR_AUTH_TYPE_FILS_SHA384: + return "FILS-SHA384"; + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + return "FILS-SHA256"; + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return "FILS-SHA384"; + case eCSR_AUTH_TYPE_DPP_RSN: + return "DPP"; + case eCSR_AUTH_TYPE_OWE: + return "OWE"; + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA256: + return "EAP Suite-B SHA256"; + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA384: + return "EAP Suite-B SHA384"; + case eCSR_AUTH_TYPE_OSEN: + return "OSEN"; + case eCSR_AUTH_TYPE_FT_SAE: + return "FT-SAE"; + case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384: + return "FT-Suite-B SHA384"; + default: + return "NONE"; + } +} + +/** + * csr_get_sta_ap_intersected_nss - Get the intersected NSS capability between + * sta and connected AP. + * @mac_ctx: Pointer to mac context + * @vdev_id: Vdev id + * + * Return: NSS value + */ +static uint8_t +csr_get_sta_ap_intersected_nss(struct mac_context *mac_ctx, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + uint8_t intrsct_nss; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, vdev_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) { + pe_warn("vdev not found for id: %d", vdev_id); + return 0; + } + wlan_vdev_obj_lock(vdev); + intrsct_nss = wlan_vdev_mlme_get_nss(vdev); + wlan_vdev_obj_unlock(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + + return intrsct_nss; +} + +static void +csr_connect_info(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct csr_roam_info *roam_info, + eCsrRoamResult u2) +{ + struct tagCsrRoamConnectedProfile *conn_profile; + struct csr_roam_profile *profile; + WLAN_HOST_DIAG_EVENT_DEF(conn_stats, + struct host_event_wlan_connection_stats); + + if (!session || !session->pCurRoamProfile || !roam_info) + return; + + conn_profile = roam_info->u.pConnectedProfile; + if (!conn_profile) + return; + profile = session->pCurRoamProfile; + qdf_mem_zero(&conn_stats, + sizeof(struct host_event_wlan_connection_stats)); + qdf_mem_copy(conn_stats.bssid, conn_profile->bssid.bytes, + QDF_MAC_ADDR_SIZE); + conn_stats.ssid_len = conn_profile->SSID.length; + if (conn_stats.ssid_len > WLAN_SSID_MAX_LEN) + conn_stats.ssid_len = WLAN_SSID_MAX_LEN; + qdf_mem_copy(conn_stats.ssid, conn_profile->SSID.ssId, + conn_stats.ssid_len); + sme_get_rssi_snr_by_bssid(MAC_HANDLE(mac_ctx), + session->pCurRoamProfile, + &conn_stats.bssid[0], + &conn_stats.rssi, NULL); + conn_stats.est_link_speed = 0; + conn_stats.chnl_bw = + diag_ch_width_from_csr_type(conn_profile->vht_channel_width); + conn_stats.dot11mode = + diag_dot11_mode_from_csr_type(conn_profile->dot11Mode); + conn_stats.bss_type = + diag_persona_from_csr_type(session->pCurRoamProfile->csrPersona); + if (conn_profile->op_freq) + conn_stats.operating_channel = + wlan_reg_freq_to_chan(mac_ctx->pdev, + conn_profile->op_freq); + + conn_stats.qos_capability = conn_profile->qosConnection; + conn_stats.auth_type = + diag_auth_type_from_csr_type(conn_profile->AuthType); + conn_stats.encryption_type = + diag_enc_type_from_csr_type(conn_profile->EncryptionType); + conn_stats.result_code = (u2 == eCSR_ROAM_RESULT_ASSOCIATED) ? 1 : 0; + conn_stats.reason_code = 0; + conn_stats.op_freq = conn_profile->op_freq; + sme_nofl_debug("+---------CONNECTION INFO START------------+"); + sme_nofl_debug("VDEV-ID: %d self_mac:"QDF_MAC_ADDR_FMT, session->vdev_id, + QDF_MAC_ADDR_REF(session->self_mac_addr.bytes)); + sme_nofl_debug("ssid: %.*s bssid: "QDF_MAC_ADDR_FMT" RSSI: %d dBm", + conn_stats.ssid_len, conn_stats.ssid, + QDF_MAC_ADDR_REF(conn_stats.bssid), conn_stats.rssi); + sme_nofl_debug("Channel Freq: %d channel_bw: %s dot11Mode: %s", conn_stats.op_freq, + csr_get_ch_width_str(conn_stats.chnl_bw), + csr_get_dot11_mode_str(conn_stats.dot11mode)); + sme_nofl_debug("AKM: %s Encry-type: %s", + csr_get_akm_str(conn_profile->AuthType), + csr_get_encr_type_str(conn_stats.encryption_type)); + sme_nofl_debug("DUT_NSS: %d | Intersected NSS:%d", session->vdev_nss, + csr_get_sta_ap_intersected_nss(mac_ctx, session->vdev_id)); + sme_nofl_debug("Qos enable: %d | Associated: %s", + conn_stats.qos_capability, + (conn_stats.result_code ? "yes" : "no")); + sme_nofl_debug("+---------CONNECTION INFO END------------+"); + + WLAN_HOST_DIAG_EVENT_REPORT(&conn_stats, EVENT_WLAN_CONN_STATS_V2); +} + +void csr_get_sta_cxn_info(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct tagCsrRoamConnectedProfile *conn_profile, + char *buf, uint32_t buf_sz) +{ + int8_t rssi = 0; + uint32_t nss, hw_mode; + struct policy_mgr_conc_connection_info *conn_info; + struct wma_txrx_node *wma_conn_table_entry = NULL; + uint32_t i = 0, len = 0, max_cxn = 0; + enum mgmt_ch_width ch_width; + enum mgmt_dot11_mode dot11mode; + enum mgmt_bss_type type; + enum mgmt_auth_type authtype; + enum mgmt_encrypt_type enctype; + + if (!session || !session->pCurRoamProfile || !conn_profile) + return; + if (!conn_profile->op_freq) + return; + qdf_mem_set(buf, buf_sz, '\0'); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tbssid: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(conn_profile->bssid.bytes)); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tssid: %.*s", conn_profile->SSID.length, + conn_profile->SSID.ssId); + sme_get_rssi_snr_by_bssid(MAC_HANDLE(mac_ctx), + session->pCurRoamProfile, + conn_profile->bssid.bytes, + &rssi, NULL); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\trssi: %d", rssi); + ch_width = diag_ch_width_from_csr_type(conn_profile->vht_channel_width); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tbw: %s", csr_get_ch_width_str(ch_width)); + dot11mode = diag_dot11_mode_from_csr_type(conn_profile->dot11Mode); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tdot11mode: %s", + csr_get_dot11_mode_str(dot11mode)); + type = diag_persona_from_csr_type(session->pCurRoamProfile->csrPersona); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tbss_type: %s", csr_get_persona(type)); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tch_freq: %d", conn_profile->op_freq); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tQoS: %d", conn_profile->qosConnection); + authtype = diag_auth_type_from_csr_type(conn_profile->AuthType); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tauth_type: %s", + csr_get_auth_type_str(authtype)); + enctype = diag_enc_type_from_csr_type(conn_profile->EncryptionType); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tencry_type: %s", + csr_get_encr_type_str(enctype)); + conn_info = policy_mgr_get_conn_info(&max_cxn); + for (i = 0; i < max_cxn; i++) + if ((conn_info->vdev_id == session->sessionId) && + (conn_info->in_use)) + break; + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tmac: %d", conn_info->mac); + wma_conn_table_entry = wma_get_interface_by_vdev_id(session->sessionId); + if (wma_conn_table_entry) + nss = wma_conn_table_entry->nss; + else + nss = 0; + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\torig_nss: %dx%d neg_nss: %dx%d", + conn_info->original_nss, conn_info->original_nss, + nss, nss); + hw_mode = policy_mgr_is_current_hwmode_dbs(mac_ctx->psoc); + len += qdf_scnprintf(buf + len, buf_sz - len, + "\n\tis_current_hw_mode_dbs: %s", + ((hw_mode != 0) ? "yes" : "no")); +} +#else +static void csr_connect_info(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct csr_roam_info *roam_info, + eCsrRoamResult u2) +{} + +#endif + +QDF_STATUS csr_roam_call_callback(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_info *roam_info, + uint32_t roamId, + eRoamCmdStatus u1, eCsrRoamResult u2) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + + WLAN_HOST_DIAG_EVENT_DEF(connectionStatus, + host_event_wlan_status_payload_type); +#endif + struct csr_roam_session *pSession; + tDot11fBeaconIEs *beacon_ies = NULL; + uint8_t chan1, chan2; + uint32_t chan_freq; + + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + sme_err("Session ID: %d is not valid", sessionId); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + pSession = CSR_GET_SESSION(mac, sessionId); + + if (false == pSession->sessionActive) { + sme_debug("Session is not Active"); + return QDF_STATUS_E_FAILURE; + } + + if (eCSR_ROAM_ASSOCIATION_COMPLETION == u1 && + eCSR_ROAM_RESULT_ASSOCIATED == u2 && roam_info) { + sme_debug("Assoc complete result: %d status: %d reason: %d", + u2, roam_info->status_code, roam_info->reasonCode); + beacon_ies = qdf_mem_malloc(sizeof(tDot11fBeaconIEs)); + if ((beacon_ies) && (roam_info->bss_desc)) { + status = csr_parse_bss_description_ies( + mac, roam_info->bss_desc, + beacon_ies); + csr_roam_populate_channels(beacon_ies, roam_info, + &chan1, &chan2); + if (0 != chan1) + roam_info->chan_info.band_center_freq1 = + cds_chan_to_freq(chan1); + else + roam_info->chan_info.band_center_freq1 = 0; + if (0 != chan2) + roam_info->chan_info.band_center_freq2 = + cds_chan_to_freq(chan2); + else + roam_info->chan_info.band_center_freq2 = 0; + } else { + roam_info->chan_info.band_center_freq1 = 0; + roam_info->chan_info.band_center_freq2 = 0; + roam_info->chan_info.info = 0; + } + roam_info->chan_info.mhz = roam_info->u.pConnectedProfile->op_freq; + chan_freq = roam_info->chan_info.mhz; + roam_info->chan_info.reg_info_1 = + (csr_get_cfg_max_tx_power(mac, chan_freq) << 16); + roam_info->chan_info.reg_info_2 = + (csr_get_cfg_max_tx_power(mac, chan_freq) << 8); + qdf_mem_free(beacon_ies); + } else if ((u1 == eCSR_ROAM_FT_REASSOC_FAILED) + && (pSession->bRefAssocStartCnt)) { + /* + * Decrement bRefAssocStartCnt for FT reassoc failure. + * Reason: For FT reassoc failures, we first call + * csr_roam_call_callback before notifying a failed roam + * completion through csr_roam_complete. The latter in + * turn calls csr_roam_process_results which tries to + * once again call csr_roam_call_callback if bRefAssocStartCnt + * is non-zero. Since this is redundant for FT reassoc + * failure, decrement bRefAssocStartCnt. + */ + pSession->bRefAssocStartCnt--; + } else if (roam_info && (u1 == eCSR_ROAM_SET_CHANNEL_RSP) + && (u2 == eCSR_ROAM_RESULT_CHANNEL_CHANGE_SUCCESS)) { + pSession->connectedProfile.op_freq = + roam_info->channelChangeRespEvent->new_op_freq; + } + + if (eCSR_ROAM_ASSOCIATION_COMPLETION == u1) + csr_connect_info(mac, pSession, roam_info, u2); + + if (mac->session_roam_complete_cb) { + if (roam_info) + roam_info->sessionId = (uint8_t) sessionId; + status = mac->session_roam_complete_cb(mac->psoc, sessionId, roam_info, + roamId, u1, u2); + } + /* + * EVENT_WLAN_STATUS_V2: eCSR_ROAM_ASSOCIATION_COMPLETION, + * eCSR_ROAM_LOSTLINK, + * eCSR_ROAM_DISASSOCIATED, + */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + qdf_mem_zero(&connectionStatus, + sizeof(host_event_wlan_status_payload_type)); + + if ((eCSR_ROAM_ASSOCIATION_COMPLETION == u1) + && (eCSR_ROAM_RESULT_ASSOCIATED == u2) && roam_info) { + connectionStatus.eventId = eCSR_WLAN_STATUS_CONNECT; + connectionStatus.bssType = + roam_info->u.pConnectedProfile->BSSType; + + if (roam_info->bss_desc) { + connectionStatus.rssi = + roam_info->bss_desc->rssi * (-1); + connectionStatus.channel = + wlan_reg_freq_to_chan( + mac->pdev, + roam_info->bss_desc->chan_freq); + } + mac->mlme_cfg->sta.current_rssi = connectionStatus.rssi; + + connectionStatus.qosCapability = + roam_info->u.pConnectedProfile->qosConnection; + connectionStatus.authType = + (uint8_t) diag_auth_type_from_csr_type( + roam_info->u.pConnectedProfile->AuthType); + connectionStatus.encryptionType = + (uint8_t) diag_enc_type_from_csr_type( + roam_info->u.pConnectedProfile->EncryptionType); + qdf_mem_copy(connectionStatus.ssid, + roam_info->u.pConnectedProfile->SSID.ssId, + roam_info->u.pConnectedProfile->SSID.length); + + connectionStatus.reason = eCSR_REASON_UNSPECIFIED; + qdf_mem_copy(&mac->sme.eventPayload, &connectionStatus, + sizeof(host_event_wlan_status_payload_type)); + WLAN_HOST_DIAG_EVENT_REPORT(&connectionStatus, + EVENT_WLAN_STATUS_V2); + } + if ((eCSR_ROAM_MIC_ERROR_IND == u1) + || (eCSR_ROAM_RESULT_MIC_FAILURE == u2)) { + qdf_mem_copy(&connectionStatus, &mac->sme.eventPayload, + sizeof(host_event_wlan_status_payload_type)); + connectionStatus.rssi = mac->mlme_cfg->sta.current_rssi; + connectionStatus.eventId = eCSR_WLAN_STATUS_DISCONNECT; + connectionStatus.reason = eCSR_REASON_MIC_ERROR; + WLAN_HOST_DIAG_EVENT_REPORT(&connectionStatus, + EVENT_WLAN_STATUS_V2); + } + if (eCSR_ROAM_RESULT_FORCED == u2) { + qdf_mem_copy(&connectionStatus, &mac->sme.eventPayload, + sizeof(host_event_wlan_status_payload_type)); + connectionStatus.rssi = mac->mlme_cfg->sta.current_rssi; + connectionStatus.eventId = eCSR_WLAN_STATUS_DISCONNECT; + connectionStatus.reason = eCSR_REASON_USER_REQUESTED; + WLAN_HOST_DIAG_EVENT_REPORT(&connectionStatus, + EVENT_WLAN_STATUS_V2); + } + if (eCSR_ROAM_RESULT_DISASSOC_IND == u2) { + qdf_mem_copy(&connectionStatus, &mac->sme.eventPayload, + sizeof(host_event_wlan_status_payload_type)); + connectionStatus.rssi = mac->mlme_cfg->sta.current_rssi; + connectionStatus.eventId = eCSR_WLAN_STATUS_DISCONNECT; + connectionStatus.reason = eCSR_REASON_DISASSOC; + if (roam_info) + connectionStatus.reasonDisconnect = + roam_info->reasonCode; + + WLAN_HOST_DIAG_EVENT_REPORT(&connectionStatus, + EVENT_WLAN_STATUS_V2); + } + if (eCSR_ROAM_RESULT_DEAUTH_IND == u2) { + qdf_mem_copy(&connectionStatus, &mac->sme.eventPayload, + sizeof(host_event_wlan_status_payload_type)); + connectionStatus.rssi = mac->mlme_cfg->sta.current_rssi; + connectionStatus.eventId = eCSR_WLAN_STATUS_DISCONNECT; + connectionStatus.reason = eCSR_REASON_DEAUTH; + if (roam_info) + connectionStatus.reasonDisconnect = + roam_info->reasonCode; + WLAN_HOST_DIAG_EVENT_REPORT(&connectionStatus, + EVENT_WLAN_STATUS_V2); + } +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + + return status; +} + +static bool csr_peer_mac_match_cmd(tSmeCmd *sme_cmd, + struct qdf_mac_addr peer_macaddr) +{ + if (sme_cmd->command == eSmeCommandRoam && + (sme_cmd->u.roamCmd.roamReason == eCsrForcedDisassocSta || + sme_cmd->u.roamCmd.roamReason == eCsrForcedDeauthSta) && + !qdf_mem_cmp(peer_macaddr.bytes, sme_cmd->u.roamCmd.peerMac, + QDF_MAC_ADDR_SIZE)) + return true; + + if (sme_cmd->command == eSmeCommandWmStatusChange) { + struct wmstatus_changecmd *wms_cmd; + + wms_cmd = &sme_cmd->u.wmStatusChangeCmd; + if (wms_cmd->Type == eCsrDisassociated && + !qdf_mem_cmp(peer_macaddr.bytes, + wms_cmd->u.DisassocIndMsg.peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE)) + return true; + + if (wms_cmd->Type == eCsrDeauthenticated && + !qdf_mem_cmp(peer_macaddr.bytes, + wms_cmd->u.DeauthIndMsg.peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE)) + return true; + } + + return false; +} + +static bool +csr_is_deauth_disassoc_in_pending_q(struct mac_context *mac_ctx, + uint8_t vdev_id, + struct qdf_mac_addr peer_macaddr) +{ + tListElem *entry = NULL; + tSmeCmd *sme_cmd; + + entry = csr_nonscan_pending_ll_peek_head(mac_ctx, LL_ACCESS_NOLOCK); + while (entry) { + sme_cmd = GET_BASE_ADDR(entry, tSmeCmd, Link); + if ((sme_cmd->vdev_id == vdev_id) && + csr_peer_mac_match_cmd(sme_cmd, peer_macaddr)) + return true; + entry = csr_nonscan_pending_ll_next(mac_ctx, entry, + LL_ACCESS_NOLOCK); + } + + return false; +} + +static bool +csr_is_deauth_disassoc_in_active_q(struct mac_context *mac_ctx, + uint8_t vdev_id, + struct qdf_mac_addr peer_macaddr) +{ + tSmeCmd *sme_cmd; + + sme_cmd = wlan_serialization_get_active_cmd(mac_ctx->psoc, vdev_id, + WLAN_SER_CMD_FORCE_DEAUTH_STA); + + if (sme_cmd && csr_peer_mac_match_cmd(sme_cmd, peer_macaddr)) + return true; + + sme_cmd = wlan_serialization_get_active_cmd(mac_ctx->psoc, vdev_id, + WLAN_SER_CMD_FORCE_DISASSOC_STA); + + if (sme_cmd && csr_peer_mac_match_cmd(sme_cmd, peer_macaddr)) + return true; + + /* + * WLAN_SER_CMD_WM_STATUS_CHANGE is of two type, the handling + * should take care of both the types. + */ + sme_cmd = wlan_serialization_get_active_cmd(mac_ctx->psoc, vdev_id, + WLAN_SER_CMD_WM_STATUS_CHANGE); + if (sme_cmd && csr_peer_mac_match_cmd(sme_cmd, peer_macaddr)) + return true; + + return false; +} + +/* + * csr_is_deauth_disassoc_already_active() - Function to check if deauth or + * disassoc is already in progress. + * @mac_ctx: Global MAC context + * @vdev_id: vdev id + * @peer_macaddr: Peer MAC address + * + * Return: True if deauth/disassoc indication can be dropped + * else false + */ +static bool +csr_is_deauth_disassoc_already_active(struct mac_context *mac_ctx, + uint8_t vdev_id, + struct qdf_mac_addr peer_macaddr) +{ + bool ret = csr_is_deauth_disassoc_in_pending_q(mac_ctx, + vdev_id, + peer_macaddr); + if (!ret) + /** + * commands are not found in pending queue, check in active + * queue as well + */ + ret = csr_is_deauth_disassoc_in_active_q(mac_ctx, + vdev_id, + peer_macaddr); + + if (ret) + sme_debug("Deauth/Disassoc already in progress for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_macaddr.bytes)); + + return ret; +} + +static void csr_roam_issue_disconnect_stats(struct mac_context *mac, + uint32_t session_id, + struct qdf_mac_addr peer_mac) +{ + tSmeCmd *cmd; + + cmd = csr_get_command_buffer(mac); + if (!cmd) { + sme_err(" fail to get command buffer"); + return; + } + + cmd->command = eSmeCommandGetdisconnectStats; + cmd->vdev_id = session_id; + qdf_mem_copy(&cmd->u.disconnect_stats_cmd.peer_mac_addr, &peer_mac, + QDF_MAC_ADDR_SIZE); + if (QDF_IS_STATUS_ERROR(csr_queue_sme_command(mac, cmd, true))) + sme_err("fail to queue get disconnect stats"); +} + +/** + * csr_roam_issue_disassociate_sta_cmd() - disassociate a associated station + * @sessionId: Session Id for Soft AP + * @p_del_sta_params: Pointer to parameters of the station to disassoc + * + * CSR function that HDD calls to delete a associated station + * + * Return: QDF_STATUS_SUCCESS on success or another QDF_STATUS_* on error + */ +QDF_STATUS csr_roam_issue_disassociate_sta_cmd(struct mac_context *mac, + uint32_t sessionId, + struct csr_del_sta_params + *p_del_sta_params) + +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSmeCmd *pCommand; + + do { + if (csr_is_deauth_disassoc_already_active(mac, sessionId, + p_del_sta_params->peerMacAddr)) + break; + pCommand = csr_get_command_buffer(mac); + if (!pCommand) { + sme_err("fail to get command buffer"); + status = QDF_STATUS_E_RESOURCES; + break; + } + pCommand->command = eSmeCommandRoam; + pCommand->vdev_id = (uint8_t) sessionId; + pCommand->u.roamCmd.roamReason = eCsrForcedDisassocSta; + qdf_mem_copy(pCommand->u.roamCmd.peerMac, + p_del_sta_params->peerMacAddr.bytes, + sizeof(pCommand->u.roamCmd.peerMac)); + pCommand->u.roamCmd.reason = + (tSirMacReasonCodes)p_del_sta_params->reason_code; + + csr_roam_issue_disconnect_stats( + mac, sessionId, + p_del_sta_params->peerMacAddr); + + status = csr_queue_sme_command(mac, pCommand, true); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("fail to send message status: %d", status); + } while (0); + + return status; +} + +/** + * csr_roam_issue_deauthSta() - delete a associated station + * @sessionId: Session Id for Soft AP + * @pDelStaParams: Pointer to parameters of the station to deauthenticate + * + * CSR function that HDD calls to delete a associated station + * + * Return: QDF_STATUS_SUCCESS on success or another QDF_STATUS_** on error + */ +QDF_STATUS csr_roam_issue_deauth_sta_cmd(struct mac_context *mac, + uint32_t sessionId, + struct csr_del_sta_params *pDelStaParams) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSmeCmd *pCommand; + + do { + if (csr_is_deauth_disassoc_already_active(mac, sessionId, + pDelStaParams->peerMacAddr)) + break; + pCommand = csr_get_command_buffer(mac); + if (!pCommand) { + sme_err("fail to get command buffer"); + status = QDF_STATUS_E_RESOURCES; + break; + } + pCommand->command = eSmeCommandRoam; + pCommand->vdev_id = (uint8_t) sessionId; + pCommand->u.roamCmd.roamReason = eCsrForcedDeauthSta; + qdf_mem_copy(pCommand->u.roamCmd.peerMac, + pDelStaParams->peerMacAddr.bytes, + sizeof(tSirMacAddr)); + pCommand->u.roamCmd.reason = + (tSirMacReasonCodes)pDelStaParams->reason_code; + + csr_roam_issue_disconnect_stats(mac, sessionId, + pDelStaParams->peerMacAddr); + + status = csr_queue_sme_command(mac, pCommand, true); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("fail to send message status: %d", status); + } while (0); + + return status; +} + +static +QDF_STATUS csr_roam_issue_deauth(struct mac_context *mac, uint32_t sessionId, + enum csr_roam_substate NewSubstate) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct qdf_mac_addr bssId = QDF_MAC_ADDR_BCAST_INIT; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + if (pSession->pConnectBssDesc) { + qdf_mem_copy(bssId.bytes, pSession->pConnectBssDesc->bssId, + sizeof(struct qdf_mac_addr)); + } + sme_debug("Deauth to Bssid " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(bssId.bytes)); + csr_roam_substate_change(mac, NewSubstate, sessionId); + + status = + csr_send_mb_deauth_req_msg(mac, sessionId, bssId.bytes, + eSIR_MAC_DEAUTH_LEAVING_BSS_REASON); + if (QDF_IS_STATUS_SUCCESS(status)) + csr_roam_link_down(mac, sessionId); + else { + sme_err("csr_send_mb_deauth_req_msg failed with status %d Session ID: %d" + QDF_MAC_ADDR_FMT, status, sessionId, + QDF_MAC_ADDR_REF(bssId.bytes)); + } + + return status; +} + +QDF_STATUS csr_roam_save_connected_bss_desc(struct mac_context *mac, + uint32_t sessionId, + struct bss_description *bss_desc) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + uint32_t size; + + if (!pSession) { + sme_err(" session %d not found ", sessionId); + return QDF_STATUS_E_FAILURE; + } + /* If no BSS description was found in this connection + * (happens with start IBSS), then nix the BSS description + * that we keep around for the connected BSS) and get out. + */ + if (!bss_desc) { + csr_free_connect_bss_desc(mac, sessionId); + } else { + size = bss_desc->length + sizeof(bss_desc->length); + if (pSession->pConnectBssDesc) { + if (((pSession->pConnectBssDesc->length) + + sizeof(pSession->pConnectBssDesc->length)) < + size) { + /* not enough room for the new BSS, + * mac->roam.pConnectBssDesc is freed inside + */ + csr_free_connect_bss_desc(mac, sessionId); + } + } + if (!pSession->pConnectBssDesc) + pSession->pConnectBssDesc = qdf_mem_malloc(size); + + if (!pSession->pConnectBssDesc) + status = QDF_STATUS_E_NOMEM; + else + qdf_mem_copy(pSession->pConnectBssDesc, bss_desc, size); + } + return status; +} + +static +QDF_STATUS csr_roam_prepare_bss_config(struct mac_context *mac, + struct csr_roam_profile *pProfile, + struct bss_description *bss_desc, + struct bss_config_param *pBssConfig, + tDot11fBeaconIEs *pIes) +{ + enum csr_cfgdot11mode cfgDot11Mode; + uint32_t join_timeout; + + QDF_ASSERT(pIes); + if (!pIes) + return QDF_STATUS_E_FAILURE; + + qdf_mem_copy(&pBssConfig->BssCap, &bss_desc->capabilityInfo, + sizeof(tSirMacCapabilityInfo)); + /* get qos */ + pBssConfig->qosType = csr_get_qos_from_bss_desc(mac, bss_desc, pIes); + /* Take SSID always from profile */ + qdf_mem_copy(&pBssConfig->SSID.ssId, + pProfile->SSIDs.SSIDList->SSID.ssId, + pProfile->SSIDs.SSIDList->SSID.length); + pBssConfig->SSID.length = pProfile->SSIDs.SSIDList->SSID.length; + + if (csr_is_nullssid(pBssConfig->SSID.ssId, pBssConfig->SSID.length)) { + sme_warn("BSS desc SSID is a wild card"); + /* Return failed if profile doesn't have an SSID either. */ + if (pProfile->SSIDs.numOfSSIDs == 0) { + sme_warn("BSS desc and profile doesn't have SSID"); + return QDF_STATUS_E_FAILURE; + } + } + + if (WLAN_REG_IS_5GHZ_CH_FREQ(bss_desc->chan_freq)) + pBssConfig->band = REG_BAND_5G; + else if (WLAN_REG_IS_24GHZ_CH_FREQ(bss_desc->chan_freq)) + pBssConfig->band = REG_BAND_2G; + else if (WLAN_REG_IS_6GHZ_CHAN_FREQ(bss_desc->chan_freq)) + pBssConfig->band = REG_BAND_6G; + else + return QDF_STATUS_E_FAILURE; + + /* phymode */ + if (csr_is_phy_mode_match(mac, pProfile->phyMode, bss_desc, + pProfile, &cfgDot11Mode, pIes)) { + pBssConfig->uCfgDot11Mode = cfgDot11Mode; + } else { + /* + * No matching phy mode found, force to 11b/g based on INI for + * 2.4Ghz and to 11a mode for 5Ghz + */ + sme_warn("Can not find match phy mode"); + if (REG_BAND_2G == pBssConfig->band) { + if (mac->roam.configParam.phyMode & + (eCSR_DOT11_MODE_11b | eCSR_DOT11_MODE_11b_ONLY)) { + pBssConfig->uCfgDot11Mode = + eCSR_CFG_DOT11_MODE_11B; + } else { + pBssConfig->uCfgDot11Mode = + eCSR_CFG_DOT11_MODE_11G; + } + } else if (pBssConfig->band == REG_BAND_5G) { + pBssConfig->uCfgDot11Mode = eCSR_CFG_DOT11_MODE_11A; + } else if (pBssConfig->band == REG_BAND_6G) { + pBssConfig->uCfgDot11Mode = + eCSR_CFG_DOT11_MODE_11AX_ONLY; + } + } + + sme_debug("phyMode=%d, uCfgDot11Mode=%d negotiatedAuthType %d", + pProfile->phyMode, pBssConfig->uCfgDot11Mode, + pProfile->negotiatedAuthType); + + /* Qos */ + if ((pBssConfig->uCfgDot11Mode != eCSR_CFG_DOT11_MODE_11N) && + (mac->roam.configParam.WMMSupportMode == eCsrRoamWmmNoQos)) { + /* + * Joining BSS is not 11n capable and WMM is disabled on client. + * Disable QoS and WMM + */ + pBssConfig->qosType = eCSR_MEDIUM_ACCESS_DCF; + } + + if (((pBssConfig->uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11N) + || (pBssConfig->uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11AC)) + && ((pBssConfig->qosType != eCSR_MEDIUM_ACCESS_WMM_eDCF_DSCP) + || (pBssConfig->qosType != eCSR_MEDIUM_ACCESS_11e_HCF) + || (pBssConfig->qosType != eCSR_MEDIUM_ACCESS_11e_eDCF))) { + /* Joining BSS is 11n capable and WMM is disabled on AP. */ + /* Assume all HT AP's are QOS AP's and enable WMM */ + pBssConfig->qosType = eCSR_MEDIUM_ACCESS_WMM_eDCF_DSCP; + } + /* auth type */ + switch (pProfile->negotiatedAuthType) { + default: + case eCSR_AUTH_TYPE_WPA: + case eCSR_AUTH_TYPE_WPA_PSK: + case eCSR_AUTH_TYPE_WPA_NONE: + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + pBssConfig->authType = eSIR_OPEN_SYSTEM; + break; + case eCSR_AUTH_TYPE_SHARED_KEY: + pBssConfig->authType = eSIR_SHARED_KEY; + break; + case eCSR_AUTH_TYPE_AUTOSWITCH: + pBssConfig->authType = eSIR_AUTO_SWITCH; + break; + case eCSR_AUTH_TYPE_SAE: + case eCSR_AUTH_TYPE_FT_SAE: + pBssConfig->authType = eSIR_AUTH_TYPE_SAE; + break; + } + /* short slot time */ + if (eCSR_CFG_DOT11_MODE_11B != cfgDot11Mode) + pBssConfig->uShortSlotTime = + mac->mlme_cfg->ht_caps.short_slot_time_enabled; + else + pBssConfig->uShortSlotTime = 0; + + if (pBssConfig->BssCap.ibss) + /* We don't support 11h on IBSS */ + pBssConfig->f11hSupport = false; + else + pBssConfig->f11hSupport = + mac->mlme_cfg->gen.enabled_11h; + /* power constraint */ + pBssConfig->uPowerLimit = + csr_get11h_power_constraint(mac, &pIes->PowerConstraints); + /* heartbeat */ + if (CSR_IS_11A_BSS(bss_desc)) + pBssConfig->uHeartBeatThresh = + mac->roam.configParam.HeartbeatThresh50; + else + pBssConfig->uHeartBeatThresh = + mac->roam.configParam.HeartbeatThresh24; + + /* + * Join timeout: if we find a BeaconInterval in the BssDescription, + * then set the Join Timeout to be 10 x the BeaconInterval. + */ + pBssConfig->uJoinTimeOut = cfg_default(CFG_JOIN_FAILURE_TIMEOUT); + if (bss_desc->beaconInterval) { + /* Make sure it is bigger than the minimal */ + join_timeout = QDF_MAX(10 * bss_desc->beaconInterval, + cfg_min(CFG_JOIN_FAILURE_TIMEOUT)); + if (join_timeout < pBssConfig->uJoinTimeOut) + pBssConfig->uJoinTimeOut = join_timeout; + } + + /* validate CB */ + if ((pBssConfig->uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11N) || + (pBssConfig->uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11N_ONLY) || + (pBssConfig->uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11AC) || + (pBssConfig->uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11AC_ONLY) || + (pBssConfig->uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11AX) || + (pBssConfig->uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11AX_ONLY)) + pBssConfig->cbMode = csr_get_cb_mode_from_ies( + mac, bss_desc->chan_freq, pIes); + else + pBssConfig->cbMode = PHY_SINGLE_CHANNEL_CENTERED; + + if (WLAN_REG_IS_24GHZ_CH_FREQ(bss_desc->chan_freq) && + pProfile->force_24ghz_in_ht20) { + pBssConfig->cbMode = PHY_SINGLE_CHANNEL_CENTERED; + sme_debug("force_24ghz_in_ht20 is set so set cbmode to 0"); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS csr_roam_prepare_bss_config_from_profile( + struct mac_context *mac, struct csr_roam_profile *pProfile, + struct bss_config_param *pBssConfig, + struct bss_description *bss_desc) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t bss_op_ch_freq = 0; + uint8_t qAPisEnabled = false; + /* SSID */ + pBssConfig->SSID.length = 0; + if (pProfile->SSIDs.numOfSSIDs) { + /* only use the first one */ + qdf_mem_copy(&pBssConfig->SSID, + &pProfile->SSIDs.SSIDList[0].SSID, + sizeof(tSirMacSSid)); + } else { + /* SSID must present */ + return QDF_STATUS_E_FAILURE; + } + /* Settomg up the capabilities */ + if (csr_is_bss_type_ibss(pProfile->BSSType)) + pBssConfig->BssCap.ibss = 1; + else + pBssConfig->BssCap.ess = 1; + + if (eCSR_ENCRYPT_TYPE_NONE != + pProfile->EncryptionType.encryptionType[0]) + pBssConfig->BssCap.privacy = 1; + + /* Update when 6G support is added for IBSS / NDI */ + pBssConfig->band = (mac->mlme_cfg->gen.band == BAND_2G ? + REG_BAND_2G : REG_BAND_5G); + /* phymode */ + if (pProfile->ChannelInfo.freq_list) + bss_op_ch_freq = pProfile->ChannelInfo.freq_list[0]; + pBssConfig->uCfgDot11Mode = csr_roam_get_phy_mode_band_for_bss( + mac, pProfile, bss_op_ch_freq, + &pBssConfig->band); + /* QOS */ + /* Is this correct to always set to this // *** */ + if (pBssConfig->BssCap.ess == 1) { + /*For Softap case enable WMM */ + if (CSR_IS_INFRA_AP(pProfile) + && (eCsrRoamWmmNoQos != + mac->roam.configParam.WMMSupportMode)) { + qAPisEnabled = true; + } else + if (csr_roam_get_qos_info_from_bss(mac, bss_desc) == + QDF_STATUS_SUCCESS) { + qAPisEnabled = true; + } else { + qAPisEnabled = false; + } + } else { + qAPisEnabled = true; + } + if ((eCsrRoamWmmNoQos != mac->roam.configParam.WMMSupportMode && + qAPisEnabled) || + ((eCSR_CFG_DOT11_MODE_11N == pBssConfig->uCfgDot11Mode && + qAPisEnabled))) { + pBssConfig->qosType = eCSR_MEDIUM_ACCESS_WMM_eDCF_DSCP; + } else { + pBssConfig->qosType = eCSR_MEDIUM_ACCESS_DCF; + } + + /* auth type */ + /* Take the preferred Auth type. */ + switch (pProfile->AuthType.authType[0]) { + default: + case eCSR_AUTH_TYPE_WPA: + case eCSR_AUTH_TYPE_WPA_PSK: + case eCSR_AUTH_TYPE_WPA_NONE: + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + pBssConfig->authType = eSIR_OPEN_SYSTEM; + break; + case eCSR_AUTH_TYPE_SHARED_KEY: + pBssConfig->authType = eSIR_SHARED_KEY; + break; + case eCSR_AUTH_TYPE_AUTOSWITCH: + pBssConfig->authType = eSIR_AUTO_SWITCH; + break; + case eCSR_AUTH_TYPE_SAE: + case eCSR_AUTH_TYPE_FT_SAE: + pBssConfig->authType = eSIR_AUTH_TYPE_SAE; + break; + } + /* short slot time */ + if (WNI_CFG_PHY_MODE_11B != pBssConfig->uCfgDot11Mode) { + pBssConfig->uShortSlotTime = + mac->mlme_cfg->ht_caps.short_slot_time_enabled; + } else { + pBssConfig->uShortSlotTime = 0; + } + /* power constraint. We don't support 11h on IBSS */ + pBssConfig->f11hSupport = false; + pBssConfig->uPowerLimit = 0; + /* heartbeat */ + if (REG_BAND_5G == pBssConfig->band || + REG_BAND_6G == pBssConfig->band) { + pBssConfig->uHeartBeatThresh = + mac->roam.configParam.HeartbeatThresh50; + } else { + pBssConfig->uHeartBeatThresh = + mac->roam.configParam.HeartbeatThresh24; + } + /* Join timeout */ + pBssConfig->uJoinTimeOut = cfg_default(CFG_JOIN_FAILURE_TIMEOUT); + + return status; +} + +static QDF_STATUS +csr_roam_get_qos_info_from_bss(struct mac_context *mac, + struct bss_description *bss_desc) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + tDot11fBeaconIEs *pIes = NULL; + + do { + if (!QDF_IS_STATUS_SUCCESS( + csr_get_parsed_bss_description_ies( + mac, bss_desc, &pIes))) { + sme_err("csr_get_parsed_bss_description_ies() failed"); + break; + } + /* check if the AP is QAP & it supports APSD */ + if (CSR_IS_QOS_BSS(pIes)) + status = QDF_STATUS_SUCCESS; + } while (0); + + if (pIes) + qdf_mem_free(pIes); + + return status; +} + +static void csr_reset_cfg_privacy(struct mac_context *mac) +{ + uint8_t Key0[WLAN_CRYPTO_KEY_WEP104_LEN] = {0}; + uint8_t Key1[WLAN_CRYPTO_KEY_WEP104_LEN] = {0}; + uint8_t Key2[WLAN_CRYPTO_KEY_WEP104_LEN] = {0}; + uint8_t Key3[WLAN_CRYPTO_KEY_WEP104_LEN] = {0}; + struct wlan_mlme_wep_cfg *wep_params = &mac->mlme_cfg->wep_params; + + mlme_set_wep_key(wep_params, MLME_WEP_DEFAULT_KEY_1, Key0, + WLAN_CRYPTO_KEY_WEP104_LEN); + mlme_set_wep_key(wep_params, MLME_WEP_DEFAULT_KEY_2, Key1, + WLAN_CRYPTO_KEY_WEP104_LEN); + mlme_set_wep_key(wep_params, MLME_WEP_DEFAULT_KEY_3, Key2, + WLAN_CRYPTO_KEY_WEP104_LEN); + mlme_set_wep_key(wep_params, MLME_WEP_DEFAULT_KEY_4, Key3, + WLAN_CRYPTO_KEY_WEP104_LEN); +} + +void csr_set_cfg_privacy(struct mac_context *mac, struct csr_roam_profile *pProfile, + bool fPrivacy) +{ + /* + * the only difference between this function and + * the csr_set_cfg_privacyFromProfile() is the setting of the privacy + * CFG based on the advertised privacy setting from the AP for WPA + * associations. See note below in this function... + */ + uint32_t privacy_enabled = 0, rsn_enabled = 0, wep_default_key_id = 0; + uint32_t WepKeyLength = WLAN_CRYPTO_KEY_WEP40_LEN; + uint32_t Key0Length = 0, Key1Length = 0, Key2Length = 0, Key3Length = 0; + + /* Reserve for the biggest key */ + uint8_t Key0[WLAN_CRYPTO_KEY_WEP104_LEN]; + uint8_t Key1[WLAN_CRYPTO_KEY_WEP104_LEN]; + uint8_t Key2[WLAN_CRYPTO_KEY_WEP104_LEN]; + uint8_t Key3[WLAN_CRYPTO_KEY_WEP104_LEN]; + + struct wlan_mlme_wep_cfg *wep_params = &mac->mlme_cfg->wep_params; + + switch (pProfile->negotiatedUCEncryptionType) { + case eCSR_ENCRYPT_TYPE_NONE: + /* for NO encryption, turn off Privacy and Rsn. */ + privacy_enabled = 0; + rsn_enabled = 0; + /* clear out the WEP keys that may be hanging around. */ + Key0Length = 0; + Key1Length = 0; + Key2Length = 0; + Key3Length = 0; + break; + + case eCSR_ENCRYPT_TYPE_WEP40_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP40: + + /* Privacy is ON. NO RSN for Wep40 static key. */ + privacy_enabled = 1; + rsn_enabled = 0; + /* Set the Wep default key ID. */ + wep_default_key_id = pProfile->Keys.defaultIndex; + /* Wep key size if 5 bytes (40 bits). */ + WepKeyLength = WLAN_CRYPTO_KEY_WEP40_LEN; + /* + * set encryption keys in the CFG database or + * clear those that are not present in this profile. + */ + if (pProfile->Keys.KeyLength[0]) { + qdf_mem_copy(Key0, + pProfile->Keys.KeyMaterial[0], + WLAN_CRYPTO_KEY_WEP40_LEN); + Key0Length = WLAN_CRYPTO_KEY_WEP40_LEN; + } else { + Key0Length = 0; + } + + if (pProfile->Keys.KeyLength[1]) { + qdf_mem_copy(Key1, + pProfile->Keys.KeyMaterial[1], + WLAN_CRYPTO_KEY_WEP40_LEN); + Key1Length = WLAN_CRYPTO_KEY_WEP40_LEN; + } else { + Key1Length = 0; + } + + if (pProfile->Keys.KeyLength[2]) { + qdf_mem_copy(Key2, + pProfile->Keys.KeyMaterial[2], + WLAN_CRYPTO_KEY_WEP40_LEN); + Key2Length = WLAN_CRYPTO_KEY_WEP40_LEN; + } else { + Key2Length = 0; + } + + if (pProfile->Keys.KeyLength[3]) { + qdf_mem_copy(Key3, + pProfile->Keys.KeyMaterial[3], + WLAN_CRYPTO_KEY_WEP40_LEN); + Key3Length = WLAN_CRYPTO_KEY_WEP40_LEN; + } else { + Key3Length = 0; + } + break; + + case eCSR_ENCRYPT_TYPE_WEP104_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP104: + + /* Privacy is ON. NO RSN for Wep40 static key. */ + privacy_enabled = 1; + rsn_enabled = 0; + /* Set the Wep default key ID. */ + wep_default_key_id = pProfile->Keys.defaultIndex; + /* Wep key size if 13 bytes (104 bits). */ + WepKeyLength = WLAN_CRYPTO_KEY_WEP104_LEN; + /* + * set encryption keys in the CFG database or clear + * those that are not present in this profile. + */ + if (pProfile->Keys.KeyLength[0]) { + qdf_mem_copy(Key0, + pProfile->Keys.KeyMaterial[0], + WLAN_CRYPTO_KEY_WEP104_LEN); + Key0Length = WLAN_CRYPTO_KEY_WEP104_LEN; + } else { + Key0Length = 0; + } + + if (pProfile->Keys.KeyLength[1]) { + qdf_mem_copy(Key1, + pProfile->Keys.KeyMaterial[1], + WLAN_CRYPTO_KEY_WEP104_LEN); + Key1Length = WLAN_CRYPTO_KEY_WEP104_LEN; + } else { + Key1Length = 0; + } + + if (pProfile->Keys.KeyLength[2]) { + qdf_mem_copy(Key2, + pProfile->Keys.KeyMaterial[2], + WLAN_CRYPTO_KEY_WEP104_LEN); + Key2Length = WLAN_CRYPTO_KEY_WEP104_LEN; + } else { + Key2Length = 0; + } + + if (pProfile->Keys.KeyLength[3]) { + qdf_mem_copy(Key3, + pProfile->Keys.KeyMaterial[3], + WLAN_CRYPTO_KEY_WEP104_LEN); + Key3Length = WLAN_CRYPTO_KEY_WEP104_LEN; + } else { + Key3Length = 0; + } + break; + + case eCSR_ENCRYPT_TYPE_TKIP: + case eCSR_ENCRYPT_TYPE_AES: + case eCSR_ENCRYPT_TYPE_AES_GCMP: + case eCSR_ENCRYPT_TYPE_AES_GCMP_256: +#ifdef FEATURE_WLAN_WAPI + case eCSR_ENCRYPT_TYPE_WPI: +#endif /* FEATURE_WLAN_WAPI */ + /* + * this is the only difference between this function and + * the csr_set_cfg_privacyFromProfile(). + * (setting of the privacy CFG based on the advertised + * privacy setting from AP for WPA/WAPI associations). + */ + privacy_enabled = (0 != fPrivacy); + /* turn on RSN enabled for WPA associations */ + rsn_enabled = 1; + /* clear static WEP keys that may be hanging around. */ + Key0Length = 0; + Key1Length = 0; + Key2Length = 0; + Key3Length = 0; + break; + default: + privacy_enabled = 0; + rsn_enabled = 0; + break; + } + + mac->mlme_cfg->wep_params.is_privacy_enabled = privacy_enabled; + mac->mlme_cfg->feature_flags.enable_rsn = rsn_enabled; + mlme_set_wep_key(wep_params, MLME_WEP_DEFAULT_KEY_1, Key0, Key0Length); + mlme_set_wep_key(wep_params, MLME_WEP_DEFAULT_KEY_2, Key1, Key1Length); + mlme_set_wep_key(wep_params, MLME_WEP_DEFAULT_KEY_3, Key2, Key2Length); + mlme_set_wep_key(wep_params, MLME_WEP_DEFAULT_KEY_4, Key3, Key3Length); + mac->mlme_cfg->wep_params.wep_default_key_id = wep_default_key_id; +} + +static void csr_set_cfg_ssid(struct mac_context *mac, tSirMacSSid *pSSID) +{ + uint32_t len = 0; + + if (pSSID->length <= WLAN_SSID_MAX_LEN) + len = pSSID->length; + else + len = WLAN_SSID_MAX_LEN; + + qdf_mem_copy(mac->mlme_cfg->sap_cfg.cfg_ssid, + (uint8_t *)pSSID->ssId, len); + mac->mlme_cfg->sap_cfg.cfg_ssid_len = len; + +} + +static QDF_STATUS csr_set_qos_to_cfg(struct mac_context *mac, uint32_t sessionId, + eCsrMediaAccessType qosType) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t QoSEnabled; + uint32_t WmeEnabled; + /* set the CFG enable/disable variables based on the + * qosType being configured. + */ + switch (qosType) { + case eCSR_MEDIUM_ACCESS_WMM_eDCF_802dot1p: + QoSEnabled = false; + WmeEnabled = true; + break; + case eCSR_MEDIUM_ACCESS_WMM_eDCF_DSCP: + QoSEnabled = false; + WmeEnabled = true; + break; + case eCSR_MEDIUM_ACCESS_WMM_eDCF_NoClassify: + QoSEnabled = false; + WmeEnabled = true; + break; + case eCSR_MEDIUM_ACCESS_11e_eDCF: + QoSEnabled = true; + WmeEnabled = false; + break; + case eCSR_MEDIUM_ACCESS_11e_HCF: + QoSEnabled = true; + WmeEnabled = false; + break; + default: + case eCSR_MEDIUM_ACCESS_DCF: + QoSEnabled = false; + WmeEnabled = false; + break; + } + /* save the WMM setting for later use */ + mac->roam.roamSession[sessionId].fWMMConnection = (bool) WmeEnabled; + mac->roam.roamSession[sessionId].fQOSConnection = (bool) QoSEnabled; + return status; +} + +static QDF_STATUS csr_get_rate_set(struct mac_context *mac, + struct csr_roam_profile *pProfile, + eCsrPhyMode phyMode, + struct bss_description *bss_desc, + tDot11fBeaconIEs *pIes, + tSirMacRateSet *pOpRateSet, + tSirMacRateSet *pExRateSet) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + int i; + enum csr_cfgdot11mode cfgDot11Mode; + uint8_t *pDstRate; + uint16_t rateBitmap = 0; + + qdf_mem_zero(pOpRateSet, sizeof(tSirMacRateSet)); + qdf_mem_zero(pExRateSet, sizeof(tSirMacRateSet)); + QDF_ASSERT(pIes); + + if (!pIes) { + sme_err("failed to parse BssDesc"); + return status; + } + + csr_is_phy_mode_match(mac, phyMode, bss_desc, pProfile, + &cfgDot11Mode, pIes); + /* + * Originally, we thought that for 11a networks, the 11a rates + * are always in the Operational Rate set & for 11b and 11g + * networks, the 11b rates appear in the Operational Rate set. + * Consequently, in either case, we would blindly put the rates + * we support into our Operational Rate set. + * (including the basic rates, which we've already verified are + * supported earlier in the roaming decision). + * However, it turns out that this is not always the case. + * Some AP's (e.g. D-Link DI-784) ram 11g rates into the + * Operational Rate set too. Now, we're a little more careful. + */ + pDstRate = pOpRateSet->rate; + if (pIes->SuppRates.present) { + for (i = 0; i < pIes->SuppRates.num_rates; i++) { + if (csr_rates_is_dot11_rate_supported(mac, + pIes->SuppRates.rates[i]) && + !csr_check_rate_bitmap( + pIes->SuppRates.rates[i], + rateBitmap)) { + csr_add_rate_bitmap(pIes->SuppRates. + rates[i], &rateBitmap); + *pDstRate++ = pIes->SuppRates.rates[i]; + pOpRateSet->numRates++; + } + } + } + /* + * If there are Extended Rates in the beacon, we will reflect the + * extended rates that we support in our Extended Operational Rate + * set. + */ + if (pIes->ExtSuppRates.present) { + pDstRate = pExRateSet->rate; + for (i = 0; i < pIes->ExtSuppRates.num_rates; i++) { + if (csr_rates_is_dot11_rate_supported(mac, + pIes->ExtSuppRates.rates[i]) && + !csr_check_rate_bitmap( + pIes->ExtSuppRates.rates[i], + rateBitmap)) { + *pDstRate++ = pIes->ExtSuppRates.rates[i]; + pExRateSet->numRates++; + } + } + } + if (pOpRateSet->numRates > 0 || pExRateSet->numRates > 0) + status = QDF_STATUS_SUCCESS; + return status; +} + +static void csr_set_cfg_rate_set(struct mac_context *mac, eCsrPhyMode phyMode, + struct csr_roam_profile *pProfile, + struct bss_description *bss_desc, + tDot11fBeaconIEs *pIes) +{ + int i; + uint8_t *pDstRate; + enum csr_cfgdot11mode cfgDot11Mode; + /* leave enough room for the max number of rates */ + uint8_t OperationalRates[CSR_DOT11_SUPPORTED_RATES_MAX]; + qdf_size_t OperationalRatesLength = 0; + /* leave enough room for the max number of rates */ + uint8_t ExtendedOperationalRates + [CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX]; + qdf_size_t ExtendedOperationalRatesLength = 0; + uint8_t MCSRateIdxSet[SIZE_OF_SUPPORTED_MCS_SET]; + qdf_size_t MCSRateLength = 0; + + QDF_ASSERT(pIes); + if (pIes) { + csr_is_phy_mode_match(mac, phyMode, bss_desc, pProfile, + &cfgDot11Mode, pIes); + /* Originally, we thought that for 11a networks, the 11a rates + * are always in the Operational Rate set & for 11b and 11g + * networks, the 11b rates appear in the Operational Rate set. + * Consequently, in either case, we would blindly put the rates + * we support into our Operational Rate set (including the basic + * rates, which we have already verified are supported earlier + * in the roaming decision). However, it turns out that this is + * not always the case. Some AP's (e.g. D-Link DI-784) ram 11g + * rates into the Operational Rate set, too. Now, we're a + * little more careful: + */ + pDstRate = OperationalRates; + if (pIes->SuppRates.present) { + for (i = 0; i < pIes->SuppRates.num_rates; i++) { + if (csr_rates_is_dot11_rate_supported + (mac, pIes->SuppRates.rates[i]) + && (OperationalRatesLength < + CSR_DOT11_SUPPORTED_RATES_MAX)) { + *pDstRate++ = pIes->SuppRates.rates[i]; + OperationalRatesLength++; + } + } + } + if (eCSR_CFG_DOT11_MODE_11G == cfgDot11Mode || + eCSR_CFG_DOT11_MODE_11N == cfgDot11Mode || + eCSR_CFG_DOT11_MODE_ABG == cfgDot11Mode) { + /* If there are Extended Rates in the beacon, we will + * reflect those extended rates that we support in out + * Extended Operational Rate set: + */ + pDstRate = ExtendedOperationalRates; + if (pIes->ExtSuppRates.present) { + for (i = 0; i < pIes->ExtSuppRates.num_rates; + i++) { + if (csr_rates_is_dot11_rate_supported + (mac, pIes->ExtSuppRates. + rates[i]) + && (ExtendedOperationalRatesLength < + CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX)) { + *pDstRate++ = + pIes->ExtSuppRates. + rates[i]; + ExtendedOperationalRatesLength++; + } + } + } + } + /* Enable proprietary MAC features if peer node is Airgo node + * and STA user wants to use them For ANI network companions, + * we need to populate the proprietary rate set with any + * proprietary rates we found in the beacon, only if user allows + * them. + */ + /* No proprietary modes... */ + /* Get MCS Rate */ + pDstRate = MCSRateIdxSet; + if (pIes->HTCaps.present) { + for (i = 0; i < VALID_MAX_MCS_INDEX; i++) { + if ((unsigned int)pIes->HTCaps. + supportedMCSSet[0] & (1 << i)) { + MCSRateLength++; + *pDstRate++ = i; + } + } + } + /* Set the operational rate set CFG variables... */ + wlan_mlme_set_cfg_str(OperationalRates, + &mac->mlme_cfg->rates.opr_rate_set, + OperationalRatesLength); + wlan_mlme_set_cfg_str(ExtendedOperationalRates, + &mac->mlme_cfg->rates.ext_opr_rate_set, + ExtendedOperationalRatesLength); + wlan_mlme_set_cfg_str(MCSRateIdxSet, + &mac->mlme_cfg->rates.current_mcs_set, + MCSRateLength); + } /* Parsing BSSDesc */ + else + sme_err("failed to parse BssDesc"); +} + +static void csr_set_cfg_rate_set_from_profile(struct mac_context *mac, + struct csr_roam_profile *pProfile) +{ + tSirMacRateSetIE DefaultSupportedRates11a = { WLAN_ELEMID_RATES, + {8, + {SIR_MAC_RATE_6, + SIR_MAC_RATE_9, + SIR_MAC_RATE_12, + SIR_MAC_RATE_18, + SIR_MAC_RATE_24, + SIR_MAC_RATE_36, + SIR_MAC_RATE_48, + SIR_MAC_RATE_54} } }; + tSirMacRateSetIE DefaultSupportedRates11b = { WLAN_ELEMID_RATES, + {4, + {SIR_MAC_RATE_1, + SIR_MAC_RATE_2, + SIR_MAC_RATE_5_5, + SIR_MAC_RATE_11} } }; + enum csr_cfgdot11mode cfgDot11Mode; + enum reg_wifi_band band; + /* leave enough room for the max number of rates */ + uint8_t OperationalRates[CSR_DOT11_SUPPORTED_RATES_MAX]; + qdf_size_t OperationalRatesLength = 0; + /* leave enough room for the max number of rates */ + uint8_t ExtendedOperationalRates + [CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX]; + qdf_size_t ExtendedOperationalRatesLength = 0; + uint32_t bss_op_ch_freq = 0; + + if (pProfile->ChannelInfo.freq_list) + bss_op_ch_freq = pProfile->ChannelInfo.freq_list[0]; + cfgDot11Mode = csr_roam_get_phy_mode_band_for_bss(mac, pProfile, + bss_op_ch_freq, + &band); + /* For 11a networks, the 11a rates go into the Operational Rate set. + * For 11b and 11g networks, the 11b rates appear in the Operational + * Rate set. In either case, we can blindly put the rates we support + * into our Operational Rate set (including the basic rates, which we + * have already verified are supported earlier in the roaming decision). + */ + if (REG_BAND_5G == band) { + /* 11a rates into the Operational Rate Set. */ + OperationalRatesLength = + DefaultSupportedRates11a.supportedRateSet.numRates * + sizeof(*DefaultSupportedRates11a.supportedRateSet.rate); + qdf_mem_copy(OperationalRates, + DefaultSupportedRates11a.supportedRateSet.rate, + OperationalRatesLength); + + /* Nothing in the Extended rate set. */ + ExtendedOperationalRatesLength = 0; + } else if (eCSR_CFG_DOT11_MODE_11B == cfgDot11Mode) { + /* 11b rates into the Operational Rate Set. */ + OperationalRatesLength = + DefaultSupportedRates11b.supportedRateSet.numRates * + sizeof(*DefaultSupportedRates11b.supportedRateSet.rate); + qdf_mem_copy(OperationalRates, + DefaultSupportedRates11b.supportedRateSet.rate, + OperationalRatesLength); + /* Nothing in the Extended rate set. */ + ExtendedOperationalRatesLength = 0; + } else { + /* 11G */ + + /* 11b rates into the Operational Rate Set. */ + OperationalRatesLength = + DefaultSupportedRates11b.supportedRateSet.numRates * + sizeof(*DefaultSupportedRates11b.supportedRateSet.rate); + qdf_mem_copy(OperationalRates, + DefaultSupportedRates11b.supportedRateSet.rate, + OperationalRatesLength); + + /* 11a rates go in the Extended rate set. */ + ExtendedOperationalRatesLength = + DefaultSupportedRates11a.supportedRateSet.numRates * + sizeof(*DefaultSupportedRates11a.supportedRateSet.rate); + qdf_mem_copy(ExtendedOperationalRates, + DefaultSupportedRates11a.supportedRateSet.rate, + ExtendedOperationalRatesLength); + } + + /* Set the operational rate set CFG variables... */ + wlan_mlme_set_cfg_str(OperationalRates, + &mac->mlme_cfg->rates.opr_rate_set, + OperationalRatesLength); + wlan_mlme_set_cfg_str(ExtendedOperationalRates, + &mac->mlme_cfg->rates.ext_opr_rate_set, + ExtendedOperationalRatesLength); +} + +static void csr_roam_ccm_cfg_set_callback(struct mac_context *mac, + uint8_t session_id) +{ + tListElem *pEntry = + csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + uint32_t sessionId; + tSmeCmd *pCommand = NULL; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + struct csr_roam_session *pSession = NULL; +#endif + if (!pEntry) { + sme_err("CFG_CNF with active list empty"); + return; + } + pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + sessionId = pCommand->vdev_id; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + pSession = &mac->roam.roamSession[sessionId]; + if (pSession->roam_synch_in_progress) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR3:csr_roam_cfg_set_callback"); + } +#endif + + if (CSR_IS_ROAM_JOINING(mac, sessionId) + && CSR_IS_ROAM_SUBSTATE_CONFIG(mac, sessionId)) { + csr_roaming_state_config_cnf_processor(mac, pCommand, + session_id); + } +} + +/* pIes may be NULL */ +QDF_STATUS csr_roam_set_bss_config_cfg(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *bss_desc, + struct bss_config_param *pBssConfig, + struct sDot11fBeaconIEs *pIes, + bool resetCountry) +{ + uint32_t cfgCb = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + uint32_t chan_freq = 0; + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + /* Make sure we have the domain info for the BSS we try to connect to. + * Do we need to worry about sequence for OSs that are not Windows?? + */ + if (bss_desc) { + if ((QDF_SAP_MODE != + csr_get_session_persona(mac, sessionId)) && + csr_learn_11dcountry_information( + mac, bss_desc, pIes, true)) { + csr_apply_country_information(mac); + } + if ((wlan_reg_11d_enabled_on_host(mac->psoc)) && pIes) { + if (!pIes->Country.present) + csr_apply_channel_power_info_wrapper(mac); + } + } + /* Qos */ + csr_set_qos_to_cfg(mac, sessionId, pBssConfig->qosType); + /* SSID */ + csr_set_cfg_ssid(mac, &pBssConfig->SSID); + + /* Auth type */ + mac->mlme_cfg->wep_params.auth_type = pBssConfig->authType; + + /* encryption type */ + csr_set_cfg_privacy(mac, pProfile, (bool) pBssConfig->BssCap.privacy); + /* short slot time */ + mac->mlme_cfg->feature_flags.enable_short_slot_time_11g = + pBssConfig->uShortSlotTime; + + mac->mlme_cfg->power.local_power_constraint = pBssConfig->uPowerLimit; + /* CB */ + + if (CSR_IS_INFRA_AP(pProfile) || CSR_IS_IBSS(pProfile)) + chan_freq = pProfile->op_freq; + else if (bss_desc) + chan_freq = bss_desc->chan_freq; + if (0 != chan_freq) { + /* for now if we are on 2.4 Ghz, CB will be always disabled */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq)) + cfgCb = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; + else + cfgCb = pBssConfig->cbMode; + } + /* Rate */ + /* Fixed Rate */ + if (bss_desc) + csr_set_cfg_rate_set(mac, (eCsrPhyMode) pProfile->phyMode, + pProfile, bss_desc, pIes); + else + csr_set_cfg_rate_set_from_profile(mac, pProfile); + + mac->mlme_cfg->timeouts.join_failure_timeout = + pBssConfig->uJoinTimeOut; + /* Any roaming related changes should be above this line */ + if (pSession && pSession->roam_synch_in_progress) { + sme_debug("Roam synch is in progress Session_id: %d", + sessionId); + return QDF_STATUS_SUCCESS; + } + /* Make this the last CFG to set. The callback will trigger a + * join_req Join time out + */ + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_CONFIG, sessionId); + + csr_roam_ccm_cfg_set_callback(mac, sessionId); + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_check_for_hidden_ssid_match() - Check if the current connected SSID + * is hidden ssid and if it matches with the roamed AP ssid. + * @mac: Global mac context pointer + * @session: csr session pointer + * @roamed_bss_desc: pointer to bss descriptor of roamed bss + * @roamed_bss_ies: Roamed AP beacon/probe IEs pointer + * + * Return: True if the SSID is hidden and matches with roamed SSID else false + */ +static bool +csr_check_for_hidden_ssid_match(struct mac_context *mac, + struct csr_roam_session *session, + struct bss_description *roamed_bss_desc, + tDot11fBeaconIEs *roamed_bss_ies) +{ + QDF_STATUS status; + bool is_null_ssid_match = false; + tDot11fBeaconIEs *connected_profile_ies = NULL; + + if (!session || !roamed_bss_ies) + return false; + + status = csr_get_parsed_bss_description_ies(mac, + session->pConnectBssDesc, + &connected_profile_ies); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Unable to get IES"); + goto error; + } + + if (!csr_is_nullssid(connected_profile_ies->SSID.ssid, + connected_profile_ies->SSID.num_ssid)) + goto error; + + /* + * After roam synch indication is received, the driver compares + * the SSID of the current AP and SSID of the roamed AP. If + * there is a mismatch, driver issues disassociate to current + * connected AP. This causes data path queues to be stopped and + * M2 to the roamed AP from userspace will fail if EAPOL is + * offloaded to userspace. The SSID of the current AP is + * parsed from the beacon IEs stored in the connected bss + * description. In hidden ssid case the SSID IE has 0 length + * and the host receives unicast probe with SSID of the + * AP in the roam synch indication. So SSID mismatch happens + * and validation fails. So fetch if the connected bss + * description has hidden ssid, fill the ssid from the + * csr_session connected_profile structure which will + * have the SSID. + */ + if (!roamed_bss_ies->SSID.present) + goto error; + + if (roamed_bss_ies->SSID.num_ssid != + session->connectedProfile.SSID.length) + goto error; + + is_null_ssid_match = !qdf_mem_cmp(session->connectedProfile.SSID.ssId, + roamed_bss_ies->SSID.ssid, + roamed_bss_ies->SSID.num_ssid); +error: + if (connected_profile_ies) + qdf_mem_free(connected_profile_ies); + + return is_null_ssid_match; +} + +/** + * csr_check_for_allowed_ssid() - Function to check if the roamed + * SSID is present in the configured Allowed SSID list + * @mac: Pointer to global mac_ctx + * @bss_desc: bss descriptor pointer + * @roamed_bss_ies: Pointer to roamed BSS IEs + * + * Return: True if SSID match found else False + */ +static bool +csr_check_for_allowed_ssid(struct mac_context *mac, + struct bss_description *bss_desc, + tDot11fBeaconIEs *roamed_bss_ies) +{ + uint8_t i; + tSirMacSSid *ssid_list = + mac->roam.configParam.roam_params.ssid_allowed_list; + uint8_t num_ssid_allowed_list = + mac->roam.configParam.roam_params.num_ssid_allowed_list; + + if (!roamed_bss_ies) { + sme_info(" Roamed BSS IEs NULL"); + return false; + } + + for (i = 0; i < num_ssid_allowed_list; i++) { + if (ssid_list[i].length == roamed_bss_ies->SSID.num_ssid && + !qdf_mem_cmp(ssid_list[i].ssId, roamed_bss_ies->SSID.ssid, + ssid_list[i].length)) + return true; + } + + return false; +} + +static +QDF_STATUS csr_roam_stop_network(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *roam_profile, + struct bss_description *bss_desc, + tDot11fBeaconIEs *pIes) +{ + QDF_STATUS status; + struct bss_config_param *pBssConfig; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + bool ssid_match; + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + pBssConfig = qdf_mem_malloc(sizeof(struct bss_config_param)); + if (!pBssConfig) + return QDF_STATUS_E_NOMEM; + + sme_debug("session id: %d", sessionId); + + status = csr_roam_prepare_bss_config(mac, roam_profile, bss_desc, + pBssConfig, pIes); + if (QDF_IS_STATUS_SUCCESS(status)) { + enum csr_roam_substate substate; + + substate = eCSR_ROAM_SUBSTATE_DISCONNECT_CONTINUE_ROAMING; + pSession->bssParams.uCfgDot11Mode = pBssConfig->uCfgDot11Mode; + /* This will allow to pass cbMode during join req */ + pSession->bssParams.cbMode = pBssConfig->cbMode; + /* For IBSS, we need to prepare some more information */ + if (csr_is_bss_type_ibss(roam_profile->BSSType) || + CSR_IS_INFRA_AP(roam_profile)) + csr_roam_prepare_bss_params(mac, sessionId, + roam_profile, bss_desc, + pBssConfig, pIes); + /* + * If we are in an IBSS, then stop the IBSS... + * Not worry about WDS connection for now + */ + if (csr_is_conn_state_ibss(mac, sessionId)) { + status = csr_roam_issue_stop_bss(mac, sessionId, + substate); + } else if (csr_is_conn_state_infra(mac, sessionId)) { + /* + * the new Bss is an Ibss OR we are roaming from + * Infra to Infra across SSIDs + * (roaming to a new SSID)... + * Not worry about WDS connection for now + */ + ssid_match = + (csr_check_for_allowed_ssid(mac, bss_desc, pIes) || + csr_check_for_hidden_ssid_match(mac, pSession, + bss_desc, pIes)); + if (!ssid_match) + ssid_match = csr_is_ssid_equal( + mac, pSession->pConnectBssDesc, + bss_desc, pIes); + + if (bss_desc && + (csr_is_ibss_bss_desc(bss_desc) || !ssid_match)) + status = csr_roam_issue_disassociate(mac, + sessionId, substate, false); + else if (bss_desc) + /* + * In an infra & going to an infra network with + * the same SSID. This calls for a reassoc seq. + * So issue the CFG sets for this new AP. Set + * parameters for this Bss. + */ + status = csr_roam_set_bss_config_cfg( + mac, sessionId, roam_profile, + bss_desc, pBssConfig, pIes, + false); + } else if (bss_desc || CSR_IS_INFRA_AP(roam_profile)) { + /* + * Neither in IBSS nor in Infra. We can go ahead and set + * the cfg for tne new network... nothing to stop. + */ + bool is_11r_roaming = false; + + is_11r_roaming = csr_roam_is11r_assoc(mac, sessionId); + /* Set parameters for this Bss. */ + status = csr_roam_set_bss_config_cfg(mac, sessionId, + roam_profile, + bss_desc, + pBssConfig, pIes, + is_11r_roaming); + } + } /* Success getting BSS config info */ + qdf_mem_free(pBssConfig); + return status; +} + +/** + * csr_roam_state_for_same_profile() - Determine roam state for same profile + * @mac_ctx: pointer to mac context + * @profile: Roam profile + * @session: Roam session + * @session_id: session id + * @ies_local: local ies + * @bss_descr: bss description + * + * This function will determine the roam state for same profile + * + * Return: Roaming state. + */ +static enum csr_join_state csr_roam_state_for_same_profile( + struct mac_context *mac_ctx, struct csr_roam_profile *profile, + struct csr_roam_session *session, + uint32_t session_id, tDot11fBeaconIEs *ies_local, + struct bss_description *bss_descr) +{ + QDF_STATUS status; + struct bss_config_param bssConfig; + + if (csr_roam_is_same_profile_keys(mac_ctx, &session->connectedProfile, + profile)) + return eCsrReassocToSelfNoCapChange; + /* The key changes */ + qdf_mem_zero(&bssConfig, sizeof(bssConfig)); + status = csr_roam_prepare_bss_config(mac_ctx, profile, bss_descr, + &bssConfig, ies_local); + if (QDF_IS_STATUS_SUCCESS(status)) { + session->bssParams.uCfgDot11Mode = + bssConfig.uCfgDot11Mode; + session->bssParams.cbMode = + bssConfig.cbMode; + /* reapply the cfg including keys so reassoc happens. */ + status = csr_roam_set_bss_config_cfg(mac_ctx, session_id, + profile, bss_descr, &bssConfig, + ies_local, false); + if (QDF_IS_STATUS_SUCCESS(status)) + return eCsrContinueRoaming; + } + + return eCsrStopRoaming; + +} + +static enum csr_join_state csr_roam_join(struct mac_context *mac, + uint32_t sessionId, tCsrScanResultInfo *pScanResult, + struct csr_roam_profile *pProfile) +{ + enum csr_join_state eRoamState = eCsrContinueRoaming; + struct bss_description *bss_desc = &pScanResult->BssDescriptor; + tDot11fBeaconIEs *pIesLocal = (tDot11fBeaconIEs *) (pScanResult->pvIes); + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return eCsrStopRoaming; + } + + if (!pIesLocal && + !QDF_IS_STATUS_SUCCESS(csr_get_parsed_bss_description_ies(mac, + bss_desc, &pIesLocal))) { + sme_err("fail to parse IEs"); + return eCsrStopRoaming; + } + if (csr_is_infra_bss_desc(bss_desc)) { + /* + * If we are connected in infra mode and the join bss descr is + * for the same BssID, then we are attempting to join the AP we + * are already connected with. In that case, see if the Bss or + * sta capabilities have changed and handle the changes + * without disturbing the current association + */ + + if (csr_is_conn_state_connected_infra(mac, sessionId) && + csr_is_bss_id_equal(bss_desc, + pSession->pConnectBssDesc) && + csr_is_ssid_equal(mac, pSession->pConnectBssDesc, + bss_desc, pIesLocal)) { + /* + * Check to see if the Auth type has changed in the + * profile. If so, we don't want to reassociate with + * authenticating first. To force this, stop the + * current association (Disassociate) and then re 'Join' + * the AP, wihch will force an Authentication (with the + * new Auth type) followed by a new Association. + */ + if (csr_is_same_profile(mac, + &pSession->connectedProfile, pProfile)) { + sme_warn("detect same profile"); + eRoamState = + csr_roam_state_for_same_profile(mac, + pProfile, pSession, sessionId, + pIesLocal, bss_desc); + } else if (!QDF_IS_STATUS_SUCCESS( + csr_roam_issue_disassociate( + mac, + sessionId, + eCSR_ROAM_SUBSTATE_DISASSOC_REQ, + false))) { + sme_err("fail disassoc session %d", + sessionId); + eRoamState = eCsrStopRoaming; + } + } else if (!QDF_IS_STATUS_SUCCESS(csr_roam_stop_network(mac, + sessionId, pProfile, bss_desc, pIesLocal))) + /* we used to pre-auth here with open auth + * networks but that wasn't working so well. + * stop the existing network before attempting + * to join the new network. + */ + eRoamState = eCsrStopRoaming; + } else if (!QDF_IS_STATUS_SUCCESS(csr_roam_stop_network(mac, sessionId, + pProfile, bss_desc, + pIesLocal))) + eRoamState = eCsrStopRoaming; + + if (pIesLocal && !pScanResult->pvIes) + qdf_mem_free(pIesLocal); + return eRoamState; +} + +static QDF_STATUS +csr_roam_should_roam(struct mac_context *mac, uint32_t sessionId, + struct bss_description *bss_desc, uint32_t roamId) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_info *roam_info; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + roam_info->bss_desc = bss_desc; + status = csr_roam_call_callback(mac, sessionId, roam_info, roamId, + eCSR_ROAM_SHOULD_ROAM, + eCSR_ROAM_RESULT_NONE); + qdf_mem_free(roam_info); + return status; +} + +/* In case no matching BSS is found, use whatever default we can find */ +static void csr_roam_assign_default_param(struct mac_context *mac, + tSmeCmd *pCommand) +{ + /* Need to get all negotiated types in place first */ + /* auth type */ + /* Take the preferred Auth type. */ + switch (pCommand->u.roamCmd.roamProfile.AuthType.authType[0]) { + default: + case eCSR_AUTH_TYPE_WPA: + case eCSR_AUTH_TYPE_WPA_PSK: + case eCSR_AUTH_TYPE_WPA_NONE: + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + pCommand->u.roamCmd.roamProfile.negotiatedAuthType = + eCSR_AUTH_TYPE_OPEN_SYSTEM; + break; + + case eCSR_AUTH_TYPE_SHARED_KEY: + pCommand->u.roamCmd.roamProfile.negotiatedAuthType = + eCSR_AUTH_TYPE_SHARED_KEY; + break; + + case eCSR_AUTH_TYPE_AUTOSWITCH: + pCommand->u.roamCmd.roamProfile.negotiatedAuthType = + eCSR_AUTH_TYPE_AUTOSWITCH; + break; + + case eCSR_AUTH_TYPE_SAE: + case eCSR_AUTH_TYPE_FT_SAE: + pCommand->u.roamCmd.roamProfile.negotiatedAuthType = + eCSR_AUTH_TYPE_SAE; + break; + } + pCommand->u.roamCmd.roamProfile.negotiatedUCEncryptionType = + pCommand->u.roamCmd.roamProfile.EncryptionType. + encryptionType[0]; + /* In this case, the multicast encryption needs to follow the + * uncast ones. + */ + pCommand->u.roamCmd.roamProfile.negotiatedMCEncryptionType = + pCommand->u.roamCmd.roamProfile.EncryptionType. + encryptionType[0]; +} + +/** + * csr_roam_select_bss() - Handle join scenario based on profile + * @mac_ctx: Global MAC Context + * @roam_bss_entry: The next BSS to join + * @csr_result_info: Result of join + * @csr_scan_result: Global scan result + * @vdev_id: SME Session ID + * @roam_id: Roaming ID + * @roam_state: Current roaming state + * @bss_list: BSS List + * + * Return: true if the entire BSS list is done, false otherwise. + */ +static bool csr_roam_select_bss(struct mac_context *mac_ctx, + tListElem **roam_bss_entry, tCsrScanResultInfo **csr_result_info, + struct tag_csrscan_result **csr_scan_result, + uint32_t vdev_id, uint32_t roam_id, + enum csr_join_state *roam_state, + struct scan_result_list *bss_list) +{ + uint32_t conc_freq = 0, chan_freq, temp_vdev_id; + bool status = false; + struct tag_csrscan_result *scan_result = NULL; + tCsrScanResultInfo *result = NULL; + enum QDF_OPMODE op_mode; + struct wlan_objmgr_vdev *vdev; + enum policy_mgr_con_mode mode; + QDF_STATUS qdf_status; + eCsrPhyMode self_phymode = mac_ctx->roam.configParam.phyMode; + tDot11fBeaconIEs *bcn_ies; + + vdev = wlan_objmgr_get_vdev_by_id_from_pdev(mac_ctx->pdev, + vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Vdev ref error"); + return true; + } + op_mode = wlan_vdev_mlme_get_opmode(vdev); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + while (*roam_bss_entry) { + scan_result = GET_BASE_ADDR(*roam_bss_entry, struct + tag_csrscan_result, Link); + /* + * If concurrency enabled take the + * concurrent connected channel first. + * Valid multichannel concurrent + * sessions exempted + */ + result = &scan_result->Result; + bcn_ies = result->pvIes; + /* + * If phymode is configured to DOT11 Only profile. + * Don't connect to profile which is less than them. + */ + if (bcn_ies && ((self_phymode == eCSR_DOT11_MODE_11n_ONLY && + !bcn_ies->HTCaps.present) || + (self_phymode == eCSR_DOT11_MODE_11ac_ONLY && + !bcn_ies->VHTCaps.present) || + (self_phymode == eCSR_DOT11_MODE_11ax_ONLY && + !bcn_ies->he_cap.present))) { + sme_info("self_phymode %d mismatch HT %d VHT %d HE %d", + self_phymode, bcn_ies->HTCaps.present, + bcn_ies->VHTCaps.present, + bcn_ies->he_cap.present); + *roam_state = eCsrStopRoamingDueToConcurrency; + status = true; + *roam_bss_entry = csr_ll_next(&bss_list->List, + *roam_bss_entry, + LL_ACCESS_LOCK); + continue; + } + + /* + * Ignore the BSS if any other vdev is already connected + * to it. + */ + qdf_status = csr_roam_get_session_id_from_bssid(mac_ctx, + (struct qdf_mac_addr *) + &result->BssDescriptor.bssId, &temp_vdev_id); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + sme_info("vdev_id %d already connected to "QDF_MAC_ADDR_FMT". select next bss for vdev_id %d", + temp_vdev_id, + QDF_MAC_ADDR_REF(result->BssDescriptor.bssId), + vdev_id); + *roam_state = eCsrStopRoamingDueToConcurrency; + status = true; + *roam_bss_entry = csr_ll_next(&bss_list->List, + *roam_bss_entry, + LL_ACCESS_LOCK); + continue; + } + + chan_freq = result->BssDescriptor.chan_freq; + mode = policy_mgr_convert_device_mode_to_qdf_type(op_mode); + /* If concurrency is not allowed select next bss */ + if (!policy_mgr_is_concurrency_allowed(mac_ctx->psoc, + mode, + chan_freq, + HW_MODE_20_MHZ)) { + sme_err("Concurrency not allowed for this channel freq %d bssid "QDF_MAC_ADDR_FMT", selecting next", + chan_freq, + QDF_MAC_ADDR_REF(result->BssDescriptor.bssId)); + *roam_state = eCsrStopRoamingDueToConcurrency; + status = true; + *roam_bss_entry = csr_ll_next(&bss_list->List, + *roam_bss_entry, + LL_ACCESS_LOCK); + continue; + } + + /* + * check if channel is allowed for current hw mode, if not fetch + * next BSS. + */ + if (!policy_mgr_is_hwmode_set_for_given_chnl( + mac_ctx->psoc, result->BssDescriptor.chan_freq)) { + sme_err("HW mode isn't properly set, freq %d BSSID "QDF_MAC_ADDR_FMT, + result->BssDescriptor.chan_freq, + QDF_MAC_ADDR_REF(result->BssDescriptor.bssId)); + *roam_state = eCsrStopRoamingDueToConcurrency; + status = true; + *roam_bss_entry = csr_ll_next(&bss_list->List, + *roam_bss_entry, + LL_ACCESS_LOCK); + continue; + } + if (policy_mgr_concurrent_open_sessions_running(mac_ctx->psoc) + && !csr_is_valid_mc_concurrent_session(mac_ctx, + vdev_id, &result->BssDescriptor)) { + conc_freq = csr_get_concurrent_operation_freq( + mac_ctx); + sme_debug("csr Conc Channel freq: %d", conc_freq); + + if (conc_freq) { + if ((conc_freq == chan_freq) || + (policy_mgr_is_hw_dbs_capable(mac_ctx->psoc) + && !WLAN_REG_IS_SAME_BAND_FREQS( + conc_freq, chan_freq))) { + /* + * make this 0 because we do not want the below + * check to pass as we don't want to connect on + * other channel + */ + sme_debug("Conc chnl freq match: %d", + conc_freq); + conc_freq = 0; + } + } + } + + /* Ok to roam this */ + if (!conc_freq && + QDF_IS_STATUS_SUCCESS(csr_roam_should_roam(mac_ctx, + vdev_id, &result->BssDescriptor, + roam_id))) { + status = false; + break; + } + *roam_state = eCsrStopRoamingDueToConcurrency; + status = true; + *roam_bss_entry = csr_ll_next(&bss_list->List, *roam_bss_entry, + LL_ACCESS_LOCK); + } + *csr_result_info = result; + *csr_scan_result = scan_result; + return status; +} + +/** + * csr_roam_join_handle_profile() - Handle join scenario based on profile + * @mac_ctx: Global MAC Context + * @session_id: SME Session ID + * @cmd: Command + * @roam_info_ptr: Pointed to the roaming info for join + * @roam_state: Current roaming state + * @result: Result of join + * @scan_result: Global scan result + * + * Return: None + */ +static void csr_roam_join_handle_profile(struct mac_context *mac_ctx, + uint32_t session_id, tSmeCmd *cmd, + struct csr_roam_info *roam_info_ptr, + enum csr_join_state *roam_state, tCsrScanResultInfo *result, + struct tag_csrscan_result *scan_result) +{ +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + uint8_t acm_mask = 0; +#endif + QDF_STATUS status; + struct csr_roam_session *session; + struct csr_roam_profile *profile = &cmd->u.roamCmd.roamProfile; + tDot11fBeaconIEs *ies_local = NULL; + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("Invalid session id %d", session_id); + return; + } + session = CSR_GET_SESSION(mac_ctx, session_id); + + /* + * We have something to roam, tell HDD when it is infra. + * For IBSS, the indication goes back to HDD via eCSR_ROAM_IBSS_IND + */ + if (CSR_IS_INFRASTRUCTURE(profile) && roam_info_ptr) { + if (session->bRefAssocStartCnt) { + session->bRefAssocStartCnt--; + roam_info_ptr->pProfile = profile; + /* + * Complete the last assoc attempt as a + * new one is about to be tried + */ + csr_roam_call_callback(mac_ctx, session_id, + roam_info_ptr, cmd->u.roamCmd.roamId, + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_RESULT_NOT_ASSOCIATED); + } + + qdf_mem_zero(roam_info_ptr, sizeof(struct csr_roam_info)); + if (!scan_result) + cmd->u.roamCmd.roamProfile.uapsd_mask = 0; + else + ies_local = scan_result->Result.pvIes; + + if (!result) { + sme_err(" cannot parse IEs"); + *roam_state = eCsrStopRoaming; + return; + } else if (scan_result && !ies_local && + (!QDF_IS_STATUS_SUCCESS( + csr_get_parsed_bss_description_ies( + mac_ctx, &result->BssDescriptor, + &ies_local)))) { + sme_err(" cannot parse IEs"); + *roam_state = eCsrStopRoaming; + return; + } + roam_info_ptr->bss_desc = &result->BssDescriptor; + cmd->u.roamCmd.pLastRoamBss = roam_info_ptr->bss_desc; + /* dont put uapsd_mask if BSS doesn't support uAPSD */ + if (scan_result && cmd->u.roamCmd.roamProfile.uapsd_mask + && CSR_IS_QOS_BSS(ies_local) + && CSR_IS_UAPSD_BSS(ies_local)) { +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + acm_mask = sme_qos_get_acm_mask(mac_ctx, + &result->BssDescriptor, ies_local); +#endif /* WLAN_MDM_CODE_REDUCTION_OPT */ + } else { + cmd->u.roamCmd.roamProfile.uapsd_mask = 0; + } + if (ies_local && !scan_result->Result.pvIes) + qdf_mem_free(ies_local); + roam_info_ptr->pProfile = profile; + session->bRefAssocStartCnt++; + csr_roam_call_callback(mac_ctx, session_id, roam_info_ptr, + cmd->u.roamCmd.roamId, eCSR_ROAM_ASSOCIATION_START, + eCSR_ROAM_RESULT_NONE); + } + if (cmd->u.roamCmd.pRoamBssEntry) { + /* + * We have BSS + * Need to assign these value because + * they are used in csr_is_same_profile + */ + scan_result = GET_BASE_ADDR(cmd->u.roamCmd.pRoamBssEntry, + struct tag_csrscan_result, Link); + /* + * The OSEN IE doesn't provide the cipher suite.Therefore set + * to constant value of AES + */ + if (cmd->u.roamCmd.roamProfile.bOSENAssociation) { + cmd->u.roamCmd.roamProfile.negotiatedUCEncryptionType = + eCSR_ENCRYPT_TYPE_AES; + cmd->u.roamCmd.roamProfile.negotiatedMCEncryptionType = + eCSR_ENCRYPT_TYPE_AES; + } else { + /* Negotiated while building scan result. */ + cmd->u.roamCmd.roamProfile.negotiatedUCEncryptionType = + scan_result->ucEncryptionType; + cmd->u.roamCmd.roamProfile.negotiatedMCEncryptionType = + scan_result->mcEncryptionType; + } + cmd->u.roamCmd.roamProfile.negotiatedAuthType = + scan_result->authType; + if (CSR_IS_START_IBSS(&cmd->u.roamCmd.roamProfile)) { + if (csr_is_same_profile(mac_ctx, + &session->connectedProfile, profile)) { + *roam_state = eCsrStartIbssSameIbss; + return; + } + } + if (cmd->u.roamCmd.fReassocToSelfNoCapChange) { + /* trying to connect to the one already connected */ + cmd->u.roamCmd.fReassocToSelfNoCapChange = false; + *roam_state = eCsrReassocToSelfNoCapChange; + return; + } + /* Attempt to Join this Bss... */ + *roam_state = csr_roam_join(mac_ctx, session_id, + &scan_result->Result, profile); + return; + } + + /* For an IBSS profile, then we need to start the IBSS. */ + if (CSR_IS_START_IBSS(profile)) { + bool same_ibss = false; + /* Attempt to start this IBSS... */ + csr_roam_assign_default_param(mac_ctx, cmd); + status = csr_roam_start_ibss(mac_ctx, session_id, + profile, &same_ibss); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (same_ibss) + *roam_state = eCsrStartIbssSameIbss; + else + *roam_state = eCsrContinueRoaming; + } else { + /* it somehow fail need to stop */ + *roam_state = eCsrStopRoaming; + } + return; + } else if (CSR_IS_INFRA_AP(profile)) { + /* Attempt to start this WDS... */ + csr_roam_assign_default_param(mac_ctx, cmd); + /* For AP WDS, we dont have any BSSDescription */ + status = csr_roam_start_wds(mac_ctx, session_id, profile, NULL); + if (QDF_IS_STATUS_SUCCESS(status)) + *roam_state = eCsrContinueRoaming; + else + *roam_state = eCsrStopRoaming; + } else if (CSR_IS_NDI(profile)) { + csr_roam_assign_default_param(mac_ctx, cmd); + status = csr_roam_start_ndi(mac_ctx, session_id, profile); + if (QDF_IS_STATUS_SUCCESS(status)) + *roam_state = eCsrContinueRoaming; + else + *roam_state = eCsrStopRoaming; + } else { + /* Nothing we can do */ + sme_warn("cannot continue without BSS list"); + *roam_state = eCsrStopRoaming; + return; + } + +} +/** + * csr_roam_join_next_bss() - Pick the next BSS for join + * @mac_ctx: Global MAC Context + * @cmd: Command + * @use_same_bss: Use Same BSS to Join + * + * Return: The Join State + */ +static enum csr_join_state csr_roam_join_next_bss(struct mac_context *mac_ctx, + tSmeCmd *cmd, bool use_same_bss) +{ + struct tag_csrscan_result *scan_result = NULL; + enum csr_join_state roam_state = eCsrStopRoaming; + struct scan_result_list *bss_list = + (struct scan_result_list *) cmd->u.roamCmd.hBSSList; + bool done = false; + struct csr_roam_info *roam_info = NULL; + uint32_t session_id = cmd->vdev_id; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + struct csr_roam_profile *profile = &cmd->u.roamCmd.roamProfile; + struct csr_roam_joinstatus *join_status; + tCsrScanResultInfo *result = NULL; + + if (!session) { + sme_err("session %d not found", session_id); + return eCsrStopRoaming; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return eCsrStopRoaming; + + qdf_mem_copy(&roam_info->bssid, &session->joinFailStatusCode.bssId, + sizeof(tSirMacAddr)); + /* + * When handling AP's capability change, continue to associate + * to same BSS and make sure pRoamBssEntry is not Null. + */ + if (bss_list) { + if (!cmd->u.roamCmd.pRoamBssEntry) { + /* Try the first BSS */ + cmd->u.roamCmd.pLastRoamBss = NULL; + cmd->u.roamCmd.pRoamBssEntry = + csr_ll_peek_head(&bss_list->List, + LL_ACCESS_LOCK); + } else if (!use_same_bss) { + cmd->u.roamCmd.pRoamBssEntry = + csr_ll_next(&bss_list->List, + cmd->u.roamCmd.pRoamBssEntry, + LL_ACCESS_LOCK); + /* + * Done with all the BSSs. + * In this case, will tell HDD the + * completion + */ + if (!cmd->u.roamCmd.pRoamBssEntry) + goto end; + /* + * We need to indicate to HDD that we + * are done with this one. + */ + roam_info->bss_desc = cmd->u.roamCmd.pLastRoamBss; + join_status = &session->joinFailStatusCode; + roam_info->status_code = join_status->status_code; + roam_info->reasonCode = join_status->reasonCode; + } else { + /* + * Try connect to same BSS again. Fill roam_info for the + * last attemp to indicate to HDD. + */ + roam_info->bss_desc = cmd->u.roamCmd.pLastRoamBss; + join_status = &session->joinFailStatusCode; + roam_info->status_code = join_status->status_code; + roam_info->reasonCode = join_status->reasonCode; + } + + done = csr_roam_select_bss(mac_ctx, + &cmd->u.roamCmd.pRoamBssEntry, + &result, &scan_result, session_id, + cmd->u.roamCmd.roamId, + &roam_state, bss_list); + if (done) + goto end; + } + + roam_info->u.pConnectedProfile = &session->connectedProfile; + + csr_roam_join_handle_profile(mac_ctx, session_id, cmd, roam_info, + &roam_state, result, scan_result); +end: + if ((eCsrStopRoaming == roam_state) && CSR_IS_INFRASTRUCTURE(profile) && + (session->bRefAssocStartCnt > 0)) { + /* + * Need to indicate association_completion if association_start + * has been done + */ + session->bRefAssocStartCnt--; + /* + * Complete the last assoc attempte as a + * new one is about to be tried + */ + roam_info->pProfile = profile; + csr_roam_call_callback(mac_ctx, session_id, + roam_info, cmd->u.roamCmd.roamId, + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_RESULT_NOT_ASSOCIATED); + } + qdf_mem_free(roam_info); + + return roam_state; +} + +static QDF_STATUS csr_roam(struct mac_context *mac, tSmeCmd *pCommand, + bool use_same_bss) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + enum csr_join_state RoamState; + enum csr_roam_substate substate; + uint32_t sessionId = pCommand->vdev_id; + + /* Attept to join a Bss... */ + RoamState = csr_roam_join_next_bss(mac, pCommand, use_same_bss); + + /* if nothing to join.. */ + if ((eCsrStopRoaming == RoamState) || + (eCsrStopRoamingDueToConcurrency == RoamState)) { + bool fComplete = false; + /* and if connected in Infrastructure mode... */ + if (csr_is_conn_state_infra(mac, sessionId)) { + /* ... then we need to issue a disassociation */ + substate = eCSR_ROAM_SUBSTATE_DISASSOC_NOTHING_TO_JOIN; + status = csr_roam_issue_disassociate(mac, sessionId, + substate, false); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_warn("fail issuing disassoc status = %d", + status); + /* + * roam command is completed by caller in the + * failed case + */ + fComplete = true; + } + } else if (csr_is_conn_state_ibss(mac, sessionId)) { + status = csr_roam_issue_stop_bss(mac, sessionId, + eCSR_ROAM_SUBSTATE_STOP_BSS_REQ); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_warn("fail issuing stop bss status = %d", + status); + /* + * roam command is completed by caller in the + * failed case + */ + fComplete = true; + } + } else if (csr_is_conn_state_connected_infra_ap(mac, + sessionId)) { + substate = eCSR_ROAM_SUBSTATE_STOP_BSS_REQ; + status = csr_roam_issue_stop_bss(mac, sessionId, + substate); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_warn("fail issuing stop bss status = %d", + status); + /* + * roam command is completed by caller in the + * failed case + */ + fComplete = true; + } + } else { + fComplete = true; + } + + if (fComplete) { + /* otherwise, we can complete the Roam command here. */ + if (eCsrStopRoamingDueToConcurrency == RoamState) + csr_roam_complete(mac, + eCsrJoinFailureDueToConcurrency, NULL, + sessionId); + else + csr_roam_complete(mac, + eCsrNothingToJoin, NULL, sessionId); + } + } else if (eCsrReassocToSelfNoCapChange == RoamState) { + csr_roam_complete(mac, eCsrSilentlyStopRoamingSaveState, + NULL, sessionId); + } else if (eCsrStartIbssSameIbss == RoamState) { + csr_roam_complete(mac, eCsrSilentlyStopRoaming, NULL, + sessionId); + } + + return status; +} + +static +QDF_STATUS csr_process_ft_reassoc_roam_command(struct mac_context *mac, + tSmeCmd *pCommand) +{ + uint32_t sessionId; + struct csr_roam_session *pSession; + struct tag_csrscan_result *pScanResult = NULL; + struct bss_description *bss_desc = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + sessionId = pCommand->vdev_id; + pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + if (CSR_IS_ROAMING(pSession) && pSession->fCancelRoaming) { + /* the roaming is cancelled. Simply complete the command */ + sme_debug("Roam command canceled"); + csr_roam_complete(mac, eCsrNothingToJoin, NULL, sessionId); + return QDF_STATUS_E_FAILURE; + } + if (pCommand->u.roamCmd.pRoamBssEntry) { + pScanResult = + GET_BASE_ADDR(pCommand->u.roamCmd.pRoamBssEntry, + struct tag_csrscan_result, Link); + bss_desc = &pScanResult->Result.BssDescriptor; + } else { + /* the roaming is cancelled. Simply complete the command */ + sme_debug("Roam command canceled"); + csr_roam_complete(mac, eCsrNothingToJoin, NULL, sessionId); + return QDF_STATUS_E_FAILURE; + } + status = csr_roam_issue_reassociate(mac, sessionId, bss_desc, + (tDot11fBeaconIEs *) (pScanResult-> + Result.pvIes), + &pCommand->u.roamCmd.roamProfile); + return status; +} + +/** + * csr_roam_trigger_reassociate() - Helper function to trigger reassociate + * @mac_ctx: pointer to mac context + * @cmd: sme command + * @session_ptr: session pointer + * @session_id: session id + * + * This function will trigger reassociate. + * + * Return: QDF_STATUS for success or failure. + */ +static QDF_STATUS +csr_roam_trigger_reassociate(struct mac_context *mac_ctx, tSmeCmd *cmd, + struct csr_roam_session *session_ptr, + uint32_t session_id) +{ + tDot11fBeaconIEs *pIes = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_info *roam_info; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + + if (session_ptr->pConnectBssDesc) { + status = csr_get_parsed_bss_description_ies(mac_ctx, + session_ptr->pConnectBssDesc, &pIes); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("fail to parse IEs"); + } else { + roam_info->reasonCode = + eCsrRoamReasonStaCapabilityChanged; + csr_roam_call_callback(mac_ctx, session_ptr->sessionId, + roam_info, 0, eCSR_ROAM_ROAMING_START, + eCSR_ROAM_RESULT_NONE); + session_ptr->roamingReason = eCsrReassocRoaming; + roam_info->bss_desc = session_ptr->pConnectBssDesc; + roam_info->pProfile = &cmd->u.roamCmd.roamProfile; + session_ptr->bRefAssocStartCnt++; + csr_roam_call_callback(mac_ctx, session_id, roam_info, + cmd->u.roamCmd.roamId, + eCSR_ROAM_ASSOCIATION_START, + eCSR_ROAM_RESULT_NONE); + + sme_debug("calling csr_roam_issue_reassociate"); + status = csr_roam_issue_reassociate(mac_ctx, session_id, + session_ptr->pConnectBssDesc, pIes, + &cmd->u.roamCmd.roamProfile); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("failed status %d", status); + csr_release_command(mac_ctx, cmd); + } else { + csr_neighbor_roam_state_transition(mac_ctx, + eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING, + session_id); + } + + + qdf_mem_free(pIes); + pIes = NULL; + } + } else { + sme_err("reassoc to same AP failed as connected BSS is NULL"); + status = QDF_STATUS_E_FAILURE; + } + qdf_mem_free(roam_info); + return status; +} + +/** + * csr_allow_concurrent_sta_connections() - Wrapper for policy_mgr api + * @mac: mac context + * @vdev_id: vdev id + * + * This function invokes policy mgr api to check for support of + * simultaneous connections on concurrent STA interfaces. + * + * Return: If supports return true else false. + */ +static +bool csr_allow_concurrent_sta_connections(struct mac_context *mac, + uint32_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + enum QDF_OPMODE vdev_mode; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, vdev_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) { + sme_err("vdev object not found for vdev_id %u", vdev_id); + return false; + } + vdev_mode = wlan_vdev_mlme_get_opmode(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + + /* If vdev mode is STA then proceed further */ + if (vdev_mode != QDF_STA_MODE) + return true; + + if (policy_mgr_allow_concurrency(mac->psoc, PM_STA_MODE, 0, + HW_MODE_20_MHZ)) + return true; + + return false; +} + +static void csr_get_peer_rssi_cb(struct stats_event *ev, void *cookie) +{ + struct mac_context *mac = (struct mac_context *)cookie; + + if (!mac) { + sme_err("Invalid mac ctx"); + return; + } + + if (!ev->peer_stats) { + sme_debug("%s no peer stats\n", __func__); + goto disconnect_stats_complete; + } + + mac->peer_rssi = ev->peer_stats->peer_rssi; + mac->peer_txrate = ev->peer_stats->tx_rate; + mac->peer_rxrate = ev->peer_stats->rx_rate; + if (!ev->peer_extended_stats) { + sme_debug("%s no peer extended stats\n", __func__); + goto disconnect_stats_complete; + } + mac->rx_mc_bc_cnt = ev->peer_extended_stats->rx_mc_bc_cnt; + +disconnect_stats_complete: + csr_roam_get_disconnect_stats_complete(mac); +} + +static void csr_get_peer_rssi(struct mac_context *mac, uint32_t session_id, + struct qdf_mac_addr peer_mac) +{ + struct wlan_objmgr_vdev *vdev; + struct request_info info = {0}; + QDF_STATUS status; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + mac->psoc, + session_id, + WLAN_LEGACY_SME_ID); + if (vdev) { + info.cookie = mac; + info.u.get_peer_rssi_cb = csr_get_peer_rssi_cb; + info.vdev_id = wlan_vdev_get_id(vdev); + info.pdev_id = wlan_objmgr_pdev_get_pdev_id( + wlan_vdev_get_pdev(vdev)); + qdf_mem_copy(info.peer_mac_addr, &peer_mac, QDF_MAC_ADDR_SIZE); + status = ucfg_mc_cp_stats_send_stats_request( + vdev, + TYPE_PEER_STATS, + &info); + sme_debug("peer_mac" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_mac.bytes)); + if (QDF_IS_STATUS_ERROR(status)) + sme_err("stats req failed: %d", status); + + wma_get_rx_retry_cnt(mac, session_id, info.peer_mac_addr); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + } +} + +QDF_STATUS csr_roam_process_command(struct mac_context *mac, tSmeCmd *pCommand) +{ + QDF_STATUS lock_status, status = QDF_STATUS_SUCCESS; + uint32_t sessionId = pCommand->vdev_id; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + sme_debug("Roam Reason: %d sessionId: %d", + pCommand->u.roamCmd.roamReason, sessionId); + + pSession->disconnect_reason = pCommand->u.roamCmd.disconnect_reason; + + switch (pCommand->u.roamCmd.roamReason) { + case eCsrForcedDisassoc: + status = csr_roam_process_disassoc_deauth(mac, pCommand, + true, false); + lock_status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(lock_status)) { + csr_roam_complete(mac, eCsrNothingToJoin, NULL, + sessionId); + return lock_status; + } + csr_free_roam_profile(mac, sessionId); + sme_release_global_lock(&mac->sme); + break; + case eCsrSmeIssuedDisassocForHandoff: + /* Not to free mac->roam.pCurRoamProfile (via + * csr_free_roam_profile) because its needed after disconnect + */ + status = csr_roam_process_disassoc_deauth(mac, pCommand, + true, false); + + break; + case eCsrForcedDisassocMICFailure: + status = csr_roam_process_disassoc_deauth(mac, pCommand, + true, true); + lock_status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(lock_status)) { + csr_roam_complete(mac, eCsrNothingToJoin, NULL, + sessionId); + return lock_status; + } + csr_free_roam_profile(mac, sessionId); + sme_release_global_lock(&mac->sme); + break; + case eCsrForcedDeauth: + status = csr_roam_process_disassoc_deauth(mac, pCommand, + false, false); + lock_status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(lock_status)) { + csr_roam_complete(mac, eCsrNothingToJoin, NULL, + sessionId); + return lock_status; + } + csr_free_roam_profile(mac, sessionId); + sme_release_global_lock(&mac->sme); + break; + case eCsrHddIssuedReassocToSameAP: + case eCsrSmeIssuedReassocToSameAP: + status = csr_roam_trigger_reassociate(mac, pCommand, + pSession, sessionId); + break; + case eCsrCapsChange: + sme_err("received eCsrCapsChange "); + csr_roam_state_change(mac, eCSR_ROAMING_STATE_JOINING, + sessionId); + status = csr_roam_issue_disassociate(mac, sessionId, + eCSR_ROAM_SUBSTATE_DISCONNECT_CONTINUE_ROAMING, + false); + break; + case eCsrSmeIssuedFTReassoc: + sme_debug("received FT Reassoc Req"); + status = csr_process_ft_reassoc_roam_command(mac, pCommand); + break; + + case eCsrStopBss: + csr_roam_state_change(mac, eCSR_ROAMING_STATE_JOINING, + sessionId); + status = csr_roam_issue_stop_bss(mac, sessionId, + eCSR_ROAM_SUBSTATE_STOP_BSS_REQ); + break; + + case eCsrForcedDisassocSta: + csr_roam_state_change(mac, eCSR_ROAMING_STATE_JOINING, + sessionId); + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_DISASSOC_REQ, + sessionId); + sme_debug("Disassociate issued with reason: %d", + pCommand->u.roamCmd.reason); + + status = csr_send_mb_disassoc_req_msg(mac, sessionId, + pCommand->u.roamCmd.peerMac, + pCommand->u.roamCmd.reason); + break; + + case eCsrForcedDeauthSta: + csr_roam_state_change(mac, eCSR_ROAMING_STATE_JOINING, + sessionId); + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_DEAUTH_REQ, + sessionId); + sme_debug("Deauth issued with reason: %d", + pCommand->u.roamCmd.reason); + + status = csr_send_mb_deauth_req_msg(mac, sessionId, + pCommand->u.roamCmd.peerMac, + pCommand->u.roamCmd.reason); + break; + + case eCsrPerformPreauth: + sme_debug("Attempting FT PreAuth Req"); + status = csr_roam_issue_ft_preauth_req(mac, sessionId, + pCommand->u.roamCmd.pLastRoamBss); + break; + + case eCsrHddIssued: + /* + * Check for simultaneous connection support on + * multiple STA interfaces. + */ + if (!csr_allow_concurrent_sta_connections(mac, sessionId)) { + sme_err("No support of conc STA con"); + csr_roam_complete(mac, eCsrNothingToJoin, NULL, + sessionId); + status = QDF_STATUS_E_FAILURE; + break; + } + /* Fall through for success case */ + + default: + csr_roam_state_change(mac, eCSR_ROAMING_STATE_JOINING, + sessionId); + + if (pCommand->u.roamCmd.fUpdateCurRoamProfile) { + /* Remember the roaming profile */ + lock_status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(lock_status)) { + csr_roam_complete(mac, eCsrNothingToJoin, NULL, + sessionId); + return lock_status; + } + csr_free_roam_profile(mac, sessionId); + pSession->pCurRoamProfile = + qdf_mem_malloc(sizeof(struct csr_roam_profile)); + if (pSession->pCurRoamProfile) { + csr_roam_copy_profile(mac, + pSession->pCurRoamProfile, + &pCommand->u.roamCmd.roamProfile); + } + sme_release_global_lock(&mac->sme); + } + /* + * At this point original uapsd_mask is saved in + * pCurRoamProfile. uapsd_mask in the pCommand may change from + * this point on. Attempt to roam with the new scan results + * (if we need to..) + */ + status = csr_roam(mac, pCommand, false); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_warn("csr_roam() failed with status = 0x%08X", + status); + break; + } + return status; +} + +void csr_reinit_roam_cmd(struct mac_context *mac, tSmeCmd *pCommand) +{ + if (pCommand->u.roamCmd.fReleaseBssList) { + csr_scan_result_purge(mac, pCommand->u.roamCmd.hBSSList); + pCommand->u.roamCmd.fReleaseBssList = false; + pCommand->u.roamCmd.hBSSList = CSR_INVALID_SCANRESULT_HANDLE; + } + if (pCommand->u.roamCmd.fReleaseProfile) { + csr_release_profile(mac, &pCommand->u.roamCmd.roamProfile); + pCommand->u.roamCmd.fReleaseProfile = false; + } + pCommand->u.roamCmd.pLastRoamBss = NULL; + pCommand->u.roamCmd.pRoamBssEntry = NULL; + /* Because u.roamCmd is union and share with scanCmd and StatusChange */ + qdf_mem_zero(&pCommand->u.roamCmd, sizeof(struct roam_cmd)); +} + +void csr_reinit_wm_status_change_cmd(struct mac_context *mac, + tSmeCmd *pCommand) +{ + qdf_mem_zero(&pCommand->u.wmStatusChangeCmd, + sizeof(struct wmstatus_changecmd)); +} + +void csr_roam_complete(struct mac_context *mac_ctx, + enum csr_roamcomplete_result Result, + void *Context, uint8_t session_id) +{ + tSmeCmd *sme_cmd; + struct wlan_serialization_command *cmd; + + cmd = wlan_serialization_peek_head_active_cmd_using_psoc( + mac_ctx->psoc, false); + if (!cmd) { + sme_err("Roam completion called but cmd is not active"); + return; + } + sme_cmd = cmd->umac_cmd; + if (!sme_cmd) { + sme_err("sme_cmd is NULL"); + return; + } + if (eSmeCommandRoam == sme_cmd->command) { + csr_roam_process_results(mac_ctx, sme_cmd, Result, Context); + csr_release_command(mac_ctx, sme_cmd); + } +} + + +void csr_reset_pmkid_candidate_list(struct mac_context *mac, + uint32_t sessionId) +{ + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session: %d not found", sessionId); + return; + } + qdf_mem_zero(&(pSession->PmkidCandidateInfo[0]), + sizeof(tPmkidCandidateInfo) * CSR_MAX_PMKID_ALLOWED); + pSession->NumPmkidCandidate = 0; +} + +/** + * csr_roam_save_params() - Helper function to save params + * @mac_ctx: pointer to mac context + * @session_ptr: Session pointer + * @auth_type: auth type + * @ie_ptr: pointer to ie + * @ie_local: pointr to local ie + * + * This function will save params to session + * + * Return: none. + */ +static QDF_STATUS csr_roam_save_params(struct mac_context *mac_ctx, + struct csr_roam_session *session_ptr, + enum csr_akm_type auth_type, + tDot11fBeaconIEs *ie_ptr, + tDot11fBeaconIEs *ie_local) +{ + uint32_t nIeLen; + uint8_t *pIeBuf; + + if ((eCSR_AUTH_TYPE_RSN == auth_type) || + (eCSR_AUTH_TYPE_FT_RSN == auth_type) || + (eCSR_AUTH_TYPE_FT_RSN_PSK == auth_type) || + (eCSR_AUTH_TYPE_FT_SAE == auth_type) || + (eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384 == auth_type) || +#if defined WLAN_FEATURE_11W + (eCSR_AUTH_TYPE_RSN_PSK_SHA256 == auth_type) || + (eCSR_AUTH_TYPE_RSN_8021X_SHA256 == auth_type) || +#endif + (eCSR_AUTH_TYPE_RSN_PSK == auth_type)) { + if (ie_local->RSN.present) { + tDot11fIERSN *rsnie = &ie_local->RSN; + /* + * Calculate the actual length + * version + gp_cipher_suite + pwise_cipher_suite_count + * + akm_suite_cnt + reserved + pwise_cipher_suites + */ + nIeLen = 8 + 2 + 2 + + (rsnie->pwise_cipher_suite_count * 4) + + (rsnie->akm_suite_cnt * 4); + if (rsnie->pmkid_count) + /* pmkid */ + nIeLen += 2 + rsnie->pmkid_count * 4; + + /* nIeLen doesn't count EID and length fields */ + session_ptr->pWpaRsnRspIE = qdf_mem_malloc(nIeLen + 2); + if (!session_ptr->pWpaRsnRspIE) + return QDF_STATUS_E_NOMEM; + + session_ptr->pWpaRsnRspIE[0] = DOT11F_EID_RSN; + session_ptr->pWpaRsnRspIE[1] = (uint8_t) nIeLen; + /* copy upto akm_suite */ + pIeBuf = session_ptr->pWpaRsnRspIE + 2; + qdf_mem_copy(pIeBuf, &rsnie->version, + sizeof(rsnie->version)); + pIeBuf += sizeof(rsnie->version); + qdf_mem_copy(pIeBuf, &rsnie->gp_cipher_suite, + sizeof(rsnie->gp_cipher_suite)); + pIeBuf += sizeof(rsnie->gp_cipher_suite); + qdf_mem_copy(pIeBuf, &rsnie->pwise_cipher_suite_count, + sizeof(rsnie->pwise_cipher_suite_count)); + pIeBuf += sizeof(rsnie->pwise_cipher_suite_count); + if (rsnie->pwise_cipher_suite_count) { + /* copy pwise_cipher_suites */ + qdf_mem_copy(pIeBuf, rsnie->pwise_cipher_suites, + rsnie->pwise_cipher_suite_count * 4); + pIeBuf += rsnie->pwise_cipher_suite_count * 4; + } + qdf_mem_copy(pIeBuf, &rsnie->akm_suite_cnt, 2); + pIeBuf += 2; + if (rsnie->akm_suite_cnt) { + /* copy akm_suite */ + qdf_mem_copy(pIeBuf, rsnie->akm_suite, + rsnie->akm_suite_cnt * 4); + pIeBuf += rsnie->akm_suite_cnt * 4; + } + /* copy the rest */ + qdf_mem_copy(pIeBuf, rsnie->akm_suite + + rsnie->akm_suite_cnt * 4, + 2 + rsnie->pmkid_count * 4); + session_ptr->nWpaRsnRspIeLength = nIeLen + 2; + } + } else if ((eCSR_AUTH_TYPE_WPA == auth_type) || + (eCSR_AUTH_TYPE_WPA_PSK == auth_type)) { + if (ie_local->WPA.present) { + tDot11fIEWPA *wpaie = &ie_local->WPA; + /* Calculate the actual length wpaie */ + nIeLen = 12 + 2 /* auth_suite_count */ + + wpaie->unicast_cipher_count * 4 + + wpaie->auth_suite_count * 4; + + /* The WPA capabilities follows the Auth Suite + * (two octects)-- this field is optional, and + * we always "send" zero, so just remove it. This is + * consistent with our assumptions in the frames + * compiler; nIeLen doesn't count EID & length fields + */ + session_ptr->pWpaRsnRspIE = qdf_mem_malloc(nIeLen + 2); + if (!session_ptr->pWpaRsnRspIE) + return QDF_STATUS_E_NOMEM; + session_ptr->pWpaRsnRspIE[0] = DOT11F_EID_WPA; + session_ptr->pWpaRsnRspIE[1] = (uint8_t) nIeLen; + pIeBuf = session_ptr->pWpaRsnRspIE + 2; + /* Copy WPA OUI */ + qdf_mem_copy(pIeBuf, &csr_wpa_oui[1], 4); + pIeBuf += 4; + qdf_mem_copy(pIeBuf, &wpaie->version, + 8 + wpaie->unicast_cipher_count * 4); + pIeBuf += 8 + wpaie->unicast_cipher_count * 4; + qdf_mem_copy(pIeBuf, &wpaie->auth_suite_count, + 2 + wpaie->auth_suite_count * 4); + pIeBuf += wpaie->auth_suite_count * 4; + session_ptr->nWpaRsnRspIeLength = nIeLen + 2; + } + } +#ifdef FEATURE_WLAN_WAPI + else if ((eCSR_AUTH_TYPE_WAPI_WAI_PSK == auth_type) || + (eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE == + auth_type)) { + if (ie_local->WAPI.present) { + tDot11fIEWAPI *wapi_ie = &ie_local->WAPI; + /* Calculate the actual length of wapi ie*/ + nIeLen = 4 + 2 /* pwise_cipher_suite_count */ + + wapi_ie->akm_suite_count * 4 + + wapi_ie->unicast_cipher_suite_count * 4 + + 6; /* gp_cipher_suite + preauth + reserved */ + + if (wapi_ie->bkid_count) + nIeLen += 2 + wapi_ie->bkid_count * 4; + + /* nIeLen doesn't count EID and length fields */ + session_ptr->pWapiRspIE = + qdf_mem_malloc(nIeLen + 2); + if (!session_ptr->pWapiRspIE) + return QDF_STATUS_E_NOMEM; + session_ptr->pWapiRspIE[0] = DOT11F_EID_WAPI; + session_ptr->pWapiRspIE[1] = (uint8_t) nIeLen; + pIeBuf = session_ptr->pWapiRspIE + 2; + /* copy upto akm_suite_count */ + qdf_mem_copy(pIeBuf, &wapi_ie->version, 2); + pIeBuf += 4; + if (wapi_ie->akm_suite_count) { + /* copy akm_suites */ + qdf_mem_copy(pIeBuf, + wapi_ie->akm_suites, + wapi_ie->akm_suite_count * 4); + pIeBuf += wapi_ie->akm_suite_count * 4; + } + qdf_mem_copy(pIeBuf, + &wapi_ie->unicast_cipher_suite_count, 2); + pIeBuf += 2; + if (wapi_ie->unicast_cipher_suite_count) { + uint16_t suite_size = + wapi_ie->unicast_cipher_suite_count * 4; + /* copy pwise_cipher_suites */ + qdf_mem_copy(pIeBuf, + wapi_ie->unicast_cipher_suites, + suite_size); + pIeBuf += suite_size; + } + /* gp_cipher_suite */ + qdf_mem_copy(pIeBuf, + wapi_ie->multicast_cipher_suite, 4); + pIeBuf += 4; + /* preauth + reserved */ + qdf_mem_copy(pIeBuf, + wapi_ie->multicast_cipher_suite + 4, 2); + pIeBuf += 2; + if (wapi_ie->bkid_count) { + /* bkid_count */ + qdf_mem_copy(pIeBuf, &wapi_ie->bkid_count, 2); + pIeBuf += 2; + /* copy akm_suites */ + qdf_mem_copy(pIeBuf, wapi_ie->bkid, + wapi_ie->bkid_count * 4); + pIeBuf += wapi_ie->bkid_count * 4; + } + session_ptr->nWapiRspIeLength = nIeLen + 2; + } + } +#endif /* FEATURE_WLAN_WAPI */ + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +csr_roam_save_security_rsp_ie(struct mac_context *mac, + uint32_t sessionId, enum csr_akm_type authType, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + tDot11fBeaconIEs *pIesLocal = pIes; + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + sme_debug("authType %d session %d", authType, sessionId); + if ((eCSR_AUTH_TYPE_WPA == authType) || + (eCSR_AUTH_TYPE_WPA_PSK == authType) || + (eCSR_AUTH_TYPE_RSN == authType) || + (eCSR_AUTH_TYPE_RSN_PSK == authType) + || (eCSR_AUTH_TYPE_FT_RSN == authType) || + (eCSR_AUTH_TYPE_FT_RSN_PSK == authType) + || (eCSR_AUTH_TYPE_FT_SAE == authType) + || (eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384 == authType) +#ifdef FEATURE_WLAN_WAPI + || (eCSR_AUTH_TYPE_WAPI_WAI_PSK == authType) || + (eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE == authType) +#endif /* FEATURE_WLAN_WAPI */ +#ifdef WLAN_FEATURE_11W + || (eCSR_AUTH_TYPE_RSN_PSK_SHA256 == authType) || + (eCSR_AUTH_TYPE_RSN_8021X_SHA256 == authType) +#endif /* FEATURE_WLAN_WAPI */ + || (eCSR_AUTH_TYPE_SAE == authType)) { + if (!pIesLocal && !QDF_IS_STATUS_SUCCESS + (csr_get_parsed_bss_description_ies(mac, + pSirBssDesc, &pIesLocal))) + sme_err(" cannot parse IEs"); + if (pIesLocal) { + status = csr_roam_save_params(mac, pSession, authType, + pIes, pIesLocal); + if (!pIes) + /* locally allocated */ + qdf_mem_free(pIesLocal); + } + } + return status; +} + +/* Returns whether the current association is a 11r assoc or not */ +bool csr_roam_is11r_assoc(struct mac_context *mac, uint8_t sessionId) +{ + return csr_neighbor_roam_is11r_assoc(mac, sessionId); +} + +/* Returns whether "Legacy Fast Roaming" is currently enabled...or not */ +bool csr_roam_is_fast_roam_enabled(struct mac_context *mac, uint32_t sessionId) +{ + struct csr_roam_session *pSession = NULL; + + if (CSR_IS_SESSION_VALID(mac, sessionId)) { + pSession = CSR_GET_SESSION(mac, sessionId); + if (pSession->pCurRoamProfile) { + if (pSession->pCurRoamProfile->csrPersona != + QDF_STA_MODE) { + return false; + } + } + } + if (true == CSR_IS_FASTROAM_IN_CONCURRENCY_INI_FEATURE_ENABLED(mac)) { + return mac->mlme_cfg->lfr.lfr_enabled; + } else { + return mac->mlme_cfg->lfr.lfr_enabled && + (!csr_is_concurrent_session_running(mac)); + } +} + +static +void csr_update_scan_entry_associnfo(struct mac_context *mac_ctx, + struct csr_roam_session *session, + enum scan_entry_connection_state state) +{ + QDF_STATUS status; + struct mlme_info mlme; + struct bss_info bss; + tCsrRoamConnectedProfile *conn_profile; + + if (!session) { + sme_debug("session is NULL"); + return; + } + conn_profile = &session->connectedProfile; + if (!CSR_IS_INFRASTRUCTURE(conn_profile)) { + sme_debug("not infra return"); + return; + } + + qdf_copy_macaddr(&bss.bssid, &conn_profile->bssid); + bss.freq = conn_profile->op_freq; + bss.ssid.length = conn_profile->SSID.length; + qdf_mem_copy(&bss.ssid.ssid, &conn_profile->SSID.ssId, + bss.ssid.length); + + sme_debug("Update MLME info in scan database. bssid "QDF_MAC_ADDR_FMT" ssid:%.*s freq %d state: %d", + QDF_MAC_ADDR_REF(bss.bssid.bytes), bss.ssid.length, + bss.ssid.ssid, bss.freq, state); + mlme.assoc_state = state; + status = ucfg_scan_update_mlme_by_bssinfo(mac_ctx->pdev, &bss, &mlme); + if (QDF_IS_STATUS_ERROR(status)) + sme_debug("Failed to update the MLME info in scan entry"); +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +static eCsrPhyMode csr_roamdot11mode_to_phymode(uint8_t dot11mode) +{ + eCsrPhyMode phymode = eCSR_DOT11_MODE_abg; + + switch (dot11mode) { + case MLME_DOT11_MODE_ALL: + phymode = eCSR_DOT11_MODE_abg; + break; + case MLME_DOT11_MODE_11A: + phymode = eCSR_DOT11_MODE_11a; + break; + case MLME_DOT11_MODE_11B: + phymode = eCSR_DOT11_MODE_11b; + break; + case MLME_DOT11_MODE_11G: + phymode = eCSR_DOT11_MODE_11g; + break; + case MLME_DOT11_MODE_11N: + phymode = eCSR_DOT11_MODE_11n; + break; + case MLME_DOT11_MODE_11G_ONLY: + phymode = eCSR_DOT11_MODE_11g_ONLY; + break; + case MLME_DOT11_MODE_11N_ONLY: + phymode = eCSR_DOT11_MODE_11n_ONLY; + break; + case MLME_DOT11_MODE_11AC: + phymode = eCSR_DOT11_MODE_11ac; + break; + case MLME_DOT11_MODE_11AC_ONLY: + phymode = eCSR_DOT11_MODE_11ac_ONLY; + break; + case MLME_DOT11_MODE_11AX: + phymode = eCSR_DOT11_MODE_11ax; + break; + case MLME_DOT11_MODE_11AX_ONLY: + phymode = eCSR_DOT11_MODE_11ax_ONLY; + break; + default: + break; + } + + return phymode; +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static void csr_roam_synch_clean_up(struct mac_context *mac, uint8_t session_id) +{ + struct scheduler_msg msg = {0}; + struct roam_offload_synch_fail *roam_offload_failed = NULL; + struct csr_roam_session *session = &mac->roam.roamSession[session_id]; + + /* Clean up the roam synch in progress for LFR3 */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Roam Synch Failed, Clean Up", __func__); + session->roam_synch_in_progress = false; + + roam_offload_failed = qdf_mem_malloc( + sizeof(struct roam_offload_synch_fail)); + if (!roam_offload_failed) + return; + + roam_offload_failed->session_id = session_id; + msg.type = WMA_ROAM_OFFLOAD_SYNCH_FAIL; + msg.reserved = 0; + msg.bodyptr = roam_offload_failed; + if (!QDF_IS_STATUS_SUCCESS(scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: Unable to post WMA_ROAM_OFFLOAD_SYNCH_FAIL to WMA", + __func__); + qdf_mem_free(roam_offload_failed); + } +} +#endif + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * csr_roam_copy_ht_profile() - Copy from src to dst + * @dst_profile: Destination HT profile + * @src_profile: Source HT profile + * + * Copy the HT profile from the given source to destination + * + * Return: None + */ +static void csr_roam_copy_ht_profile(tCsrRoamHTProfile *dst_profile, + struct ht_profile *src_profile) +{ + dst_profile->phymode = + csr_roamdot11mode_to_phymode(src_profile->dot11mode); + dst_profile->htCapability = src_profile->htCapability; + dst_profile->htSupportedChannelWidthSet = + src_profile->htSupportedChannelWidthSet; + dst_profile->htRecommendedTxWidthSet = + src_profile->htRecommendedTxWidthSet; + dst_profile->htSecondaryChannelOffset = + src_profile->htSecondaryChannelOffset; + dst_profile->vhtCapability = src_profile->vhtCapability; + dst_profile->apCenterChan = src_profile->apCenterChan; + dst_profile->apChanWidth = src_profile->apChanWidth; +} +#endif + +#if defined(WLAN_FEATURE_FILS_SK) +/** + * csr_update_fils_seq_number() - Copy FILS sequence number to roam info + * @session: CSR Roam Session + * @roam_info: Roam info + * + * Return: None + */ +static void csr_update_fils_seq_number(struct csr_roam_session *session, + struct csr_roam_info *roam_info) +{ + roam_info->is_fils_connection = true; + roam_info->fils_seq_num = session->fils_seq_num; + pe_debug("FILS sequence number %x", session->fils_seq_num); +} +#else +static inline void csr_update_fils_seq_number(struct csr_roam_session *session, + struct csr_roam_info *roam_info) +{} +#endif + +/** + * csr_roam_process_results_default() - Process the result for start bss + * @mac_ctx: Global MAC Context + * @cmd: Command to be processed + * @context: Additional data in context of the cmd + * + * Return: None + */ +static void csr_roam_process_results_default(struct mac_context *mac_ctx, + tSmeCmd *cmd, void *context, enum csr_roamcomplete_result + res) +{ + uint32_t session_id = cmd->vdev_id; + struct csr_roam_session *session; + struct csr_roam_info *roam_info; + QDF_STATUS status; + struct csr_roam_connectedinfo *prev_connect_info = NULL; + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("Invalid session id %d", session_id); + return; + } + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + session = CSR_GET_SESSION(mac_ctx, session_id); + + prev_connect_info = &session->prev_assoc_ap_info; + + sme_debug("Assoc ref count: %d", session->bRefAssocStartCnt); + + /* Update AP's assoc info in scan before removing connectedProfile */ + switch (cmd->u.roamCmd.roamReason) { + case eCsrSmeIssuedDisassocForHandoff: + case eCsrForcedDisassoc: + case eCsrForcedDeauth: + case eCsrForcedDisassocMICFailure: + csr_update_scan_entry_associnfo(mac_ctx, session, + SCAN_ENTRY_CON_STATE_NONE); + break; + default: + break; + } + if (CSR_IS_INFRASTRUCTURE(&session->connectedProfile) + || CSR_IS_ROAM_SUBSTATE_STOP_BSS_REQ(mac_ctx, session_id)) { + /* + * do not free for the other profiles as we need + * to send down stop BSS later + */ + csr_free_connect_bss_desc(mac_ctx, session_id); + csr_roam_free_connect_profile(&session->connectedProfile); + csr_roam_free_connected_info(mac_ctx, &session->connectedInfo); + csr_set_default_dot11_mode(mac_ctx); + } + + /* Copy FILS sequence number used to be updated to userspace */ + if (session->is_fils_connection) + csr_update_fils_seq_number(session, roam_info); + + switch (cmd->u.roamCmd.roamReason) { + /* + * If this transition is because of an 802.11 OID, then we + * transition back to INIT state so we sit waiting for more + * OIDs to be issued and we don't start the IDLE timer. + */ + case eCsrSmeIssuedFTReassoc: + case eCsrSmeIssuedAssocToSimilarAP: + case eCsrHddIssued: + case eCsrSmeIssuedDisassocForHandoff: + csr_roam_state_change(mac_ctx, eCSR_ROAMING_STATE_IDLE, + session_id); + roam_info->bss_desc = cmd->u.roamCmd.pLastRoamBss; + roam_info->pProfile = &cmd->u.roamCmd.roamProfile; + roam_info->status_code = + session->joinFailStatusCode.status_code; + roam_info->reasonCode = session->joinFailStatusCode.reasonCode; + qdf_mem_copy(&roam_info->bssid, + &session->joinFailStatusCode.bssId, + sizeof(struct qdf_mac_addr)); + if (prev_connect_info->pbFrames) { + roam_info->nAssocReqLength = + prev_connect_info->nAssocReqLength; + roam_info->nAssocRspLength = + prev_connect_info->nAssocRspLength; + roam_info->nBeaconLength = + prev_connect_info->nBeaconLength; + roam_info->pbFrames = prev_connect_info->pbFrames; + } + /* + * If Join fails while Handoff is in progress, indicate + * disassociated event to supplicant to reconnect + */ + if (csr_roam_is_handoff_in_progress(mac_ctx, session_id)) { + csr_neighbor_roam_indicate_connect(mac_ctx, + (uint8_t)session_id, QDF_STATUS_E_FAILURE); + } + if (session->bRefAssocStartCnt > 0) { + session->bRefAssocStartCnt--; + if (eCsrJoinFailureDueToConcurrency == res) + csr_roam_call_callback(mac_ctx, session_id, + roam_info, + cmd->u.roamCmd.roamId, + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_RESULT_ASSOC_FAIL_CON_CHANNEL); + else + csr_roam_call_callback(mac_ctx, session_id, + roam_info, + cmd->u.roamCmd.roamId, + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_RESULT_FAILURE); + } else { + /* + * bRefAssocStartCnt is not incremented when + * eRoamState == eCsrStopRoamingDueToConcurrency + * in csr_roam_join_next_bss API. so handle this in + * else case by sending assoc failure + */ + csr_roam_call_callback(mac_ctx, session_id, + roam_info, + cmd->u.roamCmd.roamId, + eCSR_ROAM_ASSOCIATION_FAILURE, + eCSR_ROAM_RESULT_FAILURE); + } + sme_debug("roam(reason %d) failed", cmd->u.roamCmd.roamReason); +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_qos_update_hand_off((uint8_t) session_id, false); + sme_qos_csr_event_ind(mac_ctx, (uint8_t) session_id, + SME_QOS_CSR_DISCONNECT_IND, NULL); +#endif + csr_roam_completion(mac_ctx, session_id, NULL, cmd, + eCSR_ROAM_RESULT_FAILURE, false); + break; + case eCsrHddIssuedReassocToSameAP: + case eCsrSmeIssuedReassocToSameAP: + csr_roam_state_change(mac_ctx, eCSR_ROAMING_STATE_IDLE, + session_id); + + csr_roam_call_callback(mac_ctx, session_id, NULL, + cmd->u.roamCmd.roamId, eCSR_ROAM_DISASSOCIATED, + eCSR_ROAM_RESULT_FORCED); +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_qos_csr_event_ind(mac_ctx, (uint8_t) session_id, + SME_QOS_CSR_DISCONNECT_IND, NULL); +#endif + csr_roam_completion(mac_ctx, session_id, NULL, cmd, + eCSR_ROAM_RESULT_FAILURE, false); + break; + case eCsrForcedDisassoc: + case eCsrForcedDeauth: + csr_roam_state_change(mac_ctx, eCSR_ROAMING_STATE_IDLE, + session_id); + csr_roam_call_callback( + mac_ctx, session_id, NULL, + cmd->u.roamCmd.roamId, eCSR_ROAM_DISASSOCIATED, + eCSR_ROAM_RESULT_FORCED); +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_qos_csr_event_ind(mac_ctx, (uint8_t) session_id, + SME_QOS_CSR_DISCONNECT_IND, + NULL); +#endif + csr_roam_link_down(mac_ctx, session_id); + + if (mac_ctx->roam.deauthRspStatus == eSIR_SME_DEAUTH_STATUS) { + sme_warn("FW still in connected state"); + break; + } + break; + case eCsrForcedIbssLeave: + csr_roam_call_callback(mac_ctx, session_id, NULL, + cmd->u.roamCmd.roamId, eCSR_ROAM_IBSS_LEAVE, + eCSR_ROAM_RESULT_IBSS_STOP); + session->connectState = eCSR_ASSOC_STATE_TYPE_IBSS_DISCONNECTED; + break; + case eCsrForcedDisassocMICFailure: + csr_roam_state_change(mac_ctx, eCSR_ROAMING_STATE_IDLE, + session_id); + + csr_roam_call_callback(mac_ctx, session_id, NULL, + cmd->u.roamCmd.roamId, eCSR_ROAM_DISASSOCIATED, + eCSR_ROAM_RESULT_MIC_FAILURE); +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_qos_csr_event_ind(mac_ctx, (uint8_t) session_id, + SME_QOS_CSR_DISCONNECT_REQ, NULL); +#endif + break; + case eCsrStopBss: + csr_roam_call_callback(mac_ctx, session_id, NULL, + cmd->u.roamCmd.roamId, eCSR_ROAM_INFRA_IND, + eCSR_ROAM_RESULT_INFRA_STOPPED); + break; + case eCsrForcedDisassocSta: + case eCsrForcedDeauthSta: + roam_info->rssi = mac_ctx->peer_rssi; + roam_info->tx_rate = mac_ctx->peer_txrate; + roam_info->rx_rate = mac_ctx->peer_rxrate; + roam_info->rx_mc_bc_cnt = mac_ctx->rx_mc_bc_cnt; + roam_info->rx_retry_cnt = mac_ctx->rx_retry_cnt; + + csr_roam_state_change(mac_ctx, eCSR_ROAMING_STATE_JOINED, + session_id); + session = CSR_GET_SESSION(mac_ctx, session_id); + if (CSR_IS_SESSION_VALID(mac_ctx, session_id) && + CSR_IS_INFRA_AP(&session->connectedProfile)) { + roam_info->u.pConnectedProfile = + &session->connectedProfile; + qdf_mem_copy(roam_info->peerMac.bytes, + cmd->u.roamCmd.peerMac, + sizeof(tSirMacAddr)); + roam_info->reasonCode = eCSR_ROAM_RESULT_FORCED; + /* Update the MAC reason code */ + roam_info->disassoc_reason = cmd->u.roamCmd.reason; + roam_info->status_code = eSIR_SME_SUCCESS; + status = csr_roam_call_callback(mac_ctx, session_id, + roam_info, + cmd->u.roamCmd.roamId, + eCSR_ROAM_LOSTLINK, + eCSR_ROAM_RESULT_FORCED); + } + break; + default: + csr_roam_state_change(mac_ctx, + eCSR_ROAMING_STATE_IDLE, session_id); + break; + } + qdf_mem_free(roam_info); +} + +/** + * csr_roam_process_start_bss_success() - Process the result for start bss + * @mac_ctx: Global MAC Context + * @cmd: Command to be processed + * @context: Additional data in context of the cmd + * + * Return: None + */ +static void csr_roam_process_start_bss_success(struct mac_context *mac_ctx, + tSmeCmd *cmd, void *context) +{ + uint32_t session_id = cmd->vdev_id; + struct csr_roam_profile *profile = &cmd->u.roamCmd.roamProfile; + struct csr_roam_session *session; + struct bss_description *bss_desc = NULL; + struct csr_roam_info *roam_info; + struct start_bss_rsp *start_bss_rsp = NULL; + eRoamCmdStatus roam_status; + eCsrRoamResult roam_result; + tDot11fBeaconIEs *ies_ptr = NULL; + tSirMacAddr bcast_mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + QDF_STATUS status; + host_log_ibss_pkt_type *ibss_log; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + struct ht_profile *src_profile = NULL; + tCsrRoamHTProfile *dst_profile = NULL; +#endif + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("Invalid session id %d", session_id); + return; + } + session = CSR_GET_SESSION(mac_ctx, session_id); + + /* + * on the StartBss Response, LIM is returning the Bss Description that + * we are beaconing. Add this Bss Description to our scan results and + * chain the Profile to this Bss Description. On a Start BSS, there was + * no detected Bss description (no partner) so we issued the Start Bss + * to start the Ibss without any Bss description. Lim was kind enough + * to return the Bss Description that we start beaconing for the newly + * started Ibss. + */ + sme_debug("receives start BSS ok indication"); + status = QDF_STATUS_E_FAILURE; + start_bss_rsp = (struct start_bss_rsp *) context; + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + if (CSR_IS_IBSS(profile)) + session->connectState = eCSR_ASSOC_STATE_TYPE_IBSS_DISCONNECTED; + else if (CSR_IS_INFRA_AP(profile)) + session->connectState = + eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTED; + else if (CSR_IS_NDI(profile)) + session->connectState = eCSR_CONNECT_STATE_TYPE_NDI_STARTED; + else + session->connectState = eCSR_ASSOC_STATE_TYPE_WDS_DISCONNECTED; + + bss_desc = &start_bss_rsp->bssDescription; + if (CSR_IS_NDI(profile)) { + csr_roam_state_change(mac_ctx, eCSR_ROAMING_STATE_JOINED, + session_id); + csr_roam_save_ndi_connected_info(mac_ctx, session_id, profile, + bss_desc); + roam_info->u.pConnectedProfile = &session->connectedProfile; + qdf_mem_copy(&roam_info->bssid, &bss_desc->bssId, + sizeof(struct qdf_mac_addr)); + } else { + csr_roam_state_change(mac_ctx, eCSR_ROAMING_STATE_JOINED, + session_id); + if (!QDF_IS_STATUS_SUCCESS + (csr_get_parsed_bss_description_ies(mac_ctx, bss_desc, + &ies_ptr))) { + sme_warn("cannot parse IBSS IEs"); + roam_info->bss_desc = bss_desc; + csr_roam_call_callback(mac_ctx, session_id, roam_info, + cmd->u.roamCmd.roamId, + eCSR_ROAM_IBSS_IND, + eCSR_ROAM_RESULT_IBSS_START_FAILED); + qdf_mem_free(roam_info); + return; + } + } + if (!CSR_IS_INFRA_AP(profile) && !CSR_IS_NDI(profile)) { + csr_scan_append_bss_description(mac_ctx, bss_desc); + } + csr_roam_save_connected_bss_desc(mac_ctx, session_id, bss_desc); + csr_roam_free_connect_profile(&session->connectedProfile); + csr_roam_free_connected_info(mac_ctx, &session->connectedInfo); + csr_roam_save_connected_information(mac_ctx, session_id, + profile, bss_desc, ies_ptr); + qdf_mem_copy(&roam_info->bssid, &bss_desc->bssId, + sizeof(struct qdf_mac_addr)); + /* We are done with the IEs so free it */ + qdf_mem_free(ies_ptr); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + WLAN_HOST_DIAG_LOG_ALLOC(ibss_log, + host_log_ibss_pkt_type, LOG_WLAN_IBSS_C); + if (ibss_log) { + uint32_t bi; + if (CSR_INVALID_SCANRESULT_HANDLE == + cmd->u.roamCmd.hBSSList) { + /* + * We start the IBSS (didn't find any + * matched IBSS out there) + */ + ibss_log->eventId = + WLAN_IBSS_EVENT_START_IBSS_RSP; + } else { + ibss_log->eventId = + WLAN_IBSS_EVENT_JOIN_IBSS_RSP; + } + if (bss_desc) { + qdf_mem_copy(ibss_log->bssid.bytes, + bss_desc->bssId, QDF_MAC_ADDR_SIZE); + ibss_log->op_freq = bss_desc->chan_freq; + ibss_log->operatingChannel = + wlan_reg_freq_to_chan(mac_ctx->pdev, + ibss_log->op_freq); + } + bi = mac_ctx->mlme_cfg->sap_cfg.beacon_interval; + /* U8 is not enough for BI */ + ibss_log->beaconInterval = (uint8_t) bi; + WLAN_HOST_DIAG_LOG_REPORT(ibss_log); + } +#endif + ibss_log = NULL; + /* + * Only set context for non-WDS_STA. We don't even need it for + * WDS_AP. But since the encryption. + * is WPA2-PSK so it won't matter. + */ + if (session->pCurRoamProfile && + !CSR_IS_INFRA_AP(session->pCurRoamProfile)) { + if (CSR_IS_ENC_TYPE_STATIC( + profile->negotiatedUCEncryptionType)) { + /* + * Issue the set Context request to LIM to establish + * the Broadcast STA context for the Ibss. In Rome IBSS + * case, dummy key installation will break proper BSS + * key installation, so skip it. + */ + if (!CSR_IS_IBSS(session->pCurRoamProfile)) { + /* NO keys. these key parameters don't matter */ + csr_issue_set_context_req_helper(mac_ctx, + profile, session_id, + &bcast_mac, false, + false, eSIR_TX_RX, + 0, 0, NULL); + } + } + if (CSR_IS_IBSS(session->pCurRoamProfile) && + (eCSR_ENCRYPT_TYPE_WEP40_STATICKEY == + profile->negotiatedUCEncryptionType || + eCSR_ENCRYPT_TYPE_WEP104_STATICKEY == + profile->negotiatedUCEncryptionType || + eCSR_ENCRYPT_TYPE_TKIP == + profile->negotiatedUCEncryptionType || + eCSR_ENCRYPT_TYPE_AES == + profile->negotiatedUCEncryptionType)) { + roam_info->fAuthRequired = true; + } + } + /* + * Only tell upper layer is we start the BSS because Vista doesn't like + * multiple connection indications. If we don't start the BSS ourself, + * handler of eSIR_SME_JOINED_NEW_BSS will trigger the connection start + * indication in Vista + */ + if (!CSR_IS_JOIN_TO_IBSS(profile)) { + roam_status = eCSR_ROAM_IBSS_IND; + roam_result = eCSR_ROAM_RESULT_IBSS_STARTED; + if (CSR_IS_INFRA_AP(profile)) { + roam_status = eCSR_ROAM_INFRA_IND; + roam_result = eCSR_ROAM_RESULT_INFRA_STARTED; + } + roam_info->staId = (uint8_t)start_bss_rsp->staId; + if (CSR_IS_NDI(profile)) { + csr_roam_update_ndp_return_params(mac_ctx, + eCsrStartBssSuccess, + &roam_status, + &roam_result, + roam_info); + } + /* + * Only tell upper layer is we start the BSS because Vista + * doesn't like multiple connection indications. If we don't + * start the BSS ourself, handler of eSIR_SME_JOINED_NEW_BSS + * will trigger the connection start indication in Vista + */ + roam_info->status_code = + session->joinFailStatusCode.status_code; + roam_info->reasonCode = session->joinFailStatusCode.reasonCode; + /* We start the IBSS (didn't find any matched IBSS out there) */ + roam_info->bss_desc = bss_desc; + if (bss_desc) + qdf_mem_copy(roam_info->bssid.bytes, bss_desc->bssId, + sizeof(struct qdf_mac_addr)); + if (!IS_FEATURE_SUPPORTED_BY_FW(SLM_SESSIONIZATION) && + (csr_is_concurrent_session_running(mac_ctx))) { + mac_ctx->roam.configParam.doBMPSWorkaround = 1; + } +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + dst_profile = &session->connectedProfile.ht_profile; + src_profile = &start_bss_rsp->ht_profile; + if (mac_ctx->roam.configParam.cc_switch_mode + != QDF_MCC_TO_SCC_SWITCH_DISABLE) + csr_roam_copy_ht_profile(dst_profile, src_profile); +#endif + csr_roam_call_callback(mac_ctx, session_id, roam_info, + cmd->u.roamCmd.roamId, + roam_status, roam_result); + } + qdf_mem_free(roam_info); +} + +#ifdef WLAN_FEATURE_FILS_SK +/** + * populate_fils_params_join_rsp() - Copy FILS params from JOIN rsp + * @mac_ctx: Global MAC Context + * @roam_info: CSR Roam Info + * @join_rsp: SME Join response + * + * Copy the FILS params from the join results + * + * Return: QDF_STATUS + */ +static QDF_STATUS populate_fils_params_join_rsp(struct mac_context *mac_ctx, + struct csr_roam_info *roam_info, + struct join_rsp *join_rsp) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct fils_join_rsp_params *roam_fils_info, + *fils_join_rsp = join_rsp->fils_join_rsp; + + if (!fils_join_rsp->fils_pmk_len || + !fils_join_rsp->fils_pmk || !fils_join_rsp->tk_len || + !fils_join_rsp->kek_len || !fils_join_rsp->gtk_len) { + sme_err("fils join rsp err: pmk len %d tk len %d kek len %d gtk len %d", + fils_join_rsp->fils_pmk_len, + fils_join_rsp->tk_len, + fils_join_rsp->kek_len, + fils_join_rsp->gtk_len); + status = QDF_STATUS_E_FAILURE; + goto free_fils_join_rsp; + } + + roam_info->fils_join_rsp = qdf_mem_malloc(sizeof(*fils_join_rsp)); + if (!roam_info->fils_join_rsp) { + status = QDF_STATUS_E_FAILURE; + goto free_fils_join_rsp; + } + + roam_fils_info = roam_info->fils_join_rsp; + roam_fils_info->fils_pmk = qdf_mem_malloc(fils_join_rsp->fils_pmk_len); + if (!roam_fils_info->fils_pmk) { + qdf_mem_free(roam_info->fils_join_rsp); + roam_info->fils_join_rsp = NULL; + status = QDF_STATUS_E_FAILURE; + goto free_fils_join_rsp; + } + + roam_info->fils_seq_num = join_rsp->fils_seq_num; + roam_fils_info->fils_pmk_len = fils_join_rsp->fils_pmk_len; + qdf_mem_copy(roam_fils_info->fils_pmk, + fils_join_rsp->fils_pmk, roam_fils_info->fils_pmk_len); + + qdf_mem_copy(roam_fils_info->fils_pmkid, + fils_join_rsp->fils_pmkid, PMKID_LEN); + + roam_fils_info->kek_len = fils_join_rsp->kek_len; + qdf_mem_copy(roam_fils_info->kek, + fils_join_rsp->kek, roam_fils_info->kek_len); + + roam_fils_info->tk_len = fils_join_rsp->tk_len; + qdf_mem_copy(roam_fils_info->tk, + fils_join_rsp->tk, fils_join_rsp->tk_len); + + roam_fils_info->gtk_len = fils_join_rsp->gtk_len; + qdf_mem_copy(roam_fils_info->gtk, + fils_join_rsp->gtk, roam_fils_info->gtk_len); + + cds_copy_hlp_info(&fils_join_rsp->dst_mac, &fils_join_rsp->src_mac, + fils_join_rsp->hlp_data_len, fils_join_rsp->hlp_data, + &roam_fils_info->dst_mac, &roam_fils_info->src_mac, + &roam_fils_info->hlp_data_len, + roam_fils_info->hlp_data); + sme_debug("FILS connect params copied to CSR!"); + +free_fils_join_rsp: + qdf_mem_free(fils_join_rsp->fils_pmk); + qdf_mem_free(fils_join_rsp); + return status; +} + +/** + * csr_process_fils_join_rsp() - Process join rsp for FILS connection + * @mac_ctx: Global MAC Context + * @profile: CSR Roam Profile + * @session_id: Session ID + * @roam_info: CSR Roam Info + * @bss_desc: BSS description + * @join_rsp: SME Join rsp + * + * Process SME join response for FILS connection + * + * Return: None + */ +static void csr_process_fils_join_rsp(struct mac_context *mac_ctx, + struct csr_roam_profile *profile, + uint32_t session_id, + struct csr_roam_info *roam_info, + struct bss_description *bss_desc, + struct join_rsp *join_rsp) +{ + QDF_STATUS status; + + if (!join_rsp || !join_rsp->fils_join_rsp) { + sme_err("Join rsp doesn't have FILS info"); + goto process_fils_join_rsp_fail; + } + + /* Copy FILS params */ + status = populate_fils_params_join_rsp(mac_ctx, roam_info, join_rsp); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Copy FILS params join rsp fails"); + goto process_fils_join_rsp_fail; + } + + status = csr_issue_set_context_req_helper(mac_ctx, profile, + session_id, &bss_desc->bssId, true, + true, eSIR_TX_RX, 0, + roam_info->fils_join_rsp->tk_len, + roam_info->fils_join_rsp->tk); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Set context for unicast fail"); + goto process_fils_join_rsp_fail; + } + + status = csr_issue_set_context_req_helper(mac_ctx, profile, + session_id, &bss_desc->bssId, true, false, + eSIR_RX_ONLY, 2, roam_info->fils_join_rsp->gtk_len, + roam_info->fils_join_rsp->gtk); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Set context for bcast fail"); + goto process_fils_join_rsp_fail; + } + return; + +process_fils_join_rsp_fail: + csr_roam_substate_change(mac_ctx, eCSR_ROAM_SUBSTATE_NONE, session_id); +} +#else + +static inline void csr_process_fils_join_rsp(struct mac_context *mac_ctx, + struct csr_roam_profile *profile, + uint32_t session_id, + struct csr_roam_info *roam_info, + struct bss_description *bss_desc, + struct join_rsp *join_rsp) +{} +#endif + +#ifdef WLAN_FEATURE_11AX +static void csr_roam_process_he_info(struct join_rsp *sme_join_rsp, + struct csr_roam_info *roam_info) +{ + roam_info->he_operation = sme_join_rsp->he_operation; +} +#else +static inline void csr_roam_process_he_info(struct join_rsp *sme_join_rsp, + struct csr_roam_info *roam_info) +{ +} +#endif + +static void csr_update_tx_pwr_to_fw(struct mac_context *mac_ctx, + uint8_t vdev_id) +{ + struct scheduler_msg msg = {0}; + + msg.type = WMA_SEND_MAX_TX_POWER; + msg.bodyval = vdev_id; + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + sme_err("Failed to post WMA_SEND_MAX_TX_POWER message to WMA"); + } +} + +/** + * csr_roam_process_join_res() - Process the Join results + * @mac_ctx: Global MAC Context + * @result: Result after the command was processed + * @cmd: Command to be processed + * @context: Additional data in context of the cmd + * + * Process the join results which are obtained in a successful join + * + * Return: None + */ +static void csr_roam_process_join_res(struct mac_context *mac_ctx, + enum csr_roamcomplete_result res, tSmeCmd *cmd, void *context) +{ + tSirMacAddr bcast_mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + sme_QosAssocInfo assoc_info; + uint32_t key_timeout_interval = 0; + uint8_t acm_mask = 0; /* HDD needs ACM mask in assoc rsp callback */ + uint32_t session_id = cmd->vdev_id; + struct csr_roam_profile *profile = &cmd->u.roamCmd.roamProfile; + struct csr_roam_session *session; + struct bss_description *bss_desc = NULL; + struct tag_csrscan_result *scan_res = NULL; + sme_qos_csr_event_indType ind_qos; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + struct ht_profile *src_profile = NULL; + tCsrRoamHTProfile *dst_profile = NULL; +#endif + tCsrRoamConnectedProfile *conn_profile = NULL; + tDot11fBeaconIEs *ies_ptr = NULL; + struct csr_roam_info *roam_info; + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct join_rsp *join_rsp = context; + uint32_t len; + + if (!join_rsp) { + sme_err("join_rsp is NULL"); + return; + } + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("Invalid session id %d", session_id); + return; + } + session = CSR_GET_SESSION(mac_ctx, session_id); + + conn_profile = &session->connectedProfile; + sme_debug("receives association indication"); + /* always free the memory here */ + if (session->pWpaRsnRspIE) { + session->nWpaRsnRspIeLength = 0; + qdf_mem_free(session->pWpaRsnRspIE); + session->pWpaRsnRspIE = NULL; + } +#ifdef FEATURE_WLAN_WAPI + if (session->pWapiRspIE) { + session->nWapiRspIeLength = 0; + qdf_mem_free(session->pWapiRspIE); + session->pWapiRspIE = NULL; + } +#endif /* FEATURE_WLAN_WAPI */ + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + if (eCsrReassocSuccess == res) { + roam_info->reassoc = true; + ind_qos = SME_QOS_CSR_REASSOC_COMPLETE; + } else { + roam_info->reassoc = false; + ind_qos = SME_QOS_CSR_ASSOC_COMPLETE; + } + + /* + * Reset remain_in_power_active_till_dhcp as + * it might have been set by last failed secured connection. + * It should be set only for secured connection. + */ + ps_global_info->remain_in_power_active_till_dhcp = false; + if (CSR_IS_INFRASTRUCTURE(profile)) + session->connectState = eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED; + else + session->connectState = eCSR_ASSOC_STATE_TYPE_WDS_CONNECTED; + /* + * Use the last connected bssdesc for reassoc-ing to the same AP. + * NOTE: What to do when reassoc to a different AP??? + */ + if ((eCsrHddIssuedReassocToSameAP == cmd->u.roamCmd.roamReason) + || (eCsrSmeIssuedReassocToSameAP == + cmd->u.roamCmd.roamReason)) { + bss_desc = session->pConnectBssDesc; + if (bss_desc) + qdf_mem_copy(&roam_info->bssid, &bss_desc->bssId, + sizeof(struct qdf_mac_addr)); + } else { + if (cmd->u.roamCmd.pRoamBssEntry) { + scan_res = GET_BASE_ADDR(cmd->u.roamCmd.pRoamBssEntry, + struct tag_csrscan_result, Link); + if (scan_res) { + bss_desc = &scan_res->Result.BssDescriptor; + ies_ptr = (tDot11fBeaconIEs *) + (scan_res->Result.pvIes); + qdf_mem_copy(&roam_info->bssid, + &bss_desc->bssId, + sizeof(struct qdf_mac_addr)); + } + } + } + if (bss_desc) { + roam_info->staId = STA_INVALID_IDX; + csr_roam_save_connected_information(mac_ctx, session_id, + profile, bss_desc, ies_ptr); + /* Save WPA/RSN IE */ + csr_roam_save_security_rsp_ie(mac_ctx, session_id, + profile->negotiatedAuthType, bss_desc, ies_ptr); +#ifdef FEATURE_WLAN_ESE + roam_info->isESEAssoc = conn_profile->isESEAssoc; +#endif + + /* + * csr_roam_state_change also affects sub-state. + * Hence, csr_roam_state_change happens first and then + * substate change. + * Moving even save profile above so that below + * mentioned conditon is also met. + * JEZ100225: Moved to after saving the profile. + * Fix needed in main/latest + */ + csr_roam_state_change(mac_ctx, + eCSR_ROAMING_STATE_JOINED, session_id); + + /* + * Make sure the Set Context is issued before link + * indication to NDIS. After link indication is + * made to NDIS, frames could start flowing. + * If we have not set context with LIM, the frames + * will be dropped for the security context may not + * be set properly. + * + * this was causing issues in the 2c_wlan_wep WHQL test + * when the SetContext was issued after the link + * indication. (Link Indication happens in the + * profFSMSetConnectedInfra call). + * + * this reordering was done on titan_prod_usb branch + * and is being replicated here. + */ + + if (CSR_IS_ENC_TYPE_STATIC + (profile->negotiatedUCEncryptionType) && + !profile->bWPSAssociation) { + /* + * Issue the set Context request to LIM to establish + * the Unicast STA context + */ + if (QDF_IS_STATUS_ERROR( + csr_issue_set_context_req_helper(mac_ctx, + profile, session_id, &bss_desc->bssId, + false, true, eSIR_TX_RX, 0, 0, NULL))) { + /* NO keys. these key parameters don't matter */ + sme_debug("Set context for unicast fail"); + csr_roam_substate_change(mac_ctx, + eCSR_ROAM_SUBSTATE_NONE, session_id); + } + /* + * Issue the set Context request to LIM + * to establish the Broadcast STA context + * NO keys. these key parameters don't matter + */ + csr_issue_set_context_req_helper(mac_ctx, profile, + session_id, &bcast_mac, + false, false, + eSIR_TX_RX, 0, 0, + NULL); + } else if (CSR_IS_AUTH_TYPE_FILS(profile->negotiatedAuthType) + && join_rsp->is_fils_connection) { + roam_info->is_fils_connection = true; + csr_process_fils_join_rsp(mac_ctx, profile, session_id, + roam_info, bss_desc, + join_rsp); + } else { + /* Need to wait for supplicant authtication */ + roam_info->fAuthRequired = true; + /* + * Set the substate to WaitForKey in case + * authentiation is needed + */ + csr_roam_substate_change(mac_ctx, + eCSR_ROAM_SUBSTATE_WAIT_FOR_KEY, + session_id); + + /* + * Set remain_in_power_active_till_dhcp to make + * sure we wait for until keys are set before + * going into BMPS. + */ + ps_global_info->remain_in_power_active_till_dhcp + = true; + + if (profile->bWPSAssociation) + key_timeout_interval = + CSR_WAIT_FOR_WPS_KEY_TIMEOUT_PERIOD; + else + key_timeout_interval = + CSR_WAIT_FOR_KEY_TIMEOUT_PERIOD; + + /* Save session_id in case of timeout */ + mac_ctx->roam.WaitForKeyTimerInfo.vdev_id = + (uint8_t) session_id; + /* + * This time should be long enough for the rest + * of the process plus setting key + */ + if (!QDF_IS_STATUS_SUCCESS + (csr_roam_start_wait_for_key_timer( + mac_ctx, key_timeout_interval)) + ) { + /* Reset state so nothing is blocked. */ + sme_err("Failed preauth timer start"); + csr_roam_substate_change(mac_ctx, + eCSR_ROAM_SUBSTATE_NONE, + session_id); + } + } + + assoc_info.bss_desc = bss_desc; /* could be NULL */ + assoc_info.pProfile = profile; + if (context) { +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (session->roam_synch_in_progress) + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("LFR3:Clear Connected info")); +#endif + csr_roam_free_connected_info(mac_ctx, + &session->connectedInfo); + len = join_rsp->assocReqLength + + join_rsp->assocRspLength + + join_rsp->beaconLength; + len += join_rsp->parsedRicRspLen; +#ifdef FEATURE_WLAN_ESE + len += join_rsp->tspecIeLen; +#endif + if (len) { + session->connectedInfo.pbFrames = + qdf_mem_malloc(len); + if (session->connectedInfo.pbFrames != + NULL) { + qdf_mem_copy( + session->connectedInfo.pbFrames, + join_rsp->frames, len); + session->connectedInfo.nAssocReqLength = + join_rsp->assocReqLength; + session->connectedInfo.nAssocRspLength = + join_rsp->assocRspLength; + session->connectedInfo.nBeaconLength = + join_rsp->beaconLength; + session->connectedInfo.nRICRspLength = + join_rsp->parsedRicRspLen; +#ifdef FEATURE_WLAN_ESE + session->connectedInfo.nTspecIeLength = + join_rsp->tspecIeLen; +#endif + roam_info->nAssocReqLength = + join_rsp->assocReqLength; + roam_info->nAssocRspLength = + join_rsp->assocRspLength; + roam_info->nBeaconLength = + join_rsp->beaconLength; + roam_info->pbFrames = + session->connectedInfo.pbFrames; + } + } + if (cmd->u.roamCmd.fReassoc) + roam_info->fReassocReq = + roam_info->fReassocRsp = true; + conn_profile->vht_channel_width = + join_rsp->vht_channel_width; + session->connectedInfo.staId = + (uint8_t)join_rsp->staId; + roam_info->staId = (uint8_t)join_rsp->staId; + roam_info->timingMeasCap = join_rsp->timingMeasCap; + roam_info->chan_info.nss = join_rsp->nss; + roam_info->chan_info.rate_flags = + join_rsp->max_rate_flags; + roam_info->chan_info.ch_width = + join_rsp->vht_channel_width; +#ifdef FEATURE_WLAN_TDLS + roam_info->tdls_prohibited = join_rsp->tdls_prohibited; + roam_info->tdls_chan_swit_prohibited = + join_rsp->tdls_chan_swit_prohibited; + sme_debug("tdls:prohibit: %d chan_swit_prohibit: %d", + roam_info->tdls_prohibited, + roam_info->tdls_chan_swit_prohibited); +#endif +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + src_profile = &join_rsp->ht_profile; + dst_profile = &conn_profile->ht_profile; + if (mac_ctx->roam.configParam.cc_switch_mode + != QDF_MCC_TO_SCC_SWITCH_DISABLE) + csr_roam_copy_ht_profile(dst_profile, + src_profile); +#endif + roam_info->vht_caps = join_rsp->vht_caps; + roam_info->ht_caps = join_rsp->ht_caps; + roam_info->hs20vendor_ie = join_rsp->hs20vendor_ie; + roam_info->ht_operation = join_rsp->ht_operation; + roam_info->vht_operation = join_rsp->vht_operation; + csr_roam_process_he_info(join_rsp, roam_info); + } else { + if (cmd->u.roamCmd.fReassoc) { + roam_info->fReassocReq = + roam_info->fReassocRsp = true; + roam_info->nAssocReqLength = + session->connectedInfo.nAssocReqLength; + roam_info->nAssocRspLength = + session->connectedInfo.nAssocRspLength; + roam_info->nBeaconLength = + session->connectedInfo.nBeaconLength; + roam_info->pbFrames = + session->connectedInfo.pbFrames; + } + } + /* + * Update the staId from the previous connected profile info + * as the reassociation is triggred at SME/HDD + */ + + if ((eCsrHddIssuedReassocToSameAP == + cmd->u.roamCmd.roamReason) || + (eCsrSmeIssuedReassocToSameAP == + cmd->u.roamCmd.roamReason)) + roam_info->staId = session->connectedInfo.staId; + +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + /* + * Indicate SME-QOS with reassoc success event, + * only after copying the frames + */ + sme_qos_csr_event_ind(mac_ctx, (uint8_t) session_id, ind_qos, + &assoc_info); +#endif + roam_info->bss_desc = bss_desc; + roam_info->status_code = + session->joinFailStatusCode.status_code; + roam_info->reasonCode = + session->joinFailStatusCode.reasonCode; +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + acm_mask = sme_qos_get_acm_mask(mac_ctx, bss_desc, NULL); +#endif + conn_profile->acm_mask = acm_mask; + conn_profile->modifyProfileFields.uapsd_mask = + join_rsp->uapsd_mask; + /* + * start UAPSD if uapsd_mask is not 0 because HDD will + * configure for trigger frame It may be better to let QoS do + * this???? + */ + if (conn_profile->modifyProfileFields.uapsd_mask) { + sme_err( + " uapsd_mask (0x%X) set, request UAPSD now", + conn_profile->modifyProfileFields.uapsd_mask); + sme_ps_start_uapsd(MAC_HANDLE(mac_ctx), session_id); + } + conn_profile->dot11Mode = session->bssParams.uCfgDot11Mode; + roam_info->u.pConnectedProfile = conn_profile; + + if (session->bRefAssocStartCnt > 0) { + session->bRefAssocStartCnt--; + if (!IS_FEATURE_SUPPORTED_BY_FW + (SLM_SESSIONIZATION) && + (csr_is_concurrent_session_running(mac_ctx))) { + mac_ctx->roam.configParam.doBMPSWorkaround = 1; + } + csr_roam_call_callback(mac_ctx, session_id, roam_info, + cmd->u.roamCmd.roamId, + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_RESULT_ASSOCIATED); + } + + csr_update_scan_entry_associnfo(mac_ctx, session, + SCAN_ENTRY_CON_STATE_ASSOC); + csr_roam_completion(mac_ctx, session_id, NULL, cmd, + eCSR_ROAM_RESULT_NONE, true); + csr_reset_pmkid_candidate_list(mac_ctx, session_id); + } else { + sme_warn("Roam command doesn't have a BSS desc"); + } + + if (csr_roam_is_sta_mode(mac_ctx, session_id)) + csr_post_roam_state_change(mac_ctx, session_id, ROAM_INIT, + REASON_CONNECT); + + /* Not to signal link up because keys are yet to be set. + * The linkup function will overwrite the sub-state that + * we need to keep at this point. + */ + if (!CSR_IS_WAIT_FOR_KEY(mac_ctx, session_id)) { +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (session->roam_synch_in_progress) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL + ("NO CSR_IS_WAIT_FOR_KEY -> csr_roam_link_up")); + } +#endif + csr_roam_link_up(mac_ctx, conn_profile->bssid); + } + + csr_update_tx_pwr_to_fw(mac_ctx, session_id); + sme_free_join_rsp_fils_params(roam_info); + qdf_mem_free(roam_info); +} + +/** + * csr_roam_process_results() - Process the Roam Results + * @mac_ctx: Global MAC Context + * @cmd: Command that has been processed + * @res: Results available after processing the command + * @context: Context + * + * Process the available results and make an appropriate decision + * + * Return: true if the command can be released, else not. + */ +static bool csr_roam_process_results(struct mac_context *mac_ctx, tSmeCmd *cmd, + enum csr_roamcomplete_result res, + void *context) +{ + bool release_cmd = true; + struct bss_description *bss_desc = NULL; + struct csr_roam_info *roam_info; + uint32_t session_id = cmd->vdev_id; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + struct csr_roam_profile *profile = &cmd->u.roamCmd.roamProfile; + eRoamCmdStatus roam_status; + eCsrRoamResult roam_result; + host_log_ibss_pkt_type *ibss_log; + struct start_bss_rsp *start_bss_rsp = NULL; + + if (!session) { + sme_err("session %d not found ", session_id); + return false; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return false; + + sme_debug("Result %d", res); + switch (res) { + case eCsrJoinSuccess: + case eCsrReassocSuccess: + csr_roam_process_join_res(mac_ctx, res, cmd, context); + break; + case eCsrStartBssSuccess: + csr_roam_process_start_bss_success(mac_ctx, cmd, context); + break; + case eCsrStartBssFailure: +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + WLAN_HOST_DIAG_LOG_ALLOC(ibss_log, + host_log_ibss_pkt_type, LOG_WLAN_IBSS_C); + if (ibss_log) { + ibss_log->status = WLAN_IBSS_STATUS_FAILURE; + WLAN_HOST_DIAG_LOG_REPORT(ibss_log); + } +#endif + ibss_log = NULL; + start_bss_rsp = (struct start_bss_rsp *)context; + roam_status = eCSR_ROAM_IBSS_IND; + roam_result = eCSR_ROAM_RESULT_IBSS_STARTED; + if (CSR_IS_INFRA_AP(profile)) { + roam_status = eCSR_ROAM_INFRA_IND; + roam_result = eCSR_ROAM_RESULT_INFRA_START_FAILED; + } + if (CSR_IS_NDI(profile)) { + csr_roam_update_ndp_return_params(mac_ctx, + eCsrStartBssFailure, + &roam_status, + &roam_result, + roam_info); + } + + if (context) + bss_desc = (struct bss_description *) context; + else + bss_desc = NULL; + roam_info->bss_desc = bss_desc; + csr_roam_call_callback(mac_ctx, session_id, roam_info, + cmd->u.roamCmd.roamId, roam_status, + roam_result); + csr_set_default_dot11_mode(mac_ctx); + break; + case eCsrSilentlyStopRoaming: + /* + * We are here because we try to start the same IBSS. + * No message to PE. return the roaming state to Joined. + */ + sme_debug("receives silently stop roam ind"); + csr_roam_state_change(mac_ctx, eCSR_ROAMING_STATE_JOINED, + session_id); + csr_roam_substate_change(mac_ctx, eCSR_ROAM_SUBSTATE_NONE, + session_id); + qdf_mem_zero(roam_info, sizeof(*roam_info)); + roam_info->bss_desc = session->pConnectBssDesc; + if (roam_info->bss_desc) + qdf_mem_copy(&roam_info->bssid, + &roam_info->bss_desc->bssId, + sizeof(struct qdf_mac_addr)); + /* + * Since there is no change in the current state, simply pass + * back no result otherwise HDD may be mistakenly mark to + * disconnected state. + */ + csr_roam_call_callback(mac_ctx, session_id, roam_info, + cmd->u.roamCmd.roamId, + eCSR_ROAM_IBSS_IND, + eCSR_ROAM_RESULT_NONE); + break; + case eCsrSilentlyStopRoamingSaveState: + /* We are here because we try to connect to the same AP */ + /* No message to PE */ + sme_debug("receives silently stop roaming indication"); + qdf_mem_zero(roam_info, sizeof(*roam_info)); + + /* to aviod resetting the substate to NONE */ + mac_ctx->roam.curState[session_id] = eCSR_ROAMING_STATE_JOINED; + /* + * No need to change substate to wai_for_key because there + * is no state change + */ + roam_info->bss_desc = session->pConnectBssDesc; + if (roam_info->bss_desc) + qdf_mem_copy(&roam_info->bssid, + &roam_info->bss_desc->bssId, + sizeof(struct qdf_mac_addr)); + roam_info->status_code = + session->joinFailStatusCode.status_code; + roam_info->reasonCode = session->joinFailStatusCode.reasonCode; + roam_info->nBeaconLength = session->connectedInfo.nBeaconLength; + roam_info->nAssocReqLength = + session->connectedInfo.nAssocReqLength; + roam_info->nAssocRspLength = + session->connectedInfo.nAssocRspLength; + roam_info->pbFrames = session->connectedInfo.pbFrames; + roam_info->staId = session->connectedInfo.staId; + roam_info->u.pConnectedProfile = &session->connectedProfile; + if (0 == roam_info->staId) + QDF_ASSERT(0); + + session->bRefAssocStartCnt--; + csr_roam_call_callback(mac_ctx, session_id, roam_info, + cmd->u.roamCmd.roamId, + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_RESULT_ASSOCIATED); + csr_roam_completion(mac_ctx, session_id, NULL, cmd, + eCSR_ROAM_RESULT_ASSOCIATED, true); + break; + case eCsrReassocFailure: + /* + * Currently Reassoc failure is handled through eCsrJoinFailure + * Need to revisit for eCsrReassocFailure handling + */ +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_qos_csr_event_ind(mac_ctx, (uint8_t) session_id, + SME_QOS_CSR_REASSOC_FAILURE, NULL); +#endif + break; + case eCsrStopBssSuccess: + if (CSR_IS_NDI(profile)) { + qdf_mem_zero(roam_info, sizeof(*roam_info)); + csr_roam_update_ndp_return_params(mac_ctx, res, + &roam_status, + &roam_result, + roam_info); + csr_roam_call_callback(mac_ctx, session_id, roam_info, + cmd->u.roamCmd.roamId, + roam_status, roam_result); + } + break; + case eCsrStopBssFailure: + if (CSR_IS_NDI(profile)) { + qdf_mem_zero(roam_info, sizeof(*roam_info)); + csr_roam_update_ndp_return_params(mac_ctx, res, + &roam_status, + &roam_result, + roam_info); + csr_roam_call_callback(mac_ctx, session_id, roam_info, + cmd->u.roamCmd.roamId, + roam_status, roam_result); + } + break; + case eCsrJoinFailure: + case eCsrNothingToJoin: + case eCsrJoinFailureDueToConcurrency: + default: + csr_roam_process_results_default(mac_ctx, cmd, context, res); + break; + } + qdf_mem_free(roam_info); + return release_cmd; +} + +#ifdef WLAN_FEATURE_FILS_SK +/* + * update_profile_fils_info: API to update FILS info from + * source profile to destination profile. + * @des_profile: pointer to destination profile + * @src_profile: pointer to souce profile + * + * Return: None + */ +static void update_profile_fils_info(struct csr_roam_profile *des_profile, + struct csr_roam_profile *src_profile) +{ + if (!src_profile || !src_profile->fils_con_info) + return; + + sme_debug("is fils %d", src_profile->fils_con_info->is_fils_connection); + + if (!src_profile->fils_con_info->is_fils_connection) + return; + + des_profile->fils_con_info = + qdf_mem_malloc(sizeof(struct cds_fils_connection_info)); + if (!des_profile->fils_con_info) + return; + + qdf_mem_copy(des_profile->fils_con_info, + src_profile->fils_con_info, + sizeof(struct cds_fils_connection_info)); + + des_profile->hlp_ie = + qdf_mem_malloc(src_profile->hlp_ie_len); + if (!des_profile->hlp_ie) + return; + + qdf_mem_copy(des_profile->hlp_ie, src_profile->hlp_ie, + src_profile->hlp_ie_len); + des_profile->hlp_ie_len = src_profile->hlp_ie_len; +} +#else +static inline +void update_profile_fils_info(struct csr_roam_profile *des_profile, + struct csr_roam_profile *src_profile) +{ } +#endif +QDF_STATUS csr_roam_copy_profile(struct mac_context *mac, + struct csr_roam_profile *pDstProfile, + struct csr_roam_profile *pSrcProfile) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t size = 0; + + qdf_mem_zero(pDstProfile, sizeof(struct csr_roam_profile)); + if (pSrcProfile->BSSIDs.numOfBSSIDs) { + size = sizeof(struct qdf_mac_addr) * pSrcProfile->BSSIDs. + numOfBSSIDs; + pDstProfile->BSSIDs.bssid = qdf_mem_malloc(size); + if (!pDstProfile->BSSIDs.bssid) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->BSSIDs.numOfBSSIDs = + pSrcProfile->BSSIDs.numOfBSSIDs; + qdf_mem_copy(pDstProfile->BSSIDs.bssid, + pSrcProfile->BSSIDs.bssid, size); + } + if (pSrcProfile->SSIDs.numOfSSIDs) { + size = sizeof(tCsrSSIDInfo) * pSrcProfile->SSIDs.numOfSSIDs; + pDstProfile->SSIDs.SSIDList = qdf_mem_malloc(size); + if (!pDstProfile->SSIDs.SSIDList) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->SSIDs.numOfSSIDs = + pSrcProfile->SSIDs.numOfSSIDs; + qdf_mem_copy(pDstProfile->SSIDs.SSIDList, + pSrcProfile->SSIDs.SSIDList, size); + } + if (pSrcProfile->nWPAReqIELength) { + pDstProfile->pWPAReqIE = + qdf_mem_malloc(pSrcProfile->nWPAReqIELength); + if (!pDstProfile->pWPAReqIE) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->nWPAReqIELength = + pSrcProfile->nWPAReqIELength; + qdf_mem_copy(pDstProfile->pWPAReqIE, pSrcProfile->pWPAReqIE, + pSrcProfile->nWPAReqIELength); + } + if (pSrcProfile->nRSNReqIELength) { + pDstProfile->pRSNReqIE = + qdf_mem_malloc(pSrcProfile->nRSNReqIELength); + if (!pDstProfile->pRSNReqIE) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->nRSNReqIELength = + pSrcProfile->nRSNReqIELength; + qdf_mem_copy(pDstProfile->pRSNReqIE, pSrcProfile->pRSNReqIE, + pSrcProfile->nRSNReqIELength); + } +#ifdef FEATURE_WLAN_WAPI + if (pSrcProfile->nWAPIReqIELength) { + pDstProfile->pWAPIReqIE = + qdf_mem_malloc(pSrcProfile->nWAPIReqIELength); + if (!pDstProfile->pWAPIReqIE) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->nWAPIReqIELength = + pSrcProfile->nWAPIReqIELength; + qdf_mem_copy(pDstProfile->pWAPIReqIE, pSrcProfile->pWAPIReqIE, + pSrcProfile->nWAPIReqIELength); + } +#endif /* FEATURE_WLAN_WAPI */ + if (pSrcProfile->nAddIEScanLength) { + pDstProfile->pAddIEScan = + qdf_mem_malloc(pSrcProfile->nAddIEScanLength); + if (!pDstProfile->pAddIEScan) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->nAddIEScanLength = + pSrcProfile->nAddIEScanLength; + qdf_mem_copy(pDstProfile->pAddIEScan, pSrcProfile->pAddIEScan, + pSrcProfile->nAddIEScanLength); + } + if (pSrcProfile->nAddIEAssocLength) { + pDstProfile->pAddIEAssoc = + qdf_mem_malloc(pSrcProfile->nAddIEAssocLength); + if (!pDstProfile->pAddIEAssoc) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->nAddIEAssocLength = + pSrcProfile->nAddIEAssocLength; + qdf_mem_copy(pDstProfile->pAddIEAssoc, pSrcProfile->pAddIEAssoc, + pSrcProfile->nAddIEAssocLength); + } + if (pSrcProfile->ChannelInfo.freq_list) { + pDstProfile->ChannelInfo.freq_list = + qdf_mem_malloc(sizeof(uint32_t) * + pSrcProfile->ChannelInfo.numOfChannels); + if (!pDstProfile->ChannelInfo.freq_list) { + pDstProfile->ChannelInfo.numOfChannels = 0; + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->ChannelInfo.numOfChannels = + pSrcProfile->ChannelInfo.numOfChannels; + qdf_mem_copy(pDstProfile->ChannelInfo.freq_list, + pSrcProfile->ChannelInfo.freq_list, + sizeof(uint32_t) * + pSrcProfile->ChannelInfo.numOfChannels); + } + pDstProfile->AuthType = pSrcProfile->AuthType; + pDstProfile->akm_list = pSrcProfile->akm_list; + pDstProfile->EncryptionType = pSrcProfile->EncryptionType; + pDstProfile->mcEncryptionType = pSrcProfile->mcEncryptionType; + pDstProfile->negotiatedUCEncryptionType = + pSrcProfile->negotiatedUCEncryptionType; + pDstProfile->negotiatedMCEncryptionType = + pSrcProfile->negotiatedMCEncryptionType; + pDstProfile->negotiatedAuthType = pSrcProfile->negotiatedAuthType; +#ifdef WLAN_FEATURE_11W + pDstProfile->MFPEnabled = pSrcProfile->MFPEnabled; + pDstProfile->MFPRequired = pSrcProfile->MFPRequired; + pDstProfile->MFPCapable = pSrcProfile->MFPCapable; +#endif + pDstProfile->BSSType = pSrcProfile->BSSType; + pDstProfile->phyMode = pSrcProfile->phyMode; + pDstProfile->csrPersona = pSrcProfile->csrPersona; + +#ifdef FEATURE_WLAN_WAPI + if (csr_is_profile_wapi(pSrcProfile)) + if (pDstProfile->phyMode & eCSR_DOT11_MODE_11n) + pDstProfile->phyMode &= ~eCSR_DOT11_MODE_11n; +#endif /* FEATURE_WLAN_WAPI */ + pDstProfile->ch_params.ch_width = pSrcProfile->ch_params.ch_width; + pDstProfile->ch_params.center_freq_seg0 = + pSrcProfile->ch_params.center_freq_seg0; + pDstProfile->ch_params.center_freq_seg1 = + pSrcProfile->ch_params.center_freq_seg1; + pDstProfile->ch_params.sec_ch_offset = + pSrcProfile->ch_params.sec_ch_offset; + /*Save the WPS info */ + pDstProfile->bWPSAssociation = pSrcProfile->bWPSAssociation; + pDstProfile->bOSENAssociation = pSrcProfile->bOSENAssociation; + pDstProfile->force_24ghz_in_ht20 = pSrcProfile->force_24ghz_in_ht20; + pDstProfile->uapsd_mask = pSrcProfile->uapsd_mask; + pDstProfile->beaconInterval = pSrcProfile->beaconInterval; + pDstProfile->privacy = pSrcProfile->privacy; + pDstProfile->fwdWPSPBCProbeReq = pSrcProfile->fwdWPSPBCProbeReq; + pDstProfile->csr80211AuthType = pSrcProfile->csr80211AuthType; + pDstProfile->dtimPeriod = pSrcProfile->dtimPeriod; + pDstProfile->ApUapsdEnable = pSrcProfile->ApUapsdEnable; + pDstProfile->SSIDs.SSIDList[0].ssidHidden = + pSrcProfile->SSIDs.SSIDList[0].ssidHidden; + pDstProfile->protEnabled = pSrcProfile->protEnabled; + pDstProfile->obssProtEnabled = pSrcProfile->obssProtEnabled; + pDstProfile->cfg_protection = pSrcProfile->cfg_protection; + pDstProfile->wps_state = pSrcProfile->wps_state; + pDstProfile->ieee80211d = pSrcProfile->ieee80211d; + qdf_mem_copy(&pDstProfile->Keys, &pSrcProfile->Keys, + sizeof(pDstProfile->Keys)); +#ifdef WLAN_FEATURE_11W + pDstProfile->MFPEnabled = pSrcProfile->MFPEnabled; + pDstProfile->MFPRequired = pSrcProfile->MFPRequired; + pDstProfile->MFPCapable = pSrcProfile->MFPCapable; +#endif + pDstProfile->mdid = pSrcProfile->mdid; + pDstProfile->add_ie_params = pSrcProfile->add_ie_params; + + update_profile_fils_info(pDstProfile, pSrcProfile); + + pDstProfile->beacon_tx_rate = pSrcProfile->beacon_tx_rate; + + if (pSrcProfile->supported_rates.numRates) { + qdf_mem_copy(pDstProfile->supported_rates.rate, + pSrcProfile->supported_rates.rate, + pSrcProfile->supported_rates.numRates); + pDstProfile->supported_rates.numRates = + pSrcProfile->supported_rates.numRates; + } + if (pSrcProfile->extended_rates.numRates) { + qdf_mem_copy(pDstProfile->extended_rates.rate, + pSrcProfile->extended_rates.rate, + pSrcProfile->extended_rates.numRates); + pDstProfile->extended_rates.numRates = + pSrcProfile->extended_rates.numRates; + } + pDstProfile->require_h2e = pSrcProfile->require_h2e; + pDstProfile->cac_duration_ms = pSrcProfile->cac_duration_ms; + pDstProfile->dfs_regdomain = pSrcProfile->dfs_regdomain; + pDstProfile->chan_switch_hostapd_rate_enabled = + pSrcProfile->chan_switch_hostapd_rate_enabled; + pDstProfile->force_rsne_override = pSrcProfile->force_rsne_override; +end: + if (!QDF_IS_STATUS_SUCCESS(status)) { + csr_release_profile(mac, pDstProfile); + pDstProfile = NULL; + } + + return status; +} + +QDF_STATUS csr_roam_copy_connected_profile(struct mac_context *mac, + uint32_t sessionId, + struct csr_roam_profile *pDstProfile) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tCsrRoamConnectedProfile *pSrcProfile = + &mac->roam.roamSession[sessionId].connectedProfile; + + qdf_mem_zero(pDstProfile, sizeof(struct csr_roam_profile)); + + pDstProfile->BSSIDs.bssid = qdf_mem_malloc(sizeof(struct qdf_mac_addr)); + if (!pDstProfile->BSSIDs.bssid) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->BSSIDs.numOfBSSIDs = 1; + qdf_copy_macaddr(pDstProfile->BSSIDs.bssid, &pSrcProfile->bssid); + + if (pSrcProfile->SSID.length > 0) { + pDstProfile->SSIDs.SSIDList = + qdf_mem_malloc(sizeof(tCsrSSIDInfo)); + if (!pDstProfile->SSIDs.SSIDList) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->SSIDs.numOfSSIDs = 1; + pDstProfile->SSIDs.SSIDList[0].handoffPermitted = + pSrcProfile->handoffPermitted; + pDstProfile->SSIDs.SSIDList[0].ssidHidden = + pSrcProfile->ssidHidden; + qdf_mem_copy(&pDstProfile->SSIDs.SSIDList[0].SSID, + &pSrcProfile->SSID, sizeof(tSirMacSSid)); + } + if (pSrcProfile->nAddIEAssocLength) { + pDstProfile->pAddIEAssoc = + qdf_mem_malloc(pSrcProfile->nAddIEAssocLength); + if (!pDstProfile->pAddIEAssoc) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->nAddIEAssocLength = pSrcProfile->nAddIEAssocLength; + qdf_mem_copy(pDstProfile->pAddIEAssoc, pSrcProfile->pAddIEAssoc, + pSrcProfile->nAddIEAssocLength); + } + pDstProfile->ChannelInfo.freq_list = qdf_mem_malloc(sizeof(uint32_t)); + if (!pDstProfile->ChannelInfo.freq_list) { + pDstProfile->ChannelInfo.numOfChannels = 0; + status = QDF_STATUS_E_NOMEM; + goto end; + } + pDstProfile->ChannelInfo.numOfChannels = 1; + pDstProfile->ChannelInfo.freq_list[0] = pSrcProfile->op_freq; + pDstProfile->AuthType.numEntries = 1; + pDstProfile->AuthType.authType[0] = pSrcProfile->AuthType; + pDstProfile->negotiatedAuthType = pSrcProfile->AuthType; + pDstProfile->akm_list = pSrcProfile->akm_list; + pDstProfile->EncryptionType.numEntries = 1; + pDstProfile->EncryptionType.encryptionType[0] = + pSrcProfile->EncryptionType; + pDstProfile->negotiatedUCEncryptionType = + pSrcProfile->EncryptionType; + pDstProfile->mcEncryptionType.numEntries = 1; + pDstProfile->mcEncryptionType.encryptionType[0] = + pSrcProfile->mcEncryptionType; + pDstProfile->negotiatedMCEncryptionType = + pSrcProfile->mcEncryptionType; + pDstProfile->BSSType = pSrcProfile->BSSType; + qdf_mem_copy(&pDstProfile->Keys, &pSrcProfile->Keys, + sizeof(pDstProfile->Keys)); + pDstProfile->mdid = pSrcProfile->mdid; +#ifdef WLAN_FEATURE_11W + pDstProfile->MFPEnabled = pSrcProfile->MFPEnabled; + pDstProfile->MFPRequired = pSrcProfile->MFPRequired; + pDstProfile->MFPCapable = pSrcProfile->MFPCapable; +#endif + +end: + if (!QDF_IS_STATUS_SUCCESS(status)) { + csr_release_profile(mac, pDstProfile); + pDstProfile = NULL; + } + + return status; +} + +QDF_STATUS csr_roam_issue_connect(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + tScanResultHandle hBSSList, + enum csr_roam_reason reason, uint32_t roamId, + bool fImediate, bool fClearScan) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSmeCmd *pCommand; + + pCommand = csr_get_command_buffer(mac); + if (!pCommand) { + csr_scan_result_purge(mac, hBSSList); + sme_err(" fail to get command buffer"); + status = QDF_STATUS_E_RESOURCES; + } else { + if (fClearScan) + csr_scan_abort_mac_scan(mac, sessionId, INVAL_SCAN_ID); + + pCommand->u.roamCmd.fReleaseProfile = false; + if (!pProfile) { + /* We can roam now + * Since pProfile is NULL, we need to build our own + * profile, set everything to default We can only + * support open and no encryption + */ + pCommand->u.roamCmd.roamProfile.AuthType.numEntries = 1; + pCommand->u.roamCmd.roamProfile.AuthType.authType[0] = + eCSR_AUTH_TYPE_OPEN_SYSTEM; + pCommand->u.roamCmd.roamProfile.EncryptionType. + numEntries = 1; + pCommand->u.roamCmd.roamProfile.EncryptionType. + encryptionType[0] = eCSR_ENCRYPT_TYPE_NONE; + pCommand->u.roamCmd.roamProfile.csrPersona = + QDF_STA_MODE; + } else { + /* make a copy of the profile */ + status = csr_roam_copy_profile(mac, &pCommand->u. + roamCmd.roamProfile, + pProfile); + if (QDF_IS_STATUS_SUCCESS(status)) + pCommand->u.roamCmd.fReleaseProfile = true; + } + + pCommand->command = eSmeCommandRoam; + pCommand->vdev_id = (uint8_t) sessionId; + pCommand->u.roamCmd.hBSSList = hBSSList; + pCommand->u.roamCmd.roamId = roamId; + pCommand->u.roamCmd.roamReason = reason; + /* We need to free the BssList when the command is done */ + pCommand->u.roamCmd.fReleaseBssList = true; + pCommand->u.roamCmd.fUpdateCurRoamProfile = true; + status = csr_queue_sme_command(mac, pCommand, fImediate); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("fail to send message status: %d", status); + } + } + + return status; +} + +QDF_STATUS csr_roam_issue_reassoc(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + tCsrRoamModifyProfileFields + *pMmodProfileFields, + enum csr_roam_reason reason, uint32_t roamId, + bool fImediate) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSmeCmd *pCommand; + + pCommand = csr_get_command_buffer(mac); + if (!pCommand) { + sme_err("fail to get command buffer"); + status = QDF_STATUS_E_RESOURCES; + } else { + csr_scan_abort_mac_scan(mac, sessionId, INVAL_SCAN_ID); + if (pProfile) { + /* This is likely trying to reassoc to + * different profile + */ + pCommand->u.roamCmd.fReleaseProfile = false; + /* make a copy of the profile */ + status = csr_roam_copy_profile(mac, &pCommand->u. + roamCmd.roamProfile, + pProfile); + pCommand->u.roamCmd.fUpdateCurRoamProfile = true; + } else { + status = csr_roam_copy_connected_profile(mac, + sessionId, + &pCommand->u.roamCmd. + roamProfile); + /* how to update WPA/WPA2 info in roamProfile?? */ + pCommand->u.roamCmd.roamProfile.uapsd_mask = + pMmodProfileFields->uapsd_mask; + } + if (QDF_IS_STATUS_SUCCESS(status)) + pCommand->u.roamCmd.fReleaseProfile = true; + pCommand->command = eSmeCommandRoam; + pCommand->vdev_id = (uint8_t) sessionId; + pCommand->u.roamCmd.roamId = roamId; + pCommand->u.roamCmd.roamReason = reason; + /* We need to free the BssList when the command is done */ + /* For reassoc there is no BSS list, so the bool set to false */ + pCommand->u.roamCmd.hBSSList = CSR_INVALID_SCANRESULT_HANDLE; + pCommand->u.roamCmd.fReleaseBssList = false; + pCommand->u.roamCmd.fReassoc = true; + csr_roam_remove_duplicate_command(mac, sessionId, pCommand, + reason); + status = csr_queue_sme_command(mac, pCommand, fImediate); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("fail to send message status = %d", status); + csr_roam_completion(mac, sessionId, NULL, NULL, + eCSR_ROAM_RESULT_FAILURE, false); + } + } + return status; +} + +QDF_STATUS csr_dequeue_roam_command(struct mac_context *mac, + enum csr_roam_reason reason, + uint8_t session_id) +{ + tListElem *pEntry; + tSmeCmd *pCommand; + + pEntry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + + if (pEntry) { + pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + if ((eSmeCommandRoam == pCommand->command) && + (eCsrPerformPreauth == reason)) { + sme_debug("DQ-Command = %d, Reason = %d", + pCommand->command, + pCommand->u.roamCmd.roamReason); + if (csr_nonscan_active_ll_remove_entry(mac, pEntry, + LL_ACCESS_LOCK)) { + csr_release_command(mac, pCommand); + } + } else if ((eSmeCommandRoam == pCommand->command) && + (eCsrSmeIssuedFTReassoc == reason)) { + sme_debug("DQ-Command = %d, Reason = %d", + pCommand->command, + pCommand->u.roamCmd.roamReason); + if (csr_nonscan_active_ll_remove_entry(mac, pEntry, + LL_ACCESS_LOCK)) { + csr_release_command(mac, pCommand); + } + } else { + sme_err("Command = %d, Reason = %d ", + pCommand->command, + pCommand->u.roamCmd.roamReason); + } + } else { + sme_err("pEntry NULL for eWNI_SME_FT_PRE_AUTH_RSP"); + } + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_FILS_SK +/** + * csr_is_fils_connection() - API to check if FILS connection + * @profile: CSR Roam Profile + * + * Return: true, if fils connection, false otherwise + */ +static bool csr_is_fils_connection(struct csr_roam_profile *profile) +{ + if (!profile->fils_con_info) + return false; + + return profile->fils_con_info->is_fils_connection; +} +#else +static bool csr_is_fils_connection(struct csr_roam_profile *pProfile) +{ + return false; +} +#endif + +/** + * csr_roam_print_candidate_aps() - print all candidate AP in sorted + * score. + * @results: scan result + * + * Return : void + */ +static void csr_roam_print_candidate_aps(tScanResultHandle results) +{ + tListElem *entry; + struct tag_csrscan_result *bss_desc = NULL; + struct scan_result_list *bss_list = NULL; + + if (!results) + return; + bss_list = (struct scan_result_list *)results; + entry = csr_ll_peek_head(&bss_list->List, LL_ACCESS_NOLOCK); + while (entry) { + bss_desc = GET_BASE_ADDR(entry, + struct tag_csrscan_result, Link); + sme_nofl_debug(QDF_MAC_ADDR_FMT " score: %d", + QDF_MAC_ADDR_REF(bss_desc->Result.BssDescriptor.bssId), + bss_desc->bss_score); + + entry = csr_ll_next(&bss_list->List, entry, + LL_ACCESS_NOLOCK); + } +} + +QDF_STATUS csr_roam_connect(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + uint32_t *pRoamId) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tScanResultHandle hBSSList; + struct scan_filter *filter; + uint32_t roamId = 0; + bool fCallCallback = false; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + struct bss_description *first_ap_profile; + enum QDF_OPMODE opmode = QDF_STA_MODE; + uint32_t ch_freq; + + if (!pSession) { + sme_err("session does not exist for given sessionId: %d", + sessionId); + return QDF_STATUS_E_FAILURE; + } + + if (!pProfile) { + sme_err("No profile specified"); + return QDF_STATUS_E_FAILURE; + } + + first_ap_profile = qdf_mem_malloc(sizeof(*first_ap_profile)); + if (!first_ap_profile) + return QDF_STATUS_E_NOMEM; + + /* Initialize the count before proceeding with the Join requests */ + pSession->join_bssid_count = 0; + pSession->discon_in_progress = false; + pSession->is_fils_connection = csr_is_fils_connection(pProfile); + sme_debug("Persona %d authtype %d encryType %d mc_encType %d", + pProfile->csrPersona, pProfile->AuthType.authType[0], + pProfile->EncryptionType.encryptionType[0], + pProfile->mcEncryptionType.encryptionType[0]); + csr_roam_cancel_roaming(mac, sessionId); + csr_scan_abort_mac_scan(mac, sessionId, INVAL_SCAN_ID); + csr_roam_remove_duplicate_command(mac, sessionId, NULL, eCsrHddIssued); + /* Check whether ssid changes */ + if (csr_is_conn_state_connected(mac, sessionId) && + pProfile->SSIDs.numOfSSIDs && + !csr_is_ssid_in_list(&pSession->connectedProfile.SSID, + &pProfile->SSIDs)) + csr_roam_issue_disassociate_cmd(mac, sessionId, + eCSR_DISCONNECT_REASON_UNSPECIFIED, + eSIR_MAC_UNSPEC_FAILURE_REASON); + /* + * If roamSession.connectState is disconnecting that mean + * disconnect was received with scan for ssid in progress + * and dropped. This state will ensure that connect will + * not be issued from scan for ssid completion. Thus + * if this fresh connect also issue scan for ssid the connect + * command will be dropped assuming disconnect is in progress. + * Thus reset connectState here + */ + if (eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTING == + mac->roam.roamSession[sessionId].connectState) + mac->roam.roamSession[sessionId].connectState = + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED; + + filter = qdf_mem_malloc(sizeof(*filter)); + if (!filter) { + status = QDF_STATUS_E_NOMEM; + goto error; + } + + /* Try to connect to any BSS */ + if (!pProfile) { + /* No encryption */ + filter->num_of_enc_type = 1; + filter->enc_type[0] = WLAN_ENCRYPT_TYPE_NONE; + } else { + /* Here is the profile we need to connect to */ + status = csr_roam_get_scan_filter_from_profile(mac, pProfile, + filter, false); + opmode = pProfile->csrPersona; + } + roamId = GET_NEXT_ROAM_ID(&mac->roam); + if (pRoamId) + *pRoamId = roamId; + if (!QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(filter); + goto error; + } + + if (pProfile && CSR_IS_INFRA_AP(pProfile)) { + /* This can be started right away */ + status = csr_roam_issue_connect(mac, sessionId, pProfile, NULL, + eCsrHddIssued, roamId, false, false); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("CSR failed to issue start BSS cmd with status: 0x%08X", + status); + fCallCallback = true; + } else + sme_debug("Connect request to proceed for sap mode"); + + qdf_mem_free(filter); + goto error; + } + status = csr_scan_get_result(mac, filter, &hBSSList); + qdf_mem_free(filter); + csr_roam_print_candidate_aps(hBSSList); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* check if set hw mode needs to be done */ + if ((opmode == QDF_STA_MODE) || + (opmode == QDF_P2P_CLIENT_MODE)) { + bool ok; + + csr_get_bssdescr_from_scan_handle(hBSSList, + first_ap_profile); + status = policy_mgr_is_chan_ok_for_dnbs( + mac->psoc, + first_ap_profile->chan_freq, &ok); + if (QDF_IS_STATUS_ERROR(status)) { + sme_debug("policy_mgr_is_chan_ok_for_dnbs():error:%d", + status); + csr_scan_result_purge(mac, hBSSList); + fCallCallback = true; + goto error; + } + if (!ok) { + sme_debug("freq:%d not ok for DNBS", + first_ap_profile->chan_freq); + csr_scan_result_purge(mac, hBSSList); + fCallCallback = true; + status = QDF_STATUS_E_INVAL; + goto error; + } + + ch_freq = csr_get_channel_for_hw_mode_change + (mac, hBSSList, sessionId); + if (!ch_freq) + ch_freq = first_ap_profile->chan_freq; + + status = policy_mgr_handle_conc_multiport( + mac->psoc, sessionId, ch_freq, + POLICY_MGR_UPDATE_REASON_NORMAL_STA); + if ((QDF_IS_STATUS_SUCCESS(status)) && + (!csr_wait_for_connection_update(mac, true))) { + sme_debug("conn update error"); + csr_scan_result_purge(mac, hBSSList); + fCallCallback = true; + status = QDF_STATUS_E_TIMEOUT; + goto error; + } else if (status == QDF_STATUS_E_FAILURE) { + sme_debug("conn update error"); + csr_scan_result_purge(mac, hBSSList); + fCallCallback = true; + goto error; + } + } + + status = csr_roam_issue_connect(mac, sessionId, pProfile, + hBSSList, eCsrHddIssued, roamId, false, false); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("CSR failed to issue connect cmd with status: 0x%08X", + status); + fCallCallback = true; + } + } else if (pProfile) { + /* Check whether it is for start ibss */ + if (CSR_IS_START_IBSS(pProfile) || + CSR_IS_NDI(pProfile)) { + status = csr_roam_issue_connect(mac, sessionId, + pProfile, NULL, eCsrHddIssued, + roamId, false, false); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed with status = 0x%08X", + status); + fCallCallback = true; + } + } else if (status == QDF_STATUS_E_EXISTS && + pProfile->BSSIDs.numOfBSSIDs) { + sme_debug("Scan entries removed either due to rssi reject or assoc disallowed"); + fCallCallback = true; + } else { + /* scan for this SSID */ + status = csr_scan_for_ssid(mac, sessionId, pProfile, + roamId, true); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("CSR failed to issue SSID scan cmd with status: 0x%08X", + status); + fCallCallback = true; + } + } + } else { + fCallCallback = true; + } + +error: + /* tell the caller if we fail to trigger a join request */ + if (fCallCallback) { + csr_roam_call_callback(mac, sessionId, NULL, roamId, + eCSR_ROAM_FAILED, eCSR_ROAM_RESULT_FAILURE); + } + qdf_mem_free(first_ap_profile); + + return status; +} + +/** + * csr_roam_reassoc() - process reassoc command + * @mac_ctx: mac global context + * @session_id: session id + * @profile: roam profile + * @mod_fields: AC info being modified in reassoc + * @roam_id: roam id to be populated + * + * Return: status of operation + */ +QDF_STATUS +csr_roam_reassoc(struct mac_context *mac_ctx, uint32_t session_id, + struct csr_roam_profile *profile, + tCsrRoamModifyProfileFields mod_fields, + uint32_t *roam_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool fCallCallback = true; + uint32_t roamId = 0; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!profile) { + sme_err("No profile specified"); + return QDF_STATUS_E_FAILURE; + } + sme_debug( + "called BSSType = %s (%d) authtype = %d encryType = %d", + sme_bss_type_to_string(profile->BSSType), + profile->BSSType, profile->AuthType.authType[0], + profile->EncryptionType.encryptionType[0]); + csr_roam_cancel_roaming(mac_ctx, session_id); + csr_scan_abort_mac_scan(mac_ctx, session_id, INVAL_SCAN_ID); + csr_roam_remove_duplicate_command(mac_ctx, session_id, NULL, + eCsrHddIssuedReassocToSameAP); + if (csr_is_conn_state_connected(mac_ctx, session_id)) { + if (profile) { + if (profile->SSIDs.numOfSSIDs && + csr_is_ssid_in_list(&session->connectedProfile.SSID, + &profile->SSIDs)) { + fCallCallback = false; + } else { + /* + * Connected SSID did not match with what is + * asked in profile + */ + sme_debug("SSID mismatch"); + } + } else if (qdf_mem_cmp(&mod_fields, + &session->connectedProfile.modifyProfileFields, + sizeof(tCsrRoamModifyProfileFields))) { + fCallCallback = false; + } else { + sme_debug( + /* + * Either the profile is NULL or none of the + * fields in tCsrRoamModifyProfileFields got + * modified + */ + "Profile NULL or nothing to modify"); + } + } else { + sme_debug("Not connected! No need to reassoc"); + } + if (!fCallCallback) { + roamId = GET_NEXT_ROAM_ID(&mac_ctx->roam); + if (roam_id) + *roam_id = roamId; + status = csr_roam_issue_reassoc(mac_ctx, session_id, profile, + &mod_fields, eCsrHddIssuedReassocToSameAP, + roamId, false); + } else { + status = csr_roam_call_callback(mac_ctx, session_id, NULL, + roamId, eCSR_ROAM_FAILED, + eCSR_ROAM_RESULT_FAILURE); + } + return status; +} + +QDF_STATUS csr_roam_process_disassoc_deauth(struct mac_context *mac, + tSmeCmd *pCommand, + bool fDisassoc, bool fMICFailure) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool fComplete = false; + enum csr_roam_substate NewSubstate; + uint32_t sessionId = pCommand->vdev_id; + + if (CSR_IS_WAIT_FOR_KEY(mac, sessionId)) { + sme_debug("Stop Wait for key timer and change substate to eCSR_ROAM_SUBSTATE_NONE"); + csr_roam_stop_wait_for_key_timer(mac); + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_NONE, + sessionId); + } + /* change state to 'Roaming'... */ + csr_roam_state_change(mac, eCSR_ROAMING_STATE_JOINING, sessionId); + mlme_set_discon_reason_n_from_ap(mac->psoc, pCommand->vdev_id, false, + pCommand->u.roamCmd.disconnect_reason); + if (csr_is_conn_state_ibss(mac, sessionId)) { + /* If we are in an IBSS, then stop the IBSS... */ + status = + csr_roam_issue_stop_bss(mac, sessionId, + eCSR_ROAM_SUBSTATE_STOP_BSS_REQ); + fComplete = (!QDF_IS_STATUS_SUCCESS(status)); + } else if (csr_is_conn_state_infra(mac, sessionId)) { + /* + * in Infrastructure, we need to disassociate from the + * Infrastructure network... + */ + NewSubstate = eCSR_ROAM_SUBSTATE_DISASSOC_FORCED; + if (eCsrSmeIssuedDisassocForHandoff == + pCommand->u.roamCmd.roamReason) { + NewSubstate = eCSR_ROAM_SUBSTATE_DISASSOC_HANDOFF; + } else + if ((eCsrForcedDisassoc == pCommand->u.roamCmd.roamReason) + && (eSIR_MAC_DISASSOC_LEAVING_BSS_REASON == + pCommand->u.roamCmd.reason)) { + NewSubstate = eCSR_ROAM_SUBSTATE_DISASSOC_STA_HAS_LEFT; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "set to substate eCSR_ROAM_SUBSTATE_DISASSOC_STA_HAS_LEFT"); + } + if (eCsrSmeIssuedDisassocForHandoff != + pCommand->u.roamCmd.roamReason) { + /* + * If we are in neighbor preauth done state then + * on receiving disassoc or deauth we dont roam + * instead we just disassoc from current ap and + * then go to disconnected state. + * This happens for ESE and 11r FT connections ONLY. + */ + if (csr_roam_is11r_assoc(mac, sessionId) && + (csr_neighbor_roam_state_preauth_done(mac, + sessionId))) + csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + mac, sessionId); +#ifdef FEATURE_WLAN_ESE + if (csr_roam_is_ese_assoc(mac, sessionId) && + (csr_neighbor_roam_state_preauth_done(mac, + sessionId))) + csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + mac, sessionId); +#endif + if (csr_roam_is_fast_roam_enabled(mac, sessionId) && + (csr_neighbor_roam_state_preauth_done(mac, + sessionId))) + csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + mac, sessionId); + } + if (fDisassoc) + status = csr_roam_issue_disassociate(mac, sessionId, + NewSubstate, + fMICFailure); + else + status = csr_roam_issue_deauth(mac, sessionId, + eCSR_ROAM_SUBSTATE_DEAUTH_REQ); + fComplete = (!QDF_IS_STATUS_SUCCESS(status)); + } else { + /* we got a dis-assoc request while not connected to any peer */ + /* just complete the command */ + fComplete = true; + status = QDF_STATUS_E_FAILURE; + } + if (fComplete) + csr_roam_complete(mac, eCsrNothingToJoin, NULL, sessionId); + + if (QDF_IS_STATUS_SUCCESS(status)) { + if (csr_is_conn_state_infra(mac, sessionId)) { + /* Set the state to disconnect here */ + mac->roam.roamSession[sessionId].connectState = + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED; + } + } else + sme_warn(" failed with status %d", status); + return status; +} + +static void csr_abort_connect_request_timers( + struct mac_context *mac, uint32_t vdev_id) +{ + struct scheduler_msg msg; + QDF_STATUS status; + enum QDF_OPMODE op_mode; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_pdev(mac->pdev, + vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Vdev ref error"); + return; + } + op_mode = wlan_vdev_mlme_get_opmode(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + if (op_mode != QDF_STA_MODE && + op_mode != QDF_P2P_CLIENT_MODE) + return; + qdf_mem_zero(&msg, sizeof(msg)); + msg.bodyval = vdev_id; + msg.type = eWNI_SME_ABORT_CONN_TIMER; + status = scheduler_post_message(QDF_MODULE_ID_MLME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) + sme_debug("msg eWNI_SME_ABORT_CONN_TIMER post fail"); +} + +QDF_STATUS csr_roam_issue_disassociate_cmd(struct mac_context *mac, + uint32_t sessionId, + eCsrRoamDisconnectReason reason, + tSirMacReasonCodes mac_reason) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSmeCmd *pCommand; + + do { + pCommand = csr_get_command_buffer(mac); + if (!pCommand) { + sme_err(" fail to get command buffer"); + status = QDF_STATUS_E_RESOURCES; + break; + } + /* Change the substate in case it is wait-for-key */ + if (CSR_IS_WAIT_FOR_KEY(mac, sessionId)) { + csr_roam_stop_wait_for_key_timer(mac); + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_NONE, + sessionId); + } + csr_abort_connect_request_timers(mac, sessionId); + + pCommand->command = eSmeCommandRoam; + pCommand->vdev_id = (uint8_t) sessionId; + sme_debug("Disassociate reason: %d, vdev_id: %d", + reason, sessionId); + switch (reason) { + case eCSR_DISCONNECT_REASON_MIC_ERROR: + pCommand->u.roamCmd.roamReason = + eCsrForcedDisassocMICFailure; + break; + case eCSR_DISCONNECT_REASON_DEAUTH: + pCommand->u.roamCmd.roamReason = eCsrForcedDeauth; + break; + case eCSR_DISCONNECT_REASON_HANDOFF: + pCommand->u.roamCmd.roamReason = + eCsrSmeIssuedDisassocForHandoff; + break; + case eCSR_DISCONNECT_REASON_UNSPECIFIED: + case eCSR_DISCONNECT_REASON_DISASSOC: + pCommand->u.roamCmd.roamReason = eCsrForcedDisassoc; + break; + case eCSR_DISCONNECT_REASON_ROAM_HO_FAIL: + pCommand->u.roamCmd.roamReason = eCsrForcedDisassoc; + break; + case eCSR_DISCONNECT_REASON_IBSS_LEAVE: + pCommand->u.roamCmd.roamReason = eCsrForcedIbssLeave; + break; + case eCSR_DISCONNECT_REASON_STA_HAS_LEFT: + pCommand->u.roamCmd.roamReason = eCsrForcedDisassoc; + pCommand->u.roamCmd.reason = + eSIR_MAC_DISASSOC_LEAVING_BSS_REASON; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "SME convert to internal reason code eCsrStaHasLeft"); + break; + case eCSR_DISCONNECT_REASON_NDI_DELETE: + pCommand->u.roamCmd.roamReason = eCsrStopBss; + pCommand->u.roamCmd.roamProfile.BSSType = + eCSR_BSS_TYPE_NDI; + default: + break; + } + pCommand->u.roamCmd.disconnect_reason = mac_reason; + status = csr_queue_sme_command(mac, pCommand, true); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("fail to send message status: %d", status); + } while (0); + return status; +} + +QDF_STATUS csr_roam_issue_stop_bss_cmd(struct mac_context *mac, uint32_t sessionId, + bool fHighPriority) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSmeCmd *pCommand; + + pCommand = csr_get_command_buffer(mac); + if (pCommand) { + /* Change the substate in case it is wait-for-key */ + if (CSR_IS_WAIT_FOR_KEY(mac, sessionId)) { + csr_roam_stop_wait_for_key_timer(mac); + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_NONE, + sessionId); + } + pCommand->command = eSmeCommandRoam; + pCommand->vdev_id = (uint8_t) sessionId; + pCommand->u.roamCmd.roamReason = eCsrStopBss; + status = csr_queue_sme_command(mac, pCommand, fHighPriority); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("fail to send message status: %d", status); + } else { + sme_err("fail to get command buffer"); + status = QDF_STATUS_E_RESOURCES; + } + return status; +} + +QDF_STATUS csr_roam_disconnect_internal(struct mac_context *mac, uint32_t sessionId, + eCsrRoamDisconnectReason reason, + tSirMacReasonCodes mac_reason) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session: %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + /* Not to call cancel roaming here */ + /* Only issue disconnect when necessary */ + if (csr_is_conn_state_connected(mac, sessionId) + || csr_is_bss_type_ibss(pSession->connectedProfile.BSSType) + || csr_is_roam_command_waiting_for_session(mac, sessionId) + || CSR_IS_CONN_NDI(&pSession->connectedProfile)) { + status = csr_roam_issue_disassociate_cmd(mac, sessionId, + reason, mac_reason); + } else if (pSession->scan_info.profile) { + mac->roam.roamSession[sessionId].connectState = + eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTING; + csr_scan_abort_mac_scan(mac, sessionId, INVAL_SCAN_ID); + status = QDF_STATUS_CMD_NOT_QUEUED; + sme_debug("Disconnect cmd not queued, Roam command is not present return with status: %d", + status); + } + + return status; +} + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +static void +csr_disable_roaming_offload(struct mac_context *mac_ctx, uint32_t vdev_id) +{ + sme_stop_roaming(MAC_HANDLE(mac_ctx), vdev_id, + REASON_DRIVER_DISABLED, + RSO_INVALID_REQUESTOR); +} +#else +static inline void +csr_disable_roaming_offload(struct mac_context *mac_ctx, uint32_t session_id) +{} +#endif + +QDF_STATUS csr_roam_disconnect(struct mac_context *mac_ctx, uint32_t session_id, + eCsrRoamDisconnectReason reason, + tSirMacReasonCodes mac_reason) +{ + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("session: %d not found ", session_id); + return QDF_STATUS_E_FAILURE; + } + + session->discon_in_progress = true; + + csr_roam_cancel_roaming(mac_ctx, session_id); + csr_disable_roaming_offload(mac_ctx, session_id); + + csr_roam_remove_duplicate_command(mac_ctx, session_id, NULL, + eCsrForcedDisassoc); + + return csr_roam_disconnect_internal(mac_ctx, session_id, reason, + mac_reason); +} + +QDF_STATUS +csr_roam_save_connected_information(struct mac_context *mac, + uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tDot11fBeaconIEs *pIesTemp = pIes; + uint8_t index; + struct csr_roam_session *pSession = NULL; + tCsrRoamConnectedProfile *pConnectProfile = NULL; + + pSession = CSR_GET_SESSION(mac, sessionId); + if (!pSession) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "session: %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + pConnectProfile = &pSession->connectedProfile; + if (pConnectProfile->pAddIEAssoc) { + qdf_mem_free(pConnectProfile->pAddIEAssoc); + pConnectProfile->pAddIEAssoc = NULL; + } + /* + * In case of LFR2.0, the connected profile is copied into a temporary + * profile and cleared and then is copied back. This is not needed for + * LFR3.0, since the profile is not cleared. + */ + if (!pSession->roam_synch_in_progress) { + qdf_mem_zero(&pSession->connectedProfile, + sizeof(tCsrRoamConnectedProfile)); + pConnectProfile->AuthType = pProfile->negotiatedAuthType; + pConnectProfile->AuthInfo = pProfile->AuthType; + pConnectProfile->akm_list = pProfile->akm_list; + pConnectProfile->EncryptionType = + pProfile->negotiatedUCEncryptionType; + pConnectProfile->EncryptionInfo = pProfile->EncryptionType; + pConnectProfile->mcEncryptionType = + pProfile->negotiatedMCEncryptionType; + pConnectProfile->mcEncryptionInfo = pProfile->mcEncryptionType; + pConnectProfile->mgmt_encryption_type = + pProfile->mgmt_encryption_type; + pConnectProfile->BSSType = pProfile->BSSType; + pConnectProfile->modifyProfileFields.uapsd_mask = + pProfile->uapsd_mask; + qdf_mem_copy(&pConnectProfile->Keys, &pProfile->Keys, + sizeof(tCsrKeys)); + if (pProfile->nAddIEAssocLength) { + pConnectProfile->pAddIEAssoc = + qdf_mem_malloc(pProfile->nAddIEAssocLength); + if (!pConnectProfile->pAddIEAssoc) + return QDF_STATUS_E_FAILURE; + + pConnectProfile->nAddIEAssocLength = + pProfile->nAddIEAssocLength; + qdf_mem_copy(pConnectProfile->pAddIEAssoc, + pProfile->pAddIEAssoc, + pProfile->nAddIEAssocLength); + } +#ifdef WLAN_FEATURE_11W + pConnectProfile->MFPEnabled = pProfile->MFPEnabled; + pConnectProfile->MFPRequired = pProfile->MFPRequired; + pConnectProfile->MFPCapable = pProfile->MFPCapable; +#endif + } + /* Save bssid */ + pConnectProfile->op_freq = pSirBssDesc->chan_freq; + pConnectProfile->beaconInterval = pSirBssDesc->beaconInterval; + if (!pConnectProfile->beaconInterval) + sme_err("ERROR: Beacon interval is ZERO"); + csr_get_bss_id_bss_desc(pSirBssDesc, &pConnectProfile->bssid); + if (pSirBssDesc->mdiePresent) { + pConnectProfile->mdid.mdie_present = 1; + pConnectProfile->mdid.mobility_domain = + (pSirBssDesc->mdie[1] << 8) | (pSirBssDesc->mdie[0]); + } + if (!pIesTemp) + status = csr_get_parsed_bss_description_ies(mac, pSirBssDesc, + &pIesTemp); +#ifdef FEATURE_WLAN_ESE + if ((csr_is_profile_ese(pProfile) || + (QDF_IS_STATUS_SUCCESS(status) && + (pIesTemp->ESEVersion.present) && + (pProfile->negotiatedAuthType == eCSR_AUTH_TYPE_OPEN_SYSTEM))) && + (mac->mlme_cfg->lfr.ese_enabled)) { + pConnectProfile->isESEAssoc = 1; + } +#endif + /* save ssid */ + if (QDF_IS_STATUS_SUCCESS(status)) { + if (pIesTemp->SSID.present && + !csr_is_nullssid(pIesTemp->SSID.ssid, + pIesTemp->SSID.num_ssid)) { + pConnectProfile->SSID.length = pIesTemp->SSID.num_ssid; + qdf_mem_copy(pConnectProfile->SSID.ssId, + pIesTemp->SSID.ssid, + pIesTemp->SSID.num_ssid); + } else if (pProfile->SSIDs.numOfSSIDs) { + pConnectProfile->SSID.length = + pProfile->SSIDs.SSIDList[0].SSID.length; + qdf_mem_copy(pConnectProfile->SSID.ssId, + pProfile->SSIDs.SSIDList[0].SSID.ssId, + pConnectProfile->SSID.length); + } + /* Save the bss desc */ + status = csr_roam_save_connected_bss_desc(mac, sessionId, + pSirBssDesc); + + if (CSR_IS_QOS_BSS(pIesTemp) || pIesTemp->HTCaps.present) + /* Some HT AP's dont send WMM IE so in that case we + * assume all HT Ap's are Qos Enabled AP's + */ + pConnectProfile->qap = true; + else + pConnectProfile->qap = false; + + if (pIesTemp->ExtCap.present) { + struct s_ext_cap *p_ext_cap = (struct s_ext_cap *) + pIesTemp->ExtCap.bytes; + pConnectProfile->proxy_arp_service = + p_ext_cap->proxy_arp_service; + } + + qdf_mem_zero(pConnectProfile->country_code, + WNI_CFG_COUNTRY_CODE_LEN); + if (pIesTemp->Country.present) { + qdf_mem_copy(pConnectProfile->country_code, + pIesTemp->Country.country, + WNI_CFG_COUNTRY_CODE_LEN); + sme_debug("Storing country in connected info, %c%c 0x%x", + pConnectProfile->country_code[0], + pConnectProfile->country_code[1], + pConnectProfile->country_code[2]); + } + + if (!pIes) + /* Free memory if it allocated locally */ + qdf_mem_free(pIesTemp); + } + /* Save Qos connection */ + pConnectProfile->qosConnection = + mac->roam.roamSession[sessionId].fWMMConnection; + + if (!QDF_IS_STATUS_SUCCESS(status)) + csr_free_connect_bss_desc(mac, sessionId); + + for (index = 0; index < pProfile->SSIDs.numOfSSIDs; index++) { + if ((pProfile->SSIDs.SSIDList[index].SSID.length == + pConnectProfile->SSID.length) + && (!qdf_mem_cmp(pProfile->SSIDs.SSIDList[index].SSID. + ssId, pConnectProfile->SSID.ssId, + pConnectProfile->SSID.length))) { + pConnectProfile->handoffPermitted = pProfile->SSIDs. + SSIDList[index].handoffPermitted; + break; + } + pConnectProfile->handoffPermitted = false; + } + + return status; +} + + +bool is_disconnect_pending(struct mac_context *pmac, uint8_t vdev_id) +{ + tListElem *entry = NULL; + tListElem *next_entry = NULL; + tSmeCmd *command = NULL; + bool disconnect_cmd_exist = false; + + entry = csr_nonscan_pending_ll_peek_head(pmac, LL_ACCESS_NOLOCK); + while (entry) { + next_entry = csr_nonscan_pending_ll_next(pmac, + entry, LL_ACCESS_NOLOCK); + + command = GET_BASE_ADDR(entry, tSmeCmd, Link); + if (command && CSR_IS_DISCONNECT_COMMAND(command) && + command->vdev_id == vdev_id){ + disconnect_cmd_exist = true; + break; + } + entry = next_entry; + } + + return disconnect_cmd_exist; +} + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +static void +csr_clear_other_bss_sae_single_pmk_entry(struct mac_context *mac, + struct bss_description *bss_desc, + uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + + if (!bss_desc->is_single_pmk) + return; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("vdev is NULL"); + return; + } + + wlan_crypto_selective_clear_sae_single_pmk_entries(vdev, + (struct qdf_mac_addr *)bss_desc->bssId); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); +} + +static void +csr_delete_current_bss_sae_single_pmk_entry(struct mac_context *mac, + struct bss_description *bss_desc, + uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct wlan_crypto_pmksa pmksa; + + if (!bss_desc->is_single_pmk) + return; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("vdev is NULL"); + return; + } + + qdf_copy_macaddr(&pmksa.bssid, + (struct qdf_mac_addr *)bss_desc->bssId); + wlan_crypto_set_del_pmksa(vdev, &pmksa, false); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); +} +#else +static inline void +csr_clear_other_bss_sae_single_pmk_entry(struct mac_context *mac, + struct bss_description *bss_desc, + uint8_t vdev_id) +{} + +static inline void +csr_delete_current_bss_sae_single_pmk_entry(struct mac_context *mac, + struct bss_description *bss_desc, + uint8_t vdev_id) +{} +#endif + +static void csr_roam_join_rsp_processor(struct mac_context *mac, + struct join_rsp *pSmeJoinRsp) +{ + tListElem *pEntry = NULL; + tSmeCmd *pCommand = NULL; + mac_handle_t mac_handle = MAC_HANDLE(mac); + struct csr_roam_session *session_ptr; + struct csr_roam_profile *profile = NULL; + struct csr_roam_connectedinfo *prev_connect_info; + struct wlan_crypto_pmksa *pmksa; + uint32_t len = 0, roamId = 0, reason_code = 0; + bool is_dis_pending; + bool use_same_bss = false; + uint8_t max_retry_count = 1; + bool retry_same_bss = false; + bool attempt_next_bss = true; + enum csr_akm_type auth_type = eCSR_AUTH_TYPE_NONE; + + if (!pSmeJoinRsp) { + sme_err("Sme Join Response is NULL"); + return; + } + + session_ptr = CSR_GET_SESSION(mac, pSmeJoinRsp->vdev_id); + if (!session_ptr) { + sme_err("session %d not found", pSmeJoinRsp->vdev_id); + return; + } + + prev_connect_info = &session_ptr->prev_assoc_ap_info; + /* The head of the active list is the request we sent */ + pEntry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (pEntry) + pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + + if (pSmeJoinRsp->is_fils_connection) + sme_debug("Fils connection"); + /* Copy Sequence Number last used for FILS assoc failure case */ + if (session_ptr->is_fils_connection) + session_ptr->fils_seq_num = pSmeJoinRsp->fils_seq_num; + + len = pSmeJoinRsp->assocReqLength + + pSmeJoinRsp->assocRspLength + pSmeJoinRsp->beaconLength; + if (prev_connect_info->pbFrames) + csr_roam_free_connected_info(mac, prev_connect_info); + + prev_connect_info->pbFrames = qdf_mem_malloc(len); + if (prev_connect_info->pbFrames) { + qdf_mem_copy(prev_connect_info->pbFrames, pSmeJoinRsp->frames, + len); + prev_connect_info->nAssocReqLength = + pSmeJoinRsp->assocReqLength; + prev_connect_info->nAssocRspLength = + pSmeJoinRsp->assocRspLength; + prev_connect_info->nBeaconLength = pSmeJoinRsp->beaconLength; + } + + if (eSIR_SME_SUCCESS == pSmeJoinRsp->status_code) { + if (pCommand && + eCsrSmeIssuedAssocToSimilarAP == + pCommand->u.roamCmd.roamReason) { +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_qos_csr_event_ind(mac, pSmeJoinRsp->vdev_id, + SME_QOS_CSR_HANDOFF_COMPLETE, NULL); +#endif + } + + if (pSmeJoinRsp->nss < session_ptr->nss) { + session_ptr->nss = pSmeJoinRsp->nss; + session_ptr->vdev_nss = pSmeJoinRsp->nss; + } + + session_ptr->supported_nss_1x1 = pSmeJoinRsp->supported_nss_1x1; + + /* + * The join bssid count can be reset as soon as + * we are done with the join requests and returning + * the response to upper layers + */ + session_ptr->join_bssid_count = 0; + + /* + * On successful connection to sae single pmk AP, + * clear all the single pmk AP. + */ + if (pCommand && pCommand->u.roamCmd.pLastRoamBss) + csr_clear_other_bss_sae_single_pmk_entry(mac, + pCommand->u.roamCmd.pLastRoamBss, + pSmeJoinRsp->vdev_id); + + csr_roam_complete(mac, eCsrJoinSuccess, (void *)pSmeJoinRsp, + pSmeJoinRsp->vdev_id); + + return; + } + + /* The head of the active list is the request we sent + * Try to get back the same profile and roam again + */ + if (pCommand) { + roamId = pCommand->u.roamCmd.roamId; + profile = &pCommand->u.roamCmd.roamProfile; + auth_type = profile->AuthType.authType[0]; + } + + reason_code = pSmeJoinRsp->protStatusCode; + session_ptr->joinFailStatusCode.reasonCode = reason_code; + session_ptr->joinFailStatusCode.status_code = pSmeJoinRsp->status_code; + + sme_warn("SmeJoinReq failed with status_code= 0x%08X [%d] reason:%d", + pSmeJoinRsp->status_code, pSmeJoinRsp->status_code, + reason_code); + + is_dis_pending = is_disconnect_pending(mac, session_ptr->sessionId); + /* + * if userspace has issued disconnection or we have reached mac tries, + * driver should not continue for next connection. + */ + if (is_dis_pending || + session_ptr->join_bssid_count >= CSR_MAX_BSSID_COUNT) + attempt_next_bss = false; + /* + * Delete the PMKID of the BSSID for which the assoc reject is + * received from the AP due to invalid PMKID reason. + * This will avoid the driver trying to connect to same AP with + * the same stale PMKID if multiple connection attempts to different + * bss fail and supplicant issues connect request back to the same + * AP. + */ + if (reason_code == eSIR_MAC_INVALID_PMKID) { + pmksa = qdf_mem_malloc(sizeof(*pmksa)); + if (!pmksa) + return; + + sme_warn("Assoc reject from BSSID:"QDF_MAC_ADDR_FMT" due to invalid PMKID", + QDF_MAC_ADDR_REF(session_ptr->joinFailStatusCode.bssId)); + qdf_mem_copy(pmksa->bssid.bytes, + &session_ptr->joinFailStatusCode.bssId, + sizeof(tSirMacAddr)); + sme_roam_del_pmkid_from_cache(mac_handle, session_ptr->vdev_id, + pmksa, false); + qdf_mem_free(pmksa); + retry_same_bss = true; + } + + if (pSmeJoinRsp->messageType == eWNI_SME_JOIN_RSP && + pSmeJoinRsp->status_code == eSIR_SME_ASSOC_TIMEOUT_RESULT_CODE && + (mlme_get_reconn_after_assoc_timeout_flag(mac->psoc, + pSmeJoinRsp->vdev_id) || + (auth_type == eCSR_AUTH_TYPE_SAE || + auth_type == eCSR_AUTH_TYPE_FT_SAE))) { + retry_same_bss = true; + if (auth_type == eCSR_AUTH_TYPE_SAE || + auth_type == eCSR_AUTH_TYPE_FT_SAE) + wlan_mlme_get_sae_assoc_retry_count(mac->psoc, + &max_retry_count); + } + + if (pSmeJoinRsp->messageType == eWNI_SME_JOIN_RSP && + pSmeJoinRsp->status_code == eSIR_SME_JOIN_TIMEOUT_RESULT_CODE && + pCommand && pCommand->u.roamCmd.hBSSList) { + struct scan_result_list *bss_list = + (struct scan_result_list *)pCommand->u.roamCmd.hBSSList; + + if (csr_ll_count(&bss_list->List) == 1) { + retry_same_bss = true; + sme_err("retry_same_bss is set"); + wlan_mlme_get_sae_assoc_retry_count(mac->psoc, + &max_retry_count); + } + } + + if (attempt_next_bss && retry_same_bss && + pCommand && pCommand->u.roamCmd.pRoamBssEntry) { + struct tag_csrscan_result *scan_result; + + scan_result = + GET_BASE_ADDR(pCommand->u.roamCmd.pRoamBssEntry, + struct tag_csrscan_result, Link); + /* Retry with same BSSID without PMKID */ + if (scan_result->retry_count < max_retry_count) { + sme_info("Retry once with same BSSID, status %d reason %d auth_type %d retry count %d max count %d", + pSmeJoinRsp->status_code, reason_code, + auth_type, scan_result->retry_count, + max_retry_count); + scan_result->retry_count++; + use_same_bss = true; + } + } + + /* + * If connection fails with Single PMK bssid, clear this pmk + * entry, Flush in case if we are not trying again with same AP + */ + if (!use_same_bss && pCommand && pCommand->u.roamCmd.pLastRoamBss) + csr_delete_current_bss_sae_single_pmk_entry( + mac, pCommand->u.roamCmd.pLastRoamBss, + pSmeJoinRsp->vdev_id); + + /* If Join fails while Handoff is in progress, indicate + * disassociated event to supplicant to reconnect + */ + if (csr_roam_is_handoff_in_progress(mac, pSmeJoinRsp->vdev_id)) { + csr_roam_call_callback(mac, pSmeJoinRsp->vdev_id, NULL, + roamId, eCSR_ROAM_DISASSOCIATED, + eCSR_ROAM_RESULT_FORCED); + /* Should indicate neighbor roam algorithm about the + * connect failure here + */ + csr_neighbor_roam_indicate_connect(mac, pSmeJoinRsp->vdev_id, + QDF_STATUS_E_FAILURE); + } + + if (pCommand && attempt_next_bss) { + csr_roam(mac, pCommand, use_same_bss); + return; + } + + /* + * When the upper layers issue a connect command, there is a roam + * command with reason eCsrHddIssued that gets enqueued and an + * associated timer for the SME command timeout is started which is + * currently 120 seconds. This command would be dequeued only upon + * successful connections. In case of join failures, if there are too + * many BSS in the cache, and if we fail Join requests with all of them, + * there is a chance of timing out the above timer. + */ + if (session_ptr->join_bssid_count >= CSR_MAX_BSSID_COUNT) + sme_err("Excessive Join Req Failures"); + + if (is_dis_pending) + sme_err("disconnect is pending, complete roam"); + + if (session_ptr->bRefAssocStartCnt) + session_ptr->bRefAssocStartCnt--; + + session_ptr->join_bssid_count = 0; + csr_roam_call_callback(mac, session_ptr->sessionId, NULL, roamId, + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_RESULT_NOT_ASSOCIATED); + csr_roam_complete(mac, eCsrNothingToJoin, NULL, pSmeJoinRsp->vdev_id); +} + +static QDF_STATUS csr_roam_issue_join(struct mac_context *mac, uint32_t sessionId, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, + struct csr_roam_profile *pProfile, + uint32_t roamId) +{ + QDF_STATUS status; + + /* Set the roaming substate to 'join attempt'... */ + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_JOIN_REQ, sessionId); + /* attempt to Join this BSS... */ + status = csr_send_join_req_msg(mac, sessionId, pSirBssDesc, pProfile, + pIes, eWNI_SME_JOIN_REQ); + return status; +} + +static void +csr_roam_reissue_roam_command(struct mac_context *mac, uint8_t session_id) +{ + tListElem *pEntry; + tSmeCmd *pCommand; + struct csr_roam_info *roam_info; + uint32_t sessionId; + struct csr_roam_session *pSession; + + pEntry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (!pEntry) { + sme_err("Disassoc rsp can't continue, no active CMD"); + return; + } + pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + if (eSmeCommandRoam != pCommand->command) { + sme_err("Active cmd, is not a roaming CMD"); + return; + } + sessionId = pCommand->vdev_id; + pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return; + } + + if (!pCommand->u.roamCmd.fStopWds) { + if (pSession->bRefAssocStartCnt > 0) { + /* + * bRefAssocStartCnt was incremented in + * csr_roam_join_next_bss when the roam command issued + * previously. As part of reissuing the roam command + * again csr_roam_join_next_bss is going increment + * RefAssocStartCnt. So make sure to decrement the + * bRefAssocStartCnt + */ + pSession->bRefAssocStartCnt--; + } + if (eCsrStopRoaming == csr_roam_join_next_bss(mac, pCommand, + true)) { + sme_warn("Failed to reissue join command"); + csr_roam_complete(mac, eCsrNothingToJoin, NULL, + session_id); + } + return; + } + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + roam_info->bss_desc = pCommand->u.roamCmd.pLastRoamBss; + roam_info->status_code = pSession->joinFailStatusCode.status_code; + roam_info->reasonCode = pSession->joinFailStatusCode.reasonCode; + pSession->connectState = eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTED; + csr_roam_call_callback(mac, sessionId, roam_info, + pCommand->u.roamCmd.roamId, + eCSR_ROAM_INFRA_IND, + eCSR_ROAM_RESULT_INFRA_DISASSOCIATED); + + if (!QDF_IS_STATUS_SUCCESS(csr_roam_issue_stop_bss(mac, sessionId, + eCSR_ROAM_SUBSTATE_STOP_BSS_REQ))) { + sme_err("Failed to reissue stop_bss command for WDS"); + csr_roam_complete(mac, eCsrNothingToJoin, NULL, session_id); + } + qdf_mem_free(roam_info); +} + +bool csr_is_roam_command_waiting_for_session(struct mac_context *mac, + uint32_t vdev_id) +{ + bool fRet = false; + tListElem *pEntry; + tSmeCmd *pCommand = NULL; + + pEntry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_NOLOCK); + if (pEntry) { + pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + if ((eSmeCommandRoam == pCommand->command) + && (vdev_id == pCommand->vdev_id)) { + fRet = true; + } + } + if (false == fRet) { + pEntry = csr_nonscan_pending_ll_peek_head(mac, + LL_ACCESS_NOLOCK); + while (pEntry) { + pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + if ((eSmeCommandRoam == pCommand->command) + && (vdev_id == pCommand->vdev_id)) { + fRet = true; + break; + } + pEntry = csr_nonscan_pending_ll_next(mac, pEntry, + LL_ACCESS_NOLOCK); + } + } + + return fRet; +} + +static void +csr_roaming_state_config_cnf_processor(struct mac_context *mac_ctx, + tSmeCmd *cmd, uint8_t vdev_id) +{ + struct tag_csrscan_result *scan_result = NULL; + struct bss_description *bss_desc = NULL; + uint32_t session_id; + struct csr_roam_session *session; + tDot11fBeaconIEs *local_ies = NULL; + bool is_ies_malloced = false; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!cmd) { + sme_err("given sme cmd is null"); + return; + } + session_id = cmd->vdev_id; + session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("session %d not found", session_id); + return; + } + + if (CSR_IS_ROAMING(session) && session->fCancelRoaming) { + /* the roaming is cancelled. Simply complete the command */ + sme_warn("Roam command canceled"); + csr_roam_complete(mac_ctx, eCsrNothingToJoin, NULL, vdev_id); + return; + } + + /* + * Successfully set the configuration parameters for the new Bss. + * Attempt to join the roaming Bss + */ + if (cmd->u.roamCmd.pRoamBssEntry) { + scan_result = GET_BASE_ADDR(cmd->u.roamCmd.pRoamBssEntry, + struct tag_csrscan_result, + Link); + bss_desc = &scan_result->Result.BssDescriptor; + } + if (csr_is_bss_type_ibss(cmd->u.roamCmd.roamProfile.BSSType) + || CSR_IS_INFRA_AP(&cmd->u.roamCmd.roamProfile) + || CSR_IS_NDI(&cmd->u.roamCmd.roamProfile)) { + if (!QDF_IS_STATUS_SUCCESS(csr_roam_issue_start_bss(mac_ctx, + session_id, &session->bssParams, + &cmd->u.roamCmd.roamProfile, + bss_desc, + cmd->u.roamCmd.roamId))) { + sme_err("CSR start BSS failed"); + /* We need to complete the command */ + csr_roam_complete(mac_ctx, eCsrStartBssFailure, NULL, + vdev_id); + } + return; + } + + if (!cmd->u.roamCmd.pRoamBssEntry) { + sme_err("pRoamBssEntry is NULL"); + /* We need to complete the command */ + csr_roam_complete(mac_ctx, eCsrJoinFailure, NULL, vdev_id); + return; + } + + if (!scan_result) { + /* If we are roaming TO an Infrastructure BSS... */ + QDF_ASSERT(scan_result); + return; + } + + if (!csr_is_infra_bss_desc(bss_desc)) { + sme_warn("found BSSType mismatching the one in BSS descp"); + return; + } + + local_ies = (tDot11fBeaconIEs *) scan_result->Result.pvIes; + if (!local_ies) { + status = csr_get_parsed_bss_description_ies(mac_ctx, bss_desc, + &local_ies); + if (!QDF_IS_STATUS_SUCCESS(status)) + return; + is_ies_malloced = true; + } + + if (csr_is_conn_state_connected_infra(mac_ctx, session_id)) { + if (csr_is_ssid_equal(mac_ctx, session->pConnectBssDesc, + bss_desc, local_ies)) { + cmd->u.roamCmd.fReassoc = true; + csr_roam_issue_reassociate(mac_ctx, session_id, + bss_desc, local_ies, + &cmd->u.roamCmd.roamProfile); + } else { + /* + * otherwise, we have to issue a new Join request to LIM + * because we disassociated from the previously + * associated AP. + */ + status = csr_roam_issue_join(mac_ctx, session_id, + bss_desc, local_ies, + &cmd->u.roamCmd.roamProfile, + cmd->u.roamCmd.roamId); + if (!QDF_IS_STATUS_SUCCESS(status)) { + /* try something else */ + csr_roam(mac_ctx, cmd, false); + } + } + } else { + status = QDF_STATUS_SUCCESS; + /* + * We need to come with other way to figure out that this is + * because of HO in BMP The below API will be only available for + * Android as it uses a different HO algorithm. Reassoc request + * will be used only for ESE and 11r handoff whereas other + * legacy roaming should use join request + */ + if (csr_roam_is_handoff_in_progress(mac_ctx, session_id) + && csr_roam_is11r_assoc(mac_ctx, session_id)) { + status = csr_roam_issue_reassociate(mac_ctx, + session_id, bss_desc, + local_ies, + &cmd->u.roamCmd.roamProfile); + } else +#ifdef FEATURE_WLAN_ESE + if (csr_roam_is_handoff_in_progress(mac_ctx, session_id) + && csr_roam_is_ese_assoc(mac_ctx, session_id)) { + /* Now serialize the reassoc command. */ + status = csr_roam_issue_reassociate_cmd(mac_ctx, + session_id); + } else +#endif + if (csr_roam_is_handoff_in_progress(mac_ctx, session_id) + && csr_roam_is_fast_roam_enabled(mac_ctx, session_id)) { + /* Now serialize the reassoc command. */ + status = csr_roam_issue_reassociate_cmd(mac_ctx, + session_id); + } else { + /* + * else we are not connected and attempting to Join. + * Issue the Join request. + */ + status = csr_roam_issue_join(mac_ctx, session_id, + bss_desc, + local_ies, + &cmd->u.roamCmd.roamProfile, + cmd->u.roamCmd.roamId); + } + if (!QDF_IS_STATUS_SUCCESS(status)) { + /* try something else */ + csr_roam(mac_ctx, cmd, false); + } + } + if (is_ies_malloced) { + /* Locally allocated */ + qdf_mem_free(local_ies); + } +} + +static void csr_roam_roaming_state_reassoc_rsp_processor(struct mac_context *mac, + struct join_rsp *pSmeJoinRsp) +{ + enum csr_roamcomplete_result result; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = NULL; + struct csr_roam_session *csr_session; + + if (pSmeJoinRsp->vdev_id >= WLAN_MAX_VDEVS) { + sme_err("Invalid session ID received %d", pSmeJoinRsp->vdev_id); + return; + } + + pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[pSmeJoinRsp->vdev_id]; + if (eSIR_SME_SUCCESS == pSmeJoinRsp->status_code) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "CSR SmeReassocReq Successful"); + result = eCsrReassocSuccess; + csr_session = CSR_GET_SESSION(mac, pSmeJoinRsp->vdev_id); + if (csr_session) { + if (pSmeJoinRsp->nss < csr_session->nss) { + csr_session->nss = pSmeJoinRsp->nss; + csr_session->vdev_nss = pSmeJoinRsp->nss; + } + csr_session->supported_nss_1x1 = + pSmeJoinRsp->supported_nss_1x1; + sme_debug("SME session supported nss: %d", + csr_session->supported_nss_1x1); + } + /* + * Since the neighbor roam algorithm uses reassoc req for + * handoff instead of join, we need the response contents while + * processing the result in csr_roam_process_results() + */ + if (csr_roam_is_handoff_in_progress(mac, + pSmeJoinRsp->vdev_id)) { + /* Need to dig more on indicating events to + * SME QoS module + */ + sme_qos_csr_event_ind(mac, pSmeJoinRsp->vdev_id, + SME_QOS_CSR_HANDOFF_COMPLETE, NULL); + csr_roam_complete(mac, result, pSmeJoinRsp, + pSmeJoinRsp->vdev_id); + } else { + csr_roam_complete(mac, result, NULL, + pSmeJoinRsp->vdev_id); + } + } + /* Should we handle this similar to handling the join failure? Is it ok + * to call csr_roam_complete() with state as CsrJoinFailure + */ + else { + sme_warn( + "CSR SmeReassocReq failed with status_code= 0x%08X [%d]", + pSmeJoinRsp->status_code, pSmeJoinRsp->status_code); + result = eCsrReassocFailure; + cds_flush_logs(WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_REASON_ROAM_FAIL, + false, false); + if ((eSIR_SME_FT_REASSOC_TIMEOUT_FAILURE == + pSmeJoinRsp->status_code) + || (eSIR_SME_FT_REASSOC_FAILURE == + pSmeJoinRsp->status_code) + || (eSIR_SME_INVALID_PARAMETERS == + pSmeJoinRsp->status_code)) { + /* Inform HDD to turn off FT flag in HDD */ + if (pNeighborRoamInfo) { + struct csr_roam_info *roam_info; + uint32_t roam_id = 0; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + mlme_set_discon_reason_n_from_ap(mac->psoc, + pSmeJoinRsp->vdev_id, false, + eSIR_MAC_HOST_TRIGGERED_ROAM_FAILURE); + csr_roam_call_callback(mac, + pSmeJoinRsp->vdev_id, + roam_info, roam_id, + eCSR_ROAM_FT_REASSOC_FAILED, + eCSR_ROAM_RESULT_SUCCESS); + /* + * Since the above callback sends a disconnect + * to HDD, we should clean-up our state + * machine as well to be in sync with the upper + * layers. There is no need to send a disassoc + * since: 1) we will never reassoc to the + * current AP in LFR, and 2) there is no need + * to issue a disassoc to the AP with which we + * were trying to reassoc. + */ + csr_roam_complete(mac, eCsrJoinFailure, NULL, + pSmeJoinRsp->vdev_id); + qdf_mem_free(roam_info); + return; + } + } + /* In the event that the Reassociation fails, then we need to + * Disassociate the current association and keep roaming. Note + * that we will attempt to Join the AP instead of a Reassoc + * since we may have attempted a 'Reassoc to self', which AP's + * that don't support Reassoc will force a Disassoc. The + * isassoc rsp message will remove the command from active list + */ + if (!QDF_IS_STATUS_SUCCESS + (csr_roam_issue_disassociate + (mac, pSmeJoinRsp->vdev_id, + eCSR_ROAM_SUBSTATE_DISASSOC_REASSOC_FAILURE, + false))) { + csr_roam_complete(mac, eCsrJoinFailure, NULL, + pSmeJoinRsp->vdev_id); + } + } +} + +static void csr_roam_roaming_state_stop_bss_rsp_processor(struct mac_context *mac, + tSirSmeRsp *pSmeRsp) +{ + enum csr_roamcomplete_result result_code = eCsrNothingToJoin; + struct csr_roam_profile *profile; + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + { + host_log_ibss_pkt_type *pIbssLog; + + WLAN_HOST_DIAG_LOG_ALLOC(pIbssLog, host_log_ibss_pkt_type, + LOG_WLAN_IBSS_C); + if (pIbssLog) { + pIbssLog->eventId = WLAN_IBSS_EVENT_STOP_RSP; + if (eSIR_SME_SUCCESS != pSmeRsp->status_code) + pIbssLog->status = WLAN_IBSS_STATUS_FAILURE; + WLAN_HOST_DIAG_LOG_REPORT(pIbssLog); + } + } +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + mac->roam.roamSession[pSmeRsp->vdev_id].connectState = + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED; + if (CSR_IS_ROAM_SUBSTATE_STOP_BSS_REQ(mac, pSmeRsp->vdev_id)) { + profile = + mac->roam.roamSession[pSmeRsp->vdev_id].pCurRoamProfile; + if (profile && CSR_IS_CONN_NDI(profile)) { + result_code = eCsrStopBssSuccess; + if (pSmeRsp->status_code != eSIR_SME_SUCCESS) + result_code = eCsrStopBssFailure; + } + csr_roam_complete(mac, result_code, NULL, pSmeRsp->vdev_id); + } else if (CSR_IS_ROAM_SUBSTATE_DISCONNECT_CONTINUE(mac, + pSmeRsp->vdev_id)) { + csr_roam_reissue_roam_command(mac, pSmeRsp->vdev_id); + } +} + +#ifdef WLAN_FEATURE_HOST_ROAM +/** + * csr_dequeue_command() - removes a command from active cmd list + * @mac: mac global context + * + * Return: void + */ +static void +csr_dequeue_command(struct mac_context *mac_ctx) +{ + bool fRemoveCmd; + tSmeCmd *cmd = NULL; + tListElem *entry = csr_nonscan_active_ll_peek_head(mac_ctx, + LL_ACCESS_LOCK); + if (!entry) { + sme_err("NO commands are active"); + return; + } + + cmd = GET_BASE_ADDR(entry, tSmeCmd, Link); + /* + * If the head of the queue is Active and it is a given cmd type, remove + * and put this on the Free queue. + */ + if (eSmeCommandRoam != cmd->command) { + sme_err("Roam command not active"); + return; + } + /* + * we need to process the result first before removing it from active + * list because state changes still happening insides + * roamQProcessRoamResults so no other roam command should be issued. + */ + fRemoveCmd = csr_nonscan_active_ll_remove_entry(mac_ctx, entry, + LL_ACCESS_LOCK); + if (cmd->u.roamCmd.fReleaseProfile) { + csr_release_profile(mac_ctx, &cmd->u.roamCmd.roamProfile); + cmd->u.roamCmd.fReleaseProfile = false; + } + if (fRemoveCmd) + csr_release_command(mac_ctx, cmd); + else + sme_err("fail to remove cmd reason %d", + cmd->u.roamCmd.roamReason); +} + +/** + * csr_post_roam_failure() - post roam failure back to csr and issues a disassoc + * @mac: mac global context + * @session_id: session id + * @roam_info: roam info struct + * @scan_filter: scan filter to free + * @cur_roam_profile: current csr roam profile + * + * Return: void + */ +static void +csr_post_roam_failure(struct mac_context *mac_ctx, + uint32_t session_id, + struct csr_roam_info *roam_info, + struct csr_roam_profile *cur_roam_profile) +{ + QDF_STATUS status; + + if (cur_roam_profile) + qdf_mem_free(cur_roam_profile); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + csr_roam_synch_clean_up(mac_ctx, session_id); +#endif + /* Inform the upper layers that the reassoc failed */ + qdf_mem_zero(roam_info, sizeof(struct csr_roam_info)); + csr_roam_call_callback(mac_ctx, session_id, roam_info, 0, + eCSR_ROAM_FT_REASSOC_FAILED, + eCSR_ROAM_RESULT_SUCCESS); + /* + * Issue a disassoc request so that PE/LIM uses this to clean-up the FT + * session. Upon success, we would re-enter this routine after receiving + * the disassoc response and will fall into the reassoc fail sub-state. + * And, eventually call csr_roam_complete which would remove the roam + * command from SME active queue. + */ + status = csr_roam_issue_disassociate(mac_ctx, session_id, + eCSR_ROAM_SUBSTATE_DISASSOC_REASSOC_FAILURE, false); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err( + "csr_roam_issue_disassociate failed, status %d", + status); + csr_roam_complete(mac_ctx, eCsrJoinFailure, NULL, session_id); + } +} + +/** + * csr_check_profile_in_scan_cache() - finds if roam profile is present in scan + * cache or not + * @mac: mac global context + * @neighbor_roam_info: roam info struct + * @hBSSList: scan result + * + * Return: true if found else false. + */ +static bool +csr_check_profile_in_scan_cache(struct mac_context *mac_ctx, + tpCsrNeighborRoamControlInfo neighbor_roam_info, + tScanResultHandle *hBSSList) +{ + QDF_STATUS status; + struct scan_filter *scan_filter; + + scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); + if (!scan_filter) + return false; + + status = csr_roam_get_scan_filter_from_profile(mac_ctx, + &neighbor_roam_info->csrNeighborRoamProfile, + scan_filter, true); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err( + "failed to prepare scan filter, status %d", + status); + qdf_mem_free(scan_filter); + return false; + } + status = csr_scan_get_result(mac_ctx, scan_filter, hBSSList); + qdf_mem_free(scan_filter); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err( + "csr_scan_get_result failed, status %d", + status); + return false; + } + return true; +} + +static +QDF_STATUS csr_roam_lfr2_issue_connect(struct mac_context *mac, + uint32_t session_id, + struct scan_result_list *hbss_list, + uint32_t roam_id) +{ + struct csr_roam_profile *cur_roam_profile = NULL; + struct csr_roam_session *session; + QDF_STATUS status; + + session = CSR_GET_SESSION(mac, session_id); + if (!session) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "session is NULL"); + return QDF_STATUS_E_FAILURE; + } + /* + * Copy the connected profile to apply the same for this + * connection as well + */ + cur_roam_profile = qdf_mem_malloc(sizeof(*cur_roam_profile)); + if (cur_roam_profile) { + /* + * notify sub-modules like QoS etc. that handoff + * happening + */ + sme_qos_csr_event_ind(mac, session_id, + SME_QOS_CSR_HANDOFF_ASSOC_REQ, + NULL); + csr_roam_copy_profile(mac, cur_roam_profile, + session->pCurRoamProfile); + /* make sure to put it at the head of the cmd queue */ + status = csr_roam_issue_connect(mac, session_id, + cur_roam_profile, hbss_list, + eCsrSmeIssuedAssocToSimilarAP, + roam_id, true, false); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err( + "issue_connect failed. status %d", + status); + + csr_release_profile(mac, cur_roam_profile); + qdf_mem_free(cur_roam_profile); + return QDF_STATUS_SUCCESS; + } else { + QDF_ASSERT(0); + csr_dequeue_command(mac); + } + return QDF_STATUS_E_NOMEM; +} + +QDF_STATUS csr_continue_lfr2_connect(struct mac_context *mac, + uint32_t session_id) +{ + uint32_t roam_id = 0; + struct csr_roam_info *roam_info; + struct scan_result_list *scan_handle_roam_ap; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + + scan_handle_roam_ap = + mac->roam.neighborRoamInfo[session_id].scan_res_lfr2_roam_ap; + if (!scan_handle_roam_ap) + goto POST_ROAM_FAILURE; + + if ((mac->roam.roamSession[session_id].connectState == + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED) || + (mac->roam.roamSession[session_id].connectState == + eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTING)) { + goto purge_scan_result; + } + + status = csr_roam_lfr2_issue_connect(mac, session_id, + scan_handle_roam_ap, + roam_id); + if (QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(roam_info); + return status; + } + +purge_scan_result: + csr_scan_result_purge(mac, scan_handle_roam_ap); + +POST_ROAM_FAILURE: + csr_post_roam_failure(mac, session_id, roam_info, NULL); + qdf_mem_free(roam_info); + return status; +} + +static +void csr_handle_disassoc_ho(struct mac_context *mac, uint32_t session_id) +{ + uint32_t roam_id = 0; + struct csr_roam_info *roam_info; + struct sCsrNeighborRoamControlInfo *neighbor_roam_info = NULL; + struct scan_result_list *scan_handle_roam_ap; + struct sCsrNeighborRoamBSSInfo *bss_node; + QDF_STATUS status; + + csr_dequeue_command(mac); + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "CSR SmeDisassocReq due to HO on session %d", session_id); + neighbor_roam_info = &mac->roam.neighborRoamInfo[session_id]; + + /* + * First ensure if the roam profile is in the scan cache. + * If not, post a reassoc failure and disconnect. + */ + if (!csr_check_profile_in_scan_cache(mac, neighbor_roam_info, + (tScanResultHandle *)&scan_handle_roam_ap)) + goto POST_ROAM_FAILURE; + + /* notify HDD about handoff and provide the BSSID too */ + roam_info->reasonCode = eCsrRoamReasonBetterAP; + + qdf_copy_macaddr(&roam_info->bssid, + neighbor_roam_info->csrNeighborRoamProfile.BSSIDs.bssid); + + /* + * For LFR2, removal of policy mgr entry for disassociated + * AP is handled in eCSR_ROAM_ROAMING_START. + * eCSR_ROAM_RESULT_NOT_ASSOCIATED is sent to differentiate + * eCSR_ROAM_ROAMING_START sent after FT preauth success + */ + csr_roam_call_callback(mac, session_id, roam_info, 0, + eCSR_ROAM_ROAMING_START, + eCSR_ROAM_RESULT_NOT_ASSOCIATED); + + bss_node = csr_neighbor_roam_next_roamable_ap(mac, + &neighbor_roam_info->FTRoamInfo.preAuthDoneList, + NULL); + if (!bss_node) { + sme_debug("LFR2DBG: bss_node is NULL"); + goto POST_ROAM_FAILURE; + } + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR2DBG: preauthed bss_node->pBssDescription BSSID"\ + QDF_MAC_ADDR_FMT",freq:%d", + QDF_MAC_ADDR_REF(bss_node->pBssDescription->bssId), + bss_node->pBssDescription->chan_freq); + + status = policy_mgr_handle_conc_multiport( + mac->psoc, session_id, + bss_node->pBssDescription->chan_freq, + POLICY_MGR_UPDATE_REASON_LFR2_ROAM); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->roam.neighborRoamInfo[session_id].scan_res_lfr2_roam_ap = + scan_handle_roam_ap; + /*if hw_mode change is required then handle roam + * issue connect in mode change response handler + */ + qdf_mem_free(roam_info); + return; + } else if (status == QDF_STATUS_E_FAILURE) + goto POST_ROAM_FAILURE; + + status = csr_roam_lfr2_issue_connect(mac, session_id, + scan_handle_roam_ap, + roam_id); + if (QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(roam_info); + return; + } + csr_scan_result_purge(mac, scan_handle_roam_ap); + +POST_ROAM_FAILURE: + mlme_set_discon_reason_n_from_ap(mac->psoc, session_id, false, + eSIR_MAC_HOST_TRIGGERED_ROAM_FAILURE); + csr_post_roam_failure(mac, session_id, roam_info, NULL); + qdf_mem_free(roam_info); +} +#else +static +void csr_handle_disassoc_ho(struct mac_context *mac, uint32_t session_id) +{ + return; +} +#endif + +static +void csr_roam_roaming_state_disassoc_rsp_processor(struct mac_context *mac, + struct disassoc_rsp *rsp) +{ + uint32_t sessionId; + struct csr_roam_session *pSession; + + sessionId = rsp->sessionId; + sme_debug("sessionId %d", sessionId); + + if (csr_is_conn_state_infra(mac, sessionId)) { + mac->roam.roamSession[sessionId].connectState = + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED; + } + + pSession = CSR_GET_SESSION(mac, sessionId); + if (!pSession) { + sme_err("session %d not found", sessionId); + return; + } + + + if (CSR_IS_ROAM_SUBSTATE_DISASSOC_NO_JOIN(mac, sessionId)) { + sme_debug("***eCsrNothingToJoin***"); + csr_roam_complete(mac, eCsrNothingToJoin, NULL, sessionId); + } else if (CSR_IS_ROAM_SUBSTATE_DISASSOC_FORCED(mac, sessionId) || + CSR_IS_ROAM_SUBSTATE_DISASSOC_REQ(mac, sessionId)) { + if (eSIR_SME_SUCCESS == rsp->status_code) { + sme_debug("CSR force disassociated successful"); + /* + * A callback to HDD will be issued from + * csr_roam_complete so no need to do anything here + */ + } + csr_roam_complete(mac, eCsrNothingToJoin, NULL, sessionId); + } else if (CSR_IS_ROAM_SUBSTATE_DISASSOC_HO(mac, sessionId)) { + csr_handle_disassoc_ho(mac, sessionId); + } /* else if ( CSR_IS_ROAM_SUBSTATE_DISASSOC_HO( mac ) ) */ + else if (CSR_IS_ROAM_SUBSTATE_REASSOC_FAIL(mac, sessionId)) { + /* Disassoc due to Reassoc failure falls into this codepath */ + csr_roam_complete(mac, eCsrJoinFailure, NULL, sessionId); + } else { + if (eSIR_SME_SUCCESS == rsp->status_code) { + /* + * Successfully disassociated from the 'old' Bss. + * We get Disassociate response in three conditions. + * 1) The case where we are disasociating from an Infra + * Bss to start an IBSS. + * 2) When we are disassociating from an Infra Bss to + * join an IBSS or a new infra network. + * 3) Where we are doing an Infra to Infra roam between + * networks with different SSIDs. + * In all cases, we set the new Bss configuration here + * and attempt to join + */ + sme_debug("Disassociated successfully"); + } else { + sme_err("DisassocReq failed, status_code= 0x%08X", + rsp->status_code); + } + /* We are not done yet. Get the data and continue roaming */ + csr_roam_reissue_roam_command(mac, sessionId); + } +} + +static void csr_roam_roaming_state_deauth_rsp_processor(struct mac_context *mac, + struct deauth_rsp *pSmeRsp) +{ + tSirResultCodes status_code; + status_code = csr_get_de_auth_rsp_status_code(pSmeRsp); + mac->roam.deauthRspStatus = status_code; + if (CSR_IS_ROAM_SUBSTATE_DEAUTH_REQ(mac, pSmeRsp->sessionId)) { + csr_roam_complete(mac, eCsrNothingToJoin, NULL, + pSmeRsp->sessionId); + } else { + if (eSIR_SME_SUCCESS == status_code) { + /* Successfully deauth from the 'old' Bss... */ + /* */ + sme_debug( + "CSR SmeDeauthReq disassociated Successfully"); + } + /* We are not done yet. Get the data and continue roaming */ + csr_roam_reissue_roam_command(mac, pSmeRsp->sessionId); + } +} + +static void +csr_roam_roaming_state_start_bss_rsp_processor(struct mac_context *mac, + struct start_bss_rsp *pSmeStartBssRsp) +{ + enum csr_roamcomplete_result result; + + if (eSIR_SME_SUCCESS == pSmeStartBssRsp->status_code) { + sme_debug("SmeStartBssReq Successful"); + result = eCsrStartBssSuccess; + } else { + sme_warn("SmeStartBssReq failed with status_code= 0x%08X", + pSmeStartBssRsp->status_code); + /* Let csr_roam_complete decide what to do */ + result = eCsrStartBssFailure; + } + csr_roam_complete(mac, result, pSmeStartBssRsp, + pSmeStartBssRsp->sessionId); +} + +/** + * csr_roam_send_disconnect_done_indication() - Send disconnect ind to HDD. + * + * @mac_ctx: mac global context + * @msg_ptr: incoming message + * + * This function gives final disconnect event to HDD after all cleanup in + * lower layers is done. + * + * Return: None + */ +static void +csr_roam_send_disconnect_done_indication(struct mac_context *mac_ctx, + tSirSmeRsp *msg_ptr) +{ + struct sir_sme_discon_done_ind *discon_ind = + (struct sir_sme_discon_done_ind *)(msg_ptr); + struct csr_roam_info *roam_info; + struct csr_roam_session *session; + struct wlan_objmgr_vdev *vdev; + uint8_t vdev_id; + + vdev_id = discon_ind->session_id; + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + + sme_debug("DISCONNECT_DONE_IND RC:%d", discon_ind->reason_code); + + if (CSR_IS_SESSION_VALID(mac_ctx, vdev_id)) { + roam_info->reasonCode = discon_ind->reason_code; + roam_info->status_code = eSIR_SME_STA_NOT_ASSOCIATED; + qdf_mem_copy(roam_info->peerMac.bytes, discon_ind->peer_mac, + ETH_ALEN); + + roam_info->rssi = mac_ctx->peer_rssi; + roam_info->tx_rate = mac_ctx->peer_txrate; + roam_info->rx_rate = mac_ctx->peer_rxrate; + roam_info->disassoc_reason = discon_ind->reason_code; + roam_info->rx_mc_bc_cnt = mac_ctx->rx_mc_bc_cnt; + roam_info->rx_retry_cnt = mac_ctx->rx_retry_cnt; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + vdev_id, + WLAN_LEGACY_SME_ID); + if (vdev) + roam_info->disconnect_ies = + mlme_get_peer_disconnect_ies(vdev); + + csr_roam_call_callback(mac_ctx, vdev_id, + roam_info, 0, eCSR_ROAM_LOSTLINK, + eCSR_ROAM_RESULT_DISASSOC_IND); + if (vdev) { + mlme_free_peer_disconnect_ies(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + } + session = CSR_GET_SESSION(mac_ctx, vdev_id); + if (!CSR_IS_INFRA_AP(&session->connectedProfile)) + csr_roam_state_change(mac_ctx, eCSR_ROAMING_STATE_IDLE, + vdev_id); + + if (CSR_IS_INFRASTRUCTURE(&session->connectedProfile)) { + csr_free_roam_profile(mac_ctx, vdev_id); + csr_free_connect_bss_desc(mac_ctx, vdev_id); + csr_roam_free_connect_profile( + &session->connectedProfile); + csr_roam_free_connected_info(mac_ctx, + &session->connectedInfo); + } + + } else { + sme_err("Inactive vdev_id %d", vdev_id); + } + + /* + * Release WM status change command as eWNI_SME_DISCONNECT_DONE_IND + * has been sent to HDD and there is nothing else left to do. + */ + csr_roam_wm_status_change_complete(mac_ctx, vdev_id); + qdf_mem_free(roam_info); +} + +/** + * csr_roaming_state_msg_processor() - process roaming messages + * @mac: mac global context + * @msg_buf: message buffer + * + * We need to be careful on whether to cast msg_buf (pSmeRsp) to other type of + * strucutres. It depends on how the message is constructed. If the message is + * sent by lim_send_sme_rsp, the msg_buf is only a generic response and can only + * be used as pointer to tSirSmeRsp. For the messages where sender allocates + * memory for specific structures, then it can be cast accordingly. + * + * Return: status of operation + */ +void csr_roaming_state_msg_processor(struct mac_context *mac, void *msg_buf) +{ + tSirSmeRsp *pSmeRsp; + tSmeIbssPeerInd *pIbssPeerInd; + struct csr_roam_info *roam_info; + + pSmeRsp = (tSirSmeRsp *)msg_buf; + + switch (pSmeRsp->messageType) { + + case eWNI_SME_JOIN_RSP: + /* in Roaming state, process the Join response message... */ + if (CSR_IS_ROAM_SUBSTATE_JOIN_REQ(mac, pSmeRsp->vdev_id)) + /* We sent a JOIN_REQ */ + csr_roam_join_rsp_processor(mac, + (struct join_rsp *)pSmeRsp); + break; + case eWNI_SME_REASSOC_RSP: + /* or the Reassociation response message... */ + if (CSR_IS_ROAM_SUBSTATE_REASSOC_REQ(mac, pSmeRsp->vdev_id)) + csr_roam_roaming_state_reassoc_rsp_processor(mac, + (struct join_rsp *)pSmeRsp); + break; + case eWNI_SME_STOP_BSS_RSP: + /* or the Stop Bss response message... */ + csr_roam_roaming_state_stop_bss_rsp_processor(mac, pSmeRsp); + break; + case eWNI_SME_DISASSOC_RSP: + /* or the Disassociate response message... */ + if (CSR_IS_ROAM_SUBSTATE_DISASSOC_REQ(mac, pSmeRsp->vdev_id) + || CSR_IS_ROAM_SUBSTATE_DISASSOC_NO_JOIN(mac, + pSmeRsp->vdev_id) + || CSR_IS_ROAM_SUBSTATE_REASSOC_FAIL(mac, + pSmeRsp->vdev_id) + || CSR_IS_ROAM_SUBSTATE_DISASSOC_FORCED(mac, + pSmeRsp->vdev_id) + || CSR_IS_ROAM_SUBSTATE_DISCONNECT_CONTINUE(mac, + pSmeRsp->vdev_id) + || CSR_IS_ROAM_SUBSTATE_DISASSOC_HO(mac, + pSmeRsp->vdev_id)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "eWNI_SME_DISASSOC_RSP subState = %s", + mac_trace_getcsr_roam_sub_state( + mac->roam.curSubState[pSmeRsp->vdev_id])); + csr_roam_roaming_state_disassoc_rsp_processor(mac, + (struct disassoc_rsp *) pSmeRsp); + } + break; + case eWNI_SME_DEAUTH_RSP: + /* or the Deauthentication response message... */ + if (CSR_IS_ROAM_SUBSTATE_DEAUTH_REQ(mac, pSmeRsp->vdev_id)) + csr_roam_roaming_state_deauth_rsp_processor(mac, + (struct deauth_rsp *) pSmeRsp); + break; + case eWNI_SME_START_BSS_RSP: + /* or the Start BSS response message... */ + if (CSR_IS_ROAM_SUBSTATE_START_BSS_REQ(mac, + pSmeRsp->vdev_id)) + csr_roam_roaming_state_start_bss_rsp_processor(mac, + (struct start_bss_rsp *)pSmeRsp); + break; + /* In case CSR issues STOP_BSS, we need to tell HDD about peer departed + * because PE is removing them + */ + case eWNI_SME_IBSS_PEER_DEPARTED_IND: + pIbssPeerInd = (tSmeIbssPeerInd *) pSmeRsp; + sme_err("Peer departed ntf from LIM in joining state"); + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + break; + + qdf_copy_macaddr(&roam_info->peerMac, &pIbssPeerInd->peer_addr); + csr_roam_call_callback(mac, pSmeRsp->vdev_id, roam_info, 0, + eCSR_ROAM_CONNECT_STATUS_UPDATE, + eCSR_ROAM_RESULT_IBSS_PEER_DEPARTED); + qdf_mem_free(roam_info); + roam_info = NULL; + break; + case eWNI_SME_TRIGGER_SAE: + sme_debug("Invoke SAE callback"); + csr_sae_callback(mac, pSmeRsp); + break; + + case eWNI_SME_SETCONTEXT_RSP: + csr_roam_check_for_link_status_change(mac, pSmeRsp); + break; + + case eWNI_SME_DISCONNECT_DONE_IND: + csr_roam_send_disconnect_done_indication(mac, pSmeRsp); + break; + + case eWNI_SME_UPPER_LAYER_ASSOC_CNF: + csr_roam_joined_state_msg_processor(mac, pSmeRsp); + break; + default: + sme_debug("Unexpected message type: %d[0x%X] received in substate %s", + pSmeRsp->messageType, pSmeRsp->messageType, + mac_trace_getcsr_roam_sub_state( + mac->roam.curSubState[pSmeRsp->vdev_id])); + /* If we are connected, check the link status change */ + if (!csr_is_conn_state_disconnected(mac, pSmeRsp->vdev_id)) + csr_roam_check_for_link_status_change(mac, pSmeRsp); + break; + } +} + +void csr_roam_joined_state_msg_processor(struct mac_context *mac, void *msg_buf) +{ + tSirSmeRsp *pSirMsg = (tSirSmeRsp *)msg_buf; + + switch (pSirMsg->messageType) { + case eWNI_SME_UPPER_LAYER_ASSOC_CNF: + { + struct csr_roam_session *pSession; + tSirSmeAssocIndToUpperLayerCnf *pUpperLayerAssocCnf; + struct csr_roam_info *roam_info; + uint32_t sessionId; + QDF_STATUS status; + + sme_debug("ASSOCIATION confirmation can be given to upper layer "); + pUpperLayerAssocCnf = + (tSirSmeAssocIndToUpperLayerCnf *)msg_buf; + status = csr_roam_get_session_id_from_bssid(mac, + (struct qdf_mac_addr *) + pUpperLayerAssocCnf-> + bssId, &sessionId); + pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + if (pUpperLayerAssocCnf->ies) + qdf_mem_free(pUpperLayerAssocCnf->ies); + return; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) { + sme_err("roam_info not allocated"); + if (pUpperLayerAssocCnf->ies) + qdf_mem_free(pUpperLayerAssocCnf->ies); + return; + } + /* send the status code as Success */ + roam_info->status_code = eSIR_SME_SUCCESS; + roam_info->u.pConnectedProfile = + &pSession->connectedProfile; + roam_info->staId = (uint8_t) pUpperLayerAssocCnf->aid; + roam_info->rsnIELen = + (uint8_t) pUpperLayerAssocCnf->rsnIE.length; + roam_info->prsnIE = + pUpperLayerAssocCnf->rsnIE.rsnIEdata; +#ifdef FEATURE_WLAN_WAPI + roam_info->wapiIELen = + (uint8_t) pUpperLayerAssocCnf->wapiIE.length; + roam_info->pwapiIE = + pUpperLayerAssocCnf->wapiIE.wapiIEdata; +#endif + roam_info->addIELen = + (uint8_t) pUpperLayerAssocCnf->addIE.length; + roam_info->paddIE = + pUpperLayerAssocCnf->addIE.addIEdata; + qdf_mem_copy(roam_info->peerMac.bytes, + pUpperLayerAssocCnf->peerMacAddr, + sizeof(tSirMacAddr)); + qdf_mem_copy(&roam_info->bssid, + pUpperLayerAssocCnf->bssId, + sizeof(struct qdf_mac_addr)); + roam_info->wmmEnabledSta = + pUpperLayerAssocCnf->wmmEnabledSta; + roam_info->timingMeasCap = + pUpperLayerAssocCnf->timingMeasCap; + qdf_mem_copy(&roam_info->chan_info, + &pUpperLayerAssocCnf->chan_info, + sizeof(struct oem_channel_info)); + + roam_info->ampdu = pUpperLayerAssocCnf->ampdu; + roam_info->sgi_enable = pUpperLayerAssocCnf->sgi_enable; + roam_info->tx_stbc = pUpperLayerAssocCnf->tx_stbc; + roam_info->rx_stbc = pUpperLayerAssocCnf->rx_stbc; + roam_info->ch_width = pUpperLayerAssocCnf->ch_width; + roam_info->mode = pUpperLayerAssocCnf->mode; + roam_info->max_supp_idx = pUpperLayerAssocCnf->max_supp_idx; + roam_info->max_ext_idx = pUpperLayerAssocCnf->max_ext_idx; + roam_info->max_mcs_idx = pUpperLayerAssocCnf->max_mcs_idx; + roam_info->rx_mcs_map = pUpperLayerAssocCnf->rx_mcs_map; + roam_info->tx_mcs_map = pUpperLayerAssocCnf->tx_mcs_map; + roam_info->ecsa_capable = pUpperLayerAssocCnf->ecsa_capable; + if (pUpperLayerAssocCnf->ht_caps.present) + roam_info->ht_caps = pUpperLayerAssocCnf->ht_caps; + if (pUpperLayerAssocCnf->vht_caps.present) + roam_info->vht_caps = pUpperLayerAssocCnf->vht_caps; + roam_info->capability_info = + pUpperLayerAssocCnf->capability_info; + roam_info->he_caps_present = + pUpperLayerAssocCnf->he_caps_present; + + if (CSR_IS_INFRA_AP(roam_info->u.pConnectedProfile)) { + if (pUpperLayerAssocCnf->ies_len > 0) { + roam_info->assocReqLength = + pUpperLayerAssocCnf->ies_len; + roam_info->assocReqPtr = + pUpperLayerAssocCnf->ies; + } + + mac->roam.roamSession[sessionId].connectState = + eCSR_ASSOC_STATE_TYPE_INFRA_CONNECTED; + roam_info->fReassocReq = + pUpperLayerAssocCnf->reassocReq; + status = csr_roam_call_callback(mac, sessionId, + roam_info, 0, + eCSR_ROAM_INFRA_IND, + eCSR_ROAM_RESULT_INFRA_ASSOCIATION_CNF); + } + if (pUpperLayerAssocCnf->ies) + qdf_mem_free(pUpperLayerAssocCnf->ies); + qdf_mem_free(roam_info); + } + break; + default: + csr_roam_check_for_link_status_change(mac, pSirMsg); + break; + } +} + +/** + * csr_update_wep_key_peer_macaddr() - Update wep key peer mac addr + * @vdev: vdev object + * @crypto_key: crypto key info + * @unicast: uncast or broadcast + * @mac_addr: peer mac address + * + * Update peer mac address to key context before set wep key to target. + * + * Return void + */ +static void +csr_update_wep_key_peer_macaddr(struct wlan_objmgr_vdev *vdev, + struct wlan_crypto_key *crypto_key, + bool unicast, + struct qdf_mac_addr *mac_addr) +{ + if (!crypto_key || !vdev) { + sme_err("vdev or crytpo_key null"); + return; + } + + if (unicast) { + qdf_mem_copy(&crypto_key->macaddr, mac_addr, + QDF_MAC_ADDR_SIZE); + } else { + if (vdev->vdev_mlme.vdev_opmode == QDF_STA_MODE || + vdev->vdev_mlme.vdev_opmode == QDF_P2P_CLIENT_MODE) + qdf_mem_copy(&crypto_key->macaddr, mac_addr, + QDF_MAC_ADDR_SIZE); + else + qdf_mem_copy(&crypto_key->macaddr, + vdev->vdev_mlme.macaddr, + QDF_MAC_ADDR_SIZE); + } +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +static void +csr_roam_diag_set_ctx_rsp(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct set_context_rsp *pRsp) +{ + WLAN_HOST_DIAG_EVENT_DEF(setKeyEvent, + host_event_wlan_security_payload_type); + if (eCSR_ENCRYPT_TYPE_NONE == + session->connectedProfile.EncryptionType) + return; + qdf_mem_zero(&setKeyEvent, + sizeof(host_event_wlan_security_payload_type)); + if (qdf_is_macaddr_group(&pRsp->peer_macaddr)) + setKeyEvent.eventId = + WLAN_SECURITY_EVENT_SET_BCAST_RSP; + else + setKeyEvent.eventId = + WLAN_SECURITY_EVENT_SET_UNICAST_RSP; + setKeyEvent.encryptionModeMulticast = + (uint8_t) diag_enc_type_from_csr_type( + session->connectedProfile.mcEncryptionType); + setKeyEvent.encryptionModeUnicast = + (uint8_t) diag_enc_type_from_csr_type( + session->connectedProfile.EncryptionType); + qdf_mem_copy(setKeyEvent.bssid, session->connectedProfile.bssid.bytes, + QDF_MAC_ADDR_SIZE); + setKeyEvent.authMode = + (uint8_t) diag_auth_type_from_csr_type( + session->connectedProfile.AuthType); + if (eSIR_SME_SUCCESS != pRsp->status_code) + setKeyEvent.status = WLAN_SECURITY_STATUS_FAILURE; + WLAN_HOST_DIAG_EVENT_REPORT(&setKeyEvent, EVENT_WLAN_SECURITY); +} +#else /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ +static void +csr_roam_diag_set_ctx_rsp(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct set_context_rsp *pRsp) +{ +} +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + +static void +csr_roam_chk_lnk_set_ctx_rsp(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + struct csr_roam_session *session; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct csr_roam_info *roam_info; + eCsrRoamResult result = eCSR_ROAM_RESULT_NONE; + struct set_context_rsp *pRsp = (struct set_context_rsp *)msg_ptr; + + if (!pRsp) { + sme_err("set key response is NULL"); + return; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + sessionId = pRsp->sessionId; + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (!session) { + sme_err("session %d not found", sessionId); + qdf_mem_free(roam_info); + return; + } + + csr_roam_diag_set_ctx_rsp(mac_ctx, session, pRsp); + + if (CSR_IS_WAIT_FOR_KEY(mac_ctx, sessionId)) { + csr_roam_stop_wait_for_key_timer(mac_ctx); + /* We are done with authentication, whethere succeed or not */ + csr_roam_substate_change(mac_ctx, eCSR_ROAM_SUBSTATE_NONE, + sessionId); + /* We do it here because this linkup function is not called + * after association when a key needs to be set. + */ + if (csr_is_conn_state_connected_infra(mac_ctx, sessionId)) + csr_roam_link_up(mac_ctx, + session->connectedProfile.bssid); + } + if (eSIR_SME_SUCCESS == pRsp->status_code) { + qdf_copy_macaddr(&roam_info->peerMac, &pRsp->peer_macaddr); + /* Make sure we install the GTK before indicating to HDD as + * authenticated. This is to prevent broadcast packets go out + * after PTK and before GTK. + */ + if (qdf_is_macaddr_broadcast(&pRsp->peer_macaddr)) { + /* + * OBSS SCAN Indication will be sent to Firmware + * to start OBSS Scan + */ + if (mac_ctx->obss_scan_offload && + wlan_reg_is_24ghz_ch_freq( + session->connectedProfile.op_freq) && + (session->connectState == + eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED) && + session->pCurRoamProfile && + (QDF_P2P_CLIENT_MODE == + session->pCurRoamProfile->csrPersona || + (QDF_STA_MODE == + session->pCurRoamProfile->csrPersona))) { + struct sme_obss_ht40_scanind_msg *msg; + + msg = qdf_mem_malloc(sizeof( + struct sme_obss_ht40_scanind_msg)); + if (!msg) { + qdf_mem_free(roam_info); + return; + } + + msg->msg_type = eWNI_SME_HT40_OBSS_SCAN_IND; + msg->length = + sizeof(struct sme_obss_ht40_scanind_msg); + qdf_copy_macaddr(&msg->mac_addr, + &session->connectedProfile.bssid); + status = umac_send_mb_message_to_mac(msg); + } + result = eCSR_ROAM_RESULT_AUTHENTICATED; + } else { + result = eCSR_ROAM_RESULT_NONE; + } + } else { + result = eCSR_ROAM_RESULT_FAILURE; + sme_err( + "CSR: setkey command failed(err=%d) PeerMac " + QDF_MAC_ADDR_FMT, + pRsp->status_code, + QDF_MAC_ADDR_REF(pRsp->peer_macaddr.bytes)); + } + /* keeping roam_id = 0 as nobody is using roam_id for set_key */ + csr_roam_call_callback(mac_ctx, sessionId, roam_info, + 0, eCSR_ROAM_SET_KEY_COMPLETE, result); + /* Indicate SME_QOS that the SET_KEY is completed, so that SME_QOS + * can go ahead and initiate the TSPEC if any are pending + */ + sme_qos_csr_event_ind(mac_ctx, (uint8_t)sessionId, + SME_QOS_CSR_SET_KEY_SUCCESS_IND, NULL); +#ifdef FEATURE_WLAN_ESE + /* Send Adjacent AP repot to new AP. */ + if (result == eCSR_ROAM_RESULT_AUTHENTICATED && + session->isPrevApInfoValid && + session->connectedProfile.isESEAssoc) { + csr_send_ese_adjacent_ap_rep_ind(mac_ctx, session); + session->isPrevApInfoValid = false; + } +#endif + qdf_mem_free(roam_info); +} + +static QDF_STATUS csr_roam_issue_set_context_req(struct mac_context *mac_ctx, + uint32_t session_id, + bool add_key, bool unicast, + uint8_t key_idx, + struct qdf_mac_addr *mac_addr) +{ + enum wlan_crypto_cipher_type cipher; + struct wlan_crypto_key *crypto_key; + uint8_t wep_key_idx = 0; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, session_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) { + sme_err("VDEV object not found for session_id %d", session_id); + return QDF_STATUS_E_INVAL; + } + cipher = wlan_crypto_get_cipher(vdev, unicast, key_idx); + if (IS_WEP_CIPHER(cipher)) { + wep_key_idx = wlan_crypto_get_default_key_idx(vdev, !unicast); + crypto_key = wlan_crypto_get_key(vdev, wep_key_idx); + csr_update_wep_key_peer_macaddr(vdev, crypto_key, unicast, + mac_addr); + } else { + crypto_key = wlan_crypto_get_key(vdev, key_idx); + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + + sme_debug("session:%d, cipher:%d, ucast:%d, idx:%d, wep:%d, add:%d", + session_id, cipher, unicast, key_idx, wep_key_idx, add_key); + if (!IS_WEP_CIPHER(cipher) && !add_key) + return QDF_STATUS_E_INVAL; + + return ucfg_crypto_set_key_req(vdev, crypto_key, (unicast ? + WLAN_CRYPTO_KEY_TYPE_UNICAST : + WLAN_CRYPTO_KEY_TYPE_GROUP)); +} + +static enum wlan_crypto_cipher_type +csr_encr_to_cipher_type(eCsrEncryptionType encr_type) +{ + switch (encr_type) { + case eCSR_ENCRYPT_TYPE_WEP40: + return WLAN_CRYPTO_CIPHER_WEP_40; + case eCSR_ENCRYPT_TYPE_WEP104: + return WLAN_CRYPTO_CIPHER_WEP_104; + case eCSR_ENCRYPT_TYPE_TKIP: + return WLAN_CRYPTO_CIPHER_TKIP; + case eCSR_ENCRYPT_TYPE_AES: + return WLAN_CRYPTO_CIPHER_AES_CCM; + case eCSR_ENCRYPT_TYPE_AES_CMAC: + return WLAN_CRYPTO_CIPHER_AES_CMAC; + case eCSR_ENCRYPT_TYPE_AES_GMAC_128: + return WLAN_CRYPTO_CIPHER_AES_GMAC; + case eCSR_ENCRYPT_TYPE_AES_GMAC_256: + return WLAN_CRYPTO_CIPHER_AES_GMAC_256; + case eCSR_ENCRYPT_TYPE_AES_GCMP: + return WLAN_CRYPTO_CIPHER_AES_GCM; + case eCSR_ENCRYPT_TYPE_AES_GCMP_256: + return WLAN_CRYPTO_CIPHER_AES_GCM_256; + default: + return WLAN_CRYPTO_CIPHER_NONE; + } +} + +#ifdef WLAN_FEATURE_FILS_SK +static QDF_STATUS +csr_roam_store_fils_key(struct wlan_objmgr_vdev *vdev, + bool unicast, uint8_t key_id, + uint16_t key_length, uint8_t *key, + tSirMacAddr *bssid, + eCsrEncryptionType encr_type) +{ + struct wlan_crypto_key *crypto_key = NULL; + QDF_STATUS status; + /* + * key index is the FW key index. + * Key_id is for host crypto component key storage index + */ + uint8_t key_index = 0; + + if (unicast) + key_index = 0; + else + key_index = 2; + + crypto_key = wlan_crypto_get_key(vdev, key_id); + if (!crypto_key) { + crypto_key = qdf_mem_malloc(sizeof(*crypto_key)); + if (!crypto_key) + return QDF_STATUS_E_INVAL; + + status = wlan_crypto_save_key(vdev, key_id, crypto_key); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Failed to save key"); + qdf_mem_free(crypto_key); + return QDF_STATUS_E_INVAL; + } + } + qdf_mem_zero(crypto_key, sizeof(*crypto_key)); + /* TODO add support for FILS cipher translation in OSIF */ + crypto_key->cipher_type = csr_encr_to_cipher_type(encr_type); + crypto_key->keylen = key_length; + crypto_key->keyix = key_index; + sme_debug("key_len %d, unicast %d", key_length, unicast); + qdf_mem_copy(&crypto_key->keyval[0], key, key_length); + qdf_mem_copy(crypto_key->macaddr, bssid, QDF_MAC_ADDR_SIZE); + + return QDF_STATUS_SUCCESS; +} +#else +static inline +QDF_STATUS csr_roam_store_fils_key(struct wlan_objmgr_vdev *vdev, + bool unicast, uint8_t key_id, + uint16_t key_length, uint8_t *key, + tSirMacAddr *bssid, + eCsrEncryptionType encr_type) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif + +QDF_STATUS +csr_issue_set_context_req_helper(struct mac_context *mac_ctx, + struct csr_roam_profile *profile, + uint32_t session_id, tSirMacAddr *bssid, + bool addkey, bool unicast, + tAniKeyDirection key_direction, uint8_t key_id, + uint16_t key_length, uint8_t *key) +{ + enum wlan_crypto_cipher_type cipher; + struct wlan_objmgr_vdev *vdev; + struct set_context_rsp install_key_rsp; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, session_id, + WLAN_LEGACY_MAC_ID); + if (!vdev) { + sme_err("VDEV object not found for session_id %d", session_id); + return QDF_STATUS_E_INVAL; + } + + cipher = wlan_crypto_get_cipher(vdev, unicast, key_id); + if (addkey && !IS_WEP_CIPHER(cipher) && + (profile && csr_is_fils_connection(profile))) + csr_roam_store_fils_key(vdev, unicast, key_id, key_length, + key, bssid, + profile->negotiatedMCEncryptionType); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + + /* + * For open mode authentication, send dummy install key response to + * send OBSS scan and QOS event. + */ + if (profile && + profile->negotiatedUCEncryptionType == eCSR_ENCRYPT_TYPE_NONE) { + if (unicast) + return QDF_STATUS_SUCCESS; + + install_key_rsp.length = sizeof(install_key_rsp); + install_key_rsp.status_code = eSIR_SME_SUCCESS; + install_key_rsp.sessionId = session_id; + qdf_mem_copy(install_key_rsp.peer_macaddr.bytes, bssid, + QDF_MAC_ADDR_SIZE); + + csr_roam_chk_lnk_set_ctx_rsp(mac_ctx, + (tSirSmeRsp *)&install_key_rsp); + + return QDF_STATUS_SUCCESS; + } + + return csr_roam_issue_set_context_req(mac_ctx, session_id, addkey, + unicast, key_id, + (struct qdf_mac_addr *)bssid); +} + +#ifdef WLAN_FEATURE_FILS_SK +/* + * csr_create_fils_realm_hash: API to create hash using realm + * @fils_con_info: fils connection info obtained from supplicant + * @tmp_hash: pointer to new hash + * + * Return: None + */ +static bool +csr_create_fils_realm_hash(struct cds_fils_connection_info *fils_con_info, + uint8_t *tmp_hash) +{ + uint8_t *hash; + uint8_t *data[1]; + + if (!fils_con_info->realm_len) + return false; + + hash = qdf_mem_malloc(SHA256_DIGEST_SIZE); + if (!hash) + return false; + + data[0] = fils_con_info->realm; + qdf_get_hash(SHA256_CRYPTO_TYPE, 1, data, + &fils_con_info->realm_len, hash); + qdf_trace_hex_dump(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_DEBUG, + hash, SHA256_DIGEST_SIZE); + qdf_mem_copy(tmp_hash, hash, 2); + qdf_mem_free(hash); + return true; +} + +static void csr_update_fils_scan_filter(struct scan_filter *filter, + struct csr_roam_profile *profile) +{ + if (profile->fils_con_info && + profile->fils_con_info->is_fils_connection) { + uint8_t realm_hash[2]; + + sme_debug("creating realm based on fils info %d", + profile->fils_con_info->is_fils_connection); + filter->fils_scan_filter.realm_check = + csr_create_fils_realm_hash(profile->fils_con_info, + realm_hash); + if (filter->fils_scan_filter.realm_check) + qdf_mem_copy(filter->fils_scan_filter.fils_realm, + realm_hash, REAM_HASH_LEN); + } +} + +#else +static void csr_update_fils_scan_filter(struct scan_filter *filter, + struct csr_roam_profile *profile) +{ } +#endif + +static inline void csr_copy_ssids(struct wlan_ssid *ssid, tSirMacSSid *from) +{ + ssid->length = from->length; + if (ssid->length > WLAN_SSID_MAX_LEN) + ssid->length = WLAN_SSID_MAX_LEN; + qdf_mem_copy(ssid->ssid, from->ssId, ssid->length); +} + +void csr_copy_ssids_from_roam_params(struct roam_ext_params *roam_params, + struct scan_filter *filter) +{ + uint8_t i; + + if (!roam_params->num_ssid_allowed_list) + return; + + filter->num_of_ssid = roam_params->num_ssid_allowed_list; + if (filter->num_of_ssid > WLAN_SCAN_FILTER_NUM_SSID) + filter->num_of_ssid = WLAN_SCAN_FILTER_NUM_SSID; + for (i = 0; i < filter->num_of_ssid; i++) + csr_copy_ssids(&filter->ssid_list[i], + &roam_params->ssid_allowed_list[i]); +} + +static void csr_copy_ssids_from_profile(tCsrSSIDs *ssid_list, + struct scan_filter *filter) +{ + uint8_t i; + + filter->num_of_ssid = ssid_list->numOfSSIDs; + if (filter->num_of_ssid > WLAN_SCAN_FILTER_NUM_SSID) + filter->num_of_ssid = WLAN_SCAN_FILTER_NUM_SSID; + for (i = 0; i < filter->num_of_ssid; i++) + csr_copy_ssids(&filter->ssid_list[i], + &ssid_list->SSIDList[i].SSID); +} + +#ifdef WLAN_ADAPTIVE_11R +static void +csr_update_adaptive_11r_scan_filter(struct mac_context *mac_ctx, + struct scan_filter *filter) +{ + filter->enable_adaptive_11r = + mac_ctx->mlme_cfg->lfr.enable_adaptive_11r; +} +#else +static inline void +csr_update_adaptive_11r_scan_filter(struct mac_context *mac_ctx, + struct scan_filter *filter) +{ + filter->enable_adaptive_11r = false; +} +#endif + +void csr_update_connect_n_roam_cmn_filter(struct mac_context *mac_ctx, + struct scan_filter *filter, + enum QDF_OPMODE opmode) +{ + enum policy_mgr_con_mode pm_mode; + uint32_t num_entries = 0, pcl_freq_list[NUM_CHANNELS] = {0}; + QDF_STATUS status; + + /* enable bss scoring for only STA mode */ + if (opmode == QDF_STA_MODE) + filter->bss_scoring_required = true; + + csr_update_adaptive_11r_scan_filter(mac_ctx, filter); + + if (filter->num_of_bssid) + return; + + if (policy_mgr_map_concurrency_mode(&opmode, &pm_mode)) { + status = policy_mgr_get_pcl(mac_ctx->psoc, pm_mode, + pcl_freq_list, &num_entries, + filter->pcl_weight_list, + NUM_CHANNELS); + if (QDF_IS_STATUS_ERROR(status)) + return; + qdf_mem_copy(filter->pcl_freq_list, pcl_freq_list, + num_entries * sizeof(pcl_freq_list[0])); + filter->num_of_pcl_channels = num_entries; + } +} + +#ifdef FEATURE_WLAN_WAPI +/** + * csr_update_phy_mode: Updates phy mode for wapi + * @profile: Source profile + * @phy_mode: phy_mode to be modified + * + * Return: None + */ +static void csr_update_phy_mode(struct csr_roam_profile *profile, + uint32_t *phy_mode) +{ + /* + * check if user asked for WAPI with 11n or auto mode, in that + * case modify the phymode to 11g + */ + if (csr_is_profile_wapi(profile)) { + if (*phy_mode & eCSR_DOT11_MODE_11n) + *phy_mode &= ~eCSR_DOT11_MODE_11n; + if (*phy_mode & eCSR_DOT11_MODE_AUTO) + *phy_mode &= ~eCSR_DOT11_MODE_AUTO; + if (!*phy_mode) + *phy_mode = eCSR_DOT11_MODE_11g; + } +} +#else +static inline +void csr_update_phy_mode(struct csr_roam_profile *profile, uint32_t *phy_mode) +{} +#endif + +/** + * csr_convert_dotllmod_phymode: Convert eCsrPhyMode to wlan_phymode + * @dotllmode: phy mode + * + * Return: returns enum wlan_phymode + */ +static enum wlan_phymode csr_convert_dotllmod_phymode(eCsrPhyMode dotllmode) +{ + enum wlan_phymode con_phy_mode; + + switch (dotllmode) { + case eCSR_DOT11_MODE_abg: + con_phy_mode = WLAN_PHYMODE_AUTO; + break; + case eCSR_DOT11_MODE_11a: + con_phy_mode = WLAN_PHYMODE_11A; + break; + case eCSR_DOT11_MODE_11b: + con_phy_mode = WLAN_PHYMODE_11B; + break; + case eCSR_DOT11_MODE_11g: + con_phy_mode = WLAN_PHYMODE_11G; + break; + case eCSR_DOT11_MODE_11n: + con_phy_mode = WLAN_PHYMODE_11NA_HT20; + break; + case eCSR_DOT11_MODE_11g_ONLY: + con_phy_mode = WLAN_PHYMODE_11G; + break; + case eCSR_DOT11_MODE_11n_ONLY: + con_phy_mode = WLAN_PHYMODE_11NA_HT20; + break; + case eCSR_DOT11_MODE_11b_ONLY: + con_phy_mode = WLAN_PHYMODE_11B; + break; + case eCSR_DOT11_MODE_11ac: + con_phy_mode = WLAN_PHYMODE_11AC_VHT160; + break; + case eCSR_DOT11_MODE_11ac_ONLY: + con_phy_mode = WLAN_PHYMODE_11AC_VHT160; + break; + case eCSR_DOT11_MODE_AUTO: + con_phy_mode = WLAN_PHYMODE_AUTO; + break; + case eCSR_DOT11_MODE_11ax: + con_phy_mode = WLAN_PHYMODE_11AXA_HE160; + break; + case eCSR_DOT11_MODE_11ax_ONLY: + con_phy_mode = WLAN_PHYMODE_11AXA_HE160; + break; + default: + con_phy_mode = WLAN_PHYMODE_AUTO; + break; + } + + return con_phy_mode; +} + +#ifdef WLAN_FEATURE_11W + +/** + * csr_update_pmf_cap_from_profile: Updates PMF cap + * @profile: Source profile + * @filter: scan filter + * + * Return: None + */ +static void csr_update_pmf_cap_from_profile(struct csr_roam_profile *profile, + struct scan_filter *filter) +{ + if (profile->MFPCapable || profile->MFPEnabled) + filter->pmf_cap = WLAN_PMF_CAPABLE; + if (profile->MFPRequired) + filter->pmf_cap = WLAN_PMF_REQUIRED; +} +#else +static inline +void csr_update_pmf_cap_from_profile(struct csr_roam_profile *profile, + struct scan_filter *filter) +{} +#endif + +QDF_STATUS +csr_roam_get_scan_filter_from_profile(struct mac_context *mac_ctx, + struct csr_roam_profile *profile, + struct scan_filter *filter, + bool is_roam) +{ + tCsrChannelInfo *ch_info; + struct roam_ext_params *roam_params; + uint8_t i; + uint32_t phy_mode; + + if (!filter || !profile) { + sme_err("filter or profile is NULL"); + return QDF_STATUS_E_FAILURE; + } + roam_params = &mac_ctx->roam.configParam.roam_params; + + qdf_mem_zero(filter, sizeof(*filter)); + if (profile->BSSIDs.numOfBSSIDs) { + filter->num_of_bssid = profile->BSSIDs.numOfBSSIDs; + if (filter->num_of_bssid > WLAN_SCAN_FILTER_NUM_BSSID) + filter->num_of_bssid = WLAN_SCAN_FILTER_NUM_BSSID; + for (i = 0; i < filter->num_of_bssid; i++) + qdf_mem_copy(filter->bssid_list[i].bytes, + profile->BSSIDs.bssid[i].bytes, + QDF_MAC_ADDR_SIZE); + } + + if (profile->SSIDs.numOfSSIDs) { + if (is_roam && roam_params->num_ssid_allowed_list) + csr_copy_ssids_from_roam_params(roam_params, filter); + else + csr_copy_ssids_from_profile(&profile->SSIDs, filter); + } + + ch_info = &profile->ChannelInfo; + if (ch_info->numOfChannels && ch_info->freq_list && + ch_info->freq_list[0]) { + filter->num_of_channels = 0; + for (i = 0; i < ch_info->numOfChannels; i++) { + if (filter->num_of_channels >= NUM_CHANNELS) { + sme_err("max allowed channel(%d) reached", + filter->num_of_channels); + break; + } + if (csr_roam_is_channel_valid(mac_ctx, + ch_info->freq_list[i])) { + filter->chan_freq_list[filter->num_of_channels] = + ch_info->freq_list[i]; + filter->num_of_channels++; + } else { + sme_debug("freq (%d) is invalid", + ch_info->freq_list[i]); + } + } + } + + if (profile->force_rsne_override) { + sme_debug("force_rsne_override set auth type and enctype to any and ignore pmf cap"); + filter->num_of_auth = 1; + filter->auth_type[0] = WLAN_AUTH_TYPE_ANY; + filter->num_of_enc_type = 1; + filter->enc_type[0] = WLAN_ENCRYPT_TYPE_ANY; + filter->num_of_mc_enc_type = 1; + filter->mc_enc_type[0] = WLAN_ENCRYPT_TYPE_ANY; + + filter->ignore_pmf_cap = true; + } else { + filter->num_of_auth = + profile->AuthType.numEntries; + if (filter->num_of_auth > WLAN_NUM_OF_SUPPORT_AUTH_TYPE) + filter->num_of_auth = WLAN_NUM_OF_SUPPORT_AUTH_TYPE; + for (i = 0; i < filter->num_of_auth; i++) + filter->auth_type[i] = + csr_covert_auth_type_new( + profile->AuthType.authType[i]); + filter->num_of_enc_type = + profile->EncryptionType.numEntries; + if (filter->num_of_enc_type > WLAN_NUM_OF_ENCRYPT_TYPE) + filter->num_of_enc_type = WLAN_NUM_OF_ENCRYPT_TYPE; + for (i = 0; i < filter->num_of_enc_type; i++) + filter->enc_type[i] = + csr_covert_enc_type_new( + profile->EncryptionType.encryptionType[i]); + filter->num_of_mc_enc_type = + profile->mcEncryptionType.numEntries; + if (filter->num_of_mc_enc_type > WLAN_NUM_OF_ENCRYPT_TYPE) + filter->num_of_mc_enc_type = WLAN_NUM_OF_ENCRYPT_TYPE; + for (i = 0; i < filter->num_of_mc_enc_type; i++) + filter->mc_enc_type[i] = + csr_covert_enc_type_new( + profile->mcEncryptionType.encryptionType[i]); + } + + if (profile->BSSType == eCSR_BSS_TYPE_INFRASTRUCTURE) + filter->bss_type = WLAN_TYPE_BSS; + else if (profile->BSSType == eCSR_BSS_TYPE_IBSS || + profile->BSSType == eCSR_BSS_TYPE_START_IBSS) + filter->bss_type = WLAN_TYPE_IBSS; + else + filter->bss_type = WLAN_TYPE_ANY; + + phy_mode = profile->phyMode; + csr_update_phy_mode(profile, &phy_mode); + + filter->dot11_mode = csr_convert_dotllmod_phymode(phy_mode); + if (profile->bWPSAssociation || profile->bOSENAssociation) + filter->ignore_auth_enc_type = true; + if (profile->countryCode[0]) + /* + * This causes the matching function to use countryCode as one + * of the criteria. + */ + qdf_mem_copy(filter->country, profile->countryCode, + CFG_COUNTRY_CODE_LEN); + + filter->mobility_domain = profile->mdid.mobility_domain; + qdf_mem_copy(filter->bssid_hint.bytes, profile->bssid_hint.bytes, + QDF_MAC_ADDR_SIZE); + + csr_update_pmf_cap_from_profile(profile, filter); + + csr_update_fils_scan_filter(filter, profile); + + csr_update_connect_n_roam_cmn_filter(mac_ctx, filter, + profile->csrPersona); + + return QDF_STATUS_SUCCESS; +} + +static +bool csr_roam_issue_wm_status_change(struct mac_context *mac, uint32_t sessionId, + enum csr_roam_wmstatus_changetypes Type, + tSirSmeRsp *pSmeRsp) +{ + bool fCommandQueued = false; + tSmeCmd *pCommand; + struct qdf_mac_addr peer_mac; + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac, sessionId); + if (!session) + return false; + + do { + /* Validate the type is ok... */ + if ((eCsrDisassociated != Type) + && (eCsrDeauthenticated != Type)) + break; + pCommand = csr_get_command_buffer(mac); + if (!pCommand) { + sme_err(" fail to get command buffer"); + break; + } + /* Change the substate in case it is waiting for key */ + if (CSR_IS_WAIT_FOR_KEY(mac, sessionId)) { + csr_roam_stop_wait_for_key_timer(mac); + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_NONE, + sessionId); + } + pCommand->command = eSmeCommandWmStatusChange; + pCommand->vdev_id = (uint8_t) sessionId; + pCommand->u.wmStatusChangeCmd.Type = Type; + if (eCsrDisassociated == Type) { + qdf_mem_copy(&pCommand->u.wmStatusChangeCmd.u. + DisassocIndMsg, pSmeRsp, + sizeof(pCommand->u.wmStatusChangeCmd.u. + DisassocIndMsg)); + qdf_mem_copy(&peer_mac, &pCommand->u.wmStatusChangeCmd. + u.DisassocIndMsg.peer_macaddr, + QDF_MAC_ADDR_SIZE); + + } else { + qdf_mem_copy(&pCommand->u.wmStatusChangeCmd.u. + DeauthIndMsg, pSmeRsp, + sizeof(pCommand->u.wmStatusChangeCmd.u. + DeauthIndMsg)); + qdf_mem_copy(&peer_mac, &pCommand->u.wmStatusChangeCmd. + u.DeauthIndMsg.peer_macaddr, + QDF_MAC_ADDR_SIZE); + } + + if (CSR_IS_INFRA_AP(&session->connectedProfile)) + csr_roam_issue_disconnect_stats(mac, sessionId, + peer_mac); + + if (QDF_IS_STATUS_SUCCESS + (csr_queue_sme_command(mac, pCommand, false))) + fCommandQueued = true; + else + sme_err("fail to send message"); + + /* AP has issued Dissac/Deauth, Set the operating mode + * value to configured value + */ + csr_set_default_dot11_mode(mac); + } while (0); + return fCommandQueued; +} + +static void csr_update_snr(struct mac_context *mac, void *pMsg) +{ + tAniGetSnrReq *pGetSnrReq = (tAniGetSnrReq *) pMsg; + + if (pGetSnrReq) { + if (QDF_STATUS_SUCCESS != wma_get_snr(pGetSnrReq)) { + sme_err("Error in wma_get_snr"); + return; + } + + } else + sme_err("pGetSnrReq is NULL"); +} + +static QDF_STATUS csr_send_reset_ap_caps_changed(struct mac_context *mac, + struct qdf_mac_addr *bssId) +{ + tpSirResetAPCapsChange pMsg; + uint16_t len; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* Create the message and send to lim */ + len = sizeof(tSirResetAPCapsChange); + pMsg = qdf_mem_malloc(len); + if (!pMsg) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + + if (QDF_IS_STATUS_SUCCESS(status)) { + pMsg->messageType = eWNI_SME_RESET_AP_CAPS_CHANGED; + pMsg->length = len; + qdf_copy_macaddr(&pMsg->bssId, bssId); + sme_debug( + "CSR reset caps change for Bssid= " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pMsg->bssId.bytes)); + status = umac_send_mb_message_to_mac(pMsg); + } else { + sme_err("Memory allocation failed"); + } + return status; +} + +#ifdef FEATURE_WLAN_ESE +/** + * csr_convert_ese_akm_to_ani() - Convert enum csr_akm_type ESE akm value to + * equivalent enum ani_akm_type value + * @akm_type: value of type enum ani_akm_type + * + * Return: ani_akm_type value corresponding + */ +static enum ani_akm_type csr_convert_ese_akm_to_ani(enum csr_akm_type akm_type) +{ + switch (akm_type) { + case eCSR_AUTH_TYPE_CCKM_RSN: + return ANI_AKM_TYPE_CCKM; + default: + return ANI_AKM_TYPE_UNKNOWN; + } +} +#else +static inline enum +ani_akm_type csr_convert_ese_akm_to_ani(enum csr_akm_type akm_type) +{ + return ANI_AKM_TYPE_UNKNOWN; +} +#endif + +#ifdef WLAN_FEATURE_SAE +/** + * csr_convert_sae_akm_to_ani() - Convert enum csr_akm_type SAE akm value to + * equivalent enum ani_akm_type value + * @akm_type: value of type enum ani_akm_type + * + * Return: ani_akm_type value corresponding + */ +static enum ani_akm_type csr_convert_sae_akm_to_ani(enum csr_akm_type akm_type) +{ + switch (akm_type) { + case eCSR_AUTH_TYPE_SAE: + return ANI_AKM_TYPE_SAE; + case eCSR_AUTH_TYPE_FT_SAE: + return ANI_AKM_TYPE_FT_SAE; + default: + return ANI_AKM_TYPE_UNKNOWN; + } +} +#else +static inline +enum ani_akm_type csr_convert_sae_akm_to_ani(enum csr_akm_type akm_type) +{ + return ANI_AKM_TYPE_UNKNOWN; +} +#endif + +/** + * csr_convert_csr_to_ani_akm_type() - Convert enum csr_akm_type value to + * equivalent enum ani_akm_type value + * @akm_type: value of type enum ani_akm_type + * + * Return: ani_akm_type value corresponding + */ +static enum ani_akm_type +csr_convert_csr_to_ani_akm_type(enum csr_akm_type akm_type) +{ + enum ani_akm_type ani_akm; + + switch (akm_type) { + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + case eCSR_AUTH_TYPE_NONE: + return ANI_AKM_TYPE_NONE; + case eCSR_AUTH_TYPE_WPA: + return ANI_AKM_TYPE_WPA; + case eCSR_AUTH_TYPE_WPA_PSK: + return ANI_AKM_TYPE_WPA_PSK; + case eCSR_AUTH_TYPE_RSN: + return ANI_AKM_TYPE_RSN; + case eCSR_AUTH_TYPE_RSN_PSK: + return ANI_AKM_TYPE_RSN_PSK; + case eCSR_AUTH_TYPE_FT_RSN: + return ANI_AKM_TYPE_FT_RSN; + case eCSR_AUTH_TYPE_FT_RSN_PSK: + return ANI_AKM_TYPE_FT_RSN_PSK; + case eCSR_AUTH_TYPE_RSN_PSK_SHA256: + return ANI_AKM_TYPE_RSN_PSK_SHA256; + case eCSR_AUTH_TYPE_RSN_8021X_SHA256: + return ANI_AKM_TYPE_RSN_8021X_SHA256; + case eCSR_AUTH_TYPE_FILS_SHA256: + return ANI_AKM_TYPE_FILS_SHA256; + case eCSR_AUTH_TYPE_FILS_SHA384: + return ANI_AKM_TYPE_FILS_SHA384; + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + return ANI_AKM_TYPE_FT_FILS_SHA256; + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return ANI_AKM_TYPE_FT_FILS_SHA384; + case eCSR_AUTH_TYPE_DPP_RSN: + return ANI_AKM_TYPE_DPP_RSN; + case eCSR_AUTH_TYPE_OWE: + return ANI_AKM_TYPE_OWE; + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA256: + return ANI_AKM_TYPE_SUITEB_EAP_SHA256; + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA384: + return ANI_AKM_TYPE_SUITEB_EAP_SHA384; + case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384: + return ANI_AKM_TYPE_FT_SUITEB_EAP_SHA384; + case eCSR_AUTH_TYPE_OSEN: + return ANI_AKM_TYPE_OSEN; + default: + ani_akm = ANI_AKM_TYPE_UNKNOWN; + } + + if (ani_akm == ANI_AKM_TYPE_UNKNOWN) + ani_akm = csr_convert_sae_akm_to_ani(akm_type); + + if (ani_akm == ANI_AKM_TYPE_UNKNOWN) + ani_akm = csr_convert_ese_akm_to_ani(akm_type); + + return ani_akm; +} + +/** + * csr_translate_akm_type() - Convert ani_akm_type value to equivalent + * enum csr_akm_type + * @akm_type: value of type ani_akm_type + * + * Return: enum csr_akm_type value + */ +static enum csr_akm_type csr_translate_akm_type(enum ani_akm_type akm_type) +{ + enum csr_akm_type csr_akm_type; + + switch (akm_type) + { + case ANI_AKM_TYPE_NONE: + csr_akm_type = eCSR_AUTH_TYPE_NONE; + break; +#ifdef WLAN_FEATURE_SAE + case ANI_AKM_TYPE_SAE: + csr_akm_type = eCSR_AUTH_TYPE_SAE; + break; +#endif + case ANI_AKM_TYPE_WPA: + csr_akm_type = eCSR_AUTH_TYPE_WPA; + break; + case ANI_AKM_TYPE_WPA_PSK: + csr_akm_type = eCSR_AUTH_TYPE_WPA_PSK; + break; + case ANI_AKM_TYPE_RSN: + csr_akm_type = eCSR_AUTH_TYPE_RSN; + break; + case ANI_AKM_TYPE_RSN_PSK: + csr_akm_type = eCSR_AUTH_TYPE_RSN_PSK; + break; + case ANI_AKM_TYPE_FT_RSN: + csr_akm_type = eCSR_AUTH_TYPE_FT_RSN; + break; + case ANI_AKM_TYPE_FT_RSN_PSK: + csr_akm_type = eCSR_AUTH_TYPE_FT_RSN_PSK; + break; +#ifdef FEATURE_WLAN_ESE + case ANI_AKM_TYPE_CCKM: + csr_akm_type = eCSR_AUTH_TYPE_CCKM_RSN; + break; +#endif + case ANI_AKM_TYPE_RSN_PSK_SHA256: + csr_akm_type = eCSR_AUTH_TYPE_RSN_PSK_SHA256; + break; + case ANI_AKM_TYPE_RSN_8021X_SHA256: + csr_akm_type = eCSR_AUTH_TYPE_RSN_8021X_SHA256; + break; + case ANI_AKM_TYPE_FILS_SHA256: + csr_akm_type = eCSR_AUTH_TYPE_FILS_SHA256; + break; + case ANI_AKM_TYPE_FILS_SHA384: + csr_akm_type = eCSR_AUTH_TYPE_FILS_SHA384; + break; + case ANI_AKM_TYPE_FT_FILS_SHA256: + csr_akm_type = eCSR_AUTH_TYPE_FT_FILS_SHA256; + break; + case ANI_AKM_TYPE_FT_FILS_SHA384: + csr_akm_type = eCSR_AUTH_TYPE_FT_FILS_SHA384; + break; + case ANI_AKM_TYPE_DPP_RSN: + csr_akm_type = eCSR_AUTH_TYPE_DPP_RSN; + break; + case ANI_AKM_TYPE_OWE: + csr_akm_type = eCSR_AUTH_TYPE_OWE; + break; + case ANI_AKM_TYPE_SUITEB_EAP_SHA256: + csr_akm_type = eCSR_AUTH_TYPE_SUITEB_EAP_SHA256; + break; + case ANI_AKM_TYPE_SUITEB_EAP_SHA384: + csr_akm_type = eCSR_AUTH_TYPE_SUITEB_EAP_SHA384; + break; + case ANI_AKM_TYPE_OSEN: + csr_akm_type = eCSR_AUTH_TYPE_OSEN; + break; + default: + csr_akm_type = eCSR_AUTH_TYPE_UNKNOWN; + } + + return csr_akm_type; +} + +static bool csr_is_sae_akm_present(tDot11fIERSN * const rsn_ie) +{ + uint16_t i; + + if (rsn_ie->akm_suite_cnt > 6) { + sme_debug("Invalid akm_suite_cnt in Rx RSN IE"); + return false; + } + + for (i = 0; i < rsn_ie->akm_suite_cnt; i++) { + if (LE_READ_4(rsn_ie->akm_suite[i]) == RSN_AUTH_KEY_MGMT_SAE) { + sme_debug("SAE AKM present"); + return true; + } + } + return false; +} + +static bool csr_is_sae_peer_allowed(struct mac_context *mac_ctx, + struct assoc_ind *assoc_ind, + struct csr_roam_session *session, + tSirMacAddr peer_mac_addr, + tDot11fIERSN *rsn_ie, + enum mac_status_code *mac_status_code) +{ + bool is_allowed = false; + + /* Allow the peer if it's SAE authenticated */ + if (assoc_ind->is_sae_authenticated) + return true; + + /* Allow the peer with valid PMKID */ + if (!rsn_ie->pmkid_count) { + *mac_status_code = eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS; + sme_debug("No PMKID present in RSNIE; Tried to use SAE AKM after non-SAE authentication"); + } else if (csr_is_pmkid_found_for_peer(mac_ctx, session, peer_mac_addr, + &rsn_ie->pmkid[0][0], + rsn_ie->pmkid_count)) { + sme_debug("Valid PMKID found for SAE peer"); + is_allowed = true; + } else { + *mac_status_code = eSIR_MAC_INVALID_PMKID; + sme_debug("No valid PMKID found for SAE peer"); + } + + return is_allowed; +} + +static QDF_STATUS +csr_send_assoc_ind_to_upper_layer_cnf_msg(struct mac_context *mac, + struct assoc_ind *ind, + QDF_STATUS status, + uint8_t vdev_id) +{ + struct scheduler_msg msg = {0}; + tSirSmeAssocIndToUpperLayerCnf *cnf; + + cnf = qdf_mem_malloc(sizeof(*cnf)); + if (!cnf) + return QDF_STATUS_E_NOMEM; + + cnf->messageType = eWNI_SME_UPPER_LAYER_ASSOC_CNF; + cnf->length = sizeof(*cnf); + cnf->sessionId = vdev_id; + + if (QDF_IS_STATUS_SUCCESS(status)) + cnf->status_code = eSIR_SME_SUCCESS; + else + cnf->status_code = eSIR_SME_ASSOC_REFUSED; + qdf_mem_copy(&cnf->bssId, &ind->bssId, sizeof(cnf->bssId)); + qdf_mem_copy(&cnf->peerMacAddr, &ind->peerMacAddr, + sizeof(cnf->peerMacAddr)); + cnf->aid = ind->staId; + cnf->wmmEnabledSta = ind->wmmEnabledSta; + cnf->rsnIE = ind->rsnIE; +#ifdef FEATURE_WLAN_WAPI + cnf->wapiIE = ind->wapiIE; +#endif + cnf->addIE = ind->addIE; + cnf->reassocReq = ind->reassocReq; + cnf->timingMeasCap = ind->timingMeasCap; + cnf->chan_info = ind->chan_info; + cnf->ampdu = ind->ampdu; + cnf->sgi_enable = ind->sgi_enable; + cnf->tx_stbc = ind->tx_stbc; + cnf->ch_width = ind->ch_width; + cnf->mode = ind->mode; + cnf->rx_stbc = ind->rx_stbc; + cnf->max_supp_idx = ind->max_supp_idx; + cnf->max_ext_idx = ind->max_ext_idx; + cnf->max_mcs_idx = ind->max_mcs_idx; + cnf->rx_mcs_map = ind->rx_mcs_map; + cnf->tx_mcs_map = ind->tx_mcs_map; + cnf->ecsa_capable = ind->ecsa_capable; + if (ind->HTCaps.present) + cnf->ht_caps = ind->HTCaps; + if (ind->VHTCaps.present) + cnf->vht_caps = ind->VHTCaps; + cnf->capability_info = ind->capability_info; + cnf->he_caps_present = ind->he_caps_present; + if (ind->assocReqPtr) { + if (ind->assocReqLength < MAX_ASSOC_REQ_IE_LEN) { + cnf->ies = qdf_mem_malloc(ind->assocReqLength); + if (!cnf->ies) { + qdf_mem_free(cnf); + return QDF_STATUS_E_NOMEM; + } + cnf->ies_len = ind->assocReqLength; + qdf_mem_copy(cnf->ies, ind->assocReqPtr, + cnf->ies_len); + } else { + sme_err("Assoc Ie length is too long"); + } + } + + msg.type = eWNI_SME_UPPER_LAYER_ASSOC_CNF; + msg.bodyptr = cnf; + sys_process_mmh_msg(mac, &msg); + + return QDF_STATUS_SUCCESS; +} + +static void +csr_roam_chk_lnk_assoc_ind_upper_layer( + struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + uint32_t session_id = WLAN_UMAC_VDEV_ID_MAX; + struct assoc_ind *assoc_ind; + QDF_STATUS status; + + assoc_ind = (struct assoc_ind *)msg_ptr; + status = csr_roam_get_session_id_from_bssid( + mac_ctx, (struct qdf_mac_addr *)assoc_ind->bssId, + &session_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_debug("Couldn't find session_id for given BSSID"); + return; + } + csr_send_assoc_ind_to_upper_layer_cnf_msg( + mac_ctx, assoc_ind, status, session_id); + /*in the association response tx compete case, + *memory for assoc_ind->assocReqPtr will be malloced + *in the lim_assoc_rsp_tx_complete -> lim_fill_sme_assoc_ind_params + *and then assoc_ind will pass here, so after using it + *in the csr_send_assoc_ind_to_upper_layer_cnf_msg and + *then free the memroy here. + */ + if (assoc_ind->assocReqLength != 0 && assoc_ind->assocReqPtr) + qdf_mem_free(assoc_ind->assocReqPtr); +} + +static void +csr_roam_chk_lnk_assoc_ind(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + struct csr_roam_session *session; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct csr_roam_info *roam_info; + struct assoc_ind *pAssocInd; + enum mac_status_code mac_status_code = eSIR_MAC_SUCCESS_STATUS; + enum csr_akm_type csr_akm_type; + + sme_debug("Receive WNI_SME_ASSOC_IND from SME"); + pAssocInd = (struct assoc_ind *) msg_ptr; + status = csr_roam_get_session_id_from_bssid(mac_ctx, + (struct qdf_mac_addr *) pAssocInd->bssId, + &sessionId); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_debug("Couldn't find session_id for given BSSID"); + return; + } + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (!session) { + sme_err("session %d not found", sessionId); + return; + } + csr_akm_type = csr_translate_akm_type(pAssocInd->akm_type); + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + /* Required for indicating the frames to upper layer */ + roam_info->assocReqLength = pAssocInd->assocReqLength; + roam_info->assocReqPtr = pAssocInd->assocReqPtr; + roam_info->beaconPtr = pAssocInd->beaconPtr; + roam_info->beaconLength = pAssocInd->beaconLength; + roam_info->status_code = eSIR_SME_SUCCESS; + roam_info->u.pConnectedProfile = &session->connectedProfile; + roam_info->staId = (uint8_t)pAssocInd->staId; + roam_info->rsnIELen = (uint8_t)pAssocInd->rsnIE.length; + roam_info->prsnIE = pAssocInd->rsnIE.rsnIEdata; +#ifdef FEATURE_WLAN_WAPI + roam_info->wapiIELen = (uint8_t)pAssocInd->wapiIE.length; + roam_info->pwapiIE = pAssocInd->wapiIE.wapiIEdata; +#endif + roam_info->addIELen = (uint8_t)pAssocInd->addIE.length; + roam_info->paddIE = pAssocInd->addIE.addIEdata; + qdf_mem_copy(roam_info->peerMac.bytes, + pAssocInd->peerMacAddr, + sizeof(tSirMacAddr)); + qdf_mem_copy(roam_info->bssid.bytes, + pAssocInd->bssId, + sizeof(struct qdf_mac_addr)); + roam_info->wmmEnabledSta = pAssocInd->wmmEnabledSta; + roam_info->timingMeasCap = pAssocInd->timingMeasCap; + roam_info->ecsa_capable = pAssocInd->ecsa_capable; + qdf_mem_copy(&roam_info->chan_info, + &pAssocInd->chan_info, + sizeof(struct oem_channel_info)); + + if (pAssocInd->HTCaps.present) + qdf_mem_copy(&roam_info->ht_caps, + &pAssocInd->HTCaps, + sizeof(tDot11fIEHTCaps)); + if (pAssocInd->VHTCaps.present) + qdf_mem_copy(&roam_info->vht_caps, + &pAssocInd->VHTCaps, + sizeof(tDot11fIEVHTCaps)); + roam_info->capability_info = pAssocInd->capability_info; + roam_info->he_caps_present = pAssocInd->he_caps_present; + + if (CSR_IS_INFRA_AP(roam_info->u.pConnectedProfile)) { + if (session->pCurRoamProfile && + CSR_IS_ENC_TYPE_STATIC( + session->pCurRoamProfile->negotiatedUCEncryptionType)) { + /* NO keys... these key parameters don't matter. */ + csr_issue_set_context_req_helper(mac_ctx, + session->pCurRoamProfile, sessionId, + &roam_info->peerMac.bytes, false, true, + eSIR_TX_RX, 0, 0, NULL); + roam_info->fAuthRequired = false; + } else { + roam_info->fAuthRequired = true; + } + if (csr_akm_type == eCSR_AUTH_TYPE_OWE) { + roam_info->owe_pending_assoc_ind = qdf_mem_malloc( + sizeof(*pAssocInd)); + if (roam_info->owe_pending_assoc_ind) + qdf_mem_copy(roam_info->owe_pending_assoc_ind, + pAssocInd, sizeof(*pAssocInd)); + } + status = csr_roam_call_callback(mac_ctx, sessionId, + roam_info, 0, eCSR_ROAM_INFRA_IND, + eCSR_ROAM_RESULT_INFRA_ASSOCIATION_IND); + if (!QDF_IS_STATUS_SUCCESS(status)) { + /* Refused due to Mac filtering */ + roam_info->status_code = eSIR_SME_ASSOC_REFUSED; + } else if (pAssocInd->rsnIE.length && WLAN_ELEMID_RSN == + pAssocInd->rsnIE.rsnIEdata[0]) { + tDot11fIERSN rsn_ie = {0}; + + if (dot11f_unpack_ie_rsn(mac_ctx, + pAssocInd->rsnIE.rsnIEdata + 2, + pAssocInd->rsnIE.length - 2, + &rsn_ie, false) + != DOT11F_PARSE_SUCCESS || + (csr_is_sae_akm_present(&rsn_ie) && + !csr_is_sae_peer_allowed(mac_ctx, pAssocInd, + session, + pAssocInd->peerMacAddr, + &rsn_ie, + &mac_status_code))) { + status = QDF_STATUS_E_INVAL; + roam_info->status_code = + eSIR_SME_ASSOC_REFUSED; + sme_debug("SAE peer not allowed: Status: %d", + mac_status_code); + } + } + } + + if (csr_akm_type != eCSR_AUTH_TYPE_OWE) { + if (CSR_IS_INFRA_AP(roam_info->u.pConnectedProfile) && + roam_info->status_code != eSIR_SME_ASSOC_REFUSED) + pAssocInd->need_assoc_rsp_tx_cb = true; + /* Send Association completion message to PE */ + status = csr_send_assoc_cnf_msg(mac_ctx, pAssocInd, status, + mac_status_code); + } + + qdf_mem_free(roam_info); +} + +static void +csr_roam_chk_lnk_disassoc_ind(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + struct csr_roam_session *session; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct disassoc_ind *pDisassocInd; + tSmeCmd *cmd; + + cmd = qdf_mem_malloc(sizeof(*cmd)); + if (!cmd) + return; + + /* + * Check if AP dis-associated us because of MIC failure. If so, + * then we need to take action immediately and not wait till the + * the WmStatusChange requests is pushed and processed + */ + pDisassocInd = (struct disassoc_ind *)msg_ptr; + status = csr_roam_get_session_id_from_bssid(mac_ctx, + &pDisassocInd->bssid, &sessionId); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Session Id not found for BSSID "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pDisassocInd->bssid.bytes)); + qdf_mem_free(cmd); + return; + } + + if (csr_is_deauth_disassoc_already_active(mac_ctx, sessionId, + pDisassocInd->peer_macaddr)) { + qdf_mem_free(cmd); + return; + } + + sme_nofl_info("disassoc from peer " QDF_MAC_ADDR_FMT + "reason: %d status: %d vid %d", + QDF_MAC_ADDR_REF(pDisassocInd->peer_macaddr.bytes), + pDisassocInd->reasonCode, + pDisassocInd->status_code, sessionId); + /* + * If we are in neighbor preauth done state then on receiving + * disassoc or deauth we dont roam instead we just disassoc + * from current ap and then go to disconnected state + * This happens for ESE and 11r FT connections ONLY. + */ + if (csr_roam_is11r_assoc(mac_ctx, sessionId) && + (csr_neighbor_roam_state_preauth_done(mac_ctx, sessionId))) + csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + mac_ctx, sessionId); +#ifdef FEATURE_WLAN_ESE + if (csr_roam_is_ese_assoc(mac_ctx, sessionId) && + (csr_neighbor_roam_state_preauth_done(mac_ctx, sessionId))) + csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + mac_ctx, sessionId); +#endif + if (csr_roam_is_fast_roam_enabled(mac_ctx, sessionId) && + (csr_neighbor_roam_state_preauth_done(mac_ctx, sessionId))) + csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + mac_ctx, sessionId); + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (!session) { + sme_err("session: %d not found", sessionId); + qdf_mem_free(cmd); + return; + } + + csr_update_scan_entry_associnfo(mac_ctx, + session, SCAN_ENTRY_CON_STATE_NONE); + + /* Update the disconnect stats */ + session->disconnect_stats.disconnection_cnt++; + session->disconnect_stats.disassoc_by_peer++; + + if (csr_is_conn_state_infra(mac_ctx, sessionId)) + session->connectState = eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED; +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_qos_csr_event_ind(mac_ctx, (uint8_t) sessionId, + SME_QOS_CSR_DISCONNECT_IND, NULL); +#endif + csr_roam_link_down(mac_ctx, sessionId); + csr_roam_issue_wm_status_change(mac_ctx, sessionId, + eCsrDisassociated, msg_ptr); + if (CSR_IS_INFRA_AP(&session->connectedProfile)) { + /* + * STA/P2P client got disassociated so remove any pending + * deauth commands in sme pending list + */ + cmd->command = eSmeCommandRoam; + cmd->vdev_id = (uint8_t) sessionId; + cmd->u.roamCmd.roamReason = eCsrForcedDeauthSta; + qdf_mem_copy(cmd->u.roamCmd.peerMac, + pDisassocInd->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE); + csr_roam_remove_duplicate_command(mac_ctx, sessionId, cmd, + eCsrForcedDeauthSta); + } + qdf_mem_free(cmd); +} + +static void +csr_roam_chk_lnk_deauth_ind(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + struct csr_roam_session *session; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct deauth_ind *pDeauthInd; + + pDeauthInd = (struct deauth_ind *)msg_ptr; + sme_debug("DEAUTH Indication from MAC for vdev_id %d bssid "QDF_MAC_ADDR_FMT, + pDeauthInd->vdev_id, + QDF_MAC_ADDR_REF(pDeauthInd->bssid.bytes)); + + status = csr_roam_get_session_id_from_bssid(mac_ctx, + &pDeauthInd->bssid, + &sessionId); + if (!QDF_IS_STATUS_SUCCESS(status)) + return; + + if (csr_is_deauth_disassoc_already_active(mac_ctx, sessionId, + pDeauthInd->peer_macaddr)) + return; + /* If we are in neighbor preauth done state then on receiving + * disassoc or deauth we dont roam instead we just disassoc + * from current ap and then go to disconnected state + * This happens for ESE and 11r FT connections ONLY. + */ + if (csr_roam_is11r_assoc(mac_ctx, sessionId) && + (csr_neighbor_roam_state_preauth_done(mac_ctx, sessionId))) + csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + mac_ctx, sessionId); +#ifdef FEATURE_WLAN_ESE + if (csr_roam_is_ese_assoc(mac_ctx, sessionId) && + (csr_neighbor_roam_state_preauth_done(mac_ctx, sessionId))) + csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + mac_ctx, sessionId); +#endif + if (csr_roam_is_fast_roam_enabled(mac_ctx, sessionId) && + (csr_neighbor_roam_state_preauth_done(mac_ctx, sessionId))) + csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + mac_ctx, sessionId); + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (!session) { + sme_err("session %d not found", sessionId); + return; + } + + csr_update_scan_entry_associnfo(mac_ctx, + session, SCAN_ENTRY_CON_STATE_NONE); + /* Update the disconnect stats */ + switch (pDeauthInd->reasonCode) { + case eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON: + session->disconnect_stats.disconnection_cnt++; + session->disconnect_stats.peer_kickout++; + break; + case eSIR_MAC_UNSPEC_FAILURE_REASON: + case eSIR_MAC_PREV_AUTH_NOT_VALID_REASON: + case eSIR_MAC_DEAUTH_LEAVING_BSS_REASON: + case eSIR_MAC_CLASS2_FRAME_FROM_NON_AUTH_STA_REASON: + case eSIR_MAC_CLASS3_FRAME_FROM_NON_ASSOC_STA_REASON: + case eSIR_MAC_STA_NOT_PRE_AUTHENTICATED_REASON: + session->disconnect_stats.disconnection_cnt++; + session->disconnect_stats.deauth_by_peer++; + break; + case eSIR_MAC_BEACON_MISSED: + session->disconnect_stats.disconnection_cnt++; + session->disconnect_stats.bmiss++; + break; + default: + /* Unknown reason code */ + break; + } + + if (csr_is_conn_state_infra(mac_ctx, sessionId)) + session->connectState = eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED; +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + sme_qos_csr_event_ind(mac_ctx, (uint8_t) sessionId, + SME_QOS_CSR_DISCONNECT_IND, NULL); +#endif + csr_roam_link_down(mac_ctx, sessionId); + csr_roam_issue_wm_status_change(mac_ctx, sessionId, + eCsrDeauthenticated, + msg_ptr); +} + +static void +csr_roam_chk_lnk_swt_ch_ind(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + struct csr_roam_session *session; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + uint16_t ie_len; + QDF_STATUS status; + struct switch_channel_ind *pSwitchChnInd; + struct csr_roam_info *roam_info; + tSirMacDsParamSetIE *ds_params_ie; + tDot11fIEHTInfo *ht_info_ie; + + /* in case of STA, the SWITCH_CHANNEL originates from its AP */ + sme_debug("eWNI_SME_SWITCH_CHL_IND from SME"); + pSwitchChnInd = (struct switch_channel_ind *)msg_ptr; + /* Update with the new channel id. The channel id is hidden in the + * status_code. + */ + status = csr_roam_get_session_id_from_bssid(mac_ctx, + &pSwitchChnInd->bssid, &sessionId); + if (QDF_IS_STATUS_ERROR(status)) + return; + + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (!session) { + sme_err("session %d not found", sessionId); + return; + } + + if (QDF_IS_STATUS_ERROR(pSwitchChnInd->status)) { + sme_err("Channel switch failed"); + csr_roam_disconnect_internal(mac_ctx, sessionId, + eCSR_DISCONNECT_REASON_DEAUTH, + eSIR_MAC_CHANNEL_SWITCH_FAILED); + return; + } + session->connectedProfile.op_freq = pSwitchChnInd->freq; + + /* Update the occupied channel list with the new switched channel */ + csr_init_occupied_channels_list(mac_ctx, sessionId); + + if (session->pConnectBssDesc) { + session->pConnectBssDesc->chan_freq = pSwitchChnInd->freq; + + ie_len = csr_get_ielen_from_bss_description( + session->pConnectBssDesc); + ds_params_ie = (tSirMacDsParamSetIE *)wlan_get_ie_ptr_from_eid( + DOT11F_EID_DSPARAMS, + (uint8_t *)session->pConnectBssDesc->ieFields, + ie_len); + if (ds_params_ie) + ds_params_ie->channelNumber = + wlan_reg_freq_to_chan(mac_ctx->pdev, + pSwitchChnInd->freq); + + ht_info_ie = (tDot11fIEHTInfo *)wlan_get_ie_ptr_from_eid( + DOT11F_EID_HTINFO, + (uint8_t *)session->pConnectBssDesc->ieFields, + ie_len); + if (ht_info_ie) { + ht_info_ie->primaryChannel = + wlan_reg_freq_to_chan(mac_ctx->pdev, + pSwitchChnInd->freq); + ht_info_ie->secondaryChannelOffset = + pSwitchChnInd->chan_params.sec_ch_offset; + } + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + roam_info->chan_info.mhz = pSwitchChnInd->freq; + roam_info->chan_info.ch_width = pSwitchChnInd->chan_params.ch_width; + roam_info->chan_info.sec_ch_offset = + pSwitchChnInd->chan_params.sec_ch_offset; + roam_info->chan_info.band_center_freq1 = + pSwitchChnInd->chan_params.mhz_freq_seg0; + roam_info->chan_info.band_center_freq2 = + pSwitchChnInd->chan_params.mhz_freq_seg1; + + switch (session->bssParams.uCfgDot11Mode) { + case eCSR_CFG_DOT11_MODE_11N: + case eCSR_CFG_DOT11_MODE_11N_ONLY: + roam_info->mode = SIR_SME_PHY_MODE_HT; + break; + case eCSR_CFG_DOT11_MODE_11AC: + case eCSR_CFG_DOT11_MODE_11AC_ONLY: + case eCSR_CFG_DOT11_MODE_11AX: + case eCSR_CFG_DOT11_MODE_11AX_ONLY: + roam_info->mode = SIR_SME_PHY_MODE_VHT; + break; + default: + roam_info->mode = SIR_SME_PHY_MODE_LEGACY; + break; + } + + status = csr_roam_call_callback(mac_ctx, sessionId, roam_info, 0, + eCSR_ROAM_STA_CHANNEL_SWITCH, + eCSR_ROAM_RESULT_NONE); + qdf_mem_free(roam_info); +} + +static void +csr_roam_chk_lnk_deauth_rsp(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + struct csr_roam_session *session; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct csr_roam_info *roam_info; + struct deauth_rsp *pDeauthRsp = (struct deauth_rsp *) msg_ptr; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + sme_debug("eWNI_SME_DEAUTH_RSP from SME"); + sessionId = pDeauthRsp->sessionId; + if (!CSR_IS_SESSION_VALID(mac_ctx, sessionId)) { + qdf_mem_free(roam_info); + return; + } + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (CSR_IS_INFRA_AP(&session->connectedProfile)) { + roam_info->u.pConnectedProfile = &session->connectedProfile; + qdf_copy_macaddr(&roam_info->peerMac, + &pDeauthRsp->peer_macaddr); + roam_info->reasonCode = eCSR_ROAM_RESULT_FORCED; + roam_info->status_code = pDeauthRsp->status_code; + status = csr_roam_call_callback(mac_ctx, sessionId, + roam_info, 0, + eCSR_ROAM_LOSTLINK, + eCSR_ROAM_RESULT_FORCED); + } + qdf_mem_free(roam_info); +} + +static void +csr_roam_chk_lnk_disassoc_rsp(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + struct csr_roam_session *session; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct csr_roam_info *roam_info; + /* + * session id is invalid here so cant use it to access the array + * curSubstate as index + */ + struct disassoc_rsp *pDisassocRsp = (struct disassoc_rsp *) msg_ptr; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + sme_debug("eWNI_SME_DISASSOC_RSP from SME "); + sessionId = pDisassocRsp->sessionId; + if (!CSR_IS_SESSION_VALID(mac_ctx, sessionId)) { + qdf_mem_free(roam_info); + return; + } + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (CSR_IS_INFRA_AP(&session->connectedProfile)) { + roam_info->u.pConnectedProfile = &session->connectedProfile; + qdf_copy_macaddr(&roam_info->peerMac, + &pDisassocRsp->peer_macaddr); + roam_info->reasonCode = eCSR_ROAM_RESULT_FORCED; + roam_info->status_code = pDisassocRsp->status_code; + status = csr_roam_call_callback(mac_ctx, sessionId, + roam_info, 0, + eCSR_ROAM_LOSTLINK, + eCSR_ROAM_RESULT_FORCED); + } + qdf_mem_free(roam_info); +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +static void +csr_roam_diag_mic_fail(struct mac_context *mac_ctx, uint32_t sessionId) +{ + WLAN_HOST_DIAG_EVENT_DEF(secEvent, + host_event_wlan_security_payload_type); + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, sessionId); + + if (!session) { + sme_err("session %d not found", sessionId); + return; + } + qdf_mem_zero(&secEvent, sizeof(host_event_wlan_security_payload_type)); + secEvent.eventId = WLAN_SECURITY_EVENT_MIC_ERROR; + secEvent.encryptionModeMulticast = + (uint8_t) diag_enc_type_from_csr_type( + session->connectedProfile.mcEncryptionType); + secEvent.encryptionModeUnicast = + (uint8_t) diag_enc_type_from_csr_type( + session->connectedProfile.EncryptionType); + secEvent.authMode = + (uint8_t) diag_auth_type_from_csr_type( + session->connectedProfile.AuthType); + qdf_mem_copy(secEvent.bssid, session->connectedProfile.bssid.bytes, + QDF_MAC_ADDR_SIZE); + WLAN_HOST_DIAG_EVENT_REPORT(&secEvent, EVENT_WLAN_SECURITY); +} +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + +static void +csr_roam_chk_lnk_mic_fail_ind(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct csr_roam_info *roam_info; + struct mic_failure_ind *mic_ind = (struct mic_failure_ind *)msg_ptr; + eCsrRoamResult result = eCSR_ROAM_RESULT_MIC_ERROR_UNICAST; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + status = csr_roam_get_session_id_from_bssid(mac_ctx, + &mic_ind->bssId, &sessionId); + if (QDF_IS_STATUS_SUCCESS(status)) { + roam_info->u.pMICFailureInfo = &mic_ind->info; + if (mic_ind->info.multicast) + result = eCSR_ROAM_RESULT_MIC_ERROR_GROUP; + else + result = eCSR_ROAM_RESULT_MIC_ERROR_UNICAST; + csr_roam_call_callback(mac_ctx, sessionId, roam_info, 0, + eCSR_ROAM_MIC_ERROR_IND, result); + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + csr_roam_diag_mic_fail(mac_ctx, sessionId); +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + qdf_mem_free(roam_info); +} + +static void +csr_roam_chk_lnk_pbs_probe_req_ind(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct csr_roam_info *roam_info; + tpSirSmeProbeReqInd pProbeReqInd = (tpSirSmeProbeReqInd) msg_ptr; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + sme_debug("WPS PBC Probe request Indication from SME"); + + status = csr_roam_get_session_id_from_bssid(mac_ctx, + &pProbeReqInd->bssid, &sessionId); + if (QDF_IS_STATUS_SUCCESS(status)) { + roam_info->u.pWPSPBCProbeReq = &pProbeReqInd->WPSPBCProbeReq; + csr_roam_call_callback(mac_ctx, sessionId, roam_info, + 0, eCSR_ROAM_WPS_PBC_PROBE_REQ_IND, + eCSR_ROAM_RESULT_WPS_PBC_PROBE_REQ_IND); + } + qdf_mem_free(roam_info); +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +static void +csr_roam_diag_joined_new_bss(struct mac_context *mac_ctx, + struct new_bss_info *pNewBss) +{ + host_log_ibss_pkt_type *pIbssLog; + uint32_t bi; + + WLAN_HOST_DIAG_LOG_ALLOC(pIbssLog, host_log_ibss_pkt_type, + LOG_WLAN_IBSS_C); + if (!pIbssLog) + return; + pIbssLog->eventId = WLAN_IBSS_EVENT_COALESCING; + if (pNewBss) { + qdf_copy_macaddr(&pIbssLog->bssid, &pNewBss->bssId); + if (pNewBss->ssId.length > HOST_LOG_MAX_SSID_SIZE) + pNewBss->ssId.length = HOST_LOG_MAX_SSID_SIZE; + qdf_mem_copy(pIbssLog->ssid, pNewBss->ssId.ssId, + pNewBss->ssId.length); + pIbssLog->op_freq = pNewBss->freq; + pIbssLog->operatingChannel = wlan_reg_freq_to_chan + (mac_ctx->pdev, + pIbssLog->op_freq); + } + bi = mac_ctx->mlme_cfg->sap_cfg.beacon_interval; + /* U8 is not enough for beacon interval */ + pIbssLog->beaconInterval = (uint8_t) bi; + WLAN_HOST_DIAG_LOG_REPORT(pIbssLog); +} +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + +static void +csr_roam_chk_lnk_wm_status_change_ntf(struct mac_context *mac_ctx, + tSirSmeRsp *msg_ptr) +{ + struct csr_roam_session *session; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + struct csr_roam_info *roam_info; + struct wm_status_change_ntf *pStatusChangeMsg; + struct ap_new_caps *pApNewCaps; + eCsrRoamResult result = eCSR_ROAM_RESULT_NONE; + tSirMacAddr Broadcastaddr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + struct new_bss_info *pNewBss; + eRoamCmdStatus roamStatus = eCSR_ROAM_FAILED; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + pStatusChangeMsg = (struct wm_status_change_ntf *) msg_ptr; + switch (pStatusChangeMsg->statusChangeCode) { + case eSIR_SME_IBSS_ACTIVE: + sessionId = csr_find_ibss_session(mac_ctx); + if (WLAN_UMAC_VDEV_ID_MAX == sessionId) + break; + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (!session) { + sme_err("session %d not found", + sessionId); + goto out; + } + session->connectState = eCSR_ASSOC_STATE_TYPE_IBSS_CONNECTED; + if (session->pConnectBssDesc) { + qdf_mem_copy(&roam_info->bssid, + session->pConnectBssDesc->bssId, + sizeof(struct qdf_mac_addr)); + roam_info->u.pConnectedProfile = + &session->connectedProfile; + } else { + sme_err("CSR: connected BSS is empty"); + } + result = eCSR_ROAM_RESULT_IBSS_CONNECT; + roamStatus = eCSR_ROAM_CONNECT_STATUS_UPDATE; + break; + + case eSIR_SME_IBSS_INACTIVE: + sessionId = csr_find_ibss_session(mac_ctx); + if (WLAN_UMAC_VDEV_ID_MAX != sessionId) { + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (!session) { + sme_err("session %d not found", sessionId); + goto out; + } + session->connectState = + eCSR_ASSOC_STATE_TYPE_IBSS_DISCONNECTED; + result = eCSR_ROAM_RESULT_IBSS_INACTIVE; + roamStatus = eCSR_ROAM_CONNECT_STATUS_UPDATE; + } + break; + + case eSIR_SME_JOINED_NEW_BSS: + /* IBSS coalescing. */ + sme_debug("CSR: eSIR_SME_JOINED_NEW_BSS received from PE"); + sessionId = csr_find_ibss_session(mac_ctx); + if (WLAN_UMAC_VDEV_ID_MAX == sessionId) + break; + session = CSR_GET_SESSION(mac_ctx, sessionId); + if (!session) { + sme_err("session %d not found", + sessionId); + goto out; + } + /* update the connection state information */ + pNewBss = &pStatusChangeMsg->statusChangeInfo.newBssInfo; +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + csr_roam_diag_joined_new_bss(mac_ctx, pNewBss); +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + csr_roam_update_connected_profile_from_new_bss(mac_ctx, + sessionId, + pNewBss); + + if ((eCSR_ENCRYPT_TYPE_NONE == + session->connectedProfile.EncryptionType)) { + csr_issue_set_context_req_helper(mac_ctx, + session->pCurRoamProfile, sessionId, + &Broadcastaddr, false, false, + eSIR_TX_RX, 0, 0, NULL); + } + result = eCSR_ROAM_RESULT_IBSS_COALESCED; + roamStatus = eCSR_ROAM_IBSS_IND; + qdf_mem_copy(&roam_info->bssid, &pNewBss->bssId, + sizeof(struct qdf_mac_addr)); + /* This BSSID is the real BSSID, save it */ + if (session->pConnectBssDesc) + qdf_mem_copy(session->pConnectBssDesc->bssId, + &pNewBss->bssId, sizeof(struct qdf_mac_addr)); + break; + + /* + * detection by LIM that the capabilities of the associated + * AP have changed. + */ + case eSIR_SME_AP_CAPS_CHANGED: + pApNewCaps = &pStatusChangeMsg->statusChangeInfo.apNewCaps; + sme_debug("CSR handling eSIR_SME_AP_CAPS_CHANGED"); + status = csr_roam_get_session_id_from_bssid(mac_ctx, + &pApNewCaps->bssId, &sessionId); + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + if (eCSR_ROAMING_STATE_JOINED == + sme_get_current_roam_state(MAC_HANDLE(mac_ctx), sessionId) + && ((eCSR_ROAM_SUBSTATE_JOINED_REALTIME_TRAFFIC + == mac_ctx->roam.curSubState[sessionId]) + || (eCSR_ROAM_SUBSTATE_NONE == + mac_ctx->roam.curSubState[sessionId]) + || (eCSR_ROAM_SUBSTATE_JOINED_NON_REALTIME_TRAFFIC + == mac_ctx->roam.curSubState[sessionId]) + || (eCSR_ROAM_SUBSTATE_JOINED_NO_TRAFFIC == + mac_ctx->roam.curSubState[sessionId]))) { + sme_warn("Calling csr_roam_disconnect_internal"); + csr_roam_disconnect_internal(mac_ctx, sessionId, + eCSR_DISCONNECT_REASON_UNSPECIFIED, + eSIR_MAC_UNSPEC_FAILURE_REASON); + } else { + sme_warn("Skipping the new scan as CSR is in state: %s and sub-state: %s", + mac_trace_getcsr_roam_state( + mac_ctx->roam.curState[sessionId]), + mac_trace_getcsr_roam_sub_state( + mac_ctx->roam.curSubState[sessionId])); + /* We ignore the caps change event if CSR is not in full + * connected state. Send one event to PE to reset + * limSentCapsChangeNtf Once limSentCapsChangeNtf set + * 0, lim can send sub sequent CAPS change event + * otherwise lim cannot send any CAPS change events to + * SME + */ + csr_send_reset_ap_caps_changed(mac_ctx, + &pApNewCaps->bssId); + } + break; + + default: + roamStatus = eCSR_ROAM_FAILED; + result = eCSR_ROAM_RESULT_NONE; + break; + } /* end switch on statusChangeCode */ + if (eCSR_ROAM_RESULT_NONE != result) { + csr_roam_call_callback(mac_ctx, sessionId, roam_info, 0, + roamStatus, result); + } +out: + qdf_mem_free(roam_info); +} + +static void +csr_roam_chk_lnk_max_assoc_exceeded(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr) +{ + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + tSmeMaxAssocInd *pSmeMaxAssocInd; + struct csr_roam_info *roam_info; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + pSmeMaxAssocInd = (tSmeMaxAssocInd *) msg_ptr; + sme_debug( + "max assoc have been reached, new peer cannot be accepted"); + sessionId = pSmeMaxAssocInd->sessionId; + roam_info->sessionId = sessionId; + qdf_copy_macaddr(&roam_info->peerMac, &pSmeMaxAssocInd->peer_mac); + csr_roam_call_callback(mac_ctx, sessionId, roam_info, 0, + eCSR_ROAM_INFRA_IND, + eCSR_ROAM_RESULT_MAX_ASSOC_EXCEEDED); + qdf_mem_free(roam_info); +} + +void csr_roam_check_for_link_status_change(struct mac_context *mac, + tSirSmeRsp *pSirMsg) +{ + if (!pSirMsg) { + sme_err("pSirMsg is NULL"); + return; + } + switch (pSirMsg->messageType) { + case eWNI_SME_ASSOC_IND: + csr_roam_chk_lnk_assoc_ind(mac, pSirMsg); + break; + case eWNI_SME_ASSOC_IND_UPPER_LAYER: + csr_roam_chk_lnk_assoc_ind_upper_layer(mac, pSirMsg); + break; + case eWNI_SME_DISASSOC_IND: + csr_roam_chk_lnk_disassoc_ind(mac, pSirMsg); + break; + case eWNI_SME_DISCONNECT_DONE_IND: + csr_roam_send_disconnect_done_indication(mac, pSirMsg); + break; + case eWNI_SME_DEAUTH_IND: + csr_roam_chk_lnk_deauth_ind(mac, pSirMsg); + break; + case eWNI_SME_SWITCH_CHL_IND: + csr_roam_chk_lnk_swt_ch_ind(mac, pSirMsg); + break; + case eWNI_SME_DEAUTH_RSP: + csr_roam_chk_lnk_deauth_rsp(mac, pSirMsg); + break; + case eWNI_SME_DISASSOC_RSP: + csr_roam_chk_lnk_disassoc_rsp(mac, pSirMsg); + break; + case eWNI_SME_MIC_FAILURE_IND: + csr_roam_chk_lnk_mic_fail_ind(mac, pSirMsg); + break; + case eWNI_SME_WPS_PBC_PROBE_REQ_IND: + csr_roam_chk_lnk_pbs_probe_req_ind(mac, pSirMsg); + break; + case eWNI_SME_WM_STATUS_CHANGE_NTF: + csr_roam_chk_lnk_wm_status_change_ntf(mac, pSirMsg); + break; + case eWNI_SME_IBSS_NEW_PEER_IND: + csr_roam_chk_lnk_ibss_new_peer_ind(mac, pSirMsg); + break; + case eWNI_SME_IBSS_PEER_DEPARTED_IND: + csr_roam_chk_lnk_ibss_peer_departed_ind(mac, pSirMsg); + break; + case eWNI_SME_SETCONTEXT_RSP: + csr_roam_chk_lnk_set_ctx_rsp(mac, pSirMsg); + break; +#ifdef FEATURE_WLAN_ESE + case eWNI_SME_GET_TSM_STATS_RSP: + sme_debug("TSM Stats rsp from PE"); + csr_tsm_stats_rsp_processor(mac, pSirMsg); + break; +#endif /* FEATURE_WLAN_ESE */ + case eWNI_SME_GET_SNR_REQ: + sme_debug("GetSnrReq from self"); + csr_update_snr(mac, pSirMsg); + break; + case eWNI_SME_FT_PRE_AUTH_RSP: + csr_roam_ft_pre_auth_rsp_processor(mac, + (tpSirFTPreAuthRsp) pSirMsg); + break; + case eWNI_SME_MAX_ASSOC_EXCEEDED: + csr_roam_chk_lnk_max_assoc_exceeded(mac, pSirMsg); + break; + case eWNI_SME_CANDIDATE_FOUND_IND: + csr_neighbor_roam_candidate_found_ind_hdlr(mac, pSirMsg); + break; + case eWNI_SME_HANDOFF_REQ: + sme_debug("Handoff Req from self"); + csr_neighbor_roam_handoff_req_hdlr(mac, pSirMsg); + break; + + default: + break; + } /* end switch on message type */ +} + +void csr_call_roaming_completion_callback(struct mac_context *mac, + struct csr_roam_session *pSession, + struct csr_roam_info *roam_info, + uint32_t roamId, + eCsrRoamResult roamResult) +{ + if (pSession) { + if (pSession->bRefAssocStartCnt) { + pSession->bRefAssocStartCnt--; + + if (0 != pSession->bRefAssocStartCnt) { + QDF_ASSERT(pSession->bRefAssocStartCnt == 0); + return; + } + /* Need to call association_completion because there + * is an assoc_start pending. + */ + csr_roam_call_callback(mac, pSession->sessionId, NULL, + roamId, + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_RESULT_FAILURE); + } + csr_roam_call_callback(mac, pSession->sessionId, roam_info, + roamId, eCSR_ROAM_ROAMING_COMPLETION, + roamResult); + } else + sme_err("pSession is NULL"); +} + +/* return a bool to indicate whether roaming completed or continue. */ +bool csr_roam_complete_roaming(struct mac_context *mac, uint32_t sessionId, + bool fForce, eCsrRoamResult roamResult) +{ + bool fCompleted = true; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found ", sessionId); + return false; + } + /* Check whether time is up */ + if (pSession->fCancelRoaming || fForce || + eCsrReassocRoaming == pSession->roamingReason || + eCsrDynamicRoaming == pSession->roamingReason) { + sme_debug("indicates roaming completion"); + if (pSession->fCancelRoaming + && CSR_IS_LOSTLINK_ROAMING(pSession->roamingReason)) { + /* roaming is cancelled, tell HDD to indicate disconnect + * Because LIM overload deauth_ind for both deauth frame + * and missed beacon we need to use this logic to + * detinguish it. For missed beacon, LIM set reason to + * be eSIR_BEACON_MISSED + */ + if (eSIR_MAC_BEACON_MISSED == + pSession->roamingStatusCode) { + roamResult = eCSR_ROAM_RESULT_LOSTLINK; + } else if (eCsrLostlinkRoamingDisassoc == + pSession->roamingReason) { + roamResult = eCSR_ROAM_RESULT_DISASSOC_IND; + } else if (eCsrLostlinkRoamingDeauth == + pSession->roamingReason) { + roamResult = eCSR_ROAM_RESULT_DEAUTH_IND; + } else { + roamResult = eCSR_ROAM_RESULT_LOSTLINK; + } + } + csr_call_roaming_completion_callback(mac, pSession, NULL, 0, + roamResult); + pSession->roamingReason = eCsrNotRoaming; + } else { + pSession->roamResult = roamResult; + if (!QDF_IS_STATUS_SUCCESS(csr_roam_start_roaming_timer(mac, + sessionId, QDF_MC_TIMER_TO_SEC_UNIT))) { + csr_call_roaming_completion_callback(mac, pSession, + NULL, 0, roamResult); + pSession->roamingReason = eCsrNotRoaming; + } else { + fCompleted = false; + } + } + return fCompleted; +} + +void csr_roam_cancel_roaming(struct mac_context *mac, uint32_t sessionId) +{ + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session: %d not found", sessionId); + return; + } + + if (CSR_IS_ROAMING(pSession)) { + sme_debug("Cancel roaming"); + pSession->fCancelRoaming = true; + if (CSR_IS_ROAM_JOINING(mac, sessionId) + && CSR_IS_ROAM_SUBSTATE_CONFIG(mac, sessionId)) { + /* No need to do anything in here because the handler + * takes care of it + */ + } else { + eCsrRoamResult roamResult = + CSR_IS_LOSTLINK_ROAMING(pSession-> + roamingReason) ? + eCSR_ROAM_RESULT_LOSTLINK : + eCSR_ROAM_RESULT_NONE; + /* Roaming is stopped after here */ + csr_roam_complete_roaming(mac, sessionId, true, + roamResult); + /* Since CSR may be in lostlink roaming situation, + * abort all roaming related activities + */ + csr_scan_abort_mac_scan(mac, sessionId, INVAL_SCAN_ID); + csr_roam_stop_roaming_timer(mac, sessionId); + } + } +} + +void csr_roam_roaming_timer_handler(void *pv) +{ + struct csr_timer_info *info = pv; + struct mac_context *mac = info->mac; + uint32_t vdev_id = info->vdev_id; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, vdev_id); + + if (!pSession) { + sme_err(" session %d not found ", vdev_id); + return; + } + + if (false == pSession->fCancelRoaming) { + csr_call_roaming_completion_callback(mac, pSession, + NULL, 0, + pSession->roamResult); + pSession->roamingReason = eCsrNotRoaming; + } +} + +/** + * csr_roam_roaming_offload_timeout_handler() - Handler for roaming failure + * @timer_data: Carries the mac_ctx and session info + * + * This function would be invoked when the roaming_offload_timer expires. + * The timer is waiting in anticipation of a related roaming event from + * the firmware after receiving the ROAM_START event. + * + * Return: None + */ +void csr_roam_roaming_offload_timeout_handler(void *timer_data) +{ + struct csr_timer_info *timer_info = timer_data; + struct wlan_objmgr_vdev *vdev; + struct mlme_roam_after_data_stall *vdev_roam_params; + struct scheduler_msg wma_msg = {0}; + struct roam_sync_timeout_timer_info *req; + QDF_STATUS status; + + if (timer_info) { + sme_debug("LFR3:roaming offload timer expired, session: %d", + timer_info->vdev_id); + } else { + sme_err("Invalid Session"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(timer_info->mac->psoc, + timer_info->vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("vdev is NULL, aborting roam start"); + return; + } + + vdev_roam_params = mlme_get_roam_invoke_params(vdev); + if (!vdev_roam_params) { + sme_err("Invalid vdev roam params, aborting timeout handler"); + status = QDF_STATUS_E_NULL_VALUE; + goto rel; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + goto rel; + + req->vdev_id = timer_info->vdev_id; + wma_msg.type = WMA_ROAM_SYNC_TIMEOUT; + wma_msg.bodyptr = req; + + status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &wma_msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Post roam offload timer fails, session id: %d", + timer_info->vdev_id); + qdf_mem_free(req); + goto rel; + } + + vdev_roam_params->roam_invoke_in_progress = false; + +rel: + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); +} + +QDF_STATUS csr_roam_start_roaming_timer(struct mac_context *mac, + uint32_t vdev_id, + uint32_t interval) +{ + QDF_STATUS status; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, vdev_id); + + if (!pSession) { + sme_err("session %d not found", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + sme_debug("csrScanStartRoamingTimer"); + pSession->roamingTimerInfo.vdev_id = (uint8_t) vdev_id; + status = qdf_mc_timer_start(&pSession->hTimerRoaming, + interval / QDF_MC_TIMER_TO_MS_UNIT); + + return status; +} + +QDF_STATUS csr_roam_stop_roaming_timer(struct mac_context *mac, + uint32_t sessionId) +{ + return qdf_mc_timer_stop + (&mac->roam.roamSession[sessionId].hTimerRoaming); +} + +void csr_roam_wait_for_key_time_out_handler(void *pv) +{ + struct csr_timer_info *info = pv; + struct mac_context *mac = info->mac; + uint8_t vdev_id = info->vdev_id; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, vdev_id); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!pSession) { + sme_err("session not found"); + return; + } + + sme_debug("WaitForKey timer expired in state: %s sub-state: %s", + mac_trace_get_neighbour_roam_state(mac->roam. + neighborRoamInfo[vdev_id]. + neighborRoamState), + mac_trace_getcsr_roam_sub_state(mac->roam. + curSubState[vdev_id])); + spin_lock(&mac->roam.roam_state_lock); + if (CSR_IS_WAIT_FOR_KEY(mac, vdev_id)) { + /* Change the substate so command queue is unblocked. */ + if (vdev_id < WLAN_MAX_VDEVS) + mac->roam.curSubState[vdev_id] = + eCSR_ROAM_SUBSTATE_NONE; + spin_unlock(&mac->roam.roam_state_lock); + + if (csr_neighbor_roam_is_handoff_in_progress(mac, vdev_id)) { + /* + * Enable heartbeat timer when hand-off is in progress + * and Key Wait timer expired. + */ + sme_debug("Enabling HB timer after WaitKey expiry nHBCount: %d)", + mac->roam.configParam.HeartbeatThresh24); + if (cfg_in_range(CFG_HEART_BEAT_THRESHOLD, + mac->roam.configParam. + HeartbeatThresh24)) + mac->mlme_cfg->timeouts.heart_beat_threshold = + mac->roam.configParam.HeartbeatThresh24; + else + mac->mlme_cfg->timeouts.heart_beat_threshold = + cfg_default(CFG_HEART_BEAT_THRESHOLD); + } + sme_debug("SME pre-auth state timeout"); + + status = sme_acquire_global_lock(&mac->sme); + if (csr_is_conn_state_connected_infra(mac, vdev_id)) { + csr_roam_link_up(mac, + pSession->connectedProfile.bssid); + if (QDF_IS_STATUS_SUCCESS(status)) { + csr_roam_disconnect(mac, vdev_id, + eCSR_DISCONNECT_REASON_UNSPECIFIED, + eSIR_MAC_KEY_TIMEOUT); + } + } else { + sme_err("session not found"); + } + sme_release_global_lock(&mac->sme); + } else { + spin_unlock(&mac->roam.roam_state_lock); + } + +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * csr_roam_roaming_offload_timer_action() - API to start/stop the timer + * @mac_ctx: MAC Context + * @interval: Value to be set for the timer + * @vdev_id: Session on which the timer should be operated + * @action: Start/Stop action for the timer + * + * API to start/stop the roaming offload timer + * + * Return: None + */ +void csr_roam_roaming_offload_timer_action( + struct mac_context *mac_ctx, uint32_t interval, uint8_t vdev_id, + uint8_t action) +{ + struct csr_roam_session *csr_session = CSR_GET_SESSION(mac_ctx, + vdev_id); + QDF_TIMER_STATE tstate; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + ("LFR3: timer action %d, session %d, intvl %d"), + action, vdev_id, interval); + if (mac_ctx) { + csr_session = CSR_GET_SESSION(mac_ctx, vdev_id); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + ("LFR3: Invalid MAC Context")); + return; + } + if (!csr_session) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + ("LFR3: session %d not found"), vdev_id); + return; + } + csr_session->roamingTimerInfo.vdev_id = (uint8_t) vdev_id; + + tstate = + qdf_mc_timer_get_current_state(&csr_session->roaming_offload_timer); + + if (action == ROAMING_OFFLOAD_TIMER_START) { + /* + * If timer is already running then re-start timer in order to + * process new ROAM_START with fresh timer. + */ + if (tstate == QDF_TIMER_STATE_RUNNING) + qdf_mc_timer_stop(&csr_session->roaming_offload_timer); + qdf_mc_timer_start(&csr_session->roaming_offload_timer, + interval / QDF_MC_TIMER_TO_MS_UNIT); + } + if (action == ROAMING_OFFLOAD_TIMER_STOP) + qdf_mc_timer_stop(&csr_session->roaming_offload_timer); + +} +#endif + +static QDF_STATUS csr_roam_start_wait_for_key_timer( + struct mac_context *mac, uint32_t interval) +{ + QDF_STATUS status; + uint8_t session_id = mac->roam.WaitForKeyTimerInfo.vdev_id; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[session_id]; + + if (csr_neighbor_roam_is_handoff_in_progress(mac, session_id)) { + /* Disable heartbeat timer when hand-off is in progress */ + sme_debug("disabling HB timer in state: %s sub-state: %s", + mac_trace_get_neighbour_roam_state( + pNeighborRoamInfo->neighborRoamState), + mac_trace_getcsr_roam_sub_state( + mac->roam.curSubState[session_id])); + mac->mlme_cfg->timeouts.heart_beat_threshold = 0; + } + sme_debug("csrScanStartWaitForKeyTimer"); + status = qdf_mc_timer_start(&mac->roam.hTimerWaitForKey, + interval / QDF_MC_TIMER_TO_MS_UNIT); + + return status; +} + +QDF_STATUS csr_roam_stop_wait_for_key_timer(struct mac_context *mac) +{ + uint8_t vdev_id = mac->roam.WaitForKeyTimerInfo.vdev_id; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[vdev_id]; + + sme_debug("WaitForKey timer stopped in state: %s sub-state: %s", + mac_trace_get_neighbour_roam_state(pNeighborRoamInfo-> + neighborRoamState), + mac_trace_getcsr_roam_sub_state(mac->roam. + curSubState[vdev_id])); + if (csr_neighbor_roam_is_handoff_in_progress(mac, vdev_id)) { + /* + * Enable heartbeat timer when hand-off is in progress + * and Key Wait timer got stopped for some reason + */ + sme_debug("Enabling HB timer after WaitKey stop nHBCount: %d", + mac->roam.configParam.HeartbeatThresh24); + if (cfg_in_range(CFG_HEART_BEAT_THRESHOLD, + mac->roam.configParam.HeartbeatThresh24)) + mac->mlme_cfg->timeouts.heart_beat_threshold = + mac->roam.configParam.HeartbeatThresh24; + else + mac->mlme_cfg->timeouts.heart_beat_threshold = + cfg_default(CFG_HEART_BEAT_THRESHOLD); + } + return qdf_mc_timer_stop(&mac->roam.hTimerWaitForKey); +} + +void csr_roam_completion(struct mac_context *mac, uint32_t vdev_id, + struct csr_roam_info *roam_info, tSmeCmd *pCommand, + eCsrRoamResult roamResult, bool fSuccess) +{ + eRoamCmdStatus roamStatus = csr_get_roam_complete_status(mac, + vdev_id); + uint32_t roamId = 0; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, vdev_id); + + if (!pSession) { + sme_err("session: %d not found ", vdev_id); + return; + } + + if (pCommand) { + roamId = pCommand->u.roamCmd.roamId; + if (vdev_id != pCommand->vdev_id) { + QDF_ASSERT(vdev_id == pCommand->vdev_id); + return; + } + } + if (eCSR_ROAM_ROAMING_COMPLETION == roamStatus) + /* if success, force roaming completion */ + csr_roam_complete_roaming(mac, vdev_id, fSuccess, + roamResult); + else { + if (pSession->bRefAssocStartCnt != 0) { + QDF_ASSERT(pSession->bRefAssocStartCnt == 0); + return; + } + sme_debug("indicates association completion roamResult: %d", + roamResult); + csr_roam_call_callback(mac, vdev_id, roam_info, roamId, + roamStatus, roamResult); + } +} + +static +QDF_STATUS csr_roam_lost_link(struct mac_context *mac, uint32_t sessionId, + uint32_t type, tSirSmeRsp *pSirMsg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct deauth_ind *pDeauthIndMsg = NULL; + struct disassoc_ind *pDisassocIndMsg = NULL; + eCsrRoamResult result = eCSR_ROAM_RESULT_LOSTLINK; + struct csr_roam_info *roam_info; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + bool from_ap = false; + + if (!pSession) { + sme_err("session: %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + if (eWNI_SME_DISASSOC_IND == type) { + result = eCSR_ROAM_RESULT_DISASSOC_IND; + pDisassocIndMsg = (struct disassoc_ind *)pSirMsg; + pSession->roamingStatusCode = pDisassocIndMsg->status_code; + pSession->joinFailStatusCode.reasonCode = + pDisassocIndMsg->reasonCode; + from_ap = pDisassocIndMsg->from_ap; + qdf_copy_macaddr(&roam_info->peerMac, + &pDisassocIndMsg->peer_macaddr); + } else if (eWNI_SME_DEAUTH_IND == type) { + result = eCSR_ROAM_RESULT_DEAUTH_IND; + pDeauthIndMsg = (struct deauth_ind *)pSirMsg; + pSession->roamingStatusCode = pDeauthIndMsg->status_code; + pSession->joinFailStatusCode.reasonCode = + pDeauthIndMsg->reasonCode; + from_ap = pDeauthIndMsg->from_ap; + qdf_copy_macaddr(&roam_info->peerMac, + &pDeauthIndMsg->peer_macaddr); + + } else { + sme_warn("gets an unknown type (%d)", type); + result = eCSR_ROAM_RESULT_NONE; + pSession->joinFailStatusCode.reasonCode = 1; + } + + mlme_set_discon_reason_n_from_ap(mac->psoc, sessionId, from_ap, + pSession->joinFailStatusCode.reasonCode); + + + csr_roam_call_callback(mac, sessionId, NULL, 0, + eCSR_ROAM_LOSTLINK_DETECTED, result); + + if (eWNI_SME_DISASSOC_IND == type) + status = csr_send_mb_disassoc_cnf_msg(mac, pDisassocIndMsg); + else if (eWNI_SME_DEAUTH_IND == type) + status = csr_send_mb_deauth_cnf_msg(mac, pDeauthIndMsg); + + /* prepare to tell HDD to disconnect */ + qdf_mem_zero(roam_info, sizeof(*roam_info)); + roam_info->status_code = (tSirResultCodes)pSession->roamingStatusCode; + roam_info->reasonCode = pSession->joinFailStatusCode.reasonCode; + if (eWNI_SME_DISASSOC_IND == type) { + /* staMacAddr */ + qdf_copy_macaddr(&roam_info->peerMac, + &pDisassocIndMsg->peer_macaddr); + roam_info->staId = (uint8_t)pDisassocIndMsg->staId; + roam_info->reasonCode = pDisassocIndMsg->reasonCode; + } else if (eWNI_SME_DEAUTH_IND == type) { + /* staMacAddr */ + qdf_copy_macaddr(&roam_info->peerMac, + &pDeauthIndMsg->peer_macaddr); + roam_info->staId = (uint8_t)pDeauthIndMsg->staId; + roam_info->reasonCode = pDeauthIndMsg->reasonCode; + roam_info->rxRssi = pDeauthIndMsg->rssi; + } + sme_debug("roamInfo.staId: %d", roam_info->staId); +/* Dont initiate internal driver based roaming after disconnection*/ + qdf_mem_free(roam_info); + return status; +} + +void csr_roam_get_disconnect_stats_complete(struct mac_context *mac) +{ + tListElem *entry; + tSmeCmd *cmd; + + entry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (!entry) { + sme_err("NO commands are ACTIVE ..."); + return; + } + + cmd = GET_BASE_ADDR(entry, tSmeCmd, Link); + if (cmd->command != eSmeCommandGetdisconnectStats) { + sme_err("Get disconn stats cmd is not ACTIVE ..."); + return; + } + + if (csr_nonscan_active_ll_remove_entry(mac, entry, LL_ACCESS_LOCK)) + csr_release_command(mac, cmd); + else + sme_err("Failed to release command"); +} + +void csr_roam_wm_status_change_complete(struct mac_context *mac, + uint8_t session_id) +{ + tListElem *pEntry; + tSmeCmd *pCommand; + + pEntry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (pEntry) { + pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + if (eSmeCommandWmStatusChange == pCommand->command) { + /* Nothing to process in a Lost Link completion.... It just kicks off a */ + /* roaming sequence. */ + if (csr_nonscan_active_ll_remove_entry(mac, pEntry, + LL_ACCESS_LOCK)) { + csr_release_command(mac, pCommand); + } else { + sme_err("Failed to release command"); + } + } else { + sme_warn("CSR: LOST LINK command is not ACTIVE ..."); + } + } else { + sme_warn("CSR: NO commands are ACTIVE ..."); + } +} + +void csr_roam_process_get_disconnect_stats_command(struct mac_context *mac, + tSmeCmd *cmd) +{ + csr_get_peer_rssi(mac, cmd->vdev_id, + cmd->u.disconnect_stats_cmd.peer_mac_addr); +} + +void csr_roam_process_wm_status_change_command( + struct mac_context *mac, tSmeCmd *pCommand) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + tSirSmeRsp *pSirSmeMsg; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, + pCommand->vdev_id); + + if (!pSession) { + sme_err("session %d not found", pCommand->vdev_id); + goto end; + } + sme_debug("session:%d, CmdType : %d", + pCommand->vdev_id, pCommand->u.wmStatusChangeCmd.Type); + + switch (pCommand->u.wmStatusChangeCmd.Type) { + case eCsrDisassociated: + pSirSmeMsg = + (tSirSmeRsp *) &pCommand->u.wmStatusChangeCmd.u. + DisassocIndMsg; + status = + csr_roam_lost_link(mac, pCommand->vdev_id, + eWNI_SME_DISASSOC_IND, pSirSmeMsg); + break; + case eCsrDeauthenticated: + pSirSmeMsg = + (tSirSmeRsp *) &pCommand->u.wmStatusChangeCmd.u. + DeauthIndMsg; + status = + csr_roam_lost_link(mac, pCommand->vdev_id, + eWNI_SME_DEAUTH_IND, pSirSmeMsg); + break; + default: + sme_warn("gets an unknown command %d", + pCommand->u.wmStatusChangeCmd.Type); + break; + } + +end: + if (status != QDF_STATUS_SUCCESS) { + /* + * As status returned is not success, there is nothing else + * left to do so release WM status change command here. + */ + csr_roam_wm_status_change_complete(mac, pCommand->vdev_id); + } +} + +/** + * csr_compute_mode_and_band() - computes dot11mode + * @mac: mac global context + * @dot11_mode: out param, do11 mode calculated + * @band: out param, band caclculated + * @opr_ch_freq: operating channel freq in MHz + * + * This function finds dot11 mode based on current mode, operating channel and + * fw supported modes. + * + * Return: void + */ +static void +csr_compute_mode_and_band(struct mac_context *mac_ctx, + enum csr_cfgdot11mode *dot11_mode, + enum reg_wifi_band *band, + uint32_t opr_ch_freq) +{ + bool vht_24_ghz = mac_ctx->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band; + + switch (mac_ctx->roam.configParam.uCfgDot11Mode) { + case eCSR_CFG_DOT11_MODE_11A: + *dot11_mode = eCSR_CFG_DOT11_MODE_11A; + *band = REG_BAND_5G; + break; + case eCSR_CFG_DOT11_MODE_11B: + *dot11_mode = eCSR_CFG_DOT11_MODE_11B; + *band = REG_BAND_2G; + break; + case eCSR_CFG_DOT11_MODE_11G: + *dot11_mode = eCSR_CFG_DOT11_MODE_11G; + *band = REG_BAND_2G; + break; + case eCSR_CFG_DOT11_MODE_11N: + *dot11_mode = eCSR_CFG_DOT11_MODE_11N; + *band = wlan_reg_freq_to_band(opr_ch_freq); + break; + case eCSR_CFG_DOT11_MODE_11AC: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) { + /* + * If the operating channel is in 2.4 GHz band, check + * for INI item to disable VHT operation in 2.4 GHz band + */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(opr_ch_freq) && + !vht_24_ghz) + /* Disable 11AC operation */ + *dot11_mode = eCSR_CFG_DOT11_MODE_11N; + else + *dot11_mode = eCSR_CFG_DOT11_MODE_11AC; + } else { + *dot11_mode = eCSR_CFG_DOT11_MODE_11N; + } + *band = wlan_reg_freq_to_band(opr_ch_freq); + break; + case eCSR_CFG_DOT11_MODE_11AC_ONLY: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) { + /* + * If the operating channel is in 2.4 GHz band, check + * for INI item to disable VHT operation in 2.4 GHz band + */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(opr_ch_freq) && + !vht_24_ghz) + /* Disable 11AC operation */ + *dot11_mode = eCSR_CFG_DOT11_MODE_11N; + else + *dot11_mode = eCSR_CFG_DOT11_MODE_11AC_ONLY; + } else { + *dot11_mode = eCSR_CFG_DOT11_MODE_11N; + } + *band = wlan_reg_freq_to_band(opr_ch_freq); + break; + case eCSR_CFG_DOT11_MODE_11AX: + case eCSR_CFG_DOT11_MODE_11AX_ONLY: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) { + *dot11_mode = mac_ctx->roam.configParam.uCfgDot11Mode; + } else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) { + /* + * If the operating channel is in 2.4 GHz band, check + * for INI item to disable VHT operation in 2.4 GHz band + */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(opr_ch_freq) && + !vht_24_ghz) + /* Disable 11AC operation */ + *dot11_mode = eCSR_CFG_DOT11_MODE_11N; + else + *dot11_mode = eCSR_CFG_DOT11_MODE_11AC; + } else { + *dot11_mode = eCSR_CFG_DOT11_MODE_11N; + } + *band = wlan_reg_freq_to_band(opr_ch_freq); + break; + case eCSR_CFG_DOT11_MODE_AUTO: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) { + *dot11_mode = eCSR_CFG_DOT11_MODE_11AX; + } else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) { + /* + * If the operating channel is in 2.4 GHz band, + * check for INI item to disable VHT operation + * in 2.4 GHz band + */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(opr_ch_freq) && + !vht_24_ghz) + /* Disable 11AC operation */ + *dot11_mode = eCSR_CFG_DOT11_MODE_11N; + else + *dot11_mode = eCSR_CFG_DOT11_MODE_11AC; + } else { + *dot11_mode = eCSR_CFG_DOT11_MODE_11N; + } + *band = wlan_reg_freq_to_band(opr_ch_freq); + break; + default: + /* + * Global dot11 Mode setting is 11a/b/g. use the channel number + * to determine the Mode setting. + */ + if (eCSR_OPERATING_CHANNEL_AUTO == opr_ch_freq) { + *band = (mac_ctx->mlme_cfg->gen.band == BAND_2G ? + REG_BAND_2G : REG_BAND_5G); + if (REG_BAND_2G == *band) { + /* + * See reason in else if ( WLAN_REG_IS_24GHZ_CH + * (opr_ch) ) to pick 11B + */ + *dot11_mode = eCSR_CFG_DOT11_MODE_11B; + } else { + /* prefer 5GHz */ + *band = REG_BAND_5G; + *dot11_mode = eCSR_CFG_DOT11_MODE_11A; + } + } else if (WLAN_REG_IS_24GHZ_CH_FREQ(opr_ch_freq)) { + /* + * WiFi tests require IBSS networks to start in 11b mode + * without any change to the default parameter settings + * on the adapter. We use ACU to start an IBSS through + * creation of a startIBSS profile. This startIBSS + * profile has Auto MACProtocol and the adapter property + * setting for dot11Mode is also AUTO. So in this case, + * let's start the IBSS network in 11b mode instead of + * 11g mode. So this is for Auto=profile->MacProtocol && + * Auto=Global. dot11Mode && profile->channel is < 14, + * then start the IBSS in b mode. + * + * Note: we used to have this start as an 11g IBSS for + * best performance. now to specify that the user will + * have to set the do11Mode in the property page to 11g + * to force it. + */ + *dot11_mode = eCSR_CFG_DOT11_MODE_11B; + *band = REG_BAND_2G; + } else { + /* else, it's a 5.0GHz channel. Set mode to 11a. */ + *dot11_mode = eCSR_CFG_DOT11_MODE_11A; + *band = REG_BAND_5G; + } + break; + } /* switch */ +} + +/** + * csr_roam_get_phy_mode_band_for_bss() - This function returns band and mode + * information. + * @mac_ctx: mac global context + * @profile: bss profile + * @bss_op_ch_freq:operating channel freq in MHz + * @band: out param, band caclculated + * + * This function finds dot11 mode based on current mode, operating channel and + * fw supported modes. The only tricky part is that if phyMode is set to 11abg, + * this function may return eCSR_CFG_DOT11_MODE_11B instead of + * eCSR_CFG_DOT11_MODE_11G if everything is set to auto-pick. + * + * Return: dot11mode + */ +static enum csr_cfgdot11mode +csr_roam_get_phy_mode_band_for_bss(struct mac_context *mac_ctx, + struct csr_roam_profile *profile, + uint32_t bss_op_ch_freq, + enum reg_wifi_band *p_band) +{ + enum reg_wifi_band band = REG_BAND_2G; + uint8_t opr_freq = 0; + enum csr_cfgdot11mode curr_mode = + mac_ctx->roam.configParam.uCfgDot11Mode; + enum csr_cfgdot11mode cfg_dot11_mode = + csr_get_cfg_dot11_mode_from_csr_phy_mode( + profile, + (eCsrPhyMode) profile->phyMode, + mac_ctx->roam.configParam.ProprietaryRatesEnabled); + + if (bss_op_ch_freq) + opr_freq = bss_op_ch_freq; + /* + * If the global setting for dot11Mode is set to auto/abg, we overwrite + * the setting in the profile. + */ + if ((!CSR_IS_INFRA_AP(profile) + && ((eCSR_CFG_DOT11_MODE_AUTO == curr_mode) + || (eCSR_CFG_DOT11_MODE_ABG == curr_mode))) + || (eCSR_CFG_DOT11_MODE_AUTO == cfg_dot11_mode) + || (eCSR_CFG_DOT11_MODE_ABG == cfg_dot11_mode)) { + csr_compute_mode_and_band(mac_ctx, &cfg_dot11_mode, + &band, bss_op_ch_freq); + } /* if( eCSR_CFG_DOT11_MODE_ABG == cfg_dot11_mode ) */ + else { + /* dot11 mode is set, lets pick the band */ + if (0 == opr_freq) { + /* channel is Auto also. */ + if (mac_ctx->mlme_cfg->gen.band == BAND_ALL) { + /* prefer 5GHz */ + band = REG_BAND_5G; + } + } else{ + band = wlan_reg_freq_to_band(bss_op_ch_freq); + } + } + if (p_band) + *p_band = band; + + if (opr_freq == 2484 && wlan_reg_is_24ghz_ch_freq(bss_op_ch_freq)) { + sme_err("Switching to Dot11B mode"); + cfg_dot11_mode = eCSR_CFG_DOT11_MODE_11B; + } + + if (wlan_reg_is_24ghz_ch_freq(bss_op_ch_freq) && + !mac_ctx->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band && + (eCSR_CFG_DOT11_MODE_11AC == cfg_dot11_mode || + eCSR_CFG_DOT11_MODE_11AC_ONLY == cfg_dot11_mode)) + cfg_dot11_mode = eCSR_CFG_DOT11_MODE_11N; + /* + * Incase of WEP Security encryption type is coming as part of add key. + * So while STart BSS dont have information + */ + if ((!CSR_IS_11n_ALLOWED(profile->EncryptionType.encryptionType[0]) + || ((profile->privacy == 1) + && (profile->EncryptionType.encryptionType[0] == + eCSR_ENCRYPT_TYPE_NONE))) + && ((eCSR_CFG_DOT11_MODE_11N == cfg_dot11_mode) || + (eCSR_CFG_DOT11_MODE_11AC == cfg_dot11_mode) || + (eCSR_CFG_DOT11_MODE_11AX == cfg_dot11_mode))) { + /* We cannot do 11n here */ + if (wlan_reg_is_24ghz_ch_freq(bss_op_ch_freq)) + cfg_dot11_mode = eCSR_CFG_DOT11_MODE_11G; + else + cfg_dot11_mode = eCSR_CFG_DOT11_MODE_11A; + } + sme_debug("dot11mode: %d phyMode %d fw sup AX %d", cfg_dot11_mode, + profile->phyMode, IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)); + return cfg_dot11_mode; +} + +QDF_STATUS csr_roam_issue_stop_bss(struct mac_context *mac, + uint32_t sessionId, enum csr_roam_substate NewSubstate) +{ + QDF_STATUS status; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + { + host_log_ibss_pkt_type *pIbssLog; + + WLAN_HOST_DIAG_LOG_ALLOC(pIbssLog, host_log_ibss_pkt_type, + LOG_WLAN_IBSS_C); + if (pIbssLog) { + pIbssLog->eventId = WLAN_IBSS_EVENT_STOP_REQ; + WLAN_HOST_DIAG_LOG_REPORT(pIbssLog); + } + } +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + /* Set the roaming substate to 'stop Bss request'... */ + csr_roam_substate_change(mac, NewSubstate, sessionId); + + /* attempt to stop the Bss (reason code is ignored...) */ + status = csr_send_mb_stop_bss_req_msg(mac, sessionId); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_warn( + "csr_send_mb_stop_bss_req_msg failed with status %d", + status); + } + return status; +} + +QDF_STATUS csr_get_cfg_valid_channels(struct mac_context *mac, + uint32_t *ch_freq_list, + uint32_t *num_ch_freq) +{ + uint8_t num_chan_temp = 0; + int i; + uint32_t *valid_ch_freq_list = + mac->mlme_cfg->reg.valid_channel_freq_list; + + *num_ch_freq = mac->mlme_cfg->reg.valid_channel_list_num; + + for (i = 0; i < *num_ch_freq; i++) { + if (!wlan_reg_is_dsrc_freq(valid_ch_freq_list[i])) { + ch_freq_list[num_chan_temp] = valid_ch_freq_list[i]; + num_chan_temp++; + } + } + + *num_ch_freq = num_chan_temp; + return QDF_STATUS_SUCCESS; +} + +int8_t csr_get_cfg_max_tx_power(struct mac_context *mac, uint32_t ch_freq) +{ + uint32_t cfg_length = 0; + int8_t maxTxPwr = 0; + tSirMacChanInfo *pCountryInfo = NULL; + uint8_t count = 0; + uint8_t maxChannels; + int32_t rem_length = 0; + + if (WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq)) { + cfg_length = mac->mlme_cfg->power.max_tx_power_5.len; + } else if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) { + cfg_length = mac->mlme_cfg->power.max_tx_power_24.len; + + } else if (wlan_reg_is_6ghz_chan_freq(ch_freq)) { + return wlan_reg_get_channel_reg_power_for_freq(mac->pdev, + ch_freq); + } else { + return maxTxPwr; + } + + pCountryInfo = qdf_mem_malloc(cfg_length); + if (!pCountryInfo) + goto error; + + if (WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq)) { + if (cfg_length > CFG_MAX_TX_POWER_5_LEN) + goto error; + qdf_mem_copy(pCountryInfo, + mac->mlme_cfg->power.max_tx_power_5.data, + cfg_length); + } else if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) { + if (cfg_length > CFG_MAX_TX_POWER_2_4_LEN) + goto error; + qdf_mem_copy(pCountryInfo, + mac->mlme_cfg->power.max_tx_power_24.data, + cfg_length); + } + + /* Identify the channel and maxtxpower */ + rem_length = cfg_length; + while (rem_length >= (sizeof(tSirMacChanInfo))) { + maxChannels = pCountryInfo[count].numChannels; + maxTxPwr = pCountryInfo[count].maxTxPower; + count++; + rem_length -= (sizeof(tSirMacChanInfo)); + + if (ch_freq >= pCountryInfo[count].first_freq && + ch_freq < (pCountryInfo[count].first_freq + maxChannels)) { + break; + } + } + +error: + if (pCountryInfo) + qdf_mem_free(pCountryInfo); + + return maxTxPwr; +} + +bool csr_roam_is_channel_valid(struct mac_context *mac, uint32_t chan_freq) +{ + bool valid = false; + uint32_t id_valid_ch; + uint32_t len = sizeof(mac->roam.valid_ch_freq_list); + + if (QDF_IS_STATUS_SUCCESS(csr_get_cfg_valid_channels(mac, + mac->roam.valid_ch_freq_list, &len))) { + for (id_valid_ch = 0; (id_valid_ch < len); + id_valid_ch++) { + if (chan_freq == + mac->roam.valid_ch_freq_list[id_valid_ch]) { + valid = true; + break; + } + } + } + mac->roam.numValidChannels = len; + return valid; +} + +/* This function check and validate whether the NIC can do CB (40MHz) */ +static ePhyChanBondState csr_get_cb_mode_from_ies(struct mac_context *mac, + uint32_t ch_freq, + tDot11fBeaconIEs *pIes) +{ + ePhyChanBondState eRet = PHY_SINGLE_CHANNEL_CENTERED; + uint32_t sec_ch_freq = 0; + uint32_t ChannelBondingMode; + struct ch_params ch_params = {0}; + + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) { + ChannelBondingMode = + mac->roam.configParam.channelBondingMode24GHz; + } else { + ChannelBondingMode = + mac->roam.configParam.channelBondingMode5GHz; + } + + if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE == ChannelBondingMode) + return PHY_SINGLE_CHANNEL_CENTERED; + + /* Figure what the other side's CB mode */ + if (!(pIes->HTCaps.present && (eHT_CHANNEL_WIDTH_40MHZ == + pIes->HTCaps.supportedChannelWidthSet))) { + return PHY_SINGLE_CHANNEL_CENTERED; + } + + /* In Case WPA2 and TKIP is the only one cipher suite in Pairwise */ + if ((pIes->RSN.present && (pIes->RSN.pwise_cipher_suite_count == 1) && + !memcmp(&(pIes->RSN.pwise_cipher_suites[0][0]), + "\x00\x0f\xac\x02", 4)) + /* In Case only WPA1 is supported and TKIP is + * the only one cipher suite in Unicast. + */ + || (!pIes->RSN.present && (pIes->WPA.present && + (pIes->WPA.unicast_cipher_count == 1) && + !memcmp(&(pIes->WPA.unicast_ciphers[0][0]), + "\x00\x50\xf2\x02", 4)))) { + sme_debug("No channel bonding in TKIP mode"); + return PHY_SINGLE_CHANNEL_CENTERED; + } + + if (!pIes->HTInfo.present) + return PHY_SINGLE_CHANNEL_CENTERED; + + /* + * This is called during INFRA STA/CLIENT and should use the merged + * value of supported channel width and recommended tx width as per + * standard + */ + sme_debug("ch freq %d scws %u rtws %u sco %u", ch_freq, + pIes->HTCaps.supportedChannelWidthSet, + pIes->HTInfo.recommendedTxWidthSet, + pIes->HTInfo.secondaryChannelOffset); + + if (pIes->HTInfo.recommendedTxWidthSet == eHT_CHANNEL_WIDTH_40MHZ) + eRet = (ePhyChanBondState)pIes->HTInfo.secondaryChannelOffset; + else + eRet = PHY_SINGLE_CHANNEL_CENTERED; + + switch (eRet) { + case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: + sec_ch_freq = ch_freq + CSR_SEC_CHANNEL_OFFSET; + break; + case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: + sec_ch_freq = ch_freq - CSR_SEC_CHANNEL_OFFSET; + break; + default: + break; + } + + if (eRet != PHY_SINGLE_CHANNEL_CENTERED) { + ch_params.ch_width = CH_WIDTH_40MHZ; + wlan_reg_set_channel_params_for_freq(mac->pdev, ch_freq, + sec_ch_freq, &ch_params); + if (ch_params.ch_width == CH_WIDTH_20MHZ || + ch_params.sec_ch_offset != eRet) { + sme_err("ch freq %d :: Supported HT BW %d and cbmode %d, APs HT BW %d and cbmode %d, so switch to 20Mhz", + ch_freq, ch_params.ch_width, + ch_params.sec_ch_offset, + pIes->HTInfo.recommendedTxWidthSet, eRet); + eRet = PHY_SINGLE_CHANNEL_CENTERED; + } + } + + return eRet; +} + +static bool csr_is_encryption_in_list(struct mac_context *mac, + tCsrEncryptionList *pCipherList, + eCsrEncryptionType encryptionType) +{ + bool fFound = false; + uint32_t idx; + + for (idx = 0; idx < pCipherList->numEntries; idx++) { + if (pCipherList->encryptionType[idx] == encryptionType) { + fFound = true; + break; + } + } + return fFound; +} + +static bool csr_is_auth_in_list(struct mac_context *mac, tCsrAuthList *pAuthList, + enum csr_akm_type authType) +{ + bool fFound = false; + uint32_t idx; + + for (idx = 0; idx < pAuthList->numEntries; idx++) { + if (pAuthList->authType[idx] == authType) { + fFound = true; + break; + } + } + return fFound; +} + +bool csr_is_same_profile(struct mac_context *mac, + tCsrRoamConnectedProfile *pProfile1, + struct csr_roam_profile *pProfile2) +{ + uint32_t i; + bool fCheck = false; + + if (!(pProfile1 && pProfile2)) + return fCheck; + + for (i = 0; i < pProfile2->SSIDs.numOfSSIDs; i++) { + fCheck = csr_is_ssid_match(mac, + pProfile2->SSIDs.SSIDList[i].SSID.ssId, + pProfile2->SSIDs.SSIDList[i].SSID.length, + pProfile1->SSID.ssId, + pProfile1->SSID.length, + false); + if (fCheck) + break; + } + if (!fCheck) + goto exit; + + if (!csr_is_auth_in_list(mac, &pProfile2->AuthType, + pProfile1->AuthType) + || (pProfile2->BSSType != pProfile1->BSSType) + || !csr_is_encryption_in_list(mac, &pProfile2->EncryptionType, + pProfile1->EncryptionType)) { + fCheck = false; + goto exit; + } + if (pProfile1->mdid.mdie_present || pProfile2->mdid.mdie_present) { + if (pProfile1->mdid.mobility_domain != + pProfile2->mdid.mobility_domain) { + fCheck = false; + goto exit; + } + } + /* Match found */ + fCheck = true; +exit: + return fCheck; +} + +static bool csr_roam_is_same_profile_keys(struct mac_context *mac, + tCsrRoamConnectedProfile *pConnProfile, + struct csr_roam_profile *pProfile2) +{ + bool fCheck = false; + int i; + + do { + /* Only check for static WEP */ + if (!csr_is_encryption_in_list + (mac, &pProfile2->EncryptionType, + eCSR_ENCRYPT_TYPE_WEP40_STATICKEY) + && !csr_is_encryption_in_list(mac, + &pProfile2->EncryptionType, + eCSR_ENCRYPT_TYPE_WEP104_STATICKEY)) { + fCheck = true; + break; + } + if (!csr_is_encryption_in_list + (mac, &pProfile2->EncryptionType, + pConnProfile->EncryptionType)) + break; + if (pConnProfile->Keys.defaultIndex != + pProfile2->Keys.defaultIndex) + break; + for (i = 0; i < CSR_MAX_NUM_KEY; i++) { + if (pConnProfile->Keys.KeyLength[i] != + pProfile2->Keys.KeyLength[i]) + break; + if (qdf_mem_cmp(&pConnProfile->Keys.KeyMaterial[i], + &pProfile2->Keys.KeyMaterial[i], + pProfile2->Keys.KeyLength[i])) { + break; + } + } + if (i == CSR_MAX_NUM_KEY) + fCheck = true; + } while (0); + return fCheck; +} + +/** + * csr_populate_basic_rates() - populates OFDM or CCK rates + * @rates: rate struct to populate + * @is_ofdm_rates: true: ofdm rates, false: cck rates + * @is_basic_rates: indicates if rates are to be masked with + * CSR_DOT11_BASIC_RATE_MASK + * + * This function will populate OFDM or CCK rates + * + * Return: void + */ +static void +csr_populate_basic_rates(tSirMacRateSet *rate_set, bool is_ofdm_rates, + bool is_basic_rates) +{ + int i = 0; + uint8_t ofdm_rates[8] = { + SIR_MAC_RATE_6, + SIR_MAC_RATE_9, + SIR_MAC_RATE_12, + SIR_MAC_RATE_18, + SIR_MAC_RATE_24, + SIR_MAC_RATE_36, + SIR_MAC_RATE_48, + SIR_MAC_RATE_54 + }; + uint8_t cck_rates[4] = { + SIR_MAC_RATE_1, + SIR_MAC_RATE_2, + SIR_MAC_RATE_5_5, + SIR_MAC_RATE_11 + }; + + if (is_ofdm_rates == true) { + rate_set->numRates = 8; + qdf_mem_copy(rate_set->rate, ofdm_rates, sizeof(ofdm_rates)); + if (is_basic_rates) { + rate_set->rate[0] |= CSR_DOT11_BASIC_RATE_MASK; + rate_set->rate[2] |= CSR_DOT11_BASIC_RATE_MASK; + rate_set->rate[4] |= CSR_DOT11_BASIC_RATE_MASK; + } + for (i = 0; i < rate_set->numRates; i++) + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + ("Default OFDM rate is %2x"), rate_set->rate[i]); + } else { + rate_set->numRates = 4; + qdf_mem_copy(rate_set->rate, cck_rates, sizeof(cck_rates)); + if (is_basic_rates) { + rate_set->rate[0] |= CSR_DOT11_BASIC_RATE_MASK; + rate_set->rate[1] |= CSR_DOT11_BASIC_RATE_MASK; + rate_set->rate[2] |= CSR_DOT11_BASIC_RATE_MASK; + rate_set->rate[3] |= CSR_DOT11_BASIC_RATE_MASK; + } + for (i = 0; i < rate_set->numRates; i++) + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + ("Default CCK rate is %2x"), rate_set->rate[i]); + + } +} + +/** + * csr_convert_mode_to_nw_type() - convert mode into network type + * @dot11_mode: dot11_mode + * @band: 2.4 or 5 GHz + * + * Return: tSirNwType + */ +static tSirNwType +csr_convert_mode_to_nw_type(enum csr_cfgdot11mode dot11_mode, + enum reg_wifi_band band) +{ + switch (dot11_mode) { + case eCSR_CFG_DOT11_MODE_11G: + return eSIR_11G_NW_TYPE; + case eCSR_CFG_DOT11_MODE_11B: + return eSIR_11B_NW_TYPE; + case eCSR_CFG_DOT11_MODE_11A: + return eSIR_11A_NW_TYPE; + case eCSR_CFG_DOT11_MODE_11N: + default: + /* + * Because LIM only verifies it against 11a, 11b or 11g, set + * only 11g or 11a here + */ + if (REG_BAND_2G == band) + return eSIR_11G_NW_TYPE; + else + return eSIR_11A_NW_TYPE; + } + return eSIR_DONOT_USE_NW_TYPE; +} + +/** + * csr_populate_supported_rates_from_hostapd() - populates operational + * and extended rates. + * from hostapd.conf file + * @opr_rates: rate struct to populate operational rates + * @ext_rates: rate struct to populate extended rates + * @profile: bss profile + * + * Return: void + */ +static void csr_populate_supported_rates_from_hostapd(tSirMacRateSet *opr_rates, + tSirMacRateSet *ext_rates, + struct csr_roam_profile *profile) +{ + int i = 0; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("supported_rates: %d extended_rates: %d"), + profile->supported_rates.numRates, + profile->extended_rates.numRates); + + if (profile->supported_rates.numRates > WLAN_SUPPORTED_RATES_IE_MAX_LEN) + profile->supported_rates.numRates = + WLAN_SUPPORTED_RATES_IE_MAX_LEN; + + if (profile->extended_rates.numRates > WLAN_SUPPORTED_RATES_IE_MAX_LEN) + profile->extended_rates.numRates = + WLAN_SUPPORTED_RATES_IE_MAX_LEN; + + if (profile->supported_rates.numRates) { + opr_rates->numRates = profile->supported_rates.numRates; + qdf_mem_copy(opr_rates->rate, + profile->supported_rates.rate, + profile->supported_rates.numRates); + for (i = 0; i < opr_rates->numRates; i++) + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Supported Rate is %2x"), opr_rates->rate[i]); + } + if (profile->extended_rates.numRates) { + ext_rates->numRates = + profile->extended_rates.numRates; + qdf_mem_copy(ext_rates->rate, + profile->extended_rates.rate, + profile->extended_rates.numRates); + for (i = 0; i < ext_rates->numRates; i++) + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Extended Rate is %2x"), ext_rates->rate[i]); + } +} + +/** + * csr_roam_get_bss_start_parms() - get bss start param from profile + * @mac: mac global context + * @pProfile: roam profile + * @pParam: out param, start bss params + * @skip_hostapd_rate: to skip given hostapd's rate + * + * This function populates start bss param from roam profile + * + * Return: void + */ +static QDF_STATUS +csr_roam_get_bss_start_parms(struct mac_context *mac, + struct csr_roam_profile *pProfile, + struct csr_roamstart_bssparams *pParam, + bool skip_hostapd_rate) +{ + enum reg_wifi_band band; + uint32_t opr_ch_freq = 0; + tSirNwType nw_type; + uint32_t tmp_opr_ch_freq = 0; + uint8_t h2e; + tSirMacRateSet *opr_rates = &pParam->operationalRateSet; + tSirMacRateSet *ext_rates = &pParam->extendedRateSet; + + if (pProfile->ChannelInfo.numOfChannels && + pProfile->ChannelInfo.freq_list) + tmp_opr_ch_freq = pProfile->ChannelInfo.freq_list[0]; + + pParam->uCfgDot11Mode = + csr_roam_get_phy_mode_band_for_bss(mac, pProfile, + tmp_opr_ch_freq, + &band); + + if (((pProfile->csrPersona == QDF_P2P_CLIENT_MODE) + || (pProfile->csrPersona == QDF_P2P_GO_MODE)) + && (pParam->uCfgDot11Mode == eCSR_CFG_DOT11_MODE_11B)) { + /* This should never happen */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_FATAL, + "For P2P (persona %d) dot11_mode is 11B", + pProfile->csrPersona); + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + nw_type = csr_convert_mode_to_nw_type(pParam->uCfgDot11Mode, band); + ext_rates->numRates = 0; + /* + * hostapd.conf will populate its basic and extended rates + * as per hw_mode, but if acs in ini is enabled driver should + * ignore basic and extended rates from hostapd.conf and should + * populate default rates. + */ + if (!cds_is_sub_20_mhz_enabled() && !skip_hostapd_rate && + (pProfile->supported_rates.numRates || + pProfile->extended_rates.numRates)) { + csr_populate_supported_rates_from_hostapd(opr_rates, + ext_rates, pProfile); + pParam->operation_chan_freq = tmp_opr_ch_freq; + } else { + switch (nw_type) { + default: + sme_err( + "sees an unknown pSirNwType (%d)", + nw_type); + return QDF_STATUS_E_INVAL; + case eSIR_11A_NW_TYPE: + csr_populate_basic_rates(opr_rates, true, true); + if (eCSR_OPERATING_CHANNEL_ANY != tmp_opr_ch_freq) { + opr_ch_freq = tmp_opr_ch_freq; + break; + } + opr_ch_freq = csr_roam_get_ibss_start_chan_freq50(mac); + if (0 == opr_ch_freq && + CSR_IS_PHY_MODE_DUAL_BAND(pProfile->phyMode) && + CSR_IS_PHY_MODE_DUAL_BAND( + mac->roam.configParam.phyMode)) { + /* + * We could not find a 5G channel by auto pick, + * let's try 2.4G channels. We only do this here + * because csr_roam_get_phy_mode_band_for_bss + * always picks 11a for AUTO + */ + nw_type = eSIR_11B_NW_TYPE; + opr_ch_freq = + csr_roam_get_ibss_start_chan_freq24(mac); + csr_populate_basic_rates(opr_rates, false, true); + } + break; + case eSIR_11B_NW_TYPE: + csr_populate_basic_rates(opr_rates, false, true); + if (eCSR_OPERATING_CHANNEL_ANY == tmp_opr_ch_freq) + opr_ch_freq = + csr_roam_get_ibss_start_chan_freq24(mac); + else + opr_ch_freq = tmp_opr_ch_freq; + break; + case eSIR_11G_NW_TYPE: + /* For P2P Client and P2P GO, disable 11b rates */ + if ((pProfile->csrPersona == QDF_P2P_CLIENT_MODE) || + (pProfile->csrPersona == QDF_P2P_GO_MODE) || + (eCSR_CFG_DOT11_MODE_11G_ONLY == + pParam->uCfgDot11Mode)) { + csr_populate_basic_rates(opr_rates, true, true); + } else { + csr_populate_basic_rates(opr_rates, false, + true); + csr_populate_basic_rates(ext_rates, true, + false); + } + if (eCSR_OPERATING_CHANNEL_ANY == tmp_opr_ch_freq) + opr_ch_freq = + csr_roam_get_ibss_start_chan_freq24(mac); + else + opr_ch_freq = tmp_opr_ch_freq; + break; + } + pParam->operation_chan_freq = opr_ch_freq; + } + + if (pProfile->require_h2e) { + h2e = WLAN_BASIC_RATE_MASK | + WLAN_BSS_MEMBERSHIP_SELECTOR_SAE_H2E; + if (ext_rates->numRates < SIR_MAC_MAX_NUMBER_OF_RATES) { + ext_rates->rate[ext_rates->numRates] = h2e; + ext_rates->numRates++; + sme_debug("H2E bss membership add to ext support rate"); + } else if (opr_rates->numRates < SIR_MAC_MAX_NUMBER_OF_RATES) { + opr_rates->rate[opr_rates->numRates] = h2e; + opr_rates->numRates++; + sme_debug("H2E bss membership add to support rate"); + } else { + sme_err("rates full, can not add H2E bss membership"); + } + } + + pParam->sirNwType = nw_type; + pParam->ch_params.ch_width = pProfile->ch_params.ch_width; + pParam->ch_params.center_freq_seg0 = + pProfile->ch_params.center_freq_seg0; + pParam->ch_params.center_freq_seg1 = + pProfile->ch_params.center_freq_seg1; + pParam->ch_params.sec_ch_offset = + pProfile->ch_params.sec_ch_offset; + return QDF_STATUS_SUCCESS; +} + +static void +csr_roam_get_bss_start_parms_from_bss_desc( + struct mac_context *mac, + struct bss_description *bss_desc, + tDot11fBeaconIEs *pIes, + struct csr_roamstart_bssparams *pParam) +{ + if (!pParam) { + sme_err("BSS param's pointer is NULL"); + return; + } + + pParam->sirNwType = bss_desc->nwType; + pParam->cbMode = PHY_SINGLE_CHANNEL_CENTERED; + pParam->operation_chan_freq = bss_desc->chan_freq; + qdf_mem_copy(&pParam->bssid, bss_desc->bssId, + sizeof(struct qdf_mac_addr)); + + if (!pIes) { + pParam->ssId.length = 0; + pParam->operationalRateSet.numRates = 0; + sme_err("IEs struct pointer is NULL"); + return; + } + + if (pIes->SuppRates.present) { + pParam->operationalRateSet.numRates = pIes->SuppRates.num_rates; + if (pIes->SuppRates.num_rates > WLAN_SUPPORTED_RATES_IE_MAX_LEN) { + sme_err( + "num_rates: %d > max val, resetting", + pIes->SuppRates.num_rates); + pIes->SuppRates.num_rates = + WLAN_SUPPORTED_RATES_IE_MAX_LEN; + } + qdf_mem_copy(pParam->operationalRateSet.rate, + pIes->SuppRates.rates, + sizeof(*pIes->SuppRates.rates) * + pIes->SuppRates.num_rates); + } + if (pIes->ExtSuppRates.present) { + pParam->extendedRateSet.numRates = pIes->ExtSuppRates.num_rates; + if (pIes->ExtSuppRates.num_rates > WLAN_SUPPORTED_RATES_IE_MAX_LEN) { + sme_err( + "num_rates: %d > max val, resetting", + pIes->ExtSuppRates.num_rates); + pIes->ExtSuppRates.num_rates = + WLAN_SUPPORTED_RATES_IE_MAX_LEN; + } + qdf_mem_copy(pParam->extendedRateSet.rate, + pIes->ExtSuppRates.rates, + sizeof(*pIes->ExtSuppRates.rates) * + pIes->ExtSuppRates.num_rates); + } + if (pIes->SSID.present) { + pParam->ssId.length = pIes->SSID.num_ssid; + qdf_mem_copy(pParam->ssId.ssId, pIes->SSID.ssid, + pParam->ssId.length); + } + pParam->cbMode = + csr_get_cb_mode_from_ies(mac, pParam->operation_chan_freq, + pIes); +} + +static void csr_roam_determine_max_rate_for_ad_hoc(struct mac_context *mac, + tSirMacRateSet *pSirRateSet) +{ + uint8_t MaxRate = 0; + uint32_t i; + uint8_t *pRate; + + pRate = pSirRateSet->rate; + for (i = 0; i < pSirRateSet->numRates; i++) { + MaxRate = QDF_MAX(MaxRate, (pRate[i] & + (~CSR_DOT11_BASIC_RATE_MASK))); + } + + /* Save the max rate in the connected state information. + * modify LastRates variable as well + */ + +} + +QDF_STATUS csr_roam_issue_start_bss(struct mac_context *mac, uint32_t sessionId, + struct csr_roamstart_bssparams *pParam, + struct csr_roam_profile *pProfile, + struct bss_description *bss_desc, + uint32_t roamId) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + enum reg_wifi_band band; + /* Set the roaming substate to 'Start BSS attempt'... */ + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_START_BSS_REQ, + sessionId); +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + /* Need to figure out whether we need to log WDS??? */ + if (CSR_IS_IBSS(pProfile)) { + host_log_ibss_pkt_type *pIbssLog; + + WLAN_HOST_DIAG_LOG_ALLOC(pIbssLog, host_log_ibss_pkt_type, + LOG_WLAN_IBSS_C); + if (pIbssLog) { + if (bss_desc) { + pIbssLog->eventId = + WLAN_IBSS_EVENT_JOIN_IBSS_REQ; + qdf_mem_copy(pIbssLog->bssid.bytes, + bss_desc->bssId, + QDF_MAC_ADDR_SIZE); + } else + pIbssLog->eventId = + WLAN_IBSS_EVENT_START_IBSS_REQ; + + qdf_mem_copy(pIbssLog->ssid, pParam->ssId.ssId, + pParam->ssId.length); + if (pProfile->ChannelInfo.numOfChannels == 0) + pIbssLog->channelSetting = AUTO_PICK; + else + pIbssLog->channelSetting = SPECIFIED; + + pIbssLog->op_freq = pParam->operation_chan_freq; + pIbssLog->operatingChannel = + wlan_reg_freq_to_chan(mac->pdev, + pIbssLog->op_freq); + WLAN_HOST_DIAG_LOG_REPORT(pIbssLog); + } + } +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + /* Put RSN information in for Starting BSS */ + pParam->nRSNIELength = (uint16_t) pProfile->nRSNReqIELength; + pParam->pRSNIE = pProfile->pRSNReqIE; + + pParam->privacy = pProfile->privacy; + pParam->fwdWPSPBCProbeReq = pProfile->fwdWPSPBCProbeReq; + pParam->authType = pProfile->csr80211AuthType; + pParam->beaconInterval = pProfile->beaconInterval; + pParam->dtimPeriod = pProfile->dtimPeriod; + pParam->ApUapsdEnable = pProfile->ApUapsdEnable; + pParam->ssidHidden = pProfile->SSIDs.SSIDList[0].ssidHidden; + if (CSR_IS_INFRA_AP(pProfile) && (pParam->operation_chan_freq != 0)) { + if (csr_is_valid_channel(mac, pParam->operation_chan_freq) != + QDF_STATUS_SUCCESS) { + pParam->operation_chan_freq = INFRA_AP_DEFAULT_CHAN_FREQ; + pParam->ch_params.ch_width = CH_WIDTH_20MHZ; + } + } + pParam->protEnabled = pProfile->protEnabled; + pParam->obssProtEnabled = pProfile->obssProtEnabled; + pParam->ht_protection = pProfile->cfg_protection; + pParam->wps_state = pProfile->wps_state; + + pParam->uCfgDot11Mode = + csr_roam_get_phy_mode_band_for_bss(mac, pProfile, + pParam->operation_chan_freq, + &band); + pParam->bssPersona = pProfile->csrPersona; + +#ifdef WLAN_FEATURE_11W + pParam->mfpCapable = (0 != pProfile->MFPCapable); + pParam->mfpRequired = (0 != pProfile->MFPRequired); +#endif + + pParam->add_ie_params.probeRespDataLen = + pProfile->add_ie_params.probeRespDataLen; + pParam->add_ie_params.probeRespData_buff = + pProfile->add_ie_params.probeRespData_buff; + + pParam->add_ie_params.assocRespDataLen = + pProfile->add_ie_params.assocRespDataLen; + pParam->add_ie_params.assocRespData_buff = + pProfile->add_ie_params.assocRespData_buff; + + if (CSR_IS_IBSS(pProfile)) { + pParam->add_ie_params.probeRespBCNDataLen = + pProfile->nWPAReqIELength; + pParam->add_ie_params.probeRespBCNData_buff = pProfile->pWPAReqIE; + } else { + pParam->add_ie_params.probeRespBCNDataLen = + pProfile->add_ie_params.probeRespBCNDataLen; + pParam->add_ie_params.probeRespBCNData_buff = + pProfile->add_ie_params.probeRespBCNData_buff; + } + + if (pProfile->csrPersona == QDF_SAP_MODE) + pParam->sap_dot11mc = mac->mlme_cfg->gen.sap_dot11mc; + else + pParam->sap_dot11mc = 1; + + sme_debug("11MC Support Enabled : %d", pParam->sap_dot11mc); + + pParam->cac_duration_ms = pProfile->cac_duration_ms; + pParam->dfs_regdomain = pProfile->dfs_regdomain; + pParam->beacon_tx_rate = pProfile->beacon_tx_rate; + + /* When starting an IBSS, start on the channel from the Profile. */ + status = csr_send_mb_start_bss_req_msg(mac, sessionId, + pProfile->BSSType, pParam, + bss_desc); + return status; +} + +void csr_roam_prepare_bss_params(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *bss_desc, + struct bss_config_param *pBssConfig, + tDot11fBeaconIEs *pIes) +{ + ePhyChanBondState cbMode = PHY_SINGLE_CHANNEL_CENTERED; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + bool skip_hostapd_rate = !pProfile->chan_switch_hostapd_rate_enabled; + + if (!pSession) { + sme_err("session %d not found", sessionId); + return; + } + + if (bss_desc) { + csr_roam_get_bss_start_parms_from_bss_desc(mac, bss_desc, pIes, + &pSession->bssParams); + if (CSR_IS_NDI(pProfile)) { + qdf_copy_macaddr(&pSession->bssParams.bssid, + &pSession->self_mac_addr); + } + } else { + csr_roam_get_bss_start_parms(mac, pProfile, + &pSession->bssParams, + skip_hostapd_rate); + /* Use the first SSID */ + if (pProfile->SSIDs.numOfSSIDs) + qdf_mem_copy(&pSession->bssParams.ssId, + pProfile->SSIDs.SSIDList, + sizeof(tSirMacSSid)); + if (pProfile->BSSIDs.numOfBSSIDs) + /* Use the first BSSID */ + qdf_mem_copy(&pSession->bssParams.bssid, + pProfile->BSSIDs.bssid, + sizeof(struct qdf_mac_addr)); + else + qdf_mem_zero(&pSession->bssParams.bssid, + sizeof(struct qdf_mac_addr)); + } + /* Set operating frequency in pProfile which will be used */ + /* in csr_roam_set_bss_config_cfg() to determine channel bonding */ + /* mode and will be configured in CFG later */ + pProfile->op_freq = pSession->bssParams.operation_chan_freq; + + if (pProfile->op_freq == 0) + sme_err("CSR cannot find a channel to start IBSS"); + else { + csr_roam_determine_max_rate_for_ad_hoc(mac, + &pSession->bssParams. + operationalRateSet); + if (CSR_IS_INFRA_AP(pProfile) || CSR_IS_START_IBSS(pProfile)) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(pProfile->op_freq)) { + cbMode = + mac->roam.configParam. + channelBondingMode24GHz; + } else { + cbMode = + mac->roam.configParam. + channelBondingMode5GHz; + } + sme_debug("## cbMode %d", cbMode); + pBssConfig->cbMode = cbMode; + pSession->bssParams.cbMode = cbMode; + } + } +} + +static void csr_roam_update_connected_profile_from_new_bss(struct mac_context *mac, + uint32_t sessionId, + struct new_bss_info *pNewBss) +{ + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return; + } + + if (pNewBss) { + /* Set the operating frequency. */ + pSession->connectedProfile.op_freq = pNewBss->freq; + /* move the BSSId from the BSS description into the connected + * state information. + */ + qdf_mem_copy(&pSession->connectedProfile.bssid.bytes, + &(pNewBss->bssId), sizeof(struct qdf_mac_addr)); + } +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +void csr_get_pmk_info(struct mac_context *mac_ctx, uint8_t session_id, + tPmkidCacheInfo *pmk_cache) +{ + struct csr_roam_session *session = NULL; + + if (!mac_ctx) { + sme_err("Mac_ctx is NULL"); + return; + } + session = CSR_GET_SESSION(mac_ctx, session_id); + if (!session) { + sme_err("session %d not found", session_id); + return; + } + qdf_mem_copy(pmk_cache->pmk, session->psk_pmk, + sizeof(session->psk_pmk)); + pmk_cache->pmk_len = session->pmk_len; +} + +QDF_STATUS csr_roam_set_psk_pmk(struct mac_context *mac, uint32_t sessionId, + uint8_t *psk_pmk, size_t pmk_len, + bool update_to_fw) +{ + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(pSession->psk_pmk, psk_pmk, sizeof(pSession->psk_pmk)); + pSession->pmk_len = pmk_len; + + if (csr_is_auth_type_ese(mac->roam.roamSession[sessionId]. + connectedProfile.AuthType)) { + sme_debug("PMK update is not required for ESE"); + return QDF_STATUS_SUCCESS; + } + + if (update_to_fw) + csr_roam_update_cfg(mac, sessionId, + REASON_ROAM_PSK_PMK_CHANGED); + + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static void csr_mem_zero_psk_pmk(struct csr_roam_session *session) +{ + qdf_mem_zero(session->psk_pmk, sizeof(session->psk_pmk)); + session->pmk_len = 0; +} +#else +static void csr_mem_zero_psk_pmk(struct csr_roam_session *session) +{ +} +#endif + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +void csr_clear_sae_single_pmk(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, tPmkidCacheInfo *pmk_cache) +{ + struct wlan_objmgr_vdev *vdev; + uint32_t keymgmt; + struct mlme_pmk_info pmk_info; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("vdev is NULL"); + return; + } + + keymgmt = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT); + if (!(keymgmt & (1 << WLAN_CRYPTO_KEY_MGMT_SAE))) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return; + } + if (pmk_cache) { + qdf_mem_copy(&pmk_info.pmk, pmk_cache->pmk, pmk_cache->pmk_len); + pmk_info.pmk_len = pmk_cache->pmk_len; + wlan_mlme_clear_sae_single_pmk_info(vdev, &pmk_info); + } else { + wlan_mlme_clear_sae_single_pmk_info(vdev, NULL); + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); +} + +void +csr_store_sae_single_pmk_to_global_cache(struct mac_context *mac, + struct csr_roam_session *session, + uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_pmk_info *pmk_info; + + if (!session->pConnectBssDesc) + return; + + if (!session->pConnectBssDesc->is_single_pmk) + return; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("vdev is NULL"); + return; + } + + /* + * Mark the AP as single PMK capable in Crypto Table + */ + wlan_crypto_set_sae_single_pmk_bss_cap(vdev, + (struct qdf_mac_addr *)session->pConnectBssDesc->bssId, + true); + + pmk_info = qdf_mem_malloc(sizeof(*pmk_info)); + if (!pmk_info) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return; + } + + qdf_mem_copy(pmk_info->pmk, session->psk_pmk, session->pmk_len); + pmk_info->pmk_len = session->pmk_len; + + wlan_mlme_update_sae_single_pmk(vdev, pmk_info); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + qdf_mem_zero(pmk_info, sizeof(*pmk_info)); + qdf_mem_free(pmk_info); +} +#endif + +QDF_STATUS csr_roam_del_pmkid_from_cache(struct mac_context *mac, + uint32_t sessionId, + tPmkidCacheInfo *pmksa, + bool flush_cache) +{ + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + bool fMatchFound = false; + uint32_t Index; + uint32_t curr_idx; + tPmkidCacheInfo *cached_pmksa; + uint32_t i; + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + /* Check if there are no entries to delete */ + if (0 == pSession->NumPmkidCache) { + sme_debug("No entries to delete/Flush"); + return QDF_STATUS_SUCCESS; + } + + if (flush_cache) { + /* Flush the entire cache */ + qdf_mem_zero(pSession->PmkidCacheInfo, + sizeof(tPmkidCacheInfo) * CSR_MAX_PMKID_ALLOWED); + /* flush single_pmk_info information */ + csr_clear_sae_single_pmk(mac->psoc, pSession->vdev_id, NULL); + pSession->NumPmkidCache = 0; + pSession->curr_cache_idx = 0; + csr_mem_zero_psk_pmk(pSession); + return QDF_STATUS_SUCCESS; + } + + /* clear single_pmk_info information */ + csr_clear_sae_single_pmk(mac->psoc, pSession->vdev_id, pmksa); + + /* !flush_cache - so look up in the cache */ + for (Index = 0; Index < CSR_MAX_PMKID_ALLOWED; Index++) { + cached_pmksa = &pSession->PmkidCacheInfo[Index]; + if ((!cached_pmksa->ssid_len) && + qdf_is_macaddr_equal(&cached_pmksa->BSSID, + &pmksa->BSSID)) + fMatchFound = 1; + + else if (cached_pmksa->ssid_len && + (!qdf_mem_cmp(cached_pmksa->ssid, + pmksa->ssid, pmksa->ssid_len)) && + (!qdf_mem_cmp(cached_pmksa->cache_id, + pmksa->cache_id, CACHE_ID_LEN))) + fMatchFound = 1; + + if (fMatchFound) { + /* Clear this - matched entry */ + qdf_mem_zero(cached_pmksa, + sizeof(tPmkidCacheInfo)); + break; + } + } + + if (Index == CSR_MAX_PMKID_ALLOWED && !fMatchFound) { + sme_debug("No such PMKSA entry exists"); + return QDF_STATUS_SUCCESS; + } + + /* Match Found, Readjust the other entries */ + curr_idx = pSession->curr_cache_idx; + if (Index < curr_idx) { + for (i = Index; i < (curr_idx - 1); i++) { + qdf_mem_copy(&pSession->PmkidCacheInfo[i], + &pSession->PmkidCacheInfo[i + 1], + sizeof(tPmkidCacheInfo)); + } + + pSession->curr_cache_idx--; + qdf_mem_zero(&pSession->PmkidCacheInfo + [pSession->curr_cache_idx], + sizeof(tPmkidCacheInfo)); + } else if (Index > curr_idx) { + for (i = Index; i > (curr_idx); i--) { + qdf_mem_copy(&pSession->PmkidCacheInfo[i], + &pSession->PmkidCacheInfo[i - 1], + sizeof(tPmkidCacheInfo)); + } + + qdf_mem_zero(&pSession->PmkidCacheInfo + [pSession->curr_cache_idx], + sizeof(tPmkidCacheInfo)); + } + + /* Decrement the count since an entry has been deleted */ + pSession->NumPmkidCache--; + sme_debug("PMKID at index=%d deleted, current index=%d cache count=%d", + Index, pSession->curr_cache_idx, pSession->NumPmkidCache); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS csr_roam_get_wpa_rsn_req_ie(struct mac_context *mac, uint32_t sessionId, + uint32_t *pLen, uint8_t *pBuf) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + uint32_t len; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + if (pLen) { + len = *pLen; + *pLen = pSession->nWpaRsnReqIeLength; + if (pBuf) { + if (len >= pSession->nWpaRsnReqIeLength) { + qdf_mem_copy(pBuf, pSession->pWpaRsnReqIE, + pSession->nWpaRsnReqIeLength); + status = QDF_STATUS_SUCCESS; + } + } + } + return status; +} + +QDF_STATUS csr_roam_get_wpa_rsn_rsp_ie(struct mac_context *mac, uint32_t sessionId, + uint32_t *pLen, uint8_t *pBuf) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + uint32_t len; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + if (pLen) { + len = *pLen; + *pLen = pSession->nWpaRsnRspIeLength; + if (pBuf) { + if (len >= pSession->nWpaRsnRspIeLength) { + qdf_mem_copy(pBuf, pSession->pWpaRsnRspIE, + pSession->nWpaRsnRspIeLength); + status = QDF_STATUS_SUCCESS; + } + } + } + return status; +} + +eRoamCmdStatus csr_get_roam_complete_status(struct mac_context *mac, + uint32_t sessionId) +{ + eRoamCmdStatus retStatus = eCSR_ROAM_CONNECT_COMPLETION; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return retStatus; + } + + if (CSR_IS_ROAMING(pSession)) { + retStatus = eCSR_ROAM_ROAMING_COMPLETION; + pSession->fRoaming = false; + } + return retStatus; +} + +static QDF_STATUS csr_roam_start_wds(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *bss_desc) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + struct bss_config_param bssConfig; + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + if (csr_is_conn_state_ibss(mac, sessionId)) { + status = csr_roam_issue_stop_bss(mac, sessionId, + eCSR_ROAM_SUBSTATE_DISCONNECT_CONTINUE_ROAMING); + } else if (csr_is_conn_state_connected_infra(mac, sessionId)) { + /* Disassociate from the connected Infrastructure network.*/ + status = csr_roam_issue_disassociate(mac, sessionId, + eCSR_ROAM_SUBSTATE_DISCONNECT_CONTINUE_ROAMING, + false); + } else { + /* We don't expect Bt-AMP HDD not to disconnect the last + * connection first at this time. Otherwise we need to add + * code to handle the situation just like IBSS. Though for + * WDS station, we need to send disassoc to PE first then + * send stop_bss to PE, before we can continue. + */ + + if (csr_is_conn_state_wds(mac, sessionId)) { + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_zero(&bssConfig, sizeof(struct bss_config_param)); + /* Assume HDD provide bssid in profile */ + qdf_copy_macaddr(&pSession->bssParams.bssid, + pProfile->BSSIDs.bssid); + /* there is no Bss description before we start an WDS so we + * need to adopt all Bss configuration parameters from the + * Profile. + */ + status = csr_roam_prepare_bss_config_from_profile(mac, + pProfile, + &bssConfig, + bss_desc); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Save profile for late use */ + csr_free_roam_profile(mac, sessionId); + pSession->pCurRoamProfile = + qdf_mem_malloc(sizeof(struct csr_roam_profile)); + if (pSession->pCurRoamProfile) { + csr_roam_copy_profile(mac, + pSession->pCurRoamProfile, + pProfile); + } + /* Prepare some more parameters for this WDS */ + csr_roam_prepare_bss_params(mac, sessionId, pProfile, + NULL, &bssConfig, NULL); + status = csr_roam_set_bss_config_cfg(mac, sessionId, + pProfile, NULL, + &bssConfig, NULL, + false); + } + } + + return status; +} + +/** + * csr_add_supported_5Ghz_channels()- Add valid 5Ghz channels + * in Join req. + * @mac_ctx: pointer to global mac structure + * @chan_list: Pointer to channel list buffer to populate + * @num_chan: Pointer to number of channels value to update + * @supp_chan_ie: Boolean to check if we need to populate as IE + * + * This function is called to update valid 5Ghz channels + * in Join req. If @supp_chan_ie is true, supported channels IE + * format[chan num 1, num of channels 1, chan num 2, num of + * channels 2, ..] is populated. Else, @chan_list would be a list + * of supported channels[chan num 1, chan num 2..] + * + * Return: void + */ +static void csr_add_supported_5Ghz_channels(struct mac_context *mac_ctx, + uint8_t *chan_list, + uint8_t *num_chnl, + bool supp_chan_ie) +{ + uint16_t i, j; + uint32_t size = 0; + + if (!chan_list) { + sme_err("chan_list buffer NULL"); + return; + } + + size = sizeof(mac_ctx->roam.valid_ch_freq_list); + if (QDF_IS_STATUS_SUCCESS + (csr_get_cfg_valid_channels(mac_ctx, + mac_ctx->roam.valid_ch_freq_list, + &size))) { + for (i = 0, j = 0; i < size; i++) { + /* Only add 5ghz channels.*/ + if (WLAN_REG_IS_5GHZ_CH_FREQ + (mac_ctx->roam.valid_ch_freq_list[i])) { + chan_list[j] = + wlan_reg_freq_to_chan(mac_ctx->pdev, + mac_ctx->roam.valid_ch_freq_list[i]); + j++; + + if (supp_chan_ie) { + chan_list[j] = 1; + j++; + } + } + } + *num_chnl = (uint8_t)j; + } else { + sme_err("can not find any valid channel"); + *num_chnl = 0; + } +} + +/** + * csr_set_ldpc_exception() - to set allow any LDPC exception permitted + * @mac_ctx: Pointer to mac context + * @session: Pointer to SME/CSR session + * @channel: Given channel number where connection will go + * @usr_cfg_rx_ldpc: User provided RX LDPC setting + * + * This API will check if hardware allows LDPC to be enabled for provided + * channel and user has enabled the RX LDPC selection + * + * Return: QDF_STATUS + */ +static QDF_STATUS csr_set_ldpc_exception(struct mac_context *mac_ctx, + struct csr_roam_session *session, uint32_t ch_freq, + bool usr_cfg_rx_ldpc) +{ + if (!mac_ctx) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "mac_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + if (!session) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "session is NULL"); + return QDF_STATUS_E_FAILURE; + } + if (usr_cfg_rx_ldpc && wma_is_rx_ldpc_supported_for_channel(ch_freq)) { + session->ht_config.ht_rx_ldpc = 1; + session->vht_config.ldpc_coding = 1; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LDPC enable for ch freq[%d]", ch_freq); + } else { + session->ht_config.ht_rx_ldpc = 0; + session->vht_config.ldpc_coding = 0; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LDPC disable for ch freq[%d]", ch_freq); + } + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_11W +/** + * csr_is_mfpc_capable() - is MFPC capable + * @ies: AP information element + * + * Return: true if MFPC capable, false otherwise + */ +bool csr_is_mfpc_capable(struct sDot11fIERSN *rsn) +{ + bool mfpc_capable = false; + + if (rsn && rsn->present && + ((rsn->RSN_Cap[0] >> 7) & 0x01)) + mfpc_capable = true; + + return mfpc_capable; +} + +/** + * csr_set_mgmt_enc_type() - set mgmt enc type for PMF + * @profile: roam profile + * @ies: AP ie + * @csr_join_req: csr join req + * + * Return: void + */ +static void csr_set_mgmt_enc_type(struct csr_roam_profile *profile, + tDot11fBeaconIEs *ies, + struct join_req *csr_join_req) +{ + if (profile->MFPEnabled) + csr_join_req->MgmtEncryptionType = + profile->mgmt_encryption_type; + else + csr_join_req->MgmtEncryptionType = eSIR_ED_NONE; + + if (profile->MFPEnabled && + !(profile->MFPRequired) && + !csr_is_mfpc_capable(&ies->RSN)) + csr_join_req->MgmtEncryptionType = eSIR_ED_NONE; +} +#else +static inline void csr_set_mgmt_enc_type(struct csr_roam_profile *profile, + tDot11fBeaconIEs *pIes, + struct join_req *csr_join_req) +{ +} +#endif + +#ifdef WLAN_FEATURE_FILS_SK +/* + * csr_validate_and_update_fils_info: Copy fils connection info to join request + * @profile: pointer to profile + * @csr_join_req: csr join request + * + * Return: None + */ +static QDF_STATUS +csr_validate_and_update_fils_info(struct mac_context *mac, + struct csr_roam_profile *profile, + struct bss_description *bss_desc, + struct join_req *csr_join_req, + uint8_t vdev_id) +{ + uint8_t cache_id[CACHE_ID_LEN] = {0}; + struct qdf_mac_addr bssid; + + if (!profile->fils_con_info) + return QDF_STATUS_SUCCESS; + + if (!profile->fils_con_info->is_fils_connection) { + sme_debug("FILS_PMKSA: Not a FILS connection"); + qdf_mem_zero(&csr_join_req->fils_con_info, + sizeof(struct cds_fils_connection_info)); + return QDF_STATUS_SUCCESS; + } + + if (bss_desc->fils_info_element.is_cache_id_present) { + qdf_mem_copy(cache_id, bss_desc->fils_info_element.cache_id, + CACHE_ID_LEN); + sme_debug("FILS_PMKSA: cache_id[0]:%d, cache_id[1]:%d", + cache_id[0], cache_id[1]); + } + + qdf_mem_copy(bssid.bytes, + csr_join_req->bssDescription.bssId, + QDF_MAC_ADDR_SIZE); + + if ((!profile->fils_con_info->r_rk_length || + !profile->fils_con_info->key_nai_length) && + !bss_desc->fils_info_element.is_cache_id_present && + !csr_lookup_fils_pmkid(mac, vdev_id, cache_id, + csr_join_req->ssId.ssId, + csr_join_req->ssId.length, &bssid)) + return QDF_STATUS_E_FAILURE; + + qdf_mem_copy(&csr_join_req->fils_con_info, + profile->fils_con_info, + sizeof(struct cds_fils_connection_info)); + + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS +csr_validate_and_update_fils_info(struct mac_context *mac, + struct csr_roam_profile *profile, + struct bss_description *bss_desc, + struct join_req *csr_join_req, + uint8_t vdev_id) +{ } +#endif + +#ifdef WLAN_FEATURE_SAE +/* + * csr_update_sae_config() - Copy SAE info to join request + * @csr_join_req: csr join request + * @mac: mac context + * @session: sme session + * + * Return: None + */ +static void csr_update_sae_config(struct join_req *csr_join_req, + struct mac_context *mac, struct csr_roam_session *session) +{ + tPmkidCacheInfo *pmkid_cache; + + pmkid_cache = qdf_mem_malloc(sizeof(*pmkid_cache)); + if (!pmkid_cache) + return; + + qdf_mem_copy(pmkid_cache->BSSID.bytes, + csr_join_req->bssDescription.bssId, + QDF_MAC_ADDR_SIZE); + + csr_join_req->sae_pmk_cached = + csr_lookup_pmkid_using_bssid(mac, session, pmkid_cache); + + qdf_mem_free(pmkid_cache); + + if (!csr_join_req->sae_pmk_cached) + return; + + sme_debug("Found for BSSID=" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(csr_join_req->bssDescription.bssId)); +} +#else +static inline void csr_update_sae_config(struct join_req *csr_join_req, + struct mac_context *mac, struct csr_roam_session *session) +{ } +#endif + +/** + * csr_get_nss_supported_by_sta_and_ap() - finds out nss from session + * and beacon from AP + * @vht_caps: VHT capabilities + * @ht_caps: HT capabilities + * @dot11_mode: dot11 mode + * + * Return: number of nss advertised by beacon + */ +static uint8_t csr_get_nss_supported_by_sta_and_ap(tDot11fIEVHTCaps *vht_caps, + tDot11fIEHTCaps *ht_caps, + tDot11fIEhe_cap *he_cap, + uint32_t dot11_mode) +{ + bool vht_capability, ht_capability, he_capability; + + vht_capability = IS_DOT11_MODE_VHT(dot11_mode); + ht_capability = IS_DOT11_MODE_HT(dot11_mode); + he_capability = IS_DOT11_MODE_HE(dot11_mode); + + if (he_capability && he_cap->present) { + if ((he_cap->rx_he_mcs_map_lt_80 & 0xC0) != 0xC0) + return NSS_4x4_MODE; + + if ((he_cap->rx_he_mcs_map_lt_80 & 0x30) != 0x30) + return NSS_3x3_MODE; + + if ((he_cap->rx_he_mcs_map_lt_80 & 0x0C) != 0x0C) + return NSS_2x2_MODE; + } else if (vht_capability && vht_caps->present) { + if ((vht_caps->rxMCSMap & 0xC0) != 0xC0) + return NSS_4x4_MODE; + + if ((vht_caps->rxMCSMap & 0x30) != 0x30) + return NSS_3x3_MODE; + + if ((vht_caps->rxMCSMap & 0x0C) != 0x0C) + return NSS_2x2_MODE; + } else if (ht_capability && ht_caps->present) { + if (ht_caps->supportedMCSSet[3]) + return NSS_4x4_MODE; + + if (ht_caps->supportedMCSSet[2]) + return NSS_3x3_MODE; + + if (ht_caps->supportedMCSSet[1]) + return NSS_2x2_MODE; + } + + return NSS_1x1_MODE; +} + +/** + * csr_check_vendor_ap_3_present() - Check if Vendor AP 3 is present + * @mac_ctx: Pointer to Global MAC structure + * @ie: Pointer to starting IE in Beacon/Probe Response + * @ie_len: Length of all IEs combined + * + * For Vendor AP 3, the condition is that Vendor AP 3 IE should be present + * and Vendor AP 4 IE should not be present. + * If Vendor AP 3 IE is present and Vendor AP 4 IE is also present, + * return false, else return true. + * + * Return: true or false + */ +static bool +csr_check_vendor_ap_3_present(struct mac_context *mac_ctx, uint8_t *ie, + uint16_t ie_len) +{ + bool ret = true; + + if ((wlan_get_vendor_ie_ptr_from_oui(SIR_MAC_VENDOR_AP_3_OUI, + SIR_MAC_VENDOR_AP_3_OUI_LEN, ie, ie_len)) && + (wlan_get_vendor_ie_ptr_from_oui(SIR_MAC_VENDOR_AP_4_OUI, + SIR_MAC_VENDOR_AP_4_OUI_LEN, ie, ie_len))) { + sme_debug("Vendor OUI 3 and Vendor OUI 4 found"); + ret = false; + } + + return ret; +} + +/** + * csr_enable_twt() - Check if its allowed to enable twt for this session + * @ie: pointer to beacon/probe resp ie's + * + * TWT is allowed only if device is in 11ax mode and peer supports + * TWT responder or if QCN ie present. + * + * Return: true or flase + */ +static bool csr_enable_twt(struct mac_context *mac_ctx, tDot11fBeaconIEs *ie) +{ + + if ((IS_FEATURE_SUPPORTED_BY_FW(DOT11AX) || + mac_ctx->mlme_cfg->twt_cfg.is_twt_requestor_enabled) && ie && + (ie->qcn_ie.present || ie->he_cap.twt_responder)) { + sme_debug("TWT is supported, hence disable UAPSD; twt req supp: %d,twt respon supp: %d, QCN_IE: %d", + mac_ctx->mlme_cfg->twt_cfg.is_twt_requestor_enabled, + ie->he_cap.twt_responder, + ie->qcn_ie.present); + return true; + } + return false; +} + +#ifdef WLAN_FEATURE_11AX +static void +csr_update_he_caps_mcs(struct wlan_mlme_cfg *mlme_cfg, + struct csr_roam_session *csr_session) +{ + uint32_t tx_mcs_map = 0; + uint32_t rx_mcs_map = 0; + uint32_t mcs_map = 0; + + rx_mcs_map = mlme_cfg->he_caps.dot11_he_cap.rx_he_mcs_map_lt_80; + tx_mcs_map = mlme_cfg->he_caps.dot11_he_cap.tx_he_mcs_map_lt_80; + mcs_map = rx_mcs_map & 0x3; + + if (csr_session->nss == 1) { + tx_mcs_map = HE_SET_MCS_4_NSS(tx_mcs_map, HE_MCS_DISABLE, 2); + rx_mcs_map = HE_SET_MCS_4_NSS(rx_mcs_map, HE_MCS_DISABLE, 2); + } else { + tx_mcs_map = HE_SET_MCS_4_NSS(tx_mcs_map, mcs_map, 2); + rx_mcs_map = HE_SET_MCS_4_NSS(rx_mcs_map, mcs_map, 2); + } + sme_debug("new HE Nss MCS MAP: Rx 0x%0X, Tx: 0x%0X", + rx_mcs_map, tx_mcs_map); + csr_session->he_config.tx_he_mcs_map_lt_80 = tx_mcs_map; + csr_session->he_config.rx_he_mcs_map_lt_80 = rx_mcs_map; +} +#else +static void +csr_update_he_caps_mcs(struct wlan_mlme_cfg *mlme_cfg, + struct csr_roam_session *csr_session) +{ +} +#endif + +#ifdef WLAN_ADAPTIVE_11R +/** + * csr_get_adaptive_11r_enabled() - Function to check if adaptive 11r + * ini is enabled or disabled + * @mac: pointer to mac context + * + * Return: true if adaptive 11r is enabled + */ +static bool +csr_get_adaptive_11r_enabled(struct mac_context *mac) +{ + return mac->mlme_cfg->lfr.enable_adaptive_11r; +} +#else +static inline bool +csr_get_adaptive_11r_enabled(struct mac_context *mac) +{ + return false; +} +#endif + +static QDF_STATUS csr_check_and_validate_6g_ap(struct mac_context *mac_ctx, + struct bss_description *bss, + struct join_req *csr_join_req, + tDot11fBeaconIEs *ie) +{ + tDot11fIEhe_op *he_op = &ie->he_op; + + if (!wlan_reg_is_6ghz_chan_freq(bss->chan_freq)) + return QDF_STATUS_SUCCESS; + + if (!he_op->oper_info_6g_present) { + sme_err(QDF_MAC_ADDR_FMT" Invalid 6GHZ AP BSS description IE", + QDF_MAC_ADDR_REF(bss->bssId)); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_11AX +static void csr_handle_iot_ap_no_common_he_rates(struct mac_context *mac, + struct csr_roam_session *session, + tDot11fBeaconIEs *ies, + uint32_t *dot11mode) +{ + uint16_t int_mcs; + + /* if the connection is not 11AX mode then return */ + if (*dot11mode != MLME_DOT11_MODE_11AX) + return; + + int_mcs = HE_INTERSECT_MCS(session->he_config.tx_he_mcs_map_lt_80, + ies->he_cap.rx_he_mcs_map_lt_80); + sme_debug("HE self rates %x AP rates %x int_mcs %x vendorIE %d", + session->he_config.rx_he_mcs_map_lt_80, + ies->he_cap.rx_he_mcs_map_lt_80, int_mcs, + ies->vendor_vht_ie.present); + if (ies->he_cap.present) + if ((int_mcs == 0xFFFF) && + (ies->vendor_vht_ie.present || + ies->VHTCaps.present)) { + *dot11mode = MLME_DOT11_MODE_11AC; + sme_debug("No common 11AX rate. Force 11AC connection"); + } +} +#else +static void csr_handle_iot_ap_no_common_he_rates(struct mac_context *mac, + struct csr_roam_session *session, + tDot11fBeaconIEs *ies, + uint32_t *dot11mode) +{ +} +#endif + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * csr_update_sae_single_pmk_ap_cap() - Function to update sae single pmk ap ie + * @mac: pointer to mac context + * @bss_desc: BSS Descriptor + * @vdev_id: Vdev id + * @akm: Akm type + * + * Return: true if sae single pmk feature is enabled + */ +static void +csr_update_sae_single_pmk_ap_cap(struct mac_context *mac, + struct bss_description *bss_desc, + uint8_t vdev_id, enum csr_akm_type akm) +{ + if (akm == eCSR_AUTH_TYPE_SAE && + mac->mlme_cfg->lfr.sae_single_pmk_feature_enabled) + wlan_mlme_set_sae_single_pmk_bss_cap(mac->psoc, vdev_id, + bss_desc->is_single_pmk); +} +#else +static inline void +csr_update_sae_single_pmk_ap_cap(struct mac_context *mac, + struct bss_description *bss_desc, + uint8_t vdev_id, enum csr_akm_type akm) +{ +} +#endif + +static void csr_get_basic_rates(tSirMacRateSet *b_rates, uint32_t chan_freq) +{ + /* + * Some IOT APs don't send supported rates in + * probe resp, hence add BSS basic rates in + * supported rates IE of assoc request. + */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq)) + csr_populate_basic_rates(b_rates, false, true); + else if (WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq)) + csr_populate_basic_rates(b_rates, true, true); +} + +/** + * The communication between HDD and LIM is thru mailbox (MB). + * Both sides will access the data structure "struct join_req". + * The rule is, while the components of "struct join_req" can be accessed in the + * regular way like struct join_req.assocType, this guideline stops at component + * tSirRSNie; + * any acces to the components after tSirRSNie is forbidden because the space + * from tSirRSNie is squeezed with the component "struct bss_description" and + * since the size of actual 'struct bss_description' varies, the receiving side + * should keep in mind not to access the components DIRECTLY after tSirRSNie. + */ +QDF_STATUS csr_send_join_req_msg(struct mac_context *mac, uint32_t sessionId, + struct bss_description *pBssDescription, + struct csr_roam_profile *pProfile, + tDot11fBeaconIEs *pIes, uint16_t messageType) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t acm_mask = 0, uapsd_mask; + uint32_t bss_freq; + uint16_t msgLen, ieLen; + tSirMacRateSet OpRateSet; + tSirMacRateSet ExRateSet; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + uint32_t dw_tmp, dot11mode = 0; + uint8_t *wpaRsnIE = NULL; + uint8_t txBFCsnValue = 0; + struct join_req *csr_join_req; + tSirMacCapabilityInfo *pAP_capabilityInfo; + bool fTmp; + int8_t pwr_limit = 0; + struct ps_global_info *ps_global_info = &mac->sme.ps_global_info; + struct ps_params *ps_param = &ps_global_info->ps_params[sessionId]; + uint8_t ese_config = 0; + tpCsrNeighborRoamControlInfo neigh_roam_info; + uint32_t value = 0, value1 = 0; + bool is_vendor_ap_present; + struct vdev_type_nss *vdev_type_nss; + struct action_oui_search_attr vendor_ap_search_attr; + tDot11fIEVHTCaps *vht_caps = NULL; + bool bvalue = 0; + enum csr_akm_type akm; + bool force_max_nss; + uint8_t ap_nss; + struct wlan_objmgr_vdev *vdev; + bool follow_ap_edca; + bool reconn_after_assoc_timeout = false; + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + /* To satisfy klockworks */ + if (!pBssDescription) { + sme_err(" pBssDescription is NULL"); + return QDF_STATUS_E_FAILURE; + } + neigh_roam_info = &mac->roam.neighborRoamInfo[sessionId]; + bss_freq = pBssDescription->chan_freq; + if ((eWNI_SME_REASSOC_REQ == messageType) || + WLAN_REG_IS_5GHZ_CH_FREQ(bss_freq)) { + pSession->disable_hi_rssi = true; + sme_debug("Disabling HI_RSSI, AP freq=%d, rssi=%d", + pBssDescription->chan_freq, pBssDescription->rssi); + } else { + pSession->disable_hi_rssi = false; + } + + do { + pSession->joinFailStatusCode.status_code = eSIR_SME_SUCCESS; + pSession->joinFailStatusCode.reasonCode = 0; + qdf_mem_copy(&pSession->joinFailStatusCode.bssId, + &pBssDescription->bssId, sizeof(tSirMacAddr)); + /* + * the struct join_req which includes a single + * bssDescription. it includes a single uint32_t for the + * IE fields, but the length field in the bssDescription + * needs to be interpreted to determine length of IE fields + * So, take the size of the struct join_req, subtract size of + * bssDescription, add the number of bytes indicated by the + * length field of the bssDescription, add the size of length + * field because it not included in the length field. + */ + msgLen = sizeof(struct join_req) - sizeof(*pBssDescription) + + pBssDescription->length + + sizeof(pBssDescription->length) + + /* + * add in the size of the WPA IE that + * we may build. + */ + sizeof(tCsrWpaIe) + sizeof(tCsrWpaAuthIe) + + sizeof(uint16_t); + csr_join_req = qdf_mem_malloc(msgLen); + if (!csr_join_req) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + + wpaRsnIE = qdf_mem_malloc(DOT11F_IE_RSN_MAX_LEN); + if (!wpaRsnIE) + status = QDF_STATUS_E_NOMEM; + + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + + status = csr_check_and_validate_6g_ap(mac, pBssDescription, + csr_join_req, pIes); + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + + csr_join_req->messageType = messageType; + csr_join_req->length = msgLen; + csr_join_req->vdev_id = (uint8_t) sessionId; + if (pIes->SSID.present && + !csr_is_nullssid(pIes->SSID.ssid, + pIes->SSID.num_ssid)) { + csr_join_req->ssId.length = pIes->SSID.num_ssid; + qdf_mem_copy(&csr_join_req->ssId.ssId, pIes->SSID.ssid, + pIes->SSID.num_ssid); + } else if (pProfile->SSIDs.numOfSSIDs) { + csr_join_req->ssId.length = + pProfile->SSIDs.SSIDList[0].SSID.length; + qdf_mem_copy(&csr_join_req->ssId.ssId, + pProfile->SSIDs.SSIDList[0].SSID.ssId, + csr_join_req->ssId.length); + } else { + csr_join_req->ssId.length = 0; + } + qdf_mem_copy(&csr_join_req->self_mac_addr, + &pSession->self_mac_addr, + sizeof(tSirMacAddr)); + sme_nofl_info("vdev-%d: Connecting to %.*s " QDF_MAC_ADDR_FMT + " rssi: %d freq: %d akm %d cipher: uc %d mc %d, CC: %c%c", + sessionId, csr_join_req->ssId.length, + csr_join_req->ssId.ssId, + QDF_MAC_ADDR_REF(pBssDescription->bssId), + pBssDescription->rssi, pBssDescription->chan_freq, + pProfile->negotiatedAuthType, + pProfile->negotiatedUCEncryptionType, + pProfile->negotiatedMCEncryptionType, + mac->scan.countryCodeCurrent[0], + mac->scan.countryCodeCurrent[1]); + /* bsstype */ + dw_tmp = csr_translate_bsstype_to_mac_type + (pProfile->BSSType); + csr_join_req->bsstype = dw_tmp; + /* dot11mode */ + dot11mode = + csr_translate_to_wni_cfg_dot11_mode(mac, + pSession->bssParams. + uCfgDot11Mode); + akm = pProfile->negotiatedAuthType; + csr_join_req->akm = csr_convert_csr_to_ani_akm_type(akm); + + csr_update_sae_single_pmk_ap_cap(mac, pBssDescription, + sessionId, akm); + + if (bss_freq <= 2484 && + !mac->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band && + dot11mode == MLME_DOT11_MODE_11AC) { + /* Need to disable VHT operation in 2.4 GHz band */ + dot11mode = MLME_DOT11_MODE_11N; + } + /* + * FIX IOT AP: + * AP capable of HE but doesn't advertize MCS rates for 1x1/2x2. + * In such scenario, associate to AP in VHT mode + */ + csr_handle_iot_ap_no_common_he_rates(mac, pSession, pIes, + &dot11mode); + + ieLen = csr_get_ielen_from_bss_description(pBssDescription); + + /* Fill the Vendor AP search params */ + vendor_ap_search_attr.ie_data = + (uint8_t *)&pBssDescription->ieFields[0]; + vendor_ap_search_attr.ie_length = ieLen; + vendor_ap_search_attr.mac_addr = &pBssDescription->bssId[0]; + vendor_ap_search_attr.nss = csr_get_nss_supported_by_sta_and_ap( + &pIes->VHTCaps, &pIes->HTCaps, + &pIes->he_cap, dot11mode); + vendor_ap_search_attr.ht_cap = pIes->HTCaps.present; + vendor_ap_search_attr.vht_cap = pIes->VHTCaps.present; + vendor_ap_search_attr.enable_2g = + wlan_reg_is_24ghz_ch_freq(bss_freq); + vendor_ap_search_attr.enable_5g = + wlan_reg_is_5ghz_ch_freq(bss_freq); + + if (wlan_reg_is_5ghz_ch_freq(bss_freq)) + vdev_type_nss = &mac->vdev_type_nss_5g; + else + vdev_type_nss = &mac->vdev_type_nss_2g; + if (pSession->pCurRoamProfile->csrPersona == + QDF_P2P_CLIENT_MODE) + pSession->vdev_nss = vdev_type_nss->p2p_cli; + else + pSession->vdev_nss = vdev_type_nss->sta; + pSession->nss = pSession->vdev_nss; + + force_max_nss = ucfg_action_oui_search(mac->psoc, + &vendor_ap_search_attr, + ACTION_OUI_FORCE_MAX_NSS); + + if (!mac->mlme_cfg->vht_caps.vht_cap_info.enable2x2) { + force_max_nss = false; + pSession->nss = 1; + } + + if (!force_max_nss) + ap_nss = csr_get_nss_supported_by_sta_and_ap( + &pIes->VHTCaps, + &pIes->HTCaps, + &pIes->he_cap, + dot11mode); + if (!force_max_nss && pSession->nss > ap_nss) { + pSession->nss = ap_nss; + pSession->vdev_nss = pSession->nss; + } + + if (pSession->nss == 1) + pSession->supported_nss_1x1 = true; + + follow_ap_edca = ucfg_action_oui_search(mac->psoc, + &vendor_ap_search_attr, + ACTION_OUI_DISABLE_AGGRESSIVE_EDCA); + + if (messageType == eWNI_SME_JOIN_REQ && + ucfg_action_oui_search(mac->psoc, &vendor_ap_search_attr, + ACTION_OUI_HOST_RECONN)) + reconn_after_assoc_timeout = true; + mlme_set_reconn_after_assoc_timeout_flag( + mac->psoc, sessionId, + reconn_after_assoc_timeout); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, + sessionId, + WLAN_LEGACY_MAC_ID); + if (vdev) { + mlme_set_follow_ap_edca_flag(vdev, follow_ap_edca); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); + } + + is_vendor_ap_present = + ucfg_action_oui_search(mac->psoc, + &vendor_ap_search_attr, + ACTION_OUI_CONNECT_1X1); + + if (is_vendor_ap_present) { + is_vendor_ap_present = csr_check_vendor_ap_3_present( + mac, + vendor_ap_search_attr.ie_data, + ieLen); + } + + /* + * For WMI_ACTION_OUI_CONNECT_1x1_WITH_1_CHAIN, the host + * sends the NSS as 1 to the FW and the FW then decides + * after receiving the first beacon after connection to + * switch to 1 Tx/Rx Chain. + */ + + if (!is_vendor_ap_present) { + is_vendor_ap_present = + ucfg_action_oui_search(mac->psoc, + &vendor_ap_search_attr, + ACTION_OUI_CONNECT_1X1_WITH_1_CHAIN); + if (is_vendor_ap_present) + sme_debug("1x1 with 1 Chain AP"); + } + + if (is_vendor_ap_present && + !policy_mgr_is_hw_dbs_2x2_capable(mac->psoc) && + ((mac->roam.configParam.is_force_1x1 == + FORCE_1X1_ENABLED_FOR_AS && mac->lteCoexAntShare) || + mac->roam.configParam.is_force_1x1 == + FORCE_1X1_ENABLED_FORCED)) { + pSession->supported_nss_1x1 = true; + pSession->vdev_nss = 1; + pSession->nss = 1; + pSession->nss_forced_1x1 = true; + sme_debug("For special ap, NSS: %d force 1x1 %d", + pSession->nss, + mac->roam.configParam.is_force_1x1); + } + + csr_update_he_caps_mcs(mac->mlme_cfg, pSession); + /* + * If CCK WAR is set for current AP, update to firmware via + * WMI_VDEV_PARAM_ABG_MODE_TX_CHAIN_NUM + */ + is_vendor_ap_present = + ucfg_action_oui_search(mac->psoc, + &vendor_ap_search_attr, + ACTION_OUI_CCKM_1X1); + if (is_vendor_ap_present) { + sme_debug("vdev: %d WMI_VDEV_PARAM_ABG_MODE_TX_CHAIN_NUM 1", + pSession->sessionId); + wma_cli_set_command( + pSession->sessionId, + (int)WMI_VDEV_PARAM_ABG_MODE_TX_CHAIN_NUM, 1, + VDEV_CMD); + } + + /* + * If Switch to 11N WAR is set for current AP, change dot11 + * mode to 11N. + */ + is_vendor_ap_present = + ucfg_action_oui_search(mac->psoc, + &vendor_ap_search_attr, + ACTION_OUI_SWITCH_TO_11N_MODE); + if (mac->roam.configParam.is_force_1x1 && + mac->lteCoexAntShare && + is_vendor_ap_present && + (dot11mode == MLME_DOT11_MODE_ALL || + dot11mode == MLME_DOT11_MODE_11AC || + dot11mode == MLME_DOT11_MODE_11AC_ONLY)) + dot11mode = MLME_DOT11_MODE_11N; + + csr_join_req->supported_nss_1x1 = pSession->supported_nss_1x1; + csr_join_req->vdev_nss = pSession->vdev_nss; + csr_join_req->nss = pSession->nss; + csr_join_req->nss_forced_1x1 = pSession->nss_forced_1x1; + csr_join_req->dot11mode = (uint8_t)dot11mode; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + csr_join_req->cc_switch_mode = + mac->roam.configParam.cc_switch_mode; +#endif + csr_join_req->staPersona = (uint8_t) pProfile->csrPersona; + csr_join_req->wps_registration = pProfile->bWPSAssociation; + csr_join_req->cbMode = (uint8_t) pSession->bssParams.cbMode; + csr_join_req->force_24ghz_in_ht20 = + pProfile->force_24ghz_in_ht20; + pSession->uapsd_mask = pProfile->uapsd_mask; + status = + csr_get_rate_set(mac, pProfile, + (eCsrPhyMode) pProfile->phyMode, + pBssDescription, pIes, &OpRateSet, + &ExRateSet); + if (!csr_enable_twt(mac, pIes)) + ps_param->uapsd_per_ac_bit_mask = pProfile->uapsd_mask; + if (QDF_IS_STATUS_SUCCESS(status)) { + /* OperationalRateSet */ + if (OpRateSet.numRates) { + csr_join_req->operationalRateSet.numRates = + OpRateSet.numRates; + qdf_mem_copy(&csr_join_req->operationalRateSet. + rate, OpRateSet.rate, + OpRateSet.numRates); + } else if (pProfile->phyMode == eCSR_DOT11_MODE_AUTO) { + tSirMacRateSet b_rates = {0}; + + csr_get_basic_rates(&b_rates, + pBssDescription->chan_freq); + csr_join_req->operationalRateSet = b_rates; + } + /* ExtendedRateSet */ + if (ExRateSet.numRates) { + csr_join_req->extendedRateSet.numRates = + ExRateSet.numRates; + qdf_mem_copy(&csr_join_req->extendedRateSet. + rate, ExRateSet.rate, + ExRateSet.numRates); + } else { + csr_join_req->extendedRateSet.numRates = 0; + } + } else if (pProfile->phyMode == eCSR_DOT11_MODE_AUTO) { + tSirMacRateSet b_rates = {0}; + + csr_get_basic_rates(&b_rates, + pBssDescription->chan_freq); + csr_join_req->operationalRateSet = b_rates; + } else { + csr_join_req->operationalRateSet.numRates = 0; + csr_join_req->extendedRateSet.numRates = 0; + } + + if (pBssDescription->adaptive_11r_ap) + pSession->is_adaptive_11r_connection = + csr_get_adaptive_11r_enabled(mac); + else + pSession->is_adaptive_11r_connection = false; + + csr_join_req->is_adaptive_11r_connection = + pSession->is_adaptive_11r_connection; + + /* rsnIE */ + if (csr_is_profile_wpa(pProfile)) { + /* Insert the Wpa IE into the join request */ + ieLen = csr_retrieve_wpa_ie(mac, sessionId, pProfile, + pBssDescription, pIes, + (tCsrWpaIe *) (wpaRsnIE)); + } else if (csr_is_profile_rsn(pProfile)) { + /* Insert the RSN IE into the join request */ + ieLen = + csr_retrieve_rsn_ie(mac, sessionId, pProfile, + pBssDescription, pIes, + (tCsrRSNIe *) (wpaRsnIE)); + csr_join_req->force_rsne_override = + pProfile->force_rsne_override; + } +#ifdef FEATURE_WLAN_WAPI + else if (csr_is_profile_wapi(pProfile)) { + /* Insert the WAPI IE into the join request */ + ieLen = + csr_retrieve_wapi_ie(mac, sessionId, pProfile, + pBssDescription, pIes, + (tCsrWapiIe *) (wpaRsnIE)); + } +#endif /* FEATURE_WLAN_WAPI */ + else + ieLen = 0; + /* remember the IE for future use */ + if (ieLen) { + if (ieLen > DOT11F_IE_RSN_MAX_LEN) { + sme_err("WPA RSN IE length :%d is more than DOT11F_IE_RSN_MAX_LEN, resetting to %d", + ieLen, DOT11F_IE_RSN_MAX_LEN); + ieLen = DOT11F_IE_RSN_MAX_LEN; + } +#ifdef FEATURE_WLAN_WAPI + if (csr_is_profile_wapi(pProfile)) { + /* Check whether we need to allocate more mem */ + if (ieLen > pSession->nWapiReqIeLength) { + if (pSession->pWapiReqIE + && pSession->nWapiReqIeLength) { + qdf_mem_free(pSession-> + pWapiReqIE); + } + pSession->pWapiReqIE = + qdf_mem_malloc(ieLen); + if (!pSession->pWapiReqIE) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + } + pSession->nWapiReqIeLength = ieLen; + qdf_mem_copy(pSession->pWapiReqIE, wpaRsnIE, + ieLen); + csr_join_req->rsnIE.length = ieLen; + qdf_mem_copy(&csr_join_req->rsnIE.rsnIEdata, + wpaRsnIE, ieLen); + } else /* should be WPA/WPA2 otherwise */ +#endif /* FEATURE_WLAN_WAPI */ + { + /* Check whether we need to allocate more mem */ + if (ieLen > pSession->nWpaRsnReqIeLength) { + if (pSession->pWpaRsnReqIE + && pSession->nWpaRsnReqIeLength) { + qdf_mem_free(pSession-> + pWpaRsnReqIE); + } + pSession->pWpaRsnReqIE = + qdf_mem_malloc(ieLen); + if (!pSession->pWpaRsnReqIE) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + } + pSession->nWpaRsnReqIeLength = ieLen; + qdf_mem_copy(pSession->pWpaRsnReqIE, wpaRsnIE, + ieLen); + csr_join_req->rsnIE.length = ieLen; + qdf_mem_copy(&csr_join_req->rsnIE.rsnIEdata, + wpaRsnIE, ieLen); + } + } else { + /* free whatever old info */ + pSession->nWpaRsnReqIeLength = 0; + if (pSession->pWpaRsnReqIE) { + qdf_mem_free(pSession->pWpaRsnReqIE); + pSession->pWpaRsnReqIE = NULL; + } +#ifdef FEATURE_WLAN_WAPI + pSession->nWapiReqIeLength = 0; + if (pSession->pWapiReqIE) { + qdf_mem_free(pSession->pWapiReqIE); + pSession->pWapiReqIE = NULL; + } +#endif /* FEATURE_WLAN_WAPI */ + csr_join_req->rsnIE.length = 0; + } +#ifdef FEATURE_WLAN_ESE + if (eWNI_SME_JOIN_REQ == messageType) + csr_join_req->cckmIE.length = 0; + else if (eWNI_SME_REASSOC_REQ == messageType) { + /* cckmIE */ + if (csr_is_profile_ese(pProfile)) { + /* Insert the CCKM IE into the join request */ + ieLen = pSession->suppCckmIeInfo.cckmIeLen; + qdf_mem_copy((void *)(wpaRsnIE), + pSession->suppCckmIeInfo.cckmIe, + ieLen); + } else + ieLen = 0; + /* + * If present, copy the IE into the + * eWNI_SME_REASSOC_REQ message buffer + */ + if (ieLen) { + /* + * Copy the CCKM IE over from the temp + * buffer (wpaRsnIE) + */ + csr_join_req->cckmIE.length = ieLen; + qdf_mem_copy(&csr_join_req->cckmIE.cckmIEdata, + wpaRsnIE, ieLen); + } else + csr_join_req->cckmIE.length = 0; + } +#endif /* FEATURE_WLAN_ESE */ + /* addIEScan */ + if (pProfile->nAddIEScanLength && pProfile->pAddIEScan) { + ieLen = pProfile->nAddIEScanLength; + if (ieLen > pSession->nAddIEScanLength) { + if (pSession->pAddIEScan + && pSession->nAddIEScanLength) { + qdf_mem_free(pSession->pAddIEScan); + } + pSession->pAddIEScan = qdf_mem_malloc(ieLen); + if (!pSession->pAddIEScan) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + } + pSession->nAddIEScanLength = ieLen; + qdf_mem_copy(pSession->pAddIEScan, pProfile->pAddIEScan, + ieLen); + csr_join_req->addIEScan.length = ieLen; + qdf_mem_copy(&csr_join_req->addIEScan.addIEdata, + pProfile->pAddIEScan, ieLen); + } else { + pSession->nAddIEScanLength = 0; + if (pSession->pAddIEScan) { + qdf_mem_free(pSession->pAddIEScan); + pSession->pAddIEScan = NULL; + } + csr_join_req->addIEScan.length = 0; + } + /* addIEAssoc */ + if (pProfile->nAddIEAssocLength && pProfile->pAddIEAssoc) { + ieLen = pProfile->nAddIEAssocLength; + if (ieLen > pSession->nAddIEAssocLength) { + if (pSession->pAddIEAssoc + && pSession->nAddIEAssocLength) { + qdf_mem_free(pSession->pAddIEAssoc); + } + pSession->pAddIEAssoc = qdf_mem_malloc(ieLen); + if (!pSession->pAddIEAssoc) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + } + pSession->nAddIEAssocLength = ieLen; + qdf_mem_copy(pSession->pAddIEAssoc, + pProfile->pAddIEAssoc, ieLen); + csr_join_req->addIEAssoc.length = ieLen; + qdf_mem_copy(&csr_join_req->addIEAssoc.addIEdata, + pProfile->pAddIEAssoc, ieLen); + } else { + pSession->nAddIEAssocLength = 0; + if (pSession->pAddIEAssoc) { + qdf_mem_free(pSession->pAddIEAssoc); + pSession->pAddIEAssoc = NULL; + } + csr_join_req->addIEAssoc.length = 0; + } + + if (eWNI_SME_REASSOC_REQ == messageType) { + /* Unmask any AC in reassoc that is ACM-set */ + uapsd_mask = (uint8_t) pProfile->uapsd_mask; + if (uapsd_mask && (pBssDescription)) { + if (CSR_IS_QOS_BSS(pIes) + && CSR_IS_UAPSD_BSS(pIes)) +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + acm_mask = + sme_qos_get_acm_mask(mac, + pBssDescription, + pIes); +#endif /* WLAN_MDM_CODE_REDUCTION_OPT */ + else + uapsd_mask = 0; + } + } + + if (!CSR_IS_11n_ALLOWED(pProfile->negotiatedUCEncryptionType)) + csr_join_req->he_with_wep_tkip = + mac->roam.configParam.wep_tkip_in_he; + + csr_join_req->UCEncryptionType = + csr_translate_encrypt_type_to_ed_type + (pProfile->negotiatedUCEncryptionType); + + csr_join_req->MCEncryptionType = + csr_translate_encrypt_type_to_ed_type + (pProfile->negotiatedMCEncryptionType); + csr_set_mgmt_enc_type(pProfile, pIes, csr_join_req); +#ifdef FEATURE_WLAN_ESE + ese_config = mac->mlme_cfg->lfr.ese_enabled; +#endif + pProfile->mdid.mdie_present = pBssDescription->mdiePresent; + if (csr_is_profile11r(mac, pProfile) +#ifdef FEATURE_WLAN_ESE + && + !((pProfile->negotiatedAuthType == + eCSR_AUTH_TYPE_OPEN_SYSTEM) && (pIes->ESEVersion.present) + && (ese_config)) +#endif + ) + csr_join_req->is11Rconnection = true; + else + csr_join_req->is11Rconnection = false; +#ifdef FEATURE_WLAN_ESE + if (true == ese_config) + csr_join_req->isESEFeatureIniEnabled = true; + else + csr_join_req->isESEFeatureIniEnabled = false; + + /* A profile can not be both ESE and 11R. But an 802.11R AP + * may be advertising support for ESE as well. So if we are + * associating Open or explicitly ESE then we will get ESE. + * If we are associating explicitly 11R only then we will get + * 11R. + */ + if ((csr_is_profile_ese(pProfile) || + ((pIes->ESEVersion.present) && + (pProfile->negotiatedAuthType == + eCSR_AUTH_TYPE_OPEN_SYSTEM))) + && (ese_config)) + csr_join_req->isESEconnection = true; + else + csr_join_req->isESEconnection = false; + + if (eWNI_SME_JOIN_REQ == messageType) { + tESETspecInfo eseTspec; + /* + * ESE-Tspec IEs in the ASSOC request is presently not + * supported. so nullify the TSPEC parameters + */ + qdf_mem_zero(&eseTspec, sizeof(tESETspecInfo)); + qdf_mem_copy(&csr_join_req->eseTspecInfo, + &eseTspec, sizeof(tESETspecInfo)); + } else if (eWNI_SME_REASSOC_REQ == messageType) { + if ((csr_is_profile_ese(pProfile) || + ((pIes->ESEVersion.present) + && (pProfile->negotiatedAuthType == + eCSR_AUTH_TYPE_OPEN_SYSTEM))) && + (ese_config)) { + tESETspecInfo eseTspec; + + qdf_mem_zero(&eseTspec, sizeof(tESETspecInfo)); + eseTspec.numTspecs = + sme_qos_ese_retrieve_tspec_info(mac, + sessionId, + (tTspecInfo *) &eseTspec. + tspec[0]); + csr_join_req->eseTspecInfo.numTspecs = + eseTspec.numTspecs; + if (eseTspec.numTspecs) { + qdf_mem_copy(&csr_join_req->eseTspecInfo + .tspec[0], + &eseTspec.tspec[0], + (eseTspec.numTspecs * + sizeof(tTspecInfo))); + } + } else { + tESETspecInfo eseTspec; + /* + * ESE-Tspec IEs in the ASSOC request is + * presently not supported. so nullify the TSPEC + * parameters + */ + qdf_mem_zero(&eseTspec, sizeof(tESETspecInfo)); + qdf_mem_copy(&csr_join_req->eseTspecInfo, + &eseTspec, + sizeof(tESETspecInfo)); + } + } +#endif /* FEATURE_WLAN_ESE */ + if (ese_config + || csr_roam_is_fast_roam_enabled(mac, sessionId)) + csr_join_req->isFastTransitionEnabled = true; + else + csr_join_req->isFastTransitionEnabled = false; + + if (csr_roam_is_fast_roam_enabled(mac, sessionId)) + csr_join_req->isFastRoamIniFeatureEnabled = true; + else + csr_join_req->isFastRoamIniFeatureEnabled = false; + + csr_join_req->txLdpcIniFeatureEnabled = + (uint8_t)mac->mlme_cfg->ht_caps.tx_ldpc_enable; + + if ((csr_is11h_supported(mac)) && + (WLAN_REG_IS_5GHZ_CH_FREQ(bss_freq)) && + (pIes->Country.present) && + (!mac->mlme_cfg->sap_cfg.country_code_priority)) { + csr_save_to_channel_power2_g_5_g(mac, + pIes->Country.num_triplets * + sizeof(tSirMacChanInfo), + (tSirMacChanInfo *) + (&pIes->Country.triplets[0])); + csr_apply_power2_current(mac); + } + /* + * If RX LDPC has been disabled for 2.4GHz channels and enabled + * for 5Ghz for STA like persona then here is how to handle + * those cases (by now channel has been decided). + */ + if (eSIR_INFRASTRUCTURE_MODE == csr_join_req->bsstype || + !policy_mgr_is_dbs_enable(mac->psoc)) + csr_set_ldpc_exception(mac, pSession, + bss_freq, + mac->mlme_cfg->ht_caps. + ht_cap_info.adv_coding_cap); + csr_join_req->ht_config = pSession->ht_config; + csr_join_req->vht_config = pSession->vht_config; + sme_debug("ht capability 0x%x VHT capability 0x%x", + (unsigned int)(*(uint32_t *) &csr_join_req->ht_config), + (unsigned int)(*(uint32_t *) &csr_join_req-> + vht_config)); + + if (IS_DOT11_MODE_HE(csr_join_req->dot11mode)) { + csr_join_req_copy_he_cap(csr_join_req, pSession); + /* change the HE caps like sts per band */ + if (!mac->usr_cfg_tx_bfee_nsts) + CSR_REVISE_REQ_HE_CAP_PER_BAND(csr_join_req, + mac, + bss_freq); + } + + value = mac->mlme_cfg->vht_caps.vht_cap_info.su_bformee; + value1 = mac->mlme_cfg->vht_caps.vht_cap_info.tx_bfee_ant_supp; + + csr_join_req->vht_config.su_beam_formee = value; + + if (pIes->VHTCaps.present) + vht_caps = &pIes->VHTCaps; + else if (pIes->vendor_vht_ie.VHTCaps.present) + vht_caps = &pIes->vendor_vht_ie.VHTCaps; + /* Set BF CSN value only if SU Bformee is enabled */ + if (vht_caps && csr_join_req->vht_config.su_beam_formee) { + txBFCsnValue = (uint8_t)value1; + /* + * Certain commercial AP display a bad behavior when + * CSN value in assoc request is more than AP's CSN. + * Sending absolute self CSN value with such AP leads to + * IOT issues. However this issue is observed only with + * CSN cap of less than 4. To avoid such issues, take a + * min of self and peer CSN while sending ASSOC request. + */ + if (pIes->Vendor1IE.present && + vht_caps->csnofBeamformerAntSup < 4) { + if (vht_caps->csnofBeamformerAntSup) + txBFCsnValue = QDF_MIN(txBFCsnValue, + vht_caps->csnofBeamformerAntSup); + } + } + csr_join_req->vht_config.csnof_beamformer_antSup = txBFCsnValue; + + bvalue = mac->mlme_cfg->vht_caps.vht_cap_info.su_bformer; + /* + * Set SU Bformer only if SU Bformer is enabled in INI + * and AP is SU Bformee capable + */ + if (bvalue && !((IS_BSS_VHT_CAPABLE(pIes->VHTCaps) && + pIes->VHTCaps.suBeamformeeCap) || + (IS_BSS_VHT_CAPABLE(pIes->vendor_vht_ie.VHTCaps) && + pIes->vendor_vht_ie.VHTCaps.suBeamformeeCap))) + bvalue = 0; + + csr_join_req->vht_config.su_beam_former = bvalue; + + /* Set num soundingdim value to 0 if SU Bformer is disabled */ + if (!csr_join_req->vht_config.su_beam_former) + csr_join_req->vht_config.num_soundingdim = 0; + + value = + mac->mlme_cfg->vht_caps.vht_cap_info.enable_mu_bformee; + /* + * Set MU Bformee only if SU Bformee is enabled and + * MU Bformee is enabled in INI + */ + if (value && csr_join_req->vht_config.su_beam_formee && + pIes->VHTCaps.muBeamformerCap) + csr_join_req->vht_config.mu_beam_formee = 1; + else + csr_join_req->vht_config.mu_beam_formee = 0; + + csr_join_req->enableVhtpAid = + mac->mlme_cfg->vht_caps.vht_cap_info.enable_paid; + + csr_join_req->enableVhtGid = + mac->mlme_cfg->vht_caps.vht_cap_info.enable_gid; + + csr_join_req->enableAmpduPs = + (uint8_t)mac->mlme_cfg->ht_caps.enable_ampdu_ps; + + csr_join_req->enableHtSmps = + (uint8_t)mac->mlme_cfg->ht_caps.enable_smps; + + csr_join_req->htSmps = (uint8_t)mac->mlme_cfg->ht_caps.smps; + csr_join_req->send_smps_action = + mac->roam.configParam.send_smps_action; + + csr_join_req->max_amsdu_num = + (uint8_t)mac->mlme_cfg->ht_caps.max_num_amsdu; + + if (mac->roam.roamSession[sessionId].fWMMConnection) + csr_join_req->isWMEenabled = true; + else + csr_join_req->isWMEenabled = false; + + if (mac->roam.roamSession[sessionId].fQOSConnection) + csr_join_req->isQosEnabled = true; + else + csr_join_req->isQosEnabled = false; + + if (pProfile->bOSENAssociation) + csr_join_req->isOSENConnection = true; + else + csr_join_req->isOSENConnection = false; + + /* Fill rrm config parameters */ + qdf_mem_copy(&csr_join_req->rrm_config, + &mac->rrm.rrmConfig, + sizeof(struct rrm_config_param)); + + pAP_capabilityInfo = + (tSirMacCapabilityInfo *) + &pBssDescription->capabilityInfo; + /* + * tell the target AP my 11H capability only if both AP and STA + * support + * 11H and the channel being used is 11a + */ + if (csr_is11h_supported(mac) && pAP_capabilityInfo->spectrumMgt + && eSIR_11A_NW_TYPE == pBssDescription->nwType) { + fTmp = true; + } else + fTmp = false; + + csr_join_req->spectrumMgtIndicator = fTmp; + csr_join_req->powerCap.minTxPower = MIN_TX_PWR_CAP; + /* + * This is required for 11k test VoWiFi Ent: Test 2. + * We need the power capabilities for Assoc Req. + * This macro is provided by the halPhyCfg.h. We pick our + * max and min capability by the halPhy provided macros + * Any change in this power cap IE should also be done + * in csr_update_driver_assoc_ies() which would send + * assoc IE's to FW which is used for LFR3 roaming + * ie. used in reassociation requests from FW. + */ + pwr_limit = csr_get_cfg_max_tx_power(mac, bss_freq); + if (0 != pwr_limit && pwr_limit < MAX_TX_PWR_CAP) + csr_join_req->powerCap.maxTxPower = pwr_limit; + else + csr_join_req->powerCap.maxTxPower = MAX_TX_PWR_CAP; + + csr_add_supported_5Ghz_channels(mac, + csr_join_req->supportedChannels.channelList, + &csr_join_req->supportedChannels.numChnl, + false); + /* Enable UAPSD only if TWT is not supported */ + if (!csr_enable_twt(mac, pIes)) + csr_join_req->uapsdPerAcBitmask = pProfile->uapsd_mask; + /* Move the entire BssDescription into the join request. */ + qdf_mem_copy(&csr_join_req->bssDescription, pBssDescription, + pBssDescription->length + + sizeof(pBssDescription->length)); + + status = csr_validate_and_update_fils_info(mac, pProfile, + pBssDescription, + csr_join_req, + sessionId); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + csr_update_sae_config(csr_join_req, mac, pSession); + /* + * conc_custom_rule1: + * If SAP comes up first and STA comes up later then SAP + * need to follow STA's channel in 2.4Ghz. In following if + * condition we are adding sanity check, just to make sure that + * if this rule is enabled then don't allow STA to connect on + * 5gz channel and also by this time SAP's channel should be the + * same as STA's channel. + */ + if (mac->roam.configParam.conc_custom_rule1) { + if ((0 == mac->roam.configParam. + is_sta_connection_in_5gz_enabled) && + WLAN_REG_IS_5GHZ_CH_FREQ(bss_freq)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "STA-conn on 5G isn't allowed"); + status = QDF_STATUS_E_FAILURE; + break; + } + if (!WLAN_REG_IS_5GHZ_CH_FREQ(bss_freq) && + csr_is_conn_allow_2g_band(mac, bss_freq) == false) { + status = QDF_STATUS_E_FAILURE; + break; + } + } + + /* + * conc_custom_rule2: + * If P2PGO comes up first and STA comes up later then P2PGO + * need to follow STA's channel in 5Ghz. In following if + * condition we are just adding sanity check to make sure that + * by this time P2PGO's channel is same as STA's channel. + */ + if (mac->roam.configParam.conc_custom_rule2 && + !WLAN_REG_IS_24GHZ_CH_FREQ(bss_freq) && + (!csr_is_conn_allow_5g_band(mac, bss_freq))) { + status = QDF_STATUS_E_FAILURE; + break; + } + + if (pSession->pCurRoamProfile->csrPersona == QDF_STA_MODE) + csr_join_req->enable_bcast_probe_rsp = + mac->mlme_cfg->oce.enable_bcast_probe_rsp; + + csr_join_req->enable_session_twt_support = csr_enable_twt(mac, + pIes); + status = umac_send_mb_message_to_mac(csr_join_req); + if (!QDF_IS_STATUS_SUCCESS(status)) { + /* + * umac_send_mb_message_to_mac would've released the mem + * allocated by csr_join_req. Let's make it defensive by + * assigning NULL to the pointer. + */ + csr_join_req = NULL; + break; + } + + if (pProfile->csrPersona == QDF_STA_MODE) + wlan_register_txrx_packetdump(OL_TXRX_PDEV_ID); + +#ifndef WLAN_MDM_CODE_REDUCTION_OPT + if (eWNI_SME_JOIN_REQ == messageType) { + /* Notify QoS module that join happening */ + pSession->join_bssid_count++; + sme_qos_csr_event_ind(mac, (uint8_t) sessionId, + SME_QOS_CSR_JOIN_REQ, NULL); + } else if (eWNI_SME_REASSOC_REQ == messageType) + /* Notify QoS module that reassoc happening */ + sme_qos_csr_event_ind(mac, (uint8_t) sessionId, + SME_QOS_CSR_REASSOC_REQ, + NULL); +#endif + } while (0); + + /* Clean up the memory in case of any failure */ + if (!QDF_IS_STATUS_SUCCESS(status) && (csr_join_req)) + qdf_mem_free(csr_join_req); + + if (wpaRsnIE) + qdf_mem_free(wpaRsnIE); + + return status; +} + +/* */ +QDF_STATUS csr_send_mb_disassoc_req_msg(struct mac_context *mac, + uint32_t sessionId, + tSirMacAddr bssId, uint16_t reasonCode) +{ + struct disassoc_req *pMsg; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!CSR_IS_SESSION_VALID(mac, sessionId)) + return QDF_STATUS_E_FAILURE; + + pMsg = qdf_mem_malloc(sizeof(*pMsg)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + + pMsg->messageType = eWNI_SME_DISASSOC_REQ; + pMsg->length = sizeof(*pMsg); + pMsg->sessionId = sessionId; + if ((pSession->pCurRoamProfile) + && (CSR_IS_INFRA_AP(pSession->pCurRoamProfile))) { + qdf_mem_copy(&pMsg->bssid.bytes, + &pSession->self_mac_addr, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(&pMsg->peer_macaddr.bytes, + bssId, + QDF_MAC_ADDR_SIZE); + } else { + qdf_mem_copy(&pMsg->bssid.bytes, + bssId, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(&pMsg->peer_macaddr.bytes, + bssId, QDF_MAC_ADDR_SIZE); + } + pMsg->reasonCode = reasonCode; + pMsg->process_ho_fail = (pSession->disconnect_reason == + eSIR_MAC_FW_TRIGGERED_ROAM_FAILURE) ? true : false; + + /* Update the disconnect stats */ + pSession->disconnect_stats.disconnection_cnt++; + pSession->disconnect_stats.disconnection_by_app++; + + /* + * The state will be DISASSOC_HANDOFF only when we are doing + * handoff. Here we should not send the disassoc over the air + * to the AP + */ + if ((CSR_IS_ROAM_SUBSTATE_DISASSOC_HO(mac, sessionId) + && csr_roam_is11r_assoc(mac, sessionId)) || + pMsg->process_ho_fail) { + /* Set DoNotSendOverTheAir flag to 1 only for handoff case */ + pMsg->doNotSendOverTheAir = CSR_DONT_SEND_DISASSOC_OVER_THE_AIR; + } + return umac_send_mb_message_to_mac(pMsg); +} + +QDF_STATUS csr_send_chng_mcc_beacon_interval(struct mac_context *mac, + uint32_t sessionId) +{ + struct change_bi_params *pMsg; + uint16_t len = 0; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + /* NO need to update the Beacon Params if update beacon parameter flag + * is not set + */ + if (!mac->roam.roamSession[sessionId].bssParams.updatebeaconInterval) + return QDF_STATUS_SUCCESS; + + mac->roam.roamSession[sessionId].bssParams.updatebeaconInterval = + false; + + /* Create the message and send to lim */ + len = sizeof(*pMsg); + pMsg = qdf_mem_malloc(len); + if (!pMsg) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (QDF_IS_STATUS_SUCCESS(status)) { + pMsg->messageType = eWNI_SME_CHNG_MCC_BEACON_INTERVAL; + pMsg->length = len; + + qdf_copy_macaddr(&pMsg->bssid, &pSession->self_mac_addr); + sme_debug("CSR Attempting to change BI for Bssid= " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pMsg->bssid.bytes)); + pMsg->sessionId = sessionId; + sme_debug("session %d BeaconInterval %d", + sessionId, + mac->roam.roamSession[sessionId].bssParams. + beaconInterval); + pMsg->beaconInterval = + mac->roam.roamSession[sessionId].bssParams.beaconInterval; + status = umac_send_mb_message_to_mac(pMsg); + } + return status; +} + +#ifdef QCA_HT_2040_COEX +QDF_STATUS csr_set_ht2040_mode(struct mac_context *mac, uint32_t sessionId, + ePhyChanBondState cbMode, bool obssEnabled) +{ + struct set_ht2040_mode *pMsg; + uint16_t len = 0; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + /* Create the message and send to lim */ + len = sizeof(struct set_ht2040_mode); + pMsg = qdf_mem_malloc(len); + if (!pMsg) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_zero(pMsg, sizeof(struct set_ht2040_mode)); + pMsg->messageType = eWNI_SME_SET_HT_2040_MODE; + pMsg->length = len; + + qdf_copy_macaddr(&pMsg->bssid, &pSession->self_mac_addr); + sme_debug( + "CSR Attempting to set HT20/40 mode for Bssid= " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pMsg->bssid.bytes)); + pMsg->sessionId = sessionId; + sme_debug(" session %d HT20/40 mode %d", + sessionId, cbMode); + pMsg->cbMode = cbMode; + pMsg->obssEnabled = obssEnabled; + status = umac_send_mb_message_to_mac(pMsg); + } + return status; +} +#endif + +QDF_STATUS csr_send_mb_deauth_req_msg(struct mac_context *mac, + uint32_t vdev_id, + tSirMacAddr bssId, uint16_t reasonCode) +{ + struct deauth_req *pMsg; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, vdev_id); + + if (!CSR_IS_SESSION_VALID(mac, vdev_id)) + return QDF_STATUS_E_FAILURE; + + pMsg = qdf_mem_malloc(sizeof(*pMsg)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + + pMsg->messageType = eWNI_SME_DEAUTH_REQ; + pMsg->length = sizeof(*pMsg); + pMsg->vdev_id = vdev_id; + + if ((pSession->pCurRoamProfile) + && (CSR_IS_INFRA_AP(pSession->pCurRoamProfile))) { + qdf_mem_copy(&pMsg->bssid, + &pSession->self_mac_addr, + QDF_MAC_ADDR_SIZE); + } else { + qdf_mem_copy(&pMsg->bssid, + bssId, QDF_MAC_ADDR_SIZE); + } + + /* Set the peer MAC address before sending the message to LIM */ + qdf_mem_copy(&pMsg->peer_macaddr.bytes, bssId, QDF_MAC_ADDR_SIZE); + pMsg->reasonCode = reasonCode; + + /* Update the disconnect stats */ + pSession->disconnect_stats.disconnection_cnt++; + pSession->disconnect_stats.disconnection_by_app++; + + return umac_send_mb_message_to_mac(pMsg); +} + +QDF_STATUS csr_send_mb_disassoc_cnf_msg(struct mac_context *mac, + struct disassoc_ind *pDisassocInd) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct disassoc_cnf *pMsg; + + do { + pMsg = qdf_mem_malloc(sizeof(*pMsg)); + if (!pMsg) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + pMsg->messageType = eWNI_SME_DISASSOC_CNF; + pMsg->status_code = eSIR_SME_SUCCESS; + pMsg->length = sizeof(*pMsg); + pMsg->vdev_id = pDisassocInd->vdev_id; + qdf_copy_macaddr(&pMsg->peer_macaddr, + &pDisassocInd->peer_macaddr); + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(pMsg); + break; + } + + qdf_copy_macaddr(&pMsg->bssid, &pDisassocInd->bssid); + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(pMsg); + break; + } + + status = umac_send_mb_message_to_mac(pMsg); + } while (0); + return status; +} + +QDF_STATUS csr_send_mb_deauth_cnf_msg(struct mac_context *mac, + struct deauth_ind *pDeauthInd) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct deauth_cnf *pMsg; + + do { + pMsg = qdf_mem_malloc(sizeof(*pMsg)); + if (!pMsg) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + pMsg->messageType = eWNI_SME_DEAUTH_CNF; + pMsg->status_code = eSIR_SME_SUCCESS; + pMsg->length = sizeof(*pMsg); + pMsg->vdev_id = pDeauthInd->vdev_id; + qdf_copy_macaddr(&pMsg->bssid, &pDeauthInd->bssid); + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(pMsg); + break; + } + qdf_copy_macaddr(&pMsg->peer_macaddr, + &pDeauthInd->peer_macaddr); + status = QDF_STATUS_SUCCESS; + if (!QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(pMsg); + break; + } + status = umac_send_mb_message_to_mac(pMsg); + } while (0); + return status; +} + +QDF_STATUS csr_send_assoc_cnf_msg(struct mac_context *mac, + struct assoc_ind *pAssocInd, + QDF_STATUS Halstatus, + enum mac_status_code mac_status_code) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct assoc_cnf *pMsg; + struct scheduler_msg msg = { 0 }; + + sme_debug("HalStatus: %d, mac_status_code %d", + Halstatus, mac_status_code); + do { + pMsg = qdf_mem_malloc(sizeof(*pMsg)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + pMsg->messageType = eWNI_SME_ASSOC_CNF; + pMsg->length = sizeof(*pMsg); + if (QDF_IS_STATUS_SUCCESS(Halstatus)) { + pMsg->status_code = eSIR_SME_SUCCESS; + } else { + pMsg->status_code = eSIR_SME_ASSOC_REFUSED; + pMsg->mac_status_code = mac_status_code; + } + /* bssId */ + qdf_mem_copy(pMsg->bssid.bytes, pAssocInd->bssId, + QDF_MAC_ADDR_SIZE); + /* peerMacAddr */ + qdf_mem_copy(pMsg->peer_macaddr.bytes, pAssocInd->peerMacAddr, + QDF_MAC_ADDR_SIZE); + /* aid */ + pMsg->aid = pAssocInd->aid; + /* OWE IE */ + if (pAssocInd->owe_ie_len) { + pMsg->owe_ie = qdf_mem_malloc(pAssocInd->owe_ie_len); + if (!pMsg->owe_ie) + return QDF_STATUS_E_NOMEM; + qdf_mem_copy(pMsg->owe_ie, pAssocInd->owe_ie, + pAssocInd->owe_ie_len); + pMsg->owe_ie_len = pAssocInd->owe_ie_len; + } + pMsg->need_assoc_rsp_tx_cb = pAssocInd->need_assoc_rsp_tx_cb; + + msg.type = pMsg->messageType; + msg.bodyval = 0; + msg.bodyptr = pMsg; + /* pMsg is freed by umac_send_mb_message_to_mac in anycase*/ + status = scheduler_post_msg_by_priority(QDF_MODULE_ID_PE, &msg, + true); + } while (0); + return status; +} + +QDF_STATUS csr_send_mb_start_bss_req_msg(struct mac_context *mac, uint32_t + sessionId, eCsrRoamBssType bssType, + struct csr_roamstart_bssparams *pParam, + struct bss_description *bss_desc) +{ + struct start_bss_req *pMsg; + uint16_t wTmp; + uint32_t value = 0; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + pSession->joinFailStatusCode.status_code = eSIR_SME_SUCCESS; + pSession->joinFailStatusCode.reasonCode = 0; + pMsg = qdf_mem_malloc(sizeof(*pMsg)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + + pMsg->messageType = eWNI_SME_START_BSS_REQ; + pMsg->vdev_id = sessionId; + pMsg->length = sizeof(*pMsg); + qdf_copy_macaddr(&pMsg->bssid, &pParam->bssid); + /* self_mac_addr */ + qdf_copy_macaddr(&pMsg->self_macaddr, &pSession->self_mac_addr); + /* beaconInterval */ + if (bss_desc && bss_desc->beaconInterval) + wTmp = bss_desc->beaconInterval; + else if (pParam->beaconInterval) + wTmp = pParam->beaconInterval; + else + wTmp = MLME_CFG_BEACON_INTERVAL_DEF; + + csr_validate_mcc_beacon_interval(mac, + pParam->operation_chan_freq, + &wTmp, sessionId, pParam->bssPersona); + /* Update the beacon Interval */ + pParam->beaconInterval = wTmp; + pMsg->beaconInterval = wTmp; + pMsg->dot11mode = + csr_translate_to_wni_cfg_dot11_mode(mac, + pParam->uCfgDot11Mode); +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + pMsg->cc_switch_mode = mac->roam.configParam.cc_switch_mode; +#endif + pMsg->bssType = csr_translate_bsstype_to_mac_type(bssType); + qdf_mem_copy(&pMsg->ssId, &pParam->ssId, sizeof(pParam->ssId)); + pMsg->oper_ch_freq = pParam->operation_chan_freq; + /* What should we really do for the cbmode. */ + pMsg->cbMode = (ePhyChanBondState) pParam->cbMode; + pMsg->vht_channel_width = pParam->ch_params.ch_width; + pMsg->center_freq_seg0 = pParam->ch_params.center_freq_seg0; + pMsg->center_freq_seg1 = pParam->ch_params.center_freq_seg1; + pMsg->sec_ch_offset = pParam->ch_params.sec_ch_offset; + pMsg->privacy = pParam->privacy; + pMsg->apUapsdEnable = pParam->ApUapsdEnable; + pMsg->ssidHidden = pParam->ssidHidden; + pMsg->fwdWPSPBCProbeReq = (uint8_t) pParam->fwdWPSPBCProbeReq; + pMsg->protEnabled = (uint8_t) pParam->protEnabled; + pMsg->obssProtEnabled = (uint8_t) pParam->obssProtEnabled; + /* set cfg related to protection */ + pMsg->ht_capab = pParam->ht_protection; + pMsg->authType = pParam->authType; + pMsg->dtimPeriod = pParam->dtimPeriod; + pMsg->wps_state = pParam->wps_state; + pMsg->isCoalesingInIBSSAllowed = mac->isCoalesingInIBSSAllowed; + pMsg->bssPersona = pParam->bssPersona; + pMsg->txLdpcIniFeatureEnabled = mac->mlme_cfg->ht_caps.tx_ldpc_enable; + + /* + * If RX LDPC has been disabled for 2.4GHz channels and enabled + * for 5Ghz for STA like persona then here is how to handle + * those cases (by now channel has been decided). + */ + if (eSIR_IBSS_MODE == pMsg->bssType || + !policy_mgr_is_dbs_enable(mac->psoc)) + csr_set_ldpc_exception(mac, pSession, + pParam->operation_chan_freq, + mac->mlme_cfg->ht_caps. + ht_cap_info.adv_coding_cap); + + pMsg->vht_config = pSession->vht_config; + pMsg->ht_config = pSession->ht_config; + + value = mac->mlme_cfg->vht_caps.vht_cap_info.su_bformee; + pMsg->vht_config.su_beam_formee = + (uint8_t)value && + (uint8_t)mac->mlme_cfg->vht_caps.vht_cap_info.tx_bfee_sap; + value = MLME_VHT_CSN_BEAMFORMEE_ANT_SUPPORTED_FW_DEF; + pMsg->vht_config.csnof_beamformer_antSup = (uint8_t)value; + pMsg->vht_config.mu_beam_formee = 0; + + sme_debug("ht capability 0x%x VHT capability 0x%x", + (*(uint32_t *) &pMsg->ht_config), + (*(uint32_t *) &pMsg->vht_config)); +#ifdef WLAN_FEATURE_11W + pMsg->pmfCapable = pParam->mfpCapable; + pMsg->pmfRequired = pParam->mfpRequired; +#endif + + if (pParam->nRSNIELength > sizeof(pMsg->rsnIE.rsnIEdata)) { + qdf_mem_free(pMsg); + return QDF_STATUS_E_INVAL; + } + pMsg->rsnIE.length = pParam->nRSNIELength; + qdf_mem_copy(pMsg->rsnIE.rsnIEdata, + pParam->pRSNIE, + pParam->nRSNIELength); + pMsg->nwType = (tSirNwType)pParam->sirNwType; + qdf_mem_copy(&pMsg->operationalRateSet, + &pParam->operationalRateSet, + sizeof(tSirMacRateSet)); + qdf_mem_copy(&pMsg->extendedRateSet, + &pParam->extendedRateSet, + sizeof(tSirMacRateSet)); + + if (IS_DOT11_MODE_HE(pMsg->dot11mode)) { + csr_start_bss_copy_he_cap(pMsg, pSession); + /* change the HE caps like sts per band */ + CSR_REVISE_REQ_HE_CAP_PER_BAND(pMsg, mac, + pParam->operation_chan_freq); + } + + pMsg->add_ie_params = pParam->add_ie_params; + pMsg->obssEnabled = mac->roam.configParam.obssEnabled; + pMsg->sap_dot11mc = pParam->sap_dot11mc; + pMsg->vendor_vht_sap = + mac->mlme_cfg->vht_caps.vht_cap_info.vendor_24ghz_band; + pMsg->cac_duration_ms = pParam->cac_duration_ms; + pMsg->dfs_regdomain = pParam->dfs_regdomain; + pMsg->beacon_tx_rate = pParam->beacon_tx_rate; + + return umac_send_mb_message_to_mac(pMsg); +} + +QDF_STATUS csr_send_mb_stop_bss_req_msg(struct mac_context *mac, + uint32_t sessionId) +{ + struct stop_bss_req *pMsg; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return QDF_STATUS_E_FAILURE; + } + + pMsg = qdf_mem_malloc(sizeof(*pMsg)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + pMsg->messageType = eWNI_SME_STOP_BSS_REQ; + pMsg->sessionId = sessionId; + pMsg->length = sizeof(*pMsg); + pMsg->reasonCode = 0; + qdf_copy_macaddr(&pMsg->bssid, &pSession->connectedProfile.bssid); + return umac_send_mb_message_to_mac(pMsg); +} + +QDF_STATUS csr_reassoc(struct mac_context *mac, uint32_t sessionId, + tCsrRoamModifyProfileFields *pModProfileFields, + uint32_t *pRoamId, bool fForce) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t roamId = 0; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if ((csr_is_conn_state_connected(mac, sessionId)) && + (fForce || (qdf_mem_cmp(&pModProfileFields, + &pSession->connectedProfile. + modifyProfileFields, + sizeof(tCsrRoamModifyProfileFields))))) { + roamId = GET_NEXT_ROAM_ID(&mac->roam); + if (pRoamId) + *pRoamId = roamId; + + status = + csr_roam_issue_reassoc(mac, sessionId, NULL, + pModProfileFields, + eCsrSmeIssuedReassocToSameAP, + roamId, false); + } + return status; +} + +/** + * csr_store_oce_cfg_flags_in_vdev() - fill OCE flags from ini + * @mac: mac_context. + * @vdev: Pointer to pdev obj + * @vdev_id: vdev_id + * + * This API will store the oce flags in vdev mlme priv object + * + * Return: none + */ +static void csr_store_oce_cfg_flags_in_vdev(struct mac_context *mac, + struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id) +{ + uint8_t *vdev_dynamic_oce; + struct wlan_objmgr_vdev *vdev = + wlan_objmgr_get_vdev_by_id_from_pdev(pdev, vdev_id, WLAN_LEGACY_MAC_ID); + + if (!vdev) + return; + + vdev_dynamic_oce = mlme_get_dynamic_oce_flags(vdev); + if (vdev_dynamic_oce) + *vdev_dynamic_oce = mac->mlme_cfg->oce.feature_bitmap; + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID); +} + +static void csr_send_set_ie(uint8_t type, uint8_t sub_type, + uint8_t vdev_id) +{ + struct send_extcap_ie *msg; + QDF_STATUS status; + + sme_debug("send SET IE msg to PE"); + + if (!(type == WLAN_VDEV_MLME_TYPE_STA || + (type == WLAN_VDEV_MLME_TYPE_AP && + sub_type == WLAN_VDEV_MLME_SUBTYPE_P2P_DEVICE))) { + sme_debug("Failed to send set IE req for vdev_%d", vdev_id); + return; + } + + msg = qdf_mem_malloc(sizeof(*msg)); + if (!msg) + return; + + msg->msg_type = eWNI_SME_SET_IE_REQ; + msg->session_id = vdev_id; + msg->length = sizeof(*msg); + status = umac_send_mb_message_to_mac(msg); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_debug("Failed to send set IE req for vdev_%d", vdev_id); +} + +void csr_get_vdev_type_nss(enum QDF_OPMODE dev_mode, uint8_t *nss_2g, + uint8_t *nss_5g) +{ + struct mac_context *mac_ctx = sme_get_mac_context(); + + if (!mac_ctx) { + sme_err("Invalid MAC context"); + return; + } + + switch (dev_mode) { + case QDF_STA_MODE: + *nss_2g = mac_ctx->vdev_type_nss_2g.sta; + *nss_5g = mac_ctx->vdev_type_nss_5g.sta; + break; + case QDF_SAP_MODE: + *nss_2g = mac_ctx->vdev_type_nss_2g.sap; + *nss_5g = mac_ctx->vdev_type_nss_5g.sap; + break; + case QDF_P2P_CLIENT_MODE: + *nss_2g = mac_ctx->vdev_type_nss_2g.p2p_cli; + *nss_5g = mac_ctx->vdev_type_nss_5g.p2p_cli; + break; + case QDF_P2P_GO_MODE: + *nss_2g = mac_ctx->vdev_type_nss_2g.p2p_go; + *nss_5g = mac_ctx->vdev_type_nss_5g.p2p_go; + break; + case QDF_P2P_DEVICE_MODE: + *nss_2g = mac_ctx->vdev_type_nss_2g.p2p_dev; + *nss_5g = mac_ctx->vdev_type_nss_5g.p2p_dev; + break; + case QDF_IBSS_MODE: + *nss_2g = mac_ctx->vdev_type_nss_2g.ibss; + *nss_5g = mac_ctx->vdev_type_nss_5g.ibss; + break; + case QDF_OCB_MODE: + *nss_2g = mac_ctx->vdev_type_nss_2g.ocb; + *nss_5g = mac_ctx->vdev_type_nss_5g.ocb; + break; + case QDF_NAN_DISC_MODE: + *nss_2g = mac_ctx->vdev_type_nss_2g.nan; + *nss_5g = mac_ctx->vdev_type_nss_5g.nan; + break; + case QDF_NDI_MODE: + *nss_2g = mac_ctx->vdev_type_nss_2g.ndi; + *nss_5g = mac_ctx->vdev_type_nss_5g.ndi; + break; + default: + *nss_2g = 1; + *nss_5g = 1; + sme_err("Unknown device mode"); + break; + } + sme_debug("mode - %d: nss_2g - %d, 5g - %d", + dev_mode, *nss_2g, *nss_5g); +} + +QDF_STATUS csr_setup_vdev_session(struct vdev_mlme_obj *vdev_mlme) +{ + QDF_STATUS status; + uint32_t existing_session_id; + struct mlme_ht_capabilities_info *ht_cap_info; + struct csr_roam_session *session; + struct mlme_vht_capabilities_info *vht_cap_info; + u8 vdev_id; + struct qdf_mac_addr *mac_addr; + mac_handle_t mac_handle; + struct mac_context *mac_ctx; + struct wlan_objmgr_vdev *vdev; + + mac_handle = cds_get_context(QDF_MODULE_ID_SME); + mac_ctx = MAC_CONTEXT(mac_handle); + if (!mac_ctx) { + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + if (!(mac_ctx->mlme_cfg)) { + sme_err("invalid mlme cfg"); + return QDF_STATUS_E_FAILURE; + } + vht_cap_info = &mac_ctx->mlme_cfg->vht_caps.vht_cap_info; + + vdev = vdev_mlme->vdev; + + vdev_id = wlan_vdev_get_id(vdev); + mac_addr = (struct qdf_mac_addr *)wlan_vdev_mlme_get_macaddr(vdev); + + /* check to see if the mac address already belongs to a session */ + status = csr_roam_get_session_id_from_bssid(mac_ctx, mac_addr, + &existing_session_id); + if (QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Session %d exists with mac address "QDF_MAC_ADDR_FMT, + existing_session_id, + QDF_MAC_ADDR_REF(mac_addr->bytes)); + return QDF_STATUS_E_FAILURE; + } + + /* attempt to retrieve session for Id */ + session = CSR_GET_SESSION(mac_ctx, vdev_id); + if (!session) { + sme_err("Session does not exist for interface %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + /* check to see if the session is already active */ + if (session->sessionActive) { + sme_err("Cannot re-open active session with Id %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + session->sessionActive = true; + session->sessionId = vdev_id; + + /* Initialize FT related data structures only in STA mode */ + sme_ft_open(MAC_HANDLE(mac_ctx), session->sessionId); + + + qdf_mem_copy(&session->self_mac_addr, mac_addr, + sizeof(struct qdf_mac_addr)); + status = qdf_mc_timer_init(&session->hTimerRoaming, + QDF_TIMER_TYPE_SW, + csr_roam_roaming_timer_handler, + &session->roamingTimerInfo); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("cannot allocate memory for Roaming timer"); + return status; + } + + status = qdf_mc_timer_init(&session->roaming_offload_timer, + QDF_TIMER_TYPE_SW, + csr_roam_roaming_offload_timeout_handler, + &session->roamingTimerInfo); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("mem fail for roaming timer"); + return status; + } + + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("timer init failed for join failure timer"); + return status; + } + + ht_cap_info = &mac_ctx->mlme_cfg->ht_caps.ht_cap_info; + session->ht_config.ht_rx_ldpc = ht_cap_info->adv_coding_cap; + session->ht_config.ht_tx_stbc = ht_cap_info->tx_stbc; + session->ht_config.ht_rx_stbc = ht_cap_info->rx_stbc; + session->ht_config.ht_sgi20 = ht_cap_info->short_gi_20_mhz; + session->ht_config.ht_sgi40 = ht_cap_info->short_gi_40_mhz; + + session->vht_config.max_mpdu_len = vht_cap_info->ampdu_len; + session->vht_config.supported_channel_widthset = + vht_cap_info->supp_chan_width; + session->vht_config.ldpc_coding = vht_cap_info->ldpc_coding_cap; + session->vht_config.shortgi80 = vht_cap_info->short_gi_80mhz; + session->vht_config.shortgi160and80plus80 = + vht_cap_info->short_gi_160mhz; + session->vht_config.tx_stbc = vht_cap_info->tx_stbc; + session->vht_config.rx_stbc = vht_cap_info->rx_stbc; + session->vht_config.su_beam_former = vht_cap_info->su_bformer; + session->vht_config.su_beam_formee = vht_cap_info->su_bformee; + session->vht_config.csnof_beamformer_antSup = + vht_cap_info->tx_bfee_ant_supp; + session->vht_config.num_soundingdim = vht_cap_info->num_soundingdim; + session->vht_config.mu_beam_former = vht_cap_info->mu_bformer; + session->vht_config.mu_beam_formee = vht_cap_info->enable_mu_bformee; + session->vht_config.vht_txops = vht_cap_info->txop_ps; + session->vht_config.htc_vhtcap = vht_cap_info->htc_vhtc; + session->vht_config.rx_antpattern = vht_cap_info->rx_antpattern; + session->vht_config.tx_antpattern = vht_cap_info->tx_antpattern; + + session->vht_config.max_ampdu_lenexp = + vht_cap_info->ampdu_len_exponent; + + csr_update_session_he_cap(mac_ctx, session); + + /* + * Do not advertise requester role for SAP & responder role + * for STA + */ + csr_init_session_twt_cap(session, vdev_mlme->mgmt.generic.type); + + csr_send_set_ie(vdev_mlme->mgmt.generic.type, + vdev_mlme->mgmt.generic.subtype, + wlan_vdev_get_id(vdev)); + + if (vdev_mlme->mgmt.generic.type == WLAN_VDEV_MLME_TYPE_STA) { + csr_store_oce_cfg_flags_in_vdev(mac_ctx, mac_ctx->pdev, + wlan_vdev_get_id(vdev)); + wlan_mlme_update_oce_flags(mac_ctx->pdev); + } + + return status; +} + +QDF_STATUS csr_process_vdev_del_rsp(struct mac_context *mac_ctx, + uint8_t *pmsg) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct del_vdev_params *rsp; + uint8_t vdev_id; + + if (!pmsg) { + sme_err("msg ptr is NULL"); + return status; + } + + rsp = (struct del_vdev_params *)pmsg; + vdev_id = rsp->vdev_id; + sme_debug("vdev delete rsp status = %d", rsp->status); + + /* + * This session is done. This will also flush all the pending command + * for this vdev, as vdev is deleted and no command should be sent + * for this vdev. Active cmnd is e_sme_command_del_vdev and will + * be removed anyway next. + */ + csr_cleanup_vdev_session(mac_ctx, vdev_id); + + if (rsp->sme_callback) { + status = sme_release_global_lock(&mac_ctx->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_debug("Failed to Release Lock"); + else { + rsp->sme_callback(rsp->vdev_id); + status = sme_acquire_global_lock(&mac_ctx->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + } + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +csr_issue_vdev_del_req(struct mac_context *mac_ctx, uint8_t vdev_id, + tSirMacAddr session_mac_addr, + csr_session_close_cb callback, + void *context) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct del_vdev_params *del_vdev_req; + struct scheduler_msg msg = {0}; + + del_vdev_req = qdf_mem_malloc(sizeof(struct del_vdev_params)); + if (!del_vdev_req) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(del_vdev_req->self_mac_addr, + session_mac_addr, sizeof(tSirMacAddr)); + + del_vdev_req->vdev_id = vdev_id; + del_vdev_req->sme_callback = callback; + del_vdev_req->sme_ctx = context; + msg.type = eWNI_SME_VDEV_DELETE_REQ; + msg.reserved = 0; + msg.bodyptr = del_vdev_req; + msg.bodyval = 0; + + sme_debug("sending eWNI_SME_VDEV_DELETE_REQ"); + status = scheduler_post_message( + QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (status != QDF_STATUS_SUCCESS) { + sme_err("wma_post_ctrl_msg failed"); + qdf_mem_free(del_vdev_req); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; + return status; +} + +void csr_cleanup_vdev_session(struct mac_context *mac, uint8_t vdev_id) +{ + if (CSR_IS_SESSION_VALID(mac, vdev_id)) { + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, + vdev_id); + + csr_roam_stop(mac, vdev_id); + + /* Clean up FT related data structures */ + sme_ft_close(MAC_HANDLE(mac), vdev_id); + csr_free_connect_bss_desc(mac, vdev_id); + sme_reset_key(MAC_HANDLE(mac), vdev_id); + csr_reset_cfg_privacy(mac); + csr_flush_roam_scan_chan_lists(mac, vdev_id); + csr_roam_free_connect_profile(&pSession->connectedProfile); + csr_roam_free_connected_info(mac, &pSession->connectedInfo); + csr_roam_free_connected_info(mac, + &pSession->prev_assoc_ap_info); + qdf_mc_timer_destroy(&pSession->hTimerRoaming); + qdf_mc_timer_destroy(&pSession->roaming_offload_timer); + csr_purge_vdev_pending_ser_cmd_list(mac, vdev_id); + csr_init_session(mac, vdev_id); + } +} + +QDF_STATUS csr_roam_vdev_delete(struct mac_context *mac_ctx, + uint8_t vdev_id, bool cleanup) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *session; + + if (!CSR_IS_SESSION_VALID(mac_ctx, vdev_id)) + return QDF_STATUS_E_INVAL; + + session = CSR_GET_SESSION(mac_ctx, vdev_id); + /* Vdev going down stop roaming */ + session->fCancelRoaming = true; + if (cleanup) { + csr_cleanup_vdev_session(mac_ctx, vdev_id); + return status; + } + + if (CSR_IS_WAIT_FOR_KEY(mac_ctx, vdev_id)) { + sme_debug("Stop Wait for key timer and change substate to eCSR_ROAM_SUBSTATE_NONE"); + csr_roam_stop_wait_for_key_timer(mac_ctx); + csr_roam_substate_change(mac_ctx, eCSR_ROAM_SUBSTATE_NONE, + vdev_id); + } + + /* + * Flush only scan commands. Non scan commands should go in sequence + * as expected by firmware and should not be flushed. + */ + csr_purge_vdev_all_scan_ser_cmd_list(mac_ctx, vdev_id); + if (!mac_ctx->session_close_cb) { + sme_err("no close session callback registered"); + return QDF_STATUS_E_FAILURE; + } + status = csr_issue_vdev_del_req(mac_ctx, vdev_id, + session->self_mac_addr.bytes, + mac_ctx->session_close_cb, NULL); + return status; +} + +static void csr_init_session(struct mac_context *mac, uint32_t sessionId) +{ + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) + return; + + pSession->sessionActive = false; + pSession->sessionId = WLAN_UMAC_VDEV_ID_MAX; + pSession->connectState = eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED; + csr_saved_scan_cmd_free_fields(mac, pSession); + csr_free_roam_profile(mac, sessionId); + csr_roam_free_connect_profile(&pSession->connectedProfile); + csr_roam_free_connected_info(mac, &pSession->connectedInfo); + csr_roam_free_connected_info(mac, + &pSession->prev_assoc_ap_info); + csr_free_connect_bss_desc(mac, sessionId); + qdf_mem_zero(&pSession->self_mac_addr, sizeof(struct qdf_mac_addr)); + if (pSession->pWpaRsnReqIE) { + qdf_mem_free(pSession->pWpaRsnReqIE); + pSession->pWpaRsnReqIE = NULL; + } + pSession->nWpaRsnReqIeLength = 0; + if (pSession->pWpaRsnRspIE) { + qdf_mem_free(pSession->pWpaRsnRspIE); + pSession->pWpaRsnRspIE = NULL; + } + pSession->nWpaRsnRspIeLength = 0; +#ifdef FEATURE_WLAN_WAPI + if (pSession->pWapiReqIE) { + qdf_mem_free(pSession->pWapiReqIE); + pSession->pWapiReqIE = NULL; + } + pSession->nWapiReqIeLength = 0; + if (pSession->pWapiRspIE) { + qdf_mem_free(pSession->pWapiRspIE); + pSession->pWapiRspIE = NULL; + } + pSession->nWapiRspIeLength = 0; +#endif /* FEATURE_WLAN_WAPI */ + if (pSession->pAddIEScan) { + qdf_mem_free(pSession->pAddIEScan); + pSession->pAddIEScan = NULL; + } + pSession->nAddIEScanLength = 0; + if (pSession->pAddIEAssoc) { + qdf_mem_free(pSession->pAddIEAssoc); + pSession->pAddIEAssoc = NULL; + } + pSession->nAddIEAssocLength = 0; +} + +QDF_STATUS csr_roam_get_session_id_from_bssid(struct mac_context *mac, + struct qdf_mac_addr *bssid, + uint32_t *pSessionId) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t i; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (CSR_IS_SESSION_VALID(mac, i)) { + if (qdf_is_macaddr_equal(bssid, + &mac->roam.roamSession[i].connectedProfile. + bssid)) { + /* Found it */ + status = QDF_STATUS_SUCCESS; + *pSessionId = i; + break; + } + } + } + return status; +} + +static void csr_roam_link_up(struct mac_context *mac, struct qdf_mac_addr bssid) +{ + uint32_t sessionId = 0; + + /* + * Update the current BSS info in ho control block based on connected + * profile info from pmac global structure + */ + + sme_debug("WLAN link UP with AP= " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(bssid.bytes)); + /* Indicate the neighbor roal algorithm about the connect indication */ + csr_roam_get_session_id_from_bssid(mac, &bssid, + &sessionId); + csr_neighbor_roam_indicate_connect(mac, sessionId, + QDF_STATUS_SUCCESS); +} + +static void csr_roam_link_down(struct mac_context *mac, uint32_t sessionId) +{ + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return; + } + /* Only to handle the case for Handover on infra link */ + if (eCSR_BSS_TYPE_INFRASTRUCTURE != pSession->connectedProfile.BSSType) + return; + /* + * Incase of station mode, immediately stop data transmission whenever + * link down is detected. + */ + if (csr_roam_is_sta_mode(mac, sessionId) + && !CSR_IS_ROAM_SUBSTATE_DISASSOC_HO(mac, sessionId) + && !csr_roam_is11r_assoc(mac, sessionId)) { + sme_debug("Inform Link lost for session %d", + sessionId); + csr_roam_call_callback(mac, sessionId, NULL, 0, + eCSR_ROAM_LOSTLINK, + eCSR_ROAM_RESULT_LOSTLINK); + } + /* Indicate the neighbor roal algorithm about the disconnect + * indication + */ + csr_neighbor_roam_indicate_disconnect(mac, sessionId); + + /* Remove this code once SLM_Sessionization is supported */ + /* BMPS_WORKAROUND_NOT_NEEDED */ + if (!IS_FEATURE_SUPPORTED_BY_FW(SLM_SESSIONIZATION) && + csr_is_infra_ap_started(mac) && + mac->roam.configParam.doBMPSWorkaround) { + mac->roam.configParam.doBMPSWorkaround = 0; + } + +} + +QDF_STATUS csr_get_snr(struct mac_context *mac, + tCsrSnrCallback callback, + struct qdf_mac_addr bssId, void *pContext) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0}; + uint32_t sessionId = WLAN_UMAC_VDEV_ID_MAX; + tAniGetSnrReq *pMsg; + + pMsg = qdf_mem_malloc(sizeof(tAniGetSnrReq)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + + status = csr_roam_get_session_id_from_bssid(mac, &bssId, &sessionId); + if (!QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(pMsg); + sme_err("Couldn't find session_id for given BSSID"); + return status; + } + + pMsg->msgType = eWNI_SME_GET_SNR_REQ; + pMsg->msgLen = (uint16_t) sizeof(tAniGetSnrReq); + pMsg->sessionId = sessionId; + pMsg->snrCallback = callback; + pMsg->pDevContext = pContext; + msg.type = eWNI_SME_GET_SNR_REQ; + msg.bodyptr = pMsg; + msg.reserved = 0; + + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, + &msg)) { + qdf_mem_free((void *)pMsg); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * csr_roam_set_key_mgmt_offload() - enable/disable key mgmt offload + * @mac_ctx: mac context. + * @session_id: Session Identifier + * @roam_key_mgmt_offload_enabled: key mgmt enable/disable flag + * @pmkid_modes: PMKID modes of PMKSA caching and OKC + * + * Return: QDF_STATUS_SUCCESS - CSR updated config successfully. + * Other status means CSR is failed to update. + */ + +QDF_STATUS csr_roam_set_key_mgmt_offload(struct mac_context *mac_ctx, + uint32_t session_id, + bool roam_key_mgmt_offload_enabled, + struct pmkid_mode_bits *pmkid_modes) +{ + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + sme_err("session %d not found", session_id); + return QDF_STATUS_E_FAILURE; + } + session->RoamKeyMgmtOffloadEnabled = roam_key_mgmt_offload_enabled; + session->pmkid_modes.fw_okc = pmkid_modes->fw_okc; + session->pmkid_modes.fw_pmksa_cache = pmkid_modes->fw_pmksa_cache; + return QDF_STATUS_SUCCESS; +} + +/** + * csr_update_roam_scan_ese_params() - Update ESE related params in RSO request + * @req_buf: Roam Scan Offload Request buffer + * @session: Current Roam Session + * + * This API will set the KRK and BTK required in case of Auth Type is CCKM. + * It will also clear the PMK Len as CCKM PMK Caching is not supported + * + * Return: None + */ +#ifdef FEATURE_WLAN_ESE +static +void csr_update_roam_scan_ese_params(struct roam_offload_scan_req *req_buf, + struct csr_roam_session *session) +{ + if (csr_is_auth_type_ese(req_buf->ConnectedNetwork.authentication)) { + qdf_mem_copy(req_buf->KRK, session->eseCckmInfo.krk, + SIR_KRK_KEY_LEN); + qdf_mem_copy(req_buf->BTK, session->eseCckmInfo.btk, + SIR_BTK_KEY_LEN); + req_buf->pmkid_modes.fw_okc = 0; + req_buf->pmkid_modes.fw_pmksa_cache = 0; + req_buf->pmk_len = 0; + qdf_mem_zero(&req_buf->PSK_PMK[0], sizeof(req_buf->PSK_PMK)); + } +} +#else +static inline +void csr_update_roam_scan_ese_params(struct roam_offload_scan_req *req_buf, + struct csr_roam_session *session) +{ +} +#endif + +#ifdef WLAN_ADAPTIVE_11R +static void +csr_update_roam_req_adaptive_11r(struct csr_roam_session *session, + struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buf) +{ + req_buf->is_adaptive_11r_connection = + session->is_adaptive_11r_connection; +} +#else +static void +csr_update_roam_req_adaptive_11r(struct csr_roam_session *session, + struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buf) +{ + req_buf->is_adaptive_11r_connection = false; +} +#endif + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * csr_fill_sae_single_pmk_info() - updates req msg with sae single pmk info + * @mac_ctx: Mac context + * @req_buf: out param, roam offload scan request packet + * @vdev_id: Vdev id + * + * Return: true in case of success + */ +static bool +csr_fill_sae_single_pmk_info(struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buf, + uint8_t vdev_id) +{ + struct wlan_mlme_sae_single_pmk single_pmk; + struct wlan_objmgr_vdev *vdev; + + if (!mac_ctx || !req_buf) { + sme_debug("Invalid session or req buff"); + return false; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("vdev is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_mlme_get_sae_single_pmk_info(vdev, &single_pmk); + + if (single_pmk.pmk_info.pmk_len && single_pmk.sae_single_pmk_ap && + mac_ctx->mlme_cfg->lfr.sae_single_pmk_feature_enabled) { + sme_debug("Update pmk with len %d same_pmk_info %d", + single_pmk.pmk_info.pmk_len, + single_pmk.sae_single_pmk_ap); + /* Update sae same pmk info in rso */ + qdf_mem_copy(req_buf->PSK_PMK, single_pmk.pmk_info.pmk, + sizeof(req_buf->PSK_PMK)); + req_buf->pmk_len = single_pmk.pmk_info.pmk_len; + + req_buf->is_sae_single_pmk = single_pmk.sae_single_pmk_ap; + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return true; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return false; +} +#else +static bool +csr_fill_sae_single_pmk_info(struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buf, + uint8_t vdev_id) +{ + return false; +} +#endif + +/** + * csr_update_roam_scan_offload_request() - updates req msg with roam offload + * parameters + * @mac: mac global context + * @req_buf: out param, roam offload scan request packet + * @session: roam session + * + * Return: void + */ +static void +csr_update_roam_scan_offload_request(struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buf, + struct csr_roam_session *session) +{ + uint32_t pmkid_modes = mac_ctx->mlme_cfg->sta.pmkid_modes; + struct sCsrNeighborRoamControlInfo *neighbor_roam_info; + + neighbor_roam_info = + &mac_ctx->roam.neighborRoamInfo[session->vdev_id]; + + req_buf->roam_offload_enabled = csr_is_roam_offload_enabled(mac_ctx); + if (!req_buf->roam_offload_enabled) + return; + + req_buf->enable_self_bss_roam = + mac_ctx->mlme_cfg->lfr.enable_self_bss_roam; + req_buf->roam_triggers.vdev_id = session->vdev_id; + req_buf->roam_triggers.trigger_bitmap = + mlme_get_roam_trigger_bitmap(mac_ctx->psoc, session->vdev_id); + req_buf->RoamKeyMgmtOffloadEnabled = session->RoamKeyMgmtOffloadEnabled; + req_buf->pmkid_modes.fw_okc = + (pmkid_modes & CFG_PMKID_MODES_OKC) ? 1 : 0; + req_buf->pmkid_modes.fw_pmksa_cache = + (pmkid_modes & CFG_PMKID_MODES_PMKSA_CACHING) ? 1 : 0; + + qdf_mem_copy(&req_buf->roam_params, + &mac_ctx->roam.configParam.roam_params, + sizeof(req_buf->roam_params)); + /* Check whether to send psk_pmk or sae_single pmk info */ + if (!csr_fill_sae_single_pmk_info(mac_ctx, req_buf, + session->sessionId)) { + qdf_mem_copy(req_buf->PSK_PMK, session->psk_pmk, + sizeof(req_buf->PSK_PMK)); + req_buf->pmk_len = session->pmk_len; + } + req_buf->R0KH_ID_Length = session->ftSmeContext.r0kh_id_len; + qdf_mem_copy(req_buf->R0KH_ID, + session->ftSmeContext.r0kh_id, + req_buf->R0KH_ID_Length); + req_buf->Prefer5GHz = (uint8_t)mac_ctx->mlme_cfg->lfr.roam_prefer_5ghz; + req_buf->RoamRssiCatGap = mac_ctx->roam.configParam.bCatRssiOffset; + req_buf->Select5GHzMargin = mac_ctx->mlme_cfg->gen.select_5ghz_margin; + req_buf->ho_delay_for_rx = mac_ctx->mlme_cfg->lfr.ho_delay_for_rx; + req_buf->roam_preauth_retry_count = + mac_ctx->mlme_cfg->lfr.roam_preauth_retry_count; + req_buf->roam_preauth_no_ack_timeout = + mac_ctx->mlme_cfg->lfr.roam_preauth_no_ack_timeout; + req_buf->min_delay_btw_roam_scans = + mac_ctx->mlme_cfg->lfr.min_delay_btw_roam_scans; + req_buf->roam_trigger_reason_bitmask = + mac_ctx->mlme_cfg->lfr.roam_trigger_reason_bitmask; + /* Do not force RSSI triggers in case controlled roaming enable */ + req_buf->roam_force_rssi_trigger = + (!neighbor_roam_info->roam_control_enable && + mac_ctx->mlme_cfg->lfr.roam_force_rssi_trigger); + csr_update_roam_req_adaptive_11r(session, mac_ctx, req_buf); + + req_buf->enable_ft_im_roaming = + mac_ctx->mlme_cfg->lfr.enable_ft_im_roaming; + /* fill bss load triggered roam related configs */ + req_buf->bss_load_trig_enabled = + mac_ctx->mlme_cfg->lfr.bss_load_trig.enabled; + req_buf->bss_load_config.bss_load_threshold = + mac_ctx->mlme_cfg->lfr.bss_load_trig.threshold; + req_buf->bss_load_config.bss_load_sample_time = + mac_ctx->mlme_cfg->lfr.bss_load_trig.sample_time; + req_buf->bss_load_config.rssi_threshold_5ghz = + mac_ctx->mlme_cfg->lfr.bss_load_trig.rssi_threshold_5ghz; + req_buf->bss_load_config.rssi_threshold_24ghz = + mac_ctx->mlme_cfg->lfr.bss_load_trig.rssi_threshold_24ghz; + req_buf->bss_load_config.vdev_id = session->sessionId; + + /* + * Fill the Idle/Disconect roaming ini parameters to be sent to + * firmware + */ + req_buf->disconnect_roam_params.enable = + mac_ctx->mlme_cfg->lfr.enable_disconnect_roam_offload; + req_buf->disconnect_roam_params.vdev_id = session->vdev_id; + req_buf->idle_roam_params.enable = + mac_ctx->mlme_cfg->lfr.enable_idle_roam; + req_buf->idle_roam_params.vdev_id = session->vdev_id; + req_buf->idle_roam_params.conn_ap_rssi_delta = + mac_ctx->mlme_cfg->lfr.idle_roam_rssi_delta; + req_buf->idle_roam_params.inactive_time = + mac_ctx->mlme_cfg->lfr.idle_roam_inactive_time; + req_buf->idle_roam_params.data_pkt_count = + mac_ctx->mlme_cfg->lfr.idle_data_packet_count; + req_buf->idle_roam_params.conn_ap_min_rssi = + mac_ctx->mlme_cfg->lfr.idle_roam_min_rssi; + req_buf->idle_roam_params.band = + mac_ctx->mlme_cfg->lfr.idle_roam_band; + + req_buf->ReassocFailureTimeout = + mac_ctx->mlme_cfg->timeouts.reassoc_failure_timeout; + csr_update_roam_scan_ese_params(req_buf, session); + + req_buf->AcUapsd.acbe_uapsd = SIR_UAPSD_GET(ACBE, session->uapsd_mask); + req_buf->AcUapsd.acbk_uapsd = SIR_UAPSD_GET(ACBK, session->uapsd_mask); + req_buf->AcUapsd.acvi_uapsd = SIR_UAPSD_GET(ACVI, session->uapsd_mask); + req_buf->AcUapsd.acvo_uapsd = SIR_UAPSD_GET(ACVO, session->uapsd_mask); + + req_buf->roaming_scan_policy = + mac_ctx->mlme_cfg->lfr.roaming_scan_policy; +} + +#ifdef WLAN_FEATURE_FIPS +QDF_STATUS +csr_process_roam_pmkid_req_callback(struct mac_context *mac_ctx, + uint8_t vdev_id, + struct roam_pmkid_req_event *src_lst) +{ + struct csr_roam_info *roam_info; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, vdev_id); + struct qdf_mac_addr *dst_list; + QDF_STATUS status; + uint32_t num_entries, i; + + if (!session) + return QDF_STATUS_E_NULL_VALUE; + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + + num_entries = src_lst->num_entries; + for (i = 0; i < num_entries; i++) { + dst_list = &src_lst->ap_bssid[i]; + qdf_copy_macaddr(&roam_info->bssid, dst_list); + + status = csr_roam_call_callback(mac_ctx, vdev_id, roam_info, + 0, eCSR_ROAM_FIPS_PMK_REQUEST, + eCSR_ROAM_RESULT_NONE); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("%s: Trigger pmkid fallback failed", __func__); + qdf_mem_free(roam_info); + return status; + } + } + qdf_mem_free(roam_info); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +csr_roam_pmkid_req_callback(uint8_t vdev_id, + struct roam_pmkid_req_event *src_lst) +{ + QDF_STATUS status; + struct mac_context *mac_ctx; + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + sme_err("%s: NULL mac ptr", __func__); + QDF_ASSERT(0); + return -EINVAL; + } + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("%s: Locking failed, bailing out", __func__); + return status; + } + + status = csr_process_roam_pmkid_req_callback(mac_ctx, vdev_id, + src_lst); + sme_release_global_lock(&mac_ctx->sme); + + return status; +} +#endif /* WLAN_FEATURE_FIPS */ +#else +static inline void +csr_update_roam_scan_offload_request(struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buf, + struct csr_roam_session *session) +{ + req_buf->roam_force_rssi_trigger = + mac_ctx->mlme_cfg->lfr.roam_force_rssi_trigger; +} +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * csr_populate_roam_chan_list() + * parameters + * @mac_ctx: global mac ctx + * @dst: Destination roam network to populate the roam chan list + * @src: Source channel list + * + * Return: QDF_STATUS enumeration + */ +static QDF_STATUS +csr_populate_roam_chan_list(struct mac_context *mac_ctx, + tSirRoamNetworkType *dst, + tCsrChannelInfo *src) +{ + enum band_info band; + uint32_t band_cap; + uint8_t i = 0; + uint8_t num_channels = 0; + uint32_t *freq_lst = src->freq_list; + + /* + * The INI channels need to be filtered with respect to the current band + * that is supported. + */ + band_cap = mac_ctx->mlme_cfg->gen.band_capability; + if (!band_cap) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Invalid band_cap(%d), roam scan offload req aborted", + band_cap); + return QDF_STATUS_E_FAILURE; + } + + band = wlan_reg_band_bitmap_to_band_info(band_cap); + + num_channels = dst->ChannelCount; + for (i = 0; i < src->numOfChannels; i++) { + if (csr_is_channel_present_in_list(dst->chan_freq_cache, + num_channels, *freq_lst)) { + freq_lst++; + continue; + } + if (is_dfs_unsafe_extra_band_chan(mac_ctx, *freq_lst, band)) { + freq_lst++; + continue; + } + dst->chan_freq_cache[num_channels++] = *freq_lst; + freq_lst++; + } + dst->ChannelCount = num_channels; + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_fetch_ch_lst_from_ini() - fetch channel list from ini and update req msg + * parameters + * @mac_ctx: global mac ctx + * @roam_info: roam info struct + * @req_buf: out param, roam offload scan request packet + * + * Return: result of operation + */ +static QDF_STATUS +csr_fetch_ch_lst_from_ini(struct mac_context *mac_ctx, + tpCsrNeighborRoamControlInfo roam_info, + struct roam_offload_scan_req *req_buf) +{ + QDF_STATUS status; + tCsrChannelInfo *specific_chan_info; + + specific_chan_info = &roam_info->cfgParams.specific_chan_info; + + status = csr_populate_roam_chan_list(mac_ctx, + &req_buf->ConnectedNetwork, + specific_chan_info); + if (status != QDF_STATUS_SUCCESS) { + sme_err("Failed to copy channels to roam list"); + return status; + } + req_buf->ChannelCacheType = CHANNEL_LIST_STATIC; + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_fetch_ch_lst_from_occupied_lst() - fetch channel list from occupied list + * and update req msg + * parameters + * @mac_ctx: global mac ctx + * @session_id: session id + * @reason: reason to roam + * @req_buf: out param, roam offload scan request packet + * @roam_info: roam info struct + * + * Return: void + */ +static void +csr_fetch_ch_lst_from_occupied_lst(struct mac_context *mac_ctx, + uint8_t session_id, + uint8_t reason, + struct roam_offload_scan_req *req_buf, + tpCsrNeighborRoamControlInfo roam_info) +{ + uint8_t i = 0; + uint8_t num_channels = 0; + uint32_t op_freq; + struct csr_roam_session *session; + uint32_t *ch_lst; + enum band_info band = BAND_ALL; + + session = &mac_ctx->roam.roamSession[session_id]; + ch_lst = mac_ctx->scan.occupiedChannels[session_id].channel_freq_list; + op_freq = session->connectedProfile.op_freq; + + if (CSR_IS_ROAM_INTRA_BAND_ENABLED(mac_ctx)) { + if (WLAN_REG_IS_5GHZ_CH_FREQ(op_freq)) + band = BAND_5G; + else if (WLAN_REG_IS_24GHZ_CH_FREQ(op_freq)) + band = BAND_2G; + else + band = BAND_UNKNOWN; + } + for (i = 0; i < mac_ctx->scan.occupiedChannels[session_id].numChannels; + i++) { + if (is_dfs_unsafe_extra_band_chan(mac_ctx, *ch_lst, band)) { + ch_lst++; + continue; + } + req_buf->ConnectedNetwork.chan_freq_cache[num_channels++] = *ch_lst; + ch_lst++; + } + req_buf->ConnectedNetwork.ChannelCount = num_channels; + req_buf->ChannelCacheType = CHANNEL_LIST_DYNAMIC; +} + +/** + * csr_fetch_valid_ch_lst() - fetch channel list from valid channel list and + * update req msg + * parameters + * @mac_ctx: global mac ctx + * @req_buf: out param, roam offload scan request packet + * + * Return: void + */ +static QDF_STATUS +csr_fetch_valid_ch_lst(struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buf, + uint8_t session_id) +{ + QDF_STATUS status; + uint32_t host_channels = 0; + uint32_t *ch_freq_list = NULL; + uint8_t i = 0, num_channels = 0; + enum band_info band = BAND_ALL; + uint32_t op_freq; + struct csr_roam_session *session; + + session = &mac_ctx->roam.roamSession[session_id]; + op_freq = session->connectedProfile.op_freq; + if (CSR_IS_ROAM_INTRA_BAND_ENABLED(mac_ctx)) { + if (WLAN_REG_IS_5GHZ_CH_FREQ(op_freq)) + band = BAND_5G; + else if (WLAN_REG_IS_24GHZ_CH_FREQ(op_freq)) + band = BAND_2G; + else + band = BAND_UNKNOWN; + } + host_channels = sizeof(mac_ctx->roam.valid_ch_freq_list); + status = csr_get_cfg_valid_channels(mac_ctx, + mac_ctx->roam.valid_ch_freq_list, + &host_channels); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Failed to get the valid channel list"); + return status; + } + ch_freq_list = mac_ctx->roam.valid_ch_freq_list; + mac_ctx->roam.numValidChannels = host_channels; + + for (i = 0; i < mac_ctx->roam.numValidChannels; i++) { + if (is_dfs_unsafe_extra_band_chan(mac_ctx, *ch_freq_list, + band)) { + ch_freq_list++; + continue; + } + req_buf->ConnectedNetwork.chan_freq_cache[num_channels++] = + *ch_freq_list; + ch_freq_list++; + } + req_buf->ChannelCacheType = CHANNEL_LIST_DYNAMIC; + req_buf->ConnectedNetwork.ChannelCount = num_channels; + + return status; +} + +/** + * csr_add_ch_lst_from_roam_scan_list() - channel from roam scan chan list + * parameters + * @mac_ctx: Global mac ctx + * @req_buf: out param, roam offload scan request packet + * @roam_info: roam info struct + * + * Return: QDF_STATUS + */ +static QDF_STATUS +csr_add_ch_lst_from_roam_scan_list(struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buf, + tpCsrNeighborRoamControlInfo roam_info) +{ + QDF_STATUS status; + tCsrChannelInfo *pref_chan_info = &roam_info->cfgParams.pref_chan_info; + + if (!pref_chan_info->numOfChannels) + return QDF_STATUS_SUCCESS; + + status = csr_populate_roam_chan_list(mac_ctx, + &req_buf->ConnectedNetwork, + pref_chan_info); + if (status != QDF_STATUS_SUCCESS) { + sme_err("Failed to copy channels to roam list"); + return status; + } + sme_dump_freq_list(pref_chan_info); + req_buf->ChannelCacheType = CHANNEL_LIST_DYNAMIC; + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_11W +/** + * csr_rso_command_fill_11w_params() - Fill the 11w related parameters + * into the roam buffer + * @mac_ctx: Global mac context buffer + * @rso_req: RSO command buffer to be filled + * + * Return: None + */ +static void +csr_rso_command_fill_11w_params(struct mac_context *mac_ctx, + uint8_t session_id, + struct roam_offload_scan_req *rso_req) +{ + tSirRoamNetworkType *network_cfg = &rso_req->ConnectedNetwork; + tAniEdType group_mgmt_cipher; + + struct wlan_objmgr_vdev *vdev; + uint16_t rsn_caps; + bool peer_rmf_capable = false; + uint32_t keymgmt; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + session_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Invalid vdev"); + return; + } + + rsn_caps = (uint16_t)wlan_crypto_get_param(vdev, + WLAN_CRYPTO_PARAM_RSN_CAP); + if (wlan_crypto_vdev_has_mgmtcipher(vdev, + (1 << WLAN_CRYPTO_CIPHER_AES_GMAC) | + (1 << WLAN_CRYPTO_CIPHER_AES_GMAC_256) | + (1 << WLAN_CRYPTO_CIPHER_AES_CMAC)) && + (rsn_caps & + WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)) + peer_rmf_capable = true; + + network_cfg->mfp_enabled = peer_rmf_capable; + + keymgmt = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MGMT_CIPHER); + + if (keymgmt & (1 << WLAN_CRYPTO_CIPHER_AES_CMAC)) { + group_mgmt_cipher = eSIR_ED_AES_128_CMAC; + } else if (keymgmt & (1 << WLAN_CRYPTO_CIPHER_AES_GMAC)) { + group_mgmt_cipher = eSIR_ED_AES_GMAC_128; + } else if (keymgmt & (1 << WLAN_CRYPTO_CIPHER_AES_GMAC_256)) { + group_mgmt_cipher = eSIR_ED_AES_GMAC_256; + } else { + group_mgmt_cipher = eSIR_ED_NONE; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + if (network_cfg->mfp_enabled) + network_cfg->gp_mgmt_cipher_suite = group_mgmt_cipher; + else + network_cfg->gp_mgmt_cipher_suite = eSIR_ED_NONE; + + pe_debug("gp_mgmt_cipher_suite %d", network_cfg->gp_mgmt_cipher_suite); +} + +static void csr_fill_pmf_caps(uint16_t *rsn_caps, + tCsrRoamConnectedProfile *profile) +{ + *rsn_caps &= ~WLAN_CRYPTO_RSN_CAP_MFP_ENABLED; + *rsn_caps &= ~WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED; + if (profile->MFPRequired) + *rsn_caps |= WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED; + if (profile->MFPCapable) + *rsn_caps |= WLAN_CRYPTO_RSN_CAP_MFP_ENABLED; +} +#else +static inline +void csr_rso_command_fill_11w_params(struct mac_context *mac_ctx, + uint8_t session_id, + struct roam_offload_scan_req *rso_req) +{} + +static inline +void csr_fill_pmf_caps(uint16_t *rsn_caps, tCsrRoamConnectedProfile *profile) +{ + *rsn_caps &= ~WLAN_CRYPTO_RSN_CAP_MFP_ENABLED; + *rsn_caps &= ~WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED; +} +#endif + +static void +csr_rso_command_fill_rsn_caps(struct mac_context *mac_ctx, uint8_t vdev_id, + uint16_t *rsn_caps, + tCsrRoamConnectedProfile *profile) +{ + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Invalid vdev"); + return; + } + + *rsn_caps = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_RSN_CAP); + csr_fill_pmf_caps(rsn_caps, profile); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); +} + +/** + * csr_update_btm_offload_config() - Update btm config param to fw + * @mac_ctx: Global mac ctx + * @command: Roam offload command + * @req_buf: roam offload scan request + * @session: roam session + * + * Return: None + */ +static void csr_update_btm_offload_config(struct mac_context *mac_ctx, + uint8_t command, + struct roam_offload_scan_req *req_buf, + struct csr_roam_session *session) +{ + struct wlan_objmgr_peer *peer; + bool is_pmf_enabled; + + req_buf->btm_offload_config = + mac_ctx->mlme_cfg->btm.btm_offload_config; + + /* Return if INI is disabled */ + if (!req_buf->btm_offload_config) + return; + + /* For RSO Stop Disable BTM offload to firmware */ + if (command == ROAM_SCAN_OFFLOAD_STOP) { + req_buf->btm_offload_config = 0; + return; + } + + if (!session->pConnectBssDesc) { + sme_err("Connected Bss Desc is NULL"); + return; + } + + peer = wlan_objmgr_get_peer(mac_ctx->psoc, + wlan_objmgr_pdev_get_pdev_id(mac_ctx->pdev), + session->pConnectBssDesc->bssId, + WLAN_LEGACY_SME_ID); + if (!peer) { + sme_debug("Peer of peer_mac "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(session->pConnectBssDesc->bssId)); + return; + } + + is_pmf_enabled = mlme_get_peer_pmf_status(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_SME_ID); + sme_debug("get is_pmf_enabled %d for "QDF_MAC_ADDR_FMT, is_pmf_enabled, + QDF_MAC_ADDR_REF(session->pConnectBssDesc->bssId)); + + /* If peer does not support PMF in case of OCE/MBO + * Connection, Disable BTM offload to firmware. + */ + if (session->pConnectBssDesc->mbo_oce_enabled_ap && + !is_pmf_enabled) + req_buf->btm_offload_config = 0; +} + +/** + * csr_create_roam_scan_offload_request() - init roam offload scan request + * + * parameters + * @mac_ctx: global mac ctx + * @command: roam scan offload command input + * @session_id: session id + * @reason: reason to roam + * @session: roam session + * @roam_info: roam info struct + * + * Return: roam offload scan request packet buffer + */ +static struct roam_offload_scan_req * +csr_create_roam_scan_offload_request(struct mac_context *mac_ctx, + uint8_t command, + uint8_t session_id, + uint8_t reason, + struct csr_roam_session *session, + tpCsrNeighborRoamControlInfo roam_info) +{ + QDF_STATUS status; + uint8_t i, j, dot11_mode; + bool ese_neighbor_list_recvd = false; + uint8_t ch_cache_str[128] = { 0 }; + struct roam_offload_scan_req *req_buf = NULL; + tpCsrChannelInfo curr_ch_lst_info = + &roam_info->roamChannelInfo.currentChannelListInfo; + tCsrChannelInfo *specific_chan_info; + + specific_chan_info = &roam_info->cfgParams.specific_chan_info; +#ifdef FEATURE_WLAN_ESE + /* + * this flag will be true if connection is ESE and no neighbor + * list received or if the connection is not ESE + */ + ese_neighbor_list_recvd = ((roam_info->isESEAssoc) + && (roam_info->roamChannelInfo.IAPPNeighborListReceived + == false)) + || (roam_info->isESEAssoc == false); +#endif /* FEATURE_WLAN_ESE */ + + req_buf = qdf_mem_malloc(sizeof(struct roam_offload_scan_req)); + if (!req_buf) + return NULL; + + req_buf->Command = command; + /* + * If command is STOP, then pass down ScanOffloadEnabled as Zero. This + * will handle the case of host driver reloads, but Riva still up and + * running + */ + if (command == ROAM_SCAN_OFFLOAD_STOP) { + /* + * clear the roaming parameters that are per connection. + * For a new connection, they have to be programmed again. + */ + if (csr_neighbor_middle_of_roaming(mac_ctx, + session_id)) + req_buf->middle_of_roaming = 1; + else + csr_roam_reset_roam_params(mac_ctx); + + req_buf->RoamScanOffloadEnabled = 0; + } else if (command == ROAM_SCAN_OFFLOAD_UPDATE_CFG) { + if (mlme_get_roam_state(mac_ctx->psoc, session_id) == + ROAM_RSO_STARTED) + req_buf->RoamScanOffloadEnabled = 1; + } else { + req_buf->RoamScanOffloadEnabled = + mac_ctx->mlme_cfg->lfr.roam_scan_offload_enabled; + } + qdf_mem_copy(req_buf->ConnectedNetwork.currAPbssid, + roam_info->currAPbssid.bytes, sizeof(struct qdf_mac_addr)); + req_buf->ConnectedNetwork.ssId.length = + mac_ctx->roam.roamSession[session_id]. + connectedProfile.SSID.length; + qdf_mem_copy(req_buf->ConnectedNetwork.ssId.ssId, + mac_ctx->roam.roamSession[session_id]. + connectedProfile.SSID.ssId, + req_buf->ConnectedNetwork.ssId.length); + req_buf->ConnectedNetwork.authentication = + mac_ctx->roam.roamSession[session_id].connectedProfile.AuthType; + req_buf->ConnectedNetwork.encryption = + mac_ctx->roam.roamSession[session_id]. + connectedProfile.EncryptionType; + req_buf->ConnectedNetwork.mcencryption = + mac_ctx->roam.roamSession[session_id]. + connectedProfile.mcEncryptionType; + + /* Copy the self RSN capabilities in roam offload request */ + csr_rso_command_fill_rsn_caps(mac_ctx, session_id, + (uint16_t *)&req_buf->rsn_caps, + &mac_ctx->roam.roamSession[session_id].connectedProfile); + csr_rso_command_fill_11w_params(mac_ctx, session_id, req_buf); + + req_buf->delay_before_vdev_stop = + roam_info->cfgParams.delay_before_vdev_stop; + req_buf->OpportunisticScanThresholdDiff = + roam_info->cfgParams.nOpportunisticThresholdDiff; + req_buf->RoamRescanRssiDiff = + roam_info->cfgParams.nRoamRescanRssiDiff; + req_buf->RoamRssiDiff = roam_info->cfgParams.roam_rssi_diff; + req_buf->rssi_abs_thresh = + mac_ctx->mlme_cfg->lfr.roam_rssi_abs_threshold; + req_buf->reason = reason; + req_buf->NeighborScanTimerPeriod = + roam_info->cfgParams.neighborScanPeriod; + req_buf->neighbor_scan_min_timer_period = + roam_info->cfgParams.neighbor_scan_min_period; + req_buf->NeighborScanChannelMinTime = + roam_info->cfgParams.minChannelScanTime; + req_buf->NeighborScanChannelMaxTime = + roam_info->cfgParams.maxChannelScanTime; + req_buf->EmptyRefreshScanPeriod = + roam_info->cfgParams.emptyScanRefreshPeriod; + req_buf->full_roam_scan_period = + roam_info->cfgParams.full_roam_scan_period; + req_buf->roam_scan_inactivity_time = + roam_info->cfgParams.roam_scan_inactivity_time; + req_buf->roam_inactive_data_packet_count = + roam_info->cfgParams.roam_inactive_data_packet_count; + req_buf->roam_scan_period_after_inactivity = + roam_info->cfgParams.roam_scan_period_after_inactivity; + + req_buf->RoamBmissFirstBcnt = + roam_info->cfgParams.nRoamBmissFirstBcnt; + req_buf->RoamBmissFinalBcnt = + roam_info->cfgParams.nRoamBmissFinalBcnt; + req_buf->RoamBeaconRssiWeight = + roam_info->cfgParams.nRoamBeaconRssiWeight; + csr_copy_mawc_config(mac_ctx, &req_buf->mawc_roam_params); + + req_buf->min_rssi_params[DEAUTH_MIN_RSSI] = + mac_ctx->mlme_cfg->trig_min_rssi[DEAUTH_MIN_RSSI]; + req_buf->min_rssi_params[BMISS_MIN_RSSI] = + mac_ctx->mlme_cfg->trig_min_rssi[BMISS_MIN_RSSI]; + req_buf->min_rssi_params[MIN_RSSI_2G_TO_5G_ROAM] = + mac_ctx->mlme_cfg->trig_min_rssi[MIN_RSSI_2G_TO_5G_ROAM]; + + req_buf->score_delta_param[IDLE_ROAM_TRIGGER] = + mac_ctx->mlme_cfg->trig_score_delta[IDLE_ROAM_TRIGGER]; + req_buf->score_delta_param[BTM_ROAM_TRIGGER] = + mac_ctx->mlme_cfg->trig_score_delta[BTM_ROAM_TRIGGER]; + +#ifdef FEATURE_WLAN_ESE + req_buf->IsESEAssoc = + csr_roam_is_ese_assoc(mac_ctx, session_id) && + ((req_buf->ConnectedNetwork.authentication == + eCSR_AUTH_TYPE_OPEN_SYSTEM) || + (csr_is_auth_type_ese(req_buf-> + ConnectedNetwork.authentication))); + req_buf->is_11r_assoc = roam_info->is11rAssoc; + + if (req_buf->IsESEAssoc || req_buf->is_11r_assoc || + req_buf->middle_of_roaming) + sme_debug("IsEseAssoc: %d is_11r_assoc: %d middle of roaming: %d ese_neighbor_list_recvd: %d cur no of chan: %d", + req_buf->IsESEAssoc, req_buf->is_11r_assoc, + req_buf->middle_of_roaming, ese_neighbor_list_recvd, + curr_ch_lst_info->numOfChannels); +#endif + + if (ese_neighbor_list_recvd || + curr_ch_lst_info->numOfChannels == 0) { + /* + * Retrieve the Channel Cache either from ini or from + * the occupied channels list along with preferred + * channel list configured by the client. + * Give Preference to INI Channels + */ + if (specific_chan_info->numOfChannels) { + status = csr_fetch_ch_lst_from_ini(mac_ctx, + roam_info, + req_buf); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "Fetch channel list from ini failed"); + qdf_mem_free(req_buf); + return NULL; + } + } else if (reason == REASON_FLUSH_CHANNEL_LIST) { + req_buf->ChannelCacheType = CHANNEL_LIST_STATIC; + req_buf->ConnectedNetwork.ChannelCount = 0; + } else { + csr_fetch_ch_lst_from_occupied_lst(mac_ctx, session_id, + reason, req_buf, + roam_info); + /* Add the preferred channel list configured by + * client to the roam channel list along with + * occupied channel list. + */ + csr_add_ch_lst_from_roam_scan_list(mac_ctx, + req_buf, + roam_info); + } + } +#ifdef FEATURE_WLAN_ESE + else { + /* + * If ESE is enabled, and a neighbor Report is received, + * then Ignore the INI Channels or the Occupied Channel + * List. Consider the channels in the neighbor list sent + * by the ESE AP + */ + csr_fetch_ch_lst_from_received_list(mac_ctx, roam_info, + curr_ch_lst_info, req_buf); + } +#endif + if (req_buf->ConnectedNetwork.ChannelCount == 0 && + reason != REASON_FLUSH_CHANNEL_LIST) { + /* Maintain the Valid Channels List */ + status = csr_fetch_valid_ch_lst(mac_ctx, req_buf, session_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Fetch channel list fail"); + qdf_mem_free(req_buf); + return NULL; + } + } + + for (i = 0, j = 0; i < req_buf->ConnectedNetwork.ChannelCount; i++) { + if (j < sizeof(ch_cache_str)) { + j += snprintf(ch_cache_str + j, + sizeof(ch_cache_str) - j, " %d", + req_buf->ConnectedNetwork. + chan_freq_cache[i]); + } else + break; + } + sme_debug("ChnlCacheType:%d, No of Chnls:%d,Channels: %s", + req_buf->ChannelCacheType, + req_buf->ConnectedNetwork.ChannelCount, ch_cache_str); + + req_buf->mdid = + mac_ctx->roam.roamSession[session_id].connectedProfile.mdid; + req_buf->sessionId = session_id; + req_buf->nProbes = roam_info->cfgParams.roam_scan_n_probes; + req_buf->HomeAwayTime = roam_info->cfgParams.roam_scan_home_away_time; + + /* + * Home Away Time should be at least equal to (MaxDwell time + (2*RFS)), + * where RFS is the RF Switching time. It is twice RFS to consider the + * time to go off channel and return to the home channel. + */ + if (req_buf->HomeAwayTime < + (req_buf->NeighborScanChannelMaxTime + + (2 * CSR_ROAM_SCAN_CHANNEL_SWITCH_TIME))) { + sme_debug("Disable Home away time(%d) as it is less than (2*RF switching time + channel max time)(%d)", + req_buf->HomeAwayTime, + (req_buf->NeighborScanChannelMaxTime + + (2 * CSR_ROAM_SCAN_CHANNEL_SWITCH_TIME))); + req_buf->HomeAwayTime = 0; + } + + /*Prepare a probe request for 2.4GHz band and one for 5GHz band */ + dot11_mode = (uint8_t) csr_translate_to_wni_cfg_dot11_mode(mac_ctx, + csr_find_best_phy_mode(mac_ctx, + mac_ctx->roam.configParam.phyMode)); + req_buf->allowDFSChannelRoam = (eSirDFSRoamScanMode) + mac_ctx->mlme_cfg->lfr.roaming_dfs_channel; + req_buf->early_stop_scan_enable = + mac_ctx->mlme_cfg->lfr.early_stop_scan_enable; + req_buf->early_stop_scan_min_threshold = + mac_ctx->mlme_cfg->lfr.early_stop_scan_min_threshold; + req_buf->early_stop_scan_max_threshold = + mac_ctx->mlme_cfg->lfr.early_stop_scan_max_threshold; + req_buf->roamscan_adaptive_dwell_mode = + mac_ctx->mlme_cfg->lfr.adaptive_roamscan_dwell_mode; + + req_buf->lca_config_params.disallow_duration = + mac_ctx->mlme_cfg->lfr.lfr3_disallow_duration; + req_buf->lca_config_params.rssi_channel_penalization = + mac_ctx->mlme_cfg->lfr.lfr3_rssi_channel_penalization; + req_buf->lca_config_params.num_disallowed_aps = + mac_ctx->mlme_cfg->lfr.lfr3_num_disallowed_aps; + + csr_update_btm_offload_config(mac_ctx, command, req_buf, session); + + req_buf->btm_solicited_timeout = + mac_ctx->mlme_cfg->btm.btm_solicited_timeout; + req_buf->btm_max_attempt_cnt = + mac_ctx->mlme_cfg->btm.btm_max_attempt_cnt; + req_buf->btm_sticky_time = + mac_ctx->mlme_cfg->btm.btm_sticky_time; + req_buf->rct_validity_timer = mac_ctx->mlme_cfg->btm.rct_validity_timer; + req_buf->disassoc_timer_threshold = + mac_ctx->mlme_cfg->btm.disassoc_timer_threshold; + req_buf->btm_query_bitmask = + mac_ctx->mlme_cfg->btm.btm_query_bitmask; + req_buf->btm_trig_min_candidate_score = + mac_ctx->mlme_cfg->btm.btm_trig_min_candidate_score; + + csr_update_roam_scan_offload_request(mac_ctx, req_buf, session); + + return req_buf; +} + +/** + * csr_update_11k_offload_params - Update 11K offload params + * @mac_ctx: MAC context + * @session: Pointer to the CSR Roam Session + * @req_buffer: Pointer to the RSO Request buffer + * @enabled: 11k offload enabled/disabled. + * + * API to update 11k offload params to Roam Scan Offload request buffer + * + * Return: none + */ +static void +csr_update_11k_offload_params(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct roam_offload_scan_req *req_buffer, + bool enabled) +{ + struct wmi_11k_offload_params *params = &req_buffer->offload_11k_params; + struct csr_config *csr_config = &mac_ctx->roam.configParam; + struct csr_neighbor_report_offload_params *neighbor_report_offload = + &csr_config->neighbor_report_offload; + + params->vdev_id = session->sessionId; + + if (enabled) { + params->offload_11k_bitmask = + csr_config->offload_11k_enable_bitmask; + } else { + params->offload_11k_bitmask = 0; + return; + } + + /* + * If none of the parameters are enabled, then set the + * offload_11k_bitmask to 0, so that we don't send the command + * to the FW and drop it in WMA + */ + if ((neighbor_report_offload->params_bitmask & + NEIGHBOR_REPORT_PARAMS_ALL) == 0) { + sme_err("No valid neighbor report offload params %x", + neighbor_report_offload->params_bitmask); + params->offload_11k_bitmask = 0; + return; + } + + /* + * First initialize all params to NEIGHBOR_REPORT_PARAM_INVALID + * Then set the values that are enabled + */ + params->neighbor_report_params.time_offset = + NEIGHBOR_REPORT_PARAM_INVALID; + params->neighbor_report_params.low_rssi_offset = + NEIGHBOR_REPORT_PARAM_INVALID; + params->neighbor_report_params.bmiss_count_trigger = + NEIGHBOR_REPORT_PARAM_INVALID; + params->neighbor_report_params.per_threshold_offset = + NEIGHBOR_REPORT_PARAM_INVALID; + params->neighbor_report_params.neighbor_report_cache_timeout = + NEIGHBOR_REPORT_PARAM_INVALID; + params->neighbor_report_params.max_neighbor_report_req_cap = + NEIGHBOR_REPORT_PARAM_INVALID; + + if (neighbor_report_offload->params_bitmask & + NEIGHBOR_REPORT_PARAMS_TIME_OFFSET) + params->neighbor_report_params.time_offset = + neighbor_report_offload->time_offset; + + if (neighbor_report_offload->params_bitmask & + NEIGHBOR_REPORT_PARAMS_LOW_RSSI_OFFSET) + params->neighbor_report_params.low_rssi_offset = + neighbor_report_offload->low_rssi_offset; + + if (neighbor_report_offload->params_bitmask & + NEIGHBOR_REPORT_PARAMS_BMISS_COUNT_TRIGGER) + params->neighbor_report_params.bmiss_count_trigger = + neighbor_report_offload->bmiss_count_trigger; + + if (neighbor_report_offload->params_bitmask & + NEIGHBOR_REPORT_PARAMS_PER_THRESHOLD_OFFSET) + params->neighbor_report_params.per_threshold_offset = + neighbor_report_offload->per_threshold_offset; + + if (neighbor_report_offload->params_bitmask & + NEIGHBOR_REPORT_PARAMS_CACHE_TIMEOUT) + params->neighbor_report_params.neighbor_report_cache_timeout = + neighbor_report_offload->neighbor_report_cache_timeout; + + if (neighbor_report_offload->params_bitmask & + NEIGHBOR_REPORT_PARAMS_MAX_REQ_CAP) + params->neighbor_report_params.max_neighbor_report_req_cap = + neighbor_report_offload->max_neighbor_report_req_cap; + + params->neighbor_report_params.ssid.length = + session->connectedProfile.SSID.length; + qdf_mem_copy(params->neighbor_report_params.ssid.mac_ssid, + session->connectedProfile.SSID.ssId, + session->connectedProfile.SSID.length); +} + +QDF_STATUS csr_invoke_neighbor_report_request(uint8_t session_id, + struct sRrmNeighborReq *neighbor_report_req, + bool send_resp_to_host) +{ + struct wmi_invoke_neighbor_report_params *invoke_params; + struct scheduler_msg msg = {0}; + + if (!neighbor_report_req) { + sme_err("Invalid params"); + return QDF_STATUS_E_INVAL; + } + + invoke_params = qdf_mem_malloc(sizeof(*invoke_params)); + if (!invoke_params) + return QDF_STATUS_E_NOMEM; + + invoke_params->vdev_id = session_id; + invoke_params->send_resp_to_host = send_resp_to_host; + + if (!neighbor_report_req->no_ssid) { + invoke_params->ssid.length = neighbor_report_req->ssid.length; + qdf_mem_copy(invoke_params->ssid.mac_ssid, + neighbor_report_req->ssid.ssId, + neighbor_report_req->ssid.length); + } else { + invoke_params->ssid.length = 0; + } + + sme_debug("Sending SIR_HAL_INVOKE_NEIGHBOR_REPORT"); + + msg.type = SIR_HAL_INVOKE_NEIGHBOR_REPORT; + msg.reserved = 0; + msg.bodyptr = invoke_params; + + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + &msg)) { + sme_err("Not able to post message to WMA"); + qdf_mem_free(invoke_params); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * check_allowed_ssid_list() - Check the WhiteList + * @req_buffer: Buffer which contains the connected profile SSID. + * @roam_params: Buffer which contains the whitelist SSID's. + * + * Check if the connected profile SSID exists in the whitelist. + * It is assumed that the framework provides this also in the whitelist. + * If it exists there is no issue. Otherwise add it to the list. + * + * Return: None + */ +static void check_allowed_ssid_list(struct roam_offload_scan_req *req_buffer, + struct roam_ext_params *roam_params) +{ + int i = 0; + bool match = false; + + for (i = 0; i < roam_params->num_ssid_allowed_list; i++) { + if ((roam_params->ssid_allowed_list[i].length == + req_buffer->ConnectedNetwork.ssId.length) && + (!qdf_mem_cmp(roam_params->ssid_allowed_list[i].ssId, + req_buffer->ConnectedNetwork.ssId.ssId, + roam_params->ssid_allowed_list[i].length))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Whitelist contains connected profile SSID"); + match = true; + break; + } + } + if (!match) { + if (roam_params->num_ssid_allowed_list >= + MAX_SSID_ALLOWED_LIST) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Whitelist is FULL. Cannot Add another entry"); + return; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Adding Connected profile SSID to whitelist"); + /* i is the next available index to add the entry.*/ + i = roam_params->num_ssid_allowed_list; + qdf_mem_copy(roam_params->ssid_allowed_list[i].ssId, + req_buffer->ConnectedNetwork.ssId.ssId, + req_buffer->ConnectedNetwork.ssId.length); + roam_params->ssid_allowed_list[i].length = + req_buffer->ConnectedNetwork.ssId.length; + roam_params->num_ssid_allowed_list++; + } +} + +/** + * csr_add_rssi_reject_ap_list() - add rssi reject AP list to the + * roam params + * @mac_ctx: mac ctx. + * @roam_params: roam params in which reject AP list needs + * to be populated. + * + * Return: None + */ +static void +csr_add_rssi_reject_ap_list(struct mac_context *mac_ctx, + struct roam_ext_params *roam_params) +{ + int i = 0; + struct reject_ap_config_params *reject_list; + + reject_list = qdf_mem_malloc(sizeof(*reject_list) * + MAX_RSSI_AVOID_BSSID_LIST); + if (!reject_list) + return; + + roam_params->num_rssi_rejection_ap = + wlan_blm_get_bssid_reject_list(mac_ctx->pdev, reject_list, + MAX_RSSI_AVOID_BSSID_LIST, + DRIVER_RSSI_REJECT_TYPE); + if (!roam_params->num_rssi_rejection_ap) { + qdf_mem_free(reject_list); + return; + } + + for (i = 0; i < roam_params->num_rssi_rejection_ap; i++) { + roam_params->rssi_reject_bssid_list[i] = reject_list[i]; + sme_debug("BSSID "QDF_MAC_ADDR_FMT" expected rssi %d remaining duration %d", + QDF_MAC_ADDR_REF(roam_params->rssi_reject_bssid_list[i].bssid.bytes), + roam_params->rssi_reject_bssid_list[i].expected_rssi, + roam_params->rssi_reject_bssid_list[i].reject_duration); + } + + qdf_mem_free(reject_list); +} + +/* + * csr_roam_send_rso_cmd() - API to send RSO command to PE + * @mac_ctx: Pointer to global MAC structure + * @session_id: Session ID + * @request_buf: Pointer to struct roam_offload_scan_req + * + * Return: QDF_STATUS + */ +static QDF_STATUS +csr_roam_send_rso_cmd(struct mac_context *mac_ctx, + uint8_t session_id, + struct roam_offload_scan_req *request_buf) +{ + QDF_STATUS status; + struct scheduler_msg message = {0}; + + message.bodyptr = request_buf; + message.type = eWNI_SME_ROAM_SCAN_OFFLOAD_REQ; + + status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &message); + + if (QDF_STATUS_SUCCESS != status) { + sme_err("Send RSO from CSR failed"); + qdf_mem_free(request_buf); + } + + return status; +} + +/** + * csr_append_assoc_ies() - Append specific IE to assoc IE's buffer + * @mac_ctx: Pointer to global mac context + * @req_buf: Pointer to Roam offload scan request + * @ie_id: IE ID to be appended + * @ie_len: IE length to be appended + * @ie_data: IE data to be appended + * + * Return: None + */ +static void csr_append_assoc_ies(struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buf, + uint8_t ie_id, uint8_t ie_len, + const uint8_t *ie_data) +{ + tSirAddie *assoc_ie = &req_buf->assoc_ie; + + if ((SIR_MAC_MAX_ADD_IE_LENGTH - assoc_ie->length) < ie_len) { + sme_err("Appending IE id: %d fails", ie_id); + return; + } + assoc_ie->addIEdata[assoc_ie->length] = ie_id; + assoc_ie->addIEdata[assoc_ie->length + 1] = ie_len; + qdf_mem_copy(&assoc_ie->addIEdata[assoc_ie->length + 2], + ie_data, ie_len); + assoc_ie->length += (ie_len + 2); +} + +#ifdef FEATURE_WLAN_ESE +/** + * ese_populate_addtional_ies() - add IEs to reassoc frame + * @mac_ctx: Pointer to global mac structure + * @session: pointer to CSR session + * @req_buf: Pointer to Roam offload scan request + * + * This function populates the TSPEC ie and appends the info + * to assoc buffer. + * + * Return: None + */ +static void ese_populate_addtional_ies(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct roam_offload_scan_req *req_buf) +{ + + uint8_t tspec_ie_hdr[SIR_MAC_OUI_WME_HDR_MIN] + = { 0x00, 0x50, 0xf2, 0x02, 0x02, 0x01 }; + uint8_t tspec_ie_buf[DOT11F_IE_WMMTSPEC_MAX_LEN], j; + ese_wmm_tspec_ie *tspec_ie; + tESETspecInfo ese_tspec; + + tspec_ie = (ese_wmm_tspec_ie *)(tspec_ie_buf + SIR_MAC_OUI_WME_HDR_MIN); + if (csr_is_wmm_supported(mac_ctx) && + mac_ctx->mlme_cfg->lfr.ese_enabled && + csr_roam_is_ese_assoc(mac_ctx, session->sessionId)) { + ese_tspec.numTspecs = sme_qos_ese_retrieve_tspec_info(mac_ctx, + session->sessionId, + (tTspecInfo *) &ese_tspec.tspec[0]); + qdf_mem_copy(tspec_ie_buf, tspec_ie_hdr, + SIR_MAC_OUI_WME_HDR_MIN); + for (j = 0; j < ese_tspec.numTspecs; j++) { + /* Populate the tspec_ie */ + ese_populate_wmm_tspec(&ese_tspec.tspec[j].tspec, + tspec_ie); + csr_append_assoc_ies(mac_ctx, req_buf, + WLAN_ELEMID_VENDOR, + DOT11F_IE_WMMTSPEC_MAX_LEN, + tspec_ie_buf); + } + } + +} +#else +static void ese_populate_addtional_ies(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct roam_offload_scan_req *req_buf) +{ +} +#endif +/** + * csr_update_driver_assoc_ies() - Append driver built IE's to assoc IE's + * @mac_ctx: Pointer to global mac structure + * @session: pointer to CSR session + * @req_buf: Pointer to Roam offload scan request + * + * Return: None + */ +static void csr_update_driver_assoc_ies(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct roam_offload_scan_req *req_buf) +{ + bool power_caps_populated = false; + uint32_t csr_11henable; + + uint8_t *rrm_cap_ie_data + = (uint8_t *) &mac_ctx->rrm.rrmPEContext.rrmEnabledCaps; + uint8_t power_cap_ie_data[DOT11F_IE_POWERCAPS_MAX_LEN] + = {MIN_TX_PWR_CAP, MAX_TX_PWR_CAP}; + uint8_t max_tx_pwr_cap = 0; + uint8_t supp_chan_ie[DOT11F_IE_SUPPCHANNELS_MAX_LEN], supp_chan_ie_len; + +#ifdef FEATURE_WLAN_ESE + static const uint8_t ese_ie[] = {0x0, 0x40, 0x96, 0x3, + ESE_VERSION_SUPPORTED}; +#endif + static const uint8_t qcn_ie[] = {0x8C, 0xFD, 0xF0, 0x1, + QCN_IE_VERSION_SUBATTR_ID, + QCN_IE_VERSION_SUBATTR_DATA_LEN, + QCN_IE_VERSION_SUPPORTED, + QCN_IE_SUBVERSION_SUPPORTED}; + + if (session->pConnectBssDesc) + max_tx_pwr_cap = csr_get_cfg_max_tx_power( + mac_ctx, + session->pConnectBssDesc->chan_freq); + + if (max_tx_pwr_cap && max_tx_pwr_cap < MAX_TX_PWR_CAP) + power_cap_ie_data[1] = max_tx_pwr_cap; + else + power_cap_ie_data[1] = MAX_TX_PWR_CAP; + + csr_11henable = mac_ctx->mlme_cfg->gen.enabled_11h; + + if (csr_11henable && csr_is11h_supported(mac_ctx)) { + /* Append power cap IE */ + csr_append_assoc_ies(mac_ctx, req_buf, WLAN_ELEMID_PWRCAP, + DOT11F_IE_POWERCAPS_MAX_LEN, + power_cap_ie_data); + power_caps_populated = true; + + /* Append Supported channels IE */ + csr_add_supported_5Ghz_channels(mac_ctx, supp_chan_ie, + &supp_chan_ie_len, true); + + csr_append_assoc_ies(mac_ctx, req_buf, + WLAN_ELEMID_SUPPCHAN, + supp_chan_ie_len, supp_chan_ie); + } + +#ifdef FEATURE_WLAN_ESE + /* Append ESE version IE if isEseIniFeatureEnabled INI is enabled */ + if (mac_ctx->mlme_cfg->lfr.ese_enabled) + csr_append_assoc_ies(mac_ctx, req_buf, WLAN_ELEMID_VENDOR, + sizeof(ese_ie), ese_ie); +#endif + + if (mac_ctx->rrm.rrmPEContext.rrmEnable) { + /* Append RRM IE */ + csr_append_assoc_ies(mac_ctx, req_buf, WLAN_ELEMID_RRM, + DOT11F_IE_RRMENABLEDCAP_MAX_LEN, + rrm_cap_ie_data); + if (!power_caps_populated) + /* Append Power cap IE if not appended already */ + csr_append_assoc_ies(mac_ctx, req_buf, + WLAN_ELEMID_PWRCAP, + DOT11F_IE_POWERCAPS_MAX_LEN, + power_cap_ie_data); + } + ese_populate_addtional_ies(mac_ctx, session, req_buf); + + /* Append QCN IE if g_support_qcn_ie INI is enabled */ + if (mac_ctx->mlme_cfg->sta.qcn_ie_support) + csr_append_assoc_ies(mac_ctx, req_buf, WLAN_ELEMID_VENDOR, + sizeof(qcn_ie), qcn_ie); +} + +/** + * csr_create_per_roam_request() - create PER roam offload scan request + * + * parameters + * @mac_ctx: global mac ctx + * @session_id: session id + * + * Return: per roam config request packet buffer + */ +static struct wmi_per_roam_config_req * +csr_create_per_roam_request(struct mac_context *mac_ctx, + uint8_t session_id) +{ + struct wmi_per_roam_config_req *req_buf = NULL; + + req_buf = qdf_mem_malloc(sizeof(struct wmi_per_roam_config_req)); + if (!req_buf) + return NULL; + + req_buf->vdev_id = session_id; + req_buf->per_config.enable = + mac_ctx->mlme_cfg->lfr.per_roam_enable; + req_buf->per_config.tx_high_rate_thresh = + mac_ctx->mlme_cfg->lfr.per_roam_config_high_rate_th; + req_buf->per_config.rx_high_rate_thresh = + mac_ctx->mlme_cfg->lfr.per_roam_config_high_rate_th; + req_buf->per_config.tx_low_rate_thresh = + mac_ctx->mlme_cfg->lfr.per_roam_config_low_rate_th; + req_buf->per_config.rx_low_rate_thresh = + mac_ctx->mlme_cfg->lfr.per_roam_config_low_rate_th; + req_buf->per_config.per_rest_time = + mac_ctx->mlme_cfg->lfr.per_roam_rest_time; + req_buf->per_config.tx_per_mon_time = + mac_ctx->mlme_cfg->lfr.per_roam_monitor_time; + req_buf->per_config.rx_per_mon_time = + mac_ctx->mlme_cfg->lfr.per_roam_monitor_time; + req_buf->per_config.tx_rate_thresh_percnt = + mac_ctx->mlme_cfg->lfr.per_roam_config_rate_th_percent; + req_buf->per_config.rx_rate_thresh_percnt = + mac_ctx->mlme_cfg->lfr.per_roam_config_rate_th_percent; + req_buf->per_config.min_candidate_rssi = + mac_ctx->mlme_cfg->lfr.per_roam_min_candidate_rssi; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "PER based roaming configuaration enable: %d vdev: %d high_rate_thresh: %d low_rate_thresh: %d rate_thresh_percnt: %d per_rest_time: %d monitor_time: %d min cand rssi: %d", + req_buf->per_config.enable, session_id, + req_buf->per_config.tx_high_rate_thresh, + req_buf->per_config.tx_low_rate_thresh, + req_buf->per_config.tx_rate_thresh_percnt, + req_buf->per_config.per_rest_time, + req_buf->per_config.tx_per_mon_time, + req_buf->per_config.min_candidate_rssi); + return req_buf; +} + +/** + * csr_roam_offload_per_scan() - populates roam offload scan request and sends + * to WMA + * + * parameters + * @mac_ctx: global mac ctx + * @session_id: session id + * + * Return: result of operation + */ +static QDF_STATUS +csr_roam_offload_per_scan(struct mac_context *mac_ctx, uint8_t session_id) +{ + struct wmi_per_roam_config_req *req_buf; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + if (!mac_ctx->mlme_cfg->lfr.per_roam_enable) { + sme_debug("PER based roaming ini is disabled"); + return QDF_STATUS_SUCCESS; + } + + req_buf = csr_create_per_roam_request(mac_ctx, session_id); + if (!req_buf) + return QDF_STATUS_E_FAILURE; + + msg.type = eWNI_SME_ROAM_SEND_PER_REQ; + msg.reserved = 0; + msg.bodyptr = req_buf; + status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + sme_debug("Unable to post WMA_SET_PER_ROAM_CONFIG_CMD to WMA"); + qdf_mem_free(req_buf); + } + return QDF_STATUS_SUCCESS; +} + +#if defined(WLAN_FEATURE_FILS_SK) +QDF_STATUS csr_update_fils_config(struct mac_context *mac, uint8_t session_id, + struct csr_roam_profile *src_profile) +{ + struct csr_roam_session *session = CSR_GET_SESSION(mac, session_id); + struct csr_roam_profile *dst_profile = NULL; + + if (!session) { + sme_err("session NULL"); + return QDF_STATUS_E_FAILURE; + } + + dst_profile = session->pCurRoamProfile; + + if (!dst_profile) { + sme_err("Current Roam profile of SME session NULL"); + return QDF_STATUS_E_FAILURE; + } + update_profile_fils_info(dst_profile, src_profile); + return QDF_STATUS_SUCCESS; +} + +/** + * copy_all_before_char() - API to copy all character before a particular char + * @str: Source string + * @str_len: Source string legnth + * @dst: Destination string + * @dst_len: Destination string legnth + * @c: Character before which all characters need to be copied + * + * Return: length of the copied string, if success. zero otherwise. + */ +static uint32_t copy_all_before_char(char *str, uint32_t str_len, + char *dst, uint32_t dst_len, char c) +{ + uint32_t len = 0; + + if (!str) + return len; + + while ((len < str_len) && (len < dst_len) && + (*str != '\0') && (*str != c)) { + *dst++ = *str++; + len++; + } + + return len; +} + +/** + * csr_update_fils_params_rso() - API to update FILS params in RSO + * @mac: Mac context + * @session: CSR Roam Session + * @req_buffer: RSO request buffer + * + * Return: None + */ +static void csr_update_fils_params_rso(struct mac_context *mac, + struct csr_roam_session *session, + struct roam_offload_scan_req *req_buffer) +{ + struct roam_fils_params *roam_fils_params; + struct cds_fils_connection_info *fils_info; + uint32_t usr_name_len; + + if (!session->pCurRoamProfile) + return; + + fils_info = session->pCurRoamProfile->fils_con_info; + if (!fils_info || !req_buffer) + return; + + if (!fils_info->key_nai_length) { + sme_debug("key_nai_length is NULL"); + return; + } + + roam_fils_params = &req_buffer->roam_fils_params; + if ((fils_info->key_nai_length > FILS_MAX_KEYNAME_NAI_LENGTH) || + (fils_info->r_rk_length > FILS_MAX_RRK_LENGTH)) { + sme_err("Fils info len error: keyname nai len(%d) rrk len(%d)", + fils_info->key_nai_length, fils_info->r_rk_length); + return; + } + + usr_name_len = copy_all_before_char(fils_info->keyname_nai, + sizeof(fils_info->keyname_nai), + roam_fils_params->username, + sizeof(roam_fils_params->username), + '@'); + if (fils_info->key_nai_length <= usr_name_len) { + sme_err("Fils info len error: key nai len %d, user name len %d", + fils_info->key_nai_length, usr_name_len); + return; + } + + roam_fils_params->username_length = usr_name_len; + req_buffer->is_fils_connection = true; + + roam_fils_params->next_erp_seq_num = fils_info->sequence_number; + + roam_fils_params->rrk_length = fils_info->r_rk_length; + qdf_mem_copy(roam_fils_params->rrk, fils_info->r_rk, + roam_fils_params->rrk_length); + + /* REALM info */ + roam_fils_params->realm_len = fils_info->key_nai_length + - roam_fils_params->username_length - 1; + qdf_mem_copy(roam_fils_params->realm, fils_info->keyname_nai + + roam_fils_params->username_length + 1, + roam_fils_params->realm_len); + sme_debug("Fils: next_erp_seq_num %d rrk_len %d realm_len:%d", + roam_fils_params->next_erp_seq_num, + roam_fils_params->rrk_length, roam_fils_params->realm_len); +} +#else +static inline +void csr_update_fils_params_rso(struct mac_context *mac, + struct csr_roam_session *session, + struct roam_offload_scan_req *req_buffer) +{} +#endif + +/** + * csr_update_score_params() - API to update Score params in RSO + * @mac_ctx: Mac context + * @req_buffer: RSO request buffer + * + * Return: None + */ +static void csr_update_score_params(struct mac_context *mac_ctx, + struct roam_offload_scan_req *req_buffer, + tpCsrNeighborRoamControlInfo roam_info) +{ + struct scoring_param *req_score_params; + struct rssi_scoring *req_rssi_score; + struct wlan_mlme_scoring_cfg *bss_score_params; + struct wlan_mlme_weight_config *weight_config; + struct wlan_mlme_rssi_cfg_score *rssi_score; + + req_score_params = &req_buffer->score_params; + req_rssi_score = &req_score_params->rssi_scoring; + + bss_score_params = &mac_ctx->mlme_cfg->scoring; + weight_config = &bss_score_params->weight_cfg; + rssi_score = &bss_score_params->rssi_score; + + if (!roam_info->cfgParams.enable_scoring_for_roam) + req_score_params->disable_bitmap = + WLAN_ROAM_SCORING_DISABLE_ALL; + + req_score_params->rssi_weightage = weight_config->rssi_weightage; + req_score_params->ht_weightage = weight_config->ht_caps_weightage; + req_score_params->vht_weightage = weight_config->vht_caps_weightage; + req_score_params->he_weightage = weight_config->he_caps_weightage; + req_score_params->bw_weightage = weight_config->chan_width_weightage; + req_score_params->band_weightage = weight_config->chan_band_weightage; + req_score_params->nss_weightage = weight_config->nss_weightage; + req_score_params->esp_qbss_weightage = + weight_config->channel_congestion_weightage; + req_score_params->beamforming_weightage = + weight_config->beamforming_cap_weightage; + req_score_params->pcl_weightage = + weight_config->pcl_weightage; + req_score_params->oce_wan_weightage = weight_config->oce_wan_weightage; + + req_score_params->bw_index_score = + bss_score_params->bandwidth_weight_per_index; + req_score_params->band_index_score = + bss_score_params->band_weight_per_index; + req_score_params->nss_index_score = + bss_score_params->nss_weight_per_index; + + req_score_params->vendor_roam_score_algorithm = + bss_score_params->vendor_roam_score_algorithm; + + req_score_params->roam_score_delta = bss_score_params->roam_score_delta; + req_score_params->roam_trigger_bitmap = + bss_score_params->roam_trigger_bitmap; + + req_rssi_score->best_rssi_threshold = rssi_score->best_rssi_threshold; + req_rssi_score->good_rssi_threshold = rssi_score->good_rssi_threshold; + req_rssi_score->bad_rssi_threshold = rssi_score->bad_rssi_threshold; + req_rssi_score->good_rssi_pcnt = rssi_score->good_rssi_pcnt; + req_rssi_score->bad_rssi_pcnt = rssi_score->bad_rssi_pcnt; + req_rssi_score->good_bucket_size = rssi_score->good_rssi_bucket_size; + req_rssi_score->bad_bucket_size = rssi_score->bad_rssi_bucket_size; + req_rssi_score->rssi_pref_5g_rssi_thresh = + rssi_score->rssi_pref_5g_rssi_thresh; + + req_score_params->esp_qbss_scoring.num_slot = + bss_score_params->esp_qbss_scoring.num_slot; + req_score_params->esp_qbss_scoring.score_pcnt3_to_0 = + bss_score_params->esp_qbss_scoring.score_pcnt3_to_0; + req_score_params->esp_qbss_scoring.score_pcnt7_to_4 = + bss_score_params->esp_qbss_scoring.score_pcnt7_to_4; + req_score_params->esp_qbss_scoring.score_pcnt11_to_8 = + bss_score_params->esp_qbss_scoring.score_pcnt11_to_8; + req_score_params->esp_qbss_scoring.score_pcnt15_to_12 = + bss_score_params->esp_qbss_scoring.score_pcnt15_to_12; + + req_score_params->oce_wan_scoring.num_slot = + bss_score_params->oce_wan_scoring.num_slot; + req_score_params->oce_wan_scoring.score_pcnt3_to_0 = + bss_score_params->oce_wan_scoring.score_pcnt3_to_0; + req_score_params->oce_wan_scoring.score_pcnt7_to_4 = + bss_score_params->oce_wan_scoring.score_pcnt7_to_4; + req_score_params->oce_wan_scoring.score_pcnt11_to_8 = + bss_score_params->oce_wan_scoring.score_pcnt11_to_8; + req_score_params->oce_wan_scoring.score_pcnt15_to_12 = + bss_score_params->oce_wan_scoring.score_pcnt15_to_12; + req_score_params->cand_min_roam_score_delta = + bss_score_params->min_roam_score_delta; +} + +uint8_t csr_get_roam_enabled_sta_sessionid(struct mac_context *mac_ctx) +{ + struct csr_roam_session *session; + uint8_t i; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + session = CSR_GET_SESSION(mac_ctx, i); + if (!session || !CSR_IS_SESSION_VALID(mac_ctx, i)) + continue; + if (!session->pCurRoamProfile || + session->pCurRoamProfile->csrPersona != QDF_STA_MODE) + continue; + + if (MLME_IS_ROAM_INITIALIZED(mac_ctx->psoc, i)) + return i; + } + + return WLAN_UMAC_VDEV_ID_MAX; +} + +#ifdef WLAN_ADAPTIVE_11R +static bool +csr_is_adaptive_11r_roam_supported(struct mac_context *mac_ctx, + struct csr_roam_session *session) +{ + if (session->is_adaptive_11r_connection) + return mac_ctx->mlme_cfg->lfr.tgt_adaptive_11r_cap; + + return true; +} +#else +static bool +csr_is_adaptive_11r_roam_supported(struct mac_context *mac_ctx, + struct csr_roam_session *session) + +{ + return true; +} +#endif + +QDF_STATUS +csr_post_rso_stop(struct mac_context *mac, uint8_t vdev_id, uint16_t reason) +{ + QDF_STATUS status; + struct roam_offload_scan_req *req; + tpCsrNeighborRoamControlInfo roam_info; + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac, vdev_id); + if (!session) { + sme_err("ROAM: incorrect vdev ID %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return QDF_STATUS_E_NOMEM; + + req->sessionId = vdev_id; + req->Command = ROAM_SCAN_OFFLOAD_STOP; + + if (reason == REASON_DRIVER_DISABLED) + req->reason = REASON_ROAM_STOP_ALL; + else if (reason == REASON_SUPPLICANT_DISABLED_ROAMING) + req->reason = REASON_SUPPLICANT_DISABLED_ROAMING; + else if (reason == REASON_DISCONNECTED) + req->reason = REASON_DISCONNECTED; + else + req->reason = REASON_SME_ISSUED; + + if (csr_neighbor_middle_of_roaming(mac, vdev_id)) + req->middle_of_roaming = 1; + else + csr_roam_reset_roam_params(mac); + + /* + * Disable offload_11k_params for current vdev + */ + req->offload_11k_params.vdev_id = vdev_id; + + status = csr_roam_send_rso_cmd(mac, vdev_id, req); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("ROAM: Post RSO stop failed, vdev_id: %d", vdev_id); + return QDF_STATUS_E_FAULT; + } + roam_info->last_sent_cmd = ROAM_SCAN_OFFLOAD_STOP; + + return status; +} + +static QDF_STATUS +csr_roam_switch_to_init(struct mac_context *mac, uint8_t vdev_id, + uint8_t reason) +{ + enum roam_offload_state cur_state; + uint8_t temp_vdev_id; + uint32_t roaming_bitmap; + QDF_STATUS status; + + cur_state = mlme_get_roam_state(mac->psoc, vdev_id); + switch (cur_state) { + case ROAM_DEINIT: + roaming_bitmap = mlme_get_roam_trigger_bitmap(mac->psoc, + vdev_id); + if (!roaming_bitmap) { + sme_info("ROAM: Cannot change to INIT state for vdev[%d]", + vdev_id); + return QDF_STATUS_E_FAILURE; + } + + /* + * Disable roaming on the enabled sta if supplicant wants to + * enable roaming on this vdev id + */ + temp_vdev_id = csr_get_roam_enabled_sta_sessionid(mac); + if ((temp_vdev_id != WLAN_UMAC_VDEV_ID_MAX) && + (vdev_id != temp_vdev_id)) { + /* + * Roam init state can be requested as part of + * initial connection or due to enable from + * supplicant via vendor command. This check will + * ensure roaming does not get enabled on this STA + * vdev id if it is not an explicit enable from + * supplicant. + */ + if (reason != REASON_SUPPLICANT_INIT_ROAMING) { + sme_info("ROAM: Roam module already initialized on vdev:[%d]", + temp_vdev_id); + return QDF_STATUS_E_FAILURE; + } + csr_post_roam_state_change(mac, temp_vdev_id, + ROAM_DEINIT, reason); + } + break; + + case ROAM_INIT: + case ROAM_RSO_STOPPED: + case ROAM_RSO_STARTED: + /* + * Already the roaming module is initialized at fw, + * just return from here + */ + default: + return QDF_STATUS_SUCCESS; + } + + status = csr_send_roam_offload_init_msg(mac, vdev_id, true); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + mlme_set_roam_state(mac->psoc, vdev_id, ROAM_INIT); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +csr_roam_switch_to_rso_start(struct mac_context *mac, uint8_t vdev_id, + uint8_t reason) +{ + enum roam_offload_state cur_state; + QDF_STATUS status; + uint8_t control_bitmap; + bool sup_disabled_roaming; + bool rso_allowed = csr_roam_is_roam_offload_scan_enabled(mac); + uint8_t rso_command = ROAM_SCAN_OFFLOAD_START; + + cur_state = mlme_get_roam_state(mac->psoc, vdev_id); + switch (cur_state) { + case ROAM_INIT: + case ROAM_RSO_STOPPED: + break; + + case ROAM_DEINIT: + status = csr_roam_switch_to_init(mac, vdev_id, reason); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + break; + case ROAM_RSO_STARTED: + /* + * Send RSO update config if roaming already enabled + */ + rso_command = ROAM_SCAN_OFFLOAD_UPDATE_CFG; + break; + default: + return QDF_STATUS_SUCCESS; + } + + if (!rso_allowed) { + sme_debug("ROAM: RSO disabled via INI"); + return QDF_STATUS_E_FAILURE; + } + + control_bitmap = mlme_get_operations_bitmap(mac->psoc, vdev_id); + if (control_bitmap) { + sme_debug("ROAM: RSO Disabled internaly: vdev[%d] bitmap[0x%x]", + vdev_id, control_bitmap); + return QDF_STATUS_E_FAILURE; + } + + status = csr_roam_offload_scan(mac, vdev_id, rso_command, reason); + if (QDF_IS_STATUS_ERROR(status)) { + sme_debug("ROAM: RSO start failed"); + return status; + } + mlme_set_roam_state(mac->psoc, vdev_id, ROAM_RSO_STARTED); + + /* + * If supplicant disabled roaming, driver does not send + * RSO cmd to fw. This causes roam invoke to fail in FW + * since RSO start never happened at least once to + * configure roaming engine in FW. + */ + sup_disabled_roaming = mlme_get_supplicant_disabled_roaming(mac->psoc, + vdev_id); + if (!sup_disabled_roaming) + return QDF_STATUS_SUCCESS; + + sme_debug("ROAM: RSO disabled by Supplicant on vdev[%d]", vdev_id); + return csr_post_roam_state_change(mac, vdev_id, ROAM_RSO_STOPPED, + REASON_SUPPLICANT_DISABLED_ROAMING); +} + +static QDF_STATUS +csr_roam_switch_to_rso_stop(struct mac_context *mac, uint8_t vdev_id, + uint8_t reason) +{ + enum roam_offload_state cur_state; + QDF_STATUS status; + + cur_state = mlme_get_roam_state(mac->psoc, vdev_id); + switch (cur_state) { + case ROAM_RSO_STARTED: + status = csr_post_rso_stop(mac, vdev_id, reason); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("ROAM: Unable to switch to RSO STOP State"); + return QDF_STATUS_E_FAILURE; + } + break; + + case ROAM_DEINIT: + case ROAM_RSO_STOPPED: + case ROAM_INIT: + /* + * Already the roaming module is initialized at fw, + * nothing to do here + */ + default: + return QDF_STATUS_SUCCESS; + } + mlme_set_roam_state(mac->psoc, vdev_id, ROAM_RSO_STOPPED); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +csr_roam_switch_to_deinit(struct mac_context *mac, uint8_t vdev_id, + uint8_t reason) +{ + QDF_STATUS status; + bool sup_disabled_roam; + enum roam_offload_state cur_state = mlme_get_roam_state(mac->psoc, + vdev_id); + switch (cur_state) { + case ROAM_RSO_STARTED: + csr_roam_switch_to_rso_stop(mac, vdev_id, reason); + break; + case ROAM_RSO_STOPPED: + /* + * When Supplicant disabled roaming is set and roam invoke + * command is received from userspace, fw starts to roam. + * But meanwhile if a disassoc/deauth is received from AP or if + * NB disconnect is initiated while supplicant disabled roam, + * RSO stop with ROAM scan mode as 0 is not sent to firmware + * since the previous state was RSO_STOPPED. This could lead + * to firmware not sending peer unmap event for the current + * AP. To avoid this, if previous RSO stop was sent with + * ROAM scan mode as 4, send RSO stop with Roam scan mode as 0 + * and then switch to ROAM_DEINIT. + */ + sup_disabled_roam = + mlme_get_supplicant_disabled_roaming(mac->psoc, + vdev_id); + if (sup_disabled_roam) { + sme_debug("vdev[%d]: supplicant disabled roam. clear roam scan mode", + vdev_id); + csr_post_rso_stop(mac, vdev_id, REASON_DISCONNECTED); + } + break; + + case ROAM_INIT: + break; + + case ROAM_DEINIT: + /* + * Already the roaming module is de-initialized at fw, + * do nothing here + */ + default: + return QDF_STATUS_SUCCESS; + } + + status = csr_send_roam_offload_init_msg(mac, vdev_id, false); + + if (QDF_IS_STATUS_ERROR(status)) + return status; + + mlme_set_roam_state(mac->psoc, vdev_id, ROAM_DEINIT); + mlme_clear_operations_bitmap(mac->psoc, vdev_id); + + if (reason != REASON_SUPPLICANT_INIT_ROAMING) + csr_enable_roaming_on_connected_sta(mac, vdev_id); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +csr_handle_roam_state_change(struct mac_context *mac, uint8_t vdev_id, + enum roam_offload_state requested_state, + uint8_t reason) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (requested_state != ROAM_DEINIT && + !csr_is_conn_state_connected_infra(mac, vdev_id)) { + sme_debug("ROAM: roam state change requested in disconnected state"); + return status; + } + + switch (requested_state) { + case ROAM_DEINIT: + status = csr_roam_switch_to_deinit(mac, vdev_id, + reason); + break; + case ROAM_INIT: + status = csr_roam_switch_to_init(mac, vdev_id, + reason); + break; + case ROAM_RSO_STARTED: + status = csr_roam_switch_to_rso_start(mac, vdev_id, + reason); + break; + case ROAM_RSO_STOPPED: + status = csr_roam_switch_to_rso_stop(mac, vdev_id, + reason); + break; + default: + sme_debug("ROAM: Invalid roam state %d", requested_state); + break; + } + + return status; +} + +QDF_STATUS +csr_post_roam_state_change(struct mac_context *mac, uint8_t vdev_id, + enum roam_offload_state state, uint8_t reason) +{ + return csr_handle_roam_state_change(mac, vdev_id, state, reason); +} + +/** + * csr_roam_offload_scan() - populates roam offload scan request and sends to + * WMA + * + * parameters + * @mac_ctx: global mac ctx + * @session_id: session id + * @command: roam scan offload command input + * @reason: reason to roam + * + * Return: result of operation + */ +QDF_STATUS +csr_roam_offload_scan(struct mac_context *mac_ctx, uint8_t session_id, + uint8_t command, uint8_t reason) +{ + uint8_t *state = NULL; + struct roam_offload_scan_req *req_buf; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + tpCsrNeighborRoamControlInfo roam_info = + &mac_ctx->roam.neighborRoamInfo[session_id]; + struct roam_ext_params *roam_params_dst; + struct roam_ext_params *roam_params_src; + uint8_t temp_session_id; + bool prev_roaming_state; + enum csr_akm_type roam_profile_akm = eCSR_AUTH_TYPE_UNKNOWN; + uint32_t fw_akm_bitmap; + bool p2p_disable_sta_roaming = 0, nan_disable_sta_roaming = 0; + + sme_debug("RSO Command %d, vdev %d, Reason %d", command, + session_id, reason); + if (!session) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "session is null"); + return QDF_STATUS_E_FAILURE; + } + + if (roam_info->neighborRoamState != + eCSR_NEIGHBOR_ROAM_STATE_CONNECTED && + (command == ROAM_SCAN_OFFLOAD_UPDATE_CFG || + command == ROAM_SCAN_OFFLOAD_START || + command == ROAM_SCAN_OFFLOAD_RESTART)) { + sme_debug("Session not in connected state, RSO not sent and state=%d ", + roam_info->neighborRoamState); + return QDF_STATUS_E_FAILURE; + } + + temp_session_id = csr_get_roam_enabled_sta_sessionid(mac_ctx); + if ((temp_session_id != WLAN_UMAC_VDEV_ID_MAX) && + (session_id != temp_session_id)) { + sme_debug("Roam cmd not for session %d on which roaming is enabled", + temp_session_id); + return QDF_STATUS_E_FAILURE; + } + + if (session->pCurRoamProfile) + roam_profile_akm = + session->pCurRoamProfile->AuthType.authType[0]; + else + sme_info("Roam profile is NULL"); + + if (CSR_IS_AKM_FILS(roam_profile_akm) && + !mac_ctx->is_fils_roaming_supported) { + sme_info("FILS Roaming not suppprted by fw"); + return QDF_STATUS_SUCCESS; + } + + if (!csr_is_adaptive_11r_roam_supported(mac_ctx, session)) { + sme_info("Adaptive 11r Roaming not suppprted by fw"); + return QDF_STATUS_SUCCESS; + } + + fw_akm_bitmap = mac_ctx->mlme_cfg->lfr.fw_akm_bitmap; + /* Roaming is not supported currently for OWE akm */ + if (roam_profile_akm == eCSR_AUTH_TYPE_OWE && + !CSR_IS_FW_OWE_ROAM_SUPPORTED(fw_akm_bitmap)) { + sme_info("OWE Roaming not suppprted by fw"); + return QDF_STATUS_SUCCESS; + } + + /* Roaming is not supported for SAE authentication */ + if (CSR_IS_AUTH_TYPE_SAE(roam_profile_akm) && + !CSR_IS_FW_SAE_ROAM_SUPPORTED(fw_akm_bitmap)) { + sme_info("Roaming not suppprted for SAE connection"); + return QDF_STATUS_SUCCESS; + } + + /* + * If fw doesn't advertise FT SAE, FT-FILS or FT-Suite-B capability, + * don't support roaming to that profile + */ + if (CSR_IS_AKM_FT_SAE(roam_profile_akm)) { + if (!CSR_IS_FW_FT_SAE_SUPPORTED(fw_akm_bitmap)) { + sme_info("Roaming not suppprted for FT SAE akm"); + return QDF_STATUS_SUCCESS; + } + } + + if (CSR_IS_AKM_FT_SUITEB_SHA384(roam_profile_akm)) { + if (!CSR_IS_FW_FT_SUITEB_SUPPORTED(fw_akm_bitmap)) { + sme_info("Roaming not suppprted for FT Suite-B akm"); + return QDF_STATUS_SUCCESS; + } + } + + if (CSR_IS_AKM_FT_FILS(roam_profile_akm)) { + if (!CSR_IS_FW_FT_FILS_SUPPORTED(fw_akm_bitmap)) { + sme_info("Roaming not suppprted for FT FILS akm"); + return QDF_STATUS_SUCCESS; + } + } + + p2p_disable_sta_roaming = + (cfg_p2p_is_roam_config_disabled(mac_ctx->psoc) && + (policy_mgr_mode_specific_connection_count(mac_ctx->psoc, + PM_P2P_CLIENT_MODE, NULL) || + policy_mgr_mode_specific_connection_count(mac_ctx->psoc, + PM_P2P_GO_MODE, NULL))); + nan_disable_sta_roaming = + (cfg_nan_is_roam_config_disabled(mac_ctx->psoc) && + policy_mgr_mode_specific_connection_count(mac_ctx->psoc, + PM_NDI_MODE, NULL)); + + if ((command == ROAM_SCAN_OFFLOAD_START || + command == ROAM_SCAN_OFFLOAD_UPDATE_CFG) && + (p2p_disable_sta_roaming || nan_disable_sta_roaming)) { + sme_info("roaming not supported for active %s connection", + p2p_disable_sta_roaming ? "p2p" : "ndi"); + return QDF_STATUS_E_FAILURE; + } + /* + * The Dynamic Config Items Update may happen even if the state is in + * INIT. It is important to ensure that the command is passed down to + * the FW only if the Infra Station is in a connected state. A connected + * station could also be in a PREAUTH or REASSOC states. + * 1) Block all CMDs that are not STOP in INIT State. For STOP always + * inform firmware irrespective of state. + * 2) Block update cfg CMD if its for REASON_ROAM_SET_BLACKLIST_BSSID, + * because we need to inform firmware of blacklisted AP for PNO in + * all states. + */ + if ((roam_info->neighborRoamState == eCSR_NEIGHBOR_ROAM_STATE_INIT) && + (command != ROAM_SCAN_OFFLOAD_STOP) && + (reason != REASON_ROAM_SET_BLACKLIST_BSSID)) { + state = mac_trace_get_neighbour_roam_state( + roam_info->neighborRoamState); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Scan Command not sent to FW state=%s and cmd=%d"), + state, command); + return QDF_STATUS_E_FAILURE; + } + + req_buf = csr_create_roam_scan_offload_request(mac_ctx, command, + session_id, reason, + session, roam_info); + if (!req_buf) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Failed to create req packet"); + return QDF_STATUS_E_FAILURE; + } + roam_params_dst = &req_buf->roam_params; + roam_params_src = &mac_ctx->roam.configParam.roam_params; + if (reason == REASON_ROAM_SET_SSID_ALLOWED) + check_allowed_ssid_list(req_buf, roam_params_src); + + /* + * If rssi disallow bssid list have any member + * fill it and send it to firmware so that firmware does not + * try to roam to these BSS until RSSI OR time condition are + * matched. + */ + csr_add_rssi_reject_ap_list(mac_ctx, roam_params_src); + + /* + * Configure the lookup threshold either from INI or from framework. + * If both are present, give higher priority to the one from framework. + */ + if (roam_params_src->alert_rssi_threshold) + req_buf->LookupThreshold = + roam_params_src->alert_rssi_threshold; + else + req_buf->LookupThreshold = + (int8_t)roam_info->cfgParams.neighborLookupThreshold * + (-1); + req_buf->rssi_thresh_offset_5g = + roam_info->cfgParams.rssi_thresh_offset_5g; + qdf_mem_copy(roam_params_dst, roam_params_src, + sizeof(*roam_params_dst)); + roam_params_dst->traffic_threshold = + mac_ctx->mlme_cfg->lfr.roam_dense_traffic_threshold; + roam_params_dst->dense_rssi_thresh_offset = + mac_ctx->mlme_cfg->lfr.roam_dense_rssi_thre_offset; + roam_params_dst->dense_min_aps_cnt = + mac_ctx->mlme_cfg->lfr.roam_dense_min_aps; + roam_params_dst->bg_scan_bad_rssi_thresh = + mac_ctx->mlme_cfg->lfr.roam_bg_scan_bad_rssi_threshold; + roam_params_dst->bg_scan_client_bitmap = + mac_ctx->mlme_cfg->lfr.roam_bg_scan_client_bitmap; + roam_params_dst->roam_bad_rssi_thresh_offset_2g = + mac_ctx->mlme_cfg->lfr.roam_bg_scan_bad_rssi_offset_2g; + roam_params_dst->roam_data_rssi_threshold_triggers = + mac_ctx->mlme_cfg->lfr.roam_data_rssi_threshold_triggers; + roam_params_dst->roam_data_rssi_threshold = + mac_ctx->mlme_cfg->lfr.roam_data_rssi_threshold; + roam_params_dst->rx_data_inactivity_time = + mac_ctx->mlme_cfg->lfr.rx_data_inactivity_time; + roam_params_dst->raise_rssi_thresh_5g = + mac_ctx->mlme_cfg->lfr.rssi_boost_threshold_5g; + roam_params_dst->drop_rssi_thresh_5g = + mac_ctx->mlme_cfg->lfr.rssi_penalize_threshold_5g; + roam_params_dst->raise_factor_5g = + mac_ctx->mlme_cfg->lfr.rssi_boost_factor_5g; + roam_params_dst->drop_factor_5g = + mac_ctx->mlme_cfg->lfr.rssi_penalize_factor_5g; + roam_params_dst->max_raise_rssi_5g = + mac_ctx->mlme_cfg->lfr.max_rssi_boost_5g; + roam_params_dst->max_drop_rssi_5g = + mac_ctx->mlme_cfg->lfr.max_rssi_penalize_5g; + + /* + * rssi_diff which is updated via framework is equivalent to the + * INI RoamRssiDiff parameter and hence should be updated. + */ + if (roam_params_src->rssi_diff) + req_buf->RoamRssiDiff = roam_params_src->rssi_diff; + + /* Set initial dense roam status */ + if (mac_ctx->scan.roam_candidate_count[session_id] > + roam_params_dst->dense_min_aps_cnt) + roam_params_dst->initial_dense_status = true; + + req_buf->hi_rssi_scan_max_count = + roam_info->cfgParams.hi_rssi_scan_max_count; + req_buf->hi_rssi_scan_delay = + roam_info->cfgParams.hi_rssi_scan_delay; + req_buf->hi_rssi_scan_rssi_ub = + roam_info->cfgParams.hi_rssi_scan_rssi_ub; + /* + * If the current operation channel is 5G frequency band, then + * there is no need to enable the HI_RSSI feature. This feature + * is useful only if we are connected to a 2.4 GHz AP and we wish + * to connect to a better 5GHz AP is available. + */ + if (session->disable_hi_rssi) + req_buf->hi_rssi_scan_rssi_delta = 0; + else + req_buf->hi_rssi_scan_rssi_delta = + roam_info->cfgParams.hi_rssi_scan_rssi_delta; + + if (command != ROAM_SCAN_OFFLOAD_STOP) { + req_buf->assoc_ie.length = session->nAddIEAssocLength; + qdf_mem_copy(req_buf->assoc_ie.addIEdata, + session->pAddIEAssoc, + session->nAddIEAssocLength); + csr_update_driver_assoc_ies(mac_ctx, session, req_buf); + csr_update_score_params(mac_ctx, req_buf, roam_info); + csr_update_fils_params_rso(mac_ctx, session, req_buf); + } + + /* + * 11k offload is enabled during RSO Start after connect indication and + * 11k offload is disabled during RSO Stop after disconnect indication + */ + if (command == ROAM_SCAN_OFFLOAD_START) + csr_update_11k_offload_params(mac_ctx, session, req_buf, TRUE); + else if (command == ROAM_SCAN_OFFLOAD_STOP) + csr_update_11k_offload_params(mac_ctx, session, req_buf, FALSE); + + prev_roaming_state = mlme_get_roam_state(mac_ctx->psoc, session_id); + policy_mgr_set_pcl_for_existing_combo(mac_ctx->psoc, PM_STA_MODE); + + /* Update PER config to FW. No need to update in case of stop command, + * FW takes care of stopping this internally + */ + if (command != ROAM_SCAN_OFFLOAD_STOP) + csr_roam_offload_per_scan(mac_ctx, session_id); + + if (!QDF_IS_STATUS_SUCCESS( + csr_roam_send_rso_cmd(mac_ctx, session_id, req_buf))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Not able to post message to PE", + __func__); + mlme_set_roam_state(mac_ctx->psoc, session_id, + prev_roaming_state); + policy_mgr_set_pcl_for_existing_combo(mac_ctx->psoc, + PM_STA_MODE); + return QDF_STATUS_E_FAILURE; + } + /* update the last sent cmd */ + roam_info->last_sent_cmd = command; + + return status; +} + +QDF_STATUS +csr_roam_offload_scan_rsp_hdlr(struct mac_context *mac, + struct roam_offload_scan_rsp *scanOffloadRsp) +{ + switch (scanOffloadRsp->reason) { + case 0: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Rsp for Roam Scan Offload with failure status"); + break; + case REASON_OS_REQUESTED_ROAMING_NOW: + csr_neighbor_roam_proceed_with_handoff_req(mac, + scanOffloadRsp->sessionId); + break; + + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Rsp for Roam Scan Offload with reason %d", + scanOffloadRsp->reason); + } + return QDF_STATUS_SUCCESS; +} +#endif + +tSmeCmd *csr_get_command_buffer(struct mac_context *mac) +{ + tSmeCmd *pCmd = sme_get_command_buffer(mac); + + if (pCmd) + mac->roam.sPendingCommands++; + + return pCmd; +} + +static void csr_free_cmd_memory(struct mac_context *mac, tSmeCmd *pCommand) +{ + if (!pCommand) { + sme_err("pCommand is NULL"); + return; + } + switch (pCommand->command) { + case eSmeCommandRoam: + csr_release_command_roam(mac, pCommand); + break; + case eSmeCommandWmStatusChange: + csr_release_command_wm_status_change(mac, pCommand); + break; + case e_sme_command_set_hw_mode: + csr_release_command_set_hw_mode(mac, pCommand); + default: + break; + } +} + +void csr_release_command_buffer(struct mac_context *mac, tSmeCmd *pCommand) +{ + if (mac->roam.sPendingCommands > 0) { + /* + * All command allocated through csr_get_command_buffer + * need to decrement the pending count when releasing + */ + mac->roam.sPendingCommands--; + csr_free_cmd_memory(mac, pCommand); + sme_release_command(mac, pCommand); + } else { + sme_err("no pending commands"); + QDF_ASSERT(0); + } +} + +void csr_release_command(struct mac_context *mac_ctx, tSmeCmd *sme_cmd) +{ + struct wlan_serialization_queued_cmd_info cmd_info; + struct wlan_serialization_command cmd; + struct wlan_objmgr_vdev *vdev; + + if (!sme_cmd) { + sme_err("sme_cmd is NULL"); + return; + } + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + sme_cmd->vdev_id, WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Invalid vdev"); + return; + } + qdf_mem_zero(&cmd_info, + sizeof(struct wlan_serialization_queued_cmd_info)); + cmd_info.cmd_id = sme_cmd->cmd_id; + cmd_info.req_type = WLAN_SER_CANCEL_NON_SCAN_CMD; + cmd_info.cmd_type = csr_get_cmd_type(sme_cmd); + cmd_info.vdev = vdev; + qdf_mem_zero(&cmd, sizeof(struct wlan_serialization_command)); + cmd.cmd_id = cmd_info.cmd_id; + cmd.cmd_type = cmd_info.cmd_type; + cmd.vdev = cmd_info.vdev; + if (wlan_serialization_is_cmd_present_in_active_queue( + mac_ctx->psoc, &cmd)) { + cmd_info.queue_type = WLAN_SERIALIZATION_ACTIVE_QUEUE; + wlan_serialization_remove_cmd(&cmd_info); + } else if (wlan_serialization_is_cmd_present_in_pending_queue( + mac_ctx->psoc, &cmd)) { + cmd_info.queue_type = WLAN_SERIALIZATION_PENDING_QUEUE; + wlan_serialization_cancel_request(&cmd_info); + } else { + sme_debug("can't find cmd_id %d cmd_type %d", cmd_info.cmd_id, + cmd_info.cmd_type); + } + if (cmd_info.vdev) + wlan_objmgr_vdev_release_ref(cmd_info.vdev, WLAN_LEGACY_SME_ID); +} + + +static enum wlan_serialization_cmd_type csr_get_roam_cmd_type( + tSmeCmd *sme_cmd) +{ + enum wlan_serialization_cmd_type cmd_type = WLAN_SER_CMD_MAX; + + switch (sme_cmd->u.roamCmd.roamReason) { + case eCsrForcedDisassoc: + case eCsrForcedDeauth: + case eCsrForcedDisassocMICFailure: + cmd_type = WLAN_SER_CMD_VDEV_DISCONNECT; + break; + case eCsrHddIssued: + if (csr_is_bss_type_ibss( + sme_cmd->u.roamCmd.roamProfile.BSSType) || + CSR_IS_INFRA_AP(&sme_cmd->u.roamCmd.roamProfile) || + CSR_IS_NDI(&sme_cmd->u.roamCmd.roamProfile)) + cmd_type = WLAN_SER_CMD_VDEV_START_BSS; + else + cmd_type = WLAN_SER_CMD_VDEV_CONNECT; + break; + case eCsrHddIssuedReassocToSameAP: + cmd_type = WLAN_SER_CMD_HDD_ISSUE_REASSOC_SAME_AP; + break; + case eCsrSmeIssuedReassocToSameAP: + cmd_type = WLAN_SER_CMD_SME_ISSUE_REASSOC_SAME_AP; + break; + case eCsrSmeIssuedDisassocForHandoff: + cmd_type = + WLAN_SER_CMD_SME_ISSUE_DISASSOC_FOR_HANDOFF; + break; + case eCsrSmeIssuedAssocToSimilarAP: + cmd_type = + WLAN_SER_CMD_SME_ISSUE_ASSOC_TO_SIMILAR_AP; + break; + case eCsrForcedIbssLeave: + cmd_type = WLAN_SER_CMD_FORCE_IBSS_LEAVE; + break; + case eCsrStopBss: + cmd_type = WLAN_SER_CMD_VDEV_STOP_BSS; + break; + case eCsrSmeIssuedFTReassoc: + cmd_type = WLAN_SER_CMD_SME_ISSUE_FT_REASSOC; + break; + case eCsrForcedDisassocSta: + cmd_type = WLAN_SER_CMD_FORCE_DISASSOC_STA; + break; + case eCsrForcedDeauthSta: + cmd_type = WLAN_SER_CMD_FORCE_DEAUTH_STA; + break; + case eCsrPerformPreauth: + cmd_type = WLAN_SER_CMD_PERFORM_PRE_AUTH; + break; + default: + break; + } + + return cmd_type; +} + +enum wlan_serialization_cmd_type csr_get_cmd_type(tSmeCmd *sme_cmd) +{ + enum wlan_serialization_cmd_type cmd_type = WLAN_SER_CMD_MAX; + + switch (sme_cmd->command) { + case eSmeCommandRoam: + cmd_type = csr_get_roam_cmd_type(sme_cmd); + break; + case eSmeCommandWmStatusChange: + cmd_type = WLAN_SER_CMD_WM_STATUS_CHANGE; + break; + case eSmeCommandGetdisconnectStats: + cmd_type = WLAN_SER_CMD_GET_DISCONNECT_STATS; + break; + case eSmeCommandAddTs: + cmd_type = WLAN_SER_CMD_ADDTS; + break; + case eSmeCommandDelTs: + cmd_type = WLAN_SER_CMD_DELTS; + break; + case e_sme_command_set_hw_mode: + cmd_type = WLAN_SER_CMD_SET_HW_MODE; + break; + case e_sme_command_nss_update: + cmd_type = WLAN_SER_CMD_NSS_UPDATE; + break; + case e_sme_command_set_dual_mac_config: + cmd_type = WLAN_SER_CMD_SET_DUAL_MAC_CONFIG; + break; + case e_sme_command_set_antenna_mode: + cmd_type = WLAN_SER_CMD_SET_ANTENNA_MODE; + break; + default: + break; + } + + return cmd_type; +} + +static uint32_t csr_get_monotonous_number(struct mac_context *mac_ctx) +{ + uint32_t cmd_id; + uint32_t mask = 0x00FFFFFF, prefix = 0x0D000000; + + cmd_id = qdf_atomic_inc_return(&mac_ctx->global_cmd_id); + cmd_id = (cmd_id & mask); + cmd_id = (cmd_id | prefix); + + return cmd_id; +} + +static void csr_fill_cmd_timeout(struct wlan_serialization_command *cmd) +{ + switch (cmd->cmd_type) { + case WLAN_SER_CMD_VDEV_DISCONNECT: + case WLAN_SER_CMD_FORCE_IBSS_LEAVE: + case WLAN_SER_CMD_WM_STATUS_CHANGE: + cmd->cmd_timeout_duration = SME_CMD_VDEV_DISCONNECT_TIMEOUT; + break; + case WLAN_SER_CMD_VDEV_START_BSS: + cmd->cmd_timeout_duration = SME_CMD_VDEV_START_BSS_TIMEOUT; + break; + case WLAN_SER_CMD_VDEV_STOP_BSS: + cmd->cmd_timeout_duration = SME_CMD_STOP_BSS_CMD_TIMEOUT; + break; + case WLAN_SER_CMD_FORCE_DISASSOC_STA: + case WLAN_SER_CMD_FORCE_DEAUTH_STA: + cmd->cmd_timeout_duration = SME_CMD_PEER_DISCONNECT_TIMEOUT; + break; + case WLAN_SER_CMD_GET_DISCONNECT_STATS: + cmd->cmd_timeout_duration = + SME_CMD_GET_DISCONNECT_STATS_TIMEOUT; + break; + case WLAN_SER_CMD_HDD_ISSUE_REASSOC_SAME_AP: + case WLAN_SER_CMD_SME_ISSUE_REASSOC_SAME_AP: + case WLAN_SER_CMD_SME_ISSUE_DISASSOC_FOR_HANDOFF: + case WLAN_SER_CMD_SME_ISSUE_ASSOC_TO_SIMILAR_AP: + case WLAN_SER_CMD_SME_ISSUE_FT_REASSOC: + case WLAN_SER_CMD_PERFORM_PRE_AUTH: + cmd->cmd_timeout_duration = SME_CMD_ROAM_CMD_TIMEOUT; + break; + case WLAN_SER_CMD_ADDTS: + case WLAN_SER_CMD_DELTS: + cmd->cmd_timeout_duration = SME_CMD_ADD_DEL_TS_TIMEOUT; + break; + case WLAN_SER_CMD_SET_HW_MODE: + case WLAN_SER_CMD_NSS_UPDATE: + case WLAN_SER_CMD_SET_DUAL_MAC_CONFIG: + case WLAN_SER_CMD_SET_ANTENNA_MODE: + cmd->cmd_timeout_duration = SME_CMD_POLICY_MGR_CMD_TIMEOUT; + break; + case WLAN_SER_CMD_VDEV_CONNECT: + /* fallthrough to use def MAX value */ + default: + cmd->cmd_timeout_duration = SME_ACTIVE_LIST_CMD_TIMEOUT_VALUE; + break; + } +} + +QDF_STATUS csr_set_serialization_params_to_cmd(struct mac_context *mac_ctx, + tSmeCmd *sme_cmd, struct wlan_serialization_command *cmd, + uint8_t high_priority) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!sme_cmd) { + sme_err("Invalid sme_cmd"); + return status; + } + if (!cmd) { + sme_err("Invalid serialization_cmd"); + return status; + } + + /* + * no need to fill command id for non-scan as they will be + * zero always + */ + sme_cmd->cmd_id = csr_get_monotonous_number(mac_ctx); + cmd->cmd_id = sme_cmd->cmd_id; + + cmd->cmd_type = csr_get_cmd_type(sme_cmd); + if (cmd->cmd_type == WLAN_SER_CMD_MAX) { + sme_err("serialization enum not found for sme_cmd type %d", + sme_cmd->command); + return status; + } + cmd->vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, + sme_cmd->vdev_id, WLAN_LEGACY_SME_ID); + if (!cmd->vdev) { + sme_err("vdev is NULL for vdev_id:%d", sme_cmd->vdev_id); + return status; + } + cmd->umac_cmd = sme_cmd; + + csr_fill_cmd_timeout(cmd); + + cmd->cmd_cb = sme_ser_cmd_callback; + cmd->is_high_priority = high_priority; + cmd->is_blocking = true; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS csr_queue_sme_command(struct mac_context *mac_ctx, tSmeCmd *sme_cmd, + bool high_priority) +{ + struct wlan_serialization_command cmd; + struct wlan_objmgr_vdev *vdev = NULL; + enum wlan_serialization_status ser_cmd_status; + QDF_STATUS status; + + if (!SME_IS_START(mac_ctx)) { + sme_err("Sme in stop state"); + QDF_ASSERT(0); + goto error; + } + + if (CSR_IS_WAIT_FOR_KEY(mac_ctx, sme_cmd->vdev_id)) { + if (!CSR_IS_DISCONNECT_COMMAND(sme_cmd)) { + sme_err("Can't process cmd(%d), waiting for key", + sme_cmd->command); + goto error; + } + } + + qdf_mem_zero(&cmd, sizeof(struct wlan_serialization_command)); + status = csr_set_serialization_params_to_cmd(mac_ctx, sme_cmd, + &cmd, high_priority); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failed to set ser params"); + goto error; + } + + vdev = cmd.vdev; + ser_cmd_status = wlan_serialization_request(&cmd); + + switch (ser_cmd_status) { + case WLAN_SER_CMD_PENDING: + case WLAN_SER_CMD_ACTIVE: + /* Command posted to active/pending list */ + status = QDF_STATUS_SUCCESS; + break; + default: + sme_err("Failed to queue command %d with status:%d", + sme_cmd->command, ser_cmd_status); + status = QDF_STATUS_E_FAILURE; + goto error; + } + + return status; + +error: + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + csr_release_command_buffer(mac_ctx, sme_cmd); + + return QDF_STATUS_E_FAILURE; +} + +QDF_STATUS csr_roam_update_config(struct mac_context *mac_ctx, uint8_t session_id, + uint16_t capab, uint32_t value) +{ + struct update_config *msg; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + sme_debug("update HT config requested"); + if (!session) { + sme_err("Session does not exist for session id %d", session_id); + return QDF_STATUS_E_FAILURE; + } + + msg = qdf_mem_malloc(sizeof(*msg)); + if (!msg) + return QDF_STATUS_E_NOMEM; + + msg->messageType = eWNI_SME_UPDATE_CONFIG; + msg->vdev_id = session_id; + msg->capab = capab; + msg->value = value; + msg->length = sizeof(*msg); + status = umac_send_mb_message_to_mac(msg); + + return status; +} + +/* Returns whether a session is in QDF_STA_MODE...or not */ +bool csr_roam_is_sta_mode(struct mac_context *mac, uint32_t sessionId) +{ + struct csr_roam_session *pSession = NULL; + + pSession = CSR_GET_SESSION(mac, sessionId); + + if (!pSession) { + sme_err("session %d not found", sessionId); + return false; + } + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + sme_err("Inactive session_id: %d", sessionId); + return false; + } + if (eCSR_BSS_TYPE_INFRASTRUCTURE != pSession->connectedProfile.BSSType) + return false; + /* There is a possibility that the above check may fail,because + * P2P CLI also uses the same BSSType (eCSR_BSS_TYPE_INFRASTRUCTURE) + * when it is connected.So,we may sneak through the above check even + * if we are not a STA mode INFRA station. So, if we sneak through + * the above condition, we can use the following check if we are + * really in STA Mode. + */ + + if (pSession->pCurRoamProfile) { + if (pSession->pCurRoamProfile->csrPersona == QDF_STA_MODE) + return true; + else + return false; + } + + return false; +} + +QDF_STATUS csr_handoff_request(struct mac_context *mac, + uint8_t sessionId, + tCsrHandoffRequest *pHandoffInfo) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0}; + + tAniHandoffReq *pMsg; + + pMsg = qdf_mem_malloc(sizeof(tAniHandoffReq)); + if (!pMsg) { + return QDF_STATUS_E_NOMEM; + } + pMsg->msgType = eWNI_SME_HANDOFF_REQ; + pMsg->msgLen = (uint16_t) sizeof(tAniHandoffReq); + pMsg->sessionId = sessionId; + pMsg->ch_freq = pHandoffInfo->ch_freq; + pMsg->handoff_src = pHandoffInfo->src; + qdf_mem_copy(pMsg->bssid, pHandoffInfo->bssid.bytes, QDF_MAC_ADDR_SIZE); + msg.type = eWNI_SME_HANDOFF_REQ; + msg.bodyptr = pMsg; + msg.reserved = 0; + if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, + &msg)) { + qdf_mem_free((void *)pMsg); + status = QDF_STATUS_E_FAILURE; + } + return status; +} + +/** + * csr_roam_channel_change_req() - Post channel change request to LIM + * @mac: mac context + * @bssid: SAP bssid + * @ch_params: channel information + * @profile: CSR profile + * + * This API is primarily used to post Channel Change Req for SAP + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_roam_channel_change_req(struct mac_context *mac, + struct qdf_mac_addr bssid, + struct ch_params *ch_params, + struct csr_roam_profile *profile) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSirChanChangeRequest *msg; + struct csr_roamstart_bssparams param; + bool skip_hostapd_rate = !profile->chan_switch_hostapd_rate_enabled; + + /* + * while changing the channel, use basic rates given by driver + * and not by hostapd as there is a chance that hostapd might + * give us rates based on original channel which may not be + * suitable for new channel + */ + qdf_mem_zero(¶m, sizeof(struct csr_roamstart_bssparams)); + + status = csr_roam_get_bss_start_parms(mac, profile, ¶m, + skip_hostapd_rate); + + if (status != QDF_STATUS_SUCCESS) { + sme_err("Failed to get bss parameters"); + return status; + } + + msg = qdf_mem_malloc(sizeof(tSirChanChangeRequest)); + if (!msg) + return QDF_STATUS_E_NOMEM; + + msg->messageType = eWNI_SME_CHANNEL_CHANGE_REQ; + msg->messageLen = sizeof(tSirChanChangeRequest); + msg->target_chan_freq = profile->ChannelInfo.freq_list[0]; + msg->sec_ch_offset = ch_params->sec_ch_offset; + msg->ch_width = profile->ch_params.ch_width; + msg->dot11mode = csr_translate_to_wni_cfg_dot11_mode(mac, + param.uCfgDot11Mode); + if (WLAN_REG_IS_24GHZ_CH_FREQ(msg->target_chan_freq) && + !mac->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band && + (msg->dot11mode == MLME_DOT11_MODE_11AC || + msg->dot11mode == MLME_DOT11_MODE_11AC_ONLY)) + msg->dot11mode = MLME_DOT11_MODE_11N; + msg->nw_type = param.sirNwType; + msg->center_freq_seg_0 = ch_params->center_freq_seg0; + msg->center_freq_seg_1 = ch_params->center_freq_seg1; + msg->cac_duration_ms = profile->cac_duration_ms; + msg->dfs_regdomain = profile->dfs_regdomain; + qdf_mem_copy(msg->bssid, bssid.bytes, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(&msg->operational_rateset, + ¶m.operationalRateSet, + sizeof(msg->operational_rateset)); + qdf_mem_copy(&msg->extended_rateset, ¶m.extendedRateSet, + sizeof(msg->extended_rateset)); + + status = umac_send_mb_message_to_mac(msg); + + return status; +} + +/* + * Post Beacon Tx Start request to LIM + * immediately after SAP CAC WAIT is + * completed without any RADAR indications. + */ +QDF_STATUS csr_roam_start_beacon_req(struct mac_context *mac, + struct qdf_mac_addr bssid, + uint8_t dfsCacWaitStatus) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSirStartBeaconIndication *pMsg; + + pMsg = qdf_mem_malloc(sizeof(tSirStartBeaconIndication)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + + pMsg->messageType = eWNI_SME_START_BEACON_REQ; + pMsg->messageLen = sizeof(tSirStartBeaconIndication); + pMsg->beaconStartStatus = dfsCacWaitStatus; + qdf_mem_copy(pMsg->bssid, bssid.bytes, QDF_MAC_ADDR_SIZE); + + status = umac_send_mb_message_to_mac(pMsg); + + return status; +} + +/* + * csr_roam_modify_add_ies - + * This function sends msg to modify the additional IE buffers in PE + * + * @mac: mac global structure + * @pModifyIE: pointer to tSirModifyIE structure + * @updateType: Type of buffer + * + * + * Return: QDF_STATUS - Success or failure + */ +QDF_STATUS +csr_roam_modify_add_ies(struct mac_context *mac, + tSirModifyIE *pModifyIE, eUpdateIEsType updateType) +{ + tpSirModifyIEsInd pModifyAddIEInd = NULL; + uint8_t *pLocalBuffer = NULL; + QDF_STATUS status; + + /* following buffer will be freed by consumer (PE) */ + pLocalBuffer = qdf_mem_malloc(pModifyIE->ieBufferlength); + if (!pLocalBuffer) + return QDF_STATUS_E_NOMEM; + + pModifyAddIEInd = qdf_mem_malloc(sizeof(tSirModifyIEsInd)); + if (!pModifyAddIEInd) { + qdf_mem_free(pLocalBuffer); + return QDF_STATUS_E_NOMEM; + } + + /*copy the IE buffer */ + qdf_mem_copy(pLocalBuffer, pModifyIE->pIEBuffer, + pModifyIE->ieBufferlength); + qdf_mem_zero(pModifyAddIEInd, sizeof(tSirModifyIEsInd)); + + pModifyAddIEInd->msgType = eWNI_SME_MODIFY_ADDITIONAL_IES; + pModifyAddIEInd->msgLen = sizeof(tSirModifyIEsInd); + + qdf_copy_macaddr(&pModifyAddIEInd->modifyIE.bssid, &pModifyIE->bssid); + + pModifyAddIEInd->modifyIE.vdev_id = pModifyIE->vdev_id; + pModifyAddIEInd->modifyIE.notify = pModifyIE->notify; + pModifyAddIEInd->modifyIE.ieID = pModifyIE->ieID; + pModifyAddIEInd->modifyIE.ieIDLen = pModifyIE->ieIDLen; + pModifyAddIEInd->modifyIE.pIEBuffer = pLocalBuffer; + pModifyAddIEInd->modifyIE.ieBufferlength = pModifyIE->ieBufferlength; + pModifyAddIEInd->modifyIE.oui_length = pModifyIE->oui_length; + + pModifyAddIEInd->updateType = updateType; + + status = umac_send_mb_message_to_mac(pModifyAddIEInd); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to send eWNI_SME_UPDATE_ADDTIONAL_IES msg status %d", + status); + qdf_mem_free(pLocalBuffer); + } + return status; +} + +/* + * csr_roam_update_add_ies - + * This function sends msg to updates the additional IE buffers in PE + * + * @mac: mac global structure + * @sessionId: SME session id + * @bssid: BSSID + * @additionIEBuffer: buffer containing addition IE from hostapd + * @length: length of buffer + * @updateType: Type of buffer + * @append: append or replace completely + * + * + * Return: QDF_STATUS - Success or failure + */ +QDF_STATUS +csr_roam_update_add_ies(struct mac_context *mac, + tSirUpdateIE *pUpdateIE, eUpdateIEsType updateType) +{ + tpSirUpdateIEsInd pUpdateAddIEs = NULL; + uint8_t *pLocalBuffer = NULL; + QDF_STATUS status; + + if (pUpdateIE->ieBufferlength != 0) { + /* Following buffer will be freed by consumer (PE) */ + pLocalBuffer = qdf_mem_malloc(pUpdateIE->ieBufferlength); + if (!pLocalBuffer) { + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(pLocalBuffer, pUpdateIE->pAdditionIEBuffer, + pUpdateIE->ieBufferlength); + } + + pUpdateAddIEs = qdf_mem_malloc(sizeof(tSirUpdateIEsInd)); + if (!pUpdateAddIEs) { + qdf_mem_free(pLocalBuffer); + return QDF_STATUS_E_NOMEM; + } + + pUpdateAddIEs->msgType = eWNI_SME_UPDATE_ADDITIONAL_IES; + pUpdateAddIEs->msgLen = sizeof(tSirUpdateIEsInd); + + qdf_copy_macaddr(&pUpdateAddIEs->updateIE.bssid, &pUpdateIE->bssid); + + pUpdateAddIEs->updateIE.vdev_id = pUpdateIE->vdev_id; + pUpdateAddIEs->updateIE.append = pUpdateIE->append; + pUpdateAddIEs->updateIE.notify = pUpdateIE->notify; + pUpdateAddIEs->updateIE.ieBufferlength = pUpdateIE->ieBufferlength; + pUpdateAddIEs->updateIE.pAdditionIEBuffer = pLocalBuffer; + + pUpdateAddIEs->updateType = updateType; + + status = umac_send_mb_message_to_mac(pUpdateAddIEs); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to send eWNI_SME_UPDATE_ADDTIONAL_IES msg status %d", + status); + qdf_mem_free(pLocalBuffer); + } + return status; +} + +/** + * csr_send_ext_change_channel()- function to post send ECSA + * action frame to lim. + * @mac_ctx: pointer to global mac structure + * @channel: new channel to switch + * @session_id: senssion it should be sent on. + * + * This function is called to post ECSA frame to lim. + * + * Return: success if msg posted to LIM else return failure + */ +QDF_STATUS csr_send_ext_change_channel(struct mac_context *mac_ctx, uint32_t channel, + uint8_t session_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct sir_sme_ext_cng_chan_req *msg; + + msg = qdf_mem_malloc(sizeof(*msg)); + if (!msg) + return QDF_STATUS_E_NOMEM; + + msg->message_type = eWNI_SME_EXT_CHANGE_CHANNEL; + msg->length = sizeof(*msg); + msg->new_channel = channel; + msg->vdev_id = session_id; + status = umac_send_mb_message_to_mac(msg); + return status; +} + +QDF_STATUS csr_csa_restart(struct mac_context *mac_ctx, uint8_t session_id) +{ + QDF_STATUS status; + struct scheduler_msg message = {0}; + + /* Serialize the req through MC thread */ + message.bodyval = session_id; + message.type = eWNI_SME_CSA_RESTART_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("scheduler_post_msg failed!(err=%d)", status); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +/** + * csr_roam_send_chan_sw_ie_request() - Request to transmit CSA IE + * @mac_ctx: Global MAC context + * @bssid: BSSID + * @target_chan_freq: Channel frequency on which to send the IE + * @csa_ie_reqd: Include/Exclude CSA IE. + * @ch_params: operating Channel related information + * + * This function sends request to transmit channel switch announcement + * IE to lower layers + * + * Return: success or failure + **/ +QDF_STATUS csr_roam_send_chan_sw_ie_request(struct mac_context *mac_ctx, + struct qdf_mac_addr bssid, + uint32_t target_chan_freq, + uint8_t csa_ie_reqd, + struct ch_params *ch_params) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSirDfsCsaIeRequest *msg; + + msg = qdf_mem_malloc(sizeof(tSirDfsCsaIeRequest)); + if (!msg) + return QDF_STATUS_E_NOMEM; + + msg->msgType = eWNI_SME_DFS_BEACON_CHAN_SW_IE_REQ; + msg->msgLen = sizeof(tSirDfsCsaIeRequest); + + msg->target_chan_freq = target_chan_freq; + msg->csaIeRequired = csa_ie_reqd; + msg->ch_switch_beacon_cnt = + mac_ctx->sap.SapDfsInfo.sap_ch_switch_beacon_cnt; + msg->ch_switch_mode = mac_ctx->sap.SapDfsInfo.sap_ch_switch_mode; + msg->dfs_ch_switch_disable = + mac_ctx->sap.SapDfsInfo.disable_dfs_ch_switch; + qdf_mem_copy(msg->bssid, bssid.bytes, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(&msg->ch_params, ch_params, sizeof(struct ch_params)); + + status = umac_send_mb_message_to_mac(msg); + + return status; +} + +QDF_STATUS csr_sta_continue_csa(struct mac_context *mac_ctx, uint8_t vdev_id) +{ + QDF_STATUS status; + struct scheduler_msg message = {0}; + + /* Serialize the req through MC thread */ + message.bodyval = vdev_id; + message.type = eWNI_SME_STA_CSA_CONTINUE_REQ; + status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &message); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("eWNI_SME_STA_CSA_CONTINUE_REQ failed!(err=%d)", + status); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +/** + * csr_roaming_report_diag_event() - Diag events for LFR3 + * @mac_ctx: MAC context + * @roam_synch_ind_ptr: Roam Synch Indication Pointer + * @reason: Reason for this event to happen + * + * The major events in the host for LFR3 roaming such as + * roam synch indication, roam synch completion and + * roam synch handoff fail will be indicated to the + * diag framework using this API. + * + * Return: None + */ +void csr_roaming_report_diag_event(struct mac_context *mac_ctx, + struct roam_offload_synch_ind *roam_synch_ind_ptr, + enum csr_diagwlan_status_eventreason reason) +{ + WLAN_HOST_DIAG_EVENT_DEF(roam_connection, + host_event_wlan_status_payload_type); + qdf_mem_zero(&roam_connection, + sizeof(host_event_wlan_status_payload_type)); + switch (reason) { + case eCSR_REASON_ROAM_SYNCH_IND: + roam_connection.eventId = eCSR_WLAN_STATUS_CONNECT; + if (roam_synch_ind_ptr) { + roam_connection.rssi = roam_synch_ind_ptr->rssi; + roam_connection.channel = + cds_freq_to_chan(roam_synch_ind_ptr->chan_freq); + } + break; + case eCSR_REASON_ROAM_SYNCH_CNF: + roam_connection.eventId = eCSR_WLAN_STATUS_CONNECT; + break; + case eCSR_REASON_ROAM_HO_FAIL: + roam_connection.eventId = eCSR_WLAN_STATUS_DISCONNECT; + break; + default: + sme_err("LFR3: Unsupported reason %d", reason); + return; + } + roam_connection.reason = reason; + WLAN_HOST_DIAG_EVENT_REPORT(&roam_connection, EVENT_WLAN_STATUS_V2); +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +void csr_process_ho_fail_ind(struct mac_context *mac_ctx, void *msg_buf) +{ + struct handoff_failure_ind *pSmeHOFailInd = msg_buf; + struct mlme_roam_after_data_stall *vdev_roam_params; + struct wlan_objmgr_vdev *vdev; + struct reject_ap_info ap_info; + uint32_t sessionId; + + if (!pSmeHOFailInd) { + sme_err("LFR3: Hand-Off Failure Ind is NULL"); + return; + } + + sessionId = pSmeHOFailInd->vdev_id; + ap_info.bssid = pSmeHOFailInd->bssid; + ap_info.reject_ap_type = DRIVER_AVOID_TYPE; + ap_info.reject_reason = REASON_ROAM_HO_FAILURE; + ap_info.source = ADDED_BY_DRIVER; + wlan_blm_add_bssid_to_reject_list(mac_ctx->pdev, &ap_info); + + + /* Roaming is supported only on Infra STA Mode. */ + if (!csr_roam_is_sta_mode(mac_ctx, sessionId)) { + sme_err("LFR3:HO Fail cannot be handled for session %d", + sessionId); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, sessionId, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("LFR3: vdev is NULL"); + return; + } + + vdev_roam_params = mlme_get_roam_invoke_params(vdev); + if (vdev_roam_params) + vdev_roam_params->roam_invoke_in_progress = false; + else + sme_err("LFR3: Vdev roam params is NULL"); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + mac_ctx->sme.set_connection_info_cb(false); + csr_roam_roaming_offload_timer_action(mac_ctx, 0, sessionId, + ROAMING_OFFLOAD_TIMER_STOP); + csr_roam_call_callback(mac_ctx, sessionId, NULL, 0, + eCSR_ROAM_NAPI_OFF, eCSR_ROAM_RESULT_FAILURE); + csr_roam_synch_clean_up(mac_ctx, sessionId); + csr_roaming_report_diag_event(mac_ctx, NULL, + eCSR_REASON_ROAM_HO_FAIL); + csr_roam_disconnect(mac_ctx, sessionId, + eCSR_DISCONNECT_REASON_ROAM_HO_FAIL, + eSIR_MAC_FW_TRIGGERED_ROAM_FAILURE); + if (mac_ctx->mlme_cfg->gen.fatal_event_trigger) + cds_flush_logs(WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_HOST_DRIVER, + WLAN_LOG_REASON_ROAM_HO_FAILURE, + false, false); +} +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + +/** + * csr_update_op_class_array() - update op class for each band + * @mac_ctx: mac global context + * @op_classes: out param, operating class array to update + * @channel_info: channel info + * @ch_name: channel band name to display in debug messages + * @i: out param, stores number of operating classes + * + * Return: void + */ +static void +csr_update_op_class_array(struct mac_context *mac_ctx, + uint8_t *op_classes, + struct csr_channel *channel_info, + char *ch_name, + uint8_t *i) +{ + uint8_t j = 0, idx = 0, class = 0; + bool found = false; + uint8_t num_channels = channel_info->numChannels; + uint8_t ch_num; + + sme_debug("Num %s channels, %d", ch_name, num_channels); + + for (idx = 0; idx < num_channels && + *i < (REG_MAX_SUPP_OPER_CLASSES - 1); idx++) { + wlan_reg_freq_to_chan_op_class( + mac_ctx->pdev, channel_info->channel_freq_list[idx], + true, BIT(BEHAV_NONE), &class, &ch_num); + + found = false; + for (j = 0; j < REG_MAX_SUPP_OPER_CLASSES - 1; j++) { + if (op_classes[j] == class) { + found = true; + break; + } + } + + if (!found) { + op_classes[*i] = class; + *i = *i + 1; + } + } +} + +/** + * csr_init_operating_classes() - update op class for all bands + * @mac: pointer to mac context. + * + * Return: void + */ +static void csr_init_operating_classes(struct mac_context *mac) +{ + uint8_t i = 0; + uint8_t j = 0; + uint8_t swap = 0; + uint8_t numClasses = 0; + uint8_t opClasses[REG_MAX_SUPP_OPER_CLASSES] = {0,}; + + sme_debug("Current Country = %c%c", + mac->scan.countryCodeCurrent[0], + mac->scan.countryCodeCurrent[1]); + + csr_update_op_class_array(mac, opClasses, + &mac->scan.base_channels, "20MHz", &i); + numClasses = i; + + /* As per spec the operating classes should be in ascending order. + * Bubble sort is fine since we don't have many classes + */ + for (i = 0; i < (numClasses - 1); i++) { + for (j = 0; j < (numClasses - i - 1); j++) { + /* For decreasing order use < */ + if (opClasses[j] > opClasses[j + 1]) { + swap = opClasses[j]; + opClasses[j] = opClasses[j + 1]; + opClasses[j + 1] = swap; + } + } + } + + /* Set the ordered list of op classes in regdomain + * for use by other modules + */ + wlan_reg_dmn_set_curr_opclasses(numClasses, &opClasses[0]); +} + +/** + * csr_find_session_by_type() - This function will find given session type from + * all sessions. + * @mac_ctx: pointer to mac context. + * @type: session type + * + * Return: session id for give session type. + **/ +static uint32_t +csr_find_session_by_type(struct mac_context *mac_ctx, enum QDF_OPMODE type) +{ + uint32_t i, session_id = WLAN_UMAC_VDEV_ID_MAX; + struct csr_roam_session *session_ptr; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (!CSR_IS_SESSION_VALID(mac_ctx, i)) + continue; + + session_ptr = CSR_GET_SESSION(mac_ctx, i); + if (type == session_ptr->bssParams.bssPersona) { + session_id = i; + break; + } + } + return session_id; +} + +/** + * csr_is_conn_allow_2g_band() - This function will check if station's conn + * is allowed in 2.4Ghz band. + * @mac_ctx: pointer to mac context. + * @chnl: station's channel. + * + * This function will check if station's connection is allowed in 5Ghz band + * after comparing it with SAP's operating channel. If SAP's operating + * channel and Station's channel is different than this function will return + * false else true. + * + * Return: true or false. + **/ +static bool csr_is_conn_allow_2g_band(struct mac_context *mac_ctx, + uint32_t ch_freq) +{ + uint32_t sap_session_id; + struct csr_roam_session *sap_session; + + if (0 == ch_freq) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("channel is zero, connection not allowed")); + + return false; + } + + sap_session_id = csr_find_session_by_type(mac_ctx, QDF_SAP_MODE); + if (WLAN_UMAC_VDEV_ID_MAX != sap_session_id) { + sap_session = CSR_GET_SESSION(mac_ctx, sap_session_id); + if (0 != sap_session->bssParams.operation_chan_freq && + sap_session->bssParams.operation_chan_freq != ch_freq) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Can't allow STA to connect, chnls not same"); + return false; + } + } + return true; +} + +/** + * csr_is_conn_allow_5g_band() - This function will check if station's conn + * is allowed in 5Ghz band. + * @mac_ctx: pointer to mac context. + * @chnl: station's channel. + * + * This function will check if station's connection is allowed in 5Ghz band + * after comparing it with P2PGO's operating channel. If P2PGO's operating + * channel and Station's channel is different than this function will return + * false else true. + * + * Return: true or false. + **/ +static bool csr_is_conn_allow_5g_band(struct mac_context *mac_ctx, + uint32_t ch_freq) +{ + uint32_t p2pgo_session_id; + struct csr_roam_session *p2pgo_session; + + if (0 == ch_freq) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("channel is zero, connection not allowed")); + return false; + } + + p2pgo_session_id = csr_find_session_by_type(mac_ctx, QDF_P2P_GO_MODE); + if (WLAN_UMAC_VDEV_ID_MAX != p2pgo_session_id) { + p2pgo_session = CSR_GET_SESSION(mac_ctx, p2pgo_session_id); + if (0 != p2pgo_session->bssParams.operation_chan_freq && + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED != + p2pgo_session->connectState && + p2pgo_session->bssParams.operation_chan_freq != ch_freq) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Can't allow STA to connect, chnls not same"); + return false; + } + } + return true; +} + +/** + * csr_process_set_hw_mode() - Set HW mode command to PE + * @mac: Globacl MAC pointer + * @command: Command received from SME + * + * Posts the set HW mode command to PE. This message passing + * through PE is required for PE's internal management + * + * Return: None + */ +void csr_process_set_hw_mode(struct mac_context *mac, tSmeCmd *command) +{ + uint32_t len; + struct s_sir_set_hw_mode *cmd = NULL; + QDF_STATUS status; + struct scheduler_msg msg = {0}; + struct sir_set_hw_mode_resp *param; + enum policy_mgr_hw_mode_change hw_mode; + enum policy_mgr_conc_next_action action; + enum set_hw_mode_status hw_mode_change_status = + SET_HW_MODE_STATUS_ECANCELED; + + /* Setting HW mode is for the entire system. + * So, no need to check session + */ + + if (!command) { + sme_err("Set HW mode param is NULL"); + goto fail; + } + + len = sizeof(*cmd); + cmd = qdf_mem_malloc(len); + if (!cmd) + /* Probably the fail response will also fail during malloc. + * Still proceeding to send response! + */ + goto fail; + + action = command->u.set_hw_mode_cmd.action; + /* For hidden SSID case, if there is any scan command pending + * it needs to be cleared before issuing set HW mode + */ + if (command->u.set_hw_mode_cmd.reason == + POLICY_MGR_UPDATE_REASON_HIDDEN_STA) { + sme_err("clear any pending scan command"); + status = csr_scan_abort_mac_scan(mac, + command->u.set_hw_mode_cmd.session_id, INVAL_SCAN_ID); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to clear scan cmd"); + goto fail; + } + } + + status = policy_mgr_validate_dbs_switch(mac->psoc, action); + + if (QDF_IS_STATUS_ERROR(status)) { + sme_debug("Hw mode change not sent to FW status = %d", status); + if (status == QDF_STATUS_E_ALREADY) + hw_mode_change_status = SET_HW_MODE_STATUS_ALREADY; + goto fail; + } + + hw_mode = policy_mgr_get_hw_mode_change_from_hw_mode_index( + mac->psoc, command->u.set_hw_mode_cmd.hw_mode_index); + + if (POLICY_MGR_HW_MODE_NOT_IN_PROGRESS == hw_mode) { + sme_err("hw_mode %d, failing", hw_mode); + goto fail; + } + + policy_mgr_set_hw_mode_change_in_progress(mac->psoc, hw_mode); + + if ((POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC == + command->u.set_hw_mode_cmd.reason) && + (true == mac->sme.get_connection_info_cb(NULL, NULL))) { + sme_err("Set HW mode refused: conn in progress"); + policy_mgr_restart_opportunistic_timer(mac->psoc, false); + goto reset_state; + } + + if ((POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC == + command->u.set_hw_mode_cmd.reason) && + (!command->u.set_hw_mode_cmd.hw_mode_index && + !policy_mgr_need_opportunistic_upgrade(mac->psoc, NULL))) { + sme_err("Set HW mode to SMM not needed anymore"); + goto reset_state; + } + + cmd->messageType = eWNI_SME_SET_HW_MODE_REQ; + cmd->length = len; + cmd->set_hw.hw_mode_index = command->u.set_hw_mode_cmd.hw_mode_index; + cmd->set_hw.reason = command->u.set_hw_mode_cmd.reason; + /* + * Below callback and context info are not needed for PE as of now. + * Storing the passed value in the same s_sir_set_hw_mode format. + */ + cmd->set_hw.set_hw_mode_cb = command->u.set_hw_mode_cmd.set_hw_mode_cb; + + sme_debug( + "Posting set hw mode req to PE session:%d reason:%d", + command->u.set_hw_mode_cmd.session_id, + command->u.set_hw_mode_cmd.reason); + + status = umac_send_mb_message_to_mac(cmd); + if (QDF_STATUS_SUCCESS != status) { + sme_err("Posting to PE failed"); + cmd = NULL; + goto reset_state; + } + return; + +reset_state: + policy_mgr_set_hw_mode_change_in_progress(mac->psoc, + POLICY_MGR_HW_MODE_NOT_IN_PROGRESS); +fail: + if (cmd) + qdf_mem_free(cmd); + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return; + + sme_debug("Sending set HW fail response to SME"); + param->status = hw_mode_change_status; + param->cfgd_hw_mode_index = 0; + param->num_vdev_mac_entries = 0; + msg.type = eWNI_SME_SET_HW_MODE_RESP; + msg.bodyptr = param; + msg.bodyval = 0; + sys_process_mmh_msg(mac, &msg); +} + +/** + * csr_process_set_dual_mac_config() - Set HW mode command to PE + * @mac: Global MAC pointer + * @command: Command received from SME + * + * Posts the set dual mac config command to PE. + * + * Return: None + */ +void csr_process_set_dual_mac_config(struct mac_context *mac, tSmeCmd *command) +{ + uint32_t len; + struct sir_set_dual_mac_cfg *cmd; + QDF_STATUS status; + struct scheduler_msg msg = {0}; + struct sir_dual_mac_config_resp *param; + + /* Setting MAC configuration is for the entire system. + * So, no need to check session + */ + + if (!command) { + sme_err("Set HW mode param is NULL"); + goto fail; + } + + len = sizeof(*cmd); + cmd = qdf_mem_malloc(len); + if (!cmd) + /* Probably the fail response will also fail during malloc. + * Still proceeding to send response! + */ + goto fail; + + cmd->message_type = eWNI_SME_SET_DUAL_MAC_CFG_REQ; + cmd->length = len; + cmd->set_dual_mac.scan_config = command->u.set_dual_mac_cmd.scan_config; + cmd->set_dual_mac.fw_mode_config = + command->u.set_dual_mac_cmd.fw_mode_config; + /* + * Below callback and context info are not needed for PE as of now. + * Storing the passed value in the same sir_set_dual_mac_cfg format. + */ + cmd->set_dual_mac.set_dual_mac_cb = + command->u.set_dual_mac_cmd.set_dual_mac_cb; + + sme_debug("Posting eWNI_SME_SET_DUAL_MAC_CFG_REQ to PE: %x %x", + cmd->set_dual_mac.scan_config, + cmd->set_dual_mac.fw_mode_config); + + status = umac_send_mb_message_to_mac(cmd); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Posting to PE failed"); + goto fail; + } + return; +fail: + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return; + + sme_err("Sending set dual mac fail response to SME"); + param->status = SET_HW_MODE_STATUS_ECANCELED; + msg.type = eWNI_SME_SET_DUAL_MAC_CFG_RESP; + msg.bodyptr = param; + msg.bodyval = 0; + sys_process_mmh_msg(mac, &msg); +} + +/** + * csr_process_set_antenna_mode() - Set antenna mode command to + * PE + * @mac: Global MAC pointer + * @command: Command received from SME + * + * Posts the set dual mac config command to PE. + * + * Return: None + */ +void csr_process_set_antenna_mode(struct mac_context *mac, tSmeCmd *command) +{ + uint32_t len; + struct sir_set_antenna_mode *cmd; + QDF_STATUS status; + struct scheduler_msg msg = {0}; + struct sir_antenna_mode_resp *param; + + /* Setting MAC configuration is for the entire system. + * So, no need to check session + */ + + if (!command) { + sme_err("Set antenna mode param is NULL"); + goto fail; + } + + len = sizeof(*cmd); + cmd = qdf_mem_malloc(len); + if (!cmd) + goto fail; + + cmd->message_type = eWNI_SME_SET_ANTENNA_MODE_REQ; + cmd->length = len; + cmd->set_antenna_mode = command->u.set_antenna_mode_cmd; + + sme_debug( + "Posting eWNI_SME_SET_ANTENNA_MODE_REQ to PE: %d %d", + cmd->set_antenna_mode.num_rx_chains, + cmd->set_antenna_mode.num_tx_chains); + + status = umac_send_mb_message_to_mac(cmd); + if (QDF_STATUS_SUCCESS != status) { + sme_err("Posting to PE failed"); + /* + * umac_send_mb_message_to_mac would've released the mem + * allocated by cmd. + */ + goto fail; + } + + return; +fail: + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return; + + sme_err("Sending set dual mac fail response to SME"); + param->status = SET_ANTENNA_MODE_STATUS_ECANCELED; + msg.type = eWNI_SME_SET_ANTENNA_MODE_RESP; + msg.bodyptr = param; + msg.bodyval = 0; + sys_process_mmh_msg(mac, &msg); +} + +/** + * csr_process_nss_update_req() - Update nss command to PE + * @mac: Globacl MAC pointer + * @command: Command received from SME + * + * Posts the nss update command to PE. This message passing + * through PE is required for PE's internal management + * + * Return: None + */ +void csr_process_nss_update_req(struct mac_context *mac, tSmeCmd *command) +{ + uint32_t len; + struct sir_nss_update_request *msg; + QDF_STATUS status; + struct scheduler_msg msg_return = {0}; + struct sir_bcn_update_rsp *param; + struct csr_roam_session *session; + + + if (!CSR_IS_SESSION_VALID(mac, command->vdev_id)) { + sme_err("Invalid session id %d", command->vdev_id); + goto fail; + } + session = CSR_GET_SESSION(mac, command->vdev_id); + + len = sizeof(*msg); + msg = qdf_mem_malloc(len); + if (!msg) + /* Probably the fail response is also fail during malloc. + * Still proceeding to send response! + */ + goto fail; + + msg->msgType = eWNI_SME_NSS_UPDATE_REQ; + msg->msgLen = sizeof(*msg); + + msg->new_nss = command->u.nss_update_cmd.new_nss; + msg->ch_width = command->u.nss_update_cmd.ch_width; + msg->vdev_id = command->u.nss_update_cmd.session_id; + + sme_debug("Posting eWNI_SME_NSS_UPDATE_REQ to PE"); + + status = umac_send_mb_message_to_mac(msg); + if (QDF_IS_STATUS_SUCCESS(status)) + return; + + sme_err("Posting to PE failed"); +fail: + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return; + + sme_err("Sending nss update fail response to SME"); + param->status = QDF_STATUS_E_FAILURE; + param->vdev_id = command->u.nss_update_cmd.session_id; + param->reason = REASON_NSS_UPDATE; + msg_return.type = eWNI_SME_NSS_UPDATE_RSP; + msg_return.bodyptr = param; + msg_return.bodyval = 0; + sys_process_mmh_msg(mac, &msg_return); +} +#ifdef FEATURE_WLAN_TDLS +/** + * csr_roam_fill_tdls_info() - Fill TDLS information + * @roam_info: Roaming information buffer + * @join_rsp: Join response which has TDLS info + * + * Return: None + */ +void csr_roam_fill_tdls_info(struct mac_context *mac_ctx, + struct csr_roam_info *roam_info, + struct join_rsp *join_rsp) +{ + roam_info->tdls_prohibited = join_rsp->tdls_prohibited; + roam_info->tdls_chan_swit_prohibited = + join_rsp->tdls_chan_swit_prohibited; + sme_debug( + "tdls:prohibit: %d, chan_swit_prohibit: %d", + roam_info->tdls_prohibited, + roam_info->tdls_chan_swit_prohibited); +} +#endif + +#if defined(WLAN_FEATURE_FILS_SK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +static void csr_copy_fils_join_rsp_roam_info(struct csr_roam_info *roam_info, + struct roam_offload_synch_ind *roam_synch_data) +{ + struct fils_join_rsp_params *roam_fils_info; + + roam_info->fils_join_rsp = qdf_mem_malloc(sizeof(*roam_fils_info)); + if (!roam_info->fils_join_rsp) + return; + + roam_fils_info = roam_info->fils_join_rsp; + cds_copy_hlp_info(&roam_synch_data->dst_mac, + &roam_synch_data->src_mac, + roam_synch_data->hlp_data_len, + roam_synch_data->hlp_data, + &roam_fils_info->dst_mac, + &roam_fils_info->src_mac, + &roam_fils_info->hlp_data_len, + roam_fils_info->hlp_data); +} + +/* + * csr_update_fils_erp_seq_num() - Update the FILS erp sequence number in + * roaming profile after roam complete + * @roam_info: roam_info pointer + * @erp_next_seq_num: next erp sequence number from firmware + * + * Return: NONE + */ +static +void csr_update_fils_erp_seq_num(struct csr_roam_profile *roam_profile, + uint16_t erp_next_seq_num) +{ + if (roam_profile->fils_con_info) + roam_profile->fils_con_info->sequence_number = erp_next_seq_num; +} +#else +static inline +void csr_copy_fils_join_rsp_roam_info(struct csr_roam_info *roam_info, + struct roam_offload_synch_ind *roam_synch_data) +{} + +static inline +void csr_update_fils_erp_seq_num(struct csr_roam_profile *roam_profile, + uint16_t erp_next_seq_num) +{} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS csr_fast_reassoc(mac_handle_t mac_handle, + struct csr_roam_profile *profile, + const tSirMacAddr bssid, uint32_t ch_freq, + uint8_t vdev_id, const tSirMacAddr connected_bssid) +{ + QDF_STATUS status; + struct wma_roam_invoke_cmd *fastreassoc; + struct scheduler_msg msg = {0}; + struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); + struct csr_roam_session *session; + struct wlan_objmgr_vdev *vdev; + struct mlme_roam_after_data_stall *vdev_roam_params; + bool roam_control_bitmap; + + session = CSR_GET_SESSION(mac_ctx, vdev_id); + if (!session) { + sme_err("session %d not found", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + if (!csr_is_conn_state_connected(mac_ctx, vdev_id)) { + sme_debug("Not in connected state, Roam Invoke not sent"); + return QDF_STATUS_E_FAILURE; + } + + roam_control_bitmap = mlme_get_operations_bitmap(mac_ctx->psoc, + vdev_id); + if (roam_control_bitmap || + !MLME_IS_ROAM_INITIALIZED(mac_ctx->psoc, vdev_id)) { + sme_debug("ROAM: RSO Disabled internaly: vdev[%d] bitmap[0x%x]", + vdev_id, roam_control_bitmap); + return QDF_STATUS_E_FAILURE; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + + if (!vdev) { + sme_err("vdev is NULL, aborting roam invoke"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev_roam_params = mlme_get_roam_invoke_params(vdev); + + if (!vdev_roam_params) { + sme_err("Invalid vdev roam params, aborting roam invoke"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return QDF_STATUS_E_NULL_VALUE; + } + + if (vdev_roam_params->roam_invoke_in_progress) { + sme_debug("Roaming already initiated by %d source", + vdev_roam_params->source); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return QDF_STATUS_E_FAILURE; + } + + fastreassoc = qdf_mem_malloc(sizeof(*fastreassoc)); + if (!fastreassoc) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return QDF_STATUS_E_NOMEM; + } + /* if both are same then set the flag */ + if (!qdf_mem_cmp(connected_bssid, bssid, ETH_ALEN)) + fastreassoc->is_same_bssid = true; + + fastreassoc->vdev_id = vdev_id; + fastreassoc->bssid[0] = bssid[0]; + fastreassoc->bssid[1] = bssid[1]; + fastreassoc->bssid[2] = bssid[2]; + fastreassoc->bssid[3] = bssid[3]; + fastreassoc->bssid[4] = bssid[4]; + fastreassoc->bssid[5] = bssid[5]; + + status = sme_get_beacon_frm(mac_handle, profile, bssid, + &fastreassoc->frame_buf, + &fastreassoc->frame_len, + &ch_freq); + + if (!ch_freq) { + sme_err("channel retrieval from BSS desc fails!"); + qdf_mem_free(fastreassoc->frame_buf); + fastreassoc->frame_buf = NULL; + fastreassoc->frame_len = 0; + qdf_mem_free(fastreassoc); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return QDF_STATUS_E_FAULT; + } + + fastreassoc->ch_freq = ch_freq; + if (QDF_STATUS_SUCCESS != status) { + sme_warn("sme_get_beacon_frm failed"); + qdf_mem_free(fastreassoc->frame_buf); + fastreassoc->frame_buf = NULL; + fastreassoc->frame_len = 0; + } + + if (csr_is_auth_type_ese(mac_ctx->roam.roamSession[vdev_id]. + connectedProfile.AuthType)) { + sme_debug("Beacon is not required for ESE"); + if (fastreassoc->frame_len) { + qdf_mem_free(fastreassoc->frame_buf); + fastreassoc->frame_buf = NULL; + fastreassoc->frame_len = 0; + } + } + + msg.type = eWNI_SME_ROAM_INVOKE; + msg.reserved = 0; + msg.bodyptr = fastreassoc; + status = scheduler_post_message(QDF_MODULE_ID_SME, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("Not able to post ROAM_INVOKE_CMD message to PE"); + qdf_mem_free(fastreassoc->frame_buf); + fastreassoc->frame_buf = NULL; + fastreassoc->frame_len = 0; + qdf_mem_free(fastreassoc); + } else { + vdev_roam_params->roam_invoke_in_progress = true; + vdev_roam_params->source = USERSPACE_INITIATED; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return status; +} +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * csr_is_sae_single_pmk_vsie_ap() - check if the Roamed AP supports sae + * roaming using single pmk connection + * with same pmk or not + * @bss_des: bss descriptor + * + * Return: True if same pmk IE is present + */ +static bool csr_is_sae_single_pmk_vsie_ap(struct bss_description *bss_des) +{ + uint16_t ie_len; + const uint8_t *vendor_ie; + + if (!bss_des) { + sme_debug("Invalid bss description"); + return false; + } + ie_len = csr_get_ielen_from_bss_description(bss_des); + + vendor_ie = + wlan_get_vendor_ie_ptr_from_oui(CSR_SINGLE_PMK_OUI, + CSR_SINGLE_PMK_OUI_SIZE, + (uint8_t *)bss_des->ieFields, + ie_len); + + if (!vendor_ie) + return false; + + sme_debug("AP supports sae single pmk feature"); + + return true; +} + +/** + * csr_check_and_set_sae_single_pmk_cap() - check if the Roamed AP support + * roaming using single pmk + * with same pmk or not + * @mac_ctx: mac context + * @session: Session + * @vdev_id: session id + * + * Return: True if same pmk IE is present + */ +static void +csr_check_and_set_sae_single_pmk_cap(struct mac_context *mac_ctx, + struct csr_roam_session *session, + uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct mlme_pmk_info *pmk_info; + tPmkidCacheInfo *pmkid_cache; + uint32_t keymgmt; + bool val, lookup_success; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + mlme_err("get vdev failed"); + return; + } + + keymgmt = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT); + + if (keymgmt & (1 << WLAN_CRYPTO_KEY_MGMT_SAE)) { + val = csr_is_sae_single_pmk_vsie_ap(session->pConnectBssDesc); + wlan_mlme_set_sae_single_pmk_bss_cap(mac_ctx->psoc, vdev_id, + val); + if (!val) + goto end; + + wlan_crypto_set_sae_single_pmk_bss_cap(vdev, + (struct qdf_mac_addr *)session->pConnectBssDesc->bssId, + true); + + pmkid_cache = qdf_mem_malloc(sizeof(*pmkid_cache)); + if (!pmkid_cache) + goto end; + + qdf_copy_macaddr(&pmkid_cache->BSSID, + &session->connectedProfile.bssid); + /* + * In SAE single pmk roaming case, there will + * be no PMK entry found for the AP in pmk cache. + * So if the lookup is successful, then we have done + * a FULL sae here. In that case, clear all other + * single pmk entries. + */ + lookup_success = csr_lookup_pmkid_using_bssid(mac_ctx, session, + pmkid_cache); + qdf_mem_free(pmkid_cache); + if (lookup_success) { + wlan_crypto_selective_clear_sae_single_pmk_entries(vdev, + &session->connectedProfile.bssid); + + pmk_info = qdf_mem_malloc(sizeof(*pmk_info)); + if (!pmk_info) + goto end; + + qdf_mem_copy(pmk_info->pmk, session->psk_pmk, + session->pmk_len); + pmk_info->pmk_len = session->pmk_len; + wlan_mlme_update_sae_single_pmk(vdev, pmk_info); + + qdf_mem_zero(pmk_info, sizeof(*pmk_info)); + qdf_mem_free(pmk_info); + } + } + +end: + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); +} +#else +static inline void +csr_check_and_set_sae_single_pmk_cap(struct mac_context *mac_ctx, + struct csr_roam_session *session, + uint8_t vdev_id) +{ +} +#endif + +#define IS_ROAM_REASON_STA_KICKOUT(reason) ((reason & 0xF) == \ + WMI_ROAM_TRIGGER_REASON_STA_KICKOUT) + +static QDF_STATUS csr_process_roam_sync_callback(struct mac_context *mac_ctx, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc, enum sir_roam_op_code reason) +{ + uint8_t session_id = roam_synch_data->roamed_vdev_id; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + tDot11fBeaconIEs *ies_local = NULL; + struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info; + struct csr_roam_info *roam_info; + tCsrRoamConnectedProfile *conn_profile = NULL; + sme_QosAssocInfo assoc_info; + struct bss_params *add_bss_params; + QDF_STATUS status = QDF_STATUS_SUCCESS; + tPmkidCacheInfo *pmkid_cache; + uint16_t len; +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + struct ht_profile *src_profile = NULL; + tCsrRoamHTProfile *dst_profile = NULL; +#endif + struct wlan_objmgr_vdev *vdev; + struct mlme_roam_after_data_stall *vdev_roam_params; + bool abort_host_scan_cap = false; + wlan_scan_id scan_id; + struct wlan_crypto_pmksa *pmksa; + uint8_t ssid_offset; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, session_id, + WLAN_LEGACY_SME_ID); + + if (!vdev) { + sme_err("vdev is NULL, aborting roam invoke"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev_roam_params = mlme_get_roam_invoke_params(vdev); + + if (!vdev_roam_params) { + sme_err("Invalid vdev roam params, aborting roam invoke"); + status = QDF_STATUS_E_NULL_VALUE; + goto end; + } + + if (!session) { + sme_err("LFR3: Session not found"); + status = QDF_STATUS_E_NULL_VALUE; + goto end; + } + + sme_debug("LFR3: reason: %d roaming in progress %d, source %d", reason, + vdev_roam_params->roam_invoke_in_progress, + vdev_roam_params->source); + + switch (reason) { + case SIR_ROAMING_DEREGISTER_STA: + /* + * The following is the first thing done in CSR + * after receiving RSI. Hence stopping the timer here. + */ + csr_roam_roaming_offload_timer_action(mac_ctx, + 0, session_id, ROAMING_OFFLOAD_TIMER_STOP); + if (session->discon_in_progress || + (MLME_IS_ROAM_STATE_STOPPED(mac_ctx->psoc, session_id) && + !vdev_roam_params->roam_invoke_in_progress) || + !CSR_IS_ROAM_JOINED(mac_ctx, session_id)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("LFR3: Session not in connected state or disconnect is in progress %d"), + session->discon_in_progress); + status = QDF_STATUS_E_FAILURE; + goto end; + } + csr_roam_call_callback(mac_ctx, session_id, NULL, 0, + eCSR_ROAM_FT_START, eCSR_ROAM_RESULT_SUCCESS); + goto end; + case SIR_ROAMING_START: + csr_roam_roaming_offload_timer_action(mac_ctx, + CSR_ROAMING_OFFLOAD_TIMEOUT_PERIOD, session_id, + ROAMING_OFFLOAD_TIMER_START); + csr_roam_call_callback(mac_ctx, session_id, NULL, 0, + eCSR_ROAM_START, eCSR_ROAM_RESULT_SUCCESS); + + /* + * For emergency deauth roaming, firmware sends ROAM start + * instead of ROAM scan start notification as data path queues + * will be stopped only during roam start notification. + * This is because, for deauth/disassoc triggered roam, the + * AP has sent deauth, and packets shouldn't be sent to AP + * after that. Since firmware is sending roam start directly + * host sends scan abort during roam scan, but in other + * triggers, the host receives roam start after candidate + * selection and roam scan is complete. So when host sends + * roam abort for emergency deauth roam trigger, the firmware + * roam scan is also aborted. This results in roaming failure. + * So send scan_id as CANCEL_HOST_SCAN_ID to scan module to + * abort only host triggered scans. + */ + abort_host_scan_cap = + wlan_mlme_get_host_scan_abort_support(mac_ctx->psoc); + if (abort_host_scan_cap) + scan_id = CANCEL_HOST_SCAN_ID; + else + scan_id = INVAL_SCAN_ID; + + wlan_abort_scan(mac_ctx->pdev, INVAL_PDEV_ID, + session_id, scan_id, false); + goto end; + case SIR_ROAMING_ABORT: + csr_roam_roaming_offload_timer_action(mac_ctx, + 0, session_id, ROAMING_OFFLOAD_TIMER_STOP); + csr_roam_call_callback(mac_ctx, session_id, NULL, 0, + eCSR_ROAM_ABORT, eCSR_ROAM_RESULT_SUCCESS); + vdev_roam_params->roam_invoke_in_progress = false; + goto end; + case SIR_ROAM_SYNCH_NAPI_OFF: + csr_roam_call_callback(mac_ctx, session_id, NULL, 0, + eCSR_ROAM_NAPI_OFF, eCSR_ROAM_RESULT_SUCCESS); + goto end; + case SIR_ROAMING_INVOKE_FAIL: + sme_debug("Roaming triggered failed source %d nud behaviour %d", + vdev_roam_params->source, mac_ctx->nud_fail_behaviour); + + /* Userspace roam req fail, disconnect with AP */ + if (vdev_roam_params->source == USERSPACE_INITIATED || + mac_ctx->nud_fail_behaviour == DISCONNECT_AFTER_ROAM_FAIL) + csr_roam_disconnect(mac_ctx, session_id, + eCSR_DISCONNECT_REASON_DEAUTH, + eSIR_MAC_USER_TRIGGERED_ROAM_FAILURE); + + vdev_roam_params->roam_invoke_in_progress = false; + goto end; + case SIR_ROAM_SYNCH_PROPAGATION: + break; + case SIR_ROAM_SYNCH_COMPLETE: + /* Handle one race condition that if candidate is already + *selected & FW has gone ahead with roaming or about to go + * ahead when set_band comes, it will be complicated for FW + * to stop the current roaming. Instead, host will check the + * roam sync to make sure the new AP is on 2G, or disconnect + * the AP. + */ + if (wlan_reg_is_disable_for_freq(mac_ctx->pdev, + roam_synch_data->chan_freq)) { + csr_roam_disconnect(mac_ctx, session_id, + eCSR_DISCONNECT_REASON_DEAUTH, + eSIR_MAC_OPER_CHANNEL_BAND_CHANGE); + sme_debug("Roaming Failed for disabled channel or band"); + vdev_roam_params->roam_invoke_in_progress = false; + goto end; + } + /* + * Following operations need to be done once roam sync + * completion is sent to FW, hence called here: + * 1) Firmware has already updated DBS policy. Update connection + * table in the host driver. + * 2) Force SCC switch if needed + * 3) Set connection in progress = false + */ + /* first update connection info from wma interface */ + policy_mgr_update_connection_info(mac_ctx->psoc, session_id); + /* then update remaining parameters from roam sync ctx */ + sme_debug("Update DBS hw mode"); + policy_mgr_hw_mode_transition_cb( + roam_synch_data->hw_mode_trans_ind.old_hw_mode_index, + roam_synch_data->hw_mode_trans_ind.new_hw_mode_index, + roam_synch_data->hw_mode_trans_ind.num_vdev_mac_entries, + roam_synch_data->hw_mode_trans_ind.vdev_mac_map, + mac_ctx->psoc); + mac_ctx->sme.set_connection_info_cb(false); + session->roam_synch_in_progress = false; + + csr_check_and_set_sae_single_pmk_cap(mac_ctx, session, + session_id); + + if (WLAN_REG_IS_5GHZ_CH_FREQ(bss_desc->chan_freq)) { + session->disable_hi_rssi = true; + sme_debug("Disabling HI_RSSI, AP freq=%d, rssi=%d", + bss_desc->chan_freq, bss_desc->rssi); + } else { + session->disable_hi_rssi = false; + } + + policy_mgr_check_n_start_opportunistic_timer(mac_ctx->psoc); + csr_roam_call_callback(mac_ctx, session_id, NULL, 0, + eCSR_ROAM_SYNCH_COMPLETE, + eCSR_ROAM_RESULT_SUCCESS); + policy_mgr_check_concurrent_intf_and_restart_sap(mac_ctx->psoc); + if (roam_synch_data->authStatus == + CSR_ROAM_AUTH_STATUS_AUTHENTICATED) + csr_roam_update_cfg(mac_ctx, session_id, + REASON_CONNECT); + vdev_roam_params->roam_invoke_in_progress = false; + goto end; + case SIR_ROAMING_DEAUTH: + csr_roam_roaming_offload_timer_action( + mac_ctx, 0, session_id, ROAMING_OFFLOAD_TIMER_STOP); + goto end; + default: + status = QDF_STATUS_E_FAILURE; + goto end; + } + session->roam_synch_in_progress = true; + session->roam_synch_data = roam_synch_data; + status = csr_get_parsed_bss_description_ies( + mac_ctx, bss_desc, &ies_local); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("LFR3: fail to parse IEs"); + session->roam_synch_in_progress = false; + goto end; + } + + conn_profile = &session->connectedProfile; + csr_roam_stop_network(mac_ctx, session_id, session->pCurRoamProfile, + bss_desc, ies_local); + ps_global_info->remain_in_power_active_till_dhcp = false; + session->connectState = eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED; + + /* Update the BLM that the previous profile has disconnected */ + wlan_blm_update_bssid_connect_params(mac_ctx->pdev, + session->connectedProfile.bssid, + BLM_AP_DISCONNECTED); + + if (IS_ROAM_REASON_STA_KICKOUT(roam_synch_data->roamReason)) { + struct reject_ap_info ap_info; + + ap_info.bssid = session->connectedProfile.bssid; + ap_info.reject_ap_type = DRIVER_AVOID_TYPE; + ap_info.reject_reason = REASON_STA_KICKOUT; + ap_info.source = ADDED_BY_DRIVER; + wlan_blm_add_bssid_to_reject_list(mac_ctx->pdev, &ap_info); + } + + /* Remove old BSSID mlme info from scan cache */ + csr_update_scan_entry_associnfo(mac_ctx, session, + SCAN_ENTRY_CON_STATE_NONE); + roam_info = qdf_mem_malloc(sizeof(struct csr_roam_info)); + if (!roam_info) { + session->roam_synch_in_progress = false; + qdf_mem_free(ies_local); + status = QDF_STATUS_E_NOMEM; + goto end; + } + csr_rso_save_ap_to_scan_cache(mac_ctx, roam_synch_data, bss_desc); + roam_info->sessionId = session_id; + + qdf_mem_copy(&roam_info->bssid.bytes, &bss_desc->bssId, + sizeof(struct qdf_mac_addr)); + csr_roam_save_connected_information(mac_ctx, session_id, + session->pCurRoamProfile, + bss_desc, + ies_local); + /* Add new mlme info to new BSSID after upting connectedProfile */ + csr_update_scan_entry_associnfo(mac_ctx, session, + SCAN_ENTRY_CON_STATE_ASSOC); + csr_roam_save_security_rsp_ie(mac_ctx, session_id, + session->pCurRoamProfile->negotiatedAuthType, + bss_desc, ies_local); + +#ifdef FEATURE_WLAN_ESE + roam_info->isESEAssoc = conn_profile->isESEAssoc; +#endif + + /* + * Encryption keys for new connection are obtained as follows: + * authStatus = CSR_ROAM_AUTH_STATUS_AUTHENTICATED + * Open - No keys required. + * Static WEP - Firmware copies keys from old AP to new AP. + * Fast roaming authentications e.g. PSK, FT, CCKM - firmware + * supplicant obtains them through 4-way handshake. + * + * authStatus = CSR_ROAM_AUTH_STATUS_CONNECTED + * All other authentications - Host supplicant performs EAPOL + * with AP after this point and sends new keys to the driver. + * Driver starts wait_for_key timer for that purpose. + */ + if (roam_synch_data->authStatus + == CSR_ROAM_AUTH_STATUS_AUTHENTICATED) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("LFR3:Don't start waitforkey timer")); + csr_roam_substate_change(mac_ctx, + eCSR_ROAM_SUBSTATE_NONE, session_id); + /* + * If authStatus is AUTHENTICATED, then we have done successful + * 4 way handshake in FW using the cached PMKID. + * However, the session->psk_pmk has the PMK of the older AP + * as set_key is not received from supplicant. + * When any RSO command is sent for the current AP, the older + * AP's PMK is sent to the FW which leads to incorrect PMK and + * leads to 4 way handshake failure when roaming happens to + * this AP again. + * Check if a PMK cache exists for the roamed AP and update + * it into the session pmk. + */ + pmkid_cache = qdf_mem_malloc(sizeof(*pmkid_cache)); + if (!pmkid_cache) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + + qdf_copy_macaddr(&pmkid_cache->BSSID, + &session->connectedProfile.bssid); + sme_debug("Trying to find PMKID for " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pmkid_cache->BSSID.bytes)); + if (csr_lookup_pmkid_using_bssid(mac_ctx, session, + pmkid_cache)) { + session->pmk_len = pmkid_cache->pmk_len; + qdf_mem_zero(session->psk_pmk, + sizeof(session->psk_pmk)); + qdf_mem_copy(session->psk_pmk, pmkid_cache->pmk, + session->pmk_len); + sme_debug("pmkid found for " QDF_MAC_ADDR_FMT " len %d", + QDF_MAC_ADDR_REF(pmkid_cache->BSSID.bytes), + (uint32_t)session->pmk_len); + } else { + sme_debug("PMKID Not found in cache for " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pmkid_cache->BSSID.bytes)); + if (roam_synch_data->pmk_len) { + pmksa = qdf_mem_malloc(sizeof(*pmksa)); + if (!pmksa) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + + session->pmk_len = roam_synch_data->pmk_len; + qdf_mem_zero(session->psk_pmk, + sizeof(session->psk_pmk)); + qdf_mem_copy(session->psk_pmk, + roam_synch_data->pmk, + session->pmk_len); + + qdf_copy_macaddr(&pmksa->bssid, + &session-> + connectedProfile.bssid); + qdf_mem_copy(pmksa->pmkid, + roam_synch_data->pmkid, PMKID_LEN); + qdf_mem_copy(pmksa->pmk, roam_synch_data->pmk, + roam_synch_data->pmk_len); + pmksa->pmk_len = roam_synch_data->pmk_len; + + if (wlan_crypto_set_del_pmksa(vdev, pmksa, true) + != QDF_STATUS_SUCCESS) { + qdf_mem_zero(pmksa, sizeof(*pmksa)); + qdf_mem_free(pmksa); + } + } + } + qdf_mem_zero(pmkid_cache, sizeof(pmkid_cache)); + qdf_mem_free(pmkid_cache); + } else { + roam_info->fAuthRequired = true; + csr_roam_substate_change(mac_ctx, + eCSR_ROAM_SUBSTATE_WAIT_FOR_KEY, + session_id); + + ps_global_info->remain_in_power_active_till_dhcp = true; + mac_ctx->roam.WaitForKeyTimerInfo.vdev_id = session_id; + if (!QDF_IS_STATUS_SUCCESS(csr_roam_start_wait_for_key_timer( + mac_ctx, CSR_WAIT_FOR_KEY_TIMEOUT_PERIOD)) + ) { + sme_err("Failed wait for key timer start"); + csr_roam_substate_change(mac_ctx, + eCSR_ROAM_SUBSTATE_NONE, + session_id); + } + csr_neighbor_roam_state_transition(mac_ctx, + eCSR_NEIGHBOR_ROAM_STATE_INIT, session_id); + } + + if (roam_synch_data->is_ft_im_roam) { + ssid_offset = SIR_MAC_ASSOC_REQ_SSID_OFFSET; + } else { + ssid_offset = SIR_MAC_REASSOC_REQ_SSID_OFFSET; + } + + roam_info->nBeaconLength = 0; + roam_info->nAssocReqLength = roam_synch_data->reassoc_req_length - + SIR_MAC_HDR_LEN_3A - ssid_offset; + roam_info->nAssocRspLength = roam_synch_data->reassocRespLength - + SIR_MAC_HDR_LEN_3A; + roam_info->pbFrames = qdf_mem_malloc(roam_info->nBeaconLength + + roam_info->nAssocReqLength + roam_info->nAssocRspLength); + if (!roam_info->pbFrames) { + session->roam_synch_in_progress = false; + if (roam_info) + qdf_mem_free(roam_info); + qdf_mem_free(ies_local); + status = QDF_STATUS_E_NOMEM; + goto end; + } + qdf_mem_copy(roam_info->pbFrames, + (uint8_t *)roam_synch_data + + roam_synch_data->reassoc_req_offset + + SIR_MAC_HDR_LEN_3A + ssid_offset, + roam_info->nAssocReqLength); + + qdf_mem_copy(roam_info->pbFrames + roam_info->nAssocReqLength, + (uint8_t *)roam_synch_data + + roam_synch_data->reassocRespOffset + + SIR_MAC_HDR_LEN_3A, + roam_info->nAssocRspLength); + + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("LFR3:Clear Connected info")); + csr_roam_free_connected_info(mac_ctx, + &session->connectedInfo); + len = roam_synch_data->join_rsp->parsedRicRspLen; + +#ifdef FEATURE_WLAN_ESE + len += roam_synch_data->join_rsp->tspecIeLen; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("LFR3: tspecLen %d"), + roam_synch_data->join_rsp->tspecIeLen); +#endif + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("LFR3: RIC length - %d"), + roam_synch_data->join_rsp->parsedRicRspLen); + if (len) { + session->connectedInfo.pbFrames = + qdf_mem_malloc(len); + if (session->connectedInfo.pbFrames) { + qdf_mem_copy(session->connectedInfo.pbFrames, + roam_synch_data->join_rsp->frames, len); + session->connectedInfo.nRICRspLength = + roam_synch_data->join_rsp->parsedRicRspLen; + +#ifdef FEATURE_WLAN_ESE + session->connectedInfo.nTspecIeLength = + roam_synch_data->join_rsp->tspecIeLen; +#endif + } + } + conn_profile->vht_channel_width = + roam_synch_data->join_rsp->vht_channel_width; + add_bss_params = (struct bss_params *)roam_synch_data->add_bss_params; + roam_info->staId = session->connectedInfo.staId; + roam_info->timingMeasCap = + roam_synch_data->join_rsp->timingMeasCap; + roam_info->chan_info.nss = roam_synch_data->join_rsp->nss; + roam_info->chan_info.rate_flags = + roam_synch_data->join_rsp->max_rate_flags; + roam_info->chan_info.ch_width = + roam_synch_data->join_rsp->vht_channel_width; + csr_roam_fill_tdls_info(mac_ctx, roam_info, roam_synch_data->join_rsp); +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + src_profile = &roam_synch_data->join_rsp->ht_profile; + dst_profile = &conn_profile->ht_profile; + if (mac_ctx->roam.configParam.cc_switch_mode + != QDF_MCC_TO_SCC_SWITCH_DISABLE) + csr_roam_copy_ht_profile(dst_profile, + src_profile); +#endif + assoc_info.bss_desc = bss_desc; + roam_info->status_code = eSIR_SME_SUCCESS; + roam_info->reasonCode = eSIR_SME_SUCCESS; + assoc_info.pProfile = session->pCurRoamProfile; + mac_ctx->roam.roamSession[session_id].connectState = + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED; + sme_qos_csr_event_ind(mac_ctx, session_id, + SME_QOS_CSR_HANDOFF_ASSOC_REQ, NULL); + sme_qos_csr_event_ind(mac_ctx, session_id, + SME_QOS_CSR_REASSOC_REQ, NULL); + sme_qos_csr_event_ind(mac_ctx, session_id, + SME_QOS_CSR_HANDOFF_COMPLETE, NULL); + mac_ctx->roam.roamSession[session_id].connectState = + eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED; + sme_qos_csr_event_ind(mac_ctx, session_id, + SME_QOS_CSR_REASSOC_COMPLETE, &assoc_info); + roam_info->bss_desc = bss_desc; + conn_profile->acm_mask = sme_qos_get_acm_mask(mac_ctx, + bss_desc, NULL); + if (conn_profile->modifyProfileFields.uapsd_mask) { + sme_debug( + " uapsd_mask (0x%X) set, request UAPSD now", + conn_profile->modifyProfileFields.uapsd_mask); + sme_ps_start_uapsd(MAC_HANDLE(mac_ctx), session_id); + } + conn_profile->dot11Mode = session->bssParams.uCfgDot11Mode; + roam_info->u.pConnectedProfile = conn_profile; + + sme_debug( + "vht ch width %d staId %d nss %d rate_flag %d dot11Mode %d", + conn_profile->vht_channel_width, + roam_info->staId, + roam_info->chan_info.nss, + roam_info->chan_info.rate_flags, + conn_profile->dot11Mode); + + if (!IS_FEATURE_SUPPORTED_BY_FW + (SLM_SESSIONIZATION) && + (csr_is_concurrent_session_running(mac_ctx))) { + mac_ctx->roam.configParam.doBMPSWorkaround = 1; + } + roam_info->roamSynchInProgress = true; + roam_info->synchAuthStatus = roam_synch_data->authStatus; + roam_info->kck_len = roam_synch_data->kck_len; + roam_info->kek_len = roam_synch_data->kek_len; + roam_info->pmk_len = roam_synch_data->pmk_len; + qdf_mem_copy(roam_info->kck, roam_synch_data->kck, roam_info->kck_len); + qdf_mem_copy(roam_info->kek, roam_synch_data->kek, roam_info->kek_len); + + if (roam_synch_data->pmk_len) + qdf_mem_copy(roam_info->pmk, roam_synch_data->pmk, + roam_synch_data->pmk_len); + + qdf_mem_copy(roam_info->pmkid, roam_synch_data->pmkid, PMKID_LEN); + roam_info->update_erp_next_seq_num = + roam_synch_data->update_erp_next_seq_num; + roam_info->next_erp_seq_num = roam_synch_data->next_erp_seq_num; + csr_update_fils_erp_seq_num(session->pCurRoamProfile, + roam_info->next_erp_seq_num); + sme_debug("Update ERP Seq Num : %d, Next ERP Seq Num : %d", + roam_info->update_erp_next_seq_num, + roam_info->next_erp_seq_num); + qdf_mem_copy(roam_info->replay_ctr, roam_synch_data->replay_ctr, + SIR_REPLAY_CTR_LEN); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("LFR3: Copy KCK, KEK(len %d) and Replay Ctr"), + roam_info->kek_len); + /* bit-4 and bit-5 indicate the subnet status */ + roam_info->subnet_change_status = + CSR_GET_SUBNET_STATUS(roam_synch_data->roamReason); + + /* fetch 4 LSB to get roam reason */ + roam_info->roam_reason = roam_synch_data->roamReason & + ROAM_REASON_MASK; + sme_debug("Update roam reason : %d", roam_info->roam_reason); + csr_copy_fils_join_rsp_roam_info(roam_info, roam_synch_data); + + csr_roam_call_callback(mac_ctx, session_id, roam_info, 0, + eCSR_ROAM_ASSOCIATION_COMPLETION, eCSR_ROAM_RESULT_ASSOCIATED); + csr_reset_pmkid_candidate_list(mac_ctx, session_id); + if (!CSR_IS_WAIT_FOR_KEY(mac_ctx, session_id)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL + ("NO CSR_IS_WAIT_FOR_KEY -> csr_roam_link_up")); + csr_roam_link_up(mac_ctx, conn_profile->bssid); + } + + session->fRoaming = false; + session->roam_synch_in_progress = false; + sme_free_join_rsp_fils_params(roam_info); + qdf_mem_free(roam_info->pbFrames); + qdf_mem_free(roam_info); + qdf_mem_free(ies_local); + +end: + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return status; +} + +/** + * csr_roam_synch_callback() - SME level callback for roam synch propagation + * @mac_ctx: MAC Context + * @roam_synch_data: Roam synch data buffer pointer + * @bss_desc: BSS descriptor pointer + * @reason: Reason for calling the callback + * + * This callback is registered with WMA and used after roaming happens in + * firmware and the call to this routine completes the roam synch + * propagation at both CSR and HDD levels. The HDD level propagation + * is achieved through the already defined callback for assoc completion + * handler. + * + * Return: Success or Failure. + */ +QDF_STATUS +csr_roam_synch_callback(struct mac_context *mac_ctx, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc, + enum sir_roam_op_code reason) +{ + QDF_STATUS status; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + + status = csr_process_roam_sync_callback(mac_ctx, roam_synch_data, + bss_desc, reason); + + sme_release_global_lock(&mac_ctx->sme); + + return status; +} + +#ifdef WLAN_FEATURE_SAE +/** + * csr_process_roam_auth_sae_callback() - API to trigger the + * WPA3 pre-auth event for candidate AP received from firmware. + * @mac_ctx: Global mac context pointer + * @vdev_id: vdev id + * @roam_bssid: Candidate BSSID to roam + * + * This function calls the hdd_sme_roam_callback with reason + * eCSR_ROAM_SAE_COMPUTE to trigger SAE auth to supplicant. + */ +static QDF_STATUS +csr_process_roam_auth_sae_callback(struct mac_context *mac_ctx, + uint8_t vdev_id, + struct qdf_mac_addr roam_bssid) +{ + struct csr_roam_info *roam_info; + struct sir_sae_info sae_info; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, vdev_id); + + if (!session) { + sme_err("WPA3 Preauth event with invalid session id:%d", + vdev_id); + return QDF_STATUS_E_FAILURE; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_FAILURE; + + sae_info.msg_len = sizeof(sae_info); + sae_info.vdev_id = vdev_id; + + sae_info.ssid.length = session->connectedProfile.SSID.length; + qdf_mem_copy(sae_info.ssid.ssId, session->connectedProfile.SSID.ssId, + sae_info.ssid.length); + + qdf_mem_copy(sae_info.peer_mac_addr.bytes, + roam_bssid.bytes, QDF_MAC_ADDR_SIZE); + + roam_info->sae_info = &sae_info; + + csr_roam_call_callback(mac_ctx, vdev_id, roam_info, 0, + eCSR_ROAM_SAE_COMPUTE, eCSR_ROAM_RESULT_NONE); + + qdf_mem_free(roam_info); + + return QDF_STATUS_SUCCESS; +} +#else +static inline QDF_STATUS +csr_process_roam_auth_sae_callback(struct mac_context *mac_ctx, + uint8_t vdev_id, + struct qdf_mac_addr roam_bssid) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif + +QDF_STATUS +csr_roam_auth_offload_callback(struct mac_context *mac_ctx, + uint8_t vdev_id, struct qdf_mac_addr bssid) +{ + QDF_STATUS status; + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (!QDF_IS_STATUS_SUCCESS(status)) + return status; + + status = csr_process_roam_auth_sae_callback(mac_ctx, vdev_id, bssid); + + sme_release_global_lock(&mac_ctx->sme); + + return status; + +} +#endif + +QDF_STATUS csr_update_owe_info(struct mac_context *mac, + struct assoc_ind *assoc_ind) +{ + uint32_t session_id = WLAN_UMAC_VDEV_ID_MAX; + QDF_STATUS status; + + status = csr_roam_get_session_id_from_bssid(mac, + (struct qdf_mac_addr *)assoc_ind->bssId, + &session_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_debug("Couldn't find session_id for given BSSID"); + return QDF_STATUS_E_FAILURE; + } + + /* Send Association completion message to PE */ + if (assoc_ind->owe_status) + status = QDF_STATUS_E_INVAL; + status = csr_send_assoc_cnf_msg(mac, assoc_ind, status, + assoc_ind->owe_status); + /* + * send a message to CSR itself just to avoid the EAPOL frames + * going OTA before association response + */ + if (assoc_ind->owe_status == 0) + status = csr_send_assoc_ind_to_upper_layer_cnf_msg(mac, + assoc_ind, + status, + session_id); + + return status; +} + +QDF_STATUS +csr_send_roam_offload_init_msg(struct mac_context *mac, uint32_t vdev_id, + bool enable) +{ + struct scheduler_msg message = {0}; + QDF_STATUS status; + struct roam_init_params *params; + + /* per contract must make a copy of the params when messaging */ + params = qdf_mem_malloc(sizeof(*params)); + if (!params) + return QDF_STATUS_E_NOMEM; + + params->vdev_id = vdev_id; + params->enable = enable; + + /* + * Post to lim and then to wma to keep the same path as that of RSO + * stop command, otherwise due to a race if deinit RSO goes first + * without RSO stop , firmware will assert. + */ + message.bodyptr = params; + message.type = eWNI_SME_ROAM_INIT_PARAM; + status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &message); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("ROAM: Failed to post ROAM_TRIGGERS msg"); + qdf_mem_free(params); + } + + return status; +} + +QDF_STATUS +csr_roam_update_cfg(struct mac_context *mac, uint8_t vdev_id, uint8_t reason) +{ + if (!MLME_IS_ROAM_STATE_RSO_STARTED(mac->psoc, vdev_id)) { + sme_debug("Update cfg received while ROAM RSO not started"); + return QDF_STATUS_E_INVAL; + } + + return csr_roam_offload_scan(mac, vdev_id, ROAM_SCAN_OFFLOAD_UPDATE_CFG, + reason); +} + +QDF_STATUS +csr_enable_roaming_on_connected_sta(struct mac_context *mac, uint8_t vdev_id) +{ + uint32_t op_ch_freq_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t vdev_id_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t sta_vdev_id = WLAN_INVALID_VDEV_ID; + struct csr_roam_session *session; + uint32_t count; + uint32_t idx; + + sta_vdev_id = csr_get_roam_enabled_sta_sessionid(mac); + if (sta_vdev_id != WLAN_UMAC_VDEV_ID_MAX) + return QDF_STATUS_E_FAILURE; + + count = policy_mgr_get_mode_specific_conn_info(mac->psoc, + op_ch_freq_list, + vdev_id_list, + PM_STA_MODE); + + if (!count) + return QDF_STATUS_E_FAILURE; + + /* + * Loop through all connected STA vdevs and roaming will be enabled + * on the STA that has a different vdev id from the one passed as + * input and has non zero roam trigger value. + */ + for (idx = 0; idx < count; idx++) { + session = CSR_GET_SESSION(mac, vdev_id_list[idx]); + if (vdev_id_list[idx] != vdev_id && + mlme_get_roam_trigger_bitmap(mac->psoc, + vdev_id_list[idx])) { + sta_vdev_id = vdev_id_list[idx]; + break; + } + } + + if (sta_vdev_id == WLAN_INVALID_VDEV_ID) + return QDF_STATUS_E_FAILURE; + + sme_debug("ROAM: Enabling roaming on vdev[%d]", sta_vdev_id); + + return csr_post_roam_state_change(mac, sta_vdev_id, ROAM_RSO_STARTED, + REASON_CTX_INIT); +} diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_scan.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_scan.c new file mode 100644 index 0000000000000000000000000000000000000000..ea344f1bac433f1aae9e61f772751680f9fcbb2c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_scan.c @@ -0,0 +1,2868 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: csr_api_scan.c + * + * Implementation for the Common Scan interfaces. + */ + +#include "ani_global.h" + +#include "csr_inside_api.h" +#include "sme_inside.h" + +#include "csr_support.h" + +#include "host_diag_core_log.h" +#include "host_diag_core_event.h" + +#include "cds_reg_service.h" +#include "wma_types.h" +#include "cds_utils.h" +#include "wma.h" + +#include "wlan_policy_mgr_api.h" +#include "wlan_hdd_main.h" +#include "pld_common.h" +#include "csr_internal.h" +#include +#include +#include +#include +#include +#include +#include "wlan_reg_services_api.h" +#include "sch_api.h" +#include "wlan_blm_api.h" +#include "qdf_crypto.h" +#include + +static void csr_set_cfg_valid_channel_list(struct mac_context *mac, + uint32_t *pchan_freq_list, + uint8_t NumChannels); + +static void csr_save_tx_power_to_cfg(struct mac_context *mac, + tDblLinkList *pList, + uint32_t cfgId); + +static void csr_set_cfg_country_code(struct mac_context *mac, + uint8_t *countryCode); + +static void csr_purge_channel_power(struct mac_context *mac, + tDblLinkList *pChannelList); + +static bool csr_roam_is_valid_channel(struct mac_context *mac, + uint32_t ch_freq); + +/* pResult is invalid calling this function. */ +void csr_free_scan_result_entry(struct mac_context *mac, + struct tag_csrscan_result *pResult) +{ + if (pResult->Result.pvIes) + qdf_mem_free(pResult->Result.pvIes); + + qdf_mem_free(pResult); +} + +static QDF_STATUS csr_ll_scan_purge_result(struct mac_context *mac, + tDblLinkList *pList) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tListElem *pEntry; + struct tag_csrscan_result *bss_desc; + + while ((pEntry = csr_ll_remove_head(pList, LL_ACCESS_NOLOCK)) != NULL) { + bss_desc = GET_BASE_ADDR(pEntry, struct tag_csrscan_result, + Link); + csr_free_scan_result_entry(mac, bss_desc); + } + + return status; +} + +QDF_STATUS csr_scan_open(struct mac_context *mac_ctx) +{ + csr_ll_open(&mac_ctx->scan.channelPowerInfoList24); + csr_ll_open(&mac_ctx->scan.channelPowerInfoList5G); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS csr_scan_close(struct mac_context *mac) +{ + csr_purge_channel_power(mac, &mac->scan.channelPowerInfoList24); + csr_purge_channel_power(mac, &mac->scan.channelPowerInfoList5G); + csr_ll_close(&mac->scan.channelPowerInfoList24); + csr_ll_close(&mac->scan.channelPowerInfoList5G); + ucfg_scan_psoc_set_disable(mac->psoc, REASON_SYSTEM_DOWN); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS csr_scan_handle_search_for_ssid(struct mac_context *mac_ctx, + uint32_t session_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + tScanResultHandle hBSSList = CSR_INVALID_SCANRESULT_HANDLE; + struct scan_filter *filter; + struct csr_roam_profile *profile; + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac_ctx, session_id); + if (!session) { + sme_err("session %d not found", session_id); + return QDF_STATUS_E_FAILURE; + } + profile = session->scan_info.profile; + sme_debug("session %d", session_id); + do { + /* If this scan is for HDD reassociate */ + if (mac_ctx->roam.neighborRoamInfo[session_id]. + uOsRequestedHandoff) { + /* notify LFR state m/c */ + status = csr_neighbor_roam_sssid_scan_done + (mac_ctx, session_id, QDF_STATUS_SUCCESS); + if (!QDF_IS_STATUS_SUCCESS(status)) + csr_neighbor_roam_start_lfr_scan(mac_ctx, + session_id); + status = QDF_STATUS_SUCCESS; + break; + } + /* + * If there is roam command waiting, ignore this roam because + * the newer roam command is the one to execute + */ + if (csr_is_roam_command_waiting_for_session(mac_ctx, session_id)) { + sme_warn("aborts because roam command waiting"); + break; + } + if (!profile) + break; + filter = qdf_mem_malloc(sizeof(*filter)); + if (!filter) { + status = QDF_STATUS_E_NOMEM; + break; + } + status = csr_roam_get_scan_filter_from_profile(mac_ctx, profile, + filter, false); + if (!QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(filter); + break; + } + status = csr_scan_get_result(mac_ctx, filter, &hBSSList); + qdf_mem_free(filter); + if (!QDF_IS_STATUS_SUCCESS(status)) + break; + if (mac_ctx->roam.roamSession[session_id].connectState == + eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTING) { + sme_err("upper layer issued disconnetion"); + status = QDF_STATUS_E_FAILURE; + break; + } + status = csr_roam_issue_connect(mac_ctx, session_id, profile, + hBSSList, eCsrHddIssued, + session->scan_info.roam_id, + true, true); + hBSSList = CSR_INVALID_SCANRESULT_HANDLE; + } while (0); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + if (CSR_INVALID_SCANRESULT_HANDLE != hBSSList) { + csr_scan_result_purge(mac_ctx, hBSSList); + } + /* We haven't done anything to this profile */ + csr_roam_call_callback(mac_ctx, session_id, NULL, + session->scan_info.roam_id, + eCSR_ROAM_ASSOCIATION_FAILURE, + eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE); + } + + return status; +} + +/** + * csr_handle_fils_scan_for_ssid_failure() - Checks and fills FILS seq number + * in roam_info structure to send to hdd + * + * @roam_profile: Pointer to current roam_profile structure + * @roam_info: Pointer to roam_info strucure to be filled + * + * Return: true for FILS connection else false + */ +#ifdef WLAN_FEATURE_FILS_SK +static bool +csr_handle_fils_scan_for_ssid_failure(struct csr_roam_profile *roam_profile, + struct csr_roam_info *roam_info) +{ + if (roam_profile && roam_profile->fils_con_info && + roam_profile->fils_con_info->is_fils_connection) { + sme_debug("send roam_info for FILS connection failure, seq %d", + roam_profile->fils_con_info->sequence_number); + roam_info->is_fils_connection = true; + roam_info->fils_seq_num = + roam_profile->fils_con_info->sequence_number; + return true; + } + + return false; +} +#else +static bool +csr_handle_fils_scan_for_ssid_failure(struct csr_roam_profile *roam_profile, + struct csr_roam_info *roam_info) +{ + return false; +} +#endif + +QDF_STATUS csr_scan_handle_search_for_ssid_failure(struct mac_context *mac_ctx, + uint32_t session_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_profile *profile; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + eCsrRoamResult roam_result; + struct csr_roam_info *roam_info = NULL; + struct tag_csrscan_result *scan_result; + + if (!session) { + sme_err("session %d not found", session_id); + return QDF_STATUS_E_FAILURE; + } + + /* If this scan is for HDD reassociate */ + if (mac_ctx->roam.neighborRoamInfo[session_id].uOsRequestedHandoff) { + /* notify LFR state m/c */ + status = csr_neighbor_roam_sssid_scan_done + (mac_ctx, session_id, QDF_STATUS_E_FAILURE); + if (!QDF_IS_STATUS_SUCCESS(status)) + csr_neighbor_roam_start_lfr_scan(mac_ctx, session_id); + return QDF_STATUS_SUCCESS; + } + + profile = session->scan_info.profile; + + /* + * Check whether it is for start ibss. No need to do anything if it + * is a JOIN request + */ + if (profile && CSR_IS_START_IBSS(profile)) { + status = csr_roam_issue_connect(mac_ctx, session_id, profile, NULL, + eCsrHddIssued, session->scan_info.roam_id, + true, true); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("failed to issue startIBSS, session_id %d status: 0x%08X roam id %d", + session_id, status, session->scan_info.roam_id); + csr_roam_call_callback(mac_ctx, session_id, NULL, + session->scan_info.roam_id, eCSR_ROAM_FAILED, + eCSR_ROAM_RESULT_FAILURE); + } + return status; + } + roam_result = eCSR_ROAM_RESULT_FAILURE; + if (profile && csr_is_bss_type_ibss(profile->BSSType)) { + roam_result = eCSR_ROAM_RESULT_IBSS_START_FAILED; + goto roam_completion; + } + + roam_info = qdf_mem_malloc(sizeof(struct csr_roam_info)); + if (!roam_info) + goto roam_completion; + + if (session->scan_info.roambssentry) { + scan_result = GET_BASE_ADDR(session->scan_info.roambssentry, + struct tag_csrscan_result, Link); + roam_info->bss_desc = &scan_result->Result.BssDescriptor; + } + roam_info->status_code = session->joinFailStatusCode.status_code; + roam_info->reasonCode = session->joinFailStatusCode.reasonCode; + + /* Only indicate assoc_completion if we indicate assoc_start. */ + if (session->bRefAssocStartCnt > 0) { + session->bRefAssocStartCnt--; + csr_roam_call_callback(mac_ctx, session_id, roam_info, + session->scan_info.roam_id, + eCSR_ROAM_ASSOCIATION_COMPLETION, + eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE); + } else { + if (!csr_handle_fils_scan_for_ssid_failure( + profile, roam_info)) { + qdf_mem_free(roam_info); + roam_info = NULL; + } + csr_roam_call_callback(mac_ctx, session_id, roam_info, + session->scan_info.roam_id, + eCSR_ROAM_ASSOCIATION_FAILURE, + eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE); + } + + if (roam_info) + qdf_mem_free(roam_info); +roam_completion: + csr_roam_completion(mac_ctx, session_id, NULL, NULL, roam_result, + false); + return status; +} + +QDF_STATUS csr_scan_result_purge(struct mac_context *mac, + tScanResultHandle hScanList) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + struct scan_result_list *pScanList = + (struct scan_result_list *) hScanList; + + if (pScanList) { + status = csr_ll_scan_purge_result(mac, &pScanList->List); + csr_ll_close(&pScanList->List); + qdf_mem_free(pScanList); + } + return status; +} + +/* Add the channel to the occupiedChannels array */ +static void csr_add_to_occupied_channels(struct mac_context *mac, + uint32_t ch_freq, + uint8_t sessionId, + struct csr_channel *occupied_ch, + bool is_init_list) +{ + QDF_STATUS status; + uint8_t num_occupied_ch = occupied_ch->numChannels; + uint32_t *occupied_ch_lst = occupied_ch->channel_freq_list; + + if (is_init_list) + mac->scan.roam_candidate_count[sessionId]++; + + if (csr_is_channel_present_in_list(occupied_ch_lst, + num_occupied_ch, ch_freq)) + return; + + status = csr_add_to_channel_list_front(occupied_ch_lst, + num_occupied_ch, ch_freq); + if (QDF_IS_STATUS_SUCCESS(status)) { + occupied_ch->numChannels++; + if (occupied_ch->numChannels > + CSR_BG_SCAN_OCCUPIED_CHANNEL_LIST_LEN) + occupied_ch->numChannels = + CSR_BG_SCAN_OCCUPIED_CHANNEL_LIST_LEN; + } +} + +/* Put the BSS into the scan result list */ +/* pIes can not be NULL */ +static void csr_scan_add_result(struct mac_context *mac_ctx, + struct tag_csrscan_result *pResult) +{ + qdf_nbuf_t buf; + uint8_t *data; + struct mgmt_rx_event_params rx_param = {0}; + struct wlan_frame_hdr *hdr; + struct wlan_bcn_frame *fixed_frame; + uint32_t buf_len, i; + struct bss_description *bss_desc; + enum mgmt_frame_type frm_type = MGMT_BEACON; + + if (!pResult) { + sme_err("pResult is null"); + return; + } + + bss_desc = &pResult->Result.BssDescriptor; + if (bss_desc->fProbeRsp) + frm_type = MGMT_PROBE_RESP; + + rx_param.pdev_id = 0; + rx_param.chan_freq = bss_desc->chan_freq; + rx_param.rssi = bss_desc->rssi; + rx_param.tsf_delta = bss_desc->tsf_delta; + + /* Set all per chain rssi as invalid */ + for (i = 0; i < WLAN_MGMT_TXRX_HOST_MAX_ANTENNA; i++) + rx_param.rssi_ctl[i] = WLAN_INVALID_PER_CHAIN_RSSI; + + buf_len = GET_IE_LEN_IN_BSS(bss_desc->length) + + + offsetof(struct wlan_bcn_frame, ie) + sizeof(*hdr); + + buf = qdf_nbuf_alloc(NULL, qdf_roundup(buf_len, 4), + 0, 4, false); + if (!buf) + return; + + qdf_nbuf_put_tail(buf, buf_len); + qdf_nbuf_set_protocol(buf, ETH_P_CONTROL); + + data = qdf_nbuf_data(buf); + hdr = (struct wlan_frame_hdr *) data; + qdf_mem_copy(hdr->i_addr3, bss_desc->bssId, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(hdr->i_addr2, bss_desc->bssId, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(hdr->i_seq, + &bss_desc->seq_ctrl, sizeof(uint16_t)); + + data += sizeof(*hdr); + fixed_frame = (struct wlan_bcn_frame *)data; + qdf_mem_copy(fixed_frame->timestamp, + bss_desc->timeStamp, 8); + fixed_frame->beacon_interval = bss_desc->beaconInterval; + fixed_frame->capability.value = bss_desc->capabilityInfo; + data += offsetof(struct wlan_bcn_frame, ie); + + qdf_mem_copy(data, bss_desc->ieFields, + GET_IE_LEN_IN_BSS(bss_desc->length)); + wlan_scan_process_bcn_probe_rx_sync(mac_ctx->psoc, buf, &rx_param, + frm_type); +} + +static bool +csr_scan_save_bss_description(struct mac_context *mac, + struct bss_description *pBSSDescription) +{ + struct tag_csrscan_result *pCsrBssDescription = NULL; + uint32_t cbBSSDesc; + uint32_t cbAllocated; + + /* figure out how big the BSS description is (the BSSDesc->length does + * NOT include the size of the length field itself). + */ + cbBSSDesc = pBSSDescription->length + sizeof(pBSSDescription->length); + + cbAllocated = sizeof(struct tag_csrscan_result) + cbBSSDesc; + + pCsrBssDescription = qdf_mem_malloc(cbAllocated); + if (!pCsrBssDescription) + return false; + + pCsrBssDescription->AgingCount = + (int32_t) mac->roam.configParam.agingCount; + sme_debug( + "Set Aging Count = %d for BSS " QDF_MAC_ADDR_FMT " ", + pCsrBssDescription->AgingCount, + QDF_MAC_ADDR_REF(pCsrBssDescription->Result.BssDescriptor. + bssId)); + qdf_mem_copy(&pCsrBssDescription->Result.BssDescriptor, + pBSSDescription, cbBSSDesc); + csr_scan_add_result(mac, pCsrBssDescription); + csr_free_scan_result_entry(mac, pCsrBssDescription); + + return true; +} + +/* Append a Bss Description... */ +bool csr_scan_append_bss_description(struct mac_context *mac, + struct bss_description *pSirBssDescription) +{ + return csr_scan_save_bss_description(mac, pSirBssDescription); +} + +static void csr_purge_channel_power(struct mac_context *mac, + tDblLinkList *pChannelList) +{ + struct csr_channel_powerinfo *pChannelSet; + tListElem *pEntry; + + /* + * Remove the channel sets from the learned list and put them + * in the free list + */ + csr_ll_lock(pChannelList); + while ((pEntry = csr_ll_remove_head(pChannelList, + LL_ACCESS_NOLOCK)) != NULL) { + pChannelSet = GET_BASE_ADDR(pEntry, + struct csr_channel_powerinfo, link); + if (pChannelSet) + qdf_mem_free(pChannelSet); + } + csr_ll_unlock(pChannelList); +} + +/* + * Save the channelList into the ultimate storage as the final stage of channel + * Input: pCountryInfo -- the country code (e.g. "USI"), channel list, and power + * limit are all stored inside this data structure + */ +QDF_STATUS csr_save_to_channel_power2_g_5_g(struct mac_context *mac, + uint32_t tableSize, + tSirMacChanInfo *channelTable) +{ + uint32_t i = tableSize / sizeof(tSirMacChanInfo); + tSirMacChanInfo *pChannelInfo; + struct csr_channel_powerinfo *pChannelSet; + bool f2GHzInfoFound = false; + bool f2GListPurged = false, f5GListPurged = false; + + pChannelInfo = channelTable; + /* atleast 3 bytes have to be remaining -- from "countryString" */ + while (i--) { + pChannelSet = qdf_mem_malloc(sizeof(struct csr_channel_powerinfo)); + if (!pChannelSet) { + pChannelInfo++; + continue; + } + pChannelSet->first_chan_freq = pChannelInfo->first_freq; + pChannelSet->numChannels = pChannelInfo->numChannels; + /* + * Now set the inter-channel offset based on the frequency band + * the channel set lies in + */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(pChannelSet->first_chan_freq) && + (pChannelSet->first_chan_freq + 5 * (pChannelSet->numChannels - 1) <= + WLAN_REG_MAX_24GHZ_CHAN_FREQ)) { + pChannelSet->interChannelOffset = 5; + f2GHzInfoFound = true; + } else if (WLAN_REG_IS_5GHZ_CH_FREQ(pChannelSet->first_chan_freq) && + (pChannelSet->first_chan_freq + 20 * (pChannelSet->numChannels - 1) <= + WLAN_REG_MAX_5GHZ_CHAN_FREQ)) { + pChannelSet->interChannelOffset = 20; + f2GHzInfoFound = false; + } else { + sme_warn("Invalid Channel freq %d Present in Country IE", + pChannelSet->first_chan_freq); + qdf_mem_free(pChannelSet); + return QDF_STATUS_E_FAILURE; + } + pChannelSet->txPower = QDF_MIN(pChannelInfo->maxTxPower, + mac->mlme_cfg->power.max_tx_power); + if (f2GHzInfoFound) { + if (!f2GListPurged) { + /* purge previous results if found new */ + csr_purge_channel_power(mac, + &mac->scan. + channelPowerInfoList24); + f2GListPurged = true; + } + if (CSR_IS_OPERATING_BG_BAND(mac)) { + /* add to the list of 2.4 GHz channel sets */ + csr_ll_insert_tail(&mac->scan. + channelPowerInfoList24, + &pChannelSet->link, + LL_ACCESS_LOCK); + } else { + sme_debug( + "Adding 11B/G ch in 11A. 1st ch freq %d", + pChannelSet->first_chan_freq); + qdf_mem_free(pChannelSet); + } + } else { + /* 5GHz info found */ + if (!f5GListPurged) { + /* purge previous results if found new */ + csr_purge_channel_power(mac, + &mac->scan. + channelPowerInfoList5G); + f5GListPurged = true; + } + if (CSR_IS_OPERATING_A_BAND(mac)) { + /* add to the list of 5GHz channel sets */ + csr_ll_insert_tail(&mac->scan. + channelPowerInfoList5G, + &pChannelSet->link, + LL_ACCESS_LOCK); + } else { + sme_debug( + "Adding 11A ch in B/G. 1st ch freq %d", + pChannelSet->first_chan_freq); + qdf_mem_free(pChannelSet); + } + } + pChannelInfo++; /* move to next entry */ + } + return QDF_STATUS_SUCCESS; +} + +void csr_apply_power2_current(struct mac_context *mac) +{ + sme_debug("Updating Cfg with power settings"); + csr_save_tx_power_to_cfg(mac, &mac->scan.channelPowerInfoList24, + BAND_2G); + csr_save_tx_power_to_cfg(mac, &mac->scan.channelPowerInfoList5G, + BAND_5G); +} + +void csr_apply_channel_power_info_to_fw(struct mac_context *mac_ctx, + struct csr_channel *ch_lst, + uint8_t *countryCode) +{ + int i; + uint8_t num_ch = 0; + uint8_t tempNumChannels = 0; + struct csr_channel tmp_ch_lst; + + if (ch_lst->numChannels) { + tempNumChannels = QDF_MIN(ch_lst->numChannels, + CFG_VALID_CHANNEL_LIST_LEN); + for (i = 0; i < tempNumChannels; i++) { + tmp_ch_lst.channel_freq_list[num_ch] = ch_lst->channel_freq_list[i]; + num_ch++; + } + tmp_ch_lst.numChannels = num_ch; + /* Store the channel+power info in the global place: Cfg */ + csr_apply_power2_current(mac_ctx); + csr_set_cfg_valid_channel_list(mac_ctx, tmp_ch_lst.channel_freq_list, + tmp_ch_lst.numChannels); + } else { + sme_err("11D channel list is empty"); + } + csr_set_cfg_country_code(mac_ctx, countryCode); +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +static void csr_diag_reset_country_information(struct mac_context *mac) +{ + + host_log_802_11d_pkt_type *p11dLog; + int Index; + + WLAN_HOST_DIAG_LOG_ALLOC(p11dLog, host_log_802_11d_pkt_type, + LOG_WLAN_80211D_C); + if (!p11dLog) + return; + + p11dLog->eventId = WLAN_80211D_EVENT_RESET; + qdf_mem_copy(p11dLog->countryCode, mac->scan.countryCodeCurrent, 3); + p11dLog->numChannel = mac->scan.base_channels.numChannels; + if (p11dLog->numChannel <= HOST_LOG_MAX_NUM_CHANNEL) { + for (Index = 0; + Index < mac->scan.base_channels.numChannels; + Index++) { + p11dLog->Channels[Index] = + wlan_reg_freq_to_chan(mac->pdev, mac->scan.base_channels.channel_freq_list[Index]); + p11dLog->TxPwr[Index] = QDF_MIN( + mac->scan.defaultPowerTable[Index].tx_power, + mac->mlme_cfg->power.max_tx_power); + } + } + + WLAN_HOST_DIAG_LOG_REPORT(p11dLog); +} +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + +/** + * csr_apply_channel_power_info_wrapper() - sends channel info to fw + * @mac: main MAC data structure + * + * This function sends the channel power info to firmware + * + * Return: none + */ +void csr_apply_channel_power_info_wrapper(struct mac_context *mac) +{ + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + csr_diag_reset_country_information(mac); +#endif /* FEATURE_WLAN_DIAG_SUPPORT_CSR */ + csr_prune_channel_list_for_mode(mac, &mac->scan.base_channels); + csr_save_channel_power_for_band(mac, false); + csr_save_channel_power_for_band(mac, true); + /* apply the channel list, power settings, and the country code. */ + csr_apply_channel_power_info_to_fw(mac, + &mac->scan.base_channels, mac->scan.countryCodeCurrent); + /* clear the 11d channel list */ + qdf_mem_zero(&mac->scan.channels11d, sizeof(mac->scan.channels11d)); +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +/* caller allocated memory for pNumChn and pChnPowerInfo */ +/* As input, *pNumChn has the size of the array of pChnPowerInfo */ +/* Upon return, *pNumChn has the number of channels assigned. */ +static void csr_get_channel_power_info(struct mac_context *mac, + tDblLinkList *list, + uint32_t *num_ch, + struct channel_power *chn_pwr_info) +{ + tListElem *entry; + uint32_t chn_idx = 0, idx; + struct csr_channel_powerinfo *ch_set; + + /* Get 2.4Ghz first */ + csr_ll_lock(list); + entry = csr_ll_peek_head(list, LL_ACCESS_NOLOCK); + while (entry && (chn_idx < *num_ch)) { + ch_set = GET_BASE_ADDR(entry, + struct csr_channel_powerinfo, link); + for (idx = 0; (idx < ch_set->numChannels) + && (chn_idx < *num_ch); idx++) { + chn_pwr_info[chn_idx].chan_num = + (uint8_t)wlan_reg_freq_to_chan( + mac->pdev, + ch_set->first_chan_freq + + idx * ch_set->interChannelOffset); + chn_pwr_info[chn_idx++].tx_power = ch_set->txPower; + } + entry = csr_ll_next(list, entry, LL_ACCESS_NOLOCK); + } + csr_ll_unlock(list); + *num_ch = chn_idx; +} + +static void csr_diag_apply_country_info(struct mac_context *mac_ctx) +{ + host_log_802_11d_pkt_type *p11dLog; + struct channel_power chnPwrInfo[CFG_VALID_CHANNEL_LIST_LEN]; + uint32_t nChnInfo = CFG_VALID_CHANNEL_LIST_LEN, nTmp; + uint8_t i; + + WLAN_HOST_DIAG_LOG_ALLOC(p11dLog, host_log_802_11d_pkt_type, + LOG_WLAN_80211D_C); + if (!p11dLog) + return; + + p11dLog->eventId = WLAN_80211D_EVENT_COUNTRY_SET; + qdf_mem_copy(p11dLog->countryCode, mac_ctx->scan.countryCode11d, 3); + p11dLog->numChannel = mac_ctx->scan.channels11d.numChannels; + if (p11dLog->numChannel > HOST_LOG_MAX_NUM_CHANNEL) + goto diag_end; + + for (i = 0; i < p11dLog->numChannel; i++) + p11dLog->Channels[i] = + wlan_reg_freq_to_chan(mac_ctx->pdev, + mac_ctx->scan.channels11d.channel_freq_list[i]); + csr_get_channel_power_info(mac_ctx, + &mac_ctx->scan.channelPowerInfoList24, + &nChnInfo, chnPwrInfo); + nTmp = nChnInfo; + nChnInfo = CFG_VALID_CHANNEL_LIST_LEN - nTmp; + csr_get_channel_power_info(mac_ctx, + &mac_ctx->scan.channelPowerInfoList5G, + &nChnInfo, &chnPwrInfo[nTmp]); + for (nTmp = 0; nTmp < p11dLog->numChannel; nTmp++) { + for (nChnInfo = 0; + nChnInfo < CFG_VALID_CHANNEL_LIST_LEN; + nChnInfo++) { + if (p11dLog->Channels[nTmp] == + chnPwrInfo[nChnInfo].chan_num) { + p11dLog->TxPwr[nTmp] = + chnPwrInfo[nChnInfo].tx_power; + break; + } + } + } +diag_end: + if (!mac_ctx->mlme_cfg->gen.enabled_11d) + p11dLog->supportMultipleDomain = WLAN_80211D_DISABLED; + else + p11dLog->supportMultipleDomain = + WLAN_80211D_SUPPORT_MULTI_DOMAIN; + WLAN_HOST_DIAG_LOG_REPORT(p11dLog); +} +#endif /* #ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR */ + +/** + * csr_apply_country_information() - apply country code information + * @mac: core MAC data structure + * + * This function programs the new country code + * + * Return: none + */ +void csr_apply_country_information(struct mac_context *mac) +{ + v_REGDOMAIN_t domainId; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!wlan_reg_11d_enabled_on_host(mac->psoc)) + return; + status = csr_get_regulatory_domain_for_country(mac, + mac->scan.countryCode11d, &domainId, SOURCE_QUERY); + if (!QDF_IS_STATUS_SUCCESS(status)) + return; + /* Check whether we need to enforce default domain */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + csr_diag_apply_country_info(mac); +#endif /* #ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR */ + + if (mac->scan.domainIdCurrent != domainId) + return; + if (mac->scan.domainIdCurrent != domainId) { + sme_debug("Domain Changed Old %d, new %d", + mac->scan.domainIdCurrent, domainId); + if (domainId >= REGDOMAIN_COUNT) + sme_err("fail to set regId %d", domainId); + } + mac->scan.domainIdCurrent = domainId; + /* switch to active scans using this new channel list */ + mac->scan.curScanType = eSIR_ACTIVE_SCAN; +} + +void csr_save_channel_power_for_band(struct mac_context *mac, bool fill_5f) +{ + uint32_t idx, count = 0; + tSirMacChanInfo *chan_info; + tSirMacChanInfo *ch_info_start; + int32_t max_ch_idx; + bool tmp_bool; + uint32_t ch_freq = 0; + + max_ch_idx = + (mac->scan.base_channels.numChannels < + CFG_VALID_CHANNEL_LIST_LEN) ? + mac->scan.base_channels.numChannels : + CFG_VALID_CHANNEL_LIST_LEN; + + chan_info = qdf_mem_malloc(sizeof(tSirMacChanInfo) * + CFG_VALID_CHANNEL_LIST_LEN); + if (!chan_info) + return; + + ch_info_start = chan_info; + for (idx = 0; idx < max_ch_idx; idx++) { + ch_freq = mac->scan.defaultPowerTable[idx].center_freq; + tmp_bool = (fill_5f && WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq)) || + (!fill_5f && WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)); + if (!tmp_bool) + continue; + + if (count >= CFG_VALID_CHANNEL_LIST_LEN) { + sme_warn("count: %d exceeded", count); + break; + } + + chan_info->first_freq = + mac->scan.defaultPowerTable[idx].center_freq; + chan_info->numChannels = 1; + chan_info->maxTxPower = + QDF_MIN(mac->scan.defaultPowerTable[idx].tx_power, + mac->mlme_cfg->power.max_tx_power); + chan_info++; + count++; + } + if (count) { + csr_save_to_channel_power2_g_5_g(mac, + count * sizeof(tSirMacChanInfo), ch_info_start); + } + qdf_mem_free(ch_info_start); +} + +bool csr_is_supported_channel(struct mac_context *mac, uint32_t chan_freq) +{ + bool fRet = false; + uint32_t i; + + for (i = 0; i < mac->scan.base_channels.numChannels; i++) { + if (chan_freq == mac->scan.base_channels.channel_freq_list[i]) { + fRet = true; + break; + } + } + + return fRet; +} + +/* + * 802.11D only: Gather 11d IE via beacon or Probe response and store them in + * pAdapter->channels11d + */ +bool csr_learn_11dcountry_information(struct mac_context *mac, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, bool fForce) +{ + QDF_STATUS status; + uint8_t *pCountryCodeSelected; + bool fRet = false; + v_REGDOMAIN_t domainId; + tDot11fBeaconIEs *pIesLocal = pIes; + bool useVoting = false; + + if ((!pSirBssDesc) && (!pIes)) + useVoting = true; + + /* check if .11d support is enabled */ + if (!wlan_reg_11d_enabled_on_host(mac->psoc)) + goto free_ie; + + if (false == useVoting) { + if (!pIesLocal && + (!QDF_IS_STATUS_SUCCESS( + csr_get_parsed_bss_description_ies( + mac, pSirBssDesc, &pIesLocal)))) + goto free_ie; + /* check if country information element is present */ + if (!pIesLocal->Country.present) + /* No country info */ + goto free_ie; + status = csr_get_regulatory_domain_for_country(mac, + pIesLocal->Country.country, &domainId, + SOURCE_QUERY); + if (QDF_IS_STATUS_SUCCESS(status) + && (domainId == REGDOMAIN_WORLD)) + goto free_ie; + } /* useVoting == false */ + + if (false == useVoting) + pCountryCodeSelected = pIesLocal->Country.country; + else + pCountryCodeSelected = mac->scan.countryCodeElected; + + if (qdf_mem_cmp(pCountryCodeSelected, mac->scan.countryCodeCurrent, + CDS_COUNTRY_CODE_LEN) == 0) { + qdf_mem_copy(mac->scan.countryCode11d, + mac->scan.countryCodeCurrent, + CDS_COUNTRY_CODE_LEN); + goto free_ie; + } + + mac->reg_hint_src = SOURCE_11D; + status = csr_get_regulatory_domain_for_country(mac, + pCountryCodeSelected, &domainId, SOURCE_11D); + if (status != QDF_STATUS_SUCCESS) { + sme_err("fail to get regId %d", domainId); + fRet = false; + goto free_ie; + } + + fRet = true; +free_ie: + if (!pIes && pIesLocal) { + /* locally allocated */ + qdf_mem_free(pIesLocal); + } + return fRet; +} + +static enum csr_scancomplete_nextcommand +csr_scan_get_next_command_state(struct mac_context *mac_ctx, + uint32_t session_id, + eCsrScanStatus scan_status, + uint32_t *chan_freq) +{ + enum csr_scancomplete_nextcommand NextCommand = eCsrNextScanNothing; + uint32_t chan_freq_ret; + struct csr_roam_session *session; + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("session %d is invalid", session_id); + return NextCommand; + } + session = CSR_GET_SESSION(mac_ctx, session_id); + switch (session->scan_info.scan_reason) { + case eCsrScanForSsid: + sme_debug("Resp for Scan For Ssid"); + chan_freq_ret = csr_scan_get_channel_for_hw_mode_change( + mac_ctx, + session_id, + session->scan_info.profile); + if ((!chan_freq_ret) && scan_status) { + NextCommand = eCsrNexteScanForSsidFailure; + sme_err("next Scan For Ssid Failure %d %d", + chan_freq_ret, scan_status); + } else { + NextCommand = eCsrNextCheckAllowConc; + *chan_freq = chan_freq_ret; + sme_debug("next CheckAllowConc"); + } + break; + default: + NextCommand = eCsrNextScanNothing; + break; + } + sme_debug("Next Command %d", NextCommand); + return NextCommand; +} + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +static void +csr_diag_scan_complete(struct mac_context *mac_ctx, + eCsrScanStatus scan_status) +{ + host_log_scan_pkt_type *pScanLog = NULL; + qdf_list_t *list = NULL; + struct wlan_objmgr_pdev *pdev = NULL; + struct scan_cache_node *cur_node = NULL; + struct scan_cache_node *next_node = NULL; + int n = 0, c = 0; + + WLAN_HOST_DIAG_LOG_ALLOC(pScanLog, + host_log_scan_pkt_type, + LOG_WLAN_SCAN_C); + if (!pScanLog) + return; + + pScanLog->eventId = WLAN_SCAN_EVENT_ACTIVE_SCAN_RSP; + + if (eCSR_SCAN_SUCCESS != scan_status) { + pScanLog->status = WLAN_SCAN_STATUS_FAILURE; + WLAN_HOST_DIAG_LOG_REPORT(pScanLog); + return; + } + pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc, + 0, WLAN_LEGACY_MAC_ID); + + if (!pdev) { + sme_err("pdev is NULL"); + return; + } + + list = ucfg_scan_get_result(pdev, NULL); + if (list) + sme_debug("num_entries %d", qdf_list_size(list)); + if (!list || (list && !qdf_list_size(list))) { + sme_err("get scan result failed"); + WLAN_HOST_DIAG_LOG_REPORT(pScanLog); + wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID); + if (list) + ucfg_scan_purge_results(list); + return; + } + + qdf_list_peek_front(list, + (qdf_list_node_t **) &cur_node); + while (cur_node) { + if (n < HOST_LOG_MAX_NUM_BSSID) { + qdf_mem_copy(pScanLog->bssid[n], + cur_node->entry->bssid.bytes, + QDF_MAC_ADDR_SIZE); + if (cur_node->entry->ssid.length > + WLAN_SSID_MAX_LEN) + cur_node->entry->ssid.length = + WLAN_SSID_MAX_LEN; + qdf_mem_copy(pScanLog->ssid[n], + cur_node->entry->ssid.ssid, + cur_node->entry->ssid.length); + n++; + } + c++; + qdf_list_peek_next( + list, + (qdf_list_node_t *) cur_node, + (qdf_list_node_t **) &next_node); + cur_node = next_node; + next_node = NULL; + } + pScanLog->numSsid = (uint8_t) n; + pScanLog->totalSsid = (uint8_t) c; + ucfg_scan_purge_results(list); + wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID); + WLAN_HOST_DIAG_LOG_REPORT(pScanLog); +} +#endif /* #ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR */ + +/** + * csr_saved_scan_cmd_free_fields() - Free internal fields of scan command + * + * @mac_ctx: Global MAC context + * @session: sme session + * + * Frees data structures allocated inside saved_scan_cmd and releases + * the profile. + * Return: None + */ + +void csr_saved_scan_cmd_free_fields(struct mac_context *mac_ctx, + struct csr_roam_session *session) +{ + if (session->scan_info.profile) { + sme_debug("Free profile for session %d", session->sessionId); + csr_release_profile(mac_ctx, + session->scan_info.profile); + qdf_mem_free(session->scan_info.profile); + session->scan_info.profile = NULL; + } + + if (session->scan_info.roambssentry) { + qdf_mem_free(session->scan_info.roambssentry); + session->scan_info.roambssentry = NULL; + } +} +/** + * csr_save_profile() - Save the profile info from sme command + * @mac_ctx: Global MAC context + * @save_cmd: Pointer where the command will be saved + * @command: Command from which the profile will be saved + * + * Saves the profile information from the SME's scan command + * + * Return: QDF_STATUS + */ +static QDF_STATUS csr_save_profile(struct mac_context *mac_ctx, + uint32_t session_id) +{ + struct tag_csrscan_result *scan_result; + struct tag_csrscan_result *temp; + uint32_t bss_len; + struct csr_roam_session *session; + + /* + * check the session's validity first, if session itself + * is not valid then there is no point of releasing the memory + * for invalid session (i.e. "goto error" case) + */ + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("session %d is invalid", session_id); + return QDF_STATUS_E_FAILURE; + } + session = CSR_GET_SESSION(mac_ctx, session_id); + if (!session->scan_info.roambssentry) + return QDF_STATUS_SUCCESS; + + scan_result = GET_BASE_ADDR(session->scan_info.roambssentry, + struct tag_csrscan_result, Link); + + bss_len = scan_result->Result.BssDescriptor.length + + sizeof(scan_result->Result.BssDescriptor.length); + + temp = qdf_mem_malloc(sizeof(struct tag_csrscan_result) + bss_len); + if (!temp) + goto error; + + temp->AgingCount = scan_result->AgingCount; + temp->preferValue = scan_result->preferValue; + temp->capValue = scan_result->capValue; + temp->ucEncryptionType = scan_result->ucEncryptionType; + temp->mcEncryptionType = scan_result->mcEncryptionType; + temp->authType = scan_result->authType; + /* pvIes is unsued in success/failure */ + temp->Result.pvIes = NULL; + + qdf_mem_copy(temp->Result.pvIes, + scan_result->Result.pvIes, + sizeof(*scan_result->Result.pvIes)); + temp->Result.ssId.length = scan_result->Result.ssId.length; + qdf_mem_copy(temp->Result.ssId.ssId, + scan_result->Result.ssId.ssId, + sizeof(scan_result->Result.ssId.ssId)); + temp->Result.timer = scan_result->Result.timer; + qdf_mem_copy(&temp->Result.BssDescriptor, + &scan_result->Result.BssDescriptor, + sizeof(temp->Result.BssDescriptor)); + temp->Link.last = temp->Link.next = NULL; + session->scan_info.roambssentry = (tListElem *)temp; + + return QDF_STATUS_SUCCESS; +error: + csr_scan_handle_search_for_ssid_failure(mac_ctx, + session_id); + csr_saved_scan_cmd_free_fields(mac_ctx, session); + + return QDF_STATUS_E_FAILURE; +} + +static void csr_handle_nxt_cmd(struct mac_context *mac_ctx, + enum csr_scancomplete_nextcommand nxt_cmd, + uint32_t session_id, + uint32_t chan_freq) +{ + QDF_STATUS status, ret; + struct csr_roam_session *session; + + switch (nxt_cmd) { + + case eCsrNexteScanForSsidSuccess: + csr_scan_handle_search_for_ssid(mac_ctx, session_id); + break; + case eCsrNexteScanForSsidFailure: + csr_scan_handle_search_for_ssid_failure(mac_ctx, session_id); + break; + case eCsrNextCheckAllowConc: + ret = policy_mgr_current_connections_update( + mac_ctx->psoc, session_id, chan_freq, + POLICY_MGR_UPDATE_REASON_HIDDEN_STA); + sme_debug("channel freq: %d session: %d status: %d", + chan_freq, session_id, ret); + + status = csr_save_profile(mac_ctx, session_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + /* csr_save_profile should report error */ + sme_err("profile save failed %d", status); + break; + } + + if (QDF_STATUS_E_FAILURE == ret) { + sme_debug("conn update fail %d", chan_freq); + csr_scan_handle_search_for_ssid_failure(mac_ctx, + session_id); + } else if ((QDF_STATUS_E_NOSUPPORT == ret) || + (QDF_STATUS_E_ALREADY == ret)) { + sme_err("conn update ret %d", ret); + csr_scan_handle_search_for_ssid(mac_ctx, session_id); + } + /* Else: Set hw mode was issued and the saved connect would + * be issued after set hw mode response + */ + if (QDF_IS_STATUS_SUCCESS(ret)) + return; + default: + break; + } + session = CSR_GET_SESSION(mac_ctx, session_id); + if (session) + csr_saved_scan_cmd_free_fields(mac_ctx, session); +} + +void csr_scan_callback(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{ + eCsrScanStatus scan_status = eCSR_SCAN_FAILURE; + enum csr_scancomplete_nextcommand NextCommand = eCsrNextScanNothing; + struct mac_context *mac_ctx; + struct csr_roam_session *session; + uint32_t session_id = 0; + uint32_t chan_freq = 0; + QDF_STATUS status; + bool success = false; + + mac_ctx = (struct mac_context *)arg; + + qdf_mtrace(QDF_MODULE_ID_SCAN, QDF_MODULE_ID_SME, event->type, + event->vdev_id, event->scan_id); + + if (!util_is_scan_completed(event, &success)) + return; + + if (success) + scan_status = eCSR_SCAN_SUCCESS; + + session_id = wlan_vdev_get_id(vdev); + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_ERROR(status)) + return; + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("session %d is invalid", session_id); + sme_release_global_lock(&mac_ctx->sme); + return; + } + + session = CSR_GET_SESSION(mac_ctx, session_id); + + sme_debug("Scan Completion: status %d session %d scan_id %d", + scan_status, session_id, event->scan_id); + + /* verify whether scan event is related to scan interested by CSR */ + if (session->scan_info.scan_id != event->scan_id) { + sme_debug("Scan Completion on wrong scan_id %d, expected %d", + session->scan_info.scan_id, event->scan_id); + sme_release_global_lock(&mac_ctx->sme); + return; + } +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + csr_diag_scan_complete(mac_ctx, scan_status); +#endif + NextCommand = csr_scan_get_next_command_state(mac_ctx, + session_id, scan_status, + &chan_freq); + /* We reuse the command here instead reissue a new command */ + csr_handle_nxt_cmd(mac_ctx, NextCommand, + session_id, chan_freq); + + sme_release_global_lock(&mac_ctx->sme); +} + +tCsrScanResultInfo *csr_scan_result_get_first(struct mac_context *mac, + tScanResultHandle hScanResult) +{ + tListElem *pEntry; + struct tag_csrscan_result *pResult; + tCsrScanResultInfo *pRet = NULL; + struct scan_result_list *pResultList = + (struct scan_result_list *) hScanResult; + + if (pResultList) { + pEntry = csr_ll_peek_head(&pResultList->List, LL_ACCESS_NOLOCK); + if (pEntry) { + pResult = GET_BASE_ADDR(pEntry, struct + tag_csrscan_result, Link); + pRet = &pResult->Result; + } + pResultList->pCurEntry = pEntry; + } + + return pRet; +} + +tCsrScanResultInfo *csr_scan_result_get_next(struct mac_context *mac, + tScanResultHandle hScanResult) +{ + tListElem *pEntry = NULL; + struct tag_csrscan_result *pResult = NULL; + tCsrScanResultInfo *pRet = NULL; + struct scan_result_list *pResultList = + (struct scan_result_list *) hScanResult; + + if (!pResultList) + return NULL; + + if (!pResultList->pCurEntry) + pEntry = csr_ll_peek_head(&pResultList->List, LL_ACCESS_NOLOCK); + else + pEntry = csr_ll_next(&pResultList->List, pResultList->pCurEntry, + LL_ACCESS_NOLOCK); + + if (pEntry) { + pResult = GET_BASE_ADDR(pEntry, struct tag_csrscan_result, + Link); + pRet = &pResult->Result; + } + pResultList->pCurEntry = pEntry; + + return pRet; +} + +/** + * csr_scan_for_ssid() - Function usually used for BSSs that suppresses SSID + * @mac_ctx: Pointer to Global Mac structure + * @profile: pointer to struct csr_roam_profile + * @roam_id: variable representing roam id + * @notify: boolean variable + * + * Function is usually used for BSSs that suppresses SSID so the profile + * shall have one and only one SSID. + * + * Return: Success - QDF_STATUS_SUCCESS, Failure - error number + */ +QDF_STATUS csr_scan_for_ssid(struct mac_context *mac_ctx, uint32_t session_id, + struct csr_roam_profile *profile, uint32_t roam_id, + bool notify) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + uint32_t num_ssid = profile->SSIDs.numOfSSIDs; + struct scan_start_request *req; + struct wlan_objmgr_vdev *vdev; + uint8_t i, num_chan = 0; + uint8_t pdev_id; + wlan_scan_id scan_id; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + sme_err("session %d is invalid", session_id); + return status; + } + + if (num_ssid != 1) { + sme_err("numSSID (%d) is invalid", profile->SSIDs.numOfSSIDs); + return status; + } + + if (!mac_ctx->pdev) { + sme_err("pdev ctx is NULL"); + return status; + } + pdev_id = wlan_objmgr_pdev_get_pdev_id(mac_ctx->pdev); + + /* Free old memory if any before its overwritten */ + csr_saved_scan_cmd_free_fields(mac_ctx, session); + session->scan_info.profile = + qdf_mem_malloc(sizeof(struct csr_roam_profile)); + if (!session->scan_info.profile) + status = QDF_STATUS_E_NOMEM; + else + status = csr_roam_copy_profile(mac_ctx, + session->scan_info.profile, + profile); + if (QDF_IS_STATUS_ERROR(status)) + goto error; + scan_id = ucfg_scan_get_scan_id(mac_ctx->psoc); + session->scan_info.scan_id = scan_id; + session->scan_info.scan_reason = eCsrScanForSsid; + session->scan_info.roam_id = roam_id; + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + status = QDF_STATUS_E_NOMEM; + goto error; + } + + vdev = wlan_objmgr_get_vdev_by_macaddr_from_psoc(mac_ctx->psoc, + pdev_id, + session->self_mac_addr.bytes, + WLAN_LEGACY_SME_ID); + ucfg_scan_init_default_params(vdev, req); + req->scan_req.scan_id = scan_id; + req->scan_req.vdev_id = session_id; + req->scan_req.scan_req_id = mac_ctx->scan.requester_id; + req->scan_req.scan_f_passive = false; + req->scan_req.scan_f_bcast_probe = false; + + if (QDF_P2P_CLIENT_MODE == profile->csrPersona) + req->scan_req.scan_priority = SCAN_PRIORITY_HIGH; + + /* Allocate memory for IE field */ + if (profile->pAddIEScan) { + req->scan_req.extraie.ptr = + qdf_mem_malloc(profile->nAddIEScanLength); + + if (!req->scan_req.extraie.ptr) + status = QDF_STATUS_E_NOMEM; + else + status = QDF_STATUS_SUCCESS; + + if (QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_copy(req->scan_req.extraie.ptr, + profile->pAddIEScan, + profile->nAddIEScanLength); + req->scan_req.extraie.len = profile->nAddIEScanLength; + } else { + sme_err("No memory for scanning IE fields"); + } + } + + req->scan_req.num_bssid = 1; + if (profile->BSSIDs.numOfBSSIDs == 1) + qdf_copy_macaddr(&req->scan_req.bssid_list[0], + profile->BSSIDs.bssid); + else + qdf_set_macaddr_broadcast(&req->scan_req.bssid_list[0]); + + if (profile->ChannelInfo.numOfChannels) { + for (i = 0; i < profile->ChannelInfo.numOfChannels; i++) { + if (csr_roam_is_valid_channel(mac_ctx, + profile->ChannelInfo.freq_list[i])) { + req->scan_req.chan_list.chan[num_chan].freq = + profile->ChannelInfo.freq_list[i]; + num_chan++; + } + } + req->scan_req.chan_list.num_chan = num_chan; + } + + /* Extend it for multiple SSID */ + if (profile->SSIDs.numOfSSIDs) { + if (profile->SSIDs.SSIDList[0].SSID.length > WLAN_SSID_MAX_LEN) { + sme_debug("wrong ssid length = %d", + profile->SSIDs.SSIDList[0].SSID.length); + status = QDF_STATUS_E_INVAL; + goto error; + } + req->scan_req.num_ssids = 1; + qdf_mem_copy(&req->scan_req.ssid[0].ssid, + &profile->SSIDs.SSIDList[0].SSID.ssId, + profile->SSIDs.SSIDList[0].SSID.length); + req->scan_req.ssid[0].length = + profile->SSIDs.SSIDList[0].SSID.length; + sme_debug("scan for SSID = %.*s", req->scan_req.ssid[0].length, + req->scan_req.ssid[0].ssid); + } + status = ucfg_scan_start(req); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); +error: + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("failed to initiate scan with status: %d", status); + csr_release_profile(mac_ctx, session->scan_info.profile); + qdf_mem_free(session->scan_info.profile); + session->scan_info.profile = NULL; + if (notify) + csr_roam_call_callback(mac_ctx, session_id, NULL, + roam_id, eCSR_ROAM_FAILED, + eCSR_ROAM_RESULT_FAILURE); + } + return status; +} + +static void csr_set_cfg_valid_channel_list(struct mac_context *mac, + uint32_t *pchan_freq_list, + uint8_t NumChannels) +{ + QDF_STATUS status; + uint8_t i; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: dump valid channel list(NumChannels(%d))", + __func__, NumChannels); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + pchan_freq_list, NumChannels); + for (i = 0; i < NumChannels; i++) { + mac->mlme_cfg->reg.valid_channel_freq_list[i] = pchan_freq_list[i]; + } + + mac->mlme_cfg->reg.valid_channel_list_num = NumChannels; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Scan offload is enabled, update default chan list"); + /* + * disable fcc constraint since new country code + * is being set + */ + mac->scan.fcc_constraint = false; + status = csr_update_channel_list(mac); + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "failed to update the supported channel list"); + } +} + +/* + * The Tx power limits are saved in the cfg for future usage. + */ +static void csr_save_tx_power_to_cfg(struct mac_context *mac, + tDblLinkList *pList, + enum band_info band) +{ + tListElem *pEntry; + uint32_t cbLen = 0, dataLen, tmp_len; + struct csr_channel_powerinfo *ch_set; + uint32_t idx, count = 0; + tSirMacChanInfo *ch_pwr_set; + uint8_t *p_buf = NULL; + + /* allocate maximum space for all channels */ + dataLen = CFG_VALID_CHANNEL_LIST_LEN * sizeof(tSirMacChanInfo); + p_buf = qdf_mem_malloc(dataLen); + if (!p_buf) + return; + + ch_pwr_set = (tSirMacChanInfo *)(p_buf); + csr_ll_lock(pList); + pEntry = csr_ll_peek_head(pList, LL_ACCESS_NOLOCK); + /* + * write the tuples (startChan, numChan, txPower) for each channel found + * in the channel power list. + */ + while (pEntry) { + ch_set = GET_BASE_ADDR(pEntry, + struct csr_channel_powerinfo, link); + if (ch_set->interChannelOffset != 5) { + /* + * we keep the 5G channel sets internally with an + * interchannel offset of 4. Expand these to the right + * format. (inter channel offset of 1 is the only option + * for the triplets that 11d advertises. + */ + tmp_len = cbLen + (ch_set->numChannels * + sizeof(tSirMacChanInfo)); + if (tmp_len >= dataLen) { + /* + * expanding this entry will overflow our + * allocation + */ + sme_err( + "Buffer overflow, start freq %d, num %d, offset %d", + ch_set->first_chan_freq, + ch_set->numChannels, + ch_set->interChannelOffset); + break; + } + + for (idx = 0; idx < ch_set->numChannels; idx++) { + ch_pwr_set->first_freq = + ch_set->first_chan_freq; + ch_pwr_set->numChannels = 1; + ch_pwr_set->maxTxPower = + QDF_MIN(ch_set->txPower, + mac->mlme_cfg->power.max_tx_power); + cbLen += sizeof(tSirMacChanInfo); + ch_pwr_set++; + count++; + } + } else { + if (cbLen + sizeof(tSirMacChanInfo) >= dataLen) { + /* this entry will overflow our allocation */ + sme_err( + "Buffer overflow, start freq %d, num %d, offset %d", + ch_set->first_chan_freq, + ch_set->numChannels, + ch_set->interChannelOffset); + break; + } + ch_pwr_set->first_freq = ch_set->first_chan_freq; + ch_pwr_set->numChannels = ch_set->numChannels; + ch_pwr_set->maxTxPower = QDF_MIN(ch_set->txPower, + mac->mlme_cfg->power.max_tx_power); + cbLen += sizeof(tSirMacChanInfo); + ch_pwr_set++; + count++; + } + pEntry = csr_ll_next(pList, pEntry, LL_ACCESS_NOLOCK); + } + csr_ll_unlock(pList); + if (band == BAND_2G) { + mac->mlme_cfg->power.max_tx_power_24.len = + sizeof(tSirMacChanInfo) * count; + if (mac->mlme_cfg->power.max_tx_power_24.len > + CFG_MAX_TX_POWER_2_4_LEN) + mac->mlme_cfg->power.max_tx_power_24.len = + CFG_MAX_TX_POWER_2_4_LEN; + qdf_mem_copy(mac->mlme_cfg->power.max_tx_power_24.data, + (uint8_t *)p_buf, + mac->mlme_cfg->power.max_tx_power_24.len); + } + if (band == BAND_5G) { + mac->mlme_cfg->power.max_tx_power_5.len = + sizeof(tSirMacChanInfo) * count; + if (mac->mlme_cfg->power.max_tx_power_5.len > + CFG_MAX_TX_POWER_5_LEN) + mac->mlme_cfg->power.max_tx_power_5.len = + CFG_MAX_TX_POWER_5_LEN; + qdf_mem_copy(mac->mlme_cfg->power.max_tx_power_5.data, + (uint8_t *)p_buf, + mac->mlme_cfg->power.max_tx_power_5.len); + } + qdf_mem_free(p_buf); +} + +static void csr_set_cfg_country_code(struct mac_context *mac, + uint8_t *countryCode) +{ + uint8_t cc[CFG_COUNTRY_CODE_LEN]; + /* v_REGDOMAIN_t DomainId */ + + sme_debug("Set Country in Cfg %s", countryCode); + qdf_mem_copy(cc, countryCode, CFG_COUNTRY_CODE_LEN); + + /* + * Don't program the bogus country codes that we created for Korea in + * the MAC. if we see the bogus country codes, program the MAC with + * the right country code. + */ + if (('K' == countryCode[0] && '1' == countryCode[1]) || + ('K' == countryCode[0] && '2' == countryCode[1]) || + ('K' == countryCode[0] && '3' == countryCode[1]) || + ('K' == countryCode[0] && '4' == countryCode[1])) { + /* + * replace the alternate Korea country codes, 'K1', 'K2', .. + * with 'KR' for Korea + */ + cc[1] = 'R'; + } + + /* + * country code is moved to mlme component, and it is limited to call + * legacy api, so required to call sch_edca_profile_update_all if + * overwrite country code in mlme component + */ + qdf_mem_copy(mac->mlme_cfg->reg.country_code, cc, CFG_COUNTRY_CODE_LEN); + mac->mlme_cfg->reg.country_code_len = CFG_COUNTRY_CODE_LEN; + sch_edca_profile_update_all(mac); + /* + * Need to let HALPHY know about the current domain so it can apply some + * domain-specific settings (TX filter...) + */ +} + +QDF_STATUS csr_get_country_code(struct mac_context *mac, uint8_t *pBuf, + uint8_t *pbLen) +{ + if (pBuf && pbLen && (*pbLen >= CFG_COUNTRY_CODE_LEN)) { + *pbLen = mac->mlme_cfg->reg.country_code_len; + qdf_mem_copy(pBuf, mac->mlme_cfg->reg.country_code, + (uint32_t)*pbLen); + return QDF_STATUS_SUCCESS; + } + + return QDF_STATUS_E_INVAL; +} + +QDF_STATUS csr_scan_abort_mac_scan(struct mac_context *mac_ctx, + uint32_t vdev_id, + uint32_t scan_id) +{ + struct scan_cancel_request *req; + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return QDF_STATUS_E_NOMEM; + + /* Get NL global context from objmgr*/ + if (vdev_id == INVAL_VDEV_ID) + vdev = wlan_objmgr_pdev_get_first_vdev(mac_ctx->pdev, + WLAN_LEGACY_SME_ID); + else + vdev = wlan_objmgr_get_vdev_by_id_from_pdev(mac_ctx->pdev, + vdev_id, WLAN_LEGACY_SME_ID); + + if (!vdev) { + sme_debug("Failed get vdev"); + qdf_mem_free(req); + return QDF_STATUS_E_INVAL; + } + + req->vdev = vdev; + req->cancel_req.scan_id = scan_id; + req->cancel_req.pdev_id = wlan_objmgr_pdev_get_pdev_id(mac_ctx->pdev); + req->cancel_req.vdev_id = vdev_id; + if (scan_id != INVAL_SCAN_ID) + req->cancel_req.req_type = WLAN_SCAN_CANCEL_SINGLE; + if (vdev_id == INVAL_VDEV_ID) + req->cancel_req.req_type = WLAN_SCAN_CANCEL_PDEV_ALL; + else + req->cancel_req.req_type = WLAN_SCAN_CANCEL_VDEV_ALL; + + status = ucfg_scan_cancel(req); + if (QDF_IS_STATUS_ERROR(status)) + sme_err("Cancel scan request failed"); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return status; +} + +bool csr_roam_is_valid_channel(struct mac_context *mac, uint32_t ch_freq) +{ + bool fValid = false; + uint32_t idx_valid_ch; + uint32_t len = mac->roam.numValidChannels; + + for (idx_valid_ch = 0; (idx_valid_ch < len); idx_valid_ch++) { + if (ch_freq == mac->roam.valid_ch_freq_list[idx_valid_ch]) { + fValid = true; + break; + } + } + return fValid; +} + +QDF_STATUS csr_scan_create_entry_in_scan_cache(struct mac_context *mac, + uint32_t sessionId, + struct qdf_mac_addr bssid, + uint32_t ch_freq) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + struct bss_description *pNewBssDescriptor = NULL; + uint32_t size = 0; + + if (!pSession) { + return QDF_STATUS_E_FAILURE; + } + sme_debug("Current bssid::"QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pSession->pConnectBssDesc->bssId)); + sme_debug("My bssid::"QDF_MAC_ADDR_FMT" ch_freq %d", + QDF_MAC_ADDR_REF(bssid.bytes), ch_freq); + + size = pSession->pConnectBssDesc->length + + sizeof(pSession->pConnectBssDesc->length); + if (!size) { + sme_err("length of bss descriptor is 0"); + return QDF_STATUS_E_FAILURE; + } + pNewBssDescriptor = qdf_mem_malloc(size); + if (!pNewBssDescriptor) + return QDF_STATUS_E_FAILURE; + + qdf_mem_copy(pNewBssDescriptor, pSession->pConnectBssDesc, size); + /* change the BSSID & channel as passed */ + qdf_mem_copy(pNewBssDescriptor->bssId, bssid.bytes, + sizeof(tSirMacAddr)); + pNewBssDescriptor->chan_freq = ch_freq; + if (!csr_scan_append_bss_description(mac, pNewBssDescriptor)) { + sme_err("csr_scan_append_bss_description failed"); + status = QDF_STATUS_E_FAILURE; + goto free_mem; + } + sme_err("entry successfully added in scan cache"); + +free_mem: + if (pNewBssDescriptor) + qdf_mem_free(pNewBssDescriptor); + + return status; +} + +#ifdef FEATURE_WLAN_ESE +/* Update the TSF with the difference in system time */ +void update_cckmtsf(uint32_t *timeStamp0, uint32_t *timeStamp1, + uint64_t *incr) +{ + uint64_t timeStamp64 = ((uint64_t) *timeStamp1 << 32) | (*timeStamp0); + + timeStamp64 = (uint64_t)(timeStamp64 + (*incr)); + *timeStamp0 = (uint32_t) (timeStamp64 & 0xffffffff); + *timeStamp1 = (uint32_t) ((timeStamp64 >> 32) & 0xffffffff); +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS +csr_rso_save_ap_to_scan_cache(struct mac_context *mac, + struct roam_offload_synch_ind *roam_sync_ind_ptr, + struct bss_description *bss_desc_ptr) +{ + uint32_t length = 0; + struct tag_csrscan_result *scan_res_ptr = NULL; + + length = roam_sync_ind_ptr->beaconProbeRespLength - + (SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_SSID_OFFSET); + scan_res_ptr = qdf_mem_malloc(sizeof(struct tag_csrscan_result) + + length); + if (!scan_res_ptr) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(&scan_res_ptr->Result.BssDescriptor, + bss_desc_ptr, + (sizeof(struct bss_description) + length)); + + sme_debug("LFR3:Add BSSID to scan cache" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(scan_res_ptr->Result.BssDescriptor.bssId)); + csr_scan_add_result(mac, scan_res_ptr); + csr_free_scan_result_entry(mac, scan_res_ptr); + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * csr_get_fst_bssdescr_ptr() - This function returns the pointer to first bss + * description from scan handle + * @result_handle: an object for the result. + * + * Return: first bss descriptor from the scan handle. + */ +struct bss_description* +csr_get_fst_bssdescr_ptr(tScanResultHandle result_handle) +{ + tListElem *first_element = NULL; + struct tag_csrscan_result *scan_result = NULL; + struct scan_result_list *bss_list = + (struct scan_result_list *)result_handle; + + if (!bss_list) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Empty bss_list")); + return NULL; + } + if (csr_ll_is_list_empty(&bss_list->List, LL_ACCESS_NOLOCK)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("bss_list->List is empty")); + return NULL; + } + first_element = csr_ll_peek_head(&bss_list->List, LL_ACCESS_NOLOCK); + if (!first_element) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("peer head return NULL")); + return NULL; + } + + scan_result = GET_BASE_ADDR(first_element, struct tag_csrscan_result, + Link); + + return &scan_result->Result.BssDescriptor; +} + +/** + * csr_get_bssdescr_from_scan_handle() - This function to extract + * first bss description from scan handle + * @result_handle: an object for the result. + * + * This function is written to extract first bss from scan handle. + * + * Return: first bss descriptor from the scan handle. + */ +struct bss_description * +csr_get_bssdescr_from_scan_handle(tScanResultHandle result_handle, + struct bss_description *bss_descr) +{ + tListElem *first_element = NULL; + struct tag_csrscan_result *scan_result = NULL; + struct scan_result_list *bss_list = + (struct scan_result_list *)result_handle; + + if (!bss_list) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Empty bss_list")); + return NULL; + } + if (csr_ll_is_list_empty(&bss_list->List, LL_ACCESS_NOLOCK)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("bss_list->List is empty")); + qdf_mem_free(bss_list); + return NULL; + } + first_element = csr_ll_peek_head(&bss_list->List, LL_ACCESS_NOLOCK); + if (first_element) { + scan_result = GET_BASE_ADDR(first_element, + struct tag_csrscan_result, + Link); + qdf_mem_copy(bss_descr, + &scan_result->Result.BssDescriptor, + sizeof(struct bss_description)); + } + return bss_descr; +} + +uint32_t +csr_get_channel_for_hw_mode_change(struct mac_context *mac_ctx, + tScanResultHandle result_handle, + uint32_t session_id) +{ + tListElem *next_element = NULL; + struct tag_csrscan_result *scan_result = NULL; + struct scan_result_list *bss_list = + (struct scan_result_list *)result_handle; + uint32_t ch_freq = 0; + + if (!bss_list) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Empty bss_list")); + goto end; + } + + if (policy_mgr_is_hw_dbs_capable(mac_ctx->psoc) == false) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("driver isn't dbs capable")); + goto end; + } + + if (policy_mgr_is_current_hwmode_dbs(mac_ctx->psoc)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("driver is already in DBS")); + goto end; + } + + if (!policy_mgr_is_dbs_allowed_for_concurrency(mac_ctx->psoc, + QDF_STA_MODE)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("DBS is not allowed for this concurrency combo")); + goto end; + } + + if (!policy_mgr_is_hw_dbs_2x2_capable(mac_ctx->psoc) && + !policy_mgr_is_hw_dbs_required_for_band(mac_ctx->psoc, + HW_MODE_MAC_BAND_2G) && + !policy_mgr_get_connection_count(mac_ctx->psoc)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("1x1 DBS HW with no prior connection")); + goto end; + } + + if (csr_ll_is_list_empty(&bss_list->List, LL_ACCESS_NOLOCK)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("bss_list->List is empty")); + qdf_mem_free(bss_list); + goto end; + } + + next_element = csr_ll_peek_head(&bss_list->List, LL_ACCESS_NOLOCK); + while (next_element) { + scan_result = GET_BASE_ADDR(next_element, + struct tag_csrscan_result, + Link); + ch_freq = scan_result->Result.BssDescriptor.chan_freq; + if (policy_mgr_is_hw_dbs_required_for_band( + mac_ctx->psoc, + HW_MODE_MAC_BAND_2G)) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) + break; + } else { + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq) && + policy_mgr_is_any_mode_active_on_band_along_with_session + (mac_ctx->psoc, + session_id, POLICY_MGR_BAND_5)) + break; + if (WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq) && + policy_mgr_is_any_mode_active_on_band_along_with_session + (mac_ctx->psoc, + session_id, POLICY_MGR_BAND_24)) + break; + } + next_element = csr_ll_next(&bss_list->List, next_element, + LL_ACCESS_NOLOCK); + } +end: + return ch_freq; +} + +uint32_t +csr_scan_get_channel_for_hw_mode_change(struct mac_context *mac_ctx, + uint32_t session_id, + struct csr_roam_profile *profile) +{ + tScanResultHandle result_handle = NULL; + QDF_STATUS status; + uint32_t first_ap_ch_freq = 0, candidate_ch_freq; + + status = sme_get_ap_channel_from_scan_cache(profile, &result_handle, + &first_ap_ch_freq); + if (status != QDF_STATUS_SUCCESS || !result_handle || + !first_ap_ch_freq) { + if (result_handle) + sme_scan_result_purge(result_handle); + sme_err("fail get scan result: status %d first ap ch %d", + status, first_ap_ch_freq); + return 0; + } + if (!policy_mgr_check_for_session_conc( + mac_ctx->psoc, session_id, first_ap_ch_freq)) { + sme_scan_result_purge(result_handle); + sme_err("Conc not allowed for the session %d ch %d", + session_id, first_ap_ch_freq); + return 0; + } + + candidate_ch_freq = csr_get_channel_for_hw_mode_change(mac_ctx, + result_handle, + session_id); + sme_scan_result_purge(result_handle); + if (!candidate_ch_freq) + candidate_ch_freq = first_ap_ch_freq; + sme_debug("session %d hw mode check candidate_chan %d", session_id, + candidate_ch_freq); + + return candidate_ch_freq; +} + +enum wlan_auth_type csr_covert_auth_type_new(enum csr_akm_type auth) +{ + switch (auth) { + case eCSR_AUTH_TYPE_NONE: + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + return WLAN_AUTH_TYPE_OPEN_SYSTEM; + case eCSR_AUTH_TYPE_SHARED_KEY: + return WLAN_AUTH_TYPE_SHARED_KEY; + case eCSR_AUTH_TYPE_AUTOSWITCH: + return WLAN_AUTH_TYPE_AUTOSWITCH; + case eCSR_AUTH_TYPE_WPA: + return WLAN_AUTH_TYPE_WPA; + case eCSR_AUTH_TYPE_WPA_PSK: + return WLAN_AUTH_TYPE_WPA_PSK; + case eCSR_AUTH_TYPE_WPA_NONE: + return WLAN_AUTH_TYPE_WPA_NONE; + case eCSR_AUTH_TYPE_RSN: + return WLAN_AUTH_TYPE_RSN; + case eCSR_AUTH_TYPE_RSN_PSK: + return WLAN_AUTH_TYPE_RSN_PSK; + case eCSR_AUTH_TYPE_FT_RSN: + return WLAN_AUTH_TYPE_FT_RSN; + case eCSR_AUTH_TYPE_FT_RSN_PSK: + return WLAN_AUTH_TYPE_FT_RSN_PSK; + case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE: + return WLAN_AUTH_TYPE_WAPI_WAI_CERTIFICATE; + case eCSR_AUTH_TYPE_WAPI_WAI_PSK: + return WLAN_AUTH_TYPE_WAPI_WAI_PSK; + case eCSR_AUTH_TYPE_CCKM_WPA: + return WLAN_AUTH_TYPE_CCKM_WPA; + case eCSR_AUTH_TYPE_CCKM_RSN: + return WLAN_AUTH_TYPE_CCKM_RSN; + case eCSR_AUTH_TYPE_RSN_PSK_SHA256: + return WLAN_AUTH_TYPE_RSN_PSK_SHA256; + case eCSR_AUTH_TYPE_RSN_8021X_SHA256: + return WLAN_AUTH_TYPE_RSN_8021X_SHA256; + case eCSR_AUTH_TYPE_FILS_SHA256: + return WLAN_AUTH_TYPE_FILS_SHA256; + case eCSR_AUTH_TYPE_FILS_SHA384: + return WLAN_AUTH_TYPE_FILS_SHA384; + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + return WLAN_AUTH_TYPE_FT_FILS_SHA256; + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return WLAN_AUTH_TYPE_FT_FILS_SHA384; + case eCSR_AUTH_TYPE_DPP_RSN: + return WLAN_AUTH_TYPE_DPP_RSN; + case eCSR_AUTH_TYPE_OWE: + return WLAN_AUTH_TYPE_OWE; + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA256: + return WLAN_AUTH_TYPE_SUITEB_EAP_SHA256; + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA384: + return WLAN_AUTH_TYPE_SUITEB_EAP_SHA384; + case eCSR_AUTH_TYPE_SAE: + return WLAN_AUTH_TYPE_SAE; + case eCSR_AUTH_TYPE_OSEN: + return WLAN_AUTH_TYPE_OSEN; + case eCSR_AUTH_TYPE_FT_SAE: + return WLAN_AUTH_TYPE_FT_SAE; + case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384: + return WLAN_AUTH_TYPE_FT_SUITEB_EAP_SHA384; + case eCSR_NUM_OF_SUPPORT_AUTH_TYPE: + default: + return WLAN_AUTH_TYPE_OPEN_SYSTEM; + } +} + +static enum csr_akm_type csr_covert_auth_type_old(enum wlan_auth_type auth) +{ + switch (auth) { + case WLAN_AUTH_TYPE_OPEN_SYSTEM: + return eCSR_AUTH_TYPE_OPEN_SYSTEM; + case WLAN_AUTH_TYPE_SHARED_KEY: + return eCSR_AUTH_TYPE_SHARED_KEY; + case WLAN_AUTH_TYPE_AUTOSWITCH: + return eCSR_AUTH_TYPE_AUTOSWITCH; + case WLAN_AUTH_TYPE_WPA: + return eCSR_AUTH_TYPE_WPA; + case WLAN_AUTH_TYPE_WPA_PSK: + return eCSR_AUTH_TYPE_WPA_PSK; + case WLAN_AUTH_TYPE_WPA_NONE: + return eCSR_AUTH_TYPE_WPA_NONE; + case WLAN_AUTH_TYPE_RSN: + return eCSR_AUTH_TYPE_RSN; + case WLAN_AUTH_TYPE_RSN_PSK: + return eCSR_AUTH_TYPE_RSN_PSK; + case WLAN_AUTH_TYPE_FT_RSN: + return eCSR_AUTH_TYPE_FT_RSN; + case WLAN_AUTH_TYPE_FT_RSN_PSK: + return eCSR_AUTH_TYPE_FT_RSN_PSK; + case WLAN_AUTH_TYPE_WAPI_WAI_CERTIFICATE: + return eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE; + case WLAN_AUTH_TYPE_WAPI_WAI_PSK: + return eCSR_AUTH_TYPE_WAPI_WAI_PSK; + case WLAN_AUTH_TYPE_CCKM_WPA: + return eCSR_AUTH_TYPE_CCKM_WPA; + case WLAN_AUTH_TYPE_CCKM_RSN: + return eCSR_AUTH_TYPE_CCKM_RSN; + case WLAN_AUTH_TYPE_RSN_PSK_SHA256: + return eCSR_AUTH_TYPE_RSN_PSK_SHA256; + case WLAN_AUTH_TYPE_RSN_8021X_SHA256: + return eCSR_AUTH_TYPE_RSN_8021X_SHA256; + case WLAN_AUTH_TYPE_FILS_SHA256: + return eCSR_AUTH_TYPE_FILS_SHA256; + case WLAN_AUTH_TYPE_FILS_SHA384: + return eCSR_AUTH_TYPE_FILS_SHA384; + case WLAN_AUTH_TYPE_FT_FILS_SHA256: + return eCSR_AUTH_TYPE_FT_FILS_SHA256; + case WLAN_AUTH_TYPE_FT_FILS_SHA384: + return eCSR_AUTH_TYPE_FT_FILS_SHA384; + case WLAN_AUTH_TYPE_DPP_RSN: + return eCSR_AUTH_TYPE_DPP_RSN; + case WLAN_AUTH_TYPE_OWE: + return eCSR_AUTH_TYPE_OWE; + case WLAN_AUTH_TYPE_SUITEB_EAP_SHA256: + return eCSR_AUTH_TYPE_SUITEB_EAP_SHA256; + case WLAN_AUTH_TYPE_SUITEB_EAP_SHA384: + return eCSR_AUTH_TYPE_SUITEB_EAP_SHA384; + case WLAN_AUTH_TYPE_SAE: + return eCSR_AUTH_TYPE_SAE; + case WLAN_AUTH_TYPE_OSEN: + return eCSR_AUTH_TYPE_OSEN; + case WLAN_AUTH_TYPE_FT_SAE: + return eCSR_AUTH_TYPE_FT_SAE; + case WLAN_AUTH_TYPE_FT_SUITEB_EAP_SHA384: + return eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384; + case WLAN_NUM_OF_SUPPORT_AUTH_TYPE: + default: + return eCSR_AUTH_TYPE_OPEN_SYSTEM; + } +} + +enum wlan_enc_type csr_covert_enc_type_new(eCsrEncryptionType enc) +{ + switch (enc) { + case eCSR_ENCRYPT_TYPE_NONE: + return WLAN_ENCRYPT_TYPE_NONE; + case eCSR_ENCRYPT_TYPE_WEP40_STATICKEY: + return WLAN_ENCRYPT_TYPE_WEP40_STATICKEY; + case eCSR_ENCRYPT_TYPE_WEP104_STATICKEY: + return WLAN_ENCRYPT_TYPE_WEP104_STATICKEY; + case eCSR_ENCRYPT_TYPE_WEP40: + return WLAN_ENCRYPT_TYPE_WEP40; + case eCSR_ENCRYPT_TYPE_WEP104: + return WLAN_ENCRYPT_TYPE_WEP104; + case eCSR_ENCRYPT_TYPE_TKIP: + return WLAN_ENCRYPT_TYPE_TKIP; + case eCSR_ENCRYPT_TYPE_AES: + return WLAN_ENCRYPT_TYPE_AES; + case eCSR_ENCRYPT_TYPE_WPI: + return WLAN_ENCRYPT_TYPE_WPI; + case eCSR_ENCRYPT_TYPE_KRK: + return WLAN_ENCRYPT_TYPE_KRK; + case eCSR_ENCRYPT_TYPE_BTK: + return WLAN_ENCRYPT_TYPE_BTK; + case eCSR_ENCRYPT_TYPE_AES_CMAC: + return WLAN_ENCRYPT_TYPE_AES_CMAC; + case eCSR_ENCRYPT_TYPE_AES_GCMP: + return WLAN_ENCRYPT_TYPE_AES_GCMP; + case eCSR_ENCRYPT_TYPE_AES_GCMP_256: + return WLAN_ENCRYPT_TYPE_AES_GCMP_256; + case eCSR_ENCRYPT_TYPE_ANY: + default: + return WLAN_ENCRYPT_TYPE_NONE; + } +} + +static eCsrEncryptionType csr_covert_enc_type_old(enum wlan_enc_type enc) +{ + switch (enc) { + case WLAN_ENCRYPT_TYPE_NONE: + return eCSR_ENCRYPT_TYPE_NONE; + case WLAN_ENCRYPT_TYPE_WEP40_STATICKEY: + return eCSR_ENCRYPT_TYPE_WEP40_STATICKEY; + case WLAN_ENCRYPT_TYPE_WEP104_STATICKEY: + return eCSR_ENCRYPT_TYPE_WEP104_STATICKEY; + case WLAN_ENCRYPT_TYPE_WEP40: + return eCSR_ENCRYPT_TYPE_WEP40; + case WLAN_ENCRYPT_TYPE_WEP104: + return eCSR_ENCRYPT_TYPE_WEP104; + case WLAN_ENCRYPT_TYPE_TKIP: + return eCSR_ENCRYPT_TYPE_TKIP; + case WLAN_ENCRYPT_TYPE_AES: + return eCSR_ENCRYPT_TYPE_AES; + case WLAN_ENCRYPT_TYPE_WPI: + return eCSR_ENCRYPT_TYPE_WPI; + case WLAN_ENCRYPT_TYPE_KRK: + return eCSR_ENCRYPT_TYPE_KRK; + case WLAN_ENCRYPT_TYPE_BTK: + return eCSR_ENCRYPT_TYPE_BTK; + case WLAN_ENCRYPT_TYPE_AES_CMAC: + return eCSR_ENCRYPT_TYPE_AES_CMAC; + case WLAN_ENCRYPT_TYPE_AES_GCMP: + return eCSR_ENCRYPT_TYPE_AES_GCMP; + case WLAN_ENCRYPT_TYPE_AES_GCMP_256: + return eCSR_ENCRYPT_TYPE_AES_GCMP_256; + case WLAN_ENCRYPT_TYPE_ANY: + default: + return eCSR_ENCRYPT_TYPE_NONE; + } +} + +#ifdef WLAN_FEATURE_FILS_SK +/** + * csr_update_bss_with_fils_data: Fill FILS params in bss desc from scan entry + * @mac_ctx: mac context + * @scan_entry: scan entry + * @bss_descr: bss description + */ +static void csr_update_bss_with_fils_data(struct mac_context *mac_ctx, + struct scan_cache_entry *scan_entry, + struct bss_description *bss_descr) +{ + int ret; + tDot11fIEfils_indication fils_indication = {0}; + struct sir_fils_indication fils_ind; + + if (!scan_entry->ie_list.fils_indication) + return; + + ret = dot11f_unpack_ie_fils_indication(mac_ctx, + scan_entry->ie_list.fils_indication + + SIR_FILS_IND_ELEM_OFFSET, + *(scan_entry->ie_list.fils_indication + 1), + &fils_indication, false); + if (DOT11F_FAILED(ret)) { + sme_err("unpack failed ret: 0x%x", ret); + return; + } + + update_fils_data(&fils_ind, &fils_indication); + if (fils_ind.realm_identifier.realm_cnt > SIR_MAX_REALM_COUNT) + fils_ind.realm_identifier.realm_cnt = SIR_MAX_REALM_COUNT; + + bss_descr->fils_info_element.realm_cnt = + fils_ind.realm_identifier.realm_cnt; + qdf_mem_copy(bss_descr->fils_info_element.realm, + fils_ind.realm_identifier.realm, + bss_descr->fils_info_element.realm_cnt * SIR_REALM_LEN); + if (fils_ind.cache_identifier.is_present) { + bss_descr->fils_info_element.is_cache_id_present = true; + qdf_mem_copy(bss_descr->fils_info_element.cache_id, + fils_ind.cache_identifier.identifier, CACHE_ID_LEN); + } + if (fils_ind.is_fils_sk_auth_supported) + bss_descr->fils_info_element.is_fils_sk_supported = true; +} +#else +static void csr_update_bss_with_fils_data(struct mac_context *mac_ctx, + struct scan_cache_entry *scan_entry, + struct bss_description *bss_descr) +{ } +#endif + +/** + * csr_is_assoc_disallowed() - Find if assoc disallowed + * bit is set in AP's beacon or probe response + * @mac_ctx: mac context + * @scan_entry: scan entry + * + * Return: True if assoc disallowed is set else false + */ +static bool csr_is_assoc_disallowed(struct mac_context *mac_ctx, + struct scan_cache_entry *scan_entry) +{ + int ret; + tDot11fIEMBO_IE mbo_ie = {0}; + uint8_t *mbo_oce; + + mbo_oce = util_scan_entry_mbo_oce(scan_entry); + + if (!mbo_oce) + return false; + + ret = dot11f_unpack_ie_MBO_IE(mac_ctx, mbo_oce + SIR_MBO_ELEM_OFFSET, + *(mbo_oce + 1) - SIR_MAC_MBO_OUI_SIZE, + &mbo_ie, false); + + if (DOT11F_FAILED(ret)) { + sme_err("unpack failed ret: 0x%x", ret); + return false; + } + + return mbo_ie.assoc_disallowed.present; +} + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * csr_fill_single_pmk_ap_cap_from_scan_entry() - WAP3_SPMK VSIE from scan + * entry + * @bss_desc: BSS Descriptor + * @scan_entry: scan entry + * + * Return: None + */ +static void +csr_fill_single_pmk_ap_cap_from_scan_entry(struct bss_description *bss_desc, + struct scan_cache_entry *scan_entry) +{ + bss_desc->is_single_pmk = util_scan_entry_single_pmk(scan_entry); +} + +#else +static inline void +csr_fill_single_pmk_ap_cap_from_scan_entry(struct bss_description *bss_desc, + struct scan_cache_entry *scan_entry) +{ +} +#endif +static QDF_STATUS csr_fill_bss_from_scan_entry(struct mac_context *mac_ctx, + struct scan_cache_entry *scan_entry, + struct tag_csrscan_result **p_result) +{ + tDot11fBeaconIEs *bcn_ies; + struct bss_description *bss_desc; + tCsrScanResultInfo *result_info; + tpSirMacMgmtHdr hdr; + uint8_t *ie_ptr; + struct tag_csrscan_result *bss; + uint32_t bss_len, alloc_len, ie_len; + QDF_STATUS status; + enum channel_state ap_channel_state; + + ap_channel_state = + wlan_reg_get_channel_state_for_freq( + mac_ctx->pdev, + scan_entry->channel.chan_freq); + if (ap_channel_state == CHANNEL_STATE_DISABLE || + ap_channel_state == CHANNEL_STATE_INVALID) { + sme_err("BSS "QDF_MAC_ADDR_FMT" channel %d invalid, not populating this BSSID", + QDF_MAC_ADDR_REF(scan_entry->bssid.bytes), + scan_entry->channel.chan_freq); + return QDF_STATUS_E_INVAL; + } + + ie_len = util_scan_entry_ie_len(scan_entry); + ie_ptr = util_scan_entry_ie_data(scan_entry); + + hdr = (tpSirMacMgmtHdr)scan_entry->raw_frame.ptr; + + bss_len = (uint16_t)(offsetof(struct bss_description, + ieFields[0]) + ie_len); + alloc_len = sizeof(struct tag_csrscan_result) + bss_len; + bss = qdf_mem_malloc(alloc_len); + if (!bss) + return QDF_STATUS_E_NOMEM; + + bss->AgingCount = + (int32_t) mac_ctx->roam.configParam.agingCount; + bss->ucEncryptionType = + csr_covert_enc_type_old(scan_entry->neg_sec_info.uc_enc); + bss->mcEncryptionType = + csr_covert_enc_type_old(scan_entry->neg_sec_info.mc_enc); + bss->authType = + csr_covert_auth_type_old(scan_entry->neg_sec_info.auth_type); + bss->bss_score = scan_entry->bss_score; + + result_info = &bss->Result; + result_info->ssId.length = scan_entry->ssid.length; + qdf_mem_copy(result_info->ssId.ssId, + scan_entry->ssid.ssid, + result_info->ssId.length); + result_info->timer = scan_entry->hidden_ssid_timestamp; + + bss_desc = &result_info->BssDescriptor; + + bss_desc->length = (uint16_t) (offsetof(struct bss_description, + ieFields[0]) - sizeof(bss_desc->length) + ie_len); + + qdf_mem_copy(bss_desc->bssId, + scan_entry->bssid.bytes, + QDF_MAC_ADDR_SIZE); + bss_desc->scansystimensec = scan_entry->boottime_ns; + qdf_mem_copy(bss_desc->timeStamp, + scan_entry->tsf_info.data, 8); + + bss_desc->beaconInterval = scan_entry->bcn_int; + bss_desc->capabilityInfo = scan_entry->cap_info.value; + + if (WLAN_REG_IS_5GHZ_CH_FREQ(scan_entry->channel.chan_freq) || + WLAN_REG_IS_6GHZ_CHAN_FREQ(scan_entry->channel.chan_freq)) + bss_desc->nwType = eSIR_11A_NW_TYPE; + else if (scan_entry->phy_mode == WLAN_PHYMODE_11B) + bss_desc->nwType = eSIR_11B_NW_TYPE; + else + bss_desc->nwType = eSIR_11G_NW_TYPE; + + bss_desc->rssi = scan_entry->rssi_raw; + bss_desc->rssi_raw = scan_entry->rssi_raw; + + /* channel frequency what peer sent in beacon/probersp. */ + bss_desc->chan_freq = scan_entry->channel.chan_freq; + bss_desc->received_time = + scan_entry->scan_entry_time; + bss_desc->startTSF[0] = + mac_ctx->rrm.rrmPEContext.startTSF[0]; + bss_desc->startTSF[1] = + mac_ctx->rrm.rrmPEContext.startTSF[1]; + bss_desc->parentTSF = + scan_entry->rrm_parent_tsf; + bss_desc->fProbeRsp = (scan_entry->frm_subtype == + MGMT_SUBTYPE_PROBE_RESP); + bss_desc->seq_ctrl = hdr->seqControl; + bss_desc->tsf_delta = scan_entry->tsf_delta; + bss_desc->assoc_disallowed = csr_is_assoc_disallowed(mac_ctx, + scan_entry); + bss_desc->adaptive_11r_ap = scan_entry->adaptive_11r_ap; + + bss_desc->mbo_oce_enabled_ap = + util_scan_entry_mbo_oce(scan_entry) ? true : false; + + csr_fill_single_pmk_ap_cap_from_scan_entry(bss_desc, scan_entry); + + qdf_mem_copy(&bss_desc->mbssid_info, &scan_entry->mbssid_info, + sizeof(struct scan_mbssid_info)); + + qdf_mem_copy((uint8_t *) &bss_desc->ieFields, + ie_ptr, ie_len); + + status = csr_get_parsed_bss_description_ies(mac_ctx, + bss_desc, &bcn_ies); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(bss); + return status; + } + result_info->pvIes = bcn_ies; + + if (bcn_ies->MobilityDomain.present) { + bss_desc->mdiePresent = true; + qdf_mem_copy((uint8_t *)&(bss_desc->mdie[0]), + (uint8_t *)&(bcn_ies->MobilityDomain.MDID), + sizeof(uint16_t)); + bss_desc->mdie[2] = + ((bcn_ies->MobilityDomain.overDSCap << 0) | + (bcn_ies->MobilityDomain.resourceReqCap << 1)); + } +#ifdef FEATURE_WLAN_ESE + if (bcn_ies->QBSSLoad.present) { + bss_desc->QBSSLoad_present = true; + bss_desc->QBSSLoad_avail = + bcn_ies->QBSSLoad.avail; + } +#endif + csr_update_bss_with_fils_data(mac_ctx, scan_entry, bss_desc); + + *p_result = bss; + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS csr_parse_scan_list(struct mac_context *mac_ctx, + struct scan_result_list *ret_list, + qdf_list_t *scan_list) +{ + struct tag_csrscan_result *pResult = NULL; + struct scan_cache_node *cur_node = NULL; + struct scan_cache_node *next_node = NULL; + + qdf_list_peek_front(scan_list, (qdf_list_node_t **) &cur_node); + + while (cur_node) { + qdf_list_peek_next(scan_list, (qdf_list_node_t *) cur_node, + (qdf_list_node_t **) &next_node); + pResult = NULL; + csr_fill_bss_from_scan_entry(mac_ctx, + cur_node->entry, &pResult); + if (pResult) + csr_ll_insert_tail(&ret_list->List, &pResult->Link, + LL_ACCESS_NOLOCK); + cur_node = next_node; + next_node = NULL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_remove_ap_with_assoc_disallowed() - Remove APs with assoc + * disallowed bit set + * @mac_ctx: mac context + * @scan_list: candidate list for the connection + * + * Return: None + */ +static void csr_remove_ap_with_assoc_disallowed(struct mac_context *mac_ctx, + struct scan_result_list *scan_list) +{ + tListElem *cur_entry; + tListElem *next_entry; + struct tag_csrscan_result *scan_res; + + if (!scan_list) + return; + + cur_entry = csr_ll_peek_head(&scan_list->List, LL_ACCESS_NOLOCK); + while (cur_entry) { + scan_res = GET_BASE_ADDR(cur_entry, struct tag_csrscan_result, + Link); + next_entry = csr_ll_next(&scan_list->List, cur_entry, + LL_ACCESS_NOLOCK); + + if (!mac_ctx->ignore_assoc_disallowed && + scan_res->Result.BssDescriptor.assoc_disallowed) { + csr_ll_remove_entry(&scan_list->List, cur_entry, + LL_ACCESS_NOLOCK); + csr_free_scan_result_entry(mac_ctx, scan_res); + } + cur_entry = next_entry; + next_entry = NULL; + } +} + +QDF_STATUS csr_scan_get_result(struct mac_context *mac_ctx, + struct scan_filter *filter, + tScanResultHandle *results) +{ + QDF_STATUS status; + struct scan_result_list *ret_list = NULL; + qdf_list_t *list = NULL; + struct wlan_objmgr_pdev *pdev = NULL; + uint32_t num_bss = 0; + + if (results) + *results = CSR_INVALID_SCANRESULT_HANDLE; + + pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc, + 0, WLAN_LEGACY_MAC_ID); + if (!pdev) { + sme_err("pdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + list = ucfg_scan_get_result(pdev, filter); + if (list) { + num_bss = qdf_list_size(list); + sme_debug("num_entries %d", num_bss); + } + + /* Filter the scan list with the blacklist, rssi reject, avoided APs */ + if (filter && filter->bss_scoring_required) + wlan_blm_filter_bssid(pdev, list); + + if (!list || (list && !qdf_list_size(list))) { + sme_debug("scan list empty"); + if (num_bss) + status = QDF_STATUS_E_EXISTS; + else + status = QDF_STATUS_E_NULL_VALUE; + goto error; + } + + ret_list = qdf_mem_malloc(sizeof(struct scan_result_list)); + if (!ret_list) { + status = QDF_STATUS_E_NOMEM; + goto error; + } + + csr_ll_open(&ret_list->List); + ret_list->pCurEntry = NULL; + status = csr_parse_scan_list(mac_ctx, ret_list, list); + if (QDF_IS_STATUS_ERROR(status) || !results) + /* Fail or No one wants the result. */ + csr_scan_result_purge(mac_ctx, (tScanResultHandle) ret_list); + else { + if (filter && filter->bss_scoring_required) + csr_remove_ap_with_assoc_disallowed(mac_ctx, ret_list); + + if (!csr_ll_count(&ret_list->List)) { + /* This mean that there is no match */ + csr_ll_close(&ret_list->List); + qdf_mem_free(ret_list); + /* + * Do not trigger scan for ssid if the scan entries + * are removed either due to rssi reject or assoc + * disallowed. + */ + if (num_bss) + status = QDF_STATUS_E_EXISTS; + else + status = QDF_STATUS_E_NULL_VALUE; + } else if (results) { + *results = ret_list; + } + } + +error: + if (list) + ucfg_scan_purge_results(list); + if (pdev) + wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID); + + return status; +} + +QDF_STATUS csr_scan_get_result_for_bssid(struct mac_context *mac_ctx, + struct qdf_mac_addr *bssid, + tCsrScanResultInfo *res) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct scan_filter *scan_filter; + tScanResultHandle filtered_scan_result = NULL; + tCsrScanResultInfo *scan_result; + + if (!mac_ctx) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("mac_ctx is NULL")); + return QDF_STATUS_E_FAILURE; + } + + scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); + if (!scan_filter) + return QDF_STATUS_E_NOMEM; + + scan_filter->num_of_bssid = 1; + qdf_mem_copy(scan_filter->bssid_list[0].bytes, bssid->bytes, + QDF_MAC_ADDR_SIZE); + + status = csr_scan_get_result(mac_ctx, scan_filter, + &filtered_scan_result); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Failed to get scan result"); + goto free_filter; + } + + scan_result = csr_scan_result_get_first(mac_ctx, filtered_scan_result); + + if (scan_result) { + res->pvIes = NULL; + res->ssId.length = scan_result->ssId.length; + qdf_mem_copy(&res->ssId.ssId, &scan_result->ssId.ssId, + res->ssId.length); + res->timer = scan_result->timer; + qdf_mem_copy(&res->BssDescriptor, &scan_result->BssDescriptor, + sizeof(struct bss_description)); + status = QDF_STATUS_SUCCESS; + } else { + status = QDF_STATUS_E_FAILURE; + } + + csr_scan_result_purge(mac_ctx, filtered_scan_result); + +free_filter: + if (scan_filter) + qdf_mem_free(scan_filter); + + return status; +} + +static inline QDF_STATUS +csr_flush_scan_results(struct mac_context *mac_ctx, + struct scan_filter *filter) +{ + struct wlan_objmgr_pdev *pdev = NULL; + QDF_STATUS status; + + pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc, + 0, WLAN_LEGACY_MAC_ID); + if (!pdev) { + sme_err("pdev is NULL"); + return QDF_STATUS_E_INVAL; + } + status = ucfg_scan_flush_results(pdev, filter); + + wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID); + return status; +} + +static inline void csr_flush_bssid(struct mac_context *mac_ctx, + uint8_t *bssid) +{ + struct scan_filter *filter; + + filter = qdf_mem_malloc(sizeof(*filter)); + if (!filter) + return; + + filter->num_of_bssid = 1; + qdf_mem_copy(filter->bssid_list[0].bytes, + bssid, QDF_MAC_ADDR_SIZE); + + csr_flush_scan_results(mac_ctx, filter); + sme_debug("Removed BSS entry:"QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(bssid)); + if (filter) + qdf_mem_free(filter); +} + +void csr_remove_bssid_from_scan_list(struct mac_context *mac_ctx, + tSirMacAddr bssid) +{ + csr_flush_bssid(mac_ctx, bssid); +} + +static void csr_dump_occupied_chan_list(struct csr_channel *occupied_ch) +{ + uint8_t idx; + uint32_t buff_len; + char *chan_buff; + uint32_t len = 0; + + buff_len = (occupied_ch->numChannels * 5) + 1; + chan_buff = qdf_mem_malloc(buff_len); + if (!chan_buff) + return; + + for (idx = 0; idx < occupied_ch->numChannels; idx++) + len += qdf_scnprintf(chan_buff + len, buff_len - len, " %d", + occupied_ch->channel_freq_list[idx]); + + sme_nofl_debug("Occupied chan list[%d]:%s", + occupied_ch->numChannels, chan_buff); + + qdf_mem_free(chan_buff); +} +void csr_init_occupied_channels_list(struct mac_context *mac_ctx, + uint8_t sessionId) +{ + qdf_list_t *list = NULL; + struct wlan_objmgr_pdev *pdev = NULL; + qdf_list_node_t *cur_lst = NULL; + qdf_list_node_t *next_lst = NULL; + struct scan_cache_node *cur_node = NULL; + struct scan_filter *filter; + tpCsrNeighborRoamControlInfo neighbor_roam_info = + &mac_ctx->roam.neighborRoamInfo[sessionId]; + tCsrRoamConnectedProfile *profile = NULL; + + if (!(mac_ctx && mac_ctx->roam.roamSession && + CSR_IS_SESSION_VALID(mac_ctx, sessionId))) { + sme_debug("Invalid session"); + return; + } + if (neighbor_roam_info->cfgParams.specific_chan_info.numOfChannels) { + /* + * Ini file contains neighbor scan channel list, hence NO need + * to build occupied channel list" + */ + sme_debug("Ini contains neighbor scan ch list"); + return; + } + + profile = &mac_ctx->roam.roamSession[sessionId].connectedProfile; + if (!profile) + return; + + filter = qdf_mem_malloc(sizeof(*filter)); + if (!filter) { + sme_err("filter is NULL"); + return; + } + + filter->num_of_auth = 1; + filter->auth_type[0] = csr_covert_auth_type_new(profile->AuthType); + filter->num_of_enc_type = 1; + filter->enc_type[0] = csr_covert_enc_type_new(profile->EncryptionType); + filter->num_of_mc_enc_type = 1; + filter->mc_enc_type[0] = + csr_covert_enc_type_new(profile->mcEncryptionType); + filter->num_of_ssid = 1; + filter->ssid_list[0].length = profile->SSID.length; + qdf_mem_copy(filter->ssid_list[0].ssid, profile->SSID.ssId, + profile->SSID.length); + csr_update_pmf_cap_from_connected_profile(profile, filter); + + pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc, 0, WLAN_LEGACY_MAC_ID); + + if (!pdev) { + sme_err("pdev is NULL"); + qdf_mem_free(filter); + return; + } + + /* Empty occupied channels here */ + mac_ctx->scan.occupiedChannels[sessionId].numChannels = 0; + mac_ctx->scan.roam_candidate_count[sessionId] = 0; + + csr_add_to_occupied_channels( + mac_ctx, + profile->op_freq, + sessionId, + &mac_ctx->scan.occupiedChannels[sessionId], + true); + list = ucfg_scan_get_result(pdev, filter); + if (list) + sme_debug("num_entries %d", qdf_list_size(list)); + if (!list || (list && !qdf_list_size(list))) { + wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID); + qdf_mem_free(filter); + if (list) + ucfg_scan_purge_results(list); + csr_dump_occupied_chan_list( + &mac_ctx->scan.occupiedChannels[sessionId]); + return; + } + + qdf_list_peek_front(list, &cur_lst); + while (cur_lst) { + cur_node = qdf_container_of(cur_lst, struct scan_cache_node, + node); + csr_add_to_occupied_channels( + mac_ctx, cur_node->entry->channel.chan_freq, + sessionId, + &mac_ctx->scan.occupiedChannels[sessionId], + true); + qdf_list_peek_next(list, cur_lst, &next_lst); + cur_lst = next_lst; + next_lst = NULL; + } + csr_dump_occupied_chan_list(&mac_ctx->scan.occupiedChannels[sessionId]); + + qdf_mem_free(filter); + ucfg_scan_purge_results(list); + wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID); +} + +/** + * csr_scan_filter_results: filter scan result based + * on valid channel list number. + * @mac_ctx: mac context + * + * Get scan result from scan list and Check Scan result channel number + * with 11d channel list if channel number is found in 11d channel list + * then do not remove scan result entry from scan list + * + * return: QDF Status + */ +QDF_STATUS csr_scan_filter_results(struct mac_context *mac_ctx) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t len = sizeof(mac_ctx->roam.valid_ch_freq_list); + struct wlan_objmgr_pdev *pdev = NULL; + uint32_t i; + uint32_t ch_freq; + uint32_t valid_ch_freq_list[CFG_VALID_CHANNEL_LIST_LEN]; + + pdev = wlan_objmgr_get_pdev_by_id(mac_ctx->psoc, + 0, WLAN_LEGACY_MAC_ID); + if (!pdev) { + sme_err("pdev is NULL"); + return QDF_STATUS_E_INVAL; + } + status = csr_get_cfg_valid_channels(mac_ctx, + mac_ctx->roam.valid_ch_freq_list, + &len); + + /* Get valid channels list from CFG */ + if (QDF_IS_STATUS_ERROR(status)) { + wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID); + sme_err("Failed to get Channel list from CFG"); + return status; + } + sme_debug("No of valid channel %d", len); + + /* This is a temporary conversion till the scm handles freq */ + + for (i = 0; i < len; i++) { + ch_freq = mac_ctx->roam.valid_ch_freq_list[i]; + valid_ch_freq_list[i] = ch_freq; + } + + ucfg_scan_filter_valid_channel(pdev, valid_ch_freq_list, len); + + wlan_objmgr_pdev_release_ref(pdev, WLAN_LEGACY_MAC_ID); + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_cmd_process.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_cmd_process.c new file mode 100644 index 0000000000000000000000000000000000000000..b4c217f844f155df8e3207a75510f6a52b11b9ce --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_cmd_process.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: csr_cmd_process.c + * + * Implementation for processing various commands. + */ +#include "ani_global.h" +#include "csr_inside_api.h" +#include "sme_inside.h" +#include "mac_trace.h" + +/** + * csr_msg_processor() - To process all csr msg + * @mac_ctx: mac context + * @msg_buf: message buffer + * + * This routine will handle all the message for csr to process + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_msg_processor(struct mac_context *mac_ctx, void *msg_buf) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSirSmeRsp *sme_rsp = (tSirSmeRsp *) msg_buf; + uint8_t vdev_id = sme_rsp->vdev_id; + enum csr_roam_state cur_state; + + cur_state = sme_get_current_roam_state(MAC_HANDLE(mac_ctx), vdev_id); + sme_debug("msg %d[0x%04X] recvd in curstate %s & substate %s id(%d)", + sme_rsp->messageType, sme_rsp->messageType, + mac_trace_getcsr_roam_state(cur_state), + mac_trace_getcsr_roam_sub_state( + mac_ctx->roam.curSubState[vdev_id]), vdev_id); + + /* Process the message based on the state of the roaming states... */ + switch (cur_state) { + case eCSR_ROAMING_STATE_JOINED: + /* are we in joined state */ + csr_roam_joined_state_msg_processor(mac_ctx, msg_buf); + break; + case eCSR_ROAMING_STATE_JOINING: + /* are we in roaming states */ + csr_roaming_state_msg_processor(mac_ctx, msg_buf); + break; + + default: + + if (sme_rsp->messageType == + eWNI_SME_UPPER_LAYER_ASSOC_CNF) { + tSirSmeAssocIndToUpperLayerCnf *upper_layer_assoc_cnf = + (tSirSmeAssocIndToUpperLayerCnf *)msg_buf; + if (upper_layer_assoc_cnf->ies) { + qdf_mem_free(upper_layer_assoc_cnf->ies); + sme_debug("free ies"); + } + break; + } + + /* + * For all other messages, we ignore it + * To work-around an issue where checking for set/remove + * key base on connection state is no longer workable + * due to failure or finding the condition meets both + * SAP and infra/IBSS requirement. + */ + if (eWNI_SME_SETCONTEXT_RSP == sme_rsp->messageType || + eWNI_SME_DISCONNECT_DONE_IND == + sme_rsp->messageType) { + sme_warn("handling msg 0x%X CSR state is %d", + sme_rsp->messageType, cur_state); + csr_roam_check_for_link_status_change(mac_ctx, + sme_rsp); + } else { + sme_err("Message 0x%04X is not handled by CSR state is %d session Id %d", + sme_rsp->messageType, cur_state, + vdev_id); + + if (eWNI_SME_FT_PRE_AUTH_RSP == + sme_rsp->messageType) { + sme_err("Dequeue eSmeCommandRoam command with reason eCsrPerformPreauth"); + csr_dequeue_roam_command(mac_ctx, + eCsrPerformPreauth, vdev_id); + } else if (eWNI_SME_REASSOC_RSP == + sme_rsp->messageType) { + sme_err("Dequeue eSmeCommandRoam command with reason eCsrSmeIssuedFTReassoc"); + csr_dequeue_roam_command(mac_ctx, + eCsrSmeIssuedFTReassoc, + vdev_id); + } + } + break; + } /* switch */ + return status; +} diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_host_scan_roam.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_host_scan_roam.c new file mode 100644 index 0000000000000000000000000000000000000000..aeff8c70dd5de4e22c8a6f395e75121cae8160be --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_host_scan_roam.c @@ -0,0 +1,675 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: csr_host_scan_roam.c + * + * Host based roaming processing scan results and initiating the roaming + */ + +#include "wma_types.h" +#include "csr_inside_api.h" +#include "sme_qos_internal.h" +#include "sme_inside.h" +#include "host_diag_core_event.h" +#include "host_diag_core_log.h" +#include "csr_api.h" +#include "sme_api.h" +#include "csr_neighbor_roam.h" +#include "mac_trace.h" +#include "wlan_policy_mgr_api.h" + +QDF_STATUS csr_roam_issue_reassociate(struct mac_context *mac, uint32_t vdev_id, + struct bss_description *bss_desc, + tDot11fBeaconIEs *ies, + struct csr_roam_profile *roam_profile) +{ + csr_roam_state_change(mac, eCSR_ROAMING_STATE_JOINING, vdev_id); + /* Set the roaming substate to 'join attempt'... */ + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_REASSOC_REQ, vdev_id); + sme_debug("calling csr_send_join_req_msg (eWNI_SME_REASSOC_REQ)"); + /* attempt to Join this BSS... */ + return csr_send_join_req_msg(mac, vdev_id, bss_desc, roam_profile, ies, + eWNI_SME_REASSOC_REQ); +} + +QDF_STATUS +csr_roam_issue_reassociate_cmd(struct mac_context *mac, uint32_t sessionId) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSmeCmd *pCommand = NULL; + bool fHighPriority = true; + bool fRemoveCmd = false; + tListElem *pEntry; + tSmeCmd *tmp_command; + + pEntry = csr_nonscan_active_ll_peek_head(mac, LL_ACCESS_LOCK); + if (pEntry) { + pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); + if (!pCommand) { + sme_err("fail to get command buffer"); + return QDF_STATUS_E_RESOURCES; + } + if (eSmeCommandRoam == pCommand->command) { + if (pCommand->u.roamCmd.roamReason == + eCsrSmeIssuedAssocToSimilarAP) + fRemoveCmd = + csr_nonscan_active_ll_remove_entry(mac, + pEntry, + LL_ACCESS_LOCK); + else + sme_err("Unexpected roam cmd present"); + if (fRemoveCmd == false) + pCommand = NULL; + } + } + if (!pCommand) { + sme_err("fail to get cmd buf based on prev roam command"); + return QDF_STATUS_E_RESOURCES; + } + do { + /* + * Get a new sme command to save the necessary info for + * the following roaming process, such as BSS list and + * roam profile. Or those info will be freed in function + * csr_reinit_roam_cmd when releasing the current command. + */ + tmp_command = csr_get_command_buffer(mac); + if (!tmp_command) { + sme_err("fail to get cmd buf!"); + csr_release_command(mac, pCommand); + return QDF_STATUS_E_RESOURCES; + } + qdf_mem_copy(tmp_command, pCommand, sizeof(*pCommand)); + pCommand->u.roamCmd.fReleaseBssList = false; + pCommand->u.roamCmd.hBSSList = CSR_INVALID_SCANRESULT_HANDLE; + pCommand->u.roamCmd.fReleaseProfile = false; + /* + * Invoking csr_release_command to release the current command + * or the following command will be stuck in pending queue. + * Because the API csr_nonscan_active_ll_remove_entry does + * not remove the current command from active queue. + */ + csr_release_command(mac, pCommand); + + pCommand = tmp_command; + /* Change the substate in case it is wait-for-key */ + if (CSR_IS_WAIT_FOR_KEY(mac, sessionId)) { + csr_roam_stop_wait_for_key_timer(mac); + csr_roam_substate_change(mac, eCSR_ROAM_SUBSTATE_NONE, + sessionId); + } + pCommand->command = eSmeCommandRoam; + pCommand->vdev_id = (uint8_t) sessionId; + pCommand->u.roamCmd.roamReason = eCsrSmeIssuedFTReassoc; + status = csr_queue_sme_command(mac, pCommand, fHighPriority); + if (!QDF_IS_STATUS_SUCCESS(status)) + sme_err("fail to send message status: %d", status); + } while (0); + + return status; +} + +void csr_neighbor_roam_process_scan_results(struct mac_context *mac_ctx, + uint8_t sessionid, tScanResultHandle *scan_results_list) +{ + tCsrScanResultInfo *scan_result; + tpCsrNeighborRoamControlInfo n_roam_info = + &mac_ctx->roam.neighborRoamInfo[sessionid]; + tpCsrNeighborRoamBSSInfo bss_info; + uint64_t age = 0; + uint32_t bss_chan_freq; + uint8_t num_candidates = 0; + uint8_t num_dropped = 0; + /* + * first iteration of scan list should consider + * age constraint for candidates + */ + bool age_constraint = true; +#ifdef FEATURE_WLAN_ESE + uint16_t qpresent; + uint16_t qavail; + bool voadmitted; +#endif + /* + * Expecting the scan result already to be in the sorted order based on + * RSSI. Based on the previous state we need to check whether the list + * should be sorted again taking neighbor score into consideration. If + * previous state is CFG_CHAN_LIST_SCAN, there should not be a neighbor + * score associated with any of the BSS. If the previous state is + * REPORT_QUERY, then there will be neighbor score for each of the APs. + * For now, let us take top of the list provided as it is by CSR Scan + * result API. Hence it is assumed that neighbor score and rssi score + * are in the same order. This will be taken care later. + */ + + do { + while (true) { + struct bss_description *descr; + + scan_result = csr_scan_result_get_next( + mac_ctx, *scan_results_list); + if (!scan_result) + break; + descr = &scan_result->BssDescriptor; + bss_chan_freq = descr->chan_freq; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Scan result: BSSID " QDF_MAC_ADDR_FMT + " (Rssi %d, Ch:%d)"), + QDF_MAC_ADDR_REF(descr->bssId), + (int)abs(descr->rssi), descr->chan_freq); + + if (!qdf_mem_cmp(descr->bssId, + n_roam_info->currAPbssid.bytes, + sizeof(tSirMacAddr))) { + /* + * currently associated AP. Do not have this + * in the roamable AP list + */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "SKIP-currently associated AP"); + continue; + } + + /* + * Continue if MCC is disabled in INI and if AP + * will create MCC + */ + if (policy_mgr_concurrent_open_sessions_running( + mac_ctx->psoc) && + !mac_ctx->roam.configParam.fenableMCCMode) { + uint32_t conc_freq; + + conc_freq = + csr_get_concurrent_operation_freq(mac_ctx); + if (conc_freq && + (conc_freq != bss_chan_freq)) { + sme_debug("MCC not supported so Ignore AP on channel %d", + descr->chan_freq); + continue; + } + } + /* + * In case of reassoc requested by upper layer, look + * for exact match of bssid & channel. csr cache might + * have duplicates + */ + if ((n_roam_info->uOsRequestedHandoff) && + ((qdf_mem_cmp( + descr->bssId, + n_roam_info->handoffReqInfo.bssid.bytes, + sizeof(tSirMacAddr))) || + (descr->chan_freq != + n_roam_info->handoffReqInfo.ch_freq))) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "SKIP-not a candidate AP for OS requested roam"); + continue; + } + + if ((n_roam_info->is11rAssoc) && + (!csr_neighbor_roam_is_preauth_candidate(mac_ctx, + sessionid, descr->bssId))) { + sme_err("BSSID in preauth fail list. Ignore"); + continue; + } + +#ifdef FEATURE_WLAN_ESE + if (!csr_roam_is_roam_offload_scan_enabled(mac_ctx) && + (n_roam_info->isESEAssoc) && + !csr_neighbor_roam_is_preauth_candidate(mac_ctx, + sessionid, descr->bssId)) { + sme_err("BSSID in preauth faillist. Ignore"); + continue; + } + + qpresent = descr->QBSSLoad_present; + qavail = descr->QBSSLoad_avail; + voadmitted = n_roam_info->isVOAdmitted; + if (voadmitted) + sme_debug("New QBSS=%s,BWavail=0x%x,req=0x%x", + ((qpresent) ? "yes" : "no"), qavail, + n_roam_info->MinQBssLoadRequired); + if (voadmitted && qpresent && + (qavail < n_roam_info->MinQBssLoadRequired)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "BSSID:" QDF_MAC_ADDR_FMT "has no BW", + QDF_MAC_ADDR_REF(descr->bssId)); + continue; + } + if (voadmitted && !qpresent) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "BSSID:" QDF_MAC_ADDR_FMT "no LOAD IE", + QDF_MAC_ADDR_REF(descr->bssId)); + continue; + } +#endif /* FEATURE_WLAN_ESE */ + + /* + * If we are supporting legacy roaming, and + * if the candidate is on the "pre-auth failed" list, + * ignore it. + */ + if (csr_roam_is_fast_roam_enabled(mac_ctx, sessionid) && + !csr_neighbor_roam_is_preauth_candidate(mac_ctx, + sessionid, descr->bssId)) { + sme_err("BSSID in preauth faillist Ignore"); + continue; + } + + /* check the age of the AP */ + age = (uint64_t) qdf_mc_timer_get_system_time() - + descr->received_time; + if (age_constraint == true && + age > ROAM_AP_AGE_LIMIT_MS) { + num_dropped++; + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_WARN, + FL("Old AP (probe rsp/beacon) skipped.") + ); + continue; + } + + /* Finished all checks, now add it to candidate list */ + bss_info = + qdf_mem_malloc(sizeof(tCsrNeighborRoamBSSInfo)); + if (!bss_info) + continue; + + bss_info->pBssDescription = + qdf_mem_malloc(descr->length + + sizeof(descr->length)); + if (bss_info->pBssDescription) { + qdf_mem_copy(bss_info->pBssDescription, descr, + descr->length + sizeof(descr->length)); + } else { + qdf_mem_free(bss_info); + continue; + } + /* + * Assign some preference value for now. Need to + * calculate theactual score based on RSSI and neighbor + * AP score + */ + bss_info->apPreferenceVal = 10; + num_candidates++; + csr_ll_insert_tail(&n_roam_info->roamableAPList, + &bss_info->List, LL_ACCESS_LOCK); + } /* end of while (csr_scan_result_get_next) */ + + /* if some candidates were found, then no need to repeat */ + if (num_candidates) + break; + /* + * if age_constraint is already false, we have done two + * iterations and no candidate were found + */ + if (age_constraint == false) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: No roam able candidates found", + __func__); + break; + } + /* + * if all candidates were dropped rescan the scan + * list but this time without age constraint. + */ + age_constraint = false; + /* if no candidates were dropped no need to repeat */ + } while (num_dropped); + + /* + * Now we have all the scan results in our local list. Good time to free + * up the the list we got as a part of csrGetScanResult + */ + csr_scan_result_purge(mac_ctx, *scan_results_list); +} + +void csr_neighbor_roam_trigger_handoff(struct mac_context *mac_ctx, + uint8_t vdev_id) +{ + if (csr_roam_is_fast_roam_enabled(mac_ctx, vdev_id)) + csr_neighbor_roam_issue_preauth_req(mac_ctx, vdev_id); + else + sme_err("Roaming is disabled"); +} + +QDF_STATUS csr_neighbor_roam_process_scan_complete(struct mac_context *mac, + uint8_t sessionId) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + struct scan_filter *filter; + tScanResultHandle scanResult; + uint32_t tempVal = 0; + QDF_STATUS hstatus; + + filter = qdf_mem_malloc(sizeof(*filter)); + if (!filter) + return QDF_STATUS_E_NOMEM; + + hstatus = csr_neighbor_roam_get_scan_filter_from_profile(mac, + filter, + sessionId); + sme_debug("Prepare scan to find neighbor AP filter status: %d", + hstatus); + if (QDF_STATUS_SUCCESS != hstatus) { + sme_err("Scan Filter prep fail for Assoc %d Bail out", + tempVal); + qdf_mem_free(filter); + return QDF_STATUS_E_FAILURE; + } + hstatus = csr_scan_get_result(mac, filter, &scanResult); + qdf_mem_free(filter); + if (hstatus != QDF_STATUS_SUCCESS) + sme_err("Get Scan Result status code %d", hstatus); + /* Process the scan results and update roamable AP list */ + csr_neighbor_roam_process_scan_results(mac, sessionId, &scanResult); + + tempVal = csr_ll_count(&pNeighborRoamInfo->roamableAPList); + + if (tempVal) { + csr_neighbor_roam_trigger_handoff(mac, sessionId); + return QDF_STATUS_SUCCESS; + } + + if (csr_roam_is_roam_offload_scan_enabled(mac)) { + if (pNeighborRoamInfo->uOsRequestedHandoff) { + csr_roam_offload_scan(mac, sessionId, + ROAM_SCAN_OFFLOAD_START, + REASON_NO_CAND_FOUND_OR_NOT_ROAMING_NOW); + pNeighborRoamInfo->uOsRequestedHandoff = 0; + } else { + /* There is no candidate or We are not roaming Now. + * Inform the FW to restart Roam Offload Scan + */ + csr_roam_offload_scan(mac, sessionId, + ROAM_SCAN_OFFLOAD_RESTART, + REASON_NO_CAND_FOUND_OR_NOT_ROAMING_NOW); + } + csr_neighbor_roam_state_transition(mac, + eCSR_NEIGHBOR_ROAM_STATE_CONNECTED, sessionId); + } + return QDF_STATUS_SUCCESS; + +} + +QDF_STATUS csr_neighbor_roam_candidate_found_ind_hdlr(struct mac_context *mac, + void *pMsg) +{ + tSirSmeCandidateFoundInd *pSirSmeCandidateFoundInd = + (tSirSmeCandidateFoundInd *) pMsg; + uint32_t sessionId = pSirSmeCandidateFoundInd->sessionId; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* we must be in connected state, if not ignore it */ + if ((eCSR_NEIGHBOR_ROAM_STATE_CONNECTED != + pNeighborRoamInfo->neighborRoamState) + || (pNeighborRoamInfo->uOsRequestedHandoff)) { + sme_err("Recvd in NotCONNECTED or OsReqHandoff. Ignore"); + status = QDF_STATUS_E_FAILURE; + } else { + /* Future enhancements: + * If firmware tags candidate beacons, give them preference + * for roaming. + * Age out older entries so that new candidate beacons + * will get preference. + */ + status = csr_neighbor_roam_process_scan_complete(mac, + sessionId); + if (QDF_STATUS_SUCCESS != status) { + sme_err("scan process complete failed, status %d", + status); + return QDF_STATUS_E_FAILURE; + } + } + + return status; +} + +void csr_neighbor_roam_free_roamable_bss_list(struct mac_context *mac_ctx, + tDblLinkList *llist) +{ + tpCsrNeighborRoamBSSInfo result = NULL; + + /* + * Pick up the head, remove and free the node till + * the list becomes empty + */ + while ((result = csr_neighbor_roam_next_roamable_ap(mac_ctx, llist, + NULL)) != NULL) { + csr_neighbor_roam_remove_roamable_ap_list_entry(mac_ctx, + llist, result); + csr_neighbor_roam_free_neighbor_roam_bss_node(mac_ctx, result); + } +} + +bool csr_neighbor_roam_remove_roamable_ap_list_entry(struct mac_context *mac, + tDblLinkList *pList, + tpCsrNeighborRoamBSSInfo + pNeighborEntry) +{ + if (pList) + return csr_ll_remove_entry(pList, &pNeighborEntry->List, + LL_ACCESS_LOCK); + + sme_debug("Remove neigh BSS node from fail list. Current count: %d", + csr_ll_count(pList)); + + return false; +} + +tpCsrNeighborRoamBSSInfo csr_neighbor_roam_next_roamable_ap( + struct mac_context *mac_ctx, tDblLinkList *llist, + tpCsrNeighborRoamBSSInfo neighbor_entry) +{ + tListElem *entry = NULL; + tpCsrNeighborRoamBSSInfo result = NULL; + + if (llist) { + if (!neighbor_entry) + entry = csr_ll_peek_head(llist, LL_ACCESS_LOCK); + else + entry = csr_ll_next(llist, &neighbor_entry->List, + LL_ACCESS_LOCK); + if (entry) + result = GET_BASE_ADDR(entry, tCsrNeighborRoamBSSInfo, + List); + } + + return result; +} + +void csr_neighbor_roam_request_handoff(struct mac_context *mac_ctx, + uint8_t session_id) +{ + struct csr_roam_info *roam_info; + tpCsrNeighborRoamControlInfo neighbor_roam_info = + &mac_ctx->roam.neighborRoamInfo[session_id]; + tCsrNeighborRoamBSSInfo handoff_node; + uint32_t roamid = 0; + QDF_STATUS status; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, "%s session_id=%d", + __func__, session_id); + + if (neighbor_roam_info->neighborRoamState != + eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE) { + sme_err("Roam requested when Neighbor roam is in %s state", + mac_trace_get_neighbour_roam_state( + neighbor_roam_info->neighborRoamState)); + return; + } + + if (false == csr_neighbor_roam_get_handoff_ap_info(mac_ctx, + &handoff_node, session_id)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("failed to obtain handoff AP")); + return; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("HANDOFF CANDIDATE BSSID "QDF_MAC_ADDR_FMT), + QDF_MAC_ADDR_REF(handoff_node.pBssDescription->bssId)); + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + csr_roam_call_callback(mac_ctx, session_id, roam_info, roamid, + eCSR_ROAM_FT_START, eCSR_ROAM_RESULT_SUCCESS); + + qdf_mem_zero(roam_info, sizeof(*roam_info)); + csr_neighbor_roam_state_transition(mac_ctx, + eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING, session_id); + + csr_neighbor_roam_send_lfr_metric_event(mac_ctx, session_id, + handoff_node.pBssDescription->bssId, + eCSR_ROAM_HANDOVER_SUCCESS); + /* Free the profile.. Just to make sure we dont leak memory here */ + csr_release_profile(mac_ctx, + &neighbor_roam_info->csrNeighborRoamProfile); + /* + * Create the Handoff AP profile. Copy the currently connected profile + * and update only the BSSID and channel number. This should happen + * before issuing disconnect + */ + status = csr_roam_copy_connected_profile(mac_ctx, session_id, + &neighbor_roam_info->csrNeighborRoamProfile); + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("csr_roam_copy_connected_profile failed %d"), + status); + qdf_mem_free(roam_info); + return; + } + qdf_mem_copy(neighbor_roam_info->csrNeighborRoamProfile.BSSIDs.bssid, + handoff_node.pBssDescription->bssId, sizeof(tSirMacAddr)); + neighbor_roam_info->csrNeighborRoamProfile.ChannelInfo.freq_list[0] = + handoff_node.pBssDescription->chan_freq; + + sme_debug("csr_roamHandoffRequested: disassociating with current AP"); + + if (!QDF_IS_STATUS_SUCCESS(csr_roam_issue_disassociate_cmd( + mac_ctx, + session_id, + eCSR_DISCONNECT_REASON_HANDOFF, + eSIR_MAC_UNSPEC_FAILURE_REASON))) { + sme_warn("csr_roamHandoffRequested: fail to issue disassoc"); + qdf_mem_free(roam_info); + return; + } + /* notify HDD for handoff, providing the BSSID too */ + roam_info->reasonCode = eCsrRoamReasonBetterAP; + + qdf_mem_copy(roam_info->bssid.bytes, + handoff_node.pBssDescription->bssId, + sizeof(struct qdf_mac_addr)); + + csr_roam_call_callback(mac_ctx, session_id, roam_info, 0, + eCSR_ROAM_ROAMING_START, eCSR_ROAM_RESULT_NONE); + qdf_mem_free(roam_info); + +} + +bool +csr_neighbor_roam_get_handoff_ap_info(struct mac_context *mac, + tpCsrNeighborRoamBSSInfo hand_off_node, + uint8_t session_id) +{ + tpCsrNeighborRoamControlInfo ngbr_roam_info = + &mac->roam.neighborRoamInfo[session_id]; + tpCsrNeighborRoamBSSInfo bss_node = NULL; + + if (!hand_off_node) { + QDF_ASSERT(hand_off_node); + return false; + } + if (ngbr_roam_info->is11rAssoc) { + /* Always the BSS info in the head is the handoff candidate */ + bss_node = csr_neighbor_roam_next_roamable_ap( + mac, + &ngbr_roam_info->FTRoamInfo.preAuthDoneList, + NULL); + sme_debug("Number of Handoff candidates: %d", + csr_ll_count(& + ngbr_roam_info->FTRoamInfo.preAuthDoneList)); + } else +#ifdef FEATURE_WLAN_ESE + if (ngbr_roam_info->isESEAssoc) { + /* Always the BSS info in the head is the handoff candidate */ + bss_node = + csr_neighbor_roam_next_roamable_ap(mac, + &ngbr_roam_info->FTRoamInfo.preAuthDoneList, + NULL); + sme_debug("Number of Handoff candidates: %d", + csr_ll_count(&ngbr_roam_info->FTRoamInfo. + preAuthDoneList)); + } else +#endif + if (csr_roam_is_fast_roam_enabled(mac, session_id)) { + /* Always the BSS info in the head is the handoff candidate */ + bss_node = + csr_neighbor_roam_next_roamable_ap(mac, + &ngbr_roam_info->FTRoamInfo.preAuthDoneList, + NULL); + sme_debug("Number of Handoff candidates: %d", + csr_ll_count( + &ngbr_roam_info->FTRoamInfo.preAuthDoneList)); + } else { + bss_node = + csr_neighbor_roam_next_roamable_ap(mac, + &ngbr_roam_info->roamableAPList, + NULL); + sme_debug("Number of Handoff candidates: %d", + csr_ll_count(&ngbr_roam_info->roamableAPList)); + } + if (!bss_node) + return false; + qdf_mem_copy(hand_off_node, bss_node, sizeof(tCsrNeighborRoamBSSInfo)); + return true; +} + +bool csr_neighbor_roam_is_handoff_in_progress(struct mac_context *mac, + uint8_t sessionId) +{ + if (eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING == + mac->roam.neighborRoamInfo[sessionId].neighborRoamState) + return true; + + return false; +} + +void csr_neighbor_roam_free_neighbor_roam_bss_node(struct mac_context *mac, + tpCsrNeighborRoamBSSInfo + neighborRoamBSSNode) +{ + if (!neighborRoamBSSNode) + return; + + if (neighborRoamBSSNode->pBssDescription) { + qdf_mem_free(neighborRoamBSSNode->pBssDescription); + neighborRoamBSSNode->pBssDescription = NULL; + } + qdf_mem_free(neighborRoamBSSNode); + neighborRoamBSSNode = NULL; +} + diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_inside_api.h b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_inside_api.h new file mode 100644 index 0000000000000000000000000000000000000000..070e489bbac502e16ee5a31203c6571d864ae25c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_inside_api.h @@ -0,0 +1,1195 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: csr_inside_api.h + * + * Define interface only used by CSR. + */ +#ifndef CSR_INSIDE_API_H__ +#define CSR_INSIDE_API_H__ + +#include "csr_support.h" +#include "sme_inside.h" +#include "cds_reg_service.h" +#include "wlan_objmgr_vdev_obj.h" + +/* This number minus 1 means the number of times a channel is scanned before + * a BSS is removed from cache scan result + */ +#define CSR_AGING_COUNT 3 + +/* These are going against the signed RSSI (int8_t) so it is between -+127 */ +#define CSR_BEST_RSSI_VALUE (-30) /* RSSI >= this is in CAT4 */ +#define CSR_DEFAULT_RSSI_DB_GAP 30 /* every 30 dbm for one category */ + +#ifdef QCA_WIFI_3_0_EMU +#define CSR_ACTIVE_SCAN_LIST_CMD_TIMEOUT (1000*30*20) +#else +#define CSR_ACTIVE_SCAN_LIST_CMD_TIMEOUT (1000*30) +#endif +/* *************************************************************************** + * The MAX BSSID Count should be lower than the command timeout value. + * As in some case auth timeout can take upto 5 sec (in case of SAE auth) try + * (command timeout/5000 - 1) candidates. + * ***************************************************************************/ +#define CSR_MAX_BSSID_COUNT (SME_ACTIVE_LIST_CMD_TIMEOUT_VALUE/5000) - 1 +#define CSR_CUSTOM_CONC_GO_BI 100 +extern uint8_t csr_wpa_oui[][CSR_WPA_OUI_SIZE]; +bool csr_is_supported_channel(struct mac_context *mac, uint32_t chan_freq); + +enum csr_scancomplete_nextcommand { + eCsrNextScanNothing, + eCsrNexteScanForSsidSuccess, + eCsrNexteScanForSsidFailure, + eCsrNextCheckAllowConc, +}; + +enum csr_roamcomplete_result { + eCsrJoinSuccess, + eCsrJoinFailure, + eCsrReassocSuccess, + eCsrReassocFailure, + eCsrNothingToJoin, + eCsrStartBssSuccess, + eCsrStartBssFailure, + eCsrSilentlyStopRoaming, + eCsrSilentlyStopRoamingSaveState, + eCsrJoinFailureDueToConcurrency, + eCsrStopBssSuccess, + eCsrStopBssFailure, +}; + +struct tag_csrscan_result { + tListElem Link; + /* This BSS is removed when it reaches 0 or less */ + int32_t AgingCount; + /* The bigger the number, the better the BSS. + * This value override capValue + */ + uint32_t preferValue; + /* The biggger the better. This value is in use only if we have + * equal preferValue + */ + uint32_t capValue; + /* This member must be the last in the structure because the end of + * struct bss_description (inside) is an + * array with nonknown size at this time + */ + /* Preferred Encryption type that matched with profile. */ + eCsrEncryptionType ucEncryptionType; + eCsrEncryptionType mcEncryptionType; + /* Preferred auth type that matched with the profile. */ + enum csr_akm_type authType; + int bss_score; + uint8_t retry_count; + + tCsrScanResultInfo Result; + /* + * WARNING - Do not add any element here + * This member Result must be the last in the structure because the end + * of struct bss_description (inside) is an array with nonknown size at + * this time. + */ +}; + +struct scan_result_list { + tDblLinkList List; + tListElem *pCurEntry; +}; + +#define CSR_IS_ENC_TYPE_STATIC(encType) ((eCSR_ENCRYPT_TYPE_NONE == (encType)) \ + || (eCSR_ENCRYPT_TYPE_WEP40_STATICKEY == (encType)) || \ + (eCSR_ENCRYPT_TYPE_WEP104_STATICKEY == (encType))) + +#define CSR_IS_AUTH_TYPE_FILS(auth_type) \ + ((eCSR_AUTH_TYPE_FILS_SHA256 == auth_type) || \ + (eCSR_AUTH_TYPE_FILS_SHA384 == auth_type) || \ + (eCSR_AUTH_TYPE_FT_FILS_SHA256 == auth_type) || \ + (eCSR_AUTH_TYPE_FT_FILS_SHA384 == auth_type)) +#define CSR_IS_WAIT_FOR_KEY(mac, sessionId) \ + (CSR_IS_ROAM_JOINED(mac, sessionId) && \ + CSR_IS_ROAM_SUBSTATE_WAITFORKEY(mac, sessionId)) +/* WIFI has a test case for not using HT rates with TKIP as encryption */ +/* We may need to add WEP but for now, TKIP only. */ + +#define CSR_IS_11n_ALLOWED(encType) ((eCSR_ENCRYPT_TYPE_TKIP != (encType)) && \ + (eCSR_ENCRYPT_TYPE_WEP40_STATICKEY != (encType)) && \ + (eCSR_ENCRYPT_TYPE_WEP104_STATICKEY != (encType)) && \ + (eCSR_ENCRYPT_TYPE_WEP40 != (encType)) && \ + (eCSR_ENCRYPT_TYPE_WEP104 != (encType))) + +#define CSR_IS_DISCONNECT_COMMAND(pCommand) ((eSmeCommandRoam == \ + (pCommand)->command) && \ + ((eCsrForcedDisassoc == (pCommand)->u.roamCmd.roamReason) || \ + (eCsrForcedDeauth == (pCommand)->u.roamCmd.roamReason) || \ + (eCsrSmeIssuedDisassocForHandoff == \ + (pCommand)->u.roamCmd.roamReason) || \ + (eCsrForcedDisassocMICFailure == \ + (pCommand)->u.roamCmd.roamReason))) + +enum csr_roam_state csr_roam_state_change(struct mac_context *mac, + enum csr_roam_state NewRoamState, + uint8_t sessionId); +void csr_roaming_state_msg_processor(struct mac_context *mac, void *msg_buf); +void csr_roam_joined_state_msg_processor(struct mac_context *mac, + void *msg_buf); +void csr_scan_callback(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg); +void csr_release_command_roam(struct mac_context *mac, tSmeCmd *pCommand); +void csr_release_command_wm_status_change(struct mac_context *mac, + tSmeCmd *pCommand); + +QDF_STATUS csr_roam_save_connected_bss_desc(struct mac_context *mac, + uint32_t sessionId, + struct bss_description *bss_desc); + +QDF_STATUS csr_roam_copy_profile(struct mac_context *mac, + struct csr_roam_profile *pDstProfile, + struct csr_roam_profile *pSrcProfile); +QDF_STATUS csr_roam_start(struct mac_context *mac); +void csr_roam_stop(struct mac_context *mac, uint32_t sessionId); + +QDF_STATUS csr_scan_open(struct mac_context *mac); +QDF_STATUS csr_scan_close(struct mac_context *mac); + +bool +csr_scan_append_bss_description(struct mac_context *mac, + struct bss_description *pSirBssDescription); + +QDF_STATUS csr_scan_for_ssid(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, uint32_t roamId, + bool notify); +/** + * csr_scan_abort_mac_scan() - Generic API to abort scan request + * @mac: pointer to pmac + * @vdev_id: pdev id + * @scan_id: scan id + * + * Generic API to abort scans + * + * Return: 0 for success, non zero for failure + */ +QDF_STATUS csr_scan_abort_mac_scan(struct mac_context *mac, uint32_t vdev_id, + uint32_t scan_id); + +/* If fForce is true we will save the new String that is learn't. */ +/* Typically it will be true in case of Join or user initiated ioctl */ +bool csr_learn_11dcountry_information(struct mac_context *mac, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, bool fForce); +void csr_apply_country_information(struct mac_context *mac); +void csr_free_scan_result_entry(struct mac_context *mac, struct tag_csrscan_result + *pResult); + +QDF_STATUS csr_roam_call_callback(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_info *roam_info, + uint32_t roamId, + eRoamCmdStatus u1, eCsrRoamResult u2); +QDF_STATUS csr_roam_issue_connect(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + tScanResultHandle hBSSList, + enum csr_roam_reason reason, uint32_t roamId, + bool fImediate, bool fClearScan); +QDF_STATUS csr_roam_issue_reassoc(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + tCsrRoamModifyProfileFields *pModProfileFields, + enum csr_roam_reason reason, uint32_t roamId, + bool fImediate); +void csr_roam_complete(struct mac_context *mac, enum csr_roamcomplete_result Result, + void *Context, uint8_t session_id); + +/** + * csr_issue_set_context_req_helper - Function to fill unicast/broadcast keys + * request to set the keys to fw + * @mac: Poiner to mac context + * @profile: Pointer to connected profile + * @vdev_id: vdev id + * @bssid: Connected BSSID + * @addkey: Is add key request to crypto + * @unicast: Unicast(1) or broadcast key(0) + * @key_direction: Key used in TX or RX or both Tx and RX path + * @key_id: Key index + * @key_length: Key length + * @key: Pointer to the key + * + * Return: QDF_STATUS + */ +QDF_STATUS +csr_issue_set_context_req_helper(struct mac_context *mac, + struct csr_roam_profile *profile, + uint32_t session_id, + tSirMacAddr *bssid, bool addkey, + bool unicast, tAniKeyDirection key_direction, + uint8_t key_id, uint16_t key_length, + uint8_t *key); + +QDF_STATUS csr_roam_process_disassoc_deauth(struct mac_context *mac, + tSmeCmd *pCommand, + bool fDisassoc, bool fMICFailure); + +QDF_STATUS +csr_roam_save_connected_information(struct mac_context *mac, + uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes); + +void csr_roam_check_for_link_status_change(struct mac_context *mac, + tSirSmeRsp *pSirMsg); + +QDF_STATUS csr_roam_issue_start_bss(struct mac_context *mac, uint32_t sessionId, + struct csr_roamstart_bssparams *pParam, + struct csr_roam_profile *pProfile, + struct bss_description *bss_desc, + uint32_t roamId); +QDF_STATUS csr_roam_issue_stop_bss(struct mac_context *mac, uint32_t sessionId, + enum csr_roam_substate NewSubstate); +bool csr_is_same_profile(struct mac_context *mac, tCsrRoamConnectedProfile + *pProfile1, struct csr_roam_profile *pProfile2); +bool csr_is_roam_command_waiting_for_session(struct mac_context *mac, + uint32_t sessionId); +eRoamCmdStatus csr_get_roam_complete_status(struct mac_context *mac, + uint32_t sessionId); +/* pBand can be NULL if caller doesn't need to get it */ +QDF_STATUS csr_roam_issue_disassociate_cmd(struct mac_context *mac, + uint32_t sessionId, + eCsrRoamDisconnectReason reason, + tSirMacReasonCodes mac_reason); +QDF_STATUS csr_roam_disconnect_internal(struct mac_context *mac, uint32_t sessionId, + eCsrRoamDisconnectReason reason, + tSirMacReasonCodes mac_reason); +/* pCommand may be NULL */ +void csr_roam_remove_duplicate_command(struct mac_context *mac, uint32_t sessionId, + tSmeCmd *pCommand, + enum csr_roam_reason eRoamReason); + +QDF_STATUS csr_send_join_req_msg(struct mac_context *mac, uint32_t sessionId, + struct bss_description *pBssDescription, + struct csr_roam_profile *pProfile, + tDot11fBeaconIEs *pIes, uint16_t messageType); +QDF_STATUS csr_send_mb_disassoc_req_msg(struct mac_context *mac, uint32_t sessionId, + tSirMacAddr bssId, uint16_t reasonCode); +QDF_STATUS csr_send_mb_deauth_req_msg(struct mac_context *mac, uint32_t sessionId, + tSirMacAddr bssId, uint16_t reasonCode); +QDF_STATUS csr_send_mb_disassoc_cnf_msg(struct mac_context *mac, + struct disassoc_ind *pDisassocInd); +QDF_STATUS csr_send_mb_deauth_cnf_msg(struct mac_context *mac, + struct deauth_ind *pDeauthInd); +QDF_STATUS csr_send_assoc_cnf_msg(struct mac_context *mac, + struct assoc_ind *pAssocInd, + QDF_STATUS status, + enum mac_status_code mac_status_code); +QDF_STATUS csr_send_mb_start_bss_req_msg(struct mac_context *mac, + uint32_t sessionId, + eCsrRoamBssType bssType, + struct csr_roamstart_bssparams *pParam, + struct bss_description *bss_desc); +QDF_STATUS csr_send_mb_stop_bss_req_msg(struct mac_context *mac, + uint32_t sessionId); + +/* Caller should put the BSS' ssid to fiedl bssSsid when + * comparing SSID for a BSS. + */ +bool csr_is_ssid_match(struct mac_context *mac, uint8_t *ssid1, uint8_t ssid1Len, + uint8_t *bssSsid, uint8_t bssSsidLen, + bool fSsidRequired); +bool csr_is_phy_mode_match(struct mac_context *mac, uint32_t phyMode, + struct bss_description *pSirBssDesc, + struct csr_roam_profile *pProfile, + enum csr_cfgdot11mode *pReturnCfgDot11Mode, + tDot11fBeaconIEs *pIes); + +/** + * csr_roam_is_channel_valid() - validate channel frequency + * @mac: mac context + * @chan_freq: channel frequency + * + * This function validates channel frequency present in valid channel + * list or not. + * + * Return: true or false + */ +bool csr_roam_is_channel_valid(struct mac_context *mac, uint32_t chan_freq); + +/** + * csr_get_cfg_valid_channels() - Get valid channel frequency list + * @mac: mac context + * @ch_freq_list: valid channel frequencies + * @num_ch_freq: valid channel nummber + * + * This function returns the valid channel frequencies. + * + * Return: QDF_STATUS_SUCCESS for success. + */ +QDF_STATUS csr_get_cfg_valid_channels(struct mac_context *mac, + uint32_t *ch_freq_list, + uint32_t *num_ch_freq); + +int8_t csr_get_cfg_max_tx_power(struct mac_context *mac, uint32_t ch_freq); + +/* To free the last roaming profile */ +void csr_free_roam_profile(struct mac_context *mac, uint32_t sessionId); +void csr_free_connect_bss_desc(struct mac_context *mac, uint32_t sessionId); + +/* to free memory allocated inside the profile structure */ +void csr_release_profile(struct mac_context *mac, + struct csr_roam_profile *pProfile); + +enum csr_cfgdot11mode +csr_get_cfg_dot11_mode_from_csr_phy_mode(struct csr_roam_profile *pProfile, + eCsrPhyMode phyMode, + bool fProprietary); + +uint32_t csr_translate_to_wni_cfg_dot11_mode(struct mac_context *mac, + enum csr_cfgdot11mode csrDot11Mode); +void csr_save_channel_power_for_band(struct mac_context *mac, bool fPopulate5GBand); +void csr_apply_channel_power_info_to_fw(struct mac_context *mac, + struct csr_channel *pChannelList, + uint8_t *countryCode); +void csr_apply_power2_current(struct mac_context *mac); +void csr_assign_rssi_for_category(struct mac_context *mac, int8_t bestApRssi, + uint8_t catOffset); + +/* return a bool to indicate whether roaming completed or continue. */ +bool csr_roam_complete_roaming(struct mac_context *mac, uint32_t sessionId, + bool fForce, eCsrRoamResult roamResult); +void csr_roam_completion(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_info *roam_info, tSmeCmd *pCommand, + eCsrRoamResult roamResult, bool fSuccess); +void csr_roam_cancel_roaming(struct mac_context *mac, uint32_t sessionId); +void csr_apply_channel_power_info_wrapper(struct mac_context *mac); +void csr_reset_pmkid_candidate_list(struct mac_context *mac, uint32_t sessionId); +QDF_STATUS csr_save_to_channel_power2_g_5_g(struct mac_context *mac, + uint32_t tableSize, tSirMacChanInfo + *channelTable); + +/* + * csr_roam_vdev_delete() - CSR api to delete vdev + * @mac_ctx: pointer to mac context + * @vdev_id: vdev id to be deleted. + * @cleanup: clean up vdev session on true + * + * Return QDF_STATUS + */ +QDF_STATUS csr_roam_vdev_delete(struct mac_context *mac_ctx, + uint8_t vdev_id, bool cleanup); + +/* + * csr_cleanup_vdev_session() - CSR api to cleanup vdev + * @mac_ctx: pointer to mac context + * @vdev_id: vdev id to be deleted. + * + * This API is used to clean up vdev information gathered during + * vdev was enabled. + * + * Return QDF_STATUS + */ +void csr_cleanup_vdev_session(struct mac_context *mac, uint8_t vdev_id); + +QDF_STATUS csr_roam_get_session_id_from_bssid(struct mac_context *mac, + struct qdf_mac_addr *bssid, + uint32_t *pSessionId); +enum csr_cfgdot11mode csr_find_best_phy_mode(struct mac_context *mac, + uint32_t phyMode); + +/* + * csr_copy_ssids_from_roam_params() - copy SSID from roam_params to scan filter + * @roam_params: roam params + * @filter: scan filter + * + * Return void + */ +void csr_copy_ssids_from_roam_params(struct roam_ext_params *roam_params, + struct scan_filter *filter); + +/* + * csr_update_connect_n_roam_cmn_filter() - update common scan filter + * @mac_ctx: pointer to mac context + * @filter: scan filter + * @opmode: opmode + * + * Return void + */ +void csr_update_connect_n_roam_cmn_filter(struct mac_context *mac_ctx, + struct scan_filter *filter, + enum QDF_OPMODE opmode); + +/* + * csr_covert_enc_type_new() - convert csr enc type to wlan enc type + * @enc: csr enc type + * + * Return enum wlan_enc_type + */ +enum wlan_enc_type csr_covert_enc_type_new(eCsrEncryptionType enc); + +/* + * csr_covert_auth_type_new() - convert csr auth type to wlan auth type + * @auth: csr auth type + * + * Return enum wlan_auth_type + */ +enum wlan_auth_type csr_covert_auth_type_new(enum csr_akm_type auth); + +/** + * csr_roam_get_scan_filter_from_profile() - prepare scan filter from + * given roam profile + * @mac: Pointer to Global MAC structure + * @profile: roam profile + * @filter: Populated scan filter based on the connected profile + * @is_roam: if filter is for roam + * + * This function creates a scan filter based on the roam profile. Based on this + * filter, scan results are obtained. + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE otherwise + */ +QDF_STATUS +csr_roam_get_scan_filter_from_profile(struct mac_context *mac_ctx, + struct csr_roam_profile *profile, + struct scan_filter *filter, + bool is_roam); + +/** + * csr_neighbor_roam_get_scan_filter_from_profile() - prepare scan filter from + * connected profile + * @mac: Pointer to Global MAC structure + * @filter: Populated scan filter based on the connected profile + * @vdev_id: Session ID + * + * This function creates a scan filter based on the currently + * connected profile. Based on this filter, scan results are obtained + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE otherwise + */ +QDF_STATUS +csr_neighbor_roam_get_scan_filter_from_profile(struct mac_context *mac, + struct scan_filter *filter, + uint8_t vdev_id); +/* + * csr_scan_get_result() - Return scan results based on filter + * @mac: Pointer to Global MAC structure + * @filter: If pFilter is NULL, all cached results are returned + * @phResult: an object for the result. + * + * Return QDF_STATUS + */ +QDF_STATUS csr_scan_get_result(struct mac_context *mac, + struct scan_filter *filter, + tScanResultHandle *phResult); + +/** + * csr_scan_get_result_for_bssid - gets the scan result from scan cache for the + * bssid specified + * @mac_ctx: mac context + * @bssid: bssid to get the scan result for + * @res: pointer to tCsrScanResultInfo + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_scan_get_result_for_bssid(struct mac_context *mac_ctx, + struct qdf_mac_addr *bssid, + tCsrScanResultInfo *res); + +/* + * csr_scan_filter_results() - + * Filter scan results based on valid channel list. + * + * mac - Pointer to Global MAC structure + * Return QDF_STATUS + */ +QDF_STATUS csr_scan_filter_results(struct mac_context *mac); + +/* + * csr_scan_result_get_first + * Returns the first element of scan result. + * + * hScanResult - returned from csr_scan_get_result + * tCsrScanResultInfo * - NULL if no result + */ +tCsrScanResultInfo *csr_scan_result_get_first(struct mac_context *mac, + tScanResultHandle hScanResult); +/* + * csr_scan_result_get_next + * Returns the next element of scan result. It can be called without calling + * csr_scan_result_get_first first + * + * hScanResult - returned from csr_scan_get_result + * Return Null if no result or reach the end + */ +tCsrScanResultInfo *csr_scan_result_get_next(struct mac_context *mac, + tScanResultHandle hScanResult); + +/* + * csr_get_country_code() - + * This function is to get the country code current being used + * pBuf - Caller allocated buffer with at least 3 bytes, upon success return, + * this has the country code + * pbLen - Caller allocated, as input, it indicates the length of pBuf. Upon + * success return, this contains the length of the data in pBuf + * Return QDF_STATUS + */ +QDF_STATUS csr_get_country_code(struct mac_context *mac, uint8_t *pBuf, + uint8_t *pbLen); + +/* + * csr_get_regulatory_domain_for_country() - + * This function is to get the regulatory domain for a country. + * This function must be called after CFG is downloaded and all the band/mode + * setting already passed into CSR. + * + * pCountry - Caller allocated buffer with at least 3 bytes specifying the + * country code + * pDomainId - Caller allocated buffer to get the return domain ID upon + * success return. Can be NULL. + * source - the source of country information. + * Return QDF_STATUS + */ +QDF_STATUS csr_get_regulatory_domain_for_country(struct mac_context *mac, + uint8_t *pCountry, + v_REGDOMAIN_t *pDomainId, + enum country_src source); + +/* some support functions */ +bool csr_is11h_supported(struct mac_context *mac); +bool csr_is11e_supported(struct mac_context *mac); +bool csr_is_wmm_supported(struct mac_context *mac); + +/* Return SUCCESS is the command is queued, failed */ +QDF_STATUS csr_queue_sme_command(struct mac_context *mac, tSmeCmd *pCommand, + bool fHighPriority); +tSmeCmd *csr_get_command_buffer(struct mac_context *mac); +void csr_release_command(struct mac_context *mac, tSmeCmd *pCommand); +void csr_release_command_buffer(struct mac_context *mac, tSmeCmd *pCommand); + +#ifdef FEATURE_WLAN_WAPI +bool csr_is_profile_wapi(struct csr_roam_profile *pProfile); +#endif /* FEATURE_WLAN_WAPI */ + +/** + * csr_get_vdev_type_nss() - gets the nss value based on vdev type + * @dev_mode: current device operating mode. + * @nss2g: Pointer to the 2G Nss parameter. + * @nss5g: Pointer to the 5G Nss parameter. + * + * Fills the 2G and 5G Nss values based on device mode. + * + * Return: None + */ +void csr_get_vdev_type_nss(enum QDF_OPMODE dev_mode, uint8_t *nss_2g, + uint8_t *nss_5g); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR + +/* Security */ +#define WLAN_SECURITY_EVENT_REMOVE_KEY_REQ 5 +#define WLAN_SECURITY_EVENT_REMOVE_KEY_RSP 6 +#define WLAN_SECURITY_EVENT_PMKID_CANDIDATE_FOUND 7 +#define WLAN_SECURITY_EVENT_PMKID_UPDATE 8 +#define WLAN_SECURITY_EVENT_MIC_ERROR 9 +#define WLAN_SECURITY_EVENT_SET_UNICAST_REQ 10 +#define WLAN_SECURITY_EVENT_SET_UNICAST_RSP 11 +#define WLAN_SECURITY_EVENT_SET_BCAST_REQ 12 +#define WLAN_SECURITY_EVENT_SET_BCAST_RSP 13 + +#define NO_MATCH 0 +#define MATCH 1 + +#define WLAN_SECURITY_STATUS_SUCCESS 0 +#define WLAN_SECURITY_STATUS_FAILURE 1 + +/* Scan */ +#define WLAN_SCAN_EVENT_ACTIVE_SCAN_REQ 1 +#define WLAN_SCAN_EVENT_ACTIVE_SCAN_RSP 2 +#define WLAN_SCAN_EVENT_PASSIVE_SCAN_REQ 3 +#define WLAN_SCAN_EVENT_PASSIVE_SCAN_RSP 4 +#define WLAN_SCAN_EVENT_HO_SCAN_REQ 5 +#define WLAN_SCAN_EVENT_HO_SCAN_RSP 6 + +#define WLAN_SCAN_STATUS_SUCCESS 0 +#define WLAN_SCAN_STATUS_FAILURE 1 +#define WLAN_SCAN_STATUS_ABORT 2 + +/* Ibss */ +#define WLAN_IBSS_EVENT_START_IBSS_REQ 0 +#define WLAN_IBSS_EVENT_START_IBSS_RSP 1 +#define WLAN_IBSS_EVENT_JOIN_IBSS_REQ 2 +#define WLAN_IBSS_EVENT_JOIN_IBSS_RSP 3 +#define WLAN_IBSS_EVENT_COALESCING 4 +#define WLAN_IBSS_EVENT_PEER_JOIN 5 +#define WLAN_IBSS_EVENT_PEER_LEAVE 6 +#define WLAN_IBSS_EVENT_STOP_REQ 7 +#define WLAN_IBSS_EVENT_STOP_RSP 8 + +#define AUTO_PICK 0 +#define SPECIFIED 1 + +#define WLAN_IBSS_STATUS_SUCCESS 0 +#define WLAN_IBSS_STATUS_FAILURE 1 + +/* 11d */ +#define WLAN_80211D_EVENT_COUNTRY_SET 0 +#define WLAN_80211D_EVENT_RESET 1 + +#define WLAN_80211D_DISABLED 0 +#define WLAN_80211D_SUPPORT_MULTI_DOMAIN 1 +#define WLAN_80211D_NOT_SUPPORT_MULTI_DOMAIN 2 + +/** + * diag_auth_type_from_csr_type() - to convert CSR auth type to DIAG auth type + * @authtype: CSR auth type + * + * DIAG tool understands its own ENUMs, so this API can be used to convert + * CSR defined auth type ENUMs to DIAG defined auth type ENUMs + * + * + * Return: DIAG auth type + */ +enum mgmt_auth_type diag_auth_type_from_csr_type(enum csr_akm_type authtype); +/** + * diag_enc_type_from_csr_type() - to convert CSR encr type to DIAG encr type + * @enctype: CSR encryption type + * + * DIAG tool understands its own ENUMs, so this API can be used to convert + * CSR defined encr type ENUMs to DIAG defined encr type ENUMs + * + * Return: DIAG encryption type + */ +enum mgmt_encrypt_type diag_enc_type_from_csr_type(eCsrEncryptionType enctype); +/** + * diag_dot11_mode_from_csr_type() - to convert CSR .11 mode to DIAG .11 mode + * @dot11mode: CSR 80211 mode + * + * DIAG tool understands its own ENUMs, so this API can be used to convert + * CSR defined 80211 mode ENUMs to DIAG defined 80211 mode ENUMs + * + * Return: DIAG 80211mode + */ +enum mgmt_dot11_mode +diag_dot11_mode_from_csr_type(enum csr_cfgdot11mode dot11mode); +/** + * diag_ch_width_from_csr_type() - to convert CSR ch width to DIAG ch width + * @ch_width: CSR channel width + * + * DIAG tool understands its own ENUMs, so this API can be used to convert + * CSR defined ch width ENUMs to DIAG defined ch width ENUMs + * + * Return: DIAG channel width + */ +enum mgmt_ch_width diag_ch_width_from_csr_type(enum phy_ch_width ch_width); +/** + * diag_persona_from_csr_type() - to convert QDF persona to DIAG persona + * @persona: QDF persona + * + * DIAG tool understands its own ENUMs, so this API can be used to convert + * QDF defined persona type ENUMs to DIAG defined persona type ENUMs + * + * Return: DIAG persona + */ +enum mgmt_bss_type diag_persona_from_csr_type(enum QDF_OPMODE persona); +#endif /* #ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR */ +/* + * csr_scan_result_purge() - + * Remove all items(tCsrScanResult) in the list and free memory for each item + * hScanResult - returned from csr_scan_get_result. hScanResult is considered + * gone by calling this function and even before this function reutrns. + * Return QDF_STATUS + */ +QDF_STATUS csr_scan_result_purge(struct mac_context *mac, + tScanResultHandle hScanResult); + +/* /////////////////////////////////////////Common Scan ends */ + +/* + * csr_roam_connect() - + * To inititiate an association + * pProfile - can be NULL to join to any open ones + * pRoamId - to get back the request ID + * Return QDF_STATUS + */ +QDF_STATUS csr_roam_connect(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + uint32_t *pRoamId); + +/* + * csr_roam_reassoc() - + * To inititiate a re-association + * pProfile - can be NULL to join the currently connected AP. In that + * case modProfileFields should carry the modified field(s) which could trigger + * reassoc + * modProfileFields - fields which are part of tCsrRoamConnectedProfile + * that might need modification dynamically once STA is up & running and this + * could trigger a reassoc + * pRoamId - to get back the request ID + * Return QDF_STATUS + */ +QDF_STATUS csr_roam_reassoc(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + tCsrRoamModifyProfileFields modProfileFields, + uint32_t *pRoamId); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/* + * csr_get_pmk_info(): store PMK in pmk_cache + * @mac_ctx: pointer to global structure for MAC + * @session_id: Sme session id + * @pmk_cache: pointer to a structure of Pmk + * + * This API gets the PMK from the session and + * stores it in the pmk_cache + * + * Return: none + */ +void csr_get_pmk_info(struct mac_context *mac_ctx, uint8_t session_id, + tPmkidCacheInfo *pmk_cache); + +/* + * csr_roam_set_psk_pmk() - store PSK/PMK in CSR session + * + * @mac - pointer to global structure for MAC + * @sessionId - Sme session id + * @psk_pmk - pointer to an array of PSK/PMK + * @update_to_fw - Send RSO update config command to firmware to update + * PMK + * + * Return QDF_STATUS - usually it succeed unless sessionId is not found + */ +QDF_STATUS csr_roam_set_psk_pmk(struct mac_context *mac, uint32_t sessionId, + uint8_t *psk_pmk, size_t pmk_len, + bool update_to_fw); + +QDF_STATUS csr_roam_set_key_mgmt_offload(struct mac_context *mac_ctx, + uint32_t session_id, + bool roam_key_mgmt_offload_enabled, + struct pmkid_mode_bits *pmkid_modes); +#endif +/* + * csr_roam_get_wpa_rsn_req_ie() - + * Return the WPA or RSN IE CSR passes to PE to JOIN request or START_BSS + * request + * pLen - caller allocated memory that has the length of pBuf as input. + * Upon returned, *pLen has the needed or IE length in pBuf. + * pBuf - Caller allocated memory that contain the IE field, if any, upon return + * Return QDF_STATUS - when fail, it usually means the buffer allocated is not + * big enough + */ +QDF_STATUS csr_roam_get_wpa_rsn_req_ie(struct mac_context *mac, uint32_t sessionId, + uint32_t *pLen, uint8_t *pBuf); + +/* + * csr_roam_get_wpa_rsn_rsp_ie() - + * Return the WPA or RSN IE from the beacon or probe rsp if connected + * + * pLen - caller allocated memory that has the length of pBuf as input. + * Upon returned, *pLen has the needed or IE length in pBuf. + * pBuf - Caller allocated memory that contain the IE field, if any, upon return + * Return QDF_STATUS - when fail, it usually means the buffer allocated is not + * big enough + */ +QDF_STATUS csr_roam_get_wpa_rsn_rsp_ie(struct mac_context *mac, uint32_t sessionId, + uint32_t *pLen, uint8_t *pBuf); + +/** + * csr_roam_get_connect_profile() - To return the current connect profile, + * caller must call csr_roam_free_connect_profile after it is done and before + * reuse for another csr_roam_get_connect_profile call. + * + * @mac: pointer to global adapter context + * @sessionId: session ID + * @pProfile: pointer to a caller allocated structure + * tCsrRoamConnectedProfile + * + * Return: QDF_STATUS. Failure if not connected, success otherwise + */ +QDF_STATUS csr_roam_get_connect_profile(struct mac_context *mac, uint32_t sessionId, + tCsrRoamConnectedProfile *pProfile); + +void csr_roam_free_connect_profile(tCsrRoamConnectedProfile *profile); + +/* + * csr_apply_channel_and_power_list() - + * HDD calls this function to set the CFG_VALID_CHANNEL_LIST base on the + * band/mode settings. This function must be called after CFG is downloaded + * and all the band/mode setting already passed into CSR. + + * Return QDF_STATUS + */ +QDF_STATUS csr_apply_channel_and_power_list(struct mac_context *mac); + +/* + * csr_roam_disconnect() - To disconnect from a network + * @mac: pointer to mac context + * @session_id: Session ID + * @reason: CSR disconnect reason code as per @enum eCsrRoamDisconnectReason + * @mac_reason: Mac Disconnect reason code as per @enum eSirMacReasonCodes + * + * Return QDF_STATUS + */ +QDF_STATUS csr_roam_disconnect(struct mac_context *mac, uint32_t session_id, + eCsrRoamDisconnectReason reason, + tSirMacReasonCodes mac_reason); + +/* This function is used to stop a BSS. It is similar of csr_roamIssueDisconnect + * but this function doesn't have any logic other than blindly trying to stop + * BSS + */ +QDF_STATUS csr_roam_issue_stop_bss_cmd(struct mac_context *mac, uint32_t sessionId, + bool fHighPriority); + +void csr_call_roaming_completion_callback(struct mac_context *mac, + struct csr_roam_session *pSession, + struct csr_roam_info *roam_info, + uint32_t roamId, + eCsrRoamResult roamResult); +/** + * csr_roam_issue_disassociate_sta_cmd() - disassociate a associated station + * @mac: Pointer to global structure for MAC + * @sessionId: Session Id for Soft AP + * @p_del_sta_params: Pointer to parameters of the station to disassoc + * + * CSR function that HDD calls to issue a deauthenticate station command + * + * Return: QDF_STATUS_SUCCESS on success or another QDF_STATUS_* on error + */ +QDF_STATUS csr_roam_issue_disassociate_sta_cmd(struct mac_context *mac, + uint32_t sessionId, + struct csr_del_sta_params + *p_del_sta_params); +/** + * csr_roam_issue_deauth_sta_cmd() - issue deauthenticate station command + * @mac: Pointer to global structure for MAC + * @sessionId: Session Id for Soft AP + * @pDelStaParams: Pointer to parameters of the station to deauthenticate + * + * CSR function that HDD calls to issue a deauthenticate station command + * + * Return: QDF_STATUS_SUCCESS on success or another QDF_STATUS_** on error + */ +QDF_STATUS csr_roam_issue_deauth_sta_cmd(struct mac_context *mac, + uint32_t sessionId, + struct csr_del_sta_params *pDelStaParams); + +/* + * csr_send_chng_mcc_beacon_interval() - + * csr function that HDD calls to send Update beacon interval + * + * sessionId - session Id for Soft AP + * Return QDF_STATUS + */ +QDF_STATUS +csr_send_chng_mcc_beacon_interval(struct mac_context *mac, uint32_t sessionId); + +/** + * csr_roam_ft_pre_auth_rsp_processor() - Handle the preauth response + * @mac_ctx: Global MAC context + * @preauth_rsp: Received preauthentication response + * + * Return: None + */ +#ifdef WLAN_FEATURE_HOST_ROAM +void csr_roam_ft_pre_auth_rsp_processor(struct mac_context *mac_ctx, + tpSirFTPreAuthRsp pFTPreAuthRsp); +#else +static inline +void csr_roam_ft_pre_auth_rsp_processor(struct mac_context *mac_ctx, + tpSirFTPreAuthRsp pFTPreAuthRsp) +{} +#endif + +#if defined(FEATURE_WLAN_ESE) +void update_cckmtsf(uint32_t *timeStamp0, uint32_t *timeStamp1, + uint64_t *incr); +#endif + +QDF_STATUS csr_roam_enqueue_preauth(struct mac_context *mac, uint32_t sessionId, + struct bss_description *pBssDescription, + enum csr_roam_reason reason, + bool fImmediate); +QDF_STATUS csr_dequeue_roam_command(struct mac_context *mac, + enum csr_roam_reason reason, + uint8_t session_id); +void csr_init_occupied_channels_list(struct mac_context *mac, uint8_t sessionId); + +QDF_STATUS csr_scan_create_entry_in_scan_cache(struct mac_context *mac, + uint32_t sessionId, + struct qdf_mac_addr bssid, + uint32_t ch_freq); + +QDF_STATUS csr_update_channel_list(struct mac_context *mac); +QDF_STATUS csr_roam_del_pmkid_from_cache(struct mac_context *mac, + uint32_t sessionId, + tPmkidCacheInfo *pmksa, + bool flush_cache); + +#if defined(WLAN_SAE_SINGLE_PMK) && defined(WLAN_FEATURE_ROAM_OFFLOAD) +/** + * csr_clear_sae_single_pmk - API to clear single_pmk_info cache + * @psoc: psoc common object + * @vdev_id: session id + * @pmksa: pmk info + * + * Return : None + */ +void csr_clear_sae_single_pmk(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, tPmkidCacheInfo *pmksa); + +void csr_store_sae_single_pmk_to_global_cache(struct mac_context *mac, + struct csr_roam_session *session, + uint8_t vdev_id); +#else +static inline void +csr_clear_sae_single_pmk(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + tPmkidCacheInfo *pmksa) +{ +} + +static inline +void csr_store_sae_single_pmk_to_global_cache(struct mac_context *mac, + struct csr_roam_session *session, + uint8_t vdev_id) +{} +#endif + +QDF_STATUS csr_send_ext_change_channel(struct mac_context *mac_ctx, + uint32_t channel, uint8_t session_id); + +/** + * csr_csa_start() - request CSA IE transmission from PE + * @mac_ctx: handle returned by mac_open + * @session_id: SAP session id + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_csa_restart(struct mac_context *mac_ctx, uint8_t session_id); + +/** + * csr_sta_continue_csa() - Continue for CSA for STA after HW mode change + * @mac_ctx: handle returned by mac_open + * @vdev_id: STA VDEV ID + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_sta_continue_csa(struct mac_context *mac_ctx, + uint8_t vdev_id); + +#ifdef QCA_HT_2040_COEX +QDF_STATUS csr_set_ht2040_mode(struct mac_context *mac, uint32_t sessionId, + ePhyChanBondState cbMode, bool obssEnabled); +#endif +QDF_STATUS csr_scan_handle_search_for_ssid(struct mac_context *mac_ctx, + uint32_t session_id); +QDF_STATUS csr_scan_handle_search_for_ssid_failure(struct mac_context *mac, + uint32_t session_id); +void csr_saved_scan_cmd_free_fields(struct mac_context *mac_ctx, + struct csr_roam_session *session); +struct bss_description* +csr_get_fst_bssdescr_ptr(tScanResultHandle result_handle); + +struct bss_description* +csr_get_bssdescr_from_scan_handle(tScanResultHandle result_handle, + struct bss_description *bss_descr); + +bool is_disconnect_pending(struct mac_context *mac_ctx, + uint8_t sessionid); + +QDF_STATUS +csr_roam_prepare_bss_config_from_profile(struct mac_context *mac_ctx, + struct csr_roam_profile *profile, + struct bss_config_param *bss_cfg, + struct bss_description *bss_desc); + +void +csr_roam_prepare_bss_params(struct mac_context *mac_ctx, uint32_t session_id, + struct csr_roam_profile *profile, + struct bss_description *bss_desc, + struct bss_config_param *bss_cfg, + tDot11fBeaconIEs *ies); + +/** + * csr_remove_bssid_from_scan_list() - remove the bssid from + * scan list + * @mac_tx: mac context. + * @bssid: bssid to be removed + * + * This function remove the given bssid from scan list. + * + * Return: void. + */ +void csr_remove_bssid_from_scan_list(struct mac_context *mac_ctx, + tSirMacAddr bssid); + +QDF_STATUS +csr_roam_set_bss_config_cfg(struct mac_context *mac_ctx, uint32_t session_id, + struct csr_roam_profile *profile, + struct bss_description *bss_desc, + struct bss_config_param *bss_cfg, + tDot11fBeaconIEs *ies, bool reset_country); + +void csr_prune_channel_list_for_mode(struct mac_context *mac, + struct csr_channel *pChannelList); + +#ifdef WLAN_FEATURE_11W +bool csr_is_mfpc_capable(struct sDot11fIERSN *rsn); +#else +static inline bool csr_is_mfpc_capable(struct sDot11fIERSN *rsn) +{ + return false; +} +#endif + +/** + * csr_get_rf_band() + * + * @channel: channel number + * + * This function is used to translate channel number to band + * + * Return: BAND_2G - if 2.4GHZ channel + * BAND_5G - if 5GHZ channel + */ +enum band_info csr_get_rf_band(uint8_t channel); + +/** + * csr_lookup_pmkid_using_bssid() - lookup pmkid using bssid + * @mac: pointer to mac + * @session: sme session pointer + * @pmk_cache: pointer to pmk cache + * + * Return: true if pmkid is found else false + */ +bool csr_lookup_pmkid_using_bssid(struct mac_context *mac, + struct csr_roam_session *session, + tPmkidCacheInfo *pmk_cache); + +/** + * csr_lookup_fils_pmkid - Lookup FILS PMKID using ssid and cache id + * @mac: Pointer to mac context + * @vdev_id: vdev id + * @cache_id: FILS cache id + * @ssid: SSID pointer + * @ssid_len: SSID length + * @bssid: Pointer to the BSSID to lookup + * + * Return: True if lookup is successful + */ +bool csr_lookup_fils_pmkid(struct mac_context *mac, uint8_t vdev_id, + uint8_t *cache_id, uint8_t *ssid, + uint8_t ssid_len, struct qdf_mac_addr *bssid); +/** + * csr_is_pmkid_found_for_peer() - check if pmkid sent by peer is present + in PMK cache. Used in SAP mode. + * @mac: pointer to mac + * @session: sme session pointer + * @peer_mac_addr: mac address of the connecting peer + * @pmkid: pointer to pmkid(s) send by peer + * @pmkid_count: number of pmkids sent by peer + * + * Return: true if pmkid is found else false + */ +bool csr_is_pmkid_found_for_peer(struct mac_context *mac, + struct csr_roam_session *session, + tSirMacAddr peer_mac_addr, + uint8_t *pmkid, uint16_t pmkid_count); +#ifdef WLAN_FEATURE_11AX +void csr_update_session_he_cap(struct mac_context *mac_ctx, + struct csr_roam_session *session); +void csr_init_session_twt_cap(struct csr_roam_session *session, + uint32_t type_of_persona); +#else +static inline void csr_update_session_he_cap(struct mac_context *mac_ctx, + struct csr_roam_session *session) +{ +} + +static inline void csr_init_session_twt_cap(struct csr_roam_session *session, + uint32_t type_of_persona) +{ +} +#endif +/** + * csr_get_channel_for_hw_mode_change() - This function to find + * out if HW mode change + * is needed for any of + * the candidate AP which + * STA could join + * @mac_ctx: mac context + * @result_handle: an object for the result. + * @session_id: STA session ID + * + * This function is written to find out for any bss from scan + * handle a HW mode change to DBS will be needed or not. + * + * Return: AP channel freq for which DBS HW mode will be needed. 0 + * means no HW mode change is needed. + */ +uint32_t +csr_get_channel_for_hw_mode_change(struct mac_context *mac_ctx, + tScanResultHandle result_handle, + uint32_t session_id); + +/** + * csr_scan_get_channel_for_hw_mode_change() - This function to find + * out if HW mode change + * is needed for any of + * the candidate AP which + * STA could join + * @mac_ctx: mac context + * @session_id: STA session ID + * @profile: profile + * + * This function is written to find out for any bss from scan + * handle a HW mode change to DBS will be needed or not. + * If there is no candidate AP which requires DBS, this function will return + * the first Candidate AP's chan. + * + * Return: AP channel freq for which HW mode change will be needed. 0 + * means no candidate AP to connect. + */ +uint32_t +csr_scan_get_channel_for_hw_mode_change( + struct mac_context *mac_ctx, uint32_t session_id, + struct csr_roam_profile *profile); +/** + * csr_setup_vdev_session() - API to setup vdev mac session + * @vdev_mlme: vdev mlme private object + * + * This API setsup the vdev session for the mac layer + * + * Returns: QDF_STATUS + */ +QDF_STATUS csr_setup_vdev_session(struct vdev_mlme_obj *vdev_mlme); + + +#ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR +/** + * csr_get_sta_cxn_info() - This function populates all the connection + * information which is formed by DUT-STA to AP + * @mac_ctx: pointer to mac context + * @session: pointer to sta session + * @conn_profile: pointer to connected DUTSTA-REFAP profile + * @buf: pointer to char buffer to write all the connection information. + * @buf_size: maximum size of the provided buffer + * + * Returns: None (information gets populated in buffer) + */ +void csr_get_sta_cxn_info(struct mac_context *mac_ctx, + struct csr_roam_session *session, + struct tagCsrRoamConnectedProfile *conn_profile, + char *buf, uint32_t buf_sz); +#endif +#endif /* CSR_INSIDE_API_H__ */ diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_link_list.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_link_list.c new file mode 100644 index 0000000000000000000000000000000000000000..5d120dc3e8c26cf6d23d142d61fe07055affc08e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_link_list.c @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2011-2012, 2014-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: csr_link_list.c + * + * Implementation for the Common link list interfaces. + */ +#include "csr_link_list.h" +#include "qdf_lock.h" +#include "qdf_mem.h" +#include "qdf_trace.h" +#include "qdf_mc_timer.h" + +static inline void csr_list_init(tListElem *pList) +{ + pList->last = pList->next = pList; +} + +static inline void csr_list_remove_entry(tListElem *pEntry) +{ + tListElem *pLast; + tListElem *pNext; + + pLast = pEntry->last; + pNext = pEntry->next; + pLast->next = pNext; + pNext->last = pLast; +} + +static inline tListElem *csr_list_remove_head(tListElem *pHead) +{ + tListElem *pEntry; + tListElem *pNext; + + pEntry = pHead->next; + pNext = pEntry->next; + pHead->next = pNext; + pNext->last = pHead; + + return pEntry; +} + +static inline tListElem *csr_list_remove_tail(tListElem *pHead) +{ + tListElem *pEntry; + tListElem *pLast; + + pEntry = pHead->last; + pLast = pEntry->last; + pHead->last = pLast; + pLast->next = pHead; + + return pEntry; +} + +static inline void csr_list_insert_tail(tListElem *pHead, tListElem *pEntry) +{ + tListElem *pLast; + + pLast = pHead->last; + pEntry->last = pLast; + pEntry->next = pHead; + pLast->next = pEntry; + pHead->last = pEntry; +} + +static inline void csr_list_insert_head(tListElem *pHead, tListElem *pEntry) +{ + tListElem *pNext; + + pNext = pHead->next; + pEntry->next = pNext; + pEntry->last = pHead; + pNext->last = pEntry; + pHead->next = pEntry; +} + +/* Insert pNewEntry before pEntry */ +static void csr_list_insert_entry(tListElem *pEntry, tListElem *pNewEntry) +{ + tListElem *pLast; + + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pEntry is Null", __func__); + return; + } + + pLast = pEntry->last; + pLast->next = pNewEntry; + pEntry->last = pNewEntry; + pNewEntry->next = pEntry; + pNewEntry->last = pLast; +} + +uint32_t csr_ll_count(tDblLinkList *pList) +{ + uint32_t c = 0; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return c; + } + + if (pList && (LIST_FLAG_OPEN == pList->Flag)) + c = pList->Count; + + return c; +} + +void csr_ll_lock(tDblLinkList *pList) +{ + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return; + } + + if (LIST_FLAG_OPEN == pList->Flag) + qdf_mutex_acquire(&pList->Lock); +} + +void csr_ll_unlock(tDblLinkList *pList) +{ + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return; + } + + if (LIST_FLAG_OPEN == pList->Flag) + qdf_mutex_release(&pList->Lock); +} + +bool csr_ll_is_list_empty(tDblLinkList *pList, bool fInterlocked) +{ + bool fEmpty = true; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return fEmpty; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + fEmpty = csrIsListEmpty(&pList->ListHead); + + if (fInterlocked) + csr_ll_unlock(pList); + } + return fEmpty; +} + +bool csr_ll_find_entry(tDblLinkList *pList, tListElem *pEntryToFind) +{ + bool fFound = false; + tListElem *pEntry; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return fFound; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + pEntry = csr_ll_peek_head(pList, LL_ACCESS_NOLOCK); + + /* Have to make sure we don't loop back to the head of the list, + * which will happen if the entry is NOT on the list. + */ + + while (pEntry && (pEntry != &pList->ListHead)) { + if (pEntry == pEntryToFind) { + fFound = true; + break; + } + pEntry = pEntry->next; + } + + } + return fFound; +} + +QDF_STATUS csr_ll_open(tDblLinkList *pList) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS qdf_status; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (LIST_FLAG_OPEN != pList->Flag) { + pList->Count = 0; + qdf_status = qdf_mutex_create(&pList->Lock); + + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + csr_list_init(&pList->ListHead); + pList->Flag = LIST_FLAG_OPEN; + } else + status = QDF_STATUS_E_FAILURE; + } + return status; +} + +void csr_ll_close(tDblLinkList *pList) +{ + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + /* Make sure the list is empty... */ + csr_ll_purge(pList, LL_ACCESS_LOCK); + qdf_mutex_destroy(&pList->Lock); + pList->Flag = LIST_FLAG_CLOSE; + } +} + +void csr_ll_insert_tail(tDblLinkList *pList, tListElem *pEntry, + bool fInterlocked) +{ + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + csr_list_insert_tail(&pList->ListHead, pEntry); + pList->Count++; + if (fInterlocked) + csr_ll_unlock(pList); + } +} + +void csr_ll_insert_head(tDblLinkList *pList, tListElem *pEntry, + bool fInterlocked) +{ + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + csr_list_insert_head(&pList->ListHead, pEntry); + pList->Count++; + if (fInterlocked) + csr_ll_unlock(pList); + } +} + +void csr_ll_insert_entry(tDblLinkList *pList, tListElem *pEntry, + tListElem *pNewEntry, bool fInterlocked) +{ + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + csr_list_insert_entry(pEntry, pNewEntry); + pList->Count++; + if (fInterlocked) + csr_ll_unlock(pList); + } +} + +tListElem *csr_ll_remove_tail(tDblLinkList *pList, bool fInterlocked) +{ + tListElem *pEntry = NULL; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return pEntry; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + if (!csrIsListEmpty(&pList->ListHead)) { + pEntry = csr_list_remove_tail(&pList->ListHead); + pList->Count--; + } + if (fInterlocked) + csr_ll_unlock(pList); + } + + return pEntry; +} + +tListElem *csr_ll_peek_tail(tDblLinkList *pList, bool fInterlocked) +{ + tListElem *pEntry = NULL; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return pEntry; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + if (!csrIsListEmpty(&pList->ListHead)) + pEntry = pList->ListHead.last; + + if (fInterlocked) + csr_ll_unlock(pList); + } + + return pEntry; +} + +tListElem *csr_ll_remove_head(tDblLinkList *pList, bool fInterlocked) +{ + tListElem *pEntry = NULL; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return pEntry; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + if (!csrIsListEmpty(&pList->ListHead)) { + pEntry = csr_list_remove_head(&pList->ListHead); + pList->Count--; + } + + if (fInterlocked) + csr_ll_unlock(pList); + } + + return pEntry; +} + +tListElem *csr_ll_peek_head(tDblLinkList *pList, bool fInterlocked) +{ + tListElem *pEntry = NULL; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return pEntry; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + if (!csrIsListEmpty(&pList->ListHead)) + pEntry = pList->ListHead.next; + + if (fInterlocked) + csr_ll_unlock(pList); + } + + return pEntry; +} + +void csr_ll_purge(tDblLinkList *pList, bool fInterlocked) +{ + tListElem *pEntry; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + /* Remove everything from the list */ + while ((pEntry = csr_ll_remove_head(pList, LL_ACCESS_NOLOCK))) + ; + + if (fInterlocked) + csr_ll_unlock(pList); + } +} + +bool csr_ll_remove_entry(tDblLinkList *pList, tListElem *pEntryToRemove, + bool fInterlocked) +{ + bool fFound = false; + tListElem *pEntry; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return fFound; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + pEntry = csr_ll_peek_head(pList, LL_ACCESS_NOLOCK); + + /* Have to make sure we don't loop back to the head of the + * list, which will happen if the entry is NOT on the list. + */ + while (pEntry && (pEntry != &pList->ListHead)) { + if (pEntry == pEntryToRemove) { + csr_list_remove_entry(pEntry); + pList->Count--; + + fFound = true; + break; + } + + pEntry = pEntry->next; + } + if (fInterlocked) + csr_ll_unlock(pList); + } + + return fFound; +} + +tListElem *csr_ll_next(tDblLinkList *pList, tListElem *pEntry, + bool fInterlocked) +{ + tListElem *pNextEntry = NULL; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return pNextEntry; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + if (!csrIsListEmpty(&pList->ListHead) + && csr_ll_find_entry(pList, pEntry)) { + pNextEntry = pEntry->next; + /* Make sure we don't walk past the head */ + if (pNextEntry == &pList->ListHead) + pNextEntry = NULL; + } + + if (fInterlocked) + csr_ll_unlock(pList); + } + + return pNextEntry; +} + +tListElem *csr_ll_previous(tDblLinkList *pList, tListElem *pEntry, + bool fInterlocked) +{ + tListElem *pNextEntry = NULL; + + if (!pList) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Error!! pList is Null", __func__); + return pNextEntry; + } + + if (LIST_FLAG_OPEN == pList->Flag) { + if (fInterlocked) + csr_ll_lock(pList); + + if (!csrIsListEmpty(&pList->ListHead) + && csr_ll_find_entry(pList, pEntry)) { + pNextEntry = pEntry->last; + /* Make sure we don't walk past the head */ + if (pNextEntry == &pList->ListHead) + pNextEntry = NULL; + } + + if (fInterlocked) + csr_ll_unlock(pList); + } + + return pNextEntry; +} diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_neighbor_roam.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_neighbor_roam.c new file mode 100644 index 0000000000000000000000000000000000000000..487e43f9f572ba1635c43f8d147772e493499353 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_neighbor_roam.c @@ -0,0 +1,1572 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: csr_neighbor_roam.c + * + * Implementation for the simple roaming algorithm for 802.11r Fast + * transitions and Legacy roaming for Android platform. + */ + +#include "wma_types.h" +#include "csr_inside_api.h" +#include "sme_qos_internal.h" +#include "sme_inside.h" +#include "host_diag_core_event.h" +#include "host_diag_core_log.h" +#include "csr_api.h" +#include "sme_api.h" +#include "csr_neighbor_roam.h" +#include "mac_trace.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_mlme_api.h" + +static const char *lfr_get_config_item_string(uint8_t reason) +{ + switch (reason) { + CASE_RETURN_STRING(REASON_LOOKUP_THRESH_CHANGED); + CASE_RETURN_STRING(REASON_OPPORTUNISTIC_THRESH_DIFF_CHANGED); + CASE_RETURN_STRING(REASON_ROAM_RESCAN_RSSI_DIFF_CHANGED); + CASE_RETURN_STRING(REASON_ROAM_BMISS_FIRST_BCNT_CHANGED); + CASE_RETURN_STRING(REASON_ROAM_BMISS_FINAL_BCNT_CHANGED); + CASE_RETURN_STRING(REASON_ROAM_BEACON_RSSI_WEIGHT_CHANGED); + default: + return "unknown"; + } +} + +static void csr_neighbor_roam_reset_channel_info(tpCsrNeighborRoamChannelInfo + rChInfo); + +void csr_neighbor_roam_state_transition(struct mac_context *mac_ctx, + uint8_t newstate, uint8_t session) +{ + mac_ctx->roam.neighborRoamInfo[session].prevNeighborRoamState = + mac_ctx->roam.neighborRoamInfo[session].neighborRoamState; + mac_ctx->roam.neighborRoamInfo[session].neighborRoamState = newstate; + sme_nofl_debug("NeighborRoam transition: vdev %d %s --> %s", + session, csr_neighbor_roam_state_to_string( + mac_ctx->roam.neighborRoamInfo[session].prevNeighborRoamState), + csr_neighbor_roam_state_to_string(newstate)); +} + +uint8_t *csr_neighbor_roam_state_to_string(uint8_t state) +{ + switch (state) { + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_CLOSED); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_INIT); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_CONNECTED); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_PREAUTHENTICATING); + CASE_RETURN_STRING(eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE); + default: + return "eCSR_NEIGHBOR_ROAM_STATE_UNKNOWN"; + } + +} + +#ifdef FEATURE_WLAN_LFR_METRICS +/** + * csr_neighbor_roam_send_lfr_metric_event() - Send event of LFR metric + * @mac_ctx: Handle returned by mac_open. + * @session_id: Session information + * @bssid: BSSID of attempted AP + * @status: Result of LFR operation + * + * LFR metrics - pre-auth completion metric + * Send the event to supplicant indicating pre-auth result + * + * Return: void + */ + +void csr_neighbor_roam_send_lfr_metric_event( + struct mac_context *mac_ctx, + uint8_t session_id, + tSirMacAddr bssid, + eRoamCmdStatus status) +{ + struct csr_roam_info *roam_info; + + roam_info = qdf_mem_malloc(sizeof(struct csr_roam_info)); + if (roam_info) { + qdf_mem_copy((void *)roam_info->bssid.bytes, + (void *)bssid, sizeof(struct qdf_mac_addr)); + csr_roam_call_callback(mac_ctx, session_id, roam_info, 0, + status, 0); + qdf_mem_free(roam_info); + } +} +#endif + +/** + * csr_neighbor_roam_update_fast_roaming_enabled() - update roaming capability + * + * @mac_ctx: Global MAC context + * @session_id: Session + * @fast_roam_enabled: Is fast roaming enabled on this device? + * This capability can be changed dynamically. + * + * Return: None + */ +QDF_STATUS csr_neighbor_roam_update_fast_roaming_enabled(struct mac_context *mac_ctx, + uint8_t session_id, + const bool fast_roam_enabled) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamControlInfo neighbor_roam_info = + &mac_ctx->roam.neighborRoamInfo[session_id]; + + switch (neighbor_roam_info->neighborRoamState) { + case eCSR_NEIGHBOR_ROAM_STATE_CONNECTED: + qdf_status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_IS_STATUS_ERROR(qdf_status)) + break; + + mlme_set_supplicant_disabled_roaming(mac_ctx->psoc, session_id, + !fast_roam_enabled); + if (fast_roam_enabled) { + csr_post_roam_state_change(mac_ctx, session_id, + ROAM_RSO_STARTED, + REASON_CONNECT); + } else { + csr_post_roam_state_change(mac_ctx, session_id, + ROAM_RSO_STOPPED, + REASON_SUPPLICANT_DISABLED_ROAMING); + } + sme_release_global_lock(&mac_ctx->sme); + break; + case eCSR_NEIGHBOR_ROAM_STATE_INIT: + sme_debug("Currently in INIT state, Nothing to do"); + break; + default: + sme_err("Unexpected state %s, returning failure", + mac_trace_get_neighbour_roam_state + (neighbor_roam_info->neighborRoamState)); + qdf_status = QDF_STATUS_E_FAILURE; + break; + } + return qdf_status; +} +QDF_STATUS csr_neighbor_roam_update_config(struct mac_context *mac_ctx, + uint8_t session_id, uint8_t value, uint8_t reason) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac_ctx->roam.neighborRoamInfo[session_id]; + tpCsrNeighborRoamCfgParams cfg_params; + eCsrNeighborRoamState state; + uint8_t old_value; + + if (!pNeighborRoamInfo) { + sme_err("Invalid Session ID %d", session_id); + return QDF_STATUS_E_FAILURE; + } + cfg_params = &pNeighborRoamInfo->cfgParams; + state = pNeighborRoamInfo->neighborRoamState; + if ((state == eCSR_NEIGHBOR_ROAM_STATE_CONNECTED) || + state == eCSR_NEIGHBOR_ROAM_STATE_INIT) { + switch (reason) { + case REASON_LOOKUP_THRESH_CHANGED: + old_value = cfg_params->neighborLookupThreshold; + cfg_params->neighborLookupThreshold = value; + pNeighborRoamInfo->currentNeighborLookupThreshold = + value; + break; + case REASON_OPPORTUNISTIC_THRESH_DIFF_CHANGED: + old_value = cfg_params->nOpportunisticThresholdDiff; + cfg_params->nOpportunisticThresholdDiff = value; + pNeighborRoamInfo->currentOpportunisticThresholdDiff = + value; + break; + case REASON_ROAM_RESCAN_RSSI_DIFF_CHANGED: + old_value = cfg_params->nRoamRescanRssiDiff; + cfg_params->nRoamRescanRssiDiff = value; + pNeighborRoamInfo->currentRoamRescanRssiDiff = value; + break; + case REASON_ROAM_BMISS_FIRST_BCNT_CHANGED: + old_value = cfg_params->nRoamBmissFirstBcnt; + cfg_params->nRoamBmissFirstBcnt = value; + pNeighborRoamInfo->currentRoamBmissFirstBcnt = value; + break; + case REASON_ROAM_BMISS_FINAL_BCNT_CHANGED: + old_value = cfg_params->nRoamBmissFinalBcnt; + cfg_params->nRoamBmissFinalBcnt = value; + pNeighborRoamInfo->currentRoamBmissFinalBcnt = value; + break; + case REASON_ROAM_BEACON_RSSI_WEIGHT_CHANGED: + old_value = cfg_params->nRoamBeaconRssiWeight; + cfg_params->nRoamBeaconRssiWeight = value; + pNeighborRoamInfo->currentRoamBeaconRssiWeight = value; + break; + default: + sme_debug("Unknown update cfg reason"); + return QDF_STATUS_E_FAILURE; + } + } else { + sme_err("Unexpected state %s, return fail", + mac_trace_get_neighbour_roam_state(state)); + return QDF_STATUS_E_FAILURE; + } + if (state == eCSR_NEIGHBOR_ROAM_STATE_CONNECTED) { + sme_debug("CONNECTED, send update cfg cmd"); + csr_roam_update_cfg(mac_ctx, session_id, reason); + } + sme_debug("LFR config for %s changed from %d to %d", + lfr_get_config_item_string(reason), old_value, value); + return QDF_STATUS_SUCCESS; +} + +/*CleanUP Routines*/ +static void csr_neighbor_roam_reset_channel_info(tpCsrNeighborRoamChannelInfo + rChInfo) +{ + if ((rChInfo->IAPPNeighborListReceived == false) && + (rChInfo->currentChannelListInfo.numOfChannels)) { + rChInfo->currentChanIndex = + CSR_NEIGHBOR_ROAM_INVALID_CHANNEL_INDEX; + rChInfo->currentChannelListInfo.numOfChannels = 0; + if (rChInfo->currentChannelListInfo.freq_list) + qdf_mem_free(rChInfo->currentChannelListInfo.freq_list); + rChInfo->currentChannelListInfo.freq_list = NULL; + } else { + rChInfo->currentChanIndex = 0; + } +} + +/** + * csr_neighbor_roam_reset_connected_state_control_info() + * + * @mac_ctx: Pointer to Global MAC structure + * @sessionId : session id + * + * This function will reset the neighbor roam control info data structures. + * This function should be invoked whenever we move to CONNECTED state from + * any state other than INIT state + * + * Return: None + */ +static void csr_neighbor_roam_reset_connected_state_control_info( + struct mac_context *mac, + uint8_t sessionId) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + + csr_neighbor_roam_reset_channel_info(&pNeighborRoamInfo-> + roamChannelInfo); + csr_neighbor_roam_free_roamable_bss_list(mac, + &pNeighborRoamInfo->roamableAPList); + + /* Do not free up the preauth done list here */ + pNeighborRoamInfo->FTRoamInfo.currentNeighborRptRetryNum = 0; + pNeighborRoamInfo->FTRoamInfo.neighborRptPending = false; + pNeighborRoamInfo->FTRoamInfo.numPreAuthRetries = 0; + pNeighborRoamInfo->FTRoamInfo.preauthRspPending = 0; + pNeighborRoamInfo->uOsRequestedHandoff = 0; + qdf_mem_zero(&pNeighborRoamInfo->handoffReqInfo, + sizeof(tCsrHandoffRequest)); +} + +static void csr_neighbor_roam_reset_report_scan_state_control_info( + struct mac_context *mac, + uint8_t sessionId) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + + qdf_zero_macaddr(&pNeighborRoamInfo->currAPbssid); +#ifdef FEATURE_WLAN_ESE + pNeighborRoamInfo->isESEAssoc = false; + pNeighborRoamInfo->isVOAdmitted = false; + pNeighborRoamInfo->MinQBssLoadRequired = 0; +#endif + + /* Purge roamable AP list */ + csr_neighbor_roam_free_roamable_bss_list(mac, + &pNeighborRoamInfo->roamableAPList); +} + +/** + * csr_neighbor_roam_reset_init_state_control_info() + * + * @mac_ctx: Pointer to Global MAC structure + * @sessionId : session id + * + * This function will reset the neighbor roam control info data structures. + * This function should be invoked whenever we move to CONNECTED state from + * INIT state + * + * Return: None + */ +static void csr_neighbor_roam_reset_init_state_control_info(struct mac_context *mac, + uint8_t sessionId) +{ + csr_neighbor_roam_reset_connected_state_control_info(mac, sessionId); + + /* In addition to the above resets, + * we should clear off the curAPBssId/Session ID in the timers + */ + csr_neighbor_roam_reset_report_scan_state_control_info(mac, sessionId); +} + +#ifdef WLAN_FEATURE_11W +void +csr_update_pmf_cap_from_connected_profile(tCsrRoamConnectedProfile *profile, + struct scan_filter *filter) +{ + if (profile->MFPCapable || profile->MFPEnabled) + filter->pmf_cap = WLAN_PMF_CAPABLE; + if (profile->MFPRequired) + filter->pmf_cap = WLAN_PMF_REQUIRED; +} +#else +void +csr_update_pmf_cap_from_connected_profile(tCsrRoamConnectedProfile *profile, + struct scan_filter *filter) +{} +#endif + +QDF_STATUS +csr_neighbor_roam_get_scan_filter_from_profile(struct mac_context *mac, + struct scan_filter *filter, + uint8_t vdev_id) +{ + tpCsrNeighborRoamControlInfo nbr_roam_info; + tCsrRoamConnectedProfile *profile; + struct roam_ext_params *roam_params; + tCsrChannelInfo *chan_info; + uint8_t num_ch = 0; + enum QDF_OPMODE opmode = QDF_STA_MODE; + + if (!filter) + return QDF_STATUS_E_FAILURE; + if (!CSR_IS_SESSION_VALID(mac, vdev_id)) + return QDF_STATUS_E_FAILURE; + + qdf_mem_zero(filter, sizeof(*filter)); + nbr_roam_info = &mac->roam.neighborRoamInfo[vdev_id]; + profile = &mac->roam.roamSession[vdev_id].connectedProfile; + roam_params = &mac->roam.configParam.roam_params; + + /* only for HDD requested handoff fill in the BSSID in the filter */ + if (nbr_roam_info->uOsRequestedHandoff) { + sme_debug("OS Requested Handoff"); + filter->num_of_bssid = 1; + qdf_mem_copy(filter->bssid_list[0].bytes, + &nbr_roam_info->handoffReqInfo.bssid.bytes, + QDF_MAC_ADDR_SIZE); + } + sme_debug("No of Allowed SSID List:%d", + roam_params->num_ssid_allowed_list); + + if (roam_params->num_ssid_allowed_list) { + csr_copy_ssids_from_roam_params(roam_params, filter); + } else { + filter->num_of_ssid = 1; + + filter->ssid_list[0].length = profile->SSID.length; + if (filter->ssid_list[0].length > WLAN_SSID_MAX_LEN) + filter->ssid_list[0].length = WLAN_SSID_MAX_LEN; + qdf_mem_copy(filter->ssid_list[0].ssid, + profile->SSID.ssId, + filter->ssid_list[0].length); + + sme_debug("Filtering for SSID %.*s,length of SSID = %u", + filter->ssid_list[0].length, + filter->ssid_list[0].ssid, + filter->ssid_list[0].length); + } + + filter->num_of_auth = 1; + filter->auth_type[0] = csr_covert_auth_type_new(profile->AuthType); + filter->num_of_enc_type = 1; + filter->enc_type[0] = + csr_covert_enc_type_new(profile->EncryptionType); + filter->num_of_mc_enc_type = 1; + filter->mc_enc_type[0] = + csr_covert_enc_type_new(profile->mcEncryptionType); + + if (profile->BSSType == eCSR_BSS_TYPE_INFRASTRUCTURE) + filter->bss_type = WLAN_TYPE_BSS; + else if (profile->BSSType == eCSR_BSS_TYPE_IBSS || + profile->BSSType == eCSR_BSS_TYPE_START_IBSS) + filter->bss_type = WLAN_TYPE_IBSS; + else + filter->bss_type = WLAN_TYPE_ANY; + + chan_info = &nbr_roam_info->roamChannelInfo.currentChannelListInfo; + num_ch = chan_info->numOfChannels; + if (num_ch) { + filter->num_of_channels = num_ch; + if (filter->num_of_channels > NUM_CHANNELS) + filter->num_of_channels = NUM_CHANNELS; + qdf_mem_copy(filter->chan_freq_list, chan_info->freq_list, + filter->num_of_channels * + sizeof(filter->chan_freq_list[0])); + } + + if (nbr_roam_info->is11rAssoc) + /* + * MDIE should be added as a part of profile. This should be + * added as a part of filter as well + */ + filter->mobility_domain = profile->mdid.mobility_domain; + + csr_update_pmf_cap_from_connected_profile(profile, filter); + + csr_update_connect_n_roam_cmn_filter(mac, filter, opmode); + + return QDF_STATUS_SUCCESS; +} + +enum band_info csr_get_rf_band(uint8_t channel) +{ + if ((channel >= SIR_11A_CHANNEL_BEGIN) && + (channel <= SIR_11A_CHANNEL_END)) + return BAND_5G; + + if ((channel >= SIR_11B_CHANNEL_BEGIN) && + (channel <= SIR_11B_CHANNEL_END)) + return BAND_2G; + + return BAND_UNKNOWN; +} + +/** + * csr_neighbor_roam_channels_filter_by_current_band() + * + * @mac_ctx: Pointer to Global MAC structure + * @session_id: Session ID + * @input_chan_freq_list: The input channel list + * @input_num_of_ch: The number of channels in input channel list + * @out_chan_freq_list: The output channel list + * @output_num_of_ch: The number of channels in output channel list + * @merged_output_num_of_ch: The final number of channels in the + * output channel list. + * + * This function is used to filter out the channels based on the + * currently associated AP channel + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE otherwise + */ +QDF_STATUS csr_neighbor_roam_channels_filter_by_current_band(struct mac_context * + mac, + uint8_t sessionId, + uint32_t *input_chan_freq_list, + uint8_t inputNumOfChannels, + uint32_t *out_chan_freq_list, + uint8_t * + pMergedOutputNumOfChannels) +{ + uint8_t i = 0; + uint8_t numChannels = 0; + uint32_t curr_ap_op_chan_freq = + mac->roam.neighborRoamInfo[sessionId].curr_ap_op_chan_freq; + /* Check for NULL pointer */ + if (!input_chan_freq_list) + return QDF_STATUS_E_INVAL; + + /* Check for NULL pointer */ + if (!out_chan_freq_list) + return QDF_STATUS_E_INVAL; + + if (inputNumOfChannels > CFG_VALID_CHANNEL_LIST_LEN) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Wrong Number of Input Channels %d", + __func__, inputNumOfChannels); + return QDF_STATUS_E_INVAL; + } + for (i = 0; i < inputNumOfChannels; i++) { + if (WLAN_REG_IS_SAME_BAND_FREQS( + curr_ap_op_chan_freq, + input_chan_freq_list[i])) { + out_chan_freq_list[numChannels] = + input_chan_freq_list[i]; + numChannels++; + } + } + + /* Return final number of channels */ + *pMergedOutputNumOfChannels = numChannels; + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_neighbor_roam_channels_filter_by_current_band() + * + * @mac_ctx: Pointer to Global MAC structure + * @pinput_chan_freq_list: The additional channels to merge in + * to the "merged" channels list. + * @input_num_of_ch: The number of additional channels. + * @out_chan_freq_list: The place to put the "merged" channel list. + * @output_num_of_ch: The original number of channels in the + * "merged" channels list. + * @merged_output_num_of_ch: The final number of channels in the + * "merged" channel list. + * + * This function is used to merge two channel list. + * NB: If called with outputNumOfChannels == 0, this routines simply + * copies the input channel list to the output channel list. if number + * of merged channels are more than 100, num of channels set to 100 + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE otherwise + */ +QDF_STATUS csr_neighbor_roam_merge_channel_lists(struct mac_context *mac, + uint32_t *pinput_chan_freq_list, + uint8_t inputNumOfChannels, + uint32_t *out_chan_freq_list, + uint8_t outputNumOfChannels, + uint8_t * + pMergedOutputNumOfChannels) +{ + uint8_t i = 0; + uint8_t j = 0; + uint8_t numChannels = outputNumOfChannels; + + /* Check for NULL pointer */ + if (!pinput_chan_freq_list) + return QDF_STATUS_E_INVAL; + + /* Check for NULL pointer */ + if (!out_chan_freq_list) + return QDF_STATUS_E_INVAL; + + if (inputNumOfChannels > CFG_VALID_CHANNEL_LIST_LEN) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Wrong Number of Input Channels %d", + __func__, inputNumOfChannels); + return QDF_STATUS_E_INVAL; + } + if (outputNumOfChannels >= CFG_VALID_CHANNEL_LIST_LEN) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: Wrong Number of Output Channels %d", + __func__, outputNumOfChannels); + return QDF_STATUS_E_INVAL; + } + /* Add the "new" channels in the input list to the end of the + * output list. + */ + for (i = 0; i < inputNumOfChannels; i++) { + for (j = 0; j < outputNumOfChannels; j++) { + if (pinput_chan_freq_list[i] + == out_chan_freq_list[j]) + break; + } + if (j == outputNumOfChannels) { + if (pinput_chan_freq_list[i]) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: [INFOLOG] Adding extra %d to Neighbor channel list", + __func__, pinput_chan_freq_list[i]); + out_chan_freq_list[numChannels] = + pinput_chan_freq_list[i]; + numChannels++; + } + } + if (numChannels >= CFG_VALID_CHANNEL_LIST_LEN) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: Merge Neighbor channel list reached Max limit %d", + __func__, numChannels); + break; + } + } + + /* Return final number of channels */ + *pMergedOutputNumOfChannels = numChannels; + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_roam_reset_roam_params - API to reset the roaming parameters + * @mac_ctx: Pointer to the global MAC structure + * + * The BSSID blacklist should not be cleared since it has to + * be used across connections. These parameters will be cleared + * and sent to firmware with with the roaming STOP command. + * + * Return: VOID + */ +void csr_roam_reset_roam_params(struct mac_context *mac_ctx) +{ + struct roam_ext_params *roam_params = NULL; + + /* + * clear all the whitelist parameters and remaining + * needs to be retained across connections. + */ + roam_params = &mac_ctx->roam.configParam.roam_params; + roam_params->num_ssid_allowed_list = 0; + qdf_mem_zero(&roam_params->ssid_allowed_list, + sizeof(tSirMacSSid) * MAX_SSID_ALLOWED_LIST); +} + +#if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) +static void csr_roam_restore_default_config(struct mac_context *mac_ctx, + uint8_t vdev_id) +{ + struct roam_triggers triggers; + + sme_set_roam_config_enable(MAC_HANDLE(mac_ctx), vdev_id, 0); + + triggers.vdev_id = vdev_id; + triggers.trigger_bitmap = wlan_mlme_get_roaming_triggers(mac_ctx->psoc); + sme_debug("Reset roam trigger bitmap to 0x%x", triggers.trigger_bitmap); + sme_set_roam_triggers(MAC_HANDLE(mac_ctx), &triggers); + sme_roam_control_restore_default_config(MAC_HANDLE(mac_ctx), + vdev_id); +} +#else +static void csr_roam_restore_default_config(struct mac_context *mac_ctx, + uint8_t vdev_id) +{ +} +#endif + +/** + * csr_neighbor_roam_indicate_disconnect() - To indicate disconnect + * @mac: The handle returned by mac_open + * @sessionId: CSR session id that got disconnected + * + * This function is called by CSR as soon as the station disconnects + * from the AP. This function does the necessary cleanup of neighbor roam + * data structures. Neighbor roam state transitions to INIT state whenever + * this function is called except if the current state is REASSOCIATING + * + * Return: QDF_STATUS + */ +QDF_STATUS csr_neighbor_roam_indicate_disconnect(struct mac_context *mac, + uint8_t sessionId) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + tCsrRoamConnectedProfile *pPrevProfile = + &pNeighborRoamInfo->prevConnProfile; + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + struct csr_roam_session *roam_session = NULL; + + if (!pSession) { + sme_err("pSession is NULL"); + return QDF_STATUS_E_FAILURE; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Disconn ind on session %d in state %d from bss :" + QDF_MAC_ADDR_FMT), sessionId, + pNeighborRoamInfo->neighborRoamState, + QDF_MAC_ADDR_REF(pSession->connectedProfile.bssid.bytes)); + /* + * Free the current previous profile and move + * the current profile to prev profile. + */ + csr_roam_free_connect_profile(pPrevProfile); + csr_roam_copy_connect_profile(mac, sessionId, pPrevProfile); + + if (pSession) { + roam_session = &mac->roam.roamSession[sessionId]; + if (pSession->pCurRoamProfile && (QDF_STA_MODE != + roam_session->pCurRoamProfile->csrPersona)) { + sme_err("Ignore disconn ind rcvd from nonSTA persona sessionId: %d csrPersonna %d", + sessionId, + (int)roam_session->pCurRoamProfile->csrPersona); + return QDF_STATUS_SUCCESS; + } +#ifdef FEATURE_WLAN_ESE + if (pSession->connectedProfile.isESEAssoc) { + qdf_mem_copy(&pSession->prevApSSID, + &pSession->connectedProfile.SSID, + sizeof(tSirMacSSid)); + qdf_copy_macaddr(&pSession->prevApBssid, + &pSession->connectedProfile.bssid); + pSession->isPrevApInfoValid = true; + pSession->roamTS1 = qdf_mc_timer_get_system_time(); + } +#endif + } + + switch (pNeighborRoamInfo->neighborRoamState) { + case eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING: + /* + * Stop scan and neighbor refresh timers. + * These are indeed not required when we'r in reassoc state. + */ + if (!CSR_IS_ROAM_SUBSTATE_DISASSOC_HO(mac, sessionId)) { + /* + * Disconnect indication during Disassoc Handoff + * sub-state is received when we are trying to + * disconnect with the old AP during roam. + * BUT, if receive a disconnect indication outside of + * Disassoc Handoff sub-state, then it means that + * this is a genuine disconnect and we need to clean up. + * Otherwise, we will be stuck in reassoc state which'll + * in-turn block scans. + */ + csr_neighbor_roam_state_transition(mac, + eCSR_NEIGHBOR_ROAM_STATE_INIT, sessionId); + pNeighborRoamInfo->roamChannelInfo. + IAPPNeighborListReceived = false; + pNeighborRoamInfo->uOsRequestedHandoff = 0; + } + break; + + case eCSR_NEIGHBOR_ROAM_STATE_INIT: + csr_neighbor_roam_reset_init_state_control_info(mac, + sessionId); + break; + + case eCSR_NEIGHBOR_ROAM_STATE_CONNECTED: + csr_neighbor_roam_state_transition(mac, + eCSR_NEIGHBOR_ROAM_STATE_INIT, sessionId); + pNeighborRoamInfo->roamChannelInfo.IAPPNeighborListReceived = + false; + csr_neighbor_roam_reset_connected_state_control_info(mac, + sessionId); + break; + + case eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE: + /* Stop pre-auth to reassoc interval timer */ + qdf_mc_timer_stop(&pSession->ftSmeContext. + preAuthReassocIntvlTimer); + case eCSR_NEIGHBOR_ROAM_STATE_PREAUTHENTICATING: + csr_neighbor_roam_state_transition(mac, + eCSR_NEIGHBOR_ROAM_STATE_INIT, sessionId); + pNeighborRoamInfo->roamChannelInfo.IAPPNeighborListReceived = + false; + csr_neighbor_roam_reset_preauth_control_info(mac, sessionId); + csr_neighbor_roam_reset_report_scan_state_control_info(mac, + sessionId); + break; + default: + sme_debug("Received disconnect event in state %s", + mac_trace_get_neighbour_roam_state( + pNeighborRoamInfo->neighborRoamState)); + sme_debug("Transit to INIT state"); + csr_neighbor_roam_state_transition(mac, + eCSR_NEIGHBOR_ROAM_STATE_INIT, sessionId); + pNeighborRoamInfo->roamChannelInfo. + IAPPNeighborListReceived = false; + pNeighborRoamInfo->uOsRequestedHandoff = 0; + break; + } + + /* + * clear the roaming parameters that are per connection. + * For a new connection, they have to be programmed again. + */ + if (!csr_neighbor_middle_of_roaming(mac, sessionId)) { + csr_roam_reset_roam_params(mac); + csr_roam_restore_default_config(mac, sessionId); + } + + /*Inform the Firmware to STOP Scanning as the host has a disconnect. */ + if (csr_roam_is_sta_mode(mac, sessionId)) + csr_post_roam_state_change(mac, sessionId, ROAM_DEINIT, + REASON_DISCONNECTED); + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_neighbor_roam_info_ctx_init() - Initialize neighbor roam struct + * @mac: mac context + * @session_id: Session Id + * + * This initializes all the necessary data structures related to the + * associated AP. + * + * Return: QDF status + */ +static void csr_neighbor_roam_info_ctx_init(struct mac_context *mac, + uint8_t session_id) +{ + tpCsrNeighborRoamControlInfo ngbr_roam_info = + &mac->roam.neighborRoamInfo[session_id]; + struct csr_roam_session *session = &mac->roam.roamSession[session_id]; + int init_ft_flag = false; + + csr_init_occupied_channels_list(mac, session_id); + csr_neighbor_roam_state_transition(mac, + eCSR_NEIGHBOR_ROAM_STATE_CONNECTED, session_id); + + qdf_copy_macaddr(&ngbr_roam_info->currAPbssid, + &session->connectedProfile.bssid); + ngbr_roam_info->curr_ap_op_chan_freq = + session->connectedProfile.op_freq; + ngbr_roam_info->currentNeighborLookupThreshold = + ngbr_roam_info->cfgParams.neighborLookupThreshold; + ngbr_roam_info->currentOpportunisticThresholdDiff = + ngbr_roam_info->cfgParams.nOpportunisticThresholdDiff; + ngbr_roam_info->currentRoamRescanRssiDiff = + ngbr_roam_info->cfgParams.nRoamRescanRssiDiff; + ngbr_roam_info->currentRoamBmissFirstBcnt = + ngbr_roam_info->cfgParams.nRoamBmissFirstBcnt; + ngbr_roam_info->currentRoamBmissFinalBcnt = + ngbr_roam_info->cfgParams.nRoamBmissFinalBcnt; + ngbr_roam_info->currentRoamBeaconRssiWeight = + ngbr_roam_info->cfgParams.nRoamBeaconRssiWeight; + + /* + * Now we can clear the preauthDone that + * was saved as we are connected afresh + */ + csr_neighbor_roam_free_roamable_bss_list(mac, + &ngbr_roam_info->FTRoamInfo.preAuthDoneList); + + /* Based on the auth scheme tell if we are 11r */ + if (csr_is_auth_type11r + (mac, session->connectedProfile.AuthType, + session->connectedProfile.mdid.mdie_present)) { + if (mac->mlme_cfg->lfr.fast_transition_enabled) + init_ft_flag = true; + ngbr_roam_info->is11rAssoc = true; + } else + ngbr_roam_info->is11rAssoc = false; + +#ifdef FEATURE_WLAN_ESE + /* Based on the auth scheme tell if we are 11r */ + if (session->connectedProfile.isESEAssoc) { + if (mac->mlme_cfg->lfr.fast_transition_enabled) + init_ft_flag = true; + ngbr_roam_info->isESEAssoc = true; + } else + ngbr_roam_info->isESEAssoc = false; +#endif + /* If "FastRoamEnabled" ini is enabled */ + if (csr_roam_is_fast_roam_enabled(mac, session_id)) + init_ft_flag = true; + + if (init_ft_flag) { + /* Initialize all the data needed for the 11r FT Preauth */ + ngbr_roam_info->FTRoamInfo.currentNeighborRptRetryNum = 0; + csr_neighbor_roam_purge_preauth_failed_list(mac); + } + + if (csr_roam_is_roam_offload_scan_enabled(mac)) { + /* + * Store the current PMK info of the AP + * to the single pmk global cache if the BSS allows + * single pmk roaming capable. + */ + csr_store_sae_single_pmk_to_global_cache(mac, session, + session_id); + + /* + * If this is not a INFRA type BSS, then do not send the command + * down to firmware.Do not send the START command for + * other session connections. + */ + if (!csr_roam_is_sta_mode(mac, session_id)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Wrong Mode %d", + session->connectedProfile.BSSType); + return; + } + ngbr_roam_info->uOsRequestedHandoff = 0; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (session->roam_synch_in_progress) { + if (mac->roam.pReassocResp) { + qdf_mem_free(mac->roam.pReassocResp); + mac->roam.pReassocResp = NULL; + } + } else +#endif + { + csr_post_roam_state_change(mac, session_id, + ROAM_RSO_STARTED, + REASON_CTX_INIT); + + } + } +} + +/** + * csr_neighbor_roam_indicate_connect() + * @mac: mac context + * @session_id: Session Id + * @qdf_status: QDF status + * + * This function is called by CSR as soon as the station connects to an AP. + * This initializes all the necessary data structures related to the + * associated AP and transitions the state to CONNECTED state + * + * Return: QDF status + */ +QDF_STATUS csr_neighbor_roam_indicate_connect( + struct mac_context *mac, uint8_t session_id, + QDF_STATUS qdf_status) +{ + tpCsrNeighborRoamControlInfo ngbr_roam_info = + &mac->roam.neighborRoamInfo[session_id]; + struct csr_roam_session *session = &mac->roam.roamSession[session_id]; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + struct csr_roam_info *roam_info; +#endif + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* if session id invalid then we need return failure */ + if (!ngbr_roam_info || !CSR_IS_SESSION_VALID(mac, session_id) + || (!mac->roam.roamSession[session_id].pCurRoamProfile)) { + return QDF_STATUS_E_FAILURE; + } + + sme_debug("Connect ind, vdev id %d in state %s", + session_id, mac_trace_get_neighbour_roam_state( + ngbr_roam_info->neighborRoamState)); + + /* Bail out if this is NOT a STA persona */ + if (mac->roam.roamSession[session_id].pCurRoamProfile->csrPersona != + QDF_STA_MODE) { + sme_debug("Ignoring Connect ind received from a non STA. session_id: %d, csrPersonna %d", + session_id, + (int)session->pCurRoamProfile->csrPersona); + return QDF_STATUS_SUCCESS; + } + /* if a concurrent session is running */ + if ((false == + CSR_IS_FASTROAM_IN_CONCURRENCY_INI_FEATURE_ENABLED(mac)) && + (csr_is_concurrent_session_running(mac))) { + sme_err("Ignoring Connect ind. received in multisession %d", + csr_is_concurrent_session_running(mac)); + return QDF_STATUS_SUCCESS; + } +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (session->roam_synch_in_progress && + (eSIR_ROAM_AUTH_STATUS_AUTHENTICATED == + session->roam_synch_data->authStatus)) { + sme_debug("LFR3: Authenticated"); + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + qdf_copy_macaddr(&roam_info->peerMac, + &session->connectedProfile.bssid); + roam_info->roamSynchInProgress = + session->roam_synch_in_progress; + csr_roam_call_callback(mac, session_id, roam_info, 0, + eCSR_ROAM_SET_KEY_COMPLETE, + eCSR_ROAM_RESULT_AUTHENTICATED); + csr_neighbor_roam_reset_init_state_control_info(mac, + session_id); + csr_neighbor_roam_info_ctx_init(mac, session_id); + qdf_mem_free(roam_info); + return status; + } +#endif + + switch (ngbr_roam_info->neighborRoamState) { + case eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING: + if (QDF_STATUS_SUCCESS != qdf_status) { + /** + * Just transition the state to INIT state.Rest of the + * clean up happens when we get next connect indication + */ + csr_neighbor_roam_state_transition(mac, + eCSR_NEIGHBOR_ROAM_STATE_INIT, session_id); + ngbr_roam_info->roamChannelInfo. + IAPPNeighborListReceived = false; + ngbr_roam_info->uOsRequestedHandoff = 0; + break; + } + /* Fall through if the status is SUCCESS */ + case eCSR_NEIGHBOR_ROAM_STATE_INIT: + /* Reset all the data structures here */ + csr_neighbor_roam_reset_init_state_control_info(mac, + session_id); + csr_neighbor_roam_info_ctx_init(mac, session_id); + break; + default: + sme_err("Connect evt received in invalid state %s Ignoring", + mac_trace_get_neighbour_roam_state( + ngbr_roam_info->neighborRoamState)); + break; + } + return status; +} + +/* + * csr_neighbor_roam_init11r_assoc_info - + * This function initializes 11r related neighbor roam data structures + * + * @mac: The handle returned by mac_open. + * + * return QDF_STATUS_SUCCESS on success, corresponding error code otherwise + */ +static QDF_STATUS csr_neighbor_roam_init11r_assoc_info(struct mac_context *mac) +{ + QDF_STATUS status; + uint8_t i; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = NULL; + tpCsr11rAssocNeighborInfo pFTRoamInfo = NULL; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + pNeighborRoamInfo = &mac->roam.neighborRoamInfo[i]; + pFTRoamInfo = &pNeighborRoamInfo->FTRoamInfo; + + pNeighborRoamInfo->is11rAssoc = false; + + pFTRoamInfo->neighborReportTimeout = + CSR_NEIGHBOR_ROAM_REPORT_QUERY_TIMEOUT; + pFTRoamInfo->neighborRptPending = false; + pFTRoamInfo->preauthRspPending = false; + + pFTRoamInfo->currentNeighborRptRetryNum = 0; + + status = csr_ll_open(&pFTRoamInfo->preAuthDoneList); + if (QDF_STATUS_SUCCESS != status) { + sme_err("LL Open of preauth done AP List failed"); + return QDF_STATUS_E_RESOURCES; + } + } + return status; +} + +/* + * csr_neighbor_roam_init() - + * This function initializes neighbor roam data structures + * + * @mac: The handle returned by mac_open. + * sessionId: Session identifier + * + * Return QDF_STATUS_SUCCESS on success, corresponding error code otherwise + */ +QDF_STATUS csr_neighbor_roam_init(struct mac_context *mac, uint8_t sessionId) +{ + QDF_STATUS status; + tCsrChannelInfo *specific_chan_info; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + + pNeighborRoamInfo->neighborRoamState = eCSR_NEIGHBOR_ROAM_STATE_CLOSED; + pNeighborRoamInfo->prevNeighborRoamState = + eCSR_NEIGHBOR_ROAM_STATE_CLOSED; + pNeighborRoamInfo->cfgParams.maxChannelScanTime = + mac->mlme_cfg->lfr.neighbor_scan_max_chan_time; + pNeighborRoamInfo->cfgParams.minChannelScanTime = + mac->mlme_cfg->lfr.neighbor_scan_min_chan_time; + pNeighborRoamInfo->cfgParams.neighborLookupThreshold = + mac->mlme_cfg->lfr.neighbor_lookup_rssi_threshold; + pNeighborRoamInfo->cfgParams.rssi_thresh_offset_5g = + mac->mlme_cfg->lfr.rssi_threshold_offset_5g; + pNeighborRoamInfo->cfgParams.delay_before_vdev_stop = + mac->mlme_cfg->lfr.delay_before_vdev_stop; + pNeighborRoamInfo->cfgParams.nOpportunisticThresholdDiff = + mac->mlme_cfg->lfr.opportunistic_scan_threshold_diff; + pNeighborRoamInfo->cfgParams.nRoamRescanRssiDiff = + mac->mlme_cfg->lfr.roam_rescan_rssi_diff; + pNeighborRoamInfo->cfgParams.nRoamBmissFirstBcnt = + mac->mlme_cfg->lfr.roam_bmiss_first_bcnt; + pNeighborRoamInfo->cfgParams.nRoamBmissFinalBcnt = + mac->mlme_cfg->lfr.roam_bmiss_final_bcnt; + pNeighborRoamInfo->cfgParams.nRoamBeaconRssiWeight = + mac->mlme_cfg->lfr.roam_beacon_rssi_weight; + pNeighborRoamInfo->cfgParams.neighborScanPeriod = + mac->mlme_cfg->lfr.neighbor_scan_timer_period; + pNeighborRoamInfo->cfgParams.neighbor_scan_min_period = + mac->mlme_cfg->lfr.neighbor_scan_min_timer_period; + pNeighborRoamInfo->cfgParams.neighborResultsRefreshPeriod = + mac->mlme_cfg->lfr.neighbor_scan_results_refresh_period; + pNeighborRoamInfo->cfgParams.emptyScanRefreshPeriod = + mac->mlme_cfg->lfr.empty_scan_refresh_period; + pNeighborRoamInfo->cfgParams.full_roam_scan_period = + mac->mlme_cfg->lfr.roam_full_scan_period; + pNeighborRoamInfo->cfgParams.enable_scoring_for_roam = + mac->mlme_cfg->scoring.enable_scoring_for_roam; + pNeighborRoamInfo->cfgParams.roam_scan_n_probes = + mac->mlme_cfg->lfr.roam_scan_n_probes; + pNeighborRoamInfo->cfgParams.roam_scan_home_away_time = + mac->mlme_cfg->lfr.roam_scan_home_away_time; + pNeighborRoamInfo->cfgParams.roam_scan_inactivity_time = + mac->mlme_cfg->lfr.roam_scan_inactivity_time; + pNeighborRoamInfo->cfgParams.roam_inactive_data_packet_count = + mac->mlme_cfg->lfr.roam_inactive_data_packet_count; + pNeighborRoamInfo->cfgParams.roam_scan_period_after_inactivity = + mac->mlme_cfg->lfr.roam_scan_period_after_inactivity; + + specific_chan_info = &pNeighborRoamInfo->cfgParams.specific_chan_info; + specific_chan_info->numOfChannels = + mac->mlme_cfg->lfr.neighbor_scan_channel_list_num; + sme_debug("number of channels: %u", specific_chan_info->numOfChannels); + if (specific_chan_info->numOfChannels != 0) { + specific_chan_info->freq_list = + qdf_mem_malloc(sizeof(uint32_t) * + specific_chan_info->numOfChannels); + if (!specific_chan_info->freq_list) { + specific_chan_info->numOfChannels = 0; + return QDF_STATUS_E_NOMEM; + } + + } else { + specific_chan_info->freq_list = NULL; + } + + /* Update the roam global structure from CFG */ + sme_chan_to_freq_list(mac->pdev, + specific_chan_info->freq_list, + mac->mlme_cfg->lfr.neighbor_scan_channel_list, + mac->mlme_cfg->lfr. + neighbor_scan_channel_list_num); + + pNeighborRoamInfo->cfgParams.hi_rssi_scan_max_count = + mac->mlme_cfg->lfr.roam_scan_hi_rssi_maxcount; + pNeighborRoamInfo->cfgParams.hi_rssi_scan_rssi_delta = + mac->mlme_cfg->lfr.roam_scan_hi_rssi_delta; + pNeighborRoamInfo->cfgParams.hi_rssi_scan_delay = + mac->mlme_cfg->lfr.roam_scan_hi_rssi_delay; + pNeighborRoamInfo->cfgParams.hi_rssi_scan_rssi_ub = + mac->mlme_cfg->lfr.roam_scan_hi_rssi_ub; + pNeighborRoamInfo->cfgParams.roam_rssi_diff = + mac->mlme_cfg->lfr.roam_rssi_diff; + + qdf_zero_macaddr(&pNeighborRoamInfo->currAPbssid); + pNeighborRoamInfo->currentNeighborLookupThreshold = + pNeighborRoamInfo->cfgParams.neighborLookupThreshold; + pNeighborRoamInfo->currentOpportunisticThresholdDiff = + pNeighborRoamInfo->cfgParams.nOpportunisticThresholdDiff; + pNeighborRoamInfo->currentRoamRescanRssiDiff = + pNeighborRoamInfo->cfgParams.nRoamRescanRssiDiff; + pNeighborRoamInfo->currentRoamBmissFirstBcnt = + pNeighborRoamInfo->cfgParams.nRoamBmissFirstBcnt; + pNeighborRoamInfo->currentRoamBmissFinalBcnt = + pNeighborRoamInfo->cfgParams.nRoamBmissFinalBcnt; + pNeighborRoamInfo->currentRoamBeaconRssiWeight = + pNeighborRoamInfo->cfgParams.nRoamBeaconRssiWeight; + qdf_mem_zero(&pNeighborRoamInfo->prevConnProfile, + sizeof(tCsrRoamConnectedProfile)); + + status = csr_ll_open(&pNeighborRoamInfo->roamableAPList); + if (QDF_STATUS_SUCCESS != status) { + sme_err("LL Open of roam able AP List failed"); + qdf_mem_free(specific_chan_info->freq_list); + specific_chan_info->freq_list = NULL; + specific_chan_info->numOfChannels = 0; + return QDF_STATUS_E_RESOURCES; + } + + pNeighborRoamInfo->roamChannelInfo.currentChanIndex = + CSR_NEIGHBOR_ROAM_INVALID_CHANNEL_INDEX; + pNeighborRoamInfo->roamChannelInfo.currentChannelListInfo. + numOfChannels = 0; + pNeighborRoamInfo->roamChannelInfo.currentChannelListInfo.freq_list = + NULL; + pNeighborRoamInfo->roamChannelInfo.IAPPNeighborListReceived = false; + + status = csr_neighbor_roam_init11r_assoc_info(mac); + if (QDF_STATUS_SUCCESS != status) { + sme_err("LL Open of roam able AP List failed"); + specific_chan_info->freq_list = NULL; + specific_chan_info->numOfChannels = 0; + csr_ll_close(&pNeighborRoamInfo->roamableAPList); + return QDF_STATUS_E_RESOURCES; + } + + csr_neighbor_roam_state_transition(mac, + eCSR_NEIGHBOR_ROAM_STATE_INIT, sessionId); + pNeighborRoamInfo->roamChannelInfo.IAPPNeighborListReceived = false; + pNeighborRoamInfo->uOsRequestedHandoff = 0; + /* Set the Last Sent Cmd as RSO_STOP */ + pNeighborRoamInfo->last_sent_cmd = ROAM_SCAN_OFFLOAD_STOP; + return QDF_STATUS_SUCCESS; +} + +/* + * csr_neighbor_roam_close() - + * This function closes/frees all the neighbor roam data structures + * + * @mac: The handle returned by mac_open. + * @sessionId: Session identifier + * + * Return VOID + */ +void csr_neighbor_roam_close(struct mac_context *mac, uint8_t sessionId) +{ + tCsrChannelInfo *current_channel_list_info; + tCsrNeighborRoamCfgParams *cfg_params; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + + if (eCSR_NEIGHBOR_ROAM_STATE_CLOSED == + pNeighborRoamInfo->neighborRoamState) { + sme_warn("Neighbor Roam Algorithm Already Closed"); + return; + } + cfg_params = &pNeighborRoamInfo->cfgParams; + if (cfg_params->specific_chan_info.freq_list) + qdf_mem_free(cfg_params->specific_chan_info.freq_list); + pNeighborRoamInfo->cfgParams.specific_chan_info.freq_list = NULL; + pNeighborRoamInfo->cfgParams.specific_chan_info.numOfChannels = 0; + + /* Should free up the nodes in the list before closing the + * double Linked list + */ + csr_neighbor_roam_free_roamable_bss_list(mac, + &pNeighborRoamInfo->roamableAPList); + csr_ll_close(&pNeighborRoamInfo->roamableAPList); + + current_channel_list_info = + &pNeighborRoamInfo->roamChannelInfo.currentChannelListInfo; + if (current_channel_list_info->freq_list) + qdf_mem_free(current_channel_list_info->freq_list); + + current_channel_list_info->freq_list = NULL; + pNeighborRoamInfo->roamChannelInfo.currentChanIndex = + CSR_NEIGHBOR_ROAM_INVALID_CHANNEL_INDEX; + current_channel_list_info->numOfChannels = 0; + pNeighborRoamInfo->roamChannelInfo.IAPPNeighborListReceived = false; + + /* Free the profile.. */ + csr_release_profile(mac, &pNeighborRoamInfo->csrNeighborRoamProfile); + csr_roam_free_connect_profile(&pNeighborRoamInfo->prevConnProfile); + pNeighborRoamInfo->FTRoamInfo.currentNeighborRptRetryNum = 0; + csr_neighbor_roam_free_roamable_bss_list(mac, + &pNeighborRoamInfo->FTRoamInfo. + preAuthDoneList); + csr_ll_close(&pNeighborRoamInfo->FTRoamInfo.preAuthDoneList); + + csr_neighbor_roam_state_transition(mac, + eCSR_NEIGHBOR_ROAM_STATE_CLOSED, sessionId); + +} + +/** + * csr_neighbor_roam_is11r_assoc() - Check if association type is 11R + * @mac_ctx: MAC Global context + * @session_id: Session ID + * + * Return: true if 11r Association, false otherwise. + */ +bool csr_neighbor_roam_is11r_assoc(struct mac_context *mac_ctx, uint8_t session_id) +{ + return mac_ctx->roam.neighborRoamInfo[session_id].is11rAssoc; +} + +/* + * csr_neighbor_middle_of_roaming() - + * This function returns true if STA is in the middle of roaming states + * + * @halHandle: The handle from HDD context. + * @sessionId: Session identifier + * + * Return bool + */ +bool csr_neighbor_middle_of_roaming(struct mac_context *mac, uint8_t sessionId) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + bool val = (eCSR_NEIGHBOR_ROAM_STATE_REASSOCIATING == + pNeighborRoamInfo->neighborRoamState) || + (eCSR_NEIGHBOR_ROAM_STATE_PREAUTHENTICATING == + pNeighborRoamInfo->neighborRoamState) || + (eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE == + pNeighborRoamInfo->neighborRoamState); + return val; +} + +/** + * csr_neighbor_roam_process_handoff_req - Processes handoff request + * + * @mac_ctx Pointer to mac context + * @session_id SME session id + * + * This function is called start with the handoff process. First do a + * SSID scan for the BSSID provided. + * + * Return: status + */ +static QDF_STATUS csr_neighbor_roam_process_handoff_req( + struct mac_context *mac_ctx, + uint8_t session_id) +{ + tpCsrNeighborRoamControlInfo roam_ctrl_info = + &mac_ctx->roam.neighborRoamInfo[session_id]; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t roam_id; + struct csr_roam_profile *profile = NULL; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + uint8_t i = 0; + uint8_t roam_now = 0; + uint8_t roamable_ap_count = 0; + struct scan_filter *scan_filter; + tScanResultHandle scan_result; + + if (!session) { + sme_err("session is NULL"); + return QDF_STATUS_E_FAILURE; + } + + roam_id = GET_NEXT_ROAM_ID(&mac_ctx->roam); + profile = qdf_mem_malloc(sizeof(struct csr_roam_profile)); + if (!profile) + return QDF_STATUS_E_NOMEM; + + status = + csr_roam_copy_profile(mac_ctx, profile, + session->pCurRoamProfile); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Profile copy failed"); + goto end; + } + /* Add the BSSID & Channel */ + profile->BSSIDs.numOfBSSIDs = 1; + if (!profile->BSSIDs.bssid) { + profile->BSSIDs.bssid = + qdf_mem_malloc(sizeof(tSirMacAddr) * + profile->BSSIDs.numOfBSSIDs); + if (!profile->BSSIDs.bssid) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + } + + /* Populate the BSSID from handoff info received from HDD */ + for (i = 0; i < profile->BSSIDs.numOfBSSIDs; i++) { + qdf_copy_macaddr(&profile->BSSIDs.bssid[i], + &roam_ctrl_info->handoffReqInfo.bssid); + } + + profile->ChannelInfo.numOfChannels = 1; + if (!profile->ChannelInfo.freq_list) { + profile->ChannelInfo.freq_list = + qdf_mem_malloc(sizeof(*profile->ChannelInfo.freq_list) * + profile->ChannelInfo.numOfChannels); + if (!profile->ChannelInfo.freq_list) { + profile->ChannelInfo.numOfChannels = 0; + status = QDF_STATUS_E_NOMEM; + goto end; + } + } + + profile->ChannelInfo.freq_list[0] = + roam_ctrl_info->handoffReqInfo.ch_freq; + + /* + * For User space connect requests, the scan has already been done. + * So, check if the BSS descriptor exists in the scan cache and + * proceed with the handoff instead of a redundant scan again. + */ + if (roam_ctrl_info->handoffReqInfo.src == CONNECT_CMD_USERSPACE) { + sme_debug("Connect cmd with bssid within same ESS"); + scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); + if (!scan_filter) { + status = QDF_STATUS_E_NOMEM; + goto end; + } + status = csr_neighbor_roam_get_scan_filter_from_profile(mac_ctx, + scan_filter, + session_id); + sme_debug("Filter creation status: %d", status); + status = csr_scan_get_result(mac_ctx, scan_filter, + &scan_result); + qdf_mem_free(scan_filter); + csr_neighbor_roam_process_scan_results(mac_ctx, session_id, + &scan_result); + roamable_ap_count = csr_ll_count( + &roam_ctrl_info->roamableAPList); + sme_debug("roam_now=%d, roamable_ap_count=%d", + roam_now, roamable_ap_count); + } + if (roam_now && roamable_ap_count) { + csr_neighbor_roam_trigger_handoff(mac_ctx, session_id); + } else { + status = csr_scan_for_ssid(mac_ctx, session_id, profile, + roam_id, false); + if (status != QDF_STATUS_SUCCESS) + sme_err("SSID scan failed"); + } + +end: + if (profile) { + csr_release_profile(mac_ctx, profile); + qdf_mem_free(profile); + } + + return status; +} + +/* + * csr_neighbor_roam_sssid_scan_done() - + * This function is called once SSID scan is done. If SSID scan failed + * to find our candidate add an entry to csr scan cache ourself before starting + * the handoff process + + * @mac: The handle returned by mac_open. + * @session_id SME session id + * + * Return QDF_STATUS_SUCCESS on success, corresponding error code otherwise + */ +QDF_STATUS csr_neighbor_roam_sssid_scan_done(struct mac_context *mac, + uint8_t sessionId, QDF_STATUS status) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + QDF_STATUS hstatus; + + /* we must be in connected state, if not ignore it */ + if (eCSR_NEIGHBOR_ROAM_STATE_CONNECTED != + pNeighborRoamInfo->neighborRoamState) { + sme_err("Received in not CONNECTED state. Ignore it"); + return QDF_STATUS_E_FAILURE; + } + /* if SSID scan failed to find our candidate add an entry to + * csr scan cache ourself + */ + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("Add an entry to csr scan cache"); + hstatus = csr_scan_create_entry_in_scan_cache(mac, sessionId, + pNeighborRoamInfo->handoffReqInfo.bssid, + pNeighborRoamInfo->handoffReqInfo.ch_freq); + if (QDF_STATUS_SUCCESS != hstatus) { + sme_err( + "csr_scan_create_entry_in_scan_cache failed with status %d", + hstatus); + return QDF_STATUS_E_FAILURE; + } + } + + /* Now we have completed scanning for the candidate provided by HDD. + * Let move on to HO + */ + hstatus = csr_neighbor_roam_process_scan_complete(mac, sessionId); + + if (QDF_STATUS_SUCCESS != hstatus) { + sme_err("Neighbor scan process complete failed with status %d", + hstatus); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + + +/** + * csr_neighbor_roam_handoff_req_hdlr - Processes handoff request + * @mac_ctx Pointer to mac context + * @msg message sent to HDD + * + * This function is called by CSR as soon as it gets a handoff request + * to SME via MC thread + * + * Return: status + */ +QDF_STATUS csr_neighbor_roam_handoff_req_hdlr( + struct mac_context *mac_ctx, void *msg) +{ + tAniHandoffReq *handoff_req = (tAniHandoffReq *) msg; + uint32_t session_id = handoff_req->sessionId; + tpCsrNeighborRoamControlInfo roam_ctrl_info = + &mac_ctx->roam.neighborRoamInfo[session_id]; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* we must be in connected state, if not ignore it */ + if (eCSR_NEIGHBOR_ROAM_STATE_CONNECTED != + roam_ctrl_info->neighborRoamState) { + sme_err("Received in not CONNECTED state. Ignore it"); + return QDF_STATUS_E_FAILURE; + } + + /* save the handoff info came from HDD as part of the reassoc req */ + handoff_req = (tAniHandoffReq *) msg; + if (!handoff_req) { + sme_err("Received msg is NULL"); + return QDF_STATUS_E_FAILURE; + } + + /* sanity check */ + if (!qdf_mem_cmp(handoff_req->bssid, + roam_ctrl_info->currAPbssid.bytes, + sizeof(tSirMacAddr))) { + sme_err("Received req has same BSSID as current AP!!"); + return QDF_STATUS_E_FAILURE; + } + roam_ctrl_info->handoffReqInfo.ch_freq = handoff_req->ch_freq; + roam_ctrl_info->handoffReqInfo.src = + handoff_req->handoff_src; + qdf_mem_copy(&roam_ctrl_info->handoffReqInfo.bssid.bytes, + &handoff_req->bssid, QDF_MAC_ADDR_SIZE); + roam_ctrl_info->uOsRequestedHandoff = 1; + + status = csr_post_roam_state_change(mac_ctx, session_id, + ROAM_RSO_STOPPED, + REASON_OS_REQUESTED_ROAMING_NOW); + if (QDF_STATUS_SUCCESS != status) { + sme_err("ROAM: RSO stop failed"); + roam_ctrl_info->uOsRequestedHandoff = 0; + } + return status; +} + +/** + * csr_neighbor_roam_proceed_with_handoff_req() + * + * @mac_ctx: Pointer to Global MAC structure + * @session_id: Session ID + * + * This function is called by CSR as soon as it gets rsp back for + * ROAM_SCAN_OFFLOAD_STOP with reason REASON_OS_REQUESTED_ROAMING_NOW + * + * Return: QDF_STATUS_SUCCESS on success, corresponding error code otherwise + */ +QDF_STATUS csr_neighbor_roam_proceed_with_handoff_req(struct mac_context *mac, + uint8_t sessionId) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + QDF_STATUS status = QDF_STATUS_SUCCESS; + /* we must be in connected state, if not ignore it */ + if ((eCSR_NEIGHBOR_ROAM_STATE_CONNECTED != + pNeighborRoamInfo->neighborRoamState) + || (!pNeighborRoamInfo->uOsRequestedHandoff)) { + sme_err( + "Received in not CONNECTED state(%d) or uOsRequestedHandoff(%d) is not set. Ignore it", + pNeighborRoamInfo->neighborRoamState, + pNeighborRoamInfo->uOsRequestedHandoff); + status = QDF_STATUS_E_FAILURE; + } else + /* Let's go ahead with handoff */ + status = csr_neighbor_roam_process_handoff_req(mac, sessionId); + + if (!QDF_IS_STATUS_SUCCESS(status)) + pNeighborRoamInfo->uOsRequestedHandoff = 0; + + return status; +} + +/* + * csr_neighbor_roam_start_lfr_scan() - + * This function is called if HDD requested handoff failed for some + * reason. start the LFR logic at that point.By the time, this function is + * called, a STOP command has already been issued. + + * @mac: The handle returned by mac_open. + * @session_id: Session ID + * + * Return QDF_STATUS_SUCCESS on success, corresponding error code otherwise + */ +QDF_STATUS csr_neighbor_roam_start_lfr_scan(struct mac_context *mac, + uint8_t sessionId) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + pNeighborRoamInfo->uOsRequestedHandoff = 0; + /* There is no candidate or We are not roaming Now. + * Inform the FW to restart Roam Offload Scan + */ + csr_post_roam_state_change(mac, sessionId, ROAM_RSO_STARTED, + REASON_NO_CAND_FOUND_OR_NOT_ROAMING_NOW); + + return QDF_STATUS_SUCCESS; + + + +} diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_roam_preauth.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_roam_preauth.c new file mode 100644 index 0000000000000000000000000000000000000000..4a02b2b13644f3ef9d459985d41f0b353c2d835d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_roam_preauth.c @@ -0,0 +1,788 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: csr_roam_preauth.c + * + * Host based roaming preauthentication implementation + */ + +#include "wma_types.h" +#include "csr_inside_api.h" +#include "sme_qos_internal.h" +#include "sme_inside.h" +#include "host_diag_core_event.h" +#include "host_diag_core_log.h" +#include "csr_api.h" +#include "sme_api.h" +#include "csr_neighbor_roam.h" +#include "mac_trace.h" +#include "wlan_policy_mgr_api.h" +#include "sir_api.h" + +static QDF_STATUS csr_neighbor_roam_add_preauth_fail(struct mac_context *mac_ctx, + uint8_t session_id, tSirMacAddr bssid); + +/** + * csr_neighbor_roam_state_preauth_done() - Check if state is preauth done + * @mac_ctx: Global MAC context + * @session_id: SME session ID + * + * Return: True if the state id preauth done, false otherwise + */ +bool csr_neighbor_roam_state_preauth_done(struct mac_context *mac_ctx, + uint8_t session_id) +{ + return mac_ctx->roam.neighborRoamInfo[session_id].neighborRoamState == + eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE; +} + +/** + * csr_neighbor_roam_tranistion_preauth_done_to_disconnected() - Transition + * the state from preauth done to disconnected + * @mac_ctx: Global MAC Context + * @session_id: SME Session ID + * + * In the event that we are associated with AP1 and we have + * completed pre auth with AP2. Then we receive a deauth/disassoc from AP1. + * At this point neighbor roam is in pre auth done state, pre auth timer + * is running. We now handle this case by stopping timer and clearing + * the pre-auth state. We basically clear up and just go to disconnected + * state + * + * Return: None + */ +void csr_neighbor_roam_tranistion_preauth_done_to_disconnected( + struct mac_context *mac_ctx, uint8_t session_id) +{ + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac_ctx->roam.neighborRoamInfo[session_id]; + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("session is NULL")); + return; + } + + if (pNeighborRoamInfo->neighborRoamState != + eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE) + return; + + qdf_mc_timer_stop(&session->ftSmeContext.preAuthReassocIntvlTimer); + csr_neighbor_roam_state_transition(mac_ctx, + eCSR_NEIGHBOR_ROAM_STATE_INIT, session_id); + pNeighborRoamInfo->roamChannelInfo.IAPPNeighborListReceived = false; + pNeighborRoamInfo->uOsRequestedHandoff = 0; +} + +/** + * csr_roam_enqueue_preauth() - Put the preauth command in the queue + * @mac_ctx: Global MAC Context + * @session_id: SME Session ID + * @bss_desc: BSS descriptor + * @reason: roaming reason + * @immediate: High priority in the queue or not + * + * Return: Success if queued properly, false otherwise. + */ +QDF_STATUS csr_roam_enqueue_preauth(struct mac_context *mac_ctx, + uint32_t session_id, + struct bss_description *bss_desc, + enum csr_roam_reason reason, bool immediate) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tSmeCmd *command; + + command = csr_get_command_buffer(mac_ctx); + if (!command) { + sme_err("fail to get command buffer"); + status = QDF_STATUS_E_RESOURCES; + } else { + if (bss_desc) { + command->command = eSmeCommandRoam; + command->vdev_id = (uint8_t) session_id; + command->u.roamCmd.roamReason = reason; + command->u.roamCmd.pLastRoamBss = bss_desc; + status = csr_queue_sme_command(mac_ctx, command, + immediate); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("fail to queue preauth,status: %d", + status); + } + } else { + status = QDF_STATUS_E_RESOURCES; + } + } + return status; +} + +/** + * csr_neighbor_roam_purge_preauth_failed_list() - Purge the preauth fail list + * @mac_ctx: Global MAC Context + * + * Return: None + */ +void csr_neighbor_roam_purge_preauth_failed_list(struct mac_context *mac_ctx) +{ + uint8_t i; + uint8_t j; + uint8_t num_mac_addr; + tpCsrNeighborRoamControlInfo neigh_roam_info = NULL; + tpCsrPreauthFailListInfo fail_list; + + for (j = 0; j < WLAN_MAX_VDEVS; j++) { + neigh_roam_info = &mac_ctx->roam.neighborRoamInfo[j]; + fail_list = &neigh_roam_info->FTRoamInfo.preAuthFailList; + num_mac_addr = fail_list->numMACAddress; + for (i = 0; i < num_mac_addr; i++) + qdf_mem_zero(fail_list->macAddress[i], + sizeof(tSirMacAddr)); + fail_list->numMACAddress = 0; + } +} + +/** + * @csr_neighbor_roam_reset_preauth_control_info - Reset preauth info + * @mac_ctx: Global MAC Context + * @session_id: SME Session ID + * + * Return: None + */ +void csr_neighbor_roam_reset_preauth_control_info(struct mac_context *mac_ctx, + uint8_t session_id) +{ + tpCsrNeighborRoamControlInfo neigh_roam_info = + &mac_ctx->roam.neighborRoamInfo[session_id]; + + neigh_roam_info->is11rAssoc = false; + csr_neighbor_roam_purge_preauth_failed_list(mac_ctx); + + neigh_roam_info->FTRoamInfo.preauthRspPending = false; + neigh_roam_info->FTRoamInfo.numPreAuthRetries = 0; + neigh_roam_info->FTRoamInfo.currentNeighborRptRetryNum = 0; + neigh_roam_info->FTRoamInfo.neighborRptPending = false; + neigh_roam_info->uOsRequestedHandoff = 0; + qdf_mem_zero(&neigh_roam_info->handoffReqInfo, + sizeof(tCsrHandoffRequest)); +} + +/** + * csr_neighbor_roam_preauth_rsp_handler() - handle preauth response + * @mac_ctx: The handle returned by mac_open. + * @session_id: SME session + * @lim_status: QDF_STATUS_SUCCESS/QDF_STATUS_E_FAILURE/QDF_STATUS_E_NOSPC/ + * eSIT_LIM_AUTH_RSP_TIMEOUT status from PE + * + * This function handle the Preauth response from PE + * Every preauth is allowed max 3 tries if it fails. If a bssid failed + * for more than MAX_TRIES, we will remove it from the list and try + * with the next node in the roamable AP list and add the BSSID to + * pre-auth failed list. If no more entries present in roamable AP list, + * transition to REPORT_SCAN state. + * + * Return: QDF_STATUS_SUCCESS on success (i.e. pre-auth processed), + * QDF_STATUS_E_FAILURE otherwise + */ +QDF_STATUS csr_neighbor_roam_preauth_rsp_handler(struct mac_context *mac_ctx, + uint8_t session_id, + QDF_STATUS lim_status) +{ + tpCsrNeighborRoamControlInfo neighbor_roam_info = + &mac_ctx->roam.neighborRoamInfo[session_id]; + QDF_STATUS status = QDF_STATUS_SUCCESS; + QDF_STATUS preauth_processed = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamBSSInfo preauth_rsp_node = NULL; + uint8_t reason; + + if (false == neighbor_roam_info->FTRoamInfo.preauthRspPending) { + /* + * This can happen when we disconnect immediately + * after sending a pre-auth request. During processing + * of the disconnect command, we would have reset + * preauthRspPending and transitioned to INIT state. + */ + sme_warn("Unexpected pre-auth response in state %d", + neighbor_roam_info->neighborRoamState); + preauth_processed = QDF_STATUS_E_FAILURE; + goto DEQ_PREAUTH; + } + /* We can receive it in these 2 states. */ + if ((neighbor_roam_info->neighborRoamState != + eCSR_NEIGHBOR_ROAM_STATE_PREAUTHENTICATING)) { + sme_debug("Preauth response received in state %s", + mac_trace_get_neighbour_roam_state + (neighbor_roam_info->neighborRoamState)); + preauth_processed = QDF_STATUS_E_FAILURE; + goto DEQ_PREAUTH; + } + + neighbor_roam_info->FTRoamInfo.preauthRspPending = false; + + if (QDF_STATUS_SUCCESS == lim_status) + preauth_rsp_node = + csr_neighbor_roam_next_roamable_ap( + mac_ctx, &neighbor_roam_info->roamableAPList, + NULL); + if ((QDF_STATUS_SUCCESS == lim_status) && (preauth_rsp_node)) { + sme_debug("Preauth completed successfully after %d tries", + neighbor_roam_info->FTRoamInfo.numPreAuthRetries); + sme_debug("After Pre-Auth: BSSID " QDF_MAC_ADDR_FMT ", ChFq:%d", + QDF_MAC_ADDR_REF( + preauth_rsp_node->pBssDescription->bssId), + preauth_rsp_node->pBssDescription->chan_freq); + + csr_neighbor_roam_send_lfr_metric_event(mac_ctx, session_id, + preauth_rsp_node->pBssDescription->bssId, + eCSR_ROAM_PREAUTH_STATUS_SUCCESS); + /* + * Preauth completed successfully. Insert the preauthenticated + * node to tail of preAuthDoneList. + */ + csr_neighbor_roam_remove_roamable_ap_list_entry(mac_ctx, + &neighbor_roam_info->roamableAPList, + preauth_rsp_node); + csr_ll_insert_tail( + &neighbor_roam_info->FTRoamInfo.preAuthDoneList, + &preauth_rsp_node->List, LL_ACCESS_LOCK); + + csr_neighbor_roam_state_transition(mac_ctx, + eCSR_NEIGHBOR_ROAM_STATE_PREAUTH_DONE, session_id); + neighbor_roam_info->FTRoamInfo.numPreAuthRetries = 0; + + /* + * The caller of this function would start a timer and by + * the time it expires, supplicant should have provided + * the updated FTIEs to SME. So, when it expires, handoff + * will be triggered then. + */ + } else { + tpCsrNeighborRoamBSSInfo neighbor_bss_node = NULL; + tListElem *entry; + bool is_dis_pending = false; + + sme_err("Preauth failed retry number %d, status: 0x%x", + neighbor_roam_info->FTRoamInfo.numPreAuthRetries, + lim_status); + + /* + * Preauth failed. Add the bssId to the preAuth failed list + * of MAC Address. Also remove the AP from roamable AP list. + */ + if ((neighbor_roam_info->FTRoamInfo.numPreAuthRetries >= + CSR_NEIGHBOR_ROAM_MAX_NUM_PREAUTH_RETRIES) || + (QDF_STATUS_E_NOSPC == lim_status)) { + /* + * We are going to remove the node as it fails for + * more than MAX tries. Reset this count to 0 + */ + neighbor_roam_info->FTRoamInfo.numPreAuthRetries = 0; + + /* + * The one in the head of the list should be one with + * which we issued pre-auth and failed + */ + entry = csr_ll_remove_head( + &neighbor_roam_info->roamableAPList, + LL_ACCESS_LOCK); + if (!entry) { + sme_err("Preauth list is empty"); + goto NEXT_PREAUTH; + } + neighbor_bss_node = GET_BASE_ADDR(entry, + tCsrNeighborRoamBSSInfo, + List); + /* + * Add the BSSID to pre-auth fail list if + * it is not requested by HDD + */ + if (!neighbor_roam_info->uOsRequestedHandoff) + status = + csr_neighbor_roam_add_preauth_fail( + mac_ctx, session_id, + neighbor_bss_node-> + pBssDescription->bssId); + csr_neighbor_roam_send_lfr_metric_event(mac_ctx, + session_id, + neighbor_bss_node->pBssDescription->bssId, + eCSR_ROAM_PREAUTH_STATUS_FAILURE); + /* Now we can free this node */ + csr_neighbor_roam_free_neighbor_roam_bss_node( + mac_ctx, neighbor_bss_node); + } +NEXT_PREAUTH: + is_dis_pending = is_disconnect_pending(mac_ctx, session_id); + if (is_dis_pending) { + sme_err("Disconnect in progress, Abort preauth"); + goto ABORT_PREAUTH; + } + /* Issue preauth request for the same/next entry */ + if (QDF_STATUS_SUCCESS == csr_neighbor_roam_issue_preauth_req( + mac_ctx, session_id)) + goto DEQ_PREAUTH; +ABORT_PREAUTH: + if (csr_roam_is_roam_offload_scan_enabled(mac_ctx)) { + reason = REASON_PREAUTH_FAILED_FOR_ALL; + if (neighbor_roam_info->uOsRequestedHandoff) { + neighbor_roam_info->uOsRequestedHandoff = 0; + csr_post_roam_state_change(mac_ctx, session_id, + ROAM_RSO_STARTED, + reason); + } else { + /* ROAM_SCAN_OFFLOAD_RESTART is a + * special command to trigger bmiss + * handler internally for LFR2 all candidate + * preauth failure. + * This should be decoupled from RSO. + */ + csr_roam_offload_scan(mac_ctx, session_id, + ROAM_SCAN_OFFLOAD_RESTART, + reason); + } + csr_neighbor_roam_state_transition(mac_ctx, + eCSR_NEIGHBOR_ROAM_STATE_CONNECTED, session_id); + } + } + +DEQ_PREAUTH: + csr_dequeue_roam_command(mac_ctx, eCsrPerformPreauth, session_id); + return preauth_processed; +} + +/** + * csr_neighbor_roam_add_preauth_fail() - add bssid to preauth failed list + * @mac_ctx: The handle returned by mac_open. + * @bssid: BSSID to be added to the preauth fail list + * + * This function adds the given BSSID to the Preauth fail list + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE otherwise + */ +static QDF_STATUS csr_neighbor_roam_add_preauth_fail(struct mac_context *mac_ctx, + uint8_t session_id, tSirMacAddr bssid) +{ + uint8_t i = 0; + tpCsrNeighborRoamControlInfo neighbor_roam_info = + &mac_ctx->roam.neighborRoamInfo[session_id]; + uint8_t num_mac_addr = neighbor_roam_info->FTRoamInfo.preAuthFailList. + numMACAddress; + + sme_warn("Added BSSID " QDF_MAC_ADDR_FMT " to Preauth failed list", + QDF_MAC_ADDR_REF(bssid)); + + for (i = 0; + i < neighbor_roam_info->FTRoamInfo.preAuthFailList.numMACAddress; + i++) { + if (!qdf_mem_cmp( + neighbor_roam_info->FTRoamInfo.preAuthFailList.macAddress[i], + bssid, sizeof(tSirMacAddr))) { + sme_warn("BSSID "QDF_MAC_ADDR_FMT" already fail list", + QDF_MAC_ADDR_REF(bssid)); + return QDF_STATUS_SUCCESS; + } + } + + if ((num_mac_addr + 1) > MAX_NUM_PREAUTH_FAIL_LIST_ADDRESS) { + sme_err("Cannot add, preauth fail list is full"); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(neighbor_roam_info->FTRoamInfo.preAuthFailList. + macAddress[num_mac_addr], bssid, sizeof(tSirMacAddr)); + neighbor_roam_info->FTRoamInfo.preAuthFailList.numMACAddress++; + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_neighbor_roam_is_preauth_candidate() + * + * @mac_ctx: Pointer to Global MAC structure + * @bssId : BSSID of the candidate + * + * This function checks whether the given MAC address is already present + * in the preauth fail list and returns true/false accordingly + * + * Return: true if preauth candidate, false otherwise + */ +bool csr_neighbor_roam_is_preauth_candidate(struct mac_context *mac, + uint8_t sessionId, tSirMacAddr bssId) +{ + uint8_t i = 0; + tpCsrNeighborRoamControlInfo pNeighborRoamInfo = + &mac->roam.neighborRoamInfo[sessionId]; + + if (csr_roam_is_roam_offload_scan_enabled(mac)) + return true; + if (0 == pNeighborRoamInfo->FTRoamInfo.preAuthFailList.numMACAddress) + return true; + + for (i = 0; + i < pNeighborRoamInfo->FTRoamInfo.preAuthFailList.numMACAddress; + i++) { + if (!qdf_mem_cmp(pNeighborRoamInfo->FTRoamInfo. + preAuthFailList.macAddress[i], bssId, + sizeof(tSirMacAddr))) { + sme_err("BSSID exists in fail list" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(bssId)); + return false; + } + } + + return true; +} + +/** + * csr_get_dot11_mode() - Derives dot11mode + * @mac_ctx: Global MAC context + * @session_id: SME Session ID + * @bss_desc: BSS descriptor + * + * Return: dot11mode + */ +static uint32_t csr_get_dot11_mode(struct mac_context *mac_ctx, + uint32_t session_id, + struct bss_description *bss_desc) +{ + struct csr_roam_session *csr_session = CSR_GET_SESSION(mac_ctx, + session_id); + enum csr_cfgdot11mode ucfg_dot11_mode, cfg_dot11_mode; + QDF_STATUS status; + tDot11fBeaconIEs *ies_local = NULL; + uint32_t dot11mode = 0; + + if (!csr_session) { + sme_err("Invalid session id %d", session_id); + return 0; + } + + sme_debug("phyMode %d", csr_session->pCurRoamProfile->phyMode); + + /* Get IE's */ + status = csr_get_parsed_bss_description_ies(mac_ctx, bss_desc, + &ies_local); + if (!QDF_IS_STATUS_SUCCESS(status)) { + sme_err("csr_get_parsed_bss_description_ies failed"); + return 0; + } + if (!ies_local) { + sme_err("ies_local is NULL"); + return 0; + } + + if (csr_is_phy_mode_match(mac_ctx, + csr_session->pCurRoamProfile->phyMode, + bss_desc, csr_session->pCurRoamProfile, + &cfg_dot11_mode, ies_local)) + ucfg_dot11_mode = cfg_dot11_mode; + else { + sme_err("Can not find match phy mode"); + if (WLAN_REG_IS_5GHZ_CH_FREQ(bss_desc->chan_freq)) + ucfg_dot11_mode = eCSR_CFG_DOT11_MODE_11A; + else + ucfg_dot11_mode = eCSR_CFG_DOT11_MODE_11G; + } + + /* dot11mode */ + dot11mode = csr_translate_to_wni_cfg_dot11_mode(mac_ctx, + ucfg_dot11_mode); + sme_debug("dot11mode %d ucfg_dot11_mode %d", + dot11mode, ucfg_dot11_mode); + + if (bss_desc->chan_freq <= CDS_CHAN_14_FREQ && + !mac_ctx->mlme_cfg->vht_caps.vht_cap_info.b24ghz_band && + MLME_DOT11_MODE_11AC == dot11mode) { + /* Need to disable VHT operation in 2.4 GHz band */ + dot11mode = MLME_DOT11_MODE_11N; + } + qdf_mem_free(ies_local); + return dot11mode; +} + +QDF_STATUS csr_roam_issue_ft_preauth_req(struct mac_context *mac_ctx, + uint32_t vdev_id, + struct bss_description *bss_desc) +{ + tpSirFTPreAuthReq preauth_req; + uint16_t auth_req_len; + struct bss_description *buf; + uint32_t dot11mode, buf_len; + QDF_STATUS status; + struct csr_roam_session *csr_session = CSR_GET_SESSION(mac_ctx, + vdev_id); + + if (!csr_session) { + sme_err("Session does not exist for vdev_id: %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + dot11mode = csr_get_dot11_mode(mac_ctx, vdev_id, bss_desc); + if (!dot11mode) { + sme_err("dot11mode is zero"); + return QDF_STATUS_E_FAILURE; + } + + auth_req_len = sizeof(tSirFTPreAuthReq); + preauth_req = qdf_mem_malloc(auth_req_len); + if (!preauth_req) + return QDF_STATUS_E_NOMEM; + + buf_len = sizeof(bss_desc->length) + bss_desc->length; + buf = qdf_mem_malloc(buf_len); + if (!buf) { + qdf_mem_free(preauth_req); + return QDF_STATUS_E_NOMEM; + } + + /* Save the SME Session ID. We need it while processing preauth resp */ + csr_session->ftSmeContext.vdev_id = vdev_id; + preauth_req->messageType = eWNI_SME_FT_PRE_AUTH_REQ; + preauth_req->pre_auth_channel_freq = bss_desc->chan_freq; + preauth_req->dot11mode = dot11mode; + + qdf_mem_copy((void *)&preauth_req->currbssId, + (void *)csr_session->connectedProfile.bssid.bytes, + sizeof(tSirMacAddr)); + qdf_mem_copy((void *)&preauth_req->preAuthbssId, + (void *)bss_desc->bssId, sizeof(tSirMacAddr)); + qdf_mem_copy((void *)&preauth_req->self_mac_addr, + (void *)&csr_session->self_mac_addr.bytes, sizeof(tSirMacAddr)); + + if (csr_roam_is11r_assoc(mac_ctx, vdev_id) && + (mac_ctx->roam.roamSession[vdev_id].connectedProfile.AuthType != + eCSR_AUTH_TYPE_OPEN_SYSTEM)) { + preauth_req->ft_ies_length = + (uint16_t) csr_session->ftSmeContext.auth_ft_ies_length; + qdf_mem_copy(preauth_req->ft_ies, + csr_session->ftSmeContext.auth_ft_ies, + csr_session->ftSmeContext.auth_ft_ies_length); + } else { + preauth_req->ft_ies_length = 0; + } + qdf_mem_copy(buf, bss_desc, buf_len); + preauth_req->pbssDescription = buf; + preauth_req->length = auth_req_len; + + status = umac_send_mb_message_to_mac(preauth_req); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(buf); + + return status; +} + +void csr_roam_ft_pre_auth_rsp_processor(struct mac_context *mac_ctx, + tpSirFTPreAuthRsp preauth_rsp) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_info *roam_info; + enum csr_akm_type conn_Auth_type; + uint32_t vdev_id = preauth_rsp->vdev_id; + struct csr_roam_session *csr_session = CSR_GET_SESSION(mac_ctx, + vdev_id); + tDot11fAuthentication *p_auth = NULL; + + if (!csr_session) { + sme_err("CSR session is NULL"); + return; + } + status = csr_neighbor_roam_preauth_rsp_handler(mac_ctx, + preauth_rsp->vdev_id, preauth_rsp->status); + if (status != QDF_STATUS_SUCCESS) { + sme_err("Preauth was not processed: %d SessionID: %d", + status, vdev_id); + return; + } + + if (QDF_STATUS_SUCCESS != (QDF_STATUS) preauth_rsp->status) + return; + csr_session->ftSmeContext.FTState = eFT_AUTH_COMPLETE; + csr_session->ftSmeContext.psavedFTPreAuthRsp = preauth_rsp; + /* No need to notify qos module if this is a non 11r & ESE roam */ + if (csr_roam_is11r_assoc(mac_ctx, preauth_rsp->vdev_id) +#ifdef FEATURE_WLAN_ESE + || csr_roam_is_ese_assoc(mac_ctx, preauth_rsp->vdev_id) +#endif + ) { + sme_qos_csr_event_ind(mac_ctx, + csr_session->ftSmeContext.vdev_id, + SME_QOS_CSR_PREAUTH_SUCCESS_IND, NULL); + } + status = + qdf_mc_timer_start( + &csr_session->ftSmeContext.preAuthReassocIntvlTimer, + 60); + if (QDF_STATUS_SUCCESS != status) { + sme_err("PreauthReassocInterval timer failed status %d", + status); + return; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return; + qdf_mem_copy((void *)&csr_session->ftSmeContext.preAuthbssId, + (void *)preauth_rsp->preAuthbssId, + sizeof(struct qdf_mac_addr)); + if (csr_roam_is11r_assoc(mac_ctx, preauth_rsp->vdev_id)) + csr_roam_call_callback(mac_ctx, preauth_rsp->vdev_id, + NULL, 0, eCSR_ROAM_FT_RESPONSE, eCSR_ROAM_RESULT_NONE); + +#ifdef FEATURE_WLAN_ESE + if (csr_roam_is_ese_assoc(mac_ctx, preauth_rsp->vdev_id)) { + csr_roam_read_tsf(mac_ctx, (uint8_t *)&roam_info->timestamp, + preauth_rsp->vdev_id); + qdf_mem_copy((void *)&roam_info->bssid, + (void *)preauth_rsp->preAuthbssId, + sizeof(struct qdf_mac_addr)); + csr_roam_call_callback(mac_ctx, preauth_rsp->vdev_id, + roam_info, 0, + eCSR_ROAM_CCKM_PREAUTH_NOTIFY, 0); + } +#endif + + if (csr_roam_is_fast_roam_enabled(mac_ctx, preauth_rsp->vdev_id)) { + /* Save the bssid from the received response */ + qdf_mem_copy((void *)&roam_info->bssid, + (void *)preauth_rsp->preAuthbssId, + sizeof(struct qdf_mac_addr)); + csr_roam_call_callback(mac_ctx, preauth_rsp->vdev_id, + roam_info, 0, eCSR_ROAM_PMK_NOTIFY, 0); + } + + qdf_mem_free(roam_info); + + /* If its an Open Auth, FT IEs are not provided by supplicant */ + /* Hence populate them here */ + conn_Auth_type = + mac_ctx->roam.roamSession[vdev_id].connectedProfile.AuthType; + + csr_session->ftSmeContext.addMDIE = false; + + /* Done with it, init it. */ + csr_session->ftSmeContext.psavedFTPreAuthRsp = NULL; + + if (csr_roam_is11r_assoc(mac_ctx, preauth_rsp->vdev_id) && + (conn_Auth_type == eCSR_AUTH_TYPE_OPEN_SYSTEM)) { + uint16_t ft_ies_length; + + ft_ies_length = preauth_rsp->ric_ies_length; + + if ((csr_session->ftSmeContext.reassoc_ft_ies) && + (csr_session->ftSmeContext.reassoc_ft_ies_length)) { + qdf_mem_free(csr_session->ftSmeContext.reassoc_ft_ies); + csr_session->ftSmeContext.reassoc_ft_ies_length = 0; + csr_session->ftSmeContext.reassoc_ft_ies = NULL; + } + p_auth = (tDot11fAuthentication *) qdf_mem_malloc( + sizeof(tDot11fAuthentication)); + + if (!p_auth) + return; + + status = dot11f_unpack_authentication(mac_ctx, + preauth_rsp->ft_ies, + preauth_rsp->ft_ies_length, p_auth, false); + if (DOT11F_FAILED(status)) + sme_err("Failed to parse an Authentication frame"); + else if (p_auth->MobilityDomain.present) + csr_session->ftSmeContext.addMDIE = true; + + qdf_mem_free(p_auth); + + if (!ft_ies_length) + return; + + csr_session->ftSmeContext.reassoc_ft_ies = + qdf_mem_malloc(ft_ies_length); + if (!csr_session->ftSmeContext.reassoc_ft_ies) + return; + + /* Copy the RIC IEs to reassoc IEs */ + qdf_mem_copy(((uint8_t *) csr_session->ftSmeContext. + reassoc_ft_ies), + (uint8_t *) preauth_rsp->ric_ies, + preauth_rsp->ric_ies_length); + csr_session->ftSmeContext.reassoc_ft_ies_length = ft_ies_length; + csr_session->ftSmeContext.addMDIE = true; + } +} + +/** + * csr_neighbor_roam_issue_preauth_req() - Send preauth request to first AP + * @mac_ctx: The handle returned by mac_open. + * @session_id: Session information + * + * This function issues preauth request to PE with the 1st AP entry in the + * roamable AP list + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE otherwise + */ +QDF_STATUS csr_neighbor_roam_issue_preauth_req(struct mac_context *mac_ctx, + uint8_t session_id) +{ + tpCsrNeighborRoamControlInfo neighbor_roam_info = + &mac_ctx->roam.neighborRoamInfo[session_id]; + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpCsrNeighborRoamBSSInfo neighbor_bss_node; + + + if (false != neighbor_roam_info->FTRoamInfo.preauthRspPending) { + /* This must not be true here */ + QDF_ASSERT(neighbor_roam_info->FTRoamInfo.preauthRspPending == + false); + return QDF_STATUS_E_FAILURE; + } + + /* + * Issue Preauth request to PE here. + * Need to issue the preauth request with the BSSID that is in the + * head of the roamable AP list. Parameters that should be passed are + * BSSID, Channel number and the neighborScanPeriod(probably). If + * roamableAPList gets empty, should transition to REPORT_SCAN state + */ + neighbor_bss_node = csr_neighbor_roam_next_roamable_ap(mac_ctx, + &neighbor_roam_info->roamableAPList, NULL); + + if (!neighbor_bss_node) { + sme_err("Roamable AP list is empty"); + return QDF_STATUS_E_FAILURE; + } + csr_neighbor_roam_send_lfr_metric_event(mac_ctx, session_id, + neighbor_bss_node->pBssDescription->bssId, + eCSR_ROAM_PREAUTH_INIT_NOTIFY); + status = csr_roam_enqueue_preauth(mac_ctx, session_id, + neighbor_bss_node->pBssDescription, + eCsrPerformPreauth, true); + + sme_debug("Before Pre-Auth: BSSID " QDF_MAC_ADDR_FMT ", Ch:%d", + QDF_MAC_ADDR_REF(neighbor_bss_node->pBssDescription->bssId), + neighbor_bss_node->pBssDescription->chan_freq); + + if (QDF_STATUS_SUCCESS != status) { + sme_err("Return failed preauth request status %d", + status); + return status; + } + + neighbor_roam_info->FTRoamInfo.preauthRspPending = true; + neighbor_roam_info->FTRoamInfo.numPreAuthRetries++; + csr_neighbor_roam_state_transition(mac_ctx, + eCSR_NEIGHBOR_ROAM_STATE_PREAUTHENTICATING, session_id); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_util.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_util.c new file mode 100644 index 0000000000000000000000000000000000000000..68df7ebdb80ae02bcd2546223aed6055824cbb68 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_util.c @@ -0,0 +1,4210 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: csr_util.c + * + * Implementation supporting routines for CSR. + */ + +#include "ani_global.h" + +#include "csr_support.h" +#include "csr_inside_api.h" +#include "sme_qos_internal.h" +#include "wma_types.h" +#include "cds_utils.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_serialization_legacy_api.h" +#include "wlan_reg_services_api.h" +#include "wlan_crypto_global_api.h" + + +uint8_t csr_wpa_oui[][CSR_WPA_OUI_SIZE] = { + {0x00, 0x50, 0xf2, 0x00} + , + {0x00, 0x50, 0xf2, 0x01} + , + {0x00, 0x50, 0xf2, 0x02} + , + {0x00, 0x50, 0xf2, 0x03} + , + {0x00, 0x50, 0xf2, 0x04} + , + {0x00, 0x50, 0xf2, 0x05} + , +#ifdef FEATURE_WLAN_ESE + {0x00, 0x40, 0x96, 0x00} + , /* CCKM */ +#endif /* FEATURE_WLAN_ESE */ +}; + +#define FT_PSK_IDX 4 +#define FT_8021X_IDX 3 + +/* + * PLEASE DO NOT ADD THE #IFDEF IN BELOW TABLE, + * IF STILL REQUIRE THEN PLEASE ADD NULL ENTRIES + * OTHERWISE IT WILL BREAK OTHER LOWER + * SECUIRTY MODES. + */ + +uint8_t csr_rsn_oui[][CSR_RSN_OUI_SIZE] = { + {0x00, 0x0F, 0xAC, 0x00} + , /* group cipher */ + {0x00, 0x0F, 0xAC, 0x01} + , /* WEP-40 or RSN */ + {0x00, 0x0F, 0xAC, 0x02} + , /* TKIP or RSN-PSK */ + {0x00, 0x0F, 0xAC, 0x03} + , /* Reserved */ + {0x00, 0x0F, 0xAC, 0x04} + , /* AES-CCMP */ + {0x00, 0x0F, 0xAC, 0x05} + , /* WEP-104 */ + {0x00, 0x40, 0x96, 0x00} + , /* CCKM */ + {0x00, 0x0F, 0xAC, 0x06} + , /* BIP (encryption type) or + * RSN-PSK-SHA256 (authentication type) + */ + /* RSN-8021X-SHA256 (authentication type) */ + {0x00, 0x0F, 0xAC, 0x05}, +#ifdef WLAN_FEATURE_FILS_SK +#define ENUM_FILS_SHA256 9 + /* FILS SHA256 */ + {0x00, 0x0F, 0xAC, 0x0E}, +#define ENUM_FILS_SHA384 10 + /* FILS SHA384 */ + {0x00, 0x0F, 0xAC, 0x0F}, +#define ENUM_FT_FILS_SHA256 11 + /* FILS FT SHA256 */ + {0x00, 0x0F, 0xAC, 0x10}, +#define ENUM_FT_FILS_SHA384 12 + /* FILS FT SHA384 */ + {0x00, 0x0F, 0xAC, 0x11}, +#else + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, +#endif + /* AES GCMP */ + {0x00, 0x0F, 0xAC, 0x08}, + /* AES GCMP-256 */ + {0x00, 0x0F, 0xAC, 0x09}, +#define ENUM_DPP_RSN 15 + /* DPP RSN */ + {0x50, 0x6F, 0x9A, 0x02}, +#define ENUM_OWE 16 + /* OWE https://tools.ietf.org/html/rfc8110 */ + {0x00, 0x0F, 0xAC, 0x12}, +#define ENUM_SUITEB_EAP256 17 + {0x00, 0x0F, 0xAC, 0x0B}, +#define ENUM_SUITEB_EAP384 18 + {0x00, 0x0F, 0xAC, 0x0C}, + +#ifdef WLAN_FEATURE_SAE +#define ENUM_SAE 19 + /* SAE */ + {0x00, 0x0F, 0xAC, 0x08}, +#define ENUM_FT_SAE 20 + /* FT SAE */ + {0x00, 0x0F, 0xAC, 0x09}, +#else + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, +#endif +#define ENUM_OSEN 21 + /* OSEN RSN */ + {0x50, 0x6F, 0x9A, 0x01}, +#define ENUM_FT_SUITEB_SHA384 22 + /* FT Suite-B SHA384 */ + {0x00, 0x0F, 0xAC, 0x0D}, + + /* define new oui here, update #define CSR_OUI_***_INDEX */ +}; + +#ifdef FEATURE_WLAN_WAPI +uint8_t csr_wapi_oui[][CSR_WAPI_OUI_SIZE] = { + {0x00, 0x14, 0x72, 0x00} + , /* Reserved */ + {0x00, 0x14, 0x72, 0x01} + , /* WAI certificate or SMS4 */ + {0x00, 0x14, 0x72, 0x02} /* WAI PSK */ +}; +#endif /* FEATURE_WLAN_WAPI */ + +uint8_t csr_group_mgmt_oui[][CSR_RSN_OUI_SIZE] = { +#define ENUM_CMAC 0 + {0x00, 0x0F, 0xAC, 0x06}, +#define ENUM_GMAC_128 1 + {0x00, 0x0F, 0xAC, 0x0B}, +#define ENUM_GMAC_256 2 + {0x00, 0x0F, 0xAC, 0x0C}, +}; + + +/* ////////////////////////////////////////////////////////////////////// */ + +/** + * \var g_phy_rates_suppt + * + * \brief Rate support lookup table + * + * + * This is a lookup table indexing rates & configuration parameters to + * support. Given a rate (in unites of 0.5Mpbs) & three bools (MIMO + * Enabled, Channel Bonding Enabled, & Concatenation Enabled), one can + * determine whether the given rate is supported by computing two + * indices. The first maps the rate to table row as indicated below + * (i.e. eHddSuppRate_6Mbps maps to row zero, eHddSuppRate_9Mbps to row + * 1, and so on). Index two can be computed like so: + * + * \code + * idx2 = ( fEsf ? 0x4 : 0x0 ) | + * ( fCb ? 0x2 : 0x0 ) | + * ( fMimo ? 0x1 : 0x0 ); + * \endcode + * + * + * Given that: + * + * \code + * fSupported = g_phy_rates_suppt[idx1][idx2]; + * \endcode + * + * + * This table is based on the document "PHY Supported Rates.doc". This + * table is permissive in that a rate is reflected as being supported + * even when turning off an enabled feature would be required. For + * instance, "PHY Supported Rates" lists 42Mpbs as unsupported when CB, + * ESF, & MIMO are all on. However, if we turn off either of CB or + * MIMO, it then becomes supported. Therefore, we mark it as supported + * even in index 7 of this table. + * + * + */ + +static const bool g_phy_rates_suppt[24][8] = { + + /* SSF SSF SSF SSF ESF ESF ESF ESF */ + /* SIMO MIMO SIMO MIMO SIMO MIMO SIMO MIMO */ + /* No CB No CB CB CB No CB No CB CB CB */ + {true, true, true, true, true, true, true, true}, /* 6Mbps */ + {true, true, true, true, true, true, true, true}, /* 9Mbps */ + {true, true, true, true, true, true, true, true}, /* 12Mbps */ + {true, true, true, true, true, true, true, true}, /* 18Mbps */ + {false, false, true, true, false, false, true, true}, /* 20Mbps */ + {true, true, true, true, true, true, true, true}, /* 24Mbps */ + {true, true, true, true, true, true, true, true}, /* 36Mbps */ + {false, false, true, true, false, true, true, true}, /* 40Mbps */ + {false, false, true, true, false, true, true, true}, /* 42Mbps */ + {true, true, true, true, true, true, true, true}, /* 48Mbps */ + {true, true, true, true, true, true, true, true}, /* 54Mbps */ + {false, true, true, true, false, true, true, true}, /* 72Mbps */ + {false, false, true, true, false, true, true, true}, /* 80Mbps */ + {false, false, true, true, false, true, true, true}, /* 84Mbps */ + {false, true, true, true, false, true, true, true}, /* 96Mbps */ + {false, true, true, true, false, true, true, true}, /* 108Mbps */ + {false, false, true, true, false, true, true, true}, /* 120Mbps */ + {false, false, true, true, false, true, true, true}, /* 126Mbps */ + {false, false, false, true, false, false, false, true}, /* 144Mbps */ + {false, false, false, true, false, false, false, true}, /* 160Mbps */ + {false, false, false, true, false, false, false, true}, /* 168Mbps */ + {false, false, false, true, false, false, false, true}, /* 192Mbps */ + {false, false, false, true, false, false, false, true}, /* 216Mbps */ + {false, false, false, true, false, false, false, true}, /* 240Mbps */ + +}; + +#define CASE_RETURN_STR(n) {\ + case (n): return (# n);\ +} + +const char *get_e_roam_cmd_status_str(eRoamCmdStatus val) +{ + switch (val) { + CASE_RETURN_STR(eCSR_ROAM_CANCELLED); + CASE_RETURN_STR(eCSR_ROAM_FAILED); + CASE_RETURN_STR(eCSR_ROAM_ROAMING_START); + CASE_RETURN_STR(eCSR_ROAM_ROAMING_COMPLETION); + CASE_RETURN_STR(eCSR_ROAM_CONNECT_COMPLETION); + CASE_RETURN_STR(eCSR_ROAM_ASSOCIATION_START); + CASE_RETURN_STR(eCSR_ROAM_ASSOCIATION_COMPLETION); + CASE_RETURN_STR(eCSR_ROAM_DISASSOCIATED); + CASE_RETURN_STR(eCSR_ROAM_ASSOCIATION_FAILURE); + CASE_RETURN_STR(eCSR_ROAM_SHOULD_ROAM); + CASE_RETURN_STR(eCSR_ROAM_SCAN_FOUND_NEW_BSS); + CASE_RETURN_STR(eCSR_ROAM_LOSTLINK); + CASE_RETURN_STR(eCSR_ROAM_LOSTLINK_DETECTED); + CASE_RETURN_STR(eCSR_ROAM_MIC_ERROR_IND); + CASE_RETURN_STR(eCSR_ROAM_IBSS_IND); + CASE_RETURN_STR(eCSR_ROAM_CONNECT_STATUS_UPDATE); + CASE_RETURN_STR(eCSR_ROAM_GEN_INFO); + CASE_RETURN_STR(eCSR_ROAM_SET_KEY_COMPLETE); + CASE_RETURN_STR(eCSR_ROAM_IBSS_LEAVE); + CASE_RETURN_STR(eCSR_ROAM_INFRA_IND); + CASE_RETURN_STR(eCSR_ROAM_WPS_PBC_PROBE_REQ_IND); + CASE_RETURN_STR(eCSR_ROAM_FT_RESPONSE); + CASE_RETURN_STR(eCSR_ROAM_FT_START); + CASE_RETURN_STR(eCSR_ROAM_SESSION_OPENED); + CASE_RETURN_STR(eCSR_ROAM_FT_REASSOC_FAILED); + CASE_RETURN_STR(eCSR_ROAM_PMK_NOTIFY); +#ifdef FEATURE_WLAN_LFR_METRICS + CASE_RETURN_STR(eCSR_ROAM_PREAUTH_INIT_NOTIFY); + CASE_RETURN_STR(eCSR_ROAM_PREAUTH_STATUS_SUCCESS); + CASE_RETURN_STR(eCSR_ROAM_PREAUTH_STATUS_FAILURE); + CASE_RETURN_STR(eCSR_ROAM_HANDOVER_SUCCESS); +#endif +#ifdef FEATURE_WLAN_TDLS + CASE_RETURN_STR(eCSR_ROAM_TDLS_STATUS_UPDATE); + CASE_RETURN_STR(eCSR_ROAM_RESULT_MGMT_TX_COMPLETE_IND); +#endif + CASE_RETURN_STR(eCSR_ROAM_DISCONNECT_ALL_P2P_CLIENTS); + CASE_RETURN_STR(eCSR_ROAM_SEND_P2P_STOP_BSS); +#ifdef WLAN_FEATURE_11W + CASE_RETURN_STR(eCSR_ROAM_UNPROT_MGMT_FRAME_IND); +#endif +#ifdef WLAN_FEATURE_RMC + CASE_RETURN_STR(eCSR_ROAM_IBSS_PEER_INFO_COMPLETE); +#endif +#ifdef FEATURE_WLAN_ESE + CASE_RETURN_STR(eCSR_ROAM_TSM_IE_IND); + CASE_RETURN_STR(eCSR_ROAM_CCKM_PREAUTH_NOTIFY); + CASE_RETURN_STR(eCSR_ROAM_ESE_ADJ_AP_REPORT_IND); + CASE_RETURN_STR(eCSR_ROAM_ESE_BCN_REPORT_IND); +#endif /* FEATURE_WLAN_ESE */ + CASE_RETURN_STR(eCSR_ROAM_DFS_RADAR_IND); + CASE_RETURN_STR(eCSR_ROAM_SET_CHANNEL_RSP); + CASE_RETURN_STR(eCSR_ROAM_DFS_CHAN_SW_NOTIFY); + CASE_RETURN_STR(eCSR_ROAM_EXT_CHG_CHNL_IND); + CASE_RETURN_STR(eCSR_ROAM_STA_CHANNEL_SWITCH); + CASE_RETURN_STR(eCSR_ROAM_NDP_STATUS_UPDATE); + CASE_RETURN_STR(eCSR_ROAM_UPDATE_SCAN_RESULT); + CASE_RETURN_STR(eCSR_ROAM_START); + CASE_RETURN_STR(eCSR_ROAM_ABORT); + CASE_RETURN_STR(eCSR_ROAM_NAPI_OFF); + CASE_RETURN_STR(eCSR_ROAM_CHANNEL_COMPLETE_IND); + CASE_RETURN_STR(eCSR_ROAM_SAE_COMPUTE); + CASE_RETURN_STR(eCSR_ROAM_FIPS_PMK_REQUEST); + default: + return "unknown"; + } +} + +const char *get_e_csr_roam_result_str(eCsrRoamResult val) +{ + switch (val) { + CASE_RETURN_STR(eCSR_ROAM_RESULT_NONE); + CASE_RETURN_STR(eCSR_ROAM_RESULT_FAILURE); + CASE_RETURN_STR(eCSR_ROAM_RESULT_ASSOCIATED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NOT_ASSOCIATED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_MIC_FAILURE); + CASE_RETURN_STR(eCSR_ROAM_RESULT_FORCED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_DISASSOC_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_DEAUTH_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_CAP_CHANGED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_STARTED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_START_FAILED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_JOIN_SUCCESS); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_JOIN_FAILED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_CONNECT); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_INACTIVE); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_NEW_PEER); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_PEER_DEPARTED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_COALESCED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_STOP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_LOSTLINK); + CASE_RETURN_STR(eCSR_ROAM_RESULT_MIC_ERROR_UNICAST); + CASE_RETURN_STR(eCSR_ROAM_RESULT_MIC_ERROR_GROUP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_AUTHENTICATED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NEW_RSN_BSS); + #ifdef FEATURE_WLAN_WAPI + CASE_RETURN_STR(eCSR_ROAM_RESULT_NEW_WAPI_BSS); + #endif /* FEATURE_WLAN_WAPI */ + CASE_RETURN_STR(eCSR_ROAM_RESULT_INFRA_STARTED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_INFRA_START_FAILED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_INFRA_STOPPED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_INFRA_ASSOCIATION_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_INFRA_ASSOCIATION_CNF); + CASE_RETURN_STR(eCSR_ROAM_RESULT_INFRA_DISASSOCIATED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_WPS_PBC_PROBE_REQ_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_SEND_ACTION_FAIL); + CASE_RETURN_STR(eCSR_ROAM_RESULT_MAX_ASSOC_EXCEEDED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_ASSOC_FAIL_CON_CHANNEL); + CASE_RETURN_STR(eCSR_ROAM_RESULT_ADD_TDLS_PEER); + CASE_RETURN_STR(eCSR_ROAM_RESULT_UPDATE_TDLS_PEER); + CASE_RETURN_STR(eCSR_ROAM_RESULT_DELETE_TDLS_PEER); + CASE_RETURN_STR(eCSR_ROAM_RESULT_TEARDOWN_TDLS_PEER_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_DELETE_ALL_TDLS_PEER_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_LINK_ESTABLISH_REQ_RSP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_TDLS_SHOULD_DISCOVER); + CASE_RETURN_STR(eCSR_ROAM_RESULT_TDLS_SHOULD_TEARDOWN); + CASE_RETURN_STR(eCSR_ROAM_RESULT_TDLS_SHOULD_PEER_DISCONNECTED); + CASE_RETURN_STR + (eCSR_ROAM_RESULT_TDLS_CONNECTION_TRACKER_NOTIFICATION); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_PEER_INFO_SUCCESS); + CASE_RETURN_STR(eCSR_ROAM_RESULT_IBSS_PEER_INFO_FAILED); + CASE_RETURN_STR(eCSR_ROAM_RESULT_DFS_RADAR_FOUND_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_CHANNEL_CHANGE_SUCCESS); + CASE_RETURN_STR(eCSR_ROAM_RESULT_CHANNEL_CHANGE_FAILURE); + CASE_RETURN_STR(eCSR_ROAM_RESULT_CSA_RESTART_RSP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_DFS_CHANSW_UPDATE_SUCCESS); + CASE_RETURN_STR(eCSR_ROAM_EXT_CHG_CHNL_UPDATE_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDI_CREATE_RSP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDI_DELETE_RSP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDP_INITIATOR_RSP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDP_NEW_PEER_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDP_CONFIRM_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDP_INDICATION); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDP_SCHED_UPDATE_RSP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDP_RESPONDER_RSP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDP_END_RSP); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDP_PEER_DEPARTED_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_NDP_END_IND); + CASE_RETURN_STR(eCSR_ROAM_RESULT_SCAN_FOR_SSID_FAILURE); + default: + return "unknown"; + } +} + +const char *csr_phy_mode_str(eCsrPhyMode phy_mode) +{ + switch (phy_mode) { + case eCSR_DOT11_MODE_abg: + return "abg"; + case eCSR_DOT11_MODE_11a: + return "11a"; + case eCSR_DOT11_MODE_11b: + return "11b"; + case eCSR_DOT11_MODE_11g: + return "11g"; + case eCSR_DOT11_MODE_11n: + return "11n"; + case eCSR_DOT11_MODE_11g_ONLY: + return "11g_only"; + case eCSR_DOT11_MODE_11n_ONLY: + return "11n_only"; + case eCSR_DOT11_MODE_11b_ONLY: + return "11b_only"; + case eCSR_DOT11_MODE_11ac: + return "11ac"; + case eCSR_DOT11_MODE_11ac_ONLY: + return "11ac_only"; + case eCSR_DOT11_MODE_AUTO: + return "auto"; + case eCSR_DOT11_MODE_11ax: + return "11ax"; + case eCSR_DOT11_MODE_11ax_ONLY: + return "11ax_only"; + default: + return "unknown"; + } +} + +void csr_purge_vdev_pending_ser_cmd_list(struct mac_context *mac_ctx, + uint32_t vdev_id) +{ + wlan_serialization_purge_all_pending_cmd_by_vdev_id(mac_ctx->pdev, + vdev_id); +} + +void csr_purge_vdev_all_scan_ser_cmd_list(struct mac_context *mac_ctx, + uint32_t vdev_id) +{ + wlan_serialization_purge_all_scan_cmd_by_vdev_id(mac_ctx->pdev, + vdev_id); +} + +void csr_purge_pdev_all_ser_cmd_list(struct mac_context *mac_ctx) +{ + wlan_serialization_purge_all_pdev_cmd(mac_ctx->pdev); +} + +tListElem *csr_nonscan_active_ll_peek_head(struct mac_context *mac_ctx, + bool inter_locked) +{ + struct wlan_serialization_command *cmd; + tSmeCmd *sme_cmd; + + cmd = wlan_serialization_peek_head_active_cmd_using_psoc(mac_ctx->psoc, + false); + if (!cmd) + return NULL; + + sme_cmd = cmd->umac_cmd; + + return &sme_cmd->Link; +} + +tListElem *csr_nonscan_pending_ll_peek_head(struct mac_context *mac_ctx, + bool inter_locked) +{ + struct wlan_serialization_command *cmd; + tSmeCmd *sme_cmd; + + cmd = wlan_serialization_peek_head_pending_cmd_using_psoc(mac_ctx->psoc, + false); + if (!cmd) + return NULL; + + sme_cmd = cmd->umac_cmd; + + return &sme_cmd->Link; +} + +bool csr_nonscan_active_ll_remove_entry(struct mac_context *mac_ctx, + tListElem *entry, bool inter_locked) +{ + tListElem *head; + + head = csr_nonscan_active_ll_peek_head(mac_ctx, inter_locked); + if (head == entry) + return true; + + return false; +} + +tListElem *csr_nonscan_pending_ll_next(struct mac_context *mac_ctx, + tListElem *entry, bool inter_locked) +{ + tSmeCmd *sme_cmd; + struct wlan_serialization_command cmd, *tcmd; + + if (!entry) + return NULL; + sme_cmd = GET_BASE_ADDR(entry, tSmeCmd, Link); + cmd.cmd_id = sme_cmd->cmd_id; + cmd.cmd_type = csr_get_cmd_type(sme_cmd); + cmd.vdev = wlan_objmgr_get_vdev_by_id_from_psoc_no_state( + mac_ctx->psoc, + sme_cmd->vdev_id, WLAN_LEGACY_SME_ID); + tcmd = wlan_serialization_get_pending_list_next_node_using_psoc( + mac_ctx->psoc, &cmd, false); + if (cmd.vdev) + wlan_objmgr_vdev_release_ref(cmd.vdev, WLAN_LEGACY_SME_ID); + if (!tcmd) { + sme_err("No cmd found"); + return NULL; + } + sme_cmd = tcmd->umac_cmd; + return &sme_cmd->Link; +} + +bool csr_get_bss_id_bss_desc(struct bss_description *pSirBssDesc, + struct qdf_mac_addr *pBssId) +{ + qdf_mem_copy(pBssId, &pSirBssDesc->bssId[0], + sizeof(struct qdf_mac_addr)); + return true; +} + +bool csr_is_bss_id_equal(struct bss_description *pSirBssDesc1, + struct bss_description *pSirBssDesc2) +{ + bool fEqual = false; + struct qdf_mac_addr bssId1; + struct qdf_mac_addr bssId2; + + do { + if (!pSirBssDesc1) + break; + if (!pSirBssDesc2) + break; + + if (!csr_get_bss_id_bss_desc(pSirBssDesc1, &bssId1)) + break; + if (!csr_get_bss_id_bss_desc(pSirBssDesc2, &bssId2)) + break; + + fEqual = qdf_is_macaddr_equal(&bssId1, &bssId2); + } while (0); + + return fEqual; +} + +static bool csr_is_conn_state(struct mac_context *mac_ctx, uint32_t session_id, + eCsrConnectState state) +{ + QDF_BUG(session_id < WLAN_MAX_VDEVS); + if (session_id >= WLAN_MAX_VDEVS) + return false; + + return mac_ctx->roam.roamSession[session_id].connectState == state; +} + +bool csr_is_conn_state_connected_infra(struct mac_context *mac_ctx, + uint32_t session_id) +{ + return csr_is_conn_state(mac_ctx, session_id, + eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED); +} + +bool csr_is_conn_state_connected(struct mac_context *mac, uint32_t sessionId) +{ + return csr_is_conn_state_connected_ibss(mac, sessionId) || + csr_is_conn_state_connected_infra(mac, sessionId) || + csr_is_conn_state_connected_wds(mac, sessionId); +} + +bool csr_is_conn_state_infra(struct mac_context *mac, uint32_t sessionId) +{ + return csr_is_conn_state_connected_infra(mac, sessionId); +} + +static tSirMacCapabilityInfo csr_get_bss_capabilities(struct bss_description * + pSirBssDesc) +{ + tSirMacCapabilityInfo dot11Caps; + + /* tSirMacCapabilityInfo is 16-bit */ + qdf_get_u16((uint8_t *) &pSirBssDesc->capabilityInfo, + (uint16_t *) &dot11Caps); + + return dot11Caps; +} + +#ifdef QCA_IBSS_SUPPORT +bool csr_is_conn_state_connected_ibss(struct mac_context *mac_ctx, + uint32_t session_id) +{ + return csr_is_conn_state(mac_ctx, session_id, + eCSR_ASSOC_STATE_TYPE_IBSS_CONNECTED); +} + +bool csr_is_conn_state_disconnected_ibss(struct mac_context *mac_ctx, + uint32_t session_id) +{ + return csr_is_conn_state(mac_ctx, session_id, + eCSR_ASSOC_STATE_TYPE_IBSS_DISCONNECTED); +} + +bool csr_is_conn_state_ibss(struct mac_context *mac, uint32_t sessionId) +{ + return csr_is_conn_state_connected_ibss(mac, sessionId) || + csr_is_conn_state_disconnected_ibss(mac, sessionId); +} + +bool csr_is_bss_type_ibss(eCsrRoamBssType bssType) +{ + return (bool) + (eCSR_BSS_TYPE_START_IBSS == bssType + || eCSR_BSS_TYPE_IBSS == bssType); +} + +bool csr_is_ibss_bss_desc(struct bss_description *pSirBssDesc) +{ + tSirMacCapabilityInfo dot11Caps = csr_get_bss_capabilities(pSirBssDesc); + + return (bool) dot11Caps.ibss; +} +#endif + +bool csr_is_conn_state_connected_wds(struct mac_context *mac_ctx, + uint32_t session_id) +{ + return csr_is_conn_state(mac_ctx, session_id, + eCSR_ASSOC_STATE_TYPE_WDS_CONNECTED); +} + +bool csr_is_conn_state_connected_infra_ap(struct mac_context *mac_ctx, + uint32_t session_id) +{ + return csr_is_conn_state(mac_ctx, session_id, + eCSR_ASSOC_STATE_TYPE_INFRA_CONNECTED) || + csr_is_conn_state(mac_ctx, session_id, + eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTED); +} + +bool csr_is_conn_state_disconnected_wds(struct mac_context *mac_ctx, + uint32_t session_id) +{ + return csr_is_conn_state(mac_ctx, session_id, + eCSR_ASSOC_STATE_TYPE_WDS_DISCONNECTED); +} + +bool csr_is_conn_state_wds(struct mac_context *mac, uint32_t sessionId) +{ + return csr_is_conn_state_connected_wds(mac, sessionId) || + csr_is_conn_state_disconnected_wds(mac, sessionId); +} + +enum csr_cfgdot11mode +csr_get_vdev_dot11_mode(struct mac_context *mac, + enum QDF_OPMODE device_mode, + enum csr_cfgdot11mode curr_dot11_mode) +{ + enum mlme_vdev_dot11_mode vdev_dot11_mode; + uint8_t dot11_mode_indx; + enum csr_cfgdot11mode dot11_mode = curr_dot11_mode; + uint32_t vdev_type_dot11_mode = + mac->mlme_cfg->dot11_mode.vdev_type_dot11_mode; + + sme_debug("curr_dot11_mode %d, vdev_dot11 %08X, dev_mode %d", + curr_dot11_mode, vdev_type_dot11_mode, device_mode); + + switch (device_mode) { + case QDF_STA_MODE: + dot11_mode_indx = STA_DOT11_MODE_INDX; + break; + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_DEVICE_MODE: + dot11_mode_indx = P2P_DEV_DOT11_MODE_INDX; + break; + case QDF_TDLS_MODE: + dot11_mode_indx = TDLS_DOT11_MODE_INDX; + break; + case QDF_NAN_DISC_MODE: + dot11_mode_indx = NAN_DISC_DOT11_MODE_INDX; + break; + case QDF_NDI_MODE: + dot11_mode_indx = NDI_DOT11_MODE_INDX; + break; + case QDF_OCB_MODE: + dot11_mode_indx = OCB_DOT11_MODE_INDX; + break; + default: + return dot11_mode; + } + vdev_dot11_mode = CSR_GET_BITS(vdev_type_dot11_mode, + dot11_mode_indx, 4); + if (vdev_dot11_mode == MLME_VDEV_DOT11_MODE_AUTO) + dot11_mode = curr_dot11_mode; + + if (CSR_IS_DOT11_MODE_11N(curr_dot11_mode) && + vdev_dot11_mode == MLME_VDEV_DOT11_MODE_11N) + dot11_mode = eCSR_CFG_DOT11_MODE_11N; + + if (CSR_IS_DOT11_MODE_11AC(curr_dot11_mode) && + vdev_dot11_mode == MLME_VDEV_DOT11_MODE_11AC) + dot11_mode = eCSR_CFG_DOT11_MODE_11AC; + + if (CSR_IS_DOT11_MODE_11AX(curr_dot11_mode) && + vdev_dot11_mode == MLME_VDEV_DOT11_MODE_11AX) + dot11_mode = eCSR_CFG_DOT11_MODE_11AX; + + sme_debug("INI vdev_dot11_mode %d new dot11_mode %d", + vdev_dot11_mode, dot11_mode); + + return dot11_mode; +} + +static bool csr_is_conn_state_ap(struct mac_context *mac, uint32_t sessionId) +{ + struct csr_roam_session *pSession; + + pSession = CSR_GET_SESSION(mac, sessionId); + if (!pSession) + return false; + if (CSR_IS_INFRA_AP(&pSession->connectedProfile)) + return true; + return false; +} + +bool csr_is_any_session_in_connect_state(struct mac_context *mac) +{ + uint32_t i; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (CSR_IS_SESSION_VALID(mac, i) && + (csr_is_conn_state_infra(mac, i) || + csr_is_conn_state_ibss(mac, i) || + csr_is_conn_state_ap(mac, i))) { + return true; + } + } + + return false; +} + +uint32_t csr_get_infra_operation_chan_freq( + struct mac_context *mac, uint8_t vdev_id) +{ + uint32_t chan_freq = 0; + struct csr_roam_session *session; + + session = CSR_GET_SESSION(mac, vdev_id); + if (!session) + return chan_freq; + + if (CSR_IS_SESSION_VALID(mac, vdev_id)) + chan_freq = session->connectedProfile.op_freq; + + return chan_freq; +} + +bool csr_is_session_client_and_connected(struct mac_context *mac, uint8_t sessionId) +{ + struct csr_roam_session *pSession = NULL; + + if (CSR_IS_SESSION_VALID(mac, sessionId) + && csr_is_conn_state_infra(mac, sessionId)) { + pSession = CSR_GET_SESSION(mac, sessionId); + if (pSession->pCurRoamProfile) { + if ((pSession->pCurRoamProfile->csrPersona == + QDF_STA_MODE) + || (pSession->pCurRoamProfile->csrPersona == + QDF_P2P_CLIENT_MODE)) + return true; + } + } + return false; +} + +uint32_t csr_get_concurrent_operation_freq(struct mac_context *mac_ctx) +{ + struct csr_roam_session *session = NULL; + uint8_t i = 0; + enum QDF_OPMODE persona; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (!CSR_IS_SESSION_VALID(mac_ctx, i)) + continue; + session = CSR_GET_SESSION(mac_ctx, i); + if (!session->pCurRoamProfile) + continue; + persona = session->pCurRoamProfile->csrPersona; + if ((((persona == QDF_STA_MODE) || + (persona == QDF_P2P_CLIENT_MODE)) && + (session->connectState == + eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED)) || + (((persona == QDF_P2P_GO_MODE) || + (persona == QDF_SAP_MODE)) + && (session->connectState != + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED))) + return session->connectedProfile.op_freq; + } + return 0; +} + +uint32_t csr_get_beaconing_concurrent_channel(struct mac_context *mac_ctx, + uint8_t vdev_id_to_skip) +{ + struct csr_roam_session *session = NULL; + uint8_t i = 0; + enum QDF_OPMODE persona; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (i == vdev_id_to_skip) + continue; + if (!CSR_IS_SESSION_VALID(mac_ctx, i)) + continue; + session = CSR_GET_SESSION(mac_ctx, i); + if (!session->pCurRoamProfile) + continue; + persona = session->pCurRoamProfile->csrPersona; + if (((persona == QDF_P2P_GO_MODE) || + (persona == QDF_SAP_MODE)) && + (session->connectState != + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED)) + return session->connectedProfile.op_freq; + } + + return 0; +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH + +#define HALF_BW_OF(eCSR_bw_val) ((eCSR_bw_val)/2) + +/* calculation of center channel based on V/HT BW and WIFI channel bw=5MHz) */ + +#define CSR_GET_HT40_PLUS_CCH(och) ((och) + 10) +#define CSR_GET_HT40_MINUS_CCH(och) ((och) - 10) + +#define CSR_GET_HT80_PLUS_LL_CCH(och) ((och) + 30) +#define CSR_GET_HT80_PLUS_HL_CCH(och) ((och) + 30) +#define CSR_GET_HT80_MINUS_LH_CCH(och) ((och) - 10) +#define CSR_GET_HT80_MINUS_HH_CCH(och) ((och) - 30) + +/** + * csr_get_ch_from_ht_profile() - to get channel from HT profile + * @mac: pointer to Mac context + * @htp: pointer to HT profile + * @och_freq: operating channel frequency + * @cfreq: channel frequency + * @hbw: half bandwidth + * + * This function will fill half bandwidth and channel frequency based + * on the HT profile + * + * Return: none + */ +static void csr_get_ch_from_ht_profile(struct mac_context *mac, + tCsrRoamHTProfile *htp, + uint32_t och_freq, uint32_t *cfreq, + uint32_t *hbw) +{ + uint32_t ch_bond; + struct ch_params chan_params = {0}; + + if (!WLAN_REG_IS_24GHZ_CH_FREQ(och_freq)) + ch_bond = mac->roam.configParam.channelBondingMode5GHz; + else + ch_bond = mac->roam.configParam.channelBondingMode24GHz; + + *hbw = HALF_BW_OF(eCSR_BW_20MHz_VAL); + + if (!ch_bond) + goto ret; + + sme_debug("HTC: %d scbw: %d rcbw: %d sco: %d VHTC: %d apc: %d apbw: %d", + htp->htCapability, htp->htSupportedChannelWidthSet, + htp->htRecommendedTxWidthSet, + htp->htSecondaryChannelOffset, + htp->vhtCapability, htp->apCenterChan, htp->apChanWidth + ); + + if (htp->vhtCapability) { + if (htp->apChanWidth == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) + *hbw = HALF_BW_OF(eCSR_BW_80MHz_VAL); + else if (htp->apChanWidth == WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) + *hbw = HALF_BW_OF(eCSR_BW_160MHz_VAL); + + if (!*hbw && htp->htCapability) { + if (htp->htSupportedChannelWidthSet == + eHT_CHANNEL_WIDTH_40MHZ) + *hbw = HALF_BW_OF(eCSR_BW_40MHz_VAL); + else + *hbw = HALF_BW_OF(eCSR_BW_20MHz_VAL); + } + } else if (htp->htCapability) { + if (htp->htSupportedChannelWidthSet == + eHT_CHANNEL_WIDTH_40MHZ) { + *hbw = HALF_BW_OF(eCSR_BW_40MHz_VAL); + } else { + *hbw = HALF_BW_OF(eCSR_BW_20MHz_VAL); + } + } +ret: + switch (*hbw * 2) { + case eCSR_BW_40MHz_VAL: + chan_params.ch_width = CH_WIDTH_40MHZ; + break; + case eCSR_BW_80MHz_VAL: + chan_params.ch_width = CH_WIDTH_80MHZ; + break; + case eCSR_BW_160MHz_VAL: + chan_params.ch_width = CH_WIDTH_160MHZ; + break; + default: + chan_params.ch_width = CH_WIDTH_20MHZ; + break; + } + wlan_reg_set_channel_params_for_freq(mac->pdev, och_freq, 0, + &chan_params); + + *cfreq = chan_params.mhz_freq_seg0; +} + +/** + * csr_calc_chb_for_sap_phymode() - to calc channel bandwidth for sap phymode + * @mac_ctx: pointer to mac context + * @sap_ch: SAP operating channel + * @sap_phymode: SAP physical mode + * @sap_cch: concurrency channel + * @sap_hbw: SAP half bw + * @chb: channel bandwidth + * + * This routine is called to calculate channel bandwidth + * + * Return: none + */ +static void csr_calc_chb_for_sap_phymode(struct mac_context *mac_ctx, + uint32_t *sap_ch, eCsrPhyMode *sap_phymode, + uint32_t *sap_cch, uint32_t *sap_hbw, uint8_t *chb) +{ + if (*sap_phymode == eCSR_DOT11_MODE_11n || + *sap_phymode == eCSR_DOT11_MODE_11n_ONLY) { + + *sap_hbw = HALF_BW_OF(eCSR_BW_40MHz_VAL); + if (*chb == PHY_DOUBLE_CHANNEL_LOW_PRIMARY) + *sap_cch = CSR_GET_HT40_PLUS_CCH(*sap_ch); + else if (*chb == PHY_DOUBLE_CHANNEL_HIGH_PRIMARY) + *sap_cch = CSR_GET_HT40_MINUS_CCH(*sap_ch); + + } else if (*sap_phymode == eCSR_DOT11_MODE_11ac || + *sap_phymode == eCSR_DOT11_MODE_11ac_ONLY || + *sap_phymode == eCSR_DOT11_MODE_11ax || + *sap_phymode == eCSR_DOT11_MODE_11ax_ONLY) { + /*11AC only 80/40/20 Mhz supported in Rome */ + if (mac_ctx->roam.configParam.nVhtChannelWidth == + (WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ + 1)) { + *sap_hbw = HALF_BW_OF(eCSR_BW_80MHz_VAL); + if (*chb == + (PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW - 1)) + *sap_cch = CSR_GET_HT80_PLUS_LL_CCH(*sap_ch); + else if (*chb == + (PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW + - 1)) + *sap_cch = CSR_GET_HT80_PLUS_HL_CCH(*sap_ch); + else if (*chb == + (PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH + - 1)) + *sap_cch = CSR_GET_HT80_MINUS_LH_CCH(*sap_ch); + else if (*chb == + (PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH + - 1)) + *sap_cch = CSR_GET_HT80_MINUS_HH_CCH(*sap_ch); + } else { + *sap_hbw = HALF_BW_OF(eCSR_BW_40MHz_VAL); + if (*chb == (PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW + - 1)) + *sap_cch = CSR_GET_HT40_PLUS_CCH(*sap_ch); + else if (*chb == + (PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW + - 1)) + *sap_cch = CSR_GET_HT40_MINUS_CCH(*sap_ch); + else if (*chb == + (PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH + - 1)) + *sap_cch = CSR_GET_HT40_PLUS_CCH(*sap_ch); + else if (*chb == + (PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH + - 1)) + *sap_cch = CSR_GET_HT40_MINUS_CCH(*sap_ch); + } + } +} + +/** + * csr_handle_conc_chnl_overlap_for_sap_go - To handle overlap for AP+AP + * @mac_ctx: pointer to mac context + * @session: Current session + * @sap_ch_freq: SAP/GO operating channel frequency + * @sap_hbw: SAP/GO half bw + * @sap_cfreq: SAP/GO channel frequency + * @intf_ch_freq: concurrent SAP/GO operating channel frequency + * @intf_hbw: concurrent SAP/GO half bw + * @intf_cfreq: concurrent SAP/GO channel frequency + * + * This routine is called to check if one SAP/GO channel is overlapping with + * other SAP/GO channel + * + * Return: none + */ +static void csr_handle_conc_chnl_overlap_for_sap_go( + struct mac_context *mac_ctx, + struct csr_roam_session *session, + uint32_t *sap_ch_freq, uint32_t *sap_hbw, uint32_t *sap_cfreq, + uint32_t *intf_ch_freq, uint32_t *intf_hbw, + uint32_t *intf_cfreq) +{ + uint32_t op_chan_freq; + + op_chan_freq = session->connectedProfile.op_freq; + /* + * if conc_custom_rule1 is defined then we don't + * want p2pgo to follow SAP's channel or SAP to + * follow P2PGO's channel. + */ + if (0 == mac_ctx->roam.configParam.conc_custom_rule1 && + 0 == mac_ctx->roam.configParam.conc_custom_rule2) { + if (*sap_ch_freq == 0) { + *sap_ch_freq = op_chan_freq; + csr_get_ch_from_ht_profile(mac_ctx, + &session->connectedProfile.ht_profile, + *sap_ch_freq, sap_cfreq, sap_hbw); + } else if (*sap_ch_freq != op_chan_freq) { + *intf_ch_freq = op_chan_freq; + csr_get_ch_from_ht_profile(mac_ctx, + &session->connectedProfile.ht_profile, + *intf_ch_freq, intf_cfreq, intf_hbw); + } + } else if (*sap_ch_freq == 0 && + (session->pCurRoamProfile->csrPersona == + QDF_SAP_MODE)) { + *sap_ch_freq = op_chan_freq; + csr_get_ch_from_ht_profile(mac_ctx, + &session->connectedProfile.ht_profile, + *sap_ch_freq, sap_cfreq, sap_hbw); + } +} + + +/** + * csr_check_concurrent_channel_overlap() - To check concurrent overlap chnls + * @mac_ctx: Pointer to mac context + * @sap_ch: SAP channel + * @sap_phymode: SAP phy mode + * @cc_switch_mode: concurrent switch mode + * + * This routine will be called to check concurrent overlap channels + * + * Return: uint16_t + */ +uint16_t csr_check_concurrent_channel_overlap(struct mac_context *mac_ctx, + uint32_t sap_ch_freq, eCsrPhyMode sap_phymode, + uint8_t cc_switch_mode) +{ + struct csr_roam_session *session = NULL; + uint8_t i = 0, chb = PHY_SINGLE_CHANNEL_CENTERED; + uint32_t intf_ch_freq = 0, sap_hbw = 0, intf_hbw = 0, intf_cfreq = 0; + uint32_t sap_cfreq = 0; + uint32_t sap_lfreq, sap_hfreq, intf_lfreq, intf_hfreq; + QDF_STATUS status; + + if (mac_ctx->roam.configParam.cc_switch_mode == + QDF_MCC_TO_SCC_SWITCH_DISABLE) + return 0; + + if (sap_ch_freq != 0) { + sap_cfreq = sap_ch_freq; + sap_hbw = HALF_BW_OF(eCSR_BW_20MHz_VAL); + + if (!WLAN_REG_IS_24GHZ_CH_FREQ(sap_ch_freq)) + chb = mac_ctx->roam.configParam.channelBondingMode5GHz; + else + chb = mac_ctx->roam.configParam.channelBondingMode24GHz; + + if (chb) + csr_calc_chb_for_sap_phymode(mac_ctx, &sap_ch_freq, + &sap_phymode, &sap_cfreq, + &sap_hbw, &chb); + } + + sme_debug("sap_ch:%d sap_phymode:%d sap_cch:%d sap_hbw:%d chb:%d", + sap_ch_freq, sap_phymode, sap_cfreq, sap_hbw, chb); + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (!CSR_IS_SESSION_VALID(mac_ctx, i)) + continue; + + session = CSR_GET_SESSION(mac_ctx, i); + if (!session->pCurRoamProfile) + continue; + if (((session->pCurRoamProfile->csrPersona == QDF_STA_MODE) || + (session->pCurRoamProfile->csrPersona == + QDF_P2P_CLIENT_MODE)) && + (session->connectState == + eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED)) { + intf_ch_freq = session->connectedProfile.op_freq; + csr_get_ch_from_ht_profile(mac_ctx, + &session->connectedProfile.ht_profile, + intf_ch_freq, &intf_cfreq, &intf_hbw); + sme_debug("%d: intf_ch:%d intf_cfreq:%d intf_hbw:%d", + i, intf_ch_freq, intf_cfreq, intf_hbw); + } else if (((session->pCurRoamProfile->csrPersona == + QDF_P2P_GO_MODE) || + (session->pCurRoamProfile->csrPersona == + QDF_SAP_MODE)) && + (session->connectState != + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED)) { + if (session->ch_switch_in_progress) + continue; + + csr_handle_conc_chnl_overlap_for_sap_go(mac_ctx, + session, &sap_ch_freq, &sap_hbw, + &sap_cfreq, &intf_ch_freq, &intf_hbw, + &intf_cfreq); + } + if (intf_ch_freq && + ((intf_ch_freq <= wlan_reg_ch_to_freq(CHAN_ENUM_2484) && + sap_ch_freq <= wlan_reg_ch_to_freq(CHAN_ENUM_2484)) || + (intf_ch_freq > wlan_reg_ch_to_freq(CHAN_ENUM_2484) && + sap_ch_freq > wlan_reg_ch_to_freq(CHAN_ENUM_2484)))) + break; + } + + sme_debug("intf_ch:%d sap_ch:%d cc_switch_mode:%d, dbs:%d", + intf_ch_freq, sap_ch_freq, cc_switch_mode, + policy_mgr_is_dbs_enable(mac_ctx->psoc)); + + if (intf_ch_freq && sap_ch_freq != intf_ch_freq && + !policy_mgr_is_force_scc(mac_ctx->psoc)) { + sap_lfreq = sap_cfreq - sap_hbw; + sap_hfreq = sap_cfreq + sap_hbw; + intf_lfreq = intf_cfreq - intf_hbw; + intf_hfreq = intf_cfreq + intf_hbw; + + sme_debug("SAP: OCH: %03d CCH: %03d BW: %d LF: %d HF: %d INTF: OCH: %03d CF: %d BW: %d LF: %d HF: %d", + sap_ch_freq, sap_cfreq, sap_hbw * 2, + sap_lfreq, sap_hfreq, intf_ch_freq, + intf_cfreq, intf_hbw * 2, intf_lfreq, intf_hfreq); + + if (!(((sap_lfreq > intf_lfreq && sap_lfreq < intf_hfreq) || + (sap_hfreq > intf_lfreq && sap_hfreq < intf_hfreq)) || + ((intf_lfreq > sap_lfreq && intf_lfreq < sap_hfreq) || + (intf_hfreq > sap_lfreq && intf_hfreq < sap_hfreq)))) + intf_ch_freq = 0; + } else if (intf_ch_freq && sap_ch_freq != intf_ch_freq && + (policy_mgr_is_force_scc(mac_ctx->psoc))) { + if (!((intf_ch_freq <= wlan_reg_ch_to_freq(CHAN_ENUM_2484) && + sap_ch_freq <= wlan_reg_ch_to_freq(CHAN_ENUM_2484)) || + (intf_ch_freq > wlan_reg_ch_to_freq(CHAN_ENUM_2484) && + sap_ch_freq > wlan_reg_ch_to_freq(CHAN_ENUM_2484)))) { + if (policy_mgr_is_dbs_enable(mac_ctx->psoc) || + cc_switch_mode == + QDF_MCC_TO_SCC_WITH_PREFERRED_BAND) + intf_ch_freq = 0; + } else if (cc_switch_mode == + QDF_MCC_TO_SCC_SWITCH_WITH_FAVORITE_CHANNEL) { + status = policy_mgr_get_sap_mandatory_channel( + mac_ctx->psoc, + &intf_ch_freq); + if (QDF_IS_STATUS_ERROR(status)) + sme_err("no mandatory channel"); + } + } else if ((intf_ch_freq == sap_ch_freq) && (cc_switch_mode == + QDF_MCC_TO_SCC_SWITCH_WITH_FAVORITE_CHANNEL)) { + if (WLAN_REG_IS_24GHZ_CH_FREQ(intf_ch_freq)) { + status = + policy_mgr_get_sap_mandatory_channel( + mac_ctx->psoc, &intf_ch_freq); + if (QDF_IS_STATUS_ERROR(status)) + sme_err("no mandatory channel"); + } + } + + if (intf_ch_freq == sap_ch_freq) + intf_ch_freq = 0; + + sme_debug("##Concurrent Channels %s Interfering", + intf_ch_freq == 0 ? "Not" : "Are"); + + return intf_ch_freq; +} +#endif + +bool csr_is_all_session_disconnected(struct mac_context *mac) +{ + uint32_t i; + bool fRc = true; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (CSR_IS_SESSION_VALID(mac, i) + && !csr_is_conn_state_disconnected(mac, i)) { + fRc = false; + break; + } + } + + return fRc; +} + +uint8_t csr_get_connected_infra(struct mac_context *mac_ctx) +{ + uint32_t i; + uint8_t connected_session = WLAN_UMAC_VDEV_ID_MAX; + + for (i = 0; i < WLAN_MAX_VDEVS; i++) { + if (CSR_IS_SESSION_VALID(mac_ctx, i) + && csr_is_conn_state_connected_infra(mac_ctx, i)) { + connected_session = i; + break; + } + } + + return connected_session; +} + + +bool csr_is_concurrent_session_running(struct mac_context *mac) +{ + uint32_t sessionId, noOfCocurrentSession = 0; + eCsrConnectState connectState; + + bool fRc = false; + + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; sessionId++) { + if (CSR_IS_SESSION_VALID(mac, sessionId)) { + connectState = + mac->roam.roamSession[sessionId].connectState; + if ((eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED == + connectState) + || (eCSR_ASSOC_STATE_TYPE_INFRA_CONNECTED == + connectState) + || (eCSR_ASSOC_STATE_TYPE_INFRA_DISCONNECTED == + connectState)) { + ++noOfCocurrentSession; + } + } + } + + /* More than one session is Up and Running */ + if (noOfCocurrentSession > 1) + fRc = true; + return fRc; +} + +bool csr_is_infra_ap_started(struct mac_context *mac) +{ + uint32_t sessionId; + bool fRc = false; + + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; sessionId++) { + if (CSR_IS_SESSION_VALID(mac, sessionId) && + (csr_is_conn_state_connected_infra_ap(mac, + sessionId))) { + fRc = true; + break; + } + } + + return fRc; + +} + +bool csr_is_conn_state_disconnected(struct mac_context *mac, uint32_t sessionId) +{ + return eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED == + mac->roam.roamSession[sessionId].connectState; +} + +/** + * csr_is_valid_mc_concurrent_session() - To check concurren session is valid + * @mac_ctx: pointer to mac context + * @session_id: session id + * @bss_descr: bss description + * + * This function validates the concurrent session + * + * Return: true or false + */ +bool csr_is_valid_mc_concurrent_session(struct mac_context *mac_ctx, + uint32_t session_id, + struct bss_description *bss_descr) +{ + struct csr_roam_session *pSession = NULL; + + /* Check for MCC support */ + if (!mac_ctx->roam.configParam.fenableMCCMode) + return false; + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) + return false; + /* Validate BeaconInterval */ + pSession = CSR_GET_SESSION(mac_ctx, session_id); + if (!pSession->pCurRoamProfile) + return false; + if (QDF_STATUS_SUCCESS == csr_validate_mcc_beacon_interval( + mac_ctx, + bss_descr->chan_freq, + &bss_descr->beaconInterval, session_id, + pSession->pCurRoamProfile->csrPersona)) + return true; + return false; +} + +bool csr_is_infra_bss_desc(struct bss_description *pSirBssDesc) +{ + tSirMacCapabilityInfo dot11Caps = csr_get_bss_capabilities(pSirBssDesc); + + return (bool) dot11Caps.ess; +} + +static bool csr_is_qos_bss_desc(struct bss_description *pSirBssDesc) +{ + tSirMacCapabilityInfo dot11Caps = csr_get_bss_capabilities(pSirBssDesc); + + return (bool) dot11Caps.qos; +} + +bool csr_is11h_supported(struct mac_context *mac) +{ + return mac->mlme_cfg->gen.enabled_11h; +} + +bool csr_is11e_supported(struct mac_context *mac) +{ + return mac->roam.configParam.Is11eSupportEnabled; +} + +bool csr_is_wmm_supported(struct mac_context *mac) +{ + if (eCsrRoamWmmNoQos == mac->roam.configParam.WMMSupportMode) + return false; + else + return true; +} + +/* pIes is the IEs for pSirBssDesc2 */ +bool csr_is_ssid_equal(struct mac_context *mac, + struct bss_description *pSirBssDesc1, + struct bss_description *pSirBssDesc2, + tDot11fBeaconIEs *pIes2) +{ + bool fEqual = false; + tSirMacSSid Ssid1, Ssid2; + tDot11fBeaconIEs *pIes1 = NULL; + tDot11fBeaconIEs *pIesLocal = pIes2; + + do { + if ((!pSirBssDesc1) || (!pSirBssDesc2)) + break; + if (!pIesLocal + && + !QDF_IS_STATUS_SUCCESS(csr_get_parsed_bss_description_ies + (mac, pSirBssDesc2, + &pIesLocal))) { + sme_err("fail to parse IEs"); + break; + } + if (!QDF_IS_STATUS_SUCCESS + (csr_get_parsed_bss_description_ies(mac, + pSirBssDesc1, &pIes1))) { + break; + } + if ((!pIes1->SSID.present) || (!pIesLocal->SSID.present)) + break; + if (pIes1->SSID.num_ssid != pIesLocal->SSID.num_ssid) + break; + qdf_mem_copy(Ssid1.ssId, pIes1->SSID.ssid, + pIes1->SSID.num_ssid); + qdf_mem_copy(Ssid2.ssId, pIesLocal->SSID.ssid, + pIesLocal->SSID.num_ssid); + + fEqual = (!qdf_mem_cmp(Ssid1.ssId, Ssid2.ssId, + pIesLocal->SSID.num_ssid)); + + } while (0); + if (pIes1) + qdf_mem_free(pIes1); + if (pIesLocal && !pIes2) + qdf_mem_free(pIesLocal); + + return fEqual; +} + +/* pIes can be passed in as NULL if the caller doesn't have one prepared */ +static bool csr_is_bss_description_wme(struct mac_context *mac, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes) +{ + /* Assume that WME is found... */ + bool fWme = true; + tDot11fBeaconIEs *pIesTemp = pIes; + + do { + if (!pIesTemp) { + if (!QDF_IS_STATUS_SUCCESS + (csr_get_parsed_bss_description_ies + (mac, pSirBssDesc, &pIesTemp))) { + fWme = false; + break; + } + } + /* if the Wme Info IE is found, then WME is supported... */ + if (CSR_IS_QOS_BSS(pIesTemp)) + break; + /* if none of these are found, then WME is NOT supported... */ + fWme = false; + } while (0); + if (!csr_is_wmm_supported(mac) && fWme) + if (!pIesTemp->HTCaps.present) + fWme = false; + + if ((!pIes) && (pIesTemp)) + /* we allocate memory here so free it before returning */ + qdf_mem_free(pIesTemp); + + return fWme; +} + +eCsrMediaAccessType +csr_get_qos_from_bss_desc(struct mac_context *mac_ctx, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes) +{ + eCsrMediaAccessType qosType = eCSR_MEDIUM_ACCESS_DCF; + + if (!pIes) { + QDF_ASSERT(pIes); + return qosType; + } + + do { + /* If we find WMM in the Bss Description, then we let this + * override and use WMM. + */ + if (csr_is_bss_description_wme(mac_ctx, pSirBssDesc, pIes)) + qosType = eCSR_MEDIUM_ACCESS_WMM_eDCF_DSCP; + else { + /* If the QoS bit is on, then the AP is + * advertising 11E QoS. + */ + if (csr_is_qos_bss_desc(pSirBssDesc)) + qosType = eCSR_MEDIUM_ACCESS_11e_eDCF; + else + qosType = eCSR_MEDIUM_ACCESS_DCF; + + /* Scale back based on the types turned on + * for the adapter. + */ + if (eCSR_MEDIUM_ACCESS_11e_eDCF == qosType + && !csr_is11e_supported(mac_ctx)) + qosType = eCSR_MEDIUM_ACCESS_DCF; + } + + } while (0); + + return qosType; +} + +/* Caller allocates memory for pIEStruct */ +QDF_STATUS csr_parse_bss_description_ies(struct mac_context *mac_ctx, + struct bss_description *bss_desc, + tDot11fBeaconIEs *pIEStruct) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + int ieLen = + (int)(bss_desc->length + sizeof(bss_desc->length) - + GET_FIELD_OFFSET(struct bss_description, ieFields)); + + if (ieLen > 0 && pIEStruct) { + if (!DOT11F_FAILED(dot11f_unpack_beacon_i_es + (mac_ctx, (uint8_t *)bss_desc->ieFields, + ieLen, pIEStruct, false))) + status = QDF_STATUS_SUCCESS; + } + + return status; +} + +/* This function will allocate memory for the parsed IEs to the caller. + * Caller must free the memory after it is done with the data only if + * this function succeeds + */ +QDF_STATUS csr_get_parsed_bss_description_ies(struct mac_context *mac_ctx, + struct bss_description *bss_desc, + tDot11fBeaconIEs **ppIEStruct) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + + if (bss_desc && ppIEStruct) { + *ppIEStruct = qdf_mem_malloc(sizeof(tDot11fBeaconIEs)); + if ((*ppIEStruct) != NULL) { + status = csr_parse_bss_description_ies(mac_ctx, + bss_desc, + *ppIEStruct); + if (!QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(*ppIEStruct); + *ppIEStruct = NULL; + } + } else { + sme_err("failed to allocate memory"); + QDF_ASSERT(0); + return QDF_STATUS_E_NOMEM; + } + } + + return status; +} + +bool csr_is_nullssid(uint8_t *pBssSsid, uint8_t len) +{ + bool fNullSsid = false; + + uint32_t SsidLength; + uint8_t *pSsidStr; + + do { + if (0 == len) { + fNullSsid = true; + break; + } + /* Consider 0 or space for hidden SSID */ + if (0 == pBssSsid[0]) { + fNullSsid = true; + break; + } + + SsidLength = len; + pSsidStr = pBssSsid; + + while (SsidLength) { + if (*pSsidStr) + break; + + pSsidStr++; + SsidLength--; + } + + if (0 == SsidLength) { + fNullSsid = true; + break; + } + } while (0); + + return fNullSsid; +} + +uint32_t csr_get_frag_thresh(struct mac_context *mac_ctx) +{ + return mac_ctx->mlme_cfg->threshold.frag_threshold; +} + +uint32_t csr_get_rts_thresh(struct mac_context *mac_ctx) +{ + return mac_ctx->mlme_cfg->threshold.rts_threshold; +} + +static eCsrPhyMode +csr_translate_to_phy_mode_from_bss_desc(struct mac_context *mac_ctx, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *ies) +{ + eCsrPhyMode phyMode; + uint8_t i; + + switch (pSirBssDesc->nwType) { + case eSIR_11A_NW_TYPE: + phyMode = eCSR_DOT11_MODE_11a; + break; + + case eSIR_11B_NW_TYPE: + phyMode = eCSR_DOT11_MODE_11b; + break; + + case eSIR_11G_NW_TYPE: + phyMode = eCSR_DOT11_MODE_11g_ONLY; + + /* Check if the BSS is in b/g mixed mode or g_only mode */ + if (!ies || !ies->SuppRates.present) { + sme_debug("Unable to get rates, assume G only mode"); + break; + } + + for (i = 0; i < ies->SuppRates.num_rates; i++) { + if (csr_rates_is_dot11_rate11b_supported_rate( + ies->SuppRates.rates[i])) { + sme_debug("One B rate is supported"); + phyMode = eCSR_DOT11_MODE_11g; + break; + } + } + break; + case eSIR_11N_NW_TYPE: + phyMode = eCSR_DOT11_MODE_11n; + break; + case eSIR_11AX_NW_TYPE: + phyMode = eCSR_DOT11_MODE_11ax; + break; + case eSIR_11AC_NW_TYPE: + default: + phyMode = eCSR_DOT11_MODE_11ac; + break; + } + return phyMode; +} + +uint32_t csr_translate_to_wni_cfg_dot11_mode(struct mac_context *mac, + enum csr_cfgdot11mode csrDot11Mode) +{ + uint32_t ret; + + switch (csrDot11Mode) { + case eCSR_CFG_DOT11_MODE_AUTO: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) + ret = MLME_DOT11_MODE_11AX; + else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + ret = MLME_DOT11_MODE_11AC; + else + ret = MLME_DOT11_MODE_11N; + break; + case eCSR_CFG_DOT11_MODE_11A: + ret = MLME_DOT11_MODE_11A; + break; + case eCSR_CFG_DOT11_MODE_11B: + ret = MLME_DOT11_MODE_11B; + break; + case eCSR_CFG_DOT11_MODE_11G: + ret = MLME_DOT11_MODE_11G; + break; + case eCSR_CFG_DOT11_MODE_11N: + ret = MLME_DOT11_MODE_11N; + break; + case eCSR_CFG_DOT11_MODE_11G_ONLY: + ret = MLME_DOT11_MODE_11G_ONLY; + break; + case eCSR_CFG_DOT11_MODE_11N_ONLY: + ret = MLME_DOT11_MODE_11N_ONLY; + break; + case eCSR_CFG_DOT11_MODE_11AC_ONLY: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + ret = MLME_DOT11_MODE_11AC_ONLY; + else + ret = MLME_DOT11_MODE_11N; + break; + case eCSR_CFG_DOT11_MODE_11AC: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + ret = MLME_DOT11_MODE_11AC; + else + ret = MLME_DOT11_MODE_11N; + break; + case eCSR_CFG_DOT11_MODE_11AX_ONLY: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) + ret = MLME_DOT11_MODE_11AX_ONLY; + else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + ret = MLME_DOT11_MODE_11AC; + else + ret = MLME_DOT11_MODE_11N; + break; + case eCSR_CFG_DOT11_MODE_11AX: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) + ret = MLME_DOT11_MODE_11AX; + else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + ret = MLME_DOT11_MODE_11AC; + else + ret = MLME_DOT11_MODE_11N; + break; + default: + sme_warn("doesn't expect %d as csrDo11Mode", csrDot11Mode); + if (BAND_2G == mac->mlme_cfg->gen.band) + ret = MLME_DOT11_MODE_11G; + else + ret = MLME_DOT11_MODE_11A; + break; + } + + return ret; +} + +/** + * csr_get_phy_mode_from_bss() - Get Phy Mode + * @mac: Global MAC context + * @pBSSDescription: BSS Descriptor + * @pPhyMode: Physical Mode + * @pIes: Pointer to the IE fields + * + * This function should only return the super set of supported modes + * 11n implies 11b/g/a/n. + * + * Return: success + **/ +QDF_STATUS csr_get_phy_mode_from_bss(struct mac_context *mac, + struct bss_description *pBSSDescription, + eCsrPhyMode *pPhyMode, tDot11fBeaconIEs *pIes) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + eCsrPhyMode phyMode = + csr_translate_to_phy_mode_from_bss_desc(mac, pBSSDescription, + pIes); + + if (pIes) { + if (pIes->HTCaps.present) { + phyMode = eCSR_DOT11_MODE_11n; + if (IS_BSS_VHT_CAPABLE(pIes->VHTCaps) || + IS_BSS_VHT_CAPABLE(pIes->vendor_vht_ie.VHTCaps)) + phyMode = eCSR_DOT11_MODE_11ac; + if (pIes->he_cap.present) + phyMode = eCSR_DOT11_MODE_11ax; + } else if (WLAN_REG_IS_6GHZ_CHAN_FREQ( + pBSSDescription->chan_freq)) { + if (pIes->he_cap.present) + phyMode = eCSR_DOT11_MODE_11ax; + else + sme_debug("Warning - 6Ghz AP no he cap"); + } + + *pPhyMode = phyMode; + } + + return status; +} + +/** + * csr_get_phy_mode_in_use() - to get phymode + * @phyModeIn: physical mode + * @bssPhyMode: physical mode in bss + * @f5GhzBand: 5Ghz band + * @pCfgDot11ModeToUse: dot11 mode in use + * + * This function returns the correct eCSR_CFG_DOT11_MODE is the two phyModes + * matches. bssPhyMode is the mode derived from the BSS description + * f5GhzBand is derived from the channel id of BSS description + * + * Return: true or false + */ +static bool csr_get_phy_mode_in_use(struct mac_context *mac_ctx, + eCsrPhyMode phyModeIn, + eCsrPhyMode bssPhyMode, + bool f5GhzBand, + enum csr_cfgdot11mode *pCfgDot11ModeToUse) +{ + bool fMatch = false; + enum csr_cfgdot11mode cfgDot11Mode; + + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + switch (phyModeIn) { + /* 11a or 11b or 11g */ + case eCSR_DOT11_MODE_abg: + fMatch = true; + if (f5GhzBand) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11A; + else if (eCSR_DOT11_MODE_11b == bssPhyMode) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11B; + else + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11G; + break; + + case eCSR_DOT11_MODE_11a: + if (f5GhzBand) { + fMatch = true; + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11A; + } + break; + + case eCSR_DOT11_MODE_11g: + if (!f5GhzBand) { + fMatch = true; + if (eCSR_DOT11_MODE_11b == bssPhyMode) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11B; + else + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11G; + } + break; + + case eCSR_DOT11_MODE_11g_ONLY: + if ((bssPhyMode == eCSR_DOT11_MODE_11g) || + (bssPhyMode == eCSR_DOT11_MODE_11g_ONLY)) { + fMatch = true; + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11G; + } + break; + + case eCSR_DOT11_MODE_11b: + case eCSR_DOT11_MODE_11b_ONLY: + if (!f5GhzBand && (bssPhyMode != eCSR_DOT11_MODE_11g_ONLY)) { + fMatch = true; + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11B; + } + break; + + case eCSR_DOT11_MODE_11n: + fMatch = true; + switch (bssPhyMode) { + case eCSR_DOT11_MODE_11g: + case eCSR_DOT11_MODE_11g_ONLY: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11G; + break; + case eCSR_DOT11_MODE_11b: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11B; + break; + case eCSR_DOT11_MODE_11a: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11A; + break; + case eCSR_DOT11_MODE_11n: + case eCSR_DOT11_MODE_11ac: + case eCSR_DOT11_MODE_11ax: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + + default: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AC; + break; + } + break; + + case eCSR_DOT11_MODE_11n_ONLY: + if (eCSR_DOT11_MODE_11n == bssPhyMode) { + fMatch = true; + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + + } + + break; + case eCSR_DOT11_MODE_11ac: + fMatch = true; + switch (bssPhyMode) { + case eCSR_DOT11_MODE_11g: + case eCSR_DOT11_MODE_11g_ONLY: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11G; + break; + case eCSR_DOT11_MODE_11b: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11B; + break; + case eCSR_DOT11_MODE_11a: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11A; + break; + case eCSR_DOT11_MODE_11n: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + case eCSR_DOT11_MODE_11ac: + case eCSR_DOT11_MODE_11ax: + default: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AC; + break; + } + break; + + case eCSR_DOT11_MODE_11ac_ONLY: + if (eCSR_DOT11_MODE_11ac == bssPhyMode) { + fMatch = true; + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AC; + } + break; + case eCSR_DOT11_MODE_11ax: + fMatch = true; + switch (bssPhyMode) { + case eCSR_DOT11_MODE_11g: + case eCSR_DOT11_MODE_11g_ONLY: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11G; + break; + case eCSR_DOT11_MODE_11b: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11B; + break; + case eCSR_DOT11_MODE_11a: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11A; + break; + case eCSR_DOT11_MODE_11n: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + case eCSR_DOT11_MODE_11ac: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AC; + break; + case eCSR_DOT11_MODE_11ax: + default: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AX; + break; + } + break; + + case eCSR_DOT11_MODE_11ax_ONLY: + if (eCSR_DOT11_MODE_11ax == bssPhyMode) { + fMatch = true; + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AX; + } + break; + + default: + fMatch = true; + switch (bssPhyMode) { + case eCSR_DOT11_MODE_11g: + case eCSR_DOT11_MODE_11g_ONLY: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11G; + break; + case eCSR_DOT11_MODE_11b: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11B; + break; + case eCSR_DOT11_MODE_11a: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11A; + break; + case eCSR_DOT11_MODE_11n: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + case eCSR_DOT11_MODE_11ac: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AC; + break; + case eCSR_DOT11_MODE_11ax: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AX; + break; + default: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_AUTO; + break; + } + break; + } + + if (fMatch && pCfgDot11ModeToUse) { + if (cfgDot11Mode == eCSR_CFG_DOT11_MODE_11AX) { + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) + *pCfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11AX; + else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + *pCfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11AC; + else + *pCfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11N; + } else { + if (cfgDot11Mode == eCSR_CFG_DOT11_MODE_11AC + && (!IS_FEATURE_SUPPORTED_BY_FW(DOT11AC))) + *pCfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11N; + else + *pCfgDot11ModeToUse = cfgDot11Mode; + } + } + return fMatch; +} + +/** + * csr_is_phy_mode_match() - to find if phy mode matches + * @mac: pointer to mac context + * @phyMode: physical mode + * @pSirBssDesc: bss description + * @pProfile: pointer to roam profile + * @pReturnCfgDot11Mode: dot1 mode to return + * @pIes: pointer to IEs + * + * This function decides whether the one of the bit of phyMode is matching the + * mode in the BSS and allowed by the user setting + * + * Return: true or false based on mode that fits the criteria + */ +bool csr_is_phy_mode_match(struct mac_context *mac, uint32_t phyMode, + struct bss_description *pSirBssDesc, + struct csr_roam_profile *pProfile, + enum csr_cfgdot11mode *pReturnCfgDot11Mode, + tDot11fBeaconIEs *pIes) +{ + bool fMatch = false; + eCsrPhyMode phyModeInBssDesc = eCSR_DOT11_MODE_AUTO; + eCsrPhyMode phyMode2 = eCSR_DOT11_MODE_AUTO; + enum csr_cfgdot11mode cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_AUTO; + uint32_t bitMask, loopCount; + uint32_t bss_chan_freq; + + if (!pProfile) { + sme_err("profile not found"); + return fMatch; + } + + if (!QDF_IS_STATUS_SUCCESS(csr_get_phy_mode_from_bss(mac, pSirBssDesc, + &phyModeInBssDesc, pIes))) + return fMatch; + + bss_chan_freq = pSirBssDesc->chan_freq; + if (WLAN_REG_IS_6GHZ_CHAN_FREQ(bss_chan_freq)) { + if (pReturnCfgDot11Mode) + *pReturnCfgDot11Mode = eCSR_CFG_DOT11_MODE_11AX; + return true; + } + + if ((0 == phyMode) || (eCSR_DOT11_MODE_AUTO & phyMode)) { + if (eCSR_CFG_DOT11_MODE_ABG == + mac->roam.configParam.uCfgDot11Mode) { + phyMode = eCSR_DOT11_MODE_abg; + } else if (eCSR_CFG_DOT11_MODE_AUTO == + mac->roam.configParam.uCfgDot11Mode) { + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) + phyMode = eCSR_DOT11_MODE_11ax; + else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + phyMode = eCSR_DOT11_MODE_11ac; + else + phyMode = eCSR_DOT11_MODE_11n; + } else { + /* user's pick */ + phyMode = mac->roam.configParam.phyMode; + } + } + + if ((0 == phyMode) || (eCSR_DOT11_MODE_AUTO & phyMode)) { + if (0 != phyMode) { + if (eCSR_DOT11_MODE_AUTO & phyMode) { + phyMode2 = + eCSR_DOT11_MODE_AUTO & phyMode; + } + } else { + phyMode2 = phyMode; + } + fMatch = csr_get_phy_mode_in_use(mac, phyMode2, + phyModeInBssDesc, + !WLAN_REG_IS_24GHZ_CH_FREQ + (bss_chan_freq), + &cfgDot11ModeToUse); + } else { + bitMask = 1; + loopCount = 0; + while (loopCount < eCSR_NUM_PHY_MODE) { + phyMode2 = (phyMode & (bitMask << loopCount++)); + if (0 != phyMode2 && + csr_get_phy_mode_in_use(mac, phyMode2, + phyModeInBssDesc, + !WLAN_REG_IS_24GHZ_CH_FREQ(bss_chan_freq), + &cfgDot11ModeToUse)) { + fMatch = true; + break; + } + } + } + + cfgDot11ModeToUse = csr_get_vdev_dot11_mode(mac, pProfile->csrPersona, + cfgDot11ModeToUse); + if (fMatch && pReturnCfgDot11Mode) { + /* + * IEEE 11n spec (8.4.3): HT STA shall + * eliminate TKIP as a choice for the pairwise + * cipher suite if CCMP is advertised by the AP + * or if the AP included an HT capabilities + * element in its Beacons and Probe Response. + */ + if ((!CSR_IS_11n_ALLOWED( + pProfile->negotiatedUCEncryptionType)) + && ((eCSR_CFG_DOT11_MODE_11N == + cfgDot11ModeToUse) || + (eCSR_CFG_DOT11_MODE_11AC == + cfgDot11ModeToUse) || + (eCSR_CFG_DOT11_MODE_11AX == + cfgDot11ModeToUse))) { + /* We cannot do 11n here */ + if (WLAN_REG_IS_24GHZ_CH_FREQ(bss_chan_freq)) { + cfgDot11ModeToUse = + eCSR_CFG_DOT11_MODE_11G; + } else { + cfgDot11ModeToUse = + eCSR_CFG_DOT11_MODE_11A; + } + } + *pReturnCfgDot11Mode = cfgDot11ModeToUse; + } + + return fMatch; +} + +enum csr_cfgdot11mode csr_find_best_phy_mode(struct mac_context *mac, + uint32_t phyMode) +{ + enum csr_cfgdot11mode cfgDot11ModeToUse; + enum band_info band = mac->mlme_cfg->gen.band; + + if ((0 == phyMode) || + (eCSR_DOT11_MODE_AUTO & phyMode) || + (eCSR_DOT11_MODE_11ax & phyMode)) { + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) { + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11AX; + } else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) { + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11AC; + } else { + /* Default to 11N mode if user has configured 11ac mode + * and FW doesn't supports 11ac mode . + */ + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11N; + } + } else if (eCSR_DOT11_MODE_11ac & phyMode) { + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) { + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11AC; + } else { + /* Default to 11N mode if user has configured 11ac mode + * and FW doesn't supports 11ac mode . + */ + } cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11N; + } else { + if ((eCSR_DOT11_MODE_11n | eCSR_DOT11_MODE_11n_ONLY) & phyMode) + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11N; + else if (eCSR_DOT11_MODE_abg & phyMode) { + if (BAND_2G != band) + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11A; + else + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11G; + } else if (eCSR_DOT11_MODE_11a & phyMode) + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11A; + else if ((eCSR_DOT11_MODE_11g | eCSR_DOT11_MODE_11g_ONLY) & + phyMode) + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11G; + else + cfgDot11ModeToUse = eCSR_CFG_DOT11_MODE_11B; + } + + return cfgDot11ModeToUse; +} + +uint32_t csr_get11h_power_constraint(struct mac_context *mac_ctx, + tDot11fIEPowerConstraints *constraints) +{ + uint32_t localPowerConstraint = 0; + + /* check if .11h support is enabled, if not, + * the power constraint is 0. + */ + if (mac_ctx->mlme_cfg->gen.enabled_11h && + constraints->present) + localPowerConstraint = constraints->localPowerConstraints; + + return localPowerConstraint; +} + +bool csr_is_profile_wpa(struct csr_roam_profile *pProfile) +{ + bool fWpaProfile = false; + + switch (pProfile->negotiatedAuthType) { + case eCSR_AUTH_TYPE_WPA: + case eCSR_AUTH_TYPE_WPA_PSK: + case eCSR_AUTH_TYPE_WPA_NONE: +#ifdef FEATURE_WLAN_ESE + case eCSR_AUTH_TYPE_CCKM_WPA: +#endif + fWpaProfile = true; + break; + + default: + fWpaProfile = false; + break; + } + + if (fWpaProfile) { + switch (pProfile->negotiatedUCEncryptionType) { + case eCSR_ENCRYPT_TYPE_WEP40: + case eCSR_ENCRYPT_TYPE_WEP104: + case eCSR_ENCRYPT_TYPE_TKIP: + case eCSR_ENCRYPT_TYPE_AES: + fWpaProfile = true; + break; + + default: + fWpaProfile = false; + break; + } + } + return fWpaProfile; +} + +bool csr_is_profile_rsn(struct csr_roam_profile *pProfile) +{ + bool fRSNProfile = false; + + switch (pProfile->negotiatedAuthType) { + case eCSR_AUTH_TYPE_RSN: + case eCSR_AUTH_TYPE_RSN_PSK: + case eCSR_AUTH_TYPE_FT_RSN: + case eCSR_AUTH_TYPE_FT_RSN_PSK: +#ifdef FEATURE_WLAN_ESE + case eCSR_AUTH_TYPE_CCKM_RSN: +#endif +#ifdef WLAN_FEATURE_11W + case eCSR_AUTH_TYPE_RSN_PSK_SHA256: + case eCSR_AUTH_TYPE_RSN_8021X_SHA256: +#endif + /* fallthrough */ + case eCSR_AUTH_TYPE_FILS_SHA256: + case eCSR_AUTH_TYPE_FILS_SHA384: + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + case eCSR_AUTH_TYPE_DPP_RSN: + case eCSR_AUTH_TYPE_OSEN: + fRSNProfile = true; + break; + + case eCSR_AUTH_TYPE_OWE: + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA256: + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA384: + case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384: + fRSNProfile = true; + break; + case eCSR_AUTH_TYPE_SAE: + case eCSR_AUTH_TYPE_FT_SAE: + fRSNProfile = true; + break; + + default: + fRSNProfile = false; + break; + } + + if (fRSNProfile) { + switch (pProfile->negotiatedUCEncryptionType) { + /* !!REVIEW - For WPA2, use of RSN IE mandates */ + /* use of AES as encryption. Here, we qualify */ + /* even if encryption type is WEP or TKIP */ + case eCSR_ENCRYPT_TYPE_WEP40: + case eCSR_ENCRYPT_TYPE_WEP104: + case eCSR_ENCRYPT_TYPE_TKIP: + case eCSR_ENCRYPT_TYPE_AES: + case eCSR_ENCRYPT_TYPE_AES_GCMP: + case eCSR_ENCRYPT_TYPE_AES_GCMP_256: + fRSNProfile = true; + break; + + default: + fRSNProfile = false; + break; + } + } + return fRSNProfile; +} + +/** + * csr_update_mcc_p2p_beacon_interval() - update p2p beacon interval + * @mac_ctx: pointer to mac context + * + * This function is to update the mcc p2p beacon interval + * + * Return: QDF_STATUS + */ +static QDF_STATUS csr_update_mcc_p2p_beacon_interval(struct mac_context *mac_ctx) +{ + uint32_t session_id = 0; + struct csr_roam_session *roam_session; + + /* If MCC is not supported just break and return SUCCESS */ + if (!mac_ctx->roam.configParam.fenableMCCMode) + return QDF_STATUS_E_FAILURE; + + for (session_id = 0; session_id < WLAN_MAX_VDEVS; session_id++) { + /* + * If GO in MCC support different beacon interval, + * change the BI of the P2P-GO + */ + roam_session = &mac_ctx->roam.roamSession[session_id]; + if (roam_session->bssParams.bssPersona != QDF_P2P_GO_MODE) + continue; + /* + * Handle different BI scneario based on the + * configuration set.If Config is set to 0x02 then + * Disconnect all the P2P clients associated. If config + * is set to 0x04 then update the BI without + * disconnecting all the clients + */ + if ((mac_ctx->roam.configParam.fAllowMCCGODiffBI == 0x04) + && (roam_session->bssParams. + updatebeaconInterval)) { + return csr_send_chng_mcc_beacon_interval(mac_ctx, + session_id); + } else if (roam_session->bssParams.updatebeaconInterval) { + /* + * If the configuration of fAllowMCCGODiffBI is set to + * other than 0x04 + */ + return csr_roam_call_callback(mac_ctx, + session_id, + NULL, 0, + eCSR_ROAM_DISCONNECT_ALL_P2P_CLIENTS, + eCSR_ROAM_RESULT_NONE); + } + } + return QDF_STATUS_E_FAILURE; +} + +static uint16_t csr_calculate_mcc_beacon_interval(struct mac_context *mac, + uint16_t sta_bi, + uint16_t go_gbi) +{ + uint8_t num_beacons = 0; + uint8_t is_multiple = 0; + uint16_t go_cbi = 0; + uint16_t go_fbi = 0; + uint16_t sta_cbi = 0; + + /* If GO's given beacon Interval is less than 100 */ + if (go_gbi < 100) + go_cbi = 100; + /* if GO's given beacon Interval is greater than or equal to 100 */ + else + go_cbi = 100 + (go_gbi % 100); + + if (sta_bi == 0) { + /* There is possibility to receive zero as value. + * Which will cause divide by zero. Hence initialise with 100 + */ + sta_bi = 100; + sme_warn("sta_bi 2nd parameter is zero, initialize to %d", + sta_bi); + } + /* check, if either one is multiple of another */ + if (sta_bi > go_cbi) + is_multiple = !(sta_bi % go_cbi); + else + is_multiple = !(go_cbi % sta_bi); + + /* if it is multiple, then accept GO's beacon interval + * range [100,199] as it is + */ + if (is_multiple) + return go_cbi; + + /* else , if it is not multiple, then then check for number of beacons + * to be inserted based on sta BI + */ + num_beacons = sta_bi / 100; + if (num_beacons) { + /* GO's final beacon interval will be aligned to sta beacon + * interval, but in the range of [100, 199]. + */ + sta_cbi = sta_bi / num_beacons; + go_fbi = sta_cbi; + } else + /* if STA beacon interval is less than 100, use GO's change + * bacon interval instead of updating to STA's beacon interval. + */ + go_fbi = go_cbi; + + return go_fbi; +} + +/** + * csr_validate_p2pcli_bcn_intrvl() - to validate p2pcli beacon interval + * @mac_ctx: pointer to mac context + * @chnl_id: channel id variable + * @bcn_interval: pointer to given beacon interval + * @session_id: given session id + * @status: fill the status in terms of QDF_STATUS to inform caller + * + * This API can provide the validation the beacon interval and re-calculate + * in case concurrency + * + * Return: bool + */ +static bool csr_validate_p2pcli_bcn_intrvl(struct mac_context *mac_ctx, + uint32_t ch_freq, uint16_t *bcn_interval, uint32_t session_id, + QDF_STATUS *status) +{ + struct csr_roam_session *roamsession; + + roamsession = &mac_ctx->roam.roamSession[session_id]; + if (roamsession->pCurRoamProfile && + (roamsession->pCurRoamProfile->csrPersona == + QDF_STA_MODE)) { + /* check for P2P client mode */ + sme_debug("Ignore Beacon Interval Validation..."); + } else if (roamsession->bssParams.bssPersona == QDF_P2P_GO_MODE) { + /* Check for P2P go scenario */ + if (roamsession->bssParams.operation_chan_freq != ch_freq && + roamsession->bssParams.beaconInterval != *bcn_interval) { + sme_err("BcnIntrvl is diff can't connect to P2P_GO network"); + *status = QDF_STATUS_E_FAILURE; + return true; + } + } + return false; +} + +/** + * csr_validate_p2pgo_bcn_intrvl() - to validate p2pgo beacon interval + * @mac_ctx: pointer to mac context + * @chnl_id: channel id variable + * @bcn_interval: pointer to given beacon interval + * @session_id: given session id + * @status: fill the status in terms of QDF_STATUS to inform caller + * + * This API can provide the validation the beacon interval and re-calculate + * in case concurrency + * + * Return: bool + */ +static bool csr_validate_p2pgo_bcn_intrvl(struct mac_context *mac_ctx, + uint32_t ch_freq, uint16_t *bcn_interval, + uint32_t session_id, QDF_STATUS *status) +{ + struct csr_roam_session *roamsession; + struct csr_config *cfg_param; + tCsrRoamConnectedProfile *conn_profile; + uint16_t new_bcn_interval; + + roamsession = &mac_ctx->roam.roamSession[session_id]; + cfg_param = &mac_ctx->roam.configParam; + conn_profile = &roamsession->connectedProfile; + if (roamsession->pCurRoamProfile && + ((roamsession->pCurRoamProfile->csrPersona == + QDF_P2P_CLIENT_MODE) || + (roamsession->pCurRoamProfile->csrPersona == + QDF_STA_MODE))) { + /* check for P2P_client scenario */ + if ((conn_profile->op_freq == 0) && + (conn_profile->beaconInterval == 0)) + return false; + + if (csr_is_conn_state_connected_infra(mac_ctx, session_id) && + conn_profile->op_freq != ch_freq && + conn_profile->beaconInterval != *bcn_interval) { + /* + * Updated beaconInterval should be used only when + * we are starting a new BSS not incase of + * client or STA case + */ + + /* Calculate beacon Interval for P2P-GO incase of MCC */ + if (cfg_param->conc_custom_rule1 || + cfg_param->conc_custom_rule2) { + new_bcn_interval = CSR_CUSTOM_CONC_GO_BI; + } else { + new_bcn_interval = + csr_calculate_mcc_beacon_interval( + mac_ctx, + conn_profile->beaconInterval, + *bcn_interval); + } + if (*bcn_interval != new_bcn_interval) + *bcn_interval = new_bcn_interval; + *status = QDF_STATUS_SUCCESS; + return true; + } + } + return false; +} + +/** + * csr_validate_sta_bcn_intrvl() - to validate sta beacon interval + * @mac_ctx: pointer to mac context + * @chnl_id: channel id variable + * @bcn_interval: pointer to given beacon interval + * @session_id: given session id + * @status: fill the status in terms of QDF_STATUS to inform caller + * + * This API can provide the validation the beacon interval and re-calculate + * in case concurrency + * + * Return: bool + */ +static bool csr_validate_sta_bcn_intrvl(struct mac_context *mac_ctx, + uint32_t ch_freq, uint16_t *bcn_interval, + uint32_t session_id, QDF_STATUS *status) +{ + struct csr_roam_session *roamsession; + struct csr_config *cfg_param; + uint16_t new_bcn_interval; + + roamsession = &mac_ctx->roam.roamSession[session_id]; + cfg_param = &mac_ctx->roam.configParam; + + if (roamsession->pCurRoamProfile && + (roamsession->pCurRoamProfile->csrPersona == + QDF_P2P_CLIENT_MODE)) { + /* check for P2P client mode */ + sme_debug("Bcn Intrvl validation not require for STA/CLIENT"); + return false; + } + if (roamsession->bssParams.bssPersona == QDF_SAP_MODE && + roamsession->bssParams.operation_chan_freq != ch_freq) { + /* + * IF SAP has started and STA wants to connect + * on different channel MCC should + * MCC should not be enabled so making it + * false to enforce on same channel + */ + sme_debug("*** MCC with SAP+STA sessions ****"); + *status = QDF_STATUS_SUCCESS; + return true; + } + /* + * Check for P2P go scenario + * if GO in MCC support different + * beacon interval, + * change the BI of the P2P-GO + */ + if (roamsession->bssParams.bssPersona == QDF_P2P_GO_MODE && + roamsession->bssParams.operation_chan_freq != ch_freq && + roamsession->bssParams.beaconInterval != *bcn_interval) { + /* if GO in MCC support diff beacon interval, return success */ + if (cfg_param->fAllowMCCGODiffBI == 0x01) { + *status = QDF_STATUS_SUCCESS; + return true; + } + /* + * Send only Broadcast disassoc and update bcn_interval + * If configuration is set to 0x04 then dont + * disconnect all the station + */ + if ((cfg_param->fAllowMCCGODiffBI == 0x02) + || (cfg_param->fAllowMCCGODiffBI == 0x04)) { + /* Check to pass the right beacon Interval */ + if (cfg_param->conc_custom_rule1 || + cfg_param->conc_custom_rule2) { + new_bcn_interval = CSR_CUSTOM_CONC_GO_BI; + } else { + new_bcn_interval = + csr_calculate_mcc_beacon_interval( + mac_ctx, *bcn_interval, + roamsession->bssParams.beaconInterval); + } + sme_debug("Peer AP BI : %d, new Beacon Interval: %d", + *bcn_interval, new_bcn_interval); + /* Update the becon Interval */ + if (new_bcn_interval != + roamsession->bssParams.beaconInterval) { + /* Update the bcn_interval now */ + sme_err("Beacon Interval got changed config used: %d", + cfg_param->fAllowMCCGODiffBI); + + roamsession->bssParams.beaconInterval = + new_bcn_interval; + roamsession->bssParams.updatebeaconInterval = + true; + *status = csr_update_mcc_p2p_beacon_interval( + mac_ctx); + return true; + } + *status = QDF_STATUS_SUCCESS; + return true; + } + if (cfg_param->fAllowMCCGODiffBI + == 0x03) { + /* Disconnect the P2P session */ + roamsession->bssParams.updatebeaconInterval = false; + *status = csr_roam_call_callback(mac_ctx, + session_id, NULL, 0, + eCSR_ROAM_SEND_P2P_STOP_BSS, + eCSR_ROAM_RESULT_NONE); + return true; + } + sme_err("BcnIntrvl is diff can't connect to preferred AP"); + *status = QDF_STATUS_E_FAILURE; + return true; + } + return false; +} + +QDF_STATUS csr_validate_mcc_beacon_interval(struct mac_context *mac_ctx, + uint32_t ch_freq, + uint16_t *bcn_interval, + uint32_t cur_session_id, + enum QDF_OPMODE cur_bss_persona) +{ + uint32_t session_id = 0; + QDF_STATUS status; + bool is_done; + + /* If MCC is not supported just break */ + if (!mac_ctx->roam.configParam.fenableMCCMode) + return QDF_STATUS_E_FAILURE; + + for (session_id = 0; session_id < WLAN_MAX_VDEVS; session_id++) { + if (cur_session_id == session_id) + continue; + + if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) + continue; + + switch (cur_bss_persona) { + case QDF_STA_MODE: + is_done = csr_validate_sta_bcn_intrvl( + mac_ctx, ch_freq, bcn_interval, + session_id, &status); + if (true == is_done) + return status; + break; + + case QDF_P2P_CLIENT_MODE: + is_done = csr_validate_p2pcli_bcn_intrvl(mac_ctx, + ch_freq, bcn_interval, session_id, + &status); + if (true == is_done) + return status; + break; + + case QDF_SAP_MODE: + case QDF_IBSS_MODE: + break; + + case QDF_P2P_GO_MODE: + is_done = csr_validate_p2pgo_bcn_intrvl(mac_ctx, + ch_freq, bcn_interval, + session_id, &status); + if (true == is_done) + return status; + break; + + default: + sme_err("Persona not supported: %d", cur_bss_persona); + return QDF_STATUS_E_FAILURE; + } + } + return QDF_STATUS_SUCCESS; +} + +/** + * csr_is_auth_type11r() - Check if Authentication type is 11R + * @mac: pointer to mac context + * @auth_type: The authentication type that is used to make the connection + * @mdie_present: Is MDIE IE present + * + * Return: true if is 11R auth type, false otherwise + */ +bool csr_is_auth_type11r(struct mac_context *mac, enum csr_akm_type auth_type, + uint8_t mdie_present) +{ + switch (auth_type) { + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + if (mdie_present && + mac->mlme_cfg->lfr.enable_ftopen) + return true; + break; + case eCSR_AUTH_TYPE_FT_RSN_PSK: + case eCSR_AUTH_TYPE_FT_RSN: + case eCSR_AUTH_TYPE_FT_SAE: + case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384: + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return true; + default: + break; + } + return false; +} + +/* Function to return true if the profile is 11r */ +bool csr_is_profile11r(struct mac_context *mac, + struct csr_roam_profile *pProfile) +{ + return csr_is_auth_type11r(mac, pProfile->negotiatedAuthType, + pProfile->mdid.mdie_present); +} + +bool csr_is_auth_type_ese(enum csr_akm_type AuthType) +{ + switch (AuthType) { + case eCSR_AUTH_TYPE_CCKM_WPA: + case eCSR_AUTH_TYPE_CCKM_RSN: + return true; + default: + break; + } + return false; +} + +#ifdef FEATURE_WLAN_ESE + +/* Function to return true if the profile is ESE */ +bool csr_is_profile_ese(struct csr_roam_profile *pProfile) +{ + return csr_is_auth_type_ese(pProfile->negotiatedAuthType); +} + +#endif + +#ifdef FEATURE_WLAN_WAPI +bool csr_is_profile_wapi(struct csr_roam_profile *pProfile) +{ + bool fWapiProfile = false; + + switch (pProfile->negotiatedAuthType) { + case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE: + case eCSR_AUTH_TYPE_WAPI_WAI_PSK: + fWapiProfile = true; + break; + + default: + fWapiProfile = false; + break; + } + + if (fWapiProfile) { + switch (pProfile->negotiatedUCEncryptionType) { + case eCSR_ENCRYPT_TYPE_WPI: + fWapiProfile = true; + break; + + default: + fWapiProfile = false; + break; + } + } + return fWapiProfile; +} +#endif /* FEATURE_WLAN_WAPI */ + +#ifdef WLAN_FEATURE_11W +static bool csr_is_wpa_oui_equal(struct mac_context *mac, uint8_t *Oui1, + uint8_t *Oui2) +{ + return !qdf_mem_cmp(Oui1, Oui2, CSR_WPA_OUI_SIZE); +} + +static bool csr_is_oui_match(struct mac_context *mac, + uint8_t AllCyphers[][CSR_WPA_OUI_SIZE], + uint8_t cAllCyphers, uint8_t Cypher[], uint8_t Oui[]) +{ + bool fYes = false; + uint8_t idx; + + for (idx = 0; idx < cAllCyphers; idx++) { + if (csr_is_wpa_oui_equal(mac, AllCyphers[idx], Cypher)) { + fYes = true; + break; + } + } + + if (fYes && Oui) + qdf_mem_copy(Oui, AllCyphers[idx], CSR_WPA_OUI_SIZE); + + return fYes; +} + +/* + * csr_is_group_mgmt_gmac_128() - check whether oui is GMAC_128 + * @mac: Global MAC context + * @all_suites: pointer to all supported akm suites + * @suite_count: all supported akm suites count + * @oui: Oui needs to be matched + * + * Return: True if OUI is GMAC_128, false otherwise + */ +static bool csr_is_group_mgmt_gmac_128(struct mac_context *mac, + uint8_t AllSuites[][CSR_RSN_OUI_SIZE], + uint8_t cAllSuites, uint8_t Oui[]) +{ + return csr_is_oui_match(mac, AllSuites, cAllSuites, + csr_group_mgmt_oui[ENUM_GMAC_128], Oui); +} + +/* + * csr_is_group_mgmt_gmac_256() - check whether oui is GMAC_256 + * @mac: Global MAC context + * @all_suites: pointer to all supported akm suites + * @suite_count: all supported akm suites count + * @oui: Oui needs to be matched + * + * Return: True if OUI is GMAC_256, false otherwise + */ +static bool csr_is_group_mgmt_gmac_256(struct mac_context *mac, + uint8_t AllSuites[][CSR_RSN_OUI_SIZE], + uint8_t cAllSuites, uint8_t Oui[]) +{ + return csr_is_oui_match(mac, AllSuites, cAllSuites, + csr_group_mgmt_oui[ENUM_GMAC_256], Oui); +} +#endif + +bool csr_is_pmkid_found_for_peer(struct mac_context *mac, + struct csr_roam_session *session, + tSirMacAddr peer_mac_addr, + uint8_t *pmkid, + uint16_t pmkid_count) +{ + uint32_t i; + uint8_t *session_pmkid; + tPmkidCacheInfo *pmkid_cache; + + pmkid_cache = qdf_mem_malloc(sizeof(*pmkid_cache)); + if (!pmkid_cache) + return false; + + qdf_mem_copy(pmkid_cache->BSSID.bytes, peer_mac_addr, + QDF_MAC_ADDR_SIZE); + + if (!csr_lookup_pmkid_using_bssid(mac, session, pmkid_cache)) { + qdf_mem_free(pmkid_cache); + return false; + } + + session_pmkid = pmkid_cache->PMKID; + for (i = 0; i < pmkid_count; i++) { + if (!qdf_mem_cmp(pmkid + (i * PMKID_LEN), + session_pmkid, PMKID_LEN)) { + qdf_mem_free(pmkid_cache); + return true; + } + } + + sme_debug("PMKID in PmkidCacheInfo doesn't match with PMKIDs of peer"); + qdf_mem_free(pmkid_cache); + + return false; +} + +bool csr_lookup_pmkid_using_bssid(struct mac_context *mac, + struct csr_roam_session *session, + tPmkidCacheInfo *pmk_cache) +{ + struct wlan_crypto_pmksa *pmksa; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, session->vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Invalid vdev"); + return false; + } + + pmksa = wlan_crypto_get_pmksa(vdev, &pmk_cache->BSSID); + if (!pmksa) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return false; + } + qdf_mem_copy(pmk_cache->PMKID, pmksa->pmkid, sizeof(pmk_cache->PMKID)); + qdf_mem_copy(pmk_cache->pmk, pmksa->pmk, pmksa->pmk_len); + pmk_cache->pmk_len = pmksa->pmk_len; + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return true; +} + +bool csr_lookup_fils_pmkid(struct mac_context *mac, + uint8_t vdev_id, uint8_t *cache_id, + uint8_t *ssid, uint8_t ssid_len, + struct qdf_mac_addr *bssid) +{ + struct wlan_crypto_pmksa *fils_ssid_pmksa, *bssid_lookup_pmksa; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, vdev_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Invalid vdev"); + return false; + } + + bssid_lookup_pmksa = wlan_crypto_get_pmksa(vdev, bssid); + fils_ssid_pmksa = + wlan_crypto_get_fils_pmksa(vdev, cache_id, ssid, ssid_len); + if (!fils_ssid_pmksa && !bssid_lookup_pmksa) { + sme_err("FILS_PMKSA: Lookup failed"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return false; + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return true; +} + +/** + * csr_update_session_pmk() - Update the pmk len and pmk in the roam session + * @session: pointer to the CSR Roam session + * @pmkid_cache: pointer to the pmkid cache + * + * Return: None + */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static void csr_update_session_pmk(struct csr_roam_session *session, + struct wlan_crypto_pmksa *pmksa) +{ + session->pmk_len = pmksa->pmk_len; + qdf_mem_zero(session->psk_pmk, sizeof(session->psk_pmk)); + qdf_mem_copy(session->psk_pmk, pmksa->pmk, session->pmk_len); +} +#else +static inline void csr_update_session_pmk(struct csr_roam_session *session, + struct wlan_crypto_pmksa *pmksa) +{ +} +#endif + +#ifdef WLAN_FEATURE_FILS_SK +/** + * csr_update_pmksa_to_profile() - update pmk and pmkid to profile which will be + * used in case of fils session + * @profile: profile + * @pmkid_cache: pmksa cache + * + * Return: None + */ +static inline void csr_update_pmksa_to_profile(struct csr_roam_profile *profile, + struct wlan_crypto_pmksa *pmksa) +{ + if (!profile->fils_con_info) + return; + + profile->fils_con_info->pmk_len = pmksa->pmk_len; + qdf_mem_copy(profile->fils_con_info->pmk, + pmksa->pmk, pmksa->pmk_len); + qdf_mem_copy(profile->fils_con_info->pmkid, + pmksa->pmkid, PMKID_LEN); +} +#else +static inline void csr_update_pmksa_to_profile(struct csr_roam_profile *profile, + struct wlan_crypto_pmksa *pmksa) +{ +} +#endif + +uint8_t csr_construct_rsn_ie(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *ap_ie, tCsrRSNIe *pRSNIe) +{ + struct wlan_objmgr_vdev *vdev; + uint8_t *rsn_ie_end = NULL; + uint8_t *rsn_ie = (uint8_t *)pRSNIe; + uint8_t ie_len = 0; + tDot11fBeaconIEs *local_ap_ie = ap_ie; + uint16_t rsn_cap = 0, self_rsn_cap; + struct wlan_crypto_pmksa pmksa, *pmksa_peer; + struct csr_roam_session *session = &mac->roam.roamSession[sessionId]; + + if (!local_ap_ie && + (!QDF_IS_STATUS_SUCCESS(csr_get_parsed_bss_description_ies + (mac, pSirBssDesc, &local_ap_ie)))) + return ie_len; + + /* get AP RSN cap */ + qdf_mem_copy(&rsn_cap, local_ap_ie->RSN.RSN_Cap, sizeof(rsn_cap)); + if (!ap_ie && local_ap_ie) + /* locally allocated */ + qdf_mem_free(local_ap_ie); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, sessionId, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Invalid vdev"); + return ie_len; + } + + self_rsn_cap = (uint16_t)wlan_crypto_get_param(vdev, + WLAN_CRYPTO_PARAM_RSN_CAP); + /* If AP is capable then use self capability else set PMF as 0 */ + if (rsn_cap & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED && + pProfile->MFPCapable) { + self_rsn_cap |= WLAN_CRYPTO_RSN_CAP_MFP_ENABLED; + if (pProfile->MFPRequired) + self_rsn_cap |= WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED; + } else { + self_rsn_cap &= ~WLAN_CRYPTO_RSN_CAP_MFP_ENABLED; + self_rsn_cap &= ~WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED; + } + wlan_crypto_set_vdev_param(vdev, WLAN_CRYPTO_PARAM_RSN_CAP, + self_rsn_cap); + qdf_mem_zero(&pmksa, sizeof(pmksa)); + if (pSirBssDesc->fils_info_element.is_cache_id_present) { + pmksa.ssid_len = + pProfile->SSIDs.SSIDList[0].SSID.length; + qdf_mem_copy(pmksa.ssid, + pProfile->SSIDs.SSIDList[0].SSID.ssId, + pProfile->SSIDs.SSIDList[0].SSID.length); + qdf_mem_copy(pmksa.cache_id, + pSirBssDesc->fils_info_element.cache_id, + CACHE_ID_LEN); + qdf_mem_copy(&pmksa.bssid, + pSirBssDesc->bssId, QDF_MAC_ADDR_SIZE); + } else { + qdf_mem_copy(&pmksa.bssid, + pSirBssDesc->bssId, QDF_MAC_ADDR_SIZE); + } + pmksa_peer = wlan_crypto_get_peer_pmksa(vdev, &pmksa); + qdf_mem_zero(&pmksa, sizeof(pmksa)); + /* + * TODO: Add support for Adaptive 11r connection after + * call to csr_get_rsn_information is added here + */ + rsn_ie_end = wlan_crypto_build_rsnie_with_pmksa(vdev, rsn_ie, + pmksa_peer); + if (rsn_ie_end) + ie_len = rsn_ie_end - rsn_ie; + + /* + * If a PMK cache is found for the BSSID, then + * update the PMK in CSR session also as this + * will be sent to the FW during RSO. + */ + if (pmksa_peer) { + csr_update_session_pmk(session, pmksa_peer); + csr_update_pmksa_to_profile(pProfile, pmksa_peer); + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return ie_len; +} + +uint8_t csr_construct_wpa_ie(struct mac_context *mac, uint8_t session_id, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrWpaIe *pWpaIe) +{ + struct wlan_objmgr_vdev *vdev; + uint8_t *wpa_ie_end = NULL; + uint8_t *wpa_ie = (uint8_t *)pWpaIe; + uint8_t ie_len = 0; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, session_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Invalid vdev"); + return ie_len; + } + wpa_ie_end = wlan_crypto_build_wpaie(vdev, wpa_ie); + if (wpa_ie_end) + ie_len = wpa_ie_end - wpa_ie; + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return ie_len; +} + +#ifdef FEATURE_WLAN_WAPI +uint8_t csr_construct_wapi_ie(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrWapiIe *pWapiIe) +{ + struct wlan_objmgr_vdev *vdev; + uint8_t *wapi_ie_end = NULL; + uint8_t *wapi_ie = (uint8_t *)pWapiIe; + uint8_t ie_len = 0; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac->psoc, sessionId, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("Invalid vdev"); + return ie_len; + } + wapi_ie_end = wlan_crypto_build_wapiie(vdev, wapi_ie); + if (wapi_ie_end) + ie_len = wapi_ie_end - wapi_ie; + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + + return ie_len; +} +#endif + +/* If a WPAIE exists in the profile, just use it. Or else construct + * one from the BSS Caller allocated memory for pWpaIe and guarrantee + * it can contain a max length WPA IE + */ +uint8_t csr_retrieve_wpa_ie(struct mac_context *mac, uint8_t session_id, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrWpaIe *pWpaIe) +{ + uint8_t cbWpaIe = 0; + + do { + if (!csr_is_profile_wpa(pProfile)) + break; + if (pProfile->nWPAReqIELength && pProfile->pWPAReqIE) { + if (pProfile->nWPAReqIELength <= + DOT11F_IE_RSN_MAX_LEN) { + cbWpaIe = (uint8_t) pProfile->nWPAReqIELength; + qdf_mem_copy(pWpaIe, pProfile->pWPAReqIE, + cbWpaIe); + } else { + sme_warn("Invalid WPA IE length %d", + pProfile->nWPAReqIELength); + } + break; + } + cbWpaIe = csr_construct_wpa_ie(mac, session_id, pProfile, + pSirBssDesc, pIes, pWpaIe); + } while (0); + + return cbWpaIe; +} + +#ifdef WLAN_FEATURE_11W +/** + * csr_get_mc_mgmt_cipher(): Get mcast management cipher from profile rsn + * @mac: mac ctx + * @profile: connect profile + * @bss: ap scan entry + * @ap_ie: AP IE's + * + * Return: none + */ +static void csr_get_mc_mgmt_cipher(struct mac_context *mac, + struct csr_roam_profile *profile, + struct bss_description *bss, + tDot11fBeaconIEs *ap_ie) +{ + int ret; + tDot11fIERSN rsn_ie = {0}; + uint8_t n_mgmt_cipher = 1; + struct rsn_caps rsn_caps; + tDot11fBeaconIEs *local_ap_ie = ap_ie; + uint8_t grp_mgmt_arr[CSR_RSN_MAX_MULTICAST_CYPHERS][CSR_RSN_OUI_SIZE]; + + if (!profile->MFPEnabled) + return; + + if (!local_ap_ie && + (!QDF_IS_STATUS_SUCCESS(csr_get_parsed_bss_description_ies + (mac, bss, &local_ap_ie)))) + return; + + qdf_mem_copy(&rsn_caps, local_ap_ie->RSN.RSN_Cap, sizeof(rsn_caps)); + + if (!ap_ie && local_ap_ie) + /* locally allocated */ + qdf_mem_free(local_ap_ie); + + /* if AP is not PMF capable return */ + if (!rsn_caps.MFPCapable) + return; + + ret = dot11f_unpack_ie_rsn(mac, profile->pRSNReqIE + 2, + profile->nRSNReqIELength -2, + &rsn_ie, false); + if (DOT11F_FAILED(ret)) + return; + + qdf_mem_copy(&rsn_caps, rsn_ie.RSN_Cap, sizeof(rsn_caps)); + + /* if self cap is not PMF capable return */ + if (!rsn_caps.MFPCapable) + return; + + qdf_mem_copy(grp_mgmt_arr, rsn_ie.gp_mgmt_cipher_suite, + CSR_RSN_OUI_SIZE); + if (csr_is_group_mgmt_gmac_128(mac, grp_mgmt_arr, n_mgmt_cipher, NULL)) + profile->mgmt_encryption_type = eSIR_ED_AES_GMAC_128; + else if (csr_is_group_mgmt_gmac_256(mac, grp_mgmt_arr, + n_mgmt_cipher, NULL)) + profile->mgmt_encryption_type = eSIR_ED_AES_GMAC_256; + else + /* Default is CMAC */ + profile->mgmt_encryption_type = eSIR_ED_AES_128_CMAC; +} +#else +static inline +void csr_get_mc_mgmt_cipher(struct mac_context *mac, + struct csr_roam_profile *profile, + struct bss_description *bss, + tDot11fBeaconIEs *ap_ie) +{ +} +#endif +/* If a RSNIE exists in the profile, just use it. Or else construct + * one from the BSS Caller allocated memory for pWpaIe and guarrantee + * it can contain a max length WPA IE + */ +uint8_t csr_retrieve_rsn_ie(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrRSNIe *pRsnIe) +{ + uint8_t cbRsnIe = 0; + + do { + if (!csr_is_profile_rsn(pProfile)) + break; + /* copy RSNIE from user as it is if test mode is enabled */ + if (pProfile->force_rsne_override && + pProfile->nRSNReqIELength && pProfile->pRSNReqIE) { + sme_debug("force_rsne_override, copy RSN IE provided by user"); + if (pProfile->nRSNReqIELength <= + DOT11F_IE_RSN_MAX_LEN) { + cbRsnIe = (uint8_t) pProfile->nRSNReqIELength; + qdf_mem_copy(pRsnIe, pProfile->pRSNReqIE, + cbRsnIe); + csr_get_mc_mgmt_cipher(mac, pProfile, + pSirBssDesc, pIes); + } else { + sme_warn("Invalid RSN IE length: %d", + pProfile->nRSNReqIELength); + } + break; + } + cbRsnIe = csr_construct_rsn_ie(mac, sessionId, pProfile, + pSirBssDesc, pIes, pRsnIe); + } while (0); + + return cbRsnIe; +} + +#ifdef FEATURE_WLAN_WAPI +/* If a WAPI IE exists in the profile, just use it. Or else construct + * one from the BSS Caller allocated memory for pWapiIe and guarrantee + * it can contain a max length WAPI IE + */ +uint8_t csr_retrieve_wapi_ie(struct mac_context *mac, uint32_t sessionId, + struct csr_roam_profile *pProfile, + struct bss_description *pSirBssDesc, + tDot11fBeaconIEs *pIes, tCsrWapiIe *pWapiIe) +{ + uint8_t cbWapiIe = 0; + + do { + if (!csr_is_profile_wapi(pProfile)) + break; + if (pProfile->nWAPIReqIELength && pProfile->pWAPIReqIE) { + if (DOT11F_IE_WAPI_MAX_LEN >= + pProfile->nWAPIReqIELength) { + cbWapiIe = (uint8_t) pProfile->nWAPIReqIELength; + qdf_mem_copy(pWapiIe, pProfile->pWAPIReqIE, + cbWapiIe); + } else { + sme_warn("Invalid WAPI IE length %d", + pProfile->nWAPIReqIELength); + } + break; + } + cbWapiIe = csr_construct_wapi_ie(mac, sessionId, + pProfile, pSirBssDesc, + pIes, pWapiIe); + } while (0); + + return cbWapiIe; +} +#endif /* FEATURE_WLAN_WAPI */ + +bool csr_rates_is_dot11_rate11b_supported_rate(uint8_t dot11Rate) +{ + bool fSupported = false; + uint16_t nonBasicRate = + (uint16_t) (BITS_OFF(dot11Rate, CSR_DOT11_BASIC_RATE_MASK)); + + switch (nonBasicRate) { + case eCsrSuppRate_1Mbps: + case eCsrSuppRate_2Mbps: + case eCsrSuppRate_5_5Mbps: + case eCsrSuppRate_11Mbps: + fSupported = true; + break; + + default: + break; + } + + return fSupported; +} + +bool csr_rates_is_dot11_rate11a_supported_rate(uint8_t dot11Rate) +{ + bool fSupported = false; + uint16_t nonBasicRate = + (uint16_t) (BITS_OFF(dot11Rate, CSR_DOT11_BASIC_RATE_MASK)); + + switch (nonBasicRate) { + case eCsrSuppRate_6Mbps: + case eCsrSuppRate_9Mbps: + case eCsrSuppRate_12Mbps: + case eCsrSuppRate_18Mbps: + case eCsrSuppRate_24Mbps: + case eCsrSuppRate_36Mbps: + case eCsrSuppRate_48Mbps: + case eCsrSuppRate_54Mbps: + fSupported = true; + break; + + default: + break; + } + + return fSupported; +} + +tAniEdType csr_translate_encrypt_type_to_ed_type(eCsrEncryptionType EncryptType) +{ + tAniEdType edType; + + switch (EncryptType) { + default: + case eCSR_ENCRYPT_TYPE_NONE: + edType = eSIR_ED_NONE; + break; + + case eCSR_ENCRYPT_TYPE_WEP40_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP40: + edType = eSIR_ED_WEP40; + break; + + case eCSR_ENCRYPT_TYPE_WEP104_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP104: + edType = eSIR_ED_WEP104; + break; + + case eCSR_ENCRYPT_TYPE_TKIP: + edType = eSIR_ED_TKIP; + break; + + case eCSR_ENCRYPT_TYPE_AES: + edType = eSIR_ED_CCMP; + break; +#ifdef FEATURE_WLAN_WAPI + case eCSR_ENCRYPT_TYPE_WPI: + edType = eSIR_ED_WPI; + break; +#endif +#ifdef WLAN_FEATURE_11W + /* 11w BIP */ + case eCSR_ENCRYPT_TYPE_AES_CMAC: + edType = eSIR_ED_AES_128_CMAC; + break; + case eCSR_ENCRYPT_TYPE_AES_GCMP: + edType = eSIR_ED_GCMP; + break; + case eCSR_ENCRYPT_TYPE_AES_GCMP_256: + edType = eSIR_ED_GCMP_256; + break; + case eCSR_ENCRYPT_TYPE_AES_GMAC_128: + edType = eSIR_ED_AES_GMAC_128; + break; + case eCSR_ENCRYPT_TYPE_AES_GMAC_256: + edType = eSIR_ED_AES_GMAC_256; + break; +#endif + } + + return edType; +} + +bool csr_is_ssid_match(struct mac_context *mac, uint8_t *ssid1, uint8_t ssid1Len, + uint8_t *bssSsid, uint8_t bssSsidLen, bool fSsidRequired) +{ + bool fMatch = false; + + do { + /* + * Check for the specification of the Broadcast SSID at the + * beginning of the list. If specified, then all SSIDs are + * matches (broadcast SSID means accept all SSIDs). + */ + if (ssid1Len == 0) { + fMatch = true; + break; + } + + /* There are a few special cases. If the Bss description has + * a Broadcast SSID, then our Profile must have a single SSID + * without Wildcards so we can program the SSID. + * + * SSID could be suppressed in beacons. In that case SSID IE + * has valid length but the SSID value is all NULL characters. + * That condition is trated same as NULL SSID + */ + if (csr_is_nullssid(bssSsid, bssSsidLen)) { + if (false == fSsidRequired) { + fMatch = true; + break; + } + } + + if (ssid1Len != bssSsidLen) + break; + if (!qdf_mem_cmp(bssSsid, ssid1, bssSsidLen)) { + fMatch = true; + break; + } + + } while (0); + + return fMatch; +} + +/* Null ssid means match */ +bool csr_is_ssid_in_list(tSirMacSSid *pSsid, tCsrSSIDs *pSsidList) +{ + bool fMatch = false; + uint32_t i; + + if (pSsidList && pSsid) { + for (i = 0; i < pSsidList->numOfSSIDs; i++) { + if (csr_is_nullssid + (pSsidList->SSIDList[i].SSID.ssId, + pSsidList->SSIDList[i].SSID.length) + || + ((pSsidList->SSIDList[i].SSID.length == + pSsid->length) + && (!qdf_mem_cmp(pSsid->ssId, + pSsidList->SSIDList[i].SSID. + ssId, pSsid->length)))) { + fMatch = true; + break; + } + } + } + + return fMatch; +} + +bool csr_is_bssid_match(struct qdf_mac_addr *pProfBssid, + struct qdf_mac_addr *BssBssid) +{ + bool fMatch = false; + struct qdf_mac_addr ProfileBssid; + + /* for efficiency of the MAC_ADDRESS functions, move the */ + /* Bssid's into MAC_ADDRESS structs. */ + qdf_mem_copy(&ProfileBssid, pProfBssid, sizeof(struct qdf_mac_addr)); + + do { + /* Give the profile the benefit of the doubt... accept + * either all 0 or the real broadcast Bssid (all 0xff) + * as broadcast Bssids (meaning to match any Bssids). + */ + if (qdf_is_macaddr_zero(&ProfileBssid) || + qdf_is_macaddr_broadcast(&ProfileBssid)) { + fMatch = true; + break; + } + + if (qdf_is_macaddr_equal(BssBssid, &ProfileBssid)) { + fMatch = true; + break; + } + + } while (0); + + return fMatch; +} + +/** + * csr_is_aggregate_rate_supported() - to check if aggregate rate is supported + * @mac_ctx: pointer to mac context + * @rate: A rate in units of 500kbps + * + * + * The rate encoding is just as in 802.11 Information Elements, except + * that the high bit is \em not interpreted as indicating a Basic Rate, + * and proprietary rates are allowed, too. + * + * Note that if the adapter's dot11Mode is g, we don't restrict the + * rates. According to hwReadEepromParameters, this will happen when: + * ... the card is configured for ALL bands through the property + * page. If this occurs, and the card is not an ABG card ,then this + * code is setting the dot11Mode to assume the mode that the + * hardware can support. For example, if the card is an 11BG card + * and we are configured to support ALL bands, then we change the + * dot11Mode to 11g because ALL in this case is only what the + * hardware can support. + * + * Return: true if the adapter is currently capable of supporting this rate + */ + +static bool csr_is_aggregate_rate_supported(struct mac_context *mac_ctx, + uint16_t rate) +{ + bool supported = false; + uint16_t idx, new_rate; + + /* In case basic rate flag is set */ + new_rate = BITS_OFF(rate, CSR_DOT11_BASIC_RATE_MASK); + if (eCSR_CFG_DOT11_MODE_11A == + mac_ctx->roam.configParam.uCfgDot11Mode) { + switch (new_rate) { + case eCsrSuppRate_6Mbps: + case eCsrSuppRate_9Mbps: + case eCsrSuppRate_12Mbps: + case eCsrSuppRate_18Mbps: + case eCsrSuppRate_24Mbps: + case eCsrSuppRate_36Mbps: + case eCsrSuppRate_48Mbps: + case eCsrSuppRate_54Mbps: + supported = true; + break; + default: + supported = false; + break; + } + + } else if (eCSR_CFG_DOT11_MODE_11B == + mac_ctx->roam.configParam.uCfgDot11Mode) { + switch (new_rate) { + case eCsrSuppRate_1Mbps: + case eCsrSuppRate_2Mbps: + case eCsrSuppRate_5_5Mbps: + case eCsrSuppRate_11Mbps: + supported = true; + break; + default: + supported = false; + break; + } + } else if (!mac_ctx->roam.configParam.ProprietaryRatesEnabled) { + + switch (new_rate) { + case eCsrSuppRate_1Mbps: + case eCsrSuppRate_2Mbps: + case eCsrSuppRate_5_5Mbps: + case eCsrSuppRate_6Mbps: + case eCsrSuppRate_9Mbps: + case eCsrSuppRate_11Mbps: + case eCsrSuppRate_12Mbps: + case eCsrSuppRate_18Mbps: + case eCsrSuppRate_24Mbps: + case eCsrSuppRate_36Mbps: + case eCsrSuppRate_48Mbps: + case eCsrSuppRate_54Mbps: + supported = true; + break; + default: + supported = false; + break; + } + } else if (eCsrSuppRate_1Mbps == new_rate || + eCsrSuppRate_2Mbps == new_rate || + eCsrSuppRate_5_5Mbps == new_rate || + eCsrSuppRate_11Mbps == new_rate) + supported = true; + else { + idx = 0x1; + + switch (new_rate) { + case eCsrSuppRate_6Mbps: + supported = g_phy_rates_suppt[0][idx]; + break; + case eCsrSuppRate_9Mbps: + supported = g_phy_rates_suppt[1][idx]; + break; + case eCsrSuppRate_12Mbps: + supported = g_phy_rates_suppt[2][idx]; + break; + case eCsrSuppRate_18Mbps: + supported = g_phy_rates_suppt[3][idx]; + break; + case eCsrSuppRate_20Mbps: + supported = g_phy_rates_suppt[4][idx]; + break; + case eCsrSuppRate_24Mbps: + supported = g_phy_rates_suppt[5][idx]; + break; + case eCsrSuppRate_36Mbps: + supported = g_phy_rates_suppt[6][idx]; + break; + case eCsrSuppRate_40Mbps: + supported = g_phy_rates_suppt[7][idx]; + break; + case eCsrSuppRate_42Mbps: + supported = g_phy_rates_suppt[8][idx]; + break; + case eCsrSuppRate_48Mbps: + supported = g_phy_rates_suppt[9][idx]; + break; + case eCsrSuppRate_54Mbps: + supported = g_phy_rates_suppt[10][idx]; + break; + case eCsrSuppRate_72Mbps: + supported = g_phy_rates_suppt[11][idx]; + break; + case eCsrSuppRate_80Mbps: + supported = g_phy_rates_suppt[12][idx]; + break; + case eCsrSuppRate_84Mbps: + supported = g_phy_rates_suppt[13][idx]; + break; + case eCsrSuppRate_96Mbps: + supported = g_phy_rates_suppt[14][idx]; + break; + case eCsrSuppRate_108Mbps: + supported = g_phy_rates_suppt[15][idx]; + break; + case eCsrSuppRate_120Mbps: + supported = g_phy_rates_suppt[16][idx]; + break; + case eCsrSuppRate_126Mbps: + supported = g_phy_rates_suppt[17][idx]; + break; + case eCsrSuppRate_144Mbps: + supported = g_phy_rates_suppt[18][idx]; + break; + case eCsrSuppRate_160Mbps: + supported = g_phy_rates_suppt[19][idx]; + break; + case eCsrSuppRate_168Mbps: + supported = g_phy_rates_suppt[20][idx]; + break; + case eCsrSuppRate_192Mbps: + supported = g_phy_rates_suppt[21][idx]; + break; + case eCsrSuppRate_216Mbps: + supported = g_phy_rates_suppt[22][idx]; + break; + case eCsrSuppRate_240Mbps: + supported = g_phy_rates_suppt[23][idx]; + break; + default: + supported = false; + break; + } + } + return supported; +} + +void csr_add_rate_bitmap(uint8_t rate, uint16_t *pRateBitmap) +{ + uint16_t rateBitmap; + uint16_t n = BITS_OFF(rate, CSR_DOT11_BASIC_RATE_MASK); + + rateBitmap = *pRateBitmap; + switch (n) { + case SIR_MAC_RATE_1: + rateBitmap |= SIR_MAC_RATE_1_BITMAP; + break; + case SIR_MAC_RATE_2: + rateBitmap |= SIR_MAC_RATE_2_BITMAP; + break; + case SIR_MAC_RATE_5_5: + rateBitmap |= SIR_MAC_RATE_5_5_BITMAP; + break; + case SIR_MAC_RATE_11: + rateBitmap |= SIR_MAC_RATE_11_BITMAP; + break; + case SIR_MAC_RATE_6: + rateBitmap |= SIR_MAC_RATE_6_BITMAP; + break; + case SIR_MAC_RATE_9: + rateBitmap |= SIR_MAC_RATE_9_BITMAP; + break; + case SIR_MAC_RATE_12: + rateBitmap |= SIR_MAC_RATE_12_BITMAP; + break; + case SIR_MAC_RATE_18: + rateBitmap |= SIR_MAC_RATE_18_BITMAP; + break; + case SIR_MAC_RATE_24: + rateBitmap |= SIR_MAC_RATE_24_BITMAP; + break; + case SIR_MAC_RATE_36: + rateBitmap |= SIR_MAC_RATE_36_BITMAP; + break; + case SIR_MAC_RATE_48: + rateBitmap |= SIR_MAC_RATE_48_BITMAP; + break; + case SIR_MAC_RATE_54: + rateBitmap |= SIR_MAC_RATE_54_BITMAP; + break; + } + *pRateBitmap = rateBitmap; +} + +bool csr_check_rate_bitmap(uint8_t rate, uint16_t rateBitmap) +{ + uint16_t n = BITS_OFF(rate, CSR_DOT11_BASIC_RATE_MASK); + + switch (n) { + case SIR_MAC_RATE_1: + rateBitmap &= SIR_MAC_RATE_1_BITMAP; + break; + case SIR_MAC_RATE_2: + rateBitmap &= SIR_MAC_RATE_2_BITMAP; + break; + case SIR_MAC_RATE_5_5: + rateBitmap &= SIR_MAC_RATE_5_5_BITMAP; + break; + case SIR_MAC_RATE_11: + rateBitmap &= SIR_MAC_RATE_11_BITMAP; + break; + case SIR_MAC_RATE_6: + rateBitmap &= SIR_MAC_RATE_6_BITMAP; + break; + case SIR_MAC_RATE_9: + rateBitmap &= SIR_MAC_RATE_9_BITMAP; + break; + case SIR_MAC_RATE_12: + rateBitmap &= SIR_MAC_RATE_12_BITMAP; + break; + case SIR_MAC_RATE_18: + rateBitmap &= SIR_MAC_RATE_18_BITMAP; + break; + case SIR_MAC_RATE_24: + rateBitmap &= SIR_MAC_RATE_24_BITMAP; + break; + case SIR_MAC_RATE_36: + rateBitmap &= SIR_MAC_RATE_36_BITMAP; + break; + case SIR_MAC_RATE_48: + rateBitmap &= SIR_MAC_RATE_48_BITMAP; + break; + case SIR_MAC_RATE_54: + rateBitmap &= SIR_MAC_RATE_54_BITMAP; + break; + } + return !!rateBitmap; +} + +bool csr_rates_is_dot11_rate_supported(struct mac_context *mac_ctx, uint8_t rate) +{ + uint16_t n = BITS_OFF(rate, CSR_DOT11_BASIC_RATE_MASK); + + return csr_is_aggregate_rate_supported(mac_ctx, n); +} + +#ifdef WLAN_FEATURE_FILS_SK +static inline void csr_free_fils_profile_info(struct csr_roam_profile *profile) +{ + if (profile->fils_con_info) { + qdf_mem_free(profile->fils_con_info); + profile->fils_con_info = NULL; + } + + if (profile->hlp_ie) { + qdf_mem_free(profile->hlp_ie); + profile->hlp_ie = NULL; + profile->hlp_ie_len = 0; + } +} +#else +static inline void csr_free_fils_profile_info(struct csr_roam_profile *profile) +{ } +#endif + +void csr_release_profile(struct mac_context *mac, struct csr_roam_profile *pProfile) +{ + if (pProfile) { + if (pProfile->BSSIDs.bssid) { + qdf_mem_free(pProfile->BSSIDs.bssid); + pProfile->BSSIDs.bssid = NULL; + } + if (pProfile->SSIDs.SSIDList) { + qdf_mem_free(pProfile->SSIDs.SSIDList); + pProfile->SSIDs.SSIDList = NULL; + } + if (pProfile->pWPAReqIE) { + qdf_mem_free(pProfile->pWPAReqIE); + pProfile->pWPAReqIE = NULL; + } + if (pProfile->pRSNReqIE) { + qdf_mem_free(pProfile->pRSNReqIE); + pProfile->pRSNReqIE = NULL; + } +#ifdef FEATURE_WLAN_WAPI + if (pProfile->pWAPIReqIE) { + qdf_mem_free(pProfile->pWAPIReqIE); + pProfile->pWAPIReqIE = NULL; + } +#endif /* FEATURE_WLAN_WAPI */ + + if (pProfile->pAddIEScan) { + qdf_mem_free(pProfile->pAddIEScan); + pProfile->pAddIEScan = NULL; + } + + if (pProfile->pAddIEAssoc) { + qdf_mem_free(pProfile->pAddIEAssoc); + pProfile->pAddIEAssoc = NULL; + } + if (pProfile->ChannelInfo.freq_list) { + qdf_mem_free(pProfile->ChannelInfo.freq_list); + pProfile->ChannelInfo.freq_list = NULL; + } + csr_free_fils_profile_info(pProfile); + qdf_mem_zero(pProfile, sizeof(struct csr_roam_profile)); + } +} + +void csr_free_roam_profile(struct mac_context *mac, uint32_t sessionId) +{ + struct csr_roam_session *pSession = &mac->roam.roamSession[sessionId]; + + if (pSession->pCurRoamProfile) { + csr_release_profile(mac, pSession->pCurRoamProfile); + qdf_mem_free(pSession->pCurRoamProfile); + pSession->pCurRoamProfile = NULL; + } +} + +void csr_free_connect_bss_desc(struct mac_context *mac, uint32_t sessionId) +{ + struct csr_roam_session *pSession = &mac->roam.roamSession[sessionId]; + + if (pSession->pConnectBssDesc) { + qdf_mem_free(pSession->pConnectBssDesc); + pSession->pConnectBssDesc = NULL; + } +} + +tSirResultCodes csr_get_de_auth_rsp_status_code(struct deauth_rsp *pSmeRsp) +{ + uint8_t *pBuffer = (uint8_t *) pSmeRsp; + uint32_t ret; + + pBuffer += + (sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint8_t) + + sizeof(uint16_t)); + /* tSirResultCodes is an enum, assuming is 32bit */ + /* If we cannot make this assumption, use copymemory */ + qdf_get_u32(pBuffer, &ret); + + return (tSirResultCodes) ret; +} + +tSirScanType csr_get_scan_type(struct mac_context *mac, uint8_t chnId) +{ + tSirScanType scanType = eSIR_PASSIVE_SCAN; + enum channel_state channelEnabledType; + + channelEnabledType = wlan_reg_get_channel_state(mac->pdev, chnId); + if (CHANNEL_STATE_ENABLE == channelEnabledType) + scanType = eSIR_ACTIVE_SCAN; + + return scanType; +} + +enum bss_type csr_translate_bsstype_to_mac_type(eCsrRoamBssType csrtype) +{ + enum bss_type ret; + + switch (csrtype) { + case eCSR_BSS_TYPE_INFRASTRUCTURE: + ret = eSIR_INFRASTRUCTURE_MODE; + break; + case eCSR_BSS_TYPE_IBSS: + case eCSR_BSS_TYPE_START_IBSS: + ret = eSIR_IBSS_MODE; + break; + case eCSR_BSS_TYPE_INFRA_AP: + ret = eSIR_INFRA_AP_MODE; + break; + case eCSR_BSS_TYPE_NDI: + ret = eSIR_NDI_MODE; + break; + case eCSR_BSS_TYPE_ANY: + default: + ret = eSIR_AUTO_MODE; + break; + } + + return ret; +} + +/* This function use the parameters to decide the CFG value. */ +/* CSR never sets MLME_DOT11_MODE_ALL to the CFG */ +/* So PE should not see MLME_DOT11_MODE_ALL when it gets the CFG value */ +enum csr_cfgdot11mode +csr_get_cfg_dot11_mode_from_csr_phy_mode(struct csr_roam_profile *pProfile, + eCsrPhyMode phyMode, + bool fProprietary) +{ + uint32_t cfgDot11Mode = eCSR_CFG_DOT11_MODE_ABG; + + switch (phyMode) { + case eCSR_DOT11_MODE_11a: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11A; + break; + case eCSR_DOT11_MODE_11b: + case eCSR_DOT11_MODE_11b_ONLY: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11B; + break; + case eCSR_DOT11_MODE_11g: + case eCSR_DOT11_MODE_11g_ONLY: + if (pProfile && (CSR_IS_INFRA_AP(pProfile)) + && (phyMode == eCSR_DOT11_MODE_11g_ONLY)) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11G_ONLY; + else + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11G; + break; + case eCSR_DOT11_MODE_11n: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + case eCSR_DOT11_MODE_11n_ONLY: + if (pProfile && CSR_IS_INFRA_AP(pProfile)) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N_ONLY; + else + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + case eCSR_DOT11_MODE_abg: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_ABG; + break; + case eCSR_DOT11_MODE_AUTO: + cfgDot11Mode = eCSR_CFG_DOT11_MODE_AUTO; + break; + + case eCSR_DOT11_MODE_11ac: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AC; + else + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + case eCSR_DOT11_MODE_11ac_ONLY: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AC_ONLY; + else + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + case eCSR_DOT11_MODE_11ax: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AX; + else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AC; + else + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + case eCSR_DOT11_MODE_11ax_ONLY: + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AX_ONLY; + else if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AC)) + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11AC; + else + cfgDot11Mode = eCSR_CFG_DOT11_MODE_11N; + break; + + default: + /* No need to assign anything here */ + break; + } + + return cfgDot11Mode; +} + +QDF_STATUS csr_get_regulatory_domain_for_country(struct mac_context *mac, + uint8_t *pCountry, + v_REGDOMAIN_t *pDomainId, + enum country_src source) +{ + QDF_STATUS status = QDF_STATUS_E_INVAL; + QDF_STATUS qdf_status; + uint8_t countryCode[CDS_COUNTRY_CODE_LEN + 1]; + v_REGDOMAIN_t domainId; + + if (pCountry) { + countryCode[0] = pCountry[0]; + countryCode[1] = pCountry[1]; + qdf_status = wlan_reg_get_domain_from_country_code(&domainId, + countryCode, + source); + + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + if (pDomainId) + *pDomainId = domainId; + status = QDF_STATUS_SUCCESS; + } else { + sme_warn("Couldn't find domain for country code %c%c", + pCountry[0], pCountry[1]); + status = QDF_STATUS_E_INVAL; + } + } + + return status; +} + +QDF_STATUS csr_get_modify_profile_fields(struct mac_context *mac, + uint32_t sessionId, + tCsrRoamModifyProfileFields * + pModifyProfileFields) +{ + if (!pModifyProfileFields) + return QDF_STATUS_E_FAILURE; + + qdf_mem_copy(pModifyProfileFields, + &mac->roam.roamSession[sessionId].connectedProfile. + modifyProfileFields, sizeof(tCsrRoamModifyProfileFields)); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS csr_set_modify_profile_fields(struct mac_context *mac, + uint32_t sessionId, + tCsrRoamModifyProfileFields * + pModifyProfileFields) +{ + struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId); + + qdf_mem_copy(&pSession->connectedProfile.modifyProfileFields, + pModifyProfileFields, sizeof(tCsrRoamModifyProfileFields)); + + return QDF_STATUS_SUCCESS; +} + + +bool csr_is_set_key_allowed(struct mac_context *mac, uint32_t sessionId) +{ + bool fRet = true; + struct csr_roam_session *pSession; + + pSession = CSR_GET_SESSION(mac, sessionId); + + /* + * This condition is not working for infra state. When infra is in + * not-connected state the pSession->pCurRoamProfile is NULL, this + * function returns true, that is incorrect. + * Since SAP requires to set key without any BSS started, it needs + * this condition to be met. In other words, this function is useless. + * The current work-around is to process setcontext_rsp no matter + * what the state is. + */ + sme_debug("is not what it intends to. Must be revisit or removed"); + if ((!pSession) + || (csr_is_conn_state_disconnected(mac, sessionId) + && (pSession->pCurRoamProfile) + && (!(CSR_IS_INFRA_AP(pSession->pCurRoamProfile)))) + ) { + fRet = false; + } + + return fRet; +} + +/* no need to acquire lock for this basic function */ +uint16_t sme_chn_to_freq(uint8_t chanNum) +{ + int i; + + for (i = 0; i < NUM_CHANNELS; i++) { + if (WLAN_REG_CH_NUM(i) == chanNum) + return WLAN_REG_CH_TO_FREQ(i); + } + + return 0; +} + +struct lim_channel_status * +csr_get_channel_status(struct mac_context *mac, uint32_t chan_freq) +{ + uint8_t i; + struct lim_scan_channel_status *channel_status; + struct lim_channel_status *entry; + + if (!mac->sap.acs_with_more_param) + return NULL; + + channel_status = &mac->lim.scan_channel_status; + for (i = 0; i < channel_status->total_channel; i++) { + entry = &channel_status->channel_status_list[i]; + if (entry->channelfreq == chan_freq) + return entry; + } + sme_err("Channel %d status info not exist", chan_freq); + + return NULL; +} + +void csr_clear_channel_status(struct mac_context *mac) +{ + struct lim_scan_channel_status *channel_status; + + if (!mac->sap.acs_with_more_param) + return; + + channel_status = &mac->lim.scan_channel_status; + channel_status->total_channel = 0; + + return; +} + +bool csr_is_channel_present_in_list(uint32_t *pChannelList, + int numChannels, uint32_t chan_freq) +{ + int i = 0; + + /* Check for NULL pointer */ + if (!pChannelList || (numChannels == 0)) + return false; + + /* Look for the channel in the list */ + for (i = 0; (i < numChannels) && + (i < CFG_VALID_CHANNEL_LIST_LEN); i++) { + if (pChannelList[i] == chan_freq) + return true; + } + + return false; +} + +/** + * sme_bsstype_to_string() - converts bss type to string. + * @bss_type: bss type enum + * + * Return: printable string for bss type + */ +const char *sme_bss_type_to_string(const uint8_t bss_type) +{ + switch (bss_type) { + CASE_RETURN_STRING(eCSR_BSS_TYPE_INFRASTRUCTURE); + CASE_RETURN_STRING(eCSR_BSS_TYPE_INFRA_AP); + CASE_RETURN_STRING(eCSR_BSS_TYPE_IBSS); + CASE_RETURN_STRING(eCSR_BSS_TYPE_START_IBSS); + CASE_RETURN_STRING(eCSR_BSS_TYPE_ANY); + default: + return "unknown bss type"; + } +} + +QDF_STATUS csr_add_to_channel_list_front(uint32_t *pChannelList, + int numChannels, uint32_t chan_freq) +{ + int i = 0; + + /* Check for NULL pointer */ + if (!pChannelList) + return QDF_STATUS_E_NULL_VALUE; + + /* Make room for the addition. (Start moving from the back.) */ + for (i = numChannels; i > 0; i--) + pChannelList[i] = pChannelList[i - 1]; + + /* Now add the NEW channel...at the front */ + pChannelList[0] = chan_freq; + + return QDF_STATUS_SUCCESS; +} + +/** + * csr_wait_for_connection_update() - Wait for hw mode update + * @mac: Pointer to the MAC context + * @do_release_reacquire_lock: Indicates whether release and + * re-acquisition of SME global lock is required. + * + * Waits for CONNECTION_UPDATE_TIMEOUT time so that the + * hw mode update can get processed. + * + * Return: True if the wait was successful, false otherwise + */ +bool csr_wait_for_connection_update(struct mac_context *mac, + bool do_release_reacquire_lock) +{ + QDF_STATUS status, ret; + + if (do_release_reacquire_lock == true) { + ret = sme_release_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(ret)) { + cds_err("lock release fail %d", ret); + return false; + } + } + + status = policy_mgr_wait_for_connection_update(mac->psoc); + + if (do_release_reacquire_lock == true) { + ret = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(ret)) + return false; + } + + if (!QDF_IS_STATUS_SUCCESS(status)) { + cds_err("wait for event failed"); + return false; + } + + return true; +} + +/** + * csr_get_session_persona() - get persona of a session + * @pmac: pointer to global MAC context + * @session_id: session id + * + * This function is to return the persona of a session + * + * Reture: enum QDF_OPMODE persona + */ +enum QDF_OPMODE csr_get_session_persona(struct mac_context *pmac, + uint32_t session_id) +{ + struct csr_roam_session *session = NULL; + + session = CSR_GET_SESSION(pmac, session_id); + if (!session || !session->pCurRoamProfile) + return QDF_MAX_NO_OF_MODE; + + return session->pCurRoamProfile->csrPersona; +} + +/** + * csr_is_ndi_started() - function to check if NDI is started + * @mac_ctx: handle to mac context + * @session_id: session identifier + * + * returns: true if NDI is started, false otherwise + */ +bool csr_is_ndi_started(struct mac_context *mac_ctx, uint32_t session_id) +{ + struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id); + + if (!session) + return false; + + return eCSR_CONNECT_STATE_TYPE_NDI_STARTED == session->connectState; +} + +bool csr_is_mcc_channel(struct mac_context *mac_ctx, uint32_t chan_freq) +{ + struct csr_roam_session *session; + enum QDF_OPMODE oper_mode; + uint32_t oper_chan_freq = 0; + uint8_t session_id; + bool hw_dbs_capable, same_band_freqs; + + if (chan_freq == 0) + return false; + + hw_dbs_capable = policy_mgr_is_hw_dbs_capable(mac_ctx->psoc); + for (session_id = 0; session_id < WLAN_MAX_VDEVS; session_id++) { + if (CSR_IS_SESSION_VALID(mac_ctx, session_id)) { + session = CSR_GET_SESSION(mac_ctx, session_id); + if (!session->pCurRoamProfile) + continue; + oper_mode = session->pCurRoamProfile->csrPersona; + if ((((oper_mode == QDF_STA_MODE) || + (oper_mode == QDF_P2P_CLIENT_MODE)) && + (session->connectState == + eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED)) || + (((oper_mode == QDF_P2P_GO_MODE) || + (oper_mode == QDF_SAP_MODE)) + && (session->connectState != + eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED))) + oper_chan_freq = + session->connectedProfile.op_freq; + + same_band_freqs = WLAN_REG_IS_SAME_BAND_FREQS( + chan_freq, oper_chan_freq); + + if (oper_chan_freq && chan_freq != oper_chan_freq && + (!hw_dbs_capable || same_band_freqs)) + return true; + } + } + + return false; +} + diff --git a/drivers/staging/qcacld-3.0/core/sme/src/nan/nan_datapath_api.c b/drivers/staging/qcacld-3.0/core/sme/src/nan/nan_datapath_api.c new file mode 100644 index 0000000000000000000000000000000000000000..c15ac649b3b1c16f9ac1421b2221f5e415278884 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/nan/nan_datapath_api.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: nan_datapath_api.c + * + * SME NAN Data path API implementation + */ +#include +#include +#include "sme_api.h" +#include "sme_inside.h" +#include "csr_internal.h" +#include "sme_nan_datapath.h" + +/** + * csr_roam_start_ndi() - Start connection for NAN datapath + * @mac_ctx: Global MAC context + * @session: SME session ID + * @profile: Configuration profile + * + * Return: Success or failure code + */ +QDF_STATUS csr_roam_start_ndi(struct mac_context *mac_ctx, uint32_t session, + struct csr_roam_profile *profile) +{ + QDF_STATUS status; + struct bss_config_param bss_cfg = {0}; + + /* Build BSS configuration from profile */ + status = csr_roam_prepare_bss_config_from_profile(mac_ctx, profile, + &bss_cfg, NULL); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac_ctx->roam.roamSession[session].bssParams.uCfgDot11Mode + = bss_cfg.uCfgDot11Mode; + /* Copy profile parameters to PE session */ + csr_roam_prepare_bss_params(mac_ctx, session, profile, NULL, + &bss_cfg, NULL); + /* + * Following routine will eventually call + * csrRoamIssueStartBss through csrRoamCcmCfgSetCallback + */ + status = csr_roam_set_bss_config_cfg(mac_ctx, session, profile, + NULL, &bss_cfg, NULL, false); + } + + sme_debug("profile config validity: %d", status); + + return status; +} + +/** + * csr_roam_save_ndi_connected_info() - Save connected profile parameters + * @mac_ctx: Global MAC context + * @session_id: Session ID + * @roam_profile: Profile given for starting BSS + * @bssdesc: BSS description from start BSS response + * + * Saves NDI profile parameters into session's connected profile. + * + * Return: None + */ +void csr_roam_save_ndi_connected_info(struct mac_context *mac_ctx, + uint32_t session_id, + struct csr_roam_profile *roam_profile, + struct bss_description *bssdesc) +{ + struct csr_roam_session *roam_session; + tCsrRoamConnectedProfile *connect_profile; + + roam_session = CSR_GET_SESSION(mac_ctx, session_id); + if (!roam_session) { + sme_err("session %d not found", session_id); + return; + } + + connect_profile = &roam_session->connectedProfile; + qdf_mem_zero(connect_profile, sizeof(*connect_profile)); + connect_profile->AuthType = roam_profile->negotiatedAuthType; + connect_profile->AuthInfo = roam_profile->AuthType; + connect_profile->EncryptionType = + roam_profile->negotiatedUCEncryptionType; + connect_profile->EncryptionInfo = roam_profile->EncryptionType; + connect_profile->mcEncryptionType = + roam_profile->negotiatedMCEncryptionType; + connect_profile->mcEncryptionInfo = + roam_profile->mcEncryptionType; + connect_profile->BSSType = roam_profile->BSSType; + connect_profile->modifyProfileFields.uapsd_mask = + roam_profile->uapsd_mask; + connect_profile->op_freq = bssdesc->chan_freq; + connect_profile->beaconInterval = 0; + qdf_mem_copy(&connect_profile->Keys, &roam_profile->Keys, + sizeof(roam_profile->Keys)); + csr_get_bss_id_bss_desc(bssdesc, &connect_profile->bssid); + connect_profile->SSID.length = 0; + csr_free_connect_bss_desc(mac_ctx, session_id); + connect_profile->qap = false; + connect_profile->qosConnection = false; +} + +/** + * csr_roam_update_ndp_return_params() - updates ndp return parameters + * @mac_ctx: MAC context handle + * @result: result of the roaming command + * @roam_status: roam status returned to the roam command initiator + * @roam_result: roam result returned to the roam command initiator + * @roam_info: Roam info data structure to be updated + * + * Results: None + */ +void csr_roam_update_ndp_return_params(struct mac_context *mac_ctx, + uint32_t result, + uint32_t *roam_status, + uint32_t *roam_result, + struct csr_roam_info *roam_info) +{ + + switch (result) { + case eCsrStartBssSuccess: + roam_info->ndp.ndi_create_params.reason = 0; + roam_info->ndp.ndi_create_params.status = + NDP_RSP_STATUS_SUCCESS; + roam_info->ndp.ndi_create_params.sta_id = roam_info->staId; + *roam_status = eCSR_ROAM_NDP_STATUS_UPDATE; + *roam_result = eCSR_ROAM_RESULT_NDI_CREATE_RSP; + break; + case eCsrStartBssFailure: + roam_info->ndp.ndi_create_params.status = NDP_RSP_STATUS_ERROR; + roam_info->ndp.ndi_create_params.reason = + NDP_NAN_DATA_IFACE_CREATE_FAILED; + *roam_status = eCSR_ROAM_NDP_STATUS_UPDATE; + *roam_result = eCSR_ROAM_RESULT_NDI_CREATE_RSP; + break; + case eCsrStopBssSuccess: + roam_info->ndp.ndi_delete_params.reason = 0; + roam_info->ndp.ndi_delete_params.status = + NDP_RSP_STATUS_SUCCESS; + *roam_status = eCSR_ROAM_NDP_STATUS_UPDATE; + *roam_result = eCSR_ROAM_RESULT_NDI_DELETE_RSP; + break; + case eCsrStopBssFailure: + roam_info->ndp.ndi_delete_params.status = NDP_RSP_STATUS_ERROR; + roam_info->ndp.ndi_delete_params.reason = + NDP_NAN_DATA_IFACE_DELETE_FAILED; + *roam_status = eCSR_ROAM_NDP_STATUS_UPDATE; + *roam_result = eCSR_ROAM_RESULT_NDI_DELETE_RSP; + break; + default: + sme_err("Invalid CSR Roam result code: %d", result); + break; + } +} diff --git a/drivers/staging/qcacld-3.0/core/sme/src/qos/sme_qos.c b/drivers/staging/qcacld-3.0/core/sme/src/qos/sme_qos.c new file mode 100644 index 0000000000000000000000000000000000000000..e3b7449554e94c0213e808b6c8361b5cf6360d5e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/qos/sme_qos.c @@ -0,0 +1,7598 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: sme_qos.c + * + * Implementation for SME QoS APIs + */ +/* $Header$ */ +/* Include Files */ + +#include "ani_global.h" + +#include "sme_inside.h" +#include "csr_inside_api.h" +#include "host_diag_core_event.h" +#include "host_diag_core_log.h" + +#include "utils_parser.h" +#include "sme_power_save_api.h" +#include "wlan_mlme_ucfg_api.h" + +#ifndef WLAN_MDM_CODE_REDUCTION_OPT +/* TODO : 6Mbps as Cisco APs seem to like only this value; analysis req. */ +#define SME_QOS_MIN_PHY_RATE 0x5B8D80 +#define SME_QOS_SURPLUS_BW_ALLOWANCE 0x2000 /* Ratio of 1.0 */ +/* Max values to bound tspec params against and avoid rollover */ +#define SME_QOS_32BIT_MAX 0xFFFFFFFF +#define SME_QOS_16BIT_MAX 0xFFFF +#define SME_QOS_16BIT_MSB 0x8000 +/* Adds y to x, but saturates at 32-bit max to avoid rollover */ +#define SME_QOS_BOUNDED_U32_ADD_Y_TO_X(_x, _y) \ + do { \ + (_x) = ((SME_QOS_32BIT_MAX - (_x)) < (_y)) ? \ + (SME_QOS_32BIT_MAX) : (_x) + (_y); \ + } while (0) + +/* + * As per WMM spec there could be max 2 TSPEC running on the same AC with + * different direction. We will refer each TSPEC with an index + */ +#define SME_QOS_TSPEC_INDEX_0 0 +#define SME_QOS_TSPEC_INDEX_1 1 +#define SME_QOS_TSPEC_INDEX_MAX 2 +#define SME_QOS_TSPEC_MASK_BIT_1_SET 1 +#define SME_QOS_TSPEC_MASK_BIT_2_SET 2 +#define SME_QOS_TSPEC_MASK_BIT_1_2_SET 3 +#define SME_QOS_TSPEC_MASK_CLEAR 0 + +/* which key to search on, in the flowlist (1 = flowID, 2 = AC, 4 = reason) */ +#define SME_QOS_SEARCH_KEY_INDEX_1 1 +#define SME_QOS_SEARCH_KEY_INDEX_2 2 +#define SME_QOS_SEARCH_KEY_INDEX_3 4 +#define SME_QOS_SEARCH_KEY_INDEX_4 8 /* ac + direction */ +#define SME_QOS_SEARCH_KEY_INDEX_5 0x10 /* ac + tspec_mask */ +/* special value for searching any Session Id */ +#define SME_QOS_SEARCH_SESSION_ID_ANY WLAN_MAX_VDEVS +#define SME_QOS_ACCESS_POLICY_EDCA 1 +#define SME_QOS_MAX_TID 255 +#define SME_QOS_TSPEC_IE_LENGTH 61 +#define SME_QOS_TSPEC_IE_TYPE 2 +#define SME_QOS_MIN_FLOW_ID 1 +#define SME_QOS_MAX_FLOW_ID 0xFFFFFFFE +#define SME_QOS_INVALID_FLOW_ID 0xFFFFFFFF +/* per the WMM Specification v1.2 Section 2.2.10 */ +/* The Dialog Token field shall be set [...] to a non-zero value */ +#define SME_QOS_MIN_DIALOG_TOKEN 1 +#define SME_QOS_MAX_DIALOG_TOKEN 0xFF +/* Type declarations */ +/* Enumeration of the various states in the QoS state m/c */ +enum sme_qos_states { + SME_QOS_CLOSED = 0, + SME_QOS_INIT, + SME_QOS_LINK_UP, + SME_QOS_REQUESTED, + SME_QOS_QOS_ON, + SME_QOS_HANDOFF, + +}; +/* Enumeration of the various Release QoS trigger */ +enum sme_qosrel_triggers { + SME_QOS_RELEASE_DEFAULT = 0, + SME_QOS_RELEASE_BY_AP, +}; +/* Enumeration of the various QoS cmds */ +enum sme_qos_cmdtype { + SME_QOS_SETUP_REQ = 0, + SME_QOS_RELEASE_REQ, + SME_QOS_MODIFY_REQ, + SME_QOS_RESEND_REQ, + SME_QOS_CMD_MAX +}; +/* Enumeration of the various QoS reason codes to be used in the Flow list */ +enum sme_qos_reasontype { + SME_QOS_REASON_SETUP = 0, + SME_QOS_REASON_RELEASE, + SME_QOS_REASON_MODIFY, + SME_QOS_REASON_MODIFY_PENDING, + SME_QOS_REASON_REQ_SUCCESS, + SME_QOS_REASON_MAX +}; + +/* Table to map user priority passed in as an argument to appropriate Access + * Category as specified in 802.11e/WMM + */ +enum qca_wlan_ac_type sme_qos_up_to_ac_map[SME_QOS_WMM_UP_MAX] = { + QCA_WLAN_AC_BE, /* User Priority 0 */ + QCA_WLAN_AC_BK, /* User Priority 1 */ + QCA_WLAN_AC_BK, /* User Priority 2 */ + QCA_WLAN_AC_BE, /* User Priority 3 */ + QCA_WLAN_AC_VI, /* User Priority 4 */ + QCA_WLAN_AC_VI, /* User Priority 5 */ + QCA_WLAN_AC_VO, /* User Priority 6 */ + QCA_WLAN_AC_VO /* User Priority 7 */ +}; + +/* + * DESCRIPTION + * SME QoS module's FLOW Link List structure. This list can hold information + * per flow/request, like TSPEC params requested, which AC it is running on + */ +struct sme_qos_flowinfoentry { + tListElem link; /* list links */ + uint8_t sessionId; + uint8_t tspec_mask; + enum sme_qos_reasontype reason; + uint32_t QosFlowID; + enum qca_wlan_ac_type ac_type; + struct sme_qos_wmmtspecinfo QoSInfo; + void *HDDcontext; + sme_QosCallback QoSCallback; + bool hoRenewal; /* set to true while re-negotiating flows after */ + /* handoff, will set to false once done with */ + /* the process. Helps SME to decide if at all */ + /* to notify HDD/LIS for flow renewal after HO */ +}; +/* + * DESCRIPTION + * SME QoS module's setup request cmd related information structure. + */ +struct sme_qos_setupcmdinfo { + uint32_t QosFlowID; + struct sme_qos_wmmtspecinfo QoSInfo; + void *HDDcontext; + sme_QosCallback QoSCallback; + enum sme_qos_wmmuptype UPType; + bool hoRenewal; /* set to true while re-negotiating flows after */ + /* handoff, will set to false once done with */ + /* the process. Helps SME to decide if at all */ + /* to notify HDD/LIS for flow renewal after HO */ +}; +/* + * DESCRIPTION + * SME QoS module's modify cmd related information structure. + */ +struct sme_qos_modifycmdinfo { + uint32_t QosFlowID; + enum qca_wlan_ac_type ac; + struct sme_qos_wmmtspecinfo QoSInfo; +}; +/* + * DESCRIPTION + * SME QoS module's resend cmd related information structure. + */ +struct sme_qos_resendcmdinfo { + uint8_t tspecMask; + enum qca_wlan_ac_type ac; + struct sme_qos_wmmtspecinfo QoSInfo; +}; +/* + * DESCRIPTION + * SME QoS module's release cmd related information structure. + */ +struct sme_qos_releasecmdinfo { + uint32_t QosFlowID; +}; +/* + * DESCRIPTION + * SME QoS module's buffered cmd related information structure. + */ +struct sme_qos_cmdinfo { + enum sme_qos_cmdtype command; + struct mac_context *mac; + uint8_t sessionId; + union { + struct sme_qos_setupcmdinfo setupCmdInfo; + struct sme_qos_modifycmdinfo modifyCmdInfo; + struct sme_qos_resendcmdinfo resendCmdInfo; + struct sme_qos_releasecmdinfo releaseCmdInfo; + } u; +}; +/* + * DESCRIPTION + * SME QoS module's buffered cmd List structure. This list can hold information + * related to any pending cmd from HDD + */ +struct sme_qos_cmdinfoentry { + tListElem link; /* list links */ + struct sme_qos_cmdinfo cmdInfo; +}; +/* + * DESCRIPTION + * SME QoS module's Per AC information structure. This can hold information on + * how many flows running on the AC, the current, previous states the AC is in + */ +struct sme_qos_acinfo { + uint8_t num_flows[SME_QOS_TSPEC_INDEX_MAX]; + enum sme_qos_states curr_state; + enum sme_qos_states prev_state; + struct sme_qos_wmmtspecinfo curr_QoSInfo[SME_QOS_TSPEC_INDEX_MAX]; + struct sme_qos_wmmtspecinfo requested_QoSInfo[SME_QOS_TSPEC_INDEX_MAX]; + /* reassoc requested for APSD */ + bool reassoc_pending; + /* + * As per WMM spec there could be max 2 TSPEC running on the same + * AC with different direction. We will refer each TSPEC with an index + */ + /* status showing if both the indices are in use */ + uint8_t tspec_mask_status; + /* tspec negotiation going on for which index */ + uint8_t tspec_pending; + /* set to true while re-negotiating flows after */ + bool hoRenewal; + /* + * handoff, will set to false once done with the process. Helps SME to + * decide if at all to notify HDD/LIS for flow renewal after HO + */ + uint8_t ricIdentifier[SME_QOS_TSPEC_INDEX_MAX]; + /* + * stores the ADD TS response for each AC. The ADD TS response is + * formed by parsing the RIC received in the the reassoc response + */ + tSirAddtsRsp addTsRsp[SME_QOS_TSPEC_INDEX_MAX]; + enum sme_qosrel_triggers relTrig; + +}; +/* + * DESCRIPTION + * SME QoS module's Per session information structure. This can hold + * information on the state of the session + */ +struct sme_qos_sessioninfo { + /* what is this entry's session id */ + uint8_t sessionId; + /* is the session currently active */ + bool sessionActive; + /* All AC info for this session */ + struct sme_qos_acinfo ac_info[QCA_WLAN_AC_ALL]; + /* Bitmask of the ACs with APSD on */ + /* Bit0:VO; Bit1:VI; Bit2:BK; Bit3:BE all other bits are ignored */ + uint8_t apsdMask; + /* association information for this session */ + sme_QosAssocInfo assocInfo; + /* ID assigned to our reassoc request */ + uint32_t roamID; + /* are we in the process of handing off to a different AP */ + bool handoffRequested; + /* commands that are being buffered for this session */ + tDblLinkList bufferedCommandList; + + bool ftHandoffInProgress; + +}; +/* + * DESCRIPTION + * Search key union. We can use the flowID, ac type, or reason to find an + * entry in the flow list + */ +union sme_qos_searchkey { + uint32_t QosFlowID; + enum qca_wlan_ac_type ac_type; + enum sme_qos_reasontype reason; +}; +/* + * DESCRIPTION + * We can either use the flowID or the ac type to find an entry in the flow + * list. The index is a bitmap telling us which key to use. Starting from LSB, + * bit 0 - Flow ID + * bit 1 - AC type + */ +struct sme_qos_searchinfo { + uint8_t sessionId; + uint8_t index; + union sme_qos_searchkey key; + enum sme_qos_wmm_dir_type direction; + uint8_t tspec_mask; +}; + +typedef QDF_STATUS (*sme_QosProcessSearchEntry)(struct mac_context *mac, + tListElem *pEntry); + +static enum sme_qos_statustype sme_qos_internal_setup_req(struct mac_context *mac, + uint8_t sessionId, + struct sme_qos_wmmtspecinfo *pQoSInfo, + sme_QosCallback QoSCallback, + void *HDDcontext, + enum sme_qos_wmmuptype UPType, + uint32_t QosFlowID, + bool buffered_cmd, bool hoRenewal); +static enum sme_qos_statustype sme_qos_internal_modify_req(struct mac_context *mac, + struct sme_qos_wmmtspecinfo *pQoSInfo, + uint32_t QosFlowID, + bool buffered_cmd); +static enum sme_qos_statustype sme_qos_internal_release_req(struct mac_context *mac, + uint8_t session_id, + uint32_t QosFlowID, + bool buffered_cmd); +static enum sme_qos_statustype sme_qos_setup(struct mac_context *mac, + uint8_t sessionId, + struct sme_qos_wmmtspecinfo *pTspec_Info, + enum qca_wlan_ac_type ac); +static QDF_STATUS sme_qos_add_ts_req(struct mac_context *mac, + uint8_t sessionId, + struct sme_qos_wmmtspecinfo *pTspec_Info, + enum qca_wlan_ac_type ac); +static QDF_STATUS sme_qos_del_ts_req(struct mac_context *mac, + uint8_t sessionId, + enum qca_wlan_ac_type ac, uint8_t tspec_mask); +static QDF_STATUS sme_qos_process_add_ts_rsp(struct mac_context *mac, + void *msg_buf); +static QDF_STATUS sme_qos_process_del_ts_ind(struct mac_context *mac, + void *msg_buf); +static QDF_STATUS sme_qos_process_del_ts_rsp(struct mac_context *mac, + void *msg_buf); +static QDF_STATUS sme_qos_process_assoc_complete_ev(struct mac_context *mac, + uint8_t sessionId, void *pEvent_info); +static QDF_STATUS sme_qos_process_reassoc_req_ev(struct mac_context *mac, + uint8_t sessionId, void *pEvent_info); +static QDF_STATUS sme_qos_process_reassoc_success_ev(struct mac_context *mac, + uint8_t sessionId, void *pEvent_info); +static QDF_STATUS sme_qos_process_reassoc_failure_ev(struct mac_context *mac, + uint8_t sessionId, void *pEvent_info); +static QDF_STATUS sme_qos_process_disconnect_ev(struct mac_context *mac, uint8_t + sessionId, void *pEvent_info); +static QDF_STATUS sme_qos_process_join_req_ev(struct mac_context *mac, uint8_t + sessionId, void *pEvent_info); +static QDF_STATUS sme_qos_process_handoff_assoc_req_ev(struct mac_context *mac, + uint8_t sessionId, + void *pEvent_info); +static QDF_STATUS sme_qos_process_handoff_success_ev(struct mac_context *mac, + uint8_t sessionId, void *pEvent_info); +static QDF_STATUS sme_qos_process_preauth_success_ind(struct mac_context *mac, + uint8_t sessionId, + void *pEvent_info); +static QDF_STATUS sme_qos_process_set_key_success_ind(struct mac_context *mac, + uint8_t sessionId, + void *pEvent_info); +static QDF_STATUS sme_qos_process_aggr_qos_rsp(struct mac_context *mac, + void *msg_buf); +static QDF_STATUS sme_qos_ft_aggr_qos_req(struct mac_context *mac, uint8_t + sessionId); +static QDF_STATUS sme_qos_process_add_ts_success_rsp(struct mac_context *mac, + uint8_t sessionId, + tSirAddtsRspInfo *pRsp); +static QDF_STATUS sme_qos_process_add_ts_failure_rsp(struct mac_context *mac, + uint8_t sessionId, + tSirAddtsRspInfo *pRsp); +static QDF_STATUS sme_qos_aggregate_params( + struct sme_qos_wmmtspecinfo *pInput_Tspec_Info, + struct sme_qos_wmmtspecinfo *pCurrent_Tspec_Info, + struct sme_qos_wmmtspecinfo *pUpdated_Tspec_Info); +static QDF_STATUS sme_qos_update_params(uint8_t sessionId, + enum qca_wlan_ac_type ac, + uint8_t tspec_mask, + struct sme_qos_wmmtspecinfo *pTspec_Info); +static enum qca_wlan_ac_type sme_qos_up_to_ac(enum sme_qos_wmmuptype up); + +static bool +sme_qos_is_acm(struct mac_context *mac, struct bss_description *pSirBssDesc, + enum qca_wlan_ac_type ac, tDot11fBeaconIEs *pIes); + +static tListElem *sme_qos_find_in_flow_list(struct sme_qos_searchinfo + search_key); +static QDF_STATUS sme_qos_find_all_in_flow_list(struct mac_context *mac, + struct sme_qos_searchinfo search_key, + sme_QosProcessSearchEntry fnp); +static void sme_qos_state_transition(uint8_t sessionId, + enum qca_wlan_ac_type ac, + enum sme_qos_states new_state); +static QDF_STATUS sme_qos_buffer_cmd(struct sme_qos_cmdinfo *pcmd, bool + insert_head); +static QDF_STATUS sme_qos_process_buffered_cmd(uint8_t sessionId); +static QDF_STATUS sme_qos_save_assoc_info(struct sme_qos_sessioninfo *pSession, + sme_QosAssocInfo *pAssoc_info); +static QDF_STATUS sme_qos_setup_fnp(struct mac_context *mac, tListElem *pEntry); +static QDF_STATUS sme_qos_modification_notify_fnp(struct mac_context *mac, + tListElem *pEntry); +static QDF_STATUS sme_qos_modify_fnp(struct mac_context *mac, tListElem *pEntry); +static QDF_STATUS sme_qos_del_ts_ind_fnp(struct mac_context *mac, tListElem + *pEntry); +static QDF_STATUS sme_qos_reassoc_success_ev_fnp(struct mac_context *mac, + tListElem *pEntry); +static QDF_STATUS sme_qos_add_ts_failure_fnp(struct mac_context *mac, tListElem + *pEntry); +static QDF_STATUS sme_qos_add_ts_success_fnp(struct mac_context *mac, tListElem + *pEntry); +static bool sme_qos_is_rsp_pending(uint8_t sessionId, enum qca_wlan_ac_type ac); +static bool sme_qos_is_uapsd_active(void); + +static QDF_STATUS sme_qos_buffer_existing_flows(struct mac_context *mac, + uint8_t sessionId); +static QDF_STATUS sme_qos_delete_existing_flows(struct mac_context *mac, + uint8_t sessionId); +static void sme_qos_cleanup_ctrl_blk_for_handoff(struct mac_context *mac, + uint8_t sessionId); +static QDF_STATUS sme_qos_delete_buffered_requests(struct mac_context *mac, + uint8_t sessionId); +static bool sme_qos_validate_requested_params(struct mac_context *mac, + struct sme_qos_wmmtspecinfo *pQoSInfo, + uint8_t sessionId); + +static QDF_STATUS qos_issue_command(struct mac_context *mac, uint8_t sessionId, + eSmeCommandType cmdType, + struct sme_qos_wmmtspecinfo *pQoSInfo, + enum qca_wlan_ac_type ac, uint8_t tspec_mask); +/* sme_qos_re_request_add_ts to re-send AddTS for the combined QoS request */ +static enum sme_qos_statustype sme_qos_re_request_add_ts(struct mac_context *mac, + uint8_t sessionId, + struct sme_qos_wmmtspecinfo *pQoSInfo, + enum qca_wlan_ac_type ac, + uint8_t tspecMask); +static void sme_qos_init_a_cs(struct mac_context *mac, uint8_t sessionId); +static QDF_STATUS sme_qos_request_reassoc(struct mac_context *mac, + uint8_t sessionId, + tCsrRoamModifyProfileFields * + pModFields, bool fForce); +static uint32_t sme_qos_assign_flow_id(void); +static uint8_t sme_qos_assign_dialog_token(void); +static QDF_STATUS sme_qos_update_tspec_mask(uint8_t sessionId, + struct sme_qos_searchinfo search_key, + uint8_t new_tspec_mask); + +/* + * DESCRIPTION + * SME QoS module's internal control block. + */ +struct sme_qos_cb_s { + /* global Mac pointer */ + struct mac_context *mac; + /* All Session Info */ + struct sme_qos_sessioninfo *sessionInfo; + /* All FLOW info */ + tDblLinkList flow_list; + /* default TSPEC params */ + struct sme_qos_wmmtspecinfo *def_QoSInfo; + /* counter for assigning Flow IDs */ + uint32_t nextFlowId; + /* counter for assigning Dialog Tokens */ + uint8_t nextDialogToken; +} sme_qos_cb; + +#ifdef WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY +static inline QDF_STATUS sme_qos_allocate_control_block_buffer(void) +{ + uint32_t buf_size; + + buf_size = WLAN_MAX_VDEVS * sizeof(struct sme_qos_sessioninfo); + sme_qos_cb.sessionInfo = qdf_mem_malloc(buf_size); + if (!sme_qos_cb.sessionInfo) + return QDF_STATUS_E_NOMEM; + + buf_size = QCA_WLAN_AC_ALL * sizeof(struct sme_qos_wmmtspecinfo); + sme_qos_cb.def_QoSInfo = qdf_mem_malloc(buf_size); + + if (!sme_qos_cb.def_QoSInfo) { + qdf_mem_free(sme_qos_cb.sessionInfo); + return QDF_STATUS_E_NOMEM; + } + + return QDF_STATUS_SUCCESS; +} + +static inline void sme_qos_free_control_block_buffer(void) +{ + qdf_mem_free(sme_qos_cb.sessionInfo); + sme_qos_cb.sessionInfo = NULL; + + qdf_mem_free(sme_qos_cb.def_QoSInfo); + sme_qos_cb.def_QoSInfo = NULL; +} + +#else /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ + +struct sme_qos_sessioninfo sessionInfo[WLAN_MAX_VDEVS]; +struct sme_qos_wmmtspecinfo def_QoSInfo[QCA_WLAN_AC_ALL]; + +static inline QDF_STATUS sme_qos_allocate_control_block_buffer(void) +{ + qdf_mem_zero(&sessionInfo, sizeof(sessionInfo)); + sme_qos_cb.sessionInfo = sessionInfo; + qdf_mem_zero(&def_QoSInfo, sizeof(def_QoSInfo)); + sme_qos_cb.def_QoSInfo = def_QoSInfo; + + return QDF_STATUS_SUCCESS; +} + +static inline void sme_qos_free_control_block_buffer(void) +{ + sme_qos_cb.sessionInfo = NULL; + sme_qos_cb.def_QoSInfo = NULL; +} +#endif /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */ + +/* External APIs definitions */ + +/** + * sme_qos_open() - called to initialize SME QoS module. + * @mac: global MAC context + * + * This function must be called before any API call to + * SME QoS module. + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_qos_open(struct mac_context *mac) +{ + struct sme_qos_sessioninfo *pSession; + uint8_t sessionId; + QDF_STATUS status; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: initializing SME-QoS module", __func__, __LINE__); + /* alloc and init the control block */ + /* (note that this will make all sessions invalid) */ + if (!QDF_IS_STATUS_SUCCESS(sme_qos_allocate_control_block_buffer())) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SME, + "%s: %d: Failed to allocate buffer", + __func__, __LINE__); + return QDF_STATUS_E_NOMEM; + } + sme_qos_cb.mac = mac; + sme_qos_cb.nextFlowId = SME_QOS_MIN_FLOW_ID; + sme_qos_cb.nextDialogToken = SME_QOS_MIN_DIALOG_TOKEN; + + /* init flow list */ + status = csr_ll_open(&sme_qos_cb.flow_list); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: cannot initialize Flow List", + __func__, __LINE__); + sme_qos_free_control_block_buffer(); + return QDF_STATUS_E_FAILURE; + } + + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; ++sessionId) { + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pSession->sessionId = sessionId; + /* initialize the session's per-AC information */ + sme_qos_init_a_cs(mac, sessionId); + /* initialize the session's buffered command list */ + status = csr_ll_open(&pSession->bufferedCommandList); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: cannot initialize cmd list for session %d", + __func__, __LINE__, sessionId); + sme_qos_free_control_block_buffer(); + return QDF_STATUS_E_FAILURE; + } + } + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: done initializing SME-QoS module", + __func__, __LINE__); + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_close() - To close down SME QoS module. There should not be + * any API call into this module after calling this function until another + * call of sme_qos_open. + * + * mac - Pointer to the global MAC parameter structure. + * Return QDF_STATUS + */ +QDF_STATUS sme_qos_close(struct mac_context *mac) +{ + struct sme_qos_sessioninfo *pSession; + enum qca_wlan_ac_type ac; + uint8_t sessionId; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: closing down SME-QoS", __func__, __LINE__); + + /* cleanup control block */ + /* close the flow list */ + csr_ll_close(&sme_qos_cb.flow_list); + /* shut down all of the sessions */ + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; ++sessionId) { + pSession = &sme_qos_cb.sessionInfo[sessionId]; + if (!pSession) + continue; + + sme_qos_init_a_cs(mac, sessionId); + /* this session doesn't require UAPSD */ + pSession->apsdMask = 0; + + pSession->handoffRequested = false; + pSession->roamID = 0; + /* need to clean up buffered req */ + sme_qos_delete_buffered_requests(mac, sessionId); + /* need to clean up flows */ + sme_qos_delete_existing_flows(mac, sessionId); + + /* Clean up the assoc info if already allocated */ + if (pSession->assocInfo.bss_desc) { + qdf_mem_free(pSession->assocInfo.bss_desc); + pSession->assocInfo.bss_desc = NULL; + } + /* close the session's buffered command list */ + csr_ll_close(&pSession->bufferedCommandList); + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) + sme_qos_state_transition(sessionId, ac, SME_QOS_CLOSED); + + pSession->sessionActive = false; + } + sme_qos_free_control_block_buffer(); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: closed down QoS", __func__, __LINE__); + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_setup_req() - The SME QoS API exposed to HDD to request for QoS + * on a particular AC. + * @mac_handle: The handle returned by mac_open. + * @sessionId: sessionId returned by sme_open_session. + * @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the + * WMM TSPEC related info as defined above, provided by HDD + * @QoSCallback: The callback which is registered per flow while + * requesting for QoS. Used for any notification for the + * flow (i.e. setup success/failure/release) which needs to + * be sent to HDD + * @HDDcontext: A cookie passed by HDD to be used by SME during any QoS + * notification (through the callabck) to HDD + * @UPType: Useful only if HDD or any other upper layer module (BAP etc.) + * looking for implicit QoS setup, in that + * case, the pQoSInfo will be NULL & SME will know about the AC + * (from the UP provided in this param) QoS is requested on + * @pQosFlowID: Identification per flow running on each AC generated by + * SME. It is only meaningful if the QoS setup for the flow is + * successful + * This function should be called after a link has been + * established, i.e. STA is associated with an AP etc. If the request involves + * admission control on the requested AC, HDD needs to provide the necessary + * Traffic Specification (TSPEC) parameters otherwise SME is going to use the + * default params. + * Return: QDF_STATUS_SUCCESS - Setup is successful. + * Other status means Setup request failed + */ +enum sme_qos_statustype sme_qos_setup_req(mac_handle_t mac_handle, + uint32_t sessionId, + struct sme_qos_wmmtspecinfo *pQoSInfo, + sme_QosCallback QoSCallback, + void *HDDcontext, + enum sme_qos_wmmuptype UPType, + uint32_t *pQosFlowID) +{ + struct sme_qos_sessioninfo *pSession; + QDF_STATUS lock_status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + enum sme_qos_statustype status; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: QoS Setup requested by client on session %d", + __func__, __LINE__, sessionId); + lock_status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(lock_status)) + return SME_QOS_STATUS_SETUP_FAILURE_RSP; + /* Make sure the session is valid */ + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Supplied Session ID %d is invalid", + __func__, __LINE__, sessionId); + status = SME_QOS_STATUS_SETUP_FAILURE_RSP; + } else { + /* Make sure the session is active */ + pSession = &sme_qos_cb.sessionInfo[sessionId]; + if (!pSession->sessionActive) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Supplied Session ID %d is inactive", + __func__, __LINE__, sessionId); + status = SME_QOS_STATUS_SETUP_FAILURE_RSP; + } else { + /* Assign a Flow ID */ + *pQosFlowID = sme_qos_assign_flow_id(); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: QoS request on session %d assigned Flow ID %d", + __func__, __LINE__, sessionId, *pQosFlowID); + /* Call the internal function for QoS setup, */ + /* adding a layer of abstraction */ + status = + sme_qos_internal_setup_req(mac, (uint8_t) + sessionId, + pQoSInfo, QoSCallback, + HDDcontext, UPType, + *pQosFlowID, false, + false); + } + } + sme_release_global_lock(&mac->sme); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: QoS setup return status on session %d is %d", + __func__, __LINE__, sessionId, status); + return status; +} + +/** + * sme_qos_modify_req() - The SME QoS API exposed to HDD to request for + * modification of certain QoS params on a flow running on a particular AC. + * @mac_handle: The handle returned by mac_open. + * @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the + * WMM TSPEC related info as defined above, provided by HDD + * @QosFlowID: Identification per flow running on each AC generated by + * SME. It is only meaningful if the QoS setup for the flow has + * been successful already + * + * This function should be called after a link has been established, + * i.e. STA is associated with an AP etc. & a QoS setup has been successful for + * that flow. If the request involves admission control on the requested AC, + * HDD needs to provide the necessary Traffic Specification (TSPEC) parameters & + * SME might start the renegotiation process through ADDTS. + * + * Return: SME_QOS_STATUS_SETUP_SUCCESS_RSP - Modification is successful. + * Other status means request failed + */ +enum sme_qos_statustype sme_qos_modify_req(mac_handle_t mac_handle, + struct sme_qos_wmmtspecinfo *pQoSInfo, + uint32_t QosFlowID) +{ + QDF_STATUS lock_status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + enum sme_qos_statustype status; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: QoS Modify requested by client for Flow %d", + __func__, __LINE__, QosFlowID); + lock_status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(lock_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Unable to obtain lock", __func__, __LINE__); + return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + } + /* Call the internal function for QoS modify, adding a + * layer of abstraction + */ + status = sme_qos_internal_modify_req(mac, pQoSInfo, QosFlowID, false); + sme_release_global_lock(&mac->sme); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: QoS Modify return status on Flow %d is %d", + __func__, __LINE__, QosFlowID, status); + return status; +} + +/** + * sme_qos_release_req() - The SME QoS API exposed to HDD to request for + * releasing a QoS flow running on a particular AC. + * + * @mac_handle: The handle returned by mac_open. + * @session_id: session_id returned by sme_open_session. + * @QosFlowID: Identification per flow running on each AC generated by SME + * It is only meaningful if the QoS setup for the flow is successful + * + * This function should be called only if a QoS is set up with a valid FlowID. + * HDD sould invoke this API only if an explicit request for QoS release has + * come from Application + * + * Return: QDF_STATUS_SUCCESS - Release is successful. + */ +enum sme_qos_statustype sme_qos_release_req(mac_handle_t mac_handle, + uint8_t session_id, + uint32_t QosFlowID) +{ + QDF_STATUS lock_status = QDF_STATUS_E_FAILURE; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + enum sme_qos_statustype status; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: QoS Release requested by client for Flow %d", + __func__, __LINE__, QosFlowID); + lock_status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(lock_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Unable to obtain lock", __func__, __LINE__); + return SME_QOS_STATUS_RELEASE_FAILURE_RSP; + } + /* Call the internal function for QoS release, adding a + * layer of abstraction + */ + status = sme_qos_internal_release_req(mac, session_id, QosFlowID, + false); + sme_release_global_lock(&mac->sme); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: QoS Release return status on Flow %d is %d", + __func__, __LINE__, QosFlowID, status); + return status; +} + +void qos_release_command(struct mac_context *mac, tSmeCmd *pCommand) +{ + qdf_mem_zero(&pCommand->u.qosCmd, sizeof(tGenericQosCmd)); + csr_release_command(mac, pCommand); +} + +/** + * sme_qos_msg_processor() - Processes QOS messages + * @mac_ctx: Pointer to the global MAC parameter structure. + * @msg_type: the type of msg passed by PE as defined in wni_api.h + * @msg: a pointer to a buffer that maps to various structures bases. + * + * sme_process_msg() calls this function for the messages that + * are handled by SME QoS module. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_qos_msg_processor(struct mac_context *mac_ctx, + uint16_t msg_type, void *msg) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + tListElem *entry = NULL; + tSmeCmd *command; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL(" msg = %d for QoS"), msg_type); + /* switch on the msg type & make the state transition accordingly */ + switch (msg_type) { + case eWNI_SME_ADDTS_RSP: + entry = csr_nonscan_active_ll_peek_head(mac_ctx, + LL_ACCESS_LOCK); + if (!entry) + break; + command = GET_BASE_ADDR(entry, tSmeCmd, Link); + if (eSmeCommandAddTs == command->command) { + status = sme_qos_process_add_ts_rsp(mac_ctx, msg); + if (csr_nonscan_active_ll_remove_entry(mac_ctx, entry, + LL_ACCESS_LOCK)) { + qos_release_command(mac_ctx, command); + } + } + break; + case eWNI_SME_DELTS_RSP: + entry = csr_nonscan_active_ll_peek_head(mac_ctx, + LL_ACCESS_LOCK); + if (!entry) + break; + command = GET_BASE_ADDR(entry, tSmeCmd, Link); + if (eSmeCommandDelTs == command->command) { + status = sme_qos_process_del_ts_rsp(mac_ctx, msg); + if (csr_nonscan_active_ll_remove_entry(mac_ctx, entry, + LL_ACCESS_LOCK)) { + qos_release_command(mac_ctx, command); + } + } + break; + case eWNI_SME_DELTS_IND: + status = sme_qos_process_del_ts_ind(mac_ctx, msg); + break; + case eWNI_SME_FT_AGGR_QOS_RSP: + status = sme_qos_process_aggr_qos_rsp(mac_ctx, msg); + break; + default: + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("unknown msg type = %d"), + msg_type); + break; + } + return status; +} + +/* + * sme_qos_csr_event_ind() - The QoS sub-module in SME expects notifications + * from CSR when certain events occur as mentioned in sme_qos_csr_event_indType. + * + * mac - Pointer to the global MAC parameter structure. + * ind - The event occurred of type sme_qos_csr_event_indType. + * pEvent_info - Information related to the event + * Return QDF_STATUS + */ +QDF_STATUS sme_qos_csr_event_ind(struct mac_context *mac, + uint8_t sessionId, + sme_qos_csr_event_indType ind, void *pEvent_info) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + sme_debug("On Session %d Event %d received from CSR", sessionId, ind); + switch (ind) { + case SME_QOS_CSR_ASSOC_COMPLETE: + /* expecting assoc info in pEvent_info */ + status = sme_qos_process_assoc_complete_ev(mac, sessionId, + pEvent_info); + break; + case SME_QOS_CSR_REASSOC_REQ: + /* nothing expected in pEvent_info */ + status = sme_qos_process_reassoc_req_ev(mac, sessionId, + pEvent_info); + break; + case SME_QOS_CSR_REASSOC_COMPLETE: + /* expecting assoc info in pEvent_info */ + status = + sme_qos_process_reassoc_success_ev(mac, sessionId, + pEvent_info); + break; + case SME_QOS_CSR_REASSOC_FAILURE: + /* nothing expected in pEvent_info */ + status = + sme_qos_process_reassoc_failure_ev(mac, sessionId, + pEvent_info); + break; + case SME_QOS_CSR_DISCONNECT_REQ: + case SME_QOS_CSR_DISCONNECT_IND: + /* nothing expected in pEvent_info */ + status = sme_qos_process_disconnect_ev(mac, sessionId, + pEvent_info); + break; + case SME_QOS_CSR_JOIN_REQ: + /* nothing expected in pEvent_info */ + status = sme_qos_process_join_req_ev(mac, sessionId, + pEvent_info); + break; + case SME_QOS_CSR_HANDOFF_ASSOC_REQ: + /* nothing expected in pEvent_info */ + status = sme_qos_process_handoff_assoc_req_ev(mac, sessionId, + pEvent_info); + break; + case SME_QOS_CSR_HANDOFF_COMPLETE: + /* nothing expected in pEvent_info */ + status = + sme_qos_process_handoff_success_ev(mac, sessionId, + pEvent_info); + break; + case SME_QOS_CSR_PREAUTH_SUCCESS_IND: + status = + sme_qos_process_preauth_success_ind(mac, sessionId, + pEvent_info); + break; + case SME_QOS_CSR_SET_KEY_SUCCESS_IND: + status = + sme_qos_process_set_key_success_ind(mac, sessionId, + pEvent_info); + break; + default: + /* Err msg */ + sme_err("On Session %d Unknown Event %d received from CSR", + sessionId, ind); + break; + } + + return status; +} + +/* + * sme_qos_get_acm_mask() - The QoS sub-module API to find out on which ACs + * AP mandates Admission Control (ACM = 1) + * (Bit0:VO; Bit1:VI; Bit2:BK; Bit3:BE all other bits are ignored) + * + * mac - Pointer to the global MAC parameter structure. + * pSirBssDesc - The event occurred of type sme_qos_csr_event_indType. + * Return a bit mask indicating for which ACs AP has ACM set to 1 + */ +uint8_t sme_qos_get_acm_mask(struct mac_context *mac, struct bss_description + *pSirBssDesc, tDot11fBeaconIEs *pIes) +{ + enum qca_wlan_ac_type ac; + uint8_t acm_mask = 0; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked", __func__, __LINE__); + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + if (sme_qos_is_acm(mac, pSirBssDesc, ac, pIes)) + acm_mask = acm_mask | (1 << (QCA_WLAN_AC_VO - ac)); + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: mask is %d", __func__, __LINE__, acm_mask); + return acm_mask; +} + +/* Internal function definitions */ + +/** + * sme_qos_internal_setup_req() - The SME QoS internal setup request handling + * function. + * + * @mac: Pointer to the global MAC parameter structure. + * @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the + * WMM TSPEC related info as defined above, provided by HDD + * @QoSCallback: The callback which is registered per flow while + * requesting for QoS. Used for any notification for the + * flow (i.e. setup success/failure/release) which needs to + * be sent to HDD + * @HDDcontext: A cookie passed by HDD to be used by SME during any QoS + * notification (through the callabck) to HDD + * @UPType: Useful only if HDD or any other upper layer module (BAP etc.) + * looking for implicit QoS setup, in that + * case, the pQoSInfo will be NULL & SME will know about the AC + * (from the UP provided in this param) QoS is requested on + * @QosFlowID: Identification per flow running on each AC generated by + * SME. It is only meaningful if the QoS setup for the flow is + * successful + * @buffered_cmd: tells us if the cmd was a buffered one or fresh from + * client + * + * If the request involves admission control on the requested AC, HDD needs to + * provide the necessary Traffic Specification (TSPEC) parameters otherwise SME + * is going to use the default params. + * + * Return: QDF_STATUS_SUCCESS - Setup is successful. + * Other status means Setup request failed + */ +static enum sme_qos_statustype sme_qos_internal_setup_req(struct mac_context *mac, + uint8_t sessionId, + struct sme_qos_wmmtspecinfo *pQoSInfo, + sme_QosCallback QoSCallback, + void *HDDcontext, + enum sme_qos_wmmuptype UPType, + uint32_t QosFlowID, + bool buffered_cmd, bool hoRenewal) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + enum qca_wlan_ac_type ac; + struct sme_qos_wmmtspecinfo Tspec_Info; + enum sme_qos_states new_state = SME_QOS_CLOSED; + struct sme_qos_flowinfoentry *pentry = NULL; + struct sme_qos_cmdinfo cmd; + enum sme_qos_statustype status = SME_QOS_STATUS_SETUP_FAILURE_RSP; + uint8_t tmask = 0; + uint8_t new_tmask = 0; + struct sme_qos_searchinfo search_key; + QDF_STATUS hstatus; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d for flow %d", + __func__, __LINE__, sessionId, QosFlowID); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + /* if caller sent an empty TSPEC, fill up with the default one */ + if (!pQoSInfo) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_WARN, + "%s: %d: caller sent an empty QoS param list, using defaults", + __func__, __LINE__); + /* find the AC with UPType passed in */ + ac = sme_qos_up_to_ac(UPType); + if (QCA_WLAN_AC_ALL == ac) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid AC %d from UP %d", + __func__, __LINE__, ac, UPType); + + return SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP; + } + Tspec_Info = sme_qos_cb.def_QoSInfo[ac]; + } else { + /* find the AC */ + ac = sme_qos_up_to_ac(pQoSInfo->ts_info.up); + if (QCA_WLAN_AC_ALL == ac) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid AC %d from UP %d", + __func__, __LINE__, ac, pQoSInfo->ts_info.up); + + return SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP; + } + /* validate QoS params */ + if (!sme_qos_validate_requested_params(mac, pQoSInfo, + sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid params", __func__, __LINE__); + return SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP; + } + Tspec_Info = *pQoSInfo; + } + pACInfo = &pSession->ac_info[ac]; + /* check to consider the following flowing scenario. + * Addts request is pending on one AC, while APSD requested on another + * which needs a reassoc. Will buffer a request if Addts is pending + * on any AC, which will safegaurd the above scenario, & also won't + * confuse PE with back to back Addts or Addts followed by Reassoc + */ + if (sme_qos_is_rsp_pending(sessionId, ac)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: buffering the setup request for flow %d in state %d since another request is pending", + __func__, __LINE__, QosFlowID, pACInfo->curr_state); + /* we need to buffer the command */ + cmd.command = SME_QOS_SETUP_REQ; + cmd.mac = mac; + cmd.sessionId = sessionId; + cmd.u.setupCmdInfo.HDDcontext = HDDcontext; + cmd.u.setupCmdInfo.QoSInfo = Tspec_Info; + cmd.u.setupCmdInfo.QoSCallback = QoSCallback; + cmd.u.setupCmdInfo.UPType = UPType; + cmd.u.setupCmdInfo.hoRenewal = hoRenewal; + cmd.u.setupCmdInfo.QosFlowID = QosFlowID; + hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: couldn't buffer the setup request in state = %d", + __func__, __LINE__, pACInfo->curr_state); + return SME_QOS_STATUS_SETUP_FAILURE_RSP; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Buffered setup request for flow = %d", + __func__, __LINE__, QosFlowID); + return SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; + } + /* get into the state m/c to see if the request can be granted */ + switch (pACInfo->curr_state) { + case SME_QOS_LINK_UP: + /* call the internal qos setup logic to decide on if the + * request is NOP, or need reassoc for APSD and/or need to + * send out ADDTS + */ + status = sme_qos_setup(mac, sessionId, &Tspec_Info, ac); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d with AC %d in state SME_QOS_LINK_UP sme_qos_setup returned with status %d", + __func__, __LINE__, sessionId, ac, status); + + if ((SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) || + (SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) + || (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == + status)) { + /* we received an expected "good" status */ + /* create an entry in the flow list */ + pentry = qdf_mem_malloc(sizeof(*pentry)); + if (!pentry) + return SME_QOS_STATUS_SETUP_FAILURE_RSP; + + pentry->ac_type = ac; + pentry->HDDcontext = HDDcontext; + pentry->QoSCallback = QoSCallback; + pentry->hoRenewal = hoRenewal; + pentry->QosFlowID = QosFlowID; + pentry->sessionId = sessionId; + /* since we are in state SME_QOS_LINK_UP this must be + * the first TSPEC on this AC, so use index 0 + * (mask bit 1) + */ + pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0] = + Tspec_Info; + if (SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) { + if (pACInfo->tspec_mask_status && + !pACInfo->reassoc_pending) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d with AC %d in state SME_QOS_LINK_UP tspec_mask_status is %d but should not be set yet", + __func__, __LINE__, sessionId, + ac, + pACInfo->tspec_mask_status); + qdf_mem_free(pentry); + return SME_QOS_STATUS_SETUP_FAILURE_RSP; + } + pACInfo->tspec_mask_status = + SME_QOS_TSPEC_MASK_BIT_1_SET; + if (!pACInfo->reassoc_pending) + /* we didn't request for reassoc, it + * must be a tspec negotiation + */ + pACInfo->tspec_pending = 1; + + pentry->reason = SME_QOS_REASON_SETUP; + new_state = SME_QOS_REQUESTED; + } else { + /* SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_ + * RSP or SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET + * _ALREADY + */ + pentry->reason = SME_QOS_REASON_REQ_SUCCESS; + new_state = SME_QOS_QOS_ON; + pACInfo->tspec_mask_status = + SME_QOS_TSPEC_MASK_BIT_1_SET; + pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0] = + Tspec_Info; + if (buffered_cmd && !pentry->hoRenewal) { + QoSCallback(MAC_HANDLE(mac), + HDDcontext, + &pACInfo-> + curr_QoSInfo + [SME_QOS_TSPEC_INDEX_0], + status, pentry->QosFlowID); + } + pentry->hoRenewal = false; + } + pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0]++; + + /* indicate on which index the flow entry belongs to & + * add it to the Flow List at the end + */ + pentry->tspec_mask = pACInfo->tspec_mask_status; + pentry->QoSInfo = Tspec_Info; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Creating entry on session %d at %pK with flowID %d", + __func__, __LINE__, + sessionId, pentry, QosFlowID); + csr_ll_insert_tail(&sme_qos_cb.flow_list, &pentry->link, + true); + } else { + /* unexpected status returned by sme_qos_setup() */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d unexpected status %d returned by sme_qos_setup", + __func__, __LINE__, sessionId, status); + new_state = pACInfo->curr_state; + if (buffered_cmd && hoRenewal) + QoSCallback(MAC_HANDLE(mac), HDDcontext, + &pACInfo-> + curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], + SME_QOS_STATUS_RELEASE_QOS_LOST_IND, + QosFlowID); + } + break; + case SME_QOS_HANDOFF: + case SME_QOS_REQUESTED: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Buffering setup request for flow %d in state = %d", + __func__, __LINE__, QosFlowID, pACInfo->curr_state); + /* buffer cmd */ + cmd.command = SME_QOS_SETUP_REQ; + cmd.mac = mac; + cmd.sessionId = sessionId; + cmd.u.setupCmdInfo.HDDcontext = HDDcontext; + cmd.u.setupCmdInfo.QoSInfo = Tspec_Info; + cmd.u.setupCmdInfo.QoSCallback = QoSCallback; + cmd.u.setupCmdInfo.UPType = UPType; + cmd.u.setupCmdInfo.hoRenewal = hoRenewal; + cmd.u.setupCmdInfo.QosFlowID = QosFlowID; + hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d couldn't buffer the setup request for flow %d in state = %d", + __func__, __LINE__, + sessionId, QosFlowID, pACInfo->curr_state); + return SME_QOS_STATUS_SETUP_FAILURE_RSP; + } + status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; + new_state = pACInfo->curr_state; + break; + case SME_QOS_QOS_ON: + + /* check if multiple flows running on the ac */ + if ((pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0] > 0) || + (pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] > 0)) { + /* do we need to care about the case where APSD + * needed on ACM = 0 below? + */ + if (CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(mac) || + sme_qos_is_acm(mac, pSession->assocInfo.bss_desc, + ac, NULL)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: tspec_mask_status = %d for AC = %d", + __func__, __LINE__, + pACInfo->tspec_mask_status, ac); + if (!pACInfo->tspec_mask_status) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: tspec_mask_status can't be 0 for ac: %d in state: %d", + __func__, __LINE__, ac, + pACInfo->curr_state); + return status; + } + /* Flow aggregation */ + if (((pACInfo->tspec_mask_status > 0) && + (pACInfo->tspec_mask_status <= + SME_QOS_TSPEC_INDEX_MAX))) { + /* Either of upstream, downstream or + * bidirectional flows are present If + * either of new stream or current + * stream is for bidirecional, aggregate + * the new stream with the current + * streams present and send out + * aggregated Tspec. + */ + if ((Tspec_Info.ts_info.direction == + SME_QOS_WMM_TS_DIR_BOTH) + || (pACInfo-> + curr_QoSInfo[pACInfo-> + tspec_mask_status - + 1].ts_info. + direction == + SME_QOS_WMM_TS_DIR_BOTH)) + /* Aggregate the new stream with + * the current stream(s). + */ + tmask = pACInfo-> + tspec_mask_status; + /* None of new stream or current + * (aggregated) streams are for + * bidirectional. Check if the new + * stream direction matches the current + * stream direction. + */ + else if (pACInfo-> + curr_QoSInfo[pACInfo-> + tspec_mask_status + - + 1].ts_info. + direction == + Tspec_Info.ts_info.direction) + /* Aggregate the new stream with + * the current stream(s). + */ + tmask = + pACInfo->tspec_mask_status; + /* New stream is in different + * direction. + */ + else { + /* No Aggregation. Mark the + * 2nd tpsec index also as + * active. + */ + tmask = + SME_QOS_TSPEC_MASK_CLEAR; + new_tmask = + SME_QOS_TSPEC_MASK_BIT_1_2_SET + & ~pACInfo-> + tspec_mask_status; + pACInfo->tspec_mask_status = + SME_QOS_TSPEC_MASK_BIT_1_2_SET; + } + } else if (SME_QOS_TSPEC_MASK_BIT_1_2_SET == + pACInfo->tspec_mask_status) { + /* Both uplink and downlink streams are + * present. If new stream is + * bidirectional, aggregate new stream + * with all existing upstreams and down + * streams. Send out new aggregated + * tpsec. + */ + if (Tspec_Info.ts_info.direction == + SME_QOS_WMM_TS_DIR_BOTH) { + /* Only one tspec index (0) will + * be in use after this + * aggregation. + */ + tmask = + SME_QOS_TSPEC_MASK_BIT_1_2_SET; + pACInfo->tspec_mask_status = + SME_QOS_TSPEC_MASK_BIT_1_SET; + } + /* New stream is also uni-directional + * Find out the tsepc index with which + * it needs to be aggregated + */ + else if (pACInfo-> + curr_QoSInfo + [SME_QOS_TSPEC_INDEX_0]. + ts_info.direction != + Tspec_Info.ts_info.direction) + /* Aggregate with 2nd tspec + * index + */ + tmask = + SME_QOS_TSPEC_MASK_BIT_2_SET; + else + /* Aggregate with 1st tspec + * index + */ + tmask = + SME_QOS_TSPEC_MASK_BIT_1_SET; + } else + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: wrong tmask = %d", + __func__, __LINE__, + pACInfo->tspec_mask_status); + } else + /* ACM = 0 */ + /* We won't be sending a TSPEC to the AP but + * we still need to aggregate to calculate + * trigger frame parameters + */ + tmask = SME_QOS_TSPEC_MASK_BIT_1_SET; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: tmask = %d, new_tmask = %d in state = %d", + __func__, __LINE__, + tmask, new_tmask, pACInfo->curr_state); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: tspec_mask_status = %d for AC = %d", + __func__, __LINE__, + pACInfo->tspec_mask_status, ac); + if (tmask) { + /* create the aggregate TSPEC */ + if (tmask != SME_QOS_TSPEC_MASK_BIT_1_2_SET) { + hstatus = + sme_qos_aggregate_params( + &Tspec_Info, + &pACInfo-> + curr_QoSInfo + [tmask - 1], + &pACInfo-> + requested_QoSInfo + [tmask - 1]); + } else { + /* Aggregate the new bidirectional + * stream with the existing upstreams + * and downstreams in tspec indices 0 + * and 1. + */ + tmask = SME_QOS_TSPEC_MASK_BIT_1_SET; + + hstatus = sme_qos_aggregate_params( + &Tspec_Info, &pACInfo-> + curr_QoSInfo + [SME_QOS_TSPEC_INDEX_0], + &pACInfo-> + requested_QoSInfo + [tmask - 1]); + if (hstatus == QDF_STATUS_SUCCESS) { + hstatus = + sme_qos_aggregate_params + (&pACInfo-> + curr_QoSInfo + [SME_QOS_TSPEC_INDEX_1], + &pACInfo-> + requested_QoSInfo[tmask - 1], + NULL); + } + } + + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: failed to aggregate params", + __func__, __LINE__); + return SME_QOS_STATUS_SETUP_FAILURE_RSP; + } + } else { + if (! + (new_tmask > 0 + && new_tmask <= SME_QOS_TSPEC_INDEX_MAX)) { + return SME_QOS_STATUS_SETUP_FAILURE_RSP; + } + tmask = new_tmask; + pACInfo->requested_QoSInfo[tmask - 1] = + Tspec_Info; + } + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: no flows running for ac = %d while in state = %d", + __func__, __LINE__, ac, pACInfo->curr_state); + return status; + } + /* although aggregating, make sure to request on the correct + * UP,TID,PSB and direction + */ + pACInfo->requested_QoSInfo[tmask - 1].ts_info.up = + Tspec_Info.ts_info.up; + pACInfo->requested_QoSInfo[tmask - 1].ts_info.tid = + Tspec_Info.ts_info.tid; + pACInfo->requested_QoSInfo[tmask - 1].ts_info.direction = + Tspec_Info.ts_info.direction; + pACInfo->requested_QoSInfo[tmask - 1].ts_info.psb = + Tspec_Info.ts_info.psb; + status = + sme_qos_setup(mac, sessionId, + &pACInfo->requested_QoSInfo[tmask - 1], + ac); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d with AC %d in state SME_QOS_QOS_ON sme_qos_setup returned with status %d", + __func__, __LINE__, sessionId, ac, status); + if ((SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) || + (SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) + || (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == + status)) { + /* we received an expected "good" status */ + /* create an entry in the flow list */ + pentry = qdf_mem_malloc(sizeof(*pentry)); + if (!pentry) + return SME_QOS_STATUS_SETUP_FAILURE_RSP; + + pentry->ac_type = ac; + pentry->HDDcontext = HDDcontext; + pentry->QoSCallback = QoSCallback; + pentry->hoRenewal = hoRenewal; + pentry->QosFlowID = QosFlowID; + pentry->sessionId = sessionId; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Creating flow %d", + __func__, __LINE__, QosFlowID); + if ((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == + status) + || (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == + status)) { + new_state = pACInfo->curr_state; + pentry->reason = SME_QOS_REASON_REQ_SUCCESS; + pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0] = + pACInfo-> + requested_QoSInfo[SME_QOS_TSPEC_INDEX_0]; + if (buffered_cmd && !pentry->hoRenewal) { + QoSCallback(MAC_HANDLE(mac), + HDDcontext, + &pACInfo-> + curr_QoSInfo + [SME_QOS_TSPEC_INDEX_0], + status, pentry->QosFlowID); + } + if ( + SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY + == status) { + /* if we are not in handoff, then notify + * all flows on this AC that the + * aggregate TSPEC may have changed + */ + if (!pentry->hoRenewal) { + qdf_mem_zero(&search_key, + sizeof + (struct sme_qos_searchinfo)); + search_key.key.ac_type = ac; + search_key.index = + SME_QOS_SEARCH_KEY_INDEX_2; + search_key.sessionId = + sessionId; + hstatus = + sme_qos_find_all_in_flow_list + (mac, search_key, + sme_qos_setup_fnp); + if (!QDF_IS_STATUS_SUCCESS + (hstatus)) { + QDF_TRACE + (QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: couldn't notify other entries on this AC =%d", + __func__, __LINE__, + ac); + } + } + } + pentry->hoRenewal = false; + } else { + /* SME_QOS_STATUS_SETUP_REQ_PENDING_RSP */ + new_state = SME_QOS_REQUESTED; + pentry->reason = SME_QOS_REASON_SETUP; + /* Need this info when addts comes back from PE + * to know on which index of the AC the request + * was from + */ + pACInfo->tspec_pending = tmask; + } + pACInfo->num_flows[tmask - 1]++; + /* indicate on which index the flow entry belongs to & + * add it to the Flow List at the end + */ + pentry->tspec_mask = tmask; + pentry->QoSInfo = Tspec_Info; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d creating entry at %pK with flowID %d", + __func__, __LINE__, + sessionId, pentry, QosFlowID); + csr_ll_insert_tail(&sme_qos_cb.flow_list, &pentry->link, + true); + } else { + /* unexpected status returned by sme_qos_setup() */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d unexpected status %d returned by sme_qos_setup", + __func__, __LINE__, sessionId, status); + new_state = pACInfo->curr_state; + } + break; + case SME_QOS_CLOSED: + case SME_QOS_INIT: + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: setup requested in unexpected state = %d", + __func__, __LINE__, pACInfo->curr_state); + new_state = pACInfo->curr_state; + } + /* If current state is same as previous no need for transistion, + * if we are doing reassoc & we are already in handoff state, no need to + * move to requested state. But make sure to set the previous state as + * requested state + */ + if ((new_state != pACInfo->curr_state) && + (!(pACInfo->reassoc_pending && + (SME_QOS_HANDOFF == pACInfo->curr_state)))) + sme_qos_state_transition(sessionId, ac, new_state); + + if (pACInfo->reassoc_pending && + (SME_QOS_HANDOFF == pACInfo->curr_state)) + pACInfo->prev_state = SME_QOS_REQUESTED; + + if ((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || + (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status)) + (void)sme_qos_process_buffered_cmd(sessionId); + + return status; +} + +/** + * sme_qos_internal_modify_req() - The SME QoS internal function to request + * for modification of certain QoS params on a flow running on a particular AC. + * @mac: Pointer to the global MAC parameter structure. + * @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the + * WMM TSPEC related info as defined above, provided by HDD + * @QosFlowID: Identification per flow running on each AC generated by + * SME. It is only meaningful if the QoS setup for the flow has + * been successful already + * + * If the request involves admission control on the requested AC, HDD needs to + * provide the necessary Traffic Specification (TSPEC) parameters & SME might + * start the renegotiation process through ADDTS. + * + * Return: SME_QOS_STATUS_SETUP_SUCCESS_RSP - Modification is successful. + * Other status means request failed + */ +static enum sme_qos_statustype sme_qos_internal_modify_req(struct mac_context *mac, + struct sme_qos_wmmtspecinfo *pQoSInfo, + uint32_t QosFlowID, + bool buffered_cmd) +{ + tListElem *pEntry = NULL; + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + struct sme_qos_flowinfoentry *pNewEntry = NULL; + struct sme_qos_flowinfoentry *flow_info = NULL; + enum qca_wlan_ac_type ac; + enum sme_qos_states new_state = SME_QOS_CLOSED; + enum sme_qos_statustype status = + SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + struct sme_qos_wmmtspecinfo Aggr_Tspec_Info; + struct sme_qos_searchinfo search_key; + struct sme_qos_cmdinfo cmd; + uint8_t sessionId; + QDF_STATUS hstatus; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked for flow %d", __func__, __LINE__, QosFlowID); + + qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo)); + /* set the key type & the key to be searched in the Flow List */ + search_key.key.QosFlowID = QosFlowID; + search_key.index = SME_QOS_SEARCH_KEY_INDEX_1; + search_key.sessionId = SME_QOS_SEARCH_SESSION_ID_ANY; + /* go through the link list to find out the details on the flow */ + pEntry = sme_qos_find_in_flow_list(search_key); + if (!pEntry) { + /* Err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: no match found for flowID = %d", + __func__, __LINE__, QosFlowID); + return SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP; + } + /* find the AC */ + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link); + ac = flow_info->ac_type; + + sessionId = flow_info->sessionId; + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pACInfo = &pSession->ac_info[ac]; + + /* validate QoS params */ + if (!sme_qos_validate_requested_params(mac, pQoSInfo, sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid params", __func__, __LINE__); + return SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP; + } + /* For modify, make sure that direction, TID and UP are not + * being altered + */ + if ((pQoSInfo->ts_info.direction != + flow_info->QoSInfo.ts_info.direction) + || (pQoSInfo->ts_info.up != flow_info->QoSInfo.ts_info.up) + || (pQoSInfo->ts_info.tid != flow_info->QoSInfo.ts_info.tid)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Modification of direction/tid/up is not allowed", + __func__, __LINE__); + + return SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP; + } + + /* should not be same as previous ioctl parameters */ + if ((pQoSInfo->nominal_msdu_size == + flow_info->QoSInfo.nominal_msdu_size) && + (pQoSInfo->maximum_msdu_size == + flow_info->QoSInfo.maximum_msdu_size) && + (pQoSInfo->min_data_rate == + flow_info->QoSInfo.min_data_rate) && + (pQoSInfo->mean_data_rate == + flow_info->QoSInfo.mean_data_rate) && + (pQoSInfo->peak_data_rate == + flow_info->QoSInfo.peak_data_rate) && + (pQoSInfo->min_service_interval == + flow_info->QoSInfo.min_service_interval) && + (pQoSInfo->max_service_interval == + flow_info->QoSInfo.max_service_interval) && + (pQoSInfo->inactivity_interval == + flow_info->QoSInfo.inactivity_interval) && + (pQoSInfo->suspension_interval == + flow_info->QoSInfo.suspension_interval) && + (pQoSInfo->surplus_bw_allowance == + flow_info->QoSInfo.surplus_bw_allowance)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: the addts parameters are same as last request, dropping the current request", + __func__, __LINE__); + + return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + } + + /* check to consider the following flowing scenario. + * Addts request is pending on one AC, while APSD requested on another + * which needs a reassoc. Will buffer a request if Addts is pending on + * any AC, which will safegaurd the above scenario, & also won't + * confuse PE with back to back Addts or Addts followed by Reassoc + */ + if (sme_qos_is_rsp_pending(sessionId, ac)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: buffering the modify request for flow %d in state %d since another request is pending", + __func__, __LINE__, QosFlowID, pACInfo->curr_state); + /* we need to buffer the command */ + cmd.command = SME_QOS_MODIFY_REQ; + cmd.mac = mac; + cmd.sessionId = sessionId; + cmd.u.modifyCmdInfo.QosFlowID = QosFlowID; + cmd.u.modifyCmdInfo.QoSInfo = *pQoSInfo; + hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: couldn't buffer the modify request in state = %d", + __func__, __LINE__, pACInfo->curr_state); + return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Buffered modify request for flow = %d", + __func__, __LINE__, QosFlowID); + return SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; + } + /* get into the stat m/c to see if the request can be granted */ + switch (pACInfo->curr_state) { + case SME_QOS_QOS_ON: + /* save the new params adding a new (duplicate) entry in the + * Flow List Once we have decided on OTA exchange needed or + * not we can delete the original one from the List + */ + pNewEntry = qdf_mem_malloc(sizeof(*pNewEntry)); + if (!pNewEntry) + return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + + pNewEntry->ac_type = ac; + pNewEntry->sessionId = sessionId; + pNewEntry->HDDcontext = flow_info->HDDcontext; + pNewEntry->QoSCallback = flow_info->QoSCallback; + pNewEntry->QosFlowID = flow_info->QosFlowID; + pNewEntry->reason = SME_QOS_REASON_MODIFY_PENDING; + /* since it is a modify request, use the same index on which + * the flow entry originally was running & add it to the Flow + * List at the end + */ + pNewEntry->tspec_mask = flow_info->tspec_mask; + pNewEntry->QoSInfo = *pQoSInfo; + /* update the entry from Flow List which needed to be + * modified + */ + flow_info->reason = SME_QOS_REASON_MODIFY; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d creating modified entry at %pK with flowID %d", + __func__, __LINE__, + sessionId, pNewEntry, pNewEntry->QosFlowID); + /* add the new entry under construction to the Flow List */ + csr_ll_insert_tail(&sme_qos_cb.flow_list, &pNewEntry->link, + true); + /* update TSPEC with the new param set */ + hstatus = sme_qos_update_params(sessionId, + ac, pNewEntry->tspec_mask, + &Aggr_Tspec_Info); + if (QDF_IS_STATUS_SUCCESS(hstatus)) { + pACInfo->requested_QoSInfo[pNewEntry->tspec_mask - 1] = + Aggr_Tspec_Info; + /* if ACM, send out a new ADDTS */ + status = sme_qos_setup(mac, sessionId, + &pACInfo-> + requested_QoSInfo[pNewEntry-> + tspec_mask - 1], + ac); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d with AC %d in state SME_QOS_QOS_ON sme_qos_setup returned with status %d", + __func__, __LINE__, sessionId, ac, status); + + if (SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) { + new_state = SME_QOS_REQUESTED; + status = + SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; + pACInfo->tspec_pending = pNewEntry->tspec_mask; + } else + if ((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP + == status) + || + (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY + == status)) { + new_state = SME_QOS_QOS_ON; + + qdf_mem_zero(&search_key, + sizeof(struct sme_qos_searchinfo)); + /* delete the original entry in FLOW list which + * got modified + */ + search_key.key.ac_type = ac; + search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; + search_key.sessionId = sessionId; + hstatus = sme_qos_find_all_in_flow_list(mac, + search_key, + sme_qos_modify_fnp); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) + status = + SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + + if (SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP != + status) { + pACInfo->curr_QoSInfo[pNewEntry-> + tspec_mask - 1] = + pACInfo-> + requested_QoSInfo[pNewEntry-> + tspec_mask - 1]; + if (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status) { + status = + SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY; + qdf_mem_zero(&search_key, + sizeof + (struct sme_qos_searchinfo)); + search_key.key.ac_type = ac; + search_key.index = + SME_QOS_SEARCH_KEY_INDEX_2; + search_key.sessionId = + sessionId; + hstatus = + sme_qos_find_all_in_flow_list + (mac, search_key, + sme_qos_modification_notify_fnp); + if (!QDF_IS_STATUS_SUCCESS + (hstatus)) { + QDF_TRACE + (QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: couldn't notify other entries on this AC =%d", + __func__, __LINE__, + ac); + } + } else + if + (SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP + == status) + status = + SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP; + } + if (buffered_cmd) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info-> + HDDcontext, + &pACInfo-> + curr_QoSInfo + [pNewEntry-> + tspec_mask - 1], + status, + flow_info-> + QosFlowID); + } + + } else { + /* unexpected status returned by + * sme_qos_setup() + */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d unexpected status %d returned by sme_qos_setup", + __func__, __LINE__, sessionId, status); + new_state = SME_QOS_QOS_ON; + } + } else { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: sme_qos_update_params() failed", + __func__, __LINE__); + new_state = SME_QOS_LINK_UP; + } + /* if we are doing reassoc & we are already in handoff state, + * no need to move to requested state. But make sure to set + * the previous state as requested state + */ + if (!(pACInfo->reassoc_pending && + (SME_QOS_HANDOFF == pACInfo->curr_state))) + sme_qos_state_transition(sessionId, ac, new_state); + else + pACInfo->prev_state = SME_QOS_REQUESTED; + break; + case SME_QOS_HANDOFF: + case SME_QOS_REQUESTED: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Buffering modify request for flow %d in state = %d", + __func__, __LINE__, QosFlowID, pACInfo->curr_state); + /* buffer cmd */ + cmd.command = SME_QOS_MODIFY_REQ; + cmd.mac = mac; + cmd.sessionId = sessionId; + cmd.u.modifyCmdInfo.QosFlowID = QosFlowID; + cmd.u.modifyCmdInfo.QoSInfo = *pQoSInfo; + hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: couldn't buffer the modify request in state = %d", + __func__, __LINE__, pACInfo->curr_state); + return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + } + status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; + break; + case SME_QOS_CLOSED: + case SME_QOS_INIT: + case SME_QOS_LINK_UP: + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: modify requested in unexpected state = %d", + __func__, __LINE__, pACInfo->curr_state); + break; + } + if ((SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) + || (SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY == + status)) + (void)sme_qos_process_buffered_cmd(sessionId); + + return status; +} + +/** + * sme_qos_internal_release_req() - release QOS flow on a particular AC + * @mac: Pointer to the global MAC parameter structure. + * @sessionId: sessionId returned by sme_open_session. + * @QosFlowID: Identification per flow running on each AC generated by SME + * It is only meaningful if the QoS setup for the flow is successful + * + * The SME QoS internal function to request + * for releasing a QoS flow running on a particular AC. + + * Return: QDF_STATUS_SUCCESS - Release is successful. + */ +static enum sme_qos_statustype sme_qos_internal_release_req(struct mac_context *mac, + uint8_t sessionId, + uint32_t QosFlowID, + bool buffered_cmd) +{ + tListElem *pEntry = NULL; + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + struct sme_qos_flowinfoentry *flow_info = NULL; + struct sme_qos_flowinfoentry *pDeletedFlow = NULL; + enum qca_wlan_ac_type ac; + enum sme_qos_states new_state = SME_QOS_CLOSED; + enum sme_qos_statustype status = SME_QOS_STATUS_RELEASE_FAILURE_RSP; + struct sme_qos_wmmtspecinfo Aggr_Tspec_Info; + struct sme_qos_searchinfo search_key; + struct sme_qos_cmdinfo cmd; + tCsrRoamModifyProfileFields modifyProfileFields; + bool deltsIssued = false; + QDF_STATUS hstatus; + bool biDirectionalFlowsPresent = false; + bool uplinkFlowsPresent = false; + bool downlinkFlowsPresent = false; + tListElem *pResult = NULL; + mac_handle_t mac_hdl = MAC_HANDLE(mac); + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked for flow %d", __func__, __LINE__, QosFlowID); + + qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo)); + /* set the key type & the key to be searched in the Flow List */ + search_key.key.QosFlowID = QosFlowID; + search_key.index = SME_QOS_SEARCH_KEY_INDEX_1; + search_key.sessionId = SME_QOS_SEARCH_SESSION_ID_ANY; + /* go through the link list to find out the details on the flow */ + pEntry = sme_qos_find_in_flow_list(search_key); + + if (!pEntry) { + /* Err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: no match found for flowID = %d", + __func__, __LINE__, QosFlowID); + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + if (!buffered_cmd && + !csr_ll_is_list_empty(&pSession->bufferedCommandList, + false)) { + cmd.command = SME_QOS_RELEASE_REQ; + cmd.mac = mac; + cmd.sessionId = sessionId; + cmd.u.releaseCmdInfo.QosFlowID = QosFlowID; + hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd); + if (QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s:%d: Buffered release request for flow = %d", + __func__, __LINE__, QosFlowID); + } + } + return SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP; + } + /* find the AC */ + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link); + ac = flow_info->ac_type; + sessionId = flow_info->sessionId; + + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Session Id: %d is invalid", + __func__, __LINE__, sessionId); + return status; + } + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pACInfo = &pSession->ac_info[ac]; + /* check to consider the following flowing scenario. + * Addts request is pending on one AC, while APSD requested on another + * which needs a reassoc. Will buffer a request if Addts is pending on + * any AC, which will safegaurd the above scenario, & also won't + * confuse PE with back to back Addts or Addts followed by Reassoc + */ + if (sme_qos_is_rsp_pending(sessionId, ac)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: buffering the release request for flow %d in state %d since another request is pending", + __func__, __LINE__, QosFlowID, pACInfo->curr_state); + /* we need to buffer the command */ + cmd.command = SME_QOS_RELEASE_REQ; + cmd.mac = mac; + cmd.sessionId = sessionId; + cmd.u.releaseCmdInfo.QosFlowID = QosFlowID; + hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: couldn't buffer the release request in state = %d", + __func__, __LINE__, pACInfo->curr_state); + return SME_QOS_STATUS_RELEASE_FAILURE_RSP; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Buffered release request for flow = %d", + __func__, __LINE__, QosFlowID); + return SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP; + } + /* get into the stat m/c to see if the request can be granted */ + switch (pACInfo->curr_state) { + case SME_QOS_QOS_ON: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: tspec_mask_status = %d for AC = %d with entry tspec_mask = %d", + __func__, __LINE__, + pACInfo->tspec_mask_status, ac, + flow_info->tspec_mask); + + /* check if multiple flows running on the ac */ + if (pACInfo->num_flows[flow_info->tspec_mask - 1] > 1) { + /* don't want to include the flow in the new TSPEC on + * which release is requested + */ + flow_info->reason = SME_QOS_REASON_RELEASE; + + /* Check if the flow being released is for bi-diretional + * Following flows may present in the system. + * a) bi-directional flows + * b) uplink flows + * c) downlink flows. + * If the flow being released is for bidirectional, + * splitting of existing streams into two tspec indices + * is required in case ff (b), (c) are present and not + * (a). In case if split occurs, all upstreams are + * aggregated into tspec index 0, downstreams are + * aggregaed into tspec index 1 and two tspec requests + * for (aggregated) upstream(s) followed by (aggregated) + * downstream(s) is sent to AP. + */ + if (flow_info->QoSInfo.ts_info.direction == + SME_QOS_WMM_TS_DIR_BOTH) { + qdf_mem_zero(&search_key, + sizeof(struct sme_qos_searchinfo)); + /* set the key type & the key to be searched in + * the Flow List + */ + search_key.key.ac_type = ac; + search_key.index = SME_QOS_SEARCH_KEY_INDEX_4; + search_key.sessionId = sessionId; + search_key.direction = SME_QOS_WMM_TS_DIR_BOTH; + pResult = sme_qos_find_in_flow_list(search_key); + if (pResult) + biDirectionalFlowsPresent = true; + + if (!biDirectionalFlowsPresent) { + /* The only existing bidirectional flow + * is being released + */ + + /* Check if uplink flows exist */ + search_key.direction = + SME_QOS_WMM_TS_DIR_UPLINK; + pResult = + sme_qos_find_in_flow_list(search_key); + if (pResult) + uplinkFlowsPresent = true; + + /* Check if downlink flows exist */ + search_key.direction = + SME_QOS_WMM_TS_DIR_DOWNLINK; + pResult = + sme_qos_find_in_flow_list(search_key); + if (pResult) + downlinkFlowsPresent = true; + + if (uplinkFlowsPresent + && downlinkFlowsPresent) { + /* Need to split the uni- + * directional flows into + * SME_QOS_TSPEC_INDEX_0 and + * SME_QOS_TSPEC_INDEX_1 + */ + + qdf_mem_zero(&search_key, + sizeof + (struct sme_qos_searchinfo)); + /* Mark all downstream flows as + * using tspec index 1 + */ + search_key.key.ac_type = ac; + search_key.index = + SME_QOS_SEARCH_KEY_INDEX_4; + search_key.sessionId = + sessionId; + search_key.direction = + SME_QOS_WMM_TS_DIR_DOWNLINK; + sme_qos_update_tspec_mask + (sessionId, search_key, + SME_QOS_TSPEC_MASK_BIT_2_SET); + + /* Aggregate all downstream + * flows + */ + hstatus = + sme_qos_update_params + (sessionId, ac, + SME_QOS_TSPEC_MASK_BIT_2_SET, + &Aggr_Tspec_Info); + + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d buffering the AddTS request for AC %d in state %d as Addts is pending on other Tspec index of this AC", + __func__, __LINE__, + sessionId, ac, + pACInfo->curr_state); + + /* Buffer the (aggregated) tspec + * request for downstream flows. + * Please note that the + * (aggregated) tspec for + * upstream flows is sent out by + * the susequent logic. + */ + cmd.command = + SME_QOS_RESEND_REQ; + cmd.mac = mac; + cmd.sessionId = sessionId; + cmd.u.resendCmdInfo.ac = ac; + cmd.u.resendCmdInfo.tspecMask = + SME_QOS_TSPEC_MASK_BIT_2_SET; + cmd.u.resendCmdInfo.QoSInfo = + Aggr_Tspec_Info; + pACInfo-> + requested_QoSInfo + [SME_QOS_TSPEC_MASK_BIT_2_SET + - 1] = Aggr_Tspec_Info; + if (!QDF_IS_STATUS_SUCCESS + (sme_qos_buffer_cmd + (&cmd, + false))) { + QDF_TRACE + (QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d unable to buffer the AddTS request for AC %d TSPEC %d in state %d", + __func__, __LINE__, + sessionId, ac, + SME_QOS_TSPEC_MASK_BIT_2_SET, + pACInfo-> + curr_state); + + return + SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + } + pACInfo->tspec_mask_status = + SME_QOS_TSPEC_MASK_BIT_1_2_SET; + + } + } + } + + /* In case of splitting of existing streams, + * tspec_mask will be pointing to tspec index 0 and + * aggregated tspec for upstream(s) is sent out here. + */ + hstatus = sme_qos_update_params(sessionId, + ac, flow_info->tspec_mask, + &Aggr_Tspec_Info); + if (QDF_IS_STATUS_SUCCESS(hstatus)) { + pACInfo->requested_QoSInfo[flow_info-> + tspec_mask - 1] = + Aggr_Tspec_Info; + /* if ACM, send out a new ADDTS */ + status = sme_qos_setup(mac, sessionId, + &pACInfo-> + requested_QoSInfo + [flow_info->tspec_mask - + 1], ac); + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d with AC %d in state SME_QOS_QOS_ON sme_qos_setup returned with status %d", + __func__, __LINE__, sessionId, ac, + status); + + if (SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == + status) { + new_state = SME_QOS_REQUESTED; + status = + SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP; + pACInfo->tspec_pending = + flow_info->tspec_mask; + } else + if ((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status)) { + new_state = SME_QOS_QOS_ON; + pACInfo->num_flows[flow_info-> + tspec_mask - 1]--; + pACInfo->curr_QoSInfo[flow_info-> + tspec_mask - 1] = + pACInfo-> + requested_QoSInfo[flow_info-> + tspec_mask - 1]; + /* delete the entry from Flow List */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Deleting entry at %pK with flowID %d", + __func__, __LINE__, flow_info, + QosFlowID); + csr_ll_remove_entry(&sme_qos_cb. + flow_list, pEntry, true); + pDeletedFlow = flow_info; + if (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status) { + qdf_mem_zero(&search_key, + sizeof + (struct sme_qos_searchinfo)); + search_key.key.ac_type = ac; + search_key.index = + SME_QOS_SEARCH_KEY_INDEX_2; + search_key.sessionId = + sessionId; + hstatus = + sme_qos_find_all_in_flow_list + (mac, search_key, + sme_qos_setup_fnp); + if (!QDF_IS_STATUS_SUCCESS + (hstatus)) { + QDF_TRACE + (QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: couldn't notify other entries on this AC =%d", + __func__, __LINE__, + ac); + } + } + status = + SME_QOS_STATUS_RELEASE_SUCCESS_RSP; + if (buffered_cmd) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info-> + HDDcontext, + &pACInfo-> + curr_QoSInfo + [flow_info-> + tspec_mask - 1], + status, + flow_info-> + QosFlowID); + } + } else { + /* unexpected status returned by + * sme_qos_setup() + */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d unexpected status %d returned by sme_qos_setup", + __func__, __LINE__, sessionId, + status); + new_state = SME_QOS_LINK_UP; + pACInfo->num_flows[flow_info-> + tspec_mask - 1]--; + pACInfo->curr_QoSInfo[flow_info-> + tspec_mask - 1] = + pACInfo-> + requested_QoSInfo[flow_info-> + tspec_mask - 1]; + /* delete the entry from Flow List */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d deleting entry at %pK with flowID %d", + __func__, __LINE__, sessionId, + flow_info, QosFlowID); + csr_ll_remove_entry(&sme_qos_cb. + flow_list, + pEntry, true); + pDeletedFlow = flow_info; + if (buffered_cmd) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info-> + HDDcontext, + &pACInfo-> + curr_QoSInfo + [flow_info-> + tspec_mask - 1], + status, + flow_info-> + QosFlowID); + } + } + } else { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: sme_qos_update_params() failed", + __func__, __LINE__); + new_state = SME_QOS_LINK_UP; + if (buffered_cmd) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info-> + HDDcontext, + &pACInfo-> + curr_QoSInfo + [flow_info-> + tspec_mask - 1], + status, + flow_info-> + QosFlowID); + } + } + } else { + /* this is the only flow aggregated in this TSPEC */ + status = SME_QOS_STATUS_RELEASE_SUCCESS_RSP; + /* check if delts needs to be sent */ + if (CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(mac) || + sme_qos_is_acm(mac, pSession->assocInfo.bss_desc, + ac, NULL)) { + /* check if other TSPEC for this AC is also + * in use + */ + if (SME_QOS_TSPEC_MASK_BIT_1_2_SET != + pACInfo->tspec_mask_status) { + /* this is the only TSPEC active on this + * AC so indicate that we no longer + * require APSD + */ + pSession->apsdMask &= + ~(1 << (QCA_WLAN_AC_VO - ac)); + /* Also update modifyProfileFields. + * uapsd_mask in CSR for consistency + */ + csr_get_modify_profile_fields(mac, + flow_info-> + sessionId, + &modifyProfileFields); + modifyProfileFields.uapsd_mask = + pSession->apsdMask; + csr_set_modify_profile_fields(mac, + flow_info-> + sessionId, + &modifyProfileFields); + if (!pSession->apsdMask) { + /* this session no longer needs + * UAPSD do any sessions still + * require UAPSD? + */ + if (!sme_qos_is_uapsd_active()) + /* No sessions require + * UAPSD so turn it off + * (really don't care + * when PMC stops it) + */ + sme_ps_uapsd_disable( + mac_hdl, + sessionId); + } + } + if (SME_QOS_RELEASE_DEFAULT == + pACInfo->relTrig) { + /* send delts */ + hstatus = + qos_issue_command(mac, + sessionId, + eSmeCommandDelTs, + NULL, ac, + flow_info-> + tspec_mask); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: sme_qos_del_ts_req() failed", + __func__, __LINE__); + status = + SME_QOS_STATUS_RELEASE_FAILURE_RSP; + } else { + pACInfo->tspec_mask_status &= + SME_QOS_TSPEC_MASK_BIT_1_2_SET + & (~flow_info->tspec_mask); + deltsIssued = true; + } + } else { + pACInfo->tspec_mask_status &= + SME_QOS_TSPEC_MASK_BIT_1_2_SET & + (~flow_info->tspec_mask); + deltsIssued = true; + } + } else if (pSession->apsdMask & + (1 << (QCA_WLAN_AC_VO - ac))) { + /* reassoc logic */ + csr_get_modify_profile_fields(mac, sessionId, + &modifyProfileFields); + modifyProfileFields.uapsd_mask |= + pSession->apsdMask; + modifyProfileFields.uapsd_mask &= + ~(1 << (QCA_WLAN_AC_VO - ac)); + pSession->apsdMask &= + ~(1 << (QCA_WLAN_AC_VO - ac)); + if (!pSession->apsdMask) { + /* this session no longer needs UAPSD + * do any sessions still require UAPSD? + */ + if (!sme_qos_is_uapsd_active()) + /* No sessions require UAPSD so + * turn it off (really don't + * care when PMC stops it) + */ + sme_ps_uapsd_disable( + mac_hdl, sessionId); + } + hstatus = sme_qos_request_reassoc(mac, + sessionId, + &modifyProfileFields, + false); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: Reassoc failed", + __func__, __LINE__); + status = + SME_QOS_STATUS_RELEASE_FAILURE_RSP; + } else { + /* no need to wait */ + pACInfo->reassoc_pending = false; + pACInfo->prev_state = SME_QOS_LINK_UP; + pACInfo->tspec_pending = 0; + } + } else { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: nothing to do for AC = %d", + __func__, __LINE__, ac); + } + + if (SME_QOS_RELEASE_BY_AP == pACInfo->relTrig) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info->HDDcontext, + &pACInfo-> + curr_QoSInfo[flow_info-> + tspec_mask - + 1], + SME_QOS_STATUS_RELEASE_QOS_LOST_IND, + flow_info->QosFlowID); + + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Deleting entry at %pK with flowID %d", + __func__, __LINE__, flow_info, + flow_info->QosFlowID); + } else if (buffered_cmd) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info->HDDcontext, + NULL, status, + flow_info->QosFlowID); + } + + if (SME_QOS_STATUS_RELEASE_FAILURE_RSP == status) + break; + + if (((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~flow_info-> + tspec_mask) > 0) + && + ((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~flow_info-> + tspec_mask) <= SME_QOS_TSPEC_INDEX_MAX)) { + if (pACInfo-> + num_flows[(SME_QOS_TSPEC_MASK_BIT_1_2_SET & + ~flow_info->tspec_mask) - 1] > + 0) + new_state = SME_QOS_QOS_ON; + else + new_state = SME_QOS_LINK_UP; + } else { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Exceeded the array bounds of pACInfo->num_flows", + __func__, __LINE__); + return + SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP; + } + + if (false == deltsIssued) { + qdf_mem_zero(&pACInfo-> + curr_QoSInfo[flow_info-> + tspec_mask - 1], + sizeof(struct sme_qos_wmmtspecinfo)); + } + qdf_mem_zero(&pACInfo-> + requested_QoSInfo[flow_info->tspec_mask - + 1], + sizeof(struct sme_qos_wmmtspecinfo)); + pACInfo->num_flows[flow_info->tspec_mask - 1]--; + /* delete the entry from Flow List */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d deleting entry at %pK with flowID %d", + __func__, __LINE__, + sessionId, flow_info, QosFlowID); + csr_ll_remove_entry(&sme_qos_cb.flow_list, pEntry, + true); + pDeletedFlow = flow_info; + pACInfo->relTrig = SME_QOS_RELEASE_DEFAULT; + } + /* if we are doing reassoc & we are already in handoff state, no + * need to move to requested state. But make sure to set the + * previous state as requested state + */ + if (SME_QOS_HANDOFF != pACInfo->curr_state) + sme_qos_state_transition(sessionId, ac, new_state); + + if (pACInfo->reassoc_pending) + pACInfo->prev_state = SME_QOS_REQUESTED; + break; + case SME_QOS_HANDOFF: + case SME_QOS_REQUESTED: + /* buffer cmd */ + cmd.command = SME_QOS_RELEASE_REQ; + cmd.mac = mac; + cmd.sessionId = sessionId; + cmd.u.releaseCmdInfo.QosFlowID = QosFlowID; + hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: couldn't buffer the release request in state = %d", + __func__, __LINE__, pACInfo->curr_state); + return SME_QOS_STATUS_RELEASE_FAILURE_RSP; + } + status = SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP; + break; + case SME_QOS_CLOSED: + case SME_QOS_INIT: + case SME_QOS_LINK_UP: + default: + /* print error msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: release request in unexpected state = %d", + __func__, __LINE__, pACInfo->curr_state); + break; + } + /* if we deleted a flow, reclaim the memory */ + if (pDeletedFlow) + qdf_mem_free(pDeletedFlow); + + if (SME_QOS_STATUS_RELEASE_SUCCESS_RSP == status) + (void)sme_qos_process_buffered_cmd(sessionId); + + return status; +} + +/** + * sme_qos_setup() - internal SME QOS setup function. + * @mac: Pointer to the global MAC parameter structure. + * @sessionId: Session upon which setup is being performed + * @pTspec_Info: Pointer to struct sme_qos_wmmtspecinfo which contains the WMM + * TSPEC related info as defined above + * @ac: Enumeration of the various EDCA Access Categories. + * + * The internal qos setup function which has the intelligence + * if the request is NOP, or for APSD and/or need to send out ADDTS. + * It also does the sanity check for QAP, AP supports APSD etc. + * The logic used in the code might be confusing. + * + * Trying to cover all the cases here. + * AP supports App wants ACM = 1 Already set APSD Result + * | 0 | 0 | 0 | 0 | NO ACM NO APSD + * | 0 | 0 | 0 | 1 | NO ACM NO APSD/INVALID + * | 0 | 0 | 1 | 0 | ADDTS + * | 0 | 0 | 1 | 1 | ADDTS + * | 0 | 1 | 0 | 0 | FAILURE + * | 0 | 1 | 0 | 1 | INVALID + * | 0 | 1 | 1 | 0 | ADDTS + * | 0 | 1 | 1 | 1 | ADDTS + * | 1 | 0 | 0 | 0 | NO ACM NO APSD + * | 1 | 0 | 0 | 1 | NO ACM NO APSD + * | 1 | 0 | 1 | 0 | ADDTS + * | 1 | 0 | 1 | 1 | ADDTS + * | 1 | 1 | 0 | 0 | REASSOC + * | 1 | 1 | 0 | 1 | NOP: APSD SET ALREADY + * | 1 | 1 | 1 | 0 | ADDTS + * | 1 | 1 | 1 | 1 | ADDTS + * + * Return: SME_QOS_STATUS_SETUP_SUCCESS_RSP if the setup is successful' + */ +static enum sme_qos_statustype sme_qos_setup(struct mac_context *mac, + uint8_t sessionId, + struct sme_qos_wmmtspecinfo *pTspec_Info, + enum qca_wlan_ac_type ac) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + enum sme_qos_statustype status = SME_QOS_STATUS_SETUP_FAILURE_RSP; + tDot11fBeaconIEs *pIes = NULL; + tCsrRoamModifyProfileFields modifyProfileFields; + QDF_STATUS hstatus; + + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Session Id %d is invalid", + __func__, __LINE__, sessionId); + return status; + } + pSession = &sme_qos_cb.sessionInfo[sessionId]; + if (!pSession->sessionActive) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Session %d is inactive", + __func__, __LINE__, sessionId); + return status; + } + if (!pSession->assocInfo.bss_desc) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Session %d has an Invalid BSS Descriptor", + __func__, __LINE__, sessionId); + return status; + } + hstatus = csr_get_parsed_bss_description_ies(mac, + pSession->assocInfo.bss_desc, + &pIes); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d unable to parse BSS IEs", + __func__, __LINE__, sessionId); + return status; + } + + /* success so pIes was allocated */ + + if (!CSR_IS_QOS_BSS(pIes)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d AP doesn't support QoS", + __func__, __LINE__, sessionId); + qdf_mem_free(pIes); + /* notify HDD through the synchronous status msg */ + return SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP; + } + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: UAPSD/PSB set %d: ", __func__, __LINE__, + pTspec_Info->ts_info.psb); + + pACInfo = &pSession->ac_info[ac]; + do { + /* is ACM enabled for this AC? */ + if (CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(mac) || + sme_qos_is_acm(mac, pSession->assocInfo.bss_desc, + ac, NULL)) { + /* ACM is enabled for this AC so we must send an + * AddTS + */ + if (pTspec_Info->ts_info.psb && + !(pIes->WMMParams. + qosInfo & SME_QOS_AP_SUPPORTS_APSD) + && !(pIes->WMMInfoAp.uapsd)) { + /* application is looking for APSD but AP + * doesn't support it + */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d AP doesn't support APSD", + __func__, __LINE__, sessionId); + break; + } + + if (SME_QOS_MAX_TID == pTspec_Info->ts_info.tid) { + /* App didn't set TID, generate one */ + pTspec_Info->ts_info.tid = + (uint8_t) (SME_QOS_WMM_UP_NC - + pTspec_Info->ts_info.up); + } + /* addts logic */ + hstatus = + qos_issue_command(mac, sessionId, + eSmeCommandAddTs, + pTspec_Info, ac, 0); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: sme_qos_add_ts_req() failed", + __func__, __LINE__); + break; + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d AddTS on AC %d is pending", + __func__, __LINE__, sessionId, ac); + status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; + break; + } + /* ACM is not enabled for this AC */ + /* Is the application looking for APSD? */ + if (0 == pTspec_Info->ts_info.psb) { + /* no, we don't need APSD but check the case, if the + * setup is called as a result of a release or modify + * which boils down to the fact that APSD was set on + * this AC but no longer needed - so we need a reassoc + * for the above case to let the AP know + */ + if (pSession-> + apsdMask & (1 << (QCA_WLAN_AC_VO - ac))) { + /* APSD was formerly enabled on this AC but is + * no longer required so we must reassociate + */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d reassoc needed to disable APSD on AC %d", + __func__, __LINE__, sessionId, ac); + csr_get_modify_profile_fields(mac, sessionId, + &modifyProfileFields); + modifyProfileFields.uapsd_mask |= + pSession->apsdMask; + modifyProfileFields.uapsd_mask &= + ~(1 << (QCA_WLAN_AC_VO - ac)); + hstatus = + sme_qos_request_reassoc(mac, sessionId, + &modifyProfileFields, + false); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: Unable to request reassociation", + __func__, __LINE__); + break; + } else { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d reassociation to enable APSD on AC %d is pending", + __func__, __LINE__, sessionId, + ac); + status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; + pACInfo->reassoc_pending = true; + } + } else { + /* we don't need APSD on this AC and we don't + * currently have APSD on this AC + */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Request is not looking for APSD & Admission Control isn't mandatory for the AC", + __func__, __LINE__); + /* return success right away */ + status = + SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP; + } + break; + } else if (!(pIes->WMMParams.qosInfo & SME_QOS_AP_SUPPORTS_APSD) + && !(pIes->WMMInfoAp.uapsd)) { + /* application is looking for APSD but AP doesn't + * support it + */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d AP doesn't support APSD", + __func__, __LINE__, sessionId); + break; + } else if (pSession-> + apsdMask & (1 << (QCA_WLAN_AC_VO - ac))) { + /* application is looking for APSD */ + /* and it is already enabled on this AC */ + status = SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Request is looking for APSD and it is already set for the AC", + __func__, __LINE__); + break; + } else { + /* application is looking for APSD but it is not enabled on this + * AC so we need to reassociate + */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "On session %d reassoc needed to enable APSD on AC %d", + sessionId, ac); + /* reassoc logic */ + /* update the UAPSD mask to include the new */ + /* AC on which APSD is requested */ + csr_get_modify_profile_fields(mac, sessionId, + &modifyProfileFields); + modifyProfileFields.uapsd_mask |= + pSession->apsdMask; + modifyProfileFields.uapsd_mask |= + 1 << (QCA_WLAN_AC_VO - ac); + hstatus = sme_qos_request_reassoc(mac, sessionId, + &modifyProfileFields, + false); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Unable to request reassociation", + __func__, __LINE__); + break; + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "On session %d reassociation to enable APSD on AC %d is pending", + sessionId, ac); + status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; + pACInfo->reassoc_pending = true; + } + } + } while (0); + + qdf_mem_free(pIes); + return status; +} + +/* This is a dummy function now. But the purpose of me adding this was to + * delay the TSPEC processing till SET_KEY completes. This function can be + * used to do any SME_QOS processing after the SET_KEY. As of now, it is + * not required as we are ok with tspec getting programmed before set_key + * as the roam timings are measured without tspec in reassoc! + */ +static QDF_STATUS sme_qos_process_set_key_success_ind(struct mac_context *mac, + uint8_t sessionId, void *pEvent_info) +{ + sme_debug("Set Key complete"); + (void)sme_qos_process_buffered_cmd(sessionId); + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_ESE +/** + * sme_qos_ese_save_tspec_response() - save TSPEC parameters. + * @mac: Pointer to the global MAC parameter structure. + * @sessionId: SME session ID + * @pTspec: Pointer to the TSPEC IE from the reassoc rsp + * @ac: Access Category for which this TSPEC rsp is received + * @tspecIndex: flow/direction + * + * This function saves the TSPEC parameters that came along in the TSPEC IE + * in the reassoc response + * + * Return: QDF_STATUS_SUCCESS - Release is successful. + */ +static QDF_STATUS +sme_qos_ese_save_tspec_response(struct mac_context *mac, uint8_t sessionId, + tDot11fIEWMMTSPEC *pTspec, uint8_t ac, + uint8_t tspecIndex) +{ + tpSirAddtsRsp pAddtsRsp = + &sme_qos_cb.sessionInfo[sessionId].ac_info[ac]. + addTsRsp[tspecIndex]; + + ac = sme_qos_up_to_ac_map[pTspec->user_priority]; + + qdf_mem_zero(pAddtsRsp, sizeof(tSirAddtsRsp)); + + pAddtsRsp->messageType = eWNI_SME_ADDTS_RSP; + pAddtsRsp->length = sizeof(tSirAddtsRsp); + pAddtsRsp->rc = QDF_STATUS_SUCCESS; + pAddtsRsp->sessionId = sessionId; + pAddtsRsp->rsp.dialogToken = 0; + pAddtsRsp->rsp.status = eSIR_MAC_SUCCESS_STATUS; + pAddtsRsp->rsp.wmeTspecPresent = pTspec->present; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: Copy Tspec to local data structure ac=%d, tspecIdx=%d", + __func__, ac, tspecIndex); + + if (pAddtsRsp->rsp.wmeTspecPresent) + /* Copy TSPEC params received in assoc response to addts + * response + */ + convert_wmmtspec(mac, &pAddtsRsp->rsp.tspec, pTspec); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_ese_process_reassoc_tspec_rsp() - process ese reassoc tspec response + * @mac: Pointer to the global MAC parameter structure. + * @sessionId: SME session ID + * @pEven_info: Pointer to the smeJoinRsp structure + * + * This function processes the WMM TSPEC IE in the reassoc response. + * Reassoc triggered as part of ESE roaming to another ESE capable AP. + * If the TSPEC was added before reassoc, as part of Call Admission Control, + * the reasso req from the STA would carry the TSPEC parameters which were + * already negotiated with the older AP. + * + * Return: QDF_STATUS_SUCCESS - Release is successful. + */ +static +QDF_STATUS sme_qos_ese_process_reassoc_tspec_rsp(struct mac_context *mac, + uint8_t sessionId, + void *pEvent_info) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + tDot11fIEWMMTSPEC *pTspecIE = NULL; + struct csr_roam_session *pCsrSession = NULL; + struct csr_roam_connectedinfo *pCsrConnectedInfo = NULL; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint8_t ac, numTspec, cnt; + uint8_t tspec_flow_index, tspec_mask_status; + uint32_t tspecIeLen; + + pCsrSession = CSR_GET_SESSION(mac, sessionId); + if (!pCsrSession) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("session %d not found"), sessionId); + return QDF_STATUS_E_FAILURE; + } + pCsrConnectedInfo = &pCsrSession->connectedInfo; + pSession = &sme_qos_cb.sessionInfo[sessionId]; + + /* Get the TSPEC IEs which came along with the reassoc response */ + /* from the pbFrames pointer */ + pTspecIE = + (tDot11fIEWMMTSPEC *) (pCsrConnectedInfo->pbFrames + + pCsrConnectedInfo->nBeaconLength + + pCsrConnectedInfo->nAssocReqLength + + pCsrConnectedInfo->nAssocRspLength + + pCsrConnectedInfo->nRICRspLength); + + /* Get the number of tspecs Ies in the frame, the min length */ + /* should be atleast equal to the one TSPEC IE */ + tspecIeLen = pCsrConnectedInfo->nTspecIeLength; + if (tspecIeLen < sizeof(tDot11fIEWMMTSPEC)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("ESE Tspec IE len %d less than min %zu"), + tspecIeLen, sizeof(tDot11fIEWMMTSPEC)); + return QDF_STATUS_E_FAILURE; + } + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_WARN, + "TspecLen = %d, pbFrames = %pK, pTspecIE = %pK", + tspecIeLen, pCsrConnectedInfo->pbFrames, pTspecIE); + + numTspec = (tspecIeLen) / sizeof(tDot11fIEWMMTSPEC); + for (cnt = 0; cnt < numTspec; cnt++) { + ac = sme_qos_up_to_ac(pTspecIE->user_priority); + if (ac >= QCA_WLAN_AC_ALL) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("ac %d more than it`s max value"), ac); + return QDF_STATUS_E_FAILURE; + } + pACInfo = &pSession->ac_info[ac]; + tspec_mask_status = pACInfo->tspec_mask_status; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_WARN, + FL("UP=%d, ac=%d, tspec_mask_status=%x"), + pTspecIE->user_priority, ac, tspec_mask_status); + + for (tspec_flow_index = 0; + tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX; + tspec_flow_index++) { + if (tspec_mask_status & (1 << tspec_flow_index)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_WARN, + "Found Tspec entry flow = %d AC = %d", + tspec_flow_index, ac); + sme_qos_ese_save_tspec_response(mac, sessionId, + pTspecIE, ac, + tspec_flow_index); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_WARN, + "Not found Tspec entry flow = %d AC = %d", + tspec_flow_index, ac); + } + } + /* Increment the pointer to point it to the next TSPEC IE */ + pTspecIE++; + } + + /* Send the Aggregated QoS request to HAL */ + status = sme_qos_ft_aggr_qos_req(mac, sessionId); + + return status; +} + +/** + * sme_qos_copy_tspec_info() - copy tspec info. + * @mac: Pointer to the global MAC parameter structure. + * @pTspec_Info: source structure + * @pTspec: destination structure + * + * This function copies the existing TSPEC parameters from the source structure + * to the destination structure. + * + * Return: None + */ +static void sme_qos_copy_tspec_info(struct mac_context *mac, + struct sme_qos_wmmtspecinfo *pTspec_Info, + struct mac_tspec_ie *pTspec) +{ + /* As per WMM_AC_testplan_v0.39 Minimum Service Interval, Maximum + * Service Interval, Service Start Time, Suspension Interval and Delay + * Bound are all intended for HCCA operation and therefore must be set + * to zero + */ + pTspec->delayBound = pTspec_Info->delay_bound; + pTspec->inactInterval = pTspec_Info->inactivity_interval; + pTspec->length = SME_QOS_TSPEC_IE_LENGTH; + pTspec->maxBurstSz = pTspec_Info->max_burst_size; + pTspec->maxMsduSz = pTspec_Info->maximum_msdu_size; + pTspec->maxSvcInterval = pTspec_Info->max_service_interval; + pTspec->meanDataRate = pTspec_Info->mean_data_rate; + pTspec->mediumTime = pTspec_Info->medium_time; + pTspec->minDataRate = pTspec_Info->min_data_rate; + pTspec->minPhyRate = pTspec_Info->min_phy_rate; + pTspec->minSvcInterval = pTspec_Info->min_service_interval; + pTspec->nomMsduSz = pTspec_Info->nominal_msdu_size; + pTspec->peakDataRate = pTspec_Info->peak_data_rate; + pTspec->surplusBw = pTspec_Info->surplus_bw_allowance; + pTspec->suspendInterval = pTspec_Info->suspension_interval; + pTspec->svcStartTime = pTspec_Info->svc_start_time; + pTspec->tsinfo.traffic.direction = pTspec_Info->ts_info.direction; + + /* Make sure UAPSD is allowed */ + if (pTspec_Info->ts_info.psb) + pTspec->tsinfo.traffic.psb = pTspec_Info->ts_info.psb; + else { + pTspec->tsinfo.traffic.psb = 0; + pTspec_Info->ts_info.psb = 0; + } + pTspec->tsinfo.traffic.tsid = pTspec_Info->ts_info.tid; + pTspec->tsinfo.traffic.userPrio = pTspec_Info->ts_info.up; + pTspec->tsinfo.traffic.accessPolicy = SME_QOS_ACCESS_POLICY_EDCA; + pTspec->tsinfo.traffic.burstSizeDefn = + pTspec_Info->ts_info.burst_size_defn; + pTspec->tsinfo.traffic.ackPolicy = pTspec_Info->ts_info.ack_policy; + pTspec->type = SME_QOS_TSPEC_IE_TYPE; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: up = %d, tid = %d", + __func__, __LINE__, + pTspec_Info->ts_info.up, pTspec_Info->ts_info.tid); +} + +/** + * sme_qos_ese_retrieve_tspec_info() - retrieve tspec info. + * @mac: Pointer to the global MAC parameter structure. + * @sessionId: SME session ID + * @pTspecInfo: Pointer to the structure to carry back the TSPEC parameters + * + * This function is called by CSR when try to create reassoc request message to + * PE - csrSendSmeReassocReqMsg. This functions get the existing tspec + * parameters to be included in the reassoc request. + * + * Return: uint8_t - number of existing negotiated TSPECs + */ +uint8_t sme_qos_ese_retrieve_tspec_info(struct mac_context *mac_ctx, + uint8_t session_id, tTspecInfo *tspec_info) +{ + struct sme_qos_sessioninfo *session; + struct sme_qos_acinfo *ac_info; + uint8_t ac, num_tspec = 0; + tTspecInfo *dst_tspec = tspec_info; + uint8_t tspec_mask; + uint8_t tspec_pending; + + /* TODO: Check if TSPEC has already been established + * if not return + */ + session = &sme_qos_cb.sessionInfo[session_id]; + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + volatile uint8_t index = 0; + + ac_info = &session->ac_info[ac]; + tspec_pending = ac_info->tspec_pending; + tspec_mask = ac_info->tspec_mask_status; + do { + /* + * If a tspec status is pending, take + * requested_QoSInfo for RIC request, + * else use curr_QoSInfo for the + * RIC request + */ + if ((tspec_mask & SME_QOS_TSPEC_MASK_BIT_1_SET) + && (tspec_pending & + SME_QOS_TSPEC_MASK_BIT_1_SET)){ + sme_qos_copy_tspec_info(mac_ctx, + &ac_info->requested_QoSInfo[index], + &dst_tspec->tspec); + dst_tspec->valid = true; + num_tspec++; + dst_tspec++; + } else if ((tspec_mask & SME_QOS_TSPEC_MASK_BIT_1_SET) + && !(tspec_pending & + SME_QOS_TSPEC_MASK_BIT_1_SET)){ + sme_qos_copy_tspec_info(mac_ctx, + &ac_info->curr_QoSInfo[index], + &dst_tspec->tspec); + dst_tspec->valid = true; + num_tspec++; + dst_tspec++; + } + tspec_mask >>= 1; + tspec_pending >>= 1; + index++; + } while (tspec_mask); + } + return num_tspec; +} + +#endif + +static +QDF_STATUS sme_qos_create_tspec_ricie(struct mac_context *mac, + struct sme_qos_wmmtspecinfo *pTspec_Info, + uint8_t *pRICBuffer, uint32_t *pRICLength, + uint8_t *pRICIdentifier) +{ + tDot11fIERICDataDesc ricIE; + uint32_t nStatus; + + if (!pRICBuffer || !pRICIdentifier || pRICLength == + NULL) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("RIC data is NULL, %pK, %pK, %pK"), + pRICBuffer, pRICIdentifier, pRICLength); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_zero(&ricIE, sizeof(tDot11fIERICDataDesc)); + + ricIE.present = 1; + ricIE.RICData.present = 1; + ricIE.RICData.resourceDescCount = 1; + ricIE.RICData.statusCode = 0; + ricIE.RICData.Identifier = sme_qos_assign_dialog_token(); +#ifndef USE_80211_WMMTSPEC_FOR_RIC + ricIE.TSPEC.present = 1; + ricIE.TSPEC.delay_bound = pTspec_Info->delay_bound; + ricIE.TSPEC.inactivity_int = pTspec_Info->inactivity_interval; + ricIE.TSPEC.burst_size = pTspec_Info->max_burst_size; + ricIE.TSPEC.max_msdu_size = pTspec_Info->maximum_msdu_size; + ricIE.TSPEC.max_service_int = pTspec_Info->max_service_interval; + ricIE.TSPEC.mean_data_rate = pTspec_Info->mean_data_rate; + ricIE.TSPEC.medium_time = 0; + ricIE.TSPEC.min_data_rate = pTspec_Info->min_data_rate; + ricIE.TSPEC.min_phy_rate = pTspec_Info->min_phy_rate; + ricIE.TSPEC.min_service_int = pTspec_Info->min_service_interval; + ricIE.TSPEC.size = pTspec_Info->nominal_msdu_size; + ricIE.TSPEC.peak_data_rate = pTspec_Info->peak_data_rate; + ricIE.TSPEC.surplus_bw_allowance = pTspec_Info->surplus_bw_allowance; + ricIE.TSPEC.suspension_int = pTspec_Info->suspension_interval; + ricIE.TSPEC.service_start_time = pTspec_Info->svc_start_time; + ricIE.TSPEC.direction = pTspec_Info->ts_info.direction; + /* Make sure UAPSD is allowed */ + if (pTspec_Info->ts_info.psb) + ricIE.TSPEC.psb = pTspec_Info->ts_info.psb; + else + ricIE.TSPEC.psb = 0; + + ricIE.TSPEC.tsid = pTspec_Info->ts_info.tid; + ricIE.TSPEC.user_priority = pTspec_Info->ts_info.up; + ricIE.TSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA; + + *pRICIdentifier = ricIE.RICData.Identifier; + + nStatus = + dot11f_pack_ie_ric_data_desc(mac, &ricIE, pRICBuffer, + sizeof(ricIE), pRICLength); + if (DOT11F_FAILED(nStatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Packing of RIC Data of length %d failed with status %d", + *pRICLength, nStatus); + } +#else /* WMM TSPEC */ + /* As per WMM_AC_testplan_v0.39 Minimum Service Interval, Maximum + * Service Interval, Service Start Time, Suspension Interval and Delay + * Bound are all intended for HCCA operation and therefore must be set + * to zero + */ + ricIE.WMMTSPEC.present = 1; + ricIE.WMMTSPEC.version = 1; + ricIE.WMMTSPEC.delay_bound = pTspec_Info->delay_bound; + ricIE.WMMTSPEC.inactivity_int = pTspec_Info->inactivity_interval; + ricIE.WMMTSPEC.burst_size = pTspec_Info->max_burst_size; + ricIE.WMMTSPEC.max_msdu_size = pTspec_Info->maximum_msdu_size; + ricIE.WMMTSPEC.max_service_int = pTspec_Info->max_service_interval; + ricIE.WMMTSPEC.mean_data_rate = pTspec_Info->mean_data_rate; + ricIE.WMMTSPEC.medium_time = 0; + ricIE.WMMTSPEC.min_data_rate = pTspec_Info->min_data_rate; + ricIE.WMMTSPEC.min_phy_rate = pTspec_Info->min_phy_rate; + ricIE.WMMTSPEC.min_service_int = pTspec_Info->min_service_interval; + ricIE.WMMTSPEC.size = pTspec_Info->nominal_msdu_size; + ricIE.WMMTSPEC.peak_data_rate = pTspec_Info->peak_data_rate; + ricIE.WMMTSPEC.surplus_bw_allowance = pTspec_Info->surplus_bw_allowance; + ricIE.WMMTSPEC.suspension_int = pTspec_Info->suspension_interval; + ricIE.WMMTSPEC.service_start_time = pTspec_Info->svc_start_time; + ricIE.WMMTSPEC.direction = pTspec_Info->ts_info.direction; + /* Make sure UAPSD is allowed */ + if (pTspec_Info->ts_info.psb) + ricIE.WMMTSPEC.psb = pTspec_Info->ts_info.psb; + else + ricIE.WMMTSPEC.psb = 0; + + ricIE.WMMTSPEC.tsid = pTspec_Info->ts_info.tid; + ricIE.WMMTSPEC.user_priority = pTspec_Info->ts_info.up; + ricIE.WMMTSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA; + + nStatus = + dot11f_pack_ie_ric_data_desc(mac, &ricIE, pRICBuffer, + sizeof(ricIE), pRICLength); + if (DOT11F_FAILED(nStatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "Packing of RIC Data of length %d failed with status %d", + *pRICLength, nStatus); + } +#endif /* 80211_TSPEC */ + *pRICIdentifier = ricIE.RICData.Identifier; + return nStatus; +} +/** + * sme_qos_process_ft_reassoc_req_ev()- processes reassoc request + * + * @session_id: SME Session Id + * + * This function Process reassoc request related to QOS + * + * Return: QDF_STATUS enumeration value. + */ +static QDF_STATUS sme_qos_process_ft_reassoc_req_ev( + uint8_t sessionId) +{ + struct sme_qos_sessioninfo *session; + struct sme_qos_acinfo *ac_info; + uint8_t ac, qos_requested = false; + uint8_t tspec_index; + struct sme_qos_flowinfoentry *flow_info = NULL; + tListElem *entry = NULL; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Invoked on session %d"), sessionId); + + session = &sme_qos_cb.sessionInfo[sessionId]; + + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + ac_info = &session->ac_info[ac]; + qos_requested = false; + + for (tspec_index = 0; + tspec_index < SME_QOS_TSPEC_INDEX_MAX; + tspec_index++) { + /* + * Only in the below case, copy the AC's curr + * QoS Info to requested QoS info + */ + if ((ac_info->ricIdentifier[tspec_index] + && !ac_info->tspec_pending) + || (ac_info-> + tspec_mask_status & (1 << tspec_index))) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "Copying the currentQos to requestedQos for AC=%d, flow=%d", + ac, tspec_index); + + ac_info->requested_QoSInfo[tspec_index] = + ac_info->curr_QoSInfo[tspec_index]; + qdf_mem_zero( + &ac_info->curr_QoSInfo[tspec_index], + sizeof(struct sme_qos_wmmtspecinfo)); + qos_requested = true; + } + } + + /* + * Only if the tspec is required, transition the state to + * SME_QOS_REQUESTED for this AC + */ + if (qos_requested) { + switch (ac_info->curr_state) { + case SME_QOS_HANDOFF: + sme_qos_state_transition(sessionId, ac, + SME_QOS_REQUESTED); + break; + default: + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "FT Reassoc req event in unexpected state %d", + ac_info->curr_state); + } + } + } + + /* + * At this point of time, we are + * disconnected from the old AP, so it is safe + * to reset all these session variables + */ + session->apsdMask = 0; + + /* + * Now change reason and HO renewal of + * all the flow in this session only + */ + entry = csr_ll_peek_head(&sme_qos_cb.flow_list, false); + if (!entry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_WARN, + FL("Flow List empty, nothing to update")); + return QDF_STATUS_E_FAILURE; + } + + do { + flow_info = GET_BASE_ADDR(entry, struct sme_qos_flowinfoentry, + link); + if (sessionId == flow_info->sessionId) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Changing FlowID %d reason to SETUP and HO renewal to false", + flow_info->QosFlowID); + flow_info->reason = SME_QOS_REASON_SETUP; + flow_info->hoRenewal = true; + } + entry = csr_ll_next(&sme_qos_cb.flow_list, entry, false); + } while (entry); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_fill_aggr_info - fill QOS Aggregation info + * + * @ac_id - index to the AC + * @ts_id - index to TS for a given AC + * @direction - traffic direction + * @msg - QOS message + * @session - sme session information + * + * this is a helper function to populate aggregation information + * for QOS message. + * + * Return: None + */ +static void sme_qos_fill_aggr_info(int ac_id, int ts_id, + enum sme_qos_wmm_dir_type direction, + tSirAggrQosReq *msg, + struct sme_qos_sessioninfo *session) +{ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Found tspec entry AC=%d, flow=%d, direction = %d"), + ac_id, ts_id, direction); + + msg->aggrInfo.aggrAddTsInfo[ac_id].dialogToken = + sme_qos_assign_dialog_token(); + msg->aggrInfo.aggrAddTsInfo[ac_id].lleTspecPresent = + session->ac_info[ac_id].addTsRsp[ts_id].rsp.lleTspecPresent; + msg->aggrInfo.aggrAddTsInfo[ac_id].numTclas = + session->ac_info[ac_id].addTsRsp[ts_id].rsp.numTclas; + qdf_mem_copy(msg->aggrInfo.aggrAddTsInfo[ac_id].tclasInfo, + session->ac_info[ac_id].addTsRsp[ts_id].rsp.tclasInfo, + SIR_MAC_TCLASIE_MAXNUM); + msg->aggrInfo.aggrAddTsInfo[ac_id].tclasProc = + session->ac_info[ac_id].addTsRsp[ts_id].rsp.tclasProc; + msg->aggrInfo.aggrAddTsInfo[ac_id].tclasProcPresent = + session->ac_info[ac_id].addTsRsp[ts_id].rsp.tclasProcPresent; + msg->aggrInfo.aggrAddTsInfo[ac_id].tspec = + session->ac_info[ac_id].addTsRsp[ts_id].rsp.tspec; + msg->aggrInfo.aggrAddTsInfo[ac_id].wmeTspecPresent = + session->ac_info[ac_id].addTsRsp[ts_id].rsp.wmeTspecPresent; + msg->aggrInfo.aggrAddTsInfo[ac_id].wsmTspecPresent = + session->ac_info[ac_id].addTsRsp[ts_id].rsp.wsmTspecPresent; + msg->aggrInfo.tspecIdx |= (1 << ac_id); + + /* Mark the index for this AC as pending for response, which would be */ + /* used to validate the AddTS response from HAL->PE->SME */ + session->ac_info[ac_id].tspec_pending = (1 << ts_id); + +} + +/** + * sme_qos_ft_aggr_qos_req - send aggregated QOS request + * + * @mac_ctx - global MAC context + * @session_id - sme session Id + * + * This function is used to send aggregated QOS request to HAL. + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_ft_aggr_qos_req(struct mac_context *mac_ctx, uint8_t + session_id) +{ + tSirAggrQosReq *aggr_req = NULL; + struct sme_qos_sessioninfo *session; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + int i, j = 0; + uint8_t direction; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("invoked on session %d"), session_id); + + session = &sme_qos_cb.sessionInfo[session_id]; + + aggr_req = qdf_mem_malloc(sizeof(tSirAggrQosReq)); + if (!aggr_req) + return QDF_STATUS_E_NOMEM; + + aggr_req->messageType = eWNI_SME_FT_AGGR_QOS_REQ; + aggr_req->length = sizeof(tSirAggrQosReq); + aggr_req->sessionId = session_id; + aggr_req->timeout = 0; + aggr_req->rspReqd = true; + qdf_mem_copy(&aggr_req->bssid.bytes[0], + &session->assocInfo.bss_desc->bssId[0], + sizeof(struct qdf_mac_addr)); + + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + for (j = 0; j < SME_QOS_TSPEC_INDEX_MAX; j++) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "ac=%d, tspec_mask_staus=%x, tspec_index=%d", + i, session->ac_info[i].tspec_mask_status, j); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("direction = %d"), + session->ac_info[i].addTsRsp[j].rsp.tspec. + tsinfo.traffic.direction); + /* Check if any flow is active on this AC */ + if (!((session->ac_info[i].tspec_mask_status) & + (1 << j))) + continue; + + direction = session->ac_info[i].addTsRsp[j].rsp.tspec. + tsinfo.traffic.direction; + + if ((direction == SME_QOS_WMM_TS_DIR_UPLINK) || + (direction == SME_QOS_WMM_TS_DIR_BOTH)) + sme_qos_fill_aggr_info(i, j, direction, + aggr_req, session); + } + } + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Sending aggregated message to HAL 0x%x"), + aggr_req->aggrInfo.tspecIdx); + + if (QDF_IS_STATUS_SUCCESS(umac_send_mb_message_to_mac(aggr_req))) { + status = QDF_STATUS_SUCCESS; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_INFO_HIGH, + FL("sent down a AGGR QoS req to PE")); + } + + return status; +} + +static +QDF_STATUS sme_qos_process_ftric_response(struct mac_context *mac, + uint8_t sessionId, + tDot11fIERICDataDesc *pRicDataDesc, + uint8_t ac, uint8_t tspecIndex) +{ + uint8_t i = 0; + tpSirAddtsRsp pAddtsRsp = &sme_qos_cb.sessionInfo[sessionId]. + ac_info[ac].addTsRsp[tspecIndex]; + + qdf_mem_zero(pAddtsRsp, sizeof(tSirAddtsRsp)); + + pAddtsRsp->messageType = eWNI_SME_ADDTS_RSP; + pAddtsRsp->length = sizeof(tSirAddtsRsp); + pAddtsRsp->rc = pRicDataDesc->RICData.statusCode; + pAddtsRsp->sessionId = sessionId; + pAddtsRsp->rsp.dialogToken = pRicDataDesc->RICData.Identifier; + pAddtsRsp->rsp.status = pRicDataDesc->RICData.statusCode; + pAddtsRsp->rsp.wmeTspecPresent = pRicDataDesc->TSPEC.present; + if (pAddtsRsp->rsp.wmeTspecPresent) + /* Copy TSPEC params received in RIC response to addts + * response + */ + convert_tspec(mac, &pAddtsRsp->rsp.tspec, + &pRicDataDesc->TSPEC); + + pAddtsRsp->rsp.numTclas = pRicDataDesc->num_TCLAS; + if (pAddtsRsp->rsp.numTclas) { + for (i = 0; i < pAddtsRsp->rsp.numTclas; i++) + /* Copy TCLAS info per index to the addts response */ + convert_tclas(mac, &pAddtsRsp->rsp.tclasInfo[i], + &pRicDataDesc->TCLAS[i]); + } + + pAddtsRsp->rsp.tclasProcPresent = pRicDataDesc->TCLASSPROC.present; + if (pAddtsRsp->rsp.tclasProcPresent) + pAddtsRsp->rsp.tclasProc = pRicDataDesc->TCLASSPROC.processing; + + pAddtsRsp->rsp.schedulePresent = pRicDataDesc->Schedule.present; + if (pAddtsRsp->rsp.schedulePresent) { + /* Copy Schedule IE params to addts response */ + convert_schedule(mac, &pAddtsRsp->rsp.schedule, + &pRicDataDesc->Schedule); + } + /* Need to check the below portion is a part of WMM TSPEC */ + /* Process Delay element */ + if (pRicDataDesc->TSDelay.present) + convert_ts_delay(mac, &pAddtsRsp->rsp.delay, + &pRicDataDesc->TSDelay); + + /* Need to call for WMMTSPEC */ + if (pRicDataDesc->WMMTSPEC.present) + convert_wmmtspec(mac, &pAddtsRsp->rsp.tspec, + &pRicDataDesc->WMMTSPEC); + + /* return sme_qos_process_add_ts_rsp(mac, &addtsRsp); */ + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_process_aggr_qos_rsp - process qos aggregation response + * + * @mac_ctx - global mac context + * @msgbuf - SME message buffer + * + * this function process the QOS aggregation response received. + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_process_aggr_qos_rsp(struct mac_context *mac_ctx, + void *msgbuf) +{ + tpSirAggrQosRsp rsp = (tpSirAggrQosRsp) msgbuf; + tSirAddtsRsp addtsrsp; + QDF_STATUS status = QDF_STATUS_SUCCESS; + int i, j = 0; + uint8_t sessionid = rsp->sessionId; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Received AGGR_QOS resp from LIM")); + + /* Copy the updated response information for TSPEC of all the ACs */ + for (i = 0; i < QCA_WLAN_AC_ALL; i++) { + uint8_t tspec_mask_status = + sme_qos_cb.sessionInfo[sessionid].ac_info[i]. + tspec_mask_status; + for (j = 0; j < SME_QOS_TSPEC_INDEX_MAX; j++) { + uint8_t direction = + sme_qos_cb.sessionInfo[sessionid]. + ac_info[i].addTsRsp[j].rsp.tspec.tsinfo.traffic. + direction; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Addts rsp from LIM AC=%d, flow=%d dir=%d, tspecIdx=%x", + i, j, direction, rsp->aggrInfo.tspecIdx); + + /* Check if the direction is Uplink or bi-directional */ + if (!(((1 << i) & rsp->aggrInfo.tspecIdx) && + ((tspec_mask_status) & (1 << j)) && + ((direction == SME_QOS_WMM_TS_DIR_UPLINK) || + (direction == SME_QOS_WMM_TS_DIR_BOTH)))) { + continue; + } + addtsrsp = + sme_qos_cb.sessionInfo[sessionid].ac_info[i]. + addTsRsp[j]; + addtsrsp.rc = rsp->aggrInfo.aggrRsp[i].status; + addtsrsp.rsp.status = rsp->aggrInfo.aggrRsp[i].status; + addtsrsp.rsp.tspec = rsp->aggrInfo.aggrRsp[i].tspec; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Processing Addts rsp from LIM AC=%d, flow=%d", + i, j); + /* post ADD TS response for each */ + if (sme_qos_process_add_ts_rsp(mac_ctx, &addtsrsp) != + QDF_STATUS_SUCCESS) + status = QDF_STATUS_E_FAILURE; + } + } + return status; +} + +/** + * sme_qos_find_matching_tspec() - utility function to find matching tspec + * @mac_ctx: global MAC context + * @sessionid: session ID + * @ac: AC index + * @ac_info: Current AC info + * @ric_data_desc: pointer to ric data + * @ric_rsplen: pointer to ric response length + * + * This utility function is called by sme_qos_process_ft_reassoc_rsp_ev + * to find the matching tspec + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_find_matching_tspec(struct mac_context *mac_ctx, + uint8_t sessionid, uint8_t ac, struct sme_qos_acinfo *ac_info, + tDot11fIERICDataDesc *ric_data_desc, uint32_t *ric_rsplen) +{ + uint8_t tspec_flow_index; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("invoked on session %d"), sessionid); + + for (tspec_flow_index = 0; + tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX; tspec_flow_index++) { + /* + * Only in the below case, copy the AC's curr QoS Info + * to requested QoS info + */ + if (!ac_info->ricIdentifier[tspec_flow_index]) + continue; + + if (!*ric_rsplen) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "RIC Response not received for AC %d on TSPEC Index %d, RIC Req Identifier = %d", + ac, tspec_flow_index, + ac_info->ricIdentifier[tspec_flow_index]); + continue; + } + /* Now we got response for this identifier. Process it. */ + if (!ric_data_desc->present) + continue; + if (!ric_data_desc->RICData.present) + continue; + + if (ric_data_desc->RICData.Identifier != + ac_info->ricIdentifier[tspec_flow_index]) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "RIC response order not same as request sent. Request ID = %d, Response ID = %d", + ac_info->ricIdentifier[tspec_flow_index], + ric_data_desc->RICData.Identifier); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "Processing RIC Response for AC %d, TSPEC Flow index %d with RIC ID %d", + ac, tspec_flow_index, + ric_data_desc->RICData.Identifier); + status = sme_qos_process_ftric_response(mac_ctx, + sessionid, ric_data_desc, ac, + tspec_flow_index); + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "Failed with status %d for AC %d in TSPEC Flow index = %d", + status, ac, tspec_flow_index); + } + } + ric_data_desc++; + *ric_rsplen -= sizeof(tDot11fIERICDataDesc); + } + return status; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * sme_qos_find_matching_tspec_lfr3() - utility function to find matching tspec + * @mac_ctx: global MAC context + * @sessionid: session ID + * @ac: AC index + * @qos_session: QOS session + * @ric_data_desc: pointer to ric data + * @ric_rsplen: ric response length + * + * This utility function is called by sme_qos_process_ft_reassoc_rsp_ev + * to find the matching tspec while LFR3 is enabled. + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_find_matching_tspec_lfr3(struct mac_context *mac_ctx, + uint8_t sessionid, uint8_t ac, struct sme_qos_sessioninfo + *qos_session, + tDot11fIERICDataDesc *ric_data_desc, uint32_t ric_rsplen) +{ + struct sme_qos_acinfo *ac_info; + uint8_t tspec_flow_idx; + bool found = false; + enum sme_qos_wmm_dir_type direction, qos_dir; + uint8_t ac1; + tDot11fIERICDataDesc *ric_data = NULL; + uint32_t ric_len; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("invoked on session %d"), sessionid); + + if (ac == QCA_WLAN_AC_ALL) { + sme_err("Invalid AC %d", ac); + return QDF_STATUS_E_FAILURE; + } + ric_data = ric_data_desc; + ric_len = ric_rsplen; + ac_info = &qos_session->ac_info[ac]; + for (tspec_flow_idx = 0; tspec_flow_idx < SME_QOS_TSPEC_INDEX_MAX; + tspec_flow_idx++) { + if (!((qos_session->ac_info[ac].tspec_mask_status) & + (1 << tspec_flow_idx))) + goto sme_qos_next_ric; + qos_dir = + ac_info->requested_QoSInfo[tspec_flow_idx].ts_info.direction; + do { + ac1 = sme_qos_up_to_ac( + ric_data->WMMTSPEC.user_priority); + if (ac1 == QCA_WLAN_AC_ALL) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + FL("Invalid AC %d UP %d"), ac1, + ric_data->WMMTSPEC.user_priority); + break; + } + direction = ric_data->WMMTSPEC.direction; + if (ac == ac1 && direction == qos_dir) { + found = true; + status = sme_qos_process_ftric_response(mac_ctx, + sessionid, ric_data, ac, + tspec_flow_idx); + if (QDF_STATUS_SUCCESS != status) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "Failed with status %d for AC %d in TSPEC Flow index = %d", + status, ac, tspec_flow_idx); + } + break; + } + ric_data++; + ric_len -= sizeof(tDot11fIERICDataDesc); + } while (ric_len); +sme_qos_next_ric: + ric_data = ric_data_desc; + ric_len = ric_rsplen; + found = false; + } + + return status; +} +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + +static +QDF_STATUS sme_qos_process_ft_reassoc_rsp_ev(struct mac_context *mac_ctx, + uint8_t sessionid, void *event_info) +{ + struct sme_qos_sessioninfo *qos_session; + struct sme_qos_acinfo *ac_info; + uint8_t ac; + tDot11fIERICDataDesc *ric_data_desc = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct csr_roam_session *csr_session = CSR_GET_SESSION(mac_ctx, + sessionid); + struct csr_roam_connectedinfo *csr_conn_info = NULL; + uint32_t ric_rsplen; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + tDot11fIERICDataDesc *ric_data = NULL; + uint32_t ric_len; +#endif + + if (!csr_session) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("The Session pointer is NULL")); + return QDF_STATUS_E_FAILURE; + } + csr_conn_info = &csr_session->connectedInfo; + ric_rsplen = csr_conn_info->nRICRspLength; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("invoked on session %d"), sessionid); + + qos_session = &sme_qos_cb.sessionInfo[sessionid]; + + ric_data_desc = (tDot11fIERICDataDesc *) ((csr_conn_info->pbFrames) + + (csr_conn_info->nBeaconLength + + csr_conn_info->nAssocReqLength + + csr_conn_info->nAssocRspLength)); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (!csr_session->roam_synch_in_progress) { +#endif + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + ac_info = &qos_session->ac_info[ac]; + sme_qos_find_matching_tspec(mac_ctx, sessionid, ac, + ac_info, ric_data_desc, &ric_rsplen); + } + + if (ric_rsplen) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("RIC Resp still follows . Rem len = %d"), + ric_rsplen); + } +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR3-11r Compare RIC in Reassoc Resp to find matching tspec in host"); + ric_data = ric_data_desc; + ric_len = ric_rsplen; + if (ric_rsplen && ric_data_desc->present && + ric_data_desc->WMMTSPEC.present) { + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; + ac++) { + sme_qos_find_matching_tspec_lfr3(mac_ctx, + sessionid, ac, qos_session, ric_data, + ric_len); + } + } else + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "LFR3-11r ric_rsplen is zero or ric_data_desc is not present or wmmtspec is not present"); + } +#endif + + /* Send the Aggregated QoS request to HAL */ + status = sme_qos_ft_aggr_qos_req(mac_ctx, sessionid); + + return status; +} + +/** + * sme_qos_add_ts_req() - send ADDTS request. + * @mac: Pointer to the global MAC parameter structure. + * @sessionId: Session upon which the TSPEC should be added + * @pTspec_Info: Pointer to struct sme_qos_wmmtspecinfo which contains the WMM + * TSPEC related info as defined above + * @ac: Enumeration of the various EDCA Access Categories. + * + * This function is used to send down the ADDTS request with TSPEC params to PE + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_add_ts_req(struct mac_context *mac, + uint8_t sessionId, + struct sme_qos_wmmtspecinfo *pTspec_Info, + enum qca_wlan_ac_type ac) +{ + tSirAddtsReq *pMsg = NULL; + struct sme_qos_sessioninfo *pSession; + QDF_STATUS status = QDF_STATUS_E_FAILURE; +#ifdef FEATURE_WLAN_ESE + struct csr_roam_session *pCsrSession = CSR_GET_SESSION(mac, sessionId); +#endif +#ifdef FEATURE_WLAN_DIAG_SUPPORT + WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type); +#endif + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d for AC %d", + __func__, __LINE__, sessionId, ac); + if (sessionId >= WLAN_MAX_VDEVS) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: sessionId(%d) is invalid", + __func__, __LINE__, sessionId); + return QDF_STATUS_E_FAILURE; + } + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pMsg = qdf_mem_malloc(sizeof(tSirAddtsReq)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + + pMsg->messageType = eWNI_SME_ADDTS_REQ; + pMsg->length = sizeof(tSirAddtsReq); + pMsg->sessionId = sessionId; + pMsg->timeout = 0; + pMsg->rspReqd = true; + pMsg->req.dialogToken = sme_qos_assign_dialog_token(); + /* As per WMM_AC_testplan_v0.39 Minimum Service Interval, Maximum + * Service Interval, Service Start Time, Suspension Interval and Delay + * Bound are all intended for HCCA operation and therefore must be set + * to zero + */ + pMsg->req.tspec.delayBound = 0; + pMsg->req.tspec.inactInterval = pTspec_Info->inactivity_interval; + pMsg->req.tspec.length = SME_QOS_TSPEC_IE_LENGTH; + pMsg->req.tspec.maxBurstSz = pTspec_Info->max_burst_size; + pMsg->req.tspec.maxMsduSz = pTspec_Info->maximum_msdu_size; + pMsg->req.tspec.maxSvcInterval = pTspec_Info->max_service_interval; + pMsg->req.tspec.meanDataRate = pTspec_Info->mean_data_rate; + pMsg->req.tspec.mediumTime = pTspec_Info->medium_time; + pMsg->req.tspec.minDataRate = pTspec_Info->min_data_rate; + pMsg->req.tspec.minPhyRate = pTspec_Info->min_phy_rate; + pMsg->req.tspec.minSvcInterval = pTspec_Info->min_service_interval; + pMsg->req.tspec.nomMsduSz = pTspec_Info->nominal_msdu_size; + pMsg->req.tspec.peakDataRate = pTspec_Info->peak_data_rate; + pMsg->req.tspec.surplusBw = pTspec_Info->surplus_bw_allowance; + pMsg->req.tspec.suspendInterval = pTspec_Info->suspension_interval; + pMsg->req.tspec.svcStartTime = 0; + pMsg->req.tspec.tsinfo.traffic.direction = + pTspec_Info->ts_info.direction; + /* Make sure UAPSD is allowed */ + if (pTspec_Info->ts_info.psb) { + pMsg->req.tspec.tsinfo.traffic.psb = pTspec_Info->ts_info.psb; + } else { + pMsg->req.tspec.tsinfo.traffic.psb = 0; + pTspec_Info->ts_info.psb = 0; + } + pMsg->req.tspec.tsinfo.traffic.tsid = pTspec_Info->ts_info.tid; + pMsg->req.tspec.tsinfo.traffic.userPrio = pTspec_Info->ts_info.up; + pMsg->req.tspec.tsinfo.traffic.accessPolicy = + SME_QOS_ACCESS_POLICY_EDCA; + pMsg->req.tspec.tsinfo.traffic.burstSizeDefn = + pTspec_Info->ts_info.burst_size_defn; + pMsg->req.tspec.tsinfo.traffic.ackPolicy = + pTspec_Info->ts_info.ack_policy; + pMsg->req.tspec.type = SME_QOS_TSPEC_IE_TYPE; + /*Fill the BSSID pMsg->req.bssId */ + if (!pSession->assocInfo.bss_desc) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: BSS descriptor is NULL so we don't send request to PE", + __func__, __LINE__); + qdf_mem_free(pMsg); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(&pMsg->bssid.bytes[0], + &pSession->assocInfo.bss_desc->bssId[0], + sizeof(struct qdf_mac_addr)); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: up = %d, tid = %d", + __func__, __LINE__, + pTspec_Info->ts_info.up, pTspec_Info->ts_info.tid); +#ifdef FEATURE_WLAN_ESE + if (pCsrSession->connectedProfile.isESEAssoc) { + pMsg->req.tsrsIE.tsid = pTspec_Info->ts_info.up; + pMsg->req.tsrsPresent = 1; + } +#endif + if (QDF_IS_STATUS_SUCCESS(umac_send_mb_message_to_mac(pMsg))) { + status = QDF_STATUS_SUCCESS; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: sent down a ADDTS req to PE", + __func__, __LINE__); + /* event: EVENT_WLAN_QOS */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT + qos.eventId = SME_QOS_DIAG_ADDTS_REQ; + qos.reasonCode = SME_QOS_DIAG_USER_REQUESTED; + WLAN_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + } + return status; +} + +/* + * sme_qos_del_ts_req() - To send down the DELTS request with TSPEC params + * to PE + * + * mac - Pointer to the global MAC parameter structure. + * sessionId - Session from which the TSPEC should be deleted + * ac - Enumeration of the various EDCA Access Categories. + * tspec_mask - on which tspec per AC, the delts is requested + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_del_ts_req(struct mac_context *mac, + uint8_t sessionId, + enum qca_wlan_ac_type ac, uint8_t tspec_mask) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + tSirDeltsReq *pMsg; + struct sme_qos_wmmtspecinfo *pTspecInfo; + +#ifdef FEATURE_WLAN_DIAG_SUPPORT + WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type); +#endif + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d for AC %d", + __func__, __LINE__, sessionId, ac); + pMsg = qdf_mem_malloc(sizeof(tSirDeltsReq)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + + /* get pointer to the TSPEC being deleted */ + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pACInfo = &pSession->ac_info[ac]; + pTspecInfo = &pACInfo->curr_QoSInfo[tspec_mask - 1]; + pMsg->messageType = eWNI_SME_DELTS_REQ; + pMsg->length = sizeof(tSirDeltsReq); + pMsg->sessionId = sessionId; + pMsg->rspReqd = true; + pMsg->req.tspec.delayBound = pTspecInfo->delay_bound; + pMsg->req.tspec.inactInterval = pTspecInfo->inactivity_interval; + pMsg->req.tspec.length = SME_QOS_TSPEC_IE_LENGTH; + pMsg->req.tspec.maxBurstSz = pTspecInfo->max_burst_size; + pMsg->req.tspec.maxMsduSz = pTspecInfo->maximum_msdu_size; + pMsg->req.tspec.maxSvcInterval = pTspecInfo->max_service_interval; + pMsg->req.tspec.meanDataRate = pTspecInfo->mean_data_rate; + pMsg->req.tspec.mediumTime = pTspecInfo->medium_time; + pMsg->req.tspec.minDataRate = pTspecInfo->min_data_rate; + pMsg->req.tspec.minPhyRate = pTspecInfo->min_phy_rate; + pMsg->req.tspec.minSvcInterval = pTspecInfo->min_service_interval; + pMsg->req.tspec.nomMsduSz = pTspecInfo->nominal_msdu_size; + pMsg->req.tspec.peakDataRate = pTspecInfo->peak_data_rate; + pMsg->req.tspec.surplusBw = pTspecInfo->surplus_bw_allowance; + pMsg->req.tspec.suspendInterval = pTspecInfo->suspension_interval; + pMsg->req.tspec.svcStartTime = pTspecInfo->svc_start_time; + pMsg->req.tspec.tsinfo.traffic.direction = + pTspecInfo->ts_info.direction; + pMsg->req.tspec.tsinfo.traffic.psb = pTspecInfo->ts_info.psb; + pMsg->req.tspec.tsinfo.traffic.tsid = pTspecInfo->ts_info.tid; + pMsg->req.tspec.tsinfo.traffic.userPrio = pTspecInfo->ts_info.up; + pMsg->req.tspec.tsinfo.traffic.accessPolicy = + SME_QOS_ACCESS_POLICY_EDCA; + pMsg->req.tspec.tsinfo.traffic.burstSizeDefn = + pTspecInfo->ts_info.burst_size_defn; + pMsg->req.tspec.tsinfo.traffic.ackPolicy = + pTspecInfo->ts_info.ack_policy; + pMsg->req.tspec.type = SME_QOS_TSPEC_IE_TYPE; + /*Fill the BSSID pMsg->req.bssId */ + if (!pSession->assocInfo.bss_desc) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: BSS descriptor is NULL so we don't send request to PE", + __func__, __LINE__); + qdf_mem_free(pMsg); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(&pMsg->bssid.bytes[0], + &pSession->assocInfo.bss_desc->bssId[0], + sizeof(struct qdf_mac_addr)); + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: up = %d, tid = %d", + __func__, __LINE__, + pTspecInfo->ts_info.up, pTspecInfo->ts_info.tid); + qdf_mem_zero(&pACInfo->curr_QoSInfo[tspec_mask - 1], + sizeof(struct sme_qos_wmmtspecinfo)); + + if (!QDF_IS_STATUS_SUCCESS(umac_send_mb_message_to_mac(pMsg))) { + sme_err("DELTS req to PE failed"); + return QDF_STATUS_E_FAILURE; + } + + sme_debug("sent down a DELTS req to PE"); +#ifdef FEATURE_WLAN_DIAG_SUPPORT + qos.eventId = SME_QOS_DIAG_DELTS; + qos.reasonCode = SME_QOS_DIAG_USER_REQUESTED; + WLAN_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); +#endif + + sme_set_tspec_uapsd_mask_per_session(mac, &pMsg->req.tspec.tsinfo, + sessionId); + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_process_add_ts_rsp() - Function to process the + * eWNI_SME_ADDTS_RSP came from PE + * + * @mac - Pointer to the global MAC parameter structure. + * @msg_buf - Pointer to the msg buffer came from PE. + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_add_ts_rsp(struct mac_context *mac, + void *msg_buf) +{ + tpSirAddtsRsp paddts_rsp = (tpSirAddtsRsp) msg_buf; + struct sme_qos_sessioninfo *pSession; + uint8_t sessionId = paddts_rsp->sessionId; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + enum sme_qos_wmmuptype up = + (enum sme_qos_wmmuptype) + paddts_rsp->rsp.tspec.tsinfo.traffic.userPrio; + struct sme_qos_acinfo *pACInfo; + enum qca_wlan_ac_type ac; +#ifdef FEATURE_WLAN_DIAG_SUPPORT + WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type); +#endif + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d for UP %d", + __func__, __LINE__, sessionId, up); + + ac = sme_qos_up_to_ac(up); + if (QCA_WLAN_AC_ALL == ac) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid AC %d from UP %d", + __func__, __LINE__, ac, up); + + return QDF_STATUS_E_FAILURE; + } + pACInfo = &pSession->ac_info[ac]; + if (SME_QOS_HANDOFF == pACInfo->curr_state) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "ADDTS Rsp received for AC %d in HANDOFF State. Dropping", + ac); + return QDF_STATUS_SUCCESS; + } + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Invoked on session %d with return code %d", + __func__, __LINE__, sessionId, paddts_rsp->rc); + if (paddts_rsp->rc) { + /* event: EVENT_WLAN_QOS */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT + qos.eventId = SME_QOS_DIAG_ADDTS_RSP; + qos.reasonCode = SME_QOS_DIAG_ADDTS_REFUSED; + WLAN_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + status = + sme_qos_process_add_ts_failure_rsp(mac, sessionId, + &paddts_rsp->rsp); + } else { + status = + sme_qos_process_add_ts_success_rsp(mac, sessionId, + &paddts_rsp->rsp); + } + return status; +} + +/* + * sme_qos_process_del_ts_rsp() - Function to process the + * eWNI_SME_DELTS_RSP came from PE + * + * mac - Pointer to the global MAC parameter structure. + * msg_buf - Pointer to the msg buffer came from PE. + * + * Return QDF_STATUS + */ +static +QDF_STATUS sme_qos_process_del_ts_rsp(struct mac_context *mac, void *msg_buf) +{ + tpSirDeltsRsp pDeltsRsp = (tpSirDeltsRsp) msg_buf; + struct sme_qos_sessioninfo *pSession; + uint8_t sessionId = pDeltsRsp->sessionId; + + /* msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Invoked on session %d with return code %d", + __func__, __LINE__, sessionId, pDeltsRsp->rc); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + (void)sme_qos_process_buffered_cmd(sessionId); + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_process_del_ts_ind() - Function to process the + * eWNI_SME_DELTS_IND came from PE + * + * Since it's a DELTS indication from AP, will notify all the flows running on + * this AC about QoS release + * + * mac - Pointer to the global MAC parameter structure. + * msg_buf - Pointer to the msg buffer came from PE. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_del_ts_ind(struct mac_context *mac, + void *msg_buf) +{ + tpSirDeltsRsp pdeltsind = (tpSirDeltsRsp)msg_buf; + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + uint8_t sessionId = pdeltsind->sessionId; + enum qca_wlan_ac_type ac; + struct sme_qos_searchinfo search_key; + struct mac_ts_info *tsinfo; + enum sme_qos_wmmuptype up = + (enum sme_qos_wmmuptype) + pdeltsind->rsp.tspec.tsinfo.traffic.userPrio; +#ifdef FEATURE_WLAN_DIAG_SUPPORT + WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type); +#endif + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Invoked on session %d for UP %d", + __func__, __LINE__, sessionId, up); + tsinfo = &pdeltsind->rsp.tspec.tsinfo; + ac = sme_qos_up_to_ac(up); + if (QCA_WLAN_AC_ALL == ac) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid AC %d from UP %d", + __func__, __LINE__, ac, up); + return QDF_STATUS_E_FAILURE; + } + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pACInfo = &pSession->ac_info[ac]; + + qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo)); + /* set the key type & the key to be searched in the Flow List */ + search_key.key.ac_type = ac; + search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; + search_key.sessionId = sessionId; + /* find all Flows on the perticular AC & delete them, also send HDD + * indication through the callback it registered per request + */ + if (!QDF_IS_STATUS_SUCCESS + (sme_qos_find_all_in_flow_list(mac, search_key, + sme_qos_del_ts_ind_fnp))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: no match found for ac = %d", __func__, + __LINE__, search_key.key.ac_type); + return QDF_STATUS_E_FAILURE; + } + sme_set_tspec_uapsd_mask_per_session(mac, tsinfo, sessionId); +/* event: EVENT_WLAN_QOS */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT + qos.eventId = SME_QOS_DIAG_DELTS; + qos.reasonCode = SME_QOS_DIAG_DELTS_IND_FROM_AP; + WLAN_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_process_assoc_complete_ev() - Function to process the + * SME_QOS_CSR_ASSOC_COMPLETE event indication from CSR + * + * pEvent_info - Pointer to relevant info from CSR. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_assoc_complete_ev(struct mac_context *mac, uint8_t + sessionId, void *pEvent_info) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + enum qca_wlan_ac_type ac = QCA_WLAN_AC_BE; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d", + __func__, __LINE__, sessionId); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + if (((SME_QOS_INIT == pSession->ac_info[QCA_WLAN_AC_BE].curr_state) + && (SME_QOS_INIT == + pSession->ac_info[QCA_WLAN_AC_BK].curr_state) + && (SME_QOS_INIT == + pSession->ac_info[QCA_WLAN_AC_VI].curr_state) + && (SME_QOS_INIT == + pSession->ac_info[QCA_WLAN_AC_VO].curr_state)) + || (pSession->handoffRequested)) { + /* get the association info */ + if (!pEvent_info) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: pEvent_info is NULL", + __func__, __LINE__); + return status; + } + if (!((sme_QosAssocInfo *)pEvent_info)->bss_desc) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: bss_desc is NULL", + __func__, __LINE__); + return status; + } + if ((pSession->assocInfo.bss_desc) && + (csr_is_bssid_match + ((struct qdf_mac_addr *) + &pSession->assocInfo.bss_desc->bssId, + (struct qdf_mac_addr *) &(((sme_QosAssocInfo *) + pEvent_info)->bss_desc->bssId)))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: assoc with the same BSS, no update needed", + __func__, __LINE__); + } else + status = sme_qos_save_assoc_info(pSession, pEvent_info); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: wrong state: BE %d, BK %d, VI %d, VO %d", + __func__, __LINE__, + pSession->ac_info[QCA_WLAN_AC_BE].curr_state, + pSession->ac_info[QCA_WLAN_AC_BK].curr_state, + pSession->ac_info[QCA_WLAN_AC_VI].curr_state, + pSession->ac_info[QCA_WLAN_AC_VO].curr_state); + return status; + } + /* the session is active */ + pSession->sessionActive = true; + if (pSession->handoffRequested) { + pSession->handoffRequested = false; + /* renew all flows */ + (void)sme_qos_process_buffered_cmd(sessionId); + status = QDF_STATUS_SUCCESS; + } else { + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + pACInfo = &pSession->ac_info[ac]; + switch (pACInfo->curr_state) { + case SME_QOS_INIT: + sme_qos_state_transition(sessionId, ac, + SME_QOS_LINK_UP); + break; + case SME_QOS_LINK_UP: + case SME_QOS_REQUESTED: + case SME_QOS_QOS_ON: + case SME_QOS_HANDOFF: + case SME_QOS_CLOSED: + default: + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d AC %d is in wrong state %d", + __func__, __LINE__, sessionId, ac, + pACInfo->curr_state); + break; + } + } + } + return status; +} + +/* + * sme_qos_process_reassoc_req_ev() - Function to process the + * SME_QOS_CSR_REASSOC_REQ event indication from CSR + * + * pEvent_info - Pointer to relevant info from CSR. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_reassoc_req_ev(struct mac_context *mac, uint8_t + sessionId, void *pEvent_info) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + enum qca_wlan_ac_type ac; + struct sme_qos_flowinfoentry *flow_info = NULL; + tListElem *entry = NULL; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: %d: invoked on session %d", + __func__, __LINE__, sessionId); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + + if (pSession->ftHandoffInProgress) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: %d: no need for state transition, should " + "already be in handoff state", __func__, __LINE__); + if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("curr_state is not HANDOFF, session %d"), + sessionId); + return QDF_STATUS_E_FAILURE; + } + sme_qos_process_ft_reassoc_req_ev(sessionId); + return QDF_STATUS_SUCCESS; + } + + if (pSession->handoffRequested) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: no need for state transition, should already be in handoff state", + __func__, __LINE__); + + if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("curr_state is not HANDOFF, session %d"), + sessionId); + return QDF_STATUS_E_FAILURE; + } + + /* + * Now change reason and HO renewal of + * all the flow in this session only + */ + entry = csr_ll_peek_head(&sme_qos_cb.flow_list, false); + if (!entry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Flow List empty, nothing to update")); + return QDF_STATUS_E_FAILURE; + } + + do { + flow_info = GET_BASE_ADDR(entry, struct sme_qos_flowinfoentry, + link); + if (sessionId == flow_info->sessionId) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_INFO_HIGH, + FL("Changing FlowID %d reason to" + " SETUP and HO renewal to true"), + flow_info->QosFlowID); + flow_info->reason = SME_QOS_REASON_SETUP; + flow_info->hoRenewal = true; + } + entry = csr_ll_next(&sme_qos_cb.flow_list, entry, + false); + } while (entry); + + /* buffer the existing flows to be renewed after handoff is + * done + */ + sme_qos_buffer_existing_flows(mac, sessionId); + /* clean up the control block partially for handoff */ + sme_qos_cleanup_ctrl_blk_for_handoff(mac, sessionId); + return QDF_STATUS_SUCCESS; + } +/* TBH: Assuming both handoff algo & 11r willn't be enabled at the same time */ + if (pSession->ftHandoffInProgress) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: no need for state transition, should already be in handoff state", + __func__, __LINE__); + + if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("curr_state is not HANDOFF, session %d"), + sessionId); + return QDF_STATUS_E_FAILURE; + } + + sme_qos_process_ft_reassoc_req_ev(sessionId); + return QDF_STATUS_SUCCESS; + } + + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + pACInfo = &pSession->ac_info[ac]; + switch (pACInfo->curr_state) { + case SME_QOS_LINK_UP: + case SME_QOS_REQUESTED: + case SME_QOS_QOS_ON: + sme_qos_state_transition(sessionId, ac, + SME_QOS_HANDOFF); + break; + case SME_QOS_HANDOFF: + /* This is normal because sme_qos_request_reassoc may + * already change the state + */ + break; + case SME_QOS_CLOSED: + case SME_QOS_INIT: + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d AC %d is in wrong state %d", + __func__, __LINE__, + sessionId, ac, pACInfo->curr_state); + break; + } + } + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_handle_handoff_state() - utility function called by + * sme_qos_process_reassoc_success_ev + * @mac_ctx: global MAC context + * @qos_session: QOS session + * @ac_info: AC information + * @ac: current AC index + * @sessionid: session id + * + * This function is called by sme_qos_process_reassoc_success_ev + * to update the state machine on the reception of reassoc success + * notificaiton + * + * Return: QDF_STATUS + */ +static +QDF_STATUS sme_qos_handle_handoff_state(struct mac_context *mac_ctx, + struct sme_qos_sessioninfo *qos_session, + struct sme_qos_acinfo *ac_info, + enum qca_wlan_ac_type ac, uint8_t sessionid) + +{ + struct sme_qos_searchinfo search_key; + struct sme_qos_searchinfo search_key1; + enum qca_wlan_ac_type ac_index; + tListElem *list_elt = NULL; + struct sme_qos_flowinfoentry *flow_info = NULL; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + /* return to our previous state */ + sme_qos_state_transition(sessionid, ac, ac_info->prev_state); + /* for which ac APSD (hence the reassoc) is requested */ + if (!ac_info->reassoc_pending) + return QDF_STATUS_SUCCESS; + + /* + * update the apsd mask in CB - make sure to take care of the + * case where we are resetting the bit in apsd_mask + */ + if (ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].ts_info.psb) + qos_session->apsdMask |= 1 << (QCA_WLAN_AC_VO - ac); + else + qos_session->apsdMask &= ~(1 << (QCA_WLAN_AC_VO - ac)); + + ac_info->reassoc_pending = false; + /* + * during setup it gets set as addts & reassoc both gets a + * pending flag ac_info->tspec_pending = 0; + */ + sme_qos_state_transition(sessionid, ac, SME_QOS_QOS_ON); + /* notify HDD with new Service Interval */ + ac_info->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0] = + ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0]; + qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo)); + /* set the key type & the key to be searched in the Flow List */ + search_key.key.ac_type = ac; + search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; + search_key.sessionId = sessionid; + /* notify PMC that reassoc is done for APSD on certain AC?? */ + + qdf_mem_zero(&search_key1, sizeof(struct sme_qos_searchinfo)); + /* set the hoRenewal field in control block if needed */ + search_key1.index = SME_QOS_SEARCH_KEY_INDEX_3; + search_key1.key.reason = SME_QOS_REASON_SETUP; + search_key1.sessionId = sessionid; + for (ac_index = QCA_WLAN_AC_BE; ac_index < QCA_WLAN_AC_ALL; + ac_index++) { + list_elt = sme_qos_find_in_flow_list(search_key1); + if (list_elt) { + flow_info = GET_BASE_ADDR(list_elt, + struct sme_qos_flowinfoentry, link); + if (flow_info->ac_type == ac) { + ac_info->hoRenewal = flow_info->hoRenewal; + break; + } + } + } + /* + * notify HDD the success for the requested flow notify all the + * other flows running on the AC that QoS got modified + */ + status = sme_qos_find_all_in_flow_list(mac_ctx, search_key, + sme_qos_reassoc_success_ev_fnp); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("no match found for ac = %d"), + search_key.key.ac_type); + return QDF_STATUS_E_FAILURE; + } + ac_info->hoRenewal = false; + qdf_mem_zero(&ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0], + sizeof(struct sme_qos_wmmtspecinfo)); + + return status; +} + +/** + * sme_qos_process_reassoc_success_ev() - process SME_QOS_CSR_REASSOC_COMPLETE + * + * @mac_ctx: global MAC context + * @sessionid: session ID + * @event_info: event buffer from CSR + * + * Function to process the SME_QOS_CSR_REASSOC_COMPLETE event indication + * from CSR + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_process_reassoc_success_ev(struct mac_context *mac_ctx, + uint8_t sessionid, void *event_info) +{ + + struct csr_roam_session *csr_roam_session = NULL; + struct sme_qos_sessioninfo *qos_session; + struct sme_qos_acinfo *ac_info; + enum qca_wlan_ac_type ac; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("invoked on session %d"), sessionid); + + if (sessionid >= WLAN_MAX_VDEVS) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("invoked on session %d"), sessionid); + return status; + } + + csr_roam_session = CSR_GET_SESSION(mac_ctx, sessionid); + + qos_session = &sme_qos_cb.sessionInfo[sessionid]; + + /* get the association info */ + if (!event_info) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("event_info is NULL")); + return status; + } + + if (!((sme_QosAssocInfo *)event_info)->bss_desc) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("bss_desc is NULL")); + return status; + } + status = sme_qos_save_assoc_info(qos_session, event_info); + if (status) + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_qos_save_assoc_info() failed")); + + /* + * Assuming both handoff algo & 11r willn't be enabled + * at the same time + */ + if (qos_session->handoffRequested) { + qos_session->handoffRequested = false; + /* renew all flows */ + (void)sme_qos_process_buffered_cmd(sessionid); + return QDF_STATUS_SUCCESS; + } + if (qos_session->ftHandoffInProgress) { + if (csr_roam_is11r_assoc(mac_ctx, sessionid)) { + if (csr_roam_session && + csr_roam_session->connectedInfo.nRICRspLength) { + status = sme_qos_process_ft_reassoc_rsp_ev( + mac_ctx, sessionid, + event_info); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, FL( + "session or RIC data is not present")); + } + } +#ifdef FEATURE_WLAN_ESE + /* + * If ESE association check for TSPEC IEs in the + * reassoc rsp frame + */ + if (csr_roam_is_ese_assoc(mac_ctx, sessionid)) { + if (csr_roam_session && + csr_roam_session->connectedInfo.nTspecIeLength) { + status = sme_qos_ese_process_reassoc_tspec_rsp( + mac_ctx, sessionid, event_info); + } + } +#endif + qos_session->ftHandoffInProgress = false; + qos_session->handoffRequested = false; + return status; + } + + qos_session->sessionActive = true; + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + ac_info = &qos_session->ac_info[ac]; + switch (ac_info->curr_state) { + case SME_QOS_HANDOFF: + status = sme_qos_handle_handoff_state(mac_ctx, + qos_session, ac_info, ac, sessionid); + break; + case SME_QOS_INIT: + case SME_QOS_CLOSED: + /* NOP */ + status = QDF_STATUS_SUCCESS; + break; + case SME_QOS_LINK_UP: + case SME_QOS_REQUESTED: + case SME_QOS_QOS_ON: + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("session %d AC %d is in wrong state %d"), + sessionid, ac, ac_info->curr_state); + break; + } + } + (void)sme_qos_process_buffered_cmd(sessionid); + return status; +} + +/* + * sme_qos_process_reassoc_failure_ev() - Function to process the + * SME_QOS_CSR_REASSOC_FAILURE event indication from CSR + * + * pEvent_info - Pointer to relevant info from CSR. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_reassoc_failure_ev(struct mac_context *mac, + uint8_t sessionId, void *pEvent_info) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + enum qca_wlan_ac_type ac; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d", + __func__, __LINE__, sessionId); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + pACInfo = &pSession->ac_info[ac]; + switch (pACInfo->curr_state) { + case SME_QOS_HANDOFF: + sme_qos_state_transition(sessionId, ac, SME_QOS_INIT); + if (pACInfo->reassoc_pending) + pACInfo->reassoc_pending = false; + + qdf_mem_zero(&pACInfo-> + curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], + sizeof(struct sme_qos_wmmtspecinfo)); + qdf_mem_zero(&pACInfo-> + requested_QoSInfo[SME_QOS_TSPEC_INDEX_0], + sizeof(struct sme_qos_wmmtspecinfo)); + qdf_mem_zero(&pACInfo-> + curr_QoSInfo[SME_QOS_TSPEC_INDEX_1], + sizeof(struct sme_qos_wmmtspecinfo)); + qdf_mem_zero(&pACInfo-> + requested_QoSInfo[SME_QOS_TSPEC_INDEX_1], + sizeof(struct sme_qos_wmmtspecinfo)); + pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_CLEAR; + pACInfo->tspec_pending = 0; + pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0] = 0; + pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] = 0; + break; + case SME_QOS_INIT: + case SME_QOS_CLOSED: + /* NOP */ + break; + case SME_QOS_LINK_UP: + case SME_QOS_REQUESTED: + case SME_QOS_QOS_ON: + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d AC %d is in wrong state %d", + __func__, __LINE__, + sessionId, ac, pACInfo->curr_state); + break; + } + } + /* need to clean up flows */ + sme_qos_delete_existing_flows(mac, sessionId); + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +#ifdef FEATURE_WLAN_ESE +static bool sme_qos_ft_handoff_required(struct mac_context *mac, + uint8_t session_id) +{ + struct csr_roam_session *csr_roam_session; + + if (csr_roam_is11r_assoc(mac, session_id)) + return true; + + csr_roam_session = CSR_GET_SESSION(mac, session_id); + + if (csr_roam_session && + csr_roam_session->roam_synch_in_progress && + csr_roam_is_ese_assoc(mac, session_id) && + csr_roam_session->connectedInfo.nTspecIeLength) + return true; + + return false; +} +#else +static inline bool sme_qos_ft_handoff_required(struct mac_context *mac, + uint8_t session_id) +{ + return csr_roam_is11r_assoc(mac, session_id) ? true : false; +} +#endif +#else +static inline bool sme_qos_ft_handoff_required(struct mac_context *mac, + uint8_t session_id) +{ + return false; +} +#endif + +#ifdef FEATURE_WLAN_ESE +static inline bool sme_qos_legacy_handoff_required(struct mac_context *mac, + uint8_t session_id) +{ + return csr_roam_is_ese_assoc(mac, session_id) ? false : true; +} +#else +static inline bool sme_qos_legacy_handoff_required(struct mac_context *mac, + uint8_t session_id) +{ + return true; +} +#endif + +/* + * sme_qos_process_handoff_assoc_req_ev() - Function to process the + * SME_QOS_CSR_HANDOFF_ASSOC_REQ event indication from CSR + * + * pEvent_info - Pointer to relevant info from CSR. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_handoff_assoc_req_ev(struct mac_context *mac, + uint8_t sessionId, void *pEvent_info) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + uint8_t ac; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d", + __func__, __LINE__, sessionId); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + pACInfo = &pSession->ac_info[ac]; + switch (pACInfo->curr_state) { + case SME_QOS_LINK_UP: + case SME_QOS_REQUESTED: + case SME_QOS_QOS_ON: + sme_qos_state_transition(sessionId, ac, + SME_QOS_HANDOFF); + break; + case SME_QOS_HANDOFF: + /* print error msg */ + if (pSession->ftHandoffInProgress) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: SME_QOS_CSR_HANDOFF_ASSOC_REQ received in SME_QOS_HANDOFF state with FT in progress", + __func__, __LINE__); + break; + } + + case SME_QOS_CLOSED: + case SME_QOS_INIT: + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d AC %d is in wrong state %d", + __func__, __LINE__, + sessionId, ac, pACInfo->curr_state); + break; + } + } + + if (sme_qos_ft_handoff_required(mac, sessionId)) + pSession->ftHandoffInProgress = true; + + /* If FT handoff/ESE in progress, legacy handoff need not be enabled */ + if (!pSession->ftHandoffInProgress && + sme_qos_legacy_handoff_required(mac, sessionId)) + pSession->handoffRequested = true; + + /* this session no longer needs UAPSD */ + pSession->apsdMask = 0; + /* do any sessions still require UAPSD? */ + sme_ps_uapsd_disable(MAC_HANDLE(mac), sessionId); + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_process_handoff_success_ev() - Function to process the + * SME_QOS_CSR_HANDOFF_COMPLETE event indication from CSR + * + * pEvent_info - Pointer to relevant info from CSR. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_handoff_success_ev(struct mac_context *mac, + uint8_t sessionId, void *pEvent_info) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + uint8_t ac; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d", + __func__, __LINE__, sessionId); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + /* go back to original state before handoff */ + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + pACInfo = &pSession->ac_info[ac]; + switch (pACInfo->curr_state) { + case SME_QOS_HANDOFF: + sme_qos_state_transition(sessionId, ac, + pACInfo->prev_state); + /* we will retry for the requested flow(s) with the + * new AP + */ + if (SME_QOS_REQUESTED == pACInfo->curr_state) + pACInfo->curr_state = SME_QOS_LINK_UP; + + status = QDF_STATUS_SUCCESS; + break; + /* FT logic, has already moved it to QOS_REQUESTED state during + * the reassoc request event, which would include the Qos + * (TSPEC) params in the reassoc req frame + */ + case SME_QOS_REQUESTED: + break; + case SME_QOS_INIT: + case SME_QOS_CLOSED: + case SME_QOS_LINK_UP: + case SME_QOS_QOS_ON: + default: +/* In case of 11r - RIC, we request QoS and Hand-off at the same time hence the + * state may be SME_QOS_REQUESTED + */ + if (pSession->ftHandoffInProgress) + break; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d AC %d is in wrong state %d", + __func__, __LINE__, + sessionId, ac, pACInfo->curr_state); + break; + } + } + return status; +} + +/* + * sme_qos_process_disconnect_ev() - Function to process the + * SME_QOS_CSR_DISCONNECT_REQ or SME_QOS_CSR_DISCONNECT_IND event indication + * from CSR + * + * pEvent_info - Pointer to relevant info from CSR. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_disconnect_ev(struct mac_context *mac, uint8_t + sessionId, void *pEvent_info) +{ + struct sme_qos_sessioninfo *pSession; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d", + __func__, __LINE__, sessionId); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + /* + * In case of 11r - RIC, we request QoS and Hand-off at the + * same time hence the state may be SME_QOS_REQUESTED + */ + if ((pSession->handoffRequested) + && !pSession->ftHandoffInProgress) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: no need for state transition, should already be in handoff state", + __func__, __LINE__); + if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("curr_state is not HANDOFF, session %d"), + sessionId); + return QDF_STATUS_SUCCESS; + } + + return QDF_STATUS_SUCCESS; + } + sme_qos_init_a_cs(mac, sessionId); + /* this session doesn't require UAPSD */ + pSession->apsdMask = 0; + + sme_ps_uapsd_disable(MAC_HANDLE(mac), sessionId); + + pSession->handoffRequested = false; + pSession->roamID = 0; + /* need to clean up buffered req */ + sme_qos_delete_buffered_requests(mac, sessionId); + /* need to clean up flows */ + sme_qos_delete_existing_flows(mac, sessionId); + /* clean up the assoc info */ + if (pSession->assocInfo.bss_desc) { + qdf_mem_free(pSession->assocInfo.bss_desc); + pSession->assocInfo.bss_desc = NULL; + } + sme_qos_cb.sessionInfo[sessionId].sessionActive = false; + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_process_join_req_ev() - Function to process the + * SME_QOS_CSR_JOIN_REQ event indication from CSR + * + * pEvent_info - Pointer to relevant info from CSR. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_join_req_ev(struct mac_context *mac, uint8_t + sessionId, void *pEvent_info) +{ + struct sme_qos_sessioninfo *pSession; + enum qca_wlan_ac_type ac; + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + if (pSession->handoffRequested) { + sme_debug("No need for state transition, should already be in handoff state"); + if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || + (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) + sme_err("curr_state is not HANDOFF, session %d", + sessionId); + /* buffer the existing flows to be renewed after handoff is + * done + */ + sme_qos_buffer_existing_flows(mac, sessionId); + /* clean up the control block partially for handoff */ + sme_qos_cleanup_ctrl_blk_for_handoff(mac, sessionId); + return QDF_STATUS_SUCCESS; + } + + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) + sme_qos_state_transition(sessionId, ac, SME_QOS_INIT); + + /* clean up the assoc info if already set */ + if (pSession->assocInfo.bss_desc) { + qdf_mem_free(pSession->assocInfo.bss_desc); + pSession->assocInfo.bss_desc = NULL; + } + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_process_preauth_success_ind() - process preauth success indication + * @mac_ctx: global MAC context + * @sessionid: session ID + * @event_info: event buffer + * + * Function to process the SME_QOS_CSR_PREAUTH_SUCCESS_IND event indication + * from CSR + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_process_preauth_success_ind(struct mac_context *mac_ctx, + uint8_t sessionid, void *event_info) +{ + struct sme_qos_sessioninfo *qos_session; + struct csr_roam_session *sme_session = CSR_GET_SESSION(mac_ctx, + sessionid); + struct sme_qos_acinfo *ac_info; + uint8_t ac; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint16_t ric_offset = 0; + uint32_t ric_ielen = 0; + uint8_t *ric_ie; + uint8_t tspec_mask_status = 0; + uint8_t tspec_pending_status = 0; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("invoked on SME session %d"), sessionid); + + if (!sme_session) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_session is NULL")); + return QDF_STATUS_E_INVAL; + } + + qos_session = &sme_qos_cb.sessionInfo[sessionid]; + + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + ac_info = &qos_session->ac_info[ac]; + + switch (ac_info->curr_state) { + case SME_QOS_LINK_UP: + case SME_QOS_REQUESTED: + case SME_QOS_QOS_ON: + sme_qos_state_transition(sessionid, ac, SME_QOS_HANDOFF); + break; + case SME_QOS_HANDOFF: + /* print error msg */ + case SME_QOS_CLOSED: + case SME_QOS_INIT: + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Session %d AC %d is in wrong state %d"), + sessionid, ac, ac_info->curr_state); + break; + } + } + + qos_session->ftHandoffInProgress = true; + + /* Check if its a 11R roaming before preparing the RIC IEs */ + if (!csr_roam_is11r_assoc(mac_ctx, sessionid)) + return status; + + /* Data is accessed from saved PreAuth Rsp */ + if (!sme_session->ftSmeContext.psavedFTPreAuthRsp) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("psavedFTPreAuthRsp is NULL")); + return QDF_STATUS_E_INVAL; + } + + /* + * Any Block Ack info there, should have been already filled by PE and + * present in this buffer and the ric_ies_length should contain the + * length of the whole RIC IEs. Filling of TSPEC info should start + * from this length + */ + ric_ie = sme_session->ftSmeContext.psavedFTPreAuthRsp->ric_ies; + ric_offset = + sme_session->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length; + + /* + * Now we have to process the currentTspeInfo inside this session and + * create the RIC IEs + */ + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + volatile uint8_t tspec_idx = 0; + + ric_ielen = 0; + ac_info = &qos_session->ac_info[ac]; + tspec_pending_status = ac_info->tspec_pending; + tspec_mask_status = ac_info->tspec_mask_status; + qdf_mem_zero(ac_info->ricIdentifier, SME_QOS_TSPEC_INDEX_MAX); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("AC %d ==> TSPEC status = %d, tspec pending = %d"), + ac, tspec_mask_status, tspec_pending_status); + + do { + if (!(tspec_mask_status & 0x1)) + goto add_next_ric; + + /* + * If a tspec status is pending, take requested_QoSInfo + * for RIC request, else use curr_QoSInfo for the + * RIC request + */ + if (tspec_pending_status & 0x1) { + status = sme_qos_create_tspec_ricie(mac_ctx, + &ac_info->requested_QoSInfo[tspec_idx], + ric_ie + ric_offset, &ric_ielen, + &ac_info->ricIdentifier[tspec_idx]); + } else { + status = sme_qos_create_tspec_ricie(mac_ctx, + &ac_info->curr_QoSInfo[tspec_idx], + ric_ie + ric_offset, &ric_ielen, + &ac_info->ricIdentifier[tspec_idx]); + } +add_next_ric: + ric_offset += ric_ielen; + sme_session->ftSmeContext.psavedFTPreAuthRsp-> + ric_ies_length += ric_ielen; + tspec_mask_status >>= 1; + tspec_pending_status >>= 1; + tspec_idx++; + } while (tspec_mask_status); + } + return status; +} + +/* + * sme_qos_process_add_ts_failure_rsp() - Function to process the + * Addts request failure response came from PE + * + * We will notify HDD only for the requested Flow, other Flows running on the AC + * stay intact + * + * mac - Pointer to the global MAC parameter structure. + * pRsp - Pointer to the addts response structure came from PE. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_add_ts_failure_rsp(struct mac_context *mac, + uint8_t sessionId, + tSirAddtsRspInfo *pRsp) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + enum qca_wlan_ac_type ac; + struct sme_qos_searchinfo search_key; + uint8_t tspec_pending; + enum sme_qos_wmmuptype up = + (enum sme_qos_wmmuptype) pRsp->tspec.tsinfo.traffic.userPrio; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d for UP %d", __func__, __LINE__, + sessionId, up); + ac = sme_qos_up_to_ac(up); + if (QCA_WLAN_AC_ALL == ac) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid AC %d from UP %d", + __func__, __LINE__, ac, up); + return QDF_STATUS_E_FAILURE; + } + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pACInfo = &pSession->ac_info[ac]; + /* is there a TSPEC request pending on this AC? */ + tspec_pending = pACInfo->tspec_pending; + if (!tspec_pending) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d an AddTS is not pending on AC %d", + __func__, __LINE__, sessionId, ac); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo)); + /* set the key type & the key to be searched in the Flow List */ + search_key.key.ac_type = ac; + search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; + search_key.sessionId = sessionId; + if (!QDF_IS_STATUS_SUCCESS + (sme_qos_find_all_in_flow_list + (mac, search_key, sme_qos_add_ts_failure_fnp))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d no match found for ac = %d", + __func__, __LINE__, sessionId, + search_key.key.ac_type); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_zero(&pACInfo->requested_QoSInfo[tspec_pending - 1], + sizeof(struct sme_qos_wmmtspecinfo)); + + if ((!pACInfo->num_flows[0]) && (!pACInfo->num_flows[1])) { + pACInfo->tspec_mask_status &= SME_QOS_TSPEC_MASK_BIT_1_2_SET & + (~pACInfo->tspec_pending); + sme_qos_state_transition(sessionId, ac, SME_QOS_LINK_UP); + } else + sme_qos_state_transition(sessionId, ac, SME_QOS_QOS_ON); + + pACInfo->tspec_pending = 0; + + (void)sme_qos_process_buffered_cmd(sessionId); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_update_tspec_mask() - Utility function to update the tspec. + * @sessionid: Session upon which the TSPEC is being updated + * @search_key: search key + * @new_tspec_mask: tspec to be set for this AC + * + * Typical usage while aggregating unidirectional flows into a bi-directional + * flow on AC which is running multiple flows + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_update_tspec_mask(uint8_t sessionid, + struct sme_qos_searchinfo + search_key, + uint8_t new_tspec_mask) +{ + tListElem *list_elt = NULL, *list_next_elt = NULL; + struct sme_qos_flowinfoentry *flow_info = NULL; + struct sme_qos_sessioninfo *qos_session; + struct sme_qos_acinfo *ac_info; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("invoked on session %d for AC %d TSPEC %d"), + sessionid, search_key.key.ac_type, new_tspec_mask); + + qos_session = &sme_qos_cb.sessionInfo[sessionid]; + + if (search_key.key.ac_type < QCA_WLAN_AC_ALL) { + ac_info = &qos_session->ac_info[search_key.key.ac_type]; + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Exceeded the array bounds")); + return QDF_STATUS_E_FAILURE; + } + + list_elt = csr_ll_peek_head(&sme_qos_cb.flow_list, false); + if (!list_elt) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Flow List empty, nothing to update")); + return QDF_STATUS_E_FAILURE; + } + + while (list_elt) { + list_next_elt = csr_ll_next(&sme_qos_cb.flow_list, list_elt, + false); + flow_info = GET_BASE_ADDR(list_elt, struct + sme_qos_flowinfoentry, link); + + if (search_key.sessionId != flow_info->sessionId) { + list_elt = list_next_elt; + continue; + } + + if (search_key.index & SME_QOS_SEARCH_KEY_INDEX_4) { + if ((search_key.key.ac_type == flow_info->ac_type) && + (search_key.direction == + flow_info->QoSInfo.ts_info.direction)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("Flow %d matches"), flow_info->QosFlowID); + ac_info->num_flows[flow_info->tspec_mask - 1]--; + ac_info->num_flows[new_tspec_mask - 1]++; + flow_info->tspec_mask = new_tspec_mask; + } + } else if (search_key.index & SME_QOS_SEARCH_KEY_INDEX_5) { + if ((search_key.key.ac_type == flow_info->ac_type) && + (search_key.tspec_mask == flow_info->tspec_mask)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("Flow %d matches"), flow_info->QosFlowID); + ac_info->num_flows[flow_info->tspec_mask - 1]--; + ac_info->num_flows[new_tspec_mask - 1]++; + flow_info->tspec_mask = new_tspec_mask; + } + } + list_elt = list_next_elt; + } + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_process_add_ts_success_rsp() - Function to process the + * Addts request success response came from PE + * + * We will notify HDD with addts success for the requested Flow, & for other + * Flows running on the AC we will send an addts modify status + * + * mac - Pointer to the global MAC parameter structure. + * pRsp - Pointer to the addts response structure came from PE. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_process_add_ts_success_rsp(struct mac_context *mac, + uint8_t sessionId, + tSirAddtsRspInfo *pRsp) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + enum qca_wlan_ac_type ac, ac_index; + struct sme_qos_searchinfo search_key; + struct sme_qos_searchinfo search_key1; + struct csr_roam_session *csr_session; + uint8_t tspec_pending; + tListElem *pEntry = NULL; + struct sme_qos_flowinfoentry *flow_info = NULL; + enum sme_qos_wmmuptype up = + (enum sme_qos_wmmuptype) pRsp->tspec.tsinfo.traffic.userPrio; +#ifdef FEATURE_WLAN_DIAG_SUPPORT + WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type); + host_log_qos_tspec_pkt_type *log_ptr = NULL; +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d for UP %d", + __func__, __LINE__, sessionId, up); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + ac = sme_qos_up_to_ac(up); + if (QCA_WLAN_AC_ALL == ac) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid AC %d from UP %d", + __func__, __LINE__, ac, up); + return QDF_STATUS_E_FAILURE; + } + pACInfo = &pSession->ac_info[ac]; + /* is there a TSPEC request pending on this AC? */ + tspec_pending = pACInfo->tspec_pending; + if (!tspec_pending) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d an AddTS is not pending on AC %d", + __func__, __LINE__, sessionId, ac); + return QDF_STATUS_E_FAILURE; + } + /* App is looking for APSD or the App which was looking for APSD has + * been released, so STA re-negotiated with AP + */ + if (pACInfo->requested_QoSInfo[tspec_pending - 1].ts_info.psb) { + /* update the session's apsd mask */ + pSession->apsdMask |= 1 << (QCA_WLAN_AC_VO - ac); + } else { + if (((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~tspec_pending) > 0) && + ((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~tspec_pending) <= + SME_QOS_TSPEC_INDEX_MAX)) { + if (!pACInfo->requested_QoSInfo + [(SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~tspec_pending) - + 1].ts_info.psb) + /* update the session's apsd mask */ + pSession->apsdMask &= + ~(1 << (QCA_WLAN_AC_VO - ac)); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Exceeded the array bounds of pACInfo->requested_QosInfo", + __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + } + + pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.burst_size_defn = + pRsp->tspec.tsinfo.traffic.burstSizeDefn; + pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.ack_policy = + pRsp->tspec.tsinfo.traffic.ackPolicy; + pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.up = + pRsp->tspec.tsinfo.traffic.userPrio; + pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.psb = + pRsp->tspec.tsinfo.traffic.psb; + pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.direction = + pRsp->tspec.tsinfo.traffic.direction; + pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.tid = + pRsp->tspec.tsinfo.traffic.tsid; + pACInfo->curr_QoSInfo[tspec_pending - 1].nominal_msdu_size = + pRsp->tspec.nomMsduSz; + pACInfo->curr_QoSInfo[tspec_pending - 1].maximum_msdu_size = + pRsp->tspec.maxMsduSz; + pACInfo->curr_QoSInfo[tspec_pending - 1].min_service_interval = + pRsp->tspec.minSvcInterval; + pACInfo->curr_QoSInfo[tspec_pending - 1].max_service_interval = + pRsp->tspec.maxSvcInterval; + pACInfo->curr_QoSInfo[tspec_pending - 1].inactivity_interval = + pRsp->tspec.inactInterval; + pACInfo->curr_QoSInfo[tspec_pending - 1].suspension_interval = + pRsp->tspec.suspendInterval; + pACInfo->curr_QoSInfo[tspec_pending - 1].svc_start_time = + pRsp->tspec.svcStartTime; + pACInfo->curr_QoSInfo[tspec_pending - 1].min_data_rate = + pRsp->tspec.minDataRate; + pACInfo->curr_QoSInfo[tspec_pending - 1].mean_data_rate = + pRsp->tspec.meanDataRate; + pACInfo->curr_QoSInfo[tspec_pending - 1].peak_data_rate = + pRsp->tspec.peakDataRate; + pACInfo->curr_QoSInfo[tspec_pending - 1].max_burst_size = + pRsp->tspec.maxBurstSz; + pACInfo->curr_QoSInfo[tspec_pending - 1].delay_bound = + pRsp->tspec.delayBound; + + pACInfo->curr_QoSInfo[tspec_pending - 1].min_phy_rate = + pRsp->tspec.minPhyRate; + pACInfo->curr_QoSInfo[tspec_pending - 1].surplus_bw_allowance = + pRsp->tspec.surplusBw; + pACInfo->curr_QoSInfo[tspec_pending - 1].medium_time = + pRsp->tspec.mediumTime; + + sme_set_tspec_uapsd_mask_per_session(mac, + &pRsp->tspec.tsinfo, sessionId); + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: On session %d AddTspec Medium Time %d", + __func__, __LINE__, sessionId, pRsp->tspec.mediumTime); + + /* Check if the current flow is for bi-directional. If so, update the + * number of flows to reflect that all flows are aggregated into tspec + * index 0. + */ + if ((pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1].ts_info. + direction == SME_QOS_WMM_TS_DIR_BOTH) + && (pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] > 0)) { + qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo)); + /* update tspec_mask for all the flows having + * SME_QOS_TSPEC_MASK_BIT_2_SET to SME_QOS_TSPEC_MASK_BIT_1_SET + */ + search_key.key.ac_type = ac; + search_key.index = SME_QOS_SEARCH_KEY_INDEX_5; + search_key.sessionId = sessionId; + search_key.tspec_mask = SME_QOS_TSPEC_MASK_BIT_2_SET; + sme_qos_update_tspec_mask(sessionId, search_key, + SME_QOS_TSPEC_MASK_BIT_1_SET); + } + + qdf_mem_zero(&search_key1, sizeof(struct sme_qos_searchinfo)); + /* set the horenewal field in control block if needed */ + search_key1.index = SME_QOS_SEARCH_KEY_INDEX_3; + search_key1.key.reason = SME_QOS_REASON_SETUP; + search_key1.sessionId = sessionId; + for (ac_index = QCA_WLAN_AC_BE; ac_index < QCA_WLAN_AC_ALL; + ac_index++) { + pEntry = sme_qos_find_in_flow_list(search_key1); + if (pEntry) { + flow_info = GET_BASE_ADDR(pEntry, + struct sme_qos_flowinfoentry, link); + if (flow_info->ac_type == ac) { + pACInfo->hoRenewal = flow_info->hoRenewal; + break; + } + } + } + qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo)); + /* set the key type & the key to be searched in the Flow List */ + search_key.key.ac_type = ac; + search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; + search_key.sessionId = sessionId; + /* notify HDD the success for the requested flow */ + /* notify all the other flows running on the AC that QoS got modified */ + if (!QDF_IS_STATUS_SUCCESS + (sme_qos_find_all_in_flow_list + (mac, search_key, sme_qos_add_ts_success_fnp))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d no match found for ac %d", + __func__, __LINE__, sessionId, + search_key.key.ac_type); + return QDF_STATUS_E_FAILURE; + } + pACInfo->hoRenewal = false; + qdf_mem_zero(&pACInfo->requested_QoSInfo[tspec_pending - 1], + sizeof(struct sme_qos_wmmtspecinfo)); + /* event: EVENT_WLAN_QOS */ +#ifdef FEATURE_WLAN_DIAG_SUPPORT + qos.eventId = SME_QOS_DIAG_ADDTS_RSP; + qos.reasonCode = SME_QOS_DIAG_ADDTS_ADMISSION_ACCEPTED; + WLAN_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); + WLAN_HOST_DIAG_LOG_ALLOC(log_ptr, host_log_qos_tspec_pkt_type, + LOG_WLAN_QOS_TSPEC_C); + if (log_ptr) { + log_ptr->delay_bound = + pACInfo->curr_QoSInfo[tspec_pending - 1].delay_bound; + log_ptr->inactivity_interval = + pACInfo->curr_QoSInfo[tspec_pending - + 1].inactivity_interval; + log_ptr->max_burst_size = + pACInfo->curr_QoSInfo[tspec_pending - 1].max_burst_size; + log_ptr->max_service_interval = + pACInfo->curr_QoSInfo[tspec_pending - + 1].max_service_interval; + log_ptr->maximum_msdu_size = + pACInfo->curr_QoSInfo[tspec_pending - 1]. + maximum_msdu_size; + log_ptr->mean_data_rate = + pACInfo->curr_QoSInfo[tspec_pending - 1].mean_data_rate; + log_ptr->medium_time = + pACInfo->curr_QoSInfo[tspec_pending - 1].medium_time; + log_ptr->min_data_rate = + pACInfo->curr_QoSInfo[tspec_pending - 1].min_data_rate; + log_ptr->min_phy_rate = + pACInfo->curr_QoSInfo[tspec_pending - 1].min_phy_rate; + log_ptr->min_service_interval = + pACInfo->curr_QoSInfo[tspec_pending - + 1].min_service_interval; + log_ptr->nominal_msdu_size = + pACInfo->curr_QoSInfo[tspec_pending - 1]. + nominal_msdu_size; + log_ptr->peak_data_rate = + pACInfo->curr_QoSInfo[tspec_pending - 1].peak_data_rate; + log_ptr->surplus_bw_allowance = + pACInfo->curr_QoSInfo[tspec_pending - + 1].surplus_bw_allowance; + log_ptr->suspension_interval = + pACInfo->curr_QoSInfo[tspec_pending - + 1].suspension_interval; + log_ptr->svc_start_time = + pACInfo->curr_QoSInfo[tspec_pending - 1].svc_start_time; + log_ptr->tsinfo[0] = + pACInfo->curr_QoSInfo[tspec_pending - + 1].ts_info.direction << 5 | + pACInfo-> + curr_QoSInfo[tspec_pending - 1].ts_info.tid << 1; + log_ptr->tsinfo[1] = + pACInfo->curr_QoSInfo[tspec_pending - + 1].ts_info.up << 11 | pACInfo-> + curr_QoSInfo[tspec_pending - 1].ts_info.psb << 10; + log_ptr->tsinfo[2] = 0; + } + WLAN_HOST_DIAG_LOG_REPORT(log_ptr); +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + pACInfo->tspec_pending = 0; + + sme_qos_state_transition(sessionId, ac, SME_QOS_QOS_ON); + + /* Inform this TSPEC IE change to FW */ + csr_session = CSR_GET_SESSION(mac, sessionId); + if ((csr_session) && (csr_session->pCurRoamProfile) && + (csr_session->pCurRoamProfile->csrPersona == QDF_STA_MODE)) + csr_roam_update_cfg(mac, sessionId, + REASON_CONNECT_IES_CHANGED); + + (void)sme_qos_process_buffered_cmd(sessionId); + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_aggregate_params() - Utility function to increament the TSPEC + * params per AC. Typical usage while using flow aggregation or deletion of + * flows + * + * pInput_Tspec_Info - Pointer to sme_QosWmmTspecInfo which contains the + * WMM TSPEC related info with which pCurrent_Tspec_Info will be updated + * pCurrent_Tspec_Info - Pointer to sme_QosWmmTspecInfo which contains + * current the WMM TSPEC related info + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_aggregate_params( + struct sme_qos_wmmtspecinfo *pInput_Tspec_Info, + struct sme_qos_wmmtspecinfo *pCurrent_Tspec_Info, + struct sme_qos_wmmtspecinfo *pUpdated_Tspec_Info) +{ + struct sme_qos_wmmtspecinfo TspecInfo; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked", __func__, __LINE__); + if (!pInput_Tspec_Info) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: input is NULL, nothing to aggregate", + __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + if (!pCurrent_Tspec_Info) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Current is NULL, can't aggregate", + __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(&TspecInfo, pCurrent_Tspec_Info, + sizeof(struct sme_qos_wmmtspecinfo)); + TspecInfo.ts_info.psb = pInput_Tspec_Info->ts_info.psb; + /* APSD preference is only meaningful if service interval + * was set by app + */ + if (pCurrent_Tspec_Info->min_service_interval && + pInput_Tspec_Info->min_service_interval && + (pCurrent_Tspec_Info->ts_info.direction != + pInput_Tspec_Info->ts_info.direction)) { + TspecInfo.min_service_interval = + QDF_MIN(pCurrent_Tspec_Info->min_service_interval, + pInput_Tspec_Info->min_service_interval); + } else if (pInput_Tspec_Info->min_service_interval) { + TspecInfo.min_service_interval = + pInput_Tspec_Info->min_service_interval; + } + if (pCurrent_Tspec_Info->max_service_interval && + pInput_Tspec_Info->max_service_interval && + (pCurrent_Tspec_Info->ts_info.direction != + pInput_Tspec_Info->ts_info.direction)) { + TspecInfo.max_service_interval = + QDF_MIN(pCurrent_Tspec_Info->max_service_interval, + pInput_Tspec_Info->max_service_interval); + } else { + TspecInfo.max_service_interval = + pInput_Tspec_Info->max_service_interval; + } + /* If directions don't match, it must necessarily be both uplink and + * downlink + */ + if (pCurrent_Tspec_Info->ts_info.direction != + pInput_Tspec_Info->ts_info.direction) + TspecInfo.ts_info.direction = + pInput_Tspec_Info->ts_info.direction; + + /* Max MSDU size : these sizes are `maxed' */ + TspecInfo.maximum_msdu_size = + QDF_MAX(pCurrent_Tspec_Info->maximum_msdu_size, + pInput_Tspec_Info->maximum_msdu_size); + + /* Inactivity interval : these sizes are `maxed' */ + TspecInfo.inactivity_interval = + QDF_MAX(pCurrent_Tspec_Info->inactivity_interval, + pInput_Tspec_Info->inactivity_interval); + + /* Delay bounds: min of all values + * Check on 0: if 0, it means initial value since delay can never be 0!! + */ + if (pCurrent_Tspec_Info->delay_bound) { + TspecInfo.delay_bound = + QDF_MIN(pCurrent_Tspec_Info->delay_bound, + pInput_Tspec_Info->delay_bound); + } else + TspecInfo.delay_bound = pInput_Tspec_Info->delay_bound; + + TspecInfo.max_burst_size = QDF_MAX(pCurrent_Tspec_Info->max_burst_size, + pInput_Tspec_Info->max_burst_size); + + /* Nominal MSDU size also has a fixed bit that needs to be `handled' + * before aggregation This can be handled only if previous size is the + * same as new or both have the fixed bit set These sizes are not added + * but `maxed' + */ + TspecInfo.nominal_msdu_size = + QDF_MAX(pCurrent_Tspec_Info->nominal_msdu_size & + ~SME_QOS_16BIT_MSB, pInput_Tspec_Info->nominal_msdu_size + & ~SME_QOS_16BIT_MSB); + + if (((pCurrent_Tspec_Info->nominal_msdu_size == 0) || + (pCurrent_Tspec_Info->nominal_msdu_size & SME_QOS_16BIT_MSB)) && + ((pInput_Tspec_Info->nominal_msdu_size == 0) || + (pInput_Tspec_Info->nominal_msdu_size & SME_QOS_16BIT_MSB))) + TspecInfo.nominal_msdu_size |= SME_QOS_16BIT_MSB; + + /* Data rates: Add up the rates for aggregation */ + SME_QOS_BOUNDED_U32_ADD_Y_TO_X(TspecInfo.peak_data_rate, + pInput_Tspec_Info->peak_data_rate); + SME_QOS_BOUNDED_U32_ADD_Y_TO_X(TspecInfo.min_data_rate, + pInput_Tspec_Info->min_data_rate); + /* mean data rate = peak data rate: aggregate to be flexible on apps */ + SME_QOS_BOUNDED_U32_ADD_Y_TO_X(TspecInfo.mean_data_rate, + pInput_Tspec_Info->mean_data_rate); + + /* + * Suspension interval : this is set to the inactivity interval since + * per spec it is less than or equal to inactivity interval + * This is not provided by app since we currently don't support the HCCA + * mode of operation Currently set it to 0 to avoid confusion: Cisco ESE + * needs ~0; spec requires inactivity interval to be > suspension + * interval: this could be tricky! + */ + TspecInfo.suspension_interval = pInput_Tspec_Info->suspension_interval; + /* Remaining parameters do not come from app as they are very WLAN + * air interface specific Set meaningful values here + */ + TspecInfo.medium_time = 0; /* per WMM spec */ + TspecInfo.min_phy_rate = SME_QOS_MIN_PHY_RATE; + TspecInfo.svc_start_time = 0; /* arbitrary */ + TspecInfo.surplus_bw_allowance += + pInput_Tspec_Info->surplus_bw_allowance; + if (TspecInfo.surplus_bw_allowance > SME_QOS_SURPLUS_BW_ALLOWANCE) + TspecInfo.surplus_bw_allowance = SME_QOS_SURPLUS_BW_ALLOWANCE; + + /* Set ack_policy to block ack even if one stream requests block + * ack policy + */ + if ((pInput_Tspec_Info->ts_info.ack_policy == + SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK) + || (pCurrent_Tspec_Info->ts_info.ack_policy == + SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK)) + TspecInfo.ts_info.ack_policy = + SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK; + + if (pInput_Tspec_Info->ts_info.burst_size_defn + || pCurrent_Tspec_Info->ts_info.burst_size_defn) + TspecInfo.ts_info.burst_size_defn = 1; + + if (pUpdated_Tspec_Info) + qdf_mem_copy(pUpdated_Tspec_Info, &TspecInfo, + sizeof(struct sme_qos_wmmtspecinfo)); + else + qdf_mem_copy(pCurrent_Tspec_Info, &TspecInfo, + sizeof(struct sme_qos_wmmtspecinfo)); + + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_update_params() - Utility function to update the TSPEC + * params per AC. Typical usage while deleting flows on AC which is running + * multiple flows + * + * sessionId - Session upon which the TSPEC is being updated + * ac - Enumeration of the various EDCA Access Categories. + * tspec_mask - on which tspec per AC, the update is requested + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_update_params(uint8_t sessionId, + enum qca_wlan_ac_type ac, + uint8_t tspec_mask, + struct sme_qos_wmmtspecinfo *pTspec_Info) +{ + tListElem *pEntry = NULL, *pNextEntry = NULL; + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + struct sme_qos_flowinfoentry *flow_info = NULL; + struct sme_qos_wmmtspecinfo Tspec_Info; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: invoked on session %d for AC %d TSPEC %d", + __func__, __LINE__, sessionId, ac, tspec_mask); + if (!pTspec_Info) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: output is NULL, can't aggregate", + __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_zero(&Tspec_Info, sizeof(struct sme_qos_wmmtspecinfo)); + pEntry = csr_ll_peek_head(&sme_qos_cb.flow_list, false); + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Flow List empty, nothing to update", + __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pACInfo = &pSession->ac_info[ac]; + /* init the TS info field */ + Tspec_Info.ts_info.up = + pACInfo->curr_QoSInfo[tspec_mask - 1].ts_info.up; + Tspec_Info.ts_info.psb = + pACInfo->curr_QoSInfo[tspec_mask - 1].ts_info.psb; + Tspec_Info.ts_info.tid = + pACInfo->curr_QoSInfo[tspec_mask - 1].ts_info.tid; + while (pEntry) { + pNextEntry = csr_ll_next(&sme_qos_cb.flow_list, pEntry, false); + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, + link); + if ((sessionId == flow_info->sessionId) && + (ac == flow_info->ac_type) && + (tspec_mask == flow_info->tspec_mask)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Flow %d matches", + __func__, __LINE__, flow_info->QosFlowID); + + if ((SME_QOS_REASON_RELEASE == flow_info->reason) || + (SME_QOS_REASON_MODIFY == flow_info->reason)) { + /* msg */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Skipping Flow %d as it is marked for release/modify", + __func__, + __LINE__, flow_info->QosFlowID); + } else + if (!QDF_IS_STATUS_SUCCESS + (sme_qos_aggregate_params + (&flow_info->QoSInfo, &Tspec_Info, + NULL))) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: sme_qos_aggregate_params() failed", + __func__, __LINE__); + } + } + pEntry = pNextEntry; + } + /* return the aggregate */ + *pTspec_Info = Tspec_Info; + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_up_to_ac() - Utility function to map an UP to AC + * + * up - Enumeration of the various User priorities (UP). + * Return an Access Category + */ +static enum qca_wlan_ac_type sme_qos_up_to_ac(enum sme_qos_wmmuptype up) +{ + enum qca_wlan_ac_type ac = QCA_WLAN_AC_ALL; + + if (up >= 0 && up < SME_QOS_WMM_UP_MAX) + ac = sme_qos_up_to_ac_map[up]; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: up = %d ac = %d returned", + __func__, __LINE__, up, ac); + return ac; +} + +/* + * sme_qos_state_transition() - The state transition function per AC. We + * save the previous state also. + * + * sessionId - Session upon which the state machine is running + * ac - Enumeration of the various EDCA Access Categories. + * new_state - The state FSM is moving to. + * + * Return None + */ +static void sme_qos_state_transition(uint8_t sessionId, + enum qca_wlan_ac_type ac, + enum sme_qos_states new_state) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pACInfo = &pSession->ac_info[ac]; + pACInfo->prev_state = pACInfo->curr_state; + pACInfo->curr_state = new_state; + if (pACInfo->curr_state != pACInfo->prev_state) + sme_debug("On session %d new %d old %d, for AC %d", + sessionId, pACInfo->curr_state, + pACInfo->prev_state, ac); +} + +/** + * sme_qos_find_in_flow_list() - find a flow entry from the flow list + * @search_key: We can either use the flowID or the ac type to find the + * entry in the flow list. + * A bitmap in struct sme_qos_searchinfo tells which key to use. + * Starting from LSB, + * bit 0 - Flow ID + * bit 1 - AC type + * + * Utility function to find an flow entry from the flow_list. + * + * Return: pointer to the list element + */ +static tListElem *sme_qos_find_in_flow_list(struct sme_qos_searchinfo + search_key) +{ + tListElem *list_elt = NULL, *list_next_elt = NULL; + struct sme_qos_flowinfoentry *flow_info = NULL; + + list_elt = csr_ll_peek_head(&sme_qos_cb.flow_list, false); + if (!list_elt) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Flow List empty, can't search")); + return NULL; + } + + while (list_elt) { + list_next_elt = csr_ll_next(&sme_qos_cb.flow_list, list_elt, + false); + flow_info = GET_BASE_ADDR(list_elt, struct + sme_qos_flowinfoentry, link); + + if ((search_key.sessionId != flow_info->sessionId) && + (search_key.sessionId != SME_QOS_SEARCH_SESSION_ID_ANY)) { + list_elt = list_next_elt; + continue; + } + + if (search_key.index & SME_QOS_SEARCH_KEY_INDEX_1) { + if (search_key.key.QosFlowID == flow_info->QosFlowID) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("match found on flowID, ending search")); + break; + } + } else if (search_key.index & SME_QOS_SEARCH_KEY_INDEX_2) { + if (search_key.key.ac_type == flow_info->ac_type) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("match found on ac, ending search")); + break; + } + } else if (search_key.index & SME_QOS_SEARCH_KEY_INDEX_3) { + if (search_key.key.reason == flow_info->reason) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("match found on reason, ending search")); + break; + } + } else if (search_key.index & SME_QOS_SEARCH_KEY_INDEX_4) { + if ((search_key.key.ac_type == flow_info->ac_type) && + (search_key.direction == + flow_info->QoSInfo.ts_info.direction)) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + FL("match found on reason, ending search")); + break; + } + } + list_elt = list_next_elt; + } + return list_elt; +} + +/** + * sme_qos_find_all_in_flow_list() - find a flow entry in the flow list + * @mac_ctx: global MAC context + * @search_key: search key + * @fnp: function pointer specifying the action type for the entry found + * + * Utility function to find an flow entry from the flow_list & act on it. + * search_key - We can either use the flowID or the ac type to find the + * entry in the flow list. + * A bitmap in struct sme_qos_searchinfo tells which key to use. Starting from + * LSB, + * bit 0 - Flow ID + * bit 1 - AC type + * + * Return: None + */ +static QDF_STATUS sme_qos_find_all_in_flow_list(struct mac_context *mac_ctx, + struct sme_qos_searchinfo search_key, + sme_QosProcessSearchEntry fnp) +{ + tListElem *list_elt = NULL, *list_next_elt = NULL; + struct sme_qos_sessioninfo *qos_session; + struct sme_qos_flowinfoentry *flow_info = NULL; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + enum qca_wlan_ac_type ac_type; + + list_elt = csr_ll_peek_head(&sme_qos_cb.flow_list, false); + if (!list_elt) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Flow List empty, can't search")); + return QDF_STATUS_E_FAILURE; + } + + while (list_elt) { + list_next_elt = csr_ll_next(&sme_qos_cb.flow_list, list_elt, + false); + flow_info = GET_BASE_ADDR(list_elt, struct + sme_qos_flowinfoentry, link); + qos_session = &sme_qos_cb.sessionInfo[flow_info->sessionId]; + if ((search_key.sessionId != flow_info->sessionId) && + (search_key.sessionId != SME_QOS_SEARCH_SESSION_ID_ANY)) { + list_elt = list_next_elt; + continue; + } + + if ((search_key.index & SME_QOS_SEARCH_KEY_INDEX_1) && + (search_key.key.QosFlowID == flow_info->QosFlowID)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("match found on flowID, ending search")); + status = fnp(mac_ctx, list_elt); + if (QDF_STATUS_E_FAILURE == status) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + FL("Failed to process entry")); + break; + } + } else if ((search_key.index & SME_QOS_SEARCH_KEY_INDEX_2) && + (search_key.key.ac_type == flow_info->ac_type)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("match found on ac, ending search")); + ac_type = flow_info->ac_type; + flow_info->hoRenewal = + qos_session->ac_info[ac_type].hoRenewal; + status = fnp(mac_ctx, list_elt); + if (QDF_STATUS_E_FAILURE == status) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + FL("Failed to process entry")); + break; + } + } + list_elt = list_next_elt; + } + return status; +} + +/* + * sme_qos_is_acm() - Utility function to check if a particular AC + * mandates Admission Control. + * + * ac - Enumeration of the various EDCA Access Categories. + * + * Return true if the AC mandates Admission Control + */ +static bool +sme_qos_is_acm(struct mac_context *mac, struct bss_description *pSirBssDesc, + enum qca_wlan_ac_type ac, tDot11fBeaconIEs *pIes) +{ + bool ret_val = false; + tDot11fBeaconIEs *pIesLocal; + + if (!pSirBssDesc) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: pSirBssDesc is NULL", __func__, __LINE__); + return false; + } + + if (pIes) + /* IEs were provided so use them locally */ + pIesLocal = pIes; + else { + /* IEs were not provided so parse them ourselves */ + if (!QDF_IS_STATUS_SUCCESS + (csr_get_parsed_bss_description_ies + (mac, pSirBssDesc, &pIesLocal))) { + /* err msg */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: csr_get_parsed_bss_description_ies() failed", + __func__, __LINE__); + return false; + } + + /* if success then pIesLocal was allocated */ + } + + if (CSR_IS_QOS_BSS(pIesLocal)) { + switch (ac) { + case QCA_WLAN_AC_BE: + if (pIesLocal->WMMParams.acbe_acm) + ret_val = true; + break; + case QCA_WLAN_AC_BK: + if (pIesLocal->WMMParams.acbk_acm) + ret_val = true; + break; + case QCA_WLAN_AC_VI: + if (pIesLocal->WMMParams.acvi_acm) + ret_val = true; + break; + case QCA_WLAN_AC_VO: + if (pIesLocal->WMMParams.acvo_acm) + ret_val = true; + break; + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: unknown AC = %d", + __func__, __LINE__, ac); + break; + } + } /* IS_QOS_BSS */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: ACM = %d for AC = %d", + __func__, __LINE__, ret_val, ac); + if (!pIes) + /* IEs were allocated locally so free them */ + qdf_mem_free(pIesLocal); + + return ret_val; +} + +/** + * sme_qos_buffer_existing_flows() - buffer existing flows in flow_list + * @mac_ctx: global MAC context + * @sessionid: session ID + * + * Utility function to buffer the existing flows in flow_list, + * so that we can renew them after handoff is done. + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_buffer_existing_flows(struct mac_context *mac_ctx, + uint8_t sessionid) +{ + tListElem *list_entry = NULL, *list_nextentry = NULL; + struct sme_qos_sessioninfo *qos_session; + struct sme_qos_flowinfoentry *flow_info = NULL; + struct sme_qos_cmdinfo cmd; + struct sme_qos_setupcmdinfo *setupinfo; + + list_entry = csr_ll_peek_head(&sme_qos_cb.flow_list, false); + if (!list_entry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Flow List empty, nothing to buffer")); + return QDF_STATUS_E_FAILURE; + } + + while (list_entry) { + list_nextentry = csr_ll_next(&sme_qos_cb.flow_list, list_entry, + false); + flow_info = GET_BASE_ADDR(list_entry, struct + sme_qos_flowinfoentry, link); + if (flow_info->sessionId != sessionid) { + list_entry = list_nextentry; + continue; + } + + if ((SME_QOS_REASON_REQ_SUCCESS == flow_info->reason) || + (SME_QOS_REASON_SETUP == flow_info->reason)) { + cmd.command = SME_QOS_SETUP_REQ; + cmd.mac = mac_ctx; + cmd.sessionId = sessionid; + setupinfo = &cmd.u.setupCmdInfo; + + setupinfo->HDDcontext = flow_info->HDDcontext; + setupinfo->QoSInfo = flow_info->QoSInfo; + setupinfo->QoSCallback = flow_info->QoSCallback; + /* shouldn't be needed */ + setupinfo->UPType = SME_QOS_WMM_UP_MAX; + setupinfo->QosFlowID = flow_info->QosFlowID; + if (SME_QOS_REASON_SETUP == flow_info->reason) + setupinfo->hoRenewal = false; + else + setupinfo->hoRenewal = true; + + if (!QDF_IS_STATUS_SUCCESS + (sme_qos_buffer_cmd(&cmd, true))) + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "couldn't buffer the setup request for flow %d in handoff state", + flow_info->QosFlowID); + else + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "buffered a setup request for flow %d in handoff state", + flow_info->QosFlowID); + } else if (SME_QOS_REASON_RELEASE == flow_info->reason) { + cmd.command = SME_QOS_RELEASE_REQ; + cmd.mac = mac_ctx; + cmd.sessionId = sessionid; + cmd.u.releaseCmdInfo.QosFlowID = flow_info->QosFlowID; + if (!QDF_IS_STATUS_SUCCESS + (sme_qos_buffer_cmd(&cmd, true))) + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "couldn't buffer the release req for flow %d in handoff state", + flow_info->QosFlowID); + else + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "buffered a release request for flow %d in handoff state", + flow_info->QosFlowID); + } else if (SME_QOS_REASON_MODIFY_PENDING == + flow_info->reason) { + cmd.command = SME_QOS_MODIFY_REQ; + cmd.mac = mac_ctx; + cmd.sessionId = sessionid; + cmd.u.modifyCmdInfo.QosFlowID = flow_info->QosFlowID; + cmd.u.modifyCmdInfo.QoSInfo = flow_info->QoSInfo; + if (!QDF_IS_STATUS_SUCCESS + (sme_qos_buffer_cmd(&cmd, true))) + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "couldn't buffer the modify req for flow %d in handoff state", + flow_info->QosFlowID); + else + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_DEBUG, + "buffered a modify request for flow %d in handoff state", + flow_info->QosFlowID); + } + /* delete the entry from Flow List */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Deleting original entry at %pK with flowID %d"), + flow_info, flow_info->QosFlowID); + csr_ll_remove_entry(&sme_qos_cb.flow_list, list_entry, true); + qdf_mem_free(flow_info); + + list_entry = list_nextentry; + } + qos_session = &sme_qos_cb.sessionInfo[sessionid]; + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_delete_existing_flows() - Utility function to Delete the existing + * flows in flow_list, if we lost connectivity. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_delete_existing_flows(struct mac_context *mac, + uint8_t sessionId) +{ + tListElem *pEntry = NULL, *pNextEntry = NULL; + struct sme_qos_flowinfoentry *flow_info = NULL; + + pEntry = csr_ll_peek_head(&sme_qos_cb.flow_list, true); + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Flow List empty, nothing to delete", + __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + while (pEntry) { + pNextEntry = csr_ll_next(&sme_qos_cb.flow_list, pEntry, true); + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, + link); + if (flow_info->sessionId == sessionId) { + if ((SME_QOS_REASON_REQ_SUCCESS == flow_info->reason) || + (SME_QOS_REASON_SETUP == flow_info->reason) || + (SME_QOS_REASON_RELEASE == flow_info->reason) || + (SME_QOS_REASON_MODIFY == flow_info->reason)) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info->HDDcontext, + NULL, + SME_QOS_STATUS_RELEASE_QOS_LOST_IND, + flow_info->QosFlowID); + } + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Deleting entry at %pK with flowID %d", + __func__, __LINE__, + flow_info, flow_info->QosFlowID); + /* delete the entry from Flow List */ + csr_ll_remove_entry(&sme_qos_cb.flow_list, pEntry, + true); + qdf_mem_free(flow_info); + } + pEntry = pNextEntry; + } + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_buffer_cmd() - buffer a request. + * @pcmd: a pointer to the cmd structure to be saved inside the buffered + * cmd link list + * @insert_head: flag indicate if cmd should be added to the list head. + * + * Utility function to buffer a request (setup/modify/release) from client + * while processing another one on the same AC. + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_buffer_cmd(struct sme_qos_cmdinfo *pcmd, + bool insert_head) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_cmdinfoentry *pentry = NULL; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Invoked", __func__, __LINE__); + pentry = qdf_mem_malloc(sizeof(struct sme_qos_cmdinfoentry)); + if (!pentry) + return QDF_STATUS_E_NOMEM; + + /* copy the entire CmdInfo */ + pentry->cmdInfo = *pcmd; + + pSession = &sme_qos_cb.sessionInfo[pcmd->sessionId]; + if (insert_head) + csr_ll_insert_head(&pSession->bufferedCommandList, + &pentry->link, true); + else + csr_ll_insert_tail(&pSession->bufferedCommandList, + &pentry->link, true); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_process_buffered_cmd() - process qos buffered request + * @session_id: Session ID + * + * Utility function to process a buffered request (setup/modify/release) + * initially came from the client. + * + * Return:QDF_STATUS + */ +static QDF_STATUS sme_qos_process_buffered_cmd(uint8_t session_id) +{ + struct sme_qos_sessioninfo *qos_session; + struct sme_qos_cmdinfoentry *pcmd = NULL; + tListElem *list_elt = NULL; + enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; + QDF_STATUS qdf_ret_status = QDF_STATUS_SUCCESS; + struct sme_qos_cmdinfo *qos_cmd = NULL; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Invoked on session %d"), session_id); + qos_session = &sme_qos_cb.sessionInfo[session_id]; + if (!csr_ll_is_list_empty(&qos_session->bufferedCommandList, false)) { + list_elt = csr_ll_remove_head(&qos_session->bufferedCommandList, + true); + if (!list_elt) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("no more buffered commands on session %d"), + session_id); + return QDF_STATUS_E_FAILURE; + } + pcmd = GET_BASE_ADDR(list_elt, struct sme_qos_cmdinfoentry, + link); + qos_cmd = &pcmd->cmdInfo; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Qos cmd %d"), qos_cmd->command); + switch (qos_cmd->command) { + case SME_QOS_SETUP_REQ: + hdd_status = sme_qos_internal_setup_req( + qos_cmd->mac, qos_cmd->sessionId, + &qos_cmd->u.setupCmdInfo.QoSInfo, + qos_cmd->u.setupCmdInfo.QoSCallback, + qos_cmd->u.setupCmdInfo.HDDcontext, + qos_cmd->u.setupCmdInfo.UPType, + qos_cmd->u.setupCmdInfo.QosFlowID, + true, qos_cmd->u.setupCmdInfo.hoRenewal); + if (SME_QOS_STATUS_SETUP_FAILURE_RSP == hdd_status) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "sme_qos_internal_setup_req failed on session %d", + session_id); + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; + case SME_QOS_RELEASE_REQ: + hdd_status = sme_qos_internal_release_req(qos_cmd->mac, + qos_cmd->sessionId, + qos_cmd->u.releaseCmdInfo.QosFlowID, + true); + if (SME_QOS_STATUS_RELEASE_FAILURE_RSP == hdd_status) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "sme_qos_internal_release_req failed on session %d", + session_id); + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; + case SME_QOS_MODIFY_REQ: + hdd_status = sme_qos_internal_modify_req(qos_cmd->mac, + &qos_cmd->u.modifyCmdInfo.QoSInfo, + qos_cmd->u.modifyCmdInfo.QosFlowID, + true); + if (SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP == + hdd_status) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "sme_qos_internal_modify_req failed on session %d", + session_id); + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; + case SME_QOS_RESEND_REQ: + hdd_status = sme_qos_re_request_add_ts(qos_cmd->mac, + qos_cmd->sessionId, + &qos_cmd->u.resendCmdInfo.QoSInfo, + qos_cmd->u.resendCmdInfo.ac, + qos_cmd->u.resendCmdInfo.tspecMask); + if (SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP == + hdd_status) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "sme_qos_re_request_add_ts failed on session %d", + session_id); + qdf_ret_status = QDF_STATUS_E_FAILURE; + } + break; + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("On session %d unknown cmd = %d"), + session_id, qos_cmd->command); + break; + } + /* buffered command has been processed, reclaim the memory */ + qdf_mem_free(pcmd); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("cmd buffer empty")); + } + return qdf_ret_status; +} + +/* + * sme_qos_delete_buffered_requests() - Utility function to Delete the buffered + * requests in the buffered_cmd_list, if we lost connectivity. + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_delete_buffered_requests(struct mac_context *mac, + uint8_t sessionId) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_cmdinfoentry *pcmd = NULL; + tListElem *pEntry = NULL, *pNextEntry = NULL; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Invoked on session %d", + __func__, __LINE__, sessionId); + pSession = &sme_qos_cb.sessionInfo[sessionId]; + pEntry = csr_ll_peek_head(&pSession->bufferedCommandList, true); + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Buffered List empty, nothing to delete on session %d", + __func__, __LINE__, sessionId); + return QDF_STATUS_E_FAILURE; + } + while (pEntry) { + pNextEntry = csr_ll_next(&pSession->bufferedCommandList, pEntry, + true); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: deleting entry from buffered List", __func__, + __LINE__); + /* delete the entry from Flow List */ + csr_ll_remove_entry(&pSession->bufferedCommandList, pEntry, + true); + /* reclaim the memory */ + pcmd = GET_BASE_ADDR(pEntry, struct sme_qos_cmdinfoentry, + link); + qdf_mem_free(pcmd); + pEntry = pNextEntry; + } + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_save_assoc_info() - save assoc info. + * @pSession: pointer to QOS session + * @pAssoc_info: pointer to the assoc structure to store the BSS descriptor + * of the AP, the profile that HDD sent down with the + * connect request + * + * Utility function to save the assoc info in the CB like BSS descriptor + * of the AP, the profile that HDD sent down with the connect request, + * while CSR notifies for assoc/reassoc success. + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_qos_save_assoc_info(struct sme_qos_sessioninfo *pSession, + sme_QosAssocInfo *pAssoc_info) +{ + struct bss_description *bss_desc = NULL; + uint32_t bssLen = 0; + + if (!pAssoc_info) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: pAssoc_info is NULL", __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + /* clean up the assoc info if already set */ + if (pSession->assocInfo.bss_desc) { + qdf_mem_free(pSession->assocInfo.bss_desc); + pSession->assocInfo.bss_desc = NULL; + } + bssLen = pAssoc_info->bss_desc->length + + sizeof(pAssoc_info->bss_desc->length); + /* save the bss Descriptor */ + bss_desc = qdf_mem_malloc(bssLen); + if (!bss_desc) + return QDF_STATUS_E_NOMEM; + + qdf_mem_copy(bss_desc, pAssoc_info->bss_desc, bssLen); + pSession->assocInfo.bss_desc = bss_desc; + /* save the apsd info from assoc */ + if (pAssoc_info->pProfile) + pSession->apsdMask |= pAssoc_info->pProfile->uapsd_mask; + + /* [TODO] Do we need to update the global APSD bitmap? */ + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_setup_fnp() - Utility function (pointer) to notify other entries + * in FLOW list on the same AC that qos params got modified + * + * mac - Pointer to the global MAC parameter structure. + * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_setup_fnp(struct mac_context *mac, tListElem *pEntry) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + struct sme_qos_flowinfoentry *flow_info = NULL; + enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND; + enum qca_wlan_ac_type ac; + + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Entry is NULL", __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link); + ac = flow_info->ac_type; + pSession = &sme_qos_cb.sessionInfo[flow_info->sessionId]; + pACInfo = &pSession->ac_info[ac]; + if (SME_QOS_REASON_REQ_SUCCESS == flow_info->reason) { + /* notify HDD, only the other Flows running on the AC */ + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info->HDDcontext, + &pACInfo->curr_QoSInfo[flow_info-> + tspec_mask - 1], + hdd_status, flow_info->QosFlowID); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Entry with flowID = %d getting notified", + __func__, __LINE__, flow_info->QosFlowID); + } + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_modification_notify_fnp() - Utility function (pointer) to notify + * other entries in FLOW list on the same AC that qos params got modified + * + * mac - Pointer to the global MAC parameter structure. + * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_modification_notify_fnp(struct mac_context *mac, tListElem + *pEntry) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + struct sme_qos_flowinfoentry *flow_info = NULL; + enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND; + enum qca_wlan_ac_type ac; + + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Entry is NULL", __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link); + ac = flow_info->ac_type; + pSession = &sme_qos_cb.sessionInfo[flow_info->sessionId]; + pACInfo = &pSession->ac_info[ac]; + if (SME_QOS_REASON_REQ_SUCCESS == flow_info->reason) { + /* notify HDD, only the other Flows running on the AC */ + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info->HDDcontext, + &pACInfo->curr_QoSInfo[flow_info-> + tspec_mask - 1], + hdd_status, flow_info->QosFlowID); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Entry with flowID = %d getting notified", + __func__, __LINE__, flow_info->QosFlowID); + } + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_modify_fnp() - Utility function (pointer) to delete the origianl + * entry in FLOW list & add the modified one + * + * mac - Pointer to the global MAC parameter structure. + * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_modify_fnp(struct mac_context *mac, tListElem *pEntry) +{ + struct sme_qos_flowinfoentry *flow_info = NULL; + + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Entry is NULL", __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link); + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("reason %d"), flow_info->reason); + switch (flow_info->reason) { + case SME_QOS_REASON_MODIFY_PENDING: + /* set the proper reason code for the new (with modified params) + * entry + */ + flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; + break; + case SME_QOS_REASON_MODIFY: + /* delete the original entry from Flow List */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Deleting original entry at %pK with flowID %d", + __func__, __LINE__, flow_info, flow_info->QosFlowID); + csr_ll_remove_entry(&sme_qos_cb.flow_list, pEntry, true); + /* reclaim the memory */ + qdf_mem_free(flow_info); + break; + default: + break; + } + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_del_ts_ind_fnp() - Utility function (pointer) to find all Flows on + * the perticular AC & delete them, also send HDD indication through the + * callback it registered per request + * + * mac - Pointer to the global MAC parameter structure. + * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_del_ts_ind_fnp(struct mac_context *mac, tListElem *pEntry) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + struct sme_qos_flowinfoentry *flow_info = NULL; + enum qca_wlan_ac_type ac; + QDF_STATUS lock_status = QDF_STATUS_E_FAILURE; + enum sme_qos_statustype status; + + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Entry is NULL", __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + /* delete the entry from Flow List */ + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link); + ac = flow_info->ac_type; + pSession = &sme_qos_cb.sessionInfo[flow_info->sessionId]; + pACInfo = &pSession->ac_info[ac]; + pACInfo->relTrig = SME_QOS_RELEASE_BY_AP; + + lock_status = sme_acquire_global_lock(&mac->sme); + if (!QDF_IS_STATUS_SUCCESS(lock_status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Unable to obtain lock", __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + /* Call the internal function for QoS release, adding a layer of + * abstraction + */ + status = + sme_qos_internal_release_req(mac, flow_info->sessionId, + flow_info->QosFlowID, false); + sme_release_global_lock(&mac->sme); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: QoS Release return status on Flow %d is %d", + __func__, __LINE__, flow_info->QosFlowID, status); + + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_reassoc_success_ev_fnp Notification function to HDD + * + * @mac_ctx: Mac context + * @entry: Pointer to an entry in the flow_list + * + * Utility function (pointer) to notify HDD + * the success for the requested flow & notify all the other flows + * running on the same AC that QoS params got modified + * + * Return: QDF_STATUS enumaration + */ +static QDF_STATUS +sme_qos_reassoc_success_ev_fnp(struct mac_context *mac_ctx, + tListElem *entry) +{ + struct sme_qos_sessioninfo *qos_session; + struct sme_qos_acinfo *ac_info; + struct sme_qos_flowinfoentry *flow_info = NULL; + bool delete_entry = false; + enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; + enum qca_wlan_ac_type ac; + QDF_STATUS pmc_status = QDF_STATUS_E_FAILURE; + + if (!entry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Entry is NULL", __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + flow_info = GET_BASE_ADDR(entry, struct sme_qos_flowinfoentry, link); + ac = flow_info->ac_type; + qos_session = &sme_qos_cb.sessionInfo[flow_info->sessionId]; + ac_info = &qos_session->ac_info[ac]; + switch (flow_info->reason) { + case SME_QOS_REASON_SETUP: + hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND; + delete_entry = false; + flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; + /* -Check for the case where we had to do reassoc to + * reset the apsd bit for the ac - release or modify + * scenario.Notify PMC as App is looking for APSD + * If we already requested then we don't need to + * do anything. + */ + if (ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0]. + ts_info.psb) { + /* this is the first flow to detect we need + * PMC in UAPSD mode + */ + pmc_status = sme_ps_start_uapsd(MAC_HANDLE(mac_ctx), + flow_info->sessionId); + /* if PMC doesn't return success right away means + * it is yet to put the module in BMPS state & later + * to UAPSD state + */ + if (QDF_STATUS_E_FAILURE == pmc_status) { + hdd_status = + SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED; + /* we need to always notify this case */ + flow_info->hoRenewal = false; + } + } + /* for any other pmc status we declare success */ + break; + case SME_QOS_REASON_RELEASE: + ac_info->num_flows[SME_QOS_TSPEC_INDEX_0]--; + /* fall through */ + case SME_QOS_REASON_MODIFY: + delete_entry = true; + break; + case SME_QOS_REASON_MODIFY_PENDING: + hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND; + delete_entry = false; + flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; + if (ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0]. + ts_info.psb) { + /* this is the first flow to detect we need + * PMC in UAPSD mode + */ + pmc_status = sme_ps_start_uapsd(MAC_HANDLE(mac_ctx), + flow_info->sessionId); + /* if PMC doesn't return success right away means + * it is yet to put the module in BMPS state & + * later to UAPSD state + */ + if (QDF_STATUS_E_FAILURE == pmc_status) { + hdd_status = + SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED; + /* we need to always notify this case */ + flow_info->hoRenewal = false; + } + } + /* for any other pmc status we declare success */ + break; + case SME_QOS_REASON_REQ_SUCCESS: + hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND; + /* fall through */ + default: + delete_entry = false; + break; + } + if (!delete_entry) { + if (!flow_info->hoRenewal) { + flow_info->QoSCallback(MAC_HANDLE(mac_ctx), + flow_info->HDDcontext, + &ac_info->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], + hdd_status, + flow_info->QosFlowID); + } else + flow_info->hoRenewal = false; + } else { + /* delete the entry from Flow List */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Deleting entry at %pK with flowID %d"), + flow_info, flow_info->QosFlowID); + csr_ll_remove_entry(&sme_qos_cb.flow_list, entry, true); + /* reclaim the memory */ + qdf_mem_free(flow_info); + } + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_add_ts_failure_fnp() - Utility function (pointer), + * if the Addts request was for for an flow setup request, delete the entry from + * Flow list & notify HDD if the Addts request was for downgrading of QoS params + * because of an flow release requested on the AC, delete the entry from Flow + * list & notify HDD if the Addts request was for change of QoS params because + * of an flow modification requested on the AC, delete the new entry from Flow + * list & notify HDD + * + * mac - Pointer to the global MAC parameter structure. + * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) + * + * Return QDF_STATUS + */ +static QDF_STATUS sme_qos_add_ts_failure_fnp(struct mac_context *mac, tListElem + *pEntry) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + struct sme_qos_flowinfoentry *flow_info = NULL; + bool inform_hdd = false; + enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; + enum qca_wlan_ac_type ac; + + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Entry is NULL", __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link); + ac = flow_info->ac_type; + pSession = &sme_qos_cb.sessionInfo[flow_info->sessionId]; + pACInfo = &pSession->ac_info[ac]; + switch (flow_info->reason) { + case SME_QOS_REASON_SETUP: + hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; + pACInfo->num_flows[pACInfo->tspec_pending - 1]--; + inform_hdd = true; + break; + case SME_QOS_REASON_RELEASE: + hdd_status = SME_QOS_STATUS_RELEASE_FAILURE_RSP; + pACInfo->num_flows[pACInfo->tspec_pending - 1]--; + inform_hdd = true; + break; + case SME_QOS_REASON_MODIFY_PENDING: + hdd_status = SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + inform_hdd = true; + break; + case SME_QOS_REASON_MODIFY: + flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; + case SME_QOS_REASON_REQ_SUCCESS: + /* fallthrough */ + default: + inform_hdd = false; + break; + } + if (inform_hdd) { + /* notify HDD, only the requested Flow, other Flows running on + * the AC stay intact + */ + if (!flow_info->hoRenewal) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info->HDDcontext, + &pACInfo->curr_QoSInfo[pACInfo-> + tspec_pending + - 1], + hdd_status, + flow_info->QosFlowID); + } else { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info->HDDcontext, + &pACInfo->curr_QoSInfo[pACInfo-> + tspec_pending + - 1], + SME_QOS_STATUS_RELEASE_QOS_LOST_IND, + flow_info->QosFlowID); + } + /* delete the entry from Flow List */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Deleting entry at %pK with flowID %d", + __func__, __LINE__, flow_info, flow_info->QosFlowID); + csr_ll_remove_entry(&sme_qos_cb.flow_list, pEntry, true); + /* reclaim the memory */ + qdf_mem_free(flow_info); + } + return QDF_STATUS_SUCCESS; +} + +/** + * sme_qos_add_ts_success_fnp() - Utility function (pointer) to notify HDD + * + * @mac_ctx: Mac context + * @entry: Pointer to an entry in the flow_list(i.e. tListElem structure). + * + * Description : Utility function (pointer), + * If the Addts request was for for an flow setup request, notify + * HDD for success for the flow & notify all the other flows running + * on the same AC that QoS params got modified + * if the Addts request was for downgrading of QoS params + * because of an flow release requested on the AC, delete + * the entry from Flow list & notify HDD if the Addts request + * was for change of QoS params because of an flow modification + * requested on the AC, delete the old entry from Flow list & notify + * HDD for success for the flow & notify all the other flows running + * on the same AC that QoS params got modified + * + * Return: Status + */ + +static QDF_STATUS sme_qos_add_ts_success_fnp(struct mac_context *mac_ctx, + tListElem *entry) +{ + struct sme_qos_sessioninfo *qos_session; + struct sme_qos_acinfo *ac_info; + struct sme_qos_flowinfoentry *flow_info = NULL; + bool inform_hdd = false; + bool delete_entry = false; + enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; + enum qca_wlan_ac_type ac; + QDF_STATUS pmc_status = QDF_STATUS_E_FAILURE; + tCsrRoamModifyProfileFields profile_fields; + uint8_t psb; + uint8_t tspec_index; + + if (!entry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Entry is NULL")); + return QDF_STATUS_E_FAILURE; + } + flow_info = GET_BASE_ADDR(entry, struct sme_qos_flowinfoentry, link); + ac = flow_info->ac_type; + qos_session = &sme_qos_cb.sessionInfo[flow_info->sessionId]; + ac_info = &qos_session->ac_info[ac]; + tspec_index = ac_info->tspec_pending - 1; + if (flow_info->tspec_mask != ac_info->tspec_pending) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + " No need to notify the HDD, the ADDTS success is not for index = %d of the AC = %d", + flow_info->tspec_mask, ac); + return QDF_STATUS_SUCCESS; + } + switch (flow_info->reason) { + case SME_QOS_REASON_SETUP: + hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND; + flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; + delete_entry = false; + inform_hdd = true; + /* check if App is looking for APSD + * notify PMC as App is looking for APSD. If we already + * requested then we don't need to do anything + */ + if (ac_info->requested_QoSInfo[tspec_index].ts_info.psb) { + /* this is the first flow to detect we need + * PMC in UAPSD mode + */ + pmc_status = sme_ps_start_uapsd(MAC_HANDLE(mac_ctx), + flow_info->sessionId); + /* if PMC doesn't return success right away means + * it is yet to put the module in BMPS state & later + * to UAPSD state + */ + if (QDF_STATUS_E_FAILURE == pmc_status) { + hdd_status = + SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED; + /* we need to always notify this case */ + flow_info->hoRenewal = false; + } + } + break; + case SME_QOS_REASON_RELEASE: + ac_info->num_flows[tspec_index]--; + hdd_status = SME_QOS_STATUS_RELEASE_SUCCESS_RSP; + inform_hdd = true; + delete_entry = true; + break; + case SME_QOS_REASON_MODIFY: + delete_entry = true; + inform_hdd = false; + break; + case SME_QOS_REASON_MODIFY_PENDING: + hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND; + delete_entry = false; + flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; + inform_hdd = true; + psb = ac_info->requested_QoSInfo[tspec_index].ts_info.psb; + /* notify PMC if App is looking for APSD + */ + if (psb) { + /* this is the first flow to detect + * we need PMC in UAPSD mode + */ + pmc_status = sme_ps_start_uapsd(MAC_HANDLE(mac_ctx), + flow_info->sessionId); + /* if PMC doesn't return success right + * away means it is yet to put + * the module in BMPS state & later to UAPSD state + */ + if (QDF_STATUS_E_FAILURE == pmc_status) { + hdd_status = + SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED; + /* we need to always notify this case */ + flow_info->hoRenewal = false; + } + } else if (!psb && + ((ac_info->num_flows[flow_info->tspec_mask - 1] == 1) + && (SME_QOS_TSPEC_MASK_BIT_1_2_SET != + ac_info->tspec_mask_status))) { + /* this is the only TSPEC active on this AC */ + /* so indicate that we no longer require APSD */ + qos_session->apsdMask &= + ~(1 << (QCA_WLAN_AC_VO - ac)); + /* Also update modifyProfileFields.uapsd_mask + * in CSR for consistency + */ + csr_get_modify_profile_fields(mac_ctx, + flow_info->sessionId, + &profile_fields); + profile_fields.uapsd_mask = + qos_session->apsdMask; + csr_set_modify_profile_fields(mac_ctx, + flow_info->sessionId, + &profile_fields); + if (!qos_session->apsdMask) + sme_ps_uapsd_disable(MAC_HANDLE(mac_ctx), + flow_info->sessionId); + } + break; + case SME_QOS_REASON_REQ_SUCCESS: + hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND; + inform_hdd = true; + /* fallthrough */ + default: + delete_entry = false; + break; + } + if (inform_hdd) { + if (!flow_info->hoRenewal) { + flow_info->QoSCallback(MAC_HANDLE(mac_ctx), + flow_info->HDDcontext, + &ac_info->curr_QoSInfo[tspec_index], + hdd_status, + flow_info->QosFlowID); + } else + flow_info->hoRenewal = false; + } + if (delete_entry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("Deleting entry at %pK with flowID %d"), + flow_info, flow_info->QosFlowID); + /* delete the entry from Flow List */ + csr_ll_remove_entry(&sme_qos_cb.flow_list, entry, true); + /* reclaim the memory */ + qdf_mem_free(flow_info); + } + return QDF_STATUS_SUCCESS; +} + +/* + * sme_qos_is_rsp_pending() - Utility function to check if we are waiting + * for an AddTS or reassoc response on some AC other than the given AC + * + * sessionId - Session we are interted in + * ac - Enumeration of the various EDCA Access Categories. + * + * Return bool + * true - Response is pending on an AC + */ +static bool sme_qos_is_rsp_pending(uint8_t sessionId, enum qca_wlan_ac_type ac) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + enum qca_wlan_ac_type acIndex; + bool status = false; + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + for (acIndex = QCA_WLAN_AC_BE; acIndex < QCA_WLAN_AC_ALL; + acIndex++) { + if (acIndex == ac) + continue; + pACInfo = &pSession->ac_info[acIndex]; + if ((pACInfo->tspec_pending) || (pACInfo->reassoc_pending)) { + status = true; + break; + } + } + return status; +} + +/* + * sme_qos_update_hand_off() - Function which can be called to update + * Hand-off state of SME QoS Session + * + * sessionId - session id + * updateHandOff - value True/False to update the handoff flag + */ +void sme_qos_update_hand_off(uint8_t sessionId, bool updateHandOff) +{ + struct sme_qos_sessioninfo *pSession; + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: handoffRequested %d updateHandOff %d", + __func__, __LINE__, pSession->handoffRequested, + updateHandOff); + + pSession->handoffRequested = updateHandOff; + +} + +/* + * sme_qos_is_uapsd_active() - Function which can be called to determine + * if any sessions require PMC to be in U-APSD mode. + * Return bool + * + * Returns true if at least one session required PMC to be in U-APSD mode + * Returns false if no sessions require PMC to be in U-APSD mode + */ +static bool sme_qos_is_uapsd_active(void) +{ + struct sme_qos_sessioninfo *pSession; + uint8_t sessionId; + + for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; ++sessionId) { + pSession = &sme_qos_cb.sessionInfo[sessionId]; + if ((pSession->sessionActive) && (pSession->apsdMask)) + return true; + } + /* no active sessions have U-APSD active */ + return false; +} + +QDF_STATUS sme_offload_qos_process_out_of_uapsd_mode(struct mac_context *mac, + uint32_t sessionId) +{ + struct sme_qos_sessioninfo *pSession; + tListElem *pEntry = NULL, *pNextEntry = NULL; + struct sme_qos_flowinfoentry *flow_info = NULL; + + pEntry = csr_ll_peek_head(&sme_qos_cb.flow_list, false); + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Flow List empty, can't search", + __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + while (pEntry) { + pNextEntry = csr_ll_next(&sme_qos_cb.flow_list, pEntry, false); + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, + link); + pSession = &sme_qos_cb.sessionInfo[flow_info->sessionId]; + /* only notify the flows which already successfully setup + * UAPSD + */ + if ((sessionId == flow_info->sessionId) && + (flow_info->QoSInfo.max_service_interval || + flow_info->QoSInfo.min_service_interval) && + (SME_QOS_REASON_REQ_SUCCESS == flow_info->reason)) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info->HDDcontext, + &pSession->ac_info[flow_info-> + ac_type].curr_QoSInfo + [flow_info->tspec_mask - 1], + SME_QOS_STATUS_OUT_OF_APSD_POWER_MODE_IND, + flow_info->QosFlowID); + } + pEntry = pNextEntry; + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS sme_offload_qos_process_into_uapsd_mode(struct mac_context *mac, + uint32_t sessionId) +{ + struct sme_qos_sessioninfo *pSession; + tListElem *pEntry = NULL, *pNextEntry = NULL; + struct sme_qos_flowinfoentry *flow_info = NULL; + + pEntry = csr_ll_peek_head(&sme_qos_cb.flow_list, false); + if (!pEntry) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Flow List empty, can't search", + __func__, __LINE__); + return QDF_STATUS_E_FAILURE; + } + while (pEntry) { + pNextEntry = csr_ll_next(&sme_qos_cb.flow_list, pEntry, false); + flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, + link); + pSession = &sme_qos_cb.sessionInfo[flow_info->sessionId]; + /* only notify the flows which already successfully setup + * UAPSD + */ + if ((sessionId == flow_info->sessionId) && + (flow_info->QoSInfo.max_service_interval || + flow_info->QoSInfo.min_service_interval) && + (SME_QOS_REASON_REQ_SUCCESS == flow_info->reason)) { + flow_info->QoSCallback(MAC_HANDLE(mac), + flow_info->HDDcontext, + &pSession->ac_info[flow_info-> + ac_type].curr_QoSInfo + [flow_info->tspec_mask - 1], + SME_QOS_STATUS_INTO_APSD_POWER_MODE_IND, + flow_info->QosFlowID); + } + pEntry = pNextEntry; + } + return QDF_STATUS_SUCCESS; +} + +void sme_qos_cleanup_ctrl_blk_for_handoff(struct mac_context *mac, + uint8_t sessionId) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + enum qca_wlan_ac_type ac; + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("invoked on session %d"), sessionId); + + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + pACInfo = &pSession->ac_info[ac]; + qdf_mem_zero(pACInfo->curr_QoSInfo, + sizeof(struct sme_qos_wmmtspecinfo) * + SME_QOS_TSPEC_INDEX_MAX); + qdf_mem_zero(pACInfo->requested_QoSInfo, + sizeof(struct sme_qos_wmmtspecinfo) * + SME_QOS_TSPEC_INDEX_MAX); + pACInfo->num_flows[0] = 0; + pACInfo->num_flows[1] = 0; + pACInfo->reassoc_pending = false; + pACInfo->tspec_mask_status = 0; + pACInfo->tspec_pending = false; + pACInfo->hoRenewal = false; + pACInfo->prev_state = SME_QOS_LINK_UP; + } +} + +/** + * sme_qos_is_ts_info_ack_policy_valid() - check if ACK policy is allowed. + * @mac_handle: The handle returned by mac_open. + * @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the + * WMM TSPEC related info, provided by HDD + * @sessionId: sessionId returned by sme_open_session. + * + * The SME QoS API exposed to HDD to check if TS info ack policy field can be + * set to "HT-immediate block acknowledgment" + * + * Return: true - Current Association is HT association and so TS info ack + * policy can be set to "HT-immediate block acknowledgment" + */ +bool sme_qos_is_ts_info_ack_policy_valid(mac_handle_t mac_handle, + struct sme_qos_wmmtspecinfo *pQoSInfo, + uint8_t sessionId) +{ + tDot11fBeaconIEs *pIes = NULL; + struct sme_qos_sessioninfo *pSession; + QDF_STATUS hstatus; + struct mac_context *mac = MAC_CONTEXT(mac_handle); + + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Session Id %d is invalid", + __func__, __LINE__, sessionId); + return false; + } + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + + if (!pSession->sessionActive) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Session %d is inactive", + __func__, __LINE__, sessionId); + return false; + } + + if (!pSession->assocInfo.bss_desc) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: Session %d has an Invalid BSS Descriptor", + __func__, __LINE__, sessionId); + return false; + } + + hstatus = csr_get_parsed_bss_description_ies(mac, + pSession->assocInfo.bss_desc, + &pIes); + if (!QDF_IS_STATUS_SUCCESS(hstatus)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d unable to parse BSS IEs", + __func__, __LINE__, sessionId); + return false; + } + + /* success means pIes was allocated */ + + if (!pIes->HTCaps.present && + pQoSInfo->ts_info.ack_policy == + SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: On session %d HT Caps aren't present but application set ack policy to HT ", + __func__, __LINE__, sessionId); + + qdf_mem_free(pIes); + return false; + } + + qdf_mem_free(pIes); + return true; +} + +static bool sme_qos_validate_requested_params(struct mac_context *mac, + struct sme_qos_wmmtspecinfo *qos_info, + uint8_t session_id) +{ + if (SME_QOS_WMM_TS_DIR_RESV == qos_info->ts_info.direction) + return false; + if (!sme_qos_is_ts_info_ack_policy_valid(MAC_HANDLE(mac), + qos_info, session_id)) + return false; + + return true; +} + +static QDF_STATUS qos_issue_command(struct mac_context *mac, uint8_t vdev_id, + eSmeCommandType cmdType, + struct sme_qos_wmmtspecinfo *pQoSInfo, + enum qca_wlan_ac_type ac, uint8_t tspec_mask) +{ + QDF_STATUS status = QDF_STATUS_E_RESOURCES; + tSmeCmd *pCommand = NULL; + + do { + pCommand = csr_get_command_buffer(mac); + if (!pCommand) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: fail to get command buffer for command %d", + __func__, __LINE__, cmdType); + break; + } + pCommand->command = cmdType; + pCommand->vdev_id = vdev_id; + switch (cmdType) { + case eSmeCommandAddTs: + if (pQoSInfo) { + status = QDF_STATUS_SUCCESS; + pCommand->u.qosCmd.tspecInfo = *pQoSInfo; + pCommand->u.qosCmd.ac = ac; + } else { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "%s: %d: NULL pointer passed", + __func__, __LINE__); + status = QDF_STATUS_E_INVAL; + } + break; + case eSmeCommandDelTs: + status = QDF_STATUS_SUCCESS; + pCommand->u.qosCmd.ac = ac; + pCommand->u.qosCmd.tspec_mask = tspec_mask; + break; + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid command type %d", + __func__, __LINE__, cmdType); + status = QDF_STATUS_E_INVAL; + break; + } + } while (0); + if (QDF_IS_STATUS_SUCCESS(status) && pCommand) + csr_queue_sme_command(mac, pCommand, false); + else if (pCommand) + qos_release_command(mac, pCommand); + + return status; +} + +bool qos_process_command(struct mac_context *mac, tSmeCmd *pCommand) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool fRemoveCmd = true; + + do { + switch (pCommand->command) { + case eSmeCommandAddTs: + status = + sme_qos_add_ts_req(mac, (uint8_t) + pCommand->vdev_id, + &pCommand->u.qosCmd.tspecInfo, + pCommand->u.qosCmd.ac); + if (QDF_IS_STATUS_SUCCESS(status)) + fRemoveCmd = false; + break; + case eSmeCommandDelTs: + status = + sme_qos_del_ts_req(mac, (uint8_t) + pCommand->vdev_id, + pCommand->u.qosCmd.ac, + pCommand->u.qosCmd.tspec_mask); + if (QDF_IS_STATUS_SUCCESS(status)) + fRemoveCmd = false; + break; + default: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "%s: %d: invalid command type %d", + __func__, __LINE__, pCommand->command); + break; + } /* switch */ + } while (0); + return fRemoveCmd; +} + +/** + * sme_qos_re_request_add_ts - Re-send AddTS for the combined QoS request + * + * @mac_ctx Pointer to mac context + * @session_id SME session id + * @qos_info - Tspec information + * @ac - Access category + * @tspec_mask - Tspec Mask + * + * This function is called to re-send AddTS for the combined QoS request + * + * Return: status + */ +static +enum sme_qos_statustype sme_qos_re_request_add_ts(struct mac_context *mac_ctx, + uint8_t session_id, struct sme_qos_wmmtspecinfo *qos_info, + enum qca_wlan_ac_type ac, uint8_t tspec_mask) +{ + struct sme_qos_sessioninfo *session; + struct sme_qos_acinfo *ac_info; + enum sme_qos_statustype status = + SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + struct sme_qos_cmdinfo cmd; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL(" Invoked on session %d for AC %d TSPEC %d"), + session_id, ac, tspec_mask); + session = &sme_qos_cb.sessionInfo[session_id]; + ac_info = &session->ac_info[ac]; + /* + * call PMC's request for power function + * AND another check is added considering the flowing scenario + * Addts reqest is pending on one AC, while APSD requested on + * another which needs a reassoc. Will buffer a request if Addts + * is pending on any AC, which will safegaurd the above scenario, + * 2& also won't confuse PE with back to back Addts or Addts + * followed by Reassoc. + */ + if (sme_qos_is_rsp_pending(session_id, ac)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + "On session %d buffering the AddTS request for AC %d in state %d as Addts is pending on other AC or waiting for full power", + session_id, ac, ac_info->curr_state); + /* buffer cmd */ + cmd.command = SME_QOS_RESEND_REQ; + cmd.mac = mac_ctx; + cmd.sessionId = session_id; + cmd.u.resendCmdInfo.ac = ac; + cmd.u.resendCmdInfo.tspecMask = tspec_mask; + cmd.u.resendCmdInfo.QoSInfo = *qos_info; + if (!QDF_IS_STATUS_SUCCESS(sme_qos_buffer_cmd(&cmd, false))) { + QDF_TRACE(QDF_MODULE_ID_SME, + QDF_TRACE_LEVEL_ERROR, + "On session %d unable to buffer the AddTS request for AC %d TSPEC %d in state %d", + session_id, ac, tspec_mask, + ac_info->curr_state); + return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + } + return SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; + } + + /* get into the stat m/c to see if the request can be granted */ + switch (ac_info->curr_state) { + case SME_QOS_QOS_ON: + { + /* if ACM, send out a new ADDTS */ + ac_info->hoRenewal = true; + status = sme_qos_setup(mac_ctx, session_id, qos_info, ac); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("sme_qos_setup returned in SME_QOS_QOS_ON state")); + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("sme_qos_setup AC %d with status =%d"), ac, status); + if (SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) { + status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; + ac_info->tspec_pending = tspec_mask; + } else if ((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == + status) || + (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == + status) || + (SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING == + status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("UAPSD is setup already status = %d "), + status); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_qos_setup return status = %d "), + status); + } + } + break; + case SME_QOS_HANDOFF: + case SME_QOS_REQUESTED: + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Re-Add request in state = %d buffer the request"), + ac_info->curr_state); + cmd.command = SME_QOS_RESEND_REQ; + cmd.mac = mac_ctx; + cmd.sessionId = session_id; + cmd.u.resendCmdInfo.ac = ac; + cmd.u.resendCmdInfo.tspecMask = tspec_mask; + cmd.u.resendCmdInfo.QoSInfo = *qos_info; + if (!QDF_IS_STATUS_SUCCESS(sme_qos_buffer_cmd(&cmd, false))) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL(" couldn't buf the read request state = %d"), + ac_info->curr_state); + return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; + } + status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; + break; + case SME_QOS_CLOSED: + case SME_QOS_INIT: + case SME_QOS_LINK_UP: + default: + /* print error msg, */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("ReAdd request in unexpected state = %d"), + ac_info->curr_state); + break; + } + if ((SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == + status) || + (SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY == + status)) + (void)sme_qos_process_buffered_cmd(session_id); + + return status; +} + +static void sme_qos_init_a_cs(struct mac_context *mac, uint8_t sessionId) +{ + struct sme_qos_sessioninfo *pSession; + enum qca_wlan_ac_type ac; + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + qdf_mem_zero(&pSession->ac_info[ac], + sizeof(struct sme_qos_acinfo)); + sme_qos_state_transition(sessionId, ac, SME_QOS_INIT); + } +} + +static QDF_STATUS sme_qos_request_reassoc(struct mac_context *mac, + uint8_t sessionId, + tCsrRoamModifyProfileFields * + pModFields, bool fForce) +{ + struct sme_qos_sessioninfo *pSession; + struct sme_qos_acinfo *pACInfo; + QDF_STATUS status; + struct csr_roam_session *session; + tCsrRoamConnectedProfile connected_profile; + struct csr_roam_profile *roam_profile; + bool roam_offload_enable = true; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Invoked on session %d with UAPSD mask 0x%X", + __func__, __LINE__, sessionId, pModFields->uapsd_mask); + + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + sme_err("Invalid session for sessionId: %d", sessionId); + return QDF_STATUS_E_FAILURE; + } + + pSession = &sme_qos_cb.sessionInfo[sessionId]; + status = ucfg_mlme_get_roaming_offload(mac->psoc, &roam_offload_enable); + if (QDF_IS_STATUS_ERROR(status)) + return status; + if (roam_offload_enable) { + session = CSR_GET_SESSION(mac, sessionId); + roam_profile = session->pCurRoamProfile; + connected_profile = session->connectedProfile; + status = sme_fast_reassoc(MAC_HANDLE(mac), roam_profile, + connected_profile.bssid.bytes, + connected_profile.op_freq, + sessionId, + connected_profile.bssid.bytes); + } else { + status = csr_reassoc(mac, sessionId, pModFields, + &pSession->roamID, fForce); + } + + if (QDF_IS_STATUS_SUCCESS(status)) { + /* Update the state to Handoff so subsequent requests are + * queued until this one is finished + */ + enum qca_wlan_ac_type ac; + + for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) { + pACInfo = &pSession->ac_info[ac]; + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: AC[%d] is in state [%d]", + __func__, __LINE__, ac, pACInfo->curr_state); + /* If it is already in HANDOFF state, don't do + * anything since we MUST preserve the previous state + * and sme_qos_state_transition will change the previous + * state + */ + if (SME_QOS_HANDOFF != pACInfo->curr_state) + sme_qos_state_transition(sessionId, ac, + SME_QOS_HANDOFF); + } + } + return status; +} + +static uint32_t sme_qos_assign_flow_id(void) +{ + uint32_t flowId; + + flowId = sme_qos_cb.nextFlowId; + if (SME_QOS_MAX_FLOW_ID == flowId) { + /* The Flow ID wrapped. This is obviously not a real life + * scenario but handle it to keep the software test folks happy + */ + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + "%s: %d: Software Test made the flow counter wrap, QoS may no longer be functional", + __func__, __LINE__); + sme_qos_cb.nextFlowId = SME_QOS_MIN_FLOW_ID; + } else + sme_qos_cb.nextFlowId++; + + return flowId; +} + +static uint8_t sme_qos_assign_dialog_token(void) +{ + uint8_t token; + + token = sme_qos_cb.nextDialogToken; + if (SME_QOS_MAX_DIALOG_TOKEN == token) + /* wrap is ok */ + sme_qos_cb.nextDialogToken = SME_QOS_MIN_DIALOG_TOKEN; + else + sme_qos_cb.nextDialogToken++; + + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG, + FL("token %d"), token); + return token; +} +#endif /* WLAN_MDM_CODE_REDUCTION_OPT */ diff --git a/drivers/staging/qcacld-3.0/core/sme/src/rrm/sme_rrm.c b/drivers/staging/qcacld-3.0/core/sme/src/rrm/sme_rrm.c new file mode 100644 index 0000000000000000000000000000000000000000..c9eec015039beadc3d7ff2897a5d9dfd42ca37f1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/sme/src/rrm/sme_rrm.c @@ -0,0 +1,1920 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DOC: sme_rrm.c + * + * Implementation for SME RRM APIs + */ + +#include "ani_global.h" +#include "sme_inside.h" +#include "sme_api.h" + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +#include "host_diag_core_event.h" +#include "host_diag_core_log.h" +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +#include "csr_inside_api.h" + +#include "rrm_global.h" +#include +#include +#include +#include + +/* Roam score for a neighbor AP will be calculated based on the below + * definitions. The calculated roam score will be used to select the + * roamable candidate from neighbor AP list + */ +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_REACHABILITY 0 +/* When we support 11r over the DS, this should have a non-zero value */ +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_SECURITY 10 +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_KEY_SCOPE 20 +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_SPECTRUM_MGMT 0 +/* Not used */ +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_QOS 5 +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_APSD 3 +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_RRM 8 +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_DELAYED_BA 0 +/* We dont support delayed BA */ +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_IMMEDIATE_BA 3 +#define RRM_ROAM_SCORE_NEIGHBOR_REPORT_MOBILITY_DOMAIN 30 + +#ifdef FEATURE_WLAN_ESE +#define RRM_ROAM_SCORE_NEIGHBOR_IAPP_LIST 30 +#endif +/* RRM SCAN DWELL TIME */ +#define RRM_SCAN_MIN_DWELL_TIME 20 + +uint64_t rrm_scan_timer; + +/** + * rrm_ll_purge_neighbor_cache() -Purges all the entries in the neighbor cache + * + * @mac: Pointer to the Hal Handle. + * @pList: Pointer the List that should be purged. + * + * This function purges all the entries in the neighbor cache and frees up all + * the internal nodes + * + * Return: void + */ +static void rrm_ll_purge_neighbor_cache(struct mac_context *mac, + tDblLinkList *pList) +{ + tListElem *pEntry; + tRrmNeighborReportDesc *pNeighborReportDesc; + + csr_ll_lock(pList); + while ((pEntry = csr_ll_remove_head(pList, LL_ACCESS_NOLOCK)) != NULL) { + pNeighborReportDesc = + GET_BASE_ADDR(pEntry, tRrmNeighborReportDesc, List); + qdf_mem_free(pNeighborReportDesc->pNeighborBssDescription); + qdf_mem_free(pNeighborReportDesc); + } + csr_ll_unlock(pList); +} + +/** + * rrm_indicate_neighbor_report_result() -calls the callback registered for + * neighbor report + * @mac: Pointer to the Hal Handle. + * @qdf_status - QDF_STATUS_SUCCESS/QDF_STATUS_FAILURE based on whether a valid + * report is received or neighbor timer expired + * + * This function calls the callback register by the caller while requesting for + * neighbor report. This function gets invoked if a neighbor report is received + * from an AP or neighbor response wait timer expires. + * + * Return: void + */ +static void rrm_indicate_neighbor_report_result(struct mac_context *mac, + QDF_STATUS qdf_status) +{ + NeighborReportRspCallback callback; + void *callbackContext; + + /* Reset the neighbor response pending status */ + mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX]. + neighborReqControlInfo.isNeighborRspPending = false; + + /* Stop the timer if it is already running. + * The timer should be running only in the SUCCESS case. + */ + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX]. + neighborReqControlInfo. + neighborRspWaitTimer)) { + sme_debug("No entry in neighbor report cache"); + qdf_mc_timer_stop(&mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX]. + neighborReqControlInfo.neighborRspWaitTimer); + } + callback = + mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX].neighborReqControlInfo. + neighborRspCallbackInfo.neighborRspCallback; + callbackContext = + mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX].neighborReqControlInfo. + neighborRspCallbackInfo.neighborRspCallbackContext; + + /* Reset the callback and the callback context before calling the + * callback. It is very likely that there may be a registration in + * callback itself. + */ + mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX].neighborReqControlInfo. + neighborRspCallbackInfo.neighborRspCallback = NULL; + mac->rrm.rrmSmeContext[DEFAULT_RRM_IDX].neighborReqControlInfo. + neighborRspCallbackInfo.neighborRspCallbackContext = NULL; + + /* Call the callback with the status received from caller */ + if (callback) + callback(callbackContext, qdf_status); +} + +/** + * sme_RrmBeaconReportXmitInd () - Send beacon report + * @mac_ctx Pointer to mac context + * @measurement_index: Measurement index + * @result_arr scan results + * @msrmnt_status flag to indicate that the measurement is done. + * @bss_count bss count + * + * Create and send the beacon report Xmit ind message to PE. + * + * Return: status + */ + +static QDF_STATUS +sme_rrm_send_beacon_report_xmit_ind(struct mac_context *mac_ctx, + uint8_t measurement_index, tCsrScanResultInfo **result_arr, + uint8_t msrmnt_status, uint8_t bss_count) +{ + struct bss_description *bss_desc = NULL; + tpSirBeaconReportXmitInd beacon_rep; + uint16_t length; + uint32_t size; + uint8_t i = 0, j = 0, counter = 0; + tCsrScanResultInfo *cur_result = NULL; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + tpRrmSMEContext rrm_ctx = + &mac_ctx->rrm.rrmSmeContext[measurement_index]; + struct bss_description *tmp_bss_desc[SIR_BCN_REPORT_MAX_BSS_DESC] = {0}; + + if (!result_arr && !msrmnt_status) { + sme_err("Beacon report xmit Ind to PE Failed"); + return QDF_STATUS_E_FAILURE; + } + + if (result_arr) + cur_result = result_arr[j]; + + do { + length = sizeof(tSirBeaconReportXmitInd); + beacon_rep = qdf_mem_malloc(length); + if (!beacon_rep) + return QDF_STATUS_E_NOMEM; + + beacon_rep->messageType = eWNI_SME_BEACON_REPORT_RESP_XMIT_IND; + beacon_rep->length = length; + beacon_rep->measurement_idx = measurement_index; + beacon_rep->uDialogToken = rrm_ctx->token; + beacon_rep->duration = rrm_ctx->duration[0]; + beacon_rep->regClass = rrm_ctx->regClass; + qdf_mem_copy(beacon_rep->bssId, rrm_ctx->sessionBssId.bytes, + QDF_MAC_ADDR_SIZE); + + i = 0; + while (cur_result) { + bss_desc = &cur_result->BssDescriptor; + if (!bss_desc) + break; + size = bss_desc->length + sizeof(bss_desc->length); + beacon_rep->pBssDescription[i] = qdf_mem_malloc(size); + if (NULL == + beacon_rep->pBssDescription[i]) + break; + qdf_mem_copy(beacon_rep->pBssDescription[i], + bss_desc, size); + tmp_bss_desc[i] = + beacon_rep->pBssDescription[i]; + sme_debug("RRM Result Bssid = " QDF_MAC_ADDR_FMT + " freq= %d, rssi = -%d", + QDF_MAC_ADDR_REF( + beacon_rep->pBssDescription[i]->bssId), + beacon_rep->pBssDescription[i]->chan_freq, + beacon_rep->pBssDescription[i]->rssi * (-1)); + beacon_rep->numBssDesc++; + if (++i >= SIR_BCN_REPORT_MAX_BSS_DESC) + break; + if (i + j >= bss_count) + break; + cur_result = + result_arr[j + i]; + } + + j += i; + if (!result_arr || (!cur_result) + || (j >= bss_count)) { + cur_result = NULL; + sme_debug("Reached to max/last BSS in cur_result list"); + } else { + cur_result = result_arr[j]; + sme_debug("Move to the next BSS set in cur_result list"); + } + beacon_rep->fMeasureDone = + (cur_result) ? false : msrmnt_status; + sme_debug("SME Sending BcnRepXmit to PE numBss %d i %d j %d", + beacon_rep->numBssDesc, i, j); + status = umac_send_mb_message_to_mac(beacon_rep); + if (status != QDF_STATUS_SUCCESS) + for (counter = 0; counter < i; ++counter) + qdf_mem_free(tmp_bss_desc[counter]); + } while (cur_result); + + return status; +} + +#ifdef FEATURE_WLAN_ESE +/** + * sme_ese_send_beacon_req_scan_results () - Send beacon report + * @mac_ctx: Pointer to mac context + * @measurement_index: Measurement request index + * @session_id: session id + * @freq: channel frequency + * @result_arr: scan results + * @msrmnt_status: flag to indicate that the measurement is done. + * @bss_count: number of bss found + * + * This function sends up the scan results received as a part of + * beacon request scanning. + * This function is called after receiving the scan results per channel + * Due to the limitation on the size of the IWEVCUSTOM buffer, we send + * 3 BSSIDs of beacon report information in one custom event; + * + * Return: status + */ +static QDF_STATUS sme_ese_send_beacon_req_scan_results( + struct mac_context *mac_ctx, uint8_t measurement_index, + uint32_t session_id, uint32_t freq, + tCsrScanResultInfo **result_arr, + uint8_t msrmnt_status, uint8_t bss_count) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + QDF_STATUS fill_ie_status; + struct bss_description *bss_desc = NULL; + uint32_t ie_len = 0; + uint32_t out_ie_len = 0; + uint8_t bss_counter = 0; + tCsrScanResultInfo *cur_result = NULL; + tpRrmSMEContext rrm_ctx = + &mac_ctx->rrm.rrmSmeContext[measurement_index]; + struct csr_roam_info *roam_info; + struct ese_bcn_report_rsp bcn_rpt_rsp; + struct ese_bcn_report_rsp *bcn_report = &bcn_rpt_rsp; + tpCsrEseBeaconReqParams cur_meas_req = NULL; + uint8_t i = 0, j = 0; + tBcnReportFields *bcn_rpt_fields; + + if (!rrm_ctx) { + sme_err("rrm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (!result_arr && !msrmnt_status) { + sme_err("Beacon report xmit Ind to HDD Failed"); + return QDF_STATUS_E_FAILURE; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return QDF_STATUS_E_NOMEM; + + if (result_arr) + cur_result = result_arr[bss_counter]; + + do { + cur_meas_req = NULL; + /* memset bcn_rpt_rsp for each iteration */ + qdf_mem_zero(&bcn_rpt_rsp, sizeof(bcn_rpt_rsp)); + + for (i = 0; i < rrm_ctx->eseBcnReqInfo.numBcnReqIe; i++) { + if (rrm_ctx->eseBcnReqInfo.bcnReq[i].ch_freq == freq) { + cur_meas_req = + &rrm_ctx->eseBcnReqInfo.bcnReq[i]; + break; + } + } + if (cur_meas_req) + bcn_report->measurementToken = + cur_meas_req->measurementToken; + sme_debug("freq: %d MeasToken: %d", freq, + bcn_report->measurementToken); + + j = 0; + while (cur_result) { + bss_desc = &cur_result->BssDescriptor; + if (!bss_desc) { + cur_result = NULL; + break; + } + ie_len = GET_IE_LEN_IN_BSS(bss_desc->length); + bcn_rpt_fields = + &bcn_report->bcnRepBssInfo[j].bcnReportFields; + bcn_rpt_fields->ChanNum = wlan_reg_freq_to_chan( + mac_ctx->pdev, + bss_desc->chan_freq); + bcn_report->bcnRepBssInfo[j].bcnReportFields.Spare = 0; + if (cur_meas_req) + bcn_rpt_fields->MeasDuration = + cur_meas_req->measurementDuration; + bcn_rpt_fields->PhyType = bss_desc->nwType; + bcn_rpt_fields->RecvSigPower = bss_desc->rssi; + bcn_rpt_fields->ParentTsf = bss_desc->parentTSF; + bcn_rpt_fields->TargetTsf[0] = bss_desc->timeStamp[0]; + bcn_rpt_fields->TargetTsf[1] = bss_desc->timeStamp[1]; + bcn_rpt_fields->BcnInterval = bss_desc->beaconInterval; + bcn_rpt_fields->CapabilityInfo = + bss_desc->capabilityInfo; + + qdf_mem_copy(bcn_rpt_fields->Bssid, + bss_desc->bssId, sizeof(tSirMacAddr)); + fill_ie_status = + sir_beacon_ie_ese_bcn_report(mac_ctx, + (uint8_t *) bss_desc->ieFields, + ie_len, + &(bcn_report->bcnRepBssInfo[j]. + pBuf), + &out_ie_len); + if (QDF_STATUS_E_FAILURE == fill_ie_status) + continue; + bcn_report->bcnRepBssInfo[j].ieLen = out_ie_len; + + sme_debug("Bssid"QDF_MAC_ADDR_FMT" Freq:%d Rssi:%d", + QDF_MAC_ADDR_REF(bss_desc->bssId), + bss_desc->chan_freq, (-1) * bss_desc->rssi); + bcn_report->numBss++; + if (++j >= SIR_BCN_REPORT_MAX_BSS_DESC) + break; + if ((bss_counter + j) >= bss_count) + break; + cur_result = result_arr[bss_counter + j]; + } + + bss_counter += j; + if (!result_arr || !cur_result || (bss_counter >= bss_count)) { + cur_result = NULL; + sme_err("Reached to the max/last BSS in cur_result list"); + } else { + cur_result = result_arr[bss_counter]; + sme_err("Move to the next BSS set in cur_result list"); + } + + bcn_report->flag = + (msrmnt_status << 1) | ((cur_result) ? true : false); + + sme_debug("SME Sending BcnRep to HDD numBss: %d j: %d bss_counter: %d flag: %d", + bcn_report->numBss, j, bss_counter, + bcn_report->flag); + + roam_info->pEseBcnReportRsp = bcn_report; + status = csr_roam_call_callback(mac_ctx, session_id, roam_info, + 0, eCSR_ROAM_ESE_BCN_REPORT_IND, + 0); + + /* Free the memory allocated to IE */ + for (i = 0; i < j; i++) + if (bcn_report->bcnRepBssInfo[i].pBuf) + qdf_mem_free(bcn_report->bcnRepBssInfo[i].pBuf); + } while (cur_result); + qdf_mem_free(roam_info); + return status; +} + +static inline +void sme_reset_ese_bcn_req_in_progress(tpRrmSMEContext sme_rrm_ctx) +{ + if (sme_rrm_ctx) + sme_rrm_ctx->eseBcnReqInProgress = false; +} + +#else + +static inline +void sme_reset_ese_bcn_req_in_progress(tpRrmSMEContext sme_rrm_ctx) +{} +#endif /* FEATURE_WLAN_ESE */ + +/** + * sme_rrm_send_scan_result() - to get scan result and send the beacon report + * @mac_ctx: pointer to mac context + * @measurement_index: Measurement request number + * @num_chan: number of channels + * @freq_list: list of channel frequencies to fetch the result from + * @measurementdone: Flag to indicate measurement done or no + * + * This function is called to get the scan result from CSR and send the beacon + * report xmit ind message to PE + * + * Return: QDF_STATUS + */ +static QDF_STATUS sme_rrm_send_scan_result(struct mac_context *mac_ctx, + uint8_t measurement_index, + uint8_t num_chan, + uint32_t *freq_list, + uint8_t measurementdone) +{ + mac_handle_t mac_handle = MAC_HANDLE(mac_ctx); + struct scan_filter *filter; + tScanResultHandle result_handle; + tCsrScanResultInfo *scan_results, *next_result; + tCsrScanResultInfo **scanresults_arr = NULL; + struct scan_result_list *result_list; + QDF_STATUS status; + uint8_t num_scan_results, counter = 0; + tpRrmSMEContext rrm_ctx = + &mac_ctx->rrm.rrmSmeContext[measurement_index]; + uint32_t session_id; + struct csr_roam_info *roam_info = NULL; + tSirScanType scan_type; + struct csr_roam_session *session; + + filter = qdf_mem_malloc(sizeof(*filter)); + if (!filter) + return QDF_STATUS_E_NOMEM; + + if (qdf_is_macaddr_zero(filter->bssid_list) || + qdf_is_macaddr_group(filter->bssid_list)) { + filter->num_of_bssid = 0; + } else { + /* update filter to get scan result with just target BSSID */ + filter->num_of_bssid = 1; + qdf_mem_copy(filter->bssid_list[0].bytes, + rrm_ctx->bssId, sizeof(struct qdf_mac_addr)); + } + + if (rrm_ctx->ssId.length) { + filter->num_of_ssid = 1; + filter->ssid_list[0].length = rrm_ctx->ssId.length; + if (filter->ssid_list[0].length > WLAN_SSID_MAX_LEN) + filter->ssid_list[0].length = WLAN_SSID_MAX_LEN; + qdf_mem_copy(filter->ssid_list[0].ssid, + rrm_ctx->ssId.ssId, filter->ssid_list[0].length); + } + + filter->num_of_channels = num_chan; + if (filter->num_of_channels > NUM_CHANNELS) + filter->num_of_channels = NUM_CHANNELS; + qdf_mem_copy(filter->chan_freq_list, freq_list, + filter->num_of_channels * + sizeof(filter->chan_freq_list[0])); + filter->rrm_measurement_filter = true; + + if (eRRM_MSG_SOURCE_ESE_UPLOAD == rrm_ctx->msgSource || + eRRM_MSG_SOURCE_LEGACY_ESE == rrm_ctx->msgSource) + scan_type = rrm_ctx->measMode[rrm_ctx->currentIndex]; + else + scan_type = rrm_ctx->measMode[0]; + + if (scan_type == eSIR_BEACON_TABLE) + filter->age_threshold = + wlan_scan_get_aging_time(mac_ctx->psoc); + + + /* + * In case this is beacon report request from last AP (before roaming) + * following call to csr_roam_get_session_id_from_bssid will fail, + * hence use current session ID instead of one stored in SME rrm context + */ + if (QDF_STATUS_E_FAILURE == csr_roam_get_session_id_from_bssid(mac_ctx, + &rrm_ctx->sessionBssId, &session_id)) { + sme_debug("BSSID mismatch, using current session_id"); + session_id = mac_ctx->roam.roamSession->sessionId; + } + status = sme_scan_get_result(mac_handle, (uint8_t)session_id, + filter, &result_handle); + qdf_mem_free(filter); + + sme_debug("RRM Measurement Done %d for index:%d", + measurementdone, measurement_index); + if (!result_handle) { + /* + * no scan results + * Spec. doesn't say anything about such condition + * Since section 7.4.6.2 (IEEE802.11k-2008) says-rrm report + * frame should contain one or more report IEs. It probably + * means dont send any respose if no matching BSS found. + * Moreover, there is no flag or field in measurement report + * IE(7.3.2.22) OR beacon report IE(7.3.2.22.6) that can be set + * to indicate no BSS found on a given channel. If we finished + * measurement on all the channels, we still need to send a + * xmit indication with moreToFollow set to MEASURMENT_DONE so + * that PE can clean any context allocated. + */ + if (!measurementdone) + return status; +#ifdef FEATURE_WLAN_ESE + if (eRRM_MSG_SOURCE_ESE_UPLOAD == rrm_ctx->msgSource) + status = sme_ese_send_beacon_req_scan_results(mac_ctx, + measurement_index, session_id, + freq_list[0], NULL, + measurementdone, 0); + else +#endif /* FEATURE_WLAN_ESE */ + status = sme_rrm_send_beacon_report_xmit_ind(mac_ctx, + measurement_index, NULL, + measurementdone, 0); + return status; + } + scan_results = sme_scan_result_get_first(mac_handle, result_handle); + if (!scan_results && measurementdone) { +#ifdef FEATURE_WLAN_ESE + if (eRRM_MSG_SOURCE_ESE_UPLOAD == rrm_ctx->msgSource) { + status = sme_ese_send_beacon_req_scan_results(mac_ctx, + measurement_index, session_id, + freq_list[0], NULL, + measurementdone, 0); + } else +#endif /* FEATURE_WLAN_ESE */ + status = sme_rrm_send_beacon_report_xmit_ind(mac_ctx, + measurement_index, + NULL, measurementdone, 0); + } + + result_list = (struct scan_result_list *)result_handle; + num_scan_results = csr_ll_count(&result_list->List); + if (!num_scan_results) { + sme_err("num_scan_results is %d", num_scan_results); + status = QDF_STATUS_E_FAILURE; + goto rrm_send_scan_results_done; + } + + sme_debug("num_scan_results %d", num_scan_results); + scanresults_arr = qdf_mem_malloc(num_scan_results * + sizeof(next_result)); + if (!scanresults_arr) { + status = QDF_STATUS_E_NOMEM; + goto rrm_send_scan_results_done; + } + + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) { + status = QDF_STATUS_E_NOMEM; + goto rrm_send_scan_results_done; + } + + session = CSR_GET_SESSION(mac_ctx, session_id); + if ((!session) || (!csr_is_conn_state_connected_infra( + mac_ctx, session_id)) || + (!session->pConnectBssDesc)) { + sme_err("Invaild session"); + status = QDF_STATUS_E_FAILURE; + goto rrm_send_scan_results_done; + } + + + while (scan_results) { + /* + * In passive scan, sta listens beacon. Connected AP beacon + * is offloaded to firmware. Firmware will discard + * connected AP beacon except that special IE exists. + * Connected AP beacon will not be sent to host. Hence, timer + * of connected AP in scan results is not updated and can + * not meet "pScanResult->timer >= RRM_scan_timer". + */ + uint8_t is_conn_bss_found = false; + + if ((scan_type == eSIR_PASSIVE_SCAN) && + (!qdf_mem_cmp(scan_results->BssDescriptor.bssId, + session->pConnectBssDesc->bssId, + sizeof(struct qdf_mac_addr)))) { + is_conn_bss_found = true; + sme_debug("Connected BSS in scan results"); + } + next_result = sme_scan_result_get_next(mac_handle, + result_handle); + sme_debug("Scan res timer:%lu, rrm scan timer:%llu", + scan_results->timer, rrm_scan_timer); + if ((scan_results->timer >= rrm_scan_timer) || + (is_conn_bss_found == true)) { + roam_info->bss_desc = &scan_results->BssDescriptor; + csr_roam_call_callback(mac_ctx, session_id, roam_info, + 0, eCSR_ROAM_UPDATE_SCAN_RESULT, + eCSR_ROAM_RESULT_NONE); + scanresults_arr[counter++] = scan_results; + } + scan_results = next_result; + if (counter >= num_scan_results) + break; + } + /* + * The beacon report should be sent whether the counter is zero or + * non-zero. There might be a few scan results in the cache but not + * actually are a result of this scan. During that scenario, the + * counter will be zero. The report should be sent and LIM will further + * cleanup the RRM to accept the further incoming requests + * In case the counter is Zero, the pScanResultsArr will be NULL. + * The next level routine does a check for the measurementDone to + * determine whether to send a report or not. + */ + sme_debug("Number of BSS Desc with RRM Scan %d", counter); + if (counter || measurementdone) { +#ifdef FEATURE_WLAN_ESE + if (eRRM_MSG_SOURCE_ESE_UPLOAD == rrm_ctx->msgSource) + status = sme_ese_send_beacon_req_scan_results(mac_ctx, + measurement_index, session_id, + freq_list[0], scanresults_arr, + measurementdone, counter); + else +#endif /* FEATURE_WLAN_ESE */ + status = sme_rrm_send_beacon_report_xmit_ind(mac_ctx, + measurement_index, scanresults_arr, + measurementdone, counter); + } + +rrm_send_scan_results_done: + if (scanresults_arr) + qdf_mem_free(scanresults_arr); + qdf_mem_free(roam_info); + sme_scan_result_purge(result_handle); + + return status; +} + + +/** + * sme_rrm_scan_request_callback() -Sends the beacon report xmit to PE + * @mac_handle: Opaque handle to the MAC context + * @pSmeRrmContext: SME rrm context for measurement request + * @sessionId: session id + * @scanId: Scan ID. + * @status: CSR Status. + * + * The sme module calls this callback function once it finish the scan request + * and this function send the beacon report xmit to PE and starts a timer of + * random interval to issue next request. + * + * Return : 0 for success, non zero for failure + */ +static QDF_STATUS sme_rrm_scan_request_callback(struct mac_context *mac, + tpRrmSMEContext pSmeRrmContext, + uint8_t sessionId, + uint32_t scanId, + eCsrScanStatus status) +{ + uint16_t interval; + uint32_t time_tick; + QDF_STATUS qdf_status; + uint32_t session_id; + bool valid_result = true; + uint8_t ch_idx, num_chan; + uint32_t *freq_list; + + /* + * RRM scan response received after roaming to different AP. + * Post message to PE for rrm cleanup. + */ + qdf_status = csr_roam_get_session_id_from_bssid(mac, + &pSmeRrmContext->sessionBssId, + &session_id); + if (qdf_status == QDF_STATUS_E_FAILURE) { + sme_debug("Cleanup RRM context due to STA roaming"); + valid_result = false; + } + + /* if any more channels are pending, start a timer of a random value + * within randomization interval. + */ + freq_list = pSmeRrmContext->channelList.freq_list; + if (!freq_list) { + sme_err("[802.11 RRM]: Global freq list is null"); + pSmeRrmContext->channelList.numOfChannels = 0; + sme_reset_ese_bcn_req_in_progress(pSmeRrmContext); + return QDF_STATUS_E_FAILURE; + } + + ch_idx = pSmeRrmContext->currentIndex; + num_chan = pSmeRrmContext->channelList.numOfChannels; + if (((ch_idx + 1) < num_chan) && valid_result) { + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state( + &pSmeRrmContext->IterMeasTimer)) { + /* + * Measurement random timer is already running, this + * should not happen because the driver doesn't support + * multiple measurements simultaneously. Also for + * multiple measurements on a single report, the + * channels in op class should be appended to the global + * frequency list + */ + sme_err("[802.11 RRM]: meas timer is already running"); + sme_rrm_send_scan_result(mac, + pSmeRrmContext->measurement_idx, + 1, &freq_list[ch_idx], true); + qdf_mem_free(pSmeRrmContext->channelList.freq_list); + pSmeRrmContext->channelList.freq_list = NULL; + pSmeRrmContext->channelList.numOfChannels = 0; + sme_reset_ese_bcn_req_in_progress(pSmeRrmContext); + return QDF_STATUS_E_FAILURE; + } + + sme_rrm_send_scan_result(mac, pSmeRrmContext->measurement_idx, + 1, &freq_list[ch_idx], false); + + /* Advance the current index. */ + pSmeRrmContext->currentIndex++; + /* start the timer to issue next request. */ + /* From timer tick get a random number within 10ms and max + * randmization interval. + */ + time_tick = qdf_mc_timer_get_system_ticks(); + interval = + time_tick % (pSmeRrmContext->randnIntvl - 10 + 1) + 10; + + sme_debug("Set timer for interval %d ", interval); + qdf_status = qdf_mc_timer_start(&pSmeRrmContext->IterMeasTimer, + interval); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + qdf_mem_free(pSmeRrmContext->channelList.freq_list); + pSmeRrmContext->channelList.freq_list = NULL; + pSmeRrmContext->channelList.numOfChannels = 0; + sme_reset_ese_bcn_req_in_progress(pSmeRrmContext); + } + + } else { + /* Done with the measurement. Clean up all context and send a + * message to PE with measurement done flag set. + */ + sme_rrm_send_scan_result(mac, pSmeRrmContext->measurement_idx, + 1, &freq_list[ch_idx], true); + qdf_mem_free(pSmeRrmContext->channelList.freq_list); + pSmeRrmContext->channelList.freq_list = NULL; + pSmeRrmContext->channelList.numOfChannels = 0; + sme_reset_ese_bcn_req_in_progress(pSmeRrmContext); + } + + return QDF_STATUS_SUCCESS; +} + +static void sme_rrm_scan_event_callback(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{ + struct mac_context *mac_ctx; + uint32_t scan_id; + uint8_t session_id, i; + eCsrScanStatus scan_status = eCSR_SCAN_FAILURE; + bool success = false; + tpRrmSMEContext smerrmctx; + + mac_ctx = (struct mac_context *)arg; + if (!mac_ctx) { + sme_err("invalid mac_ctx"); + return; + } + + session_id = wlan_vdev_get_id(vdev); + scan_id = event->scan_id; + + qdf_mtrace(QDF_MODULE_ID_SCAN, QDF_MODULE_ID_SME, event->type, + event->vdev_id, event->scan_id); + + if (!util_is_scan_completed(event, &success)) + return; + + if (success) + scan_status = eCSR_SCAN_SUCCESS; + + for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { + smerrmctx = &mac_ctx->rrm.rrmSmeContext[i]; + if (smerrmctx->scan_id == scan_id) + break; + + if (i == (MAX_MEASUREMENT_REQUEST - 1)) + return; + } + + sme_debug("Scan completed for scan_id:%d measurement_idx:%d", + scan_id, smerrmctx->measurement_idx); + sme_rrm_scan_request_callback(mac_ctx, smerrmctx, session_id, + scan_id, scan_status); +} + +/** + * sme_rrm_issue_scan_req() - To issue rrm scan request + * @mac_ctx: pointer to mac context + * + * This routine is called to issue rrm scan request + * + * Return: QDF_STATUS + */ +static QDF_STATUS +sme_rrm_issue_scan_req(struct mac_context *mac_ctx, uint8_t idx) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpRrmSMEContext sme_rrm_ctx = &mac_ctx->rrm.rrmSmeContext[idx]; + uint32_t session_id; + tSirScanType scan_type; + uint8_t ch_idx; + uint32_t *freq_list; + + status = csr_roam_get_session_id_from_bssid(mac_ctx, + &sme_rrm_ctx->sessionBssId, &session_id); + if (status != QDF_STATUS_SUCCESS) { + sme_err("sme session ID not found for bssid= "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(sme_rrm_ctx->sessionBssId.bytes)); + status = QDF_STATUS_E_FAILURE; + goto send_ind; + } + + if ((sme_rrm_ctx->currentIndex) >= + sme_rrm_ctx->channelList.numOfChannels) { + sme_rrm_send_beacon_report_xmit_ind(mac_ctx, idx, NULL, + true, 0); + sme_debug("done with the complete ch lt. finish and fee now"); + goto free_ch_lst; + } + + if (eRRM_MSG_SOURCE_ESE_UPLOAD == sme_rrm_ctx->msgSource || + eRRM_MSG_SOURCE_LEGACY_ESE == sme_rrm_ctx->msgSource) + scan_type = sme_rrm_ctx->measMode[sme_rrm_ctx->currentIndex]; + else + scan_type = sme_rrm_ctx->measMode[0]; + + if ((eSIR_ACTIVE_SCAN == scan_type) || + (eSIR_PASSIVE_SCAN == scan_type)) { + uint32_t max_chan_time; + uint64_t current_time; + struct scan_start_request *req; + struct wlan_objmgr_vdev *vdev; + uint32_t freq; + + if (!sme_rrm_ctx->channelList.numOfChannels || + !sme_rrm_ctx->channelList.freq_list) { + sme_err("[802.11 RRM]: Global freq list is null"); + status = QDF_STATUS_E_FAILURE; + goto send_ind; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + status = QDF_STATUS_E_NOMEM; + goto send_ind; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + mac_ctx->psoc, + session_id, + WLAN_LEGACY_SME_ID); + if (!vdev) { + sme_err("VDEV is null %d", session_id); + status = QDF_STATUS_E_INVAL; + qdf_mem_free(req); + goto send_ind; + } + ucfg_scan_init_default_params(vdev, req); + req->scan_req.scan_id = ucfg_scan_get_scan_id(mac_ctx->psoc); + sme_rrm_ctx->scan_id = req->scan_req.scan_id; + + sme_debug("RRM_SCN: rrm_idx:%d scan_id:%d", + sme_rrm_ctx->measurement_idx, sme_rrm_ctx->scan_id); + req->scan_req.scan_f_passive = + (scan_type == eSIR_ACTIVE_SCAN) ? false : true; + req->scan_req.vdev_id = wlan_vdev_get_id(vdev); + req->scan_req.scan_req_id = sme_rrm_ctx->req_id; + qdf_mem_copy(&req->scan_req.bssid_list[0], sme_rrm_ctx->bssId, + QDF_MAC_ADDR_SIZE); + req->scan_req.num_bssid = 1; + if (sme_rrm_ctx->ssId.length) { + req->scan_req.num_ssids = 1; + qdf_mem_copy(&req->scan_req.ssid[0].ssid, + sme_rrm_ctx->ssId.ssId, + sme_rrm_ctx->ssId.length); + req->scan_req.ssid[0].length = sme_rrm_ctx->ssId.length; + } + + /* + * set min and max channel time + * sme_rrm_ctx->duration; Dont use min timeout. + */ + if (eRRM_MSG_SOURCE_ESE_UPLOAD == sme_rrm_ctx->msgSource || + eRRM_MSG_SOURCE_LEGACY_ESE == sme_rrm_ctx->msgSource) + max_chan_time = + sme_rrm_ctx->duration[sme_rrm_ctx->currentIndex]; + else + max_chan_time = sme_rrm_ctx->duration[0]; + + /* + * Use max_chan_time if max_chan_time is more than def value + * depending on type of scan. + */ + if (req->scan_req.scan_f_passive) { + if (max_chan_time >= RRM_SCAN_MIN_DWELL_TIME) + req->scan_req.dwell_time_passive = + max_chan_time; + sme_debug("Passive Max Dwell Time(%d)", + req->scan_req.dwell_time_passive); + } else { + if (max_chan_time >= RRM_SCAN_MIN_DWELL_TIME) { + req->scan_req.dwell_time_active = max_chan_time; + req->scan_req.dwell_time_active_2g = max_chan_time; + } + sme_debug("Active Max Dwell Time(%d) 2G Dwell time %d", + req->scan_req.dwell_time_active, + req->scan_req.dwell_time_active_2g); + } + + req->scan_req.adaptive_dwell_time_mode = SCAN_DWELL_MODE_STATIC; + /* + * For RRM scans timing is very important especially when the + * request is for limited channels. There is no need for + * firmware to rest for about 100-200 ms on the home channel. + * Instead, it can start the scan right away which will make the + * host to respond with the beacon report as quickly as + * possible. Ensure that the scan requests are not back to back + * and hence there is a check to see if the requests are atleast + * 1 second apart. + */ + current_time = (uint64_t)qdf_mc_timer_get_system_time(); + sme_debug("prev scan triggered before %llu ms, totalchannels %d", + current_time - rrm_scan_timer, + sme_rrm_ctx->channelList.numOfChannels); + if ((abs(current_time - rrm_scan_timer) > 1000) && + (sme_rrm_ctx->channelList.numOfChannels == 1)) { + req->scan_req.max_rest_time = 1; + req->scan_req.min_rest_time = 1; + req->scan_req.idle_time = 1; + } + + rrm_scan_timer = (uint64_t)qdf_mc_timer_get_system_time(); + + /* set requestType to full scan */ + req->scan_req.chan_list.num_chan = 1; + freq = sme_rrm_ctx->channelList.freq_list[ + sme_rrm_ctx->currentIndex]; + req->scan_req.chan_list.chan[0].freq = freq; + sme_debug("active duration %d passive %d On freq %d", + req->scan_req.dwell_time_active, + req->scan_req.dwell_time_passive, + req->scan_req.chan_list.chan[0].freq); + /* + * Fill RRM scan type for these requests. This is done + * because in scan concurrency update params we update the + * dwell time active which was not the expectation. + * So doing a check of RRM scan request, we would not + * update the dwell time. + */ + req->scan_req.scan_type = SCAN_TYPE_RRM; + + status = ucfg_scan_start(req); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + if (QDF_IS_STATUS_ERROR(status)) + goto send_ind; + + return status; + } else if (eSIR_BEACON_TABLE == scan_type) { + /* + * In beacon table mode, scan results are taken directly from + * scan cache without issuing any scan request. So, it is not + * proper to update rrm_scan_timer with latest time and hence + * made it to zero to satisfy + * pScanResult->timer >= rrm_scan_timer + */ + rrm_scan_timer = 0; + freq_list = sme_rrm_ctx->channelList.freq_list; + if (!freq_list) { + sme_err("[802.11 RRM]: Global freq list is null"); + sme_reset_ese_bcn_req_in_progress(sme_rrm_ctx); + status = QDF_STATUS_E_FAILURE; + goto send_ind; + } + + ch_idx = sme_rrm_ctx->currentIndex; + for (; ch_idx < sme_rrm_ctx->channelList.numOfChannels; ch_idx++) { + if ((ch_idx + 1) < + sme_rrm_ctx->channelList.numOfChannels) { + sme_rrm_send_scan_result(mac_ctx, idx, 1, + &freq_list[ch_idx], + false); + /* Advance the current index. */ + sme_rrm_ctx->currentIndex++; + } else { + /* + * Done with the measurement. Clean up all + * context and send a message to PE with + * measurement done flag set. + */ + sme_rrm_send_scan_result(mac_ctx, idx, 1, + &freq_list[ch_idx], + true); + sme_reset_ese_bcn_req_in_progress(sme_rrm_ctx); + goto free_ch_lst; + } + } + } + + sme_err("Unknown beacon report req mode(%d)", scan_type); + /* + * Indicate measurement completion to PE + * If this is not done, pCurrentReq pointer will not be freed + * and PE will not handle subsequent Beacon requests + */ +send_ind: + sme_rrm_send_beacon_report_xmit_ind(mac_ctx, idx, NULL, true, 0); +free_ch_lst: + qdf_mem_free(sme_rrm_ctx->channelList.freq_list); + sme_rrm_ctx->channelList.freq_list = NULL; + sme_rrm_ctx->channelList.numOfChannels = 0; + return status; +} + +static QDF_STATUS sme_rrm_fill_scan_channels(struct mac_context *mac, + uint8_t *country, + tpRrmSMEContext sme_rrm_context, + uint8_t op_class, + uint32_t num_channels) +{ + uint32_t num_chan = 0; + uint32_t i; + uint32_t *freq_list; + bool found; + + freq_list = sme_rrm_context->channelList.freq_list; + found = false; + for (i = 0; i < num_channels; i++) { + found = wlan_reg_country_opclass_freq_check(mac->pdev, + country, + op_class, + freq_list[i]); + if (found) { + freq_list[num_chan] = freq_list[i]; + num_chan++; + } + found = false; + } + + sme_rrm_context->channelList.numOfChannels = num_chan; + if (sme_rrm_context->channelList.numOfChannels == 0) { + qdf_mem_free(sme_rrm_context->channelList.freq_list); + sme_rrm_context->channelList.freq_list = NULL; + sme_err("No channels populated with requested operation class and current country, Hence abort the rrm operation"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +static uint8_t *sme_rrm_get_meas_mode_string(uint8_t meas_mode) +{ + switch (meas_mode) { + CASE_RETURN_STRING(eSIR_PASSIVE_SCAN); + CASE_RETURN_STRING(eSIR_ACTIVE_SCAN); + CASE_RETURN_STRING(eSIR_BEACON_TABLE); + default: + return (uint8_t *)"UNKNOWN"; + break; + } +} + +/** + * sme_rrm_process_beacon_report_req_ind() -Process beacon report request + * @mac:- Global Mac structure + * @msg_buf:- a pointer to a buffer that maps to various structures base + * on the message type.The beginning of the buffer can always + * map to tSirSmeRsp. + * + * This is called to process the Beacon + * report request from peer AP forwarded through PE . + * + * Return : QDF_STATUS_SUCCESS - Validation is successful. + */ +QDF_STATUS sme_rrm_process_beacon_report_req_ind(struct mac_context *mac, + void *msg_buf) +{ + tpSirBeaconReportReqInd beacon_req = (tpSirBeaconReportReqInd)msg_buf; + tpRrmSMEContext sme_rrm_ctx; + uint32_t len = 0, i = 0, j = 0; + uint8_t country[WNI_CFG_COUNTRY_CODE_LEN]; + uint32_t session_id; + struct csr_roam_session *session; + QDF_STATUS status; + uint32_t num_chan, local_num_channel; + bool chan_valid; + uint32_t *rrm_freq_list, *local_rrm_freq_list; + uint32_t bcn_chan_freq, local_bcn_chan_freq; + tRrmPEContext rrm_context; + + sme_rrm_ctx = &mac->rrm.rrmSmeContext[beacon_req->measurement_idx]; + rrm_context = mac->rrm.rrmPEContext; + + status = csr_roam_get_session_id_from_bssid(mac, (struct qdf_mac_addr *) + beacon_req->bssId, + &session_id); + if (QDF_IS_STATUS_ERROR(status)) { + sme_err("sme session ID not found for bssid"); + goto cleanup; + } + + session = CSR_GET_SESSION(mac, session_id); + if (!session) { + sme_err("Invalid session id %d", session_id); + status = QDF_STATUS_E_FAILURE; + goto cleanup; + } + + qdf_mem_zero(country, WNI_CFG_COUNTRY_CODE_LEN); + if (session->connectedProfile.country_code[0]) + qdf_mem_copy(country, session->connectedProfile.country_code, + WNI_CFG_COUNTRY_CODE_LEN); + else + country[2] = OP_CLASS_GLOBAL; + + sme_debug("RRM_SCN: Index:%d Request Reg class %d, AP's country code %c%c 0x%x, channel = %d", + beacon_req->measurement_idx, + beacon_req->channel_info.reg_class, + country[0], country[1], country[2], + beacon_req->channel_info.chan_num); + + if (beacon_req->channel_list.num_channels > SIR_ESE_MAX_MEAS_IE_REQS) { + sme_err("Beacon report request numChannels:%u exceeds max num channels", + beacon_req->channel_list.num_channels); + status = QDF_STATUS_E_INVAL; + goto cleanup; + } + + /* section 11.10.8.1 (IEEE Std 802.11k-2008) */ + /* channel 0 and 255 has special meaning. */ + if ((beacon_req->channel_info.chan_num == 0) || + ((beacon_req->channel_info.chan_num == 255) && + (beacon_req->channel_list.num_channels == 0))) { + /* Add all the channel in the regulatory domain. */ + len = mac->mlme_cfg->reg.valid_channel_list_num; + if (sme_rrm_ctx->channelList.freq_list) { + qdf_mem_free(sme_rrm_ctx->channelList.freq_list); + sme_rrm_ctx->channelList.freq_list = NULL; + } + sme_rrm_ctx->channelList.freq_list = + qdf_mem_malloc(sizeof(uint32_t) * len); + if (!sme_rrm_ctx->channelList.freq_list) { + status = QDF_STATUS_E_NOMEM; + sme_rrm_ctx->channelList.numOfChannels = 0; + goto cleanup; + } + + csr_get_cfg_valid_channels( + mac, sme_rrm_ctx->channelList.freq_list, &len); + + if (beacon_req->channel_info.reg_class) { + if (sme_rrm_fill_scan_channels( + mac, country, sme_rrm_ctx, + beacon_req->channel_info.reg_class, len) != + QDF_STATUS_SUCCESS) + goto cleanup; + } else { + sme_rrm_ctx->channelList.numOfChannels = len; + } + } else { + len = 0; + sme_rrm_ctx->channelList.numOfChannels = 0; + num_chan = 0; + + /* If valid channel is present. We first Measure on the given + * channel and if there are additional channels present in + * APchannelreport, measure on these also. + */ + if (beacon_req->channel_info.chan_num != 255) + len = 1; + + len += beacon_req->channel_list.num_channels; + + if (sme_rrm_ctx->channelList.freq_list) { + qdf_mem_free(sme_rrm_ctx->channelList.freq_list); + sme_rrm_ctx->channelList.freq_list = NULL; + } + sme_rrm_ctx->channelList.freq_list = + qdf_mem_malloc(sizeof(uint32_t) * len); + if (!sme_rrm_ctx->channelList.freq_list) { + sme_rrm_ctx->channelList.numOfChannels = 0; + status = QDF_STATUS_E_NOMEM; + goto cleanup; + } + + rrm_freq_list = sme_rrm_ctx->channelList.freq_list; + bcn_chan_freq = beacon_req->channel_info.chan_freq; + + if (beacon_req->channel_info.chan_num != 255) { + chan_valid = + csr_roam_is_channel_valid(mac, bcn_chan_freq); + + if (chan_valid) { + rrm_freq_list[num_chan] = bcn_chan_freq; + num_chan++; + } else { + sme_err("Invalid channel: %d", + beacon_req->channel_info.chan_num); + } + } + + for (i = 0; i < beacon_req->channel_list.num_channels; i++) { + bcn_chan_freq = + beacon_req->channel_list.chan_freq_lst[i]; + chan_valid = + csr_roam_is_channel_valid(mac, bcn_chan_freq); + + if (chan_valid) { + rrm_freq_list[num_chan] = bcn_chan_freq; + num_chan++; + } + } + + sme_rrm_ctx->channelList.numOfChannels = num_chan; + } + + local_rrm_freq_list = sme_rrm_ctx->channelList.freq_list; + local_num_channel = 0; + for (i = 0; i < sme_rrm_ctx->channelList.numOfChannels; i++) { + local_bcn_chan_freq = local_rrm_freq_list[i]; + chan_valid = true; + + if (beacon_req->measurement_idx > 0) { + for (j = 0; j < rrm_context.beacon_rpt_chan_num; j ++) { + if (rrm_context.beacon_rpt_chan_list[j] == + local_bcn_chan_freq) { + /* + * Ignore this channel, As this is already + * included in previous request + */ + chan_valid = false; + break; + } + } + } + + if (chan_valid) { + rrm_context. + beacon_rpt_chan_list[rrm_context.beacon_rpt_chan_num] = + local_bcn_chan_freq; + rrm_context.beacon_rpt_chan_num++; + + if (rrm_context.beacon_rpt_chan_num >= + MAX_NUM_CHANNELS) { + /* this should never happen */ + sme_err("Reset beacon_rpt_chan_num : %d", + rrm_context.beacon_rpt_chan_num); + rrm_context.beacon_rpt_chan_num = 0; + } + local_rrm_freq_list[local_num_channel] = + local_bcn_chan_freq; + local_num_channel++; + } + } + + if (local_num_channel == 0) + goto cleanup; + + sme_rrm_ctx->channelList.numOfChannels = local_num_channel; + + /* Copy session bssid */ + qdf_mem_copy(sme_rrm_ctx->sessionBssId.bytes, beacon_req->bssId, + sizeof(tSirMacAddr)); + + /* copy measurement bssid */ + qdf_mem_copy(sme_rrm_ctx->bssId, beacon_req->macaddrBssid, + sizeof(tSirMacAddr)); + + /* Copy ssid */ + qdf_mem_copy(&sme_rrm_ctx->ssId, &beacon_req->ssId, + sizeof(tAniSSID)); + + sme_rrm_ctx->token = beacon_req->uDialogToken; + sme_rrm_ctx->regClass = beacon_req->channel_info.reg_class; + sme_rrm_ctx->randnIntvl = + QDF_MAX(beacon_req->randomizationInterval, + mac->rrm.rrmConfig.max_randn_interval); + sme_rrm_ctx->currentIndex = 0; + sme_rrm_ctx->msgSource = beacon_req->msgSource; + qdf_mem_copy((uint8_t *)&sme_rrm_ctx->measMode, + (uint8_t *)&beacon_req->fMeasurementtype, + SIR_ESE_MAX_MEAS_IE_REQS); + qdf_mem_copy((uint8_t *)&sme_rrm_ctx->duration, + (uint8_t *)&beacon_req->measurementDuration, + SIR_ESE_MAX_MEAS_IE_REQS); + + sme_debug("token: %d randnIntvl: %d msgSource: %d measurementduration %d, rrm_ctx duration %d Meas_mode: %s", + sme_rrm_ctx->token, sme_rrm_ctx->randnIntvl, + sme_rrm_ctx->msgSource, beacon_req->measurementDuration[0], + sme_rrm_ctx->duration[0], + sme_rrm_get_meas_mode_string(sme_rrm_ctx->measMode[0])); + + return sme_rrm_issue_scan_req(mac, beacon_req->measurement_idx); + +cleanup: + if (beacon_req->msgSource == eRRM_MSG_SOURCE_11K) { + /* Copy session bssid */ + qdf_mem_copy(sme_rrm_ctx->sessionBssId.bytes, + beacon_req->bssId, sizeof(tSirMacAddr)); + + /* copy measurement bssid */ + qdf_mem_copy(sme_rrm_ctx->bssId, beacon_req->macaddrBssid, + sizeof(tSirMacAddr)); + sme_rrm_ctx->token = beacon_req->uDialogToken; + sme_rrm_ctx->regClass = + beacon_req->channel_info.reg_class; + sme_rrm_ctx->randnIntvl = + QDF_MAX(beacon_req->randomizationInterval, + mac->rrm.rrmConfig.max_randn_interval); + + sme_rrm_send_beacon_report_xmit_ind(mac, + sme_rrm_ctx->measurement_idx, NULL, true, 0); + } + + return status; +} + +/** + * sme_rrm_neighbor_report_request() - This is API can be used to trigger a + * Neighbor report from the peer. + * @sessionId: session identifier on which the request should be made. + * @pNeighborReq: a pointer to a neighbor report request. + * + * This is API can be used to trigger a Neighbor report from the peer. + * + * Return: QDF_STATUS_SUCCESS - Validation is successful. + */ +QDF_STATUS sme_rrm_neighbor_report_request(struct mac_context *mac, uint8_t + sessionId, tpRrmNeighborReq + pNeighborReq, + tpRrmNeighborRspCallbackInfo + callbackInfo) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpSirNeighborReportReqInd pMsg; + struct csr_roam_session *pSession; + + sme_debug("Request to send Neighbor report request received "); + if (!CSR_IS_SESSION_VALID(mac, sessionId)) { + sme_err("Invalid session %d", sessionId); + return QDF_STATUS_E_INVAL; + } + pSession = CSR_GET_SESSION(mac, sessionId); + + /* If already a report is pending, return failure */ + if (true == + mac->rrm.rrmSmeContext[0].neighborReqControlInfo. + isNeighborRspPending) { + sme_err("Neighbor request already pending.. Not allowed"); + return QDF_STATUS_E_AGAIN; + } + + pMsg = qdf_mem_malloc(sizeof(tSirNeighborReportReqInd)); + if (!pMsg) + return QDF_STATUS_E_NOMEM; + + rrm_ll_purge_neighbor_cache(mac, + &mac->rrm.rrmSmeContext[0].neighborReportCache); + + pMsg->messageType = eWNI_SME_NEIGHBOR_REPORT_REQ_IND; + pMsg->length = sizeof(tSirNeighborReportReqInd); + qdf_mem_copy(&pMsg->bssId, &pSession->connectedProfile.bssid, + sizeof(tSirMacAddr)); + pMsg->noSSID = pNeighborReq->no_ssid; + qdf_mem_copy(&pMsg->ucSSID, &pNeighborReq->ssid, sizeof(tSirMacSSid)); + + status = umac_send_mb_message_to_mac(pMsg); + if (status != QDF_STATUS_SUCCESS) + return QDF_STATUS_E_FAILURE; + + /* Neighbor report request message sent successfully to PE. + * Now register the callbacks + */ + mac->rrm.rrmSmeContext[0].neighborReqControlInfo. + neighborRspCallbackInfo.neighborRspCallback = + callbackInfo->neighborRspCallback; + mac->rrm.rrmSmeContext[0].neighborReqControlInfo. + neighborRspCallbackInfo.neighborRspCallbackContext = + callbackInfo->neighborRspCallbackContext; + mac->rrm.rrmSmeContext[0].neighborReqControlInfo.isNeighborRspPending = + true; + + /* Start neighbor response wait timer now */ + qdf_mc_timer_start(&mac->rrm.rrmSmeContext[0].neighborReqControlInfo. + neighborRspWaitTimer, callbackInfo->timeout); + + return QDF_STATUS_SUCCESS; +} + +/** + * rrm_calculate_neighbor_ap_roam_score() - caclulates roam score + * @mac_ctx: mac global context + * @pNeighborReportDesc: Neighbor BSS Descriptor node for which roam score + * should be calculated + * + * This API is called while handling individual neighbor reports from the APs + * neighbor AP report to calculate the cumulative roam score before storing it + * in neighbor cache. + * + * Return: void + */ +static void +rrm_calculate_neighbor_ap_roam_score(struct mac_context *mac_ctx, + tpRrmNeighborReportDesc nbr_report_desc) +{ + tpSirNeighborBssDescripton nbr_bss_desc; + uint32_t roam_score = 0; +#ifdef FEATURE_WLAN_ESE + uint8_t session_id; +#endif + if (!nbr_report_desc) { + QDF_ASSERT(0); + return; + } + + if (!nbr_report_desc->pNeighborBssDescription) { + QDF_ASSERT(0); + return; + } + + nbr_bss_desc = nbr_report_desc->pNeighborBssDescription; + if (!nbr_bss_desc->bssidInfo.rrmInfo.fMobilityDomain) + goto check_11r_assoc; + + roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_MOBILITY_DOMAIN; + if (!nbr_bss_desc->bssidInfo.rrmInfo.fSameSecurityMode) + goto check_11r_assoc; + + roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_SECURITY; + if (!nbr_bss_desc->bssidInfo.rrmInfo.fSameAuthenticator) + goto check_11r_assoc; + + roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_KEY_SCOPE; + if (!nbr_bss_desc->bssidInfo.rrmInfo.fCapRadioMeasurement) + goto check_11r_assoc; + + roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_RRM; + if (nbr_bss_desc->bssidInfo.rrmInfo.fCapSpectrumMeasurement) + roam_score += + RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_SPECTRUM_MGMT; + + if (nbr_bss_desc->bssidInfo.rrmInfo.fCapQos) + roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_QOS; + + if (nbr_bss_desc->bssidInfo.rrmInfo.fCapApsd) + roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_APSD; + + if (nbr_bss_desc->bssidInfo.rrmInfo.fCapDelayedBlockAck) + roam_score += + RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_DELAYED_BA; + + if (nbr_bss_desc->bssidInfo.rrmInfo.fCapImmediateBlockAck) + roam_score += + RRM_ROAM_SCORE_NEIGHBOR_REPORT_CAPABILITY_IMMEDIATE_BA; + + if (nbr_bss_desc->bssidInfo.rrmInfo.fApPreauthReachable) + roam_score += RRM_ROAM_SCORE_NEIGHBOR_REPORT_REACHABILITY; + +check_11r_assoc: +#ifdef FEATURE_WLAN_ESE + session_id = nbr_report_desc->sessionId; + /* It has come in the report so its the best score */ + if (csr_neighbor_roam_is11r_assoc(mac_ctx, session_id) == false) { + /* IAPP Route so lets make use of this info save all AP, as the + * list does not come all the time. Save and reuse till the next + * AP List comes to us. Even save our own MAC address. Will be + * useful next time around. + */ + roam_score += RRM_ROAM_SCORE_NEIGHBOR_IAPP_LIST; + } +#endif + nbr_report_desc->roamScore = roam_score; +} + +/** + * rrm_store_neighbor_rpt_by_roam_score()-store Neighbor BSS descriptor + * @mac: Pointer to mac context + * @pNeighborReportDesc - Neighbor BSS Descriptor node to be stored in cache + * @index: RRM sme context index + * + * This API is called to store a given + * Neighbor BSS descriptor to the neighbor cache. This function + * stores the neighbor BSS descriptors in such a way that descriptors + * are sorted by roamScore in descending order + * + * Return: void. + */ +static void rrm_store_neighbor_rpt_by_roam_score(struct mac_context *mac, + tpRrmNeighborReportDesc pNeighborReportDesc, + uint8_t index) +{ + tpRrmSMEContext pSmeRrmContext = &mac->rrm.rrmSmeContext[0]; + tListElem *pEntry; + tRrmNeighborReportDesc *pTempNeighborReportDesc; + + if (!pNeighborReportDesc) { + QDF_ASSERT(0); + return; + } + if (!pNeighborReportDesc->pNeighborBssDescription) { + QDF_ASSERT(0); + return; + } + + if (csr_ll_is_list_empty + (&pSmeRrmContext->neighborReportCache, LL_ACCESS_LOCK)) { + sme_err("Neighbor report cache is empty.. Adding a entry now"); + /* Neighbor list cache is empty. Insert this entry + * in the tail + */ + csr_ll_insert_tail(&pSmeRrmContext->neighborReportCache, + &pNeighborReportDesc->List, LL_ACCESS_LOCK); + return; + } + /* Should store the neighbor BSS description in the order + * sorted by roamScore in descending order. APs with highest + * roamScore should be the 1st entry in the list + */ + pEntry = csr_ll_peek_head(&pSmeRrmContext->neighborReportCache, + LL_ACCESS_LOCK); + while (pEntry) { + pTempNeighborReportDesc = GET_BASE_ADDR(pEntry, + tRrmNeighborReportDesc, List); + if (pTempNeighborReportDesc->roamScore < + pNeighborReportDesc->roamScore) + break; + pEntry = csr_ll_next(&pSmeRrmContext-> + neighborReportCache, pEntry, LL_ACCESS_LOCK); + } + + if (pEntry) + /* This BSS roamscore is better than something in the + * list. Insert this before that one + */ + csr_ll_insert_entry(&pSmeRrmContext->neighborReportCache, + pEntry, &pNeighborReportDesc->List, + LL_ACCESS_LOCK); + else + /* All the entries in the list has a better roam Score + * than this one. Insert this at the last + */ + csr_ll_insert_tail(&pSmeRrmContext->neighborReportCache, + &pNeighborReportDesc->List, + LL_ACCESS_LOCK); +} + +/** + * sme_rrm_process_neighbor_report() -Process the Neighbor report received + * from PE + * @mac - Global MAC structure + * @msg_buf - a pointer to a buffer that maps to various structures base + * on the message type. + * The beginning of the buffer can always map to tSirSmeRsp. + * This is called to process the Neighbor report received from PE. + * + * Return: QDF_STATUS_SUCCESS - Validation is successful + */ +static QDF_STATUS sme_rrm_process_neighbor_report(struct mac_context *mac, + void *msg_buf) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpSirNeighborReportInd neighbor_rpt = (tpSirNeighborReportInd)msg_buf; + tpRrmNeighborReportDesc neighbor_rpt_desc; + uint8_t i = 0; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + /* Purge the cache on reception of unsolicited neighbor report */ + if (!mac->rrm.rrmSmeContext[neighbor_rpt->measurement_idx]. + neighborReqControlInfo.isNeighborRspPending) + rrm_ll_purge_neighbor_cache(mac, + &mac->rrm.rrmSmeContext[neighbor_rpt->measurement_idx]. + neighborReportCache); + + for (i = 0; i < neighbor_rpt->numNeighborReports; i++) { + neighbor_rpt_desc = + qdf_mem_malloc(sizeof(tRrmNeighborReportDesc)); + if (!neighbor_rpt_desc) { + status = QDF_STATUS_E_NOMEM; + goto end; + + } + + neighbor_rpt_desc->pNeighborBssDescription = + qdf_mem_malloc(sizeof(tSirNeighborBssDescription)); + if (!neighbor_rpt_desc->pNeighborBssDescription) { + qdf_mem_free(neighbor_rpt_desc); + status = QDF_STATUS_E_NOMEM; + goto end; + } + qdf_mem_copy(neighbor_rpt_desc->pNeighborBssDescription, + &neighbor_rpt->sNeighborBssDescription[i], + sizeof(tSirNeighborBssDescription)); + + sme_debug("Received neighbor report with Neighbor BSSID: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF( + neighbor_rpt->sNeighborBssDescription[i].bssId)); + + rrm_calculate_neighbor_ap_roam_score(mac, neighbor_rpt_desc); + + if (neighbor_rpt_desc->roamScore > 0) { + rrm_store_neighbor_rpt_by_roam_score( + mac, neighbor_rpt_desc, + neighbor_rpt->measurement_idx); + } else { + sme_err("Roam score of BSSID " QDF_MAC_ADDR_FMT + " is 0, Ignoring..", + QDF_MAC_ADDR_REF(neighbor_rpt-> + sNeighborBssDescription[i]. + bssId)); + + qdf_mem_free( + neighbor_rpt_desc->pNeighborBssDescription); + qdf_mem_free(neighbor_rpt_desc); + } + } +end: + + if (!csr_ll_count( + &mac->rrm.rrmSmeContext[neighbor_rpt->measurement_idx]. + neighborReportCache)) + qdf_status = QDF_STATUS_E_FAILURE; + + rrm_indicate_neighbor_report_result(mac, qdf_status); + + return status; +} + +/** + * sme_rrm_msg_processor()-Process RRM message + * @mac - Pointer to the global MAC parameter structure. + * @msg_type - the type of msg passed by PE as defined in wni_api.h + * @msg_buf - a pointer to a buffer that maps to various structures base + * on the message type. + * The beginning of the buffer can always map to tSirSmeRsp. + * sme_process_msg() calls this function for the + * messages that are handled by SME RRM module. + * + * Return: QDF_STATUS_SUCCESS - Validation is successful. + */ +QDF_STATUS sme_rrm_msg_processor(struct mac_context *mac, uint16_t msg_type, + void *msg_buf) +{ + sme_debug("Msg = %d for RRM measurement", msg_type); + + /* switch on the msg type & make the state transition accordingly */ + switch (msg_type) { + case eWNI_SME_NEIGHBOR_REPORT_IND: + sme_rrm_process_neighbor_report(mac, msg_buf); + break; + + case eWNI_SME_BEACON_REPORT_REQ_IND: + sme_rrm_process_beacon_report_req_ind(mac, msg_buf); + break; + + default: + sme_err("Unknown msg type: %d", msg_type); + break; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * rrm_iter_meas_timer_handle() - Timer handler to handlet the timeout + * @ mac - The handle returned by mac_open. + * + * Timer handler to handlet the timeout condition when a specific BT + * stop event does not come back, in which case to restore back the + * heartbeat timer. + * + * Return: NULL + */ +static void rrm_iter_meas_timer_handle(void *data) +{ + struct mac_context *mac; + mac_handle_t mac_handle = cds_get_context(QDF_MODULE_ID_SME); + tpRrmSMEContext sme_rrm_ctx = (tpRrmSMEContext)data; + + mac = MAC_CONTEXT(mac_handle); + if (!mac) { + sme_err("Mac ctx is NULL"); + return; + } + + sme_debug("Randomization timer expired...send on next channel"); + + /* Issue a scan req for next channel. */ + sme_rrm_issue_scan_req(mac, sme_rrm_ctx->measurement_idx); +} + +/** + * rrm_neighbor_rsp_timeout_handler() - Timer handler to handlet the timeout + * @mac - The handle returned by mac_open. + * + * Timer handler to handle the timeout condition when a neighbor request is sent + * and no neighbor response is received from the AP + * + * Return: NULL + */ +static void rrm_neighbor_rsp_timeout_handler(void *userData) +{ + struct mac_context *mac = (struct mac_context *) userData; + + sme_warn("Neighbor Response timed out"); + rrm_indicate_neighbor_report_result(mac, QDF_STATUS_E_FAILURE); +} + +/** + * rrm_change_default_config_param() - Changing default config param to new + * @mac - The handle returned by mac_open. + * + * Return: None + */ +static void rrm_change_default_config_param(struct mac_context *mac) +{ + mac->rrm.rrmConfig.rrm_enabled = + mac->mlme_cfg->rrm_config.rrm_enabled; + mac->rrm.rrmConfig.sap_rrm_enabled = + mac->mlme_cfg->rrm_config.sap_rrm_enabled; + mac->rrm.rrmConfig.max_randn_interval = + mac->mlme_cfg->rrm_config.rrm_rand_interval; + + qdf_mem_copy(&mac->rrm.rrmConfig.rm_capability, + &mac->mlme_cfg->rrm_config.rm_capability, + RMENABLEDCAP_MAX_LEN); +} + +/** + * rrm_open() - Initialze all RRM module + * @ mac: The handle returned by mac_open. + * + * Initialze all RRM module. + * + * Return: QDF_STATUS + */ +QDF_STATUS rrm_open(struct mac_context *mac) +{ + + QDF_STATUS qdf_status; + tpRrmSMEContext pSmeRrmContext; + QDF_STATUS qdf_ret_status = QDF_STATUS_SUCCESS; + uint8_t i; + + mac->rrm.rrmConfig.max_randn_interval = 50; /* ms */ + + for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { + pSmeRrmContext = &mac->rrm.rrmSmeContext[i]; + + qdf_status = qdf_mc_timer_init(&pSmeRrmContext->IterMeasTimer, + QDF_TIMER_TYPE_SW, + rrm_iter_meas_timer_handle, + (void *)pSmeRrmContext); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + sme_err("Fail to init measurement timer"); + return QDF_STATUS_E_FAILURE; + } + + qdf_status = + qdf_mc_timer_init(&pSmeRrmContext->neighborReqControlInfo. + neighborRspWaitTimer, QDF_TIMER_TYPE_SW, + rrm_neighbor_rsp_timeout_handler, + (void *)mac); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + sme_err("Fail to init neighbor rsp wait timer"); + return QDF_STATUS_E_FAILURE; + } + + pSmeRrmContext->measurement_idx = i; + pSmeRrmContext->neighborReqControlInfo.isNeighborRspPending = + false; + + qdf_ret_status = + csr_ll_open(&pSmeRrmContext->neighborReportCache); + if (QDF_STATUS_SUCCESS != qdf_ret_status) { + sme_err("Fail to open neighbor cache result"); + return QDF_STATUS_E_FAILURE; + } + } + + rrm_change_default_config_param(mac); + + return QDF_STATUS_SUCCESS; +} + +/** + * rrm_close() - Release all RRM modules and their resources. + * @mac - The handle returned by mac_open. + * + * Release all RRM modules and their resources. + * + * Return: QDF_STATUS + * QDF_STATUS_E_FAILURE success + * QDF_STATUS_SUCCESS failure + */ + +QDF_STATUS rrm_close(struct mac_context *mac) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + tpRrmSMEContext pSmeRrmContext; + uint8_t i; + + for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { + pSmeRrmContext = &mac->rrm.rrmSmeContext[i]; + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state( + &pSmeRrmContext->IterMeasTimer)) { + qdf_status = qdf_mc_timer_stop( + &pSmeRrmContext->IterMeasTimer); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sme_err("Timer stop fail"); + } + + if (pSmeRrmContext->channelList.freq_list) { + qdf_mem_free(pSmeRrmContext->channelList.freq_list); + pSmeRrmContext->channelList.freq_list = NULL; + pSmeRrmContext->channelList.numOfChannels = 0; + } + + qdf_status = + qdf_mc_timer_destroy(&pSmeRrmContext->IterMeasTimer); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sme_err("Fail to destroy timer"); + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&pSmeRrmContext-> + neighborReqControlInfo. + neighborRspWaitTimer)) { + qdf_status = qdf_mc_timer_stop(&pSmeRrmContext-> + neighborReqControlInfo. + neighborRspWaitTimer); + if (QDF_IS_STATUS_ERROR(qdf_status)) + sme_err("Timer stop fail"); + } + + qdf_status = qdf_mc_timer_destroy( + &pSmeRrmContext->neighborReqControlInfo. + neighborRspWaitTimer); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + sme_err("Fail to destroy timer"); + + rrm_ll_purge_neighbor_cache( + mac, &pSmeRrmContext->neighborReportCache); + csr_ll_close(&pSmeRrmContext->neighborReportCache); + } + + return qdf_status; + +} + +QDF_STATUS rrm_start(struct mac_context *mac_ctx) +{ + tpRrmSMEContext smerrmctx; + wlan_scan_requester req_id; + uint8_t i; + + + /* Register with scan component */ + req_id = ucfg_scan_register_requester(mac_ctx->psoc, + "RRM", + sme_rrm_scan_event_callback, + mac_ctx); + + for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { + smerrmctx = &mac_ctx->rrm.rrmSmeContext[i]; + smerrmctx->req_id = req_id; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS rrm_stop(struct mac_context *mac_ctx) +{ + tpRrmSMEContext smerrmctx; + wlan_scan_requester req_id; + uint8_t i; + + for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) { + smerrmctx = &mac_ctx->rrm.rrmSmeContext[i]; + req_id = smerrmctx->req_id; + smerrmctx->req_id = 0; + } + + ucfg_scan_unregister_requester(mac_ctx->psoc, + req_id); + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma.h new file mode 100644 index 0000000000000000000000000000000000000000..afa63f0a540fe9dd2a26895117f156f45e182344 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma.h @@ -0,0 +1,2640 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WMA_H +#define WMA_H + +#include "a_types.h" +#include "qdf_types.h" +#include "osapi_linux.h" +#include "htc_packet.h" +#include "i_qdf_event.h" +#include "wmi_services.h" +#include "wmi_unified.h" +#include "wmi_version.h" +#include "qdf_types.h" +#include "qdf_status.h" +#include "cds_sched.h" +#include "cds_config.h" +#include "sir_mac_prot_def.h" +#include "wma_types.h" +#include +#include "utils_api.h" +#include "lim_types.h" +#include "wmi_unified_api.h" +#include "cdp_txrx_cmn.h" +#include "dbglog.h" +#include "cds_ieee80211_common.h" +#include "wlan_objmgr_psoc_obj.h" +#include +#include +#include "wma_api.h" +#include "wmi_unified_param.h" +#include "wmi.h" + +/* Platform specific configuration for max. no. of fragments */ +#define QCA_OL_11AC_TX_MAX_FRAGS 2 + +/* Private */ + +#define WMA_READY_EVENTID_TIMEOUT 6000 +#define WMA_SERVICE_READY_EXT_TIMEOUT 6000 +#define NAN_CLUSTER_ID_BYTES 4 + +#define WMA_CRASH_INJECT_TIMEOUT 5000 + +/* MAC ID to PDEV ID mapping is as given below + * MAC_ID PDEV_ID + * 0 1 + * 1 2 + * SOC Level WMI_PDEV_ID_SOC + */ +#define WMA_MAC_TO_PDEV_MAP(x) ((x) + (1)) +#define WMA_PDEV_TO_MAC_MAP(x) ((x) - (1)) + +#define WMA_MAX_MGMT_MPDU_LEN 2000 + +#define MAX_PRINT_FAILURE_CNT 50 + +#define WMA_INVALID_VDEV_ID 0xFF + +/* Deprecated logging macros, to be removed. Please do not use in new code */ +#define WMA_LOGD(params ...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_WMA, params) +#define WMA_LOGI(params ...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_WMA, params) +#define WMA_LOGW(params ...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_WMA, params) +#define WMA_LOGE(params ...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_WMA, params) +#define WMA_LOGP(params ...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_WMA, params) + +#define wma_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_WMA, params) +#define wma_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_WMA, params) +#define wma_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_WMA, params) +#define wma_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_WMA, params) +#define wma_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_WMA, params) +#define wma_debug_rl(params...) QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_WMA, params) +#define wma_err_rl(params...) QDF_TRACE_ERROR_RL(QDF_MODULE_ID_WMA, params) + +#define wma_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_WMA, params) +#define wma_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_WMA, params) +#define wma_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_WMA, params) +#define wma_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_WMA, params) +#define wma_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_WMA, params) + +#define WMA_DEBUG_ALWAYS + +#ifdef WMA_DEBUG_ALWAYS +#define WMA_LOGA(params ...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_WMA, params) +#else +#define WMA_LOGA(params ...) +#endif + +#define WMA_WILDCARD_PDEV_ID 0x0 + +#define WMA_HW_DEF_SCAN_MAX_DURATION 30000 /* 30 secs */ + +#define WMA_EAPOL_SUBTYPE_GET_MIN_LEN 21 +#define WMA_EAPOL_INFO_GET_MIN_LEN 23 +#define WMA_IS_DHCP_GET_MIN_LEN 38 +#define WMA_DHCP_SUBTYPE_GET_MIN_LEN 0x11D +#define WMA_DHCP_INFO_GET_MIN_LEN 50 +#define WMA_ARP_SUBTYPE_GET_MIN_LEN 22 +#define WMA_IPV4_PROTO_GET_MIN_LEN 24 +#define WMA_IPV4_PKT_INFO_GET_MIN_LEN 42 +#define WMA_ICMP_SUBTYPE_GET_MIN_LEN 35 +#define WMA_IPV6_PROTO_GET_MIN_LEN 21 +#define WMA_IPV6_PKT_INFO_GET_MIN_LEN 62 +#define WMA_ICMPV6_SUBTYPE_GET_MIN_LEN 55 + +/* Beacon tx rate */ +#define WMA_BEACON_TX_RATE_1_M 10 +#define WMA_BEACON_TX_RATE_2_M 20 +#define WMA_BEACON_TX_RATE_5_5_M 55 +#define WMA_BEACON_TX_RATE_11_M 110 +#define WMA_BEACON_TX_RATE_6_M 60 +#define WMA_BEACON_TX_RATE_9_M 90 +#define WMA_BEACON_TX_RATE_12_M 120 +#define WMA_BEACON_TX_RATE_18_M 180 +#define WMA_BEACON_TX_RATE_24_M 240 +#define WMA_BEACON_TX_RATE_36_M 360 +#define WMA_BEACON_TX_RATE_48_M 480 +#define WMA_BEACON_TX_RATE_54_M 540 + +/* Roaming default values + * All time and period values are in milliseconds. + * All rssi values are in dB except for WMA_NOISE_FLOOR_DBM_DEFAULT. + */ + +#define WMA_ROAM_SCAN_CHANNEL_SWITCH_TIME (4) +#define WMA_NOISE_FLOOR_DBM_DEFAULT (-96) +#define WMA_RSSI_MIN_VALUE (-128) +#define WMA_RSSI_MAX_VALUE (127) +#define WMA_ROAM_RSSI_DIFF_DEFAULT (5) +#define WMA_ROAM_DWELL_TIME_ACTIVE_DEFAULT (100) +#define WMA_ROAM_DWELL_TIME_PASSIVE_DEFAULT (110) +#define WMA_ROAM_MIN_REST_TIME_DEFAULT (50) +#define WMA_ROAM_MAX_REST_TIME_DEFAULT (500) + +#define WMA_INVALID_KEY_IDX 0xff + +#define WMA_MAX_RF_CHAINS(x) ((1 << x) - 1) +#define WMA_MIN_RF_CHAINS (1) +#define WMA_MAX_NSS (2) + +#define WMA_NOA_IE_SIZE(num_desc) (2 + (13 * (num_desc))) +#define WMA_MAX_NOA_DESCRIPTORS 4 + +#define WMA_TIM_SUPPORTED_PVB_LENGTH ((HAL_NUM_STA / 8) + 1) + +#define WMA_BSS_STATUS_STARTED 0x1 +#define WMA_BSS_STATUS_STOPPED 0x2 + +#define WMA_PEER_ASSOC_CNF_START 0x01 +#define WMA_PEER_ASSOC_TIMEOUT SIR_PEER_ASSOC_TIMEOUT + +#define WMA_DELETE_STA_RSP_START 0x02 +#define WMA_DELETE_STA_TIMEOUT SIR_DELETE_STA_TIMEOUT + +#define WMA_DEL_P2P_SELF_STA_RSP_START 0x03 +#define WMA_SET_LINK_PEER_RSP 0x04 +#define WMA_DELETE_PEER_RSP 0x05 + +#define WMA_PDEV_SET_HW_MODE_RESP 0x06 +#define WMA_PDEV_MAC_CFG_RESP 0x07 + +/* FW response timeout values in milli seconds */ +#define WMA_VDEV_PLCY_MGR_TIMEOUT SIR_VDEV_PLCY_MGR_TIMEOUT +#define WMA_VDEV_HW_MODE_REQUEST_TIMEOUT WMA_VDEV_PLCY_MGR_TIMEOUT +#define WMA_VDEV_DUAL_MAC_CFG_TIMEOUT WMA_VDEV_PLCY_MGR_TIMEOUT +#define WMA_VDEV_PLCY_MGR_WAKE_LOCK_TIMEOUT \ + (WMA_VDEV_PLCY_MGR_TIMEOUT + 500) + + +#define WMA_VDEV_SET_KEY_WAKELOCK_TIMEOUT WAKELOCK_DURATION_RECOMMENDED + +#define WMA_TX_Q_RECHECK_TIMER_WAIT 2 /* 2 ms */ +#define WMA_MAX_NUM_ARGS 8 + +#define WMA_SMPS_PARAM_VALUE_S 29 + +/* + * Setting the Tx Comp Timeout to 1 secs. + * TODO: Need to Revist the Timing + */ +#define WMA_TX_FRAME_COMPLETE_TIMEOUT 1000 +#define WMA_TX_FRAME_BUFFER_NO_FREE 0 +#define WMA_TX_FRAME_BUFFER_FREE 1 + +/* + * TODO: Add WMI_CMD_ID_MAX as part of WMI_CMD_ID + * instead of assigning it to the last valid wmi + * cmd+1 to avoid updating this when a command is + * added/deleted. + */ +#define WMI_CMDID_MAX (WMI_TXBF_CMDID + 1) + +#define WMA_NLO_FREQ_THRESH 1000 /* in MHz */ +#define WMA_MSEC_TO_USEC(msec) (msec * 1000) /* msec to usec */ + +#define WMA_AUTH_REQ_RECV_WAKE_LOCK_TIMEOUT WAKELOCK_DURATION_RECOMMENDED +#define WMA_ASSOC_REQ_RECV_WAKE_LOCK_DURATION WAKELOCK_DURATION_RECOMMENDED +#define WMA_DEAUTH_RECV_WAKE_LOCK_DURATION WAKELOCK_DURATION_RECOMMENDED +#define WMA_DISASSOC_RECV_WAKE_LOCK_DURATION WAKELOCK_DURATION_RECOMMENDED +#define WMA_ROAM_HO_WAKE_LOCK_DURATION (500) /* in msec */ +#define WMA_ROAM_PREAUTH_WAKE_LOCK_DURATION (2 * 1000) + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +#define WMA_AUTO_SHUTDOWN_WAKE_LOCK_DURATION WAKELOCK_DURATION_RECOMMENDED +#endif +#define WMA_BMISS_EVENT_WAKE_LOCK_DURATION WAKELOCK_DURATION_RECOMMENDED +#define WMA_FW_RSP_EVENT_WAKE_LOCK_DURATION WAKELOCK_DURATION_MAX + +#define WMA_TXMIC_LEN 8 +#define WMA_RXMIC_LEN 8 +#define WMA_IV_KEY_LEN 16 + +/* + * Length = (2 octets for Index and CTWin/Opp PS) and + * (13 octets for each NOA Descriptors) + */ + +#define WMA_P2P_NOA_IE_OPP_PS_SET (0x80) +#define WMA_P2P_NOA_IE_CTWIN_MASK (0x7F) + +#define WMA_P2P_IE_ID 0xdd +#define WMA_P2P_WFA_OUI { 0x50, 0x6f, 0x9a } +#define WMA_P2P_WFA_VER 0x09 /* ver 1.0 */ + +/* P2P Sub element definitions (according to table 5 of Wifi's P2P spec) */ +#define WMA_P2P_SUB_ELEMENT_STATUS 0 +#define WMA_P2P_SUB_ELEMENT_MINOR_REASON 1 +#define WMA_P2P_SUB_ELEMENT_CAPABILITY 2 +#define WMA_P2P_SUB_ELEMENT_DEVICE_ID 3 +#define WMA_P2P_SUB_ELEMENT_GO_INTENT 4 +#define WMA_P2P_SUB_ELEMENT_CONFIGURATION_TIMEOUT 5 +#define WMA_P2P_SUB_ELEMENT_LISTEN_CHANNEL 6 +#define WMA_P2P_SUB_ELEMENT_GROUP_BSSID 7 +#define WMA_P2P_SUB_ELEMENT_EXTENDED_LISTEN_TIMING 8 +#define WMA_P2P_SUB_ELEMENT_INTENDED_INTERFACE_ADDR 9 +#define WMA_P2P_SUB_ELEMENT_MANAGEABILITY 10 +#define WMA_P2P_SUB_ELEMENT_CHANNEL_LIST 11 +#define WMA_P2P_SUB_ELEMENT_NOA 12 +#define WMA_P2P_SUB_ELEMENT_DEVICE_INFO 13 +#define WMA_P2P_SUB_ELEMENT_GROUP_INFO 14 +#define WMA_P2P_SUB_ELEMENT_GROUP_ID 15 +#define WMA_P2P_SUB_ELEMENT_INTERFACE 16 +#define WMA_P2P_SUB_ELEMENT_OP_CHANNEL 17 +#define WMA_P2P_SUB_ELEMENT_INVITATION_FLAGS 18 +#define WMA_P2P_SUB_ELEMENT_VENDOR 221 + +/* Macros for handling unaligned memory accesses */ +#define P2PIE_PUT_LE16(a, val) \ + do { \ + (a)[1] = ((uint16_t) (val)) >> 8; \ + (a)[0] = ((uint16_t) (val)) & 0xff; \ + } while (0) + +#define P2PIE_PUT_LE32(a, val) \ + do { \ + (a)[3] = (uint8_t) ((((uint32_t) (val)) >> 24) & 0xff); \ + (a)[2] = (uint8_t) ((((uint32_t) (val)) >> 16) & 0xff); \ + (a)[1] = (uint8_t) ((((uint32_t) (val)) >> 8) & 0xff); \ + (a)[0] = (uint8_t) (((uint32_t) (val)) & 0xff); \ + } while (0) + + +#define WMA_DEFAULT_MAX_PSPOLL_BEFORE_WAKE 1 + +#define WMA_VHT_PPS_PAID_MATCH 1 +#define WMA_VHT_PPS_GID_MATCH 2 +#define WMA_VHT_PPS_DELIM_CRC_FAIL 3 + +#define WMA_DEFAULT_HW_MODE_INDEX 0xFFFF +#define TWO_THIRD (2/3) + +/** + * WMA hardware mode list bit-mask definitions. + * Bits 4:0, 31:29 are unused. + * + * The below definitions are added corresponding to WMI DBS HW mode + * list to make it independent of firmware changes for WMI definitions. + * Currently these definitions have dependency with BIT positions of + * the existing WMI macros. Thus, if the BIT positions are changed for + * WMI macros, then these macros' BIT definitions are also need to be + * changed. + */ +#define WMA_HW_MODE_MAC0_TX_STREAMS_BITPOS (28) +#define WMA_HW_MODE_MAC0_RX_STREAMS_BITPOS (24) +#define WMA_HW_MODE_MAC1_TX_STREAMS_BITPOS (20) +#define WMA_HW_MODE_MAC1_RX_STREAMS_BITPOS (16) +#define WMA_HW_MODE_MAC0_BANDWIDTH_BITPOS (12) +#define WMA_HW_MODE_MAC1_BANDWIDTH_BITPOS (8) +#define WMA_HW_MODE_DBS_MODE_BITPOS (7) +#define WMA_HW_MODE_AGILE_DFS_MODE_BITPOS (6) +#define WMA_HW_MODE_SBS_MODE_BITPOS (5) + +#define WMA_HW_MODE_MAC0_TX_STREAMS_MASK \ + (0xf << WMA_HW_MODE_MAC0_TX_STREAMS_BITPOS) +#define WMA_HW_MODE_MAC0_RX_STREAMS_MASK \ + (0xf << WMA_HW_MODE_MAC0_RX_STREAMS_BITPOS) +#define WMA_HW_MODE_MAC1_TX_STREAMS_MASK \ + (0xf << WMA_HW_MODE_MAC1_TX_STREAMS_BITPOS) +#define WMA_HW_MODE_MAC1_RX_STREAMS_MASK \ + (0xf << WMA_HW_MODE_MAC1_RX_STREAMS_BITPOS) +#define WMA_HW_MODE_MAC0_BANDWIDTH_MASK \ + (0xf << WMA_HW_MODE_MAC0_BANDWIDTH_BITPOS) +#define WMA_HW_MODE_MAC1_BANDWIDTH_MASK \ + (0xf << WMA_HW_MODE_MAC1_BANDWIDTH_BITPOS) +#define WMA_HW_MODE_DBS_MODE_MASK \ + (0x1 << WMA_HW_MODE_DBS_MODE_BITPOS) +#define WMA_HW_MODE_AGILE_DFS_MODE_MASK \ + (0x1 << WMA_HW_MODE_AGILE_DFS_MODE_BITPOS) +#define WMA_HW_MODE_SBS_MODE_MASK \ + (0x1 << WMA_HW_MODE_SBS_MODE_BITPOS) + +#define WMA_HW_MODE_MAC0_TX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, WMA_HW_MODE_MAC0_TX_STREAMS_BITPOS, 4, value) +#define WMA_HW_MODE_MAC0_RX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, WMA_HW_MODE_MAC0_RX_STREAMS_BITPOS, 4, value) +#define WMA_HW_MODE_MAC1_TX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, WMA_HW_MODE_MAC1_TX_STREAMS_BITPOS, 4, value) +#define WMA_HW_MODE_MAC1_RX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, WMA_HW_MODE_MAC1_RX_STREAMS_BITPOS, 4, value) +#define WMA_HW_MODE_MAC0_BANDWIDTH_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, WMA_HW_MODE_MAC0_BANDWIDTH_BITPOS, 4, value) +#define WMA_HW_MODE_MAC1_BANDWIDTH_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, WMA_HW_MODE_MAC1_BANDWIDTH_BITPOS, 4, value) +#define WMA_HW_MODE_DBS_MODE_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, WMA_HW_MODE_DBS_MODE_BITPOS, 1, value) +#define WMA_HW_MODE_AGILE_DFS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, WMA_HW_MODE_AGILE_DFS_MODE_BITPOS, 1, value) +#define WMA_HW_MODE_SBS_MODE_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, WMA_HW_MODE_SBS_MODE_BITPOS, 1, value) + +#define WMA_HW_MODE_MAC0_TX_STREAMS_GET(hw_mode) \ + ((hw_mode & WMA_HW_MODE_MAC0_TX_STREAMS_MASK) >> \ + WMA_HW_MODE_MAC0_TX_STREAMS_BITPOS) +#define WMA_HW_MODE_MAC0_RX_STREAMS_GET(hw_mode) \ + ((hw_mode & WMA_HW_MODE_MAC0_RX_STREAMS_MASK) >> \ + WMA_HW_MODE_MAC0_RX_STREAMS_BITPOS) +#define WMA_HW_MODE_MAC1_TX_STREAMS_GET(hw_mode) \ + ((hw_mode & WMA_HW_MODE_MAC1_TX_STREAMS_MASK) >> \ + WMA_HW_MODE_MAC1_TX_STREAMS_BITPOS) +#define WMA_HW_MODE_MAC1_RX_STREAMS_GET(hw_mode) \ + ((hw_mode & WMA_HW_MODE_MAC1_RX_STREAMS_MASK) >> \ + WMA_HW_MODE_MAC1_RX_STREAMS_BITPOS) +#define WMA_HW_MODE_MAC0_BANDWIDTH_GET(hw_mode) \ + ((hw_mode & WMA_HW_MODE_MAC0_BANDWIDTH_MASK) >> \ + WMA_HW_MODE_MAC0_BANDWIDTH_BITPOS) +#define WMA_HW_MODE_MAC1_BANDWIDTH_GET(hw_mode) \ + ((hw_mode & WMA_HW_MODE_MAC1_BANDWIDTH_MASK) >> \ + WMA_HW_MODE_MAC1_BANDWIDTH_BITPOS) +#define WMA_HW_MODE_DBS_MODE_GET(hw_mode) \ + ((hw_mode & WMA_HW_MODE_DBS_MODE_MASK) >> \ + WMA_HW_MODE_DBS_MODE_BITPOS) +#define WMA_HW_MODE_AGILE_DFS_GET(hw_mode) \ + ((hw_mode & WMA_HW_MODE_AGILE_DFS_MODE_MASK) >> \ + WMA_HW_MODE_AGILE_DFS_MODE_BITPOS) +#define WMA_HW_MODE_SBS_MODE_GET(hw_mode) \ + ((hw_mode & WMA_HW_MODE_SBS_MODE_MASK) >> \ + WMA_HW_MODE_SBS_MODE_BITPOS) + +/* + * Extract 2G or 5G tx/rx chainmask + * format of txrx_chainmask (from wmi_service_ready_event_fixed_param): + * [7:0] - 2G band tx chain mask + * [15:8] - 2G band rx chain mask + * [23:16] - 5G band tx chain mask + * [31:24] - 5G band rx chain mask + */ +#define EXTRACT_TX_CHAIN_MASK_2G(chainmask) ((chainmask) & 0xFF) +#define EXTRACT_RX_CHAIN_MASK_2G(chainmask) (((chainmask) >> 8) & 0xFF) +#define EXTRACT_TX_CHAIN_MASK_5G(chainmask) (((chainmask) >> 16) & 0xFF) +#define EXTRACT_RX_CHAIN_MASK_5G(chainmask) (((chainmask) >> 24) & 0xFF) + +/* + * PROBE_REQ_TX_DELAY + * param to specify probe request Tx delay for scans triggered on this VDEV + */ +#define PROBE_REQ_TX_DELAY 10 + +/* PROBE_REQ_TX_TIME_GAP + * param to specify the time gap between each set of probe request transmission. + * The number of probe requests in each set depends on the ssid_list and, + * bssid_list in the scan request. This parameter will get applied only, + * for the scans triggered on this VDEV. + */ +#define PROBE_REQ_TX_TIME_GAP 20 + +typedef void (*txFailIndCallback)(uint8_t *peer_mac, uint8_t seqNo); + + +/** + * enum wma_rx_exec_ctx - wma rx execution context + * @WMA_RX_WORK_CTX: work queue context execution + * @WMA_RX_TASKLET_CTX: tasklet context execution + * @WMA_RX_SERIALIZER_CTX: MC thread context execution + * + */ +enum wma_rx_exec_ctx { + WMA_RX_WORK_CTX = WMI_RX_WORK_CTX, + WMA_RX_TASKLET_CTX = WMI_RX_TASKLET_CTX, + WMA_RX_SERIALIZER_CTX = WMI_RX_SERIALIZER_CTX, +}; + +/** + * struct beacon_info - structure to store beacon template + * @buf: skb ptr + * @len: length + * @dma_mapped: is it dma mapped or not + * @tim_ie_offset: TIM IE offset + * @dtim_count: DTIM count + * @seq_no: sequence no + * @noa_sub_ie: NOA sub IE + * @noa_sub_ie_len: NOA sub IE length + * @noa_ie: NOA IE + * @p2p_ie_offset: p2p IE offset + * @lock: lock + */ +struct beacon_info { + qdf_nbuf_t buf; + uint32_t len; + uint8_t dma_mapped; + uint32_t tim_ie_offset; + uint8_t dtim_count; + uint16_t seq_no; + uint8_t noa_sub_ie[2 + WMA_NOA_IE_SIZE(WMA_MAX_NOA_DESCRIPTORS)]; + uint16_t noa_sub_ie_len; + uint8_t *noa_ie; + uint16_t p2p_ie_offset; + qdf_spinlock_t lock; +}; + +/** + * struct beacon_tim_ie - structure to store TIM IE of beacon + * @tim_ie: tim ie + * @tim_len: tim ie length + * @dtim_count: dtim count + * @dtim_period: dtim period + * @tim_bitctl: tim bit control + * @tim_bitmap: tim bitmap + */ +struct beacon_tim_ie { + uint8_t tim_ie; + uint8_t tim_len; + uint8_t dtim_count; + uint8_t dtim_period; + uint8_t tim_bitctl; + uint8_t tim_bitmap[1]; +} __ATTRIB_PACK; + +/** + * struct pps - packet power save parameter + * @paid_match_enable: paid match enable + * @gid_match_enable: gid match enable + * @tim_clear: time clear + * @dtim_clear: dtim clear + * @eof_delim: eof delim + * @mac_match: mac match + * @delim_fail: delim fail + * @nsts_zero: nsts zero + * @rssi_chk: RSSI check + * @ebt_5g: ebt 5GHz + */ +struct pps { + bool paid_match_enable; + bool gid_match_enable; + bool tim_clear; + bool dtim_clear; + bool eof_delim; + bool mac_match; + bool delim_fail; + bool nsts_zero; + bool rssi_chk; + bool ebt_5g; +}; + +/** + * struct qpower_params - qpower related parameters + * @max_ps_poll_cnt: max ps poll count + * @max_tx_before_wake: max tx before wake + * @spec_ps_poll_wake_interval: ps poll wake interval + * @max_spec_nodata_ps_poll: no data ps poll + */ +struct qpower_params { + uint32_t max_ps_poll_cnt; + uint32_t max_tx_before_wake; + uint32_t spec_ps_poll_wake_interval; + uint32_t max_spec_nodata_ps_poll; +}; + + +/** + * struct gtx_config_t - GTX config + * @gtxRTMask: for HT and VHT rate masks + * @gtxUsrcfg: host request for GTX mask + * @gtxPERThreshold: PER Threshold (default: 10%) + * @gtxPERMargin: PER margin (default: 2%) + * @gtxTPCstep: TCP step (default: 1) + * @gtxTPCMin: TCP min (default: 5) + * @gtxBWMask: BW mask (20/40/80/160 Mhz) + */ +typedef struct { + uint32_t gtxRTMask[2]; + uint32_t gtxUsrcfg; + uint32_t gtxPERThreshold; + uint32_t gtxPERMargin; + uint32_t gtxTPCstep; + uint32_t gtxTPCMin; + uint32_t gtxBWMask; +} gtx_config_t; + +/** + * struct pdev_cli_config_t - store pdev parameters + * @ani_enable: ANI is enabled/disable on target + * @ani_poll_len: store ANI polling period + * @ani_listen_len: store ANI listening period + * @ani_ofdm_level: store ANI OFDM immunity level + * @ani_cck_level: store ANI CCK immunity level + * @cwmenable: Dynamic bw is enable/disable in fw + * @txchainmask: tx chain mask + * @rxchainmask: rx chain mask + * @txpow2g: tx power limit for 2GHz + * @txpow5g: tx power limit for 5GHz + * + * This structure stores pdev parameters. + * Some of these parameters are set in fw and some + * parameters are only maintained in host. + */ +typedef struct { + uint32_t ani_enable; + uint32_t ani_poll_len; + uint32_t ani_listen_len; + uint32_t ani_ofdm_level; + uint32_t ani_cck_level; + uint32_t cwmenable; + uint32_t cts_cbw; + uint32_t txchainmask; + uint32_t rxchainmask; + uint32_t txpow2g; + uint32_t txpow5g; +} pdev_cli_config_t; + +/** + * struct vdev_cli_config_t - store vdev parameters + * @nss: nss width + * @ldpc: is ldpc is enable/disable + * @tx_stbc: TX STBC is enable/disable + * @rx_stbc: RX STBC is enable/disable + * @shortgi: short gi is enable/disable + * @rtscts_en: RTS/CTS is enable/disable + * @chwidth: channel width + * @tx_rate: tx rate + * @ampdu: ampdu size + * @amsdu: amsdu size + * @erx_adjust: enable/disable early rx enable + * @erx_bmiss_num: target bmiss number per sample + * @erx_bmiss_cycle: sample cycle + * @erx_slop_step: slop_step value + * @erx_init_slop: init slop + * @erx_adj_pause: pause adjust enable/disable + * @erx_dri_sample: enable/disable drift sample + * @pps_params: packet power save parameters + * @qpower_params: qpower parameters + * @gtx_info: GTX offload info + * @dcm: DCM enable/disable + * @range_ext: HE range extension enable/disable + * + * This structure stores vdev parameters. + * Some of these parameters are set in fw and some + * parameters are only maintained in host. + */ +typedef struct { + uint32_t nss; + uint32_t ldpc; + uint32_t tx_stbc; + uint32_t rx_stbc; + uint32_t shortgi; + uint32_t rtscts_en; + uint32_t chwidth; + uint32_t tx_rate; + uint32_t ampdu; + uint32_t amsdu; + uint32_t erx_adjust; + uint32_t erx_bmiss_num; + uint32_t erx_bmiss_cycle; + uint32_t erx_slop_step; + uint32_t erx_init_slop; + uint32_t erx_adj_pause; + uint32_t erx_dri_sample; + struct pps pps_params; + struct qpower_params qpower_params; + gtx_config_t gtx_info; +#ifdef WLAN_FEATURE_11AX + uint8_t dcm; + uint8_t range_ext; +#endif +} vdev_cli_config_t; + +/** + * struct wma_version_info - Store wmi version info + * @major: wmi major version + * @minor: wmi minor version + * @revision: wmi revision number + */ +struct wma_version_info { + u_int32_t major; + u_int32_t minor; + u_int32_t revision; +}; + +#define CMAC_IPN_LEN (6) +#define WMA_IGTK_KEY_INDEX_4 (4) +#define WMA_IGTK_KEY_INDEX_5 (5) + +/** + * struct wma_igtk_ipn_t - GTK IPN info + * @ipn: IPN info + */ +typedef struct { + uint8_t ipn[CMAC_IPN_LEN]; +} wma_igtk_ipn_t; + +/** + * struct wma_igtk_key_t - GTK key + * @key_id: key id + * @key_cipher: key type + */ +typedef struct { + /* IPN is maintained per iGTK keyID + * 0th index for iGTK keyID = 4; + * 1st index for iGTK KeyID = 5 + */ + wma_igtk_ipn_t key_id[2]; + uint32_t key_cipher; +} wma_igtk_key_t; + +struct roam_synch_frame_ind { + uint32_t bcn_probe_rsp_len; + uint8_t *bcn_probe_rsp; + uint8_t is_beacon; + uint32_t reassoc_req_len; + uint8_t *reassoc_req; + uint32_t reassoc_rsp_len; + uint8_t *reassoc_rsp; +}; + +/* Max number of invalid peer entries */ +#define INVALID_PEER_MAX_NUM 5 + +/** + * struct wma_invalid_peer_params - stores invalid peer entries + * @rx_macaddr: store mac addr of invalid peer + */ +struct wma_invalid_peer_params { + uint8_t rx_macaddr[QDF_MAC_ADDR_SIZE]; +}; + +/** + * struct wma_txrx_node - txrx node + * @vdev: pointer to vdev object + * @beacon: beacon info + * @config: per vdev config parameters + * @scan_info: scan info + * @type: type + * @sub_type: sub type + * @nlo_match_evt_received: is nlo match event received or not + * @pno_in_progress: is pno in progress or not + * @plm_in_progress: is plm in progress or not + * @beaconInterval: beacon interval + * @llbCoexist: 11b coexist + * @shortSlotTimeSupported: is short slot time supported or not + * @dtimPeriod: DTIM period + * @mhz: channel frequency in KHz + * @chan_width: channel bandwidth + * @vdev_up: is vdev up or not + * @tsfadjust: TSF adjust + * @addBssStaContext: add bss context + * @aid: association id + * @rmfEnabled: Robust Management Frame (RMF) enabled/disabled + * @key: GTK key + * @uapsd_cached_val: uapsd cached value + * @stats_rsp: stats response + * @del_staself_req: delete sta self request + * @bss_status: bss status + * @nss: nss value + * @is_channel_switch: is channel switch + * @pause_bitmap: pause bitmap + * @nwType: network type (802.11a/b/g/n/ac) + * @staKeyParams: sta key parameters + * @ps_enabled: is powersave enable/disable + * @peer_count: peer count + * @roam_synch_in_progress: flag is in progress or not + * @plink_status_req: link status request + * @psnr_req: snr request + * @tx_streams: number of tx streams can be used by the vdev + * @mac_id: the mac on which vdev is on + * @wep_default_key_idx: wep default index for group key + * @arp_offload_req: cached arp offload request + * @ns_offload_req: cached ns offload request + * @rcpi_req: rcpi request + * @in_bmps: Whether bmps for this interface has been enabled + * @vdev_set_key_wakelock: wakelock to protect vdev set key op with firmware + * @vdev_set_key_runtime_wakelock: runtime pm wakelock for set key + * @ch_freq: channel frequency + * @roam_scan_stats_req: cached roam scan stats request + * @wma_invalid_peer_params: structure storing invalid peer params + * @invalid_peer_idx: invalid peer index + * It stores parameters per vdev in wma. + */ +struct wma_txrx_node { + struct wlan_objmgr_vdev *vdev; + struct beacon_info *beacon; + vdev_cli_config_t config; + uint32_t type; + uint32_t sub_type; +#ifdef FEATURE_WLAN_ESE + bool plm_in_progress; +#endif + tSirMacBeaconInterval beaconInterval; + uint8_t llbCoexist; + uint8_t shortSlotTimeSupported; + uint8_t dtimPeriod; + A_UINT32 mhz; + enum phy_ch_width chan_width; + bool vdev_active; + uint64_t tsfadjust; + tAddStaParams *addBssStaContext; + uint16_t aid; + uint8_t rmfEnabled; + wma_igtk_key_t key; + uint32_t uapsd_cached_val; + void *del_staself_req; + bool is_del_sta_defered; + qdf_atomic_t bss_status; + enum tx_rate_info rate_flags; + uint8_t nss; + uint16_t pause_bitmap; + uint32_t nwType; + tSetStaKeyParams *staKeyParams; + uint32_t peer_count; + bool roam_synch_in_progress; + void *plink_status_req; + void *psnr_req; +#ifdef FEATURE_WLAN_EXTSCAN + bool extscan_in_progress; +#endif + uint32_t tx_streams; + uint32_t mac_id; + bool roaming_in_progress; + int32_t roam_synch_delay; + uint8_t wep_default_key_idx; + struct sme_rcpi_req *rcpi_req; + bool in_bmps; + struct beacon_filter_param beacon_filter; + bool beacon_filter_enabled; + qdf_wake_lock_t vdev_set_key_wakelock; + qdf_runtime_lock_t vdev_set_key_runtime_wakelock; + struct roam_synch_frame_ind roam_synch_frame_ind; + bool is_waiting_for_key; + uint32_t ch_freq; + uint16_t ch_flagext; + struct sir_roam_scan_stats *roam_scan_stats_req; + struct wma_invalid_peer_params invalid_peers[INVALID_PEER_MAX_NUM]; + uint8_t invalid_peer_idx; +}; + +/** + * struct ibss_power_save_params - IBSS power save parameters + * @atimWindowLength: ATIM window length + * @isPowerSaveAllowed: is power save allowed + * @isPowerCollapseAllowed: is power collapsed allowed + * @isAwakeonTxRxEnabled: is awake on tx/rx enabled + * @inactivityCount: inactivity count + * @txSPEndInactivityTime: tx SP end inactivity time + * @ibssPsWarmupTime: IBSS power save warm up time + * @ibssPs1RxChainInAtimEnable: IBSS power save rx chain in ATIM enable + */ +typedef struct { + uint32_t atimWindowLength; + uint32_t isPowerSaveAllowed; + uint32_t isPowerCollapseAllowed; + uint32_t isAwakeonTxRxEnabled; + uint32_t inactivityCount; + uint32_t txSPEndInactivityTime; + uint32_t ibssPsWarmupTime; + uint32_t ibssPs1RxChainInAtimEnable; +} ibss_power_save_params; + +/** + * struct mac_ss_bw_info - hw_mode_list PHY/MAC params for each MAC + * @mac_tx_stream: Max TX stream + * @mac_rx_stream: Max RX stream + * @mac_bw: Max bandwidth + */ +struct mac_ss_bw_info { + uint32_t mac_tx_stream; + uint32_t mac_rx_stream; + uint32_t mac_bw; +}; + +/** + * struct wma_ini_config - Structure to hold wma ini configuration + * @max_no_of_peers: Max Number of supported + * + * Placeholder for WMA ini parameters. + */ +struct wma_ini_config { + uint8_t max_no_of_peers; +}; + +/** + * struct wma_valid_channels - Channel details part of WMI_SCAN_CHAN_LIST_CMDID + * @num_channels: Number of channels + * @ch_freq_list: Channel Frequency list + */ +struct wma_valid_channels { + uint8_t num_channels; + uint32_t ch_freq_list[NUM_CHANNELS]; +}; + +#ifdef FEATURE_WLM_STATS +/** + * struct wma_wlm_stats_data - Data required to be used to send WLM req + * @wlm_stats_max_size: Buffer size provided by userspace + * @wlm_stats_cookie: Cookie to retrieve WLM req data + * @wlm_stats_callback: Callback to be used to send WLM response + */ +struct wma_wlm_stats_data { + uint32_t wlm_stats_max_size; + void *wlm_stats_cookie; + wma_wlm_stats_cb wlm_stats_callback; +}; +#endif + +/** + * struct t_wma_handle - wma context + * @wmi_handle: wmi handle + * @cds_context: cds handle + * @mac_context: mac context + * @psoc: psoc context + * @pdev: physical device global object + * @target_suspend: target suspend event + * @recovery_event: wma FW recovery event + * @max_station: max stations + * @max_bssid: max bssid + * @myaddr: current mac address + * @hwaddr: mac address from EEPROM + * @lpss_support: LPSS feature is supported in target or not + * @wmi_ready: wmi status flag + * @wlan_init_status: wlan init status + * @qdf_dev: qdf device + * @wmi_service_bitmap: wmi services bitmap received from Target + * @wmi_service_ext_bitmap: extended wmi services bitmap received from Target + * @tx_frm_download_comp_cb: Tx Frame Compl Cb registered by umac + * @tx_frm_download_comp_event: Event to wait for tx download completion + * @tx_queue_empty_event: Dummy event to wait for draining MSDUs left + * in hardware tx queue and before requesting VDEV_STOP. Nobody will + * set this and wait will timeout, and code will poll the pending tx + * descriptors number to be zero. + * @umac_ota_ack_cb: Ack Complete Callback registered by umac + * @umac_data_ota_ack_cb: ack complete callback + * @last_umac_data_ota_timestamp: timestamp when OTA of last umac data + * was done + * @last_umac_data_nbuf: cache nbuf ptr for the last umac data buf + * @needShutdown: is shutdown needed or not + * @tgt_cfg_update_cb: configuration update callback + * @reg_cap: regulatory capablities + * @scan_id: scan id + * @interfaces: txrx nodes(per vdev) + * @pdevconfig: pdev related configrations + * @wma_hold_req_queue: Queue use to serialize requests to firmware + * @wma_hold_req_q_lock: Mutex for @wma_hold_req_queue + * @vht_supp_mcs: VHT supported MCS + * @is_fw_assert: is fw asserted + * @ack_work_ctx: Context for deferred processing of TX ACK + * @powersave_mode: power save mode + * @pGetRssiReq: get RSSI request + * @get_one_peer_info: When a "get peer info" request is active, is + * the request for a single peer? + * @peer_macaddr: When @get_one_peer_info is true, the peer's mac address + * @thermal_mgmt_info: Thermal mitigation related info + * @enable_mc_list: To Check if Multicast list filtering is enabled in FW + * @ibss_started: is IBSS started or not + * @ibsskey_info: IBSS key info + * @hddTxFailCb: tx fail indication callback + * @extscan_wake_lock: extscan wake lock + * @wow_wake_lock: wow wake lock + * @wow_auth_req_wl: wow wake lock for auth req + * @wow_assoc_req_wl: wow wake lock for assoc req + * @wow_deauth_rec_wl: wow wake lock for deauth req + * @wow_disassoc_rec_wl: wow wake lock for disassoc req + * @wow_ap_assoc_lost_wl: wow wake lock for assoc lost req + * @wow_auto_shutdown_wl: wow wake lock for shutdown req + * @roam_ho_wl: wake lock for roam handoff req + * @wow_nack: wow negative ack flag + * @is_wow_bus_suspended: is wow bus suspended flag + * @suitable_ap_hb_failure: better ap found + * @suitable_ap_hb_failure_rssi: RSSI when suitable_ap_hb_failure + * triggered for later usage to report RSSI at beacon miss scenario + * @wma_ibss_power_save_params: IBSS Power Save config Parameters + * @IsRArateLimitEnabled: RA rate limiti s enabled or not + * @RArateLimitInterval: RA rate limit interval + * @is_lpass_enabled: Flag to indicate if LPASS feature is enabled or not + * @staMaxLIModDtim: station max listen interval + * @staModDtim: station mode DTIM + * @staDynamicDtim: station dynamic DTIM + * @hw_bd_id: hardware board id + * @hw_bd_info: hardware board info + * @miracast_value: miracast value + * @log_completion_timer: log completion timer + * @num_dbs_hw_modes: Number of HW modes supported by the FW + * @hw_mode: DBS HW mode list + * @old_hw_mode_index: Previous configured HW mode index + * @new_hw_mode_index: Current configured HW mode index + * @peer_authorized_cb: peer authorized hdd callback + * @ocb_config_req: OCB request context + * @self_gen_frm_pwr: Self-generated frame power + * @tx_chain_mask_cck: Is the CCK tx chain mask enabled + * @service_ready_ext_timer: Timer for service ready extended. Note + * this is a a timer instead of wait event because on receiving the + * service ready event, we will be waiting on the MC thread for the + * service extended ready event which is also processed in MC + * thread. This leads to MC thread being stuck. Alternative was to + * process these events in tasklet/workqueue context. But, this + * leads to race conditions when the events are processed in two + * different context. So, processing ready event and extended ready + * event in the serialized MC thread context with a timer. + * @csr_roam_synch_cb: CSR callback for firmware Roam Sync events + * @pe_roam_synch_cb: pe callback for firmware Roam Sync events + * @csr_roam_auth_event_handle_cb: CSR callback for target authentication + * offload event. + * @wmi_cmd_rsp_wake_lock: wmi command response wake lock + * @wmi_cmd_rsp_runtime_lock: wmi command response bus lock + * @active_uc_apf_mode: Setting that determines how APF is applied in + * active mode for uc packets + * @active_mc_bc_apf_mode: Setting that determines how APF is applied in + * active mode for MC/BC packets + * @ini_config: Initial configuration from upper layer + * @saved_chan: saved channel list sent as part of + * WMI_SCAN_CHAN_LIST_CMDID + * @nan_datapath_enabled: Is NAN datapath support enabled in firmware? + * @fw_timeout_crash: Should firmware be reset upon response timeout? + * @sub_20_support: Does target support sub-20MHz bandwidth (aka + * half-rate and quarter-rate)? + * @is_dfs_offloaded: Is dfs and cac timer offloaded? + * @wma_mgmt_tx_packetdump_cb: Callback function for TX packet dump + * @wma_mgmt_rx_packetdump_cb: Callback function for RX packet dump + * @rcpi_enabled: Is RCPI enabled? + * @link_stats_results: Structure for handing link stats from firmware + * @tx_fail_cnt: Number of TX failures + * @wlm_data: Data required for WLM req and resp handling + * @he_cap: 802.11ax capabilities + * @bandcapability: band capability configured through ini + * @tx_bfee_8ss_enabled: Is Tx Beamformee support for 8x8 enabled? + * @in_imps: Is device in Idle Mode Power Save? + * @dynamic_nss_chains_update: per vdev nss, chains update + * @ito_repeat_count: Indicates ito repeated count + * @wma_fw_time_sync_timer: timer used for firmware time sync + * * @fw_therm_throt_support: FW Supports thermal throttling? + * + * This structure is the global wma context. It contains global wma + * module parameters and handles of other modules. + + */ +typedef struct { + void *wmi_handle; + void *cds_context; + struct mac_context *mac_context; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + qdf_event_t target_suspend; + qdf_event_t runtime_suspend; + qdf_event_t recovery_event; + uint16_t max_station; + uint16_t max_bssid; + uint8_t myaddr[QDF_MAC_ADDR_SIZE]; + uint8_t hwaddr[QDF_MAC_ADDR_SIZE]; +#ifdef WLAN_FEATURE_LPSS + uint8_t lpss_support; +#endif + uint8_t ap_arpns_support; + bool wmi_ready; + uint32_t wlan_init_status; + qdf_device_t qdf_dev; + uint32_t wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; + uint32_t wmi_service_ext_bitmap[WMI_SERVICE_SEGMENT_BM_SIZE32]; + wma_tx_dwnld_comp_callback tx_frm_download_comp_cb; + qdf_event_t tx_frm_download_comp_event; + qdf_event_t tx_queue_empty_event; + wma_tx_ota_comp_callback umac_data_ota_ack_cb; + unsigned long last_umac_data_ota_timestamp; + qdf_nbuf_t last_umac_data_nbuf; + bool needShutdown; + wma_tgt_cfg_cb tgt_cfg_update_cb; + HAL_REG_CAPABILITIES reg_cap; + uint32_t scan_id; + struct wma_txrx_node *interfaces; + pdev_cli_config_t pdevconfig; + qdf_list_t wma_hold_req_queue; + qdf_spinlock_t wma_hold_req_q_lock; + uint32_t vht_supp_mcs; + uint8_t is_fw_assert; + struct wma_tx_ack_work_ctx *ack_work_ctx; + uint8_t powersave_mode; + void *pGetRssiReq; + bool get_one_peer_info; + struct qdf_mac_addr peer_macaddr; + t_thermal_mgmt thermal_mgmt_info; + bool enable_mc_list; + uint8_t ibss_started; + tSetBssKeyParams ibsskey_info; + txFailIndCallback hddTxFailCb; +#ifdef FEATURE_WLAN_EXTSCAN + qdf_wake_lock_t extscan_wake_lock; +#endif + qdf_wake_lock_t wow_wake_lock; + qdf_wake_lock_t wow_auth_req_wl; + qdf_wake_lock_t wow_assoc_req_wl; + qdf_wake_lock_t wow_deauth_rec_wl; + qdf_wake_lock_t wow_disassoc_rec_wl; + qdf_wake_lock_t wow_ap_assoc_lost_wl; + qdf_wake_lock_t wow_auto_shutdown_wl; + qdf_wake_lock_t roam_ho_wl; + qdf_wake_lock_t roam_preauth_wl; + int wow_nack; + qdf_atomic_t is_wow_bus_suspended; + bool suitable_ap_hb_failure; + uint32_t suitable_ap_hb_failure_rssi; + ibss_power_save_params wma_ibss_power_save_params; +#ifdef WLAN_FEATURE_LPSS + bool is_lpass_enabled; +#endif + uint8_t staMaxLIModDtim; + uint8_t staModDtim; + uint8_t staDynamicDtim; + uint32_t hw_bd_id; + uint32_t hw_bd_info[HW_BD_INFO_SIZE]; + uint32_t miracast_value; + qdf_mc_timer_t log_completion_timer; + uint32_t num_dbs_hw_modes; + struct dbs_hw_mode_info hw_mode; + uint32_t old_hw_mode_index; + uint32_t new_hw_mode_index; + wma_peer_authorized_fp peer_authorized_cb; + struct sir_ocb_config *ocb_config_req; + uint16_t self_gen_frm_pwr; + bool tx_chain_mask_cck; + qdf_mc_timer_t service_ready_ext_timer; + + QDF_STATUS (*csr_roam_synch_cb)(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason); + QDF_STATUS (*csr_roam_auth_event_handle_cb)(struct mac_context *mac, + uint8_t vdev_id, + struct qdf_mac_addr bssid); + QDF_STATUS (*pe_roam_synch_cb)(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason); + QDF_STATUS (*pe_disconnect_cb) (struct mac_context *mac, + uint8_t vdev_id, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len, + uint16_t reason_code); + QDF_STATUS (*csr_roam_pmkid_req_cb)(uint8_t vdev_id, + struct roam_pmkid_req_event *bss_list); + qdf_wake_lock_t wmi_cmd_rsp_wake_lock; + qdf_runtime_lock_t wmi_cmd_rsp_runtime_lock; + qdf_runtime_lock_t sap_prevent_runtime_pm_lock; + enum active_apf_mode active_uc_apf_mode; + enum active_apf_mode active_mc_bc_apf_mode; + struct wma_ini_config ini_config; + struct wma_valid_channels saved_chan; + bool nan_datapath_enabled; + bool fw_timeout_crash; + bool sub_20_support; + bool is_dfs_offloaded; + ol_txrx_pktdump_cb wma_mgmt_tx_packetdump_cb; + ol_txrx_pktdump_cb wma_mgmt_rx_packetdump_cb; + bool rcpi_enabled; + tSirLLStatsResults *link_stats_results; + uint64_t tx_fail_cnt; +#ifdef FEATURE_WLM_STATS + struct wma_wlm_stats_data wlm_data; +#endif +#ifdef WLAN_FEATURE_11AX + struct he_capability he_cap; +#endif + uint8_t bandcapability; + bool tx_bfee_8ss_enabled; + bool in_imps; + bool dynamic_nss_chains_support; + uint8_t ito_repeat_count; + qdf_mc_timer_t wma_fw_time_sync_timer; + bool fw_therm_throt_support; + bool enable_tx_compl_tsf64; +#ifdef WLAN_FEATURE_PKT_CAPTURE + bool is_pktcapture_enabled; +#endif +} t_wma_handle, *tp_wma_handle; + +/** + * wma_vdev_nss_chain_params_send() - send vdev nss chain params to fw. + * @vdev_id: vdev_id + * @user_cfg: pointer to the params structure + * + * This function sends nss chain params to the fw + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_FAILURE on error + */ +QDF_STATUS +wma_vdev_nss_chain_params_send(uint8_t vdev_id, + struct wlan_mlme_nss_chains *user_cfg); + +/** + * wma_send_regdomain_info_to_fw() - send regdomain info to fw + * @reg_dmn: reg domain + * @regdmn2G: 2G reg domain + * @regdmn5G: 5G reg domain + * @ctl2G: 2G test limit + * @ctl5G: 5G test limit + * + * Return: none + */ +void wma_send_regdomain_info_to_fw(uint32_t reg_dmn, uint16_t regdmn2G, + uint16_t regdmn5G, uint8_t ctl2G, + uint8_t ctl5G); +/** + * enum frame_index - Frame index + * @GENERIC_NODOWNLD_NOACK_COMP_INDEX: Frame index for no download comp no ack + * @GENERIC_DOWNLD_COMP_NOACK_COMP_INDEX: Frame index for download comp no ack + * @GENERIC_DOWNLD_COMP_ACK_COMP_INDEX: Frame index for download comp and ack + * @GENERIC_NODOWLOAD_ACK_COMP_INDEX: Frame index for no download comp and ack + * @FRAME_INDEX_MAX: maximum frame index + */ +enum frame_index { + GENERIC_NODOWNLD_NOACK_COMP_INDEX, + GENERIC_DOWNLD_COMP_NOACK_COMP_INDEX, + GENERIC_DOWNLD_COMP_ACK_COMP_INDEX, + GENERIC_NODOWLOAD_ACK_COMP_INDEX, + FRAME_INDEX_MAX +}; + +/** + * struct wma_tx_ack_work_ctx - tx ack work context + * @wma_handle: wma handle + * @sub_type: sub type + * @status: status + * @ack_cmp_work: work structure + */ +struct wma_tx_ack_work_ctx { + tp_wma_handle wma_handle; + uint16_t sub_type; + int32_t status; + qdf_work_t ack_cmp_work; +}; + +/** + * struct wma_target_req - target request parameters + * @event_timeout: event timeout + * @node: list + * @user_data: user data + * @msg_type: message type + * @vdev_id: vdev id + * @type: type + */ +struct wma_target_req { + qdf_mc_timer_t event_timeout; + qdf_list_node_t node; + void *user_data; + uint32_t msg_type; + uint8_t vdev_id; + uint8_t type; +}; + +/** + * struct wma_set_key_params - set key parameters + * @vdev_id: vdev id + * @def_key_idx: used to see if we have to read the key from cfg + * @key_len: key length + * @peer_mac: peer mac address + * @singl_tid_rc: 1=Single TID based Replay Count, 0=Per TID based RC + * @key_type: key type + * @key_idx: key index + * @unicast: unicast flag + * @key_data: key data + */ +struct wma_set_key_params { + uint8_t vdev_id; + /* def_key_idx can be used to see if we have to read the key from cfg */ + uint32_t def_key_idx; + uint16_t key_len; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + uint8_t singl_tid_rc; + enum eAniEdType key_type; + uint32_t key_idx; + bool unicast; + uint8_t key_data[SIR_MAC_MAX_KEY_LENGTH]; + uint8_t key_rsc[WLAN_CRYPTO_RSC_SIZE]; +}; + +/** + * struct t_thermal_cmd_params - thermal command parameters + * @minTemp: minimum temprature + * @maxTemp: maximum temprature + * @thermalEnable: thermal enable + */ +typedef struct { + uint16_t minTemp; + uint16_t maxTemp; + uint8_t thermalEnable; +} t_thermal_cmd_params, *tp_thermal_cmd_params; + +/** + * enum wma_cfg_cmd_id - wma cmd ids + * @WMA_VDEV_TXRX_FWSTATS_ENABLE_CMDID: txrx firmware stats enable command + * @WMA_VDEV_TXRX_FWSTATS_RESET_CMDID: txrx firmware stats reset command + * @WMA_VDEV_MCC_SET_TIME_LATENCY: set MCC latency time + * @WMA_VDEV_MCC_SET_TIME_QUOTA: set MCC time quota + * @WMA_VDEV_IBSS_SET_ATIM_WINDOW_SIZE: set IBSS ATIM window size + * @WMA_VDEV_IBSS_SET_POWER_SAVE_ALLOWED: set IBSS enable power save + * @WMA_VDEV_IBSS_SET_POWER_COLLAPSE_ALLOWED: set IBSS power collapse enable + * @WMA_VDEV_IBSS_SET_AWAKE_ON_TX_RX: awake IBSS on TX/RX + * @WMA_VDEV_IBSS_SET_INACTIVITY_TIME: set IBSS inactivity time + * @WMA_VDEV_IBSS_SET_TXSP_END_INACTIVITY_TIME: set IBSS TXSP + * @WMA_VDEV_IBSS_PS_SET_WARMUP_TIME_SECS: set IBSS power save warmup time + * @WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW: set IBSS power save ATIM + * @WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID: get IPA microcontroller fw stats + * @WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID: get IPA uC wifi-sharing stats + * @WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID: set IPA uC quota limit + * + * wma command ids for configuration request which + * does not involve sending a wmi command. + */ +enum wma_cfg_cmd_id { + WMA_VDEV_TXRX_FWSTATS_ENABLE_CMDID = WMI_CMDID_MAX, + WMA_VDEV_TXRX_FWSTATS_RESET_CMDID, + WMA_VDEV_MCC_SET_TIME_LATENCY, + WMA_VDEV_MCC_SET_TIME_QUOTA, + WMA_VDEV_IBSS_SET_ATIM_WINDOW_SIZE, + WMA_VDEV_IBSS_SET_POWER_SAVE_ALLOWED, + WMA_VDEV_IBSS_SET_POWER_COLLAPSE_ALLOWED, + WMA_VDEV_IBSS_SET_AWAKE_ON_TX_RX, + WMA_VDEV_IBSS_SET_INACTIVITY_TIME, + WMA_VDEV_IBSS_SET_TXSP_END_INACTIVITY_TIME, + WMA_VDEV_IBSS_PS_SET_WARMUP_TIME_SECS, + WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW, + WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID, + WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID, + WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID, + WMA_CMD_ID_MAX +}; + +/** + * struct wma_trigger_uapsd_params - trigger uapsd parameters + * @wmm_ac: wmm access category + * @user_priority: user priority + * @service_interval: service interval + * @suspend_interval: suspend interval + * @delay_interval: delay interval + */ +typedef struct wma_trigger_uapsd_params { + uint32_t wmm_ac; + uint32_t user_priority; + uint32_t service_interval; + uint32_t suspend_interval; + uint32_t delay_interval; +} t_wma_trigger_uapsd_params, *tp_wma_trigger_uapsd_params; + +/** + * enum uapsd_peer_param_max_sp - U-APSD maximum service period of peer station + * @UAPSD_MAX_SP_LEN_UNLIMITED: unlimited max service period + * @UAPSD_MAX_SP_LEN_2: max service period = 2 + * @UAPSD_MAX_SP_LEN_4: max service period = 4 + * @UAPSD_MAX_SP_LEN_6: max service period = 6 + */ +enum uapsd_peer_param_max_sp { + UAPSD_MAX_SP_LEN_UNLIMITED = 0, + UAPSD_MAX_SP_LEN_2 = 2, + UAPSD_MAX_SP_LEN_4 = 4, + UAPSD_MAX_SP_LEN_6 = 6 +}; + +/** + * enum uapsd_peer_param_enabled_ac - U-APSD Enabled AC's of peer station + * @UAPSD_VO_ENABLED: enable uapsd for voice + * @UAPSD_VI_ENABLED: enable uapsd for video + * @UAPSD_BK_ENABLED: enable uapsd for background + * @UAPSD_BE_ENABLED: enable uapsd for best effort + */ +enum uapsd_peer_param_enabled_ac { + UAPSD_VO_ENABLED = 0x01, + UAPSD_VI_ENABLED = 0x02, + UAPSD_BK_ENABLED = 0x04, + UAPSD_BE_ENABLED = 0x08 +}; + +/** + * enum profile_id_t - Firmware profiling index + * @PROF_CPU_IDLE: cpu idle profile + * @PROF_PPDU_PROC: ppdu processing profile + * @PROF_PPDU_POST: ppdu post profile + * @PROF_HTT_TX_INPUT: htt tx input profile + * @PROF_MSDU_ENQ: msdu enqueue profile + * @PROF_PPDU_POST_HAL: ppdu post profile + * @PROF_COMPUTE_TX_TIME: tx time profile + * @PROF_MAX_ID: max profile index + */ +enum profile_id_t { + PROF_CPU_IDLE, + PROF_PPDU_PROC, + PROF_PPDU_POST, + PROF_HTT_TX_INPUT, + PROF_MSDU_ENQ, + PROF_PPDU_POST_HAL, + PROF_COMPUTE_TX_TIME, + PROF_MAX_ID, +}; + +/** + * struct p2p_ie - P2P IE structural definition. + * @p2p_id: p2p id + * @p2p_len: p2p length + * @p2p_oui: p2p OUI + * @p2p_oui_type: p2p OUI type + */ +struct p2p_ie { + uint8_t p2p_id; + uint8_t p2p_len; + uint8_t p2p_oui[3]; + uint8_t p2p_oui_type; +} __packed; + +/** + * struct p2p_noa_descriptor - noa descriptor + * @type_count: 255: continuous schedule, 0: reserved + * @duration: Absent period duration in micro seconds + * @interval: Absent period interval in micro seconds + * @start_time: 32 bit tsf time when in starts + */ +struct p2p_noa_descriptor { + uint8_t type_count; + uint32_t duration; + uint32_t interval; + uint32_t start_time; +} __packed; + +/** + * struct p2p_sub_element_noa - p2p noa element + * @p2p_sub_id: p2p sub id + * @p2p_sub_len: p2p sub length + * @index: identifies instance of NOA su element + * @oppPS: oppPS state of the AP + * @ctwindow: ctwindow in TUs + * @num_descriptors: number of NOA descriptors + * @noa_descriptors: noa descriptors + */ +struct p2p_sub_element_noa { + uint8_t p2p_sub_id; + uint8_t p2p_sub_len; + uint8_t index; /* identifies instance of NOA su element */ + uint8_t oppPS:1, /* oppPS state of the AP */ + ctwindow:7; /* ctwindow in TUs */ + uint8_t num_descriptors; /* number of NOA descriptors */ + struct p2p_noa_descriptor noa_descriptors[WMA_MAX_NOA_DESCRIPTORS]; +}; + +/** + * struct wma_decap_info_t - decapsulation info + * @hdr: header + * @hdr_len: header length + */ +struct wma_decap_info_t { + uint8_t hdr[sizeof(struct ieee80211_qosframe_addr4)]; + int32_t hdr_len; +}; + +/** + * enum packet_power_save - packet power save params + * @WMI_VDEV_PPS_PAID_MATCH: paid match param + * @WMI_VDEV_PPS_GID_MATCH: gid match param + * @WMI_VDEV_PPS_EARLY_TIM_CLEAR: early tim clear param + * @WMI_VDEV_PPS_EARLY_DTIM_CLEAR: early dtim clear param + * @WMI_VDEV_PPS_EOF_PAD_DELIM: eof pad delim param + * @WMI_VDEV_PPS_MACADDR_MISMATCH: macaddr mismatch param + * @WMI_VDEV_PPS_DELIM_CRC_FAIL: delim CRC fail param + * @WMI_VDEV_PPS_GID_NSTS_ZERO: gid nsts zero param + * @WMI_VDEV_PPS_RSSI_CHECK: RSSI check param + * @WMI_VDEV_PPS_5G_EBT: 5G ebt param + */ +typedef enum { + WMI_VDEV_PPS_PAID_MATCH = 0, + WMI_VDEV_PPS_GID_MATCH = 1, + WMI_VDEV_PPS_EARLY_TIM_CLEAR = 2, + WMI_VDEV_PPS_EARLY_DTIM_CLEAR = 3, + WMI_VDEV_PPS_EOF_PAD_DELIM = 4, + WMI_VDEV_PPS_MACADDR_MISMATCH = 5, + WMI_VDEV_PPS_DELIM_CRC_FAIL = 6, + WMI_VDEV_PPS_GID_NSTS_ZERO = 7, + WMI_VDEV_PPS_RSSI_CHECK = 8, + WMI_VDEV_VHT_SET_GID_MGMT = 9, + WMI_VDEV_PPS_5G_EBT = 10 +} packet_power_save; + +/** + * enum green_tx_param - green tx parameters + * @WMI_VDEV_PARAM_GTX_HT_MCS: ht mcs param + * @WMI_VDEV_PARAM_GTX_VHT_MCS: vht mcs param + * @WMI_VDEV_PARAM_GTX_USR_CFG: user cfg param + * @WMI_VDEV_PARAM_GTX_THRE: thre param + * @WMI_VDEV_PARAM_GTX_MARGIN: green tx margin param + * @WMI_VDEV_PARAM_GTX_STEP: green tx step param + * @WMI_VDEV_PARAM_GTX_MINTPC: mintpc param + * @WMI_VDEV_PARAM_GTX_BW_MASK: bandwidth mask + */ +typedef enum { + WMI_VDEV_PARAM_GTX_HT_MCS, + WMI_VDEV_PARAM_GTX_VHT_MCS, + WMI_VDEV_PARAM_GTX_USR_CFG, + WMI_VDEV_PARAM_GTX_THRE, + WMI_VDEV_PARAM_GTX_MARGIN, + WMI_VDEV_PARAM_GTX_STEP, + WMI_VDEV_PARAM_GTX_MINTPC, + WMI_VDEV_PARAM_GTX_BW_MASK, +} green_tx_param; + +/** + * enum uapsd_ac - U-APSD Access Categories + * @UAPSD_BE: best effort + * @UAPSD_BK: back ground + * @UAPSD_VI: video + * @UAPSD_VO: voice + */ +enum uapsd_ac { + UAPSD_BE, + UAPSD_BK, + UAPSD_VI, + UAPSD_VO +}; + +/** + * wma_disable_uapsd_per_ac() - disable uapsd per ac + * @wmi_handle: wma handle + * @vdev_id: vdev id + * @ac: access category + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_disable_uapsd_per_ac(tp_wma_handle wma_handle, + uint32_t vdev_id, enum uapsd_ac ac); + +/** + * enum uapsd_up - U-APSD User Priorities + * @UAPSD_UP_BE: best effort + * @UAPSD_UP_BK: back ground + * @UAPSD_UP_RESV: reserve + * @UAPSD_UP_EE: Excellent Effort + * @UAPSD_UP_CL: Critical Applications + * @UAPSD_UP_VI: video + * @UAPSD_UP_VO: voice + * @UAPSD_UP_NC: Network Control + */ +enum uapsd_up { + UAPSD_UP_BE, + UAPSD_UP_BK, + UAPSD_UP_RESV, + UAPSD_UP_EE, + UAPSD_UP_CL, + UAPSD_UP_VI, + UAPSD_UP_VO, + UAPSD_UP_NC, + UAPSD_UP_MAX +}; + +/** + * struct wma_roam_invoke_cmd - roam invoke command + * @vdev_id: vdev id + * @bssid: mac address + * @ch_freq: channel frequency + * @frame_len: frame length, includs mac header, fixed params and ies + * @frame_buf: buffer contaning probe response or beacon + * @is_same_bssid: flag to indicate if roaming is requested for same bssid + * @forced_roaming: Roaming to be done without giving bssid, and channel. + */ +struct wma_roam_invoke_cmd { + uint32_t vdev_id; + uint8_t bssid[QDF_MAC_ADDR_SIZE]; + uint32_t ch_freq; + uint32_t frame_len; + uint8_t *frame_buf; + uint8_t is_same_bssid; + bool forced_roaming; +}; + +A_UINT32 e_csr_auth_type_to_rsn_authmode(enum csr_akm_type authtype, + eCsrEncryptionType encr); +A_UINT32 e_csr_encryption_type_to_rsn_cipherset(eCsrEncryptionType encr); + +/** + * wma_trigger_uapsd_params() - set trigger uapsd parameter + * @wmi_handle: wma handle + * @vdev_id: vdev id + * @trigger_uapsd_params: trigger uapsd parameters + * + * This function sets the trigger uapsd + * params such as service interval, delay + * interval and suspend interval which + * will be used by the firmware to send + * trigger frames periodically when there + * is no traffic on the transmit side. + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_trigger_uapsd_params(tp_wma_handle wma_handle, uint32_t vdev_id, + tp_wma_trigger_uapsd_params + trigger_uapsd_params); + +void wma_send_flush_logs_to_fw(tp_wma_handle wma_handle); +void wma_log_completion_timeout(void *data); + +#ifdef FEATURE_RSSI_MONITOR +/** + * wma_set_rssi_monitoring() - set rssi monitoring + * @handle: WMA handle + * @req: rssi monitoring request structure + * + * This function takes the incoming @req and sends it down to the + * firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_set_rssi_monitoring(tp_wma_handle wma, + struct rssi_monitor_param *req); +#else /* FEATURE_RSSI_MONITOR */ +static inline +QDF_STATUS wma_set_rssi_monitoring(tp_wma_handle wma, + struct rssi_monitor_param *req) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_RSSI_MONITOR */ + +QDF_STATUS wma_send_pdev_set_pcl_cmd(tp_wma_handle wma_handle, + struct set_pcl_req *msg); + +QDF_STATUS wma_send_pdev_set_hw_mode_cmd(tp_wma_handle wma_handle, + struct policy_mgr_hw_mode *msg); + +QDF_STATUS wma_send_pdev_set_dual_mac_config(tp_wma_handle wma_handle, + struct policy_mgr_dual_mac_config *msg); +QDF_STATUS wma_send_pdev_set_antenna_mode(tp_wma_handle wma_handle, + struct sir_antenna_mode_param *msg); + +struct wma_target_req *wma_fill_hold_req(tp_wma_handle wma, + uint8_t vdev_id, uint32_t msg_type, + uint8_t type, void *params, + uint32_t timeout); + +int wma_mgmt_tx_completion_handler(void *handle, uint8_t *cmpl_event_params, + uint32_t len); +int wma_mgmt_tx_bundle_completion_handler(void *handle, + uint8_t *cmpl_event_params, uint32_t len); +uint32_t wma_get_vht_ch_width(void); +QDF_STATUS +wma_config_debug_module_cmd(wmi_unified_t wmi_handle, A_UINT32 param, + A_UINT32 val, A_UINT32 *module_id_bitmap, + A_UINT32 bitmap_len); + +#ifdef FEATURE_LFR_SUBNET_DETECTION +/** + * wma_set_gateway_params() - set gateway parameters + * @wma: WMA handle + * @req: gateway update request parameter structure + * + * This function takes the incoming @req and sends it down to the + * firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_set_gateway_params(tp_wma_handle wma, + struct gateway_update_req_param *req); +#else +static inline +QDF_STATUS wma_set_gateway_params(tp_wma_handle wma, + struct gateway_update_req_param *req) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_LFR_SUBNET_DETECTION */ + +QDF_STATUS wma_lro_config_cmd(void *handle, + struct cdp_lro_hash_config *wma_lro_cmd); + +QDF_STATUS wma_ht40_stop_obss_scan(tp_wma_handle wma_handle, + int32_t vdev_id); + +QDF_STATUS wma_process_fw_test_cmd(WMA_HANDLE handle, + struct set_fwtest_params *wma_fwtest); + +QDF_STATUS wma_send_ht40_obss_scanind(tp_wma_handle wma, + struct obss_ht40_scanind *req); + +uint32_t wma_get_num_of_setbits_from_bitmask(uint32_t mask); + +#ifdef FEATURE_WLAN_APF +/** + * wma_get_apf_caps_event_handler() - Event handler for get apf capability + * @handle: WMA global handle + * @cmd_param_info: command event data + * @len: Length of @cmd_param_info + * + * Return: 0 on Success or Errno on failure + */ +int wma_get_apf_caps_event_handler(void *handle, + u_int8_t *cmd_param_info, + u_int32_t len); + +/** + * wma_get_apf_capabilities - Send get apf capability to firmware + * @wma_handle: wma handle + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS wma_get_apf_capabilities(tp_wma_handle wma); + +/** + * wma_set_apf_instructions - Set apf instructions to firmware + * @wma: wma handle + * @apf_set_offload: APF offload information to set to firmware + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS +wma_set_apf_instructions(tp_wma_handle wma, + struct sir_apf_set_offload *apf_set_offload); + +/** + * wma_send_apf_enable_cmd - Send apf enable/disable cmd + * @wma_handle: wma handle + * @vdev_id: vdev id + * @apf_enable: true: Enable APF Int., false: Disable APF Int. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS wma_send_apf_enable_cmd(WMA_HANDLE handle, uint8_t vdev_id, + bool apf_enable); + +/** + * wma_send_apf_write_work_memory_cmd - Command to write into the apf work + * memory + * @wma_handle: wma handle + * @write_params: APF parameters for the write operation + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS +wma_send_apf_write_work_memory_cmd(WMA_HANDLE handle, + struct wmi_apf_write_memory_params + *write_params); + +/** + * wma_send_apf_read_work_memory_cmd - Command to get part of apf work memory + * @wma_handle: wma handle + * @callback: HDD callback to receive apf get mem event + * @context: Context for the HDD callback + * @read_params: APF parameters for the get operation + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS +wma_send_apf_read_work_memory_cmd(WMA_HANDLE handle, + struct wmi_apf_read_memory_params + *read_params); + +/** + * wma_apf_read_work_memory_event_handler - Event handler for get apf mem + * operation + * @handle: wma handle + * @evt_buf: Buffer pointer to the event + * @len: Length of the event buffer + * + * Return: status. + */ +int wma_apf_read_work_memory_event_handler(void *handle, uint8_t *evt_buf, + uint32_t len); +#else /* FEATURE_WLAN_APF */ +static inline QDF_STATUS wma_get_apf_capabilities(tp_wma_handle wma) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +wma_set_apf_instructions(tp_wma_handle wma, + struct sir_apf_set_offload *apf_set_offload) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_WLAN_APF */ + +void wma_process_set_pdev_ie_req(tp_wma_handle wma, + struct set_ie_param *ie_params); +void wma_process_set_pdev_ht_ie_req(tp_wma_handle wma, + struct set_ie_param *ie_params); +void wma_process_set_pdev_vht_ie_req(tp_wma_handle wma, + struct set_ie_param *ie_params); + +QDF_STATUS wma_remove_peer(tp_wma_handle wma, uint8_t *mac_addr, + uint8_t vdev_id, bool no_fw_peer_delete); + +QDF_STATUS wma_create_peer(tp_wma_handle wma, uint8_t peer_addr[6], + u_int32_t peer_type, u_int8_t vdev_id, + bool roam_synch_in_progress); + +QDF_STATUS wma_peer_unmap_conf_cb(uint8_t vdev_id, + uint32_t peer_id_cnt, + uint16_t *peer_id_list); + +bool wma_objmgr_peer_exist(tp_wma_handle wma, + uint8_t *peer_addr, uint8_t *peer_vdev_id); + +/** + * wma_get_cca_stats() - send request to fw to get CCA + * @wmi_hdl: wma handle + * @vdev_id: vdev id + * + * Return: QDF status + */ +QDF_STATUS wma_get_cca_stats(tp_wma_handle wma_handle, + uint8_t vdev_id); + +struct wma_ini_config *wma_get_ini_handle(tp_wma_handle wma_handle); + +/** + * wma_chan_phy__mode() - get host phymode for channel + * @freq: channel freq + * @chan_width: maximum channel width possible + * @dot11_mode: maximum phy_mode possible + * + * Return: return host phymode + */ +enum wlan_phymode wma_chan_phy_mode(uint32_t freq, enum phy_ch_width chan_width, + uint8_t dot11_mode); + +/** + * wma_host_to_fw_phymode() - convert host to fw phymode + * @host_phymode: phymode to convert + * + * Return: one of the values defined in enum WMI_HOST_WLAN_PHY_MODE; + * or WMI_HOST_MODE_UNKNOWN if the conversion fails + */ +WMI_HOST_WLAN_PHY_MODE wma_host_to_fw_phymode(enum wlan_phymode host_phymode); + +/** + * wma_fw_to_host_phymode() - convert fw to host phymode + * @phymode: phymode to convert + * + * Return: one of the values defined in enum wlan_phymode; + * or WLAN_PHYMODE_AUTO if the conversion fails + */ +enum wlan_phymode wma_fw_to_host_phymode(WMI_HOST_WLAN_PHY_MODE phymode); + + +#ifdef FEATURE_OEM_DATA_SUPPORT +/** + * wma_start_oem_req_cmd() - send oem request command to fw + * @wma_handle: wma handle + * @oem_data_req: the pointer of oem req buf + * + * Return: QDF status + */ +QDF_STATUS wma_start_oem_req_cmd(tp_wma_handle wma_handle, + struct oem_data_req *oem_data_req); +#endif + +#ifdef FEATURE_OEM_DATA +/** + * wma_start_oem_data_cmd() - send oem data command to fw + * @wma_handle: wma handle + * @oem_data: the pointer of oem data buf + * + * Return: QDF status + */ +QDF_STATUS wma_start_oem_data_cmd(tp_wma_handle wma_handle, + struct oem_data *oem_data); +#endif + +QDF_STATUS wma_enable_disable_caevent_ind(tp_wma_handle wma_handle, + uint8_t val); +void wma_register_packetdump_callback( + ol_txrx_pktdump_cb wma_mgmt_tx_packetdump_cb, + ol_txrx_pktdump_cb wma_mgmt_rx_packetdump_cb); +void wma_deregister_packetdump_callback(void); +void wma_update_sta_inactivity_timeout(tp_wma_handle wma, + struct sme_sta_inactivity_timeout *sta_inactivity_timer); + +/** + * wma_form_rx_packet() - form rx cds packet + * @buf: buffer + * @mgmt_rx_params: mgmt rx params + * @rx_pkt: cds packet + * + * This functions forms a cds packet from the rx mgmt frame received. + * + * Return: 0 for success or error code + */ +int wma_form_rx_packet(qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + cds_pkt_t *rx_pkt); + +/** + * wma_mgmt_unified_cmd_send() - send the mgmt tx packet + * @vdev: objmgr vdev + * @buf: buffer + * @desc_id: desc id + * @mgmt_tx_params: mgmt rx params + * + * This functions sends mgmt tx packet to WMI layer. + * + * Return: 0 for success or error code + */ +QDF_STATUS wma_mgmt_unified_cmd_send(struct wlan_objmgr_vdev *vdev, + qdf_nbuf_t buf, uint32_t desc_id, + void *mgmt_tx_params); + +/** + * wma_mgmt_nbuf_unmap_cb() - dma unmap for pending mgmt pkts + * @pdev: objmgr pdev + * @buf: buffer + * + * This function does the dma unmap of the pending mgmt packet cleanup + * + * Return: None + */ +#ifndef CONFIG_HL_SUPPORT +void wma_mgmt_nbuf_unmap_cb(struct wlan_objmgr_pdev *pdev, + qdf_nbuf_t buf); +#else +static inline void wma_mgmt_nbuf_unmap_cb(struct wlan_objmgr_pdev *pdev, + qdf_nbuf_t buf) +{} +#endif + +/** + * wma_chan_info_event_handler() - chan info event handler + * @handle: wma handle + * @event_buf: event handler data + * @len: length of @event_buf + * + * this function will handle the WMI_CHAN_INFO_EVENTID + * + * Return: int + */ +int wma_chan_info_event_handler(void *handle, uint8_t *event_buf, + uint32_t len); + +/** + * wma_update_vdev_pause_bitmap() - update vdev pause bitmap + * @vdev_id: the Id of the vdev to configure + * @value: value pause bitmap value + * + * Return: None + */ +static inline +void wma_vdev_update_pause_bitmap(uint8_t vdev_id, uint16_t value) +{ + tp_wma_handle wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *iface; + + if (!wma) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return; + } + + if (vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: Invalid vdev_id: %d", __func__, vdev_id); + return; + } + + iface = &wma->interfaces[vdev_id]; + + if (!iface) { + WMA_LOGE("%s: Interface is NULL", __func__); + return; + } + + iface->pause_bitmap = value; +} + +/** + * wma_vdev_get_pause_bitmap() - Get vdev pause bitmap + * @vdev_id: the Id of the vdev to configure + * + * Return: Vdev pause bitmap value else 0 on error + */ +static inline +uint16_t wma_vdev_get_pause_bitmap(uint8_t vdev_id) +{ + tp_wma_handle wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *iface; + + if (!wma) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return 0; + } + + iface = &wma->interfaces[vdev_id]; + + if (!iface) { + WMA_LOGE("%s: Interface is NULL", __func__); + return 0; + } + + return iface->pause_bitmap; +} + +/** + * wma_vdev_is_device_in_low_pwr_mode - is device in power save mode + * @vdev_id: the Id of the vdev to configure + * + * Return: true if device is in low power mode else false + */ +static inline bool wma_vdev_is_device_in_low_pwr_mode(uint8_t vdev_id) +{ + tp_wma_handle wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *iface; + + if (!wma) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return 0; + } + + iface = &wma->interfaces[vdev_id]; + + if (!iface) { + WMA_LOGE("%s: Interface is NULL", __func__); + return 0; + } + + return iface->in_bmps || wma->in_imps; +} + +/** + * wma_vdev_get_dtim_period - Get dtim period value from mlme + * @vdev_id: vdev index number + * @value: pointer to the value to fill out + * + * Note caller must verify return status before using value + * + * Return: QDF_STATUS_SUCCESS when fetched a valid value from cfg else + * QDF_STATUS_E_FAILURE + */ +static inline +QDF_STATUS wma_vdev_get_dtim_period(uint8_t vdev_id, uint8_t *value) +{ + tp_wma_handle wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *iface; + /* set value to zero */ + *value = 0; + + if (!wma) + return QDF_STATUS_E_FAILURE; + + iface = &wma->interfaces[vdev_id]; + + if (!iface) + return QDF_STATUS_E_FAILURE; + + *value = iface->dtimPeriod; + return QDF_STATUS_SUCCESS; +} + +/** + * wma_vdev_get_beacon_interval - Get beacon interval from mlme + * @vdev_id: vdev index number + * @value: pointer to the value to fill out + * + * Note caller must verify return status before using value + * + * Return: QDF_STATUS_SUCCESS when fetched a valid value from cfg else + * QDF_STATUS_E_FAILURE + */ +static inline +QDF_STATUS wma_vdev_get_beacon_interval(uint8_t vdev_id, uint16_t *value) +{ + tp_wma_handle wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *iface; + /* set value to zero */ + *value = 0; + + if (!wma) + return QDF_STATUS_E_FAILURE; + + iface = &wma->interfaces[vdev_id]; + + if (!iface) + return QDF_STATUS_E_FAILURE; + + *value = iface->beaconInterval; + return QDF_STATUS_SUCCESS; +} + +/** + * wma_get_vdev_rate_flag - Get beacon rate flag from mlme + * @vdev_id: vdev index number + * @rate_flag: pointer to the value to fill out + * + * Note caller must verify return status before using value + * + * Return: QDF_STATUS_SUCCESS when fetched a valid value from mlme else + * QDF_STATUS_E_FAILURE + */ +static inline QDF_STATUS +wma_get_vdev_rate_flag(struct wlan_objmgr_vdev *vdev, uint32_t *rate_flag) +{ + struct vdev_mlme_obj *mlme_obj; + + if (!vdev) { + WMA_LOGE("%s vdev is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + WMA_LOGE("%s Failed to get mlme_obj", __func__); + return QDF_STATUS_E_FAILURE; + } + + *rate_flag = mlme_obj->mgmt.rate_info.rate_flags; + return QDF_STATUS_SUCCESS; +} + +/** + * wma_vdev_set_pause_bit() - Set a bit in vdev pause bitmap + * @vdev_id: the Id of the vdev to configure + * @bit_pos: set bit position in pause bitmap + * + * Return: None + */ +static inline +void wma_vdev_set_pause_bit(uint8_t vdev_id, wmi_tx_pause_type bit_pos) +{ + tp_wma_handle wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *iface; + + if (!wma) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return; + } + + iface = &wma->interfaces[vdev_id]; + + if (!iface) { + WMA_LOGE("%s: Interface is NULL", __func__); + return; + } + + iface->pause_bitmap |= (1 << bit_pos); +} + +/** + * wma_vdev_clear_pause_bit() - Clear a bit from vdev pause bitmap + * @vdev_id: the Id of the vdev to configure + * @bit_pos: set bit position in pause bitmap + * + * Return: None + */ +static inline +void wma_vdev_clear_pause_bit(uint8_t vdev_id, wmi_tx_pause_type bit_pos) +{ + tp_wma_handle wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *iface; + + if (!wma) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return; + } + + iface = &wma->interfaces[vdev_id]; + + if (!iface) { + WMA_LOGE("%s: Interface is NULL", __func__); + return; + } + + iface->pause_bitmap &= ~(1 << bit_pos); +} + +/** + * wma_process_roaming_config() - process roam request + * @wma_handle: wma handle + * @roam_req: roam request parameters + * + * Main routine to handle ROAM commands coming from CSR module. + * + * Return: QDF status + */ +QDF_STATUS wma_process_roaming_config(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * wma_send_roam_preauth_status() - Send the preauth status to wmi + * @handle: WMA handle + * @roam_req: Pointer to wmi_roam_auth_status_params from sae + * + * Return: None + */ +void +wma_send_roam_preauth_status(tp_wma_handle wma_handle, + struct wmi_roam_auth_status_params *params); +#else +static inline void +wma_send_roam_preauth_status(tp_wma_handle wma_handle, + struct wmi_roam_auth_status_params *params) +{} +#endif + +/** + * wma_handle_roam_sync_timeout() - Update roaming status at wma layer + * @wma_handle: wma handle + * @info: Info for roaming start timer + * + * This function gets called in case of roaming offload timer get expired + * + * Return: None + */ +void wma_handle_roam_sync_timeout(tp_wma_handle wma_handle, + struct roam_sync_timeout_timer_info *info); + +#ifdef WMI_INTERFACE_EVENT_LOGGING +static inline void wma_print_wmi_cmd_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (wma) { + print(print_priv, "Command Log (count %u)", count); + wmi_print_cmd_log(wma->wmi_handle, count, print, print_priv); + } +} + +static inline void wma_print_wmi_cmd_tx_cmp_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (wma) { + print(print_priv, "Command Tx Complete Log (count %u)", count); + wmi_print_cmd_tx_cmp_log(wma->wmi_handle, count, print, + print_priv); + } +} + +static inline void wma_print_wmi_mgmt_cmd_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (wma) { + print(print_priv, "Management Command Log (count %u)", count); + wmi_print_mgmt_cmd_log(wma->wmi_handle, count, print, + print_priv); + } +} + +static inline void wma_print_wmi_mgmt_cmd_tx_cmp_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (wma) { + print(print_priv, + "Management Command Tx Complete Log (count %u)", count); + wmi_print_mgmt_cmd_tx_cmp_log(wma->wmi_handle, count, print, + print_priv); + } +} + +static inline void wma_print_wmi_event_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (wma) { + print(print_priv, "Event Log (count %u)", count); + wmi_print_event_log(wma->wmi_handle, count, print, print_priv); + } +} + +static inline void wma_print_wmi_rx_event_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (wma) { + print(print_priv, "Rx Event Log (count %u)", count); + wmi_print_rx_event_log(wma->wmi_handle, count, print, + print_priv); + } +} + +static inline void wma_print_wmi_mgmt_event_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (wma) { + print(print_priv, "Management Event Log (count %u)", count); + wmi_print_mgmt_event_log(wma->wmi_handle, count, print, + print_priv); + } +} +#else + +static inline void wma_print_wmi_cmd_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ +} + +static inline void wma_print_wmi_cmd_tx_cmp_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ +} + +static inline void wma_print_wmi_mgmt_cmd_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ +} + +static inline void wma_print_wmi_mgmt_cmd_tx_cmp_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ +} + +static inline void wma_print_wmi_event_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ +} + +static inline void wma_print_wmi_rx_event_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ +} + +static inline void wma_print_wmi_mgmt_event_log(uint32_t count, + qdf_abstract_print *print, + void *print_priv) +{ +} +#endif /* WMI_INTERFACE_EVENT_LOGGING */ + +/** + * wma_set_rx_reorder_timeout_val() - set rx recorder timeout value + * @wma_handle: pointer to wma handle + * @reorder_timeout: rx reorder timeout value + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_set_rx_reorder_timeout_val(tp_wma_handle wma_handle, + struct sir_set_rx_reorder_timeout_val *reorder_timeout); + +/** + * wma_set_rx_blocksize() - set rx blocksize + * @wma_handle: pointer to wma handle + * @peer_rx_blocksize: rx blocksize for peer mac + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_set_rx_blocksize(tp_wma_handle wma_handle, + struct sir_peer_set_rx_blocksize *peer_rx_blocksize); +/** + * wma_configure_smps_params() - Configures the smps parameters to set + * @vdev_id: Virtual device for the command + * @param_id: SMPS parameter ID + * @param_val: Value to be set for the parameter + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS wma_configure_smps_params(uint32_t vdev_id, uint32_t param_id, + uint32_t param_val); + +/* + * wma_chip_power_save_failure_detected_handler() - chip pwr save fail detected + * event handler + * @handle: wma handle + * @cmd_param_info: event handler data + * @len: length of @cmd_param_info + * + * Return: QDF_STATUS_SUCCESS on success; error code otherwise + */ +int wma_chip_power_save_failure_detected_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +/** + * wma_get_chain_rssi() - send wmi cmd to get chain rssi + * @wma_handle: wma handler + * @req_params: requset params + * + * Return: Return QDF_STATUS + */ +QDF_STATUS wma_get_chain_rssi(tp_wma_handle wma_handle, + struct get_chain_rssi_req_params *req_params); + +/** + * wma_config_bmiss_bcnt_params() - set bmiss config parameters + * @vdev_id: virtual device for the command + * @first_cnt: bmiss first value + * @final_cnt: bmiss final value + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS wma_config_bmiss_bcnt_params(uint32_t vdev_id, uint32_t first_cnt, + uint32_t final_cnt); + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD +/** + * wma_check_and_set_wake_timer(): checks all interfaces and if any interface + * has install_key pending, sets timer pattern in fw to wake up host after + * specified time has elapsed. + * @time: time after which host wants to be awaken. + * + * Return: None + */ +void wma_check_and_set_wake_timer(uint32_t time); +#endif + +/** + * wma_delete_invalid_peer_entries() - Delete invalid peer entries stored + * @vdev_id: virtual interface id + * @peer_mac_addr: Peer MAC address + * + * Removes the invalid peer mac entry from wma node + */ +void wma_delete_invalid_peer_entries(uint8_t vdev_id, uint8_t *peer_mac_addr); + +/** + * wma_rx_invalid_peer_ind() - the callback for DP to notify WMA layer + * invalid peer data is received, this function will send message to + * lim module. + * @vdev_id: virtual device ID + * @wh: Pointer to 802.11 frame header + * + * Return: 0 for success or non-zero on failure + */ +uint8_t wma_rx_invalid_peer_ind(uint8_t vdev_id, void *wh); + +/** + * wma_dp_send_delba_ind() - the callback for DP to notify WMA layer + * to del ba of rx + * @vdev_id: vdev id + * @peer_macaddr: peer mac address + * @tid: tid of rx + * @reason_code: reason code + * + * Return: 0 for success or non-zero on failure + */ +int wma_dp_send_delba_ind(uint8_t vdev_id, + uint8_t *peer_macaddr, + uint8_t tid, + uint8_t reason_code); + +/** + * is_roam_inprogress() - Is vdev in progress + * @vdev_id: vdev of interest + * + * Return: true if roaming, false otherwise + */ +bool wma_is_roam_in_progress(uint32_t vdev_id); + +/** + * wma_get_psoc_from_scn_handle() - API to get psoc from scn handle + * @scn_handle: opaque wma handle + * + * API to get psoc from scn handle + * + * Return: psoc context or null in case of failure + */ +struct wlan_objmgr_psoc *wma_get_psoc_from_scn_handle(void *scn_handle); + +/** + * wma_set_peer_ucast_cipher() - Update unicast cipher fof the peer + * @mac_addr: peer mac address + * @cipher: peer cipher type + * + * Return: None + */ +void wma_set_peer_ucast_cipher(uint8_t *mac_addr, + enum wlan_crypto_cipher_type cipher); + +/** + * wma_update_set_key() - Update WMA layer for set key + * @session_id: vdev session identifier + * @pairwise: denotes if it is pairwise or group key + * @key_index: Key Index + * @cipher_type: cipher type being used for the encryption/decryption + * + * Return: None + */ +void wma_update_set_key(uint8_t session_id, bool pairwise, + uint8_t key_index, + enum wlan_crypto_cipher_type cipher_type); + +/** + * wma_get_igtk() - Get the IGTK that was stored in the session earlier + * @iface: Interface for which the key is being requested + * @key_len: key length + * @igtk_key_idx: igtk key idx + * + * Return: Pointer to the key + */ +uint8_t *wma_get_igtk(struct wma_txrx_node *iface, uint16_t *key_len, + uint16_t igtk_key_idx); + +#ifdef WLAN_FEATURE_MOTION_DETECTION +/** + * wma_motion_det_host_event_handler - motion detection event handler + * @handle: WMA global handle + * @event: motion detection event + * @len: Length of cmd + * + * Call motion detection event callback handler + * + * Return: 0 on success, else error on failure + */ + +int wma_motion_det_host_event_handler(void *handle, u_int8_t *event, + u_int32_t len); + +/** + * wma_motion_det_base_line_host_event_handler - md baselining event handler + * @handle: WMA global handle + * @event: motion detection baselining event + * @len: Length of cmd + * + * Return: 0 on success, else error on failure + */ +int wma_motion_det_base_line_host_event_handler(void *handle, u_int8_t *event, + u_int32_t len); +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +/** + * wma_add_bss_peer_sta() - creat bss peer when sta connect + * @self_mac: self mac address + * @bssid: AP bssid + * @roam_sync: if roam sync is in progress + * + * Return: 0 on success, else error on failure + */ +QDF_STATUS wma_add_bss_peer_sta(uint8_t *self_mac, uint8_t *bssid, + bool roam_sync); + +/** + * wma_send_vdev_stop() - WMA api to send vdev stop to fw + * @vdev_id: vdev id + * + * Return: 0 on success, else error on failure + */ +QDF_STATUS wma_send_vdev_stop(uint8_t vdev_id); + +/** + * wma_pre_assoc_req() - wma pre assoc req when sta connect + * @add_bss: add bss param + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_pre_assoc_req(struct bss_params *add_bss); + +/** + * wma_add_bss_lfr3() - add bss during LFR3 offload roaming + * @wma: wma handler + * @add_bss: add bss param + * + * Return: None + */ +void wma_add_bss_lfr3(tp_wma_handle wma, struct bss_params *add_bss); + +#ifdef WLAN_FEATURE_HOST_ROAM +/** + * wma_add_bss_lfr2_vdev_start() - add bss and start vdev during host roaming + * @vdev: vdev in object manager + * @add_bss: add bss param + * + * Return: None + */ +QDF_STATUS wma_add_bss_lfr2_vdev_start(struct wlan_objmgr_vdev *vdev, + struct bss_params *add_bss); +#endif + +/** + * wma_send_peer_assoc_req() - wma send peer assoc req when sta connect + * @add_bss: add bss param + * + * Return: None + */ +QDF_STATUS wma_send_peer_assoc_req(struct bss_params *add_bss); + +/** + * wma_get_rx_chainmask() - API to get rx chainmask from mac phy capability + * @pdev_id: pdev id + * @chainmask_2g: pointer to return 2g chainmask + * @chainmask_5g: pointer to return 5g chainmask + * + * API to get rx chainmask from mac phy capability directly. + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS wma_get_rx_chainmask(uint8_t pdev_id, uint32_t *chainmask_2g, + uint32_t *chainmask_5g); + +/** + * wma_handle_channel_switch_resp() - handle channel switch resp + * @wma: wma handle + * @rsp: response for channel switch + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_handle_channel_switch_resp(tp_wma_handle wma, + struct vdev_start_response *rsp); + +/** + * wma_pre_chan_switch_setup() - handler before channel switch vdev start + * @vdev_id: vdev id + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_pre_chan_switch_setup(uint8_t vdev_id); + +/** + * wma_post_chan_switch_setup() - handler after channel switch vdev start + * @vdev_id: vdev id + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_post_chan_switch_setup(uint8_t vdev_id); + +/** + * wma_vdev_pre_start() - prepare vdev start + * @vdev_id: vdev id + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_vdev_pre_start(uint8_t vdev_id, bool restart); + +/** + * wma_remove_bss_peer_on_vdev_start_failure() - remove the bss peers in case of + * vdev start request failure + * @wma: wma handle. + * @vdev_id: vdev id + * + * This API deletes the BSS peer created during ADD BSS in case of ADD BSS + * request sent to the FW fails. + * + * Return: None; + */ +void wma_remove_bss_peer_on_vdev_start_failure(tp_wma_handle wma, + uint8_t vdev_id); + +/** + * wma_send_add_bss_resp() - send add bss failure + * @wma: wma handle. + * @vdev_id: vdev id + * @status: status + * + * Return: None + */ +void wma_send_add_bss_resp(tp_wma_handle wma, uint8_t vdev_id, + QDF_STATUS status); + +/** + * wma_post_vdev_start_setup() - wma post vdev start handler + * @wma: wma handle. + * @vdev_id: vdev id + * + * Return: Success or Failure status + */ +QDF_STATUS wma_post_vdev_start_setup(uint8_t vdev_id); + +/** + * wma_pre_vdev_start_setup() - wma pre vdev start handler + * @wma: wma handle. + * @vdev_id: vdev id + * @addbss_param: bss param + * + * Return: Success or Failure status + */ +QDF_STATUS wma_pre_vdev_start_setup(uint8_t vdev_id, + struct bss_params *add_bss); + +#ifdef FEATURE_ANI_LEVEL_REQUEST +/** + * wma_send_ani_level_request() - Send get ani level cmd to WMI + * @wma_handle: wma handle. + * @freqs: pointer to channels for which ANI level has to be retrieved + * @num_freqs: number of channels in the above parameter + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_send_ani_level_request(tp_wma_handle wma_handle, + uint32_t *freqs, uint8_t num_freqs); +#endif /* FEATURE_ANI_LEVEL_REQUEST */ +#endif + diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma_api.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma_api.h new file mode 100644 index 0000000000000000000000000000000000000000..06432d71b89ff050d371b58859cf107c8ad5a38c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma_api.h @@ -0,0 +1,824 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WMA_API_H +#define WMA_API_H + +#include "osdep.h" +#include "ani_global.h" +#include "a_types.h" +#include "osapi_linux.h" +#include "wmi_unified.h" +#ifdef NOT_YET +#include "htc_api.h" +#endif +#include "lim_global.h" +#include "cds_utils.h" +#include "scheduler_api.h" +#include "wlan_policy_mgr_api.h" +#include "wma_sar_public_structs.h" +#include +#include "include/wlan_vdev_mlme.h" +#include "wlan_mlme_vdev_mgr_interface.h" + +typedef void *WMA_HANDLE; + +/** + * enum GEN_PARAM - general parameters + * @GEN_VDEV_PARAM_AMPDU: Set ampdu size + * @GEN_VDEV_PARAM_AMSDU: Set amsdu size + * @GEN_PARAM_CRASH_INJECT: inject crash + * @GEN_PARAM_CAPTURE_TSF: read tsf + * @GEN_PARAM_RESET_TSF_GPIO: reset tsf gpio + * @GEN_VDEV_ROAM_SYNCH_DELAY: roam sync delay + */ +enum GEN_PARAM { + GEN_VDEV_PARAM_AMPDU = 0x1, + GEN_VDEV_PARAM_AMSDU, + GEN_PARAM_CRASH_INJECT, + GEN_PARAM_CAPTURE_TSF, + GEN_PARAM_RESET_TSF_GPIO, + GEN_VDEV_ROAM_SYNCH_DELAY, +}; + +/** + * struct wma_caps_per_phy - various caps per phy + * @ht_2g: entire HT cap for 2G band in terms of 32 bit flag + * @ht_5g: entire HT cap for 5G band in terms of 32 bit flag + * @vht_2g: entire VHT cap for 2G band in terms of 32 bit flag + * @vht_5g: entire VHT cap for 5G band in terms of 32 bit flag + * @he_2g: entire HE cap for 2G band in terms of 32 bit flag + * @he_5g: entire HE cap for 5G band in terms of 32 bit flag + * @tx_chain_mask_2G: tx chain mask for 2g + * @rx_chain_mask_2G: rx chain mask for 2g + * @tx_chain_mask_5G: tx chain mask for 5g + * @rx_chain_mask_5G: rx chain mask for 5g + */ +struct wma_caps_per_phy { + uint32_t ht_2g; + uint32_t ht_5g; + uint32_t vht_2g; + uint32_t vht_5g; + uint32_t he_2g[PSOC_HOST_MAX_MAC_SIZE]; + uint32_t he_5g[PSOC_HOST_MAX_MAC_SIZE]; + uint32_t tx_chain_mask_2G; + uint32_t rx_chain_mask_2G; + uint32_t tx_chain_mask_5G; + uint32_t rx_chain_mask_5G; +}; + + +#define VDEV_CMD 1 +#define PDEV_CMD 2 +#define GEN_CMD 3 +#define DBG_CMD 4 +#define PPS_CMD 5 +#define QPOWER_CMD 6 +#define GTX_CMD 7 + +typedef void (*wma_peer_authorized_fp) (uint32_t vdev_id); + + +QDF_STATUS wma_pre_start(void); + +QDF_STATUS wma_mc_process_handler(struct scheduler_msg *msg); + +QDF_STATUS wma_start(void); + +/** + * wma_stop() - wma stop function. + * + * Performs all of the operations required to stop the WMA layer + * + * Return: QDF_STATUS_SUCCESS on success, QDF Error on failure + */ +QDF_STATUS wma_stop(void); + +QDF_STATUS wma_close(void); + +QDF_STATUS wma_wmi_service_close(void); + +QDF_STATUS wma_wmi_work_close(void); + +int wma_rx_ready_event(void *handle, uint8_t *ev, uint32_t len); + +int wma_rx_service_ready_event(void *handle, uint8_t *ev, uint32_t len); + +int wma_rx_service_ready_ext_event(void *handle, uint8_t *ev, uint32_t len); + +void wma_setneedshutdown(void); + +bool wma_needshutdown(void); + +QDF_STATUS wma_wait_for_ready_event(WMA_HANDLE handle); + +int wma_cli_get_command(int vdev_id, int param_id, int vpdev); +int wma_cli_set_command(int vdev_id, int param_id, int sval, int vpdev); +int wma_cli_set2_command(int vdev_id, int param_id, int sval1, + int sval2, int vpdev); + +/** + * wma_get_fw_phy_mode_for_freq_cb() - Callback to get current PHY Mode. + * @freq: channel freq + * @chan_width: maximum channel width possible + * @phy_mode: firmware PHY Mode + * + * Return: None + */ +void wma_get_fw_phy_mode_for_freq_cb(uint32_t freq, uint32_t chan_width, + uint32_t *phy_mode); +/** + * wma_get_phy_mode_cb() - Callback to get current PHY Mode. + * @chan: channel number + * @chan_width: maximum channel width possible + * @phy_mode: PHY Mode + * + * Return: None + */ +void wma_get_phy_mode_cb(uint8_t chan, uint32_t chan_width, + enum wlan_phymode *phy_mode); + +QDF_STATUS wma_set_htconfig(uint8_t vdev_id, uint16_t ht_capab, int value); + +void wma_set_peer_authorized_cb(void *wma_ctx, wma_peer_authorized_fp auth_cb); +QDF_STATUS wma_set_peer_param(void *wma_ctx, uint8_t *peer_addr, + uint32_t param_id, + uint32_t param_value, uint32_t vdev_id); +QDF_STATUS wma_get_link_speed(WMA_HANDLE handle, + struct link_speed_info *pLinkSpeed); +#ifdef NOT_YET +QDF_STATUS wma_update_channel_list(WMA_HANDLE handle, void *scan_chan_info); +#endif + +/** + * wma_get_vdev_address_by_vdev_id() - lookup MAC address from vdev ID + * @vdev_id: vdev id + * + * Return: mac address + */ +uint8_t *wma_get_vdev_address_by_vdev_id(uint8_t vdev_id); +struct wma_txrx_node *wma_get_interface_by_vdev_id(uint8_t vdev_id); +QDF_STATUS wma_get_connection_info(uint8_t vdev_id, + struct policy_mgr_vdev_entry_info *conn_table_entry); +QDF_STATUS wma_ndi_update_connection_info(uint8_t vdev_id, + struct nan_datapath_channel_info *ndp_chan_info); + +bool wma_is_vdev_up(uint8_t vdev_id); + +void *wma_get_beacon_buffer_by_vdev_id(uint8_t vdev_id, uint32_t *buffer_size); + +bool wma_get_fw_wlan_feat_caps(enum cap_bitmap feature); +void wma_set_fw_wlan_feat_caps(enum cap_bitmap feature); + +QDF_STATUS wma_post_ctrl_msg(struct mac_context *mac, struct scheduler_msg *pMsg); + +void wma_update_intf_hw_mode_params(uint32_t vdev_id, uint32_t mac_id, + uint32_t cfgd_hw_mode_index); +void wma_set_dbs_capability_ut(uint32_t dbs); +QDF_STATUS wma_get_caps_for_phyidx_hwmode(struct wma_caps_per_phy *caps_per_phy, + enum hw_mode_dbs_capab hw_mode, enum cds_band_type band); +bool wma_is_rx_ldpc_supported_for_channel(uint32_t ch_freq); + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS +int wma_unified_radio_tx_mem_free(void *handle); + +/* + * wma_unified_link_stats_results_mem_free() - Free the memory for + * link_stats_results->results allocated when event comes. + * @link_stats_results: pointer to the memory that is to be freed + * + * Return: None + */ +void +wma_unified_link_stats_results_mem_free(tSirLLStatsResults *link_stats_results); + +#else /* WLAN_FEATURE_LINK_LAYER_STATS */ +static inline int wma_unified_radio_tx_mem_free(void *handle) +{ + return 0; +} +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +/** + * wma_form_unit_test_cmd_and_send() - to form a wma command and send it to FW + * @session_id: wma session id to be filled while forming the command + * @module_id: module id given by user to be filled in the command + * @arg_count: number of argument count + * @arg: pointer to argument list + * + * This API exposed to HDD layer which takes the argument from user and forms + * the wma unit test command to be sent down to firmware + * + * Return: QDF_STATUS based on overall success + */ +QDF_STATUS wma_form_unit_test_cmd_and_send(uint32_t vdev_id, + uint32_t module_id, uint32_t arg_count, uint32_t *arg); + +/** + * wma_lro_init() - sends LRO configuration to FW + * @lro_config: pointer to the config parameters + * + * This function ends LRO configuration to FW. + * + * Return: 0 for success or reasons for failure + */ +int wma_lro_init(struct cdp_lro_hash_config *lro_config); + +QDF_STATUS wma_remove_beacon_filter(WMA_HANDLE wma, + struct beacon_filter_param *filter_params); + +QDF_STATUS wma_add_beacon_filter(WMA_HANDLE wma, + struct beacon_filter_param *filter_params); +QDF_STATUS wma_send_adapt_dwelltime_params(WMA_HANDLE handle, + struct adaptive_dwelltime_params *dwelltime_params); + +/** + * wma_send_dbs_scan_selection_params() - send DBS scan selection configuration + * params to firmware + * @handle: wma handler + * @dbs_scan_params: pointer to wmi_dbs_scan_sel_params + * + * Return: QDF_STATUS_SUCCESS on success and QDF failure reason code for failure + */ +QDF_STATUS wma_send_dbs_scan_selection_params(WMA_HANDLE handle, + struct wmi_dbs_scan_sel_params *dbs_scan_params); +QDF_STATUS wma_set_tx_power_scale(uint8_t vdev_id, int value); +QDF_STATUS wma_set_tx_power_scale_decr_db(uint8_t vdev_id, int value); + +bool wma_is_csa_offload_enabled(void); +/** + * wma_is_mbssid_enabled - checks MBSSID support + * + * Return: true or false + */ +bool wma_is_mbssid_enabled(void); + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +bool wma_is_p2p_lo_capable(void); +#else +static inline bool wma_is_p2p_lo_capable(void) +{ + return 0; +} +#endif +bool wma_capability_enhanced_mcast_filter(void); +void wma_process_pdev_hw_mode_trans_ind(void *wma, + wmi_pdev_hw_mode_transition_event_fixed_param *fixed_param, + wmi_pdev_set_hw_mode_response_vdev_mac_entry *vdev_mac_entry, + struct sir_hw_mode_trans_ind *hw_mode_trans_ind); + +/** + * wma_set_cts2self_for_p2p_go() - set CTS2SELF command for P2P GO. + * @wma_handle: pointer to wma handle. + * @cts2self_for_p2p_go: value needs to set to firmware. + * + * At the time of driver startup, inform about ini parma to FW that + * if legacy client connects to P2P GO, stop using NOA for P2P GO. + * + * Return: QDF_STATUS. + */ +QDF_STATUS wma_set_cts2self_for_p2p_go(void *wma_handle, + uint32_t cts2self_for_p2p_go); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * wma_get_roam_scan_ch() - API to get roam scan channel list. + * @wma_handle: pointer to wma handle. + * @vdev_id: vdev id + * + * Return: QDF_STATUS. + */ +QDF_STATUS wma_get_roam_scan_ch(wmi_unified_t wma, + uint8_t vdev_id); +#else +static inline +QDF_STATUS wma_get_roam_scan_ch(wmi_unified_t wma, + uint8_t vdev_id) +{ + return QDF_STATUS_E_FAILURE; +} +#endif + +/** + * wma_set_tx_rx_aggr_size() - set tx rx aggregation size + * @vdev_id: vdev id + * @tx_size: tx aggr size + * @rx_size: rx aggr size + * @aggr_type: aggregation type + * + * This function try to set the aggregation size. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS wma_set_tx_rx_aggr_size(uint8_t vdev_id, + uint32_t tx_size, + uint32_t rx_size, + wmi_vdev_custom_aggr_type_t aggr_type); +/** + * wma_set_tx_rx_aggr_size_per_ac() - set aggregation size per ac + * @wma_handle: pointer to wma handle. + * @vdev_id: vdev_id + * @qos_aggr: QoS data + * @aggr_type: aggregation type + * + * This function try to set the aggregation size per AC. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS +wma_set_tx_rx_aggr_size_per_ac(WMA_HANDLE wma_handle, + uint8_t vdev_id, + struct wlan_mlme_qos *qos_aggr, + wmi_vdev_custom_aggr_type_t aggr_type); + +/** + * wma_set_sw_retry_threshold_per_ac() - set sw retry threshold per AC for tx + * @handle: wma handle + * @vdev_id: vdev id + * @qos_aggr: pointer to QOS TX/RX aggregation values + * + * This function sends WMI command to set the sw retry threshold per AC + * for Tx. + * + * Return: QDF_STATUS. + */ +QDF_STATUS wma_set_sw_retry_threshold_per_ac + (WMA_HANDLE handle, + uint8_t vdev_id, struct wlan_mlme_qos *qos_aggr); +/** + * wma_set_sw_retry_threshold() - set sw retry threshold for tx + * @vdev_id: vdev + * @retry: retry number + * @param_id: aggregrate sw retry threshold param id + * + * This function sends WMI command to set the sw retry threshold for Tx. + * + * Return: QDF_STATUS. + */ +QDF_STATUS wma_set_sw_retry_threshold(uint8_t vdev_id, uint32_t retry, + uint32_t param_id); + +/** + * wma_get_sar_limit() - get SAR limits from the target + * @handle: wma handle + * @callback: Callback function to invoke with the results + * @context: Opaque context to pass back to caller in the callback + * + * This function sends WMI command to get SAR limits. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS wma_get_sar_limit(WMA_HANDLE handle, + wma_sar_cb callback, void *context); + +/** + * wma_set_sar_limit() - set sar limits in the target + * @handle: wma handle + * @sar_limit_cmd_params: sar limit cmd params + * + * This function sends WMI command to set SAR limits. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS wma_set_sar_limit(WMA_HANDLE handle, + struct sar_limit_cmd_params *sar_limit_params); + +/** + * wma_send_coex_config_cmd() - Send coex config params + * @wma_handle: wma handle + * @coex_cfg_params: struct to coex cofig params + * + * This function sends WMI command to send coex cofig params + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_send_coex_config_cmd(WMA_HANDLE wma_handle, + struct coex_config_params *coex_cfg_params); + +/** + * wma_set_power_config() - update power config in wma + * @vdev_id: the Id of the vdev to configure + * @power: new power value + * + * Return: QDF_STATUS_SUCCESS on success, error number otherwise + */ +QDF_STATUS wma_set_power_config(uint8_t vdev_id, enum powersave_mode power); + +#ifdef FEATURE_WLAN_D0WOW +static inline bool wma_d0_wow_is_supported(void) +{ + return true; +} +#else +static inline bool wma_d0_wow_is_supported(void) +{ + return false; +} +#endif + +/** + * wma_store_pdev() - store pdev + * @wma_ctx: wma context + * @pdev: pdev context + * + * Return: void + */ +void wma_store_pdev(void *wma_ctx, struct wlan_objmgr_pdev *pdev); + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS +/** + * wmi_to_sir_peer_type() - convert peer type from WMI to SIR enum + * @type: enum wmi_peer_type + * + * Return: tSirWifiPeerType + */ +tSirWifiPeerType wmi_to_sir_peer_type(enum wmi_peer_type type); +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +QDF_STATUS wma_crash_inject(WMA_HANDLE wma_handle, uint32_t type, + uint32_t delay_time_ms); + +/** + * wma_critical_events_in_flight() - get the number of critical events in flight + * + * This API gets the number of events in flight which should prevent power + * collapse. + * + * Return: the number of critical events in flight + */ +uint32_t wma_critical_events_in_flight(void); + +#ifdef FEATURE_WLAN_DYNAMIC_CVM +/** + * wma_set_vc_mode_config() - set voltage corner mode config to FW. + * @wma_handle: pointer to wma handle. + * @vc_bitmap: value needs to set to firmware. + * + * At the time of driver startup, set operating voltage corner mode + * for differenet phymode and bw configurations. + * + * Return: QDF_STATUS. + */ +QDF_STATUS wma_set_vc_mode_config(void *wma_handle, + uint32_t vc_bitmap); +#endif + +QDF_STATUS wma_process_dhcp_ind(WMA_HANDLE wma_handle, + tAniDHCPInd *ta_dhcp_ind); + +/** + * wma_wmi_stop() - send wmi stop cmd + * + * Return: None + */ +void wma_wmi_stop(void); + +/** + * wma_get_mcs_idx() - get mcs index + * @raw_rate: raw rate from fw + * @rate_flags: rate flags + * @nss: nss + * @dcm: dcm + * @guard_interval: guard interval + * @mcs_rate_flag: mcs rate flags + * + * Return: mcs index + */ +uint8_t wma_get_mcs_idx(uint16_t raw_rate, enum tx_rate_info rate_flags, + uint8_t *nss, uint8_t *dcm, + enum txrate_gi *guard_interval, + enum tx_rate_info *mcs_rate_flag); + +/** + * wma_get_hidden_ssid_restart_in_progress() - check if hidden ssid restart is + * in progress + * @iface: iface pointer + * + * Return: true if hidden ssid restart is in progress else false + */ +bool wma_get_hidden_ssid_restart_in_progress(struct wma_txrx_node *iface); + +/** + * wma_get_channel_switch_in_progress() - check if channel switch is in progress + * @iface: iface pointer + * + * Return: true if channel switch is in progress else false + */ +bool wma_get_channel_switch_in_progress(struct wma_txrx_node *iface); + +/** + * wma_sta_mlme_vdev_start_continue() - VDEV start response handling + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV start response actions + * + * Return: SUCCESS on successful completion of start response operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_sta_mlme_vdev_start_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_sta_mlme_vdev_roam_notify() - VDEV roam notify handling + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV roam event handling + * + * Return: SUCCESS on successful completion of roam event handling + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_sta_mlme_vdev_roam_notify(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_ap_mlme_vdev_start_continue() - VDEV start response handling + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV start response actions + * + * Return: SUCCESS on successful completion of start response operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_ap_mlme_vdev_start_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_sta_vdev_up_send() - Send VDEV UP command + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV UP Command + * + * Return: SUCCESS on successful completion of start response operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_sta_vdev_up_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_mlme_vdev_stop_continue() - VDEV stop response handling + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV stop response actions + * + * Return: SUCCESS on successful completion of stop response operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_mlme_vdev_stop_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_ap_mlme_vdev_down_send() - VDEV down operation + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV down operation + * + * Return: SUCCESS on successful completion of VDEV down operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_ap_mlme_vdev_down_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_mlme_vdev_notify_down_complete() - VDEV init state transition + * notification + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API notifies MLME on moving to INIT state + * + * Return: SUCCESS on successful completion of down notification + * FAILURE, if it fails due to any + */ +QDF_STATUS +wma_mlme_vdev_notify_down_complete(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_ap_mlme_vdev_stop_start_send() - handle vdev stop during start req + * @vdev_mlme_obj: VDEV MLME comp object + * @type: restart req or start req + * @data_len: data size + * @data: event data + * + * API handle vdev stop during start req + * + * Return: SUCCESS alsways + */ +QDF_STATUS wma_ap_mlme_vdev_stop_start_send(struct vdev_mlme_obj *vdev_mlme, + enum vdev_cmd_type type, + uint16_t data_len, void *data); + +/** + * wma_sta_mlme_vdev_down_send() - VDEV down operation + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV down operation + * + * Return: SUCCESS on successful completion of VDEV down operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_sta_mlme_vdev_down_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); +/** + * wma_post_vdev_create_setup() - Post vdev create setup + * @vdev: vdev obj + * + * This API is invoked after vded is created to perform post + * vdev create operations i.e. creating peer and setting vdev params. + * + * Return: SUCCESS on successful post vdev operations, FAILURE, if it + * fails due to any + */ +QDF_STATUS wma_post_vdev_create_setup(struct wlan_objmgr_vdev *vdev); + +/** + * wma_mon_mlme_vdev_start_continue() - VDEV start response handling + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV start response actions + * + * Return: SUCCESS on successful completion of start response operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_mon_mlme_vdev_start_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_mon_mlme_vdev_up_send() - Send VDEV UP command + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV UP Command + * + * Return: SUCCESS on successful completion of start response operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_mon_mlme_vdev_up_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_mon_mlme_vdev_stop_send() - VDEV stop operation + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV stop operation + * + * Return: SUCCESS on successful completion of VDEV stop operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_mon_mlme_vdev_stop_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_mon_mlme_vdev_down_send() - VDEV down operation + * @vdev_mlme_obj: VDEV MLME comp object + * @data_len: data size + * @data: event data + * + * API invokes VDEV down operation + * + * Return: SUCCESS on successful completion of VDEV down operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_mon_mlme_vdev_down_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data); + +/** + * wma_vdev_detach_callback() - VDEV delete response handler + * @rsp: pointer to vdev delete response + * + * This API proccesses vdev delete response and gives to upper layers + * + * Return: SUCCESS on successful completion of VDEV delete operation + * FAILURE, if it fails due to any + */ +QDF_STATUS wma_vdev_detach_callback(struct vdev_delete_response *rsp); + +/** + * wma_vdev_stop_resp_handler() - vdev stop response handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: buffer length + * + * Return: 0 for success or error code + */ +QDF_STATUS wma_vdev_stop_resp_handler(struct vdev_mlme_obj *vdev_mlme, + struct vdev_stop_response *rsp); + +/** + * wma_vdev_start_resp_handler() - vdev start response handler + * @vdev_mlme: vdev mlme obj + * @rsp: vdev start response + * + * Return: QDF status + */ +QDF_STATUS wma_vdev_start_resp_handler(struct vdev_mlme_obj *vdev_mlme, + struct vdev_start_response *rsp); + +#ifdef FEATURE_WLM_STATS +/** + * typedef wma_wlm_stats_cb() - Callback function for WLM stats + * @cookie: Cookie provided by client during callback registration + * @data: Hex ASCII representation of the WLM stats + */ +typedef void (*wma_wlm_stats_cb)(void *cookie, const char *data); + +/** + * wma_wlm_stats_req() - Send a req to WLAN Latency Manager in FW + * @vdev_id: vdev id to be sent to FW's WLM + * @bitmask: A bitmask which is requested by user to be sent to FW's WLM + * @max_size: Size of user's buffer to store the response + * @cb: A callback to be called to once response is available + * @cookie: A cookie to be used by callback to retrieve the context of req + * + * This API is used to send a message to WLAN latency manager component + * in FW to retrieve some latency related data and send it to user space. + * Driver is just a pass-through for user to interract with FW. + * + * Return: 0 on success and non-zero for error + */ +int wma_wlm_stats_req(int vdev_id, uint32_t bitmask, uint32_t max_size, + wma_wlm_stats_cb cb, void *cookie); + +/** + * wma_wlm_stats_rsp() - Handler to handle the response from FW's WLM component + * @wma_ctx: WMA context + * @event: WMI TLV event data + * @len: WMI TLV length + * + * This API is registered with WMI component in order to handle the response + * coming from FW's WLM correspondence to WLM REQ to FW. This API takes the + * data coming as HEX stream and write in to CHAR buffer as HEX CHAR stream + * and send this char buffer to user space through callback. + * + * Return: 0 on success and non-zero for error + */ +int wma_wlm_stats_rsp(void *wma_ctx, uint8_t *event, uint32_t len); +#endif /* FEATURE_WLM_STATS */ + +/** + * wma_update_roam_offload_flag() - update roam offload flag to fw + * @wma: wma handle + * @params: Roaming enable/disable params + * + * Return: none + */ +void wma_update_roam_offload_flag(void *handle, + struct roam_init_params *params); + +/** + * wma_self_peer_create() - create self peer in objmgr + * @vdev_mlme: vdev mlme component private object + * + * Create the self peer in firmware for beaconing vdev's and create then + * object manager self-peer for the vdev. + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_vdev_self_peer_create(struct vdev_mlme_obj *vdev_mlme); + +/** + * wma_cleanup_vdev() - cleanup wma layers vdev + * @vdev: Object manager vdev + * + * This function cleansup the wma layers vdev related data. + * + * Return: None + */ +void wma_cleanup_vdev(struct wlan_objmgr_vdev *vdev); + +#endif /* WMA_API_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma_coex.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma_coex.h new file mode 100644 index 0000000000000000000000000000000000000000..33ff1fd4d531d656b4db3797dcdf9bfad7df9096 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma_coex.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WLAN_HDD_DEBUGFS_COEX_H +#define _WLAN_HDD_DEBUGFS_COEX_H + +#include +#include "sme_api.h" + +#ifdef WLAN_MWS_INFO_DEBUGFS +void wma_get_mws_coex_info_req(tp_wma_handle wma_handle, + struct sir_get_mws_coex_info *req); +void wma_register_mws_coex_events(tp_wma_handle wma_handle); +#else +static void wma_register_mws_coex_events(tp_wma_handle wma_handle) +{ +} +#endif +#endif diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma_fw_state.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma_fw_state.h new file mode 100644 index 0000000000000000000000000000000000000000..d508cb0fb09ffa0c7b4de4ed80f2f46ee3ae5d4d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma_fw_state.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_fw_state.h + * + * Get firmware state related API's and definitions + */ + +#ifndef __WMA_FW_STATE_H +#define __WMA_FW_STATE_H + +#include "wma.h" + +#ifdef FEATURE_FW_STATE +/** + * wma_get_fw_state() - send wmi cmd to get fw state + * @wma_handle: wma handler + * + * Return: Return QDF_STATUS + */ +QDF_STATUS wma_get_fw_state(tp_wma_handle wma_handle); +void wma_register_fw_state_events(wmi_unified_t wmi_handle); +#else /* FEATURE_FW_STATE */ +static inline +void wma_register_fw_state_events(WMA_HANDLE wma_handle) +{ +} +#endif /* FEATURE_FW_STATE */ +#endif /* __WMA_FW_STATE_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma_he.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma_he.h new file mode 100644 index 0000000000000000000000000000000000000000..1bf80a93e27cdc016c925e57eb960c44a4ede25e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma_he.h @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WMA_HE_H +#define __WMA_HE_H + +#include "wma.h" +#include "sir_api.h" +#include "target_if.h" + +#ifdef WLAN_FEATURE_11AX +/** + * wma_print_he_cap() - Print HE capabilities + * @he_cap: pointer to HE Capability + * + * Received HE capabilities are converted into dot11f structure. + * This function will print all the HE capabilities as stored + * in the dot11f structure. + * + * Return: None + */ +void wma_print_he_cap(tDot11fIEhe_cap *he_cap); + +/** + * wma_print_he_ppet() - Prints HE PPE Threshold + * @he_ppet: PPE Threshold + * + * This function prints HE PPE Threshold as received from FW. + * Refer to the definition of wmi_ppe_threshold to understand + * how PPE thresholds are packed by FW for a given NSS and RU. + * + * Return: none + */ +void wma_print_he_ppet(void *ppet); + +/** + * wma_print_he_phy_cap() - Print HE PHY Capability + * @phy_cap: pointer to PHY Capability + * + * This function prints HE PHY Capability received from FW. + * + * Return: none + */ +void wma_print_he_phy_cap(uint32_t *phy_cap); + +/** + * wma_print_he_mac_cap_w1() - Print HE MAC Capability + * @mac_cap: MAC Capability + * + * This function prints HE MAC Capability received from FW. + * + * Return: none + */ +void wma_print_he_mac_cap_w1(uint32_t mac_cap); + +/** + * wma_print_he_mac_cap_w2() - Print HE MAC Capability + * @mac_cap: MAC Capability + * + * This function prints HE MAC Capability received from FW. + * + * Return: none + */ +void wma_print_he_mac_cap_w2(uint32_t mac_cap); + +/** + * wma_print_he_op() - Print HE Operation + * @he_cap: pointer to HE Operation + * + * Print HE operation stored as dot11f structure + * + * Return: None + */ +void wma_print_he_op(tDot11fIEhe_op *he_ops); + +/** + * wma_update_target_ext_he_cap() - Update HE caps with given extended cap + * @tgt_hdl: target psoc information + * @tgt_cfg: Target config + * + * This function loop through each hardware mode and for each hardware mode + * again it loop through each MAC/PHY and pull the caps 2G and 5G specific + * HE caps and derives the final cap. + * + * Return: None + */ +void wma_update_target_ext_he_cap(struct target_psoc_info *tgt_hdl, + struct wma_tgt_cfg *tgt_cfg); + +/* + * wma_he_update_tgt_services() - update tgt cfg to indicate 11ax support + * @wmi_handle: pointer to WMI handle + * @cfg: pointer to WMA target services + * + * Based on WMI SERVICES information, enable 11ax support and set DOT11AX bit + * in feature caps bitmap. + * + * Return: None + */ +void wma_he_update_tgt_services(struct wmi_unified *wmi_handle, + struct wma_tgt_services *cfg); + +/** + * wma_populate_peer_he_cap() - populate peer HE capabilities in peer assoc cmd + * @peer: pointer to peer assoc params + * @params: pointer to ADD STA params + * + * Return: None + */ +void wma_populate_peer_he_cap(struct peer_assoc_params *peer, + tpAddStaParams params); + +/** + * wma_update_vdev_he_ops() - update he ops in vdev start request + * @he_ops: target he ops + * @he_op: source he ops + * + * Return: None + */ +void wma_update_vdev_he_ops(uint32_t *he_ops, tDot11fIEhe_op *he_op); + +#define DOT11AX_HEMU_MODE 0x30 +#define HE_SUBFEE 0 +#define HE_SUBFER 1 +#define HE_MUBFEE 2 +#define HE_MUBFER 3 + +/** + * wma_set_he_txbf_params() - set HE Tx beamforming params to FW + * @vdev_id: VDEV id + * @su bfer: SU beamformer capability + * @su bfee: SU beamformee capability + * @mu bfer: MU beamformer capability + * + * Return: None + */ +void wma_set_he_txbf_params(uint8_t vdev_id, bool su_bfer, + bool su_bfee, bool mu_bfer); + + +/** + * wma_set_he_txbf_cfg() - set HE Tx beamforming mlme cfg to FW + * @mac: Global MAC context + * @vdev_id: VDEV id + * + * Return: None + */ +void wma_set_he_txbf_cfg(struct mac_context *mac, uint8_t vdev_id); +/** + * wma_vdev_set_he_bss_params() - set HE OPs in vdev start + * @wma: pointer to wma handle + * @vdev_id: VDEV id + * @he_info: pointer to he info + * + * Return: None + */ +void wma_vdev_set_he_bss_params(tp_wma_handle wma, uint8_t vdev_id, + struct vdev_mlme_he_ops_info *he_info); + +/** + * wma_vdev_set_he_config() - set HE Config in vdev start + * @wma: pointer to wma handle + * @vdev_id: VDEV id + * @add_bss: BSS params + * + * Return: None + */ +void wma_vdev_set_he_config(tp_wma_handle wma, uint8_t vdev_id, + struct bss_params *add_bss); + +static inline bool wma_is_peer_he_capable(tpAddStaParams params) +{ + return params->he_capable; +} + +/** + * wma_update_he_ops_ie() - update the HE OPS IE to firmware + * @wma: pointer to wma context + * @vdev_id: vdev id + * @he_ops: 32bit value of HE ops + * + * This API is used to send updated HE operational IE to firmware, so that + * firmware can be in sync with host + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_update_he_ops_ie(tp_wma_handle wma, uint8_t vdev_id, + tDot11fIEhe_op *he_ops); + +/** + * wma_get_he_capabilities() - Get HE capabilities from WMA + * @he_cap: Pointer to HE capabilities + * + * Currently HE capabilities are not updated in wma_handle. This + * is an interface for upper layer to query capabilities from WMA. + * When the real use case arise, update wma_handle with HE capabilities + * as required. + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_get_he_capabilities(struct he_capability *he_cap); + +/** + * wma_set_he_vdev_param() - update he vdev param in wma + * @intr: pointer to wma_txrx_node + * @param_id: vdev param id + * @value: value of vdev param + * + * Result: None + */ +void wma_set_he_vdev_param(struct wma_txrx_node *intr, WMI_VDEV_PARAM param_id, + uint32_t value); + +/** + * wma_get_he_vdev_param() - retrieve he vdev param from wma + * @intr: pointer to wma_txrx_node + * @param_id: vdev param id + * + * Result: param value + */ +uint32_t wma_get_he_vdev_param(struct wma_txrx_node *intr, + WMI_VDEV_PARAM param_id); + +#else +static inline void wma_print_he_cap(tDot11fIEhe_cap *he_cap) +{ +} + +static inline void wma_print_he_ppet(void *ppet) +{ +} + +static inline void wma_print_he_phy_cap(uint32_t *phy_cap) +{ +} + +static inline void wma_print_he_mac_cap_w1(uint32_t mac_cap) +{ +} + +static inline void wma_print_he_mac_cap_w2(uint32_t mac_cap) +{ +} + +static inline void wma_print_he_op(tDot11fIEhe_op *he_ops) +{ +} + +static inline void wma_update_target_ext_he_cap(struct + target_psoc_info *tgt_hdl, + struct wma_tgt_cfg *tgt_cfg) +{ +} + +static inline void wma_he_update_tgt_services(struct wmi_unified *wmi_handle, + struct wma_tgt_services *cfg) +{ + cfg->en_11ax = false; + return; +} + +static inline void wma_populate_peer_he_cap(struct peer_assoc_params *peer, + tpAddStaParams params) +{ +} + +static inline +void wma_update_vdev_he_ops(uint32_t *he_ops, tDot11fIEhe_op *he_op) +{ +} + +static inline void wma_set_he_txbf_params(uint8_t vdev_id, bool su_bfer, + bool su_bfee, bool mu_bfer) +{ +} + +static inline void wma_set_he_txbf_cfg(struct mac_context *mac, uint8_t vdev_id) +{ +} + +static inline QDF_STATUS wma_update_he_ops_ie(tp_wma_handle wma, + uint8_t vdev_id, tDot11fIEhe_op *he_ops) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void wma_vdev_set_he_bss_params(tp_wma_handle wma, uint8_t vdev_id, + struct vdev_mlme_he_ops_info *he_info) +{ +} + +static inline void wma_vdev_set_he_config(tp_wma_handle wma, uint8_t vdev_id, + struct bss_params *add_bss) +{ +} + +static inline bool wma_is_peer_he_capable(tpAddStaParams params) +{ + return false; +} + +static inline void wma_set_he_vdev_param(struct wma_txrx_node *intr, + WMI_VDEV_PARAM param_id, uint32_t value) +{ + WMA_LOGI(FL("Unable to update WMI_VDEV_PARAM: %0x"), param_id); +} + +static inline uint32_t wma_get_he_vdev_param(struct wma_txrx_node *intr, + WMI_VDEV_PARAM param_id) +{ + WMA_LOGI(FL("Unable to update WMI_VDEV_PARAM: %0x"), param_id); + return 0; +} + +#endif + +#endif /* __WMA_HE_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma_if.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma_if.h new file mode 100644 index 0000000000000000000000000000000000000000..ab36d962fd19a4a81f74507fd8e157860c7c2657 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma_if.h @@ -0,0 +1,812 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HALMSGAPI_H_ +#define _HALMSGAPI_H_ + +#include "qdf_types.h" +#include "sir_api.h" +#include "sir_params.h" + +/* + * Validate the OS Type being built + */ + +#if defined(ANI_OS_TYPE_ANDROID) /* ANDROID */ + +#if defined(ANI_OS_TYPE_QNX) +#error "more than one ANI_OS_TYPE_xxx is defined for this build" +#endif + +#elif defined(ANI_OS_TYPE_QNX) /* QNX */ + +#if defined(ANI_OS_TYPE_ANDROID) +#error "more than one ANI_OS_TYPE_xxx is defined for this build" +#endif + +#elif !defined(ANI_OS_TYPE_ANDROID) && !defined(ANI_OS_TYPE_QNX) /* NONE */ +#error "NONE of the ANI_OS_TYPE_xxx are defined for this build" +#endif + +/* operMode in ADD BSS message */ +#define BSS_OPERATIONAL_MODE_AP 0 +#define BSS_OPERATIONAL_MODE_STA 1 +#define BSS_OPERATIONAL_MODE_IBSS 2 +#define BSS_OPERATIONAL_MODE_NDI 3 + +/* STA entry type in add sta message */ +#define STA_ENTRY_SELF 0 +#define STA_ENTRY_OTHER 1 +#define STA_ENTRY_BSSID 2 +/* Special station id for transmitting broadcast frames. */ +#define STA_ENTRY_BCAST 3 +#define STA_ENTRY_PEER STA_ENTRY_OTHER +#ifdef FEATURE_WLAN_TDLS +#define STA_ENTRY_TDLS_PEER 4 +#endif /* FEATURE_WLAN_TDLS */ +#define STA_ENTRY_NDI_PEER 5 + +#define STA_INVALID_IDX 0xFF + +/* invalid channel id. */ +#define INVALID_CHANNEL_ID 0 + +/** + * enum eFrameType - frame types + * @TXRX_FRM_RAW: raw frame + * @TXRX_FRM_ETH2: ethernet frame + * @TXRX_FRM_802_3: 802.3 frame + * @TXRX_FRM_802_11_MGMT: 802.11 mgmt frame + * @TXRX_FRM_802_11_CTRL: 802.11 control frame + * @TXRX_FRM_802_11_DATA: 802.11 data frame + */ +typedef enum { + TXRX_FRM_RAW, + TXRX_FRM_ETH2, + TXRX_FRM_802_3, + TXRX_FRM_802_11_MGMT, + TXRX_FRM_802_11_CTRL, + TXRX_FRM_802_11_DATA, + TXRX_FRM_IGNORED, /* This frame will be dropped */ + TXRX_FRM_MAX +} eFrameType; + +/** + * enum eFrameTxDir - frame tx direction + * @ANI_TXDIR_IBSS: IBSS frame + * @ANI_TXDIR_TODS: frame to DS + * @ANI_TXDIR_FROMDS: Frame from DS + * @ANI_TXDIR_WDS: WDS frame + */ +typedef enum { + ANI_TXDIR_IBSS = 0, + ANI_TXDIR_TODS, + ANI_TXDIR_FROMDS, + ANI_TXDIR_WDS +} eFrameTxDir; + +/** + *struct sAniBeaconStruct - Beacon structure + * @beaconLength: beacon length + * @macHdr: mac header for beacon + */ +typedef struct sAniBeaconStruct { + uint32_t beaconLength; + tSirMacMgmtHdr macHdr; +} qdf_packed tAniBeaconStruct, *tpAniBeaconStruct; + +/** + * struct sAniProbeRspStruct - probeRsp template structure + * @macHdr: mac header for probe response + */ +struct sAniProbeRspStruct { + tSirMacMgmtHdr macHdr; + /* probeRsp body follows here */ +} qdf_packed; + +/** + * struct tAddStaParams - add sta related parameters + * @bssId: bssid of sta + * @assocId: associd + * @staType: 0 - Self, 1 other/remote, 2 - bssid + * @staMac: MAC Address of STA + * @listenInterval: Listen interval + * @wmmEnabled: Support for 11e/WMM + * @uAPSD: U-APSD Flags: 1b per AC + * @maxSPLen: Max SP Length + * @htCapable: 11n HT capable STA + * @txChannelWidthSet: TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz + * @mimoPS: MIMO Power Save + * @maxAmpduDensity: 3 : 0~7 : 2^(11nAMPDUdensity -4) + * @maxAmsduSize: 1 : 3839 bytes, 0 : 7935 bytes + * @fShortGI40Mhz: short GI support for 40Mhz packets + * @fShortGI20Mhz: short GI support for 20Mhz packets + * @supportedRates: legacy supported rates + * @status: QDF status + * @staIdx: station index + * @updateSta: pdate the existing STA entry, if this flag is set + * @rmfEnabled: Robust Management Frame (RMF) enabled/disabled + * @encryptType: The unicast encryption type in the association + * @sessionId: PE session id + * @p2pCapableSta: if this is a P2P Capable Sta + * @csaOffloadEnable: CSA offload enable flag + * @vhtCapable: is VHT capabale or not + * @vhtTxChannelWidthSet: VHT channel width + * @vhtSupportedRxNss: VHT supported RX NSS + * @vhtTxBFCapable: txbf capable or not + * @vhtTxMUBformeeCapable: Bformee capable or not + * @enableVhtpAid: enable VHT AID + * @enableAmpduPs: AMPDU power save + * @enableHtSmps: enable HT SMPS + * @htSmpsconfig: HT SMPS config + * @htLdpcCapable: HT LDPC capable + * @vhtLdpcCapable: VHT LDPC capable + * @vht_mcs_10_11_supp: VHT MCS 10 & 11 support + * @smesessionId: sme session id + * @wpa_rsn: RSN capable + * @capab_info: capabality info + * @ht_caps: HT capabalities + * @vht_caps: VHT vapabalities + * @nwType: NW Type + * @maxTxPower: max tx power + * @atimIePresent: Peer Atim Info + * @peerAtimWindowLength: peer ATIM Window length + * @nss: Return the number of spatial streams supported + * @stbc_capable: stbc capable + * @no_ptk_4_way: Do not need 4-way handshake + * + * This structure contains parameter required for + * add sta request of upper layer. + */ +typedef struct { + tSirMacAddr bssId; + uint16_t assocId; + /* Field to indicate if this is sta entry for itself STA adding entry + * for itself or remote (AP adding STA after successful association. + * This may or may not be required in production driver. + */ + uint8_t staType; + tSirMacAddr staMac; + uint16_t listenInterval; + uint8_t wmmEnabled; + uint8_t uAPSD; + uint8_t maxSPLen; + uint8_t htCapable; + enum phy_ch_width ch_width; + tSirMacHTMIMOPowerSaveState mimoPS; + uint8_t maxAmpduSize; + uint8_t maxAmpduDensity; + /* 11n Parameters */ + /* HT STA should set it to 1 if it is enabled in BSS + * HT STA should set it to 0 if AP does not support it. + * This indication is sent to HAL and HAL uses this flag + * to pickup up appropriate 40Mhz rates. + */ + uint8_t fShortGI40Mhz; + uint8_t fShortGI20Mhz; + struct supported_rates supportedRates; + /* + * Following parameters are for returning status and station index from + * HAL to PE via response message. HAL does not read them. + */ + /* The return status of SIR_HAL_ADD_STA_REQ is reported here */ + QDF_STATUS status; + uint8_t updateSta; + uint8_t rmfEnabled; + uint32_t encryptType; + uint8_t sessionId; + uint8_t p2pCapableSta; + uint8_t csaOffloadEnable; + uint8_t vhtCapable; + uint8_t vhtSupportedRxNss; + uint8_t vht_160mhz_nss; + uint8_t vht_80p80mhz_nss; + uint8_t vht_extended_nss_bw_cap; + uint8_t vhtTxBFCapable; + uint8_t enable_su_tx_bformer; + uint8_t vhtTxMUBformeeCapable; + uint8_t enableVhtpAid; + uint8_t enableAmpduPs; + uint8_t enableHtSmps; + uint8_t htSmpsconfig; + bool send_smps_action; + uint8_t htLdpcCapable; + uint8_t vhtLdpcCapable; + uint8_t vht_mcs_10_11_supp; + uint8_t smesessionId; + uint8_t wpa_rsn; + uint16_t capab_info; + uint16_t ht_caps; + uint32_t vht_caps; + tSirNwType nwType; + int8_t maxTxPower; + uint8_t atimIePresent; + uint32_t peerAtimWindowLength; + uint8_t nonRoamReassoc; + uint32_t nss; +#ifdef WLAN_FEATURE_11AX + bool he_capable; + tDot11fIEhe_cap he_config; + tDot11fIEhe_op he_op; + tDot11fIEhe_6ghz_band_cap he_6ghz_band_caps; +#endif + uint8_t stbc_capable; +#ifdef WLAN_SUPPORT_TWT + uint8_t twt_requestor; + uint8_t twt_responder; +#endif + bool no_ptk_4_way; +} tAddStaParams, *tpAddStaParams; + +/** + * struct tDeleteStaParams - parameters required for del sta request + * @assocId: association index + * @status: status + * @respReqd: is response required + * @sessionId: PE session id + * @smesessionId: SME session id + * @staType: station type + * @staMac: station mac + */ +typedef struct { + uint16_t assocId; + QDF_STATUS status; + uint8_t respReqd; + uint8_t sessionId; + uint8_t smesessionId; + uint8_t staType; + tSirMacAddr staMac; +} tDeleteStaParams, *tpDeleteStaParams; + +/** + * struct tSetStaKeyParams - set key params + * @staIdx: station id + * @encType: encryption type + * @defWEPIdx: Default WEP key, valid only for static WEP, must between 0 and 3 + * @key: valid only for non-static WEP encyrptions + * @singleTidRc: 1=Single TID based Replay Count, 0=Per TID based RC + * @smesessionId: sme session id + * @peerMacAddr: peer mac address + * @status: status + * @sendRsp: send response + * @macaddr: MAC address of the peer + * + * This is used by PE to configure the key information on a given station. + * When the secType is WEP40 or WEP104, the defWEPIdx is used to locate + * a preconfigured key from a BSS the station assoicated with; otherwise + * a new key descriptor is created based on the key field. + */ +typedef struct { + tAniEdType encType; + uint8_t defWEPIdx; + tSirKeys key[SIR_MAC_MAX_NUM_OF_DEFAULT_KEYS]; + uint8_t singleTidRc; + uint8_t vdev_id; + struct qdf_mac_addr peer_macaddr; + QDF_STATUS status; + uint8_t sendRsp; + struct qdf_mac_addr macaddr; +} tSetStaKeyParams, *tpSetStaKeyParams; + +/** + * struct sLimMlmSetKeysReq - set key request parameters + * @peerMacAddr: peer mac address + * @sessionId: PE session id + * @vdev_id: vdev id + * @aid: association id + * @edType: Encryption/Decryption type + * @numKeys: number of keys + * @key: key data + */ +typedef struct sLimMlmSetKeysReq { + struct qdf_mac_addr peer_macaddr; + uint8_t sessionId; /* Added For BT-AMP Support */ + uint8_t vdev_id; /* Added for drivers based on wmi interface */ + uint16_t aid; + tAniEdType edType; /* Encryption/Decryption type */ + uint8_t numKeys; + tSirKeys key[SIR_MAC_MAX_NUM_OF_DEFAULT_KEYS]; +} tLimMlmSetKeysReq, *tpLimMlmSetKeysReq; + +/** + * struct struct bss_params - parameters required for add bss params + * @bssId: MAC Address/BSSID + * @nwType: network type + * @shortSlotTimeSupported: is short slot time supported or not + * @llbCoexist: 11b coexist supported or not + * @beaconInterval: beacon interval + * @dtimPeriod: DTIM period + * @htCapable: Enable/Disable HT capabilities + * @rmfEnabled: RMF enabled/disabled + * @staContext: sta context + * @updateBss: update the existing BSS entry, if this flag is set + * @maxTxPower: max power to be used after applying the power constraint + * @extSetStaKeyParamValid: Ext Bss Config Msg if set + * @extSetStaKeyParam: SetStaKeyParams for ext bss msg + * @bSpectrumMgtEnabled: Spectrum Management Capability, 1:Enabled, 0:Disabled. + * @vhtCapable: VHT capablity + * @ch_width: VHT tx channel width + * @he_capable: HE Capability + * @no_ptk_4_way: Do not need 4-way handshake + */ +struct bss_params { + tSirMacAddr bssId; + tSirNwType nwType; + uint8_t shortSlotTimeSupported; + uint8_t llbCoexist; + tSirMacBeaconInterval beaconInterval; + uint8_t dtimPeriod; + uint8_t htCapable; + uint8_t rmfEnabled; + tAddStaParams staContext; + /* HAL should update the existing BSS entry, if this flag is set. + * PE will set this flag in case of reassoc, where we want to resue the + * the old bssID and still return success. + */ + uint8_t updateBss; + int8_t maxTxPower; + + uint8_t extSetStaKeyParamValid; + tSetStaKeyParams extSetStaKeyParam; + uint8_t vhtCapable; + enum phy_ch_width ch_width; + uint8_t nonRoamReassoc; +#ifdef WLAN_FEATURE_11AX + bool he_capable; + uint32_t he_sta_obsspd; +#endif + bool no_ptk_4_way; +}; + +/** + * struct add_bss_rsp - params required for add bss response + * @vdev_id: vdev_id + * @status: QDF status + * @chain_mask: chain mask vdev start resp + * @smps_mode: smps mode in vdev start resp + */ +struct add_bss_rsp { + uint8_t vdev_id; + QDF_STATUS status; + uint32_t chain_mask; + uint8_t smps_mode; +}; + +/** + * struct struct del_bss_resp - params required for del bss response + * @status: QDF status + * @vdev_id: vdev_id + */ +struct del_bss_resp { + QDF_STATUS status; + uint8_t vdev_id; +}; + +typedef enum eDelStaReasonCode { + HAL_DEL_STA_REASON_CODE_KEEP_ALIVE = 0x1, + HAL_DEL_STA_REASON_CODE_TIM_BASED = 0x2, + HAL_DEL_STA_REASON_CODE_RA_BASED = 0x3, + HAL_DEL_STA_REASON_CODE_UNKNOWN_A2 = 0x4, + HAL_DEL_STA_REASON_CODE_BTM_DISASSOC_IMMINENT = 0x5, + HAL_DEL_STA_REASON_CODE_SA_QUERY_TIMEOUT = 0x6, + HAL_DEL_STA_REASON_CODE_XRETRY = 0x7, +} tDelStaReasonCode; + +typedef enum eSmpsModeValue { + STATIC_SMPS_MODE = 0x0, + DYNAMIC_SMPS_MODE = 0x1, + SMPS_MODE_RESERVED = 0x2, + SMPS_MODE_DISABLED = 0x3 +} tSmpsModeValue; + +/** + * struct tDeleteStaContext - params required for delete sta request + * @assocId: association id + * @bssId: mac address + * @addr2: mac address + * @reasonCode: reason code + * @rssi: rssi value during disconnection + */ +typedef struct { + bool is_tdls; + uint8_t vdev_id; + uint16_t assocId; + tSirMacAddr bssId; + tSirMacAddr addr2; + uint16_t reasonCode; + int8_t rssi; +} tDeleteStaContext, *tpDeleteStaContext; + +#ifdef FEATURE_OEM_DATA_SUPPORT + +#ifndef OEM_DATA_RSP_SIZE +#define OEM_DATA_RSP_SIZE 1724 +#endif + +/** + * struct tStartOemDataRsp - start OEM Data response + * @target_rsp: Indicates if the rsp is from Target or WMA generated. + * @rsp_len: oem data response length + * @oem_data_rsp: pointer to OEM Data response + */ +typedef struct { + bool target_rsp; + uint32_t rsp_len; + uint8_t *oem_data_rsp; +} tStartOemDataRsp, *tpStartOemDataRsp; +#endif /* FEATURE_OEM_DATA_SUPPORT */ + +/** + * struct beacon_gen_params - params required for beacon gen request + * @bssdd: BSSID for which it is time to generate a beacon + */ +struct beacon_gen_params { + tSirMacAddr bssid; +}; + +/** + * struct tSendbeaconParams - send beacon parameters + * vdev_id: vdev id + * @bssId: BSSID mac address + * @beacon: beacon data + * @beaconLength: beacon length of template + * @timIeOffset: TIM IE offset + * @p2pIeOffset: P2P IE offset + * @csa_count_offset: Offset of Switch count field in CSA IE + * @ecsa_count_offset: Offset of Switch count field in ECSA IE + * @reason: bcn update reason + * @status: beacon send status + */ +typedef struct { + uint8_t vdev_id; + tSirMacAddr bssId; + uint8_t beacon[SIR_MAX_BEACON_SIZE]; + uint32_t beaconLength; + uint32_t timIeOffset; + uint16_t p2pIeOffset; + uint32_t csa_count_offset; + uint32_t ecsa_count_offset; + enum sir_bcn_update_reason reason; + QDF_STATUS status; +} tSendbeaconParams, *tpSendbeaconParams; + +/** + * struct tSendProbeRespParams - send probe response parameters + * @bssId: BSSID + * @probeRespTemplate: probe response template + * @probeRespTemplateLen: probe response template length + * @ucProxyProbeReqValidIEBmap: valid IE bitmap + */ +typedef struct sSendProbeRespParams { + tSirMacAddr bssId; + uint8_t probeRespTemplate[SIR_MAX_PROBE_RESP_SIZE]; + uint32_t probeRespTemplateLen; + uint32_t ucProxyProbeReqValidIEBmap[8]; +} tSendProbeRespParams, *tpSendProbeRespParams; + +/** + * struct tSetBssKeyParams - BSS key parameters + * @encType: encryption Type + * @numKeys: number of keys + * @key: key data + * @singleTidRc: 1=Single TID based Replay Count, 0=Per TID based RC + * @vdev_id: vdev id id + * @status: return status of command + * @macaddr: MAC address of the peer + */ +typedef struct { + tAniEdType encType; + uint8_t numKeys; + tSirKeys key[SIR_MAC_MAX_NUM_OF_DEFAULT_KEYS]; + uint8_t singleTidRc; + uint8_t vdev_id; + QDF_STATUS status; + struct qdf_mac_addr macaddr; +} tSetBssKeyParams, *tpSetBssKeyParams; + +/** + * struct tUpdateBeaconParams - update beacon request parameters + * @bss_idx: BSSID index + * @fShortPreamble: shortPreamble mode + * @fShortSlotTime: short Slot time + * @beaconInterval: Beacon Interval + * @llaCoexist: 11a coexist + * @llbCoexist: 11b coexist + * @llgCoexist: 11g coexist + * @ht20MhzCoexist: HT 20MHz coexist + * @fLsigTXOPProtectionFullSupport: TXOP protection supported or not + * @fRIFSMode: RIFS mode + * @paramChangeBitmap: change bitmap + * @vdev_id: vdev_id + */ +typedef struct { + uint8_t bss_idx; + uint8_t fShortPreamble; + uint8_t fShortSlotTime; + uint16_t beaconInterval; + uint8_t llaCoexist; + uint8_t llbCoexist; + uint8_t llgCoexist; + uint8_t ht20MhzCoexist; + uint8_t llnNonGFCoexist; + uint8_t fLsigTXOPProtectionFullSupport; + uint8_t fRIFSMode; + uint16_t paramChangeBitmap; + uint8_t vdev_id; + uint32_t bss_color; + bool bss_color_disabled; +} tUpdateBeaconParams, *tpUpdateBeaconParams; + +/** + * struct tUpdateVHTOpMode - VHT operating mode + * @opMode: VHT operating mode + * @staId: station id + * @smesessionId: SME session id + * @peer_mac: peer mac address + */ +typedef struct { + uint16_t opMode; + uint16_t smesessionId; + tSirMacAddr peer_mac; +} tUpdateVHTOpMode, *tpUpdateVHTOpMode; + +/** + * struct tUpdateRxNss - update rx nss parameters + * @rxNss: rx nss value + * @staId: station id + * @smesessionId: sme session id + * @peer_mac: peer mac address + */ +typedef struct { + uint16_t rxNss; + uint16_t smesessionId; + tSirMacAddr peer_mac; +} tUpdateRxNss, *tpUpdateRxNss; + +/** + * struct tUpdateMembership - update membership parmaters + * @membership: membership value + * @staId: station id + * @smesessionId: SME session id + * @peer_mac: peer mac address + */ +typedef struct { + uint32_t membership; + uint16_t smesessionId; + tSirMacAddr peer_mac; +} tUpdateMembership, *tpUpdateMembership; + +/** + * struct tUpdateUserPos - update user position parmeters + * @userPos: user position + * @staId: station id + * @smesessionId: sme session id + * @peer_mac: peer mac address + */ +typedef struct { + uint32_t userPos; + uint16_t smesessionId; + tSirMacAddr peer_mac; +} tUpdateUserPos, *tpUpdateUserPos; + +/** + * struct tEdcaParams - EDCA parameters + * @vdev_id: vdev id + * @acbe: best effort access category + * @acbk: Background access category + * @acvi: video access category + * @acvo: voice access category + * @mu_edca_params: flag to indicate MU EDCA + */ +typedef struct { + uint16_t vdev_id; + tSirMacEdcaParamRecord acbe; + tSirMacEdcaParamRecord acbk; + tSirMacEdcaParamRecord acvi; + tSirMacEdcaParamRecord acvo; + bool mu_edca_params; +} tEdcaParams, *tpEdcaParams; + +/** + * struct tSetMIMOPS - MIMO power save related parameters + * @htMIMOPSState: MIMO Power Save State + * @status: response status + * @fsendRsp: send response flag + * @peerMac: peer mac address + * @sessionId: session id + */ +typedef struct sSet_MIMOPS { + tSirMacHTMIMOPowerSaveState htMIMOPSState; + QDF_STATUS status; + uint8_t fsendRsp; + tSirMacAddr peerMac; + uint8_t sessionId; +} tSetMIMOPS, *tpSetMIMOPS; + +/** + * struct tMaxTxPowerParams - Max Tx Power parameters + * @bssId: BSSID is needed to identify which session issued this request + * @selfStaMacAddr: self mac address + * @power: tx power in dbm + * @dev_mode: device mode + * Request Type = SIR_HAL_SET_MAX_TX_POWER_REQ + */ +typedef struct sMaxTxPowerParams { + struct qdf_mac_addr bssId; + struct qdf_mac_addr selfStaMacAddr; + /* In request, + * power == MaxTx power to be used. + * In response, + * power == tx power used for management frames. + */ + int8_t power; + enum QDF_OPMODE dev_mode; +} tMaxTxPowerParams, *tpMaxTxPowerParams; + +/** + * struct tMaxTxPowerPerBandParams - max tx power per band info + * @bandInfo: band info + * @power: power in dbm + */ +typedef struct sMaxTxPowerPerBandParams { + enum band_info bandInfo; + int8_t power; +} tMaxTxPowerPerBandParams, *tpMaxTxPowerPerBandParams; + +/** + * struct vdev_create_req_param - vdev create request params + * @vdev_id: vdev_id + * @status: response status code + */ +struct vdev_create_req_param { + uint32_t vdev_id; + QDF_STATUS status; +}; + +/** + * struct set_ie_param - set IE params structure + * @pdev_id: pdev id + * @ie_type: IE type + * @nss: Nss value + * @ie_len: IE length + * @ie_ptr: Pointer to IE data + * + * Holds the set pdev IE req data. + */ +struct set_ie_param { + uint8_t pdev_id; + uint8_t ie_type; + uint8_t nss; + uint8_t ie_len; + uint8_t *ie_ptr; +}; + +/** + * struct set_dtim_params - dtim params + * @session_id: SME Session ID + * @dtim_period: dtim period + */ +struct set_dtim_params { + uint8_t session_id; + uint8_t dtim_period; +}; + +#define DOT11_HT_IE 1 +#define DOT11_VHT_IE 2 + +/** + * struct del_vdev_params - Del Sta Self params + * @session_id: SME Session ID + * @status: response status code + * @sme_callback: callback to be called from WMA to SME + * @sme_ctx: pointer to context provided by SME + */ +struct del_vdev_params { + tSirMacAddr self_mac_addr; + uint8_t vdev_id; + uint32_t status; + csr_session_close_cb sme_callback; + void *sme_ctx; +}; + +/** + * struct del_sta_self_rsp_params - Del Sta Self response params + * @self_sta_param: sta params + * @generate_rsp: generate response to upper layers + */ +struct del_sta_self_rsp_params { + struct del_vdev_params *self_sta_param; +}; + +/** + * struct send_peer_unmap_conf_params - Send Peer Unmap Conf param + * @vdev_id: vdev ID + * @peer_id_cnt: peer_id count + * @peer_id_list: list of peer IDs + */ +struct send_peer_unmap_conf_params { + uint8_t vdev_id; + uint32_t peer_id_cnt; + uint16_t *peer_id_list; +}; + +/** + * struct tDisableIntraBssFwd - intra bss forward parameters + * @sessionId: session id + * @disableintrabssfwd: disable intra bss forward flag + */ +typedef struct sDisableIntraBssFwd { + uint16_t sessionId; + bool disableintrabssfwd; +} qdf_packed tDisableIntraBssFwd, *tpDisableIntraBssFwd; + +#ifdef WLAN_FEATURE_STATS_EXT +/** + * struct tStatsExtRequest - ext stats request + * @vdev_id: vdev id + * @request_data_len: request data length + * @request_data: request data + */ +typedef struct sStatsExtRequest { + uint32_t vdev_id; + uint32_t request_data_len; + uint8_t request_data[]; +} tStatsExtRequest, *tpStatsExtRequest; +#endif /* WLAN_FEATURE_STATS_EXT */ + +/* + * struct roam_blacklist_timeout - BTM blacklist entry + * @bssid - bssid that is to be blacklisted + * @timeout - time duration for which the bssid is blacklisted + * @received_time - boot timestamp at which the firmware event was received + * @rssi - rssi value for which the bssid is blacklisted + * @reject_reason: reason to add the BSSID to BLM + * @original_timeout: original timeout sent by the AP + * @source: Source of adding the BSSID to BLM + */ +struct roam_blacklist_timeout { + struct qdf_mac_addr bssid; + uint32_t timeout; + qdf_time_t received_time; + int32_t rssi; + enum blm_reject_ap_reason reject_reason; + uint32_t original_timeout; + enum blm_reject_ap_source source; +}; + +/* + * struct roam_blacklist_event - Blacklist event entries destination structure + * @num_entries: total entries sent over the event + * @roam_blacklist: blacklist details + */ +struct roam_blacklist_event { + uint32_t num_entries; + struct roam_blacklist_timeout roam_blacklist[]; +}; + +/* + * struct roam_pmkid_req_event - Pmkid event with entries destination structure + * @num_entries: total entries sent over the event + * @ap_bssid: bssid list + */ +struct roam_pmkid_req_event { + uint32_t num_entries; + struct qdf_mac_addr ap_bssid[]; +}; + +#endif /* _HALMSGAPI_H_ */ diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma_internal.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..7943cc4b0adf27d5e41836017c0fa8dae8224160 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma_internal.h @@ -0,0 +1,1997 @@ +/* + * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WMA_INTERNAL_H +#define WMA_INTERNAL_H +#include +#if !defined(REMOVE_PKT_LOG) +#include "pktlog_ac.h" +#endif + +/* ################### defines ################### */ +/* + * TODO: Following constant should be shared by firwmare in + * wmi_unified.h. This will be done once wmi_unified.h is updated. + */ +#define WMI_PEER_STATE_AUTHORIZED 0x2 + +#define WMA_2_4_GHZ_MAX_FREQ 3000 + +/*AR9888/AR6320 noise floor approx value + * similar to the mentioned the WMA + */ +#define WMA_TGT_NOISE_FLOOR_DBM (-96) +#define WMA_INVALID_PER_CHAIN_SNR (0x80) +#define WMA_INVALID_PER_CHAIN_RSSI (0xFF) + +/* + * Make sure that link monitor and keep alive + * default values should be in sync with CFG. + */ +#define WMA_LINK_MONITOR_DEFAULT_TIME_SECS 10 +#define WMA_KEEP_ALIVE_DEFAULT_TIME_SECS 5 + +/* The maximum number of patterns that can be transmitted by the firmware + * and maximum patterns size. + */ +#ifndef WMA_MAXNUM_PERIODIC_TX_PTRNS +#define WMA_MAXNUM_PERIODIC_TX_PTRNS 6 +#endif + +#define WMA_WMM_EXPO_TO_VAL(val) ((1 << (val)) - 1) + +#define MAX_HT_MCS_IDX 8 +#define MAX_VHT_MCS_IDX 10 +#ifdef WLAN_FEATURE_11AX +#define MAX_HE_MCS_IDX 12 +#endif +#define INVALID_MCS_IDX 255 + +#define IS_MCS_HAS_DCM_RATE(val) \ + ((val) == 0 || (val) == 1 || \ + (val) == 3 || (val) == 4) + +#define LINK_STATUS_LEGACY 0 +#define LINK_STATUS_VHT 0x1 +#define LINK_STATUS_MIMO 0x2 +#define LINK_SUPPORT_VHT 0x4 +#define LINK_SUPPORT_MIMO 0x8 + +#define LINK_RATE_VHT 0x3 + +#define MAX_ENTRY_HOLD_REQ_QUEUE 2 +#define MAX_ENTRY_VDEV_RESP_QUEUE 10 + +/** + * struct index_data_rate_type - non vht data rate type + * @mcs_index: mcs rate index + * @ht20_rate: HT20 supported rate table + * @ht40_rate: HT40 supported rate table + */ +struct index_data_rate_type { + uint8_t mcs_index; + uint16_t ht20_rate[2]; + uint16_t ht40_rate[2]; +}; + +/** + * struct index_vht_data_rate_type - vht data rate type + * @mcs_index: mcs rate index + * @ht20_rate: VHT20 supported rate table + * @ht40_rate: VHT40 supported rate table + * @ht80_rate: VHT80 supported rate table + * @ht160_rate: VHT160 supported rate table + */ +struct index_vht_data_rate_type { + uint8_t mcs_index; + uint16_t ht20_rate[2]; + uint16_t ht40_rate[2]; + uint16_t ht80_rate[2]; + uint16_t ht160_rate[2]; +}; + +#ifdef WLAN_FEATURE_11AX +#define MAX_HE_DCM_INDEX 2 +/** + * struct index_he_data_rate_type - he data rate type + * @beacon_rate_index: Beacon rate index + * @supported_he80_rate: he80 rate + * @supported_he40_rate: he40 rate + * @supported_he20_rate: he20 rate + * @supported_he160_rate: he160 rate + */ +struct index_he_data_rate_type { + uint8_t beacon_rate_index; + uint16_t supported_he20_rate[MAX_HE_DCM_INDEX][3]; + uint16_t supported_he40_rate[MAX_HE_DCM_INDEX][3]; + uint16_t supported_he80_rate[MAX_HE_DCM_INDEX][3]; + uint16_t supported_he160_rate[MAX_HE_DCM_INDEX][3]; +}; +#endif + +struct wifi_scan_cmd_req_params; +/* + * wma_main.c functions declarations + */ + +/** + * wma_send_msg_by_priority() - Send wma message to PE with priority. + * @wma_handle: wma handle + * @msg_type: message type + * @body_ptr: message body ptr + * @body_val: message body value + * @is_high_priority: if msg is high priority + * + * Return: none + */ +void wma_send_msg_by_priority(tp_wma_handle wma_handle, uint16_t msg_type, + void *body_ptr, uint32_t body_val, bool is_high_priority); + +/** + * wma_send_msg() - Send wma message to PE. + * @wma_handle: wma handle + * @msg_type: message type + * @body_ptr: message body ptr + * @body_val: message body value + * + * Return: none + */ +void wma_send_msg(tp_wma_handle wma_handle, uint16_t msg_type, + void *body_ptr, uint32_t body_val); + +/** + * wma_send_msg_high_priority() - Send wma message to PE with high priority. + * @wma_handle: wma handle + * @msg_type: message type + * @body_ptr: message body ptr + * @body_val: message body value + * + * Return: none + */ +void wma_send_msg_high_priority(tp_wma_handle wma_handle, uint16_t msg_type, + void *body_ptr, uint32_t body_val); + +void wma_data_tx_ack_comp_hdlr(void *wma_context, + qdf_nbuf_t netbuf, int32_t status); + +QDF_STATUS wma_set_ppsconfig(uint8_t vdev_id, uint16_t pps_param, + int value); + +/* + * wma_scan_roam.c functions declarations + */ + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +void wma_process_roam_invoke(WMA_HANDLE handle, + struct wma_roam_invoke_cmd *roaminvoke); + +void wma_process_roam_synch_fail(WMA_HANDLE handle, + struct roam_offload_synch_fail *synch_fail); + +int wma_roam_synch_event_handler(void *handle, uint8_t *event, + uint32_t len); + +#ifdef WLAN_FEATURE_FIPS +/** + * wma_register_pmkid_req_event_handler() - Register pmkid request event handler + * @wma_handle: wma_handle + * + * This function register pmkid request event handler. + */ +void wma_register_pmkid_req_event_handler(tp_wma_handle wma_handle); + +/** + * wma_roam_pmkid_request_event_handler() - Handles roam pmkid request event + * @handle: wma_handle + * @event: pmkid request event data pointer + * @len: length of the data + * + * Handles pmkid request event from firmware which is triggered after roam + * candidate selection. + */ +int wma_roam_pmkid_request_event_handler(void *handle, + uint8_t *event, + uint32_t len); +#else +static inline void +wma_register_pmkid_req_event_handler(tp_wma_handle wma_handle) +{ +} + +static inline int +wma_roam_pmkid_request_event_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + return 0; +} +#endif /* WLAN_FEATURE_FIPS */ + +/** + * wma_roam_auth_offload_event_handler() - Handle LFR-3.0 Roam authentication + * offload event. + * @handle: wma_handle + * @event: rso auth offload event data pointer + * @len: length of the data + * + * Handles roam authentication offload event from firmware which is triggered + * after roam candidate selection. + */ +int wma_roam_auth_offload_event_handler(WMA_HANDLE handle, uint8_t *event, + uint32_t len); + +/** + * wma_roam_stats_event_handler() - Handle the WMI_ROAM_STATS_EVENTID + * from target + * @handle: wma_handle + * @event: roam debug stats event data pointer + * @len: length of the data + * + * This function handles the roam debug stats from the target and logs it + * to kmsg. This WMI_ROAM_STATS_EVENTID event is received whenever roam + * scan trigger happens or when neighbor report is sent by the firmware. + * + * Return: Success or Failure status + */ +int wma_roam_stats_event_handler(WMA_HANDLE handle, uint8_t *event, + uint32_t len); + +/** + * wma_mlme_roam_synch_event_handler_cb() - roam synch event handler + * @handle: wma handle + * @event: event data + * @len: length of data + * + * This function is roam synch event handler. It sends roam + * indication for upper layer. + * + * Return: Success or Failure status + */ +int wma_mlme_roam_synch_event_handler_cb(void *handle, uint8_t *event, + uint32_t len); + +/** + * wma_roam_synch_frame_event_handler() - roam synch frame event handler + * @handle: wma handle + * @event: event data + * @len: length of data + * + * This function is roam synch frame event handler. + * + * Return: Success or Failure status + */ +int wma_roam_synch_frame_event_handler(void *handle, uint8_t *event, + uint32_t len); +#else +static inline int wma_mlme_roam_synch_event_handler_cb(void *handle, + uint8_t *event, + uint32_t len) +{ + return 0; +} + +static inline int +wma_roam_stats_event_handler(WMA_HANDLE handle, uint8_t *event, + uint32_t len) +{ + return 0; +} + +static inline int +wma_roam_pmkid_request_event_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + return 0; +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * wma_roam_scan_chan_list_event_handler() - roam scan chan list event handler + * @handle: wma handle + * @event: pointer to fw event + * @len: length of event + * + * Return: Success or Failure status + */ +int wma_roam_scan_chan_list_event_handler(WMA_HANDLE handle, + uint8_t *event, + uint32_t len); +#else +static inline int +wma_roam_scan_chan_list_event_handler(WMA_HANDLE handle, uint8_t *event, + uint32_t len) +{ + return 0; +} +#endif + +/** + * wma_update_per_roam_config() -per roam config parameter updation to FW + * @handle: wma handle + * @req_buf: per roam config parameters + * + * Return: none + */ +void wma_update_per_roam_config(WMA_HANDLE handle, + struct wmi_per_roam_config_req *req_buf); + +QDF_STATUS wma_update_channel_list(WMA_HANDLE handle, + tSirUpdateChanList *chan_list); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS wma_roam_scan_fill_self_caps(tp_wma_handle wma_handle, + roam_offload_param * + roam_offload_params, + struct roam_offload_scan_req *roam_req); +#endif + +QDF_STATUS wma_roam_scan_offload_mode(tp_wma_handle wma_handle, + wmi_start_scan_cmd_fixed_param * + scan_cmd_fp, + struct roam_offload_scan_req *roam_req, + uint32_t mode, uint32_t vdev_id); + +/** + * wma_roam_scan_mawc_params() - send roam scan mode request to fw + * @wma_handle: wma handle + * @roam_req: roam request param + * + * Fill the MAWC roaming parameters and send + * WMI_ROAM_CONFIGURE_MAWC_CMDID TLV to firmware. + * + * Return: QDF status + */ +QDF_STATUS wma_roam_scan_mawc_params(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req); + +/** + * wma_roam_scan_offload_rssi_threshold() - set scan offload rssi threashold + * @wma_handle: wma handle + * @roam_req: Roaming request buffer + * + * Send WMI_ROAM_SCAN_RSSI_THRESHOLD TLV to firmware + * + * Return: QDF status + */ +QDF_STATUS +wma_roam_scan_offload_rssi_thresh(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req); + +QDF_STATUS +wma_roam_scan_offload_scan_period(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req); + +QDF_STATUS wma_roam_scan_offload_rssi_change(tp_wma_handle wma_handle, + uint32_t vdev_id, + int32_t rssi_change_thresh, + uint32_t bcn_rssi_weight, + uint32_t hirssi_delay_btw_scans); + +QDF_STATUS wma_roam_scan_offload_chan_list(tp_wma_handle wma_handle, + uint8_t chan_count, + uint32_t *chan_freq_list, + uint8_t list_type, uint32_t vdev_id); + +A_UINT32 e_csr_auth_type_to_rsn_authmode(enum csr_akm_type authtype, + eCsrEncryptionType encr); + +A_UINT32 e_csr_encryption_type_to_rsn_cipherset(eCsrEncryptionType encr); + +void wma_roam_scan_fill_scan_params(tp_wma_handle wma_handle, + struct mac_context *mac, + struct roam_offload_scan_req *roam_req, + wmi_start_scan_cmd_fixed_param * + scan_params); + +QDF_STATUS wma_roam_scan_bmiss_cnt(tp_wma_handle wma_handle, + A_INT32 first_bcnt, + A_UINT32 final_bcnt, uint32_t vdev_id); + +QDF_STATUS wma_roam_scan_offload_command(tp_wma_handle wma_handle, + uint32_t command, uint32_t vdev_id); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +void wma_set_ric_req(tp_wma_handle wma, void *msg, uint8_t is_add_ts); +#endif + +#ifdef FEATURE_WLAN_EXTSCAN + +int wma_extscan_start_stop_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +int wma_extscan_operations_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +int wma_extscan_table_usage_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +int wma_extscan_capabilities_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +int wma_extscan_hotlist_match_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +int wma_extscan_cached_results_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +int wma_extscan_change_results_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +int wma_passpoint_match_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +#endif + +int wma_handle_btm_blacklist_event(void *handle, uint8_t *cmd_param_info, + uint32_t len); + +#ifdef FEATURE_WLAN_EXTSCAN +int wma_extscan_wow_event_callback(void *handle, void *event, uint32_t len); + +void wma_register_extscan_event_handler(tp_wma_handle wma_handle); + +/** + * wma_start_extscan() - start extscan command to fw. + * @wma: wma handle + * @params: extscan command request params + * + * This function sends start extscan request to fw. + * + * Return: QDF Status. + */ +QDF_STATUS wma_start_extscan(tp_wma_handle wma, + struct wifi_scan_cmd_req_params *pstart); + +/** + * wma_stop_extscan() - stop extscan command to fw. + * @wma: wma handle + * @params: stop scan command request params + * + * This function sends stop extscan request to fw. + * + * Return: QDF Status. + */ +QDF_STATUS wma_stop_extscan(tp_wma_handle wma, + struct extscan_stop_req_params *params); + +/** + * wma_extscan_start_hotlist_monitor() - start hotlist monitor + * @wma: wma handle + * @params: hotlist request params + * + * This function configures hotlist monitor in fw. + * + * Return: QDF status + */ +QDF_STATUS wma_extscan_start_hotlist_monitor(tp_wma_handle wma, + struct extscan_bssid_hotlist_set_params *params); + +/** + * wma_extscan_stop_hotlist_monitor() - stop hotlist monitor + * @wma: wma handle + * @params: hotlist request params + * + * This function configures hotlist monitor to stop in fw. + * + * Return: QDF status + */ +QDF_STATUS wma_extscan_stop_hotlist_monitor(tp_wma_handle wma, + struct extscan_bssid_hotlist_reset_params *params); + +/** + * wma_extscan_start_change_monitor() - send start change monitor cmd + * @wma: wma handle + * @params: change monitor request params + * + * This function sends start change monitor request to fw. + * + * Return: QDF status + */ +QDF_STATUS +wma_extscan_start_change_monitor(tp_wma_handle wma, + struct extscan_set_sig_changereq_params *params); + +/** + * wma_extscan_stop_change_monitor() - send stop change monitor cmd + * @wma: wma handle + * @params: change monitor request params + * + * This function sends stop change monitor request to fw. + * + * Return: QDF status + */ +QDF_STATUS +wma_extscan_stop_change_monitor(tp_wma_handle wma, + struct extscan_capabilities_reset_params *params); + +/** + * wma_extscan_get_cached_results() - extscan get cached results + * @wma: wma handle + * @params: cached results parameters + * + * This function send request to fw to get cached results. + * + * Return: QDF status + */ +QDF_STATUS +wma_extscan_get_cached_results(tp_wma_handle wma, + struct extscan_cached_result_params *params); + +/** + * wma_extscan_get_capabilities() - extscan get capabilities + * @wma: wma handle + * @params: get capabilities params + * + * This function sends request to fw to get extscan capabilities. + * + * Return: QDF status + */ +QDF_STATUS +wma_extscan_get_capabilities(tp_wma_handle wma, + struct extscan_capabilities_params *params); + +/** + * wma_set_epno_network_list() - set epno network list + * @wma: WMA handle + * @req: epno config params request structure + * + * This function reads the incoming epno config request structure + * and constructs the WMI message to the firmware. + * + * Return: 0 on success, error number otherwise + */ +QDF_STATUS wma_set_epno_network_list(tp_wma_handle wma, + struct wifi_enhanced_pno_params *req); + +/** + * wma_set_passpoint_network_list() - set passpoint network list + * @wma: WMA handle + * @params: passpoint network request structure + * + * This function sends the passpoint configs down to the firmware + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS +wma_set_passpoint_network_list(tp_wma_handle wma, + struct wifi_passpoint_req_param *params); + +/** + * wma_reset_passpoint_network_list() - reset passpoint network list + * @wma: WMA handle + * @params: passpoint network request structure + * + * This function sends down WMI command with network id set to wildcard id. + * firmware shall clear all the config entries + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS +wma_reset_passpoint_network_list(tp_wma_handle wma, + struct wifi_passpoint_req_param *params); +#endif + +/** + * wma_scan_probe_setoui() - set scan probe OUI + * @wma: wma handle + * @set_oui: OUI parameters + * + * set scan probe OUI parameters in firmware + * + * Return: QDF status + */ +QDF_STATUS wma_scan_probe_setoui(tp_wma_handle wma, + struct scan_mac_oui *set_oui); + +void wma_roam_better_ap_handler(tp_wma_handle wma, uint32_t vdev_id); + +int wma_roam_event_callback(WMA_HANDLE handle, uint8_t *event_buf, + uint32_t len); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +void wma_process_roam_synch_complete(WMA_HANDLE handle, uint8_t vdev_id); +static inline bool wma_is_roam_synch_in_progress(tp_wma_handle wma, + uint8_t vdev_id) +{ + return wma->interfaces[vdev_id].roam_synch_in_progress; +} +#else +static inline bool wma_is_roam_synch_in_progress(tp_wma_handle wma, + uint8_t vdev_id) +{ + return false; +} +#endif + +/* + * wma_dev_if.c functions declarations + */ + +/** + * wma_find_vdev_id_by_addr() - find vdev_id from mac address + * @wma: wma handle + * @addr: mac address + * @vdev_id: return vdev_id + * + * Return: SUCCESS or FAILURE + */ +QDF_STATUS wma_find_vdev_id_by_addr(tp_wma_handle wma, uint8_t *addr, + uint8_t *vdev_id); + +bool wma_is_vdev_in_ap_mode(tp_wma_handle wma, uint8_t vdev_id); + +#ifdef QCA_IBSS_SUPPORT +/** + * wma_is_vdev_in_ibss_mode() - check that vdev is in ibss mode or not + * @wma: wma handle + * @vdev_id: vdev id + * + * Helper function to know whether given vdev id + * is in IBSS mode or not. + * + * Return: True/False + */ +bool wma_is_vdev_in_ibss_mode(tp_wma_handle wma, uint8_t vdev_id); + +/** + * wma_adjust_ibss_heart_beat_timer() - set ibss heart beat timer in fw. + * @wma: wma handle + * @vdev_id: vdev id + * @peer_num_delta: peer number delta value + * + * Return: none + */ +void wma_adjust_ibss_heart_beat_timer(tp_wma_handle wma, + uint8_t vdev_id, + int8_t peer_num_delta); + +/** + * wma_set_ibss_pwrsave_params() - set ibss power save parameter to fw + * @wma: wma handle + * @vdev_id: vdev id + * + * Return: 0 for success or error code. + */ +QDF_STATUS +wma_set_ibss_pwrsave_params(tp_wma_handle wma, uint8_t vdev_id); + +/** + * wma_ibss_peer_info_event_handler() - IBSS peer info event handler + * @handle: wma handle + * @data: event data + * @len: length of data + * + * This function handles IBSS peer info event from FW. + * + * Return: 0 for success or error code + */ +int wma_ibss_peer_info_event_handler(void *handle, uint8_t *data, + uint32_t len); +#else +/** + * wma_is_vdev_in_ibss_mode(): dummy function + * @wma: wma handle + * @vdev_id: vdev id + * + * Return false since no vdev can be in ibss mode without ibss support + */ +static inline +bool wma_is_vdev_in_ibss_mode(tp_wma_handle wma, uint8_t vdev_id) +{ + return false; +} + +/** + * wma_adjust_ibss_heart_beat_timer() - set ibss heart beat timer in fw. + * @wma: wma handle + * @vdev_id: vdev id + * @peer_num_delta: peer number delta value + * + * This function is dummy + * + * Return: none + */ +static inline void +wma_adjust_ibss_heart_beat_timer(tp_wma_handle wma, + uint8_t vdev_id, + int8_t peer_num_delta) +{ +} + +/** + * wma_ibss_peer_info_event_handler() - IBSS peer info event handler + * @handle: wma handle + * @data: event data + * @len: length of data + * + * This function is dummy + * + * Return: 0 for success or error code + */ +static inline int +wma_ibss_peer_info_event_handler(void *handle, uint8_t *data, + uint32_t len) +{ + return 0; +} + +/** + * wma_set_ibss_pwrsave_params() - set ibss power save parameter to fw + * @wma: wma handle + * @vdev_id: vdev id + * + * This function is dummy + * + * Return: 0 for success or error code. + */ +static inline QDF_STATUS +wma_set_ibss_pwrsave_params(tp_wma_handle wma, uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* QCA_IBSS_SUPPORT */ + +/** + * wma_get_vdev_bssid() - Get BSSID from mlme_obj + * @vdev - pointer to vdev + * + * This API is used to get BSSID stored in vdev mlme object. + * + * Return: pointer to bssid on success else NULL. + */ +uint8_t *wma_get_vdev_bssid(struct wlan_objmgr_vdev *vdev); + +/** + * wma_find_bssid_by_vdev_id() - Get the BSS ID corresponding to the vdev ID + * @wma - wma handle + * @vdev_id - vdev ID + * + * Return: Returns pointer to bssid on success, + * otherwise returns NULL. + */ +static inline uint8_t *wma_find_bssid_by_vdev_id(tp_wma_handle wma, + uint8_t vdev_id) +{ + if (vdev_id >= wma->max_bssid) + return NULL; + + return wma_get_vdev_bssid(wma->interfaces[vdev_id].vdev); +} + +/** + * wma_find_vdev_id_by_bssid() - Get the corresponding vdev_id from BSSID + * @wma - wma handle + * @bssid - bssid address + * @vdev_id - vdev ID + * + * Return: SUCCESS or FAILURE. + */ +QDF_STATUS wma_find_vdev_id_by_bssid(tp_wma_handle wma, uint8_t *bssid, + uint8_t *vdev_id); + +/** + * wma_vdev_detach() - send vdev delete command to fw + * @wma_handle: wma handle + * @pdel_vdev_req_param: del vdev params + * + * Return: QDF status + */ +QDF_STATUS wma_vdev_detach(tp_wma_handle wma_handle, + struct del_vdev_params *pdel_vdev_req_param); + +QDF_STATUS wma_vdev_set_param(wmi_unified_t wmi_handle, uint32_t if_id, + uint32_t param_id, uint32_t param_value); + +QDF_STATUS wma_remove_peer(tp_wma_handle wma, uint8_t *mac_addr, + uint8_t vdev_id, bool no_fw_peer_delete); + +QDF_STATUS wma_peer_unmap_conf_send(tp_wma_handle wma, + struct send_peer_unmap_conf_params *msg); + +QDF_STATUS wma_create_peer(tp_wma_handle wma, + uint8_t peer_addr[QDF_MAC_ADDR_SIZE], + uint32_t peer_type, uint8_t vdev_id, + bool roam_synch_in_progress); + +/** + * wma_send_del_bss_response() - send delete bss resp + * @wma: wma handle + * @resp: pointer to del bss response + * + * Return: none + */ +void wma_send_del_bss_response(tp_wma_handle wma, struct del_bss_resp *resp); + +/** + * __wma_handle_vdev_stop_rsp() - vdev stop response handler + * @resp_event: pointer to response received + * + * Return: QDF_STATUS_SUCCESS for success or QDF_ERROR code + */ +QDF_STATUS +__wma_handle_vdev_stop_rsp(struct vdev_stop_response *resp_event); + +void wma_hold_req_timer(void *data); +struct wma_target_req *wma_fill_hold_req(tp_wma_handle wma, + uint8_t vdev_id, uint32_t msg_type, + uint8_t type, void *params, + uint32_t timeout); + +/** + * wma_add_bss() - Add BSS request to fw as per opmode + * @wma: wma handle + * @params: add bss params + * + * Return: none + */ +void wma_add_bss(tp_wma_handle wma, struct bss_params *params); + +/** + * wma_add_sta() - process add sta request as per opmode + * @wma: wma handle + * @add_Sta: add sta params + * + * Return: none + */ +void wma_add_sta(tp_wma_handle wma, tpAddStaParams add_sta); + +/** + * wma_delete_sta() - process del sta request as per opmode + * @wma: wma handle + * @del_sta: delete sta params + * + * Return: none + */ +void wma_delete_sta(tp_wma_handle wma, tpDeleteStaParams del_sta); + +/** + * wma_delete_bss() - process delete bss request from upper layer + * @wma: wma handle + * @vdev_id: vdev id + * + * Return: none + */ +void wma_delete_bss(tp_wma_handle wma, uint8_t vdev_id); + +int32_t wma_find_vdev_by_type(tp_wma_handle wma, int32_t type); + +/** + * wma_set_vdev_intrabss_fwd() - set intra_fwd value to wni_in. + * @wma_handle: wma handle + * @pdis_intra_fwd: Pointer to DisableIntraBssFwd struct + * + * Return: none + */ +void wma_set_vdev_intrabss_fwd(tp_wma_handle wma_handle, + tpDisableIntraBssFwd pdis_intra_fwd); + +/** + * wma_delete_bss_ho_fail() - process delete bss request for handoff failure + * @wma: wma handle + * @vdev_id: vdev id + * + * Delete BSS in case of ROAM_HO_FAIL processing is handled separately in + * this routine. It needs to be done without sending any commands to firmware + * because firmware has already stopped and deleted peer and vdev is down. + * Relevant logic is aggregated from other routines. It changes the host + * data structures without sending VDEV_STOP, PEER_FLUSH_TIDS, PEER_DELETE + * and VDEV_DOWN commands to firmware. + * + * Return: none + */ +void wma_delete_bss_ho_fail(tp_wma_handle wma, uint8_t vdev_id); + +uint32_t wma_get_bcn_rate_code(uint16_t rate); + +/* + * wma_mgmt.c functions declarations + */ +#ifdef WLAN_WMI_BCN +int wma_beacon_swba_handler(void *handle, uint8_t *event, uint32_t len); +#endif + +/** + * wma_peer_sta_kickout_event_handler() - kickout event handler + * @handle: wma handle + * @event: event data + * @len: data length + * + * Kickout event is received from firmware on observing beacon miss + * It handles kickout event for different modes and indicate to + * upper layers. + * + * Return: 0 for success or error code + */ +int wma_peer_sta_kickout_event_handler(void *handle, uint8_t *event, + uint32_t len); + +/** + * wma_unified_bcntx_status_event_handler() - beacon tx status event handler + * @handle: wma handle + * @cmd_param_info: event data + * @len: data length + * + * WMI Handler for WMI_OFFLOAD_BCN_TX_STATUS_EVENTID event from firmware. + * This event is generated by FW when the beacon transmission is offloaded + * and the host performs beacon template modification using WMI_BCN_TMPL_CMDID + * The FW generates this event when the first successful beacon transmission + * after template update + * + * Return: 0 for success or error code + */ +int wma_unified_bcntx_status_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +void wma_set_sta_sa_query_param(tp_wma_handle wma, + uint8_t vdev_id); + +void wma_set_sta_keep_alive(tp_wma_handle wma, uint8_t vdev_id, + uint32_t method, uint32_t timeperiod, + uint8_t *hostv4addr, uint8_t *destv4addr, + uint8_t *destmac); + +/** + * wma_objmgr_set_peer_mlme_phymode() - set phymode to peer object + * @wma: wma handle + * @mac_addr: mac addr of peer + * @phymode: phymode value to set + * + * Return: None + */ +void wma_objmgr_set_peer_mlme_phymode(tp_wma_handle wma, uint8_t *mac_addr, + enum wlan_phymode phymode); + +QDF_STATUS wma_send_peer_assoc(tp_wma_handle wma, + tSirNwType nw_type, + tpAddStaParams params); + +QDF_STATUS wmi_unified_vdev_set_gtx_cfg_send(wmi_unified_t wmi_handle, + uint32_t if_id, + gtx_config_t *gtx_info); + +void wma_update_protection_mode(tp_wma_handle wma, uint8_t vdev_id, + uint8_t llbcoexist); + +void wma_process_update_beacon_params(tp_wma_handle wma, + tUpdateBeaconParams *bcn_params); + +/** + * wma_update_rts_params() - update cfg parameters to target + * @wma: wma handle + * @value: rts_threshold + * + * Return: none + */ +void wma_update_rts_params(tp_wma_handle wma, uint32_t value); + +/** + * wma_update_frag_params() - update cfg parameters to target + * @wma: wma handle + * @value: frag_threshold + * + * Return: none + */ +void wma_update_frag_params(tp_wma_handle wma, uint32_t value); + +QDF_STATUS wma_process_update_edca_param_req(WMA_HANDLE handle, + tEdcaParams *edca_params); + +/** + * wma_tbttoffset_update_event_handler() - tbtt offset update handler + * @handle: wma handle + * @event: event buffer + * @len: data length + * + * Return: 0 for success or error code + */ +int wma_tbttoffset_update_event_handler(void *handle, uint8_t *event, + uint32_t len); + +void wma_send_probe_rsp_tmpl(tp_wma_handle wma, + tpSendProbeRespParams probe_rsp_info); + +/** + * wma_set_ap_vdev_up() - send vdev up req + * @wma: wma handle + * @vdev_id: vdev id + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_set_ap_vdev_up(tp_wma_handle wma, uint8_t vdev_id); + +void wma_send_beacon(tp_wma_handle wma, tpSendbeaconParams bcn_info); + +void wma_set_keepalive_req(tp_wma_handle wma, + struct keep_alive_req *keepalive); + +void wma_beacon_miss_handler(tp_wma_handle wma, uint32_t vdev_id, + int32_t rssi); + +void wma_process_update_opmode(tp_wma_handle wma_handle, + tUpdateVHTOpMode *update_vht_opmode); + +void wma_process_update_rx_nss(tp_wma_handle wma_handle, + tUpdateRxNss *update_rx_nss); + +void wma_process_update_membership(tp_wma_handle wma_handle, + tUpdateMembership *membership); + +void wma_process_update_userpos(tp_wma_handle wma_handle, + tUpdateUserPos *userpos); + +/* + * wma_power.c functions declarations + */ + +/** + * wma_enable_sta_ps_mode() - enable sta powersave params in fw + * @ps_req: power save request + * + * Return: none + */ +void wma_enable_sta_ps_mode(tpEnablePsParams ps_req); + +QDF_STATUS wma_unified_set_sta_ps_param(wmi_unified_t wmi_handle, + uint32_t vdev_id, uint32_t param, + uint32_t value); + +QDF_STATUS wma_set_ap_peer_uapsd(tp_wma_handle wma, uint32_t vdev_id, + uint8_t *peer_addr, uint8_t uapsd_value, + uint8_t max_sp); + +void wma_update_edca_params_for_ac(tSirMacEdcaParamRecord *edca_param, + struct wmi_host_wme_vparams *wmm_param, + int ac, bool mu_edca_param); + +void wma_set_tx_power(WMA_HANDLE handle, + tMaxTxPowerParams *tx_pwr_params); + +void wma_set_max_tx_power(WMA_HANDLE handle, + tMaxTxPowerParams *tx_pwr_params); + +void wma_disable_sta_ps_mode(tpDisablePsParams ps_req); + +/** + * wma_send_max_tx_pwrlmt() - send max tx power limit to fw + * @handle: wma handle + * @vdev_id: vdev id + * + * Return: none + */ +void wma_send_max_tx_pwrlmt(WMA_HANDLE handle, uint8_t vdev_id); + +/** + * wma_enable_uapsd_mode() - enable uapsd mode in fw + * @wma: wma handle + * @ps_req: power save request + * + * Return: none + */ +void wma_enable_uapsd_mode(tp_wma_handle wma, tpEnableUapsdParams ps_req); + +void wma_disable_uapsd_mode(tp_wma_handle wma, tpDisableUapsdParams ps_req); + +QDF_STATUS wma_get_temperature(tp_wma_handle wma_handle); + +int wma_pdev_temperature_evt_handler(void *handle, uint8_t *event, + uint32_t len); + +QDF_STATUS wma_process_tx_power_limits(WMA_HANDLE handle, + struct tx_power_limit *ptxlim); + +void wma_update_noa(struct beacon_info *beacon, + struct p2p_sub_element_noa *noa_ie); + +void wma_update_probe_resp_noa(tp_wma_handle wma_handle, + struct p2p_sub_element_noa *noa_ie); + +void wma_process_set_mimops_req(tp_wma_handle wma_handle, + tSetMIMOPS *mimops); + +QDF_STATUS wma_set_mimops(tp_wma_handle wma, uint8_t vdev_id, int value); + +QDF_STATUS wma_notify_modem_power_state(void *wma_ptr, + tSirModemPowerStateInd *pReq); + +QDF_STATUS wma_set_smps_params(tp_wma_handle wma, uint8_t vdev_id, + int value); + +/* + * wma_data.c functions declarations + */ +/** + * wma_set_bss_rate_flags() - set rate flags based on BSS capability + * @wma: pointer to wma handle + * @vdev_id: vdev id + * @add_bss: pointer to bss params + * + * Return: none + */ +void wma_set_bss_rate_flags(tp_wma_handle wma, uint8_t vdev_id, + struct bss_params *add_bss); + +/** + * wma_get_vht_rate_flags() - Return the VHT rate flags corresponding to the BW + * @ch_width: BW for which rate flags is required + * + * Return: Rate flags corresponding to ch_width + */ +enum tx_rate_info wma_get_vht_rate_flags(enum phy_ch_width ch_width); + +/** + * wma_get_ht_rate_flags() - Return the HT rate flags corresponding to the BW + * @ch_width: BW for which rate flags is required + * + * Return: Rate flags corresponding to ch_width + */ +enum tx_rate_info wma_get_ht_rate_flags(enum phy_ch_width ch_width); + +/** + * wma_get_he_rate_flags() - Return the HE rate flags corresponding to the BW + * @ch_width: BW for which rate flags is required + * + * Return: Rate flags corresponding to ch_width + */ +enum tx_rate_info wma_get_he_rate_flags(enum phy_ch_width ch_width); + +int32_t wmi_unified_send_txbf(tp_wma_handle wma, tpAddStaParams params); + +/** + * wma_check_txrx_chainmask() - check txrx chainmask + * @num_rf_chains: number of rf chains + * @cmd_value: command value + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_check_txrx_chainmask(int num_rf_chains, int cmd_value); + +int wma_peer_state_change_event_handler(void *handle, + uint8_t *event_buff, + uint32_t len); + +QDF_STATUS wma_set_enable_disable_mcc_adaptive_scheduler(uint32_t + mcc_adaptive_scheduler); + +QDF_STATUS wma_set_mcc_channel_time_latency + (tp_wma_handle wma, + uint32_t mcc_channel, uint32_t mcc_channel_time_latency); + +QDF_STATUS wma_set_mcc_channel_time_quota + (tp_wma_handle wma, + uint32_t adapter_1_chan_number, + uint32_t adapter_1_quota, uint32_t adapter_2_chan_number); + +/** + * wma_process_rate_update_indate() - rate update indication + * @wma: wma handle + * @pRateUpdateParams: Rate update params + * + * This function update rate & short GI interval to fw based on params + * send by SME. + * + * Return: QDF status + */ +QDF_STATUS wma_process_rate_update_indicate(tp_wma_handle wma, + tSirRateUpdateInd * + pRateUpdateParams); + +QDF_STATUS wma_tx_attach(tp_wma_handle wma_handle); + +QDF_STATUS wma_tx_detach(tp_wma_handle wma_handle); + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || \ + defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(CONFIG_HL_SUPPORT) + +/** + * wma_mcc_vdev_tx_pause_evt_handler() - pause event handler + * @handle: wma handle + * @event: event buffer + * @len: data length + * + * This function handle pause event from fw and pause/unpause + * vdev. + * + * Return: 0 for success or error code. + */ +int wma_mcc_vdev_tx_pause_evt_handler(void *handle, uint8_t *event, + uint32_t len); +#endif + +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) +QDF_STATUS wma_process_init_bad_peer_tx_ctl_info(tp_wma_handle wma, + struct t_bad_peer_txtcl_config *config); +#else +static inline QDF_STATUS +wma_process_init_bad_peer_tx_ctl_info(tp_wma_handle wma, + struct t_bad_peer_txtcl_config *config) +{ + return QDF_STATUS_E_FAILURE; +} +#endif + +QDF_STATUS wma_process_init_thermal_info(tp_wma_handle wma, + t_thermal_mgmt *pThermalParams); + +QDF_STATUS wma_process_set_thermal_level(tp_wma_handle wma, + uint8_t thermal_level); + +QDF_STATUS wma_set_thermal_mgmt(tp_wma_handle wma_handle, + t_thermal_cmd_params thermal_info); + +int wma_thermal_mgmt_evt_handler(void *handle, uint8_t *event, + uint32_t len); + +int wma_fast_tx_fail_event_handler(void *handle, uint8_t *data, + uint32_t len); + +/* + * wma_utils.c functions declarations + */ + +#ifdef WLAN_FEATURE_STATS_EXT +/** + * wma_stats_ext_event_handler() - extended stats event handler + * @handle: wma handle + * @event_buf: event buffer received from fw + * @len: length of data + * + * Return: 0 for success or error code + */ +int wma_stats_ext_event_handler(void *handle, uint8_t *event_buf, + uint32_t len); +#endif + +enum eSmpsModeValue host_map_smps_mode(A_UINT32 fw_smps_mode); +int wma_smps_mode_to_force_mode_param(uint8_t smps_mode); + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS +void wma_register_ll_stats_event_handler(tp_wma_handle wma_handle); + +/** + * wma_process_ll_stats_clear_req() - clear link layer stats + * @wma: wma handle + * @clearReq: ll stats clear request command params + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_process_ll_stats_clear_req + (tp_wma_handle wma, const tpSirLLStatsClearReq clearReq); + +QDF_STATUS wma_process_ll_stats_set_req + (tp_wma_handle wma, const tpSirLLStatsSetReq setReq); + +/** + * wma_process_ll_stats_get_req() - link layer stats get request + * @wma:wma handle + * @getReq:ll stats get request command params + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_process_ll_stats_get_req + (tp_wma_handle wma, const tpSirLLStatsGetReq getReq); + +int wma_unified_link_iface_stats_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); +void wma_config_stats_ext_threshold(tp_wma_handle wma, + struct sir_ll_ext_stats_threshold *thresh); +#endif + +void wma_post_link_status(tAniGetLinkStatus *pGetLinkStatus, + uint8_t link_status); + +/** + * wma_link_status_event_handler() - link status event handler + * @handle: wma handle + * @cmd_param_info: data from event + * @len: length + * + * Return: 0 for success or error code + */ +int wma_link_status_event_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len); + +/** + * wma_rso_cmd_status_event_handler() - RSO Command status event handler + * @wmi_event: WMI event + * + * This function is used to send RSO command status to upper layer + * + * Return: 0 for success + */ +int wma_rso_cmd_status_event_handler(wmi_roam_event_fixed_param *wmi_event); + +int wma_stats_event_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len); + +QDF_STATUS wma_send_link_speed(uint32_t link_speed); + +int wma_link_speed_event_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len); + +QDF_STATUS wma_wni_cfg_dnld(tp_wma_handle wma_handle); + +int wma_unified_debug_print_event_handler(void *handle, uint8_t *datap, + uint32_t len); + +/** + * wma_peer_phymode() - get phymode + * @nw_type: nw type + * @sta_type: sta type + * @is_ht: is ht supported + * @ch_width: supported channel width + * @is_vht: is vht supported + * @is_he: is HE supported + * + * Return: host phymode + */ +enum wlan_phymode +wma_peer_phymode(tSirNwType nw_type, uint8_t sta_type, + uint8_t is_ht, uint8_t ch_width, + uint8_t is_vht, bool is_he); + +int32_t wma_txrx_fw_stats_reset(tp_wma_handle wma_handle, + uint8_t vdev_id, uint32_t value); + +int32_t wma_set_txrx_fw_stats_level(tp_wma_handle wma_handle, + uint8_t vdev_id, uint32_t value); + +/* + * wma_features.c functions declarations + */ + +/** + * wma_sar_register_event_handlers() - Register SAR event handlers + * @handle: WMA Handle + * + * Function to be called during WMA initialization to register SAR + * event handlers with WMI + * + * Return: QDF_STATUS_SUCCESS if registration is successful, otherwise + * an error enumeration + */ +QDF_STATUS wma_sar_register_event_handlers(WMA_HANDLE handle); + +void wma_process_link_status_req(tp_wma_handle wma, + tAniGetLinkStatus *pGetLinkStatus); + +/** + * wma_get_peer_info_ext() - get peer info + * @handle: wma interface + * @peer_info_req: get peer info request information + * + * This function will send WMI_REQUEST_PEER_STATS_INFO_CMDID to FW + * + * Return: 0 on success, otherwise error value + */ +QDF_STATUS wma_get_peer_info_ext(WMA_HANDLE handle, + struct sir_peer_info_ext_req *peer_info_req); + +/** + * wma_get_isolation() - get antenna isolation + * @handle: wma interface + * + * This function will send WMI_COEX_GET_ANTENNA_ISOLATION_CMDID to FW + * + * Return: 0 on success, otherwise error value + */ +QDF_STATUS wma_get_isolation(tp_wma_handle wma); + +/** + * wma_peer_info_event_handler() - Handler for WMI_PEER_STATS_INFO_EVENTID + * @handle: WMA global handle + * @cmd_param_info: Command event data + * @len: Length of cmd_param_info + * + * This function will handle WMI_PEER_STATS_INFO_EVENTID + * + * Return: 0 on success, error code otherwise + */ +int wma_peer_info_event_handler(void *handle, u_int8_t *cmd_param_info, + u_int32_t len); + +int wma_profile_data_report_event_handler(void *handle, uint8_t *event_buf, + uint32_t len); + +QDF_STATUS wma_unified_fw_profiling_cmd(wmi_unified_t wmi_handle, + uint32_t cmd, uint32_t value1, uint32_t value2); + +int wma_unified_csa_offload_enable(tp_wma_handle wma, uint8_t vdev_id); + +#ifdef FEATURE_WLAN_TDLS +int wma_tdls_event_handler(void *handle, uint8_t *event, uint32_t len); +#endif + +int wma_csa_offload_handler(void *handle, uint8_t *event, uint32_t len); + +#ifdef FEATURE_OEM_DATA_SUPPORT +int wma_oem_data_response_handler(void *handle, uint8_t *datap, + uint32_t len); +#endif + +#if !defined(REMOVE_PKT_LOG) +QDF_STATUS wma_pktlog_wmi_send_cmd(WMA_HANDLE handle, + struct ath_pktlog_wmi_params *params); +#endif + +int wma_wow_wakeup_host_event(void *handle, uint8_t *event, + uint32_t len); + +int wma_d0_wow_disable_ack_event(void *handle, uint8_t *event, uint32_t len); + +int wma_pdev_resume_event_handler(void *handle, uint8_t *event, uint32_t len); + +void wma_del_ts_req(tp_wma_handle wma, struct del_ts_params *msg); + +/** + * wma_aggr_qos_req() - send aggr qos request to fw + * @wma: handle to wma + * @pAggrQosRspMsg - combined struct for all ADD_TS requests. + * + * A function to handle WMA_AGGR_QOS_REQ. This will send out + * ADD_TS requestes to firmware in loop for all the ACs with + * active flow. + * + * Return: none + */ +void wma_aggr_qos_req(tp_wma_handle wma, + struct aggr_add_ts_param *pAggrQosRspMsg); + +void wma_add_ts_req(tp_wma_handle wma, struct add_ts_param *msg); + +#ifdef FEATURE_WLAN_ESE +QDF_STATUS wma_process_tsm_stats_req(tp_wma_handle wma_handler, + void *pTsmStatsMsg); +void wma_config_plm(tp_wma_handle wma, struct plm_req_params *plm); +#endif + +QDF_STATUS wma_process_mcbc_set_filter_req(tp_wma_handle wma_handle, + tSirRcvFltMcAddrList * mcbc_param); +QDF_STATUS wma_process_cesium_enable_ind(tp_wma_handle wma); + +QDF_STATUS wma_process_get_peer_info_req + (tp_wma_handle wma, tSirIbssGetPeerInfoReqParams *pReq); + +QDF_STATUS wma_process_tx_fail_monitor_ind + (tp_wma_handle wma, tAniTXFailMonitorInd *pReq); + +#ifdef FEATURE_WLAN_RMC +QDF_STATUS wma_process_rmc_enable_ind(tp_wma_handle wma); + +QDF_STATUS wma_process_rmc_disable_ind(tp_wma_handle wma); + +QDF_STATUS wma_process_rmc_action_period_ind(tp_wma_handle wma); +#else +static inline +QDF_STATUS wma_process_rmc_enable_ind(tp_wma_handle wma) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS wma_process_rmc_disable_ind(tp_wma_handle wma) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +QDF_STATUS wma_process_rmc_action_period_ind(tp_wma_handle wma) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS wma_process_add_periodic_tx_ptrn_ind(WMA_HANDLE handle, + tSirAddPeriodicTxPtrn *pattern); + +QDF_STATUS wma_process_del_periodic_tx_ptrn_ind(WMA_HANDLE handle, + tSirDelPeriodicTxPtrn * + pDelPeriodicTxPtrnParams); + +#ifdef WLAN_FEATURE_STATS_EXT +/** + * wma_stats_ext_req() - request ext stats from fw + * @wma_ptr: wma handle + * @preq: stats ext params + * + * Return: QDF status + */ +QDF_STATUS wma_stats_ext_req(void *wma_ptr, tpStatsExtRequest preq); +#endif + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +QDF_STATUS wma_enable_ext_wow(tp_wma_handle wma, tpSirExtWoWParams params); + +int wma_set_app_type1_params_in_fw(tp_wma_handle wma, + tpSirAppType1Params appType1Params); + +QDF_STATUS wma_set_app_type2_params_in_fw(tp_wma_handle wma, + tpSirAppType2Params appType2Params); +#endif + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +int wma_auto_shutdown_event_handler(void *handle, uint8_t *event, + uint32_t len); + +/** + * wma_set_auto_shutdown_timer_req() - sets auto shutdown timer in firmware + * @wma_handle: wma handle + * @auto_sh_cmd: auto shutdown timer params + * + * Return: QDF status + */ +QDF_STATUS +wma_set_auto_shutdown_timer_req(tp_wma_handle wma_handle, + struct auto_shutdown_cmd *auto_sh_cmd); +#endif + +#ifdef WLAN_FEATURE_TSF +int wma_vdev_tsf_handler(void *handle, uint8_t *data, uint32_t data_len); +QDF_STATUS wma_capture_tsf(tp_wma_handle wma_handle, uint32_t vdev_id); +QDF_STATUS wma_reset_tsf_gpio(tp_wma_handle wma_handle, uint32_t vdev_id); +QDF_STATUS wma_set_tsf_gpio_pin(WMA_HANDLE handle, uint32_t pin); +#else +static inline QDF_STATUS wma_capture_tsf(tp_wma_handle wma_handle, + uint32_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS wma_reset_tsf_gpio(tp_wma_handle wma_handle, + uint32_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} + +static inline int wma_vdev_tsf_handler(void *handle, uint8_t *data, + uint32_t data_len) +{ + return 0; +} + +static inline QDF_STATUS wma_set_tsf_gpio_pin(WMA_HANDLE handle, uint32_t pin) +{ + return QDF_STATUS_E_INVAL; +} +#endif +QDF_STATUS wma_set_wisa_params(tp_wma_handle wma, struct sir_wisa_params *wisa); + +#ifdef DHCP_SERVER_OFFLOAD +/** + * wma_process_dhcpserver_offload() - enable DHCP server offload + * @wma_handle: wma handle + * @params: DHCP server offload information + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS +wma_process_dhcpserver_offload(tp_wma_handle wma_handle, + struct dhcp_offload_info_params *params); +#endif + +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING +QDF_STATUS wma_set_led_flashing(tp_wma_handle wma_handle, + struct flashing_req_params *flashing); +#endif + +/** + * wma_sar_rsp_evt_handler() - process sar response event from FW. + * @handle: ol scn handle + * @event: event buffer + * @len: buffer length + * + * Return: 0 for success or error code + */ +int wma_sar_rsp_evt_handler(ol_scn_t handle, uint8_t *event, uint32_t len); + +#ifdef FEATURE_WLAN_CH_AVOID +QDF_STATUS wma_process_ch_avoid_update_req(tp_wma_handle wma_handle, + tSirChAvoidUpdateReq * + ch_avoid_update_req); +#endif + +#ifdef FEATURE_WLAN_TDLS +int wma_update_tdls_peer_state(WMA_HANDLE handle, + struct tdls_peer_update_state *peer_state); +#endif + +void wma_set_vdev_mgmt_rate(tp_wma_handle wma, uint8_t vdev_id); +void wma_set_sap_keepalive(tp_wma_handle wma, uint8_t vdev_id); + +#ifdef FEATURE_RSSI_MONITOR +int wma_rssi_breached_event_handler(void *handle, + u_int8_t *cmd_param_info, u_int32_t len); +#else /* FEATURE_RSSI_MONITOR */ +static inline +int wma_rssi_breached_event_handler(void *handle, + u_int8_t *cmd_param_info, u_int32_t len) +{ + return 0; +} +#endif /* FEATURE_RSSI_MONITOR */ + +QDF_STATUS wma_process_cfg_action_frm_tb_ppdu(tp_wma_handle wma, + struct cfg_action_frm_tb_ppdu *cfg_info); + +QDF_STATUS wma_process_set_ie_info(tp_wma_handle wma, + struct vdev_ie_info *ie_info); +int wma_peer_assoc_conf_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len); + +int wma_peer_delete_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len); +void wma_remove_req(tp_wma_handle wma, uint8_t vdev_id, + uint8_t type); + +QDF_STATUS wma_process_hal_pwr_dbg_cmd(WMA_HANDLE handle, + struct sir_mac_pwr_dbg_cmd * + sir_pwr_dbg_params); + +/** + * wma_lost_link_info_handler() - collect lost link information and inform SME + * @wma: WMA handle + * @vdev_id: vdev ID + * @rssi: rssi at disconnection time + * + * Return: none + */ +void wma_lost_link_info_handler(tp_wma_handle wma, uint32_t vdev_id, + int32_t rssi); +int wma_unified_power_debug_stats_event_handler(void *handle, + uint8_t *cmd_param_info, uint32_t len); +/** + * wma_unified_beacon_debug_stats_event_handler() - collect beacon debug stats + * @handle: WMA handle + * @cmd_param_info: data from event + * @len: length + * + * Return: 0 for success or error code + */ +int wma_unified_beacon_debug_stats_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len); + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +/** + * wma_sta_kickout_event()- send sta kickout event + * @kickout_reason - reasoncode for kickout + * @macaddr[QDF_MAC_ADDR_SIZE]: Peer mac address + * @vdev_id: Unique id for identifying the VDEV + * + * This function sends sta kickout diag event + * + * Return: void. + */ +void wma_sta_kickout_event(uint32_t kickout_reason, uint8_t vdev_id, + uint8_t *macaddr); +#else +static inline void wma_sta_kickout_event(uint32_t kickout_reason, + uint8_t vdev_id, uint8_t *macaddr) +{ + +}; +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +/** + * wma_get_rcpi_req() - get rcpi request + * @handle: wma handle + * @rcpi_request: rcpi params + * + * Return: none + */ +QDF_STATUS wma_get_rcpi_req(WMA_HANDLE handle, + struct sme_rcpi_req *rcpi_request); + +/** + * wma_rcpi_event_handler() - rcpi event handler + * @handle: wma handle + * @cmd_param_info: data from event + * @len: length + * + * Return: 0 for success or error code + */ +int wma_rcpi_event_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len); + +/** + * wma_acquire_wakelock() - acquire the given wakelock + * @wl: the wakelock to acquire + * @msec: the wakelock duration in milliseconds + * + * This also acquires the wma runtime pm lock. + * + * Return: None + */ +void wma_acquire_wakelock(qdf_wake_lock_t *wl, uint32_t msec); + +/** + * wma_release_wakelock() - release the given wakelock + * @wl: the wakelock to release + * + * This also releases the wma runtime pm lock. + * + * Return: None + */ +void wma_release_wakelock(qdf_wake_lock_t *wl); + +/** + * wma_send_vdev_stop_to_fw() - send the vdev stop command to firmware + * @wma: a reference to the global WMA handle + * @vdev_id: the Id of the vdev to stop + * + * Consumers should call wma_release_wakelock() upon receipt of the vdev stop + * response from firmware to avoid power penalties. + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_send_vdev_stop_to_fw(t_wma_handle *wma, uint8_t vdev_id); + +int wma_get_arp_stats_handler(void *handle, uint8_t *data, uint32_t data_len); + +/** + * wma_send_vdev_down_to_fw() - send the vdev down command to firmware + * @wma: a reference to the global WMA handle + * @vdev_id: the Id of the vdev to down + * + * This also releases the vdev start wakelock. + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_send_vdev_down_to_fw(t_wma_handle *wma, uint8_t vdev_id); + +/* + * wma_rx_aggr_failure_event_handler - event handler to handle rx aggr failure + * @handle: the wma handle + * @event_buf: buffer with event + * @len: buffer length + * + * This function receives rx aggregation failure event and then pass to upper + * layer + * + * Return: 0 on success + */ +int wma_rx_aggr_failure_event_handler(void *handle, u_int8_t *event_buf, + u_int32_t len); + +/** + * wma_wlan_bt_activity_evt_handler - event handler to handle bt activity + * @handle: the WMA handle + * @event: buffer with the event parameters + * @len: length of the buffer + * + * This function receives BT activity event from firmware and passes the event + * information to upper layers + * + * Return: 0 on success + */ +int wma_wlan_bt_activity_evt_handler(void *handle, uint8_t *event, + uint32_t len); + +/** + * wma_pdev_div_info_evt_handler - event handler to handle antenna info + * @handle: the wma handle + * @event_buf: buffer with event + * @len: buffer length + * + * This function receives antenna info from firmware and passes the event + * to upper layer + * + * Return: 0 on success + */ +int wma_pdev_div_info_evt_handler(void *handle, u_int8_t *event_buf, + u_int32_t len); + +/** + * wma_update_beacon_interval() - update beacon interval in fw + * @wma: wma handle + * @vdev_id: vdev id + * @beaconInterval: becon interval + * + * Return: none + */ +void +wma_update_beacon_interval(tp_wma_handle wma, uint8_t vdev_id, + uint16_t beaconInterval); + +#define RESET_BEACON_INTERVAL_TIMEOUT 200 + +struct wma_beacon_interval_reset_req { + qdf_timer_t event_timeout; + uint8_t vdev_id; + uint16_t interval; +}; + +/** + * wma_fill_beacon_interval_reset_req() - req to reset beacon interval + * @wma: wma handle + * @vdev_id: vdev id + * @beacon_interval: beacon interval + * @timeout: timeout val + * + * Return: status + */ +int wma_fill_beacon_interval_reset_req(tp_wma_handle wma, uint8_t vdev_id, + uint16_t beacon_interval, uint32_t timeout); +/* + * wma_is_vdev_valid() - check the vdev status + * @vdev_id: vdev identifier + * + * This function verifies the vdev validity + * + * Return: 'true' on valid vdev else 'false' + */ +bool wma_is_vdev_valid(uint32_t vdev_id); + +/** + * wma_vdev_obss_detection_info_handler - event handler to handle obss detection + * @handle: the wma handle + * @event: buffer with event + * @len: buffer length + * + * This function receives obss detection info from firmware which is used to + * decide obss protection. + * + * Return: 0 on success + */ +int wma_vdev_obss_detection_info_handler(void *handle, uint8_t *event, + uint32_t len); + +/** + * wma_vdev_bss_color_collision_info_handler - event handler to + * handle obss color collision detection. + * @handle: the wma handle + * @event: buffer with event + * @len: buffer length + * + * This function receives obss color collision detection info from firmware + * which is used to select new bss color. + * + * Return: 0 on success + */ +int wma_vdev_bss_color_collision_info_handler(void *handle, + uint8_t *event, + uint32_t len); + +#ifdef WLAN_SUPPORT_TWT +/** + * wma_twt_en_complete_event_handler - TWT enable complete event handler + * @handle: wma handle + * @event: buffer with event + * @len: buffer length + * + * Return: 0 on success + */ +int wma_twt_en_complete_event_handler(void *handle, + uint8_t *event, uint32_t len); + +/** + * wma_twt_disable_comp_event_handler- TWT disable complete event handler + * @handle: wma handle + * @event: buffer with event + * @len: buffer length + * + * Return: 0 on success + */ +int wma_twt_disable_comp_event_handler(void *handle, uint8_t *event, + uint32_t len); +#else +static inline int wma_twt_en_complete_event_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + return 0; +} + +static inline int wma_twt_disable_comp_event_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + return 0; +} +#endif + +/** + * wma_get_roam_scan_stats() - Get roam scan stats request + * @handle: wma handle + * @req: request details + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_get_roam_scan_stats(WMA_HANDLE handle, + struct sir_roam_scan_stats *req); + +/** + * wma_roam_scan_stats_event_handler() - roam scan stats event handler + * @handle: wma handle + * @event: event data + * @len: length of data + * + * Return: Success or Failure status + */ +int wma_roam_scan_stats_event_handler(void *handle, uint8_t *event, + uint32_t len); + +/** + * wma_send_vdev_down() - send del bss req to firmware + * @wma: wma handle. + * @req: pointer to del bss response + * + * This function sends del bss resp to upper layer + * + * Return: none + */ +void wma_send_vdev_down(tp_wma_handle wma, struct del_bss_resp *req); + +/** + * wma_cold_boot_cal_event_handler() - Cold boot cal event handler + * @wma_ctx: wma handle + * @event_buff: event data + * @len: length of data + * + * Return: Success or Failure status + */ +int wma_cold_boot_cal_event_handler(void *wma_ctx, uint8_t *event_buff, + uint32_t len); + +#ifdef FEATURE_OEM_DATA +/** + * wma_oem_event_handler() - oem data event handler + * @wma_ctx: wma handle + * @event_buff: event data + * @len: length of event buffer + * + * Return: Success or Failure status + */ +int wma_oem_event_handler(void *wma_ctx, uint8_t *event_buff, uint32_t len); +#endif + +/** + * wma_set_roam_triggers() - Send roam trigger bitmap to WMI + * @wma_handle: wma handle + * @triggers: Carries vdev id and roam trigger bitmap. + * + * Return: Success or Failure status + */ +QDF_STATUS wma_set_roam_triggers(tp_wma_handle wma_handle, + struct roam_triggers *triggers); + +/** + * wma_get_ani_level_evt_handler - event handler to fetch ani level + * @handle: the wma handle + * @event_buf: buffer with event + * @len: buffer length + * + * This function receives ani level from firmware and passes the event + * to upper layer + * + * Return: 0 on success + */ +int wma_get_ani_level_evt_handler(void *handle, uint8_t *event_buf, + uint32_t len); +#endif diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma_tgt_cfg.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma_tgt_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..cc809cfb4e9173c3230043ace33768b297b529eb --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma_tgt_cfg.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WMA_TGT_CFG_H +#define WMA_TGT_CFG_H + +#include "wma_sar_public_structs.h" +#include "nan_public_structs.h" + +/** + * struct wma_tgt_services - target services + * @sta_power_save: sta power save + * @uapsd: uapsd + * @ap_dfs: ap dfs + * @en_11ac: enable 11ac + * @arp_offload: arp offload + * @early_rx: early rx + * @pno_offload: pno offload + * @beacon_offload: beacon offload + * @lte_coex_ant_share: LTE coex ant share + * @en_tdls: enable tdls + * @en_tdls_offchan: enable tdls offchan + * @en_tdls_uapsd_buf_sta: enable sta tdls uapsd buf + * @en_tdls_uapsd_sleep_sta: enable sta tdls uapsd sleep + * @en_roam_offload: enable roam offload + * @en_11ax: enable 11ax + * @is_fw_mawc_capable: Motion Aided Wireless Connectivity feature + * @twt_requestor: TWT requestor capability + * @twt_responder: TWT responder capability + * @bcn_reception_stats: Beacon Reception stats capability + * @is_roam_scan_ch_to_host: Get roam scan channels from fw supported + */ +struct wma_tgt_services { + uint32_t sta_power_save; + bool uapsd; + uint32_t ap_dfs; + uint32_t en_11ac; + uint32_t arp_offload; + uint32_t early_rx; +#ifdef FEATURE_WLAN_SCAN_PNO + bool pno_offload; +#endif /* FEATURE_WLAN_SCAN_PNO */ + bool beacon_offload; + bool pmf_offload; + uint32_t lte_coex_ant_share; +#ifdef FEATURE_WLAN_TDLS + bool en_tdls; + bool en_tdls_offchan; + bool en_tdls_uapsd_buf_sta; + bool en_tdls_uapsd_sleep_sta; +#endif /* FEATURE_WLAN_TDLS */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + bool en_roam_offload; +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + bool en_11ax; + bool get_peer_info_enabled; + bool is_fils_roaming_supported; + bool is_fw_mawc_capable; + bool is_11k_offload_supported; + bool twt_requestor; + bool twt_responder; + bool obss_scan_offload; + bool bcn_reception_stats; + bool is_roam_scan_ch_to_host; +}; + +/** + * struct wma_tgt_ht_cap - ht capabalitiy + * @mpdu_density: mpdu density + * @ht_rx_stbc: ht rx stbc + * @ht_tx_stbc: ht tx stbc + * @ht_rx_ldpc: ht rx ldpc + * @ht_sgi_20: ht sgi 20 + * @ht_sgi_40: ht sgi 40 + * @num_rf_chains: num of rf chains + */ +struct wma_tgt_ht_cap { + uint32_t mpdu_density; + bool ht_rx_stbc; + bool ht_tx_stbc; + bool ht_rx_ldpc; + bool ht_sgi_20; + bool ht_sgi_40; + uint32_t num_rf_chains; +}; + +/** + * struct wma_tgt_vht_cap - vht capabalities + * @vht_max_mpdu: vht max mpdu + * @supp_chan_width: supported channel width + * @vht_rx_ldpc: vht rx ldpc + * @vht_short_gi_80: vht short gi 80 + * @vht_short_gi_160: vht short gi 160 + * @vht_tx_stbc: vht tx stbc + * @vht_rx_stbc: vht rx stbc + * @vht_su_bformer: vht su bformer + * @vht_su_bformee: vht su bformee + * @vht_mu_bformer: vht mu bformer + * @vht_mu_bformee: vht mu bformee + * @vht_max_ampdu_len_exp: vht max ampdu len exp + * @vht_txop_ps: vht txop ps + * @vht_mcs_10_11_supp: VHT MCS 10 & 11 support + */ +struct wma_tgt_vht_cap { + uint32_t vht_max_mpdu; + uint32_t supp_chan_width; + uint32_t vht_rx_ldpc; + uint32_t vht_short_gi_80; + uint32_t vht_short_gi_160; + uint32_t vht_tx_stbc; + uint32_t vht_rx_stbc; + uint32_t vht_su_bformer; + uint32_t vht_su_bformee; + uint32_t vht_mu_bformer; + uint32_t vht_mu_bformee; + uint32_t vht_max_ampdu_len_exp; + uint32_t vht_txop_ps; + uint32_t vht_mcs_10_11_supp; +}; + +/** + * struct board_info - Structure for board related information + * @bdf_version: board file version + * @ref_design_id: reference design id + * @customer_id: customer id + * @project_id: project id + * @board_data_rev: board data revision + * + * This board information will be stored in board file during the + * calibration and customization. + * + */ +struct board_info { + uint32_t bdf_version; + uint32_t ref_design_id; + uint32_t customer_id; + uint32_t project_id; + uint32_t board_data_rev; +}; + +/** + * struct wma_tgt_cfg - target config + * @target_fw_version: target fw version + * @target_fw_vers_ext: target fw extended sub version + * @band_cap: band capability bitmap + * @reg_domain: reg domain + * @eeprom_rd_ext: eeprom rd ext + * @hw_macaddr: hw mcast addr + * @services: struct wma_tgt_services + * @ht_cap: struct wma_tgt_ht_cap + * @vht_cap: struct wma_tgt_vht_cap + * @max_intf_count: max interface count + * @lpss_support: lpass support + * @egap_support: enhanced green ap support + * @nan_datapath_enabled: nan data path support + * @he_cap: HE capability received from FW + * @dfs_cac_offload: dfs and cac timer offloaded + * @tx_bfee_8ss_enabled: Tx Beamformee support for 8x8 + * @dynamic_nss_chains_update: per vdev dynamic nss, chains update + * @rcpi_enabled: for checking rcpi support + * @obss_detection_offloaded: obss detection offloaded to firmware + * @obss_color_collision_offloaded: obss color collision offloaded to firmware + * @sar_version: Version of SAR supported by firmware + * @bcast_twt_support: braodcast twt support + * @restricted_80p80_bw_supp: Restricted 80+80MHz(165MHz BW) support + */ +struct wma_tgt_cfg { + uint32_t target_fw_version; + uint32_t target_fw_vers_ext; + uint32_t band_cap; + uint32_t reg_domain; + uint32_t eeprom_rd_ext; + struct qdf_mac_addr hw_macaddr; + struct wma_tgt_services services; + struct wma_tgt_ht_cap ht_cap; + struct wma_tgt_vht_cap vht_cap; + uint8_t max_intf_count; +#ifdef WLAN_FEATURE_LPSS + uint8_t lpss_support; +#endif + uint8_t ap_arpns_support; + uint32_t fine_time_measurement_cap; +#ifdef WLAN_FEATURE_NAN + bool nan_datapath_enabled; +#endif + bool sub_20_support; + uint16_t wmi_max_len; +#ifdef WLAN_FEATURE_11AX + tDot11fIEhe_cap he_cap; + uint8_t ppet_2g[HE_MAX_PPET_SIZE]; + uint8_t ppet_5g[HE_MAX_PPET_SIZE]; + tDot11fIEhe_cap he_cap_2g; + tDot11fIEhe_cap he_cap_5g; +#endif + bool dfs_cac_offload; + bool tx_bfee_8ss_enabled; + bool dynamic_nss_chains_support; + bool rcpi_enabled; + bool obss_detection_offloaded; + bool obss_color_collision_offloaded; + uint32_t hw_bd_id; + struct board_info hw_bd_info; + enum sar_version sar_version; + struct nan_tgt_caps nan_caps; + bool bcast_twt_support; + bool restricted_80p80_bw_supp; +}; +#endif /* WMA_TGT_CFG_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma_twt.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma_twt.h new file mode 100644 index 0000000000000000000000000000000000000000..43f64d8f0c40273fa168dd9ac62791f57df502d1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma_twt.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WMA_TWT_H +#define __WMA_TWT_H + +#include "wma.h" +#include "wmi_unified_twt_param.h" + +#ifdef WLAN_SUPPORT_TWT +/** + * wma_send_twt_enable_cmd() - Send TWT Enable command to firmware + * @pdev_id: pdev id + * @congestion_timeout: Timeout value for the TWT congestion timer + * @bcast_val: broadcast twt support + * + * Return: None + */ +void wma_send_twt_enable_cmd(uint32_t pdev_id, + uint32_t congestion_timeout, + bool bcast_val); + +/** + * wma_set_twt_peer_caps() - Fill the peer TWT capabilities + * @params: STA context params which will store the capabilities + * @cmd: Command in which the capabilities should be populated + * + * Return: None + */ +void wma_set_twt_peer_caps(tpAddStaParams params, + struct peer_assoc_params *cmd); + +/** + * wma_send_twt_disable_cmd() - Send TWT disable command to firmware + * @pdev_id: pdev id + * + * Return: None + */ +void wma_send_twt_disable_cmd(uint32_t pdev_id); + +/** + * wma_twt_process_add_dialog() - Process twt add dialog command + * @params: add dialog configuration param + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_twt_process_add_dialog(struct wmi_twt_add_dialog_param *params); + +/** + * wma_twt_process_del_dialog() - Process del dialog command + * @params: del dialog configuration param + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_twt_process_del_dialog(struct wmi_twt_del_dialog_param *params); + +#else +static inline void wma_send_twt_enable_cmd(uint32_t pdev_id, + uint32_t congestion_timeout, + bool bcast_val) +{ + WMA_LOGD(FL("TWT not supported as WLAN_SUPPORT_TWT is disabled")); +} + +static inline void wma_send_twt_disable_cmd(uint32_t pdev_id) +{ +} + +static inline void wma_set_twt_peer_caps(tpAddStaParams params, + struct peer_assoc_params *cmd) +{ +} + +static inline QDF_STATUS wma_twt_process_add_dialog( + struct wmi_twt_add_dialog_param *params) +{ + WMA_LOGD(FL("TWT not supported as WLAN_SUPPORT_TWT is disabled")); + + return QDF_STATUS_E_INVAL; +} + +static inline QDF_STATUS wma_twt_process_del_dialog( + struct wmi_twt_del_dialog_param *params) +{ + WMA_LOGD(FL("TWT not supported as WLAN_SUPPORT_TWT is disabled")); + + return QDF_STATUS_E_INVAL; +} +#endif + +#endif /* __WMA_HE_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/inc/wma_types.h b/drivers/staging/qcacld-3.0/core/wma/inc/wma_types.h new file mode 100644 index 0000000000000000000000000000000000000000..f695c39674fdad9c5f49b0e729c8116e0f46ee2a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/inc/wma_types.h @@ -0,0 +1,779 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_QCT_WMA_H +#define WLAN_QCT_WMA_H + +#include "ani_global.h" + +#include "wma_api.h" +#include "wma_tgt_cfg.h" +#include "i_cds_packet.h" + +#define IS_FEATURE_SUPPORTED_BY_FW(feat_enum_value) \ + wma_get_fw_wlan_feat_caps(feat_enum_value) + +#define IS_IBSS_HEARTBEAT_OFFLOAD_FEATURE_ENABLE 1 + +#define DPU_FEEDBACK_UNPROTECTED_ERROR 0x0F + +#define WMA_GET_RX_MAC_HEADER(pRxMeta) \ + (tpSirMacMgmtHdr)(((t_packetmeta *)pRxMeta)->mpdu_hdr_ptr) + +#define WMA_GET_RX_MPDUHEADER3A(pRxMeta) \ + (tpSirMacDataHdr3a)(((t_packetmeta *)pRxMeta)->mpdu_hdr_ptr) + +#define WMA_GET_RX_MPDU_HEADER_LEN(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->mpdu_hdr_len) + +#define WMA_GET_RX_MPDU_LEN(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->mpdu_len) + +#define WMA_GET_RX_PAYLOAD_LEN(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->mpdu_data_len) + +#define WMA_GET_RX_TSF_DELTA(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->tsf_delta) + +#define WMA_GET_RX_MAC_RATE_IDX(pRxMeta) 0 + +#define WMA_GET_RX_MPDU_DATA(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->mpdu_data_ptr) + +#define WMA_GET_RX_UNKNOWN_UCAST(pRxMeta) 0 + +#define WMA_GET_RX_FREQ(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->frequency) + +#define WMA_GET_RX_FT_DONE(pRxMeta) 0 + +#define WMA_GET_RX_DPU_FEEDBACK(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->dpuFeedback) + +#define WMA_GET_RX_BEACON_SENT(pRxMeta) 0 + +#define WMA_GET_RX_TSF_LATER(pRxMeta) 0 + +#define WMA_GET_RX_TIMESTAMP(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->timestamp) + +#define WMA_GET_OFFLOADSCANLEARN(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->offloadScanLearn) +#define WMA_GET_ROAMCANDIDATEIND(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->roamCandidateInd) +#define WMA_GET_SESSIONID(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->session_id) +#define WMA_GET_SCAN_SRC(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->scan_src) + +#ifdef FEATURE_WLAN_EXTSCAN +#define WMA_IS_EXTSCAN_SCAN_SRC(pRxMeta) \ + ((((t_packetmeta *)pRxMeta)->scan_src) == WMI_MGMT_RX_HDR_EXTSCAN) +#define WMA_IS_EPNO_SCAN_SRC(pRxMeta) \ + ((((t_packetmeta *)pRxMeta)->scan_src) & WMI_MGMT_RX_HDR_ENLO) +#endif /* FEATURE_WLAN_EXTSCAN */ + +#define WMA_GET_RX_SNR(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->snr) + +#define WMA_GET_RX_RFBAND(pRxMeta) 0 + +#define WMA_MAX_TXPOWER_INVALID 127 +/* rssi value normalized to noise floor of -96 dBm */ +#define WMA_GET_RX_RSSI_NORMALIZED(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->rssi) + +/* raw rssi based on actual noise floor in hardware */ +#define WMA_GET_RX_RSSI_RAW(pRxMeta) \ + (((t_packetmeta *)pRxMeta)->rssi_raw) + +/* + * the repeat_cnt is reserved by FW team, the current value + * is always 0xffffffff + */ +#define WMI_WOW_PULSE_REPEAT_CNT 0xffffffff + + +/* WMA Messages */ +#define WMA_MSG_TYPES_BEGIN SIR_HAL_MSG_TYPES_BEGIN +#define WMA_ITC_MSG_TYPES_BEGIN SIR_HAL_ITC_MSG_TYPES_BEGIN +#define WMA_RADAR_DETECTED_IND SIR_HAL_RADAR_DETECTED_IND + +#define WMA_ADD_STA_REQ SIR_HAL_ADD_STA_REQ +#define WMA_ADD_STA_RSP SIR_HAL_ADD_STA_RSP +#define WMA_DELETE_STA_REQ SIR_HAL_DELETE_STA_REQ +#define WMA_DELETE_STA_RSP SIR_HAL_DELETE_STA_RSP +#define WMA_ADD_BSS_REQ SIR_HAL_ADD_BSS_REQ +#define WMA_DELETE_BSS_REQ SIR_HAL_DELETE_BSS_REQ +#define WMA_DELETE_BSS_HO_FAIL_REQ SIR_HAL_DELETE_BSS_HO_FAIL_REQ +#define WMA_DELETE_BSS_RSP SIR_HAL_DELETE_BSS_RSP +#define WMA_DELETE_BSS_HO_FAIL_RSP SIR_HAL_DELETE_BSS_HO_FAIL_RSP +#define WMA_SEND_BEACON_REQ SIR_HAL_SEND_BEACON_REQ +#define WMA_SEND_BCN_RSP SIR_HAL_SEND_BCN_RSP +#define WMA_SEND_PROBE_RSP_TMPL SIR_HAL_SEND_PROBE_RSP_TMPL +#define WMA_ROAM_BLACLIST_MSG SIR_HAL_ROAM_BLACKLIST_MSG +#define WMA_SEND_PEER_UNMAP_CONF SIR_HAL_SEND_PEER_UNMAP_CONF + +#define WMA_SET_BSSKEY_RSP SIR_HAL_SET_BSSKEY_RSP +#define WMA_SET_STAKEY_RSP SIR_HAL_SET_STAKEY_RSP +#define WMA_UPDATE_EDCA_PROFILE_IND SIR_HAL_UPDATE_EDCA_PROFILE_IND + +#define WMA_UPDATE_BEACON_IND SIR_HAL_UPDATE_BEACON_IND +#define WMA_CHNL_SWITCH_REQ SIR_HAL_CHNL_SWITCH_REQ +#define WMA_ADD_TS_REQ SIR_HAL_ADD_TS_REQ +#define WMA_DEL_TS_REQ SIR_HAL_DEL_TS_REQ + +#define WMA_MISSED_BEACON_IND SIR_HAL_MISSED_BEACON_IND + +#define WMA_HIDDEN_SSID_RESTART_RSP SIR_HAL_HIDDEN_SSID_RESTART_RSP +#define WMA_SWITCH_CHANNEL_RSP SIR_HAL_SWITCH_CHANNEL_RSP +#define WMA_P2P_NOA_ATTR_IND SIR_HAL_P2P_NOA_ATTR_IND +#define WMA_PWR_SAVE_CFG SIR_HAL_PWR_SAVE_CFG + +#define WMA_IBSS_STA_ADD SIR_HAL_IBSS_STA_ADD +#define WMA_TIMER_ADJUST_ADAPTIVE_THRESHOLD_IND SIR_HAL_TIMER_ADJUST_ADAPTIVE_THRESHOLD_IND +#define WMA_SET_LINK_STATE SIR_HAL_SET_LINK_STATE +#define WMA_SET_STA_BCASTKEY_RSP SIR_HAL_SET_STA_BCASTKEY_RSP +#define WMA_ADD_TS_RSP SIR_HAL_ADD_TS_RSP +#define WMA_DPU_MIC_ERROR SIR_HAL_DPU_MIC_ERROR +#define WMA_TIMER_CHIP_MONITOR_TIMEOUT SIR_HAL_TIMER_CHIP_MONITOR_TIMEOUT +#define WMA_TIMER_TRAFFIC_ACTIVITY_REQ SIR_HAL_TIMER_TRAFFIC_ACTIVITY_REQ +#define WMA_TIMER_ADC_RSSI_STATS SIR_HAL_TIMER_ADC_RSSI_STATS +#define WMA_TIMER_TRAFFIC_STATS_IND SIR_HAL_TRAFFIC_STATS_IND + +#ifdef WLAN_FEATURE_11W +#define WMA_EXCLUDE_UNENCRYPTED_IND SIR_HAL_EXCLUDE_UNENCRYPTED_IND +#endif + +#ifdef FEATURE_WLAN_ESE +#define WMA_TSM_STATS_REQ SIR_HAL_TSM_STATS_REQ +#define WMA_TSM_STATS_RSP SIR_HAL_TSM_STATS_RSP +#endif + +#define WMA_ROAM_SCAN_CH_REQ SIR_HAL_ROAM_SCAN_CH_REQ + +#define WMA_HT40_OBSS_SCAN_IND SIR_HAL_HT40_OBSS_SCAN_IND + +#define WMA_SET_MIMOPS_REQ SIR_HAL_SET_MIMOPS_REQ +#define WMA_SET_MIMOPS_RSP SIR_HAL_SET_MIMOPS_RSP +#define WMA_SYS_READY_IND SIR_HAL_SYS_READY_IND +#define WMA_SET_TX_POWER_REQ SIR_HAL_SET_TX_POWER_REQ +#define WMA_SET_TX_POWER_RSP SIR_HAL_SET_TX_POWER_RSP +#define WMA_GET_TX_POWER_REQ SIR_HAL_GET_TX_POWER_REQ +#define WMA_SEND_MAX_TX_POWER SIR_HAL_SEND_MAX_TX_POWER + +#define WMA_ENABLE_UAPSD_REQ SIR_HAL_ENABLE_UAPSD_REQ +#define WMA_DISABLE_UAPSD_REQ SIR_HAL_DISABLE_UAPSD_REQ + +#define WMA_SET_KEY_DONE SIR_HAL_SET_KEY_DONE + +/* / PE <-> HAL BTC messages */ +#define WMA_BTC_SET_CFG SIR_HAL_BTC_SET_CFG +#define WMA_HANDLE_FW_MBOX_RSP SIR_HAL_HANDLE_FW_MBOX_RSP + +#define WMA_SET_MAX_TX_POWER_REQ SIR_HAL_SET_MAX_TX_POWER_REQ +#define WMA_SET_MAX_TX_POWER_RSP SIR_HAL_SET_MAX_TX_POWER_RSP +#define WMA_SET_DTIM_PERIOD SIR_HAL_SET_DTIM_PERIOD + +#define WMA_SET_MAX_TX_POWER_PER_BAND_REQ \ + SIR_HAL_SET_MAX_TX_POWER_PER_BAND_REQ + +/* / PE <-> HAL Host Offload message */ +#define WMA_SET_HOST_OFFLOAD SIR_HAL_SET_HOST_OFFLOAD + +/* / PE <-> HAL Keep Alive message */ +#define WMA_SET_KEEP_ALIVE SIR_HAL_SET_KEEP_ALIVE + +#ifdef WLAN_NS_OFFLOAD +#define WMA_SET_NS_OFFLOAD SIR_HAL_SET_NS_OFFLOAD +#endif /* WLAN_NS_OFFLOAD */ + +#ifdef FEATURE_WLAN_TDLS +#define WMA_SET_TDLS_LINK_ESTABLISH_REQ SIR_HAL_TDLS_LINK_ESTABLISH_REQ +#define WMA_SET_TDLS_LINK_ESTABLISH_REQ_RSP SIR_HAL_TDLS_LINK_ESTABLISH_REQ_RSP +#endif + +#define WMA_WLAN_SUSPEND_IND SIR_HAL_WLAN_SUSPEND_IND +#define WMA_WLAN_RESUME_REQ SIR_HAL_WLAN_RESUME_REQ +#define WMA_MSG_TYPES_END SIR_HAL_MSG_TYPES_END + +#define WMA_AGGR_QOS_REQ SIR_HAL_AGGR_QOS_REQ +#define WMA_AGGR_QOS_RSP SIR_HAL_AGGR_QOS_RSP + +#define WMA_CSA_OFFLOAD_EVENT SIR_CSA_OFFLOAD_EVENT + +#ifdef FEATURE_WLAN_ESE +#define WMA_SET_PLM_REQ SIR_HAL_SET_PLM_REQ +#endif + +#define WMA_ROAM_SCAN_OFFLOAD_REQ SIR_HAL_ROAM_SCAN_OFFLOAD_REQ +#define WMA_ROAM_PRE_AUTH_STATUS SIR_HAL_ROAM_PRE_AUTH_STATUS_IND + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +#define WMA_ROAM_OFFLOAD_SYNCH_IND SIR_HAL_ROAM_OFFLOAD_SYNCH_IND +#define WMA_ROAM_OFFLOAD_SYNCH_FAIL SIR_HAL_ROAM_OFFLOAD_SYNCH_FAIL +#endif + +#define WMA_8023_MULTICAST_LIST_REQ SIR_HAL_8023_MULTICAST_LIST_REQ + +#ifdef WLAN_FEATURE_PACKET_FILTERING +#define WMA_RECEIVE_FILTER_SET_FILTER_REQ SIR_HAL_RECEIVE_FILTER_SET_FILTER_REQ +#define WMA_PACKET_COALESCING_FILTER_MATCH_COUNT_REQ SIR_HAL_PACKET_COALESCING_FILTER_MATCH_COUNT_REQ +#define WMA_PACKET_COALESCING_FILTER_MATCH_COUNT_RSP SIR_HAL_PACKET_COALESCING_FILTER_MATCH_COUNT_RSP +#define WMA_RECEIVE_FILTER_CLEAR_FILTER_REQ SIR_HAL_RECEIVE_FILTER_CLEAR_FILTER_REQ +#endif /* WLAN_FEATURE_PACKET_FILTERING */ + +#define WMA_DHCP_START_IND SIR_HAL_DHCP_START_IND +#define WMA_DHCP_STOP_IND SIR_HAL_DHCP_STOP_IND + +#define WMA_TX_FAIL_MONITOR_IND SIR_HAL_TX_FAIL_MONITOR_IND + +#ifdef WLAN_FEATURE_GTK_OFFLOAD +#define WMA_GTK_OFFLOAD_REQ SIR_HAL_GTK_OFFLOAD_REQ +#define WMA_GTK_OFFLOAD_GETINFO_REQ SIR_HAL_GTK_OFFLOAD_GETINFO_REQ +#define WMA_GTK_OFFLOAD_GETINFO_RSP SIR_HAL_GTK_OFFLOAD_GETINFO_RSP +#endif /* WLAN_FEATURE_GTK_OFFLOAD */ + +#define WMA_SET_TM_LEVEL_REQ SIR_HAL_SET_TM_LEVEL_REQ + +#define WMA_UPDATE_OP_MODE SIR_HAL_UPDATE_OP_MODE +#define WMA_UPDATE_RX_NSS SIR_HAL_UPDATE_RX_NSS +#define WMA_UPDATE_MEMBERSHIP SIR_HAL_UPDATE_MEMBERSHIP +#define WMA_UPDATE_USERPOS SIR_HAL_UPDATE_USERPOS + +#ifdef WLAN_FEATURE_NAN +#define WMA_NAN_REQUEST SIR_HAL_NAN_REQUEST +#endif + +#define WMA_START_SCAN_OFFLOAD_REQ SIR_HAL_START_SCAN_OFFLOAD_REQ +#define WMA_STOP_SCAN_OFFLOAD_REQ SIR_HAL_STOP_SCAN_OFFLOAD_REQ +#define WMA_UPDATE_CHAN_LIST_REQ SIR_HAL_UPDATE_CHAN_LIST_REQ +#define WMA_RX_SCAN_EVENT SIR_HAL_RX_SCAN_EVENT +#define WMA_RX_CHN_STATUS_EVENT SIR_HAL_RX_CHN_STATUS_EVENT +#define WMA_IBSS_PEER_INACTIVITY_IND SIR_HAL_IBSS_PEER_INACTIVITY_IND + +#define WMA_CLI_SET_CMD SIR_HAL_CLI_SET_CMD + +#ifndef REMOVE_PKT_LOG +#define WMA_PKTLOG_ENABLE_REQ SIR_HAL_PKTLOG_ENABLE_REQ +#endif + +#ifdef FEATURE_WLAN_LPHB +#define WMA_LPHB_CONF_REQ SIR_HAL_LPHB_CONF_IND +#endif /* FEATURE_WLAN_LPHB */ + +#ifdef FEATURE_WLAN_CH_AVOID +#define WMA_CH_AVOID_UPDATE_REQ SIR_HAL_CH_AVOID_UPDATE_REQ +#endif /* FEATURE_WLAN_CH_AVOID */ + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +#define WMA_SET_AUTO_SHUTDOWN_TIMER_REQ SIR_HAL_SET_AUTO_SHUTDOWN_TIMER_REQ +#endif + +#define WMA_ADD_PERIODIC_TX_PTRN_IND SIR_HAL_ADD_PERIODIC_TX_PTRN_IND +#define WMA_DEL_PERIODIC_TX_PTRN_IND SIR_HAL_DEL_PERIODIC_TX_PTRN_IND + +#define WMA_TX_POWER_LIMIT SIR_HAL_SET_TX_POWER_LIMIT + +#define WMA_RATE_UPDATE_IND SIR_HAL_RATE_UPDATE_IND + +#define WMA_SEND_ADDBA_REQ SIR_HAL_SEND_ADDBA_REQ +#define WMA_INIT_THERMAL_INFO_CMD SIR_HAL_INIT_THERMAL_INFO_CMD +#define WMA_SET_THERMAL_LEVEL SIR_HAL_SET_THERMAL_LEVEL +#define WMA_RMC_ENABLE_IND SIR_HAL_RMC_ENABLE_IND +#define WMA_RMC_DISABLE_IND SIR_HAL_RMC_DISABLE_IND +#define WMA_RMC_ACTION_PERIOD_IND SIR_HAL_RMC_ACTION_PERIOD_IND + +/* IBSS peer info related message */ +#define WMA_GET_IBSS_PEER_INFO_REQ SIR_HAL_IBSS_PEER_INFO_REQ + +#define WMA_IBSS_CESIUM_ENABLE_IND SIR_HAL_IBSS_CESIUM_ENABLE_IND + +#define WMA_INIT_BAD_PEER_TX_CTL_INFO_CMD SIR_HAL_BAD_PEER_TX_CTL_INI_CMD + +#ifdef FEATURE_WLAN_TDLS +#define WMA_UPDATE_TDLS_PEER_STATE SIR_HAL_UPDATE_TDLS_PEER_STATE +#define WMA_TDLS_SHOULD_DISCOVER_CMD SIR_HAL_TDLS_SHOULD_DISCOVER +#define WMA_TDLS_SHOULD_TEARDOWN_CMD SIR_HAL_TDLS_SHOULD_TEARDOWN +#define WMA_TDLS_PEER_DISCONNECTED_CMD SIR_HAL_TDLS_PEER_DISCONNECTED +#endif +#define WMA_SET_SAP_INTRABSS_DIS SIR_HAL_SET_SAP_INTRABSS_DIS + +/* Message to indicate beacon tx completion after beacon template update + * beacon offload case + */ +#define WMA_DFS_BEACON_TX_SUCCESS_IND SIR_HAL_BEACON_TX_SUCCESS_IND +#define WMA_DISASSOC_TX_COMP SIR_HAL_DISASSOC_TX_COMP +#define WMA_DEAUTH_TX_COMP SIR_HAL_DEAUTH_TX_COMP + +#define WMA_GET_PEER_INFO_EXT SIR_HAL_GET_PEER_INFO_EXT + +#define WMA_GET_ISOLATION SIR_HAL_GET_ISOLATION + +#define WMA_MODEM_POWER_STATE_IND SIR_HAL_MODEM_POWER_STATE_IND + +#ifdef WLAN_FEATURE_STATS_EXT +#define WMA_STATS_EXT_REQUEST SIR_HAL_STATS_EXT_REQUEST +#endif + +#define WMA_GET_TEMPERATURE_REQ SIR_HAL_GET_TEMPERATURE_REQ +#define WMA_SET_WISA_PARAMS SIR_HAL_SET_WISA_PARAMS + +#ifdef FEATURE_WLAN_EXTSCAN +#define WMA_EXTSCAN_GET_CAPABILITIES_REQ SIR_HAL_EXTSCAN_GET_CAPABILITIES_REQ +#define WMA_EXTSCAN_START_REQ SIR_HAL_EXTSCAN_START_REQ +#define WMA_EXTSCAN_STOP_REQ SIR_HAL_EXTSCAN_STOP_REQ +#define WMA_EXTSCAN_SET_BSSID_HOTLIST_REQ SIR_HAL_EXTSCAN_SET_BSS_HOTLIST_REQ +#define WMA_EXTSCAN_RESET_BSSID_HOTLIST_REQ SIR_HAL_EXTSCAN_RESET_BSS_HOTLIST_REQ +#define WMA_EXTSCAN_SET_SIGNF_CHANGE_REQ SIR_HAL_EXTSCAN_SET_SIGNF_CHANGE_REQ +#define WMA_EXTSCAN_RESET_SIGNF_CHANGE_REQ SIR_HAL_EXTSCAN_RESET_SIGNF_CHANGE_REQ +#define WMA_EXTSCAN_GET_CACHED_RESULTS_REQ SIR_HAL_EXTSCAN_GET_CACHED_RESULTS_REQ +#define WMA_SET_EPNO_LIST_REQ SIR_HAL_SET_EPNO_LIST_REQ +#define WMA_SET_PASSPOINT_LIST_REQ SIR_HAL_SET_PASSPOINT_LIST_REQ +#define WMA_RESET_PASSPOINT_LIST_REQ SIR_HAL_RESET_PASSPOINT_LIST_REQ + +#endif /* FEATURE_WLAN_EXTSCAN */ + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS +#define WMA_LINK_LAYER_STATS_CLEAR_REQ SIR_HAL_LL_STATS_CLEAR_REQ +#define WMA_LINK_LAYER_STATS_SET_REQ SIR_HAL_LL_STATS_SET_REQ +#define WMA_LINK_LAYER_STATS_GET_REQ SIR_HAL_LL_STATS_GET_REQ +#define WMA_LINK_LAYER_STATS_RESULTS_RSP SIR_HAL_LL_STATS_RESULTS_RSP +#define WDA_LINK_LAYER_STATS_SET_THRESHOLD SIR_HAL_LL_STATS_EXT_SET_THRESHOLD +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +#define WMA_LINK_STATUS_GET_REQ SIR_HAL_LINK_STATUS_GET_REQ + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +#define WMA_WLAN_EXT_WOW SIR_HAL_CONFIG_EXT_WOW +#define WMA_WLAN_SET_APP_TYPE1_PARAMS SIR_HAL_CONFIG_APP_TYPE1_PARAMS +#define WMA_WLAN_SET_APP_TYPE2_PARAMS SIR_HAL_CONFIG_APP_TYPE2_PARAMS +#endif + +#define WMA_SET_SCAN_MAC_OUI_REQ SIR_HAL_SET_SCAN_MAC_OUI_REQ +#define WMA_TSF_GPIO_PIN SIR_HAL_TSF_GPIO_PIN_REQ + +#ifdef DHCP_SERVER_OFFLOAD +#define WMA_SET_DHCP_SERVER_OFFLOAD_CMD SIR_HAL_SET_DHCP_SERVER_OFFLOAD +#endif /* DHCP_SERVER_OFFLOAD */ + +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING +#define WMA_LED_FLASHING_REQ SIR_HAL_LED_FLASHING_REQ +#endif + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +#define WMA_UPDATE_Q2Q_IE_IND SIR_HAL_UPDATE_Q2Q_IE_IND +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ +#define WMA_SET_RSSI_MONITOR_REQ SIR_HAL_SET_RSSI_MONITOR_REQ + +#define WMA_OCB_SET_CONFIG_CMD SIR_HAL_OCB_SET_CONFIG_CMD +#define WMA_OCB_SET_UTC_TIME_CMD SIR_HAL_OCB_SET_UTC_TIME_CMD +#define WMA_OCB_START_TIMING_ADVERT_CMD SIR_HAL_OCB_START_TIMING_ADVERT_CMD +#define WMA_OCB_STOP_TIMING_ADVERT_CMD SIR_HAL_OCB_STOP_TIMING_ADVERT_CMD +#define WMA_OCB_GET_TSF_TIMER_CMD SIR_HAL_OCB_GET_TSF_TIMER_CMD +#define WMA_DCC_GET_STATS_CMD SIR_HAL_DCC_GET_STATS_CMD +#define WMA_DCC_CLEAR_STATS_CMD SIR_HAL_DCC_CLEAR_STATS_CMD +#define WMA_DCC_UPDATE_NDL_CMD SIR_HAL_DCC_UPDATE_NDL_CMD +#define WMA_SET_IE_INFO SIR_HAL_SET_IE_INFO + +#define WMA_LRO_CONFIG_CMD SIR_HAL_LRO_CONFIG_CMD +#define WMA_GW_PARAM_UPDATE_REQ SIR_HAL_GATEWAY_PARAM_UPDATE_REQ +#define WMA_ADD_BCN_FILTER_CMDID SIR_HAL_ADD_BCN_FILTER_CMDID +#define WMA_REMOVE_BCN_FILTER_CMDID SIR_HAL_REMOVE_BCN_FILTER_CMDID +#define WMA_SET_ADAPT_DWELLTIME_CONF_PARAMS SIR_HAL_SET_ADAPT_DWELLTIME_PARAMS + +#define WDA_APF_GET_CAPABILITIES_REQ SIR_HAL_APF_GET_CAPABILITIES_REQ +#define WMA_ROAM_SYNC_TIMEOUT SIR_HAL_WMA_ROAM_SYNC_TIMEOUT + +#define WMA_SET_PDEV_IE_REQ SIR_HAL_SET_PDEV_IE_REQ +#define WMA_UPDATE_WEP_DEFAULT_KEY SIR_HAL_UPDATE_WEP_DEFAULT_KEY +#define WMA_SEND_FREQ_RANGE_CONTROL_IND SIR_HAL_SEND_FREQ_RANGE_CONTROL_IND +#define WMA_POWER_DEBUG_STATS_REQ SIR_HAL_POWER_DEBUG_STATS_REQ +#define WMA_BEACON_DEBUG_STATS_REQ SIR_HAL_BEACON_DEBUG_STATS_REQ +#define WMA_GET_RCPI_REQ SIR_HAL_GET_RCPI_REQ +#define WMA_ROAM_BLACKLIST_MSG SIR_HAL_ROAM_BLACKLIST_MSG + +#define WMA_SET_DBS_SCAN_SEL_CONF_PARAMS SIR_HAL_SET_DBS_SCAN_SEL_PARAMS + +#define WMA_SET_WOW_PULSE_CMD SIR_HAL_SET_WOW_PULSE_CMD + +#define WMA_SET_PER_ROAM_CONFIG_CMD SIR_HAL_SET_PER_ROAM_CONFIG_CMD + +#define WMA_SEND_AP_VDEV_UP SIR_HAL_SEND_AP_VDEV_UP + +#define WMA_SET_ARP_STATS_REQ SIR_HAL_SET_ARP_STATS_REQ +#define WMA_GET_ARP_STATS_REQ SIR_HAL_GET_ARP_STATS_REQ +#define WMA_SET_LIMIT_OFF_CHAN SIR_HAL_SET_LIMIT_OFF_CHAN +#define WMA_OBSS_DETECTION_REQ SIR_HAL_OBSS_DETECTION_REQ +#define WMA_OBSS_DETECTION_INFO SIR_HAL_OBSS_DETECTION_INFO +#define WMA_INVOKE_NEIGHBOR_REPORT SIR_HAL_INVOKE_NEIGHBOR_REPORT +#define WMA_OBSS_COLOR_COLLISION_REQ SIR_HAL_OBSS_COLOR_COLLISION_REQ +#define WMA_OBSS_COLOR_COLLISION_INFO SIR_HAL_OBSS_COLOR_COLLISION_INFO + +#define WMA_CFG_VENDOR_ACTION_TB_PPDU SIR_HAL_CFG_VENDOR_ACTION_TB_PPDU +#define WMA_GET_ROAM_SCAN_STATS SIR_HAL_GET_ROAM_SCAN_STATS + +#ifdef WLAN_FEATURE_MOTION_DETECTION +#define WMA_SET_MOTION_DET_CONFIG SIR_HAL_SET_MOTION_DET_CONFIG +#define WMA_SET_MOTION_DET_ENABLE SIR_HAL_SET_MOTION_DET_ENABLE +#define WMA_SET_MOTION_DET_BASE_LINE_CONFIG \ + SIR_HAL_SET_MOTION_DET_BASE_LINE_CONFIG +#define WMA_SET_MOTION_DET_BASE_LINE_ENABLE \ + SIR_HAL_SET_MOTION_DET_BASE_LINE_ENABLE +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +#ifdef FW_THERMAL_THROTTLE_SUPPORT +#define WMA_SET_THERMAL_THROTTLE_CFG SIR_HAL_SET_THERMAL_THROTTLE_CFG +#define WMA_SET_THERMAL_MGMT SIR_HAL_SET_THERMAL_MGMT +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + +#ifdef WLAN_MWS_INFO_DEBUGFS +#define WMA_GET_MWS_COEX_INFO_REQ SIR_HAL_GET_MWS_COEX_INFO_REQ +#endif +#define WMA_SET_ROAM_TRIGGERS SIR_HAL_SET_ROAM_TRIGGERS + +#define WMA_ROAM_INIT_PARAM SIR_HAL_INIT_ROAM_OFFLOAD_PARAM + +#define WMA_DATA_STALL_TRIGGER 6 + +/* Bit 6 will be used to control BD rate for Management frames */ +#define HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME 0x40 + +#define wma_tx_frame(hHal, pFrmBuf, frmLen, frmType, txDir, tid, pCompFunc, \ + pData, txFlag, sessionid, channel_freq, rid) \ + (QDF_STATUS)( wma_tx_packet( \ + cds_get_context(QDF_MODULE_ID_WMA), \ + (pFrmBuf), \ + (frmLen), \ + (frmType), \ + (txDir), \ + (tid), \ + (pCompFunc), \ + (pData), \ + (NULL), \ + (txFlag), \ + (sessionid), \ + (false), \ + (channel_freq), \ + (rid))) + +#define wma_tx_frameWithTxComplete(hHal, pFrmBuf, frmLen, frmType, txDir, tid, \ + pCompFunc, pData, pCBackFnTxComp, txFlag, sessionid, tdlsflag, \ + channel_freq, rid) \ + (QDF_STATUS)( wma_tx_packet( \ + cds_get_context(QDF_MODULE_ID_WMA), \ + (pFrmBuf), \ + (frmLen), \ + (frmType), \ + (txDir), \ + (tid), \ + (pCompFunc), \ + (pData), \ + (pCBackFnTxComp), \ + (txFlag), \ + (sessionid), \ + (tdlsflag), \ + (channel_freq), \ + (rid))) + +/** + * struct sUapsd_Params - Powersave Offload Changes + * @bkDeliveryEnabled: BK delivery enabled flag + * @beDeliveryEnabled: BE delivery enabled flag + * @viDeliveryEnabled: VI delivery enabled flag + * @voDeliveryEnabled: VO delivery enabled flag + * @bkTriggerEnabled: BK trigger enabled flag + * @beTriggerEnabled: BE trigger enabled flag + * @viTriggerEnabled: VI trigger enabled flag + * @voTriggerEnabled: VO trigger enabled flag + */ +typedef struct sUapsd_Params { + uint8_t bkDeliveryEnabled:1; + uint8_t beDeliveryEnabled:1; + uint8_t viDeliveryEnabled:1; + uint8_t voDeliveryEnabled:1; + uint8_t bkTriggerEnabled:1; + uint8_t beTriggerEnabled:1; + uint8_t viTriggerEnabled:1; + uint8_t voTriggerEnabled:1; + bool enable_ps; +} tUapsd_Params, *tpUapsd_Params; + +/** + * struct sEnablePsParams - Enable PowerSave Params + * @psSetting: power save setting + * @uapsdParams: UAPSD Parameters + * @sessionid: sme session id / vdev id + */ +typedef struct sEnablePsParams { + tSirAddonPsReq psSetting; + tUapsd_Params uapsdParams; + uint32_t sessionid; +} tEnablePsParams, *tpEnablePsParams; + +/** + * struct sDisablePsParams - Disable PowerSave Params + * @psSetting: power save setting + * @sessionid: sme session id / vdev id + */ +typedef struct sDisablePsParams { + tSirAddonPsReq psSetting; + uint32_t sessionid; +} tDisablePsParams, *tpDisablePsParams; + +/** + * struct sEnableUapsdParams - Enable Uapsd Params + * @uapsdParams: UAPSD parameters + * @bssid: mac address + * @sessionid: sme session id/ vdev id + * @status: success/failure + */ +typedef struct sEnableUapsdParams { + tUapsd_Params uapsdParams; + tSirMacAddr bssid; + uint32_t sessionid; + uint32_t status; +} tEnableUapsdParams, *tpEnableUapsdParams; + +/** + * struct sDisableUapsdParams - Disable Uapsd Params + * @bssid: mac address + * @sessionid: sme session id/ vdev id + * @status: success/failure + */ +typedef struct sDisableUapsdParams { + tSirMacAddr bssid; + uint32_t sessionid; + uint32_t status; +} tDisableUapsdParams, *tpDisableUapsdParams; + +/** + * wma_tx_dwnld_comp_callback - callback function for TX dwnld complete + * @context: global mac pointer + * @buf: buffer + * @bFreeData: to free/not free the buffer + * + * callback function for mgmt tx download completion. + * + * Return: QDF_STATUS_SUCCESS in case of success + */ +typedef QDF_STATUS (*wma_tx_dwnld_comp_callback)(void *context, qdf_nbuf_t buf, + bool bFreeData); + +/** + * wma_tx_ota_comp_callback - callback function for TX complete + * @context: global mac pointer + * @buf: buffer + * @status: tx completion status + * @params: tx completion params + * + * callback function for mgmt tx ota completion. + * + * Return: QDF_STATUS_SUCCESS in case of success + */ +typedef QDF_STATUS (*wma_tx_ota_comp_callback)(void *context, qdf_nbuf_t buf, + uint32_t status, void *params); + +/* generic callback for updating parameters from target to HDD */ +typedef int (*wma_tgt_cfg_cb)(hdd_handle_t handle, struct wma_tgt_cfg *cfg); + +/** + * struct wma_cli_set_cmd_t - set command parameters + * @param_id: parameter id + * @param_value: parameter value + * @param_sec_value: parameter sec value + * @param_vdev_id: parameter vdev id + * @param_vp_dev: is it per vdev/pdev + */ +typedef struct { + uint32_t param_id; + uint32_t param_value; + uint32_t param_sec_value; + uint32_t param_vdev_id; + uint32_t param_vp_dev; +} wma_cli_set_cmd_t; + +enum rateid { + RATEID_1MBPS = 0, + RATEID_2MBPS, + RATEID_5_5MBPS, + RATEID_11MBPS, + RATEID_6MBPS, + RATEID_9MBPS, + RATEID_12MBPS, + RATEID_18MBPS, + RATEID_24MBPS, + RATEID_36MBPS, + RATEID_48MBPS = 10, + RATEID_54MBPS, + RATEID_DEFAULT +}; + +QDF_STATUS wma_post_ctrl_msg(struct mac_context *mac, struct scheduler_msg *pMsg); + +QDF_STATUS u_mac_post_ctrl_msg(void *pSirGlobal, tSirMbMsg *pMb); + +QDF_STATUS wma_set_idle_ps_config(void *wma_ptr, uint32_t idle_ps); +QDF_STATUS wma_get_snr(tAniGetSnrReq *psnr_req); + +/** + * wma_get_rx_retry_cnt() - API to get rx retry count from data path + * @mac: pointer to mac context + * @vdev_id: vdev id + * @mac_addr: mac address of the remote station + * + * Return: none + */ +void wma_get_rx_retry_cnt(struct mac_context *mac, uint8_t vdev_id, + uint8_t *mac_addr); + +/** + * wma_set_wlm_latency_level() - set latency level to FW + * @wma_ptr: wma handle + * @latency_params: latency params + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_set_wlm_latency_level(void *wma_ptr, + struct wlm_latency_level_param *latency_params); + +QDF_STATUS +wma_ds_peek_rx_packet_info + (cds_pkt_t *vosDataBuff, void **ppRxHeader, bool bSwap); + +/** + * wma_tx_abort() - abort tx + * @vdev_id: vdev id + * + * In case of deauth host abort transmitting packet. + * + * Return: none + */ +void wma_tx_abort(uint8_t vdev_id); + +/** + * wma_tx_packet() - Sends Tx Frame to TxRx + * @wma_context: wma context + * @tx_frame: frame buffer + * @frmLen: frame length + * @frmType: frame type + * @txDir: tx diection + * @tid: TID + * @tx_frm_download_comp_cb: tx download callback handler + * @pData: tx packet + * @tx_frm_ota_comp_cb: OTA complition handler + * @tx_flag: tx flag + * @vdev_id: vdev id + * @tdls_flag: tdls flag + * @channel_freq: channel frequency + * @rid: rate id + * + * This function sends the frame corresponding to the + * given vdev id. + * This is blocking call till the downloading of frame is complete. + * + * Return: QDF status + */ +QDF_STATUS wma_tx_packet(void *wma_context, void *tx_frame, uint16_t frmLen, + eFrameType frmType, eFrameTxDir txDir, uint8_t tid, + wma_tx_dwnld_comp_callback tx_frm_download_comp_cb, + void *pData, + wma_tx_ota_comp_callback tx_frm_ota_comp_cb, + uint8_t tx_flag, uint8_t vdev_id, bool tdls_flag, + uint16_t channel_freq, enum rateid rid); + +/** + * wma_open() - Allocate wma context and initialize it. + * @psoc: Psoc pointer + * @pTgtUpdCB: tgt config update callback fun + * @cds_cfg: mac parameters + * @target_type: Target type + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc, + wma_tgt_cfg_cb pTgtUpdCB, struct cds_config_info *cds_cfg, + uint32_t target_type); + +/** + * wma_vdev_init() - initialize a wma vdev + * @vdev: the vdev to initialize + * + * Return: None + */ +void wma_vdev_init(struct wma_txrx_node *vdev); + +/** + * wma_vdev_deinit() - de-initialize a wma vdev + * @vdev: the vdev to de-initialize + * + * Return: None + */ +void wma_vdev_deinit(struct wma_txrx_node *vdev); + +QDF_STATUS wma_register_mgmt_frm_client(void); + +QDF_STATUS wma_de_register_mgmt_frm_client(void); +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS wma_register_roaming_callbacks( + QDF_STATUS (*csr_roam_synch_cb)(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason), + QDF_STATUS (*csr_roam_auth_event_handle_cb)( + struct mac_context *mac, + uint8_t vdev_id, + struct qdf_mac_addr bssid), + QDF_STATUS (*pe_roam_synch_cb)(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason), + QDF_STATUS (*pe_disconnect_cb) (struct mac_context *mac, + uint8_t vdev_id, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len, + uint16_t reason_code), + QDF_STATUS (*csr_roam_pmkid_req_cb)(uint8_t vdev_id, + struct roam_pmkid_req_event *bss_list)); +#else +static inline QDF_STATUS wma_register_roaming_callbacks( + QDF_STATUS (*csr_roam_synch_cb)(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason), + QDF_STATUS (*csr_roam_auth_event_handle_cb)( + struct mac_context *mac, + uint8_t vdev_id, + struct qdf_mac_addr bssid), + QDF_STATUS (*pe_roam_synch_cb)(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason), + QDF_STATUS (*pe_disconnect_cb) (struct mac_context *mac, + uint8_t vdev_id, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len, + uint16_t reason_code), + QDF_STATUS (*csr_roam_pmkid_req_cb)(uint8_t vdev_id, + struct roam_pmkid_req_event *bss_list)) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif + +#endif diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wlan_qct_wma_legacy.c b/drivers/staging/qcacld-3.0/core/wma/src/wlan_qct_wma_legacy.c new file mode 100644 index 0000000000000000000000000000000000000000..9e01fbaad8c1bb330fff52f276695eaa9d494f69 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wlan_qct_wma_legacy.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_qct_wma_legacy.c + * + * This software unit holds the implementation of the WLAN Device Adaptation + * Layer for the legacy functionalities that were part of the old HAL. + * + * The functions externalized by this module are to be called ONLY by other + * WLAN modules that properly register with the Transport Layer initially. + * + */ + +/* Standard include files */ +/* Application Specific include files */ +#include "lim_api.h" +#include "wma.h" +#include "sme_power_save_api.h" +/* Locally used Defines */ + +#define HAL_MMH_MB_MSG_TYPE_MASK 0xFF00 + +/** + * wma_post_ctrl_msg() - Posts WMA messages to MC thread + * @mac: MAC parameters structure + * @pMsg: pointer with message + * + * Return: Success or Failure + */ + +QDF_STATUS wma_post_ctrl_msg(struct mac_context *mac, struct scheduler_msg *pMsg) +{ + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, pMsg)) + return QDF_STATUS_E_FAILURE; + else + return QDF_STATUS_SUCCESS; +} + +/** + * u_mac_post_ctrl_msg() - post ctrl msg + * @pMb: A pointer to the maibox message + * + * Forwards the completely received message to the respective + * modules for further processing. + * + * NOTE: + * This function has been moved to the API file because for MAC running + * on Windows host, the host module will call this routine directly to + * send any mailbox messages. Making this function an API makes sure that + * outside world (any module outside MMH) only calls APIs to use MMH + * services and not an internal function. + * + * Return: success/error code + */ + +QDF_STATUS u_mac_post_ctrl_msg(void *pSirGlobal, tSirMbMsg *pMb) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = (struct mac_context *) pSirGlobal; + + msg.type = pMb->type; + msg.bodyval = 0; + msg.bodyptr = pMb; + + switch (msg.type & HAL_MMH_MB_MSG_TYPE_MASK) { + case WMA_MSG_TYPES_BEGIN: /* Posts a message to the HAL MsgQ */ + status = wma_post_ctrl_msg(mac, &msg); + break; + + case SIR_LIM_MSG_TYPES_BEGIN: /* Posts a message to the LIM MsgQ */ + status = lim_post_msg_api(mac, &msg); + break; + + case SIR_SME_MSG_TYPES_BEGIN: /* Posts a message to the LIM MsgQ */ + status = sme_post_pe_message(mac, &msg); + break; + + default: + WMA_LOGD("Unknown message type = 0x%X\n", msg.type); + qdf_mem_free(msg.bodyptr); + return QDF_STATUS_E_FAILURE; + } + + if (status != QDF_STATUS_SUCCESS) + qdf_mem_free(msg.bodyptr); + + return status; + +} /* u_mac_post_ctrl_msg() */ + +QDF_STATUS umac_send_mb_message_to_mac(void *msg) +{ + void *mac_handle = cds_get_context(QDF_MODULE_ID_SME); + + if (!mac_handle) { + QDF_TRACE_ERROR(QDF_MODULE_ID_SYS, "Invalid MAC handle"); + qdf_mem_free(msg); + return QDF_STATUS_E_FAILURE; + } + + return u_mac_post_ctrl_msg(mac_handle, msg); +} diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_coex.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_coex.c new file mode 100644 index 0000000000000000000000000000000000000000..732692f197946d33c07ce997d38502096064c3de --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_coex.c @@ -0,0 +1,377 @@ + +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "wmi_unified.h" + +/** + * wma_mws_coex_state_host_event_handler - Coex state Event Handler + * @handle: pointer to scn handle + * @event: Coex state event + * @len: Length of cmd + * + * Return: 0 on success, else error on failure + */ +static int wma_mws_coex_state_host_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + WMI_VDEV_GET_MWS_COEX_STATE_EVENTID_param_tlvs *param_tlvs; + wmi_vdev_get_mws_coex_state_fixed_param *param_buf; + struct mws_coex_state coex_state; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + wma_debug("Enter"); + param_tlvs = + (WMI_VDEV_GET_MWS_COEX_STATE_EVENTID_param_tlvs *)event; + if (!param_tlvs) { + wma_err("Invalid stats event"); + return -EINVAL; + } + + param_buf = param_tlvs->fixed_param; + if (!param_buf || !mac || !mac->sme.mws_coex_info_state_resp_callback) { + wma_debug("NULL mac ptr or HDD callback is null"); + return -EINVAL; + } + + if (!param_buf) { + wma_debug("NULL mws coes state event fixed param"); + return -EINVAL; + } + + coex_state.vdev_id = param_buf->vdev_id; + coex_state.coex_scheme_bitmap = param_buf->coex_scheme_bitmap; + coex_state.active_conflict_count = param_buf->active_conflict_count; + coex_state.potential_conflict_count = + param_buf->potential_conflict_count; + coex_state.chavd_group0_bitmap = param_buf->chavd_group0_bitmap; + coex_state.chavd_group1_bitmap = param_buf->chavd_group1_bitmap; + coex_state.chavd_group2_bitmap = param_buf->chavd_group2_bitmap; + coex_state.chavd_group3_bitmap = param_buf->chavd_group3_bitmap; + mac->sme.mws_coex_info_state_resp_callback(&coex_state, + mac->sme.mws_coex_info_ctx, + WMI_MWS_COEX_STATE); + wma_debug("vdev_id = %u coex_scheme_bitmap = %u active_conflict_count = %u potential_conflict_count = %u chavd_group0_bitmap = %u chavd_group1_bitmap = %u chavd_group2_bitmap = %u chavd_group3_bitmap = %u", + param_buf->vdev_id, param_buf->coex_scheme_bitmap, + param_buf->active_conflict_count, + param_buf->potential_conflict_count, + param_buf->chavd_group0_bitmap, + param_buf->chavd_group1_bitmap, + param_buf->chavd_group2_bitmap, + param_buf->chavd_group3_bitmap); + + wma_debug("Exit"); + return 0; +} + +/** + * wma_mws_coex_state_dpwb_event_handler - DPWB state Event Handler + * @handle: handle to scn + * @event: DPWB state event + * @len: Length of cmd + * + * Return: 0 on success, else error on failure + */ +static int wma_mws_coex_state_dpwb_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + WMI_VDEV_GET_MWS_COEX_DPWB_STATE_EVENTID_param_tlvs *param_tlvs; + wmi_vdev_get_mws_coex_dpwb_state_fixed_param *param_buf; + struct mws_coex_dpwb_state coex_dpwb; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + wma_debug("Enter"); + param_tlvs = + (WMI_VDEV_GET_MWS_COEX_DPWB_STATE_EVENTID_param_tlvs *)event; + if (!param_tlvs) { + wma_err("Invalid coex mws event"); + return -EINVAL; + } + + param_buf = param_tlvs->fixed_param; + if (!param_buf || !mac || !mac->sme.mws_coex_info_state_resp_callback) { + wma_debug("NULL mac ptr or HDD callback is null"); + return -EINVAL; + } + + if (!param_buf) { + wma_err("NULL mws dpwb info event fixed param"); + return -EINVAL; + } + + coex_dpwb.vdev_id = param_buf->vdev_id; + coex_dpwb.current_dpwb_state = param_buf->current_dpwb_state; + coex_dpwb.pnp1_value = param_buf->pnp1_value; + coex_dpwb.lte_dutycycle = param_buf->lte_dutycycle; + coex_dpwb.sinr_wlan_on = param_buf->sinr_wlan_on; + coex_dpwb.sinr_wlan_off = param_buf->sinr_wlan_off; + coex_dpwb.bler_count = param_buf->bler_count; + coex_dpwb.block_count = param_buf->block_count; + coex_dpwb.wlan_rssi_level = param_buf->wlan_rssi_level; + coex_dpwb.wlan_rssi = param_buf->wlan_rssi; + coex_dpwb.is_tdm_running = param_buf->is_tdm_running; + mac->sme.mws_coex_info_state_resp_callback(&coex_dpwb, + mac->sme.mws_coex_info_ctx, + WMI_MWS_COEX_DPWB_STATE); + wma_debug("vdev_id = %u current_dpwb_state = %d pnp1_value = %d lte_dutycycle = %d sinr_wlan_on = %d sinr_wlan_off = %d bler_count = %u block_count = %u wlan_rssi_level = %u wlan_rssi = %d is_tdm_running = %u", + param_buf->vdev_id, param_buf->current_dpwb_state, + param_buf->pnp1_value, + param_buf->lte_dutycycle, param_buf->sinr_wlan_on, + param_buf->sinr_wlan_off, param_buf->bler_count, + param_buf->block_count, param_buf->wlan_rssi_level, + param_buf->wlan_rssi, param_buf->is_tdm_running); + + wma_debug("Exit"); + return 0; +} + +/** + * wma_mws_coex_tdm_event_handler - TDM state Event Handler + * @handle: handle to scn + * @event: TDM state event + * @len: Length of cmd + * + * Return: 0 on success, else error on failure + */ +static int wma_mws_coex_tdm_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + WMI_VDEV_GET_MWS_COEX_TDM_STATE_EVENTID_param_tlvs *param_tlvs; + wmi_vdev_get_mws_coex_tdm_state_fixed_param *param_buf; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + struct mws_coex_tdm_state coex_tdm; + + wma_debug("Enter"); + param_tlvs = + (WMI_VDEV_GET_MWS_COEX_TDM_STATE_EVENTID_param_tlvs *)event; + if (!param_tlvs) { + wma_err("Invalid MWS coex event"); + return -EINVAL; + } + + param_buf = param_tlvs->fixed_param; + if (!param_buf || !mac || !mac->sme.mws_coex_info_state_resp_callback) { + wma_debug("NULL mac ptr or HDD callback is null"); + return -EINVAL; + } + + if (!param_buf) { + wma_debug("NULL mws tdm info event fixed param"); + return -EINVAL; + } + + coex_tdm.vdev_id = param_buf->vdev_id; + coex_tdm.tdm_policy_bitmap = param_buf->tdm_policy_bitmap; + coex_tdm.tdm_sf_bitmap = param_buf->tdm_sf_bitmap; + + mac->sme.mws_coex_info_state_resp_callback(&coex_tdm, + mac->sme.mws_coex_info_ctx, + WMI_MWS_COEX_TDM_STATE); + wma_debug("vdev_id = %u tdm_policy_bitmap = %u tdm_sf_bitmap = %u", + param_buf->vdev_id, param_buf->tdm_policy_bitmap, + param_buf->tdm_sf_bitmap); + + wma_debug("Exit"); + return 0; +} + +/** + * wma_mws_coex_idrx_event_handler - IDRX state Event Handler + * @handle: handle to scn + * @event: IDRX state event + * @len: Length of cmd + * + * Return: 0 on success, else error on failure + */ +static int wma_mws_coex_idrx_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + WMI_VDEV_GET_MWS_COEX_IDRX_STATE_EVENTID_param_tlvs *param_tlvs; + wmi_vdev_get_mws_coex_idrx_state_fixed_param *param_buf; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + struct mws_coex_idrx_state coex_idrx; + + wma_debug("Enter"); + param_tlvs = + (WMI_VDEV_GET_MWS_COEX_IDRX_STATE_EVENTID_param_tlvs *)event; + if (!param_tlvs) { + wma_err("Invalid stats event"); + return -EINVAL; + } + + param_buf = param_tlvs->fixed_param; + if (!param_buf || !mac || !mac->sme.mws_coex_info_state_resp_callback) { + wma_debug("NULL mac ptr or HDD callback is null"); + return -EINVAL; + } + + if (!param_buf) { + wma_debug("NULL MWS Coex event fixed param"); + return -EINVAL; + } + + coex_idrx.vdev_id = param_buf->vdev_id; + coex_idrx.sub0_techid = param_buf->sub0_techid; + coex_idrx.sub0_policy = param_buf->sub0_policy; + coex_idrx.sub0_is_link_critical = param_buf->sub0_is_link_critical; + coex_idrx.sub0_static_power = param_buf->sub0_static_power; + coex_idrx.sub0_rssi = param_buf->sub0_rssi; + coex_idrx.sub1_techid = param_buf->sub1_techid; + coex_idrx.sub1_policy = param_buf->sub1_policy; + coex_idrx.sub1_is_link_critical = param_buf->sub1_is_link_critical; + coex_idrx.sub1_static_power = param_buf->sub1_static_power; + coex_idrx.sub1_rssi = param_buf->sub1_rssi; + mac->sme.mws_coex_info_state_resp_callback(&coex_idrx, + mac->sme.mws_coex_info_ctx, + WMI_MWS_COEX_IDRX_STATE); + + wma_debug("vdev_id = %u sub0_techid = %u sub0_policy = %u sub0_is_link_critical = %u sub0_static_power = %u sub0_rssi = %d sub1_techid = %d sub1_policy = %d sub1_is_link_critical = %d sub1_static_power = %u sub1_rssi= %d", + param_buf->vdev_id, param_buf->sub0_techid, + param_buf->sub0_policy, param_buf->sub0_is_link_critical, + param_buf->sub0_static_power, param_buf->sub0_rssi, + param_buf->sub1_techid, param_buf->sub1_policy, + param_buf->sub1_is_link_critical, + param_buf->sub1_static_power, param_buf->sub1_rssi); + + wma_debug("EXIT"); + return 0; +} + +/** + * wma_mws_coex_antenna_sharing_event_handler - Antenna sharing Event Handler + * @handle: handle to scn + * @event: Antenna sharing state event + * @len: Length of cmd + * + * Return: 0 on success, else error on failure + */ +static int wma_mws_coex_antenna_sharing_event_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + WMI_VDEV_GET_MWS_COEX_ANTENNA_SHARING_STATE_EVENTID_param_tlvs + *param_tlvs = + (WMI_VDEV_GET_MWS_COEX_ANTENNA_SHARING_STATE_EVENTID_param_tlvs *)event; + wmi_vdev_get_mws_coex_antenna_sharing_state_fixed_param *param_buf; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + struct mws_antenna_sharing_info *antenna_sharing; + + wma_debug("Enter"); + if (!param_tlvs) { + wma_err("Invalid stats event"); + return -EINVAL; + } + + param_buf = param_tlvs->fixed_param; + if (!param_buf || !mac || !mac->sme.mws_coex_info_state_resp_callback) { + wma_debug("NULL mac ptr or HDD callback is null"); + return -EINVAL; + } + + if (!param_buf) { + wma_debug("NULL MWS event fixed param"); + return -EINVAL; + } + + antenna_sharing = qdf_mem_malloc(sizeof(*antenna_sharing)); + if (!antenna_sharing) + return -ENOMEM; + + antenna_sharing->vdev_id = param_buf->vdev_id; + antenna_sharing->coex_flags = param_buf->coex_flags; + antenna_sharing->coex_config = param_buf->coex_config; + antenna_sharing->tx_chain_mask = param_buf->tx_chain_mask; + antenna_sharing->rx_chain_mask = param_buf->rx_chain_mask; + antenna_sharing->rx_nss = param_buf->rx_nss; + antenna_sharing->force_mrc = param_buf->force_mrc; + antenna_sharing->rssi_type = param_buf->rssi_type; + antenna_sharing->chain0_rssi = param_buf->chain0_rssi; + antenna_sharing->chain1_rssi = param_buf->chain1_rssi; + antenna_sharing->combined_rssi = param_buf->combined_rssi; + antenna_sharing->imbalance = param_buf->imbalance; + antenna_sharing->mrc_threshold = param_buf->mrc_threshold; + antenna_sharing->grant_duration = param_buf->grant_duration; + + mac->sme.mws_coex_info_state_resp_callback(antenna_sharing, + mac->sme.mws_coex_info_ctx, + WMI_MWS_COEX_ANTENNA_SHARING_STATE); + wma_debug("vdev_id = %u coex_flags = %u coex_config = %u tx_chain_mask = %u rx_chain_mask = %u rx_nss = %u force_mrc = %u rssi_type = %u chain0_rssi = %d chain1_rssi = %d chain0_rssi = %d imbalance = %u mrc_threshold = %d grant_duration = %u", + param_buf->vdev_id, param_buf->coex_flags, + param_buf->coex_config, param_buf->tx_chain_mask, + param_buf->rx_chain_mask, + param_buf->rx_nss, param_buf->force_mrc, + param_buf->rssi_type, param_buf->chain0_rssi, + param_buf->chain1_rssi, param_buf->combined_rssi, + param_buf->imbalance, param_buf->mrc_threshold, + param_buf->grant_duration); + + qdf_mem_free(antenna_sharing); + wma_debug("EXIT"); + return 0; +} + +void wma_get_mws_coex_info_req(tp_wma_handle wma_handle, + struct sir_get_mws_coex_info *req) +{ + QDF_STATUS status; + + status = wmi_unified_send_mws_coex_req_cmd(wma_handle->wmi_handle, + req->vdev_id, req->cmd_id); + + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to send mws coex info"); +} + +void wma_register_mws_coex_events(tp_wma_handle wma_handle) +{ + if (!wma_handle) { + wma_err("wma_handle is NULL"); + return; + } + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_vdev_get_mws_coex_state_eventid, + wma_mws_coex_state_host_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler( + wma_handle->wmi_handle, + wmi_vdev_get_mws_coex_dpwb_state_eventid, + wma_mws_coex_state_dpwb_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler( + wma_handle->wmi_handle, + wmi_vdev_get_mws_coex_tdm_state_eventid, + wma_mws_coex_tdm_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler( + wma_handle->wmi_handle, + wmi_vdev_get_mws_coex_idrx_state_eventid, + wma_mws_coex_idrx_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler( + wma_handle->wmi_handle, + wmi_vdev_get_mws_coex_antenna_sharing_state_eventid, + wma_mws_coex_antenna_sharing_event_handler, + WMA_RX_SERIALIZER_CTX); +} diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_data.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_data.c new file mode 100644 index 0000000000000000000000000000000000000000..21e94a15af4ba3b70761172d567e6cf5737fdd37 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_data.c @@ -0,0 +1,3276 @@ +/* + * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_data.c + * This file contains tx/rx and data path related functions. + */ + +/* Header files */ + +#include "wma.h" +#include "enet.h" +#include "wma_api.h" +#include "cds_api.h" +#include "wmi_unified_api.h" +#include "wlan_qct_sys.h" +#include "wni_api.h" +#include "ani_global.h" +#include "wmi_unified.h" +#include "wni_cfg.h" +#include +#if defined(CONFIG_HL_SUPPORT) +#include "wlan_tgt_def_config_hl.h" +#else +#include "wlan_tgt_def_config.h" +#endif +#include "qdf_nbuf.h" +#include "qdf_types.h" +#include "qdf_mem.h" +#include "qdf_util.h" + +#include "wma_types.h" +#include "lim_api.h" +#include "lim_session_utils.h" + +#include "cds_utils.h" + +#if !defined(REMOVE_PKT_LOG) +#include "pktlog_ac.h" +#endif /* REMOVE_PKT_LOG */ + +#include "dbglog_host.h" +#include "csr_api.h" +#include "ol_fw.h" + +#include "wma_internal.h" +#include "cdp_txrx_flow_ctrl_legacy.h" +#include "cdp_txrx_cmn.h" +#include "cdp_txrx_misc.h" +#include +#include +#include "cdp_txrx_stats.h" +#include +#include "wlan_mgmt_txrx_utils_api.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_objmgr_peer_obj.h" +#include +#include "cfg_ucfg_api.h" +#include "wlan_policy_mgr_ucfg.h" +#include +#include "wlan_lmac_if_api.h" +#include +#include +#include + +struct wma_search_rate { + int32_t rate; + uint8_t flag; +}; + +#define WMA_MAX_OFDM_CCK_RATE_TBL_SIZE 12 +/* In ofdm_cck_rate_tbl->flag, if bit 7 is 1 it's CCK, otherwise it ofdm. + * Lower bit carries the ofdm/cck index for encoding the rate + */ +static struct wma_search_rate ofdm_cck_rate_tbl[WMA_MAX_OFDM_CCK_RATE_TBL_SIZE] = { + {540, 4}, /* 4: OFDM 54 Mbps */ + {480, 0}, /* 0: OFDM 48 Mbps */ + {360, 5}, /* 5: OFDM 36 Mbps */ + {240, 1}, /* 1: OFDM 24 Mbps */ + {180, 6}, /* 6: OFDM 18 Mbps */ + {120, 2}, /* 2: OFDM 12 Mbps */ + {110, (1 << 7)}, /* 0: CCK 11 Mbps Long */ + {90, 7}, /* 7: OFDM 9 Mbps */ + {60, 3}, /* 3: OFDM 6 Mbps */ + {55, ((1 << 7) | 1)}, /* 1: CCK 5.5 Mbps Long */ + {20, ((1 << 7) | 2)}, /* 2: CCK 2 Mbps Long */ + {10, ((1 << 7) | 3)} /* 3: CCK 1 Mbps Long */ +}; + +#define WMA_MAX_VHT20_RATE_TBL_SIZE 9 +/* In vht20_400ns_rate_tbl flag carries the mcs index for encoding the rate */ +static struct wma_search_rate vht20_400ns_rate_tbl[WMA_MAX_VHT20_RATE_TBL_SIZE] = { + {867, 8}, /* MCS8 1SS short GI */ + {722, 7}, /* MCS7 1SS short GI */ + {650, 6}, /* MCS6 1SS short GI */ + {578, 5}, /* MCS5 1SS short GI */ + {433, 4}, /* MCS4 1SS short GI */ + {289, 3}, /* MCS3 1SS short GI */ + {217, 2}, /* MCS2 1SS short GI */ + {144, 1}, /* MCS1 1SS short GI */ + {72, 0} /* MCS0 1SS short GI */ +}; + +/* In vht20_800ns_rate_tbl flag carries the mcs index for encoding the rate */ +static struct wma_search_rate vht20_800ns_rate_tbl[WMA_MAX_VHT20_RATE_TBL_SIZE] = { + {780, 8}, /* MCS8 1SS long GI */ + {650, 7}, /* MCS7 1SS long GI */ + {585, 6}, /* MCS6 1SS long GI */ + {520, 5}, /* MCS5 1SS long GI */ + {390, 4}, /* MCS4 1SS long GI */ + {260, 3}, /* MCS3 1SS long GI */ + {195, 2}, /* MCS2 1SS long GI */ + {130, 1}, /* MCS1 1SS long GI */ + {65, 0} /* MCS0 1SS long GI */ +}; + +#define WMA_MAX_VHT40_RATE_TBL_SIZE 10 +/* In vht40_400ns_rate_tbl flag carries the mcs index for encoding the rate */ +static struct wma_search_rate vht40_400ns_rate_tbl[WMA_MAX_VHT40_RATE_TBL_SIZE] = { + {2000, 9}, /* MCS9 1SS short GI */ + {1800, 8}, /* MCS8 1SS short GI */ + {1500, 7}, /* MCS7 1SS short GI */ + {1350, 6}, /* MCS6 1SS short GI */ + {1200, 5}, /* MCS5 1SS short GI */ + {900, 4}, /* MCS4 1SS short GI */ + {600, 3}, /* MCS3 1SS short GI */ + {450, 2}, /* MCS2 1SS short GI */ + {300, 1}, /* MCS1 1SS short GI */ + {150, 0}, /* MCS0 1SS short GI */ +}; + +static struct wma_search_rate vht40_800ns_rate_tbl[WMA_MAX_VHT40_RATE_TBL_SIZE] = { + {1800, 9}, /* MCS9 1SS long GI */ + {1620, 8}, /* MCS8 1SS long GI */ + {1350, 7}, /* MCS7 1SS long GI */ + {1215, 6}, /* MCS6 1SS long GI */ + {1080, 5}, /* MCS5 1SS long GI */ + {810, 4}, /* MCS4 1SS long GI */ + {540, 3}, /* MCS3 1SS long GI */ + {405, 2}, /* MCS2 1SS long GI */ + {270, 1}, /* MCS1 1SS long GI */ + {135, 0} /* MCS0 1SS long GI */ +}; + +#define WMA_MAX_VHT80_RATE_TBL_SIZE 10 +static struct wma_search_rate vht80_400ns_rate_tbl[WMA_MAX_VHT80_RATE_TBL_SIZE] = { + {4333, 9}, /* MCS9 1SS short GI */ + {3900, 8}, /* MCS8 1SS short GI */ + {3250, 7}, /* MCS7 1SS short GI */ + {2925, 6}, /* MCS6 1SS short GI */ + {2600, 5}, /* MCS5 1SS short GI */ + {1950, 4}, /* MCS4 1SS short GI */ + {1300, 3}, /* MCS3 1SS short GI */ + {975, 2}, /* MCS2 1SS short GI */ + {650, 1}, /* MCS1 1SS short GI */ + {325, 0} /* MCS0 1SS short GI */ +}; + +static struct wma_search_rate vht80_800ns_rate_tbl[WMA_MAX_VHT80_RATE_TBL_SIZE] = { + {3900, 9}, /* MCS9 1SS long GI */ + {3510, 8}, /* MCS8 1SS long GI */ + {2925, 7}, /* MCS7 1SS long GI */ + {2633, 6}, /* MCS6 1SS long GI */ + {2340, 5}, /* MCS5 1SS long GI */ + {1755, 4}, /* MCS4 1SS long GI */ + {1170, 3}, /* MCS3 1SS long GI */ + {878, 2}, /* MCS2 1SS long GI */ + {585, 1}, /* MCS1 1SS long GI */ + {293, 0} /* MCS0 1SS long GI */ +}; + +#define WMA_MAX_HT20_RATE_TBL_SIZE 8 +static struct wma_search_rate ht20_400ns_rate_tbl[WMA_MAX_HT20_RATE_TBL_SIZE] = { + {722, 7}, /* MCS7 1SS short GI */ + {650, 6}, /* MCS6 1SS short GI */ + {578, 5}, /* MCS5 1SS short GI */ + {433, 4}, /* MCS4 1SS short GI */ + {289, 3}, /* MCS3 1SS short GI */ + {217, 2}, /* MCS2 1SS short GI */ + {144, 1}, /* MCS1 1SS short GI */ + {72, 0} /* MCS0 1SS short GI */ +}; + +static struct wma_search_rate ht20_800ns_rate_tbl[WMA_MAX_HT20_RATE_TBL_SIZE] = { + {650, 7}, /* MCS7 1SS long GI */ + {585, 6}, /* MCS6 1SS long GI */ + {520, 5}, /* MCS5 1SS long GI */ + {390, 4}, /* MCS4 1SS long GI */ + {260, 3}, /* MCS3 1SS long GI */ + {195, 2}, /* MCS2 1SS long GI */ + {130, 1}, /* MCS1 1SS long GI */ + {65, 0} /* MCS0 1SS long GI */ +}; + +#define WMA_MAX_HT40_RATE_TBL_SIZE 8 +static struct wma_search_rate ht40_400ns_rate_tbl[WMA_MAX_HT40_RATE_TBL_SIZE] = { + {1500, 7}, /* MCS7 1SS short GI */ + {1350, 6}, /* MCS6 1SS short GI */ + {1200, 5}, /* MCS5 1SS short GI */ + {900, 4}, /* MCS4 1SS short GI */ + {600, 3}, /* MCS3 1SS short GI */ + {450, 2}, /* MCS2 1SS short GI */ + {300, 1}, /* MCS1 1SS short GI */ + {150, 0} /* MCS0 1SS short GI */ +}; + +static struct wma_search_rate ht40_800ns_rate_tbl[WMA_MAX_HT40_RATE_TBL_SIZE] = { + {1350, 7}, /* MCS7 1SS long GI */ + {1215, 6}, /* MCS6 1SS long GI */ + {1080, 5}, /* MCS5 1SS long GI */ + {810, 4}, /* MCS4 1SS long GI */ + {540, 3}, /* MCS3 1SS long GI */ + {405, 2}, /* MCS2 1SS long GI */ + {270, 1}, /* MCS1 1SS long GI */ + {135, 0} /* MCS0 1SS long GI */ +}; + +/** + * wma_bin_search_rate() - binary search function to find rate + * @tbl: rate table + * @tbl_size: table size + * @mbpsx10_rate: return mbps rate + * @ret_flag: return flag + * + * Return: none + */ +static void wma_bin_search_rate(struct wma_search_rate *tbl, int32_t tbl_size, + int32_t *mbpsx10_rate, uint8_t *ret_flag) +{ + int32_t upper, lower, mid; + + /* the table is descenting. index holds the largest value and the + * bottom index holds the smallest value + */ + + upper = 0; /* index 0 */ + lower = tbl_size - 1; /* last index */ + + if (*mbpsx10_rate >= tbl[upper].rate) { + /* use the largest rate */ + *mbpsx10_rate = tbl[upper].rate; + *ret_flag = tbl[upper].flag; + return; + } else if (*mbpsx10_rate <= tbl[lower].rate) { + /* use the smallest rate */ + *mbpsx10_rate = tbl[lower].rate; + *ret_flag = tbl[lower].flag; + return; + } + /* now we do binery search to get the floor value */ + while (lower - upper > 1) { + mid = (upper + lower) >> 1; + if (*mbpsx10_rate == tbl[mid].rate) { + /* found the exact match */ + *mbpsx10_rate = tbl[mid].rate; + *ret_flag = tbl[mid].flag; + return; + } + /* not found. if mid's rate is larger than input move + * upper to mid. If mid's rate is larger than input + * move lower to mid. + */ + if (*mbpsx10_rate > tbl[mid].rate) + lower = mid; + else + upper = mid; + } + /* after the bin search the index is the ceiling of rate */ + *mbpsx10_rate = tbl[upper].rate; + *ret_flag = tbl[upper].flag; + return; +} + +/** + * wma_fill_ofdm_cck_mcast_rate() - fill ofdm cck mcast rate + * @mbpsx10_rate: mbps rates + * @nss: nss + * @rate: rate + * + * Return: QDF status + */ +static QDF_STATUS wma_fill_ofdm_cck_mcast_rate(int32_t mbpsx10_rate, + uint8_t nss, uint8_t *rate) +{ + uint8_t idx = 0; + + wma_bin_search_rate(ofdm_cck_rate_tbl, WMA_MAX_OFDM_CCK_RATE_TBL_SIZE, + &mbpsx10_rate, &idx); + + /* if bit 7 is set it uses CCK */ + if (idx & 0x80) + *rate |= (1 << 6) | (idx & 0xF); /* set bit 6 to 1 for CCK */ + else + *rate |= (idx & 0xF); + return QDF_STATUS_SUCCESS; +} + +/** + * wma_set_ht_vht_mcast_rate() - set ht/vht mcast rate + * @shortgi: short gaurd interval + * @mbpsx10_rate: mbps rates + * @sgi_idx: shortgi index + * @sgi_rate: shortgi rate + * @lgi_idx: longgi index + * @lgi_rate: longgi rate + * @premable: preamble + * @rate: rate + * @streaming_rate: streaming rate + * + * Return: none + */ +static void wma_set_ht_vht_mcast_rate(uint32_t shortgi, int32_t mbpsx10_rate, + uint8_t sgi_idx, int32_t sgi_rate, + uint8_t lgi_idx, int32_t lgi_rate, + uint8_t premable, uint8_t *rate, + int32_t *streaming_rate) +{ + if (shortgi == 0) { + *rate |= (premable << 6) | (lgi_idx & 0xF); + *streaming_rate = lgi_rate; + } else { + *rate |= (premable << 6) | (sgi_idx & 0xF); + *streaming_rate = sgi_rate; + } +} + +/** + * wma_fill_ht20_mcast_rate() - fill ht20 mcast rate + * @shortgi: short gaurd interval + * @mbpsx10_rate: mbps rates + * @nss: nss + * @rate: rate + * @streaming_rate: streaming rate + * + * Return: QDF status + */ +static QDF_STATUS wma_fill_ht20_mcast_rate(uint32_t shortgi, + int32_t mbpsx10_rate, uint8_t nss, + uint8_t *rate, + int32_t *streaming_rate) +{ + uint8_t sgi_idx = 0, lgi_idx = 0; + int32_t sgi_rate, lgi_rate; + + if (nss == 1) + mbpsx10_rate = mbpsx10_rate >> 1; + + sgi_rate = mbpsx10_rate; + lgi_rate = mbpsx10_rate; + if (shortgi) + wma_bin_search_rate(ht20_400ns_rate_tbl, + WMA_MAX_HT20_RATE_TBL_SIZE, &sgi_rate, + &sgi_idx); + else + wma_bin_search_rate(ht20_800ns_rate_tbl, + WMA_MAX_HT20_RATE_TBL_SIZE, &lgi_rate, + &lgi_idx); + + wma_set_ht_vht_mcast_rate(shortgi, mbpsx10_rate, sgi_idx, sgi_rate, + lgi_idx, lgi_rate, 2, rate, streaming_rate); + if (nss == 1) + *streaming_rate = *streaming_rate << 1; + return QDF_STATUS_SUCCESS; +} + +/** + * wma_fill_ht40_mcast_rate() - fill ht40 mcast rate + * @shortgi: short gaurd interval + * @mbpsx10_rate: mbps rates + * @nss: nss + * @rate: rate + * @streaming_rate: streaming rate + * + * Return: QDF status + */ +static QDF_STATUS wma_fill_ht40_mcast_rate(uint32_t shortgi, + int32_t mbpsx10_rate, uint8_t nss, + uint8_t *rate, + int32_t *streaming_rate) +{ + uint8_t sgi_idx = 0, lgi_idx = 0; + int32_t sgi_rate, lgi_rate; + + /* for 2x2 divide the rate by 2 */ + if (nss == 1) + mbpsx10_rate = mbpsx10_rate >> 1; + + sgi_rate = mbpsx10_rate; + lgi_rate = mbpsx10_rate; + if (shortgi) + wma_bin_search_rate(ht40_400ns_rate_tbl, + WMA_MAX_HT40_RATE_TBL_SIZE, &sgi_rate, + &sgi_idx); + else + wma_bin_search_rate(ht40_800ns_rate_tbl, + WMA_MAX_HT40_RATE_TBL_SIZE, &lgi_rate, + &lgi_idx); + + wma_set_ht_vht_mcast_rate(shortgi, mbpsx10_rate, sgi_idx, sgi_rate, + lgi_idx, lgi_rate, 2, rate, streaming_rate); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_fill_vht20_mcast_rate() - fill vht20 mcast rate + * @shortgi: short gaurd interval + * @mbpsx10_rate: mbps rates + * @nss: nss + * @rate: rate + * @streaming_rate: streaming rate + * + * Return: QDF status + */ +static QDF_STATUS wma_fill_vht20_mcast_rate(uint32_t shortgi, + int32_t mbpsx10_rate, uint8_t nss, + uint8_t *rate, + int32_t *streaming_rate) +{ + uint8_t sgi_idx = 0, lgi_idx = 0; + int32_t sgi_rate, lgi_rate; + + /* for 2x2 divide the rate by 2 */ + if (nss == 1) + mbpsx10_rate = mbpsx10_rate >> 1; + + sgi_rate = mbpsx10_rate; + lgi_rate = mbpsx10_rate; + if (shortgi) + wma_bin_search_rate(vht20_400ns_rate_tbl, + WMA_MAX_VHT20_RATE_TBL_SIZE, &sgi_rate, + &sgi_idx); + else + wma_bin_search_rate(vht20_800ns_rate_tbl, + WMA_MAX_VHT20_RATE_TBL_SIZE, &lgi_rate, + &lgi_idx); + + wma_set_ht_vht_mcast_rate(shortgi, mbpsx10_rate, sgi_idx, sgi_rate, + lgi_idx, lgi_rate, 3, rate, streaming_rate); + if (nss == 1) + *streaming_rate = *streaming_rate << 1; + return QDF_STATUS_SUCCESS; +} + +/** + * wma_fill_vht40_mcast_rate() - fill vht40 mcast rate + * @shortgi: short gaurd interval + * @mbpsx10_rate: mbps rates + * @nss: nss + * @rate: rate + * @streaming_rate: streaming rate + * + * Return: QDF status + */ +static QDF_STATUS wma_fill_vht40_mcast_rate(uint32_t shortgi, + int32_t mbpsx10_rate, uint8_t nss, + uint8_t *rate, + int32_t *streaming_rate) +{ + uint8_t sgi_idx = 0, lgi_idx = 0; + int32_t sgi_rate, lgi_rate; + + /* for 2x2 divide the rate by 2 */ + if (nss == 1) + mbpsx10_rate = mbpsx10_rate >> 1; + + sgi_rate = mbpsx10_rate; + lgi_rate = mbpsx10_rate; + if (shortgi) + wma_bin_search_rate(vht40_400ns_rate_tbl, + WMA_MAX_VHT40_RATE_TBL_SIZE, &sgi_rate, + &sgi_idx); + else + wma_bin_search_rate(vht40_800ns_rate_tbl, + WMA_MAX_VHT40_RATE_TBL_SIZE, &lgi_rate, + &lgi_idx); + + wma_set_ht_vht_mcast_rate(shortgi, mbpsx10_rate, + sgi_idx, sgi_rate, lgi_idx, lgi_rate, + 3, rate, streaming_rate); + if (nss == 1) + *streaming_rate = *streaming_rate << 1; + return QDF_STATUS_SUCCESS; +} + +/** + * wma_fill_vht80_mcast_rate() - fill vht80 mcast rate + * @shortgi: short gaurd interval + * @mbpsx10_rate: mbps rates + * @nss: nss + * @rate: rate + * @streaming_rate: streaming rate + * + * Return: QDF status + */ +static QDF_STATUS wma_fill_vht80_mcast_rate(uint32_t shortgi, + int32_t mbpsx10_rate, uint8_t nss, + uint8_t *rate, + int32_t *streaming_rate) +{ + uint8_t sgi_idx = 0, lgi_idx = 0; + int32_t sgi_rate, lgi_rate; + + /* for 2x2 divide the rate by 2 */ + if (nss == 1) + mbpsx10_rate = mbpsx10_rate >> 1; + + sgi_rate = mbpsx10_rate; + lgi_rate = mbpsx10_rate; + if (shortgi) + wma_bin_search_rate(vht80_400ns_rate_tbl, + WMA_MAX_VHT80_RATE_TBL_SIZE, &sgi_rate, + &sgi_idx); + else + wma_bin_search_rate(vht80_800ns_rate_tbl, + WMA_MAX_VHT80_RATE_TBL_SIZE, &lgi_rate, + &lgi_idx); + + wma_set_ht_vht_mcast_rate(shortgi, mbpsx10_rate, sgi_idx, sgi_rate, + lgi_idx, lgi_rate, 3, rate, streaming_rate); + if (nss == 1) + *streaming_rate = *streaming_rate << 1; + return QDF_STATUS_SUCCESS; +} + +/** + * wma_fill_ht_mcast_rate() - fill ht mcast rate + * @shortgi: short gaurd interval + * @chwidth: channel width + * @chanmode: channel mode + * @mhz: frequency + * @mbpsx10_rate: mbps rates + * @nss: nss + * @rate: rate + * @streaming_rate: streaming rate + * + * Return: QDF status + */ +static QDF_STATUS wma_fill_ht_mcast_rate(uint32_t shortgi, + uint32_t chwidth, int32_t mbpsx10_rate, + uint8_t nss, uint8_t *rate, + int32_t *streaming_rate) +{ + int32_t ret = 0; + + *streaming_rate = 0; + if (chwidth == 0) + ret = wma_fill_ht20_mcast_rate(shortgi, mbpsx10_rate, + nss, rate, streaming_rate); + else if (chwidth == 1) + ret = wma_fill_ht40_mcast_rate(shortgi, mbpsx10_rate, + nss, rate, streaming_rate); + else + WMA_LOGE("%s: Error, Invalid chwidth enum %d", __func__, + chwidth); + return (*streaming_rate != 0) ? QDF_STATUS_SUCCESS : QDF_STATUS_E_INVAL; +} + +/** + * wma_fill_vht_mcast_rate() - fill vht mcast rate + * @shortgi: short gaurd interval + * @chwidth: channel width + * @chanmode: channel mode + * @mhz: frequency + * @mbpsx10_rate: mbps rates + * @nss: nss + * @rate: rate + * @streaming_rate: streaming rate + * + * Return: QDF status + */ +static QDF_STATUS wma_fill_vht_mcast_rate(uint32_t shortgi, + uint32_t chwidth, + int32_t mbpsx10_rate, uint8_t nss, + uint8_t *rate, + int32_t *streaming_rate) +{ + int32_t ret = 0; + + *streaming_rate = 0; + if (chwidth == 0) + ret = wma_fill_vht20_mcast_rate(shortgi, mbpsx10_rate, nss, + rate, streaming_rate); + else if (chwidth == 1) + ret = wma_fill_vht40_mcast_rate(shortgi, mbpsx10_rate, nss, + rate, streaming_rate); + else if (chwidth == 2) + ret = wma_fill_vht80_mcast_rate(shortgi, mbpsx10_rate, nss, + rate, streaming_rate); + else + WMA_LOGE("%s: chwidth enum %d not supported", + __func__, chwidth); + return (*streaming_rate != 0) ? QDF_STATUS_SUCCESS : QDF_STATUS_E_INVAL; +} + +#define WMA_MCAST_1X1_CUT_OFF_RATE 2000 +/** + * wma_encode_mc_rate() - fill mc rates + * @shortgi: short gaurd interval + * @chwidth: channel width + * @chanmode: channel mode + * @mhz: frequency + * @mbpsx10_rate: mbps rates + * @nss: nss + * @rate: rate + * + * Return: QDF status + */ +static QDF_STATUS wma_encode_mc_rate(uint32_t shortgi, uint32_t chwidth, + A_UINT32 mhz, int32_t mbpsx10_rate, uint8_t nss, + uint8_t *rate) +{ + int32_t ret = 0; + + /* nss input value: 0 - 1x1; 1 - 2x2; 2 - 3x3 + * the phymode selection is based on following assumption: + * (1) if the app specifically requested 1x1 or 2x2 we hornor it + * (2) if mbpsx10_rate <= 540: always use BG + * (3) 540 < mbpsx10_rate <= 2000: use 1x1 HT/VHT + * (4) 2000 < mbpsx10_rate: use 2x2 HT/VHT + */ + WMA_LOGE("%s: Input: nss = %d, mbpsx10 = 0x%x, chwidth = %d, shortgi = %d", + __func__, nss, mbpsx10_rate, chwidth, shortgi); + if ((mbpsx10_rate & 0x40000000) && nss > 0) { + /* bit 30 indicates user inputed nss, + * bit 28 and 29 used to encode nss + */ + uint8_t user_nss = (mbpsx10_rate & 0x30000000) >> 28; + + nss = (user_nss < nss) ? user_nss : nss; + /* zero out bits 19 - 21 to recover the actual rate */ + mbpsx10_rate &= ~0x70000000; + } else if (mbpsx10_rate <= WMA_MCAST_1X1_CUT_OFF_RATE) { + /* if the input rate is less or equal to the + * 1x1 cutoff rate we use 1x1 only + */ + nss = 0; + } + /* encode NSS bits (bit 4, bit 5) */ + *rate = (nss & 0x3) << 4; + /* if mcast input rate exceeds the ofdm/cck max rate 54mpbs + * we try to choose best ht/vht mcs rate + */ + if (540 < mbpsx10_rate) { + /* cannot use ofdm/cck, choose closest ht/vht mcs rate */ + uint8_t rate_ht = *rate; + uint8_t rate_vht = *rate; + int32_t stream_rate_ht = 0; + int32_t stream_rate_vht = 0; + int32_t stream_rate = 0; + + ret = wma_fill_ht_mcast_rate(shortgi, chwidth, mbpsx10_rate, + nss, &rate_ht, + &stream_rate_ht); + if (ret != QDF_STATUS_SUCCESS) + stream_rate_ht = 0; + if (mhz < WMA_2_4_GHZ_MAX_FREQ) { + /* not in 5 GHZ frequency */ + *rate = rate_ht; + stream_rate = stream_rate_ht; + goto ht_vht_done; + } + /* capable doing 11AC mcast so that search vht tables */ + ret = wma_fill_vht_mcast_rate(shortgi, chwidth, mbpsx10_rate, + nss, &rate_vht, + &stream_rate_vht); + if (ret != QDF_STATUS_SUCCESS) { + if (stream_rate_ht != 0) + ret = QDF_STATUS_SUCCESS; + *rate = rate_ht; + stream_rate = stream_rate_ht; + goto ht_vht_done; + } + if (stream_rate_ht == 0) { + /* only vht rate available */ + *rate = rate_vht; + stream_rate = stream_rate_vht; + } else { + /* set ht as default first */ + *rate = rate_ht; + stream_rate = stream_rate_ht; + if (stream_rate < mbpsx10_rate) { + if (mbpsx10_rate <= stream_rate_vht || + stream_rate < stream_rate_vht) { + *rate = rate_vht; + stream_rate = stream_rate_vht; + } + } else { + if (stream_rate_vht >= mbpsx10_rate && + stream_rate_vht < stream_rate) { + *rate = rate_vht; + stream_rate = stream_rate_vht; + } + } + } +ht_vht_done: + WMA_LOGE("%s: NSS = %d, freq = %d", + __func__, nss, mhz); + WMA_LOGD(" %s: input_rate = %d, chwidth = %d rate = 0x%x, streaming_rate = %d", + __func__, mbpsx10_rate, chwidth, *rate, stream_rate); + } else { + if (mbpsx10_rate > 0) + ret = wma_fill_ofdm_cck_mcast_rate(mbpsx10_rate, + nss, rate); + else + *rate = 0xFF; + + WMA_LOGE("%s: NSS = %d, input_rate = %d, rate = 0x%x", + __func__, nss, mbpsx10_rate, *rate); + } + return ret; +} + +/** + * wma_cp_stats_set_rate_flag() - set rate flags within cp_stats priv object + * @wma: wma handle + * @vdev_id: vdev id + * + * Return: none + */ +static void wma_cp_stats_set_rate_flag(tp_wma_handle wma, uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc = wma->psoc; + struct wma_txrx_node *iface = &wma->interfaces[vdev_id]; + uint32_t rate_flag; + QDF_STATUS status; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_LEGACY_WMA_ID); + if (!vdev) { + WMA_LOGE("%s, vdev not found for id: %d", __func__, + vdev_id); + return; + } + + status = wma_get_vdev_rate_flag(iface->vdev, &rate_flag); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s, vdev not found for id: %d", __func__, + vdev_id); + return; + } + ucfg_mc_cp_stats_set_rate_flags(vdev, rate_flag); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); +} + +#ifdef WLAN_FEATURE_11AX +/** + * wma_set_bss_rate_flags_he() - set rate flags based on BSS capability + * @rate_flags: rate_flags pointer + * @add_bss: add_bss params + * + * Return: QDF_STATUS + */ +static QDF_STATUS wma_set_bss_rate_flags_he(enum tx_rate_info *rate_flags, + struct bss_params *add_bss) +{ + if (!add_bss->he_capable) + return QDF_STATUS_E_NOSUPPORT; + + *rate_flags |= wma_get_he_rate_flags(add_bss->ch_width); + + wma_debug("he_capable %d rate_flags 0x%x", add_bss->he_capable, + *rate_flags); + return QDF_STATUS_SUCCESS; +} + +static bool wma_get_bss_he_capable(struct bss_params *add_bss) +{ + return add_bss->he_capable; +} +#else +static QDF_STATUS wma_set_bss_rate_flags_he(enum tx_rate_info *rate_flags, + struct bss_params *add_bss) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static bool wma_get_bss_he_capable(struct bss_params *add_bss) +{ + return false; +} +#endif + +enum tx_rate_info wma_get_vht_rate_flags(enum phy_ch_width ch_width) +{ + enum tx_rate_info rate_flags = 0; + + if (ch_width == CH_WIDTH_80P80MHZ) + rate_flags |= TX_RATE_VHT160 | TX_RATE_VHT80 | TX_RATE_VHT40 | + TX_RATE_VHT20; + if (ch_width == CH_WIDTH_160MHZ) + rate_flags |= TX_RATE_VHT160 | TX_RATE_VHT80 | TX_RATE_VHT40 | + TX_RATE_VHT20; + if (ch_width == CH_WIDTH_80MHZ) + rate_flags |= TX_RATE_VHT80 | TX_RATE_VHT40 | TX_RATE_VHT20; + else if (ch_width) + rate_flags |= TX_RATE_VHT40 | TX_RATE_VHT20; + else + rate_flags |= TX_RATE_VHT20; + return rate_flags; +} + +enum tx_rate_info wma_get_ht_rate_flags(enum phy_ch_width ch_width) +{ + enum tx_rate_info rate_flags = 0; + + if (ch_width) + rate_flags |= TX_RATE_HT40 | TX_RATE_HT20; + else + rate_flags |= TX_RATE_HT20; + + return rate_flags; +} + +enum tx_rate_info wma_get_he_rate_flags(enum phy_ch_width ch_width) +{ + enum tx_rate_info rate_flags = 0; + + if (ch_width == CH_WIDTH_160MHZ || + ch_width == CH_WIDTH_80P80MHZ) + rate_flags |= TX_RATE_HE160 | TX_RATE_HE80 | TX_RATE_HE40 | + TX_RATE_HE20; + else if (ch_width == CH_WIDTH_80MHZ) + rate_flags |= TX_RATE_HE80 | TX_RATE_HE40 | TX_RATE_HE20; + else if (ch_width) + rate_flags |= TX_RATE_HE40 | TX_RATE_HE20; + else + rate_flags |= TX_RATE_HE20; + + return rate_flags; +} + +void wma_set_bss_rate_flags(tp_wma_handle wma, uint8_t vdev_id, + struct bss_params *add_bss) +{ + struct wma_txrx_node *iface = &wma->interfaces[vdev_id]; + struct vdev_mlme_obj *vdev_mlme; + enum tx_rate_info *rate_flags; + + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (!vdev_mlme) { + WMA_LOGE("%s: Failed to get mlme obj for vdev_%d", + __func__, vdev_id); + return; + } + rate_flags = &vdev_mlme->mgmt.rate_info.rate_flags; + *rate_flags = 0; + + if (QDF_STATUS_SUCCESS != + wma_set_bss_rate_flags_he(rate_flags, add_bss)) { + if (add_bss->vhtCapable) { + *rate_flags = wma_get_vht_rate_flags(add_bss->ch_width); + } + /* avoid to conflict with htCapable flag */ + else if (add_bss->htCapable) { + *rate_flags |= wma_get_ht_rate_flags(add_bss->ch_width); + } + } + + if (add_bss->staContext.fShortGI20Mhz || + add_bss->staContext.fShortGI40Mhz) + *rate_flags |= TX_RATE_SGI; + + if (!add_bss->htCapable && !add_bss->vhtCapable && + !wma_get_bss_he_capable(add_bss)) + *rate_flags = TX_RATE_LEGACY; + + wma_debug("capable: vht %u, ht %u, rate_flags %x, ch_width %d", + add_bss->vhtCapable, add_bss->htCapable, + *rate_flags, add_bss->ch_width); + + wma_cp_stats_set_rate_flag(wma, vdev_id); +} + +/** + * wmi_unified_send_txbf() - set txbf parameter to fw + * @wma: wma handle + * @params: txbf parameters + * + * Return: 0 for success or error code + */ +int32_t wmi_unified_send_txbf(tp_wma_handle wma, tpAddStaParams params) +{ + wmi_vdev_txbf_en txbf_en = {0}; + + /* This is set when Other partner is Bformer + * and we are capable bformee(enabled both in ini and fw) + */ + txbf_en.sutxbfee = params->vhtTxBFCapable; + txbf_en.mutxbfee = params->vhtTxMUBformeeCapable; + txbf_en.sutxbfer = params->enable_su_tx_bformer; + + /* When MU TxBfee is set, SU TxBfee must be set by default */ + if (txbf_en.mutxbfee) + txbf_en.sutxbfee = txbf_en.mutxbfee; + + WMA_LOGD("txbf_en.sutxbfee %d txbf_en.mutxbfee %d, sutxbfer %d", + txbf_en.sutxbfee, txbf_en.mutxbfee, txbf_en.sutxbfer); + + return wma_vdev_set_param(wma->wmi_handle, + params->smesessionId, + WMI_VDEV_PARAM_TXBF, + *((A_UINT8 *) &txbf_en)); +} + +/** + * wma_data_tx_ack_work_handler() - process data tx ack + * @ack_work: work structure + * + * Return: none + */ +static void wma_data_tx_ack_work_handler(void *ack_work) +{ + struct wma_tx_ack_work_ctx *work; + tp_wma_handle wma_handle; + wma_tx_ota_comp_callback ack_cb; + + if (cds_is_load_or_unload_in_progress()) { + WMA_LOGE("%s: Driver load/unload in progress", __func__); + return; + } + + work = (struct wma_tx_ack_work_ctx *)ack_work; + + wma_handle = work->wma_handle; + ack_cb = wma_handle->umac_data_ota_ack_cb; + + if (work->status) + WMA_LOGE("Data Tx Ack Cb Status %d", work->status); + else + WMA_LOGD("Data Tx Ack Cb Status %d", work->status); + + /* Call the Ack Cb registered by UMAC */ + if (ack_cb) + ack_cb(wma_handle->mac_context, NULL, work->status, NULL); + else + WMA_LOGE("Data Tx Ack Cb is NULL"); + + wma_handle->umac_data_ota_ack_cb = NULL; + wma_handle->last_umac_data_nbuf = NULL; + qdf_mem_free(work); + wma_handle->ack_work_ctx = NULL; +} + +/** + * wma_data_tx_ack_comp_hdlr() - handles tx data ack completion + * @context: context with which the handler is registered + * @netbuf: tx data nbuf + * @err: status of tx completion + * + * This is the cb registered with TxRx for + * Ack Complete + * + * Return: none + */ +void +wma_data_tx_ack_comp_hdlr(void *wma_context, qdf_nbuf_t netbuf, int32_t status) +{ + tp_wma_handle wma_handle = (tp_wma_handle) wma_context; + + if (!wma_handle) { + WMA_LOGE("%s: Invalid WMA Handle", __func__); + return; + } + + /* + * if netBuf does not match with pending nbuf then just free the + * netbuf and do not call ack cb + */ + if (wma_handle->last_umac_data_nbuf != netbuf) { + if (wma_handle->umac_data_ota_ack_cb) { + WMA_LOGE("%s: nbuf does not match but umac_data_ota_ack_cb is not null", + __func__); + } else { + WMA_LOGE("%s: nbuf does not match and umac_data_ota_ack_cb is also null", + __func__); + } + goto free_nbuf; + } + + if (wma_handle && wma_handle->umac_data_ota_ack_cb) { + struct wma_tx_ack_work_ctx *ack_work; + + ack_work = qdf_mem_malloc(sizeof(struct wma_tx_ack_work_ctx)); + wma_handle->ack_work_ctx = ack_work; + if (ack_work) { + ack_work->wma_handle = wma_handle; + ack_work->sub_type = 0; + ack_work->status = status; + + qdf_create_work(0, &ack_work->ack_cmp_work, + wma_data_tx_ack_work_handler, + ack_work); + qdf_sched_work(0, &ack_work->ack_cmp_work); + } + } + +free_nbuf: + /* unmap and freeing the tx buf as txrx is not taking care */ + qdf_nbuf_unmap_single(wma_handle->qdf_dev, netbuf, QDF_DMA_TO_DEVICE); + qdf_nbuf_free(netbuf); +} + +/** + * wma_check_txrx_chainmask() - check txrx chainmask + * @num_rf_chains: number of rf chains + * @cmd_value: command value + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_check_txrx_chainmask(int num_rf_chains, int cmd_value) +{ + if ((cmd_value > WMA_MAX_RF_CHAINS(num_rf_chains)) || + (cmd_value < WMA_MIN_RF_CHAINS)) { + WMA_LOGE("%s: Requested value %d over the range", + __func__, cmd_value); + return QDF_STATUS_E_INVAL; + } + return QDF_STATUS_SUCCESS; +} + +/** + * wma_peer_state_change_event_handler() - peer state change event handler + * @handle: wma handle + * @event_buff: event buffer + * @len: length of buffer + * + * This event handler unpauses vdev if peer state change to AUTHORIZED STATE + * + * Return: 0 for success or error code + */ +int wma_peer_state_change_event_handler(void *handle, + uint8_t *event_buff, + uint32_t len) +{ + WMI_PEER_STATE_EVENTID_param_tlvs *param_buf; + wmi_peer_state_event_fixed_param *event; +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + tp_wma_handle wma_handle = (tp_wma_handle) handle; +#endif + + if (!event_buff) { + WMA_LOGE("%s: Received NULL event ptr from FW", __func__); + return -EINVAL; + } + param_buf = (WMI_PEER_STATE_EVENTID_param_tlvs *) event_buff; + if (!param_buf) { + WMA_LOGE("%s: Received NULL buf ptr from FW", __func__); + return -ENOMEM; + } + + event = param_buf->fixed_param; + + if ((cdp_get_opmode(cds_get_context(QDF_MODULE_ID_SOC), + event->vdev_id) == wlan_op_mode_sta) && + event->state == WMI_PEER_STATE_AUTHORIZED) { + /* + * set event so that hdd + * can procced and unpause tx queue + */ +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL + if (!wma_handle->peer_authorized_cb) { + WMA_LOGE("%s: peer authorized cb not registered", + __func__); + return -EINVAL; + } + wma_handle->peer_authorized_cb(event->vdev_id); +#endif + } + + return 0; +} + +/** + * wma_set_enable_disable_mcc_adaptive_scheduler() -enable/disable mcc scheduler + * @mcc_adaptive_scheduler: enable/disable + * + * This function enable/disable mcc adaptive scheduler in fw. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_set_enable_disable_mcc_adaptive_scheduler(uint32_t + mcc_adaptive_scheduler) +{ + tp_wma_handle wma = NULL; + uint32_t pdev_id; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) + return QDF_STATUS_E_FAULT; + + /* + * Since there could be up to two instances of OCS in FW (one per MAC), + * FW provides the option of enabling and disabling MAS on a per MAC + * basis. But, Host does not have enable/disable option for individual + * MACs. So, FW agreed for the Host to send down a 'pdev id' of 0. + * When 'pdev id' of 0 is used, FW treats this as a SOC level command + * and applies the same value to both MACs. Irrespective of the value + * of 'WMI_SERVICE_DEPRECATED_REPLACE', the pdev id needs to be '0' + * (SOC level) for WMI_RESMGR_ADAPTIVE_OCS_ENABLE_DISABLE_CMDID + */ + pdev_id = WMI_PDEV_ID_SOC; + + return wmi_unified_set_enable_disable_mcc_adaptive_scheduler_cmd( + wma->wmi_handle, mcc_adaptive_scheduler, pdev_id); +} + +/** + * wma_set_mcc_channel_time_latency() -set MCC channel time latency + * @wma: wma handle + * @mcc_channel: mcc channel + * @mcc_channel_time_latency: MCC channel time latency. + * + * Currently used to set time latency for an MCC vdev/adapter using operating + * channel of it and channel number. The info is provided run time using + * iwpriv command: iwpriv setMccLatency . + * + * Return: QDF status + */ +QDF_STATUS wma_set_mcc_channel_time_latency(tp_wma_handle wma, + uint32_t mcc_channel, uint32_t mcc_channel_time_latency) +{ + bool mcc_adapt_sch = false; + struct mac_context *mac = NULL; + uint32_t channel1 = mcc_channel; + uint32_t chan1_freq = cds_chan_to_freq(channel1); + + if (!wma) { + WMA_LOGE("%s:NULL wma ptr. Exiting", __func__); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + mac = cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + /* First step is to confirm if MCC is active */ + if (!lim_is_in_mcc(mac)) { + WMA_LOGE("%s: MCC is not active. Exiting", __func__); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + /* Confirm MCC adaptive scheduler feature is disabled */ + if (policy_mgr_get_dynamic_mcc_adaptive_sch(mac->psoc, + &mcc_adapt_sch) == + QDF_STATUS_SUCCESS) { + if (mcc_adapt_sch) { + WMA_LOGD("%s: Can't set channel latency while MCC ADAPTIVE SCHED is enabled. Exit", + __func__); + return QDF_STATUS_SUCCESS; + } + } else { + WMA_LOGE("%s: Failed to get value for MCC_ADAPTIVE_SCHED, " + "Exit w/o setting latency", __func__); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_set_mcc_channel_time_latency_cmd(wma->wmi_handle, + chan1_freq, + mcc_channel_time_latency); +} + +/** + * wma_set_mcc_channel_time_quota() -set MCC channel time quota + * @wma: wma handle + * @adapter_1_chan_number: adapter 1 channel number + * @adapter_1_quota: adapter 1 quota + * @adapter_2_chan_number: adapter 2 channel number + * + * Currently used to set time quota for 2 MCC vdevs/adapters using (operating + * channel, quota) for each mode . The info is provided run time using + * iwpriv command: iwpriv setMccQuota . + * Note: the quota provided in command is for the same mode in cmd. HDD + * checks if MCC mode is active, gets the second mode and its operating chan. + * Quota for the 2nd role is calculated as 100 - quota of first mode. + * + * Return: QDF status + */ +QDF_STATUS wma_set_mcc_channel_time_quota(tp_wma_handle wma, + uint32_t adapter_1_chan_number, uint32_t adapter_1_quota, + uint32_t adapter_2_chan_number) +{ + bool mcc_adapt_sch = false; + struct mac_context *mac = NULL; + uint32_t chan1_freq = cds_chan_to_freq(adapter_1_chan_number); + uint32_t chan2_freq = cds_chan_to_freq(adapter_2_chan_number); + + if (!wma) { + WMA_LOGE("%s:NULL wma ptr. Exiting", __func__); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + mac = cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + /* First step is to confirm if MCC is active */ + if (!lim_is_in_mcc(mac)) { + WMA_LOGD("%s: MCC is not active. Exiting", __func__); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + /* Confirm MCC adaptive scheduler feature is disabled */ + if (policy_mgr_get_dynamic_mcc_adaptive_sch(mac->psoc, + &mcc_adapt_sch) == + QDF_STATUS_SUCCESS) { + if (mcc_adapt_sch) { + WMA_LOGD("%s: Can't set channel quota while MCC_ADAPTIVE_SCHED is enabled. Exit", + __func__); + return QDF_STATUS_SUCCESS; + } + } else { + WMA_LOGE("%s: Failed to retrieve WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED. Exit", + __func__); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_set_mcc_channel_time_quota_cmd(wma->wmi_handle, + chan1_freq, + adapter_1_quota, + chan2_freq); +} + +QDF_STATUS wma_process_rate_update_indicate(tp_wma_handle wma, + tSirRateUpdateInd * + pRateUpdateParams) +{ + int32_t ret = 0; + uint8_t vdev_id = 0; + int32_t mbpsx10_rate = -1; + uint32_t paramId; + uint8_t rate = 0; + uint32_t short_gi, rate_flag; + struct wma_txrx_node *intr = wma->interfaces; + QDF_STATUS status; + + /* Get the vdev id */ + if (wma_find_vdev_id_by_addr(wma, pRateUpdateParams->bssid.bytes, + &vdev_id)) { + WMA_LOGE("vdev handle is invalid for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(pRateUpdateParams->bssid.bytes)); + qdf_mem_free(pRateUpdateParams); + return QDF_STATUS_E_INVAL; + } + short_gi = intr[vdev_id].config.shortgi; + + status = wma_get_vdev_rate_flag(intr[vdev_id].vdev, &rate_flag); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to get rate_flag for VDEV_%d", + __func__, vdev_id); + qdf_mem_free(pRateUpdateParams); + return QDF_STATUS_E_INVAL; + } + + if (short_gi == 0) + short_gi = (rate_flag & TX_RATE_SGI) ? true : false; + /* first check if reliable TX mcast rate is used. If not check the bcast + * Then is mcast. Mcast rate is saved in mcastDataRate24GHz + */ + if (pRateUpdateParams->reliableMcastDataRateTxFlag > 0) { + mbpsx10_rate = pRateUpdateParams->reliableMcastDataRate; + paramId = WMI_VDEV_PARAM_MCAST_DATA_RATE; + if (pRateUpdateParams-> + reliableMcastDataRateTxFlag & TX_RATE_SGI) + short_gi = 1; /* upper layer specified short GI */ + } else if (pRateUpdateParams->bcastDataRate > -1) { + mbpsx10_rate = pRateUpdateParams->bcastDataRate; + paramId = WMI_VDEV_PARAM_BCAST_DATA_RATE; + } else { + mbpsx10_rate = pRateUpdateParams->mcastDataRate24GHz; + paramId = WMI_VDEV_PARAM_MCAST_DATA_RATE; + if (pRateUpdateParams-> + mcastDataRate24GHzTxFlag & TX_RATE_SGI) + short_gi = 1; /* upper layer specified short GI */ + } + WMA_LOGE("%s: dev_id = %d, dev_type = %d, dev_mode = %d,", + __func__, vdev_id, intr[vdev_id].type, + pRateUpdateParams->dev_mode); + WMA_LOGE("%s: mac = "QDF_MAC_ADDR_FMT", config.shortgi = %d, rate_flags = 0x%x", + __func__, QDF_MAC_ADDR_REF(pRateUpdateParams->bssid.bytes), + intr[vdev_id].config.shortgi, rate_flag); + ret = wma_encode_mc_rate(short_gi, intr[vdev_id].config.chwidth, + intr[vdev_id].mhz, mbpsx10_rate, + pRateUpdateParams->nss, &rate); + if (ret != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Error, Invalid input rate value", __func__); + qdf_mem_free(pRateUpdateParams); + return ret; + } + status = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_SGI, short_gi); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Fail to Set WMI_VDEV_PARAM_SGI(%d), status = %d", + __func__, short_gi, status); + qdf_mem_free(pRateUpdateParams); + return status; + } + status = wma_vdev_set_param(wma->wmi_handle, + vdev_id, paramId, rate); + qdf_mem_free(pRateUpdateParams); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Fail to Set rate, status = %d", __func__, status); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_mgmt_tx_ack_comp_hdlr() - handles tx ack mgmt completion + * @context: context with which the handler is registered + * @netbuf: tx mgmt nbuf + * @status: status of tx completion + * + * This is callback registered with TxRx for + * Ack Complete. + * + * Return: none + */ +static void +wma_mgmt_tx_ack_comp_hdlr(void *wma_context, qdf_nbuf_t netbuf, int32_t status) +{ + tp_wma_handle wma_handle = (tp_wma_handle) wma_context; + struct wlan_objmgr_pdev *pdev = (struct wlan_objmgr_pdev *) + wma_handle->pdev; + struct wmi_mgmt_params mgmt_params = {}; + uint16_t desc_id; + uint8_t vdev_id; + + desc_id = QDF_NBUF_CB_MGMT_TXRX_DESC_ID(netbuf); + vdev_id = mgmt_txrx_get_vdev_id(pdev, desc_id); + + mgmt_params.vdev_id = vdev_id; + mgmt_txrx_tx_completion_handler(pdev, desc_id, status, &mgmt_params); +} + +/** + * wma_mgmt_tx_dload_comp_hldr() - handles tx mgmt completion + * @context: context with which the handler is registered + * @netbuf: tx mgmt nbuf + * @status: status of tx completion + * + * This function calls registered download callback while sending mgmt packet. + * + * Return: none + */ +static void +wma_mgmt_tx_dload_comp_hldr(void *wma_context, qdf_nbuf_t netbuf, + int32_t status) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + tp_wma_handle wma_handle = (tp_wma_handle) wma_context; + void *mac_context = wma_handle->mac_context; + + WMA_LOGD("Tx Complete Status %d", status); + + if (!wma_handle->tx_frm_download_comp_cb) { + WMA_LOGE("Tx Complete Cb not registered by umac"); + return; + } + + /* Call Tx Mgmt Complete Callback registered by umac */ + wma_handle->tx_frm_download_comp_cb(mac_context, netbuf, 0); + + /* Reset Callback */ + wma_handle->tx_frm_download_comp_cb = NULL; + + /* Set the Tx Mgmt Complete Event */ + qdf_status = qdf_event_set(&wma_handle->tx_frm_download_comp_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + WMA_LOGP("%s: Event Set failed - tx_frm_comp_event", __func__); +} + +/** + * wma_tx_attach() - attach tx related callbacks + * @pwmaCtx: wma context + * + * attaches tx fn with underlying layer. + * + * Return: QDF status + */ +QDF_STATUS wma_tx_attach(tp_wma_handle wma_handle) +{ + /* Get the Vos Context */ + struct cds_context *cds_handle = + (struct cds_context *) (wma_handle->cds_context); + + /* Get the txRx Pdev ID */ + uint8_t pdev_id = WMI_PDEV_ID_SOC; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + /* Register for Tx Management Frames */ + cdp_mgmt_tx_cb_set(soc, pdev_id, 0, + wma_mgmt_tx_dload_comp_hldr, + wma_mgmt_tx_ack_comp_hdlr, wma_handle); + + /* Register callback to send PEER_UNMAP_RESPONSE cmd*/ + if (cdp_cfg_get_peer_unmap_conf_support(soc)) + cdp_peer_unmap_sync_cb_set(soc, pdev_id, + wma_peer_unmap_conf_cb); + + /* Store the Mac Context */ + wma_handle->mac_context = cds_handle->mac_context; + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_tx_detach() - detach tx related callbacks + * @tp_wma_handle: wma context + * + * Deregister with TxRx for Tx Mgmt Download and Ack completion. + * + * Return: QDF status + */ +QDF_STATUS wma_tx_detach(tp_wma_handle wma_handle) +{ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + /* Get the txRx Pdev ID */ + uint8_t pdev_id = WMI_PDEV_ID_SOC; + + if (!soc) { + WMA_LOGE("%s:SOC context is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (pdev_id != OL_TXRX_INVALID_PDEV_ID) { + /* Deregister with TxRx for Tx Mgmt completion call back */ + cdp_mgmt_tx_cb_set(soc, pdev_id, 0, NULL, NULL, NULL); + } + + /* Reset Tx Frm Callbacks */ + wma_handle->tx_frm_download_comp_cb = NULL; + + /* Reset Tx Data Frame Ack Cb */ + wma_handle->umac_data_ota_ack_cb = NULL; + + /* Reset last Tx Data Frame nbuf ptr */ + wma_handle->last_umac_data_nbuf = NULL; + + return QDF_STATUS_SUCCESS; +} + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || \ + defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(CONFIG_HL_SUPPORT) +static void wma_process_vdev_tx_pause_evt(void *soc, + tp_wma_handle wma, + wmi_tx_pause_event_fixed_param *event, + uint8_t vdev_id) +{ + /* PAUSE action, add bitmap */ + if (event->action == ACTION_PAUSE) { + /* Exclude TDLS_OFFCHAN_CHOP from vdev based pauses */ + if (event->pause_type == PAUSE_TYPE_CHOP_TDLS_OFFCHAN) { + cdp_fc_vdev_pause(soc, vdev_id, + OL_TXQ_PAUSE_REASON_FW, + event->pause_type); + } else { + /* + * Now only support per-dev pause so it is not + * necessary to pause a paused queue again. + */ + if (!wma_vdev_get_pause_bitmap(vdev_id)) + cdp_fc_vdev_pause(soc, vdev_id, + OL_TXQ_PAUSE_REASON_FW, + event->pause_type); + + wma_vdev_set_pause_bit(vdev_id, + event->pause_type); + } + } + /* UNPAUSE action, clean bitmap */ + else if (event->action == ACTION_UNPAUSE) { + /* Exclude TDLS_OFFCHAN_CHOP from vdev based pauses */ + if (event->pause_type == PAUSE_TYPE_CHOP_TDLS_OFFCHAN) { + cdp_fc_vdev_unpause(soc, vdev_id, + OL_TXQ_PAUSE_REASON_FW, + event->pause_type); + } else { + /* Handle unpause only if already paused */ + if (wma_vdev_get_pause_bitmap(vdev_id)) { + wma_vdev_clear_pause_bit(vdev_id, + event->pause_type); + + if (wma->interfaces[vdev_id].pause_bitmap) + return; + + /* PAUSE BIT MAP is cleared + * UNPAUSE VDEV + */ + cdp_fc_vdev_unpause(soc, vdev_id, + OL_TXQ_PAUSE_REASON_FW, + event->pause_type); + } + } + } else { + WMA_LOGE("Not Valid Action Type %d", event->action); + } +} + +int wma_mcc_vdev_tx_pause_evt_handler(void *handle, uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_TX_PAUSE_EVENTID_param_tlvs *param_buf; + wmi_tx_pause_event_fixed_param *wmi_event; + uint8_t vdev_id; + A_UINT32 vdev_map; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + param_buf = (WMI_TX_PAUSE_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("Invalid roam event buffer"); + return -EINVAL; + } + + if (ucfg_pmo_get_wow_bus_suspend(wma->psoc)) { + WMA_LOGD(" Suspend is in progress: Pause/Unpause Tx is NoOp"); + return 0; + } + + if (!soc) { + WMA_LOGE("%s:SOC context is NULL", __func__); + return -EINVAL; + } + + wmi_event = param_buf->fixed_param; + vdev_map = wmi_event->vdev_map; + /* FW mapped vdev from ID + * vdev_map = (1 << vdev_id) + * So, host should unmap to ID + */ + for (vdev_id = 0; vdev_map != 0 && vdev_id < wma->max_bssid; + vdev_id++) { + if (!(vdev_map & 0x1)) { + /* No Vdev */ + } else { + if (!wma->interfaces[vdev_id].vdev) { + WMA_LOGE("%s: vdev is NULL for %d", __func__, + vdev_id); + /* Test Next VDEV */ + vdev_map >>= 1; + continue; + } + + wma_process_vdev_tx_pause_evt(soc, wma, + wmi_event, + vdev_id); + + WMA_LOGD + ("vdev_id %d, pause_map 0x%x, pause type %d, action %d", + vdev_id, wma_vdev_get_pause_bitmap(vdev_id), + wmi_event->pause_type, wmi_event->action); + } + /* Test Next VDEV */ + vdev_map >>= 1; + } + + return 0; +} + +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + +#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) + +/** + * wma_set_peer_rate_report_condition - + * this function set peer rate report + * condition info to firmware. + * @handle: Handle of WMA + * @config: Bad peer configuration from SIR module + * + * It is a wrapper function to sent WMI_PEER_SET_RATE_REPORT_CONDITION_CMDID + * to the firmare\target.If the command sent to firmware failed, free the + * buffer that allocated. + * + * Return: QDF_STATUS based on values sent to firmware + */ +static +QDF_STATUS wma_set_peer_rate_report_condition(WMA_HANDLE handle, + struct t_bad_peer_txtcl_config *config) +{ + tp_wma_handle wma_handle = (tp_wma_handle)handle; + struct wmi_peer_rate_report_params rate_report_params = {0}; + u_int32_t i, j; + + rate_report_params.rate_report_enable = config->enable; + rate_report_params.backoff_time = config->tgt_backoff; + rate_report_params.timer_period = config->tgt_report_prd; + for (i = 0; i < WMI_PEER_RATE_REPORT_COND_MAX_NUM; i++) { + rate_report_params.report_per_phy[i].cond_flags = + config->threshold[i].cond; + rate_report_params.report_per_phy[i].delta.delta_min = + config->threshold[i].delta; + rate_report_params.report_per_phy[i].delta.percent = + config->threshold[i].percentage; + for (j = 0; j < WMI_MAX_NUM_OF_RATE_THRESH; j++) { + rate_report_params.report_per_phy[i]. + report_rate_threshold[j] = + config->threshold[i].thresh[j]; + } + } + + return wmi_unified_peer_rate_report_cmd(wma_handle->wmi_handle, + &rate_report_params); +} + +/** + * wma_process_init_bad_peer_tx_ctl_info - + * this function to initialize peer rate report config info. + * @handle: Handle of WMA + * @config: Bad peer configuration from SIR module + * + * This function initializes the bad peer tx control data structure in WMA, + * sends down the initial configuration to the firmware and configures + * the peer status update seeting in the tx_rx module. + * + * Return: QDF_STATUS based on procedure status + */ + +QDF_STATUS wma_process_init_bad_peer_tx_ctl_info(tp_wma_handle wma, + struct t_bad_peer_txtcl_config *config) +{ + /* Parameter sanity check */ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!wma || !config) { + WMA_LOGE("%s Invalid input\n", __func__); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGE("%s enable %d period %d txq limit %d\n", __func__, + config->enable, + config->period, + config->txq_limit); + + /* Only need to initialize the setting + * when the feature is enabled + */ + if (config->enable) { + int i = 0; + + cdp_bad_peer_txctl_set_setting(soc, + WMI_PDEV_ID_SOC, + config->enable, + config->period, + config->txq_limit); + + for (i = 0; i < WLAN_WMA_IEEE80211_MAX_LEVEL; i++) { + u_int32_t threshold, limit; + + threshold = config->threshold[i].thresh[0]; + limit = config->threshold[i].txlimit; + cdp_bad_peer_txctl_update_threshold(soc, + WMI_PDEV_ID_SOC, + i, + threshold, + limit); + } + } + + return wma_set_peer_rate_report_condition(wma, config); +} +#endif /* defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) */ + +#ifdef FW_THERMAL_THROTTLE_SUPPORT +/** + * wma_update_thermal_mitigation_to_fw - update thermal mitigation to fw + * @wma: wma handle + * @thermal_level: thermal level + * + * This function sends down thermal mitigation params to the fw + * + * Returns: QDF_STATUS_SUCCESS for success otherwise failure + */ +static QDF_STATUS wma_update_thermal_mitigation_to_fw(tp_wma_handle wma, + u_int8_t thermal_level) +{ + struct thermal_mitigation_params therm_data = {0}; + + /* Check if vdev is in mcc, if in mcc set dc value as 10, else 100 */ + therm_data.dc = 100; + therm_data.enable = 1; + therm_data.levelconf[0].dcoffpercent = + wma->thermal_mgmt_info.throttle_duty_cycle_tbl[thermal_level]; + therm_data.levelconf[0].priority = 0; + + return wmi_unified_thermal_mitigation_param_cmd_send(wma->wmi_handle, + &therm_data); +} +#else /* FW_THERMAL_THROTTLE_SUPPORT */ +/** + * wma_update_thermal_mitigation_to_fw - update thermal mitigation to fw + * @wma: wma handle + * @thermal_level: thermal level + * + * This function sends down thermal mitigation params to the fw + * + * Returns: QDF_STATUS_SUCCESS for success otherwise failure + */ +static QDF_STATUS wma_update_thermal_mitigation_to_fw(tp_wma_handle wma, + u_int8_t thermal_level) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * wma_update_thermal_cfg_to_fw() - update thermal configuration to FW + * @wma: Pointer to WMA handle + * + * This function update thermal configuration to FW + * + * Returns: QDF_STATUS_SUCCESS for success otherwise failure + */ +static QDF_STATUS wma_update_thermal_cfg_to_fw(tp_wma_handle wma) +{ + t_thermal_cmd_params thermal_params; + + /* Get the temperature thresholds to set in firmware */ + thermal_params.minTemp = + wma->thermal_mgmt_info.thermalLevels[WLAN_WMA_THERMAL_LEVEL_0]. + minTempThreshold; + thermal_params.maxTemp = + wma->thermal_mgmt_info.thermalLevels[WLAN_WMA_THERMAL_LEVEL_0]. + maxTempThreshold; + thermal_params.thermalEnable = + wma->thermal_mgmt_info.thermalMgmtEnabled; + + WMA_LOGE("TM sending to fw: min_temp %d max_temp %d enable %d", + thermal_params.minTemp, thermal_params.maxTemp, + thermal_params.thermalEnable); + + return wma_set_thermal_mgmt(wma, thermal_params); +} + +/** + * wma_process_init_thermal_info() - initialize thermal info + * @wma: Pointer to WMA handle + * @pThermalParams: Pointer to thermal mitigation parameters + * + * This function initializes the thermal management table in WMA, + * sends down the initial temperature thresholds to the firmware + * and configures the throttle period in the tx rx module + * + * Returns: QDF_STATUS_SUCCESS for success otherwise failure + */ +QDF_STATUS wma_process_init_thermal_info(tp_wma_handle wma, + t_thermal_mgmt *pThermalParams) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; +#ifdef FW_THERMAL_THROTTLE_SUPPORT + int i = 0; +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + + if (!wma || !pThermalParams) { + WMA_LOGE("TM Invalid input"); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("TM enable %d period %d", pThermalParams->thermalMgmtEnabled, + pThermalParams->throttlePeriod); + + WMA_LOGD("Throttle Duty Cycle Level in percentage:\n" + "0 %d\n" + "1 %d\n" + "2 %d\n" + "3 %d", + pThermalParams->throttle_duty_cycle_tbl[0], + pThermalParams->throttle_duty_cycle_tbl[1], + pThermalParams->throttle_duty_cycle_tbl[2], + pThermalParams->throttle_duty_cycle_tbl[3]); + + wma->thermal_mgmt_info.thermalMgmtEnabled = + pThermalParams->thermalMgmtEnabled; + wma->thermal_mgmt_info.thermalLevels[0].minTempThreshold = + pThermalParams->thermalLevels[0].minTempThreshold; + wma->thermal_mgmt_info.thermalLevels[0].maxTempThreshold = + pThermalParams->thermalLevels[0].maxTempThreshold; + wma->thermal_mgmt_info.thermalLevels[1].minTempThreshold = + pThermalParams->thermalLevels[1].minTempThreshold; + wma->thermal_mgmt_info.thermalLevels[1].maxTempThreshold = + pThermalParams->thermalLevels[1].maxTempThreshold; + wma->thermal_mgmt_info.thermalLevels[2].minTempThreshold = + pThermalParams->thermalLevels[2].minTempThreshold; + wma->thermal_mgmt_info.thermalLevels[2].maxTempThreshold = + pThermalParams->thermalLevels[2].maxTempThreshold; + wma->thermal_mgmt_info.thermalLevels[3].minTempThreshold = + pThermalParams->thermalLevels[3].minTempThreshold; + wma->thermal_mgmt_info.thermalLevels[3].maxTempThreshold = + pThermalParams->thermalLevels[3].maxTempThreshold; + wma->thermal_mgmt_info.thermalCurrLevel = WLAN_WMA_THERMAL_LEVEL_0; + + WMA_LOGD("TM level min max:\n" + "0 %d %d\n" + "1 %d %d\n" + "2 %d %d\n" + "3 %d %d", + wma->thermal_mgmt_info.thermalLevels[0].minTempThreshold, + wma->thermal_mgmt_info.thermalLevels[0].maxTempThreshold, + wma->thermal_mgmt_info.thermalLevels[1].minTempThreshold, + wma->thermal_mgmt_info.thermalLevels[1].maxTempThreshold, + wma->thermal_mgmt_info.thermalLevels[2].minTempThreshold, + wma->thermal_mgmt_info.thermalLevels[2].maxTempThreshold, + wma->thermal_mgmt_info.thermalLevels[3].minTempThreshold, + wma->thermal_mgmt_info.thermalLevels[3].maxTempThreshold); + +#ifdef FW_THERMAL_THROTTLE_SUPPORT + for (i = 0; i < THROTTLE_LEVEL_MAX; i++) + wma->thermal_mgmt_info.throttle_duty_cycle_tbl[i] = + pThermalParams->throttle_duty_cycle_tbl[i]; +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ + + if (wma->thermal_mgmt_info.thermalMgmtEnabled) { + if (!wma->fw_therm_throt_support) { + cdp_throttle_init_period( + cds_get_context(QDF_MODULE_ID_SOC), + WMI_PDEV_ID_SOC, pThermalParams->throttlePeriod, + &pThermalParams->throttle_duty_cycle_tbl[0]); + } else { + qdf_status = wma_update_thermal_mitigation_to_fw( + wma, WLAN_WMA_THERMAL_LEVEL_0); + if (QDF_STATUS_SUCCESS != qdf_status) + return qdf_status; + } + qdf_status = wma_update_thermal_cfg_to_fw(wma); + } + return qdf_status; +} + +/** + * wma_set_thermal_level_ind() - send SME set thermal level indication message + * @level: thermal level + * + * Send SME SET_THERMAL_LEVEL_IND message + * + * Returns: none + */ +static void wma_set_thermal_level_ind(u_int8_t level) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct scheduler_msg sme_msg = {0}; + + WMA_LOGI(FL("Thermal level: %d"), level); + + sme_msg.type = eWNI_SME_SET_THERMAL_LEVEL_IND; + sme_msg.bodyptr = NULL; + sme_msg.bodyval = level; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + WMA_LOGE(FL( + "Fail to post set thermal level ind msg")); +} + +/** + * wma_process_set_thermal_level() - sets thermal level + * @wma: Pointer to WMA handle + * @thermal_level : Thermal level + * + * This function sets the new thermal throttle level in the + * txrx module and sends down the corresponding temperature + * thresholds to the firmware + * + * Returns: QDF_STATUS_SUCCESS for success otherwise failure + */ +QDF_STATUS wma_process_set_thermal_level(tp_wma_handle wma, + uint8_t thermal_level) +{ + if (!wma) { + WMA_LOGE("TM Invalid input"); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGE("TM set level %d", thermal_level); + + /* Check if thermal mitigation is enabled */ + if (!wma->thermal_mgmt_info.thermalMgmtEnabled) { + WMA_LOGE("Thermal mgmt is not enabled, ignoring set level command"); + return QDF_STATUS_E_FAILURE; + } + + if (thermal_level >= WLAN_WMA_MAX_THERMAL_LEVELS) { + WMA_LOGE("Invalid thermal level set %d", thermal_level); + return QDF_STATUS_E_FAILURE; + } + + if (thermal_level == wma->thermal_mgmt_info.thermalCurrLevel) { + WMA_LOGD("Current level %d is same as the set level, ignoring", + wma->thermal_mgmt_info.thermalCurrLevel); + return QDF_STATUS_SUCCESS; + } + + wma->thermal_mgmt_info.thermalCurrLevel = thermal_level; + + cdp_throttle_set_level(cds_get_context(QDF_MODULE_ID_SOC), + WMI_PDEV_ID_SOC, thermal_level); + + /* Send SME SET_THERMAL_LEVEL_IND message */ + wma_set_thermal_level_ind(thermal_level); + + return QDF_STATUS_SUCCESS; +} + + +/** + * wma_set_thermal_mgmt() - set thermal mgmt command to fw + * @wma_handle: Pointer to WMA handle + * @thermal_info: Thermal command information + * + * This function sends the thermal management command + * to the firmware + * + * Return: QDF_STATUS_SUCCESS for success otherwise failure + */ +QDF_STATUS wma_set_thermal_mgmt(tp_wma_handle wma_handle, + t_thermal_cmd_params thermal_info) +{ + struct thermal_cmd_params mgmt_thermal_info = {0}; + + if (!wma_handle) { + wma_err("Invalid input"); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + mgmt_thermal_info.min_temp = thermal_info.minTemp; + mgmt_thermal_info.max_temp = thermal_info.maxTemp; + mgmt_thermal_info.thermal_enable = thermal_info.thermalEnable; + + return wmi_unified_set_thermal_mgmt_cmd(wma_handle->wmi_handle, + &mgmt_thermal_info); +} + +/** + * wma_thermal_mgmt_get_level() - returns throttle level + * @handle: Pointer to WMA handle + * @temp: temperature + * + * This function returns the thermal(throttle) level + * given the temperature + * + * Return: thermal (throttle) level + */ +static uint8_t wma_thermal_mgmt_get_level(void *handle, uint32_t temp) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + int i; + uint8_t level; + + level = i = wma->thermal_mgmt_info.thermalCurrLevel; + while (temp < wma->thermal_mgmt_info.thermalLevels[i].minTempThreshold + && i > 0) { + i--; + level = i; + } + + i = wma->thermal_mgmt_info.thermalCurrLevel; + while (temp > wma->thermal_mgmt_info.thermalLevels[i].maxTempThreshold + && i < (WLAN_WMA_MAX_THERMAL_LEVELS - 1)) { + i++; + level = i; + } + + WMA_LOGW("Change thermal level from %d -> %d\n", + wma->thermal_mgmt_info.thermalCurrLevel, level); + + return level; +} + +/** + * wma_thermal_mgmt_evt_handler() - thermal mgmt event handler + * @wma_handle: Pointer to WMA handle + * @event: Thermal event information + * @len: length of the event + * + * This function handles the thermal mgmt event from the firmware + * + * Return: 0 for success otherwise failure + */ +int wma_thermal_mgmt_evt_handler(void *handle, uint8_t *event, uint32_t len) +{ + tp_wma_handle wma; + wmi_thermal_mgmt_event_fixed_param *tm_event; + uint8_t thermal_level; + t_thermal_cmd_params thermal_params; + WMI_THERMAL_MGMT_EVENTID_param_tlvs *param_buf; + + if (!event || !handle) { + WMA_LOGE("Invalid thermal mitigation event buffer"); + return -EINVAL; + } + + wma = (tp_wma_handle) handle; + + if (!wma) { + WMA_LOGE("%s: Failed to get wma handle", __func__); + return -EINVAL; + } + + param_buf = (WMI_THERMAL_MGMT_EVENTID_param_tlvs *) event; + + /* Check if thermal mitigation is enabled */ + if (!wma->thermal_mgmt_info.thermalMgmtEnabled) { + WMA_LOGE("Thermal mgmt is not enabled, ignoring event"); + return -EINVAL; + } + + tm_event = param_buf->fixed_param; + WMA_LOGD("Thermal mgmt event received with temperature %d", + tm_event->temperature_degreeC); + + /* Get the thermal mitigation level for the reported temperature */ + thermal_level = wma_thermal_mgmt_get_level(handle, + tm_event->temperature_degreeC); + WMA_LOGD("Thermal mgmt level %d", thermal_level); + + if (thermal_level == wma->thermal_mgmt_info.thermalCurrLevel) { + WMA_LOGD("Current level %d is same as the set level, ignoring", + wma->thermal_mgmt_info.thermalCurrLevel); + return 0; + } + + wma->thermal_mgmt_info.thermalCurrLevel = thermal_level; + + if (!wma->fw_therm_throt_support) { + /* Inform txrx */ + cdp_throttle_set_level(cds_get_context(QDF_MODULE_ID_SOC), + WMI_PDEV_ID_SOC, thermal_level); + } + + /* Send SME SET_THERMAL_LEVEL_IND message */ + wma_set_thermal_level_ind(thermal_level); + + if (wma->fw_therm_throt_support) { + /* Send duty cycle info to firmware for fw to throttle */ + if (QDF_STATUS_SUCCESS != + wma_update_thermal_mitigation_to_fw(wma, thermal_level)) + return QDF_STATUS_E_FAILURE; + } + + /* Get the temperature thresholds to set in firmware */ + thermal_params.minTemp = + wma->thermal_mgmt_info.thermalLevels[thermal_level]. + minTempThreshold; + thermal_params.maxTemp = + wma->thermal_mgmt_info.thermalLevels[thermal_level]. + maxTempThreshold; + thermal_params.thermalEnable = + wma->thermal_mgmt_info.thermalMgmtEnabled; + + if (QDF_STATUS_SUCCESS != wma_set_thermal_mgmt(wma, thermal_params)) { + WMA_LOGE("Could not send thermal mgmt command to the firmware!"); + return -EINVAL; + } + + return 0; +} + +#ifdef QCA_IBSS_SUPPORT +int wma_ibss_peer_info_event_handler(void *handle, uint8_t *data, + uint32_t len) +{ + struct scheduler_msg cds_msg = {0}; + wmi_peer_info *peer_info; + tSirIbssPeerInfoParams *pSmeRsp; + uint32_t count, num_peers, status; + tSirIbssGetPeerInfoRspParams *pRsp; + WMI_PEER_INFO_EVENTID_param_tlvs *param_tlvs; + wmi_peer_info_event_fixed_param *fix_param; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + tp_wma_handle wma = (tp_wma_handle)handle; + + if (!wma) { + WMA_LOGE("Invalid wma"); + return 0; + } + + param_tlvs = (WMI_PEER_INFO_EVENTID_param_tlvs *) data; + fix_param = param_tlvs->fixed_param; + peer_info = param_tlvs->peer_info; + num_peers = fix_param->num_peers; + status = 0; + + WMA_LOGE("%s: num_peers %d", __func__, num_peers); + + pRsp = qdf_mem_malloc(sizeof(tSirIbssGetPeerInfoRspParams)); + if (!pRsp) + return 0; + + /*sanity check */ + if (!(num_peers) || (num_peers > 32) || + (num_peers > param_tlvs->num_peer_info) || + (!peer_info)) { + WMA_LOGE("%s: Invalid event data from target num_peers %d peer_info %pK", + __func__, num_peers, peer_info); + status = 1; + goto send_response; + } + + /* + *For displaying only connected IBSS peer info, iterate till + *last but one entry only as last entry is used for IBSS creator + */ + for (count = 0; count < num_peers-1; count++) { + pSmeRsp = &pRsp->ibssPeerInfoRspParams.peerInfoParams[count]; + + WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_info->peer_mac_address, + peer_mac); + qdf_mem_copy(pSmeRsp->mac_addr, peer_mac, + sizeof(pSmeRsp->mac_addr)); + pSmeRsp->mcsIndex = 0; + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_hw_db2dbm_support)) + pSmeRsp->rssi = peer_info->rssi; + else + pSmeRsp->rssi = peer_info->rssi + + WMA_TGT_NOISE_FLOOR_DBM; + + pSmeRsp->txRate = peer_info->data_rate; + + wma_err("peer " QDF_MAC_ADDR_FMT "rssi %d txRate %d", + QDF_MAC_ADDR_REF(peer_mac), + pSmeRsp->rssi, pSmeRsp->txRate); + + peer_info++; + } + +send_response: + /* message header */ + pRsp->mesgType = eWNI_SME_IBSS_PEER_INFO_RSP; + pRsp->mesgLen = sizeof(tSirIbssGetPeerInfoRspParams); + pRsp->ibssPeerInfoRspParams.status = status; + pRsp->ibssPeerInfoRspParams.numPeers = num_peers; + + /* cds message wrapper */ + cds_msg.type = eWNI_SME_IBSS_PEER_INFO_RSP; + cds_msg.bodyptr = (void *)pRsp; + cds_msg.bodyval = 0; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &cds_msg)) { + WMA_LOGE("%s: could not post peer info rsp msg to SME", + __func__); + /* free the mem and return */ + qdf_mem_free((void *)pRsp); + } + + return 0; +} +#endif + +/** + * wma_fast_tx_fail_event_handler() -tx failure event handler + * @handle: wma handle + * @data: event data + * @len: data length + * + * Handle fast tx failure indication event from FW + * + * Return: 0 for success or error code. + */ +int wma_fast_tx_fail_event_handler(void *handle, uint8_t *data, + uint32_t len) +{ + uint8_t tx_fail_cnt; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_PEER_TX_FAIL_CNT_THR_EVENTID_param_tlvs *param_tlvs; + wmi_peer_tx_fail_cnt_thr_event_fixed_param *fix_param; + + param_tlvs = (WMI_PEER_TX_FAIL_CNT_THR_EVENTID_param_tlvs *) data; + fix_param = param_tlvs->fixed_param; + + WMI_MAC_ADDR_TO_CHAR_ARRAY(&fix_param->peer_mac_address, peer_mac); + WMA_LOGE("%s: received fast tx failure event for peer 0x:%2x:0x%2x:0x%2x:0x%2x:0x%2x:0x%2x seq No %d", + __func__, + peer_mac[0], peer_mac[1], peer_mac[2], peer_mac[3], + peer_mac[4], peer_mac[5], fix_param->seq_no); + + tx_fail_cnt = fix_param->seq_no; + + /*call HDD callback */ + if (wma->hddTxFailCb) + wma->hddTxFailCb(peer_mac, tx_fail_cnt); + else + WMA_LOGE("%s: HDD callback is %pK", __func__, wma->hddTxFailCb); + + return 0; +} + +/** + * wma_decap_to_8023() - Decapsulate to 802.3 format + * @msdu: skb buffer + * @info: decapsulate info + * + * Return: none + */ +static void wma_decap_to_8023(qdf_nbuf_t msdu, struct wma_decap_info_t *info) +{ + struct llc_snap_hdr_t *llc_hdr; + uint16_t ether_type; + uint16_t l2_hdr_space; + struct ieee80211_qosframe_addr4 *wh; + uint8_t local_buf[ETHERNET_HDR_LEN]; + uint8_t *buf; + struct ethernet_hdr_t *ethr_hdr; + + buf = (uint8_t *) qdf_nbuf_data(msdu); + llc_hdr = (struct llc_snap_hdr_t *)buf; + ether_type = (llc_hdr->ethertype[0] << 8) | llc_hdr->ethertype[1]; + /* do llc remove if needed */ + l2_hdr_space = 0; + if (IS_SNAP(llc_hdr)) { + if (IS_BTEP(llc_hdr)) { + /* remove llc */ + l2_hdr_space += sizeof(struct llc_snap_hdr_t); + llc_hdr = NULL; + } else if (IS_RFC1042(llc_hdr)) { + if (!(ether_type == ETHERTYPE_AARP || + ether_type == ETHERTYPE_IPX)) { + /* remove llc */ + l2_hdr_space += sizeof(struct llc_snap_hdr_t); + llc_hdr = NULL; + } + } + } + if (l2_hdr_space > ETHERNET_HDR_LEN) + buf = qdf_nbuf_pull_head(msdu, l2_hdr_space - ETHERNET_HDR_LEN); + else if (l2_hdr_space < ETHERNET_HDR_LEN) + buf = qdf_nbuf_push_head(msdu, ETHERNET_HDR_LEN - l2_hdr_space); + + /* mpdu hdr should be present in info,re-create ethr_hdr based on + * mpdu hdr + */ + wh = (struct ieee80211_qosframe_addr4 *)info->hdr; + ethr_hdr = (struct ethernet_hdr_t *)local_buf; + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr1, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr2, + QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_TODS: + qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr3, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr2, + QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_FROMDS: + qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr1, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr3, + QDF_MAC_ADDR_SIZE); + break; + case IEEE80211_FC1_DIR_DSTODS: + qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr3, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr4, + QDF_MAC_ADDR_SIZE); + break; + } + + if (!llc_hdr) { + ethr_hdr->ethertype[0] = (ether_type >> 8) & 0xff; + ethr_hdr->ethertype[1] = (ether_type) & 0xff; + } else { + uint32_t pktlen = + qdf_nbuf_len(msdu) - sizeof(ethr_hdr->ethertype); + ether_type = (uint16_t) pktlen; + ether_type = qdf_nbuf_len(msdu) - sizeof(struct ethernet_hdr_t); + ethr_hdr->ethertype[0] = (ether_type >> 8) & 0xff; + ethr_hdr->ethertype[1] = (ether_type) & 0xff; + } + qdf_mem_copy(buf, ethr_hdr, ETHERNET_HDR_LEN); +} + +/** + * wma_ieee80211_hdrsize() - get 802.11 header size + * @data: 80211 frame + * + * Return: size of header + */ +static int32_t wma_ieee80211_hdrsize(const void *data) +{ + const struct ieee80211_frame *wh = (const struct ieee80211_frame *)data; + int32_t size = sizeof(struct ieee80211_frame); + + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) + size += QDF_MAC_ADDR_SIZE; + if (IEEE80211_QOS_HAS_SEQ(wh)) + size += sizeof(uint16_t); + return size; +} + +/** + * rate_pream: Mapping from data rates to preamble. + */ +static uint32_t rate_pream[] = {WMI_RATE_PREAMBLE_CCK, WMI_RATE_PREAMBLE_CCK, + WMI_RATE_PREAMBLE_CCK, WMI_RATE_PREAMBLE_CCK, + WMI_RATE_PREAMBLE_OFDM, WMI_RATE_PREAMBLE_OFDM, + WMI_RATE_PREAMBLE_OFDM, WMI_RATE_PREAMBLE_OFDM, + WMI_RATE_PREAMBLE_OFDM, WMI_RATE_PREAMBLE_OFDM, + WMI_RATE_PREAMBLE_OFDM, WMI_RATE_PREAMBLE_OFDM}; + +/** + * rate_mcs: Mapping from data rates to MCS (+4 for OFDM to keep the sequence). + */ +static uint32_t rate_mcs[] = {WMI_MAX_CCK_TX_RATE_1M, WMI_MAX_CCK_TX_RATE_2M, + WMI_MAX_CCK_TX_RATE_5_5M, WMI_MAX_CCK_TX_RATE_11M, + WMI_MAX_OFDM_TX_RATE_6M + 4, + WMI_MAX_OFDM_TX_RATE_9M + 4, + WMI_MAX_OFDM_TX_RATE_12M + 4, + WMI_MAX_OFDM_TX_RATE_18M + 4, + WMI_MAX_OFDM_TX_RATE_24M + 4, + WMI_MAX_OFDM_TX_RATE_36M + 4, + WMI_MAX_OFDM_TX_RATE_48M + 4, + WMI_MAX_OFDM_TX_RATE_54M + 4}; + +#define WMA_TX_SEND_MGMT_TYPE 0 +#define WMA_TX_SEND_DATA_TYPE 1 + +/** + * wma_update_tx_send_params() - Update tx_send_params TLV info + * @tx_param: Pointer to tx_send_params + * @rid: rate ID passed by PE + * + * Return: None + */ +static void wma_update_tx_send_params(struct tx_send_params *tx_param, + enum rateid rid) +{ + uint8_t preamble = 0, nss = 0, rix = 0; + + preamble = rate_pream[rid]; + rix = rate_mcs[rid]; + + tx_param->mcs_mask = (1 << rix); + tx_param->nss_mask = (1 << nss); + tx_param->preamble_type = (1 << preamble); + tx_param->frame_type = WMA_TX_SEND_MGMT_TYPE; + + WMA_LOGD(FL("rate_id: %d, mcs: %0x, nss: %0x, preamble: %0x"), + rid, tx_param->mcs_mask, tx_param->nss_mask, + tx_param->preamble_type); +} + +#ifdef WLAN_FEATURE_11W +uint8_t *wma_get_igtk(struct wma_txrx_node *iface, uint16_t *key_len, + uint16_t igtk_key_idx) +{ + struct wlan_crypto_key *crypto_key; + + if (!(igtk_key_idx == WMA_IGTK_KEY_INDEX_4 || + igtk_key_idx == WMA_IGTK_KEY_INDEX_5)) { + wma_err("Invalid igtk_key_idx %d", igtk_key_idx); + *key_len = 0; + return NULL; + } + crypto_key = wlan_crypto_get_key(iface->vdev, igtk_key_idx); + if (!crypto_key) { + wma_err("IGTK not found for igtk_idx %d", igtk_key_idx); + *key_len = 0; + return NULL; + } + *key_len = crypto_key->keylen; + + return &crypto_key->keyval[0]; +} +#endif + +QDF_STATUS wma_tx_packet(void *wma_context, void *tx_frame, uint16_t frmLen, + eFrameType frmType, eFrameTxDir txDir, uint8_t tid, + wma_tx_dwnld_comp_callback tx_frm_download_comp_cb, + void *pData, + wma_tx_ota_comp_callback tx_frm_ota_comp_cb, + uint8_t tx_flag, uint8_t vdev_id, bool tdls_flag, + uint16_t channel_freq, enum rateid rid) +{ + tp_wma_handle wma_handle = (tp_wma_handle) (wma_context); + int32_t status; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + int32_t is_high_latency; + bool is_wmi_mgmt_tx = false; + enum frame_index tx_frm_index = GENERIC_NODOWNLD_NOACK_COMP_INDEX; + tpSirMacFrameCtl pFc = (tpSirMacFrameCtl) (qdf_nbuf_data(tx_frame)); + uint8_t use_6mbps = 0; + uint8_t downld_comp_required = 0; + uint16_t chanfreq; +#ifdef WLAN_FEATURE_11W + uint8_t *pFrame = NULL; + void *pPacket = NULL; + uint16_t newFrmLen = 0; + uint8_t *igtk; + uint16_t key_len; +#endif /* WLAN_FEATURE_11W */ + struct wma_txrx_node *iface; + struct mac_context *mac; + tpSirMacMgmtHdr mHdr; + struct wmi_mgmt_params mgmt_param = {0}; + struct cdp_cfg *ctrl_pdev; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct ieee80211_frame *wh; + struct wlan_objmgr_peer *peer = NULL; + struct wlan_objmgr_psoc *psoc; + void *mac_addr; + bool is_5g = false; + uint8_t pdev_id; + + if (!wma_handle) { + WMA_LOGE("wma_handle is NULL"); + cds_packet_free((void *)tx_frame); + return QDF_STATUS_E_FAILURE; + } + iface = &wma_handle->interfaces[vdev_id]; + + if (!soc) { + WMA_LOGE("%s:SOC context is NULL", __func__); + cds_packet_free((void *)tx_frame); + return QDF_STATUS_E_FAILURE; + } + + cdp_hl_tdls_flag_reset(soc, vdev_id, false); + + if (frmType >= TXRX_FRM_MAX) { + WMA_LOGE("Invalid Frame Type Fail to send Frame"); + cds_packet_free((void *)tx_frame); + return QDF_STATUS_E_FAILURE; + } + + mac = cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + cds_packet_free((void *)tx_frame); + return QDF_STATUS_E_FAILURE; + } + /* + * Currently only support to + * send 80211 Mgmt and 80211 Data are added. + */ + if (!((frmType == TXRX_FRM_802_11_MGMT) || + (frmType == TXRX_FRM_802_11_DATA))) { + WMA_LOGE("No Support to send other frames except 802.11 Mgmt/Data"); + cds_packet_free((void *)tx_frame); + return QDF_STATUS_E_FAILURE; + } + +#ifdef WLAN_FEATURE_11W + if ((iface && (iface->rmfEnabled || tx_flag & HAL_USE_PMF)) && + (frmType == TXRX_FRM_802_11_MGMT) && + (pFc->subType == SIR_MAC_MGMT_DISASSOC || + pFc->subType == SIR_MAC_MGMT_DEAUTH || + pFc->subType == SIR_MAC_MGMT_ACTION)) { + struct ieee80211_frame *wh = + (struct ieee80211_frame *)qdf_nbuf_data(tx_frame); + if (!QDF_IS_ADDR_BROADCAST(wh->i_addr1) && + !IEEE80211_IS_MULTICAST(wh->i_addr1)) { + if (pFc->wep) { + uint8_t mic_len, hdr_len, pdev_id; + + /* Allocate extra bytes for privacy header and + * trailer + */ + if (iface->type == WMI_VDEV_TYPE_NDI && + (tx_flag & HAL_USE_PMF)) { + hdr_len = IEEE80211_CCMP_HEADERLEN; + mic_len = IEEE80211_CCMP_MICLEN; + } else { + pdev_id = wlan_objmgr_pdev_get_pdev_id( + wma_handle->pdev); + qdf_status = mlme_get_peer_mic_len( + wma_handle->psoc, + pdev_id, wh->i_addr1, + &mic_len, &hdr_len); + + if (QDF_IS_STATUS_ERROR(qdf_status)) { + cds_packet_free(( + void *)tx_frame); + goto error; + } + } + + newFrmLen = frmLen + hdr_len + mic_len; + qdf_status = + cds_packet_alloc((uint16_t) newFrmLen, + (void **)&pFrame, + (void **)&pPacket); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGP("%s: Failed to allocate %d bytes for RMF status code (%x)", + __func__, newFrmLen, + qdf_status); + /* Free the original packet memory */ + cds_packet_free((void *)tx_frame); + goto error; + } + + /* + * Initialize the frame with 0's and only fill + * MAC header and data, Keep the CCMP header and + * trailer as 0's, firmware shall fill this + */ + qdf_mem_zero(pFrame, newFrmLen); + qdf_mem_copy(pFrame, wh, sizeof(*wh)); + qdf_mem_copy(pFrame + sizeof(*wh) + + hdr_len, + pData + sizeof(*wh), + frmLen - sizeof(*wh)); + + cds_packet_free((void *)tx_frame); + tx_frame = pPacket; + pData = pFrame; + frmLen = newFrmLen; + pFc = (tpSirMacFrameCtl) + (qdf_nbuf_data(tx_frame)); + } + } else { + int8_t igtk_key_id; + + /* Allocate extra bytes for MMIE */ + newFrmLen = frmLen + IEEE80211_MMIE_LEN; + qdf_status = cds_packet_alloc((uint16_t) newFrmLen, + (void **)&pFrame, + (void **)&pPacket); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGP("%s: Failed to allocate %d bytes for RMF status code (%x)", + __func__, newFrmLen, + qdf_status); + /* Free the original packet memory */ + cds_packet_free((void *)tx_frame); + goto error; + } + /* + * Initialize the frame with 0's and only fill + * MAC header and data. MMIE field will be + * filled by cds_attach_mmie API + */ + qdf_mem_zero(pFrame, newFrmLen); + qdf_mem_copy(pFrame, wh, sizeof(*wh)); + qdf_mem_copy(pFrame + sizeof(*wh), + pData + sizeof(*wh), frmLen - sizeof(*wh)); + igtk_key_id = + wlan_crypto_get_default_key_idx(iface->vdev, + true); + /* Get actual igtk key id adding 4 */ + igtk_key_id += WMA_IGTK_KEY_INDEX_4; + igtk = wma_get_igtk(iface, &key_len, igtk_key_id); + if (!igtk) { + wma_err_rl("IGTK not present for igtk_key_id %d", + igtk_key_id); + cds_packet_free((void *)tx_frame); + cds_packet_free((void *)pPacket); + goto error; + } + if (!cds_attach_mmie(igtk, iface->key.key_id[ + igtk_key_id - + WMA_IGTK_KEY_INDEX_4].ipn, + igtk_key_id, + pFrame, + pFrame + newFrmLen, newFrmLen)) { + wma_alert("Failed to attach MMIE"); + /* Free the original packet memory */ + cds_packet_free((void *)tx_frame); + cds_packet_free((void *)pPacket); + goto error; + } + cds_packet_free((void *)tx_frame); + tx_frame = pPacket; + pData = pFrame; + frmLen = newFrmLen; + pFc = (tpSirMacFrameCtl) (qdf_nbuf_data(tx_frame)); + } + /* + * Some target which support sending mgmt frame based on htt + * would DMA write this PMF tx frame buffer, it may cause smmu + * check permission fault, set a flag to do bi-direction DMA + * map, normal tx unmap is enough for this case. + */ + QDF_NBUF_CB_TX_DMA_BI_MAP((qdf_nbuf_t)tx_frame) = 1; + } +#endif /* WLAN_FEATURE_11W */ + mHdr = (tpSirMacMgmtHdr)qdf_nbuf_data(tx_frame); + if ((frmType == TXRX_FRM_802_11_MGMT) && + (pFc->subType == SIR_MAC_MGMT_PROBE_RSP)) { + uint64_t adjusted_tsf_le; + struct ieee80211_frame *wh = + (struct ieee80211_frame *)qdf_nbuf_data(tx_frame); + + /* Make the TSF offset negative to match TSF in beacons */ + adjusted_tsf_le = cpu_to_le64(0ULL - + wma_handle->interfaces[vdev_id]. + tsfadjust); + A_MEMCPY(&wh[1], &adjusted_tsf_le, sizeof(adjusted_tsf_le)); + } + if (frmType == TXRX_FRM_802_11_DATA) { + qdf_nbuf_t ret; + qdf_nbuf_t skb = (qdf_nbuf_t) tx_frame; + + struct wma_decap_info_t decap_info; + struct ieee80211_frame *wh = + (struct ieee80211_frame *)qdf_nbuf_data(skb); + unsigned long curr_timestamp = qdf_mc_timer_get_system_ticks(); + + /* + * 1) TxRx Module expects data input to be 802.3 format + * So Decapsulation has to be done. + * 2) Only one Outstanding Data pending for Ack is allowed + */ + if (tx_frm_ota_comp_cb) { + if (wma_handle->umac_data_ota_ack_cb) { + /* + * If last data frame was sent more than 5 secs + * ago and still we didn't receive ack/nack from + * fw then allow Tx of this data frame + */ + if (curr_timestamp >= + wma_handle->last_umac_data_ota_timestamp + + 500) { + WMA_LOGE("%s: No Tx Ack for last data frame for more than 5 secs, allow Tx of current data frame", + __func__); + } else { + WMA_LOGE("%s: Already one Data pending for Ack, reject Tx of data frame", + __func__); + cds_packet_free((void *)tx_frame); + return QDF_STATUS_E_FAILURE; + } + } + } else { + /* + * Data Frames are sent through TxRx Non Standard Data + * path so Ack Complete Cb is must + */ + WMA_LOGE("No Ack Complete Cb. Don't Allow"); + cds_packet_free((void *)tx_frame); + return QDF_STATUS_E_FAILURE; + } + + /* Take out 802.11 header from skb */ + decap_info.hdr_len = wma_ieee80211_hdrsize(wh); + qdf_mem_copy(decap_info.hdr, wh, decap_info.hdr_len); + qdf_nbuf_pull_head(skb, decap_info.hdr_len); + + /* Decapsulate to 802.3 format */ + wma_decap_to_8023(skb, &decap_info); + + /* Zero out skb's context buffer for the driver to use */ + qdf_mem_zero(skb->cb, sizeof(skb->cb)); + + /* Terminate the (single-element) list of tx frames */ + skb->next = NULL; + + /* Store the Ack Complete Cb */ + wma_handle->umac_data_ota_ack_cb = tx_frm_ota_comp_cb; + + /* Store the timestamp and nbuf for this data Tx */ + wma_handle->last_umac_data_ota_timestamp = curr_timestamp; + wma_handle->last_umac_data_nbuf = skb; + + /* Send the Data frame to TxRx in Non Standard Path */ + cdp_hl_tdls_flag_reset(soc, + vdev_id, tdls_flag); + + ret = cdp_tx_non_std(soc, + vdev_id, + OL_TX_SPEC_NO_FREE, skb); + + cdp_hl_tdls_flag_reset(soc, + vdev_id, false); + + if (ret) { + WMA_LOGE("TxRx Rejected. Fail to do Tx"); + /* Call Download Cb so that umac can free the buffer */ + if (tx_frm_download_comp_cb) + tx_frm_download_comp_cb(wma_handle->mac_context, + tx_frame, + WMA_TX_FRAME_BUFFER_FREE); + wma_handle->umac_data_ota_ack_cb = NULL; + wma_handle->last_umac_data_nbuf = NULL; + return QDF_STATUS_E_FAILURE; + } + + /* Call Download Callback if passed */ + if (tx_frm_download_comp_cb) + tx_frm_download_comp_cb(wma_handle->mac_context, + tx_frame, + WMA_TX_FRAME_BUFFER_NO_FREE); + + return QDF_STATUS_SUCCESS; + } + + ctrl_pdev = cdp_get_ctrl_pdev_from_vdev(soc, vdev_id); + if (!ctrl_pdev) { + WMA_LOGE("ol_pdev_handle is NULL\n"); + cds_packet_free((void *)tx_frame); + return QDF_STATUS_E_FAILURE; + } + is_high_latency = cdp_cfg_is_high_latency(soc, ctrl_pdev); + is_wmi_mgmt_tx = wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_mgmt_tx_wmi); + + downld_comp_required = tx_frm_download_comp_cb && is_high_latency && + (!is_wmi_mgmt_tx) && tx_frm_ota_comp_cb; + + /* Fill the frame index to send */ + if (pFc->type == SIR_MAC_MGMT_FRAME) { + if (tx_frm_ota_comp_cb) { + if (downld_comp_required) + tx_frm_index = + GENERIC_DOWNLD_COMP_ACK_COMP_INDEX; + else + tx_frm_index = GENERIC_NODOWLOAD_ACK_COMP_INDEX; + + } else { + tx_frm_index = + GENERIC_NODOWNLD_NOACK_COMP_INDEX; + } + } + + /* + * If Dowload Complete is required + * Wait for download complete + */ + if (downld_comp_required) { + /* Store Tx Comp Cb */ + wma_handle->tx_frm_download_comp_cb = tx_frm_download_comp_cb; + + /* Reset the Tx Frame Complete Event */ + qdf_status = qdf_event_reset( + &wma_handle->tx_frm_download_comp_event); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGP("%s: Event Reset failed tx comp event %x", + __func__, qdf_status); + cds_packet_free((void *)tx_frame); + goto error; + } + } + + /* If the frame has to be sent at BD Rate2 inform TxRx */ + if (tx_flag & HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME) + use_6mbps = 1; + + if (pFc->subType == SIR_MAC_MGMT_PROBE_RSP) { + if ((wma_is_vdev_in_ap_mode(wma_handle, vdev_id)) && + (0 != wma_handle->interfaces[vdev_id].mhz)) + chanfreq = wma_handle->interfaces[vdev_id].mhz; + else + chanfreq = channel_freq; + WMA_LOGD("%s: Probe response frame on channel %d vdev:%d", + __func__, chanfreq, vdev_id); + if (wma_is_vdev_in_ap_mode(wma_handle, vdev_id) && !chanfreq) + WMA_LOGE("%s: AP oper chan is zero", __func__); + } else if (pFc->subType == SIR_MAC_MGMT_ACTION || + pFc->subType == SIR_MAC_MGMT_AUTH) { + chanfreq = channel_freq; + } else { + chanfreq = 0; + } + + if (mac->mlme_cfg->gen.debug_packet_log & 0x1) { + if ((pFc->type == SIR_MAC_MGMT_FRAME) && + (pFc->subType != SIR_MAC_MGMT_PROBE_REQ) && + (pFc->subType != SIR_MAC_MGMT_PROBE_RSP)) { + WMA_LOGD("TX MGMT - Type %hu, SubType %hu seq_num[%d]", + pFc->type, pFc->subType, + ((mHdr->seqControl.seqNumHi << 4) | + mHdr->seqControl.seqNumLo)); + } + } + + if (wlan_reg_is_5ghz_ch_freq(wma_handle->interfaces[vdev_id].ch_freq)) + is_5g = true; + + mgmt_param.tx_frame = tx_frame; + mgmt_param.frm_len = frmLen; + mgmt_param.vdev_id = vdev_id; + mgmt_param.pdata = pData; + mgmt_param.chanfreq = chanfreq; + mgmt_param.qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + mgmt_param.use_6mbps = use_6mbps; + mgmt_param.tx_type = tx_frm_index; + + /* + * Update the tx_params TLV only for rates + * other than 1Mbps and 6 Mbps + */ + if (rid < RATEID_DEFAULT && + (rid != RATEID_1MBPS && !(rid == RATEID_6MBPS && is_5g))) { + WMA_LOGD(FL("using rate id: %d for Tx"), rid); + mgmt_param.tx_params_valid = true; + wma_update_tx_send_params(&mgmt_param.tx_param, rid); + } + + psoc = wma_handle->psoc; + if (!psoc) { + WMA_LOGE("%s: psoc ctx is NULL", __func__); + cds_packet_free((void *)tx_frame); + goto error; + } + + if (!wma_handle->pdev) { + WMA_LOGE("%s: pdev ctx is NULL", __func__); + cds_packet_free((void *)tx_frame); + goto error; + } + + pdev_id = wlan_objmgr_pdev_get_pdev_id(wma_handle->pdev); + wh = (struct ieee80211_frame *)(qdf_nbuf_data(tx_frame)); + mac_addr = wh->i_addr1; + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_MGMT_NB_ID); + if (!peer) { + mac_addr = wh->i_addr2; + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_MGMT_NB_ID); + } + + status = wlan_mgmt_txrx_mgmt_frame_tx(peer, wma_handle->mac_context, + (qdf_nbuf_t)tx_frame, NULL, + tx_frm_ota_comp_cb, + WLAN_UMAC_COMP_MLME, + &mgmt_param); + + wlan_objmgr_peer_release_ref(peer, WLAN_MGMT_NB_ID); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: mgmt tx failed", __func__); + qdf_nbuf_free((qdf_nbuf_t)tx_frame); + goto error; + } + + /* + * Failed to send Tx Mgmt Frame + */ + if (status) { + /* Call Download Cb so that umac can free the buffer */ + uint32_t rem; + + if (tx_frm_download_comp_cb) + tx_frm_download_comp_cb(wma_handle->mac_context, + tx_frame, + WMA_TX_FRAME_BUFFER_FREE); + rem = qdf_do_div_rem(wma_handle->tx_fail_cnt, + MAX_PRINT_FAILURE_CNT); + if (!rem) + WMA_LOGE("%s: Failed to send Mgmt Frame", __func__); + else + WMA_LOGD("%s: Failed to send Mgmt Frame", __func__); + wma_handle->tx_fail_cnt++; + goto error; + } + + if (!tx_frm_download_comp_cb) + return QDF_STATUS_SUCCESS; + + /* + * Wait for Download Complete + * if required + */ + if (downld_comp_required) { + /* + * Wait for Download Complete + * @ Integrated : Dxe Complete + * @ Discrete : Target Download Complete + */ + qdf_status = + qdf_wait_for_event_completion(&wma_handle-> + tx_frm_download_comp_event, + WMA_TX_FRAME_COMPLETE_TIMEOUT); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGP("Wait Event failed txfrm_comp_event"); + /* + * @Integrated: Something Wrong with Dxe + * TODO: Some Debug Code + * Here We need to trigger SSR since + * since system went into a bad state where + * we didn't get Download Complete for almost + * WMA_TX_FRAME_COMPLETE_TIMEOUT (1 sec) + */ + /* display scheduler stats */ + return cdp_display_stats(soc, CDP_SCHEDULER_STATS, + QDF_STATS_VERBOSITY_LEVEL_HIGH); + } + } + + return QDF_STATUS_SUCCESS; + +error: + wma_handle->tx_frm_download_comp_cb = NULL; + wma_handle->umac_data_ota_ack_cb = NULL; + return QDF_STATUS_E_FAILURE; +} + +/** + * wma_ds_peek_rx_packet_info() - peek rx packet info + * @pkt: packet + * @pkt_meta: packet meta + * @bSwap: byte swap + * + * Function fills the rx packet meta info from the the cds packet + * + * Return: QDF status + */ +QDF_STATUS wma_ds_peek_rx_packet_info(cds_pkt_t *pkt, void **pkt_meta, + bool bSwap) +{ + /* Sanity Check */ + if (!pkt) { + WMA_LOGE("wma:Invalid parameter sent on wma_peek_rx_pkt_info"); + return QDF_STATUS_E_FAULT; + } + + *pkt_meta = &(pkt->pkt_meta); + + return QDF_STATUS_SUCCESS; +} + +#ifdef HL_RX_AGGREGATION_HOLE_DETECTION +void ol_rx_aggregation_hole(uint32_t hole_info) +{ + struct sir_sme_rx_aggr_hole_ind *rx_aggr_hole_event; + uint32_t alloc_len; + cds_msg_t cds_msg = { 0 }; + QDF_STATUS status; + + alloc_len = sizeof(*rx_aggr_hole_event) + + sizeof(rx_aggr_hole_event->hole_info_array[0]); + rx_aggr_hole_event = qdf_mem_malloc(alloc_len); + if (!rx_aggr_hole_event) + return; + + rx_aggr_hole_event->hole_cnt = 1; + rx_aggr_hole_event->hole_info_array[0] = hole_info; + + cds_msg.type = eWNI_SME_RX_AGGR_HOLE_IND; + cds_msg.bodyptr = rx_aggr_hole_event; + cds_msg.bodyval = 0; + + status = cds_mq_post_message(CDS_MQ_ID_SME, &cds_msg); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(rx_aggr_hole_event); + return; + } +} +#endif + +/** + * ol_rx_err() - ol rx err handler + * @pdev: ol pdev + * @vdev_id: vdev id + * @peer_mac_addr: peer mac address + * @tid: TID + * @tsf32: TSF + * @err_type: error type + * @rx_frame: rx frame + * @pn: PN Number + * @key_id: key id + * + * This function handles rx error and send MIC error failure to LIM + * + * Return: none + */ +/* + * Local prototype added to temporarily address warning caused by + * -Wmissing-prototypes. A more correct solution will come later + * as a solution to IR-196435 at whihc point this prototype will + * be removed. + */ +void ol_rx_err(void *pdev, uint8_t vdev_id, + uint8_t *peer_mac_addr, int tid, uint32_t tsf32, + enum ol_rx_err_type err_type, qdf_nbuf_t rx_frame, + uint64_t *pn, uint8_t key_id); +void ol_rx_err(void *pdev, uint8_t vdev_id, + uint8_t *peer_mac_addr, int tid, uint32_t tsf32, + enum ol_rx_err_type err_type, qdf_nbuf_t rx_frame, + uint64_t *pn, uint8_t key_id) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct mic_failure_ind *mic_err_ind; + qdf_ether_header_t *eth_hdr; + uint8_t *bssid; + struct scheduler_msg cds_msg = {0}; + + if (!wma) + return; + + if (err_type != OL_RX_ERR_TKIP_MIC) + return; + + if (qdf_nbuf_len(rx_frame) < sizeof(*eth_hdr)) + return; + eth_hdr = (qdf_ether_header_t *)qdf_nbuf_data(rx_frame); + mic_err_ind = qdf_mem_malloc(sizeof(*mic_err_ind)); + if (!mic_err_ind) + return; + + mic_err_ind->messageType = eWNI_SME_MIC_FAILURE_IND; + mic_err_ind->length = sizeof(*mic_err_ind); + mic_err_ind->sessionId = vdev_id; + bssid = wma_get_vdev_bssid(wma->interfaces[vdev_id].vdev); + if (!bssid) { + WMA_LOGE("%s: Failed to get bssid for vdev_%d", + __func__, vdev_id); + qdf_mem_free((void *)mic_err_ind); + return; + } + qdf_copy_macaddr(&mic_err_ind->bssId, + (struct qdf_mac_addr *)bssid); + qdf_mem_copy(mic_err_ind->info.taMacAddr, + (struct qdf_mac_addr *) peer_mac_addr, + sizeof(tSirMacAddr)); + qdf_mem_copy(mic_err_ind->info.srcMacAddr, + (struct qdf_mac_addr *) eth_hdr->ether_shost, + sizeof(tSirMacAddr)); + qdf_mem_copy(mic_err_ind->info.dstMacAddr, + (struct qdf_mac_addr *) eth_hdr->ether_dhost, + sizeof(tSirMacAddr)); + mic_err_ind->info.keyId = key_id; + mic_err_ind->info.multicast = + IEEE80211_IS_MULTICAST(eth_hdr->ether_dhost); + qdf_mem_copy(mic_err_ind->info.TSC, pn, SIR_CIPHER_SEQ_CTR_SIZE); + + qdf_mem_zero(&cds_msg, sizeof(struct scheduler_msg)); + cds_msg.type = eWNI_SME_MIC_FAILURE_IND; + cds_msg.bodyptr = (void *) mic_err_ind; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_TXRX, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, + &cds_msg)) { + WMA_LOGE("%s: could not post mic failure indication to SME", + __func__); + qdf_mem_free((void *)mic_err_ind); + } +} + +void wma_tx_abort(uint8_t vdev_id) +{ +#define PEER_ALL_TID_BITMASK 0xffffffff + tp_wma_handle wma; + uint32_t peer_tid_bitmap = PEER_ALL_TID_BITMASK; + struct wma_txrx_node *iface; + uint8_t *bssid; + struct peer_flush_params param = {0}; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) + return; + + iface = &wma->interfaces[vdev_id]; + if (!iface->vdev) { + WMA_LOGE("%s: iface->vdev is NULL", __func__); + return; + } + + bssid = wma_get_vdev_bssid(iface->vdev); + if (!bssid) { + WMA_LOGE("%s: Failed to get bssid for vdev_%d", + __func__, vdev_id); + return; + } + + WMA_LOGD("%s: vdevid %d bssid "QDF_MAC_ADDR_FMT, __func__, vdev_id, + QDF_MAC_ADDR_REF(bssid)); + wma_vdev_set_pause_bit(vdev_id, PAUSE_TYPE_HOST); + cdp_fc_vdev_pause(cds_get_context(QDF_MODULE_ID_SOC), vdev_id, + OL_TXQ_PAUSE_REASON_TX_ABORT, 0); + + /* Flush all TIDs except MGMT TID for this peer in Target */ + peer_tid_bitmap &= ~(0x1 << WMI_MGMT_TID); + param.peer_tid_bitmap = peer_tid_bitmap; + param.vdev_id = vdev_id; + wmi_unified_peer_flush_tids_send(wma->wmi_handle, bssid, + ¶m); +} + +/** + * wma_lro_config_cmd() - process the LRO config command + * @wma: Pointer to WMA handle + * @wma_lro_cmd: Pointer to LRO configuration parameters + * + * This function sends down the LRO configuration parameters to + * the firmware to enable LRO, sets the TCP flags and sets the + * seed values for the toeplitz hash generation + * + * Return: QDF_STATUS_SUCCESS for success otherwise failure + */ +QDF_STATUS wma_lro_config_cmd(void *handle, + struct cdp_lro_hash_config *wma_lro_cmd) +{ + struct wmi_lro_config_cmd_t wmi_lro_cmd = {0}; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma || !wma_lro_cmd) { + wma_err("Invalid input!"); + return QDF_STATUS_E_FAILURE; + } + + wmi_lro_cmd.lro_enable = wma_lro_cmd->lro_enable; + wmi_lro_cmd.tcp_flag = wma_lro_cmd->tcp_flag; + wmi_lro_cmd.tcp_flag_mask = wma_lro_cmd->tcp_flag_mask; + qdf_mem_copy(wmi_lro_cmd.toeplitz_hash_ipv4, + wma_lro_cmd->toeplitz_hash_ipv4, + LRO_IPV4_SEED_ARR_SZ * sizeof(uint32_t)); + qdf_mem_copy(wmi_lro_cmd.toeplitz_hash_ipv6, + wma_lro_cmd->toeplitz_hash_ipv6, + LRO_IPV6_SEED_ARR_SZ * sizeof(uint32_t)); + + return wmi_unified_lro_config_cmd(wma->wmi_handle, + &wmi_lro_cmd); +} + +void wma_delete_invalid_peer_entries(uint8_t vdev_id, uint8_t *peer_mac_addr) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + uint8_t i; + struct wma_txrx_node *iface; + + if (!wma) { + wma_err("wma handle is NULL"); + return; + } + + iface = &wma->interfaces[vdev_id]; + + if (peer_mac_addr) { + for (i = 0; i < INVALID_PEER_MAX_NUM; i++) { + if (qdf_mem_cmp + (iface->invalid_peers[i].rx_macaddr, + peer_mac_addr, + QDF_MAC_ADDR_SIZE) == 0) { + qdf_mem_zero(iface->invalid_peers[i].rx_macaddr, + sizeof(QDF_MAC_ADDR_SIZE)); + break; + } + } + if (i == INVALID_PEER_MAX_NUM) + wma_debug("peer_mac_addr "QDF_MAC_ADDR_FMT" is not found", + QDF_MAC_ADDR_REF(peer_mac_addr)); + } else { + qdf_mem_zero(iface->invalid_peers, + sizeof(iface->invalid_peers)); + } +} + +uint8_t wma_rx_invalid_peer_ind(uint8_t vdev_id, void *wh) +{ + struct ol_rx_inv_peer_params *rx_inv_msg; + struct ieee80211_frame *wh_l = (struct ieee80211_frame *)wh; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + uint8_t i, index; + bool invalid_peer_found = false; + struct wma_txrx_node *iface; + + if (!wma) { + wma_err("wma handle is NULL"); + return -EINVAL; + } + + iface = &wma->interfaces[vdev_id]; + rx_inv_msg = qdf_mem_malloc(sizeof(struct ol_rx_inv_peer_params)); + if (!rx_inv_msg) + return -ENOMEM; + + index = iface->invalid_peer_idx; + rx_inv_msg->vdev_id = vdev_id; + qdf_mem_copy(rx_inv_msg->ra, wh_l->i_addr1, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(rx_inv_msg->ta, wh_l->i_addr2, QDF_MAC_ADDR_SIZE); + + + for (i = 0; i < INVALID_PEER_MAX_NUM; i++) { + if (qdf_mem_cmp + (iface->invalid_peers[i].rx_macaddr, + rx_inv_msg->ta, + QDF_MAC_ADDR_SIZE) == 0) { + invalid_peer_found = true; + break; + } + } + + if (!invalid_peer_found) { + qdf_mem_copy(iface->invalid_peers[index].rx_macaddr, + rx_inv_msg->ta, + QDF_MAC_ADDR_SIZE); + + /* reset count if reached max */ + iface->invalid_peer_idx = + (index + 1) % INVALID_PEER_MAX_NUM; + + /* send deauth */ + WMA_LOGD("%s: vdev_id %d", __func__, vdev_id); + wma_debug(" RA: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(rx_inv_msg->ra)); + wma_debug(" TA: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(rx_inv_msg->ta)); + + wma_send_msg(wma, + SIR_LIM_RX_INVALID_PEER, + (void *)rx_inv_msg, 0); + } else { + wma_debug_rl("Ignore invalid peer indication as received more than once " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(rx_inv_msg->ta)); + qdf_mem_free(rx_inv_msg); + } + + return 0; +} + +int wma_dp_send_delba_ind(uint8_t vdev_id, uint8_t *peer_macaddr, + uint8_t tid, uint8_t reason_code) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct lim_delba_req_info *req; + + if (!wma || !peer_macaddr) { + wma_err("wma handle or mac addr is NULL"); + return -EINVAL; + } + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return -ENOMEM; + req->vdev_id = vdev_id; + qdf_mem_copy(req->peer_macaddr, peer_macaddr, QDF_MAC_ADDR_SIZE); + req->tid = tid; + req->reason_code = reason_code; + WMA_LOGD("req delba_ind vdev %d "QDF_MAC_ADDR_FMT" tid %d reason %d", + vdev_id, QDF_MAC_ADDR_REF(peer_macaddr), tid, reason_code); + wma_send_msg_high_priority(wma, SIR_HAL_REQ_SEND_DELBA_REQ_IND, + (void *)req, 0); + + return 0; +} + +bool wma_is_roam_in_progress(uint32_t vdev_id) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) + return false; + + return wma->interfaces[vdev_id].roaming_in_progress; +} diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_dev_if.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_dev_if.c new file mode 100644 index 0000000000000000000000000000000000000000..0e9a8cfd8c9332cd96952f3e63f90255dcb5a316 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_dev_if.c @@ -0,0 +1,5323 @@ +/* + * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_dev_if.c + * This file contains vdev & peer related operations. + */ + +/* Header files */ + +#include "wma.h" +#include "wma_api.h" +#include "cds_api.h" +#include "wmi_unified_api.h" +#include "wlan_qct_sys.h" +#include "wni_api.h" +#include "ani_global.h" +#include "wmi_unified.h" +#include "wni_cfg.h" + +#include "qdf_nbuf.h" +#include "qdf_types.h" +#include "qdf_mem.h" + +#include "wma_types.h" +#include "lim_api.h" +#include "lim_session_utils.h" + +#include "cds_utils.h" + +#if !defined(REMOVE_PKT_LOG) +#include "pktlog_ac.h" +#endif /* REMOVE_PKT_LOG */ + +#include "dbglog_host.h" +#include "csr_api.h" + +#include "wma_internal.h" + +#include "wma_ocb.h" +#include "cdp_txrx_cfg.h" +#include "cdp_txrx_flow_ctrl_legacy.h" +#include +#include +#include +#include +#include +#ifdef WLAN_FEATURE_PKT_CAPTURE +#include +#endif /* WLAN_FEATURE_PKT_CAPTURE */ + +#include "wlan_policy_mgr_api.h" +#include "wma_nan_datapath.h" +#if defined(CONFIG_HL_SUPPORT) +#include "wlan_tgt_def_config_hl.h" +#else +#include "wlan_tgt_def_config.h" +#endif +#include +#include +#include "wlan_pmo_ucfg_api.h" +#include "wlan_reg_services_api.h" +#include +#include "wma_he.h" +#include "wlan_roam_debug.h" +#include "wlan_ocb_ucfg_api.h" +#include "init_deinit_lmac.h" +#include +#include "wlan_policy_mgr_ucfg.h" +#include "wlan_mlme_public_struct.h" +#include "wlan_mlme_api.h" +#include "wlan_mlme_main.h" +#include "wlan_mlme_ucfg_api.h" +#include +#include "../../core/src/vdev_mgr_ops.h" +#include "wlan_utility.h" +#include +#include "wmi_unified_vdev_api.h" + +QDF_STATUS wma_find_vdev_id_by_addr(tp_wma_handle wma, uint8_t *addr, + uint8_t *vdev_id) +{ + uint8_t i; + struct wlan_objmgr_vdev *vdev; + + for (i = 0; i < wma->max_bssid; i++) { + vdev = wma->interfaces[i].vdev; + if (!vdev) + continue; + + if (qdf_is_macaddr_equal( + (struct qdf_mac_addr *)wlan_vdev_mlme_get_macaddr(vdev), + (struct qdf_mac_addr *)addr) == true) { + *vdev_id = i; + return QDF_STATUS_SUCCESS; + } + } + return QDF_STATUS_E_FAILURE; +} + + +/** + * wma_is_vdev_in_ap_mode() - check that vdev is in ap mode or not + * @wma: wma handle + * @vdev_id: vdev id + * + * Helper function to know whether given vdev id + * is in AP mode or not. + * + * Return: True/False + */ +bool wma_is_vdev_in_ap_mode(tp_wma_handle wma, uint8_t vdev_id) +{ + struct wma_txrx_node *intf = wma->interfaces; + + if (vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: Invalid vdev_id %hu", __func__, vdev_id); + QDF_ASSERT(0); + return false; + } + + if ((intf[vdev_id].type == WMI_VDEV_TYPE_AP) && + ((intf[vdev_id].sub_type == WMI_UNIFIED_VDEV_SUBTYPE_P2P_GO) || + (intf[vdev_id].sub_type == 0))) + return true; + + return false; +} + +#ifdef QCA_IBSS_SUPPORT +bool wma_is_vdev_in_ibss_mode(tp_wma_handle wma, uint8_t vdev_id) +{ + struct wma_txrx_node *intf = wma->interfaces; + + if (vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: Invalid vdev_id %hu", __func__, vdev_id); + QDF_ASSERT(0); + return false; + } + + if (intf[vdev_id].type == WMI_VDEV_TYPE_IBSS) + return true; + + return false; +} + +/** + * wma_send_peer_atim_window_len() - send peer atim window length + * @wma: wma handle + * @add_sta: add sta parameters + * + * This API sends the peer Atim Window length if IBSS + * power save is enabled by the firmware. + * + * Return: none + */ +static void +wma_send_peer_atim_window_len(tp_wma_handle wma, tpAddStaParams add_sta) +{ + if (wma_is_vdev_in_ibss_mode(wma, add_sta->smesessionId) && + wmi_service_enabled(wma->wmi_handle, + wmi_service_ibss_pwrsave)) { + /* + * If ATIM Window is present in the peer + * beacon then send it to firmware else + * configure Zero ATIM Window length to + * firmware. + */ + if (add_sta->atimIePresent) { + wma_set_peer_param(wma, add_sta->staMac, + WMI_PEER_IBSS_ATIM_WINDOW_LENGTH, + add_sta->peerAtimWindowLength, + add_sta->smesessionId); + } else { + wma_set_peer_param(wma, add_sta->staMac, + WMI_PEER_IBSS_ATIM_WINDOW_LENGTH, + 0, add_sta->smesessionId); + } + } +} +#else +static inline void +wma_send_peer_atim_window_len(tp_wma_handle wma, tpAddStaParams add_sta) +{ +} +#endif /* QCA_IBSS_SUPPORT */ + +uint8_t *wma_get_vdev_bssid(struct wlan_objmgr_vdev *vdev) +{ + struct vdev_mlme_obj *mlme_obj; + + if (!vdev) { + WMA_LOGE("%s vdev is NULL", __func__); + return NULL; + } + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + WMA_LOGE("%s Failed to get mlme_obj", __func__); + return NULL; + } + + return mlme_obj->mgmt.generic.bssid; +} + +QDF_STATUS wma_find_vdev_id_by_bssid(tp_wma_handle wma, uint8_t *bssid, + uint8_t *vdev_id) +{ + int i; + uint8_t *bssid_addr; + + for (i = 0; i < wma->max_bssid; i++) { + if (!wma->interfaces[i].vdev) + continue; + bssid_addr = wma_get_vdev_bssid(wma->interfaces[i].vdev); + if (!bssid_addr) + continue; + + if (qdf_is_macaddr_equal( + (struct qdf_mac_addr *)bssid_addr, + (struct qdf_mac_addr *)bssid) == true) { + *vdev_id = i; + return QDF_STATUS_SUCCESS; + } + } + + return QDF_STATUS_E_FAILURE; +} + +/** + * wma_find_req_on_timer_expiry() - find request by address + * @wma: wma handle + * @req: pointer to the target request + * + * On timer expiry, the pointer to the req message is received from the + * timer callback. Lookup the wma_hold_req_queue for the request with the + * same address and return success if found. + * + * Return: QDF_STATUS + */ +static QDF_STATUS wma_find_req_on_timer_expiry(tp_wma_handle wma, + struct wma_target_req *req) +{ + struct wma_target_req *req_msg = NULL; + bool found = false; + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + QDF_STATUS status; + + qdf_spin_lock_bh(&wma->wma_hold_req_q_lock); + if (QDF_STATUS_SUCCESS != qdf_list_peek_front(&wma->wma_hold_req_queue, + &next_node)) { + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + WMA_LOGE(FL("unable to get msg node from request queue")); + return QDF_STATUS_E_FAILURE; + } + + do { + cur_node = next_node; + req_msg = qdf_container_of(cur_node, + struct wma_target_req, node); + if (req_msg != req) + continue; + + found = true; + status = qdf_list_remove_node(&wma->wma_hold_req_queue, + cur_node); + if (QDF_STATUS_SUCCESS != status) { + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + WMA_LOGD(FL("Failed to remove request for req %pK"), + req); + return QDF_STATUS_E_FAILURE; + } + break; + } while (QDF_STATUS_SUCCESS == + qdf_list_peek_next(&wma->wma_hold_req_queue, + cur_node, &next_node)); + + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + if (!found) { + WMA_LOGE(FL("target request not found for req %pK"), + req); + return QDF_STATUS_E_INVAL; + } + + WMA_LOGD(FL("target request found for vdev id: %d type %d"), + req_msg->vdev_id, req_msg->type); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_find_req() - find target request for vdev id + * @wma: wma handle + * @vdev_id: vdev id + * @type: request type + * + * Find target request for given vdev id & type of request. + * Remove that request from active list. + * + * Return: return target request if found or NULL. + */ +static struct wma_target_req *wma_find_req(tp_wma_handle wma, + uint8_t vdev_id, uint8_t type) +{ + struct wma_target_req *req_msg = NULL; + bool found = false; + qdf_list_node_t *node1 = NULL, *node2 = NULL; + QDF_STATUS status; + + qdf_spin_lock_bh(&wma->wma_hold_req_q_lock); + if (QDF_STATUS_SUCCESS != qdf_list_peek_front(&wma->wma_hold_req_queue, + &node2)) { + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + WMA_LOGE(FL("unable to get msg node from request queue")); + return NULL; + } + + do { + node1 = node2; + req_msg = qdf_container_of(node1, struct wma_target_req, node); + if (req_msg->vdev_id != vdev_id) + continue; + if (req_msg->type != type) + continue; + + found = true; + status = qdf_list_remove_node(&wma->wma_hold_req_queue, node1); + if (QDF_STATUS_SUCCESS != status) { + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + WMA_LOGD(FL("Failed to remove request for vdev_id %d type %d"), + vdev_id, type); + return NULL; + } + break; + } while (QDF_STATUS_SUCCESS == + qdf_list_peek_next(&wma->wma_hold_req_queue, node1, + &node2)); + + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + if (!found) { + WMA_LOGE(FL("target request not found for vdev_id %d type %d"), + vdev_id, type); + return NULL; + } + + WMA_LOGD(FL("target request found for vdev id: %d type %d"), + vdev_id, type); + + return req_msg; +} + +/** + * wma_find_remove_req_msgtype() - find and remove request for vdev id + * @wma: wma handle + * @vdev_id: vdev id + * @msg_type: message request type + * + * Find target request for given vdev id & sub type of request. + * Remove the same from active list. + * + * Return: Success if request found, failure other wise + */ +static struct wma_target_req *wma_find_remove_req_msgtype(tp_wma_handle wma, + uint8_t vdev_id, uint32_t msg_type) +{ + struct wma_target_req *req_msg = NULL; + bool found = false; + qdf_list_node_t *node1 = NULL, *node2 = NULL; + QDF_STATUS status; + + qdf_spin_lock_bh(&wma->wma_hold_req_q_lock); + if (QDF_STATUS_SUCCESS != qdf_list_peek_front(&wma->wma_hold_req_queue, + &node2)) { + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + WMA_LOGE(FL("unable to get msg node from request queue")); + return NULL; + } + + do { + node1 = node2; + req_msg = qdf_container_of(node1, struct wma_target_req, node); + if (req_msg->vdev_id != vdev_id) + continue; + if (req_msg->msg_type != msg_type) + continue; + + found = true; + status = qdf_list_remove_node(&wma->wma_hold_req_queue, node1); + if (QDF_STATUS_SUCCESS != status) { + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + WMA_LOGD(FL("Failed to remove request. vdev_id %d type %d"), + vdev_id, msg_type); + return NULL; + } + break; + } while (QDF_STATUS_SUCCESS == + qdf_list_peek_next(&wma->wma_hold_req_queue, node1, + &node2)); + + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + if (!found) { + WMA_LOGE(FL("target request not found for vdev_id %d type %d"), + vdev_id, msg_type); + return NULL; + } + + WMA_LOGD(FL("target request found for vdev id: %d type %d"), + vdev_id, msg_type); + + return req_msg; +} + +/** + * wma_send_vdev_del_resp() - send vdev del resp to Upper layer + * @param: params of del vdev response + * + * Return: none + */ +static inline void wma_send_vdev_del_resp(struct del_vdev_params *param) +{ + struct scheduler_msg sme_msg = {0}; + QDF_STATUS status; + + sme_msg.type = eWNI_SME_VDEV_DELETE_RSP; + sme_msg.bodyptr = param; + + status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + qdf_mem_free(param); + wma_err("Fail to send vdev del resp"); + } +} + +QDF_STATUS wma_vdev_detach_callback(struct vdev_delete_response *rsp) +{ + tp_wma_handle wma; + struct wma_txrx_node *iface = NULL; + struct del_vdev_params *param; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + wma_err("wma handle is NULL for VDEV_%d", rsp->vdev_id); + return QDF_STATUS_E_FAILURE; + } + + /* Sanitize the vdev id*/ + if (rsp->vdev_id > wma->max_bssid) { + wma_err("vdev delete response with invalid vdev_id :%d", + rsp->vdev_id); + QDF_BUG(0); + return QDF_STATUS_E_FAILURE; + } + + iface = &wma->interfaces[rsp->vdev_id]; + + if (!iface->del_staself_req) { + wma_err(" iface handle is NULL for VDEV_%d", rsp->vdev_id); + return QDF_STATUS_E_FAILURE; + } + + wma_debug("vdev del response received for VDEV_%d", rsp->vdev_id); + param = (struct del_vdev_params *)iface->del_staself_req; + iface->del_staself_req = NULL; + + if (iface->roam_scan_stats_req) { + struct sir_roam_scan_stats *roam_scan_stats_req = + iface->roam_scan_stats_req; + + iface->roam_scan_stats_req = NULL; + qdf_mem_free(roam_scan_stats_req); + } + + wma_vdev_deinit(iface); + qdf_mem_zero(iface, sizeof(*iface)); + wma_vdev_init(iface); + + param->status = QDF_STATUS_SUCCESS; + wma_send_vdev_del_resp(param); + + return QDF_STATUS_SUCCESS; +} + +static void +wma_cdp_vdev_detach(ol_txrx_soc_handle soc, tp_wma_handle wma_handle, + uint8_t vdev_id) +{ + struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; + struct wlan_objmgr_vdev *vdev = iface->vdev; + + if (!vdev) { + WMA_LOGE(FL("vdev is NULL")); + return; + } + + if (soc && wlan_vdev_get_id(vdev) != WLAN_INVALID_VDEV_ID) + cdp_vdev_detach(soc, vdev_id, NULL, NULL); +} + +/** + * wma_release_vdev_ref() - Release vdev object reference count + * @iface: wma interface txrx node + * + * Purpose of this function is to release vdev object reference count + * from wma interface txrx node. + * + * Return: None + */ +static void +wma_release_vdev_ref(struct wma_txrx_node *iface) +{ + struct wlan_objmgr_vdev *vdev; + + vdev = iface->vdev; + + iface->vdev_active = false; + iface->vdev = NULL; + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); +} + +/** + * wma_handle_monitor_mode_vdev_detach() - Stop and down monitor mode vdev + * @wma_handle: wma handle + * @vdev_id: used to get wma interface txrx node + * + * Monitor mode is unconneted mode, so do explicit vdev stop and down + * + * Return: None + */ +static void wma_handle_monitor_mode_vdev_detach(tp_wma_handle wma, + uint8_t vdev_id) +{ + struct wma_txrx_node *iface; + + iface = &wma->interfaces[vdev_id]; + wlan_vdev_mlme_sm_deliver_evt(iface->vdev, + WLAN_VDEV_SM_EV_DOWN, + 0, NULL); + iface->vdev_active = false; +} + +/** + * wma_handle_vdev_detach() - wma vdev detach handler + * @wma_handle: pointer to wma handle + * @del_vdev_req_param: pointer to del req param + * + * Return: none. + */ +static QDF_STATUS wma_handle_vdev_detach(tp_wma_handle wma_handle, + struct del_vdev_params *del_vdev_req_param) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint8_t vdev_id = del_vdev_req_param->vdev_id; + struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct vdev_mlme_obj *vdev_mlme; + + if (!soc) { + WMA_LOGE("%s:SOC context is NULL", __func__); + goto rel_ref; + } + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (!vdev_mlme) { + wma_err("Failed to get vdev mlme obj for vdev id %d", + del_vdev_req_param->vdev_id); + goto rel_ref; + } + + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + wma_handle_monitor_mode_vdev_detach(wma_handle, vdev_id); + + iface->del_staself_req = del_vdev_req_param; + wma_cdp_vdev_detach(soc, wma_handle, vdev_id); + wma_release_vdev_ref(iface); + + status = vdev_mgr_delete_send(vdev_mlme); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Unable to remove an interface"); + goto out; + } + + return status; + +rel_ref: + wma_cdp_vdev_detach(soc, wma_handle, vdev_id); + wma_release_vdev_ref(iface); +out: + wma_vdev_deinit(iface); + qdf_mem_zero(iface, sizeof(*iface)); + wma_vdev_init(iface); + return status; +} + +/** + * wma_self_peer_remove() - Self peer remove handler + * @wma: wma handle + * @del_vdev_req_param: vdev id + * @generate_vdev_rsp: request type + * + * Return: success if peer delete command sent to firmware, else failure. + */ +static QDF_STATUS wma_self_peer_remove(tp_wma_handle wma_handle, + struct del_vdev_params *del_vdev_req) +{ + QDF_STATUS qdf_status; + uint8_t vdev_id = del_vdev_req->vdev_id; + struct wma_target_req *msg = NULL; + struct del_sta_self_rsp_params *sta_self_wmi_rsp; + + wma_debug("P2P Device: removing self peer "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(del_vdev_req->self_mac_addr)); + + qdf_status = wma_remove_peer(wma_handle, del_vdev_req->self_mac_addr, + vdev_id, false); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + wma_err("wma_remove_peer is failed"); + goto error; + } + + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_sync_delete_cmds)) { + sta_self_wmi_rsp = + qdf_mem_malloc(sizeof(struct del_sta_self_rsp_params)); + if (!sta_self_wmi_rsp) { + qdf_status = QDF_STATUS_E_NOMEM; + goto error; + } + sta_self_wmi_rsp->self_sta_param = del_vdev_req; + msg = wma_fill_hold_req(wma_handle, vdev_id, + WMA_DELETE_STA_REQ, + WMA_DEL_P2P_SELF_STA_RSP_START, + sta_self_wmi_rsp, + WMA_DELETE_STA_TIMEOUT); + if (!msg) { + wma_err("Failed to allocate request for vdev_id %d", + vdev_id); + wma_remove_req(wma_handle, vdev_id, + WMA_DEL_P2P_SELF_STA_RSP_START); + qdf_mem_free(sta_self_wmi_rsp); + qdf_status = QDF_STATUS_E_FAILURE; + goto error; + } + } + +error: + return qdf_status; +} + +static bool wma_vdev_uses_self_peer(uint32_t vdev_type, uint32_t vdev_subtype) +{ + switch (vdev_type) { + case WMI_VDEV_TYPE_AP: + return vdev_subtype == WMI_UNIFIED_VDEV_SUBTYPE_P2P_DEVICE; + + case WMI_VDEV_TYPE_MONITOR: + case WMI_VDEV_TYPE_OCB: + return true; + + default: + return false; + } +} + +/** + * wma_remove_objmgr_peer() - remove objmgr peer information from host driver + * @wma: wma handle + * @vdev_id: vdev id + * @peer_addr: peer mac address + * + * Return: none + */ +static void wma_remove_objmgr_peer(tp_wma_handle wma, uint8_t vdev_id, + uint8_t *peer_addr) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_peer *obj_peer; + struct wlan_objmgr_vdev *obj_vdev; + struct wlan_objmgr_pdev *obj_pdev; + uint8_t pdev_id = 0; + + psoc = wma->psoc; + if (!psoc) { + WMA_LOGE("%s:PSOC is NULL", __func__); + return; + } + + obj_vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_LEGACY_WMA_ID); + if (!obj_vdev) { + WMA_LOGE("Obj vdev not found. Unable to remove peer"); + return; + } + obj_pdev = wlan_vdev_get_pdev(obj_vdev); + pdev_id = wlan_objmgr_pdev_get_pdev_id(obj_pdev); + obj_peer = wlan_objmgr_get_peer(psoc, pdev_id, peer_addr, + WLAN_LEGACY_WMA_ID); + if (obj_peer) { + wlan_objmgr_peer_obj_delete(obj_peer); + /* Unref to decrement ref happened in find_peer */ + wlan_objmgr_peer_release_ref(obj_peer, WLAN_LEGACY_WMA_ID); + } else { + WMA_LOGE("Peer "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(peer_addr)); + } + + wlan_objmgr_vdev_release_ref(obj_vdev, WLAN_LEGACY_WMA_ID); +} + +static QDF_STATUS wma_check_for_deffered_peer_delete(tp_wma_handle wma_handle, + struct del_vdev_params + *pdel_vdev_req_param) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t vdev_id = pdel_vdev_req_param->vdev_id; + struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; + uint32_t vdev_stop_type; + + if (qdf_atomic_read(&iface->bss_status) == WMA_BSS_STATUS_STARTED) { + status = mlme_get_vdev_stop_type(iface->vdev, &vdev_stop_type); + if (QDF_IS_STATUS_ERROR(status)) { + wma_err("Failed to get wma req msg_type for vdev_id: %d", + vdev_id); + status = QDF_STATUS_E_INVAL; + return status; + } + + if (vdev_stop_type != WMA_DELETE_BSS_REQ) { + status = QDF_STATUS_E_INVAL; + return status; + } + + wma_debug("BSS is not yet stopped. Defering vdev(vdev id %x) deletion", + vdev_id); + iface->del_staself_req = pdel_vdev_req_param; + iface->is_del_sta_defered = true; + } + + return status; +} + +static QDF_STATUS wma_vdev_self_peer_delete(tp_wma_handle wma_handle, + struct del_vdev_params + *pdel_vdev_req_param) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t vdev_id = pdel_vdev_req_param->vdev_id; + struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; + + if (wma_vdev_uses_self_peer(iface->type, iface->sub_type)) { + status = wma_self_peer_remove(wma_handle, pdel_vdev_req_param); + if (QDF_IS_STATUS_ERROR(status)) { + wma_err("can't remove selfpeer, send rsp session: %d", + vdev_id); + status = wma_handle_vdev_detach(wma_handle, + pdel_vdev_req_param); + if (QDF_IS_STATUS_ERROR(status)) { + wma_err("Trigger recovery for vdev %d", + vdev_id); + cds_trigger_recovery(QDF_REASON_UNSPECIFIED); + } + return status; + } + } else if (iface->type == WMI_VDEV_TYPE_STA) { + wma_remove_objmgr_peer(wma_handle, vdev_id, + pdel_vdev_req_param->self_mac_addr); + } + + return status; +} + +QDF_STATUS wma_vdev_detach(tp_wma_handle wma_handle, + struct del_vdev_params *pdel_vdev_req_param) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t vdev_id = pdel_vdev_req_param->vdev_id; + struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; + + if (!iface->vdev) { + WMA_LOGE("vdev %d is NULL", vdev_id); + goto send_rsp; + } + + status = wma_check_for_deffered_peer_delete(wma_handle, + pdel_vdev_req_param); + if (QDF_IS_STATUS_ERROR(status)) + goto send_fail_rsp; + + if (iface->is_del_sta_defered) + return status; + + iface->is_del_sta_defered = false; + + status = wma_vdev_self_peer_delete(wma_handle, pdel_vdev_req_param); + + if (QDF_IS_STATUS_ERROR(status)) { + wma_err("Failed to send self peer delete:%d", status); + goto send_rsp; + } + + if (iface->type != WMI_VDEV_TYPE_MONITOR) + iface->vdev_active = false; + + if (!wma_vdev_uses_self_peer(iface->type, iface->sub_type) || + !wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_sync_delete_cmds)) { + status = wma_handle_vdev_detach(wma_handle, + pdel_vdev_req_param); + } + + if (QDF_IS_STATUS_ERROR(status)) + goto send_rsp; + + return status; + +send_fail_rsp: + WMA_LOGE("rcvd del_self_sta without del_bss; vdev_id:%d", vdev_id); + cds_trigger_recovery(QDF_REASON_UNSPECIFIED); + status = QDF_STATUS_E_FAILURE; + +send_rsp: + pdel_vdev_req_param->status = status; + wma_send_vdev_del_resp(pdel_vdev_req_param); + + return status; +} + +/** + * wma_send_start_resp() - send vdev start response to upper layer + * @wma: wma handle + * @add_bss: add bss params + * @resp_event: response params + * + * Return: none + */ +static void wma_send_start_resp(tp_wma_handle wma, + struct add_bss_rsp *add_bss_rsp, + struct vdev_start_response *rsp) +{ + struct wma_txrx_node *iface = &wma->interfaces[rsp->vdev_id]; + QDF_STATUS status; + + if (QDF_IS_STATUS_SUCCESS(rsp->status) && + QDF_IS_STATUS_SUCCESS(add_bss_rsp->status)) { + status = + wlan_vdev_mlme_sm_deliver_evt(iface->vdev, + WLAN_VDEV_SM_EV_START_RESP, + sizeof(*add_bss_rsp), + add_bss_rsp); + if (QDF_IS_STATUS_SUCCESS(status)) + return; + + add_bss_rsp->status = status; + } + + /* Send vdev stop if vdev start was success */ + if (QDF_IS_STATUS_ERROR(add_bss_rsp->status) && + QDF_IS_STATUS_SUCCESS(rsp->status)) { + wlan_vdev_mlme_sm_deliver_evt(iface->vdev, + WLAN_VDEV_SM_EV_DOWN, + sizeof(*add_bss_rsp), + add_bss_rsp); + return; + } + + wma_remove_bss_peer_on_vdev_start_failure(wma, rsp->vdev_id); + + WMA_LOGD(FL("Sending add bss rsp to umac(vdev %d status %d)"), + rsp->vdev_id, add_bss_rsp->status); + lim_handle_add_bss_rsp(wma->mac_context, add_bss_rsp); +} + +/** + * wma_vdev_start_rsp() - send vdev start response to upper layer + * @wma: wma handle + * @vdev: vdev + * @resp_event: response params + * + * Return: none + */ +static void wma_vdev_start_rsp(tp_wma_handle wma, struct wlan_objmgr_vdev *vdev, + struct vdev_start_response *rsp) +{ + struct beacon_info *bcn; + enum QDF_OPMODE opmode; + struct add_bss_rsp *add_bss_rsp; + + opmode = wlan_vdev_mlme_get_opmode(vdev); + + add_bss_rsp = qdf_mem_malloc(sizeof(*add_bss_rsp)); + if (!add_bss_rsp) + return; + + add_bss_rsp->vdev_id = rsp->vdev_id; + add_bss_rsp->status = rsp->status; + add_bss_rsp->chain_mask = rsp->chain_mask; + add_bss_rsp->smps_mode = host_map_smps_mode(rsp->smps_mode); + +#ifdef QCA_IBSS_SUPPORT + WMA_LOGD("%s: vdev start response received for %s mode", __func__, + opmode == QDF_IBSS_MODE ? "IBSS" : "non-IBSS"); +#endif /* QCA_IBSS_SUPPORT */ + + if (rsp->status) + goto send_fail_resp; + + if ((opmode == QDF_P2P_GO_MODE) || + (opmode == QDF_SAP_MODE) +#ifdef QCA_IBSS_SUPPORT + || (opmode == QDF_IBSS_MODE) +#endif /* QCA_IBSS_SUPPORT */ + ) { + wma->interfaces[rsp->vdev_id].beacon = + qdf_mem_malloc(sizeof(struct beacon_info)); + + bcn = wma->interfaces[rsp->vdev_id].beacon; + if (!bcn) { + add_bss_rsp->status = QDF_STATUS_E_NOMEM; + goto send_fail_resp; + } + bcn->buf = qdf_nbuf_alloc(NULL, SIR_MAX_BEACON_SIZE, 0, + sizeof(uint32_t), 0); + if (!bcn->buf) { + qdf_mem_free(bcn); + add_bss_rsp->status = QDF_STATUS_E_FAILURE; + goto send_fail_resp; + } + bcn->seq_no = MIN_SW_SEQ; + qdf_spinlock_create(&bcn->lock); + qdf_atomic_set(&wma->interfaces[rsp->vdev_id].bss_status, + WMA_BSS_STATUS_STARTED); + WMA_LOGD("%s: AP mode (type %d subtype %d) BSS is started", + __func__, wma->interfaces[rsp->vdev_id].type, + wma->interfaces[rsp->vdev_id].sub_type); + } + +send_fail_resp: + wma_send_start_resp(wma, add_bss_rsp, rsp); +} + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +/** + * wma_find_mcc_ap() - finds if device is operating AP in MCC mode or not + * @wma: wma handle. + * @vdev_id: vdev ID of device for which MCC has to be checked + * @add: flag indicating if current device is added or deleted + * + * This function parses through all the interfaces in wma and finds if + * any of those devces are in MCC mode with AP. If such a vdev is found + * involved AP vdevs are sent WDA_UPDATE_Q2Q_IE_IND msg to update their + * beacon template to include Q2Q IE. + * + * Return: none + */ +static void wma_find_mcc_ap(tp_wma_handle wma, uint8_t vdev_id, bool add) +{ + uint8_t i; + uint16_t prev_ch_freq = 0; + bool is_ap = false; + bool result = false; + uint8_t *ap_vdev_ids = NULL; + uint8_t num_ch = 0; + + ap_vdev_ids = qdf_mem_malloc(wma->max_bssid); + if (!ap_vdev_ids) + return; + + for (i = 0; i < wma->max_bssid; i++) { + ap_vdev_ids[i] = -1; + if (add == false && i == vdev_id) + continue; + + if (wma_is_vdev_up(vdev_id) || (i == vdev_id && add)) { + if (wma->interfaces[i].type == WMI_VDEV_TYPE_AP) { + is_ap = true; + ap_vdev_ids[i] = i; + } + + if (wma->interfaces[i].mhz != prev_ch_freq) { + num_ch++; + prev_ch_freq = wma->interfaces[i].mhz; + } + } + } + + if (is_ap && (num_ch > 1)) + result = true; + else + result = false; + + wma_send_msg(wma, WMA_UPDATE_Q2Q_IE_IND, (void *)ap_vdev_ids, result); +} +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +/** + * wma_handle_hidden_ssid_restart() - handle hidden ssid restart + * @wma: wma handle + * @iface: interfcae pointer + * + * Return: none + */ +static void wma_handle_hidden_ssid_restart(tp_wma_handle wma, + struct wma_txrx_node *iface) +{ + wlan_vdev_mlme_sm_deliver_evt(iface->vdev, + WLAN_VDEV_SM_EV_RESTART_RESP, + 0, NULL); +} + +static void wma_peer_send_phymode(struct wlan_objmgr_vdev *vdev, + void *object, void *arg) +{ + struct wlan_objmgr_peer *peer = object; + enum wlan_phymode old_peer_phymode; + struct wlan_channel *vdev_chan; + enum wlan_phymode new_phymode; + tSirNwType nw_type; + uint32_t fw_phymode; + uint32_t max_ch_width_supported; + tp_wma_handle wma; + uint8_t *peer_mac_addr; + uint8_t vdev_id; + + if (wlan_peer_get_peer_type(peer) == WLAN_PEER_SELF) + return; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + + if(!wma) { + wma_err("wma handle NULL"); + return; + } + + old_peer_phymode = wlan_peer_get_phymode(peer); + vdev_chan = wlan_vdev_mlme_get_des_chan(vdev); + + peer_mac_addr = wlan_peer_get_macaddr(peer); + + if (WLAN_REG_IS_24GHZ_CH_FREQ(vdev_chan->ch_freq)) { + if (vdev_chan->ch_phymode == WLAN_PHYMODE_11B || + old_peer_phymode == WLAN_PHYMODE_11B) + nw_type = eSIR_11B_NW_TYPE; + else + nw_type = eSIR_11G_NW_TYPE; + } else { + nw_type = eSIR_11A_NW_TYPE; + } + new_phymode = wma_peer_phymode(nw_type, STA_ENTRY_PEER, + IS_WLAN_PHYMODE_HT(old_peer_phymode), + vdev_chan->ch_width, + IS_WLAN_PHYMODE_VHT(old_peer_phymode), + IS_WLAN_PHYMODE_HE(old_peer_phymode)); + + wlan_peer_set_phymode(peer, new_phymode); + + fw_phymode = wma_host_to_fw_phymode(new_phymode); + vdev_id = wlan_vdev_get_id(vdev); + + wma_set_peer_param(wma, peer_mac_addr, WMI_PEER_PHYMODE, + fw_phymode, vdev_id); + + max_ch_width_supported = + wmi_get_ch_width_from_phy_mode(wma->wmi_handle, fw_phymode); + wma_set_peer_param(wma, peer_mac_addr, WMI_PEER_CHWIDTH, + max_ch_width_supported, vdev_id); + + wma_debug("FW phymode %d old phymode %d new phymode %d bw %d macaddr "QDF_MAC_ADDR_FMT, + fw_phymode, old_peer_phymode, new_phymode, + max_ch_width_supported, QDF_MAC_ADDR_REF(peer_mac_addr)); +} + +static +void wma_update_rate_flags_after_vdev_restart(tp_wma_handle wma, + struct wma_txrx_node *iface) +{ + struct vdev_mlme_obj *vdev_mlme; + enum tx_rate_info rate_flags = 0; + enum wlan_phymode bss_phymode; + struct wlan_channel *des_chan; + + if (iface->type != WMI_VDEV_TYPE_STA) + return; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (!vdev_mlme) + return; + + des_chan = wlan_vdev_mlme_get_des_chan(iface->vdev); + bss_phymode = des_chan->ch_phymode; + + if (IS_WLAN_PHYMODE_HE(bss_phymode)) { + rate_flags = wma_get_he_rate_flags(des_chan->ch_width); + } else if (IS_WLAN_PHYMODE_VHT(bss_phymode)) { + rate_flags = wma_get_vht_rate_flags(des_chan->ch_width); + } else if (IS_WLAN_PHYMODE_HT(bss_phymode)) { + rate_flags = wma_get_ht_rate_flags(des_chan->ch_width); + } else { + rate_flags = TX_RATE_LEGACY; + } + + wma_debug("bss phymode %d rate_flags %x, ch_width %d", + bss_phymode, rate_flags, des_chan->ch_width); + ucfg_mc_cp_stats_set_rate_flags(iface->vdev, rate_flags); +} + +QDF_STATUS wma_handle_channel_switch_resp(tp_wma_handle wma, + struct vdev_start_response *rsp) +{ + enum wlan_vdev_sm_evt event; + struct wma_txrx_node *iface; + + iface = &wma->interfaces[rsp->vdev_id]; + WMA_LOGD("%s: Send channel switch resp vdev %d status %d", + __func__, rsp->vdev_id, rsp->status); + + /* Indicate channel switch failure to LIM */ + if (QDF_IS_STATUS_ERROR(rsp->status) && + (iface->type == WMI_VDEV_TYPE_MONITOR || + wma_is_vdev_in_ap_mode(wma, rsp->vdev_id) || + mlme_is_chan_switch_in_progress(iface->vdev))) { + mlme_set_chan_switch_in_progress(iface->vdev, false); + lim_process_switch_channel_rsp(wma->mac_context, rsp); + return QDF_STATUS_SUCCESS; + } + + if (QDF_IS_STATUS_SUCCESS(rsp->status) && + rsp->resp_type == WMI_VDEV_RESTART_RESP_EVENT) { + wlan_objmgr_iterate_peerobj_list(iface->vdev, + wma_peer_send_phymode, NULL, + WLAN_LEGACY_WMA_ID); + wma_update_rate_flags_after_vdev_restart(wma, iface); + } + + if (wma_is_vdev_in_ap_mode(wma, rsp->vdev_id) || + mlme_is_chan_switch_in_progress(iface->vdev)) + event = WLAN_VDEV_SM_EV_RESTART_RESP; + else + event = WLAN_VDEV_SM_EV_START_RESP; + wlan_vdev_mlme_sm_deliver_evt(iface->vdev, event, + sizeof(rsp), rsp); + + return QDF_STATUS_SUCCESS; +} + +/* + * wma_get_ratemask_type() - convert user input ratemask type to FW type + * @type: User input ratemask type maintained in HDD + * @fwtype: Value return arg for fw ratemask type value + * + * Return: FW configurable ratemask type + */ +static QDF_STATUS wma_get_ratemask_type(enum wlan_mlme_ratemask_type type, + uint8_t *fwtype) +{ + switch (type) { + case WLAN_MLME_RATEMASK_TYPE_CCK: + *fwtype = WMI_RATEMASK_TYPE_CCK; + break; + case WLAN_MLME_RATEMASK_TYPE_HT: + *fwtype = WMI_RATEMASK_TYPE_HT; + break; + case WLAN_MLME_RATEMASK_TYPE_VHT: + *fwtype = WMI_RATEMASK_TYPE_VHT; + break; + case WLAN_MLME_RATEMASK_TYPE_HE: + *fwtype = WMI_RATEMASK_TYPE_HE; + break; + default: + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_vdev_start_resp_handler(struct vdev_mlme_obj *vdev_mlme, + struct vdev_start_response *rsp) +{ + tp_wma_handle wma; + struct wma_txrx_node *iface; + target_resource_config *wlan_res_cfg; + struct wlan_objmgr_psoc *psoc; +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); +#endif + QDF_STATUS status; + enum vdev_assoc_type assoc_type = VDEV_ASSOC; + struct vdev_mlme_obj *mlme_obj; + struct wlan_mlme_psoc_ext_obj *mlme_psoc_obj; + const struct wlan_mlme_ratemask *ratemask_cfg; + struct config_ratemask_params rparams = {0}; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + wma_err("wma wma is NULL for VDEV_%d", rsp->vdev_id); + return QDF_STATUS_E_FAILURE; + } + psoc = wma->psoc; + if (!psoc) { + WMA_LOGE("%s: psoc is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + mlme_psoc_obj = mlme_get_psoc_ext_obj(psoc); + ratemask_cfg = &mlme_psoc_obj->cfg.ratemask_cfg; + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + if (!mac_ctx) { + WMA_LOGE("%s: Failed to get mac_ctx", __func__); + return QDF_STATUS_E_FAILURE; + } +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + + wlan_res_cfg = lmac_get_tgt_res_cfg(psoc); + if (!wlan_res_cfg) { + WMA_LOGE("%s: Wlan resource config is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (rsp->vdev_id >= wma->max_bssid) { + WMA_LOGE("Invalid vdev id received from firmware"); + return QDF_STATUS_E_FAILURE; + } + + if (wma_is_vdev_in_ap_mode(wma, rsp->vdev_id)) + tgt_dfs_radar_enable(wma->pdev, 0, 0, true); + + iface = &wma->interfaces[rsp->vdev_id]; + if (!iface->vdev) { + wma_err("Invalid vdev"); + return QDF_STATUS_E_FAILURE; + } + + if (rsp->status == QDF_STATUS_SUCCESS) { + wma->interfaces[rsp->vdev_id].tx_streams = + rsp->cfgd_tx_streams; + + if (wlan_res_cfg->use_pdev_id) { + if (rsp->mac_id == OL_TXRX_PDEV_ID) { + wma_err("soc level id received for mac id"); + return -QDF_STATUS_E_INVAL; + } + wma->interfaces[rsp->vdev_id].mac_id = + WMA_PDEV_TO_MAC_MAP(rsp->mac_id); + } else { + wma->interfaces[rsp->vdev_id].mac_id = + rsp->mac_id; + } + + WMA_LOGD("%s: vdev:%d tx ss=%d rx ss=%d chain mask=%d mac=%d", + __func__, + rsp->vdev_id, + rsp->cfgd_tx_streams, + rsp->cfgd_rx_streams, + rsp->chain_mask, + wma->interfaces[rsp->vdev_id].mac_id); + + /* Fill bss_chan after vdev start */ + qdf_mem_copy(iface->vdev->vdev_mlme.bss_chan, + iface->vdev->vdev_mlme.des_chan, + sizeof(struct wlan_channel)); + } + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE + if (rsp->status == QDF_STATUS_SUCCESS + && mac_ctx->sap.sap_channel_avoidance) + wma_find_mcc_ap(wma, rsp->vdev_id, true); +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + + if (wma_get_hidden_ssid_restart_in_progress(iface) && + wma_is_vdev_in_ap_mode(wma, rsp->vdev_id)) { + wma_handle_hidden_ssid_restart(wma, iface); + return QDF_STATUS_SUCCESS; + } + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->mgmt.generic.tx_pwrlimit = rsp->max_allowed_tx_power; + if (iface->type == WMI_VDEV_TYPE_STA) + assoc_type = mlme_get_assoc_type(vdev_mlme->vdev); + + if (mlme_is_chan_switch_in_progress(iface->vdev) || + iface->type == WMI_VDEV_TYPE_MONITOR || + (iface->type == WMI_VDEV_TYPE_STA && + (assoc_type == VDEV_ASSOC || assoc_type == VDEV_REASSOC))) { + status = wma_handle_channel_switch_resp(wma, + rsp); + if (QDF_IS_STATUS_ERROR(status)) + return QDF_STATUS_E_FAILURE; + } else if (iface->type == WMI_VDEV_TYPE_OCB) { + mlme_obj->proto.sta.assoc_id = iface->aid; + if (vdev_mgr_up_send(mlme_obj) != QDF_STATUS_SUCCESS) { + WMA_LOGE(FL("failed to send vdev up")); + return QDF_STATUS_E_FAILURE; + } + ucfg_ocb_config_channel(wma->pdev); + } else { + struct qdf_mac_addr bss_peer; + + status = + mlme_get_vdev_bss_peer_mac_addr(iface->vdev, &bss_peer); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to get bssid", __func__); + return QDF_STATUS_E_INVAL; + } + qdf_mem_copy(mlme_obj->mgmt.generic.bssid, bss_peer.bytes, + QDF_MAC_ADDR_SIZE); + wma_vdev_start_rsp(wma, vdev_mlme->vdev, rsp); + } + if (iface->type == WMI_VDEV_TYPE_AP && wma_is_vdev_up(rsp->vdev_id)) + wma_set_sap_keepalive(wma, rsp->vdev_id); + + /* Send ratemask to firmware */ + if ((ratemask_cfg->type > WLAN_MLME_RATEMASK_TYPE_NO_MASK) && + (ratemask_cfg->type < WLAN_MLME_RATEMASK_TYPE_MAX)) { + struct wmi_unified *wmi_handle = wma->wmi_handle; + + if (!wmi_handle) { + wma_err(FL("wmi_handle is null")); + return QDF_STATUS_E_INVAL; + } + + rparams.vdev_id = rsp->vdev_id; + status = wma_get_ratemask_type(ratemask_cfg->type, + &rparams.type); + + if (QDF_IS_STATUS_ERROR(status)) { + wma_err(FL("unable to map ratemask")); + /* don't fail, default rates will still work */ + return QDF_STATUS_SUCCESS; + } + + rparams.lower32 = ratemask_cfg->lower32; + rparams.higher32 = ratemask_cfg->higher32; + rparams.lower32_2 = ratemask_cfg->lower32_2; + rparams.higher32_2 = ratemask_cfg->higher32_2; + + status = wmi_unified_vdev_config_ratemask_cmd_send(wmi_handle, + &rparams); + /* Only log failure. Do not abort */ + if (QDF_IS_STATUS_ERROR(status)) + wma_err(FL("failed to send ratemask")); + } + + return QDF_STATUS_SUCCESS; +} + +bool wma_is_vdev_valid(uint32_t vdev_id) +{ + tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGD("%s: vdev_id: %d, null wma_handle", __func__, vdev_id); + return false; + } + + /* No of interface are allocated based on max_bssid value */ + if (vdev_id >= wma_handle->max_bssid) { + WMA_LOGD("%s: vdev_id: %d is invalid, max_bssid: %d", + __func__, vdev_id, wma_handle->max_bssid); + return false; + } + + return wma_handle->interfaces[vdev_id].vdev_active; +} + +/** + * wma_vdev_set_param() - set per vdev params in fw + * @wmi_handle: wmi handle + * @if_id: vdev id + * @param_id: parameter id + * @param_value: parameter value + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS +wma_vdev_set_param(wmi_unified_t wmi_handle, uint32_t if_id, + uint32_t param_id, uint32_t param_value) +{ + struct vdev_set_params param = {0}; + + if (!wma_is_vdev_valid(if_id)) { + WMA_LOGE(FL("vdev_id: %d is not active reject the req: param id %d val %d"), + if_id, param_id, param_value); + return QDF_STATUS_E_INVAL; + } + + param.vdev_id = if_id; + param.param_id = param_id; + param.param_value = param_value; + + return wmi_unified_vdev_set_param_send(wmi_handle, ¶m); +} + +/** + * wma_set_peer_authorized_cb() - set peer authorized callback function + * @wma_ctx: wma handle + * @auth_cb: peer authorized callback + * + * Return: none + */ +void wma_set_peer_authorized_cb(void *wma_ctx, wma_peer_authorized_fp auth_cb) +{ + tp_wma_handle wma_handle = (tp_wma_handle) wma_ctx; + + wma_handle->peer_authorized_cb = auth_cb; +} + +/** + * wma_set_peer_param() - set peer parameter in fw + * @wma_ctx: wma handle + * @peer_addr: peer mac address + * @param_id: parameter id + * @param_value: parameter value + * @vdev_id: vdev id + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_set_peer_param(void *wma_ctx, uint8_t *peer_addr, + uint32_t param_id, uint32_t param_value, + uint32_t vdev_id) +{ + tp_wma_handle wma_handle = (tp_wma_handle) wma_ctx; + struct peer_set_params param = {0}; + QDF_STATUS status; + + param.vdev_id = vdev_id; + param.param_value = param_value; + param.param_id = param_id; + + status = wmi_set_peer_param_send(wma_handle->wmi_handle, + peer_addr, + ¶m); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("vdev_id: %d peer set failed, id %d, val %d", + vdev_id, param_id, param_value); + return status; +} + +/** + * wma_peer_unmap_conf_send - send peer unmap conf cmnd to fw + * @wma_ctx: wma handle + * @msg: peer unmap conf params + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_peer_unmap_conf_send(tp_wma_handle wma, + struct send_peer_unmap_conf_params *msg) +{ + QDF_STATUS qdf_status; + + if (!msg) { + WMA_LOGE("%s: null input params", __func__); + return QDF_STATUS_E_INVAL; + } + + qdf_status = wmi_unified_peer_unmap_conf_send( + wma->wmi_handle, + msg->vdev_id, + msg->peer_id_cnt, + msg->peer_id_list); + + if (qdf_status != QDF_STATUS_SUCCESS) + WMA_LOGE("%s: peer_unmap_conf_send failed %d", + __func__, qdf_status); + + qdf_mem_free(msg->peer_id_list); + msg->peer_id_list = NULL; + + return qdf_status; +} + +/** + * wma_peer_unmap_conf_cb - send peer unmap conf cmnd to fw + * @vdev_id: vdev id + * @peer_id_cnt: no of peer id + * @peer_id_list: list of peer ids + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_peer_unmap_conf_cb(uint8_t vdev_id, + uint32_t peer_id_cnt, + uint16_t *peer_id_list) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + QDF_STATUS qdf_status; + + if (!wma) { + WMA_LOGE("%s: peer_id_cnt: %d, null wma_handle", + __func__, peer_id_cnt); + return QDF_STATUS_E_INVAL; + } + + qdf_status = wmi_unified_peer_unmap_conf_send( + wma->wmi_handle, + vdev_id, peer_id_cnt, + peer_id_list); + + if (qdf_status == QDF_STATUS_E_BUSY) { + QDF_STATUS retcode; + struct scheduler_msg msg = {0}; + struct send_peer_unmap_conf_params *peer_unmap_conf_req; + void *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + + WMA_LOGD("%s: post unmap_conf cmd to MC thread", __func__); + + if (!mac_ctx) { + WMA_LOGE("%s: mac_ctx is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + peer_unmap_conf_req = qdf_mem_malloc(sizeof( + struct send_peer_unmap_conf_params)); + + if (!peer_unmap_conf_req) { + WMA_LOGE("%s: peer_unmap_conf_req memory alloc failed", + __func__); + return QDF_STATUS_E_NOMEM; + } + + peer_unmap_conf_req->vdev_id = vdev_id; + peer_unmap_conf_req->peer_id_cnt = peer_id_cnt; + peer_unmap_conf_req->peer_id_list = qdf_mem_malloc( + sizeof(uint16_t) * peer_id_cnt); + if (!peer_unmap_conf_req->peer_id_list) { + WMA_LOGE("%s: peer_id_list memory alloc failed", + __func__); + qdf_mem_free(peer_unmap_conf_req); + peer_unmap_conf_req = NULL; + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(peer_unmap_conf_req->peer_id_list, + peer_id_list, sizeof(uint16_t) * peer_id_cnt); + + msg.type = WMA_SEND_PEER_UNMAP_CONF; + msg.reserved = 0; + msg.bodyptr = peer_unmap_conf_req; + msg.bodyval = 0; + + retcode = wma_post_ctrl_msg(mac_ctx, &msg); + if (retcode != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: wma_post_ctrl_msg failed", __func__); + qdf_mem_free(peer_unmap_conf_req->peer_id_list); + qdf_mem_free(peer_unmap_conf_req); + return QDF_STATUS_E_FAILURE; + } + } + + return qdf_status; +} + +bool wma_objmgr_peer_exist(tp_wma_handle wma, + uint8_t *peer_addr, uint8_t *peer_vdev_id) +{ + struct wlan_objmgr_peer *peer; + + peer = wlan_objmgr_get_peer_by_mac(wma->psoc, peer_addr, + WLAN_LEGACY_WMA_ID); + if (!peer) + return false; + + if (peer_vdev_id) + *peer_vdev_id = wlan_vdev_get_id(wlan_peer_get_vdev(peer)); + + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + + return true; +} + +/** + * wma_remove_peer() - remove peer information from host driver and fw + * @wma: wma handle + * @mac_addr: peer mac address, to be removed + * @vdev_id: vdev id + * @no_fw_peer_delete: If true dont send peer delete to firmware + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_remove_peer(tp_wma_handle wma, uint8_t *mac_addr, + uint8_t vdev_id, bool no_fw_peer_delete) +{ +#define PEER_ALL_TID_BITMASK 0xffffffff + uint32_t peer_tid_bitmap = PEER_ALL_TID_BITMASK; + uint8_t *peer_addr = mac_addr; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE] = {0}; + struct peer_flush_params param = {0}; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + uint32_t bitmap = 1 << CDP_PEER_DELETE_NO_SPECIAL; + bool peer_unmap_conf_support_enabled; + uint8_t peer_vdev_id; + + if (!wma->interfaces[vdev_id].peer_count) { + WMA_LOGE("%s: Can't remove peer with peer_addr "QDF_MAC_ADDR_FMT" vdevid %d peer_count %d", + __func__, QDF_MAC_ADDR_REF(peer_addr), vdev_id, + wma->interfaces[vdev_id].peer_count); + cds_trigger_recovery(QDF_REASON_UNSPECIFIED); + return QDF_STATUS_E_INVAL; + } + + if (!soc) { + WMA_LOGE("%s:SOC context is NULL", __func__); + QDF_BUG(0); + return QDF_STATUS_E_INVAL; + } + + if (!wma_objmgr_peer_exist(wma, peer_addr, &peer_vdev_id)) { + wma_err("peer doesn't exist peer_addr "QDF_MAC_ADDR_FMT" vdevid %d peer_count %d", + QDF_MAC_ADDR_REF(peer_addr), vdev_id, + wma->interfaces[vdev_id].peer_count); + return QDF_STATUS_E_INVAL; + } + + if (peer_vdev_id != vdev_id) { + wma_err("peer "QDF_MAC_ADDR_FMT" is on vdev id %d but delete req on vdevid %d peer_count %d", + QDF_MAC_ADDR_REF(peer_addr), peer_vdev_id, vdev_id, + wma->interfaces[vdev_id].peer_count); + return QDF_STATUS_E_INVAL; + } + peer_unmap_conf_support_enabled = + cdp_cfg_get_peer_unmap_conf_support(soc); + + cdp_peer_teardown(soc, vdev_id, peer_addr); + + if (no_fw_peer_delete) + goto peer_detach; + + /* Flush all TIDs except MGMT TID for this peer in Target */ + peer_tid_bitmap &= ~(0x1 << WMI_MGMT_TID); + param.peer_tid_bitmap = peer_tid_bitmap; + param.vdev_id = vdev_id; + if (!wmi_service_enabled(wma->wmi_handle, + wmi_service_peer_delete_no_peer_flush_tids_cmd)) + wmi_unified_peer_flush_tids_send(wma->wmi_handle, mac_addr, + ¶m); + + /* peer->ref_cnt is not visible in WMA */ + wlan_roam_debug_log(vdev_id, DEBUG_PEER_DELETE_SEND, + DEBUG_INVALID_PEER_ID, peer_addr, NULL, + 0, 0); + qdf_status = wmi_unified_peer_delete_send(wma->wmi_handle, peer_addr, + vdev_id); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE("%s Peer delete could not be sent to firmware %d", + __func__, qdf_status); + /* Clear default bit and set to NOT_START_UNMAP */ + bitmap = 1 << CDP_PEER_DO_NOT_START_UNMAP_TIMER; + qdf_status = QDF_STATUS_E_FAILURE; + } + +peer_detach: + WMA_LOGD("%s: vdevid %d is detaching with peer_addr "QDF_MAC_ADDR_FMT" peer_count %d", + __func__, vdev_id, QDF_MAC_ADDR_REF(peer_addr), + wma->interfaces[vdev_id].peer_count); + /* Copy peer mac to find and delete objmgr peer */ + qdf_mem_copy(peer_mac, peer_addr, QDF_MAC_ADDR_SIZE); + if (no_fw_peer_delete && + is_cdp_peer_detach_force_delete_supported(soc)) { + if (!peer_unmap_conf_support_enabled) { + WMA_LOGD("%s: LFR3: trigger force delete for peer "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(peer_addr)); + cdp_peer_detach_force_delete(soc, vdev_id, peer_addr); + } else { + cdp_peer_delete_sync(soc, vdev_id, peer_addr, + wma_peer_unmap_conf_cb, + bitmap); + } + } else { + if (no_fw_peer_delete) { + WMA_LOGD("%s: LFR3: normal peer delete for peer "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(peer_addr)); + } + if (peer_unmap_conf_support_enabled) + cdp_peer_delete_sync(soc, vdev_id, peer_addr, + wma_peer_unmap_conf_cb, + bitmap); + else + cdp_peer_delete(soc, vdev_id, peer_addr, bitmap); + } + + wma_remove_objmgr_peer(wma, vdev_id, peer_mac); + + wma->interfaces[vdev_id].peer_count--; +#undef PEER_ALL_TID_BITMASK + + return qdf_status; +} + +/** + * wma_get_peer_type() - Determine the type of peer(eg. STA/AP) and return it + * @wma: wma handle + * @vdev_id: vdev id + * @peer_addr: peer mac address + * @wma_peer_type: wma peer type + * + * Return: Peer type + */ +static int wma_get_obj_mgr_peer_type(tp_wma_handle wma, uint8_t vdev_id, + uint8_t *peer_addr, uint32_t wma_peer_type) + +{ + uint32_t obj_peer_type = 0; + struct wlan_objmgr_vdev *vdev; + uint8_t *addr; + + vdev = wma->interfaces[vdev_id].vdev; + if (!vdev) { + WMA_LOGE("Couldnt find vdev for VDEV_%d", vdev_id); + return obj_peer_type; + } + addr = wlan_vdev_mlme_get_macaddr(vdev); + + if (wma_peer_type == WMI_PEER_TYPE_TDLS) + return WLAN_PEER_TDLS; + + if (!qdf_mem_cmp(addr, peer_addr, QDF_MAC_ADDR_SIZE)) { + obj_peer_type = WLAN_PEER_SELF; + } else if (wma->interfaces[vdev_id].type == WMI_VDEV_TYPE_STA) { + if (wma->interfaces[vdev_id].sub_type == + WMI_UNIFIED_VDEV_SUBTYPE_P2P_CLIENT) + obj_peer_type = WLAN_PEER_P2P_GO; + else + obj_peer_type = WLAN_PEER_AP; + } else if (wma->interfaces[vdev_id].type == WMI_VDEV_TYPE_AP) { + obj_peer_type = WLAN_PEER_STA; + } else if (wma->interfaces[vdev_id].type == WMI_VDEV_TYPE_IBSS) { + obj_peer_type = WLAN_PEER_IBSS; + } else if (wma->interfaces[vdev_id].type == WMI_VDEV_TYPE_NDI) { + obj_peer_type = WLAN_PEER_NDP; + } else { + WMA_LOGE("Couldnt find peertype for type %d and sub type %d", + wma->interfaces[vdev_id].type, + wma->interfaces[vdev_id].sub_type); + } + + return obj_peer_type; + +} + +/** + * wma_create_objmgr_peer() - create objmgr peer information in host driver + * @wma: wma handle + * @vdev_id: vdev id + * @peer_addr: peer mac address + * @wma_peer_type: peer type + * + * Return: objmgr peer pointer + */ + +static struct wlan_objmgr_peer *wma_create_objmgr_peer(tp_wma_handle wma, + uint8_t vdev_id, + uint8_t *peer_addr, + uint32_t wma_peer_type) +{ + uint32_t obj_peer_type = 0; + struct wlan_objmgr_peer *obj_peer = NULL; + struct wlan_objmgr_vdev *obj_vdev = NULL; + struct wlan_objmgr_psoc *psoc = wma->psoc; + uint8_t peer_vdev_id; + + /* + * Check if peer with same MAC exist on any Vdev, If so avoid + * adding this peer. + */ + if (wma_objmgr_peer_exist(wma, peer_addr, &peer_vdev_id)) { + wma_info("Peer "QDF_MAC_ADDR_FMT" already exist on vdev %d, current vdev %d", + QDF_MAC_ADDR_REF(peer_addr), peer_vdev_id, vdev_id); + return NULL; + } + + obj_peer_type = wma_get_obj_mgr_peer_type(wma, vdev_id, peer_addr, + wma_peer_type); + if (!obj_peer_type) { + WMA_LOGE("Invalid obj peer type. Unable to create peer %d", + obj_peer_type); + return NULL; + } + + /* Create obj_mgr peer */ + obj_vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, + WLAN_LEGACY_WMA_ID); + + if (!obj_vdev) { + WMA_LOGE("Invalid obj vdev. Unable to create peer %d", + obj_peer_type); + return NULL; + } + + obj_peer = wlan_objmgr_peer_obj_create(obj_vdev, obj_peer_type, + peer_addr); + wlan_objmgr_vdev_release_ref(obj_vdev, WLAN_LEGACY_WMA_ID); + + return obj_peer; + +} +/** + * wma_create_peer() - send peer create command to fw + * @wma: wma handle + * @peer_addr: peer mac addr + * @peer_type: peer type + * @vdev_id: vdev id + * @roam_synch_in_progress: roam in progress + * + * Return: QDF status + */ +QDF_STATUS wma_create_peer(tp_wma_handle wma, + uint8_t peer_addr[QDF_MAC_ADDR_SIZE], + uint32_t peer_type, uint8_t vdev_id, + bool roam_synch_in_progress) +{ + struct peer_create_params param = {0}; + uint8_t *mac_addr_raw; + void *dp_soc = cds_get_context(QDF_MODULE_ID_SOC); + struct wlan_objmgr_psoc *psoc = wma->psoc; + target_resource_config *wlan_res_cfg; + struct wlan_objmgr_peer *obj_peer = NULL; + QDF_STATUS status; + + if (!psoc) { + WMA_LOGE("%s: psoc is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + wlan_res_cfg = lmac_get_tgt_res_cfg(psoc); + if (!wlan_res_cfg) { + WMA_LOGE("%s: psoc target res cfg is null", __func__); + return QDF_STATUS_E_INVAL; + } + + if (++wma->interfaces[vdev_id].peer_count > + wlan_res_cfg->num_peers) { + WMA_LOGE("%s, the peer count exceeds the limit %d", __func__, + wma->interfaces[vdev_id].peer_count - 1); + goto err; + } + + if (!dp_soc) { + WMA_LOGE("%s:DP SOC context is NULL", __func__); + goto err; + } + + if (qdf_is_macaddr_group((struct qdf_mac_addr *)peer_addr) || + qdf_is_macaddr_zero((struct qdf_mac_addr *)peer_addr)) { + WMA_LOGE("Invalid peer address received reject it"); + goto err; + } + + /* + * Check if peer with same MAC exist on other Vdev, If so avoid + * adding this peer, as it will cause FW to crash. + */ + if (cdp_find_peer_exist_on_other_vdev(dp_soc, vdev_id, peer_addr, + wma->max_bssid)) + goto err; + + obj_peer = wma_create_objmgr_peer(wma, vdev_id, peer_addr, peer_type); + if (!obj_peer) + goto err; + + /* The peer object should be created before sending the WMI peer + * create command to firmware. This is to prevent a race condition + * where the HTT peer map event is received before the peer object + * is created in the data path + */ + status = cdp_peer_create(dp_soc, vdev_id, peer_addr); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s : Unable to attach peer "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(peer_addr)); + wlan_objmgr_peer_obj_delete(obj_peer); + goto err; + } + + if (peer_type == WMI_PEER_TYPE_TDLS) + cdp_peer_set_peer_as_tdls(dp_soc, vdev_id, peer_addr, true); + + if (roam_synch_in_progress) { + WMA_LOGD("%s: LFR3: Created peer "QDF_MAC_ADDR_FMT" vdev_id %d, peer_count %d", + __func__, + QDF_MAC_ADDR_REF(peer_addr), vdev_id, + wma->interfaces[vdev_id].peer_count); + cdp_peer_setup(dp_soc, vdev_id, peer_addr); + return QDF_STATUS_SUCCESS; + } + param.peer_addr = peer_addr; + param.peer_type = peer_type; + param.vdev_id = vdev_id; + if (wmi_unified_peer_create_send(wma->wmi_handle, + ¶m) != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s : Unable to create peer in Target", __func__); + if (cdp_cfg_get_peer_unmap_conf_support(dp_soc)) + cdp_peer_delete_sync( + dp_soc, vdev_id, peer_addr, + wma_peer_unmap_conf_cb, + 1 << CDP_PEER_DO_NOT_START_UNMAP_TIMER); + else + cdp_peer_delete( + dp_soc, vdev_id, peer_addr, + 1 << CDP_PEER_DO_NOT_START_UNMAP_TIMER); + wlan_objmgr_peer_obj_delete(obj_peer); + goto err; + } + + WMA_LOGD("%s: Created peer peer_addr "QDF_MAC_ADDR_FMT" vdev_id %d, peer_count - %d", + __func__, QDF_MAC_ADDR_REF(peer_addr), vdev_id, + wma->interfaces[vdev_id].peer_count); + + wlan_roam_debug_log(vdev_id, DEBUG_PEER_CREATE_SEND, + DEBUG_INVALID_PEER_ID, peer_addr, NULL, 0, 0); + cdp_peer_setup(dp_soc, vdev_id, peer_addr); + + mac_addr_raw = cdp_get_vdev_mac_addr(dp_soc, vdev_id); + if (!mac_addr_raw) { + WMA_LOGE("%s: peer mac addr is NULL", __func__); + return QDF_STATUS_E_FAULT; + } + + /* for each remote ibss peer, clear its keys */ + if (wma_is_vdev_in_ibss_mode(wma, vdev_id) && + qdf_mem_cmp(peer_addr, mac_addr_raw, QDF_MAC_ADDR_SIZE)) { + tpSetStaKeyParams key_info; + + key_info = qdf_mem_malloc(sizeof(*key_info)); + if (!key_info) { + return QDF_STATUS_E_NOMEM; + } + WMA_LOGD("%s: remote ibss peer "QDF_MAC_ADDR_FMT" key clearing\n", + __func__, QDF_MAC_ADDR_REF(peer_addr)); + qdf_mem_zero(key_info, sizeof(*key_info)); + key_info->vdev_id = vdev_id; + qdf_mem_copy(key_info->peer_macaddr.bytes, peer_addr, + QDF_MAC_ADDR_SIZE); + key_info->sendRsp = false; + } + + return QDF_STATUS_SUCCESS; +err: + wma->interfaces[vdev_id].peer_count--; + return QDF_STATUS_E_FAILURE; +} + +/** + * wma_remove_bss_peer() - remove BSS peer + * @wma: pointer to WMA handle + * @vdev_id: vdev id on which delete BSS request was received + * @vdev_stop_resp: pointer to Delete BSS response + * + * This function is called on receiving vdev stop response from FW or + * vdev stop response timeout. In case of IBSS/NDI, use vdev's self MAC + * for removing the peer. In case of STA/SAP use bssid passed as part of + * delete STA parameter. + * + * Return: 0 on success, ERROR code on failure + */ +static int wma_remove_bss_peer(tp_wma_handle wma, uint32_t vdev_id, + struct del_bss_resp *vdev_stop_resp, + uint8_t type) +{ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint8_t *mac_addr = NULL; + struct wma_target_req *del_req; + int ret_value = 0; + QDF_STATUS qdf_status; + struct qdf_mac_addr bssid; + + if (wma_is_vdev_in_ibss_mode(wma, vdev_id) || + WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces, vdev_id)) { + mac_addr = cdp_get_vdev_mac_addr(soc, vdev_id); + if (!mac_addr) { + WMA_LOGE(FL("mac_addr is NULL for vdev_id = %d"), + vdev_id); + return -EINVAL; + } + } else { + qdf_status = mlme_get_vdev_bss_peer_mac_addr( + wma->interfaces[vdev_id].vdev, + &bssid); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE(FL("Failed to get bssid for vdev_id: %d"), + vdev_id); + return -EINVAL; + } + mac_addr = bssid.bytes; + } + + qdf_status = wma_remove_peer(wma, mac_addr, vdev_id, false); + + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE(FL("wma_remove_peer failed vdev_id:%d"), vdev_id); + return -EINVAL; + } + + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_sync_delete_cmds)) { + WMA_LOGD(FL("Wait for the peer delete. vdev_id %d"), + vdev_id); + del_req = wma_fill_hold_req(wma, vdev_id, + WMA_DELETE_STA_REQ, + type, + vdev_stop_resp, + WMA_DELETE_STA_TIMEOUT); + if (!del_req) { + WMA_LOGE(FL("Failed to allocate request. vdev_id %d"), + vdev_id); + vdev_stop_resp->status = QDF_STATUS_E_NOMEM; + ret_value = -EINVAL; + } + } + + return ret_value; +} + +#ifdef FEATURE_WLAN_APF +/* + * get_fw_active_apf_mode() - convert HDD APF mode to FW configurable APF + * mode + * @mode: APF mode maintained in HDD + * + * Return: FW configurable BP mode + */ +static enum wmi_host_active_apf_mode +get_fw_active_apf_mode(enum active_apf_mode mode) +{ + switch (mode) { + case ACTIVE_APF_DISABLED: + return WMI_HOST_ACTIVE_APF_DISABLED; + case ACTIVE_APF_ENABLED: + return WMI_HOST_ACTIVE_APF_ENABLED; + case ACTIVE_APF_ADAPTIVE: + return WMI_HOST_ACTIVE_APF_ADAPTIVE; + default: + WMA_LOGE("Invalid Active APF Mode %d; Using 'disabled'", mode); + return WMI_HOST_ACTIVE_APF_DISABLED; + } +} + +/** + * wma_config_active_apf_mode() - Config active APF mode in FW + * @wma: the WMA handle + * @vdev_id: the Id of the vdev for which the configuration should be applied + * + * Return: QDF status + */ +static QDF_STATUS wma_config_active_apf_mode(t_wma_handle *wma, uint8_t vdev_id) +{ + enum wmi_host_active_apf_mode uc_mode, mcbc_mode; + + uc_mode = get_fw_active_apf_mode(wma->active_uc_apf_mode); + mcbc_mode = get_fw_active_apf_mode(wma->active_mc_bc_apf_mode); + + WMA_LOGD("Configuring Active APF Mode UC:%d MC/BC:%d for vdev %u", + uc_mode, mcbc_mode, vdev_id); + + return wmi_unified_set_active_apf_mode_cmd(wma->wmi_handle, vdev_id, + uc_mode, mcbc_mode); +} +#else /* FEATURE_WLAN_APF */ +static QDF_STATUS wma_config_active_apf_mode(t_wma_handle *wma, uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_WLAN_APF */ + +#ifdef FEATURE_AP_MCC_CH_AVOIDANCE +/** + * wma_check_and_find_mcc_ap() - finds if device is operating AP + * in MCC mode or not + * @wma: wma handle. + * @vdev_id: vdev ID of device for which MCC has to be checked + * + * This function internally calls wma_find_mcc_ap finds if + * device is operating AP in MCC mode or not + * + * Return: none + */ +static void +wma_check_and_find_mcc_ap(tp_wma_handle wma, uint8_t vdev_id) +{ + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac_ctx) { + WMA_LOGE("%s: Failed to get mac_ctx", __func__); + return; + } + if (mac_ctx->sap.sap_channel_avoidance) + wma_find_mcc_ap(wma, vdev_id, false); +} +#else +static inline void +wma_check_and_find_mcc_ap(tp_wma_handle wma, uint8_t vdev_id) +{} +#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ + +void wma_send_del_bss_response(tp_wma_handle wma, struct del_bss_resp *resp) +{ + struct wma_txrx_node *iface; + struct beacon_info *bcn; + uint8_t vdev_id; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!resp) { + WMA_LOGE("%s req is NULL", __func__); + return; + } + + vdev_id = resp->vdev_id; + iface = &wma->interfaces[vdev_id]; + + if (!iface->vdev) { + WMA_LOGE("%s vdev id %d iface->vdev is NULL", + __func__, vdev_id); + if (resp) + qdf_mem_free(resp); + return; + } + + cdp_fc_vdev_flush(soc, vdev_id); + WMA_LOGD("%s, vdev_id: %d, un-pausing tx_ll_queue for VDEV_STOP rsp", + __func__, vdev_id); + cdp_fc_vdev_unpause(soc, vdev_id, OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); + wma_vdev_clear_pause_bit(vdev_id, PAUSE_TYPE_HOST); + qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STOPPED); + WMA_LOGD("%s: (type %d subtype %d) BSS is stopped", + __func__, iface->type, iface->sub_type); + + bcn = wma->interfaces[vdev_id].beacon; + if (bcn) { + WMA_LOGD("%s: Freeing beacon struct %pK, template memory %pK", + __func__, bcn, bcn->buf); + if (bcn->dma_mapped) + qdf_nbuf_unmap_single(wma->qdf_dev, bcn->buf, + QDF_DMA_TO_DEVICE); + qdf_nbuf_free(bcn->buf); + qdf_mem_free(bcn); + wma->interfaces[vdev_id].beacon = NULL; + } + + /* Timeout status means its WMA generated DEL BSS REQ when ADD + * BSS REQ was timed out to stop the VDEV in this case no need + * to send response to UMAC + */ + if (resp->status == QDF_STATUS_FW_MSG_TIMEDOUT) { + qdf_mem_free(resp); + WMA_LOGE("%s: DEL BSS from ADD BSS timeout do not send resp to UMAC (vdev id %x)", + __func__, vdev_id); + } else { + resp->status = QDF_STATUS_SUCCESS; + wma_send_msg_high_priority(wma, WMA_DELETE_BSS_RSP, + (void *)resp, 0); + } + + if (iface->del_staself_req && iface->is_del_sta_defered) { + iface->is_del_sta_defered = false; + WMA_LOGA("scheduling defered deletion (vdev id %x)", + vdev_id); + wma_vdev_detach(wma, iface->del_staself_req); + } +} + +void wma_send_vdev_down(tp_wma_handle wma, struct del_bss_resp *resp) +{ + uint8_t vdev_id; + struct wma_txrx_node *iface = &wma->interfaces[resp->vdev_id]; + uint32_t vdev_stop_type; + QDF_STATUS status; + + if (!resp) { + WMA_LOGE("%s req is NULL", __func__); + return; + } + + vdev_id = resp->vdev_id; + status = mlme_get_vdev_stop_type(iface->vdev, &vdev_stop_type); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s : Failed to get vdev stop type", __func__); + qdf_mem_free(resp); + return; + } + + if (vdev_stop_type != WMA_DELETE_BSS_HO_FAIL_REQ) { + if (wma_send_vdev_down_to_fw(wma, vdev_id) != + QDF_STATUS_SUCCESS) + WMA_LOGE("Failed to send vdev down cmd: vdev %d", + vdev_id); + else + wma_check_and_find_mcc_ap(wma, vdev_id); + } + wlan_vdev_mlme_sm_deliver_evt(iface->vdev, + WLAN_VDEV_SM_EV_DOWN_COMPLETE, + sizeof(*resp), resp); +} + +/** + * wma_send_vdev_down_req() - handle vdev down req + * @wma: wma handle + * @resp: pointer to vde del bss response + * + * Return: none + */ +static void wma_send_vdev_down_req(tp_wma_handle wma, + struct del_bss_resp *resp) +{ + struct wma_txrx_node *iface = &wma->interfaces[resp->vdev_id]; + + wlan_vdev_mlme_sm_deliver_evt(iface->vdev, + WLAN_VDEV_SM_EV_MLME_DOWN_REQ, + sizeof(*resp), resp); +} + +#ifdef WLAN_FEATURE_11W +static void wma_clear_iface_key(struct wma_txrx_node *iface) +{ + qdf_mem_zero(&iface->key, sizeof(iface->key)); +} +#else +static void wma_clear_iface_key(struct wma_txrx_node *iface) +{ +} +#endif + +#ifdef WLAN_FEATURE_PKT_CAPTURE +/** + * wma_set_packet_capture_mode() - set packet capture mode + * @wma: wma handle + * @vdev_id: vdev id + * @val: mode to set + * + * Return: 0 on success, errno on failure + */ +static int wma_set_packet_capture_mode( + tp_wma_handle wma_handle, + uint8_t vdev_id, + uint8_t val) +{ + int ret; + + ret = wma_cli_set_command(vdev_id, + WMI_VDEV_PARAM_PACKET_CAPTURE_MODE, + val, VDEV_CMD); + return ret; +} + +/** + * wma_handle_packet_capture_mode() - handle packet capture mode + * @wma_handle: wma handle + * @vdev_id: vdev id + * @pdev_id: pdev id + * + * Return: none + */ +static void wma_handle_packet_capture_mode( + tp_wma_handle wma_handle, + uint8_t vdev_id, uint8_t pdev_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (soc && cds_is_pktcapture_enabled() && + wma_handle->is_pktcapture_enabled && + (cds_get_pktcapture_mode() != PKT_CAPTURE_MODE_DISABLE)) { + uint8_t val = cds_get_pktcapture_mode(); + + status = wma_set_packet_capture_mode( + wma_handle, vdev_id, val); + + if (status != QDF_STATUS_SUCCESS) + WMA_LOGE("failed to set capture mode (err=%d)", + status); + else if (status == QDF_STATUS_SUCCESS) + cdp_set_packet_capture_mode(soc, pdev_id, val); + } +} +#else +static void wma_handle_packet_capture_mode( + tp_wma_handle wma_handle, + uint8_t vdev_id, uint8_t pdev_id) +{ +} +#endif + +QDF_STATUS +__wma_handle_vdev_stop_rsp(struct vdev_stop_response *resp_event) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *iface; + int status = QDF_STATUS_SUCCESS; + struct qdf_mac_addr bssid; + uint32_t vdev_stop_type; + struct del_bss_resp *vdev_stop_resp; + + if (!wma) { + WMA_LOGE("%s: wma is null", __func__); + return QDF_STATUS_E_INVAL; + } + + /* Ignore stop_response in Monitor mode */ + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + return QDF_STATUS_SUCCESS; + + iface = &wma->interfaces[resp_event->vdev_id]; + + /* vdev in stopped state, no more waiting for key */ + iface->is_waiting_for_key = false; + + /* + * Reset the rmfEnabled as there might be MGMT action frames + * sent on this vdev before the next session is established. + */ + if (iface->rmfEnabled) { + iface->rmfEnabled = 0; + WMA_LOGD(FL("Reset rmfEnabled for vdev %d"), + resp_event->vdev_id); + } + + status = mlme_get_vdev_bss_peer_mac_addr(iface->vdev, &bssid); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to get bssid", __func__); + return QDF_STATUS_E_INVAL; + } + + /* Clear key information */ + wma_clear_iface_key(iface); + status = mlme_get_vdev_stop_type(iface->vdev, &vdev_stop_type); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to get wma req msg type for vdev id %d", + __func__, resp_event->vdev_id); + return QDF_STATUS_E_INVAL; + } + + vdev_stop_resp = qdf_mem_malloc(sizeof(*vdev_stop_resp)); + if (!vdev_stop_resp) { + WMA_LOGE("%s: Failed to alloc vdev_stop_resp for vdev id %d", + __func__, resp_event->vdev_id); + return QDF_STATUS_E_NOMEM; + } + + if (vdev_stop_type == WMA_DELETE_BSS_HO_FAIL_REQ) { + status = wma_remove_peer(wma, bssid.bytes, + resp_event->vdev_id, true); + if (QDF_IS_STATUS_ERROR(status)) + goto free_params; + + vdev_stop_resp->status = status; + vdev_stop_resp->vdev_id = resp_event->vdev_id; + wma_send_vdev_down_req(wma, vdev_stop_resp); + } else if (vdev_stop_type == WMA_DELETE_BSS_REQ || + vdev_stop_type == WMA_SET_LINK_STATE) { + uint8_t type; + + /* CCA is required only for sta interface */ + if (iface->type == WMI_VDEV_TYPE_STA) + wma_get_cca_stats(wma, resp_event->vdev_id); + if (vdev_stop_type == WMA_DELETE_BSS_REQ) + type = WMA_DELETE_PEER_RSP; + else + type = WMA_SET_LINK_PEER_RSP; + + vdev_stop_resp->vdev_id = resp_event->vdev_id; + vdev_stop_resp->status = status; + status = wma_remove_bss_peer(wma, resp_event->vdev_id, + vdev_stop_resp, type); + if (status) { + WMA_LOGE("%s Del bss failed vdev:%d", __func__, + resp_event->vdev_id); + wma_send_vdev_down_req(wma, vdev_stop_resp); + return status; + } + + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_sync_delete_cmds)) + return status; + + wma_send_vdev_down_req(wma, vdev_stop_resp); + } + + return status; + +free_params: + qdf_mem_free(vdev_stop_resp); + return status; +} + +/** + * wma_handle_vdev_stop_rsp() - handle vdev stop resp + * @wma: wma handle + * @resp_event: fw resp + * + * Return: QDF_STATUS + */ +static QDF_STATUS +wma_handle_vdev_stop_rsp(tp_wma_handle wma, + struct vdev_stop_response *resp_event) +{ + struct wma_txrx_node *iface; + + iface = &wma->interfaces[resp_event->vdev_id]; + return wlan_vdev_mlme_sm_deliver_evt(iface->vdev, + WLAN_VDEV_SM_EV_STOP_RESP, + sizeof(*resp_event), resp_event); +} + +QDF_STATUS wma_vdev_stop_resp_handler(struct vdev_mlme_obj *vdev_mlme, + struct vdev_stop_response *rsp) +{ + tp_wma_handle wma; + struct wma_txrx_node *iface = NULL; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + wma_err("wma handle is NULL for VDEV_%d", rsp->vdev_id); + return status; + } + + iface = &wma->interfaces[vdev_mlme->vdev->vdev_objmgr.vdev_id]; + + if (rsp->vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: Invalid vdev_id %d from FW", + __func__, rsp->vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = wma_handle_vdev_stop_rsp(wma, rsp); + + return status; +} + +void wma_cleanup_vdev(struct wlan_objmgr_vdev *vdev) +{ + tp_wma_handle wma_handle; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint8_t vdev_id = wlan_vdev_get_id(vdev); + struct vdev_mlme_obj *vdev_mlme; + + if (!soc) { + wma_err("SOC handle is NULL"); + return; + } + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + wma_err("WMA context is invalid"); + return; + } + + if (!wma_handle->interfaces[vdev_id].vdev) { + wma_err("vdev is NULL"); + return; + } + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + wma_err("Failed to get vdev mlme obj for vdev id %d", vdev_id); + return; + } + + wma_cdp_vdev_detach(soc, wma_handle, vdev_id); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); + wma_handle->interfaces[vdev_id].vdev = NULL; + wma_handle->interfaces[vdev_id].vdev_active = false; +} + +QDF_STATUS wma_vdev_self_peer_create(struct vdev_mlme_obj *vdev_mlme) +{ + struct wlan_objmgr_peer *obj_peer; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev; + tp_wma_handle wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + wma_err("WMA context is invalid"); + return QDF_STATUS_E_FAILURE; + } + + if (wma_vdev_uses_self_peer(vdev_mlme->mgmt.generic.type, + vdev_mlme->mgmt.generic.subtype)) { + status = wma_create_peer(wma_handle, + vdev->vdev_mlme.macaddr, + WMI_PEER_TYPE_DEFAULT, + wlan_vdev_get_id(vdev), false); + if (QDF_IS_STATUS_ERROR(status)) + wma_err("Failed to create peer %d", status); + } else if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA) { + obj_peer = wma_create_objmgr_peer(wma_handle, + wlan_vdev_get_id(vdev), + vdev->vdev_mlme.macaddr, + WMI_PEER_TYPE_DEFAULT); + if (!obj_peer) { + wma_err("Failed to create obj mgr peer for self"); + status = QDF_STATUS_E_INVAL; + } + } + + return status; +} + +QDF_STATUS wma_post_vdev_create_setup(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + uint32_t cfg_val; + bool mcc_adapt_sch = false; + QDF_STATUS ret; + struct mlme_ht_capabilities_info *ht_cap_info; + u_int8_t vdev_id; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct wlan_mlme_qos *qos_aggr; + struct vdev_mlme_obj *vdev_mlme; + tp_wma_handle wma_handle; + uint8_t amsdu_val; + + if (!mac) { + WMA_LOGE("%s: Failed to get mac", __func__); + return QDF_STATUS_E_FAILURE; + } + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + WMA_LOGE("%s: WMA context is invalid", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!soc) { + WMA_LOGE("%s: SOC context is invalid", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (wlan_objmgr_vdev_try_get_ref(vdev, WLAN_LEGACY_WMA_ID) != + QDF_STATUS_SUCCESS) + return QDF_STATUS_E_FAILURE; + + vdev_id = wlan_vdev_get_id(vdev); + wma_handle->interfaces[vdev_id].vdev = vdev; + wma_handle->interfaces[vdev_id].vdev_active = true; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + WMA_LOGE("%s: Failed to get vdev mlme obj!", __func__); + goto end; + } + + wma_vdev_update_pause_bitmap(vdev_id, 0); + + wma_handle->interfaces[vdev_id].type = + vdev_mlme->mgmt.generic.type; + wma_handle->interfaces[vdev_id].sub_type = + vdev_mlme->mgmt.generic.subtype; + + qos_aggr = &mac->mlme_cfg->qos_mlme_params; + status = wma_set_tx_rx_aggr_size(vdev_id, qos_aggr->tx_aggregation_size, + qos_aggr->rx_aggregation_size, + WMI_VDEV_CUSTOM_AGGR_TYPE_AMPDU); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set aggregation sizes(status = %d)", + status); + + status = wlan_mlme_get_max_amsdu_num(wma_handle->psoc, &amsdu_val); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("failed to get amsdu aggr.size %d", status); + } else { + status = wma_set_tx_rx_aggr_size(vdev_id, amsdu_val, + amsdu_val, + WMI_VDEV_CUSTOM_AGGR_TYPE_AMSDU); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("failed to set amsdu aggr.size %d", status); + } + } + + if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA) { + status = wma_set_tx_rx_aggr_size_per_ac( + wma_handle, vdev_id, + qos_aggr, + WMI_VDEV_CUSTOM_AGGR_TYPE_AMPDU); + + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set aggr size per ac(status = %d)", + status); + + wma_set_sta_keep_alive( + wma_handle, vdev_id, + SIR_KEEP_ALIVE_NULL_PKT, + mac->mlme_cfg->sta.sta_keep_alive_period, + NULL, NULL, NULL); + + /* offload STA SA query related params to fwr */ + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_sta_pmf_offload)) { + wma_set_sta_sa_query_param(wma_handle, vdev_id); + } + + status = wma_set_sw_retry_threshold( + vdev_id, + qos_aggr->tx_aggr_sw_retry_threshold, + WMI_PDEV_PARAM_AGG_SW_RETRY_TH); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set sw retry threshold (status = %d)", + status); + + status = wma_set_sw_retry_threshold( + vdev_id, + qos_aggr->tx_non_aggr_sw_retry_threshold, + WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set sw retry threshold tx non aggr(status = %d)", + status); + + wma_handle_packet_capture_mode(wma_handle, vdev_id, + WMI_PDEV_ID_SOC); + + status = wma_set_sw_retry_threshold_per_ac(wma_handle, vdev_id, + qos_aggr); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set sw retry threshold per ac(status = %d)", + status); + } + + WMA_LOGD("Setting WMI_VDEV_PARAM_DISCONNECT_TH: %d", + mac->mlme_cfg->gen.dropped_pkt_disconnect_thresh); + status = wma_vdev_set_param( + wma_handle->wmi_handle, vdev_id, + WMI_VDEV_PARAM_DISCONNECT_TH, + mac->mlme_cfg->gen.dropped_pkt_disconnect_thresh); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set DISCONNECT_TH(status = %d)", status); + + status = wma_vdev_set_param( + wma_handle->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_MCC_RTSCTS_PROTECTION_ENABLE, + mac->roam.configParam.mcc_rts_cts_prot_enable); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set MCC_RTSCTS_PROTECTION_ENABLE(status = %d)", + status); + + status = wma_vdev_set_param( + wma_handle->wmi_handle, vdev_id, + WMI_VDEV_PARAM_MCC_BROADCAST_PROBE_ENABLE, + mac->roam.configParam.mcc_bcast_prob_resp_enable); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set MCC_BROADCAST_PROBE_ENABLE(status = %d)", + status); + + if (wlan_mlme_get_rts_threshold(mac->psoc, + &cfg_val) == + QDF_STATUS_SUCCESS) { + status = wma_vdev_set_param(wma_handle->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_RTS_THRESHOLD, + cfg_val); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set RTS_THRESHOLD(status = %d)", + status); + } else { + WMA_LOGE("Fail to get val for rts threshold, leave unchanged"); + } + + if (wlan_mlme_get_frag_threshold(mac->psoc, + &cfg_val) == + QDF_STATUS_SUCCESS) { + wma_vdev_set_param(wma_handle->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + cfg_val); + } else { + WMA_LOGE("Fail to get val for frag threshold, leave unchanged"); + } + + ht_cap_info = &mac->mlme_cfg->ht_caps.ht_cap_info; + status = wma_vdev_set_param(wma_handle->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_TX_STBC, + ht_cap_info->tx_stbc); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set TX_STBC(status = %d)", status); + + wma_set_vdev_mgmt_rate(wma_handle, vdev_id); + if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) + wma_set_he_txbf_cfg(mac, vdev_id); + + /* Initialize roaming offload state */ + if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA && + vdev_mlme->mgmt.generic.subtype == 0) { + /* Pass down enable/disable bcast probe rsp to FW */ + ret = wma_vdev_set_param( + wma_handle->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE, + mac->mlme_cfg->oce.enable_bcast_probe_rsp); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to set WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE"); + + /* Pass down the FILS max channel guard time to FW */ + ret = wma_vdev_set_param( + wma_handle->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME, + mac->mlme_cfg->sta.fils_max_chan_guard_time); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to set WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME"); + + /* Pass down the Probe Request tx delay(in ms) to FW */ + ret = wma_vdev_set_param( + wma_handle->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_PROBE_DELAY, + PROBE_REQ_TX_DELAY); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to set WMI_VDEV_PARAM_PROBE_DELAY"); + + /* Pass down the probe request tx time gap(in ms) to FW */ + ret = wma_vdev_set_param( + wma_handle->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_REPEAT_PROBE_TIME, + PROBE_REQ_TX_TIME_GAP); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to set WMI_VDEV_PARAM_REPEAT_PROBE_TIME"); + } + + if ((vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA || + vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_AP) && + vdev_mlme->mgmt.generic.subtype == 0) { + wma_vdev_set_param(wma_handle->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_ENABLE_DISABLE_OCE_FEATURES, + mac->mlme_cfg->oce.feature_bitmap); + } + + /* Initialize BMISS parameters */ + if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA && + vdev_mlme->mgmt.generic.subtype == 0) + wma_roam_scan_bmiss_cnt(wma_handle, + mac->mlme_cfg->lfr.roam_bmiss_first_bcnt, + mac->mlme_cfg->lfr.roam_bmiss_final_bcnt, + vdev_id); + + if (policy_mgr_get_dynamic_mcc_adaptive_sch(mac->psoc, + &mcc_adapt_sch) == + QDF_STATUS_SUCCESS) { + WMA_LOGD("%s: setting ini value for WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED: %d", + __func__, mcc_adapt_sch); + ret = + wma_set_enable_disable_mcc_adaptive_scheduler(mcc_adapt_sch); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed to set WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED"); + } + } else { + WMA_LOGE("Failed to get value for WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED, leaving unchanged"); + } + + if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA && + ucfg_pmo_is_apf_enabled(wma_handle->psoc)) { + ret = wma_config_active_apf_mode(wma_handle, + vdev_id); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to configure active APF mode"); + } + + cdp_data_tx_cb_set(soc, vdev_id, + wma_data_tx_ack_comp_hdlr, + wma_handle); + + return QDF_STATUS_SUCCESS; + +end: + wma_cleanup_vdev(vdev); + return QDF_STATUS_E_FAILURE; +} + +enum mlme_bcn_tx_rate_code wma_get_bcn_rate_code(uint16_t rate) +{ + /* rate in multiples of 100 Kbps */ + switch (rate) { + case WMA_BEACON_TX_RATE_1_M: + return MLME_BCN_TX_RATE_CODE_1_M; + case WMA_BEACON_TX_RATE_2_M: + return MLME_BCN_TX_RATE_CODE_2_M; + case WMA_BEACON_TX_RATE_5_5_M: + return MLME_BCN_TX_RATE_CODE_5_5_M; + case WMA_BEACON_TX_RATE_11_M: + return MLME_BCN_TX_RATE_CODE_11M; + case WMA_BEACON_TX_RATE_6_M: + return MLME_BCN_TX_RATE_CODE_6_M; + case WMA_BEACON_TX_RATE_9_M: + return MLME_BCN_TX_RATE_CODE_9_M; + case WMA_BEACON_TX_RATE_12_M: + return MLME_BCN_TX_RATE_CODE_12_M; + case WMA_BEACON_TX_RATE_18_M: + return MLME_BCN_TX_RATE_CODE_18_M; + case WMA_BEACON_TX_RATE_24_M: + return MLME_BCN_TX_RATE_CODE_24_M; + case WMA_BEACON_TX_RATE_36_M: + return MLME_BCN_TX_RATE_CODE_36_M; + case WMA_BEACON_TX_RATE_48_M: + return MLME_BCN_TX_RATE_CODE_48_M; + case WMA_BEACON_TX_RATE_54_M: + return MLME_BCN_TX_RATE_CODE_54_M; + default: + return MLME_BCN_TX_RATE_CODE_1_M; + } +} + +QDF_STATUS wma_vdev_pre_start(uint8_t vdev_id, bool restart) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *intr; + struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + struct wlan_mlme_nss_chains *ini_cfg; + struct vdev_mlme_obj *mlme_obj; + struct wlan_objmgr_vdev *vdev; + struct wlan_channel *des_chan; + + if (!wma) { + wma_err("Invalid wma handle"); + return QDF_STATUS_E_FAILURE; + } + if (!mac_ctx) { + wma_err("Invalid mac context"); + return QDF_STATUS_E_FAILURE; + } + intr = wma->interfaces; + if (!intr) { + wma_err("Invalid interface"); + return QDF_STATUS_E_FAILURE; + } + if (vdev_id >= WLAN_MAX_VDEVS) { + wma_err("Invalid vdev id"); + return QDF_STATUS_E_INVAL; + } + vdev = intr[vdev_id].vdev; + if (!vdev) { + wma_err("Invalid vdev"); + return QDF_STATUS_E_FAILURE; + } + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + wma_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + des_chan = vdev->vdev_mlme.des_chan; + + ini_cfg = mlme_get_ini_vdev_config(vdev); + if (!ini_cfg) { + wma_err("nss chain ini config NULL"); + return QDF_STATUS_E_FAILURE; + } + + intr[vdev_id].config.gtx_info.gtxRTMask[0] = + CFG_TGT_DEFAULT_GTX_HT_MASK; + intr[vdev_id].config.gtx_info.gtxRTMask[1] = + CFG_TGT_DEFAULT_GTX_VHT_MASK; + + intr[vdev_id].config.gtx_info.gtxUsrcfg = + mac_ctx->mlme_cfg->sta.tgt_gtx_usr_cfg; + + intr[vdev_id].config.gtx_info.gtxPERThreshold = + CFG_TGT_DEFAULT_GTX_PER_THRESHOLD; + intr[vdev_id].config.gtx_info.gtxPERMargin = + CFG_TGT_DEFAULT_GTX_PER_MARGIN; + intr[vdev_id].config.gtx_info.gtxTPCstep = + CFG_TGT_DEFAULT_GTX_TPC_STEP; + intr[vdev_id].config.gtx_info.gtxTPCMin = + CFG_TGT_DEFAULT_GTX_TPC_MIN; + intr[vdev_id].config.gtx_info.gtxBWMask = + CFG_TGT_DEFAULT_GTX_BW_MASK; + intr[vdev_id].mhz = des_chan->ch_freq; + intr[vdev_id].chan_width = des_chan->ch_width; + intr[vdev_id].ch_freq = des_chan->ch_freq; + intr[vdev_id].ch_flagext = des_chan->ch_flagext; + + /* + * If the channel has DFS set, flip on radar reporting. + * + * It may be that this should only be done for IBSS/hostap operation + * as this flag may be interpreted (at some point in the future) + * by the firmware as "oh, and please do radar DETECTION." + * + * If that is ever the case we would insert the decision whether to + * enable the firmware flag here. + */ + if (QDF_GLOBAL_MONITOR_MODE != cds_get_conparam() && + utils_is_dfs_chan_for_freq(wma->pdev, + wlan_reg_legacy_chan_to_freq(wma->pdev, + des_chan->ch_ieee))) + mlme_obj->mgmt.generic.disable_hw_ack = true; + + if (mlme_obj->mgmt.rate_info.bcn_tx_rate) { + wma_debug("beacon tx rate [%u * 100 Kbps]", + mlme_obj->mgmt.rate_info.bcn_tx_rate); + /* + * beacon_tx_rate is in multiples of 100 Kbps. + * Convert the data rate to hw rate code. + */ + mlme_obj->mgmt.rate_info.bcn_tx_rate = + wma_get_bcn_rate_code(mlme_obj->mgmt.rate_info.bcn_tx_rate); + } + + if (!restart) { + WMA_LOGD("%s, vdev_id: %d, unpausing tx_ll_queue at VDEV_START", + __func__, vdev_id); + + cdp_fc_vdev_unpause(cds_get_context(QDF_MODULE_ID_SOC), + vdev_id, 0xffffffff, 0); + wma_vdev_update_pause_bitmap(vdev_id, 0); + } + + /* Send the dynamic nss chain params before vdev start to fw */ + if (wma->dynamic_nss_chains_support) + wma_vdev_nss_chain_params_send(vdev_id, ini_cfg); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_peer_assoc_conf_handler() - peer assoc conf handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: buffer length + * + * Return: 0 for success or error code + */ +int wma_peer_assoc_conf_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_PEER_ASSOC_CONF_EVENTID_param_tlvs *param_buf; + wmi_peer_assoc_conf_event_fixed_param *event; + struct wma_target_req *req_msg; + uint8_t macaddr[QDF_MAC_ADDR_SIZE]; + int status = 0; + + param_buf = (WMI_PEER_ASSOC_CONF_EVENTID_param_tlvs *) cmd_param_info; + if (!param_buf) { + WMA_LOGE("Invalid peer assoc conf event buffer"); + return -EINVAL; + } + + event = param_buf->fixed_param; + if (!event) { + WMA_LOGE("Invalid peer assoc conf event buffer"); + return -EINVAL; + } + + WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->peer_macaddr, macaddr); + WMA_LOGD(FL("peer assoc conf for vdev:%d mac="QDF_MAC_ADDR_FMT), + event->vdev_id, QDF_MAC_ADDR_REF(macaddr)); + + req_msg = wma_find_req(wma, event->vdev_id, + WMA_PEER_ASSOC_CNF_START); + + if (!req_msg) { + WMA_LOGE(FL("Failed to lookup request message for vdev %d"), + event->vdev_id); + return -EINVAL; + } + + qdf_mc_timer_stop(&req_msg->event_timeout); + + if (req_msg->msg_type == WMA_ADD_STA_REQ) { + tpAddStaParams params = (tpAddStaParams)req_msg->user_data; + + if (!params) { + WMA_LOGE(FL("add STA params is NULL for vdev %d"), + event->vdev_id); + status = -EINVAL; + goto free_req_msg; + } + + /* peer assoc conf event means the cmd succeeds */ + params->status = event->status; + WMA_LOGD(FL("Send ADD_STA_RSP: statype %d vdev_id %d aid %d bssid "QDF_MAC_ADDR_FMT" status %d"), + params->staType, params->smesessionId, + params->assocId, QDF_MAC_ADDR_REF(params->bssId), + params->status); + wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, + (void *)params, 0); + } else if (req_msg->msg_type == WMA_ADD_BSS_REQ) { + wma_send_add_bss_resp(wma, event->vdev_id, event->status); + } else { + WMA_LOGE(FL("Unhandled request message type: %d"), + req_msg->msg_type); + } + +free_req_msg: + qdf_mc_timer_destroy(&req_msg->event_timeout); + qdf_mem_free(req_msg); + + return status; +} + +/** + * wma_peer_delete_handler() - peer delete response handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: buffer length + * + * Return: 0 for success or error code + */ +int wma_peer_delete_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_PEER_DELETE_RESP_EVENTID_param_tlvs *param_buf; + wmi_peer_delete_cmd_fixed_param *event; + struct wma_target_req *req_msg; + tDeleteStaParams *del_sta; + uint8_t macaddr[QDF_MAC_ADDR_SIZE]; + int status = 0; + + param_buf = (WMI_PEER_DELETE_RESP_EVENTID_param_tlvs *)cmd_param_info; + if (!param_buf) { + WMA_LOGE("Invalid vdev delete event buffer"); + return -EINVAL; + } + + event = (wmi_peer_delete_cmd_fixed_param *)param_buf->fixed_param; + if (!event) { + WMA_LOGE("Invalid vdev delete event buffer"); + return -EINVAL; + } + + WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->peer_macaddr, macaddr); + WMA_LOGD(FL("Peer Delete Response, vdev %d Peer "QDF_MAC_ADDR_FMT), + event->vdev_id, QDF_MAC_ADDR_REF(macaddr)); + wlan_roam_debug_log(event->vdev_id, DEBUG_PEER_DELETE_RESP, + DEBUG_INVALID_PEER_ID, macaddr, NULL, 0, 0); + req_msg = wma_find_remove_req_msgtype(wma, event->vdev_id, + WMA_DELETE_STA_REQ); + if (!req_msg) { + WMA_LOGD("Peer Delete response is not handled"); + return -EINVAL; + } + + wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock); + + /* Cleanup timeout handler */ + qdf_mc_timer_stop(&req_msg->event_timeout); + qdf_mc_timer_destroy(&req_msg->event_timeout); + + if (req_msg->type == WMA_DELETE_STA_RSP_START) { + del_sta = req_msg->user_data; + if (del_sta->respReqd) { + WMA_LOGD(FL("Sending peer del rsp to umac")); + wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP, + (void *)del_sta, QDF_STATUS_SUCCESS); + } else { + qdf_mem_free(del_sta); + } + } else if (req_msg->type == WMA_DEL_P2P_SELF_STA_RSP_START) { + struct del_sta_self_rsp_params *data; + + data = (struct del_sta_self_rsp_params *)req_msg->user_data; + WMA_LOGD(FL("Calling vdev detach handler")); + status = wma_handle_vdev_detach(wma, data->self_sta_param); + if (QDF_IS_STATUS_ERROR(status)) { + data->self_sta_param->status = status; + wma_send_vdev_del_resp(data->self_sta_param); + } + qdf_mem_free(data); + } else if (req_msg->type == WMA_SET_LINK_PEER_RSP || + req_msg->type == WMA_DELETE_PEER_RSP) { + wma_send_vdev_down_req(wma, req_msg->user_data); + } + qdf_mem_free(req_msg); + + return status; +} + +static +void wma_trigger_recovery_assert_on_fw_timeout(uint16_t wma_msg, + enum qdf_hang_reason reason) +{ + WMA_LOGE("%s timed out, triggering recovery", + mac_trace_get_wma_msg_string(wma_msg)); + qdf_trigger_self_recovery(NULL, reason); +} + +static inline bool wma_crash_on_fw_timeout(bool crash_enabled) +{ + /* Discard FW timeouts and dont crash during SSR */ + if (cds_is_driver_recovering()) + return false; + + /* Firmware is down send failure response */ + if (cds_is_fw_down()) + return false; + + if (cds_is_driver_unloading()) + return false; + + return crash_enabled; +} + +/** + * wma_hold_req_timer() - wma hold request timeout function + * @data: target request params + * + * Return: none + */ +void wma_hold_req_timer(void *data) +{ + tp_wma_handle wma; + struct wma_target_req *tgt_req = (struct wma_target_req *)data; + QDF_STATUS status; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE(FL("Failed to get wma")); + return; + } + + status = wma_find_req_on_timer_expiry(wma, tgt_req); + + if (QDF_IS_STATUS_ERROR(status)) { + /* + * if find request failed, then firmware rsp should have + * consumed the buffer. Do not free. + */ + WMA_LOGD(FL("Failed to lookup request message - %pK"), + tgt_req); + return; + } + WMA_LOGA(FL("request %d is timed out for vdev_id - %d"), + tgt_req->msg_type, tgt_req->vdev_id); + + if (tgt_req->msg_type == WMA_ADD_STA_REQ) { + tpAddStaParams params = (tpAddStaParams) tgt_req->user_data; + + params->status = QDF_STATUS_E_TIMEOUT; + WMA_LOGA(FL("WMA_ADD_STA_REQ timed out")); + WMA_LOGD(FL("Sending add sta rsp to umac (mac:"QDF_MAC_ADDR_FMT", status:%d)"), + QDF_MAC_ADDR_REF(params->staMac), params->status); + if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) + wma_trigger_recovery_assert_on_fw_timeout( + WMA_ADD_STA_REQ, + QDF_AP_STA_CONNECT_REQ_TIMEOUT); + wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, + (void *)params, 0); + } else if (tgt_req->msg_type == WMA_ADD_BSS_REQ) { + + WMA_LOGA(FL("WMA_ADD_BSS_REQ timed out")); + WMA_LOGD(FL("Sending add bss rsp to umac (vdev %d, status:%d)"), + tgt_req->vdev_id, QDF_STATUS_E_TIMEOUT); + + if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) + wma_trigger_recovery_assert_on_fw_timeout( + WMA_ADD_BSS_REQ, + QDF_STA_AP_CONNECT_REQ_TIMEOUT); + + wma_send_add_bss_resp(wma, tgt_req->vdev_id, + QDF_STATUS_E_TIMEOUT); + } else if ((tgt_req->msg_type == WMA_DELETE_STA_REQ) && + (tgt_req->type == WMA_DELETE_STA_RSP_START)) { + tpDeleteStaParams params = + (tpDeleteStaParams) tgt_req->user_data; + params->status = QDF_STATUS_E_TIMEOUT; + WMA_LOGE(FL("WMA_DEL_STA_REQ timed out")); + WMA_LOGE(FL("Sending del sta rsp to umac (mac:"QDF_MAC_ADDR_FMT", status:%d)"), + QDF_MAC_ADDR_REF(params->staMac), params->status); + + if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) + wma_trigger_recovery_assert_on_fw_timeout( + WMA_DELETE_STA_REQ, + QDF_PEER_DELETION_TIMEDOUT); + wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP, + (void *)params, 0); + } else if ((tgt_req->msg_type == WMA_DELETE_STA_REQ) && + (tgt_req->type == WMA_DEL_P2P_SELF_STA_RSP_START)) { + struct del_sta_self_rsp_params *del_sta; + + del_sta = (struct del_sta_self_rsp_params *)tgt_req->user_data; + + del_sta->self_sta_param->status = QDF_STATUS_E_TIMEOUT; + WMA_LOGA(FL("wma delete sta p2p request timed out")); + + if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) + wma_trigger_recovery_assert_on_fw_timeout( + WMA_DELETE_STA_REQ, + QDF_PEER_DELETION_TIMEDOUT); + status = wma_handle_vdev_detach(wma, del_sta->self_sta_param); + if (QDF_IS_STATUS_ERROR(status)) { + del_sta->self_sta_param->status = status; + wma_send_vdev_del_resp(del_sta->self_sta_param); + } + qdf_mem_free(tgt_req->user_data); + } else if ((tgt_req->msg_type == WMA_DELETE_STA_REQ) && + (tgt_req->type == WMA_SET_LINK_PEER_RSP || + tgt_req->type == WMA_DELETE_PEER_RSP)) { + struct del_bss_resp *params = + (struct del_bss_resp *)tgt_req->user_data; + + params->status = QDF_STATUS_E_TIMEOUT; + WMA_LOGE(FL("wma delete peer for del bss req timed out")); + + if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) + wma_trigger_recovery_assert_on_fw_timeout( + WMA_DELETE_STA_REQ, + QDF_PEER_DELETION_TIMEDOUT); + wma_send_vdev_down_req(wma, params); + } else if ((tgt_req->msg_type == SIR_HAL_PDEV_SET_HW_MODE) && + (tgt_req->type == WMA_PDEV_SET_HW_MODE_RESP)) { + struct sir_set_hw_mode_resp *params = + qdf_mem_malloc(sizeof(*params)); + + WMA_LOGE(FL("set hw mode req timed out")); + + if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) + wma_trigger_recovery_assert_on_fw_timeout( + SIR_HAL_PDEV_SET_HW_MODE, + QDF_MAC_HW_MODE_CHANGE_TIMEOUT); + if (!params) { + WMA_LOGE(FL("Failed to allocate memory for params")); + goto timer_destroy; + } + params->status = SET_HW_MODE_STATUS_ECANCELED; + params->cfgd_hw_mode_index = 0; + params->num_vdev_mac_entries = 0; + wma_send_msg_high_priority(wma, SIR_HAL_PDEV_SET_HW_MODE_RESP, + params, 0); + } else if ((tgt_req->msg_type == SIR_HAL_PDEV_DUAL_MAC_CFG_REQ) && + (tgt_req->type == WMA_PDEV_MAC_CFG_RESP)) { + struct sir_dual_mac_config_resp *resp = + qdf_mem_malloc(sizeof(*resp)); + + WMA_LOGE(FL("set dual mac config timeout")); + if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) + wma_trigger_recovery_assert_on_fw_timeout( + SIR_HAL_PDEV_DUAL_MAC_CFG_REQ, + QDF_MAC_HW_MODE_CONFIG_TIMEOUT); + if (!resp) { + WMA_LOGE(FL("Failed to allocate memory for resp")); + goto timer_destroy; + } + + resp->status = SET_HW_MODE_STATUS_ECANCELED; + wma_send_msg_high_priority(wma, SIR_HAL_PDEV_MAC_CFG_RESP, + resp, 0); + } else { + WMA_LOGE(FL("Unhandled timeout for msg_type:%d and type:%d"), + tgt_req->msg_type, tgt_req->type); + QDF_BUG(0); + } + +timer_destroy: + qdf_mc_timer_destroy(&tgt_req->event_timeout); + qdf_mem_free(tgt_req); +} + +/** + * wma_fill_hold_req() - fill wma request + * @wma: wma handle + * @msg_type: message type + * @type: request type + * @params: request params + * @timeout: timeout value + * + * Return: wma_target_req ptr + */ +struct wma_target_req *wma_fill_hold_req(tp_wma_handle wma, + uint8_t vdev_id, + uint32_t msg_type, uint8_t type, + void *params, uint32_t timeout) +{ + struct wma_target_req *req; + QDF_STATUS status; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return NULL; + + WMA_LOGD(FL("vdev_id %d msg %d type %d"), vdev_id, msg_type, type); + qdf_spin_lock_bh(&wma->wma_hold_req_q_lock); + req->vdev_id = vdev_id; + req->msg_type = msg_type; + req->type = type; + req->user_data = params; + status = qdf_list_insert_back(&wma->wma_hold_req_queue, &req->node); + if (QDF_STATUS_SUCCESS != status) { + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + WMA_LOGE(FL("Failed add request in queue")); + qdf_mem_free(req); + return NULL; + } + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + qdf_mc_timer_init(&req->event_timeout, QDF_TIMER_TYPE_SW, + wma_hold_req_timer, req); + qdf_mc_timer_start(&req->event_timeout, timeout); + return req; +} + +/** + * wma_remove_req() - remove request + * @wma: wma handle + * @vdev_id: vdev id + * @type: type + * + * Return: none + */ +void wma_remove_req(tp_wma_handle wma, uint8_t vdev_id, + uint8_t type) +{ + struct wma_target_req *req_msg; + + WMA_LOGD(FL("Remove req for vdev: %d type: %d"), vdev_id, type); + req_msg = wma_find_req(wma, vdev_id, type); + if (!req_msg) { + WMA_LOGE(FL("target req not found for vdev: %d type: %d"), + vdev_id, type); + return; + } + + qdf_mc_timer_stop(&req_msg->event_timeout); + qdf_mc_timer_destroy(&req_msg->event_timeout); + qdf_mem_free(req_msg); +} + +/** + * wma_vdev_set_bss_params() - BSS set params functions + * @wma: wma handle + * @vdev_id: vdev id + * @beaconInterval: beacon interval + * @dtimPeriod: DTIM period + * @shortSlotTimeSupported: short slot time + * @llbCoexist: llbCoexist + * @maxTxPower: max tx power + * + * Return: none + */ +static void +wma_vdev_set_bss_params(tp_wma_handle wma, int vdev_id, + tSirMacBeaconInterval beaconInterval, + uint8_t dtimPeriod, uint8_t shortSlotTimeSupported, + uint8_t llbCoexist, int8_t maxTxPower) +{ + QDF_STATUS ret; + uint32_t slot_time; + struct wma_txrx_node *intr = wma->interfaces; + + /* Beacon Interval setting */ + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_BEACON_INTERVAL, + beaconInterval); + + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("failed to set WMI_VDEV_PARAM_BEACON_INTERVAL"); + ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, vdev_id, + &intr[vdev_id].config.gtx_info); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("failed to set WMI_VDEV_PARAM_DTIM_PERIOD"); + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_DTIM_PERIOD, + dtimPeriod); + intr[vdev_id].dtimPeriod = dtimPeriod; + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("failed to set WMI_VDEV_PARAM_DTIM_PERIOD"); + + if (!maxTxPower) + WMA_LOGW("Setting Tx power limit to 0"); + wma_debug("Set max Tx power to %d", maxTxPower); + if (maxTxPower != INVALID_TXPOWER) { + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_TX_PWRLIMIT, + maxTxPower); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("failed to set WMI_VDEV_PARAM_TX_PWRLIMIT"); + else + mlme_set_max_reg_power(intr[vdev_id].vdev, maxTxPower); + } else { + wma_err("Invalid max Tx power"); + } + /* Slot time */ + if (shortSlotTimeSupported) + slot_time = WMI_VDEV_SLOT_TIME_SHORT; + else + slot_time = WMI_VDEV_SLOT_TIME_LONG; + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_SLOT_TIME, + slot_time); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("failed to set WMI_VDEV_PARAM_SLOT_TIME"); + + /* Initialize protection mode in case of coexistence */ + wma_update_protection_mode(wma, vdev_id, llbCoexist); + +} + +#ifdef WLAN_FEATURE_11W +static void wma_set_mgmt_frame_protection(tp_wma_handle wma) +{ + struct pdev_params param = {0}; + QDF_STATUS ret; + + /* + * when 802.11w PMF is enabled for hw encr/decr + * use hw MFP Qos bits 0x10 + */ + param.param_id = WMI_PDEV_PARAM_PMF_QOS; + param.param_value = true; + ret = wmi_unified_pdev_param_send(wma->wmi_handle, + ¶m, WMA_WILDCARD_PDEV_ID); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("%s: Failed to set QOS MFP/PMF (%d)", + __func__, ret); + } else { + WMA_LOGD("%s: QOS MFP/PMF set", __func__); + } +} + +/** + * wma_set_peer_pmf_status() - Get the peer and update PMF capability of it + * @wma: wma handle + * @peer_mac: peer mac addr + * @is_pmf_enabled: Carries the status whether PMF is enabled or not + * + * Return: QDF_STATUS + */ +static QDF_STATUS +wma_set_peer_pmf_status(tp_wma_handle wma, uint8_t *peer_mac, + bool is_pmf_enabled) +{ + struct wlan_objmgr_peer *peer; + + peer = wlan_objmgr_get_peer(wma->psoc, + wlan_objmgr_pdev_get_pdev_id(wma->pdev), + peer_mac, WLAN_LEGACY_WMA_ID); + if (!peer) { + WMA_LOGE("Peer of peer_mac "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(peer_mac)); + return QDF_STATUS_E_INVAL; + } + mlme_set_peer_pmf_status(peer, is_pmf_enabled); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + WMA_LOGD("set is_pmf_enabled %d for "QDF_MAC_ADDR_FMT, + is_pmf_enabled, QDF_MAC_ADDR_REF(peer_mac)); + + return QDF_STATUS_SUCCESS; +} +#else +static inline void wma_set_mgmt_frame_protection(tp_wma_handle wma) +{ +} + +static QDF_STATUS +wma_set_peer_pmf_status(tp_wma_handle wma, uint8_t *peer_mac, + bool is_pmf_enabled) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_11W */ + +QDF_STATUS wma_pre_vdev_start_setup(uint8_t vdev_id, + struct bss_params *add_bss) +{ + QDF_STATUS status; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct wma_txrx_node *iface; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct vdev_mlme_obj *mlme_obj; + uint8_t *mac_addr; + + if (!soc) { + wma_err("Invalid soc"); + return QDF_STATUS_E_FAILURE; + } + if (!wma) { + wma_err("Invalid wma handle"); + return QDF_STATUS_E_FAILURE; + } + iface = &wma->interfaces[vdev_id]; + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (!mlme_obj) { + wma_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + wma_set_bss_rate_flags(wma, vdev_id, add_bss); + if (wlan_vdev_mlme_get_opmode(iface->vdev) == QDF_NDI_MODE || + wlan_vdev_mlme_get_opmode(iface->vdev) == QDF_IBSS_MODE) + mac_addr = mlme_obj->mgmt.generic.bssid; + else + mac_addr = wlan_vdev_mlme_get_macaddr(iface->vdev); + + status = wma_create_peer(wma, mac_addr, + WMI_PEER_TYPE_DEFAULT, vdev_id, false); + if (status != QDF_STATUS_SUCCESS) { + wma_err("Failed to create peer"); + return status; + } + + if (!cdp_find_peer_exist(soc, OL_TXRX_PDEV_ID, mac_addr)) { + wma_err("Failed to find peer "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac_addr)); + return QDF_STATUS_E_FAILURE; + } + + iface->rmfEnabled = add_bss->rmfEnabled; + if (add_bss->rmfEnabled) + wma_set_mgmt_frame_protection(wma); + + if (wlan_vdev_mlme_get_opmode(iface->vdev) == QDF_IBSS_MODE) { + tSetBssKeyParams key_info; + /* clear leftover ibss keys on bss peer */ + wma_debug("ibss bss key clearing"); + qdf_mem_zero(&key_info, sizeof(key_info)); + key_info.vdev_id = vdev_id; + key_info.numKeys = SIR_MAC_MAX_NUM_OF_DEFAULT_KEYS; + qdf_mem_copy(&wma->ibsskey_info, &key_info, + sizeof(tSetBssKeyParams)); + /* + * If IBSS Power Save is supported by firmware + * set the IBSS power save params to firmware. + */ + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_ibss_pwrsave)) { + status = wma_set_ibss_pwrsave_params(wma, vdev_id); + } + } + return status; +} + +QDF_STATUS wma_post_vdev_start_setup(uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *intr; + struct vdev_mlme_obj *mlme_obj; + struct wlan_objmgr_vdev *vdev; + uint8_t bss_power; + + if (!wma) { + wma_err("Invalid wma handle"); + return QDF_STATUS_E_FAILURE; + } + intr = &wma->interfaces[vdev_id]; + if (!intr) { + wma_err("Invalid interface"); + return QDF_STATUS_E_FAILURE; + } + vdev = intr->vdev; + if (!vdev) { + wma_err("vdev is NULL"); + return QDF_STATUS_E_FAILURE; + } + if (wlan_vdev_mlme_get_opmode(vdev) == QDF_NDI_MODE || + wlan_vdev_mlme_get_opmode(vdev) == QDF_IBSS_MODE) { + /* Initialize protection mode to no protection */ + wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_PROTECTION_MODE, + IEEE80211_PROT_NONE); + return status; + } + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + wma_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + /* Fill bss_chan after vdev start */ + qdf_mem_copy(vdev->vdev_mlme.bss_chan, + vdev->vdev_mlme.des_chan, + sizeof(struct wlan_channel)); + + bss_power = wlan_reg_get_channel_reg_power_for_freq(wma->pdev, + vdev->vdev_mlme.bss_chan->ch_freq); + wma_vdev_set_bss_params(wma, vdev_id, + mlme_obj->proto.generic.beacon_interval, + mlme_obj->proto.generic.dtim_period, + mlme_obj->proto.generic.slot_time, + mlme_obj->proto.generic.protection_mode, + bss_power); + + wma_vdev_set_he_bss_params(wma, vdev_id, + &mlme_obj->proto.he_ops_info); + + return status; +} + +static QDF_STATUS wma_update_iface_params(tp_wma_handle wma, + struct bss_params *add_bss) +{ + struct wma_txrx_node *iface; + uint8_t vdev_id; + + vdev_id = add_bss->staContext.smesessionId; + iface = &wma->interfaces[vdev_id]; + wma_set_bss_rate_flags(wma, vdev_id, add_bss); + + if (iface->addBssStaContext) + qdf_mem_free(iface->addBssStaContext); + iface->addBssStaContext = qdf_mem_malloc(sizeof(tAddStaParams)); + if (!iface->addBssStaContext) + return QDF_STATUS_E_RESOURCES; + *iface->addBssStaContext = add_bss->staContext; + if (iface->staKeyParams) { + qdf_mem_free(iface->staKeyParams); + iface->staKeyParams = NULL; + } + if (add_bss->extSetStaKeyParamValid) { + iface->staKeyParams = + qdf_mem_malloc(sizeof(tSetStaKeyParams)); + if (!iface->staKeyParams) { + qdf_mem_free(iface->addBssStaContext); + iface->addBssStaContext = NULL; + return QDF_STATUS_E_RESOURCES; + } + *iface->staKeyParams = add_bss->extSetStaKeyParam; + } + /* Save parameters later needed by WMA_ADD_STA_REQ */ + iface->rmfEnabled = add_bss->rmfEnabled; + if (add_bss->rmfEnabled) + wma_set_peer_pmf_status(wma, add_bss->bssId, true); + iface->beaconInterval = add_bss->beaconInterval; + iface->llbCoexist = add_bss->llbCoexist; + iface->shortSlotTimeSupported = add_bss->shortSlotTimeSupported; + iface->nwType = add_bss->nwType; + + return QDF_STATUS_SUCCESS; +} + +static inline +bool wma_cdp_find_peer_by_addr(uint8_t *peer_addr) +{ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint8_t pdev_id = OL_TXRX_PDEV_ID; + + if (!soc || pdev_id == OL_TXRX_INVALID_PDEV_ID) { + WMA_LOGE("%s Failed to get pdev/soc", __func__); + return NULL; + } + + return cdp_find_peer_exist(soc, pdev_id, peer_addr); +} + +static +QDF_STATUS wma_save_bss_params(tp_wma_handle wma, struct bss_params *add_bss) +{ + QDF_STATUS status; + + wma_vdev_set_he_config(wma, add_bss->staContext.smesessionId, add_bss); + if (!wma_cdp_find_peer_by_addr(add_bss->bssId)) + status = QDF_STATUS_E_FAILURE; + else + status = QDF_STATUS_SUCCESS; + qdf_mem_copy(add_bss->staContext.staMac, add_bss->bssId, + sizeof(add_bss->staContext.staMac)); + + WMA_LOGD("%s: update_bss %d nw_type %d bssid "QDF_MAC_ADDR_FMT" status %d", + __func__, add_bss->updateBss, add_bss->nwType, + QDF_MAC_ADDR_REF(add_bss->bssId), status); + + return status; +} + +QDF_STATUS wma_pre_assoc_req(struct bss_params *add_bss) +{ + QDF_STATUS status; + tp_wma_handle wma; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("Invalid wma"); + return QDF_STATUS_E_INVAL; + } + + status = wma_update_iface_params(wma, add_bss); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = wma_save_bss_params(wma, add_bss); + + return status; +} + +void wma_add_bss_lfr3(tp_wma_handle wma, struct bss_params *add_bss) +{ + QDF_STATUS status; + + status = wma_update_iface_params(wma, add_bss); + if (QDF_IS_STATUS_ERROR(status)) + return; + + if (!wma_cdp_find_peer_by_addr(add_bss->bssId)) { + WMA_LOGE("%s Failed to find peer "QDF_MAC_ADDR_FMT, __func__, + QDF_MAC_ADDR_REF(add_bss->bssId)); + return; + } + WMA_LOGD("LFR3:%s: bssid "QDF_MAC_ADDR_FMT, __func__, + QDF_MAC_ADDR_REF(add_bss->bssId)); +} + + +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2) +static +QDF_STATUS wma_set_cdp_vdev_pause_reason(tp_wma_handle wma, uint8_t vdev_id) +{ + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + cdp_fc_vdev_pause(soc, vdev_id, + OL_TXQ_PAUSE_REASON_PEER_UNAUTHORIZED, 0); + + return QDF_STATUS_SUCCESS; +} +#else +static inline +QDF_STATUS wma_set_cdp_vdev_pause_reason(tp_wma_handle wma, uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +void wma_send_add_bss_resp(tp_wma_handle wma, uint8_t vdev_id, + QDF_STATUS status) +{ + struct add_bss_rsp *add_bss_rsp; + + add_bss_rsp = qdf_mem_malloc(sizeof(*add_bss_rsp)); + if (!add_bss_rsp) + return; + + add_bss_rsp->vdev_id = vdev_id; + add_bss_rsp->status = status; + lim_handle_add_bss_rsp(wma->mac_context, add_bss_rsp); +} + +#ifdef WLAN_FEATURE_HOST_ROAM +QDF_STATUS wma_add_bss_lfr2_vdev_start(struct wlan_objmgr_vdev *vdev, + struct bss_params *add_bss) +{ + tp_wma_handle wma; + QDF_STATUS status; + struct vdev_mlme_obj *mlme_obj; + uint8_t vdev_id; + bool peer_exist = false; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma || !vdev) { + WMA_LOGE("Invalid wma or vdev"); + return QDF_STATUS_E_INVAL; + } + + vdev_id = vdev->vdev_objmgr.vdev_id; + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + wma_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = wma_update_iface_params(wma, add_bss); + if (QDF_IS_STATUS_ERROR(status)) + goto send_fail_resp; + + peer_exist = wma_cdp_find_peer_by_addr(mlme_obj->mgmt.generic.bssid); + if (!peer_exist) { + wma_err("Failed to find peer "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mlme_obj->mgmt.generic.bssid)); + goto send_fail_resp; + } + + status = wma_vdev_pre_start(vdev_id, false); + if (QDF_IS_STATUS_ERROR(status)) { + wma_err("failed, status: %d", status); + goto peer_cleanup; + } + status = vdev_mgr_start_send(mlme_obj, false); + if (QDF_IS_STATUS_ERROR(status)) { + wma_err("failed, status: %d", status); + goto peer_cleanup; + } + status = wma_set_cdp_vdev_pause_reason(wma, vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + goto peer_cleanup; + + /* ADD_BSS_RESP will be deferred to completion of VDEV_START */ + return QDF_STATUS_SUCCESS; + +peer_cleanup: + if (peer_exist) + wma_remove_peer(wma, mlme_obj->mgmt.generic.bssid, vdev_id, + false); + +send_fail_resp: + wma_send_add_bss_resp(wma, vdev_id, QDF_STATUS_E_FAILURE); + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS wma_send_peer_assoc_req(struct bss_params *add_bss) +{ + struct wma_target_req *msg; + tp_wma_handle wma; + uint8_t vdev_id; + bool peer_exist; + QDF_STATUS status; + struct wma_txrx_node *iface; + int pps_val = 0; + struct vdev_mlme_obj *mlme_obj; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("Invalid wma"); + return QDF_STATUS_E_INVAL; + } + if (!mac) { + WMA_LOGE("Invalid mac context"); + return QDF_STATUS_E_INVAL; + } + + vdev_id = add_bss->staContext.smesessionId; + + iface = &wma->interfaces[vdev_id]; + status = wma_update_iface_params(wma, add_bss); + if (QDF_IS_STATUS_ERROR(status)) + goto send_resp; + + peer_exist = wma_cdp_find_peer_by_addr(add_bss->bssId); + if (add_bss->nonRoamReassoc && peer_exist) + goto send_resp; + + if (!add_bss->updateBss) + goto send_resp; + + if (!peer_exist) { + WMA_LOGE("%s: %d Failed to find peer "QDF_MAC_ADDR_FMT, + __func__, __LINE__, QDF_MAC_ADDR_REF(add_bss->bssId)); + status = QDF_STATUS_E_FAILURE; + goto send_resp; + } + + + if (add_bss->staContext.encryptType == eSIR_ED_NONE) { + WMA_LOGD("%s: Update peer("QDF_MAC_ADDR_FMT") state into auth", + __func__, QDF_MAC_ADDR_REF(add_bss->bssId)); + cdp_peer_state_update(soc, add_bss->bssId, + OL_TXRX_PEER_STATE_AUTH); + } else { + WMA_LOGD("%s: Update peer("QDF_MAC_ADDR_FMT") state into conn", + __func__, QDF_MAC_ADDR_REF(add_bss->bssId)); + cdp_peer_state_update(soc, add_bss->bssId, + OL_TXRX_PEER_STATE_CONN); + status = wma_set_cdp_vdev_pause_reason(wma, vdev_id); + if (QDF_IS_STATUS_ERROR(status)) + goto send_resp; + } + + wmi_unified_send_txbf(wma, &add_bss->staContext); + + pps_val = ((mac->mlme_cfg->sta.enable_5g_ebt << 31) & + 0xffff0000) | (PKT_PWR_SAVE_5G_EBT & 0xffff); + status = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_PACKET_POWERSAVE, + pps_val); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to send wmi packet power save cmd"); + else + WMA_LOGD("Sent packet power save %x", pps_val); + + add_bss->staContext.no_ptk_4_way = add_bss->no_ptk_4_way; + + status = wma_send_peer_assoc(wma, add_bss->nwType, + &add_bss->staContext); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to send peer assoc status:%d", status); + goto send_resp; + } + + /* we just had peer assoc, so install key will be done later */ + if (add_bss->staContext.encryptType != eSIR_ED_NONE) + iface->is_waiting_for_key = true; + + if (add_bss->rmfEnabled) + wma_set_mgmt_frame_protection(wma); + + wma_vdev_set_bss_params(wma, add_bss->staContext.smesessionId, + add_bss->beaconInterval, + add_bss->dtimPeriod, + add_bss->shortSlotTimeSupported, + add_bss->llbCoexist, + add_bss->maxTxPower); + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (!mlme_obj) { + WMA_LOGE("Failed to mlme obj"); + status = QDF_STATUS_E_FAILURE; + goto send_resp; + } + /* + * Store the bssid in interface table, bssid will + * be used during group key setting sta mode. + */ + qdf_mem_copy(mlme_obj->mgmt.generic.bssid, + add_bss->bssId, QDF_MAC_ADDR_SIZE); + + wma_save_bss_params(wma, add_bss); + + if (!wmi_service_enabled(wma->wmi_handle, + wmi_service_peer_assoc_conf)) { + WMA_LOGI(FL("WMI_SERVICE_PEER_ASSOC_CONF not enabled")); + goto send_resp; + } + + msg = wma_fill_hold_req(wma, vdev_id, WMA_ADD_BSS_REQ, + WMA_PEER_ASSOC_CNF_START, NULL, + WMA_PEER_ASSOC_TIMEOUT); + if (!msg) { + WMA_LOGE(FL("Failed to allocate request for vdev_id %d"), + vdev_id); + wma_remove_req(wma, vdev_id, WMA_PEER_ASSOC_CNF_START); + status = QDF_STATUS_E_FAILURE; + goto send_resp; + } + + return QDF_STATUS_SUCCESS; + +send_resp: + wma_send_add_bss_resp(wma, vdev_id, status); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_add_sta_req_ap_mode() - process add sta request in ap mode + * @wma: wma handle + * @add_sta: add sta params + * + * Return: none + */ +static void wma_add_sta_req_ap_mode(tp_wma_handle wma, tpAddStaParams add_sta) +{ + enum ol_txrx_peer_state state = OL_TXRX_PEER_STATE_CONN; + uint8_t pdev_id = OL_TXRX_PDEV_ID; + QDF_STATUS status; + int32_t ret; + struct wma_txrx_node *iface = NULL; + struct wma_target_req *msg; + bool peer_assoc_cnf = false; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint32_t i, j; + uint16_t mcs_limit; + uint8_t *rate_pos; + struct mac_context *mac = wma->mac_context; + + /* UMAC sends WMA_ADD_STA_REQ msg twice to WMA when the station + * associates. First WMA_ADD_STA_REQ will have staType as + * STA_ENTRY_PEER and second posting will have STA_ENTRY_SELF. + * Peer creation is done in first WMA_ADD_STA_REQ and second + * WMA_ADD_STA_REQ which has STA_ENTRY_SELF is ignored and + * send fake response with success to UMAC. Otherwise UMAC + * will get blocked. + */ + if (add_sta->staType != STA_ENTRY_PEER) { + add_sta->status = QDF_STATUS_SUCCESS; + goto send_rsp; + } + + iface = &wma->interfaces[add_sta->smesessionId]; + + if (cdp_find_peer_exist_on_vdev(soc, add_sta->smesessionId, + add_sta->staMac)) { + wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId, + false); + WMA_LOGE("%s: Peer already exists, Deleted peer with peer_addr "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(add_sta->staMac)); + } + /* The code above only checks the peer existence on its own vdev. + * Need to check whether the peer exists on other vDevs because firmware + * can't create the peer if the peer with same MAC address already + * exists on the pDev. As this peer belongs to other vDevs, just return + * here. + */ + if (cdp_find_peer_exist(soc, pdev_id, add_sta->staMac)) { + WMA_LOGE("%s: My vdev id %d, but Peer exists on other vdev with peer_addr "QDF_MAC_ADDR_FMT, + __func__, add_sta->smesessionId, + QDF_MAC_ADDR_REF(add_sta->staMac)); + add_sta->status = QDF_STATUS_E_FAILURE; + goto send_rsp; + } + + wma_delete_invalid_peer_entries(add_sta->smesessionId, add_sta->staMac); + + status = wma_create_peer(wma, add_sta->staMac, WMI_PEER_TYPE_DEFAULT, + add_sta->smesessionId, false); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Failed to create peer for "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(add_sta->staMac)); + add_sta->status = status; + goto send_rsp; + } + + if (!cdp_find_peer_exist_on_vdev(soc, add_sta->smesessionId, + add_sta->staMac)) { + WMA_LOGE("%s: Failed to find peer handle using peer mac "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(add_sta->staMac)); + add_sta->status = QDF_STATUS_E_FAILURE; + wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId, + false); + goto send_rsp; + } + + wmi_unified_send_txbf(wma, add_sta); + + /* + * Get MCS limit from ini configure, and map it to rate parameters + * This will limit HT rate upper bound. CFG_CTRL_MASK is used to + * check whether ini config is enabled and CFG_DATA_MASK to get the + * MCS value. + */ +#define CFG_CTRL_MASK 0xFF00 +#define CFG_DATA_MASK 0x00FF + + mcs_limit = mac->mlme_cfg->rates.sap_max_mcs_txdata; + + if (mcs_limit & CFG_CTRL_MASK) { + WMA_LOGD("%s: set mcs_limit %x", __func__, mcs_limit); + + mcs_limit &= CFG_DATA_MASK; + rate_pos = (u_int8_t *)add_sta->supportedRates.supportedMCSSet; + for (i = 0, j = 0; i < MAX_SUPPORTED_RATES;) { + if (j < mcs_limit / 8) { + rate_pos[j] = 0xff; + j++; + i += 8; + } else if (j < mcs_limit / 8 + 1) { + if (i <= mcs_limit) + rate_pos[i / 8] |= 1 << (i % 8); + else + rate_pos[i / 8] &= ~(1 << (i % 8)); + i++; + + if (i >= (j + 1) * 8) + j++; + } else { + rate_pos[j++] = 0; + i += 8; + } + } + } + + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_peer_assoc_conf)) { + peer_assoc_cnf = true; + msg = wma_fill_hold_req(wma, add_sta->smesessionId, + WMA_ADD_STA_REQ, WMA_PEER_ASSOC_CNF_START, + add_sta, WMA_PEER_ASSOC_TIMEOUT); + if (!msg) { + WMA_LOGE(FL("Failed to alloc request for vdev_id %d"), + add_sta->smesessionId); + add_sta->status = QDF_STATUS_E_FAILURE; + wma_remove_req(wma, add_sta->smesessionId, + WMA_PEER_ASSOC_CNF_START); + wma_remove_peer(wma, add_sta->staMac, + add_sta->smesessionId, false); + peer_assoc_cnf = false; + goto send_rsp; + } + } else { + WMA_LOGE(FL("WMI_SERVICE_PEER_ASSOC_CONF not enabled")); + } + + ret = wma_send_peer_assoc(wma, add_sta->nwType, add_sta); + if (ret) { + add_sta->status = QDF_STATUS_E_FAILURE; + wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId, + false); + goto send_rsp; + } + + wma_send_peer_atim_window_len(wma, add_sta); + if (add_sta->rmfEnabled) + wma_set_peer_pmf_status(wma, add_sta->staMac, true); + + if (add_sta->uAPSD) { + status = wma_set_ap_peer_uapsd(wma, add_sta->smesessionId, + add_sta->staMac, + add_sta->uAPSD, add_sta->maxSPLen); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to set peer uapsd param for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(add_sta->staMac)); + add_sta->status = QDF_STATUS_E_FAILURE; + wma_remove_peer(wma, add_sta->staMac, + add_sta->smesessionId, false); + goto send_rsp; + } + } + + WMA_LOGD("%s: Moving peer "QDF_MAC_ADDR_FMT" to state %d", + __func__, QDF_MAC_ADDR_REF(add_sta->staMac), state); + cdp_peer_state_update(soc, add_sta->staMac, state); + + add_sta->nss = iface->nss; + add_sta->status = QDF_STATUS_SUCCESS; +send_rsp: + /* Do not send add stat resp when peer assoc cnf is enabled */ + if (peer_assoc_cnf) { + WMA_LOGD(FL("WMI_SERVICE_PEER_ASSOC_CONF is enabled")); + return; + } + + WMA_LOGD(FL("statype %d vdev_id %d aid %d bssid "QDF_MAC_ADDR_FMT" status %d"), + add_sta->staType, add_sta->smesessionId, + add_sta->assocId, QDF_MAC_ADDR_REF(add_sta->bssId), + add_sta->status); + wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, (void *)add_sta, 0); +} + +#ifdef FEATURE_WLAN_TDLS + +/** + * wma_add_tdls_sta() - process add sta request in TDLS mode + * @wma: wma handle + * @add_sta: add sta params + * + * Return: none + */ +static void wma_add_tdls_sta(tp_wma_handle wma, tpAddStaParams add_sta) +{ + QDF_STATUS status; + int32_t ret; + struct tdls_peer_update_state *peer_state; + struct wma_target_req *msg; + bool peer_assoc_cnf = false; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint8_t pdev_id = OL_TXRX_PDEV_ID; + + WMA_LOGD("%s: staType: %d, updateSta: %d, bssId: "QDF_MAC_ADDR_FMT", staMac: "QDF_MAC_ADDR_FMT, + __func__, add_sta->staType, + add_sta->updateSta, QDF_MAC_ADDR_REF(add_sta->bssId), + QDF_MAC_ADDR_REF(add_sta->staMac)); + + if (wma_is_roam_synch_in_progress(wma, add_sta->smesessionId) || + wma_is_roam_in_progress(add_sta->smesessionId)) { + WMA_LOGE("%s: roaming in progress, reject add sta!", __func__); + add_sta->status = QDF_STATUS_E_PERM; + goto send_rsp; + } + + if (0 == add_sta->updateSta) { + /* its a add sta request * */ + + cdp_peer_copy_mac_addr_raw(soc, add_sta->smesessionId, + add_sta->bssId); + + WMA_LOGD("%s: addSta, calling wma_create_peer for "QDF_MAC_ADDR_FMT", vdev_id %hu", + __func__, QDF_MAC_ADDR_REF(add_sta->staMac), + add_sta->smesessionId); + + status = wma_create_peer(wma, add_sta->staMac, + WMI_PEER_TYPE_TDLS, + add_sta->smesessionId, false); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Failed to create peer for "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(add_sta->staMac)); + add_sta->status = status; + goto send_rsp; + } + + WMA_LOGD("%s: addSta, after calling cdp_local_peer_id, staMac: "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(add_sta->staMac)); + + peer_state = qdf_mem_malloc(sizeof(*peer_state)); + if (!peer_state) { + add_sta->status = QDF_STATUS_E_NOMEM; + goto send_rsp; + } + + peer_state->peer_state = WMI_TDLS_PEER_STATE_PEERING; + peer_state->vdev_id = add_sta->smesessionId; + qdf_mem_copy(&peer_state->peer_macaddr, + &add_sta->staMac, sizeof(tSirMacAddr)); + wma_update_tdls_peer_state(wma, peer_state); + } else { + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_peer_assoc_conf)) { + WMA_LOGE(FL("WMI_SERVICE_PEER_ASSOC_CONF is enabled")); + peer_assoc_cnf = true; + msg = wma_fill_hold_req(wma, add_sta->smesessionId, + WMA_ADD_STA_REQ, WMA_PEER_ASSOC_CNF_START, + add_sta, WMA_PEER_ASSOC_TIMEOUT); + if (!msg) { + WMA_LOGE(FL("Failed to alloc request for vdev_id %d"), + add_sta->smesessionId); + add_sta->status = QDF_STATUS_E_FAILURE; + wma_remove_req(wma, add_sta->smesessionId, + WMA_PEER_ASSOC_CNF_START); + wma_remove_peer(wma, add_sta->staMac, + add_sta->smesessionId, false); + peer_assoc_cnf = false; + goto send_rsp; + } + } else { + WMA_LOGE(FL("WMI_SERVICE_PEER_ASSOC_CONF not enabled")); + } + + WMA_LOGD("%s: changeSta, calling wma_send_peer_assoc", + __func__); + if (add_sta->rmfEnabled) + wma_set_peer_pmf_status(wma, add_sta->staMac, true); + + ret = + wma_send_peer_assoc(wma, add_sta->nwType, add_sta); + if (ret) { + add_sta->status = QDF_STATUS_E_FAILURE; + wma_remove_peer(wma, add_sta->staMac, + add_sta->smesessionId, false); + cdp_peer_add_last_real_peer(soc, pdev_id, + add_sta->smesessionId); + wma_remove_req(wma, add_sta->smesessionId, + WMA_PEER_ASSOC_CNF_START); + peer_assoc_cnf = false; + + goto send_rsp; + } + } + +send_rsp: + /* Do not send add stat resp when peer assoc cnf is enabled */ + if (peer_assoc_cnf) + return; + + WMA_LOGD(FL("statype %d vdev_id %d aid %d bssid "QDF_MAC_ADDR_FMT" status %d"), + add_sta->staType, add_sta->smesessionId, + add_sta->assocId, QDF_MAC_ADDR_REF(add_sta->bssId), + add_sta->status); + wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, (void *)add_sta, 0); +} +#endif + +/** + * wma_send_bss_color_change_enable() - send bss color change enable cmd. + * @wma: wma handle + * @params: add sta params + * + * Send bss color change command to firmware, to enable firmware to update + * internally if any change in bss color in advertised by associated AP. + * + * Return: none + */ +#ifdef WLAN_FEATURE_11AX +static void wma_send_bss_color_change_enable(tp_wma_handle wma, + tpAddStaParams params) +{ + QDF_STATUS status; + uint32_t vdev_id = params->smesessionId; + + if (!params->he_capable) { + WMA_LOGD("%s: he_capable is not set for vdev_id:%d", + __func__, vdev_id); + return; + } + + status = wmi_unified_send_bss_color_change_enable_cmd(wma->wmi_handle, + vdev_id, + true); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to enable bss color change offload, vdev:%d", + vdev_id); + } + + return; +} +#else +static void wma_send_bss_color_change_enable(tp_wma_handle wma, + tpAddStaParams params) +{ +} +#endif + +/** + * wma_add_sta_req_sta_mode() - process add sta request in sta mode + * @wma: wma handle + * @params: add sta params + * + * Return: none + */ +static void wma_add_sta_req_sta_mode(tp_wma_handle wma, tpAddStaParams params) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wma_txrx_node *iface; + int8_t maxTxPower; + int ret = 0; + struct wma_target_req *msg; + bool peer_assoc_cnf = false; + int smps_param; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + +#ifdef FEATURE_WLAN_TDLS + if (STA_ENTRY_TDLS_PEER == params->staType) { + wma_add_tdls_sta(wma, params); + return; + } +#endif + + iface = &wma->interfaces[params->smesessionId]; + if (params->staType != STA_ENTRY_SELF) { + WMA_LOGE("%s: unsupported station type %d", + __func__, params->staType); + goto out; + } + if (params->nonRoamReassoc) { + cdp_peer_state_update(soc, params->bssId, + OL_TXRX_PEER_STATE_AUTH); + qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STARTED); + iface->aid = params->assocId; + goto out; + } + + if (wma_is_vdev_up(params->smesessionId)) { + WMA_LOGD("%s: vdev id %d is already UP for "QDF_MAC_ADDR_FMT, + __func__, params->smesessionId, + QDF_MAC_ADDR_REF(params->bssId)); + status = QDF_STATUS_E_FAILURE; + goto out; + } + + if (cdp_peer_state_get(soc, params->smesessionId, + params->bssId) == OL_TXRX_PEER_STATE_DISC) { + /* + * This is the case for reassociation. + * peer state update and peer_assoc is required since it + * was not done by WMA_ADD_BSS_REQ. + */ + + /* Update peer state */ + if (params->encryptType == eSIR_ED_NONE) { + WMA_LOGD("%s: Update peer("QDF_MAC_ADDR_FMT") state into auth", + __func__, QDF_MAC_ADDR_REF(params->bssId)); + cdp_peer_state_update(soc, params->bssId, + OL_TXRX_PEER_STATE_AUTH); + } else { + WMA_LOGD("%s: Update peer("QDF_MAC_ADDR_FMT") state into conn", + __func__, QDF_MAC_ADDR_REF(params->bssId)); + cdp_peer_state_update(soc, params->bssId, + OL_TXRX_PEER_STATE_CONN); + } + + if (wma_is_roam_synch_in_progress(wma, params->smesessionId)) { + /* iface->nss = params->nss; */ + /*In LFR2.0, the following operations are performed as + * part of wma_send_peer_assoc. As we are + * skipping this operation, we are just executing the + * following which are useful for LFR3.0 + */ + cdp_peer_state_update(soc, params->bssId, + OL_TXRX_PEER_STATE_AUTH); + qdf_atomic_set(&iface->bss_status, + WMA_BSS_STATUS_STARTED); + iface->aid = params->assocId; + WMA_LOGD("LFR3:statype %d vdev %d aid %d bssid "QDF_MAC_ADDR_FMT, + params->staType, params->smesessionId, + params->assocId, + QDF_MAC_ADDR_REF(params->bssId)); + return; + } + wmi_unified_send_txbf(wma, params); + + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_peer_assoc_conf)) { + peer_assoc_cnf = true; + msg = wma_fill_hold_req(wma, params->smesessionId, + WMA_ADD_STA_REQ, WMA_PEER_ASSOC_CNF_START, + params, WMA_PEER_ASSOC_TIMEOUT); + if (!msg) { + WMA_LOGD(FL("Failed to alloc request for vdev_id %d"), + params->smesessionId); + params->status = QDF_STATUS_E_FAILURE; + wma_remove_req(wma, params->smesessionId, + WMA_PEER_ASSOC_CNF_START); + wma_remove_peer(wma, params->bssId, + params->smesessionId, false); + peer_assoc_cnf = false; + goto out; + } + } else { + WMA_LOGD(FL("WMI_SERVICE_PEER_ASSOC_CONF not enabled")); + } + + ((tAddStaParams *)iface->addBssStaContext)->no_ptk_4_way = + params->no_ptk_4_way; + + qdf_mem_copy(((tAddStaParams *)iface->addBssStaContext)-> + supportedRates.supportedMCSSet, + params->supportedRates.supportedMCSSet, + SIR_MAC_MAX_SUPPORTED_MCS_SET); + + + ret = wma_send_peer_assoc(wma, + iface->nwType, + (tAddStaParams *) iface->addBssStaContext); + if (ret) { + status = QDF_STATUS_E_FAILURE; + wma_remove_peer(wma, params->bssId, + params->smesessionId, false); + goto out; + } + + if (params->rmfEnabled) { + wma_set_mgmt_frame_protection(wma); + wma_set_peer_pmf_status(wma, params->bssId, true); + } + } + maxTxPower = params->maxTxPower; + wma_vdev_set_bss_params(wma, params->smesessionId, + iface->beaconInterval, iface->dtimPeriod, + iface->shortSlotTimeSupported, + iface->llbCoexist, maxTxPower); + + params->csaOffloadEnable = 0; + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_csa_offload)) { + params->csaOffloadEnable = 1; + if (wma_unified_csa_offload_enable(wma, params->smesessionId) < + 0) { + WMA_LOGE("Unable to enable CSA offload for vdev_id:%d", + params->smesessionId); + } + } + + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_filter_ipsec_natkeepalive)) { + if (wmi_unified_nat_keepalive_en_cmd(wma->wmi_handle, + params->smesessionId)) { + WMA_LOGE("Unable to enable NAT keepalive for vdev_id:%d", + params->smesessionId); + } + } + qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STARTED); + /* Sta is now associated, configure various params */ + + /* Send SMPS force command to FW to send the required + * action frame only when SM power save is enbaled in + * from INI. In case dynamic antenna selection, the + * action frames are sent by the chain mask manager + * In addition to the action frames, The SM power save is + * published in the assoc request HT SMPS IE for both cases. + */ + if ((params->enableHtSmps) && (params->send_smps_action)) { + smps_param = wma_smps_mode_to_force_mode_param( + params->htSmpsconfig); + if (smps_param >= 0) { + WMA_LOGD("%s: Send SMPS force mode: %d", + __func__, params->htSmpsconfig); + wma_set_mimops(wma, params->smesessionId, + smps_param); + } + } + + wma_send_bss_color_change_enable(wma, params); + + /* Partial AID match power save, enable when SU bformee */ + if (params->enableVhtpAid && params->vhtTxBFCapable) + wma_set_ppsconfig(params->smesessionId, + WMA_VHT_PPS_PAID_MATCH, 1); + + /* Enable AMPDU power save, if htCapable/vhtCapable */ + if (params->enableAmpduPs && (params->htCapable || params->vhtCapable)) + wma_set_ppsconfig(params->smesessionId, + WMA_VHT_PPS_DELIM_CRC_FAIL, 1); + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_listen_interval_offload_support)) { + wma_debug("listen interval offload enabled, setting params"); + status = wma_vdev_set_param(wma->wmi_handle, + params->smesessionId, + WMI_VDEV_PARAM_MAX_LI_OF_MODDTIM, + wma->staMaxLIModDtim); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE(FL("can't set MAX_LI for session: %d"), + params->smesessionId); + } + status = wma_vdev_set_param(wma->wmi_handle, + params->smesessionId, + WMI_VDEV_PARAM_DYNDTIM_CNT, + wma->staDynamicDtim); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE(FL("can't set DYNDTIM_CNT for session: %d"), + params->smesessionId); + } + status = wma_vdev_set_param(wma->wmi_handle, + params->smesessionId, + WMI_VDEV_PARAM_MODDTIM_CNT, + wma->staModDtim); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE(FL("can't set DTIM_CNT for session: %d"), + params->smesessionId); + } + + } else { + WMA_LOGD("%s: listen interval offload is not set", + __func__); + } + + iface->aid = params->assocId; + params->nss = iface->nss; +out: + /* Do not send add stat resp when peer assoc cnf is enabled */ + if (peer_assoc_cnf) + return; + + params->status = status; + wma_debug("vdev_id %d aid %d sta mac " QDF_MAC_ADDR_FMT " status %d", + params->smesessionId, params->assocId, + QDF_MAC_ADDR_REF(params->bssId), params->status); + /* Don't send a response during roam sync operation */ + if (!wma_is_roam_synch_in_progress(wma, params->smesessionId)) + wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, + (void *)params, 0); +} + +/** + * wma_delete_sta_req_ap_mode() - process delete sta request from UMAC in AP mode + * @wma: wma handle + * @del_sta: delete sta params + * + * Return: none + */ +static void wma_delete_sta_req_ap_mode(tp_wma_handle wma, + tpDeleteStaParams del_sta) +{ + struct wma_target_req *msg; + QDF_STATUS qdf_status; + + qdf_status = wma_remove_peer(wma, del_sta->staMac, + del_sta->smesessionId, false); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE(FL("wma_remove_peer failed")); + del_sta->status = QDF_STATUS_E_FAILURE; + goto send_del_rsp; + } + del_sta->status = QDF_STATUS_SUCCESS; + + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_sync_delete_cmds)) { + msg = wma_fill_hold_req(wma, del_sta->smesessionId, + WMA_DELETE_STA_REQ, + WMA_DELETE_STA_RSP_START, del_sta, + WMA_DELETE_STA_TIMEOUT); + if (!msg) { + WMA_LOGE(FL("Failed to allocate request. vdev_id %d"), + del_sta->smesessionId); + wma_remove_req(wma, del_sta->smesessionId, + WMA_DELETE_STA_RSP_START); + del_sta->status = QDF_STATUS_E_NOMEM; + goto send_del_rsp; + } + + wma_acquire_wakelock(&wma->wmi_cmd_rsp_wake_lock, + WMA_FW_RSP_EVENT_WAKE_LOCK_DURATION); + + return; + } + +send_del_rsp: + if (del_sta->respReqd) { + WMA_LOGD("%s: Sending del rsp to umac (status: %d)", + __func__, del_sta->status); + wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP, + (void *)del_sta, 0); + } +} + +#ifdef FEATURE_WLAN_TDLS +/** + * wma_del_tdls_sta() - process delete sta request from UMAC in TDLS + * @wma: wma handle + * @del_sta: delete sta params + * + * Return: none + */ +static void wma_del_tdls_sta(tp_wma_handle wma, tpDeleteStaParams del_sta) +{ + struct tdls_peer_update_state *peer_state; + struct wma_target_req *msg; + int status; + + peer_state = qdf_mem_malloc(sizeof(*peer_state)); + if (!peer_state) { + del_sta->status = QDF_STATUS_E_NOMEM; + goto send_del_rsp; + } + + peer_state->peer_state = TDLS_PEER_STATE_TEARDOWN; + peer_state->vdev_id = del_sta->smesessionId; + peer_state->resp_reqd = del_sta->respReqd; + qdf_mem_copy(&peer_state->peer_macaddr, + &del_sta->staMac, sizeof(tSirMacAddr)); + + WMA_LOGD("%s: sending tdls_peer_state for peer mac: "QDF_MAC_ADDR_FMT", peerState: %d", + __func__, QDF_MAC_ADDR_REF(peer_state->peer_macaddr), + peer_state->peer_state); + + status = wma_update_tdls_peer_state(wma, peer_state); + + if (status < 0) { + WMA_LOGE("%s: wma_update_tdls_peer_state returned failure", + __func__); + del_sta->status = QDF_STATUS_E_FAILURE; + goto send_del_rsp; + } + + if (del_sta->respReqd && + wmi_service_enabled(wma->wmi_handle, + wmi_service_sync_delete_cmds)) { + del_sta->status = QDF_STATUS_SUCCESS; + msg = wma_fill_hold_req(wma, + del_sta->smesessionId, + WMA_DELETE_STA_REQ, + WMA_DELETE_STA_RSP_START, del_sta, + WMA_DELETE_STA_TIMEOUT); + if (!msg) { + WMA_LOGE(FL("Failed to allocate vdev_id %d"), + del_sta->smesessionId); + wma_remove_req(wma, + del_sta->smesessionId, + WMA_DELETE_STA_RSP_START); + del_sta->status = QDF_STATUS_E_NOMEM; + goto send_del_rsp; + } + + wma_acquire_wakelock(&wma->wmi_cmd_rsp_wake_lock, + WMA_FW_RSP_EVENT_WAKE_LOCK_DURATION); + } + + return; + +send_del_rsp: + if (del_sta->respReqd) { + WMA_LOGD("%s: Sending del rsp to umac (status: %d)", + __func__, del_sta->status); + wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP, + (void *)del_sta, 0); + } +} +#endif + +/** + * wma_delete_sta_req_sta_mode() - process delete sta request from UMAC + * @wma: wma handle + * @params: delete sta params + * + * Return: none + */ +static void wma_delete_sta_req_sta_mode(tp_wma_handle wma, + tpDeleteStaParams params) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wma_txrx_node *iface; + + iface = &wma->interfaces[params->smesessionId]; + iface->uapsd_cached_val = 0; +#ifdef FEATURE_WLAN_TDLS + if (STA_ENTRY_TDLS_PEER == params->staType) { + wma_del_tdls_sta(wma, params); + return; + } +#endif + params->status = status; + if (params->respReqd) { + WMA_LOGD("%s: vdev_id %d status %d", __func__, + params->smesessionId, status); + wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP, + (void *)params, 0); + } +} + +static void wma_sap_prevent_runtime_pm(tp_wma_handle wma) +{ + qdf_runtime_pm_prevent_suspend(&wma->sap_prevent_runtime_pm_lock); +} + +static void wma_sap_allow_runtime_pm(tp_wma_handle wma) +{ + qdf_runtime_pm_allow_suspend(&wma->sap_prevent_runtime_pm_lock); +} + +void wma_add_sta(tp_wma_handle wma, tpAddStaParams add_sta) +{ + uint8_t oper_mode = BSS_OPERATIONAL_MODE_STA; + void *htc_handle; + + htc_handle = lmac_get_htc_hdl(wma->psoc); + if (!htc_handle) { + WMA_LOGE(":%sHTC handle is NULL:%d", __func__, __LINE__); + return; + } + + wma_debug("Vdev %d BSSID "QDF_MAC_ADDR_FMT, add_sta->smesessionId, + QDF_MAC_ADDR_REF(add_sta->bssId)); + + if (wma_is_vdev_in_ap_mode(wma, add_sta->smesessionId)) + oper_mode = BSS_OPERATIONAL_MODE_AP; + else if (wma_is_vdev_in_ibss_mode(wma, add_sta->smesessionId)) + oper_mode = BSS_OPERATIONAL_MODE_IBSS; + + if (WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces, add_sta->smesessionId)) + oper_mode = BSS_OPERATIONAL_MODE_NDI; + switch (oper_mode) { + case BSS_OPERATIONAL_MODE_STA: + wma_add_sta_req_sta_mode(wma, add_sta); + break; + + /* IBSS should share the same code as AP mode */ + case BSS_OPERATIONAL_MODE_IBSS: + case BSS_OPERATIONAL_MODE_AP: + wma_add_sta_req_ap_mode(wma, add_sta); + break; + case BSS_OPERATIONAL_MODE_NDI: + wma_add_sta_ndi_mode(wma, add_sta); + break; + } + + /* handle wow for sap, ibss and nan with 1 or more peer in same way */ + if (BSS_OPERATIONAL_MODE_IBSS == oper_mode || + BSS_OPERATIONAL_MODE_AP == oper_mode || + BSS_OPERATIONAL_MODE_NDI == oper_mode) { + wma_debug("disable runtime pm and vote for link up"); + htc_vote_link_up(htc_handle); + wma_sap_prevent_runtime_pm(wma); + } + + /* adjust heart beat thresold timer value for detecting ibss peer + * departure + */ + if (oper_mode == BSS_OPERATIONAL_MODE_IBSS) + wma_adjust_ibss_heart_beat_timer(wma, add_sta->smesessionId, 1); +} + +void wma_delete_sta(tp_wma_handle wma, tpDeleteStaParams del_sta) +{ + uint8_t oper_mode = BSS_OPERATIONAL_MODE_STA; + uint8_t smesession_id = del_sta->smesessionId; + bool rsp_requested = del_sta->respReqd; + void *htc_handle; + + htc_handle = lmac_get_htc_hdl(wma->psoc); + if (!htc_handle) { + WMA_LOGE(":%sHTC handle is NULL:%d", __func__, __LINE__); + return; + } + + if (wma_is_vdev_in_ap_mode(wma, smesession_id)) + oper_mode = BSS_OPERATIONAL_MODE_AP; + if (wma_is_vdev_in_ibss_mode(wma, smesession_id)) { + oper_mode = BSS_OPERATIONAL_MODE_IBSS; + WMA_LOGD("%s: to delete sta for IBSS mode", __func__); + } + if (del_sta->staType == STA_ENTRY_NDI_PEER) + oper_mode = BSS_OPERATIONAL_MODE_NDI; + + wma_debug("oper_mode %d", oper_mode); + wma_debug("vdev_id %d status %d", + del_sta->smesessionId, del_sta->status); + + switch (oper_mode) { + case BSS_OPERATIONAL_MODE_STA: + if (wma_is_roam_synch_in_progress(wma, smesession_id)) { + wma_debug("LFR3: Del STA on vdev_id %d", + del_sta->smesessionId); + qdf_mem_free(del_sta); + return; + } + wma_delete_sta_req_sta_mode(wma, del_sta); + if (!rsp_requested) + qdf_mem_free(del_sta); + + break; + + case BSS_OPERATIONAL_MODE_IBSS: /* IBSS shares AP code */ + case BSS_OPERATIONAL_MODE_AP: + wma_delete_sta_req_ap_mode(wma, del_sta); + /* free the memory here only if sync feature is not enabled */ + if (!rsp_requested && + !wmi_service_enabled(wma->wmi_handle, + wmi_service_sync_delete_cmds)) { + WMA_LOGD(FL("vdev_id %d status %d"), + del_sta->smesessionId, del_sta->status); + qdf_mem_free(del_sta); + } else if (!rsp_requested && + (del_sta->status != QDF_STATUS_SUCCESS)) { + WMA_LOGD(FL("Release del_sta mem vdev_id %d status %d"), + del_sta->smesessionId, del_sta->status); + qdf_mem_free(del_sta); + } + break; + case BSS_OPERATIONAL_MODE_NDI: + wma_delete_sta_req_ndi_mode(wma, del_sta); + break; + default: + WMA_LOGE(FL("Incorrect oper mode %d"), oper_mode); + qdf_mem_free(del_sta); + } + + /* handle wow for sap, ibss and nan with 1 or more peer in same way */ + if (BSS_OPERATIONAL_MODE_IBSS == oper_mode || + BSS_OPERATIONAL_MODE_AP == oper_mode || + BSS_OPERATIONAL_MODE_NDI == oper_mode) { + wma_debug("allow runtime pm and vote for link down"); + htc_vote_link_down(htc_handle); + wma_sap_allow_runtime_pm(wma); + } + + /* adjust heart beat thresold timer value for + * detecting ibss peer departure + */ + if (oper_mode == BSS_OPERATIONAL_MODE_IBSS) + wma_adjust_ibss_heart_beat_timer(wma, smesession_id, -1); +} + +void wma_delete_bss_ho_fail(tp_wma_handle wma, uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wma_txrx_node *iface; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct vdev_stop_response resp_event; + struct del_bss_resp *vdev_stop_resp; + uint8_t *bssid; + + iface = &wma->interfaces[vdev_id]; + if (!iface) { + WMA_LOGE("%s iface for vdev_id %d is already deleted", + __func__, vdev_id); + goto fail_del_bss_ho_fail; + } + bssid = wma_get_vdev_bssid(iface->vdev); + if (!bssid) { + WMA_LOGE("%s:Invalid bssid", __func__); + status = QDF_STATUS_E_FAILURE; + goto fail_del_bss_ho_fail; + } + qdf_mem_zero(bssid, QDF_MAC_ADDR_SIZE); + + if (iface->psnr_req) { + qdf_mem_free(iface->psnr_req); + iface->psnr_req = NULL; + } + + if (iface->rcpi_req) { + struct sme_rcpi_req *rcpi_req = iface->rcpi_req; + + iface->rcpi_req = NULL; + qdf_mem_free(rcpi_req); + } + + if (iface->roam_scan_stats_req) { + struct sir_roam_scan_stats *roam_scan_stats_req = + iface->roam_scan_stats_req; + + iface->roam_scan_stats_req = NULL; + qdf_mem_free(roam_scan_stats_req); + } + + WMA_LOGD("%s, vdev_id: %d, pausing tx_ll_queue for VDEV_STOP (del_bss)", + __func__, vdev_id); + cdp_fc_vdev_pause(soc, vdev_id, OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); + wma_vdev_set_pause_bit(vdev_id, PAUSE_TYPE_HOST); + cdp_fc_vdev_flush(soc, vdev_id); + WMA_LOGD("%s, vdev_id: %d, un-pausing tx_ll_queue for VDEV_STOP rsp", + __func__, vdev_id); + cdp_fc_vdev_unpause(soc, vdev_id, OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); + wma_vdev_clear_pause_bit(vdev_id, PAUSE_TYPE_HOST); + qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STOPPED); + WMA_LOGD("%s: (type %d subtype %d) BSS is stopped", + __func__, iface->type, iface->sub_type); + + status = mlme_set_vdev_stop_type(iface->vdev, + WMA_DELETE_BSS_HO_FAIL_REQ); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to set wma req msg_type for vdev_id: %d", + __func__, vdev_id); + goto fail_del_bss_ho_fail; + } + + /* Try to use the vdev stop response path */ + resp_event.vdev_id = vdev_id; + status = wma_handle_vdev_stop_rsp(wma, &resp_event); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to handle vdev stop rsp for vdev_id %d", + __func__, vdev_id); + goto fail_del_bss_ho_fail; + } + + return; + +fail_del_bss_ho_fail: + vdev_stop_resp = qdf_mem_malloc(sizeof(*vdev_stop_resp)); + if (!vdev_stop_resp) { + WMA_LOGE("%s: Failed to alloc del bss resp ", __func__); + return; + } + vdev_stop_resp->vdev_id = vdev_id; + vdev_stop_resp->status = status; + wma_send_msg_high_priority(wma, WMA_DELETE_BSS_HO_FAIL_RSP, + (void *)vdev_stop_resp, 0); +} + +/** + * wma_wait_tx_complete() - Wait till tx packets are drained + * @wma: wma handle + * @session_id: vdev id + * + * Return: none + */ +static void wma_wait_tx_complete(tp_wma_handle wma, + uint32_t session_id) +{ + uint8_t max_wait_iterations = 0, delay = 0; + cdp_config_param_type val; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + QDF_STATUS status; + + if (!wma_is_vdev_valid(session_id)) { + WMA_LOGE("%s: Vdev is not valid: %d", + __func__, session_id); + return; + } + + status = ucfg_mlme_get_delay_before_vdev_stop(wma->psoc, &delay); + if (QDF_IS_STATUS_ERROR(status)) + wma_err("Failed to get delay before vdev stop"); + + max_wait_iterations = delay / WMA_TX_Q_RECHECK_TIMER_WAIT; + if (cdp_txrx_get_pdev_param(soc, + wlan_objmgr_pdev_get_pdev_id(wma->pdev), + CDP_TX_PENDING, &val)) + return; + while (val.cdp_pdev_param_tx_pending && max_wait_iterations) { + WMA_LOGW(FL("Waiting for outstanding packet to drain.")); + qdf_wait_for_event_completion(&wma->tx_queue_empty_event, + WMA_TX_Q_RECHECK_TIMER_WAIT); + if (cdp_txrx_get_pdev_param( + soc, + wlan_objmgr_pdev_get_pdev_id(wma->pdev), + CDP_TX_PENDING, &val)) + return; + max_wait_iterations--; + } +} + +void wma_delete_bss(tp_wma_handle wma, uint8_t vdev_id) +{ + bool peer_exist = false; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t tx_pending = 0; + cdp_config_param_type val; + bool roam_synch_in_progress = false; + struct wma_txrx_node *iface; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct qdf_mac_addr bssid; + struct del_bss_resp *params; + uint8_t *addr, *bssid_addr; + + iface = &wma->interfaces[vdev_id]; + if (!iface || !iface->vdev) { + WMA_LOGE("%s vdev id %d is already deleted", + __func__, vdev_id); + goto out; + } + + status = mlme_get_vdev_bss_peer_mac_addr(iface->vdev, &bssid); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s vdev id %d : failed to get bssid", + __func__, vdev_id); + goto out; + } + + addr = wlan_vdev_mlme_get_macaddr(iface->vdev); + if (!addr) { + WMA_LOGE("%s vdev id %d : failed to get macaddr", + __func__, vdev_id); + goto out; + } + if (wma_is_vdev_in_ibss_mode(wma, vdev_id)) + /* in rome ibss case, self mac is used to create the bss peer */ + peer_exist = wma_cdp_find_peer_by_addr(addr); + else if (WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces, + vdev_id)) + /* In ndi case, self mac is used to create the self peer */ + peer_exist = wma_cdp_find_peer_by_addr(addr); + else + peer_exist = wma_cdp_find_peer_by_addr(bssid.bytes); + if (!peer_exist) { + WMA_LOGE("%s: Failed to find peer "QDF_MAC_ADDR_FMT, __func__, + QDF_MAC_ADDR_REF(bssid.bytes)); + status = QDF_STATUS_E_FAILURE; + goto out; + } + bssid_addr = wma_get_vdev_bssid(wma->interfaces[vdev_id].vdev); + if (!bssid_addr) { + WMA_LOGE("%s: Failed to bssid for vdev_%d", __func__, + vdev_id); + status = QDF_STATUS_E_FAILURE; + goto out; + } + qdf_mem_zero(bssid_addr, + QDF_MAC_ADDR_SIZE); + + wma_delete_invalid_peer_entries(vdev_id, NULL); + + if (iface->psnr_req) { + qdf_mem_free(iface->psnr_req); + iface->psnr_req = NULL; + } + + if (iface->rcpi_req) { + struct sme_rcpi_req *rcpi_req = iface->rcpi_req; + + iface->rcpi_req = NULL; + qdf_mem_free(rcpi_req); + } + + if (iface->roam_scan_stats_req) { + struct sir_roam_scan_stats *roam_scan_stats_req = + iface->roam_scan_stats_req; + + iface->roam_scan_stats_req = NULL; + qdf_mem_free(roam_scan_stats_req); + } + + if (wlan_op_mode_ibss == cdp_get_opmode(soc, vdev_id)) + wma->ibss_started = 0; + + if (wma_is_roam_synch_in_progress(wma, vdev_id)) { + roam_synch_in_progress = true; + WMA_LOGD("LFR3:%s: Setting vdev_up to FALSE for session %d", + __func__, vdev_id); + + goto detach_peer; + } + + status = mlme_set_vdev_stop_type(iface->vdev, + WMA_DELETE_BSS_REQ); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to set wma req msg_type for vdev_id: %d", + __func__, vdev_id); + goto out; + } + + cdp_txrx_get_pdev_param(soc, wlan_objmgr_pdev_get_pdev_id(wma->pdev), + CDP_TX_PENDING, &val); + tx_pending = val.cdp_pdev_param_tx_pending; + WMA_LOGD(FL("Outstanding msdu packets: %u"), tx_pending); + wma_wait_tx_complete(wma, vdev_id); + + cdp_txrx_get_pdev_param(soc, wlan_objmgr_pdev_get_pdev_id(wma->pdev), + CDP_TX_PENDING, &val); + if (tx_pending) { + WMA_LOGD(FL("Outstanding msdu packets before VDEV_STOP : %u"), + tx_pending); + } + + WMA_LOGD("%s, vdev_id: %d, pausing tx_ll_queue for VDEV_STOP (del_bss)", + __func__, vdev_id); + wma_vdev_set_pause_bit(vdev_id, PAUSE_TYPE_HOST); + cdp_fc_vdev_pause(soc, vdev_id, + OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); + + if (wma_send_vdev_stop_to_fw(wma, vdev_id)) { + WMA_LOGE("%s: %d Failed to send vdev stop", __func__, __LINE__); + status = QDF_STATUS_E_FAILURE; + qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STOPPED); + goto detach_peer; + } + WMA_LOGD("%s: bssid "QDF_MAC_ADDR_FMT" vdev_id %d", + __func__, QDF_MAC_ADDR_REF(bssid.bytes), vdev_id); + + return; + +detach_peer: + wma_remove_peer(wma, bssid.bytes, vdev_id, + wma_is_roam_synch_in_progress(wma, vdev_id)); + if (wma_is_roam_synch_in_progress(wma, vdev_id)) + return; + +out: + params = qdf_mem_malloc(sizeof(*params)); + if (!params) { + WMA_LOGE("%s vdev id %d : failed to alloc del bss resp", + __func__, vdev_id); + return; + } + params->vdev_id = vdev_id; + params->status = status; + wma_send_msg_high_priority(wma, WMA_DELETE_BSS_RSP, params, 0); +} + +/** + * wma_find_ibss_vdev() - This function finds vdev_id based on input type + * @wma: wma handle + * @type: vdev type + * + * Return: vdev id + */ +int32_t wma_find_vdev_by_type(tp_wma_handle wma, int32_t type) +{ + int32_t vdev_id = 0; + struct wma_txrx_node *intf = wma->interfaces; + + for (vdev_id = 0; vdev_id < wma->max_bssid; vdev_id++) { + if (intf) { + if (intf[vdev_id].type == type) + return vdev_id; + } + } + + return -EFAULT; +} + +void wma_set_vdev_intrabss_fwd(tp_wma_handle wma_handle, + tpDisableIntraBssFwd pdis_intra_fwd) +{ + struct wlan_objmgr_vdev *vdev; + + WMA_LOGD("%s:intra_fwd:vdev(%d) intrabss_dis=%s", + __func__, pdis_intra_fwd->sessionId, + (pdis_intra_fwd->disableintrabssfwd ? "true" : "false")); + + vdev = wma_handle->interfaces[pdis_intra_fwd->sessionId].vdev; + cdp_cfg_vdev_rx_set_intrabss_fwd(cds_get_context(QDF_MODULE_ID_SOC), + pdis_intra_fwd->sessionId, + pdis_intra_fwd->disableintrabssfwd); +} + +/** + * wma_get_pdev_from_scn_handle() - API to get pdev from scn handle + * @scn_handle: opaque wma handle + * + * API to get pdev from scn handle + * + * Return: None + */ +static struct wlan_objmgr_pdev *wma_get_pdev_from_scn_handle(void *scn_handle) +{ + tp_wma_handle wma_handle; + + if (!scn_handle) { + WMA_LOGE("invalid scn handle"); + return NULL; + } + wma_handle = (tp_wma_handle)scn_handle; + + return wma_handle->pdev; +} + +void wma_store_pdev(void *wma_ctx, struct wlan_objmgr_pdev *pdev) +{ + tp_wma_handle wma = (tp_wma_handle)wma_ctx; + QDF_STATUS status; + + status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_LEGACY_WMA_ID); + if (QDF_STATUS_SUCCESS != status) { + wma->pdev = NULL; + return; + } + + wma->pdev = pdev; + + target_if_store_pdev_target_if_ctx(wma_get_pdev_from_scn_handle); + target_pdev_set_wmi_handle(wma->pdev->tgt_if_handle, + wma->wmi_handle); +} + +/** + * wma_vdev_reset_beacon_interval_timer() - reset beacon interval back + * to its original value after the channel switch. + * + * @data: data + * + * Return: void + */ +static void wma_vdev_reset_beacon_interval_timer(void *data) +{ + tp_wma_handle wma; + struct wma_beacon_interval_reset_req *req = + (struct wma_beacon_interval_reset_req *)data; + uint16_t beacon_interval = req->interval; + uint8_t vdev_id = req->vdev_id; + + wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: Failed to get wma", __func__); + goto end; + } + + /* Change the beacon interval back to its original value */ + WMA_LOGE("%s: Change beacon interval back to %d", + __func__, beacon_interval); + wma_update_beacon_interval(wma, vdev_id, beacon_interval); + +end: + qdf_timer_stop(&req->event_timeout); + qdf_timer_free(&req->event_timeout); + qdf_mem_free(req); +} + +int wma_fill_beacon_interval_reset_req(tp_wma_handle wma, uint8_t vdev_id, + uint16_t beacon_interval, uint32_t timeout) +{ + struct wma_beacon_interval_reset_req *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) + return -ENOMEM; + + WMA_LOGD("%s: vdev_id %d ", __func__, vdev_id); + req->vdev_id = vdev_id; + req->interval = beacon_interval; + qdf_timer_init(NULL, &req->event_timeout, + wma_vdev_reset_beacon_interval_timer, req, QDF_TIMER_TYPE_SW); + qdf_timer_start(&req->event_timeout, timeout); + + return 0; +} + +QDF_STATUS wma_set_wlm_latency_level(void *wma_ptr, + struct wlm_latency_level_param *latency_params) +{ + QDF_STATUS ret; + tp_wma_handle wma = (tp_wma_handle)wma_ptr; + + WMA_LOGD("%s: set latency level %d, flags flag 0x%x", + __func__, latency_params->wlm_latency_level, + latency_params->wlm_latency_flags); + + ret = wmi_unified_wlm_latency_level_cmd(wma->wmi_handle, + latency_params); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGW("Failed to set latency level"); + + return ret; +} + +QDF_STATUS wma_add_bss_peer_sta(uint8_t *self_mac, uint8_t *bssid, + bool roam_synch) +{ + uint8_t vdev_id; + tp_wma_handle wma; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("Invalid wma"); + goto err; + } + if (wma_find_vdev_id_by_addr(wma, self_mac, &vdev_id)) { + WMA_LOGE("vdev not found for addr: "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(self_mac)); + goto err; + } + status = wma_create_peer(wma, bssid, WMI_PEER_TYPE_DEFAULT, + vdev_id, roam_synch); +err: + return status; +} + +QDF_STATUS wma_send_vdev_stop(uint8_t vdev_id) +{ + tp_wma_handle wma; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + QDF_STATUS status; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("Invalid wma"); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("%s, vdev_id: %d, pausing tx_ll_queue for VDEV_STOP", + __func__, vdev_id); + cdp_fc_vdev_pause(soc, vdev_id, + OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); + + status = mlme_set_vdev_stop_type( + wma->interfaces[vdev_id].vdev, + WMA_SET_LINK_STATE); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGP(FL("Failed to set wma req msg_type for vdev_id %d"), + vdev_id); + return QDF_STATUS_E_FAILURE; + } + + wma_vdev_set_pause_bit(vdev_id, PAUSE_TYPE_HOST); + + status = wma_send_vdev_stop_to_fw(wma, vdev_id); + if (QDF_IS_STATUS_ERROR(status)) { + struct vdev_stop_response resp_event; + + wma_info("vdev %d Failed to send vdev stop", vdev_id); + resp_event.vdev_id = vdev_id; + mlme_set_connection_fail(wma->interfaces[vdev_id].vdev, false); + wma_handle_vdev_stop_rsp(wma, &resp_event); + } + + /* + * Remove peer, Vdev down and sending set link + * response will be handled in vdev stop response + * handler + */ + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_features.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_features.c new file mode 100644 index 0000000000000000000000000000000000000000..b689169a2204579c7fc64ca04e204d080cda57a1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_features.c @@ -0,0 +1,5562 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_features.c + * This file contains different features related functions like WoW, + * Offloads, TDLS etc. + */ + +/* Header files */ + +#include "cds_ieee80211_common.h" /* ieee80211_frame */ +#include "wma.h" +#include "wma_api.h" +#include "cds_api.h" +#include "wmi_unified_api.h" +#include "wlan_qct_sys.h" +#include "wni_api.h" +#include "ani_global.h" +#include "wmi_unified.h" +#include "wni_cfg.h" +#include +#include + +#include "qdf_nbuf.h" +#include "qdf_types.h" +#include "qdf_mem.h" +#include "qdf_util.h" + +#include "wma_types.h" +#include "lim_api.h" +#include "lim_session_utils.h" +#include "cfg_ucfg_api.h" +#include "cds_utils.h" +#include "cfg_qos.h" +#if !defined(REMOVE_PKT_LOG) +#include "pktlog_ac.h" +#endif /* REMOVE_PKT_LOG */ + +#include "dbglog_host.h" +#include "csr_api.h" +#include "ol_fw.h" + +#include "wma_internal.h" +#include "wma_nan_datapath.h" +#include +#include "wlan_pmo_ucfg_api.h" +#include +#include "wlan_reg_services_api.h" +#include "wlan_roam_debug.h" +#include +#ifdef WLAN_FEATURE_NAN +#include "target_if_nan.h" +#endif +#include "wlan_scan_api.h" +#include +#include "cdp_txrx_host_stats.h" + +/** + * WMA_SET_VDEV_IE_SOURCE_HOST - Flag to identify the source of VDEV SET IE + * command. The value is 0x0 for the VDEV SET IE WMI commands from mobile + * MCL platform. + */ +#define WMA_SET_VDEV_IE_SOURCE_HOST 0x0 + +#if defined(FEATURE_WLAN_DIAG_SUPPORT) +/** + * qdf_wma_wow_wakeup_stats_event()- send wow wakeup stats + * @tp_wma_handle wma: WOW wakeup packet counter + * + * This function sends wow wakeup stats diag event + * + * Return: void. + */ +static inline void qdf_wma_wow_wakeup_stats_event(tp_wma_handle wma) +{ + QDF_STATUS status; + struct wake_lock_stats stats = {0}; + + WLAN_HOST_DIAG_EVENT_DEF(wow_stats, + struct host_event_wlan_powersave_wow_stats); + + status = ucfg_mc_cp_stats_get_psoc_wake_lock_stats(wma->psoc, &stats); + if (QDF_IS_STATUS_ERROR(status)) + return; + qdf_mem_zero(&wow_stats, sizeof(wow_stats)); + + wow_stats.wow_bcast_wake_up_count = stats.bcast_wake_up_count; + wow_stats.wow_ipv4_mcast_wake_up_count = stats.ipv4_mcast_wake_up_count; + wow_stats.wow_ipv6_mcast_wake_up_count = stats.ipv6_mcast_wake_up_count; + wow_stats.wow_ipv6_mcast_ra_stats = stats.ipv6_mcast_ra_stats; + wow_stats.wow_ipv6_mcast_ns_stats = stats.ipv6_mcast_ns_stats; + wow_stats.wow_ipv6_mcast_na_stats = stats.ipv6_mcast_na_stats; + wow_stats.wow_pno_match_wake_up_count = stats.pno_match_wake_up_count; + wow_stats.wow_pno_complete_wake_up_count = + stats.pno_complete_wake_up_count; + wow_stats.wow_gscan_wake_up_count = stats.gscan_wake_up_count; + wow_stats.wow_low_rssi_wake_up_count = stats.low_rssi_wake_up_count; + wow_stats.wow_rssi_breach_wake_up_count = + stats.rssi_breach_wake_up_count; + wow_stats.wow_icmpv4_count = stats.icmpv4_count; + wow_stats.wow_icmpv6_count = stats.icmpv6_count; + wow_stats.wow_oem_response_wake_up_count = + stats.oem_response_wake_up_count; + + WLAN_HOST_DIAG_EVENT_REPORT(&wow_stats, EVENT_WLAN_POWERSAVE_WOW_STATS); +} +#else +static inline void qdf_wma_wow_wakeup_stats_event(tp_wma_handle wma) +{ + return; +} +#endif + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +/** + * wma_wake_reason_auto_shutdown() - to post auto shutdown event to sme + * + * Return: 0 for success or error code + */ +static int wma_wake_reason_auto_shutdown(void) +{ + QDF_STATUS qdf_status; + struct scheduler_msg sme_msg = { 0 }; + + sme_msg.type = eWNI_SME_AUTO_SHUTDOWN_IND; + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + WMA_LOGE("Fail to post eWNI_SME_AUTO_SHUTDOWN_IND msg to SME"); + + return qdf_status_to_os_return(qdf_status); +} +#else +static inline int wma_wake_reason_auto_shutdown(void) +{ + return 0; +} +#endif /* FEATURE_WLAN_AUTO_SHUTDOWN */ + +#ifdef FEATURE_WLAN_SCAN_PNO +static int wma_wake_reason_nlod(t_wma_handle *wma, uint8_t vdev_id) +{ + wmi_nlo_event nlo_event = { .vdev_id = vdev_id }; + WMI_NLO_MATCH_EVENTID_param_tlvs param = { .fixed_param = &nlo_event }; + + return target_if_nlo_match_event_handler(wma, (uint8_t *)¶m, + sizeof(param)); +} +#else +static inline int wma_wake_reason_nlod(t_wma_handle *wma, uint8_t vdev_id) +{ + return 0; +} +#endif /* FEATURE_WLAN_SCAN_PNO */ + +#ifdef WLAN_FEATURE_NAN +/** + * wma_nan_rsp_handler_callback() - call NAN Discovery event handler + * @handle: wma handle + * @event: event buffer + * @len: buffer length + * + * Return: 0 for success or error code + */ +static int wma_nan_rsp_handler_callback(void *handle, uint8_t *event, + uint32_t len) +{ + return target_if_nan_rsp_handler(handle, event, len); +} +#else +static inline int wma_nan_rsp_handler_callback(void *handle, uint8_t *event, + uint32_t len) +{ + return 0; +} +#endif + +/** + * wma_get_snr() - get RSSI from fw + * @psnr_req: request params + * + * Return: QDF status + */ +QDF_STATUS wma_get_snr(tAniGetSnrReq *psnr_req) +{ + tAniGetSnrReq *psnr_req_bkp; + tp_wma_handle wma_handle = NULL; + struct wma_txrx_node *intr; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s : Failed to get wma_handle", __func__); + return QDF_STATUS_E_FAULT; + } + + intr = &wma_handle->interfaces[psnr_req->sessionId]; + /* command is in progress */ + if (intr->psnr_req) { + WMA_LOGE("%s : previous snr request is pending", __func__); + return QDF_STATUS_SUCCESS; + } + + psnr_req_bkp = qdf_mem_malloc(sizeof(tAniGetSnrReq)); + if (!psnr_req_bkp) + return QDF_STATUS_E_NOMEM; + + qdf_mem_zero(psnr_req_bkp, sizeof(tAniGetSnrReq)); + psnr_req_bkp->pDevContext = psnr_req->pDevContext; + psnr_req_bkp->snrCallback = psnr_req->snrCallback; + intr->psnr_req = (void *)psnr_req_bkp; + + if (wmi_unified_snr_cmd(wma_handle->wmi_handle, + psnr_req->sessionId)) { + WMA_LOGE("Failed to send host stats request to fw"); + qdf_mem_free(psnr_req_bkp); + intr->psnr_req = NULL; + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +void wma_get_rx_retry_cnt(struct mac_context *mac, uint8_t vdev_id, + uint8_t *mac_addr) +{ + struct cdp_peer_stats *peer_stats; + QDF_STATUS status; + + peer_stats = qdf_mem_malloc(sizeof(*peer_stats)); + if (!peer_stats) { + wma_err("Failed to allocate memory for peer stats"); + return; + } + + status = cdp_host_get_peer_stats(cds_get_context(QDF_MODULE_ID_SOC), + vdev_id, mac_addr, peer_stats); + + if (QDF_IS_STATUS_ERROR(status)) { + wma_err("Failed to get peer stats"); + goto exit; + } + + mac->rx_retry_cnt = peer_stats->rx.rx_retries; + wma_debug("Rx retry count %d, Peer" QDF_MAC_ADDR_FMT, mac->rx_retry_cnt, + QDF_MAC_ADDR_REF(mac_addr)); + +exit: + qdf_mem_free(peer_stats); +} + +/** + * wma_process_link_status_req() - process link status request from UMAC + * @wma: wma handle + * @pGetLinkStatus: get link params + * + * Return: none + */ +void wma_process_link_status_req(tp_wma_handle wma, + tAniGetLinkStatus *pGetLinkStatus) +{ + struct link_status_params cmd = {0}; + struct wma_txrx_node *iface = + &wma->interfaces[pGetLinkStatus->sessionId]; + + if (iface->plink_status_req) { + WMA_LOGE("%s:previous link status request is pending,deleting the new request", + __func__); + qdf_mem_free(pGetLinkStatus); + return; + } + + iface->plink_status_req = pGetLinkStatus; + cmd.vdev_id = pGetLinkStatus->sessionId; + if (wmi_unified_link_status_req_cmd(wma->wmi_handle, &cmd)) { + WMA_LOGE("Failed to send WMI link status request to fw"); + iface->plink_status_req = NULL; + goto end; + } + + return; + +end: + wma_post_link_status(pGetLinkStatus, LINK_STATUS_LEGACY); +} + +#ifdef WLAN_FEATURE_TSF +/** + * wma_vdev_tsf_handler() - handle tsf event indicated by FW + * @handle: wma context + * @data: event buffer + * @data len: length of event buffer + * + * Return: 0 on success + */ +int wma_vdev_tsf_handler(void *handle, uint8_t *data, uint32_t data_len) +{ + struct scheduler_msg tsf_msg = {0}; + WMI_VDEV_TSF_REPORT_EVENTID_param_tlvs *param_buf; + wmi_vdev_tsf_report_event_fixed_param *tsf_event; + struct stsf *ptsf; + + if (!data) { + WMA_LOGE("%s: invalid pointer", __func__); + return -EINVAL; + } + ptsf = qdf_mem_malloc(sizeof(*ptsf)); + if (!ptsf) + return -ENOMEM; + + param_buf = (WMI_VDEV_TSF_REPORT_EVENTID_param_tlvs *)data; + tsf_event = param_buf->fixed_param; + + ptsf->vdev_id = tsf_event->vdev_id; + ptsf->tsf_low = tsf_event->tsf_low; + ptsf->tsf_high = tsf_event->tsf_high; + ptsf->soc_timer_low = tsf_event->qtimer_low; + ptsf->soc_timer_high = tsf_event->qtimer_high; + ptsf->global_tsf_low = tsf_event->wlan_global_tsf_low; + ptsf->global_tsf_high = tsf_event->wlan_global_tsf_high; + wma_nofl_debug("receive WMI_VDEV_TSF_REPORT_EVENTID on %d, tsf: %d %d", + ptsf->vdev_id, ptsf->tsf_low, ptsf->tsf_high); + + wma_nofl_debug("g_tsf: %d %d; soc_timer: %d %d", + ptsf->global_tsf_low, ptsf->global_tsf_high, + ptsf->soc_timer_low, ptsf->soc_timer_high); + tsf_msg.type = eWNI_SME_TSF_EVENT; + tsf_msg.bodyptr = ptsf; + tsf_msg.bodyval = 0; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &tsf_msg)) { + qdf_mem_free(ptsf); + return -EINVAL; + } + return 0; +} + +#ifdef QCA_WIFI_3_0 +#define TSF_FW_ACTION_CMD TSF_TSTAMP_QTIMER_CAPTURE_REQ +#else +#define TSF_FW_ACTION_CMD TSF_TSTAMP_CAPTURE_REQ +#endif +/** + * wma_capture_tsf() - send wmi to fw to capture tsf + * @wma_handle: wma handler + * @vdev_id: vdev id + * + * Return: wmi send state + */ +QDF_STATUS wma_capture_tsf(tp_wma_handle wma_handle, uint32_t vdev_id) +{ + QDF_STATUS status; + wmi_buf_t buf; + wmi_vdev_tsf_tstamp_action_cmd_fixed_param *cmd; + int len = sizeof(*cmd); + + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_vdev_tsf_tstamp_action_cmd_fixed_param *) wmi_buf_data(buf); + cmd->vdev_id = vdev_id; + cmd->tsf_action = TSF_FW_ACTION_CMD; + WMA_LOGD("%s :vdev_id %u, tsf_cmd: %d", __func__, cmd->vdev_id, + cmd->tsf_action); + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_vdev_tsf_tstamp_action_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_vdev_tsf_tstamp_action_cmd_fixed_param)); + + status = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_VDEV_TSF_TSTAMP_ACTION_CMDID); + if (QDF_IS_STATUS_ERROR(status)) + wmi_buf_free(buf); + + return status; +} + +/** + * wma_reset_tsf_gpio() - send wmi to fw to reset GPIO + * @wma_handle: wma handler + * @vdev_id: vdev id + * + * Return: wmi send state + */ +QDF_STATUS wma_reset_tsf_gpio(tp_wma_handle wma_handle, uint32_t vdev_id) +{ + QDF_STATUS status; + wmi_buf_t buf; + wmi_vdev_tsf_tstamp_action_cmd_fixed_param *cmd; + int len = sizeof(*cmd); + uint8_t *buf_ptr; + + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (uint8_t *) wmi_buf_data(buf); + cmd = (wmi_vdev_tsf_tstamp_action_cmd_fixed_param *) buf_ptr; + cmd->vdev_id = vdev_id; + cmd->tsf_action = TSF_TSTAMP_CAPTURE_RESET; + + WMA_LOGD("%s :vdev_id %u, TSF_TSTAMP_CAPTURE_RESET", __func__, + cmd->vdev_id); + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_vdev_tsf_tstamp_action_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_vdev_tsf_tstamp_action_cmd_fixed_param)); + + status = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_VDEV_TSF_TSTAMP_ACTION_CMDID); + if (QDF_IS_STATUS_ERROR(status)) + wmi_buf_free(buf); + + return status; +} + +/** + * wma_set_tsf_gpio_pin() - send wmi cmd to configure gpio pin + * @handle: wma handler + * @pin: GPIO pin id + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_set_tsf_gpio_pin(WMA_HANDLE handle, uint32_t pin) +{ + tp_wma_handle wma = (tp_wma_handle)handle; + struct pdev_params pdev_param = {0}; + int32_t ret; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not set gpio", __func__); + return QDF_STATUS_E_INVAL; + } + + WMA_LOGD("%s: set tsf gpio pin: %d", __func__, pin); + + pdev_param.param_id = WMI_PDEV_PARAM_WNTS_CONFIG; + pdev_param.param_value = pin; + ret = wmi_unified_pdev_param_send(wma->wmi_handle, + &pdev_param, + WMA_WILDCARD_PDEV_ID); + if (ret) { + WMA_LOGE("%s: Failed to set tsf gpio pin (%d)", __func__, ret); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * wma_set_wisa_params(): Set WISA features related params in FW + * @wma_handle: WMA handle + * @wisa: Pointer to WISA param struct + * + * Return: CDF status + */ +QDF_STATUS wma_set_wisa_params(tp_wma_handle wma_handle, + struct sir_wisa_params *wisa) +{ + QDF_STATUS status; + wmi_buf_t buf; + wmi_vdev_wisa_cmd_fixed_param *cmd; + int len = sizeof(*cmd); + + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_vdev_wisa_cmd_fixed_param *) wmi_buf_data(buf); + cmd->wisa_mode = wisa->mode; + cmd->vdev_id = wisa->vdev_id; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_vdev_wisa_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_vdev_wisa_cmd_fixed_param)); + + status = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_VDEV_WISA_CMDID); + if (QDF_IS_STATUS_ERROR(status)) + wmi_buf_free(buf); + + return status; +} + +/** + * wma_process_dhcp_ind() - process dhcp indication from SME + * @wma_handle: wma handle + * @ta_dhcp_ind: DHCP indication + * + * Return: QDF Status + */ +QDF_STATUS wma_process_dhcp_ind(WMA_HANDLE handle, + tAniDHCPInd *ta_dhcp_ind) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + uint8_t vdev_id; + wmi_peer_set_param_cmd_fixed_param peer_set_param_fp = {0}; + + if (!wma_handle) { + WMA_LOGE("%s : wma_handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!ta_dhcp_ind) { + WMA_LOGE("%s : DHCP indication is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (wma_find_vdev_id_by_addr(wma_handle, + ta_dhcp_ind->adapterMacAddr.bytes, + &vdev_id)) { + WMA_LOGE("%s: Failed to find vdev id for DHCP indication", + __func__); + return QDF_STATUS_E_FAILURE; + } + + wma_debug("WMA --> WMI_PEER_SET_PARAM triggered by DHCP, msgType=%s, device_mode=%d, macAddr=" QDF_MAC_ADDR_FMT, + ta_dhcp_ind->msgType == WMA_DHCP_START_IND ? + "WMA_DHCP_START_IND" : "WMA_DHCP_STOP_IND", + ta_dhcp_ind->device_mode, + QDF_MAC_ADDR_REF(ta_dhcp_ind->peerMacAddr.bytes)); + + /* fill in values */ + peer_set_param_fp.vdev_id = vdev_id; + peer_set_param_fp.param_id = WMI_PEER_CRIT_PROTO_HINT_ENABLED; + if (WMA_DHCP_START_IND == ta_dhcp_ind->msgType) + peer_set_param_fp.param_value = 1; + else + peer_set_param_fp.param_value = 0; + WMI_CHAR_ARRAY_TO_MAC_ADDR(ta_dhcp_ind->peerMacAddr.bytes, + &peer_set_param_fp.peer_macaddr); + + return wmi_unified_process_dhcp_ind(wma_handle->wmi_handle, + &peer_set_param_fp); +} + +enum wlan_phymode wma_chan_phy_mode(uint32_t freq, enum phy_ch_width chan_width, + uint8_t dot11_mode) +{ + enum wlan_phymode phymode = WLAN_PHYMODE_AUTO; + uint16_t bw_val = wlan_reg_get_bw_value(chan_width); + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + WMA_LOGE("%s : wma_handle is NULL", __func__); + return WLAN_PHYMODE_AUTO; + } + + if (chan_width >= CH_WIDTH_INVALID) { + wma_err_rl("%s : Invalid channel width %d", __func__, chan_width); + return WLAN_PHYMODE_AUTO; + } + + if (wlan_reg_is_24ghz_ch_freq(freq)) { + if (((CH_WIDTH_5MHZ == chan_width) || + (CH_WIDTH_10MHZ == chan_width)) && + ((MLME_DOT11_MODE_11B == dot11_mode) || + (MLME_DOT11_MODE_11G == dot11_mode) || + (MLME_DOT11_MODE_11N == dot11_mode) || + (MLME_DOT11_MODE_ALL == dot11_mode) || + (MLME_DOT11_MODE_11AC == dot11_mode) || + (MLME_DOT11_MODE_11AX == dot11_mode))) + phymode = WLAN_PHYMODE_11G; + else { + switch (dot11_mode) { + case MLME_DOT11_MODE_11B: + if ((bw_val == 20) || (bw_val == 40)) + phymode = WLAN_PHYMODE_11B; + break; + case MLME_DOT11_MODE_11G: + if ((bw_val == 20) || (bw_val == 40)) + phymode = WLAN_PHYMODE_11G; + break; + case MLME_DOT11_MODE_11G_ONLY: + if ((bw_val == 20) || (bw_val == 40)) + phymode = WLAN_PHYMODE_11G_ONLY; + break; + case MLME_DOT11_MODE_11N: + case MLME_DOT11_MODE_11N_ONLY: + if (bw_val == 20) + phymode = WLAN_PHYMODE_11NG_HT20; + else if (bw_val == 40) + phymode = WLAN_PHYMODE_11NG_HT40; + break; + case MLME_DOT11_MODE_ALL: + case MLME_DOT11_MODE_11AC: + case MLME_DOT11_MODE_11AC_ONLY: + if (bw_val == 20) + phymode = WLAN_PHYMODE_11AC_VHT20_2G; + else if (bw_val == 40) + phymode = WLAN_PHYMODE_11AC_VHT40_2G; + break; + case MLME_DOT11_MODE_11AX: + case MLME_DOT11_MODE_11AX_ONLY: + if (20 == bw_val) + phymode = WLAN_PHYMODE_11AXG_HE20; + else if (40 == bw_val) + phymode = WLAN_PHYMODE_11AXG_HE40; + break; + default: + break; + } + } + } else if (wlan_reg_is_dsrc_freq(freq)) + phymode = WLAN_PHYMODE_11A; + else { + if (((CH_WIDTH_5MHZ == chan_width) || + (CH_WIDTH_10MHZ == chan_width)) && + ((MLME_DOT11_MODE_11A == dot11_mode) || + (MLME_DOT11_MODE_11N == dot11_mode) || + (MLME_DOT11_MODE_ALL == dot11_mode) || + (MLME_DOT11_MODE_11AC == dot11_mode) || + (MLME_DOT11_MODE_11AX == dot11_mode))) + phymode = WLAN_PHYMODE_11A; + else { + switch (dot11_mode) { + case MLME_DOT11_MODE_11A: + if (0 < bw_val) + phymode = WLAN_PHYMODE_11A; + break; + case MLME_DOT11_MODE_11N: + case MLME_DOT11_MODE_11N_ONLY: + if (bw_val == 20) + phymode = WLAN_PHYMODE_11NA_HT20; + else if (40 <= bw_val) + phymode = WLAN_PHYMODE_11NA_HT40; + break; + case MLME_DOT11_MODE_ALL: + case MLME_DOT11_MODE_11AC: + case MLME_DOT11_MODE_11AC_ONLY: + if (bw_val == 20) + phymode = WLAN_PHYMODE_11AC_VHT20; + else if (bw_val == 40) + phymode = WLAN_PHYMODE_11AC_VHT40; + else if (bw_val == 80) + phymode = WLAN_PHYMODE_11AC_VHT80; + else if (chan_width == CH_WIDTH_160MHZ) + phymode = WLAN_PHYMODE_11AC_VHT160; + else if (chan_width == CH_WIDTH_80P80MHZ) + phymode = WLAN_PHYMODE_11AC_VHT80_80; + break; + case MLME_DOT11_MODE_11AX: + case MLME_DOT11_MODE_11AX_ONLY: + if (20 == bw_val) + phymode = WLAN_PHYMODE_11AXA_HE20; + else if (40 == bw_val) + phymode = WLAN_PHYMODE_11AXA_HE40; + else if (80 == bw_val) + phymode = WLAN_PHYMODE_11AXA_HE80; + else if (CH_WIDTH_160MHZ == chan_width) + phymode = WLAN_PHYMODE_11AXA_HE160; + else if (CH_WIDTH_80P80MHZ == chan_width) + phymode = WLAN_PHYMODE_11AXA_HE80_80; + break; + default: + break; + } + } + } + + WMA_LOGD("%s: phymode %d freq %d ch_width %d dot11_mode %d", + __func__, phymode, freq, chan_width, dot11_mode); + + QDF_ASSERT(phymode != WLAN_PHYMODE_AUTO); + return phymode; +} + +/** + * wma_get_link_speed() -send command to get linkspeed + * @handle: wma handle + * @pLinkSpeed: link speed info + * + * Return: QDF status + */ +QDF_STATUS wma_get_link_speed(WMA_HANDLE handle, + struct link_speed_info *pLinkSpeed) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + wmi_mac_addr peer_macaddr; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue get link speed cmd", + __func__); + return QDF_STATUS_E_INVAL; + } + if (!wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_estimate_linkspeed)) { + WMA_LOGE("%s: Linkspeed feature bit not enabled Sending value 0 as link speed.", + __func__); + wma_send_link_speed(0); + return QDF_STATUS_E_FAILURE; + } + /* Copy the peer macaddress to the wma buffer */ + WMI_CHAR_ARRAY_TO_MAC_ADDR(pLinkSpeed->peer_macaddr.bytes, + &peer_macaddr); + WMA_LOGD("%s: pLinkSpeed->peerMacAddr: "QDF_MAC_ADDR_FMT", peer_macaddr.mac_addr31to0: 0x%x, peer_macaddr.mac_addr47to32: 0x%x", + __func__, QDF_MAC_ADDR_REF(pLinkSpeed->peer_macaddr.bytes), + peer_macaddr.mac_addr31to0, + peer_macaddr.mac_addr47to32); + if (wmi_unified_get_link_speed_cmd(wma_handle->wmi_handle, + peer_macaddr)) { + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_get_peer_info_ext(WMA_HANDLE handle, + struct sir_peer_info_ext_req *peer_info_req) +{ + tp_wma_handle wma_handle = (tp_wma_handle)handle; + wmi_request_peer_stats_info_cmd_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint32_t len; + uint8_t *buf_ptr; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue get rssi", + __func__); + return QDF_STATUS_E_INVAL; + } + + WMA_LOGI("%s send WMI_REQUEST_PEER_STATS_INFO_CMDID", __func__); + + len = sizeof(wmi_request_peer_stats_info_cmd_fixed_param); + wmi_buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!wmi_buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (uint8_t *)wmi_buf_data(wmi_buf); + + cmd = (wmi_request_peer_stats_info_cmd_fixed_param *)buf_ptr; + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_request_peer_stats_info_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_request_peer_stats_info_cmd_fixed_param)); + cmd->vdev_id = peer_info_req->sessionid; + cmd->request_type = WMI_REQUEST_ONE_PEER_STATS_INFO; + wma_handle->get_one_peer_info = true; + WMI_CHAR_ARRAY_TO_MAC_ADDR(peer_info_req->peer_macaddr.bytes, + &cmd->peer_macaddr); + cmd->reset_after_request = peer_info_req->reset_after_request; + + if (wmi_unified_cmd_send(wma_handle->wmi_handle, wmi_buf, len, + WMI_REQUEST_PEER_STATS_INFO_CMDID)) { + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGI("%s vdev_id %d, mac "QDF_MAC_ADDR_FMT", req_type %x, reset %x", + __func__, + cmd->vdev_id, + QDF_MAC_ADDR_REF(peer_info_req->peer_macaddr.bytes), + cmd->request_type, + cmd->reset_after_request); + + qdf_mem_copy(&(wma_handle->peer_macaddr), + &(peer_info_req->peer_macaddr), + QDF_MAC_ADDR_SIZE); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_get_isolation(tp_wma_handle wma) +{ + wmi_coex_get_antenna_isolation_cmd_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint32_t len; + uint8_t *buf_ptr; + + WMA_LOGD("%s: get isolation", __func__); + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue get isolation", + __func__); + return QDF_STATUS_E_INVAL; + } + + len = sizeof(wmi_coex_get_antenna_isolation_cmd_fixed_param); + wmi_buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!wmi_buf) { + WMA_LOGE("%s: wmi_buf_alloc failed", __func__); + return QDF_STATUS_E_NOMEM; + } + buf_ptr = (uint8_t *)wmi_buf_data(wmi_buf); + + cmd = (wmi_coex_get_antenna_isolation_cmd_fixed_param *)buf_ptr; + WMITLV_SET_HDR( + &cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_coex_get_antenna_isolation_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_coex_get_antenna_isolation_cmd_fixed_param)); + + if (wmi_unified_cmd_send(wma->wmi_handle, wmi_buf, len, + WMI_COEX_GET_ANTENNA_ISOLATION_CMDID)) { + WMA_LOGE("Failed to get isolation request from fw"); + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_add_beacon_filter() - Issue WMI command to set beacon filter + * @wma: wma handler + * @filter_params: beacon_filter_param to set + * + * Return: Return QDF_STATUS + */ +QDF_STATUS wma_add_beacon_filter(WMA_HANDLE handle, + struct beacon_filter_param *filter_params) +{ + int i; + wmi_buf_t wmi_buf; + u_int8_t *buf; + A_UINT32 *ie_map; + int ret; + struct wma_txrx_node *iface; + tp_wma_handle wma = (tp_wma_handle) handle; + + wmi_add_bcn_filter_cmd_fixed_param *cmd; + int len = sizeof(wmi_add_bcn_filter_cmd_fixed_param); + + len += WMI_TLV_HDR_SIZE; + len += BCN_FLT_MAX_ELEMS_IE_LIST*sizeof(A_UINT32); + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue set beacon filter", + __func__); + return QDF_STATUS_E_INVAL; + } + + iface = &wma->interfaces[filter_params->vdev_id]; + qdf_mem_copy(&iface->beacon_filter, filter_params, + sizeof(struct beacon_filter_param)); + iface->beacon_filter_enabled = true; + + wmi_buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!wmi_buf) + return QDF_STATUS_E_NOMEM; + + buf = (u_int8_t *) wmi_buf_data(wmi_buf); + + cmd = (wmi_add_bcn_filter_cmd_fixed_param *)wmi_buf_data(wmi_buf); + cmd->vdev_id = filter_params->vdev_id; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_add_bcn_filter_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_add_bcn_filter_cmd_fixed_param)); + + buf += sizeof(wmi_add_bcn_filter_cmd_fixed_param); + + WMITLV_SET_HDR(buf, WMITLV_TAG_ARRAY_UINT32, + (BCN_FLT_MAX_ELEMS_IE_LIST * sizeof(u_int32_t))); + + ie_map = (A_UINT32 *)(buf + WMI_TLV_HDR_SIZE); + for (i = 0; i < BCN_FLT_MAX_ELEMS_IE_LIST; i++) { + ie_map[i] = filter_params->ie_map[i]; + WMA_LOGD("beacon filter ie map = %u", ie_map[i]); + } + + ret = wmi_unified_cmd_send(wma->wmi_handle, wmi_buf, len, + WMI_ADD_BCN_FILTER_CMDID); + if (ret) { + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** +* wma_remove_beacon_filter() - Issue WMI command to remove beacon filter +* @wma: wma handler +* @filter_params: beacon_filter_params +* +* Return: Return QDF_STATUS +*/ +QDF_STATUS wma_remove_beacon_filter(WMA_HANDLE handle, + struct beacon_filter_param *filter_params) +{ + wmi_buf_t buf; + tp_wma_handle wma = (tp_wma_handle) handle; + wmi_rmv_bcn_filter_cmd_fixed_param *cmd; + int len = sizeof(wmi_rmv_bcn_filter_cmd_fixed_param); + int ret; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, cannot issue remove beacon filter", + __func__); + return QDF_STATUS_E_INVAL; + } + + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_rmv_bcn_filter_cmd_fixed_param *)wmi_buf_data(buf); + cmd->vdev_id = filter_params->vdev_id; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_rmv_bcn_filter_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_rmv_bcn_filter_cmd_fixed_param)); + + ret = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_RMV_BCN_FILTER_CMDID); + if (ret) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_send_adapt_dwelltime_params() - send adaptive dwelltime configuration + * params to firmware + * @wma_handle: wma handler + * @dwelltime_params: pointer to dwelltime_params + * + * Return: QDF_STATUS_SUCCESS on success and QDF failure reason code for failure + */ +QDF_STATUS wma_send_adapt_dwelltime_params(WMA_HANDLE handle, + struct adaptive_dwelltime_params *dwelltime_params) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + struct wmi_adaptive_dwelltime_params wmi_param = {0}; + int32_t err; + + wmi_param.is_enabled = dwelltime_params->is_enabled; + wmi_param.dwelltime_mode = dwelltime_params->dwelltime_mode; + wmi_param.lpf_weight = dwelltime_params->lpf_weight; + wmi_param.passive_mon_intval = dwelltime_params->passive_mon_intval; + wmi_param.wifi_act_threshold = dwelltime_params->wifi_act_threshold; + err = wmi_unified_send_adapt_dwelltime_params_cmd(wma_handle-> + wmi_handle, &wmi_param); + if (err) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_send_dbs_scan_selection_params(WMA_HANDLE handle, + struct wmi_dbs_scan_sel_params *dbs_scan_params) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + int32_t err; + + err = wmi_unified_send_dbs_scan_sel_params_cmd(wma_handle-> + wmi_handle, dbs_scan_params); + if (err) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_unified_fw_profiling_cmd() - send FW profiling cmd to WLAN FW + * @wma: wma handle + * @cmd: Profiling command index + * @value1: parameter1 value + * @value2: parameter2 value + * + * Return: 0 for success else error code + */ +QDF_STATUS wma_unified_fw_profiling_cmd(wmi_unified_t wmi_handle, + uint32_t cmd, uint32_t value1, uint32_t value2) +{ + int ret; + + ret = wmi_unified_fw_profiling_data_cmd(wmi_handle, cmd, + value1, value2); + if (ret) { + WMA_LOGE("enable cmd Failed for id %d value %d", + value1, value2); + return ret; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_wow_set_wake_time() - set timer pattern tlv, so that firmware will wake + * up host after specified time is elapsed + * @wma_handle: wma handle + * @vdev_id: vdev id + * @cookie: value to identify reason why host set up wake call. + * @time: time in ms + * + * Return: QDF status + */ +static QDF_STATUS wma_wow_set_wake_time(WMA_HANDLE wma_handle, uint8_t vdev_id, + uint32_t cookie, uint32_t time) +{ + int ret; + tp_wma_handle wma = (tp_wma_handle)wma_handle; + + WMA_LOGD(FL("send timer patter with time: %d and vdev = %d to fw"), + time, vdev_id); + ret = wmi_unified_wow_timer_pattern_cmd(wma->wmi_handle, vdev_id, + cookie, time); + if (ret) { + WMA_LOGE(FL("Failed to send timer patter to fw")); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD +/** + * wma_check_and_set_wake_timer(): checks all interfaces and if any interface + * has install_key pending, sets timer pattern in fw to wake up host after + * specified time has elapsed. + * @wma: wma handle + * @time: time after which host wants to be awaken. + * + * Return: None + */ +void wma_check_and_set_wake_timer(uint32_t time) +{ + int i; + struct wma_txrx_node *iface; + bool is_set_key_in_progress = false; + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + WMA_LOGE("%s: WMA is closed", + __func__); + return; + } + + if (!wmi_service_enabled(wma->wmi_handle, + wmi_service_wow_wakeup_by_timer_pattern)) { + WMA_LOGD("TIME_PATTERN is not enabled"); + return; + } + + for (i = 0; i < wma->max_bssid; i++) { + iface = &wma->interfaces[i]; + if (iface->vdev_active && iface->is_waiting_for_key) { + /* + * right now cookie is dont care, since FW disregards + * that. + */ + is_set_key_in_progress = true; + wma_wow_set_wake_time((WMA_HANDLE)wma, i, 0, time); + break; + } + } + + if (!is_set_key_in_progress) + WMA_LOGD("set key not in progress for any vdev"); +} + +/** + * wma_unified_csa_offload_enable() - sen CSA offload enable command + * @wma: wma handle + * @vdev_id: vdev id + * + * Return: 0 for success or error code + */ +int wma_unified_csa_offload_enable(tp_wma_handle wma, uint8_t vdev_id) +{ + if (wmi_unified_csa_offload_enable(wma->wmi_handle, + vdev_id)) { + WMA_LOGP("%s: Failed to send CSA offload enable command", + __func__); + return -EIO; + } + + return 0; +} +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ + +static uint8_t * +wma_parse_ch_switch_wrapper_ie(uint8_t *ch_wr_ie, uint8_t sub_ele_id) +{ + uint8_t len = 0, sub_ele_len = 0; + struct ie_header *ele; + + ele = (struct ie_header *)ch_wr_ie; + if (ele->ie_id != WLAN_ELEMID_CHAN_SWITCH_WRAP || + ele->ie_len == 0) + return NULL; + + len = ele->ie_len; + ele = (struct ie_header *)(ch_wr_ie + sizeof(struct ie_header)); + + while (len > 0) { + sub_ele_len = sizeof(struct ie_header) + ele->ie_len; + len -= sub_ele_len; + if (ele->ie_id == sub_ele_id) + return (uint8_t *)ele; + + ele = (struct ie_header *)((uint8_t *)ele + sub_ele_len); + } + + return NULL; +} + +/** + * wma_csa_offload_handler() - CSA event handler + * @handle: wma handle + * @event: event buffer + * @len: buffer length + * + * This event is sent by firmware when it receives CSA IE. + * + * Return: 0 for success or error code + */ +int wma_csa_offload_handler(void *handle, uint8_t *event, uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_CSA_HANDLING_EVENTID_param_tlvs *param_buf; + wmi_csa_event_fixed_param *csa_event; + uint8_t bssid[QDF_MAC_ADDR_SIZE]; + uint8_t vdev_id = 0; + uint8_t cur_chan = 0; + struct ieee80211_channelswitch_ie *csa_ie; + struct csa_offload_params *csa_offload_event; + struct ieee80211_extendedchannelswitch_ie *xcsa_ie; + struct ieee80211_ie_wide_bw_switch *wb_ie; + struct wma_txrx_node *intr = wma->interfaces; + + param_buf = (WMI_CSA_HANDLING_EVENTID_param_tlvs *) event; + + WMA_LOGD("%s: Enter", __func__); + if (!param_buf) { + WMA_LOGE("Invalid csa event buffer"); + return -EINVAL; + } + csa_event = param_buf->fixed_param; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&csa_event->i_addr2, &bssid[0]); + + if (wma_find_vdev_id_by_bssid(wma, bssid, &vdev_id)) { + WMA_LOGE("Invalid bssid received %s:%d", __func__, __LINE__); + return -EINVAL; + } + + csa_offload_event = qdf_mem_malloc(sizeof(*csa_offload_event)); + if (!csa_offload_event) + return -EINVAL; + + if (wma->interfaces[vdev_id].roaming_in_progress || + wma->interfaces[vdev_id].roam_synch_in_progress) { + WMA_LOGE("Roaming in progress for vdev %d, ignore csa_offload_event", + vdev_id); + qdf_mem_free(csa_offload_event); + return -EINVAL; + } + + qdf_mem_zero(csa_offload_event, sizeof(*csa_offload_event)); + qdf_mem_copy(csa_offload_event->bssId, &bssid, QDF_MAC_ADDR_SIZE); + + if (csa_event->ies_present_flag & WMI_CSA_IE_PRESENT) { + csa_ie = (struct ieee80211_channelswitch_ie *) + (&csa_event->csa_ie[0]); + csa_offload_event->channel = csa_ie->newchannel; + csa_offload_event->csa_chan_freq = + wlan_reg_legacy_chan_to_freq(wma->pdev, + csa_ie->newchannel); + csa_offload_event->switch_mode = csa_ie->switchmode; + } else if (csa_event->ies_present_flag & WMI_XCSA_IE_PRESENT) { + xcsa_ie = (struct ieee80211_extendedchannelswitch_ie *) + (&csa_event->xcsa_ie[0]); + csa_offload_event->channel = xcsa_ie->newchannel; + csa_offload_event->switch_mode = xcsa_ie->switchmode; + csa_offload_event->new_op_class = xcsa_ie->newClass; + if (wlan_reg_is_6ghz_op_class(wma->pdev, xcsa_ie->newClass)) { + csa_offload_event->csa_chan_freq = + wlan_reg_chan_band_to_freq + (wma->pdev, xcsa_ie->newchannel, + BIT(REG_BAND_6G)); + } else { + csa_offload_event->csa_chan_freq = + wlan_reg_legacy_chan_to_freq + (wma->pdev, xcsa_ie->newchannel); + } + } else { + WMA_LOGE("CSA Event error: No CSA IE present"); + qdf_mem_free(csa_offload_event); + return -EINVAL; + } + + if (csa_event->ies_present_flag & WMI_WBW_IE_PRESENT) { + wb_ie = (struct ieee80211_ie_wide_bw_switch *) + (&csa_event->wb_ie[0]); + csa_offload_event->new_ch_width = wb_ie->new_ch_width; + csa_offload_event->new_ch_freq_seg1 = wb_ie->new_ch_freq_seg1; + csa_offload_event->new_ch_freq_seg2 = wb_ie->new_ch_freq_seg2; + } else if (csa_event->ies_present_flag & + WMI_CSWRAP_IE_EXTENDED_PRESENT) { + wb_ie = (struct ieee80211_ie_wide_bw_switch *) + wma_parse_ch_switch_wrapper_ie( + (uint8_t *)&csa_event->cswrap_ie_extended, + WLAN_ELEMID_WIDE_BAND_CHAN_SWITCH); + if (wb_ie) { + csa_offload_event->new_ch_width = wb_ie->new_ch_width; + csa_offload_event->new_ch_freq_seg1 = + wb_ie->new_ch_freq_seg1; + csa_offload_event->new_ch_freq_seg2 = + wb_ie->new_ch_freq_seg2; + csa_event->ies_present_flag |= WMI_WBW_IE_PRESENT; + } + } + + csa_offload_event->ies_present_flag = csa_event->ies_present_flag; + + WMA_LOGD("CSA: BSSID "QDF_MAC_ADDR_FMT" chan %d freq %d flag 0x%x width = %d freq1 = %d freq2 = %d op class = %d", + QDF_MAC_ADDR_REF(csa_offload_event->bssId), + csa_offload_event->channel, + csa_offload_event->csa_chan_freq, + csa_event->ies_present_flag, + csa_offload_event->new_ch_width, + csa_offload_event->new_ch_freq_seg1, + csa_offload_event->new_ch_freq_seg2, + csa_offload_event->new_op_class); + + cur_chan = cds_freq_to_chan(intr[vdev_id].mhz); + /* + * basic sanity check: requested channel should not be 0 + * and equal to home channel + */ + if (0 == csa_offload_event->channel) { + WMA_LOGE("CSA Event with channel %d. Ignore !!", + csa_offload_event->channel); + qdf_mem_free(csa_offload_event); + return -EINVAL; + } + + wma_send_msg(wma, WMA_CSA_OFFLOAD_EVENT, (void *)csa_offload_event, 0); + return 0; +} + +#ifdef FEATURE_OEM_DATA_SUPPORT +/** + * wma_oem_data_response_handler() - OEM data response event handler + * @handle: wma handle + * @datap: data ptr + * @len: data length + * + * Return: 0 for success or error code + */ +int wma_oem_data_response_handler(void *handle, + uint8_t *datap, uint32_t len) +{ + WMI_OEM_RESPONSE_EVENTID_param_tlvs *param_buf; + uint8_t *data; + uint32_t datalen; + struct oem_data_rsp *oem_rsp; + struct mac_context *pmac = cds_get_context(QDF_MODULE_ID_PE); + + if (!pmac) { + WMA_LOGE(FL("Invalid pmac")); + return -EINVAL; + } + + if (!pmac->sme.oem_data_rsp_callback) { + WMA_LOGE(FL("Callback not registered")); + return -EINVAL; + } + + param_buf = (WMI_OEM_RESPONSE_EVENTID_param_tlvs *) datap; + if (!param_buf) { + WMA_LOGE(FL("Received NULL buf ptr from FW")); + return -ENOMEM; + } + + data = param_buf->data; + datalen = param_buf->num_data; + + if (!data) { + WMA_LOGE(FL("Received NULL data from FW")); + return -EINVAL; + } + + if (datalen > OEM_DATA_RSP_SIZE) { + WMA_LOGE(FL("Received data len %d exceeds max value %d"), + datalen, OEM_DATA_RSP_SIZE); + return -EINVAL; + } + + oem_rsp = qdf_mem_malloc(sizeof(*oem_rsp)); + if (!oem_rsp) + return -ENOMEM; + + oem_rsp->rsp_len = datalen; + if (oem_rsp->rsp_len) { + oem_rsp->data = qdf_mem_malloc(oem_rsp->rsp_len); + if (!oem_rsp->data) { + qdf_mem_free(oem_rsp); + return -ENOMEM; + } + } else { + WMA_LOGE(FL("Invalid rsp length: %d"), + oem_rsp->rsp_len); + qdf_mem_free(oem_rsp); + return -EINVAL; + } + + qdf_mem_copy(oem_rsp->data, data, datalen); + + WMA_LOGD("Sending OEM_DATA_RSP(len: %d) to upper layer", datalen); + + pmac->sme.oem_data_rsp_callback(oem_rsp); + + if (oem_rsp->data) + qdf_mem_free(oem_rsp->data); + qdf_mem_free(oem_rsp); + + return 0; +} + +QDF_STATUS wma_start_oem_req_cmd(tp_wma_handle wma_handle, + struct oem_data_req *oem_data_req) +{ + QDF_STATUS ret; + + WMA_LOGD(FL("Send OEM Data Request to target")); + + if (!oem_data_req || !oem_data_req->data) { + WMA_LOGE(FL("oem_data_req is null")); + return QDF_STATUS_E_INVAL; + } + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE(FL("WMA - closed, can not send Oem data request cmd")); + qdf_mem_free(oem_data_req->data); + return QDF_STATUS_E_INVAL; + } + + /* legacy api, for oem data request case */ + ret = wmi_unified_start_oem_data_cmd(wma_handle->wmi_handle, + oem_data_req->data_len, + oem_data_req->data); + + if (!QDF_IS_STATUS_SUCCESS(ret)) + WMA_LOGE(FL("wmi cmd send failed")); + + return ret; +} +#endif /* FEATURE_OEM_DATA_SUPPORT */ + +#ifdef FEATURE_OEM_DATA +QDF_STATUS wma_start_oem_data_cmd(tp_wma_handle wma_handle, + struct oem_data *oem_data) +{ + QDF_STATUS ret; + + wma_debug("Send OEM Data to target"); + + if (!oem_data || !oem_data->data) { + wma_err("oem_data is null"); + return QDF_STATUS_E_INVAL; + } + + if (!wma_handle || !wma_handle->wmi_handle) { + wma_err("WMA - closed"); + return QDF_STATUS_E_INVAL; + } + + /* common api, for oem data command case */ + ret = wmi_unified_start_oemv2_data_cmd(wma_handle->wmi_handle, + oem_data); + if (!QDF_IS_STATUS_SUCCESS(ret)) + wma_err("call start wmi cmd failed"); + + return ret; +} +#endif + +#if !defined(REMOVE_PKT_LOG) && defined(FEATURE_PKTLOG) +/** + * wma_pktlog_wmi_send_cmd() - send pktlog enable/disable command to target + * @handle: wma handle + * @params: pktlog params + * + * Return: QDF status + */ +QDF_STATUS wma_pktlog_wmi_send_cmd(WMA_HANDLE handle, + struct ath_pktlog_wmi_params *params) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + int ret; + + ret = wmi_unified_pktlog_wmi_send_cmd(wma_handle->wmi_handle, + params->pktlog_event, + params->cmd_id, params->user_triggered); + if (ret) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} +#endif /* !REMOVE_PKT_LOG && FEATURE_PKTLOG */ + +/** + * wma_wow_wake_reason_str() - Converts wow wakeup reason code to text format + * @wake_reason - WOW wake reason + * + * Return: reason code in string format + */ +static const uint8_t *wma_wow_wake_reason_str(A_INT32 wake_reason) +{ + switch (wake_reason) { + case WOW_REASON_UNSPECIFIED: + return "UNSPECIFIED"; + case WOW_REASON_NLOD: + return "NLOD"; + case WOW_REASON_AP_ASSOC_LOST: + return "AP_ASSOC_LOST"; + case WOW_REASON_LOW_RSSI: + return "LOW_RSSI"; + case WOW_REASON_DEAUTH_RECVD: + return "DEAUTH_RECVD"; + case WOW_REASON_DISASSOC_RECVD: + return "DISASSOC_RECVD"; + case WOW_REASON_GTK_HS_ERR: + return "GTK_HS_ERR"; + case WOW_REASON_EAP_REQ: + return "EAP_REQ"; + case WOW_REASON_FOURWAY_HS_RECV: + return "FOURWAY_HS_RECV"; + case WOW_REASON_TIMER_INTR_RECV: + return "TIMER_INTR_RECV"; + case WOW_REASON_PATTERN_MATCH_FOUND: + return "PATTERN_MATCH_FOUND"; + case WOW_REASON_RECV_MAGIC_PATTERN: + return "RECV_MAGIC_PATTERN"; + case WOW_REASON_P2P_DISC: + return "P2P_DISC"; + case WOW_REASON_WLAN_HB: + return "WLAN_HB"; + case WOW_REASON_CSA_EVENT: + return "CSA_EVENT"; + case WOW_REASON_PROBE_REQ_WPS_IE_RECV: + return "PROBE_REQ_WPS_IE_RECV"; + case WOW_REASON_AUTH_REQ_RECV: + return "AUTH_REQ_RECV"; + case WOW_REASON_ASSOC_REQ_RECV: + return "ASSOC_REQ_RECV"; + case WOW_REASON_HTT_EVENT: + return "HTT_EVENT"; + case WOW_REASON_RA_MATCH: + return "RA_MATCH"; + case WOW_REASON_HOST_AUTO_SHUTDOWN: + return "HOST_AUTO_SHUTDOWN"; + case WOW_REASON_IOAC_MAGIC_EVENT: + return "IOAC_MAGIC_EVENT"; + case WOW_REASON_IOAC_SHORT_EVENT: + return "IOAC_SHORT_EVENT"; + case WOW_REASON_IOAC_EXTEND_EVENT: + return "IOAC_EXTEND_EVENT"; + case WOW_REASON_IOAC_TIMER_EVENT: + return "IOAC_TIMER_EVENT"; + case WOW_REASON_ROAM_HO: + return "ROAM_HO"; + case WOW_REASON_ROAM_PREAUTH_START: + return "ROAM_PREAUTH_START_EVENT"; + case WOW_REASON_DFS_PHYERR_RADADR_EVENT: + return "DFS_PHYERR_RADADR_EVENT"; + case WOW_REASON_BEACON_RECV: + return "BEACON_RECV"; + case WOW_REASON_CLIENT_KICKOUT_EVENT: + return "CLIENT_KICKOUT_EVENT"; + case WOW_REASON_NAN_EVENT: + return "NAN_EVENT"; + case WOW_REASON_EXTSCAN: + return "EXTSCAN"; + case WOW_REASON_RSSI_BREACH_EVENT: + return "RSSI_BREACH_EVENT"; + case WOW_REASON_IOAC_REV_KA_FAIL_EVENT: + return "IOAC_REV_KA_FAIL_EVENT"; + case WOW_REASON_IOAC_SOCK_EVENT: + return "IOAC_SOCK_EVENT"; + case WOW_REASON_NLO_SCAN_COMPLETE: + return "NLO_SCAN_COMPLETE"; + case WOW_REASON_PACKET_FILTER_MATCH: + return "PACKET_FILTER_MATCH"; + case WOW_REASON_ASSOC_RES_RECV: + return "ASSOC_RES_RECV"; + case WOW_REASON_REASSOC_REQ_RECV: + return "REASSOC_REQ_RECV"; + case WOW_REASON_REASSOC_RES_RECV: + return "REASSOC_RES_RECV"; + case WOW_REASON_ACTION_FRAME_RECV: + return "ACTION_FRAME_RECV"; + case WOW_REASON_BPF_ALLOW: + return "BPF_ALLOW"; + case WOW_REASON_NAN_DATA: + return "NAN_DATA"; + case WOW_REASON_OEM_RESPONSE_EVENT: + return "OEM_RESPONSE_EVENT"; + case WOW_REASON_TDLS_CONN_TRACKER_EVENT: + return "TDLS_CONN_TRACKER_EVENT"; + case WOW_REASON_CRITICAL_LOG: + return "CRITICAL_LOG"; + case WOW_REASON_P2P_LISTEN_OFFLOAD: + return "P2P_LISTEN_OFFLOAD"; + case WOW_REASON_NAN_EVENT_WAKE_HOST: + return "NAN_EVENT_WAKE_HOST"; + case WOW_REASON_DEBUG_TEST: + return "DEBUG_TEST"; + case WOW_REASON_CHIP_POWER_FAILURE_DETECT: + return "CHIP_POWER_FAILURE_DETECT"; + case WOW_REASON_11D_SCAN: + return "11D_SCAN"; + case WOW_REASON_SAP_OBSS_DETECTION: + return "SAP_OBSS_DETECTION"; + case WOW_REASON_BSS_COLOR_COLLISION_DETECT: + return "BSS_COLOR_COLLISION_DETECT"; +#ifdef WLAN_FEATURE_MOTION_DETECTION + case WOW_REASON_WLAN_MD: + return "MOTION_DETECT"; + case WOW_REASON_WLAN_BL: + return "MOTION_DETECT_BASELINE"; +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + case WOW_REASON_PAGE_FAULT: + return "PAGE_FAULT"; + case WOW_REASON_ROAM_PMKID_REQUEST: + return "ROAM_PMKID_REQUEST"; + default: + return "unknown"; + } +} + +static bool wma_wow_reason_has_stats(enum wake_reason_e reason) +{ + switch (reason) { + case WOW_REASON_ASSOC_REQ_RECV: + case WOW_REASON_DISASSOC_RECVD: + case WOW_REASON_ASSOC_RES_RECV: + case WOW_REASON_REASSOC_REQ_RECV: + case WOW_REASON_REASSOC_RES_RECV: + case WOW_REASON_AUTH_REQ_RECV: + case WOW_REASON_DEAUTH_RECVD: + case WOW_REASON_ACTION_FRAME_RECV: + case WOW_REASON_BPF_ALLOW: + case WOW_REASON_PATTERN_MATCH_FOUND: + case WOW_REASON_PACKET_FILTER_MATCH: + case WOW_REASON_RA_MATCH: + case WOW_REASON_NLOD: + case WOW_REASON_NLO_SCAN_COMPLETE: + case WOW_REASON_LOW_RSSI: + case WOW_REASON_EXTSCAN: + case WOW_REASON_RSSI_BREACH_EVENT: + case WOW_REASON_OEM_RESPONSE_EVENT: + case WOW_REASON_CHIP_POWER_FAILURE_DETECT: + case WOW_REASON_11D_SCAN: + return true; +#ifdef WLAN_FEATURE_MOTION_DETECTION + case WOW_REASON_WLAN_MD: + case WOW_REASON_WLAN_BL: + return true; +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + default: + return false; + } +} + +static void wma_inc_wow_stats(t_wma_handle *wma, + WOW_EVENT_INFO_fixed_param *wake_info) +{ + ucfg_mc_cp_stats_inc_wake_lock_stats(wma->psoc, + wake_info->vdev_id, + wake_info->wake_reason); +} + +static void wma_wow_stats_display(struct wake_lock_stats *stats) +{ + WMA_LOGA("WLAN wake reason counters:"); + WMA_LOGA("uc:%d bc:%d v4_mc:%d v6_mc:%d ra:%d ns:%d na:%d " + "icmp:%d icmpv6:%d", + stats->ucast_wake_up_count, + stats->bcast_wake_up_count, + stats->ipv4_mcast_wake_up_count, + stats->ipv6_mcast_wake_up_count, + stats->ipv6_mcast_ra_stats, + stats->ipv6_mcast_ns_stats, + stats->ipv6_mcast_na_stats, + stats->icmpv4_count, + stats->icmpv6_count); + + WMA_LOGA("assoc:%d disassoc:%d assoc_resp:%d reassoc:%d " + "reassoc_resp:%d auth:%d deauth:%d action:%d", + stats->mgmt_assoc, + stats->mgmt_disassoc, + stats->mgmt_assoc_resp, + stats->mgmt_reassoc, + stats->mgmt_reassoc_resp, + stats->mgmt_auth, + stats->mgmt_deauth, + stats->mgmt_action); + + WMA_LOGA("pno_match:%d pno_complete:%d gscan:%d " + "low_rssi:%d rssi_breach:%d oem:%d scan_11d:%d", + stats->pno_match_wake_up_count, + stats->pno_complete_wake_up_count, + stats->gscan_wake_up_count, + stats->low_rssi_wake_up_count, + stats->rssi_breach_wake_up_count, + stats->oem_response_wake_up_count, + stats->scan_11d); +} + +static void wma_print_wow_stats(t_wma_handle *wma, + WOW_EVENT_INFO_fixed_param *wake_info) +{ + struct wlan_objmgr_vdev *vdev; + struct wake_lock_stats stats = {0}; + + if (!wma_wow_reason_has_stats(wake_info->wake_reason)) + return; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, + wake_info->vdev_id, + WLAN_LEGACY_WMA_ID); + if (!vdev) { + WMA_LOGE("%s, vdev_id: %d, failed to get vdev from psoc", + __func__, wake_info->vdev_id); + return; + } + + ucfg_mc_cp_stats_get_vdev_wake_lock_stats(vdev, &stats); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); + wma_wow_stats_display(&stats); +} + +#ifdef FEATURE_WLAN_EXTSCAN +/** + * wma_extscan_get_eventid_from_tlvtag() - map tlv tag to corresponding event id + * @tag: WMI TLV tag + * + * Return: + * 0 if TLV tag is invalid + * else return corresponding WMI event id + */ +static int wma_extscan_get_eventid_from_tlvtag(uint32_t tag) +{ + uint32_t event_id; + + switch (tag) { + case WMITLV_TAG_STRUC_wmi_extscan_start_stop_event_fixed_param: + event_id = WMI_EXTSCAN_START_STOP_EVENTID; + break; + + case WMITLV_TAG_STRUC_wmi_extscan_operation_event_fixed_param: + event_id = WMI_EXTSCAN_OPERATION_EVENTID; + break; + + case WMITLV_TAG_STRUC_wmi_extscan_table_usage_event_fixed_param: + event_id = WMI_EXTSCAN_TABLE_USAGE_EVENTID; + break; + + case WMITLV_TAG_STRUC_wmi_extscan_cached_results_event_fixed_param: + event_id = WMI_EXTSCAN_CACHED_RESULTS_EVENTID; + break; + + case WMITLV_TAG_STRUC_wmi_extscan_wlan_change_results_event_fixed_param: + event_id = WMI_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID; + break; + + case WMITLV_TAG_STRUC_wmi_extscan_hotlist_match_event_fixed_param: + event_id = WMI_EXTSCAN_HOTLIST_MATCH_EVENTID; + break; + + case WMITLV_TAG_STRUC_wmi_extscan_capabilities_event_fixed_param: + event_id = WMI_EXTSCAN_CAPABILITIES_EVENTID; + break; + + default: + event_id = 0; + WMA_LOGE("%s: Unknown tag: %d", __func__, tag); + break; + } + + WMA_LOGI("%s: For tag %d WMI event 0x%x", __func__, tag, event_id); + return event_id; +} +#else +static int wma_extscan_get_eventid_from_tlvtag(uint32_t tag) +{ + return 0; +} +#endif + +/** + * wow_get_wmi_eventid() - map reason or tlv tag to corresponding event id + * @tag: WMI TLV tag + * @reason: WOW reason + * + * WOW reason type is primarily used to find the ID. If there could be + * multiple events that can be sent as a WOW event with same reason + * then tlv tag is used to identify the corresponding event. + * + * Return: + * 0 if TLV tag/reason is invalid + * else return corresponding WMI event id + */ +static int wow_get_wmi_eventid(int32_t reason, uint32_t tag) +{ + int event_id; + + switch (reason) { + case WOW_REASON_AP_ASSOC_LOST: + event_id = WMI_ROAM_EVENTID; + break; + case WOW_REASON_NLO_SCAN_COMPLETE: + event_id = WMI_NLO_SCAN_COMPLETE_EVENTID; + break; + case WOW_REASON_CSA_EVENT: + event_id = WMI_CSA_HANDLING_EVENTID; + break; + case WOW_REASON_LOW_RSSI: + event_id = WMI_ROAM_EVENTID; + break; + case WOW_REASON_CLIENT_KICKOUT_EVENT: + event_id = WMI_PEER_STA_KICKOUT_EVENTID; + break; + case WOW_REASON_EXTSCAN: + event_id = wma_extscan_get_eventid_from_tlvtag(tag); + break; + case WOW_REASON_RSSI_BREACH_EVENT: + event_id = WMI_RSSI_BREACH_EVENTID; + break; + case WOW_REASON_NAN_EVENT: + event_id = WMI_NAN_EVENTID; + break; + case WOW_REASON_NAN_DATA: + event_id = wma_ndp_get_eventid_from_tlvtag(tag); + break; + case WOW_REASON_TDLS_CONN_TRACKER_EVENT: + event_id = WOW_TDLS_CONN_TRACKER_EVENT; + break; + case WOW_REASON_ROAM_HO: + event_id = WMI_ROAM_EVENTID; + break; + case WOW_REASON_11D_SCAN: + event_id = WMI_11D_NEW_COUNTRY_EVENTID; + break; + case WOW_ROAM_PREAUTH_START_EVENT: + event_id = WMI_ROAM_PREAUTH_STATUS_CMDID; + break; + case WOW_REASON_ROAM_PMKID_REQUEST: + event_id = WMI_ROAM_PMKID_REQUEST_EVENTID; + break; + default: + WMA_LOGD(FL("No Event Id for WOW reason %s(%d)"), + wma_wow_wake_reason_str(reason), reason); + event_id = 0; + break; + } + wlan_roam_debug_log(WMA_INVALID_VDEV_ID, DEBUG_WOW_REASON, + DEBUG_INVALID_PEER_ID, NULL, NULL, + reason, event_id); + + return event_id; +} + +/** + * is_piggybacked_event() - Returns true if the given wake reason indicates + * there will be piggybacked TLV event data + * @reason: WOW reason + * + * There are three types of WoW event payloads: none, piggybacked event, and + * network packet. This function returns true for wake reasons that fall into + * the piggybacked event case. + * + * Return: true for piggybacked event data + */ +static bool is_piggybacked_event(int32_t reason) +{ + switch (reason) { + case WOW_REASON_AP_ASSOC_LOST: + case WOW_REASON_NLO_SCAN_COMPLETE: + case WOW_REASON_CSA_EVENT: + case WOW_REASON_LOW_RSSI: + case WOW_REASON_CLIENT_KICKOUT_EVENT: + case WOW_REASON_EXTSCAN: + case WOW_REASON_RSSI_BREACH_EVENT: + case WOW_REASON_NAN_EVENT: + case WOW_REASON_NAN_DATA: + case WOW_REASON_TDLS_CONN_TRACKER_EVENT: + case WOW_REASON_ROAM_HO: + case WOW_REASON_ROAM_PMKID_REQUEST: + return true; + default: + return false; + } +} + +/** + * wma_pkt_proto_subtype_to_string() - to convert proto subtype + * of data packet to string. + * @proto_subtype: proto subtype for data packet + * + * This function returns the string for the proto subtype of + * data packet. + * + * Return: string for proto subtype for data packet + */ +static const char * +wma_pkt_proto_subtype_to_string(enum qdf_proto_subtype proto_subtype) +{ + switch (proto_subtype) { + case QDF_PROTO_EAPOL_M1: + return "EAPOL M1"; + case QDF_PROTO_EAPOL_M2: + return "EAPOL M2"; + case QDF_PROTO_EAPOL_M3: + return "EAPOL M3"; + case QDF_PROTO_EAPOL_M4: + return "EAPOL M4"; + case QDF_PROTO_DHCP_DISCOVER: + return "DHCP DISCOVER"; + case QDF_PROTO_DHCP_REQUEST: + return "DHCP REQUEST"; + case QDF_PROTO_DHCP_OFFER: + return "DHCP OFFER"; + case QDF_PROTO_DHCP_ACK: + return "DHCP ACK"; + case QDF_PROTO_DHCP_NACK: + return "DHCP NACK"; + case QDF_PROTO_DHCP_RELEASE: + return "DHCP RELEASE"; + case QDF_PROTO_DHCP_INFORM: + return "DHCP INFORM"; + case QDF_PROTO_DHCP_DECLINE: + return "DHCP DECLINE"; + case QDF_PROTO_ARP_REQ: + return "ARP REQUEST"; + case QDF_PROTO_ARP_RES: + return "ARP RESPONSE"; + case QDF_PROTO_ICMP_REQ: + return "ICMP REQUEST"; + case QDF_PROTO_ICMP_RES: + return "ICMP RESPONSE"; + case QDF_PROTO_ICMPV6_REQ: + return "ICMPV6 REQUEST"; + case QDF_PROTO_ICMPV6_RES: + return "ICMPV6 RESPONSE"; + case QDF_PROTO_ICMPV6_RS: + return "ICMPV6 RS"; + case QDF_PROTO_ICMPV6_RA: + return "ICMPV6 RA"; + case QDF_PROTO_ICMPV6_NS: + return "ICMPV6 NS"; + case QDF_PROTO_ICMPV6_NA: + return "ICMPV6 NA"; + case QDF_PROTO_IPV4_UDP: + return "IPV4 UDP Packet"; + case QDF_PROTO_IPV4_TCP: + return "IPV4 TCP Packet"; + case QDF_PROTO_IPV6_UDP: + return "IPV6 UDP Packet"; + case QDF_PROTO_IPV6_TCP: + return "IPV6 TCP Packet"; + default: + return NULL; + } +} + +/** + * wma_wow_get_pkt_proto_subtype() - get the proto subtype of the packet. + * @data: Pointer to the packet data buffer + * @len: length of the packet data buffer + * + * Return: proto subtype of the packet. + */ +static enum qdf_proto_subtype +wma_wow_get_pkt_proto_subtype(uint8_t *data, uint32_t len) +{ + uint16_t eth_type; + uint8_t proto_type; + + if (len < QDF_NBUF_TRAC_ETH_TYPE_OFFSET + 2) { + WMA_LOGE("Malformed ethernet packet: length %u < %d", + len, QDF_NBUF_TRAC_ETH_TYPE_OFFSET + 2); + return QDF_PROTO_INVALID; + } + + eth_type = *(uint16_t *)(data + QDF_NBUF_TRAC_ETH_TYPE_OFFSET); + eth_type = qdf_cpu_to_be16(eth_type); + + WMA_LOGD("Ether Type: 0x%04x", eth_type); + switch (eth_type) { + case QDF_NBUF_TRAC_EAPOL_ETH_TYPE: + if (len < WMA_EAPOL_SUBTYPE_GET_MIN_LEN) + return QDF_PROTO_INVALID; + + WMA_LOGD("EAPOL Packet"); + return qdf_nbuf_data_get_eapol_subtype(data); + + case QDF_NBUF_TRAC_ARP_ETH_TYPE: + if (len < WMA_ARP_SUBTYPE_GET_MIN_LEN) + return QDF_PROTO_INVALID; + + WMA_LOGD("ARP Packet"); + return qdf_nbuf_data_get_arp_subtype(data); + + case QDF_NBUF_TRAC_IPV4_ETH_TYPE: + if (len < WMA_IPV4_PROTO_GET_MIN_LEN) + return QDF_PROTO_INVALID; + + WMA_LOGD("IPV4 Packet"); + + proto_type = qdf_nbuf_data_get_ipv4_proto(data); + WMA_LOGD("IPV4_proto_type: %u", proto_type); + + switch (proto_type) { + case QDF_NBUF_TRAC_ICMP_TYPE: + if (len < WMA_ICMP_SUBTYPE_GET_MIN_LEN) + return QDF_PROTO_INVALID; + + WMA_LOGD("ICMP Packet"); + return qdf_nbuf_data_get_icmp_subtype(data); + + case QDF_NBUF_TRAC_UDP_TYPE: + if (len < WMA_IS_DHCP_GET_MIN_LEN) + return QDF_PROTO_IPV4_UDP; + + if (!qdf_nbuf_data_is_ipv4_dhcp_pkt(data)) + return QDF_PROTO_INVALID; + + if (len < WMA_DHCP_SUBTYPE_GET_MIN_LEN) + return QDF_PROTO_INVALID; + + WMA_LOGD("DHCP Packet"); + return qdf_nbuf_data_get_dhcp_subtype(data); + + case QDF_NBUF_TRAC_TCP_TYPE: + return QDF_PROTO_IPV4_TCP; + + default: + return QDF_PROTO_INVALID; + } + + case QDF_NBUF_TRAC_IPV6_ETH_TYPE: + if (len < WMA_IPV6_PROTO_GET_MIN_LEN) + return QDF_PROTO_INVALID; + + WMA_LOGD("IPV6 Packet"); + + proto_type = qdf_nbuf_data_get_ipv6_proto(data); + WMA_LOGD("IPV6_proto_type: %u", proto_type); + + switch (proto_type) { + case QDF_NBUF_TRAC_ICMPV6_TYPE: + if (len < WMA_ICMPV6_SUBTYPE_GET_MIN_LEN) + return QDF_PROTO_INVALID; + + WMA_LOGD("ICMPV6 Packet"); + return qdf_nbuf_data_get_icmpv6_subtype(data); + + case QDF_NBUF_TRAC_UDP_TYPE: + return QDF_PROTO_IPV6_UDP; + + case QDF_NBUF_TRAC_TCP_TYPE: + return QDF_PROTO_IPV6_TCP; + + default: + return QDF_PROTO_INVALID; + } + + default: + return QDF_PROTO_INVALID; + } +} + +static void wma_log_pkt_eapol(uint8_t *data, uint32_t length) +{ + uint16_t pkt_len, key_len; + + if (length < WMA_EAPOL_INFO_GET_MIN_LEN) + return; + + pkt_len = *(uint16_t *)(data + EAPOL_PKT_LEN_OFFSET); + key_len = *(uint16_t *)(data + EAPOL_KEY_LEN_OFFSET); + WMA_LOGD("Pkt_len: %u, Key_len: %u", + qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(key_len)); +} + +static void wma_log_pkt_dhcp(uint8_t *data, uint32_t length) +{ + uint16_t pkt_len; + uint32_t trans_id; + + if (length < WMA_DHCP_INFO_GET_MIN_LEN) + return; + + pkt_len = *(uint16_t *)(data + DHCP_PKT_LEN_OFFSET); + trans_id = *(uint32_t *)(data + DHCP_TRANSACTION_ID_OFFSET); + WMA_LOGD("Pkt_len: %u, Transaction_id: %u", + qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(trans_id)); +} + +static void wma_log_pkt_icmpv4(uint8_t *data, uint32_t length) +{ + uint16_t pkt_len, seq_num; + + if (length < WMA_IPV4_PKT_INFO_GET_MIN_LEN) + return; + + pkt_len = *(uint16_t *)(data + IPV4_PKT_LEN_OFFSET); + seq_num = *(uint16_t *)(data + ICMP_SEQ_NUM_OFFSET); + WMA_LOGD("Pkt_len: %u, Seq_num: %u", + qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(seq_num)); +} + +static void wma_log_pkt_icmpv6(uint8_t *data, uint32_t length) +{ + uint16_t pkt_len, seq_num; + + if (length < WMA_IPV6_PKT_INFO_GET_MIN_LEN) + return; + + pkt_len = *(uint16_t *)(data + IPV6_PKT_LEN_OFFSET); + seq_num = *(uint16_t *)(data + ICMPV6_SEQ_NUM_OFFSET); + WMA_LOGD("Pkt_len: %u, Seq_num: %u", + qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(seq_num)); +} + +static void wma_log_pkt_ipv4(uint8_t *data, uint32_t length) +{ + uint16_t pkt_len, src_port, dst_port; + char *ip_addr; + + if (length < WMA_IPV4_PKT_INFO_GET_MIN_LEN) + return; + + pkt_len = *(uint16_t *)(data + IPV4_PKT_LEN_OFFSET); + ip_addr = (char *)(data + IPV4_SRC_ADDR_OFFSET); + WMA_LOGD("src addr %d:%d:%d:%d", ip_addr[0], ip_addr[1], + ip_addr[2], ip_addr[3]); + ip_addr = (char *)(data + IPV4_DST_ADDR_OFFSET); + WMA_LOGD("dst addr %d:%d:%d:%d", ip_addr[0], ip_addr[1], + ip_addr[2], ip_addr[3]); + src_port = *(uint16_t *)(data + IPV4_SRC_PORT_OFFSET); + dst_port = *(uint16_t *)(data + IPV4_DST_PORT_OFFSET); + WMA_LOGI("Pkt_len: %u, src_port: %u, dst_port: %u", + qdf_cpu_to_be16(pkt_len), + qdf_cpu_to_be16(src_port), + qdf_cpu_to_be16(dst_port)); +} + +static void wma_log_pkt_ipv6(uint8_t *data, uint32_t length) +{ + uint16_t pkt_len, src_port, dst_port; + char *ip_addr; + + if (length < WMA_IPV6_PKT_INFO_GET_MIN_LEN) + return; + + pkt_len = *(uint16_t *)(data + IPV6_PKT_LEN_OFFSET); + ip_addr = (char *)(data + IPV6_SRC_ADDR_OFFSET); + WMA_LOGD("src addr "IPV6_ADDR_STR, ip_addr[0], + ip_addr[1], ip_addr[2], ip_addr[3], ip_addr[4], + ip_addr[5], ip_addr[6], ip_addr[7], ip_addr[8], + ip_addr[9], ip_addr[10], ip_addr[11], + ip_addr[12], ip_addr[13], ip_addr[14], + ip_addr[15]); + ip_addr = (char *)(data + IPV6_DST_ADDR_OFFSET); + WMA_LOGD("dst addr "IPV6_ADDR_STR, ip_addr[0], + ip_addr[1], ip_addr[2], ip_addr[3], ip_addr[4], + ip_addr[5], ip_addr[6], ip_addr[7], ip_addr[8], + ip_addr[9], ip_addr[10], ip_addr[11], + ip_addr[12], ip_addr[13], ip_addr[14], + ip_addr[15]); + src_port = *(uint16_t *)(data + IPV6_SRC_PORT_OFFSET); + dst_port = *(uint16_t *)(data + IPV6_DST_PORT_OFFSET); + WMA_LOGI("Pkt_len: %u, src_port: %u, dst_port: %u", + qdf_cpu_to_be16(pkt_len), + qdf_cpu_to_be16(src_port), + qdf_cpu_to_be16(dst_port)); +} + +static void wma_log_pkt_tcpv4(uint8_t *data, uint32_t length) +{ + uint32_t seq_num; + + if (length < WMA_IPV4_PKT_INFO_GET_MIN_LEN) + return; + + seq_num = *(uint32_t *)(data + IPV4_TCP_SEQ_NUM_OFFSET); + WMA_LOGD("TCP_seq_num: %u", qdf_cpu_to_be16(seq_num)); +} + +static void wma_log_pkt_tcpv6(uint8_t *data, uint32_t length) +{ + uint32_t seq_num; + + if (length < WMA_IPV6_PKT_INFO_GET_MIN_LEN) + return; + + seq_num = *(uint32_t *)(data + IPV6_TCP_SEQ_NUM_OFFSET); + WMA_LOGD("TCP_seq_num: %u", qdf_cpu_to_be16(seq_num)); +} + +static void wma_wow_inc_wake_lock_stats_by_dst_addr(t_wma_handle *wma, + uint8_t vdev_id, + uint8_t *dest_mac) +{ + ucfg_mc_cp_stats_inc_wake_lock_stats_by_dst_addr(wma->psoc, + vdev_id, + dest_mac); +} + +static void wma_wow_inc_wake_lock_stats_by_protocol(t_wma_handle *wma, + uint8_t vdev_id, enum qdf_proto_subtype proto_subtype) +{ + ucfg_mc_cp_stats_inc_wake_lock_stats_by_protocol(wma->psoc, + vdev_id, + proto_subtype); +} + +/** + * wma_wow_parse_data_pkt() - API to parse data buffer for data + * packet that resulted in WOW wakeup. + * @stats: per-vdev stats for tracking packet types + * @data: Pointer to data buffer + * @length: data buffer length + * + * This function parses the data buffer received (first few bytes of + * skb->data) to get information like src mac addr, dst mac addr, packet + * len, seq_num, etc. It also increments stats for different packet types. + * + * Return: void + */ +static void wma_wow_parse_data_pkt(t_wma_handle *wma, + uint8_t vdev_id, uint8_t *data, + uint32_t length) +{ + uint8_t *src_mac; + uint8_t *dest_mac; + const char *proto_subtype_name; + enum qdf_proto_subtype proto_subtype; + + WMA_LOGD("packet length: %u", length); + if (length < QDF_NBUF_TRAC_IPV4_OFFSET) + return; + + src_mac = data + QDF_NBUF_SRC_MAC_OFFSET; + dest_mac = data + QDF_NBUF_DEST_MAC_OFFSET; + wma_info("Src_mac: " QDF_MAC_ADDR_FMT ", Dst_mac: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(src_mac), QDF_MAC_ADDR_REF(dest_mac)); + + wma_wow_inc_wake_lock_stats_by_dst_addr(wma, vdev_id, dest_mac); + + proto_subtype = wma_wow_get_pkt_proto_subtype(data, length); + proto_subtype_name = wma_pkt_proto_subtype_to_string(proto_subtype); + if (proto_subtype_name) + WMA_LOGI("WOW Wakeup: %s rcvd", proto_subtype_name); + + switch (proto_subtype) { + case QDF_PROTO_EAPOL_M1: + case QDF_PROTO_EAPOL_M2: + case QDF_PROTO_EAPOL_M3: + case QDF_PROTO_EAPOL_M4: + wma_log_pkt_eapol(data, length); + break; + + case QDF_PROTO_DHCP_DISCOVER: + case QDF_PROTO_DHCP_REQUEST: + case QDF_PROTO_DHCP_OFFER: + case QDF_PROTO_DHCP_ACK: + case QDF_PROTO_DHCP_NACK: + case QDF_PROTO_DHCP_RELEASE: + case QDF_PROTO_DHCP_INFORM: + case QDF_PROTO_DHCP_DECLINE: + wma_log_pkt_dhcp(data, length); + break; + + case QDF_PROTO_ICMP_REQ: + case QDF_PROTO_ICMP_RES: + wma_wow_inc_wake_lock_stats_by_protocol(wma, vdev_id, + proto_subtype); + wma_log_pkt_icmpv4(data, length); + break; + + case QDF_PROTO_ICMPV6_REQ: + case QDF_PROTO_ICMPV6_RES: + case QDF_PROTO_ICMPV6_RS: + case QDF_PROTO_ICMPV6_RA: + case QDF_PROTO_ICMPV6_NS: + case QDF_PROTO_ICMPV6_NA: + wma_wow_inc_wake_lock_stats_by_protocol(wma, vdev_id, + proto_subtype); + wma_log_pkt_icmpv6(data, length); + break; + + case QDF_PROTO_IPV4_UDP: + wma_log_pkt_ipv4(data, length); + break; + case QDF_PROTO_IPV4_TCP: + wma_log_pkt_ipv4(data, length); + wma_log_pkt_tcpv4(data, length); + break; + + case QDF_PROTO_IPV6_UDP: + wma_log_pkt_ipv6(data, length); + break; + case QDF_PROTO_IPV6_TCP: + wma_log_pkt_ipv6(data, length); + wma_log_pkt_tcpv6(data, length); + break; + default: + break; + } +} + +/** + * wma_wow_dump_mgmt_buffer() - API to parse data buffer for mgmt. + * packet that resulted in WOW wakeup. + * @wow_packet_buffer: Pointer to data buffer + * @buf_len: length of data buffer + * + * This function parses the data buffer received (802.11 header) + * to get information like src mac addr, dst mac addr, seq_num, + * frag_num, etc. + * + * Return: void + */ +static void wma_wow_dump_mgmt_buffer(uint8_t *wow_packet_buffer, + uint32_t buf_len) +{ + struct ieee80211_frame_addr4 *wh; + + WMA_LOGD("wow_buf_pkt_len: %u", buf_len); + wh = (struct ieee80211_frame_addr4 *) + (wow_packet_buffer); + if (buf_len >= sizeof(struct ieee80211_frame)) { + uint8_t to_from_ds, frag_num; + uint32_t seq_num; + + wma_err("RA: " QDF_MAC_ADDR_FMT " TA: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wh->i_addr1), + QDF_MAC_ADDR_REF(wh->i_addr2)); + + WMA_LOGE("TO_DS: %u, FROM_DS: %u", + wh->i_fc[1] & IEEE80211_FC1_DIR_TODS, + wh->i_fc[1] & IEEE80211_FC1_DIR_FROMDS); + + to_from_ds = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + + switch (to_from_ds) { + case IEEE80211_FC1_DIR_NODS: + wma_err("BSSID: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wh->i_addr3)); + break; + case IEEE80211_FC1_DIR_TODS: + wma_err("DA: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wh->i_addr3)); + break; + case IEEE80211_FC1_DIR_FROMDS: + wma_err("SA: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wh->i_addr3)); + break; + case IEEE80211_FC1_DIR_DSTODS: + if (buf_len >= sizeof(struct ieee80211_frame_addr4)) + wma_err("DA: " QDF_MAC_ADDR_FMT " SA: " + QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wh->i_addr3), + QDF_MAC_ADDR_REF(wh->i_addr4)); + break; + } + + seq_num = (((*(uint16_t *)wh->i_seq) & + IEEE80211_SEQ_SEQ_MASK) >> + IEEE80211_SEQ_SEQ_SHIFT); + frag_num = (((*(uint16_t *)wh->i_seq) & + IEEE80211_SEQ_FRAG_MASK) >> + IEEE80211_SEQ_FRAG_SHIFT); + + WMA_LOGE("SEQ_NUM: %u, FRAG_NUM: %u", + seq_num, frag_num); + } else { + WMA_LOGE("Insufficient buffer length for mgmt. packet"); + } +} + +/** + * wma_acquire_wakelock() - conditionally aquires a wakelock base on wake reason + * @wma: the wma handle with the wakelocks to aquire + * @wake_reason: wow wakeup reason + * + * Return: None + */ +static void wma_acquire_wow_wakelock(t_wma_handle *wma, int wake_reason) +{ + qdf_wake_lock_t *wl; + uint32_t ms; + + switch (wake_reason) { + case WOW_REASON_AUTH_REQ_RECV: + wl = &wma->wow_auth_req_wl; + ms = WMA_AUTH_REQ_RECV_WAKE_LOCK_TIMEOUT; + break; + case WOW_REASON_ASSOC_REQ_RECV: + wl = &wma->wow_assoc_req_wl; + ms = WMA_ASSOC_REQ_RECV_WAKE_LOCK_DURATION; + break; + case WOW_REASON_DEAUTH_RECVD: + wl = &wma->wow_deauth_rec_wl; + ms = WMA_DEAUTH_RECV_WAKE_LOCK_DURATION; + break; + case WOW_REASON_DISASSOC_RECVD: + wl = &wma->wow_disassoc_rec_wl; + ms = WMA_DISASSOC_RECV_WAKE_LOCK_DURATION; + break; + case WOW_REASON_AP_ASSOC_LOST: + wl = &wma->wow_ap_assoc_lost_wl; + ms = WMA_BMISS_EVENT_WAKE_LOCK_DURATION; + break; +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + case WOW_REASON_HOST_AUTO_SHUTDOWN: + wl = &wma->wow_auto_shutdown_wl; + ms = WMA_AUTO_SHUTDOWN_WAKE_LOCK_DURATION; + break; +#endif + case WOW_REASON_ROAM_HO: + wl = &wma->roam_ho_wl; + ms = WMA_ROAM_HO_WAKE_LOCK_DURATION; + break; + case WOW_REASON_ROAM_PREAUTH_START: + wl = &wma->roam_preauth_wl; + ms = WMA_ROAM_PREAUTH_WAKE_LOCK_DURATION; + default: + return; + } + + WMA_LOGA("Holding %d msec wake_lock", ms); + cds_host_diag_log_work(wl, ms, WIFI_POWER_EVENT_WAKELOCK_WOW); + qdf_wake_lock_timeout_acquire(wl, ms); +} + +/** + * wma_wake_reason_ap_assoc_lost() - WOW_REASON_AP_ASSOC_LOST handler + * @wma: Pointer to wma handle + * @event: pointer to piggybacked WMI_ROAM_EVENTID_param_tlvs buffer + * @len: length of the event buffer + * + * Return: Errno + */ +static int +wma_wake_reason_ap_assoc_lost(t_wma_handle *wma, void *event, uint32_t len) +{ + WMI_ROAM_EVENTID_param_tlvs *event_param; + wmi_roam_event_fixed_param *roam_event; + + event_param = event; + if (!event_param) { + WMA_LOGE("AP Assoc Lost event data is null"); + return -EINVAL; + } + + roam_event = event_param->fixed_param; + WMA_LOGA(FL("Beacon miss indication on vdev %d"), roam_event->vdev_id); + + wma_beacon_miss_handler(wma, roam_event->vdev_id, roam_event->rssi); + + return 0; +} + +static const char *wma_vdev_type_str(uint32_t vdev_type) +{ + switch (vdev_type) { + case WMI_VDEV_TYPE_AP: + return "AP"; + case WMI_VDEV_TYPE_STA: + return "STA"; + case WMI_VDEV_TYPE_IBSS: + return "IBSS"; + case WMI_VDEV_TYPE_MONITOR: + return "MONITOR"; + case WMI_VDEV_TYPE_NAN: + return "NAN"; + case WMI_VDEV_TYPE_OCB: + return "OCB"; + case WMI_VDEV_TYPE_NDI: + return "NDI"; + default: + return "unknown"; + } +} + +static int wma_wake_event_packet( + t_wma_handle *wma, + WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param, + uint32_t length) +{ + WOW_EVENT_INFO_fixed_param *wake_info; + struct wma_txrx_node *vdev; + uint8_t *packet; + uint32_t packet_len; + + if (event_param->num_wow_packet_buffer <= 4) { + WMA_LOGE("Invalid wow packet buffer from firmware %u", + event_param->num_wow_packet_buffer); + return -EINVAL; + } + /* first 4 bytes are the length, followed by the buffer */ + packet_len = *(uint32_t *)event_param->wow_packet_buffer; + packet = event_param->wow_packet_buffer + 4; + + if (!packet_len) { + WMA_LOGE("Wake event packet is empty"); + return 0; + } + + if (packet_len > (event_param->num_wow_packet_buffer - 4)) { + WMA_LOGE("Invalid packet_len from firmware, packet_len: %u, num_wow_packet_buffer: %u", + packet_len, + event_param->num_wow_packet_buffer); + return -EINVAL; + } + + wake_info = event_param->fixed_param; + + switch (wake_info->wake_reason) { + case WOW_REASON_AUTH_REQ_RECV: + case WOW_REASON_ASSOC_REQ_RECV: + case WOW_REASON_DEAUTH_RECVD: + case WOW_REASON_DISASSOC_RECVD: + case WOW_REASON_ASSOC_RES_RECV: + case WOW_REASON_REASSOC_REQ_RECV: + case WOW_REASON_REASSOC_RES_RECV: + case WOW_REASON_BEACON_RECV: + case WOW_REASON_ACTION_FRAME_RECV: + /* management frame case */ + wma_wow_dump_mgmt_buffer(packet, packet_len); + break; + + case WOW_REASON_BPF_ALLOW: + case WOW_REASON_PATTERN_MATCH_FOUND: + case WOW_REASON_RA_MATCH: + case WOW_REASON_RECV_MAGIC_PATTERN: + case WOW_REASON_PACKET_FILTER_MATCH: + WMA_LOGD("Wake event packet:"); + qdf_trace_hex_dump(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, + packet, packet_len); + + vdev = &wma->interfaces[wake_info->vdev_id]; + wma_wow_parse_data_pkt(wma, wake_info->vdev_id, + packet, packet_len); + break; + + case WOW_REASON_PAGE_FAULT: + /* + * In case PAGE_FAULT occurs on non-DRV platform, + * dump event buffer which contains more info regarding + * current page fault. + */ + wma_debug("PAGE_FAULT occurs during suspend: packet_len %u", + packet_len); + qdf_trace_hex_dump(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, + packet, packet_len); + break; + + default: + WMA_LOGE("Wake reason %s is not a packet event", + wma_wow_wake_reason_str(wake_info->wake_reason)); + return -EINVAL; + } + + return 0; +} + +static int wma_wake_event_no_payload( + t_wma_handle *wma, + WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param, + uint32_t length) +{ + WOW_EVENT_INFO_fixed_param *wake_info = event_param->fixed_param; + + switch (wake_info->wake_reason) { + case WOW_REASON_HOST_AUTO_SHUTDOWN: + return wma_wake_reason_auto_shutdown(); + + case WOW_REASON_NLOD: + return wma_wake_reason_nlod(wma, wake_info->vdev_id); + + default: + return 0; + } +} + +static int wma_wake_event_piggybacked( + t_wma_handle *wma, + WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param, + uint32_t length) +{ + int errno = 0; + void *pb_event; + uint32_t pb_event_len; + uint32_t wake_reason; + uint32_t event_id; + uint8_t *bssid; + tpDeleteStaContext del_sta_ctx; + + /* + * There are "normal" cases where a wake reason that usually contains a + * piggybacked event is empty. In these cases we just want to wake up, + * and no action is needed. Bail out now if that is the case. + */ + if (!event_param->wow_packet_buffer || + event_param->num_wow_packet_buffer <= 4) { + WMA_LOGE("Invalid wow packet buffer from firmware %u", + event_param->num_wow_packet_buffer); + return 0; + } + + bssid = wma_get_vdev_bssid + (wma->interfaces[event_param->fixed_param->vdev_id].vdev); + if (!bssid) { + WMA_LOGE("%s: Failed to get bssid for vdev_%d", + __func__, event_param->fixed_param->vdev_id); + return 0; + } + wake_reason = event_param->fixed_param->wake_reason; + + /* parse piggybacked event from param buffer */ + { + int ret_code; + uint8_t *pb_event_buf; + uint32_t tag; + + /* first 4 bytes are the length, followed by the buffer */ + pb_event_len = *(uint32_t *)event_param->wow_packet_buffer; + if (pb_event_len > (event_param->num_wow_packet_buffer - 4)) { + WMA_LOGE("Invalid pb_event_len from firmware, pb_event_len: %u, num_wow_packet_buffer: %u", + pb_event_len, + event_param->num_wow_packet_buffer); + return -EINVAL; + } + pb_event_buf = event_param->wow_packet_buffer + 4; + + WMA_LOGD("piggybacked event buffer:"); + qdf_trace_hex_dump(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, + pb_event_buf, pb_event_len); + + tag = WMITLV_GET_TLVTAG(WMITLV_GET_HDR(pb_event_buf)); + event_id = wow_get_wmi_eventid(wake_reason, tag); + if (!event_id) { + WMA_LOGE(FL("Unable to find Event Id")); + return -EINVAL; + } + + ret_code = wmitlv_check_and_pad_event_tlvs(wma, pb_event_buf, + pb_event_len, + event_id, &pb_event); + if (ret_code) { + WMA_LOGE(FL("Bad TLVs; len:%d, event_id:%d, status:%d"), + pb_event_len, event_id, ret_code); + return -EINVAL; + } + } + + switch (wake_reason) { + case WOW_REASON_AP_ASSOC_LOST: + errno = wma_wake_reason_ap_assoc_lost(wma, pb_event, + pb_event_len); + break; + +#ifdef FEATURE_WLAN_SCAN_PNO + case WOW_REASON_NLO_SCAN_COMPLETE: + errno = target_if_nlo_complete_handler(wma, pb_event, + pb_event_len); + break; +#endif /* FEATURE_WLAN_SCAN_PNO */ + + case WOW_REASON_CSA_EVENT: + errno = wma_csa_offload_handler(wma, pb_event, pb_event_len); + break; + + /* + * WOW_REASON_LOW_RSSI is used for following roaming events - + * WMI_ROAM_REASON_BETTER_AP, WMI_ROAM_REASON_BMISS, + * WMI_ROAM_REASON_SUITABLE_AP will be handled by + * wma_roam_event_callback(). + * WOW_REASON_ROAM_HO is associated with + * WMI_ROAM_REASON_HO_FAILED event and it will be handled by + * wma_roam_event_callback(). + */ + case WOW_REASON_LOW_RSSI: + case WOW_REASON_ROAM_HO: + wlan_roam_debug_log(event_param->fixed_param->vdev_id, + DEBUG_WOW_ROAM_EVENT, + DEBUG_INVALID_PEER_ID, + NULL, NULL, wake_reason, + pb_event_len); + if (pb_event_len > 0) { + errno = wma_roam_event_callback(wma, pb_event, + pb_event_len); + } else { + /* + * No wow_packet_buffer means a better AP beacon + * will follow in a later event. + */ + WMA_LOGD("Host woken up because of better AP beacon"); + } + break; + + case WOW_REASON_CLIENT_KICKOUT_EVENT: + errno = wma_peer_sta_kickout_event_handler(wma, pb_event, + pb_event_len); + break; + +#ifdef FEATURE_WLAN_EXTSCAN + case WOW_REASON_EXTSCAN: + errno = wma_extscan_wow_event_callback(wma, pb_event, + pb_event_len); + break; +#endif + + case WOW_REASON_RSSI_BREACH_EVENT: + errno = wma_rssi_breached_event_handler(wma, pb_event, + pb_event_len); + break; + + case WOW_REASON_NAN_EVENT: + errno = wma_nan_rsp_handler_callback(wma, pb_event, + pb_event_len); + break; + + case WOW_REASON_NAN_DATA: + errno = wma_ndp_wow_event_callback(wma, pb_event, pb_event_len, + event_id); + break; + +#ifdef FEATURE_WLAN_TDLS + case WOW_REASON_TDLS_CONN_TRACKER_EVENT: + errno = wma_tdls_event_handler(wma, pb_event, pb_event_len); + break; +#endif + + case WOW_REASON_TIMER_INTR_RECV: + /* + * Right now firmware is not returning any cookie host has + * programmed. So do not check for cookie. + */ + WMA_LOGE("WOW_REASON_TIMER_INTR_RECV received, indicating key exchange did not finish. Initiate disconnect"); + del_sta_ctx = qdf_mem_malloc(sizeof(*del_sta_ctx)); + if (!del_sta_ctx) + break; + + del_sta_ctx->is_tdls = false; + del_sta_ctx->vdev_id = event_param->fixed_param->vdev_id; + qdf_mem_copy(del_sta_ctx->addr2, bssid, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(del_sta_ctx->bssId, bssid, QDF_MAC_ADDR_SIZE); + del_sta_ctx->reasonCode = HAL_DEL_STA_REASON_CODE_KEEP_ALIVE; + wma_send_msg(wma, SIR_LIM_DELETE_STA_CONTEXT_IND, del_sta_ctx, + 0); + break; + case WOW_REASON_ROAM_PMKID_REQUEST: + WMA_LOGD("Host woken up because of PMKID request event"); + errno = wma_roam_pmkid_request_event_handler(wma, pb_event, + pb_event_len); + break; + default: + WMA_LOGE("Wake reason %s(%u) is not a piggybacked event", + wma_wow_wake_reason_str(wake_reason), wake_reason); + errno = -EINVAL; + break; + } + + wmitlv_free_allocated_event_tlvs(event_id, &pb_event); + + return errno; +} + +static void wma_debug_assert_page_fault_wakeup(uint32_t reason) +{ + /* During DRV if page fault wake up then assert */ + if ((WOW_REASON_PAGE_FAULT == reason) && (qdf_is_drv_connected())) + QDF_DEBUG_PANIC("Unexpected page fault wake up detected during DRV wow"); +} + +static void wma_wake_event_log_reason(t_wma_handle *wma, + WOW_EVENT_INFO_fixed_param *wake_info) +{ + struct wma_txrx_node *vdev; + + /* "Unspecified" means APPS triggered wake, else firmware triggered */ + if (wake_info->wake_reason != WOW_REASON_UNSPECIFIED) { + vdev = &wma->interfaces[wake_info->vdev_id]; + WMA_LOGA("WLAN triggered wakeup: %s (%d), vdev: %d (%s)", + wma_wow_wake_reason_str(wake_info->wake_reason), + wake_info->wake_reason, + wake_info->vdev_id, + wma_vdev_type_str(vdev->type)); + wma_debug_assert_page_fault_wakeup(wake_info->wake_reason); + } else if (!wmi_get_runtime_pm_inprogress(wma->wmi_handle)) { + WMA_LOGA("Non-WLAN triggered wakeup: %s (%d)", + wma_wow_wake_reason_str(wake_info->wake_reason), + wake_info->wake_reason); + } + + qdf_wow_wakeup_host_event(wake_info->wake_reason); + qdf_wma_wow_wakeup_stats_event(wma); +} + +/** + * wma_wow_wakeup_host_event() - wakeup host event handler + * @handle: wma handle + * @event: event data + * @len: buffer length + * + * Handler to catch wow wakeup host event. This event will have + * reason why the firmware has woken the host. + * + * Return: Errno + */ +int wma_wow_wakeup_host_event(void *handle, uint8_t *event, uint32_t len) +{ + int errno; + t_wma_handle *wma = handle; + WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param; + WOW_EVENT_INFO_fixed_param *wake_info; + + event_param = (WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *)event; + if (!event_param) { + WMA_LOGE("Wake event data is null"); + return -EINVAL; + } + + wake_info = event_param->fixed_param; + + if (wake_info->vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: received invalid vdev_id %d", + __func__, wake_info->vdev_id); + return -EINVAL; + } + + wma_wake_event_log_reason(wma, wake_info); + + ucfg_pmo_psoc_wakeup_host_event_received(wma->psoc); + + wma_print_wow_stats(wma, wake_info); + /* split based on payload type */ + if (is_piggybacked_event(wake_info->wake_reason)) + errno = wma_wake_event_piggybacked(wma, event_param, len); + else if (event_param->wow_packet_buffer) + errno = wma_wake_event_packet(wma, event_param, len); + else + errno = wma_wake_event_no_payload(wma, event_param, len); + + wma_inc_wow_stats(wma, wake_info); + wma_print_wow_stats(wma, wake_info); + wma_acquire_wow_wakelock(wma, wake_info->wake_reason); + + return errno; +} + +#ifdef FEATURE_WLAN_D0WOW +/** + * wma_d0_wow_disable_ack_event() - wakeup host event handler + * @handle: wma handle + * @event: event data + * @len: buffer length + * + * Handler to catch D0-WOW disable ACK event. This event will have + * reason why the firmware has woken the host. + * This is for backward compatible with cld2.0. + * + * Return: 0 for success or error + */ +int wma_d0_wow_disable_ack_event(void *handle, uint8_t *event, uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle)handle; + WMI_D0_WOW_DISABLE_ACK_EVENTID_param_tlvs *param_buf; + wmi_d0_wow_disable_ack_event_fixed_param *resp_data; + + param_buf = (WMI_D0_WOW_DISABLE_ACK_EVENTID_param_tlvs *)event; + if (!param_buf) { + WMA_LOGE("Invalid D0-WOW disable ACK event buffer!"); + return -EINVAL; + } + + resp_data = param_buf->fixed_param; + + ucfg_pmo_psoc_wakeup_host_event_received(wma->psoc); + + WMA_LOGD("Received D0-WOW disable ACK"); + + return 0; +} +#else +int wma_d0_wow_disable_ack_event(void *handle, uint8_t *event, uint32_t len) +{ + return 0; +} +#endif + +/** + * wma_pdev_resume_event_handler() - PDEV resume event handler + * @handle: wma handle + * @event: event data + * @len: buffer length + * + * Return: 0 for success or error + */ +int wma_pdev_resume_event_handler(void *handle, uint8_t *event, uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + + WMA_LOGA("Received PDEV resume event"); + + ucfg_pmo_psoc_wakeup_host_event_received(wma->psoc); + + return 0; +} + +/** + * wma_del_ts_req() - send DELTS request to fw + * @wma: wma handle + * @msg: delts params + * + * Return: none + */ +void wma_del_ts_req(tp_wma_handle wma, struct del_ts_params *msg) +{ + if (!wma_is_vdev_valid(msg->sessionId)) { + WMA_LOGE("%s: vdev id:%d is not active ", __func__, + msg->sessionId); + qdf_mem_free(msg); + return; + } + if (wmi_unified_del_ts_cmd(wma->wmi_handle, + msg->sessionId, + TID_TO_WME_AC(msg->userPrio))) { + WMA_LOGP("%s: Failed to send vdev DELTS command", __func__); + } + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (msg->setRICparams == true) + wma_set_ric_req(wma, msg, false); +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + qdf_mem_free(msg); +} + +void wma_aggr_qos_req(tp_wma_handle wma, + struct aggr_add_ts_param *aggr_qos_rsp_msg) +{ + if (!wma_is_vdev_valid(aggr_qos_rsp_msg->vdev_id)) { + WMA_LOGE("%s: vdev id:%d is not active ", __func__, + aggr_qos_rsp_msg->vdev_id); + return; + } + wmi_unified_aggr_qos_cmd(wma->wmi_handle, aggr_qos_rsp_msg); + /* send response to upper layers from here only. */ + wma_send_msg_high_priority(wma, WMA_AGGR_QOS_RSP, aggr_qos_rsp_msg, 0); +} + +#ifdef FEATURE_WLAN_ESE +/** + * wma_set_tsm_interval() - Set TSM interval + * @req: pointer to ADDTS request + * + * Return: QDF_STATUS_E_FAILURE or QDF_STATUS_SUCCESS + */ +static QDF_STATUS wma_set_tsm_interval(struct add_ts_param *req) +{ + /* + * msmt_interval is in unit called TU (1 TU = 1024 us) + * max value of msmt_interval cannot make resulting + * interval_milliseconds overflow 32 bit + * + */ + uint32_t interval_milliseconds; + + interval_milliseconds = (req->tsm_interval * 1024) / 1000; + + cdp_tx_set_compute_interval(cds_get_context(QDF_MODULE_ID_SOC), + WMI_PDEV_ID_SOC, + interval_milliseconds); + return QDF_STATUS_SUCCESS; +} +#else +static inline QDF_STATUS wma_set_tsm_interval(struct add_ts_param *req) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_WLAN_ESE */ + +/** + * wma_add_ts_req() - send ADDTS request to fw + * @wma: wma handle + * @msg: ADDTS params + * + * Return: none + */ +void wma_add_ts_req(tp_wma_handle wma, struct add_ts_param *msg) +{ + struct add_ts_param cmd = {0}; + + msg->status = QDF_STATUS_SUCCESS; + if (wma_set_tsm_interval(msg) == QDF_STATUS_SUCCESS) { + + cmd.vdev_id = msg->vdev_id; + cmd.tspec.tsinfo.traffic.userPrio = + TID_TO_WME_AC(msg->tspec.tsinfo.traffic.userPrio); + cmd.tspec.mediumTime = msg->tspec.mediumTime; + if (wmi_unified_add_ts_cmd(wma->wmi_handle, &cmd)) + msg->status = QDF_STATUS_E_FAILURE; + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + if (msg->set_ric_params) + wma_set_ric_req(wma, msg, true); +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + + } + wma_send_msg_high_priority(wma, WMA_ADD_TS_RSP, msg, 0); +} + +#ifdef FEATURE_WLAN_ESE + +#define TSM_DELAY_HISTROGRAM_BINS 4 +/** + * wma_process_tsm_stats_req() - process tsm stats request + * @wma_handler - handle to wma + * @pTsmStatsMsg - TSM stats struct that needs to be populated and + * passed in message. + * + * A parallel function to WMA_ProcessTsmStatsReq for pronto. This + * function fetches stats from data path APIs and post + * WMA_TSM_STATS_RSP msg back to LIM. + * + * Return: QDF status + */ +QDF_STATUS wma_process_tsm_stats_req(tp_wma_handle wma_handler, + void *pTsmStatsMsg) +{ + uint8_t counter; + uint32_t queue_delay_microsec = 0; + uint32_t tx_delay_microsec = 0; + uint16_t packet_count = 0; + uint16_t packet_loss_count = 0; + tpAniTrafStrmMetrics pTsmMetric = NULL; + tpAniGetTsmStatsReq pStats = (tpAniGetTsmStatsReq) pTsmStatsMsg; + tpAniGetTsmStatsRsp pTsmRspParams = NULL; + int tid = pStats->tid; + /* + * The number of histrogram bin report by data path api are different + * than required by TSM, hence different (6) size array used + */ + uint16_t bin_values[QCA_TX_DELAY_HIST_REPORT_BINS] = { 0, }; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + /* get required values from data path APIs */ + cdp_tx_delay(soc, + WMI_PDEV_ID_SOC, + &queue_delay_microsec, + &tx_delay_microsec, tid); + cdp_tx_delay_hist(soc, + WMI_PDEV_ID_SOC, + bin_values, tid); + cdp_tx_packet_count(soc, + WMI_PDEV_ID_SOC, + &packet_count, + &packet_loss_count, tid); + + pTsmRspParams = qdf_mem_malloc(sizeof(*pTsmRspParams)); + if (!pTsmRspParams) { + QDF_ASSERT(0); + qdf_mem_free(pTsmStatsMsg); + return QDF_STATUS_E_NOMEM; + } + qdf_copy_macaddr(&pTsmRspParams->bssid, &pStats->bssId); + pTsmRspParams->rc = QDF_STATUS_E_FAILURE; + pTsmRspParams->tsmStatsReq = pStats; + pTsmMetric = &pTsmRspParams->tsmMetrics; + /* populate pTsmMetric */ + pTsmMetric->UplinkPktQueueDly = queue_delay_microsec; + /* store only required number of bin values */ + for (counter = 0; counter < TSM_DELAY_HISTROGRAM_BINS; counter++) { + pTsmMetric->UplinkPktQueueDlyHist[counter] = + bin_values[counter]; + } + pTsmMetric->UplinkPktTxDly = tx_delay_microsec; + pTsmMetric->UplinkPktLoss = packet_loss_count; + pTsmMetric->UplinkPktCount = packet_count; + + /* + * No need to populate roaming delay and roaming count as they are + * being populated just before sending IAPP frame out + */ + /* post this message to LIM/PE */ + wma_send_msg(wma_handler, WMA_TSM_STATS_RSP, (void *)pTsmRspParams, 0); + return QDF_STATUS_SUCCESS; +} + +#endif /* FEATURE_WLAN_ESE */ + +/** + * wma_process_mcbc_set_filter_req() - process mcbc set filter request + * @wma_handle: wma handle + * @mcbc_param: mcbc params + * + * Return: QDF status + */ +QDF_STATUS wma_process_mcbc_set_filter_req(tp_wma_handle wma_handle, + tSirRcvFltMcAddrList *mcbc_param) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * wma_process_cesium_enable_ind() - enables cesium functionality in target + * @wma: wma handle + * + * Return: QDF status + */ +QDF_STATUS wma_process_cesium_enable_ind(tp_wma_handle wma) +{ + QDF_STATUS ret; + int32_t vdev_id; + + vdev_id = wma_find_vdev_by_type(wma, WMI_VDEV_TYPE_IBSS); + if (vdev_id < 0) { + WMA_LOGE("%s: IBSS vdev does not exist could not enable cesium", + __func__); + return QDF_STATUS_E_FAILURE; + } + + /* Send enable cesium command to target */ + WMA_LOGE("Enable cesium in target for vdevId %d ", vdev_id); + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_ENABLE_RMC, 1); + if (ret) { + WMA_LOGE("Enable cesium failed for vdevId %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** + * wma_process_get_peer_info_req() - sends get peer info cmd to target + * @wma: wma handle + * @preq: get peer info request + * + * Return: QDF status + */ +QDF_STATUS wma_process_get_peer_info_req + (tp_wma_handle wma, tSirIbssGetPeerInfoReqParams *pReq) +{ + int32_t ret; + uint8_t *p; + uint16_t len; + wmi_buf_t buf; + int32_t vdev_id; + uint8_t pdev_id; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + wmi_peer_info_req_cmd_fixed_param *p_get_peer_info_cmd; + uint8_t bcast_mac[QDF_MAC_ADDR_SIZE] = { 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff }; + + if (!soc) { + WMA_LOGE("%s: SOC context is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + vdev_id = wma_find_vdev_by_type(wma, WMI_VDEV_TYPE_IBSS); + if (vdev_id < 0) { + WMA_LOGE("%s: IBSS vdev does not exist could not get peer info", + __func__); + return QDF_STATUS_E_FAILURE; + } + + pdev_id = WMI_PDEV_ID_SOC; + if (pdev_id == OL_TXRX_INVALID_PDEV_ID) { + WMA_LOGE("%s: Failed to get pdev id", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (qdf_is_macaddr_broadcast(&pReq->peer_mac)) { + /*get info for all peers */ + qdf_mem_copy(peer_mac, bcast_mac, QDF_MAC_ADDR_SIZE); + } else { + /*get info for a single peer */ + if (!cdp_find_peer_exist(soc, pdev_id, pReq->peer_mac.bytes)) { + WMA_LOGE("%s: Failed to get peer handle using peer " + QDF_MAC_ADDR_FMT, __func__, + QDF_MAC_ADDR_REF(pReq->peer_mac.bytes)); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGE("%s: peer mac: " QDF_MAC_ADDR_FMT, __func__, + QDF_MAC_ADDR_REF(pReq->peer_mac.bytes)); + qdf_mem_copy(peer_mac, pReq->peer_mac.bytes, QDF_MAC_ADDR_SIZE); + } + + len = sizeof(wmi_peer_info_req_cmd_fixed_param); + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_FAILURE; + + p = (uint8_t *) wmi_buf_data(buf); + qdf_mem_zero(p, len); + p_get_peer_info_cmd = (wmi_peer_info_req_cmd_fixed_param *) p; + + WMITLV_SET_HDR(&p_get_peer_info_cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_peer_info_req_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN + (wmi_peer_info_req_cmd_fixed_param)); + + p_get_peer_info_cmd->vdev_id = vdev_id; + WMI_CHAR_ARRAY_TO_MAC_ADDR(peer_mac, + &p_get_peer_info_cmd->peer_mac_address); + + ret = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_PEER_INFO_REQ_CMDID); + if (ret != QDF_STATUS_SUCCESS) + wmi_buf_free(buf); + + WMA_LOGE("IBSS get peer info cmd sent len: %d, vdev %d command id: %d, status: %d", + len, vdev_id, WMI_PEER_INFO_REQ_CMDID, ret); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_process_tx_fail_monitor_ind() - sends tx fail monitor cmd to target + * @wma: wma handle + * @pReq: tx fail monitor command params + * + * Return: QDF status + */ +QDF_STATUS wma_process_tx_fail_monitor_ind(tp_wma_handle wma, + tAniTXFailMonitorInd *pReq) +{ + QDF_STATUS ret; + int32_t vdev_id; + + vdev_id = wma_find_vdev_by_type(wma, WMI_VDEV_TYPE_IBSS); + if (vdev_id < 0) { + WMA_LOGE("%s: IBSS vdev does not exist could not send fast tx fail monitor indication message to target", + __func__); + return QDF_STATUS_E_FAILURE; + } + + /* Send enable cesium command to target */ + WMA_LOGE("send fast tx fail monitor ind cmd target for vdevId %d val %d", + vdev_id, pReq->tx_fail_count); + + if (pReq->tx_fail_count == 0) + wma->hddTxFailCb = NULL; + else + wma->hddTxFailCb = pReq->txFailIndCallback; + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_SET_IBSS_TX_FAIL_CNT_THR, + pReq->tx_fail_count); + if (ret) { + WMA_LOGE("tx fail monitor failed for vdevId %d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_RMC +/** + * wma_process_rmc_enable_ind() - enables RMC functionality in target + * @wma: wma handle + * + * Return: QDF status + */ +QDF_STATUS wma_process_rmc_enable_ind(tp_wma_handle wma) +{ + int ret; + uint8_t *p; + uint16_t len; + wmi_buf_t buf; + int32_t vdev_id; + wmi_rmc_set_mode_cmd_fixed_param *p_rmc_enable_cmd; + + vdev_id = wma_find_vdev_by_type(wma, WMI_VDEV_TYPE_IBSS); + if (vdev_id < 0) { + WMA_LOGE("%s: IBSS vdev does not exist could not enable RMC", + __func__); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(wmi_rmc_set_mode_cmd_fixed_param); + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_FAILURE; + + p = (uint8_t *) wmi_buf_data(buf); + qdf_mem_zero(p, len); + p_rmc_enable_cmd = (wmi_rmc_set_mode_cmd_fixed_param *) p; + + WMITLV_SET_HDR(&p_rmc_enable_cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_rmc_set_mode_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN + (wmi_rmc_set_mode_cmd_fixed_param)); + + p_rmc_enable_cmd->vdev_id = vdev_id; + p_rmc_enable_cmd->enable_rmc = WMI_RMC_MODE_ENABLED; + + ret = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_RMC_SET_MODE_CMDID); + if (ret != QDF_STATUS_SUCCESS) + wmi_buf_free(buf); + + WMA_LOGE("Enable RMC cmd sent len: %d, vdev %d command id: %d, status: %d", + len, vdev_id, WMI_RMC_SET_MODE_CMDID, ret); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_process_rmc_disable_ind() - disables rmc functionality in target + * @wma: wma handle + * + * Return: QDF status + */ +QDF_STATUS wma_process_rmc_disable_ind(tp_wma_handle wma) +{ + int ret; + uint8_t *p; + uint16_t len; + wmi_buf_t buf; + int32_t vdev_id; + wmi_rmc_set_mode_cmd_fixed_param *p_rmc_disable_cmd; + + vdev_id = wma_find_vdev_by_type(wma, WMI_VDEV_TYPE_IBSS); + if (vdev_id < 0) { + WMA_LOGE("%s: IBSS vdev does not exist could not disable RMC", + __func__); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(wmi_rmc_set_mode_cmd_fixed_param); + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_FAILURE; + + p = (uint8_t *) wmi_buf_data(buf); + qdf_mem_zero(p, len); + p_rmc_disable_cmd = (wmi_rmc_set_mode_cmd_fixed_param *) p; + + WMITLV_SET_HDR(&p_rmc_disable_cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_rmc_set_mode_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN + (wmi_rmc_set_mode_cmd_fixed_param)); + + p_rmc_disable_cmd->vdev_id = vdev_id; + p_rmc_disable_cmd->enable_rmc = WMI_RMC_MODE_DISABLED; + + ret = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_RMC_SET_MODE_CMDID); + if (ret != QDF_STATUS_SUCCESS) + wmi_buf_free(buf); + + WMA_LOGE("Disable RMC cmd sent len: %d, vdev %d command id: %d, status: %d", + len, vdev_id, WMI_RMC_SET_MODE_CMDID, ret); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_process_rmc_action_period_ind() - sends RMC action period to target + * @wma: wma handle + * + * Return: QDF status + */ +QDF_STATUS wma_process_rmc_action_period_ind(tp_wma_handle wma) +{ + int ret; + uint8_t *p; + uint16_t len; + uint32_t periodicity_msec; + wmi_buf_t buf; + int32_t vdev_id; + wmi_rmc_set_action_period_cmd_fixed_param *p_rmc_cmd; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: MAC mac does not exist", __func__); + return QDF_STATUS_E_FAILURE; + } + + vdev_id = wma_find_vdev_by_type(wma, WMI_VDEV_TYPE_IBSS); + if (vdev_id < 0) { + WMA_LOGE("%s: IBSS vdev does not exist could not send RMC action period to target", + __func__); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(wmi_rmc_set_action_period_cmd_fixed_param); + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_FAILURE; + + p = (uint8_t *) wmi_buf_data(buf); + qdf_mem_zero(p, len); + p_rmc_cmd = (wmi_rmc_set_action_period_cmd_fixed_param *) p; + + WMITLV_SET_HDR(&p_rmc_cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_rmc_set_action_period_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN + (wmi_rmc_set_action_period_cmd_fixed_param)); + + periodicity_msec = mac->mlme_cfg->sap_cfg.rmc_action_period_freq; + p_rmc_cmd->vdev_id = vdev_id; + p_rmc_cmd->periodicity_msec = periodicity_msec; + + ret = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_RMC_SET_ACTION_PERIOD_CMDID); + if (ret != QDF_STATUS_SUCCESS) + wmi_buf_free(buf); + + WMA_LOGE("RMC action period %d cmd sent len: %d, vdev %d command id: %d, status: %d", + periodicity_msec, len, vdev_id, WMI_RMC_SET_ACTION_PERIOD_CMDID, + ret); + + return QDF_STATUS_SUCCESS; +} +#endif /* FEATURE_WLAN_RMC */ + +/** + * wma_process_add_periodic_tx_ptrn_ind() - add periodic tx pattern + * @handle: wma handle + * @pattern: tx pattern params + * + * Return: QDF status + */ +QDF_STATUS wma_process_add_periodic_tx_ptrn_ind(WMA_HANDLE handle, + tSirAddPeriodicTxPtrn *pattern) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + struct periodic_tx_pattern *params_ptr; + uint8_t vdev_id; + QDF_STATUS status; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue fw add pattern cmd", + __func__); + return QDF_STATUS_E_INVAL; + } + + if (wma_find_vdev_id_by_addr(wma_handle, + pattern->mac_address.bytes, + &vdev_id)) { + WMA_LOGE("%s: Failed to find vdev id for "QDF_MAC_ADDR_FMT, + __func__, + QDF_MAC_ADDR_REF(pattern->mac_address.bytes)); + return QDF_STATUS_E_INVAL; + } + + params_ptr = qdf_mem_malloc(sizeof(*params_ptr)); + if (!params_ptr) + return QDF_STATUS_E_NOMEM; + + params_ptr->ucPtrnId = pattern->ucPtrnId; + params_ptr->ucPtrnSize = pattern->ucPtrnSize; + params_ptr->usPtrnIntervalMs = pattern->usPtrnIntervalMs; + qdf_mem_copy(¶ms_ptr->mac_address, &pattern->mac_address, + sizeof(struct qdf_mac_addr)); + qdf_mem_copy(params_ptr->ucPattern, pattern->ucPattern, + params_ptr->ucPtrnSize); + + status = wmi_unified_process_add_periodic_tx_ptrn_cmd( + wma_handle->wmi_handle, params_ptr, vdev_id); + + qdf_mem_free(params_ptr); + return status; +} + +/** + * wma_process_del_periodic_tx_ptrn_ind - del periodic tx ptrn + * @handle: wma handle + * @pDelPeriodicTxPtrnParams: tx ptrn params + * + * Return: QDF status + */ +QDF_STATUS wma_process_del_periodic_tx_ptrn_ind(WMA_HANDLE handle, + tSirDelPeriodicTxPtrn * + pDelPeriodicTxPtrnParams) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + uint8_t vdev_id; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue Del Pattern cmd", + __func__); + return QDF_STATUS_E_INVAL; + } + + if (wma_find_vdev_id_by_addr( + wma_handle, + pDelPeriodicTxPtrnParams->mac_address.bytes, + &vdev_id)) { + WMA_LOGE("%s: Failed to find vdev id for "QDF_MAC_ADDR_FMT, + __func__, + QDF_MAC_ADDR_REF(pDelPeriodicTxPtrnParams->mac_address.bytes)); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_process_del_periodic_tx_ptrn_cmd( + wma_handle->wmi_handle, vdev_id, + pDelPeriodicTxPtrnParams->ucPtrnId); +} + +#ifdef WLAN_FEATURE_STATS_EXT +QDF_STATUS wma_stats_ext_req(void *wma_ptr, tpStatsExtRequest preq) +{ + tp_wma_handle wma = (tp_wma_handle) wma_ptr; + struct stats_ext_params *params; + size_t params_len; + QDF_STATUS status; + + if (!wma) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + params_len = sizeof(*params) + preq->request_data_len; + params = qdf_mem_malloc(params_len); + if (!params) + return QDF_STATUS_E_NOMEM; + + params->vdev_id = preq->vdev_id; + params->request_data_len = preq->request_data_len; + if (preq->request_data_len > 0) + qdf_mem_copy(params->request_data, preq->request_data, + params->request_data_len); + + status = wmi_unified_stats_ext_req_cmd(wma->wmi_handle, params); + qdf_mem_free(params); + + return status; +} + +#endif /* WLAN_FEATURE_STATS_EXT */ + +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT +/** + * wma_send_status_of_ext_wow() - send ext wow status to SME + * @wma: wma handle + * @status: status + * + * Return: none + */ +static void wma_send_status_of_ext_wow(tp_wma_handle wma, bool status) +{ + tSirReadyToExtWoWInd *ready_to_extwow; + QDF_STATUS vstatus; + struct scheduler_msg message = {0}; + uint8_t len; + + WMA_LOGD("Posting ready to suspend indication to umac"); + + len = sizeof(tSirReadyToExtWoWInd); + ready_to_extwow = qdf_mem_malloc(len); + if (!ready_to_extwow) + return; + + ready_to_extwow->mesgType = eWNI_SME_READY_TO_EXTWOW_IND; + ready_to_extwow->mesgLen = len; + ready_to_extwow->status = status; + + message.type = eWNI_SME_READY_TO_EXTWOW_IND; + message.bodyptr = (void *)ready_to_extwow; + message.bodyval = 0; + + vstatus = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &message); + if (vstatus != QDF_STATUS_SUCCESS) + qdf_mem_free(ready_to_extwow); +} + +/** + * wma_enable_ext_wow() - enable ext wow in fw + * @wma: wma handle + * @params: ext wow params + * + * Return:0 for success or error code + */ +QDF_STATUS wma_enable_ext_wow(tp_wma_handle wma, tpSirExtWoWParams params) +{ + struct ext_wow_params wow_params = {0}; + QDF_STATUS status; + + if (!wma) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + wow_params.vdev_id = params->vdev_id; + wow_params.type = (enum wmi_ext_wow_type) params->type; + wow_params.wakeup_pin_num = params->wakeup_pin_num; + + status = wmi_unified_enable_ext_wow_cmd(wma->wmi_handle, + &wow_params); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + wma_send_status_of_ext_wow(wma, true); + return status; + +} + +/** + * wma_set_app_type1_params_in_fw() - set app type1 params in fw + * @wma: wma handle + * @appType1Params: app type1 params + * + * Return: QDF status + */ +int wma_set_app_type1_params_in_fw(tp_wma_handle wma, + tpSirAppType1Params appType1Params) +{ + int ret; + + ret = wmi_unified_app_type1_params_in_fw_cmd(wma->wmi_handle, + (struct app_type1_params *)appType1Params); + if (ret) { + WMA_LOGE("%s: Failed to set APP TYPE1 PARAMS", __func__); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_set_app_type2_params_in_fw() - set app type2 params in fw + * @wma: wma handle + * @appType2Params: app type2 params + * + * Return: QDF status + */ +QDF_STATUS wma_set_app_type2_params_in_fw(tp_wma_handle wma, + tpSirAppType2Params appType2Params) +{ + struct app_type2_params params = {0}; + + if (!wma) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + params.vdev_id = appType2Params->vdev_id; + params.rc4_key_len = appType2Params->rc4_key_len; + qdf_mem_copy(params.rc4_key, appType2Params->rc4_key, 16); + params.ip_id = appType2Params->ip_id; + params.ip_device_ip = appType2Params->ip_device_ip; + params.ip_server_ip = appType2Params->ip_server_ip; + params.tcp_src_port = appType2Params->tcp_src_port; + params.tcp_dst_port = appType2Params->tcp_dst_port; + params.tcp_seq = appType2Params->tcp_seq; + params.tcp_ack_seq = appType2Params->tcp_ack_seq; + params.keepalive_init = appType2Params->keepalive_init; + params.keepalive_min = appType2Params->keepalive_min; + params.keepalive_max = appType2Params->keepalive_max; + params.keepalive_inc = appType2Params->keepalive_inc; + params.tcp_tx_timeout_val = appType2Params->tcp_tx_timeout_val; + params.tcp_rx_timeout_val = appType2Params->tcp_rx_timeout_val; + qdf_mem_copy(¶ms.gateway_mac, &appType2Params->gateway_mac, + sizeof(struct qdf_mac_addr)); + + return wmi_unified_set_app_type2_params_in_fw_cmd(wma->wmi_handle, + ¶ms); +} +#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */ + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN +/** + * wma_auto_shutdown_event_handler() - process auto shutdown timer trigger + * @handle: wma handle + * @event: event buffer + * @len: buffer length + * + * Return: 0 for success or error code + */ +int wma_auto_shutdown_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + wmi_host_auto_shutdown_event_fixed_param *wmi_auto_sh_evt; + WMI_HOST_AUTO_SHUTDOWN_EVENTID_param_tlvs *param_buf = + (WMI_HOST_AUTO_SHUTDOWN_EVENTID_param_tlvs *) + event; + + if (!param_buf || !param_buf->fixed_param) { + WMA_LOGE("%s:%d: Invalid Auto shutdown timer evt", __func__, + __LINE__); + return -EINVAL; + } + + wmi_auto_sh_evt = param_buf->fixed_param; + + if (wmi_auto_sh_evt->shutdown_reason + != WMI_HOST_AUTO_SHUTDOWN_REASON_TIMER_EXPIRY) { + WMA_LOGE("%s:%d: Invalid Auto shutdown timer evt", __func__, + __LINE__); + return -EINVAL; + } + + WMA_LOGD("%s:%d: Auto Shutdown Evt: %d", __func__, __LINE__, + wmi_auto_sh_evt->shutdown_reason); + return wma_wake_reason_auto_shutdown(); +} + +QDF_STATUS +wma_set_auto_shutdown_timer_req(tp_wma_handle wma_handle, + struct auto_shutdown_cmd *auto_sh_cmd) +{ + if (!auto_sh_cmd) { + WMA_LOGE("%s : Invalid Autoshutdown cfg cmd", __func__); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_set_auto_shutdown_timer_cmd(wma_handle->wmi_handle, + auto_sh_cmd->timer_val); +} +#endif /* FEATURE_WLAN_AUTO_SHUTDOWN */ + +#ifdef DHCP_SERVER_OFFLOAD +QDF_STATUS +wma_process_dhcpserver_offload(tp_wma_handle wma_handle, + struct dhcp_offload_info_params *params) +{ + QDF_STATUS status; + wmi_unified_t wmi_handle; + + if (!wma_handle) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + wmi_handle = wma_handle->wmi_handle; + status = wmi_unified_process_dhcpserver_offload_cmd(wmi_handle, + params); + WMA_LOGD("Set dhcp server offload to vdev %d status %d", + params->vdev_id, status); + + return status; +} +#endif /* DHCP_SERVER_OFFLOAD */ + +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING +/** + * wma_set_led_flashing() - set led flashing in fw + * @wma_handle: wma handle + * @flashing: flashing request + * + * Return: QDF status + */ +QDF_STATUS wma_set_led_flashing(tp_wma_handle wma_handle, + struct flashing_req_params *flashing) +{ + QDF_STATUS status; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE(FL("WMA is closed, can not issue cmd")); + return QDF_STATUS_E_INVAL; + } + if (!flashing) { + WMA_LOGE(FL("invalid parameter: flashing")); + return QDF_STATUS_E_INVAL; + } + status = wmi_unified_set_led_flashing_cmd(wma_handle->wmi_handle, + flashing); + return status; +} +#endif /* WLAN_FEATURE_GPIO_LED_FLASHING */ + +int wma_sar_rsp_evt_handler(ol_scn_t handle, uint8_t *event, uint32_t len) +{ + tp_wma_handle wma_handle; + wmi_unified_t wmi_handle; + QDF_STATUS status; + + WMA_LOGD(FL("handle:%pK event:%pK len:%u"), handle, event, len); + + wma_handle = handle; + if (!wma_handle) { + WMA_LOGE(FL("NULL wma_handle")); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE(FL("NULL wmi_handle")); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_extract_sar2_result_event(wmi_handle, + event, len); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE(FL("Event extract failure: %d"), status); + return -EINVAL; + } + + return 0; +} + +#ifdef FEATURE_WLAN_CH_AVOID +/** + * wma_process_ch_avoid_update_req() - handles channel avoid update request + * @wma_handle: wma handle + * @ch_avoid_update_req: channel avoid update params + * + * Return: QDF status + */ +QDF_STATUS wma_process_ch_avoid_update_req(tp_wma_handle wma_handle, + tSirChAvoidUpdateReq * + ch_avoid_update_req) +{ + QDF_STATUS status; + + if (!wma_handle) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + if (!ch_avoid_update_req) { + WMA_LOGE("%s : ch_avoid_update_req is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("%s: WMA --> WMI_CHAN_AVOID_UPDATE", __func__); + + status = wmi_unified_process_ch_avoid_update_cmd( + wma_handle->wmi_handle); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + WMA_LOGD("%s: WMA --> WMI_CHAN_AVOID_UPDATE sent through WMI", + __func__); + return status; +} +#endif + +void wma_send_regdomain_info_to_fw(uint32_t reg_dmn, uint16_t regdmn2G, + uint16_t regdmn5G, uint8_t ctl2G, + uint8_t ctl5G) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + int32_t cck_mask_val = 0; + struct pdev_params pdev_param = {0}; + QDF_STATUS ret = QDF_STATUS_SUCCESS; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + WMA_LOGD("reg_dmn: %d regdmn2g: %d regdmn5g :%d ctl2g: %d ctl5g: %d", + reg_dmn, regdmn2G, regdmn5G, ctl2G, ctl5G); + + if (!wma) { + WMA_LOGE("%s: wma context is NULL", __func__); + return; + } + + status = wmi_unified_send_regdomain_info_to_fw_cmd(wma->wmi_handle, + reg_dmn, regdmn2G, regdmn5G, ctl2G, ctl5G); + if (status == QDF_STATUS_E_NOMEM) + return; + + if ((((reg_dmn & ~CTRY_FLAG) == CTRY_JAPAN15) || + ((reg_dmn & ~CTRY_FLAG) == CTRY_KOREA_ROC)) && + (true == wma->tx_chain_mask_cck)) + cck_mask_val = 1; + + cck_mask_val |= (wma->self_gen_frm_pwr << 16); + pdev_param.param_id = WMI_PDEV_PARAM_TX_CHAIN_MASK_CCK; + pdev_param.param_value = cck_mask_val; + ret = wmi_unified_pdev_param_send(wma->wmi_handle, + &pdev_param, + WMA_WILDCARD_PDEV_ID); + + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("failed to set PDEV tx_chain_mask_cck %d", + ret); +} + +#ifdef FEATURE_WLAN_TDLS +/** + * wma_tdls_event_handler() - handle TDLS event + * @handle: wma handle + * @event: event buffer + * @len: buffer length + * + * Return: 0 for success or error code + */ +int wma_tdls_event_handler(void *handle, uint8_t *event, uint32_t len) +{ + /* TODO update with target rx ops */ + return 0; +} + +/** + * wma_update_tdls_peer_state() - update TDLS peer state + * @handle: wma handle + * @peer_state: TDLS peer state params + * + * Return: 0 for success or error code + */ +int wma_update_tdls_peer_state(WMA_HANDLE handle, + struct tdls_peer_update_state *peer_state) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + uint32_t i; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct tdls_peer_params *peer_cap; + int ret = 0; + uint32_t *ch_mhz = NULL; + size_t ch_mhz_len; + uint8_t chan_id; + bool restore_last_peer = false; + QDF_STATUS qdf_status; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue cmd", __func__); + ret = -EINVAL; + goto end_tdls_peer_state; + } + + if (!soc) { + WMA_LOGE("%s: SOC context is NULL", __func__); + ret = -EINVAL; + goto end_tdls_peer_state; + } + + if (wma_is_roam_synch_in_progress(wma_handle, + peer_state->vdev_id)) { + WMA_LOGE("%s: roaming in progress, reject peer update cmd!", + __func__); + ret = -EPERM; + goto end_tdls_peer_state; + } + + + if (!wma_objmgr_peer_exist(wma_handle, + peer_state->peer_macaddr, NULL)) { + wma_err("peer:" QDF_MAC_ADDR_FMT "doesn't exist", + QDF_MAC_ADDR_REF(peer_state->peer_macaddr)); + ret = -EINVAL; + goto end_tdls_peer_state; + } + + peer_cap = &peer_state->peer_cap; + + /* peer capability info is valid only when peer state is connected */ + if (TDLS_PEER_STATE_CONNECTED != peer_state->peer_state) + qdf_mem_zero(peer_cap, sizeof(*peer_cap)); + + if (peer_cap->peer_chanlen) { + ch_mhz_len = sizeof(*ch_mhz) * peer_cap->peer_chanlen; + ch_mhz = qdf_mem_malloc(ch_mhz_len); + if (!ch_mhz) { + ret = -ENOMEM; + goto end_tdls_peer_state; + } + + for (i = 0; i < peer_cap->peer_chanlen; ++i) { + chan_id = peer_cap->peer_chan[i].chan_id; + ch_mhz[i] = cds_chan_to_freq(chan_id); + } + } + + cdp_peer_set_tdls_offchan_enabled(soc, peer_state->vdev_id, + peer_state->peer_macaddr, + !!peer_cap->peer_off_chan_support); + + if (wmi_unified_update_tdls_peer_state_cmd(wma_handle->wmi_handle, + peer_state, + ch_mhz)) { + WMA_LOGE("%s: failed to send tdls peer update state command", + __func__); + ret = -EIO; + /* Fall through to delete TDLS peer for teardown */ + } + + /* in case of teardown, remove peer from fw */ + if (TDLS_PEER_STATE_TEARDOWN == peer_state->peer_state) { + restore_last_peer = cdp_peer_is_vdev_restore_last_peer( + soc, + peer_state->vdev_id, + peer_state->peer_macaddr); + + wma_debug("calling wma_remove_peer for peer " QDF_MAC_ADDR_FMT + " vdevId: %d", + QDF_MAC_ADDR_REF(peer_state->peer_macaddr), + peer_state->vdev_id); + qdf_status = wma_remove_peer(wma_handle, + peer_state->peer_macaddr, + peer_state->vdev_id, false); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE(FL("wma_remove_peer failed")); + ret = -EINVAL; + } + cdp_peer_update_last_real_peer(soc, WMI_PDEV_ID_SOC, + peer_state->vdev_id, + restore_last_peer); + } + + if (TDLS_PEER_STATE_CONNECTED == peer_state->peer_state) { + cdp_peer_state_update(soc, peer_state->peer_macaddr, + OL_TXRX_PEER_STATE_AUTH); + } + +end_tdls_peer_state: + if (ch_mhz) + qdf_mem_free(ch_mhz); + if (peer_state) + qdf_mem_free(peer_state); + return ret; +} +#endif /* FEATURE_WLAN_TDLS */ + +/* + * wma_process_cfg_action_frm_tb_ppdu() - action frame TB PPDU cfg to firmware + * @wma: Pointer to WMA handle + * @cfg_info: Pointer for cfg info + * + * Return: QDF_STATUS_SUCCESS for success otherwise failure + * + */ +QDF_STATUS wma_process_cfg_action_frm_tb_ppdu(tp_wma_handle wma, + struct cfg_action_frm_tb_ppdu *cfg_info) +{ + struct cfg_action_frm_tb_ppdu_param cmd = {0}; + + if (!wma) { + WMA_LOGE(FL("WMA pointer is NULL")); + return QDF_STATUS_E_FAILURE; + } + + cmd.frm_len = cfg_info->frm_len; + cmd.cfg = cfg_info->cfg; + cmd.data = cfg_info->data; + + WMA_LOGD(FL("cfg: %d, frm_len: %d"), + cfg_info->cfg, cfg_info->frm_len); + + return wmi_unified_cfg_action_frm_tb_ppdu_cmd(wma->wmi_handle, &cmd); +} + + +/* + * wma_process_set_ie_info() - Function to send IE info to firmware + * @wma: Pointer to WMA handle + * @ie_data: Pointer for ie data + * + * This function sends IE information to firmware + * + * Return: QDF_STATUS_SUCCESS for success otherwise failure + * + */ +QDF_STATUS wma_process_set_ie_info(tp_wma_handle wma, + struct vdev_ie_info *ie_info) +{ + struct wma_txrx_node *interface; + struct vdev_ie_info_param cmd = {0}; + + if (!ie_info || !wma) { + WMA_LOGE(FL("input pointer is NULL")); + return QDF_STATUS_E_FAILURE; + } + + /* Validate the input */ + if (ie_info->length <= 0) { + WMA_LOGE(FL("Invalid IE length")); + return QDF_STATUS_E_INVAL; + } + + if (!wma_is_vdev_valid(ie_info->vdev_id)) { + WMA_LOGE(FL("vdev_id: %d is not active"), ie_info->vdev_id); + return QDF_STATUS_E_INVAL; + } + + interface = &wma->interfaces[ie_info->vdev_id]; + cmd.vdev_id = ie_info->vdev_id; + cmd.ie_id = ie_info->ie_id; + cmd.length = ie_info->length; + cmd.band = ie_info->band; + cmd.data = ie_info->data; + cmd.ie_source = WMA_SET_VDEV_IE_SOURCE_HOST; + + WMA_LOGD(FL("vdev id: %d, ie_id: %d, band: %d, len: %d"), + ie_info->vdev_id, ie_info->ie_id, ie_info->band, + ie_info->length); + + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, + ie_info->data, ie_info->length); + + return wmi_unified_process_set_ie_info_cmd(wma->wmi_handle, &cmd); +} + +#ifdef FEATURE_WLAN_APF +/** + * wma_get_apf_caps_event_handler() - Event handler for get apf capability + * @handle: WMA global handle + * @cmd_param_info: command event data + * @len: Length of @cmd_param_info + * + * Return: 0 on Success or Errno on failure + */ +int wma_get_apf_caps_event_handler(void *handle, u_int8_t *cmd_param_info, + u_int32_t len) +{ + WMI_BPF_CAPABILIY_INFO_EVENTID_param_tlvs *param_buf; + wmi_bpf_capability_info_evt_fixed_param *event; + struct sir_apf_get_offload *apf_get_offload; + struct mac_context *pmac = (struct mac_context *)cds_get_context( + QDF_MODULE_ID_PE); + + if (!pmac) { + WMA_LOGE("%s: Invalid pmac", __func__); + return -EINVAL; + } + if (!pmac->sme.apf_get_offload_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + + param_buf = (WMI_BPF_CAPABILIY_INFO_EVENTID_param_tlvs *)cmd_param_info; + event = param_buf->fixed_param; + apf_get_offload = qdf_mem_malloc(sizeof(*apf_get_offload)); + if (!apf_get_offload) + return -ENOMEM; + + apf_get_offload->apf_version = event->bpf_version; + apf_get_offload->max_apf_filters = event->max_bpf_filters; + apf_get_offload->max_bytes_for_apf_inst = + event->max_bytes_for_bpf_inst; + WMA_LOGD("%s: APF capabilities version: %d max apf filter size: %d", + __func__, apf_get_offload->apf_version, + apf_get_offload->max_bytes_for_apf_inst); + + WMA_LOGD("%s: sending apf capabilities event to hdd", __func__); + pmac->sme.apf_get_offload_cb(pmac->sme.apf_get_offload_context, + apf_get_offload); + qdf_mem_free(apf_get_offload); + return 0; +} + +QDF_STATUS wma_get_apf_capabilities(tp_wma_handle wma) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + wmi_bpf_get_capability_cmd_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint32_t len; + u_int8_t *buf_ptr; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE(FL("WMA is closed, can not issue get APF capab")); + return QDF_STATUS_E_INVAL; + } + + if (!wmi_service_enabled(wma->wmi_handle, wmi_service_apf_offload)) { + WMA_LOGE(FL("APF cababilities feature bit not enabled")); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(*cmd); + wmi_buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!wmi_buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (u_int8_t *) wmi_buf_data(wmi_buf); + cmd = (wmi_bpf_get_capability_cmd_fixed_param *) buf_ptr; + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_bpf_get_capability_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_bpf_get_capability_cmd_fixed_param)); + + if (wmi_unified_cmd_send(wma->wmi_handle, wmi_buf, len, + WMI_BPF_GET_CAPABILITY_CMDID)) { + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + return status; +} + +QDF_STATUS wma_set_apf_instructions(tp_wma_handle wma, + struct sir_apf_set_offload *apf_set_offload) +{ + wmi_bpf_set_vdev_instructions_cmd_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint32_t len = 0, len_aligned = 0; + u_int8_t *buf_ptr; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue set APF capability", + __func__); + return QDF_STATUS_E_INVAL; + } + + if (!wmi_service_enabled(wma->wmi_handle, + wmi_service_apf_offload)) { + WMA_LOGE(FL("APF offload feature Disabled")); + return QDF_STATUS_E_NOSUPPORT; + } + + if (!apf_set_offload) { + WMA_LOGE("%s: Invalid APF instruction request", __func__); + return QDF_STATUS_E_INVAL; + } + + if (apf_set_offload->session_id >= wma->max_bssid) { + WMA_LOGE(FL("Invalid vdev_id: %d"), + apf_set_offload->session_id); + return QDF_STATUS_E_INVAL; + } + + if (!wma_is_vdev_up(apf_set_offload->session_id)) { + WMA_LOGE("vdev %d is not up skipping APF offload", + apf_set_offload->session_id); + return QDF_STATUS_E_INVAL; + } + + if (apf_set_offload->total_length) { + len_aligned = roundup(apf_set_offload->current_length, + sizeof(A_UINT32)); + len = len_aligned + WMI_TLV_HDR_SIZE; + } + + len += sizeof(*cmd); + wmi_buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!wmi_buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (u_int8_t *) wmi_buf_data(wmi_buf); + cmd = (wmi_bpf_set_vdev_instructions_cmd_fixed_param *) buf_ptr; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_bpf_set_vdev_instructions_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_bpf_set_vdev_instructions_cmd_fixed_param)); + cmd->vdev_id = apf_set_offload->session_id; + cmd->filter_id = apf_set_offload->filter_id; + cmd->total_length = apf_set_offload->total_length; + cmd->current_offset = apf_set_offload->current_offset; + cmd->current_length = apf_set_offload->current_length; + + if (apf_set_offload->total_length) { + buf_ptr += + sizeof(wmi_bpf_set_vdev_instructions_cmd_fixed_param); + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, len_aligned); + buf_ptr += WMI_TLV_HDR_SIZE; + qdf_mem_copy(buf_ptr, apf_set_offload->program, + apf_set_offload->current_length); + } + + if (wmi_unified_cmd_send(wma->wmi_handle, wmi_buf, len, + WMI_BPF_SET_VDEV_INSTRUCTIONS_CMDID)) { + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + WMA_LOGD(FL("APF offload enabled in fw")); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_send_apf_enable_cmd(WMA_HANDLE handle, uint8_t vdev_id, + bool apf_enable) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tp_wma_handle wma = (tp_wma_handle) handle; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE(FL("WMA is closed, can not issue get APF capab")); + return QDF_STATUS_E_INVAL; + } + + if (!wma_is_vdev_valid(vdev_id)) + return QDF_STATUS_E_INVAL; + + if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap, + WMI_SERVICE_BPF_OFFLOAD)) { + WMA_LOGE(FL("APF cababilities feature bit not enabled")); + return QDF_STATUS_E_FAILURE; + } + + status = wmi_unified_send_apf_enable_cmd(wma->wmi_handle, vdev_id, + apf_enable); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to send apf enable/disable cmd"); + return QDF_STATUS_E_FAILURE; + } + + if (apf_enable) + WMA_LOGD("Sent APF Enable on vdevid: %d", vdev_id); + else + WMA_LOGD("Sent APF Disable on vdevid: %d", vdev_id); + + return status; +} + +QDF_STATUS +wma_send_apf_write_work_memory_cmd(WMA_HANDLE handle, + struct wmi_apf_write_memory_params + *write_params) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tp_wma_handle wma = (tp_wma_handle) handle; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE(FL("WMA is closed, can not issue write APF mem")); + return QDF_STATUS_E_INVAL; + } + + if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap, + WMI_SERVICE_BPF_OFFLOAD)) { + WMA_LOGE(FL("APF cababilities feature bit not enabled")); + return QDF_STATUS_E_FAILURE; + } + + if (wmi_unified_send_apf_write_work_memory_cmd(wma->wmi_handle, + write_params)) { + WMA_LOGE(FL("Failed to send APF write mem command")); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("Sent APF wite mem on vdevid: %d", write_params->vdev_id); + return status; +} + +int wma_apf_read_work_memory_event_handler(void *handle, uint8_t *evt_buf, + uint32_t len) +{ + tp_wma_handle wma_handle; + wmi_unified_t wmi_handle; + struct wmi_apf_read_memory_resp_event_params evt_params = {0}; + QDF_STATUS status; + struct mac_context *pmac = cds_get_context(QDF_MODULE_ID_PE); + + WMA_LOGD(FL("handle:%pK event:%pK len:%u"), handle, evt_buf, len); + + wma_handle = handle; + if (!wma_handle) { + WMA_LOGE(FL("NULL wma_handle")); + return -EINVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE(FL("NULL wmi_handle")); + return -EINVAL; + } + + if (!pmac) { + WMA_LOGE(FL("Invalid pmac")); + return -EINVAL; + } + + if (!pmac->sme.apf_read_mem_cb) { + WMA_LOGE(FL("Callback not registered")); + return -EINVAL; + } + + status = wmi_extract_apf_read_memory_resp_event(wmi_handle, + evt_buf, &evt_params); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE(FL("Event extract failure: %d"), status); + return -EINVAL; + } + + pmac->sme.apf_read_mem_cb(pmac->hdd_handle, &evt_params); + + return 0; +} + +QDF_STATUS wma_send_apf_read_work_memory_cmd(WMA_HANDLE handle, + struct wmi_apf_read_memory_params + *read_params) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tp_wma_handle wma = (tp_wma_handle) handle; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE(FL("WMA is closed, can not issue read APF memory")); + return QDF_STATUS_E_INVAL; + } + + if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap, + WMI_SERVICE_BPF_OFFLOAD)) { + WMA_LOGE(FL("APF cababilities feature bit not enabled")); + return QDF_STATUS_E_FAILURE; + } + + if (wmi_unified_send_apf_read_work_memory_cmd(wma->wmi_handle, + read_params)) { + WMA_LOGE(FL("Failed to send APF read memory command")); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("Sent APF read memory on vdevid: %d", read_params->vdev_id); + return status; +} +#endif /* FEATURE_WLAN_APF */ + +QDF_STATUS wma_set_tx_rx_aggr_size(uint8_t vdev_id, + uint32_t tx_size, + uint32_t rx_size, + wmi_vdev_custom_aggr_type_t aggr_type) +{ + tp_wma_handle wma_handle; + wmi_vdev_set_custom_aggr_size_cmd_fixed_param *cmd; + int32_t len; + wmi_buf_t buf; + u_int8_t *buf_ptr; + int ret; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + + if (!wma_handle) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return QDF_STATUS_E_INVAL; + } + + len = sizeof(*cmd); + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (u_int8_t *) wmi_buf_data(buf); + cmd = (wmi_vdev_set_custom_aggr_size_cmd_fixed_param *) buf_ptr; + qdf_mem_zero(cmd, sizeof(*cmd)); + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_vdev_set_custom_aggr_size_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_vdev_set_custom_aggr_size_cmd_fixed_param)); + + cmd->vdev_id = vdev_id; + cmd->tx_aggr_size = tx_size; + cmd->rx_aggr_size = rx_size; + /* bit 2 (aggr_type): TX Aggregation Type (0=A-MPDU, 1=A-MSDU) */ + if (aggr_type == WMI_VDEV_CUSTOM_AGGR_TYPE_AMSDU) + cmd->enable_bitmap |= 0x04; + + WMA_LOGD("tx aggr: %d rx aggr: %d vdev: %d enable_bitmap %d", + cmd->tx_aggr_size, cmd->rx_aggr_size, cmd->vdev_id, + cmd->enable_bitmap); + + ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID); + if (ret) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_set_tx_rx_aggr_size_per_ac(WMA_HANDLE handle, + uint8_t vdev_id, + struct wlan_mlme_qos *qos_aggr, + wmi_vdev_custom_aggr_type_t aggr_type) +{ + wmi_vdev_set_custom_aggr_size_cmd_fixed_param *cmd; + int32_t len; + wmi_buf_t buf; + u_int8_t *buf_ptr; + int ret; + int queue_num; + uint32_t tx_aggr_size[4]; + tp_wma_handle wma_handle = (tp_wma_handle)handle; + + if (!wma_handle) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return QDF_STATUS_E_INVAL; + } + + tx_aggr_size[0] = qos_aggr->tx_aggregation_size_be; + tx_aggr_size[1] = qos_aggr->tx_aggregation_size_bk; + tx_aggr_size[2] = qos_aggr->tx_aggregation_size_vi; + tx_aggr_size[3] = qos_aggr->tx_aggregation_size_vo; + + for (queue_num = 0; queue_num < 4; queue_num++) { + if (tx_aggr_size[queue_num] == 0) + continue; + + len = sizeof(*cmd); + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (u_int8_t *)wmi_buf_data(buf); + cmd = (wmi_vdev_set_custom_aggr_size_cmd_fixed_param *)buf_ptr; + qdf_mem_zero(cmd, sizeof(*cmd)); + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_vdev_set_custom_aggr_size_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_vdev_set_custom_aggr_size_cmd_fixed_param)); + + cmd->vdev_id = vdev_id; + cmd->rx_aggr_size = qos_aggr->rx_aggregation_size; + cmd->tx_aggr_size = tx_aggr_size[queue_num]; + /* bit 5: tx_ac_enable, if set, ac bitmap is valid. */ + cmd->enable_bitmap = 0x20 | queue_num; + /* bit 2 (aggr_type): TX Aggregation Type (0=A-MPDU, 1=A-MSDU) */ + if (aggr_type == WMI_VDEV_CUSTOM_AGGR_TYPE_AMSDU) + cmd->enable_bitmap |= 0x04; + + WMA_LOGD("queue_num: %d, tx aggr: %d rx aggr: %d vdev: %d, bitmap: %d", + queue_num, cmd->tx_aggr_size, + cmd->rx_aggr_size, cmd->vdev_id, + cmd->enable_bitmap); + + ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID); + if (ret) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS wma_set_sw_retry_by_qos( + tp_wma_handle handle, uint8_t vdev_id, + wmi_vdev_custom_sw_retry_type_t retry_type, + wmi_traffic_ac ac_type, + uint32_t sw_retry) +{ + wmi_vdev_set_custom_sw_retry_th_cmd_fixed_param *cmd; + int32_t len; + wmi_buf_t buf; + u_int8_t *buf_ptr; + int ret; + + len = sizeof(*cmd); + buf = wmi_buf_alloc(handle->wmi_handle, len); + + if (!buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (u_int8_t *)wmi_buf_data(buf); + cmd = (wmi_vdev_set_custom_sw_retry_th_cmd_fixed_param *)buf_ptr; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_vdev_set_custom_sw_retry_th_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_vdev_set_custom_sw_retry_th_cmd_fixed_param)); + + cmd->vdev_id = vdev_id; + cmd->ac_type = ac_type; + cmd->sw_retry_type = retry_type; + cmd->sw_retry_th = sw_retry; + + wma_debug("ac_type: %d re_type: %d threshold: %d vid: %d", + cmd->ac_type, cmd->sw_retry_type, + cmd->sw_retry_th, cmd->vdev_id); + + ret = wmi_unified_cmd_send(handle->wmi_handle, + buf, len, + WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID); + + if (ret) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_set_sw_retry_threshold_per_ac(WMA_HANDLE handle, + uint8_t vdev_id, + struct wlan_mlme_qos *qos_aggr) +{ + QDF_STATUS ret; + int retry_type, queue_num; + uint32_t tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_MAX][WMI_AC_MAX]; + uint32_t sw_retry; + tp_wma_handle wma_handle = (tp_wma_handle)handle; + + if (!wma_handle) { + wma_err("%s: WMA context is invalid!", __func__); + return QDF_STATUS_E_INVAL; + } + + tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_AGGR][WMI_AC_BE] = + qos_aggr->tx_aggr_sw_retry_threshold_be; + tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_AGGR][WMI_AC_BK] = + qos_aggr->tx_aggr_sw_retry_threshold_bk; + tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_AGGR][WMI_AC_VI] = + qos_aggr->tx_aggr_sw_retry_threshold_vi; + tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_AGGR][WMI_AC_VO] = + qos_aggr->tx_aggr_sw_retry_threshold_vo; + + tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR][WMI_AC_BE] = + qos_aggr->tx_non_aggr_sw_retry_threshold_be; + tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR][WMI_AC_BK] = + qos_aggr->tx_non_aggr_sw_retry_threshold_bk; + tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR][WMI_AC_VI] = + qos_aggr->tx_non_aggr_sw_retry_threshold_vi; + tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR][WMI_AC_VO] = + qos_aggr->tx_non_aggr_sw_retry_threshold_vo; + + retry_type = WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR; + while (retry_type < WMI_VDEV_CUSTOM_SW_RETRY_TYPE_MAX) { + for (queue_num = 0; queue_num < WMI_AC_MAX; queue_num++) { + if (tx_sw_retry[retry_type][queue_num] == 0) + continue; + + sw_retry = tx_sw_retry[retry_type][queue_num]; + ret = wma_set_sw_retry_by_qos(wma_handle, + vdev_id, + retry_type, + queue_num, + sw_retry); + + if (QDF_IS_STATUS_ERROR(ret)) + return ret; + } + retry_type++; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_set_sw_retry_threshold(uint8_t vdev_id, uint32_t retry, + uint32_t param_id) +{ + uint32_t max, min; + uint32_t ret; + + if (param_id == WMI_PDEV_PARAM_AGG_SW_RETRY_TH) { + max = cfg_max(CFG_TX_AGGR_SW_RETRY); + min = cfg_min(CFG_TX_AGGR_SW_RETRY); + } else { + max = cfg_max(CFG_TX_NON_AGGR_SW_RETRY); + min = cfg_min(CFG_TX_NON_AGGR_SW_RETRY); + } + + retry = (retry > max) ? max : retry; + retry = (retry < min) ? min : retry; + + ret = wma_cli_set_command(vdev_id, param_id, retry, PDEV_CMD); + if (ret) + return QDF_STATUS_E_IO; + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_process_fw_test_cmd() - send unit test command to fw. + * @handle: wma handle + * @wma_fwtest: fw test command + * + * This function send fw test command to fw. + * + * Return: none + */ +QDF_STATUS wma_process_fw_test_cmd(WMA_HANDLE handle, + struct set_fwtest_params *wma_fwtest) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue fw test cmd", + __func__); + return QDF_STATUS_E_FAILURE; + } + + if (wmi_unified_fw_test_cmd(wma_handle->wmi_handle, + (struct set_fwtest_params *)wma_fwtest)) { + WMA_LOGE("%s: Failed to issue fw test cmd", + __func__); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** + * wma_enable_disable_caevent_ind() - Issue WMI command to enable or + * disable ca event indication + * @wma: wma handler + * @val: boolean value true or false + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_enable_disable_caevent_ind(tp_wma_handle wma, uint8_t val) +{ + WMI_CHAN_AVOID_RPT_ALLOW_CMD_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint8_t *buf_ptr; + uint32_t len; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE(FL("WMA is closed, can not issue set/clear CA")); + return QDF_STATUS_E_INVAL; + } + + len = sizeof(*cmd); + wmi_buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!wmi_buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (uint8_t *) wmi_buf_data(wmi_buf); + cmd = (WMI_CHAN_AVOID_RPT_ALLOW_CMD_fixed_param *) buf_ptr; + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_WMI_CHAN_AVOID_RPT_ALLOW_CMD_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + WMI_CHAN_AVOID_RPT_ALLOW_CMD_fixed_param)); + cmd->rpt_allow = val; + if (wmi_unified_cmd_send(wma->wmi_handle, wmi_buf, len, + WMI_CHAN_AVOID_RPT_ALLOW_CMDID)) { + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +static wma_sar_cb sar_callback; +static void *sar_context; + +static int wma_sar_event_handler(void *handle, uint8_t *evt_buf, uint32_t len) +{ + tp_wma_handle wma_handle; + wmi_unified_t wmi_handle; + struct sar_limit_event *event; + wma_sar_cb callback; + QDF_STATUS status; + + WMA_LOGI(FL("handle:%pK event:%pK len:%u"), handle, evt_buf, len); + + wma_handle = handle; + if (!wma_handle) { + WMA_LOGE(FL("NULL wma_handle")); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE(FL("NULL wmi_handle")); + return QDF_STATUS_E_INVAL; + } + + event = qdf_mem_malloc(sizeof(*event)); + if (!event) + return QDF_STATUS_E_NOMEM; + + status = wmi_unified_extract_sar_limit_event(wmi_handle, + evt_buf, event); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE(FL("Event extract failure: %d"), status); + qdf_mem_free(event); + return QDF_STATUS_E_INVAL; + } + + callback = sar_callback; + sar_callback = NULL; + if (callback) + callback(sar_context, event); + + qdf_mem_free(event); + + return 0; +} + +QDF_STATUS wma_sar_register_event_handlers(WMA_HANDLE handle) +{ + tp_wma_handle wma_handle = handle; + wmi_unified_t wmi_handle; + + if (!wma_handle) { + WMA_LOGE(FL("NULL wma_handle")); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE(FL("NULL wmi_handle")); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_register_event_handler(wmi_handle, + wmi_sar_get_limits_event_id, + wma_sar_event_handler, + WMA_RX_WORK_CTX); +} + +QDF_STATUS wma_get_sar_limit(WMA_HANDLE handle, + wma_sar_cb callback, void *context) +{ + tp_wma_handle wma_handle = handle; + wmi_unified_t wmi_handle; + QDF_STATUS status; + + if (!wma_handle) { + WMA_LOGE(FL("NULL wma_handle")); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE(FL("NULL wmi_handle")); + return QDF_STATUS_E_INVAL; + } + + sar_callback = callback; + sar_context = context; + status = wmi_unified_get_sar_limit_cmd(wmi_handle); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE(FL("wmi_unified_get_sar_limit_cmd() error: %u"), + status); + sar_callback = NULL; + } + + return status; +} + +QDF_STATUS wma_set_sar_limit(WMA_HANDLE handle, + struct sar_limit_cmd_params *sar_limit_params) +{ + int ret; + tp_wma_handle wma = (tp_wma_handle) handle; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue set sar limit msg", + __func__); + return QDF_STATUS_E_INVAL; + } + + if (!sar_limit_params) { + WMA_LOGE("%s: set sar limit ptr NULL", + __func__); + return QDF_STATUS_E_INVAL; + } + + ret = wmi_unified_send_sar_limit_cmd(wma->wmi_handle, + sar_limit_params); + + return ret; +} + +QDF_STATUS wma_send_coex_config_cmd(WMA_HANDLE wma_handle, + struct coex_config_params *coex_cfg_params) +{ + tp_wma_handle wma = (tp_wma_handle)wma_handle; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue coex config command", + __func__); + return QDF_STATUS_E_INVAL; + } + + if (!coex_cfg_params) { + WMA_LOGE("%s: coex cfg params ptr NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_send_coex_config_cmd(wma->wmi_handle, + coex_cfg_params); +} + +/** + * wma_get_arp_stats_handler() - handle arp stats data + * indicated by FW + * @handle: wma context + * @data: event buffer + * @data len: length of event buffer + * + * Return: 0 on success + */ +int wma_get_arp_stats_handler(void *handle, uint8_t *data, + uint32_t data_len) +{ + WMI_VDEV_GET_ARP_STAT_EVENTID_param_tlvs *param_buf; + wmi_vdev_get_arp_stats_event_fixed_param *data_event; + wmi_vdev_get_connectivity_check_stats *connect_stats_event; + uint8_t *buf_ptr; + struct rsp_stats rsp = {0}; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: Invalid mac context", __func__); + return -EINVAL; + } + + if (!mac->sme.get_arp_stats_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + + if (!data) { + WMA_LOGE("%s: invalid pointer", __func__); + return -EINVAL; + } + param_buf = (WMI_VDEV_GET_ARP_STAT_EVENTID_param_tlvs *)data; + if (!param_buf) { + WMA_LOGE("%s: Invalid get arp stats event", __func__); + return -EINVAL; + } + data_event = param_buf->fixed_param; + if (!data_event) { + WMA_LOGE("%s: Invalid get arp stats data event", __func__); + return -EINVAL; + } + rsp.arp_req_enqueue = data_event->arp_req_enqueue; + rsp.vdev_id = data_event->vdev_id; + rsp.arp_req_tx_success = data_event->arp_req_tx_success; + rsp.arp_req_tx_failure = data_event->arp_req_tx_failure; + rsp.arp_rsp_recvd = data_event->arp_rsp_recvd; + rsp.out_of_order_arp_rsp_drop_cnt = + data_event->out_of_order_arp_rsp_drop_cnt; + rsp.dad_detected = data_event->dad_detected; + rsp.connect_status = data_event->connect_status; + rsp.ba_session_establishment_status = + data_event->ba_session_establishment_status; + + buf_ptr = (uint8_t *)data_event; + buf_ptr = buf_ptr + sizeof(wmi_vdev_get_arp_stats_event_fixed_param) + + WMI_TLV_HDR_SIZE; + connect_stats_event = (wmi_vdev_get_connectivity_check_stats *)buf_ptr; + + if (((connect_stats_event->tlv_header & 0xFFFF0000) >> 16 == + WMITLV_TAG_STRUC_wmi_vdev_get_connectivity_check_stats)) { + rsp.connect_stats_present = true; + rsp.tcp_ack_recvd = connect_stats_event->tcp_ack_recvd; + rsp.icmpv4_rsp_recvd = connect_stats_event->icmpv4_rsp_recvd; + WMA_LOGD("tcp_ack_recvd %d icmpv4_rsp_recvd %d", + connect_stats_event->tcp_ack_recvd, + connect_stats_event->icmpv4_rsp_recvd); + } + + mac->sme.get_arp_stats_cb(mac->hdd_handle, &rsp, + mac->sme.get_arp_stats_context); + + return 0; +} + +/** + * wma_unified_power_debug_stats_event_handler() - WMA handler function to + * handle Power stats event from firmware + * @handle: Pointer to wma handle + * @cmd_param_info: Pointer to Power stats event TLV + * @len: Length of the cmd_param_info + * + * Return: 0 on success, error number otherwise + */ + #ifdef WLAN_POWER_DEBUG +int wma_unified_power_debug_stats_event_handler(void *handle, + uint8_t *cmd_param_info, uint32_t len) +{ + WMI_PDEV_CHIP_POWER_STATS_EVENTID_param_tlvs *param_tlvs; + struct power_stats_response *power_stats_results; + wmi_pdev_chip_power_stats_event_fixed_param *param_buf; + uint32_t power_stats_len, stats_registers_len, *debug_registers; + + struct mac_context *mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + + param_tlvs = + (WMI_PDEV_CHIP_POWER_STATS_EVENTID_param_tlvs *) cmd_param_info; + + param_buf = (wmi_pdev_chip_power_stats_event_fixed_param *) + param_tlvs->fixed_param; + if (!mac) { + wma_debug("NULL mac ptr"); + return -EINVAL; + } + + if (!param_buf) { + WMA_LOGD("%s: NULL power stats event fixed param", __func__); + return -EINVAL; + } + + if (param_buf->num_debug_register > ((WMI_SVC_MSG_MAX_SIZE - + sizeof(wmi_pdev_chip_power_stats_event_fixed_param)) / + sizeof(uint32_t)) || + param_buf->num_debug_register > param_tlvs->num_debug_registers) { + WMA_LOGE("excess payload: LEN num_debug_register:%u", + param_buf->num_debug_register); + return -EINVAL; + } + debug_registers = param_tlvs->debug_registers; + stats_registers_len = + (sizeof(uint32_t) * param_buf->num_debug_register); + power_stats_len = stats_registers_len + sizeof(*power_stats_results); + power_stats_results = qdf_mem_malloc(power_stats_len); + if (!power_stats_results) + return -ENOMEM; + + WMA_LOGD("Cumulative sleep time %d cumulative total on time %d deep sleep enter counter %d last deep sleep enter tstamp ts %d debug registers fmt %d num debug register %d", + param_buf->cumulative_sleep_time_ms, + param_buf->cumulative_total_on_time_ms, + param_buf->deep_sleep_enter_counter, + param_buf->last_deep_sleep_enter_tstamp_ms, + param_buf->debug_register_fmt, + param_buf->num_debug_register); + + power_stats_results->cumulative_sleep_time_ms + = param_buf->cumulative_sleep_time_ms; + power_stats_results->cumulative_total_on_time_ms + = param_buf->cumulative_total_on_time_ms; + power_stats_results->deep_sleep_enter_counter + = param_buf->deep_sleep_enter_counter; + power_stats_results->last_deep_sleep_enter_tstamp_ms + = param_buf->last_deep_sleep_enter_tstamp_ms; + power_stats_results->debug_register_fmt + = param_buf->debug_register_fmt; + power_stats_results->num_debug_register + = param_buf->num_debug_register; + + power_stats_results->debug_registers + = (uint32_t *)(power_stats_results + 1); + + qdf_mem_copy(power_stats_results->debug_registers, + debug_registers, stats_registers_len); + + if (mac->sme.sme_power_debug_stats_callback) + mac->sme.sme_power_debug_stats_callback(mac, + power_stats_results); + + qdf_mem_free(power_stats_results); + return 0; +} +#else +int wma_unified_power_debug_stats_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + return 0; +} +#endif + +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS +int wma_unified_beacon_debug_stats_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_VDEV_BCN_RECEPTION_STATS_EVENTID_param_tlvs *param_tlvs; + struct bcn_reception_stats_rsp *bcn_reception_stats; + wmi_vdev_bcn_recv_stats_fixed_param *param_buf; + struct mac_context *mac = + (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + + param_tlvs = + (WMI_VDEV_BCN_RECEPTION_STATS_EVENTID_param_tlvs *)cmd_param_info; + if (!param_tlvs) { + WMA_LOGA("%s: Invalid stats event", __func__); + return -EINVAL; + } + + param_buf = (wmi_vdev_bcn_recv_stats_fixed_param *) + param_tlvs->fixed_param; + if (!param_buf || !mac || !mac->sme.beacon_stats_resp_callback) { + WMA_LOGD("%s: NULL mac ptr or HDD callback is null", __func__); + return -EINVAL; + } + + if (!param_buf) { + WMA_LOGD("%s: NULL beacon stats event fixed param", __func__); + return -EINVAL; + } + + bcn_reception_stats = qdf_mem_malloc(sizeof(*bcn_reception_stats)); + if (!bcn_reception_stats) + return -ENOMEM; + + bcn_reception_stats->total_bcn_cnt = param_buf->total_bcn_cnt; + bcn_reception_stats->total_bmiss_cnt = param_buf->total_bmiss_cnt; + bcn_reception_stats->vdev_id = param_buf->vdev_id; + + WMA_LOGD("Total beacon count %d total beacon miss count %d vdev_id %d", + param_buf->total_bcn_cnt, + param_buf->total_bmiss_cnt, + param_buf->vdev_id); + + qdf_mem_copy(bcn_reception_stats->bmiss_bitmap, + param_buf->bmiss_bitmap, + MAX_BCNMISS_BITMAP * sizeof(uint32_t)); + + mac->sme.beacon_stats_resp_callback(bcn_reception_stats, + mac->sme.beacon_stats_context); + qdf_mem_free(bcn_reception_stats); + return 0; +} +#else +int wma_unified_beacon_debug_stats_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + return 0; +} +#endif + +int wma_chan_info_event_handler(void *handle, uint8_t *event_buf, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle)handle; + WMI_CHAN_INFO_EVENTID_param_tlvs *param_buf; + wmi_chan_info_event_fixed_param *event; + struct scan_chan_info buf; + struct mac_context *mac = NULL; + struct lim_channel_status *channel_status; + bool snr_monitor_enabled; + struct wlan_objmgr_vdev *vdev; + enum QDF_OPMODE mode; + + if (wma && wma->cds_context) + mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: Invalid mac context", __func__); + return -EINVAL; + } + + param_buf = (WMI_CHAN_INFO_EVENTID_param_tlvs *)event_buf; + if (!param_buf) { + WMA_LOGE("Invalid chan info event buffer"); + return -EINVAL; + } + event = param_buf->fixed_param; + if (!event) { + WMA_LOGA("%s: Invalid fixed param", __func__); + return -EINVAL; + } + + snr_monitor_enabled = wlan_scan_is_snr_monitor_enabled(mac->psoc); + if (snr_monitor_enabled && mac->chan_info_cb) { + buf.tx_frame_count = event->tx_frame_cnt; + buf.clock_freq = event->mac_clk_mhz; + buf.cmd_flag = event->cmd_flags; + buf.freq = event->freq; + buf.noise_floor = event->noise_floor; + buf.cycle_count = event->cycle_count; + buf.rx_clear_count = event->rx_clear_count; + mac->chan_info_cb(&buf); + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, event->vdev_id, + WLAN_LEGACY_WMA_ID); + + if (!vdev) { + wma_err("vdev not found for vdev %d", event->vdev_id); + return -EINVAL; + } + mode = wlan_vdev_mlme_get_opmode(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); + + if (mac->sap.acs_with_more_param && mode == QDF_SAP_MODE) { + channel_status = qdf_mem_malloc(sizeof(*channel_status)); + if (!channel_status) + return -ENOMEM; + + wma_debug("freq %d nf %d rxcnt %u cyccnt %u tx_r %d tx_t %d", + event->freq, event->noise_floor, + event->rx_clear_count, event->cycle_count, + event->chan_tx_pwr_range, event->chan_tx_pwr_tp); + + channel_status->channelfreq = event->freq; + channel_status->noise_floor = event->noise_floor; + channel_status->rx_clear_count = + event->rx_clear_count; + channel_status->cycle_count = event->cycle_count; + channel_status->chan_tx_pwr_range = + event->chan_tx_pwr_range; + channel_status->chan_tx_pwr_throughput = + event->chan_tx_pwr_tp; + channel_status->rx_frame_count = + event->rx_frame_count; + channel_status->bss_rx_cycle_count = + event->my_bss_rx_cycle_count; + channel_status->rx_11b_mode_data_duration = + event->rx_11b_mode_data_duration; + channel_status->tx_frame_count = event->tx_frame_cnt; + channel_status->mac_clk_mhz = event->mac_clk_mhz; + channel_status->channel_id = + cds_freq_to_chan(event->freq); + channel_status->cmd_flags = + event->cmd_flags; + + wma_send_msg(handle, WMA_RX_CHN_STATUS_EVENT, + (void *)channel_status, 0); + } + + return 0; +} + +int wma_rx_aggr_failure_event_handler(void *handle, u_int8_t *event_buf, + u_int32_t len) +{ + WMI_REPORT_RX_AGGR_FAILURE_EVENTID_param_tlvs *param_buf; + struct sir_sme_rx_aggr_hole_ind *rx_aggr_hole_event; + wmi_rx_aggr_failure_event_fixed_param *rx_aggr_failure_info; + wmi_rx_aggr_failure_info *hole_info; + uint32_t i, alloc_len; + struct mac_context *mac; + + mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + if (!mac || !mac->sme.stats_ext2_cb) { + WMA_LOGD("%s: NULL mac ptr or HDD callback is null", __func__); + return -EINVAL; + } + + param_buf = (WMI_REPORT_RX_AGGR_FAILURE_EVENTID_param_tlvs *)event_buf; + if (!param_buf) { + WMA_LOGE("%s: Invalid stats ext event buf", __func__); + return -EINVAL; + } + + rx_aggr_failure_info = param_buf->fixed_param; + hole_info = param_buf->failure_info; + + if (rx_aggr_failure_info->num_failure_info > ((WMI_SVC_MSG_MAX_SIZE - + sizeof(*rx_aggr_hole_event)) / + sizeof(rx_aggr_hole_event->hole_info_array[0]))) { + WMA_LOGE("%s: Excess data from WMI num_failure_info %d", + __func__, rx_aggr_failure_info->num_failure_info); + return -EINVAL; + } + + alloc_len = sizeof(*rx_aggr_hole_event) + + (rx_aggr_failure_info->num_failure_info)* + sizeof(rx_aggr_hole_event->hole_info_array[0]); + rx_aggr_hole_event = qdf_mem_malloc(alloc_len); + if (!rx_aggr_hole_event) + return -ENOMEM; + + rx_aggr_hole_event->hole_cnt = rx_aggr_failure_info->num_failure_info; + if (rx_aggr_hole_event->hole_cnt > param_buf->num_failure_info) { + WMA_LOGE("Invalid no of hole count: %d", + rx_aggr_hole_event->hole_cnt); + qdf_mem_free(rx_aggr_hole_event); + return -EINVAL; + } + WMA_LOGD("aggr holes_sum: %d\n", + rx_aggr_failure_info->num_failure_info); + for (i = 0; i < rx_aggr_hole_event->hole_cnt; i++) { + rx_aggr_hole_event->hole_info_array[i] = + hole_info->end_seq - hole_info->start_seq + 1; + WMA_LOGD("aggr_index: %d\tstart_seq: %d\tend_seq: %d\t" + "hole_info: %d mpdu lost", + i, hole_info->start_seq, hole_info->end_seq, + rx_aggr_hole_event->hole_info_array[i]); + hole_info++; + } + + mac->sme.stats_ext2_cb(mac->hdd_handle, rx_aggr_hole_event); + qdf_mem_free(rx_aggr_hole_event); + + return 0; +} + +int wma_wlan_bt_activity_evt_handler(void *handle, uint8_t *event, uint32_t len) +{ + wmi_coex_bt_activity_event_fixed_param *fixed_param; + WMI_WLAN_COEX_BT_ACTIVITY_EVENTID_param_tlvs *param_buf = + (WMI_WLAN_COEX_BT_ACTIVITY_EVENTID_param_tlvs *)event; + struct scheduler_msg sme_msg = {0}; + QDF_STATUS qdf_status; + + if (!param_buf) { + WMA_LOGE(FL("Invalid BT activity event buffer")); + return -EINVAL; + } + + fixed_param = param_buf->fixed_param; + if (!fixed_param) { + WMA_LOGE(FL("Invalid BT activity event fixed param buffer")); + return -EINVAL; + } + + WMA_LOGI(FL("Received BT activity event %u"), + fixed_param->coex_profile_evt); + + sme_msg.type = eWNI_SME_BT_ACTIVITY_INFO_IND; + sme_msg.bodyptr = NULL; + sme_msg.bodyval = fixed_param->coex_profile_evt; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (QDF_IS_STATUS_ERROR(qdf_status)) + return -EINVAL; + + return 0; +} + +int wma_pdev_div_info_evt_handler(void *handle, u_int8_t *event_buf, + u_int32_t len) +{ + WMI_PDEV_DIV_RSSI_ANTID_EVENTID_param_tlvs *param_buf; + wmi_pdev_div_rssi_antid_event_fixed_param *event; + struct chain_rssi_result chain_rssi_result; + u_int32_t i; + u_int8_t macaddr[QDF_MAC_ADDR_SIZE]; + tp_wma_handle wma = (tp_wma_handle)handle; + + struct mac_context *pmac = (struct mac_context *)cds_get_context( + QDF_MODULE_ID_PE); + if (!pmac || !wma) { + WMA_LOGE(FL("Invalid pmac or wma")); + return -EINVAL; + } + + if (!pmac->sme.get_chain_rssi_cb) { + WMA_LOGE(FL("Invalid get_chain_rssi_cb")); + return -EINVAL; + } + param_buf = (WMI_PDEV_DIV_RSSI_ANTID_EVENTID_param_tlvs *) event_buf; + if (!param_buf) { + WMA_LOGE(FL("Invalid rssi antid event buffer")); + return -EINVAL; + } + + event = param_buf->fixed_param; + if (!event) { + WMA_LOGE(FL("Invalid fixed param")); + return -EINVAL; + } + + if (event->num_chains_valid > CHAIN_MAX_NUM) { + WMA_LOGE(FL("Invalid num of chains")); + return -EINVAL; + } + + qdf_mem_zero(&chain_rssi_result, sizeof(chain_rssi_result)); + + WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->macaddr, macaddr); + wma_debug("macaddr: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(macaddr)); + + WMA_LOGD(FL("num_chains_valid: %d"), event->num_chains_valid); + chain_rssi_result.num_chains_valid = event->num_chains_valid; + + qdf_mem_copy(chain_rssi_result.chain_rssi, event->chain_rssi, + sizeof(event->chain_rssi)); + + qdf_mem_copy(chain_rssi_result.chain_evm, event->chain_evm, + sizeof(event->chain_evm)); + + qdf_mem_copy(chain_rssi_result.ant_id, event->ant_id, + sizeof(event->ant_id)); + + for (i = 0; i < chain_rssi_result.num_chains_valid; i++) { + WMA_LOGD(FL("chain_rssi: %d, chain_evm: %d,ant_id: %d"), + chain_rssi_result.chain_rssi[i], + chain_rssi_result.chain_evm[i], + chain_rssi_result.ant_id[i]); + + if (!wmi_service_enabled(wma->wmi_handle, + wmi_service_hw_db2dbm_support)) { + if (chain_rssi_result.chain_rssi[i] != + WMA_INVALID_PER_CHAIN_SNR) + chain_rssi_result.chain_rssi[i] += + WMA_TGT_NOISE_FLOOR_DBM; + else + chain_rssi_result.chain_rssi[i] = + WMA_INVALID_PER_CHAIN_RSSI; + } + } + + pmac->sme.get_chain_rssi_cb(pmac->sme.get_chain_rssi_context, + &chain_rssi_result); + + return 0; +} + +int wma_vdev_obss_detection_info_handler(void *handle, uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + struct wmi_obss_detect_info *obss_detection; + QDF_STATUS status; + + if (!event) { + WMA_LOGE("Invalid obss_detection_info event buffer"); + return -EINVAL; + } + + obss_detection = qdf_mem_malloc(sizeof(*obss_detection)); + if (!obss_detection) + return -ENOMEM; + + status = wmi_unified_extract_obss_detection_info(wma->wmi_handle, + event, obss_detection); + + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to extract obss info", __func__); + qdf_mem_free(obss_detection); + return -EINVAL; + } + + if (!wma_is_vdev_valid(obss_detection->vdev_id)) { + WMA_LOGE("%s: Invalid vdev id %d", __func__, + obss_detection->vdev_id); + qdf_mem_free(obss_detection); + return -EINVAL; + } + + wma_send_msg(wma, WMA_OBSS_DETECTION_INFO, obss_detection, 0); + + return 0; +} + +static void wma_send_set_key_rsp(uint8_t vdev_id, bool pairwise, + uint8_t key_index) +{ + tSetStaKeyParams *key_info_uc; + tSetBssKeyParams *key_info_mc; + struct wlan_crypto_key *crypto_key; + struct wlan_objmgr_vdev *vdev; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct qdf_mac_addr bcast_mac = QDF_MAC_ADDR_BCAST_INIT; + + if (!wma) { + wma_err("WMA context does not exist"); + return; + } + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, + vdev_id, + WLAN_LEGACY_WMA_ID); + if (!vdev) { + wma_err("VDEV object not found"); + return; + } + crypto_key = wlan_crypto_get_key(vdev, key_index); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); + if (!crypto_key) { + wma_debug("crypto_key not found"); + return; + } + + if (pairwise) { + key_info_uc = qdf_mem_malloc(sizeof(*key_info_uc)); + if (!key_info_uc) + return; + key_info_uc->vdev_id = vdev_id; + key_info_uc->status = QDF_STATUS_SUCCESS; + key_info_uc->key[0].keyLength = crypto_key->keylen; + qdf_mem_copy(&key_info_uc->macaddr, &crypto_key->macaddr, + QDF_MAC_ADDR_SIZE); + wma_send_msg_high_priority(wma, WMA_SET_STAKEY_RSP, + key_info_uc, 0); + } else { + key_info_mc = qdf_mem_malloc(sizeof(*key_info_mc)); + if (!key_info_mc) + return; + key_info_mc->vdev_id = vdev_id; + key_info_mc->status = QDF_STATUS_SUCCESS; + key_info_mc->key[0].keyLength = crypto_key->keylen; + qdf_mem_copy(&key_info_mc->macaddr, &bcast_mac, + QDF_MAC_ADDR_SIZE); + wma_send_msg_high_priority(wma, WMA_SET_BSSKEY_RSP, + key_info_mc, 0); + } +} + +static uint32_t wma_cipher_to_cap(enum wlan_crypto_cipher_type cipher) +{ + switch (cipher) { + case WLAN_CRYPTO_CIPHER_WEP: return WLAN_CRYPTO_CAP_WEP; + case WLAN_CRYPTO_CIPHER_WEP_40: return WLAN_CRYPTO_CAP_WEP; + case WLAN_CRYPTO_CIPHER_WEP_104: return WLAN_CRYPTO_CAP_WEP; + case WLAN_CRYPTO_CIPHER_AES_OCB: return WLAN_CRYPTO_CAP_AES; + case WLAN_CRYPTO_CIPHER_AES_CCM: return WLAN_CRYPTO_CAP_AES; + case WLAN_CRYPTO_CIPHER_AES_CCM_256: return WLAN_CRYPTO_CAP_AES; + case WLAN_CRYPTO_CIPHER_AES_GCM: return WLAN_CRYPTO_CAP_AES; + case WLAN_CRYPTO_CIPHER_AES_GCM_256: return WLAN_CRYPTO_CAP_AES; + case WLAN_CRYPTO_CIPHER_CKIP: return WLAN_CRYPTO_CAP_CKIP; + case WLAN_CRYPTO_CIPHER_TKIP: return WLAN_CRYPTO_CAP_TKIP_MIC; + case WLAN_CRYPTO_CIPHER_WAPI_SMS4: return WLAN_CRYPTO_CAP_WAPI_SMS4; + case WLAN_CRYPTO_CIPHER_WAPI_GCM4: return WLAN_CRYPTO_CAP_WAPI_GCM4; + case WLAN_CRYPTO_CIPHER_FILS_AEAD: return WLAN_CRYPTO_CAP_FILS_AEAD; + default: return 0; + } +} + +void wma_set_peer_ucast_cipher(uint8_t *mac_addr, + enum wlan_crypto_cipher_type cipher) +{ + int32_t set_val = 0, cipher_cap; + struct wlan_objmgr_peer *peer; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + wma_err("wma context is NULL"); + return; + } + + peer = wlan_objmgr_get_peer(wma->psoc, + wlan_objmgr_pdev_get_pdev_id(wma->pdev), + mac_addr, WLAN_LEGACY_WMA_ID); + if (!peer) { + wma_err("Peer of peer_mac "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(mac_addr)); + return; + } + cipher_cap = wma_cipher_to_cap(cipher); + MLME_SET_BIT(set_val, cipher_cap); + wlan_crypto_set_peer_param(peer, WLAN_CRYPTO_PARAM_CIPHER_CAP, + set_val); + set_val = 0; + MLME_SET_BIT(set_val, cipher); + wlan_crypto_set_peer_param(peer, WLAN_CRYPTO_PARAM_UCAST_CIPHER, + set_val); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + + wma_debug("Set unicast cipher %x and cap %x for "QDF_MAC_ADDR_FMT, + 1 << cipher, 1 << cipher_cap, + QDF_MAC_ADDR_REF(mac_addr)); +} + +static void wma_reset_ipn(struct wma_txrx_node *iface, uint8_t key_index) +{ + if (key_index == WMA_IGTK_KEY_INDEX_4 || + key_index == WMA_IGTK_KEY_INDEX_5) + qdf_mem_zero(iface->key.key_id[key_index - + WMA_IGTK_KEY_INDEX_4].ipn, + CMAC_IPN_LEN); +} + +void wma_update_set_key(uint8_t session_id, bool pairwise, + uint8_t key_index, + enum wlan_crypto_cipher_type cipher_type) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *iface; + + if (!wma) { + wma_err("Invalid WMA context"); + return; + } + iface = &wma->interfaces[session_id]; + if (!iface) { + wma_err("iface not found for session id %d", session_id); + return; + } + + if (cipher_type == WLAN_CRYPTO_CIPHER_AES_GMAC || + cipher_type == WLAN_CRYPTO_CIPHER_AES_GMAC_256 || + cipher_type == WLAN_CRYPTO_CIPHER_AES_CMAC) + iface->key.key_cipher = + wlan_crypto_cipher_to_wmi_cipher(cipher_type); + + if (iface) { + wma_reset_ipn(iface, key_index); + iface->is_waiting_for_key = false; + } + if (!pairwise && iface) { + /* Its GTK release the wake lock */ + wma_debug("Release set key wake lock"); + qdf_runtime_pm_allow_suspend( + &iface->vdev_set_key_runtime_wakelock); + wma_release_wakelock(&iface->vdev_set_key_wakelock); + } + + wma_send_set_key_rsp(session_id, pairwise, key_index); +} + +int wma_vdev_bss_color_collision_info_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + struct wmi_obss_color_collision_info *obss_color_info; + QDF_STATUS status; + + if (!event) { + WMA_LOGE("Invalid obss_color_collision event buffer"); + return -EINVAL; + } + + obss_color_info = qdf_mem_malloc(sizeof(*obss_color_info)); + if (!obss_color_info) + return -ENOMEM; + + status = wmi_unified_extract_obss_color_collision_info(wma->wmi_handle, + event, + obss_color_info); + + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to extract obss color info", __func__); + qdf_mem_free(obss_color_info); + return -EINVAL; + } + + if (!wma_is_vdev_valid(obss_color_info->vdev_id)) { + WMA_LOGE("%s: Invalid vdev id %d", __func__, + obss_color_info->vdev_id); + qdf_mem_free(obss_color_info); + return -EINVAL; + } + + wma_send_msg(wma, WMA_OBSS_COLOR_COLLISION_INFO, obss_color_info, 0); + + return 0; +} + +#ifdef FEATURE_ANI_LEVEL_REQUEST +int wma_get_ani_level_evt_handler(void *handle, uint8_t *event_buf, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle)handle; + struct wmi_host_ani_level_event *ani = NULL; + uint32_t num_freqs = 0; + QDF_STATUS status; + struct mac_context *pmac; + int ret = 0; + + pmac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + if (!pmac || !wma) { + WMA_LOGE(FL("Invalid pmac or wma")); + return -EINVAL; + } + + status = wmi_unified_extract_ani_level(wma->wmi_handle, event_buf, + &ani, &num_freqs); + + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to extract ani level", __func__); + return -EINVAL; + } + + if (!pmac->ani_params.ani_level_cb) { + WMA_LOGE(FL("Invalid ani_level_cb")); + ret = -EINVAL; + goto free; + } + + pmac->ani_params.ani_level_cb(ani, num_freqs, + pmac->ani_params.context); + +free: + qdf_mem_free(ani); + return ret; +} +#else +int wma_get_ani_level_evt_handler(void *handle, uint8_t *event_buf, + uint32_t len) +{ + return 0; +} +#endif + diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_fips_api.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_fips_api.c new file mode 100644 index 0000000000000000000000000000000000000000..70f1e65eb806c10be3af2537b9585e1f64c54028 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_fips_api.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_fips_api.c + * + * WLAN Host Device Driver FIPS Certification Feature + */ + +#include "wma.h" +#include "wma_fips_api.h" +#include "wmi_unified_api.h" + +static wma_fips_cb fips_callback; +static void *fips_context; + +static int +wma_fips_event_handler(void *handle, uint8_t *event, uint32_t len) +{ + tp_wma_handle wma_handle; + wmi_unified_t wmi_handle; + struct wmi_host_fips_event_param param; + wma_fips_cb callback; + QDF_STATUS status; + + WMA_LOGI(FL("handle:%pK event:%pK len:%u"), handle, event, len); + + wma_handle = handle; + if (!wma_handle) { + WMA_LOGE(FL("NULL wma_handle")); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE(FL("NULL wmi_handle")); + return QDF_STATUS_E_INVAL; + } + + status = wmi_extract_fips_event_data(wmi_handle, event, ¶m); + + WMA_LOGI(FL("Received FIPS event, pdev:%u status:%u data_len:%u"), + param.pdev_id, param.error_status, param.data_len); + + /* make sure extraction error is propagated to upper layers */ + if (QDF_IS_STATUS_ERROR(status)) + param.error_status = FIPS_ERROR_OPER_TIMEOUT; + + callback = fips_callback; + fips_callback = NULL; + if (callback) + callback(fips_context, ¶m); + + return 0; +} + +QDF_STATUS wma_fips_request(WMA_HANDLE handle, + struct fips_params *param, + wma_fips_cb callback, + void *context) +{ + tp_wma_handle wma_handle = handle; + wmi_unified_t wmi_handle; + QDF_STATUS status; + + if (!wma_handle) { + WMA_LOGE(FL("NULL wma_handle")); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE(FL("NULL wmi_handle")); + return QDF_STATUS_E_INVAL; + } + + fips_callback = callback; + fips_context = context; + status = wmi_unified_pdev_fips_cmd_send(wmi_handle, param); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE(FL("wmi_unified_pdev_fips_cmd_send() error: %u"), + status); + fips_callback = NULL; + } + + return status; +} + +QDF_STATUS wma_fips_register_event_handlers(WMA_HANDLE handle) +{ + tp_wma_handle wma_handle = handle; + + return wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_pdev_fips_event_id, + wma_fips_event_handler, + WMA_RX_WORK_CTX); +} diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_fips_api.h b/drivers/staging/qcacld-3.0/core/wma/src/wma_fips_api.h new file mode 100644 index 0000000000000000000000000000000000000000..5ca0efb94470f31888b344687afe22e974f3a769 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_fips_api.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WMA_FIPS_API_H +#define __WMA_FIPS_API_H + +#include "wma_api.h" +#include "wmi_unified_api.h" +#include "wma_fips_public_structs.h" + +#ifdef WLAN_FEATURE_FIPS +/** + * wma_fips_request() - Perform a FIPS certification operation + * @handle: WMA handle of the object being certified + * @param: The FIPS certification parameters + * @callback: Callback function to invoke with the results + * @context: Opaque context to pass back to caller in the callback + * + * Return: QDF_STATUS_SUCCESS if the request is successfully sent + * to firmware for processing, otherwise an error status. + */ +QDF_STATUS wma_fips_request(WMA_HANDLE handle, + struct fips_params *param, + wma_fips_cb callback, + void *context); + +/** + * wma_fips_register_event_handlers() - Register FIPS event handlers + * @handle: WMA handle of the object being initialized + * + * This function registers all WMI event handlers required by the FIPS + * feature. + * + * Return: QDF_STATUS_SUCCESS upon success, otherwise an error + */ +QDF_STATUS wma_fips_register_event_handlers(WMA_HANDLE handle); + +#else /* WLAN_FEATURE_FIPS */ + +static inline +QDF_STATUS wma_fips_request(WMA_HANDLE handle, + const struct fips_params *param, + wma_fips_cb callback, + void *context) +{ + return QDF_STATUS_E_NOSUPPORT; +} + +static inline +QDF_STATUS wma_fips_register_event_handlers(WMA_HANDLE wma_handle) +{ + return QDF_STATUS_SUCCESS; +} + +#endif /* WLAN_FEATURE_FIPS */ + +#endif /* __WMA_FIPS_API_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_fips_public_structs.h b/drivers/staging/qcacld-3.0/core/wma/src/wma_fips_public_structs.h new file mode 100644 index 0000000000000000000000000000000000000000..13c5999b3b6029702feee146a57c2ce103e1207d --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_fips_public_structs.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WMA_FIPS_PUBLIC_STRUCTS_H +#define __WMA_FIPS_PUBLIC_STRUCTS_H + +struct wmi_host_fips_event_param; + +/** + * typedef wma_fips_cb() - FIPS callback function + * @context: Opaque context provided by caller in FIPS request + * @param: FIPS event parameters + */ +typedef void (*wma_fips_cb)(void *context, + struct wmi_host_fips_event_param *param); + +#endif /* __WMA_FIPS_PUBLIC_STRUCTS_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_fw_state.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_fw_state.c new file mode 100644 index 0000000000000000000000000000000000000000..68e8145465fdbb34d81617c047a815697627f39c --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_fw_state.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_fw_state.c + * + * The implementation for getting firmware state + */ + +#include "wma_fw_state.h" +#include "wmi_unified_api.h" + +QDF_STATUS wma_get_fw_state(tp_wma_handle wma_handle) +{ + wmi_echo_cmd_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint32_t len = sizeof(*cmd); + + if (!wma_handle) { + WMA_LOGE(FL("WMA is closed, can not issue cmd")); + return QDF_STATUS_E_INVAL; + } + + wmi_buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!wmi_buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_echo_cmd_fixed_param *)wmi_buf_data(wmi_buf); + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_echo_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_echo_cmd_fixed_param)); + cmd->value = true; + + if (wmi_unified_cmd_send(wma_handle->wmi_handle, wmi_buf, len, + WMI_ECHO_CMDID)) { + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_echo_event_handler() - process fw state rsp + * @handle: wma interface + * @buf: wmi event buf pointer + * @len: length of event buffer + * + * This function will send eWNI_SME_FW_STATUS_IND to SME + * + * Return: 0 for success or error code + */ +static int wma_echo_event_handler(void *handle, uint8_t *buf, uint32_t len) +{ + struct scheduler_msg sme_msg = { + .type = eWNI_SME_FW_STATUS_IND, + }; + QDF_STATUS qdf_status; + + WMA_LOGD("Received Echo reply from firmware!"); + + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("%s: Fail to post fw state reply msg", __func__); + return -EINVAL; + } + + return 0; +} + +void wma_register_fw_state_events(wmi_unified_t wmi_handle) +{ + wmi_unified_register_event_handler(wmi_handle, + wmi_echo_event_id, + wma_echo_event_handler, + WMA_RX_SERIALIZER_CTX); +} diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_he.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_he.c new file mode 100644 index 0000000000000000000000000000000000000000..5b5ce5ad481bbe981b17aa4d0cd20f86e7d4fcf7 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_he.c @@ -0,0 +1,1494 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_he.c + * + * WLAN Host Device Driver 802.11ax - High Efficiency Implementation + */ + +#include "wma_he.h" +#include "wmi_unified_api.h" +#include "cds_utils.h" +#include "wma_internal.h" + +/** + * wma_he_ppet_merge() - Merge PPET8 and PPET16 for a given ru and nss + * @host_ppet: pointer to dot11f array + * @byte_idx_p: pointer to byte index position where ppet should be added + * @used_p: pointer to used position + * @ppet: incoming PPET to be merged + * + * This function does the actual packing of dot11f structure. Split the + * incoming PPET(3 bits) to fit into an octet. If there are more than + * 3 bits available in a given byte_idx no splitting is required. + * + * Return: None + */ +static void wma_he_ppet_merge(uint8_t *host_ppet, int *byte_idx_p, int *used_p, + uint8_t ppet) +{ + int byte_idx = *byte_idx_p, used = *used_p; + int lshift, rshift; + + if (used <= (HE_BYTE_SIZE - HE_PPET_SIZE)) { + /* Enough space to fit the incoming PPET */ + lshift = used; + host_ppet[byte_idx] |= (ppet << lshift); + used += HE_PPET_SIZE; + if (used == HE_BYTE_SIZE) { + used = 0; + byte_idx++; + } + } else { + /* Need to split the PPET */ + lshift = used; + rshift = HE_BYTE_SIZE - used; + host_ppet[byte_idx] |= (ppet << lshift); + byte_idx++; + used = 0; + host_ppet[byte_idx] |= (ppet >> rshift); + used += HE_PPET_SIZE - rshift; + } + + *byte_idx_p = byte_idx; + *used_p = used; +} + +/** + * wma_he_populate_ppet() - Helper function for PPET conversion + * @ppet: pointer to fw array + * @nss: Number of NSS + * @ru: Number of RU + * @host_ppet: pointer to dot11f array + * @req_byte: Number of bytes in dot11f array + * + * This function retrieves PPET16/PPET8 pair for combination of NSS/RU + * and try to pack them in the OTA type dot11f structure by calling + * wma_he_ppet_merge. + * + * Return: None + */ +static void wma_he_populate_ppet(uint32_t *ppet, int nss, int ru, + uint8_t *host_ppet, int req_byte) +{ + int byte_idx = 0, used, i, j; + uint8_t ppet16, ppet8; + + WMA_LOGD(FL("nss: %d ru: %d req_byte: %d\n"), nss, ru, req_byte); + /* NSS and RU_IDX are already populated */ + used = HE_PPET_NSS_RU_LEN; + + for (i = 0; i < nss; i++) { + for (j = 1; j <= ru; j++) { + ppet16 = WMI_GET_PPET16(ppet, j, i); + ppet8 = WMI_GET_PPET8(ppet, j, i); + WMA_LOGD(FL("ppet16 (nss:%d ru:%d): %04x"), + i, j, ppet16); + WMA_LOGD(FL("ppet8 (nss:%d ru:%d): %04x"), + i, j, ppet8); + wma_he_ppet_merge(host_ppet, &byte_idx, &used, ppet16); + wma_he_ppet_merge(host_ppet, &byte_idx, &used, ppet8); + } + } + +} + +/** + * wma_convert_he_ppet() - convert WMI ppet structure to dot11f structure + * @he_ppet: pointer to dot11f ppet structure + * @ppet: pointer to FW ppet structure + * + * PPET info coming from FW is stored as described in WMI definition. Convert + * that into equivalent dot11f structure. + * + * Return: None + */ +static void wma_convert_he_ppet(uint8_t *he_ppet, + struct wmi_host_ppe_threshold *ppet) +{ + int bits, req_byte; + struct ppet_hdr *hdr; + uint8_t ru_count, mask; + struct ppe_threshold *ppet_1; + + ppet_1 = NULL; + if (!ppet) { + WMA_LOGE(FL("PPET is NULL")); + qdf_mem_zero(he_ppet, HE_MAX_PPET_SIZE); + return; + } + + hdr = (struct ppet_hdr *)he_ppet; + hdr->nss = ppet->numss_m1; + hdr->ru_idx_mask = ppet->ru_bit_mask; + mask = hdr->ru_idx_mask; + for (ru_count = 0; mask; mask >>= 1) + if (mask & 0x01) + ru_count++; + + /* + * there will be two PPET for each NSS/RU pair + * PPET8 and PPET16, hence HE_PPET_SIZE * 2 bits for PPET + */ + bits = HE_PPET_NSS_RU_LEN + ((hdr->nss + 1) * ru_count) * + (HE_PPET_SIZE * 2); + + req_byte = (bits / HE_BYTE_SIZE) + 1; + wma_he_populate_ppet(ppet->ppet16_ppet8_ru3_ru0, hdr->nss + 1, + ru_count, he_ppet, req_byte); +} + +/** + * wma_convert_he_cap() - convert HE capabilities into dot11f structure + * @he_cap: pointer to dot11f structure + * @mac_cap: Received HE MAC capability + * @phy_cap: Received HE PHY capability + * @supp_mcs: Max MCS supported (Tx/Rx) + * @tx_chain_mask: Tx chain mask + * @rx_chain_mask: Rx chain mask + * + * This function converts various HE capability received as part of extended + * service ready event into dot11f structure. GET macros are defined at WMI + * layer, use them to unpack the incoming FW capability. + * + * Return: None + */ +static void wma_convert_he_cap(tDot11fIEhe_cap *he_cap, uint32_t *mac_cap, + uint32_t *phy_cap, uint32_t supp_mcs, + uint32_t tx_chain_mask, uint32_t rx_chain_mask) +{ + uint8_t nss, chan_width; + uint16_t rx_mcs_le_80, tx_mcs_le_80, rx_mcs_160, tx_mcs_160; + + nss = QDF_MAX(wma_get_num_of_setbits_from_bitmask(tx_chain_mask), + wma_get_num_of_setbits_from_bitmask(rx_chain_mask)); + + he_cap->present = true; + /* HE MAC capabilities */ + he_cap->htc_he = WMI_HECAP_MAC_HECTRL_GET(mac_cap[0]); + he_cap->twt_request = WMI_HECAP_MAC_TWTREQ_GET(mac_cap[0]); + he_cap->twt_responder = WMI_HECAP_MAC_TWTRSP_GET(mac_cap[0]); + he_cap->fragmentation = WMI_HECAP_MAC_HEFRAG_GET(mac_cap[0]); + he_cap->max_num_frag_msdu_amsdu_exp = + WMI_HECAP_MAC_MAXFRAGMSDU_GET(mac_cap[0]); + he_cap->min_frag_size = WMI_HECAP_MAC_MINFRAGSZ_GET(mac_cap[0]); + he_cap->trigger_frm_mac_pad = WMI_HECAP_MAC_TRIGPADDUR_GET(mac_cap[0]); + he_cap->multi_tid_aggr_rx_supp = + WMI_HECAP_MAC_ACKMTIDAMPDU_GET(mac_cap[0]); + he_cap->he_link_adaptation = WMI_HECAP_MAC_HELINK_ADPT_GET(mac_cap[0]); + he_cap->all_ack = WMI_HECAP_MAC_AACK_GET(mac_cap[0]); + he_cap->trigd_rsp_sched = WMI_HECAP_MAC_TRS_GET(mac_cap[0]); + he_cap->a_bsr = WMI_HECAP_MAC_BSR_GET(mac_cap[0]); + he_cap->broadcast_twt = WMI_HECAP_MAC_BCSTTWT_GET(mac_cap[0]); + he_cap->ba_32bit_bitmap = WMI_HECAP_MAC_32BITBA_GET(mac_cap[0]); + he_cap->mu_cascade = WMI_HECAP_MAC_MUCASCADE_GET(mac_cap[0]); + he_cap->ack_enabled_multitid = + WMI_HECAP_MAC_ACKMTIDAMPDU_GET(mac_cap[0]); + he_cap->omi_a_ctrl = WMI_HECAP_MAC_OMI_GET(mac_cap[0]); + he_cap->ofdma_ra = WMI_HECAP_MAC_OFDMARA_GET(mac_cap[0]); + he_cap->max_ampdu_len_exp_ext = + WMI_HECAP_MAC_MAXAMPDULEN_EXP_GET(mac_cap[0]); + he_cap->amsdu_frag = WMI_HECAP_MAC_AMSDUFRAG_GET(mac_cap[0]); + he_cap->flex_twt_sched = WMI_HECAP_MAC_FLEXTWT_GET(mac_cap[0]); + he_cap->rx_ctrl_frame = WMI_HECAP_MAC_MBSS_GET(mac_cap[0]); + he_cap->bsrp_ampdu_aggr = WMI_HECAP_MAC_BSRPAMPDU_GET(mac_cap[1]); + he_cap->qtp = WMI_HECAP_MAC_QTP_GET(mac_cap[1]); + he_cap->a_bqr = WMI_HECAP_MAC_ABQR_GET(mac_cap[1]); + he_cap->spatial_reuse_param_rspder = + WMI_HECAP_MAC_SRPRESP_GET(mac_cap[1]); + he_cap->ndp_feedback_supp = WMI_HECAP_MAC_NDPFDBKRPT_GET(mac_cap[1]); + he_cap->ops_supp = WMI_HECAP_MAC_OPS_GET(mac_cap[1]); + he_cap->amsdu_in_ampdu = WMI_HECAP_MAC_AMSDUINAMPDU_GET(mac_cap[1]); + he_cap->multi_tid_aggr_tx_supp = WMI_HECAP_MAC_MTID_TX_GET(mac_cap[1]); + he_cap->he_sub_ch_sel_tx_supp = + WMI_HECAP_MAC_SUBCHANSELTX_GET(mac_cap[1]); + he_cap->ul_2x996_tone_ru_supp = WMI_HECAP_MAC_UL2X996RU_GET(mac_cap[1]); + he_cap->om_ctrl_ul_mu_data_dis_rx = + WMI_HECAP_MAC_OMCULMUDDIS_GET(mac_cap[1]); + he_cap->he_dynamic_smps = + WMI_HECAP_MAC_DYNSMPWRSAVE_GET(mac_cap[1]); + he_cap->punctured_sounding_supp = + WMI_HECAP_MAC_PUNCSOUNDING_GET(mac_cap[1]); + he_cap->ht_vht_trg_frm_rx_supp = + WMI_HECAP_MAC_HTVHTTRIGRX_GET(mac_cap[1]); + /* HE PHY capabilities */ + chan_width = WMI_HECAP_PHY_CBW_GET(phy_cap); + he_cap->chan_width_0 = HE_CH_WIDTH_GET_BIT(chan_width, 0); + he_cap->chan_width_1 = HE_CH_WIDTH_GET_BIT(chan_width, 1); + he_cap->chan_width_2 = HE_CH_WIDTH_GET_BIT(chan_width, 2); + he_cap->chan_width_3 = HE_CH_WIDTH_GET_BIT(chan_width, 3); + he_cap->chan_width_4 = HE_CH_WIDTH_GET_BIT(chan_width, 4); + he_cap->chan_width_5 = HE_CH_WIDTH_GET_BIT(chan_width, 5); + he_cap->chan_width_6 = HE_CH_WIDTH_GET_BIT(chan_width, 6); + he_cap->rx_pream_puncturing = WMI_HECAP_PHY_PREAMBLEPUNCRX_GET(phy_cap); + he_cap->device_class = WMI_HECAP_PHY_COD_GET(phy_cap); + he_cap->ldpc_coding = WMI_HECAP_PHY_LDPC_GET(phy_cap); + he_cap->he_1x_ltf_800_gi_ppdu = WMI_HECAP_PHY_LTFGIFORHE_GET(phy_cap); + he_cap->midamble_tx_rx_max_nsts = + WMI_HECAP_PHY_MIDAMBLETXRXMAXNSTS_GET(phy_cap); + he_cap->he_4x_ltf_3200_gi_ndp = WMI_HECAP_PHY_LTFGIFORNDP_GET(phy_cap); + he_cap->rx_stbc_lt_80mhz = WMI_HECAP_PHY_RXSTBC_GET(phy_cap); + he_cap->tb_ppdu_tx_stbc_lt_80mhz = WMI_HECAP_PHY_TXSTBC_GET(phy_cap); + he_cap->rx_stbc_gt_80mhz = WMI_HECAP_PHY_STBCRXGT80_GET(phy_cap); + he_cap->tb_ppdu_tx_stbc_gt_80mhz = + WMI_HECAP_PHY_STBCTXGT80_GET(phy_cap); + + he_cap->doppler = (WMI_HECAP_PHY_RXDOPPLER_GET(phy_cap) << 1) | + WMI_HECAP_PHY_TXDOPPLER(phy_cap); + he_cap->ul_mu = (WMI_HECAP_PHY_ULMUMIMOOFDMA_GET(phy_cap) << 1) | + WMI_HECAP_PHY_UL_MU_MIMO_GET(phy_cap); + he_cap->dcm_enc_tx = WMI_HECAP_PHY_DCMTX_GET(phy_cap); + he_cap->dcm_enc_rx = WMI_HECAP_PHY_DCMRX_GET(phy_cap); + he_cap->ul_he_mu = WMI_HECAP_PHY_ULHEMU_GET(phy_cap); + he_cap->su_beamformer = WMI_HECAP_PHY_SUBFMR_GET(phy_cap); + he_cap->su_beamformee = WMI_HECAP_PHY_SUBFME_GET(phy_cap); + he_cap->mu_beamformer = WMI_HECAP_PHY_MUBFMR_GET(phy_cap); + he_cap->bfee_sts_lt_80 = WMI_HECAP_PHY_BFMESTSLT80MHZ_GET(phy_cap); + he_cap->bfee_sts_gt_80 = WMI_HECAP_PHY_BFMESTSGT80MHZ_GET(phy_cap); + he_cap->num_sounding_lt_80 = WMI_HECAP_PHY_NUMSOUNDLT80MHZ_GET(phy_cap); + he_cap->num_sounding_gt_80 = WMI_HECAP_PHY_NUMSOUNDGT80MHZ_GET(phy_cap); + he_cap->su_feedback_tone16 = + WMI_HECAP_PHY_NG16SUFEEDBACKLT80_GET(phy_cap); + he_cap->mu_feedback_tone16 = + WMI_HECAP_PHY_NG16MUFEEDBACKGT80_GET(phy_cap); + he_cap->codebook_su = WMI_HECAP_PHY_CODBK42SU_GET(phy_cap); + he_cap->codebook_mu = WMI_HECAP_PHY_CODBK75MU_GET(phy_cap); + he_cap->beamforming_feedback = + WMI_HECAP_PHY_BFFEEDBACKTRIG_GET(phy_cap); + he_cap->he_er_su_ppdu = WMI_HECAP_PHY_HEERSU_GET(phy_cap); + he_cap->dl_mu_mimo_part_bw = + WMI_HECAP_PHY_DLMUMIMOPARTIALBW_GET(phy_cap); + he_cap->ppet_present = WMI_HECAP_PHY_PETHRESPRESENT_GET(phy_cap); + he_cap->srp = WMI_HECAP_PHY_SRPSPRESENT_GET(phy_cap); + he_cap->power_boost = WMI_HECAP_PHY_PWRBOOSTAR_GET(phy_cap); + he_cap->he_ltf_800_gi_4x = + WMI_HECAP_PHY_4XLTFAND800NSECSGI_GET(phy_cap); + he_cap->max_nc = WMI_HECAP_PHY_MAXNC_GET(phy_cap); + he_cap->tb_ppdu_tx_stbc_gt_80mhz = + WMI_HECAP_PHY_STBCTXGT80_GET(phy_cap); + he_cap->rx_stbc_gt_80mhz = WMI_HECAP_PHY_STBCRXGT80_GET(phy_cap); + he_cap->er_he_ltf_800_gi_4x = + WMI_HECAP_PHY_ERSU4X800NSECGI_GET(phy_cap); + he_cap->he_ppdu_20_in_40Mhz_2G = + WMI_HECAP_PHY_HEPPDU20IN40MHZ2G_GET(phy_cap); + he_cap->he_ppdu_20_in_160_80p80Mhz = + WMI_HECAP_PHY_HEPPDU20IN160OR80P80MHZ_GET(phy_cap); + he_cap->he_ppdu_80_in_160_80p80Mhz = + WMI_HECAP_PHY_HEPPDU80IN160OR80P80MHZ_GET(phy_cap); + he_cap->er_1x_he_ltf_gi = WMI_HECAP_PHY_ERSU1X800NSECGI_GET(phy_cap); + he_cap->midamble_tx_rx_1x_he_ltf = + WMI_HECAP_PHY_MIDAMBLETXRX2XAND1XHELTF_GET(phy_cap); + + he_cap->dcm_max_bw = WMI_HECAP_PHY_DCMMAXBW_GET(phy_cap); + he_cap->longer_than_16_he_sigb_ofdm_sym = + WMI_HECAP_PHY_LNG16SIGBSYMBSUPRT_GET(phy_cap); + he_cap->non_trig_cqi_feedback = + WMI_HECAP_PHY_NONTRIGCQIFEEDBK_GET(phy_cap); + he_cap->tx_1024_qam_lt_242_tone_ru = + WMI_HECAP_PHY_TX1024QAM242RUSUPRT_GET(phy_cap); + he_cap->rx_1024_qam_lt_242_tone_ru = + WMI_HECAP_PHY_RX1024QAM242RUSUPRT_GET(phy_cap); + he_cap->rx_full_bw_su_he_mu_compress_sigb = + WMI_HECAP_PHY_RXFULBWSUWCMPRSSIGB_GET(phy_cap); + he_cap->rx_full_bw_su_he_mu_non_cmpr_sigb = + WMI_HECAP_PHY_RXFULBWSUWNONCMPRSSIGB_GET(phy_cap); + + /* + * supp_mcs is split into 16 bits with lower indicating le_80 and + * upper indicating 160 and 80_80. + */ + WMA_LOGD(FL("supported_mcs: 0x%08x\n"), supp_mcs); + rx_mcs_le_80 = supp_mcs & 0xFFFF; + tx_mcs_le_80 = supp_mcs & 0xFFFF; + rx_mcs_160 = (supp_mcs & 0xFFFF0000) >> 16; + tx_mcs_160 = (supp_mcs & 0xFFFF0000) >> 16; + /* if FW indicated it is using 1x1 disable upper NSS-MCS sets */ + if (nss == NSS_1x1_MODE) { + rx_mcs_le_80 |= HE_MCS_INV_MSK_4_NSS(1); + tx_mcs_le_80 |= HE_MCS_INV_MSK_4_NSS(1); + rx_mcs_160 |= HE_MCS_INV_MSK_4_NSS(1); + tx_mcs_160 |= HE_MCS_INV_MSK_4_NSS(1); + } + he_cap->rx_he_mcs_map_lt_80 = rx_mcs_le_80; + he_cap->tx_he_mcs_map_lt_80 = tx_mcs_le_80; + *((uint16_t *)he_cap->tx_he_mcs_map_160) = rx_mcs_160; + *((uint16_t *)he_cap->rx_he_mcs_map_160) = tx_mcs_160; + *((uint16_t *)he_cap->rx_he_mcs_map_80_80) = rx_mcs_160; + *((uint16_t *)he_cap->tx_he_mcs_map_80_80) = tx_mcs_160; +} + +/** + * wma_derive_ext_he_cap() - Derive HE caps based on given value + * @he_cap: pointer to given HE caps to be filled + * @new_he_cap: new HE cap info provided. + * + * This function takes the value provided in and combines it wht the incoming + * HE capability. After decoding, what ever value it gets, + * it takes the union(max) or intersection(min) with previously derived values. + * Currently, intersection(min) is taken for all the capabilities. + * + * Return: none + */ +static void wma_derive_ext_he_cap(tDot11fIEhe_cap *he_cap, + tDot11fIEhe_cap *new_cap, + bool is_5g_cap) +{ + uint16_t mcs_1, mcs_2; + + if (!he_cap->present) { + /* First time update, copy the capability as is */ + qdf_mem_copy(he_cap, new_cap, sizeof(*he_cap)); + he_cap->present = true; + return; + } + /* Take union(max) or intersection(min) of the capabilities */ + he_cap->htc_he = QDF_MIN(he_cap->htc_he, new_cap->htc_he); + he_cap->twt_request = QDF_MIN(he_cap->twt_request, + new_cap->twt_request); + he_cap->twt_responder = QDF_MIN(he_cap->twt_responder, + new_cap->twt_responder); + he_cap->fragmentation = QDF_MIN(he_cap->fragmentation, + new_cap->fragmentation); + he_cap->max_num_frag_msdu_amsdu_exp = QDF_MIN( + he_cap->max_num_frag_msdu_amsdu_exp, + new_cap->max_num_frag_msdu_amsdu_exp); + he_cap->min_frag_size = QDF_MIN(he_cap->min_frag_size, + new_cap->min_frag_size); + he_cap->trigger_frm_mac_pad = + QDF_MIN(he_cap->trigger_frm_mac_pad, + new_cap->trigger_frm_mac_pad); + he_cap->multi_tid_aggr_rx_supp = QDF_MIN(he_cap->multi_tid_aggr_rx_supp, + new_cap->multi_tid_aggr_rx_supp); + he_cap->he_link_adaptation = QDF_MIN(he_cap->he_link_adaptation, + new_cap->he_link_adaptation); + he_cap->all_ack = QDF_MIN(he_cap->all_ack, + new_cap->all_ack); + he_cap->trigd_rsp_sched = QDF_MIN(he_cap->trigd_rsp_sched, + new_cap->trigd_rsp_sched); + he_cap->a_bsr = QDF_MIN(he_cap->a_bsr, + new_cap->a_bsr); + he_cap->broadcast_twt = QDF_MIN(he_cap->broadcast_twt, + new_cap->broadcast_twt); + he_cap->ba_32bit_bitmap = QDF_MIN(he_cap->ba_32bit_bitmap, + new_cap->ba_32bit_bitmap); + he_cap->mu_cascade = QDF_MIN(he_cap->mu_cascade, + new_cap->mu_cascade); + he_cap->ack_enabled_multitid = + QDF_MIN(he_cap->ack_enabled_multitid, + new_cap->ack_enabled_multitid); + he_cap->omi_a_ctrl = QDF_MIN(he_cap->omi_a_ctrl, + new_cap->omi_a_ctrl); + he_cap->ofdma_ra = QDF_MIN(he_cap->ofdma_ra, + new_cap->ofdma_ra); + he_cap->max_ampdu_len_exp_ext = QDF_MIN(he_cap->max_ampdu_len_exp_ext, + new_cap->max_ampdu_len_exp_ext); + he_cap->amsdu_frag = QDF_MIN(he_cap->amsdu_frag, + new_cap->amsdu_frag); + he_cap->flex_twt_sched = QDF_MIN(he_cap->flex_twt_sched, + new_cap->flex_twt_sched); + he_cap->rx_ctrl_frame = QDF_MIN(he_cap->rx_ctrl_frame, + new_cap->rx_ctrl_frame); + he_cap->bsrp_ampdu_aggr = QDF_MIN(he_cap->bsrp_ampdu_aggr, + new_cap->bsrp_ampdu_aggr); + he_cap->qtp = QDF_MIN(he_cap->qtp, new_cap->qtp); + he_cap->a_bqr = QDF_MIN(he_cap->a_bqr, new_cap->a_bqr); + he_cap->spatial_reuse_param_rspder = QDF_MIN( + he_cap->spatial_reuse_param_rspder, + new_cap->spatial_reuse_param_rspder); + he_cap->ndp_feedback_supp = QDF_MIN(he_cap->ndp_feedback_supp, + new_cap->ndp_feedback_supp); + he_cap->ops_supp = QDF_MIN(he_cap->ops_supp, new_cap->ops_supp); + he_cap->amsdu_in_ampdu = QDF_MIN(he_cap->amsdu_in_ampdu, + new_cap->amsdu_in_ampdu); + + he_cap->chan_width_0 = he_cap->chan_width_0 | new_cap->chan_width_0; + he_cap->chan_width_1 = he_cap->chan_width_1 | new_cap->chan_width_1; + he_cap->chan_width_2 = he_cap->chan_width_2 | new_cap->chan_width_2; + he_cap->chan_width_3 = he_cap->chan_width_3 | new_cap->chan_width_3; + he_cap->chan_width_4 = he_cap->chan_width_4 | new_cap->chan_width_4; + he_cap->chan_width_5 = he_cap->chan_width_5 | new_cap->chan_width_5; + he_cap->chan_width_6 = he_cap->chan_width_6 | new_cap->chan_width_6; + + he_cap->rx_pream_puncturing = + QDF_MIN(he_cap->rx_pream_puncturing, + new_cap->rx_pream_puncturing); + he_cap->device_class = QDF_MIN(he_cap->device_class, + new_cap->device_class); + he_cap->ldpc_coding = QDF_MIN(he_cap->ldpc_coding, + new_cap->ldpc_coding); + he_cap->he_1x_ltf_800_gi_ppdu = + QDF_MIN(he_cap->he_1x_ltf_800_gi_ppdu, + new_cap->he_1x_ltf_800_gi_ppdu); + he_cap->midamble_tx_rx_max_nsts = + QDF_MIN(he_cap->midamble_tx_rx_max_nsts, + new_cap->midamble_tx_rx_max_nsts); + he_cap->he_4x_ltf_3200_gi_ndp = + QDF_MIN(he_cap->he_4x_ltf_3200_gi_ndp, + new_cap->he_4x_ltf_3200_gi_ndp); + he_cap->tb_ppdu_tx_stbc_lt_80mhz = QDF_MIN( + he_cap->tb_ppdu_tx_stbc_lt_80mhz, + new_cap->tb_ppdu_tx_stbc_lt_80mhz); + he_cap->rx_stbc_lt_80mhz = QDF_MIN(he_cap->rx_stbc_lt_80mhz, + new_cap->rx_stbc_lt_80mhz); + he_cap->doppler = QDF_MIN(he_cap->doppler, + new_cap->doppler); + he_cap->ul_mu = QDF_MIN(he_cap->ul_mu, new_cap->ul_mu); + he_cap->dcm_enc_tx = QDF_MIN(he_cap->dcm_enc_tx, + new_cap->dcm_enc_tx); + he_cap->dcm_enc_rx = QDF_MIN(he_cap->dcm_enc_rx, + new_cap->dcm_enc_rx); + he_cap->ul_he_mu = QDF_MIN(he_cap->ul_he_mu, new_cap->ul_he_mu); + he_cap->su_beamformer = QDF_MIN(he_cap->su_beamformer, + new_cap->su_beamformer); + he_cap->su_beamformee = QDF_MIN(he_cap->su_beamformee, + new_cap->su_beamformee); + he_cap->mu_beamformer = QDF_MIN(he_cap->mu_beamformer, + new_cap->mu_beamformer); + he_cap->bfee_sts_lt_80 = QDF_MIN(he_cap->bfee_sts_lt_80, + new_cap->bfee_sts_lt_80); + he_cap->bfee_sts_gt_80 = QDF_MIN(he_cap->bfee_sts_gt_80, + new_cap->bfee_sts_gt_80); + he_cap->num_sounding_lt_80 = QDF_MIN(he_cap->num_sounding_lt_80, + new_cap->num_sounding_lt_80); + he_cap->num_sounding_gt_80 = QDF_MIN(he_cap->num_sounding_gt_80, + new_cap->num_sounding_gt_80); + he_cap->su_feedback_tone16 = QDF_MIN(he_cap->su_feedback_tone16, + new_cap->su_feedback_tone16); + he_cap->mu_feedback_tone16 = QDF_MIN(he_cap->mu_feedback_tone16, + new_cap->mu_feedback_tone16); + he_cap->codebook_su = QDF_MIN(he_cap->codebook_su, + new_cap->codebook_su); + he_cap->codebook_mu = QDF_MIN(he_cap->codebook_mu, + new_cap->codebook_mu); + he_cap->beamforming_feedback = + QDF_MIN(he_cap->beamforming_feedback, + new_cap->beamforming_feedback); + he_cap->he_er_su_ppdu = QDF_MIN(he_cap->he_er_su_ppdu, + new_cap->he_er_su_ppdu); + he_cap->dl_mu_mimo_part_bw = QDF_MIN(he_cap->dl_mu_mimo_part_bw, + new_cap->dl_mu_mimo_part_bw); + he_cap->ppet_present = QDF_MIN(he_cap->ppet_present, + new_cap->ppet_present); + he_cap->srp = QDF_MIN(he_cap->srp, new_cap->srp); + he_cap->power_boost = QDF_MIN(he_cap->power_boost, + new_cap->power_boost); + he_cap->he_ltf_800_gi_4x = QDF_MIN(he_cap->he_ltf_800_gi_4x, + new_cap->he_ltf_800_gi_4x); + he_cap->er_he_ltf_800_gi_4x = + QDF_MIN(he_cap->er_he_ltf_800_gi_4x, + new_cap->er_he_ltf_800_gi_4x); + he_cap->he_ppdu_20_in_40Mhz_2G = + QDF_MIN(he_cap->he_ppdu_20_in_40Mhz_2G, + new_cap->he_ppdu_20_in_40Mhz_2G); + he_cap->he_ppdu_20_in_160_80p80Mhz = + QDF_MIN(he_cap->he_ppdu_20_in_160_80p80Mhz, + new_cap->he_ppdu_20_in_160_80p80Mhz); + he_cap->he_ppdu_80_in_160_80p80Mhz = + QDF_MIN(he_cap->he_ppdu_80_in_160_80p80Mhz, + new_cap->he_ppdu_80_in_160_80p80Mhz); + he_cap->er_1x_he_ltf_gi = QDF_MIN(he_cap->er_1x_he_ltf_gi, + new_cap->er_1x_he_ltf_gi); + he_cap->midamble_tx_rx_1x_he_ltf = + QDF_MIN(he_cap->midamble_tx_rx_1x_he_ltf, + new_cap->midamble_tx_rx_1x_he_ltf); + he_cap->reserved2 = QDF_MIN(he_cap->reserved2, + new_cap->reserved2); + + /* take intersection for MCS map */ + mcs_1 = he_cap->rx_he_mcs_map_lt_80; + mcs_2 = new_cap->rx_he_mcs_map_lt_80; + he_cap->rx_he_mcs_map_lt_80 = HE_INTERSECT_MCS(mcs_1, mcs_2); + mcs_1 = he_cap->tx_he_mcs_map_lt_80; + mcs_2 = new_cap->tx_he_mcs_map_lt_80; + he_cap->tx_he_mcs_map_lt_80 = HE_INTERSECT_MCS(mcs_1, mcs_2); + if (is_5g_cap) { + mcs_1 = *((uint16_t *)he_cap->rx_he_mcs_map_160); + mcs_2 = *((uint16_t *)new_cap->rx_he_mcs_map_160); + *((uint16_t *)he_cap->rx_he_mcs_map_160) = + HE_INTERSECT_MCS(mcs_1, mcs_2); + mcs_1 = *((uint16_t *)he_cap->tx_he_mcs_map_160); + mcs_2 = *((uint16_t *)new_cap->tx_he_mcs_map_160); + *((uint16_t *)he_cap->tx_he_mcs_map_160) = + HE_INTERSECT_MCS(mcs_1, mcs_2); + mcs_1 = *((uint16_t *)he_cap->rx_he_mcs_map_80_80); + mcs_2 = *((uint16_t *)new_cap->rx_he_mcs_map_80_80); + *((uint16_t *)he_cap->rx_he_mcs_map_80_80) = + HE_INTERSECT_MCS(mcs_1, mcs_2); + mcs_1 = *((uint16_t *)he_cap->tx_he_mcs_map_80_80); + mcs_2 = *((uint16_t *)new_cap->tx_he_mcs_map_80_80); + *((uint16_t *)he_cap->tx_he_mcs_map_80_80) = + HE_INTERSECT_MCS(mcs_1, mcs_2); + } +} + +void wma_print_he_cap(tDot11fIEhe_cap *he_cap) +{ + uint8_t chan_width; + struct ppet_hdr *hdr; + + if (!he_cap->present) { + WMA_LOGI(FL("HE Capabilities not present")); + return; + } + + WMA_LOGD(FL("HE Capabilities:")); + + /* HE MAC capabilities */ + WMA_LOGD("\tHTC-HE conrol: 0x%01x", he_cap->htc_he); + WMA_LOGD("\tTWT Requestor support: 0x%01x", he_cap->twt_request); + WMA_LOGD("\tTWT Responder support: 0x%01x", he_cap->twt_responder); + WMA_LOGD("\tFragmentation support: 0x%02x", he_cap->fragmentation); + WMA_LOGD("\tMax no.of frag MSDUs: 0x%03x", + he_cap->max_num_frag_msdu_amsdu_exp); + WMA_LOGD("\tMin. frag size: 0x%02x", he_cap->min_frag_size); + WMA_LOGD("\tTrigger MAC pad duration: 0x%02x", + he_cap->trigger_frm_mac_pad); + WMA_LOGD("\tMulti-TID aggr RX support: 0x%03x", + he_cap->multi_tid_aggr_rx_supp); + WMA_LOGD("\tLink adaptation: 0x%02x", he_cap->he_link_adaptation); + WMA_LOGD("\tAll ACK support: 0x%01x", he_cap->all_ack); + WMA_LOGD("\tTriggered resp. scheduling: 0x%01x", + he_cap->trigd_rsp_sched); + WMA_LOGD("\tA-Buff status report: 0x%01x", he_cap->a_bsr); + WMA_LOGD("\tBroadcast TWT support: 0x%01x", he_cap->broadcast_twt); + WMA_LOGD("\t32bit BA bitmap support: 0x%01x", he_cap->ba_32bit_bitmap); + WMA_LOGD("\tMU Cascading support: 0x%01x", he_cap->mu_cascade); + WMA_LOGD("\tACK enabled Multi-TID: 0x%01x", + he_cap->ack_enabled_multitid); + WMA_LOGD("\tOMI A-Control support: 0x%01x", he_cap->omi_a_ctrl); + WMA_LOGD("\tOFDMA RA support: 0x%01x", he_cap->ofdma_ra); + WMA_LOGD("\tMax A-MPDU Length: 0x%02x", he_cap->max_ampdu_len_exp_ext); + WMA_LOGD("\tA-MSDU Fragmentation: 0x%01x", he_cap->amsdu_frag); + WMA_LOGD("\tFlex. TWT sched support: 0x%01x", he_cap->flex_twt_sched); + WMA_LOGD("\tRx Ctrl frame to MBSS: 0x%01x", he_cap->rx_ctrl_frame); + WMA_LOGD("\tBSRP A-MPDU Aggregation: 0x%01x", he_cap->bsrp_ampdu_aggr); + WMA_LOGD("\tQuite Time Period support: 0x%01x", he_cap->qtp); + WMA_LOGD("\tA-BQR support: 0x%01x", he_cap->a_bqr); + WMA_LOGD("\tSR Responder: 0x%01x", he_cap->spatial_reuse_param_rspder); + WMA_LOGD("\tndp feedback supp: 0x%01x", he_cap->ndp_feedback_supp); + WMA_LOGD("\tops supp: 0x%01x", he_cap->ops_supp); + WMA_LOGD("\tamsdu in ampdu: 0x%01x", he_cap->amsdu_in_ampdu); + WMA_LOGD("\tMulti-TID aggr Tx support: 0x%03x", + he_cap->multi_tid_aggr_tx_supp); + WMA_LOGD("\tHE sub ch sel tx supp: 0x%01x", + he_cap->he_sub_ch_sel_tx_supp); + WMA_LOGD("\tUL 2x996 tone RU supp: 0x%01x", + he_cap->ul_2x996_tone_ru_supp); + WMA_LOGD("\tOM ctrl UL MU data dis rx supp: 0x%01x", + he_cap->om_ctrl_ul_mu_data_dis_rx); + WMA_LOGD("\tHE dynamic SMPS supp: 0x%01x", + he_cap->he_dynamic_smps); + WMA_LOGD("\tPunctured sounding supp: 0x%01x", + he_cap->punctured_sounding_supp); + WMA_LOGD("\tHT VHT Trigger frame Rx supp: 0x%01x", + he_cap->ht_vht_trg_frm_rx_supp); + + /* HE PHY capabilities */ + chan_width = HE_CH_WIDTH_COMBINE(he_cap->chan_width_0, + he_cap->chan_width_1, he_cap->chan_width_2, + he_cap->chan_width_3, he_cap->chan_width_4, + he_cap->chan_width_5, he_cap->chan_width_6); + + WMA_LOGD("\tChannel width support: 0x%07x", chan_width); + WMA_LOGD("\tPreamble puncturing Rx: 0x%04x", + he_cap->rx_pream_puncturing); + WMA_LOGD("\tClass of device: 0x%01x", he_cap->device_class); + WMA_LOGD("\tLDPC coding support: 0x%01x", he_cap->ldpc_coding); + WMA_LOGD("\tLTF and GI for HE PPDUs: 0x%02x", + he_cap->he_1x_ltf_800_gi_ppdu); + WMA_LOGD("\tMidamble Tx Rx MAX NSTS: 0x%02x", + he_cap->midamble_tx_rx_max_nsts); + WMA_LOGD("\tLTF and GI for NDP: 0x%02x", he_cap->he_4x_ltf_3200_gi_ndp); + WMA_LOGD("\tTB PPDU STBC Tx support <= 80M: 0x%01x", + he_cap->tb_ppdu_tx_stbc_lt_80mhz); + WMA_LOGD("\tSTBC Rx support <= 80M: 0x%01x", he_cap->rx_stbc_lt_80mhz); + WMA_LOGD("\tDoppler support: 0x%02x", he_cap->doppler); + WMA_LOGD("\tUL MU: 0x%02x", he_cap->ul_mu); + WMA_LOGD("\tDCM encoding Tx: 0x%03x", he_cap->dcm_enc_tx); + WMA_LOGD("\tDCM encoding Rx: 0x%03x", he_cap->dcm_enc_rx); + WMA_LOGD("\tHE MU PPDU payload support: 0x%01x", he_cap->ul_he_mu); + WMA_LOGD("\tSU Beamformer: 0x%01x", he_cap->su_beamformer); + WMA_LOGD("\tSU Beamformee: 0x%01x", he_cap->su_beamformee); + WMA_LOGD("\tMU Beamformer: 0x%01x", he_cap->mu_beamformer); + WMA_LOGD("\tBeamformee STS for <= 80Mhz: 0x%03x", + he_cap->bfee_sts_lt_80); + WMA_LOGD("\tBeamformee STS for > 80Mhz: 0x%03x", + he_cap->bfee_sts_gt_80); + WMA_LOGD("\tNo. of sounding dim <= 80Mhz: 0x%03x", + he_cap->num_sounding_lt_80); + WMA_LOGD("\tNo. of sounding dim > 80Mhz: 0x%03x", + he_cap->num_sounding_gt_80); + WMA_LOGD("\tNg=16 for SU feedback support: 0x%01x", + he_cap->su_feedback_tone16); + WMA_LOGD("\tNg=16 for MU feedback support: 0x%01x", + he_cap->mu_feedback_tone16); + WMA_LOGD("\tCodebook size for SU: 0x%01x", he_cap->codebook_su); + WMA_LOGD("\tCodebook size for MU: 0x%01x ", he_cap->codebook_mu); + WMA_LOGD("\tBeamforming trigger w/ Trigger: 0x%01x", + he_cap->beamforming_feedback); + WMA_LOGD("\tHE ER SU PPDU payload: 0x%01x", he_cap->he_er_su_ppdu); + WMA_LOGD("\tDL MUMIMO on partial BW: 0x%01x", + he_cap->dl_mu_mimo_part_bw); + WMA_LOGD("\tPPET present: 0x%01x", he_cap->ppet_present); + WMA_LOGD("\tSRP based SR-support: 0x%01x", he_cap->srp); + WMA_LOGD("\tPower boost factor: 0x%01x", he_cap->power_boost); + WMA_LOGD("\t4x HE LTF support: 0x%01x", he_cap->he_ltf_800_gi_4x); + + WMA_LOGD("\tMax NC: 0x%01x", he_cap->max_nc); + WMA_LOGD("\tTB PPDU stbc Tx gt 80mhz: 0x%01x", + he_cap->tb_ppdu_tx_stbc_gt_80mhz); + WMA_LOGD("\tstbc Rx gt 80mhz: 0x%01x", he_cap->rx_stbc_gt_80mhz); + WMA_LOGD("\ter_he_ltf_800_gi_4x: 0x%01x", he_cap->er_he_ltf_800_gi_4x); + WMA_LOGD("\the_ppdu_20_in_40Mhz_2G: 0x%01x", + he_cap->he_ppdu_20_in_40Mhz_2G); + WMA_LOGD("\the_ppdu_20_in_160_80p80Mhz: 0x%01x", + he_cap->he_ppdu_20_in_160_80p80Mhz); + WMA_LOGD("\the_ppdu_80_in_160_80p80Mhz: 0x%01x", + he_cap->he_ppdu_80_in_160_80p80Mhz); + WMA_LOGD("\ter_1x_he_ltf_gi: 0x%01x", + he_cap->er_1x_he_ltf_gi); + WMA_LOGD("\tmidamble_tx_rx_1x_he_ltf: 0x%01x", + he_cap->midamble_tx_rx_1x_he_ltf); + WMA_LOGD("\tDCM max BW: 0x%02x", + he_cap->dcm_max_bw); + WMA_LOGD("\tlonger_than_16_he_sigb_ofdm_sym: 0x%01x", + he_cap->longer_than_16_he_sigb_ofdm_sym); + WMA_LOGD("\tnon_trig_cqi_feedback: 0x%01x", + he_cap->non_trig_cqi_feedback); + WMA_LOGD("\ttx_1024_qam_lt_242_tone_ru: 0x%01x", + he_cap->tx_1024_qam_lt_242_tone_ru); + WMA_LOGD("\trx_1024_qam_lt_242_tone_ru: 0x%01x", + he_cap->rx_1024_qam_lt_242_tone_ru); + WMA_LOGD("\trx_full_bw_su_he_mu_compress_sigb: 0x%01x", + he_cap->rx_full_bw_su_he_mu_compress_sigb); + WMA_LOGD("\trx_full_bw_su_he_mu_non_cmpr_sigb: 0x%01x", + he_cap->rx_full_bw_su_he_mu_non_cmpr_sigb); + WMA_LOGD("\tRx MCS MAP for BW <= 80 MHz: 0x%x", + he_cap->rx_he_mcs_map_lt_80); + WMA_LOGD("\tTx MCS MAP for BW <= 80 MHz: 0x%x", + he_cap->tx_he_mcs_map_lt_80); + WMA_LOGD("\tRx MCS MAP for BW = 160 MHz: 0x%x", + *((uint16_t *)he_cap->rx_he_mcs_map_160)); + WMA_LOGD("\tTx MCS MAP for BW = 160 MHz: 0x%x", + *((uint16_t *)he_cap->tx_he_mcs_map_160)); + WMA_LOGD("\tRx MCS MAP for BW = 80 + 80 MHz: 0x%x", + *((uint16_t *)he_cap->rx_he_mcs_map_80_80)); + WMA_LOGD("\tTx MCS MAP for BW = 80 + 80 MHz: 0x%x", + *((uint16_t *)he_cap->tx_he_mcs_map_80_80)); + + hdr = (struct ppet_hdr *)&he_cap->ppet.ppe_threshold.ppe_th[0]; + /* HE PPET */ + WMA_LOGD("\tNSS: %d", hdr->nss + 1); + WMA_LOGD("\tRU Index mask: 0x%04x", hdr->ru_idx_mask); + WMA_LOGD("\tnum_ppet: %d", he_cap->ppet.ppe_threshold.num_ppe_th); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, + he_cap->ppet.ppe_threshold.ppe_th, + he_cap->ppet.ppe_threshold.num_ppe_th); +} + +void wma_print_he_ppet(void *he_ppet) +{ + int numss, ru_count, ru_bit_mask, i, j; + struct wmi_host_ppe_threshold *ppet = he_ppet; + + if (!ppet) { + WMA_LOGI(FL("PPET is NULL")); + return; + } + + numss = ppet->numss_m1 + 1; + ru_bit_mask = ppet->ru_bit_mask; + + WMA_LOGD(FL("HE PPET: ru_idx_mask: %04x"), ru_bit_mask); + for (ru_count = 0; ru_bit_mask; ru_bit_mask >>= 1) + if (ru_bit_mask & 0x1) + ru_count++; + + if (ru_count > 0) { + WMA_LOGD(FL("PPET has following RU INDEX,")); + if (ppet->ru_bit_mask & HE_RU_ALLOC_INDX0_MASK) + WMA_LOGD("\tRU ALLOCATION INDEX 0"); + if (ppet->ru_bit_mask & HE_RU_ALLOC_INDX1_MASK) + WMA_LOGD("\tRU ALLOCATION INDEX 1"); + if (ppet->ru_bit_mask & HE_RU_ALLOC_INDX2_MASK) + WMA_LOGD("\tRU ALLOCATION INDEX 2"); + if (ppet->ru_bit_mask & HE_RU_ALLOC_INDX3_MASK) + WMA_LOGD("\tRU ALLOCATION INDEX 3"); + } + + WMA_LOGD(FL("HE PPET: nss: %d, ru_count: %d"), numss, ru_count); + + for (i = 0; i < numss; i++) { + WMA_LOGD("PPET for NSS[%d]", i); + for (j = 1; j <= ru_count; j++) { + WMA_LOGD("\tNSS[%d],RU[%d]: PPET16: %02x PPET8: %02x", + i, j, + WMI_GET_PPET16(ppet->ppet16_ppet8_ru3_ru0, j, i), + WMI_GET_PPET8(ppet->ppet16_ppet8_ru3_ru0, j, i)); + } + } + +} + +void wma_print_he_phy_cap(uint32_t *phy_cap) +{ + WMA_LOGD(FL("HE PHY Capabilities:")); + + WMA_LOGD("\tChannel width support: 0x%07x", + WMI_HECAP_PHY_CBW_GET(phy_cap)); + WMA_LOGD("\tPreamble puncturing Rx: 0x%04x", + WMI_HECAP_PHY_PREAMBLEPUNCRX_GET(phy_cap)); + WMA_LOGD("\tClass of device: 0x%01x", WMI_HECAP_PHY_COD_GET(phy_cap)); + WMA_LOGD("\tLDPC coding support: 0x%01x", + WMI_HECAP_PHY_LDPC_GET(phy_cap)); + WMA_LOGD("\tLTF and GI for HE PPDUs: 0x%02x", + WMI_HECAP_PHY_LTFGIFORHE_GET(phy_cap)); + WMA_LOGD("\tLTF and GI for NDP: 0x%02x", + WMI_HECAP_PHY_LTFGIFORNDP_GET(phy_cap)); + WMA_LOGD("\tSTBC Tx & Rx support (BW <= 80Mhz): 0x%02x", + (WMI_HECAP_PHY_RXSTBC_GET(phy_cap) << 1) | + WMI_HECAP_PHY_TXSTBC_GET(phy_cap)); + WMA_LOGD("\tDoppler support: 0x%02x", + (WMI_HECAP_PHY_RXDOPPLER_GET(phy_cap) << 1) | + WMI_HECAP_PHY_TXDOPPLER(phy_cap)); + WMA_LOGD("\tUL MU (Full BW): 0x%01x", + WMI_HECAP_PHY_UL_MU_MIMO_GET(phy_cap)); + WMA_LOGD("\tUL MU (Partial BW): 0x%01x", + WMI_HECAP_PHY_ULMUMIMOOFDMA_GET(phy_cap)); + WMA_LOGD("\tDCM encoding Tx: 0x%03x", WMI_HECAP_PHY_DCMTX_GET(phy_cap)); + WMA_LOGD("\tDCM encoding Rx: 0x%03x", WMI_HECAP_PHY_DCMRX_GET(phy_cap)); + WMA_LOGD("\tHE MU PPDU payload support: 0x%01x", + WMI_HECAP_PHY_ULHEMU_GET(phy_cap)); + WMA_LOGD("\tSU Beamformer: 0x%01x", WMI_HECAP_PHY_SUBFMR_GET(phy_cap)); + WMA_LOGD("\tSU Beamformee: 0x%01x", WMI_HECAP_PHY_SUBFME_GET(phy_cap)); + WMA_LOGD("\tMU Beamformer: 0x%01x", WMI_HECAP_PHY_MUBFMR_GET(phy_cap)); + WMA_LOGD("\tBeamformee STS for <= 80Mhz: 0x%03x", + WMI_HECAP_PHY_BFMESTSLT80MHZ_GET(phy_cap)); + WMA_LOGD("\tNSTS total for <= 80Mhz: 0x%03x", + WMI_HECAP_PHY_NSTSLT80MHZ_GET(phy_cap)); + WMA_LOGD("\tBeamformee STS for > 80Mhz: 0x%03x", + WMI_HECAP_PHY_BFMESTSGT80MHZ_GET(phy_cap)); + WMA_LOGD("\tNSTS total for > 80Mhz: 0x%03x", + WMI_HECAP_PHY_NSTSGT80MHZ_GET(phy_cap)); + WMA_LOGD("\tNo. of sounding dim <= 80Mhz: 0x%03x", + WMI_HECAP_PHY_NUMSOUNDLT80MHZ_GET(phy_cap)); + WMA_LOGD("\tNo. of sounding dim > 80Mhz: 0x%03x", + WMI_HECAP_PHY_NUMSOUNDGT80MHZ_GET(phy_cap)); + WMA_LOGD("\tNg=16 for SU feedback support: 0x%01x", + WMI_HECAP_PHY_NG16SUFEEDBACKLT80_GET(phy_cap)); + WMA_LOGD("\tNg=16 for MU feedback support: 0x%01x", + WMI_HECAP_PHY_NG16MUFEEDBACKGT80_GET(phy_cap)); + WMA_LOGD("\tCodebook size for SU: 0x%01x", + WMI_HECAP_PHY_CODBK42SU_GET(phy_cap)); + WMA_LOGD("\tCodebook size for MU: 0x%01x ", + WMI_HECAP_PHY_CODBK75MU_GET(phy_cap)); + WMA_LOGD("\tBeamforming trigger w/ Trigger: 0x%01x", + WMI_HECAP_PHY_BFFEEDBACKTRIG_GET(phy_cap)); + WMA_LOGD("\tHE ER SU PPDU payload: 0x%01x", + WMI_HECAP_PHY_HEERSU_GET(phy_cap)); + WMA_LOGD("\tDL MUMIMO on partial BW: 0x%01x", + WMI_HECAP_PHY_DLMUMIMOPARTIALBW_GET(phy_cap)); + WMA_LOGD("\tPPET present: 0x%01x", + WMI_HECAP_PHY_PETHRESPRESENT_GET(phy_cap)); + WMA_LOGD("\tSRP based SR-support: 0x%01x", + WMI_HECAP_PHY_SRPSPRESENT_GET(phy_cap)); + WMA_LOGD("\tPower boost factor: 0x%01x", + WMI_HECAP_PHY_PWRBOOSTAR_GET(phy_cap)); + WMA_LOGD("\t4x HE LTF support: 0x%01x", + WMI_HECAP_PHY_4XLTFAND800NSECSGI_GET(phy_cap)); + WMA_LOGD("\tMax Nc supported: 0x%03x", + WMI_HECAP_PHY_MAXNC_GET(phy_cap)); + WMA_LOGD("\tSTBC Tx & Rx support (BW > 80Mhz): 0x%02x", + (WMI_HECAP_PHY_STBCRXGT80_GET(phy_cap) << 1) | + WMI_HECAP_PHY_STBCTXGT80_GET(phy_cap)); + WMA_LOGD("\tER 4x HE LTF support: 0x%01x", + WMI_HECAP_PHY_ERSU4X800NSECGI_GET(phy_cap)); +} + +void wma_print_he_mac_cap_w1(uint32_t mac_cap) +{ + WMA_LOGD(FL("HE MAC Capabilities:")); + + WMA_LOGD("\tHTC-HE conrol: 0x%01x", WMI_HECAP_MAC_HECTRL_GET(mac_cap)); + WMA_LOGD("\tTWT Requestor support: 0x%01x", + WMI_HECAP_MAC_TWTREQ_GET(mac_cap)); + WMA_LOGD("\tTWT Responder support: 0x%01x", + WMI_HECAP_MAC_TWTRSP_GET(mac_cap)); + WMA_LOGD("\tFragmentation support: 0x%02x", + WMI_HECAP_MAC_HEFRAG_GET(mac_cap)); + WMA_LOGD("\tMax no.of frag MSDUs: 0x%03x", + WMI_HECAP_MAC_MAXFRAGMSDU_GET(mac_cap)); + WMA_LOGD("\tMin. frag size: 0x%02x", + WMI_HECAP_MAC_MINFRAGSZ_GET(mac_cap)); + WMA_LOGD("\tTrigger MAC pad duration: 0x%02x", + WMI_HECAP_MAC_TRIGPADDUR_GET(mac_cap)); + WMA_LOGD("\tMulti-TID aggr Rx support: 0x%03x", + WMI_HECAP_MAC_MTID_RX_GET(mac_cap)); + WMA_LOGD("\tLink adaptation: 0x%02x", + WMI_HECAP_MAC_HELINK_ADPT_GET(mac_cap)); + WMA_LOGD("\tAll ACK support: 0x%01x", + WMI_HECAP_MAC_AACK_GET(mac_cap)); + WMA_LOGD("\tUL MU resp. scheduling: 0x%01x", + WMI_HECAP_MAC_TRS_GET(mac_cap)); + WMA_LOGD("\tA-Buff status report: 0x%01x", + WMI_HECAP_MAC_BSR_GET(mac_cap)); + WMA_LOGD("\tBroadcast TWT support: 0x%01x", + WMI_HECAP_MAC_BCSTTWT_GET(mac_cap)); + WMA_LOGD("\t32bit BA bitmap support: 0x%01x", + WMI_HECAP_MAC_32BITBA_GET(mac_cap)); + WMA_LOGD("\tMU Cascading support: 0x%01x", + WMI_HECAP_MAC_MUCASCADE_GET(mac_cap)); + WMA_LOGD("\tACK enabled Multi-TID: 0x%01x", + WMI_HECAP_MAC_ACKMTIDAMPDU_GET(mac_cap)); + WMA_LOGD("\tOMI A-Control support: 0x%01x", + WMI_HECAP_MAC_OMI_GET(mac_cap)); + WMA_LOGD("\tOFDMA RA support: 0x%01x", + WMI_HECAP_MAC_OFDMARA_GET(mac_cap)); + WMA_LOGD("\tMax A-MPDU Length: 0x%02x", + WMI_HECAP_MAC_MAXAMPDULEN_EXP_GET(mac_cap)); + WMA_LOGD("\tA-MSDU Fragmentation: 0x%01x", + WMI_HECAP_MAC_AMSDUFRAG_GET(mac_cap)); + WMA_LOGD("\tFlex. TWT sched support: 0x%01x", + WMI_HECAP_MAC_FLEXTWT_GET(mac_cap)); + WMA_LOGD("\tRx Ctrl frame to MBSS: 0x%01x", + WMI_HECAP_MAC_MBSS_GET(mac_cap)); +} + +void wma_print_he_mac_cap_w2(uint32_t mac_cap) +{ + WMA_LOGD("\tBSRP A-MPDU Aggregation: 0x%01x", + WMI_HECAP_MAC_BSRPAMPDU_GET(mac_cap)); + WMA_LOGD("\tQuite Time Period support: 0x%01x", + WMI_HECAP_MAC_QTP_GET(mac_cap)); + WMA_LOGD("\tA-BQR support: 0x%01x", WMI_HECAP_MAC_ABQR_GET(mac_cap)); + WMA_LOGD("\tSR Responder support: 0x%01x", + WMI_HECAP_MAC_SRPRESP_GET(mac_cap)); + WMA_LOGD("\tNDP Feedback Support: 0x%01x", + WMI_HECAP_MAC_NDPFDBKRPT_GET(mac_cap)); + WMA_LOGD("\tOPS Support: 0x%01x", + WMI_HECAP_MAC_OPS_GET(mac_cap)); + WMA_LOGD("\tMulti-TID aggr Tx support: 0x%03x", + WMI_HECAP_MAC_MTID_TX_GET(mac_cap)); + WMA_LOGD("\tSub Ch selective Tx support: 0x%01x", + WMI_HECAP_MAC_SUBCHANSELTX_GET(mac_cap)); + WMA_LOGD("\tUL 2x996 tone RU: 0x%01x", + WMI_HECAP_MAC_UL2X996RU_GET(mac_cap)); + WMA_LOGD("\tOM ctrl UL MU data disable Rx: 0x%01x", + WMI_HECAP_MAC_OMCULMUDDIS_GET(mac_cap)); +} + +void wma_update_target_ext_he_cap(struct target_psoc_info *tgt_hdl, + struct wma_tgt_cfg *tgt_cfg) +{ + tDot11fIEhe_cap *he_cap = &tgt_cfg->he_cap; + int i, num_hw_modes, total_mac_phy_cnt; + struct wlan_psoc_host_mac_phy_caps *mac_cap, *mac_phy_cap; + tDot11fIEhe_cap he_cap_mac; + tDot11fIEhe_cap tmp_he_cap = {0}; + bool is_5g_cap; + + qdf_mem_zero(&tgt_cfg->he_cap_2g, sizeof(tgt_cfg->he_cap_2g)); + qdf_mem_zero(&tgt_cfg->he_cap_5g, sizeof(tgt_cfg->he_cap_5g)); + num_hw_modes = target_psoc_get_num_hw_modes(tgt_hdl); + mac_phy_cap = target_psoc_get_mac_phy_cap(tgt_hdl); + total_mac_phy_cnt = target_psoc_get_total_mac_phy_cnt(tgt_hdl); + + if (!mac_phy_cap) { + WMA_LOGE(FL("Invalid MAC PHY capabilities handle")); + he_cap->present = false; + return; + } + + if (!num_hw_modes) { + WMA_LOGE(FL("No extended HE cap for current SOC")); + he_cap->present = false; + return; + } + + if (!tgt_cfg->services.en_11ax) { + WMA_LOGI(FL("Target does not support 11AX")); + he_cap->present = false; + return; + } + + for (i = 0; i < total_mac_phy_cnt; i++) { + qdf_mem_zero(&he_cap_mac, + sizeof(tDot11fIEhe_cap)); + mac_cap = &mac_phy_cap[i]; + is_5g_cap = false; + if (mac_cap->supported_bands & WLAN_2G_CAPABILITY) { + wma_convert_he_cap(&he_cap_mac, + mac_cap->he_cap_info_2G, + mac_cap->he_cap_phy_info_2G, + mac_cap->he_supp_mcs_2G, + mac_cap->tx_chain_mask_2G, + mac_cap->rx_chain_mask_2G); + WMA_LOGD(FL("2g phy: nss: %d, ru_idx_msk: %d"), + mac_cap->he_ppet2G.numss_m1, + mac_cap->he_ppet2G.ru_bit_mask); + wma_convert_he_ppet(tgt_cfg->ppet_2g, + (struct wmi_host_ppe_threshold *) + &mac_cap->he_ppet2G); + } + + if (he_cap_mac.present) { + wma_derive_ext_he_cap(&tmp_he_cap, + &he_cap_mac, + is_5g_cap); + wma_derive_ext_he_cap(&tgt_cfg->he_cap_2g, + &he_cap_mac, + is_5g_cap); + } + + qdf_mem_zero(&he_cap_mac, + sizeof(tDot11fIEhe_cap)); + if (mac_cap->supported_bands & WLAN_5G_CAPABILITY) { + wma_convert_he_cap(&he_cap_mac, + mac_cap->he_cap_info_5G, + mac_cap->he_cap_phy_info_5G, + mac_cap->he_supp_mcs_5G, + mac_cap->tx_chain_mask_5G, + mac_cap->rx_chain_mask_5G); + WMA_LOGD(FL("5g phy: nss: %d, ru_idx_msk: %d"), + mac_cap->he_ppet5G.numss_m1, + mac_cap->he_ppet5G.ru_bit_mask); + wma_convert_he_ppet(tgt_cfg->ppet_5g, + (struct wmi_host_ppe_threshold *) + &mac_cap->he_ppet5G); + is_5g_cap = true; + } + if (he_cap_mac.present) { + wma_derive_ext_he_cap(&tmp_he_cap, + &he_cap_mac, + is_5g_cap); + wma_derive_ext_he_cap(&tgt_cfg->he_cap_5g, + &he_cap_mac, + is_5g_cap); + } + } + + qdf_mem_copy(he_cap, &tmp_he_cap, sizeof(*he_cap)); + wma_print_he_cap(he_cap); +} + +void wma_he_update_tgt_services(struct wmi_unified *wmi_handle, + struct wma_tgt_services *cfg) +{ + if (wmi_service_enabled(wmi_handle, wmi_service_11ax)) { + cfg->en_11ax = true; + wma_set_fw_wlan_feat_caps(DOT11AX); + WMA_LOGI(FL("11ax is enabled")); + } else { + WMA_LOGI(FL("11ax is not enabled")); + } +} + +void wma_print_he_op(tDot11fIEhe_op *he_ops) +{ + WMA_LOGD(FL("bss_color %0x def_pe_dur %0x twt_req %0x txop_rts_thre %0x vht_oper %0x"), + he_ops->bss_color, he_ops->default_pe, + he_ops->twt_required, he_ops->txop_rts_threshold, + he_ops->vht_oper_present); + WMA_LOGD(FL("\tpart_bss_color: %0x, MBSSID AP: %0x, BSS color dis %0x"), + he_ops->partial_bss_col, he_ops->co_located_bss, + he_ops->bss_col_disabled); +} + +/** + * wma_parse_he_ppet() - Convert PPET stored in dot11f structure into FW + * structure. + * @rcvd_ppet: pointer to dot11f format PPET + * @peer_ppet: pointer peer_ppet to be sent in peer assoc + * + * This function converts the sequence of PPET stored in the host in OTA type + * structure into FW understandable structure to be sent as part of peer assoc + * command. + * + * Return: None + */ +static void wma_parse_he_ppet(int8_t *rcvd_ppet, + struct wmi_host_ppe_threshold *peer_ppet) +{ + struct ppet_hdr *hdr; + uint8_t num_ppet, mask, mask1, mask2; + uint32_t ppet1, ppet2, ppet; + uint8_t bits, pad, pad_bits, req_byte; + uint8_t byte_idx, start, i, j, parsed; + uint32_t *ppet_r = peer_ppet->ppet16_ppet8_ru3_ru0; + uint8_t nss, ru; + + hdr = (struct ppet_hdr *)rcvd_ppet; + nss = hdr->nss + 1; + mask = hdr->ru_idx_mask; + peer_ppet->numss_m1 = nss - 1; + peer_ppet->ru_bit_mask = mask; + + for (ru = 0; mask; mask >>= 1) { + if (mask & 0x1) + ru++; + } + + WMA_LOGD(FL("Rcvd nss=%d ru_idx_mask: %0x ru_count=%d"), + nss, hdr->ru_idx_mask, ru); + + /* each nss-ru pair have 2 PPET (PPET8/PPET16) */ + bits = HE_PPET_NSS_RU_LEN + (nss + ru) * (HE_PPET_SIZE * 2); + pad = bits % HE_BYTE_SIZE; + pad_bits = HE_BYTE_SIZE - pad; + req_byte = (bits + pad_bits) / HE_BYTE_SIZE; + + /* + * PPE Threshold Field Format + * +-----------+--------------+--------------------+-------------+ + * | NSS | RU idx mask | PPE Threshold info | Padding | + * +-----------+--------------+--------------------+-------------+ + * 3 4 1 + variable variable (bits) + * + * PPE Threshold Info field: + * number of NSS:n, number of RU: m + * +------------+-----------+-----+------------+-----------+-----+-----------+-----------+ + * | PPET16 for | PPET8 for | ... | PPET16 for | PPET8 for | ... | PET16 for | PPET8 for | + * | NSS1, RU1 | NSS1, RU1 | ... | NSS1, RUm | NSS1, RUm | ... | NSSn, RUm | NSSn, RUm | + * +------------+-----------+-----+------------+-----------+-----+-----------+-----------+ + * 3 3 ... 3 3 ... 3 3 + */ + + /* first bit of first PPET is in the last bit of first byte */ + parsed = HE_PPET_NSS_RU_LEN; + + /* + * refer wmi_ppe_threshold defn to understand how ppet is stored. + * Index of ppet array(ppet16_ppet8_ru3_ru0) is the NSS value. + * Each item in ppet16_ppet8_ru3_ru0 holds ppet for all the RUs. + */ + num_ppet = ru * 2; /* for each NSS */ + for (i = 0; i < nss; i++) { + for (j = 1; j <= num_ppet; j++) { + start = parsed + (i * (num_ppet * HE_PPET_SIZE)) + + (j-1) * HE_PPET_SIZE; + byte_idx = start / HE_BYTE_SIZE; + start = start % HE_BYTE_SIZE; + + if (start <= HE_BYTE_SIZE - HE_PPET_SIZE) { + mask = 0x07 << start; + ppet = (rcvd_ppet[byte_idx] & mask) >> start; + ppet_r[i] |= (ppet << (j - 1) * HE_PPET_SIZE); + } else { + mask1 = 0x07 << start; + ppet1 = (rcvd_ppet[byte_idx] & mask1) >> start; + mask2 = 0x07 >> (HE_BYTE_SIZE - start); + ppet2 = (rcvd_ppet[byte_idx + 1] & mask2) << + (HE_BYTE_SIZE - start); + ppet = ppet1 | ppet2; + ppet_r[i] |= (ppet << (j - 1) * HE_PPET_SIZE); + } + WMA_LOGD(FL("nss:%d ru:%d ppet_r:%0x"), i, j/2, + ppet_r[i]); + } + } +} + +void wma_populate_peer_he_cap(struct peer_assoc_params *peer, + tpAddStaParams params) +{ + tDot11fIEhe_cap *he_cap = ¶ms->he_config; + tDot11fIEhe_op *he_op = ¶ms->he_op; + uint32_t *phy_cap = peer->peer_he_cap_phyinfo; + uint32_t mac_cap[PSOC_HOST_MAX_MAC_SIZE] = {0}, he_ops = 0; + uint8_t temp, i, chan_width; + + if (params->he_capable) + peer->he_flag = 1; + else + return; + + /* HE MAC capabilities */ + WMI_HECAP_MAC_HECTRL_SET(mac_cap[0], he_cap->htc_he); + WMI_HECAP_MAC_TWTREQ_SET(mac_cap[0], he_cap->twt_request); + WMI_HECAP_MAC_TWTRSP_SET(mac_cap[0], he_cap->twt_responder); + WMI_HECAP_MAC_HEFRAG_SET(mac_cap[0], he_cap->fragmentation); + WMI_HECAP_MAC_MAXFRAGMSDU_SET(mac_cap[0], + he_cap->max_num_frag_msdu_amsdu_exp); + WMI_HECAP_MAC_MINFRAGSZ_SET(mac_cap[0], he_cap->min_frag_size); + WMI_HECAP_MAC_TRIGPADDUR_SET(mac_cap[0], he_cap->trigger_frm_mac_pad); + WMI_HECAP_MAC_ACKMTIDAMPDU_SET(mac_cap[0], + he_cap->multi_tid_aggr_rx_supp); + WMI_HECAP_MAC_HELKAD_SET(mac_cap[0], he_cap->he_link_adaptation); + WMI_HECAP_MAC_AACK_SET(mac_cap[0], he_cap->all_ack); + WMI_HECAP_MAC_TRS_SET(mac_cap[0], he_cap->trigd_rsp_sched); + WMI_HECAP_MAC_BSR_SET(mac_cap[0], he_cap->a_bsr); + WMI_HECAP_MAC_BCSTTWT_SET(mac_cap[0], he_cap->broadcast_twt); + WMI_HECAP_MAC_32BITBA_SET(mac_cap[0], he_cap->ba_32bit_bitmap); + WMI_HECAP_MAC_MUCASCADE_SET(mac_cap[0], he_cap->mu_cascade); + WMI_HECAP_MAC_ACKMTIDAMPDU_SET(mac_cap[0], + he_cap->ack_enabled_multitid); + WMI_HECAP_MAC_OMI_SET(mac_cap[0], he_cap->omi_a_ctrl); + WMI_HECAP_MAC_OFDMARA_SET(mac_cap[0], he_cap->ofdma_ra); + WMI_HECAP_MAC_MAXAMPDULEN_EXP_SET(mac_cap[0], + he_cap->max_ampdu_len_exp_ext); + WMI_HECAP_MAC_AMSDUFRAG_SET(mac_cap[0], he_cap->amsdu_frag); + WMI_HECAP_MAC_FLEXTWT_SET(mac_cap[0], he_cap->flex_twt_sched); + WMI_HECAP_MAC_MBSS_SET(mac_cap[0], he_cap->rx_ctrl_frame); + WMI_HECAP_MAC_BSRPAMPDU_SET(mac_cap[1], he_cap->bsrp_ampdu_aggr); + WMI_HECAP_MAC_QTP_SET(mac_cap[1], he_cap->qtp); + WMI_HECAP_MAC_ABQR_SET(mac_cap[1], he_cap->a_bqr); + WMI_HECAP_MAC_SRPRESP_SET(mac_cap[1], + he_cap->spatial_reuse_param_rspder); + WMI_HECAP_MAC_OPS_SET(mac_cap[1], he_cap->ops_supp); + WMI_HECAP_MAC_NDPFDBKRPT_SET(mac_cap[1], he_cap->ndp_feedback_supp); + WMI_HECAP_MAC_AMSDUINAMPDU_SET(mac_cap[1], he_cap->amsdu_in_ampdu); + WMI_HECAP_MAC_MTID_TX_SET(mac_cap[1], he_cap->multi_tid_aggr_tx_supp); + WMI_HECAP_MAC_SUBCHANSELTX_SET(mac_cap[1], + he_cap->he_sub_ch_sel_tx_supp); + WMI_HECAP_MAC_UL2X996RU_SET(mac_cap[1], he_cap->ul_2x996_tone_ru_supp); + WMI_HECAP_MAC_OMCULMUDDIS_SET(mac_cap[1], + he_cap->om_ctrl_ul_mu_data_dis_rx); + WMI_HECAP_MAC_DYNSMPWRSAVE_SET(mac_cap[1], he_cap->he_dynamic_smps); + WMI_HECAP_MAC_PUNCSOUNDING_SET(mac_cap[1], + he_cap->punctured_sounding_supp); + WMI_HECAP_MAC_HTVHTTRIGRX_SET(mac_cap[1], + he_cap->ht_vht_trg_frm_rx_supp); + qdf_mem_copy(peer->peer_he_cap_macinfo, mac_cap, sizeof(mac_cap)); + + /* HE PHY capabilities */ + chan_width = HE_CH_WIDTH_COMBINE(he_cap->chan_width_0, + he_cap->chan_width_1, he_cap->chan_width_2, + he_cap->chan_width_3, he_cap->chan_width_4, + he_cap->chan_width_5, he_cap->chan_width_6); + WMI_HECAP_PHY_CBW_SET(phy_cap, chan_width); + WMI_HECAP_PHY_PREAMBLEPUNCRX_SET(phy_cap, he_cap->rx_pream_puncturing); + WMI_HECAP_PHY_COD_SET(phy_cap, he_cap->device_class); + WMI_HECAP_PHY_LDPC_SET(phy_cap, he_cap->ldpc_coding); + WMI_HECAP_PHY_LTFGIFORHE_SET(phy_cap, he_cap->he_1x_ltf_800_gi_ppdu); + WMI_HECAP_PHY_MIDAMBLETXRXMAXNSTS_SET(phy_cap, + he_cap->midamble_tx_rx_max_nsts); + WMI_HECAP_PHY_LTFGIFORNDP_SET(phy_cap, he_cap->he_4x_ltf_3200_gi_ndp); + + WMI_HECAP_PHY_RXSTBC_SET(phy_cap, he_cap->rx_stbc_lt_80mhz); + WMI_HECAP_PHY_TXSTBC_SET(phy_cap, he_cap->tb_ppdu_tx_stbc_lt_80mhz); + + temp = he_cap->doppler & 0x1; + WMI_HECAP_PHY_RXDOPPLER_SET(phy_cap, temp); + temp = he_cap->doppler >> 0x1; + WMI_HECAP_PHY_TXDOPPLER_SET(phy_cap, temp); + + temp = he_cap->ul_mu & 0x1; + WMI_HECAP_PHY_UL_MU_MIMO_SET(phy_cap, temp); + temp = he_cap->ul_mu >> 0x1; + WMI_HECAP_PHY_ULMUMIMOOFDMA_SET(phy_cap, temp); + + WMI_HECAP_PHY_DCMTX_SET(phy_cap, he_cap->dcm_enc_tx); + WMI_HECAP_PHY_DCMRX_SET(phy_cap, he_cap->dcm_enc_rx); + WMI_HECAP_PHY_ULHEMU_SET(phy_cap, he_cap->ul_he_mu); + WMI_HECAP_PHY_SUBFMR_SET(phy_cap, he_cap->su_beamformer); + WMI_HECAP_PHY_SUBFME_SET(phy_cap, he_cap->su_beamformee); + WMI_HECAP_PHY_MUBFMR_SET(phy_cap, he_cap->mu_beamformer); + WMI_HECAP_PHY_BFMESTSLT80MHZ_SET(phy_cap, he_cap->bfee_sts_lt_80); + WMI_HECAP_PHY_BFMESTSGT80MHZ_SET(phy_cap, he_cap->bfee_sts_gt_80); + WMI_HECAP_PHY_NUMSOUNDLT80MHZ_SET(phy_cap, he_cap->num_sounding_lt_80); + WMI_HECAP_PHY_NUMSOUNDGT80MHZ_SET(phy_cap, he_cap->num_sounding_gt_80); + WMI_HECAP_PHY_NG16SUFEEDBACKLT80_SET(phy_cap, + he_cap->su_feedback_tone16); + WMI_HECAP_PHY_NG16MUFEEDBACKGT80_SET(phy_cap, + he_cap->mu_feedback_tone16); + WMI_HECAP_PHY_CODBK42SU_SET(phy_cap, he_cap->codebook_su); + WMI_HECAP_PHY_CODBK75MU_SET(phy_cap, he_cap->codebook_mu); + WMI_HECAP_PHY_BFFEEDBACKTRIG_SET(phy_cap, he_cap->beamforming_feedback); + WMI_HECAP_PHY_HEERSU_SET(phy_cap, he_cap->he_er_su_ppdu); + WMI_HECAP_PHY_DLMUMIMOPARTIALBW_SET(phy_cap, + he_cap->dl_mu_mimo_part_bw); + WMI_HECAP_PHY_PETHRESPRESENT_SET(phy_cap, he_cap->ppet_present); + WMI_HECAP_PHY_SRPPRESENT_SET(phy_cap, he_cap->srp); + WMI_HECAP_PHY_PWRBOOSTAR_SET(phy_cap, he_cap->power_boost); + WMI_HECAP_PHY_4XLTFAND800NSECSGI_SET(phy_cap, he_cap->he_ltf_800_gi_4x); + + WMI_HECAP_PHY_MAXNC_SET(phy_cap, he_cap->max_nc); + + WMI_HECAP_PHY_STBCRXGT80_SET(phy_cap, he_cap->rx_stbc_gt_80mhz); + WMI_HECAP_PHY_STBCTXGT80_SET(phy_cap, he_cap->tb_ppdu_tx_stbc_gt_80mhz); + + WMI_HECAP_PHY_ERSU4X800NSECGI_SET(phy_cap, he_cap->er_he_ltf_800_gi_4x); + WMI_HECAP_PHY_HEPPDU20IN40MHZ2G_SET(phy_cap, + he_cap->he_ppdu_20_in_40Mhz_2G); + WMI_HECAP_PHY_HEPPDU20IN160OR80P80MHZ_SET(phy_cap, + he_cap->he_ppdu_20_in_160_80p80Mhz); + WMI_HECAP_PHY_HEPPDU80IN160OR80P80MHZ_SET(phy_cap, + he_cap->he_ppdu_80_in_160_80p80Mhz); + WMI_HECAP_PHY_ERSU1X800NSECGI_SET(phy_cap, he_cap->er_1x_he_ltf_gi); + WMI_HECAP_PHY_MIDAMBLETXRX2XAND1XHELTF_SET(phy_cap, + he_cap-> + midamble_tx_rx_1x_he_ltf); + WMI_HECAP_PHY_DCMMAXBW_SET(phy_cap, he_cap->dcm_max_bw); + WMI_HECAP_PHY_LNG16SIGBSYMBSUPRT_SET(phy_cap, + he_cap-> + longer_than_16_he_sigb_ofdm_sym); + WMI_HECAP_PHY_NONTRIGCQIFEEDBK_SET(phy_cap, + he_cap->non_trig_cqi_feedback); + WMI_HECAP_PHY_TX1024QAM242RUSUPRT_SET(phy_cap, + he_cap-> + tx_1024_qam_lt_242_tone_ru); + WMI_HECAP_PHY_RX1024QAM242RUSUPRT_SET(phy_cap, + he_cap-> + rx_1024_qam_lt_242_tone_ru); + WMI_HECAP_PHY_RXFULBWSUWCMPRSSIGB_SET(phy_cap, + he_cap->rx_full_bw_su_he_mu_compress_sigb); + WMI_HECAP_PHY_RXFULBWSUWNONCMPRSSIGB_SET(phy_cap, + he_cap->rx_full_bw_su_he_mu_non_cmpr_sigb); + + /* as per 11ax draft 1.4 */ + peer->peer_he_mcs_count = 1; + peer->peer_he_rx_mcs_set[0] = + params->supportedRates.rx_he_mcs_map_lt_80; + peer->peer_he_tx_mcs_set[0] = + params->supportedRates.tx_he_mcs_map_lt_80; + + if (params->ch_width > CH_WIDTH_80MHZ) { + peer->peer_he_mcs_count = WMI_HOST_MAX_HE_RATE_SET; + peer->peer_he_rx_mcs_set[1] = + params->supportedRates.rx_he_mcs_map_160; + peer->peer_he_tx_mcs_set[1] = + params->supportedRates.tx_he_mcs_map_160; + peer->peer_he_rx_mcs_set[2] = + params->supportedRates.rx_he_mcs_map_80_80; + peer->peer_he_tx_mcs_set[2] = + params->supportedRates.tx_he_mcs_map_80_80; + } + +#define HE2x2MCSMASK 0xc + + peer->peer_nss = ((params->supportedRates.rx_he_mcs_map_lt_80 & + HE2x2MCSMASK) == HE2x2MCSMASK) ? 1 : 2; + for (i = 0; i < peer->peer_he_mcs_count; i++) + WMA_LOGD(FL("[HE - MCS Map: %d] rx_mcs: 0x%x, tx_mcs: 0x%x"), i, + peer->peer_he_rx_mcs_set[i], + peer->peer_he_tx_mcs_set[i]); + + WMI_HEOPS_COLOR_SET(he_ops, he_op->bss_color); + WMI_HEOPS_DEFPE_SET(he_ops, he_op->default_pe); + WMI_HEOPS_TWT_SET(he_ops, he_op->twt_required); + WMI_HEOPS_RTSTHLD_SET(he_ops, he_op->txop_rts_threshold); + WMI_HEOPS_ERSUDIS_SET(he_ops, he_op->er_su_disable); + WMI_HEOPS_PARTBSSCOLOR_SET(he_ops, he_op->partial_bss_col); + WMI_HEOPS_BSSCOLORDISABLE_SET(he_ops, he_op->bss_col_disabled); + peer->peer_he_ops = he_ops; + + wma_parse_he_ppet(he_cap->ppet.ppe_threshold.ppe_th, &peer->peer_ppet); + + wma_print_he_cap(he_cap); + WMA_LOGD(FL("Peer HE Capabilities:")); + wma_print_he_phy_cap(phy_cap); + wma_print_he_mac_cap_w1(mac_cap[0]); + wma_print_he_mac_cap_w2(mac_cap[1]); + wma_print_he_ppet(&peer->peer_ppet); + + if (params->he_6ghz_band_caps.present) { + peer->peer_he_caps_6ghz = + (params->he_6ghz_band_caps.min_mpdu_start_spacing << + HE_6G_MIN_MPDU_START_SAPCE_BIT_POS) | + (params->he_6ghz_band_caps.max_ampdu_len_exp << + HE_6G_MAX_AMPDU_LEN_EXP_BIT_POS) | + (params->he_6ghz_band_caps.max_mpdu_len << + HE_6G_MAX_MPDU_LEN_BIT_POS) | + (params->he_6ghz_band_caps.sm_pow_save << + HE_6G_SMPS_BIT_POS) | + (params->he_6ghz_band_caps.rd_responder << + HE_6G_RD_RESP_BIT_POS) | + (params->he_6ghz_band_caps.rx_ant_pattern_consistency << + HE_6G_RX_ANT_PATTERN_BIT_POS) | + (params->he_6ghz_band_caps.tx_ant_pattern_consistency << + HE_6G_TX_ANT_PATTERN_BIT_POS); + WMA_LOGD(FL("HE 6GHz band caps: %0x"), peer->peer_he_caps_6ghz); + } else { + WMA_LOGD(FL("HE 6GHz band caps not present")); + peer->peer_he_caps_6ghz = 0; + } +} + +void wma_update_vdev_he_ops(uint32_t *he_ops, tDot11fIEhe_op *he_op) +{ + WMI_HEOPS_COLOR_SET(*he_ops, he_op->bss_color); + WMI_HEOPS_DEFPE_SET(*he_ops, he_op->default_pe); + WMI_HEOPS_TWT_SET(*he_ops, he_op->twt_required); + WMI_HEOPS_RTSTHLD_SET(*he_ops, he_op->txop_rts_threshold); + WMI_HEOPS_PARTBSSCOLOR_SET(*he_ops, he_op->partial_bss_col); + WMI_HEOPS_BSSCOLORDISABLE_SET(*he_ops, he_op->bss_col_disabled); +} + +void wma_vdev_set_he_bss_params(tp_wma_handle wma, uint8_t vdev_id, + struct vdev_mlme_he_ops_info *he_info) +{ + QDF_STATUS ret; + + if (!he_info->he_ops) + return; + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_HEOPS_0_31, he_info->he_ops); + + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE(FL("Failed to set HE OPs")); +} + +void wma_vdev_set_he_config(tp_wma_handle wma, uint8_t vdev_id, + struct bss_params *add_bss) +{ + QDF_STATUS ret; + int8_t pd_min, pd_max, sec_ch_ed, tx_pwr; + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_OBSSPD, add_bss->he_sta_obsspd); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE(FL("Failed to set HE Config")); + pd_min = add_bss->he_sta_obsspd & 0xff, + pd_max = (add_bss->he_sta_obsspd & 0xff00) >> 8, + sec_ch_ed = (add_bss->he_sta_obsspd & 0xff0000) >> 16, + tx_pwr = (add_bss->he_sta_obsspd & 0xff000000) >> 24; + WMA_LOGD(FL("HE_STA_OBSSPD: PD_MIN: %d PD_MAX: %d SEC_CH_ED: %d TX_PWR: %d"), + pd_min, pd_max, sec_ch_ed, tx_pwr); +} + +QDF_STATUS wma_update_he_ops_ie(tp_wma_handle wma, uint8_t vdev_id, + tDot11fIEhe_op *he_op) +{ + QDF_STATUS ret; + uint32_t dword_he_op = 0; + + if (!wma) { + WMA_LOGE(FL("wrong wma_handle....")); + return QDF_STATUS_E_FAILURE; + } + + WMI_HEOPS_COLOR_SET(dword_he_op, he_op->bss_color); + WMI_HEOPS_DEFPE_SET(dword_he_op, he_op->default_pe); + WMI_HEOPS_TWT_SET(dword_he_op, he_op->twt_required); + WMI_HEOPS_RTSTHLD_SET(dword_he_op, he_op->txop_rts_threshold); + WMI_HEOPS_PARTBSSCOLOR_SET(dword_he_op, he_op->partial_bss_col); + WMI_HEOPS_BSSCOLORDISABLE_SET(dword_he_op, he_op->bss_col_disabled); + + WMA_LOGD("vdev_id: %d HE_OPs: 0x%x", vdev_id, dword_he_op); + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_HEOPS_0_31, dword_he_op); + + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE(FL("Failed to set HE OPs")); + + return ret; +} + +void wma_set_he_txbf_cfg(struct mac_context *mac, uint8_t vdev_id) +{ + wma_set_he_txbf_params(vdev_id, + mac->mlme_cfg->he_caps.dot11_he_cap.su_beamformer, + mac->mlme_cfg->he_caps.dot11_he_cap.su_beamformee, + mac->mlme_cfg->he_caps.dot11_he_cap.mu_beamformer); +} + +void wma_set_he_txbf_params(uint8_t vdev_id, bool su_bfer, + bool su_bfee, bool mu_bfer) +{ + uint32_t hemu_mode; + QDF_STATUS status; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + wma_err("Invalid WMA handle"); + return; + } + + hemu_mode = DOT11AX_HEMU_MODE; + hemu_mode |= ((su_bfer << HE_SUBFER) | (su_bfee << HE_SUBFEE) | + (mu_bfer << HE_MUBFER) | (su_bfee << HE_MUBFEE)); + /* + * Enable / disable trigger access for a AP vdev's peers. + * For a STA mode vdev this will enable/disable triggered + * access and enable/disable Multi User mode of operation. + * A value of 0 in a given bit disables corresponding mode. + * bit | hemu mode + * --------------- + * 0 | HE SUBFEE + * 1 | HE SUBFER + * 2 | HE MUBFEE + * 3 | HE MUBFER + * 4 | DL OFDMA, for AP its DL Tx OFDMA for Sta its Rx OFDMA + * 5 | UL OFDMA, for AP its Tx OFDMA trigger for Sta its + * Rx OFDMA trigger receive & UL response + * 6 | UL MUMIMO + */ + status = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_SET_HEMU_MODE, hemu_mode); + WMA_LOGD("set HEMU_MODE (hemu_mode = 0x%x)", hemu_mode); + + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to set HEMU_MODE(status = %d)", status); +} + +QDF_STATUS wma_get_he_capabilities(struct he_capability *he_cap) +{ + tp_wma_handle wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + WMA_LOGE(FL("Invalid WMA handle")); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_copy(he_cap->phy_cap, + &wma_handle->he_cap.phy_cap, + WMI_MAX_HECAP_PHY_SIZE); + he_cap->mac_cap = wma_handle->he_cap.mac_cap; + he_cap->mcs = wma_handle->he_cap.mcs; + + he_cap->ppet.numss_m1 = wma_handle->he_cap.ppet.numss_m1; + he_cap->ppet.ru_bit_mask = wma_handle->he_cap.ppet.ru_bit_mask; + qdf_mem_copy(&he_cap->ppet.ppet16_ppet8_ru3_ru0, + &wma_handle->he_cap.ppet.ppet16_ppet8_ru3_ru0, + WMI_MAX_NUM_SS); + + return QDF_STATUS_SUCCESS; +} + +void wma_set_he_vdev_param(struct wma_txrx_node *intr, WMI_VDEV_PARAM param_id, + uint32_t value) +{ + switch (param_id) { + case WMI_VDEV_PARAM_HE_DCM: + intr->config.dcm = value; + break; + case WMI_VDEV_PARAM_HE_RANGE_EXT: + intr->config.range_ext = value; + break; + default: + WMA_LOGE(FL("Unhandled HE vdev param: %0x"), param_id); + break; + } +} + +uint32_t wma_get_he_vdev_param(struct wma_txrx_node *intr, + WMI_VDEV_PARAM param_id) +{ + switch (param_id) { + case WMI_VDEV_PARAM_HE_DCM: + return intr->config.dcm; + case WMI_VDEV_PARAM_HE_RANGE_EXT: + return intr->config.range_ext; + default: + WMA_LOGE(FL("Unhandled HE vdev param: %0x"), param_id); + break; + } + return 0; +} diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_main.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_main.c new file mode 100644 index 0000000000000000000000000000000000000000..db1ee1bfa8a889249efbf02f5528eec1f9edf8ab --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_main.c @@ -0,0 +1,9546 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_main.c + * + * This file contains wma initialization and FW exchange + * related functions. + */ + +/* Header files */ + +#include "wma.h" +#include "wma_api.h" +#include "cds_api.h" +#include "wmi_unified_api.h" +#include "wlan_qct_sys.h" +#include "wni_api.h" +#include "ani_global.h" +#include "wmi_unified.h" +#include "wni_cfg.h" +#if defined(CONFIG_HL_SUPPORT) +#include "wlan_tgt_def_config_hl.h" +#else +#include "wlan_tgt_def_config.h" +#endif +#include "qdf_nbuf.h" +#include "qdf_types.h" +#include "qdf_mem.h" +#include "wma_types.h" +#include "lim_api.h" +#include "lim_session_utils.h" + +#include "cds_utils.h" + +#if !defined(REMOVE_PKT_LOG) +#include "pktlog_ac.h" +#endif /* REMOVE_PKT_LOG */ + +#include "dbglog_host.h" +#include "csr_api.h" +#include "ol_fw.h" + +#include "wma_internal.h" + +#include "wma_ocb.h" +#include "wlan_policy_mgr_api.h" +#include "cdp_txrx_cfg.h" +#include "cdp_txrx_flow_ctrl_legacy.h" +#include "cdp_txrx_flow_ctrl_v2.h" +#include "cdp_txrx_ipa.h" +#include "cdp_txrx_misc.h" +#include "wma_fips_api.h" +#include "wma_nan_datapath.h" +#include "wma_fw_state.h" +#include "wlan_lmac_if_def.h" +#include "wlan_lmac_if_api.h" +#include "target_if.h" +#include "target_if_scan.h" +#include "wlan_global_lmac_if_api.h" +#include "target_if_pmo.h" +#include "wma_he.h" +#include "wlan_pmo_obj_mgmt_api.h" + +#include "wlan_reg_tgt_api.h" +#include "wlan_reg_services_api.h" +#include +#include +#include "wifi_pos_api.h" +#include "hif_main.h" +#include +#include +#include "init_event_handler.h" +#include "init_deinit_lmac.h" +#include "target_if_green_ap.h" +#include "service_ready_param.h" +#include "wlan_cp_stats_mc_ucfg_api.h" +#include "cfg_nan_api.h" +#include "wlan_mlme_api.h" +#include "wlan_mlme_ucfg_api.h" +#include "cfg_ucfg_api.h" +#include "init_cmd_api.h" +#include "nan_ucfg_api.h" +#include "wma_coex.h" +#include "target_if_vdev_mgr_rx_ops.h" +#include "wlan_tdls_cfg_api.h" +#include "wlan_policy_mgr_i.h" +#include "target_if_psoc_timer_tx_ops.h" + +#ifdef DIRECT_BUF_RX_ENABLE +#include +#endif + +#define WMA_LOG_COMPLETION_TIMER 3000 /* 3 seconds */ +#define WMI_TLV_HEADROOM 128 + +#define WMA_FW_TIME_SYNC_TIMER 60000 /* 1 min */ + +static uint32_t g_fw_wlan_feat_caps; +/** + * wma_get_fw_wlan_feat_caps() - get fw feature capablity + * @feature: feature enum value + * + * Return: true/false + */ +bool wma_get_fw_wlan_feat_caps(enum cap_bitmap feature) +{ + return (g_fw_wlan_feat_caps & (1 << feature)) ? true : false; +} + +/** + * wma_set_fw_wlan_feat_caps() - set fw feature capablity + * @feature: feature enum value + * + * Return: None + */ +void wma_set_fw_wlan_feat_caps(enum cap_bitmap feature) +{ + g_fw_wlan_feat_caps |= (1 << feature); +} + +/** + * wma_service_ready_ext_evt_timeout() - Service ready extended event timeout + * @data: Timeout handler data + * + * This function is called when the FW fails to send WMI_SERVICE_READY_EXT_EVENT + * message + * + * Return: None + */ +static void wma_service_ready_ext_evt_timeout(void *data) +{ + tp_wma_handle wma_handle; + + WMA_LOGA("%s: Timeout waiting for WMI_SERVICE_READY_EXT_EVENT", + __func__); + + wma_handle = (tp_wma_handle) data; + + if (!wma_handle) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + goto end; + } + +end: + /* Assert here. Panic is being called in insmod thread */ + QDF_ASSERT(0); +} + +/** + * wma_get_ini_handle() - API to get WMA ini info handle + * @wma: WMA Handle + * + * Returns the pointer to WMA ini structure. + * Return: struct wma_ini_config + */ +struct wma_ini_config *wma_get_ini_handle(tp_wma_handle wma) +{ + if (!wma) { + WMA_LOGE("%s: Invalid WMA context\n", __func__); + return NULL; + } + + return &wma->ini_config; +} + +#define MAX_SUPPORTED_PEERS_REV1_1 14 +#define MAX_SUPPORTED_PEERS_REV1_3 32 +#define MIN_NO_OF_PEERS 1 + +/** + * wma_get_number_of_peers_supported - API to query for number of peers + * supported + * @wma: WMA Handle + * + * Return: Max Number of Peers Supported + */ +static uint8_t wma_get_number_of_peers_supported(tp_wma_handle wma) +{ + struct wma_ini_config *cfg = wma_get_ini_handle(wma); + uint8_t max_no_of_peers = cfg ? cfg->max_no_of_peers : MIN_NO_OF_PEERS; + + return max_no_of_peers; +} + +/** + * wma_get_number_of_tids_supported - API to query for number of tids supported + * @no_of_peers_supported: Number of peer supported + * + * Return: Max number of tids supported + */ +#if defined(CONFIG_HL_SUPPORT) +static uint32_t wma_get_number_of_tids_supported(uint8_t no_of_peers_supported, + uint8_t num_vdevs) +{ + return 4 * no_of_peers_supported; +} +#else +static uint32_t wma_get_number_of_tids_supported(uint8_t no_of_peers_supported, + uint8_t num_vdevs) +{ + return 2 * (no_of_peers_supported + num_vdevs + 2); +} +#endif + +#if (defined(IPA_DISABLE_OVERRIDE)) && (!defined(IPA_OFFLOAD)) +static void wma_set_ipa_disable_config( + target_resource_config *tgt_cfg) +{ + tgt_cfg->ipa_disable = true; +} +#else +static void wma_set_ipa_disable_config( + target_resource_config *tgt_cfg) +{ + tgt_cfg->ipa_disable = false; +} +#endif + +#ifndef NUM_OF_ADDITIONAL_FW_PEERS +#define NUM_OF_ADDITIONAL_FW_PEERS 2 +#endif + +/** + * wma_update_num_peers_tids() - Update num_peers and tids based on num_vdevs + * @wma_handle: wma handle + * @tgt_cfg: Resource config given to target + * + * Get num_vdevs from tgt_cfg and update num_peers and tids based on it. + * + * Return: none + */ +static void wma_update_num_peers_tids(t_wma_handle *wma_handle, + target_resource_config *tgt_cfg) + +{ + uint8_t no_of_peers_supported; + + no_of_peers_supported = wma_get_number_of_peers_supported(wma_handle); + + tgt_cfg->num_peers = no_of_peers_supported + tgt_cfg->num_vdevs + + NUM_OF_ADDITIONAL_FW_PEERS; + /* The current firmware implementation requires the number of + * offload peers should be (number of vdevs + 1). + */ + tgt_cfg->num_tids = + wma_get_number_of_tids_supported(no_of_peers_supported, + tgt_cfg->num_vdevs); +} + +/** + * wma_set_default_tgt_config() - set default tgt config + * @wma_handle: wma handle + * @tgt_cfg: Resource config given to target + * + * Return: none + */ +static void wma_set_default_tgt_config(tp_wma_handle wma_handle, + target_resource_config *tgt_cfg, + struct cds_config_info *cds_cfg) +{ + qdf_mem_zero(tgt_cfg, sizeof(target_resource_config)); + + tgt_cfg->num_vdevs = cds_cfg->num_vdevs; + wma_update_num_peers_tids(wma_handle, tgt_cfg); + + /* The current firmware implementation requires the number of + * offload peers should be (number of vdevs + 1). + */ + tgt_cfg->num_offload_peers = cds_cfg->ap_maxoffload_peers + 1; + tgt_cfg->num_offload_reorder_buffs = + cds_cfg->ap_maxoffload_reorderbuffs + 1; + tgt_cfg->num_peer_keys = CFG_TGT_NUM_PEER_KEYS; + tgt_cfg->ast_skid_limit = CFG_TGT_AST_SKID_LIMIT; + tgt_cfg->tx_chain_mask = CFG_TGT_DEFAULT_TX_CHAIN_MASK; + tgt_cfg->rx_chain_mask = CFG_TGT_DEFAULT_RX_CHAIN_MASK; + tgt_cfg->rx_timeout_pri[0] = CFG_TGT_RX_TIMEOUT_LO_PRI; + tgt_cfg->rx_timeout_pri[1] = CFG_TGT_RX_TIMEOUT_LO_PRI; + tgt_cfg->rx_timeout_pri[2] = CFG_TGT_RX_TIMEOUT_LO_PRI; + tgt_cfg->rx_timeout_pri[3] = CFG_TGT_RX_TIMEOUT_HI_PRI; + tgt_cfg->rx_decap_mode = CFG_TGT_RX_DECAP_MODE; + tgt_cfg->scan_max_pending_req = WLAN_MAX_ACTIVE_SCANS_ALLOWED; + tgt_cfg->bmiss_offload_max_vdev = + CFG_TGT_DEFAULT_BMISS_OFFLOAD_MAX_VDEV; + tgt_cfg->roam_offload_max_vdev = CFG_TGT_DEFAULT_ROAM_OFFLOAD_MAX_VDEV; + tgt_cfg->roam_offload_max_ap_profiles = + CFG_TGT_DEFAULT_ROAM_OFFLOAD_MAX_PROFILES; + tgt_cfg->num_mcast_groups = CFG_TGT_DEFAULT_NUM_MCAST_GROUPS; + tgt_cfg->num_mcast_table_elems = CFG_TGT_DEFAULT_NUM_MCAST_TABLE_ELEMS; + tgt_cfg->mcast2ucast_mode = CFG_TGT_DEFAULT_MCAST2UCAST_MODE; + tgt_cfg->tx_dbg_log_size = CFG_TGT_DEFAULT_TX_DBG_LOG_SIZE; + tgt_cfg->num_wds_entries = CFG_TGT_WDS_ENTRIES; + tgt_cfg->dma_burst_size = CFG_TGT_DEFAULT_DMA_BURST_SIZE; + tgt_cfg->mac_aggr_delim = CFG_TGT_DEFAULT_MAC_AGGR_DELIM; + tgt_cfg->rx_skip_defrag_timeout_dup_detection_check = + CFG_TGT_DEFAULT_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK, + tgt_cfg->vow_config = CFG_TGT_DEFAULT_VOW_CONFIG; + tgt_cfg->gtk_offload_max_vdev = CFG_TGT_DEFAULT_GTK_OFFLOAD_MAX_VDEV; + tgt_cfg->num_msdu_desc = CFG_TGT_NUM_MSDU_DESC; + tgt_cfg->max_frag_entries = CFG_TGT_MAX_FRAG_TABLE_ENTRIES; + tgt_cfg->num_tdls_vdevs = CFG_TGT_NUM_TDLS_VDEVS; + tgt_cfg->num_tdls_conn_table_entries = + cfg_tdls_get_max_peer_count(wma_handle->psoc); + tgt_cfg->beacon_tx_offload_max_vdev = + CFG_TGT_DEFAULT_BEACON_TX_OFFLOAD_MAX_VDEV; + tgt_cfg->num_multicast_filter_entries = + CFG_TGT_MAX_MULTICAST_FILTER_ENTRIES; + tgt_cfg->num_wow_filters = 0; + tgt_cfg->num_keep_alive_pattern = WMA_MAXNUM_PERIODIC_TX_PTRNS; + tgt_cfg->num_max_sta_vdevs = CFG_TGT_DEFAULT_MAX_STA_VDEVS; + tgt_cfg->keep_alive_pattern_size = 0; + tgt_cfg->max_tdls_concurrent_sleep_sta = + CFG_TGT_NUM_TDLS_CONC_SLEEP_STAS; + tgt_cfg->max_tdls_concurrent_buffer_sta = + CFG_TGT_NUM_TDLS_CONC_BUFFER_STAS; + tgt_cfg->wmi_send_separate = 0; + tgt_cfg->num_ocb_vdevs = CFG_TGT_NUM_OCB_VDEVS; + tgt_cfg->num_ocb_channels = CFG_TGT_NUM_OCB_CHANNELS; + tgt_cfg->num_ocb_schedules = CFG_TGT_NUM_OCB_SCHEDULES; + + + tgt_cfg->mgmt_comp_evt_bundle_support = true; + tgt_cfg->tx_msdu_new_partition_id_support = true; + + cfg_nan_get_max_ndi(wma_handle->psoc, + &tgt_cfg->max_ndi); + + if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) + tgt_cfg->rx_decap_mode = CFG_TGT_RX_DECAP_MODE_RAW; + + cfg_nan_get_ndp_max_sessions(wma_handle->psoc, + &tgt_cfg->max_ndp_sessions); + + wma_set_ipa_disable_config(tgt_cfg); +} + +/** + * wma_cli_get_command() - WMA "get" command processor + * @vdev_id: virtual device for the command + * @param_id: parameter id + * @vpdev: parameter category + * + * Return: parameter value on success, -EINVAL on failure + */ +int wma_cli_get_command(int vdev_id, int param_id, int vpdev) +{ + int ret = 0; + tp_wma_handle wma; + struct wma_txrx_node *intr = NULL; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + WMA_LOGE("%s: Invalid wma handle", __func__); + return -EINVAL; + } + + intr = wma->interfaces; + + if (VDEV_CMD == vpdev) { + switch (param_id) { + case WMI_VDEV_PARAM_NSS: + ret = intr[vdev_id].config.nss; + break; +#ifdef QCA_SUPPORT_GTX + case WMI_VDEV_PARAM_GTX_HT_MCS: + ret = intr[vdev_id].config.gtx_info.gtxRTMask[0]; + break; + case WMI_VDEV_PARAM_GTX_VHT_MCS: + ret = intr[vdev_id].config.gtx_info.gtxRTMask[1]; + break; + case WMI_VDEV_PARAM_GTX_USR_CFG: + ret = intr[vdev_id].config.gtx_info.gtxUsrcfg; + break; + case WMI_VDEV_PARAM_GTX_THRE: + ret = intr[vdev_id].config.gtx_info.gtxPERThreshold; + break; + case WMI_VDEV_PARAM_GTX_MARGIN: + ret = intr[vdev_id].config.gtx_info.gtxPERMargin; + break; + case WMI_VDEV_PARAM_GTX_STEP: + ret = intr[vdev_id].config.gtx_info.gtxTPCstep; + break; + case WMI_VDEV_PARAM_GTX_MINTPC: + ret = intr[vdev_id].config.gtx_info.gtxTPCMin; + break; + case WMI_VDEV_PARAM_GTX_BW_MASK: + ret = intr[vdev_id].config.gtx_info.gtxBWMask; + break; +#endif /* QCA_SUPPORT_GTX */ + case WMI_VDEV_PARAM_LDPC: + ret = intr[vdev_id].config.ldpc; + break; + case WMI_VDEV_PARAM_TX_STBC: + ret = intr[vdev_id].config.tx_stbc; + break; + case WMI_VDEV_PARAM_RX_STBC: + ret = intr[vdev_id].config.rx_stbc; + break; + case WMI_VDEV_PARAM_SGI: + ret = intr[vdev_id].config.shortgi; + break; + case WMI_VDEV_PARAM_ENABLE_RTSCTS: + ret = intr[vdev_id].config.rtscts_en; + break; + case WMI_VDEV_PARAM_CHWIDTH: + ret = intr[vdev_id].config.chwidth; + break; + case WMI_VDEV_PARAM_FIXED_RATE: + ret = intr[vdev_id].config.tx_rate; + break; + case WMI_VDEV_PARAM_HE_DCM: + case WMI_VDEV_PARAM_HE_RANGE_EXT: + ret = wma_get_he_vdev_param(&intr[vdev_id], param_id); + break; + default: + WMA_LOGE("Invalid cli_get vdev command/Not yet implemented 0x%x", + param_id); + return -EINVAL; + } + } else if (PDEV_CMD == vpdev) { + switch (param_id) { + case WMI_PDEV_PARAM_ANI_ENABLE: + ret = wma->pdevconfig.ani_enable; + break; + case WMI_PDEV_PARAM_ANI_POLL_PERIOD: + ret = wma->pdevconfig.ani_poll_len; + break; + case WMI_PDEV_PARAM_ANI_LISTEN_PERIOD: + ret = wma->pdevconfig.ani_listen_len; + break; + case WMI_PDEV_PARAM_ANI_OFDM_LEVEL: + ret = wma->pdevconfig.ani_ofdm_level; + break; + case WMI_PDEV_PARAM_ANI_CCK_LEVEL: + ret = wma->pdevconfig.ani_cck_level; + break; + case WMI_PDEV_PARAM_DYNAMIC_BW: + ret = wma->pdevconfig.cwmenable; + break; + case WMI_PDEV_PARAM_CTS_CBW: + ret = wma->pdevconfig.cts_cbw; + break; + case WMI_PDEV_PARAM_TX_CHAIN_MASK: + ret = wma->pdevconfig.txchainmask; + break; + case WMI_PDEV_PARAM_RX_CHAIN_MASK: + ret = wma->pdevconfig.rxchainmask; + break; + case WMI_PDEV_PARAM_TXPOWER_LIMIT2G: + ret = wma->pdevconfig.txpow2g; + break; + case WMI_PDEV_PARAM_TXPOWER_LIMIT5G: + ret = wma->pdevconfig.txpow5g; + break; + default: + WMA_LOGE("Invalid cli_get pdev command/Not yet implemented 0x%x", + param_id); + return -EINVAL; + } + } else if (GEN_CMD == vpdev) { + switch (param_id) { + case GEN_VDEV_PARAM_AMPDU: + ret = intr[vdev_id].config.ampdu; + break; + case GEN_VDEV_PARAM_AMSDU: + ret = intr[vdev_id].config.amsdu; + break; + case GEN_VDEV_ROAM_SYNCH_DELAY: + ret = intr[vdev_id].roam_synch_delay; + break; + default: + WMA_LOGE("Invalid generic vdev command/Not yet implemented 0x%x", + param_id); + return -EINVAL; + } + } else if (PPS_CMD == vpdev) { + switch (param_id) { + case WMI_VDEV_PPS_PAID_MATCH: + ret = intr[vdev_id].config.pps_params.paid_match_enable; + break; + case WMI_VDEV_PPS_GID_MATCH: + ret = intr[vdev_id].config.pps_params.gid_match_enable; + break; + case WMI_VDEV_PPS_EARLY_TIM_CLEAR: + ret = intr[vdev_id].config.pps_params.tim_clear; + break; + case WMI_VDEV_PPS_EARLY_DTIM_CLEAR: + ret = intr[vdev_id].config.pps_params.dtim_clear; + break; + case WMI_VDEV_PPS_EOF_PAD_DELIM: + ret = intr[vdev_id].config.pps_params.eof_delim; + break; + case WMI_VDEV_PPS_MACADDR_MISMATCH: + ret = intr[vdev_id].config.pps_params.mac_match; + break; + case WMI_VDEV_PPS_DELIM_CRC_FAIL: + ret = intr[vdev_id].config.pps_params.delim_fail; + break; + case WMI_VDEV_PPS_GID_NSTS_ZERO: + ret = intr[vdev_id].config.pps_params.nsts_zero; + break; + case WMI_VDEV_PPS_RSSI_CHECK: + ret = intr[vdev_id].config.pps_params.rssi_chk; + break; + default: + WMA_LOGE("Invalid pps vdev command/Not yet implemented 0x%x", + param_id); + return -EINVAL; + } + } else if (QPOWER_CMD == vpdev) { + switch (param_id) { + case WMI_STA_PS_PARAM_QPOWER_PSPOLL_COUNT: + ret = intr[vdev_id].config.qpower_params. + max_ps_poll_cnt; + break; + case WMI_STA_PS_PARAM_QPOWER_MAX_TX_BEFORE_WAKE: + ret = intr[vdev_id].config.qpower_params. + max_tx_before_wake; + break; + case WMI_STA_PS_PARAM_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL: + ret = intr[vdev_id].config.qpower_params. + spec_ps_poll_wake_interval; + break; + case WMI_STA_PS_PARAM_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL: + ret = intr[vdev_id].config.qpower_params. + max_spec_nodata_ps_poll; + break; + default: + WMA_LOGE("Invalid generic vdev command/Not yet implemented 0x%x", + param_id); + return -EINVAL; + } + } else if (GTX_CMD == vpdev) { + switch (param_id) { + case WMI_VDEV_PARAM_GTX_HT_MCS: + ret = intr[vdev_id].config.gtx_info.gtxRTMask[0]; + break; + case WMI_VDEV_PARAM_GTX_VHT_MCS: + ret = intr[vdev_id].config.gtx_info.gtxRTMask[1]; + break; + case WMI_VDEV_PARAM_GTX_USR_CFG: + ret = intr[vdev_id].config.gtx_info.gtxUsrcfg; + break; + case WMI_VDEV_PARAM_GTX_THRE: + ret = intr[vdev_id].config.gtx_info.gtxPERThreshold; + break; + case WMI_VDEV_PARAM_GTX_MARGIN: + ret = intr[vdev_id].config.gtx_info.gtxPERMargin; + break; + case WMI_VDEV_PARAM_GTX_STEP: + ret = intr[vdev_id].config.gtx_info.gtxTPCstep; + break; + case WMI_VDEV_PARAM_GTX_MINTPC: + ret = intr[vdev_id].config.gtx_info.gtxTPCMin; + break; + case WMI_VDEV_PARAM_GTX_BW_MASK: + ret = intr[vdev_id].config.gtx_info.gtxBWMask; + break; + default: + WMA_LOGE("Invalid generic vdev command/Not yet implemented 0x%x", + param_id); + return -EINVAL; + } + } + return ret; +} + +/** + * wma_cli_set2_command() - WMA "set 2 params" command processor + * @vdev_id: virtual device for the command + * @param_id: parameter id + * @sval1: first parameter value + * @sval2: second parameter value + * @vpdev: parameter category + * + * Command handler for set operations which require 2 parameters + * + * Return: 0 on success, errno on failure + */ +int wma_cli_set2_command(int vdev_id, int param_id, int sval1, + int sval2, int vpdev) +{ + struct scheduler_msg msg = { 0 }; + wma_cli_set_cmd_t *iwcmd; + + iwcmd = qdf_mem_malloc(sizeof(*iwcmd)); + if (!iwcmd) + return -ENOMEM; + + qdf_mem_zero(iwcmd, sizeof(*iwcmd)); + iwcmd->param_value = sval1; + iwcmd->param_sec_value = sval2; + iwcmd->param_vdev_id = vdev_id; + iwcmd->param_id = param_id; + iwcmd->param_vp_dev = vpdev; + msg.type = WMA_CLI_SET_CMD; + msg.reserved = 0; + msg.bodyptr = iwcmd; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + qdf_mem_free(iwcmd); + return -EIO; + } + return 0; +} + +/** + * wma_cli_set_command() - WMA "set" command processor + * @vdev_id: virtual device for the command + * @param_id: parameter id + * @sval: parameter value + * @vpdev: parameter category + * + * Command handler for set operations + * + * Return: 0 on success, errno on failure + */ +int wma_cli_set_command(int vdev_id, int param_id, int sval, int vpdev) +{ + return wma_cli_set2_command(vdev_id, param_id, sval, 0, vpdev); + +} + +QDF_STATUS wma_form_unit_test_cmd_and_send(uint32_t vdev_id, + uint32_t module_id, uint32_t arg_count, uint32_t *arg) +{ + struct wmi_unit_test_cmd *unit_test_args; + tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + uint32_t i; + QDF_STATUS status; + + WMA_LOGD(FL("enter")); + + if (!wma_is_vdev_valid(vdev_id)) + return QDF_STATUS_E_FAILURE; + + if (arg_count > WMA_MAX_NUM_ARGS) { + WMA_LOGE(FL("arg_count is crossed the boundary")); + return QDF_STATUS_E_FAILURE; + } + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE(FL("Invalid WMA/WMI handle")); + return QDF_STATUS_E_FAILURE; + } + unit_test_args = qdf_mem_malloc(sizeof(*unit_test_args)); + if (!unit_test_args) + return QDF_STATUS_E_NOMEM; + + unit_test_args->vdev_id = vdev_id; + unit_test_args->module_id = module_id; + unit_test_args->num_args = arg_count; + for (i = 0; i < arg_count; i++) + unit_test_args->args[i] = arg[i]; + + status = wmi_unified_unit_test_cmd(wma_handle->wmi_handle, + unit_test_args); + qdf_mem_free(unit_test_args); + WMA_LOGD(FL("exit")); + + return status; +} + +static void wma_process_send_addba_req(tp_wma_handle wma_handle, + struct send_add_ba_req *send_addba) +{ + QDF_STATUS status; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE(FL("Invalid WMA/WMI handle")); + qdf_mem_free(send_addba); + return; + } + + status = wmi_unified_addba_send_cmd_send(wma_handle->wmi_handle, + send_addba->mac_addr, + &send_addba->param); + if (QDF_STATUS_SUCCESS != status) { + WMA_LOGE(FL("Failed to process WMA_SEND_ADDBA_REQ")); + } + wma_debug("sent ADDBA req to" QDF_MAC_ADDR_FMT "tid %d buff_size %d", + QDF_MAC_ADDR_REF(send_addba->mac_addr), + send_addba->param.tidno, + send_addba->param.buffersize); + + qdf_mem_free(send_addba); +} + +/** + * wma_set_priv_cfg() - set private config parameters + * @wma_handle: wma handle + * @privcmd: private command + * + * Return: 0 for success or error code + */ +static int32_t wma_set_priv_cfg(tp_wma_handle wma_handle, + wma_cli_set_cmd_t *privcmd) +{ + int32_t ret = 0; + + switch (privcmd->param_id) { + case WMA_VDEV_TXRX_FWSTATS_ENABLE_CMDID: + ret = wma_set_txrx_fw_stats_level(wma_handle, + privcmd->param_vdev_id, + privcmd->param_value); + break; + case WMA_VDEV_TXRX_FWSTATS_RESET_CMDID: + ret = wma_txrx_fw_stats_reset(wma_handle, + privcmd->param_vdev_id, + privcmd->param_value); + break; + case WMI_STA_SMPS_FORCE_MODE_CMDID: + ret = wma_set_mimops(wma_handle, + privcmd->param_vdev_id, + privcmd->param_value); + break; + case WMI_STA_SMPS_PARAM_CMDID: + wma_set_smps_params(wma_handle, privcmd->param_vdev_id, + privcmd->param_value); + break; + case WMA_VDEV_MCC_SET_TIME_LATENCY: + { + /* Extract first MCC adapter/vdev channel number and latency */ + uint8_t mcc_channel = privcmd->param_value & 0x000000FF; + uint8_t mcc_channel_latency = + (privcmd->param_value & 0x0000FF00) >> 8; + int ret = -1; + + WMA_LOGD("%s: Parsed input: Channel #1:%d, latency:%dms", + __func__, mcc_channel, mcc_channel_latency); + ret = wma_set_mcc_channel_time_latency(wma_handle, + mcc_channel, + mcc_channel_latency); + } + break; + case WMA_VDEV_MCC_SET_TIME_QUOTA: + { + /* Extract the MCC 2 adapters/vdevs channel numbers and time + * quota value for the first adapter only (which is specified + * in iwpriv command. + */ + uint8_t adapter_2_chan_number = + privcmd->param_value & 0x000000FF; + uint8_t adapter_1_chan_number = + (privcmd->param_value & 0x0000FF00) >> 8; + uint8_t adapter_1_quota = + (privcmd->param_value & 0x00FF0000) >> 16; + int ret = -1; + + WMA_LOGD("%s: Parsed input: Channel #1:%d, Channel #2:%d, quota 1:%dms", + __func__, adapter_1_chan_number, + adapter_2_chan_number, adapter_1_quota); + + ret = wma_set_mcc_channel_time_quota(wma_handle, + adapter_1_chan_number, + adapter_1_quota, + adapter_2_chan_number); + } + break; + case WMA_VDEV_IBSS_SET_ATIM_WINDOW_SIZE: + { + wma_handle->wma_ibss_power_save_params.atimWindowLength = + privcmd->param_value; + WMA_LOGD("%s: IBSS power save ATIM Window = %d", + __func__, wma_handle->wma_ibss_power_save_params. + atimWindowLength); + } + break; + case WMA_VDEV_IBSS_SET_POWER_SAVE_ALLOWED: + { + wma_handle->wma_ibss_power_save_params.isPowerSaveAllowed = + privcmd->param_value; + WMA_LOGD("%s: IBSS is Power Save Allowed = %d", + __func__, wma_handle->wma_ibss_power_save_params. + isPowerSaveAllowed); + } + break; + case WMA_VDEV_IBSS_SET_POWER_COLLAPSE_ALLOWED: + { + wma_handle->wma_ibss_power_save_params. isPowerCollapseAllowed = + privcmd->param_value; + WMA_LOGD("%s: IBSS is Power Collapse Allowed = %d", + __func__, wma_handle->wma_ibss_power_save_params. + isPowerCollapseAllowed); + } + break; + case WMA_VDEV_IBSS_SET_AWAKE_ON_TX_RX: + { + wma_handle->wma_ibss_power_save_params.isAwakeonTxRxEnabled = + privcmd->param_value; + WMA_LOGD("%s: IBSS Power Save Awake on Tx/Rx Enabled = %d", + __func__, wma_handle->wma_ibss_power_save_params. + isAwakeonTxRxEnabled); + } + break; + case WMA_VDEV_IBSS_SET_INACTIVITY_TIME: + { + wma_handle->wma_ibss_power_save_params.inactivityCount = + privcmd->param_value; + WMA_LOGD("%s: IBSS Power Save Data Inactivity Count = %d", + __func__, wma_handle->wma_ibss_power_save_params. + inactivityCount); + } + break; + case WMA_VDEV_IBSS_SET_TXSP_END_INACTIVITY_TIME: + { + wma_handle->wma_ibss_power_save_params.txSPEndInactivityTime = + privcmd->param_value; + WMA_LOGD("%s: IBSS Power Save Transmit EOSP inactivity time out = %d", + __func__, wma_handle->wma_ibss_power_save_params. + txSPEndInactivityTime); + } + break; + case WMA_VDEV_IBSS_PS_SET_WARMUP_TIME_SECS: + { + wma_handle->wma_ibss_power_save_params.ibssPsWarmupTime = + privcmd->param_value; + WMA_LOGD("%s: IBSS Power Save Warm Up Time in Seconds = %d", + __func__, wma_handle->wma_ibss_power_save_params. + ibssPsWarmupTime); + } + break; + case WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW: + { + wma_handle->wma_ibss_power_save_params.ibssPs1RxChainInAtimEnable + = privcmd->param_value; + WMA_LOGD("%s: IBSS Power Save single RX Chain Enable In ATIM = %d", + __func__, wma_handle->wma_ibss_power_save_params. + ibssPs1RxChainInAtimEnable); + } + break; + + default: + WMA_LOGE("Invalid wma config command id:%d", privcmd->param_id); + ret = -EINVAL; + } + return ret; +} + +/** + * wma_set_dtim_period() - set dtim period to FW + * @wma: wma handle + * @dtim_params: dtim params + * + * Return: none + */ +static void wma_set_dtim_period(tp_wma_handle wma, + struct set_dtim_params *dtim_params) +{ + struct wma_txrx_node *iface = + &wma->interfaces[dtim_params->session_id]; + if (!wma_is_vdev_valid(dtim_params->session_id)) { + WMA_LOGE("%s: invalid VDEV", __func__); + return; + } + WMA_LOGD("%s: set dtim_period %d", __func__, + dtim_params->dtim_period); + iface->dtimPeriod = dtim_params->dtim_period; + +} + +/** + * wma_process_cli_set_cmd() - set parameters to fw + * @wma: wma handle + * @privcmd: command + * + * Return: none + */ +static void wma_process_cli_set_cmd(tp_wma_handle wma, + wma_cli_set_cmd_t *privcmd) +{ + int vid = privcmd->param_vdev_id, pps_val = 0; + QDF_STATUS ret; + struct wma_txrx_node *intr = wma->interfaces; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + struct qpower_params *qparams = &intr[vid].config.qpower_params; + struct pdev_params pdev_param; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct target_psoc_info *tgt_hdl; + + if (!mac) { + WMA_LOGE("%s: Failed to get mac", __func__); + return; + } + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wma->psoc); + if (!tgt_hdl) { + WMA_LOGE("%s: target psoc info is NULL", __func__); + return; + } + + if (privcmd->param_id >= WMI_CMDID_MAX) { + /* + * This configuration setting is not done using any wmi + * command, call appropriate handler. + */ + if (wma_set_priv_cfg(wma, privcmd)) + WMA_LOGE("Failed to set wma priv congiuration"); + return; + } + + switch (privcmd->param_vp_dev) { + case VDEV_CMD: + if (!wma_is_vdev_valid(privcmd->param_vdev_id)) { + WMA_LOGE("%s Vdev id is not valid", __func__); + return; + } + + WMA_LOGD("vdev id %d pid %d pval %d", privcmd->param_vdev_id, + privcmd->param_id, privcmd->param_value); + ret = wma_vdev_set_param(wma->wmi_handle, + privcmd->param_vdev_id, + privcmd->param_id, + privcmd->param_value); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("wma_vdev_set_param failed ret %d", + ret); + return; + } + break; + case PDEV_CMD: + WMA_LOGD("pdev pid %d pval %d", privcmd->param_id, + privcmd->param_value); + if ((privcmd->param_id == WMI_PDEV_PARAM_RX_CHAIN_MASK) || + (privcmd->param_id == WMI_PDEV_PARAM_TX_CHAIN_MASK)) { + if (QDF_STATUS_SUCCESS != + wma_check_txrx_chainmask( + target_if_get_num_rf_chains(tgt_hdl), + privcmd->param_value)) { + WMA_LOGD("Chainmask value is invalid"); + return; + } + } + pdev_param.param_id = privcmd->param_id; + pdev_param.param_value = privcmd->param_value; + ret = wmi_unified_pdev_param_send(wma->wmi_handle, + &pdev_param, + WMA_WILDCARD_PDEV_ID); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("wma_vdev_set_param failed ret %d", + ret); + return; + } + break; + case GEN_CMD: + { + struct wma_txrx_node *intr = wma->interfaces; + wmi_vdev_custom_aggr_type_t aggr_type = + WMI_VDEV_CUSTOM_AGGR_TYPE_AMSDU; + + WMA_LOGD("gen pid %d pval %d", privcmd->param_id, + privcmd->param_value); + + switch (privcmd->param_id) { + case GEN_VDEV_PARAM_AMSDU: + case GEN_VDEV_PARAM_AMPDU: + if (!soc) { + WMA_LOGE("%s:SOC context is NULL", __func__); + return; + } + + if (privcmd->param_id == GEN_VDEV_PARAM_AMPDU) { + ret = cdp_aggr_cfg(soc, privcmd->param_vdev_id, + privcmd->param_value, 0); + if (ret) + WMA_LOGE("cdp_aggr_cfg set ampdu failed ret %d", + ret); + else + intr[privcmd->param_vdev_id].config. + ampdu = privcmd->param_value; + + aggr_type = + WMI_VDEV_CUSTOM_AGGR_TYPE_AMPDU; + } + + ret = wma_set_tx_rx_aggr_size(vid, + privcmd->param_value, + privcmd->param_value, + aggr_type); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("set_aggr_size failed ret %d", ret); + return; + } + break; + case GEN_PARAM_CRASH_INJECT: + if (QDF_GLOBAL_FTM_MODE == cds_get_conparam()) + WMA_LOGE("Crash inject not allowed in FTM mode"); + else + ret = wma_crash_inject(wma, + privcmd->param_value, + privcmd->param_sec_value); + break; + case GEN_PARAM_CAPTURE_TSF: + ret = wma_capture_tsf(wma, privcmd->param_value); + break; + case GEN_PARAM_RESET_TSF_GPIO: + ret = wma_reset_tsf_gpio(wma, privcmd->param_value); + break; + default: + WMA_LOGE("Invalid param id 0x%x", + privcmd->param_id); + break; + } + break; + } + case DBG_CMD: + WMA_LOGD("dbg pid %d pval %d", privcmd->param_id, + privcmd->param_value); + switch (privcmd->param_id) { + case WMI_DBGLOG_LOG_LEVEL: + ret = dbglog_set_log_lvl(wma->wmi_handle, + privcmd->param_value); + if (ret) + WMA_LOGE("dbglog_set_log_lvl failed ret %d", + ret); + break; + case WMI_DBGLOG_VAP_ENABLE: + ret = dbglog_vap_log_enable(wma->wmi_handle, + privcmd->param_value, true); + if (ret) + WMA_LOGE("dbglog_vap_log_enable failed ret %d", + ret); + break; + case WMI_DBGLOG_VAP_DISABLE: + ret = dbglog_vap_log_enable(wma->wmi_handle, + privcmd->param_value, false); + if (ret) + WMA_LOGE("dbglog_vap_log_enable failed ret %d", + ret); + break; + case WMI_DBGLOG_MODULE_ENABLE: + ret = dbglog_module_log_enable(wma->wmi_handle, + privcmd->param_value, true); + if (ret) + WMA_LOGE("dbglog_module_log_enable failed ret %d", + ret); + break; + case WMI_DBGLOG_MODULE_DISABLE: + ret = dbglog_module_log_enable(wma->wmi_handle, + privcmd->param_value, false); + if (ret) + WMA_LOGE("dbglog_module_log_enable failed ret %d", + ret); + break; + case WMI_DBGLOG_MOD_LOG_LEVEL: + ret = dbglog_set_mod_log_lvl(wma->wmi_handle, + privcmd->param_value); + if (ret) + WMA_LOGE("dbglog_module_log_enable failed ret %d", + ret); + break; + case WMI_DBGLOG_MOD_WOW_LOG_LEVEL: + ret = dbglog_set_mod_wow_log_lvl(wma->wmi_handle, + privcmd->param_value); + if (ret) + wma_err("WMI_DBGLOG_MOD_WOW_LOG_LEVEL failed ret %d", + ret); + break; + case WMI_DBGLOG_TYPE: + ret = dbglog_parser_type_init(wma->wmi_handle, + privcmd->param_value); + if (ret) + WMA_LOGE("dbglog_parser_type_init failed ret %d", + ret); + break; + case WMI_DBGLOG_REPORT_ENABLE: + ret = dbglog_report_enable(wma->wmi_handle, + privcmd->param_value); + if (ret) + WMA_LOGE("dbglog_report_enable failed ret %d", + ret); + break; + case WMI_WLAN_PROFILE_TRIGGER_CMDID: + ret = wma_unified_fw_profiling_cmd(wma->wmi_handle, + WMI_WLAN_PROFILE_TRIGGER_CMDID, + privcmd->param_value, 0); + if (ret) + WMA_LOGE("Profile cmd failed for %d ret %d", + WMI_WLAN_PROFILE_TRIGGER_CMDID, ret); + break; + case WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID: + ret = wma_unified_fw_profiling_cmd(wma->wmi_handle, + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + privcmd->param_value, + privcmd->param_sec_value); + if (ret) + WMA_LOGE("Profile cmd failed for %d ret %d", + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + ret); + break; + case WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID: + ret = wma_unified_fw_profiling_cmd(wma->wmi_handle, + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + privcmd->param_value, + privcmd->param_sec_value); + if (ret) + WMA_LOGE("Profile cmd failed for %d ret %d", + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + ret); + break; + case WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID: + ret = wma_unified_fw_profiling_cmd(wma->wmi_handle, + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + 0, 0); + if (ret) + WMA_LOGE("Profile cmd failed for %d ret %d", + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + ret); + break; + case WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID: + ret = wma_unified_fw_profiling_cmd(wma->wmi_handle, + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + 0, 0); + if (ret) + WMA_LOGE("Profile cmd failed for %d ret %d", + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + ret); + break; + case WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID: + /* Set the Green AP */ + ret = wmi_unified_green_ap_ps_send + (wma->wmi_handle, privcmd->param_value, + WMA_WILDCARD_PDEV_ID); + if (ret) { + WMA_LOGE("Set GreenAP Failed val %d", + privcmd->param_value); + } + break; + + default: + WMA_LOGE("Invalid param id 0x%x", privcmd->param_id); + break; + } + break; + case PPS_CMD: + WMA_LOGD("dbg pid %d pval %d", privcmd->param_id, + privcmd->param_value); + switch (privcmd->param_id) { + + case WMI_VDEV_PPS_PAID_MATCH: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_PAID_MATCH & 0xffff); + intr[vid].config.pps_params.paid_match_enable = + privcmd->param_value; + break; + case WMI_VDEV_PPS_GID_MATCH: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_GID_MATCH & 0xffff); + intr[vid].config.pps_params.gid_match_enable = + privcmd->param_value; + break; + case WMI_VDEV_PPS_EARLY_TIM_CLEAR: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_EARLY_TIM_CLEAR & 0xffff); + intr[vid].config.pps_params.tim_clear = + privcmd->param_value; + break; + case WMI_VDEV_PPS_EARLY_DTIM_CLEAR: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_EARLY_DTIM_CLEAR & 0xffff); + intr[vid].config.pps_params.dtim_clear = + privcmd->param_value; + break; + case WMI_VDEV_PPS_EOF_PAD_DELIM: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_EOF_PAD_DELIM & 0xffff); + intr[vid].config.pps_params.eof_delim = + privcmd->param_value; + break; + case WMI_VDEV_PPS_MACADDR_MISMATCH: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_MACADDR_MISMATCH & 0xffff); + intr[vid].config.pps_params.mac_match = + privcmd->param_value; + break; + case WMI_VDEV_PPS_DELIM_CRC_FAIL: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_DELIM_CRC_FAIL & 0xffff); + intr[vid].config.pps_params.delim_fail = + privcmd->param_value; + break; + case WMI_VDEV_PPS_GID_NSTS_ZERO: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_GID_NSTS_ZERO & 0xffff); + intr[vid].config.pps_params.nsts_zero = + privcmd->param_value; + break; + case WMI_VDEV_PPS_RSSI_CHECK: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_RSSI_CHECK & 0xffff); + intr[vid].config.pps_params.rssi_chk = + privcmd->param_value; + break; + case WMI_VDEV_PPS_5G_EBT: + pps_val = ((privcmd->param_value << 31) & 0xffff0000) | + (PKT_PWR_SAVE_5G_EBT & 0xffff); + intr[vid].config.pps_params.ebt_5g = + privcmd->param_value; + break; + default: + WMA_LOGE("Invalid param id 0x%x", privcmd->param_id); + break; + } + break; + + case QPOWER_CMD: + WMA_LOGD("QPOWER CLI CMD pid %d pval %d", privcmd->param_id, + privcmd->param_value); + switch (privcmd->param_id) { + case WMI_STA_PS_PARAM_QPOWER_PSPOLL_COUNT: + WMA_LOGD("QPOWER CLI CMD:Ps Poll Cnt val %d", + privcmd->param_value); + /* Set the QPower Ps Poll Count */ + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, + vid, WMI_STA_PS_PARAM_QPOWER_PSPOLL_COUNT, + privcmd->param_value); + if (ret) { + WMA_LOGE("Set Q-PsPollCnt Failed vdevId %d val %d", + vid, privcmd->param_value); + } else { + qparams->max_ps_poll_cnt = privcmd->param_value; + } + break; + case WMI_STA_PS_PARAM_QPOWER_MAX_TX_BEFORE_WAKE: + WMA_LOGD("QPOWER CLI CMD:Max Tx Before wake val %d", + privcmd->param_value); + /* Set the QPower Max Tx Before Wake */ + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, + vid, WMI_STA_PS_PARAM_QPOWER_MAX_TX_BEFORE_WAKE, + privcmd->param_value); + if (ret) { + WMA_LOGE("Set Q-MaxTxBefWake Failed vId %d val %d", + vid, privcmd->param_value); + } else { + qparams->max_tx_before_wake = + privcmd->param_value; + } + break; + case WMI_STA_PS_PARAM_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL: + WMA_LOGD("QPOWER CLI CMD:Ps Poll Wake Inv val %d", + privcmd->param_value); + /* Set the QPower Spec Ps Poll Wake Inv */ + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, vid, + WMI_STA_PS_PARAM_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL, + privcmd->param_value); + if (ret) { + WMA_LOGE("Set Q-PsPoll WakeIntv Failed vId %d val %d", + vid, privcmd->param_value); + } else { + qparams->spec_ps_poll_wake_interval = + privcmd->param_value; + } + break; + case WMI_STA_PS_PARAM_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL: + WMA_LOGD("QPOWER CLI CMD:Spec NoData Ps Poll val %d", + privcmd->param_value); + /* Set the QPower Spec NoData PsPoll */ + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, vid, + WMI_STA_PS_PARAM_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL, + privcmd->param_value); + if (ret) { + WMA_LOGE("Set Q-SpecNoDataPsPoll Failed vId %d val %d", + vid, privcmd->param_value); + } else { + qparams->max_spec_nodata_ps_poll = + privcmd->param_value; + } + break; + + default: + WMA_LOGE("Invalid param id 0x%x", privcmd->param_id); + break; + } + break; + case GTX_CMD: + WMA_LOGD("vdev id %d pid %d pval %d", privcmd->param_vdev_id, + privcmd->param_id, privcmd->param_value); + switch (privcmd->param_id) { + case WMI_VDEV_PARAM_GTX_HT_MCS: + intr[vid].config.gtx_info.gtxRTMask[0] = + privcmd->param_value; + ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, + privcmd->param_vdev_id, + &intr[vid].config.gtx_info); + break; + case WMI_VDEV_PARAM_GTX_VHT_MCS: + intr[vid].config.gtx_info.gtxRTMask[1] = + privcmd->param_value; + ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, + privcmd->param_vdev_id, + &intr[vid].config.gtx_info); + break; + + case WMI_VDEV_PARAM_GTX_USR_CFG: + intr[vid].config.gtx_info.gtxUsrcfg = + privcmd->param_value; + ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, + privcmd->param_vdev_id, + &intr[vid].config.gtx_info); + break; + + case WMI_VDEV_PARAM_GTX_THRE: + intr[vid].config.gtx_info.gtxPERThreshold = + privcmd->param_value; + ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, + privcmd->param_vdev_id, + &intr[vid].config.gtx_info); + break; + + case WMI_VDEV_PARAM_GTX_MARGIN: + intr[vid].config.gtx_info.gtxPERMargin = + privcmd->param_value; + ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, + privcmd->param_vdev_id, + &intr[vid].config.gtx_info); + break; + + case WMI_VDEV_PARAM_GTX_STEP: + intr[vid].config.gtx_info.gtxTPCstep = + privcmd->param_value; + ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, + privcmd->param_vdev_id, + &intr[vid].config.gtx_info); + break; + + case WMI_VDEV_PARAM_GTX_MINTPC: + intr[vid].config.gtx_info.gtxTPCMin = + privcmd->param_value; + ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, + privcmd->param_vdev_id, + &intr[vid].config.gtx_info); + break; + + case WMI_VDEV_PARAM_GTX_BW_MASK: + intr[vid].config.gtx_info.gtxBWMask = + privcmd->param_value; + ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, + privcmd->param_vdev_id, + &intr[vid].config.gtx_info); + if (ret) { + WMA_LOGE("wma_vdev_set_param failed ret %d", + ret); + return; + } + break; + default: + break; + } + break; + + default: + WMA_LOGE("Invalid vpdev command id"); + } + if (1 == privcmd->param_vp_dev) { + switch (privcmd->param_id) { + case WMI_VDEV_PARAM_NSS: + intr[vid].config.nss = privcmd->param_value; + break; + case WMI_VDEV_PARAM_LDPC: + intr[vid].config.ldpc = privcmd->param_value; + break; + case WMI_VDEV_PARAM_TX_STBC: + intr[vid].config.tx_stbc = privcmd->param_value; + break; + case WMI_VDEV_PARAM_RX_STBC: + intr[vid].config.rx_stbc = privcmd->param_value; + break; + case WMI_VDEV_PARAM_SGI: + intr[vid].config.shortgi = privcmd->param_value; + break; + case WMI_VDEV_PARAM_ENABLE_RTSCTS: + intr[vid].config.rtscts_en = privcmd->param_value; + break; + case WMI_VDEV_PARAM_CHWIDTH: + intr[vid].config.chwidth = privcmd->param_value; + break; + case WMI_VDEV_PARAM_FIXED_RATE: + intr[vid].config.tx_rate = privcmd->param_value; + break; + case WMI_VDEV_PARAM_EARLY_RX_ADJUST_ENABLE: + intr[vid].config.erx_adjust = privcmd->param_value; + break; + case WMI_VDEV_PARAM_EARLY_RX_TGT_BMISS_NUM: + intr[vid].config.erx_bmiss_num = privcmd->param_value; + break; + case WMI_VDEV_PARAM_EARLY_RX_BMISS_SAMPLE_CYCLE: + intr[vid].config.erx_bmiss_cycle = privcmd->param_value; + break; + case WMI_VDEV_PARAM_EARLY_RX_SLOP_STEP: + intr[vid].config.erx_slop_step = privcmd->param_value; + break; + case WMI_VDEV_PARAM_EARLY_RX_INIT_SLOP: + intr[vid].config.erx_init_slop = privcmd->param_value; + break; + case WMI_VDEV_PARAM_EARLY_RX_ADJUST_PAUSE: + intr[vid].config.erx_adj_pause = privcmd->param_value; + break; + case WMI_VDEV_PARAM_EARLY_RX_DRIFT_SAMPLE: + intr[vid].config.erx_dri_sample = privcmd->param_value; + break; + case WMI_VDEV_PARAM_HE_DCM: + case WMI_VDEV_PARAM_HE_RANGE_EXT: + wma_set_he_vdev_param(&intr[vid], privcmd->param_id, + privcmd->param_value); + break; + default: + WMA_LOGD("Invalid wma_cli_set vdev command/Not yet implemented 0x%x", + privcmd->param_id); + break; + } + } else if (2 == privcmd->param_vp_dev) { + switch (privcmd->param_id) { + case WMI_PDEV_PARAM_ANI_ENABLE: + wma->pdevconfig.ani_enable = privcmd->param_value; + break; + case WMI_PDEV_PARAM_ANI_POLL_PERIOD: + wma->pdevconfig.ani_poll_len = privcmd->param_value; + break; + case WMI_PDEV_PARAM_ANI_LISTEN_PERIOD: + wma->pdevconfig.ani_listen_len = privcmd->param_value; + break; + case WMI_PDEV_PARAM_ANI_OFDM_LEVEL: + wma->pdevconfig.ani_ofdm_level = privcmd->param_value; + break; + case WMI_PDEV_PARAM_ANI_CCK_LEVEL: + wma->pdevconfig.ani_cck_level = privcmd->param_value; + break; + case WMI_PDEV_PARAM_DYNAMIC_BW: + wma->pdevconfig.cwmenable = privcmd->param_value; + break; + case WMI_PDEV_PARAM_CTS_CBW: + wma->pdevconfig.cts_cbw = privcmd->param_value; + break; + case WMI_PDEV_PARAM_TX_CHAIN_MASK: + wma->pdevconfig.txchainmask = privcmd->param_value; + break; + case WMI_PDEV_PARAM_RX_CHAIN_MASK: + wma->pdevconfig.rxchainmask = privcmd->param_value; + break; + case WMI_PDEV_PARAM_TXPOWER_LIMIT2G: + wma->pdevconfig.txpow2g = privcmd->param_value; + if (mac->mlme_cfg->gen.band_capability & BIT(REG_BAND_2G)) + mac->mlme_cfg->power.current_tx_power_level = + (uint8_t)privcmd->param_value; + else + WMA_LOGE("Current band is not 2G"); + break; + case WMI_PDEV_PARAM_TXPOWER_LIMIT5G: + wma->pdevconfig.txpow5g = privcmd->param_value; + if (mac->mlme_cfg->gen.band_capability & BIT(REG_BAND_5G)) + mac->mlme_cfg->power.current_tx_power_level = + (uint8_t)privcmd->param_value; + else + WMA_LOGE("Current band is not 5G"); + break; + default: + WMA_LOGD("Invalid wma_cli_set pdev command/Not yet implemented 0x%x", + privcmd->param_id); + break; + } + } else if (5 == privcmd->param_vp_dev) { + ret = wma_vdev_set_param(wma->wmi_handle, + privcmd->param_vdev_id, + WMI_VDEV_PARAM_PACKET_POWERSAVE, + pps_val); + if (ret) + WMA_LOGE("Failed to send wmi packet power save cmd"); + else + WMA_LOGD("Sent packet power save cmd %d value %x to target", + privcmd->param_id, pps_val); + } +} + +uint32_t wma_critical_events_in_flight(void) +{ + t_wma_handle *wma; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma || !wma->wmi_handle) { + WMA_LOGE("Invalid wma or wmi handle"); + return 0; + } + return wmi_critical_events_in_flight(wma->wmi_handle); +} + +/** + * wma_process_hal_pwr_dbg_cmd() - send hal pwr dbg cmd to fw. + * @handle: wma handle + * @sir_pwr_dbg_params: unit test command + * + * This function send unit test command to fw. + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error + */ +QDF_STATUS wma_process_hal_pwr_dbg_cmd(WMA_HANDLE handle, + struct sir_mac_pwr_dbg_cmd * + sir_pwr_dbg_params) +{ + tp_wma_handle wma_handle = (tp_wma_handle)handle; + int i; + struct wmi_power_dbg_params wmi_pwr_dbg_params; + QDF_STATUS status; + + if (!sir_pwr_dbg_params) { + WMA_LOGE("%s: sir_pwr_dbg_params is null", __func__); + return QDF_STATUS_E_INVAL; + } + wmi_pwr_dbg_params.module_id = sir_pwr_dbg_params->module_id; + wmi_pwr_dbg_params.pdev_id = sir_pwr_dbg_params->pdev_id; + wmi_pwr_dbg_params.num_args = sir_pwr_dbg_params->num_args; + + for (i = 0; i < wmi_pwr_dbg_params.num_args; i++) + wmi_pwr_dbg_params.args[i] = sir_pwr_dbg_params->args[i]; + + status = wmi_unified_send_power_dbg_cmd(wma_handle->wmi_handle, + &wmi_pwr_dbg_params); + + return status; +} + +static void wma_discard_fw_event(struct scheduler_msg *msg) +{ + if (!msg->bodyptr) + return; + + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + msg->bodyval = 0; + msg->type = 0; +} + +QDF_STATUS +wma_vdev_nss_chain_params_send(uint8_t vdev_id, + struct wlan_mlme_nss_chains *user_cfg) +{ + tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + struct vdev_nss_chains vdev_user_cfg; + if (!wma_handle) { + WMA_LOGE("%s: wma_handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + vdev_user_cfg.disable_rx_mrc[NSS_CHAINS_BAND_2GHZ] = + user_cfg->disable_rx_mrc[NSS_CHAINS_BAND_2GHZ]; + vdev_user_cfg.disable_tx_mrc[NSS_CHAINS_BAND_2GHZ] = + user_cfg->disable_tx_mrc[NSS_CHAINS_BAND_2GHZ]; + vdev_user_cfg.disable_rx_mrc[NSS_CHAINS_BAND_5GHZ] = + user_cfg->disable_rx_mrc[NSS_CHAINS_BAND_5GHZ]; + vdev_user_cfg.disable_tx_mrc[NSS_CHAINS_BAND_5GHZ] = + user_cfg->disable_tx_mrc[NSS_CHAINS_BAND_5GHZ]; + + vdev_user_cfg.num_rx_chains[NSS_CHAINS_BAND_2GHZ] + = user_cfg->num_rx_chains[NSS_CHAINS_BAND_2GHZ]; + vdev_user_cfg.num_tx_chains[NSS_CHAINS_BAND_2GHZ] + = user_cfg->num_tx_chains[NSS_CHAINS_BAND_2GHZ]; + vdev_user_cfg.num_rx_chains[NSS_CHAINS_BAND_5GHZ] = + user_cfg->num_rx_chains[NSS_CHAINS_BAND_5GHZ]; + vdev_user_cfg.num_tx_chains[NSS_CHAINS_BAND_5GHZ] = + user_cfg->num_tx_chains[NSS_CHAINS_BAND_5GHZ]; + + vdev_user_cfg.rx_nss[NSS_CHAINS_BAND_2GHZ] = + user_cfg->rx_nss[NSS_CHAINS_BAND_2GHZ]; + vdev_user_cfg.tx_nss[NSS_CHAINS_BAND_2GHZ] = + user_cfg->tx_nss[NSS_CHAINS_BAND_2GHZ]; + vdev_user_cfg.rx_nss[NSS_CHAINS_BAND_5GHZ] = + user_cfg->rx_nss[NSS_CHAINS_BAND_5GHZ]; + vdev_user_cfg.tx_nss[NSS_CHAINS_BAND_5GHZ] = + user_cfg->tx_nss[NSS_CHAINS_BAND_5GHZ]; + + vdev_user_cfg.num_tx_chains_11a = user_cfg->num_tx_chains_11a; + vdev_user_cfg.num_tx_chains_11b = user_cfg->num_tx_chains_11b; + vdev_user_cfg.num_tx_chains_11g = user_cfg->num_tx_chains_11g; + + return wmi_unified_vdev_nss_chain_params_send(wma_handle->wmi_handle, + vdev_id, + &vdev_user_cfg); +} + +/** + * wma_antenna_isolation_event_handler() - antenna isolation event handler + * @handle: wma handle + * @param: event data + * @len: length + * + * Return: 0 for success or error code + */ +static int wma_antenna_isolation_event_handler(void *handle, + u8 *param, + u32 len) +{ + struct scheduler_msg cds_msg = {0}; + wmi_coex_report_isolation_event_fixed_param *event; + WMI_COEX_REPORT_ANTENNA_ISOLATION_EVENTID_param_tlvs *param_buf; + struct sir_isolation_resp *pisolation; + struct mac_context *mac = NULL; + + WMA_LOGD("%s: handle %pK param %pK len %d", __func__, + handle, param, len); + + mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + WMA_LOGE("%s: Invalid mac context", __func__); + return -EINVAL; + } + + pisolation = qdf_mem_malloc(sizeof(*pisolation)); + if (!pisolation) + return 0; + + param_buf = + (WMI_COEX_REPORT_ANTENNA_ISOLATION_EVENTID_param_tlvs *)param; + if (!param_buf) { + WMA_LOGE("%s: Invalid isolation event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + pisolation->isolation_chain0 = event->isolation_chain0; + pisolation->isolation_chain1 = event->isolation_chain1; + pisolation->isolation_chain2 = event->isolation_chain2; + pisolation->isolation_chain3 = event->isolation_chain3; + + WMA_LOGD("%s: chain1 %d chain2 %d chain3 %d chain4 %d", __func__, + pisolation->isolation_chain0, pisolation->isolation_chain1, + pisolation->isolation_chain2, pisolation->isolation_chain3); + + cds_msg.type = eWNI_SME_ANTENNA_ISOLATION_RSP; + cds_msg.bodyptr = pisolation; + cds_msg.bodyval = 0; + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &cds_msg)) { + WMA_LOGE("%s: could not post peer info rsp msg to SME", + __func__); + /* free the mem and return */ + qdf_mem_free(pisolation); + } + + return 0; +} + +/** + * wma_init_max_no_of_peers - API to initialize wma configuration params + * @wma_handle: WMA Handle + * @max_peers: Max Peers supported + * + * Return: void + */ +static uint8_t wma_init_max_no_of_peers(tp_wma_handle wma_handle, + uint16_t max_peers) +{ + struct wma_ini_config *cfg = wma_get_ini_handle(wma_handle); + struct hif_opaque_softc *scn = cds_get_context(QDF_MODULE_ID_HIF); + uint32_t tgt_version = hif_get_target_info_handle(scn)->target_version; + uint8_t max_no_of_peers; + uint8_t max_supported_peers = (tgt_version == AR6320_REV1_1_VERSION) ? + MAX_SUPPORTED_PEERS_REV1_1 : MAX_SUPPORTED_PEERS_REV1_3; + + if (!cfg) { + WMA_LOGE("%s: NULL WMA ini handle", __func__); + return 0; + } + + max_no_of_peers = (max_peers > max_supported_peers) ? + max_supported_peers : max_peers; + cfg->max_no_of_peers = max_no_of_peers; + return max_no_of_peers; +} + +/** + * wma_cleanup_hold_req() - cleanup hold request queue + * @wma: wma handle + * + * Return: none + */ +static void wma_cleanup_hold_req(tp_wma_handle wma) +{ + struct wma_target_req *req_msg = NULL; + qdf_list_node_t *node1 = NULL; + + qdf_spin_lock_bh(&wma->wma_hold_req_q_lock); + if (!qdf_list_size(&wma->wma_hold_req_queue)) { + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + WMA_LOGD(FL("request queue is empty")); + return; + } + + /* peek front, and then cleanup it in wma_hold_req_timer */ + while (QDF_STATUS_SUCCESS == + qdf_list_peek_front(&wma->wma_hold_req_queue, &node1)) { + req_msg = qdf_container_of(node1, struct wma_target_req, node); + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); + /* Cleanup timeout handler */ + qdf_mc_timer_stop(&req_msg->event_timeout); + wma_hold_req_timer(req_msg); + qdf_spin_lock_bh(&wma->wma_hold_req_q_lock); + } + qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); +} + +/** + * wma_cleanup_vdev_resp_and_hold_req() - cleaunup the vdev resp and hold req + * queue + * @msg :scheduler msg + * + * Return: QDF_STATUS + */ +static QDF_STATUS +wma_cleanup_vdev_resp_and_hold_req(struct scheduler_msg *msg) +{ + tp_wma_handle wma; + + if (!msg || !msg->bodyptr) { + WMA_LOGE(FL("msg or body pointer is NULL")); + return QDF_STATUS_E_INVAL; + } + + wma = msg->bodyptr; + target_if_flush_psoc_vdev_timers(wma->psoc); + wma_cleanup_hold_req(wma); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_cleanup_vdev_resp_and_hold_req_flush_cb() - flush cb for the msg to clean + * up vdev resp and hold req + * @msg :scheduler msg + * + * As passed msg->bodyptr is wma in this case this is dummy flush cb so that + * driver doesnt try to free msg->bodyptr when this msg is flushed. + * + * Return: QDF_STATUS + */ +static inline QDF_STATUS +wma_cleanup_vdev_resp_and_hold_req_flush_cb(struct scheduler_msg *msg) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * wma_shutdown_notifier_cb - Shutdown notifer call back + * @priv : WMA handle + * + * During recovery, WMA may wait for resume to complete if the crash happens + * while in suspend. This may cause delays in completing the recovery. This call + * back would be called during recovery and the event is completed so that if + * the resume is waiting on FW to respond then it can get out of the wait so + * that recovery thread can start bringing down all the modules. + * + * Return: None + */ +static void wma_shutdown_notifier_cb(void *priv) +{ + tp_wma_handle wma_handle = priv; + struct scheduler_msg msg = { 0 }; + QDF_STATUS status; + + ucfg_pmo_psoc_wakeup_host_event_received(wma_handle->psoc); + wmi_stop(wma_handle->wmi_handle); + + msg.bodyptr = wma_handle; + msg.callback = wma_cleanup_vdev_resp_and_hold_req; + msg.flush_callback = wma_cleanup_vdev_resp_and_hold_req_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_TARGET_IF, &msg); +} + +struct wma_version_info g_wmi_version_info; + +#ifdef WLAN_FEATURE_MEMDUMP_ENABLE +/** + * wma_state_info_dump() - prints state information of wma layer + * @buf: buffer pointer + * @size: size of buffer to be filled + * + * This function is used to dump state information of wma layer + * + * Return: None + */ +static void wma_state_info_dump(char **buf_ptr, uint16_t *size) +{ + uint8_t vdev_id; + uint16_t len = 0; + t_wma_handle *wma; + char *buf = *buf_ptr; + struct wma_txrx_node *iface; + struct wake_lock_stats stats; + struct wlan_objmgr_vdev *vdev; + uint32_t rate_flag; + QDF_STATUS status; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return; + } + + WMA_LOGD("%s: size of buffer: %d", __func__, *size); + + for (vdev_id = 0; vdev_id < wma->max_bssid; vdev_id++) { + iface = &wma->interfaces[vdev_id]; + vdev = iface->vdev; + if (!vdev) + continue; + + status = wma_get_vdev_rate_flag(iface->vdev, &rate_flag); + if (QDF_IS_STATUS_ERROR(status)) + continue; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, + vdev_id, WLAN_LEGACY_WMA_ID); + if (!vdev) + continue; + ucfg_mc_cp_stats_get_vdev_wake_lock_stats(vdev, &stats); + len += qdf_scnprintf(buf + len, *size - len, + "\n" + "vdev_id %d\n" + "WoW Stats\n" + "\tpno_match %u\n" + "\tpno_complete %u\n" + "\tgscan %u\n" + "\tlow_rssi %u\n" + "\trssi_breach %u\n" + "\tucast %u\n" + "\tbcast %u\n" + "\ticmpv4 %u\n" + "\ticmpv6 %u\n" + "\tipv4_mcast %u\n" + "\tipv6_mcast %u\n" + "\tipv6_mcast_ra %u\n" + "\tipv6_mcast_ns %u\n" + "\tipv6_mcast_na %u\n" + "\toem_response %u\n" + "dtimPeriod %d\n" + "chan_width %d\n" + "vdev_active %d\n" + "vdev_up %d\n" + "aid %d\n" + "rate_flags %d\n" + "nss %d\n" + "nwType %d\n" + "tx_streams %d", + vdev_id, + stats.pno_match_wake_up_count, + stats.pno_complete_wake_up_count, + stats.gscan_wake_up_count, + stats.low_rssi_wake_up_count, + stats.rssi_breach_wake_up_count, + stats.ucast_wake_up_count, + stats.bcast_wake_up_count, + stats.icmpv4_count, + stats.icmpv6_count, + stats.ipv4_mcast_wake_up_count, + stats.ipv6_mcast_wake_up_count, + stats.ipv6_mcast_ra_stats, + stats.ipv6_mcast_ns_stats, + stats.ipv6_mcast_na_stats, + stats.oem_response_wake_up_count, + iface->dtimPeriod, + iface->chan_width, + iface->vdev_active, + wma_is_vdev_up(vdev_id), + iface->aid, + rate_flag, + iface->nss, + iface->nwType, + iface->tx_streams); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); + } + + *size -= len; + *buf_ptr += len; +} + +/** + * wma_register_debug_callback() - registration function for wma layer + * to print wma state information + */ +static void wma_register_debug_callback(void) +{ + qdf_register_debug_callback(QDF_MODULE_ID_WMA, &wma_state_info_dump); +} +#else /* WLAN_FEATURE_MEMDUMP_ENABLE */ +static void wma_register_debug_callback(void) +{ +} +#endif /* WLAN_FEATURE_MEMDUMP_ENABLE */ +/** + * wma_register_tx_ops_handler() - register tx_ops of southbound + * @tx_ops: tx_ops pointer in southbound + * + * Return: 0 on success, errno on failure + */ +static QDF_STATUS +wma_register_tx_ops_handler(struct wlan_lmac_if_tx_ops *tx_ops) +{ + /* + * Assign tx_ops, it's up to UMAC modules to declare and define these + * functions which are used to send wmi command to target. + */ + + if (!tx_ops) { + WMA_LOGE("%s: pointer to lmac if tx ops is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + /* mgmt_txrx component's tx ops */ + tx_ops->mgmt_txrx_tx_ops.mgmt_tx_send = wma_mgmt_unified_cmd_send; + + /* mgmt txrx component nbuf op for nbuf dma unmap */ + tx_ops->mgmt_txrx_tx_ops.tx_drain_nbuf_op = wma_mgmt_nbuf_unmap_cb; + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_target_if_open() - Attach UMAC modules' interface with wmi layer + * @wma_handle: wma handle + * + * Separate module defines below functions: + * 1. tgt_wmi__ api sends wmi command, assigned to south bound + * tx_ops function pointers; + * 2. module's south dispatcher handles information from lower layer, assigned + * to south bound rx_ops function pointers; + * 3. wmi event handler deals with wmi event, extracts umac needed information, + * and call rx_ops(module's dispatcher). It executes in tasklet context and + * is up to dispatcher to decide the context to reside in tasklet or in + * thread context. + * + * Return: None + */ +static void wma_target_if_open(tp_wma_handle wma_handle) +{ + struct wlan_objmgr_psoc *psoc = wma_handle->psoc; + + if (!psoc) + return; + + wlan_global_lmac_if_set_txops_registration_cb(WLAN_DEV_OL, + target_if_register_tx_ops); + wlan_lmac_if_set_umac_txops_registration_cb( + wma_register_tx_ops_handler); + wlan_global_lmac_if_open(psoc); + +} + +/** + * wma_legacy_service_ready_event_handler() - legacy (ext)service ready handler + * @event_id: event_id + * @handle: wma handle + * @event_data: event data + * @length: event length + * + * Return: 0 for success, negative error code for failure + */ +static int wma_legacy_service_ready_event_handler(uint32_t event_id, + void *handle, + uint8_t *event_data, + uint32_t length) +{ + switch (event_id) { + case wmi_service_ready_event_id: + return wma_rx_service_ready_event(handle, event_data, length); + case wmi_service_ready_ext_event_id: + return wma_rx_service_ready_ext_event(handle, event_data, + length); + case wmi_ready_event_id: + return wma_rx_ready_event(handle, event_data, length); + default: + WMA_LOGE("Legacy callback invoked with invalid event_id:%d", + event_id); + QDF_BUG(0); + } + + return 0; +} + +/** + * wma_flush_complete_evt_handler() - FW log flush complete event handler + * @handle: WMI handle + * @event: Event recevied from FW + * @len: Length of the event + * + */ +static int wma_flush_complete_evt_handler(void *handle, + u_int8_t *event, + u_int32_t len) +{ + QDF_STATUS status; + tp_wma_handle wma = (tp_wma_handle) handle; + + WMI_DEBUG_MESG_FLUSH_COMPLETE_EVENTID_param_tlvs *param_buf; + wmi_debug_mesg_flush_complete_fixed_param *wmi_event; + wmi_debug_mesg_fw_data_stall_param *data_stall_event; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + uint8_t *buf_ptr; + uint32_t reason_code; + + param_buf = (WMI_DEBUG_MESG_FLUSH_COMPLETE_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("Invalid log flush complete event buffer"); + return QDF_STATUS_E_FAILURE; + } + + wmi_event = param_buf->fixed_param; + reason_code = wmi_event->reserved0; + WMA_LOGD("Received reason code %d from FW", reason_code); + + if (reason_code == WMA_DATA_STALL_TRIGGER) { + buf_ptr = (uint8_t *)wmi_event; + buf_ptr = buf_ptr + + sizeof(wmi_debug_mesg_flush_complete_fixed_param) + + WMI_TLV_HDR_SIZE; + data_stall_event = + (wmi_debug_mesg_fw_data_stall_param *)buf_ptr; + } + + if (reason_code == WMA_DATA_STALL_TRIGGER && + ((data_stall_event->tlv_header & 0xFFFF0000) >> 16 == + WMITLV_TAG_STRUC_wmi_debug_mesg_fw_data_stall_param)) { + /** + * Log data stall info received from FW: + * + * Possible data stall recovery types: + * WLAN_DBG_DATA_STALL_RECOVERY_CONNECT_DISCONNECT + * WLAN_DBG_DATA_STALL_RECOVERY_CONNECT_MAC_PHY_RESET + * WLAN_DBG_DATA_STALL_RECOVERY_CONNECT_PDR + * + * Possible data stall event types: + * WLAN_DBG_DATA_STALL_VDEV_PAUSE + * WLAN_DBG_DATA_STALL_HWSCHED_CMD_FILTER + * WLAN_DBG_DATA_STALL_HWSCHED_CMD_FLUSH + * WLAN_DBG_DATA_STALL_RX_REFILL_FAILED + * WLAN_DBG_DATA_STALL_RX_FCS_LEN_ERROR + * + * reason_code1: + * The information stored in reason_code1 varies based on the + * data stall type values: + * + * data_stall_type | reason_code1 + * ----------------------------------------------------- + * HWSCHED_CMD_FLUSH | flush req reason (0-40) + * RX_REFILL_FAILED | ring_id (0-7) + * RX_FCS_LEN_ERROR | exact error type + * + * reasone_code2: + * on which tid/hwq stall happened + * + */ + QDF_TRACE(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, + "Data Stall event:"); + QDF_TRACE(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, + "data_stall_type: %x vdev_id_bitmap: %x reason_code1: %x reason_code2: %x recovery_type: %x ", + data_stall_event->data_stall_type, + data_stall_event->vdev_id_bitmap, + data_stall_event->reason_code1, + data_stall_event->reason_code2, + data_stall_event->recovery_type); + + cdp_post_data_stall_event(soc, + DATA_STALL_LOG_INDICATOR_FIRMWARE, + data_stall_event->data_stall_type, + OL_TXRX_PDEV_ID, + data_stall_event->vdev_id_bitmap, + data_stall_event->recovery_type); + } + + /* + * reason_code = 0; Flush event in response to flush command + * reason_code = other value; Asynchronous flush event for fatal events + */ + if (!reason_code && (cds_is_log_report_in_progress() == false)) { + WMA_LOGD("Received WMI flush event without sending CMD"); + return -EINVAL; + } else if (!reason_code && cds_is_log_report_in_progress() == true) { + /* Flush event in response to flush command */ + WMA_LOGD("Received WMI flush event in response to flush CMD"); + status = qdf_mc_timer_stop(&wma->log_completion_timer); + if (status != QDF_STATUS_SUCCESS) + WMA_LOGE("Failed to stop the log completion timeout"); + cds_logging_set_fw_flush_complete(); + return QDF_STATUS_SUCCESS; + } else if (reason_code && cds_is_log_report_in_progress() == false) { + /* Asynchronous flush event for fatal events */ + status = cds_set_log_completion(WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_FIRMWARE, + reason_code, false); + if (QDF_STATUS_SUCCESS != status) { + WMA_LOGE("%s: Failed to set log trigger params", + __func__); + return QDF_STATUS_E_FAILURE; + } + cds_logging_set_fw_flush_complete(); + return status; + } else { + /* Asynchronous flush event for fatal event, + * but, report in progress already + */ + WMA_LOGD("%s: Bug report already in progress - dropping! type:%d, indicator=%d reason_code=%d", + __func__, WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_FIRMWARE, reason_code); + return QDF_STATUS_E_FAILURE; + } + /* Asynchronous flush event for fatal event, + * but, report in progress already + */ + WMA_LOGW("%s: Bug report already in progress - dropping! type:%d, indicator=%d reason_code=%d", + __func__, WLAN_LOG_TYPE_FATAL, + WLAN_LOG_INDICATOR_FIRMWARE, reason_code); + return QDF_STATUS_E_FAILURE; +} + +#ifdef WLAN_CONV_SPECTRAL_ENABLE +/** + * wma_extract_single_phyerr_spectral() - extract single phy error from event + * @handle: wma handle + * @param evt_buf: pointer to event buffer + * @param datalen: data length of event buffer + * @param buf_offset: Pointer to hold value of current event buffer offset + * post extraction + * @param phyerr: Pointer to hold phyerr + * + * Return: QDF_STATUS + */ +static QDF_STATUS wma_extract_single_phyerr_spectral(void *handle, + void *evt_buf, + uint16_t datalen, uint16_t *buf_offset, + wmi_host_phyerr_t *phyerr) +{ + wmi_single_phyerr_rx_event *ev; + int n = *buf_offset; + + ev = (wmi_single_phyerr_rx_event *)((uint8_t *)evt_buf + n); + + if (n < datalen) { + /* ensure there's at least space for the header */ + if ((datalen - n) < sizeof(ev->hdr)) { + WMA_LOGE("%s: not enough space? (datalen=%d, n=%d, hdr=%zu bytes", + __func__, datalen, n, sizeof(ev->hdr)); + return QDF_STATUS_E_FAILURE; + } + + phyerr->bufp = ev->bufp; + phyerr->buf_len = ev->hdr.buf_len; + + /* + * Sanity check the buffer length of the event against + * what we currently have. + * + * Since buf_len is 32 bits, we check if it overflows + * a large 32 bit value. It's not 0x7fffffff because + * we increase n by (buf_len + sizeof(hdr)), which would + * in itself cause n to overflow. + * + * If "int" is 64 bits then this becomes a moot point. + */ + if (ev->hdr.buf_len > 0x7f000000) { + WMA_LOGE("%s: buf_len is garbage? (0x%x)", + __func__, ev->hdr.buf_len); + return QDF_STATUS_E_FAILURE; + } + if (n + ev->hdr.buf_len > datalen) { + WMA_LOGE("%s: buf_len exceeds available space n=%d, buf_len=%d, datalen=%d", + __func__, n, ev->hdr.buf_len, datalen); + return QDF_STATUS_E_FAILURE; + } + + phyerr->phy_err_code = WMI_UNIFIED_PHYERRCODE_GET(&ev->hdr); + phyerr->tsf_timestamp = ev->hdr.tsf_timestamp; + +#ifdef DEBUG_SPECTRAL_SCAN + WMA_LOGD("%s: len=%d, tsf=0x%08x, rssi = 0x%x/0x%x/0x%x/0x%x, comb rssi = 0x%x, phycode=%d", + __func__, + ev->hdr.buf_len, + ev->hdr.tsf_timestamp, + ev->hdr.rssi_chain0, + ev->hdr.rssi_chain1, + ev->hdr.rssi_chain2, + ev->hdr.rssi_chain3, + WMI_UNIFIED_RSSI_COMB_GET(&ev->hdr), + phyerr->phy_err_code); + + /* + * For now, unroll this loop - the chain 'value' field isn't + * a variable but glued together into a macro field definition. + * Grr. :-) + */ + WMA_LOGD("%s: chain 0: raw=0x%08x; pri20=%d sec20=%d sec40=%d sec80=%d", + __func__, + ev->hdr.rssi_chain0, + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 0, PRI20), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 0, SEC20), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 0, SEC40), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 0, SEC80)); + + WMA_LOGD("%s: chain 1: raw=0x%08x: pri20=%d sec20=%d sec40=%d sec80=%d", + __func__, + ev->hdr.rssi_chain1, + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 1, PRI20), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 1, SEC20), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 1, SEC40), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 1, SEC80)); + + WMA_LOGD("%s: chain 2: raw=0x%08x: pri20=%d sec20=%d sec40=%d sec80=%d", + __func__, + ev->hdr.rssi_chain2, + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 2, PRI20), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 2, SEC20), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 2, SEC40), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 2, SEC80)); + + WMA_LOGD("%s: chain 3: raw=0x%08x: pri20=%d sec20=%d sec40=%d sec80=%d", + __func__, + ev->hdr.rssi_chain3, + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 3, PRI20), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 3, SEC20), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 3, SEC40), + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 3, SEC80)); + + + WMA_LOGD("%s: freq_info_1=0x%08x, freq_info_2=0x%08x", + __func__, ev->hdr.freq_info_1, ev->hdr.freq_info_2); + + /* + * The NF chain values are signed and are negative - hence + * the cast evilness. + */ + WMA_LOGD("%s: nfval[1]=0x%08x, nfval[2]=0x%08x, nf=%d/%d/%d/%d, freq1=%d, freq2=%d, cw=%d", + __func__, + ev->hdr.nf_list_1, + ev->hdr.nf_list_2, + (int) WMI_UNIFIED_NF_CHAIN_GET(&ev->hdr, 0), + (int) WMI_UNIFIED_NF_CHAIN_GET(&ev->hdr, 1), + (int) WMI_UNIFIED_NF_CHAIN_GET(&ev->hdr, 2), + (int) WMI_UNIFIED_NF_CHAIN_GET(&ev->hdr, 3), + WMI_UNIFIED_FREQ_INFO_GET(&ev->hdr, 1), + WMI_UNIFIED_FREQ_INFO_GET(&ev->hdr, 2), + WMI_UNIFIED_CHWIDTH_GET(&ev->hdr)); +#endif + + /* + * If required, pass spectral events to the spectral module + */ + if (ev->hdr.buf_len > 0) { + + /* Initialize the NF values to Zero. */ + phyerr->rf_info.noise_floor[0] = + WMI_UNIFIED_NF_CHAIN_GET(&ev->hdr, 0); + phyerr->rf_info.noise_floor[1] = + WMI_UNIFIED_NF_CHAIN_GET(&ev->hdr, 1); + phyerr->rf_info.noise_floor[2] = + WMI_UNIFIED_NF_CHAIN_GET(&ev->hdr, 2); + phyerr->rf_info.noise_floor[3] = + WMI_UNIFIED_NF_CHAIN_GET(&ev->hdr, 3); + + /* populate the rf info */ + phyerr->rf_info.rssi_comb = + WMI_UNIFIED_RSSI_COMB_GET(&ev->hdr); + + /* Need to unroll loop due to macro + * constraints chain 0 + */ + phyerr->rf_info.pc_rssi_info[0].rssi_pri20 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 0, PRI20); + phyerr->rf_info.pc_rssi_info[0].rssi_sec20 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 0, SEC20); + phyerr->rf_info.pc_rssi_info[0].rssi_sec40 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 0, SEC40); + phyerr->rf_info.pc_rssi_info[0].rssi_sec80 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 0, SEC80); + + /* chain 1 */ + phyerr->rf_info.pc_rssi_info[1].rssi_pri20 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 1, PRI20); + phyerr->rf_info.pc_rssi_info[1].rssi_sec20 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 1, SEC20); + phyerr->rf_info.pc_rssi_info[1].rssi_sec40 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 1, SEC40); + phyerr->rf_info.pc_rssi_info[1].rssi_sec80 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 1, SEC80); + + /* chain 2 */ + phyerr->rf_info.pc_rssi_info[2].rssi_pri20 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 2, PRI20); + phyerr->rf_info.pc_rssi_info[2].rssi_sec20 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 2, SEC20); + phyerr->rf_info.pc_rssi_info[2].rssi_sec40 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 2, SEC40); + phyerr->rf_info.pc_rssi_info[2].rssi_sec80 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 2, SEC80); + + /* chain 3 */ + phyerr->rf_info.pc_rssi_info[3].rssi_pri20 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 3, PRI20); + phyerr->rf_info.pc_rssi_info[3].rssi_sec20 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 3, SEC20); + phyerr->rf_info.pc_rssi_info[3].rssi_sec40 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 3, SEC40); + phyerr->rf_info.pc_rssi_info[3].rssi_sec80 = + WMI_UNIFIED_RSSI_CHAN_GET(&ev->hdr, 3, SEC80); + + phyerr->chan_info.center_freq1 = + WMI_UNIFIED_FREQ_INFO_GET(&ev->hdr, 1); + phyerr->chan_info.center_freq2 = + WMI_UNIFIED_FREQ_INFO_GET(&ev->hdr, 2); + + } + + /* + * Advance the buffer pointer to the next PHY error. + * buflen is the length of this payload, so we need to + * advance past the current header _AND_ the payload. + */ + n += sizeof(*ev) + ev->hdr.buf_len; + } + *buf_offset += n; + + return QDF_STATUS_SUCCESS; +} + +/** + * spectral_phyerr_event_handler() - spectral phyerr event handler + * @handle: wma handle + * @data: data buffer + * @datalen: buffer length + * + * Return: QDF_STATUS + */ +static QDF_STATUS spectral_phyerr_event_handler(void *handle, + uint8_t *data, + uint32_t datalen) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint16_t buf_offset, event_buf_len = 0; + wmi_single_phyerr_rx_event *ev; + wmi_host_phyerr_t phyerr; + struct target_if_spectral_rfqual_info rfqual_info; + struct target_if_spectral_chan_info chan_info; + struct target_if_spectral_acs_stats acs_stats; + + if (!wma) { + WMA_LOGE("%s:wma handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + memset(&phyerr, 0, sizeof(wmi_host_phyerr_t)); + status = wmi_extract_comb_phyerr(wma->wmi_handle, data, datalen, + &buf_offset, &phyerr); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: extract comb phyerr failed", __func__); + return QDF_STATUS_E_FAILURE; + } + + ev = (wmi_single_phyerr_rx_event *)phyerr.bufp; + event_buf_len = phyerr.buf_len; + /* Loop over the bufp, extracting out phyerrors */ + buf_offset = 0; + while (buf_offset < event_buf_len) { + if (wma_extract_single_phyerr_spectral(handle, ev, + event_buf_len, &buf_offset, &phyerr)) { + WMA_LOGE("%s: extract single phy err failed", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (phyerr.buf_len > 0) { + if (sizeof(phyerr.rf_info) > sizeof(rfqual_info)) + qdf_mem_copy(&rfqual_info, &phyerr.rf_info, + sizeof(rfqual_info)); + else + qdf_mem_copy(&rfqual_info, &phyerr.rf_info, + sizeof(phyerr.rf_info)); + + if (sizeof(phyerr.chan_info) > sizeof(chan_info)) + qdf_mem_copy(&chan_info, &phyerr.chan_info, + sizeof(chan_info)); + else + qdf_mem_copy(&chan_info, &phyerr.chan_info, + sizeof(phyerr.chan_info)); + + target_if_spectral_process_phyerr(wma->pdev, phyerr.bufp, + phyerr.buf_len, + &rfqual_info, + &chan_info, + phyerr.tsf64, + &acs_stats); + } + } + + return status; +} +#else +static QDF_STATUS +wma_extract_single_phyerr_spectral(void *handle, void *evt_buf, + uint16_t datalen, + uint16_t *buf_offset, + wmi_host_phyerr_t *phyerr) +{ + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS spectral_phyerr_event_handler(void *handle, + uint8_t *data, uint32_t datalen) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * dfs_phyerr_event_handler() - dfs phyerr event handler + * @handle: wma handle + * @data: data buffer + * @datalen: buffer length + * @fulltsf: 64 bit event TSF + * + * Function to process DFS phy errors. + * + * Return: QDF_STATUS + */ +static QDF_STATUS dfs_phyerr_event_handler(tp_wma_handle handle, + uint8_t *data, + uint32_t datalen, + uint64_t fulltsf) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_lmac_if_dfs_rx_ops *dfs_rx_ops; + wmi_host_phyerr_t phyerr; + int8_t rssi_comb; + uint16_t buf_offset; + + if (!handle->psoc) { + WMA_LOGE("%s: psoc is null", __func__); + return QDF_STATUS_E_INVAL; + } + + dfs_rx_ops = wlan_lmac_if_get_dfs_rx_ops(handle->psoc); + if (!dfs_rx_ops) { + WMA_LOGE("%s: dfs_rx_ops is null", __func__); + return QDF_STATUS_E_INVAL; + } + + if (!dfs_rx_ops->dfs_process_phyerr) { + WMA_LOGE("%s: dfs_process_phyerr handler is null", __func__); + return QDF_STATUS_E_INVAL; + } + + if (!handle->pdev) { + WMA_LOGE("%s: pdev is null", __func__); + return -EINVAL; + } + + buf_offset = 0; + while (buf_offset < datalen) { + status = wmi_extract_single_phyerr(handle->wmi_handle, data, datalen, + &buf_offset, &phyerr); + if (QDF_IS_STATUS_ERROR(status)) { + /* wmi_extract_single_phyerr has logs */ + return status; + } + + rssi_comb = phyerr.rf_info.rssi_comb & 0xFF; + if (phyerr.buf_len > 0) + dfs_rx_ops->dfs_process_phyerr(handle->pdev, + &phyerr.bufp[0], + phyerr.buf_len, + rssi_comb, + rssi_comb, + phyerr.tsf_timestamp, + fulltsf); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_unified_phyerr_rx_event_handler() - phyerr event handler + * @handle: wma handle + * @data: data buffer + * @datalen: buffer length + * + * WMI Handler for WMI_PHYERR_EVENTID event from firmware. + * This handler is currently handling DFS and spectral scan + * phy errors. + * + * Return: 0 for success, other value for failure + */ +static int wma_unified_phyerr_rx_event_handler(void *handle, + uint8_t *data, + uint32_t datalen) +{ + /* phyerr handling is moved to cmn project + * As WIN still uses handler registration in non-cmn code. + * need complete testing of non offloaded DFS code before we enable + * it in cmn code. + **/ + tp_wma_handle wma = (tp_wma_handle) handle; + QDF_STATUS status = QDF_STATUS_SUCCESS; + wmi_host_phyerr_t phyerr; + uint16_t buf_offset = 0; + wmi_single_phyerr_rx_event *ev; + uint16_t event_buf_len = 0; + wmi_host_phyerr_t phyerr2; + bool spectralscan = false; + + if (!wma) { + WMA_LOGE("%s: wma handle is null", __func__); + return -EINVAL; + } + + /* sanity check on data length */ + status = wmi_extract_comb_phyerr(wma->wmi_handle, data, datalen, + &buf_offset, &phyerr); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: extract phyerr failed: %d", __func__, status); + return qdf_status_to_os_return(status); + } + ev = (wmi_single_phyerr_rx_event *)phyerr.bufp; + event_buf_len = phyerr.buf_len; + /* Loop over the bufp, extracting out phyerrors */ + buf_offset = 0; + while (ev && (buf_offset < event_buf_len)) { + if (wma_extract_single_phyerr_spectral(handle, ev, + event_buf_len, + &buf_offset, + &phyerr2)) { + WMA_LOGE("%s: extract single phy err failed", __func__); + return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); + } + if ((buf_offset != 0) && (phyerr2.phy_err_code == 0x26 || + phyerr2.phy_err_code == 0x24)) { + spectralscan = true; + } else { + break; + } + } + if (spectralscan) { + status = spectral_phyerr_event_handler(wma, data, datalen); + return qdf_status_to_os_return(status); + } + /* handle different PHY Error conditions */ + if (((phyerr.phy_err_mask0 & (WMI_PHY_ERROR_MASK0_RADAR | + WMI_PHY_ERROR_MASK0_FALSE_RADAR_EXT | + WMI_PHY_ERROR_MASK0_SPECTRAL_SCAN)) == 0)) { + WMA_LOGD("%s: Unknown phy error event", __func__); + return -EINVAL; + } + + /* Handle Spectral or DFS PHY Error */ + if (phyerr.phy_err_mask0 & (WMI_PHY_ERROR_MASK0_RADAR | + WMI_PHY_ERROR_MASK0_FALSE_RADAR_EXT)) { + if (wma->is_dfs_offloaded) { + WMA_LOGD("%s: Unexpected phy error, dfs offloaded", + __func__); + return -EINVAL; + } + status = dfs_phyerr_event_handler(wma, + phyerr.bufp, + phyerr.buf_len, + phyerr.tsf64); + } else if (phyerr.phy_err_mask0 & (WMI_PHY_ERROR_MASK0_SPECTRAL_SCAN | + WMI_PHY_ERROR_MASK0_FALSE_RADAR_EXT)) { + status = spectral_phyerr_event_handler(wma, data, datalen); + } + + return qdf_status_to_os_return(status); +} + +void wma_vdev_init(struct wma_txrx_node *vdev) +{ + qdf_wake_lock_create(&vdev->vdev_set_key_wakelock, "vdev_set_key"); + qdf_runtime_lock_init(&vdev->vdev_set_key_runtime_wakelock); + vdev->is_waiting_for_key = false; +} + +void wma_vdev_deinit(struct wma_txrx_node *vdev) +{ + struct beacon_info *bcn; + tp_wma_handle wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + /* validate the wma_handle */ + if (!wma_handle) { + WMA_LOGE("%s: Invalid wma handle", __func__); + return; + } + + bcn = vdev->beacon; + if (bcn) { + if (bcn->dma_mapped) + qdf_nbuf_unmap_single(wma_handle->qdf_dev, + bcn->buf, QDF_DMA_TO_DEVICE); + qdf_nbuf_free(bcn->buf); + qdf_mem_free(bcn); + vdev->beacon = NULL; + } + + if (vdev->vdev_active == true) + vdev->vdev_active = false; + + if (vdev->addBssStaContext) { + qdf_mem_free(vdev->addBssStaContext); + vdev->addBssStaContext = NULL; + } + + if (vdev->staKeyParams) { + qdf_mem_free(vdev->staKeyParams); + vdev->staKeyParams = NULL; + } + + if (vdev->psnr_req) { + qdf_mem_free(vdev->psnr_req); + vdev->psnr_req = NULL; + } + + if (vdev->rcpi_req) { + qdf_mem_free(vdev->rcpi_req); + vdev->rcpi_req = NULL; + } + + if (vdev->roam_scan_stats_req) { + struct sir_roam_scan_stats *req; + + req = vdev->roam_scan_stats_req; + vdev->roam_scan_stats_req = NULL; + qdf_mem_free(req); + } + + if (vdev->roam_synch_frame_ind.bcn_probe_rsp) { + qdf_mem_free(vdev->roam_synch_frame_ind.bcn_probe_rsp); + vdev->roam_synch_frame_ind.bcn_probe_rsp = NULL; + } + + if (vdev->roam_synch_frame_ind.reassoc_req) { + qdf_mem_free(vdev->roam_synch_frame_ind.reassoc_req); + vdev->roam_synch_frame_ind.reassoc_req = NULL; + } + + if (vdev->roam_synch_frame_ind.reassoc_rsp) { + qdf_mem_free(vdev->roam_synch_frame_ind.reassoc_rsp); + vdev->roam_synch_frame_ind.reassoc_rsp = NULL; + } + + qdf_runtime_lock_deinit(&vdev->vdev_set_key_runtime_wakelock); + qdf_wake_lock_destroy(&vdev->vdev_set_key_wakelock); + vdev->is_waiting_for_key = false; +} + +/** + * wma_wmi_stop() - generic function to block WMI commands + * @return: None + */ +void wma_wmi_stop(void) +{ + tp_wma_handle wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if ((!wma_handle) || (!wma_handle->wmi_handle)) { + QDF_TRACE(QDF_MODULE_ID_WMI, QDF_TRACE_LEVEL_INFO, + "wma_handle or wmi_handle is NULL\n"); + return; + } + wmi_stop(wma_handle->wmi_handle); +} + +#ifdef WLAN_WMI_BCN +static QDF_STATUS +wma_register_swba_events(wmi_unified_t wmi_handle) +{ + QDF_STATUS status; + + status = wmi_unified_register_event_handler(wmi_handle, + wmi_host_swba_event_id, + wma_beacon_swba_handler, + WMA_RX_SERIALIZER_CTX); + + return status; +} +#else +static QDF_STATUS wma_register_swba_events(wmi_unified_t wmi_handle) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +#ifdef FEATURE_WLAN_APF +static void wma_register_apf_events(tp_wma_handle wma_handle) +{ + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_WMI, QDF_TRACE_LEVEL_INFO, + "wma_handle is NULL\n"); + return; + } + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_apf_capability_info_event_id, + wma_get_apf_caps_event_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_apf_get_vdev_work_memory_resp_event_id, + wma_apf_read_work_memory_event_handler, + WMA_RX_SERIALIZER_CTX); +} +#else /* FEATURE_WLAN_APF */ +static void wma_register_apf_events(tp_wma_handle wma_handle) +{ +} +#endif /* FEATURE_WLAN_APF */ + +#ifdef WLAN_FEATURE_MOTION_DETECTION +/** + * wma_register_md_events - Register motion detection event handlers + * @wma_handle: wma handle + * Return: None + */ +static void wma_register_md_events(tp_wma_handle wma_handle) +{ + if (!wma_handle) { + QDF_TRACE(QDF_MODULE_ID_WMI, QDF_TRACE_LEVEL_INFO, + "wma_handle is NULL\n"); + return; + } + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_motion_det_host_eventid, + wma_motion_det_host_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler( + wma_handle->wmi_handle, + wmi_motion_det_base_line_host_eventid, + wma_motion_det_base_line_host_event_handler, + WMA_RX_SERIALIZER_CTX); +} +#else /* WLAN_FEATURE_MOTION_DETECTION */ +/** + * wma_register_md_events - Register motion detection event handlers + * @wma_handle: wma handle + * Return: None + */ +static void wma_register_md_events(tp_wma_handle wma_handle) +{ +} +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +#ifdef FEATURE_WLM_STATS +static void wma_register_wlm_stats_events(tp_wma_handle wma_handle) +{ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_wlm_stats_event_id, + wma_wlm_stats_rsp, + WMA_RX_SERIALIZER_CTX); +} +#else /* FEATURE_WLM_STATS */ +static void wma_register_wlm_stats_events(tp_wma_handle wma_handle) +{ +} +#endif /* FEATURE_WLM_STATS */ + +struct wlan_objmgr_psoc *wma_get_psoc_from_scn_handle(void *scn_handle) +{ + tp_wma_handle wma_handle; + + if (!scn_handle) { + WMA_LOGE("invalid scn handle"); + return NULL; + } + wma_handle = (tp_wma_handle)scn_handle; + + return wma_handle->psoc; +} + +void wma_get_fw_phy_mode_for_freq_cb(uint32_t freq, uint32_t chan_width, + uint32_t *phy_mode) +{ + uint32_t dot11_mode; + enum wlan_phymode host_phy_mode; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + wma_err("MAC context is NULL"); + *phy_mode = WLAN_PHYMODE_AUTO; + return; + } + + dot11_mode = mac->mlme_cfg->dot11_mode.dot11_mode; + host_phy_mode = wma_chan_phy_mode(freq, chan_width, dot11_mode); + *phy_mode = wma_host_to_fw_phymode(host_phy_mode); +} + +void wma_get_phy_mode_cb(uint8_t chan, uint32_t chan_width, + enum wlan_phymode *phy_mode) +{ + uint32_t dot11_mode; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + uint32_t freq; + + if (!mac) { + wma_err("MAC context is NULL"); + *phy_mode = WLAN_PHYMODE_AUTO; + return; + } + + freq = wlan_reg_chan_to_freq(mac->pdev, chan); + dot11_mode = mac->mlme_cfg->dot11_mode.dot11_mode; + *phy_mode = wma_chan_phy_mode(freq, chan_width, dot11_mode); +} + +#ifdef WLAN_FEATURE_NAN +static void +wma_register_nan_callbacks(tp_wma_handle wma_handle) +{ + struct nan_callbacks cb_obj = {0}; + + cb_obj.update_ndi_conn = wma_ndi_update_connection_info; + + ucfg_nan_register_wma_callbacks(wma_handle->psoc, &cb_obj); +} +#else +static void wma_register_nan_callbacks(tp_wma_handle wma_handle) +{ +} +#endif + +/** + * wma_open() - Allocate wma context and initialize it. + * @cds_context: cds context + * @wma_tgt_cfg_cb: tgt config callback fun + * @radar_ind_cb: dfs radar indication callback + * @cds_cfg: mac parameters + * + * Return: 0 on success, errno on failure + */ +QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc, + wma_tgt_cfg_cb tgt_cfg_cb, + struct cds_config_info *cds_cfg, + uint32_t target_type) +{ + tp_wma_handle wma_handle; + HTC_HANDLE htc_handle; + qdf_device_t qdf_dev; + void *wmi_handle; + QDF_STATUS qdf_status; + struct wmi_unified_attach_params *params; + struct policy_mgr_wma_cbacks wma_cbacks; + struct target_psoc_info *tgt_psoc_info; + int i; + bool val = 0; + void *cds_context; + target_resource_config *wlan_res_cfg; + uint32_t self_gen_frm_pwr = 0; + + WMA_LOGD("%s: Enter", __func__); + + cds_context = cds_get_global_context(); + if (!cds_context) { + WMA_LOGE("%s: Invalid CDS context", __func__); + return QDF_STATUS_E_INVAL; + } + + g_wmi_version_info.major = __WMI_VER_MAJOR_; + g_wmi_version_info.minor = __WMI_VER_MINOR_; + g_wmi_version_info.revision = __WMI_REVISION_; + + qdf_dev = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + htc_handle = cds_get_context(QDF_MODULE_ID_HTC); + + if (!htc_handle) { + WMA_LOGE("%s: Invalid HTC handle", __func__); + return QDF_STATUS_E_INVAL; + } + + /* Alloc memory for WMA Context */ + qdf_status = cds_alloc_context(QDF_MODULE_ID_WMA, + (void **)&wma_handle, + sizeof(*wma_handle)); + + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Memory allocation failed for wma_handle", + __func__); + return qdf_status; + } + + qdf_mem_zero(wma_handle, sizeof(t_wma_handle)); + + if (target_if_alloc_psoc_tgt_info(psoc)) { + WMA_LOGE("%s target psoc info allocation failed", __func__); + qdf_status = QDF_STATUS_E_NOMEM; + goto err_free_wma_handle; + } + + if (cds_get_conparam() != QDF_GLOBAL_FTM_MODE) { +#ifdef FEATURE_WLAN_EXTSCAN + qdf_wake_lock_create(&wma_handle->extscan_wake_lock, + "wlan_extscan_wl"); +#endif /* FEATURE_WLAN_EXTSCAN */ + qdf_wake_lock_create(&wma_handle->wow_wake_lock, + "wlan_wow_wl"); + qdf_wake_lock_create(&wma_handle->wow_auth_req_wl, + "wlan_auth_req_wl"); + qdf_wake_lock_create(&wma_handle->wow_assoc_req_wl, + "wlan_assoc_req_wl"); + qdf_wake_lock_create(&wma_handle->wow_deauth_rec_wl, + "wlan_deauth_rec_wl"); + qdf_wake_lock_create(&wma_handle->wow_disassoc_rec_wl, + "wlan_disassoc_rec_wl"); + qdf_wake_lock_create(&wma_handle->wow_ap_assoc_lost_wl, + "wlan_ap_assoc_lost_wl"); + qdf_wake_lock_create(&wma_handle->wow_auto_shutdown_wl, + "wlan_auto_shutdown_wl"); + qdf_wake_lock_create(&wma_handle->roam_ho_wl, + "wlan_roam_ho_wl"); + qdf_wake_lock_create(&wma_handle->roam_preauth_wl, + "wlan_roam_preauth_wl"); + } + + qdf_status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_LEGACY_WMA_ID); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE("%s: PSOC get_ref fails", __func__); + goto err_get_psoc_ref; + } + wma_handle->psoc = psoc; + + wma_target_if_open(wma_handle); + + /* + * Allocate locally used params with its rx_ops member, + * and free it immediately after used. + */ + params = qdf_mem_malloc(sizeof(*params)); + if (!params) { + qdf_status = QDF_STATUS_E_NOMEM; + goto err_wma_handle; + } + + params->osdev = NULL; + params->target_type = WMI_TLV_TARGET; + params->use_cookie = false; + params->psoc = psoc; + params->max_commands = WMI_MAX_CMDS; + + /* initialize tlv attach */ + wmi_tlv_init(); + + /* attach the wmi */ + wmi_handle = wmi_unified_attach(wma_handle, params); + qdf_mem_free(params); + if (!wmi_handle) { + WMA_LOGE("%s: failed to attach WMI", __func__); + qdf_status = QDF_STATUS_E_NOMEM; + goto err_wma_handle; + } + + target_if_register_legacy_service_ready_cb( + wma_legacy_service_ready_event_handler); + + WMA_LOGA("WMA --> wmi_unified_attach - success"); + + /* store the wmi handle in tgt_if_handle */ + tgt_psoc_info = wlan_psoc_get_tgt_if_handle(psoc); + + target_psoc_set_target_type(tgt_psoc_info, target_type); + /* Save the WMI & HTC handle */ + target_psoc_set_wmi_hdl(tgt_psoc_info, wmi_handle); + wma_handle->wmi_handle = wmi_handle; + target_psoc_set_htc_hdl(tgt_psoc_info, htc_handle); + wma_handle->cds_context = cds_context; + wma_handle->qdf_dev = qdf_dev; + wma_handle->enable_tx_compl_tsf64 = + cds_cfg->enable_tx_compl_tsf64; + + /* Register Converged Event handlers */ + init_deinit_register_tgt_psoc_ev_handlers(psoc); + + /* Initialize max_no_of_peers for wma_get_number_of_peers_supported() */ + cds_cfg->max_station = wma_init_max_no_of_peers(wma_handle, + cds_cfg->max_station); + + wlan_mlme_set_assoc_sta_limit(psoc, cds_cfg->max_station); + + /* initialize default target config */ + wlan_res_cfg = target_psoc_get_wlan_res_cfg(tgt_psoc_info); + if (!wlan_res_cfg) { + WMA_LOGE("%s: wlan_res_cfg is null", __func__); + qdf_status = QDF_STATUS_E_NOMEM; + goto err_wma_handle; + } + + wma_set_default_tgt_config(wma_handle, wlan_res_cfg, cds_cfg); + + qdf_status = wlan_mlme_get_tx_chainmask_cck(psoc, &val); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Failed to get tx_chainmask_cck", __func__); + qdf_status = QDF_STATUS_E_FAILURE; + goto err_wma_handle; + } + wma_handle->tx_chain_mask_cck = val; + + qdf_status = wlan_mlme_get_self_gen_frm_pwr(psoc, &self_gen_frm_pwr); + if (qdf_status != QDF_STATUS_SUCCESS) + WMA_LOGE("%s: Failed to get self_gen_frm_pwr", __func__); + wma_handle->self_gen_frm_pwr = self_gen_frm_pwr; + + cds_cfg->max_bssid = WLAN_MAX_VDEVS; + + wma_handle->max_station = cds_cfg->max_station; + wma_handle->max_bssid = cds_cfg->max_bssid; + wma_handle->enable_mc_list = + ucfg_pmo_is_mc_addr_list_enabled(wma_handle->psoc); + wma_handle->active_uc_apf_mode = + ucfg_pmo_get_active_uc_apf_mode(wma_handle->psoc); + wma_handle->active_mc_bc_apf_mode = + ucfg_pmo_get_active_mc_bc_apf_mode(wma_handle->psoc); + wma_handle->link_stats_results = NULL; +#ifdef WLAN_FEATURE_LPSS + wma_handle->is_lpass_enabled = cds_cfg->is_lpass_enabled; +#endif + wma_handle->interfaces = qdf_mem_malloc(sizeof(struct wma_txrx_node) * + wma_handle->max_bssid); + if (!wma_handle->interfaces) { + qdf_status = QDF_STATUS_E_NOMEM; + goto err_scn_context; + } + + for (i = 0; i < wma_handle->max_bssid; ++i) + wma_vdev_init(&wma_handle->interfaces[i]); + + /* Register the debug print event handler */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_debug_print_event_id, + wma_unified_debug_print_event_handler, + WMA_RX_SERIALIZER_CTX); + /* Register profiling event Handler */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_wlan_profile_data_event_id, + wma_profile_data_report_event_handler, + WMA_RX_SERIALIZER_CTX); + + wma_handle->tgt_cfg_update_cb = tgt_cfg_cb; + wma_handle->old_hw_mode_index = WMA_DEFAULT_HW_MODE_INDEX; + wma_handle->new_hw_mode_index = WMA_DEFAULT_HW_MODE_INDEX; + wma_handle->saved_chan.num_channels = 0; + wma_handle->fw_timeout_crash = cds_cfg->fw_timeout_crash; + + qdf_status = qdf_mc_timer_init(&wma_handle->service_ready_ext_timer, + QDF_TIMER_TYPE_SW, + wma_service_ready_ext_evt_timeout, + wma_handle); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("Failed to initialize service ready ext timeout"); + goto err_event_init; + } + + qdf_status = qdf_event_create(&wma_handle->target_suspend); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: target suspend event initialization failed", + __func__); + goto err_event_init; + } + + /* Init Tx Frame Complete event */ + qdf_status = qdf_event_create(&wma_handle->tx_frm_download_comp_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("%s: failed to init tx_frm_download_comp_event", + __func__); + goto err_event_init; + } + + /* Init tx queue empty check event */ + qdf_status = qdf_event_create(&wma_handle->tx_queue_empty_event); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("%s: failed to init tx_queue_empty_event", __func__); + goto err_event_init; + } + + qdf_status = cds_shutdown_notifier_register(wma_shutdown_notifier_cb, + wma_handle); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Shutdown notifier register failed: %d", + __func__, qdf_status); + goto err_event_init; + } + + qdf_status = qdf_event_create(&wma_handle->runtime_suspend); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: runtime_suspend event initialization failed", + __func__); + goto err_event_init; + } + + qdf_status = qdf_event_create(&wma_handle->recovery_event); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: recovery event initialization failed", __func__); + goto err_event_init; + } + + qdf_list_create(&wma_handle->wma_hold_req_queue, + MAX_ENTRY_HOLD_REQ_QUEUE); + qdf_spinlock_create(&wma_handle->wma_hold_req_q_lock); + qdf_atomic_init(&wma_handle->is_wow_bus_suspended); + + /* register for STA kickout function */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_peer_sta_kickout_event_id, + wma_peer_sta_kickout_event_handler, + WMA_RX_SERIALIZER_CTX); + + /* register for stats response event */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_get_arp_stats_req_id, + wma_get_arp_stats_handler, + WMA_RX_SERIALIZER_CTX); + + /* register for fw state response event */ + wma_register_fw_state_events(wma_handle->wmi_handle); + + /* register for peer info response event */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_peer_stats_info_event_id, + wma_peer_info_event_handler, + WMA_RX_SERIALIZER_CTX); + +#ifdef WLAN_POWER_DEBUG + /* register for Chip Power stats event */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_pdev_chip_power_stats_event_id, + wma_unified_power_debug_stats_event_handler, + WMA_RX_SERIALIZER_CTX); +#endif +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS + /* register for beacon stats event */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_vdev_bcn_reception_stats_event_id, + wma_unified_beacon_debug_stats_event_handler, + WMA_RX_SERIALIZER_CTX); +#endif + + /* register for linkspeed response event */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_peer_estimated_linkspeed_event_id, + wma_link_speed_event_handler, + WMA_RX_SERIALIZER_CTX); + +#ifdef FEATURE_OEM_DATA_SUPPORT + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_oem_response_event_id, + wma_oem_data_response_handler, + WMA_RX_SERIALIZER_CTX); +#endif /* FEATURE_OEM_DATA_SUPPORT */ + + /* Register peer change event handler */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_peer_state_event_id, + wma_peer_state_change_event_handler, + WMA_RX_WORK_CTX); + + /* Register beacon tx complete event id. The event is required + * for sending channel switch announcement frames + */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_offload_bcn_tx_status_event_id, + wma_unified_bcntx_status_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_update_vdev_rate_stats_event_id, + wma_link_status_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_roam_scan_stats_event_id, + wma_roam_scan_stats_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_pdev_cold_boot_cal_event_id, + wma_cold_boot_cal_event_handler, + WMA_RX_WORK_CTX); + +#ifdef FEATURE_OEM_DATA + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_oem_data_event_id, + wma_oem_event_handler, + WMA_RX_WORK_CTX); +#endif + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + /* Register event handler for processing Link Layer Stats + * response from the FW + */ + wma_register_ll_stats_event_handler(wma_handle); + +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + + wmi_set_tgt_assert(wma_handle->wmi_handle, + cds_cfg->force_target_assert_enabled); + /* Firmware debug log */ + qdf_status = dbglog_init(wma_handle->wmi_handle); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Firmware Dbglog initialization failed", __func__); + goto err_dbglog_init; + } + + /* + * Update Powersave mode + * 1 - Legacy Powersave + Deepsleep Disabled + * 2 - QPower + Deepsleep Disabled + * 3 - Legacy Powersave + Deepsleep Enabled + * 4 - QPower + Deepsleep Enabled + */ + wma_handle->powersave_mode = + ucfg_pmo_power_save_offload_enabled(wma_handle->psoc); + wma_handle->staMaxLIModDtim = cds_cfg->sta_maxlimod_dtim; + wma_handle->staModDtim = ucfg_pmo_get_sta_mod_dtim(wma_handle->psoc); + wma_handle->staDynamicDtim = + ucfg_pmo_get_sta_dynamic_dtim(wma_handle->psoc); + +#ifdef WLAN_FEATURE_STATS_EXT + /* register for extended stats event */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_stats_ext_event_id, + wma_stats_ext_event_handler, + WMA_RX_SERIALIZER_CTX); +#endif /* WLAN_FEATURE_STATS_EXT */ +#ifdef FEATURE_WLAN_EXTSCAN + wma_register_extscan_event_handler(wma_handle); +#endif /* WLAN_FEATURE_STATS_EXT */ + + WMA_LOGD("%s: Exit", __func__); + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_roam_synch_event_id, + wma_roam_synch_event_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_roam_synch_frame_event_id, + wma_roam_synch_frame_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_roam_auth_offload_event_id, + wma_roam_auth_offload_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_roam_stats_event_id, + wma_roam_stats_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_roam_scan_chan_list_id, + wma_roam_scan_chan_list_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_roam_blacklist_event_id, + wma_handle_btm_blacklist_event, + WMA_RX_SERIALIZER_CTX); + + wma_register_pmkid_req_event_handler(wma_handle); +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_rssi_breach_event_id, + wma_rssi_breached_event_handler, + WMA_RX_SERIALIZER_CTX); + + qdf_wake_lock_create(&wma_handle->wmi_cmd_rsp_wake_lock, + "wlan_fw_rsp_wakelock"); + qdf_runtime_lock_init(&wma_handle->wmi_cmd_rsp_runtime_lock); + qdf_runtime_lock_init(&wma_handle->sap_prevent_runtime_pm_lock); + + /* Register peer assoc conf event handler */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_peer_assoc_conf_event_id, + wma_peer_assoc_conf_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_peer_delete_response_event_id, + wma_peer_delete_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_chan_info_event_id, + wma_chan_info_event_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_dbg_mesg_flush_complete_event_id, + wma_flush_complete_evt_handler, + WMA_RX_WORK_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_report_rx_aggr_failure_event_id, + wma_rx_aggr_failure_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler( + wma_handle->wmi_handle, + wmi_coex_report_antenna_isolation_event_id, + wma_antenna_isolation_event_handler, + WMA_RX_SERIALIZER_CTX); + + wma_handle->ito_repeat_count = cds_cfg->ito_repeat_count; + wma_handle->bandcapability = cds_cfg->bandcapability; + + /* Register PWR_SAVE_FAIL event only in case of recovery(1) */ + if (ucfg_pmo_get_auto_power_fail_mode(wma_handle->psoc) == + PMO_FW_TO_SEND_WOW_IND_ON_PWR_FAILURE) { + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_pdev_chip_pwr_save_failure_detect_event_id, + wma_chip_power_save_failure_detected_handler, + WMA_RX_WORK_CTX); + } + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_pdev_div_rssi_antid_event_id, + wma_pdev_div_info_evt_handler, + WMA_RX_WORK_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_get_ani_level_event_id, + wma_get_ani_level_evt_handler, + WMA_RX_WORK_CTX); + + wma_register_debug_callback(); + wifi_pos_register_get_phy_mode_cb(wma_handle->psoc, + wma_get_phy_mode_cb); + wifi_pos_register_get_fw_phy_mode_for_freq_cb( + wma_handle->psoc, + wma_get_fw_phy_mode_for_freq_cb); + + /* Register callback with PMO so PMO can update the vdev pause bitmap*/ + pmo_register_pause_bitmap_notifier(wma_handle->psoc, + wma_vdev_update_pause_bitmap); + pmo_register_get_pause_bitmap(wma_handle->psoc, + wma_vdev_get_pause_bitmap); + pmo_register_is_device_in_low_pwr_mode(wma_handle->psoc, + wma_vdev_is_device_in_low_pwr_mode); + pmo_register_get_dtim_period_callback(wma_handle->psoc, + wma_vdev_get_dtim_period); + pmo_register_get_beacon_interval_callback(wma_handle->psoc, + wma_vdev_get_beacon_interval); + wma_cbacks.wma_get_connection_info = wma_get_connection_info; + wma_register_nan_callbacks(wma_handle); + qdf_status = policy_mgr_register_wma_cb(wma_handle->psoc, &wma_cbacks); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("Failed to register wma cb with Policy Manager"); + } + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_phyerr_event_id, + wma_unified_phyerr_rx_event_handler, + WMA_RX_WORK_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_sap_obss_detection_report_event_id, + wma_vdev_obss_detection_info_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_obss_color_collision_report_event_id, + wma_vdev_bss_color_collision_info_handler, + WMA_RX_WORK_CTX); + +#ifdef WLAN_SUPPORT_TWT + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_twt_enable_complete_event_id, + wma_twt_en_complete_event_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_twt_disable_complete_event_id, + wma_twt_disable_comp_event_handler, + WMA_RX_SERIALIZER_CTX); +#endif + + wma_register_apf_events(wma_handle); + wma_register_md_events(wma_handle); + wma_register_wlm_stats_events(wma_handle); + wma_register_mws_coex_events(wma_handle); + return QDF_STATUS_SUCCESS; + +err_dbglog_init: + qdf_wake_lock_destroy(&wma_handle->wmi_cmd_rsp_wake_lock); + qdf_runtime_lock_deinit(&wma_handle->sap_prevent_runtime_pm_lock); + qdf_runtime_lock_deinit(&wma_handle->wmi_cmd_rsp_runtime_lock); + qdf_spinlock_destroy(&wma_handle->wma_hold_req_q_lock); +err_event_init: + wmi_unified_unregister_event_handler(wma_handle->wmi_handle, + wmi_debug_print_event_id); + + for (i = 0; i < wma_handle->max_bssid; ++i) + wma_vdev_deinit(&wma_handle->interfaces[i]); + + qdf_mem_free(wma_handle->interfaces); + +err_scn_context: + qdf_mem_free(((struct cds_context *) cds_context)->cfg_ctx); + ((struct cds_context *)cds_context)->cfg_ctx = NULL; + qdf_mem_free(wmi_handle); + +err_wma_handle: + wlan_objmgr_psoc_release_ref(psoc, WLAN_LEGACY_WMA_ID); +err_get_psoc_ref: + target_if_free_psoc_tgt_info(psoc); + if (cds_get_conparam() != QDF_GLOBAL_FTM_MODE) { +#ifdef FEATURE_WLAN_EXTSCAN + qdf_wake_lock_destroy(&wma_handle->extscan_wake_lock); +#endif /* FEATURE_WLAN_EXTSCAN */ + qdf_wake_lock_destroy(&wma_handle->wow_wake_lock); + qdf_wake_lock_destroy(&wma_handle->wow_auth_req_wl); + qdf_wake_lock_destroy(&wma_handle->wow_assoc_req_wl); + qdf_wake_lock_destroy(&wma_handle->wow_deauth_rec_wl); + qdf_wake_lock_destroy(&wma_handle->wow_disassoc_rec_wl); + qdf_wake_lock_destroy(&wma_handle->wow_ap_assoc_lost_wl); + qdf_wake_lock_destroy(&wma_handle->wow_auto_shutdown_wl); + qdf_wake_lock_destroy(&wma_handle->roam_ho_wl); + qdf_wake_lock_destroy(&wma_handle->roam_preauth_wl); + } +err_free_wma_handle: + cds_free_context(QDF_MODULE_ID_WMA, wma_handle); + + WMA_LOGD("%s: Exit", __func__); + + return qdf_status; +} + +/** + * wma_pre_start() - wma pre start + * + * Return: 0 on success, errno on failure + */ +QDF_STATUS wma_pre_start(void) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + tp_wma_handle wma_handle; + void *htc_handle; + + WMA_LOGD("%s: Enter", __func__); + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + /* Validate the wma_handle */ + if (!wma_handle) { + WMA_LOGE("%s: invalid wma handle", __func__); + qdf_status = QDF_STATUS_E_INVAL; + goto end; + } + + htc_handle = lmac_get_htc_hdl(wma_handle->psoc); + if (!htc_handle) { + WMA_LOGE("%s: invalid htc handle", __func__); + qdf_status = QDF_STATUS_E_INVAL; + goto end; + } + + /* Open endpoint for ctrl path - WMI <--> HTC */ + qdf_status = wmi_unified_connect_htc_service(wma_handle->wmi_handle, + htc_handle); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: wmi_unified_connect_htc_service", __func__); + if (!cds_is_fw_down()) + QDF_BUG(0); + + qdf_status = QDF_STATUS_E_FAULT; + goto end; + } + + WMA_LOGD("WMA --> wmi_unified_connect_htc_service - success"); + +end: + WMA_LOGD("%s: Exit", __func__); + return qdf_status; +} + +void wma_send_msg_by_priority(tp_wma_handle wma_handle, uint16_t msg_type, + void *body_ptr, uint32_t body_val, bool is_high_priority) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + msg.type = msg_type; + msg.bodyval = body_val; + msg.bodyptr = body_ptr; + msg.flush_callback = wma_discard_fw_event; + + status = scheduler_post_msg_by_priority(QDF_MODULE_ID_PE, + &msg, is_high_priority); + if (!QDF_IS_STATUS_SUCCESS(status)) { + if (body_ptr) + qdf_mem_free(body_ptr); + wma_err("Failed to send msg"); + } +} + + +void wma_send_msg(tp_wma_handle wma_handle, uint16_t msg_type, + void *body_ptr, uint32_t body_val) +{ + wma_send_msg_by_priority(wma_handle, msg_type, + body_ptr, body_val, false); +} + +void wma_send_msg_high_priority(tp_wma_handle wma_handle, uint16_t msg_type, + void *body_ptr, uint32_t body_val) +{ + wma_send_msg_by_priority(wma_handle, msg_type, + body_ptr, body_val, true); +} + +/** + * wma_set_base_macaddr_indicate() - set base mac address in fw + * @wma_handle: wma handle + * @customAddr: base mac address + * + * Return: 0 for success or error code + */ +static int wma_set_base_macaddr_indicate(tp_wma_handle wma_handle, + tSirMacAddr *customAddr) +{ + int err; + + err = wmi_unified_set_base_macaddr_indicate_cmd(wma_handle->wmi_handle, + (uint8_t *)customAddr); + if (err) + return -EIO; + wma_debug("Base MAC Addr: " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF((*customAddr))); + + return 0; +} + +/** + * wma_log_supported_evt_handler() - Enable/Disable FW diag/log events + * @handle: WMA handle + * @event: Event received from FW + * @len: Length of the event + * + * Enables the low frequency events and disables the high frequency + * events. Bit 17 indicates if the event if low/high frequency. + * 1 - high frequency, 0 - low frequency + * + * Return: 0 on successfully enabling/disabling the events + */ +static int wma_log_supported_evt_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + + if (wmi_unified_log_supported_evt_cmd(wma->wmi_handle, + event, len)) + return -EINVAL; + + return 0; +} + +/** + * wma_pdev_set_hw_mode_resp_evt_handler() - Set HW mode resp evt handler + * @handle: WMI handle + * @event: Event recevied from FW + * @len: Length of the event + * + * Event handler for WMI_PDEV_SET_HW_MODE_RESP_EVENTID that is sent to host + * driver in response to a WMI_PDEV_SET_HW_MODE_CMDID being sent to WLAN + * firmware + * + * Return: QDF_STATUS + */ +static int wma_pdev_set_hw_mode_resp_evt_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + WMI_PDEV_SET_HW_MODE_RESP_EVENTID_param_tlvs *param_buf; + wmi_pdev_set_hw_mode_response_event_fixed_param *wmi_event; + wmi_pdev_set_hw_mode_response_vdev_mac_entry *vdev_mac_entry; + uint32_t i; + struct sir_set_hw_mode_resp *hw_mode_resp; + tp_wma_handle wma = (tp_wma_handle) handle; + + if (!wma) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + /* Since WMA handle itself is NULL, we cannot send fail + * response back to LIM here + */ + return QDF_STATUS_E_NULL_VALUE; + } + + wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock); + wma_remove_req(wma, 0, WMA_PDEV_SET_HW_MODE_RESP); + + hw_mode_resp = qdf_mem_malloc(sizeof(*hw_mode_resp)); + if (!hw_mode_resp) { + /* Since this memory allocation itself failed, we cannot + * send fail response back to LIM here + */ + return QDF_STATUS_E_NULL_VALUE; + } + + param_buf = (WMI_PDEV_SET_HW_MODE_RESP_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("Invalid WMI_PDEV_SET_HW_MODE_RESP_EVENTID event"); + /* Need to send response back to upper layer to free + * active command list + */ + goto fail; + } + if (param_buf->fixed_param->num_vdev_mac_entries >= + MAX_VDEV_SUPPORTED) { + WMA_LOGE("num_vdev_mac_entries crossed max value"); + goto fail; + } + + wmi_event = param_buf->fixed_param; + if (wmi_event->num_vdev_mac_entries > + param_buf->num_wmi_pdev_set_hw_mode_response_vdev_mac_mapping) { + WMA_LOGE("Invalid num_vdev_mac_entries: %d", + wmi_event->num_vdev_mac_entries); + goto fail; + } + hw_mode_resp->status = wmi_event->status; + hw_mode_resp->cfgd_hw_mode_index = wmi_event->cfgd_hw_mode_index; + hw_mode_resp->num_vdev_mac_entries = wmi_event->num_vdev_mac_entries; + + WMA_LOGD("%s: status:%d cfgd_hw_mode_index:%d num_vdev_mac_entries:%d", + __func__, wmi_event->status, + wmi_event->cfgd_hw_mode_index, + wmi_event->num_vdev_mac_entries); + vdev_mac_entry = + param_buf->wmi_pdev_set_hw_mode_response_vdev_mac_mapping; + + /* Store the vdev-mac map in WMA and prepare to send to PE */ + for (i = 0; i < wmi_event->num_vdev_mac_entries; i++) { + uint32_t vdev_id, mac_id, pdev_id; + + vdev_id = vdev_mac_entry[i].vdev_id; + pdev_id = vdev_mac_entry[i].pdev_id; + if (pdev_id == OL_TXRX_PDEV_ID) { + WMA_LOGE("%s: soc level id received for mac id)", + __func__); + goto fail; + } + if (vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: vdev_id: %d is invalid, max_bssid: %d", + __func__, vdev_id, wma->max_bssid); + goto fail; + } + + mac_id = WMA_PDEV_TO_MAC_MAP(vdev_mac_entry[i].pdev_id); + + WMA_LOGD("%s: vdev_id:%d mac_id:%d", + __func__, vdev_id, mac_id); + + hw_mode_resp->vdev_mac_map[i].vdev_id = vdev_id; + hw_mode_resp->vdev_mac_map[i].mac_id = mac_id; + wma_update_intf_hw_mode_params(vdev_id, mac_id, + wmi_event->cfgd_hw_mode_index); + } + + if (hw_mode_resp->status == SET_HW_MODE_STATUS_OK) { + if (WMA_DEFAULT_HW_MODE_INDEX == wma->new_hw_mode_index) { + wma->new_hw_mode_index = wmi_event->cfgd_hw_mode_index; + } else { + wma->old_hw_mode_index = wma->new_hw_mode_index; + wma->new_hw_mode_index = wmi_event->cfgd_hw_mode_index; + } + policy_mgr_update_hw_mode_index(wma->psoc, + wmi_event->cfgd_hw_mode_index); + } + + WMA_LOGD("%s: Updated: old_hw_mode_index:%d new_hw_mode_index:%d", + __func__, wma->old_hw_mode_index, wma->new_hw_mode_index); + + wma_send_msg(wma, SIR_HAL_PDEV_SET_HW_MODE_RESP, + (void *) hw_mode_resp, 0); + + return QDF_STATUS_SUCCESS; + +fail: + WMA_LOGE("%s: Sending fail response to LIM", __func__); + hw_mode_resp->status = SET_HW_MODE_STATUS_ECANCELED; + hw_mode_resp->cfgd_hw_mode_index = 0; + hw_mode_resp->num_vdev_mac_entries = 0; + wma_send_msg(wma, SIR_HAL_PDEV_SET_HW_MODE_RESP, + (void *) hw_mode_resp, 0); + + return QDF_STATUS_E_FAILURE; +} + +/** + * wma_process_pdev_hw_mode_trans_ind() - Process HW mode transition info + * + * @handle: WMA handle + * @fixed_param: Event fixed parameters + * @vdev_mac_entry - vdev mac entry + * @hw_mode_trans_ind - Buffer to store parsed information + * + * Parses fixed_param, vdev_mac_entry and fills in the information into + * hw_mode_trans_ind and wma + * + * Return: None + */ +void wma_process_pdev_hw_mode_trans_ind(void *handle, + wmi_pdev_hw_mode_transition_event_fixed_param *fixed_param, + wmi_pdev_set_hw_mode_response_vdev_mac_entry *vdev_mac_entry, + struct sir_hw_mode_trans_ind *hw_mode_trans_ind) +{ + uint32_t i; + tp_wma_handle wma = (tp_wma_handle) handle; + + if (fixed_param->num_vdev_mac_entries > MAX_VDEV_SUPPORTED) { + WMA_LOGE("Number of Vdev mac entries %d exceeded" + " max vdev supported %d", + fixed_param->num_vdev_mac_entries, + MAX_VDEV_SUPPORTED); + return; + } + hw_mode_trans_ind->old_hw_mode_index = fixed_param->old_hw_mode_index; + hw_mode_trans_ind->new_hw_mode_index = fixed_param->new_hw_mode_index; + hw_mode_trans_ind->num_vdev_mac_entries = + fixed_param->num_vdev_mac_entries; + WMA_LOGD("%s: old_hw_mode_index:%d new_hw_mode_index:%d entries=%d", + __func__, fixed_param->old_hw_mode_index, + fixed_param->new_hw_mode_index, + fixed_param->num_vdev_mac_entries); + + if (!vdev_mac_entry) { + WMA_LOGE("Invalid vdev_mac_entry"); + return; + } + + /* Store the vdev-mac map in WMA and send to policy manager */ + for (i = 0; i < fixed_param->num_vdev_mac_entries; i++) { + uint32_t vdev_id, mac_id, pdev_id; + + vdev_id = vdev_mac_entry[i].vdev_id; + pdev_id = vdev_mac_entry[i].pdev_id; + + if (pdev_id == OL_TXRX_PDEV_ID) { + WMA_LOGE("%s: soc level id received for mac id)", + __func__); + return; + } + if (vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: vdev_id: %d is invalid, max_bssid: %d", + __func__, vdev_id, wma->max_bssid); + return; + } + + mac_id = WMA_PDEV_TO_MAC_MAP(vdev_mac_entry[i].pdev_id); + + wma_debug("vdev_id:%d mac_id:%d", vdev_id, mac_id); + + hw_mode_trans_ind->vdev_mac_map[i].vdev_id = vdev_id; + hw_mode_trans_ind->vdev_mac_map[i].mac_id = mac_id; + wma_update_intf_hw_mode_params(vdev_id, mac_id, + fixed_param->new_hw_mode_index); + } + wma->old_hw_mode_index = fixed_param->old_hw_mode_index; + wma->new_hw_mode_index = fixed_param->new_hw_mode_index; + policy_mgr_update_new_hw_mode_index(wma->psoc, + fixed_param->new_hw_mode_index); + policy_mgr_update_old_hw_mode_index(wma->psoc, + fixed_param->old_hw_mode_index); + + WMA_LOGD("%s: Updated: old_hw_mode_index:%d new_hw_mode_index:%d", + __func__, wma->old_hw_mode_index, wma->new_hw_mode_index); +} + +/** + * wma_pdev_hw_mode_transition_evt_handler() - HW mode transition evt handler + * @handle: WMI handle + * @event: Event recevied from FW + * @len: Length of the event + * + * Event handler for WMI_PDEV_HW_MODE_TRANSITION_EVENTID that indicates an + * asynchronous hardware mode transition. This event notifies the host driver + * that firmware independently changed the hardware mode for some reason, such + * as Coex, LFR 3.0, etc + * + * Return: Success on receiving valid params from FW + */ +static int wma_pdev_hw_mode_transition_evt_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + WMI_PDEV_HW_MODE_TRANSITION_EVENTID_param_tlvs *param_buf; + wmi_pdev_hw_mode_transition_event_fixed_param *wmi_event; + wmi_pdev_set_hw_mode_response_vdev_mac_entry *vdev_mac_entry; + struct sir_hw_mode_trans_ind *hw_mode_trans_ind; + tp_wma_handle wma = (tp_wma_handle) handle; + + if (!wma) { + /* This is an async event. So, not sending any event to LIM */ + WMA_LOGE("Invalid WMA handle"); + return QDF_STATUS_E_NULL_VALUE; + } + + param_buf = (WMI_PDEV_HW_MODE_TRANSITION_EVENTID_param_tlvs *) event; + if (!param_buf) { + /* This is an async event. So, not sending any event to LIM */ + WMA_LOGE("Invalid WMI_PDEV_HW_MODE_TRANSITION_EVENTID event"); + return QDF_STATUS_E_FAILURE; + } + + if (param_buf->fixed_param->num_vdev_mac_entries > MAX_VDEV_SUPPORTED) { + WMA_LOGE("num_vdev_mac_entries: %d crossed max value: %d", + param_buf->fixed_param->num_vdev_mac_entries, + MAX_VDEV_SUPPORTED); + return QDF_STATUS_E_FAILURE; + } + + hw_mode_trans_ind = qdf_mem_malloc(sizeof(*hw_mode_trans_ind)); + if (!hw_mode_trans_ind) + return QDF_STATUS_E_NOMEM; + + wmi_event = param_buf->fixed_param; + vdev_mac_entry = + param_buf->wmi_pdev_set_hw_mode_response_vdev_mac_mapping; + if (wmi_event->num_vdev_mac_entries > + param_buf->num_wmi_pdev_set_hw_mode_response_vdev_mac_mapping) { + WMA_LOGE("Invalid num_vdev_mac_entries: %d", + wmi_event->num_vdev_mac_entries); + qdf_mem_free(hw_mode_trans_ind); + return -EINVAL; + } + wma_process_pdev_hw_mode_trans_ind(wma, wmi_event, vdev_mac_entry, + hw_mode_trans_ind); + /* Pass the message to PE */ + wma_send_msg(wma, SIR_HAL_PDEV_HW_MODE_TRANS_IND, + (void *) hw_mode_trans_ind, 0); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_pdev_set_dual_mode_config_resp_evt_handler() - Dual mode evt handler + * @handle: WMI handle + * @event: Event received from FW + * @len: Length of the event + * + * Notifies the host driver of the completion or failure of a + * WMI_PDEV_SET_MAC_CONFIG_CMDID command. This event would be returned to + * the host driver once the firmware has completed a reconfiguration of the Scan + * and FW mode configuration. This changes could include entering or leaving a + * dual mac configuration for either scan and/or more permanent firmware mode. + * + * Return: Success on receiving valid params from FW + */ +static int wma_pdev_set_dual_mode_config_resp_evt_handler(void *handle, + uint8_t *event, + uint32_t len) +{ + WMI_PDEV_SET_MAC_CONFIG_RESP_EVENTID_param_tlvs *param_buf; + wmi_pdev_set_mac_config_response_event_fixed_param *wmi_event; + tp_wma_handle wma = (tp_wma_handle) handle; + struct sir_dual_mac_config_resp *dual_mac_cfg_resp; + + if (!wma) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + /* Since the WMA handle is NULL, we cannot send resp to LIM. + * So, returning from here. + */ + return QDF_STATUS_E_NULL_VALUE; + } + wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock); + wma_remove_req(wma, 0, WMA_PDEV_MAC_CFG_RESP); + + dual_mac_cfg_resp = qdf_mem_malloc(sizeof(*dual_mac_cfg_resp)); + if (!dual_mac_cfg_resp) + /* Since the mem alloc failed, we cannot send resp to LIM. + * So, returning from here. + */ + return QDF_STATUS_E_NULL_VALUE; + + param_buf = (WMI_PDEV_SET_MAC_CONFIG_RESP_EVENTID_param_tlvs *) + event; + if (!param_buf) { + WMA_LOGE("%s: Invalid event", __func__); + goto fail; + } + + wmi_event = param_buf->fixed_param; + WMA_LOGD("%s: status:%d", __func__, wmi_event->status); + dual_mac_cfg_resp->status = wmi_event->status; + + if (SET_HW_MODE_STATUS_OK == dual_mac_cfg_resp->status) { + policy_mgr_update_dbs_scan_config(wma->psoc); + policy_mgr_update_dbs_fw_config(wma->psoc); + } + + /* Pass the message to PE */ + wma_send_msg(wma, SIR_HAL_PDEV_MAC_CFG_RESP, + (void *) dual_mac_cfg_resp, 0); + + return QDF_STATUS_SUCCESS; + +fail: + WMA_LOGE("%s: Sending fail response to LIM", __func__); + dual_mac_cfg_resp->status = SET_HW_MODE_STATUS_ECANCELED; + wma_send_msg(wma, SIR_HAL_PDEV_MAC_CFG_RESP, + (void *) dual_mac_cfg_resp, 0); + + return QDF_STATUS_E_FAILURE; + +} + +/** + * wma_send_time_stamp_sync_cmd() - timer callback send timestamp to + * firmware to sync with host. + * @wma_handle: wma handle + * + * Return: void + */ +static void wma_send_time_stamp_sync_cmd(void *data) +{ + tp_wma_handle wma_handle; + QDF_STATUS qdf_status; + + wma_handle = (tp_wma_handle) data; + + wmi_send_time_stamp_sync_cmd_tlv(wma_handle->wmi_handle); + + /* Start/Restart the timer */ + qdf_status = qdf_mc_timer_start(&wma_handle->wma_fw_time_sync_timer, + WMA_FW_TIME_SYNC_TIMER); + if (QDF_IS_STATUS_ERROR(qdf_status)) + WMA_LOGE("Failed to start the firmware time sync timer"); +} + +#ifdef WLAN_CONV_SPECTRAL_ENABLE +static void wma_register_spectral_cmds(tp_wma_handle wma_handle) +{ + struct wmi_spectral_cmd_ops cmd_ops; + + cmd_ops.wmi_spectral_configure_cmd_send = + wmi_unified_vdev_spectral_configure_cmd_send; + cmd_ops.wmi_spectral_enable_cmd_send = + wmi_unified_vdev_spectral_enable_cmd_send; + wlan_register_wmi_spectral_cmd_ops(wma_handle->pdev, &cmd_ops); +} +#else +static void wma_register_spectral_cmds(tp_wma_handle wma_handle) +{ +} +#endif +/** + * wma_start() - wma start function. + * Initialize event handlers and timers. + * + * Return: 0 on success, QDF Error on failure + */ +QDF_STATUS wma_start(void) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + tp_wma_handle wma_handle; + int status; + struct wmi_unified *wmi_handle; + struct mac_context *mac = NULL; + + WMA_LOGD("%s: Enter", __func__); + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + /* validate the wma_handle */ + if (!wma_handle) { + WMA_LOGE("%s: Invalid wma handle", __func__); + qdf_status = QDF_STATUS_E_INVAL; + goto end; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(wma_handle->psoc); + if (!wmi_handle) { + WMA_LOGE("%s: Invalid wmi handle", __func__); + qdf_status = QDF_STATUS_E_INVAL; + goto end; + } + + mac = cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + WMA_LOGE("%s: Invalid mac context", __func__); + goto end; + } + + status = wmi_unified_register_event_handler(wmi_handle, + wmi_roam_event_id, + wma_roam_event_callback, + WMA_RX_SERIALIZER_CTX); + if (0 != status) { + WMA_LOGE("%s: Failed to register Roam callback", __func__); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + status = wmi_unified_register_event_handler(wmi_handle, + wmi_wow_wakeup_host_event_id, + wma_wow_wakeup_host_event, + WMA_RX_TASKLET_CTX); + if (status) { + WMA_LOGE("%s: Failed to register wow wakeup host event handler", + __func__); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + if (wma_d0_wow_is_supported()) { + status = wmi_unified_register_event_handler( + wmi_handle, + wmi_d0_wow_disable_ack_event_id, + wma_d0_wow_disable_ack_event, + WMA_RX_TASKLET_CTX); + if (status) { + WMA_LOGE("%s: Failed to register d0wow disable ack" + " event handler", __func__); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + } + + status = wmi_unified_register_event_handler(wmi_handle, + wmi_pdev_resume_event_id, + wma_pdev_resume_event_handler, + WMA_RX_TASKLET_CTX); + if (status) { + WMA_LOGE("%s: Failed to register PDEV resume event handler", + __func__); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || \ + defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(CONFIG_HL_SUPPORT) + WMA_LOGD("MCC TX Pause Event Handler register"); + status = wmi_unified_register_event_handler(wmi_handle, + wmi_tx_pause_event_id, + wma_mcc_vdev_tx_pause_evt_handler, + WMA_RX_TASKLET_CTX); +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + + WMA_LOGD("Registering SAR2 response handler"); + status = wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_wlan_sar2_result_event_id, + wma_sar_rsp_evt_handler, + WMA_RX_SERIALIZER_CTX); + if (status) { + WMA_LOGE("Failed to register sar response event cb"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + WMA_LOGD("Registering auto shutdown handler"); + status = wmi_unified_register_event_handler(wmi_handle, + wmi_host_auto_shutdown_event_id, + wma_auto_shutdown_event_handler, + WMA_RX_SERIALIZER_CTX); + if (status) { + WMA_LOGE("Failed to register WMI Auto shutdown event handler"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } +#endif /* FEATURE_WLAN_AUTO_SHUTDOWN */ + status = wmi_unified_register_event_handler(wmi_handle, + wmi_thermal_mgmt_event_id, + wma_thermal_mgmt_evt_handler, + WMA_RX_SERIALIZER_CTX); + if (status) { + WMA_LOGE("Failed to register thermal mitigation event cb"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + status = wma_ocb_register_callbacks(wma_handle); + if (!QDF_IS_STATUS_SUCCESS(status)) { + WMA_LOGE("Failed to register OCB callbacks"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + qdf_status = QDF_STATUS_SUCCESS; + +#ifdef QCA_WIFI_FTM + /* + * Tx mgmt attach requires TXRX context which is not created + * in FTM mode. So skip the TX mgmt attach. + */ + if (cds_get_conparam() == QDF_GLOBAL_FTM_MODE) + goto end; +#endif /* QCA_WIFI_FTM */ + + if (wmi_service_enabled(wmi_handle, wmi_service_rmc)) { + + WMA_LOGD("FW supports cesium network, registering event handlers"); + + status = wmi_unified_register_event_handler( + wmi_handle, + wmi_peer_info_event_id, + wma_ibss_peer_info_event_handler, + WMA_RX_SERIALIZER_CTX); + if (status) { + WMA_LOGE("Failed to register ibss peer info event cb"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + status = wmi_unified_register_event_handler( + wmi_handle, + wmi_peer_tx_fail_cnt_thr_event_id, + wma_fast_tx_fail_event_handler, + WMA_RX_SERIALIZER_CTX); + if (status) { + WMA_LOGE("Failed to register peer fast tx failure event cb"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + } else { + WMA_LOGD("Target does not support cesium network"); + } + + qdf_status = wma_tx_attach(wma_handle); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Failed to register tx management", __func__); + goto end; + } + + if (!mac->mlme_cfg->gen.enable_remove_time_stamp_sync_cmd && + cds_get_conparam() != QDF_GLOBAL_FTM_MODE) { + /* Initialize firmware time stamp sync timer */ + qdf_status = qdf_mc_timer_init( + &wma_handle->wma_fw_time_sync_timer, + QDF_TIMER_TYPE_SW, + wma_send_time_stamp_sync_cmd, + wma_handle); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE("Failed to init fw time sync timer"); + goto end; + } + + /* Start firmware time stamp sync timer */ + wma_send_time_stamp_sync_cmd(wma_handle); + } + /* Initialize log completion timeout */ + qdf_status = qdf_mc_timer_init(&wma_handle->log_completion_timer, + QDF_TIMER_TYPE_SW, + wma_log_completion_timeout, + wma_handle); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("Failed to initialize log completion timeout"); + goto end; + } + + status = wma_fips_register_event_handlers(wma_handle); + if (!QDF_IS_STATUS_SUCCESS(status)) { + WMA_LOGE("Failed to register FIPS event handler"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + status = wma_sar_register_event_handlers(wma_handle); + if (!QDF_IS_STATUS_SUCCESS(status)) { + WMA_LOGE("Failed to register SAR event handlers"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + /* Initialize the get temperature event handler */ + status = wmi_unified_register_event_handler(wmi_handle, + wmi_pdev_temperature_event_id, + wma_pdev_temperature_evt_handler, + WMA_RX_SERIALIZER_CTX); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE("Failed to register get_temperature event cb"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + status = wmi_unified_register_event_handler(wmi_handle, + wmi_vdev_tsf_report_event_id, + wma_vdev_tsf_handler, + WMA_RX_SERIALIZER_CTX); + if (0 != status) { + WMA_LOGE("%s: Failed to register tsf callback", __func__); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + /* Initialize the wma_pdev_set_hw_mode_resp_evt_handler event handler */ + status = wmi_unified_register_event_handler(wmi_handle, + wmi_pdev_set_hw_mode_rsp_event_id, + wma_pdev_set_hw_mode_resp_evt_handler, + WMA_RX_SERIALIZER_CTX); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE("Failed to register set hw mode resp event cb"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + /* Initialize the WMI_SOC_HW_MODE_TRANSITION_EVENTID event handler */ + status = wmi_unified_register_event_handler(wmi_handle, + wmi_pdev_hw_mode_transition_event_id, + wma_pdev_hw_mode_transition_evt_handler, + WMA_RX_SERIALIZER_CTX); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE("Failed to register hw mode transition event cb"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + /* Initialize the set dual mac configuration event handler */ + status = wmi_unified_register_event_handler(wmi_handle, + wmi_pdev_set_mac_config_resp_event_id, + wma_pdev_set_dual_mode_config_resp_evt_handler, + WMA_RX_SERIALIZER_CTX); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE("Failed to register hw mode transition event cb"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + + status = wmi_unified_register_event_handler(wmi_handle, + wmi_coex_bt_activity_event_id, + wma_wlan_bt_activity_evt_handler, + WMA_RX_SERIALIZER_CTX); + if (!QDF_IS_STATUS_SUCCESS(status)) { + WMA_LOGE("Failed to register coex bt activity event handler"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + wma_register_spectral_cmds(wma_handle); + +end: + WMA_LOGD("%s: Exit", __func__); + return qdf_status; +} + +QDF_STATUS wma_stop(void) +{ + tp_wma_handle wma_handle; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + int i; + struct mac_context *mac = NULL; + struct wlan_objmgr_vdev *vdev; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + WMA_LOGD("%s: Enter", __func__); + /* validate the wma_handle */ + if (!wma_handle) { + WMA_LOGE("%s: Invalid handle", __func__); + qdf_status = QDF_STATUS_E_INVAL; + goto end; + } + mac = cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + WMA_LOGE("%s: Invalid mac context", __func__); + goto end; + } +#ifdef QCA_WIFI_FTM + /* + * Tx mgmt detach requires TXRX context which is not created + * in FTM mode. So skip the TX mgmt detach. + */ + if (cds_get_conparam() == QDF_GLOBAL_FTM_MODE) { + qdf_status = QDF_STATUS_SUCCESS; + goto end; + } +#endif /* QCA_WIFI_FTM */ + + if (wma_handle->ack_work_ctx) { + cds_flush_work(&wma_handle->ack_work_ctx->ack_cmp_work); + qdf_mem_free(wma_handle->ack_work_ctx); + wma_handle->ack_work_ctx = NULL; + } + + /* Destroy the timer for log completion */ + qdf_status = qdf_mc_timer_destroy(&wma_handle->log_completion_timer); + if (qdf_status != QDF_STATUS_SUCCESS) + WMA_LOGE("Failed to destroy the log completion timer"); + /* clean up ll-queue for all vdev */ + for (i = 0; i < wma_handle->max_bssid; i++) { + vdev = wma_handle->interfaces[i].vdev; + if (!vdev) + continue; + + if (wma_is_vdev_up(i)) + cdp_fc_vdev_flush(cds_get_context(QDF_MODULE_ID_SOC), + i); + } + + if (!mac->mlme_cfg->gen.enable_remove_time_stamp_sync_cmd && + cds_get_conparam() != QDF_GLOBAL_FTM_MODE) { + /* Destroy firmware time stamp sync timer */ + qdf_status = qdf_mc_timer_destroy( + &wma_handle->wma_fw_time_sync_timer); + if (QDF_IS_STATUS_ERROR(qdf_status)) + WMA_LOGE("Failed to destroy fw sync timer"); + } + + qdf_status = wma_tx_detach(wma_handle); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Failed to deregister tx management", __func__); + goto end; + } + +end: + WMA_LOGD("%s: Exit", __func__); + return qdf_status; +} + +/** + * wma_wmi_service_close() - close wma wmi service interface. + * + * Return: 0 on success, QDF Error on failure + */ +QDF_STATUS wma_wmi_service_close(void) +{ + void *cds_ctx; + tp_wma_handle wma_handle; + uint8_t i; + + WMA_LOGD("%s: Enter", __func__); + + cds_ctx = cds_get_global_context(); + if (!cds_ctx) { + WMA_LOGE("%s: Invalid CDS context", __func__); + return QDF_STATUS_E_INVAL; + } + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + /* validate the wma_handle */ + if (!wma_handle) { + WMA_LOGE("%s: Invalid wma handle", __func__); + return QDF_STATUS_E_INVAL; + } + + /* validate the wmi handle */ + if (!wma_handle->wmi_handle) { + WMA_LOGE("%s: Invalid wmi handle", __func__); + return QDF_STATUS_E_INVAL; + } + + /* dettach the wmi serice */ + WMA_LOGD("calling wmi_unified_detach"); + wmi_unified_detach(wma_handle->wmi_handle); + wma_handle->wmi_handle = NULL; + + for (i = 0; i < wma_handle->max_bssid; i++) + wma_vdev_deinit(&wma_handle->interfaces[i]); + + qdf_mem_free(wma_handle->interfaces); + + /* free the wma_handle */ + cds_free_context(QDF_MODULE_ID_WMA, wma_handle); + + if (((struct cds_context *)cds_ctx)->cfg_ctx) + qdf_mem_free(((struct cds_context *)cds_ctx)->cfg_ctx); + ((struct cds_context *)cds_ctx)->cfg_ctx = NULL; + WMA_LOGD("%s: Exit", __func__); + return QDF_STATUS_SUCCESS; +} + +/** + * wma_wmi_work_close() - close the work queue items associated with WMI + * + * This function closes work queue items associated with WMI, but not fully + * closes WMI service. + * + * Return: QDF_STATUS_SUCCESS if work close is successful. Otherwise + * proper error codes. + */ +QDF_STATUS wma_wmi_work_close(void) +{ + tp_wma_handle wma_handle; + + WMA_LOGD("%s: Enter", __func__); + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + /* validate the wma_handle */ + if (!wma_handle) { + WMA_LOGE("%s: Invalid wma handle", __func__); + return QDF_STATUS_E_INVAL; + } + + /* validate the wmi handle */ + if (!wma_handle->wmi_handle) { + WMA_LOGE("%s: Invalid wmi handle", __func__); + return QDF_STATUS_E_INVAL; + } + + /* remove the wmi work */ + WMA_LOGD("calling wmi_unified_remove_work"); + wmi_unified_remove_work(wma_handle->wmi_handle); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_close() - wma close function. + * cleanup resources attached with wma. + * + * Return: 0 on success, QDF Error on failure + */ +QDF_STATUS wma_close(void) +{ + tp_wma_handle wma_handle; + struct target_psoc_info *tgt_psoc_info; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + WMA_LOGD("%s: Enter", __func__); + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + /* validate the wma_handle */ + if (!wma_handle) { + WMA_LOGE("%s: Invalid wma handle", __func__); + return QDF_STATUS_E_INVAL; + } + + /* validate the wmi handle */ + if (!wma_handle->wmi_handle) { + WMA_LOGE("%s: Invalid wmi handle", __func__); + return QDF_STATUS_E_INVAL; + } + + /* Free DBS list */ + if (wma_handle->hw_mode.hw_mode_list) { + qdf_mem_free(wma_handle->hw_mode.hw_mode_list); + wma_handle->hw_mode.hw_mode_list = NULL; + WMA_LOGD("%s: DBS list is freed", __func__); + } + + if (cds_get_conparam() != QDF_GLOBAL_FTM_MODE) { +#ifdef FEATURE_WLAN_EXTSCAN + qdf_wake_lock_destroy(&wma_handle->extscan_wake_lock); +#endif /* FEATURE_WLAN_EXTSCAN */ + qdf_wake_lock_destroy(&wma_handle->wow_wake_lock); + qdf_wake_lock_destroy(&wma_handle->wow_auth_req_wl); + qdf_wake_lock_destroy(&wma_handle->wow_assoc_req_wl); + qdf_wake_lock_destroy(&wma_handle->wow_deauth_rec_wl); + qdf_wake_lock_destroy(&wma_handle->wow_disassoc_rec_wl); + qdf_wake_lock_destroy(&wma_handle->wow_ap_assoc_lost_wl); + qdf_wake_lock_destroy(&wma_handle->wow_auto_shutdown_wl); + qdf_wake_lock_destroy(&wma_handle->roam_ho_wl); + qdf_wake_lock_destroy(&wma_handle->roam_preauth_wl); + } + + /* unregister Firmware debug log */ + qdf_status = dbglog_deinit(wma_handle->wmi_handle); + if (qdf_status != QDF_STATUS_SUCCESS) + WMA_LOGE("%s: dbglog_deinit failed", __func__); + + qdf_status = qdf_mc_timer_destroy(&wma_handle->service_ready_ext_timer); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + WMA_LOGE("%s: Failed to destroy service ready ext event timer", + __func__); + + qdf_event_destroy(&wma_handle->target_suspend); + qdf_event_destroy(&wma_handle->runtime_suspend); + qdf_event_destroy(&wma_handle->recovery_event); + qdf_event_destroy(&wma_handle->tx_frm_download_comp_event); + qdf_event_destroy(&wma_handle->tx_queue_empty_event); + wma_cleanup_hold_req(wma_handle); + qdf_wake_lock_destroy(&wma_handle->wmi_cmd_rsp_wake_lock); + qdf_runtime_lock_deinit(&wma_handle->sap_prevent_runtime_pm_lock); + qdf_runtime_lock_deinit(&wma_handle->wmi_cmd_rsp_runtime_lock); + qdf_spinlock_destroy(&wma_handle->wma_hold_req_q_lock); + + if (wma_handle->pGetRssiReq) { + qdf_mem_free(wma_handle->pGetRssiReq); + wma_handle->pGetRssiReq = NULL; + } + + wma_unified_radio_tx_mem_free(wma_handle); + + if (wma_handle->pdev) { + wlan_objmgr_pdev_release_ref(wma_handle->pdev, + WLAN_LEGACY_WMA_ID); + wma_handle->pdev = NULL; + } + + pmo_unregister_get_beacon_interval_callback(wma_handle->psoc); + pmo_unregister_get_dtim_period_callback(wma_handle->psoc); + pmo_unregister_is_device_in_low_pwr_mode(wma_handle->psoc); + pmo_unregister_get_pause_bitmap(wma_handle->psoc); + pmo_unregister_pause_bitmap_notifier(wma_handle->psoc); + + tgt_psoc_info = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); + init_deinit_free_num_units(wma_handle->psoc, tgt_psoc_info); + target_if_free_psoc_tgt_info(wma_handle->psoc); + + wlan_objmgr_psoc_release_ref(wma_handle->psoc, WLAN_LEGACY_WMA_ID); + wma_handle->psoc = NULL; + + WMA_LOGD("%s: Exit", __func__); + return QDF_STATUS_SUCCESS; +} + +/** + * wma_update_fw_config() - update fw configuration + * @psoc: psoc to query configuration from + * @tgt_hdl: target capability info + * + * Return: none + */ +static void wma_update_fw_config(struct wlan_objmgr_psoc *psoc, + struct target_psoc_info *tgt_hdl) +{ + target_resource_config *cfg = &tgt_hdl->info.wlan_res_cfg; + + /* Override the no. of max fragments as per platform configuration */ + cfg->max_frag_entries = QDF_MIN(QCA_OL_11AC_TX_MAX_FRAGS, + target_if_get_max_frag_entry(tgt_hdl)); + target_if_set_max_frag_entry(tgt_hdl, cfg->max_frag_entries); + + cfg->num_wow_filters = ucfg_pmo_get_num_wow_filters(psoc); + cfg->apf_instruction_size = ucfg_pmo_get_apf_instruction_size(psoc); + cfg->num_packet_filters = ucfg_pmo_get_num_packet_filters(psoc); +} + +/** + * wma_set_tx_partition_base() - set TX MSDU ID partition base for IPA + * @value: TX MSDU ID partition base + * + * Return: none + */ +#ifdef IPA_OFFLOAD +static void wma_set_tx_partition_base(uint32_t value) +{ + cdp_ipa_set_uc_tx_partition_base( + cds_get_context(QDF_MODULE_ID_SOC), + (struct cdp_cfg *)cds_get_context(QDF_MODULE_ID_CFG), + value); + WMA_LOGD("%s: TX_MSDU_ID_PARTITION=%d", __func__, + value); +} +#else +static void wma_set_tx_partition_base(uint32_t value) +{ +} +#endif + +/** + * wma_update_target_services() - update target services from wma handle + * @wmi_handle: Unified wmi handle + * @cfg: target services + * + * Return: none + */ +static inline void wma_update_target_services(struct wmi_unified *wmi_handle, + struct wma_tgt_services *cfg) +{ + /* STA power save */ + cfg->sta_power_save = wmi_service_enabled(wmi_handle, + wmi_service_sta_pwrsave); + + /* Enable UAPSD */ + cfg->uapsd = wmi_service_enabled(wmi_handle, + wmi_service_ap_uapsd); + + /* Update AP DFS service */ + cfg->ap_dfs = wmi_service_enabled(wmi_handle, + wmi_service_ap_dfs); + + /* Enable 11AC */ + cfg->en_11ac = wmi_service_enabled(wmi_handle, + wmi_service_11ac); + if (cfg->en_11ac) + g_fw_wlan_feat_caps |= (1 << DOT11AC); + + /* Proactive ARP response */ + g_fw_wlan_feat_caps |= (1 << WLAN_PERIODIC_TX_PTRN); + + /* Enable WOW */ + g_fw_wlan_feat_caps |= (1 << WOW); + + /* ARP offload */ + cfg->arp_offload = wmi_service_enabled(wmi_handle, + wmi_service_arpns_offload); + + /* Adaptive early-rx */ + cfg->early_rx = wmi_service_enabled(wmi_handle, + wmi_service_early_rx); +#ifdef FEATURE_WLAN_SCAN_PNO + /* PNO offload */ + if (wmi_service_enabled(wmi_handle, wmi_service_nlo)) { + cfg->pno_offload = true; + g_fw_wlan_feat_caps |= (1 << PNO); + } +#endif /* FEATURE_WLAN_SCAN_PNO */ + +#ifdef FEATURE_WLAN_EXTSCAN + if (wmi_service_enabled(wmi_handle, wmi_service_extscan)) + g_fw_wlan_feat_caps |= (1 << EXTENDED_SCAN); +#endif /* FEATURE_WLAN_EXTSCAN */ + cfg->lte_coex_ant_share = wmi_service_enabled(wmi_handle, + wmi_service_lte_ant_share_support); +#ifdef FEATURE_WLAN_TDLS + /* Enable TDLS */ + if (wmi_service_enabled(wmi_handle, wmi_service_tdls)) { + cfg->en_tdls = 1; + g_fw_wlan_feat_caps |= (1 << TDLS); + } + /* Enable advanced TDLS features */ + if (wmi_service_enabled(wmi_handle, wmi_service_tdls_offchan)) { + cfg->en_tdls_offchan = 1; + g_fw_wlan_feat_caps |= (1 << TDLS_OFF_CHANNEL); + } + + cfg->en_tdls_uapsd_buf_sta = + wmi_service_enabled(wmi_handle, + wmi_service_tdls_uapsd_buffer_sta); + cfg->en_tdls_uapsd_sleep_sta = + wmi_service_enabled(wmi_handle, + wmi_service_tdls_uapsd_sleep_sta); +#endif /* FEATURE_WLAN_TDLS */ + if (wmi_service_enabled + (wmi_handle, wmi_service_beacon_offload)) + cfg->beacon_offload = true; + if (wmi_service_enabled + (wmi_handle, wmi_service_sta_pmf_offload)) + cfg->pmf_offload = true; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + /* Enable Roam Offload */ + cfg->en_roam_offload = wmi_service_enabled(wmi_handle, + wmi_service_roam_ho_offload); +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ +#ifdef WLAN_FEATURE_NAN + if (wmi_service_enabled(wmi_handle, wmi_service_nan)) + g_fw_wlan_feat_caps |= (1 << NAN); +#endif /* WLAN_FEATURE_NAN */ + + if (wmi_service_enabled(wmi_handle, wmi_service_rtt)) + g_fw_wlan_feat_caps |= (1 << RTT); + + if (wmi_service_enabled(wmi_handle, + wmi_service_tx_msdu_id_new_partition_support)) { + wma_set_tx_partition_base(HTT_TX_IPA_NEW_MSDU_ID_SPACE_BEGIN); + } else { + wma_set_tx_partition_base(HTT_TX_IPA_MSDU_ID_SPACE_BEGIN); + } + + wma_he_update_tgt_services(wmi_handle, cfg); + + cfg->get_peer_info_enabled = + wmi_service_enabled(wmi_handle, + wmi_service_peer_stats_info); + if (wmi_service_enabled(wmi_handle, wmi_service_fils_support)) + cfg->is_fils_roaming_supported = true; + + if (wmi_service_enabled(wmi_handle, wmi_service_mawc_support)) + cfg->is_fw_mawc_capable = true; + + if (wmi_service_enabled(wmi_handle, + wmi_service_11k_neighbour_report_support)) + cfg->is_11k_offload_supported = true; + + if (wmi_service_enabled(wmi_handle, wmi_service_twt_requestor)) + cfg->twt_requestor = true; + if (wmi_service_enabled(wmi_handle, wmi_service_twt_responder)) + cfg->twt_responder = true; + if (wmi_service_enabled(wmi_handle, wmi_service_obss_scan)) + cfg->obss_scan_offload = true; + if (wmi_service_enabled(wmi_handle, wmi_service_beacon_reception_stats)) + cfg->bcn_reception_stats = true; + + if (wmi_service_enabled(wmi_handle, wmi_service_vdev_latency_config)) + g_fw_wlan_feat_caps |= (1 << VDEV_LATENCY_CONFIG); + if (wmi_service_enabled(wmi_handle, + wmi_roam_scan_chan_list_to_host_support)) + cfg->is_roam_scan_ch_to_host = true; +} + +/** + * wma_update_target_ht_cap() - update ht capabality from wma handle + * @tgt_hdl: pointer to structure target_psoc_info + * @cfg: ht capability + * + * Return: none + */ +static inline void +wma_update_target_ht_cap(struct target_psoc_info *tgt_hdl, + struct wma_tgt_ht_cap *cfg) +{ + int ht_cap_info; + + ht_cap_info = target_if_get_ht_cap_info(tgt_hdl); + /* RX STBC */ + cfg->ht_rx_stbc = !!(ht_cap_info & WMI_HT_CAP_RX_STBC); + + /* TX STBC */ + cfg->ht_tx_stbc = !!(ht_cap_info & WMI_HT_CAP_TX_STBC); + + /* MPDU density */ + cfg->mpdu_density = ht_cap_info & WMI_HT_CAP_MPDU_DENSITY; + + /* HT RX LDPC */ + cfg->ht_rx_ldpc = !!(ht_cap_info & WMI_HT_CAP_LDPC); + + /* HT SGI */ + cfg->ht_sgi_20 = !!(ht_cap_info & WMI_HT_CAP_HT20_SGI); + + cfg->ht_sgi_40 = !!(ht_cap_info & WMI_HT_CAP_HT40_SGI); + + /* RF chains */ + cfg->num_rf_chains = target_if_get_num_rf_chains(tgt_hdl); + + WMA_LOGD("%s: ht_cap_info - %x ht_rx_stbc - %d, ht_tx_stbc - %d\n" + "mpdu_density - %d ht_rx_ldpc - %d ht_sgi_20 - %d\n" + "ht_sgi_40 - %d num_rf_chains - %d", __func__, + ht_cap_info, + cfg->ht_rx_stbc, cfg->ht_tx_stbc, cfg->mpdu_density, + cfg->ht_rx_ldpc, cfg->ht_sgi_20, cfg->ht_sgi_40, + cfg->num_rf_chains); + +} + +/** + * wma_update_target_vht_cap() - update vht capabality from wma handle + * @tgt_hdl: pointer to structure target_psoc_info + * @cfg: vht capabality + * + * Return: none + */ +static inline void +wma_update_target_vht_cap(struct target_psoc_info *tgt_hdl, + struct wma_tgt_vht_cap *cfg) +{ + int vht_cap_info = target_if_get_vht_cap_info(tgt_hdl); + + if (vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_11454) + cfg->vht_max_mpdu = WMI_VHT_CAP_MAX_MPDU_LEN_11454; + else if (vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_7935) + cfg->vht_max_mpdu = WMI_VHT_CAP_MAX_MPDU_LEN_7935; + else + cfg->vht_max_mpdu = 0; + + + if (vht_cap_info & WMI_VHT_CAP_CH_WIDTH_80P80_160MHZ) { + cfg->supp_chan_width = 1 << eHT_CHANNEL_WIDTH_80P80MHZ; + cfg->supp_chan_width |= 1 << eHT_CHANNEL_WIDTH_160MHZ; + } else if (vht_cap_info & WMI_VHT_CAP_CH_WIDTH_160MHZ) { + cfg->supp_chan_width = 1 << eHT_CHANNEL_WIDTH_160MHZ; + } else { + cfg->supp_chan_width = 1 << eHT_CHANNEL_WIDTH_80MHZ; + } + + cfg->vht_rx_ldpc = vht_cap_info & WMI_VHT_CAP_RX_LDPC; + + cfg->vht_short_gi_80 = vht_cap_info & WMI_VHT_CAP_SGI_80MHZ; + cfg->vht_short_gi_160 = vht_cap_info & WMI_VHT_CAP_SGI_160MHZ; + + cfg->vht_tx_stbc = vht_cap_info & WMI_VHT_CAP_TX_STBC; + + cfg->vht_rx_stbc = + (vht_cap_info & WMI_VHT_CAP_RX_STBC_1SS) | + (vht_cap_info & WMI_VHT_CAP_RX_STBC_2SS) | + (vht_cap_info & WMI_VHT_CAP_RX_STBC_3SS); + + cfg->vht_max_ampdu_len_exp = (vht_cap_info & + WMI_VHT_CAP_MAX_AMPDU_LEN_EXP) + >> WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT; + + cfg->vht_su_bformer = vht_cap_info & WMI_VHT_CAP_SU_BFORMER; + + cfg->vht_su_bformee = vht_cap_info & WMI_VHT_CAP_SU_BFORMEE; + + cfg->vht_mu_bformer = vht_cap_info & WMI_VHT_CAP_MU_BFORMER; + + cfg->vht_mu_bformee = vht_cap_info & WMI_VHT_CAP_MU_BFORMEE; + + cfg->vht_txop_ps = vht_cap_info & WMI_VHT_CAP_TXOP_PS; + + WMA_LOGD("%s: max_mpdu %d supp_chan_width %x rx_ldpc %x\n" + "short_gi_80 %x tx_stbc %x rx_stbc %x txop_ps %x\n" + "su_bformee %x mu_bformee %x max_ampdu_len_exp %d", __func__, + cfg->vht_max_mpdu, cfg->supp_chan_width, cfg->vht_rx_ldpc, + cfg->vht_short_gi_80, cfg->vht_tx_stbc, cfg->vht_rx_stbc, + cfg->vht_txop_ps, cfg->vht_su_bformee, cfg->vht_mu_bformee, + cfg->vht_max_ampdu_len_exp); +} + +/** + * wma_update_supported_bands() - update supported bands from service ready ext + * @supported_bands: Supported band given by FW through service ready ext params + * @new_supported_bands: New supported band which needs to be updated by + * this API which WMA layer understands + * + * This API will convert FW given supported band to enum which WMA layer + * understands + * + * Return: QDF_STATUS + */ +static QDF_STATUS wma_update_supported_bands( + WLAN_BAND_CAPABILITY supported_bands, + WMI_PHY_CAPABILITY *new_supported_bands) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!new_supported_bands) { + WMA_LOGE("%s: NULL new supported band variable", __func__); + return QDF_STATUS_E_FAILURE; + } + switch (supported_bands) { + case WLAN_2G_CAPABILITY: + *new_supported_bands |= WMI_11G_CAPABILITY; + break; + case WLAN_5G_CAPABILITY: + *new_supported_bands |= WMI_11A_CAPABILITY; + break; + default: + WMA_LOGE("%s: wrong supported band", __func__); + status = QDF_STATUS_E_FAILURE; + break; + } + return status; +} + +/** + * wma_derive_ext_ht_cap() - Derive HT caps based on given value + * @ht_cap: given pointer to HT caps which needs to be updated + * @tx_chain: given tx chainmask value + * @rx_chain: given rx chainmask value + * @value: new HT cap info provided in form of bitmask + * + * This function takes the value provided in form of bitmask and decodes + * it. After decoding, what ever value it gets, it takes the union(max) or + * intersection(min) with previously derived values. + * + * Return: none + * + */ +static void wma_derive_ext_ht_cap( + struct wma_tgt_ht_cap *ht_cap, uint32_t value, + uint32_t tx_chain, uint32_t rx_chain) +{ + struct wma_tgt_ht_cap tmp = {0}; + + if (!ht_cap) + return; + + if (!qdf_mem_cmp(ht_cap, &tmp, sizeof(struct wma_tgt_ht_cap))) { + ht_cap->ht_rx_stbc = (!!(value & WMI_HT_CAP_RX_STBC)); + ht_cap->ht_tx_stbc = (!!(value & WMI_HT_CAP_TX_STBC)); + ht_cap->mpdu_density = (!!(value & WMI_HT_CAP_MPDU_DENSITY)); + ht_cap->ht_rx_ldpc = (!!(value & WMI_HT_CAP_RX_LDPC)); + ht_cap->ht_sgi_20 = (!!(value & WMI_HT_CAP_HT20_SGI)); + ht_cap->ht_sgi_40 = (!!(value & WMI_HT_CAP_HT40_SGI)); + ht_cap->num_rf_chains = + QDF_MAX(wma_get_num_of_setbits_from_bitmask(tx_chain), + wma_get_num_of_setbits_from_bitmask(rx_chain)); + } else { + ht_cap->ht_rx_stbc = QDF_MIN(ht_cap->ht_rx_stbc, + (!!(value & WMI_HT_CAP_RX_STBC))); + ht_cap->ht_tx_stbc = QDF_MAX(ht_cap->ht_tx_stbc, + (!!(value & WMI_HT_CAP_TX_STBC))); + ht_cap->mpdu_density = QDF_MIN(ht_cap->mpdu_density, + (!!(value & WMI_HT_CAP_MPDU_DENSITY))); + ht_cap->ht_rx_ldpc = QDF_MIN(ht_cap->ht_rx_ldpc, + (!!(value & WMI_HT_CAP_RX_LDPC))); + ht_cap->ht_sgi_20 = QDF_MIN(ht_cap->ht_sgi_20, + (!!(value & WMI_HT_CAP_HT20_SGI))); + ht_cap->ht_sgi_40 = QDF_MIN(ht_cap->ht_sgi_40, + (!!(value & WMI_HT_CAP_HT40_SGI))); + ht_cap->num_rf_chains = + QDF_MAX(ht_cap->num_rf_chains, + QDF_MAX(wma_get_num_of_setbits_from_bitmask( + tx_chain), + wma_get_num_of_setbits_from_bitmask( + rx_chain))); + } +} + +/** + * wma_update_target_ext_ht_cap() - Update HT caps with given extended cap + * @tgt_hdl - target psoc information + * @ht_cap: HT cap structure to be filled + * + * This function loop through each hardware mode and for each hardware mode + * again it loop through each MAC/PHY and pull the caps 2G and 5G specific + * HT caps and derives the final cap. + * + * Return: none + * + */ +static void wma_update_target_ext_ht_cap(struct target_psoc_info *tgt_hdl, + struct wma_tgt_ht_cap *ht_cap) +{ + int i, total_mac_phy_cnt; + uint32_t ht_2g, ht_5g; + struct wma_tgt_ht_cap tmp_ht_cap = {0}, tmp_cap = {0}; + struct wlan_psoc_host_mac_phy_caps *mac_phy_cap; + int num_hw_modes; + + total_mac_phy_cnt = target_psoc_get_total_mac_phy_cnt(tgt_hdl); + num_hw_modes = target_psoc_get_num_hw_modes(tgt_hdl); + mac_phy_cap = target_psoc_get_mac_phy_cap(tgt_hdl); + + if (!mac_phy_cap) { + WMA_LOGE("Invalid MAC PHY capabilities handle"); + return; + } + + /* + * for legacy device extended cap might not even come, so in that case + * don't overwrite legacy values + */ + if (!num_hw_modes) { + WMA_LOGD("%s: No extended HT cap for current SOC", __func__); + return; + } + + for (i = 0; i < total_mac_phy_cnt; i++) { + ht_2g = mac_phy_cap[i].ht_cap_info_2G; + ht_5g = mac_phy_cap[i].ht_cap_info_5G; + if (ht_2g) + wma_derive_ext_ht_cap(&tmp_ht_cap, + ht_2g, + mac_phy_cap[i].tx_chain_mask_2G, + mac_phy_cap[i].rx_chain_mask_2G); + if (ht_5g) + wma_derive_ext_ht_cap(&tmp_ht_cap, + ht_5g, + mac_phy_cap[i].tx_chain_mask_5G, + mac_phy_cap[i].rx_chain_mask_5G); + } + + if (qdf_mem_cmp(&tmp_cap, &tmp_ht_cap, + sizeof(struct wma_tgt_ht_cap))) { + qdf_mem_copy(ht_cap, &tmp_ht_cap, + sizeof(struct wma_tgt_ht_cap)); + } + + WMA_LOGD("%s: [ext ht cap] ht_rx_stbc - %d, ht_tx_stbc - %d\n" + "mpdu_density - %d ht_rx_ldpc - %d ht_sgi_20 - %d\n" + "ht_sgi_40 - %d num_rf_chains - %d", __func__, + ht_cap->ht_rx_stbc, ht_cap->ht_tx_stbc, + ht_cap->mpdu_density, ht_cap->ht_rx_ldpc, + ht_cap->ht_sgi_20, ht_cap->ht_sgi_40, + ht_cap->num_rf_chains); +} + +/** + * wma_derive_ext_vht_cap() - Derive VHT caps based on given value + * @vht_cap: pointer to given VHT caps to be filled + * @value: new VHT cap info provided in form of bitmask + * + * This function takes the value provided in form of bitmask and decodes + * it. After decoding, what ever value it gets, it takes the union(max) or + * intersection(min) with previously derived values. + * + * Return: none + * + */ +static void wma_derive_ext_vht_cap( + struct wma_tgt_vht_cap *vht_cap, uint32_t value) +{ + struct wma_tgt_vht_cap tmp_cap = {0}; + uint32_t tmp = 0; + + if (!vht_cap) + return; + + if (!qdf_mem_cmp(vht_cap, &tmp_cap, + sizeof(struct wma_tgt_vht_cap))) { + if (value & WMI_VHT_CAP_MAX_MPDU_LEN_11454) + vht_cap->vht_max_mpdu = WMI_VHT_CAP_MAX_MPDU_LEN_11454; + else if (value & WMI_VHT_CAP_MAX_MPDU_LEN_7935) + vht_cap->vht_max_mpdu = WMI_VHT_CAP_MAX_MPDU_LEN_7935; + else + vht_cap->vht_max_mpdu = 0; + + if (value & WMI_VHT_CAP_CH_WIDTH_80P80_160MHZ) { + vht_cap->supp_chan_width = + 1 << eHT_CHANNEL_WIDTH_80P80MHZ; + vht_cap->supp_chan_width |= + 1 << eHT_CHANNEL_WIDTH_160MHZ; + } else if (value & WMI_VHT_CAP_CH_WIDTH_160MHZ) { + vht_cap->supp_chan_width = + 1 << eHT_CHANNEL_WIDTH_160MHZ; + } else { + vht_cap->supp_chan_width = 1 << eHT_CHANNEL_WIDTH_80MHZ; + } + vht_cap->vht_rx_ldpc = value & WMI_VHT_CAP_RX_LDPC; + vht_cap->vht_short_gi_80 = value & WMI_VHT_CAP_SGI_80MHZ; + vht_cap->vht_short_gi_160 = value & WMI_VHT_CAP_SGI_160MHZ; + vht_cap->vht_tx_stbc = value & WMI_VHT_CAP_TX_STBC; + vht_cap->vht_rx_stbc = + (value & WMI_VHT_CAP_RX_STBC_1SS) | + (value & WMI_VHT_CAP_RX_STBC_2SS) | + (value & WMI_VHT_CAP_RX_STBC_3SS); + vht_cap->vht_max_ampdu_len_exp = + (value & WMI_VHT_CAP_MAX_AMPDU_LEN_EXP) >> + WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT; + vht_cap->vht_su_bformer = value & WMI_VHT_CAP_SU_BFORMER; + vht_cap->vht_su_bformee = value & WMI_VHT_CAP_SU_BFORMEE; + vht_cap->vht_mu_bformer = value & WMI_VHT_CAP_MU_BFORMER; + vht_cap->vht_mu_bformee = value & WMI_VHT_CAP_MU_BFORMEE; + vht_cap->vht_txop_ps = value & WMI_VHT_CAP_TXOP_PS; + } else { + if (value & WMI_VHT_CAP_MAX_MPDU_LEN_11454) + tmp = WMI_VHT_CAP_MAX_MPDU_LEN_11454; + else if (value & WMI_VHT_CAP_MAX_MPDU_LEN_7935) + tmp = WMI_VHT_CAP_MAX_MPDU_LEN_7935; + else + tmp = 0; + vht_cap->vht_max_mpdu = QDF_MIN(vht_cap->vht_max_mpdu, tmp); + + if ((value & WMI_VHT_CAP_CH_WIDTH_80P80_160MHZ)) { + tmp = (1 << eHT_CHANNEL_WIDTH_80P80MHZ) | + (1 << eHT_CHANNEL_WIDTH_160MHZ); + } else if (value & WMI_VHT_CAP_CH_WIDTH_160MHZ) { + tmp = 1 << eHT_CHANNEL_WIDTH_160MHZ; + } else { + tmp = 1 << eHT_CHANNEL_WIDTH_80MHZ; + } + vht_cap->supp_chan_width = + QDF_MAX(vht_cap->supp_chan_width, tmp); + vht_cap->vht_rx_ldpc = QDF_MIN(vht_cap->vht_rx_ldpc, + value & WMI_VHT_CAP_RX_LDPC); + vht_cap->vht_short_gi_80 = QDF_MAX(vht_cap->vht_short_gi_80, + value & WMI_VHT_CAP_SGI_80MHZ); + vht_cap->vht_short_gi_160 = QDF_MAX(vht_cap->vht_short_gi_160, + value & WMI_VHT_CAP_SGI_160MHZ); + vht_cap->vht_tx_stbc = QDF_MAX(vht_cap->vht_tx_stbc, + value & WMI_VHT_CAP_TX_STBC); + vht_cap->vht_rx_stbc = QDF_MIN(vht_cap->vht_rx_stbc, + (value & WMI_VHT_CAP_RX_STBC_1SS) | + (value & WMI_VHT_CAP_RX_STBC_2SS) | + (value & WMI_VHT_CAP_RX_STBC_3SS)); + vht_cap->vht_max_ampdu_len_exp = + QDF_MIN(vht_cap->vht_max_ampdu_len_exp, + (value & WMI_VHT_CAP_MAX_AMPDU_LEN_EXP) >> + WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT); + vht_cap->vht_su_bformer = QDF_MAX(vht_cap->vht_su_bformer, + value & WMI_VHT_CAP_SU_BFORMER); + vht_cap->vht_su_bformee = QDF_MAX(vht_cap->vht_su_bformee, + value & WMI_VHT_CAP_SU_BFORMEE); + vht_cap->vht_mu_bformer = QDF_MAX(vht_cap->vht_mu_bformer, + value & WMI_VHT_CAP_MU_BFORMER); + vht_cap->vht_mu_bformee = QDF_MAX(vht_cap->vht_mu_bformee, + value & WMI_VHT_CAP_MU_BFORMEE); + vht_cap->vht_txop_ps = QDF_MIN(vht_cap->vht_txop_ps, + value & WMI_VHT_CAP_TXOP_PS); + } +} + +/** + * wma_update_target_ext_vht_cap() - Update VHT caps with given extended cap + * @tgt_hdl - target psoc information + * @vht_cap: VHT cap structure to be filled + * + * This function loop through each hardware mode and for each hardware mode + * again it loop through each MAC/PHY and pull the caps 2G and 5G specific + * VHT caps and derives the final cap. + * + * Return: none + * + */ +static void wma_update_target_ext_vht_cap(struct target_psoc_info *tgt_hdl, + struct wma_tgt_vht_cap *vht_cap) +{ + int i, num_hw_modes, total_mac_phy_cnt; + uint32_t vht_cap_info_2g, vht_cap_info_5g; + struct wma_tgt_vht_cap tmp_vht_cap = {0}, tmp_cap = {0}; + struct wlan_psoc_host_mac_phy_caps *mac_phy_cap; + uint32_t vht_mcs_10_11_supp = 0; + + total_mac_phy_cnt = target_psoc_get_total_mac_phy_cnt(tgt_hdl); + num_hw_modes = target_psoc_get_num_hw_modes(tgt_hdl); + + mac_phy_cap = target_psoc_get_mac_phy_cap(tgt_hdl); + if (!mac_phy_cap) { + WMA_LOGE("Invalid MAC PHY capabilities handle"); + return; + } + + /* + * for legacy device extended cap might not even come, so in that case + * don't overwrite legacy values + */ + if (!num_hw_modes) { + WMA_LOGD("%s: No extended VHT cap for current SOC", __func__); + return; + } + + for (i = 0; i < total_mac_phy_cnt; i++) { + vht_cap_info_2g = mac_phy_cap[i].vht_cap_info_2G; + vht_cap_info_5g = mac_phy_cap[i].vht_cap_info_5G; + if (vht_cap_info_2g) + wma_derive_ext_vht_cap(&tmp_vht_cap, + vht_cap_info_2g); + if (vht_cap_info_5g) + wma_derive_ext_vht_cap(&tmp_vht_cap, + vht_cap_info_5g); + if (WMI_GET_BITS(mac_phy_cap[i].vht_supp_mcs_5G, 16, 2) && + WMI_VHT_MCS_NOTIFY_EXT_SS_GET(mac_phy_cap[i]. + vht_supp_mcs_5G)) + vht_mcs_10_11_supp = 1; + if (WMI_GET_BITS(mac_phy_cap[i].vht_supp_mcs_2G, 16, 2) && + WMI_VHT_MCS_NOTIFY_EXT_SS_GET(mac_phy_cap[i]. + vht_supp_mcs_2G)) + vht_mcs_10_11_supp = 1; + } + + if (qdf_mem_cmp(&tmp_cap, &tmp_vht_cap, + sizeof(struct wma_tgt_vht_cap))) { + qdf_mem_copy(vht_cap, &tmp_vht_cap, + sizeof(struct wma_tgt_vht_cap)); + } + vht_cap->vht_mcs_10_11_supp = vht_mcs_10_11_supp; + WMA_LOGD("%s: [ext vhtcap] max_mpdu %d supp_chan_width %x rx_ldpc %x\n" + "short_gi_80 %x tx_stbc %x rx_stbc %x txop_ps %x\n" + "su_bformee %x mu_bformee %x max_ampdu_len_exp %d\n" + "vht_mcs_10_11_supp %d", __func__, + vht_cap->vht_max_mpdu, vht_cap->supp_chan_width, + vht_cap->vht_rx_ldpc, vht_cap->vht_short_gi_80, + vht_cap->vht_tx_stbc, vht_cap->vht_rx_stbc, + vht_cap->vht_txop_ps, vht_cap->vht_su_bformee, + vht_cap->vht_mu_bformee, vht_cap->vht_max_ampdu_len_exp, + vht_cap->vht_mcs_10_11_supp); +} + +static void +wma_update_sar_version(struct wlan_psoc_host_service_ext_param *param, + struct wma_tgt_cfg *cfg) +{ + cfg->sar_version = param ? param->sar_version : SAR_VERSION_1; +} + +/** + * wma_update_hdd_band_cap() - update band cap which hdd understands + * @supported_band: supported band which has been given by FW + * @tgt_cfg: target configuration to be updated + * @psoc: psoc ptr + * + * Convert WMA given supported band to enum which HDD understands + * + * Return: None + */ +static void wma_update_hdd_band_cap(WMI_PHY_CAPABILITY supported_band, + struct wma_tgt_cfg *tgt_cfg, + struct wlan_objmgr_psoc *psoc) +{ + switch (supported_band) { + case WMI_11G_CAPABILITY: + case WMI_11NG_CAPABILITY: + tgt_cfg->band_cap = BIT(REG_BAND_2G); + break; + case WMI_11A_CAPABILITY: + case WMI_11NA_CAPABILITY: + case WMI_11AC_CAPABILITY: + tgt_cfg->band_cap = BIT(REG_BAND_5G); + break; + case WMI_11AG_CAPABILITY: + case WMI_11NAG_CAPABILITY: + case WMI_11AX_CAPABILITY: + tgt_cfg->band_cap = (BIT(REG_BAND_2G) | BIT(REG_BAND_5G)); + if (wlan_reg_is_6ghz_supported(psoc)) + tgt_cfg->band_cap |= BIT(REG_BAND_6G); + break; + default: + tgt_cfg->band_cap = (BIT(REG_BAND_2G) | + BIT(REG_BAND_5G) | + BIT(REG_BAND_6G)); + } +} + +/** + * wma_update_obss_detection_support() - update obss detection offload support + * @wh: wma handle + * @tgt_cfg: target configuration to be updated + * + * Update obss detection offload support based on service bit. + * + * Return: None + */ +static void wma_update_obss_detection_support(tp_wma_handle wh, + struct wma_tgt_cfg *tgt_cfg) +{ + if (wmi_service_enabled(wh->wmi_handle, + wmi_service_ap_obss_detection_offload)) + tgt_cfg->obss_detection_offloaded = true; + else + tgt_cfg->obss_detection_offloaded = false; +} + +/** + * wma_update_bcast_twt_support() - update bcost twt support + * @wh: wma handle + * @tgt_cfg: target configuration to be updated + * + * Update braodcast twt support based on service bit. + * + * Return: None + */ +static void wma_update_bcast_twt_support(tp_wma_handle wh, + struct wma_tgt_cfg *tgt_cfg) +{ + if (wmi_service_enabled(wh->wmi_handle, + wmi_service_bcast_twt_support)) + tgt_cfg->bcast_twt_support = true; + else + tgt_cfg->bcast_twt_support = false; +} + +/** + * wma_update_obss_color_collision_support() - update obss color collision + * offload support + * @wh: wma handle + * @tgt_cfg: target configuration to be updated + * + * Update obss color collision offload support based on service bit. + * + * Return: None + */ +static void wma_update_obss_color_collision_support(tp_wma_handle wh, + struct wma_tgt_cfg *tgt_cfg) +{ + if (wmi_service_enabled(wh->wmi_handle, wmi_service_bss_color_offload)) + tgt_cfg->obss_color_collision_offloaded = true; + else + tgt_cfg->obss_color_collision_offloaded = false; +} + +/** + * wma_update_restricted_80p80_bw_support() - update restricted 80+80 supprot + * @wh: wma handle + * @tgt_cfg: target configuration to be updated + * + * Update restricted 80+80MHz (165MHz) BW support based on service bit. + * + * Return: None + */ +static void wma_update_restricted_80p80_bw_support(tp_wma_handle wh, + struct wma_tgt_cfg *tgt_cfg) +{ + if (wmi_service_enabled(wh->wmi_handle, + wmi_service_bw_165mhz_support)) + tgt_cfg->restricted_80p80_bw_supp = true; + else + tgt_cfg->restricted_80p80_bw_supp = false; +} + +#ifdef WLAN_SUPPORT_GREEN_AP +static void wma_green_ap_register_handlers(tp_wma_handle wma_handle) +{ + if (WMI_SERVICE_IS_ENABLED(wma_handle->wmi_service_bitmap, + WMI_SERVICE_EGAP)) + target_if_green_ap_register_egap_event_handler( + wma_handle->pdev); + +} +#else +static void wma_green_ap_register_handlers(tp_wma_handle wma_handle) +{ +} +#endif + +#ifdef WLAN_FEATURE_NAN +static void wma_update_nan_target_caps(tp_wma_handle wma_handle, + struct wma_tgt_cfg *tgt_cfg) +{ + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_nan_disable_support)) + tgt_cfg->nan_caps.nan_disable_supported = 1; + + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_nan_dbs_support)) + tgt_cfg->nan_caps.nan_dbs_supported = 1; + + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_ndi_dbs_support)) + tgt_cfg->nan_caps.ndi_dbs_supported = 1; + + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_nan_sap_support)) + tgt_cfg->nan_caps.nan_sap_supported = 1; + + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_ndi_sap_support)) + tgt_cfg->nan_caps.ndi_sap_supported = 1; + + if (wmi_service_enabled(wma_handle->wmi_handle, wmi_service_nan_vdev)) + tgt_cfg->nan_caps.nan_vdev_allowed = 1; + + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_sta_nan_ndi_four_port)) + tgt_cfg->nan_caps.sta_nan_ndi_ndi_allowed = 1; +} +#else +static void wma_update_nan_target_caps(tp_wma_handle wma_handle, + struct wma_tgt_cfg *tgt_cfg) +{ +} +#endif + +static uint8_t +wma_convert_chainmask_to_chain(uint8_t chainmask) +{ + uint8_t num_chains = 0; + + while (chainmask) { + chainmask &= (chainmask - 1); + num_chains++; + } + + return num_chains; +} + +static void +wma_fill_chain_cfg(struct target_psoc_info *tgt_hdl, + uint8_t phy) +{ + struct mac_context *mac_ctx; + uint8_t num_chain; + struct wlan_psoc_host_mac_phy_caps *mac_phy_cap = + tgt_hdl->info.mac_phy_cap; + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + WMA_LOGE("fill chain cfg failed as mac_ctx is NULL"); + return; + } + + num_chain = wma_convert_chainmask_to_chain(mac_phy_cap[phy]. + tx_chain_mask_2G); + + if (num_chain > mac_ctx->fw_chain_cfg.max_tx_chains_2g) + mac_ctx->fw_chain_cfg.max_tx_chains_2g = num_chain; + + num_chain = wma_convert_chainmask_to_chain(mac_phy_cap[phy]. + tx_chain_mask_5G); + + if (num_chain > mac_ctx->fw_chain_cfg.max_tx_chains_5g) + mac_ctx->fw_chain_cfg.max_tx_chains_5g = num_chain; + + num_chain = wma_convert_chainmask_to_chain(mac_phy_cap[phy]. + rx_chain_mask_2G); + + if (num_chain > mac_ctx->fw_chain_cfg.max_rx_chains_2g) + mac_ctx->fw_chain_cfg.max_rx_chains_2g = num_chain; + + num_chain = wma_convert_chainmask_to_chain(mac_phy_cap[phy]. + rx_chain_mask_5G); + + if (num_chain > mac_ctx->fw_chain_cfg.max_rx_chains_5g) + mac_ctx->fw_chain_cfg.max_rx_chains_5g = num_chain; +} + +static void wma_update_mlme_related_tgt_caps(struct wlan_objmgr_psoc *psoc, + struct wmi_unified *wmi_handle) +{ + struct mlme_tgt_caps mlme_tgt_cfg; + + mlme_tgt_cfg.data_stall_recovery_fw_support = + wmi_service_enabled(wmi_handle, + wmi_service_data_stall_recovery_support); + + mlme_tgt_cfg.stop_all_host_scan_support = + wmi_service_enabled(wmi_handle, + wmi_service_host_scan_stop_vdev_all); + + /* Call this at last only after filling all the tgt caps */ + wlan_mlme_update_cfg_with_tgt_caps(psoc, &mlme_tgt_cfg); +} + +static bool +wma_is_dbs_mandatory(struct wlan_objmgr_psoc *psoc, + struct target_psoc_info *tgt_hdl) +{ + uint8_t i, total_mac_phy_cnt; + struct wlan_psoc_host_mac_phy_caps *mac_cap, *mac_phy_cap; + uint8_t supported_band = 0; + + if (!policy_mgr_find_if_fw_supports_dbs(psoc) || + !policy_mgr_find_if_hwlist_has_dbs(psoc)) { + wma_debug("DBS is not mandatory"); + return false; + } + + total_mac_phy_cnt = target_psoc_get_total_mac_phy_cnt(tgt_hdl); + mac_phy_cap = target_psoc_get_mac_phy_cap(tgt_hdl); + if (!mac_phy_cap) { + WMA_LOGE("Invalid MAC PHY capabilities handle"); + return false; + } + + + for (i = 0; i < total_mac_phy_cnt; i++) { + mac_cap = &mac_phy_cap[i]; + if (mac_cap && (mac_cap->phy_id == 0)) + supported_band |= mac_cap->supported_bands; + } + + /* If Mac0 supports both the bands then DBS is not mandatory */ + if (supported_band & WLAN_2G_CAPABILITY && + supported_band & WLAN_5G_CAPABILITY) { + wma_debug("Mac0 supports both bands DBS is optional"); + return false; + } + + wma_info("MAC0 does not support both bands %d DBS is mandatory", + supported_band); + + return true; +} + +/** + * wma_update_hdd_cfg() - update HDD config + * @wma_handle: wma handle + * + * Return: Zero on success err number on failure + */ +static int wma_update_hdd_cfg(tp_wma_handle wma_handle) +{ + struct wma_tgt_cfg tgt_cfg; + void *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + target_resource_config *wlan_res_cfg; + struct wlan_psoc_host_service_ext_param *service_ext_param; + struct target_psoc_info *tgt_hdl; + struct wmi_unified *wmi_handle; + uint8_t i; + int ret; + + WMA_LOGD("%s: Enter", __func__); + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); + if (!tgt_hdl) { + WMA_LOGE("%s: target psoc info is NULL", __func__); + return -EINVAL; + } + + wlan_res_cfg = target_psoc_get_wlan_res_cfg(tgt_hdl); + if (!wlan_res_cfg) { + WMA_LOGE("%s: wlan_res_cfg is null", __func__); + return -EINVAL; + } + + service_ext_param = + target_psoc_get_service_ext_param(tgt_hdl); + wmi_handle = get_wmi_unified_hdl_from_psoc(wma_handle->psoc); + if (!wmi_handle) { + WMA_LOGE("%s: wmi handle is NULL", __func__); + return -EINVAL; + } + + wma_update_mlme_related_tgt_caps(wma_handle->psoc, wmi_handle); + qdf_mem_zero(&tgt_cfg, sizeof(struct wma_tgt_cfg)); + + tgt_cfg.sub_20_support = wma_handle->sub_20_support; + tgt_cfg.reg_domain = wma_handle->reg_cap.eeprom_rd; + tgt_cfg.eeprom_rd_ext = wma_handle->reg_cap.eeprom_rd_ext; + + tgt_cfg.max_intf_count = wlan_res_cfg->num_vdevs; + + qdf_mem_copy(tgt_cfg.hw_macaddr.bytes, wma_handle->hwaddr, + ATH_MAC_LEN); + + wma_update_target_services(wmi_handle, &tgt_cfg.services); + wma_update_target_ht_cap(tgt_hdl, &tgt_cfg.ht_cap); + wma_update_target_vht_cap(tgt_hdl, &tgt_cfg.vht_cap); + /* + * This will overwrite the structure filled by wma_update_target_ht_cap + * and wma_update_target_vht_cap APIs. + */ + wma_update_target_ext_ht_cap(tgt_hdl, &tgt_cfg.ht_cap); + wma_update_target_ext_vht_cap(tgt_hdl, &tgt_cfg.vht_cap); + + wma_update_target_ext_he_cap(tgt_hdl, &tgt_cfg); + + tgt_cfg.target_fw_version = target_if_get_fw_version(tgt_hdl); + if (service_ext_param) + tgt_cfg.target_fw_vers_ext = + service_ext_param->fw_build_vers_ext; + + tgt_cfg.hw_bd_id = wma_handle->hw_bd_id; + tgt_cfg.hw_bd_info.bdf_version = wma_handle->hw_bd_info[BDF_VERSION]; + tgt_cfg.hw_bd_info.ref_design_id = + wma_handle->hw_bd_info[REF_DESIGN_ID]; + tgt_cfg.hw_bd_info.customer_id = wma_handle->hw_bd_info[CUSTOMER_ID]; + tgt_cfg.hw_bd_info.project_id = wma_handle->hw_bd_info[PROJECT_ID]; + tgt_cfg.hw_bd_info.board_data_rev = + wma_handle->hw_bd_info[BOARD_DATA_REV]; + +#ifdef WLAN_FEATURE_LPSS + tgt_cfg.lpss_support = wma_handle->lpss_support; +#endif /* WLAN_FEATURE_LPSS */ + tgt_cfg.ap_arpns_support = wma_handle->ap_arpns_support; + tgt_cfg.dfs_cac_offload = wma_handle->is_dfs_offloaded; + tgt_cfg.rcpi_enabled = wma_handle->rcpi_enabled; + wma_update_hdd_band_cap(target_if_get_phy_capability(tgt_hdl), + &tgt_cfg, wma_handle->psoc); + wma_update_sar_version(service_ext_param, &tgt_cfg); + tgt_cfg.fine_time_measurement_cap = + target_if_get_wmi_fw_sub_feat_caps(tgt_hdl); + tgt_cfg.wmi_max_len = wmi_get_max_msg_len(wma_handle->wmi_handle) + - WMI_TLV_HEADROOM; + tgt_cfg.tx_bfee_8ss_enabled = wma_handle->tx_bfee_8ss_enabled; + tgt_cfg.dynamic_nss_chains_support = + wma_handle->dynamic_nss_chains_support; + wma_update_obss_detection_support(wma_handle, &tgt_cfg); + wma_update_obss_color_collision_support(wma_handle, &tgt_cfg); + wma_update_hdd_cfg_ndp(wma_handle, &tgt_cfg); + wma_update_nan_target_caps(wma_handle, &tgt_cfg); + wma_update_bcast_twt_support(wma_handle, &tgt_cfg); + wma_update_restricted_80p80_bw_support(wma_handle, &tgt_cfg); + /* Take the max of chains supported by FW, which will limit nss */ + for (i = 0; i < tgt_hdl->info.total_mac_phy_cnt; i++) + wma_fill_chain_cfg(tgt_hdl, i); + + ret = wma_handle->tgt_cfg_update_cb(hdd_ctx, &tgt_cfg); + if (ret) + return -EINVAL; + + wma_green_ap_register_handlers(wma_handle); + + return ret; +} + +/** + * wma_dump_dbs_hw_mode() - Print the DBS HW modes + * @wma_handle: WMA handle + * + * Prints the DBS HW modes sent by the FW as part + * of WMI ready event + * + * Return: None + */ +static void wma_dump_dbs_hw_mode(tp_wma_handle wma_handle) +{ + uint32_t i, param; + + if (!wma_handle) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + return; + } + + for (i = 0; i < wma_handle->num_dbs_hw_modes; i++) { + param = wma_handle->hw_mode.hw_mode_list[i]; + WMA_LOGD("%s:[%d]-MAC0: tx_ss:%d rx_ss:%d bw_idx:%d", + __func__, i, + WMA_HW_MODE_MAC0_TX_STREAMS_GET(param), + WMA_HW_MODE_MAC0_RX_STREAMS_GET(param), + WMA_HW_MODE_MAC0_BANDWIDTH_GET(param)); + WMA_LOGD("%s:[%d]-MAC1: tx_ss:%d rx_ss:%d bw_idx:%d", + __func__, i, + WMA_HW_MODE_MAC1_TX_STREAMS_GET(param), + WMA_HW_MODE_MAC1_RX_STREAMS_GET(param), + WMA_HW_MODE_MAC1_BANDWIDTH_GET(param)); + WMA_LOGD("%s:[%d] DBS:%d SBS:%d", __func__, i, + WMA_HW_MODE_DBS_MODE_GET(param), + WMA_HW_MODE_SBS_MODE_GET(param)); + } + policy_mgr_dump_dbs_hw_mode(wma_handle->psoc); +} + +/** + * wma_init_scan_fw_mode_config() - Initialize scan/fw mode config + * @psoc: Object manager psoc + * @scan_config: Scam mode configuration + * @fw_config: FW mode configuration + * + * Enables all the valid bits of concurrent_scan_config_bits and + * fw_mode_config_bits. + * + * Return: None + */ +static void wma_init_scan_fw_mode_config(struct wlan_objmgr_psoc *psoc, + uint32_t scan_config, + uint32_t fw_config) +{ + WMA_LOGD("%s: Enter", __func__); + + if (!psoc) { + WMA_LOGE("%s: obj psoc is NULL", __func__); + return; + } + + policy_mgr_init_dbs_config(psoc, scan_config, fw_config); +} + +static void wma_set_pmo_caps(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + tp_wma_handle wma; + struct pmo_device_caps caps; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: wma handler is null", __func__); + return; + } + + caps.arp_ns_offload = + wmi_service_enabled(wma->wmi_handle, wmi_service_arpns_offload); + caps.apf = + wmi_service_enabled(wma->wmi_handle, wmi_service_apf_offload); + caps.packet_filter = + wmi_service_enabled(wma->wmi_handle, + wmi_service_packet_filter_offload); + caps.unified_wow = + wmi_service_enabled(wma->wmi_handle, + wmi_service_unified_wow_capability); + caps.li_offload = + wmi_service_enabled(wma->wmi_handle, + wmi_service_listen_interval_offload_support + ); + + status = ucfg_pmo_psoc_set_caps(psoc, &caps); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to set PMO capabilities; status:%d", status); +} + +/** + * wma_set_mlme_caps() - Populate the MLME related target capabilities to the + * mlme component + * @psoc: Pointer to psoc object + * + * Return: None + */ +static void wma_set_mlme_caps(struct wlan_objmgr_psoc *psoc) +{ + tp_wma_handle wma; + bool tgt_cap; + uint32_t akm_bitmap = 0; + QDF_STATUS status; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: wma handler is null", __func__); + return; + } + + tgt_cap = wmi_service_enabled(wma->wmi_handle, + wmi_service_adaptive_11r_support); + + status = ucfg_mlme_set_tgt_adaptive_11r_cap(psoc, tgt_cap); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to set adaptive 11r cap"); + + tgt_cap = wmi_service_enabled(wma->wmi_handle, + wmi_service_wpa3_ft_sae_support); + if (tgt_cap) + akm_bitmap |= (1 << AKM_FT_SAE); + + tgt_cap = wmi_service_enabled(wma->wmi_handle, + wmi_service_wpa3_ft_suite_b_support); + if (tgt_cap) + akm_bitmap |= (1 << AKM_FT_SUITEB_SHA384); + + tgt_cap = wmi_service_enabled(wma->wmi_handle, + wmi_service_ft_fils); + if (tgt_cap) + akm_bitmap |= (1 << AKM_FT_FILS); + + tgt_cap = wmi_service_enabled(wma->wmi_handle, + wmi_service_owe_roam_support); + if (tgt_cap) + akm_bitmap |= (1 << AKM_OWE); + + tgt_cap = wmi_service_enabled(wma->wmi_handle, + wmi_service_sae_roam_support); + if (tgt_cap) + akm_bitmap |= (1 << AKM_SAE); + + + status = mlme_set_tgt_wpa3_roam_cap(psoc, akm_bitmap); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to set sae roam support"); +} + +static void wma_set_component_caps(struct wlan_objmgr_psoc *psoc) +{ + wma_set_pmo_caps(psoc); + wma_set_mlme_caps(psoc); +} + +#if defined(WLAN_FEATURE_GTK_OFFLOAD) && defined(WLAN_POWER_MANAGEMENT_OFFLOAD) +static QDF_STATUS wma_register_gtk_offload_event(tp_wma_handle wma_handle) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!wma_handle) { + WMA_LOGE("%s: wma_handle passed is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_gtk_offload)) { + status = wmi_unified_register_event_handler( + wma_handle->wmi_handle, + wmi_gtk_offload_status_event_id, + target_if_pmo_gtk_offload_status_event, + WMA_RX_WORK_CTX); + } + return status; +} +#else +static QDF_STATUS wma_register_gtk_offload_event(tp_wma_handle wma_handle) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_GTK_OFFLOAD && WLAN_POWER_MANAGEMENT_OFFLOAD */ + +/** + * wma_rx_service_ready_event() - event handler to process + * wmi rx sevice ready event. + * @handle: wma handle + * @cmd_param_info: command params info + * + * Return: none + */ +int wma_rx_service_ready_event(void *handle, uint8_t *cmd_param_info, + uint32_t length) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + WMI_SERVICE_READY_EVENTID_param_tlvs *param_buf; + wmi_service_ready_event_fixed_param *ev; + QDF_STATUS status; + uint32_t *ev_wlan_dbs_hw_mode_list; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct target_psoc_info *tgt_hdl; + struct wlan_psoc_target_capability_info *tgt_cap_info; + target_resource_config *wlan_res_cfg; + struct wmi_unified *wmi_handle; + uint32_t *service_bitmap; + + WMA_LOGD("%s: Enter", __func__); + + if (!handle) { + WMA_LOGE("%s: wma_handle passed is NULL", __func__); + return -EINVAL; + } + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); + if (!tgt_hdl) { + WMA_LOGE("%s: target psoc info is NULL", __func__); + return -EINVAL; + } + + wlan_res_cfg = target_psoc_get_wlan_res_cfg(tgt_hdl); + tgt_cap_info = target_psoc_get_target_caps(tgt_hdl); + service_bitmap = target_psoc_get_service_bitmap(tgt_hdl); + + param_buf = (WMI_SERVICE_READY_EVENTID_param_tlvs *) cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid arguments", __func__); + return -EINVAL; + } + + ev = param_buf->fixed_param; + if (!ev) { + WMA_LOGE("%s: Invalid buffer", __func__); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(wma_handle->psoc); + if (!wmi_handle) { + WMA_LOGE("%s: wmi handle is NULL", __func__); + return -EINVAL; + } + + WMA_LOGD("WMA <-- WMI_SERVICE_READY_EVENTID"); + + if (ev->num_dbs_hw_modes > param_buf->num_wlan_dbs_hw_mode_list) { + WMA_LOGE("FW dbs_hw_mode entry %d more than value %d in TLV hdr", + ev->num_dbs_hw_modes, + param_buf->num_wlan_dbs_hw_mode_list); + return -EINVAL; + } + + wma_handle->num_dbs_hw_modes = ev->num_dbs_hw_modes; + ev_wlan_dbs_hw_mode_list = param_buf->wlan_dbs_hw_mode_list; + + /* Continuing with the rest of the processing, + * even if memory allocation fails + */ + wma_handle->hw_mode.hw_mode_list = + qdf_mem_malloc(sizeof(*wma_handle->hw_mode.hw_mode_list) * + wma_handle->num_dbs_hw_modes); + + if (wma_handle->hw_mode.hw_mode_list) + qdf_mem_copy(wma_handle->hw_mode.hw_mode_list, + ev_wlan_dbs_hw_mode_list, + (sizeof(*wma_handle->hw_mode.hw_mode_list) * + wma_handle->num_dbs_hw_modes)); + + policy_mgr_init_dbs_hw_mode(wma_handle->psoc, + ev->num_dbs_hw_modes, ev_wlan_dbs_hw_mode_list); + wma_dump_dbs_hw_mode(wma_handle); + + /* Initializes the fw_mode and scan_config to zero. + * If ext service ready event is present it will set + * the actual values of these two params. + * This is to ensure that no garbage values would be + * present in the absence of ext service ready event. + */ + wma_init_scan_fw_mode_config(wma_handle->psoc, 0, 0); + + qdf_mem_copy(&wma_handle->reg_cap, param_buf->hal_reg_capabilities, + sizeof(HAL_REG_CAPABILITIES)); + + wma_handle->vht_supp_mcs = ev->vht_supp_mcs; + + wma_handle->new_hw_mode_index = tgt_cap_info->default_dbs_hw_mode_index; + policy_mgr_update_new_hw_mode_index(wma_handle->psoc, + tgt_cap_info->default_dbs_hw_mode_index); + + WMA_LOGD("%s: Firmware default hw mode index : %d", + __func__, tgt_cap_info->default_dbs_hw_mode_index); + WMA_LOGI("%s: Firmware build version : %08x", + __func__, ev->fw_build_vers); + WMA_LOGD("FW fine time meas cap: 0x%x", + tgt_cap_info->wmi_fw_sub_feat_caps); + + wma_handle->hw_bd_id = ev->hw_bd_id; + + wma_handle->hw_bd_info[BDF_VERSION] = + WMI_GET_BDF_VERSION(ev->hw_bd_info); + wma_handle->hw_bd_info[REF_DESIGN_ID] = + WMI_GET_REF_DESIGN(ev->hw_bd_info); + wma_handle->hw_bd_info[CUSTOMER_ID] = + WMI_GET_CUSTOMER_ID(ev->hw_bd_info); + wma_handle->hw_bd_info[PROJECT_ID] = + WMI_GET_PROJECT_ID(ev->hw_bd_info); + wma_handle->hw_bd_info[BOARD_DATA_REV] = + WMI_GET_BOARD_DATA_REV(ev->hw_bd_info); + + WMA_LOGI("%s: Board id: %x, Board version: %x %x %x %x %x", + __func__, wma_handle->hw_bd_id, + wma_handle->hw_bd_info[BDF_VERSION], + wma_handle->hw_bd_info[REF_DESIGN_ID], + wma_handle->hw_bd_info[CUSTOMER_ID], + wma_handle->hw_bd_info[PROJECT_ID], + wma_handle->hw_bd_info[BOARD_DATA_REV]); + + /* wmi service is ready */ + qdf_mem_copy(wma_handle->wmi_service_bitmap, + service_bitmap, + sizeof(wma_handle->wmi_service_bitmap)); + + cdp_cfg_tx_set_is_mgmt_over_wmi_enabled(soc, + wmi_service_enabled(wmi_handle, wmi_service_mgmt_tx_wmi)); + cdp_set_desc_global_pool_size(soc, ev->num_msdu_desc); + /* SWBA event handler for beacon transmission */ + status = wma_register_swba_events(wma_handle->wmi_handle); + + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to register swba beacon event cb"); + goto free_hw_mode_list; + } +#ifdef WLAN_FEATURE_LPSS + wma_handle->lpss_support = + wmi_service_enabled(wmi_handle, wmi_service_lpass); +#endif /* WLAN_FEATURE_LPSS */ + + /* + * This Service bit is added to check for ARP/NS Offload + * support for LL/HL targets + */ + wma_handle->ap_arpns_support = + wmi_service_enabled(wmi_handle, wmi_service_ap_arpns_offload); + + if (wmi_service_enabled(wmi_handle, wmi_service_csa_offload)) { + WMA_LOGD("%s: FW support CSA offload capability", __func__); + status = wmi_unified_register_event_handler( + wmi_handle, + wmi_csa_handling_event_id, + wma_csa_offload_handler, + WMA_RX_SERIALIZER_CTX); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to register CSA offload event cb"); + goto free_hw_mode_list; + } + } + + if (wmi_service_enabled(wmi_handle, wmi_service_mgmt_tx_wmi)) { + WMA_LOGD("Firmware supports management TX over WMI,use WMI interface instead of HTT for management Tx"); + /* + * Register Tx completion event handler for MGMT Tx over WMI + * case + */ + status = wmi_unified_register_event_handler( + wmi_handle, + wmi_mgmt_tx_completion_event_id, + wma_mgmt_tx_completion_handler, + WMA_RX_SERIALIZER_CTX); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to register MGMT over WMI completion handler"); + goto free_hw_mode_list; + } + + status = wmi_unified_register_event_handler( + wmi_handle, + wmi_mgmt_tx_bundle_completion_event_id, + wma_mgmt_tx_bundle_completion_handler, + WMA_RX_SERIALIZER_CTX); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to register MGMT over WMI completion handler"); + goto free_hw_mode_list; + } + + } else { + WMA_LOGE("FW doesnot support WMI_SERVICE_MGMT_TX_WMI, Use HTT interface for Management Tx"); + } + + status = wma_register_gtk_offload_event(wma_handle); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to register GTK offload event cb"); + goto free_hw_mode_list; + } + + status = wmi_unified_register_event_handler(wmi_handle, + wmi_tbttoffset_update_event_id, + wma_tbttoffset_update_event_handler, + WMA_RX_SERIALIZER_CTX); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to register WMI_TBTTOFFSET_UPDATE_EVENTID callback"); + goto free_hw_mode_list; + } + + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_rcpi_support)) { + /* register for rcpi response event */ + status = wmi_unified_register_event_handler( + wmi_handle, + wmi_update_rcpi_event_id, + wma_rcpi_event_handler, + WMA_RX_SERIALIZER_CTX); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to register RCPI event handler"); + goto free_hw_mode_list; + } + wma_handle->rcpi_enabled = true; + } + + /* mac_id is replaced with pdev_id in converged firmware to have + * multi-radio support. In order to maintain backward compatibility + * with old fw, host needs to check WMI_SERVICE_DEPRECATED_REPLACE + * in service bitmap from FW and host needs to set use_pdev_id in + * wmi_resource_config to true. If WMI_SERVICE_DEPRECATED_REPLACE + * service is not set, then host shall not expect MAC ID from FW in + * VDEV START RESPONSE event and host shall use PDEV ID. + */ + if (wmi_service_enabled(wmi_handle, wmi_service_deprecated_replace)) + wlan_res_cfg->use_pdev_id = true; + else + wlan_res_cfg->use_pdev_id = false; + + wlan_res_cfg->max_num_dbs_scan_duty_cycle = CDS_DBS_SCAN_CLIENTS_MAX; + + /* Initialize the log supported event handler */ + status = wmi_unified_register_event_handler(wmi_handle, + wmi_diag_event_id_log_supported_event_id, + wma_log_supported_evt_handler, + WMA_RX_SERIALIZER_CTX); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to register log supported event cb"); + goto free_hw_mode_list; + } + + cdp_mark_first_wakeup_packet( + soc, OL_TXRX_PDEV_ID, + wmi_service_enabled(wmi_handle, + wmi_service_mark_first_wakeup_packet)); + wma_handle->is_dfs_offloaded = + wmi_service_enabled(wmi_handle, + wmi_service_dfs_phyerr_offload); + + wma_handle->nan_datapath_enabled = + wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_nan_data); + + wma_handle->fw_therm_throt_support = + wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_tt); + + wma_set_component_caps(wma_handle->psoc); + + wma_update_fw_config(wma_handle->psoc, tgt_hdl); + + status = wmi_unified_save_fw_version_cmd(wmi_handle, param_buf); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to send WMI_INIT_CMDID command"); + goto free_hw_mode_list; + } + + if (wmi_service_enabled(wmi_handle, wmi_service_ext_msg)) { + status = qdf_mc_timer_start( + &wma_handle->service_ready_ext_timer, + WMA_SERVICE_READY_EXT_TIMEOUT); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to start the service ready ext timer"); + } + wma_handle->tx_bfee_8ss_enabled = + wmi_service_enabled(wmi_handle, wmi_service_8ss_tx_bfee); + + wma_handle->dynamic_nss_chains_support = wmi_service_enabled(wmi_handle, + wmi_service_per_vdev_chain_support); + target_psoc_set_num_radios(tgt_hdl, 1); + + return 0; + +free_hw_mode_list: + if (wma_handle->hw_mode.hw_mode_list) { + qdf_mem_free(wma_handle->hw_mode.hw_mode_list); + wma_handle->hw_mode.hw_mode_list = NULL; + WMA_LOGD("%s: DBS list is freed", __func__); + } + + return -EINVAL; + +} + +/** + * wma_get_caps_for_phyidx_hwmode() - to fetch caps for given hw mode and band + * @caps_per_phy: Pointer to capabilities structure which needs to be filled + * @hw_mode: Provided hardware mode + * @band: Provide band i.e. 2G or 5G + * + * This API finds cap which suitable for provided hw mode and band. If user + * is provides some invalid hw mode then it will automatically falls back to + * default hw mode + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_get_caps_for_phyidx_hwmode(struct wma_caps_per_phy *caps_per_phy, + enum hw_mode_dbs_capab hw_mode, enum cds_band_type band) +{ + t_wma_handle *wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + struct target_psoc_info *tgt_hdl; + int ht_cap_info, vht_cap_info; + uint8_t our_hw_mode = hw_mode, num_hw_modes; + struct wlan_psoc_host_mac_phy_caps *mac_phy_cap; + struct wlan_psoc_target_capability_info *tgt_cap_info; + uint8_t total_mac_phy_cnt, i; + + if (!wma_handle) { + WMA_LOGE("Invalid wma handle"); + return QDF_STATUS_E_FAILURE; + } + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); + if (!tgt_hdl) { + WMA_LOGE("%s: target psoc info is NULL", __func__); + return -EINVAL; + } + if (!caps_per_phy) { + WMA_LOGE("Invalid caps pointer"); + return QDF_STATUS_E_FAILURE; + } + + ht_cap_info = target_if_get_ht_cap_info(tgt_hdl); + vht_cap_info = target_if_get_vht_cap_info(tgt_hdl); + num_hw_modes = target_psoc_get_num_hw_modes(tgt_hdl); + mac_phy_cap = target_psoc_get_mac_phy_cap(tgt_hdl); + tgt_cap_info = target_psoc_get_target_caps(tgt_hdl); + + if (!mac_phy_cap) { + WMA_LOGE("Invalid MAC PHY capabilities handle"); + return QDF_STATUS_E_FAILURE; + } + + if (!tgt_cap_info) { + WMA_LOGE("Invalid target capabilities handle"); + return QDF_STATUS_E_FAILURE; + } + + if (!num_hw_modes) { + WMA_LOGD("Invalid number of hw modes, use legacy HT/VHT caps"); + caps_per_phy->ht_2g = ht_cap_info; + caps_per_phy->ht_5g = ht_cap_info; + caps_per_phy->vht_2g = vht_cap_info; + caps_per_phy->vht_5g = vht_cap_info; + /* legacy platform doesn't support HE IE */ + caps_per_phy->he_2g[0] = 0; + caps_per_phy->he_2g[1] = 0; + caps_per_phy->he_5g[0] = 0; + caps_per_phy->he_5g[1] = 0; + caps_per_phy->tx_chain_mask_2G = + EXTRACT_TX_CHAIN_MASK_2G(tgt_cap_info->txrx_chainmask); + caps_per_phy->rx_chain_mask_2G = + EXTRACT_RX_CHAIN_MASK_2G(tgt_cap_info->txrx_chainmask); + caps_per_phy->tx_chain_mask_5G = + EXTRACT_TX_CHAIN_MASK_5G(tgt_cap_info->txrx_chainmask); + caps_per_phy->rx_chain_mask_5G = + EXTRACT_RX_CHAIN_MASK_5G(tgt_cap_info->txrx_chainmask); + + return QDF_STATUS_SUCCESS; + } + + if (!policy_mgr_is_dbs_enable(wma_handle->psoc)) + our_hw_mode = HW_MODE_DBS_NONE; + + total_mac_phy_cnt = target_psoc_get_total_mac_phy_cnt(tgt_hdl); + for (i = 0; i < total_mac_phy_cnt; i++) { + if (our_hw_mode == HW_MODE_DBS && + mac_phy_cap[i].hw_mode_config_type != WMI_HW_MODE_DBS) + continue; + + if ((band == CDS_BAND_2GHZ || band == CDS_BAND_ALL) && + (WLAN_2G_CAPABILITY & mac_phy_cap[i].supported_bands) && + !caps_per_phy->tx_chain_mask_2G) { + caps_per_phy->ht_2g = mac_phy_cap[i].ht_cap_info_2G; + caps_per_phy->vht_2g = mac_phy_cap[i].vht_cap_info_2G; + qdf_mem_copy(caps_per_phy->he_2g, + mac_phy_cap[i].he_cap_info_2G, + sizeof(caps_per_phy->he_2g)); + + caps_per_phy->tx_chain_mask_2G = + mac_phy_cap[i].tx_chain_mask_2G; + caps_per_phy->rx_chain_mask_2G = + mac_phy_cap[i].rx_chain_mask_2G; + + WMA_LOGD("Select 2G capable phyid[%d] chain %d %d ht 0x%x vht 0x%x", + i, + caps_per_phy->tx_chain_mask_2G, + caps_per_phy->rx_chain_mask_2G, + caps_per_phy->ht_2g, + caps_per_phy->vht_2g); + } + if ((band == CDS_BAND_5GHZ || band == CDS_BAND_ALL) && + (WLAN_5G_CAPABILITY & mac_phy_cap[i].supported_bands) && + !caps_per_phy->tx_chain_mask_5G) { + caps_per_phy->ht_5g = mac_phy_cap[i].ht_cap_info_5G; + caps_per_phy->vht_5g = mac_phy_cap[i].vht_cap_info_5G; + qdf_mem_copy(caps_per_phy->he_5g, + mac_phy_cap[i].he_cap_info_5G, + sizeof(caps_per_phy->he_5g)); + + caps_per_phy->tx_chain_mask_5G = + mac_phy_cap[i].tx_chain_mask_5G; + caps_per_phy->rx_chain_mask_5G = + mac_phy_cap[i].rx_chain_mask_5G; + + WMA_LOGD("Select 5G capable phyid[%d] chain %d %d ht 0x%x vht 0x%x", + i, + caps_per_phy->tx_chain_mask_5G, + caps_per_phy->rx_chain_mask_5G, + caps_per_phy->ht_5g, + caps_per_phy->vht_5g); + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_is_rx_ldpc_supported_for_channel() - to find out if ldpc is supported + * + * @ch_freq: Channel freq for which it needs to check if rx ldpc is enabled + * + * This API takes channel number as argument and takes default hw mode as DBS + * to check if rx LDPC support is enabled for that channel or no + */ +bool wma_is_rx_ldpc_supported_for_channel(uint32_t ch_freq) +{ + t_wma_handle *wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + struct target_psoc_info *tgt_hdl; + struct wma_caps_per_phy caps_per_phy = {0}; + enum cds_band_type band; + bool status; + uint8_t num_hw_modes; + + if (!wma_handle) { + WMA_LOGE("Invalid wma handle"); + return false; + } + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); + if (!tgt_hdl) { + WMA_LOGE("Target handle is NULL"); + return QDF_STATUS_E_FAILURE; + } + + num_hw_modes = target_psoc_get_num_hw_modes(tgt_hdl); + + if (!WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) + band = CDS_BAND_5GHZ; + else + band = CDS_BAND_2GHZ; + + if (QDF_STATUS_SUCCESS != wma_get_caps_for_phyidx_hwmode( + &caps_per_phy, + HW_MODE_DBS, band)) { + return false; + } + + /* + * Legacy platforms like Rome set WMI_HT_CAP_LDPC to specify RX LDPC + * capability. But new platforms like Helium set WMI_HT_CAP_RX_LDPC + * instead. + */ + if (0 == num_hw_modes) { + status = (!!(caps_per_phy.ht_2g & WMI_HT_CAP_LDPC)); + } else { + if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) + status = (!!(caps_per_phy.ht_2g & WMI_HT_CAP_RX_LDPC)); + else + status = (!!(caps_per_phy.ht_5g & WMI_HT_CAP_RX_LDPC)); + } + + return status; +} + +/** + * wma_print_mac_phy_capabilities() - Prints MAC PHY capabilities + * @cap: pointer to WMI_MAC_PHY_CAPABILITIES + * @index: MAC_PHY index + * + * Return: none + */ +static void wma_print_mac_phy_capabilities(struct wlan_psoc_host_mac_phy_caps + *cap, int index) +{ + uint32_t mac_2G[PSOC_HOST_MAX_MAC_SIZE]; + uint32_t mac_5G[PSOC_HOST_MAX_MAC_SIZE]; + uint32_t phy_2G[WMI_MAX_HECAP_PHY_SIZE]; + uint32_t phy_5G[WMI_MAX_HECAP_PHY_SIZE]; + struct wlan_psoc_host_ppe_threshold ppet_2G, ppet_5G; + + WMA_LOGD("\t: index [%d]", index); + WMA_LOGD("\t: cap for hw_mode_id[%d]", cap->hw_mode_id); + WMA_LOGD("\t: pdev_id[%d]", cap->pdev_id); + WMA_LOGD("\t: phy_id[%d]", cap->phy_id); + WMA_LOGD("\t: hw_mode_config_type[%d]", cap->hw_mode_config_type); + WMA_LOGD("\t: supports_11b[%d]", cap->supports_11b); + WMA_LOGD("\t: supports_11g[%d]", cap->supports_11g); + WMA_LOGD("\t: supports_11a[%d]", cap->supports_11a); + WMA_LOGD("\t: supports_11n[%d]", cap->supports_11n); + WMA_LOGD("\t: supports_11ac[%d]", cap->supports_11ac); + WMA_LOGD("\t: supports_11ax[%d]", cap->supports_11ax); + WMA_LOGD("\t: supported_bands[%d]", cap->supported_bands); + WMA_LOGD("\t: ampdu_density[%d]", cap->ampdu_density); + WMA_LOGD("\t: max_bw_supported_2G[%d]", cap->max_bw_supported_2G); + WMA_LOGD("\t: ht_cap_info_2G[%d]", cap->ht_cap_info_2G); + WMA_LOGD("\t: vht_cap_info_2G[0x%0X]", cap->vht_cap_info_2G); + WMA_LOGD("\t: vht_supp_mcs_2G[0x%0X]", cap->vht_supp_mcs_2G); + WMA_LOGD("\t: tx_chain_mask_2G[%d]", cap->tx_chain_mask_2G); + WMA_LOGD("\t: rx_chain_mask_2G[%d]", cap->rx_chain_mask_2G); + WMA_LOGD("\t: max_bw_supported_5G[%d]", cap->max_bw_supported_5G); + WMA_LOGD("\t: ht_cap_info_5G[%d]", cap->ht_cap_info_5G); + WMA_LOGD("\t: vht_cap_info_5G[0x%0X]", cap->vht_cap_info_5G); + WMA_LOGD("\t: vht_supp_mcs_5G[0x%0X]", cap->vht_supp_mcs_5G); + WMA_LOGD("\t: tx_chain_mask_5G[%d]", cap->tx_chain_mask_5G); + WMA_LOGD("\t: rx_chain_mask_5G[%d]", cap->rx_chain_mask_5G); + WMA_LOGD("\t: he_cap_info_2G[0][%08x]", cap->he_cap_info_2G[0]); + WMA_LOGD("\t: he_cap_info_2G[1][%08x]", cap->he_cap_info_2G[1]); + WMA_LOGD("\t: he_supp_mcs_2G[%08x]", cap->he_supp_mcs_2G); + WMA_LOGD("\t: he_cap_info_5G[0][%08x]", cap->he_cap_info_5G[0]); + WMA_LOGD("\t: he_cap_info_5G[1][%08x]", cap->he_cap_info_5G[1]); + WMA_LOGD("\t: he_supp_mcs_5G[%08x]", cap->he_supp_mcs_5G); + qdf_mem_copy(mac_2G, cap->he_cap_info_2G, sizeof(mac_2G)); + qdf_mem_copy(mac_5G, cap->he_cap_info_5G, sizeof(mac_5G)); + qdf_mem_copy(phy_2G, cap->he_cap_phy_info_2G, + WMI_MAX_HECAP_PHY_SIZE * 4); + qdf_mem_copy(phy_5G, cap->he_cap_phy_info_5G, + WMI_MAX_HECAP_PHY_SIZE * 4); + ppet_2G = cap->he_ppet2G; + ppet_5G = cap->he_ppet5G; + + wma_print_he_mac_cap_w1(mac_2G[0]); + wma_print_he_mac_cap_w2(mac_2G[1]); + wma_print_he_phy_cap(phy_2G); + wma_print_he_ppet(&ppet_2G); + wma_print_he_mac_cap_w1(mac_5G[0]); + wma_print_he_mac_cap_w1(mac_5G[1]); + wma_print_he_phy_cap(phy_5G); + wma_print_he_ppet(&ppet_5G); +} + +/** + * wma_print_populate_soc_caps() - Prints all the caps populated per hw mode + * @tgt_info: target related info + * + * This function prints all the caps populater per hw mode and per PHY + * + * Return: none + */ +static void wma_print_populate_soc_caps(struct target_psoc_info *tgt_hdl) +{ + int i, num_hw_modes, total_mac_phy_cnt; + struct wlan_psoc_host_mac_phy_caps *mac_phy_cap, *tmp; + + num_hw_modes = target_psoc_get_num_hw_modes(tgt_hdl); + total_mac_phy_cnt = target_psoc_get_total_mac_phy_cnt(tgt_hdl); + + /* print number of hw modes */ + WMA_LOGD("%s: num of hw modes [%d]", __func__, num_hw_modes); + WMA_LOGD("%s: num mac_phy_cnt [%d]", __func__, total_mac_phy_cnt); + + mac_phy_cap = target_psoc_get_mac_phy_cap(tgt_hdl); + if (!mac_phy_cap) { + WMA_LOGE("Invalid MAC PHY capabilities handle"); + return; + } + + WMA_LOGD("%s: <====== HW mode cap printing starts ======>", __func__); + /* print cap of each hw mode */ + for (i = 0; i < total_mac_phy_cnt; i++) { + if (&mac_phy_cap[i]) { + WMA_LOGD("====>: hw mode id[%d], phy id[%d]", + mac_phy_cap[i].hw_mode_id, + mac_phy_cap[i].phy_id); + tmp = &mac_phy_cap[i]; + wma_print_mac_phy_capabilities(tmp, i); + } + } + WMA_LOGD("%s: <====== HW mode cap printing ends ======>\n", __func__); +} + +/** + * wma_map_wmi_channel_width_to_hw_mode_bw() - returns bandwidth + * in terms of hw_mode_bandwidth + * @width: bandwidth in terms of wmi_channel_width + * + * This function returns the bandwidth in terms of hw_mode_bandwidth. + * + * Return: BW in terms of hw_mode_bandwidth. + */ +static enum hw_mode_bandwidth wma_map_wmi_channel_width_to_hw_mode_bw( + wmi_channel_width width) +{ + switch (width) { + case WMI_CHAN_WIDTH_20: + return HW_MODE_20_MHZ; + case WMI_CHAN_WIDTH_40: + return HW_MODE_40_MHZ; + case WMI_CHAN_WIDTH_80: + return HW_MODE_80_MHZ; + case WMI_CHAN_WIDTH_160: + return HW_MODE_160_MHZ; + case WMI_CHAN_WIDTH_80P80: + return HW_MODE_80_PLUS_80_MHZ; + case WMI_CHAN_WIDTH_5: + return HW_MODE_5_MHZ; + case WMI_CHAN_WIDTH_10: + return HW_MODE_10_MHZ; + default: + return HW_MODE_BW_NONE; + } + + return HW_MODE_BW_NONE; +} + +/** + * wma_get_hw_mode_params() - get TX-RX stream and bandwidth + * supported from the capabilities. + * @caps: PHY capability + * @info: param to store TX-RX stream and BW information + * + * This function will calculate TX-RX stream and bandwidth supported + * as per the PHY capability, and assign to mac_ss_bw_info. + * + * Return: none + */ +static void wma_get_hw_mode_params(struct wlan_psoc_host_mac_phy_caps *caps, + struct mac_ss_bw_info *info) +{ + if (!caps) { + WMA_LOGE("%s: Invalid capabilities", __func__); + return; + } + + info->mac_tx_stream = wma_get_num_of_setbits_from_bitmask( + QDF_MAX(caps->tx_chain_mask_2G, + caps->tx_chain_mask_5G)); + info->mac_rx_stream = wma_get_num_of_setbits_from_bitmask( + QDF_MAX(caps->rx_chain_mask_2G, + caps->rx_chain_mask_5G)); + info->mac_bw = wma_map_wmi_channel_width_to_hw_mode_bw( + QDF_MAX(caps->max_bw_supported_2G, + caps->max_bw_supported_5G)); +} + +/** + * wma_set_hw_mode_params() - sets TX-RX stream, bandwidth and + * DBS in hw_mode_list + * @wma_handle: pointer to wma global structure + * @mac0_ss_bw_info: TX-RX streams, BW for MAC0 + * @mac1_ss_bw_info: TX-RX streams, BW for MAC1 + * @pos: refers to hw_mode_index + * @dbs_mode: dbs_mode for the dbs_hw_mode + * @sbs_mode: sbs_mode for the sbs_hw_mode + * + * This function sets TX-RX stream, bandwidth and DBS mode in + * hw_mode_list. + * + * Return: none + */ +static void wma_set_hw_mode_params(t_wma_handle *wma_handle, + struct mac_ss_bw_info mac0_ss_bw_info, + struct mac_ss_bw_info mac1_ss_bw_info, + uint32_t pos, uint32_t dbs_mode, + uint32_t sbs_mode) +{ + WMA_HW_MODE_MAC0_TX_STREAMS_SET( + wma_handle->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_tx_stream); + WMA_HW_MODE_MAC0_RX_STREAMS_SET( + wma_handle->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_rx_stream); + WMA_HW_MODE_MAC0_BANDWIDTH_SET( + wma_handle->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_bw); + WMA_HW_MODE_MAC1_TX_STREAMS_SET( + wma_handle->hw_mode.hw_mode_list[pos], + mac1_ss_bw_info.mac_tx_stream); + WMA_HW_MODE_MAC1_RX_STREAMS_SET( + wma_handle->hw_mode.hw_mode_list[pos], + mac1_ss_bw_info.mac_rx_stream); + WMA_HW_MODE_MAC1_BANDWIDTH_SET( + wma_handle->hw_mode.hw_mode_list[pos], + mac1_ss_bw_info.mac_bw); + WMA_HW_MODE_DBS_MODE_SET( + wma_handle->hw_mode.hw_mode_list[pos], + dbs_mode); + WMA_HW_MODE_AGILE_DFS_SET( + wma_handle->hw_mode.hw_mode_list[pos], + HW_MODE_AGILE_DFS_NONE); + WMA_HW_MODE_SBS_MODE_SET( + wma_handle->hw_mode.hw_mode_list[pos], + sbs_mode); +} + +/** + * wma_update_hw_mode_list() - updates hw_mode_list + * @wma_handle: pointer to wma global structure + * @tgt_hdl - target psoc information + * + * This function updates hw_mode_list with tx_streams, rx_streams, + * bandwidth, dbs and agile dfs for each hw_mode. + * + * Returns: 0 for success else failure. + */ +static QDF_STATUS wma_update_hw_mode_list(t_wma_handle *wma_handle, + struct target_psoc_info *tgt_hdl) +{ + struct wlan_psoc_host_mac_phy_caps *tmp, *mac_phy_cap; + uint32_t i, hw_config_type, j = 0; + uint32_t dbs_mode, sbs_mode; + struct mac_ss_bw_info mac0_ss_bw_info = {0}; + struct mac_ss_bw_info mac1_ss_bw_info = {0}; + WMI_PHY_CAPABILITY new_supported_band = 0; + bool supported_band_update_failure = false; + struct wlan_psoc_target_capability_info *tgt_cap_info; + int num_hw_modes; + + if (!wma_handle) { + WMA_LOGE("%s: Invalid wma handle", __func__); + return QDF_STATUS_E_FAILURE; + } + + num_hw_modes = target_psoc_get_num_hw_modes(tgt_hdl); + mac_phy_cap = target_psoc_get_mac_phy_cap(tgt_hdl); + tgt_cap_info = target_psoc_get_target_caps(tgt_hdl); + + if (!mac_phy_cap) { + WMA_LOGE("mac_phy_cap Null"); + return QDF_STATUS_E_FAILURE; + } + + /* + * This list was updated as part of service ready event. Re-populate + * HW mode list from the device capabilities. + */ + if (wma_handle->hw_mode.hw_mode_list) { + qdf_mem_free(wma_handle->hw_mode.hw_mode_list); + wma_handle->hw_mode.hw_mode_list = NULL; + WMA_LOGD("%s: DBS list is freed", __func__); + } + + wma_handle->hw_mode.hw_mode_list = + qdf_mem_malloc(sizeof(*wma_handle->hw_mode.hw_mode_list) * + num_hw_modes); + if (!wma_handle->hw_mode.hw_mode_list) { + wma_handle->num_dbs_hw_modes = 0; + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("%s: Updated HW mode list: Num modes:%d", + __func__, num_hw_modes); + + wma_handle->num_dbs_hw_modes = num_hw_modes; + for (i = 0; i < num_hw_modes; i++) { + /* Update for MAC0 */ + tmp = &mac_phy_cap[j++]; + wma_get_hw_mode_params(tmp, &mac0_ss_bw_info); + hw_config_type = tmp->hw_mode_config_type; + dbs_mode = HW_MODE_DBS_NONE; + sbs_mode = HW_MODE_SBS_NONE; + mac1_ss_bw_info.mac_tx_stream = 0; + mac1_ss_bw_info.mac_rx_stream = 0; + mac1_ss_bw_info.mac_bw = 0; + if (wma_update_supported_bands(tmp->supported_bands, + &new_supported_band) + != QDF_STATUS_SUCCESS) + supported_band_update_failure = true; + + /* SBS and DBS have dual MAC. Upto 2 MACs are considered. */ + if ((hw_config_type == WMI_HW_MODE_DBS) || + (hw_config_type == WMI_HW_MODE_SBS_PASSIVE) || + (hw_config_type == WMI_HW_MODE_SBS)) { + /* Update for MAC1 */ + tmp = &mac_phy_cap[j++]; + wma_get_hw_mode_params(tmp, &mac1_ss_bw_info); + if (hw_config_type == WMI_HW_MODE_DBS) + dbs_mode = HW_MODE_DBS; + if ((hw_config_type == WMI_HW_MODE_SBS_PASSIVE) || + (hw_config_type == WMI_HW_MODE_SBS)) + sbs_mode = HW_MODE_SBS; + if (QDF_STATUS_SUCCESS != + wma_update_supported_bands(tmp->supported_bands, + &new_supported_band)) + supported_band_update_failure = true; + } + + /* Updating HW mode list */ + wma_set_hw_mode_params(wma_handle, mac0_ss_bw_info, + mac1_ss_bw_info, i, dbs_mode, + sbs_mode); + } + + /* overwrite phy_capability which we got from service ready event */ + if (!supported_band_update_failure) { + WMA_LOGD("%s: updating supported band from old[%d] to new[%d]", + __func__, target_if_get_phy_capability(tgt_hdl), + new_supported_band); + target_if_set_phy_capability(tgt_hdl, new_supported_band); + } + + if (QDF_STATUS_SUCCESS != + policy_mgr_update_hw_mode_list(wma_handle->psoc, + tgt_hdl)) + WMA_LOGE("%s: failed to update policy manager", __func__); + wma_dump_dbs_hw_mode(wma_handle); + return QDF_STATUS_SUCCESS; +} + +static void wma_init_wifi_pos_dma_rings(t_wma_handle *wma_handle, + uint8_t num_mac, void *buf) +{ + struct hif_opaque_softc *hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + void *hal_soc; + + if (!hif_ctx) { + WMA_LOGE("invalid hif context"); + return; + } + + hal_soc = hif_get_hal_handle(hif_ctx); + + wifi_pos_init_cir_cfr_rings(wma_handle->psoc, hal_soc, num_mac, buf); +} + +/** + * wma_populate_soc_caps() - populate entire SOC's capabilities + * @wma_handle: pointer to wma global structure + * @tgt_hdl: target psoc information + * @param_buf: pointer to param of service ready extension event from fw + * + * This API populates all capabilities of entire SOC. For example, + * how many number of hw modes are supported by this SOC, what are the + * capabilities of each phy per hw mode, what are HAL reg capabilities per + * phy. + * + * Return: none + */ +static void wma_populate_soc_caps(t_wma_handle *wma_handle, + struct target_psoc_info *tgt_hdl, + WMI_SERVICE_READY_EXT_EVENTID_param_tlvs *param_buf) +{ + + WMA_LOGD("%s: Enter", __func__); + + wma_init_wifi_pos_dma_rings(wma_handle, + param_buf->num_oem_dma_ring_caps, + param_buf->oem_dma_ring_caps); + + wma_print_populate_soc_caps(tgt_hdl); +} + +/** + * wma_init_dbr_params() - init dbr params + * @wma_handle: pointer to wma global structure + * + * This API initializes params of direct buffer rx component. + * + * Return: none + */ +#ifdef DIRECT_BUF_RX_ENABLE +static void wma_init_dbr_params(t_wma_handle *wma_handle) +{ + struct hif_opaque_softc *hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + void *hal_soc; + + if (!hif_ctx) { + WMA_LOGE("invalid hif context"); + return; + } + + hal_soc = hif_get_hal_handle(hif_ctx); + direct_buf_rx_target_attach(wma_handle->psoc, hal_soc, + wma_handle->qdf_dev); +} +#else +static inline void wma_init_dbr_params(t_wma_handle *wma_handle) +{ +} +#endif + +/** + * wma_set_coex_res_cfg() - Set target COEX resource configuration. + * @wma_handle: pointer to wma global structure + * @wlan_res_cfg: Pointer to target resource configuration + * + * Return: none + */ +#ifdef FEATURE_COEX_CONFIG +static void wma_set_coex_res_cfg(t_wma_handle *wma_handle, + struct wmi_unified *wmi_handle, + target_resource_config *wlan_res_cfg) +{ + if (cfg_get(wma_handle->psoc, CFG_THREE_WAY_COEX_CONFIG_LEGACY) && + wmi_service_enabled(wmi_handle, + wmi_service_three_way_coex_config_legacy)) { + wlan_res_cfg->three_way_coex_config_legacy_en = true; + } else { + wlan_res_cfg->three_way_coex_config_legacy_en = false; + } +} +#else +static void wma_set_coex_res_cfg(t_wma_handle *wma_handle, + struct wmi_unified *wmi_handle, + target_resource_config *wlan_res_cfg) +{ +} +#endif + +/** + * wma_rx_service_ready_ext_event() - evt handler for sevice ready ext event. + * @handle: wma handle + * @event: params of the service ready extended event + * @length: param length + * + * Return: none + */ +int wma_rx_service_ready_ext_event(void *handle, uint8_t *event, + uint32_t length) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + WMI_SERVICE_READY_EXT_EVENTID_param_tlvs *param_buf; + wmi_service_ready_ext_event_fixed_param *ev; + QDF_STATUS ret; + struct target_psoc_info *tgt_hdl; + uint32_t conc_scan_config_bits, fw_config_bits; + struct wmi_unified *wmi_handle; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + target_resource_config *wlan_res_cfg; + + WMA_LOGD("%s: Enter", __func__); + + if (!wma_handle) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(wma_handle->psoc); + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); + if (!tgt_hdl) { + WMA_LOGE("%s: target psoc info is NULL", __func__); + return -EINVAL; + } + + wlan_res_cfg = target_psoc_get_wlan_res_cfg(tgt_hdl); + param_buf = (WMI_SERVICE_READY_EXT_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("%s: Invalid event", __func__); + return -EINVAL; + } + + ev = param_buf->fixed_param; + if (!ev) { + WMA_LOGE("%s: Invalid buffer", __func__); + return -EINVAL; + } + + WMA_LOGD("WMA <-- WMI_SERVICE_READY_EXT_EVENTID"); + + fw_config_bits = target_if_get_fw_config_bits(tgt_hdl); + conc_scan_config_bits = target_if_get_conc_scan_config_bits(tgt_hdl); + + WMA_LOGD("%s: Defaults: scan config:%x FW mode config:%x", + __func__, conc_scan_config_bits, fw_config_bits); + + ret = qdf_mc_timer_stop(&wma_handle->service_ready_ext_timer); + if (!QDF_IS_STATUS_SUCCESS(ret)) { + WMA_LOGE("Failed to stop the service ready ext timer"); + return -EINVAL; + } + wma_populate_soc_caps(wma_handle, tgt_hdl, param_buf); + + ret = wma_update_hw_mode_list(wma_handle, tgt_hdl); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed to update hw mode list"); + return -EINVAL; + } + + WMA_LOGD("WMA --> WMI_INIT_CMDID"); + + if (wma_is_dbs_mandatory(wma_handle->psoc, tgt_hdl) && + (policy_mgr_is_dual_mac_disabled_in_ini(wma_handle->psoc))) { + policy_mgr_set_dual_mac_feature(wma_handle->psoc, + ENABLE_DBS_CXN_AND_DISABLE_SIMULTANEOUS_SCAN); + policy_mgr_set_ch_select_plcy(wma_handle->psoc, + POLICY_MGR_CH_SELECT_POLICY_DEF); + } + wma_init_scan_fw_mode_config(wma_handle->psoc, conc_scan_config_bits, + fw_config_bits); + + target_psoc_set_num_radios(tgt_hdl, 1); + + if (wmi_service_enabled(wmi_handle, + wmi_service_new_htt_msg_format)) { + cdp_cfg_set_new_htt_msg_format(soc, 1); + wlan_res_cfg->new_htt_msg_format = true; + } else { + cdp_cfg_set_new_htt_msg_format(soc, 0); + wlan_res_cfg->new_htt_msg_format = false; + } + + if (QDF_GLOBAL_FTM_MODE != cds_get_conparam() && + ucfg_mlme_get_peer_unmap_conf(wma_handle->psoc) && + wmi_service_enabled(wmi_handle, + wmi_service_peer_unmap_cnf_support)) { + wlan_res_cfg->peer_unmap_conf_support = true; + cdp_cfg_set_peer_unmap_conf_support(soc, true); + } else { + wlan_res_cfg->peer_unmap_conf_support = false; + cdp_cfg_set_peer_unmap_conf_support(soc, false); + } + + if (wma_handle->enable_tx_compl_tsf64 && + wmi_service_enabled(wmi_handle, + wmi_service_tx_compl_tsf64)) { + wlan_res_cfg->tstamp64_en = true; + cdp_cfg_set_tx_compl_tsf64(soc, true); + } else { + wlan_res_cfg->tstamp64_en = false; + cdp_cfg_set_tx_compl_tsf64(soc, false); + } + + if (wmi_service_enabled(wma_handle->wmi_handle, wmi_service_nan_vdev)) + ucfg_nan_set_vdev_creation_supp_by_fw(wma_handle->psoc, true); + + /* + * Firmware can accommodate maximum 4 vdevs and the ini gNumVdevs + * indicates the same. + * If host driver is going to create vdev for NAN, it indicates + * the total no.of vdevs supported to firmware which includes the + * NAN vdev. + * If firmware is going to create NAN discovery vdev, host should + * indicate 3 vdevs and firmware shall add 1 vdev for NAN. So decrement + * the num_vdevs by 1. + */ + if (ucfg_nan_is_vdev_creation_allowed(wma_handle->psoc) || + QDF_GLOBAL_FTM_MODE == cds_get_conparam()) { + wlan_res_cfg->nan_separate_iface_support = true; + } else { + wlan_res_cfg->num_vdevs--; + wma_update_num_peers_tids(wma_handle, wlan_res_cfg); + } + + WMA_LOGD("%s: num_vdevs: %u", __func__, wlan_res_cfg->num_vdevs); + + wma_init_dbr_params(wma_handle); + + wma_set_coex_res_cfg(wma_handle, wmi_handle, wlan_res_cfg); + + return 0; +} + +/** + * wma_rx_ready_event() - event handler to process + * wmi rx ready event. + * @handle: wma handle + * @cmd_param_info: command params info + * @length: param length + * + * Return: none + */ +int wma_rx_ready_event(void *handle, uint8_t *cmd_param_info, + uint32_t length) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + WMI_READY_EVENTID_param_tlvs *param_buf = NULL; + wmi_ready_event_fixed_param *ev = NULL; + int ret; + + WMA_LOGD("%s: Enter", __func__); + + param_buf = (WMI_READY_EVENTID_param_tlvs *) cmd_param_info; + if (!(wma_handle && param_buf)) { + WMA_LOGE("%s: Invalid arguments", __func__); + QDF_ASSERT(0); + return -EINVAL; + } + + WMA_LOGD("WMA <-- WMI_READY_EVENTID"); + + ev = param_buf->fixed_param; + /* Indicate to the waiting thread that the ready + * event was received + */ + wma_handle->sub_20_support = + wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_half_rate_quarter_rate_support); + wma_handle->wmi_ready = true; + wma_handle->wlan_init_status = ev->status; + + if (wma_handle->is_dfs_offloaded) + wmi_unified_dfs_phyerr_offload_en_cmd( + wma_handle->wmi_handle, 0); + /* copy the mac addr */ + WMI_MAC_ADDR_TO_CHAR_ARRAY(&ev->mac_addr, wma_handle->myaddr); + WMI_MAC_ADDR_TO_CHAR_ARRAY(&ev->mac_addr, wma_handle->hwaddr); + ret = wma_update_hdd_cfg(wma_handle); + if (ret) + return ret; + + WMA_LOGD("Exit"); + + return 0; +} + +/** + * wma_setneedshutdown() - setting wma needshutdown flag + * + * Return: none + */ +void wma_setneedshutdown(void) +{ + tp_wma_handle wma_handle; + + WMA_LOGD("%s: Enter", __func__); + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s: Invalid arguments", __func__); + QDF_ASSERT(0); + return; + } + + wma_handle->needShutdown = true; + WMA_LOGD("%s: Exit", __func__); +} + +/** + * wma_needshutdown() - Is wma needs shutdown? + * + * Return: returns true/false + */ +bool wma_needshutdown(void) +{ + tp_wma_handle wma_handle; + + WMA_LOGD("%s: Enter", __func__); + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s: Invalid arguments", __func__); + QDF_ASSERT(0); + return false; + } + + WMA_LOGD("%s: Exit", __func__); + return wma_handle->needShutdown; +} + +/** + * wma_wait_for_ready_event() - wait for wma ready event + * @handle: wma handle + * + * Return: 0 for success or QDF error + */ +QDF_STATUS wma_wait_for_ready_event(WMA_HANDLE handle) +{ + tp_wma_handle wma_handle = (tp_wma_handle)handle; + QDF_STATUS status; + struct target_psoc_info *tgt_hdl; + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); + if (!tgt_hdl) { + wma_err("target psoc info is NULL"); + return QDF_STATUS_E_INVAL; + } + + status = qdf_wait_for_event_completion(&tgt_hdl->info.event, + WMA_READY_EVENTID_TIMEOUT); + if (!tgt_hdl->info.wmi_ready) { + wma_err("Error in pdev creation"); + return QDF_STATUS_E_INVAL; + } + + if (status == QDF_STATUS_E_TIMEOUT) + wma_err("Timeout waiting for FW ready event"); + else if (QDF_IS_STATUS_ERROR(status)) + wma_err("Failed to wait for FW ready event; status:%u", status); + else + wma_info("FW ready event received"); + + return status; +} + +/** + * wma_set_ppsconfig() - set pps config in fw + * @vdev_id: vdev id + * @pps_param: pps params + * @val : param value + * + * Return: 0 for success or QDF error + */ +QDF_STATUS wma_set_ppsconfig(uint8_t vdev_id, uint16_t pps_param, + int val) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + int ret = -EIO; + uint32_t pps_val; + + if (!wma) { + WMA_LOGE("%s: Failed to get wma", __func__); + return QDF_STATUS_E_INVAL; + } + + switch (pps_param) { + case WMA_VHT_PPS_PAID_MATCH: + pps_val = ((val << 31) & 0xffff0000) | + (PKT_PWR_SAVE_PAID_MATCH & 0xffff); + goto pkt_pwr_save_config; + case WMA_VHT_PPS_GID_MATCH: + pps_val = ((val << 31) & 0xffff0000) | + (PKT_PWR_SAVE_GID_MATCH & 0xffff); + goto pkt_pwr_save_config; + case WMA_VHT_PPS_DELIM_CRC_FAIL: + pps_val = ((val << 31) & 0xffff0000) | + (PKT_PWR_SAVE_DELIM_CRC_FAIL & 0xffff); + goto pkt_pwr_save_config; + + /* Enable the code below as and when the functionality + * is supported/added in host. + */ +#ifdef NOT_YET + case WMA_VHT_PPS_EARLY_TIM_CLEAR: + pps_val = ((val << 31) & 0xffff0000) | + (PKT_PWR_SAVE_EARLY_TIM_CLEAR & 0xffff); + goto pkt_pwr_save_config; + case WMA_VHT_PPS_EARLY_DTIM_CLEAR: + pps_val = ((val << 31) & 0xffff0000) | + (PKT_PWR_SAVE_EARLY_DTIM_CLEAR & 0xffff); + goto pkt_pwr_save_config; + case WMA_VHT_PPS_EOF_PAD_DELIM: + pps_val = ((val << 31) & 0xffff0000) | + (PKT_PWR_SAVE_EOF_PAD_DELIM & 0xffff); + goto pkt_pwr_save_config; + case WMA_VHT_PPS_MACADDR_MISMATCH: + pps_val = ((val << 31) & 0xffff0000) | + (PKT_PWR_SAVE_MACADDR_MISMATCH & 0xffff); + goto pkt_pwr_save_config; + case WMA_VHT_PPS_GID_NSTS_ZERO: + pps_val = ((val << 31) & 0xffff0000) | + (PKT_PWR_SAVE_GID_NSTS_ZERO & 0xffff); + goto pkt_pwr_save_config; + case WMA_VHT_PPS_RSSI_CHECK: + pps_val = ((val << 31) & 0xffff0000) | + (PKT_PWR_SAVE_RSSI_CHECK & 0xffff); + goto pkt_pwr_save_config; +#endif /* NOT_YET */ +pkt_pwr_save_config: + WMA_LOGD("vdev_id:%d val:0x%x pps_val:0x%x", vdev_id, + val, pps_val); + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_PACKET_POWERSAVE, + pps_val); + break; + default: + WMA_LOGE("%s:INVALID PPS CONFIG", __func__); + } + + return (ret) ? QDF_STATUS_E_FAILURE : QDF_STATUS_SUCCESS; +} + +/** + * wma_process_set_mas() - Function to enable/disable MAS + * @wma: Pointer to WMA handle + * @mas_val: 1-Enable MAS, 0-Disable MAS + * + * This function enables/disables the MAS value + * + * Return: QDF_SUCCESS for success otherwise failure + */ +static QDF_STATUS wma_process_set_mas(tp_wma_handle wma, + uint32_t *mas_val) +{ + uint32_t val; + + if (!wma || !mas_val) { + WMA_LOGE("%s: Invalid input to enable/disable MAS", __func__); + return QDF_STATUS_E_FAILURE; + } + + val = (*mas_val); + + if (QDF_STATUS_SUCCESS != + wma_set_enable_disable_mcc_adaptive_scheduler(val)) { + WMA_LOGE("%s: Unable to enable/disable MAS", __func__); + return QDF_STATUS_E_FAILURE; + } + WMA_LOGE("%s: Value is %d", __func__, val); + return QDF_STATUS_SUCCESS; +} + +/** + * wma_process_set_miracast() - Function to set miracast value in WMA + * @wma: Pointer to WMA handle + * @miracast_val: 0-Disabled,1-Source,2-Sink + * + * This function stores the miracast value in WMA + * + * Return: QDF_SUCCESS for success otherwise failure + * + */ +static QDF_STATUS wma_process_set_miracast(tp_wma_handle wma, + uint32_t *miracast_val) +{ + if (!wma || !miracast_val) { + WMA_LOGE("%s: Invalid input to store miracast value", __func__); + return QDF_STATUS_E_FAILURE; + } + + wma->miracast_value = *miracast_val; + WMA_LOGE("%s: Miracast value is %d", __func__, wma->miracast_value); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_config_stats_factor() - Function to configure stats avg. factor + * @wma: pointer to WMA handle + * @avg_factor: stats. avg. factor passed down by userspace + * + * This function configures the avg. stats value in firmware + * + * Return: QDF_STATUS_SUCCESS for success otherwise failure + * + */ +static QDF_STATUS wma_config_stats_factor(tp_wma_handle wma, + struct sir_stats_avg_factor *avg_factor) +{ + QDF_STATUS ret; + + if (!wma || !avg_factor) { + WMA_LOGE("%s: Invalid input of stats avg factor", __func__); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma->wmi_handle, + avg_factor->vdev_id, + WMI_VDEV_PARAM_STATS_AVG_FACTOR, + avg_factor->stats_avg_factor); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE(" failed to set avg_factor for vdev_id %d", + avg_factor->vdev_id); + } + + WMA_LOGD("%s: Set stats_avg_factor %d for vdev_id %d", __func__, + avg_factor->stats_avg_factor, avg_factor->vdev_id); + + return ret; +} + +/** + * wma_config_guard_time() - Function to set guard time in firmware + * @wma: pointer to WMA handle + * @guard_time: guard time passed down by userspace + * + * This function configures the guard time in firmware + * + * Return: QDF_STATUS_SUCCESS for success otherwise failure + * + */ +static QDF_STATUS wma_config_guard_time(tp_wma_handle wma, + struct sir_guard_time_request *guard_time) +{ + QDF_STATUS ret; + + if (!wma || !guard_time) { + WMA_LOGE("%s: Invalid input of guard time", __func__); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma->wmi_handle, + guard_time->vdev_id, + WMI_VDEV_PARAM_RX_LEAK_WINDOW, + guard_time->guard_time); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE(" failed to set guard time for vdev_id %d", + guard_time->vdev_id); + } + + WMA_LOGD("Set guard time %d for vdev_id %d", + guard_time->guard_time, guard_time->vdev_id); + + return ret; +} + +/** + * wma_enable_specific_fw_logs() - Start/Stop logging of diag event/log id + * @wma_handle: WMA handle + * @start_log: Start logging related parameters + * + * Send the command to the FW based on which specific logging of diag + * event/log id can be started/stopped + * + * Return: None + */ +static void wma_enable_specific_fw_logs(tp_wma_handle wma_handle, + struct sir_wifi_start_log *start_log) +{ + + if (!start_log) { + WMA_LOGE("%s: start_log pointer is NULL", __func__); + return; + } + if (!wma_handle) { + WMA_LOGE("%s: Invalid wma handle", __func__); + return; + } + + if (!((start_log->ring_id == RING_ID_CONNECTIVITY) || + (start_log->ring_id == RING_ID_FIRMWARE_DEBUG))) { + WMA_LOGD("%s: Not connectivity or fw debug ring: %d", + __func__, start_log->ring_id); + return; + } + + wmi_unified_enable_specific_fw_logs_cmd(wma_handle->wmi_handle, + (struct wmi_wifi_start_log *)start_log); +} + +#define MEGABYTE (1024 * 1024) +/** + * wma_set_wifi_start_packet_stats() - Start/stop packet stats + * @wma_handle: WMA handle + * @start_log: Struture containing the start wifi logger params + * + * This function is used to send the WMA commands to start/stop logging + * of per packet statistics + * + * Return: None + * + */ +#ifdef REMOVE_PKT_LOG +static void wma_set_wifi_start_packet_stats(void *wma_handle, + struct sir_wifi_start_log *start_log) +{ +} + +#else +static void wma_set_wifi_start_packet_stats(void *wma_handle, + struct sir_wifi_start_log *start_log) +{ + struct hif_opaque_softc *scn; + uint32_t log_state; + + if (!start_log) { + WMA_LOGE("%s: start_log pointer is NULL", __func__); + return; + } + if (!wma_handle) { + WMA_LOGE("%s: Invalid wma handle", __func__); + return; + } + + /* No need to register for ring IDs other than packet stats */ + if (start_log->ring_id != RING_ID_PER_PACKET_STATS) { + WMA_LOGD("%s: Ring id is not for per packet stats: %d", + __func__, start_log->ring_id); + return; + } + + scn = cds_get_context(QDF_MODULE_ID_HIF); + if (!scn) { + WMA_LOGE("%s: Invalid HIF handle", __func__); + return; + } + +#ifdef HELIUMPLUS + log_state = ATH_PKTLOG_ANI | ATH_PKTLOG_RCUPDATE | ATH_PKTLOG_RCFIND | + ATH_PKTLOG_RX | ATH_PKTLOG_TX | + ATH_PKTLOG_TEXT | ATH_PKTLOG_SW_EVENT; +#elif defined(QCA_WIFI_QCA6390) || defined(QCA_WIFI_QCA6490) || \ + defined(QCA_WIFI_QCA6750) + log_state = ATH_PKTLOG_RCFIND | ATH_PKTLOG_RCUPDATE | + ATH_PKTLOG_TX | ATH_PKTLOG_LITE_T2H | + ATH_PKTLOG_SW_EVENT | ATH_PKTLOG_RX; +#elif defined(QCA_WIFI_QCA6290) + log_state = ATH_PKTLOG_LITE_RX | ATH_PKTLOG_LITE_T2H; +#else + WMA_LOGD("%s: Packet log Not supported", __func__); + log_state = 0; +#endif + if (start_log->size != 0) { + pktlog_setsize(scn, start_log->size * MEGABYTE); + return; + } else if (start_log->is_pktlog_buff_clear == true) { + pktlog_clearbuff(scn, start_log->is_pktlog_buff_clear); + return; + } + + if (start_log->verbose_level == WLAN_LOG_LEVEL_ACTIVE) { + pktlog_enable(scn, log_state, start_log->ini_triggered, + start_log->user_triggered, + start_log->is_iwpriv_command); + WMA_LOGD("%s: Enabling per packet stats", __func__); + } else { + pktlog_enable(scn, 0, start_log->ini_triggered, + start_log->user_triggered, + start_log->is_iwpriv_command); + WMA_LOGD("%s: Disabling per packet stats", __func__); + } +} +#endif + +/** + * wma_send_flush_logs_to_fw() - Send log flush command to FW + * @wma_handle: WMI handle + * + * This function is used to send the flush command to the FW, + * that will flush the fw logs that are residue in the FW + * + * Return: None + */ +void wma_send_flush_logs_to_fw(tp_wma_handle wma_handle) +{ + QDF_STATUS status; + + status = wmi_unified_flush_logs_to_fw_cmd(wma_handle->wmi_handle); + if (QDF_IS_STATUS_ERROR(status)) + return; + + status = qdf_mc_timer_start(&wma_handle->log_completion_timer, + WMA_LOG_COMPLETION_TIMER); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to start the log completion timer"); +} + +/** + * wma_update_wep_default_key - To update default key id + * @wma: pointer to wma handler + * @update_def_key: pointer to wep_update_default_key_idx + * + * This function makes a copy of default key index to txrx node + * + * Return: Success + */ +static QDF_STATUS wma_update_wep_default_key(tp_wma_handle wma, + struct wep_update_default_key_idx *update_def_key) +{ + struct wma_txrx_node *iface = + &wma->interfaces[update_def_key->session_id]; + iface->wep_default_key_idx = update_def_key->default_idx; + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_update_tx_fail_cnt_th() - Set threshold for TX pkt fail + * @wma_handle: WMA handle + * @tx_fail_cnt_th: sme_tx_fail_cnt_threshold parameter + * + * This function is used to set Tx pkt fail count threshold, + * FW will do disconnect with station once this threshold is reached. + * + * Return: QDF_STATUS + */ +static QDF_STATUS wma_update_tx_fail_cnt_th(tp_wma_handle wma, + struct sme_tx_fail_cnt_threshold *tx_fail_cnt_th) +{ + u_int8_t vdev_id; + u_int32_t tx_fail_disconn_th; + int ret = -EIO; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE(FL("WMA is closed, can not issue Tx pkt fail count threshold")); + return QDF_STATUS_E_INVAL; + } + vdev_id = tx_fail_cnt_th->session_id; + tx_fail_disconn_th = tx_fail_cnt_th->tx_fail_cnt_threshold; + WMA_LOGD("Set TX pkt fail count threshold vdevId %d count %d", + vdev_id, tx_fail_disconn_th); + + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_DISCONNECT_TH, + tx_fail_disconn_th); + + if (ret) { + WMA_LOGE(FL("Failed to send TX pkt fail count threshold command")); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_update_short_retry_limit() - Set retry limit for short frames + * @wma_handle: WMA handle + * @short_retry_limit_th: retry limir count for Short frames. + * + * This function is used to configure the transmission retry limit at which + * short frames needs to be retry. + * + * Return: QDF_STATUS + */ +static QDF_STATUS wma_update_short_retry_limit(tp_wma_handle wma, + struct sme_short_retry_limit *short_retry_limit_th) +{ + uint8_t vdev_id; + uint32_t short_retry_limit; + int ret; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("WMA is closed, can not issue short retry limit threshold"); + return QDF_STATUS_E_INVAL; + } + vdev_id = short_retry_limit_th->session_id; + short_retry_limit = short_retry_limit_th->short_retry_limit; + WMA_LOGD("Set short retry limit threshold vdevId %d count %d", + vdev_id, short_retry_limit); + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_NON_AGG_SW_RETRY_TH, + short_retry_limit); + + if (ret) { + WMA_LOGE("Failed to send short limit threshold command"); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** + * wma_update_long_retry_limit() - Set retry limit for long frames + * @wma_handle: WMA handle + * @long_retry_limit_th: retry limir count for long frames + * + * This function is used to configure the transmission retry limit at which + * long frames needs to be retry + * + * Return: QDF_STATUS + */ +static QDF_STATUS wma_update_long_retry_limit(tp_wma_handle wma, + struct sme_long_retry_limit *long_retry_limit_th) +{ + uint8_t vdev_id; + uint32_t long_retry_limit; + int ret; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("WMA is closed, can not issue long retry limit threshold"); + return QDF_STATUS_E_INVAL; + } + vdev_id = long_retry_limit_th->session_id; + long_retry_limit = long_retry_limit_th->long_retry_limit; + WMA_LOGD("Set TX pkt fail count threshold vdevId %d count %d", + vdev_id, long_retry_limit); + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_AGG_SW_RETRY_TH, + long_retry_limit); + + if (ret) { + WMA_LOGE("Failed to send long limit threshold command"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/* + * wma_update_sta_inactivity_timeout() - Set sta_inactivity_timeout to fw + * @wma_handle: WMA handle + * @sta_inactivity_timer: sme_sta_inactivity_timeout + * + * This function is used to set sta_inactivity_timeout. + * If a station does not send anything in sta_inactivity_timeout seconds, an + * empty data frame is sent to it in order to verify whether it is + * still in range. If this frame is not ACKed, the station will be + * disassociated and then deauthenticated. + * + * Return: None + */ +void wma_update_sta_inactivity_timeout(tp_wma_handle wma, + struct sme_sta_inactivity_timeout *sta_inactivity_timer) +{ + uint8_t vdev_id; + uint32_t max_unresponsive_time; + uint32_t min_inactive_time, max_inactive_time; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("WMA is closed, can not issue sta_inactivity_timeout"); + return; + } + vdev_id = sta_inactivity_timer->session_id; + max_unresponsive_time = sta_inactivity_timer->sta_inactivity_timeout; + max_inactive_time = max_unresponsive_time * TWO_THIRD; + min_inactive_time = max_unresponsive_time - max_inactive_time; + + if (wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + min_inactive_time)) + WMA_LOGE("Failed to Set AP MIN IDLE INACTIVE TIME"); + + if (wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + max_inactive_time)) + WMA_LOGE("Failed to Set AP MAX IDLE INACTIVE TIME"); + + if (wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + max_unresponsive_time)) + WMA_LOGE("Failed to Set MAX UNRESPONSIVE TIME"); + + WMA_LOGD("%s:vdev_id:%d min_inactive_time: %u max_inactive_time: %u max_unresponsive_time: %u", + __func__, vdev_id, + min_inactive_time, max_inactive_time, + max_unresponsive_time); +} + +#ifdef WLAN_FEATURE_WOW_PULSE + + +#define WMI_WOW_HOSTWAKEUP_GPIO_CMD_FIXED_PARAM \ +WMI_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMD_fixed_param + + +#define WMITLV_TAG_HOSTWAKEUP_GPIO_CMD_FIXED_PARAM \ +WMITLV_TAG_STRUC_wmi_wow_hostwakeup_gpio_pin_pattern_config_cmd_fixed_param + +/** + * wma_send_wow_pulse_cmd() - send wmi cmd of wow pulse cmd + * information to fw. + * @wma_handle: wma handler + * @udp_response: wow_pulse_mode pointer + * + * Return: Return QDF_STATUS + */ +static QDF_STATUS wma_send_wow_pulse_cmd(tp_wma_handle wma_handle, + struct wow_pulse_mode *wow_pulse_cmd) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + wmi_buf_t buf; + WMI_WOW_HOSTWAKEUP_GPIO_CMD_FIXED_PARAM *cmd; + u_int16_t len; + + len = sizeof(*cmd); + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (WMI_WOW_HOSTWAKEUP_GPIO_CMD_FIXED_PARAM *)wmi_buf_data(buf); + qdf_mem_zero(cmd, len); + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_HOSTWAKEUP_GPIO_CMD_FIXED_PARAM, + WMITLV_GET_STRUCT_TLVLEN( + WMI_WOW_HOSTWAKEUP_GPIO_CMD_FIXED_PARAM)); + + cmd->enable = wow_pulse_cmd->wow_pulse_enable; + cmd->pin = wow_pulse_cmd->wow_pulse_pin; + cmd->interval_low = wow_pulse_cmd->wow_pulse_interval_low; + cmd->interval_high = wow_pulse_cmd->wow_pulse_interval_high; + cmd->repeat_cnt = WMI_WOW_PULSE_REPEAT_CNT; + + if (wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMDID)) { + wmi_buf_free(buf); + status = QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("%s: Exit", __func__); + return status; +} + +#undef WMI_WOW_HOSTWAKEUP_GPIO_CMD_FIXED_PARAM +#undef WMITLV_TAG_HOSTWAKEUP_GPIO_CMD_FIXED_PARAM +#undef WMI_WOW_PULSE_REPEAT_CNT + +#else +static inline QDF_STATUS wma_send_wow_pulse_cmd(tp_wma_handle wma_handle, + struct wow_pulse_mode *wow_pulse_cmd) +{ + return QDF_STATUS_E_FAILURE; +} +#endif + + +/** + * wma_process_power_debug_stats_req() - Process the Chip Power stats collect + * request and pass the Power stats request to Fw + * @wma_handle: WMA handle + * + * Return: QDF_STATUS + */ +#ifdef WLAN_POWER_DEBUG +static QDF_STATUS wma_process_power_debug_stats_req(tp_wma_handle wma_handle) +{ + wmi_pdev_get_chip_power_stats_cmd_fixed_param *cmd; + int32_t len; + wmi_buf_t buf; + uint8_t *buf_ptr; + int ret; + + if (!wma_handle) { + WMA_LOGE("%s: input pointer is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(*cmd); + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (u_int8_t *) wmi_buf_data(buf); + cmd = (wmi_pdev_get_chip_power_stats_cmd_fixed_param *) buf_ptr; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_get_chip_power_stats_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_pdev_get_chip_power_stats_cmd_fixed_param)); + cmd->pdev_id = 0; + + WMA_LOGD("POWER_DEBUG_STATS - Get Request Params; Pdev id - %d", + cmd->pdev_id); + ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_PDEV_GET_CHIP_POWER_STATS_CMDID); + if (ret) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS wma_process_power_debug_stats_req(tp_wma_handle wma_handle) +{ + return QDF_STATUS_SUCCESS; +} +#endif +#ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS +static QDF_STATUS wma_process_beacon_debug_stats_req(tp_wma_handle wma_handle, + uint32_t *vdev_id) +{ + wmi_vdev_get_bcn_recv_stats_cmd_fixed_param *cmd; + int32_t len; + wmi_buf_t buf; + uint8_t *buf_ptr; + int ret; + + WMA_LOGD("%s: Enter", __func__); + if (!wma_handle) { + WMA_LOGE("%s: input pointer is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(*cmd); + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (u_int8_t *)wmi_buf_data(buf); + cmd = (wmi_vdev_get_bcn_recv_stats_cmd_fixed_param *)buf_ptr; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_get_bcn_recv_stats_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_vdev_get_bcn_recv_stats_cmd_fixed_param)); + cmd->vdev_id = *vdev_id; + + WMA_LOGD("BEACON_DEBUG_STATS - Get Request Params; vdev id - %d", + cmd->vdev_id); + ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_VDEV_GET_BCN_RECEPTION_STATS_CMDID); + if (ret) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("%s: Exit", __func__); + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS wma_process_beacon_debug_stats_req(tp_wma_handle wma_handle, + uint32_t *vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * wma_set_arp_req_stats() - process set arp stats request command to fw + * @wma_handle: WMA handle + * @req_buf: set srp stats request buffer + * + * Return: None + */ +static void wma_set_arp_req_stats(WMA_HANDLE handle, + struct set_arp_stats_params *req_buf) +{ + QDF_STATUS status; + struct set_arp_stats *arp_stats; + tp_wma_handle wma_handle = (tp_wma_handle) handle; + struct wlan_objmgr_vdev *vdev; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, cannot send per roam config", + __func__); + return; + } + + if (!wma_is_vdev_valid(req_buf->vdev_id)) { + WMA_LOGE("vdev id:%d is not active", req_buf->vdev_id); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma_handle->psoc, + req_buf->vdev_id, + WLAN_LEGACY_WMA_ID); + if (!vdev) { + WMA_LOGE("Can't get vdev by vdev_id:%d", req_buf->vdev_id); + return; + } + + if (!wma_is_vdev_up(req_buf->vdev_id)) { + WMA_LOGD("vdev id:%d is not started", req_buf->vdev_id); + goto release_ref; + } + + arp_stats = (struct set_arp_stats *)req_buf; + status = wmi_unified_set_arp_stats_req(wma_handle->wmi_handle, + arp_stats); + if (QDF_IS_STATUS_ERROR(status)) + wma_err("failed to set arp stats to FW"); + +release_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); +} + +/** + * wma_get_arp_req_stats() - process get arp stats request command to fw + * @wma_handle: WMA handle + * @req_buf: get srp stats request buffer + * + * Return: None + */ +static void wma_get_arp_req_stats(WMA_HANDLE handle, + struct get_arp_stats_params *req_buf) +{ + QDF_STATUS status; + struct get_arp_stats *arp_stats; + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, cannot send per roam config", + __func__); + return; + } + if (!wma_is_vdev_valid(req_buf->vdev_id)) { + WMA_LOGE("vdev id:%d is not active", req_buf->vdev_id); + return; + } + + arp_stats = (struct get_arp_stats *)req_buf; + status = wmi_unified_get_arp_stats_req(wma_handle->wmi_handle, + arp_stats); + if (QDF_IS_STATUS_ERROR(status)) + wma_err("failed to send get arp stats to FW"); +} + +/** + * wma_set_del_pmkid_cache() - API to set/delete PMKID cache entry in fw + * @handle: WMA handle + * @pmk_cache: PMK cache entry + * + * Return: None + */ +static void wma_set_del_pmkid_cache(WMA_HANDLE handle, + struct wmi_unified_pmk_cache *pmk_cache) +{ + QDF_STATUS status; + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("WMA is closed, cannot send set del pmkid"); + return; + } + + status = wmi_unified_set_del_pmkid_cache(wma_handle->wmi_handle, + pmk_cache); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to send set/del pmkid cmd to fw"); +} + +/** + * wma_send_invoke_neighbor_report() - API to send invoke neighbor report + * command to fw + * + * @handle: WMA handle + * @params: Pointer to invoke neighbor report params + * + * Return: None + */ +static +void wma_send_invoke_neighbor_report(WMA_HANDLE handle, + struct wmi_invoke_neighbor_report_params *params) +{ + QDF_STATUS status; + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("WMA is closed, cannot send invoke neighbor report"); + return; + } + + status = wmi_unified_invoke_neighbor_report_cmd(wma_handle->wmi_handle, + params); + + if (status != QDF_STATUS_SUCCESS) + WMA_LOGE("failed to send invoke neighbor report command"); +} + +QDF_STATUS wma_set_rx_reorder_timeout_val(tp_wma_handle wma_handle, + struct sir_set_rx_reorder_timeout_val *reorder_timeout) +{ + wmi_pdev_set_reorder_timeout_val_cmd_fixed_param *cmd; + uint32_t len; + wmi_buf_t buf; + int ret; + + if (!reorder_timeout) { + WMA_LOGE(FL("invalid pointer")); + return QDF_STATUS_E_INVAL; + } + + if (!wma_handle) { + WMA_LOGE(FL("WMA context is invald!")); + return QDF_STATUS_E_INVAL; + } + len = sizeof(*cmd); + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_pdev_set_reorder_timeout_val_cmd_fixed_param *) + wmi_buf_data(buf); + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_pdev_set_reorder_timeout_val_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN(wmi_pdev_set_reorder_timeout_val_cmd_fixed_param)); + + memcpy(cmd->rx_timeout_pri, reorder_timeout->rx_timeout_pri, + sizeof(reorder_timeout->rx_timeout_pri)); + + WMA_LOGD("rx aggr record timeout: VO: %d, VI: %d, BE: %d, BK: %d", + cmd->rx_timeout_pri[0], cmd->rx_timeout_pri[1], + cmd->rx_timeout_pri[2], cmd->rx_timeout_pri[3]); + + ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID); + if (ret) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_set_rx_blocksize(tp_wma_handle wma_handle, + struct sir_peer_set_rx_blocksize *peer_rx_blocksize) +{ + wmi_peer_set_rx_blocksize_cmd_fixed_param *cmd; + int32_t len; + wmi_buf_t buf; + u_int8_t *buf_ptr; + int ret; + + if (!peer_rx_blocksize) { + WMA_LOGE(FL("invalid pointer")); + return QDF_STATUS_E_INVAL; + } + + if (!wma_handle) { + WMA_LOGE(FL(" WMA context is invald!")); + return QDF_STATUS_E_INVAL; + } + + len = sizeof(*cmd); + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (u_int8_t *) wmi_buf_data(buf); + cmd = (wmi_peer_set_rx_blocksize_cmd_fixed_param *) buf_ptr; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_peer_set_rx_blocksize_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN(wmi_peer_set_rx_blocksize_cmd_fixed_param)); + + cmd->vdev_id = peer_rx_blocksize->vdev_id; + cmd->rx_block_ack_win_limit = + peer_rx_blocksize->rx_block_ack_win_limit; + WMI_CHAR_ARRAY_TO_MAC_ADDR(peer_rx_blocksize->peer_macaddr.bytes, + &cmd->peer_macaddr); + + WMA_LOGD("rx aggr blocksize: %d", cmd->rx_block_ack_win_limit); + + ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_PEER_SET_RX_BLOCKSIZE_CMDID); + if (ret) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_get_chain_rssi(tp_wma_handle wma_handle, + struct get_chain_rssi_req_params *req_params) +{ + wmi_pdev_div_get_rssi_antid_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint32_t len = sizeof(wmi_pdev_div_get_rssi_antid_fixed_param); + u_int8_t *buf_ptr; + + if (!wma_handle) { + WMA_LOGE(FL("WMA is closed, can not issue cmd")); + return QDF_STATUS_E_INVAL; + } + + wmi_buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!wmi_buf) + return QDF_STATUS_E_NOMEM; + + buf_ptr = (u_int8_t *)wmi_buf_data(wmi_buf); + + cmd = (wmi_pdev_div_get_rssi_antid_fixed_param *)buf_ptr; + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_pdev_div_get_rssi_antid_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_pdev_div_get_rssi_antid_fixed_param)); + cmd->pdev_id = 0; + WMI_CHAR_ARRAY_TO_MAC_ADDR(req_params->peer_macaddr.bytes, + &cmd->macaddr); + + if (wmi_unified_cmd_send(wma_handle->wmi_handle, wmi_buf, len, + WMI_PDEV_DIV_GET_RSSI_ANTID_CMDID)) { + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +#if defined(WLAN_FEATURE_FILS_SK) +/** + * wma_roam_scan_send_hlp() - API to send HLP IE info to fw + * @wma_handle: WMA handle + * @req: HLP params + * + * Return: QDF_STATUS + */ +static QDF_STATUS wma_roam_scan_send_hlp(tp_wma_handle wma_handle, + struct hlp_params *req) +{ + struct hlp_params *params; + QDF_STATUS status; + + params = qdf_mem_malloc(sizeof(*params)); + if (!params) + return QDF_STATUS_E_NOMEM; + + params->vdev_id = req->vdev_id; + params->hlp_ie_len = req->hlp_ie_len; + qdf_mem_copy(params->hlp_ie, req->hlp_ie, req->hlp_ie_len); + status = wmi_unified_roam_send_hlp_cmd(wma_handle->wmi_handle, params); + + WMA_LOGD("Send HLP status %d vdev id %d", status, params->vdev_id); + qdf_trace_hex_dump(QDF_MODULE_ID_WMI, QDF_TRACE_LEVEL_DEBUG, + params->hlp_ie, 10); + + qdf_mem_free(params); + return status; +} +#else +static QDF_STATUS wma_roam_scan_send_hlp(tp_wma_handle wma_handle, + struct hlp_params *req) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * wma_process_set_limit_off_chan() - set limit off chanel parameters + * @wma_handle: pointer to wma handle + * @param: pointer to sir_limit_off_chan + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +static QDF_STATUS wma_process_limit_off_chan(tp_wma_handle wma_handle, + struct sir_limit_off_chan *param) +{ + int32_t err; + struct wmi_limit_off_chan_param limit_off_chan_param; + + if (param->vdev_id >= wma_handle->max_bssid) { + WMA_LOGE(FL("Invalid vdev_id: %d"), param->vdev_id); + return QDF_STATUS_E_INVAL; + } + if (!wma_is_vdev_up(param->vdev_id)) { + WMA_LOGD("vdev %d is not up skipping limit_off_chan_param", + param->vdev_id); + return QDF_STATUS_E_INVAL; + } + + limit_off_chan_param.vdev_id = param->vdev_id; + limit_off_chan_param.status = param->is_tos_active; + limit_off_chan_param.max_offchan_time = param->max_off_chan_time; + limit_off_chan_param.rest_time = param->rest_time; + limit_off_chan_param.skip_dfs_chans = param->skip_dfs_chans; + + err = wmi_unified_send_limit_off_chan_cmd(wma_handle->wmi_handle, + &limit_off_chan_param); + if (err) { + WMA_LOGE("\n failed to set limit off chan cmd"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS wma_process_obss_color_collision_req(tp_wma_handle wma_handle, + struct wmi_obss_color_collision_cfg_param *cfg) +{ + QDF_STATUS status; + + if (cfg->vdev_id >= wma_handle->max_bssid) { + WMA_LOGE(FL("Invalid vdev_id: %d"), cfg->vdev_id); + return QDF_STATUS_E_INVAL; + } + if (!wma_is_vdev_up(cfg->vdev_id)) { + WMA_LOGE("vdev %d is not up skipping obss color collision req", + cfg->vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_send_obss_color_collision_cfg_cmd(wma_handle-> + wmi_handle, cfg); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to send obss color collision cfg"); + + return status; +} + +/** + * wma_send_obss_detection_cfg() - send obss detection cfg to firmware + * @wma_handle: pointer to wma handle + * @cfg: obss detection configuration + * + * Send obss detection configuration to firmware. + * + * Return: None + */ +static void wma_send_obss_detection_cfg(tp_wma_handle wma_handle, + struct wmi_obss_detection_cfg_param + *cfg) +{ + QDF_STATUS status; + + if (cfg->vdev_id >= wma_handle->max_bssid) { + WMA_LOGE(FL("Invalid vdev_id: %d"), cfg->vdev_id); + return; + } + if (!wma_is_vdev_up(cfg->vdev_id)) { + WMA_LOGE("vdev %d is not up skipping obss detection req", + cfg->vdev_id); + return; + } + + status = wmi_unified_send_obss_detection_cfg_cmd(wma_handle->wmi_handle, + cfg); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to send obss detection cfg"); + + return; +} + +#ifdef WLAN_FEATURE_MOTION_DETECTION +/** + * wma_motion_det_host_event_handler - motion detection event handler + * @handle: WMA global handle + * @event: motion detection event + * @len: Length of cmd + * + * Call motion detection event callback handler + * + * Return: 0 on success, else error on failure + */ +int wma_motion_det_host_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + wmi_motion_det_event *motion_det_event_hdr; + WMI_MOTION_DET_HOST_EVENTID_param_tlvs *param_buf = + (WMI_MOTION_DET_HOST_EVENTID_param_tlvs *)event; + struct sir_md_evt *md_event; + struct mac_context *pmac = (struct mac_context *)cds_get_context( + QDF_MODULE_ID_PE); + + if (!param_buf) { + WMA_LOGE("Invalid motion det host event buffer"); + return -EINVAL; + } + + if (!pmac || !pmac->sme.md_host_evt_cb) { + WMA_LOGE("Invalid motion detect callback"); + return -EINVAL; + } + + motion_det_event_hdr = param_buf->fixed_param; + WMA_LOGA("motion detect host event received, vdev_id=%d, status=%d", + motion_det_event_hdr->vdev_id, motion_det_event_hdr->status); + + md_event = qdf_mem_malloc(sizeof(*md_event)); + if (!md_event) + return -ENOMEM; + + md_event->vdev_id = motion_det_event_hdr->vdev_id; + md_event->status = motion_det_event_hdr->status; + + pmac->sme.md_host_evt_cb(pmac->sme.md_ctx, md_event); + + qdf_mem_free(md_event); + return 0; +} + +/** + * wma_motion_det_base_line_host_event_handler - md baselining event handler + * @handle: WMA global handle + * @event: motion detection baselining event + * @len: Length of cmd + * + * Return: 0 on success, else error on failure + */ +int wma_motion_det_base_line_host_event_handler(void *handle, + uint8_t *event, uint32_t len) +{ + wmi_motion_det_base_line_event *motion_det_base_line_event_hdr; + WMI_MOTION_DET_BASE_LINE_HOST_EVENTID_param_tlvs *param_buf = + (WMI_MOTION_DET_BASE_LINE_HOST_EVENTID_param_tlvs *)event; + struct sir_md_bl_evt *md_bl_event; + struct mac_context *pmac = (struct mac_context *)cds_get_context( + QDF_MODULE_ID_PE); + + if (!param_buf) { + WMA_LOGE("Invalid motion detection base line event buffer"); + return -EINVAL; + } + + if (!pmac || !pmac->sme.md_bl_evt_cb) { + WMA_LOGE("Invalid motion detection base line callback"); + return -EINVAL; + } + + motion_det_base_line_event_hdr = param_buf->fixed_param; + WMA_LOGA("motion detection base line event received, vdev_id=%d", + motion_det_base_line_event_hdr->vdev_id); + WMA_LOGA("baseline_value=%d bl_max_corr_resv=%d bl_min_corr_resv=%d", + motion_det_base_line_event_hdr->bl_baseline_value, + motion_det_base_line_event_hdr->bl_max_corr_reserved, + motion_det_base_line_event_hdr->bl_min_corr_reserved); + + md_bl_event = qdf_mem_malloc(sizeof(*md_bl_event)); + if (!md_bl_event) + return -ENOMEM; + + md_bl_event->vdev_id = motion_det_base_line_event_hdr->vdev_id; + md_bl_event->bl_baseline_value = + motion_det_base_line_event_hdr->bl_baseline_value; + md_bl_event->bl_max_corr_reserved = + motion_det_base_line_event_hdr->bl_max_corr_reserved; + md_bl_event->bl_min_corr_reserved = + motion_det_base_line_event_hdr->bl_min_corr_reserved; + + pmac->sme.md_bl_evt_cb(pmac->sme.md_ctx, md_bl_event); + + qdf_mem_free(md_bl_event); + return 0; +} + +/** + * wma_set_motion_det_config - Sends motion detection configuration wmi cmd + * @wma_handle: WMA global handle + * @motion_det_cfg: motion detection configuration + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_ERROR on error + */ +static QDF_STATUS wma_set_motion_det_config( + tp_wma_handle wma_handle, + struct sme_motion_det_cfg *motion_det_cfg) +{ + wmi_motion_det_config_params_cmd_fixed_param *cmd; + wmi_buf_t buf; + int err; + + buf = wmi_buf_alloc(wma_handle->wmi_handle, sizeof(*cmd)); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_motion_det_config_params_cmd_fixed_param *)wmi_buf_data(buf); + qdf_mem_zero(cmd, sizeof(*cmd)); + + WMITLV_SET_HDR( + &cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_motion_det_config_params_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_motion_det_config_params_cmd_fixed_param)); + cmd->vdev_id = motion_det_cfg->vdev_id; + cmd->time_t1 = motion_det_cfg->time_t1; + cmd->time_t2 = motion_det_cfg->time_t2; + cmd->n1 = motion_det_cfg->n1; + cmd->n2 = motion_det_cfg->n2; + cmd->time_t1_gap = motion_det_cfg->time_t1_gap; + cmd->time_t2_gap = motion_det_cfg->time_t2_gap; + cmd->coarse_K = motion_det_cfg->coarse_K; + cmd->fine_K = motion_det_cfg->fine_K; + cmd->coarse_Q = motion_det_cfg->coarse_Q; + cmd->fine_Q = motion_det_cfg->fine_Q; + cmd->md_coarse_thr_high = motion_det_cfg->md_coarse_thr_high; + cmd->md_fine_thr_high = motion_det_cfg->md_fine_thr_high; + cmd->md_coarse_thr_low = motion_det_cfg->md_coarse_thr_low; + cmd->md_fine_thr_low = motion_det_cfg->md_fine_thr_low; + + err = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, sizeof(*cmd), + WMI_MOTION_DET_CONFIG_PARAM_CMDID); + if (err) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + WMA_LOGA("Set motion_det_config to vdevId %d\n" + "time_t1 %d\n" + "time_t2 %d\n" + "n1 %d\n" + "n2 %d\n" + "time_t1_gap %d\n" + "time_t2_gap %d\n" + "coarse_K %d\n" + "fine_K %d\n" + "coarse_Q %d\n" + "fine_Q %d\n" + "md_coarse_thr_high %d\n" + "md_fine_thr_high %d\n" + "md_coarse_thr_low %d\n" + "md_fine_thr_low %d\n", + motion_det_cfg->vdev_id, + motion_det_cfg->time_t1, + motion_det_cfg->time_t2, + motion_det_cfg->n1, + motion_det_cfg->n2, + motion_det_cfg->time_t1_gap, + motion_det_cfg->time_t2_gap, + motion_det_cfg->coarse_K, + motion_det_cfg->fine_K, + motion_det_cfg->coarse_Q, + motion_det_cfg->fine_Q, + motion_det_cfg->md_coarse_thr_high, + motion_det_cfg->md_fine_thr_high, + motion_det_cfg->md_coarse_thr_low, + motion_det_cfg->md_fine_thr_low); + return QDF_STATUS_SUCCESS; +} + +/** + * wma_set_motion_det_enable - Sends motion detection start/stop wmi cmd + * @wma_handle: WMA global handle + * @md_en: motion detection start/stop + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_ERROR on error + */ +static QDF_STATUS wma_set_motion_det_enable(tp_wma_handle wma_handle, + struct sme_motion_det_en *md_en) +{ + wmi_motion_det_start_stop_cmd_fixed_param *cmd; + wmi_buf_t buf; + int err; + + buf = wmi_buf_alloc(wma_handle->wmi_handle, sizeof(*cmd)); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_motion_det_start_stop_cmd_fixed_param *)wmi_buf_data(buf); + qdf_mem_zero(cmd, sizeof(*cmd)); + + WMITLV_SET_HDR( + &cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_motion_det_start_stop_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_motion_det_start_stop_cmd_fixed_param)); + cmd->vdev_id = md_en->vdev_id; + cmd->enable = md_en->enable; + + err = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, sizeof(*cmd), + WMI_MOTION_DET_START_STOP_CMDID); + if (err) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + WMA_LOGA("Set motion_det_enable to vdevId %d %d", md_en->vdev_id, + md_en->enable); + return QDF_STATUS_SUCCESS; +} + +/** + * wma_set_motion_det_base_line_config - Sends md baselining cfg wmi cmd + * @wma_handle: WMA global handle + * @md_base_line_cfg: md baselining configuration + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_ERROR on error + */ +static QDF_STATUS wma_set_motion_det_base_line_config( + tp_wma_handle wma_handle, + struct sme_motion_det_base_line_cfg *md_base_line_cfg) +{ + wmi_motion_det_base_line_config_params_cmd_fixed_param *cmd; + wmi_buf_t buf; + int err; + + buf = wmi_buf_alloc(wma_handle->wmi_handle, sizeof(*cmd)); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_motion_det_base_line_config_params_cmd_fixed_param *) + wmi_buf_data(buf); + qdf_mem_zero(cmd, sizeof(*cmd)); + + WMITLV_SET_HDR( + &cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_motion_det_base_line_config_params_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_motion_det_base_line_config_params_cmd_fixed_param)); + + cmd->vdev_id = md_base_line_cfg->vdev_id; + cmd->bl_time_t = md_base_line_cfg->bl_time_t; + cmd->bl_packet_gap = md_base_line_cfg->bl_packet_gap; + cmd->bl_n = md_base_line_cfg->bl_n; + cmd->bl_num_meas = md_base_line_cfg->bl_num_meas; + + err = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, sizeof(*cmd), + WMI_MOTION_DET_BASE_LINE_CONFIG_PARAM_CMDID); + if (err) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + WMA_LOGA("Set motion_det_baseline_config to vdevId %d\n" + "bl_time_t %d\n" + "bl_packet_gap %d\n" + "bl_n %d\n" + "bl_num_meas %d\n", + md_base_line_cfg->vdev_id, + md_base_line_cfg->bl_time_t, + md_base_line_cfg->bl_packet_gap, + md_base_line_cfg->bl_n, + md_base_line_cfg->bl_num_meas); + return QDF_STATUS_SUCCESS; +} + +/** + * wma_set_motion_det_base_line_enable - Sends md baselining start/stop wmi cmd + * @wma_handle: WMA global handle + * @md_base_line_en: motion detection baselining start/stop + * + * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_ERROR on error + */ +static QDF_STATUS wma_set_motion_det_base_line_enable( + tp_wma_handle wma_handle, + struct sme_motion_det_base_line_en *md_base_line_en) +{ + wmi_motion_det_base_line_start_stop_cmd_fixed_param *cmd; + wmi_buf_t buf; + int err; + + buf = wmi_buf_alloc(wma_handle->wmi_handle, sizeof(*cmd)); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_motion_det_base_line_start_stop_cmd_fixed_param *) + wmi_buf_data(buf); + qdf_mem_zero(cmd, sizeof(*cmd)); + + WMITLV_SET_HDR( + &cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_motion_det_base_line_start_stop_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_motion_det_base_line_start_stop_cmd_fixed_param)); + + cmd->vdev_id = md_base_line_en->vdev_id; + cmd->enable = md_base_line_en->enable; + + err = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, sizeof(*cmd), + WMI_MOTION_DET_BASE_LINE_START_STOP_CMDID); + if (err) { + wmi_buf_free(buf); + return QDF_STATUS_E_FAILURE; + } + WMA_LOGA("Set motion_det_base_line_enable to vdevId %d enable %d", + md_base_line_en->vdev_id, md_base_line_en->enable); + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_MOTION_DETECTION */ + +/** + * wma_mc_process_msg() - process wma messages and call appropriate function. + * @msg: message + * + * Return: QDF_SUCCESS for success otherwise failure + */ +static QDF_STATUS wma_mc_process_msg(struct scheduler_msg *msg) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + tp_wma_handle wma_handle; + + if (!msg) { + WMA_LOGE("msg is NULL"); + QDF_ASSERT(0); + qdf_status = QDF_STATUS_E_INVAL; + goto end; + } + + WMA_LOGD("msg->type = %x %s", msg->type, + mac_trace_get_wma_msg_string(msg->type)); + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s: wma_handle is NULL", __func__); + QDF_ASSERT(0); + qdf_mem_free(msg->bodyptr); + qdf_status = QDF_STATUS_E_INVAL; + goto end; + } + + switch (msg->type) { +#ifdef FEATURE_WLAN_ESE + case WMA_TSM_STATS_REQ: + WMA_LOGD("McThread: WMA_TSM_STATS_REQ"); + wma_process_tsm_stats_req(wma_handle, (void *)msg->bodyptr); + break; +#endif /* FEATURE_WLAN_ESE */ + case WMA_UPDATE_CHAN_LIST_REQ: + wma_update_channel_list(wma_handle, + (tSirUpdateChanList *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_ADD_STA_REQ: + wma_add_sta(wma_handle, (tpAddStaParams) msg->bodyptr); + break; + case WMA_SEND_PEER_UNMAP_CONF: + wma_peer_unmap_conf_send( + wma_handle, + (struct send_peer_unmap_conf_params *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_DELETE_STA_REQ: + wma_delete_sta(wma_handle, (tpDeleteStaParams) msg->bodyptr); + break; + case WMA_DELETE_BSS_HO_FAIL_REQ: + wma_delete_bss_ho_fail(wma_handle, msg->bodyval); + break; + case WMA_DELETE_BSS_REQ: + wma_delete_bss(wma_handle, msg->bodyval); + break; + case WMA_UPDATE_EDCA_PROFILE_IND: + wma_process_update_edca_param_req(wma_handle, + (tEdcaParams *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SEND_BEACON_REQ: + wma_send_beacon(wma_handle, (tpSendbeaconParams) msg->bodyptr); + break; + case WMA_SEND_AP_VDEV_UP: + wma_set_ap_vdev_up(wma_handle, msg->bodyval); + break; + case WMA_SEND_PROBE_RSP_TMPL: + wma_send_probe_rsp_tmpl(wma_handle, + (tpSendProbeRespParams) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_CLI_SET_CMD: + wma_process_cli_set_cmd(wma_handle, + (wma_cli_set_cmd_t *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_PDEV_IE_REQ: + wma_process_set_pdev_ie_req(wma_handle, + (struct set_ie_param *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#if !defined(REMOVE_PKT_LOG) && defined(FEATURE_PKTLOG) + case WMA_PKTLOG_ENABLE_REQ: + wma_pktlog_wmi_send_cmd(wma_handle, + (struct ath_pktlog_wmi_params *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* REMOVE_PKT_LOG */ + case WMA_ENABLE_UAPSD_REQ: + wma_enable_uapsd_mode(wma_handle, + (tpEnableUapsdParams) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_DISABLE_UAPSD_REQ: + wma_disable_uapsd_mode(wma_handle, + (tpDisableUapsdParams) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_DTIM_PERIOD: + wma_set_dtim_period(wma_handle, + (struct set_dtim_params *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_TX_POWER_REQ: + wma_set_tx_power(wma_handle, (tpMaxTxPowerParams) msg->bodyptr); + break; + case WMA_SET_MAX_TX_POWER_REQ: + wma_set_max_tx_power(wma_handle, + (tpMaxTxPowerParams) msg->bodyptr); + break; + case WMA_SEND_MAX_TX_POWER: + wma_send_max_tx_pwrlmt(wma_handle, msg->bodyval); + break; + case WMA_SET_KEEP_ALIVE: + wma_set_keepalive_req(wma_handle, msg->bodyptr); + break; +#ifdef FEATURE_WLAN_ESE + case WMA_SET_PLM_REQ: + wma_config_plm(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif + + case WMA_UPDATE_OP_MODE: + wma_process_update_opmode(wma_handle, + (tUpdateVHTOpMode *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_UPDATE_RX_NSS: + wma_process_update_rx_nss(wma_handle, + (tUpdateRxNss *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_UPDATE_MEMBERSHIP: + wma_process_update_membership(wma_handle, + (tUpdateMembership *) msg->bodyptr); + break; + case WMA_UPDATE_USERPOS: + wma_process_update_userpos(wma_handle, + (tUpdateUserPos *) msg->bodyptr); + break; + case WMA_UPDATE_BEACON_IND: + wma_process_update_beacon_params(wma_handle, + (tUpdateBeaconParams *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + + case WMA_ADD_TS_REQ: + wma_add_ts_req(wma_handle, msg->bodyptr); + break; + + case WMA_DEL_TS_REQ: + wma_del_ts_req(wma_handle, msg->bodyptr); + break; + + case WMA_AGGR_QOS_REQ: + wma_aggr_qos_req(wma_handle, msg->bodyptr); + break; + + case WMA_8023_MULTICAST_LIST_REQ: + wma_process_mcbc_set_filter_req(wma_handle, + (tpSirRcvFltMcAddrList) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_ROAM_SCAN_OFFLOAD_REQ: + wma_process_roaming_config(wma_handle, msg->bodyptr); + break; + + case WMA_ROAM_PRE_AUTH_STATUS: + wma_send_roam_preauth_status(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + + case WMA_ROAM_SYNC_TIMEOUT: + wma_handle_roam_sync_timeout(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_RATE_UPDATE_IND: + wma_process_rate_update_indicate(wma_handle, + (tSirRateUpdateInd *) msg->bodyptr); + break; + +#ifdef FEATURE_WLAN_TDLS + case WMA_UPDATE_TDLS_PEER_STATE: + wma_update_tdls_peer_state(wma_handle, msg->bodyptr); + break; +#endif /* FEATURE_WLAN_TDLS */ + case WMA_ADD_PERIODIC_TX_PTRN_IND: + wma_process_add_periodic_tx_ptrn_ind(wma_handle, + (tSirAddPeriodicTxPtrn *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_DEL_PERIODIC_TX_PTRN_IND: + wma_process_del_periodic_tx_ptrn_ind(wma_handle, + (tSirDelPeriodicTxPtrn *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_TX_POWER_LIMIT: + wma_process_tx_power_limits(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SEND_ADDBA_REQ: + wma_process_send_addba_req(wma_handle, + (struct send_add_ba_req *)msg->bodyptr); + break; + +#ifdef FEATURE_WLAN_CH_AVOID + case WMA_CH_AVOID_UPDATE_REQ: + wma_process_ch_avoid_update_req(wma_handle, + (tSirChAvoidUpdateReq *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* FEATURE_WLAN_CH_AVOID */ +#ifdef FEATURE_WLAN_AUTO_SHUTDOWN + case WMA_SET_AUTO_SHUTDOWN_TIMER_REQ: + wma_set_auto_shutdown_timer_req(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* FEATURE_WLAN_AUTO_SHUTDOWN */ + case WMA_DHCP_START_IND: + case WMA_DHCP_STOP_IND: + wma_process_dhcp_ind(wma_handle, (tAniDHCPInd *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + + case WMA_IBSS_CESIUM_ENABLE_IND: + wma_process_cesium_enable_ind(wma_handle); + break; + case WMA_GET_IBSS_PEER_INFO_REQ: + wma_process_get_peer_info_req(wma_handle, + (tSirIbssGetPeerInfoReqParams *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_TX_FAIL_MONITOR_IND: + wma_process_tx_fail_monitor_ind(wma_handle, + (tAniTXFailMonitorInd *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + + case WMA_RMC_ENABLE_IND: + wma_process_rmc_enable_ind(wma_handle); + break; + case WMA_RMC_DISABLE_IND: + wma_process_rmc_disable_ind(wma_handle); + break; + case WMA_RMC_ACTION_PERIOD_IND: + wma_process_rmc_action_period_ind(wma_handle); + break; + case WMA_INIT_THERMAL_INFO_CMD: + wma_process_init_thermal_info(wma_handle, + (t_thermal_mgmt *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + + case WMA_SET_THERMAL_LEVEL: + wma_process_set_thermal_level(wma_handle, msg->bodyval); + break; +#ifdef CONFIG_HL_SUPPORT + case WMA_INIT_BAD_PEER_TX_CTL_INFO_CMD: + wma_process_init_bad_peer_tx_ctl_info( + wma_handle, + (struct t_bad_peer_txtcl_config *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif + case WMA_SET_MIMOPS_REQ: + wma_process_set_mimops_req(wma_handle, + (tSetMIMOPS *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_SAP_INTRABSS_DIS: + wma_set_vdev_intrabss_fwd(wma_handle, + (tDisableIntraBssFwd *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_GET_PEER_INFO_EXT: + wma_get_peer_info_ext(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_GET_ISOLATION: + wma_get_isolation(wma_handle); + break; + case WMA_MODEM_POWER_STATE_IND: + wma_notify_modem_power_state(wma_handle, + (tSirModemPowerStateInd *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#ifdef WLAN_FEATURE_STATS_EXT + case WMA_STATS_EXT_REQUEST: + wma_stats_ext_req(wma_handle, + (tpStatsExtRequest) (msg->bodyptr)); + qdf_mem_free(msg->bodyptr); + break; +#endif /* WLAN_FEATURE_STATS_EXT */ +#ifdef WLAN_FEATURE_EXTWOW_SUPPORT + case WMA_WLAN_EXT_WOW: + wma_enable_ext_wow(wma_handle, + (tSirExtWoWParams *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_WLAN_SET_APP_TYPE1_PARAMS: + wma_set_app_type1_params_in_fw(wma_handle, + (tSirAppType1Params *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_WLAN_SET_APP_TYPE2_PARAMS: + wma_set_app_type2_params_in_fw(wma_handle, + (tSirAppType2Params *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */ +#ifdef FEATURE_WLAN_EXTSCAN + case WMA_EXTSCAN_START_REQ: + wma_start_extscan(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_EXTSCAN_STOP_REQ: + wma_stop_extscan(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_EXTSCAN_SET_BSSID_HOTLIST_REQ: + wma_extscan_start_hotlist_monitor(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_EXTSCAN_RESET_BSSID_HOTLIST_REQ: + wma_extscan_stop_hotlist_monitor(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_EXTSCAN_SET_SIGNF_CHANGE_REQ: + wma_extscan_start_change_monitor(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_EXTSCAN_RESET_SIGNF_CHANGE_REQ: + wma_extscan_stop_change_monitor(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_EXTSCAN_GET_CACHED_RESULTS_REQ: + wma_extscan_get_cached_results(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_EXTSCAN_GET_CAPABILITIES_REQ: + wma_extscan_get_capabilities(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_EPNO_LIST_REQ: + wma_set_epno_network_list(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_PASSPOINT_LIST_REQ: + /* Issue reset passpoint network list first and clear + * the entries + */ + wma_reset_passpoint_network_list(wma_handle, msg->bodyptr); + + wma_set_passpoint_network_list(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_RESET_PASSPOINT_LIST_REQ: + wma_reset_passpoint_network_list(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* FEATURE_WLAN_EXTSCAN */ + case WMA_SET_PER_ROAM_CONFIG_CMD: + wma_update_per_roam_config(wma_handle, + (struct wmi_per_roam_config_req *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_SCAN_MAC_OUI_REQ: + wma_scan_probe_setoui(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + case WMA_LINK_LAYER_STATS_CLEAR_REQ: + wma_process_ll_stats_clear_req(wma_handle, + (tpSirLLStatsClearReq) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_LINK_LAYER_STATS_SET_REQ: + wma_process_ll_stats_set_req(wma_handle, + (tpSirLLStatsSetReq) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_LINK_LAYER_STATS_GET_REQ: + wma_process_ll_stats_get_req(wma_handle, + (tpSirLLStatsGetReq) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WDA_LINK_LAYER_STATS_SET_THRESHOLD: + wma_config_stats_ext_threshold(wma_handle, + (struct sir_ll_ext_stats_threshold *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + case WMA_ROAM_OFFLOAD_SYNCH_FAIL: + wma_process_roam_synch_fail(wma_handle, + (struct roam_offload_synch_fail *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_ROAM_INVOKE: + wma_process_roam_invoke(wma_handle, + (struct wma_roam_invoke_cmd *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + case SIR_HAL_SET_BASE_MACADDR_IND: + wma_set_base_macaddr_indicate(wma_handle, + (tSirMacAddr *) msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_LINK_STATUS_GET_REQ: + wma_process_link_status_req(wma_handle, + (tAniGetLinkStatus *) msg->bodyptr); + break; + case WMA_GET_TEMPERATURE_REQ: + wma_get_temperature(wma_handle); + qdf_mem_free(msg->bodyptr); + break; + case WMA_TSF_GPIO_PIN: + wma_set_tsf_gpio_pin(wma_handle, msg->bodyval); + break; + +#ifdef DHCP_SERVER_OFFLOAD + case WMA_SET_DHCP_SERVER_OFFLOAD_CMD: + wma_process_dhcpserver_offload(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* DHCP_SERVER_OFFLOAD */ +#ifdef WLAN_FEATURE_GPIO_LED_FLASHING + case WMA_LED_FLASHING_REQ: + wma_set_led_flashing(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* WLAN_FEATURE_GPIO_LED_FLASHING */ + case SIR_HAL_SET_MAS: + wma_process_set_mas(wma_handle, + (uint32_t *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_SET_MIRACAST: + wma_process_set_miracast(wma_handle, + (uint32_t *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_CONFIG_STATS_FACTOR: + wma_config_stats_factor(wma_handle, + (struct sir_stats_avg_factor *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_CONFIG_GUARD_TIME: + wma_config_guard_time(wma_handle, + (struct sir_guard_time_request *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_START_STOP_LOGGING: + wma_set_wifi_start_packet_stats(wma_handle, + (struct sir_wifi_start_log *)msg->bodyptr); + wma_enable_specific_fw_logs(wma_handle, + (struct sir_wifi_start_log *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_FLUSH_LOG_TO_FW: + wma_send_flush_logs_to_fw(wma_handle); + /* Body ptr is NULL here */ + break; + case WMA_SET_RSSI_MONITOR_REQ: + wma_set_rssi_monitoring(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_PDEV_SET_PCL_TO_FW: + wma_send_pdev_set_pcl_cmd(wma_handle, + (struct set_pcl_req *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_PDEV_SET_HW_MODE: + wma_send_pdev_set_hw_mode_cmd(wma_handle, + (struct policy_mgr_hw_mode *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_WISA_PARAMS: + wma_set_wisa_params(wma_handle, + (struct sir_wisa_params *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_PDEV_DUAL_MAC_CFG_REQ: + wma_send_pdev_set_dual_mac_config(wma_handle, + (struct policy_mgr_dual_mac_config *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_IE_INFO: + wma_process_set_ie_info(wma_handle, + (struct vdev_ie_info *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_CFG_VENDOR_ACTION_TB_PPDU: + wma_process_cfg_action_frm_tb_ppdu(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_SOC_ANTENNA_MODE_REQ: + wma_send_pdev_set_antenna_mode(wma_handle, + (struct sir_antenna_mode_param *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_LRO_CONFIG_CMD: + wma_lro_config_cmd(wma_handle, + (struct cdp_lro_hash_config *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_GW_PARAM_UPDATE_REQ: + wma_set_gateway_params(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_ADAPT_DWELLTIME_CONF_PARAMS: + wma_send_adapt_dwelltime_params(wma_handle, + (struct adaptive_dwelltime_params *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_HT40_OBSS_SCAN_IND: + wma_send_ht40_obss_scanind(wma_handle, + (struct obss_ht40_scanind *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_ADD_BCN_FILTER_CMDID: + wma_add_beacon_filter(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_REMOVE_BCN_FILTER_CMDID: + wma_remove_beacon_filter(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WDA_APF_GET_CAPABILITIES_REQ: + wma_get_apf_capabilities(wma_handle); + break; + case SIR_HAL_POWER_DBG_CMD: + wma_process_hal_pwr_dbg_cmd(wma_handle, + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_UPDATE_WEP_DEFAULT_KEY: + wma_update_wep_default_key(wma_handle, + (struct wep_update_default_key_idx *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SEND_FREQ_RANGE_CONTROL_IND: + wma_enable_disable_caevent_ind(wma_handle, msg->bodyval); + break; + case SIR_HAL_UPDATE_TX_FAIL_CNT_TH: + wma_update_tx_fail_cnt_th(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_LONG_RETRY_LIMIT_CNT: + wma_update_long_retry_limit(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_SHORT_RETRY_LIMIT_CNT: + wma_update_short_retry_limit(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_POWER_DEBUG_STATS_REQ: + wma_process_power_debug_stats_req(wma_handle); + break; + case WMA_BEACON_DEBUG_STATS_REQ: + wma_process_beacon_debug_stats_req(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_GET_RCPI_REQ: + wma_get_rcpi_req(wma_handle, + (struct sme_rcpi_req *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_WOW_PULSE_CMD: + wma_send_wow_pulse_cmd(wma_handle, + (struct wow_pulse_mode *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_DBS_SCAN_SEL_CONF_PARAMS: + wma_send_dbs_scan_selection_params(wma_handle, + (struct wmi_dbs_scan_sel_params *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_ARP_STATS_REQ: + wma_set_arp_req_stats(wma_handle, + (struct set_arp_stats_params *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_GET_ARP_STATS_REQ: + wma_get_arp_req_stats(wma_handle, + (struct get_arp_stats_params *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case SIR_HAL_SET_DEL_PMKID_CACHE: + wma_set_del_pmkid_cache(wma_handle, msg->bodyptr); + if (msg->bodyptr) { + qdf_mem_zero(msg->bodyptr, + sizeof(struct wmi_unified_pmk_cache)); + qdf_mem_free(msg->bodyptr); + } + break; + case SIR_HAL_HLP_IE_INFO: + wma_roam_scan_send_hlp(wma_handle, + (struct hlp_params *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_LIMIT_OFF_CHAN: + wma_process_limit_off_chan(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_OBSS_DETECTION_REQ: + wma_send_obss_detection_cfg(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_INVOKE_NEIGHBOR_REPORT: + wma_send_invoke_neighbor_report(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_OBSS_COLOR_COLLISION_REQ: + wma_process_obss_color_collision_req(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_GET_ROAM_SCAN_STATS: + wma_get_roam_scan_stats(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#ifdef WLAN_FEATURE_MOTION_DETECTION + case WMA_SET_MOTION_DET_CONFIG: + wma_set_motion_det_config( + wma_handle, + (struct sme_motion_det_cfg *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_MOTION_DET_ENABLE: + wma_set_motion_det_enable( + wma_handle, + (struct sme_motion_det_en *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_MOTION_DET_BASE_LINE_CONFIG: + wma_set_motion_det_base_line_config( + wma_handle, + (struct sme_motion_det_base_line_cfg *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_MOTION_DET_BASE_LINE_ENABLE: + wma_set_motion_det_base_line_enable( + wma_handle, + (struct sme_motion_det_base_line_en *)msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif /* WLAN_FEATURE_MOTION_DETECTION */ +#ifdef FW_THERMAL_THROTTLE_SUPPORT + case WMA_SET_THERMAL_THROTTLE_CFG: + if (!wma_handle->thermal_mgmt_info.thermalMgmtEnabled) + wmi_unified_thermal_mitigation_param_cmd_send( + wma_handle->wmi_handle, msg->bodyptr); + else + qdf_status = QDF_STATUS_E_INVAL; + qdf_mem_free(msg->bodyptr); + break; + case WMA_SET_THERMAL_MGMT: + if (!wma_handle->thermal_mgmt_info.thermalMgmtEnabled) + wma_set_thermal_mgmt( + wma_handle, + *((t_thermal_cmd_params *)msg->bodyptr)); + else + qdf_status = QDF_STATUS_E_INVAL; + qdf_mem_free(msg->bodyptr); + break; +#endif /* FW_THERMAL_THROTTLE_SUPPORT */ +#ifdef WLAN_MWS_INFO_DEBUGFS + case WMA_GET_MWS_COEX_INFO_REQ: + wma_get_mws_coex_info_req(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; +#endif + case WMA_SET_ROAM_TRIGGERS: + wma_set_roam_triggers(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_ROAM_INIT_PARAM: + wma_update_roam_offload_flag(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case WMA_ROAM_SCAN_CH_REQ: + wma_get_roam_scan_ch(wma_handle->wmi_handle, msg->bodyval); + break; + default: + WMA_LOGD("Unhandled WMA message of type %d", msg->type); + if (msg->bodyptr) + qdf_mem_free(msg->bodyptr); + } +end: + return qdf_status; +} + +QDF_STATUS wma_mc_process_handler(struct scheduler_msg *msg) +{ + return wma_mc_process_msg(msg); +} + +/** + * wma_log_completion_timeout() - Log completion timeout + * @data: Timeout handler data + * + * This function is called when log completion timer expires + * + * Return: None + */ +void wma_log_completion_timeout(void *data) +{ + tp_wma_handle wma_handle; + + WMA_LOGD("%s: Timeout occurred for log completion command", __func__); + + wma_handle = (tp_wma_handle) data; + if (!wma_handle) + WMA_LOGE("%s: Invalid WMA handle", __func__); + + /* Though we did not receive any event from FW, + * we can flush whatever logs we have with us + */ + cds_logging_set_fw_flush_complete(); +} + +/** + * wma_map_pcl_weights() - Map PCL weights + * @pcl_weight: Internal PCL weights + * + * Maps the internal weights of PCL to the weights needed by FW + * + * Return: Mapped channel weight of type wmi_pcl_chan_weight + */ +static wmi_pcl_chan_weight wma_map_pcl_weights(uint32_t pcl_weight) +{ + switch (pcl_weight) { + case WEIGHT_OF_GROUP1_PCL_CHANNELS: + return WMI_PCL_WEIGHT_VERY_HIGH; + case WEIGHT_OF_GROUP2_PCL_CHANNELS: + return WMI_PCL_WEIGHT_HIGH; + case WEIGHT_OF_GROUP3_PCL_CHANNELS: + return WMI_PCL_WEIGHT_MEDIUM; + case WEIGHT_OF_NON_PCL_CHANNELS: + return WMI_PCL_WEIGHT_LOW; + default: + return WMI_PCL_WEIGHT_DISALLOW; + } +} + +/** + * wma_send_pdev_set_pcl_cmd() - Send WMI_SOC_SET_PCL_CMDID to FW + * @wma_handle: WMA handle + * @msg: PCL structure containing the PCL and the number of channels + * + * WMI_PDEV_SET_PCL_CMDID provides a Preferred Channel List (PCL) to the WLAN + * firmware. The DBS Manager is the consumer of this information in the WLAN + * firmware. The channel list will be used when a Virtual DEVice (VDEV) needs + * to migrate to a new channel without host driver involvement. An example of + * this behavior is Legacy Fast Roaming (LFR 3.0). Generally, the host will + * manage the channel selection without firmware involvement. + * + * WMI_PDEV_SET_PCL_CMDID will carry only the weight list and not the actual + * channel list. The weights corresponds to the channels sent in + * WMI_SCAN_CHAN_LIST_CMDID. The channels from PCL would be having a higher + * weightage compared to the non PCL channels. + * + * Return: Success if the cmd is sent successfully to the firmware + */ +QDF_STATUS wma_send_pdev_set_pcl_cmd(tp_wma_handle wma_handle, + struct set_pcl_req *msg) +{ + uint32_t i; + QDF_STATUS status; + + if (!wma_handle) { + WMA_LOGE("%s: WMA handle is NULL. Cannot issue command", + __func__); + return QDF_STATUS_E_NULL_VALUE; + } + + for (i = 0; i < wma_handle->saved_chan.num_channels; i++) { + msg->chan_weights.saved_chan_list[i] = + wma_handle->saved_chan.ch_freq_list[i]; + } + + msg->chan_weights.saved_num_chan = wma_handle->saved_chan.num_channels; + status = policy_mgr_get_valid_chan_weights(wma_handle->psoc, + (struct policy_mgr_pcl_chan_weights *)&msg->chan_weights); + + for (i = 0; i < msg->chan_weights.saved_num_chan; i++) { + msg->chan_weights.weighed_valid_list[i] = + wma_map_pcl_weights( + msg->chan_weights.weighed_valid_list[i]); + /* Dont allow roaming on 2G when 5G_ONLY configured */ + if (((wma_handle->bandcapability == BAND_5G) || + (msg->band_mask == BIT(REG_BAND_5G))) && + (WLAN_REG_IS_24GHZ_CH_FREQ( + msg->chan_weights.saved_chan_list[i]))) { + msg->chan_weights.weighed_valid_list[i] = + WEIGHT_OF_DISALLOWED_CHANNELS; + } + if (msg->band_mask == BIT(REG_BAND_2G) && + !WLAN_REG_IS_24GHZ_CH_FREQ( + msg->chan_weights.saved_chan_list[i])) + msg->chan_weights.weighed_valid_list[i] = + WEIGHT_OF_DISALLOWED_CHANNELS; + } + + if (!QDF_IS_STATUS_SUCCESS(status)) { + WMA_LOGE("%s: Error in creating weighed pcl", __func__); + return status; + } + wma_debug("Dump channel list send to wmi"); + policy_mgr_dump_channel_list(msg->chan_weights.saved_num_chan, + msg->chan_weights.saved_chan_list, + msg->chan_weights.weighed_valid_list); + + if (wmi_unified_pdev_set_pcl_cmd(wma_handle->wmi_handle, + &msg->chan_weights)) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_send_pdev_set_hw_mode_cmd() - Send WMI_PDEV_SET_HW_MODE_CMDID to FW + * @wma_handle: WMA handle + * @msg: Structure containing the following parameters + * + * - hw_mode_index: The HW_Mode field is a enumerated type that is selected + * from the HW_Mode table, which is returned in the WMI_SERVICE_READY_EVENTID. + * + * Provides notification to the WLAN firmware that host driver is requesting a + * HardWare (HW) Mode change. This command is needed to support iHelium in the + * configurations that include the Dual Band Simultaneous (DBS) feature. + * + * Return: Success if the cmd is sent successfully to the firmware + */ +QDF_STATUS wma_send_pdev_set_hw_mode_cmd(tp_wma_handle wma_handle, + struct policy_mgr_hw_mode *msg) +{ + struct sir_set_hw_mode_resp *param; + struct wma_target_req *timeout_msg; + + if (!wma_handle) { + WMA_LOGE("%s: WMA handle is NULL. Cannot issue command", + __func__); + /* Handle is NULL. Will not be able to send failure + * response as well + */ + return QDF_STATUS_E_NULL_VALUE; + } + + if (!msg) { + WMA_LOGE("%s: Set HW mode param is NULL", __func__); + /* Lets try to free the active command list */ + goto fail; + } + + wma_acquire_wakelock(&wma_handle->wmi_cmd_rsp_wake_lock, + WMA_VDEV_HW_MODE_REQUEST_TIMEOUT); + if (wmi_unified_soc_set_hw_mode_cmd(wma_handle->wmi_handle, + msg->hw_mode_index)) { + wma_release_wakelock(&wma_handle->wmi_cmd_rsp_wake_lock); + goto fail; + } + timeout_msg = wma_fill_hold_req(wma_handle, 0, + SIR_HAL_PDEV_SET_HW_MODE, + WMA_PDEV_SET_HW_MODE_RESP, NULL, + WMA_VDEV_HW_MODE_REQUEST_TIMEOUT - 1); + if (!timeout_msg) { + WMA_LOGE("Failed to allocate request for SIR_HAL_PDEV_SET_HW_MODE"); + wma_remove_req(wma_handle, 0, WMA_PDEV_SET_HW_MODE_RESP); + } + + return QDF_STATUS_SUCCESS; +fail: + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return QDF_STATUS_E_NULL_VALUE; + + param->status = SET_HW_MODE_STATUS_ECANCELED; + param->cfgd_hw_mode_index = 0; + param->num_vdev_mac_entries = 0; + WMA_LOGE("%s: Sending HW mode fail response to LIM", __func__); + wma_send_msg(wma_handle, SIR_HAL_PDEV_SET_HW_MODE_RESP, + (void *) param, 0); + return QDF_STATUS_E_FAILURE; +} + +/** + * wma_send_pdev_set_dual_mac_config() - Set dual mac config to FW + * @wma_handle: WMA handle + * @msg: Dual MAC config parameters + * + * Configures WLAN firmware with the dual MAC features + * + * Return: QDF_STATUS. 0 on success. + */ +QDF_STATUS wma_send_pdev_set_dual_mac_config(tp_wma_handle wma_handle, + struct policy_mgr_dual_mac_config *msg) +{ + QDF_STATUS status; + struct wma_target_req *req_msg; + struct sir_dual_mac_config_resp *resp; + + if (!wma_handle) { + WMA_LOGE("%s: WMA handle is NULL. Cannot issue command", + __func__); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!msg) { + WMA_LOGE("%s: Set dual mode config is NULL", __func__); + return QDF_STATUS_E_NULL_VALUE; + } + + /* + * aquire the wake lock here and release it in response handler function + * In error condition, release the wake lock right away + */ + wma_acquire_wakelock(&wma_handle->wmi_cmd_rsp_wake_lock, + WMA_VDEV_PLCY_MGR_WAKE_LOCK_TIMEOUT); + status = wmi_unified_pdev_set_dual_mac_config_cmd( + wma_handle->wmi_handle, + (struct policy_mgr_dual_mac_config *)msg); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to send WMI_PDEV_SET_DUAL_MAC_CONFIG_CMDID: %d", + __func__, status); + wma_release_wakelock(&wma_handle->wmi_cmd_rsp_wake_lock); + goto fail; + } + policy_mgr_update_dbs_req_config(wma_handle->psoc, + msg->scan_config, msg->fw_mode_config); + + req_msg = wma_fill_hold_req(wma_handle, 0, + SIR_HAL_PDEV_DUAL_MAC_CFG_REQ, + WMA_PDEV_MAC_CFG_RESP, NULL, + WMA_VDEV_DUAL_MAC_CFG_TIMEOUT); + if (!req_msg) { + WMA_LOGE("Failed to allocate request for SIR_HAL_PDEV_DUAL_MAC_CFG_REQ"); + wma_remove_req(wma_handle, 0, WMA_PDEV_MAC_CFG_RESP); + } + + return QDF_STATUS_SUCCESS; + +fail: + resp = qdf_mem_malloc(sizeof(*resp)); + if (!resp) + return QDF_STATUS_E_NULL_VALUE; + + resp->status = SET_HW_MODE_STATUS_ECANCELED; + WMA_LOGE("%s: Sending failure response to LIM", __func__); + wma_send_msg(wma_handle, SIR_HAL_PDEV_MAC_CFG_RESP, (void *) resp, 0); + return QDF_STATUS_E_FAILURE; + +} + +/** + * wma_send_pdev_set_antenna_mode() - Set antenna mode to FW + * @wma_handle: WMA handle + * @msg: Antenna mode parameters + * + * Send WMI_PDEV_SET_ANTENNA_MODE_CMDID to FW requesting to + * modify the number of TX/RX chains from host + * + * Return: QDF_STATUS. 0 on success. + */ +QDF_STATUS wma_send_pdev_set_antenna_mode(tp_wma_handle wma_handle, + struct sir_antenna_mode_param *msg) +{ + wmi_pdev_set_antenna_mode_cmd_fixed_param *cmd; + wmi_buf_t buf; + uint32_t len; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct sir_antenna_mode_resp *param; + + if (!wma_handle) { + WMA_LOGE("%s: WMA handle is NULL. Cannot issue command", + __func__); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!msg) { + WMA_LOGE("%s: Set antenna mode param is NULL", __func__); + return QDF_STATUS_E_NULL_VALUE; + } + + len = sizeof(*cmd); + + buf = wmi_buf_alloc(wma_handle->wmi_handle, len); + if (!buf) { + status = QDF_STATUS_E_NOMEM; + goto resp; + } + + cmd = (wmi_pdev_set_antenna_mode_cmd_fixed_param *) wmi_buf_data(buf); + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_pdev_set_antenna_mode_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_pdev_set_antenna_mode_cmd_fixed_param)); + + cmd->pdev_id = OL_TXRX_PDEV_ID; + /* Bits 0-15 is num of RX chains 16-31 is num of TX chains */ + cmd->num_txrx_chains = msg->num_rx_chains; + cmd->num_txrx_chains |= (msg->num_tx_chains << 16); + + WMA_LOGD("%s: Num of chains TX: %d RX: %d txrx_chains: 0x%x", + __func__, msg->num_tx_chains, + msg->num_rx_chains, cmd->num_txrx_chains); + + if (wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, + WMI_PDEV_SET_ANTENNA_MODE_CMDID)) { + wmi_buf_free(buf); + status = QDF_STATUS_E_FAILURE; + goto resp; + } + status = QDF_STATUS_SUCCESS; + +resp: + param = qdf_mem_malloc(sizeof(*param)); + if (!param) + return QDF_STATUS_E_NOMEM; + + param->status = (status) ? + SET_ANTENNA_MODE_STATUS_ECANCELED : + SET_ANTENNA_MODE_STATUS_OK; + WMA_LOGE("%s: Send antenna mode resp to LIM status: %d", + __func__, param->status); + wma_send_msg(wma_handle, SIR_HAL_SOC_ANTENNA_MODE_RESP, + (void *) param, 0); + return status; +} + +/** + * wma_crash_inject() - sends command to FW to simulate crash + * @wma_handle: pointer of WMA context + * @type: subtype of the command + * @delay_time_ms: time in milliseconds for FW to delay the crash + * + * This function will send a command to FW in order to simulate different + * kinds of FW crashes. + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_crash_inject(WMA_HANDLE wma_handle, uint32_t type, + uint32_t delay_time_ms) +{ + struct crash_inject param; + tp_wma_handle wma = (tp_wma_handle)wma_handle; + + param.type = type; + param.delay_time_ms = delay_time_ms; + return wmi_crash_inject(wma->wmi_handle, ¶m); +} + +#ifdef RECEIVE_OFFLOAD +int wma_lro_init(struct cdp_lro_hash_config *lro_config) +{ + struct scheduler_msg msg = {0}; + struct cdp_lro_hash_config *iwcmd; + + iwcmd = qdf_mem_malloc(sizeof(*iwcmd)); + if (!iwcmd) + return -ENOMEM; + + *iwcmd = *lro_config; + + msg.type = WMA_LRO_CONFIG_CMD; + msg.reserved = 0; + msg.bodyptr = iwcmd; + + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg)) { + qdf_mem_free(iwcmd); + return -EAGAIN; + } + + WMA_LOGD("sending the LRO configuration to the fw"); + return 0; +} +#endif + +QDF_STATUS wma_configure_smps_params(uint32_t vdev_id, uint32_t param_id, + uint32_t param_val) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + int smps_cmd_value; + int status = QDF_STATUS_E_INVAL; + + if (!wma) { + WMA_LOGE("%s: Failed to get wma", __func__); + return status; + } + + smps_cmd_value = param_id << WMI_SMPS_PARAM_VALUE_S; + smps_cmd_value = smps_cmd_value | param_val; + + status = wma_set_smps_params(wma, vdev_id, smps_cmd_value); + if (status) + WMA_LOGE("Failed to set SMPS Param"); + + return status; +} + + +/** + * wma_config_bmiss_bcnt_params() - set bmiss config parameters + * @vdev_id: virtual device for the command + * @first_cnt: bmiss first value + * @final_cnt: bmiss final value + * + * Return: QDF_STATUS_SUCCESS or non-zero on failure + */ +QDF_STATUS wma_config_bmiss_bcnt_params(uint32_t vdev_id, uint32_t first_cnt, + uint32_t final_cnt) +{ + tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + int status = QDF_STATUS_E_INVAL; + + if (!wma_handle) { + WMA_LOGE("%s: Failed to get wma", __func__); + return status; + } + + status = wma_roam_scan_bmiss_cnt(wma_handle, first_cnt, final_cnt, + vdev_id); + + if (status) + WMA_LOGE("Failed to set Bmiss Param"); + + return status; +} + +QDF_STATUS wma_get_rx_chainmask(uint8_t pdev_id, uint32_t *chainmask_2g, + uint32_t *chainmask_5g) +{ + struct wlan_psoc_host_mac_phy_caps *mac_phy_cap; + uint8_t total_mac_phy_cnt, idx; + struct target_psoc_info *tgt_hdl; + uint32_t hw_mode_idx = 0, num_hw_modes = 0; + + tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + wma_err("wma_handle is NULL"); + return QDF_STATUS_E_INVAL; + } + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); + if (!tgt_hdl) { + WMA_LOGE("%s: target psoc info is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + total_mac_phy_cnt = target_psoc_get_total_mac_phy_cnt(tgt_hdl); + num_hw_modes = target_psoc_get_num_hw_modes(tgt_hdl); + if (total_mac_phy_cnt <= pdev_id) { + WMA_LOGE("%s: mac phy cnt %d, pdev id %d", __func__, + total_mac_phy_cnt, pdev_id); + return QDF_STATUS_E_FAILURE; + } + + if ((wma_handle->new_hw_mode_index != WMA_DEFAULT_HW_MODE_INDEX) && + (num_hw_modes > 0) && + (wma_handle->new_hw_mode_index < num_hw_modes)) + hw_mode_idx = wma_handle->new_hw_mode_index; + mac_phy_cap = target_psoc_get_mac_phy_cap(tgt_hdl); + if (!mac_phy_cap) { + WMA_LOGE("Invalid MAC PHY capabilities handle"); + return QDF_STATUS_E_FAILURE; + } + for (idx = 0; idx < total_mac_phy_cnt; idx++) { + if (mac_phy_cap[idx].hw_mode_id != hw_mode_idx) + continue; + if (mac_phy_cap[idx].supported_bands & WLAN_2G_CAPABILITY) + *chainmask_2g = mac_phy_cap[idx].rx_chain_mask_2G; + if (mac_phy_cap[idx].supported_bands & WLAN_5G_CAPABILITY) + *chainmask_5g = mac_phy_cap[idx].rx_chain_mask_5G; + } + WMA_LOGD("%s, pdev id: %d, hw_mode_idx: %d, rx chainmask 2g:%d, 5g:%d", + __func__, pdev_id, hw_mode_idx, *chainmask_2g, *chainmask_5g); + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_ANI_LEVEL_REQUEST +QDF_STATUS wma_send_ani_level_request(tp_wma_handle wma_handle, + uint32_t *freqs, uint8_t num_freqs) +{ + return wmi_unified_ani_level_cmd_send(wma_handle->wmi_handle, freqs, + num_freqs); +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_mgmt.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_mgmt.c new file mode 100644 index 0000000000000000000000000000000000000000..6d6f38518afcb820c578d256dce3827a13522965 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_mgmt.c @@ -0,0 +1,3919 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_mgmt.c + * + * This file contains STA/SAP/IBSS and protocol related functions. + */ + +/* Header files */ + +#include "wma.h" +#include "wma_api.h" +#include "cds_api.h" +#include "wmi_unified_api.h" +#include "wlan_qct_sys.h" +#include "wni_api.h" +#include "ani_global.h" +#include "wmi_unified.h" +#include "wni_cfg.h" + +#include "qdf_nbuf.h" +#include "qdf_types.h" +#include "qdf_mem.h" + +#include "wma_types.h" +#include "lim_api.h" +#include "lim_session_utils.h" + +#include "cds_utils.h" +#include "wlan_blm_api.h" +#if !defined(REMOVE_PKT_LOG) +#include "pktlog_ac.h" +#else +#include "pktlog_ac_fmt.h" +#endif /* REMOVE_PKT_LOG */ + +#include "dbglog_host.h" +#include "csr_api.h" +#include "ol_fw.h" +#include "wma_internal.h" +#include "wlan_policy_mgr_api.h" +#include "cdp_txrx_flow_ctrl_legacy.h" +#include +#include +#include +#include +#include +#include +#include "wlan_mgmt_txrx_tgt_api.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_lmac_if_api.h" +#include +#include "wma_he.h" +#include +#include "wma_twt.h" +#include "wlan_p2p_cfg_api.h" +#include "cfg_ucfg_api.h" +#include "cfg_mlme_sta.h" +#include "wlan_mlme_api.h" +#include "wmi_unified_bcn_api.h" +#include +#include +#include <../../core/src/vdev_mgr_ops.h> + +#if !defined(REMOVE_PKT_LOG) +#include +#endif + +/** + * wma_send_bcn_buf_ll() - prepare and send beacon buffer to fw for LL + * @wma: wma handle + * @vdev_id: vdev id + * @param_buf: SWBA parameters + * + * Return: none + */ +#ifdef WLAN_WMI_BCN +static void wma_send_bcn_buf_ll(tp_wma_handle wma, + uint8_t vdev_id, + WMI_HOST_SWBA_EVENTID_param_tlvs *param_buf) +{ + struct ieee80211_frame *wh; + struct beacon_info *bcn; + wmi_tim_info *tim_info = param_buf->tim_info; + uint8_t *bcn_payload; + QDF_STATUS ret; + struct beacon_tim_ie *tim_ie; + wmi_p2p_noa_info *p2p_noa_info = param_buf->p2p_noa_info; + struct p2p_sub_element_noa noa_ie; + struct wmi_bcn_send_from_host params; + uint8_t i; + + bcn = wma->interfaces[vdev_id].beacon; + if (!bcn || !bcn->buf) { + WMA_LOGE("%s: Invalid beacon buffer", __func__); + return; + } + + if (!param_buf->tim_info || !param_buf->p2p_noa_info) { + WMA_LOGE("%s: Invalid tim info or p2p noa info", __func__); + return; + } + + if (WMI_UNIFIED_NOA_ATTR_NUM_DESC_GET(p2p_noa_info) > + WMI_P2P_MAX_NOA_DESCRIPTORS) { + WMA_LOGE("%s: Too many descriptors %d", __func__, + WMI_UNIFIED_NOA_ATTR_NUM_DESC_GET(p2p_noa_info)); + return; + } + + qdf_spin_lock_bh(&bcn->lock); + + bcn_payload = qdf_nbuf_data(bcn->buf); + + tim_ie = (struct beacon_tim_ie *)(&bcn_payload[bcn->tim_ie_offset]); + + if (tim_info->tim_changed) { + if (tim_info->tim_num_ps_pending) + qdf_mem_copy(&tim_ie->tim_bitmap, tim_info->tim_bitmap, + WMA_TIM_SUPPORTED_PVB_LENGTH); + else + qdf_mem_zero(&tim_ie->tim_bitmap, + WMA_TIM_SUPPORTED_PVB_LENGTH); + /* + * Currently we support fixed number of + * peers as limited by HAL_NUM_STA. + * tim offset is always 0 + */ + tim_ie->tim_bitctl = 0; + } + + /* Update DTIM Count */ + if (tim_ie->dtim_count == 0) + tim_ie->dtim_count = tim_ie->dtim_period - 1; + else + tim_ie->dtim_count--; + + /* + * DTIM count needs to be backedup so that + * when umac updates the beacon template + * current dtim count can be updated properly + */ + bcn->dtim_count = tim_ie->dtim_count; + + /* update state for buffered multicast frames on DTIM */ + if (tim_info->tim_mcast && (tim_ie->dtim_count == 0 || + tim_ie->dtim_period == 1)) + tim_ie->tim_bitctl |= 1; + else + tim_ie->tim_bitctl &= ~1; + + /* To avoid sw generated frame sequence the same as H/W generated frame, + * the value lower than min_sw_seq is reserved for HW generated frame + */ + if ((bcn->seq_no & IEEE80211_SEQ_MASK) < MIN_SW_SEQ) + bcn->seq_no = MIN_SW_SEQ; + + wh = (struct ieee80211_frame *)bcn_payload; + *(uint16_t *) &wh->i_seq[0] = htole16(bcn->seq_no + << IEEE80211_SEQ_SEQ_SHIFT); + bcn->seq_no++; + + if (WMI_UNIFIED_NOA_ATTR_IS_MODIFIED(p2p_noa_info)) { + qdf_mem_zero(&noa_ie, sizeof(noa_ie)); + + noa_ie.index = + (uint8_t) WMI_UNIFIED_NOA_ATTR_INDEX_GET(p2p_noa_info); + noa_ie.oppPS = + (uint8_t) WMI_UNIFIED_NOA_ATTR_OPP_PS_GET(p2p_noa_info); + noa_ie.ctwindow = + (uint8_t) WMI_UNIFIED_NOA_ATTR_CTWIN_GET(p2p_noa_info); + noa_ie.num_descriptors = (uint8_t) + WMI_UNIFIED_NOA_ATTR_NUM_DESC_GET(p2p_noa_info); + WMA_LOGI("%s: index %u, oppPs %u, ctwindow %u, num_descriptors = %u", + __func__, noa_ie.index, + noa_ie.oppPS, noa_ie.ctwindow, noa_ie.num_descriptors); + for (i = 0; i < noa_ie.num_descriptors; i++) { + noa_ie.noa_descriptors[i].type_count = + (uint8_t) p2p_noa_info->noa_descriptors[i]. + type_count; + noa_ie.noa_descriptors[i].duration = + p2p_noa_info->noa_descriptors[i].duration; + noa_ie.noa_descriptors[i].interval = + p2p_noa_info->noa_descriptors[i].interval; + noa_ie.noa_descriptors[i].start_time = + p2p_noa_info->noa_descriptors[i].start_time; + WMA_LOGI("%s: NoA descriptor[%d] type_count %u, duration %u, interval %u, start_time = %u", + __func__, i, + noa_ie.noa_descriptors[i].type_count, + noa_ie.noa_descriptors[i].duration, + noa_ie.noa_descriptors[i].interval, + noa_ie.noa_descriptors[i].start_time); + } + wma_update_noa(bcn, &noa_ie); + + /* Send a msg to LIM to update the NoA IE in probe response + * frames transmitted by the host + */ + wma_update_probe_resp_noa(wma, &noa_ie); + } + + if (bcn->dma_mapped) { + qdf_nbuf_unmap_single(wma->qdf_dev, bcn->buf, QDF_DMA_TO_DEVICE); + bcn->dma_mapped = 0; + } + ret = qdf_nbuf_map_single(wma->qdf_dev, bcn->buf, QDF_DMA_TO_DEVICE); + if (ret != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: failed map beacon buf to DMA region", __func__); + qdf_spin_unlock_bh(&bcn->lock); + return; + } + + bcn->dma_mapped = 1; + params.vdev_id = vdev_id; + params.data_len = bcn->len; + params.frame_ctrl = *((A_UINT16 *) wh->i_fc); + params.frag_ptr = qdf_nbuf_get_frag_paddr(bcn->buf, 0); + params.dtim_flag = 0; + /* notify Firmware of DTM and mcast/bcast traffic */ + if (tim_ie->dtim_count == 0) { + params.dtim_flag |= WMI_BCN_SEND_DTIM_ZERO; + /* deliver mcast/bcast traffic in next DTIM beacon */ + if (tim_ie->tim_bitctl & 0x01) + params.dtim_flag |= WMI_BCN_SEND_DTIM_BITCTL_SET; + } + + wmi_unified_bcn_buf_ll_cmd(wma->wmi_handle, + ¶ms); + + qdf_spin_unlock_bh(&bcn->lock); +} +#else +static inline void +wma_send_bcn_buf_ll(tp_wma_handle wma, + uint8_t vdev_id, + WMI_HOST_SWBA_EVENTID_param_tlvs *param_buf) +{ +} +#endif +/** + * wma_beacon_swba_handler() - swba event handler + * @handle: wma handle + * @event: event data + * @len: data length + * + * SWBA event is alert event to Host requesting host to Queue a beacon + * for transmission use only in host beacon mode + * + * Return: 0 for success or error code + */ +#ifdef WLAN_WMI_BCN +int wma_beacon_swba_handler(void *handle, uint8_t *event, uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_HOST_SWBA_EVENTID_param_tlvs *param_buf; + wmi_host_swba_event_fixed_param *swba_event; + uint32_t vdev_map; + uint8_t vdev_id = 0; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + param_buf = (WMI_HOST_SWBA_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("Invalid swba event buffer"); + return -EINVAL; + } + swba_event = param_buf->fixed_param; + vdev_map = swba_event->vdev_map; + + wma_debug("vdev_map = %d", vdev_map); + for (; vdev_map && vdev_id < wma->max_bssid; + vdev_id++, vdev_map >>= 1) { + if (!(vdev_map & 0x1)) + continue; + if (!cdp_cfg_is_high_latency(soc, + (struct cdp_cfg *)cds_get_context(QDF_MODULE_ID_CFG))) + wma_send_bcn_buf_ll(wma, vdev_id, param_buf); + break; + } + return 0; +} +#else +static inline int +wma_beacon_swba_handler(void *handle, uint8_t *event, uint32_t len) +{ + return 0; +} +#endif + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +void wma_sta_kickout_event(uint32_t kickout_reason, uint8_t vdev_id, + uint8_t *macaddr) +{ + WLAN_HOST_DIAG_EVENT_DEF(sta_kickout, struct host_event_wlan_kickout); + qdf_mem_zero(&sta_kickout, sizeof(sta_kickout)); + sta_kickout.reasoncode = kickout_reason; + sta_kickout.vdev_id = vdev_id; + if (macaddr) + qdf_mem_copy(sta_kickout.peer_mac, macaddr, + QDF_MAC_ADDR_SIZE); + WLAN_HOST_DIAG_EVENT_REPORT(&sta_kickout, EVENT_WLAN_STA_KICKOUT); +} +#endif + +int wma_peer_sta_kickout_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_PEER_STA_KICKOUT_EVENTID_param_tlvs *param_buf = NULL; + wmi_peer_sta_kickout_event_fixed_param *kickout_event = NULL; + uint8_t vdev_id, macaddr[QDF_MAC_ADDR_SIZE]; + tpDeleteStaContext del_sta_ctx; + struct ibss_peer_inactivity_ind *inactivity; + uint8_t *addr, *bssid; + struct wlan_objmgr_vdev *vdev; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + param_buf = (WMI_PEER_STA_KICKOUT_EVENTID_param_tlvs *) event; + kickout_event = param_buf->fixed_param; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&kickout_event->peer_macaddr, macaddr); + if (cdp_peer_get_vdevid(soc, macaddr, &vdev_id) != + QDF_STATUS_SUCCESS) { + WMA_LOGE("Not able to find BSSID for peer ["QDF_MAC_ADDR_FMT"]", + QDF_MAC_ADDR_REF(macaddr)); + return -EINVAL; + } + vdev = wma->interfaces[vdev_id].vdev; + if (!vdev) { + WMA_LOGE("Not able to find vdev for VDEV_%d", vdev_id); + return -EINVAL; + } + addr = wlan_vdev_mlme_get_macaddr(vdev); + + wma_nofl_info("STA kickout for "QDF_MAC_ADDR_FMT", on mac "QDF_MAC_ADDR_FMT", vdev %d, reason:%d", + QDF_MAC_ADDR_REF(macaddr), QDF_MAC_ADDR_REF(addr), + vdev_id, kickout_event->reason); + + if (wma->interfaces[vdev_id].roaming_in_progress) { + WMA_LOGE("Ignore STA kick out since roaming is in progress"); + return -EINVAL; + } + bssid = wma_get_vdev_bssid(wma->interfaces[vdev_id].vdev); + if (!bssid) { + WMA_LOGE("%s: Failed to get bssid for vdev_%d", + __func__, vdev_id); + return -ENOMEM; + } + + switch (kickout_event->reason) { + case WMI_PEER_STA_KICKOUT_REASON_IBSS_DISCONNECT: + inactivity = qdf_mem_malloc(sizeof(*inactivity)); + if (!inactivity) { + WMA_LOGE("QDF MEM Alloc Failed for tSirIbssPeerInactivity"); + return -ENOMEM; + } + qdf_mem_copy(inactivity->peer_addr.bytes, macaddr, + QDF_MAC_ADDR_SIZE); + wma_send_msg(wma, WMA_IBSS_PEER_INACTIVITY_IND, + inactivity, 0); + goto exit_handler; +#ifdef FEATURE_WLAN_TDLS + case WMI_PEER_STA_KICKOUT_REASON_TDLS_DISCONNECT: + del_sta_ctx = (tpDeleteStaContext) + qdf_mem_malloc(sizeof(tDeleteStaContext)); + if (!del_sta_ctx) { + WMA_LOGE("%s: mem alloc failed for struct del_sta_context for TDLS peer: "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(macaddr)); + return -ENOMEM; + } + + del_sta_ctx->is_tdls = true; + del_sta_ctx->vdev_id = vdev_id; + qdf_mem_copy(del_sta_ctx->addr2, macaddr, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(del_sta_ctx->bssId, bssid, + QDF_MAC_ADDR_SIZE); + del_sta_ctx->reasonCode = HAL_DEL_STA_REASON_CODE_KEEP_ALIVE; + wma_send_msg(wma, SIR_LIM_DELETE_STA_CONTEXT_IND, + (void *)del_sta_ctx, 0); + goto exit_handler; +#endif /* FEATURE_WLAN_TDLS */ + + case WMI_PEER_STA_KICKOUT_REASON_UNSPECIFIED: + /* + * Default legacy value used by original firmware implementation + */ + if (wma->interfaces[vdev_id].type == WMI_VDEV_TYPE_STA && + (wma->interfaces[vdev_id].sub_type == 0 || + wma->interfaces[vdev_id].sub_type == + WMI_UNIFIED_VDEV_SUBTYPE_P2P_CLIENT) && + !qdf_mem_cmp(bssid, + macaddr, QDF_MAC_ADDR_SIZE)) { + wma_sta_kickout_event( + HOST_STA_KICKOUT_REASON_UNSPECIFIED, vdev_id, macaddr); + /* + * KICKOUT event is for current station-AP connection. + * Treat it like final beacon miss. Station may not have + * missed beacons but not able to transmit frames to AP + * for a long time. Must disconnect to get out of + * this sticky situation. + * In future implementation, roaming module will also + * handle this event and perform a scan. + */ + WMA_LOGW("%s: WMI_PEER_STA_KICKOUT_REASON_UNSPECIFIED event for STA", + __func__); + wma_beacon_miss_handler(wma, vdev_id, + kickout_event->rssi); + goto exit_handler; + } + break; + + case WMI_PEER_STA_KICKOUT_REASON_XRETRY: + case WMI_PEER_STA_KICKOUT_REASON_INACTIVITY: + /* + * Handle SA query kickout is same as inactivity kickout. + * This could be for STA or SAP role + */ + case WMI_PEER_STA_KICKOUT_REASON_SA_QUERY_TIMEOUT: + default: + break; + } + + /* + * default action is to send delete station context indication to LIM + */ + del_sta_ctx = + (tDeleteStaContext *) qdf_mem_malloc(sizeof(tDeleteStaContext)); + if (!del_sta_ctx) { + WMA_LOGE("QDF MEM Alloc Failed for struct del_sta_context"); + return -ENOMEM; + } + + del_sta_ctx->is_tdls = false; + del_sta_ctx->vdev_id = vdev_id; + qdf_mem_copy(del_sta_ctx->addr2, macaddr, QDF_MAC_ADDR_SIZE); + qdf_mem_copy(del_sta_ctx->bssId, addr, QDF_MAC_ADDR_SIZE); + if (kickout_event->reason == + WMI_PEER_STA_KICKOUT_REASON_SA_QUERY_TIMEOUT) + del_sta_ctx->reasonCode = + HAL_DEL_STA_REASON_CODE_SA_QUERY_TIMEOUT; + else if (kickout_event->reason == WMI_PEER_STA_KICKOUT_REASON_XRETRY) + del_sta_ctx->reasonCode = HAL_DEL_STA_REASON_CODE_XRETRY; + else + del_sta_ctx->reasonCode = HAL_DEL_STA_REASON_CODE_KEEP_ALIVE; + + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_hw_db2dbm_support)) + del_sta_ctx->rssi = kickout_event->rssi; + else + del_sta_ctx->rssi = kickout_event->rssi + + WMA_TGT_NOISE_FLOOR_DBM; + wma_sta_kickout_event(del_sta_ctx->reasonCode, vdev_id, macaddr); + wma_send_msg(wma, SIR_LIM_DELETE_STA_CONTEXT_IND, (void *)del_sta_ctx, + 0); + wma_lost_link_info_handler(wma, vdev_id, del_sta_ctx->rssi); + +exit_handler: + return 0; +} + +int wma_unified_bcntx_status_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_OFFLOAD_BCN_TX_STATUS_EVENTID_param_tlvs *param_buf; + wmi_offload_bcn_tx_status_event_fixed_param *resp_event; + tSirFirstBeaconTxCompleteInd *beacon_tx_complete_ind; + + param_buf = + (WMI_OFFLOAD_BCN_TX_STATUS_EVENTID_param_tlvs *) cmd_param_info; + if (!param_buf) { + WMA_LOGE("Invalid bcn tx response event buffer"); + return -EINVAL; + } + + resp_event = param_buf->fixed_param; + + if (resp_event->vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: received invalid vdev_id %d", + __func__, resp_event->vdev_id); + return -EINVAL; + } + + /* Check for valid handle to ensure session is not + * deleted in any race + */ + if (!wma->interfaces[resp_event->vdev_id].vdev) { + WMA_LOGE("%s: vdev is NULL for vdev_%d", + __func__, resp_event->vdev_id); + return -EINVAL; + } + + /* Beacon Tx Indication supports only AP mode. Ignore in other modes */ + if (wma_is_vdev_in_ap_mode(wma, resp_event->vdev_id) == false) { + WMA_LOGI("%s: Beacon Tx Indication does not support type %d and sub_type %d", + __func__, wma->interfaces[resp_event->vdev_id].type, + wma->interfaces[resp_event->vdev_id].sub_type); + return 0; + } + + beacon_tx_complete_ind = (tSirFirstBeaconTxCompleteInd *) + qdf_mem_malloc(sizeof(tSirFirstBeaconTxCompleteInd)); + if (!beacon_tx_complete_ind) { + WMA_LOGE("%s: Failed to alloc beacon_tx_complete_ind", + __func__); + return -ENOMEM; + } + + beacon_tx_complete_ind->messageType = WMA_DFS_BEACON_TX_SUCCESS_IND; + beacon_tx_complete_ind->length = sizeof(tSirFirstBeaconTxCompleteInd); + beacon_tx_complete_ind->bss_idx = resp_event->vdev_id; + + wma_send_msg(wma, WMA_DFS_BEACON_TX_SUCCESS_IND, + (void *)beacon_tx_complete_ind, 0); + return 0; +} + +/** + * wma_get_go_probe_timeout() - get P2P GO probe timeout + * @mac: UMAC handler + * @max_inactive_time: return max inactive time + * @max_unresponsive_time: return max unresponsive time + * + * Return: none + */ +#ifdef CONVERGED_P2P_ENABLE +static inline void +wma_get_go_probe_timeout(struct mac_context *mac, + uint32_t *max_inactive_time, + uint32_t *max_unresponsive_time) +{ + uint32_t keep_alive; + QDF_STATUS status; + + status = cfg_p2p_get_go_link_monitor_period(mac->psoc, + max_inactive_time); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to go monitor period"); + *max_inactive_time = WMA_LINK_MONITOR_DEFAULT_TIME_SECS; + } + status = cfg_p2p_get_go_keepalive_period(mac->psoc, + &keep_alive); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to read go keep alive"); + keep_alive = WMA_KEEP_ALIVE_DEFAULT_TIME_SECS; + } + + *max_unresponsive_time = *max_inactive_time + keep_alive; +} +#else +static inline void +wma_get_go_probe_timeout(struct mac_context *mac, + uint32_t *max_inactive_time, + uint32_t *max_unresponsive_time) +{ +} +#endif + +/** + * wma_get_link_probe_timeout() - get link timeout based on sub type + * @mac: UMAC handler + * @sub_type: vdev syb type + * @max_inactive_time: return max inactive time + * @max_unresponsive_time: return max unresponsive time + * + * Return: none + */ +static inline void +wma_get_link_probe_timeout(struct mac_context *mac, + uint32_t sub_type, + uint32_t *max_inactive_time, + uint32_t *max_unresponsive_time) +{ + if (sub_type == WMI_UNIFIED_VDEV_SUBTYPE_P2P_GO) { + wma_get_go_probe_timeout(mac, max_inactive_time, + max_unresponsive_time); + } else { + *max_inactive_time = + mac->mlme_cfg->timeouts.ap_link_monitor_timeout; + *max_unresponsive_time = *max_inactive_time + + mac->mlme_cfg->timeouts.ap_keep_alive_timeout; + } +} + +/** + * wma_verify_rate_code() - verify if rate code is valid. + * @rate_code: rate code + * @band: band information + * + * Return: verify result + */ +static bool wma_verify_rate_code(u_int32_t rate_code, enum cds_band_type band) +{ + uint8_t preamble, nss, rate; + bool valid = true; + + preamble = (rate_code & 0xc0) >> 6; + nss = (rate_code & 0x30) >> 4; + rate = rate_code & 0xf; + + switch (preamble) { + case WMI_RATE_PREAMBLE_CCK: + if (nss != 0 || rate > 3 || band == CDS_BAND_5GHZ) + valid = false; + break; + case WMI_RATE_PREAMBLE_OFDM: + if (nss != 0 || rate > 7) + valid = false; + break; + case WMI_RATE_PREAMBLE_HT: + if (nss != 0 || rate > 7) + valid = false; + break; + case WMI_RATE_PREAMBLE_VHT: + if (nss != 0 || rate > 9) + valid = false; + break; + default: + break; + } + return valid; +} + +#define TX_MGMT_RATE_2G_ENABLE_OFFSET 30 +#define TX_MGMT_RATE_5G_ENABLE_OFFSET 31 +#define TX_MGMT_RATE_2G_OFFSET 0 +#define TX_MGMT_RATE_5G_OFFSET 12 + +/** + * wma_set_mgmt_rate() - set vdev mgmt rate. + * @wma: wma handle + * @vdev_id: vdev id + * + * Return: None + */ +void wma_set_vdev_mgmt_rate(tp_wma_handle wma, uint8_t vdev_id) +{ + uint32_t cfg_val; + int ret; + uint32_t per_band_mgmt_tx_rate = 0; + enum cds_band_type band = 0; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: Failed to get mac", __func__); + return; + } + + cfg_val = mac->mlme_cfg->sap_cfg.rate_tx_mgmt; + band = CDS_BAND_ALL; + if ((cfg_val == MLME_CFG_TX_MGMT_RATE_DEF) || + !wma_verify_rate_code(cfg_val, band)) { + wma_nofl_debug("default WNI_CFG_RATE_FOR_TX_MGMT, ignore"); + } else { + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_MGMT_TX_RATE, + cfg_val); + if (ret) + WMA_LOGE("Failed to set WMI_VDEV_PARAM_MGMT_TX_RATE"); + } + + cfg_val = mac->mlme_cfg->sap_cfg.rate_tx_mgmt_2g; + band = CDS_BAND_2GHZ; + if ((cfg_val == MLME_CFG_TX_MGMT_2G_RATE_DEF) || + !wma_verify_rate_code(cfg_val, band)) { + wma_nofl_debug("use default 2G MGMT rate."); + per_band_mgmt_tx_rate &= + ~(1 << TX_MGMT_RATE_2G_ENABLE_OFFSET); + } else { + per_band_mgmt_tx_rate |= + (1 << TX_MGMT_RATE_2G_ENABLE_OFFSET); + per_band_mgmt_tx_rate |= + ((cfg_val & 0x7FF) << TX_MGMT_RATE_2G_OFFSET); + } + + cfg_val = mac->mlme_cfg->sap_cfg.rate_tx_mgmt; + band = CDS_BAND_5GHZ; + if ((cfg_val == MLME_CFG_TX_MGMT_5G_RATE_DEF) || + !wma_verify_rate_code(cfg_val, band)) { + wma_nofl_debug("use default 5G MGMT rate."); + per_band_mgmt_tx_rate &= + ~(1 << TX_MGMT_RATE_5G_ENABLE_OFFSET); + } else { + per_band_mgmt_tx_rate |= + (1 << TX_MGMT_RATE_5G_ENABLE_OFFSET); + per_band_mgmt_tx_rate |= + ((cfg_val & 0x7FF) << TX_MGMT_RATE_5G_OFFSET); + } + + ret = wma_vdev_set_param( + wma->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_PER_BAND_MGMT_TX_RATE, + per_band_mgmt_tx_rate); + if (ret) + WMA_LOGE("Failed to set WMI_VDEV_PARAM_PER_BAND_MGMT_TX_RATE"); + +} + +/** + * wma_set_sap_keepalive() - set SAP keep alive parameters to fw + * @wma: wma handle + * @vdev_id: vdev id + * + * Return: none + */ +void wma_set_sap_keepalive(tp_wma_handle wma, uint8_t vdev_id) +{ + uint32_t min_inactive_time, max_inactive_time, max_unresponsive_time; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + QDF_STATUS status; + + if (!mac) { + WMA_LOGE("%s: Failed to get mac", __func__); + return; + } + + wma_get_link_probe_timeout(mac, wma->interfaces[vdev_id].sub_type, + &max_inactive_time, &max_unresponsive_time); + + min_inactive_time = max_inactive_time / 2; + + status = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + min_inactive_time); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to Set AP MIN IDLE INACTIVE TIME"); + + status = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + max_inactive_time); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to Set AP MAX IDLE INACTIVE TIME"); + + status = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + max_unresponsive_time); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to Set MAX UNRESPONSIVE TIME"); + + wma_debug("vdev_id:%d min_inactive_time: %u max_inactive_time: %u max_unresponsive_time: %u", + vdev_id, min_inactive_time, max_inactive_time, + max_unresponsive_time); +} + +/** + * wma_set_sta_sa_query_param() - set sta sa query parameters + * @wma: wma handle + * @vdev_id: vdev id + + * This function sets sta query related parameters in fw. + * + * Return: none + */ + +void wma_set_sta_sa_query_param(tp_wma_handle wma, + uint8_t vdev_id) +{ + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + uint8_t max_retries; + uint16_t retry_interval; + + if (!mac) { + WMA_LOGE(FL("mac context is NULL")); + return; + } + + max_retries = mac->mlme_cfg->gen.pmf_sa_query_max_retries; + retry_interval = mac->mlme_cfg->gen.pmf_sa_query_retry_interval; + + wmi_unified_set_sta_sa_query_param_cmd(wma->wmi_handle, + vdev_id, + max_retries, + retry_interval); +} + +/** + * wma_set_sta_keep_alive() - set sta keep alive parameters + * @wma: wma handle + * @vdev_id: vdev id + * @method: method for keep alive + * @timeperiod: time period + * @hostv4addr: host ipv4 address + * @destv4addr: dst ipv4 address + * @destmac: destination mac + * + * This function sets keep alive related parameters in fw. + * + * Return: none + */ +void wma_set_sta_keep_alive(tp_wma_handle wma, uint8_t vdev_id, + uint32_t method, uint32_t timeperiod, + uint8_t *hostv4addr, uint8_t *destv4addr, + uint8_t *destmac) +{ + struct sta_keep_alive_params params = { 0 }; + + if (!wma) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return; + } + + if (timeperiod > cfg_max(CFG_INFRA_STA_KEEP_ALIVE_PERIOD)) { + WMI_LOGE("Invalid period %d Max limit %d", timeperiod, + cfg_max(CFG_INFRA_STA_KEEP_ALIVE_PERIOD)); + return; + } + + params.vdev_id = vdev_id; + params.method = method; + params.timeperiod = timeperiod; + if (hostv4addr) + qdf_mem_copy(params.hostv4addr, hostv4addr, QDF_IPV4_ADDR_SIZE); + if (destv4addr) + qdf_mem_copy(params.destv4addr, destv4addr, QDF_IPV4_ADDR_SIZE); + if (destmac) + qdf_mem_copy(params.destmac, destmac, QDF_MAC_ADDR_SIZE); + + wmi_unified_set_sta_keep_alive_cmd(wma->wmi_handle, ¶ms); +} + +/* + * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": + * 0 for no restriction + * 1 for 1/4 us - Our lower layer calculations limit our precision to 1 msec + * 2 for 1/2 us - Our lower layer calculations limit our precision to 1 msec + * 3 for 1 us + * 4 for 2 us + * 5 for 4 us + * 6 for 8 us + * 7 for 16 us + */ +static const uint8_t wma_mpdu_spacing[] = { 0, 1, 1, 1, 2, 4, 8, 16 }; + +/** + * wma_parse_mpdudensity() - give mpdu spacing from mpdu density + * @mpdudensity: mpdu density + * + * Return: mpdu spacing or 0 for error + */ +static inline uint8_t wma_parse_mpdudensity(uint8_t mpdudensity) +{ + if (mpdudensity < sizeof(wma_mpdu_spacing)) + return wma_mpdu_spacing[mpdudensity]; + else + return 0; +} + +#define CFG_CTRL_MASK 0xFF00 +#define CFG_DATA_MASK 0x00FF + +/** + * wma_mask_tx_ht_rate() - mask tx ht rate based on config + * @wma: wma handle + * @mcs_set mcs set buffer + * + * Return: None + */ +static void wma_mask_tx_ht_rate(tp_wma_handle wma, uint8_t *mcs_set) +{ + uint32_t i, j; + uint16_t mcs_limit; + uint8_t *rate_pos = mcs_set; + struct mac_context *mac = wma->mac_context; + + /* + * Get MCS limit from ini configure, and map it to rate parameters + * This will limit HT rate upper bound. CFG_CTRL_MASK is used to + * check whether ini config is enabled and CFG_DATA_MASK to get the + * MCS value. + */ + mcs_limit = mac->mlme_cfg->rates.max_htmcs_txdata; + + if (mcs_limit & CFG_CTRL_MASK) { + wma_debug("set mcs_limit %x", mcs_limit); + + mcs_limit &= CFG_DATA_MASK; + for (i = 0, j = 0; i < MAX_SUPPORTED_RATES;) { + if (j < mcs_limit / 8) { + rate_pos[j] = 0xff; + j++; + i += 8; + } else if (j < mcs_limit / 8 + 1) { + if (i <= mcs_limit) + rate_pos[i / 8] |= 1 << (i % 8); + else + rate_pos[i / 8] &= ~(1 << (i % 8)); + i++; + + if (i >= (j + 1) * 8) + j++; + } else { + rate_pos[j++] = 0; + i += 8; + } + } + } +} + +#if SUPPORT_11AX +/** + * wma_fw_to_host_phymode_11ax() - convert fw to host phymode for 11ax phymodes + * @phymode: phymode to convert + * + * Return: one of the 11ax values defined in enum wlan_phymode; + * or WLAN_PHYMODE_AUTO if the input is not an 11ax phymode + */ +static enum wlan_phymode +wma_fw_to_host_phymode_11ax(WMI_HOST_WLAN_PHY_MODE phymode) +{ + switch (phymode) { + default: + return WLAN_PHYMODE_AUTO; + case WMI_HOST_MODE_11AX_HE20: + return WLAN_PHYMODE_11AXA_HE20; + case WMI_HOST_MODE_11AX_HE40: + return WLAN_PHYMODE_11AXA_HE40; + case WMI_HOST_MODE_11AX_HE80: + return WLAN_PHYMODE_11AXA_HE80; + case WMI_HOST_MODE_11AX_HE80_80: + return WLAN_PHYMODE_11AXA_HE80_80; + case WMI_HOST_MODE_11AX_HE160: + return WLAN_PHYMODE_11AXA_HE160; + case WMI_HOST_MODE_11AX_HE20_2G: + return WLAN_PHYMODE_11AXG_HE20; + case WMI_HOST_MODE_11AX_HE40_2G: + return WLAN_PHYMODE_11AXG_HE40; + case WMI_HOST_MODE_11AX_HE80_2G: + return WLAN_PHYMODE_11AXG_HE80; + } + return WLAN_PHYMODE_AUTO; +} +#else +static enum wlan_phymode +wma_fw_to_host_phymode_11ax(WMI_HOST_WLAN_PHY_MODE phymode) +{ + return WLAN_PHYMODE_AUTO; +} +#endif + +#ifdef CONFIG_160MHZ_SUPPORT +/** + * wma_fw_to_host_phymode_160() - convert fw to host phymode for 160 mhz + * phymodes + * @phymode: phymode to convert + * + * Return: one of the 160 mhz values defined in enum wlan_phymode; + * or WLAN_PHYMODE_AUTO if the input is not a 160 mhz phymode + */ +static enum wlan_phymode +wma_fw_to_host_phymode_160(WMI_HOST_WLAN_PHY_MODE phymode) +{ + switch (phymode) { + default: + return WLAN_PHYMODE_AUTO; + case WMI_HOST_MODE_11AC_VHT80_80: + return WLAN_PHYMODE_11AC_VHT80_80; + case WMI_HOST_MODE_11AC_VHT160: + return WLAN_PHYMODE_11AC_VHT160; + } +} +#else +static enum wlan_phymode +wma_fw_to_host_phymode_160(WMI_HOST_WLAN_PHY_MODE phymode) +{ + return WLAN_PHYMODE_AUTO; +} +#endif + +enum wlan_phymode wma_fw_to_host_phymode(WMI_HOST_WLAN_PHY_MODE phymode) +{ + enum wlan_phymode host_phymode; + switch (phymode) { + default: + host_phymode = wma_fw_to_host_phymode_160(phymode); + if (host_phymode != WLAN_PHYMODE_AUTO) + return host_phymode; + return wma_fw_to_host_phymode_11ax(phymode); + case WMI_HOST_MODE_11A: + return WLAN_PHYMODE_11A; + case WMI_HOST_MODE_11G: + return WLAN_PHYMODE_11G; + case WMI_HOST_MODE_11B: + return WLAN_PHYMODE_11B; + case WMI_HOST_MODE_11GONLY: + return WLAN_PHYMODE_11G_ONLY; + case WMI_HOST_MODE_11NA_HT20: + return WLAN_PHYMODE_11NA_HT20; + case WMI_HOST_MODE_11NG_HT20: + return WLAN_PHYMODE_11NG_HT20; + case WMI_HOST_MODE_11NA_HT40: + return WLAN_PHYMODE_11NA_HT40; + case WMI_HOST_MODE_11NG_HT40: + return WLAN_PHYMODE_11NG_HT40; + case WMI_HOST_MODE_11AC_VHT20: + return WLAN_PHYMODE_11AC_VHT20; + case WMI_HOST_MODE_11AC_VHT40: + return WLAN_PHYMODE_11AC_VHT40; + case WMI_HOST_MODE_11AC_VHT80: + return WLAN_PHYMODE_11AC_VHT80; + case WMI_HOST_MODE_11AC_VHT20_2G: + return WLAN_PHYMODE_11AC_VHT20_2G; + case WMI_HOST_MODE_11AC_VHT40_2G: + return WLAN_PHYMODE_11AC_VHT40_2G; + case WMI_HOST_MODE_11AC_VHT80_2G: + return WLAN_PHYMODE_11AC_VHT80_2G; + } +} + +#ifdef CONFIG_160MHZ_SUPPORT +/** + * wma_host_to_fw_phymode_160() - convert host to fw phymode for 160 mhz + * @host_phymode: phymode to convert + * + * Return: one of the 160 mhz values defined in enum WMI_HOST_WLAN_PHY_MODE; + * or WMI_HOST_MODE_UNKNOWN if the input is not a 160 mhz phymode + */ +static WMI_HOST_WLAN_PHY_MODE +wma_host_to_fw_phymode_160(enum wlan_phymode host_phymode) +{ + switch (host_phymode) { + case WLAN_PHYMODE_11AC_VHT80_80: + return WMI_HOST_MODE_11AC_VHT80_80; + case WLAN_PHYMODE_11AC_VHT160: + return WMI_HOST_MODE_11AC_VHT160; + default: + return WMI_HOST_MODE_UNKNOWN; + } +} +#else +static WMI_HOST_WLAN_PHY_MODE +wma_host_to_fw_phymode_160(enum wlan_phymode host_phymode) +{ + return WMI_HOST_MODE_UNKNOWN; +} +#endif + +#if SUPPORT_11AX +/** + * wma_host_to_fw_phymode_11ax() - convert host to fw phymode for 11ax phymode + * @host_phymode: phymode to convert + * + * Return: one of the 11ax values defined in enum WMI_HOST_WLAN_PHY_MODE; + * or WMI_HOST_MODE_UNKNOWN if the input is not an 11ax phymode + */ +static WMI_HOST_WLAN_PHY_MODE +wma_host_to_fw_phymode_11ax(enum wlan_phymode host_phymode) +{ + switch (host_phymode) { + case WLAN_PHYMODE_11AXA_HE20: + return WMI_HOST_MODE_11AX_HE20; + case WLAN_PHYMODE_11AXA_HE40: + return WMI_HOST_MODE_11AX_HE40; + case WLAN_PHYMODE_11AXA_HE80: + return WMI_HOST_MODE_11AX_HE80; + case WLAN_PHYMODE_11AXA_HE80_80: + return WMI_HOST_MODE_11AX_HE80_80; + case WLAN_PHYMODE_11AXA_HE160: + return WMI_HOST_MODE_11AX_HE160; + case WLAN_PHYMODE_11AXG_HE20: + return WMI_HOST_MODE_11AX_HE20_2G; + case WLAN_PHYMODE_11AXG_HE40: + case WLAN_PHYMODE_11AXG_HE40PLUS: + case WLAN_PHYMODE_11AXG_HE40MINUS: + return WMI_HOST_MODE_11AX_HE40_2G; + case WLAN_PHYMODE_11AXG_HE80: + return WMI_HOST_MODE_11AX_HE80_2G; + default: + return WMI_HOST_MODE_UNKNOWN; + } +} +#else +static WMI_HOST_WLAN_PHY_MODE +wma_host_to_fw_phymode_11ax(enum wlan_phymode host_phymode) +{ + return WMI_HOST_MODE_UNKNOWN; +} +#endif + +WMI_HOST_WLAN_PHY_MODE wma_host_to_fw_phymode(enum wlan_phymode host_phymode) +{ + WMI_HOST_WLAN_PHY_MODE fw_phymode; + + switch (host_phymode) { + case WLAN_PHYMODE_11A: + return WMI_HOST_MODE_11A; + case WLAN_PHYMODE_11G: + return WMI_HOST_MODE_11G; + case WLAN_PHYMODE_11B: + return WMI_HOST_MODE_11B; + case WLAN_PHYMODE_11G_ONLY: + return WMI_HOST_MODE_11GONLY; + case WLAN_PHYMODE_11NA_HT20: + return WMI_HOST_MODE_11NA_HT20; + case WLAN_PHYMODE_11NG_HT20: + return WMI_HOST_MODE_11NG_HT20; + case WLAN_PHYMODE_11NA_HT40: + return WMI_HOST_MODE_11NA_HT40; + case WLAN_PHYMODE_11NG_HT40: + case WLAN_PHYMODE_11NG_HT40PLUS: + case WLAN_PHYMODE_11NG_HT40MINUS: + return WMI_HOST_MODE_11NG_HT40; + case WLAN_PHYMODE_11AC_VHT20: + return WMI_HOST_MODE_11AC_VHT20; + case WLAN_PHYMODE_11AC_VHT40: + return WMI_HOST_MODE_11AC_VHT40; + case WLAN_PHYMODE_11AC_VHT80: + return WMI_HOST_MODE_11AC_VHT80; + case WLAN_PHYMODE_11AC_VHT20_2G: + return WMI_HOST_MODE_11AC_VHT20_2G; + case WLAN_PHYMODE_11AC_VHT40PLUS_2G: + case WLAN_PHYMODE_11AC_VHT40MINUS_2G: + case WLAN_PHYMODE_11AC_VHT40_2G: + return WMI_HOST_MODE_11AC_VHT40_2G; + case WLAN_PHYMODE_11AC_VHT80_2G: + return WMI_HOST_MODE_11AC_VHT80_2G; + default: + fw_phymode = wma_host_to_fw_phymode_160(host_phymode); + if (fw_phymode != WMI_HOST_MODE_UNKNOWN) + return fw_phymode; + return wma_host_to_fw_phymode_11ax(host_phymode); + } +} + +void wma_objmgr_set_peer_mlme_phymode(tp_wma_handle wma, uint8_t *mac_addr, + enum wlan_phymode phymode) +{ + uint8_t pdev_id; + struct wlan_objmgr_peer *peer; + struct wlan_objmgr_psoc *psoc = wma->psoc; + + pdev_id = wlan_objmgr_pdev_get_pdev_id(wma->pdev); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_LEGACY_WMA_ID); + if (!peer) + return; + + wlan_peer_obj_lock(peer); + wlan_peer_set_phymode(peer, phymode); + wlan_peer_obj_unlock(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); +} + +/** + * wma_objmgr_set_peer_mlme_type() - set peer type to peer object + * @wma: wma handle + * @mac_addr: mac addr of peer + * @peer_type: peer type value to set + * + * Return: None + */ +static void wma_objmgr_set_peer_mlme_type(tp_wma_handle wma, + uint8_t *mac_addr, + enum wlan_peer_type peer_type) +{ + uint8_t pdev_id; + struct wlan_objmgr_peer *peer; + struct wlan_objmgr_psoc *psoc = wma->psoc; + + pdev_id = wlan_objmgr_pdev_get_pdev_id(wma->pdev); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_LEGACY_WMA_ID); + if (!peer) + return; + + wlan_peer_obj_lock(peer); + wlan_peer_set_peer_type(peer, peer_type); + wlan_peer_obj_unlock(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); +} + +/** + * wmi_unified_send_peer_assoc() - send peer assoc command to fw + * @wma: wma handle + * @nw_type: nw type + * @params: add sta params + * + * This function send peer assoc command to firmware with + * different parameters. + * + * Return: QDF_STATUS + */ +QDF_STATUS wma_send_peer_assoc(tp_wma_handle wma, + tSirNwType nw_type, + tpAddStaParams params) +{ + struct peer_assoc_params *cmd; + int32_t ret, max_rates, i; + uint8_t *rate_pos; + wmi_rate_set peer_legacy_rates, peer_ht_rates; + uint32_t num_peer_11b_rates = 0; + uint32_t num_peer_11a_rates = 0; + enum wlan_phymode phymode, vdev_phymode; + uint32_t peer_nss = 1; + struct wma_txrx_node *intr = NULL; + bool is_he; + QDF_STATUS status; + struct mac_context *mac = wma->mac_context; + struct wlan_channel *des_chan; + + cmd = qdf_mem_malloc(sizeof(struct peer_assoc_params)); + if (!cmd) { + WMA_LOGE("Failed to allocate peer_assoc_params param"); + return QDF_STATUS_E_NOMEM; + } + + intr = &wma->interfaces[params->smesessionId]; + + wma_mask_tx_ht_rate(wma, params->supportedRates.supportedMCSSet); + + qdf_mem_zero(&peer_legacy_rates, sizeof(wmi_rate_set)); + qdf_mem_zero(&peer_ht_rates, sizeof(wmi_rate_set)); + qdf_mem_zero(cmd, sizeof(struct peer_assoc_params)); + + is_he = wma_is_peer_he_capable(params); + if ((params->ch_width > CH_WIDTH_40MHZ) && + ((nw_type == eSIR_11G_NW_TYPE) || + (nw_type == eSIR_11B_NW_TYPE))) { + WMA_LOGE("ch_width %d sent in 11G, configure to 40MHz", + params->ch_width); + params->ch_width = CH_WIDTH_40MHZ; + } + phymode = wma_peer_phymode(nw_type, params->staType, + params->htCapable, params->ch_width, + params->vhtCapable, is_he); + + des_chan = wlan_vdev_mlme_get_des_chan(intr->vdev); + vdev_phymode = des_chan->ch_phymode; + if ((intr->type == WMI_VDEV_TYPE_AP) && (phymode > vdev_phymode)) { + wma_nofl_debug("Peer phymode %d is not allowed. Set it equal to sap/go phymode %d", + phymode, vdev_phymode); + phymode = vdev_phymode; + } + + if (!mac->mlme_cfg->rates.disable_abg_rate_txdata) { + /* Legacy Rateset */ + rate_pos = (uint8_t *) peer_legacy_rates.rates; + for (i = 0; i < SIR_NUM_11B_RATES; i++) { + if (!params->supportedRates.llbRates[i]) + continue; + rate_pos[peer_legacy_rates.num_rates++] = + params->supportedRates.llbRates[i]; + num_peer_11b_rates++; + } + for (i = 0; i < SIR_NUM_11A_RATES; i++) { + if (!params->supportedRates.llaRates[i]) + continue; + rate_pos[peer_legacy_rates.num_rates++] = + params->supportedRates.llaRates[i]; + num_peer_11a_rates++; + } + } + + if ((phymode == WLAN_PHYMODE_11A && num_peer_11a_rates == 0) || + (phymode == WLAN_PHYMODE_11B && num_peer_11b_rates == 0)) { + WMA_LOGW("%s: Invalid phy rates. phymode 0x%x, 11b_rates %d, 11a_rates %d", + __func__, phymode, num_peer_11b_rates, + num_peer_11a_rates); + qdf_mem_free(cmd); + return QDF_STATUS_E_INVAL; + } + + /* HT Rateset */ + max_rates = sizeof(peer_ht_rates.rates) / + sizeof(peer_ht_rates.rates[0]); + rate_pos = (uint8_t *) peer_ht_rates.rates; + for (i = 0; i < MAX_SUPPORTED_RATES; i++) { + if (params->supportedRates.supportedMCSSet[i / 8] & + (1 << (i % 8))) { + rate_pos[peer_ht_rates.num_rates++] = i; + if (i >= 8) { + /* MCS8 or higher rate is present, must be 2x2 */ + peer_nss = 2; + } + } + if (peer_ht_rates.num_rates == max_rates) + break; + } + + if (params->htCapable && !peer_ht_rates.num_rates) { + uint8_t temp_ni_rates[8] = { 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x6, 0x7}; + /* + * Workaround for EV 116382: The peer is marked HT but with + * supported rx mcs set is set to 0. 11n spec mandates MCS0-7 + * for a HT STA. So forcing the supported rx mcs rate to + * MCS 0-7. This workaround will be removed once we get + * clarification from WFA regarding this STA behavior. + */ + + /* TODO: Do we really need this? */ + WMA_LOGW("Peer is marked as HT capable but supported mcs rate is 0"); + peer_ht_rates.num_rates = sizeof(temp_ni_rates); + qdf_mem_copy((uint8_t *) peer_ht_rates.rates, temp_ni_rates, + peer_ht_rates.num_rates); + } + + /* in ap/ibss mode and for tdls peer, use mac address of the peer in + * the other end as the new peer address; in sta mode, use bss id to + * be the new peer address + */ + if ((wma_is_vdev_in_ap_mode(wma, params->smesessionId)) + || (wma_is_vdev_in_ibss_mode(wma, params->smesessionId)) +#ifdef FEATURE_WLAN_TDLS + || (STA_ENTRY_TDLS_PEER == params->staType) +#endif /* FEATURE_WLAN_TDLS */ + ) { + qdf_mem_copy(cmd->peer_mac, params->staMac, + sizeof(cmd->peer_mac)); + } else { + qdf_mem_copy(cmd->peer_mac, params->bssId, + sizeof(cmd->peer_mac)); + } + wma_objmgr_set_peer_mlme_phymode(wma, cmd->peer_mac, phymode); + + cmd->vdev_id = params->smesessionId; + cmd->peer_new_assoc = 1; + cmd->peer_associd = params->assocId; + + cmd->is_wme_set = 1; + + if (params->wmmEnabled) + cmd->qos_flag = 1; + + if (params->uAPSD) { + cmd->apsd_flag = 1; + wma_nofl_debug("Set WMI_PEER_APSD: uapsd Mask %d", + params->uAPSD); + } + + if (params->htCapable) { + cmd->ht_flag = 1; + cmd->qos_flag = 1; + cmd->peer_rate_caps |= WMI_RC_HT_FLAG; + } + + if (params->vhtCapable) { + cmd->ht_flag = 1; + cmd->qos_flag = 1; + cmd->vht_flag = 1; + cmd->peer_rate_caps |= WMI_RC_HT_FLAG; + } + + if (params->ch_width) { + cmd->bw_40 = 1; + cmd->peer_rate_caps |= WMI_RC_CW40_FLAG; + if (params->fShortGI40Mhz) + cmd->peer_rate_caps |= WMI_RC_SGI_FLAG; + } else if (params->fShortGI20Mhz) { + cmd->peer_rate_caps |= WMI_RC_SGI_FLAG; + } + + if (params->ch_width == CH_WIDTH_80MHZ) + cmd->bw_80 = 1; + else if (params->ch_width == CH_WIDTH_160MHZ) + cmd->bw_160 = 1; + else if (params->ch_width == CH_WIDTH_80P80MHZ) + cmd->bw_160 = 1; + + cmd->peer_vht_caps = params->vht_caps; + if (params->p2pCapableSta) { + cmd->p2p_capable_sta = 1; + wma_objmgr_set_peer_mlme_type(wma, params->staMac, + WLAN_PEER_P2P_CLI); + } + + if (params->rmfEnabled) + cmd->is_pmf_enabled = 1; + + if (params->stbc_capable) + cmd->stbc_flag = 1; + + if (params->htLdpcCapable || params->vhtLdpcCapable) + cmd->ldpc_flag = 1; + + switch (params->mimoPS) { + case eSIR_HT_MIMO_PS_STATIC: + cmd->static_mimops_flag = 1; + break; + case eSIR_HT_MIMO_PS_DYNAMIC: + cmd->dynamic_mimops_flag = 1; + break; + case eSIR_HT_MIMO_PS_NO_LIMIT: + cmd->spatial_mux_flag = 1; + break; + default: + break; + } + + wma_set_twt_peer_caps(params, cmd); +#ifdef FEATURE_WLAN_TDLS + if (STA_ENTRY_TDLS_PEER == params->staType) + cmd->auth_flag = 1; +#endif /* FEATURE_WLAN_TDLS */ + + if (params->wpa_rsn +#ifdef FEATURE_WLAN_WAPI + || params->encryptType == eSIR_ED_WPI +#endif /* FEATURE_WLAN_WAPI */ + ) { + if (!params->no_ptk_4_way) + cmd->need_ptk_4_way = 1; + wma_nofl_debug("Acquire set key wake lock for %d ms", + WMA_VDEV_SET_KEY_WAKELOCK_TIMEOUT); + wma_acquire_wakelock(&intr->vdev_set_key_wakelock, + WMA_VDEV_SET_KEY_WAKELOCK_TIMEOUT); + qdf_runtime_pm_prevent_suspend( + &intr->vdev_set_key_runtime_wakelock); + } + if (params->wpa_rsn >> 1) + cmd->need_gtk_2_way = 1; + +#ifdef FEATURE_WLAN_WAPI + if (params->encryptType == eSIR_ED_WPI) { + ret = wma_vdev_set_param(wma->wmi_handle, params->smesessionId, + WMI_VDEV_PARAM_DROP_UNENCRY, false); + if (ret) { + WMA_LOGE + ("Set WMI_VDEV_PARAM_DROP_UNENCRY Param status:%d\n", + ret); + qdf_mem_free(cmd); + return ret; + } + } +#endif /* FEATURE_WLAN_WAPI */ + + cmd->peer_caps = params->capab_info; + cmd->peer_listen_intval = params->listenInterval; + cmd->peer_ht_caps = params->ht_caps; + cmd->peer_max_mpdu = (1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + + params->maxAmpduSize)) - 1; + cmd->peer_mpdu_density = wma_parse_mpdudensity(params->maxAmpduDensity); + + if (params->supportedRates.supportedMCSSet[1] && + params->supportedRates.supportedMCSSet[2]) + cmd->peer_rate_caps |= WMI_RC_TS_FLAG; + else if (params->supportedRates.supportedMCSSet[1]) + cmd->peer_rate_caps |= WMI_RC_DS_FLAG; + + /* Update peer legacy rate information */ + cmd->peer_legacy_rates.num_rates = peer_legacy_rates.num_rates; + qdf_mem_copy(cmd->peer_legacy_rates.rates, peer_legacy_rates.rates, + peer_legacy_rates.num_rates); + + /* Update peer HT rate information */ + cmd->peer_ht_rates.num_rates = peer_ht_rates.num_rates; + qdf_mem_copy(cmd->peer_ht_rates.rates, peer_ht_rates.rates, + peer_ht_rates.num_rates); + + /* VHT Rates */ + + cmd->peer_nss = peer_nss; + /* + * Because of DBS a vdev may come up in any of the two MACs with + * different capabilities. STBC capab should be fetched for given + * hard_mode->MAC_id combo. It is planned that firmware should provide + * these dev capabilities. But for now number of tx streams can be used + * to identify if Tx STBC needs to be disabled. + */ + if (intr->tx_streams < 2) { + cmd->peer_vht_caps &= ~(1 << SIR_MAC_VHT_CAP_TXSTBC); + wma_nofl_debug("Num tx_streams: %d, Disabled txSTBC", + intr->tx_streams); + } + + cmd->vht_capable = params->vhtCapable; + if (params->vhtCapable) { +#define VHT2x2MCSMASK 0xc + cmd->rx_max_rate = params->supportedRates.vhtRxHighestDataRate; + cmd->rx_mcs_set = params->supportedRates.vhtRxMCSMap; + cmd->tx_max_rate = params->supportedRates.vhtTxHighestDataRate; + cmd->tx_mcs_set = params->supportedRates.vhtTxMCSMap; + + if (params->vhtSupportedRxNss) { + cmd->peer_nss = params->vhtSupportedRxNss; + } else { + cmd->peer_nss = ((cmd->rx_mcs_set & VHT2x2MCSMASK) + == VHT2x2MCSMASK) ? 1 : 2; + } + + if (params->vht_mcs_10_11_supp) { + WMI_SET_BITS(cmd->tx_mcs_set, 16, cmd->peer_nss, + ((1 << cmd->peer_nss) - 1)); + WMI_VHT_MCS_NOTIFY_EXT_SS_SET(cmd->tx_mcs_set, 1); + } + if (params->vht_extended_nss_bw_cap && + (params->vht_160mhz_nss || params->vht_80p80mhz_nss)) { + /* + * bit[2:0] : Represents value of Rx NSS for 160 MHz + * bit[5:3] : Represents value of Rx NSS for 80_80 MHz + * Extended NSS support + * bit[30:6]: Reserved + * bit[31] : MSB(0/1): 1 in case of valid data + */ + cmd->peer_bw_rxnss_override |= (1 << 31); + if (params->vht_160mhz_nss) + cmd->peer_bw_rxnss_override |= + (params->vht_160mhz_nss - 1); + if (params->vht_80p80mhz_nss) + cmd->peer_bw_rxnss_override |= + ((params->vht_80p80mhz_nss - 1) << 3); + wma_debug("peer_bw_rxnss_override %0X", + cmd->peer_bw_rxnss_override); + } + } + + wma_debug("rx_max_rate %d, rx_mcs %x, tx_max_rate %d, tx_mcs: %x num rates %d need 4 way %d", + cmd->rx_max_rate, cmd->rx_mcs_set, cmd->tx_max_rate, + cmd->tx_mcs_set, peer_ht_rates.num_rates, + cmd->need_ptk_4_way); + + /* + * Limit nss to max number of rf chain supported by target + * Otherwise Fw will crash + */ + if (cmd->peer_nss > WMA_MAX_NSS) { + WMA_LOGE(FL("peer Nss %d is more than supported"), + cmd->peer_nss); + cmd->peer_nss = WMA_MAX_NSS; + } + + wma_populate_peer_he_cap(cmd, params); + if (!wma_is_vdev_in_ap_mode(wma, params->smesessionId)) + intr->nss = cmd->peer_nss; + + /* Till conversion is not done in WMI we need to fill fw phy mode */ + cmd->peer_phymode = wma_host_to_fw_phymode(phymode); + + status = wmi_unified_peer_assoc_send(wma->wmi_handle, + cmd); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGP(FL("Failed to send peer assoc command status = %d"), + status); + qdf_mem_free(cmd); + + return status; +} + +/** + * wmi_unified_vdev_set_gtx_cfg_send() - set GTX params + * @wmi_handle: wmi handle + * @if_id: vdev id + * @gtx_info: GTX config params + * + * This function set GTX related params in firmware. + * + * Return: 0 for success or error code + */ +QDF_STATUS wmi_unified_vdev_set_gtx_cfg_send(wmi_unified_t wmi_handle, + uint32_t if_id, + gtx_config_t *gtx_info) +{ + struct wmi_gtx_config params; + + params.gtx_rt_mask[0] = gtx_info->gtxRTMask[0]; + params.gtx_rt_mask[1] = gtx_info->gtxRTMask[1]; + params.gtx_usrcfg = gtx_info->gtxUsrcfg; + params.gtx_threshold = gtx_info->gtxPERThreshold; + params.gtx_margin = gtx_info->gtxPERMargin; + params.gtx_tpcstep = gtx_info->gtxTPCstep; + params.gtx_tpcmin = gtx_info->gtxTPCMin; + params.gtx_bwmask = gtx_info->gtxBWMask; + + return wmi_unified_vdev_set_gtx_cfg_cmd(wmi_handle, + if_id, ¶ms); + +} + +/** + * wma_update_protection_mode() - update protection mode + * @wma: wma handle + * @vdev_id: vdev id + * @llbcoexist: protection mode info + * + * This function set protection mode(RTS/CTS) to fw for passed vdev id. + * + * Return: none + */ +void wma_update_protection_mode(tp_wma_handle wma, uint8_t vdev_id, + uint8_t llbcoexist) +{ + QDF_STATUS ret; + enum ieee80211_protmode prot_mode; + + prot_mode = llbcoexist ? IEEE80211_PROT_CTSONLY : IEEE80211_PROT_NONE; + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_PROTECTION_MODE, + prot_mode); + + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to send wmi protection mode cmd"); + else + wma_nofl_debug("Updated protection mode %d to target", + prot_mode); +} + +void +wma_update_beacon_interval(tp_wma_handle wma, uint8_t vdev_id, + uint16_t beaconInterval) +{ + QDF_STATUS ret; + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_BEACON_INTERVAL, + beaconInterval); + + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to update beacon interval"); + else + WMA_LOGI("Updated beacon interval %d for vdev %d", + beaconInterval, vdev_id); +} + +#ifdef WLAN_FEATURE_11AX_BSS_COLOR +/** + * wma_update_bss_color() - update beacon bss color in fw + * @wma: wma handle + * @vdev_id: vdev id + * @he_ops: HE operation, only the bss_color and bss_color_disabled fields + * are updated. + * + * Return: none + */ +static void +wma_update_bss_color(tp_wma_handle wma, uint8_t vdev_id, + tUpdateBeaconParams *bcn_params) +{ + QDF_STATUS ret; + uint32_t dword_he_ops = 0; + + WMI_HEOPS_COLOR_SET(dword_he_ops, bcn_params->bss_color); + WMI_HEOPS_BSSCOLORDISABLE_SET(dword_he_ops, + bcn_params->bss_color_disabled); + wma_nofl_debug("vdev: %d, update bss color, HE_OPS: 0x%x", + vdev_id, dword_he_ops); + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_BSS_COLOR, dword_he_ops); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to update HE operations"); +} +#else +static void wma_update_bss_color(tp_wma_handle wma, uint8_t vdev_id, + tUpdateBeaconParams *bcn_params) +{ +} +#endif + +/** + * wma_process_update_beacon_params() - update beacon parameters to target + * @wma: wma handle + * @bcn_params: beacon parameters + * + * Return: none + */ +void +wma_process_update_beacon_params(tp_wma_handle wma, + tUpdateBeaconParams *bcn_params) +{ + if (!bcn_params) { + WMA_LOGE("bcn_params NULL"); + return; + } + + if (bcn_params->vdev_id >= wma->max_bssid) { + WMA_LOGE("Invalid vdev id %d", bcn_params->vdev_id); + return; + } + + if (bcn_params->paramChangeBitmap & PARAM_BCN_INTERVAL_CHANGED) { + wma_update_beacon_interval(wma, bcn_params->vdev_id, + bcn_params->beaconInterval); + } + + if (bcn_params->paramChangeBitmap & PARAM_llBCOEXIST_CHANGED) + wma_update_protection_mode(wma, bcn_params->vdev_id, + bcn_params->llbCoexist); + + if (bcn_params->paramChangeBitmap & PARAM_BSS_COLOR_CHANGED) + wma_update_bss_color(wma, bcn_params->vdev_id, + bcn_params); +} + +void wma_update_rts_params(tp_wma_handle wma, uint32_t value) +{ + uint8_t vdev_id; + QDF_STATUS ret; + struct wlan_objmgr_vdev *vdev; + + for (vdev_id = 0; vdev_id < wma->max_bssid; vdev_id++) { + vdev = wma->interfaces[vdev_id].vdev; + if (!vdev) + continue; + ret = wma_vdev_set_param(wma->wmi_handle, + vdev_id, + WMI_VDEV_PARAM_RTS_THRESHOLD, + value); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Update cfg param fail for vdevId %d", + vdev_id); + } +} + +void wma_update_frag_params(tp_wma_handle wma, uint32_t value) +{ + uint8_t vdev_id; + QDF_STATUS ret; + struct wlan_objmgr_vdev *vdev; + + for (vdev_id = 0; vdev_id < wma->max_bssid; vdev_id++) { + vdev = wma->interfaces[vdev_id].vdev; + if (!vdev) + continue; + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + value); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Update cfg params failed for vdevId %d", + vdev_id); + } +} + +#ifdef FEATURE_WLAN_WAPI +#define WPI_IV_LEN 16 +#if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ + defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) +/** + * wma_fill_in_wapi_key_params() - update key parameters about wapi + * @key_params: wma key parameters + * @params: parameters pointer to be set + * @mode: operation mode + * + * Return: None + */ +static inline void wma_fill_in_wapi_key_params( + struct wma_set_key_params *key_params, + struct set_key_params *params, uint8_t mode) +{ + /* + * Since MCL shares same FW with WIN for Napier/Hasting, FW WAPI logic + * is fit for WIN, change it to align with WIN. + */ + unsigned char iv_init_ap[16] = { 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, + 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, + 0x5c, 0x36, 0x5c, 0x37}; + unsigned char iv_init_sta[16] = { 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, + 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, + 0x5c, 0x36, 0x5c, 0x36}; + + if (mode == wlan_op_mode_ap) { + qdf_mem_copy(params->rx_iv, iv_init_sta, + WPI_IV_LEN); + qdf_mem_copy(params->tx_iv, iv_init_ap, + WPI_IV_LEN); + } else { + qdf_mem_copy(params->rx_iv, iv_init_ap, + WPI_IV_LEN); + qdf_mem_copy(params->tx_iv, iv_init_sta, + WPI_IV_LEN); + } + + params->key_txmic_len = WMA_TXMIC_LEN; + params->key_rxmic_len = WMA_RXMIC_LEN; + + params->key_cipher = WMI_CIPHER_WAPI; +} +#else +static inline void wma_fill_in_wapi_key_params( + struct wma_set_key_params *key_params, + struct set_key_params *params, uint8_t mode) +{ + /*initialize receive and transmit IV with default values */ + /* **Note: tx_iv must be sent in reverse** */ + unsigned char tx_iv[16] = { 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, + 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, + 0x36, 0x5c, 0x36, 0x5c}; + unsigned char rx_iv[16] = { 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, + 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, + 0x5c, 0x36, 0x5c, 0x37}; + if (mode == wlan_op_mode_ap) { + /* Authenticator initializes the value of PN as + * 0x5C365C365C365C365C365C365C365C36 for MCastkeyUpdate + */ + if (key_params->unicast) + tx_iv[0] = 0x37; + + rx_iv[WPI_IV_LEN - 1] = 0x36; + } else { + if (!key_params->unicast) + rx_iv[WPI_IV_LEN - 1] = 0x36; + } + + params->key_txmic_len = WMA_TXMIC_LEN; + params->key_rxmic_len = WMA_RXMIC_LEN; + + qdf_mem_copy(params->rx_iv, &rx_iv, + WPI_IV_LEN); + qdf_mem_copy(params->tx_iv, &tx_iv, + WPI_IV_LEN); + params->key_cipher = WMI_CIPHER_WAPI; +} +#endif +#endif + +#ifdef QCA_IBSS_SUPPORT +/** + * wma_calc_ibss_heart_beat_timer() - calculate IBSS heart beat timer + * @peer_num: number of peers + * + * Return: heart beat timer value + */ +static uint16_t wma_calc_ibss_heart_beat_timer(int16_t peer_num) +{ + /* heart beat timer value look-up table */ + /* entry index : (the number of currently connected peers) - 1 + * entry value : the heart time threshold value in seconds for + * detecting ibss peer departure + */ + static const uint16_t heart_beat_timer[MAX_PEERS] = { + 4, 4, 4, 4, 4, 4, 4, 4, + 8, 8, 8, 8, 8, 8, 8, 8, + 12, 12, 12, 12, 12, 12, 12, 12, + 16, 16, 16, 16, 16, 16, 16, 16 + }; + + if (peer_num < 1 || peer_num > MAX_PEERS) + return 0; + + return heart_beat_timer[peer_num - 1]; +} + +void wma_adjust_ibss_heart_beat_timer(tp_wma_handle wma, + uint8_t vdev_id, + int8_t peer_num_delta) +{ + int16_t new_peer_num; + uint16_t new_timer_value_sec; + uint32_t new_timer_value_ms; + QDF_STATUS status; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (peer_num_delta != 1 && peer_num_delta != -1) { + WMA_LOGE("Invalid peer_num_delta value %d", peer_num_delta); + return; + } + + /* adjust peer numbers */ + new_peer_num = cdp_peer_update_ibss_add_peer_num_of_vdev( + soc, vdev_id, + peer_num_delta); + if (OL_TXRX_INVALID_NUM_PEERS == new_peer_num) { + WMA_LOGE("new peer num %d out of valid boundary", new_peer_num); + return; + } + + /* reset timer value if all peers departed */ + if (new_peer_num == 0) { + cdp_set_ibss_vdev_heart_beat_timer(soc, vdev_id, 0); + return; + } + + /* calculate new timer value */ + new_timer_value_sec = wma_calc_ibss_heart_beat_timer(new_peer_num); + if (new_timer_value_sec == 0) { + WMA_LOGE("timer value %d is invalid for peer number %d", + new_timer_value_sec, new_peer_num); + return; + } + if (new_timer_value_sec == + cdp_set_ibss_vdev_heart_beat_timer(soc, vdev_id, + new_timer_value_sec)) { + wma_nofl_debug("timer value %d stays same, no need to notify target", + new_timer_value_sec); + return; + } + + new_timer_value_ms = ((uint32_t)new_timer_value_sec) * 1000; + + status = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_IBSS_MAX_BCN_LOST_MS, + new_timer_value_ms); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to set IBSS link monitoring timer value"); + return; + } + + wma_nofl_debug("Set IBSS link monitor timer: peer_num = %d timer_value = %d", + new_peer_num, new_timer_value_ms); +} +#endif /* QCA_IBSS_SUPPORT */ + +/** + * wma_process_update_edca_param_req() - update EDCA params + * @handle: wma handle + * @edca_params: edca parameters + * + * This function updates EDCA parameters to the target + * + * Return: QDF Status + */ +QDF_STATUS wma_process_update_edca_param_req(WMA_HANDLE handle, + tEdcaParams *edca_params) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + struct wmi_host_wme_vparams wmm_param[QCA_WLAN_AC_ALL]; + tSirMacEdcaParamRecord *edca_record; + int ac; + struct ol_tx_wmm_param_t ol_tx_wmm_param; + uint8_t vdev_id; + QDF_STATUS status; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + vdev_id = edca_params->vdev_id; + if (!wma_is_vdev_valid(vdev_id)) { + WMA_LOGE("%s: vdev id:%d is not active ", __func__, vdev_id); + goto fail; + } + + for (ac = 0; ac < QCA_WLAN_AC_ALL; ac++) { + switch (ac) { + case QCA_WLAN_AC_BE: + edca_record = &edca_params->acbe; + break; + case QCA_WLAN_AC_BK: + edca_record = &edca_params->acbk; + break; + case QCA_WLAN_AC_VI: + edca_record = &edca_params->acvi; + break; + case QCA_WLAN_AC_VO: + edca_record = &edca_params->acvo; + break; + default: + goto fail; + } + + wma_update_edca_params_for_ac(edca_record, &wmm_param[ac], ac, + edca_params->mu_edca_params); + + ol_tx_wmm_param.ac[ac].aifs = wmm_param[ac].aifs; + ol_tx_wmm_param.ac[ac].cwmin = wmm_param[ac].cwmin; + ol_tx_wmm_param.ac[ac].cwmax = wmm_param[ac].cwmax; + } + + status = wmi_unified_process_update_edca_param(wma_handle->wmi_handle, + vdev_id, + edca_params->mu_edca_params, + wmm_param); + if (status == QDF_STATUS_E_NOMEM) + return status; + else if (status == QDF_STATUS_E_FAILURE) + goto fail; + + cdp_set_wmm_param(soc, WMI_PDEV_ID_SOC, ol_tx_wmm_param); + + return QDF_STATUS_SUCCESS; + +fail: + WMA_LOGE("%s: Failed to set WMM Paremeters", __func__); + return QDF_STATUS_E_FAILURE; +} + +/** + * wmi_unified_probe_rsp_tmpl_send() - send probe response template to fw + * @wma: wma handle + * @vdev_id: vdev id + * @probe_rsp_info: probe response info + * + * Return: 0 for success or error code + */ +static int wmi_unified_probe_rsp_tmpl_send(tp_wma_handle wma, + uint8_t vdev_id, + tpSendProbeRespParams probe_rsp_info) +{ + uint64_t adjusted_tsf_le; + struct ieee80211_frame *wh; + struct wmi_probe_resp_params params; + + wma_debug("Send probe response template for vdev %d", vdev_id); + + /* + * Make the TSF offset negative so probe response in the same + * staggered batch have the same TSF. + */ + adjusted_tsf_le = cpu_to_le64(0ULL - + wma->interfaces[vdev_id].tsfadjust); + /* Update the timstamp in the probe response buffer with adjusted TSF */ + wh = (struct ieee80211_frame *)probe_rsp_info->probeRespTemplate; + A_MEMCPY(&wh[1], &adjusted_tsf_le, sizeof(adjusted_tsf_le)); + + params.prb_rsp_template_len = probe_rsp_info->probeRespTemplateLen; + params.prb_rsp_template_frm = probe_rsp_info->probeRespTemplate; + + return wmi_unified_probe_rsp_tmpl_send_cmd(wma->wmi_handle, vdev_id, + ¶ms); +} + +/** + * wma_unified_bcn_tmpl_send() - send beacon template to fw + * @wma:wma handle + * @vdev_id: vdev id + * @bcn_info: beacon info + * @bytes_to_strip: bytes to strip + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +static QDF_STATUS wma_unified_bcn_tmpl_send(tp_wma_handle wma, + uint8_t vdev_id, + const tpSendbeaconParams bcn_info, + uint8_t bytes_to_strip) +{ + struct beacon_tmpl_params params = {0}; + uint32_t tmpl_len, tmpl_len_aligned; + uint8_t *frm; + QDF_STATUS ret; + uint8_t *p2p_ie; + uint16_t p2p_ie_len = 0; + uint64_t adjusted_tsf_le; + struct ieee80211_frame *wh; + + if (!wma_is_vdev_valid(vdev_id)) { + WMA_LOGE("%s: vdev id:%d is not active ", __func__, vdev_id); + return QDF_STATUS_E_INVAL; + } + + wma_nofl_debug("Send beacon template for vdev %d", vdev_id); + + if (bcn_info->p2pIeOffset) { + p2p_ie = bcn_info->beacon + bcn_info->p2pIeOffset; + p2p_ie_len = (uint16_t) p2p_ie[1] + 2; + } + + /* + * XXX: The first byte of beacon buffer contains beacon length + * only when UMAC in sending the beacon template. In othercases + * (ex: from tbtt update) beacon length is read from beacon + * information. + */ + if (bytes_to_strip) + tmpl_len = *(uint32_t *) &bcn_info->beacon[0]; + else + tmpl_len = bcn_info->beaconLength; + + if (tmpl_len > WMI_BEACON_TX_BUFFER_SIZE) { + wma_err("tmpl_len: %d > %d. Invalid tmpl len", tmpl_len, + WMI_BEACON_TX_BUFFER_SIZE); + return -EINVAL; + } + + if (p2p_ie_len) { + if (tmpl_len <= p2p_ie_len) { + wma_err("tmpl_len %d <= p2p_ie_len %d, Invalid", + tmpl_len, p2p_ie_len); + return -EINVAL; + } + tmpl_len -= (uint32_t) p2p_ie_len; + } + + frm = bcn_info->beacon + bytes_to_strip; + tmpl_len_aligned = roundup(tmpl_len, sizeof(A_UINT32)); + /* + * Make the TSF offset negative so beacons in the same + * staggered batch have the same TSF. + */ + adjusted_tsf_le = cpu_to_le64(0ULL - + wma->interfaces[vdev_id].tsfadjust); + /* Update the timstamp in the beacon buffer with adjusted TSF */ + wh = (struct ieee80211_frame *)frm; + A_MEMCPY(&wh[1], &adjusted_tsf_le, sizeof(adjusted_tsf_le)); + + + + params.vdev_id = vdev_id; + params.tim_ie_offset = bcn_info->timIeOffset - bytes_to_strip; + params.tmpl_len = tmpl_len; + params.frm = frm; + params.tmpl_len_aligned = tmpl_len_aligned; + if (bcn_info->csa_count_offset && + (bcn_info->csa_count_offset > bytes_to_strip)) + params.csa_switch_count_offset = + bcn_info->csa_count_offset - bytes_to_strip; + if (bcn_info->ecsa_count_offset && + (bcn_info->ecsa_count_offset > bytes_to_strip)) + params.ext_csa_switch_count_offset = + bcn_info->ecsa_count_offset - bytes_to_strip; + + ret = wmi_unified_beacon_tmpl_send_cmd(wma->wmi_handle, + ¶ms); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("%s: Failed to send bcn tmpl: %d", __func__, ret); + + return ret; +} + +/** + * wma_store_bcn_tmpl() - store beacon template + * @wma: wma handle + * @vdev_id: vdev id + * @bcn_info: beacon params + * + * This function stores beacon template locally. + * This will send to target on the reception of + * SWBA event. + * + * Return: QDF status + */ +static QDF_STATUS wma_store_bcn_tmpl(tp_wma_handle wma, uint8_t vdev_id, + tpSendbeaconParams bcn_info) +{ + struct beacon_info *bcn; + uint32_t len; + uint8_t *bcn_payload; + struct beacon_tim_ie *tim_ie; + + bcn = wma->interfaces[vdev_id].beacon; + if (!bcn || !bcn->buf) { + WMA_LOGE("%s: Memory is not allocated to hold bcn template", + __func__); + return QDF_STATUS_E_INVAL; + } + + len = *(uint32_t *) &bcn_info->beacon[0]; + if (len > SIR_MAX_BEACON_SIZE - sizeof(uint32_t)) { + WMA_LOGE("%s: Received beacon len %u exceeding max limit %lu", + __func__, len, (unsigned long)( + SIR_MAX_BEACON_SIZE - sizeof(uint32_t))); + return QDF_STATUS_E_INVAL; + } + qdf_spin_lock_bh(&bcn->lock); + + /* + * Copy received beacon template content in local buffer. + * this will be send to target on the reception of SWBA + * event from target. + */ + qdf_nbuf_trim_tail(bcn->buf, qdf_nbuf_len(bcn->buf)); + memcpy(qdf_nbuf_data(bcn->buf), + bcn_info->beacon + 4 /* Exclude beacon length field */, + len); + if (bcn_info->timIeOffset > 3) + bcn->tim_ie_offset = bcn_info->timIeOffset - 4; + else + bcn->tim_ie_offset = bcn_info->timIeOffset; + + if (bcn_info->p2pIeOffset > 3) + bcn->p2p_ie_offset = bcn_info->p2pIeOffset - 4; + else + bcn->p2p_ie_offset = bcn_info->p2pIeOffset; + + bcn_payload = qdf_nbuf_data(bcn->buf); + if (bcn->tim_ie_offset) { + tim_ie = (struct beacon_tim_ie *) + (&bcn_payload[bcn->tim_ie_offset]); + /* + * Initial Value of bcn->dtim_count will be 0. + * But if the beacon gets updated then current dtim + * count will be restored + */ + tim_ie->dtim_count = bcn->dtim_count; + tim_ie->tim_bitctl = 0; + } + + qdf_nbuf_put_tail(bcn->buf, len); + bcn->len = len; + + qdf_spin_unlock_bh(&bcn->lock); + + return QDF_STATUS_SUCCESS; +} + +int wma_tbttoffset_update_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_TBTTOFFSET_UPDATE_EVENTID_param_tlvs *param_buf; + wmi_tbtt_offset_event_fixed_param *tbtt_offset_event; + struct wma_txrx_node *intf; + struct beacon_info *bcn; + tSendbeaconParams bcn_info; + uint32_t *adjusted_tsf = NULL; + uint32_t if_id = 0, vdev_map; + + if (!wma) { + WMA_LOGE("Invalid wma handle"); + return -EINVAL; + } + + param_buf = (WMI_TBTTOFFSET_UPDATE_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("Invalid tbtt update event buffer"); + return -EINVAL; + } + + tbtt_offset_event = param_buf->fixed_param; + intf = wma->interfaces; + vdev_map = tbtt_offset_event->vdev_map; + adjusted_tsf = param_buf->tbttoffset_list; + if (!adjusted_tsf) { + WMA_LOGE("%s: Invalid adjusted_tsf", __func__); + return -EINVAL; + } + + for (; (if_id < wma->max_bssid && vdev_map); vdev_map >>= 1, if_id++) { + if (!intf[if_id].vdev) + continue; + + if (!(vdev_map & 0x1)) + continue; + + bcn = intf[if_id].beacon; + if (!bcn) { + WMA_LOGE("%s: Invalid beacon", __func__); + return -EINVAL; + } + if (!bcn->buf) { + WMA_LOGE("%s: Invalid beacon buffer", __func__); + return -EINVAL; + } + /* Save the adjusted TSF */ + intf[if_id].tsfadjust = adjusted_tsf[if_id]; + + qdf_spin_lock_bh(&bcn->lock); + qdf_mem_zero(&bcn_info, sizeof(bcn_info)); + qdf_mem_copy(bcn_info.beacon, + qdf_nbuf_data(bcn->buf), bcn->len); + bcn_info.p2pIeOffset = bcn->p2p_ie_offset; + bcn_info.beaconLength = bcn->len; + bcn_info.timIeOffset = bcn->tim_ie_offset; + qdf_spin_unlock_bh(&bcn->lock); + + /* Update beacon template in firmware */ + wma_unified_bcn_tmpl_send(wma, if_id, &bcn_info, 0); + } + return 0; +} + +/** + * wma_p2p_go_set_beacon_ie() - set beacon IE for p2p go + * @wma_handle: wma handle + * @vdev_id: vdev id + * @p2pIe: p2p IE + * + * Return: 0 for success or error code + */ +static int wma_p2p_go_set_beacon_ie(t_wma_handle *wma_handle, + A_UINT32 vdev_id, uint8_t *p2pIe) +{ + if (!wma_handle) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_p2p_go_set_beacon_ie_cmd(wma_handle->wmi_handle, + vdev_id, p2pIe); +} + +/** + * wma_send_probe_rsp_tmpl() - send probe resp template + * @wma: wma handle + * @probe_rsp_info: probe response info + * + * This funciton sends probe response template to fw which + * firmware will use in case of probe response offload. + * + * Return: none + */ +void wma_send_probe_rsp_tmpl(tp_wma_handle wma, + tpSendProbeRespParams probe_rsp_info) +{ + uint8_t vdev_id; + struct sAniProbeRspStruct *probe_rsp; + + if (!probe_rsp_info) { + WMA_LOGE(FL("probe_rsp_info is NULL")); + return; + } + + probe_rsp = (struct sAniProbeRspStruct *) + (probe_rsp_info->probeRespTemplate); + if (!probe_rsp) { + WMA_LOGE(FL("probe_rsp is NULL")); + return; + } + + if (wma_find_vdev_id_by_addr(wma, probe_rsp->macHdr.sa, &vdev_id)) { + WMA_LOGE(FL("failed to get vdev id")); + return; + } + + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_beacon_offload)) { + wma_nofl_debug("Beacon Offload Enabled Sending Unified command"); + if (wmi_unified_probe_rsp_tmpl_send(wma, vdev_id, + probe_rsp_info) < 0) { + WMA_LOGE(FL("wmi_unified_probe_rsp_tmpl_send Failed ")); + return; + } + } +} + +QDF_STATUS wma_set_ap_vdev_up(tp_wma_handle wma, uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct vdev_mlme_obj *mlme_obj; + struct wlan_objmgr_vdev *vdev; + struct wma_txrx_node *iface; + + iface = &wma->interfaces[vdev_id]; + vdev = iface->vdev; + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + WMA_LOGE(FL("failed to get mlme_obj")); + return QDF_STATUS_E_INVAL; + } + mlme_obj->proto.sta.assoc_id = 0; + + status = vdev_mgr_up_send(mlme_obj); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE(FL("failed to send vdev up")); + return status; + } + wma_set_sap_keepalive(wma, vdev_id); + wma_set_vdev_mgmt_rate(wma, vdev_id); + + return status; +} + +/** + * wma_send_beacon() - send beacon template + * @wma: wma handle + * @bcn_info: beacon info + * + * This funciton store beacon template locally and + * update keep alive parameters + * + * Return: none + */ +void wma_send_beacon(tp_wma_handle wma, tpSendbeaconParams bcn_info) +{ + uint8_t vdev_id; + QDF_STATUS status; + uint8_t *p2p_ie; + struct sAniBeaconStruct *beacon; + + wma_nofl_debug("Beacon update reason %d", bcn_info->reason); + beacon = (struct sAniBeaconStruct *) (bcn_info->beacon); + if (wma_find_vdev_id_by_addr(wma, beacon->macHdr.sa, &vdev_id)) { + WMA_LOGE("%s : failed to get vdev id", __func__); + status = QDF_STATUS_E_INVAL; + goto send_rsp; + } + + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_beacon_offload)) { + status = wma_unified_bcn_tmpl_send(wma, vdev_id, bcn_info, 4); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s : wmi_unified_bcn_tmpl_send Failed ", + __func__); + goto send_rsp; + } + + if (bcn_info->p2pIeOffset) { + p2p_ie = bcn_info->beacon + bcn_info->p2pIeOffset; + wma_debug("p2pIe is present - vdev_id %hu, p2p_ie = %pK, p2p ie len = %hu", + vdev_id, p2p_ie, p2p_ie[1]); + if (wma_p2p_go_set_beacon_ie(wma, vdev_id, + p2p_ie) < 0) { + WMA_LOGE("%s : wmi_unified_bcn_tmpl_send Failed ", + __func__); + status = QDF_STATUS_E_INVAL; + goto send_rsp; + } + } + } + status = wma_store_bcn_tmpl(wma, vdev_id, bcn_info); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s : wma_store_bcn_tmpl Failed", __func__); + goto send_rsp; + } + +send_rsp: + bcn_info->status = status; + wma_send_msg(wma, WMA_SEND_BCN_RSP, (void *)bcn_info, 0); +} + +/** + * wma_set_keepalive_req() - send keep alive request to fw + * @wma: wma handle + * @keepalive: keep alive parameters + * + * Return: none + */ +void wma_set_keepalive_req(tp_wma_handle wma, + struct keep_alive_req *keepalive) +{ + wma_nofl_debug("KEEPALIVE:PacketType:%d", keepalive->packetType); + wma_set_sta_keep_alive(wma, keepalive->sessionId, + keepalive->packetType, + keepalive->timePeriod, + keepalive->hostIpv4Addr, + keepalive->destIpv4Addr, + keepalive->dest_macaddr.bytes); + + qdf_mem_free(keepalive); +} + +/** + * wma_beacon_miss_handler() - beacon miss event handler + * @wma: wma handle + * @vdev_id: vdev id + * @riis: rssi value + * + * This function send beacon miss indication to upper layers. + * + * Return: none + */ +void wma_beacon_miss_handler(tp_wma_handle wma, uint32_t vdev_id, int32_t rssi) +{ + struct missed_beacon_ind *beacon_miss_ind; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + beacon_miss_ind = qdf_mem_malloc(sizeof(*beacon_miss_ind)); + if (!beacon_miss_ind) + return; + + if (mac && mac->sme.tx_queue_cb) + mac->sme.tx_queue_cb(mac->hdd_handle, vdev_id, + WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + beacon_miss_ind->messageType = WMA_MISSED_BEACON_IND; + beacon_miss_ind->length = sizeof(*beacon_miss_ind); + beacon_miss_ind->bss_idx = vdev_id; + + wma_send_msg(wma, WMA_MISSED_BEACON_IND, beacon_miss_ind, 0); + if (!wmi_service_enabled(wma->wmi_handle, + wmi_service_hw_db2dbm_support)) + rssi += WMA_TGT_NOISE_FLOOR_DBM; + wma_lost_link_info_handler(wma, vdev_id, rssi); +} + +/** + * wma_get_status_str() - get string of tx status from firmware + * @status: tx status + * + * Return: converted string of tx status + */ +static const char *wma_get_status_str(uint32_t status) +{ + switch (status) { + default: + return "unknown"; + CASE_RETURN_STRING(WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK); + CASE_RETURN_STRING(WMI_MGMT_TX_COMP_TYPE_DISCARD); + CASE_RETURN_STRING(WMI_MGMT_TX_COMP_TYPE_INSPECT); + CASE_RETURN_STRING(WMI_MGMT_TX_COMP_TYPE_COMPLETE_NO_ACK); + CASE_RETURN_STRING(WMI_MGMT_TX_COMP_TYPE_MAX); + } +} + +#ifdef CONFIG_HL_SUPPORT +static inline void wma_mgmt_unmap_buf(tp_wma_handle wma_handle, qdf_nbuf_t buf) +{ +} +#else +static inline void wma_mgmt_unmap_buf(tp_wma_handle wma_handle, qdf_nbuf_t buf) +{ + qdf_nbuf_unmap_single(wma_handle->qdf_dev, buf, QDF_DMA_TO_DEVICE); +} +#endif + +#if !defined(REMOVE_PKT_LOG) +/** + * wma_mgmt_pktdump_status_map() - map MGMT Tx completion status with + * packet dump Tx status + * @status: MGMT Tx completion status + * + * Return: packet dump tx_status enum + */ +static inline enum tx_status +wma_mgmt_pktdump_status_map(WMI_MGMT_TX_COMP_STATUS_TYPE status) +{ + enum tx_status pktdump_status; + + switch (status) { + case WMI_MGMT_TX_COMP_TYPE_COMPLETE_OK: + pktdump_status = tx_status_ok; + break; + case WMI_MGMT_TX_COMP_TYPE_DISCARD: + pktdump_status = tx_status_discard; + break; + case WMI_MGMT_TX_COMP_TYPE_COMPLETE_NO_ACK: + pktdump_status = tx_status_no_ack; + break; + default: + pktdump_status = tx_status_discard; + break; + } + return pktdump_status; +} +#endif + +/** + * wma_process_mgmt_tx_completion() - process mgmt completion + * @wma_handle: wma handle + * @desc_id: descriptor id + * @status: status + * + * Return: 0 for success or error code + */ +static int wma_process_mgmt_tx_completion(tp_wma_handle wma_handle, + uint32_t desc_id, uint32_t status) +{ + struct wlan_objmgr_pdev *pdev; + qdf_nbuf_t buf = NULL; + QDF_STATUS ret; +#if !defined(REMOVE_PKT_LOG) + uint8_t vdev_id = 0; + ol_txrx_pktdump_cb packetdump_cb; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + enum tx_status pktdump_status; +#endif + struct wmi_mgmt_params mgmt_params = {}; + + if (!wma_handle) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return -EINVAL; + } + + wma_debug("status: %s wmi_desc_id: %d", + wma_get_status_str(status), desc_id); + + pdev = wma_handle->pdev; + if (!pdev) { + WMA_LOGE("%s: psoc ptr is NULL", __func__); + return -EINVAL; + } + + buf = mgmt_txrx_get_nbuf(pdev, desc_id); + + + if (buf) + wma_mgmt_unmap_buf(wma_handle, buf); + +#if !defined(REMOVE_PKT_LOG) + vdev_id = mgmt_txrx_get_vdev_id(pdev, desc_id); + mgmt_params.vdev_id = vdev_id; + packetdump_cb = wma_handle->wma_mgmt_tx_packetdump_cb; + pktdump_status = wma_mgmt_pktdump_status_map(status); + if (packetdump_cb) + packetdump_cb(soc, WMI_PDEV_ID_SOC, vdev_id, + buf, pktdump_status, TX_MGMT_PKT); +#endif + + ret = mgmt_txrx_tx_completion_handler(pdev, desc_id, status, + &mgmt_params); + + if (ret != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Failed to process mgmt tx completion", __func__); + return -EINVAL; + } + + return 0; +} + +/** + * wma_mgmt_tx_completion_handler() - wma mgmt Tx completion event handler + * @handle: wma handle + * @cmpl_event_params: completion event handler data + * @len: length of @cmpl_event_params + * + * Return: 0 on success; error number otherwise + */ + +int wma_mgmt_tx_completion_handler(void *handle, uint8_t *cmpl_event_params, + uint32_t len) +{ + tp_wma_handle wma_handle = (tp_wma_handle)handle; + WMI_MGMT_TX_COMPLETION_EVENTID_param_tlvs *param_buf; + wmi_mgmt_tx_compl_event_fixed_param *cmpl_params; + + param_buf = (WMI_MGMT_TX_COMPLETION_EVENTID_param_tlvs *) + cmpl_event_params; + if (!param_buf || !wma_handle) { + WMA_LOGE("%s: Invalid mgmt Tx completion event", __func__); + return -EINVAL; + } + cmpl_params = param_buf->fixed_param; + + wma_process_mgmt_tx_completion(wma_handle, + cmpl_params->desc_id, cmpl_params->status); + + return 0; +} + +/** + * wma_mgmt_tx_bundle_completion_handler() - mgmt bundle comp handler + * @handle: wma handle + * @buf: buffer + * @len: length + * + * Return: 0 for success or error code + */ +int wma_mgmt_tx_bundle_completion_handler(void *handle, uint8_t *buf, + uint32_t len) +{ + tp_wma_handle wma_handle = (tp_wma_handle)handle; + WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID_param_tlvs *param_buf; + wmi_mgmt_tx_compl_bundle_event_fixed_param *cmpl_params; + uint32_t num_reports; + uint32_t *desc_ids; + uint32_t *status; + uint32_t i, buf_len; + bool excess_data = false; + + param_buf = (WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID_param_tlvs *)buf; + if (!param_buf || !wma_handle) { + WMA_LOGE("%s: Invalid mgmt Tx completion event", __func__); + return -EINVAL; + } + cmpl_params = param_buf->fixed_param; + num_reports = cmpl_params->num_reports; + desc_ids = (uint32_t *)(param_buf->desc_ids); + status = (uint32_t *)(param_buf->status); + + /* buf contains num_reports * sizeof(uint32) len of desc_ids and + * num_reports * sizeof(uint32) status, + * so (2 x (num_reports * sizeof(uint32)) should not exceed MAX + */ + if (cmpl_params->num_reports > (WMI_SVC_MSG_MAX_SIZE / + (2 * sizeof(uint32_t)))) + excess_data = true; + else + buf_len = cmpl_params->num_reports * (2 * sizeof(uint32_t)); + + if (excess_data || (sizeof(*cmpl_params) > (WMI_SVC_MSG_MAX_SIZE - + buf_len))) { + WMA_LOGE("excess wmi buffer: num_reports %d", + cmpl_params->num_reports); + return -EINVAL; + } + + if ((cmpl_params->num_reports > param_buf->num_desc_ids) || + (cmpl_params->num_reports > param_buf->num_status)) { + WMA_LOGE("Invalid num_reports %d, num_desc_ids %d, num_status %d", + cmpl_params->num_reports, param_buf->num_desc_ids, + param_buf->num_status); + return -EINVAL; + } + + for (i = 0; i < num_reports; i++) + wma_process_mgmt_tx_completion(wma_handle, + desc_ids[i], status[i]); + return 0; +} + +/** + * wma_process_update_opmode() - process update VHT opmode cmd from UMAC + * @wma_handle: wma handle + * @update_vht_opmode: vht opmode + * + * Return: none + */ +void wma_process_update_opmode(tp_wma_handle wma_handle, + tUpdateVHTOpMode *update_vht_opmode) +{ + wmi_host_channel_width ch_width; + uint8_t pdev_id; + struct wlan_objmgr_peer *peer; + struct wlan_objmgr_psoc *psoc = wma_handle->psoc; + enum wlan_phymode peer_phymode; + uint32_t fw_phymode; + enum wlan_peer_type peer_type; + + pdev_id = wlan_objmgr_pdev_get_pdev_id(wma_handle->pdev); + peer = wlan_objmgr_get_peer(psoc, pdev_id, + update_vht_opmode->peer_mac, + WLAN_LEGACY_WMA_ID); + if (!peer) { + WMA_LOGE("peer object invalid"); + return; + } + + peer_type = wlan_peer_get_peer_type(peer); + if (peer_type == WLAN_PEER_SELF) { + WMA_LOGE("self peer wrongly used"); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + return; + } + + wlan_peer_obj_lock(peer); + peer_phymode = wlan_peer_get_phymode(peer); + wlan_peer_obj_unlock(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + + fw_phymode = wma_host_to_fw_phymode(peer_phymode); + + ch_width = wmi_get_ch_width_from_phy_mode(wma_handle->wmi_handle, + fw_phymode); + wma_debug("ch_width: %d, fw phymode: %d peer_phymode %d", + ch_width, fw_phymode, peer_phymode); + if (ch_width < update_vht_opmode->opMode) { + WMA_LOGE("%s: Invalid peer bw update %d, self bw %d", + __func__, update_vht_opmode->opMode, + ch_width); + return; + } + + wma_debug("opMode = %d", update_vht_opmode->opMode); + wma_set_peer_param(wma_handle, update_vht_opmode->peer_mac, + WMI_PEER_CHWIDTH, update_vht_opmode->opMode, + update_vht_opmode->smesessionId); +} + +/** + * wma_process_update_rx_nss() - process update RX NSS cmd from UMAC + * @wma_handle: wma handle + * @update_rx_nss: rx nss value + * + * Return: none + */ +void wma_process_update_rx_nss(tp_wma_handle wma_handle, + tUpdateRxNss *update_rx_nss) +{ + struct target_psoc_info *tgt_hdl; + struct wma_txrx_node *intr = + &wma_handle->interfaces[update_rx_nss->smesessionId]; + int rx_nss = update_rx_nss->rxNss; + int num_rf_chains; + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); + if (!tgt_hdl) { + WMA_LOGE("%s: target psoc info is NULL", __func__); + return; + } + + num_rf_chains = target_if_get_num_rf_chains(tgt_hdl); + if (rx_nss > num_rf_chains || rx_nss > WMA_MAX_NSS) + rx_nss = QDF_MIN(num_rf_chains, WMA_MAX_NSS); + + intr->nss = (uint8_t)rx_nss; + update_rx_nss->rxNss = (uint32_t)rx_nss; + + wma_debug("Rx Nss = %d", update_rx_nss->rxNss); + + wma_set_peer_param(wma_handle, update_rx_nss->peer_mac, + WMI_PEER_NSS, update_rx_nss->rxNss, + update_rx_nss->smesessionId); +} + +/** + * wma_process_update_membership() - process update group membership cmd + * @wma_handle: wma handle + * @membership: group membership info + * + * Return: none + */ +void wma_process_update_membership(tp_wma_handle wma_handle, + tUpdateMembership *membership) +{ + wma_debug("membership = %x ", membership->membership); + + wma_set_peer_param(wma_handle, membership->peer_mac, + WMI_PEER_MEMBERSHIP, membership->membership, + membership->smesessionId); +} + +/** + * wma_process_update_userpos() - process update user pos cmd from UMAC + * @wma_handle: wma handle + * @userpos: user pos value + * + * Return: none + */ +void wma_process_update_userpos(tp_wma_handle wma_handle, + tUpdateUserPos *userpos) +{ + wma_debug("userPos = %x ", userpos->userPos); + + wma_set_peer_param(wma_handle, userpos->peer_mac, + WMI_PEER_USERPOS, userpos->userPos, + userpos->smesessionId); + + /* Now that membership/userpos is updated in fw, + * enable GID PPS. + */ + wma_set_ppsconfig(userpos->smesessionId, WMA_VHT_PPS_GID_MATCH, 1); + +} + +QDF_STATUS wma_set_cts2self_for_p2p_go(void *wma_handle, + uint32_t cts2self_for_p2p_go) +{ + int32_t ret; + tp_wma_handle wma = (tp_wma_handle)wma_handle; + struct pdev_params pdevparam; + + pdevparam.param_id = WMI_PDEV_PARAM_CTS2SELF_FOR_P2P_GO_CONFIG; + pdevparam.param_value = cts2self_for_p2p_go; + + ret = wmi_unified_pdev_param_send(wma->wmi_handle, + &pdevparam, + WMA_WILDCARD_PDEV_ID); + if (ret) { + WMA_LOGE("Fail to Set CTS2SELF for p2p GO %d", + cts2self_for_p2p_go); + return QDF_STATUS_E_FAILURE; + } + + wma_nofl_debug("Successfully Set CTS2SELF for p2p GO %d", + cts2self_for_p2p_go); + + return QDF_STATUS_SUCCESS; +} + + +/** + * wma_set_htconfig() - set ht config parameters to target + * @vdev_id: vdev id + * @ht_capab: ht capablity + * @value: value of ht param + * + * Return: QDF status + */ +QDF_STATUS wma_set_htconfig(uint8_t vdev_id, uint16_t ht_capab, int value) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + QDF_STATUS ret = QDF_STATUS_E_FAILURE; + + if (!wma) { + WMA_LOGE("%s: Failed to get wma", __func__); + return QDF_STATUS_E_INVAL; + } + + switch (ht_capab) { + case WNI_CFG_HT_CAP_INFO_ADVANCE_CODING: + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_LDPC, + value); + break; + case WNI_CFG_HT_CAP_INFO_TX_STBC: + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_TX_STBC, + value); + break; + case WNI_CFG_HT_CAP_INFO_RX_STBC: + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_RX_STBC, + value); + break; + case WNI_CFG_HT_CAP_INFO_SHORT_GI_20MHZ: + case WNI_CFG_HT_CAP_INFO_SHORT_GI_40MHZ: + WMA_LOGE("%s: ht_capab = %d, value = %d", __func__, ht_capab, + value); + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_SGI, value); + if (ret == QDF_STATUS_SUCCESS) + wma->interfaces[vdev_id].config.shortgi = value; + break; + default: + WMA_LOGE("%s:INVALID HT CONFIG", __func__); + } + + return ret; +} + +#ifdef WLAN_FEATURE_11W + +/** + * wma_extract_ccmp_pn() - extract 6 byte PN from the CCMP header + * @ccmp_ptr: CCMP header + * + * Return: PN extracted from header. + */ +static uint64_t wma_extract_ccmp_pn(uint8_t *ccmp_ptr) +{ + uint8_t rsvd, key, pn[6]; + uint64_t new_pn; + + /* + * +-----+-----+------+----------+-----+-----+-----+-----+ + * | PN0 | PN1 | rsvd | rsvd/key | PN2 | PN3 | PN4 | PN5 | + * +-----+-----+------+----------+-----+-----+-----+-----+ + * CCMP Header Format + */ + + /* Extract individual bytes */ + pn[0] = (uint8_t) *ccmp_ptr; + pn[1] = (uint8_t) *(ccmp_ptr + 1); + rsvd = (uint8_t) *(ccmp_ptr + 2); + key = (uint8_t) *(ccmp_ptr + 3); + pn[2] = (uint8_t) *(ccmp_ptr + 4); + pn[3] = (uint8_t) *(ccmp_ptr + 5); + pn[4] = (uint8_t) *(ccmp_ptr + 6); + pn[5] = (uint8_t) *(ccmp_ptr + 7); + + /* Form 6 byte PN with 6 individual bytes of PN */ + new_pn = ((uint64_t) pn[5] << 40) | + ((uint64_t) pn[4] << 32) | + ((uint64_t) pn[3] << 24) | + ((uint64_t) pn[2] << 16) | + ((uint64_t) pn[1] << 8) | ((uint64_t) pn[0] << 0); + + return new_pn; +} + +/** + * wma_is_ccmp_pn_replay_attack() - detect replay attacking using PN in CCMP + * @wma: wma context + * @wh: 802.11 frame header + * @ccmp_ptr: CCMP frame header + * + * Return: true/false + */ +static bool +wma_is_ccmp_pn_replay_attack(tp_wma_handle wma, struct ieee80211_frame *wh, + uint8_t *ccmp_ptr) +{ + uint64_t new_pn; + bool ret = false; + struct peer_mlme_priv_obj *peer_priv; + struct wlan_objmgr_peer *peer; + + new_pn = wma_extract_ccmp_pn(ccmp_ptr); + + peer = wlan_objmgr_get_peer_by_mac(wma->psoc, wh->i_addr2, + WLAN_LEGACY_WMA_ID); + if (!peer) + return ret; + + peer_priv = wlan_objmgr_peer_get_comp_private_obj(peer, + WLAN_UMAC_COMP_MLME); + if (!peer_priv) { + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + return ret; + } + + if (peer_priv->last_pn_valid) { + if (new_pn > peer_priv->last_pn) { + peer_priv->last_pn = new_pn; + } else { + wma_err_rl("PN Replay attack detected"); + /* per 11W amendment, keeping track of replay attacks */ + peer_priv->rmf_pn_replays += 1; + ret = true; + } + } else { + peer_priv->last_pn_valid = 1; + peer_priv->last_pn = new_pn; + } + + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + + return ret; +} + +#ifdef WLAN_FEATURE_11W +/** + * wma_process_bip() - process mmie in rmf frame + * @wma_handle: wma handle + * @iface: txrx node + * @wh: 80211 frame + * @wbuf: Buffer + * + * Return: 0 for success or error code + */ + +static +int wma_process_bip(tp_wma_handle wma_handle, + struct wma_txrx_node *iface, + struct ieee80211_frame *wh, + qdf_nbuf_t wbuf +) +{ + uint16_t mmie_size; + uint16_t key_id; + uint8_t *efrm; + uint8_t *igtk; + uint16_t key_len; + + efrm = qdf_nbuf_data(wbuf) + qdf_nbuf_len(wbuf); + + if (iface->key.key_cipher == WMI_CIPHER_AES_CMAC) { + mmie_size = cds_get_mmie_size(); + } else if (iface->key.key_cipher == WMI_CIPHER_AES_GMAC || + iface->key.key_cipher == WMI_CIPHER_BIP_GMAC_256) { + mmie_size = cds_get_gmac_mmie_size(); + } else { + WMA_LOGE(FL("Invalid key cipher %d"), iface->key.key_cipher); + return -EINVAL; + } + + /* Check if frame is invalid length */ + if (efrm - (uint8_t *)wh < sizeof(*wh) + mmie_size) { + WMA_LOGE(FL("Invalid frame length")); + return -EINVAL; + } + + key_id = (uint16_t)*(efrm - mmie_size + 2); + if (!((key_id == WMA_IGTK_KEY_INDEX_4) + || (key_id == WMA_IGTK_KEY_INDEX_5))) { + WMA_LOGE(FL("Invalid KeyID(%d) dropping the frame"), key_id); + return -EINVAL; + } + + wma_debug("key_cipher %d key_id %d", iface->key.key_cipher, key_id); + + igtk = wma_get_igtk(iface, &key_len, key_id); + switch (iface->key.key_cipher) { + case WMI_CIPHER_AES_CMAC: + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_sta_pmf_offload)) { + /* + * if 11w offload is enabled then mmie validation is + * performed in firmware, host just need to trim the + * mmie. + */ + qdf_nbuf_trim_tail(wbuf, cds_get_mmie_size()); + } else { + if (cds_is_mmie_valid(igtk, iface->key.key_id[ + key_id - + WMA_IGTK_KEY_INDEX_4].ipn, + (uint8_t *)wh, efrm)) { + wma_debug("Protected BC/MC frame MMIE validation successful"); + /* Remove MMIE */ + qdf_nbuf_trim_tail(wbuf, cds_get_mmie_size()); + } else { + wma_debug("BC/MC MIC error or MMIE not present, dropping the frame"); + return -EINVAL; + } + } + break; + + case WMI_CIPHER_AES_GMAC: + case WMI_CIPHER_BIP_GMAC_256: + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_gmac_offload_support)) { + /* + * if gmac offload is enabled then mmie validation is + * performed in firmware, host just need to trim the + * mmie. + */ + wma_debug("Trim GMAC MMIE"); + qdf_nbuf_trim_tail(wbuf, cds_get_gmac_mmie_size()); + } else { + if (cds_is_gmac_mmie_valid(igtk, + iface->key.key_id[key_id - WMA_IGTK_KEY_INDEX_4].ipn, + (uint8_t *) wh, efrm, key_len)) { + wma_debug("Protected BC/MC frame GMAC MMIE validation successful"); + /* Remove MMIE */ + qdf_nbuf_trim_tail(wbuf, + cds_get_gmac_mmie_size()); + } else { + wma_debug("BC/MC GMAC MIC error or MMIE not present, dropping the frame"); + return -EINVAL; + } + } + break; + + default: + WMA_LOGE(FL("Unsupported key cipher %d"), + iface->key.key_cipher); + } + + + return 0; +} +#endif + +/** + * wma_process_rmf_frame() - process rmf frame + * @wma_handle: wma handle + * @iface: txrx node + * @wh: 80211 frame + * @rx_pkt: rx packet + * @wbuf: Buffer + * + * Return: 0 for success or error code + */ +static +int wma_process_rmf_frame(tp_wma_handle wma_handle, + struct wma_txrx_node *iface, + struct ieee80211_frame *wh, + cds_pkt_t *rx_pkt, + qdf_nbuf_t wbuf) +{ + uint8_t *orig_hdr; + uint8_t *ccmp; + uint8_t mic_len, hdr_len, pdev_id; + QDF_STATUS status; + + if ((wh)->i_fc[1] & IEEE80211_FC1_WEP) { + if (QDF_IS_ADDR_BROADCAST(wh->i_addr1) || + IEEE80211_IS_MULTICAST(wh->i_addr1)) { + WMA_LOGE("Encrypted BC/MC frame dropping the frame"); + cds_pkt_return_packet(rx_pkt); + return -EINVAL; + } + + if (iface->type == WMI_VDEV_TYPE_NDI) { + hdr_len = IEEE80211_CCMP_HEADERLEN; + mic_len = IEEE80211_CCMP_MICLEN; + } else { + pdev_id = + wlan_objmgr_pdev_get_pdev_id(wma_handle->pdev); + status = mlme_get_peer_mic_len(wma_handle->psoc, + pdev_id, wh->i_addr2, + &mic_len, &hdr_len); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("Failed to get mic hdr and length"); + cds_pkt_return_packet(rx_pkt); + return -EINVAL; + } + } + + if (qdf_nbuf_len(wbuf) < (sizeof(*wh) + hdr_len + mic_len)) { + WMA_LOGE("Buffer length less than expected %d", + (int)qdf_nbuf_len(wbuf)); + cds_pkt_return_packet(rx_pkt); + return -EINVAL; + } + + orig_hdr = (uint8_t *) qdf_nbuf_data(wbuf); + /* Pointer to head of CCMP header */ + ccmp = orig_hdr + sizeof(*wh); + if (wma_is_ccmp_pn_replay_attack(wma_handle, wh, ccmp)) { + wma_err_rl("Dropping the frame"); + cds_pkt_return_packet(rx_pkt); + return -EINVAL; + } + + /* Strip privacy headers (and trailer) + * for a received frame + */ + qdf_mem_move(orig_hdr + + hdr_len, wh, + sizeof(*wh)); + qdf_nbuf_pull_head(wbuf, + hdr_len); + qdf_nbuf_trim_tail(wbuf, mic_len); + /* + * CCMP header has been pulled off + * reinitialize the start pointer of mac header + * to avoid accessing incorrect address + */ + wh = (struct ieee80211_frame *) qdf_nbuf_data(wbuf); + rx_pkt->pkt_meta.mpdu_hdr_ptr = + qdf_nbuf_data(wbuf); + rx_pkt->pkt_meta.mpdu_len = qdf_nbuf_len(wbuf); + rx_pkt->pkt_buf = wbuf; + if (rx_pkt->pkt_meta.mpdu_len >= + rx_pkt->pkt_meta.mpdu_hdr_len) { + rx_pkt->pkt_meta.mpdu_data_len = + rx_pkt->pkt_meta.mpdu_len - + rx_pkt->pkt_meta.mpdu_hdr_len; + } else { + WMA_LOGE("mpdu len %d less than hdr %d, dropping frame", + rx_pkt->pkt_meta.mpdu_len, + rx_pkt->pkt_meta.mpdu_hdr_len); + cds_pkt_return_packet(rx_pkt); + return -EINVAL; + } + + if (rx_pkt->pkt_meta.mpdu_data_len > WMA_MAX_MGMT_MPDU_LEN) { + WMA_LOGE("Data Len %d greater than max, dropping frame", + rx_pkt->pkt_meta.mpdu_data_len); + cds_pkt_return_packet(rx_pkt); + return -EINVAL; + } + rx_pkt->pkt_meta.mpdu_data_ptr = + rx_pkt->pkt_meta.mpdu_hdr_ptr + + rx_pkt->pkt_meta.mpdu_hdr_len; + wma_debug("BSSID: "QDF_MAC_ADDR_FMT" tsf_delta: %u", + QDF_MAC_ADDR_REF(wh->i_addr3), + rx_pkt->pkt_meta.tsf_delta); + } else { + if (QDF_IS_ADDR_BROADCAST(wh->i_addr1) || + IEEE80211_IS_MULTICAST(wh->i_addr1)) { + if (0 != wma_process_bip(wma_handle, iface, wh, wbuf)) { + cds_pkt_return_packet(rx_pkt); + return -EINVAL; + } + } else { + WMA_LOGE("Rx unprotected unicast mgmt frame"); + rx_pkt->pkt_meta.dpuFeedback = + DPU_FEEDBACK_UNPROTECTED_ERROR; + } + } + return 0; +} + +/** + * wma_get_peer_pmf_status() - Get the PMF capability of peer + * @wma: wma handle + * @peer_mac: peer mac addr + * + * Return: True if PMF is enabled, false otherwise. + */ +static bool +wma_get_peer_pmf_status(tp_wma_handle wma, uint8_t *peer_mac) +{ + struct wlan_objmgr_peer *peer; + bool is_pmf_enabled; + + if (!peer_mac) { + WMA_LOGE("peer_mac is NULL"); + return false; + } + + peer = wlan_objmgr_get_peer(wma->psoc, + wlan_objmgr_pdev_get_pdev_id(wma->pdev), + peer_mac, WLAN_LEGACY_WMA_ID); + if (!peer) { + WMA_LOGE("Peer of peer_mac "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(peer_mac)); + return false; + } + is_pmf_enabled = mlme_get_peer_pmf_status(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + wma_nofl_debug("get is_pmf_enabled %d for "QDF_MAC_ADDR_FMT, + is_pmf_enabled, QDF_MAC_ADDR_REF(peer_mac)); + + return is_pmf_enabled; +} + +/** + * wma_check_and_process_rmf_frame() - Process the frame if it is of rmf type + * @wma_handle: wma handle + * @vdev_id: vdev id + * @wh: double pointer to 802.11 frame header which will be updated if the + * frame is of rmf type. + * @rx_pkt: rx packet + * @buf: Buffer + * + * Process the frame as rmf frame only if both DUT and peer are of PMF capable + * + * Return: 0 for success or error code + */ +static int +wma_check_and_process_rmf_frame(tp_wma_handle wma_handle, + uint8_t vdev_id, + struct ieee80211_frame **wh, + cds_pkt_t *rx_pkt, + qdf_nbuf_t buf) +{ + int status; + struct wma_txrx_node *iface; + struct ieee80211_frame *hdr = *wh; + + iface = &(wma_handle->interfaces[vdev_id]); + if (iface->type != WMI_VDEV_TYPE_NDI && !iface->rmfEnabled) + return 0; + + if (qdf_is_macaddr_group((struct qdf_mac_addr *)(hdr->i_addr1)) || + qdf_is_macaddr_broadcast((struct qdf_mac_addr *)(hdr->i_addr1)) || + wma_get_peer_pmf_status(wma_handle, hdr->i_addr2) || + (iface->type == WMI_VDEV_TYPE_NDI && + (hdr->i_fc[1] & IEEE80211_FC1_WEP))) { + status = wma_process_rmf_frame(wma_handle, iface, hdr, + rx_pkt, buf); + if (status) + return status; + /* + * CCMP header might have been pulled off reinitialize the + * start pointer of mac header + */ + *wh = (struct ieee80211_frame *)qdf_nbuf_data(buf); + } + + return 0; +} +#else +static inline int +wma_check_and_process_rmf_frame(tp_wma_handle wma_handle, + uint8_t vdev_id, + struct ieee80211_frame **wh, + cds_pkt_t *rx_pkt, + qdf_nbuf_t buf) +{ + return 0; +} +#endif + +/** + * wma_is_pkt_drop_candidate() - check if the mgmt frame should be droppped + * @wma_handle: wma handle + * @peer_addr: peer MAC address + * @bssid: BSSID Address + * @subtype: Management frame subtype + * + * This function is used to decide if a particular management frame should be + * dropped to prevent DOS attack. Timestamp is used to decide the DOS attack. + * + * Return: true if the packet should be dropped and false oterwise + */ +static bool wma_is_pkt_drop_candidate(tp_wma_handle wma_handle, + uint8_t *peer_addr, uint8_t *bssid, + uint8_t subtype) +{ + bool should_drop = false; + uint8_t nan_addr[] = {0x50, 0x6F, 0x9A, 0x01, 0x00, 0x00}; + + /* Drop the beacons from NAN device */ + if ((subtype == MGMT_SUBTYPE_BEACON) && + (!qdf_mem_cmp(nan_addr, bssid, NAN_CLUSTER_ID_BYTES))) { + should_drop = true; + goto end; + } +end: + return should_drop; +} + +#define RATE_LIMIT 16 + +int wma_form_rx_packet(qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + cds_pkt_t *rx_pkt) +{ + uint8_t vdev_id = WMA_INVALID_VDEV_ID; + struct ieee80211_frame *wh; + uint8_t mgt_type, mgt_subtype; + int status; + tp_wma_handle wma_handle = (tp_wma_handle) + cds_get_context(QDF_MODULE_ID_WMA); +#if !defined(REMOVE_PKT_LOG) + ol_txrx_pktdump_cb packetdump_cb; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); +#endif + static uint8_t limit_prints_invalid_len = RATE_LIMIT - 1; + static uint8_t limit_prints_load_unload = RATE_LIMIT - 1; + static uint8_t limit_prints_recovery = RATE_LIMIT - 1; + + if (!wma_handle) { + WMA_LOGE(FL("wma handle is NULL")); + qdf_nbuf_free(buf); + qdf_mem_free(rx_pkt); + return -EINVAL; + } + + if (!mgmt_rx_params) { + limit_prints_invalid_len++; + if (limit_prints_invalid_len == RATE_LIMIT) { + wma_debug("mgmt rx params is NULL"); + limit_prints_invalid_len = 0; + } + qdf_nbuf_free(buf); + qdf_mem_free(rx_pkt); + return -EINVAL; + } + + if (cds_is_load_or_unload_in_progress()) { + limit_prints_load_unload++; + if (limit_prints_load_unload == RATE_LIMIT) { + wma_debug("Load/Unload in progress"); + limit_prints_load_unload = 0; + } + qdf_nbuf_free(buf); + qdf_mem_free(rx_pkt); + return -EINVAL; + } + + if (cds_is_driver_recovering()) { + limit_prints_recovery++; + if (limit_prints_recovery == RATE_LIMIT) { + wma_debug("Recovery in progress"); + limit_prints_recovery = 0; + } + qdf_nbuf_free(buf); + qdf_mem_free(rx_pkt); + return -EINVAL; + } + + if (cds_is_driver_in_bad_state()) { + limit_prints_recovery++; + if (limit_prints_recovery == RATE_LIMIT) { + wma_debug("Driver in bad state"); + limit_prints_recovery = 0; + } + qdf_nbuf_free(buf); + qdf_mem_free(rx_pkt); + return -EINVAL; + } + + /* + * Fill in meta information needed by pe/lim + * TODO: Try to maintain rx metainfo as part of skb->data. + */ + rx_pkt->pkt_meta.frequency = mgmt_rx_params->chan_freq; + rx_pkt->pkt_meta.scan_src = mgmt_rx_params->flags; + + /* + * Get the rssi value from the current snr value + * using standard noise floor of -96. + */ + rx_pkt->pkt_meta.rssi = mgmt_rx_params->snr + + WMA_NOISE_FLOOR_DBM_DEFAULT; + rx_pkt->pkt_meta.snr = mgmt_rx_params->snr; + + /* If absolute rssi is available from firmware, use it */ + if (mgmt_rx_params->rssi != 0) + rx_pkt->pkt_meta.rssi_raw = mgmt_rx_params->rssi; + else + rx_pkt->pkt_meta.rssi_raw = rx_pkt->pkt_meta.rssi; + + + /* + * FIXME: Assigning the local timestamp as hw timestamp is not + * available. Need to see if pe/lim really uses this data. + */ + rx_pkt->pkt_meta.timestamp = (uint32_t) jiffies; + rx_pkt->pkt_meta.mpdu_hdr_len = sizeof(struct ieee80211_frame); + rx_pkt->pkt_meta.mpdu_len = mgmt_rx_params->buf_len; + + /* + * The buf_len should be at least 802.11 header len + */ + if (mgmt_rx_params->buf_len < rx_pkt->pkt_meta.mpdu_hdr_len) { + WMA_LOGE("MPDU Len %d lesser than header len %d", + mgmt_rx_params->buf_len, + rx_pkt->pkt_meta.mpdu_hdr_len); + qdf_nbuf_free(buf); + qdf_mem_free(rx_pkt); + return -EINVAL; + } + + rx_pkt->pkt_meta.mpdu_data_len = mgmt_rx_params->buf_len - + rx_pkt->pkt_meta.mpdu_hdr_len; + + rx_pkt->pkt_meta.roamCandidateInd = 0; + + wh = (struct ieee80211_frame *)qdf_nbuf_data(buf); + + /* + * If the mpdu_data_len is greater than Max (2k), drop the frame + */ + if (rx_pkt->pkt_meta.mpdu_data_len > WMA_MAX_MGMT_MPDU_LEN) { + wma_err("Data Len %d greater than max, dropping frame from "QDF_MAC_ADDR_FMT, + rx_pkt->pkt_meta.mpdu_data_len, + QDF_MAC_ADDR_REF(wh->i_addr3)); + qdf_nbuf_free(buf); + qdf_mem_free(rx_pkt); + return -EINVAL; + } + + rx_pkt->pkt_meta.mpdu_hdr_ptr = qdf_nbuf_data(buf); + rx_pkt->pkt_meta.mpdu_data_ptr = rx_pkt->pkt_meta.mpdu_hdr_ptr + + rx_pkt->pkt_meta.mpdu_hdr_len; + rx_pkt->pkt_meta.tsf_delta = mgmt_rx_params->tsf_delta; + rx_pkt->pkt_buf = buf; + + /* If it is a beacon/probe response, save it for future use */ + mgt_type = (wh)->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + mgt_subtype = (wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + if (mgt_type == IEEE80211_FC0_TYPE_MGT && + (mgt_subtype == MGMT_SUBTYPE_DISASSOC || + mgt_subtype == MGMT_SUBTYPE_DEAUTH || + mgt_subtype == MGMT_SUBTYPE_ACTION)) { + if (wma_find_vdev_id_by_bssid(wma_handle, wh->i_addr3, + &vdev_id) == QDF_STATUS_SUCCESS) { + status = wma_check_and_process_rmf_frame(wma_handle, + vdev_id, + &wh, + rx_pkt, + buf); + if (status) + return status; + } else if (wma_find_vdev_id_by_addr(wma_handle, wh->i_addr1, + &vdev_id)) { + status = wma_check_and_process_rmf_frame(wma_handle, + vdev_id, + &wh, + rx_pkt, + buf); + if (status) + return status; + } + } + + rx_pkt->pkt_meta.session_id = + (vdev_id == WMA_INVALID_VDEV_ID ? 0 : vdev_id); + + if (mgt_type == IEEE80211_FC0_TYPE_MGT && + (mgt_subtype == MGMT_SUBTYPE_BEACON || + mgt_subtype == MGMT_SUBTYPE_PROBE_RESP)) { + if (mgmt_rx_params->buf_len <= + (sizeof(struct ieee80211_frame) + + offsetof(struct wlan_bcn_frame, ie))) { + wma_debug("Dropping frame from "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(wh->i_addr3)); + cds_pkt_return_packet(rx_pkt); + return -EINVAL; + } + } + + if (wma_is_pkt_drop_candidate(wma_handle, wh->i_addr2, wh->i_addr3, + mgt_subtype)) { + cds_pkt_return_packet(rx_pkt); + return -EINVAL; + } + +#if !defined(REMOVE_PKT_LOG) + packetdump_cb = wma_handle->wma_mgmt_rx_packetdump_cb; + if ((mgt_type == IEEE80211_FC0_TYPE_MGT && + mgt_subtype != MGMT_SUBTYPE_BEACON) && + packetdump_cb) + packetdump_cb(soc, mgmt_rx_params->pdev_id, + rx_pkt->pkt_meta.session_id, rx_pkt->pkt_buf, + QDF_STATUS_SUCCESS, RX_MGMT_PKT); +#endif + return 0; +} + +/** + * wma_mem_endianness_based_copy() - does memory copy from src to dst + * @dst: destination address + * @src: source address + * @size: size to be copied + * + * This function copies the memory of size passed from source + * address to destination address. + * + * Return: Nothing + */ +#ifdef BIG_ENDIAN_HOST +static void wma_mem_endianness_based_copy( + uint8_t *dst, uint8_t *src, uint32_t size) +{ + /* + * For big endian host, copy engine byte_swap is enabled + * But the rx mgmt frame buffer content is in network byte order + * Need to byte swap the mgmt frame buffer content - so when + * copy engine does byte_swap - host gets buffer content in the + * correct byte order. + */ + + uint32_t i; + uint32_t *destp, *srcp; + + destp = (uint32_t *) dst; + srcp = (uint32_t *) src; + for (i = 0; i < (roundup(size, sizeof(uint32_t)) / 4); i++) { + *destp = cpu_to_le32(*srcp); + destp++; + srcp++; + } +} +#else +static void wma_mem_endianness_based_copy( + uint8_t *dst, uint8_t *src, uint32_t size) +{ + qdf_mem_copy(dst, src, size); +} +#endif + +#define RESERVE_BYTES 100 +/** + * wma_mgmt_rx_process() - process management rx frame. + * @handle: wma handle + * @data: rx data + * @data_len: data length + * + * Return: 0 for success or error code + */ +static int wma_mgmt_rx_process(void *handle, uint8_t *data, + uint32_t data_len) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + struct mgmt_rx_event_params *mgmt_rx_params; + struct wlan_objmgr_psoc *psoc; + uint8_t *bufp; + qdf_nbuf_t wbuf; + QDF_STATUS status; + + if (!wma_handle) { + WMA_LOGE("%s: Failed to get WMA context", __func__); + return -EINVAL; + } + + mgmt_rx_params = qdf_mem_malloc(sizeof(*mgmt_rx_params)); + if (!mgmt_rx_params) { + WMA_LOGE("%s: memory allocation failed", __func__); + return -ENOMEM; + } + + if (wmi_extract_mgmt_rx_params(wma_handle->wmi_handle, + data, mgmt_rx_params, &bufp) != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: Extraction of mgmt rx params failed", __func__); + qdf_mem_free(mgmt_rx_params); + return -EINVAL; + } + + if (mgmt_rx_params->buf_len > data_len || + !mgmt_rx_params->buf_len || + !bufp) { + WMA_LOGE("Invalid data_len %u, buf_len %u bufp %pK", + data_len, mgmt_rx_params->buf_len, bufp); + qdf_mem_free(mgmt_rx_params); + return -EINVAL; + } + + if (!mgmt_rx_params->chan_freq) { + /* + * It indicates that FW is legacy and is operating on + * channel numbers and it also indicates that BAND_6G support + * is not there as BAND_6G works only on frequencies and channel + * numbers can be treated as unique. + */ + mgmt_rx_params->chan_freq = wlan_reg_legacy_chan_to_freq( + wma_handle->pdev, + mgmt_rx_params->channel); + } + + mgmt_rx_params->pdev_id = 0; + mgmt_rx_params->rx_params = NULL; + + /* + * Allocate the memory for this rx packet, add extra 100 bytes for:- + * + * 1. Filling the missing RSN capabilites by some APs, which fill the + * RSN IE length as extra 2 bytes but dont fill the IE data with + * capabilities, resulting in failure in unpack core due to length + * mismatch. Check sir_validate_and_rectify_ies for more info. + * + * 2. In the API wma_process_rmf_frame(), the driver trims the CCMP + * header by overwriting the IEEE header to memory occupied by CCMP + * header, but an overflow is possible if the memory allocated to + * frame is less than the sizeof(struct ieee80211_frame) +CCMP + * HEADER len, so allocating 100 bytes would solve this issue too. + * + * 3. CCMP header is pointing to orig_hdr + + * sizeof(struct ieee80211_frame) which could also result in OOB + * access, if the data len is less than + * sizeof(struct ieee80211_frame), allocating extra bytes would + * result in solving this issue too. + */ + wbuf = qdf_nbuf_alloc(NULL, roundup(mgmt_rx_params->buf_len + + RESERVE_BYTES, + 4), 0, 4, false); + if (!wbuf) { + qdf_mem_free(mgmt_rx_params); + return -ENOMEM; + } + + qdf_nbuf_put_tail(wbuf, mgmt_rx_params->buf_len); + qdf_nbuf_set_protocol(wbuf, ETH_P_CONTROL); + + qdf_mem_zero(((uint8_t *)qdf_nbuf_data(wbuf) + mgmt_rx_params->buf_len), + (roundup(mgmt_rx_params->buf_len + RESERVE_BYTES, 4) - + mgmt_rx_params->buf_len)); + + wma_mem_endianness_based_copy(qdf_nbuf_data(wbuf), + bufp, mgmt_rx_params->buf_len); + + psoc = (struct wlan_objmgr_psoc *) + wma_handle->psoc; + if (!psoc) { + WMA_LOGE("%s: psoc ctx is NULL", __func__); + qdf_nbuf_free(wbuf); + qdf_mem_free(mgmt_rx_params); + return -EINVAL; + } + + status = mgmt_txrx_rx_handler(psoc, wbuf, mgmt_rx_params); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(mgmt_rx_params); + return -EINVAL; + } + + qdf_mem_free(mgmt_rx_params); + return 0; +} + +/** + * wma_de_register_mgmt_frm_client() - deregister management frame + * + * This function deregisters the event handler registered for + * WMI_MGMT_RX_EVENTID. + * + * Return: QDF status + */ +QDF_STATUS wma_de_register_mgmt_frm_client(void) +{ + tp_wma_handle wma_handle = (tp_wma_handle) + cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s: Failed to get WMA context", __func__); + return QDF_STATUS_E_NULL_VALUE; + } + +#ifdef QCA_WIFI_FTM + if (cds_get_conparam() == QDF_GLOBAL_FTM_MODE) + return QDF_STATUS_SUCCESS; +#endif + + if (wmi_unified_unregister_event_handler(wma_handle->wmi_handle, + wmi_mgmt_rx_event_id) != 0) { + WMA_LOGE("Failed to Unregister rx mgmt handler with wmi"); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * wma_register_roaming_callbacks() - Register roaming callbacks + * @csr_roam_synch_cb: CSR roam synch callback routine pointer + * @pe_roam_synch_cb: PE roam synch callback routine pointer + * @csr_roam_auth_event_handle_cb: CSR callback routine pointer + * @csr_roam_pmkid_req_cb: CSR roam pmkid callback routine pointer + * + * Register the SME and PE callback routines with WMA for + * handling roaming + * + * Return: Success or Failure Status + */ +QDF_STATUS wma_register_roaming_callbacks( + QDF_STATUS (*csr_roam_synch_cb)(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason), + QDF_STATUS (*csr_roam_auth_event_handle_cb)(struct mac_context *mac, + uint8_t vdev_id, + struct qdf_mac_addr bssid), + QDF_STATUS (*pe_roam_synch_cb)(struct mac_context *mac, + struct roam_offload_synch_ind *roam_synch_data, + struct bss_description *bss_desc_ptr, + enum sir_roam_op_code reason), + QDF_STATUS (*pe_disconnect_cb) (struct mac_context *mac, + uint8_t vdev_id, + uint8_t *deauth_disassoc_frame, + uint16_t deauth_disassoc_frame_len, + uint16_t reason_code), + QDF_STATUS (*csr_roam_pmkid_req_cb)(uint8_t vdev_id, + struct roam_pmkid_req_event *bss_list)) +{ + + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + WMA_LOGE("%s: Failed to get WMA context", __func__); + return QDF_STATUS_E_FAILURE; + } + wma->csr_roam_synch_cb = csr_roam_synch_cb; + wma->csr_roam_auth_event_handle_cb = csr_roam_auth_event_handle_cb; + wma->pe_roam_synch_cb = pe_roam_synch_cb; + wma->pe_disconnect_cb = pe_disconnect_cb; + wma_debug("Registered roam synch callbacks with WMA successfully"); + + wma->csr_roam_pmkid_req_cb = csr_roam_pmkid_req_cb; + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * wma_register_mgmt_frm_client() - register management frame callback + * + * This function registers event handler for WMI_MGMT_RX_EVENTID. + * + * Return: QDF status + */ +QDF_STATUS wma_register_mgmt_frm_client(void) +{ + tp_wma_handle wma_handle = (tp_wma_handle) + cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s: Failed to get WMA context", __func__); + return QDF_STATUS_E_NULL_VALUE; + } + + if (wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_mgmt_rx_event_id, + wma_mgmt_rx_process, + WMA_RX_WORK_CTX) != 0) { + WMA_LOGE("Failed to register rx mgmt handler with wmi"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_register_packetdump_callback() - stores tx and rx mgmt packet dump + * callback handler + * @tx_cb: tx mgmt packetdump cb + * @rx_cb: rx mgmt packetdump cb + * + * This function is used to store tx and rx mgmt. packet dump callback + * + * Return: None + * + */ +void wma_register_packetdump_callback( + ol_txrx_pktdump_cb tx_cb, + ol_txrx_pktdump_cb rx_cb) +{ + tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("wma handle is NULL"); + return; + } + + wma_handle->wma_mgmt_tx_packetdump_cb = tx_cb; + wma_handle->wma_mgmt_rx_packetdump_cb = rx_cb; +} + +/** + * wma_deregister_packetdump_callback() - removes tx and rx mgmt packet dump + * callback handler + * + * This function is used to remove tx and rx mgmt. packet dump callback + * + * Return: None + * + */ +void wma_deregister_packetdump_callback(void) +{ + tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("wma handle is NULL"); + return; + } + + wma_handle->wma_mgmt_tx_packetdump_cb = NULL; + wma_handle->wma_mgmt_rx_packetdump_cb = NULL; +} + +QDF_STATUS wma_mgmt_unified_cmd_send(struct wlan_objmgr_vdev *vdev, + qdf_nbuf_t buf, uint32_t desc_id, + void *mgmt_tx_params) +{ + tp_wma_handle wma_handle; + int ret; + QDF_STATUS status = QDF_STATUS_E_INVAL; + struct wmi_mgmt_params *mgmt_params = + (struct wmi_mgmt_params *)mgmt_tx_params; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!mgmt_params) { + WMA_LOGE("%s: mgmt_params ptr passed is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + mgmt_params->desc_id = desc_id; + + if (!vdev) { + WMA_LOGE("%s: vdev ptr passed is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_mgmt_tx_wmi)) { + status = wmi_mgmt_unified_cmd_send(wma_handle->wmi_handle, + mgmt_params); + } else { + QDF_NBUF_CB_MGMT_TXRX_DESC_ID(buf) + = mgmt_params->desc_id; + + ret = cdp_mgmt_send_ext(soc, mgmt_params->vdev_id, buf, + mgmt_params->tx_type, + mgmt_params->use_6mbps, + mgmt_params->chanfreq); + status = qdf_status_from_os_return(ret); + } + + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE("%s: mgmt tx failed", __func__); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +#ifndef CONFIG_HL_SUPPORT +void wma_mgmt_nbuf_unmap_cb(struct wlan_objmgr_pdev *pdev, + qdf_nbuf_t buf) +{ + struct wlan_objmgr_psoc *psoc; + qdf_device_t dev; + + if (!buf) + return; + + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) { + WMA_LOGE("%s: Psoc handle NULL", __func__); + return; + } + + dev = wlan_psoc_get_qdf_dev(psoc); + if (wlan_psoc_nif_fw_ext_cap_get(psoc, WLAN_SOC_CEXT_WMI_MGMT_REF)) + qdf_nbuf_unmap_single(dev, buf, QDF_DMA_TO_DEVICE); +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_nan_datapath.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_nan_datapath.c new file mode 100644 index 0000000000000000000000000000000000000000..e367fd7616b84372a6145d6c14191ca95eadd70a --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_nan_datapath.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_nan_datapath.c + * + * WMA NAN Data path API implementation + */ + +#include "wma.h" +#include "wma_api.h" +#include "wmi_unified_api.h" +#include "wmi_unified.h" +#include "wma_nan_datapath.h" +#include "wma_internal.h" +#include "cds_utils.h" +#include "cdp_txrx_peer_ops.h" +#include "cdp_txrx_tx_delay.h" +#include "cdp_txrx_misc.h" +#include + +/** + * wma_add_sta_ndi_mode() - Process ADD_STA for NaN Data path + * @wma: wma handle + * @add_sta: Parameters of ADD_STA command + * + * Sends CREATE_PEER command to firmware + * Return: void + */ +void wma_add_sta_ndi_mode(tp_wma_handle wma, tpAddStaParams add_sta) +{ + enum ol_txrx_peer_state state = OL_TXRX_PEER_STATE_CONN; + uint8_t pdev_id = WMI_PDEV_ID_SOC; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + QDF_STATUS status; + struct wma_txrx_node *iface; + + iface = &wma->interfaces[add_sta->smesessionId]; + wma_debug("vdev: %d, peer_mac_addr: "QDF_MAC_ADDR_FMT, + add_sta->smesessionId, QDF_MAC_ADDR_REF(add_sta->staMac)); + + if (cdp_find_peer_exist_on_vdev(soc, add_sta->smesessionId, + add_sta->staMac)) { + WMA_LOGE(FL("NDI peer already exists, peer_addr "QDF_MAC_ADDR_FMT), + QDF_MAC_ADDR_REF(add_sta->staMac)); + add_sta->status = QDF_STATUS_E_EXISTS; + goto send_rsp; + } + + /* + * The code above only checks the peer existence on its own vdev. + * Need to check whether the peer exists on other vDevs because firmware + * can't create the peer if the peer with same MAC address already + * exists on the pDev. As this peer belongs to other vDevs, just return + * here. + */ + if (cdp_find_peer_exist(soc, pdev_id, add_sta->staMac)) { + WMA_LOGE(FL("peer exists on other vdev with peer_addr "QDF_MAC_ADDR_FMT), + QDF_MAC_ADDR_REF(add_sta->staMac)); + add_sta->status = QDF_STATUS_E_EXISTS; + goto send_rsp; + } + + status = wma_create_peer(wma, add_sta->staMac, + WMI_PEER_TYPE_NAN_DATA, add_sta->smesessionId, + false); + if (status != QDF_STATUS_SUCCESS) { + WMA_LOGE(FL("Failed to create peer for "QDF_MAC_ADDR_FMT), + QDF_MAC_ADDR_REF(add_sta->staMac)); + add_sta->status = status; + goto send_rsp; + } + + if (!cdp_find_peer_exist_on_vdev(soc, add_sta->smesessionId, + add_sta->staMac)) { + WMA_LOGE(FL("Failed to find peer handle using peer mac "QDF_MAC_ADDR_FMT), + QDF_MAC_ADDR_REF(add_sta->staMac)); + add_sta->status = QDF_STATUS_E_FAILURE; + wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId, + false); + goto send_rsp; + } + + WMA_LOGD(FL("Moving peer "QDF_MAC_ADDR_FMT" to state %d"), + QDF_MAC_ADDR_REF(add_sta->staMac), state); + cdp_peer_state_update(soc, add_sta->staMac, state); + + add_sta->nss = iface->nss; + add_sta->status = QDF_STATUS_SUCCESS; +send_rsp: + WMA_LOGD(FL("Sending add sta rsp to umac (mac:"QDF_MAC_ADDR_FMT", status:%d)"), + QDF_MAC_ADDR_REF(add_sta->staMac), add_sta->status); + wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, (void *)add_sta, 0); +} + +/** + * wma_delete_sta_req_ndi_mode() - Process DEL_STA request for NDI data peer + * @wma: WMA context + * @del_sta: DEL_STA parameters from LIM + * + * Removes wma/txrx peer entry for the NDI STA + * + * Return: None + */ +void wma_delete_sta_req_ndi_mode(tp_wma_handle wma, + tpDeleteStaParams del_sta) +{ + wma_remove_peer(wma, del_sta->staMac, + del_sta->smesessionId, false); + del_sta->status = QDF_STATUS_SUCCESS; + + if (del_sta->respReqd) { + WMA_LOGD(FL("Sending del rsp to umac (status: %d)"), + del_sta->status); + wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP, del_sta, 0); + } else { + WMA_LOGD(FL("NDI Del Sta resp not needed")); + qdf_mem_free(del_sta); + } + +} diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_nan_datapath.h b/drivers/staging/qcacld-3.0/core/wma/src/wma_nan_datapath.h new file mode 100644 index 0000000000000000000000000000000000000000..d774512347c063aa04518bf86fc57add7af56040 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_nan_datapath.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_nan_datapath.h + * + * WMA NAN Data path API specification + */ + +#ifndef __WMA_NAN_DATAPATH_H +#define __WMA_NAN_DATAPATH_H + +#include +#include +#include "wma.h" +#include "sir_api.h" +#include "sme_nan_datapath.h" + +static inline int wma_ndp_wow_event_callback(void *handle, void *event, + uint32_t len, uint32_t event_id) +{ + return 0; +} + +static inline uint32_t wma_ndp_get_eventid_from_tlvtag(uint32_t tag) +{ + return 0; +} + +#ifdef WLAN_FEATURE_NAN +#define WMA_IS_VDEV_IN_NDI_MODE(intf, vdev_id) \ + (WMI_VDEV_TYPE_NDI == intf[vdev_id].type) + +void wma_add_sta_ndi_mode(tp_wma_handle wma, tpAddStaParams add_sta); + +/** + * wma_update_hdd_cfg_ndp() - Update target device NAN datapath capability + * @wma_handle: pointer to WMA context + * @tgt_cfg: Pointer to target configuration data structure + * + * Return: none + */ +static inline void wma_update_hdd_cfg_ndp(tp_wma_handle wma_handle, + struct wma_tgt_cfg *tgt_cfg) +{ + tgt_cfg->nan_datapath_enabled = wma_handle->nan_datapath_enabled; +} + +void wma_delete_sta_req_ndi_mode(tp_wma_handle wma, + tpDeleteStaParams del_sta); + +/** + * wma_is_ndi_active() - Determines of the nan data iface is active + * @wma_handle: handle to wma context + * + * Returns: true if ndi active, flase otherwise + */ +static inline bool wma_is_ndi_active(tp_wma_handle wma_handle) +{ + int i; + + for (i = 0; i < wma_handle->max_bssid; i++) { + if (wma_handle->interfaces[i].type == WMI_VDEV_TYPE_NDI && + wma_handle->interfaces[i].peer_count > 0) + return true; + } + return false; +} +#else /* WLAN_FEATURE_NAN */ +#define WMA_IS_VDEV_IN_NDI_MODE(intf, vdev_id) (false) +static inline void wma_update_hdd_cfg_ndp(tp_wma_handle wma_handle, + struct wma_tgt_cfg *tgt_cfg) +{ + return; +} + +static inline void wma_delete_sta_req_ndi_mode(tp_wma_handle wma, + tpDeleteStaParams del_sta) +{ +} +static inline void wma_add_sta_ndi_mode(tp_wma_handle wma, + tpAddStaParams add_sta) {} + +static inline bool wma_is_ndi_active(tp_wma_handle wma_handle) { return false; } +#endif /* WLAN_FEATURE_NAN */ + +#endif /* __WMA_NAN_DATAPATH_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_ocb.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_ocb.c new file mode 100644 index 0000000000000000000000000000000000000000..6770c40cf61a2036dabce6c0d4695706c3424bde --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_ocb.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_ocb.c + * + * WLAN Host Device Driver 802.11p OCB implementation + */ + +#include "wma_ocb.h" +#include "cds_utils.h" +#include "cds_api.h" +#include "wlan_ocb_ucfg_api.h" +#include "lim_utils.h" +#include "../../core/src/vdev_mgr_ops.h" + +/** + * wma_start_ocb_vdev() - start OCB vdev + * @config: ocb channel config + * + * Return: QDF_STATUS_SUCCESS on success + */ +static QDF_STATUS wma_start_ocb_vdev(struct ocb_config *config) +{ + QDF_STATUS status; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct wlan_objmgr_vdev *vdev; + struct vdev_mlme_obj *mlme_obj; + struct wlan_channel *des_chan; + uint8_t dot11_mode; + + vdev = wma->interfaces[config->vdev_id].vdev; + if (!vdev) { + wma_err("vdev is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + wma_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + des_chan = vdev->vdev_mlme.des_chan; + + des_chan->ch_freq = config->channels[0].chan_freq; + if (wlan_reg_is_24ghz_ch_freq(des_chan->ch_freq)) + dot11_mode = MLME_DOT11_MODE_11G; + else + dot11_mode = MLME_DOT11_MODE_11A; + des_chan->ch_ieee = + wlan_reg_freq_to_chan(wma->pdev, des_chan->ch_freq); + + status = lim_set_ch_phy_mode(vdev, dot11_mode); + if (QDF_IS_STATUS_ERROR(status)) + return QDF_STATUS_E_FAILURE; + + mlme_obj->mgmt.chainmask_info.num_rx_chain = 2; + mlme_obj->mgmt.chainmask_info.num_tx_chain = 2; + + status = wma_vdev_pre_start(config->vdev_id, false); + if (status != QDF_STATUS_SUCCESS) + return status; + + status = vdev_mgr_start_send(mlme_obj, false); + + return status; +} + +QDF_STATUS wma_ocb_register_callbacks(tp_wma_handle wma_handle) +{ + ucfg_ocb_register_vdev_start(wma_handle->pdev, wma_start_ocb_vdev); + + return QDF_STATUS_SUCCESS; +} diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_ocb.h b/drivers/staging/qcacld-3.0/core/wma/src/wma_ocb.h new file mode 100644 index 0000000000000000000000000000000000000000..a6339a3dd87035d19371187b55edf0aaecaa5ae3 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_ocb.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WMA_OCB_H +#define __WMA_OCB_H + +#include "wma.h" + +#ifdef WLAN_FEATURE_DSRC +/** + * wma_ocb_register_callbacks() - register WMA layer callback for OCB + * @wma_handle: wma handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +QDF_STATUS wma_ocb_register_callbacks(tp_wma_handle wma_handle); +#else +/** + * wma_ocb_register_callbacks() - register WMA layer callback for OCB + * @wma_handle: wma handle + * + * Return: QDF_STATUS_SUCCESS on success + */ +static inline QDF_STATUS wma_ocb_register_callbacks(tp_wma_handle wma_handle) +{ + return QDF_STATUS_SUCCESS; +} +#endif +#endif /* __WMA_OCB_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_power.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_power.c new file mode 100644 index 0000000000000000000000000000000000000000..934e5f0acf4e262bc70d82e48120f4a003c6032e --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_power.c @@ -0,0 +1,1680 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_power.c + * This file contains powersave related functions. + */ + +/* Header files */ + +#include "wma.h" +#include "wma_api.h" +#include "cds_api.h" +#include "wmi_unified_api.h" +#include "wlan_qct_sys.h" +#include "wni_api.h" +#include "ani_global.h" +#include "wmi_unified.h" +#include "wni_cfg.h" + +#include "qdf_nbuf.h" +#include "qdf_types.h" +#include "qdf_mem.h" +#include "wma_types.h" +#include "lim_api.h" +#include "lim_session_utils.h" + +#include "cds_utils.h" + +#if !defined(REMOVE_PKT_LOG) +#include "pktlog_ac.h" +#endif /* REMOVE_PKT_LOG */ + +#include "dbglog_host.h" +#include "csr_api.h" +#include "ol_fw.h" + +#include "wma_internal.h" +#include "wlan_pmo_ucfg_api.h" + +/** + * wma_unified_modem_power_state() - set modem power state to fw + * @wmi_handle: wmi handle + * @param_value: parameter value + * + * Return: QDF_STATUS + */ +static QDF_STATUS +wma_unified_modem_power_state(wmi_unified_t wmi_handle, uint32_t param_value) +{ + QDF_STATUS status; + wmi_modem_power_state_cmd_param *cmd; + wmi_buf_t buf; + uint16_t len = sizeof(*cmd); + + buf = wmi_buf_alloc(wmi_handle, len); + if (!buf) + return -ENOMEM; + + cmd = (wmi_modem_power_state_cmd_param *) wmi_buf_data(buf); + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_modem_power_state_cmd_param, + WMITLV_GET_STRUCT_TLVLEN + (wmi_modem_power_state_cmd_param)); + cmd->modem_power_state = param_value; + WMA_LOGD("%s: Setting cmd->modem_power_state = %u", __func__, + param_value); + status = wmi_unified_cmd_send(wmi_handle, buf, len, + WMI_MODEM_POWER_STATE_CMDID); + if (QDF_IS_STATUS_ERROR(status)) + wmi_buf_free(buf); + + return status; +} + +/** + * wma_unified_set_sta_ps_param() - set sta power save parameter to fw + * @wmi_handle: wmi handle + * @vdev_id: vdev id + * @param: param + * @value: parameter value + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_unified_set_sta_ps_param(wmi_unified_t wmi_handle, + uint32_t vdev_id, uint32_t param, + uint32_t value) +{ + tp_wma_handle wma; + struct wma_txrx_node *iface; + struct sta_ps_params sta_ps_param = {0}; + QDF_STATUS status; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: wma is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!wma_is_vdev_valid(vdev_id)) + return QDF_STATUS_E_INVAL; + + WMA_LOGD("Set Sta Ps param vdevId %d Param %d val %d", + vdev_id, param, value); + iface = &wma->interfaces[vdev_id]; + + sta_ps_param.vdev_id = vdev_id; + sta_ps_param.param_id = param; + sta_ps_param.value = value; + status = wmi_unified_sta_ps_cmd_send(wmi_handle, &sta_ps_param); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + return status; +} + +#ifdef QCA_IBSS_SUPPORT +QDF_STATUS +wma_set_ibss_pwrsave_params(tp_wma_handle wma, uint8_t vdev_id) +{ + QDF_STATUS ret; + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_ATIM_WINDOW_LENGTH, + wma->wma_ibss_power_save_params.atimWindowLength); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed to set WMI_VDEV_PARAM_ATIM_WINDOW_LENGTH ret = %d", + ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_IS_IBSS_POWER_SAVE_ALLOWED, + wma->wma_ibss_power_save_params.isPowerSaveAllowed); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed, set WMI_VDEV_PARAM_IS_IBSS_POWER_SAVE_ALLOWED ret=%d", + ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_IS_POWER_COLLAPSE_ALLOWED, + wma->wma_ibss_power_save_params.isPowerCollapseAllowed); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed, set WMI_VDEV_PARAM_IS_POWER_COLLAPSE_ALLOWED ret=%d", + ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_IS_AWAKE_ON_TXRX_ENABLED, + wma->wma_ibss_power_save_params.isAwakeonTxRxEnabled); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed, set WMI_VDEV_PARAM_IS_AWAKE_ON_TXRX_ENABLED ret=%d", + ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_INACTIVITY_CNT, + wma->wma_ibss_power_save_params.inactivityCount); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed, set WMI_VDEV_PARAM_INACTIVITY_CNT ret=%d", + ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_TXSP_END_INACTIVITY_TIME_MS, + wma->wma_ibss_power_save_params.txSPEndInactivityTime); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed, set WMI_VDEV_PARAM_TXSP_END_INACTIVITY_TIME_MS ret=%d", + ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_IBSS_PS_WARMUP_TIME_SECS, + wma->wma_ibss_power_save_params.ibssPsWarmupTime); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed, set WMI_VDEV_PARAM_IBSS_PS_WARMUP_TIME_SECS ret=%d", + ret); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE, + wma->wma_ibss_power_save_params.ibssPs1RxChainInAtimEnable); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed to set IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE ret=%d", + ret); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} +#endif /* QCA_IBSS_SUPPORT */ + +/** + * wma_set_ap_peer_uapsd() - set powersave parameters in ap mode to fw + * @wma: wma handle + * @vdev_id: vdev id + * @peer_addr: peer mac address + * @uapsd_value: uapsd value + * @max_sp: maximum service period + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_set_ap_peer_uapsd(tp_wma_handle wma, uint32_t vdev_id, + uint8_t *peer_addr, uint8_t uapsd_value, + uint8_t max_sp) +{ + uint32_t uapsd = 0; + uint32_t max_sp_len = 0; + QDF_STATUS ret; + struct ap_ps_params param = {0}; + + if (uapsd_value & UAPSD_VO_ENABLED) { + uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN; + } + + if (uapsd_value & UAPSD_VI_ENABLED) { + uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN; + } + + if (uapsd_value & UAPSD_BK_ENABLED) { + uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN; + } + + if (uapsd_value & UAPSD_BE_ENABLED) { + uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN; + } + + switch (max_sp) { + case UAPSD_MAX_SP_LEN_2: + max_sp_len = WMI_AP_PS_PEER_PARAM_MAX_SP_2; + break; + case UAPSD_MAX_SP_LEN_4: + max_sp_len = WMI_AP_PS_PEER_PARAM_MAX_SP_4; + break; + case UAPSD_MAX_SP_LEN_6: + max_sp_len = WMI_AP_PS_PEER_PARAM_MAX_SP_6; + break; + default: + max_sp_len = WMI_AP_PS_PEER_PARAM_MAX_SP_UNLIMITED; + break; + } + + WMA_LOGD("Set WMI_AP_PS_PEER_PARAM_UAPSD 0x%x for "QDF_MAC_ADDR_FMT, + uapsd, QDF_MAC_ADDR_REF(peer_addr)); + param.vdev_id = vdev_id; + param.param = WMI_AP_PS_PEER_PARAM_UAPSD; + param.value = uapsd; + ret = wmi_unified_ap_ps_cmd_send(wma->wmi_handle, peer_addr, + ¶m); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed to set WMI_AP_PS_PEER_PARAM_UAPSD for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_addr)); + return ret; + } + + WMA_LOGD("Set WMI_AP_PS_PEER_PARAM_MAX_SP 0x%x for "QDF_MAC_ADDR_FMT, + max_sp_len, QDF_MAC_ADDR_REF(peer_addr)); + + param.vdev_id = vdev_id; + param.param = WMI_AP_PS_PEER_PARAM_MAX_SP; + param.value = max_sp_len; + ret = wmi_unified_ap_ps_cmd_send(wma->wmi_handle, peer_addr, + ¶m); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Failed to set WMI_AP_PS_PEER_PARAM_MAX_SP for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(peer_addr)); + return ret; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_update_edca_params_for_ac() - to update per ac EDCA parameters + * @edca_param: EDCA parameters + * @wmm_param: wmm parameters + * @ac: access category + * + * Return: none + */ +void wma_update_edca_params_for_ac(tSirMacEdcaParamRecord *edca_param, + struct wmi_host_wme_vparams *wmm_param, + int ac, bool mu_edca_param) +{ + wmm_param->cwmin = WMA_WMM_EXPO_TO_VAL(edca_param->cw.min); + wmm_param->cwmax = WMA_WMM_EXPO_TO_VAL(edca_param->cw.max); + wmm_param->aifs = edca_param->aci.aifsn; + if (mu_edca_param) + wmm_param->mu_edca_timer = edca_param->mu_edca_timer; + else + wmm_param->txoplimit = edca_param->txoplimit; + wmm_param->acm = edca_param->aci.acm; + + wmm_param->noackpolicy = edca_param->no_ack; + + WMA_LOGD("WMM PARAMS AC[%d]: AIFS %d Min %d Max %d %s %d ACM %d NOACK %d", + ac, wmm_param->aifs, wmm_param->cwmin, + wmm_param->cwmax, + mu_edca_param ? "MU_EDCA TIMER" : "TXOP", + mu_edca_param ? wmm_param->mu_edca_timer : + wmm_param->txoplimit, + wmm_param->acm, wmm_param->noackpolicy); +} + +/** + * wma_set_tx_power() - set tx power limit in fw + * @handle: wma handle + * @tx_pwr_params: tx power parameters + * + * Return: none + */ +void wma_set_tx_power(WMA_HANDLE handle, + tMaxTxPowerParams *tx_pwr_params) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + uint8_t vdev_id; + QDF_STATUS ret = QDF_STATUS_E_FAILURE; + int8_t max_reg_power; + struct wma_txrx_node *iface; + + if (tx_pwr_params->dev_mode == QDF_SAP_MODE || + tx_pwr_params->dev_mode == QDF_P2P_GO_MODE) { + ret = wma_find_vdev_id_by_addr(wma_handle, + tx_pwr_params->bssId.bytes, + &vdev_id); + } else { + ret = wma_find_vdev_id_by_bssid(wma_handle, + tx_pwr_params->bssId.bytes, + &vdev_id); + } + if (ret) { + WMA_LOGE("vdev id is invalid for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(tx_pwr_params->bssId.bytes)); + qdf_mem_free(tx_pwr_params); + return; + } + + if (!wma_is_vdev_up(vdev_id)) { + WMA_LOGE("%s: vdev id %d is not up for "QDF_MAC_ADDR_FMT, + __func__, vdev_id, + QDF_MAC_ADDR_REF(tx_pwr_params->bssId.bytes)); + qdf_mem_free(tx_pwr_params); + return; + } + + iface = &wma_handle->interfaces[vdev_id]; + if (tx_pwr_params->power == 0) { + /* set to default. Since the app does not care the tx power + * we keep the previous setting + */ + mlme_set_tx_power(iface->vdev, tx_pwr_params->power); + ret = 0; + goto end; + } + + max_reg_power = mlme_get_max_reg_power(iface->vdev); + + if (max_reg_power != 0) { + /* make sure tx_power less than max_tx_power */ + if (tx_pwr_params->power > max_reg_power) { + tx_pwr_params->power = max_reg_power; + } + } + if (mlme_get_tx_power(iface->vdev) != tx_pwr_params->power) { + + /* tx_power changed, Push the tx_power to FW */ + WMA_LOGI("%s: Set TX pwr limit [WMI_VDEV_PARAM_TX_PWRLIMIT] to %d", + __func__, tx_pwr_params->power); + ret = wma_vdev_set_param(wma_handle->wmi_handle, vdev_id, + WMI_VDEV_PARAM_TX_PWRLIMIT, + tx_pwr_params->power); + if (ret == QDF_STATUS_SUCCESS) + mlme_set_tx_power(iface->vdev, tx_pwr_params->power); + } else { + /* no tx_power change */ + ret = QDF_STATUS_SUCCESS; + } +end: + qdf_mem_free(tx_pwr_params); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to set vdev param WMI_VDEV_PARAM_TX_PWRLIMIT"); +} + +void wma_send_max_tx_pwrlmt(WMA_HANDLE handle, uint8_t vdev_id) +{ + uint32_t max_tx_pwr; + struct wma_txrx_node *iface; + struct vdev_mlme_obj *mlme_obj; + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + iface = &wma_handle->interfaces[vdev_id]; + if (!iface) { + wma_err("Failed to get iface handle for vdev_id %d", vdev_id); + return; + } + + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (!mlme_obj) + return; + + max_tx_pwr = mlme_obj->mgmt.generic.tx_pwrlimit; + if (!max_tx_pwr) + return; + + wma_vdev_set_param(wma_handle->wmi_handle, vdev_id, + WMI_VDEV_PARAM_TX_PWRLIMIT, + max_tx_pwr); +} + +/** + * wma_set_max_tx_power() - set max tx power limit in fw + * @handle: wma handle + * @tx_pwr_params: tx power parameters + * + * Return: none + */ +void wma_set_max_tx_power(WMA_HANDLE handle, + tMaxTxPowerParams *tx_pwr_params) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + uint8_t vdev_id; + QDF_STATUS ret = QDF_STATUS_E_FAILURE; + int8_t prev_max_power; + int8_t max_reg_power; + struct wma_txrx_node *iface; + + if (tx_pwr_params->dev_mode == QDF_SAP_MODE || + tx_pwr_params->dev_mode == QDF_P2P_GO_MODE) { + ret = wma_find_vdev_id_by_addr(wma_handle, + tx_pwr_params->bssId.bytes, + &vdev_id); + } else { + ret = wma_find_vdev_id_by_bssid(wma_handle, + tx_pwr_params->bssId.bytes, + &vdev_id); + } + if (ret) { + WMA_LOGE("vdev id is invalid for "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(tx_pwr_params->bssId.bytes)); + qdf_mem_free(tx_pwr_params); + return; + } + + if (!wma_is_vdev_up(vdev_id)) { + WMA_LOGE("%s: vdev id %d is not up", __func__, vdev_id); + qdf_mem_free(tx_pwr_params); + return; + } + + iface = &wma_handle->interfaces[vdev_id]; + if (mlme_get_max_reg_power(iface->vdev) == tx_pwr_params->power) { + ret = QDF_STATUS_SUCCESS; + goto end; + } + prev_max_power = mlme_get_max_reg_power(iface->vdev); + + mlme_set_max_reg_power(iface->vdev, tx_pwr_params->power); + + max_reg_power = mlme_get_max_reg_power(iface->vdev); + + if (max_reg_power == 0) { + ret = QDF_STATUS_SUCCESS; + goto end; + } + WMA_LOGI("Set MAX TX pwr limit [WMI_VDEV_PARAM_TX_PWRLIMIT] to %d", + max_reg_power); + ret = wma_vdev_set_param(wma_handle->wmi_handle, vdev_id, + WMI_VDEV_PARAM_TX_PWRLIMIT, + max_reg_power); + if (ret == QDF_STATUS_SUCCESS) + mlme_set_tx_power(iface->vdev, max_reg_power); + else + mlme_set_max_reg_power(iface->vdev, prev_max_power); +end: + qdf_mem_free(tx_pwr_params); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("%s: Failed to set vdev param WMI_VDEV_PARAM_TX_PWRLIMIT", + __func__); +} + +/** + * wmi_unified_set_sta_ps() - set sta powersave params in fw + * @handle: wma handle + * @vdev_id: vdev id + * @val: value + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +static QDF_STATUS wmi_unified_set_sta_ps(wmi_unified_t wmi_handle, + uint32_t vdev_id, uint8_t val) +{ + QDF_STATUS ret; + + ret = wmi_unified_set_sta_ps_mode(wmi_handle, vdev_id, + val); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to send set Mimo PS ret = %d", ret); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_get_uapsd_mask() - get uapsd mask based on uapsd parameters + * @uapsd_params: uapsed parameters + * + * Return: uapsd mask + */ +static inline uint32_t wma_get_uapsd_mask(tpUapsd_Params uapsd_params) +{ + uint32_t uapsd_val = 0; + + if (uapsd_params->beDeliveryEnabled) + uapsd_val |= WMI_STA_PS_UAPSD_AC0_DELIVERY_EN; + + if (uapsd_params->beTriggerEnabled) + uapsd_val |= WMI_STA_PS_UAPSD_AC0_TRIGGER_EN; + + if (uapsd_params->bkDeliveryEnabled) + uapsd_val |= WMI_STA_PS_UAPSD_AC1_DELIVERY_EN; + + if (uapsd_params->bkTriggerEnabled) + uapsd_val |= WMI_STA_PS_UAPSD_AC1_TRIGGER_EN; + + if (uapsd_params->viDeliveryEnabled) + uapsd_val |= WMI_STA_PS_UAPSD_AC2_DELIVERY_EN; + + if (uapsd_params->viTriggerEnabled) + uapsd_val |= WMI_STA_PS_UAPSD_AC2_TRIGGER_EN; + + if (uapsd_params->voDeliveryEnabled) + uapsd_val |= WMI_STA_PS_UAPSD_AC3_DELIVERY_EN; + + if (uapsd_params->voTriggerEnabled) + uapsd_val |= WMI_STA_PS_UAPSD_AC3_TRIGGER_EN; + + return uapsd_val; +} + +/** + * wma_set_force_sleep() - set power save parameters to fw + * @wma: wma handle + * @vdev_id: vdev id + * @enable: enable/disable + * @power_config: power configuration + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +static QDF_STATUS wma_set_force_sleep(tp_wma_handle wma, + uint32_t vdev_id, + uint8_t enable, + enum powersave_mode power_config, + bool enable_ps) +{ + QDF_STATUS ret; + uint32_t cfg_data_val = 0; + /* get mac to access CFG data base */ + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + uint32_t rx_wake_policy; + uint32_t tx_wake_threshold; + uint32_t pspoll_count; + uint32_t inactivity_time; + uint32_t psmode; + + WMA_LOGD("Set Force Sleep vdevId %d val %d", vdev_id, enable); + + if (!mac) { + WMA_LOGE("%s: Unable to get PE context", __func__); + return QDF_STATUS_E_NOMEM; + } + + inactivity_time = mac->mlme_cfg->timeouts.ps_data_inactivity_timeout; + + if (enable) { + /* override normal configuration and force station asleep */ + rx_wake_policy = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD; + tx_wake_threshold = WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER; + + if (ucfg_pmo_get_max_ps_poll(mac->psoc)) + pspoll_count = + (uint32_t)ucfg_pmo_get_max_ps_poll(mac->psoc); + else + pspoll_count = WMA_DEFAULT_MAX_PSPOLL_BEFORE_WAKE; + + psmode = WMI_STA_PS_MODE_ENABLED; + } else { + /* Ps Poll Wake Policy */ + if (ucfg_pmo_get_max_ps_poll(mac->psoc)) { + /* Ps Poll is enabled */ + rx_wake_policy = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD; + pspoll_count = + (uint32_t)ucfg_pmo_get_max_ps_poll(mac->psoc); + tx_wake_threshold = WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER; + } else { + rx_wake_policy = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + pspoll_count = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; + tx_wake_threshold = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; + } + psmode = WMI_STA_PS_MODE_ENABLED; + } + + /* + * Advanced power save is enabled by default in Firmware + * So Disable advanced power save explicitly + */ + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, vdev_id, + WMI_STA_PS_ENABLE_QPOWER, + power_config); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("%s(%d) Power Failed vdevId %d", + power_config ? "Enable" : "Disable", + power_config, vdev_id); + return ret; + } + WMA_LOGD("Power %s(%d) vdevId %d", + power_config ? "Enabled" : "Disabled", + power_config, vdev_id); + + /* Set the Wake Policy to WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD */ + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, vdev_id, + WMI_STA_PS_PARAM_RX_WAKE_POLICY, + rx_wake_policy); + + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Setting wake policy Failed vdevId %d", vdev_id); + return ret; + } + WMA_LOGD("Setting wake policy to %d vdevId %d", + rx_wake_policy, vdev_id); + + /* Set the Tx Wake Threshold */ + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, vdev_id, + WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD, + tx_wake_threshold); + + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Setting TxWake Threshold vdevId %d", vdev_id); + return ret; + } + WMA_LOGD("Setting TxWake Threshold to %d vdevId %d", + tx_wake_threshold, vdev_id); + + /* Set the Ps Poll Count */ + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, vdev_id, + WMI_STA_PS_PARAM_PSPOLL_COUNT, + pspoll_count); + + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Set Ps Poll Count Failed vdevId %d ps poll cnt %d", + vdev_id, pspoll_count); + return ret; + } + WMA_LOGD("Set Ps Poll Count vdevId %d ps poll cnt %d", + vdev_id, pspoll_count); + + /* Set the Tx/Rx InActivity */ + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, vdev_id, + WMI_STA_PS_PARAM_INACTIVITY_TIME, + inactivity_time); + + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Setting Tx/Rx InActivity Failed vdevId %d InAct %d", + vdev_id, inactivity_time); + return ret; + } + WMA_LOGD("Set Tx/Rx InActivity vdevId %d InAct %d", + vdev_id, inactivity_time); + + /* Enable Sta Mode Power save */ + if (enable_ps) { + ret = wmi_unified_set_sta_ps(wma->wmi_handle, vdev_id, true); + + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Enable Sta Mode Ps Failed vdevId %d", + vdev_id); + return ret; + } + } + + /* Set Listen Interval */ + cfg_data_val = mac->mlme_cfg->sap_cfg.listen_interval; + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_LISTEN_INTERVAL, + cfg_data_val); + if (QDF_IS_STATUS_ERROR(ret)) { + /* Even it fails continue Fw will take default LI */ + WMA_LOGE("Failed to Set Listen Interval vdevId %d", vdev_id); + } + WMA_LOGD("Set Listen Interval vdevId %d Listen Intv %d", + vdev_id, cfg_data_val); + + return QDF_STATUS_SUCCESS; +} + +static uint8_t wma_get_power_config(tp_wma_handle wma) +{ + WMA_LOGD("POWER mode is %d", wma->powersave_mode); + + return wma->powersave_mode; +} + +void wma_enable_sta_ps_mode(tpEnablePsParams ps_req) +{ + uint32_t vdev_id = ps_req->sessionid; + QDF_STATUS ret; + enum powersave_mode power_config; + struct wma_txrx_node *iface; + t_wma_handle *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + wma_err("wma handle is null"); + return; + } + + iface = &wma_handle->interfaces[vdev_id]; + + if (!iface || !iface->vdev) { + WMA_LOGE("%s: vdev is NULL for vdev_%d", __func__, vdev_id); + return; + } + + power_config = wma_get_power_config(wma_handle); + if (eSIR_ADDON_NOTHING == ps_req->psSetting) { + if (power_config && iface->uapsd_cached_val) { + power_config = 0; + WMA_LOGD("Advanced power save is disabled"); + } + WMA_LOGD("Enable Sta Mode Ps vdevId %d", vdev_id); + ret = wma_unified_set_sta_ps_param(wma_handle->wmi_handle, + vdev_id, + WMI_STA_PS_PARAM_UAPSD, 0); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Set Uapsd param 0 Failed vdevId %d", vdev_id); + return; + } + + ret = wma_set_force_sleep(wma_handle, vdev_id, false, + power_config, true); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Enable Sta Ps Failed vdevId %d", vdev_id); + return; + } + } else if (eSIR_ADDON_ENABLE_UAPSD == ps_req->psSetting) { + uint32_t uapsd_val = 0; + + uapsd_val = wma_get_uapsd_mask(&ps_req->uapsdParams); + if (uapsd_val != iface->uapsd_cached_val) { + WMA_LOGD("Enable Uapsd vdevId %d Mask %d", + vdev_id, uapsd_val); + ret = + wma_unified_set_sta_ps_param(wma_handle->wmi_handle, + vdev_id, + WMI_STA_PS_PARAM_UAPSD, + uapsd_val); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Enable Uapsd Failed vdevId %d", + vdev_id); + return; + } + /* Cache the Uapsd Mask */ + iface->uapsd_cached_val = uapsd_val; + } else { + WMA_LOGD("Already Uapsd Enabled vdevId %d Mask %d", + vdev_id, uapsd_val); + } + + if (power_config && iface->uapsd_cached_val) { + power_config = 0; + WMA_LOGD("Qpower is disabled"); + } + WMA_LOGD("Enable Forced Sleep vdevId %d", vdev_id); + ret = wma_set_force_sleep(wma_handle, vdev_id, true, + power_config, true); + + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Enable Forced Sleep Failed vdevId %d", + vdev_id); + return; + } + } + + if (wma_handle->ito_repeat_count) { + WMA_LOGI("Set ITO count to %d for vdevId %d", + wma_handle->ito_repeat_count, vdev_id); + + ret = wma_unified_set_sta_ps_param(wma_handle->wmi_handle, + vdev_id, + WMI_STA_PS_PARAM_MAX_RESET_ITO_COUNT_ON_TIM_NO_TXRX, + wma_handle->ito_repeat_count); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Set ITO count failed vdevId %d Error %d", + vdev_id, ret); + return; + } + } + + /* power save request succeeded */ + iface->in_bmps = true; +} + + +/** + * wma_disable_sta_ps_mode() - disable sta powersave params in fw + * @wma: wma handle + * @ps_req: power save request + * + * Return: none + */ +void wma_disable_sta_ps_mode(tpDisablePsParams ps_req) +{ + QDF_STATUS ret; + uint32_t vdev_id = ps_req->sessionid; + struct wma_txrx_node *iface; + t_wma_handle *wma_handle; + + wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma_handle) { + wma_err("wma handle is null"); + return; + } + + iface = &wma_handle->interfaces[vdev_id]; + + WMA_LOGD("Disable Sta Mode Ps vdevId %d", vdev_id); + + /* Disable Sta Mode Power save */ + ret = wmi_unified_set_sta_ps(wma_handle->wmi_handle, vdev_id, false); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Disable Sta Mode Ps Failed vdevId %d", vdev_id); + return; + } + iface->in_bmps = false; + + /* Disable UAPSD incase if additional Req came */ + if (eSIR_ADDON_DISABLE_UAPSD == ps_req->psSetting) { + WMA_LOGD("Disable Uapsd vdevId %d", vdev_id); + ret = wma_unified_set_sta_ps_param(wma_handle->wmi_handle, + vdev_id, + WMI_STA_PS_PARAM_UAPSD, 0); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Disable Uapsd Failed vdevId %d", vdev_id); + /* + * Even this fails we can proceed as success + * since we disabled powersave + */ + } + } +} + +QDF_STATUS wma_set_power_config(uint8_t vdev_id, enum powersave_mode power) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return QDF_STATUS_E_INVAL; + } + + WMA_LOGI("configuring power: %d", power); + wma->powersave_mode = power; + return wma_unified_set_sta_ps_param(wma->wmi_handle, + vdev_id, + WMI_STA_PS_ENABLE_QPOWER, + wma_get_power_config(wma)); +} + +void wma_enable_uapsd_mode(tp_wma_handle wma, tpEnableUapsdParams ps_req) +{ + QDF_STATUS ret; + uint32_t vdev_id = ps_req->sessionid; + uint32_t uapsd_val = 0; + enum powersave_mode power_config = wma_get_power_config(wma); + struct wma_txrx_node *iface = &wma->interfaces[vdev_id]; + + if (!iface->vdev) { + WMA_LOGE("%s: vdev is NULL for vdev_%d", __func__, vdev_id); + return; + } + + /* Disable Sta Mode Power save */ + ret = wmi_unified_set_sta_ps(wma->wmi_handle, vdev_id, false); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Disable Sta Mode Ps Failed vdevId %d", vdev_id); + return; + } + + uapsd_val = wma_get_uapsd_mask(&ps_req->uapsdParams); + + WMA_LOGD("Enable Uapsd vdevId %d Mask %d", vdev_id, uapsd_val); + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, vdev_id, + WMI_STA_PS_PARAM_UAPSD, uapsd_val); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Enable Uapsd Failed vdevId %d", vdev_id); + return; + } + + if (power_config && uapsd_val) { + power_config = 0; + WMA_LOGD("Disable power %d", vdev_id); + } + iface->uapsd_cached_val = uapsd_val; + WMA_LOGD("Enable Forced Sleep vdevId %d", vdev_id); + ret = wma_set_force_sleep(wma, vdev_id, true, + power_config, ps_req->uapsdParams.enable_ps); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Enable Forced Sleep Failed vdevId %d", vdev_id); + return; + } + +} + +/** + * wma_disable_uapsd_mode() - disable uapsd mode in fw + * @wma: wma handle + * @ps_req: power save request + * + * Return: none + */ +void wma_disable_uapsd_mode(tp_wma_handle wma, + tpDisableUapsdParams ps_req) +{ + QDF_STATUS ret; + uint32_t vdev_id = ps_req->sessionid; + enum powersave_mode power_config = wma_get_power_config(wma); + + WMA_LOGD("Disable Uapsd vdevId %d", vdev_id); + + /* Disable Sta Mode Power save */ + ret = wmi_unified_set_sta_ps(wma->wmi_handle, vdev_id, false); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Disable Sta Mode Ps Failed vdevId %d", vdev_id); + return; + } + + ret = wma_unified_set_sta_ps_param(wma->wmi_handle, vdev_id, + WMI_STA_PS_PARAM_UAPSD, 0); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Disable Uapsd Failed vdevId %d", vdev_id); + return; + } + + /* Re enable Sta Mode Powersave with proper configuration */ + ret = wma_set_force_sleep(wma, vdev_id, false, + power_config, true); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Disable Forced Sleep Failed vdevId %d", vdev_id); + return; + } +} + +/** + * wma_set_sta_uapsd_auto_trig_cmd() - set uapsd auto trigger command + * @wmi_handle: wma handle + * @vdevid: vdev id + * @peer_addr: peer mac address + * @trig_param: auto trigger parameters + * @num_ac: number of access category + * + * This function sets the trigger + * uapsd params such as service interval, delay interval + * and suspend interval which will be used by the firmware + * to send trigger frames periodically when there is no + * traffic on the transmit side. + * + * Return: 0 for success or error code. + */ +static QDF_STATUS wma_set_sta_uapsd_auto_trig_cmd(wmi_unified_t wmi_handle, + uint32_t vdevid, + uint8_t peer_addr[QDF_MAC_ADDR_SIZE], + struct sta_uapsd_params *trig_param, + uint32_t num_ac) +{ + QDF_STATUS ret; + struct sta_uapsd_trig_params cmd = {0}; + + cmd.vdevid = vdevid; + cmd.auto_triggerparam = trig_param; + cmd.num_ac = num_ac; + + qdf_mem_copy((uint8_t *) cmd.peer_addr, (uint8_t *) peer_addr, + sizeof(uint8_t) * QDF_MAC_ADDR_SIZE); + ret = wmi_unified_set_sta_uapsd_auto_trig_cmd(wmi_handle, + &cmd); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to send set uapsd param ret = %d", ret); + + return ret; +} + +QDF_STATUS wma_trigger_uapsd_params(tp_wma_handle wma_handle, uint32_t vdev_id, + tp_wma_trigger_uapsd_params + trigger_uapsd_params) +{ + QDF_STATUS ret; + uint8_t *bssid; + struct sta_uapsd_params uapsd_trigger_param; + + WMA_LOGD("Trigger uapsd params vdev id %d", vdev_id); + + WMA_LOGD("WMM AC %d User Priority %d SvcIntv %d DelIntv %d SusIntv %d", + trigger_uapsd_params->wmm_ac, + trigger_uapsd_params->user_priority, + trigger_uapsd_params->service_interval, + trigger_uapsd_params->delay_interval, + trigger_uapsd_params->suspend_interval); + + if (!wmi_service_enabled(wma_handle->wmi_handle, + wmi_sta_uapsd_basic_auto_trig) || + !wmi_service_enabled(wma_handle->wmi_handle, + wmi_sta_uapsd_var_auto_trig)) { + WMA_LOGD("Trigger uapsd is not supported vdev id %d", vdev_id); + return QDF_STATUS_SUCCESS; + } + + uapsd_trigger_param.wmm_ac = trigger_uapsd_params->wmm_ac; + uapsd_trigger_param.user_priority = trigger_uapsd_params->user_priority; + uapsd_trigger_param.service_interval = + trigger_uapsd_params->service_interval; + uapsd_trigger_param.suspend_interval = + trigger_uapsd_params->suspend_interval; + uapsd_trigger_param.delay_interval = + trigger_uapsd_params->delay_interval; + + bssid = wma_get_vdev_bssid(wma_handle->interfaces[vdev_id].vdev); + if (!bssid) { + WMA_LOGE("%s: Failed to get bssid for vdev_%d", + __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + ret = wma_set_sta_uapsd_auto_trig_cmd(wma_handle->wmi_handle, + vdev_id, bssid, + &uapsd_trigger_param, 1); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Fail to send uapsd param cmd for vdevid %d ret = %d", + ret, vdev_id); + return ret; + } + + return ret; +} + +QDF_STATUS wma_disable_uapsd_per_ac(tp_wma_handle wma_handle, + uint32_t vdev_id, enum uapsd_ac ac) +{ + QDF_STATUS ret; + uint8_t *bssid; + struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; + struct sta_uapsd_params uapsd_trigger_param; + enum uapsd_up user_priority; + + WMA_LOGD("Disable Uapsd per ac vdevId %d ac %d", vdev_id, ac); + + switch (ac) { + case UAPSD_VO: + iface->uapsd_cached_val &= + ~(WMI_STA_PS_UAPSD_AC3_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN); + user_priority = UAPSD_UP_VO; + break; + case UAPSD_VI: + iface->uapsd_cached_val &= + ~(WMI_STA_PS_UAPSD_AC2_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN); + user_priority = UAPSD_UP_VI; + break; + case UAPSD_BK: + iface->uapsd_cached_val &= + ~(WMI_STA_PS_UAPSD_AC1_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN); + user_priority = UAPSD_UP_BK; + break; + case UAPSD_BE: + iface->uapsd_cached_val &= + ~(WMI_STA_PS_UAPSD_AC0_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN); + user_priority = UAPSD_UP_BE; + break; + default: + WMA_LOGE("Invalid AC vdevId %d ac %d", vdev_id, ac); + return QDF_STATUS_E_FAILURE; + } + + /* + * Disable Auto Trigger Functionality before + * disabling uapsd for a particular AC + */ + uapsd_trigger_param.wmm_ac = ac; + uapsd_trigger_param.user_priority = user_priority; + uapsd_trigger_param.service_interval = 0; + uapsd_trigger_param.suspend_interval = 0; + uapsd_trigger_param.delay_interval = 0; + + bssid = wma_get_vdev_bssid(wma_handle->interfaces[vdev_id].vdev); + if (!bssid) { + WMA_LOGE("%s: Failed to get bssid for vdev_%d", + __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + ret = wma_set_sta_uapsd_auto_trig_cmd(wma_handle->wmi_handle, + vdev_id, bssid, + &uapsd_trigger_param, 1); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Fail to send auto trig cmd for vdevid %d ret = %d", + ret, vdev_id); + return ret; + } + + ret = wma_unified_set_sta_ps_param(wma_handle->wmi_handle, vdev_id, + WMI_STA_PS_PARAM_UAPSD, + iface->uapsd_cached_val); + if (QDF_IS_STATUS_ERROR(ret)) { + WMA_LOGE("Disable Uapsd per ac Failed vdevId %d ac %d", vdev_id, + ac); + return ret; + } + WMA_LOGD("Disable Uapsd per ac vdevId %d val %d", vdev_id, + iface->uapsd_cached_val); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_get_temperature() - get pdev temperature req + * @wmi_handle: wma handle + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_get_temperature(tp_wma_handle wma_handle) +{ + QDF_STATUS ret = QDF_STATUS_SUCCESS; + + ret = wmi_unified_get_temperature(wma_handle->wmi_handle); + if (ret) + WMA_LOGE("Failed to send set Mimo PS ret = %d", ret); + + return ret; +} + +/** + * wma_pdev_temperature_evt_handler() - pdev temperature event handler + * @handle: wma handle + * @event: event buffer + * @len : length + * + * Return: 0 for success or error code. + */ +int wma_pdev_temperature_evt_handler(void *handle, uint8_t *event, + uint32_t len) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct scheduler_msg sme_msg = { 0 }; + WMI_PDEV_TEMPERATURE_EVENTID_param_tlvs *param_buf; + wmi_pdev_temperature_event_fixed_param *wmi_event; + + param_buf = (WMI_PDEV_TEMPERATURE_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("Invalid pdev_temperature event buffer"); + return -EINVAL; + } + + wmi_event = param_buf->fixed_param; + WMA_LOGI(FL("temperature: %d"), wmi_event->value); + + sme_msg.type = eWNI_SME_MSG_GET_TEMPERATURE_IND; + sme_msg.bodyptr = NULL; + sme_msg.bodyval = wmi_event->value; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + WMA_LOGE(FL("Fail to post get temperature ind msg")); + return 0; +} + +/** + * wma_process_tx_power_limits() - sends the power limits for 2g/5g to firmware + * @handle: wma handle + * @ptxlim: power limit value + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_process_tx_power_limits(WMA_HANDLE handle, + struct tx_power_limit *ptxlim) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + int32_t ret = 0; + uint32_t txpower_params2g = 0; + uint32_t txpower_params5g = 0; + struct pdev_params pdevparam; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue tx power limit", + __func__); + return QDF_STATUS_E_INVAL; + } + /* Set value and reason code for 2g and 5g power limit */ + + SET_PDEV_PARAM_TXPOWER_REASON(txpower_params2g, + WMI_PDEV_PARAM_TXPOWER_REASON_SAR); + SET_PDEV_PARAM_TXPOWER_VALUE(txpower_params2g, ptxlim->txPower2g); + + SET_PDEV_PARAM_TXPOWER_REASON(txpower_params5g, + WMI_PDEV_PARAM_TXPOWER_REASON_SAR); + SET_PDEV_PARAM_TXPOWER_VALUE(txpower_params5g, ptxlim->txPower5g); + + WMA_LOGD("%s: txpower2g: %x txpower5g: %x", + __func__, txpower_params2g, txpower_params5g); + + pdevparam.param_id = WMI_PDEV_PARAM_TXPOWER_LIMIT2G; + pdevparam.param_value = txpower_params2g; + ret = wmi_unified_pdev_param_send(wma->wmi_handle, + &pdevparam, + WMA_WILDCARD_PDEV_ID); + if (ret) { + WMA_LOGE("%s: Failed to set txpower 2g (%d)", __func__, ret); + return QDF_STATUS_E_FAILURE; + } + pdevparam.param_id = WMI_PDEV_PARAM_TXPOWER_LIMIT5G; + pdevparam.param_value = txpower_params5g; + ret = wmi_unified_pdev_param_send(wma->wmi_handle, + &pdevparam, + WMA_WILDCARD_PDEV_ID); + if (ret) { + WMA_LOGE("%s: Failed to set txpower 5g (%d)", __func__, ret); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_WMI_BCN +/** + * wma_add_p2p_ie() - add p2p IE + * @frm: ptr where p2p ie needs to add + * + * Return: ptr after p2p ie + */ +static uint8_t *wma_add_p2p_ie(uint8_t *frm) +{ + uint8_t wfa_oui[3] = WMA_P2P_WFA_OUI; + struct p2p_ie *p2p_ie = (struct p2p_ie *)frm; + + p2p_ie->p2p_id = WMA_P2P_IE_ID; + p2p_ie->p2p_oui[0] = wfa_oui[0]; + p2p_ie->p2p_oui[1] = wfa_oui[1]; + p2p_ie->p2p_oui[2] = wfa_oui[2]; + p2p_ie->p2p_oui_type = WMA_P2P_WFA_VER; + p2p_ie->p2p_len = 4; + return frm + sizeof(struct p2p_ie); +} + +/** + * wma_update_beacon_noa_ie() - update beacon ie + * @bcn: beacon info + * @new_noa_sub_ie_len: ie length + * + * Return: none + */ +static void wma_update_beacon_noa_ie(struct beacon_info *bcn, + uint16_t new_noa_sub_ie_len) +{ + struct p2p_ie *p2p_ie; + uint8_t *buf; + + /* if there is nothing to add, just return */ + if (new_noa_sub_ie_len == 0) { + if (bcn->noa_sub_ie_len && bcn->noa_ie) { + WMA_LOGD("%s: NoA is present in previous beacon, but not present in swba event, So Reset the NoA", + __func__); + /* TODO: Assuming p2p noa ie is last ie in the beacon */ + qdf_mem_zero(bcn->noa_ie, (bcn->noa_sub_ie_len + + sizeof(struct p2p_ie))); + bcn->len -= (bcn->noa_sub_ie_len + + sizeof(struct p2p_ie)); + bcn->noa_ie = NULL; + bcn->noa_sub_ie_len = 0; + } + WMA_LOGD("%s: No need to update NoA", __func__); + return; + } + + if (bcn->noa_sub_ie_len && bcn->noa_ie) { + /* NoA present in previous beacon, update it */ + WMA_LOGD("%s: NoA present in previous beacon, update the NoA IE, bcn->len %u bcn->noa_sub_ie_len %u", + __func__, bcn->len, bcn->noa_sub_ie_len); + bcn->len -= (bcn->noa_sub_ie_len + sizeof(struct p2p_ie)); + qdf_mem_zero(bcn->noa_ie, + (bcn->noa_sub_ie_len + sizeof(struct p2p_ie))); + } else { /* NoA is not present in previous beacon */ + WMA_LOGD("%s: NoA not present in previous beacon, add it bcn->len %u", + __func__, bcn->len); + buf = qdf_nbuf_data(bcn->buf); + bcn->noa_ie = buf + bcn->len; + } + + bcn->noa_sub_ie_len = new_noa_sub_ie_len; + wma_add_p2p_ie(bcn->noa_ie); + p2p_ie = (struct p2p_ie *)bcn->noa_ie; + p2p_ie->p2p_len += new_noa_sub_ie_len; + qdf_mem_copy((bcn->noa_ie + sizeof(struct p2p_ie)), bcn->noa_sub_ie, + new_noa_sub_ie_len); + + bcn->len += (new_noa_sub_ie_len + sizeof(struct p2p_ie)); + WMA_LOGI("%s: Updated beacon length with NoA Ie is %u", + __func__, bcn->len); +} + +/** + * wma_p2p_create_sub_ie_noa() - put p2p noa ie + * @buf: buffer + * @noa: noa element ie + * @new_noa_sub_ie_len: ie length + * + * Return: none + */ +static void wma_p2p_create_sub_ie_noa(uint8_t *buf, + struct p2p_sub_element_noa *noa, + uint16_t *new_noa_sub_ie_len) +{ + uint8_t tmp_octet = 0; + int i; + uint8_t *buf_start = buf; + + *buf++ = WMA_P2P_SUB_ELEMENT_NOA; /* sub-element id */ + ASSERT(noa->num_descriptors <= WMA_MAX_NOA_DESCRIPTORS); + + /* + * Length = (2 octets for Index and CTWin/Opp PS) and + * (13 octets for each NOA Descriptors) + */ + P2PIE_PUT_LE16(buf, WMA_NOA_IE_SIZE(noa->num_descriptors)); + buf += 2; + + *buf++ = noa->index; /* Instance Index */ + + tmp_octet = noa->ctwindow & WMA_P2P_NOA_IE_CTWIN_MASK; + if (noa->oppPS) + tmp_octet |= WMA_P2P_NOA_IE_OPP_PS_SET; + *buf++ = tmp_octet; /* Opp Ps and CTWin capabilities */ + + for (i = 0; i < noa->num_descriptors; i++) { + ASSERT(noa->noa_descriptors[i].type_count != 0); + + *buf++ = noa->noa_descriptors[i].type_count; + + P2PIE_PUT_LE32(buf, noa->noa_descriptors[i].duration); + buf += 4; + P2PIE_PUT_LE32(buf, noa->noa_descriptors[i].interval); + buf += 4; + P2PIE_PUT_LE32(buf, noa->noa_descriptors[i].start_time); + buf += 4; + } + *new_noa_sub_ie_len = (buf - buf_start); +} + +/** + * wma_update_noa() - update noa params + * @beacon: beacon info + * @noa_ie: noa ie + * + * Return: none + */ +void wma_update_noa(struct beacon_info *beacon, + struct p2p_sub_element_noa *noa_ie) +{ + uint16_t new_noa_sub_ie_len; + + /* Call this function by holding the spinlock on beacon->lock */ + + if (noa_ie) { + if ((noa_ie->ctwindow == 0) && (noa_ie->oppPS == 0) && + (noa_ie->num_descriptors == 0)) { + /* NoA is not present */ + WMA_LOGD("%s: NoA is not present", __func__); + new_noa_sub_ie_len = 0; + } else { + /* Create the binary blob containing NOA sub-IE */ + WMA_LOGD("%s: Create NOA sub ie", __func__); + wma_p2p_create_sub_ie_noa(&beacon->noa_sub_ie[0], + noa_ie, &new_noa_sub_ie_len); + } + } else { + WMA_LOGD("%s: No need to add NOA", __func__); + new_noa_sub_ie_len = 0; /* no NOA IE sub-attributes */ + } + + wma_update_beacon_noa_ie(beacon, new_noa_sub_ie_len); +} + +/** + * wma_update_probe_resp_noa() - update noa IE in probe response + * @wma_handle: wma handle + * @noa_ie: noa ie + * + * Return: none + */ +void wma_update_probe_resp_noa(tp_wma_handle wma_handle, + struct p2p_sub_element_noa *noa_ie) +{ + tSirP2PNoaAttr *noa_attr = qdf_mem_malloc(sizeof(tSirP2PNoaAttr)); + WMA_LOGD("Received update NoA event"); + if (!noa_attr) + return; + + qdf_mem_zero(noa_attr, sizeof(tSirP2PNoaAttr)); + + noa_attr->index = noa_ie->index; + noa_attr->oppPsFlag = noa_ie->oppPS; + noa_attr->ctWin = noa_ie->ctwindow; + if (!noa_ie->num_descriptors) { + WMA_LOGD("Zero NoA descriptors"); + } else { + WMA_LOGD("%d NoA descriptors", noa_ie->num_descriptors); + noa_attr->uNoa1IntervalCnt = + noa_ie->noa_descriptors[0].type_count; + noa_attr->uNoa1Duration = noa_ie->noa_descriptors[0].duration; + noa_attr->uNoa1Interval = noa_ie->noa_descriptors[0].interval; + noa_attr->uNoa1StartTime = + noa_ie->noa_descriptors[0].start_time; + if (noa_ie->num_descriptors > 1) { + noa_attr->uNoa2IntervalCnt = + noa_ie->noa_descriptors[1].type_count; + noa_attr->uNoa2Duration = + noa_ie->noa_descriptors[1].duration; + noa_attr->uNoa2Interval = + noa_ie->noa_descriptors[1].interval; + noa_attr->uNoa2StartTime = + noa_ie->noa_descriptors[1].start_time; + } + } + WMA_LOGI("Sending SIR_HAL_P2P_NOA_ATTR_IND to LIM"); + wma_send_msg(wma_handle, SIR_HAL_P2P_NOA_ATTR_IND, (void *)noa_attr, 0); +} + +#else +static inline uint8_t *wma_add_p2p_ie(uint8_t *frm) +{ + return 0; +} + +static inline void +wma_update_beacon_noa_ie(struct beacon_info *bcn, + uint16_t new_noa_sub_ie_len) +{ +} + +static inline void +wma_p2p_create_sub_ie_noa(uint8_t *buf, + struct p2p_sub_element_noa *noa, + uint16_t *new_noa_sub_ie_len) +{ +} + +void wma_update_noa(struct beacon_info *beacon, + struct p2p_sub_element_noa *noa_ie) +{ +} + +void wma_update_probe_resp_noa(tp_wma_handle wma_handle, + struct p2p_sub_element_noa *noa_ie) +{ +} + +#endif + +/** + * wma_process_set_mimops_req() - Set the received MiMo PS state to firmware + * @handle: wma handle + * @mimops: MIMO powersave params + * + * Return: none + */ +void wma_process_set_mimops_req(tp_wma_handle wma_handle, + tSetMIMOPS *mimops) +{ + /* Translate to what firmware understands */ + if (mimops->htMIMOPSState == eSIR_HT_MIMO_PS_DYNAMIC) + mimops->htMIMOPSState = WMI_PEER_MIMO_PS_DYNAMIC; + else if (mimops->htMIMOPSState == eSIR_HT_MIMO_PS_STATIC) + mimops->htMIMOPSState = WMI_PEER_MIMO_PS_STATIC; + else if (mimops->htMIMOPSState == eSIR_HT_MIMO_PS_NO_LIMIT) + mimops->htMIMOPSState = WMI_PEER_MIMO_PS_NONE; + + wma_debug("htMIMOPSState = %d, sessionId = %d peerMac <"QDF_MAC_ADDR_FMT">", + mimops->htMIMOPSState, mimops->sessionId, + QDF_MAC_ADDR_REF(mimops->peerMac)); + + wma_set_peer_param(wma_handle, mimops->peerMac, + WMI_PEER_MIMO_PS_STATE, mimops->htMIMOPSState, + mimops->sessionId); +} + +/** + * wma_set_mimops() - set MIMO powersave + * @handle: wma handle + * @vdev_id: vdev id + * @value: value + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_set_mimops(tp_wma_handle wma, uint8_t vdev_id, int value) +{ + QDF_STATUS ret; + + ret = wmi_unified_set_mimops(wma->wmi_handle, vdev_id, + value); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to send set Mimo PS ret = %d", ret); + + return ret; +} + +/** + * wma_notify_modem_power_state() - notify modem power state + * @wma_ptr: wma handle + * @pReq: modem power state + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_notify_modem_power_state(void *wma_ptr, + tSirModemPowerStateInd *pReq) +{ + QDF_STATUS status; + tp_wma_handle wma = (tp_wma_handle) wma_ptr; + + WMA_LOGD("%s: WMA notify Modem Power State %d", __func__, pReq->param); + + status = wma_unified_modem_power_state(wma->wmi_handle, pReq->param); + if (QDF_IS_STATUS_ERROR(status)) { + wma_err("Failed to notify Modem Power State %d", pReq->param); + return status; + } + + WMA_LOGD("Successfully notify Modem Power State %d", pReq->param); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_set_idle_ps_config() - enable/disble Low Power Support(Pdev Specific) + * @wma_ptr: wma handle + * @idle_ps: idle powersave + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_set_idle_ps_config(void *wma_ptr, uint32_t idle_ps) +{ + int32_t ret; + tp_wma_handle wma = (tp_wma_handle) wma_ptr; + struct pdev_params pdevparam; + + WMA_LOGD("WMA Set Idle Ps Config [1:set 0:clear] val %d", idle_ps); + + /* Set Idle Mode Power Save Config */ + pdevparam.param_id = WMI_PDEV_PARAM_IDLE_PS_CONFIG; + pdevparam.param_value = idle_ps; + ret = wmi_unified_pdev_param_send(wma->wmi_handle, + &pdevparam, + WMA_WILDCARD_PDEV_ID); + + if (ret) { + WMA_LOGE("Fail to Set Idle Ps Config %d", idle_ps); + return QDF_STATUS_E_FAILURE; + } + wma->in_imps = !!idle_ps; + + WMA_LOGD("Successfully Set Idle Ps Config %d", idle_ps); + return QDF_STATUS_SUCCESS; +} + +/** + * wma_set_smps_params() - set smps params + * @wma: wma handle + * @vdev_id: vdev id + * @value: value + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_set_smps_params(tp_wma_handle wma, uint8_t vdev_id, + int value) +{ + QDF_STATUS ret; + + ret = wmi_unified_set_smps_params(wma->wmi_handle, vdev_id, + value); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Failed to send set Mimo PS ret = %d", ret); + + return ret; +} + +#ifdef FEATURE_TX_POWER +/** + * wma_set_tx_power_scale() - set tx power scale + * @vdev_id: vdev id + * @value: value + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_set_tx_power_scale(uint8_t vdev_id, int value) +{ + QDF_STATUS ret; + tp_wma_handle wma_handle = + (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s: wma_handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!wma_is_vdev_up(vdev_id)) { + WMA_LOGE("%s: vdev id %d is not up", __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma_handle->wmi_handle, vdev_id, + WMI_VDEV_PARAM_TXPOWER_SCALE, value); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Set tx power scale failed"); + + return ret; +} + +/** + * wma_set_tx_power_scale_decr_db() - decrease power by DB value + * @vdev_id: vdev id + * @value: value + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS wma_set_tx_power_scale_decr_db(uint8_t vdev_id, int value) +{ + QDF_STATUS ret; + tp_wma_handle wma_handle = + (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma_handle) { + WMA_LOGE("%s: wma_handle is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!wma_is_vdev_up(vdev_id)) { + WMA_LOGE("%s: vdev id %d is not up", __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + + ret = wma_vdev_set_param(wma_handle->wmi_handle, vdev_id, + WMI_VDEV_PARAM_TXPOWER_SCALE_DECR_DB, value); + if (QDF_IS_STATUS_ERROR(ret)) + WMA_LOGE("Decrease tx power value failed"); + + return ret; +} +#endif /* FEATURE_TX_POWER */ + diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_sar_public_structs.h b/drivers/staging/qcacld-3.0/core/wma/src/wma_sar_public_structs.h new file mode 100644 index 0000000000000000000000000000000000000000..f2b330ba3f8ecc104fc3ee31faa76b4130776c76 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_sar_public_structs.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WMA_SAR_PUBLIC_STRUCTS_H +#define __WMA_SAR_PUBLIC_STRUCTS_H + +struct sar_limit_event; + +enum sar_version { + SAR_VERSION_1, + SAR_VERSION_2 +}; + +/** + * typedef wma_sar_cb() - SAR callback function + * @context: Opaque context provided by caller in the original request + * @event: SAR limits event + */ +typedef void (*wma_sar_cb)(void *context, struct sar_limit_event *event); + +#endif /* __WMA_SAR_PUBLIC_STRUCTS_H */ diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_scan_roam.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_scan_roam.c new file mode 100644 index 0000000000000000000000000000000000000000..32b57d9c4e506d91156635bdb8d81e0e600cecd8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_scan_roam.c @@ -0,0 +1,6557 @@ +/* + * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_scan_roam.c + * This file contains functions related to scan and + * roaming functionality. + */ + +/* Header files */ + +#include "wma.h" +#include "wma_api.h" +#include "cds_api.h" +#include "wmi_unified_api.h" +#include "wlan_qct_sys.h" +#include "wni_api.h" +#include "ani_global.h" +#include "wmi_unified.h" +#include "wni_cfg.h" +#include +#include +#include + +#include "qdf_nbuf.h" +#include "qdf_types.h" +#include "qdf_mem.h" +#include "wlan_blm_api.h" + +#include "wma_types.h" +#include "lim_api.h" +#include "lim_session_utils.h" + +#include "cds_utils.h" +#include "wlan_policy_mgr_api.h" +#include + +#if !defined(REMOVE_PKT_LOG) +#include "pktlog_ac.h" +#endif /* REMOVE_PKT_LOG */ + +#include "dbglog_host.h" +#include "csr_api.h" +#include "ol_fw.h" + +#include "wma_internal.h" +#if defined(CONFIG_HL_SUPPORT) +#include "wlan_tgt_def_config_hl.h" +#else +#include "wlan_tgt_def_config.h" +#endif +#include "wlan_reg_services_api.h" +#include "wlan_roam_debug.h" +#include "wlan_mlme_public_struct.h" + +/* This is temporary, should be removed */ +#include "ol_htt_api.h" +#include +#include "wma_he.h" +#include +#include +#include "wma_nan_datapath.h" +#include "wlan_mlme_api.h" +#include +#include +#include +#include +#include "wlan_blm_api.h" + +#ifdef FEATURE_WLAN_DIAG_SUPPORT /* FEATURE_WLAN_DIAG_SUPPORT */ +#include "host_diag_core_log.h" +#endif /* FEATURE_WLAN_DIAG_SUPPORT */ + +#ifdef FEATURE_WLAN_EXTSCAN +#define WMA_EXTSCAN_CYCLE_WAKE_LOCK_DURATION WAKELOCK_DURATION_RECOMMENDED + +/* + * Maximum number of entires that could be present in the + * WMI_EXTSCAN_HOTLIST_MATCH_EVENT buffer from the firmware + */ +#define WMA_EXTSCAN_MAX_HOTLIST_ENTRIES 10 +#endif + +static inline wmi_host_channel_width +wma_map_phy_ch_bw_to_wmi_channel_width(enum phy_ch_width ch_width) +{ + switch (ch_width) { + case CH_WIDTH_20MHZ: + return WMI_HOST_CHAN_WIDTH_20; + case CH_WIDTH_40MHZ: + return WMI_HOST_CHAN_WIDTH_40; + case CH_WIDTH_80MHZ: + return WMI_HOST_CHAN_WIDTH_80; + case CH_WIDTH_160MHZ: + return WMI_HOST_CHAN_WIDTH_160; + case CH_WIDTH_5MHZ: + return WMI_HOST_CHAN_WIDTH_5; + case CH_WIDTH_10MHZ: + return WMI_HOST_CHAN_WIDTH_10; + default: + return WMI_HOST_CHAN_WIDTH_20; + } +} + +#define WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ 0 +#define WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ 1 +#define WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ 2 +#define WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ 3 + +/** + * wma_update_channel_list() - update channel list + * @handle: wma handle + * @chan_list: channel list + * + * Function is used to update the support channel list in fw. + * + * Return: QDF status + */ +QDF_STATUS wma_update_channel_list(WMA_HANDLE handle, + tSirUpdateChanList *chan_list) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + int i, len; + struct scan_chan_list_params *scan_ch_param; + struct channel_param *chan_p; + struct ch_params ch_params; + + len = sizeof(struct channel_param) * chan_list->numChan + + offsetof(struct scan_chan_list_params, ch_param[0]); + scan_ch_param = qdf_mem_malloc(len); + if (!scan_ch_param) + return QDF_STATUS_E_NOMEM; + + qdf_mem_zero(scan_ch_param, len); + WMA_LOGD("no of channels = %d", chan_list->numChan); + chan_p = &scan_ch_param->ch_param[0]; + scan_ch_param->nallchans = chan_list->numChan; + scan_ch_param->max_bw_support_present = true; + wma_handle->saved_chan.num_channels = chan_list->numChan; + WMA_LOGD("ht %d, vht %d, vht_24 %d", chan_list->ht_en, + chan_list->vht_en, chan_list->vht_24_en); + + for (i = 0; i < chan_list->numChan; ++i) { + chan_p->mhz = chan_list->chanParam[i].freq; + chan_p->cfreq1 = chan_p->mhz; + chan_p->cfreq2 = 0; + wma_handle->saved_chan.ch_freq_list[i] = + chan_list->chanParam[i].freq; + + if (chan_list->chanParam[i].dfsSet) { + chan_p->is_chan_passive = 1; + chan_p->dfs_set = 1; + } + + if (chan_list->chanParam[i].nan_disabled) + chan_p->nan_disabled = 1; + + if (chan_p->mhz < WMA_2_4_GHZ_MAX_FREQ) { + chan_p->phy_mode = MODE_11G; + if (chan_list->vht_en && chan_list->vht_24_en) + chan_p->allow_vht = 1; + } else { + chan_p->phy_mode = MODE_11A; + if (chan_list->vht_en && + !(WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_p->mhz))) + chan_p->allow_vht = 1; + } + + if (chan_list->ht_en && + !(WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_p->mhz))) + chan_p->allow_ht = 1; + + if (chan_list->he_en) + chan_p->allow_he = 1; + + if (chan_list->chanParam[i].half_rate) + chan_p->half_rate = 1; + else if (chan_list->chanParam[i].quarter_rate) + chan_p->quarter_rate = 1; + + if (wlan_reg_is_6ghz_psc_chan_freq( + chan_p->mhz)) + chan_p->psc_channel = 1; + + /*TODO: Set WMI_SET_CHANNEL_MIN_POWER */ + /*TODO: Set WMI_SET_CHANNEL_ANTENNA_MAX */ + /*TODO: WMI_SET_CHANNEL_REG_CLASSID */ + chan_p->maxregpower = chan_list->chanParam[i].pwr; + + ch_params.ch_width = CH_WIDTH_160MHZ; + wlan_reg_set_channel_params_for_freq(wma_handle->pdev, + chan_p->mhz, 0, + &ch_params); + + chan_p->max_bw_supported = + wma_map_phy_ch_bw_to_wmi_channel_width(ch_params.ch_width); + chan_p++; + } + + qdf_status = wmi_unified_scan_chan_list_cmd_send(wma_handle->wmi_handle, + scan_ch_param); + + if (QDF_IS_STATUS_ERROR(qdf_status)) + WMA_LOGE("Failed to send WMI_SCAN_CHAN_LIST_CMDID"); + + qdf_mem_free(scan_ch_param); + + return qdf_status; +} + +QDF_STATUS wma_roam_scan_mawc_params(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{ + struct wmi_mawc_roam_params *params; + QDF_STATUS status; + + if (!roam_req) { + WMA_LOGE("No MAWC parameters to send"); + return QDF_STATUS_E_INVAL; + } + params = qdf_mem_malloc(sizeof(*params)); + if (!params) + return QDF_STATUS_E_NOMEM; + + params->vdev_id = roam_req->sessionId; + params->enable = roam_req->mawc_roam_params.mawc_enabled && + roam_req->mawc_roam_params.mawc_roam_enabled; + params->traffic_load_threshold = + roam_req->mawc_roam_params.mawc_roam_traffic_threshold; + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_hw_db2dbm_support)) + params->best_ap_rssi_threshold = + roam_req->mawc_roam_params.mawc_roam_ap_rssi_threshold; + else + params->best_ap_rssi_threshold = + roam_req->mawc_roam_params.mawc_roam_ap_rssi_threshold - + WMA_NOISE_FLOOR_DBM_DEFAULT; + params->rssi_stationary_high_adjust = + roam_req->mawc_roam_params.mawc_roam_rssi_high_adjust; + params->rssi_stationary_low_adjust = + roam_req->mawc_roam_params.mawc_roam_rssi_low_adjust; + status = wmi_unified_roam_mawc_params_cmd( + wma_handle->wmi_handle, params); + qdf_mem_free(params); + + return status; +} +#ifdef WLAN_FEATURE_FILS_SK +/** + * wma_roam_scan_fill_fils_params() - API to fill FILS params in RSO command + * @wma_handle: WMA handle + * @params: Pointer to destination RSO params to be filled + * @roam_req: Pointer to RSO params from CSR + * + * Return: None + */ +static void wma_roam_scan_fill_fils_params(tp_wma_handle wma_handle, + struct roam_offload_scan_params + *params, struct roam_offload_scan_req + *roam_req) +{ + struct roam_fils_params *dst_fils_params, *src_fils_params; + + if (!params || !roam_req || !roam_req->is_fils_connection) + return; + + src_fils_params = &roam_req->roam_fils_params; + dst_fils_params = ¶ms->roam_fils_params; + + params->add_fils_tlv = true; + + dst_fils_params->username_length = src_fils_params->username_length; + qdf_mem_copy(dst_fils_params->username, src_fils_params->username, + dst_fils_params->username_length); + + dst_fils_params->next_erp_seq_num = src_fils_params->next_erp_seq_num; + dst_fils_params->rrk_length = src_fils_params->rrk_length; + qdf_mem_copy(dst_fils_params->rrk, src_fils_params->rrk, + dst_fils_params->rrk_length); + + dst_fils_params->rik_length = src_fils_params->rik_length; + qdf_mem_copy(dst_fils_params->rik, src_fils_params->rik, + dst_fils_params->rik_length); + + dst_fils_params->fils_ft_len = src_fils_params->fils_ft_len; + qdf_mem_copy(dst_fils_params->fils_ft, src_fils_params->fils_ft, + dst_fils_params->fils_ft_len); + + dst_fils_params->realm_len = src_fils_params->realm_len; + qdf_mem_copy(dst_fils_params->realm, src_fils_params->realm, + dst_fils_params->realm_len); +} +#else +static inline void wma_roam_scan_fill_fils_params( + tp_wma_handle wma_handle, + struct roam_offload_scan_params *params, + struct roam_offload_scan_req *roam_req) + +{ } +#endif + +/** + * wma_roam_scan_offload_set_params() - Set roam scan offload params + * @wma_handle: pointer to wma context + * @params: pointer to roam scan offload params + * @roam_req: roam request param + * + * Get roam scan offload parameters from roam req and prepare to fill + * WMI_ROAM_SCAN_MODE TLV + * + * Return: None + */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static void wma_roam_scan_offload_set_params( + tp_wma_handle wma_handle, + struct roam_offload_scan_params *params, + struct roam_offload_scan_req *roam_req) +{ + params->auth_mode = WMI_AUTH_NONE; + if (!roam_req) + return; + params->auth_mode = e_csr_auth_type_to_rsn_authmode + (roam_req->ConnectedNetwork.authentication, + roam_req->ConnectedNetwork.encryption); + + params->disable_self_roam = + !roam_req->enable_self_bss_roam; + params->roam_offload_enabled = roam_req->roam_offload_enabled; + params->roam_offload_params.ho_delay_for_rx = + roam_req->ho_delay_for_rx; + params->roam_offload_params.roam_preauth_retry_count = + roam_req->roam_preauth_retry_count; + params->roam_offload_params.roam_preauth_no_ack_timeout = + roam_req->roam_preauth_no_ack_timeout; + params->prefer_5ghz = roam_req->Prefer5GHz; + params->roam_rssi_cat_gap = roam_req->RoamRssiCatGap; + params->select_5ghz_margin = roam_req->Select5GHzMargin; + params->reassoc_failure_timeout = + roam_req->ReassocFailureTimeout; + params->rokh_id_length = roam_req->R0KH_ID_Length; + qdf_mem_copy(params->rokh_id, roam_req->R0KH_ID, + WMI_ROAM_R0KH_ID_MAX_LEN); + qdf_mem_copy(params->krk, roam_req->KRK, WMI_KRK_KEY_LEN); + qdf_mem_copy(params->btk, roam_req->BTK, WMI_BTK_KEY_LEN); + qdf_mem_copy(params->psk_pmk, roam_req->PSK_PMK, + roam_req->pmk_len); + params->pmk_len = roam_req->pmk_len; + params->roam_key_mgmt_offload_enabled = + roam_req->RoamKeyMgmtOffloadEnabled; + wma_roam_scan_fill_self_caps(wma_handle, + ¶ms->roam_offload_params, roam_req); + params->fw_okc = roam_req->pmkid_modes.fw_okc; + params->fw_pmksa_cache = roam_req->pmkid_modes.fw_pmksa_cache; + params->rct_validity_timer = roam_req->rct_validity_timer; + params->is_adaptive_11r = roam_req->is_adaptive_11r_connection; + params->is_sae_same_pmk = roam_req->is_sae_single_pmk; + params->enable_ft_im_roaming = roam_req->enable_ft_im_roaming; + wma_debug("qos_caps %d, qos_enabled %d, ho_delay_for_rx %d, roam_scan_mode %d SAE single pmk %d roam_preauth: retrycount %d, no_ack_timeout %d", + params->roam_offload_params.qos_caps, + params->roam_offload_params.qos_enabled, + params->roam_offload_params.ho_delay_for_rx, params->mode, + params->is_sae_same_pmk, + params->roam_offload_params.roam_preauth_retry_count, + params->roam_offload_params.roam_preauth_no_ack_timeout); +} +#else +static void wma_roam_scan_offload_set_params( + tp_wma_handle wma_handle, + struct roam_offload_scan_params *params, + struct roam_offload_scan_req *roam_req) +{} +#endif + +/** + * wma_roam_scan_offload_mode() - send roam scan mode request to fw + * @wma_handle: pointer to wma context + * @scan_cmd_fp: start scan command ptr + * @roam_req: roam request param + * @mode: mode + * @vdev_id: vdev id + * + * send WMI_ROAM_SCAN_MODE TLV to firmware. It has a piggyback + * of WMI_ROAM_SCAN_MODE. + * + * Return: QDF status + */ +QDF_STATUS wma_roam_scan_offload_mode(tp_wma_handle wma_handle, + wmi_start_scan_cmd_fixed_param * + scan_cmd_fp, + struct roam_offload_scan_req *roam_req, + uint32_t mode, uint32_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct roam_offload_scan_params *params = + qdf_mem_malloc(sizeof(*params)); + + if (!params) + return QDF_STATUS_E_NOMEM; + + if (!wma_is_vdev_valid(vdev_id)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, vdev_id); + qdf_mem_free(params); + return QDF_STATUS_E_FAILURE; + } + + params->is_roam_req_valid = 0; + params->mode = mode; + params->vdev_id = vdev_id; + if (roam_req) { + params->is_roam_req_valid = 1; + params->min_delay_btw_roam_scans = + roam_req->min_delay_btw_roam_scans; + params->roam_trigger_reason_bitmask = + roam_req->roam_trigger_reason_bitmask; + params->is_ese_assoc = roam_req->IsESEAssoc; + params->is_11r_assoc = roam_req->is_11r_assoc; + params->mdid = roam_req->mdid; + params->assoc_ie_length = roam_req->assoc_ie.length; + qdf_mem_copy(params->assoc_ie, roam_req->assoc_ie.addIEdata, + roam_req->assoc_ie.length); + /* Configure roaming scan behavior (DBS/Non-DBS scan) */ + if (roam_req->roaming_scan_policy) + scan_cmd_fp->scan_ctrl_flags_ext |= + WMI_SCAN_DBS_POLICY_FORCE_NONDBS; + else + scan_cmd_fp->scan_ctrl_flags_ext |= + WMI_SCAN_DBS_POLICY_DEFAULT; + + wma_roam_scan_fill_fils_params(wma_handle, params, roam_req); + } + wma_debug("min_delay_btw_roam_scans %d, roam_tri_reason_bitmask %d", + params->min_delay_btw_roam_scans, + params->roam_trigger_reason_bitmask); + + wma_roam_scan_offload_set_params( + wma_handle, + params, + roam_req); + + status = wmi_unified_roam_scan_offload_mode_cmd(wma_handle->wmi_handle, + scan_cmd_fp, params); + qdf_mem_zero(params, sizeof(*params)); + qdf_mem_free(params); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + return status; +} + +QDF_STATUS +wma_roam_scan_offload_rssi_thresh(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{ + struct roam_offload_scan_rssi_params params = {0}; + QDF_STATUS status = QDF_STATUS_SUCCESS; + int rssi_thresh, rssi_thresh_diff; + struct roam_ext_params *roam_params; + int32_t good_rssi_threshold; + uint32_t hirssi_scan_max_count; + uint32_t hirssi_scan_delta; + int32_t hirssi_upper_bound; + bool db2dbm_enabled; + + /* Send rssi threshold */ + roam_params = &roam_req->roam_params; + db2dbm_enabled = wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_hw_db2dbm_support); + if (db2dbm_enabled) { + rssi_thresh = roam_req->LookupThreshold; + } else { + rssi_thresh = roam_req->LookupThreshold - + WMA_NOISE_FLOOR_DBM_DEFAULT; + rssi_thresh &= 0x000000ff; + } + rssi_thresh_diff = roam_req->OpportunisticScanThresholdDiff; + hirssi_scan_max_count = roam_req->hi_rssi_scan_max_count; + hirssi_scan_delta = roam_req->hi_rssi_scan_rssi_delta; + if (db2dbm_enabled) { + hirssi_upper_bound = roam_req->hi_rssi_scan_rssi_ub; + } else { + hirssi_upper_bound = roam_req->hi_rssi_scan_rssi_ub - + WMA_NOISE_FLOOR_DBM_DEFAULT; + } + + /* fill in threshold values */ + params.vdev_id = roam_req->sessionId; + params.rssi_thresh = rssi_thresh; + params.rssi_thresh_diff = rssi_thresh_diff & 0x000000ff; + params.hi_rssi_scan_max_count = hirssi_scan_max_count; + params.hi_rssi_scan_rssi_delta = hirssi_scan_delta; + params.hi_rssi_scan_rssi_ub = hirssi_upper_bound & 0x00000ff; + params.raise_rssi_thresh_5g = roam_params->raise_rssi_thresh_5g; + params.dense_rssi_thresh_offset = + roam_params->dense_rssi_thresh_offset; + params.dense_min_aps_cnt = roam_params->dense_min_aps_cnt; + params.traffic_threshold = + roam_params->traffic_threshold; + params.initial_dense_status = roam_params->initial_dense_status; + if (db2dbm_enabled) { + params.bg_scan_bad_rssi_thresh = + roam_params->bg_scan_bad_rssi_thresh; + params.roam_data_rssi_threshold = + roam_params->roam_data_rssi_threshold; + } else { + params.bg_scan_bad_rssi_thresh = + roam_params->bg_scan_bad_rssi_thresh - + WMA_NOISE_FLOOR_DBM_DEFAULT; + params.roam_data_rssi_threshold = + roam_params->roam_data_rssi_threshold - + WMA_NOISE_FLOOR_DBM_DEFAULT; + } + + params.bg_scan_client_bitmap = roam_params->bg_scan_client_bitmap; + params.roam_bad_rssi_thresh_offset_2g = + roam_params->roam_bad_rssi_thresh_offset_2g; + if (params.roam_bad_rssi_thresh_offset_2g) + params.flags |= WMI_ROAM_BG_SCAN_FLAGS_2G_TO_5G_ONLY; + params.roam_data_rssi_threshold_triggers = + roam_params->roam_data_rssi_threshold_triggers; + params.rx_data_inactivity_time = roam_params->rx_data_inactivity_time; + + /* + * The current Noise floor in firmware is -96dBm. Penalty/Boost + * threshold is applied on a weaker signal to make it even more weaker. + * So, there is a chance that the user may configure a very low + * Penalty/Boost threshold beyond the noise floor. If that is the case, + * then suppress the penalty/boost threshold to the noise floor. + */ + if (roam_params->raise_rssi_thresh_5g < WMA_NOISE_FLOOR_DBM_DEFAULT) { + if (db2dbm_enabled) { + params.penalty_threshold_5g = WMA_RSSI_MIN_VALUE; + params.boost_threshold_5g = WMA_RSSI_MAX_VALUE; + } else { + params.penalty_threshold_5g = 0; + } + } else { + if (db2dbm_enabled) + params.boost_threshold_5g = + roam_params->raise_rssi_thresh_5g; + else + params.boost_threshold_5g = + (roam_params->raise_rssi_thresh_5g - + WMA_NOISE_FLOOR_DBM_DEFAULT) & 0x000000ff; + } + if (roam_params->drop_rssi_thresh_5g < WMA_NOISE_FLOOR_DBM_DEFAULT) { + if (db2dbm_enabled) + params.penalty_threshold_5g = WMA_RSSI_MIN_VALUE; + else + params.penalty_threshold_5g = 0; + + } else { + if (db2dbm_enabled) + params.penalty_threshold_5g = + roam_params->drop_rssi_thresh_5g; + else + params.penalty_threshold_5g = + (roam_params->drop_rssi_thresh_5g - + WMA_NOISE_FLOOR_DBM_DEFAULT) & 0x000000ff; + } + params.raise_factor_5g = roam_params->raise_factor_5g; + params.drop_factor_5g = roam_params->drop_factor_5g; + params.max_raise_rssi_5g = roam_params->max_raise_rssi_5g; + params.max_drop_rssi_5g = roam_params->max_drop_rssi_5g; + + if (roam_params->good_rssi_roam) + good_rssi_threshold = WMA_NOISE_FLOOR_DBM_DEFAULT; + else + good_rssi_threshold = 0; + + if (db2dbm_enabled) + params.good_rssi_threshold = good_rssi_threshold; + else + params.good_rssi_threshold = (good_rssi_threshold - + WMA_NOISE_FLOOR_DBM_DEFAULT) & + 0x000000ff; + + if (roam_req->early_stop_scan_enable) { + if (db2dbm_enabled) { + params.roam_earlystop_thres_min = + roam_req->early_stop_scan_min_threshold; + params.roam_earlystop_thres_max = + roam_req->early_stop_scan_max_threshold; + } else { + params.roam_earlystop_thres_min = + roam_req->early_stop_scan_min_threshold - + WMA_NOISE_FLOOR_DBM_DEFAULT; + params.roam_earlystop_thres_max = + roam_req->early_stop_scan_max_threshold - + WMA_NOISE_FLOOR_DBM_DEFAULT; + } + } else { + if (db2dbm_enabled) { + params.roam_earlystop_thres_min = + WMA_RSSI_MIN_VALUE; + params.roam_earlystop_thres_max = + WMA_RSSI_MIN_VALUE; + } else { + params.roam_earlystop_thres_min = 0; + params.roam_earlystop_thres_max = 0; + } + } + params.rssi_thresh_offset_5g = + roam_req->rssi_thresh_offset_5g; + + wma_debug("good_rssi_threshold %d, early_stop_thresholds en=%d, min=%d, max=%d roam_scan_rssi_thresh=%d, roam_rssi_thresh_diff=%d", + params.good_rssi_threshold, roam_req->early_stop_scan_enable, + params.roam_earlystop_thres_min, + params.roam_earlystop_thres_max, rssi_thresh, + rssi_thresh_diff); + + status = wmi_unified_roam_scan_offload_rssi_thresh_cmd( + wma_handle->wmi_handle, ¶ms); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("roam_scan_offload_rssi_thresh_cmd failed %d", status); + return status; + } + + wma_debug("hirssi max cnt %d, delta %d, hirssi upper bound %d dense rssi thresh offset %d, dense min aps cnt %d, traffic_threshold %d dense_status=%d", + hirssi_scan_max_count, hirssi_scan_delta, hirssi_upper_bound, + roam_params->dense_rssi_thresh_offset, + roam_params->dense_min_aps_cnt, + roam_params->traffic_threshold, + roam_params->initial_dense_status); + wma_debug("BG Scan Bad RSSI:%d, bitmap:0x%x Offset for 2G to 5G Roam:%d", + roam_params->bg_scan_bad_rssi_thresh, + roam_params->bg_scan_client_bitmap, + roam_params->roam_bad_rssi_thresh_offset_2g); + + return status; +} + +/** + * wma_roam_scan_offload_scan_period() - set roam offload scan period + * @wma_handle: wma handle + * @roam_req: Pointer to roam_offload_scan_req + * + * Send WMI_ROAM_SCAN_PERIOD parameters to fw. + * + * Return: QDF status + */ +QDF_STATUS +wma_roam_scan_offload_scan_period(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{ + uint8_t vdev_id; + struct roam_scan_period_params scan_period_params; + + vdev_id = roam_req->sessionId; + if (!wma_is_vdev_valid(vdev_id)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + + scan_period_params.vdev_id = vdev_id; + scan_period_params.scan_period = roam_req->EmptyRefreshScanPeriod; + scan_period_params.scan_age = (3 * roam_req->EmptyRefreshScanPeriod); + scan_period_params.roam_scan_inactivity_time = + roam_req->roam_scan_inactivity_time; + scan_period_params.roam_inactive_data_packet_count = + roam_req->roam_inactive_data_packet_count; + scan_period_params.roam_scan_period_after_inactivity = + roam_req->roam_scan_period_after_inactivity; + scan_period_params.full_scan_period = roam_req->full_roam_scan_period; + + return wmi_unified_roam_scan_offload_scan_period(wma_handle->wmi_handle, + &scan_period_params); +} + +/** + * wma_roam_scan_offload_rssi_change() - set roam offload RSSI change threshold + * @wma_handle: wma handle + * @rssi_change_thresh: RSSI Change threshold + * @bcn_rssi_weight: beacon RSSI weight + * @vdev_id: vdev id + * + * Send WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD parameters to fw. + * + * Return: QDF status + */ +QDF_STATUS wma_roam_scan_offload_rssi_change(tp_wma_handle wma_handle, + uint32_t vdev_id, + int32_t rssi_change_thresh, + uint32_t bcn_rssi_weight, + uint32_t hirssi_delay_btw_scans) +{ + if (!wma_is_vdev_valid(vdev_id)) { + wma_err("Invalid vdev id:%d", vdev_id); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_roam_scan_offload_rssi_change_cmd( + wma_handle->wmi_handle, + vdev_id, rssi_change_thresh, + bcn_rssi_weight, hirssi_delay_btw_scans); +} + +/** + * wma_roam_scan_offload_chan_list() - set roam offload channel list + * @wma_handle: wma handle + * @chan_count: channel count + * @chan_freq_list: channel list + * @list_type: list type + * @vdev_id: vdev id + * + * Set roam offload channel list. + * + * Return: QDF status + */ +QDF_STATUS wma_roam_scan_offload_chan_list(tp_wma_handle wma_handle, + uint8_t chan_count, + uint32_t *chan_freq_list, + uint8_t list_type, uint32_t vdev_id) +{ + QDF_STATUS status; + int i; + uint32_t *chan_list_mhz = NULL, buf_len = 0, len = 0; + uint8_t *chan_buff = NULL; + + if (!wma_is_vdev_valid(vdev_id)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + + if (chan_count) { + chan_list_mhz = qdf_mem_malloc(chan_count * sizeof(*chan_list_mhz)); + if (!chan_list_mhz) + return QDF_STATUS_E_NOMEM; + + /* + * Buffer of (num channl * 5) + 1 to consider the 4 char freq + * and 1 space after it for each channel and 1 to end the string + * with NULL. + */ + buf_len = (chan_count * 5) + 1; + chan_buff = qdf_mem_malloc(buf_len); + } + + for (i = 0; ((i < chan_count) && + (i < SIR_ROAM_MAX_CHANNELS)); i++) { + chan_list_mhz[i] = chan_freq_list[i]; + if (chan_buff) + len += qdf_scnprintf(chan_buff + len, buf_len - len, + " %d", chan_list_mhz[i]); + } + + if (chan_buff) { + wma_debug("Freq list[%d]:%s", chan_count, chan_buff); + qdf_mem_free(chan_buff); + } + + status = wmi_unified_roam_scan_offload_chan_list_cmd( + wma_handle->wmi_handle, + chan_count, chan_list_mhz, + list_type, vdev_id); + qdf_mem_free(chan_list_mhz); + + return status; +} + +/** + * e_csr_auth_type_to_rsn_authmode() - map csr auth type to rsn authmode + * @authtype: CSR authtype + * @encr: CSR Encryption + * + * Map CSR's authentication type into RSN auth mode used by firmware + * + * Return: WMI RSN auth mode + */ +A_UINT32 e_csr_auth_type_to_rsn_authmode(enum csr_akm_type authtype, + eCsrEncryptionType encr) +{ + switch (authtype) { + case eCSR_AUTH_TYPE_OPEN_SYSTEM: + return WMI_AUTH_OPEN; + case eCSR_AUTH_TYPE_WPA: + return WMI_AUTH_WPA; + case eCSR_AUTH_TYPE_WPA_PSK: + return WMI_AUTH_WPA_PSK; + case eCSR_AUTH_TYPE_RSN: + return WMI_AUTH_RSNA; + case eCSR_AUTH_TYPE_RSN_PSK: + return WMI_AUTH_RSNA_PSK; + case eCSR_AUTH_TYPE_FT_RSN: + return WMI_AUTH_FT_RSNA; + case eCSR_AUTH_TYPE_FT_RSN_PSK: + return WMI_AUTH_FT_RSNA_PSK; +#ifdef FEATURE_WLAN_WAPI + case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE: + return WMI_AUTH_WAPI; + case eCSR_AUTH_TYPE_WAPI_WAI_PSK: + return WMI_AUTH_WAPI_PSK; +#endif /* FEATURE_WLAN_WAPI */ +#ifdef FEATURE_WLAN_ESE + case eCSR_AUTH_TYPE_CCKM_WPA: + return WMI_AUTH_CCKM_WPA; + case eCSR_AUTH_TYPE_CCKM_RSN: + return WMI_AUTH_CCKM_RSNA; +#endif /* FEATURE_WLAN_ESE */ +#ifdef WLAN_FEATURE_11W + case eCSR_AUTH_TYPE_RSN_PSK_SHA256: + return WMI_AUTH_RSNA_PSK_SHA256; + case eCSR_AUTH_TYPE_RSN_8021X_SHA256: + return WMI_AUTH_RSNA_8021X_SHA256; +#endif /* WLAN_FEATURE_11W */ + case eCSR_AUTH_TYPE_NONE: + case eCSR_AUTH_TYPE_AUTOSWITCH: + /* In case of WEP and other keys, NONE means OPEN auth */ + if (encr == eCSR_ENCRYPT_TYPE_WEP40_STATICKEY || + encr == eCSR_ENCRYPT_TYPE_WEP104_STATICKEY || + encr == eCSR_ENCRYPT_TYPE_WEP40 || + encr == eCSR_ENCRYPT_TYPE_WEP104 || + encr == eCSR_ENCRYPT_TYPE_TKIP || + encr == eCSR_ENCRYPT_TYPE_AES || + encr == eCSR_ENCRYPT_TYPE_AES_GCMP || + encr == eCSR_ENCRYPT_TYPE_AES_GCMP_256) { + return WMI_AUTH_OPEN; + } + return WMI_AUTH_NONE; + case eCSR_AUTH_TYPE_SHARED_KEY: + return WMI_AUTH_SHARED; + case eCSR_AUTH_TYPE_FILS_SHA256: + return WMI_AUTH_RSNA_FILS_SHA256; + case eCSR_AUTH_TYPE_FILS_SHA384: + return WMI_AUTH_RSNA_FILS_SHA384; + case eCSR_AUTH_TYPE_FT_FILS_SHA256: + return WMI_AUTH_FT_RSNA_FILS_SHA256; + case eCSR_AUTH_TYPE_FT_FILS_SHA384: + return WMI_AUTH_FT_RSNA_FILS_SHA384; + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA256: + return WMI_AUTH_RSNA_SUITE_B_8021X_SHA256; + case eCSR_AUTH_TYPE_SUITEB_EAP_SHA384: + return WMI_AUTH_RSNA_SUITE_B_8021X_SHA384; + case eCSR_AUTH_TYPE_OWE: + return WMI_AUTH_WPA3_OWE; + case eCSR_AUTH_TYPE_SAE: + return WMI_AUTH_WPA3_SAE; + case eCSR_AUTH_TYPE_FT_SAE: + return WMI_AUTH_FT_RSNA_SAE; + case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384: + return WMI_AUTH_FT_RSNA_SUITE_B_8021X_SHA384; + default: + return WMI_AUTH_NONE; + } +} + +/** + * e_csr_encryption_type_to_rsn_cipherset() - map csr enc type to ESN cipher + * @encr: CSR Encryption + * + * Map CSR's encryption type into RSN cipher types used by firmware + * + * Return: WMI RSN cipher + */ +A_UINT32 e_csr_encryption_type_to_rsn_cipherset(eCsrEncryptionType encr) +{ + + switch (encr) { + case eCSR_ENCRYPT_TYPE_WEP40_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP104_STATICKEY: + case eCSR_ENCRYPT_TYPE_WEP40: + case eCSR_ENCRYPT_TYPE_WEP104: + return WMI_CIPHER_WEP; + case eCSR_ENCRYPT_TYPE_TKIP: + return WMI_CIPHER_TKIP; + case eCSR_ENCRYPT_TYPE_AES: + return WMI_CIPHER_AES_CCM; + /* FWR will use key length to distinguish GCMP 128 or 256 */ + case eCSR_ENCRYPT_TYPE_AES_GCMP: + case eCSR_ENCRYPT_TYPE_AES_GCMP_256: + return WMI_CIPHER_AES_GCM; +#ifdef FEATURE_WLAN_WAPI + case eCSR_ENCRYPT_TYPE_WPI: + return WMI_CIPHER_WAPI; +#endif /* FEATURE_WLAN_WAPI */ + case eCSR_ENCRYPT_TYPE_ANY: + return WMI_CIPHER_ANY; + case eCSR_ENCRYPT_TYPE_NONE: + default: + return WMI_CIPHER_NONE; + } +} + +/** + * wma_convert_gp_mgmt_cipher_to_target_cipher_type() - map csr ani group mgmt + * enc type to RSN cipher + * @encr: CSR Encryption + * + * Map CSR's group management cipher type into RSN cipher types used by firmware + * + * Return: WMI RSN cipher + */ +static uint32_t +wma_convert_gp_mgmt_cipher_to_target_cipher_type(tAniEdType cipher_type) +{ + switch (cipher_type) { + /* BIP-CMAC-128 (00:0f:ac: 0x06) */ + case eSIR_ED_AES_128_CMAC: + return WMI_CIPHER_AES_CMAC; + + /* BIP-GMAC-128 (00:0f:ac: 0x0b) */ + case eSIR_ED_AES_GMAC_128: + return WMI_CIPHER_AES_GMAC; + + /* BIP-GMAC-256(00:0f:ac: 0x0c)*/ + case eSIR_ED_AES_GMAC_256: + return WMI_CIPHER_BIP_GMAC_256; + case eSIR_ED_NONE: + default: + return WMI_CIPHER_NONE; + } +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * wma_roam_scan_get_cckm_mode() - Get the CCKM auth mode + * @roam_req: Roaming request buffer + * @auth_mode: Auth mode to be converted + * + * Based on LFR2.0 or LFR3.0, return the proper auth type + * + * Return: if LFR2.0, then return WMI_AUTH_CCKM for backward compatibility + * if LFR3.0 then return the appropriate auth type + */ +static +uint32_t wma_roam_scan_get_cckm_mode(struct roam_offload_scan_req *roam_req, + uint32_t auth_mode) +{ + if (roam_req->roam_offload_enabled) + return auth_mode; + else + return WMI_AUTH_CCKM; + +} +#else +static +uint32_t wma_roam_scan_get_cckm_mode(struct roam_offload_scan_req *roam_req, + uint32_t auth_mode) +{ + return WMI_AUTH_CCKM; +} +#endif +/** + * wma_roam_scan_fill_ap_profile() - fill ap_profile + * @roam_req: roam offload scan request + * @profile: ap profile + * + * Fill ap_profile structure from configured parameters + * + * Return: none + */ +static void +wma_roam_scan_fill_ap_profile(struct roam_offload_scan_req *roam_req, + struct ap_profile *profile) +{ + uint32_t rsn_authmode; + + qdf_mem_zero(profile, sizeof(*profile)); + if (!roam_req) { + profile->ssid.length = 0; + profile->ssid.mac_ssid[0] = 0; + profile->rsn_authmode = WMI_AUTH_NONE; + profile->rsn_ucastcipherset = WMI_CIPHER_NONE; + profile->rsn_mcastcipherset = WMI_CIPHER_NONE; + profile->rsn_mcastmgmtcipherset = WMI_CIPHER_NONE; + profile->rssi_threshold = WMA_ROAM_RSSI_DIFF_DEFAULT; + + return; + } + + profile->ssid.length = roam_req->ConnectedNetwork.ssId.length; + qdf_mem_copy(profile->ssid.mac_ssid, + roam_req->ConnectedNetwork.ssId.ssId, + profile->ssid.length); + profile->rsn_authmode = + e_csr_auth_type_to_rsn_authmode( + roam_req->ConnectedNetwork.authentication, + roam_req->ConnectedNetwork.encryption); + rsn_authmode = profile->rsn_authmode; + + if (rsn_authmode == WMI_AUTH_CCKM_WPA || + rsn_authmode == WMI_AUTH_CCKM_RSNA) + profile->rsn_authmode = + wma_roam_scan_get_cckm_mode(roam_req, rsn_authmode); + + /* Pairwise cipher suite */ + profile->rsn_ucastcipherset = + e_csr_encryption_type_to_rsn_cipherset( + roam_req->ConnectedNetwork.encryption); + + /* Group cipher suite */ + profile->rsn_mcastcipherset = + e_csr_encryption_type_to_rsn_cipherset( + roam_req->ConnectedNetwork.mcencryption); + + /* Group management cipher suite */ + profile->rsn_mcastmgmtcipherset = + wma_convert_gp_mgmt_cipher_to_target_cipher_type( + roam_req->ConnectedNetwork.gp_mgmt_cipher_suite); + + profile->rssi_threshold = roam_req->RoamRssiDiff; + if (roam_req->rssi_abs_thresh) + profile->rssi_abs_thresh = roam_req->rssi_abs_thresh; +#ifdef WLAN_FEATURE_11W + if (roam_req->ConnectedNetwork.mfp_enabled) + profile->flags |= WMI_AP_PROFILE_FLAG_PMF; +#endif +} + +/** + * wma_process_set_pdev_ie_req() - process the pdev set IE req + * @wma: Pointer to wma handle + * @ie_params: Pointer to IE data. + * + * Sends the WMI req to set the IE to FW. + * + * Return: None + */ +void wma_process_set_pdev_ie_req(tp_wma_handle wma, + struct set_ie_param *ie_params) +{ + if (ie_params->ie_type == DOT11_HT_IE) + wma_process_set_pdev_ht_ie_req(wma, ie_params); + if (ie_params->ie_type == DOT11_VHT_IE) + wma_process_set_pdev_vht_ie_req(wma, ie_params); + + qdf_mem_free(ie_params->ie_ptr); +} + +/** + * wma_process_set_pdev_ht_ie_req() - sends HT IE data to FW + * @wma: Pointer to wma handle + * @ie_params: Pointer to IE data. + * @nss: Nss values to prepare the HT IE. + * + * Sends the WMI req to set the HT IE to FW. + * + * Return: None + */ +void wma_process_set_pdev_ht_ie_req(tp_wma_handle wma, + struct set_ie_param *ie_params) +{ + QDF_STATUS status; + wmi_pdev_set_ht_ie_cmd_fixed_param *cmd; + wmi_buf_t buf; + uint16_t len; + uint16_t ie_len_pad; + uint8_t *buf_ptr; + + len = sizeof(*cmd) + WMI_TLV_HDR_SIZE; + ie_len_pad = roundup(ie_params->ie_len, sizeof(uint32_t)); + len += ie_len_pad; + + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return; + + cmd = (wmi_pdev_set_ht_ie_cmd_fixed_param *) wmi_buf_data(buf); + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_pdev_set_ht_ie_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_pdev_set_ht_ie_cmd_fixed_param)); + cmd->reserved0 = 0; + cmd->ie_len = ie_params->ie_len; + cmd->tx_streams = ie_params->nss; + cmd->rx_streams = ie_params->nss; + WMA_LOGD("Setting pdev HT ie with Nss = %u", + ie_params->nss); + buf_ptr = (uint8_t *)cmd + sizeof(*cmd); + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, ie_len_pad); + if (ie_params->ie_len) { + qdf_mem_copy(buf_ptr + WMI_TLV_HDR_SIZE, + (uint8_t *)ie_params->ie_ptr, + ie_params->ie_len); + } + + status = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_PDEV_SET_HT_CAP_IE_CMDID); + if (QDF_IS_STATUS_ERROR(status)) + wmi_buf_free(buf); +} + +/** + * wma_process_set_pdev_vht_ie_req() - sends VHT IE data to FW + * @wma: Pointer to wma handle + * @ie_params: Pointer to IE data. + * @nss: Nss values to prepare the VHT IE. + * + * Sends the WMI req to set the VHT IE to FW. + * + * Return: None + */ +void wma_process_set_pdev_vht_ie_req(tp_wma_handle wma, + struct set_ie_param *ie_params) +{ + QDF_STATUS status; + wmi_pdev_set_vht_ie_cmd_fixed_param *cmd; + wmi_buf_t buf; + uint16_t len; + uint16_t ie_len_pad; + uint8_t *buf_ptr; + + len = sizeof(*cmd) + WMI_TLV_HDR_SIZE; + ie_len_pad = roundup(ie_params->ie_len, sizeof(uint32_t)); + len += ie_len_pad; + + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return; + + cmd = (wmi_pdev_set_vht_ie_cmd_fixed_param *) wmi_buf_data(buf); + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_pdev_set_vht_ie_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_pdev_set_vht_ie_cmd_fixed_param)); + cmd->reserved0 = 0; + cmd->ie_len = ie_params->ie_len; + cmd->tx_streams = ie_params->nss; + cmd->rx_streams = ie_params->nss; + WMA_LOGD("Setting pdev VHT ie with Nss = %u", + ie_params->nss); + buf_ptr = (uint8_t *)cmd + sizeof(*cmd); + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, ie_len_pad); + if (ie_params->ie_len) { + qdf_mem_copy(buf_ptr + WMI_TLV_HDR_SIZE, + (uint8_t *)ie_params->ie_ptr, + ie_params->ie_len); + } + + status = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_PDEV_SET_VHT_CAP_IE_CMDID); + if (QDF_IS_STATUS_ERROR(status)) + wmi_buf_free(buf); +} + +/** + * wma_roam_scan_scan_params() - fill roam scan params + * @wma_handle: wma handle + * @mac: Mac ptr + * @scan_params: scan parameters + * @roam_req: NULL if this routine is called before connect + * It will be non-NULL if called after assoc. + * + * Fill scan_params structure from configured parameters + * + * Return: none + */ +void wma_roam_scan_fill_scan_params(tp_wma_handle wma_handle, + struct mac_context *mac, + struct roam_offload_scan_req *roam_req, + wmi_start_scan_cmd_fixed_param * + scan_params) +{ + uint8_t channels_per_burst = 0; + + if (!mac) { + WMA_LOGE("%s: mac is NULL", __func__); + return; + } + + qdf_mem_zero(scan_params, sizeof(wmi_start_scan_cmd_fixed_param)); + scan_params->scan_ctrl_flags = WMI_SCAN_ADD_CCK_RATES | + WMI_SCAN_ADD_OFDM_RATES | + WMI_SCAN_ADD_DS_IE_IN_PROBE_REQ; + if (roam_req) { + /* Parameters updated after association is complete */ + wlan_scan_cfg_get_passive_dwelltime(wma_handle->psoc, + &scan_params->dwell_time_passive); + /* + * Here is the formula, + * T(HomeAway) = N * T(dwell) + (N+1) * T(cs) + * where N is number of channels scanned in single burst + */ + scan_params->dwell_time_active = + roam_req->NeighborScanChannelMaxTime; + if (roam_req->HomeAwayTime < + 2 * WMA_ROAM_SCAN_CHANNEL_SWITCH_TIME) { + /* clearly we can't follow home away time. + * Make it a split scan. + */ + scan_params->burst_duration = 0; + } else { + channels_per_burst = + (roam_req->HomeAwayTime - + WMA_ROAM_SCAN_CHANNEL_SWITCH_TIME) + / (scan_params->dwell_time_active + + WMA_ROAM_SCAN_CHANNEL_SWITCH_TIME); + + if (channels_per_burst < 1) { + /* dwell time and home away time conflicts */ + /* we will override dwell time */ + scan_params->dwell_time_active = + roam_req->HomeAwayTime - + 2 * WMA_ROAM_SCAN_CHANNEL_SWITCH_TIME; + scan_params->burst_duration = + scan_params->dwell_time_active; + } else { + scan_params->burst_duration = + channels_per_burst * + scan_params->dwell_time_active; + } + } + if (roam_req->allowDFSChannelRoam == + SIR_ROAMING_DFS_CHANNEL_ENABLED_NORMAL + && roam_req->HomeAwayTime > 0 + && roam_req->ChannelCacheType != CHANNEL_LIST_STATIC) { + /* Roaming on DFS channels is supported and it is not + * app channel list. It is ok to override homeAwayTime + * to accommodate DFS dwell time in burst + * duration. + */ + scan_params->burst_duration = + QDF_MAX(scan_params->burst_duration, + scan_params->dwell_time_passive); + } + scan_params->min_rest_time = + roam_req->neighbor_scan_min_timer_period; + scan_params->max_rest_time = roam_req->NeighborScanTimerPeriod; + scan_params->repeat_probe_time = (roam_req->nProbes > 0) ? + QDF_MAX(scan_params->dwell_time_active / + roam_req->nProbes, 1) : 0; + scan_params->probe_spacing_time = 0; + scan_params->probe_delay = 0; + /* 30 seconds for full scan cycle */ + scan_params->max_scan_time = WMA_HW_DEF_SCAN_MAX_DURATION; + scan_params->idle_time = scan_params->min_rest_time; + scan_params->n_probes = roam_req->nProbes; + if (roam_req->allowDFSChannelRoam == + SIR_ROAMING_DFS_CHANNEL_DISABLED) { + scan_params->scan_ctrl_flags |= WMI_SCAN_BYPASS_DFS_CHN; + } else { + /* Roaming scan on DFS channel is allowed. + * No need to change any flags for default + * allowDFSChannelRoam = 1. + * Special case where static channel list is given by\ + * application that contains DFS channels. + * Assume that the application has knowledge of matching + * APs being active and that probe request transmission + * is permitted on those channel. + * Force active scans on those channels. + */ + + if (roam_req->allowDFSChannelRoam == + SIR_ROAMING_DFS_CHANNEL_ENABLED_ACTIVE && + roam_req->ChannelCacheType == CHANNEL_LIST_STATIC && + roam_req->ConnectedNetwork.ChannelCount > 0) { + scan_params->scan_ctrl_flags |= + WMI_SCAN_FLAG_FORCE_ACTIVE_ON_DFS; + } + } + WMI_SCAN_SET_DWELL_MODE(scan_params->scan_ctrl_flags, + roam_req->roamscan_adaptive_dwell_mode); + + } else { + /* roam_req = NULL during initial or pre-assoc invocation */ + scan_params->dwell_time_active = + WMA_ROAM_DWELL_TIME_ACTIVE_DEFAULT; + scan_params->dwell_time_passive = + WMA_ROAM_DWELL_TIME_PASSIVE_DEFAULT; + scan_params->min_rest_time = WMA_ROAM_MIN_REST_TIME_DEFAULT; + scan_params->max_rest_time = WMA_ROAM_MAX_REST_TIME_DEFAULT; + scan_params->repeat_probe_time = 0; + scan_params->probe_spacing_time = 0; + scan_params->probe_delay = 0; + scan_params->max_scan_time = WMA_HW_DEF_SCAN_MAX_DURATION; + scan_params->idle_time = scan_params->min_rest_time; + scan_params->burst_duration = 0; + scan_params->n_probes = 0; + } + + wma_debug("Rome roam scan parameters: dwell time: active %d passive %d, minrest %d max rest %d repeat probe time %d n_probes %d", + scan_params->dwell_time_active, + scan_params->dwell_time_passive, scan_params->min_rest_time, + scan_params->max_rest_time, scan_params->repeat_probe_time, + scan_params->n_probes); + + wma_debug("max_scan_time %d, idle_time %d, burst_duration %d, scan_ctrl_flags 0x%x", + scan_params->max_scan_time, scan_params->idle_time, + scan_params->burst_duration, scan_params->scan_ctrl_flags); +} + +/** + * wma_roam_scan_offload_ap_profile() - set roam ap profile in fw + * @wma_handle: wma handle + * @mac_ctx: Mac ptr + * @roam_req: Request which contains the ap profile + * + * Send WMI_ROAM_AP_PROFILE to firmware + * + * Return: QDF status + */ +static QDF_STATUS wma_roam_scan_offload_ap_profile(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{ + struct ap_profile_params ap_profile; + bool db2dbm_enabled; + + if (!wma_is_vdev_valid(roam_req->sessionId)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, + roam_req->sessionId); + return QDF_STATUS_E_FAILURE; + } + ap_profile.vdev_id = roam_req->sessionId; + wma_roam_scan_fill_ap_profile(roam_req, &ap_profile.profile); + + db2dbm_enabled = wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_hw_db2dbm_support); + if (!ap_profile.profile.rssi_abs_thresh) { + if (db2dbm_enabled) + ap_profile.profile.rssi_abs_thresh = WMA_RSSI_MIN_VALUE; + } else { + if (!db2dbm_enabled) + ap_profile.profile.rssi_abs_thresh -= + WMA_NOISE_FLOOR_DBM_DEFAULT; + } + ap_profile.param = roam_req->score_params; + ap_profile.min_rssi_params[DEAUTH_MIN_RSSI] = + roam_req->min_rssi_params[DEAUTH_MIN_RSSI]; + ap_profile.min_rssi_params[BMISS_MIN_RSSI] = + roam_req->min_rssi_params[BMISS_MIN_RSSI]; + ap_profile.min_rssi_params[MIN_RSSI_2G_TO_5G_ROAM] = + roam_req->min_rssi_params[MIN_RSSI_2G_TO_5G_ROAM]; + if (!db2dbm_enabled) { + ap_profile.min_rssi_params[DEAUTH_MIN_RSSI].min_rssi -= + WMA_NOISE_FLOOR_DBM_DEFAULT; + ap_profile.min_rssi_params[DEAUTH_MIN_RSSI].min_rssi &= + 0x000000ff; + + ap_profile.min_rssi_params[BMISS_MIN_RSSI].min_rssi -= + WMA_NOISE_FLOOR_DBM_DEFAULT; + ap_profile.min_rssi_params[BMISS_MIN_RSSI].min_rssi &= + 0x000000ff; + ap_profile.min_rssi_params[MIN_RSSI_2G_TO_5G_ROAM].min_rssi -= + WMA_NOISE_FLOOR_DBM_DEFAULT; + ap_profile.min_rssi_params[MIN_RSSI_2G_TO_5G_ROAM].min_rssi &= + 0x000000ff; + } + + ap_profile.score_delta_param[IDLE_ROAM_TRIGGER] = + roam_req->score_delta_param[IDLE_ROAM_TRIGGER]; + ap_profile.score_delta_param[BTM_ROAM_TRIGGER] = + roam_req->score_delta_param[BTM_ROAM_TRIGGER]; + + return wmi_unified_send_roam_scan_offload_ap_cmd(wma_handle->wmi_handle, + &ap_profile); +} + +/** + * wma_roam_scan_filter() - Filter to be applied while roaming + * @wma_handle: Global WMA Handle + * @roam_req: Request which contains the filters + * + * There are filters such as whitelist, blacklist and preferred + * list that need to be applied to the scan results to form the + * probable candidates for roaming. + * + * Return: Return success upon successfully passing the + * parameters to the firmware, otherwise failure. + */ +static QDF_STATUS wma_roam_scan_filter(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{ + int i; + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t num_bssid_black_list = 0, num_ssid_white_list = 0, + num_bssid_preferred_list = 0, num_rssi_rejection_ap = 0; + uint32_t op_bitmap = 0; + struct roam_ext_params *roam_params; + struct roam_scan_filter_params *params; + struct lca_disallow_config_params *lca_config_params; + + if (!wma_is_vdev_valid(roam_req->sessionId)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, + roam_req->sessionId); + return QDF_STATUS_E_FAILURE; + } + + params = qdf_mem_malloc(sizeof(struct roam_scan_filter_params)); + if (!params) + return QDF_STATUS_E_NOMEM; + + roam_params = &roam_req->roam_params; + lca_config_params = &roam_req->lca_config_params; + if (roam_req->Command != ROAM_SCAN_OFFLOAD_STOP) { + switch (roam_req->reason) { + case REASON_ROAM_SET_BLACKLIST_BSSID: + op_bitmap |= 0x1; + num_bssid_black_list = + roam_params->num_bssid_avoid_list; + break; + case REASON_ROAM_SET_SSID_ALLOWED: + op_bitmap |= 0x2; + num_ssid_white_list = + roam_params->num_ssid_allowed_list; + break; + case REASON_ROAM_SET_FAVORED_BSSID: + op_bitmap |= 0x4; + num_bssid_preferred_list = + roam_params->num_bssid_favored; + break; + case REASON_CTX_INIT: + if (roam_req->Command == ROAM_SCAN_OFFLOAD_START) { + params->lca_disallow_config_present = true; + op_bitmap |= + ROAM_FILTER_OP_BITMAP_LCA_DISALLOW | + ROAM_FILTER_OP_BITMAP_RSSI_REJECTION_OCE; + num_rssi_rejection_ap = + roam_params->num_rssi_rejection_ap; + } else { + WMA_LOGD("%s : Roam Filter need not be sent", __func__); + qdf_mem_free(params); + return QDF_STATUS_SUCCESS; + } + break; + default: + WMA_LOGD("%s : Roam Filter need not be sent", __func__); + qdf_mem_free(params); + return QDF_STATUS_SUCCESS; + } + } else { + /* In case of STOP command, reset all the variables + * except for blacklist BSSID which should be retained + * across connections. + */ + op_bitmap = 0x2 | 0x4; + num_ssid_white_list = roam_params->num_ssid_allowed_list; + num_bssid_preferred_list = roam_params->num_bssid_favored; + } + + /* fill in fixed values */ + params->vdev_id = roam_req->sessionId; + params->op_bitmap = op_bitmap; + params->num_bssid_black_list = num_bssid_black_list; + params->num_ssid_white_list = num_ssid_white_list; + params->num_bssid_preferred_list = num_bssid_preferred_list; + params->num_rssi_rejection_ap = num_rssi_rejection_ap; + params->delta_rssi = wlan_blm_get_rssi_blacklist_threshold(wma_handle->pdev); + qdf_mem_copy(params->bssid_avoid_list, roam_params->bssid_avoid_list, + MAX_BSSID_AVOID_LIST * sizeof(struct qdf_mac_addr)); + + for (i = 0; i < num_ssid_white_list; i++) { + qdf_mem_copy(params->ssid_allowed_list[i].mac_ssid, + roam_params->ssid_allowed_list[i].ssId, + roam_params->ssid_allowed_list[i].length); + params->ssid_allowed_list[i].length = + roam_params->ssid_allowed_list[i].length; + wma_debug("SSID %d: %.*s", i, + params->ssid_allowed_list[i].length, + params->ssid_allowed_list[i].mac_ssid); + } + + for (i = 0; i < params->num_bssid_black_list; i++) + wma_debug("Blacklist bssid[%d]:" QDF_MAC_ADDR_FMT, i, + QDF_MAC_ADDR_REF(params->bssid_avoid_list[i].bytes)); + qdf_mem_copy(params->bssid_favored, roam_params->bssid_favored, + MAX_BSSID_FAVORED * sizeof(struct qdf_mac_addr)); + qdf_mem_copy(params->bssid_favored_factor, + roam_params->bssid_favored_factor, MAX_BSSID_FAVORED); + qdf_mem_copy(params->rssi_rejection_ap, + roam_params->rssi_reject_bssid_list, + MAX_RSSI_AVOID_BSSID_LIST * + sizeof(struct reject_ap_config_params)); + + for (i = 0; i < params->num_bssid_preferred_list; i++) + wma_debug("Preferred Bssid[%d]:"QDF_MAC_ADDR_FMT" score: %d", i, + QDF_MAC_ADDR_REF(params->bssid_favored[i].bytes), + params->bssid_favored_factor[i]); + + if (params->lca_disallow_config_present) { + params->disallow_duration + = lca_config_params->disallow_duration; + params->rssi_channel_penalization + = lca_config_params->rssi_channel_penalization; + params->num_disallowed_aps + = lca_config_params->num_disallowed_aps; + wma_debug("disallow_dur %d rssi_chan_pen %d num_disallowed_aps %d", + params->disallow_duration, + params->rssi_channel_penalization, + params->num_disallowed_aps); + } + status = wmi_unified_roam_scan_filter_cmd(wma_handle->wmi_handle, + params); + + qdf_mem_free(params); + return status; +} + +/** + * wma_roam_scan_bmiss_cnt() - set bmiss count to fw + * @wma_handle: wma handle + * @first_bcnt: first bmiss count + * @final_bcnt: final bmiss count + * @vdev_id: vdev id + * + * set first & final biss count to fw. + * + * Return: QDF status + */ +QDF_STATUS wma_roam_scan_bmiss_cnt(tp_wma_handle wma_handle, + A_INT32 first_bcnt, + A_UINT32 final_bcnt, uint32_t vdev_id) +{ + QDF_STATUS status; + + WMA_LOGD("%s: first_bcnt: %d, final_bcnt: %d", __func__, first_bcnt, + final_bcnt); + + status = wma_vdev_set_param(wma_handle->wmi_handle, + vdev_id, WMI_VDEV_PARAM_BMISS_FIRST_BCNT, + first_bcnt); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("wma_vdev_set_param WMI_VDEV_PARAM_BMISS_FIRST_BCNT returned Error %d", + status); + return status; + } + + status = wma_vdev_set_param(wma_handle->wmi_handle, + vdev_id, WMI_VDEV_PARAM_BMISS_FINAL_BCNT, + final_bcnt); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("wma_vdev_set_param WMI_VDEV_PARAM_BMISS_FINAL_BCNT returned Error %d", + status); + return status; + } + + return status; +} + +/** + * wma_roam_scan_offload_command() - set roam offload command + * @wma_handle: wma handle + * @command: command + * @vdev_id: vdev id + * + * This function set roam offload command to fw. + * + * Return: QDF status + */ +QDF_STATUS wma_roam_scan_offload_command(tp_wma_handle wma_handle, + uint32_t command, uint32_t vdev_id) +{ + if (!wma_is_vdev_valid(vdev_id)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_roam_scan_offload_cmd(wma_handle->wmi_handle, + command, vdev_id); +} + +/** + * wma_roam_scan_btm_offload() - Send BTM offload config + * @wma_handle: wma handle + * @roam_req: roam request parameters + * + * This function is used to send BTM offload config to fw + * + * Return: QDF status + */ +static QDF_STATUS +wma_roam_scan_btm_offload(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{ + struct wmi_btm_config *params; + QDF_STATUS status; + + params = qdf_mem_malloc(sizeof(struct wmi_btm_config)); + if (!params) + return QDF_STATUS_E_FAILURE; + + params->vdev_id = roam_req->sessionId; + params->btm_offload_config = roam_req->btm_offload_config; + params->btm_solicited_timeout = roam_req->btm_solicited_timeout; + params->btm_max_attempt_cnt = roam_req->btm_max_attempt_cnt; + params->btm_sticky_time = roam_req->btm_sticky_time; + params->disassoc_timer_threshold = roam_req->disassoc_timer_threshold; + params->btm_query_bitmask = roam_req->btm_query_bitmask; + params->btm_candidate_min_score = + roam_req->btm_trig_min_candidate_score; + + WMA_LOGD("%s: vdev %u btm_offload:%u btm_query_bitmask:%u btm_candidate_min_score:%d", + __func__, params->vdev_id, params->btm_offload_config, + params->btm_query_bitmask, params->btm_candidate_min_score); + + status = wmi_unified_send_btm_config(wma_handle->wmi_handle, params); + qdf_mem_free(params); + + return status; +} + +/** + * wma_send_roam_bss_load_config() - API to send load bss trigger + * related parameters to fw + * @handle: WMA handle + * @roam_req: bss load config parameters from csr to be sent to fw + * + * Return: None + */ +static +void wma_send_roam_bss_load_config(WMA_HANDLE handle, + struct wmi_bss_load_config *params) +{ + QDF_STATUS status; + tp_wma_handle wma_handle = (tp_wma_handle) handle; + bool db2dbm_enabled; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("WMA is closed, cannot send bss load config"); + return; + } + + db2dbm_enabled = wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_hw_db2dbm_support); + if (!db2dbm_enabled) { + params->rssi_threshold_5ghz -= WMA_NOISE_FLOOR_DBM_DEFAULT; + params->rssi_threshold_5ghz &= 0x000000ff; + + params->rssi_threshold_24ghz -= WMA_NOISE_FLOOR_DBM_DEFAULT; + params->rssi_threshold_24ghz &= 0x000000ff; + } + + WMA_LOGD("%s: Bss load trig params vdev %u threshold %u sample_time: %u 5Ghz RSSI threshold:%d 2.4G rssi threshold:%d", + __func__, params->vdev_id, params->bss_load_threshold, + params->bss_load_sample_time, params->rssi_threshold_5ghz, + params->rssi_threshold_24ghz); + + status = wmi_unified_send_bss_load_config(wma_handle->wmi_handle, + params); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to send bss load trigger config command"); +} + +/** + * wma_send_offload_11k_params() - API to send 11k offload params to FW + * @handle: WMA handle + * @params: Pointer to 11k offload params + * + * Return: None + */ +static +QDF_STATUS wma_send_offload_11k_params(WMA_HANDLE handle, + struct wmi_11k_offload_params *params) +{ + QDF_STATUS status; + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, cannot send 11k offload cmd", + __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_11k_neighbour_report_support)) { + WMA_LOGE(FL("FW doesn't support 11k offload")); + return QDF_STATUS_E_NOSUPPORT; + } + + /* + * If 11k enable command and ssid length is 0, drop it + */ + if (params->offload_11k_bitmask && + !params->neighbor_report_params.ssid.length) { + WMA_LOGD("%s: SSID Len 0", __func__); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_offload_11k_cmd(wma_handle->wmi_handle, params); + + if (status != QDF_STATUS_SUCCESS) + WMA_LOGE("failed to send 11k offload command"); + + return status; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * wma_send_disconnect_roam_params() - Send the disconnect roam parameters + * to wmi + * @handle: WMA handle + * @roam_req: Pointer to roam_offload_scan_req sent from CSR + * + * Return: None + */ +static void +wma_send_disconnect_roam_params(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{ + QDF_STATUS status; + struct wmi_disconnect_roam_params *params = + &roam_req->disconnect_roam_params; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("WMA is closed, cannot send disconnect roam params"); + return; + } + + switch (roam_req->Command) { + case ROAM_SCAN_OFFLOAD_START: + case ROAM_SCAN_OFFLOAD_UPDATE_CFG: + if (!params->enable) + return; + break; + case ROAM_SCAN_OFFLOAD_STOP: + params->enable = false; + break; + default: + break; + } + + status = wmi_unified_send_disconnect_roam_params(wma_handle->wmi_handle, + params); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to send disconnect roam parameters"); +} + +/** + * wma_send_idle_roam_params() - Send the idle roam parameters to wmi + * @handle: WMA handle + * @roam_req: Pointer to roam_offload_scan_req sent from CSR + * + * Return: None + */ +static void +wma_send_idle_roam_params(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{ + QDF_STATUS status; + bool db2dbm_enabled; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("WMA is closed, cannot send idle roam params"); + return; + } + + switch (roam_req->Command) { + case ROAM_SCAN_OFFLOAD_START: + case ROAM_SCAN_OFFLOAD_UPDATE_CFG: + if (!roam_req->idle_roam_params.enable) + return; + break; + case ROAM_SCAN_OFFLOAD_STOP: + roam_req->idle_roam_params.enable = false; + break; + default: + break; + } + + db2dbm_enabled = wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_hw_db2dbm_support); + if (!db2dbm_enabled) { + roam_req->idle_roam_params.conn_ap_min_rssi -= + WMA_NOISE_FLOOR_DBM_DEFAULT; + roam_req->idle_roam_params.conn_ap_min_rssi &= 0x000000ff; + } + + status = wmi_unified_send_idle_roam_params(wma_handle->wmi_handle, + &roam_req->idle_roam_params); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to send idle roam parameters"); +} + +void +wma_send_roam_preauth_status(tp_wma_handle wma_handle, + struct wmi_roam_auth_status_params *params) +{ + QDF_STATUS status; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("WMA is closed, cannot send roam prauth status"); + return; + } + + status = wmi_unified_send_roam_preauth_status(wma_handle->wmi_handle, + params); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("failed to send disconnect roam preauth status"); +} + +#else +static inline void +wma_send_disconnect_roam_params(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{} + +static inline void +wma_send_idle_roam_params(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{} +#endif + +/* wma_set_vdev_roam_reason_vsie: set vdev param + *WMI_VDEV_PARAM_ENABLE_DISABLE_ROAM_REASON_VSIE + * + *@wma: wma handler + *@vdev_id: vdev id + *@is_roam_reason_vsie_enabled: enable_roam_reason_vsie ini value + * + * Return: Void + */ +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +static void wma_set_vdev_roam_reason_vsie(tp_wma_handle wma, uint8_t vdev_id, + bool is_roam_reason_vsie_enabled) +{ + int ret; + + ret = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_ENABLE_DISABLE_ROAM_REASON_VSIE, + is_roam_reason_vsie_enabled); + if (ret) + WMA_LOGE("Failed to set vdev param %d", + WMI_VDEV_PARAM_ENABLE_DISABLE_ROAM_REASON_VSIE); +} +#else +static void wma_set_vdev_roam_reason_vsie(tp_wma_handle wma, uint8_t vdev_id, + bool is_roam_reason_vsie_enabled) +{ +} +#endif + +/** + * wma_process_roaming_config() - process roam request + * @wma_handle: wma handle + * @roam_req: roam request parameters + * + * Main routine to handle ROAM commands coming from CSR module. + * + * Return: QDF status + */ +QDF_STATUS wma_process_roaming_config(tp_wma_handle wma_handle, + struct roam_offload_scan_req *roam_req) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + wmi_start_scan_cmd_fixed_param scan_params; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + uint32_t mode = 0; + uint8_t enable_roam_reason_vsie = 0; + struct wmi_bss_load_config *bss_load_cfg; + + if (!mac) { + WMA_LOGE("%s: mac is NULL", __func__); + qdf_mem_zero(roam_req, sizeof(*roam_req)); + qdf_mem_free(roam_req); + return QDF_STATUS_E_FAILURE; + } + + wma_debug("RSO Command:%d, reason %d vdev %d enable %d 11k bitmask %d", + roam_req->Command, roam_req->reason, roam_req->sessionId, + roam_req->RoamScanOffloadEnabled, + roam_req->offload_11k_params.offload_11k_bitmask); + wma_handle->interfaces[roam_req->sessionId].roaming_in_progress = false; + switch (roam_req->Command) { + case ROAM_SCAN_OFFLOAD_START: + /* + * Scan/Roam threshold parameters are translated from + * fields of struct roam_offload_scan_req to WMITLV + * values sent to Rome firmware some of these + * parameters are configurable in qcom_cfg.ini + */ + + /* First param is positive rssi value to trigger rssi based scan + * Opportunistic scan is started at 30dB > trigger rssi. + */ + wma_handle->suitable_ap_hb_failure = false; + + qdf_status = wma_roam_scan_offload_rssi_thresh(wma_handle, + roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + qdf_status = wma_roam_scan_bmiss_cnt(wma_handle, + roam_req->RoamBmissFirstBcnt, + roam_req->RoamBmissFinalBcnt, + roam_req->sessionId); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + + wlan_mlme_get_roam_reason_vsie_status(wma_handle->psoc, + &enable_roam_reason_vsie); + wma_set_vdev_roam_reason_vsie(wma_handle, roam_req->sessionId, + enable_roam_reason_vsie); + wma_set_roam_triggers(wma_handle, &roam_req->roam_triggers); + + /* Opportunistic scan runs on a timer, value set by + * EmptyRefreshScanPeriod. Age out the entries after 3 such + * cycles. + */ + if (roam_req->EmptyRefreshScanPeriod > 0) { + qdf_status = + wma_roam_scan_offload_scan_period(wma_handle, + roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + + mode = WMI_ROAM_SCAN_MODE_PERIODIC; + if (roam_req->roam_force_rssi_trigger) + mode |= WMI_ROAM_SCAN_MODE_RSSI_CHANGE; + + } else if (roam_req->roam_force_rssi_trigger) { + mode = WMI_ROAM_SCAN_MODE_RSSI_CHANGE; + } + + /* Start new rssi triggered scan only if it changes by + * RoamRssiDiff value. Beacon weight of 14 means average rssi + * is taken over 14 previous samples + 2 times the current + * beacon's rssi. + */ + qdf_status = wma_roam_scan_offload_rssi_change(wma_handle, + roam_req->sessionId, + roam_req->RoamRescanRssiDiff, + roam_req->RoamBeaconRssiWeight, + roam_req->hi_rssi_scan_delay); + + if (qdf_status != QDF_STATUS_SUCCESS) + break; + + qdf_status = wma_roam_scan_offload_ap_profile(wma_handle, + roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + + qdf_status = wma_roam_scan_offload_chan_list(wma_handle, + roam_req->ConnectedNetwork.ChannelCount, + &roam_req->ConnectedNetwork.chan_freq_cache[0], + roam_req->ChannelCacheType, + roam_req->sessionId); + if ((qdf_status != QDF_STATUS_SUCCESS) && + (qdf_status != QDF_STATUS_E_EMPTY)) + break; + + + wma_roam_scan_fill_scan_params(wma_handle, mac, roam_req, + &scan_params); + qdf_status = + wma_roam_scan_offload_mode(wma_handle, &scan_params, + roam_req, mode, + roam_req->sessionId); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + if (wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_mawc_support)) { + qdf_status = + wma_roam_scan_mawc_params(wma_handle, roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("Sending roaming MAWC params failed"); + break; + } + } else { + WMA_LOGD("MAWC roaming not supported by firmware"); + } + qdf_status = wma_roam_scan_filter(wma_handle, roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("Sending start for roam scan filter failed"); + break; + } + + /* Send BTM config as disabled during RSO Stop */ + qdf_status = wma_roam_scan_btm_offload(wma_handle, + roam_req); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE("Sending BTM config to fw failed"); + break; + } + + /* + * Send 11k offload enable and bss load trigger parameters + * to FW as part of RSO Start + */ + qdf_status = wma_send_offload_11k_params(wma_handle, + &roam_req->offload_11k_params); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE("11k offload enable not sent, status %d", + qdf_status); + break; + } + + if (roam_req->bss_load_trig_enabled) { + bss_load_cfg = &roam_req->bss_load_config; + wma_send_roam_bss_load_config(wma_handle, bss_load_cfg); + } + + wma_send_disconnect_roam_params(wma_handle, roam_req); + wma_send_idle_roam_params(wma_handle, roam_req); + break; + + case ROAM_SCAN_OFFLOAD_STOP: + /* + * If roam synch propagation is in progress and an user space + * disconnect is requested, then there is no need to send the + * RSO STOP to firmware, since the roaming is already complete. + * If the RSO STOP is sent to firmware, then an HO_FAIL will be + * generated and the expectation from firmware would be to + * clean up the peer context on the host and not send down any + * WMI PEER DELETE commands to firmware. But, if the user space + * disconnect gets processed first, then there is a chance to + * send down the PEER DELETE commands. Hence, if we do not + * receive the HO_FAIL, and we complete the roam sync + * propagation, then the host and firmware will be in sync with + * respect to the peer and then the user space disconnect can + * be handled gracefully in a normal way. + * + * Ensure to check the reason code since the RSO Stop might + * come when roam sync failed as well and at that point it + * should go through to the firmware and receive HO_FAIL + * and clean up. + */ + if (wma_is_roam_synch_in_progress(wma_handle, + roam_req->sessionId) && + roam_req->reason == + REASON_ROAM_STOP_ALL) { + WMA_LOGD("Dont send RSO stop during roam sync"); + break; + } + + /* + * Send 11k offload disable command to FW as part of RSO Stop + */ + qdf_status = + wma_send_offload_11k_params(wma_handle, + &roam_req->offload_11k_params); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + WMA_LOGE("11k offload disable not sent, status %d", + qdf_status); + break; + } + + /* Send BTM config as disabled during RSO Stop */ + qdf_status = wma_roam_scan_btm_offload(wma_handle, roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE(FL("Sending BTM config to fw failed")); + break; + } + + wma_handle->suitable_ap_hb_failure = false; + + wma_roam_scan_fill_scan_params(wma_handle, mac, + NULL, &scan_params); + + if (roam_req->reason == REASON_ROAM_STOP_ALL || + roam_req->reason == REASON_DISCONNECTED || + roam_req->reason == REASON_ROAM_SYNCH_FAILED) { + mode = WMI_ROAM_SCAN_MODE_NONE; + } else { + if (csr_is_roam_offload_enabled(mac)) + mode = WMI_ROAM_SCAN_MODE_NONE | + WMI_ROAM_SCAN_MODE_ROAMOFFLOAD; + else + mode = WMI_ROAM_SCAN_MODE_NONE; + } + + qdf_status = wma_roam_scan_offload_mode( + wma_handle, + &scan_params, NULL, mode, + roam_req->sessionId); + /* + * After sending the roam scan mode because of a disconnect, + * clear the scan bitmap client as well by sending + * the following command + */ + wma_roam_scan_offload_rssi_thresh(wma_handle, roam_req); + /* + * If the STOP command is due to a disconnect, then + * send the filter command to clear all the filter + * entries. If it is roaming scenario, then do not + * send the cleared entries. + */ + if (!roam_req->middle_of_roaming) { + qdf_status = wma_roam_scan_filter(wma_handle, roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("clear for roam scan filter failed"); + break; + } + } + + wma_send_disconnect_roam_params(wma_handle, roam_req); + wma_send_idle_roam_params(wma_handle, roam_req); + + /* + * Disable all roaming triggers if RSO stop is as part of + * disconnect + */ + if (mode == WMI_ROAM_SCAN_MODE_NONE) { + struct roam_triggers roam_triggers; + + roam_triggers.vdev_id = roam_req->sessionId; + roam_triggers.trigger_bitmap = 0; + wma_set_roam_triggers(wma_handle, &roam_triggers); + } + + if (roam_req->reason == + REASON_OS_REQUESTED_ROAMING_NOW) { + struct scheduler_msg cds_msg = {0}; + struct roam_offload_scan_rsp *scan_offload_rsp; + + scan_offload_rsp = + qdf_mem_malloc(sizeof(*scan_offload_rsp)); + if (!scan_offload_rsp) { + qdf_mem_free(roam_req); + return QDF_STATUS_E_NOMEM; + } + cds_msg.type = eWNI_SME_ROAM_SCAN_OFFLOAD_RSP; + scan_offload_rsp->sessionId = roam_req->sessionId; + scan_offload_rsp->reason = roam_req->reason; + cds_msg.bodyptr = scan_offload_rsp; + /* + * Since REASSOC request is processed in + * Roam_Scan_Offload_Rsp post a dummy rsp msg back to + * SME with proper reason code. + */ + if (QDF_STATUS_SUCCESS != + scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, + &cds_msg)) { + qdf_mem_free(scan_offload_rsp); + } + } + break; + + case ROAM_SCAN_OFFLOAD_ABORT_SCAN: + /* If roam scan is running, stop that cycle. + * It will continue automatically on next trigger. + */ + qdf_status = wma_roam_scan_offload_command(wma_handle, + WMI_ROAM_SCAN_STOP_CMD, + roam_req->sessionId); + break; + + case ROAM_SCAN_OFFLOAD_RESTART: + /* Rome offload engine does not stop after any scan. + * If this command is sent because all preauth attempts failed + * and WMI_ROAM_REASON_SUITABLE_AP event was received earlier, + * now it is time to call it heartbeat failure. + */ + if ((roam_req->reason == REASON_PREAUTH_FAILED_FOR_ALL) + && wma_handle->suitable_ap_hb_failure) { + WMA_LOGE("%s: Sending heartbeat failure after preauth failures", + __func__); + wma_beacon_miss_handler(wma_handle, + roam_req->sessionId, + wma_handle->suitable_ap_hb_failure_rssi); + wma_handle->suitable_ap_hb_failure = false; + } + break; + + case ROAM_SCAN_OFFLOAD_UPDATE_CFG: + wma_handle->suitable_ap_hb_failure = false; + + qdf_status = wma_roam_scan_bmiss_cnt(wma_handle, + roam_req->RoamBmissFirstBcnt, + roam_req->RoamBmissFinalBcnt, + roam_req->sessionId); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + + qdf_status = wma_roam_scan_filter(wma_handle, roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) { + WMA_LOGE("Sending update for roam scan filter failed"); + break; + } + + + /* + * Runtime (after association) changes to rssi thresholds and + * other parameters. + */ + qdf_status = wma_roam_scan_offload_chan_list(wma_handle, + roam_req->ConnectedNetwork.ChannelCount, + &roam_req->ConnectedNetwork.chan_freq_cache[0], + roam_req->ChannelCacheType, + roam_req->sessionId); + /* + * Even though the channel list is empty, we can + * still go ahead and start Roaming. + */ + if ((qdf_status != QDF_STATUS_SUCCESS) && + (qdf_status != QDF_STATUS_E_EMPTY)) + break; + + + qdf_status = wma_roam_scan_offload_rssi_thresh(wma_handle, + roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + + if (roam_req->EmptyRefreshScanPeriod > 0) { + qdf_status = wma_roam_scan_offload_scan_period( + wma_handle, roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + + mode = WMI_ROAM_SCAN_MODE_PERIODIC; + if (roam_req->roam_force_rssi_trigger) + mode |= WMI_ROAM_SCAN_MODE_RSSI_CHANGE; + + } else if (roam_req->roam_force_rssi_trigger) { + mode = WMI_ROAM_SCAN_MODE_RSSI_CHANGE; + } + + qdf_status = wma_roam_scan_offload_rssi_change(wma_handle, + roam_req->sessionId, + roam_req->RoamRescanRssiDiff, + roam_req->RoamBeaconRssiWeight, + roam_req->hi_rssi_scan_delay); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + + qdf_status = wma_roam_scan_offload_ap_profile(wma_handle, + roam_req); + if (qdf_status != QDF_STATUS_SUCCESS) + break; + + if (!roam_req->RoamScanOffloadEnabled) + break; + + wma_roam_scan_fill_scan_params(wma_handle, mac, roam_req, + &scan_params); + qdf_status = + wma_roam_scan_offload_mode(wma_handle, &scan_params, + roam_req, mode, + roam_req->sessionId); + + wma_send_disconnect_roam_params(wma_handle, roam_req); + wma_send_idle_roam_params(wma_handle, roam_req); + + break; + + default: + break; + } + qdf_mem_zero(roam_req, sizeof(*roam_req)); + qdf_mem_free(roam_req); + return qdf_status; +} + +void wma_update_per_roam_config(WMA_HANDLE handle, + struct wmi_per_roam_config_req *req_buf) +{ + QDF_STATUS status; + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, cannot send per roam config", + __func__); + return; + } + + status = wmi_unified_set_per_roam_config(wma_handle->wmi_handle, + req_buf); + if (QDF_IS_STATUS_ERROR(status)) + wma_err("failed to set per roam config to FW"); +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + +/** + * wma_process_roam_invoke() - send roam invoke command to fw. + * @handle: wma handle + * @roaminvoke: roam invoke command + * + * Send roam invoke command to fw for fastreassoc. + * + * Return: none + */ +void wma_process_roam_invoke(WMA_HANDLE handle, + struct wma_roam_invoke_cmd *roaminvoke) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + uint32_t ch_hz; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not send roam invoke", + __func__); + goto free_frame_buf; + } + + if (!wma_is_vdev_valid(roaminvoke->vdev_id)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, + roaminvoke->vdev_id); + goto free_frame_buf; + } + ch_hz = roaminvoke->ch_freq; + wmi_unified_roam_invoke_cmd(wma_handle->wmi_handle, + (struct wmi_roam_invoke_cmd *)roaminvoke, + ch_hz); +free_frame_buf: + if (roaminvoke->frame_len) { + qdf_mem_free(roaminvoke->frame_buf); + roaminvoke->frame_buf = NULL; + } +} + +/** + * wma_process_roam_synch_fail() -roam synch failure handle + * @handle: wma handle + * @synch_fail: roam synch fail parameters + * + * Return: none + */ +void wma_process_roam_synch_fail(WMA_HANDLE handle, + struct roam_offload_synch_fail *synch_fail) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not clean-up roam synch", + __func__); + return; + } + wlan_roam_debug_log(synch_fail->session_id, + DEBUG_ROAM_SYNCH_FAIL, + DEBUG_INVALID_PEER_ID, NULL, NULL, 0, 0); + + /* Hand Off Failure could happen as an exception, when a roam synch + * indication is posted to Host, but a roam synch complete is not + * posted to the firmware.So, clear the roam synch in progress + * flag before disconnecting the session through this event. + */ + wma_handle->interfaces[synch_fail->session_id].roam_synch_in_progress = + false; +} + +/** + * wma_free_roam_synch_frame_ind() - Free the bcn_probe_rsp, reassoc_req, + * reassoc_rsp received as part of the ROAM_SYNC_FRAME event + * + * @iface - interaface corresponding to a vdev + * + * This API is used to free the buffer allocated during the ROAM_SYNC_FRAME + * event + * + */ +static void wma_free_roam_synch_frame_ind(struct wma_txrx_node *iface) +{ + if (iface->roam_synch_frame_ind.bcn_probe_rsp) { + qdf_mem_free(iface->roam_synch_frame_ind.bcn_probe_rsp); + iface->roam_synch_frame_ind.bcn_probe_rsp_len = 0; + iface->roam_synch_frame_ind.bcn_probe_rsp = NULL; + } + if (iface->roam_synch_frame_ind.reassoc_req) { + qdf_mem_free(iface->roam_synch_frame_ind.reassoc_req); + iface->roam_synch_frame_ind.reassoc_req_len = 0; + iface->roam_synch_frame_ind.reassoc_req = NULL; + } + if (iface->roam_synch_frame_ind.reassoc_rsp) { + qdf_mem_free(iface->roam_synch_frame_ind.reassoc_rsp); + iface->roam_synch_frame_ind.reassoc_rsp_len = 0; + iface->roam_synch_frame_ind.reassoc_rsp = NULL; + } +} + +/** + * wma_fill_data_synch_frame_event() - Fill the the roam sync data buffer using + * synch frame event data + * @wma: Global WMA Handle + * @roam_synch_ind_ptr: Buffer to be filled + * @param_buf: Source buffer + * + * Firmware sends all the required information required for roam + * synch propagation as TLV's and stored in param_buf. These + * parameters are parsed and filled into the roam synch indication + * buffer which will be used at different layers for propagation. + * + * Return: None + */ +static void wma_fill_data_synch_frame_event(tp_wma_handle wma, + struct roam_offload_synch_ind *roam_synch_ind_ptr, + struct wma_txrx_node *iface) +{ + uint8_t *bcn_probersp_ptr; + uint8_t *reassoc_rsp_ptr; + uint8_t *reassoc_req_ptr; + + /* Beacon/Probe Rsp data */ + roam_synch_ind_ptr->beaconProbeRespOffset = + sizeof(struct roam_offload_synch_ind); + bcn_probersp_ptr = (uint8_t *) roam_synch_ind_ptr + + roam_synch_ind_ptr->beaconProbeRespOffset; + roam_synch_ind_ptr->beaconProbeRespLength = + iface->roam_synch_frame_ind.bcn_probe_rsp_len; + qdf_mem_copy(bcn_probersp_ptr, + iface->roam_synch_frame_ind.bcn_probe_rsp, + roam_synch_ind_ptr->beaconProbeRespLength); + qdf_mem_free(iface->roam_synch_frame_ind.bcn_probe_rsp); + iface->roam_synch_frame_ind.bcn_probe_rsp = NULL; + + /* ReAssoc Rsp data */ + roam_synch_ind_ptr->reassocRespOffset = + sizeof(struct roam_offload_synch_ind) + + roam_synch_ind_ptr->beaconProbeRespLength; + roam_synch_ind_ptr->reassocRespLength = + iface->roam_synch_frame_ind.reassoc_rsp_len; + reassoc_rsp_ptr = (uint8_t *) roam_synch_ind_ptr + + roam_synch_ind_ptr->reassocRespOffset; + qdf_mem_copy(reassoc_rsp_ptr, + iface->roam_synch_frame_ind.reassoc_rsp, + roam_synch_ind_ptr->reassocRespLength); + qdf_mem_free(iface->roam_synch_frame_ind.reassoc_rsp); + iface->roam_synch_frame_ind.reassoc_rsp = NULL; + + /* ReAssoc Req data */ + roam_synch_ind_ptr->reassoc_req_offset = + sizeof(struct roam_offload_synch_ind) + + roam_synch_ind_ptr->beaconProbeRespLength + + roam_synch_ind_ptr->reassocRespLength; + roam_synch_ind_ptr->reassoc_req_length = + iface->roam_synch_frame_ind.reassoc_req_len; + reassoc_req_ptr = (uint8_t *) roam_synch_ind_ptr + + roam_synch_ind_ptr->reassoc_req_offset; + qdf_mem_copy(reassoc_req_ptr, + iface->roam_synch_frame_ind.reassoc_req, + roam_synch_ind_ptr->reassoc_req_length); + qdf_mem_free(iface->roam_synch_frame_ind.reassoc_req); + iface->roam_synch_frame_ind.reassoc_req = NULL; +} + +/** + * wma_fill_data_synch_event() - Fill the the roam sync data buffer using synch + * event data + * @wma: Global WMA Handle + * @roam_synch_ind_ptr: Buffer to be filled + * @param_buf: Source buffer + * + * Firmware sends all the required information required for roam + * synch propagation as TLV's and stored in param_buf. These + * parameters are parsed and filled into the roam synch indication + * buffer which will be used at different layers for propagation. + * + * Return: None + */ +static void wma_fill_data_synch_event(tp_wma_handle wma, + struct roam_offload_synch_ind *roam_synch_ind_ptr, + WMI_ROAM_SYNCH_EVENTID_param_tlvs *param_buf) +{ + uint8_t *bcn_probersp_ptr; + uint8_t *reassoc_rsp_ptr; + uint8_t *reassoc_req_ptr; + wmi_roam_synch_event_fixed_param *synch_event; + + synch_event = param_buf->fixed_param; + + /* Beacon/Probe Rsp data */ + roam_synch_ind_ptr->beaconProbeRespOffset = + sizeof(struct roam_offload_synch_ind); + bcn_probersp_ptr = (uint8_t *) roam_synch_ind_ptr + + roam_synch_ind_ptr->beaconProbeRespOffset; + roam_synch_ind_ptr->beaconProbeRespLength = + synch_event->bcn_probe_rsp_len; + qdf_mem_copy(bcn_probersp_ptr, param_buf->bcn_probe_rsp_frame, + roam_synch_ind_ptr->beaconProbeRespLength); + /* ReAssoc Rsp data */ + roam_synch_ind_ptr->reassocRespOffset = + sizeof(struct roam_offload_synch_ind) + + roam_synch_ind_ptr->beaconProbeRespLength; + roam_synch_ind_ptr->reassocRespLength = synch_event->reassoc_rsp_len; + reassoc_rsp_ptr = (uint8_t *) roam_synch_ind_ptr + + roam_synch_ind_ptr->reassocRespOffset; + qdf_mem_copy(reassoc_rsp_ptr, + param_buf->reassoc_rsp_frame, + roam_synch_ind_ptr->reassocRespLength); + + /* ReAssoc Req data */ + roam_synch_ind_ptr->reassoc_req_offset = + sizeof(struct roam_offload_synch_ind) + + roam_synch_ind_ptr->beaconProbeRespLength + + roam_synch_ind_ptr->reassocRespLength; + roam_synch_ind_ptr->reassoc_req_length = synch_event->reassoc_req_len; + reassoc_req_ptr = (uint8_t *) roam_synch_ind_ptr + + roam_synch_ind_ptr->reassoc_req_offset; + qdf_mem_copy(reassoc_req_ptr, param_buf->reassoc_req_frame, + roam_synch_ind_ptr->reassoc_req_length); +} + +/** + * wma_fill_roam_synch_buffer() - Fill the the roam sync buffer + * @wma: Global WMA Handle + * @roam_synch_ind_ptr: Buffer to be filled + * @param_buf: Source buffer + * + * Firmware sends all the required information required for roam + * synch propagation as TLV's and stored in param_buf. These + * parameters are parsed and filled into the roam synch indication + * buffer which will be used at different layers for propagation. + * + * Return: Success or Failure + */ +static int wma_fill_roam_synch_buffer(tp_wma_handle wma, + struct roam_offload_synch_ind *roam_synch_ind_ptr, + WMI_ROAM_SYNCH_EVENTID_param_tlvs *param_buf) +{ + wmi_roam_synch_event_fixed_param *synch_event; + wmi_channel *chan; + wmi_key_material *key; + wmi_key_material_ext *key_ft; + struct wma_txrx_node *iface = NULL; + wmi_roam_fils_synch_tlv_param *fils_info; + wmi_roam_pmk_cache_synch_tlv_param *pmk_cache_info; + int status = -EINVAL; + uint8_t kck_len; + uint8_t kek_len; + + synch_event = param_buf->fixed_param; + roam_synch_ind_ptr->roamed_vdev_id = synch_event->vdev_id; + roam_synch_ind_ptr->authStatus = synch_event->auth_status; + roam_synch_ind_ptr->roamReason = synch_event->roam_reason; + roam_synch_ind_ptr->rssi = synch_event->rssi; + iface = &wma->interfaces[synch_event->vdev_id]; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&synch_event->bssid, + roam_synch_ind_ptr->bssid.bytes); + WMA_LOGD("%s: roamedVdevId %d authStatus %d roamReason %d rssi %d isBeacon %d", + __func__, roam_synch_ind_ptr->roamed_vdev_id, + roam_synch_ind_ptr->authStatus, roam_synch_ind_ptr->roamReason, + roam_synch_ind_ptr->rssi, roam_synch_ind_ptr->isBeacon); + + if (!QDF_IS_STATUS_SUCCESS( + wma->csr_roam_synch_cb(wma->mac_context, roam_synch_ind_ptr, + NULL, SIR_ROAMING_DEREGISTER_STA))) { + WMA_LOGE("LFR3: CSR Roam synch cb failed"); + wma_free_roam_synch_frame_ind(iface); + return status; + } + + /* + * If lengths of bcn_probe_rsp, reassoc_req and reassoc_rsp are zero in + * synch_event driver would have received bcn_probe_rsp, reassoc_req + * and reassoc_rsp via the event WMI_ROAM_SYNCH_FRAME_EVENTID + */ + if ((!synch_event->bcn_probe_rsp_len) && + (!synch_event->reassoc_req_len) && + (!synch_event->reassoc_rsp_len)) { + if (!iface->roam_synch_frame_ind.bcn_probe_rsp) { + WMA_LOGE("LFR3: bcn_probe_rsp is NULL"); + QDF_ASSERT(iface->roam_synch_frame_ind. + bcn_probe_rsp); + wma_free_roam_synch_frame_ind(iface); + return status; + } + if (!iface->roam_synch_frame_ind.reassoc_rsp) { + WMA_LOGE("LFR3: reassoc_rsp is NULL"); + QDF_ASSERT(iface->roam_synch_frame_ind. + reassoc_rsp); + wma_free_roam_synch_frame_ind(iface); + return status; + } + if (!iface->roam_synch_frame_ind.reassoc_req) { + WMA_LOGE("LFR3: reassoc_req is NULL"); + QDF_ASSERT(iface->roam_synch_frame_ind. + reassoc_req); + wma_free_roam_synch_frame_ind(iface); + return status; + } + wma_fill_data_synch_frame_event(wma, roam_synch_ind_ptr, iface); + } else { + wma_fill_data_synch_event(wma, roam_synch_ind_ptr, param_buf); + } + chan = param_buf->chan; + if (chan) { + roam_synch_ind_ptr->chan_freq = chan->mhz; + roam_synch_ind_ptr->phy_mode = + wma_fw_to_host_phymode(WMI_GET_CHANNEL_MODE(chan)); + } else { + roam_synch_ind_ptr->phy_mode = WLAN_PHYMODE_AUTO; + } + + key = param_buf->key; + key_ft = param_buf->key_ext; + if (key) { + roam_synch_ind_ptr->kck_len = SIR_KCK_KEY_LEN; + qdf_mem_copy(roam_synch_ind_ptr->kck, key->kck, + SIR_KCK_KEY_LEN); + roam_synch_ind_ptr->kek_len = SIR_KEK_KEY_LEN; + qdf_mem_copy(roam_synch_ind_ptr->kek, key->kek, + SIR_KEK_KEY_LEN); + qdf_mem_copy(roam_synch_ind_ptr->replay_ctr, + key->replay_counter, SIR_REPLAY_CTR_LEN); + } else if (key_ft) { + /* + * For AKM 00:0F:AC (FT suite-B-SHA384) + * KCK-bits:192 KEK-bits:256 + * Firmware sends wmi_key_material_ext tlv now only if + * auth is FT Suite-B SHA-384 auth. If further new suites + * are added, add logic to get kck, kek bits based on + * akm protocol + */ + kck_len = KCK_192BIT_KEY_LEN; + kek_len = KEK_256BIT_KEY_LEN; + + roam_synch_ind_ptr->kck_len = kck_len; + qdf_mem_copy(roam_synch_ind_ptr->kck, + key_ft->key_buffer, kck_len); + + roam_synch_ind_ptr->kek_len = kek_len; + qdf_mem_copy(roam_synch_ind_ptr->kek, + (key_ft->key_buffer + kck_len), + kek_len); + + qdf_mem_copy(roam_synch_ind_ptr->replay_ctr, + (key_ft->key_buffer + kek_len + kck_len), + SIR_REPLAY_CTR_LEN); + } + + if (param_buf->hw_mode_transition_fixed_param) + wma_process_pdev_hw_mode_trans_ind(wma, + param_buf->hw_mode_transition_fixed_param, + param_buf->wmi_pdev_set_hw_mode_response_vdev_mac_mapping, + &roam_synch_ind_ptr->hw_mode_trans_ind); + else + WMA_LOGD(FL("hw_mode transition fixed param is NULL")); + + fils_info = param_buf->roam_fils_synch_info; + if (fils_info) { + if ((fils_info->kek_len > SIR_KEK_KEY_LEN_FILS) || + (fils_info->pmk_len > SIR_PMK_LEN)) { + WMA_LOGE("%s: Invalid kek_len %d or pmk_len %d", + __func__, + fils_info->kek_len, + fils_info->pmk_len); + wma_free_roam_synch_frame_ind(iface); + return status; + } + + roam_synch_ind_ptr->kek_len = fils_info->kek_len; + qdf_mem_copy(roam_synch_ind_ptr->kek, fils_info->kek, + fils_info->kek_len); + + roam_synch_ind_ptr->pmk_len = fils_info->pmk_len; + qdf_mem_copy(roam_synch_ind_ptr->pmk, fils_info->pmk, + fils_info->pmk_len); + + qdf_mem_copy(roam_synch_ind_ptr->pmkid, fils_info->pmkid, + PMKID_LEN); + + roam_synch_ind_ptr->update_erp_next_seq_num = + fils_info->update_erp_next_seq_num; + roam_synch_ind_ptr->next_erp_seq_num = + fils_info->next_erp_seq_num; + + WMA_LOGD("Update ERP Seq Num %d, Next ERP Seq Num %d", + roam_synch_ind_ptr->update_erp_next_seq_num, + roam_synch_ind_ptr->next_erp_seq_num); + } + + pmk_cache_info = param_buf->roam_pmk_cache_synch_info; + if (pmk_cache_info && (pmk_cache_info->pmk_len)) { + if (pmk_cache_info->pmk_len > SIR_PMK_LEN) { + WMA_LOGE("%s: Invalid pmk_len %d", __func__, + pmk_cache_info->pmk_len); + wma_free_roam_synch_frame_ind(iface); + return status; + } + + roam_synch_ind_ptr->pmk_len = pmk_cache_info->pmk_len; + qdf_mem_copy(roam_synch_ind_ptr->pmk, + pmk_cache_info->pmk, pmk_cache_info->pmk_len); + qdf_mem_copy(roam_synch_ind_ptr->pmkid, + pmk_cache_info->pmkid, PMKID_LEN); + } + wma_free_roam_synch_frame_ind(iface); + return 0; +} + +static void wma_update_roamed_peer_unicast_cipher(tp_wma_handle wma, + uint32_t uc_cipher, + uint32_t cipher_cap, + uint8_t *peer_mac) +{ + struct wlan_objmgr_peer *peer; + + if (!peer_mac) { + wma_err("wma ctx or peer mac is NULL"); + return; + } + + peer = wlan_objmgr_get_peer(wma->psoc, + wlan_objmgr_pdev_get_pdev_id(wma->pdev), + peer_mac, WLAN_LEGACY_WMA_ID); + if (!peer) { + wma_err("Peer of peer_mac "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(peer_mac)); + return; + } + wlan_crypto_set_peer_param(peer, WLAN_CRYPTO_PARAM_CIPHER_CAP, + cipher_cap); + wlan_crypto_set_peer_param(peer, WLAN_CRYPTO_PARAM_UCAST_CIPHER, + uc_cipher); + + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + + wma_debug("Set unicast cipher %x and cap %x for "QDF_MAC_ADDR_FMT, + uc_cipher, cipher_cap, QDF_MAC_ADDR_REF(peer_mac)); +} + +static void wma_get_peer_uc_cipher(tp_wma_handle wma, uint8_t *peer_mac, + uint32_t *uc_cipher, uint32_t *cipher_cap) +{ + uint32_t cipher, cap; + struct wlan_objmgr_peer *peer; + + if (!peer_mac) { + WMA_LOGE("wma ctx or peer_mac is NULL"); + return; + } + peer = wlan_objmgr_get_peer(wma->psoc, + wlan_objmgr_pdev_get_pdev_id(wma->pdev), + peer_mac, WLAN_LEGACY_WMA_ID); + if (!peer) { + WMA_LOGE("Peer of peer_mac "QDF_MAC_ADDR_FMT" not found", + QDF_MAC_ADDR_REF(peer_mac)); + return; + } + + cipher = wlan_crypto_get_peer_param(peer, + WLAN_CRYPTO_PARAM_UCAST_CIPHER); + cap = wlan_crypto_get_peer_param(peer, WLAN_CRYPTO_PARAM_CIPHER_CAP); + wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); + + if (uc_cipher) + *uc_cipher = cipher; + if (cipher_cap) + *cipher_cap = cap; +} + +/** + * wma_roam_update_vdev() - Update the STA and BSS + * @wma: Global WMA Handle + * @roam_synch_ind_ptr: Information needed for roam sync propagation + * + * This function will perform all the vdev related operations with + * respect to the self sta and the peer after roaming and completes + * the roam synch propagation with respect to WMA layer. + * + * Return: None + */ +static void +wma_roam_update_vdev(tp_wma_handle wma, + struct roam_offload_synch_ind *roam_synch_ind_ptr) +{ + tDeleteStaParams *del_sta_params; + tAddStaParams *add_sta_params; + uint32_t uc_cipher = 0, cipher_cap = 0; + uint8_t vdev_id, *bssid; + + vdev_id = roam_synch_ind_ptr->roamed_vdev_id; + wma->interfaces[vdev_id].nss = roam_synch_ind_ptr->nss; + + del_sta_params = qdf_mem_malloc(sizeof(*del_sta_params)); + if (!del_sta_params) { + return; + } + + add_sta_params = qdf_mem_malloc(sizeof(*add_sta_params)); + if (!add_sta_params) { + qdf_mem_free(del_sta_params); + return; + } + + qdf_mem_zero(del_sta_params, sizeof(*del_sta_params)); + qdf_mem_zero(add_sta_params, sizeof(*add_sta_params)); + + del_sta_params->smesessionId = vdev_id; + add_sta_params->staType = STA_ENTRY_SELF; + add_sta_params->smesessionId = vdev_id; + qdf_mem_copy(&add_sta_params->bssId, &roam_synch_ind_ptr->bssid.bytes, + QDF_MAC_ADDR_SIZE); + add_sta_params->assocId = roam_synch_ind_ptr->aid; + + bssid = wma_get_vdev_bssid(wma->interfaces[vdev_id].vdev); + if (!bssid) { + WMA_LOGE("%s: Failed to get bssid for vdev_%d", + __func__, vdev_id); + return; + } + + /* + * Get uc cipher of old peer to update new peer as it doesnt + * change in roaming + */ + wma_get_peer_uc_cipher(wma, bssid, + &uc_cipher, &cipher_cap); + + wma_delete_sta(wma, del_sta_params); + wma_delete_bss(wma, vdev_id); + wma_add_bss_peer_sta(roam_synch_ind_ptr->self_mac.bytes, + roam_synch_ind_ptr->bssid.bytes, true); + /* Update new peer's uc cipher */ + wma_update_roamed_peer_unicast_cipher(wma, uc_cipher, cipher_cap, + roam_synch_ind_ptr->bssid.bytes); + wma_add_bss_lfr3(wma, roam_synch_ind_ptr->add_bss_params); + wma_add_sta(wma, add_sta_params); + qdf_mem_copy(bssid, roam_synch_ind_ptr->bssid.bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_free(add_sta_params); +} + +static void wma_update_phymode_on_roam(tp_wma_handle wma, uint8_t *bssid, + wmi_channel *chan, + struct wma_txrx_node *iface) +{ + enum wlan_phymode bss_phymode; + struct wlan_channel *des_chan; + struct vdev_mlme_obj *vdev_mlme; + uint8_t channel; + + channel = wlan_freq_to_chan(iface->mhz); + if (chan) + bss_phymode = + wma_fw_to_host_phymode(WMI_GET_CHANNEL_MODE(chan)); + else + wma_get_phy_mode_cb(channel, iface->chan_width, &bss_phymode); + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + des_chan = wlan_vdev_mlme_get_des_chan(iface->vdev); + + des_chan->ch_phymode = bss_phymode; + if (!vdev_mlme) + return; + /* Till conversion is not done in WMI we need to fill fw phy mode */ + vdev_mlme->mgmt.generic.phy_mode = wma_host_to_fw_phymode(bss_phymode); + + /* update new phymode to peer */ + wma_objmgr_set_peer_mlme_phymode(wma, bssid, bss_phymode); + + WMA_LOGD("LFR3: new phymode %d", bss_phymode); +} + +/** + * wma_post_roam_sync_failure: Send roam sync failure ind to fw + * @wma: wma handle + * @vdev_id: session id + * + * Return: None + */ +static void wma_post_roam_sync_failure(tp_wma_handle wma, uint8_t vdev_id) +{ + struct roam_offload_scan_req *roam_req; + + roam_req = qdf_mem_malloc(sizeof(struct roam_offload_scan_req)); + if (roam_req) { + roam_req->Command = ROAM_SCAN_OFFLOAD_STOP; + roam_req->reason = REASON_ROAM_SYNCH_FAILED; + roam_req->sessionId = vdev_id; + wma_debug("In cleanup: RSO Command:%d, reason %d vdev %d", + roam_req->Command, roam_req->reason, + roam_req->sessionId); + wma_process_roaming_config(wma, roam_req); + } +} + +int wma_mlme_roam_synch_event_handler_cb(void *handle, uint8_t *event, + uint32_t len) +{ + WMI_ROAM_SYNCH_EVENTID_param_tlvs *param_buf = NULL; + wmi_roam_synch_event_fixed_param *synch_event = NULL; + tp_wma_handle wma = (tp_wma_handle) handle; + struct roam_offload_synch_ind *roam_synch_ind_ptr = NULL; + struct bss_description *bss_desc_ptr = NULL; + uint16_t ie_len = 0; + int status = -EINVAL; + qdf_time_t roam_synch_received = qdf_get_system_timestamp(); + uint32_t roam_synch_data_len; + A_UINT32 bcn_probe_rsp_len; + A_UINT32 reassoc_rsp_len; + A_UINT32 reassoc_req_len; + + WMA_LOGD("LFR3: Received WMA_ROAM_OFFLOAD_SYNCH_IND"); + if (!event) { + WMA_LOGE("%s: event param null", __func__); + goto cleanup_label; + } + + param_buf = (WMI_ROAM_SYNCH_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("%s: received null buf from target", __func__); + goto cleanup_label; + } + + synch_event = param_buf->fixed_param; + if (!synch_event) { + WMA_LOGE("%s: received null event data from target", __func__); + goto cleanup_label; + } + + if (synch_event->vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: received invalid vdev_id %d", + __func__, synch_event->vdev_id); + return status; + } + + /* + * This flag is set during ROAM_START and once this event is being + * executed which is a run to completion, no other event can interrupt + * this in MC thread context. So, it is OK to reset the flag here as + * soon as we receive this event. + */ + wma->interfaces[synch_event->vdev_id].roaming_in_progress = false; + + if (synch_event->bcn_probe_rsp_len > + param_buf->num_bcn_probe_rsp_frame || + synch_event->reassoc_req_len > + param_buf->num_reassoc_req_frame || + synch_event->reassoc_rsp_len > + param_buf->num_reassoc_rsp_frame) { + WMA_LOGD("Invalid synch payload: LEN bcn:%d, req:%d, rsp:%d", + synch_event->bcn_probe_rsp_len, + synch_event->reassoc_req_len, + synch_event->reassoc_rsp_len); + goto cleanup_label; + } + + wlan_roam_debug_log(synch_event->vdev_id, DEBUG_ROAM_SYNCH_IND, + DEBUG_INVALID_PEER_ID, NULL, NULL, + synch_event->bssid.mac_addr31to0, + synch_event->bssid.mac_addr47to32); + DPTRACE(qdf_dp_trace_record_event(QDF_DP_TRACE_EVENT_RECORD, + synch_event->vdev_id, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_PROTO_TYPE_EVENT, QDF_ROAM_SYNCH)); + + if (wma_is_roam_synch_in_progress(wma, synch_event->vdev_id)) { + WMA_LOGE("%s: Ignoring RSI since one is already in progress", + __func__); + goto cleanup_label; + } + + /* + * All below length fields are unsigned and hence positive numbers. + * Maximum number during the addition would be (3 * MAX_LIMIT(UINT32) + + * few fixed fields). + */ + WMA_LOGD("synch payload: LEN bcn:%d, req:%d, rsp:%d", + synch_event->bcn_probe_rsp_len, + synch_event->reassoc_req_len, + synch_event->reassoc_rsp_len); + + /* + * If lengths of bcn_probe_rsp, reassoc_req and reassoc_rsp are zero in + * synch_event driver would have received bcn_probe_rsp, reassoc_req + * and reassoc_rsp via the event WMI_ROAM_SYNCH_FRAME_EVENTID + */ + if ((!synch_event->bcn_probe_rsp_len) && + (!synch_event->reassoc_req_len) && + (!synch_event->reassoc_rsp_len)) { + bcn_probe_rsp_len = wma->interfaces[synch_event->vdev_id]. + roam_synch_frame_ind. + bcn_probe_rsp_len; + reassoc_req_len = wma->interfaces[synch_event->vdev_id]. + roam_synch_frame_ind.reassoc_req_len; + reassoc_rsp_len = wma->interfaces[synch_event->vdev_id]. + roam_synch_frame_ind.reassoc_rsp_len; + + roam_synch_data_len = bcn_probe_rsp_len + reassoc_rsp_len + + reassoc_req_len + sizeof(struct roam_offload_synch_ind); + + WMA_LOGD("Updated synch payload: LEN bcn:%d, req:%d, rsp:%d", + bcn_probe_rsp_len, + reassoc_req_len, + reassoc_rsp_len); + } else { + bcn_probe_rsp_len = synch_event->bcn_probe_rsp_len; + reassoc_req_len = synch_event->reassoc_req_len; + reassoc_rsp_len = synch_event->reassoc_rsp_len; + + if (synch_event->bcn_probe_rsp_len > WMI_SVC_MSG_MAX_SIZE) + goto cleanup_label; + if (synch_event->reassoc_rsp_len > + (WMI_SVC_MSG_MAX_SIZE - synch_event->bcn_probe_rsp_len)) + goto cleanup_label; + if (synch_event->reassoc_req_len > + WMI_SVC_MSG_MAX_SIZE - (synch_event->bcn_probe_rsp_len + + synch_event->reassoc_rsp_len)) + goto cleanup_label; + + roam_synch_data_len = bcn_probe_rsp_len + + reassoc_rsp_len + reassoc_req_len; + + /* + * Below is the check for the entire size of the message + * received from the firmware. + */ + if (roam_synch_data_len > WMI_SVC_MSG_MAX_SIZE - + (sizeof(*synch_event) + sizeof(wmi_channel) + + sizeof(wmi_key_material) + sizeof(uint32_t))) + goto cleanup_label; + + roam_synch_data_len += sizeof(struct roam_offload_synch_ind); + } + + cds_host_diag_log_work(&wma->roam_ho_wl, + WMA_ROAM_HO_WAKE_LOCK_DURATION, + WIFI_POWER_EVENT_WAKELOCK_WOW); + qdf_wake_lock_timeout_acquire(&wma->roam_ho_wl, + WMA_ROAM_HO_WAKE_LOCK_DURATION); + + wma->interfaces[synch_event->vdev_id].roam_synch_in_progress = true; + + roam_synch_ind_ptr = qdf_mem_malloc(roam_synch_data_len); + if (!roam_synch_ind_ptr) { + QDF_ASSERT(roam_synch_ind_ptr); + status = -ENOMEM; + goto cleanup_label; + } + qdf_mem_zero(roam_synch_ind_ptr, roam_synch_data_len); + status = wma_fill_roam_synch_buffer(wma, + roam_synch_ind_ptr, param_buf); + if (status != 0) + goto cleanup_label; + /* 24 byte MAC header and 12 byte to ssid IE */ + if (roam_synch_ind_ptr->beaconProbeRespLength > + (SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_SSID_OFFSET)) { + ie_len = roam_synch_ind_ptr->beaconProbeRespLength - + (SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_SSID_OFFSET); + } else { + WMA_LOGE("LFR3: Invalid Beacon Length"); + goto cleanup_label; + } + bss_desc_ptr = qdf_mem_malloc(sizeof(struct bss_description) + ie_len); + if (!bss_desc_ptr) { + QDF_ASSERT(bss_desc_ptr); + status = -ENOMEM; + goto cleanup_label; + } + qdf_mem_zero(bss_desc_ptr, sizeof(struct bss_description) + ie_len); + if (QDF_IS_STATUS_ERROR(wma->pe_roam_synch_cb(wma->mac_context, + roam_synch_ind_ptr, bss_desc_ptr, + SIR_ROAM_SYNCH_PROPAGATION))) { + WMA_LOGE("LFR3: PE roam synch cb failed"); + status = -EBUSY; + goto cleanup_label; + } + + wma_roam_update_vdev(wma, roam_synch_ind_ptr); + wma->csr_roam_synch_cb(wma->mac_context, roam_synch_ind_ptr, + bss_desc_ptr, SIR_ROAM_SYNCH_PROPAGATION); + wma_process_roam_synch_complete(wma, synch_event->vdev_id); + + /* update freq and channel width */ + wma->interfaces[synch_event->vdev_id].mhz = + roam_synch_ind_ptr->chan_freq; + if (roam_synch_ind_ptr->join_rsp) + wma->interfaces[synch_event->vdev_id].chan_width = + roam_synch_ind_ptr->join_rsp->vht_channel_width; + /* + * update phy_mode in wma to avoid mismatch in phymode between host and + * firmware. The phymode stored in peer->peer_mlme.phymode is + * sent to firmware as part of opmode update during either - vht opmode + * action frame received or during opmode change detected while + * processing beacon. Any mismatch of this value with firmware phymode + * results in firmware assert. + */ + wma_update_phymode_on_roam(wma, roam_synch_ind_ptr->bssid.bytes, + param_buf->chan, + &wma->interfaces[synch_event->vdev_id]); + + wma->csr_roam_synch_cb(wma->mac_context, roam_synch_ind_ptr, + bss_desc_ptr, SIR_ROAM_SYNCH_COMPLETE); + wma->interfaces[synch_event->vdev_id].roam_synch_delay = + qdf_get_system_timestamp() - roam_synch_received; + WMA_LOGD("LFR3: roam_synch_delay:%d", + wma->interfaces[synch_event->vdev_id].roam_synch_delay); + wma->csr_roam_synch_cb(wma->mac_context, roam_synch_ind_ptr, + bss_desc_ptr, SIR_ROAM_SYNCH_NAPI_OFF); + + status = 0; + +cleanup_label: + if (status != 0) { + if (roam_synch_ind_ptr) + wma->csr_roam_synch_cb(wma->mac_context, + roam_synch_ind_ptr, NULL, + SIR_ROAMING_ABORT); + if (synch_event) + wma_post_roam_sync_failure(wma, synch_event->vdev_id); + } + if (roam_synch_ind_ptr && roam_synch_ind_ptr->join_rsp) + qdf_mem_free(roam_synch_ind_ptr->join_rsp); + if (roam_synch_ind_ptr) + qdf_mem_free(roam_synch_ind_ptr); + if (bss_desc_ptr) + qdf_mem_free(bss_desc_ptr); + if (wma && synch_event) + wma->interfaces[synch_event->vdev_id].roam_synch_in_progress = + false; + + return status; +} + +int wma_roam_synch_frame_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + WMI_ROAM_SYNCH_FRAME_EVENTID_param_tlvs *param_buf = NULL; + wmi_roam_synch_frame_event_fixed_param *synch_frame_event = NULL; + tp_wma_handle wma = (tp_wma_handle) handle; + A_UINT32 vdev_id; + struct wma_txrx_node *iface = NULL; + int status = -EINVAL; + + WMA_LOGD("LFR3:Synch Frame event"); + if (!event) { + WMA_LOGE("event param null"); + return status; + } + + param_buf = (WMI_ROAM_SYNCH_FRAME_EVENTID_param_tlvs *) event; + if (!param_buf) { + WMA_LOGE("received null buf from target"); + return status; + } + + synch_frame_event = param_buf->fixed_param; + if (!synch_frame_event) { + WMA_LOGE("received null event data from target"); + return status; + } + + if (synch_frame_event->vdev_id >= wma->max_bssid) { + WMA_LOGE("received invalid vdev_id %d", + synch_frame_event->vdev_id); + return status; + } + + if (synch_frame_event->bcn_probe_rsp_len > + param_buf->num_bcn_probe_rsp_frame || + synch_frame_event->reassoc_req_len > + param_buf->num_reassoc_req_frame || + synch_frame_event->reassoc_rsp_len > + param_buf->num_reassoc_rsp_frame) { + WMA_LOGE("fixed/actual len err: bcn:%d/%d req:%d/%d rsp:%d/%d", + synch_frame_event->bcn_probe_rsp_len, + param_buf->num_bcn_probe_rsp_frame, + synch_frame_event->reassoc_req_len, + param_buf->num_reassoc_req_frame, + synch_frame_event->reassoc_rsp_len, + param_buf->num_reassoc_rsp_frame); + return status; + } + + vdev_id = synch_frame_event->vdev_id; + iface = &wma->interfaces[vdev_id]; + + if (wma_is_roam_synch_in_progress(wma, vdev_id)) { + WMA_LOGE("Ignoring this event as it is unexpected"); + wma_free_roam_synch_frame_ind(iface); + return status; + } + WMA_LOGD("LFR3: Received ROAM_SYNCH_FRAME_EVENT"); + + WMA_LOGD("synch frame payload: LEN bcn:%d, req:%d, rsp:%d morefrag: %d", + synch_frame_event->bcn_probe_rsp_len, + synch_frame_event->reassoc_req_len, + synch_frame_event->reassoc_rsp_len, + synch_frame_event->more_frag); + + if (synch_frame_event->bcn_probe_rsp_len) { + iface->roam_synch_frame_ind.bcn_probe_rsp_len = + synch_frame_event->bcn_probe_rsp_len; + iface->roam_synch_frame_ind.is_beacon = + synch_frame_event->is_beacon; + + if (iface->roam_synch_frame_ind.bcn_probe_rsp) + qdf_mem_free(iface->roam_synch_frame_ind. + bcn_probe_rsp); + iface->roam_synch_frame_ind.bcn_probe_rsp = + qdf_mem_malloc(iface->roam_synch_frame_ind. + bcn_probe_rsp_len); + if (!iface->roam_synch_frame_ind.bcn_probe_rsp) { + QDF_ASSERT(iface->roam_synch_frame_ind. + bcn_probe_rsp); + status = -ENOMEM; + wma_free_roam_synch_frame_ind(iface); + return status; + } + qdf_mem_copy(iface->roam_synch_frame_ind. + bcn_probe_rsp, + param_buf->bcn_probe_rsp_frame, + iface->roam_synch_frame_ind.bcn_probe_rsp_len); + } + + if (synch_frame_event->reassoc_req_len) { + iface->roam_synch_frame_ind.reassoc_req_len = + synch_frame_event->reassoc_req_len; + + if (iface->roam_synch_frame_ind.reassoc_req) + qdf_mem_free(iface->roam_synch_frame_ind.reassoc_req); + iface->roam_synch_frame_ind.reassoc_req = + qdf_mem_malloc(iface->roam_synch_frame_ind. + reassoc_req_len); + if (!iface->roam_synch_frame_ind.reassoc_req) { + QDF_ASSERT(iface->roam_synch_frame_ind. + reassoc_req); + status = -ENOMEM; + wma_free_roam_synch_frame_ind(iface); + return status; + } + qdf_mem_copy(iface->roam_synch_frame_ind.reassoc_req, + param_buf->reassoc_req_frame, + iface->roam_synch_frame_ind.reassoc_req_len); + } + + if (synch_frame_event->reassoc_rsp_len) { + iface->roam_synch_frame_ind.reassoc_rsp_len = + synch_frame_event->reassoc_rsp_len; + + if (iface->roam_synch_frame_ind.reassoc_rsp) + qdf_mem_free(iface->roam_synch_frame_ind.reassoc_rsp); + + iface->roam_synch_frame_ind.reassoc_rsp = + qdf_mem_malloc(iface->roam_synch_frame_ind. + reassoc_rsp_len); + if (!iface->roam_synch_frame_ind.reassoc_rsp) { + QDF_ASSERT(iface->roam_synch_frame_ind. + reassoc_rsp); + status = -ENOMEM; + wma_free_roam_synch_frame_ind(iface); + return status; + } + qdf_mem_copy(iface->roam_synch_frame_ind.reassoc_rsp, + param_buf->reassoc_rsp_frame, + iface->roam_synch_frame_ind.reassoc_rsp_len); + } + return 0; +} + +/** + * __wma_roam_synch_event_handler() - roam synch event handler + * @handle: wma handle + * @event: event data + * @len: length of data + * + * This function is roam synch event handler.It sends roam + * indication for upper layer. + * + * Return: Success or Failure status + */ +int wma_roam_synch_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + int status = -EINVAL; + tp_wma_handle wma = (tp_wma_handle) handle; + struct wma_txrx_node *iface = NULL; + wmi_roam_synch_event_fixed_param *synch_event = NULL; + WMI_ROAM_SYNCH_EVENTID_param_tlvs *param_buf = NULL; + struct vdev_mlme_obj *mlme_obj; + + if (!event) { + wma_err_rl("event param null"); + return status; + } + + param_buf = (WMI_ROAM_SYNCH_EVENTID_param_tlvs *)event; + if (!param_buf) { + wma_err_rl("received null buf from target"); + return status; + } + synch_event = param_buf->fixed_param; + if (!synch_event) { + wma_err_rl("received null event data from target"); + return status; + } + + if (synch_event->vdev_id >= wma->max_bssid) { + wma_err_rl("received invalid vdev_id %d", + synch_event->vdev_id); + return status; + } + + iface = &wma->interfaces[synch_event->vdev_id]; + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (mlme_obj) + mlme_obj->mgmt.generic.tx_pwrlimit = + synch_event->max_allowed_tx_power; + + qdf_status = wlan_vdev_mlme_sm_deliver_evt(iface->vdev, + WLAN_VDEV_SM_EV_ROAM, + len, + event); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + wma_err("Failed to send the EV_ROAM"); + wma_post_roam_sync_failure(wma, synch_event->vdev_id); + return status; + } + wma_debug("Posted EV_ROAM to VDEV SM"); + return 0; +} + +int wma_roam_auth_offload_event_handler(WMA_HANDLE handle, uint8_t *event, + uint32_t len) +{ + QDF_STATUS status; + tp_wma_handle wma = (tp_wma_handle) handle; + struct mac_context *mac_ctx; + wmi_roam_preauth_start_event_fixed_param *rso_auth_start_ev; + WMI_ROAM_PREAUTH_START_EVENTID_param_tlvs *param_buf; + struct qdf_mac_addr ap_bssid; + uint8_t vdev_id; + + if (!event) { + wma_err_rl("received null event from target"); + return -EINVAL; + } + + param_buf = (WMI_ROAM_PREAUTH_START_EVENTID_param_tlvs *) event; + if (!param_buf) { + wma_err_rl("received null buf from target"); + return -EINVAL; + } + + rso_auth_start_ev = param_buf->fixed_param; + if (!rso_auth_start_ev) { + wma_err_rl("received null event data from target"); + return -EINVAL; + } + + if (rso_auth_start_ev->vdev_id > wma->max_bssid) { + wma_err_rl("received invalid vdev_id %d", + rso_auth_start_ev->vdev_id); + return -EINVAL; + } + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + wma_err("NULL mac ptr"); + QDF_ASSERT(0); + return -EINVAL; + } + + cds_host_diag_log_work(&wma->roam_preauth_wl, + WMA_ROAM_PREAUTH_WAKE_LOCK_DURATION, + WIFI_POWER_EVENT_WAKELOCK_WOW); + qdf_wake_lock_timeout_acquire(&wma->roam_ho_wl, + WMA_ROAM_HO_WAKE_LOCK_DURATION); + + WMI_MAC_ADDR_TO_CHAR_ARRAY(&rso_auth_start_ev->candidate_ap_bssid, + ap_bssid.bytes); + if (qdf_is_macaddr_zero(&ap_bssid) || + qdf_is_macaddr_broadcast(&ap_bssid) || + qdf_is_macaddr_group(&ap_bssid)) { + wma_err_rl("Invalid bssid"); + return -EINVAL; + } + + vdev_id = rso_auth_start_ev->vdev_id; + wma_debug("Received Roam auth offload event for bss:"QDF_MAC_ADDR_FMT" vdev_id:%d", + QDF_MAC_ADDR_REF(ap_bssid.bytes), vdev_id); + + lim_sae_auth_cleanup_retry(mac_ctx, vdev_id); + status = wma->csr_roam_auth_event_handle_cb(mac_ctx, vdev_id, ap_bssid); + if (QDF_IS_STATUS_ERROR(status)) { + wma_err_rl("Trigger pre-auth failed"); + return -EINVAL; + } + + return 0; +} + +int wma_roam_scan_chan_list_event_handler(WMA_HANDLE handle, + uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle)handle; + WMI_ROAM_SCAN_CHANNEL_LIST_EVENTID_param_tlvs *param_buf; + wmi_roam_scan_channel_list_event_fixed_param *fixed_param; + uint8_t vdev_id, i = 0, num_ch = 0; + struct roam_scan_ch_resp *resp; + struct scheduler_msg sme_msg = {0}; + + param_buf = (WMI_ROAM_SCAN_CHANNEL_LIST_EVENTID_param_tlvs *)event; + if (!param_buf) { + wma_err_rl("NULL event received from target"); + return -EINVAL; + } + + fixed_param = param_buf->fixed_param; + if (!fixed_param) { + wma_err_rl(" NULL fixed param"); + return -EINVAL; + } + + vdev_id = fixed_param->vdev_id; + if (vdev_id >= wma->max_bssid) { + wma_err_rl("Invalid vdev_id %d", vdev_id); + return -EINVAL; + } + + num_ch = (param_buf->num_channel_list < + WNI_CFG_VALID_CHANNEL_LIST_LEN) ? + param_buf->num_channel_list : + WNI_CFG_VALID_CHANNEL_LIST_LEN; + + resp = qdf_mem_malloc(sizeof(struct roam_scan_ch_resp) + + num_ch * sizeof(param_buf->channel_list[0])); + if (!resp) { + wma_err_rl("Failed to alloc resp message"); + return -EINVAL; + } + + resp->chan_list = (uint32_t *)(resp + 1); + resp->vdev_id = vdev_id; + resp->command_resp = fixed_param->command_response; + resp->num_channels = param_buf->num_channel_list; + + for (i = 0; i < num_ch; i++) + resp->chan_list[i] = param_buf->channel_list[i]; + + sme_msg.type = eWNI_SME_GET_ROAM_SCAN_CH_LIST_EVENT; + sme_msg.bodyptr = resp; + + if (scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg)) { + WMA_LOGE(FL("Failed to post msg to SME")); + qdf_mem_free(sme_msg.bodyptr); + return -EINVAL; + } + + return 0; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * wma_get_trigger_detail_str - Return roam trigger string from the + * enum WMI_ROAM_TRIGGER_REASON + * @roam_info: Pointer to the roam trigger info + * @buf: Destination buffer to write the reason string + * + * Return: None + */ +static void +wma_get_trigger_detail_str(struct wmi_roam_trigger_info *roam_info, char *buf) +{ + uint16_t buf_cons, buf_left = MAX_ROAM_DEBUG_BUF_SIZE; + char *temp = buf; + + buf_cons = qdf_snprint(temp, buf_left, "Reason: \"%s\" ", + mlme_get_roam_trigger_str(roam_info->trigger_reason)); + temp += buf_cons; + buf_left -= buf_cons; + + if (roam_info->trigger_sub_reason) { + buf_cons = qdf_snprint( + temp, buf_left, "Sub-Reason: %s", + mlme_get_sub_reason_str(roam_info->trigger_sub_reason)); + temp += buf_cons; + buf_left -= buf_cons; + } + + switch (roam_info->trigger_reason) { + case WMI_ROAM_TRIGGER_REASON_PER: + case WMI_ROAM_TRIGGER_REASON_BMISS: + case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI: + case WMI_ROAM_TRIGGER_REASON_MAWC: + case WMI_ROAM_TRIGGER_REASON_DENSE: + case WMI_ROAM_TRIGGER_REASON_BACKGROUND: + case WMI_ROAM_TRIGGER_REASON_IDLE: + case WMI_ROAM_TRIGGER_REASON_FORCED: + case WMI_ROAM_TRIGGER_REASON_UNIT_TEST: + return; + case WMI_ROAM_TRIGGER_REASON_BTM: + buf_cons = qdf_snprint(temp, buf_left, + "Req_mode: %d Disassoc_timer: %d", + roam_info->btm_trig_data.btm_request_mode, + roam_info->btm_trig_data.disassoc_timer); + temp += buf_cons; + buf_left -= buf_cons; + + buf_cons = qdf_snprint(temp, buf_left, + "validity_interval: %d candidate_list_cnt: %d resp_status: %d, bss_termination_timeout: %d, mbo_assoc_retry_timeout: %d", + roam_info->btm_trig_data.validity_interval, + roam_info->btm_trig_data.candidate_list_count, + roam_info->btm_trig_data.btm_resp_status, + roam_info->btm_trig_data. + btm_bss_termination_timeout, + roam_info->btm_trig_data. + btm_mbo_assoc_retry_timeout); + buf_left -= buf_cons; + temp += buf_cons; + return; + case WMI_ROAM_TRIGGER_REASON_BSS_LOAD: + buf_cons = qdf_snprint(temp, buf_left, "CU: %d %% ", + roam_info->cu_trig_data.cu_load); + temp += buf_cons; + buf_left -= buf_cons; + return; + case WMI_ROAM_TRIGGER_REASON_DEAUTH: + buf_cons = qdf_snprint(temp, buf_left, "Type: %d Reason: %d ", + roam_info->deauth_trig_data.type, + roam_info->deauth_trig_data.reason); + temp += buf_cons; + buf_left -= buf_cons; + return; + case WMI_ROAM_TRIGGER_REASON_LOW_RSSI: + case WMI_ROAM_TRIGGER_REASON_PERIODIC: + /* + * Use roam_info->current_rssi get the RSSI of current AP after + * roam scan is triggered. This avoids discrepency with the + * next rssi threshold value printed in roam scan details. + * roam_info->rssi_trig_data.threshold gives the rssi threshold + * for the Low Rssi/Periodic scan trigger. + */ + buf_cons = qdf_snprint(temp, buf_left, + " Cur_Rssi threshold:%d Current AP RSSI: %d", + roam_info->rssi_trig_data.threshold, + roam_info->current_rssi); + temp += buf_cons; + buf_left -= buf_cons; + return; + default: + return; + } +} + +/** + * wma_rso_print_trigger_info - Roam trigger related details + * @data: Pointer to the roam trigger data + * @vdev_id: Vdev ID + * + * Prints the vdev, roam trigger reason, time of the day at which roaming + * was triggered. + * + * Return: None + */ +static void +wma_rso_print_trigger_info(struct wmi_roam_trigger_info *data, uint8_t vdev_id) +{ + char *buf; + char time[TIME_STRING_LEN]; + + buf = qdf_mem_malloc(MAX_ROAM_DEBUG_BUF_SIZE); + if (!buf) + return; + + wma_get_trigger_detail_str(data, buf); + mlme_get_converted_timestamp(data->timestamp, time); + WMA_LOGI("%s [ROAM_TRIGGER]: VDEV[%d] %s", time, vdev_id, buf); + + qdf_mem_free(buf); +} + +/** + * wma_log_roam_scan_candidates - Print roam scan candidate AP info + * @ap: Pointer to the candidate AP list + * @num_entries: Number of candidate APs + * + * Print the RSSI, CU load, Cu score, RSSI score, total score, BSSID + * and time stamp at which the candidate was found details. + * + * Return: None + */ +static void +wma_log_roam_scan_candidates(struct wmi_roam_candidate_info *ap, + uint8_t num_entries) +{ + uint16_t i; + char time[TIME_STRING_LEN], time2[TIME_STRING_LEN]; + + wma_nofl_info("%62s%62s", LINE_STR, LINE_STR); + wma_nofl_info("%13s %16s %8s %4s %4s %5s/%3s %3s/%3s %7s %7s %6s %12s %20s", + "AP BSSID", "TSTAMP", "CH", "TY", "ETP", "RSSI", + "SCR", "CU%", "SCR", "TOT_SCR", "BL_RSN", "BL_SRC", + "BL_TSTAMP", "BL_TIMEOUT(ms)"); + wma_nofl_info("%62s%62s", LINE_STR, LINE_STR); + + if (num_entries > MAX_ROAM_CANDIDATE_AP) + num_entries = MAX_ROAM_CANDIDATE_AP; + + for (i = 0; i < num_entries; i++) { + mlme_get_converted_timestamp(ap->timestamp, time); + mlme_get_converted_timestamp(ap->bl_timestamp, time2); + wma_nofl_info(QDF_MAC_ADDR_FMT " %17s %4d %-4s %4d %3d/%-4d %2d/%-4d %5d %7d %7d %17s %9d", + QDF_MAC_ADDR_REF(ap->bssid.bytes), time, + ap->freq, + ((ap->type == 0) ? "C_AP" : + ((ap->type == 2) ? "R_AP" : "P_AP")), + ap->etp, ap->rssi, ap->rssi_score, ap->cu_load, + ap->cu_score, ap->total_score, ap->bl_reason, + ap->bl_source, time2, ap->bl_original_timeout); + ap++; + } +} + +/** + * wma_rso_print_scan_info - Print the roam scan details and candidate AP + * details + * @scan: Pointer to the received tlv after sanitization + * @vdev_id: Vdev ID + * @trigger: Roam scan trigger reason + * @timestamp: Host timestamp in millisecs + * + * Prinst the roam scan details with time of the day when the scan was + * triggered and roam candidate AP with score details + * + * Return: None + */ +static void +wma_rso_print_scan_info(struct wmi_roam_scan_data *scan, uint8_t vdev_id, + uint32_t trigger, uint32_t timestamp) +{ + uint16_t num_ch = scan->num_chan; + uint16_t buf_cons = 0, buf_left = ROAM_CHANNEL_BUF_SIZE; + uint8_t i; + char *buf, *buf1, *tmp; + char time[TIME_STRING_LEN]; + + buf = qdf_mem_malloc(ROAM_CHANNEL_BUF_SIZE); + if (!buf) + return; + + tmp = buf; + /* For partial scans, print the channel info */ + if (!scan->type) { + buf_cons = qdf_snprint(tmp, buf_left, "{"); + buf_left -= buf_cons; + tmp += buf_cons; + + for (i = 0; i < num_ch; i++) { + buf_cons = qdf_snprint(tmp, buf_left, "%d ", + scan->chan_freq[i]); + buf_left -= buf_cons; + tmp += buf_cons; + } + buf_cons = qdf_snprint(tmp, buf_left, "}"); + buf_left -= buf_cons; + tmp += buf_cons; + } + + buf1 = qdf_mem_malloc(ROAM_FAILURE_BUF_SIZE); + if (!buf1) { + qdf_mem_free(buf); + return; + } + + if (WMI_ROAM_TRIGGER_REASON_LOW_RSSI == trigger || + WMI_ROAM_TRIGGER_REASON_PERIODIC == trigger) + qdf_snprint(buf1, ROAM_FAILURE_BUF_SIZE, + "next_rssi_threshold: %d dBm", + scan->next_rssi_threshold); + + mlme_get_converted_timestamp(timestamp, time); + WMA_LOGI("%s [ROAM_SCAN]: VDEV[%d] Scan_type: %s %s %s", + time, vdev_id, (scan->type ? "FULL" : "PARTIAL"), + buf1, buf); + wma_log_roam_scan_candidates(scan->ap, scan->num_ap); + + qdf_mem_free(buf); + qdf_mem_free(buf1); +} + +/** + * wma_rso_print_roam_result() - Print roam result related info + * @res: Roam result strucure pointer + * @vdev_id: Vdev id + * + * Print roam result and failure reason if roaming failed. + * + * Return: None + */ +static void +wma_rso_print_roam_result(struct wmi_roam_result *res, + uint8_t vdev_id) +{ + char *buf; + char time[TIME_STRING_LEN]; + + buf = qdf_mem_malloc(ROAM_FAILURE_BUF_SIZE); + if (!buf) + return; + + if (!res->status) + qdf_snprint(buf, ROAM_FAILURE_BUF_SIZE, "Reason: %s", + mlme_get_roam_fail_reason_str(res->fail_reason)); + + mlme_get_converted_timestamp(res->timestamp, time); + WMA_LOGI("%s [ROAM_RESULT]: VDEV[%d] %s %s", + time, vdev_id, (res->status) ? "SUCCESS" : "FAILED", buf); + + qdf_mem_free(buf); +} + +/** + * wma_rso_print_11kv_info - Print neighbor report/BTM related data + * @neigh_rpt: Pointer to the extracted TLV structure + * @vdev_id: Vdev ID + * + * Print BTM/neighbor report info that is sent by firmware after + * connection/roaming to an AP. + * + * Return: none + */ +static void +wma_rso_print_11kv_info(struct wmi_neighbor_report_data *neigh_rpt, + uint8_t vdev_id) +{ + char time[TIME_STRING_LEN], time1[TIME_STRING_LEN]; + char *buf, *tmp; + uint8_t type = neigh_rpt->req_type, i; + uint16_t buf_left = ROAM_CHANNEL_BUF_SIZE, buf_cons; + uint8_t num_ch = neigh_rpt->num_freq; + + if (!type) + return; + + buf = qdf_mem_malloc(ROAM_CHANNEL_BUF_SIZE); + if (!buf) + return; + + tmp = buf; + if (num_ch) { + buf_cons = qdf_snprint(tmp, buf_left, "{ "); + buf_left -= buf_cons; + tmp += buf_cons; + + for (i = 0; i < num_ch; i++) { + buf_cons = qdf_snprint(tmp, buf_left, "%d ", + neigh_rpt->freq[i]); + buf_left -= buf_cons; + tmp += buf_cons; + } + + buf_cons = qdf_snprint(tmp, buf_left, "}"); + buf_left -= buf_cons; + tmp += buf_cons; + } + + mlme_get_converted_timestamp(neigh_rpt->req_time, time); + WMA_LOGI("%s [%s] VDEV[%d]", time, + (type == 1) ? "BTM_QUERY" : "NEIGH_RPT_REQ", vdev_id); + + if (neigh_rpt->resp_time) { + mlme_get_converted_timestamp(neigh_rpt->resp_time, time1); + WMA_LOGI("%s [%s] VDEV[%d] %s", time1, + (type == 1) ? "BTM_REQ" : "NEIGH_RPT_RSP", vdev_id, + (num_ch > 0) ? buf : "NO Ch update"); + } else { + WMA_LOGI("%s No response received from AP", + (type == 1) ? "BTM" : "NEIGH_RPT"); + } + qdf_mem_free(buf); +} + +int wma_roam_stats_event_handler(WMA_HANDLE handle, uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_ROAM_STATS_EVENTID_param_tlvs *param_buf; + wmi_roam_stats_event_fixed_param *fixed_param; + struct mlme_roam_debug_info *roam_info = NULL; + uint8_t vdev_id, i; + uint8_t num_tlv = 0, num_chan = 0, num_ap = 0, num_rpt = 0; + uint32_t rem_len; + QDF_STATUS status; + + param_buf = (WMI_ROAM_STATS_EVENTID_param_tlvs *)event; + if (!param_buf) { + wma_err_rl("NULL event received from target"); + goto err; + } + + fixed_param = param_buf->fixed_param; + if (!fixed_param) { + wma_err_rl(" NULL fixed param"); + goto err; + } + + vdev_id = fixed_param->vdev_id; + if (vdev_id >= wma->max_bssid) { + wma_err_rl("Invalid vdev_id %d", vdev_id); + goto err; + } + + num_tlv = fixed_param->roam_scan_trigger_count; + if (num_tlv > MAX_ROAM_SCAN_STATS_TLV) { + wma_err_rl("Limiting roam triggers to 5"); + num_tlv = MAX_ROAM_SCAN_STATS_TLV; + } + + rem_len = len - sizeof(*fixed_param); + if (rem_len < num_tlv * sizeof(wmi_roam_trigger_reason)) { + wma_err_rl("Invalid roam trigger data"); + goto err; + } + + rem_len -= num_tlv * sizeof(wmi_roam_trigger_reason); + if (rem_len < num_tlv * sizeof(wmi_roam_scan_info)) { + wma_err_rl("Invalid roam scan data"); + goto err; + } + + rem_len -= num_tlv * sizeof(wmi_roam_scan_info); + if (rem_len < num_tlv * sizeof(wmi_roam_result)) { + wma_err_rl("Invalid roam result data"); + goto err; + } + + rem_len -= num_tlv * sizeof(wmi_roam_result); + if (rem_len < (num_tlv * sizeof(wmi_roam_neighbor_report_info))) { + wma_err_rl("Invalid roam neighbor report data"); + goto err; + } + + rem_len -= num_tlv * sizeof(wmi_roam_neighbor_report_info); + if (rem_len < (param_buf->num_roam_scan_chan_info * + sizeof(wmi_roam_scan_channel_info))) { + wma_err_rl("Invalid roam chan data num_tlv:%d", + param_buf->num_roam_scan_chan_info); + goto err; + } + + rem_len -= param_buf->num_roam_scan_chan_info * + sizeof(wmi_roam_scan_channel_info); + + if (rem_len < (param_buf->num_roam_ap_info * + sizeof(wmi_roam_ap_info))) { + wma_err_rl("Invalid roam ap data num_tlv:%d", + param_buf->num_roam_ap_info); + goto err; + } + + rem_len -= param_buf->num_roam_ap_info * sizeof(wmi_roam_ap_info); + if (rem_len < (param_buf->num_roam_neighbor_report_chan_info * + sizeof(wmi_roam_neighbor_report_channel_info))) { + wma_err_rl("Invalid roam neigb rpt chan data num_tlv:%d", + param_buf->num_roam_neighbor_report_chan_info); + goto err; + } + + if (!num_tlv) { + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return -ENOMEM; + + status = wmi_unified_extract_roam_11kv_stats( + wma->wmi_handle, event, + &roam_info->data_11kv, 0, 0); + if (QDF_IS_STATUS_ERROR(status)) { + wma_debug_rl("%s: Roam 11kv stats extract failed vdev %d", + __func__, vdev_id); + qdf_mem_free(roam_info); + goto err; + } + + if (roam_info->data_11kv.present) + wma_rso_print_11kv_info(&roam_info->data_11kv, vdev_id); + + qdf_mem_free(roam_info); + return 0; + } + + for (i = 0; i < num_tlv; i++) { + roam_info = qdf_mem_malloc(sizeof(*roam_info)); + if (!roam_info) + return -ENOMEM; + + /* + * Roam Trigger id and that specific roam trigger related + * details. + */ + status = wmi_unified_extract_roam_trigger_stats( + wma->wmi_handle, event, + &roam_info->trigger, i); + if (QDF_IS_STATUS_ERROR(status)) { + wma_debug_rl("%s: Extract roam trigger stats failed vdev %d at %d iteration", + __func__, vdev_id, i); + qdf_mem_free(roam_info); + return -EINVAL; + } + + /* Roam scan related details - Scan channel, scan type .. */ + status = wmi_unified_extract_roam_scan_stats( + wma->wmi_handle, event, + &roam_info->scan, i, + num_chan, num_ap); + if (QDF_IS_STATUS_ERROR(status)) { + wma_debug_rl("%s: Roam scan stats extract failed vdev %d at %d iteration", + __func__, vdev_id, i); + qdf_mem_free(roam_info); + return -EINVAL; + } + num_chan += roam_info->scan.num_chan; + num_ap += roam_info->scan.num_ap; + + /* Roam result - Success/Failure status, failure reason */ + status = wmi_unified_extract_roam_result_stats( + wma->wmi_handle, event, + &roam_info->result, i); + if (QDF_IS_STATUS_ERROR(status)) { + wma_debug_rl("%s: Roam result stats extract failed vdev %d at %d iteration", + __func__, vdev_id, i); + qdf_mem_free(roam_info); + return -EINVAL; + } + + /* BTM req/resp or Neighbor report/response info */ + status = wmi_unified_extract_roam_11kv_stats( + wma->wmi_handle, event, + &roam_info->data_11kv, i, num_rpt); + if (QDF_IS_STATUS_ERROR(status)) { + wma_debug_rl("%s: Roam 11kv stats extract failed vdev %d at %d iteration", + __func__, vdev_id, i); + qdf_mem_free(roam_info); + return -EINVAL; + } + num_rpt += roam_info->data_11kv.num_freq; + + /* Driver debug logs */ + if (roam_info->trigger.present) + wma_rso_print_trigger_info(&roam_info->trigger, + vdev_id); + + if (roam_info->scan.present && roam_info->trigger.present) + wma_rso_print_scan_info(&roam_info->scan, vdev_id, + roam_info->trigger.trigger_reason, + roam_info->trigger.timestamp); + + if (roam_info->result.present) + wma_rso_print_roam_result(&roam_info->result, vdev_id); + + if (roam_info->data_11kv.present) + wma_rso_print_11kv_info(&roam_info->data_11kv, vdev_id); + + qdf_mem_free(roam_info); + } + + return 0; + +err: + return -EINVAL; +} +#endif + +#define RSN_CAPS_SHIFT 16 +/** + * wma_roam_scan_fill_self_caps() - fill capabilities + * @wma_handle: wma handle + * @roam_offload_params: offload parameters + * @roam_req: roam request + * + * This function fills roam self capablities. + * + * Return: QDF status + */ +QDF_STATUS wma_roam_scan_fill_self_caps(tp_wma_handle wma_handle, + roam_offload_param * + roam_offload_params, + struct roam_offload_scan_req *roam_req) +{ + qdf_size_t val_len; + struct mac_context *mac = NULL; + tSirMacCapabilityInfo selfCaps; + uint32_t val = 0; + uint16_t *pCfgValue16; + uint8_t nCfgValue8, *pCfgValue8; + tSirMacQosInfoStation macQosInfoSta; + + qdf_mem_zero(&macQosInfoSta, sizeof(tSirMacQosInfoStation)); + /* Roaming is done only for INFRA STA type. + * So, ess will be one and ibss will be Zero + */ + mac = cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + WMA_LOGE("%s:NULL mac ptr. Exiting", __func__); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + selfCaps.ess = 1; + selfCaps.ibss = 0; + + val = mac->mlme_cfg->wep_params.is_privacy_enabled; + if (val) + selfCaps.privacy = 1; + + if (mac->mlme_cfg->ht_caps.short_preamble) + selfCaps.shortPreamble = 1; + + selfCaps.pbcc = 0; + selfCaps.channelAgility = 0; + + if (mac->mlme_cfg->feature_flags.enable_short_slot_time_11g) + selfCaps.shortSlotTime = 1; + + if (mac->mlme_cfg->gen.enabled_11h) + selfCaps.spectrumMgt = 1; + + if (mac->mlme_cfg->wmm_params.qos_enabled) + selfCaps.qos = 1; + + if (mac->mlme_cfg->scoring.apsd_enabled) + selfCaps.apsd = 1; + + selfCaps.rrm = mac->rrm.rrmConfig.rrm_enabled; + + val = mac->mlme_cfg->feature_flags.enable_block_ack; + selfCaps.delayedBA = + (uint16_t) ((val >> WNI_CFG_BLOCK_ACK_ENABLED_DELAYED) & 1); + selfCaps.immediateBA = + (uint16_t) ((val >> WNI_CFG_BLOCK_ACK_ENABLED_IMMEDIATE) & 1); + pCfgValue16 = (uint16_t *) &selfCaps; + + /* + * RSN caps arent been sent to firmware, so in case of PMF required, + * the firmware connects to a non PMF AP advertising PMF not required + * in the re-assoc request which violates protocol. + * So send this to firmware in the roam SCAN offload command to + * let it configure the params in the re-assoc request too. + * Instead of making another infra, send the RSN-CAPS in MSB of + * beacon Caps. + */ + roam_offload_params->capability = *((uint16_t *)(&roam_req->rsn_caps)); + roam_offload_params->capability <<= RSN_CAPS_SHIFT; + roam_offload_params->capability |= ((*pCfgValue16) & 0xFFFF); + + roam_offload_params->ht_caps_info = + *(uint16_t *)&mac->mlme_cfg->ht_caps.ht_cap_info; + + roam_offload_params->ampdu_param = + *(uint8_t *)&mac->mlme_cfg->ht_caps.ampdu_params; + + roam_offload_params->ht_ext_cap = + *(uint16_t *)&mac->mlme_cfg->ht_caps.ext_cap_info; + + val_len = ROAM_OFFLOAD_NUM_MCS_SET; + if (wlan_mlme_get_cfg_str((uint8_t *)roam_offload_params->mcsset, + &mac->mlme_cfg->rates.supported_mcs_set, + &val_len) != QDF_STATUS_SUCCESS) { + QDF_TRACE(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_ERROR, + "Failed to get CFG_SUPPORTED_MCS_SET"); + return QDF_STATUS_E_FAILURE; + } + + /* tSirMacTxBFCapabilityInfo */ + nCfgValue8 = (uint8_t)mac->mlme_cfg->vht_caps.vht_cap_info.tx_bf_cap; + roam_offload_params->ht_txbf = nCfgValue8 & 0xFF; + /* tSirMacASCapabilityInfo */ + nCfgValue8 = (uint8_t)mac->mlme_cfg->vht_caps.vht_cap_info.as_cap; + roam_offload_params->asel_cap = nCfgValue8 & 0xFF; + + /* QOS Info */ + nCfgValue8 = mac->mlme_cfg->wmm_params.max_sp_length; + macQosInfoSta.maxSpLen = nCfgValue8; + macQosInfoSta.moreDataAck = 0; + macQosInfoSta.qack = 0; + macQosInfoSta.acbe_uapsd = roam_req->AcUapsd.acbe_uapsd; + macQosInfoSta.acbk_uapsd = roam_req->AcUapsd.acbk_uapsd; + macQosInfoSta.acvi_uapsd = roam_req->AcUapsd.acvi_uapsd; + macQosInfoSta.acvo_uapsd = roam_req->AcUapsd.acvo_uapsd; + pCfgValue8 = (uint8_t *) &macQosInfoSta; + /* macQosInfoSta Only queue_request is set.Refer to + * populate_dot11f_wmm_caps for more details + */ + roam_offload_params->qos_caps = (*pCfgValue8) & 0xFF; + if (roam_offload_params->qos_caps) + roam_offload_params->qos_enabled = true; + roam_offload_params->wmm_caps = 0x4 & 0xFF; + return QDF_STATUS_SUCCESS; +} + +/** + * wma_set_ric_req() - set ric request element + * @wma: wma handle + * @msg: message + * @is_add_ts: is addts required + * + * This function sets ric request element for 11r roaming. + * + * Return: none + */ +void wma_set_ric_req(tp_wma_handle wma, void *msg, uint8_t is_add_ts) +{ + if (!wma) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return; + } + + wmi_unified_set_ric_req_cmd(wma->wmi_handle, msg, is_add_ts); +} +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + +#ifdef FEATURE_RSSI_MONITOR +QDF_STATUS wma_set_rssi_monitoring(tp_wma_handle wma, + struct rssi_monitor_param *req) +{ + if (!wma) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_set_rssi_monitoring_cmd(wma->wmi_handle, req); +} + +/** + * wma_rssi_breached_event_handler() - rssi breached event handler + * @handle: wma handle + * @cmd_param_info: event handler data + * @len: length of @cmd_param_info + * + * Return: 0 on success; error number otherwise + */ +int wma_rssi_breached_event_handler(void *handle, + u_int8_t *cmd_param_info, u_int32_t len) +{ + WMI_RSSI_BREACH_EVENTID_param_tlvs *param_buf; + wmi_rssi_breach_event_fixed_param *event; + struct rssi_breach_event rssi; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!mac || !wma) { + WMA_LOGE("%s: Invalid mac/wma context", __func__); + return -EINVAL; + } + if (!mac->sme.rssi_threshold_breached_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + param_buf = (WMI_RSSI_BREACH_EVENTID_param_tlvs *)cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid rssi breached event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + + rssi.request_id = event->request_id; + rssi.session_id = event->vdev_id; + if (wmi_service_enabled(wma->wmi_handle, + wmi_service_hw_db2dbm_support)) + rssi.curr_rssi = event->rssi; + else + rssi.curr_rssi = event->rssi + WMA_TGT_NOISE_FLOOR_DBM; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->bssid, rssi.curr_bssid.bytes); + + WMA_LOGD("%s: req_id: %u vdev_id: %d curr_rssi: %d", __func__, + rssi.request_id, rssi.session_id, rssi.curr_rssi); + WMA_LOGI("%s: curr_bssid: "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(rssi.curr_bssid.bytes)); + + mac->sme.rssi_threshold_breached_cb(mac->hdd_handle, &rssi); + WMA_LOGD("%s: Invoke HDD rssi breached callback", __func__); + return 0; +} +#endif /* FEATURE_RSSI_MONITOR */ + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +/** + * wma_roam_ho_fail_handler() - LFR3.0 roam hand off failed handler + * @wma: wma handle + * @vdev_id: vdev id + * + * Return: none + */ +static void +wma_roam_ho_fail_handler(tp_wma_handle wma, uint32_t vdev_id, + struct qdf_mac_addr bssid) +{ + struct handoff_failure_ind *ho_failure_ind; + struct scheduler_msg sme_msg = { 0 }; + QDF_STATUS qdf_status; + + ho_failure_ind = qdf_mem_malloc(sizeof(*ho_failure_ind)); + if (!ho_failure_ind) + return; + + ho_failure_ind->vdev_id = vdev_id; + ho_failure_ind->bssid = bssid; + + sme_msg.type = eWNI_SME_HO_FAIL_IND; + sme_msg.bodyptr = ho_failure_ind; + sme_msg.bodyval = 0; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("Fail to post eWNI_SME_HO_FAIL_IND msg to SME"); + qdf_mem_free(ho_failure_ind); + return; + } +} + +/** + * wma_process_roam_synch_complete() - roam synch complete command to fw. + * @handle: wma handle + * @synchcnf: offload synch confirmation params + * + * This function sends roam synch complete event to fw. + * + * Return: none + */ +void wma_process_roam_synch_complete(WMA_HANDLE handle, uint8_t vdev_id) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + if (!wma_handle || !wma_handle->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue roam synch cnf", + __func__); + return; + } + + if (!wma_is_vdev_valid(vdev_id)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, vdev_id); + return; + } + + if (wmi_unified_roam_synch_complete_cmd(wma_handle->wmi_handle, + vdev_id)) { + return; + } + + DPTRACE(qdf_dp_trace_record_event(QDF_DP_TRACE_EVENT_RECORD, + vdev_id, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_PROTO_TYPE_EVENT, QDF_ROAM_COMPLETE)); + + WMA_LOGI("LFR3: Posting WMA_ROAM_OFFLOAD_SYNCH_CNF"); + wlan_roam_debug_log(vdev_id, DEBUG_ROAM_SYNCH_CNF, + DEBUG_INVALID_PEER_ID, NULL, NULL, 0, 0); + +} +#endif /* WLAN_FEATURE_ROAM_OFFLOAD */ + +QDF_STATUS wma_pre_chan_switch_setup(uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *intr; + uint16_t beacon_interval_ori; + bool restart; + uint16_t reduced_beacon_interval; + struct vdev_mlme_obj *mlme_obj; + struct wlan_objmgr_vdev *vdev; + + if (!wma) { + pe_err("wma is NULL"); + return QDF_STATUS_E_FAILURE; + } + intr = &wma->interfaces[vdev_id]; + if (!intr) { + pe_err("wma txrx node is NULL"); + return QDF_STATUS_E_FAILURE; + } + vdev = intr->vdev; + mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!mlme_obj) { + pe_err("vdev component object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + restart = + wma_get_channel_switch_in_progress(intr); + if (restart && intr->beacon_filter_enabled) + wma_remove_beacon_filter(wma, &intr->beacon_filter); + + reduced_beacon_interval = + wma->mac_context->sap.SapDfsInfo.reduced_beacon_interval; + if (wma_is_vdev_in_ap_mode(wma, vdev_id) && reduced_beacon_interval) { + + + /* Reduce the beacon interval just before the channel switch. + * This would help in reducing the downtime on the STA side + * (which is waiting for beacons from the AP to resume back + * transmission). Switch back the beacon_interval to its + * original value after the channel switch based on the + * timeout. This would ensure there are atleast some beacons + * sent with increased frequency. + */ + + WMA_LOGD("%s: Changing beacon interval to %d", + __func__, reduced_beacon_interval); + + /* Add a timer to reset the beacon interval back*/ + beacon_interval_ori = mlme_obj->proto.generic.beacon_interval; + mlme_obj->proto.generic.beacon_interval = + reduced_beacon_interval; + if (wma_fill_beacon_interval_reset_req(wma, + vdev_id, + beacon_interval_ori, + RESET_BEACON_INTERVAL_TIMEOUT)) { + + WMA_LOGD("%s: Failed to fill beacon interval reset req", + __func__); + } + } + + status = wma_vdev_pre_start(vdev_id, restart); + + return status; +} + +QDF_STATUS wma_post_chan_switch_setup(uint8_t vdev_id) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct wma_txrx_node *intr; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct wlan_channel *des_chan; + cdp_config_param_type val; + + if (!wma) { + pe_err("wma is NULL"); + return QDF_STATUS_E_FAILURE; + } + intr = &wma->interfaces[vdev_id]; + if (!intr) { + pe_err("wma txrx node is NULL"); + return QDF_STATUS_E_FAILURE; + } + /* + * Record monitor mode channel here in case HW + * indicate RX PPDU TLV with invalid channel number. + */ + if (intr->type == WMI_VDEV_TYPE_MONITOR) { + des_chan = intr->vdev->vdev_mlme.des_chan; + val.cdp_pdev_param_monitor_chan = des_chan->ch_ieee; + cdp_txrx_set_pdev_param(soc, + wlan_objmgr_pdev_get_pdev_id(wma->pdev), + CDP_MONITOR_CHANNEL, val); + val.cdp_pdev_param_mon_freq = des_chan->ch_freq; + cdp_txrx_set_pdev_param(soc, + wlan_objmgr_pdev_get_pdev_id(wma->pdev), + CDP_MONITOR_FREQUENCY, val); + } + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_WLAN_ESE +/** + * wma_plm_start() - plm start request + * @wma: wma handle + * @params: plm request parameters + * + * This function request FW to start PLM. + * + * Return: QDF status + */ +static QDF_STATUS wma_plm_start(tp_wma_handle wma, + struct plm_req_params *params) +{ + QDF_STATUS status; + + WMA_LOGD("PLM Start"); + + status = wmi_unified_plm_start_cmd(wma->wmi_handle, params); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + wma->interfaces[params->vdev_id].plm_in_progress = true; + + WMA_LOGD("Plm start request sent successfully for vdev %d", + params->vdev_id); + + return status; +} + +/** + * wma_plm_stop() - plm stop request + * @wma: wma handle + * @params: plm request parameters + * + * This function request FW to stop PLM. + * + * Return: QDF status + */ +static QDF_STATUS wma_plm_stop(tp_wma_handle wma, + struct plm_req_params *params) +{ + QDF_STATUS status; + + if (!wma->interfaces[params->vdev_id].plm_in_progress) { + WMA_LOGE("No active plm req found, skip plm stop req"); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("PLM Stop"); + + status = wmi_unified_plm_stop_cmd(wma->wmi_handle, params); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + wma->interfaces[params->vdev_id].plm_in_progress = false; + + WMA_LOGD("Plm stop request sent successfully for vdev %d", + params->vdev_id); + + return status; +} + +/** + * wma_config_plm() - config PLM + * @wma: wma handle + * @params: plm request parameters + * + * Return: none + */ +void wma_config_plm(tp_wma_handle wma, struct plm_req_params *params) +{ + QDF_STATUS ret; + + if (!params || !wma) + return; + + if (params->enable) + ret = wma_plm_start(wma, params); + else + ret = wma_plm_stop(wma, params); + + if (ret) + WMA_LOGE("%s: PLM %s failed %d", __func__, + params->enable ? "start" : "stop", ret); +} +#endif + +#ifdef FEATURE_WLAN_EXTSCAN +/** + * wma_extscan_wow_event_callback() - extscan wow event callback + * @handle: WMA handle + * @event: event buffer + * @len: length of @event buffer + * + * In wow case, the wow event is followed by the payload of the event + * which generated the wow event. + * payload is 4 bytes of length followed by event buffer. the first 4 bytes + * of event buffer is common tlv header, which is a combination + * of tag (higher 2 bytes) and length (lower 2 bytes). The tag is used to + * identify the event which triggered wow event. + * Payload is extracted and converted into generic tlv structure before + * being passed to this function. + * + * @Return: Errno + */ +int wma_extscan_wow_event_callback(void *handle, void *event, uint32_t len) +{ + uint32_t tag = WMITLV_GET_TLVTAG(WMITLV_GET_HDR(event)); + + switch (tag) { + case WMITLV_TAG_STRUC_wmi_extscan_start_stop_event_fixed_param: + return wma_extscan_start_stop_event_handler(handle, event, len); + + case WMITLV_TAG_STRUC_wmi_extscan_operation_event_fixed_param: + return wma_extscan_operations_event_handler(handle, event, len); + + case WMITLV_TAG_STRUC_wmi_extscan_table_usage_event_fixed_param: + return wma_extscan_table_usage_event_handler(handle, event, + len); + + case WMITLV_TAG_STRUC_wmi_extscan_cached_results_event_fixed_param: + return wma_extscan_cached_results_event_handler(handle, event, + len); + + case WMITLV_TAG_STRUC_wmi_extscan_wlan_change_results_event_fixed_param: + return wma_extscan_change_results_event_handler(handle, event, + len); + + case WMITLV_TAG_STRUC_wmi_extscan_hotlist_match_event_fixed_param: + return wma_extscan_hotlist_match_event_handler(handle, event, + len); + + case WMITLV_TAG_STRUC_wmi_extscan_capabilities_event_fixed_param: + return wma_extscan_capabilities_event_handler(handle, event, + len); + + default: + WMA_LOGE(FL("Unknown tag: %d"), tag); + return 0; + } +} + +/** + * wma_register_extscan_event_handler() - register extscan event handler + * @wma_handle: wma handle + * + * This function register extscan related event handlers. + * + * Return: none + */ +void wma_register_extscan_event_handler(tp_wma_handle wma_handle) +{ + if (!wma_handle) { + WMA_LOGE("%s: extscan wma_handle is NULL", __func__); + return; + } + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_extscan_start_stop_event_id, + wma_extscan_start_stop_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_extscan_capabilities_event_id, + wma_extscan_capabilities_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_extscan_hotlist_match_event_id, + wma_extscan_hotlist_match_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_extscan_wlan_change_results_event_id, + wma_extscan_change_results_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_extscan_operation_event_id, + wma_extscan_operations_event_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_extscan_table_usage_event_id, + wma_extscan_table_usage_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_extscan_cached_results_event_id, + wma_extscan_cached_results_event_handler, + WMA_RX_SERIALIZER_CTX); + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_passpoint_match_event_id, + wma_passpoint_match_event_handler, + WMA_RX_SERIALIZER_CTX); +} + +/** + * wma_extscan_start_stop_event_handler() - extscan start/stop event handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: data length + * + * This function handles different extscan related commands + * like start/stop/get results etc and indicate to upper layers. + * + * Return: 0 for success or error code. + */ +int wma_extscan_start_stop_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_EXTSCAN_START_STOP_EVENTID_param_tlvs *param_buf; + wmi_extscan_start_stop_event_fixed_param *event; + struct sir_extscan_generic_response *extscan_ind; + uint16_t event_type; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: Invalid mac", __func__); + return -EINVAL; + } + if (!mac->sme.ext_scan_ind_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + param_buf = (WMI_EXTSCAN_START_STOP_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid extscan event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + extscan_ind = qdf_mem_malloc(sizeof(*extscan_ind)); + if (!extscan_ind) + return -ENOMEM; + + switch (event->command) { + case WMI_EXTSCAN_START_CMDID: + event_type = eSIR_EXTSCAN_START_RSP; + extscan_ind->status = event->status; + extscan_ind->request_id = event->request_id; + break; + case WMI_EXTSCAN_STOP_CMDID: + event_type = eSIR_EXTSCAN_STOP_RSP; + extscan_ind->status = event->status; + extscan_ind->request_id = event->request_id; + break; + case WMI_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMDID: + extscan_ind->status = event->status; + extscan_ind->request_id = event->request_id; + if (event->mode == WMI_EXTSCAN_MODE_STOP) + event_type = + eSIR_EXTSCAN_RESET_SIGNIFICANT_WIFI_CHANGE_RSP; + else + event_type = + eSIR_EXTSCAN_SET_SIGNIFICANT_WIFI_CHANGE_RSP; + break; + case WMI_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMDID: + extscan_ind->status = event->status; + extscan_ind->request_id = event->request_id; + if (event->mode == WMI_EXTSCAN_MODE_STOP) + event_type = eSIR_EXTSCAN_RESET_BSSID_HOTLIST_RSP; + else + event_type = eSIR_EXTSCAN_SET_BSSID_HOTLIST_RSP; + break; + case WMI_EXTSCAN_GET_CACHED_RESULTS_CMDID: + extscan_ind->status = event->status; + extscan_ind->request_id = event->request_id; + event_type = eSIR_EXTSCAN_CACHED_RESULTS_RSP; + break; + case WMI_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMDID: + extscan_ind->status = event->status; + extscan_ind->request_id = event->request_id; + if (event->mode == WMI_EXTSCAN_MODE_STOP) + event_type = + eSIR_EXTSCAN_RESET_SSID_HOTLIST_RSP; + else + event_type = + eSIR_EXTSCAN_SET_SSID_HOTLIST_RSP; + break; + default: + WMA_LOGE("%s: Unknown event(%d) from target", + __func__, event->status); + qdf_mem_free(extscan_ind); + return -EINVAL; + } + mac->sme.ext_scan_ind_cb(mac->hdd_handle, event_type, extscan_ind); + WMA_LOGD("%s: sending event to umac for requestid %u with status %d", + __func__, extscan_ind->request_id, extscan_ind->status); + qdf_mem_free(extscan_ind); + return 0; +} + +/** + * wma_extscan_operations_event_handler() - extscan operation event handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: length + * + * This function handles different operations related event and indicate + * upper layers with appropriate callback. + * + * Return: 0 for success or error code. + */ +int wma_extscan_operations_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_EXTSCAN_OPERATION_EVENTID_param_tlvs *param_buf; + wmi_extscan_operation_event_fixed_param *oprn_event; + tSirExtScanOnScanEventIndParams *oprn_ind; + uint32_t cnt; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: Invalid mac", __func__); + return -EINVAL; + } + if (!mac->sme.ext_scan_ind_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + param_buf = (WMI_EXTSCAN_OPERATION_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid scan operation event", __func__); + return -EINVAL; + } + oprn_event = param_buf->fixed_param; + oprn_ind = qdf_mem_malloc(sizeof(*oprn_ind)); + if (!oprn_ind) + return -ENOMEM; + + oprn_ind->requestId = oprn_event->request_id; + + switch (oprn_event->event) { + case WMI_EXTSCAN_BUCKET_COMPLETED_EVENT: + oprn_ind->status = 0; + goto exit_handler; + case WMI_EXTSCAN_CYCLE_STARTED_EVENT: + WMA_LOGD("%s: received WMI_EXTSCAN_CYCLE_STARTED_EVENT", + __func__); + + if (oprn_event->num_buckets > param_buf->num_bucket_id) { + WMA_LOGE("FW mesg num_buk %d more than TLV hdr %d", + oprn_event->num_buckets, + param_buf->num_bucket_id); + qdf_mem_free(oprn_ind); + return -EINVAL; + } + + cds_host_diag_log_work(&wma->extscan_wake_lock, + WMA_EXTSCAN_CYCLE_WAKE_LOCK_DURATION, + WIFI_POWER_EVENT_WAKELOCK_EXT_SCAN); + qdf_wake_lock_timeout_acquire(&wma->extscan_wake_lock, + WMA_EXTSCAN_CYCLE_WAKE_LOCK_DURATION); + oprn_ind->scanEventType = WIFI_EXTSCAN_CYCLE_STARTED_EVENT; + oprn_ind->status = 0; + oprn_ind->buckets_scanned = 0; + for (cnt = 0; cnt < oprn_event->num_buckets; cnt++) + oprn_ind->buckets_scanned |= + (1 << param_buf->bucket_id[cnt]); + WMA_LOGD(FL("num_buckets %u request_id %u buckets_scanned %u"), + oprn_event->num_buckets, oprn_ind->requestId, + oprn_ind->buckets_scanned); + break; + case WMI_EXTSCAN_CYCLE_COMPLETED_EVENT: + WMA_LOGD("%s: received WMI_EXTSCAN_CYCLE_COMPLETED_EVENT", + __func__); + qdf_wake_lock_release(&wma->extscan_wake_lock, + WIFI_POWER_EVENT_WAKELOCK_EXT_SCAN); + oprn_ind->scanEventType = WIFI_EXTSCAN_CYCLE_COMPLETED_EVENT; + oprn_ind->status = 0; + /* Set bucket scanned mask to zero on cycle complete */ + oprn_ind->buckets_scanned = 0; + break; + case WMI_EXTSCAN_BUCKET_STARTED_EVENT: + WMA_LOGD("%s: received WMI_EXTSCAN_BUCKET_STARTED_EVENT", + __func__); + oprn_ind->scanEventType = WIFI_EXTSCAN_BUCKET_STARTED_EVENT; + oprn_ind->status = 0; + goto exit_handler; + case WMI_EXTSCAN_THRESHOLD_NUM_SCANS: + WMA_LOGD("%s: received WMI_EXTSCAN_THRESHOLD_NUM_SCANS", + __func__); + oprn_ind->scanEventType = WIFI_EXTSCAN_THRESHOLD_NUM_SCANS; + oprn_ind->status = 0; + break; + case WMI_EXTSCAN_THRESHOLD_PERCENT: + WMA_LOGD("%s: received WMI_EXTSCAN_THRESHOLD_PERCENT", + __func__); + oprn_ind->scanEventType = WIFI_EXTSCAN_THRESHOLD_PERCENT; + oprn_ind->status = 0; + break; + default: + WMA_LOGE("%s: Unknown event(%d) from target", + __func__, oprn_event->event); + qdf_mem_free(oprn_ind); + return -EINVAL; + } + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_EXTSCAN_SCAN_PROGRESS_EVENT_IND, oprn_ind); + WMA_LOGI("%s: sending scan progress event to hdd", __func__); +exit_handler: + qdf_mem_free(oprn_ind); + return 0; +} + +/** + * wma_extscan_table_usage_event_handler() - extscan table usage event handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: length + * + * This function handles table usage related event and indicate + * upper layers with appropriate callback. + * + * Return: 0 for success or error code. + */ +int wma_extscan_table_usage_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_EXTSCAN_TABLE_USAGE_EVENTID_param_tlvs *param_buf; + wmi_extscan_table_usage_event_fixed_param *event; + tSirExtScanResultsAvailableIndParams *tbl_usg_ind; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: Invalid mac", __func__); + return -EINVAL; + } + if (!mac->sme.ext_scan_ind_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + param_buf = (WMI_EXTSCAN_TABLE_USAGE_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid table usage event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + tbl_usg_ind = qdf_mem_malloc(sizeof(*tbl_usg_ind)); + if (!tbl_usg_ind) + return -ENOMEM; + + tbl_usg_ind->requestId = event->request_id; + tbl_usg_ind->numResultsAvailable = event->entries_in_use; + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_EXTSCAN_SCAN_RES_AVAILABLE_IND, + tbl_usg_ind); + WMA_LOGI("%s: sending scan_res available event to hdd", __func__); + qdf_mem_free(tbl_usg_ind); + return 0; +} + +/** + * wma_extscan_capabilities_event_handler() - extscan capabilities event handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: length + * + * This function handles capabilities event and indicate + * upper layers with registered callback. + * + * Return: 0 for success or error code. + */ +int wma_extscan_capabilities_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_EXTSCAN_CAPABILITIES_EVENTID_param_tlvs *param_buf; + wmi_extscan_capabilities_event_fixed_param *event; + wmi_extscan_cache_capabilities *src_cache; + wmi_extscan_hotlist_monitor_capabilities *src_hotlist; + wmi_extscan_wlan_change_monitor_capabilities *src_change; + struct ext_scan_capabilities_response *dest_capab; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: Invalid mac", __func__); + return -EINVAL; + } + if (!mac->sme.ext_scan_ind_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + param_buf = (WMI_EXTSCAN_CAPABILITIES_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid capabilities event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + src_cache = param_buf->extscan_cache_capabilities; + src_hotlist = param_buf->hotlist_capabilities; + src_change = param_buf->wlan_change_capabilities; + + if (!src_cache || !src_hotlist || !src_change) { + WMA_LOGE("%s: Invalid capabilities list", __func__); + return -EINVAL; + } + dest_capab = qdf_mem_malloc(sizeof(*dest_capab)); + if (!dest_capab) + return -ENOMEM; + + dest_capab->requestId = event->request_id; + dest_capab->max_scan_buckets = src_cache->max_buckets; + dest_capab->max_scan_cache_size = src_cache->scan_cache_entry_size; + dest_capab->max_ap_cache_per_scan = src_cache->max_bssid_per_scan; + dest_capab->max_scan_reporting_threshold = + src_cache->max_table_usage_threshold; + + dest_capab->max_hotlist_bssids = src_hotlist->max_hotlist_entries; + dest_capab->max_rssi_sample_size = + src_change->max_rssi_averaging_samples; + dest_capab->max_bssid_history_entries = + src_change->max_rssi_history_entries; + dest_capab->max_significant_wifi_change_aps = + src_change->max_wlan_change_entries; + dest_capab->max_hotlist_ssids = + event->num_extscan_hotlist_ssid; + dest_capab->max_number_epno_networks = + event->num_epno_networks; + dest_capab->max_number_epno_networks_by_ssid = + event->num_epno_networks; + dest_capab->max_number_of_white_listed_ssid = + event->num_roam_ssid_whitelist; + dest_capab->max_number_of_black_listed_bssid = + event->num_roam_bssid_blacklist; + dest_capab->status = 0; + + WMA_LOGD("%s: request_id: %u status: %d", + __func__, dest_capab->requestId, dest_capab->status); + + WMA_LOGD("%s: Capabilities: max_scan_buckets: %d, max_hotlist_bssids: %d, max_scan_cache_size: %d, max_ap_cache_per_scan: %d", + __func__, dest_capab->max_scan_buckets, + dest_capab->max_hotlist_bssids, dest_capab->max_scan_cache_size, + dest_capab->max_ap_cache_per_scan); + WMA_LOGD("%s: max_scan_reporting_threshold: %d, max_rssi_sample_size: %d, max_bssid_history_entries: %d, max_significant_wifi_change_aps: %d", + __func__, dest_capab->max_scan_reporting_threshold, + dest_capab->max_rssi_sample_size, + dest_capab->max_bssid_history_entries, + dest_capab->max_significant_wifi_change_aps); + + WMA_LOGD("%s: Capabilities: max_hotlist_ssids: %d, max_number_epno_networks: %d, max_number_epno_networks_by_ssid: %d", + __func__, dest_capab->max_hotlist_ssids, + dest_capab->max_number_epno_networks, + dest_capab->max_number_epno_networks_by_ssid); + WMA_LOGD("%s: max_number_of_white_listed_ssid: %d, max_number_of_black_listed_bssid: %d", + __func__, dest_capab->max_number_of_white_listed_ssid, + dest_capab->max_number_of_black_listed_bssid); + + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_EXTSCAN_GET_CAPABILITIES_IND, dest_capab); + qdf_mem_free(dest_capab); + return 0; +} + +/** + * wma_extscan_hotlist_match_event_handler() - hotlist match event handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: length + * + * This function handles hotlist match event and indicate + * upper layers with registered callback. + * + * Return: 0 for success or error code. + */ +int wma_extscan_hotlist_match_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_EXTSCAN_HOTLIST_MATCH_EVENTID_param_tlvs *param_buf; + wmi_extscan_hotlist_match_event_fixed_param *event; + struct extscan_hotlist_match *dest_hotlist; + tSirWifiScanResult *dest_ap; + wmi_extscan_wlan_descriptor *src_hotlist; + uint32_t numap; + int j, ap_found = 0; + uint32_t buf_len; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: Invalid mac", __func__); + return -EINVAL; + } + if (!mac->sme.ext_scan_ind_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + param_buf = (WMI_EXTSCAN_HOTLIST_MATCH_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid hotlist match event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + src_hotlist = param_buf->hotlist_match; + numap = event->total_entries; + + if (!src_hotlist || !numap) { + WMA_LOGE("%s: Hotlist AP's list invalid", __func__); + return -EINVAL; + } + if (numap > param_buf->num_hotlist_match) { + WMA_LOGE("Invalid no of total enteries %d", numap); + return -EINVAL; + } + if (numap > WMA_EXTSCAN_MAX_HOTLIST_ENTRIES) { + WMA_LOGE("%s: Total Entries %u greater than max", + __func__, numap); + numap = WMA_EXTSCAN_MAX_HOTLIST_ENTRIES; + } + + buf_len = sizeof(wmi_extscan_hotlist_match_event_fixed_param) + + WMI_TLV_HDR_SIZE + + (numap * sizeof(wmi_extscan_wlan_descriptor)); + + if (buf_len > len) { + WMA_LOGE("Invalid buf len from FW %d numap %d", len, numap); + return -EINVAL; + } + + dest_hotlist = qdf_mem_malloc(sizeof(*dest_hotlist) + + sizeof(*dest_ap) * numap); + if (!dest_hotlist) + return -ENOMEM; + + dest_ap = &dest_hotlist->ap[0]; + dest_hotlist->numOfAps = event->total_entries; + dest_hotlist->requestId = event->config_request_id; + + if (event->first_entry_index + + event->num_entries_in_page < event->total_entries) + dest_hotlist->moreData = 1; + else + dest_hotlist->moreData = 0; + + WMA_LOGD("%s: Hotlist match: requestId: %u," + "numOfAps: %d", __func__, + dest_hotlist->requestId, dest_hotlist->numOfAps); + + /* + * Currently firmware sends only one bss information in-case + * of both hotlist ap found and lost. + */ + for (j = 0; j < numap; j++) { + dest_ap->rssi = 0; + dest_ap->channel = src_hotlist->channel; + dest_ap->ts = src_hotlist->tstamp; + ap_found = src_hotlist->flags & WMI_HOTLIST_FLAG_PRESENCE; + dest_ap->rtt = src_hotlist->rtt; + dest_ap->rtt_sd = src_hotlist->rtt_sd; + dest_ap->beaconPeriod = src_hotlist->beacon_interval; + dest_ap->capability = src_hotlist->capabilities; + dest_ap->ieLength = src_hotlist->ie_length; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&src_hotlist->bssid, + dest_ap->bssid.bytes); + if (src_hotlist->ssid.ssid_len > WLAN_SSID_MAX_LEN) { + WMA_LOGE("%s Invalid SSID len %d, truncating", + __func__, src_hotlist->ssid.ssid_len); + src_hotlist->ssid.ssid_len = WLAN_SSID_MAX_LEN; + } + qdf_mem_copy(dest_ap->ssid, src_hotlist->ssid.ssid, + src_hotlist->ssid.ssid_len); + dest_ap->ssid[src_hotlist->ssid.ssid_len] = '\0'; + dest_ap++; + src_hotlist++; + } + dest_hotlist->ap_found = ap_found; + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_EXTSCAN_HOTLIST_MATCH_IND, dest_hotlist); + WMA_LOGI("%s: sending hotlist match event to hdd", __func__); + qdf_mem_free(dest_hotlist); + return 0; +} + +/** wma_extscan_find_unique_scan_ids() - find unique scan ids + * @cmd_param_info: event data. + * + * This utility function parses the input bss table of information + * and find the unique number of scan ids + * + * Return: 0 on success; error number otherwise + */ +static int wma_extscan_find_unique_scan_ids(const u_int8_t *cmd_param_info) +{ + WMI_EXTSCAN_CACHED_RESULTS_EVENTID_param_tlvs *param_buf; + wmi_extscan_cached_results_event_fixed_param *event; + wmi_extscan_wlan_descriptor *src_hotlist; + wmi_extscan_rssi_info *src_rssi; + int prev_scan_id, scan_ids_cnt, i; + + param_buf = (WMI_EXTSCAN_CACHED_RESULTS_EVENTID_param_tlvs *) + cmd_param_info; + event = param_buf->fixed_param; + src_hotlist = param_buf->bssid_list; + src_rssi = param_buf->rssi_list; + + /* Find the unique number of scan_id's for grouping */ + prev_scan_id = src_rssi->scan_cycle_id; + scan_ids_cnt = 1; + for (i = 1; i < param_buf->num_rssi_list; i++) { + src_rssi++; + + if (prev_scan_id != src_rssi->scan_cycle_id) { + scan_ids_cnt++; + prev_scan_id = src_rssi->scan_cycle_id; + } + } + + return scan_ids_cnt; +} + +/** wma_fill_num_results_per_scan_id() - fill number of bss per scan id + * @cmd_param_info: event data. + * @scan_id_group: pointer to scan id group. + * + * This utility function parses the input bss table of information + * and finds how many bss are there per unique scan id. + * + * Return: 0 on success; error number otherwise + */ +static int wma_fill_num_results_per_scan_id(const u_int8_t *cmd_param_info, + struct extscan_cached_scan_result *scan_id_group) +{ + WMI_EXTSCAN_CACHED_RESULTS_EVENTID_param_tlvs *param_buf; + wmi_extscan_cached_results_event_fixed_param *event; + wmi_extscan_wlan_descriptor *src_hotlist; + wmi_extscan_rssi_info *src_rssi; + struct extscan_cached_scan_result *t_scan_id_grp; + int i, prev_scan_id; + + param_buf = (WMI_EXTSCAN_CACHED_RESULTS_EVENTID_param_tlvs *) + cmd_param_info; + event = param_buf->fixed_param; + src_hotlist = param_buf->bssid_list; + src_rssi = param_buf->rssi_list; + t_scan_id_grp = scan_id_group; + + prev_scan_id = src_rssi->scan_cycle_id; + + t_scan_id_grp->scan_id = src_rssi->scan_cycle_id; + t_scan_id_grp->flags = src_rssi->flags; + t_scan_id_grp->buckets_scanned = src_rssi->buckets_scanned; + t_scan_id_grp->num_results = 1; + for (i = 1; i < param_buf->num_rssi_list; i++) { + src_rssi++; + if (prev_scan_id == src_rssi->scan_cycle_id) { + t_scan_id_grp->num_results++; + } else { + t_scan_id_grp++; + prev_scan_id = t_scan_id_grp->scan_id = + src_rssi->scan_cycle_id; + t_scan_id_grp->flags = src_rssi->flags; + t_scan_id_grp->buckets_scanned = + src_rssi->buckets_scanned; + t_scan_id_grp->num_results = 1; + } + } + return 0; +} + +/** wma_group_num_bss_to_scan_id() - group bss to scan id table + * @cmd_param_info: event data. + * @cached_result: pointer to cached table. + * + * This function reads the bss information from the format + * ------------------------------------------------------------------------ + * | bss info {rssi, channel, ssid, bssid, timestamp} | scan id_1 | flags | + * | bss info {rssi, channel, ssid, bssid, timestamp} | scan id_2 | flags | + * ........................................................................ + * | bss info {rssi, channel, ssid, bssid, timestamp} | scan id_N | flags | + * ------------------------------------------------------------------------ + * + * and converts it into the below format and store it + * + * ------------------------------------------------------------------------ + * | scan id_1 | -> bss info_1 -> bss info_2 -> .... bss info_M1 + * | scan id_2 | -> bss info_1 -> bss info_2 -> .... bss info_M2 + * ...................... + * | scan id_N | -> bss info_1 -> bss info_2 -> .... bss info_Mn + * ------------------------------------------------------------------------ + * + * Return: 0 on success; error number otherwise + */ +static int wma_group_num_bss_to_scan_id(const u_int8_t *cmd_param_info, + struct extscan_cached_scan_results *cached_result) +{ + WMI_EXTSCAN_CACHED_RESULTS_EVENTID_param_tlvs *param_buf; + wmi_extscan_cached_results_event_fixed_param *event; + wmi_extscan_wlan_descriptor *src_hotlist; + wmi_extscan_rssi_info *src_rssi; + struct extscan_cached_scan_results *t_cached_result; + struct extscan_cached_scan_result *t_scan_id_grp; + int i, j; + tSirWifiScanResult *ap; + + param_buf = (WMI_EXTSCAN_CACHED_RESULTS_EVENTID_param_tlvs *) + cmd_param_info; + event = param_buf->fixed_param; + src_hotlist = param_buf->bssid_list; + src_rssi = param_buf->rssi_list; + t_cached_result = cached_result; + t_scan_id_grp = &t_cached_result->result[0]; + + if ((t_cached_result->num_scan_ids * + QDF_MIN(t_scan_id_grp->num_results, + param_buf->num_bssid_list)) > param_buf->num_bssid_list) { + WMA_LOGE("%s:num_scan_ids %d, num_results %d num_bssid_list %d", + __func__, + t_cached_result->num_scan_ids, + t_scan_id_grp->num_results, + param_buf->num_bssid_list); + return -EINVAL; + } + + WMA_LOGD("%s: num_scan_ids:%d", __func__, + t_cached_result->num_scan_ids); + for (i = 0; i < t_cached_result->num_scan_ids; i++) { + WMA_LOGD("%s: num_results:%d", __func__, + t_scan_id_grp->num_results); + t_scan_id_grp->ap = qdf_mem_malloc(t_scan_id_grp->num_results * + sizeof(*ap)); + if (!t_scan_id_grp->ap) + return -ENOMEM; + + ap = &t_scan_id_grp->ap[0]; + for (j = 0; j < QDF_MIN(t_scan_id_grp->num_results, + param_buf->num_bssid_list); j++) { + ap->channel = src_hotlist->channel; + ap->ts = WMA_MSEC_TO_USEC(src_rssi->tstamp); + ap->rtt = src_hotlist->rtt; + ap->rtt_sd = src_hotlist->rtt_sd; + ap->beaconPeriod = src_hotlist->beacon_interval; + ap->capability = src_hotlist->capabilities; + ap->ieLength = src_hotlist->ie_length; + + /* Firmware already applied noise floor adjustment and + * due to WMI interface "UINT32 rssi", host driver + * receives a positive value, hence convert to + * signed char to get the absolute rssi. + */ + ap->rssi = (signed char) src_rssi->rssi; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&src_hotlist->bssid, + ap->bssid.bytes); + + if (src_hotlist->ssid.ssid_len > + WLAN_SSID_MAX_LEN) { + WMA_LOGD("%s Invalid SSID len %d, truncating", + __func__, src_hotlist->ssid.ssid_len); + src_hotlist->ssid.ssid_len = + WLAN_SSID_MAX_LEN; + } + qdf_mem_copy(ap->ssid, src_hotlist->ssid.ssid, + src_hotlist->ssid.ssid_len); + ap->ssid[src_hotlist->ssid.ssid_len] = '\0'; + ap++; + src_rssi++; + src_hotlist++; + } + t_scan_id_grp++; + } + return 0; +} + +/** + * wma_extscan_cached_results_event_handler() - cached results event handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: length of @cmd_param_info + * + * This function handles cached results event and indicate + * cached results to upper layer. + * + * Return: 0 for success or error code. + */ +int wma_extscan_cached_results_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_EXTSCAN_CACHED_RESULTS_EVENTID_param_tlvs *param_buf; + wmi_extscan_cached_results_event_fixed_param *event; + struct extscan_cached_scan_results *dest_cachelist; + struct extscan_cached_scan_result *dest_result; + struct extscan_cached_scan_results empty_cachelist; + wmi_extscan_wlan_descriptor *src_hotlist; + wmi_extscan_rssi_info *src_rssi; + int i, moredata, scan_ids_cnt, buf_len, status; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + uint32_t total_len; + bool excess_data = false; + + if (!mac) { + WMA_LOGE("%s: Invalid mac", __func__); + return -EINVAL; + } + if (!mac->sme.ext_scan_ind_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + param_buf = (WMI_EXTSCAN_CACHED_RESULTS_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid cached results event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + src_hotlist = param_buf->bssid_list; + src_rssi = param_buf->rssi_list; + WMA_LOGI("Total_entries: %u first_entry_index: %u num_entries_in_page: %d", + event->total_entries, + event->first_entry_index, + event->num_entries_in_page); + + if (!src_hotlist || !src_rssi || !event->num_entries_in_page) { + WMA_LOGW("%s: Cached results empty, send 0 results", __func__); + goto noresults; + } + + if (event->num_entries_in_page > + (WMI_SVC_MSG_MAX_SIZE - sizeof(*event))/sizeof(*src_hotlist) || + event->num_entries_in_page > param_buf->num_bssid_list) { + WMA_LOGE("%s:excess num_entries_in_page %d in WMI event. num_bssid_list %d", + __func__, + event->num_entries_in_page, param_buf->num_bssid_list); + return -EINVAL; + } else { + total_len = sizeof(*event) + + (event->num_entries_in_page * sizeof(*src_hotlist)); + } + for (i = 0; i < event->num_entries_in_page; i++) { + if (src_hotlist[i].ie_length > + WMI_SVC_MSG_MAX_SIZE - total_len) { + excess_data = true; + break; + } else { + total_len += src_hotlist[i].ie_length; + WMA_LOGD("total len IE: %d", total_len); + } + + if (src_hotlist[i].number_rssi_samples > + (WMI_SVC_MSG_MAX_SIZE - total_len) / sizeof(*src_rssi)) { + excess_data = true; + break; + } else { + total_len += (src_hotlist[i].number_rssi_samples * + sizeof(*src_rssi)); + WMA_LOGD("total len RSSI samples: %d", total_len); + } + } + if (excess_data) { + WMA_LOGE("%s:excess data in WMI event", + __func__); + return -EINVAL; + } + + if (event->first_entry_index + + event->num_entries_in_page < event->total_entries) + moredata = 1; + else + moredata = 0; + + dest_cachelist = qdf_mem_malloc(sizeof(*dest_cachelist)); + if (!dest_cachelist) + return -ENOMEM; + + qdf_mem_zero(dest_cachelist, sizeof(*dest_cachelist)); + dest_cachelist->request_id = event->request_id; + dest_cachelist->more_data = moredata; + + scan_ids_cnt = wma_extscan_find_unique_scan_ids(cmd_param_info); + WMA_LOGD("%s: scan_ids_cnt %d", __func__, scan_ids_cnt); + dest_cachelist->num_scan_ids = scan_ids_cnt; + + buf_len = sizeof(*dest_result) * scan_ids_cnt; + dest_cachelist->result = qdf_mem_malloc(buf_len); + if (!dest_cachelist->result) { + qdf_mem_free(dest_cachelist); + return -ENOMEM; + } + + dest_result = dest_cachelist->result; + wma_fill_num_results_per_scan_id(cmd_param_info, dest_result); + + status = wma_group_num_bss_to_scan_id(cmd_param_info, dest_cachelist); + if (!status) + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_EXTSCAN_CACHED_RESULTS_IND, + dest_cachelist); + else + WMA_LOGD("wma_group_num_bss_to_scan_id failed, not calling callback"); + + dest_result = dest_cachelist->result; + for (i = 0; i < dest_cachelist->num_scan_ids; i++) { + if (dest_result->ap) + qdf_mem_free(dest_result->ap); + dest_result++; + } + qdf_mem_free(dest_cachelist->result); + qdf_mem_free(dest_cachelist); + return status; + +noresults: + empty_cachelist.request_id = event->request_id; + empty_cachelist.more_data = 0; + empty_cachelist.num_scan_ids = 0; + + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_EXTSCAN_CACHED_RESULTS_IND, + &empty_cachelist); + return 0; +} + +/** + * wma_extscan_change_results_event_handler() - change results event handler + * @handle: wma handle + * @cmd_param_info: event buffer + * @len: length + * + * This function handles change results event and indicate + * change results to upper layer. + * + * Return: 0 for success or error code. + */ +int wma_extscan_change_results_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID_param_tlvs *param_buf; + wmi_extscan_wlan_change_results_event_fixed_param *event; + tSirWifiSignificantChangeEvent *dest_chglist; + tSirWifiSignificantChange *dest_ap; + wmi_extscan_wlan_change_result_bssid *src_chglist; + + uint32_t numap; + int i, k; + uint8_t *src_rssi; + int count = 0; + int moredata; + uint32_t rssi_num = 0; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + uint32_t buf_len; + bool excess_data = false; + + if (!mac) { + WMA_LOGE("%s: Invalid mac", __func__); + return -EINVAL; + } + if (!mac->sme.ext_scan_ind_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + param_buf = (WMI_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid change monitor event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + src_chglist = param_buf->bssid_signal_descriptor_list; + src_rssi = param_buf->rssi_list; + numap = event->num_entries_in_page; + + if (!src_chglist || !numap) { + WMA_LOGE("%s: Results invalid", __func__); + return -EINVAL; + } + if (numap > param_buf->num_bssid_signal_descriptor_list) { + WMA_LOGE("%s: Invalid num of entries in page: %d", __func__, numap); + return -EINVAL; + } + for (i = 0; i < numap; i++) { + if (src_chglist->num_rssi_samples > (UINT_MAX - rssi_num)) { + WMA_LOGE("%s: Invalid num of rssi samples %d numap %d rssi_num %d", + __func__, src_chglist->num_rssi_samples, + numap, rssi_num); + return -EINVAL; + } + rssi_num += src_chglist->num_rssi_samples; + src_chglist++; + } + src_chglist = param_buf->bssid_signal_descriptor_list; + + if (event->first_entry_index + + event->num_entries_in_page < event->total_entries) { + moredata = 1; + } else { + moredata = 0; + } + + do { + if (event->num_entries_in_page > + (WMI_SVC_MSG_MAX_SIZE - sizeof(*event))/ + sizeof(*src_chglist)) { + excess_data = true; + break; + } else { + buf_len = + sizeof(*event) + (event->num_entries_in_page * + sizeof(*src_chglist)); + } + if (rssi_num > + (WMI_SVC_MSG_MAX_SIZE - buf_len)/sizeof(int32_t)) { + excess_data = true; + break; + } + } while (0); + + if (excess_data) { + WMA_LOGE("buffer len exceeds WMI payload,numap:%d, rssi_num:%d", + numap, rssi_num); + QDF_ASSERT(0); + return -EINVAL; + } + dest_chglist = qdf_mem_malloc(sizeof(*dest_chglist) + + sizeof(*dest_ap) * numap + + sizeof(int32_t) * rssi_num); + if (!dest_chglist) + return -ENOMEM; + + dest_ap = &dest_chglist->ap[0]; + for (i = 0; i < numap; i++) { + dest_ap->channel = src_chglist->channel; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&src_chglist->bssid, + dest_ap->bssid.bytes); + dest_ap->numOfRssi = src_chglist->num_rssi_samples; + if (dest_ap->numOfRssi) { + if ((dest_ap->numOfRssi + count) > + param_buf->num_rssi_list) { + WMA_LOGE("%s: Invalid num in rssi list: %d", + __func__, dest_ap->numOfRssi); + qdf_mem_free(dest_chglist); + return -EINVAL; + } + for (k = 0; k < dest_ap->numOfRssi; k++) { + dest_ap->rssi[k] = WMA_TGT_NOISE_FLOOR_DBM + + src_rssi[count++]; + } + } + dest_ap = (tSirWifiSignificantChange *)((char *)dest_ap + + dest_ap->numOfRssi * sizeof(int32_t) + + sizeof(*dest_ap)); + src_chglist++; + } + dest_chglist->requestId = event->request_id; + dest_chglist->moreData = moredata; + dest_chglist->numResults = numap; + + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_EXTSCAN_SIGNIFICANT_WIFI_CHANGE_RESULTS_IND, + dest_chglist); + WMA_LOGI("%s: sending change monitor results", __func__); + qdf_mem_free(dest_chglist); + return 0; +} + +/** + * wma_passpoint_match_event_handler() - passpoint match found event handler + * @handle: WMA handle + * @cmd_param_info: event data + * @len: event data length + * + * This is the passpoint match found event handler; it reads event data from + * @cmd_param_info and fill in the destination buffer and sends indication + * up layer. + * + * Return: 0 on success; error number otherwise + */ +int wma_passpoint_match_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_PASSPOINT_MATCH_EVENTID_param_tlvs *param_buf; + wmi_passpoint_event_hdr *event; + struct wifi_passpoint_match *dest_match; + tSirWifiScanResult *dest_ap; + uint8_t *buf_ptr; + uint32_t buf_len = 0; + bool excess_data = false; + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGE("%s: Invalid mac", __func__); + return -EINVAL; + } + if (!mac->sme.ext_scan_ind_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + + param_buf = (WMI_PASSPOINT_MATCH_EVENTID_param_tlvs *) cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid passpoint match event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + buf_ptr = (uint8_t *)param_buf->fixed_param; + + do { + if (event->ie_length > (WMI_SVC_MSG_MAX_SIZE)) { + excess_data = true; + break; + } else { + buf_len = event->ie_length; + } + + if (event->anqp_length > (WMI_SVC_MSG_MAX_SIZE)) { + excess_data = true; + break; + } else { + buf_len += event->anqp_length; + } + + } while (0); + + if (excess_data || buf_len > (WMI_SVC_MSG_MAX_SIZE - sizeof(*event)) || + buf_len > (WMI_SVC_MSG_MAX_SIZE - sizeof(*dest_match)) || + (event->ie_length + event->anqp_length) > param_buf->num_bufp) { + WMA_LOGE("IE Length: %u or ANQP Length: %u is huge, num_bufp: %u", + event->ie_length, event->anqp_length, + param_buf->num_bufp); + return -EINVAL; + } + + if (event->ssid.ssid_len > WLAN_SSID_MAX_LEN) { + WMA_LOGD("%s: Invalid ssid len %d, truncating", + __func__, event->ssid.ssid_len); + event->ssid.ssid_len = WLAN_SSID_MAX_LEN; + } + + dest_match = qdf_mem_malloc(sizeof(*dest_match) + buf_len); + + if (!dest_match) + return -EINVAL; + + dest_ap = &dest_match->ap; + dest_match->request_id = 0; + dest_match->id = event->id; + dest_match->anqp_len = event->anqp_length; + WMA_LOGI("%s: passpoint match: id: %u anqp length %u", __func__, + dest_match->id, dest_match->anqp_len); + + dest_ap->channel = event->channel_mhz; + dest_ap->ts = event->timestamp; + dest_ap->rtt = event->rtt; + dest_ap->rssi = event->rssi; + dest_ap->rtt_sd = event->rtt_sd; + dest_ap->beaconPeriod = event->beacon_period; + dest_ap->capability = event->capability; + dest_ap->ieLength = event->ie_length; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->bssid, dest_ap->bssid.bytes); + qdf_mem_copy(dest_ap->ssid, event->ssid.ssid, + event->ssid.ssid_len); + dest_ap->ssid[event->ssid.ssid_len] = '\0'; + qdf_mem_copy(dest_ap->ieData, buf_ptr + sizeof(*event) + + WMI_TLV_HDR_SIZE, dest_ap->ieLength); + qdf_mem_copy(dest_match->anqp, buf_ptr + sizeof(*event) + + WMI_TLV_HDR_SIZE + dest_ap->ieLength, + dest_match->anqp_len); + + mac->sme.ext_scan_ind_cb(mac->hdd_handle, + eSIR_PASSPOINT_NETWORK_FOUND_IND, + dest_match); + WMA_LOGI("%s: sending passpoint match event to hdd", __func__); + qdf_mem_free(dest_match); + return 0; +} + +QDF_STATUS wma_start_extscan(tp_wma_handle wma, + struct wifi_scan_cmd_req_params *params) +{ + QDF_STATUS status; + + if (!wma || !wma->wmi_handle) { + wma_err("WMA is closed, can not issue cmd"); + return QDF_STATUS_E_INVAL; + } + if (!wmi_service_enabled(wma->wmi_handle, wmi_service_extscan)) { + wma_err("extscan not enabled"); + return QDF_STATUS_E_FAILURE; + } + + if (!params) { + wma_err("NULL param"); + return QDF_STATUS_E_NOMEM; + } + + status = wmi_unified_start_extscan_cmd(wma->wmi_handle, params); + if (QDF_IS_STATUS_SUCCESS(status)) + wma->interfaces[params->vdev_id].extscan_in_progress = true; + + wma_debug("Exit, vdev %d, status %d", params->vdev_id, status); + + return status; +} + +QDF_STATUS wma_stop_extscan(tp_wma_handle wma, + struct extscan_stop_req_params *params) +{ + QDF_STATUS status; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, cannot issue cmd", __func__); + return QDF_STATUS_E_INVAL; + } + if (!wmi_service_enabled(wma->wmi_handle, wmi_service_extscan)) { + WMA_LOGE("%s: extscan not enabled", __func__); + return QDF_STATUS_E_FAILURE; + } + + status = wmi_unified_stop_extscan_cmd(wma->wmi_handle, params); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + wma->interfaces[params->vdev_id].extscan_in_progress = false; + WMA_LOGD("Extscan stop request sent successfully for vdev %d", + params->vdev_id); + + return status; +} + +QDF_STATUS wma_extscan_start_hotlist_monitor(tp_wma_handle wma, + struct extscan_bssid_hotlist_set_params *params) +{ + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue hotlist cmd", + __func__); + return QDF_STATUS_E_INVAL; + } + + if (!params) { + WMA_LOGE("%s: Invalid params", __func__); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_extscan_start_hotlist_monitor_cmd(wma->wmi_handle, + params); +} + +QDF_STATUS wma_extscan_stop_hotlist_monitor(tp_wma_handle wma, + struct extscan_bssid_hotlist_reset_params *params) +{ + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue cmd", __func__); + return QDF_STATUS_E_INVAL; + } + + if (!params) { + WMA_LOGE("%s: Invalid params", __func__); + return QDF_STATUS_E_INVAL; + } + if (!wmi_service_enabled(wma->wmi_handle, + wmi_service_extscan)) { + WMA_LOGE("%s: extscan not enabled", __func__); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_extscan_stop_hotlist_monitor_cmd(wma->wmi_handle, + params); +} + +QDF_STATUS +wma_extscan_start_change_monitor(tp_wma_handle wma, + struct extscan_set_sig_changereq_params *params) +{ + QDF_STATUS status; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed,can not issue cmd", + __func__); + return QDF_STATUS_E_INVAL; + } + + if (!params) { + WMA_LOGE("%s: NULL params", __func__); + return QDF_STATUS_E_NOMEM; + } + + status = wmi_unified_extscan_start_change_monitor_cmd(wma->wmi_handle, + params); + return status; +} + +QDF_STATUS wma_extscan_stop_change_monitor(tp_wma_handle wma, + struct extscan_capabilities_reset_params *params) +{ + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue cmd", __func__); + return QDF_STATUS_E_INVAL; + } + if (!wmi_service_enabled(wma->wmi_handle, + wmi_service_extscan)) { + WMA_LOGE("%s: ext scan not enabled", __func__); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_extscan_stop_change_monitor_cmd(wma->wmi_handle, + params); +} + +QDF_STATUS +wma_extscan_get_cached_results(tp_wma_handle wma, + struct extscan_cached_result_params *params) +{ + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, cannot issue cmd", __func__); + return QDF_STATUS_E_INVAL; + } + if (!wmi_service_enabled(wma->wmi_handle, wmi_service_extscan)) { + WMA_LOGE("%s: extscan not enabled", __func__); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_extscan_get_cached_results_cmd(wma->wmi_handle, + params); +} + +QDF_STATUS +wma_extscan_get_capabilities(tp_wma_handle wma, + struct extscan_capabilities_params *params) +{ + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue cmd", __func__); + return QDF_STATUS_E_INVAL; + } + if (!wmi_service_enabled(wma->wmi_handle, wmi_service_extscan)) { + WMA_LOGE("%s: extscan not enabled", __func__); + return QDF_STATUS_E_FAILURE; + } + + return wmi_unified_extscan_get_capabilities_cmd(wma->wmi_handle, + params); +} + +QDF_STATUS wma_set_epno_network_list(tp_wma_handle wma, + struct wifi_enhanced_pno_params *req) +{ + QDF_STATUS status; + + wma_debug("Enter"); + + if (!wma || !wma->wmi_handle) { + wma_err("WMA is closed, can not issue cmd"); + return QDF_STATUS_E_FAILURE; + } + + if (!wmi_service_enabled(wma->wmi_handle, wmi_service_extscan)) { + wma_err("extscan not enabled"); + return QDF_STATUS_E_NOSUPPORT; + } + + status = wmi_unified_set_epno_network_list_cmd(wma->wmi_handle, req); + wma_debug("Exit, vdev %d, status %d", req->vdev_id, status); + + return status; +} + +QDF_STATUS +wma_set_passpoint_network_list(tp_wma_handle wma, + struct wifi_passpoint_req_param *params) +{ + QDF_STATUS status; + + wma_debug("Enter"); + + if (!wma || !wma->wmi_handle) { + wma_err("WMA is closed, can not issue cmd"); + return QDF_STATUS_E_FAILURE; + } + if (!wmi_service_enabled(wma->wmi_handle, wmi_service_extscan)) { + wma_err("extscan not enabled"); + return QDF_STATUS_E_NOSUPPORT; + } + + status = wmi_unified_set_passpoint_network_list_cmd(wma->wmi_handle, + params); + wma_debug("Exit, vdev %d, status %d", params->vdev_id, status); + + return status; +} + +QDF_STATUS +wma_reset_passpoint_network_list(tp_wma_handle wma, + struct wifi_passpoint_req_param *params) +{ + QDF_STATUS status; + + wma_debug("Enter"); + + if (!wma || !wma->wmi_handle) { + wma_err("WMA is closed, can not issue cmd"); + return QDF_STATUS_E_FAILURE; + } + if (!wmi_service_enabled(wma->wmi_handle, wmi_service_extscan)) { + wma_err("extscan not enabled"); + return QDF_STATUS_E_NOSUPPORT; + } + + status = wmi_unified_reset_passpoint_network_list_cmd(wma->wmi_handle, + params); + wma_debug("Exit, vdev %d, status %d", params->vdev_id, status); + + return status; +} + +#endif + +QDF_STATUS wma_scan_probe_setoui(tp_wma_handle wma, + struct scan_mac_oui *set_oui) +{ + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue cmd", __func__); + return QDF_STATUS_E_INVAL; + } + + if (!wma_is_vdev_valid(set_oui->vdev_id)) { + WMA_LOGE("%s: vdev_id: %d is not active", __func__, + set_oui->vdev_id); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_scan_probe_setoui_cmd(wma->wmi_handle, set_oui); +} + +/** + * wma_roam_better_ap_handler() - better ap event handler + * @wma: wma handle + * @vdev_id: vdev id + * + * Handler for WMI_ROAM_REASON_BETTER_AP event from roam firmware in Rome. + * This event means roam algorithm in Rome has found a better matching + * candidate AP. The indication is sent to SME. + * + * Return: none + */ +void wma_roam_better_ap_handler(tp_wma_handle wma, uint32_t vdev_id) +{ + struct scheduler_msg cds_msg = {0}; + tSirSmeCandidateFoundInd *candidate_ind; + QDF_STATUS status; + + candidate_ind = qdf_mem_malloc(sizeof(tSirSmeCandidateFoundInd)); + if (!candidate_ind) + return; + + wma->interfaces[vdev_id].roaming_in_progress = true; + candidate_ind->messageType = eWNI_SME_CANDIDATE_FOUND_IND; + candidate_ind->sessionId = vdev_id; + candidate_ind->length = sizeof(tSirSmeCandidateFoundInd); + + cds_msg.type = eWNI_SME_CANDIDATE_FOUND_IND; + cds_msg.bodyptr = candidate_ind; + cds_msg.callback = sme_mc_process_handler; + wma_debug("Posting candidate ind to SME, vdev %d", vdev_id); + + status = scheduler_post_message(QDF_MODULE_ID_WMA, QDF_MODULE_ID_SME, + QDF_MODULE_ID_SCAN, &cds_msg); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(candidate_ind); +} + +/** + * wma_handle_btm_disassoc_imminent_msg() - Send del sta msg to lim on receiving + * BTM request from AP with disassoc imminent reason + * @wma_handle: wma handle + * @vdev_id: vdev id + * + * Return: None + */ +static void wma_handle_btm_disassoc_imminent_msg(tp_wma_handle wma_handle, + uint32_t vdev_id) +{ + tpDeleteStaContext del_sta_ctx; + + del_sta_ctx = qdf_mem_malloc(sizeof(tDeleteStaContext)); + if (!del_sta_ctx) + return; + + del_sta_ctx->vdev_id = vdev_id; + del_sta_ctx->reasonCode = HAL_DEL_STA_REASON_CODE_BTM_DISASSOC_IMMINENT; + wma_send_msg(wma_handle, SIR_LIM_DELETE_STA_CONTEXT_IND, + (void *)del_sta_ctx, 0); +} + +/** + * wma_handle_hw_mode_in_roam_fail() - Fill hw mode info if present in policy + * manager. + * @wma: wma handle + * @param: roam event params + * + * Return: None + */ +static int wma_handle_hw_mode_transition(tp_wma_handle wma, + WMI_ROAM_EVENTID_param_tlvs *param) +{ + struct sir_hw_mode_trans_ind *hw_mode_trans_ind; + struct scheduler_msg sme_msg = {0}; + QDF_STATUS status; + + if (param->hw_mode_transition_fixed_param) { + hw_mode_trans_ind = qdf_mem_malloc(sizeof(*hw_mode_trans_ind)); + if (!hw_mode_trans_ind) + return -ENOMEM; + wma_process_pdev_hw_mode_trans_ind(wma, + param->hw_mode_transition_fixed_param, + param->wmi_pdev_set_hw_mode_response_vdev_mac_mapping, + hw_mode_trans_ind); + + WMA_LOGI(FL("Update HW mode")); + sme_msg.type = eWNI_SME_HW_MODE_TRANS_IND; + sme_msg.bodyptr = hw_mode_trans_ind; + + status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(hw_mode_trans_ind); + } else { + WMA_LOGD(FL("hw_mode transition fixed param is NULL")); + } + + return 0; +} + +/** + * wma_invalid_roam_reason_handler() - Handle Invalid roam notification + * @wma: wma handle + * @vdev_id: vdev id + * @op_code: Operation to be done by the callback + * + * This function calls pe and csr callbacks with proper op_code + * + * Return: None + */ +static void wma_invalid_roam_reason_handler(tp_wma_handle wma_handle, + uint32_t vdev_id, + uint32_t notif) +{ + struct roam_offload_synch_ind *roam_synch_data; + enum sir_roam_op_code op_code; + + if (notif == WMI_ROAM_NOTIF_ROAM_START) { + wma_handle->interfaces[vdev_id].roaming_in_progress = true; + op_code = SIR_ROAMING_START; + } else if (notif == WMI_ROAM_NOTIF_ROAM_ABORT) { + wma_handle->interfaces[vdev_id].roaming_in_progress = false; + op_code = SIR_ROAMING_ABORT; + lim_sae_auth_cleanup_retry(wma_handle->mac_context, vdev_id); + } else { + WMA_LOGD(FL("Invalid notif %d"), notif); + return; + } + + roam_synch_data = qdf_mem_malloc(sizeof(*roam_synch_data)); + if (!roam_synch_data) + return; + + roam_synch_data->roamed_vdev_id = vdev_id; + wma_handle->pe_roam_synch_cb(wma_handle->mac_context, roam_synch_data, + NULL, op_code); + wma_handle->csr_roam_synch_cb(wma_handle->mac_context, roam_synch_data, + NULL, op_code); + + qdf_mem_free(roam_synch_data); +} + +void wma_handle_roam_sync_timeout(tp_wma_handle wma_handle, + struct roam_sync_timeout_timer_info *info) +{ + wma_invalid_roam_reason_handler(wma_handle, info->vdev_id, + WMI_ROAM_NOTIF_ROAM_ABORT); +} + +/** + * wma_roam_event_callback() - roam event callback + * @handle: wma handle + * @event_buf: event buffer + * @len: buffer length + * + * Handler for all events from roam engine in firmware + * + * Return: 0 for success or error code + */ +int wma_roam_event_callback(WMA_HANDLE handle, uint8_t *event_buf, + uint32_t len) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + WMI_ROAM_EVENTID_param_tlvs *param_buf; + wmi_roam_event_fixed_param *wmi_event; + struct roam_offload_synch_ind *roam_synch_data; + uint8_t *frame = NULL; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + struct qdf_mac_addr bssid; +#endif + + param_buf = (WMI_ROAM_EVENTID_param_tlvs *) event_buf; + if (!param_buf) { + WMA_LOGE("Invalid roam event buffer"); + return -EINVAL; + } + + wmi_event = param_buf->fixed_param; + WMA_LOGD("%s: Reason %x, Notif %x for vdevid %x, rssi %d", + __func__, wmi_event->reason, wmi_event->notif, + wmi_event->vdev_id, wmi_event->rssi); + + if (wmi_event->vdev_id >= wma_handle->max_bssid) { + WMA_LOGE("Invalid vdev id from firmware"); + return -EINVAL; + } + wlan_roam_debug_log(wmi_event->vdev_id, DEBUG_ROAM_EVENT, + DEBUG_INVALID_PEER_ID, NULL, NULL, + wmi_event->reason, + (wmi_event->reason == WMI_ROAM_REASON_INVALID) ? + wmi_event->notif : wmi_event->rssi); + + DPTRACE(qdf_dp_trace_record_event(QDF_DP_TRACE_EVENT_RECORD, + wmi_event->vdev_id, QDF_TRACE_DEFAULT_PDEV_ID, + QDF_PROTO_TYPE_EVENT, QDF_ROAM_EVENTID)); + + switch (wmi_event->reason) { + case WMI_ROAM_REASON_BTM: + /* + * This event is received from firmware if firmware is unable to + * find candidate AP after roam scan and BTM request from AP + * has disassoc imminent bit set. + */ + WMA_LOGD("Kickout due to btm request"); + wma_sta_kickout_event(HOST_STA_KICKOUT_REASON_BTM, + wmi_event->vdev_id, NULL); + wma_handle_btm_disassoc_imminent_msg(wma_handle, + wmi_event->vdev_id); + break; + case WMI_ROAM_REASON_BMISS: + /* + * WMI_ROAM_REASON_BMISS can get called in soft IRQ context, so + * avoid using CSR/PE structure directly + */ + WMA_LOGD("Beacon Miss for vdevid %x", wmi_event->vdev_id); + wma_beacon_miss_handler(wma_handle, wmi_event->vdev_id, + wmi_event->rssi); + wma_sta_kickout_event(HOST_STA_KICKOUT_REASON_BMISS, + wmi_event->vdev_id, NULL); + break; + case WMI_ROAM_REASON_BETTER_AP: + /* + * WMI_ROAM_REASON_BETTER_AP can get called in soft IRQ context, + * so avoid using CSR/PE structure directly. + */ + WMA_LOGD("%s:Better AP found for vdevid %x, rssi %d", __func__, + wmi_event->vdev_id, wmi_event->rssi); + wma_handle->suitable_ap_hb_failure = false; + wma_roam_better_ap_handler(wma_handle, wmi_event->vdev_id); + break; + case WMI_ROAM_REASON_SUITABLE_AP: + /* + * WMI_ROAM_REASON_SUITABLE_AP can get called in soft IRQ + * context, so avoid using CSR/PE structure directly. + */ + wma_handle->suitable_ap_hb_failure = true; + wma_handle->suitable_ap_hb_failure_rssi = wmi_event->rssi; + WMA_LOGD("%s:Bmiss scan AP found for vdevid %x, rssi %d", + __func__, wmi_event->vdev_id, wmi_event->rssi); + wma_roam_better_ap_handler(wma_handle, wmi_event->vdev_id); + break; +#ifdef WLAN_FEATURE_ROAM_OFFLOAD + case WMI_ROAM_REASON_HO_FAILED: + /* + * WMI_ROAM_REASON_HO_FAILED can get called in soft IRQ context, + * so avoid using CSR/PE structure directly. + */ + WMA_LOGE("LFR3:Hand-Off Failed for vdevid %x", + wmi_event->vdev_id); + bssid.bytes[0] = wmi_event->notif_params >> 0 & 0xFF; + bssid.bytes[1] = wmi_event->notif_params >> 8 & 0xFF; + bssid.bytes[2] = wmi_event->notif_params >> 16 & 0xFF; + bssid.bytes[3] = wmi_event->notif_params >> 24 & 0xFF; + bssid.bytes[4] = wmi_event->notif_params1 >> 0 & 0xFF; + bssid.bytes[5] = wmi_event->notif_params1 >> 8 & 0xFF; + WMA_LOGE("mac addr to avoid "QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(bssid.bytes)); + wma_handle_hw_mode_transition(wma_handle, param_buf); + wma_roam_ho_fail_handler(wma_handle, wmi_event->vdev_id, bssid); + wma_handle->interfaces[wmi_event->vdev_id].roaming_in_progress = + false; + lim_sae_auth_cleanup_retry(wma_handle->mac_context, + wmi_event->vdev_id); + break; +#endif + case WMI_ROAM_REASON_INVALID: + wma_invalid_roam_reason_handler(wma_handle, wmi_event->vdev_id, + wmi_event->notif); + break; + case WMI_ROAM_REASON_RSO_STATUS: + wma_rso_cmd_status_event_handler(wmi_event); + break; + case WMI_ROAM_REASON_INVOKE_ROAM_FAIL: + wma_handle_hw_mode_transition(wma_handle, param_buf); + roam_synch_data = qdf_mem_malloc(sizeof(*roam_synch_data)); + if (!roam_synch_data) + return -ENOMEM; + + lim_sae_auth_cleanup_retry(wma_handle->mac_context, + wmi_event->vdev_id); + roam_synch_data->roamed_vdev_id = wmi_event->vdev_id; + wma_handle->csr_roam_synch_cb(wma_handle->mac_context, + roam_synch_data, NULL, + SIR_ROAMING_INVOKE_FAIL); + qdf_mem_free(roam_synch_data); + break; + case WMI_ROAM_REASON_DEAUTH: + WMA_LOGD("%s: Received disconnect roam event reason:%d", + __func__, wmi_event->notif_params); + if (wmi_event->notif_params1) + frame = param_buf->deauth_disassoc_frame; + wma_handle->pe_disconnect_cb(wma_handle->mac_context, + wmi_event->vdev_id, + frame, wmi_event->notif_params1, + wmi_event->notif_params); + roam_synch_data = qdf_mem_malloc(sizeof(*roam_synch_data)); + if (!roam_synch_data) + return -ENOMEM; + + roam_synch_data->roamed_vdev_id = wmi_event->vdev_id; + wma_handle->csr_roam_synch_cb( + wma_handle->mac_context, + roam_synch_data, NULL, SIR_ROAMING_DEAUTH); + qdf_mem_free(roam_synch_data); + break; + default: + WMA_LOGD("%s:Unhandled Roam Event %x for vdevid %x", __func__, + wmi_event->reason, wmi_event->vdev_id); + break; + } + return 0; +} + +#ifdef FEATURE_LFR_SUBNET_DETECTION +QDF_STATUS wma_set_gateway_params(tp_wma_handle wma, + struct gateway_update_req_param *req) +{ + if (!wma) { + WMA_LOGE("%s: wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_set_gateway_params_cmd(wma->wmi_handle, req); +} +#endif /* FEATURE_LFR_SUBNET_DETECTION */ + +/** + * wma_ht40_stop_obss_scan() - ht40 obss stop scan + * @wma: WMA handel + * @vdev_id: vdev identifier + * + * Return: Return QDF_STATUS, otherwise appropriate failure code + */ +QDF_STATUS wma_ht40_stop_obss_scan(tp_wma_handle wma, int32_t vdev_id) +{ + QDF_STATUS status; + wmi_buf_t buf; + wmi_obss_scan_disable_cmd_fixed_param *cmd; + int len = sizeof(*cmd); + + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + WMA_LOGD("cmd %x vdev_id %d", WMI_OBSS_SCAN_DISABLE_CMDID, vdev_id); + + cmd = (wmi_obss_scan_disable_cmd_fixed_param *) wmi_buf_data(buf); + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_obss_scan_disable_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_obss_scan_disable_cmd_fixed_param)); + + cmd->vdev_id = vdev_id; + status = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_OBSS_SCAN_DISABLE_CMDID); + if (QDF_IS_STATUS_ERROR(status)) + wmi_buf_free(buf); + + return status; +} + +/** + * wma_send_ht40_obss_scanind() - ht40 obss start scan indication + * @wma: WMA handel + * @req: start scan request + * + * Return: Return QDF_STATUS, otherwise appropriate failure code + */ +QDF_STATUS wma_send_ht40_obss_scanind(tp_wma_handle wma, + struct obss_ht40_scanind *req) +{ + QDF_STATUS status; + wmi_buf_t buf; + wmi_obss_scan_enable_cmd_fixed_param *cmd; + int len = 0; + uint8_t *buf_ptr, i; + uint8_t *channel_list; + uint32_t *chan_freq_list; + + len += sizeof(wmi_obss_scan_enable_cmd_fixed_param); + + len += WMI_TLV_HDR_SIZE; + len += qdf_roundup(sizeof(uint8_t) * req->channel_count, + sizeof(uint32_t)); + + len += WMI_TLV_HDR_SIZE; + len += qdf_roundup(sizeof(uint8_t) * 1, sizeof(uint32_t)); + + /* length calculation for chan_freqs */ + len += WMI_TLV_HDR_SIZE; + len += sizeof(uint32_t) * req->channel_count; + + WMA_LOGE("cmdlen %d vdev_id %d channel count %d iefield_len %d", + len, req->bss_id, req->channel_count, req->iefield_len); + + WMA_LOGE("scantype %d active_time %d passive %d Obss interval %d", + req->scan_type, req->obss_active_dwelltime, + req->obss_passive_dwelltime, + req->obss_width_trigger_interval); + + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return QDF_STATUS_E_NOMEM; + + cmd = (wmi_obss_scan_enable_cmd_fixed_param *) wmi_buf_data(buf); + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_obss_scan_enable_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN(wmi_obss_scan_enable_cmd_fixed_param)); + + buf_ptr = (uint8_t *) cmd; + + cmd->vdev_id = req->bss_id; + cmd->scan_type = req->scan_type; + cmd->obss_scan_active_dwell = + req->obss_active_dwelltime; + cmd->obss_scan_passive_dwell = + req->obss_passive_dwelltime; + cmd->bss_channel_width_trigger_scan_interval = + req->obss_width_trigger_interval; + cmd->bss_width_channel_transition_delay_factor = + req->bsswidth_ch_trans_delay; + cmd->obss_scan_active_total_per_channel = + req->obss_active_total_per_channel; + cmd->obss_scan_passive_total_per_channel = + req->obss_passive_total_per_channel; + cmd->obss_scan_activity_threshold = + req->obss_activity_threshold; + + cmd->channel_len = req->channel_count; + cmd->forty_mhz_intolerant = req->fortymhz_intolerent; + cmd->current_operating_class = req->current_operatingclass; + cmd->ie_len = req->iefield_len; + + buf_ptr += sizeof(wmi_obss_scan_enable_cmd_fixed_param); + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, + qdf_roundup(req->channel_count, sizeof(uint32_t))); + + buf_ptr += WMI_TLV_HDR_SIZE; + channel_list = (uint8_t *) buf_ptr; + + for (i = 0; i < req->channel_count; i++) { + channel_list[i] = + wlan_reg_freq_to_chan(wma->pdev, req->chan_freq_list[i]); + WMA_LOGD("Ch[%d]: %d ", i, channel_list[i]); + } + + buf_ptr += qdf_roundup(sizeof(uint8_t) * req->channel_count, + sizeof(uint32_t)); + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, + qdf_roundup(1, sizeof(uint32_t))); + buf_ptr += WMI_TLV_HDR_SIZE; + + buf_ptr += qdf_roundup(sizeof(uint8_t) * 1, sizeof(uint32_t)); + + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_UINT32, + sizeof(uint32_t) * req->channel_count); + buf_ptr += WMI_TLV_HDR_SIZE; + + chan_freq_list = (uint32_t *)buf_ptr; + for (i = 0; i < req->channel_count; i++) { + chan_freq_list[i] = req->chan_freq_list[i]; + WMA_LOGD("freq[%u]: %u ", i, chan_freq_list[i]); + } + + buf_ptr += sizeof(uint32_t) * req->channel_count; + + status = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_OBSS_SCAN_ENABLE_CMDID); + if (QDF_IS_STATUS_ERROR(status)) + wmi_buf_free(buf); + + return status; +} + +static enum blm_reject_ap_reason wma_get_reject_reason(uint32_t reason) +{ + switch(reason) { + case WMI_BL_REASON_NUD_FAILURE: + return REASON_NUD_FAILURE; + case WMI_BL_REASON_STA_KICKOUT: + return REASON_STA_KICKOUT; + case WMI_BL_REASON_ROAM_HO_FAILURE: + return REASON_ROAM_HO_FAILURE; + case WMI_BL_REASON_ASSOC_REJECT_POOR_RSSI: + return REASON_ASSOC_REJECT_POOR_RSSI; + case WMI_BL_REASON_ASSOC_REJECT_OCE: + return REASON_ASSOC_REJECT_OCE; + case WMI_BL_REASON_USERSPACE_BL: + return REASON_USERSPACE_BL; + case WMI_BL_REASON_USERSPACE_AVOID_LIST: + return REASON_USERSPACE_AVOID_LIST; + case WMI_BL_REASON_BTM_DIASSOC_IMMINENT: + return REASON_BTM_DISASSOC_IMMINENT; + case WMI_BL_REASON_BTM_BSS_TERMINATION: + return REASON_BTM_BSS_TERMINATION; + case WMI_BL_REASON_BTM_MBO_RETRY: + return REASON_BTM_MBO_RETRY; + case WMI_BL_REASON_REASSOC_RSSI_REJECT: + return REASON_REASSOC_RSSI_REJECT; + case WMI_BL_REASON_REASSOC_NO_MORE_STAS: + return REASON_REASSOC_NO_MORE_STAS; + default: + return REASON_UNKNOWN; + } +} + +int wma_handle_btm_blacklist_event(void *handle, uint8_t *cmd_param_info, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_ROAM_BLACKLIST_EVENTID_param_tlvs *param_buf; + wmi_roam_blacklist_event_fixed_param *resp_event; + wmi_roam_blacklist_with_timeout_tlv_param *src_list; + struct roam_blacklist_event *dst_list; + struct roam_blacklist_timeout *roam_blacklist; + uint32_t num_entries, i; + + param_buf = (WMI_ROAM_BLACKLIST_EVENTID_param_tlvs *)cmd_param_info; + if (!param_buf) { + WMA_LOGE("Invalid event buffer"); + return -EINVAL; + } + + resp_event = param_buf->fixed_param; + if (!resp_event) { + WMA_LOGE("%s: received null event data from target", __func__); + return -EINVAL; + } + + if (resp_event->vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: received invalid vdev_id %d", + __func__, resp_event->vdev_id); + return -EINVAL; + } + + num_entries = param_buf->num_blacklist_with_timeout; + if (num_entries == 0) { + /* no aps to blacklist just return*/ + WMA_LOGE("%s: No APs in blacklist received", __func__); + return 0; + } + + if (num_entries > MAX_RSSI_AVOID_BSSID_LIST) { + WMA_LOGE("%s: num blacklist entries:%d exceeds maximum value", + __func__, num_entries); + return -EINVAL; + } + + src_list = param_buf->blacklist_with_timeout; + if (len < (sizeof(*resp_event) + (num_entries * sizeof(*src_list)))) { + WMA_LOGE("%s: Invalid length:%d", __func__, len); + return -EINVAL; + } + + dst_list = qdf_mem_malloc(sizeof(struct roam_blacklist_event) + + (sizeof(struct roam_blacklist_timeout) * + num_entries)); + if (!dst_list) + return -ENOMEM; + + roam_blacklist = &dst_list->roam_blacklist[0]; + for (i = 0; i < num_entries; i++) { + WMI_MAC_ADDR_TO_CHAR_ARRAY(&src_list->bssid, + roam_blacklist->bssid.bytes); + roam_blacklist->timeout = src_list->timeout; + roam_blacklist->received_time = src_list->timestamp; + roam_blacklist->original_timeout = src_list->original_timeout; + roam_blacklist->reject_reason = + wma_get_reject_reason(src_list->reason); + roam_blacklist->source = src_list->source; + roam_blacklist++; + src_list++; + } + + dst_list->num_entries = num_entries; + wma_send_msg(wma, WMA_ROAM_BLACKLIST_MSG, (void *)dst_list, 0); + return 0; +} + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS wma_set_roam_triggers(tp_wma_handle wma, + struct roam_triggers *triggers) +{ + if (!wma_is_vdev_valid(triggers->vdev_id)) + return QDF_STATUS_E_INVAL; + + return wmi_unified_set_roam_triggers(wma->wmi_handle, triggers); +} + +#ifdef WLAN_FEATURE_FIPS +void wma_register_pmkid_req_event_handler(tp_wma_handle wma_handle) +{ + if (!wma_handle) { + wma_err("%s: pmkid req wma_handle is NULL", __func__); + return; + } + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_roam_pmkid_request_event_id, + wma_roam_pmkid_request_event_handler, + WMA_RX_SERIALIZER_CTX); +} + +int wma_roam_pmkid_request_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + WMI_ROAM_PMKID_REQUEST_EVENTID_param_tlvs *param_buf; + wmi_roam_pmkid_request_event_fixed_param *roam_pmkid_req_ev; + wmi_roam_pmkid_request_tlv_param *src_list; + tp_wma_handle wma = (tp_wma_handle)handle; + struct roam_pmkid_req_event *dst_list; + struct qdf_mac_addr *roam_bsslist; + uint32_t num_entries, i; + QDF_STATUS status; + + if (!event) { + wma_err("%s: received null event from target", __func__); + return -EINVAL; + } + + param_buf = (WMI_ROAM_PMKID_REQUEST_EVENTID_param_tlvs *)event; + if (!param_buf) { + wma_err("%s: received null buf from target", __func__); + return -EINVAL; + } + + roam_pmkid_req_ev = param_buf->fixed_param; + if (!roam_pmkid_req_ev) { + wma_err("%s: received null event data from target", __func__); + return -EINVAL; + } + + if (roam_pmkid_req_ev->vdev_id >= wma->max_bssid) { + wma_err("%s: received invalid vdev_id %d", + __func__, roam_pmkid_req_ev->vdev_id); + return -EINVAL; + } + + num_entries = param_buf->num_pmkid_request; + if (num_entries > MAX_RSSI_AVOID_BSSID_LIST) { + wma_err("%s: num bssid entries:%d exceeds maximum value", + __func__, num_entries); + return -EINVAL; + } + + src_list = param_buf->pmkid_request; + if (len < (sizeof(*roam_pmkid_req_ev) + + (num_entries * sizeof(*src_list)))) { + wma_err("%s: Invalid length:%d", __func__, len); + return -EINVAL; + } + + dst_list = qdf_mem_malloc(sizeof(struct roam_pmkid_req_event) + + (sizeof(struct qdf_mac_addr) * num_entries)); + if (!dst_list) + return -ENOMEM; + + for (i = 0; i < num_entries; i++) { + roam_bsslist = &dst_list->ap_bssid[i]; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&src_list->bssid, + roam_bsslist->bytes); + if (qdf_is_macaddr_zero(roam_bsslist) || + qdf_is_macaddr_broadcast(roam_bsslist) || + qdf_is_macaddr_group(roam_bsslist)) { + wma_err("%s: Invalid bssid", __func__); + qdf_mem_free(dst_list); + return -EINVAL; + } + WMA_LOGD("%s:Received pmkid fallback for bssid: "QDF_MAC_ADDR_FMT" vdev_id:%d", + __func__, QDF_MAC_ADDR_REF(roam_bsslist->bytes), + roam_pmkid_req_ev->vdev_id); + src_list++; + } + dst_list->num_entries = num_entries; + + status = wma->csr_roam_pmkid_req_cb(roam_pmkid_req_ev->vdev_id, + dst_list); + if (QDF_IS_STATUS_ERROR(status)) { + wma_err("%s: Pmkid request failed", __func__); + qdf_mem_free(dst_list); + return -EINVAL; + } + + qdf_mem_free(dst_list); + return 0; +} +#endif /* WLAN_FEATURE_FIPS */ +#else +inline QDF_STATUS +wma_set_roam_triggers(tp_wma_handle wma, struct roam_triggers *triggers) +{ + return QDF_STATUS_E_NOSUPPORT; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_twt.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_twt.c new file mode 100644 index 0000000000000000000000000000000000000000..84e88a6951f7fee78f5716de5abbb2d9356412db --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_twt.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_twt.c + * + * WLAN Host Device Driver TWT - Target Wake Time Implementation + */ + +#include "wma_twt.h" +#include "wmi_unified_twt_api.h" +#include "wma_internal.h" +#include "wmi_unified_priv.h" + +void wma_send_twt_enable_cmd(uint32_t pdev_id, + uint32_t congestion_timeout, bool bcast_val) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + struct wmi_twt_enable_param twt_enable_params = {0}; + int32_t ret; + + if (!wma) { + WMA_LOGE("Invalid WMA context, enable TWT failed"); + return; + } + twt_enable_params.pdev_id = pdev_id; + twt_enable_params.sta_cong_timer_ms = congestion_timeout; + TWT_EN_DIS_FLAGS_SET_BTWT(twt_enable_params.flags, bcast_val); + ret = wmi_unified_twt_enable_cmd(wma->wmi_handle, &twt_enable_params); + + if (ret) + WMA_LOGE("Failed to enable TWT"); +} + +int wma_twt_en_complete_event_handler(void *handle, + uint8_t *event, uint32_t len) +{ + struct wmi_twt_enable_complete_event_param param; + tp_wma_handle wma_handle = (tp_wma_handle) handle; + wmi_unified_t wmi_handle; + struct mac_context *mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + int status = -EINVAL; + + if (!wma_handle) { + WMA_LOGE("Invalid wma handle for TWT complete"); + return status; + } + wmi_handle = (wmi_unified_t)wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE("Invalid wmi handle for TWT complete"); + return status; + } + if (!mac) { + WMA_LOGE("Invalid MAC context"); + return status; + } + if (wmi_handle->ops->extract_twt_enable_comp_event) + status = wmi_handle->ops->extract_twt_enable_comp_event( + wmi_handle, + event, + ¶m); + WMA_LOGD("TWT: Received TWT enable comp event, status:%d", status); + + if (mac->sme.twt_enable_cb) + mac->sme.twt_enable_cb(mac->hdd_handle, ¶m); + + return status; +} + +void wma_send_twt_disable_cmd(uint32_t pdev_id) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + struct wmi_twt_disable_param twt_disable_params = {0}; + int32_t ret; + + if (!wma) { + WMA_LOGE("Invalid WMA context, Disable TWT failed"); + return; + } + twt_disable_params.pdev_id = pdev_id; + ret = wmi_unified_twt_disable_cmd(wma->wmi_handle, &twt_disable_params); + + if (ret) + WMA_LOGE("Failed to disable TWT"); +} + +int wma_twt_disable_comp_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + struct mac_context *mac; + + mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + WMA_LOGE("Invalid MAC context"); + return -EINVAL; + } + + WMA_LOGD("TWT: Rcvd TWT disable comp event"); + + if (mac->sme.twt_disable_cb) + mac->sme.twt_disable_cb(mac->hdd_handle); + + return 0; +} + +void wma_set_twt_peer_caps(tpAddStaParams params, struct peer_assoc_params *cmd) +{ + if (params->twt_requestor) + cmd->twt_requester = 1; + if (params->twt_responder) + cmd->twt_responder = 1; +} + +QDF_STATUS wma_twt_process_add_dialog( + struct wmi_twt_add_dialog_param *params) +{ + t_wma_handle *wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + wmi_unified_t wmi_handle; + + if (!wma_handle) { + WMA_LOGE("Invalid WMA context, twt add dialog failed"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = (wmi_unified_t)wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE("Invalid wmi handle, twt add dialog failed"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_twt_add_dialog_cmd(wmi_handle, params); +} + +QDF_STATUS wma_twt_process_del_dialog( + struct wmi_twt_del_dialog_param *params) +{ + t_wma_handle *wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + wmi_unified_t wmi_handle; + + if (!wma_handle) { + WMA_LOGE("Invalid WMA context, twt del dialog failed"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = (wmi_unified_t)wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE("Invalid wmi handle, twt del dialog failed"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_twt_del_dialog_cmd(wmi_handle, params); +} + diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_utils.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..61fd15bcb4e985752ec6f2850125bab9473d8d21 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_utils.c @@ -0,0 +1,4946 @@ +/* + * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_utis.c + * This file contains utilities and stats related functions. + */ + +/* Header files */ + +#include "wma.h" +#include "wma_api.h" +#include "cds_api.h" +#include "wmi_unified_api.h" +#include "wlan_qct_sys.h" +#include "wni_api.h" +#include "ani_global.h" +#include "wmi_unified.h" +#include "wni_cfg.h" + +#include "qdf_nbuf.h" +#include "qdf_types.h" +#include "qdf_mem.h" + +#include "wma_types.h" +#include "lim_api.h" +#include "lim_session_utils.h" + +#include "cds_utils.h" + +#if !defined(REMOVE_PKT_LOG) +#include "pktlog_ac.h" +#endif /* REMOVE_PKT_LOG */ + +#include "dbglog_host.h" +#include "csr_api.h" +#include "ol_fw.h" + +#include "wma_internal.h" +#include "wlan_policy_mgr_api.h" +#include "wmi_unified_param.h" +#include "linux/ieee80211.h" +#include +#include +#include "cds_reg_service.h" +#include "target_if.h" +#include +#include +#include "host_diag_core_log.h" +#include +#include <../../core/src/vdev_mgr_ops.h> +#include "cdp_txrx_misc.h" +#include + +/* MCS Based rate table */ +/* HT MCS parameters with Nss = 1 */ +static struct index_data_rate_type mcs_nss1[] = { + /* MCS L20 S20 L40 S40 */ + {0, {65, 72}, {135, 150 } }, + {1, {130, 144}, {270, 300 } }, + {2, {195, 217}, {405, 450 } }, + {3, {260, 289}, {540, 600 } }, + {4, {390, 433}, {815, 900 } }, + {5, {520, 578}, {1080, 1200} }, + {6, {585, 650}, {1215, 1350} }, + {7, {650, 722}, {1350, 1500} } +}; + +/* HT MCS parameters with Nss = 2 */ +static struct index_data_rate_type mcs_nss2[] = { + /* MCS L20 S20 L40 S40 */ + {0, {130, 144}, {270, 300 } }, + {1, {260, 289}, {540, 600 } }, + {2, {390, 433}, {810, 900 } }, + {3, {520, 578}, {1080, 1200} }, + {4, {780, 867}, {1620, 1800} }, + {5, {1040, 1156}, {2160, 2400} }, + {6, {1170, 1300}, {2430, 2700} }, + {7, {1300, 1440}, {2700, 3000} } +}; + +/* MCS Based VHT rate table */ +/* MCS parameters with Nss = 1*/ +static struct index_vht_data_rate_type vht_mcs_nss1[] = { + /* MCS L20 S20 L40 S40 L80 S80 L160 S160*/ + {0, {65, 72 }, {135, 150}, {293, 325}, {585, 650} }, + {1, {130, 144}, {270, 300}, {585, 650}, {1170, 1300} }, + {2, {195, 217}, {405, 450}, {878, 975}, {1755, 1950} }, + {3, {260, 289}, {540, 600}, {1170, 1300}, {2340, 2600} }, + {4, {390, 433}, {810, 900}, {1755, 1950}, {3510, 3900} }, + {5, {520, 578}, {1080, 1200}, {2340, 2600}, {4680, 5200} }, + {6, {585, 650}, {1215, 1350}, {2633, 2925}, {5265, 5850} }, + {7, {650, 722}, {1350, 1500}, {2925, 3250}, {5850, 6500} }, + {8, {780, 867}, {1620, 1800}, {3510, 3900}, {7020, 7800} }, + {9, {865, 960}, {1800, 2000}, {3900, 4333}, {7800, 8667} } +}; + +/*MCS parameters with Nss = 2*/ +static struct index_vht_data_rate_type vht_mcs_nss2[] = { + /* MCS L20 S20 L40 S40 L80 S80 L160 S160*/ + {0, {130, 144}, {270, 300}, { 585, 650}, {1170, 1300} }, + {1, {260, 289}, {540, 600}, {1170, 1300}, {2340, 2600} }, + {2, {390, 433}, {810, 900}, {1755, 1950}, {3510, 3900} }, + {3, {520, 578}, {1080, 1200}, {2340, 2600}, {4680, 5200} }, + {4, {780, 867}, {1620, 1800}, {3510, 3900}, {7020, 7800} }, + {5, {1040, 1156}, {2160, 2400}, {4680, 5200}, {9360, 10400} }, + {6, {1170, 1300}, {2430, 2700}, {5265, 5850}, {10530, 11700} }, + {7, {1300, 1444}, {2700, 3000}, {5850, 6500}, {11700, 13000} }, + {8, {1560, 1733}, {3240, 3600}, {7020, 7800}, {14040, 15600} }, + {9, {1730, 1920}, {3600, 4000}, {7800, 8667}, {15600, 17333} } +}; + +#ifdef WLAN_FEATURE_11AX +/* MCS Based HE rate table */ +/* MCS parameters with Nss = 1*/ +static struct index_he_data_rate_type he_mcs_nss1[] = { +/* MCS, {dcm0:0.8/1.6/3.2}, {dcm1:0.8/1.6/3.2} */ + {0, {{86, 81, 73 }, {43, 40, 36 } }, /* HE20 */ + {{172, 163, 146 }, {86, 81, 73 } }, /* HE40 */ + {{360, 340, 306 }, {180, 170, 153} }, /* HE80 */ + {{721, 681, 613 }, {360, 340, 306} } }, /* HE160/HE80+80 */ + {1, {{172, 163, 146 }, {86, 81, 73 } }, + {{344, 325, 293 }, {172, 163, 146} }, + {{721, 681, 613 }, {360, 340, 306} }, + {{1441, 1361, 1225}, {721, 681, 613} } }, + {2, {{258, 244, 219 }, {0} }, + {{516, 488, 439 }, {0} }, + {{1081, 1021, 919 }, {0} }, + {{2162, 2042, 1838}, {0} } }, + {3, {{344, 325, 293 }, {172, 163, 146} }, + {{688, 650, 585 }, {344, 325, 293} }, + {{1441, 1361, 1225}, {721, 681, 613} }, + {{2882, 2722, 2450}, {1441, 1361, 1225} } }, + {4, {{516, 488, 439 }, {258, 244, 219} }, + {{1032, 975, 878 }, {516, 488, 439} }, + {{2162, 2042, 1838}, {1081, 1021, 919} }, + {{4324, 4083, 3675}, {2162, 2042, 1838} } }, + {5, {{688, 650, 585 }, {0} }, + {{1376, 1300, 1170}, {0} }, + {{2882, 2722, 2450}, {0} }, + {{5765, 5444, 4900}, {0} } }, + {6, {{774, 731, 658 }, {0} }, + {{1549, 1463, 1316}, {0} }, + {{3243, 3063, 2756}, {0} }, + {{6485, 6125, 5513}, {0} } }, + {7, {{860, 813, 731 }, {0} }, + {{1721, 1625, 1463}, {0} }, + {{3603, 3403, 3063}, {0} }, + {{7206, 6806, 6125}, {0} } }, + {8, {{1032, 975, 878 }, {0} }, + {{2065, 1950, 1755}, {0} }, + {{4324, 4083, 3675}, {0} }, + {{8647, 8167, 7350}, {0} } }, + {9, {{1147, 1083, 975 }, {0} }, + {{2294, 2167, 1950}, {0} }, + {{4804, 4537, 4083}, {0} }, + {{9607, 9074, 8166}, {0} } }, + {10, {{1290, 1219, 1097}, {0} }, + {{2581, 2438, 2194}, {0} }, + {{5404, 5104, 4594}, {0} }, + {{10809, 10208, 9188}, {0} } }, + {11, {{1434, 1354, 1219}, {0} }, + {{2868, 2708, 2438}, {0} }, + {{6004, 5671, 5104}, {0} }, + {{12010, 11342, 10208}, {0} } } +}; + +/*MCS parameters with Nss = 2*/ +static struct index_he_data_rate_type he_mcs_nss2[] = { +/* MCS, {dcm0:0.8/1.6/3.2}, {dcm1:0.8/1.6/3.2} */ + {0, {{172, 163, 146 }, {86, 81, 73 } }, /* HE20 */ + {{344, 325, 293 }, {172, 163, 146} }, /* HE40 */ + {{721, 681, 613 }, {360, 340, 306} }, /* HE80 */ + {{1441, 1361, 1225}, {721, 681, 613} } }, /* HE160/HE80+80 */ + {1, {{344, 325, 293 }, {172, 163, 146} }, + {{688, 650, 585 }, {344, 325, 293} }, + {{1441, 1361, 1225}, {721, 681, 613} }, + {{2882, 2722, 2450}, {1441, 1361, 1225} } }, + {2, {{516, 488, 439 }, {0} }, + {{1032, 975, 878 }, {0} }, + {{2162, 2042, 1838}, {0} }, + {{4324, 4083, 3675}, {0} } }, + {3, {{688, 650, 585 }, {344, 325, 293 } }, + {{1376, 1300, 1170}, {688, 650, 585 } }, + {{2882, 2722, 2450}, {1441, 1361, 1225} }, + {{5765, 5444, 4900}, {2882, 2722, 2450} } }, + {4, {{1032, 975, 878 }, {516, 488, 439 } }, + {{2065, 1950, 1755}, {1032, 975, 878 } }, + {{4324, 4083, 3675}, {2162, 2042, 1838} }, + {{8647, 8167, 7350}, {4324, 4083, 3675} } }, + {5, {{1376, 1300, 1170}, {0} }, + {{2753, 2600, 2340}, {0} }, + {{5765, 5444, 4900}, {0} }, + {{11529, 10889, 9800}, {0} } }, + {6, {{1549, 1463, 1316}, {0} }, + {{3097, 2925, 2633}, {0} }, + {{6485, 6125, 5513}, {0} }, + {{12971, 12250, 11025}, {0} } }, + {7, {{1721, 1625, 1463}, {0} }, + {{3441, 3250, 2925}, {0} }, + {{7206, 6806, 6125}, {0} }, + {{14412, 13611, 12250}, {0} } }, + {8, {{2065, 1950, 1755}, {0} }, + {{4129, 3900, 3510}, {0} }, + {{8647, 8167, 7350}, {0} }, + {{17294, 16333, 14700}, {0} } }, + {9, {{2294, 2167, 1950}, {0} }, + {{4588, 4333, 3900}, {0} }, + {{9607, 9074, 8166}, {0} }, + {{19215, 18148, 16333}, {0} } }, + {10, {{2581, 2438, 2194}, {0} }, + {{5162, 4875, 4388}, {0} }, + {{10809, 10208, 9188}, {0} }, + {{21618, 20417, 18375}, {0} } }, + {11, {{2868, 2708, 2438}, {0} }, + {{5735, 5417, 4875}, {0} }, + {{12010, 11343, 10208}, {0} }, + {{24019, 22685, 20416}, {0} } } +}; +#endif + +#ifdef BIG_ENDIAN_HOST + +/* ############# function definitions ############ */ + +/** + * wma_swap_bytes() - swap bytes + * @pv: buffer + * @n: swap bytes + * + * Return: none + */ +void wma_swap_bytes(void *pv, uint32_t n) +{ + int32_t no_words; + int32_t i; + uint32_t *word_ptr; + + no_words = n / sizeof(uint32_t); + word_ptr = (uint32_t *) pv; + for (i = 0; i < no_words; i++) + *(word_ptr + i) = __cpu_to_le32(*(word_ptr + i)); +} + +#define SWAPME(x, len) wma_swap_bytes(&x, len) +#endif /* BIG_ENDIAN_HOST */ + +/** + * wma_mcs_rate_match() - find the match mcs rate + * @raw_rate: the rate to look up + * @is_he: if it is he rate + * @nss1_rate: the nss1 rate + * @nss2_rate: the nss2 rate + * @nss: the nss in use + * @guard_interval: to get guard interval from rate + * + * This is a helper function to find the match of the tx_rate + * and return nss/guard interval. + * + * Return: the found rate or 0 otherwise + */ +static inline uint16_t wma_mcs_rate_match(uint16_t raw_rate, + bool is_he, + uint16_t *nss1_rate, + uint16_t *nss2_rate, + uint8_t *nss, + enum txrate_gi *guard_interval) +{ + uint8_t gi_index; + uint8_t gi_index_max = 2; + uint16_t ret_rate = 0; + + if (is_he) + gi_index_max = 3; + + for (gi_index = 0; gi_index < gi_index_max; gi_index++) { + if (raw_rate == nss1_rate[gi_index]) { + *nss = 1; + ret_rate = nss1_rate[gi_index]; + break; + } + if (*nss == 2 && raw_rate == nss2_rate[gi_index]) { + ret_rate = nss2_rate[gi_index]; + break; + } + } + + if (ret_rate) { + if (gi_index == 1) + *guard_interval = + is_he ? TXRATE_GI_1_6_US : TXRATE_GI_0_4_US; + else if (is_he && gi_index == 2) + *guard_interval = TXRATE_GI_3_2_US; + else + *guard_interval = TXRATE_GI_0_8_US; + } + + return ret_rate; +} + +#ifdef WLAN_FEATURE_11AX +/** + * wma_get_mcs_idx() - get mcs index + * @raw_rate: raw rate from fw + * @rate_flags: rate flags + * @nss: nss + * @dcm: dcm + * @guard_interval: guard interval + * @mcs_rate_flag: mcs rate flags + * @p_index: index for matched rate + * + * Return: return match rate if found, else 0 + */ +static uint16_t wma_match_he_rate(uint16_t raw_rate, + enum tx_rate_info rate_flags, + uint8_t *nss, uint8_t *dcm, + enum txrate_gi *guard_interval, + enum tx_rate_info *mcs_rate_flag, + uint8_t *p_index) +{ + uint8_t index = 0; + uint8_t dcm_index_max = 1; + uint8_t dcm_index = 0; + uint16_t match_rate = 0; + uint16_t *nss1_rate; + uint16_t *nss2_rate; + + *p_index = 0; + if (!(rate_flags & (TX_RATE_HE160 | TX_RATE_HE80 | TX_RATE_HE40 | + TX_RATE_HE20))) + return 0; + + for (index = 0; index < MAX_HE_MCS_IDX; index++) { + dcm_index_max = IS_MCS_HAS_DCM_RATE(index) ? 2 : 1; + + for (dcm_index = 0; dcm_index < dcm_index_max; + dcm_index++) { + if (rate_flags & TX_RATE_HE160) { + nss1_rate = &he_mcs_nss1[index]. + supported_he160_rate[dcm_index][0]; + nss2_rate = &he_mcs_nss2[index]. + supported_he160_rate[dcm_index][0]; + /* check for he160 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 1, + nss1_rate, + nss2_rate, + nss, + guard_interval); + if (match_rate) + goto rate_found; + } + + if (rate_flags & (TX_RATE_HE80 | TX_RATE_HE160)) { + nss1_rate = &he_mcs_nss1[index]. + supported_he80_rate[dcm_index][0]; + nss2_rate = &he_mcs_nss2[index]. + supported_he80_rate[dcm_index][0]; + /* check for he80 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 1, + nss1_rate, + nss2_rate, + nss, + guard_interval); + if (match_rate) { + *mcs_rate_flag &= ~TX_RATE_HE160; + goto rate_found; + } + } + + if (rate_flags & (TX_RATE_HE40 | TX_RATE_HE80 | + TX_RATE_HE160)) { + nss1_rate = &he_mcs_nss1[index]. + supported_he40_rate[dcm_index][0]; + nss2_rate = &he_mcs_nss2[index]. + supported_he40_rate[dcm_index][0]; + /* check for he40 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 1, + nss1_rate, + nss2_rate, + nss, + guard_interval); + + if (match_rate) { + *mcs_rate_flag &= ~(TX_RATE_HE80 | + TX_RATE_HE160); + goto rate_found; + } + } + + if (rate_flags & (TX_RATE_HE80 | TX_RATE_HE40 | + TX_RATE_HE20 | TX_RATE_HE160)) { + nss1_rate = &he_mcs_nss1[index]. + supported_he20_rate[dcm_index][0]; + nss2_rate = &he_mcs_nss2[index]. + supported_he20_rate[dcm_index][0]; + /* check for he20 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 1, + nss1_rate, + nss2_rate, + nss, + guard_interval); + + if (match_rate) { + *mcs_rate_flag &= TX_RATE_HE20; + goto rate_found; + } + } + } + } + +rate_found: + if (match_rate) { + if (dcm_index == 1) + *dcm = 1; + *p_index = index; + } + return match_rate; +} +#else +static uint16_t wma_match_he_rate(uint16_t raw_rate, + enum tx_rate_info rate_flags, + uint8_t *nss, uint8_t *dcm, + enum txrate_gi *guard_interval, + enum tx_rate_info *mcs_rate_flag, + uint8_t *p_index) +{ + return 0; +} +#endif + +uint8_t wma_get_mcs_idx(uint16_t raw_rate, enum tx_rate_info rate_flags, + uint8_t *nss, uint8_t *dcm, + enum txrate_gi *guard_interval, + enum tx_rate_info *mcs_rate_flag) +{ + uint8_t index = 0; + uint16_t match_rate = 0; + uint16_t *nss1_rate; + uint16_t *nss2_rate; + + wma_debug("Rates from FW: raw_rate:%d rate_flgs: 0x%x, nss: %d", + raw_rate, rate_flags, *nss); + + *mcs_rate_flag = rate_flags; + + match_rate = wma_match_he_rate(raw_rate, rate_flags, + nss, dcm, guard_interval, + mcs_rate_flag, &index); + if (match_rate) + goto rate_found; + + for (index = 0; index < MAX_VHT_MCS_IDX; index++) { + if (rate_flags & TX_RATE_VHT160) { + nss1_rate = &vht_mcs_nss1[index].ht160_rate[0]; + nss2_rate = &vht_mcs_nss2[index].ht160_rate[0]; + /* check for vht160 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 0, + nss1_rate, + nss2_rate, + nss, guard_interval); + if (match_rate) + goto rate_found; + } + if (rate_flags & (TX_RATE_VHT80 | TX_RATE_VHT160)) { + nss1_rate = &vht_mcs_nss1[index].ht80_rate[0]; + nss2_rate = &vht_mcs_nss2[index].ht80_rate[0]; + /* check for vht80 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 0, + nss1_rate, + nss2_rate, + nss, guard_interval); + if (match_rate) { + *mcs_rate_flag &= ~TX_RATE_VHT160; + goto rate_found; + } + } + if (rate_flags & (TX_RATE_VHT40 | TX_RATE_VHT80 | + TX_RATE_VHT160)) { + nss1_rate = &vht_mcs_nss1[index].ht40_rate[0]; + nss2_rate = &vht_mcs_nss2[index].ht40_rate[0]; + /* check for vht40 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 0, + nss1_rate, + nss2_rate, + nss, guard_interval); + if (match_rate) { + *mcs_rate_flag &= ~TX_RATE_VHT80; + goto rate_found; + } + } + if (rate_flags & (TX_RATE_VHT20 | TX_RATE_VHT40 | + TX_RATE_VHT80 | TX_RATE_VHT160)) { + nss1_rate = &vht_mcs_nss1[index].ht20_rate[0]; + nss2_rate = &vht_mcs_nss2[index].ht20_rate[0]; + /* check for vht20 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 0, + nss1_rate, + nss2_rate, + nss, guard_interval); + if (match_rate) { + *mcs_rate_flag &= ~(TX_RATE_VHT80 | + TX_RATE_VHT40); + goto rate_found; + } + } + } + for (index = 0; index < MAX_HT_MCS_IDX; index++) { + if (rate_flags & TX_RATE_HT40) { + nss1_rate = &mcs_nss1[index].ht40_rate[0]; + nss2_rate = &mcs_nss2[index].ht40_rate[0]; + /* check for ht40 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 0, + nss1_rate, + nss2_rate, + nss, guard_interval); + if (match_rate) { + *mcs_rate_flag = TX_RATE_HT40; + if (*nss == 2) + index += MAX_HT_MCS_IDX; + goto rate_found; + } + } + if (rate_flags & (TX_RATE_HT20 | TX_RATE_HT40)) { + nss1_rate = &mcs_nss1[index].ht20_rate[0]; + nss2_rate = &mcs_nss2[index].ht20_rate[0]; + /* check for ht20 nss1/2 rate set */ + match_rate = wma_mcs_rate_match(raw_rate, 0, + nss1_rate, + nss2_rate, + nss, guard_interval); + if (match_rate) { + *mcs_rate_flag = TX_RATE_HT20; + if (*nss == 2) + index += MAX_HT_MCS_IDX; + goto rate_found; + } + } + } + +rate_found: + + /* set SGI flag only if this is SGI rate */ + if (match_rate && *guard_interval == TXRATE_GI_0_4_US) + *mcs_rate_flag |= TX_RATE_SGI; + else + *mcs_rate_flag &= ~TX_RATE_SGI; + + WMA_LOGD("%s Matched rate in table: %d index: %d" + " mcs_rate_flag: 0x%x nss %d guard interval %d", + __func__, match_rate, index, *mcs_rate_flag, + *nss, *guard_interval); + + return match_rate ? index : INVALID_MCS_IDX; +} + +void wma_lost_link_info_handler(tp_wma_handle wma, uint32_t vdev_id, + int32_t rssi) +{ + struct sir_lost_link_info *lost_link_info; + QDF_STATUS qdf_status; + struct scheduler_msg sme_msg = {0}; + + if (vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: received invalid vdev_id %d", + __func__, vdev_id); + return; + } + + /* report lost link information only for STA mode */ + if (wma_is_vdev_up(vdev_id) && + (WMI_VDEV_TYPE_STA == wma->interfaces[vdev_id].type) && + (0 == wma->interfaces[vdev_id].sub_type)) { + lost_link_info = qdf_mem_malloc(sizeof(*lost_link_info)); + if (!lost_link_info) + return; + + lost_link_info->vdev_id = vdev_id; + lost_link_info->rssi = rssi; + sme_msg.type = eWNI_SME_LOST_LINK_INFO_IND; + sme_msg.bodyptr = lost_link_info; + sme_msg.bodyval = 0; + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, + &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("%s: fail to post msg to SME", __func__); + qdf_mem_free(lost_link_info); + } + } +} + +/** + * host_map_smps_mode() - map fw smps mode to enum eSmpsModeValue + * @fw_smps_mode: fw smps mode + * + * Return: return enum eSmpsModeValue + */ +enum eSmpsModeValue host_map_smps_mode(A_UINT32 fw_smps_mode) +{ + enum eSmpsModeValue smps_mode = SMPS_MODE_DISABLED; + + switch (fw_smps_mode) { + case WMI_SMPS_FORCED_MODE_STATIC: + smps_mode = STATIC_SMPS_MODE; + break; + case WMI_SMPS_FORCED_MODE_DYNAMIC: + smps_mode = DYNAMIC_SMPS_MODE; + break; + default: + smps_mode = SMPS_MODE_DISABLED; + } + + return smps_mode; +} + +/** + * wma_smps_mode_to_force_mode_param() - Map smps mode to force + * mode commmand param + * @smps_mode: SMPS mode according to the protocol + * + * Return: int > 0 for success else failure + */ +int wma_smps_mode_to_force_mode_param(uint8_t smps_mode) +{ + int param = -EINVAL; + + switch (smps_mode) { + case STATIC_SMPS_MODE: + param = WMI_SMPS_FORCED_MODE_STATIC; + break; + case DYNAMIC_SMPS_MODE: + param = WMI_SMPS_FORCED_MODE_DYNAMIC; + break; + case SMPS_MODE_DISABLED: + param = WMI_SMPS_FORCED_MODE_DISABLED; + break; + default: + WMA_LOGE(FL("smps mode cannot be mapped :%d "), + smps_mode); + } + return param; +} + +#ifdef WLAN_FEATURE_STATS_EXT +#ifdef FEATURE_STATS_EXT_V2 +int wma_stats_ext_event_handler(void *handle, uint8_t *event_buf, + uint32_t len) +{ + WMI_STATS_EXT_EVENTID_param_tlvs *param_buf; + tSirStatsExtEvent *stats_ext_event; + wmi_stats_ext_event_fixed_param *stats_ext_info; + QDF_STATUS status; + struct scheduler_msg cds_msg = {0}; + uint8_t *buf_ptr; + uint32_t alloc_len; + struct cdp_txrx_ext_stats ext_stats = {0}; + struct cdp_soc_t *soc_hdl = cds_get_context(QDF_MODULE_ID_SOC); + + wma_debug("%s: Posting stats ext event to SME", __func__); + + param_buf = (WMI_STATS_EXT_EVENTID_param_tlvs *)event_buf; + if (!param_buf) { + wma_err("%s: Invalid stats ext event buf", __func__); + return -EINVAL; + } + + stats_ext_info = param_buf->fixed_param; + buf_ptr = (uint8_t *)stats_ext_info; + + alloc_len = sizeof(tSirStatsExtEvent); + alloc_len += stats_ext_info->data_len; + alloc_len += sizeof(struct cdp_txrx_ext_stats); + + if (stats_ext_info->data_len > (WMI_SVC_MSG_MAX_SIZE - + WMI_TLV_HDR_SIZE - sizeof(*stats_ext_info)) || + stats_ext_info->data_len > param_buf->num_data) { + wma_err("Excess data_len:%d, num_data:%d", + stats_ext_info->data_len, param_buf->num_data); + return -EINVAL; + } + stats_ext_event = qdf_mem_malloc(alloc_len); + if (!stats_ext_event) + return -ENOMEM; + + buf_ptr += sizeof(wmi_stats_ext_event_fixed_param) + WMI_TLV_HDR_SIZE; + + stats_ext_event->vdev_id = stats_ext_info->vdev_id; + stats_ext_event->event_data_len = stats_ext_info->data_len; + qdf_mem_copy(stats_ext_event->event_data, + buf_ptr, stats_ext_event->event_data_len); + + cdp_txrx_ext_stats_request(soc_hdl, OL_TXRX_PDEV_ID, &ext_stats); + qdf_mem_copy(stats_ext_event->event_data + + stats_ext_event->event_data_len, + &ext_stats, sizeof(struct cdp_txrx_ext_stats)); + + stats_ext_event->event_data_len += sizeof(struct cdp_txrx_ext_stats); + + cds_msg.type = eWNI_SME_STATS_EXT_EVENT; + cds_msg.bodyptr = (void *)stats_ext_event; + cds_msg.bodyval = 0; + + status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &cds_msg); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(stats_ext_event); + return -EFAULT; + } + + wma_debug("%s: stats ext event Posted to SME", __func__); + return 0; +} +#else +int wma_stats_ext_event_handler(void *handle, uint8_t *event_buf, + uint32_t len) +{ + WMI_STATS_EXT_EVENTID_param_tlvs *param_buf; + tSirStatsExtEvent *stats_ext_event; + wmi_stats_ext_event_fixed_param *stats_ext_info; + QDF_STATUS status; + struct scheduler_msg cds_msg = {0}; + uint8_t *buf_ptr; + uint32_t alloc_len; + + WMA_LOGD("%s: Posting stats ext event to SME", __func__); + + param_buf = (WMI_STATS_EXT_EVENTID_param_tlvs *)event_buf; + if (!param_buf) { + WMA_LOGE("%s: Invalid stats ext event buf", __func__); + return -EINVAL; + } + + stats_ext_info = param_buf->fixed_param; + buf_ptr = (uint8_t *)stats_ext_info; + + alloc_len = sizeof(tSirStatsExtEvent); + alloc_len += stats_ext_info->data_len; + + if (stats_ext_info->data_len > (WMI_SVC_MSG_MAX_SIZE - + WMI_TLV_HDR_SIZE - sizeof(*stats_ext_info)) || + stats_ext_info->data_len > param_buf->num_data) { + WMA_LOGE("Excess data_len:%d, num_data:%d", + stats_ext_info->data_len, param_buf->num_data); + return -EINVAL; + } + stats_ext_event = qdf_mem_malloc(alloc_len); + if (!stats_ext_event) + return -ENOMEM; + + buf_ptr += sizeof(wmi_stats_ext_event_fixed_param) + WMI_TLV_HDR_SIZE; + + stats_ext_event->vdev_id = stats_ext_info->vdev_id; + stats_ext_event->event_data_len = stats_ext_info->data_len; + qdf_mem_copy(stats_ext_event->event_data, + buf_ptr, stats_ext_event->event_data_len); + + cds_msg.type = eWNI_SME_STATS_EXT_EVENT; + cds_msg.bodyptr = (void *)stats_ext_event; + cds_msg.bodyval = 0; + + status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &cds_msg); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(stats_ext_event); + return -EFAULT; + } + + WMA_LOGD("%s: stats ext event Posted to SME", __func__); + return 0; +} +#endif +#endif /* WLAN_FEATURE_STATS_EXT */ + +/** + * wma_profile_data_report_event_handler() - fw profiling handler + * @handle: wma handle + * @event_buf: event buffer received from fw + * @len: length of data + * + * Return: 0 for success or error code + */ +int wma_profile_data_report_event_handler(void *handle, uint8_t *event_buf, + uint32_t len) +{ + WMI_WLAN_PROFILE_DATA_EVENTID_param_tlvs *param_buf; + wmi_wlan_profile_ctx_t *profile_ctx; + wmi_wlan_profile_t *profile_data; + uint32_t i = 0; + uint32_t entries; + char temp_str[150]; + + param_buf = (WMI_WLAN_PROFILE_DATA_EVENTID_param_tlvs *) event_buf; + if (!param_buf) { + WMA_LOGE("%s: Invalid profile data event buf", __func__); + return -EINVAL; + } + + profile_ctx = param_buf->profile_ctx; + entries = profile_ctx->bin_count; + if (entries > param_buf->num_profile_data) { + WMA_LOGE("FW bin count %d more than data %d in TLV hdr", + entries, + param_buf->num_profile_data); + return -EINVAL; + } + + QDF_TRACE(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_ERROR, + "Profile data stats\n"); + QDF_TRACE(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_ERROR, + "TOT: %d\n" + "tx_msdu_cnt: %d\n" + "tx_mpdu_cnt: %d\n" + "tx_ppdu_cnt: %d\n" + "rx_msdu_cnt: %d\n" + "rx_mpdu_cnt: %d\n" + "bin_count: %d\n", + profile_ctx->tot, + profile_ctx->tx_msdu_cnt, + profile_ctx->tx_mpdu_cnt, + profile_ctx->tx_ppdu_cnt, + profile_ctx->rx_msdu_cnt, + profile_ctx->rx_mpdu_cnt, + profile_ctx->bin_count); + + QDF_TRACE(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_ERROR, + "Profile ID: Count: TOT: Min: Max: hist_intvl: hist[0]: hist[1]:hist[2]"); + + profile_data = param_buf->profile_data; + for (i = 0; i < entries; i++) { + if (i == WMI_WLAN_PROFILE_MAX_BIN_CNT) + break; + snprintf(temp_str, sizeof(temp_str), + " %d : %d : %d : %d : %d : %d : %d : %d : %d", + profile_data[i].id, + profile_data[i].cnt, + profile_data[i].tot, + profile_data[i].min, + profile_data[i].max, + profile_data[i].hist_intvl, + profile_data[i].hist[0], + profile_data[i].hist[1], + profile_data[i].hist[2]); + QDF_TRACE(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_ERROR, + "%s", temp_str); + } + + return 0; +} + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS + +#define WMA_FILL_TX_STATS(eve, msg) do {\ + (msg)->msdus = (eve)->tx_msdu_cnt;\ + (msg)->mpdus = (eve)->tx_mpdu_cnt;\ + (msg)->ppdus = (eve)->tx_ppdu_cnt;\ + (msg)->bytes = (eve)->tx_bytes;\ + (msg)->drops = (eve)->tx_msdu_drop_cnt;\ + (msg)->drop_bytes = (eve)->tx_drop_bytes;\ + (msg)->retries = (eve)->tx_mpdu_retry_cnt;\ + (msg)->failed = (eve)->tx_mpdu_fail_cnt;\ +} while (0) + +#define WMA_FILL_RX_STATS(eve, msg) do {\ + (msg)->mpdus = (eve)->mac_rx_mpdu_cnt;\ + (msg)->bytes = (eve)->mac_rx_bytes;\ + (msg)->ppdus = (eve)->phy_rx_ppdu_cnt;\ + (msg)->ppdu_bytes = (eve)->phy_rx_bytes;\ + (msg)->mpdu_retry = (eve)->rx_mpdu_retry_cnt;\ + (msg)->mpdu_dup = (eve)->rx_mpdu_dup_cnt;\ + (msg)->mpdu_discard = (eve)->rx_mpdu_discard_cnt;\ +} while (0) + +/** + * wma_get_ll_stats_ext_buf() - alloc result buffer for MAC counters + * @len: buffer length output + * @peer_num: peer number + * @fixed_param: fixed parameters in WMI event + * + * Structure of the stats message + * LL_EXT_STATS + * | + * |--Channel stats[1~n] + * |--Peer[1~n] + * | + * +---Signal + * +---TX + * | +---BE + * | +---BK + * | +---VI + * | +---VO + * | + * +---RX + * +---BE + * +---BK + * +---VI + * +---VO + * For each Access Category, the arregation and mcs + * stats are as this: + * TX + * +-BE/BK/VI/VO + * +----tx_mpdu_aggr_array + * +----tx_succ_mcs_array + * +----tx_fail_mcs_array + * +----tx_delay_array + * RX + * +-BE/BK/VI/VO + * +----rx_mpdu_aggr_array + * +----rx_mcs_array + * + * return: Address for result buffer. + */ +static tSirLLStatsResults *wma_get_ll_stats_ext_buf(uint32_t *len, + uint32_t peer_num, + wmi_report_stats_event_fixed_param *fixed_param) +{ + tSirLLStatsResults *buf; + uint32_t buf_len; + uint32_t total_array_len, total_peer_len; + bool excess_data = false; + + if (!len || !fixed_param) { + WMA_LOGE(FL("Invalid input parameters.")); + return NULL; + } + + /* + * Result buffer has a structure like this: + * --------------------------------- + * | trigger_cond_i | + * +-------------------------------+ + * | cca_chgd_bitmap | + * +-------------------------------+ + * | sig_chgd_bitmap | + * +-------------------------------+ + * | tx_chgd_bitmap | + * +-------------------------------+ + * | rx_chgd_bitmap | + * +-------------------------------+ + * | peer_num | + * +-------------------------------+ + * | channel_num | + * +-------------------------------+ + * | tx_mpdu_aggr_array_len | + * +-------------------------------+ + * | tx_succ_mcs_array_len | + * +-------------------------------+ + * | tx_fail_mcs_array_len | + * +-------------------------------+ + * | tx_delay_array_len | + * +-------------------------------+ + * | rx_mpdu_aggr_array_len | + * +-------------------------------+ + * | rx_mcs_array_len | + * +-------------------------------+ + * | pointer to CCA stats | + * +-------------------------------+ + * | CCA stats | + * +-------------------------------+ + * | peer_stats |----+ + * +-------------------------------+ | + * | TX aggr/mcs parameters array | | + * | Length of this buffer is | | + * | not fixed. |<-+ | + * +-------------------------------+ | | + * | per peer tx stats |--+ | + * | BE | <--+ + * | BK | | + * | VI | | + * | VO | | + * +-------------------------------+ | + * | TX aggr/mcs parameters array | | + * | Length of this buffer is | | + * | not fixed. |<-+ | + * +-------------------------------+ | | + * | peer peer rx stats |--+ | + * | BE | <--+ + * | BK | + * | VI | + * | VO | + * --------------------------------- + */ + + buf_len = sizeof(tSirLLStatsResults) + + sizeof(struct sir_wifi_ll_ext_stats); + do { + if (fixed_param->num_chan_cca_stats > (WMI_SVC_MSG_MAX_SIZE / + sizeof(struct sir_wifi_chan_cca_stats))) { + excess_data = true; + break; + } + buf_len += (fixed_param->num_chan_cca_stats * + sizeof(struct sir_wifi_chan_cca_stats)); + if (fixed_param->tx_mpdu_aggr_array_len > + WMI_SVC_MSG_MAX_SIZE) { + excess_data = true; + break; + } else { + total_array_len = fixed_param->tx_mpdu_aggr_array_len; + } + if (fixed_param->tx_succ_mcs_array_len > + (WMI_SVC_MSG_MAX_SIZE - total_array_len)) { + excess_data = true; + break; + } else { + total_array_len += fixed_param->tx_succ_mcs_array_len; + } + if (fixed_param->tx_fail_mcs_array_len > + (WMI_SVC_MSG_MAX_SIZE - total_array_len)) { + excess_data = true; + break; + } else { + total_array_len += fixed_param->tx_fail_mcs_array_len; + } + if (fixed_param->tx_ppdu_delay_array_len > + (WMI_SVC_MSG_MAX_SIZE - total_array_len)) { + excess_data = true; + break; + } else { + total_array_len += fixed_param->tx_ppdu_delay_array_len; + } + if (fixed_param->rx_mpdu_aggr_array_len > + (WMI_SVC_MSG_MAX_SIZE - total_array_len)) { + excess_data = true; + break; + } else { + total_array_len += fixed_param->rx_mpdu_aggr_array_len; + } + if (fixed_param->rx_mcs_array_len > + (WMI_SVC_MSG_MAX_SIZE - total_array_len)) { + excess_data = true; + break; + } else { + total_array_len += fixed_param->rx_mcs_array_len; + } + + if (total_array_len > (WMI_SVC_MSG_MAX_SIZE / + (sizeof(uint32_t) * WLAN_MAX_AC))) { + excess_data = true; + break; + } else { + total_peer_len = (sizeof(uint32_t) * WLAN_MAX_AC * + total_array_len) + + (WLAN_MAX_AC * + (sizeof(struct sir_wifi_tx) + + sizeof(struct sir_wifi_rx))); + } + if (total_peer_len > WMI_SVC_MSG_MAX_SIZE) { + excess_data = true; + break; + } + if (peer_num > WMI_SVC_MSG_MAX_SIZE / (total_peer_len + + sizeof(struct sir_wifi_ll_ext_peer_stats))) { + excess_data = true; + break; + } else { + buf_len += peer_num * + (sizeof(struct sir_wifi_ll_ext_peer_stats) + + total_peer_len); + } + } while (0); + + if (excess_data || (buf_len > WMI_SVC_MSG_MAX_SIZE)) { + WMA_LOGE("%s: excess wmi buffer: peer %d cca %d tx_mpdu %d tx_succ%d tx_fail %d tx_ppdu %d rx_mpdu %d rx_mcs %d", + __func__, peer_num, fixed_param->num_chan_cca_stats, + fixed_param->tx_mpdu_aggr_array_len, + fixed_param->tx_succ_mcs_array_len, + fixed_param->tx_fail_mcs_array_len, + fixed_param->tx_ppdu_delay_array_len, + fixed_param->rx_mpdu_aggr_array_len, + fixed_param->rx_mcs_array_len); + return NULL; + } + + buf = qdf_mem_malloc(buf_len); + if (!buf) + *len = 0; + else + *len = buf_len; + + return buf; +} + +/** + * wma_fill_tx_stats() - Fix TX stats into result buffer + * @ll_stats: LL stats buffer + * @fix_param: parameters with fixed length in WMI event + * @param_buf: parameters without fixed length in WMI event + * @buf: buffer for TLV parameters + * + * Return: QDF_STATUS + */ +static QDF_STATUS +wma_fill_tx_stats(struct sir_wifi_ll_ext_stats *ll_stats, + wmi_report_stats_event_fixed_param *fix_param, + WMI_REPORT_STATS_EVENTID_param_tlvs *param_buf, + uint8_t **buf, uint32_t *buf_length) +{ + uint8_t *result; + uint32_t i, j, k; + wmi_peer_ac_tx_stats *wmi_peer_tx; + wmi_tx_stats *wmi_tx; + struct sir_wifi_tx *tx_stats; + struct sir_wifi_ll_ext_peer_stats *peer_stats; + uint32_t *tx_mpdu_aggr, *tx_succ_mcs, *tx_fail_mcs, *tx_delay; + uint32_t len, dst_len, param_len, num_entries, + tx_mpdu_aggr_array_len, tx_succ_mcs_array_len, + tx_fail_mcs_array_len, tx_delay_array_len; + + result = *buf; + dst_len = *buf_length; + tx_mpdu_aggr_array_len = fix_param->tx_mpdu_aggr_array_len; + ll_stats->tx_mpdu_aggr_array_len = tx_mpdu_aggr_array_len; + tx_succ_mcs_array_len = fix_param->tx_succ_mcs_array_len; + ll_stats->tx_succ_mcs_array_len = tx_succ_mcs_array_len; + tx_fail_mcs_array_len = fix_param->tx_fail_mcs_array_len; + ll_stats->tx_fail_mcs_array_len = tx_fail_mcs_array_len; + tx_delay_array_len = fix_param->tx_ppdu_delay_array_len; + ll_stats->tx_delay_array_len = tx_delay_array_len; + wmi_peer_tx = param_buf->peer_ac_tx_stats; + wmi_tx = param_buf->tx_stats; + + len = fix_param->num_peer_ac_tx_stats * + WLAN_MAX_AC * tx_mpdu_aggr_array_len * sizeof(uint32_t); + param_len = param_buf->num_tx_mpdu_aggr * sizeof(uint32_t); + if (len <= dst_len && len <= param_len && param_buf->tx_mpdu_aggr) { + tx_mpdu_aggr = (uint32_t *)result; + qdf_mem_copy(tx_mpdu_aggr, param_buf->tx_mpdu_aggr, len); + result += len; + dst_len -= len; + } else { + WMA_LOGE(FL("TX_MPDU_AGGR invalid arg, %d, %d, %d"), + len, dst_len, param_len); + return QDF_STATUS_E_FAILURE; + } + + len = fix_param->num_peer_ac_tx_stats * WLAN_MAX_AC * + tx_succ_mcs_array_len * sizeof(uint32_t); + param_len = param_buf->num_tx_succ_mcs * sizeof(uint32_t); + if (len <= dst_len && len <= param_len && param_buf->tx_succ_mcs) { + tx_succ_mcs = (uint32_t *)result; + qdf_mem_copy(tx_succ_mcs, param_buf->tx_succ_mcs, len); + result += len; + dst_len -= len; + } else { + WMA_LOGE(FL("TX_SUCC_MCS invalid arg, %d, %d, %d"), + len, dst_len, param_len); + return QDF_STATUS_E_FAILURE; + } + + len = fix_param->num_peer_ac_tx_stats * WLAN_MAX_AC * + tx_fail_mcs_array_len * sizeof(uint32_t); + param_len = param_buf->num_tx_fail_mcs * sizeof(uint32_t); + if (len <= dst_len && len <= param_len && param_buf->tx_fail_mcs) { + tx_fail_mcs = (uint32_t *)result; + qdf_mem_copy(tx_fail_mcs, param_buf->tx_fail_mcs, len); + result += len; + dst_len -= len; + } else { + WMA_LOGE(FL("TX_FAIL_MCS invalid arg, %d, %d %d"), + len, dst_len, param_len); + return QDF_STATUS_E_FAILURE; + } + + len = fix_param->num_peer_ac_tx_stats * + WLAN_MAX_AC * tx_delay_array_len * sizeof(uint32_t); + param_len = param_buf->num_tx_ppdu_delay * sizeof(uint32_t); + if (len <= dst_len && len <= param_len && param_buf->tx_ppdu_delay) { + tx_delay = (uint32_t *)result; + qdf_mem_copy(tx_delay, param_buf->tx_ppdu_delay, len); + result += len; + dst_len -= len; + } else { + WMA_LOGE(FL("TX_DELAY invalid arg, %d, %d, %d"), + len, dst_len, param_len); + return QDF_STATUS_E_FAILURE; + } + + /* per peer tx stats */ + peer_stats = ll_stats->peer_stats; + if (!wmi_peer_tx || !wmi_tx || !peer_stats) { + WMA_LOGE(FL("Invalid arg, peer_tx %pK, wmi_tx %pK stats %pK"), + wmi_peer_tx, wmi_tx, peer_stats); + return QDF_STATUS_E_FAILURE; + } + + num_entries = fix_param->num_peer_ac_tx_stats * WLAN_MAX_AC; + if (num_entries > param_buf->num_tx_stats) { + wma_err("tx stats invalid arg, %d", num_entries); + return QDF_STATUS_E_FAILURE; + } + + for (i = 0; i < fix_param->num_peer_ac_tx_stats; i++) { + uint32_t peer_id = wmi_peer_tx[i].peer_id; + struct sir_wifi_tx *ac; + wmi_tx_stats *wmi_tx_stats; + + for (j = 0; j < ll_stats->peer_num; j++) { + peer_stats += j; + if (peer_stats->peer_id == WIFI_INVALID_PEER_ID || + peer_stats->peer_id == peer_id) + break; + } + + if (j < ll_stats->peer_num) { + peer_stats->peer_id = wmi_peer_tx[i].peer_id; + peer_stats->vdev_id = wmi_peer_tx[i].vdev_id; + tx_stats = (struct sir_wifi_tx *)result; + for (k = 0; k < WLAN_MAX_AC; k++) { + wmi_tx_stats = &wmi_tx[i * WLAN_MAX_AC + k]; + ac = &tx_stats[k]; + WMA_FILL_TX_STATS(wmi_tx_stats, ac); + ac->mpdu_aggr_size = tx_mpdu_aggr; + ac->aggr_len = tx_mpdu_aggr_array_len * + sizeof(uint32_t); + ac->success_mcs_len = tx_succ_mcs_array_len * + sizeof(uint32_t); + ac->success_mcs = tx_succ_mcs; + ac->fail_mcs = tx_fail_mcs; + ac->fail_mcs_len = tx_fail_mcs_array_len * + sizeof(uint32_t); + ac->delay = tx_delay; + ac->delay_len = tx_delay_array_len * + sizeof(uint32_t); + peer_stats->ac_stats[k].tx_stats = ac; + peer_stats->ac_stats[k].type = k; + tx_mpdu_aggr += tx_mpdu_aggr_array_len; + tx_succ_mcs += tx_succ_mcs_array_len; + tx_fail_mcs += tx_fail_mcs_array_len; + tx_delay += tx_delay_array_len; + } + result += WLAN_MAX_AC * sizeof(struct sir_wifi_tx); + } else { + /* + * Buffer for Peer TX counter overflow. + * There is peer ID mismatch between TX, RX, + * signal counters. + */ + WMA_LOGE(FL("One peer TX info is dropped.")); + + tx_mpdu_aggr += tx_mpdu_aggr_array_len * WLAN_MAX_AC; + tx_succ_mcs += tx_succ_mcs_array_len * WLAN_MAX_AC; + tx_fail_mcs += tx_fail_mcs_array_len * WLAN_MAX_AC; + tx_delay += tx_delay_array_len * WLAN_MAX_AC; + } + } + *buf = result; + *buf_length = dst_len; + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_fill_rx_stats() - Fix RX stats into result buffer + * @ll_stats: LL stats buffer + * @fix_param: parameters with fixed length in WMI event + * @param_buf: parameters without fixed length in WMI event + * @buf: buffer for TLV parameters + * + * Return: QDF_STATUS + */ +static QDF_STATUS +wma_fill_rx_stats(struct sir_wifi_ll_ext_stats *ll_stats, + wmi_report_stats_event_fixed_param *fix_param, + WMI_REPORT_STATS_EVENTID_param_tlvs *param_buf, + uint8_t **buf, uint32_t *buf_length) +{ + uint8_t *result; + uint32_t i, j, k; + uint32_t *rx_mpdu_aggr, *rx_mcs; + wmi_rx_stats *wmi_rx; + wmi_peer_ac_rx_stats *wmi_peer_rx; + struct sir_wifi_rx *rx_stats; + struct sir_wifi_ll_ext_peer_stats *peer_stats; + uint32_t len, dst_len, param_len, + rx_mpdu_aggr_array_len, rx_mcs_array_len; + + rx_mpdu_aggr_array_len = fix_param->rx_mpdu_aggr_array_len; + ll_stats->rx_mpdu_aggr_array_len = rx_mpdu_aggr_array_len; + rx_mcs_array_len = fix_param->rx_mcs_array_len; + ll_stats->rx_mcs_array_len = rx_mcs_array_len; + wmi_peer_rx = param_buf->peer_ac_rx_stats; + wmi_rx = param_buf->rx_stats; + + result = *buf; + dst_len = *buf_length; + len = sizeof(uint32_t) * (fix_param->num_peer_ac_rx_stats * + WLAN_MAX_AC * rx_mpdu_aggr_array_len); + param_len = param_buf->num_rx_mpdu_aggr * sizeof(uint32_t); + if (len <= dst_len && len <= param_len && param_buf->rx_mpdu_aggr) { + rx_mpdu_aggr = (uint32_t *)result; + qdf_mem_copy(rx_mpdu_aggr, param_buf->rx_mpdu_aggr, len); + result += len; + dst_len -= len; + } else { + WMA_LOGE(FL("RX_MPDU_AGGR invalid arg %d, %d, %d"), + len, dst_len, param_len); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(uint32_t) * (fix_param->num_peer_ac_rx_stats * + WLAN_MAX_AC * rx_mcs_array_len); + param_len = param_buf->num_rx_mcs * sizeof(uint32_t); + if (len <= dst_len && len <= param_len && param_buf->rx_mcs) { + rx_mcs = (uint32_t *)result; + qdf_mem_copy(rx_mcs, param_buf->rx_mcs, len); + result += len; + dst_len -= len; + } else { + WMA_LOGE(FL("RX_MCS invalid arg %d, %d, %d"), + len, dst_len, param_len); + return QDF_STATUS_E_FAILURE; + } + + /* per peer rx stats */ + peer_stats = ll_stats->peer_stats; + if (!wmi_peer_rx || !wmi_rx || !peer_stats) { + WMA_LOGE(FL("Invalid arg, peer_rx %pK, wmi_rx %pK stats %pK"), + wmi_peer_rx, wmi_rx, peer_stats); + return QDF_STATUS_E_FAILURE; + } + for (i = 0; i < fix_param->num_peer_ac_rx_stats; i++) { + uint32_t peer_id = wmi_peer_rx[i].peer_id; + struct sir_wifi_rx *ac; + wmi_rx_stats *wmi_rx_stats; + + for (j = 0; j < ll_stats->peer_num; j++) { + peer_stats += j; + if ((peer_stats->peer_id == WIFI_INVALID_PEER_ID) || + (peer_stats->peer_id == peer_id)) + break; + } + + if (j < ll_stats->peer_num) { + peer_stats->peer_id = wmi_peer_rx[i].peer_id; + peer_stats->vdev_id = wmi_peer_rx[i].vdev_id; + peer_stats->sta_ps_inds = wmi_peer_rx[i].sta_ps_inds; + peer_stats->sta_ps_durs = wmi_peer_rx[i].sta_ps_durs; + peer_stats->rx_probe_reqs = + wmi_peer_rx[i].rx_probe_reqs; + peer_stats->rx_oth_mgmts = wmi_peer_rx[i].rx_oth_mgmts; + rx_stats = (struct sir_wifi_rx *)result; + + for (k = 0; k < WLAN_MAX_AC; k++) { + wmi_rx_stats = &wmi_rx[i * WLAN_MAX_AC + k]; + ac = &rx_stats[k]; + WMA_FILL_RX_STATS(wmi_rx_stats, ac); + ac->mpdu_aggr = rx_mpdu_aggr; + ac->aggr_len = rx_mpdu_aggr_array_len * + sizeof(uint32_t); + ac->mcs = rx_mcs; + ac->mcs_len = rx_mcs_array_len * + sizeof(uint32_t); + peer_stats->ac_stats[k].rx_stats = ac; + peer_stats->ac_stats[k].type = k; + rx_mpdu_aggr += rx_mpdu_aggr_array_len; + rx_mcs += rx_mcs_array_len; + } + result += WLAN_MAX_AC * sizeof(struct sir_wifi_rx); + } else { + /* + * Buffer for Peer RX counter overflow. + * There is peer ID mismatch between TX, RX, + * signal counters. + */ + WMA_LOGE(FL("One peer RX info is dropped.")); + rx_mpdu_aggr += rx_mpdu_aggr_array_len * WLAN_MAX_AC; + rx_mcs += rx_mcs_array_len * WLAN_MAX_AC; + } + } + *buf = result; + *buf_length = dst_len; + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_ll_stats_evt_handler() - handler for MAC layer counters. + * @handle - wma handle + * @event - FW event + * @len - length of FW event + * + * return: 0 success. + */ +static int wma_ll_stats_evt_handler(void *handle, u_int8_t *event, + u_int32_t len) +{ + WMI_REPORT_STATS_EVENTID_param_tlvs *param_buf; + wmi_report_stats_event_fixed_param *fixed_param; + tSirLLStatsResults *link_stats_results; + wmi_chan_cca_stats *wmi_cca_stats; + wmi_peer_signal_stats *wmi_peer_signal; + struct sir_wifi_ll_ext_stats *ll_stats; + struct sir_wifi_ll_ext_peer_stats *peer_stats; + struct sir_wifi_chan_cca_stats *cca_stats; + struct sir_wifi_peer_signal_stats *peer_signal; + uint8_t *result; + uint32_t i, peer_num, result_size, dst_len; + struct mac_context *mac; + struct scheduler_msg sme_msg = { 0 }; + QDF_STATUS qdf_status; + + mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + if (!mac) { + WMA_LOGD("%s: NULL mac ptr. Exiting", __func__); + return -EINVAL; + } + + if (!mac->sme.link_layer_stats_ext_cb) { + WMA_LOGD("%s: HDD callback is null", __func__); + return -EINVAL; + } + + WMA_LOGD("%s: Posting MAC counters event to HDD", __func__); + + param_buf = (WMI_REPORT_STATS_EVENTID_param_tlvs *)event; + if (!param_buf) { + WMA_LOGD("%s: param_buf is null", __func__); + return -EINVAL; + } + fixed_param = param_buf->fixed_param; + if (!fixed_param) { + WMA_LOGD("%s: fixed_param is null", __func__); + return -EINVAL; + } + wmi_cca_stats = param_buf->chan_cca_stats; + wmi_peer_signal = param_buf->peer_signal_stats; + if (fixed_param->num_peer_signal_stats > + param_buf->num_peer_signal_stats || + fixed_param->num_peer_ac_tx_stats > + param_buf->num_peer_ac_tx_stats || + fixed_param->num_peer_ac_rx_stats > + param_buf->num_peer_ac_rx_stats) { + WMA_LOGE("%s: excess num_peer_signal_stats:%d, num_peer_ac_tx_stats:%d, num_peer_ac_rx_stats:%d", + __func__, fixed_param->num_peer_signal_stats, + fixed_param->num_peer_ac_tx_stats, + fixed_param->num_peer_ac_rx_stats); + return -EINVAL; + } + + /* Get the MAX of three peer numbers */ + peer_num = fixed_param->num_peer_signal_stats > + fixed_param->num_peer_ac_tx_stats ? + fixed_param->num_peer_signal_stats : + fixed_param->num_peer_ac_tx_stats; + peer_num = peer_num > fixed_param->num_peer_ac_rx_stats ? + peer_num : fixed_param->num_peer_ac_rx_stats; + + if (peer_num == 0) + return -EINVAL; + + link_stats_results = wma_get_ll_stats_ext_buf(&result_size, + peer_num, + fixed_param); + if (!link_stats_results) { + WMA_LOGE("%s: Fail to allocate stats buffer", __func__); + return -EINVAL; + } + link_stats_results->paramId = WMI_LL_STATS_EXT_MAC_COUNTER; + link_stats_results->num_peers = peer_num; + link_stats_results->peer_event_number = 1; + link_stats_results->moreResultToFollow = 0; + + ll_stats = (struct sir_wifi_ll_ext_stats *)link_stats_results->results; + ll_stats->trigger_cond_id = fixed_param->trigger_cond_id; + ll_stats->cca_chgd_bitmap = fixed_param->cca_chgd_bitmap; + ll_stats->sig_chgd_bitmap = fixed_param->sig_chgd_bitmap; + ll_stats->tx_chgd_bitmap = fixed_param->tx_chgd_bitmap; + ll_stats->rx_chgd_bitmap = fixed_param->rx_chgd_bitmap; + ll_stats->channel_num = fixed_param->num_chan_cca_stats; + ll_stats->peer_num = peer_num; + + result = (uint8_t *)ll_stats->stats; + if (!result) { + WMA_LOGE("%s: result is null", __func__); + qdf_mem_free(link_stats_results); + return -EINVAL; + } + peer_stats = (struct sir_wifi_ll_ext_peer_stats *)result; + ll_stats->peer_stats = peer_stats; + + for (i = 0; i < peer_num && peer_stats; i++) { + peer_stats[i].peer_id = WIFI_INVALID_PEER_ID; + peer_stats[i].vdev_id = WIFI_INVALID_VDEV_ID; + } + + /* Per peer signal */ + result_size -= sizeof(struct sir_wifi_ll_ext_stats); + dst_len = sizeof(struct sir_wifi_peer_signal_stats); + for (i = 0; + i < fixed_param->num_peer_signal_stats && + peer_stats && wmi_peer_signal; + i++) { + peer_stats[i].peer_id = wmi_peer_signal->peer_id; + peer_stats[i].vdev_id = wmi_peer_signal->vdev_id; + peer_signal = &peer_stats[i].peer_signal_stats; + + WMA_LOGD("%d antennas for peer %d", + wmi_peer_signal->num_chains_valid, + wmi_peer_signal->peer_id); + if (dst_len <= result_size && peer_signal) { + peer_signal->vdev_id = wmi_peer_signal->vdev_id; + peer_signal->peer_id = wmi_peer_signal->peer_id; + peer_signal->num_chain = + wmi_peer_signal->num_chains_valid; + qdf_mem_copy(peer_signal->per_ant_snr, + wmi_peer_signal->per_chain_snr, + sizeof(peer_signal->per_ant_snr)); + qdf_mem_copy(peer_signal->nf, + wmi_peer_signal->per_chain_nf, + sizeof(peer_signal->nf)); + qdf_mem_copy(peer_signal->per_ant_rx_mpdus, + wmi_peer_signal->per_antenna_rx_mpdus, + sizeof(peer_signal->per_ant_rx_mpdus)); + qdf_mem_copy(peer_signal->per_ant_tx_mpdus, + wmi_peer_signal->per_antenna_tx_mpdus, + sizeof(peer_signal->per_ant_tx_mpdus)); + result_size -= dst_len; + } else { + WMA_LOGE(FL("Invalid length of PEER signal.")); + } + wmi_peer_signal++; + } + + result += peer_num * sizeof(struct sir_wifi_ll_ext_peer_stats); + cca_stats = (struct sir_wifi_chan_cca_stats *)result; + ll_stats->cca = cca_stats; + dst_len = sizeof(*cca_stats); + for (i = 0; + i < ll_stats->channel_num && cca_stats && wmi_cca_stats; + i++) { + if (dst_len <= result_size) { + cca_stats->vdev_id = wmi_cca_stats->vdev_id; + cca_stats->idle_time = wmi_cca_stats->idle_time; + cca_stats->tx_time = wmi_cca_stats->tx_time; + cca_stats->rx_in_bss_time = + wmi_cca_stats->rx_in_bss_time; + cca_stats->rx_out_bss_time = + wmi_cca_stats->rx_out_bss_time; + cca_stats->rx_busy_time = wmi_cca_stats->rx_busy_time; + cca_stats->rx_in_bad_cond_time = + wmi_cca_stats->rx_in_bad_cond_time; + cca_stats->tx_in_bad_cond_time = + wmi_cca_stats->tx_in_bad_cond_time; + cca_stats->wlan_not_avail_time = + wmi_cca_stats->wlan_not_avail_time; + result_size -= dst_len; + } else { + WMA_LOGE(FL("Invalid length of CCA.")); + } + cca_stats++; + } + + result += i * sizeof(struct sir_wifi_chan_cca_stats); + qdf_status = wma_fill_tx_stats(ll_stats, fixed_param, param_buf, + &result, &result_size); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + qdf_status = wma_fill_rx_stats(ll_stats, fixed_param, param_buf, + &result, &result_size); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) { + sme_msg.type = eWMI_SME_LL_STATS_IND; + sme_msg.bodyptr = (void *)link_stats_results; + sme_msg.bodyval = 0; + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, + &sme_msg); + } + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + qdf_mem_free(link_stats_results); + return -EINVAL; + } + + return 0; +} + +/** + * wma_unified_link_peer_stats_event_handler() - peer stats event handler + * @handle: wma handle + * @cmd_param_info: data received with event from fw + * @len: length of data + * + * Return: 0 for success or error code + */ +static int wma_unified_link_peer_stats_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_PEER_LINK_STATS_EVENTID_param_tlvs *param_tlvs; + wmi_peer_stats_event_fixed_param *fixed_param; + wmi_peer_link_stats *peer_stats, *temp_peer_stats; + wmi_rate_stats *rate_stats; + tSirLLStatsResults *link_stats_results; + uint8_t *results, *t_peer_stats, *t_rate_stats; + uint32_t count, rate_cnt; + uint32_t total_num_rates = 0; + uint32_t next_res_offset, next_peer_offset, next_rate_offset; + size_t peer_info_size, peer_stats_size, rate_stats_size; + size_t link_stats_results_size; + bool excess_data = false; + uint32_t buf_len = 0; + struct cdp_peer_stats *dp_stats; + void *dp_soc = cds_get_context(QDF_MODULE_ID_SOC); + QDF_STATUS status; + uint8_t mcs_index; + + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGD("%s: NULL mac ptr. Exiting", __func__); + return -EINVAL; + } + + if (!mac->sme.link_layer_stats_cb) { + WMA_LOGD("%s: HDD callback is null", __func__); + return -EINVAL; + } + + param_tlvs = (WMI_PEER_LINK_STATS_EVENTID_param_tlvs *) cmd_param_info; + if (!param_tlvs) { + WMA_LOGA("%s: Invalid stats event", __func__); + return -EINVAL; + } + /* + * cmd_param_info contains + * wmi_peer_stats_event_fixed_param fixed_param; + * num_peers * size of(struct wmi_peer_link_stats) + * total_num_rates * size of(struct wmi_rate_stats) + * total_num_rates is the sum of the rates of all the peers. + */ + fixed_param = param_tlvs->fixed_param; + peer_stats = param_tlvs->peer_stats; + rate_stats = param_tlvs->peer_rate_stats; + + if (!fixed_param || !peer_stats || + (peer_stats->num_rates && !rate_stats)) { + WMA_LOGA("%s: Invalid param_tlvs for Peer Stats", __func__); + return -EINVAL; + } + + do { + if (fixed_param->num_peers > + WMI_SVC_MSG_MAX_SIZE/sizeof(wmi_peer_link_stats) || + fixed_param->num_peers > param_tlvs->num_peer_stats) { + excess_data = true; + break; + } else { + buf_len = fixed_param->num_peers * + sizeof(wmi_peer_link_stats); + } + temp_peer_stats = (wmi_peer_link_stats *) peer_stats; + for (count = 0; count < fixed_param->num_peers; count++) { + if (temp_peer_stats->num_rates > + WMI_SVC_MSG_MAX_SIZE / sizeof(wmi_rate_stats)) { + excess_data = true; + break; + } else { + total_num_rates += temp_peer_stats->num_rates; + if (total_num_rates > + WMI_SVC_MSG_MAX_SIZE / + sizeof(wmi_rate_stats) || total_num_rates > + param_tlvs->num_peer_rate_stats) { + excess_data = true; + break; + } + buf_len += temp_peer_stats->num_rates * + sizeof(wmi_rate_stats); + } + temp_peer_stats++; + } + } while (0); + + if (excess_data || + (buf_len > WMI_SVC_MSG_MAX_SIZE - sizeof(*fixed_param))) { + WMA_LOGE("excess wmi buffer: rates:%d, peers:%d", + peer_stats->num_rates, fixed_param->num_peers); + return -EINVAL; + } + + peer_stats_size = sizeof(struct wifi_peer_stat); + peer_info_size = sizeof(struct wifi_peer_info); + rate_stats_size = sizeof(struct wifi_rate_stat); + link_stats_results_size = + sizeof(*link_stats_results) + peer_stats_size + + (fixed_param->num_peers * peer_info_size) + + (total_num_rates * rate_stats_size); + + link_stats_results = qdf_mem_malloc(link_stats_results_size); + if (!link_stats_results) + return -ENOMEM; + + qdf_mem_zero(link_stats_results, link_stats_results_size); + + link_stats_results->paramId = WMI_LINK_STATS_ALL_PEER; + link_stats_results->rspId = fixed_param->request_id; + link_stats_results->ifaceId = 0; + link_stats_results->num_peers = fixed_param->num_peers; + link_stats_results->peer_event_number = fixed_param->peer_event_number; + link_stats_results->moreResultToFollow = fixed_param->more_data; + + qdf_mem_copy(link_stats_results->results, + &fixed_param->num_peers, peer_stats_size); + dp_stats = qdf_mem_malloc(sizeof(*dp_stats)); + if (!dp_stats) { + WMA_LOGE("dp stats allocation failed"); + qdf_mem_free(link_stats_results); + return -ENOMEM; + } + + results = (uint8_t *) link_stats_results->results; + t_peer_stats = (uint8_t *) peer_stats; + t_rate_stats = (uint8_t *) rate_stats; + next_res_offset = peer_stats_size; + next_peer_offset = WMI_TLV_HDR_SIZE; + next_rate_offset = WMI_TLV_HDR_SIZE; + for (rate_cnt = 0; rate_cnt < fixed_param->num_peers; rate_cnt++) { + qdf_mem_copy(results + next_res_offset, + t_peer_stats + next_peer_offset, peer_info_size); + next_res_offset += peer_info_size; + + status = cdp_host_get_peer_stats(dp_soc, + link_stats_results->ifaceId, + (uint8_t *)&peer_stats->peer_mac_address, + dp_stats); + + /* Copy rate stats associated with this peer */ + for (count = 0; count < peer_stats->num_rates; count++) { + mcs_index = RATE_STAT_GET_MCS_INDEX(rate_stats->rate); + if (QDF_IS_STATUS_SUCCESS(status)) { + if (rate_stats->rate && mcs_index < MAX_MCS) + rate_stats->rx_mpdu = + dp_stats->rx.rx_mpdu_cnt[mcs_index]; + else + rate_stats->rx_mpdu = 0; + } + rate_stats++; + + qdf_mem_copy(results + next_res_offset, + t_rate_stats + next_rate_offset, + rate_stats_size); + next_res_offset += rate_stats_size; + next_rate_offset += sizeof(*rate_stats); + } + next_peer_offset += sizeof(*peer_stats); + peer_stats++; + } + + qdf_mem_free(dp_stats); + /* call hdd callback with Link Layer Statistics + * vdev_id/ifacId in link_stats_results will be + * used to retrieve the correct HDD context + */ + mac->sme.link_layer_stats_cb(mac->hdd_handle, + WMA_LINK_LAYER_STATS_RESULTS_RSP, + link_stats_results, + mac->sme.ll_stats_context); + qdf_mem_free(link_stats_results); + + return 0; +} + +/** + * wma_unified_link_stats_results_mem_free() - Free link stats results memory + * #link_stats_results: pointer to link stats result + * + * Return: 0 on success, error number otherwise. + */ +void wma_unified_link_stats_results_mem_free( + tSirLLStatsResults *link_stats_results) +{ + struct wifi_radio_stats *rs_results; + uint32_t i = 0; + + if (!link_stats_results) + return; + + rs_results = (struct wifi_radio_stats *) + &link_stats_results->results[0]; + for (i = 0; i < link_stats_results->num_radio; i++) { + if (rs_results->tx_time_per_power_level) { + qdf_mem_free(rs_results->tx_time_per_power_level); + rs_results->tx_time_per_power_level = NULL; + } + + if (rs_results->channels) { + qdf_mem_free(rs_results->channels); + rs_results->channels = NULL; + } + rs_results++; + } +} + +/** + * wma_unified_radio_tx_mem_free() - Free radio tx power stats memory + * @handle: WMI handle + * + * Return: 0 on success, error number otherwise. + */ +int wma_unified_radio_tx_mem_free(void *handle) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + + if (!wma_handle->link_stats_results) + return 0; + + wma_unified_link_stats_results_mem_free(wma_handle->link_stats_results); + + qdf_mem_free(wma_handle->link_stats_results); + wma_handle->link_stats_results = NULL; + + return 0; +} + +/** + * wma_unified_radio_tx_power_level_stats_event_handler() - tx power level stats + * @handle: WMI handle + * @cmd_param_info: command param info + * @len: Length of @cmd_param_info + * + * This is the WMI event handler function to receive radio stats tx + * power level stats. + * + * Return: 0 on success, error number otherwise. + */ +static int wma_unified_radio_tx_power_level_stats_event_handler(void *handle, + u_int8_t *cmd_param_info, u_int32_t len) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID_param_tlvs *param_tlvs; + wmi_tx_power_level_stats_evt_fixed_param *fixed_param; + uint8_t *tx_power_level_values; + tSirLLStatsResults *link_stats_results; + struct wifi_radio_stats *rs_results; + uint32_t max_total_num_tx_power_levels = MAX_TPC_LEVELS * NUM_OF_BANDS * + MAX_SPATIAL_STREAM_ANY_V3; + + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGD("%s: NULL mac ptr. Exiting", __func__); + return -EINVAL; + } + + if (!mac->sme.link_layer_stats_cb) { + WMA_LOGD("%s: HDD callback is null", __func__); + return -EINVAL; + } + + param_tlvs = (WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_tlvs) { + WMA_LOGA("%s: Invalid tx power level stats event", __func__); + return -EINVAL; + } + + fixed_param = param_tlvs->fixed_param; + if (!fixed_param) { + WMA_LOGA("%s:Invalid param_tlvs for Radio tx_power level Stats", + __func__); + return -EINVAL; + } + + link_stats_results = wma_handle->link_stats_results; + if (!link_stats_results) { + WMA_LOGA("%s: link_stats_results is NULL", __func__); + return -EINVAL; + } + + if (fixed_param->num_tx_power_levels > ((WMI_SVC_MSG_MAX_SIZE - + sizeof(*fixed_param)) / sizeof(uint32_t)) || + fixed_param->num_tx_power_levels > + param_tlvs->num_tx_time_per_power_level) { + WMA_LOGE("%s: excess tx_power buffers:%d, num_tx_time_per_power_level:%d", + __func__, fixed_param->num_tx_power_levels, + param_tlvs->num_tx_time_per_power_level); + return -EINVAL; + } + + if (fixed_param->radio_id >= link_stats_results->num_radio) { + WMA_LOGE("%s: Invalid radio_id %d num_radio %d", + __func__, fixed_param->radio_id, + link_stats_results->num_radio); + return -EINVAL; + } + + if (fixed_param->total_num_tx_power_levels > + max_total_num_tx_power_levels) { + WMA_LOGD("Invalid total_num_tx_power_levels %d", + fixed_param->total_num_tx_power_levels); + return -EINVAL; + } + + rs_results = (struct wifi_radio_stats *) &link_stats_results->results[0] + + fixed_param->radio_id; + tx_power_level_values = (uint8_t *) param_tlvs->tx_time_per_power_level; + + if (rs_results->total_num_tx_power_levels && + fixed_param->total_num_tx_power_levels > + rs_results->total_num_tx_power_levels) { + WMA_LOGE("%s: excess tx_power buffers:%d, total_num_tx_power_levels:%d", + __func__, fixed_param->total_num_tx_power_levels, + rs_results->total_num_tx_power_levels); + return -EINVAL; + } + + rs_results->total_num_tx_power_levels = + fixed_param->total_num_tx_power_levels; + if (!rs_results->total_num_tx_power_levels) { + link_stats_results->nr_received++; + goto post_stats; + } + + if ((fixed_param->power_level_offset > + rs_results->total_num_tx_power_levels) || + (fixed_param->num_tx_power_levels > + rs_results->total_num_tx_power_levels - + fixed_param->power_level_offset)) { + WMA_LOGE("%s: Invalid offset %d total_num %d num %d", + __func__, fixed_param->power_level_offset, + rs_results->total_num_tx_power_levels, + fixed_param->num_tx_power_levels); + return -EINVAL; + } + + if (!rs_results->tx_time_per_power_level) { + rs_results->tx_time_per_power_level = qdf_mem_malloc( + sizeof(uint32_t) * + rs_results->total_num_tx_power_levels); + if (!rs_results->tx_time_per_power_level) { + /* In error case, atleast send the radio stats without + * tx_power_level stats */ + rs_results->total_num_tx_power_levels = 0; + link_stats_results->nr_received++; + goto post_stats; + } + } + qdf_mem_copy(&rs_results->tx_time_per_power_level[ + fixed_param->power_level_offset], + tx_power_level_values, + sizeof(uint32_t) * fixed_param->num_tx_power_levels); + if (rs_results->total_num_tx_power_levels == + (fixed_param->num_tx_power_levels + + fixed_param->power_level_offset)) { + link_stats_results->moreResultToFollow = 0; + link_stats_results->nr_received++; + } + WMA_LOGD("num tx pwr lvls %u num tx pwr lvls %u pwr lvl offset %u radio_id %u moretofollow: %u nr_received: %u", + fixed_param->total_num_tx_power_levels, + fixed_param->num_tx_power_levels, + fixed_param->power_level_offset, fixed_param->radio_id, + link_stats_results->moreResultToFollow, + link_stats_results->nr_received); + + /* If still data to receive, return from here */ + if (link_stats_results->moreResultToFollow) + return 0; + +post_stats: + if (link_stats_results->num_radio != link_stats_results->nr_received) { + /* Not received all radio stats yet, don't post yet */ + return 0; + } + + /* call hdd callback with Link Layer Statistics + * vdev_id/ifacId in link_stats_results will be + * used to retrieve the correct HDD context + */ + mac->sme.link_layer_stats_cb(mac->hdd_handle, + WMA_LINK_LAYER_STATS_RESULTS_RSP, + link_stats_results, + mac->sme.ll_stats_context); + wma_unified_radio_tx_mem_free(handle); + + return 0; +} + +/** + * wma_unified_link_radio_stats_event_handler() - radio link stats event handler + * @handle: wma handle + * @cmd_param_info: data received with event from fw + * @len: length of data + * + * Return: 0 for success or error code + */ +static int wma_unified_link_radio_stats_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + WMI_RADIO_LINK_STATS_EVENTID_param_tlvs *param_tlvs; + wmi_radio_link_stats_event_fixed_param *fixed_param; + wmi_radio_link_stats *radio_stats; + wmi_channel_stats *channel_stats; + tSirLLStatsResults *link_stats_results; + uint8_t *results, *t_radio_stats, *t_channel_stats; + uint32_t next_chan_offset, count; + size_t radio_stats_size, chan_stats_size; + size_t link_stats_results_size; + struct wifi_radio_stats *rs_results; + struct wifi_channel_stats *chn_results; + + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGD("%s: NULL mac ptr. Exiting", __func__); + return -EINVAL; + } + + if (!mac->sme.link_layer_stats_cb) { + WMA_LOGD("%s: HDD callback is null", __func__); + return -EINVAL; + } + + param_tlvs = (WMI_RADIO_LINK_STATS_EVENTID_param_tlvs *) cmd_param_info; + if (!param_tlvs) { + WMA_LOGA("%s: Invalid stats event", __func__); + return -EINVAL; + } + + /* + * cmd_param_info contains + * wmi_radio_link_stats_event_fixed_param fixed_param; + * size of(struct wmi_radio_link_stats); + * num_channels * size of(struct wmi_channel_stats) + */ + fixed_param = param_tlvs->fixed_param; + if (fixed_param && !fixed_param->num_radio && + !fixed_param->more_radio_events) { + WMA_LOGD("FW indicates dummy link radio stats"); + if (!wma_handle->link_stats_results) { + wma_handle->link_stats_results = qdf_mem_malloc( + sizeof(*link_stats_results)); + if (!wma_handle->link_stats_results) + return -ENOMEM; + } + + /* + * Free the already allocated memory, if any, before setting + * the num_radio to 0 + */ + wma_unified_link_stats_results_mem_free( + wma_handle->link_stats_results); + + link_stats_results = wma_handle->link_stats_results; + link_stats_results->num_radio = fixed_param->num_radio; + goto link_radio_stats_cb; + } + + radio_stats = param_tlvs->radio_stats; + channel_stats = param_tlvs->channel_stats; + + if (!fixed_param || !radio_stats || + (radio_stats->num_channels && !channel_stats)) { + WMA_LOGA("%s: Invalid param_tlvs for Radio Stats", __func__); + return -EINVAL; + } + if (radio_stats->num_channels > + (NUM_24GHZ_CHANNELS + NUM_5GHZ_CHANNELS) || + radio_stats->num_channels > param_tlvs->num_channel_stats) { + WMA_LOGE("%s: Too many channels %d", + __func__, radio_stats->num_channels); + return -EINVAL; + } + + radio_stats_size = sizeof(struct wifi_radio_stats); + chan_stats_size = sizeof(struct wifi_channel_stats); + if (fixed_param->num_radio > + (UINT_MAX - sizeof(*link_stats_results))/radio_stats_size) { + WMA_LOGE("excess num_radio %d is leading to int overflow", + fixed_param->num_radio); + return -EINVAL; + } + link_stats_results_size = sizeof(*link_stats_results) + + fixed_param->num_radio * radio_stats_size; + + if (radio_stats->radio_id >= fixed_param->num_radio) { + WMA_LOGE("%s, invalid radio id:%d, num radio:%d", + __func__, radio_stats->radio_id, + fixed_param->num_radio); + return -EINVAL; + } + + if (!wma_handle->link_stats_results) { + wma_handle->link_stats_results = qdf_mem_malloc( + link_stats_results_size); + if (!wma_handle->link_stats_results) + return -ENOMEM; + } + link_stats_results = wma_handle->link_stats_results; + if (link_stats_results->num_radio == 0) { + link_stats_results->num_radio = fixed_param->num_radio; + } else if (link_stats_results->num_radio < fixed_param->num_radio) { + /* + * The link stats results size allocated based on num_radio of + * first event must be same as following events. Otherwise these + * events may be spoofed. Drop all of them and report error. + */ + WMA_LOGE("Invalid following WMI_RADIO_LINK_STATS_EVENTID. Discarding this set"); + wma_unified_radio_tx_mem_free(handle); + return -EINVAL; + } + + WMA_LOGD("Radio stats Fixed Param: req_id: %u num_radio: %u more_radio_events: %u", + fixed_param->request_id, fixed_param->num_radio, + fixed_param->more_radio_events); + + results = (uint8_t *) link_stats_results->results; + t_radio_stats = (uint8_t *) radio_stats; + t_channel_stats = (uint8_t *) channel_stats; + + rs_results = (struct wifi_radio_stats *) &results[0] + radio_stats->radio_id; + rs_results->radio = radio_stats->radio_id; + rs_results->on_time = radio_stats->on_time; + rs_results->tx_time = radio_stats->tx_time; + rs_results->rx_time = radio_stats->rx_time; + rs_results->on_time_scan = radio_stats->on_time_scan; + rs_results->on_time_nbd = radio_stats->on_time_nbd; + rs_results->on_time_gscan = radio_stats->on_time_gscan; + rs_results->on_time_roam_scan = radio_stats->on_time_roam_scan; + rs_results->on_time_pno_scan = radio_stats->on_time_pno_scan; + rs_results->on_time_hs20 = radio_stats->on_time_hs20; + rs_results->total_num_tx_power_levels = 0; + if (rs_results->tx_time_per_power_level) { + qdf_mem_free(rs_results->tx_time_per_power_level); + rs_results->tx_time_per_power_level = NULL; + } + if (rs_results->channels) { + qdf_mem_free(rs_results->channels); + rs_results->channels = NULL; + } + rs_results->num_channels = radio_stats->num_channels; + rs_results->on_time_host_scan = radio_stats->on_time_host_scan; + rs_results->on_time_lpi_scan = radio_stats->on_time_lpi_scan; + if (rs_results->num_channels) { + rs_results->channels = qdf_mem_malloc( + radio_stats->num_channels * + chan_stats_size); + if (!rs_results->channels) { + wma_unified_radio_tx_mem_free(handle); + return -ENOMEM; + } + + chn_results = (struct wifi_channel_stats *) &rs_results->channels[0]; + next_chan_offset = WMI_TLV_HDR_SIZE; + WMA_LOGD("Channel Stats Info"); + for (count = 0; count < radio_stats->num_channels; count++) { + WMA_LOGD("freq %u width %u freq0 %u freq1 %u awake time %u cca busy time %u", + channel_stats->center_freq, + channel_stats->channel_width, + channel_stats->center_freq0, + channel_stats->center_freq1, + channel_stats->radio_awake_time, + channel_stats->cca_busy_time); + channel_stats++; + + qdf_mem_copy(chn_results, + t_channel_stats + next_chan_offset, + chan_stats_size); + chn_results++; + next_chan_offset += sizeof(*channel_stats); + } + } + +link_radio_stats_cb: + link_stats_results->paramId = WMI_LINK_STATS_RADIO; + link_stats_results->rspId = fixed_param->request_id; + link_stats_results->ifaceId = 0; + link_stats_results->peer_event_number = 0; + + /* + * Backward compatibility: + * There are firmware(s) which will send Radio stats only with + * more_radio_events set to 0 and firmware which sends Radio stats + * followed by tx_power level stats with more_radio_events set to 1. + * if more_radio_events is set to 1, buffer the radio stats and + * wait for tx_power_level stats. + */ + link_stats_results->moreResultToFollow = fixed_param->more_radio_events; + + if (link_stats_results->moreResultToFollow) { + /* More results coming, don't post yet */ + return 0; + } + if (link_stats_results->num_radio) { + link_stats_results->nr_received++; + + if (link_stats_results->num_radio != + link_stats_results->nr_received) { + /* Not received all radio stats yet, don't post yet */ + return 0; + } + } + + mac->sme.link_layer_stats_cb(mac->hdd_handle, + WMA_LINK_LAYER_STATS_RESULTS_RSP, + link_stats_results, + mac->sme.ll_stats_context); + wma_unified_radio_tx_mem_free(handle); + + return 0; +} + +#ifdef WLAN_PEER_PS_NOTIFICATION +/** + * wma_peer_ps_evt_handler() - handler for PEER power state change. + * @handle: wma handle + * @event: FW event + * @len: length of FW event + * + * Once peer STA power state changes, an event will be indicated by + * FW. This function send a link layer state change msg to HDD. HDD + * link layer callback will converts the event to NL msg. + * + * Return: 0 Success. Others fail. + */ +static int wma_peer_ps_evt_handler(void *handle, u_int8_t *event, + u_int32_t len) +{ + WMI_PEER_STA_PS_STATECHG_EVENTID_param_tlvs *param_buf; + wmi_peer_sta_ps_statechange_event_fixed_param *fixed_param; + struct wifi_peer_stat *peer_stat; + struct wifi_peer_info *peer_info; + tSirLLStatsResults *link_stats_results; + tSirMacAddr mac_address; + uint32_t result_len; + cds_msg_t sme_msg = { 0 }; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGD("%s: NULL mac ptr. Exiting", __func__); + return -EINVAL; + } + + if (!mac->sme.link_layer_stats_ext_cb) { + WMA_LOGD("%s: HDD callback is null", __func__); + return -EINVAL; + } + + WMA_LOGD("%s: Posting Peer Stats PS event to HDD", __func__); + + param_buf = (WMI_PEER_STA_PS_STATECHG_EVENTID_param_tlvs *)event; + fixed_param = param_buf->fixed_param; + + result_len = sizeof(tSirLLStatsResults) + + sizeof(struct wifi_peer_stat) + + sizeof(struct wifi_peer_info); + link_stats_results = qdf_mem_malloc(result_len); + if (!link_stats_results) + return -EINVAL; + + WMI_MAC_ADDR_TO_CHAR_ARRAY(&fixed_param->peer_macaddr, &mac_address[0]); + WMA_LOGD("Peer power state change event from FW"); + WMA_LOGD("Fixed Param:"); + WMA_LOGD("MAC address: %2x:%2x:%2x:%2x:%2x:%2x, Power state: %d", + mac_address[0], mac_address[1], mac_address[2], + mac_address[3], mac_address[4], mac_address[5], + fixed_param->peer_ps_state); + + link_stats_results->paramId = WMI_LL_STATS_EXT_PS_CHG; + link_stats_results->num_peers = 1; + link_stats_results->peer_event_number = 1; + link_stats_results->moreResultToFollow = 0; + + peer_stat = (struct wifi_peer_stat *)link_stats_results->results; + peer_stat->numPeers = 1; + peer_info = (struct wifi_peer_info *)peer_stat->peer_info; + qdf_mem_copy(&peer_info->peer_macaddr, + &mac_address, + sizeof(tSirMacAddr)); + peer_info->power_saving = fixed_param->peer_ps_state; + + sme_msg.type = eWMI_SME_LL_STATS_IND; + sme_msg.bodyptr = link_stats_results; + sme_msg.bodyval = 0; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("%s: Fail to post ps change ind msg", __func__); + qdf_mem_free(link_stats_results); + } + + return 0; +} +#else +/** + * wma_peer_ps_evt_handler() - handler for PEER power state change. + * @handle: wma handle + * @event: FW event + * @len: length of FW event + * + * Once peer STA power state changes, an event will be indicated by + * FW. This function send a link layer state change msg to HDD. HDD + * link layer callback will converts the event to NL msg. + * + * Return: 0 Success. Others fail. + */ +static inline int wma_peer_ps_evt_handler(void *handle, u_int8_t *event, + u_int32_t len) +{ + return 0; +} +#endif + +/** + * wma_register_ll_stats_event_handler() - register link layer stats related + * event handler + * @wma_handle: wma handle + * + * Return: none + */ +void wma_register_ll_stats_event_handler(tp_wma_handle wma_handle) +{ + if (!wma_handle) { + WMA_LOGE("%s: wma_handle is NULL", __func__); + return; + } + + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_iface_link_stats_event_id, + wma_unified_link_iface_stats_event_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_peer_link_stats_event_id, + wma_unified_link_peer_stats_event_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_radio_link_stats_link, + wma_unified_link_radio_stats_event_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_radio_tx_power_level_stats_event_id, + wma_unified_radio_tx_power_level_stats_event_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_peer_sta_ps_statechg_event_id, + wma_peer_ps_evt_handler, + WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + wmi_report_stats_event_id, + wma_ll_stats_evt_handler, + WMA_RX_SERIALIZER_CTX); + +} + +QDF_STATUS wma_process_ll_stats_clear_req(tp_wma_handle wma, + const tpSirLLStatsClearReq clearReq) +{ + uint8_t *addr; + struct ll_stats_clear_params cmd = {0}; + int ret; + struct wlan_objmgr_vdev *vdev; + + if (!clearReq || !wma) { + WMA_LOGE("%s: input pointer is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + vdev = wma->interfaces[clearReq->staId].vdev; + if (!vdev) { + WMA_LOGE("%s: vdev is NULL for vdev_%d", + __func__, clearReq->staId); + return QDF_STATUS_E_FAILURE; + } + + cmd.stop_req = clearReq->stopReq; + cmd.vdev_id = clearReq->staId; + cmd.stats_clear_mask = clearReq->statsClearReqMask; + + vdev = wma->interfaces[clearReq->staId].vdev; + if (!vdev) { + WMA_LOGE("%s: Failed to get vdev for vdev_%d", + __func__, clearReq->staId); + return QDF_STATUS_E_FAILURE; + } + addr = wlan_vdev_mlme_get_macaddr(vdev); + if (!addr) { + WMA_LOGE("%s: Failed to get macaddr for vdev_%d", + __func__, clearReq->staId); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(cmd.peer_macaddr.bytes, addr, QDF_MAC_ADDR_SIZE); + ret = wmi_unified_process_ll_stats_clear_cmd(wma->wmi_handle, &cmd); + if (ret) { + WMA_LOGE("%s: Failed to send clear link stats req", __func__); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_process_ll_stats_set_req() - link layer stats set request + * @wma: wma handle + * @setReq: ll stats set request command params + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_process_ll_stats_set_req(tp_wma_handle wma, + const tpSirLLStatsSetReq setReq) +{ + struct ll_stats_set_params cmd = {0}; + int ret; + + if (!setReq || !wma) { + WMA_LOGE("%s: input pointer is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + cmd.mpdu_size_threshold = setReq->mpduSizeThreshold; + cmd.aggressive_statistics_gathering = + setReq->aggressiveStatisticsGathering; + + ret = wmi_unified_process_ll_stats_set_cmd(wma->wmi_handle, + &cmd); + if (ret) { + WMA_LOGE("%s: Failed to send set link stats request", __func__); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_process_ll_stats_get_req(tp_wma_handle wma, + const tpSirLLStatsGetReq getReq) +{ + struct wlan_objmgr_vdev *vdev; + uint8_t *addr; + struct ll_stats_get_params cmd = {0}; + int ret; + + if (!getReq || !wma) { + WMA_LOGE("%s: input pointer is NULL", __func__); + return QDF_STATUS_E_FAILURE; + } + + if (!wma_is_vdev_valid(getReq->staId)) { + WMA_LOGE("%s: vdev:%d not created yet", __func__, + getReq->staId); + return QDF_STATUS_E_FAILURE; + } + + cmd.req_id = getReq->reqId; + cmd.param_id_mask = getReq->paramIdMask; + cmd.vdev_id = getReq->staId; + + vdev = wma->interfaces[getReq->staId].vdev; + if (!vdev) { + WMA_LOGE("%s: Failed to get vdev for vdev_%d", + __func__, getReq->staId); + return QDF_STATUS_E_FAILURE; + } + addr = wlan_vdev_mlme_get_macaddr(vdev); + if (!addr) { + WMA_LOGE("%s: Failed to get macaddr for vdev_%d", + __func__, getReq->staId); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_copy(cmd.peer_macaddr.bytes, addr, QDF_MAC_ADDR_SIZE); + ret = wmi_unified_process_ll_stats_get_cmd(wma->wmi_handle, &cmd); + if (ret) { + WMA_LOGE("%s: Failed to send get link stats request", __func__); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_unified_link_iface_stats_event_handler() - link iface stats event handler + * @wma:wma handle + * @cmd_param_info: data from event + * @len: length + * + * Return: 0 for success or error code + */ +int wma_unified_link_iface_stats_event_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + tp_wma_handle wma_handle = (tp_wma_handle)handle; + WMI_IFACE_LINK_STATS_EVENTID_param_tlvs *param_tlvs; + wmi_iface_link_stats_event_fixed_param *fixed_param; + wmi_iface_link_stats *link_stats, *iface_link_stats; + wmi_wmm_ac_stats *ac_stats, *iface_ac_stats; + wmi_iface_offload_stats *offload_stats, *iface_offload_stats; + tSirLLStatsResults *link_stats_results; + struct wifi_interface_stats *iface_stat; + uint32_t count; + size_t link_stats_size, ac_stats_size, iface_info_size; + size_t link_stats_results_size, offload_stats_size; + size_t total_ac_size, total_offload_size; + bool db2dbm_enabled; + + struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGD("%s: NULL mac ptr. Exiting", __func__); + return -EINVAL; + } + + if (!mac->sme.link_layer_stats_cb) { + WMA_LOGD("%s: HDD callback is null", __func__); + return -EINVAL; + } + + param_tlvs = (WMI_IFACE_LINK_STATS_EVENTID_param_tlvs *) cmd_param_info; + if (!param_tlvs) { + WMA_LOGA("%s: Invalid stats event", __func__); + return -EINVAL; + } + + /* + * cmd_param_info contains + * wmi_iface_link_stats_event_fixed_param fixed_param; + * wmi_iface_link_stats iface_link_stats; + * iface_link_stats->num_ac * size of(struct wmi_wmm_ac_stats) + * fixed_param->num_offload_stats * size of(wmi_iface_offload_stats); + */ + fixed_param = param_tlvs->fixed_param; + link_stats = param_tlvs->iface_link_stats; + ac_stats = param_tlvs->ac; + offload_stats = param_tlvs->iface_offload_stats; + + if (!fixed_param || !link_stats || (link_stats->num_ac && !ac_stats) || + (fixed_param->num_offload_stats && !offload_stats)) { + WMA_LOGA("%s: Invalid param_tlvs for Iface Stats", __func__); + return -EINVAL; + } + if (link_stats->num_ac > WIFI_AC_MAX || link_stats->num_ac > + param_tlvs->num_ac) { + WMA_LOGE("%s: Excess data received from firmware num_ac %d, param_tlvs->num_ac %d", + __func__, link_stats->num_ac, param_tlvs->num_ac); + return -EINVAL; + } + if (fixed_param->num_offload_stats > WMI_OFFLOAD_STATS_TYPE_MAX || + fixed_param->num_offload_stats > + param_tlvs->num_iface_offload_stats) { + WMA_LOGE("%s: Excess num offload stats recvd from fw: %d, um_iface_offload_stats: %d", + __func__, fixed_param->num_offload_stats, + param_tlvs->num_iface_offload_stats); + return -EINVAL; + } + + link_stats_size = sizeof(struct wifi_interface_stats); + iface_info_size = sizeof(struct wifi_interface_info); + + ac_stats_size = sizeof(wmi_wmm_ac_stats); + offload_stats_size = sizeof(wmi_iface_offload_stats); + + total_ac_size = ac_stats_size * WIFI_AC_MAX; + total_offload_size = offload_stats_size * WMI_OFFLOAD_STATS_TYPE_MAX + + member_size(struct wifi_interface_stats, + num_offload_stats); + + link_stats_results_size = sizeof(*link_stats_results) + link_stats_size; + + link_stats_results = qdf_mem_malloc(link_stats_results_size); + if (!link_stats_results) + return -ENOMEM; + + qdf_mem_zero(link_stats_results, link_stats_results_size); + + link_stats_results->paramId = WMI_LINK_STATS_IFACE; + link_stats_results->rspId = fixed_param->request_id; + link_stats_results->ifaceId = fixed_param->vdev_id; + link_stats_results->num_peers = link_stats->num_peers; + link_stats_results->peer_event_number = 0; + link_stats_results->moreResultToFollow = 0; + + /* results is copied to struct wifi_interface_stats in upper layer + * struct wifi_interface_stats + * - struct wifi_interface_info (all fields except roaming is + * filled by host in the upper layer) + * - various members of struct wifi_interface_stats (from + * wmi_iface_link_stats) + * - ACs information (from wmi_wmm_ac_stats) + * - num_offload_stats (from fixed param) + * - offload stats (from wmi_iface_offload_stats) + */ + + iface_stat = (struct wifi_interface_stats *)link_stats_results->results; + + iface_link_stats = &iface_stat->link_stats; + *iface_link_stats = *link_stats; + db2dbm_enabled = wmi_service_enabled(wma_handle->wmi_handle, + wmi_service_hw_db2dbm_support); + if (!db2dbm_enabled) { + /* FW doesn't indicate support for HW db2dbm conversion */ + iface_link_stats->rssi_mgmt += WMA_TGT_NOISE_FLOOR_DBM; + iface_link_stats->rssi_data += WMA_TGT_NOISE_FLOOR_DBM; + iface_link_stats->rssi_ack += WMA_TGT_NOISE_FLOOR_DBM; + } + WMA_LOGD("db2dbm: %d, rssi_mgmt: %d, rssi_data: %d, rssi_ack: %d", + db2dbm_enabled, iface_link_stats->rssi_mgmt, + iface_link_stats->rssi_data, iface_link_stats->rssi_ack); + + /* Copy roaming state */ + iface_stat->info.roaming = link_stats->roam_state; + + iface_ac_stats = &iface_stat->ac_stats[0]; + for (count = 0; count < link_stats->num_ac; count++) { + *iface_ac_stats = *ac_stats; + ac_stats++; + iface_ac_stats++; + } + + /* Copy wmi_iface_offload_stats to wifi_iface_offload_stat */ + iface_stat->num_offload_stats = fixed_param->num_offload_stats; + iface_offload_stats = &iface_stat->offload_stats[0]; + for (count = 0; count < fixed_param->num_offload_stats; count++) { + *iface_offload_stats = *offload_stats; + offload_stats++; + iface_offload_stats++; + } + + /* call hdd callback with Link Layer Statistics + * vdev_id/ifacId in link_stats_results will be + * used to retrieve the correct HDD context + */ + mac->sme.link_layer_stats_cb(mac->hdd_handle, + WMA_LINK_LAYER_STATS_RESULTS_RSP, + link_stats_results, + mac->sme.ll_stats_context); + qdf_mem_free(link_stats_results); + + return 0; +} + +/** + * wma_config_stats_ext_threshold - set threthold for MAC counters + * @wma: wma handler + * @threshold: threhold for MAC counters + * + * For each MAC layer counter, FW holds two copies. One is the current value. + * The other is the last report. Once a current counter's increment is larger + * than the threshold, FW will indicate that counter to host even if the + * monitoring timer does not expire. + * + * Return: None + */ +void wma_config_stats_ext_threshold(tp_wma_handle wma, + struct sir_ll_ext_stats_threshold *thresh) +{ + QDF_STATUS status; + uint32_t len, tag, hdr_len; + uint8_t *buf_ptr; + wmi_buf_t buf; + wmi_pdev_set_stats_threshold_cmd_fixed_param *cmd; + wmi_chan_cca_stats_thresh *cca; + wmi_peer_signal_stats_thresh *signal; + wmi_tx_stats_thresh *tx; + wmi_rx_stats_thresh *rx; + + if (!thresh) { + WMA_LOGE(FL("Invalid threshold input.")); + return; + } + + len = sizeof(wmi_pdev_set_stats_threshold_cmd_fixed_param) + + sizeof(wmi_chan_cca_stats_thresh) + + sizeof(wmi_peer_signal_stats_thresh) + + sizeof(wmi_tx_stats_thresh) + + sizeof(wmi_rx_stats_thresh) + + 5 * WMI_TLV_HDR_SIZE; + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) + return; + + buf_ptr = (u_int8_t *)wmi_buf_data(buf); + tag = WMITLV_TAG_STRUC_wmi_pdev_set_stats_threshold_cmd_fixed_param; + hdr_len = WMITLV_GET_STRUCT_TLVLEN( + wmi_pdev_set_stats_threshold_cmd_fixed_param); + WMA_LOGD(FL("Setting fixed parameters. tag=%d, len=%d"), tag, hdr_len); + cmd = (wmi_pdev_set_stats_threshold_cmd_fixed_param *)buf_ptr; + WMITLV_SET_HDR(&cmd->tlv_header, tag, hdr_len); + cmd->enable_thresh = thresh->enable; + cmd->use_thresh_bitmap = thresh->enable_bitmap; + cmd->gbl_thresh = thresh->global_threshold; + cmd->cca_thresh_enable_bitmap = thresh->cca_bitmap; + cmd->signal_thresh_enable_bitmap = thresh->signal_bitmap; + cmd->tx_thresh_enable_bitmap = thresh->tx_bitmap; + cmd->rx_thresh_enable_bitmap = thresh->rx_bitmap; + len = sizeof(wmi_pdev_set_stats_threshold_cmd_fixed_param); + + tag = WMITLV_TAG_STRUC_wmi_chan_cca_stats_thresh, + hdr_len = WMITLV_GET_STRUCT_TLVLEN(wmi_chan_cca_stats_thresh); + cca = (wmi_chan_cca_stats_thresh *)(buf_ptr + len); + WMITLV_SET_HDR(&cca->tlv_header, tag, hdr_len); + WMA_LOGD(FL("Setting cca parameters. tag=%d, len=%d"), tag, hdr_len); + cca->idle_time = thresh->cca.idle_time; + cca->tx_time = thresh->cca.tx_time; + cca->rx_in_bss_time = thresh->cca.rx_in_bss_time; + cca->rx_out_bss_time = thresh->cca.rx_out_bss_time; + cca->rx_busy_time = thresh->cca.rx_busy_time; + cca->rx_in_bad_cond_time = thresh->cca.rx_in_bad_cond_time; + cca->tx_in_bad_cond_time = thresh->cca.tx_in_bad_cond_time; + cca->wlan_not_avail_time = thresh->cca.wlan_not_avail_time; + WMA_LOGD(FL("idle time=%d, tx_time=%d, in_bss=%d, out_bss=%d"), + cca->idle_time, cca->tx_time, + cca->rx_in_bss_time, cca->rx_out_bss_time); + WMA_LOGD(FL("rx_busy=%d, rx_bad=%d, tx_bad=%d, not_avail=%d"), + cca->rx_busy_time, cca->rx_in_bad_cond_time, + cca->tx_in_bad_cond_time, cca->wlan_not_avail_time); + len += sizeof(wmi_chan_cca_stats_thresh); + + signal = (wmi_peer_signal_stats_thresh *)(buf_ptr + len); + tag = WMITLV_TAG_STRUC_wmi_peer_signal_stats_thresh; + hdr_len = WMITLV_GET_STRUCT_TLVLEN(wmi_peer_signal_stats_thresh); + WMA_LOGD(FL("Setting signal parameters. tag=%d, len=%d"), tag, hdr_len); + WMITLV_SET_HDR(&signal->tlv_header, tag, hdr_len); + signal->per_chain_snr = thresh->signal.snr; + signal->per_chain_nf = thresh->signal.nf; + WMA_LOGD(FL("snr=%d, nf=%d"), signal->per_chain_snr, + signal->per_chain_nf); + len += sizeof(wmi_peer_signal_stats_thresh); + + tx = (wmi_tx_stats_thresh *)(buf_ptr + len); + tag = WMITLV_TAG_STRUC_wmi_tx_stats_thresh; + hdr_len = WMITLV_GET_STRUCT_TLVLEN(wmi_tx_stats_thresh); + WMA_LOGD(FL("Setting TX parameters. tag=%d, len=%d"), tag, len); + WMITLV_SET_HDR(&tx->tlv_header, tag, hdr_len); + tx->tx_msdu_cnt = thresh->tx.msdu; + tx->tx_mpdu_cnt = thresh->tx.mpdu; + tx->tx_ppdu_cnt = thresh->tx.ppdu; + tx->tx_bytes = thresh->tx.bytes; + tx->tx_msdu_drop_cnt = thresh->tx.msdu_drop; + tx->tx_drop_bytes = thresh->tx.byte_drop; + tx->tx_mpdu_retry_cnt = thresh->tx.mpdu_retry; + tx->tx_mpdu_fail_cnt = thresh->tx.mpdu_fail; + tx->tx_ppdu_fail_cnt = thresh->tx.ppdu_fail; + tx->tx_mpdu_aggr = thresh->tx.aggregation; + tx->tx_succ_mcs = thresh->tx.succ_mcs; + tx->tx_fail_mcs = thresh->tx.fail_mcs; + tx->tx_ppdu_delay = thresh->tx.delay; + WMA_LOGD(FL("msdu=%d, mpdu=%d, ppdu=%d, bytes=%d, msdu_drop=%d"), + tx->tx_msdu_cnt, tx->tx_mpdu_cnt, tx->tx_ppdu_cnt, + tx->tx_bytes, tx->tx_msdu_drop_cnt); + WMA_LOGD(FL("byte_drop=%d, mpdu_retry=%d, mpdu_fail=%d, ppdu_fail=%d"), + tx->tx_drop_bytes, tx->tx_mpdu_retry_cnt, + tx->tx_mpdu_fail_cnt, tx->tx_ppdu_fail_cnt); + WMA_LOGD(FL("aggr=%d, succ_mcs=%d, fail_mcs=%d, delay=%d"), + tx->tx_mpdu_aggr, tx->tx_succ_mcs, tx->tx_fail_mcs, + tx->tx_ppdu_delay); + len += sizeof(wmi_tx_stats_thresh); + + rx = (wmi_rx_stats_thresh *)(buf_ptr + len); + tag = WMITLV_TAG_STRUC_wmi_rx_stats_thresh, + hdr_len = WMITLV_GET_STRUCT_TLVLEN(wmi_rx_stats_thresh); + WMITLV_SET_HDR(&rx->tlv_header, tag, hdr_len); + WMA_LOGD(FL("Setting RX parameters. tag=%d, len=%d"), tag, hdr_len); + rx->mac_rx_mpdu_cnt = thresh->rx.mpdu; + rx->mac_rx_bytes = thresh->rx.bytes; + rx->phy_rx_ppdu_cnt = thresh->rx.ppdu; + rx->phy_rx_bytes = thresh->rx.ppdu_bytes; + rx->rx_disorder_cnt = thresh->rx.disorder; + rx->rx_mpdu_retry_cnt = thresh->rx.mpdu_retry; + rx->rx_mpdu_dup_cnt = thresh->rx.mpdu_dup; + rx->rx_mpdu_discard_cnt = thresh->rx.mpdu_discard; + rx->rx_mpdu_aggr = thresh->rx.aggregation; + rx->rx_mcs = thresh->rx.mcs; + rx->sta_ps_inds = thresh->rx.ps_inds; + rx->sta_ps_durs = thresh->rx.ps_durs; + rx->rx_probe_reqs = thresh->rx.probe_reqs; + rx->rx_oth_mgmts = thresh->rx.other_mgmt; + WMA_LOGD(FL("rx_mpdu=%d, rx_bytes=%d, rx_ppdu=%d, rx_pbytes=%d"), + rx->mac_rx_mpdu_cnt, rx->mac_rx_bytes, + rx->phy_rx_ppdu_cnt, rx->phy_rx_bytes); + WMA_LOGD(FL("disorder=%d, rx_dup=%d, rx_aggr=%d, rx_mcs=%d"), + rx->rx_disorder_cnt, rx->rx_mpdu_dup_cnt, + rx->rx_mpdu_aggr, rx->rx_mcs); + WMA_LOGD(FL("rx_ind=%d, rx_dur=%d, rx_probe=%d, rx_mgmt=%d"), + rx->sta_ps_inds, rx->sta_ps_durs, + rx->rx_probe_reqs, rx->rx_oth_mgmts); + len += sizeof(wmi_rx_stats_thresh); + + WMA_LOGA("WMA --> WMI_PDEV_SET_STATS_THRESHOLD_CMDID(0x%x), length=%d", + WMI_PDEV_SET_STATS_THRESHOLD_CMDID, len); + status = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_PDEV_SET_STATS_THRESHOLD_CMDID); + if (QDF_IS_STATUS_ERROR(status)) + wmi_buf_free(buf); +} +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +/** + * wma_post_link_status() - post link status to SME + * @pGetLinkStatus: SME Link status + * @link_status: Link status + * + * Return: none + */ +void wma_post_link_status(tAniGetLinkStatus *pGetLinkStatus, + uint8_t link_status) +{ + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct scheduler_msg sme_msg = { 0 }; + + pGetLinkStatus->linkStatus = link_status; + sme_msg.type = eWNI_SME_LINK_STATUS_IND; + sme_msg.bodyptr = pGetLinkStatus; + sme_msg.bodyval = 0; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("%s: Fail to post link status ind msg", __func__); + qdf_mem_free(pGetLinkStatus); + } +} + +int wma_link_status_event_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_UPDATE_VDEV_RATE_STATS_EVENTID_param_tlvs *param_buf; + wmi_vdev_rate_stats_event_fixed_param *event; + wmi_vdev_rate_ht_info *ht_info; + struct wma_txrx_node *intr = wma->interfaces; + uint8_t link_status = LINK_STATUS_LEGACY; + uint32_t i, rate_flag; + QDF_STATUS status; + + param_buf = + (WMI_UPDATE_VDEV_RATE_STATS_EVENTID_param_tlvs *) cmd_param_info; + if (!param_buf) { + WMA_LOGA("%s: Invalid stats event", __func__); + return -EINVAL; + } + + event = (wmi_vdev_rate_stats_event_fixed_param *) + param_buf->fixed_param; + ht_info = (wmi_vdev_rate_ht_info *) param_buf->ht_info; + + if (!ht_info) { + wma_err("Invalid ht_info"); + return -EINVAL; + } + + WMA_LOGD("num_vdev_stats: %d", event->num_vdev_stats); + + if (event->num_vdev_stats > ((WMI_SVC_MSG_MAX_SIZE - + sizeof(*event)) / sizeof(*ht_info)) || + event->num_vdev_stats > param_buf->num_ht_info) { + WMA_LOGE("%s: excess vdev_stats buffers:%d, num_ht_info:%d", + __func__, event->num_vdev_stats, + param_buf->num_ht_info); + return -EINVAL; + } + + if (!wma_is_vdev_valid(ht_info->vdevid)) { + wma_err("Invalid vdevid %d", ht_info->vdevid); + return -EINVAL; + } + + if (!intr[ht_info->vdevid].vdev) { + wma_err("Vdev is NULL"); + return -EINVAL; + } + + status = wma_get_vdev_rate_flag(intr[ht_info->vdevid].vdev, &rate_flag); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to get rate flag", __func__); + return -EINVAL; + } + + for (i = 0; (i < event->num_vdev_stats) && ht_info; i++) { + WMA_LOGD("%s vdevId:%d tx_nss:%d rx_nss:%d tx_preamble:%d rx_preamble:%d", + __func__, ht_info->vdevid, ht_info->tx_nss, + ht_info->rx_nss, ht_info->tx_preamble, + ht_info->rx_preamble); + if (ht_info->vdevid < wma->max_bssid + && intr[ht_info->vdevid].plink_status_req) { + if (ht_info->tx_nss || ht_info->rx_nss) + link_status = LINK_STATUS_MIMO; + + if ((ht_info->tx_preamble == LINK_RATE_VHT) || + (ht_info->rx_preamble == LINK_RATE_VHT)) + link_status |= LINK_STATUS_VHT; + + if (intr[ht_info->vdevid].nss == 2) + link_status |= LINK_SUPPORT_MIMO; + + if (rate_flag & + (TX_RATE_VHT20 | TX_RATE_VHT40 | + TX_RATE_VHT80)) + link_status |= LINK_SUPPORT_VHT; + + wma_post_link_status( + intr[ht_info->vdevid].plink_status_req, + link_status); + intr[ht_info->vdevid].plink_status_req = NULL; + link_status = LINK_STATUS_LEGACY; + } + + ht_info++; + } + + return 0; +} + +int wma_rso_cmd_status_event_handler(wmi_roam_event_fixed_param *wmi_event) +{ + struct rso_cmd_status *rso_status; + struct scheduler_msg sme_msg = {0}; + QDF_STATUS qdf_status; + + rso_status = qdf_mem_malloc(sizeof(*rso_status)); + if (!rso_status) + return -ENOMEM; + + rso_status->vdev_id = wmi_event->vdev_id; + if (WMI_ROAM_NOTIF_SCAN_MODE_SUCCESS == wmi_event->notif) + rso_status->status = true; + else if (WMI_ROAM_NOTIF_SCAN_MODE_FAIL == wmi_event->notif) + rso_status->status = false; + sme_msg.type = eWNI_SME_RSO_CMD_STATUS_IND; + sme_msg.bodyptr = rso_status; + sme_msg.bodyval = 0; + WMA_LOGD("%s: Post RSO cmd status to SME", __func__); + + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("%s: fail to post RSO cmd status to SME", __func__); + qdf_mem_free(rso_status); + } + return 0; +} + +/** + * wma_fill_peer_info() - fill SIR peer info from WMI peer info struct + * @wma: wma interface + * @stats_info: WMI peer info pointer + * @peer_info: SIR peer info pointer + * + * This function will fill SIR peer info from WMI peer info struct + * + * Return: None + */ +static void wma_fill_peer_info(tp_wma_handle wma, + wmi_peer_stats_info *stats_info, + struct sir_peer_info_ext *peer_info) +{ + int i; + + peer_info->tx_packets = stats_info->tx_packets.low_32; + peer_info->tx_bytes = stats_info->tx_bytes.high_32; + peer_info->tx_bytes <<= 32; + peer_info->tx_bytes += stats_info->tx_bytes.low_32; + peer_info->rx_packets = stats_info->rx_packets.low_32; + peer_info->rx_bytes = stats_info->rx_bytes.high_32; + peer_info->rx_bytes <<= 32; + peer_info->rx_bytes += stats_info->rx_bytes.low_32; + peer_info->tx_retries = stats_info->tx_retries; + peer_info->tx_failed = stats_info->tx_failed; + peer_info->tx_succeed = stats_info->tx_succeed; + peer_info->rssi = stats_info->peer_rssi; + peer_info->tx_rate = stats_info->last_tx_bitrate_kbps; + peer_info->tx_rate_code = stats_info->last_tx_rate_code; + peer_info->rx_rate = stats_info->last_rx_bitrate_kbps; + peer_info->rx_rate_code = stats_info->last_rx_rate_code; + for (i = 0; i < WMI_MAX_CHAINS; i++) + peer_info->peer_rssi_per_chain[i] = + stats_info->peer_rssi_per_chain[i]; +} + +/** + * wma_peer_info_ext_rsp() - process peer ext info ext + * @handle: wma interface + * @buf: wmi event buf pointer + * + * This function will send eWNI_SME_GET_PEER_INFO_EXT_IND to SME + * + * Return: 0 on success, error code otherwise + */ +static QDF_STATUS wma_peer_info_ext_rsp(tp_wma_handle wma, u_int8_t *buf) +{ + wmi_peer_stats_info_event_fixed_param *event; + wmi_peer_stats_info *stats_info; + struct sir_peer_info_ext_resp *resp; + struct sir_peer_info_ext *peer_info; + struct scheduler_msg sme_msg = {0}; + int i, j = 0; + QDF_STATUS qdf_status; + + event = (wmi_peer_stats_info_event_fixed_param *)buf; + stats_info = (wmi_peer_stats_info *)(buf + + sizeof(wmi_peer_stats_info_event_fixed_param)); + + if (wma->get_one_peer_info) { + resp = qdf_mem_malloc(sizeof(struct sir_peer_info_ext_resp) + + sizeof(resp->info[0])); + if (!resp) + return QDF_STATUS_E_NOMEM; + + resp->count = 0; + peer_info = &resp->info[0]; + for (i = 0; i < event->num_peers; i++) { + WMI_MAC_ADDR_TO_CHAR_ARRAY(&stats_info->peer_macaddr, + peer_info->peer_macaddr.bytes); + + if (!qdf_mem_cmp(peer_info->peer_macaddr.bytes, + wma->peer_macaddr.bytes, + QDF_MAC_ADDR_SIZE)) { + wma_fill_peer_info(wma, stats_info, peer_info); + resp->count++; + break; + } + + stats_info = stats_info + 1; + } + } else { + resp = qdf_mem_malloc(sizeof(struct sir_peer_info_ext_resp) + + event->num_peers * sizeof(resp->info[0])); + if (!resp) + return QDF_STATUS_E_NOMEM; + + resp->count = event->num_peers; + for (i = 0; i < event->num_peers; i++) { + peer_info = &resp->info[j]; + WMI_MAC_ADDR_TO_CHAR_ARRAY(&stats_info->peer_macaddr, + peer_info->peer_macaddr.bytes); + + if (!qdf_mem_cmp(peer_info->peer_macaddr.bytes, + wma->myaddr, QDF_MAC_ADDR_SIZE)) { + resp->count = resp->count - 1; + } else { + wma_fill_peer_info(wma, stats_info, peer_info); + j++; + } + stats_info = stats_info + 1; + } + } + + sme_msg.type = eWNI_SME_GET_PEER_INFO_EXT_IND; + sme_msg.bodyptr = resp; + sme_msg.bodyval = 0; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + WMA_LOGE("%s: Fail to post get peer info msg", __func__); + qdf_mem_free(resp); + } + + return qdf_status; +} + +/** + * dump_peer_stats_info() - dump wmi peer info struct + * @event: wmi peer info fixed param pointer + * @peer_stats: wmi peer stats info pointer + * + * This function will dump wmi peer info struct + * + * Return: None + */ +static void dump_peer_stats_info(wmi_peer_stats_info_event_fixed_param *event, + wmi_peer_stats_info *peer_stats) +{ + int i, j; + wmi_peer_stats_info *stats = peer_stats; + u_int8_t mac[6]; + + WMA_LOGI("%s vdev_id %d, num_peers %d more_data %d", + __func__, event->vdev_id, + event->num_peers, event->more_data); + + for (i = 0; i < event->num_peers; i++) { + WMI_MAC_ADDR_TO_CHAR_ARRAY(&stats->peer_macaddr, mac); + WMA_LOGI("%s mac "QDF_MAC_ADDR_FMT, __func__, + QDF_MAC_ADDR_REF(mac)); + WMA_LOGI("%s tx_bytes %d %d tx_packets %d %d", + __func__, + stats->tx_bytes.low_32, + stats->tx_bytes.high_32, + stats->tx_packets.low_32, + stats->tx_packets.high_32); + WMA_LOGI("%s rx_bytes %d %d rx_packets %d %d", + __func__, + stats->rx_bytes.low_32, + stats->rx_bytes.high_32, + stats->rx_packets.low_32, + stats->rx_packets.high_32); + WMA_LOGI("%s tx_retries %d tx_failed %d", + __func__, stats->tx_retries, stats->tx_failed); + WMA_LOGI("%s tx_rate_code %x rx_rate_code %x", + __func__, + stats->last_tx_rate_code, + stats->last_rx_rate_code); + WMA_LOGI("%s tx_rate %x rx_rate %x", + __func__, + stats->last_tx_bitrate_kbps, + stats->last_rx_bitrate_kbps); + WMA_LOGI("%s peer_rssi %d", __func__, stats->peer_rssi); + WMA_LOGI("%s tx_succeed %d", __func__, stats->tx_succeed); + for (j = 0; j < WMI_MAX_CHAINS; j++) + WMA_LOGI("%s chain%d_rssi %d", __func__, j, + stats->peer_rssi_per_chain[j]); + + stats++; + } +} + +int wma_peer_info_event_handler(void *handle, u_int8_t *cmd_param_info, + u_int32_t len) +{ + tp_wma_handle wma = (tp_wma_handle) handle; + WMI_PEER_STATS_INFO_EVENTID_param_tlvs *param_buf; + wmi_peer_stats_info_event_fixed_param *event; + u_int32_t buf_size; + u_int8_t *buf; + + param_buf = + (WMI_PEER_STATS_INFO_EVENTID_param_tlvs *)cmd_param_info; + if (!param_buf) { + WMA_LOGA("%s: Invalid stats event", __func__); + return -EINVAL; + } + + WMA_LOGI("%s Recv WMI_PEER_STATS_INFO_EVENTID", __func__); + event = param_buf->fixed_param; + if (event->num_peers > + ((WMI_SVC_MSG_MAX_SIZE - + sizeof(wmi_peer_stats_info_event_fixed_param))/ + sizeof(wmi_peer_stats_info)) || event->num_peers > + param_buf->num_peer_stats_info) { + WMA_LOGE("Excess num of peers from fw: %d, num_peer_stats_info:%d", + event->num_peers, param_buf->num_peer_stats_info); + return -EINVAL; + } + buf_size = sizeof(wmi_peer_stats_info_event_fixed_param) + + sizeof(wmi_peer_stats_info) * event->num_peers; + buf = qdf_mem_malloc(buf_size); + if (!buf) + return -ENOMEM; + + qdf_mem_copy(buf, param_buf->fixed_param, + sizeof(wmi_peer_stats_info_event_fixed_param)); + qdf_mem_copy((buf + sizeof(wmi_peer_stats_info_event_fixed_param)), + param_buf->peer_stats_info, + sizeof(wmi_peer_stats_info) * event->num_peers); + WMA_LOGI("%s dump peer stats info", __func__); + dump_peer_stats_info(event, param_buf->peer_stats_info); + + wma_peer_info_ext_rsp(wma, buf); + qdf_mem_free(buf); + + return 0; +} + +/** + * wma_send_link_speed() - send link speed to SME + * @link_speed: link speed + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS wma_send_link_speed(uint32_t link_speed) +{ + struct mac_context *mac_ctx; + struct link_speed_info *ls_ind; + + mac_ctx = cds_get_context(QDF_MODULE_ID_PE); + if (!mac_ctx) { + WMA_LOGD("%s: NULL mac ptr. Exiting", __func__); + return QDF_STATUS_E_INVAL; + } + + ls_ind = qdf_mem_malloc(sizeof(*ls_ind)); + if (!ls_ind) + return QDF_STATUS_E_NOMEM; + + ls_ind->estLinkSpeed = link_speed; + if (mac_ctx->sme.link_speed_cb) + mac_ctx->sme.link_speed_cb(ls_ind, + mac_ctx->sme.link_speed_context); + else + WMA_LOGD("%s: link_speed_cb is null", __func__); + qdf_mem_free(ls_ind); + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_link_speed_event_handler() - link speed event handler + * @handle: wma handle + * @cmd_param_info: event data + * @len: length + * + * Return: 0 for success or error code + */ +int wma_link_speed_event_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len) +{ + WMI_PEER_ESTIMATED_LINKSPEED_EVENTID_param_tlvs *param_buf; + wmi_peer_estimated_linkspeed_event_fixed_param *event; + QDF_STATUS qdf_status; + + param_buf = (WMI_PEER_ESTIMATED_LINKSPEED_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid linkspeed event", __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + qdf_status = wma_send_link_speed(event->est_linkspeed_kbps); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) + return -EINVAL; + return 0; +} + +#define BIG_ENDIAN_MAX_DEBUG_BUF 500 +/** + * wma_unified_debug_print_event_handler() - debug print event handler + * @handle: wma handle + * @datap: data pointer + * @len: length + * + * Return: 0 for success or error code + */ +int wma_unified_debug_print_event_handler(void *handle, uint8_t *datap, + uint32_t len) +{ + WMI_DEBUG_PRINT_EVENTID_param_tlvs *param_buf; + uint8_t *data; + uint32_t datalen; + + param_buf = (WMI_DEBUG_PRINT_EVENTID_param_tlvs *) datap; + if (!param_buf || !param_buf->data) { + WMA_LOGE("Get NULL point message from FW"); + return -ENOMEM; + } + data = param_buf->data; + datalen = param_buf->num_data; + if (datalen > WMI_SVC_MSG_MAX_SIZE) { + WMA_LOGE("Received data len %d exceeds max value %d", + datalen, WMI_SVC_MSG_MAX_SIZE); + return QDF_STATUS_E_FAILURE; + } + data[datalen - 1] = '\0'; + +#ifdef BIG_ENDIAN_HOST + { + if (datalen >= BIG_ENDIAN_MAX_DEBUG_BUF) { + WMA_LOGE("%s Invalid data len %d, limiting to max", + __func__, datalen); + datalen = BIG_ENDIAN_MAX_DEBUG_BUF - 1; + } + char dbgbuf[BIG_ENDIAN_MAX_DEBUG_BUF] = { 0 }; + + memcpy(dbgbuf, data, datalen); + SWAPME(dbgbuf, datalen); + WMA_LOGD("FIRMWARE:%s", dbgbuf); + return 0; + } +#else + WMA_LOGD("FIRMWARE:%s", data); + return 0; +#endif /* BIG_ENDIAN_HOST */ +} + +enum wlan_phymode +wma_peer_phymode(tSirNwType nw_type, uint8_t sta_type, + uint8_t is_ht, uint8_t ch_width, + uint8_t is_vht, bool is_he) +{ + enum wlan_phymode phymode = WLAN_PHYMODE_AUTO; + + switch (nw_type) { + case eSIR_11B_NW_TYPE: +#ifdef FEATURE_WLAN_TDLS + if (STA_ENTRY_TDLS_PEER == sta_type) { + if (is_vht) { + if (CH_WIDTH_80MHZ == ch_width) + phymode = WLAN_PHYMODE_11AC_VHT80; + else + phymode = (CH_WIDTH_40MHZ == ch_width) ? + WLAN_PHYMODE_11AC_VHT40 : + WLAN_PHYMODE_11AC_VHT20_2G; + } else if (is_ht) { + phymode = (CH_WIDTH_40MHZ == ch_width) ? + WLAN_PHYMODE_11NG_HT40 : + WLAN_PHYMODE_11NG_HT20; + } else + phymode = WLAN_PHYMODE_11B; + } else +#endif /* FEATURE_WLAN_TDLS */ + { + phymode = WLAN_PHYMODE_11B; + if (is_ht || is_vht || is_he) + WMA_LOGE("HT/VHT is enabled with 11B NW type"); + } + break; + case eSIR_11G_NW_TYPE: + if (!(is_ht || is_vht || is_he)) { + phymode = WLAN_PHYMODE_11G; + break; + } + if (CH_WIDTH_40MHZ < ch_width) + WMA_LOGE("80/160 MHz BW sent in 11G, configured 40MHz"); + if (ch_width) + phymode = (is_he) ? WLAN_PHYMODE_11AXG_HE40 : (is_vht) ? + WLAN_PHYMODE_11AC_VHT40_2G : + WLAN_PHYMODE_11NG_HT40; + else + phymode = (is_he) ? WLAN_PHYMODE_11AXG_HE20 : (is_vht) ? + WLAN_PHYMODE_11AC_VHT20_2G : + WLAN_PHYMODE_11NG_HT20; + break; + case eSIR_11A_NW_TYPE: + if (!(is_ht || is_vht || is_he)) { + phymode = WLAN_PHYMODE_11A; + break; + } + if (is_he) { + if (ch_width == CH_WIDTH_160MHZ) + phymode = WLAN_PHYMODE_11AXA_HE160; + else if (ch_width == CH_WIDTH_80P80MHZ) + phymode = WLAN_PHYMODE_11AXA_HE80_80; + else if (ch_width == CH_WIDTH_80MHZ) + phymode = WLAN_PHYMODE_11AXA_HE80; + else + phymode = (ch_width) ? + WLAN_PHYMODE_11AXA_HE40 : + WLAN_PHYMODE_11AXA_HE20; + } else if (is_vht) { + if (ch_width == CH_WIDTH_160MHZ) + phymode = WLAN_PHYMODE_11AC_VHT160; + else if (ch_width == CH_WIDTH_80P80MHZ) + phymode = WLAN_PHYMODE_11AC_VHT80_80; + else if (ch_width == CH_WIDTH_80MHZ) + phymode = WLAN_PHYMODE_11AC_VHT80; + else + phymode = (ch_width) ? + WLAN_PHYMODE_11AC_VHT40 : + WLAN_PHYMODE_11AC_VHT20; + } else + phymode = (ch_width) ? WLAN_PHYMODE_11NA_HT40 : + WLAN_PHYMODE_11NA_HT20; + break; + default: + WMA_LOGE("%s: Invalid nw type %d", __func__, nw_type); + break; + } + WMA_LOGD(FL("nw_type %d is_ht %d ch_width %d is_vht %d is_he %d phymode %d"), + nw_type, is_ht, ch_width, is_vht, is_he, phymode); + + return phymode; +} + +/** + * wma_txrx_fw_stats_reset() - reset txrx fw statistics + * @wma_handle: wma handle + * @vdev_id: vdev id + * @value: value + * + * Return: 0 for success or return error + */ +int32_t wma_txrx_fw_stats_reset(tp_wma_handle wma_handle, + uint8_t vdev_id, uint32_t value) +{ + struct ol_txrx_stats_req req; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!soc) { + WMA_LOGE("%s:SOC context is NULL", __func__); + return -EINVAL; + } + + qdf_mem_zero(&req, sizeof(req)); + req.stats_type_reset_mask = value; + cdp_fw_stats_get(soc, vdev_id, &req, false, false); + + return 0; +} + +#ifdef HELIUMPLUS +#define SET_UPLOAD_MASK(_mask, _rate_info) \ + ((_mask) = 1 << (_rate_info ## _V2)) +#else /* !HELIUMPLUS */ +#define SET_UPLOAD_MASK(_mask, _rate_info) \ + ((_mask) = 1 << (_rate_info)) +#endif + +#if defined(HELIUMPLUS) || defined(QCN7605_SUPPORT) +static bool wma_is_valid_fw_stats_cmd(uint32_t value) +{ + if (value > (HTT_DBG_NUM_STATS + 1) || + value == (HTT_DBG_STATS_RX_RATE_INFO + 1) || + value == (HTT_DBG_STATS_TX_RATE_INFO + 1) || + value == (HTT_DBG_STATS_TXBF_MUSU_NDPA_PKT + 1)) { + WMA_LOGE("%s: Not supported", __func__); + return false; + } + return true; +} +#else +static bool wma_is_valid_fw_stats_cmd(uint32_t value) +{ + if (value > (HTT_DBG_NUM_STATS + 1) || + value == (HTT_DBG_STATS_RX_RATE_INFO_V2 + 1) || + value == (HTT_DBG_STATS_TX_RATE_INFO_V2 + 1) || + value == (HTT_DBG_STATS_TXBF_MUSU_NDPA_PKT + 1)) { + WMA_LOGE("%s: Not supported", __func__); + return false; + } + return true; +} +#endif + +/** + * wma_set_txrx_fw_stats_level() - set txrx fw stats level + * @wma_handle: wma handle + * @vdev_id: vdev id + * @value: value + * + * Return: 0 for success or return error + */ +int32_t wma_set_txrx_fw_stats_level(tp_wma_handle wma_handle, + uint8_t vdev_id, uint32_t value) +{ + struct ol_txrx_stats_req req; + uint32_t l_up_mask; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!soc) { + WMA_LOGE("%s:SOC context is NULL", __func__); + return -EINVAL; + } + + if (wma_is_valid_fw_stats_cmd(value) == false) + return -EINVAL; + + qdf_mem_zero(&req, sizeof(req)); + req.print.verbose = 1; + + /* TODO: Need to check how to avoid mem leak*/ + l_up_mask = 1 << (value - 1); + req.stats_type_upload_mask = l_up_mask; + + cdp_fw_stats_get(soc, vdev_id, &req, false, true); + + return 0; +} + +/** + * wma_get_cca_stats() - send request to fw to get CCA + * @wma_handle: wma handle + * @vdev_id: vdev id + * + * Return: QDF status + */ +QDF_STATUS wma_get_cca_stats(tp_wma_handle wma_handle, + uint8_t vdev_id) +{ + if (wmi_unified_congestion_request_cmd(wma_handle->wmi_handle, + vdev_id)) { + WMA_LOGE("Failed to congestion request to fw"); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +/** + * wma_get_beacon_buffer_by_vdev_id() - get the beacon buffer from vdev ID + * @vdev_id: vdev id + * @buffer_size: size of buffer + * + * Return: none + */ +void *wma_get_beacon_buffer_by_vdev_id(uint8_t vdev_id, uint32_t *buffer_size) +{ + tp_wma_handle wma; + struct beacon_info *beacon; + uint8_t *buf; + uint32_t buf_size; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + return NULL; + } + + if (vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: Invalid vdev_id %u", __func__, vdev_id); + return NULL; + } + + if (!wma_is_vdev_in_ap_mode(wma, vdev_id)) { + WMA_LOGE("%s: vdevid %d is not in AP mode", __func__, vdev_id); + return NULL; + } + + beacon = wma->interfaces[vdev_id].beacon; + + if (!beacon) { + WMA_LOGE("%s: beacon invalid", __func__); + return NULL; + } + + qdf_spin_lock_bh(&beacon->lock); + + buf_size = qdf_nbuf_len(beacon->buf); + buf = qdf_mem_malloc(buf_size); + if (!buf) { + qdf_spin_unlock_bh(&beacon->lock); + return NULL; + } + + qdf_mem_copy(buf, qdf_nbuf_data(beacon->buf), buf_size); + + qdf_spin_unlock_bh(&beacon->lock); + + if (buffer_size) + *buffer_size = buf_size; + + return buf; +} + +uint8_t *wma_get_vdev_address_by_vdev_id(uint8_t vdev_id) +{ + tp_wma_handle wma; + struct wlan_objmgr_vdev *vdev; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + return NULL; + } + + if (vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: Invalid vdev_id %u", __func__, vdev_id); + return NULL; + } + vdev = wma->interfaces[vdev_id].vdev; + if (!vdev) { + WMA_LOGE("%s: Invalid vdev for vdev_id %u", __func__, vdev_id); + return NULL; + } + return wlan_vdev_mlme_get_macaddr(vdev); +} + +QDF_STATUS wma_get_connection_info(uint8_t vdev_id, + struct policy_mgr_vdev_entry_info *conn_table_entry) +{ + struct wma_txrx_node *wma_conn_table_entry; + + wma_conn_table_entry = wma_get_interface_by_vdev_id(vdev_id); + if (!wma_conn_table_entry) { + WMA_LOGE("%s: can't find vdev_id %d in WMA table", __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + conn_table_entry->chan_width = wma_conn_table_entry->chan_width; + conn_table_entry->mac_id = wma_conn_table_entry->mac_id; + conn_table_entry->mhz = wma_conn_table_entry->mhz; + conn_table_entry->sub_type = wma_conn_table_entry->sub_type; + conn_table_entry->type = wma_conn_table_entry->type; + conn_table_entry->ch_flagext = wma_conn_table_entry->ch_flagext; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_ndi_update_connection_info(uint8_t vdev_id, + struct nan_datapath_channel_info *ndp_chan_info) +{ + struct wma_txrx_node *wma_iface_entry; + + wma_iface_entry = wma_get_interface_by_vdev_id(vdev_id); + if (!wma_iface_entry) { + WMA_LOGE("%s: can't find vdev_id %d in WMA table", __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + + if (WMI_VDEV_TYPE_NDI != wma_iface_entry->type) { + WMA_LOGE("%s: Given vdev id(%d) not of type NDI!", + __func__, vdev_id); + return QDF_STATUS_E_FAILURE; + } + + if (!ndp_chan_info) { + WMA_LOGE("%s: Provided chan info is NULL!", __func__); + return QDF_STATUS_E_FAILURE; + } + + wma_iface_entry->chan_width = ndp_chan_info->ch_width; + wma_iface_entry->mhz = ndp_chan_info->freq; + wma_iface_entry->nss = ndp_chan_info->nss; + wma_iface_entry->mac_id = ndp_chan_info->mac_id; + + return QDF_STATUS_SUCCESS; +} + +/** + * wma_get_interface_by_vdev_id() - lookup interface entry using vdev ID + * @vdev_id: vdev id + * + * Return: entry from vdev table + */ +struct wma_txrx_node *wma_get_interface_by_vdev_id(uint8_t vdev_id) +{ + tp_wma_handle wma; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + return NULL; + } + + if (vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: Invalid vdev_id %u", __func__, vdev_id); + return NULL; + } + + return &wma->interfaces[vdev_id]; +} + +/** + * wma_update_intf_hw_mode_params() - Update WMA params + * @vdev_id: VDEV id whose params needs to be updated + * @mac_id: MAC id to be updated + * @cfgd_hw_mode_index: HW mode index from which Tx and Rx SS will be updated + * + * Updates the MAC id, tx spatial stream, rx spatial stream in WMA + * + * Return: None + */ +void wma_update_intf_hw_mode_params(uint32_t vdev_id, uint32_t mac_id, + uint32_t cfgd_hw_mode_index) +{ + tp_wma_handle wma; + struct policy_mgr_hw_mode_params hw_mode; + QDF_STATUS status; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + return; + } + + if (!wma->interfaces) { + WMA_LOGE("%s: Interface is NULL", __func__); + return; + } + + status = policy_mgr_get_hw_mode_from_idx(wma->psoc, cfgd_hw_mode_index, + &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + WMA_LOGE("%s: cfgd_hw_mode_index %d not found", __func__, + cfgd_hw_mode_index); + return; + } + wma->interfaces[vdev_id].mac_id = mac_id; + if (mac_id == 0) + wma->interfaces[vdev_id].tx_streams = + hw_mode.mac0_tx_ss; + else + wma->interfaces[vdev_id].tx_streams = + hw_mode.mac1_tx_ss; + + WMA_LOGD("%s: vdev %d, update tx ss:%d mac %d hw_mode_id %d", + __func__, + vdev_id, + wma->interfaces[vdev_id].tx_streams, + mac_id, + cfgd_hw_mode_index); +} + +/** + * wma_get_vht_ch_width - return vht channel width + * + * Return: return vht channel width + */ +uint32_t wma_get_vht_ch_width(void) +{ + uint32_t fw_ch_wd = WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ; + tp_wma_handle wm_hdl = cds_get_context(QDF_MODULE_ID_WMA); + struct target_psoc_info *tgt_hdl; + int vht_cap_info; + + if (!wm_hdl) + return fw_ch_wd; + + tgt_hdl = wlan_psoc_get_tgt_if_handle(wm_hdl->psoc); + if (!tgt_hdl) + return fw_ch_wd; + + vht_cap_info = target_if_get_vht_cap_info(tgt_hdl); + if (vht_cap_info & WMI_VHT_CAP_CH_WIDTH_80P80_160MHZ) + fw_ch_wd = WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ; + else if (vht_cap_info & WMI_VHT_CAP_CH_WIDTH_160MHZ) + fw_ch_wd = WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ; + + return fw_ch_wd; +} + +/** + * wma_get_num_of_setbits_from_bitmask() - to get num of setbits from bitmask + * @mask: given bitmask + * + * This helper function should return number of setbits from bitmask + * + * Return: number of setbits from bitmask + */ +uint32_t wma_get_num_of_setbits_from_bitmask(uint32_t mask) +{ + uint32_t num_of_setbits = 0; + + while (mask) { + mask &= (mask - 1); + num_of_setbits++; + } + return num_of_setbits; +} + +/** + * wma_is_csa_offload_enabled - checks fw CSA offload capability + * + * Return: true or false + */ + +bool wma_is_csa_offload_enabled(void) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) + return false; + + return wmi_service_enabled(wma->wmi_handle, + wmi_service_csa_offload); +} + +bool wma_is_mbssid_enabled(void) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) + return false; + + return wmi_service_enabled(wma->wmi_handle, + wmi_service_infra_mbssid); +} + +#ifdef FEATURE_FW_LOG_PARSING +/** + * wma_config_debug_module_cmd - set debug log config + * @wmi_handle: wmi layer handle + * @param: debug log parameter + * @val: debug log value + * @module_id_bitmap: debug module id bitmap + * @bitmap_len: debug module bitmap length + * + * Return: QDF_STATUS_SUCCESS for success or error code + */ +QDF_STATUS +wma_config_debug_module_cmd(wmi_unified_t wmi_handle, A_UINT32 param, + A_UINT32 val, A_UINT32 *module_id_bitmap, + A_UINT32 bitmap_len) +{ + struct dbglog_params dbg_param; + + dbg_param.param = param; + dbg_param.val = val; + dbg_param.module_id_bitmap = module_id_bitmap; + dbg_param.bitmap_len = bitmap_len; + + return wmi_unified_dbglog_cmd_send(wmi_handle, &dbg_param); +} +#endif +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * wma_is_p2p_lo_capable() - if driver is capable of p2p listen offload + * + * This function checks if driver is capable of p2p listen offload + * true: capable of p2p offload + * false: not capable + * + * Return: true - capable, false - not capable + */ +bool wma_is_p2p_lo_capable(void) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (wma) { + return wmi_service_enabled + (wma->wmi_handle, + wmi_service_p2p_listen_offload_support); + } + + return 0; +} +#endif + +#ifdef WLAN_FEATURE_ROAM_OFFLOAD +QDF_STATUS wma_get_roam_scan_ch(wmi_unified_t wmi_handle, + uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct roam_scan_ch_resp *roam_ch; + struct scheduler_msg sme_msg = {0}; + + if (!wma_is_vdev_valid(vdev_id)) { + wma_err("vdev_id: %d is not active", vdev_id); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_get_roam_scan_ch_list(wmi_handle, vdev_id); + if (QDF_IS_STATUS_SUCCESS(status)) + return status; + roam_ch = qdf_mem_malloc(sizeof(struct roam_scan_ch_resp)); + if (!roam_ch) { + wma_err("Failed to alloc resp"); + return QDF_STATUS_E_INVAL; + } + + roam_ch->command_resp = 1; + roam_ch->num_channels = 0; + roam_ch->chan_list = NULL; + roam_ch->vdev_id = vdev_id; + sme_msg.type = eWNI_SME_GET_ROAM_SCAN_CH_LIST_EVENT; + sme_msg.bodyptr = roam_ch; + + if (scheduler_post_message(QDF_MODULE_ID_WMA, + QDF_MODULE_ID_SME, + QDF_MODULE_ID_SME, &sme_msg)) { + wma_err("Failed to post msg to SME"); + qdf_mem_free(roam_ch); + return QDF_STATUS_E_INVAL; + } + + return status; +} +#endif + +bool wma_capability_enhanced_mcast_filter(void) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (wma) { + return wmi_service_enabled(wma->wmi_handle, + wmi_service_enhanced_mcast_filter); + } + + return 0; +} + + +bool wma_is_vdev_up(uint8_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + tp_wma_handle wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA); + bool is_up = false; + + if (!wma) { + WMA_LOGE("%s: WMA context is invald!", __func__); + return is_up; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, vdev_id, + WLAN_LEGACY_WMA_ID); + if (vdev) { + is_up = QDF_IS_STATUS_SUCCESS(wlan_vdev_is_up(vdev)); + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); + } + return is_up; +} + +void wma_acquire_wakelock(qdf_wake_lock_t *wl, uint32_t msec) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + cds_host_diag_log_work(wl, msec, WIFI_POWER_EVENT_WAKELOCK_WMI_CMD_RSP); + qdf_wake_lock_timeout_acquire(wl, msec); + qdf_runtime_pm_prevent_suspend(&wma->wmi_cmd_rsp_runtime_lock); +} + +void wma_release_wakelock(qdf_wake_lock_t *wl) +{ + t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); + + qdf_wake_lock_release(wl, WIFI_POWER_EVENT_WAKELOCK_WMI_CMD_RSP); + qdf_runtime_pm_allow_suspend(&wma->wmi_cmd_rsp_runtime_lock); +} + +QDF_STATUS wma_send_vdev_stop_to_fw(t_wma_handle *wma, uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct wma_txrx_node *iface = &wma->interfaces[vdev_id]; + struct vdev_mlme_obj *vdev_mlme = NULL; + + if (!wma_is_vdev_valid(vdev_id)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, vdev_id); + return status; + } + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (!vdev_mlme) { + WMA_LOGE("Failed to get vdev mlme obj for vdev id %d", vdev_id); + return status; + } + + /* + * Reset the dynamic nss chains config to the ini values, as when the + * vdev gets its started again, this would be a fresh connection, + * and we dont want the config of previous connection to affect the + * current connection. + */ + qdf_mem_copy(mlme_get_dynamic_vdev_config(iface->vdev), + mlme_get_ini_vdev_config(iface->vdev), + sizeof(struct wlan_mlme_nss_chains)); + + status = vdev_mgr_stop_send(vdev_mlme); + + return status; +} + +QDF_STATUS wma_get_rcpi_req(WMA_HANDLE handle, + struct sme_rcpi_req *rcpi_request) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + struct rcpi_req cmd = {0}; + struct wma_txrx_node *iface; + struct sme_rcpi_req *node_rcpi_req; + + WMA_LOGD("%s: Enter", __func__); + iface = &wma_handle->interfaces[rcpi_request->session_id]; + /* command is in progress */ + if (iface->rcpi_req) { + WMA_LOGE("%s : previous rcpi request is pending", __func__); + return QDF_STATUS_SUCCESS; + } + + node_rcpi_req = qdf_mem_malloc(sizeof(*node_rcpi_req)); + if (!node_rcpi_req) + return QDF_STATUS_E_NOMEM; + + *node_rcpi_req = *rcpi_request; + iface->rcpi_req = node_rcpi_req; + + cmd.vdev_id = rcpi_request->session_id; + qdf_mem_copy(cmd.mac_addr, &rcpi_request->mac_addr, QDF_MAC_ADDR_SIZE); + cmd.measurement_type = rcpi_request->measurement_type; + + if (wmi_unified_send_request_get_rcpi_cmd(wma_handle->wmi_handle, + &cmd)) { + WMA_LOGE("%s: Failed to send WMI_REQUEST_RCPI_CMDID", + __func__); + iface->rcpi_req = NULL; + qdf_mem_free(node_rcpi_req); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("%s: Exit", __func__); + + return QDF_STATUS_SUCCESS; +} + +int wma_rcpi_event_handler(void *handle, uint8_t *cmd_param_info, + uint32_t len) +{ + struct rcpi_res res = {0}; + struct sme_rcpi_req *rcpi_req; + struct qdf_mac_addr qdf_mac; + struct wma_txrx_node *iface; + QDF_STATUS status = QDF_STATUS_SUCCESS; + tp_wma_handle wma_handle = (tp_wma_handle)handle; + + status = wmi_extract_rcpi_response_event(wma_handle->wmi_handle, + cmd_param_info, &res); + if (status == QDF_STATUS_E_INVAL) + return -EINVAL; + + if (res.vdev_id >= wma_handle->max_bssid) { + WMA_LOGE("%s: received invalid vdev_id %d", + __func__, res.vdev_id); + return -EINVAL; + } + + iface = &wma_handle->interfaces[res.vdev_id]; + if (!iface->rcpi_req) { + WMI_LOGE("rcpi_req buffer not available"); + return 0; + } + + rcpi_req = iface->rcpi_req; + if (!rcpi_req->rcpi_callback) { + iface->rcpi_req = NULL; + qdf_mem_free(rcpi_req); + return 0; + } + + if ((res.measurement_type == RCPI_MEASUREMENT_TYPE_INVALID) || + (res.vdev_id != rcpi_req->session_id) || + (res.measurement_type != rcpi_req->measurement_type) || + (qdf_mem_cmp(res.mac_addr, &rcpi_req->mac_addr, + QDF_MAC_ADDR_SIZE))) { + WMI_LOGE("invalid rcpi_response"); + iface->rcpi_req = NULL; + qdf_mem_free(rcpi_req); + return 0; + } + + qdf_mem_copy(&qdf_mac, res.mac_addr, QDF_MAC_ADDR_SIZE); + (rcpi_req->rcpi_callback)(rcpi_req->rcpi_context, qdf_mac, + res.rcpi_value, status); + iface->rcpi_req = NULL; + qdf_mem_free(rcpi_req); + + return 0; +} + +/** + * wma_set_roam_offload_flag() - Set roam offload flag to fw + * @wma: wma handle + * @vdev_id: vdev id + * @is_set: set or clear + * + * Return: none + */ +static void wma_set_roam_offload_flag(tp_wma_handle wma, uint8_t vdev_id, + bool is_set) +{ + QDF_STATUS status; + uint32_t flag = 0; + bool disable_4way_hs_offload; + bool bmiss_skip_full_scan; + + if (is_set) { + flag = WMI_ROAM_FW_OFFLOAD_ENABLE_FLAG | + WMI_ROAM_BMISS_FINAL_SCAN_ENABLE_FLAG; + + wlan_mlme_get_4way_hs_offload(wma->psoc, + &disable_4way_hs_offload); + /* + * If 4-way HS offload is disabled then let supplicant handle + * 4way HS and firmware will still do LFR3.0 till reassoc phase. + */ + if (disable_4way_hs_offload) + flag |= WMI_VDEV_PARAM_SKIP_ROAM_EAPOL_4WAY_HANDSHAKE; + + wlan_mlme_get_bmiss_skip_full_scan_value(wma->psoc, + &bmiss_skip_full_scan); + /* + * If WMI_ROAM_BMISS_FINAL_SCAN_ENABLE_FLAG is set, then + * WMI_ROAM_BMISS_FINAL_SCAN_TYPE_FLAG decides whether firmware + * does channel map based partial scan or partial scan followed + * by full scan in case no candidate is found in partial scan. + */ + if (bmiss_skip_full_scan) + flag |= WMI_ROAM_BMISS_FINAL_SCAN_TYPE_FLAG; + } + + wma_debug("vdev_id:%d, is_set:%d, flag:%d", vdev_id, is_set, flag); + status = wma_vdev_set_param(wma->wmi_handle, vdev_id, + WMI_VDEV_PARAM_ROAM_FW_OFFLOAD, flag); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("Failed to set WMI_VDEV_PARAM_ROAM_FW_OFFLOAD"); +} + +void wma_update_roam_offload_flag(void *handle, + struct roam_init_params *params) +{ + tp_wma_handle wma = handle; + struct wma_txrx_node *iface; + + if (!wma_is_vdev_valid(params->vdev_id)) { + WMA_LOGE("%s: vdev_id: %d is not active", __func__, + params->vdev_id); + return; + } + + iface = &wma->interfaces[params->vdev_id]; + + if ((iface->type != WMI_VDEV_TYPE_STA) || + (iface->sub_type != 0)) { + WMA_LOGE("%s: this isn't a STA: %d", + __func__, params->vdev_id); + return; + } + + wma_set_roam_offload_flag(wma, params->vdev_id, params->enable); +} + +QDF_STATUS wma_send_vdev_down_to_fw(t_wma_handle *wma, uint8_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct wma_txrx_node *iface = &wma->interfaces[vdev_id]; + struct vdev_mlme_obj *vdev_mlme; + + if (!wma_is_vdev_valid(vdev_id)) { + WMA_LOGE("%s: Invalid vdev id:%d", __func__, vdev_id); + return status; + } + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); + if (!vdev_mlme) { + WMA_LOGE("Failed to get vdev mlme obj for vdev id %d", vdev_id); + return status; + } + + wma->interfaces[vdev_id].roaming_in_progress = false; + + status = vdev_mgr_down_send(vdev_mlme); + + return status; +} + +#ifdef WLAN_FEATURE_LINK_LAYER_STATS +tSirWifiPeerType wmi_to_sir_peer_type(enum wmi_peer_type type) +{ + switch (type) { + case WMI_PEER_TYPE_DEFAULT: + return WIFI_PEER_STA; + case WMI_PEER_TYPE_BSS: + return WIFI_PEER_AP; + case WMI_PEER_TYPE_TDLS: + return WIFI_PEER_TDLS; + case WMI_PEER_TYPE_NAN_DATA: + return WIFI_PEER_NAN; + default: + WMA_LOGE("Cannot map wmi_peer_type %d to HAL peer type", type); + return WIFI_PEER_INVALID; + } +} +#endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +#ifdef FEATURE_WLAN_DYNAMIC_CVM +/** + * wma_set_vc_mode_config() - set voltage corner mode config to FW. + * @wma_handle: pointer to wma handle. + * @vc_bitmap: value needs to set to firmware. + * + * At the time of driver startup, set operating voltage corner mode + * for differenet phymode and bw configurations. + * + * Return: QDF_STATUS. + */ +QDF_STATUS wma_set_vc_mode_config(void *wma_handle, + uint32_t vc_bitmap) +{ + int32_t ret; + tp_wma_handle wma = (tp_wma_handle)wma_handle; + struct pdev_params pdevparam; + + pdevparam.param_id = WMI_PDEV_UPDATE_WDCVS_ALGO; + pdevparam.param_value = vc_bitmap; + + ret = wmi_unified_pdev_param_send(wma->wmi_handle, + &pdevparam, + WMA_WILDCARD_PDEV_ID); + if (ret) { + WMA_LOGE("Fail to Set Voltage Corner config (0x%x)", + vc_bitmap); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("Successfully Set Voltage Corner config (0x%x)", + vc_bitmap); + + return QDF_STATUS_SUCCESS; +} +#endif + +int wma_chip_power_save_failure_detected_handler(void *handle, + uint8_t *cmd_param_info, + uint32_t len) +{ + tp_wma_handle wma = (tp_wma_handle)handle; + WMI_PDEV_CHIP_POWER_SAVE_FAILURE_DETECTED_EVENTID_param_tlvs *param_buf; + wmi_chip_power_save_failure_detected_fixed_param *event; + struct chip_pwr_save_fail_detected_params pwr_save_fail_params; + struct mac_context *mac = (struct mac_context *)cds_get_context( + QDF_MODULE_ID_PE); + if (!wma) { + WMA_LOGE("%s: wma_handle is NULL", __func__); + return -EINVAL; + } + if (!mac) { + WMA_LOGE("%s: Invalid mac context", __func__); + return -EINVAL; + } + if (!mac->sme.chip_power_save_fail_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + + param_buf = + (WMI_PDEV_CHIP_POWER_SAVE_FAILURE_DETECTED_EVENTID_param_tlvs *) + cmd_param_info; + if (!param_buf) { + WMA_LOGE("%s: Invalid pwr_save_fail_params breached event", + __func__); + return -EINVAL; + } + event = param_buf->fixed_param; + pwr_save_fail_params.failure_reason_code = + event->power_save_failure_reason_code; + pwr_save_fail_params.wake_lock_bitmap[0] = + event->protocol_wake_lock_bitmap[0]; + pwr_save_fail_params.wake_lock_bitmap[1] = + event->protocol_wake_lock_bitmap[1]; + pwr_save_fail_params.wake_lock_bitmap[2] = + event->protocol_wake_lock_bitmap[2]; + pwr_save_fail_params.wake_lock_bitmap[3] = + event->protocol_wake_lock_bitmap[3]; + mac->sme.chip_power_save_fail_cb(mac->hdd_handle, + &pwr_save_fail_params); + + WMA_LOGD("%s: Invoke HDD pwr_save_fail callback", __func__); + return 0; +} + +int wma_roam_scan_stats_event_handler(void *handle, uint8_t *event, + uint32_t len) +{ + tp_wma_handle wma_handle; + wmi_unified_t wmi_handle; + struct sir_roam_scan_stats *roam_scan_stats_req = NULL; + struct wma_txrx_node *iface = NULL; + struct wmi_roam_scan_stats_res *res = NULL; + int ret = 0; + uint32_t vdev_id; + QDF_STATUS status; + + wma_handle = handle; + if (!wma_handle) { + WMA_LOGE(FL("NULL wma_handle")); + return -EINVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + WMA_LOGE(FL("NULL wmi_handle")); + return -EINVAL; + } + + status = wmi_extract_roam_scan_stats_res_evt(wmi_handle, event, + &vdev_id, + &res); + + /* vdev_id can be invalid though status is success, hence validate */ + if (vdev_id >= wma_handle->max_bssid) { + WMA_LOGE(FL("Received invalid vdev_id: %d"), vdev_id); + ret = -EINVAL; + goto free_res; + } + + /* Get interface for valid vdev_id */ + iface = &wma_handle->interfaces[vdev_id]; + if (!iface) { + WMI_LOGE(FL("Interface not available for vdev_id: %d"), + vdev_id); + ret = -EINVAL; + goto free_res; + } + + roam_scan_stats_req = iface->roam_scan_stats_req; + iface->roam_scan_stats_req = NULL; + if (!roam_scan_stats_req) { + WMI_LOGE(FL("No pending request vdev_id: %d"), vdev_id); + ret = -EINVAL; + goto free_res; + } + + if (!QDF_IS_STATUS_SUCCESS(status) || + !roam_scan_stats_req->cb || + roam_scan_stats_req->vdev_id != vdev_id) { + WMI_LOGE(FL("roam_scan_stats buffer not available")); + ret = -EINVAL; + goto free_roam_scan_stats_req; + } + + roam_scan_stats_req->cb(roam_scan_stats_req->context, res); + +free_roam_scan_stats_req: + qdf_mem_free(roam_scan_stats_req); + roam_scan_stats_req = NULL; + +free_res: + qdf_mem_free(res); + res = NULL; + + return ret; +} + +QDF_STATUS wma_get_roam_scan_stats(WMA_HANDLE handle, + struct sir_roam_scan_stats *req) +{ + tp_wma_handle wma_handle = (tp_wma_handle)handle; + struct wmi_roam_scan_stats_req cmd = {0}; + struct wma_txrx_node *iface; + struct sir_roam_scan_stats *node_req = NULL; + + WMA_LOGD("%s: Enter", __func__); + iface = &wma_handle->interfaces[req->vdev_id]; + /* command is in progress */ + if (iface->roam_scan_stats_req) { + WMA_LOGE(FL("previous roam scan stats req is pending")); + return QDF_STATUS_SUCCESS; + } + + node_req = qdf_mem_malloc(sizeof(*node_req)); + if (!node_req) + return QDF_STATUS_E_NOMEM; + + *node_req = *req; + iface->roam_scan_stats_req = node_req; + cmd.vdev_id = req->vdev_id; + + if (wmi_unified_send_roam_scan_stats_cmd(wma_handle->wmi_handle, + &cmd)) { + WMA_LOGE("%s: Failed to send WMI_REQUEST_ROAM_SCAN_STATS_CMDID", + __func__); + iface->roam_scan_stats_req = NULL; + qdf_mem_free(node_req); + return QDF_STATUS_E_FAILURE; + } + + WMA_LOGD("%s: Exit", __func__); + + return QDF_STATUS_SUCCESS; +} + +void wma_remove_bss_peer_on_vdev_start_failure(tp_wma_handle wma, + uint8_t vdev_id) +{ + uint8_t pdev_id = WMI_PDEV_ID_SOC; + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + QDF_STATUS status; + struct qdf_mac_addr bss_peer; + struct wma_txrx_node *iface; + + iface = &wma->interfaces[vdev_id]; + + status = mlme_get_vdev_bss_peer_mac_addr(iface->vdev, &bss_peer); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to get bssid", __func__); + return; + } + + WMA_LOGE("%s: ADD BSS failure for vdev %d", __func__, vdev_id); + + if (!cdp_find_peer_exist(soc, pdev_id, bss_peer.bytes)) { + WMA_LOGE("%s Failed to find peer "QDF_MAC_ADDR_FMT, + __func__, QDF_MAC_ADDR_REF(bss_peer.bytes)); + return; + } + + wma_remove_peer(wma, bss_peer.bytes, vdev_id, false); +} + +QDF_STATUS wma_sta_vdev_up_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + uint8_t vdev_id; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + QDF_STATUS status; + struct wma_txrx_node *iface; + + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + vdev_id = wlan_vdev_get_id(vdev_mlme->vdev); + iface = &wma->interfaces[vdev_id]; + vdev_mlme->proto.sta.assoc_id = iface->aid; + + status = vdev_mgr_up_send(vdev_mlme); + + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to send vdev up cmd: vdev %d", + __func__, vdev_id); + status = QDF_STATUS_E_FAILURE; + } else { + wma_set_vdev_mgmt_rate(wma, vdev_id); + if (iface->beacon_filter_enabled) + wma_add_beacon_filter( + wma, + &iface->beacon_filter); + } + + return QDF_STATUS_SUCCESS; +} + +bool wma_get_hidden_ssid_restart_in_progress(struct wma_txrx_node *iface) +{ + if (!iface) + return false; + + return ap_mlme_is_hidden_ssid_restart_in_progress(iface->vdev); +} + +bool wma_get_channel_switch_in_progress(struct wma_txrx_node *iface) +{ + if (!iface) + return false; + + return mlme_is_chan_switch_in_progress(iface->vdev); +} + +static QDF_STATUS wma_vdev_send_start_resp(tp_wma_handle wma, + struct add_bss_rsp *add_bss_rsp) +{ + WMA_LOGD(FL("Sending add bss rsp to umac(vdev %d status %d)"), + add_bss_rsp->vdev_id, add_bss_rsp->status); + lim_handle_add_bss_rsp(wma->mac_context, add_bss_rsp); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_sta_mlme_vdev_start_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + enum vdev_assoc_type assoc_type; + + if (!wma) { + wma_err("Invalid wma handle"); + return QDF_STATUS_E_FAILURE; + } + if (mlme_is_chan_switch_in_progress(vdev_mlme->vdev)) { + mlme_set_chan_switch_in_progress(vdev_mlme->vdev, false); + lim_process_switch_channel_rsp(wma->mac_context, data); + return QDF_STATUS_SUCCESS; + } + + assoc_type = mlme_get_assoc_type(vdev_mlme->vdev); + switch (assoc_type) { + case VDEV_ASSOC: + case VDEV_REASSOC: + lim_process_switch_channel_rsp(wma->mac_context, data); + break; + case VDEV_FT_REASSOC: + lim_handle_add_bss_rsp(wma->mac_context, data); + break; + default: + WMA_LOGE(FL("assoc_type %d is invalid"), assoc_type); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_sta_mlme_vdev_roam_notify(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + tp_wma_handle wma; + int ret; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + ret = wma_mlme_roam_synch_event_handler_cb(wma, data, data_len); + if (ret != 0) { + wma_err("Failed to process roam synch event"); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +QDF_STATUS wma_ap_mlme_vdev_start_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + tp_wma_handle wma; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev; + uint8_t vdev_id; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + if (mlme_is_chan_switch_in_progress(vdev)) { + mlme_set_chan_switch_in_progress(vdev, false); + lim_process_switch_channel_rsp(wma->mac_context, data); + } else if (ap_mlme_is_hidden_ssid_restart_in_progress(vdev)) { + vdev_id = vdev->vdev_objmgr.vdev_id; + lim_process_mlm_update_hidden_ssid_rsp(wma->mac_context, + vdev_id); + ap_mlme_set_hidden_ssid_restart_in_progress(vdev, false); + } else { + status = wma_vdev_send_start_resp(wma, data); + } + + return status; +} + +QDF_STATUS wma_mlme_vdev_stop_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + return __wma_handle_vdev_stop_rsp( + (struct vdev_stop_response *)data); +} + +QDF_STATUS wma_ap_mlme_vdev_down_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + wma_send_vdev_down(wma, data); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wma_mlme_vdev_notify_down_complete(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + tp_wma_handle wma; + QDF_STATUS status; + uint32_t vdev_stop_type; + struct del_bss_resp *resp = (struct del_bss_resp *)data; + + if (mlme_is_connection_fail(vdev_mlme->vdev) || + mlme_get_vdev_start_failed(vdev_mlme->vdev)) { + WMA_LOGD("%s Vdev start req failed, no action required", + __func__); + mlme_set_connection_fail(vdev_mlme->vdev, false); + mlme_set_vdev_start_failed(vdev_mlme->vdev, false); + return QDF_STATUS_SUCCESS; + } + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + status = QDF_STATUS_E_INVAL; + goto end; + } + + status = mlme_get_vdev_stop_type(wma->interfaces[resp->vdev_id].vdev, + &vdev_stop_type); + if (QDF_IS_STATUS_ERROR(status)) { + WMA_LOGE("%s: Failed to get msg_type", __func__); + status = QDF_STATUS_E_INVAL; + goto end; + } + + if (vdev_stop_type == WMA_DELETE_BSS_HO_FAIL_REQ) { + resp->status = QDF_STATUS_SUCCESS; + wma_send_msg_high_priority(wma, WMA_DELETE_BSS_HO_FAIL_RSP, + (void *)resp, 0); + return QDF_STATUS_SUCCESS; + } + + if (vdev_stop_type == WMA_SET_LINK_STATE) { + lim_join_result_callback(wma->mac_context, + wlan_vdev_get_id(vdev_mlme->vdev)); + } else { + wma_send_del_bss_response(wma, resp); + return QDF_STATUS_SUCCESS; + } + +end: + qdf_mem_free(resp); + + return status; +} + +QDF_STATUS wma_ap_mlme_vdev_stop_start_send(struct vdev_mlme_obj *vdev_mlme, + enum vdev_cmd_type type, + uint16_t data_len, void *data) +{ + tp_wma_handle wma; + struct add_bss_rsp *add_bss_rsp = data; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + if (wma_send_vdev_stop_to_fw(wma, add_bss_rsp->vdev_id)) + WMA_LOGE(FL("Failed to send vdev stop for vdev id %d"), + add_bss_rsp->vdev_id); + + wma_remove_bss_peer_on_vdev_start_failure(wma, add_bss_rsp->vdev_id); + + return wma_vdev_send_start_resp(wma, add_bss_rsp); +} + +QDF_STATUS wma_mon_mlme_vdev_start_continue(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + if (mlme_is_chan_switch_in_progress(vdev_mlme->vdev)) + mlme_set_chan_switch_in_progress(vdev_mlme->vdev, false); + + lim_process_switch_channel_rsp(wma->mac_context, data); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wma_mon_mlme_vdev_up_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + uint8_t vdev_id; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + QDF_STATUS status; + struct wma_txrx_node *iface; + + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + vdev_id = wlan_vdev_get_id(vdev_mlme->vdev); + iface = &wma->interfaces[vdev_id]; + vdev_mlme->proto.sta.assoc_id = 0; + + status = vdev_mgr_up_send(vdev_mlme); + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("%s: Failed to send vdev up cmd: vdev %d", + __func__, vdev_id); + + return status; +} + +QDF_STATUS wma_mon_mlme_vdev_stop_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + uint8_t vdev_id; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + QDF_STATUS status; + + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + vdev_id = wlan_vdev_get_id(vdev_mlme->vdev); + + status = wma_send_vdev_stop_to_fw(wma, vdev_id); + + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("%s: Failed to send vdev stop cmd: vdev %d", + __func__, vdev_id); + + wlan_vdev_mlme_sm_deliver_evt(vdev_mlme->vdev, + WLAN_VDEV_SM_EV_MLME_DOWN_REQ, + 0, + NULL); + + return status; +} + +QDF_STATUS wma_mon_mlme_vdev_down_send(struct vdev_mlme_obj *vdev_mlme, + uint16_t data_len, void *data) +{ + uint8_t vdev_id; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + QDF_STATUS status; + + if (!wma) { + WMA_LOGE("%s wma handle is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + vdev_id = wlan_vdev_get_id(vdev_mlme->vdev); + + status = wma_send_vdev_down_to_fw(wma, vdev_id); + + if (QDF_IS_STATUS_ERROR(status)) + WMA_LOGE("%s: Failed to send vdev down cmd: vdev %d", + __func__, vdev_id); + + wlan_vdev_mlme_sm_deliver_evt(vdev_mlme->vdev, + WLAN_VDEV_SM_EV_DOWN_COMPLETE, + 0, + NULL); + + return status; +} + +#ifdef FEATURE_WLM_STATS +int wma_wlm_stats_req(int vdev_id, uint32_t bitmask, uint32_t max_size, + wma_wlm_stats_cb cb, void *cookie) +{ + tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); + wmi_unified_t wmi_handle; + wmi_buf_t wmi_buf; + uint32_t buf_len, tlv_tag, tlv_len; + wmi_request_wlm_stats_cmd_fixed_param *cmd; + QDF_STATUS status; + + if (!wma_handle) { + wma_err("Invalid wma handle"); + return -EINVAL; + } + + wmi_handle = wma_handle->wmi_handle; + if (!wmi_handle) { + wma_err("Invalid wmi handle for wlm_stats_event_handler"); + return -EINVAL; + } + + if (!wmi_service_enabled(wmi_handle, wmi_service_wlm_stats_support)) { + wma_err("Feature not supported by firmware"); + return -ENOTSUPP; + } + + wma_handle->wlm_data.wlm_stats_cookie = cookie; + wma_handle->wlm_data.wlm_stats_callback = cb; + wma_handle->wlm_data.wlm_stats_max_size = max_size; + + buf_len = sizeof(*cmd); + wmi_buf = wmi_buf_alloc(wma_handle->wmi_handle, buf_len); + if (!wmi_buf) + return -EINVAL; + + cmd = (void *)wmi_buf_data(wmi_buf); + + tlv_tag = WMITLV_TAG_STRUC_wmi_request_wlm_stats_cmd_fixed_param; + tlv_len = + WMITLV_GET_STRUCT_TLVLEN(wmi_request_wlm_stats_cmd_fixed_param); + WMITLV_SET_HDR(&cmd->tlv_header, tlv_tag, tlv_len); + + cmd->vdev_id = vdev_id; + cmd->request_bitmask = bitmask; + status = wmi_unified_cmd_send(wma_handle->wmi_handle, wmi_buf, buf_len, + WMI_REQUEST_WLM_STATS_CMDID); + if (QDF_IS_STATUS_ERROR(status)) { + wmi_buf_free(wmi_buf); + return -EINVAL; + } + /* info logging per test team request */ + wma_info("---->sent request for vdev:%d", vdev_id); + + return 0; +} + +int wma_wlm_stats_rsp(void *wma_ctx, uint8_t *event, uint32_t evt_len) +{ + WMI_WLM_STATS_EVENTID_param_tlvs *param_tlvs; + wmi_wlm_stats_event_fixed_param *param; + tp_wma_handle wma_handle = wma_ctx; + char *data; + void *cookie; + uint32_t *raw_data; + uint32_t len, buffer_size, raw_data_num, i; + + if (!wma_handle) { + wma_err("Invalid wma handle"); + return -EINVAL; + } + if (!wma_handle->wlm_data.wlm_stats_callback) { + wma_err("No callback registered"); + return -EINVAL; + } + + param_tlvs = (WMI_WLM_STATS_EVENTID_param_tlvs *)event; + param = param_tlvs->fixed_param; + if (!param) { + wma_err("Fix size param is not present, something is wrong"); + return -EINVAL; + } + + /* info logging per test team request */ + wma_info("---->Received response for vdev:%d", param->vdev_id); + + raw_data = param_tlvs->data; + raw_data_num = param_tlvs->num_data; + + len = 0; + buffer_size = wma_handle->wlm_data.wlm_stats_max_size; + data = qdf_mem_malloc(buffer_size); + if (!data) + return -ENOMEM; + + len += qdf_scnprintf(data + len, buffer_size - len, "\n%x ", + param->request_bitmask); + len += qdf_scnprintf(data + len, buffer_size - len, "%x ", + param->vdev_id); + len += qdf_scnprintf(data + len, buffer_size - len, "%x ", + param->timestamp); + len += qdf_scnprintf(data + len, buffer_size - len, "%x ", + param->req_interval); + if (!raw_data) + goto send_data; + + len += qdf_scnprintf(data + len, buffer_size - len, "\ndata:\n"); + + for (i = 0; i < raw_data_num; i++) + len += qdf_scnprintf(data + len, buffer_size - len, "%x ", + *raw_data++); + +send_data: + cookie = wma_handle->wlm_data.wlm_stats_cookie; + wma_handle->wlm_data.wlm_stats_callback(cookie, data); + + qdf_mem_free(data); + + return 0; +} +#endif /* FEATURE_WLM_STATS */ + +#ifdef FEATURE_WLAN_DIAG_SUPPORT +static QDF_STATUS wma_send_cold_boot_cal_data(uint8_t *data, + wmi_cold_boot_cal_data_fixed_param *event) +{ + struct host_log_cold_boot_cal_data_type *log_ptr = NULL; + + WLAN_HOST_DIAG_LOG_ALLOC(log_ptr, + struct host_log_cold_boot_cal_data_type, + LOG_WLAN_COLD_BOOT_CAL_DATA_C); + + if (!log_ptr) + return QDF_STATUS_E_NOMEM; + + log_ptr->version = VERSION_LOG_WLAN_COLD_BOOT_CAL_DATA_C; + log_ptr->cb_cal_data_len = event->data_len; + log_ptr->flags = event->flags; + qdf_mem_copy(log_ptr->cb_cal_data, data, log_ptr->cb_cal_data_len); + + WLAN_HOST_DIAG_LOG_REPORT(log_ptr); + + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS wma_send_cold_boot_cal_data(uint8_t *data, + wmi_cold_boot_cal_data_fixed_param *event) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +int wma_cold_boot_cal_event_handler(void *wma_ctx, uint8_t *event_buff, + uint32_t len) +{ + WMI_PDEV_COLD_BOOT_CAL_DATA_EVENTID_param_tlvs *param_buf; + wmi_cold_boot_cal_data_fixed_param *event; + QDF_STATUS status; + tp_wma_handle wma_handle = (tp_wma_handle)wma_ctx; + + if (!wma_handle) { + wma_err("NULL wma handle"); + return -EINVAL; + } + + param_buf = + (WMI_PDEV_COLD_BOOT_CAL_DATA_EVENTID_param_tlvs *)event_buff; + if (!param_buf) { + wma_err("Invalid Cold Boot Cal Event"); + return -EINVAL; + } + + event = param_buf->fixed_param; + if ((event->data_len > param_buf->num_data) || + (param_buf->num_data > HOST_LOG_MAX_COLD_BOOT_CAL_DATA_SIZE)) { + WMA_LOGE("Excess data_len:%d, num_data:%d", event->data_len, + param_buf->num_data); + return -EINVAL; + } + + status = wma_send_cold_boot_cal_data((uint8_t *)param_buf->data, event); + if (status != QDF_STATUS_SUCCESS) { + wma_err("Cold Boot Cal Diag log not sent"); + return -ENOMEM; + } + + return 0; +} + +#ifdef FEATURE_OEM_DATA +int wma_oem_event_handler(void *wma_ctx, uint8_t *event_buff, uint32_t len) +{ + WMI_OEM_DATA_EVENTID_param_tlvs *param_buf; + struct mac_context *pmac = + (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); + wmi_oem_data_event_fixed_param *event; + struct oem_data oem_event_data; + + if (!pmac) { + wma_err("NULL mac handle"); + return -EINVAL; + } + + if (!pmac->sme.oem_data_event_handler_cb) { + wma_err("oem data handler cb is not registered"); + return -EINVAL; + } + + param_buf = + (WMI_OEM_DATA_EVENTID_param_tlvs *)event_buff; + if (!param_buf) { + wma_err("Invalid oem data Event"); + return -EINVAL; + } + + event = param_buf->fixed_param; + if (!event) { + wma_err("Invalid fixed param in oem data Event"); + return -EINVAL; + } + + if (event->data_len > param_buf->num_data) { + wma_err("Invalid data len %d num_data %d", event->data_len, + param_buf->num_data); + return -EINVAL; + } + + oem_event_data.data_len = event->data_len; + oem_event_data.data = param_buf->data; + pmac->sme.oem_data_event_handler_cb(&oem_event_data, + pmac->sme.oem_data_vdev_id); + + return QDF_STATUS_SUCCESS; +} +#endif diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_utils_ut.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_utils_ut.c new file mode 100644 index 0000000000000000000000000000000000000000..c86c7a50344883d34f06f3e5d997db2198588b88 --- /dev/null +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_utils_ut.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015-2016, 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wma_utils_ut.c + * This file contains utilities related to unit test framework + */ + +/* Header files */ + +#include "wma.h" + +/** + * wma_set_dbs_capability_ut() - Set DBS capability for UT framework + * @dbs: Value of DBS capability to be set + * + * Sets the DBS capability for unit test framework. If the HW mode is + * already sent by the FW, only the DBS capability needs to be set. If the + * FW did not send any HW mode list, a single entry is created and DBS mode + * is set in it. The DBS capability is also set in the service bit map. + * + * Return: None + */ +void wma_set_dbs_capability_ut(uint32_t dbs) +{ + tp_wma_handle wma; + uint32_t i; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + return; + } + + /* DBS list was not configured by the FW, so + * for UT, we can configure a single entry + */ + if (!wma->hw_mode.hw_mode_list) { + wma->num_dbs_hw_modes = 1; + wma->hw_mode.hw_mode_list = + qdf_mem_malloc(sizeof(*wma->hw_mode.hw_mode_list) * + wma->num_dbs_hw_modes); + if (!wma->hw_mode.hw_mode_list) + return; + + wma->hw_mode.hw_mode_list[0] = 0x0000; + } + + for (i = 0; i < wma->num_dbs_hw_modes; i++) { + WMA_HW_MODE_DBS_MODE_SET(wma->hw_mode.hw_mode_list[i], + dbs); + } + + WMI_SERVICE_ENABLE(wma->wmi_service_bitmap, + WMI_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT); +} diff --git a/drivers/staging/qcacld-3.0/os_if/fw_offload/inc/os_if_fwol.h b/drivers/staging/qcacld-3.0/os_if/fw_offload/inc/os_if_fwol.h new file mode 100644 index 0000000000000000000000000000000000000000..8ecbc0ff214523e142e031ff8b623b8e7f01ebac --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/fw_offload/inc/os_if_fwol.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: os_if_fwol.h + * + * This Header file provide declaration for OS interface API + */ + +#ifndef __OS_IF_FWOL_H__ +#define __OS_IF_FWOL_H__ + +#include "wlan_objmgr_vdev_obj.h" + +#ifdef WLAN_FEATURE_ELNA +/** + * os_if_fwol_set_elna_bypass() - Set eLNA bypass + * @vdev: Pointer to vdev + * @attr: Pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +int os_if_fwol_set_elna_bypass(struct wlan_objmgr_vdev *vdev, + const struct nlattr *attr); + +/** + * os_if_fwol_get_elna_bypass() - Get eLNA bypass + * @vdev: Pointer to vdev + * @skb: sk buffer to hold nl80211 attributes + * @attr: Pointer to struct nlattr + * + * Return: 0 on success; error number otherwise + */ +int os_if_fwol_get_elna_bypass(struct wlan_objmgr_vdev *vdev, + struct sk_buff *skb, + const struct nlattr *attr); +#else +static inline int os_if_fwol_set_elna_bypass(struct wlan_objmgr_vdev *vdev, + const struct nlattr *attr) +{ + return 0; +} + +static inline int os_if_fwol_get_elna_bypass(struct wlan_objmgr_vdev *vdev, + struct sk_buff *skb, + const struct nlattr *attr) +{ + return 0; +} +#endif /* WLAN_FEATURE_ELNA */ + +#ifdef WLAN_SEND_DSCP_UP_MAP_TO_FW +/** + * os_if_fwol_send_dscp_up_map_to_fw() - Send DSCP to UP map to FW + * @vdev: Pointer to vdev + * @dscp_to_up_map: Array of DSCP to UP map values + * + * Return: 0 on success; error number otherwise + */ +int os_if_fwol_send_dscp_up_map_to_fw(struct wlan_objmgr_vdev *vdev, + uint32_t *dscp_to_up_map); +#else +static inline int os_if_fwol_send_dscp_up_map_to_fw( + struct wlan_objmgr_vdev *vdev, uint32_t *dscp_to_up_map) +{ + return -EOPNOTSUPP; +} +#endif /* WLAN_SEND_DSCP_UP_MAP_TO_FW */ + +#endif /* __OS_IF_FWOL_H__ */ diff --git a/drivers/staging/qcacld-3.0/os_if/fw_offload/src/os_if_fwol.c b/drivers/staging/qcacld-3.0/os_if/fw_offload/src/os_if_fwol.c new file mode 100644 index 0000000000000000000000000000000000000000..90ee99744f5e56165f26f151c8db13200cc16c97 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/fw_offload/src/os_if_fwol.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: defines driver functions interfacing with linux kernel + */ + +#include "wlan_cfg80211.h" +#include "wlan_osif_request_manager.h" +#include "wlan_fwol_public_structs.h" +#include "wlan_fwol_ucfg_api.h" +#include "os_if_fwol.h" + +#ifdef WLAN_FEATURE_ELNA +#define WLAN_WAIT_TIME_GET_ELNA_BYPASS 1500 + +int os_if_fwol_set_elna_bypass(struct wlan_objmgr_vdev *vdev, + const struct nlattr *attr) +{ + struct set_elna_bypass_request req; + QDF_STATUS status; + + req.vdev_id = vdev->vdev_objmgr.vdev_id; + req.en_dis = nla_get_u8(attr); + if (req.en_dis > 1) { + osif_err("Invalid elna_bypass value %d", req.en_dis); + return -EINVAL; + } + + status = ucfg_fwol_set_elna_bypass(vdev, &req); + if (!QDF_IS_STATUS_SUCCESS(status)) + osif_err("Failed to set ELNA BYPASS, %d", status); + + return qdf_status_to_os_return(status); +} + +struct osif_get_elna_bypass_priv { + uint8_t en_dis; +}; + +/** + * os_if_fwol_get_elna_bypass_callback() - Get eLNA bypass callback + * @context: Call context + * @response: Pointer to response structure + * + * Return: void + */ +static void +os_if_fwol_get_elna_bypass_callback(void *context, + struct get_elna_bypass_response *response) +{ + struct osif_request *request; + struct osif_get_elna_bypass_priv *priv; + + request = osif_request_get(context); + if (!request) { + osif_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + priv->en_dis = response->en_dis; + + osif_request_complete(request); + osif_request_put(request); +} + +int os_if_fwol_get_elna_bypass(struct wlan_objmgr_vdev *vdev, + struct sk_buff *skb, + const struct nlattr *attr) +{ + struct get_elna_bypass_request req; + void *cookie; + struct osif_request *request; + struct osif_get_elna_bypass_priv *priv; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = WLAN_WAIT_TIME_GET_ELNA_BYPASS, + }; + QDF_STATUS status; + int ret = 0; + + req.vdev_id = vdev->vdev_objmgr.vdev_id; + + request = osif_request_alloc(¶ms); + if (!request) { + osif_err("Request allocation failure"); + return -ENOMEM; + } + cookie = osif_request_cookie(request); + + status = ucfg_fwol_get_elna_bypass(vdev, &req, + os_if_fwol_get_elna_bypass_callback, + cookie); + if (!QDF_IS_STATUS_SUCCESS(status)) { + osif_err("Failed to get ELNA BYPASS, %d", status); + ret = qdf_status_to_os_return(status); + goto end; + } + + ret = osif_request_wait_for_response(request); + if (ret) { + osif_err("Operation timed out"); + goto end; + } + + priv = osif_request_priv(request); + if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS, + priv->en_dis)) { + osif_err("put fail"); + ret = -EINVAL; + } + +end: + osif_request_put(request); + return ret; +} +#endif /* #ifdef WLAN_FEATURE_ELNA */ + +#ifdef WLAN_SEND_DSCP_UP_MAP_TO_FW +int os_if_fwol_send_dscp_up_map_to_fw(struct wlan_objmgr_vdev *vdev, + uint32_t *dscp_to_up_map) +{ + QDF_STATUS status; + + status = ucfg_fwol_send_dscp_up_map_to_fw(vdev, dscp_to_up_map); + if (!QDF_IS_STATUS_SUCCESS(status)) + osif_err("Failed to send dscp_up_map to FW, %d", status); + + return qdf_status_to_os_return(status); +} +#endif diff --git a/drivers/staging/qcacld-3.0/os_if/interop_issues_ap/inc/wlan_cfg80211_interop_issues_ap.h b/drivers/staging/qcacld-3.0/os_if/interop_issues_ap/inc/wlan_cfg80211_interop_issues_ap.h new file mode 100644 index 0000000000000000000000000000000000000000..24a7cd25c539a8c6817053340149e5fda4f1ffdb --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/interop_issues_ap/inc/wlan_cfg80211_interop_issues_ap.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_cfg80211_interop_issues_ap.h + * + * This Header file provide declaration for cfg80211 command handler API + */ + +#ifndef __WLAN_CFG80211_INTEROP_ISSUES_AP_H__ +#define __WLAN_CFG80211_INTEROP_ISSUES_AP_H__ + +#include +#include +#include + +#ifdef WLAN_FEATURE_INTEROP_ISSUES_AP + +#define FEATURE_INTEROP_ISSUES_AP_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV | \ + WIPHY_VENDOR_CMD_NEED_RUNNING, \ + .doit = wlan_cfg80211_set_interop_issues_ap_config \ +}, + +#define FEATURE_INTEROP_ISSUES_AP_VENDOR_COMMANDS_INDEX \ + [QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP_INDEX] = { \ + .vendor_id = QCA_NL80211_VENDOR_ID, \ + .subcmd = QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP \ + }, + +/** + * wlan_cfg80211_init_interop_issues_ap() - init interop issues ap setting + * @pdev: the pointer of pdev object + * + * Return: none + */ +void wlan_cfg80211_init_interop_issues_ap(struct wlan_objmgr_pdev *pdev); + +/** + * wlan_cfg80211_set_interop_issues_ap_config() - set interop issues ap config + * @wiphy: pointer to wireless wiphy structure + * @wdev: pointer to wireless_dev structure + * @data: Pointer to the data to be passed via vendor interface + * @data_len: Length of the data to be passed + * + * Return: Return the Success or Failure code + */ +int wlan_cfg80211_set_interop_issues_ap_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); +#else +static inline +void wlan_cfg80211_init_interop_issues_ap(struct wlan_objmgr_pdev *pdev) {} +#define FEATURE_INTEROP_ISSUES_AP_VENDOR_COMMANDS +#define FEATURE_INTEROP_ISSUES_AP_VENDOR_COMMANDS_INDEX +#endif +#endif /* __WLAN_CFG80211_INTEROP_ISSUES_AP_H__ */ diff --git a/drivers/staging/qcacld-3.0/os_if/interop_issues_ap/src/wlan_cfg80211_interop_issues_ap.c b/drivers/staging/qcacld-3.0/os_if/interop_issues_ap/src/wlan_cfg80211_interop_issues_ap.c new file mode 100644 index 0000000000000000000000000000000000000000..2357ab1db4a7a45b7555f4b582fa305745a69fc8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/interop_issues_ap/src/wlan_cfg80211_interop_issues_ap.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: defines driver functions interfacing with linux kernel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_hdd_main.h" +#include "cfg_ucfg_api.h" + +const struct nla_policy +interop_issues_ap_policy[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE] = { + .type = NLA_U32, + .len = sizeof(uint32_t) }, + [QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST] = { + .type = NLA_U32, + .len = sizeof(uint32_t) }, + [QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID] = { + .type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE }, +}; + +/** + * wlan_cfg80211_send_interop_issues_ap_cb() - report information + * @data: interop issues ap mac received from fw + * + * Generate a wlan interop issues ap info package and send it to user + * space daemon through netlink. + * + * Return: none + */ +static void +wlan_cfg80211_send_interop_issues_ap_cb( + struct wlan_interop_issues_ap_event *data) +{ + struct wlan_objmgr_pdev *pdev; + struct pdev_osif_priv *os_priv; + struct sk_buff *skb; + uint32_t index, len; + + if (!data) { + osif_err("Invalid result."); + return; + } + + pdev = data->pdev; + if (!pdev) { + osif_err("pdev is null."); + return; + } + os_priv = wlan_pdev_get_ospriv(pdev); + if (!os_priv) { + osif_err("os_priv is null."); + return; + } + + index = QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP_INDEX; + len = nla_total_size(QDF_MAC_ADDR_SIZE + NLMSG_HDRLEN); + skb = cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, len, index, + GFP_KERNEL); + if (!skb) { + osif_err("skb alloc failed"); + return; + } + + osif_debug("interop issues ap mac:" QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(data->rap_addr.bytes)); + + if (nla_put(skb, + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID, + QDF_MAC_ADDR_SIZE, data->rap_addr.bytes)) { + osif_err("nla put fail"); + kfree_skb(skb); + return; + } + + cfg80211_vendor_event(skb, GFP_KERNEL); +} + +static void wlan_interop_issues_ap_register_cbk(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_interop_issues_ap_callbacks cb; + + cb.os_if_interop_issues_ap_event_handler = + wlan_cfg80211_send_interop_issues_ap_cb; + ucfg_register_interop_issues_ap_callback(pdev, &cb); +} + +/** + * wlan_parse_interop_issues_ap() - parse the interop issues ap info + * @interop_issues_ap: the pointer of interop issues ap + * @attr: list of attributes + * + * Return: 0 on success; error number on failure + */ +static int +wlan_parse_interop_issues_ap(struct qdf_mac_addr *interop_issues_ap, + struct nlattr *attr) +{ + struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX + 1]; + struct nlattr *curr_attr = NULL; + uint32_t rem; + qdf_size_t i = 0; + + nla_for_each_nested(curr_attr, attr, rem) { + if (i == MAX_INTEROP_ISSUES_AP_NUM) { + osif_err("Ignoring excess"); + break; + } + + if (wlan_cfg80211_nla_parse(tb2, + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX, + nla_data(curr_attr), + nla_len(curr_attr), + interop_issues_ap_policy)) { + osif_err("nla_parse failed"); + return -EINVAL; + } + if (!tb2[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID]) { + osif_err("attr addr failed"); + return -EINVAL; + } + nla_memcpy(interop_issues_ap[i].bytes, + tb2[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID], + QDF_MAC_ADDR_SIZE); + osif_debug(QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(interop_issues_ap[i].bytes)); + i++; + } + + return i; +} + +/** + * __wlan_cfg80211_set_interop_issues_ap_config() - set config status + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * Return: 0 on success and errno on failure + */ +static int +__wlan_cfg80211_set_interop_issues_ap_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX + 1]; + struct nlattr *attr; + uint32_t count = 0; + struct wlan_interop_issues_ap_info interop_issues_ap = {0}; + struct wlan_objmgr_psoc *psoc; + + if (!adapter->vdev) { + osif_err("Invalid vdev"); + return -EINVAL; + } + + psoc = wlan_vdev_get_psoc(adapter->vdev); + if (!psoc) { + osif_err("Invalid psoc"); + return -EINVAL; + } + + if (wlan_cfg80211_nla_parse(tb, + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX, + data, data_len, + interop_issues_ap_policy)) { + osif_err("Invalid ATTR"); + return -EINVAL; + } + + attr = tb[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST]; + if (attr) { + count = + wlan_parse_interop_issues_ap(interop_issues_ap.rap_items, + attr); + if (count < 0) + return -EINVAL; + } + + osif_debug("Num of interop issues ap: %d", count); + interop_issues_ap.count = count; + interop_issues_ap.detect_enable = true; + + /* + * need to figure out a converged way of obtaining the vdev for + * a given netdev that doesn't involve the legacy mechanism. + */ + ucfg_set_interop_issues_ap_config(psoc, &interop_issues_ap); + + return 0; +} + +int wlan_cfg80211_set_interop_issues_ap_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int ret; + + ret = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (ret) + return ret; + + ret = __wlan_cfg80211_set_interop_issues_ap_config(wiphy, wdev, + data, data_len); + osif_psoc_sync_op_stop(psoc_sync); + + return ret; +} + +void wlan_cfg80211_init_interop_issues_ap(struct wlan_objmgr_pdev *pdev) +{ + /* + * the special mac is used to trigger uplayer sets + * interop issues ap list to fw when driver reloads but + * cnss-daemon does not restart. + */ + uint8_t fmac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct wlan_interop_issues_ap_info interop_issues_ap = {0}; + struct wlan_interop_issues_ap_event data; + struct wlan_objmgr_psoc *psoc; + + wlan_interop_issues_ap_register_cbk(pdev); + + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) { + osif_err("Invalid psoc"); + return; + } + interop_issues_ap.detect_enable = true; + ucfg_set_interop_issues_ap_config(psoc, &interop_issues_ap); + + data.pdev = pdev; + qdf_mem_copy(data.rap_addr.bytes, fmac, QDF_MAC_ADDR_SIZE); + + wlan_cfg80211_send_interop_issues_ap_cb(&data); +} diff --git a/drivers/staging/qcacld-3.0/os_if/nan/inc/os_if_nan.h b/drivers/staging/qcacld-3.0/os_if/nan/inc/os_if_nan.h new file mode 100644 index 0000000000000000000000000000000000000000..3c474fd489babff028e67a1a626eeeff4cba80bc --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/nan/inc/os_if_nan.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2012-2019, 2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: declares nan component os interface APIs + */ + +#ifndef _OS_IF_NAN_H_ +#define _OS_IF_NAN_H_ + +#include "qdf_types.h" +#ifdef WLAN_FEATURE_NAN +#include "nan_public_structs.h" +#include "nan_ucfg_api.h" + +/** + * struct ndi_find_vdev_filter - find vdev filter object. this can be extended + * @ifname: interface name of vdev + * @found_vdev: found vdev object matching one or more of above params + */ +struct ndi_find_vdev_filter { + char *ifname; + struct wlan_objmgr_vdev *found_vdev; +}; + +/** + * os_if_nan_process_ndp_cmd: os_if api to handle nan request message + * @psoc: pointer to psoc object + * @data: request data. contains vendor cmd tlvs + * @data_len: length of data + * @is_ndp_allowed: Indicates whether to allow NDP creation. + * NDI creation is always allowed. + * + * Return: status of operation + */ +int os_if_nan_process_ndp_cmd(struct wlan_objmgr_psoc *psoc, + const void *data, int data_len, + bool is_ndp_allowed); + +/** + * os_if_nan_register_hdd_callbacks: os_if api to register hdd callbacks + * @psoc: pointer to psoc object + * @cb_obj: struct pointer containing callbacks + * + * Return: status of operation + */ +int os_if_nan_register_hdd_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj); + +/** + * os_if_nan_register_lim_callbacks: os_if api to register lim callbacks + * @psoc: pointer to psoc object + * @cb_obj: struct pointer containing callbacks + * + * Return: status of operation + */ +int os_if_nan_register_lim_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj); + +/** + * os_if_nan_post_ndi_create_rsp: os_if api to pos ndi create rsp to umac nan + * component + * @psoc: pointer to psoc object + * @vdev_id: vdev id of ndi + * @success: if create was success or failure + * + * Return: None + */ +void os_if_nan_post_ndi_create_rsp(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool success); + +/** + * os_if_nan_post_ndi_delete_rsp: os_if api to pos ndi delete rsp to umac nan + * component + * @psoc: pointer to psoc object + * @vdev_id: vdev id of ndi + * @success: if delete was success or failure + * + * Return: None + */ +void os_if_nan_post_ndi_delete_rsp(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool success); + +/** + * os_if_nan_ndi_session_end: os_if api to process ndi session end + * component + * @vdev: pointer to vdev deleted + * + * Return: None + */ +void os_if_nan_ndi_session_end(struct wlan_objmgr_vdev *vdev); + +/** + * os_if_nan_set_ndi_state: os_if api set NDI state + * @vdev: pointer to vdev deleted + * @state: value to set + * + * Return: status of operation + */ +static inline QDF_STATUS os_if_nan_set_ndi_state(struct wlan_objmgr_vdev *vdev, + uint32_t state) +{ + return ucfg_nan_set_ndi_state(vdev, state); +} + +/** + * os_if_nan_set_ndp_create_transaction_id: set ndp create transaction id + * @vdev: pointer to vdev object + * @val: value to set + * + * Return: status of operation + */ +static inline QDF_STATUS os_if_nan_set_ndp_create_transaction_id( + struct wlan_objmgr_vdev *vdev, + uint16_t val) +{ + return ucfg_nan_set_ndp_create_transaction_id(vdev, val); +} + +/** + * os_if_nan_set_ndp_delete_transaction_id: set ndp delete transaction id + * @vdev: pointer to vdev object + * @val: value to set + * + * Return: status of operation + */ +static inline QDF_STATUS os_if_nan_set_ndp_delete_transaction_id( + struct wlan_objmgr_vdev *vdev, + uint16_t val) +{ + return ucfg_nan_set_ndp_delete_transaction_id(vdev, val); +} + +/** + * os_if_process_nan_req: os_if api to handle NAN requests attached to the + * vendor command QCA_NL80211_VENDOR_SUBCMD_NAN_EXT + * @psoc: pointer to psoc object + * @vdev_id: NAN vdev id + * @data: request data. contains vendor cmd tlvs + * @data_len: length of data + * + * Return: status of operation + */ +int os_if_process_nan_req(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + const void *data, int data_len); +#else + +static inline void os_if_nan_post_ndi_create_rsp(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool success) +{ +} + +static inline void os_if_nan_post_ndi_delete_rsp(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool success) +{ +} + +#endif /* WLAN_FEATURE_NAN */ + +#endif diff --git a/drivers/staging/qcacld-3.0/os_if/nan/src/os_if_nan.c b/drivers/staging/qcacld-3.0/os_if/nan/src/os_if_nan.c new file mode 100644 index 0000000000000000000000000000000000000000..b171d90a789a699e662b6d34afcee2104578d2d5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/nan/src/os_if_nan.c @@ -0,0 +1,2649 @@ +/* + * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: defines nan component os interface APIs + */ + +#include "osif_sync.h" +#include "qdf_str.h" +#include "qdf_trace.h" +#include "qdf_types.h" +#include "os_if_nan.h" +#include "wlan_nan_api.h" +#include "nan_ucfg_api.h" +#include "wlan_osif_priv.h" +#include +#include "wlan_cfg80211.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_vdev_obj.h" +#include "wlan_utility.h" +#include "wlan_osif_request_manager.h" +#include "wlan_mlme_ucfg_api.h" + +#define NAN_CMD_MAX_SIZE 2048 + +/* NLA policy */ +static const struct nla_policy +nan_attr_policy[QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA] = { + .type = NLA_BINARY, + .len = NAN_CMD_MAX_SIZE + }, + [QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NAN_DISC_24GHZ_BAND_FREQ] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NAN_DISC_5GHZ_BAND_FREQ] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, +}; + +/* NLA policy */ +static const struct nla_policy +vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID] = { + .type = NLA_U16, + .len = sizeof(uint16_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR] = { + .type = NLA_NUL_STRING, + .len = IFNAMSIZ - 1 + }, + [QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR] = { + .type = NLA_UNSPEC, + .len = QDF_MAC_ADDR_SIZE + }, + [QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY] = { + .type = NLA_U16, + .len = sizeof(uint16_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO] = { + .type = NLA_BINARY, + .len = NDP_APP_INFO_LEN + }, + [QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR] = { + .type = NLA_BINARY, + .len = QDF_MAC_ADDR_SIZE + }, + [QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY] = { + .type = NLA_BINARY, + .len = NDP_NUM_INSTANCE_ID + }, + [QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_CSID] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_PMK] = { + .type = NLA_BINARY, + .len = NDP_PMK_LEN + }, + [QCA_WLAN_VENDOR_ATTR_NDP_SCID] = { + .type = NLA_BINARY, + .len = NDP_SCID_BUF_LEN + }, + [QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE] = { + .type = NLA_BINARY, + .len = NAN_PASSPHRASE_MAX_LEN + }, + [QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME] = { + .type = NLA_BINARY, + .len = NAN_MAX_SERVICE_NAME_LEN + }, + [QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO] = { + .type = NLA_BINARY, + .len = NAN_CH_INFO_MAX_LEN + }, + [QCA_WLAN_VENDOR_ATTR_NDP_NSS] = { + .type = NLA_U32, + .len = sizeof(uint32_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR] = { + .type = NLA_UNSPEC, + .len = QDF_IPV6_ADDR_SIZE + }, + [QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT] = { + .type = NLA_U16, + .len = sizeof(uint16_t) + }, + [QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL] = { + .type = NLA_U8, + .len = sizeof(uint8_t) + }, +}; + +/** + * os_if_get_ndi_vdev_by_ifname_cb() - callback function to return vdev object + * from psoc matching given interface name + * @psoc: psoc object + * @obj: object used to iterate the callback function + * @arg: return argument which will be filled by the function + * + * Return : NULL + */ +static void os_if_get_ndi_vdev_by_ifname_cb(struct wlan_objmgr_psoc *psoc, + void *obj, void *arg) +{ + struct wlan_objmgr_vdev *vdev = obj; + struct ndi_find_vdev_filter *filter = arg; + struct vdev_osif_priv *osif_priv; + + if (filter->found_vdev) + return; + + wlan_vdev_obj_lock(vdev); + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + wlan_vdev_obj_unlock(vdev); + return; + } + + if (!osif_priv->wdev) { + wlan_vdev_obj_unlock(vdev); + return; + } + + if (!qdf_str_cmp(osif_priv->wdev->netdev->name, filter->ifname)) + filter->found_vdev = vdev; + + wlan_vdev_obj_unlock(vdev); +} + +/** + * os_if_get_ndi_vdev_by_ifname() - function to return vdev object from psoc + * matching given interface name + * @psoc: psoc object + * @ifname: interface name + * + * This function returns vdev object from psoc by interface name. If found this + * will also take reference with given ref_id + * + * Return : vdev object if found, NULL otherwise + */ +static struct wlan_objmgr_vdev * +os_if_get_ndi_vdev_by_ifname(struct wlan_objmgr_psoc *psoc, char *ifname) +{ + QDF_STATUS status; + struct ndi_find_vdev_filter filter = {0}; + + filter.ifname = ifname; + wlan_objmgr_iterate_obj_list(psoc, WLAN_VDEV_OP, + os_if_get_ndi_vdev_by_ifname_cb, + &filter, 0, WLAN_NAN_ID); + + if (!filter.found_vdev) + return NULL; + + status = wlan_objmgr_vdev_try_get_ref(filter.found_vdev, WLAN_NAN_ID); + if (QDF_IS_STATUS_ERROR(status)) + return NULL; + + return filter.found_vdev; +} + +/** + * os_if_ndi_get_if_name() - get vdev's interface name + * @vdev: VDEV object + * + * API to get vdev's interface name + * + * Return: vdev's interface name + */ +static const uint8_t *os_if_ndi_get_if_name(struct wlan_objmgr_vdev *vdev) +{ + struct vdev_osif_priv *osif_priv; + + wlan_vdev_obj_lock(vdev); + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + wlan_vdev_obj_unlock(vdev); + return NULL; + } + + if (!osif_priv->wdev) { + wlan_vdev_obj_unlock(vdev); + return NULL; + } + + wlan_vdev_obj_unlock(vdev); + + return osif_priv->wdev->netdev->name; +} + +static int __os_if_nan_process_ndi_create(struct wlan_objmgr_psoc *psoc, + char *iface_name, + struct nlattr **tb) +{ + int ret; + QDF_STATUS status; + uint16_t transaction_id; + struct wlan_objmgr_vdev *nan_vdev; + struct nan_callbacks cb_obj; + + osif_debug("enter"); + + nan_vdev = os_if_get_ndi_vdev_by_ifname(psoc, iface_name); + if (nan_vdev) { + osif_err("NAN data interface %s is already present", + iface_name); + wlan_objmgr_vdev_release_ref(nan_vdev, WLAN_NAN_ID); + return -EEXIST; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]) { + osif_err("transaction id is unavailable"); + return -EINVAL; + } + transaction_id = + nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]); + + status = ucfg_nan_get_callbacks(psoc, &cb_obj); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("Couldn't get ballback object"); + return -EINVAL; + } + + ret = cb_obj.ndi_open(iface_name); + if (ret) { + osif_err("ndi_open failed"); + return ret; + } + + return cb_obj.ndi_start(iface_name, transaction_id); +} + +static int +osif_nla_str(struct nlattr **tb, size_t attr_id, char **out_str) +{ + if (!tb || !tb[attr_id]) + return -EINVAL; + + *out_str = nla_data(tb[attr_id]); + + return 0; +} + +static int +osif_device_from_psoc(struct wlan_objmgr_psoc *psoc, struct device **out_dev) +{ + qdf_device_t qdf_dev; + + if (!psoc) + return -EINVAL; + + qdf_dev = wlan_psoc_get_qdf_dev(psoc); + if (!qdf_dev || !qdf_dev->dev) + return -EINVAL; + + *out_dev = qdf_dev->dev; + + return 0; +} + +static int osif_net_dev_from_vdev(struct wlan_objmgr_vdev *vdev, + struct net_device **out_net_dev) +{ + struct vdev_osif_priv *priv; + + if (!vdev) + return -EINVAL; + + priv = wlan_vdev_get_ospriv(vdev); + if (!priv || !priv->wdev || !priv->wdev->netdev) + return -EINVAL; + + *out_net_dev = priv->wdev->netdev; + + return 0; +} + +static int osif_net_dev_from_ifname(struct wlan_objmgr_psoc *psoc, + char *iface_name, + struct net_device **out_net_dev) +{ + struct wlan_objmgr_vdev *vdev; + struct net_device *net_dev; + int errno; + + vdev = os_if_get_ndi_vdev_by_ifname(psoc, iface_name); + if (!vdev) + return -EINVAL; + + errno = osif_net_dev_from_vdev(vdev, &net_dev); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + + if (errno) + return errno; + + *out_net_dev = net_dev; + + return 0; +} + +static int os_if_nan_process_ndi_create(struct wlan_objmgr_psoc *psoc, + struct nlattr **tb) +{ + struct device *dev; + struct net_device *net_dev; + struct osif_vdev_sync *vdev_sync; + char *ifname; + int errno; + + errno = osif_nla_str(tb, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, &ifname); + if (errno) + return errno; + + errno = osif_device_from_psoc(psoc, &dev); + if (errno) + return errno; + + errno = osif_vdev_sync_create_and_trans(dev, &vdev_sync); + if (errno) + return errno; + + errno = __os_if_nan_process_ndi_create(psoc, ifname, tb); + if (errno) + goto destroy_sync; + + errno = osif_net_dev_from_ifname(psoc, ifname, &net_dev); + if (errno) + goto destroy_sync; + + osif_vdev_sync_register(net_dev, vdev_sync); + osif_vdev_sync_trans_stop(vdev_sync); + + return 0; + +destroy_sync: + osif_vdev_sync_trans_stop(vdev_sync); + osif_vdev_sync_destroy(vdev_sync); + + return errno; +} + +static int __os_if_nan_process_ndi_delete(struct wlan_objmgr_psoc *psoc, + char *iface_name, + struct nlattr **tb) +{ + uint8_t vdev_id; + QDF_STATUS status; + uint32_t num_peers; + uint16_t transaction_id; + struct nan_callbacks cb_obj; + struct wlan_objmgr_vdev *nan_vdev = NULL; + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]) { + osif_err("Transaction id is unavailable"); + return -EINVAL; + } + + nan_vdev = os_if_get_ndi_vdev_by_ifname(psoc, iface_name); + if (!nan_vdev) { + osif_debug("Nan datapath interface is not present"); + return -EINVAL; + } + + transaction_id = + nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]); + vdev_id = wlan_vdev_get_id(nan_vdev); + num_peers = ucfg_nan_get_active_peers(nan_vdev); + /* + * os_if_get_ndi_vdev_by_ifname increments ref count + * decrement here since vdev returned by that api is not used any more + */ + wlan_objmgr_vdev_release_ref(nan_vdev, WLAN_NAN_ID); + + /* check if there are active peers on the adapter */ + if (num_peers) + osif_err("NDP peers active: %d, active NDPs may not be terminated", + num_peers); + + status = ucfg_nan_get_callbacks(psoc, &cb_obj); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("Couldn't get ballback object"); + return -EINVAL; + } + + return cb_obj.ndi_delete(vdev_id, iface_name, transaction_id); +} + +static int os_if_nan_process_ndi_delete(struct wlan_objmgr_psoc *psoc, + struct nlattr **tb) +{ + struct net_device *net_dev; + struct osif_vdev_sync *vdev_sync; + char *ifname; + int errno; + + errno = osif_nla_str(tb, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, &ifname); + if (errno) + return errno; + + errno = osif_net_dev_from_ifname(psoc, ifname, &net_dev); + if (errno) + return errno; + + errno = osif_vdev_sync_trans_start_wait(net_dev, &vdev_sync); + if (errno) + return errno; + + osif_vdev_sync_unregister(net_dev); + osif_vdev_sync_wait_for_ops(vdev_sync); + + errno = __os_if_nan_process_ndi_delete(psoc, ifname, tb); + if (errno) + goto reregister; + + osif_vdev_sync_trans_stop(vdev_sync); + osif_vdev_sync_destroy(vdev_sync); + + return 0; + +reregister: + osif_vdev_sync_register(net_dev, vdev_sync); + osif_vdev_sync_trans_stop(vdev_sync); + + return errno; +} + +/** + * os_if_nan_parse_security_params() - parse vendor attributes for security + * params. + * @tb: parsed NL attribute list + * @ncs_sk_type: out parameter to populate ncs_sk_type + * @pmk: out parameter to populate pmk + * @passphrase: out parameter to populate passphrase + * @service_name: out parameter to populate service_name + * + * Return: 0 on success or error code on failure + */ +static int os_if_nan_parse_security_params(struct nlattr **tb, + uint32_t *ncs_sk_type, struct nan_datapath_pmk *pmk, + struct ndp_passphrase *passphrase, + struct ndp_service_name *service_name) +{ + if (!ncs_sk_type || !pmk || !passphrase || !service_name) { + osif_err("out buffers for one ore more parameters is null"); + return -EINVAL; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_CSID]) { + *ncs_sk_type = + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_CSID]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]) { + pmk->pmk_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]); + qdf_mem_copy(pmk->pmk, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]), + pmk->pmk_len); + osif_err("pmk len: %d", pmk->pmk_len); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR, + pmk->pmk, pmk->pmk_len); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE]) { + passphrase->passphrase_len = + nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE]); + qdf_mem_copy(passphrase->passphrase, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE]), + passphrase->passphrase_len); + osif_err("passphrase len: %d", passphrase->passphrase_len); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR, + passphrase->passphrase, + passphrase->passphrase_len); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME]) { + service_name->service_name_len = + nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME]); + qdf_mem_copy(service_name->service_name, + nla_data( + tb[QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME]), + service_name->service_name_len); + osif_err("service_name len: %d", + service_name->service_name_len); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR, + service_name->service_name, + service_name->service_name_len); + } + + return 0; +} + +/** + * __os_if_nan_process_ndp_initiator_req() - NDP initiator request handler + * @ctx: hdd context + * @tb: parsed NL attribute list + * + * tb will contain following vendor attributes: + * QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR + * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID + * QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL - optional + * QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG + * QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID + * QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR + * QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO - optional + * QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS - optional + * QCA_WLAN_VENDOR_ATTR_NDP_PMK - optional + * QCA_WLAN_VENDOR_ATTR_NDP_CSID - optional + * QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE - optional + * QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME - optional + * + * Return: 0 on success or error code on failure + */ +static int __os_if_nan_process_ndp_initiator_req(struct wlan_objmgr_psoc *psoc, + char *iface_name, + struct nlattr **tb) +{ + int ret = 0; + QDF_STATUS status; + enum nan_datapath_state state; + struct wlan_objmgr_vdev *nan_vdev; + struct nan_datapath_initiator_req req = {0}; + + nan_vdev = os_if_get_ndi_vdev_by_ifname(psoc, iface_name); + if (!nan_vdev) { + osif_err("NAN data interface %s not available", iface_name); + return -EINVAL; + } + + if (nan_vdev->vdev_mlme.vdev_opmode != QDF_NDI_MODE) { + osif_err("Interface found is not NDI"); + ret = -EINVAL; + goto initiator_req_failed; + } + + state = ucfg_nan_get_ndi_state(nan_vdev); + if (state == NAN_DATA_NDI_DELETED_STATE || + state == NAN_DATA_NDI_DELETING_STATE || + state == NAN_DATA_NDI_CREATING_STATE) { + osif_err("Data request not allowed in NDI current state: %d", + state); + ret = -EINVAL; + goto initiator_req_failed; + } + + if (!ucfg_nan_is_sta_ndp_concurrency_allowed(psoc, nan_vdev)) { + osif_err("NDP creation not allowed"); + ret = -EOPNOTSUPP; + goto initiator_req_failed; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]) { + osif_err("Transaction ID is unavailable"); + ret = -EINVAL; + goto initiator_req_failed; + } + req.transaction_id = + nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]); + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL]) { + req.channel = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL]); + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG]) { + req.channel_cfg = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG]); + } else { + osif_err("Channel config is unavailable"); + ret = -EINVAL; + goto initiator_req_failed; + } + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID]) { + osif_err("NDP service instance ID is unavailable"); + ret = -EINVAL; + goto initiator_req_failed; + } + req.service_instance_id = + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID]); + + qdf_mem_copy(req.self_ndi_mac_addr.bytes, + wlan_vdev_mlme_get_macaddr(nan_vdev), QDF_MAC_ADDR_SIZE); + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR]) { + osif_err("NDI peer discovery mac addr is unavailable"); + ret = -EINVAL; + goto initiator_req_failed; + } + qdf_mem_copy(req.peer_discovery_mac_addr.bytes, + nla_data( + tb[QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR]), + QDF_MAC_ADDR_SIZE); + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]) { + req.ndp_info.ndp_app_info_len = + nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]); + qdf_mem_copy(req.ndp_info.ndp_app_info, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]), + req.ndp_info.ndp_app_info_len); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS]) { + /* at present ndp config stores 4 bytes QOS info only */ + req.ndp_config.ndp_cfg_len = 4; + *((uint32_t *)req.ndp_config.ndp_cfg) = + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS]); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR]) { + req.is_ipv6_addr_present = true; + qdf_mem_copy(req.ipv6_addr, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR]), + QDF_IPV6_ADDR_SIZE); + } + + if (os_if_nan_parse_security_params(tb, &req.ncs_sk_type, &req.pmk, + &req.passphrase, + &req.service_name)) { + osif_err("inconsistent security params in request."); + ret = -EINVAL; + goto initiator_req_failed; + } + + req.vdev = nan_vdev; + status = ucfg_nan_req_processor(nan_vdev, &req, NDP_INITIATOR_REQ); + ret = qdf_status_to_os_return(status); +initiator_req_failed: + if (ret) + wlan_objmgr_vdev_release_ref(nan_vdev, WLAN_NAN_ID); + + return ret; +} + +static int os_if_nan_process_ndp_initiator_req(struct wlan_objmgr_psoc *psoc, + struct nlattr **tb) +{ + struct net_device *net_dev; + struct osif_vdev_sync *vdev_sync; + char *ifname; + int errno; + + errno = osif_nla_str(tb, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, &ifname); + if (errno) + return errno; + + errno = osif_net_dev_from_ifname(psoc, ifname, &net_dev); + if (errno) + return errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __os_if_nan_process_ndp_initiator_req(psoc, ifname, tb); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __os_if_nan_process_ndp_responder_req() - NDP responder request handler + * @nan_ctx: hdd context + * @tb: parsed NL attribute list + * + * tb includes following vendor attributes: + * QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR + * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID + * QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID + * QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE + * QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO - optional + * QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS - optional + * QCA_WLAN_VENDOR_ATTR_NDP_PMK - optional + * QCA_WLAN_VENDOR_ATTR_NDP_CSID - optional + * QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE - optional + * QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME - optional + * + * Return: 0 on success or error code on failure + */ +static int __os_if_nan_process_ndp_responder_req(struct wlan_objmgr_psoc *psoc, + char *iface_name, + struct nlattr **tb) +{ + int ret = 0; + QDF_STATUS status; + enum nan_datapath_state state; + struct wlan_objmgr_vdev *nan_vdev = NULL; + struct nan_datapath_responder_req req = {0}; + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE]) { + osif_err("ndp_rsp is unavailable"); + return -EINVAL; + } + req.ndp_rsp = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE]); + + if (req.ndp_rsp == NAN_DATAPATH_RESPONSE_ACCEPT) { + /* Check for an existing NAN interface */ + nan_vdev = os_if_get_ndi_vdev_by_ifname(psoc, iface_name); + if (!nan_vdev) { + osif_err("NAN data iface %s not available", + iface_name); + return -ENODEV; + } + + if (nan_vdev->vdev_mlme.vdev_opmode != QDF_NDI_MODE) { + osif_err("Interface found is not NDI"); + ret = -ENODEV; + goto responder_req_failed; + } + + if (!ucfg_nan_is_sta_ndp_concurrency_allowed(psoc, nan_vdev)) { + osif_err("NDP creation not allowed"); + ret = -EOPNOTSUPP; + goto responder_req_failed; + } + } else { + /* + * If the data indication is rejected, the userspace + * may not send the iface name. Use the first NDI + * in that case + */ + osif_debug("ndp rsp rejected, using first NDI"); + + nan_vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc( + psoc, QDF_NDI_MODE, WLAN_NAN_ID); + if (!nan_vdev) { + osif_err("NAN data iface is not available"); + return -ENODEV; + } + } + + state = ucfg_nan_get_ndi_state(nan_vdev); + if (state == NAN_DATA_NDI_DELETED_STATE || + state == NAN_DATA_NDI_DELETING_STATE || + state == NAN_DATA_NDI_CREATING_STATE) { + osif_err("Data request not allowed in current NDI state:%d", + state); + ret = -EAGAIN; + goto responder_req_failed; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]) { + osif_err("Transaction ID is unavailable"); + ret = -EINVAL; + goto responder_req_failed; + } + req.transaction_id = + nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]); + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID]) { + osif_err("Instance ID is unavailable"); + ret = -EINVAL; + goto responder_req_failed; + } + req.ndp_instance_id = + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID]); + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]) { + req.ndp_info.ndp_app_info_len = + nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]); + qdf_mem_copy(req.ndp_info.ndp_app_info, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]), + req.ndp_info.ndp_app_info_len); + } else { + osif_debug("NDP app info is unavailable"); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS]) { + /* at present ndp config stores 4 bytes QOS info only */ + req.ndp_config.ndp_cfg_len = 4; + *((uint32_t *)req.ndp_config.ndp_cfg) = + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS]); + } else { + osif_debug("NDP config data is unavailable"); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR]) { + req.is_ipv6_addr_present = true; + qdf_mem_copy(req.ipv6_addr, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR]), + QDF_IPV6_ADDR_SIZE); + } + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT]) { + req.is_port_present = true; + req.port = nla_get_u16( + tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT]); + } + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL]) { + req.is_protocol_present = true; + req.protocol = nla_get_u8( + tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL]); + } + osif_debug("ipv6 addr present: %d, addr: %pI6", + req.is_ipv6_addr_present, req.ipv6_addr); + osif_debug("port %d, present: %d protocol %d, present: %d", + req.port, req.is_port_present, req.protocol, + req.is_protocol_present); + + if (os_if_nan_parse_security_params(tb, &req.ncs_sk_type, &req.pmk, + &req.passphrase, &req.service_name)) { + osif_err("inconsistent security params in request."); + ret = -EINVAL; + goto responder_req_failed; + } + + osif_debug("vdev_id: %d, transaction_id: %d, ndp_rsp %d, ndp_instance_id: %d, ndp_app_info_len: %d, csid: %d", + wlan_vdev_get_id(nan_vdev), req.transaction_id, req.ndp_rsp, + req.ndp_instance_id, req.ndp_info.ndp_app_info_len, + req.ncs_sk_type); + + req.vdev = nan_vdev; + status = ucfg_nan_req_processor(nan_vdev, &req, NDP_RESPONDER_REQ); + ret = qdf_status_to_os_return(status); + +responder_req_failed: + if (ret) + wlan_objmgr_vdev_release_ref(nan_vdev, WLAN_NAN_ID); + + return ret; + +} + +static int os_if_nan_process_ndp_responder_req(struct wlan_objmgr_psoc *psoc, + struct nlattr **tb) +{ + struct net_device *net_dev; + struct osif_vdev_sync *vdev_sync; + char *ifname; + int errno; + + errno = osif_nla_str(tb, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, &ifname); + if (errno) + return errno; + + errno = osif_net_dev_from_ifname(psoc, ifname, &net_dev); + if (errno) + return errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __os_if_nan_process_ndp_responder_req(psoc, ifname, tb); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +/** + * __os_if_nan_process_ndp_end_req() - NDP end request handler + * @psoc: pointer to psoc object + * + * @tb: parsed NL attribute list + * tb includes following vendor attributes: + * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID + * + * Return: 0 on success or error code on failure + */ +static int __os_if_nan_process_ndp_end_req(struct wlan_objmgr_psoc *psoc, + struct nlattr **tb) +{ + int ret = 0; + QDF_STATUS status; + struct wlan_objmgr_vdev *nan_vdev; + struct nan_datapath_end_req req = {0}; + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]) { + osif_err("Transaction ID is unavailable"); + return -EINVAL; + } + req.transaction_id = + nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]); + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY]) { + osif_err("NDP instance ID array is unavailable"); + return -EINVAL; + } + + req.num_ndp_instances = + nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY]) / + sizeof(uint32_t); + if (0 >= req.num_ndp_instances) { + osif_err("Num NDP instances is 0"); + return -EINVAL; + } + qdf_mem_copy(req.ndp_ids, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY]), + req.num_ndp_instances * sizeof(uint32_t)); + + osif_debug("sending ndp_end_req to SME, transaction_id: %d", + req.transaction_id); + + nan_vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc(psoc, QDF_NDI_MODE, + WLAN_NAN_ID); + if (!nan_vdev) { + osif_err("NAN data interface is not available"); + return -EINVAL; + } + + req.vdev = nan_vdev; + status = ucfg_nan_req_processor(nan_vdev, &req, NDP_END_REQ); + ret = qdf_status_to_os_return(status); + if (ret) + wlan_objmgr_vdev_release_ref(nan_vdev, WLAN_NAN_ID); + + return ret; +} + +static int os_if_nan_process_ndp_end_req(struct wlan_objmgr_psoc *psoc, + struct nlattr **tb) +{ + struct wlan_objmgr_vdev *vdev; + struct net_device *net_dev; + struct osif_vdev_sync *vdev_sync; + int errno; + + vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc(psoc, QDF_NDI_MODE, + WLAN_NAN_ID); + if (!vdev) + return -EINVAL; + + errno = osif_net_dev_from_vdev(vdev, &net_dev); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); + + if (errno) + return errno; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __os_if_nan_process_ndp_end_req(psoc, tb); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +int os_if_nan_process_ndp_cmd(struct wlan_objmgr_psoc *psoc, + const void *data, int data_len, + bool is_ndp_allowed) +{ + uint32_t ndp_cmd_type; + uint16_t transaction_id; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX + 1]; + char *iface_name; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX, + data, data_len, vendor_attr_policy)) { + osif_err("Invalid NDP vendor command attributes"); + return -EINVAL; + } + + /* Parse and fetch NDP Command Type*/ + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD]) { + osif_err("NAN datapath cmd type failed"); + return -EINVAL; + } + ndp_cmd_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD]); + + if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]) { + osif_err("attr transaction id failed"); + return -EINVAL; + } + transaction_id = nla_get_u16( + tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]); + + if (tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]) { + iface_name = nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]); + osif_debug("Transaction Id: %u NDPCmd: %u iface_name: %s", + transaction_id, ndp_cmd_type, iface_name); + } else { + osif_debug("Transaction Id: %u NDPCmd: %u iface_name: unspecified", + transaction_id, ndp_cmd_type); + } + + switch (ndp_cmd_type) { + case QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE: + return os_if_nan_process_ndi_create(psoc, tb); + case QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE: + return os_if_nan_process_ndi_delete(psoc, tb); + case QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST: + if (!is_ndp_allowed) { + osif_err("Unsupported concurrency for NAN datapath"); + return -EOPNOTSUPP; + } + return os_if_nan_process_ndp_initiator_req(psoc, tb); + case QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_REQUEST: + if (!is_ndp_allowed) { + osif_err("Unsupported concurrency for NAN datapath"); + return -EOPNOTSUPP; + } + return os_if_nan_process_ndp_responder_req(psoc, tb); + case QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST: + if (!is_ndp_allowed) { + osif_err("Unsupported concurrency for NAN datapath"); + return -EOPNOTSUPP; + } + return os_if_nan_process_ndp_end_req(psoc, tb); + default: + osif_err("Unrecognized NDP vendor cmd %d", ndp_cmd_type); + return -EINVAL; + } + + return -EINVAL; +} + +static inline uint32_t osif_ndp_get_ndp_initiator_rsp_len(void) +{ + uint32_t data_len = NLMSG_HDRLEN; + + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE].len); + + return data_len; +} + +/** + * os_if_ndp_initiator_rsp_handler() - NDP initiator response handler + * @vdev: pointer to vdev object + * @rsp_params: response parameters + * + * Following vendor event is sent to cfg80211: + * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD = + * QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE (4 bytes) + * + * Return: none + */ +static void os_if_ndp_initiator_rsp_handler(struct wlan_objmgr_vdev *vdev, + struct nan_datapath_initiator_rsp *rsp) +{ + uint32_t data_len; + struct sk_buff *vendor_event; + struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev); + struct pdev_osif_priv *os_priv = wlan_pdev_get_ospriv(pdev); + + if (!rsp) { + osif_err("Invalid NDP Initator response"); + return; + } + + data_len = osif_ndp_get_ndp_initiator_rsp_len(); + vendor_event = cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, + data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, + GFP_ATOMIC); + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE)) + goto ndp_initiator_rsp_nla_failed; + + if (nla_put_u16(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID, + rsp->transaction_id)) + goto ndp_initiator_rsp_nla_failed; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID, + rsp->ndp_instance_id)) + goto ndp_initiator_rsp_nla_failed; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE, + rsp->status)) + goto ndp_initiator_rsp_nla_failed; + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, + rsp->reason)) + goto ndp_initiator_rsp_nla_failed; + + osif_debug("NDP Initiator rsp sent, tid:%d, instance id:%d, status:%d, reason: %d", + rsp->transaction_id, rsp->ndp_instance_id, rsp->status, + rsp->reason); + cfg80211_vendor_event(vendor_event, GFP_ATOMIC); + return; +ndp_initiator_rsp_nla_failed: + osif_err("nla_put api failed"); + kfree_skb(vendor_event); +} + +static inline uint32_t osif_ndp_get_ndp_responder_rsp_len(void) +{ + uint32_t data_len = NLMSG_HDRLEN; + + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE].len); + + return data_len; +} + +/* + * os_if_ndp_responder_rsp_handler() - NDP responder response handler + * @vdev: pointer to vdev object + * @rsp: response parameters + * + * Following vendor event is sent to cfg80211: + * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD = + * QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE (4 bytes) + * + * Return: none + */ +static void os_if_ndp_responder_rsp_handler(struct wlan_objmgr_vdev *vdev, + struct nan_datapath_responder_rsp *rsp) +{ + uint16_t data_len; + struct sk_buff *vendor_event; + struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev); + struct pdev_osif_priv *os_priv = wlan_pdev_get_ospriv(pdev); + + if (!rsp) { + osif_err("Invalid NDP Responder response"); + return; + } + + data_len = osif_ndp_get_ndp_responder_rsp_len(); + vendor_event = cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, + data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, + GFP_ATOMIC); + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE)) + goto ndp_responder_rsp_nla_failed; + + if (nla_put_u16(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID, + rsp->transaction_id)) + goto ndp_responder_rsp_nla_failed; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE, + rsp->status)) + goto ndp_responder_rsp_nla_failed; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, + rsp->reason)) + goto ndp_responder_rsp_nla_failed; + + cfg80211_vendor_event(vendor_event, GFP_ATOMIC); + return; +ndp_responder_rsp_nla_failed: + osif_err("nla_put api failed"); + kfree_skb(vendor_event); +} + +static inline uint32_t osif_ndp_get_ndp_req_ind_len( + struct nan_datapath_indication_event *event) +{ + uint32_t data_len = NLMSG_HDRLEN; + + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_CSID].len); + /* allocate space including NULL terminator */ + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR].len + 1); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR].len); + if (event->is_ipv6_addr_present) + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR].len); + if (event->scid.scid_len) + data_len += nla_total_size(event->scid.scid_len); + if (event->ndp_info.ndp_app_info_len) + data_len += nla_total_size(event->ndp_info.ndp_app_info_len); + + return data_len; +} + +/** + * os_if_ndp_indication_handler() - NDP indication handler + * @vdev: pointer to vdev object + * @ind_params: indication parameters + * + * Following vendor event is sent to cfg80211: + * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD = + * QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR (IFNAMSIZ) + * QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR (6 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR (6 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO (ndp_app_info_len size) + * QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_CSID(4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_SCID(scid_len in size) + * QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR (16 bytes) + * + * Return: none + */ +static void os_if_ndp_indication_handler(struct wlan_objmgr_vdev *vdev, + struct nan_datapath_indication_event *event) +{ + const uint8_t *ifname; + uint16_t data_len; + qdf_size_t ifname_len; + uint32_t ndp_qos_config; + struct sk_buff *vendor_event; + enum nan_datapath_state state; + struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev); + struct pdev_osif_priv *os_priv = wlan_pdev_get_ospriv(pdev); + + if (!event) { + osif_err("Invalid NDP Indication"); + return; + } + + osif_debug("NDP Indication, policy: %d", event->policy); + state = ucfg_nan_get_ndi_state(vdev); + /* check if we are in middle of deleting/creating the interface */ + + if (state == NAN_DATA_NDI_DELETED_STATE || + state == NAN_DATA_NDI_DELETING_STATE || + state == NAN_DATA_NDI_CREATING_STATE) { + osif_err("Data request not allowed in current NDI state: %d", + state); + return; + } + + ifname = os_if_ndi_get_if_name(vdev); + if (!ifname) { + osif_err("ifname is null"); + return; + } + ifname_len = qdf_str_len(ifname); + if (ifname_len > IFNAMSIZ) { + osif_err("ifname(%zu) too long", ifname_len); + return; + } + + data_len = osif_ndp_get_ndp_req_ind_len(event); + /* notify response to the upper layer */ + vendor_event = cfg80211_vendor_event_alloc(os_priv->wiphy, + NULL, data_len, + QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, + GFP_ATOMIC); + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND)) + goto ndp_indication_nla_failed; + + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, + ifname_len, ifname)) + goto ndp_indication_nla_failed; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID, + event->service_instance_id)) + goto ndp_indication_nla_failed; + + if (nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR, + QDF_MAC_ADDR_SIZE, event->peer_mac_addr.bytes)) + goto ndp_indication_nla_failed; + + if (nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR, + QDF_MAC_ADDR_SIZE, event->peer_discovery_mac_addr.bytes)) + goto ndp_indication_nla_failed; + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID, + event->ndp_instance_id)) + goto ndp_indication_nla_failed; + + if (event->ndp_info.ndp_app_info_len) + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO, + event->ndp_info.ndp_app_info_len, + event->ndp_info.ndp_app_info)) + goto ndp_indication_nla_failed; + + if (event->ndp_config.ndp_cfg_len) { + ndp_qos_config = *((uint32_t *)event->ndp_config.ndp_cfg); + /* at present ndp config stores 4 bytes QOS info only */ + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS, + ndp_qos_config)) + goto ndp_indication_nla_failed; + } + + if (event->scid.scid_len) { + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_CSID, + event->ncs_sk_type)) + goto ndp_indication_nla_failed; + + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SCID, + event->scid.scid_len, + event->scid.scid)) + goto ndp_indication_nla_failed; + + osif_debug("csid: %d, scid_len: %d", + event->ncs_sk_type, event->scid.scid_len); + + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, + event->scid.scid, event->scid.scid_len); + } + + if (event->is_ipv6_addr_present) { + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR, + QDF_IPV6_ADDR_SIZE, event->ipv6_addr)) + goto ndp_indication_nla_failed; + } + + cfg80211_vendor_event(vendor_event, GFP_ATOMIC); + return; +ndp_indication_nla_failed: + osif_err("nla_put api failed"); + kfree_skb(vendor_event); +} + +static inline uint32_t osif_ndp_get_ndp_confirm_ind_len( + struct nan_datapath_confirm_event *ndp_confirm) +{ + uint32_t ch_info_len = 0; + uint32_t data_len = NLMSG_HDRLEN; + + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR].len); + /* allocate space including NULL terminator */ + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR].len + 1); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE].len); + if (ndp_confirm->ndp_info.ndp_app_info_len) + data_len += + nla_total_size(ndp_confirm->ndp_info.ndp_app_info_len); + + if (ndp_confirm->is_ipv6_addr_present) + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR].len); + if (ndp_confirm->is_port_present) + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT].len); + if (ndp_confirm->is_protocol_present) + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL].len); + + /* ch_info is a nested array of following attributes */ + ch_info_len += nla_total_size( + vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL].len); + ch_info_len += nla_total_size( + vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH].len); + ch_info_len += nla_total_size( + vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_NDP_NSS].len); + + if (ndp_confirm->num_channels) + data_len += ndp_confirm->num_channels * + nla_total_size(ch_info_len); + + return data_len; +} + +static QDF_STATUS os_if_ndp_confirm_pack_ch_info(struct sk_buff *event, + struct nan_datapath_confirm_event *ndp_confirm) +{ + int idx = 0; + struct nlattr *ch_array, *ch_element; + + if (!ndp_confirm->num_channels) + return QDF_STATUS_SUCCESS; + + ch_array = nla_nest_start(event, QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO); + if (!ch_array) + return QDF_STATUS_E_FAULT; + + for (idx = 0; idx < ndp_confirm->num_channels; idx++) { + ch_element = nla_nest_start(event, idx); + if (!ch_element) + return QDF_STATUS_E_FAULT; + + if (nla_put_u32(event, QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL, + ndp_confirm->ch[idx].freq)) + return QDF_STATUS_E_FAULT; + + if (nla_put_u32(event, QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH, + ndp_confirm->ch[idx].ch_width)) + return QDF_STATUS_E_FAULT; + + if (nla_put_u32(event, QCA_WLAN_VENDOR_ATTR_NDP_NSS, + ndp_confirm->ch[idx].nss)) + return QDF_STATUS_E_FAULT; + nla_nest_end(event, ch_element); + } + nla_nest_end(event, ch_array); + + return QDF_STATUS_SUCCESS; +} + +/** + * os_if_ndp_confirm_ind_handler() - NDP confirm indication handler + * @vdev: pointer to vdev object + * @ind_params: indication parameters + * + * Following vendor event is sent to cfg80211: + * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD = + * QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR (6 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR (IFNAMSIZ) + * QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO (ndp_app_info_len size) + * QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR (16 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT (2 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL (1 byte) + * + * Return: none + */ +static void +os_if_ndp_confirm_ind_handler(struct wlan_objmgr_vdev *vdev, + struct nan_datapath_confirm_event *ndp_confirm) +{ + const uint8_t *ifname; + uint32_t data_len; + QDF_STATUS status; + qdf_size_t ifname_len; + struct sk_buff *vendor_event; + struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev); + struct pdev_osif_priv *os_priv = wlan_pdev_get_ospriv(pdev); + + if (!ndp_confirm) { + osif_err("Invalid NDP Initator response"); + return; + } + + ifname = os_if_ndi_get_if_name(vdev); + if (!ifname) { + osif_err("ifname is null"); + return; + } + ifname_len = qdf_str_len(ifname); + if (ifname_len > IFNAMSIZ) { + osif_err("ifname(%zu) too long", ifname_len); + return; + } + + data_len = osif_ndp_get_ndp_confirm_ind_len(ndp_confirm); + vendor_event = cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, + data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, + GFP_ATOMIC); + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND)) + goto ndp_confirm_nla_failed; + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID, + ndp_confirm->ndp_instance_id)) + goto ndp_confirm_nla_failed; + + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR, + QDF_MAC_ADDR_SIZE, ndp_confirm->peer_ndi_mac_addr.bytes)) + goto ndp_confirm_nla_failed; + + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, + ifname_len, ifname)) + goto ndp_confirm_nla_failed; + + if (ndp_confirm->ndp_info.ndp_app_info_len && + nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO, + ndp_confirm->ndp_info.ndp_app_info_len, + ndp_confirm->ndp_info.ndp_app_info)) + goto ndp_confirm_nla_failed; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE, + ndp_confirm->rsp_code)) + goto ndp_confirm_nla_failed; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, + ndp_confirm->reason_code)) + goto ndp_confirm_nla_failed; + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS, + ndp_confirm->num_channels)) + goto ndp_confirm_nla_failed; + + status = os_if_ndp_confirm_pack_ch_info(vendor_event, ndp_confirm); + if (QDF_IS_STATUS_ERROR(status)) + goto ndp_confirm_nla_failed; + + if (ndp_confirm->is_ipv6_addr_present) { + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR, + QDF_IPV6_ADDR_SIZE, ndp_confirm->ipv6_addr)) + goto ndp_confirm_nla_failed; + } + if (ndp_confirm->is_port_present) + if (nla_put_u16(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT, + ndp_confirm->port)) + goto ndp_confirm_nla_failed; + if (ndp_confirm->is_protocol_present) + if (nla_put_u8(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL, + ndp_confirm->protocol)) + goto ndp_confirm_nla_failed; + + cfg80211_vendor_event(vendor_event, GFP_ATOMIC); + osif_debug("NDP confim sent, ndp instance id: %d, peer addr: "QDF_MAC_ADDR_FMT" rsp_code: %d, reason_code: %d", + ndp_confirm->ndp_instance_id, + QDF_MAC_ADDR_REF(ndp_confirm->peer_ndi_mac_addr.bytes), + ndp_confirm->rsp_code, ndp_confirm->reason_code); + + return; +ndp_confirm_nla_failed: + osif_err("nla_put api failed"); + kfree_skb(vendor_event); +} + +static inline uint32_t osif_ndp_get_ndp_end_rsp_len(void) +{ + uint32_t data_len = NLMSG_HDRLEN; + + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID].len); + + return data_len; +} + +/** + * os_if_ndp_end_rsp_handler() - NDP end response handler + * @vdev: pointer to vdev object + * @rsp_params: response parameters + * + * Following vendor event is sent to cfg80211: + * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD = + * QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE(4 bytest) + * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes) + * + * Return: none + */ +static void os_if_ndp_end_rsp_handler(struct wlan_objmgr_vdev *vdev, + struct nan_datapath_end_rsp_event *rsp) +{ + uint32_t data_len; + struct sk_buff *vendor_event; + struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev); + struct pdev_osif_priv *os_priv = wlan_pdev_get_ospriv(pdev); + + if (!rsp) { + osif_err("Invalid ndp end response"); + return; + } + + data_len = osif_ndp_get_ndp_end_rsp_len(); + vendor_event = cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, + data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, + GFP_ATOMIC); + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE)) + goto ndp_end_rsp_nla_failed; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE, + rsp->status)) + goto ndp_end_rsp_nla_failed; + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, + rsp->reason)) + goto ndp_end_rsp_nla_failed; + + if (nla_put_u16(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID, + rsp->transaction_id)) + goto ndp_end_rsp_nla_failed; + + osif_debug("NDP End rsp sent, transaction id: %u, status: %u, reason: %u", + rsp->transaction_id, rsp->status, rsp->reason); + cfg80211_vendor_event(vendor_event, GFP_ATOMIC); + return; + +ndp_end_rsp_nla_failed: + osif_err("nla_put api failed"); + kfree_skb(vendor_event); +} + +static inline uint32_t osif_ndp_get_ndp_end_ind_len( + struct nan_datapath_end_indication_event *end_ind) +{ + uint32_t data_len = NLMSG_HDRLEN; + + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD].len); + if (end_ind->num_ndp_ids) + data_len += nla_total_size(end_ind->num_ndp_ids * + sizeof(uint32_t)); + + return data_len; +} + +/** + * os_if_ndp_end_ind_handler() - NDP end indication handler + * @vdev: pointer to vdev object + * @ind_params: indication parameters + * + * Following vendor event is sent to cfg80211: + * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD = + * QCA_WLAN_VENDOR_ATTR_NDP_END_IND (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY (4 * num of NDP Instances) + * + * Return: none + */ +static void os_if_ndp_end_ind_handler(struct wlan_objmgr_vdev *vdev, + struct nan_datapath_end_indication_event *end_ind) +{ + uint32_t data_len, i; + uint32_t *ndp_instance_array; + struct sk_buff *vendor_event; + struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev); + struct pdev_osif_priv *os_priv = wlan_pdev_get_ospriv(pdev); + + if (!end_ind) { + osif_err("Invalid ndp end indication"); + return; + } + + ndp_instance_array = qdf_mem_malloc(end_ind->num_ndp_ids * + sizeof(*ndp_instance_array)); + if (!ndp_instance_array) { + osif_err("Failed to allocate ndp_instance_array"); + return; + } + for (i = 0; i < end_ind->num_ndp_ids; i++) + ndp_instance_array[i] = end_ind->ndp_map[i].ndp_instance_id; + + data_len = osif_ndp_get_ndp_end_ind_len(end_ind); + vendor_event = cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, + data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, + GFP_ATOMIC); + if (!vendor_event) { + qdf_mem_free(ndp_instance_array); + osif_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + QCA_WLAN_VENDOR_ATTR_NDP_END_IND)) + goto ndp_end_ind_nla_failed; + + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY, + end_ind->num_ndp_ids * sizeof(*ndp_instance_array), + ndp_instance_array)) + goto ndp_end_ind_nla_failed; + + cfg80211_vendor_event(vendor_event, GFP_ATOMIC); + qdf_mem_free(ndp_instance_array); + return; + +ndp_end_ind_nla_failed: + osif_err("nla_put api failed"); + kfree_skb(vendor_event); + qdf_mem_free(ndp_instance_array); +} + +/** + * os_if_new_peer_ind_handler() - NDP new peer indication handler + * @adapter: pointer to adapter context + * @ind_params: indication parameters + * + * Return: none + */ +static void os_if_new_peer_ind_handler(struct wlan_objmgr_vdev *vdev, + struct nan_datapath_peer_ind *peer_ind) +{ + int ret; + QDF_STATUS status; + uint8_t vdev_id = wlan_vdev_get_id(vdev); + struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); + uint32_t active_peers = ucfg_nan_get_active_peers(vdev); + struct nan_callbacks cb_obj; + + if (!peer_ind) { + osif_err("Invalid new NDP peer params"); + return; + } + + status = ucfg_nan_get_callbacks(psoc, &cb_obj); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("failed to get callbacks"); + return; + } + + osif_debug("vdev_id: %d, peer_mac: "QDF_MAC_ADDR_FMT, + vdev_id, QDF_MAC_ADDR_REF(peer_ind->peer_mac_addr.bytes)); + ret = cb_obj.new_peer_ind(vdev_id, peer_ind->sta_id, + &peer_ind->peer_mac_addr, + (active_peers == 0 ? true : false)); + if (ret) { + osif_err("new peer handling at HDD failed %d", ret); + return; + } + + active_peers++; + ucfg_nan_set_active_peers(vdev, active_peers); + osif_debug("num_peers: %d", active_peers); +} + +/** + * os_if_peer_departed_ind_handler() - Handle NDP peer departed indication + * @adapter: pointer to adapter context + * @ind_params: indication parameters + * + * Return: none + */ +static void os_if_peer_departed_ind_handler(struct wlan_objmgr_vdev *vdev, + struct nan_datapath_peer_ind *peer_ind) +{ + QDF_STATUS status; + struct nan_callbacks cb_obj; + uint8_t vdev_id = wlan_vdev_get_id(vdev); + struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); + uint32_t active_peers = ucfg_nan_get_active_peers(vdev); + + status = ucfg_nan_get_callbacks(psoc, &cb_obj); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("failed to get callbacks"); + return; + } + + if (!peer_ind) { + osif_err("Invalid new NDP peer params"); + return; + } + osif_debug("vdev_id: %d, peer_mac: "QDF_MAC_ADDR_FMT, + vdev_id, QDF_MAC_ADDR_REF(peer_ind->peer_mac_addr.bytes)); + active_peers--; + ucfg_nan_set_active_peers(vdev, active_peers); + cb_obj.peer_departed_ind(vdev_id, peer_ind->sta_id, + &peer_ind->peer_mac_addr, + (active_peers == 0 ? true : false)); +} + +static inline uint32_t osif_ndp_get_ndi_create_rsp_len(void) +{ + uint32_t data_len = NLMSG_HDRLEN; + + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE].len); + + return data_len; +} + +/** + * os_if_ndp_iface_create_rsp_handler() - NDP iface create response handler + * @adapter: pointer to adapter context + * @rsp_params: response parameters + * + * The function is expected to send a response back to the user space + * even if the creation of BSS has failed + * + * Following vendor event is sent to cfg80211: + * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD = + * QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE (4 bytes) + * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE + * + * Return: none + */ +static void os_if_ndp_iface_create_rsp_handler(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + void *rsp_params) +{ + uint32_t data_len; + QDF_STATUS status; + bool create_fail = false; + struct nan_callbacks cb_obj; + struct sk_buff *vendor_event; + uint16_t create_transaction_id; + struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev); + struct pdev_osif_priv *os_priv = wlan_pdev_get_ospriv(pdev); + uint32_t create_status = NAN_DATAPATH_RSP_STATUS_ERROR; + uint32_t create_reason = NAN_DATAPATH_NAN_DATA_IFACE_CREATE_FAILED; + struct nan_datapath_inf_create_rsp *ndi_rsp = + (struct nan_datapath_inf_create_rsp *)rsp_params; + + status = ucfg_nan_get_callbacks(psoc, &cb_obj); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("Couldn't get ballback object"); + return; + } + + if (ndi_rsp) { + create_status = ndi_rsp->status; + create_reason = ndi_rsp->reason; + } else { + osif_debug("Invalid ndi create response"); + create_fail = true; + } + + create_transaction_id = ucfg_nan_get_ndp_create_transaction_id(vdev); + data_len = osif_ndp_get_ndi_create_rsp_len(); + /* notify response to the upper layer */ + vendor_event = cfg80211_vendor_event_alloc(os_priv->wiphy, + NULL, + data_len, + QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, + GFP_KERNEL); + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + create_fail = true; + goto close_ndi; + } + + /* Sub vendor command */ + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE)) { + osif_err("QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD put fail"); + goto nla_put_failure; + } + + /* Transaction id */ + if (nla_put_u16(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID, + create_transaction_id)) { + osif_err("VENDOR_ATTR_NDP_TRANSACTION_ID put fail"); + goto nla_put_failure; + } + + /* Status code */ + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE, + create_status)) { + osif_err("VENDOR_ATTR_NDP_DRV_RETURN_TYPE put fail"); + goto nla_put_failure; + } + + /* Status return value */ + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, + create_reason)) { + osif_err("VENDOR_ATTR_NDP_DRV_RETURN_VALUE put fail"); + goto nla_put_failure; + } + + osif_debug("transaction id: %u status code: %u Reason: %u", + create_transaction_id, create_status, create_reason); + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + + if (!create_fail) { + /* update txrx queues and register self sta */ + cb_obj.drv_ndi_create_rsp_handler(wlan_vdev_get_id(vdev), + ndi_rsp); + } else { + osif_err("NDI interface creation failed with reason %d", + create_reason); + goto close_ndi; + } + + return; + +nla_put_failure: + kfree_skb(vendor_event); +close_ndi: + cb_obj.ndi_close(wlan_vdev_get_id(vdev)); + return; +} + +/** + * os_if_ndp_iface_delete_rsp_handler() - NDP iface delete response handler + * @adapter: pointer to adapter context + * @rsp_params: response parameters + * + * Return: none + */ +static void os_if_ndp_iface_delete_rsp_handler(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + void *rsp_params) +{ + QDF_STATUS status; + uint8_t vdev_id = wlan_vdev_get_id(vdev); + struct nan_datapath_inf_delete_rsp *ndi_rsp = rsp_params; + struct nan_callbacks cb_obj; + + if (!ndi_rsp) { + osif_err("Invalid ndi delete response"); + return; + } + + status = ucfg_nan_get_callbacks(psoc, &cb_obj); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("Couldn't get ballback object"); + return; + } + + if (ndi_rsp->status == NAN_DATAPATH_RSP_STATUS_SUCCESS) + osif_debug("NDI BSS successfully stopped"); + else + osif_debug("NDI BSS stop failed with reason %d", + ndi_rsp->reason); + + ucfg_nan_set_ndi_delete_rsp_reason(vdev, ndi_rsp->reason); + ucfg_nan_set_ndi_delete_rsp_status(vdev, ndi_rsp->status); + cb_obj.drv_ndi_delete_rsp_handler(vdev_id); +} + +static inline uint32_t osif_ndp_get_ndp_sch_update_ind_len( + struct nan_datapath_sch_update_event *sch_update) +{ + uint32_t ch_info_len = 0; + uint32_t data_len = NLMSG_HDRLEN; + + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR].len); + if (sch_update->num_ndp_instances) + data_len += nla_total_size(sch_update->num_ndp_instances * + sizeof(uint32_t)); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REASON].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS].len); + /* ch_info is a nested array of following attributes */ + ch_info_len += nla_total_size( + vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL].len); + ch_info_len += nla_total_size( + vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH].len); + ch_info_len += nla_total_size( + vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_NDP_NSS].len); + + if (sch_update->num_ndp_instances) + data_len += sch_update->num_ndp_instances * + nla_total_size(ch_info_len); + + return data_len; +} + +static QDF_STATUS os_if_ndp_sch_update_pack_ch_info(struct sk_buff *event, + struct nan_datapath_sch_update_event *sch_update) +{ + int idx = 0; + struct nlattr *ch_array, *ch_element; + + osif_debug("num_ch: %d", sch_update->num_channels); + if (!sch_update->num_channels) + return QDF_STATUS_SUCCESS; + + ch_array = nla_nest_start(event, QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO); + if (!ch_array) + return QDF_STATUS_E_FAULT; + + for (idx = 0; idx < sch_update->num_channels; idx++) { + osif_debug("ch[%d]: freq: %d, width: %d, nss: %d", + idx, sch_update->ch[idx].freq, + sch_update->ch[idx].ch_width, + sch_update->ch[idx].nss); + ch_element = nla_nest_start(event, idx); + if (!ch_element) + return QDF_STATUS_E_FAULT; + + if (nla_put_u32(event, QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL, + sch_update->ch[idx].freq)) + return QDF_STATUS_E_FAULT; + + if (nla_put_u32(event, QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH, + sch_update->ch[idx].ch_width)) + return QDF_STATUS_E_FAULT; + + if (nla_put_u32(event, QCA_WLAN_VENDOR_ATTR_NDP_NSS, + sch_update->ch[idx].nss)) + return QDF_STATUS_E_FAULT; + nla_nest_end(event, ch_element); + } + nla_nest_end(event, ch_array); + + return QDF_STATUS_SUCCESS; +} + +/** + * os_if_ndp_sch_update_ind_handler() - NDP schedule update handler + * @vdev: vdev object pointer + * @ind: sch update pointer + * + * Following vendor event is sent to cfg80211: + * + * Return: none + */ +static void os_if_ndp_sch_update_ind_handler(struct wlan_objmgr_vdev *vdev, + void *ind) +{ + int idx = 0; + const uint8_t *ifname; + QDF_STATUS status; + uint32_t data_len; + uint8_t ifname_len; + struct sk_buff *vendor_event; + struct nan_datapath_sch_update_event *sch_update = ind; + struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev); + struct pdev_osif_priv *os_priv = wlan_pdev_get_ospriv(pdev); + + if (!sch_update) { + osif_err("Invalid sch update params"); + return; + } + + ifname = os_if_ndi_get_if_name(vdev); + if (!ifname) { + osif_err("ifname is null"); + return; + } + ifname_len = qdf_str_len(ifname); + if (ifname_len > IFNAMSIZ) { + osif_err("ifname(%d) too long", ifname_len); + return; + } + + data_len = osif_ndp_get_ndp_sch_update_ind_len(sch_update); + vendor_event = cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, + data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, + GFP_ATOMIC); + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND)) + goto ndp_sch_ind_nla_failed; + + if (nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR, + QDF_MAC_ADDR_SIZE, sch_update->peer_addr.bytes)) + goto ndp_sch_ind_nla_failed; + + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY, + sch_update->num_ndp_instances * sizeof(uint32_t), + sch_update->ndp_instances)) + goto ndp_sch_ind_nla_failed; + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REASON, + sch_update->flags)) + goto ndp_sch_ind_nla_failed; + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS, + sch_update->num_channels)) + goto ndp_sch_ind_nla_failed; + + status = os_if_ndp_sch_update_pack_ch_info(vendor_event, sch_update); + if (QDF_IS_STATUS_ERROR(status)) + goto ndp_sch_ind_nla_failed; + + osif_debug("Flags: %d, num_instance_id: %d", sch_update->flags, + sch_update->num_ndp_instances); + + for (idx = 0; idx < sch_update->num_ndp_instances; idx++) + osif_debug("ndp_instance[%d]: %d", idx, + sch_update->ndp_instances[idx]); + + cfg80211_vendor_event(vendor_event, GFP_ATOMIC); + return; + +ndp_sch_ind_nla_failed: + osif_err("nla_put api failed"); + kfree_skb(vendor_event); +} + +/** + * os_if_ndp_host_update_handler() - NDP Host update handler + * @vdev: vdev object pointer + * @evt: pointer to host update event + * + * Return: none + */ +static void os_if_ndp_host_update_handler(struct wlan_objmgr_vdev *vdev, + void *evt) +{ + struct nan_vdev_priv_obj *vdev_nan_obj; + struct nan_datapath_host_event *event; + struct osif_request *request; + + vdev_nan_obj = nan_get_vdev_priv_obj(vdev); + if (!vdev_nan_obj) { + osif_err("vdev_nan_obj is NULL"); + return; + } + + request = osif_request_get(vdev_nan_obj->disable_context); + if (!request) { + osif_debug("Obsolete request"); + return; + } + + event = osif_request_priv(request); + qdf_mem_copy(event, evt, sizeof(*event)); + + osif_request_complete(request); + osif_request_put(request); +} + +static void os_if_nan_datapath_event_handler(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + uint32_t type, void *msg) +{ + switch (type) { + case NAN_DATAPATH_INF_CREATE_RSP: + os_if_ndp_iface_create_rsp_handler(psoc, vdev, msg); + break; + case NAN_DATAPATH_INF_DELETE_RSP: + os_if_ndp_iface_delete_rsp_handler(psoc, vdev, msg); + break; + case NDP_CONFIRM: + os_if_ndp_confirm_ind_handler(vdev, msg); + break; + case NDP_INITIATOR_RSP: + os_if_ndp_initiator_rsp_handler(vdev, msg); + break; + case NDP_INDICATION: + os_if_ndp_indication_handler(vdev, msg); + break; + case NDP_NEW_PEER: + os_if_new_peer_ind_handler(vdev, msg); + break; + case NDP_RESPONDER_RSP: + os_if_ndp_responder_rsp_handler(vdev, msg); + break; + case NDP_END_RSP: + os_if_ndp_end_rsp_handler(vdev, msg); + break; + case NDP_END_IND: + os_if_ndp_end_ind_handler(vdev, msg); + break; + case NDP_PEER_DEPARTED: + os_if_peer_departed_ind_handler(vdev, msg); + break; + case NDP_SCHEDULE_UPDATE: + os_if_ndp_sch_update_ind_handler(vdev, msg); + break; + case NDP_HOST_UPDATE: + os_if_ndp_host_update_handler(vdev, msg); + break; + default: + break; + } +} + +int os_if_nan_register_lim_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj) +{ + return ucfg_nan_register_lim_callbacks(psoc, cb_obj); +} + +void os_if_nan_post_ndi_create_rsp(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool success) +{ + struct nan_datapath_inf_create_rsp rsp = {0}; + struct wlan_objmgr_vdev *vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, vdev_id, WLAN_NAN_ID); + + if (!vdev) { + osif_err("vdev is null"); + return; + } + + if (success) { + rsp.status = NAN_DATAPATH_RSP_STATUS_SUCCESS; + rsp.reason = 0; + os_if_nan_datapath_event_handler(psoc, vdev, + NAN_DATAPATH_INF_CREATE_RSP, + &rsp); + } else { + rsp.status = NAN_DATAPATH_RSP_STATUS_ERROR; + rsp.reason = NAN_DATAPATH_NAN_DATA_IFACE_CREATE_FAILED; + os_if_nan_datapath_event_handler(psoc, vdev, + NAN_DATAPATH_INF_CREATE_RSP, + &rsp); + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); +} + +void os_if_nan_post_ndi_delete_rsp(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, bool success) +{ + struct nan_datapath_inf_delete_rsp rsp = {0}; + struct wlan_objmgr_vdev *vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, vdev_id, WLAN_NAN_ID); + if (!vdev) { + osif_err("vdev is null"); + return; + } + + if (success) { + rsp.status = NAN_DATAPATH_RSP_STATUS_SUCCESS; + rsp.reason = 0; + os_if_nan_datapath_event_handler(psoc, vdev, + NAN_DATAPATH_INF_DELETE_RSP, + &rsp); + } else { + rsp.status = NAN_DATAPATH_RSP_STATUS_ERROR; + rsp.reason = NAN_DATAPATH_NAN_DATA_IFACE_DELETE_FAILED; + os_if_nan_datapath_event_handler(psoc, vdev, + NAN_DATAPATH_INF_DELETE_RSP, + &rsp); + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); +} + +static inline uint32_t osif_ndp_get_ndi_delete_rsp_len(void) +{ + uint32_t data_len = NLMSG_HDRLEN; + + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE].len); + data_len += nla_total_size(vendor_attr_policy[ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE].len); + + return data_len; +} + +void os_if_nan_ndi_session_end(struct wlan_objmgr_vdev *vdev) +{ + uint32_t data_len; + struct sk_buff *vendor_event; + struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev); + struct pdev_osif_priv *os_priv = wlan_pdev_get_ospriv(pdev); + enum nan_datapath_state state; + + /* + * The virtual adapters are stopped and closed even during + * driver unload or stop, the service layer is not required + * to be informed in that case (response is not expected) + */ + state = ucfg_nan_get_ndi_state(vdev); + if (state != NAN_DATA_NDI_DELETING_STATE && + state != NAN_DATA_DISCONNECTED_STATE) { + osif_err("NDI interface deleted: state: %u", state); + return; + } + + data_len = osif_ndp_get_ndi_delete_rsp_len(); + /* notify response to the upper layer */ + vendor_event = cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, + data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + return; + } + + /* Sub vendor command goes first */ + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE)) { + osif_err("VENDOR_ATTR_NDP_SUBCMD put fail"); + goto failure; + } + + /* Transaction id */ + if (nla_put_u16(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID, + ucfg_nan_get_ndp_delete_transaction_id(vdev))) { + osif_err("VENDOR_ATTR_NDP_TRANSACTION_ID put fail"); + goto failure; + } + + /* Status code */ + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE, + ucfg_nan_get_ndi_delete_rsp_status(vdev))) { + osif_err("VENDOR_ATTR_NDP_DRV_RETURN_TYPE put fail"); + goto failure; + } + + /* Status return value */ + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, + ucfg_nan_get_ndi_delete_rsp_reason(vdev))) { + osif_err("VENDOR_ATTR_NDP_DRV_RETURN_VALUE put fail"); + goto failure; + } + + osif_debug("delete transaction id: %u, status code: %u reason: %u", + ucfg_nan_get_ndp_delete_transaction_id(vdev), + ucfg_nan_get_ndi_delete_rsp_status(vdev), + ucfg_nan_get_ndi_delete_rsp_reason(vdev)); + + ucfg_nan_set_ndp_delete_transaction_id(vdev, 0); + ucfg_nan_set_ndi_state(vdev, NAN_DATA_NDI_DELETED_STATE); + ucfg_ndi_remove_entry_from_policy_mgr(vdev); + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + + return; +failure: + kfree_skb(vendor_event); +} + +/** + * os_if_nan_discovery_event_handler() - NAN Discovery Interface event handler + * @nan_evt: NAN Event parameters + * + * Module sends a NAN related vendor event to the upper layer + * + * Return: none + */ +static void os_if_nan_discovery_event_handler(struct nan_event_params *nan_evt) +{ + struct sk_buff *vendor_event; + struct wlan_objmgr_pdev *pdev; + struct pdev_osif_priv *os_priv; + + /* + * Since Partial Offload chipsets have only one pdev per psoc, the first + * pdev from the pdev list is used. + */ + pdev = wlan_objmgr_get_pdev_by_id(nan_evt->psoc, 0, WLAN_NAN_ID); + if (!pdev) { + osif_err("null pdev"); + return; + } + os_priv = wlan_pdev_get_ospriv(pdev); + + vendor_event = + cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, + nan_evt->buf_len + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_NAN_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + goto fail; + } + + if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NAN, nan_evt->buf_len, + nan_evt->buf)) { + osif_err("QCA_WLAN_VENDOR_ATTR_NAN put failed"); + goto fail; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); +fail: + wlan_objmgr_pdev_release_ref(pdev, WLAN_NAN_ID); +} + +int os_if_nan_register_hdd_callbacks(struct wlan_objmgr_psoc *psoc, + struct nan_callbacks *cb_obj) +{ + cb_obj->os_if_ndp_event_handler = os_if_nan_datapath_event_handler; + cb_obj->os_if_nan_event_handler = os_if_nan_discovery_event_handler; + return ucfg_nan_register_hdd_callbacks(psoc, cb_obj); +} + +static int os_if_nan_generic_req(struct wlan_objmgr_psoc *psoc, + struct nlattr **tb) +{ + struct nan_generic_req *nan_req; + uint32_t buf_len; + QDF_STATUS status; + + buf_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA]); + + nan_req = qdf_mem_malloc(sizeof(*nan_req) + buf_len); + if (!nan_req) { + osif_err("Request allocation failure"); + return -ENOMEM; + } + + nan_req->psoc = psoc; + nan_req->params.request_data_len = buf_len; + nla_memcpy(nan_req->params.request_data, + tb[QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA], buf_len); + + status = ucfg_nan_discovery_req(nan_req, NAN_GENERIC_REQ); + + if (QDF_IS_STATUS_SUCCESS(status)) + osif_debug("Successfully sent a NAN request"); + else + osif_err("Unable to send a NAN request"); + + qdf_mem_free(nan_req); + return qdf_status_to_os_return(status); +} + +static int os_if_process_nan_disable_req(struct wlan_objmgr_psoc *psoc, + struct nlattr **tb) +{ + uint8_t *data; + uint32_t data_len; + QDF_STATUS status; + + data = nla_data(tb[QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA]); + data_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA]); + + status = ucfg_disable_nan_discovery(psoc, data, data_len); + + return qdf_status_to_os_return(status); +} + +static int os_if_process_nan_enable_req(struct wlan_objmgr_psoc *psoc, + struct nlattr **tb) +{ + uint32_t chan_freq_2g, chan_freq_5g = 0; + uint32_t buf_len; + QDF_STATUS status; + uint32_t fine_time_meas_cap; + struct nan_enable_req *nan_req; + + if (!tb[QCA_WLAN_VENDOR_ATTR_NAN_DISC_24GHZ_BAND_FREQ]) { + osif_err("NAN Social channel for 2.4Gz is unavailable!"); + return -EINVAL; + } + chan_freq_2g = + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NAN_DISC_24GHZ_BAND_FREQ]); + + if (tb[QCA_WLAN_VENDOR_ATTR_NAN_DISC_5GHZ_BAND_FREQ]) + chan_freq_5g = + nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_NAN_DISC_5GHZ_BAND_FREQ]); + + if (!ucfg_is_nan_enable_allowed(psoc, chan_freq_2g)) { + osif_err("NAN Enable not allowed at this moment for channel %d", + chan_freq_2g); + return -EINVAL; + } + + buf_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA]); + + nan_req = qdf_mem_malloc(sizeof(*nan_req) + buf_len); + + if (!nan_req) { + osif_err("Request allocation failure"); + return -ENOMEM; + } + nan_req->social_chan_2g_freq = chan_freq_2g; + if (chan_freq_5g) + nan_req->social_chan_5g_freq = chan_freq_5g; + nan_req->psoc = psoc; + nan_req->params.request_data_len = buf_len; + + ucfg_mlme_get_fine_time_meas_cap(psoc, &fine_time_meas_cap); + nan_req->params.rtt_cap = fine_time_meas_cap; + + nla_memcpy(nan_req->params.request_data, + tb[QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA], buf_len); + + osif_debug("Sending NAN Enable Req. NAN Ch Freq: %d %d", + nan_req->social_chan_2g_freq, nan_req->social_chan_5g_freq); + status = ucfg_nan_discovery_req(nan_req, NAN_ENABLE_REQ); + + if (QDF_IS_STATUS_SUCCESS(status)) + osif_debug("Successfully sent NAN Enable request"); + else + osif_err("Unable to send NAN Enable request"); + + qdf_mem_free(nan_req); + return qdf_status_to_os_return(status); +} + +int os_if_process_nan_req(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, + const void *data, int data_len) +{ + uint32_t nan_subcmd; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_MAX + 1]; + + if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_MAX, + data, data_len, nan_attr_policy)) { + osif_err("Invalid NAN vendor command attributes"); + return -EINVAL; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA]) { + osif_err("NAN cmd data missing!"); + return -EINVAL; + } + + /* + * If target does not support NAN DBS, send request with type GENERIC. + * These will be treated as passthrough by the driver. This is to make + * sure that HW mode is not set to DBS by NAN Enable request. NAN state + * machine will remain unaffected in this case. + */ + if (!ucfg_is_nan_dbs_supported(psoc)) { + policy_mgr_check_and_stop_opportunistic_timer(psoc, vdev_id); + return os_if_nan_generic_req(psoc, tb); + } + + /* + * Send all requests other than Enable/Disable as type GENERIC. + * These will be treated as passthrough by the driver. + */ + if (!tb[QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE]) + return os_if_nan_generic_req(psoc, tb); + + nan_subcmd = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE]); + + switch (nan_subcmd) { + case QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ: + return os_if_process_nan_enable_req(psoc, tb); + case QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ: + return os_if_process_nan_disable_req(psoc, tb); + default: + osif_err("Unrecognized NAN subcmd type(%d)", nan_subcmd); + return -EINVAL; + } +} diff --git a/drivers/staging/qcacld-3.0/os_if/p2p/inc/wlan_cfg80211_p2p.h b/drivers/staging/qcacld-3.0/os_if/p2p/inc/wlan_cfg80211_p2p.h new file mode 100644 index 0000000000000000000000000000000000000000..9e7a94798d9953d94f181a1a56554abc41eccd16 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/p2p/inc/wlan_cfg80211_p2p.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: declares P2P functions interfacing with linux kernel + */ + +#ifndef _WLAN_CFG80211_P2P_H_ +#define _WLAN_CFG80211_P2P_H_ + +#include + +struct wlan_objmgr_psoc; +struct wlan_objmgr_vdev; +struct ieee80211_channel; + +/** + * p2p_psoc_enable() - psoc API to enable p2p component + * @psoc: soc object + * + * This function used to enable P2P component and register events. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * p2p_psoc_disable() - psoc API to disable p2p component + * @psoc: soc object + * + * This function used to disable P2P component and unregister events. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_disable(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_cfg80211_roc() - API to process cfg80211 roc request + * @vdev: Pointer to vdev object + * @chan: Pointer to channel + * @duration: Duration for this roc request + * @cookie: Pointer to return cookie to up layer + * + * API to trigger remain on channel request. It returns cookie + * as the identifier of roc. + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_roc(struct wlan_objmgr_vdev *vdev, + struct ieee80211_channel *chan, uint32_t duration, + uint64_t *cookie); + +/** + * wlan_cfg80211_cancel_roc() - API to process cfg80211 cancel remain + * on channel request + * @vdev: Pointer to vdev object + * @cookie: Find out the roc request by cookie + * + * API to trigger cancel remain on channel request. + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_cancel_roc(struct wlan_objmgr_vdev *vdev, + uint64_t cookie); + +/** + * wlan_cfg80211_mgmt_tx() - API to process cfg80211 mgmt tx request + * @vdev: Pointer to vdev object + * @chan: Pointer to channel + * @wait: wait time for this mgmt tx request + * @buf: TX buffer + * @len: Length of tx buffer + * @no_cck: Required cck or not + * @dont_wait_for_ack: Wait for ack or not + * @cookie: Return the cookie to caller + * + * API to trigger mgmt frame tx request. It returns cookie as the + * identifier of this tx. + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_mgmt_tx(struct wlan_objmgr_vdev *vdev, + struct ieee80211_channel *chan, bool offchan, uint32_t wait, + const uint8_t *buf, uint32_t len, bool no_cck, + bool dont_wait_for_ack, uint64_t *cookie); + +/** + * wlan_cfg80211_mgmt_tx_cancel() - API to process cfg80211 cancel to + * wait mgmt tx + * @vdev: Pointer to vdev object + * @cookie: Find out the mgmt tx request by cookie + * + * API to trigger cancel mgmt frame tx request. + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_mgmt_tx_cancel(struct wlan_objmgr_vdev *vdev, + uint64_t cookie); + +#endif /* _WLAN_CFG80211_P2P_H_ */ diff --git a/drivers/staging/qcacld-3.0/os_if/p2p/src/wlan_cfg80211_p2p.c b/drivers/staging/qcacld-3.0/os_if/p2p/src/wlan_cfg80211_p2p.c new file mode 100644 index 0000000000000000000000000000000000000000..ddeba7bf0d5166bfea02e8de25d45ff90d4f2bf1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/p2p/src/wlan_cfg80211_p2p.c @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: defines driver functions interfacing with linux kernel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_cfg80211.h" +#include "wlan_cfg80211_p2p.h" + +#define MAX_NO_OF_2_4_CHANNELS 14 +#define MAX_OFFCHAN_TIME_FOR_DNBS 150 + +/** + * wlan_p2p_rx_callback() - Callback for rx mgmt frame + * @user_data: pointer to soc object + * @rx_frame: RX mgmt frame information + * + * This callback will be used to rx frames in os interface. + * + * Return: None + */ +static void wlan_p2p_rx_callback(void *user_data, + struct p2p_rx_mgmt_frame *rx_frame) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + + psoc = user_data; + if (!psoc) { + osif_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + rx_frame->vdev_id, WLAN_P2P_ID); + if (!vdev) { + osif_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + osif_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + osif_err("wdev is null"); + goto fail; + } + + osif_debug("Indicate frame over nl80211, idx:%d", + wdev->netdev->ifindex); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) + cfg80211_rx_mgmt(wdev, rx_frame->rx_freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, + NL80211_RXMGMT_FLAG_ANSWERED); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + cfg80211_rx_mgmt(wdev, rx_frame->rx_freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, + NL80211_RXMGMT_FLAG_ANSWERED, GFP_ATOMIC); +#else + cfg80211_rx_mgmt(wdev, rx_frame->rx_freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, GFP_ATOMIC); +#endif /* LINUX_VERSION_CODE */ +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); +} + +/** + * wlan_p2p_action_tx_cnf_callback() - Callback for tx confirmation + * @user_data: pointer to soc object + * @tx_cnf: tx confirmation information + * + * This callback will be used to give tx mgmt frame confirmation to + * os interface. + * + * Return: None + */ +static void wlan_p2p_action_tx_cnf_callback(void *user_data, + struct p2p_tx_cnf *tx_cnf) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + bool is_success; + + psoc = user_data; + if (!psoc) { + osif_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_cnf->vdev_id, WLAN_P2P_ID); + if (!vdev) { + osif_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + osif_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + osif_err("wireless dev is null"); + goto fail; + } + + is_success = tx_cnf->status ? false : true; + cfg80211_mgmt_tx_status( + wdev, + tx_cnf->action_cookie, + tx_cnf->buf, tx_cnf->buf_len, + is_success, GFP_KERNEL); +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * wlan_p2p_lo_event_callback() - Callback for listen offload event + * @user_data: pointer to soc object + * @p2p_lo_event: listen offload event information + * + * This callback will be used to give listen offload event to os interface. + * + * Return: None + */ +static void wlan_p2p_lo_event_callback(void *user_data, + struct p2p_lo_event *p2p_lo_event) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + struct sk_buff *vendor_event; + + osif_debug("user data:%pK, vdev id:%d, reason code:%d", + user_data, p2p_lo_event->vdev_id, + p2p_lo_event->reason_code); + + psoc = user_data; + if (!psoc) { + osif_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + p2p_lo_event->vdev_id, WLAN_P2P_ID); + if (!vdev) { + osif_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + osif_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + osif_err("wireless dev is null"); + goto fail; + } + + vendor_event = cfg80211_vendor_event_alloc(wdev->wiphy, NULL, + sizeof(uint32_t) + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX, + GFP_KERNEL); + if (!vendor_event) { + osif_err("cfg80211_vendor_event_alloc failed"); + goto fail; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON, + p2p_lo_event->reason_code)) { + osif_err("nla put failed"); + kfree_skb(vendor_event); + goto fail; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); +} + +static inline void wlan_p2p_init_lo_event(struct p2p_start_param *start_param, + struct wlan_objmgr_psoc *psoc) +{ + start_param->lo_event_cb = wlan_p2p_lo_event_callback; + start_param->lo_event_cb_data = psoc; +} +#else +static inline void wlan_p2p_init_lo_event(struct p2p_start_param *start_param, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif /* FEATURE_P2P_LISTEN_OFFLOAD */ +/** + * wlan_p2p_event_callback() - Callback for P2P event + * @user_data: pointer to soc object + * @p2p_event: p2p event information + * + * This callback will be used to give p2p event to os interface. + * + * Return: None + */ +static void wlan_p2p_event_callback(void *user_data, + struct p2p_event *p2p_event) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct ieee80211_channel *chan; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + + osif_debug("user data:%pK, vdev id:%d, event type:%d", + user_data, p2p_event->vdev_id, p2p_event->roc_event); + + psoc = user_data; + if (!psoc) { + osif_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + p2p_event->vdev_id, WLAN_P2P_ID); + if (!vdev) { + osif_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + osif_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + osif_err("wireless dev is null"); + goto fail; + } + + chan = ieee80211_get_channel(wdev->wiphy, + wlan_chan_to_freq(p2p_event->chan)); + if (!chan) { + osif_err("channel conversion failed"); + goto fail; + } + + if (p2p_event->roc_event == ROC_EVENT_READY_ON_CHAN) { + cfg80211_ready_on_channel(wdev, + p2p_event->cookie, chan, + p2p_event->duration, GFP_KERNEL); + } else if (p2p_event->roc_event == ROC_EVENT_COMPLETED) { + cfg80211_remain_on_channel_expired(wdev, + p2p_event->cookie, chan, GFP_KERNEL); + } else { + osif_err("Invalid p2p event"); + } + +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); +} + +QDF_STATUS p2p_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + struct p2p_start_param start_param; + + if (!psoc) { + osif_err("psoc null"); + return QDF_STATUS_E_INVAL; + } + + start_param.rx_cb = wlan_p2p_rx_callback; + start_param.rx_cb_data = psoc; + start_param.event_cb = wlan_p2p_event_callback; + start_param.event_cb_data = psoc; + start_param.tx_cnf_cb = wlan_p2p_action_tx_cnf_callback; + start_param.tx_cnf_cb_data = psoc; + wlan_p2p_init_lo_event(&start_param, psoc); + + return ucfg_p2p_psoc_start(psoc, &start_param); +} + +QDF_STATUS p2p_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + if (!psoc) { + osif_err("psoc null"); + return QDF_STATUS_E_INVAL; + } + + return ucfg_p2p_psoc_stop(psoc); +} + +int wlan_cfg80211_roc(struct wlan_objmgr_vdev *vdev, + struct ieee80211_channel *chan, uint32_t duration, + uint64_t *cookie) +{ + struct p2p_roc_req roc_req = {0}; + struct wlan_objmgr_psoc *psoc; + uint8_t vdev_id; + bool ok; + int ret; + + if (!vdev) { + osif_err("invalid vdev object"); + return -EINVAL; + } + + if (!chan) { + osif_err("invalid channel"); + return -EINVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + osif_err("psoc handle is NULL"); + return -EINVAL; + } + + roc_req.chan = (uint32_t)wlan_freq_to_chan(chan->center_freq); + roc_req.duration = duration; + roc_req.vdev_id = (uint32_t)vdev_id; + + ret = policy_mgr_is_chan_ok_for_dnbs(psoc, chan->center_freq, &ok); + if (QDF_IS_STATUS_ERROR(ret)) { + osif_err("policy_mgr_is_chan_ok_for_dnbs():ret:%d", + ret); + return -EINVAL; + } + + if (!ok) { + osif_err("channel%d not OK for DNBS", roc_req.chan); + return -EINVAL; + } + + return qdf_status_to_os_return( + ucfg_p2p_roc_req(psoc, &roc_req, cookie)); +} + +int wlan_cfg80211_cancel_roc(struct wlan_objmgr_vdev *vdev, + uint64_t cookie) +{ + struct wlan_objmgr_psoc *psoc; + + if (!vdev) { + osif_err("invalid vdev object"); + return -EINVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + osif_err("psoc handle is NULL"); + return -EINVAL; + } + + return qdf_status_to_os_return( + ucfg_p2p_roc_cancel_req(psoc, cookie)); +} + +int wlan_cfg80211_mgmt_tx(struct wlan_objmgr_vdev *vdev, + struct ieee80211_channel *chan, bool offchan, + unsigned int wait, + const uint8_t *buf, uint32_t len, bool no_cck, + bool dont_wait_for_ack, uint64_t *cookie) +{ + struct p2p_mgmt_tx mgmt_tx = {0}; + struct wlan_objmgr_psoc *psoc; + uint8_t vdev_id; + uint32_t channel = 0; + + if (!vdev) { + osif_err("invalid vdev object"); + return -EINVAL; + } + + if (chan) + channel = (uint32_t)wlan_freq_to_chan(chan->center_freq); + else + osif_debug("NULL chan, set channel to 0"); + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + osif_err("psoc handle is NULL"); + return -EINVAL; + } + + /** + * When offchannel time is more than MAX_OFFCHAN_TIME_FOR_DNBS, + * allow offchannel only if Do_Not_Switch_Channel is not set. + */ + if (wait > MAX_OFFCHAN_TIME_FOR_DNBS) { + int ret; + bool ok; + + ret = policy_mgr_is_chan_ok_for_dnbs( + psoc, wlan_chan_to_freq(channel), &ok); + if (QDF_IS_STATUS_ERROR(ret)) { + osif_err("policy_mgr_is_chan_ok_for_dnbs():ret:%d", + ret); + return -EINVAL; + } + if (!ok) { + osif_err("Rejecting mgmt_tx for channel:%d as DNSC is set", + channel); + return -EINVAL; + } + } + + mgmt_tx.vdev_id = (uint32_t)vdev_id; + mgmt_tx.chan = channel; + mgmt_tx.wait = wait; + mgmt_tx.len = len; + mgmt_tx.no_cck = (uint32_t)no_cck; + mgmt_tx.dont_wait_for_ack = (uint32_t)dont_wait_for_ack; + mgmt_tx.off_chan = (uint32_t)offchan; + mgmt_tx.buf = buf; + + return qdf_status_to_os_return( + ucfg_p2p_mgmt_tx(psoc, &mgmt_tx, cookie)); +} + +int wlan_cfg80211_mgmt_tx_cancel(struct wlan_objmgr_vdev *vdev, + uint64_t cookie) +{ + struct wlan_objmgr_psoc *psoc; + + if (!vdev) { + osif_err("invalid vdev object"); + return -EINVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + osif_err("psoc handle is NULL"); + return -EINVAL; + } + + return qdf_status_to_os_return( + ucfg_p2p_mgmt_tx_cancel(psoc, vdev, cookie)); +} diff --git a/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_driver_sync.h b/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_driver_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..11e03a056ada4c9bac173e21fe98063ac109d130 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_driver_sync.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __OSIF_DRIVER_SYNC_H +#define __OSIF_DRIVER_SYNC_H + +#include "qdf_types.h" + +/** + * struct osif_driver_sync - opaque synchronization handle for a driver + */ +struct osif_driver_sync; + +/** + * osif_driver_sync_create() - create a driver synchronization context + * @out_driver_sync: out parameter for the new synchronization context + * + * Return: Errno + */ +qdf_must_check int +osif_driver_sync_create(struct osif_driver_sync **out_driver_sync); + +/** + * osif_driver_sync_create_and_trans() - create a driver synchronization context + * @out_driver_sync: out parameter for the new synchronization context + * + * For protecting the driver initialization process. + * + * Return: Errno + */ +#define osif_driver_sync_create_and_trans(out_driver_sync) \ + __osif_driver_sync_create_and_trans(out_driver_sync, __func__) + +qdf_must_check int +__osif_driver_sync_create_and_trans(struct osif_driver_sync **out_driver_sync, + const char *desc); + +/** + * osif_driver_sync_destroy() - destroy a driver synchronization context + * @driver_sync: the context to destroy + * + * Return: none + */ +void osif_driver_sync_destroy(struct osif_driver_sync *driver_sync); + +/** + * osif_driver_sync_register() - register a driver for operations/transitions + * @driver_sync: the driver synchronization context to register + * + * Return: none + */ +void osif_driver_sync_register(struct osif_driver_sync *driver_sync); + +/** + * osif_driver_sync_unregister() - unregister a driver for + * operations/transitions + * + * Return: the driver synchronization context that was registered for @dev + */ +struct osif_driver_sync *osif_driver_sync_unregister(void); + +/** + * osif_driver_sync_trans_start() - attempt to start a driver transition + * @out_driver_sync: out parameter for the synchronization context previously + * registered, populated on success + * + * Return: Errno + */ +#define osif_driver_sync_trans_start(out_driver_sync) \ + __osif_driver_sync_trans_start(out_driver_sync, __func__) + +qdf_must_check int +__osif_driver_sync_trans_start(struct osif_driver_sync **out_driver_sync, + const char *desc); + +/** + * osif_driver_sync_trans_start_wait() - attempt to start a driver transition, + * blocking if a conflicting transition is in flight + * @out_driver_sync: out parameter for the synchronization context previously + * registered, populated on success + * + * Return: Errno + */ +#define osif_driver_sync_trans_start_wait(out_driver_sync) \ + __osif_driver_sync_trans_start_wait(out_driver_sync, __func__) + +qdf_must_check int +__osif_driver_sync_trans_start_wait(struct osif_driver_sync **out_driver_sync, + const char *desc); + +/** + * osif_driver_sync_trans_stop() - stop a transition associated with + * @driver_sync + * @driver_sync: the synchonization context tracking the transition + * + * Return: none + */ +void osif_driver_sync_trans_stop(struct osif_driver_sync *driver_sync); + +/** + * osif_driver_sync_assert_trans_protected() - assert that the driver is + * currently protected by a transition + * + * Return: none + */ +void osif_driver_sync_assert_trans_protected(void); + +/** + * osif_driver_sync_op_start() - attempt to start a driver operation + * @out_driver_sync: out parameter for the synchronization context previously + * registered, populated on success + * + * Return: Errno + */ +#define osif_driver_sync_op_start(out_driver_sync) \ + __osif_driver_sync_op_start(out_driver_sync, __func__) + +qdf_must_check int +__osif_driver_sync_op_start(struct osif_driver_sync **out_driver_sync, + const char *func); + +/** + * osif_driver_sync_op_stop() - stop an operation associated with @driver_sync + * @driver_sync: the synchonization context tracking the operation + * + * Return: none + */ +#define osif_driver_sync_op_stop(driver_sync) \ + __osif_driver_sync_op_stop(driver_sync, __func__) + +void __osif_driver_sync_op_stop(struct osif_driver_sync *driver_sync, + const char *func); + +/** + * osif_driver_sync_wait_for_ops() - wait until all @driver_sync operations + * complete + * @driver_sync: the synchonization context tracking the operations + * + * Return: None + */ +void osif_driver_sync_wait_for_ops(struct osif_driver_sync *driver_sync); + +#endif /* __OSIF_DRIVER_SYNC_H */ + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_psoc_sync.h b/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_psoc_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..e5f7f6e2e88a7f56ff2298cda7cb5b27372ee790 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_psoc_sync.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __OSIF_PSOC_SYNC_H +#define __OSIF_PSOC_SYNC_H + +#include "linux/device.h" +#include "wlan_dsc_driver.h" +#include "qdf_types.h" + +/** + * struct osif_psoc_sync - opaque synchronization handle for a psoc + */ +struct osif_psoc_sync; + +/** + * osif_psoc_sync_create() - create a psoc synchronization context + * @out_psoc_sync: out parameter for the new synchronization context + * + * Return: Errno + */ +qdf_must_check int +osif_psoc_sync_create(struct osif_psoc_sync **out_psoc_sync); + +/** + * osif_psoc_sync_create_and_trans() - create a psoc synchronization context + * @out_psoc_sync: out parameter for the new synchronization context + * + * For protecting the device creation process. + * + * Return: Errno + */ +#define osif_psoc_sync_create_and_trans(out_psoc_sync) \ + __osif_psoc_sync_create_and_trans(out_psoc_sync, __func__) + +qdf_must_check int +__osif_psoc_sync_create_and_trans(struct osif_psoc_sync **out_psoc_sync, + const char *desc); + +/** + * osif_psoc_sync_destroy() - destroy a psoc synchronization context + * @psoc_sync: the context to destroy + * + * Return: none + */ +void osif_psoc_sync_destroy(struct osif_psoc_sync *psoc_sync); + +/** + * osif_psoc_sync_register() - register a psoc for operations/transitions + * @dev: the device to use as the operation/transition lookup key + * @psoc_sync: the psoc synchronization context to register + * + * Return: none + */ +void osif_psoc_sync_register(struct device *dev, + struct osif_psoc_sync *psoc_sync); + +/** + * osif_psoc_sync_unregister() - unregister a psoc for operations/transitions + * @dev: the device originally used to register the psoc_sync context + * + * Return: the psoc synchronization context that was registered for @dev + */ +struct osif_psoc_sync *osif_psoc_sync_unregister(struct device *dev); + +/** + * osif_psoc_sync_trans_start() - attempt to start a transition on @dev + * @dev: the device to transition + * @out_psoc_sync: out parameter for the synchronization context registered with + * @dev, populated on success + * + * Return: Errno + */ +#define osif_psoc_sync_trans_start(dev, out_psoc_sync) \ + __osif_psoc_sync_trans_start(dev, out_psoc_sync, __func__) + +qdf_must_check int +__osif_psoc_sync_trans_start(struct device *dev, + struct osif_psoc_sync **out_psoc_sync, + const char *desc); + +/** + * osif_psoc_sync_trans_start_wait() - attempt to start a transition on @dev, + * blocking if a conflicting transition is in flight + * @dev: the device to transition + * @out_psoc_sync: out parameter for the synchronization context registered with + * @dev, populated on success + * + * Return: Errno + */ +#define osif_psoc_sync_trans_start_wait(dev, out_psoc_sync) \ + __osif_psoc_sync_trans_start_wait(dev, out_psoc_sync, __func__) + +qdf_must_check int +__osif_psoc_sync_trans_start_wait(struct device *dev, + struct osif_psoc_sync **out_psoc_sync, + const char *desc); + +/** + * osif_psoc_sync_trans_resume() - resume a transition on @dev + * @dev: the device under transition + * @out_psoc_sync: out parameter for the synchronization context registered with + * @dev, populated on success + * + * Return: Errno + */ +int osif_psoc_sync_trans_resume(struct device *dev, + struct osif_psoc_sync **out_psoc_sync); + +/** + * osif_psoc_sync_trans_stop() - stop a transition associated with @psoc_sync + * @psoc_sync: the synchonization context tracking the transition + * + * Return: none + */ +void osif_psoc_sync_trans_stop(struct osif_psoc_sync *psoc_sync); + +/** + * osif_psoc_sync_assert_trans_protected() - assert that @dev is currently + * protected by a transition + * @dev: the device to check + * + * Return: none + */ +void osif_psoc_sync_assert_trans_protected(struct device *dev); + +/** + * osif_psoc_sync_op_start() - attempt to start an operation on @dev + * @dev: the device to operate against + * @out_psoc_sync: out parameter for the synchronization context registered with + * @dev, populated on success + * + * Return: Errno + */ +#define osif_psoc_sync_op_start(dev, out_psoc_sync) \ + __osif_psoc_sync_op_start(dev, out_psoc_sync, __func__) + +qdf_must_check int +__osif_psoc_sync_op_start(struct device *dev, + struct osif_psoc_sync **out_psoc_sync, + const char *func); + +/** + * osif_psoc_sync_op_stop() - stop an operation associated with @psoc_sync + * @psoc_sync: the synchonization context tracking the operation + * + * Return: none + */ +#define osif_psoc_sync_op_stop(psoc_sync) \ + __osif_psoc_sync_op_stop(psoc_sync, __func__) + +void __osif_psoc_sync_op_stop(struct osif_psoc_sync *psoc_sync, + const char *func); + +/** + * osif_psoc_sync_wait_for_ops() - wait until all @psoc_sync operations complete + * @psoc_sync: the synchonization context tracking the operations + * + * Return: None + */ +void osif_psoc_sync_wait_for_ops(struct osif_psoc_sync *psoc_sync); + +#endif /* __OSIF_PSOC_SYNC_H */ + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_sync.h b/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..02fc97c93c383e761d7c90a7f2a82bb7bbdd851e --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_sync.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Operating System Interface, Synchronization (osif_sync) + * + * osif_sync is a collection of APIs for synchronizing and safely interacting + * with external processes/threads via various operating system interfaces. It + * relies heavily on the Driver Synchronization Core, which defines the + * synchronization primitives and behavior. + * + * While DSC is great at doing the required synchronization, it (by design) does + * not address the gap between receiving a callback and involing the appropriate + * DSC protections. For example, given an input net_device pointer from the + * kernel, how does one safely aquire the appropriate DSC context? osif_sync + * implements this logic via wrapping DSC APIs with a registration mechanism. + * + * For example, after the creation of a new dsc_vdev context, osif_sync allows + * it to be registered with a specific net_device pointer as a key. Future + * operations against this net_device can use this pointer to atomically lookup + * the DSC context, and start either a DSC transition or DSC operation. If this + * is successful, the operation continues and is guaranteed protected from major + * state changes. Otherwise, the operation attempt is rejected. + * + * See also: wlan_dsc.h + */ + +#ifndef __OSIF_SYNC_H +#define __OSIF_SYNC_H + +#include "osif_driver_sync.h" +#include "osif_psoc_sync.h" +#include "osif_vdev_sync.h" + +/** + * osif_sync_init() - global initializer + * + * Return: None + */ +void osif_sync_init(void); + +/** + * osif_sync_deinit() - global de-initializer + * + * Return: None + */ +void osif_sync_deinit(void); + +#endif /* __OSIF_SYNC_H */ + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_vdev_sync.h b/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_vdev_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..0993cb037e13ac4dd1f832296a2b79157f59c037 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/inc/osif_vdev_sync.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __OSIF_VDEV_SYNC_H +#define __OSIF_VDEV_SYNC_H + +#include "linux/device.h" +#include "linux/netdevice.h" +#include "qdf_types.h" + +/** + * struct osif_vdev_sync - opaque synchronization handle for a vdev + */ +struct osif_vdev_sync; + +/** + * osif_vdev_sync_create() - create a vdev synchronization context + * @dev: parent device to the vdev + * @out_vdev_sync: out parameter for the new synchronization context + * + * Return: Errno + */ +qdf_must_check int +osif_vdev_sync_create(struct device *dev, + struct osif_vdev_sync **out_vdev_sync); + +/** + * osif_vdev_sync_create_and_trans() - create a vdev synchronization context + * @dev: parent device to the vdev + * @out_vdev_sync: out parameter for the new synchronization context + * + * For protecting the net_device creation process. + * + * Return: Errno + */ +#define osif_vdev_sync_create_and_trans(dev, out_vdev_sync) \ + __osif_vdev_sync_create_and_trans(dev, out_vdev_sync, __func__) + +qdf_must_check int +__osif_vdev_sync_create_and_trans(struct device *dev, + struct osif_vdev_sync **out_vdev_sync, + const char *desc); + +/** + * osif_vdev_sync_destroy() - destroy a vdev synchronization context + * @vdev_sync: the context to destroy + * + * Return: none + */ +void osif_vdev_sync_destroy(struct osif_vdev_sync *vdev_sync); + +/** + * osif_vdev_sync_register() - register a vdev for operations/transitions + * @net_dev: the net_device to use as the operation/transition lookup key + * @vdev_sync: the vdev synchronization context to register + * + * Return: none + */ +void osif_vdev_sync_register(struct net_device *net_dev, + struct osif_vdev_sync *vdev_sync); + +/** + * osif_vdev_sync_unregister() - unregister a vdev for operations/transitions + * @net_dev: the net_device originally used to register the vdev_sync context + * + * Return: the vdev synchronization context that was registered for @net_dev + */ +struct osif_vdev_sync *osif_vdev_sync_unregister(struct net_device *net_dev); + +/** + * osif_vdev_sync_trans_start() - attempt to start a transition on @net_dev + * @net_dev: the net_device to transition + * @out_vdev_sync: out parameter for the synchronization context registered with + * @net_dev, populated on success + * + * Return: Errno + */ +#define osif_vdev_sync_trans_start(net_dev, out_vdev_sync) \ + __osif_vdev_sync_trans_start(net_dev, out_vdev_sync, __func__) + +qdf_must_check int +__osif_vdev_sync_trans_start(struct net_device *net_dev, + struct osif_vdev_sync **out_vdev_sync, + const char *desc); + +/** + * osif_vdev_sync_trans_start_wait() - attempt to start a transition on + * @net_dev, blocking if a conflicting transition is in flight + * @net_dev: the net_device to transition + * @out_vdev_sync: out parameter for the synchronization context registered with + * @net_dev, populated on success + * + * Return: Errno + */ +#define osif_vdev_sync_trans_start_wait(net_dev, out_vdev_sync) \ + __osif_vdev_sync_trans_start_wait(net_dev, out_vdev_sync, __func__) + +qdf_must_check int +__osif_vdev_sync_trans_start_wait(struct net_device *net_dev, + struct osif_vdev_sync **out_vdev_sync, + const char *desc); + +/** + * osif_vdev_sync_trans_stop() - stop a transition associated with @vdev_sync + * @vdev_sync: the synchonization context tracking the transition + * + * Return: none + */ +void osif_vdev_sync_trans_stop(struct osif_vdev_sync *vdev_sync); + +/** + * osif_vdev_sync_assert_trans_protected() - assert that @net_dev is currently + * protected by a transition + * @net_dev: the net_device to check + * + * Return: none + */ +void osif_vdev_sync_assert_trans_protected(struct net_device *net_dev); + +/** + * osif_vdev_sync_op_start() - attempt to start an operation on @net_dev + * @net_dev: the net_device to operate against + * @out_vdev_sync: out parameter for the synchronization context registered with + * @net_dev, populated on success + * + * Return: Errno + */ +#define osif_vdev_sync_op_start(net_dev, out_vdev_sync) \ + __osif_vdev_sync_op_start(net_dev, out_vdev_sync, __func__) + +qdf_must_check int +__osif_vdev_sync_op_start(struct net_device *net_dev, + struct osif_vdev_sync **out_vdev_sync, + const char *func); + +/** + * osif_vdev_sync_op_stop() - stop an operation associated with @vdev_sync + * @vdev_sync: the synchonization context tracking the operation + * + * Return: none + */ +#define osif_vdev_sync_op_stop(net_dev) \ + __osif_vdev_sync_op_stop(net_dev, __func__) + +void __osif_vdev_sync_op_stop(struct osif_vdev_sync *vdev_sync, + const char *func); + +/** + * osif_vdev_sync_wait_for_ops() - wait until all @vdev_sync operations complete + * @vdev_sync: the synchonization context tracking the operations + * + * Return: None + */ +void osif_vdev_sync_wait_for_ops(struct osif_vdev_sync *vdev_sync); + +#endif /* __OSIF_VDEV_SYNC_H */ + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/src/__osif_driver_sync.h b/drivers/staging/qcacld-3.0/os_if/sync/src/__osif_driver_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..58f40ebb6f6169af37db942b2c5dbfef4425bbb8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/src/__osif_driver_sync.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ____OSIF_DRIVER_SYNC_H +#define ____OSIF_DRIVER_SYNC_H + +#include "qdf_status.h" +#include "wlan_dsc_psoc.h" + +/** + * osif_driver_sync_init() - global initializer + * + * Return: None + */ +void osif_driver_sync_init(void); + +/** + * osif_driver_sync_deinit() - global de-initializer + * + * Return: None + */ +void osif_driver_sync_deinit(void); + +/** + * osif_driver_sync_dsc_psoc_create() - create a dsc_psoc and attach it to the + * global dsc_driver instance + * @out_dsc_psoc: output pointer parameter for the new dsc_psoc + * + * Return: QDF_STATUS + */ +QDF_STATUS osif_driver_sync_dsc_psoc_create(struct dsc_psoc **out_dsc_psoc); + +#endif /* ____OSIF_DRIVER_SYNC_H */ + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/src/__osif_psoc_sync.h b/drivers/staging/qcacld-3.0/os_if/sync/src/__osif_psoc_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..ed6e5a50bab91833ce7122f50304f3f227363fe1 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/src/__osif_psoc_sync.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ____OSIF_PSOC_SYNC_H +#define ____OSIF_PSOC_SYNC_H + +#include "linux/device.h" +#include "qdf_status.h" +#include "wlan_dsc_vdev.h" + +/** + * osif_psoc_sync_init() - global initializer + * + * Return: None + */ +void osif_psoc_sync_init(void); + +/** + * osif_psoc_sync_deinit() - global de-initializer + * + * Return: None + */ +void osif_psoc_sync_deinit(void); + +/** + * osif_psoc_sync_dsc_vdev_create() - create a dsc_vdev and attach it to a + * dsc_psoc keyed by @dev + * @dev: the device to key off of + * @out_dsc_vdev: output pointer parameter for the new dsc_vdev + * + * Return: QDF_STATUS + */ +QDF_STATUS osif_psoc_sync_dsc_vdev_create(struct device *dev, + struct dsc_vdev **out_dsc_vdev); + +#endif /* ____OSIF_PSOC_SYNC_H */ + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/src/__osif_vdev_sync.h b/drivers/staging/qcacld-3.0/os_if/sync/src/__osif_vdev_sync.h new file mode 100644 index 0000000000000000000000000000000000000000..a0ca21fb5c39db9611577f382472909dd3413e3c --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/src/__osif_vdev_sync.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ____OSIF_VDEV_SYNC_H +#define ____OSIF_VDEV_SYNC_H + +/** + * osif_vdev_sync_init() - global initializer + * + * Return: None + */ +void osif_vdev_sync_init(void); + +/** + * osif_vdev_sync_deinit() - global de-initializer + * + * Return: None + */ +void osif_vdev_sync_deinit(void); + +#endif /* ____OSIF_VDEV_SYNC_H */ + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/src/osif_driver_sync.c b/drivers/staging/qcacld-3.0/os_if/sync/src/osif_driver_sync.c new file mode 100644 index 0000000000000000000000000000000000000000..d28bf64fcddb7a48785802cb48e2609c3f6139e8 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/src/osif_driver_sync.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "__osif_driver_sync.h" +#include "osif_driver_sync.h" +#include "qdf_lock.h" +#include "qdf_status.h" +#include "qdf_types.h" +#include "wlan_dsc_driver.h" +#include "wlan_dsc_psoc.h" + +/** + * struct osif_driver_sync - a driver synchronization context + * @dsc_driver: the dsc_driver used for synchronization + * @in_use: indicates if the context is being used + * @is_registered: indicates if the context is registered for + * transitions/operations + */ +struct osif_driver_sync { + struct dsc_driver *dsc_driver; + bool in_use; + bool is_registered; +}; + +static struct osif_driver_sync __osif_driver_sync; +static qdf_spinlock_t __osif_driver_sync_lock; + +#define osif_driver_sync_lock_create() \ + qdf_spinlock_create(&__osif_driver_sync_lock) +#define osif_driver_sync_lock_destroy() \ + qdf_spinlock_destroy(&__osif_driver_sync_lock) +#define osif_driver_sync_lock() qdf_spin_lock_bh(&__osif_driver_sync_lock) +#define osif_driver_sync_unlock() qdf_spin_unlock_bh(&__osif_driver_sync_lock) +#define osif_driver_sync_lock_assert() \ + QDF_BUG(qdf_spin_is_locked(&__osif_driver_sync_lock)) + +static struct osif_driver_sync *osif_driver_sync_lookup(void) +{ + osif_driver_sync_lock_assert(); + + if (!__osif_driver_sync.is_registered) + return NULL; + + return &__osif_driver_sync; +} + +static struct osif_driver_sync *osif_driver_sync_get(void) +{ + osif_driver_sync_lock_assert(); + + if (__osif_driver_sync.in_use) + return NULL; + + __osif_driver_sync.in_use = true; + + return &__osif_driver_sync; +} + +static void osif_driver_sync_put(struct osif_driver_sync *driver_sync) +{ + osif_driver_sync_lock_assert(); + + qdf_mem_zero(driver_sync, sizeof(*driver_sync)); +} + +int osif_driver_sync_create(struct osif_driver_sync **out_driver_sync) +{ + QDF_STATUS status; + struct osif_driver_sync *driver_sync; + + QDF_BUG(out_driver_sync); + if (!out_driver_sync) + return -EINVAL; + + osif_driver_sync_lock(); + driver_sync = osif_driver_sync_get(); + osif_driver_sync_unlock(); + if (!driver_sync) + return -ENOMEM; + + status = dsc_driver_create(&driver_sync->dsc_driver); + if (QDF_IS_STATUS_ERROR(status)) + goto sync_put; + + *out_driver_sync = driver_sync; + + return 0; + +sync_put: + osif_driver_sync_lock(); + osif_driver_sync_put(driver_sync); + osif_driver_sync_unlock(); + + return qdf_status_to_os_return(status); +} + +int +__osif_driver_sync_create_and_trans(struct osif_driver_sync **out_driver_sync, + const char *desc) +{ + struct osif_driver_sync *driver_sync; + QDF_STATUS status; + int errno; + + errno = osif_driver_sync_create(&driver_sync); + if (errno) + return errno; + + status = dsc_driver_trans_start(driver_sync->dsc_driver, desc); + if (QDF_IS_STATUS_ERROR(status)) + goto sync_destroy; + + *out_driver_sync = driver_sync; + + return 0; + +sync_destroy: + osif_driver_sync_destroy(driver_sync); + + return qdf_status_to_os_return(status); +} + +void osif_driver_sync_destroy(struct osif_driver_sync *driver_sync) +{ + QDF_BUG(driver_sync); + if (!driver_sync) + return; + + dsc_driver_destroy(&driver_sync->dsc_driver); + + osif_driver_sync_lock(); + osif_driver_sync_put(driver_sync); + osif_driver_sync_unlock(); +} + +void osif_driver_sync_register(struct osif_driver_sync *driver_sync) +{ + QDF_BUG(driver_sync); + if (!driver_sync) + return; + + osif_driver_sync_lock(); + driver_sync->is_registered = true; + osif_driver_sync_unlock(); +} + +struct osif_driver_sync *osif_driver_sync_unregister(void) +{ + struct osif_driver_sync *driver_sync; + + osif_driver_sync_lock(); + driver_sync = osif_driver_sync_lookup(); + if (driver_sync) + driver_sync->is_registered = false; + osif_driver_sync_unlock(); + + return driver_sync; +} + +typedef QDF_STATUS (*driver_start_func)(struct dsc_driver *, const char *); + +static int +__osif_driver_sync_start_callback(struct osif_driver_sync **out_driver_sync, + const char *desc, + driver_start_func driver_start_cb) +{ + QDF_STATUS status; + struct osif_driver_sync *driver_sync; + + osif_driver_sync_lock_assert(); + + *out_driver_sync = NULL; + + driver_sync = osif_driver_sync_lookup(); + if (!driver_sync) + return -EAGAIN; + + status = driver_start_cb(driver_sync->dsc_driver, desc); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + *out_driver_sync = driver_sync; + + return 0; +} + +static int +__osif_driver_sync_start_wait_callback( + struct osif_driver_sync **out_driver_sync, + const char *desc, + driver_start_func driver_start_cb) +{ + QDF_STATUS status; + struct osif_driver_sync *driver_sync; + + *out_driver_sync = NULL; + + osif_driver_sync_lock(); + driver_sync = osif_driver_sync_lookup(); + osif_driver_sync_unlock(); + if (!driver_sync) + return -EAGAIN; + + status = driver_start_cb(driver_sync->dsc_driver, desc); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + *out_driver_sync = driver_sync; + + return 0; +} +int __osif_driver_sync_trans_start(struct osif_driver_sync **out_driver_sync, + const char *desc) +{ + int errno; + + osif_driver_sync_lock(); + errno = __osif_driver_sync_start_callback(out_driver_sync, desc, + dsc_driver_trans_start); + osif_driver_sync_unlock(); + + return errno; +} + +int +__osif_driver_sync_trans_start_wait(struct osif_driver_sync **out_driver_sync, + const char *desc) +{ + int errno; + + /* since dsc_driver_trans_start_wait may sleep do not take lock here */ + errno = __osif_driver_sync_start_wait_callback(out_driver_sync, desc, + dsc_driver_trans_start_wait); + + return errno; +} + +void osif_driver_sync_trans_stop(struct osif_driver_sync *driver_sync) +{ + dsc_driver_trans_stop(driver_sync->dsc_driver); +} + +void osif_driver_sync_assert_trans_protected(void) +{ + struct osif_driver_sync *driver_sync; + + osif_driver_sync_lock(); + + driver_sync = osif_driver_sync_lookup(); + QDF_BUG(driver_sync); + if (driver_sync) + dsc_driver_assert_trans_protected(driver_sync->dsc_driver); + + osif_driver_sync_unlock(); +} + +int __osif_driver_sync_op_start(struct osif_driver_sync **out_driver_sync, + const char *func) +{ + int errno; + + osif_driver_sync_lock(); + errno = __osif_driver_sync_start_callback(out_driver_sync, func, + _dsc_driver_op_start); + osif_driver_sync_unlock(); + + return errno; +} + +void __osif_driver_sync_op_stop(struct osif_driver_sync *driver_sync, + const char *func) +{ + _dsc_driver_op_stop(driver_sync->dsc_driver, func); +} + +void osif_driver_sync_wait_for_ops(struct osif_driver_sync *driver_sync) +{ + dsc_driver_wait_for_ops(driver_sync->dsc_driver); +} + +void osif_driver_sync_init(void) +{ + osif_driver_sync_lock_create(); +} + +void osif_driver_sync_deinit(void) +{ + osif_driver_sync_lock_destroy(); +} + +static QDF_STATUS +__osif_driver_sync_dsc_psoc_create(struct dsc_psoc **out_dsc_psoc) +{ + struct osif_driver_sync *driver_sync; + + osif_driver_sync_lock_assert(); + + driver_sync = osif_driver_sync_lookup(); + if (!driver_sync) + return QDF_STATUS_E_INVAL; + + return dsc_psoc_create(driver_sync->dsc_driver, out_dsc_psoc); +} + +QDF_STATUS osif_driver_sync_dsc_psoc_create(struct dsc_psoc **out_dsc_psoc) +{ + QDF_STATUS status; + + osif_driver_sync_lock(); + status = __osif_driver_sync_dsc_psoc_create(out_dsc_psoc); + osif_driver_sync_unlock(); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/src/osif_psoc_sync.c b/drivers/staging/qcacld-3.0/os_if/sync/src/osif_psoc_sync.c new file mode 100644 index 0000000000000000000000000000000000000000..71ca1eb12a1b59803da6c4fe40ebe8437c6e16b5 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/src/osif_psoc_sync.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "linux/device.h" +#include "__osif_driver_sync.h" +#include "__osif_psoc_sync.h" +#include "osif_psoc_sync.h" +#include "qdf_lock.h" +#include "qdf_status.h" +#include "qdf_types.h" +#include "wlan_dsc_psoc.h" +#include "wlan_dsc_vdev.h" + +/** + * struct osif_psoc_sync - a psoc synchronization context + * @dev: the device used as a lookup key + * @dsc_psoc: the dsc_psoc used for synchronization + * @in_use: indicates if the context is being used + */ +struct osif_psoc_sync { + struct device *dev; + struct dsc_psoc *dsc_psoc; + bool in_use; +}; + +static struct osif_psoc_sync __osif_psoc_sync_arr[WLAN_MAX_PSOCS]; +static qdf_spinlock_t __osif_psoc_sync_lock; + +#define osif_psoc_sync_lock_create() qdf_spinlock_create(&__osif_psoc_sync_lock) +#define osif_psoc_sync_lock_destroy() \ + qdf_spinlock_destroy(&__osif_psoc_sync_lock) +#define osif_psoc_sync_lock() qdf_spin_lock_bh(&__osif_psoc_sync_lock) +#define osif_psoc_sync_unlock() qdf_spin_unlock_bh(&__osif_psoc_sync_lock) +#define osif_psoc_sync_lock_assert() \ + QDF_BUG(qdf_spin_is_locked(&__osif_psoc_sync_lock)) + +static struct osif_psoc_sync *osif_psoc_sync_lookup(struct device *dev) +{ + int i; + + osif_psoc_sync_lock_assert(); + + for (i = 0; i < QDF_ARRAY_SIZE(__osif_psoc_sync_arr); i++) { + struct osif_psoc_sync *psoc_sync = __osif_psoc_sync_arr + i; + + if (!psoc_sync->in_use) + continue; + + if (psoc_sync->dev == dev) + return psoc_sync; + } + + return NULL; +} + +static struct osif_psoc_sync *osif_psoc_sync_get(void) +{ + int i; + + osif_psoc_sync_lock_assert(); + + for (i = 0; i < QDF_ARRAY_SIZE(__osif_psoc_sync_arr); i++) { + struct osif_psoc_sync *psoc_sync = __osif_psoc_sync_arr + i; + + if (!psoc_sync->in_use) { + psoc_sync->in_use = true; + return psoc_sync; + } + } + + return NULL; +} + +static void osif_psoc_sync_put(struct osif_psoc_sync *psoc_sync) +{ + osif_psoc_sync_lock_assert(); + + qdf_mem_zero(psoc_sync, sizeof(*psoc_sync)); +} + +int osif_psoc_sync_create(struct osif_psoc_sync **out_psoc_sync) +{ + QDF_STATUS status; + struct osif_psoc_sync *psoc_sync; + + QDF_BUG(out_psoc_sync); + if (!out_psoc_sync) + return -EINVAL; + + osif_psoc_sync_lock(); + psoc_sync = osif_psoc_sync_get(); + osif_psoc_sync_unlock(); + if (!psoc_sync) + return -ENOMEM; + + status = osif_driver_sync_dsc_psoc_create(&psoc_sync->dsc_psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto sync_put; + + *out_psoc_sync = psoc_sync; + + return 0; + +sync_put: + osif_psoc_sync_lock(); + osif_psoc_sync_put(psoc_sync); + osif_psoc_sync_unlock(); + + return qdf_status_to_os_return(status); +} + +int __osif_psoc_sync_create_and_trans(struct osif_psoc_sync **out_psoc_sync, + const char *desc) +{ + struct osif_psoc_sync *psoc_sync; + QDF_STATUS status; + int errno; + + errno = osif_psoc_sync_create(&psoc_sync); + if (errno) + return errno; + + status = dsc_psoc_trans_start(psoc_sync->dsc_psoc, desc); + if (QDF_IS_STATUS_ERROR(status)) + goto sync_destroy; + + *out_psoc_sync = psoc_sync; + + return 0; + +sync_destroy: + osif_psoc_sync_destroy(psoc_sync); + + return qdf_status_to_os_return(status); +} + +void osif_psoc_sync_destroy(struct osif_psoc_sync *psoc_sync) +{ + QDF_BUG(psoc_sync); + if (!psoc_sync) + return; + + dsc_psoc_destroy(&psoc_sync->dsc_psoc); + + osif_psoc_sync_lock(); + osif_psoc_sync_put(psoc_sync); + osif_psoc_sync_unlock(); +} + +void osif_psoc_sync_register(struct device *dev, + struct osif_psoc_sync *psoc_sync) +{ + QDF_BUG(dev); + QDF_BUG(psoc_sync); + if (!psoc_sync) + return; + + osif_psoc_sync_lock(); + psoc_sync->dev = dev; + osif_psoc_sync_unlock(); +} + +struct osif_psoc_sync *osif_psoc_sync_unregister(struct device *dev) +{ + struct osif_psoc_sync *psoc_sync; + + QDF_BUG(dev); + if (!dev) + return NULL; + + osif_psoc_sync_lock(); + psoc_sync = osif_psoc_sync_lookup(dev); + if (psoc_sync) + psoc_sync->dev = NULL; + osif_psoc_sync_unlock(); + + return psoc_sync; +} + +typedef QDF_STATUS (*psoc_start_func)(struct dsc_psoc *, const char *); + +static int +__osif_psoc_sync_start_callback(struct device *dev, + struct osif_psoc_sync **out_psoc_sync, + const char *desc, + psoc_start_func psoc_start_cb) +{ + QDF_STATUS status; + struct osif_psoc_sync *psoc_sync; + + osif_psoc_sync_lock_assert(); + + *out_psoc_sync = NULL; + + psoc_sync = osif_psoc_sync_lookup(dev); + if (!psoc_sync) + return -EAGAIN; + + status = psoc_start_cb(psoc_sync->dsc_psoc, desc); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + *out_psoc_sync = psoc_sync; + + return 0; +} + +static int +__osif_psoc_sync_start_wait_callback(struct device *dev, + struct osif_psoc_sync **out_psoc_sync, + const char *desc, + psoc_start_func psoc_start_cb) +{ + QDF_STATUS status; + struct osif_psoc_sync *psoc_sync; + + *out_psoc_sync = NULL; + + osif_psoc_sync_lock(); + psoc_sync = osif_psoc_sync_lookup(dev); + osif_psoc_sync_unlock(); + if (!psoc_sync) + return -EAGAIN; + + status = psoc_start_cb(psoc_sync->dsc_psoc, desc); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + *out_psoc_sync = psoc_sync; + + return 0; +} + +int __osif_psoc_sync_trans_start(struct device *dev, + struct osif_psoc_sync **out_psoc_sync, + const char *desc) +{ + int errno; + + osif_psoc_sync_lock(); + errno = __osif_psoc_sync_start_callback(dev, out_psoc_sync, desc, + dsc_psoc_trans_start); + osif_psoc_sync_unlock(); + + return errno; +} + +int __osif_psoc_sync_trans_start_wait(struct device *dev, + struct osif_psoc_sync **out_psoc_sync, + const char *desc) +{ + int errno; + + /* since dsc_psoc_trans_start_wait may sleep do not take lock here */ + errno = __osif_psoc_sync_start_wait_callback(dev, out_psoc_sync, desc, + dsc_psoc_trans_start_wait); + + return errno; +} + +static QDF_STATUS __assert_trans_cb(struct dsc_psoc *dsc_psoc, const char *desc) +{ + dsc_psoc_assert_trans_protected(dsc_psoc); + + return QDF_STATUS_SUCCESS; +} + +int osif_psoc_sync_trans_resume(struct device *dev, + struct osif_psoc_sync **out_psoc_sync) +{ + int errno; + + osif_psoc_sync_lock(); + errno = __osif_psoc_sync_start_callback(dev, out_psoc_sync, NULL, + __assert_trans_cb); + osif_psoc_sync_unlock(); + + return errno; +} + +void osif_psoc_sync_trans_stop(struct osif_psoc_sync *psoc_sync) +{ + dsc_psoc_trans_stop(psoc_sync->dsc_psoc); +} + +void osif_psoc_sync_assert_trans_protected(struct device *dev) +{ + struct osif_psoc_sync *psoc_sync; + + osif_psoc_sync_lock(); + + psoc_sync = osif_psoc_sync_lookup(dev); + QDF_BUG(psoc_sync); + if (psoc_sync) + dsc_psoc_assert_trans_protected(psoc_sync->dsc_psoc); + + osif_psoc_sync_unlock(); +} + +int __osif_psoc_sync_op_start(struct device *dev, + struct osif_psoc_sync **out_psoc_sync, + const char *func) +{ + int errno; + + osif_psoc_sync_lock(); + errno = __osif_psoc_sync_start_callback(dev, out_psoc_sync, func, + _dsc_psoc_op_start); + osif_psoc_sync_unlock(); + + return errno; +} + +void __osif_psoc_sync_op_stop(struct osif_psoc_sync *psoc_sync, + const char *func) +{ + _dsc_psoc_op_stop(psoc_sync->dsc_psoc, func); +} + +void osif_psoc_sync_wait_for_ops(struct osif_psoc_sync *psoc_sync) +{ + dsc_psoc_wait_for_ops(psoc_sync->dsc_psoc); +} + +void osif_psoc_sync_init(void) +{ + osif_psoc_sync_lock_create(); +} + +void osif_psoc_sync_deinit(void) +{ + osif_psoc_sync_lock_destroy(); +} + +static QDF_STATUS +__osif_psoc_sync_dsc_vdev_create(struct device *dev, + struct dsc_vdev **out_dsc_vdev) +{ + struct osif_psoc_sync *psoc_sync; + + osif_psoc_sync_lock_assert(); + + psoc_sync = osif_psoc_sync_lookup(dev); + if (!psoc_sync) + return QDF_STATUS_E_INVAL; + + return dsc_vdev_create(psoc_sync->dsc_psoc, out_dsc_vdev); +} + +QDF_STATUS osif_psoc_sync_dsc_vdev_create(struct device *dev, + struct dsc_vdev **out_dsc_vdev) +{ + QDF_STATUS status; + + osif_psoc_sync_lock(); + status = __osif_psoc_sync_dsc_vdev_create(dev, out_dsc_vdev); + osif_psoc_sync_unlock(); + + return status; +} + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/src/osif_sync.c b/drivers/staging/qcacld-3.0/os_if/sync/src/osif_sync.c new file mode 100644 index 0000000000000000000000000000000000000000..eff18b403d003ed0b69bcbb80a7b407efb291fb4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/src/osif_sync.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "__osif_driver_sync.h" +#include "__osif_psoc_sync.h" +#include "__osif_vdev_sync.h" +#include "osif_sync.h" + +void osif_sync_init(void) +{ + osif_driver_sync_init(); + osif_psoc_sync_init(); + osif_vdev_sync_init(); +} + +void osif_sync_deinit(void) +{ + osif_vdev_sync_deinit(); + osif_psoc_sync_deinit(); + osif_driver_sync_deinit(); +} + diff --git a/drivers/staging/qcacld-3.0/os_if/sync/src/osif_vdev_sync.c b/drivers/staging/qcacld-3.0/os_if/sync/src/osif_vdev_sync.c new file mode 100644 index 0000000000000000000000000000000000000000..fc6293b17aeef5e4b9c377b325cee42f5276a948 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/sync/src/osif_vdev_sync.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "linux/device.h" +#include "linux/netdevice.h" +#include "__osif_psoc_sync.h" +#include "__osif_vdev_sync.h" +#include "osif_vdev_sync.h" +#include "qdf_lock.h" +#include "qdf_status.h" +#include "qdf_types.h" + +/** + * struct osif_vdev_sync - a vdev synchronization context + * @net_dev: the net_device used as a lookup key + * @dsc_vdev: the dsc_vdev used for synchronization + * @in_use: indicates if the context is being used + */ +struct osif_vdev_sync { + struct net_device *net_dev; + struct dsc_vdev *dsc_vdev; + bool in_use; +}; + +static struct osif_vdev_sync __osif_vdev_sync_arr[WLAN_MAX_VDEVS]; +static qdf_spinlock_t __osif_vdev_sync_lock; + +#define osif_vdev_sync_lock_create() qdf_spinlock_create(&__osif_vdev_sync_lock) +#define osif_vdev_sync_lock_destroy() \ + qdf_spinlock_destroy(&__osif_vdev_sync_lock) +#define osif_vdev_sync_lock() qdf_spin_lock_bh(&__osif_vdev_sync_lock) +#define osif_vdev_sync_unlock() qdf_spin_unlock_bh(&__osif_vdev_sync_lock) +#define osif_vdev_sync_lock_assert() \ + QDF_BUG(qdf_spin_is_locked(&__osif_vdev_sync_lock)) + +static struct osif_vdev_sync *osif_vdev_sync_lookup(struct net_device *net_dev) +{ + int i; + + osif_vdev_sync_lock_assert(); + + for (i = 0; i < QDF_ARRAY_SIZE(__osif_vdev_sync_arr); i++) { + struct osif_vdev_sync *vdev_sync = __osif_vdev_sync_arr + i; + + if (!vdev_sync->in_use) + continue; + + if (vdev_sync->net_dev == net_dev) + return vdev_sync; + } + + return NULL; +} + +static struct osif_vdev_sync *osif_vdev_sync_get(void) +{ + int i; + + osif_vdev_sync_lock_assert(); + + for (i = 0; i < QDF_ARRAY_SIZE(__osif_vdev_sync_arr); i++) { + struct osif_vdev_sync *vdev_sync = __osif_vdev_sync_arr + i; + + if (!vdev_sync->in_use) { + vdev_sync->in_use = true; + return vdev_sync; + } + } + + return NULL; +} + +static void osif_vdev_sync_put(struct osif_vdev_sync *vdev_sync) +{ + osif_vdev_sync_lock_assert(); + + qdf_mem_zero(vdev_sync, sizeof(*vdev_sync)); +} + +int osif_vdev_sync_create(struct device *dev, + struct osif_vdev_sync **out_vdev_sync) +{ + struct osif_vdev_sync *vdev_sync; + QDF_STATUS status; + + QDF_BUG(dev); + if (!dev) + return -EINVAL; + + QDF_BUG(out_vdev_sync); + if (!out_vdev_sync) + return -EINVAL; + + osif_vdev_sync_lock(); + vdev_sync = osif_vdev_sync_get(); + osif_vdev_sync_unlock(); + if (!vdev_sync) + return -ENOMEM; + + status = osif_psoc_sync_dsc_vdev_create(dev, &vdev_sync->dsc_vdev); + if (QDF_IS_STATUS_ERROR(status)) + goto sync_put; + + *out_vdev_sync = vdev_sync; + + return 0; + +sync_put: + osif_vdev_sync_lock(); + osif_vdev_sync_put(vdev_sync); + osif_vdev_sync_unlock(); + + return qdf_status_to_os_return(status); +} + +int __osif_vdev_sync_create_and_trans(struct device *dev, + struct osif_vdev_sync **out_vdev_sync, + const char *desc) +{ + struct osif_vdev_sync *vdev_sync; + QDF_STATUS status; + int errno; + + errno = osif_vdev_sync_create(dev, &vdev_sync); + if (errno) + return errno; + + status = dsc_vdev_trans_start(vdev_sync->dsc_vdev, desc); + if (QDF_IS_STATUS_ERROR(status)) + goto sync_destroy; + + *out_vdev_sync = vdev_sync; + + return 0; + +sync_destroy: + osif_vdev_sync_destroy(vdev_sync); + + return qdf_status_to_os_return(status); +} + +void osif_vdev_sync_destroy(struct osif_vdev_sync *vdev_sync) +{ + QDF_BUG(vdev_sync); + if (!vdev_sync) + return; + + dsc_vdev_destroy(&vdev_sync->dsc_vdev); + + osif_vdev_sync_lock(); + osif_vdev_sync_put(vdev_sync); + osif_vdev_sync_unlock(); +} + +void osif_vdev_sync_register(struct net_device *net_dev, + struct osif_vdev_sync *vdev_sync) +{ + QDF_BUG(net_dev); + QDF_BUG(vdev_sync); + if (!vdev_sync) + return; + + osif_vdev_sync_lock(); + vdev_sync->net_dev = net_dev; + osif_vdev_sync_unlock(); +} + +struct osif_vdev_sync *osif_vdev_sync_unregister(struct net_device *net_dev) +{ + struct osif_vdev_sync *vdev_sync; + + QDF_BUG(net_dev); + if (!net_dev) + return NULL; + + osif_vdev_sync_lock(); + vdev_sync = osif_vdev_sync_lookup(net_dev); + if (vdev_sync) + vdev_sync->net_dev = NULL; + osif_vdev_sync_unlock(); + + return vdev_sync; +} + +typedef QDF_STATUS (*vdev_start_func)(struct dsc_vdev *, const char *); + +static int +__osif_vdev_sync_start_callback(struct net_device *net_dev, + struct osif_vdev_sync **out_vdev_sync, + const char *desc, + vdev_start_func vdev_start_cb) +{ + QDF_STATUS status; + struct osif_vdev_sync *vdev_sync; + + osif_vdev_sync_lock_assert(); + + *out_vdev_sync = NULL; + + vdev_sync = osif_vdev_sync_lookup(net_dev); + if (!vdev_sync) + return -EAGAIN; + + status = vdev_start_cb(vdev_sync->dsc_vdev, desc); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + *out_vdev_sync = vdev_sync; + + return 0; +} + +static int +__osif_vdev_sync_start_wait_callback(struct net_device *net_dev, + struct osif_vdev_sync **out_vdev_sync, + const char *desc, + vdev_start_func vdev_start_cb) +{ + QDF_STATUS status; + struct osif_vdev_sync *vdev_sync; + + *out_vdev_sync = NULL; + + osif_vdev_sync_lock(); + vdev_sync = osif_vdev_sync_lookup(net_dev); + osif_vdev_sync_unlock(); + if (!vdev_sync) + return -EAGAIN; + + status = vdev_start_cb(vdev_sync->dsc_vdev, desc); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + *out_vdev_sync = vdev_sync; + + return 0; +} + +int __osif_vdev_sync_trans_start(struct net_device *net_dev, + struct osif_vdev_sync **out_vdev_sync, + const char *desc) +{ + int errno; + + osif_vdev_sync_lock(); + errno = __osif_vdev_sync_start_callback(net_dev, out_vdev_sync, desc, + dsc_vdev_trans_start); + osif_vdev_sync_unlock(); + + return errno; +} + +int __osif_vdev_sync_trans_start_wait(struct net_device *net_dev, + struct osif_vdev_sync **out_vdev_sync, + const char *desc) +{ + int errno; + + /* since dsc_vdev_trans_start_wait may sleep do not take lock here */ + errno = __osif_vdev_sync_start_wait_callback(net_dev, + out_vdev_sync, desc, + dsc_vdev_trans_start_wait); + + return errno; +} + +void osif_vdev_sync_trans_stop(struct osif_vdev_sync *vdev_sync) +{ + dsc_vdev_trans_stop(vdev_sync->dsc_vdev); +} + +void osif_vdev_sync_assert_trans_protected(struct net_device *net_dev) +{ + struct osif_vdev_sync *vdev_sync; + + osif_vdev_sync_lock(); + + vdev_sync = osif_vdev_sync_lookup(net_dev); + QDF_BUG(vdev_sync); + if (vdev_sync) + dsc_vdev_assert_trans_protected(vdev_sync->dsc_vdev); + + osif_vdev_sync_unlock(); +} + +int __osif_vdev_sync_op_start(struct net_device *net_dev, + struct osif_vdev_sync **out_vdev_sync, + const char *func) +{ + int errno; + + osif_vdev_sync_lock(); + errno = __osif_vdev_sync_start_callback(net_dev, out_vdev_sync, func, + _dsc_vdev_op_start); + osif_vdev_sync_unlock(); + + return errno; +} + +void __osif_vdev_sync_op_stop(struct osif_vdev_sync *vdev_sync, + const char *func) +{ + _dsc_vdev_op_stop(vdev_sync->dsc_vdev, func); +} + +void osif_vdev_sync_wait_for_ops(struct osif_vdev_sync *vdev_sync) +{ + dsc_vdev_wait_for_ops(vdev_sync->dsc_vdev); +} + +void osif_vdev_sync_init(void) +{ + osif_vdev_sync_lock_create(); +} + +void osif_vdev_sync_deinit(void) +{ + osif_vdev_sync_lock_destroy(); +} + diff --git a/drivers/staging/qcacld-3.0/os_if/tdls/inc/wlan_cfg80211_tdls.h b/drivers/staging/qcacld-3.0/os_if/tdls/inc/wlan_cfg80211_tdls.h new file mode 100644 index 0000000000000000000000000000000000000000..1ba308dc8884b31dfe3d208e6db0a3f7dab3d021 --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/tdls/inc/wlan_cfg80211_tdls.h @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: declares driver functions interfacing with linux kernel + */ + +#ifndef _WLAN_CFG80211_TDLS_H_ +#define _WLAN_CFG80211_TDLS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef FEATURE_WLAN_TDLS + +#define TDLS_VDEV_MAGIC 0x54444c53 /* "TDLS" */ + +/** + * struct osif_tdls_vdev - OS tdls vdev private structure + * @tdls_add_peer_comp: Completion to add tdls peer + * @tdls_del_peer_comp: Completion to delete tdls peer + * @tdls_mgmt_comp: Completion to send tdls mgmt packets + * @tdls_link_establish_req_comp: Completion to establish link, sync to + * send establish params to firmware, not used today. + * @tdls_teardown_comp: Completion to teardown tdls peer + * @tdls_user_cmd_comp: tdls user command completion event + * @tdls_antenna_switch_comp: Completion to switch antenna + * @tdls_add_peer_status: Peer status after add peer + * @mgmt_tx_completion_status: Tdls mgmt frames TX completion status code + * @tdls_user_cmd_len: tdls user command written buffer length + * @tdls_antenna_switch_status: return status after antenna switch + * @tdls_user_cmd_in_progress: tdls user command progress status. + */ +struct osif_tdls_vdev { + struct completion tdls_add_peer_comp; + struct completion tdls_del_peer_comp; + struct completion tdls_mgmt_comp; + struct completion tdls_link_establish_req_comp; + struct completion tdls_teardown_comp; + struct completion tdls_user_cmd_comp; + struct completion tdls_antenna_switch_comp; + QDF_STATUS tdls_add_peer_status; + uint32_t mgmt_tx_completion_status; + uint32_t tdls_user_cmd_len; + int tdls_antenna_switch_status; + bool tdls_user_cmd_in_progress; +}; + +/** + * enum qca_wlan_vendor_tdls_trigger_mode_vdev_map: Maps the user space TDLS + * trigger mode in the host driver. + * @WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: TDLS Connection and + * disconnection handled by user space. + * @WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: TDLS connection and + * disconnection controlled by host driver based on data traffic. + * @WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: TDLS connection and + * disconnection jointly controlled by user space and host driver. + */ +enum qca_wlan_vendor_tdls_trigger_mode_vdev_map { + WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT, + WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT, + WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = + ((QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT | + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT) << 1), +}; + +/** + * wlan_cfg80211_tdls_osif_priv_init() - API to initialize tdls os private + * @vdev: vdev object + * + * API to initialize tdls os private + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_cfg80211_tdls_osif_priv_init(struct wlan_objmgr_vdev *vdev); + +/** + * wlan_cfg80211_tdls_osif_priv_deinit() - API to deinitialize tdls os private + * @vdev: vdev object + * + * API to deinitialize tdls os private + * + * Return: None + */ +void wlan_cfg80211_tdls_osif_priv_deinit(struct wlan_objmgr_vdev *vdev); + +/** + * wlan_cfg80211_tdls_add_peer() - process cfg80211 add TDLS peer request + * @vdev: vdev object + * @mac: MAC address for TDLS peer + * + * Return: 0 for success; negative errno otherwise + */ +int wlan_cfg80211_tdls_add_peer(struct wlan_objmgr_vdev *vdev, + const uint8_t *mac); + +/** + * wlan_cfg80211_tdls_update_peer() - process cfg80211 update TDLS peer request + * @vdev: vdev object + * @mac: MAC address for TDLS peer + * @params: Pointer to station parameters + * + * Return: 0 for success; negative errno otherwise + */ +int wlan_cfg80211_tdls_update_peer(struct wlan_objmgr_vdev *vdev, + const uint8_t *mac, + struct station_parameters *params); + +/** + * wlan_cfg80211_tdls_configure_mode() - configure tdls mode + * @vdev: vdev obj manager + * @trigger_mode: tdls trgger mode + * + * Return: 0 for success; negative errno otherwise + */ +int wlan_cfg80211_tdls_configure_mode(struct wlan_objmgr_vdev *vdev, + uint32_t trigger_mode); + +/** + * wlan_cfg80211_tdls_oper() - process cfg80211 operation on an TDLS peer + * @vdev: vdev object + * @peer: MAC address of the TDLS peer + * @oper: cfg80211 TDLS operation + * + * Return: 0 on success; negative errno otherwise + */ +int wlan_cfg80211_tdls_oper(struct wlan_objmgr_vdev *vdev, + const uint8_t *peer, + enum nl80211_tdls_operation oper); + +/** + * wlan_cfg80211_tdls_get_all_peers() - get all the TDLS peers from the list + * @vdev: vdev object + * @buf: output buffer + * @buflen: valid length of the output error + * + * Return: length of the output buffer + */ +int wlan_cfg80211_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev, + char *buf, int buflen); + +/** + * wlan_cfg80211_tdls_mgmt() - process tdls management frames from the supplicant + * @vdev: vdev object + * @peer: MAC address of the TDLS peer + * @action_code: type of TDLS mgmt frame to be sent + * @dialog_token: dialog token used in the frame + * @status_code: status to be incuded in the frame + * @peer_capability: peer capability information + * @buf: additional IEs to be included + * @len: length of additional Ies + * @oper: cfg80211 TDLS operation + * + * Return: 0 on success; negative errno otherwise + */ +int wlan_cfg80211_tdls_mgmt(struct wlan_objmgr_vdev *vdev, + const uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + const uint8_t *buf, size_t len); + +/** + * wlan_tdls_antenna_switch() - process tdls antenna switch + * @vdev: vdev object + * @mode: antenna mode + * + * Return: 0 on success; -EAGAIN to retry + */ +int wlan_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, uint32_t mode); + +/** + * wlan_cfg80211_tdls_event_callback() - callback for tdls module + * @userdata: user data + * @type: request callback type + * @param: passed parameter + * + * This is used by TDLS to sync with os interface + * + * Return: None + */ +void wlan_cfg80211_tdls_event_callback(void *userdata, + enum tdls_event_type type, + struct tdls_osif_indication *param); + +/** + * wlan_cfg80211_tdls_rx_callback() - Callback for rx mgmt frame + * @user_data: pointer to soc object + * @rx_frame: RX mgmt frame information + * + * This callback will be used to rx frames in os interface. + * + * Return: None + */ +void wlan_cfg80211_tdls_rx_callback(void *user_data, + struct tdls_rx_mgmt_frame *rx_frame); + +/** + * hdd_notify_tdls_reset_adapter() - notify reset adapter to TDLS + * @vdev: vdev object manager + * + * Notify hdd reset adapter to TDLS component + * + * Return: None + */ +void hdd_notify_tdls_reset_adapter(struct wlan_objmgr_vdev *vdev); + +/** + * hdd_notify_sta_connect() - notify sta connect to TDLS + * @session_id: pointer to soc object + * @tdls_chan_swit_prohibited: indicates channel switch capability + * @tdls_prohibited: indicates tdls allowed or not + * @vdev: vdev object manager + * + * Notify sta connect event to TDLS component + * + * Return: None + */ +void +hdd_notify_sta_connect(uint8_t session_id, + bool tdls_chan_swit_prohibited, + bool tdls_prohibited, + struct wlan_objmgr_vdev *vdev); + +/** + * hdd_notify_sta_disconnect() - notify sta disconnect to TDLS + * @session_id: pointer to soc object + * @lfr_roam: indicate, whether disconnect due to lfr roam + * @bool user_disconnect: disconnect from user space + * @vdev: vdev object manager + * + * Notify sta disconnect event to TDLS component + * + * Return: None + */ +void hdd_notify_sta_disconnect(uint8_t session_id, + bool lfr_roam, + bool user_disconnect, + struct wlan_objmgr_vdev *vdev); + +/** + * hdd_notify_teardown_tdls_links() - notify TDLS to teardown links + * @psoc: psoc object + * + * Notify tdls to teardown all the links, due to certain events + * in the system + * + * Return: None + */ +void hdd_notify_teardown_tdls_links(struct wlan_objmgr_psoc *psoc); + +#else /* FEATURE_WLAN_TDLS */ +static inline +QDF_STATUS wlan_cfg80211_tdls_osif_priv_init(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline +void wlan_cfg80211_tdls_osif_priv_deinit(struct wlan_objmgr_vdev *vdev) +{ +} + +static inline void +hdd_notify_tdls_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ +} + +static inline void +hdd_notify_sta_connect(uint8_t session_id, + bool tdls_chan_swit_prohibited, + bool tdls_prohibited, + struct wlan_objmgr_vdev *vdev) +{ +} + +static inline +void hdd_notify_sta_disconnect(uint8_t session_id, + bool lfr_roam, + bool user_disconnect, + struct wlan_objmgr_vdev *vdev) +{ + +} + +static inline +int wlan_cfg80211_tdls_configure_mode(struct wlan_objmgr_vdev *vdev, + uint32_t trigger_mode) +{ + return 0; +} + +static inline +void hdd_notify_teardown_tdls_links(struct wlan_objmgr_psoc *psoc) +{ + +} +#endif /* FEATURE_WLAN_TDLS */ +#endif /* _WLAN_CFG80211_TDLS_H_ */ diff --git a/drivers/staging/qcacld-3.0/os_if/tdls/src/wlan_cfg80211_tdls.c b/drivers/staging/qcacld-3.0/os_if/tdls/src/wlan_cfg80211_tdls.c new file mode 100644 index 0000000000000000000000000000000000000000..7136e946fb1ab7d68a7a97f3479ec12c7c89008b --- /dev/null +++ b/drivers/staging/qcacld-3.0/os_if/tdls/src/wlan_cfg80211_tdls.c @@ -0,0 +1,1017 @@ +/* + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: defines driver functions interfacing with linux kernel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_cfg80211_mc_cp_stats.h" +#include "sir_api.h" + + +#define TDLS_MAX_NO_OF_2_4_CHANNELS 14 + +static int wlan_cfg80211_tdls_validate_mac_addr(const uint8_t *mac) +{ + static const uint8_t temp_mac[QDF_MAC_ADDR_SIZE] = {0}; + + if (!qdf_mem_cmp(mac, temp_mac, QDF_MAC_ADDR_SIZE)) { + osif_debug("Invalid Mac address " QDF_MAC_ADDR_FMT + " cmd declined.", + QDF_MAC_ADDR_REF(mac)); + return -EINVAL; + } + + return 0; +} + +QDF_STATUS wlan_cfg80211_tdls_osif_priv_init(struct wlan_objmgr_vdev *vdev) +{ + struct osif_tdls_vdev *tdls_priv; + struct vdev_osif_priv *osif_priv; + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + osif_err("osif_priv is NULL!"); + return QDF_STATUS_E_FAULT; + } + + osif_debug("initialize tdls os if layer private structure"); + tdls_priv = qdf_mem_malloc(sizeof(*tdls_priv)); + if (!tdls_priv) { + osif_err("failed to allocate memory for tdls_priv"); + return QDF_STATUS_E_NOMEM; + } + init_completion(&tdls_priv->tdls_add_peer_comp); + init_completion(&tdls_priv->tdls_del_peer_comp); + init_completion(&tdls_priv->tdls_mgmt_comp); + init_completion(&tdls_priv->tdls_link_establish_req_comp); + init_completion(&tdls_priv->tdls_teardown_comp); + init_completion(&tdls_priv->tdls_user_cmd_comp); + init_completion(&tdls_priv->tdls_antenna_switch_comp); + + osif_priv->osif_tdls = tdls_priv; + + return QDF_STATUS_SUCCESS; +} + +void wlan_cfg80211_tdls_osif_priv_deinit(struct wlan_objmgr_vdev *vdev) +{ + struct vdev_osif_priv *osif_priv; + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + osif_err("osif_priv is NULL!"); + return; + } + + osif_debug("deinitialize tdls os if layer private structure"); + if (osif_priv->osif_tdls) + qdf_mem_free(osif_priv->osif_tdls); + osif_priv->osif_tdls = NULL; +} + +void hdd_notify_teardown_tdls_links(struct wlan_objmgr_psoc *psoc) +{ + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + QDF_STATUS status; + unsigned long rc; + struct wlan_objmgr_vdev *vdev; + + vdev = ucfg_get_tdls_vdev(psoc, WLAN_OSIF_ID); + if (!vdev) + return; + + osif_priv = wlan_vdev_get_ospriv(vdev); + + if (!osif_priv || !osif_priv->osif_tdls) { + osif_err("osif priv or tdls priv is NULL"); + goto release_ref; + } + tdls_priv = osif_priv->osif_tdls; + + reinit_completion(&tdls_priv->tdls_teardown_comp); + status = ucfg_tdls_teardown_links(psoc); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("ucfg_tdls_teardown_links failed err %d", status); + goto release_ref; + } + + osif_debug("Wait for tdls teardown completion. Timeout %u ms", + WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS); + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_teardown_comp, + msecs_to_jiffies(WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS)); + + if (0 == rc) { + osif_err(" Teardown Completion timed out rc: %ld", rc); + goto release_ref; + } + + osif_debug("TDLS teardown completion status %ld ", rc); + +release_ref: + wlan_objmgr_vdev_release_ref(vdev, + WLAN_OSIF_ID); +} + +void hdd_notify_tdls_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ + ucfg_tdls_notify_reset_adapter(vdev); +} + +void +hdd_notify_sta_connect(uint8_t session_id, + bool tdls_chan_swit_prohibited, + bool tdls_prohibited, + struct wlan_objmgr_vdev *vdev) +{ + struct tdls_sta_notify_params notify_info = {0}; + QDF_STATUS status; + + if (!vdev) { + osif_err("vdev is NULL"); + return; + } + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("can't get vdev"); + return; + } + + notify_info.session_id = session_id; + notify_info.vdev = vdev; + notify_info.tdls_chan_swit_prohibited = tdls_chan_swit_prohibited; + notify_info.tdls_prohibited = tdls_prohibited; + ucfg_tdls_notify_sta_connect(¬ify_info); +} + +void hdd_notify_sta_disconnect(uint8_t session_id, + bool lfr_roam, + bool user_disconnect, + struct wlan_objmgr_vdev *vdev) +{ + struct tdls_sta_notify_params notify_info = {0}; + QDF_STATUS status; + + if (!vdev) { + osif_err("vdev is NULL"); + return; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("can't get vdev"); + return; + } + + notify_info.session_id = session_id; + notify_info.lfr_roam = lfr_roam; + notify_info.tdls_chan_swit_prohibited = false; + notify_info.tdls_prohibited = false; + notify_info.vdev = vdev; + notify_info.user_disconnect = user_disconnect; + ucfg_tdls_notify_sta_disconnect(¬ify_info); +} + +int wlan_cfg80211_tdls_add_peer(struct wlan_objmgr_vdev *vdev, + const uint8_t *mac) +{ + struct tdls_add_peer_params *add_peer_req; + int status; + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + unsigned long rc; + + status = wlan_cfg80211_tdls_validate_mac_addr(mac); + + if (status) + return status; + + osif_debug("Add TDLS peer " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac)); + + add_peer_req = qdf_mem_malloc(sizeof(*add_peer_req)); + if (!add_peer_req) { + osif_err("Failed to allocate tdls add peer request mem"); + return -EINVAL; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv || !osif_priv->osif_tdls) { + osif_err("osif_tdls_vdev or osif_priv is NULL for the current vdev"); + status = -EINVAL; + goto error; + } + tdls_priv = osif_priv->osif_tdls; + add_peer_req->vdev_id = wlan_vdev_get_id(vdev); + + qdf_mem_copy(add_peer_req->peer_addr, mac, QDF_MAC_ADDR_SIZE); + + reinit_completion(&tdls_priv->tdls_add_peer_comp); + status = ucfg_tdls_add_peer(vdev, add_peer_req); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("ucfg_tdls_add_peer returned err %d", status); + status = -EIO; + goto error; + } + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_add_peer_comp, + msecs_to_jiffies(WAIT_TIME_TDLS_ADD_STA)); + if (!rc) { + osif_err("timeout for tdls add peer indication %ld", rc); + status = -EPERM; + goto error; + } + + if (QDF_IS_STATUS_ERROR(tdls_priv->tdls_add_peer_status)) { + osif_err("tdls add peer failed, status:%d", + tdls_priv->tdls_add_peer_status); + status = -EPERM; + } +error: + qdf_mem_free(add_peer_req); + return status; +} + +static bool +is_duplicate_channel(uint8_t *arr, int index, uint8_t match) +{ + int i; + + for (i = 0; i < index; i++) { + if (arr[i] == match) + return true; + } + return false; +} + +static void +tdls_calc_channels_from_staparams(struct tdls_update_peer_params *req_info, + struct station_parameters *params) +{ + int i = 0, j = 0, k = 0, no_of_channels = 0; + int num_unique_channels; + int next; + uint8_t *dest_chans; + const uint8_t *src_chans; + + dest_chans = req_info->supported_channels; + src_chans = params->supported_channels; + + /* Convert (first channel , number of channels) tuple to + * the total list of channels. This goes with the assumption + * that if the first channel is < 14, then the next channels + * are an incremental of 1 else an incremental of 4 till the number + * of channels. + */ + for (i = 0; i < params->supported_channels_len && + j < WLAN_MAC_MAX_SUPP_CHANNELS; i += 2) { + int wifi_chan_index; + + if (!is_duplicate_channel(dest_chans, j, src_chans[i])) + dest_chans[j] = src_chans[i]; + else + continue; + + wifi_chan_index = ((dest_chans[j] <= WLAN_CHANNEL_14) ? 1 : 4); + no_of_channels = src_chans[i + 1]; + + osif_debug("i:%d,j:%d,k:%d,[%d]:%d,index:%d,chans_num: %d", + i, j, k, j, + dest_chans[j], + wifi_chan_index, + no_of_channels); + + for (k = 1; k <= no_of_channels && + j < WLAN_MAC_MAX_SUPP_CHANNELS - 1; k++) { + next = dest_chans[j] + wifi_chan_index; + + if (!is_duplicate_channel(dest_chans, j + 1, next)) + dest_chans[j + 1] = next; + else + continue; + + osif_debug("i: %d, j: %d, k: %d, [%d]: %d", + i, j, k, j + 1, dest_chans[j + 1]); + j += 1; + } + } + num_unique_channels = j + 1; + osif_debug("Unique Channel List: supported_channels "); + for (i = 0; i < num_unique_channels; i++) + osif_debug("[%d]: %d,", i, dest_chans[i]); + + if (num_unique_channels > NUM_CHANNELS) + num_unique_channels = NUM_CHANNELS; + req_info->supported_channels_len = num_unique_channels; + osif_debug("After removing duplcates supported_channels_len: %d", + req_info->supported_channels_len); +} + +static void +wlan_cfg80211_tdls_extract_params(struct tdls_update_peer_params *req_info, + struct station_parameters *params) +{ + int i; + + osif_debug("sta cap %d, uapsd_queue %d, max_sp %d", + params->capability, + params->uapsd_queues, params->max_sp); + + if (!req_info) { + osif_err("reg_info is NULL"); + return; + } + req_info->capability = params->capability; + req_info->uapsd_queues = params->uapsd_queues; + req_info->max_sp = params->max_sp; + + if (params->supported_channels_len) + tdls_calc_channels_from_staparams(req_info, params); + + if (params->supported_oper_classes_len > WLAN_MAX_SUPP_OPER_CLASSES) { + osif_debug("received oper classes:%d, resetting it to max supported: %d", + params->supported_oper_classes_len, + WLAN_MAX_SUPP_OPER_CLASSES); + params->supported_oper_classes_len = WLAN_MAX_SUPP_OPER_CLASSES; + } + + qdf_mem_copy(req_info->supported_oper_classes, + params->supported_oper_classes, + params->supported_oper_classes_len); + req_info->supported_oper_classes_len = + params->supported_oper_classes_len; + + if (params->ext_capab_len) + qdf_mem_copy(req_info->extn_capability, params->ext_capab, + sizeof(req_info->extn_capability)); + + if (params->ht_capa) { + req_info->htcap_present = 1; + qdf_mem_copy(&req_info->ht_cap, params->ht_capa, + sizeof(struct htcap_cmn_ie)); + } + + req_info->supported_rates_len = params->supported_rates_len; + + /* Note : The Maximum sizeof supported_rates sent by the Supplicant is + * 32. The supported_rates array , for all the structures propogating + * till Add Sta to the firmware has to be modified , if the supplicant + * (ieee80211) is modified to send more rates. + */ + + /* To avoid Data Currption , set to max length to SIR_MAC_MAX_SUPP_RATES + */ + if (req_info->supported_rates_len > WLAN_MAC_MAX_SUPP_RATES) + req_info->supported_rates_len = WLAN_MAC_MAX_SUPP_RATES; + + if (req_info->supported_rates_len) { + qdf_mem_copy(req_info->supported_rates, + params->supported_rates, + req_info->supported_rates_len); + osif_debug("Supported Rates with Length %d", + req_info->supported_rates_len); + + for (i = 0; i < req_info->supported_rates_len; i++) + osif_debug("[%d]: %0x", i, + req_info->supported_rates[i]); + } + + if (params->vht_capa) { + req_info->vhtcap_present = 1; + qdf_mem_copy(&req_info->vht_cap, params->vht_capa, + sizeof(struct vhtcap)); + } + + if (params->ht_capa || params->vht_capa || + (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))) + req_info->is_qos_wmm_sta = true; + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_MFP)) { + osif_debug("TDLS peer pmf capable"); + req_info->is_pmf = 1; + } +} + +int wlan_cfg80211_tdls_update_peer(struct wlan_objmgr_vdev *vdev, + const uint8_t *mac, + struct station_parameters *params) +{ + struct tdls_update_peer_params *req_info; + int status; + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + unsigned long rc; + + status = wlan_cfg80211_tdls_validate_mac_addr(mac); + + if (status) + return status; + + osif_debug("Update TDLS peer " QDF_MAC_ADDR_FMT, + QDF_MAC_ADDR_REF(mac)); + + req_info = qdf_mem_malloc(sizeof(*req_info)); + if (!req_info) { + osif_err("Failed to allocate tdls add peer request mem"); + return -EINVAL; + } + wlan_cfg80211_tdls_extract_params(req_info, params); + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv || !osif_priv->osif_tdls) { + osif_err("osif priv or tdls priv is NULL"); + status = -EINVAL; + goto error; + } + tdls_priv = osif_priv->osif_tdls; + req_info->vdev_id = wlan_vdev_get_id(vdev); + qdf_mem_copy(req_info->peer_addr, mac, QDF_MAC_ADDR_SIZE); + + reinit_completion(&tdls_priv->tdls_add_peer_comp); + status = ucfg_tdls_update_peer(vdev, req_info); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("ucfg_tdls_update_peer returned err %d", status); + status = -EIO; + goto error; + } + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_add_peer_comp, + msecs_to_jiffies(WAIT_TIME_TDLS_ADD_STA)); + if (!rc) { + osif_err("timeout for tdls update peer indication %ld", rc); + status = -EPERM; + goto error; + } + + if (QDF_IS_STATUS_ERROR(tdls_priv->tdls_add_peer_status)) { + osif_err("tdls update peer failed, status:%d", + tdls_priv->tdls_add_peer_status); + status = -EPERM; + } +error: + qdf_mem_free(req_info); + return status; +} + +static char *tdls_oper_to_str(enum nl80211_tdls_operation oper) +{ + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + return "TDLS_ENABLE_LINK"; + case NL80211_TDLS_DISABLE_LINK: + return "TDLS_DISABLE_LINK"; + case NL80211_TDLS_TEARDOWN: + return "TDLS_TEARDOWN"; + case NL80211_TDLS_SETUP: + return "TDLS_SETUP"; + default: + return "UNKNOWN:ERR"; + } +} + +static enum tdls_command_type tdls_oper_to_cmd(enum nl80211_tdls_operation oper) +{ + if (oper == NL80211_TDLS_ENABLE_LINK) + return TDLS_CMD_ENABLE_LINK; + else if (oper == NL80211_TDLS_DISABLE_LINK) + return TDLS_CMD_DISABLE_LINK; + else if (oper == NL80211_TDLS_TEARDOWN) + return TDLS_CMD_REMOVE_FORCE_PEER; + else if (oper == NL80211_TDLS_SETUP) + return TDLS_CMD_CONFIG_FORCE_PEER; + else + return 0; +} + +int wlan_cfg80211_tdls_configure_mode(struct wlan_objmgr_vdev *vdev, + uint32_t trigger_mode) +{ + enum tdls_feature_mode tdls_mode; + struct tdls_set_mode_params set_mode_params; + int status; + + if (!vdev) + return -EINVAL; + + switch (trigger_mode) { + case WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: + tdls_mode = TDLS_SUPPORT_EXP_TRIG_ONLY; + return 0; + case WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: + tdls_mode = TDLS_SUPPORT_EXT_CONTROL; + break; + case WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: + tdls_mode = TDLS_SUPPORT_IMP_MODE; + return 0; + default: + osif_err("Invalid TDLS trigger mode"); + return -EINVAL; + } + + osif_notice("cfg80211 tdls trigger mode %d", trigger_mode); + set_mode_params.source = TDLS_SET_MODE_SOURCE_USER; + set_mode_params.tdls_mode = tdls_mode; + set_mode_params.update_last = false; + set_mode_params.vdev = vdev; + + status = ucfg_tdls_set_operating_mode(&set_mode_params); + return status; +} + +int wlan_cfg80211_tdls_oper(struct wlan_objmgr_vdev *vdev, + const uint8_t *peer, + enum nl80211_tdls_operation oper) +{ + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + int status; + unsigned long rc; + enum tdls_command_type cmd; + + status = wlan_cfg80211_tdls_validate_mac_addr(peer); + + if (status) + return status; + + if (NL80211_TDLS_DISCOVERY_REQ == oper) { + osif_warn( + "We don't support in-driver setup/teardown/discovery"); + return -ENOTSUPP; + } + + osif_debug("%s start", tdls_oper_to_str(oper)); + cmd = tdls_oper_to_cmd(oper); + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + case NL80211_TDLS_TEARDOWN: + case NL80211_TDLS_SETUP: + status = ucfg_tdls_oper(vdev, peer, cmd); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("%s fail %d", + tdls_oper_to_str(oper), status); + status = -EIO; + goto error; + } + break; + case NL80211_TDLS_DISABLE_LINK: + osif_priv = wlan_vdev_get_ospriv(vdev); + + if (!osif_priv || !osif_priv->osif_tdls) { + osif_err("osif priv or tdls priv is NULL"); + status = -EINVAL; + goto error; + } + tdls_priv = osif_priv->osif_tdls; + reinit_completion(&tdls_priv->tdls_del_peer_comp); + status = ucfg_tdls_oper(vdev, peer, cmd); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("ucfg_tdls_disable_link fail %d", status); + status = -EIO; + goto error; + } + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_del_peer_comp, + msecs_to_jiffies(WAIT_TIME_TDLS_DEL_STA)); + if (!rc) { + osif_err("timeout for tdls disable link %ld", rc); + status = -EPERM; + } + break; + default: + osif_err("unsupported event %d", oper); + status = -ENOTSUPP; + } + +error: + return status; +} + +void wlan_cfg80211_tdls_rx_callback(void *user_data, + struct tdls_rx_mgmt_frame *rx_frame) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + + psoc = user_data; + if (!psoc) { + osif_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + rx_frame->vdev_id, WLAN_TDLS_NB_ID); + if (!vdev) { + osif_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + osif_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + osif_err("wdev is null"); + goto fail; + } + + osif_notice("Indicate frame over nl80211, vdev id:%d, idx:%d", + rx_frame->vdev_id, wdev->netdev->ifindex); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) + cfg80211_rx_mgmt(wdev, rx_frame->rx_freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, + NL80211_RXMGMT_FLAG_ANSWERED); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + cfg80211_rx_mgmt(wdev, rx_frame->rx_freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, + NL80211_RXMGMT_FLAG_ANSWERED, GFP_ATOMIC); +#else + cfg80211_rx_mgmt(wdev, rx_frame->rx_freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, GFP_ATOMIC); +#endif /* LINUX_VERSION_CODE */ +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +} + +static void wlan_cfg80211_update_tdls_peers_rssi(struct wlan_objmgr_vdev *vdev) +{ + int ret = 0, i; + struct stats_event *rssi_info; + struct qdf_mac_addr bcast_mac = QDF_MAC_ADDR_BCAST_INIT; + + rssi_info = wlan_cfg80211_mc_cp_stats_get_peer_rssi( + vdev, bcast_mac.bytes, + &ret); + if (ret || !rssi_info) { + osif_err("get peer rssi fail"); + wlan_cfg80211_mc_cp_stats_free_stats_event(rssi_info); + return; + } + + for (i = 0; i < rssi_info->num_peer_stats; i++) + ucfg_tdls_set_rssi(vdev, rssi_info->peer_stats[i].peer_macaddr, + rssi_info->peer_stats[i].peer_rssi); + + wlan_cfg80211_mc_cp_stats_free_stats_event(rssi_info); +} + +int wlan_cfg80211_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev, + char *buf, int buflen) +{ + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + int32_t len; + QDF_STATUS status; + unsigned long rc; + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv || !osif_priv->osif_tdls) { + osif_err("osif_tdls_vdev or osif_priv is NULL for the current vdev"); + return -EINVAL; + } + + tdls_priv = osif_priv->osif_tdls; + + /* + * We shouldn't use completion_done here for checking for completion + * as this will always return false, as tdls_user_cmd_comp.done will + * remain in init state always. So, the very first command will also + * not work. + * In general completion_done is used to check if there are multiple + * threads waiting on the complete event that's why it will return true + * only when tdls_user_cmd_comp.done is set with complete() + * In general completion_done will return true only when + * tdls_user_cmd_comp.done is set that will happen in complete(). + * Also, if there is already a thread waiting for wait_for_completion, + * this function will + * return true only after the wait timer is over or condition is + * met as wait_for_completion will hold out the hold lock and will + * will prevent completion_done from returning. + * Better to use a flag to determine command condition. + */ + if (tdls_priv->tdls_user_cmd_in_progress) { + osif_err("TDLS user cmd still in progress, reject this one"); + return -EBUSY; + } + + tdls_priv->tdls_user_cmd_in_progress = true; + wlan_cfg80211_update_tdls_peers_rssi(vdev); + + reinit_completion(&tdls_priv->tdls_user_cmd_comp); + status = ucfg_tdls_get_all_peers(vdev, buf, buflen); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("ucfg_tdls_get_all_peers failed err %d", status); + len = scnprintf(buf, buflen, + "\nucfg_tdls_send_mgmt failed\n"); + goto error_get_tdls_peers; + } + + osif_debug("Wait for tdls_user_cmd_comp. Timeout %u ms", + WAIT_TIME_FOR_TDLS_USER_CMD); + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_user_cmd_comp, + msecs_to_jiffies(WAIT_TIME_FOR_TDLS_USER_CMD)); + + if (0 == rc) { + osif_err("TDLS user cmd get all peers timed out rc %ld", + rc); + len = scnprintf(buf, buflen, + "\nTDLS user cmd get all peers timed out\n"); + goto error_get_tdls_peers; + } + + len = tdls_priv->tdls_user_cmd_len; + +error_get_tdls_peers: + tdls_priv->tdls_user_cmd_in_progress = false; + return len; +} + +int wlan_cfg80211_tdls_mgmt(struct wlan_objmgr_vdev *vdev, + const uint8_t *peer_mac, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + const uint8_t *buf, size_t len) +{ + struct tdls_action_frame_request mgmt_req; + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + int status; + unsigned long rc; + struct tdls_set_responder_req set_responder; + + status = wlan_cfg80211_tdls_validate_mac_addr(peer_mac); + + if (status) + return status; + + osif_priv = wlan_vdev_get_ospriv(vdev); + + if (!osif_priv || !osif_priv->osif_tdls) { + osif_err("osif priv or tdls priv is NULL"); + return -EINVAL; + } + + tdls_priv = osif_priv->osif_tdls; + + /* make sure doesn't call send_mgmt() while it is pending */ + if (TDLS_VDEV_MAGIC == tdls_priv->mgmt_tx_completion_status) { + osif_err(QDF_MAC_ADDR_FMT " action %d couldn't sent, as one is pending. return EBUSY", + QDF_MAC_ADDR_REF(peer_mac), action_code); + return -EBUSY; + } + + /* Reset TDLS VDEV magic */ + tdls_priv->mgmt_tx_completion_status = TDLS_VDEV_MAGIC; + + + /*prepare the request */ + + /* Validate the management Request */ + mgmt_req.chk_frame.action_code = action_code; + qdf_mem_copy(mgmt_req.chk_frame.peer_mac, peer_mac, QDF_MAC_ADDR_SIZE); + mgmt_req.chk_frame.dialog_token = dialog_token; + mgmt_req.chk_frame.action_code = action_code; + mgmt_req.chk_frame.status_code = status_code; + mgmt_req.chk_frame.len = len; + + mgmt_req.vdev = vdev; + mgmt_req.vdev_id = wlan_vdev_get_id(vdev); + mgmt_req.session_id = mgmt_req.vdev_id; + /* populate management req params */ + qdf_mem_copy(mgmt_req.tdls_mgmt.peer_mac.bytes, + peer_mac, QDF_MAC_ADDR_SIZE); + mgmt_req.tdls_mgmt.dialog = dialog_token; + mgmt_req.tdls_mgmt.frame_type = action_code; + mgmt_req.tdls_mgmt.len = len; + mgmt_req.tdls_mgmt.peer_capability = peer_capability; + mgmt_req.tdls_mgmt.status_code = mgmt_req.chk_frame.status_code; + + /*populate the additional IE's */ + mgmt_req.cmd_buf = buf; + mgmt_req.len = len; + + reinit_completion(&tdls_priv->tdls_mgmt_comp); + status = ucfg_tdls_send_mgmt_frame(&mgmt_req); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("ucfg_tdls_send_mgmt failed err %d", status); + status = -EIO; + tdls_priv->mgmt_tx_completion_status = false; + goto error_mgmt_req; + } + + osif_debug("Wait for tdls_mgmt_comp. Timeout %u ms", + WAIT_TIME_FOR_TDLS_MGMT); + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_mgmt_comp, + msecs_to_jiffies(WAIT_TIME_FOR_TDLS_MGMT)); + + if ((0 == rc) || (QDF_STATUS_SUCCESS != + tdls_priv->mgmt_tx_completion_status)) { + osif_err("%s rc %ld mgmtTxCompletionStatus %u", + !rc ? "Mgmt Tx Completion timed out" : + "Mgmt Tx Completion failed", + rc, tdls_priv->mgmt_tx_completion_status); + + tdls_priv->mgmt_tx_completion_status = false; + status = -EINVAL; + goto error_mgmt_req; + } + + osif_debug("Mgmt Tx Completion status %ld TxCompletion %u", + rc, tdls_priv->mgmt_tx_completion_status); + + if (TDLS_SETUP_RESPONSE == action_code || + TDLS_SETUP_CONFIRM == action_code) { + qdf_mem_copy(set_responder.peer_mac, peer_mac, + QDF_MAC_ADDR_SIZE); + set_responder.vdev = vdev; + if (TDLS_SETUP_RESPONSE == action_code) + set_responder.responder = false; + if (TDLS_SETUP_CONFIRM == action_code) + set_responder.responder = true; + ucfg_tdls_responder(&set_responder); + } + +error_mgmt_req: + return status; +} + +int wlan_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, uint32_t mode) +{ + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + int ret; + unsigned long rc; + + if (!vdev) { + osif_err("vdev is NULL"); + return -EAGAIN; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv || !osif_priv->osif_tdls) { + osif_err("osif priv or tdls priv is NULL"); + ret = -EINVAL; + goto error; + } + tdls_priv = osif_priv->osif_tdls; + + reinit_completion(&tdls_priv->tdls_antenna_switch_comp); + ret = ucfg_tdls_antenna_switch(vdev, mode); + if (QDF_IS_STATUS_ERROR(ret)) { + osif_err("ucfg_tdls_antenna_switch failed err %d", ret); + ret = -EAGAIN; + goto error; + } + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_antenna_switch_comp, + msecs_to_jiffies(WAIT_TIME_FOR_TDLS_ANTENNA_SWITCH)); + if (!rc) { + osif_err("timeout for tdls antenna switch %ld", rc); + ret = -EAGAIN; + goto error; + } + + ret = tdls_priv->tdls_antenna_switch_status; + osif_debug("tdls antenna switch status:%d", ret); +error: + return ret; +} + +static void +wlan_cfg80211_tdls_indicate_discovery(struct tdls_osif_indication *ind) +{ + struct vdev_osif_priv *osif_vdev; + + osif_vdev = wlan_vdev_get_ospriv(ind->vdev); + + cfg80211_tdls_oper_request(osif_vdev->wdev->netdev, + ind->peer_mac, NL80211_TDLS_DISCOVERY_REQ, + false, GFP_KERNEL); +} + +static void +wlan_cfg80211_tdls_indicate_setup(struct tdls_osif_indication *ind) +{ + struct vdev_osif_priv *osif_vdev; + + osif_vdev = wlan_vdev_get_ospriv(ind->vdev); + + osif_debug("Indication to request TDLS setup"); + cfg80211_tdls_oper_request(osif_vdev->wdev->netdev, + ind->peer_mac, NL80211_TDLS_SETUP, false, + GFP_KERNEL); +} + +static void +wlan_cfg80211_tdls_indicate_teardown(struct tdls_osif_indication *ind) +{ + struct vdev_osif_priv *osif_vdev; + + osif_vdev = wlan_vdev_get_ospriv(ind->vdev); + + osif_debug("Teardown reason %d", ind->reason); + cfg80211_tdls_oper_request(osif_vdev->wdev->netdev, + ind->peer_mac, NL80211_TDLS_TEARDOWN, + ind->reason, GFP_KERNEL); +} + +void wlan_cfg80211_tdls_event_callback(void *user_data, + enum tdls_event_type type, + struct tdls_osif_indication *ind) +{ + struct vdev_osif_priv *osif_vdev; + struct osif_tdls_vdev *tdls_priv; + + if (!ind || !ind->vdev) { + osif_err("ind: %pK", ind); + return; + } + osif_vdev = wlan_vdev_get_ospriv(ind->vdev); + + if (!osif_vdev || !osif_vdev->osif_tdls) { + osif_err("osif priv or tdls priv is NULL"); + return; + } + + tdls_priv = osif_vdev->osif_tdls; + + switch (type) { + case TDLS_EVENT_MGMT_TX_ACK_CNF: + tdls_priv->mgmt_tx_completion_status = ind->status; + complete(&tdls_priv->tdls_mgmt_comp); + break; + case TDLS_EVENT_ADD_PEER: + tdls_priv->tdls_add_peer_status = ind->status; + complete(&tdls_priv->tdls_add_peer_comp); + break; + case TDLS_EVENT_DEL_PEER: + complete(&tdls_priv->tdls_del_peer_comp); + break; + case TDLS_EVENT_DISCOVERY_REQ: + wlan_cfg80211_tdls_indicate_discovery(ind); + break; + case TDLS_EVENT_TEARDOWN_REQ: + wlan_cfg80211_tdls_indicate_teardown(ind); + break; + case TDLS_EVENT_SETUP_REQ: + wlan_cfg80211_tdls_indicate_setup(ind); + break; + case TDLS_EVENT_TEARDOWN_LINKS_DONE: + complete(&tdls_priv->tdls_teardown_comp); + break; + case TDLS_EVENT_USER_CMD: + tdls_priv->tdls_user_cmd_len = ind->status; + complete(&tdls_priv->tdls_user_cmd_comp); + break; + + case TDLS_EVENT_ANTENNA_SWITCH: + tdls_priv->tdls_antenna_switch_status = ind->status; + complete(&tdls_priv->tdls_antenna_switch_comp); + default: + break; + } +} diff --git a/drivers/staging/qcacld-3.0/uapi/linux/a_debug.h b/drivers/staging/qcacld-3.0/uapi/linux/a_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..db21bc2d66df5a53cf4a33c2135c0e014fa04ab6 --- /dev/null +++ b/drivers/staging/qcacld-3.0/uapi/linux/a_debug.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _A_DEBUG_H_ +#define _A_DEBUG_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include "osapi_linux.h" + +/* standard debug print masks bits 0..7 */ +#define ATH_DEBUG_ERR (1 << 0) /* errors */ +#define ATH_DEBUG_WARN (1 << 1) /* warnings */ +#define ATH_DEBUG_INFO (1 << 2) /* informational (module startup info) */ +#define ATH_DEBUG_TRC (1 << 3) /* generic function call tracing */ +#define ATH_DEBUG_RSVD1 (1 << 4) +#define ATH_DEBUG_RSVD2 (1 << 5) +#define ATH_DEBUG_RSVD3 (1 << 6) +#define ATH_DEBUG_RSVD4 (1 << 7) + +#define ATH_DEBUG_MASK_DEFAULTS (ATH_DEBUG_ERR | ATH_DEBUG_WARN) +#define ATH_DEBUG_ANY 0xFFFF + +/* other aliases used throughout */ +#define ATH_DEBUG_ERROR ATH_DEBUG_ERR +#define ATH_LOG_ERR ATH_DEBUG_ERR +#define ATH_LOG_INF ATH_DEBUG_INFO +#define ATH_LOG_TRC ATH_DEBUG_TRC +#define ATH_DEBUG_TRACE ATH_DEBUG_TRC +#define ATH_DEBUG_INIT ATH_DEBUG_INFO + +/* bits 8..31 are module-specific masks */ +#define ATH_DEBUG_MODULE_MASK_SHIFT 8 + +/* macro to make a module-specific masks */ +#define ATH_DEBUG_MAKE_MODULE_MASK(index) (1 << (ATH_DEBUG_MODULE_MASK_SHIFT + (index))) + +void debug_dump_bytes(A_UCHAR *buffer, A_UINT16 length, + char *pDescription); + +/* Debug support on a per-module basis + * + * Usage: + * + * Each module can utilize it's own debug mask variable. A set of commonly used + * masks are provided (ERRORS, WARNINGS, TRACE etc..). It is up to each module + * to define module-specific masks using the macros above. + * + * Each module defines a single debug mask variable debug_XXX where the "name" of the module is + * common to all C-files within that module. This requires every C-file that includes a_debug.h + * to define the module name in that file. + * + * Example: + * + * #define ATH_MODULE_NAME htc + * #include "a_debug.h" + * + * This will define a debug mask structure called debug_htc and all debug macros will reference this + * variable. + * + * A module can define module-specific bit masks using the ATH_DEBUG_MAKE_MODULE_MASK() macro: + * + * #define ATH_DEBUG_MY_MASK1 ATH_DEBUG_MAKE_MODULE_MASK(0) + * #define ATH_DEBUG_MY_MASK2 ATH_DEBUG_MAKE_MODULE_MASK(1) + * + * The instantiation of the debug structure should be made by the module. When a module is + * instantiated, the module can set a description string, a default mask and an array of description + * entries containing information on each module-defined debug mask. + * NOTE: The instantiation is statically allocated, only one instance can exist per module. + * + * Example: + * + * + * #define ATH_DEBUG_BMI ATH_DEBUG_MAKE_MODULE_MASK(0) + * + * #ifdef DEBUG + * static ATH_DEBUG_MASK_DESCRIPTION bmi_debug_desc[] = { + * { ATH_DEBUG_BMI , "BMI Tracing"}, <== description of the module specific mask + * }; + * + * ATH_DEBUG_INSTANTIATE_MODULE_VAR(bmi, + * "bmi" <== module name + * "Boot Manager Interface", <== description of module + * ATH_DEBUG_MASK_DEFAULTS, <== defaults + * ATH_DEBUG_DESCRIPTION_COUNT(bmi_debug_desc), + * bmi_debug_desc); + * + * #endif + * + * A module can optionally register it's debug module information in order for other tools to change the + * bit mask at runtime. A module can call A_REGISTER_MODULE_DEBUG_INFO() in it's module + * init code. This macro can be called multiple times without consequence. The debug info maintains + * state to indicate whether the information was previously registered. + * + * */ + +#define ATH_DEBUG_MAX_MASK_DESC_LENGTH 32 +#define ATH_DEBUG_MAX_MOD_DESC_LENGTH 64 + +typedef struct { + A_UINT32 Mask; + A_CHAR Description[ATH_DEBUG_MAX_MASK_DESC_LENGTH]; +} ATH_DEBUG_MASK_DESCRIPTION; + +#define ATH_DEBUG_INFO_FLAGS_REGISTERED (1 << 0) + +typedef struct _ATH_DEBUG_MODULE_DBG_INFO { + struct _ATH_DEBUG_MODULE_DBG_INFO *pNext; + A_CHAR ModuleName[16]; + A_CHAR ModuleDescription[ATH_DEBUG_MAX_MOD_DESC_LENGTH]; + A_UINT32 Flags; + A_UINT32 CurrentMask; + int MaxDescriptions; + ATH_DEBUG_MASK_DESCRIPTION *pMaskDescriptions; /* pointer to array of descriptions */ +} ATH_DEBUG_MODULE_DBG_INFO; + +#define ATH_DEBUG_DESCRIPTION_COUNT(d) (int)((sizeof((d))) / (sizeof(ATH_DEBUG_MASK_DESCRIPTION))) + +#define GET_ATH_MODULE_DEBUG_VAR_NAME(s) _XGET_ATH_MODULE_NAME_DEBUG_(s) +#define GET_ATH_MODULE_DEBUG_VAR_MASK(s) _XGET_ATH_MODULE_NAME_DEBUG_(s).CurrentMask +#define _XGET_ATH_MODULE_NAME_DEBUG_(s) debug_ ## s + +#ifdef WLAN_DEBUG + +/* for source files that will instantiate the debug variables */ +#define ATH_DEBUG_INSTANTIATE_MODULE_VAR(s, name, moddesc, initmask, count, descriptions) \ + ATH_DEBUG_MODULE_DBG_INFO GET_ATH_MODULE_DEBUG_VAR_NAME(s) = \ + {NULL, (name), (moddesc), 0, (initmask), (count), (descriptions)} + +#ifdef ATH_MODULE_NAME +extern ATH_DEBUG_MODULE_DBG_INFO +GET_ATH_MODULE_DEBUG_VAR_NAME(ATH_MODULE_NAME); +#define AR_DEBUG_LVL_CHECK(lvl) (GET_ATH_MODULE_DEBUG_VAR_MASK(ATH_MODULE_NAME) & (lvl)) +#endif /* ATH_MODULE_NAME */ + +#define ATH_DEBUG_SET_DEBUG_MASK(s, lvl) GET_ATH_MODULE_DEBUG_VAR_MASK(s) = (lvl) + +#define ATH_DEBUG_DECLARE_EXTERN(s) \ + extern ATH_DEBUG_MODULE_DBG_INFO GET_ATH_MODULE_DEBUG_VAR_NAME(s) + +#define AR_DEBUG_PRINTBUF(buffer, length, desc) debug_dump_bytes(buffer, length, desc) + +#define AR_DEBUG_ASSERT A_ASSERT + +void a_dump_module_debug_info(ATH_DEBUG_MODULE_DBG_INFO *pInfo); +void a_register_module_debug_info(ATH_DEBUG_MODULE_DBG_INFO *pInfo); +#ifdef A_SIMOS_DEVHOST +#define A_DUMP_MODULE_DEBUG_INFO(s) a_dump_module_debug_info(&(GET_ATH_MODULE_DEBUG_VAR_NAME(s))) +#define A_REGISTER_MODULE_DEBUG_INFO(s) a_register_module_debug_info(&(GET_ATH_MODULE_DEBUG_VAR_NAME(s))) +#else +#define A_DUMP_MODULE_DEBUG_INFO(s) +#define A_REGISTER_MODULE_DEBUG_INFO(s) +#endif + +#else /* !DEBUG */ +/* NON DEBUG */ +#define ATH_DEBUG_INSTANTIATE_MODULE_VAR(s, name, moddesc, initmask, count, descriptions) +#define AR_DEBUG_LVL_CHECK(lvl) 0 +#define AR_DEBUG_PRINTBUF(buffer, length, desc) +#define AR_DEBUG_ASSERT(test) +#define ATH_DEBUG_DECLARE_EXTERN(s) +#define ATH_DEBUG_SET_DEBUG_MASK(s, lvl) +#define A_DUMP_MODULE_DEBUG_INFO(s) +#define A_REGISTER_MODULE_DEBUG_INFO(s) + +#endif + +#if defined(__linux__) && !defined(LINUX_EMULATION) +#include "debug_linux.h" +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/drivers/staging/qcacld-3.0/uapi/linux/a_types.h b/drivers/staging/qcacld-3.0/uapi/linux/a_types.h new file mode 100644 index 0000000000000000000000000000000000000000..190d40df6afd201eff6333def8a9e3593b0df199 --- /dev/null +++ b/drivers/staging/qcacld-3.0/uapi/linux/a_types.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* depot/sw/qca_main/perf_pwr_offload/drivers/host/include/a_types.h#7 - integrate change 1327637 (ktext) */ +/* ============================================================================== */ +/* This file contains the definitions of the basic atheros data types. */ +/* It is used to map the data types in atheros files to a platform specific */ +/* type. */ +/* */ +/* Author(s): ="Atheros" */ +/* ============================================================================== */ + +#ifndef _A_TYPES_H_ +#define _A_TYPES_H_ +#include + +typedef unsigned int A_UINT32; +typedef unsigned long long A_UINT64; +typedef unsigned short A_UINT16; +typedef unsigned char A_UINT8; +typedef int A_INT32; +typedef short A_INT16; +typedef char A_INT8; +typedef unsigned char A_UCHAR; +typedef char A_CHAR; +typedef _Bool A_BOOL; + +#endif /* _ATHTYPES_H_ */ diff --git a/drivers/staging/qcacld-3.0/uapi/linux/athstartpack.h b/drivers/staging/qcacld-3.0/uapi/linux/athstartpack.h new file mode 100644 index 0000000000000000000000000000000000000000..0b92352228ec2ac218873f3eb2fb779ca7e69a74 --- /dev/null +++ b/drivers/staging/qcacld-3.0/uapi/linux/athstartpack.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ATHSTARTPACK_H +#define _ATHSTARTPACK_H + +#if defined(LINUX) || defined(__linux__) +#include "osapi_linux.h" +#endif /* LINUX */ + +#ifdef QNX +#endif /* QNX */ + +#if __LONG_MAX__ == __INT_MAX__ +/* 32-bit compilation */ +#define PREPACK64 +#define POSTPACK64 +#else +/* 64-bit compilation */ +#define PREPACK64 PREPACK +#define POSTPACK64 POSTPACK +#endif + +#endif /* _ATHSTARTPACK_H */ diff --git a/drivers/staging/qcacld-3.0/uapi/linux/dbglog_common.h b/drivers/staging/qcacld-3.0/uapi/linux/dbglog_common.h new file mode 100644 index 0000000000000000000000000000000000000000..75c2d5e729c9b5727dd3ab771b5d49a95077bd83 --- /dev/null +++ b/drivers/staging/qcacld-3.0/uapi/linux/dbglog_common.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2011, 2014-2015 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DBGLOG_COMMON_H_ +#define _DBGLOG_COMMON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "dbglog_id.h" +#include "dbglog.h" + +#define MAX_DBG_MSGS 256 + +#define ATH6KL_FWLOG_PAYLOAD_SIZE 1500 + +#define DBGLOG_PRINT_PREFIX "FWLOG: " + +/* Handy Macros to read data length and type from FW */ +#define WLAN_DIAG_0_TYPE_S 0 +#define WLAN_DIAG_0_TYPE 0x000000ff +#define WLAN_DIAG_0_TYPE_GET(x) WMI_F_MS(x, WLAN_DIAG_0_TYPE) +#define WLAN_DIAG_0_TYPE_SET(x, y) WMI_F_RMW(x, y, WLAN_DIAG_0_TYPE) +/* bits 8-15 reserved */ + +/* length includes the size of wlan_diag_data */ +#define WLAN_DIAG_0_LEN_S 16 +#define WLAN_DIAG_0_LEN 0xffff0000 +#define WLAN_DIAG_0_LEN_GET(x) WMI_F_MS(x, WLAN_DIAG_0_LEN) +#define WLAN_DIAG_0_LEN_SET(x, y) WMI_F_RMW(x, y, WLAN_DIAG_0_LEN) + +#define CNSS_DIAG_SLEEP_INTERVAL 5 /* In secs */ + +#define ATH6KL_FWLOG_MAX_ENTRIES 20 +#define ATH6KL_FWLOG_PAYLOAD_SIZE 1500 + +#define DIAG_WLAN_DRIVER_UNLOADED 6 +#define DIAG_WLAN_DRIVER_LOADED 7 +#define DIAG_TYPE_LOGS 1 +#define DIAG_TYPE_EVENTS 2 + +typedef enum { + DBGLOG_PROCESS_DEFAULT = 0, + DBGLOG_PROCESS_PRINT_RAW, /* print them in debug view */ + DBGLOG_PROCESS_POOL_RAW, /* user buffer pool to save them */ + DBGLOG_PROCESS_NET_RAW, /* user buffer pool to save them */ + DBGLOG_PROCESS_MAX, +} dbglog_process_t; + +enum cnss_diag_type { + DIAG_TYPE_FW_EVENT, /* send fw event- to diag */ + DIAG_TYPE_FW_LOG, /* send log event- to diag */ + DIAG_TYPE_FW_DEBUG_MSG, /* send dbg message- to diag */ + DIAG_TYPE_INIT_REQ, /* cnss_diag initialization- from diag */ + DIAG_TYPE_FW_MSG, /* fw msg command-to diag */ + DIAG_TYPE_HOST_MSG, /* host command-to diag */ + DIAG_TYPE_CRASH_INJECT, /*crash inject-from diag */ + DIAG_TYPE_DBG_LEVEL, /* DBG LEVEL-from diag */ +}; + +enum wlan_diag_config_type { + DIAG_VERSION_INFO, +}; + +enum wlan_diag_frame_type { + WLAN_DIAG_TYPE_CONFIG, + WLAN_DIAG_TYPE_EVENT, + WLAN_DIAG_TYPE_LOG, + WLAN_DIAG_TYPE_MSG, + WLAN_DIAG_TYPE_LEGACY_MSG, +}; + +/* log/event are always 32-bit aligned. Padding is inserted after + * optional payload to satisify this requirement */ +struct wlan_diag_data { + unsigned int word0; /* type, length */ + unsigned int target_time; + unsigned int code; /* Diag log or event Code */ + uint8_t payload[0]; +}; + +struct dbglog_slot { + unsigned int diag_type; + unsigned int timestamp; + unsigned int length; + unsigned int dropped; + /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */ + uint8_t payload[0]; +} __packed; + +typedef struct event_report_s { + unsigned int diag_type; + unsigned short event_id; + unsigned short length; +} event_report_t; + +/* + * Custom debug_print handlers + * Args: + * module Id + * vap id + * debug msg id + * Time stamp + * no of arguments + * pointer to the buffer holding the args + */ +typedef A_BOOL (*module_dbg_print)(A_UINT32, A_UINT16, A_UINT32, + A_UINT32, A_UINT16, A_UINT32 *); + +/** Register module specific dbg print*/ +void dbglog_reg_modprint(A_UINT32 mod_id, module_dbg_print printfn); + +#ifdef __cplusplus +} +#endif + +#endif /* _DBGLOG_COMMON_H_ */ diff --git a/drivers/staging/qcacld-3.0/uapi/linux/debug_linux.h b/drivers/staging/qcacld-3.0/uapi/linux/debug_linux.h new file mode 100644 index 0000000000000000000000000000000000000000..d91dd110f8f448cfd1709dec6c017da0e5cad403 --- /dev/null +++ b/drivers/staging/qcacld-3.0/uapi/linux/debug_linux.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DEBUG_LINUX_H_ +#define _DEBUG_LINUX_H_ + +/* macro to remove parens */ +#define ATH_PRINTX_ARG(arg ...) arg + +#ifdef WLAN_DEBUG +/* NOTE: the AR_DEBUG_PRINTF macro is defined here to handle special handling of variable arg macros + * which may be compiler dependent. */ +#define AR_DEBUG_PRINTF(mask, args) do { \ + if (GET_ATH_MODULE_DEBUG_VAR_MASK(ATH_MODULE_NAME) & (mask)) { \ + A_LOGGER(mask, ATH_MODULE_NAME, ATH_PRINTX_ARG args); \ + } \ +} while (0) +#else +/* on non-debug builds, keep in error and warning messages in the driver, all other + * message tracing will get compiled out */ +#define AR_DEBUG_PRINTF(mask, args) \ + if ((mask) & (ATH_DEBUG_ERR | ATH_DEBUG_WARN)) { A_PRINTF(ATH_PRINTX_ARG args); } + +#endif + +#endif /* _DEBUG_LINUX_H_ */ diff --git a/drivers/staging/qcacld-3.0/uapi/linux/osapi_linux.h b/drivers/staging/qcacld-3.0/uapi/linux/osapi_linux.h new file mode 100644 index 0000000000000000000000000000000000000000..99504f9e994f799f29f22478b1f63cfc938dbee2 --- /dev/null +++ b/drivers/staging/qcacld-3.0/uapi/linux/osapi_linux.h @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2013-2018, 2021 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* ------------------------------------------------------------------------------ */ +/* This file contains the definitions of the basic atheros data types. */ +/* It is used to map the data types in atheros files to a platform specific */ +/* type. */ +/* ------------------------------------------------------------------------------ */ + +#ifndef _OSAPI_LINUX_H_ +#define _OSAPI_LINUX_H_ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +/* #include */ +#include "a_types.h" + +#ifdef __GNUC__ +#define __ATTRIB_PACK __attribute__ ((packed)) +#define __ATTRIB_PRINTF __attribute__ ((format (printf, 1, 2))) +#define __ATTRIB_NORETURN __attribute__ ((noreturn)) +#ifndef INLINE +#define INLINE __inline__ +#endif +#else /* Not GCC */ +#define __ATTRIB_PACK +#define __ATTRIB_PRINTF +#define __ATTRIB_NORETURN +#ifndef INLINE +#define INLINE __inline +#endif +#endif /* End __GNUC__ */ + +#define PREPACK +#define POSTPACK __ATTRIB_PACK + +#define A_MEMCPY(dst, src, len) memcpy((A_UINT8 *)(dst), (src), (len)) +#define A_MEMZERO(addr, len) memset(addr, 0, len) +#define A_MEMSET(addr, value, size) memset((addr), (value), (size)) +#define A_MEMCMP(addr1, addr2, len) memcmp((addr1), (addr2), (len)) + +#define A_LOGGER(mask, mod, args ...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_QDF, args) +#define A_PRINTF(args ...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_QDF, args) +#define A_SNPRINTF(buf, len, args ...) snprintf(buf, len, args) +#define A_OFFSETOF(type, field) offsetof(type, field) + +/* + * Timer Functions + */ +#define A_MSLEEP(msecs) \ + { \ + set_current_state(TASK_INTERRUPTIBLE); \ + schedule_timeout((HZ * (msecs)) / 1000); \ + set_current_state(TASK_RUNNING); \ + } + +typedef struct timer_list A_TIMER; + +/* + * Wait Queue related functions + */ +#ifndef wait_event_interruptible_timeout +#define __wait_event_interruptible_timeout(wq, condition, ret) \ + do { \ + wait_queue_t __wait; \ + init_waitqueue_entry(&__wait, current); \ + \ + add_wait_queue(&wq, &__wait); \ + for (;; ) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (!signal_pending(current)) { \ + ret = schedule_timeout(ret); \ + if (!ret) \ + break; \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + current->state = TASK_RUNNING; \ + remove_wait_queue(&wq, &__wait); \ + } while (0) + +#define wait_event_interruptible_timeout(wq, condition, timeout) \ + ({ \ + long __ret = timeout; \ + if (!(condition)) \ + __wait_event_interruptible_timeout(wq, condition, __ret); \ + __ret; \ + }) +#endif /* wait_event_interruptible_timeout */ + +#ifdef WLAN_DEBUG +#ifdef A_SIMOS_DEVHOST +extern unsigned int panic_on_assert; +#define A_ASSERT(expr) \ + if (!(expr)) { \ + printk(KERN_ALERT "Debug Assert Caught, File %s, Line: %d, Test:%s\n", __FILE__, __LINE__, # expr); \ + if (panic_on_assert) panic(# expr); \ + } +#else +#define A_ASSERT(expr) \ + if (!(expr)) { \ + printk(KERN_ALERT "Debug Assert Caught, File %s, Line: %d, Test:%s\n", __FILE__, __LINE__, # expr); \ + } +#endif +#else +#define A_ASSERT(expr) +#endif /* DEBUG */ + +#ifdef ANDROID_ENV +struct firmware; +int android_request_firmware(const struct firmware **firmware_p, + const char *filename, struct device *device); +void android_release_firmware(const struct firmware *firmware); +#define A_REQUEST_FIRMWARE(_ppf, _pfile, _dev) android_request_firmware(_ppf, _pfile, _dev) +#define A_RELEASE_FIRMWARE(_pf) android_release_firmware(_pf) +#else +#define A_REQUEST_FIRMWARE(_ppf, _pfile, _dev) request_firmware(_ppf, _pfile, _dev) +#define A_RELEASE_FIRMWARE(_pf) release_firmware(_pf) +#endif + +/* + * Network buffer queue support + */ +typedef struct sk_buff_head A_NETBUF_QUEUE_T; + +#define A_NETBUF_FREE(bufPtr) \ + a_netbuf_free(bufPtr) +#define A_NETBUF_LEN(bufPtr) \ + a_netbuf_to_len(bufPtr) +#define A_NETBUF_PUSH(bufPtr, len) \ + a_netbuf_push(bufPtr, len) +#define A_NETBUF_PUT(bufPtr, len) \ + a_netbuf_put(bufPtr, len) +#define A_NETBUF_TRIM(bufPtr, len) \ + a_netbuf_trim(bufPtr, len) +#define A_NETBUF_PULL(bufPtr, len) \ + a_netbuf_pull(bufPtr, len) +#define A_NETBUF_HEADROOM(bufPtr) \ + a_netbuf_headroom(bufPtr) +#define A_NETBUF_SETLEN(bufPtr, len) \ + a_netbuf_setlen(bufPtr, len) + +/* Add data to end of a buffer */ +#define A_NETBUF_PUT_DATA(bufPtr, srcPtr, len) \ + a_netbuf_put_data(bufPtr, srcPtr, len) + +/* Add data to start of the buffer */ +#define A_NETBUF_PUSH_DATA(bufPtr, srcPtr, len) \ + a_netbuf_push_data(bufPtr, srcPtr, len) + +/* Remove data at start of the buffer */ +#define A_NETBUF_PULL_DATA(bufPtr, dstPtr, len) \ + a_netbuf_pull_data(bufPtr, dstPtr, len) + +/* Remove data from the end of the buffer */ +#define A_NETBUF_TRIM_DATA(bufPtr, dstPtr, len) \ + a_netbuf_trim_data(bufPtr, dstPtr, len) + +/* View data as "size" contiguous bytes of type "t" */ +#define A_NETBUF_VIEW_DATA(bufPtr, t, size) \ + (t)(((struct skbuf *)(bufPtr))->data) + +/* return the beginning of the headroom for the buffer */ +#define A_NETBUF_HEAD(bufPtr) \ + ((((struct sk_buff *)(bufPtr))->head)) + +/* + * OS specific network buffer access routines + */ +void a_netbuf_free(void *bufPtr); +void *a_netbuf_to_data(void *bufPtr); +A_UINT32 a_netbuf_to_len(void *bufPtr); +A_STATUS a_netbuf_push(void *bufPtr, A_INT32 len); +A_STATUS a_netbuf_push_data(void *bufPtr, char *srcPtr, A_INT32 len); +A_STATUS a_netbuf_put(void *bufPtr, A_INT32 len); +A_STATUS a_netbuf_put_data(void *bufPtr, char *srcPtr, A_INT32 len); +A_STATUS a_netbuf_pull(void *bufPtr, A_INT32 len); +A_STATUS a_netbuf_pull_data(void *bufPtr, char *dstPtr, A_INT32 len); +A_STATUS a_netbuf_trim(void *bufPtr, A_INT32 len); +A_STATUS a_netbuf_trim_data(void *bufPtr, char *dstPtr, A_INT32 len); +A_STATUS a_netbuf_setlen(void *bufPtr, A_INT32 len); +A_INT32 a_netbuf_headroom(void *bufPtr); +void a_netbuf_enqueue(A_NETBUF_QUEUE_T *q, void *pkt); +void a_netbuf_prequeue(A_NETBUF_QUEUE_T *q, void *pkt); +void *a_netbuf_dequeue(A_NETBUF_QUEUE_T *q); +int a_netbuf_queue_size(A_NETBUF_QUEUE_T *q); +int a_netbuf_queue_empty(A_NETBUF_QUEUE_T *q); +int a_netbuf_queue_empty(A_NETBUF_QUEUE_T *q); +void a_netbuf_queue_init(A_NETBUF_QUEUE_T *q); + +#ifdef QCA_PARTNER_PLATFORM +#include "ath_carr_pltfrm.h" +#endif /* QCA_PARTNER_PLATFORM */ + +#else /* __KERNEL__ */ + +#ifdef __GNUC__ +#define __ATTRIB_PACK __attribute__ ((packed)) +#define __ATTRIB_PRINTF __attribute__ ((format (printf, 1, 2))) +#define __ATTRIB_NORETURN __attribute__ ((noreturn)) +#ifndef inline +#define inline __inline__ +#endif +#ifndef INLINE +#define INLINE __inline__ +#endif +#else /* Not GCC */ +#define __ATTRIB_PACK +#define __ATTRIB_PRINTF +#define __ATTRIB_NORETURN +#ifndef inline +#define inline __inline +#endif +#ifndef INLINE +#define INLINE __inline +#endif +#endif /* End __GNUC__ */ + +#define PREPACK +#define POSTPACK __ATTRIB_PACK + +#define A_MEMCPY(dst, src, len) memcpy((dst), (src), (len)) +#define A_MEMSET(addr, value, size) memset((addr), (value), (size)) +#define A_MEMZERO(addr, len) memset((addr), 0, (len)) +#define A_MEMCMP(addr1, addr2, len) memcmp((addr1), (addr2), (len)) + +#ifdef ANDROID +#ifndef err +#include +#define err(_s, args ...) do { \ + fprintf(stderr, "%s: line %d ", __FILE__, __LINE__); \ + fprintf(stderr, args); fprintf(stderr, ": %d\n", errno); \ + exit(_s); } while (0) +#endif +#else +#include +#endif + +#endif /* __KERNEL__ */ + +#endif /* _OSAPI_LINUX_H_ */ diff --git a/drivers/staging/qcacld-3.0/uapi/linux/pktlog_ac_fmt.h b/drivers/staging/qcacld-3.0/uapi/linux/pktlog_ac_fmt.h new file mode 100644 index 0000000000000000000000000000000000000000..c85f555454a86a93e433ae7f62c1fcefd86c32f4 --- /dev/null +++ b/drivers/staging/qcacld-3.0/uapi/linux/pktlog_ac_fmt.h @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PKTLOG_FMT_H_ +#define _PKTLOG_FMT_H_ + +#ifndef REMOVE_PKT_LOG + +#define CUR_PKTLOG_VER 10010 /* Packet log version */ +#define PKTLOG_MAGIC_NUM 7735225 + +#ifdef __linux__ +#ifdef MULTI_IF_NAME +#define PKTLOG_PROC_DIR "ath_pktlog" MULTI_IF_NAME +#define WLANDEV_BASENAME "cld" MULTI_IF_NAME +#else +#define PKTLOG_PROC_DIR "ath_pktlog" +#define WLANDEV_BASENAME "cld" +#endif +#endif +#define PKTLOG_PROC_SYSTEM "system" +#ifdef WIN32 +#pragma pack(push, pktlog_fmt, 1) +#define __ATTRIB_PACK +#elif defined(__EFI__) +#define __ATTRIB_PACK +#else +#ifndef __ATTRIB_PACK +#define __ATTRIB_PACK __attribute__ ((packed)) +#endif +#endif +#include +/* + * Each packet log entry consists of the following fixed length header + * followed by variable length log information determined by log_type + */ + +struct ath_pktlog_hdr { + uint16_t flags; + uint16_t missed_cnt; +#ifdef HELIUMPLUS + uint8_t log_type; + uint8_t macId; +#else + uint16_t log_type; +#endif + uint16_t size; + uint32_t timestamp; +#ifdef PKTLOG_HAS_SPECIFIC_DATA + uint32_t type_specific_data; +#endif +} __ATTRIB_PACK; + +#define ATH_PKTLOG_HDR_FLAGS_MASK 0xffff +#define ATH_PKTLOG_HDR_FLAGS_SHIFT 0 +#define ATH_PKTLOG_HDR_FLAGS_OFFSET 0 +#define ATH_PKTLOG_HDR_MISSED_CNT_MASK 0xffff0000 +#define ATH_PKTLOG_HDR_MISSED_CNT_SHIFT 16 +#define ATH_PKTLOG_HDR_MISSED_CNT_OFFSET 0 +#ifdef HELIUMPLUS +#define ATH_PKTLOG_HDR_LOG_TYPE_MASK 0x00ff +#define ATH_PKTLOG_HDR_LOG_TYPE_SHIFT 0 +#define ATH_PKTLOG_HDR_LOG_TYPE_OFFSET 1 +#define ATH_PKTLOG_HDR_MAC_ID_MASK 0xff00 +#define ATH_PKTLOG_HDR_MAC_ID_SHIFT 8 +#define ATH_PKTLOG_HDR_MAC_ID_OFFSET 1 +#else +#define ATH_PKTLOG_HDR_LOG_TYPE_MASK 0xffff +#define ATH_PKTLOG_HDR_LOG_TYPE_SHIFT 0 +#define ATH_PKTLOG_HDR_LOG_TYPE_OFFSET 1 +#endif + +#define ATH_PKTLOG_HDR_SIZE_MASK 0xffff0000 +#define ATH_PKTLOG_HDR_SIZE_SHIFT 16 +#define ATH_PKTLOG_HDR_SIZE_OFFSET 1 +#define ATH_PKTLOG_HDR_TIMESTAMP_OFFSET 2 +#define ATH_PKTLOG_HDR_TYPE_SPECIFIC_DATA_OFFSET 3 + +/** + * enum - Pktlog flag field details + * packet origin [1:0] + * 00 - Local + * 01 - Remote + * 10 - Unknown/Not applicable + * 11 - Reserved + * reserved [15:2] + */ +enum { + PKTLOG_FLG_FRM_TYPE_LOCAL_S = 0, + PKTLOG_FLG_FRM_TYPE_REMOTE_S, + PKTLOG_FLG_FRM_TYPE_CLONE_S, + PKTLOG_FLG_FRM_TYPE_CBF_S, + PKTLOG_FLG_FRM_TYPE_UNKNOWN_S +}; + +#define PHFLAGS_INTERRUPT_CONTEXT 0x80000000 + +/* Masks for setting pktlog events filters */ +#define ATH_PKTLOG_TX 0x000000001 +#define ATH_PKTLOG_RX 0x000000002 +#define ATH_PKTLOG_RCFIND 0x000000004 +#define ATH_PKTLOG_RCUPDATE 0x000000008 +#define ATH_PKTLOG_ANI 0x000000010 +#define ATH_PKTLOG_TEXT 0x000000020 +#define ATH_PKTLOG_PHYERR 0x000000040 +#define ATH_PKTLOG_PROMISC 0x000000080 +#define ATH_PKTLOG_SW_EVENT 0x000000100 + +/* WIN defns */ +#define ATH_PKTLOG_H_INFO 0x000000200 +#define ATH_PKTLOG_STEERING 0x000000400 +#define ATH_PKTLOG_REMOTE_LOGGING_ENABLE 0x000000800 +#define ATH_PKTLOG_TX_CAPTURE_ENABLE 0x000001000 +#define ATH_PKTLOG_LITE_T2H 0x000002000 +#define ATH_PKTLOG_LITE_RX 0x000004000 + +/* Types of packet log events */ +#define PKTLOG_TYPE_TX_CTRL 1 +#define PKTLOG_TYPE_TX_STAT 2 +#define PKTLOG_TYPE_TX_MSDU_ID 3 +#define PKTLOG_TYPE_TX_FRM_HDR 4 +#define PKTLOG_TYPE_RX_STAT 5 +#define PKTLOG_TYPE_RC_FIND 6 +#define PKTLOG_TYPE_RC_UPDATE 7 +#define PKTLOG_TYPE_TX_VIRT_ADDR 8 +#define PKTLOG_TYPE_SMART_ANTENNA 9 +#define PKTLOG_TYPE_SW_EVENT 10 +#define PKTLOG_TYPE_PKT_DUMP 11 +/* Command to process Monitor status ring (Rx) buffers */ +#define PKTLOG_TYPE_RX_STATBUF 22 +/* Command to process PPDU Tx buffers from CE */ +#define PKTLOG_TYPE_LITE_T2H 23 +/* Command to process PPDU Rx buffers from Monitor status ring */ +#define PKTLOG_TYPE_LITE_RX 24 +#define PKTLOG_TYPE_MAX 25 + +#define PKTLOG_MAX_TXCTL_WORDS 57 /* +2 words for bitmap */ +#define PKTLOG_MAX_TXSTATUS_WORDS 32 +#define PKTLOG_MAX_PROTO_WORDS 16 +#define PKTLOG_MAX_RXDESC_WORDS 62 +#define PKTLOG_HDR_SIZE_16 0x8000 + +struct txctl_frm_hdr { + uint16_t framectrl; /* frame control field from header */ + uint16_t seqctrl; /* frame control field from header */ + uint16_t bssid_tail; /* last two octets of bssid */ + uint16_t sa_tail; /* last two octets of SA */ + uint16_t da_tail; /* last two octets of DA */ + uint16_t resvd; +}; + +#if defined(HELIUMPLUS) +/* Peregrine 11ac based */ +#define MAX_PKT_INFO_MSDU_ID 1 +#else +/* Peregrine 11ac based */ +#define MAX_PKT_INFO_MSDU_ID 192 +#endif /* defined(HELIUMPLUS) */ + +/* + * msdu_id_info_t is defined for reference only + */ +struct msdu_id_info { + uint32_t num_msdu; + uint8_t bound_bmap[(MAX_PKT_INFO_MSDU_ID + 7)>>3]; + /* TODO: + * Convert the id's to uint32_t + * Reduces computation in the driver code + */ + uint16_t id[MAX_PKT_INFO_MSDU_ID]; +} __ATTRIB_PACK; +#define MSDU_ID_INFO_NUM_MSDU_OFFSET 0 /* char offset */ +#define MSDU_ID_INFO_BOUND_BM_OFFSET offsetof(struct msdu_id_info, bound_bmap) +#define MSDU_ID_INFO_ID_OFFSET offsetof(struct msdu_id_info, id) + + +struct ath_pktlog_txctl { + struct ath_pktlog_hdr pl_hdr; + /* struct txctl_frm_hdr frm_hdr; */ + void *txdesc_hdr_ctl; /* frm_hdr + Tx descriptor words */ + struct { + struct txctl_frm_hdr frm_hdr; + uint32_t txdesc_ctl[PKTLOG_MAX_TXCTL_WORDS]; + /* uint32_t *proto_hdr; / * protocol header (variable length!) * / */ + /* uint32_t *misc; / * Can be used for HT specific or other misc info * / */ + } priv; +} __ATTRIB_PACK; + +struct ath_pktlog_tx_status { + struct ath_pktlog_hdr pl_hdr; + void *ds_status; + int32_t misc[0]; /* Can be used for HT specific or other misc info */ +} __ATTRIB_PACK; + +struct ath_pktlog_msdu_info { + struct ath_pktlog_hdr pl_hdr; + void *ath_msdu_info; + A_UINT32 num_msdu; + struct { + /* + * Provision to add more information fields + */ + struct msdu_info_t { + A_UINT32 num_msdu; + A_UINT8 bound_bmap[MAX_PKT_INFO_MSDU_ID >> 3]; + } msdu_id_info; + /* + * array of num_msdu + * Static implementation will consume unwanted memory + * Need to split the pktlog_get_buf to get the buffer pointer only + */ + uint16_t msdu_len[MAX_PKT_INFO_MSDU_ID]; + } priv; + size_t priv_size; + +} __ATTRIB_PACK; + +struct ath_pktlog_rx_info { + struct ath_pktlog_hdr pl_hdr; + void *rx_desc; +} __ATTRIB_PACK; + +struct ath_pktlog_rc_find { + struct ath_pktlog_hdr pl_hdr; + void *rcFind; +} __ATTRIB_PACK; + +struct ath_pktlog_sw_event { + struct ath_pktlog_hdr pl_hdr; + void *sw_event; +} __ATTRIB_PACK; + +struct ath_pktlog_rc_update { + struct ath_pktlog_hdr pl_hdr; + void *txRateCtrl; /* rate control state proper */ +} __ATTRIB_PACK; + +#ifdef WIN32 +#pragma pack(pop, pktlog_fmt) +#endif +#ifdef __ATTRIB_PACK +#undef __ATTRIB_PACK +#endif /* __ATTRIB_PACK */ + +/* + * The following header is included in the beginning of the file, + * followed by log entries when the log buffer is read through procfs + */ + +struct ath_pktlog_bufhdr { + uint32_t magic_num; /* Used by post processing scripts */ + uint32_t version; /* Set to CUR_PKTLOG_VER */ +}; + +struct ath_pktlog_buf { + struct ath_pktlog_bufhdr bufhdr; + int32_t rd_offset; + volatile int32_t wr_offset; + /* Whenever this bytes written value croses 4K bytes, + * logging will be triggered + */ + int32_t bytes_written; + /* Index of the messages sent to userspace */ + uint32_t msg_index; + /* Offset for read */ + loff_t offset; + char log_data[0]; +}; + +#define PKTLOG_MOV_RD_IDX(_rd_offset, _log_buf, _log_size) \ + do { \ + if ((_rd_offset + sizeof(struct ath_pktlog_hdr) + \ + ((struct ath_pktlog_hdr *)((_log_buf)->log_data + \ + (_rd_offset)))->size) <= _log_size) { \ + _rd_offset = ((_rd_offset) + sizeof(struct ath_pktlog_hdr) + \ + ((struct ath_pktlog_hdr *)((_log_buf)->log_data + \ + (_rd_offset)))->size); \ + } else { \ + _rd_offset = ((struct ath_pktlog_hdr *)((_log_buf)->log_data + \ + (_rd_offset)))->size; \ + } \ + (_rd_offset) = (((_log_size) - (_rd_offset)) >= \ + sizeof(struct ath_pktlog_hdr)) ? _rd_offset : 0; \ + } while (0) + +#endif /* REMOVE_PKT_LOG */ + +/** + * enum pkt_type - packet type + * @START_MONITOR: indicates parser to start packetdump parsing + * @STOP_MONITOR: indicates parser to stop packetdump parsing + * @TX_MGMT_PKT: TX management Packet + * @TX_DATA_PKT: TX data Packet + * @RX_MGMT_PKT: RX management Packet + * @RX_DATA_PKT: RX data Packet + * + * This enum has packet types + */ +enum pkt_type { + START_MONITOR = 1, + STOP_MONITOR, + TX_MGMT_PKT, + TX_DATA_PKT, + RX_MGMT_PKT, + RX_DATA_PKT, +}; + +/** + * enum tx_pkt_fate - tx packet fate + * @TX_PKT_FATE_ACKED: Sent over air and ACKed + * @TX_PKT_FATE_SENT: Sent over air but not ACKed. + * @TX_PKT_FATE_FW_QUEUED: Queued within firmware, + * but not yet sent over air + * @TX_PKT_FATE_FW_DROP_INVALID: Dropped by firmware as invalid. + * E.g. bad source address, bad checksum, or invalid for current state. + * @TX_PKT_FATE_FW_DROP_NOBUFS: Dropped by firmware due + * to lack of buffer space + * @TX_PKT_FATE_FW_DROP_OTHER: Dropped by firmware for any other + * reason. Includes frames that were sent by driver to firmware, but + * unaccounted for by firmware. + * @TX_PKT_FATE_DRV_QUEUED: Queued within driver, not yet sent to firmware. + * @TX_PKT_FATE_DRV_DROP_INVALID: Dropped by driver as invalid. + * E.g. bad source address, or invalid for current state. + * @TX_PKT_FATE_DRV_DROP_NOBUFS: Dropped by driver due to lack of buffer space + * @TX_PKT_FATE_DRV_DROP_OTHER: Dropped by driver for any other reason. + * E.g. out of buffers. + * + * This enum has packet fate types + */ + +enum tx_pkt_fate { + TX_PKT_FATE_ACKED, + TX_PKT_FATE_SENT, + TX_PKT_FATE_FW_QUEUED, + TX_PKT_FATE_FW_DROP_INVALID, + TX_PKT_FATE_FW_DROP_NOBUFS, + TX_PKT_FATE_FW_DROP_OTHER, + TX_PKT_FATE_DRV_QUEUED, + TX_PKT_FATE_DRV_DROP_INVALID, + TX_PKT_FATE_DRV_DROP_NOBUFS, + TX_PKT_FATE_DRV_DROP_OTHER, +}; + +/** + * enum rx_pkt_fate - rx packet fate + * @RX_PKT_FATE_SUCCESS: Valid and delivered to + * network stack (e.g., netif_rx()). + * @RX_PKT_FATE_FW_QUEUED: Queued within firmware, + * but not yet sent to driver. + * @RX_PKT_FATE_FW_DROP_FILTER: Dropped by firmware + * due to host-programmable filters. + * @RX_PKT_FATE_FW_DROP_INVALID: Dropped by firmware + * as invalid. E.g. bad checksum, decrypt failed, or invalid for current state. + * @RX_PKT_FATE_FW_DROP_NOBUFS: Dropped by firmware + * due to lack of buffer space. + * @RX_PKT_FATE_FW_DROP_OTHER: Dropped by firmware + * for any other reason. + * @RX_PKT_FATE_DRV_QUEUED: Queued within driver, + * not yet delivered to network stack. + * @RX_PKT_FATE_DRV_DROP_FILTER: Dropped by driver + * due to filter rules. + * @RX_PKT_FATE_DRV_DROP_INVALID: Dropped by driver as invalid. + * E.g. not permitted in current state. + * @RX_PKT_FATE_DRV_DROP_NOBUFS: Dropped by driver + * due to lack of buffer space. + * @RX_PKT_FATE_DRV_DROP_OTHER: Dropped by driver for any other reason. + * + * This enum has packet fate types + */ + +enum rx_pkt_fate { + RX_PKT_FATE_SUCCESS, + RX_PKT_FATE_FW_QUEUED, + RX_PKT_FATE_FW_DROP_FILTER, + RX_PKT_FATE_FW_DROP_INVALID, + RX_PKT_FATE_FW_DROP_NOBUFS, + RX_PKT_FATE_FW_DROP_OTHER, + RX_PKT_FATE_DRV_QUEUED, + RX_PKT_FATE_DRV_DROP_FILTER, + RX_PKT_FATE_DRV_DROP_INVALID, + RX_PKT_FATE_DRV_DROP_NOBUFS, + RX_PKT_FATE_DRV_DROP_OTHER, +}; + +#endif /* _PKTLOG_FMT_H_ */